import type { ByteCount, BrowserFilePolicyProfile, BrowserManagedDownloadCapabilityResolver, BrowserManagedDownloadCapabilityReceipt, DownloadDeliveryPort, DownloadOutcome, DurableObjectDescriptor, DurableObjectId, DurableObjectMaintenancePort, DurableObjectRead, DurableObjectStorePort, ExampleQuarantinedUpload, ExampleQuarantinedUploadPort, ExampleUploadPartReceipt, ExampleUploadSession, FileBytePattern, FileCandidate, FileByteSource, FileContentPort, FileInspection, FileInspectionPolicy, FilePolicyReference, FilePickerPort, FileSelectionOutcome, FileSelectionLimitReduction, FileSelectionPolicy, FileVerificationReceipt, LocalFileRef, MigrationProgress, ObjectGeneration, ObjectStoreRecoverySummary, OfflineCommitReceipt, OfflinePage, OfflineStoreLifecycleEvent, OfflineStoreMaintenancePort, OfflineStoreStatus, PublicAssetEntry, PublicCacheInspection, PublicCacheLookup, PublicResponseCacheAdmin, StorageDurabilityPort, StorageEstimate, StoredRecord, StructuredOfflineMutation, StructuredOfflineStore, TransientPreviewPort, RegisteredDownloadPolicy, RegisteredPreviewPolicy, } from "./browser-file-storage-contracts.ts"; import type { CapabilityResult } from "./contracts.ts"; import { failure, success } from "./fake-adapters.ts"; export function asByteCount(value: number): ByteCount { if (!Number.isSafeInteger(value) || value < 0) { throw new TypeError("Byte counts must be non-negative safe integers."); } return value as ByteCount; } export function asLocalFileRef(value: string): LocalFileRef { if (value.length === 0) { throw new TypeError("Local file references must not be empty."); } return value as LocalFileRef; } export function asFileVerificationReceipt( value: string, ): FileVerificationReceipt { if (!/^[a-z0-9][a-z0-9:_-]{0,127}$/i.test(value)) { throw new TypeError("File verification receipts must be opaque tokens."); } return value as FileVerificationReceipt; } const issuedFilePolicyReferences = new WeakSet(); export function browserFilePolicyReference( policyKey: string, intention: string, ): FilePolicyReference { const token = /^[a-z0-9][a-z0-9._:-]{0,127}$/i; if (!token.test(policyKey) || !token.test(intention)) { throw new TypeError("File policy references must be opaque tokens."); } const reference = Object.freeze({ policyKey: policyKey as FilePolicyReference["policyKey"], intention: intention as FilePolicyReference["intention"], }); issuedFilePolicyReferences.add(reference); return reference; } export function asBrowserManagedDownloadCapabilityReceipt( value: string, ): BrowserManagedDownloadCapabilityReceipt { if (!/^[a-z0-9][a-z0-9:_-]{0,127}$/i.test(value)) { throw new TypeError( "Browser-managed download receipts must be opaque tokens.", ); } return value as BrowserManagedDownloadCapabilityReceipt; } type MemoryPolicyProfile = Readonly<{ bindingId: string; selection?: FileSelectionPolicy; inspection?: FileInspectionPolicy; preview?: RegisteredPreviewPolicy; download?: RegisteredDownloadPolicy; }>; /** * Composition-time dataset policy registry used by the deterministic fakes. * It deep-snapshots profile data and resolves only the exact registered * reference object. Equal key/intention strings are not authority. */ export class MemoryBrowserFilePolicyRegistry { readonly #profiles = new Map(); constructor(profiles: ReadonlyArray) { if (profiles.length === 0) { throw new TypeError("At least one file policy profile is required."); } const semanticKeys = new Set(); for (const profile of profiles) { if ( !issuedFilePolicyReferences.has(profile.reference) || (!profile.selection && !profile.inspection && !profile.preview && !profile.download) || (profile.preview && !profile.inspection) ) { throw new TypeError("The file policy profile is invalid."); } const bindingId = `${profile.reference.policyKey}\u0000${profile.reference.intention}`; if (semanticKeys.has(bindingId)) { throw new TypeError("The file policy profile is duplicated."); } semanticKeys.add(bindingId); this.#profiles.set( profile.reference, Object.freeze({ bindingId, ...(profile.selection ? { selection: snapshotSelectionPolicy(profile.selection) } : {}), ...(profile.inspection ? { inspection: snapshotInspectionPolicy( profile.inspection, ), } : {}), ...(profile.preview ? { preview: snapshotPreviewPolicy(profile.preview) } : {}), ...(profile.download ? { download: Object.freeze({ ...profile.download, }), } : {}), }), ); } } selection( reference: FilePolicyReference, reductions?: FileSelectionLimitReduction, ): CapabilityResult { const profile = this.#profiles.get(reference); if (!profile?.selection) return rejectedPolicy(); const maxCount = reduceLimit( reductions?.maxCount, profile.selection.maxCount, ); const maxFileBytes = reduceLimit( reductions?.maxFileBytes, profile.selection.maxFileBytes, ); const maxTotalBytes = reduceLimit( reductions?.maxTotalBytes, profile.selection.maxTotalBytes, ); if ( maxCount === null || maxFileBytes === null || maxTotalBytes === null ) { return failure( "LIMIT_EXCEEDED", false, "A caller may only reduce registered file limits.", ); } return success( Object.freeze({ ...profile.selection, maxCount, maxFileBytes: asByteCount(maxFileBytes), maxTotalBytes: asByteCount(maxTotalBytes), }), ); } inspection( reference: FilePolicyReference, reduction?: ByteCount, ): CapabilityResult< Readonly<{ policy: FileInspectionPolicy; bindingId: string; }> > { const profile = this.#profiles.get(reference); if (!profile?.inspection) return rejectedPolicy(); const maxInspectionBytes = reduceLimit( reduction, profile.inspection.maxInspectionBytes, ); if (maxInspectionBytes === null) { return failure( "LIMIT_EXCEEDED", false, "A caller may only reduce registered inspection limits.", ); } return success( Object.freeze({ bindingId: profile.bindingId, policy: Object.freeze({ ...profile.inspection, maxInspectionBytes: asByteCount(maxInspectionBytes), }), }), ); } preview( reference: FilePolicyReference, reduction?: ByteCount, ): CapabilityResult< Readonly<{ bindingId: string; allowedMediaTypes: ReadonlySet; maxPreviewBytes: ByteCount; }> > { const profile = this.#profiles.get(reference); if (!profile?.preview || !profile.inspection) { return rejectedPolicy(); } const maxPreviewBytes = reduceLimit( reduction, profile.preview.maxPreviewBytes, ); if (maxPreviewBytes === null) { return failure( "LIMIT_EXCEEDED", false, "A caller may only reduce registered preview limits.", ); } return success( Object.freeze({ bindingId: profile.bindingId, allowedMediaTypes: new Set( profile.preview.allowedMediaTypes, ), maxPreviewBytes: asByteCount(maxPreviewBytes), }), ); } download( reference: FilePolicyReference, reductions: Readonly<{ maxTransferBytes?: ByteCount; maxBufferedBytes?: ByteCount; }>, ): CapabilityResult { const profile = this.#profiles.get(reference); if (!profile?.download) return rejectedPolicy(); const maxTransferBytes = reduceLimit( reductions.maxTransferBytes, profile.download.maxTransferBytes, ); const maxBufferedBytes = reduceLimit( reductions.maxBufferedBytes, Math.min( profile.download.maxBufferedBytes, maxTransferBytes ?? -1, ), ); if ( maxTransferBytes === null || maxBufferedBytes === null ) { return failure( "LIMIT_EXCEEDED", false, "A caller may only reduce registered download limits.", ); } return success( Object.freeze({ ...profile.download, maxTransferBytes: asByteCount(maxTransferBytes), maxBufferedBytes: asByteCount(maxBufferedBytes), }), ); } } function rejectedPolicy(): CapabilityResult { return failure( "POLICY_REJECTED", false, "The composition-issued file policy is not available.", ); } function reduceLimit( reduction: number | undefined, ceiling: number, ): number | null { const value = reduction ?? ceiling; return Number.isSafeInteger(value) && value > 0 && value <= ceiling ? value : null; } function snapshotSelectionPolicy( policy: FileSelectionPolicy, ): FileSelectionPolicy { return Object.freeze({ ...policy, accept: Object.freeze( policy.accept.map((rule) => Object.freeze({ mediaType: rule.mediaType, extensions: Object.freeze([...rule.extensions]), }), ), ), }); } function snapshotInspectionPolicy( policy: FileInspectionPolicy, ): FileInspectionPolicy { return Object.freeze({ ...policy, acceptedSignatures: Object.freeze( policy.acceptedSignatures.map((rule) => Object.freeze({ mediaType: rule.mediaType, extensions: Object.freeze([...rule.extensions]), patterns: Object.freeze( rule.patterns.map((pattern) => Object.freeze({ offset: pattern.offset, bytes: Object.freeze([...pattern.bytes]), ...(pattern.mask ? { mask: Object.freeze([...pattern.mask]) } : {}), }), ), ), }), ), ), }); } function snapshotPreviewPolicy( policy: RegisteredPreviewPolicy, ): RegisteredPreviewPolicy { return Object.freeze({ allowedMediaTypes: Object.freeze([ ...policy.allowedMediaTypes, ]), maxPreviewBytes: policy.maxPreviewBytes, }); } export function asDurableObjectId(value: string): DurableObjectId { if (value.length === 0) { throw new TypeError("Durable object IDs must not be empty."); } return value as DurableObjectId; } export function asObjectGeneration(value: number): ObjectGeneration { if (!Number.isSafeInteger(value) || value < 1) { throw new TypeError("Object generations must be positive safe integers."); } return value as ObjectGeneration; } function cancelled(signal?: AbortSignal): CapabilityResult | null { return signal?.aborted ? failure("ABORTED", false, "The operation was cancelled.") : null; } function isReadableStatus(status: OfflineStoreStatus): boolean { return status.kind === "READY" || status.kind === "READ_ONLY"; } function isWritableStatus(status: OfflineStoreStatus): boolean { return status.kind === "READY"; } function cloneBytes(bytes: Uint8Array): Uint8Array { return bytes.slice(); } function extensionOf(fileName: string): string | null { const normalized = fileName.normalize("NFC"); const index = normalized.lastIndexOf("."); return index > 0 && index < normalized.length - 1 ? normalized.slice(index).toLowerCase() : null; } function acceptsCandidate( policy: FileSelectionPolicy, candidate: FileCandidate, ): boolean { if (policy.accept.length === 0) return true; const extension = extensionOf(candidate.displayName); return policy.accept.some( (rule) => (candidate.reportedMediaType !== null && candidate.reportedMediaType.toLowerCase() === rule.mediaType.toLowerCase()) || (extension !== null && rule.extensions.some( (accepted) => accepted.toLowerCase() === extension, )), ); } function matchesFileBytePattern( bytes: Uint8Array, pattern: FileBytePattern, ): boolean { if (pattern.offset + pattern.bytes.length > bytes.byteLength) { return false; } for (const [index, expected] of pattern.bytes.entries()) { const actual = bytes[pattern.offset + index]; const mask = pattern.mask?.[index] ?? 0xff; if ( actual === undefined || !Number.isInteger(expected) || expected < 0 || expected > 0xff || (actual & mask) !== (expected & mask) ) { return false; } } return true; } export type MemoryFileFixture = Readonly<{ ref: LocalFileRef; displayName: string; bytes: Uint8Array; reportedMediaType: string | null; detectedMediaType: string | null; lastModifiedEpochMs?: number | null; signature?: FileInspection["signature"]; source?: FileCandidate["source"]; }>; /** * Deterministic picker and transient file vault. Metadata matching is only a * preflight policy simulation. Any separately selected backend transfer * workflow must independently revalidate content. */ export class MemoryFileSelectionAdapter implements FilePickerPort, FileContentPort { readonly support; readonly #policies: MemoryBrowserFilePolicyRegistry; readonly #files = new Map(); readonly #verifications = new Map< FileVerificationReceipt, Readonly<{ ref: LocalFileRef; policyBindingId: string; mediaType: string; }> >(); #verificationSequence = 0; #nextOutcome: "SELECTED" | "DISMISSED" | "PERMISSION_DENIED" = "SELECTED"; constructor( files: ReadonlyArray, policies: MemoryBrowserFilePolicyRegistry, support: FilePickerPort["support"] = { nativeInput: true, systemOpenPicker: false, systemSavePicker: false, }, ) { this.#policies = policies; this.support = Object.freeze({ ...support }); for (const file of files) { this.#files.set(file.ref, { ...file, bytes: cloneBytes(file.bytes), }); } } setNextOutcome( outcome: "SELECTED" | "DISMISSED" | "PERMISSION_DENIED", ): void { this.#nextOutcome = outcome; } async select(input: { policy: FilePolicyReference; limits?: FileSelectionLimitReduction; signal?: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const outcome = this.#nextOutcome; this.#nextOutcome = "SELECTED"; if (outcome === "DISMISSED") { return success({ kind: "DISMISSED" }); } if (outcome === "PERMISSION_DENIED") { return failure( "PERMISSION_DENIED", false, "File access permission was denied.", ); } const resolvedPolicy = this.#policies.selection( input.policy, input.limits, ); if (!resolvedPolicy.ok) return resolvedPolicy; const policy = resolvedPolicy.value; const candidates = Array.from(this.#files.values(), (file) => Object.freeze({ ref: file.ref, displayName: file.displayName, sizeBytes: asByteCount(file.bytes.byteLength), reportedMediaType: file.reportedMediaType, lastModifiedEpochMs: file.lastModifiedEpochMs ?? null, source: file.source ?? "NATIVE_INPUT", }), ); const total = candidates.reduce( (sum, candidate) => sum + candidate.sizeBytes, 0, ); if ( candidates.length > policy.maxCount || (!policy.multiple && candidates.length > 1) || candidates.some( (candidate) => candidate.sizeBytes > policy.maxFileBytes || (!policy.allowEmpty && candidate.sizeBytes === 0), ) || total > policy.maxTotalBytes ) { return failure( "LIMIT_EXCEEDED", false, "The selected files exceed the approved count or byte budget.", ); } if ( candidates.some( (candidate) => !acceptsCandidate(policy, candidate), ) ) { return failure( "POLICY_REJECTED", false, "A selected file does not match the intake policy.", ); } return success({ kind: "SELECTED", files: candidates }); } async inspect(input: { ref: LocalFileRef; policy: FilePolicyReference; maxInspectionBytes?: ByteCount; signal: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const resolvedPolicy = this.#policies.inspection( input.policy, input.maxInspectionBytes, ); if (!resolvedPolicy.ok) return resolvedPolicy; const { policy, bindingId } = resolvedPolicy.value; const file = this.#files.get(input.ref); if (!file) { return failure("NOT_FOUND", false, "The selected file is no longer available."); } this.#invalidateVerifications(input.ref); const inspectedBytes = file.bytes.slice( 0, policy.maxInspectionBytes, ); const matchedRule = policy.acceptedSignatures.find( (rule) => rule.mediaType.toLowerCase() === file.detectedMediaType?.toLowerCase() && rule.patterns.some((pattern) => matchesFileBytePattern(inspectedBytes, pattern), ), ); const detectedMediaType = matchedRule?.mediaType ?? null; const signature = file.signature === "MISMATCHED" ? "MISMATCHED" : matchedRule ? "MATCHED" : file.signature === "MATCHED" ? "MISMATCHED" : "UNKNOWN"; const verificationReceipt = signature === "MATCHED" && detectedMediaType !== null ? asFileVerificationReceipt( `verification:${++this.#verificationSequence}`, ) : null; if (verificationReceipt) { this.#verifications.set( verificationReceipt, Object.freeze({ ref: input.ref, policyBindingId: bindingId, mediaType: detectedMediaType!, }), ); } return success({ byteLength: asByteCount(file.bytes.byteLength), reportedMediaType: file.reportedMediaType, detectedMediaType, normalizedExtension: extensionOf(file.displayName), signature, verificationReceipt, }); } async readRange(input: { ref: LocalFileRef; offset: ByteCount; length: ByteCount; signal: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const file = this.#files.get(input.ref); if (!file) { return failure("NOT_FOUND", false, "The selected file is no longer available."); } const end = input.offset + input.length; if ( !Number.isSafeInteger(end) || input.offset > file.bytes.byteLength || end > file.bytes.byteLength ) { return failure("INVALID_INPUT", false, "The requested byte range is invalid."); } return success(file.bytes.slice(input.offset, end)); } async openSource(input: { ref: LocalFileRef; signal: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const file = this.#files.get(input.ref); if (!file) { return failure( "NOT_FOUND", false, "The selected file is no longer available.", ); } const bytes = cloneBytes(file.bytes); return success( Object.freeze({ byteLength: asByteCount(bytes.byteLength), async *stream(signal: AbortSignal) { const streamAbort = cancelled(signal); if (streamAbort) { yield streamAbort; return; } yield success(cloneBytes(bytes)); }, }), ); } release(ref: LocalFileRef): void { this.#invalidateVerifications(ref); this.#files.delete(ref); } verification( receipt: FileVerificationReceipt, ): Readonly<{ ref: LocalFileRef; policyBindingId: string; mediaType: string; byteLength: ByteCount; }> | null { const verification = this.#verifications.get(receipt); if (!verification) return null; const file = this.#files.get(verification.ref); return file ? Object.freeze({ ...verification, byteLength: asByteCount(file.bytes.byteLength), }) : null; } #invalidateVerifications(ref: LocalFileRef): void { for (const [receipt, verification] of this.#verifications) { if (verification.ref === ref) this.#verifications.delete(receipt); } } } export class MemoryTransientPreviewAdapter implements TransientPreviewPort { static readonly #ACTIVE_CONTENT_DENYLIST = new Set([ "application/pdf", "application/xhtml+xml", "application/xml", "image/svg+xml", "text/html", "text/xml", ]); readonly #files: MemoryFileSelectionAdapter; readonly #policies: MemoryBrowserFilePolicyRegistry; readonly #active = new Set(); #sequence = 0; #disposed = false; constructor( files: MemoryFileSelectionAdapter, policies: MemoryBrowserFilePolicyRegistry, ) { this.#files = files; this.#policies = policies; } async create( input: Parameters[0], ): ReturnType { if (this.#disposed) { return failure( "PROVIDER_UNAVAILABLE", false, "The preview adapter has been disposed.", ); } const aborted = cancelled(input.signal); if (aborted) return aborted; const verification = this.#files.verification( input.verificationReceipt, ); const resolvedPolicy = this.#policies.preview( input.policy, input.maxPreviewBytes, ); if (!resolvedPolicy.ok) return resolvedPolicy; const policy = resolvedPolicy.value; if ( !verification || verification.ref !== input.ref || verification.policyBindingId !== policy.bindingId ) { return failure( "POLICY_REJECTED", false, "The preview verification receipt is invalid.", ); } if ( verification.byteLength > policy.maxPreviewBytes ) { return failure( "LIMIT_EXCEEDED", false, "The preview exceeds its byte budget.", ); } if ( !policy.allowedMediaTypes.has(verification.mediaType) || MemoryTransientPreviewAdapter.#ACTIVE_CONTENT_DENYLIST.has( verification.mediaType.toLowerCase(), ) ) { return failure( "POLICY_REJECTED", false, "The file type is not approved for inline preview.", ); } const url = `blob:memory-preview-${++this.#sequence}`; this.#active.add(url); let released = false; return success( Object.freeze({ url, mediaType: verification.mediaType, release: () => { if (released) return; released = true; this.#active.delete(url); }, }), ); } get activeLeaseCount(): number { return this.#active.size; } dispose(): void { if (this.#disposed) return; this.#disposed = true; this.#active.clear(); } } type MemoryUploadState = { expectedBytes: number; parts: Map< number, Readonly<{ offset: number; receipt: ExampleUploadPartReceipt }> >; idempotency: Map< string, Readonly<{ fingerprint: string; receipt: ExampleUploadPartReceipt; }> >; }; /** * Deterministic example for a separately selected, backend-authorized * resumable upload workflow. It is not part of browser-file composition. */ export class MemoryExampleQuarantinedUploadAdapter implements ExampleQuarantinedUploadPort { readonly #sessions = new Map(); #sequence = 0; constructor( private readonly maxUploadBytes: ByteCount, private readonly partSizeBytes: ByteCount, private readonly now: () => number = Date.now, ) {} async create(input: { purpose: string; byteLength: ByteCount; detectedMediaType: string; signal: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; if ( input.purpose.length === 0 || input.detectedMediaType.length === 0 || input.byteLength > this.maxUploadBytes ) { return failure("LIMIT_EXCEEDED", false, "The upload policy rejected the file."); } const sessionId = `upload-${++this.#sequence}`; this.#sessions.set(sessionId, { expectedBytes: input.byteLength, parts: new Map(), idempotency: new Map(), }); return success({ sessionId, partSizeBytes: this.partSizeBytes, maxConcurrency: 2, expiresAt: new Date(this.now() + 15 * 60_000).toISOString(), checksumAlgorithm: "SHA-256", }); } async uploadPart(input: { sessionId: string; partNumber: number; offset: ByteCount; bytes: Uint8Array; checksumSha256: string; idempotencyKey: string; signal: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const session = this.#sessions.get(input.sessionId); if (!session) { return failure( "EXPIRED_RESOURCE", false, "The upload session is no longer available.", ); } const fingerprint = [ input.partNumber, input.offset, input.bytes.byteLength, input.checksumSha256, ].join(":"); const replay = session.idempotency.get(input.idempotencyKey); if (replay) { return replay.fingerprint === fingerprint ? success(replay.receipt) : failure( "CONFLICT", false, "The idempotency key was reused for a different upload part.", ); } if ( input.partNumber < 1 || input.bytes.byteLength === 0 || input.bytes.byteLength > this.partSizeBytes || input.offset + input.bytes.byteLength > session.expectedBytes || input.checksumSha256.length === 0 ) { return failure("INVALID_INPUT", false, "The upload part is invalid."); } if (session.parts.has(input.partNumber)) { return failure("CONFLICT", false, "The upload part already exists."); } const receipt = Object.freeze({ partNumber: input.partNumber, acceptedBytes: asByteCount(input.bytes.byteLength), checksumSha256: input.checksumSha256, }); session.parts.set(input.partNumber, { offset: input.offset, receipt, }); session.idempotency.set(input.idempotencyKey, { fingerprint, receipt, }); return success(receipt); } async complete(input: { sessionId: string; parts: ReadonlyArray; signal: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const session = this.#sessions.get(input.sessionId); if (!session) { return failure( "EXPIRED_RESOURCE", false, "The upload session is no longer available.", ); } const partNumbers = new Set(input.parts.map((part) => part.partNumber)); const acceptedBytes = input.parts.reduce( (sum, part) => sum + part.acceptedBytes, 0, ); const matches = input.parts.every( (part) => session.parts.get(part.partNumber)?.receipt.checksumSha256 === part.checksumSha256, ); const storedParts = Array.from(session.parts.values()).sort( (left, right) => left.offset - right.offset, ); let nextOffset = 0; const contiguous = storedParts.every((part) => { if (part.offset !== nextOffset) return false; nextOffset += part.receipt.acceptedBytes; return true; }); if ( !matches || partNumbers.size !== input.parts.length || input.parts.length !== session.parts.size || !contiguous || nextOffset !== session.expectedBytes || acceptedBytes !== session.expectedBytes ) { return failure( "INTEGRITY_FAILED", false, "The uploaded parts did not pass final verification.", ); } this.#sessions.delete(input.sessionId); return success({ resourceId: `resource-${input.sessionId}`, state: "QUARANTINED", }); } async abort( sessionId: string, signal?: AbortSignal, ): Promise> { const aborted = cancelled(signal); if (aborted) return aborted; this.#sessions.delete(sessionId); return success(undefined); } } const windowsDeviceNames = new Set([ "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", ]); function sanitizeFileNameCharacter(character: string): string { const codePoint = character.codePointAt(0) ?? 0; const control = codePoint <= 31 || codePoint === 127; const bidiControl = (codePoint >= 0x202a && codePoint <= 0x202e) || (codePoint >= 0x2066 && codePoint <= 0x2069); if (control || bidiControl) return ""; return character === ":" ? "_" : character; } export function sanitizeDownloadFileName( candidate: string, fallback = "download.bin", ): string { const normalized = candidate.normalize("NFC"); const pathSegments = normalized.split(/[/\\]/); const leafName = pathSegments.at(-1) ?? normalized; const cleaned = Array.from(leafName) .map(sanitizeFileNameCharacter) .join("") .trim() .replace(/^[. ]+|[. ]+$/g, "") .replace(/_+/g, "_"); const bounded = Array.from(cleaned).slice(0, 180).join(""); const stem = bounded.split(".")[0]?.toUpperCase() ?? ""; if ( bounded.length === 0 || bounded === "." || bounded === ".." || windowsDeviceNames.has(stem) ) { return fallback; } return bounded; } export class RecordingDownloadDeliveryAdapter implements DownloadDeliveryPort { readonly receipts: Array< Readonly<{ fileName: string; strategy: string; bytesWritten: number }> > = []; readonly #policies: MemoryBrowserFilePolicyRegistry; readonly #resolveBrowserManaged: BrowserManagedDownloadCapabilityResolver["resolve"]; readonly #now: () => number; #sequence = 0; constructor( policies: MemoryBrowserFilePolicyRegistry, browserManagedCapabilities: BrowserManagedDownloadCapabilityResolver, now: () => number = Date.now, ) { this.#policies = policies; this.#resolveBrowserManaged = browserManagedCapabilities.resolve.bind( browserManagedCapabilities, ); this.#now = now; } async deliver( input: Parameters[0], ): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const resolvedPolicy = this.#policies.download(input.policy, { maxTransferBytes: input.maxTransferBytes, maxBufferedBytes: input.maxBufferedBytes, }); if (!resolvedPolicy.ok) return resolvedPolicy; const policy = resolvedPolicy.value; const sanitized = sanitizeDownloadFileName(input.suggestedFileName); const safeExtension = policy.safeExtension.toLowerCase(); const fileName = sanitized.toLowerCase().endsWith(safeExtension) ? sanitized : `${sanitized.replace(/\.[^.]+$/, "")}${safeExtension}`; const transferId = `download-${++this.#sequence}`; if ( (policy.strategy === "BROWSER_MANAGED" && input.source.kind !== "BROWSER_MANAGED_RESOURCE") || (policy.strategy !== "BROWSER_MANAGED" && input.source.kind === "BROWSER_MANAGED_RESOURCE") ) { return failure( "POLICY_REJECTED", false, "The source is incompatible with the registered strategy.", ); } if ( policy.strategy === "BROWSER_MANAGED" && input.source.kind === "BROWSER_MANAGED_RESOURCE" ) { const capability = this.#resolveBrowserManaged({ resourceId: input.source.resourceId, capabilityReceipt: input.source.capabilityReceipt, }); if (!capability.ok) return capability; if ( capability.value.capabilityReceipt !== input.source.capabilityReceipt || capability.value.resourceId !== input.source.resourceId || capability.value.mediaType !== policy.mediaType || capability.value.safeExtension !== policy.safeExtension || capability.value.maxBytes > policy.maxTransferBytes || capability.value.expiresAtEpochMs <= this.#now() || (policy.integrity === "REQUIRED" && !capability.value.expectedSha256) ) { return failure( capability.value.expiresAtEpochMs <= this.#now() ? "EXPIRED_RESOURCE" : "POLICY_REJECTED", false, "The server-bound download capability is invalid.", ); } this.receipts.push({ fileName, strategy: policy.strategy, bytesWritten: 0, }); return success({ kind: "BROWSER_HANDOFF", transferId }); } if (input.source.kind === "AUTHORIZED_STREAM_RESOURCE") { return failure( "UNSUPPORTED", false, "This fake cannot stream an authorized server resource.", ); } if (input.source.kind !== "GENERATED") { return failure( "POLICY_REJECTED", false, "The download source is incompatible with this fake.", ); } const expectedSha256 = input.source.expectedSha256; if (policy.integrity === "REQUIRED" && !expectedSha256) { return failure( "POLICY_REJECTED", false, "The registered download policy requires integrity.", ); } if ( policy.strategy === "BOUNDED_OBJECT_URL" && input.source.bytes.byteLength !== null && input.source.bytes.byteLength > policy.maxBufferedBytes ) { return failure( "LIMIT_EXCEEDED", false, "The generated download exceeds the buffering budget.", ); } let bytesWritten = 0; const integrityChunks: Uint8Array[] = []; for await (const chunkResult of input.source.bytes.stream( input.signal, )) { if (!chunkResult.ok) return chunkResult; const chunk = chunkResult.value; const duringTransfer = cancelled(input.signal); if (duringTransfer) return duringTransfer; bytesWritten += chunk.byteLength; if (bytesWritten > policy.maxTransferBytes) { return failure( "LIMIT_EXCEEDED", false, "The generated download exceeds the transfer budget.", ); } if ( input.source.bytes.byteLength !== null && bytesWritten > input.source.bytes.byteLength ) { return failure( "INTEGRITY_FAILED", false, "The generated download exceeded its declared size.", ); } if (expectedSha256) integrityChunks.push(cloneBytes(chunk)); input.onProgress({ phase: "TRANSFERRING", transferredBytes: bytesWritten, totalBytes: input.source.bytes.byteLength, }); } if ( input.source.bytes.byteLength !== null && bytesWritten !== input.source.bytes.byteLength ) { return failure( "INTEGRITY_FAILED", false, "The generated download did not match its declared size.", ); } if (expectedSha256) { const beforeVerification = cancelled(input.signal); if (beforeVerification) return beforeVerification; input.onProgress({ phase: "VERIFYING", transferredBytes: bytesWritten, totalBytes: input.source.bytes.byteLength, }); const actualSha256 = await sha256Hex(concatenateBytes(integrityChunks)); if (actualSha256 !== expectedSha256.toLowerCase()) { return failure( "INTEGRITY_FAILED", false, "The generated download failed integrity verification.", ); } } this.receipts.push({ fileName, strategy: policy.strategy, bytesWritten, }); return success({ kind: "SAVED", transferId, bytesWritten: asByteCount(bytesWritten), integrity: expectedSha256 ? "VERIFIED" : "NOT_PROVIDED", }); } } function concatenateBytes(chunks: ReadonlyArray): Uint8Array { const totalBytes = chunks.reduce((total, chunk) => total + chunk.byteLength, 0); const combined = new Uint8Array(totalBytes); let offset = 0; for (const chunk of chunks) { combined.set(chunk, offset); offset += chunk.byteLength; } return combined; } type MemoryStructuredOfflineStoreOptions = Readonly<{ maxBytes: number; now?: () => number; initialStatus?: OfflineStoreStatus; }>; export class MemoryStructuredOfflineStore implements StructuredOfflineStore, OfflineStoreMaintenancePort { readonly #records = new Map>(); readonly #receipts = new Map< string, Readonly<{ fingerprint: string; receipt: OfflineCommitReceipt }> >(); readonly #now: () => number; readonly #maxBytes: number; #status: OfflineStoreStatus; #onLifecycle: ((event: OfflineStoreLifecycleEvent) => void) | null = null; #commitSequence = 0; constructor(options: MemoryStructuredOfflineStoreOptions) { this.#maxBytes = options.maxBytes; this.#now = options.now ?? Date.now; this.#status = options.initialStatus ?? Object.freeze({ kind: "CLOSED", reason: "DISPOSED", }); } async open(input: { partitionKey: string; signal?: AbortSignal; onLifecycle(event: OfflineStoreLifecycleEvent): void; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; if (input.partitionKey.length === 0) { return failure("INVALID_INPUT", false, "The storage partition is invalid."); } this.#onLifecycle = input.onLifecycle; if (this.#status.kind === "UPGRADE_BLOCKED") { input.onLifecycle({ kind: "STATUS_CHANGED", status: this.#status }); return success(this.#status); } this.#status = Object.freeze({ kind: "READY", persistence: "BEST_EFFORT", }); input.onLifecycle({ kind: "STATUS_CHANGED", status: this.#status }); return success(this.#status); } async read( id: string, signal?: AbortSignal, ): Promise | null>> { const aborted = cancelled(signal); if (aborted) return aborted; if (!isReadableStatus(this.#status)) { return failure("PROVIDER_UNAVAILABLE", false, "The offline store is not open."); } const record = this.#records.get(id); if ( record?.expiresAtEpochMs !== null && record?.expiresAtEpochMs !== undefined && record.expiresAtEpochMs <= this.#now() ) { return success(null); } return success(record ? structuredClone(record) : null); } async page(input: { cursor?: string; limit: number; includeExpired?: boolean; signal?: AbortSignal; }): Promise>> { const aborted = cancelled(input.signal); if (aborted) return aborted; if (!isReadableStatus(this.#status)) { return failure("PROVIDER_UNAVAILABLE", false, "The offline store is not open."); } if (!Number.isInteger(input.limit) || input.limit < 1 || input.limit > 500) { return failure("LIMIT_EXCEEDED", false, "The offline page limit is invalid."); } const offset = input.cursor === undefined ? 0 : Number(input.cursor); if (!Number.isSafeInteger(offset) || offset < 0) { return failure("INVALID_INPUT", false, "The offline cursor is invalid."); } const now = this.#now(); const records = Array.from(this.#records.values()) .filter( (record) => input.includeExpired === true || record.expiresAtEpochMs === null || record.expiresAtEpochMs > now, ) .sort((left, right) => left.id.localeCompare(right.id)); const selected = records .slice(offset, offset + input.limit) .map((record) => structuredClone(record)); const nextOffset = offset + selected.length; return success({ records: selected, nextCursor: nextOffset < records.length ? String(nextOffset) : null, }); } async commit(input: { idempotencyKey: string; mutations: ReadonlyArray>; signal?: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; if (!isWritableStatus(this.#status)) { return failure("PROVIDER_UNAVAILABLE", false, "The offline store is read-only."); } if (input.idempotencyKey.length === 0 || input.mutations.length === 0) { return failure("INVALID_INPUT", false, "The offline commit is empty."); } let fingerprint: string; try { fingerprint = JSON.stringify(input.mutations); } catch { return failure( "CORRUPT_DATA", false, "The offline mutation cannot be encoded safely.", ); } const replay = this.#receipts.get(input.idempotencyKey); if (replay) { return replay.fingerprint === fingerprint ? success({ ...replay.receipt, replayed: true }) : failure( "CONFLICT", false, "The idempotency key was reused for a different offline commit.", ); } const draft = new Map( Array.from(this.#records, ([id, record]) => [ id, structuredClone(record), ]), ); const revisions: Record = {}; const now = this.#now(); try { for (const mutation of input.mutations) { const current = draft.get(mutation.id); if (!revisionAllows(mutation.revision, current?.revision)) { return failure( "CONFLICT", false, "The offline record changed before the commit.", ); } if (mutation.kind === "DELETE") { draft.delete(mutation.id); revisions[mutation.id] = null; continue; } const revision = (current?.revision ?? 0) + 1; draft.set( mutation.id, structuredClone({ id: mutation.id, revision, payloadVersion: mutation.payloadVersion, createdAtEpochMs: current?.createdAtEpochMs ?? now, updatedAtEpochMs: now, expiresAtEpochMs: mutation.expiresAtEpochMs, value: mutation.value, }), ); revisions[mutation.id] = revision; } } catch { return failure( "CORRUPT_DATA", false, "The offline value cannot be persisted safely.", ); } if (estimateRecordBytes(draft) > this.#maxBytes) { return failure( "QUOTA_EXCEEDED", true, "The offline storage budget was exceeded.", ); } const lastAbortCheck = cancelled(input.signal); if (lastAbortCheck) return lastAbortCheck; this.#records.clear(); for (const [id, record] of draft) this.#records.set(id, record); const receipt = Object.freeze({ commitId: `commit-${++this.#commitSequence}`, revisions: Object.freeze(revisions), replayed: false, }); this.#receipts.set(input.idempotencyKey, { fingerprint, receipt }); return success(receipt); } async clearPartition(input: { reason: "LOGOUT" | "ACCOUNT_DELETION" | "USER_REQUEST"; signal?: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; this.#records.clear(); this.#receipts.clear(); return success(undefined); } status(): OfflineStoreStatus { return this.#status; } close(): void { this.#onLifecycle = null; this.#status = Object.freeze({ kind: "CLOSED", reason: "DISPOSED" }); } async resumeDataMigration(input: { migrationId: string; maxRecords: number; timeBudgetMs: number; signal?: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; if ( input.migrationId.length === 0 || input.maxRecords < 1 || input.timeBudgetMs < 1 ) { return failure("INVALID_INPUT", false, "The migration budget is invalid."); } const progress = Object.freeze({ migrationId: input.migrationId, state: "COMPLETED" as const, processedCount: Math.min(this.#records.size, input.maxRecords), remainingEstimate: 0, }); this.#onLifecycle?.({ kind: "MIGRATION_PROGRESS", ...progress }); return success(progress); } async purgeExpired(input: { maxRecords: number; nowEpochMs: number; signal?: AbortSignal; }): Promise>> { const aborted = cancelled(input.signal); if (aborted) return aborted; if (input.maxRecords < 1) { return failure("INVALID_INPUT", false, "The purge budget is invalid."); } let purgedCount = 0; for (const [id, record] of this.#records) { if (purgedCount >= input.maxRecords) break; if ( record.expiresAtEpochMs !== null && record.expiresAtEpochMs <= input.nowEpochMs ) { this.#records.delete(id); purgedCount += 1; } } return success({ purgedCount }); } } function revisionAllows( guard: StructuredOfflineMutation["revision"], currentRevision: number | undefined, ): boolean { if (guard.kind === "ANY") return true; if (guard.kind === "MUST_NOT_EXIST") return currentRevision === undefined; return currentRevision === guard.revision; } function estimateRecordBytes( records: ReadonlyMap>, ): number { try { return new TextEncoder().encode(JSON.stringify(Array.from(records.values()))) .byteLength; } catch { return Number.POSITIVE_INFINITY; } } export class MemoryStorageDurabilityAdapter implements StorageDurabilityPort { #persisted: boolean; constructor( private readonly usageBytes: number | null, private readonly quotaBytes: number | null, private readonly persistenceDecision: "GRANTED" | "DENIED", persisted = false, ) { this.#persisted = persisted; } async inspect( signal?: AbortSignal, ): Promise> { return ( cancelled(signal) ?? success({ usageBytes: this.usageBytes, quotaBytes: this.quotaBytes, persisted: this.#persisted, pressure: storagePressure( this.usageBytes, this.quotaBytes, ), }) ); } async requestPersistence(input: { reason: "PROTECT_UNSYNCED_USER_DATA"; userInitiated: true; signal?: AbortSignal; }): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; this.#persisted = this.persistenceDecision === "GRANTED"; return success(this.persistenceDecision); } } function storagePressure( usageBytes: number | null, quotaBytes: number | null, ): StorageEstimate["pressure"] { if ( usageBytes === null || quotaBytes === null || !Number.isFinite(usageBytes) || !Number.isFinite(quotaBytes) || usageBytes < 0 || quotaBytes <= 0 ) { return "UNKNOWN"; } const ratio = usageBytes / quotaBytes; if (ratio >= 0.85) return "CRITICAL"; if (ratio >= 0.7) return "PRESSURE"; return "NORMAL"; } type MemoryObject = Readonly<{ descriptor: DurableObjectDescriptor; chunks: ReadonlyArray; }>; type PendingObject = Readonly<{ id: DurableObjectId; phase: "PREPARING" | "FILES_READY"; }>; export class MemoryDurableObjectStore implements DurableObjectStorePort, DurableObjectMaintenancePort { readonly #objects = new Map(); readonly #pending = new Map(); #failurePhase: PendingObject["phase"] | null = null; constructor( private readonly maxObjectBytes: ByteCount, private readonly backend: "OPFS" | "INDEXEDDB_BLOB" | "NONE" = "OPFS", ) {} failNextPutAfter(phase: PendingObject["phase"]): void { this.#failurePhase = phase; } async capabilities(signal?: AbortSignal) { return ( cancelled(signal) ?? success({ backend: this.backend, persistence: "BEST_EFFORT" as const, maxObjectBytes: this.maxObjectBytes, }) ); } async put( input: Parameters[0], ): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; if (this.backend === "NONE") { return failure("UNSUPPORTED", false, "Durable object storage is unsupported."); } if (input.declaredByteLength > this.maxObjectBytes) { return failure("LIMIT_EXCEEDED", false, "The object exceeds its byte budget."); } const current = this.#objects.get(input.id); if ( (input.expectedGeneration === null && current !== undefined) || (input.expectedGeneration !== null && current?.descriptor.generation !== input.expectedGeneration) ) { return failure("CONFLICT", false, "The object generation changed."); } this.#pending.set(input.id, { id: input.id, phase: "PREPARING" }); if (this.#failurePhase === "PREPARING") { this.#failurePhase = null; return failure( "PROVIDER_UNAVAILABLE", true, "The object writer stopped after journal preparation.", ); } const chunks: Uint8Array[] = []; let totalBytes = 0; try { for await (const chunkResult of input.source) { if (!chunkResult.ok) return chunkResult; const chunk = chunkResult.value; const duringWrite = cancelled(input.signal); if (duringWrite) return duringWrite; if (!(chunk instanceof Uint8Array)) { return failure( "CORRUPT_DATA", false, "The object source emitted an invalid byte chunk.", ); } totalBytes += chunk.byteLength; if ( totalBytes > input.declaredByteLength || totalBytes > this.maxObjectBytes ) { return failure( "INTEGRITY_FAILED", false, "The object exceeded its declared size.", ); } chunks.push(cloneBytes(chunk)); input.onProgress({ phase: "TRANSFERRING", transferredBytes: totalBytes, totalBytes: input.declaredByteLength, }); } } catch { return failure( "NOT_READABLE", true, "The object byte source could not be read.", ); } if (totalBytes !== input.declaredByteLength) { return failure( "INTEGRITY_FAILED", false, "The object did not match its declared size.", ); } this.#pending.set(input.id, { id: input.id, phase: "FILES_READY" }); if (this.#failurePhase === "FILES_READY") { this.#failurePhase = null; return failure( "PROVIDER_UNAVAILABLE", true, "The object writer stopped before logical commit.", ); } const generation = asObjectGeneration( (current?.descriptor.generation ?? 0) + 1, ); const chunkDigests = await Promise.all(chunks.map(sha256Hex)); const chunkSizeBytes = Math.max( 0, ...chunks.map((chunk) => chunk.byteLength), ); const canonicalTree = new TextEncoder().encode( [ totalBytes, chunkSizeBytes, ...chunks.map( (chunk, index) => `${chunk.byteLength}:${chunkDigests[index] ?? "missing"}`, ), ].join(":"), ); const descriptor: DurableObjectDescriptor = Object.freeze({ id: input.id, generation, byteLength: asByteCount(totalBytes), mediaType: input.mediaType, integrity: Object.freeze({ algorithm: "SHA-256-TREE-V1", rootDigest: await sha256Hex(canonicalTree), chunkSizeBytes: asByteCount(chunkSizeBytes), }), dataClass: input.dataClass, retention: input.retention, }); this.#objects.set(input.id, { descriptor, chunks }); this.#pending.delete(input.id); return success(descriptor); } async open( id: DurableObjectId, signal?: AbortSignal, ): Promise> { const aborted = cancelled(signal); if (aborted) return aborted; const object = this.#objects.get(id); if (!object) { return failure("NOT_FOUND", false, "The durable object does not exist."); } const chunks = object.chunks.map(cloneBytes); return success({ descriptor: object.descriptor, chunks: chunksFrom(chunks), }); } async remove( input: Parameters[0], ): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const current = this.#objects.get(input.id); if (!current) { return failure("NOT_FOUND", false, "The durable object does not exist."); } if (current.descriptor.generation !== input.expectedGeneration) { return failure("CONFLICT", false, "The object generation changed."); } this.#objects.delete(input.id); return success(undefined); } async reconcile( input: Parameters[0], ): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; if (input.maxEntries < 1 || input.timeBudgetMs < 1) { return failure("INVALID_INPUT", false, "The recovery budget is invalid."); } const pending = Array.from(this.#pending.keys()).slice(0, input.maxEntries); for (const id of pending) this.#pending.delete(id); return success({ resumedCount: 0, purgedCount: pending.length, quarantinedCount: 0, nextCursor: this.#pending.size > 0 ? String(pending.length) : null, }); } } async function sha256Hex(bytes: Uint8Array): Promise { const buffer = new ArrayBuffer(bytes.byteLength); new Uint8Array(buffer).set(bytes); const digest = await crypto.subtle.digest("SHA-256", buffer); return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0"), ).join(""); } async function* chunksFrom( chunks: ReadonlyArray, ): AsyncIterable> { for (const chunk of chunks) yield success(cloneBytes(chunk)); } type MemoryCacheRelease = Readonly<{ manifestDigest: string; entries: ReadonlyMap; }>; type MemoryPublicResponseCacheOptions = Readonly<{ origin: string; maxEntryBytes: number; allowedMediaTypes: ReadonlySet; forbiddenPaths?: ReadonlyArray; }>; export class MemoryPublicResponseCache implements PublicResponseCacheAdmin { readonly #releases = new Map(); readonly #origin: string; readonly #maxEntryBytes: number; readonly #allowedMediaTypes: ReadonlySet; readonly #forbiddenPaths: ReadonlyArray; #activeReleaseId: string | null = null; #previousReleaseId: string | null = null; constructor(options: MemoryPublicResponseCacheOptions) { this.#origin = new URL(options.origin).origin; this.#maxEntryBytes = options.maxEntryBytes; this.#allowedMediaTypes = options.allowedMediaTypes; this.#forbiddenPaths = options.forbiddenPaths ?? [ "/config.json", "/release-manifest.json", "/api/", "/auth/", "/user/", "/tenant/", ]; } async stageRelease( input: Parameters[0], ): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; if ( input.releaseId.length === 0 || input.manifestDigest.length === 0 || input.entries.length === 0 ) { return failure("INVALID_INPUT", false, "The cache release is invalid."); } const staged = new Map(); for (const entry of input.entries) { const key = this.#cacheKey(entry.url); if ( key === null || entry.byteLength > this.#maxEntryBytes || entry.integritySha256.length === 0 || entry.requestCredentials !== "OMIT" || entry.dataClass !== "PUBLIC" || !this.#allowedMediaTypes.has(entry.mediaType) || staged.has(key) ) { return failure( "POLICY_REJECTED", false, "A public cache entry violates the cache policy.", ); } staged.set(key, Object.freeze({ ...entry, url: key })); } this.#releases.set(input.releaseId, { manifestDigest: input.manifestDigest, entries: staged, }); return success(undefined); } async activateRelease( input: Parameters[0], ): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; const candidate = this.#releases.get(input.releaseId); if ( !candidate || candidate.manifestDigest !== input.manifestDigest || this.#activeReleaseId !== input.expectedPreviousReleaseId ) { return failure( "CONFLICT", false, "The cache candidate is incomplete or stale.", ); } this.#previousReleaseId = this.#activeReleaseId; this.#activeReleaseId = input.releaseId; return success(undefined); } async lookup( input: Parameters[0], ): Promise> { const aborted = cancelled(input.signal); if (aborted) return aborted; if ( input.requestCredentials !== "OMIT" || input.hasAuthorization !== false ) { return success({ kind: "MISS", reason: "POLICY_REJECTED" }); } if (!this.#activeReleaseId) { return success({ kind: "MISS", reason: "NO_ACTIVE_RELEASE" }); } const key = this.#cacheKey(input.url); if (!key) return success({ kind: "MISS", reason: "POLICY_REJECTED" }); const entry = this.#releases .get(this.#activeReleaseId) ?.entries.get(key); return entry ? success({ kind: "HIT", releaseId: this.#activeReleaseId, entry, }) : success({ kind: "MISS", reason: "NOT_FOUND" }); } async inspect( signal?: AbortSignal, ): Promise> { const aborted = cancelled(signal); if (aborted) return aborted; const candidateReleaseIds = Array.from(this.#releases.keys()).filter( (releaseId) => releaseId !== this.#activeReleaseId && releaseId !== this.#previousReleaseId, ); const ownedBytes = Array.from(this.#releases.values()).reduce( (total, release) => total + Array.from(release.entries.values()).reduce( (releaseTotal, entry) => releaseTotal + entry.byteLength, 0, ), 0, ); return success({ activeReleaseId: this.#activeReleaseId, previousReleaseId: this.#previousReleaseId, candidateReleaseIds, ownedBytes, }); } async rollback( signal?: AbortSignal, ): Promise> { const aborted = cancelled(signal); if (aborted) return aborted; if (!this.#previousReleaseId) { return failure("NOT_FOUND", false, "No previous cache release is available."); } const active = this.#activeReleaseId; this.#activeReleaseId = this.#previousReleaseId; this.#previousReleaseId = active; return success(undefined); } async deleteOwned( input: Parameters[0], ): Promise>> { const aborted = cancelled(input.signal); if (aborted) return aborted; let deletedCount = 0; if (input.roles.includes("CANDIDATE")) { for (const releaseId of Array.from(this.#releases.keys())) { if ( releaseId !== this.#activeReleaseId && releaseId !== this.#previousReleaseId ) { this.#releases.delete(releaseId); deletedCount += 1; } } } if ( input.roles.includes("PREVIOUS") && this.#previousReleaseId !== null ) { this.#releases.delete(this.#previousReleaseId); this.#previousReleaseId = null; deletedCount += 1; } return success({ deletedCount }); } #cacheKey(value: string): string | null { try { const url = new URL(value, this.#origin); if ( url.origin !== this.#origin || this.#forbiddenPaths.some( (path) => url.pathname === path || (path.endsWith("/") && url.pathname.startsWith(path)), ) ) { return null; } url.hash = ""; return url.href; } catch { return null; } } }