export type ResumableUploadRuntimePolicy = Readonly<{ partSizeBytes: number; maxFileBytes: number; maxPartCount: number; maxConcurrency: number; maxInFlightBytes: number; partBufferCopyFactor: number; maxSourceChunkBytes: number; maxRetries: number; retryBaseDelayMs: number; retryMaxDelayMs: number; maxRetryAfterMs: number; capabilityRefreshSkewMs: number; maxSessionLifetimeMs: number; providerAttemptTimeoutMs: number; /** * TR-RR-06. The bound `dispose()` applies to its drain. A non-cooperative * mutation lock or provider would otherwise make teardown unbounded, so a * caller could never learn whether the runtime was quiescent. */ cleanupDeadlineMs: number; }>; const MIB = 1024 * 1024; const GIB = 1024 * MIB; const ABSOLUTE_LIMITS = Object.freeze({ maxPartSizeBytes: 64 * MIB, maxFileBytes: 100 * GIB, maxPartCount: 10_000, maxConcurrency: 8, maxInFlightBytes: 256 * MIB, maxPartBufferCopyFactor: 8, maxSourceChunkBytes: 64 * MIB, maxRetries: 8, maxRetryDelayMs: 60_000, maxRetryAfterMs: 60_000, maxCapabilityRefreshSkewMs: 5 * 60_000, maxSessionLifetimeMs: 7 * 24 * 60 * 60_000, maxProviderAttemptTimeoutMs: 2 * 60_000, }); const DEFAULT_POLICY: ResumableUploadRuntimePolicy = Object.freeze({ partSizeBytes: 5 * MIB, maxFileBytes: 5 * GIB, maxPartCount: 1_024, maxConcurrency: 3, maxInFlightBytes: 20 * MIB, partBufferCopyFactor: 4, maxSourceChunkBytes: 8 * MIB, maxRetries: 3, retryBaseDelayMs: 250, retryMaxDelayMs: 5_000, maxRetryAfterMs: 30_000, capabilityRefreshSkewMs: 5_000, maxSessionLifetimeMs: 24 * 60 * 60_000, providerAttemptTimeoutMs: 30_000, cleanupDeadlineMs: 10_000, }); export function resolveResumableUploadRuntimePolicy( input: Partial = {}, ): ResumableUploadRuntimePolicy { const policy: ResumableUploadRuntimePolicy = Object.freeze({ ...DEFAULT_POLICY, ...input, }); if ( !positiveSafeInteger(policy.partSizeBytes) || policy.partSizeBytes > ABSOLUTE_LIMITS.maxPartSizeBytes || !positiveSafeInteger(policy.maxFileBytes) || policy.maxFileBytes > ABSOLUTE_LIMITS.maxFileBytes || !positiveSafeInteger(policy.maxPartCount) || policy.maxPartCount > ABSOLUTE_LIMITS.maxPartCount || !positiveSafeInteger(policy.maxConcurrency) || policy.maxConcurrency > ABSOLUTE_LIMITS.maxConcurrency || !positiveSafeInteger(policy.maxInFlightBytes) || policy.maxInFlightBytes > ABSOLUTE_LIMITS.maxInFlightBytes || !positiveSafeInteger(policy.partBufferCopyFactor) || policy.partBufferCopyFactor > ABSOLUTE_LIMITS.maxPartBufferCopyFactor || policy.maxInFlightBytes < policy.partSizeBytes * policy.partBufferCopyFactor || !positiveSafeInteger(policy.maxSourceChunkBytes) || policy.maxSourceChunkBytes > ABSOLUTE_LIMITS.maxSourceChunkBytes || !nonNegativeSafeInteger(policy.maxRetries) || policy.maxRetries > ABSOLUTE_LIMITS.maxRetries || !positiveSafeInteger(policy.retryBaseDelayMs) || policy.retryBaseDelayMs > ABSOLUTE_LIMITS.maxRetryDelayMs || !positiveSafeInteger(policy.retryMaxDelayMs) || policy.retryMaxDelayMs > ABSOLUTE_LIMITS.maxRetryDelayMs || policy.retryBaseDelayMs > policy.retryMaxDelayMs || !nonNegativeSafeInteger(policy.maxRetryAfterMs) || policy.maxRetryAfterMs > ABSOLUTE_LIMITS.maxRetryAfterMs || !nonNegativeSafeInteger(policy.capabilityRefreshSkewMs) || policy.capabilityRefreshSkewMs > ABSOLUTE_LIMITS.maxCapabilityRefreshSkewMs || !positiveSafeInteger(policy.maxSessionLifetimeMs) || policy.maxSessionLifetimeMs > ABSOLUTE_LIMITS.maxSessionLifetimeMs || !positiveSafeInteger(policy.providerAttemptTimeoutMs) || policy.providerAttemptTimeoutMs > ABSOLUTE_LIMITS.maxProviderAttemptTimeoutMs || !positiveSafeInteger(policy.cleanupDeadlineMs) || policy.cleanupDeadlineMs > ABSOLUTE_LIMITS.maxProviderAttemptTimeoutMs * 2 || Math.ceil(policy.maxFileBytes / policy.partSizeBytes) > policy.maxPartCount ) { throw new TypeError("Resumable upload policy is invalid."); } return policy; } function positiveSafeInteger(value: number): boolean { return Number.isSafeInteger(value) && value > 0; } function nonNegativeSafeInteger(value: number): boolean { return Number.isSafeInteger(value) && value >= 0; }