@ai-matrx/data 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/dist/files/tus.cjs +245 -0
- package/dist/files/tus.cjs.map +1 -0
- package/dist/files/tus.d.cts +106 -0
- package/dist/files/tus.d.ts +106 -0
- package/dist/files/tus.js +214 -0
- package/dist/files/tus.js.map +1 -0
- package/dist/files.cjs +46 -6
- package/dist/files.cjs.map +1 -1
- package/dist/files.d.cts +62 -2
- package/dist/files.d.ts +62 -2
- package/dist/files.js +46 -6
- package/dist/files.js.map +1 -1
- package/dist/types.cjs +19 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +53855 -0
- package/dist/types.d.ts +53855 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/package.json +24 -1
package/dist/files.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/credentials.ts","../src/files/errors.ts","../src/files/recognize.ts","../src/files/client.ts","../src/files/session.ts","../src/files/heal.ts"],"sourcesContent":["/**\n * The credentials port — the ONE credential→header mapping for every client\n * package (DATA-PACKAGE-DESIGN.md \"The credentials port\"; API-key lane ratified\n * as C16, 2026-08-29).\n *\n * Three credential kinds, one mapping, in exactly one place:\n *\n * - `user` — a Supabase JWT: `Authorization: Bearer <jwt>` (both lanes)\n * - `guest` — a fingerprint: `X-Fingerprint-ID: <id>` (API lane only)\n * - `api_key` — an AI Matrx API key: `Authorization: Bearer mx_live_...`.\n * The server's ONE AuthMiddleware disambiguates by shape (`mx_` prefix vs\n * three-dot JWT), so both `user` and `api_key` ride the same header.\n *\n * No polling, no readiness loop: readiness is the port's promise.\n */\n\n/** A credential a Matrx client can present. */\nexport type MatrxCredential =\n | { kind: \"user\"; accessToken: string }\n | { kind: \"guest\"; fingerprintId: string }\n | { kind: \"api_key\"; apiKey: string };\n\n/**\n * The async source of the current credential. `get()` resolves when the host\n * knows its auth state (null = anonymous); `onChange` lets transports refresh\n * cached headers when the credential rotates.\n */\nexport interface CredentialsPort {\n get(): Promise<MatrxCredential | null>;\n onChange?(cb: () => void): () => void;\n}\n\n/** Prefix that marks a bearer token as an AI Matrx API key, never a JWT. */\nexport const API_KEY_PREFIX = \"mx_\";\n\n/**\n * Map a credential to the exact request headers the AI Matrx platform expects.\n * This is the single place that knowledge lives — transports spread the result\n * into their request headers and add `X-Organization-Id` from the org context\n * binding separately.\n */\nexport function credentialToHeaders(\n credential: MatrxCredential | null,\n): Record<string, string> {\n if (credential === null) return {};\n switch (credential.kind) {\n case \"user\":\n return { Authorization: `Bearer ${credential.accessToken}` };\n case \"guest\":\n return { \"X-Fingerprint-ID\": credential.fingerprintId };\n case \"api_key\": {\n if (!credential.apiKey.startsWith(API_KEY_PREFIX)) {\n throw new Error(\n `credentialToHeaders: an api_key credential must start with \"${API_KEY_PREFIX}\" ` +\n \"(AI Matrx API keys look like mx_live_...)\",\n );\n }\n return { Authorization: `Bearer ${credential.apiKey}` };\n }\n }\n}\n","/**\n * @ai-matrx/data/files — error taxonomy + the ONE unavailable-state classifier.\n *\n * Ported from matrx-frontend `features/files/handler/errors.ts`. The crucial\n * distinction this lane enforces:\n *\n * - `FileAccessDeniedError` → the user does NOT have access (a proven 403\n * from our own file server). Reject. Never retry.\n * - a transient auth failure on a durable URL → refresh the file session\n * and retry the SAME URL (the client's `recoverLoadError`); never a\n * terminal error.\n *\n * \"Unavailable is a rendered state, not an incident\" (handler law 15): the\n * typed states classify via {@link classifyUnavailable} and render as error\n * panels — this module never logs.\n */\n\nimport type { MediaUnavailableReason } from \"./types\";\n\nexport type FileErrorCode =\n | \"access_denied\"\n | \"not_found\"\n | \"deleted\"\n | \"share_link_invalid\"\n | \"external_fetch_failed\"\n | \"cors_blocked\"\n | \"mime_unknown\"\n | \"upload_failed\"\n | \"upload_cancelled\"\n | \"quota_exceeded\"\n | \"in_flight\"\n | \"internal\";\n\nexport class FileError extends Error {\n readonly code: FileErrorCode;\n readonly fileId?: string | undefined;\n override readonly cause?: unknown;\n\n constructor(\n code: FileErrorCode,\n message: string,\n opts?: { fileId?: string | undefined; cause?: unknown },\n ) {\n super(message);\n this.name = \"FileError\";\n this.code = code;\n this.fileId = opts?.fileId;\n this.cause = opts?.cause;\n }\n}\n\nexport class FileAccessDeniedError extends FileError {\n constructor(\n // A real 403 from our own file server — a proven refusal, not a guess.\n message = \"This file isn't available to you.\",\n opts?: { fileId?: string | undefined },\n ) {\n super(\"access_denied\", message, opts);\n this.name = \"FileAccessDeniedError\";\n }\n}\n\nexport class FileNotFoundError extends FileError {\n // A 404 from the file server: the server could not retrieve the file —\n // NOT that it was deleted (that is FileDeletedError) and not that it\n // never existed.\n constructor(\n message = \"We couldn't retrieve this file.\",\n opts?: { fileId?: string | undefined },\n ) {\n super(\"not_found\", message, opts);\n this.name = \"FileNotFoundError\";\n }\n}\n\nexport class FileDeletedError extends FileError {\n constructor(message = \"File is in trash\", opts?: { fileId?: string | undefined }) {\n super(\"deleted\", message, opts);\n this.name = \"FileDeletedError\";\n }\n}\n\nexport class ShareLinkInvalidError extends FileError {\n constructor(message = \"Share link is invalid, expired, or revoked\") {\n super(\"share_link_invalid\", message);\n this.name = \"ShareLinkInvalidError\";\n }\n}\n\nexport class ExternalFetchError extends FileError {\n constructor(message = \"Failed to fetch external URL\", opts?: { cause?: unknown }) {\n super(\"external_fetch_failed\", message, opts);\n this.name = \"ExternalFetchError\";\n }\n}\n\nexport class FileUploadError extends FileError {\n constructor(message: string, opts?: { cause?: unknown }) {\n super(\"upload_failed\", message, opts);\n this.name = \"FileUploadError\";\n }\n}\n\n/**\n * Map an HTTP status from OUR file server's byte/record routes onto a typed\n * error. 401 is deliberately NOT access-denied — a 401 on a durable URL is\n * the session-refresh lane, never a proven refusal.\n */\nexport function fileErrorFromStatus(\n status: number,\n fileId?: string,\n): FileError | null {\n if (status === 403)\n return new FileAccessDeniedError(undefined, fileId === undefined ? {} : { fileId });\n if (status === 404)\n return new FileNotFoundError(undefined, fileId === undefined ? {} : { fileId });\n if (status === 410) return new ShareLinkInvalidError();\n return null;\n}\n\n/**\n * The typed-unavailable classifier behind `MediaClient.classifyError`.\n * Matches this package's own classes AND — structurally, by `code` — any\n * host error taxonomy that speaks the same vocabulary (the origin handler's\n * `FileHandlerError` classifies without an instanceof relationship).\n */\nexport function classifyUnavailable(err: unknown): MediaUnavailableReason {\n if (err instanceof FileAccessDeniedError) return \"access_denied\";\n if (err instanceof FileNotFoundError) return \"not_found\";\n if (err instanceof FileDeletedError) return \"deleted\";\n if (err && typeof err === \"object\" && \"code\" in err) {\n const code = (err as { code?: unknown }).code;\n if (code === \"access_denied\" || code === \"not_found\" || code === \"deleted\") {\n return code;\n }\n }\n return \"unknown\";\n}\n","/**\n * @ai-matrx/data/files — URL classification and recognition.\n *\n * Three origin modules collapse here, behavior-verbatim (the D108 scar\n * tissue): matrx-frontend `lib/media/signed-url.ts` (the ONE legacy\n * signed-URL classifier, both AWS dialects), `lib/media/durability.ts`\n * (durable-vs-expiring classification + the fail-closed share guard), and\n * the identity-recovering pieces of `lib/media/our-file-sources.ts`\n * (byte-endpoint promotion, signed-bucket file_id recovery, CDN detection).\n *\n * What generalized at port time: the HOST NAMES. The origin hardcoded\n * `matrxserver.com` / `matrx-user-files.s3`; here the origin list is\n * injected as a VALUE ({@link FileOrigins}) with the production topology as\n * the shipped default. The signed-URL classification itself is\n * host-independent and deliberately non-configurable.\n */\n\nimport type { FileOrigins } from \"./types\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\n\n// ---------------------------------------------------------------------------\n// Signed / expiring URL classification (host-independent, non-configurable)\n// ---------------------------------------------------------------------------\n\n/**\n * Matches either AWS signing dialect (SigV2 query auth, SigV4 presigned).\n * Intentionally broad: presence of any of these query keys means the URL\n * carries a time-limited signature and must be treated as a legacy expiring\n * URL — never durable, never persisted as identity.\n */\nexport const SIGNED_URL_RE =\n /[?&](x-amz-signature|x-amz-credential|x-amz-date|x-amz-expires|awsaccesskeyid|signature|expires)=/i;\n\n/** True when `url` is a legacy signed/expiring URL in either AWS dialect. */\nexport function isSignedUrl(url: string | null | undefined): boolean {\n if (!url) return false;\n return SIGNED_URL_RE.test(url);\n}\n\n/**\n * A revocable share link (`…/share/<token>/download|view|raw`) is never a\n * durable identity — being revocable is the entire POINT of a share link.\n * It carries no signature, so the signed-URL test alone called it durable;\n * that is exactly how D108's screenshots became dead pointers. Kept in\n * parity with the DB twin `public.mtx_is_durable_media_url`.\n */\nconst REVOCABLE_SHARE_URL_RE =\n /\\/share\\/[0-9a-f-]{8,}\\/(download|view|raw)(\\?|$)/i;\n\nexport type MediaUrlKind = \"empty\" | \"durable\" | \"expiring\";\n\n/** Mirror of the DB `mtx_is_durable_media_url` classifier. */\nexport function classifyMediaUrl(url: string | null | undefined): MediaUrlKind {\n if (!url) return \"empty\";\n if (isSignedUrl(url) || REVOCABLE_SHARE_URL_RE.test(url)) return \"expiring\";\n return \"durable\";\n}\n\nexport function isDurableMediaUrl(url: string | null | undefined): boolean {\n return classifyMediaUrl(url) !== \"expiring\";\n}\n\n/**\n * Return a URL only when it is safe to expose through a share/copy action.\n * A presigned object-store URL is a bearer credential; sharing one is\n * banned. FAILS CLOSED: the caller mints a canonical share link (or uses an\n * internal viewer URL) when this returns null.\n */\nexport function shareableMediaUrl(\n url: string | null | undefined,\n): string | null {\n if (!url || !isDurableMediaUrl(url)) return null;\n return url;\n}\n\n// ---------------------------------------------------------------------------\n// Origin-aware recognition (the injected origin list)\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_FILE_ENDPOINT_HOST_RE =\n /(^|\\.)matrxserver\\.com$|^localhost$|^127\\.0\\.0\\.1$/i;\nconst DEFAULT_USER_FILES_HOST_RE =\n /(^|\\.)matrx-user-files\\.s3|s3[.-].*amazonaws\\.com/i;\nconst DEFAULT_CDN_HOST_RE = /(^|\\.)cdn\\.matrxserver\\.com$/i;\n\nconst FILE_ENDPOINT_PATH_RE =\n /^\\/(?:files\\/([0-9a-f-]{36})\\/download|media\\/([0-9a-f-]{36})\\/v\\/[^/]+)\\/?$/i;\n\nexport interface FileUrlRecognizer {\n /**\n * `{base}/files/{file_id}/download[?…]` or `{base}/media/{file_id}/v/{class}`\n * on one of OUR hosts — an IDENTITY in disguise (QA F2): rendered as an\n * opaque external URL it fails for everyone whose file-session cookie\n * isn't fresh, and canvas consumers fetch it with no Authorization header.\n * Returns the file_id so callers promote it to the file_id lane.\n */\n fileIdFromFileEndpointUrl(url: string): string | null;\n /**\n * Recover the file_id from a legacy signed user-files URL\n * (`…/{user_id}/{file_id}?X-Amz-…`) so a durable URL can be re-minted\n * from identity forever.\n */\n fileIdFromUserFilesUrl(url: string): string | null;\n /** Permanent public CDN URL (injected CDN hosts + the generic heuristic). */\n looksLikeCdnUrl(url: string): boolean;\n /** True when the URL points at any of OUR recognized origins. */\n isOurUrl(url: string): boolean;\n}\n\nexport function createFileUrlRecognizer(\n origins: FileOrigins = {},\n): FileUrlRecognizer {\n const endpointHosts = origins.fileEndpointHosts ?? DEFAULT_FILE_ENDPOINT_HOST_RE;\n const userFilesHosts = origins.userFilesHosts ?? DEFAULT_USER_FILES_HOST_RE;\n const cdnHosts = origins.cdnHosts ?? DEFAULT_CDN_HOST_RE;\n\n function parse(url: string): URL | null {\n try {\n return new URL(url);\n } catch {\n return null;\n }\n }\n\n function fileIdFromFileEndpointUrl(url: string): string | null {\n const u = parse(url);\n if (!u || !endpointHosts.test(u.hostname)) return null;\n const m = u.pathname.match(FILE_ENDPOINT_PATH_RE);\n const candidate = m ? (m[1] ?? m[2]) : null;\n return candidate && isUuidShape(candidate) ? candidate : null;\n }\n\n function fileIdFromUserFilesUrl(url: string): string | null {\n const u = parse(url);\n if (!u || !userFilesHosts.test(u.hostname)) return null;\n const segs = u.pathname.split(\"/\").filter(Boolean); // [user_id, file_id, …]\n const candidate = segs[1] ?? segs[segs.length - 1];\n return candidate && isUuidShape(candidate) ? candidate : null;\n }\n\n function looksLikeCdnUrl(url: string): boolean {\n const u = parse(url);\n if (u && cdnHosts.test(u.hostname)) return true;\n // The origin's generic heuristic (media-client `looksLikeCdnUrl`).\n return url.startsWith(\"https://cdn.\") || url.includes(\"/cdn/\");\n }\n\n function isOurUrl(url: string): boolean {\n return (\n fileIdFromFileEndpointUrl(url) !== null ||\n fileIdFromUserFilesUrl(url) !== null ||\n (() => {\n const u = parse(url);\n return !!u && cdnHosts.test(u.hostname);\n })()\n );\n }\n\n return { fileIdFromFileEndpointUrl, fileIdFromUserFilesUrl, looksLikeCdnUrl, isOurUrl };\n}\n\n// ---------------------------------------------------------------------------\n// Mime sniffing (origin: our-file-sources.ts)\n// ---------------------------------------------------------------------------\n\nconst EXT_MIME: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n m4a: \"audio/mp4\",\n ogg: \"audio/ogg\",\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mov: \"video/quicktime\",\n csv: \"text/csv\",\n json: \"application/json\",\n md: \"text/markdown\",\n txt: \"text/plain\",\n html: \"text/html\",\n};\n\n/**\n * Best-effort mime from a URL: prefer an explicit `response-content-type`\n * query param (our legacy signed URLs carry it), else map a path extension.\n * Returns null when nothing is recognizable — the caller then hydrates the\n * file record for the real type.\n */\nexport function mimeFromUrl(url: string): string | null {\n try {\n const u = new URL(url);\n const ct = u.searchParams.get(\"response-content-type\");\n if (ct) return decodeURIComponent(ct);\n } catch {\n // fall through to extension sniffing on the raw string\n }\n const path = url.split(/[?#]/)[0] ?? \"\";\n const ext = path.match(/\\.([a-z0-9]+)$/i)?.[1]?.toLowerCase();\n return ext ? (EXT_MIME[ext] ?? null) : null;\n}\n\n/**\n * Best-effort file NAME from a URL: prefer the `filename=\"…\"` carried in our\n * legacy signed URLs' `response-content-disposition`, else the last path\n * segment when it actually looks like a name (has an extension). Returns null\n * otherwise — a UUID path segment is an id, not a name, and must never be\n * shown as one.\n */\nexport function fileNameFromUrl(url: string): string | null {\n try {\n const u = new URL(url);\n const disposition = u.searchParams.get(\"response-content-disposition\");\n const named = disposition?.match(/filename\\*?=(?:UTF-8'')?\"?([^\";]+)\"?/i);\n if (named?.[1]) return decodeURIComponent(named[1]).trim() || null;\n } catch {\n // fall through to path sniffing on the raw string\n }\n const last = url.split(/[?#]/)[0]?.split(\"/\").filter(Boolean).pop();\n if (!last || !/\\.[a-z0-9]{1,5}$/i.test(last)) return null;\n return decodeURIComponent(last);\n}\n\n// ---------------------------------------------------------------------------\n// Durable URL builders (origin: handler/utils/python-base.ts). THE one URL\n// contract — no other module in this package concatenates byte-route URLs.\n// ---------------------------------------------------------------------------\n\nexport interface FileUrls {\n /** Authenticated direct-download endpoint (`Content-Disposition: attachment`). */\n download: string;\n /** Same bytes, `Content-Disposition: inline` — works as `<img src>` etc. */\n inline: string;\n}\n\n/**\n * Build every durable URL variant for a cld_files id. These URLs never\n * expire and carry only the file id; auth is an Authorization header on\n * `fetch()` or the `mx_files_session` cookie for element bindings.\n * `?inline=1` matches the exact spelling the backend emits on\n * `FileRecord.url`, keeping cache keys identical either way.\n */\nexport function fileUrls(filesBaseUrl: string, fileId: string): FileUrls {\n const id = encodeURIComponent(fileId);\n const base = `${filesBaseUrl.replace(/\\/$/, \"\")}/files/${id}/download`;\n return { download: base, inline: `${base}?inline=1` };\n}\n\n/**\n * The clean public byte-streaming share endpoint (`GET /share/{token}`,\n * inline disposition) — the canonical embeddable URL for a minted link.\n */\nexport function shareUrl(filesBaseUrl: string, token: string): string {\n return `${filesBaseUrl.replace(/\\/$/, \"\")}/share/${encodeURIComponent(token)}`;\n}\n","/**\n * @ai-matrx/data/files — THE Matrx file-communication client.\n *\n * The client SDK for the AI Dream file contract — the communication half of\n * the media seam (C13). Behavior is ported verbatim from matrx-frontend's\n * `features/files/media-client/client.ts` (the 436-line host adapter this\n * tier exists to collapse) + `features/files/handler/{session,errors}.ts` +\n * `lib/media/{durability,signed-url,our-file-sources}.ts`; only the coupling\n * seams inverted — injected values and ports instead of Redux singletons and\n * `NEXT_PUBLIC_*` reads. Wire shapes are verified against the aidream server\n * source (`packages/matrx-files/matrx_files/api/router_files.py`,\n * `records.py`, `router_share.py`) — see `types.ts` `*Wire` interfaces.\n *\n * The client structurally satisfies `@ai-matrx/media`'s `MediaClient` port\n * (compile-time proof: media's own `port-satisfaction.test.ts`) and adds the doors media never\n * needs as separate calls: `ensureSession`, `shareableUrlNoMint` (the\n * copy-never-mints door), and the recognizer.\n *\n * Law enforcement lives behind `resolve()`:\n * - a signed/expiring URL is REFUSED (screams via the diagnostics sink,\n * then throws) — law 3 / D108;\n * - private image pixels ride the bearer-authenticated blob lane\n * (`transport: \"blob\"`);\n * - video/audio and public CDN URLs bind durable URLs directly\n * (`mx_files_session` cookie / permanent CDN).\n */\n\nimport { credentialToHeaders, type CredentialsPort } from \"../credentials\";\nimport {\n classifyUnavailable,\n ExternalFetchError,\n FileError,\n fileErrorFromStatus,\n FileUploadError,\n} from \"./errors\";\nimport {\n classifyMediaUrl,\n createFileUrlRecognizer,\n fileUrls,\n mimeFromUrl,\n shareableMediaUrl,\n shareUrl,\n type FileUrlRecognizer,\n} from \"./recognize\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\nimport { createFilesSession, type FilesSession } from \"./session\";\nimport { healLoadErrorLadder } from \"./heal\";\nimport type {\n BlobCacheEntry,\n BlobCachePort,\n DurableSrc,\n FileMetadata,\n FileMetadataPort,\n FileOrigins,\n FileRecordWire,\n FilesBlobHandle,\n FilesDiagnosticsSink,\n FilesHealAttempt,\n FilesHealResult,\n FilesRecoveryReport,\n FilesRecoveryVerdict,\n FilesResolution,\n FileShownTo,\n FileUploadResponseWire,\n MediaKind,\n MediaMultiUploadResult,\n MediaRef,\n MediaRefLike,\n MediaUnavailableReason,\n MediaUploadOptions,\n MediaUploadResult,\n ShareLinkRecordWire,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Transport policy (origin: upload/cloudUpload.ts — the ONE decision point)\n// ---------------------------------------------------------------------------\n\n/** Files at or above this size must go resumable (TUS). */\nexport const TUS_TRANSPORT_THRESHOLD_BYTES = 80 * 1024 * 1024;\n\nexport type UploadTransport = \"buffered\" | \"tus\";\n\n/** The ONE place the buffered-vs-TUS decision is made. */\nexport function resolveUploadTransport(\n fileSizeBytes: number,\n override?: UploadTransport,\n): UploadTransport {\n if (override) return override;\n return fileSizeBytes >= TUS_TRANSPORT_THRESHOLD_BYTES ? \"tus\" : \"buffered\";\n}\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\nexport interface MatrxFilesClientConfig {\n /** The credentials port (core) — maps to Bearer / X-Fingerprint-ID headers. */\n credentials: CredentialsPort;\n /**\n * The backend base serving the durable file byte routes (main backend or\n * the standalone files host). A function is re-resolved per use — pass one\n * when the host's routing ladder can change at runtime. May throw while\n * unresolved (SSR/early boot); the session lane treats that as \"nothing to\n * establish\" and the resolve lane surfaces it as a plain error.\n */\n filesBaseUrl: string | (() => string);\n /**\n * Additional bases the `mx_files_session` cookie must be established on\n * (cookies are per-host; the main backend and the standalone files host\n * both serve durable URLs). Defaults to just `filesBaseUrl`.\n */\n sessionBases?: (string | (() => string))[] | undefined;\n /** The host's URL identity — every member has a Matrx-topology default. */\n origins?: FileOrigins | undefined;\n /** Failure/violation sink. Default: a loud `console.error` scream. */\n diagnostics?: FilesDiagnosticsSink | undefined;\n /** Injected transport. Default: `globalThis.fetch`. */\n fetchImpl?: typeof fetch | undefined;\n /** Metadata source override (host stores). Default: `GET /files/{id}` cache. */\n metadata?: FileMetadataPort | undefined;\n /** Byte-cache override (persistent tiers). Default: in-memory LRU (250 MB). */\n blobCache?: BlobCachePort | undefined;\n /** Byte budget for the DEFAULT in-memory blob cache. */\n blobCacheBudgetBytes?: number | undefined;\n /**\n * RN's cookie-less lane: when true, `resolve()` attaches the current auth\n * headers to non-CDN resolutions (`<Image source={{uri, headers}}>`).\n * `resolve()` is synchronous, so the header snapshot is primed lazily from\n * the credentials port (and refreshed on `onChange`); call\n * `client.primeAuth()` at boot to guarantee the first resolve carries them.\n * Web hosts leave this off — the cookie session authenticates elements.\n */\n attachAuthHeadersToResolution?: boolean | undefined;\n /**\n * The resumable transport for files ≥ {@link TUS_TRANSPORT_THRESHOLD_BYTES}.\n * v1 of this tier ships the buffered multipart transport only — the TUS\n * transport (16 MiB chunks, IndexedDB resume URLs, `router_tus.py`) is\n * live-unverified at the origin (handler FEATURE.md invariant 10) and\n * lands with real browser proof; until then a host that needs it injects\n * its own. Absent + oversized file → typed `FileUploadError`.\n */\n largeUploadTransport?:\n | ((file: File, opts: MediaUploadOptions) => Promise<MediaUploadResult>)\n | undefined;\n /**\n * Host batch-upload door (dedup pre-flight dialogs, folder-id targeting).\n * Default: sequential `upload()` per file, never cancelled.\n */\n uploadMany?:\n | ((files: File[], opts: MediaUploadOptions) => Promise<MediaMultiUploadResult>)\n | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// The client surface\n// ---------------------------------------------------------------------------\n\nexport interface MatrxFilesClient<TSrc extends string = DurableSrc> {\n /**\n * Synchronous durable-URL build + classification. Returns null when the\n * ref carries no usable identity. THROWS (and screams through the sink)\n * on a signed/expiring URL — the D108 guard lives behind this door.\n */\n resolve(ref: MediaRefLike): FilesResolution<TSrc> | null;\n /** Bytes for canvas/crossOrigin/private-pixel consumers. */\n getBlob(ref: MediaRefLike): Promise<FilesBlobHandle>;\n /**\n * The ONE retry policy: session refresh → same-URL retry, second failure\n * terminal. Called by the render layer's onError path, never re-implemented.\n * Returns a {@link FilesRecoveryReport} (media 0.3.0 `MediaRecoveryReport`\n * twin) when the session lane learned the mint's `secret_source` and/or a\n * failed mint's HTTP status — so a host wiring this client into\n * `@ai-matrx/media` inherits the ephemeral-secret scream automatically.\n * Against an older server it stays the bare string verdict.\n */\n recoverLoadError(\n src: TSrc,\n attempt: number,\n ): Promise<FilesRecoveryVerdict | FilesRecoveryReport>;\n /** ONE retry policy for a failed object URL backed by a durable file ref. */\n recoverBlobLoadError(\n ref: MediaRefLike,\n attempt: number,\n ): Promise<FilesRecoveryVerdict>;\n /**\n * The heal ladder (media `MediaClient.healLoadError` twin): runs AFTER the\n * ONE retry is exhausted. Bearer byte fetch on the primary host, then on\n * the alternate host; every attempt recorded; the root cause named. Never\n * throws — a dead ladder is a result with `src: null`.\n */\n healLoadError(\n ref: MediaRefLike,\n context: { src: TSrc; attempts: FilesHealAttempt[] },\n ): Promise<FilesHealResult>;\n /** Buffered multipart upload → durable identity (`POST /files/upload`). */\n upload(file: File | Blob, opts?: MediaUploadOptions): Promise<MediaUploadResult>;\n uploadMany(files: File[], opts?: MediaUploadOptions): Promise<MediaMultiUploadResult>;\n /**\n * THE public-link door (media 0.2.2 semantics): permanent CDN URL for\n * public files, else reuse-or-mint a no-expiry read-only share link.\n * Minting is a click-time side effect — call this ON CLICK, never on\n * render. Fails closed (null), never emits a signed URL (law 4).\n */\n shareableUrl(ref: MediaRefLike): Promise<string | null>;\n /**\n * Side-effect-free twin for the quick COPY action: returns a durable\n * public URL when one already exists and null otherwise — it NEVER mints\n * a share link (minting is the SHARE door's job).\n */\n shareableUrlNoMint(ref: MediaRefLike): string | null;\n /** Typed unavailable states — render-as-state, never thrown as incidents. */\n classifyError(err: unknown): MediaUnavailableReason;\n /** The `mx_files_session` bootstrap — idempotent, deduped, never throws. */\n ensureSession(opts?: { force?: boolean | undefined }): Promise<void>;\n /** Prime the auth-header snapshot for the RN resolution lane. */\n primeAuth(): Promise<void>;\n /** The origin recognizer bound to this client's injected origin list. */\n recognizer: FileUrlRecognizer;\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nfunction defaultDiagnostics(event: {\n source: string;\n context: string;\n message: string;\n detail?: string | undefined;\n}): void {\n if (event.source === \"media-durability\") {\n // Verbatim scream from the origin durability guard: a non-public,\n // expiring URL reaching a render/store path is a server-side defect —\n // never papered over silently.\n console.error(\n \"\\n================ MEDIA-DURABILITY VIOLATION ================\\n\" +\n `A non-public, EXPIRING media URL reached \"${event.context}\".\\n` +\n \"This must never be persisted/rendered for public or owned media — it WILL\\n\" +\n \"break when the signature expires. The media should have been saved PUBLIC\\n\" +\n \"at generation (durable CDN/public-bucket URL), or resolved from its\\n\" +\n \"file_id (which re-mints a durable URL forever).\\n\" +\n `URL: ${(event.detail ?? \"\").slice(0, 180)}\\n` +\n \"===========================================================\\n\",\n );\n return;\n }\n console.error(`[files:${event.source}] ${event.message}`, event.detail ?? \"\");\n}\n\nconst DEFAULT_BLOB_BUDGET_BYTES = 250 * 1024 * 1024;\n\n/**\n * The default byte cache: the origin's tier-1 in-memory LRU (blob-cache.ts),\n * byte-budgeted; the cache OWNS the object URLs and revokes on eviction.\n * Persistent tiers (IndexedDB, the service worker) stay host-injected.\n */\nexport function createMemoryBlobCache(\n budgetBytes = DEFAULT_BLOB_BUDGET_BYTES,\n): BlobCachePort {\n interface Entry extends BlobCacheEntry {\n bytes: number;\n }\n const cache = new Map<string, Entry>();\n let totalBytes = 0;\n\n return {\n get(fileId: string): BlobCacheEntry | null {\n const entry = cache.get(fileId);\n if (!entry) return null;\n // Bump the LRU position (Map iteration order = insertion order).\n cache.delete(fileId);\n cache.set(fileId, entry);\n return entry;\n },\n set(fileId: string, blob: Blob, url: string): void {\n const existing = cache.get(fileId);\n if (existing) {\n totalBytes -= existing.bytes;\n cache.delete(fileId);\n if (existing.url !== url) {\n try {\n URL.revokeObjectURL(existing.url);\n } catch {\n /* revocation must never break the caller */\n }\n }\n }\n const bytes = blob.size;\n cache.set(fileId, { blob, url, bytes });\n totalBytes += bytes;\n for (const [key, entry] of cache) {\n if (totalBytes <= budgetBytes || key === fileId) break;\n cache.delete(key);\n totalBytes -= entry.bytes;\n try {\n URL.revokeObjectURL(entry.url);\n } catch {\n /* revocation must never break the caller */\n }\n }\n },\n invalidate(fileId: string): void {\n const existing = cache.get(fileId);\n if (!existing) return;\n cache.delete(fileId);\n totalBytes -= existing.bytes;\n try {\n URL.revokeObjectURL(existing.url);\n } catch {\n /* revocation must never break recovery */\n }\n },\n };\n}\n\n/**\n * The upload's row-access form fields (access ladder T-13). The two words are written\n * explicitly on every upload — never left to a server default: not published, shown to\n * only me, unless the caller says otherwise. A caller still on the retiring level word\n * is translated here, once.\n */\nexport function uploadAccessFields(opts: MediaUploadOptions): Record<string, string> {\n const legacy = opts.visibility; // T-13 transitional wire input\n const published = opts.publishedToWeb ?? (legacy === undefined ? false : legacy === \"public\");\n const shownTo: FileShownTo | null =\n opts.shownTo ??\n (opts.publishedToWeb !== undefined || legacy === undefined || legacy === \"personal\" ? \"only_me\" : null);\n const fields: Record<string, string> = { published_to_web: String(published) };\n if (shownTo) fields.shown_to = shownTo;\n return fields;\n}\n\nfunction recordToMetadata(record: FileRecordWire): FileMetadata {\n return {\n mimeType: record.mime_type ?? null,\n publishedToWeb: record.published_to_web ?? null,\n cdnUrl: record.cdn_url ?? null,\n publicUrl: record.public_url ?? null,\n thumbnailUrl: record.thumbnail_url ?? null,\n deletedAt: record.deleted_at ?? null,\n fileName: record.file_name ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createMatrxFilesClient<TSrc extends string = DurableSrc>(\n config: MatrxFilesClientConfig,\n): MatrxFilesClient<TSrc> {\n const diagnostics: FilesDiagnosticsSink =\n config.diagnostics ?? defaultDiagnostics;\n const fetchImpl: typeof fetch =\n config.fetchImpl ?? ((...args) => globalThis.fetch(...args));\n const recognizer = createFileUrlRecognizer(config.origins ?? {});\n const blobCache = config.blobCache ?? createMemoryBlobCache(config.blobCacheBudgetBytes);\n\n function resolveBase(entry: string | (() => string)): string {\n return typeof entry === \"function\" ? entry() : entry;\n }\n\n function filesBase(): string {\n return resolveBase(config.filesBaseUrl).replace(/\\/$/, \"\");\n }\n\n async function authHeaders(\n extra?: Record<string, string>,\n ): Promise<Record<string, string>> {\n const credential = await config.credentials.get();\n return { ...credentialToHeaders(credential), ...extra };\n }\n\n // --- session ------------------------------------------------------------\n\n const session: FilesSession = createFilesSession({\n resolveBases: () => {\n const entries = config.sessionBases ?? [config.filesBaseUrl];\n const bases: string[] = [];\n for (const entry of entries) {\n try {\n bases.push(resolveBase(entry).replace(/\\/$/, \"\"));\n } catch {\n // This base is unresolved (SSR/early boot) — others still count.\n }\n }\n return bases;\n },\n buildHeaders: () => authHeaders(),\n fetchImpl,\n diagnostics,\n });\n\n // --- auth-header snapshot (RN resolution lane) --------------------------\n\n let headerSnapshot: Record<string, string> | null = null;\n let headerPrimeStarted = false;\n\n async function refreshHeaderSnapshot(): Promise<void> {\n try {\n headerSnapshot = await authHeaders();\n } catch {\n headerSnapshot = null;\n }\n }\n\n function primeHeadersLazily(): void {\n if (headerPrimeStarted) return;\n headerPrimeStarted = true;\n void refreshHeaderSnapshot();\n config.credentials.onChange?.(() => {\n void refreshHeaderSnapshot();\n });\n }\n\n // --- metadata -----------------------------------------------------------\n\n const metadata: FileMetadataPort =\n config.metadata ?? createDefaultMetadataPort();\n\n function createDefaultMetadataPort(): FileMetadataPort {\n const cache = new Map<string, FileMetadata>();\n const inflight = new Map<string, Promise<FileMetadata | undefined>>();\n return {\n peek(fileId: string): FileMetadata | undefined {\n return cache.get(fileId);\n },\n hydrate(fileId: string): Promise<FileMetadata | undefined> {\n const cached = cache.get(fileId);\n if (cached) return Promise.resolve(cached);\n let pending = inflight.get(fileId);\n if (!pending) {\n pending = (async () => {\n const headers = await authHeaders({ Accept: \"application/json\" });\n const response = await fetchImpl(\n `${filesBase()}/files/${encodeURIComponent(fileId)}`,\n { headers },\n );\n if (!response.ok) {\n const typed = fileErrorFromStatus(response.status, fileId);\n if (typed) throw typed;\n throw new FileError(\n \"internal\",\n `GET /files/${fileId} → HTTP ${response.status}`,\n { fileId },\n );\n }\n const record = (await response.json()) as FileRecordWire;\n const meta = recordToMetadata(record);\n cache.set(fileId, meta);\n return meta;\n })().finally(() => {\n inflight.delete(fileId);\n });\n inflight.set(fileId, pending);\n }\n return pending;\n },\n };\n }\n\n /**\n * Fire-and-forget metadata hydration for a file id (public/CDN detection,\n * mime, thumbnail). Deduped per id; scheduled on a microtask so a\n * render-time `resolve()` never performs I/O in the render phase. A failed\n * attempt allows a later re-request (mirrors the origin's scheduleHydration).\n */\n const hydrationRequested = new Set<string>();\n function scheduleHydration(fileId: string): void {\n if (hydrationRequested.has(fileId)) return;\n if (metadata.peek(fileId)) return;\n hydrationRequested.add(fileId);\n queueMicrotask(() => {\n void metadata\n .hydrate(fileId)\n .then((meta) => {\n if (!meta) hydrationRequested.delete(fileId);\n })\n .catch(() => {\n hydrationRequested.delete(fileId);\n });\n });\n }\n\n // --- ref normalization (origin: media-client normalizeRef) --------------\n\n type NormalizedRef =\n | { kind: \"file_id\"; fileId: string; mime: string | null }\n | { kind: \"url\"; url: string; mime: string | null }\n | null;\n\n function normalizeUrl(url: string, mime: string | null): NormalizedRef {\n // A URL pointing at one of OUR authenticated byte endpoints is an\n // IDENTITY in disguise (QA F2) — promote it to the file_id lane so\n // pixels ride the bearer-authenticated blob transport.\n const endpointFileId = recognizer.fileIdFromFileEndpointUrl(url);\n if (endpointFileId) {\n return {\n kind: \"file_id\",\n fileId: endpointFileId,\n mime: mime ?? mimeFromUrl(url),\n };\n }\n return { kind: \"url\", url, mime };\n }\n\n function normalizeRef(ref: MediaRefLike | null | undefined): NormalizedRef {\n if (!ref) return null;\n if (typeof ref === \"string\") {\n if (isUuidShape(ref)) return { kind: \"file_id\", fileId: ref, mime: null };\n if (!ref.trim()) return null;\n return normalizeUrl(ref, null);\n }\n if (ref.file_id) {\n return { kind: \"file_id\", fileId: ref.file_id, mime: ref.mime_type ?? null };\n }\n if (ref.url) return normalizeUrl(ref.url, ref.mime_type ?? null);\n return null;\n }\n\n function mimeToKind(mime: string | null): MediaKind {\n if (!mime) return \"image\"; // matches the origin's <img> default\n if (mime.startsWith(\"image/\")) return \"image\";\n if (mime.startsWith(\"video/\")) return \"video\";\n if (mime.startsWith(\"audio/\")) return \"audio\";\n return \"file\";\n }\n\n function refuseExpiringUrl(url: string, context: string): never {\n diagnostics({\n source: \"media-durability\",\n context,\n message: `Expiring media URL reached \"${context}\" — should be public/durable`,\n detail: String(url).slice(0, 300),\n });\n throw new FileError(\n \"internal\",\n \"Expiring/signed media URL refused — a signed URL is a handoff, never an identity. Pass the file_id.\",\n );\n }\n\n function mint(url: string): TSrc {\n // The brand exists to stop UNCLASSIFIED strings; every call site here\n // sits behind the classifier/normalizer, which is the entire point.\n return url as string as TSrc;\n }\n\n // --- blob lane ----------------------------------------------------------\n\n const inflightDownloads = new Map<string, Promise<Blob>>();\n\n async function fetchFileBytes(fileId: string): Promise<Blob> {\n const headers = await authHeaders();\n const response = await fetchImpl(fileUrls(filesBase(), fileId).download, {\n headers,\n });\n if (!response.ok) {\n const typed = fileErrorFromStatus(response.status, fileId);\n if (typed) throw typed;\n throw new FileError(\n \"internal\",\n `GET /files/${fileId}/download → HTTP ${response.status}`,\n { fileId },\n );\n }\n return response.blob();\n }\n\n async function fileIdBlob(fileId: string): Promise<{ url: string; blob: Blob }> {\n if (!isUuidShape(fileId)) {\n throw new FileError(\n \"internal\",\n `getBlob: \"${fileId}\" is not a cld_files id — host-namespaced/virtual ids are resolved by the host before this client.`,\n { fileId },\n );\n }\n const cached = blobCache.get(fileId);\n if (cached) return { url: cached.url, blob: cached.blob };\n const hydrated = (await blobCache.hydrate?.(fileId)) ?? null;\n if (hydrated) return { url: hydrated.url, blob: hydrated.blob };\n // In-flight dedup: two simultaneous consumers of the same fileId share\n // ONE network download (origin: hooks/useFileBlob.ts).\n let download = inflightDownloads.get(fileId);\n if (!download) {\n download = fetchFileBytes(fileId);\n inflightDownloads.set(fileId, download);\n void download\n .finally(() => {\n if (inflightDownloads.get(fileId) === download) {\n inflightDownloads.delete(fileId);\n }\n })\n .catch(() => {\n // Every awaiting consumer handles the rejection; this only stops\n // the void'd cleanup chain from re-surfacing it as unhandled.\n });\n }\n const blob = await download;\n const already = blobCache.get(fileId);\n const objectUrl = already?.url ?? URL.createObjectURL(blob);\n if (!already) blobCache.set(fileId, blob, objectUrl, { mimeType: blob.type });\n return { url: objectUrl, blob };\n }\n\n // --- upload lane --------------------------------------------------------\n\n function newRequestId(): string {\n const c = globalThis.crypto as Crypto | undefined;\n if (c?.randomUUID) return c.randomUUID();\n return `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n }\n\n function toUploadFile(file: File | Blob, opts: MediaUploadOptions): File {\n if (typeof File !== \"undefined\" && file instanceof File && !opts.fileName) {\n return file;\n }\n const mime =\n opts.mimeType ?? (file.type || \"application/octet-stream\");\n const name =\n opts.fileName ??\n (typeof File !== \"undefined\" && file instanceof File\n ? file.name\n : `upload-${Date.now()}.${mime.split(\"/\")[1]?.split(\";\")[0] ?? \"bin\"}`);\n return new File([file], name, { type: mime });\n }\n\n async function postMultipart(\n form: FormData,\n opts: MediaUploadOptions,\n requestId: string,\n ): Promise<FileUploadResponseWire> {\n const url = `${filesBase()}/files/upload`;\n const headers = await authHeaders({\n Accept: \"application/json\",\n \"X-Request-Id\": requestId,\n // Reuse requestId as the idempotency key — single intended upload,\n // single key; the backend stores it so retries don't double-create\n // version rows (origin: cloudUpload.ts).\n \"X-Idempotency-Key\": requestId,\n });\n\n // XHR path only when byte progress is requested and XHR exists (fetch\n // has no upload progress). Everything else rides the injected fetch.\n if (opts.onProgress && typeof XMLHttpRequest !== \"undefined\") {\n const onProgress = opts.onProgress;\n return new Promise<FileUploadResponseWire>((resolvePromise, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open(\"POST\", url);\n for (const [key, value] of Object.entries(headers)) {\n xhr.setRequestHeader(key, value);\n }\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable) onProgress(event.loaded, event.total);\n };\n xhr.onerror = () =>\n reject(new FileUploadError(\"Upload failed: network error\"));\n xhr.onabort = () => reject(new FileUploadError(\"Upload cancelled\"));\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n resolvePromise(JSON.parse(xhr.responseText) as FileUploadResponseWire);\n } catch (err) {\n reject(\n new FileUploadError(\"Upload response was not JSON\", { cause: err }),\n );\n }\n } else {\n reject(\n new FileUploadError(\n `Upload failed: HTTP ${xhr.status}${xhr.responseText ? ` — ${xhr.responseText.slice(0, 300)}` : \"\"}`,\n ),\n );\n }\n };\n if (opts.signal) {\n opts.signal.addEventListener(\"abort\", () => xhr.abort(), { once: true });\n }\n xhr.send(form);\n });\n }\n\n const init: RequestInit = { method: \"POST\", headers, body: form };\n if (opts.signal) init.signal = opts.signal;\n const response = await fetchImpl(url, init);\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new FileUploadError(\n `Upload failed: HTTP ${response.status}${text ? ` — ${text.slice(0, 300)}` : \"\"}`,\n );\n }\n return (await response.json()) as FileUploadResponseWire;\n }\n\n async function upload(\n file: File | Blob,\n opts: MediaUploadOptions = {},\n ): Promise<MediaUploadResult> {\n const asFile = toUploadFile(file, opts);\n\n // A target folder ID only exists on the host batch door (which owns\n // id→path resolution and the dedup pre-flight) — the wire contract\n // addresses folders by PATH. Route through it so the file lands where\n // the caller asked (origin: media-client upload → requestUpload).\n if (opts.parentFolderId != null) {\n if (!config.uploadMany) {\n throw new FileUploadError(\n \"upload: `parentFolderId` names a host folder id, but no `uploadMany` \" +\n \"port is injected. The wire contract addresses folders by PATH \" +\n \"(`folderPath`); inject the host batch door to upload by folder id.\",\n );\n }\n const result = await config.uploadMany([asFile], opts);\n if (result.cancelled) throw new FileUploadError(\"Upload cancelled\");\n const fileId = result.uploaded[0];\n if (!fileId) {\n throw new FileUploadError(result.failed[0]?.error ?? \"Upload failed\");\n }\n return { fileId, ref: { file_id: fileId } };\n }\n\n if (resolveUploadTransport(asFile.size) === \"tus\") {\n if (config.largeUploadTransport) {\n return config.largeUploadTransport(asFile, opts);\n }\n throw new FileUploadError(\n `File is ${asFile.size} bytes — at or over the resumable-transport threshold ` +\n `(${TUS_TRANSPORT_THRESHOLD_BYTES}). This tier ships the buffered multipart ` +\n \"transport; inject `largeUploadTransport` (the host's TUS lane) for files this size.\",\n );\n }\n\n const folder = (opts.folderPath ?? \"Inbox\").replace(/^\\/+|\\/+$/g, \"\");\n const form = new FormData();\n form.append(\"file\", asFile);\n form.append(\"file_path\", `${folder}/${asFile.name}`);\n for (const [key, value] of Object.entries(uploadAccessFields(opts))) form.append(key, value);\n if (opts.metadata) form.append(\"metadata_json\", JSON.stringify(opts.metadata));\n\n const response = await postMultipart(form, opts, newRequestId());\n if (!response.file_id) {\n throw new FileUploadError(\n \"Upload completed without a durable file identity; refusing to attach a temporary URL\",\n );\n }\n const ref: MediaRef = { file_id: response.file_id };\n if (response.url) ref.url = response.url;\n if (asFile.type) ref.mime_type = asFile.type;\n return { fileId: response.file_id, ref };\n }\n\n async function uploadManySequential(\n files: File[],\n opts: MediaUploadOptions,\n ): Promise<MediaMultiUploadResult> {\n const uploaded: string[] = [];\n const failed: Array<{ name: string; error: string }> = [];\n for (const file of files) {\n try {\n const result = await upload(file, opts);\n uploaded.push(result.fileId);\n } catch (err) {\n failed.push({\n name: file.name,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n return { uploaded, failed, cancelled: false };\n }\n\n // --- share lane ---------------------------------------------------------\n\n function permanentPublicUrl(meta: FileMetadata | undefined): string | null {\n if (meta?.publishedToWeb !== true) return null;\n const candidate = shareableMediaUrl(meta.cdnUrl ?? meta.publicUrl ?? null);\n if (!candidate) return null;\n // A public row is not proof that every URL stored on it is anonymously\n // renderable. During CDN backfill, public_url may still be our\n // authenticated /files/{id}/download endpoint. Treating that endpoint as\n // public element transport bypasses the bearer-authenticated blob lane and\n // leaves the media element permanently broken after a session refresh.\n return recognizer.fileIdFromFileEndpointUrl(candidate) ? null : candidate;\n }\n\n async function mintOrReuseShareLink(fileId: string): Promise<string | null> {\n const base = filesBase();\n const headers = await authHeaders({ Accept: \"application/json\" });\n const listResponse = await fetchImpl(\n `${base}/files/${encodeURIComponent(fileId)}/share-links`,\n { headers },\n );\n if (!listResponse.ok) {\n const typed = fileErrorFromStatus(listResponse.status, fileId);\n if (typed) throw typed;\n throw new FileError(\n \"internal\",\n `GET /files/${fileId}/share-links → HTTP ${listResponse.status}`,\n { fileId },\n );\n }\n const links = (await listResponse.json()) as ShareLinkRecordWire[];\n // Reuse an existing no-expiry read-only link (the origin popover's exact\n // filter), or mint one.\n const existing = links.find(\n (l) =>\n l.permission_level === \"viewer\" && !l.expires_at && !l.max_uses && l.token,\n );\n if (existing?.token) return shareUrl(base, existing.token);\n\n const createResponse = await fetchImpl(\n `${base}/files/${encodeURIComponent(fileId)}/share-links`,\n {\n method: \"POST\",\n headers: { ...headers, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ permission_level: \"viewer\" }),\n },\n );\n if (!createResponse.ok) {\n const typed = fileErrorFromStatus(createResponse.status, fileId);\n if (typed) throw typed;\n throw new FileError(\n \"internal\",\n `POST /files/${fileId}/share-links → HTTP ${createResponse.status}`,\n { fileId },\n );\n }\n const created = (await createResponse.json()) as ShareLinkRecordWire;\n return created.token ? shareUrl(base, created.token) : null;\n }\n\n // --- the client ---------------------------------------------------------\n\n const client: MatrxFilesClient<TSrc> = {\n recognizer,\n\n resolve(ref: MediaRefLike): FilesResolution<TSrc> | null {\n if (config.attachAuthHeadersToResolution) primeHeadersLazily();\n const norm = normalizeRef(ref);\n if (!norm) return null;\n\n if (norm.kind === \"url\") {\n if (classifyMediaUrl(norm.url) === \"expiring\") {\n refuseExpiringUrl(norm.url, \"files-client.resolve\");\n }\n const mime = norm.mime ?? mimeFromUrl(norm.url);\n const ours = recognizer.isOurUrl(norm.url);\n const resolution: FilesResolution<TSrc> = {\n src: mint(norm.url),\n kind: mimeToKind(mime),\n transport: \"element\",\n isCdn: recognizer.looksLikeCdnUrl(norm.url),\n // A session refresh can't resurrect someone else's link rot.\n recoverable: ours,\n };\n if (mime) resolution.mimeType = mime;\n return resolution;\n }\n\n const { fileId } = norm;\n const meta = metadata.peek(fileId);\n if (isUuidShape(fileId)) scheduleHydration(fileId);\n\n const mime = norm.mime ?? meta?.mimeType ?? null;\n const kind = mimeToKind(mime);\n const thumbRaw = meta?.thumbnailUrl ?? null;\n const thumbnailSrc =\n thumbRaw && classifyMediaUrl(thumbRaw) !== \"expiring\"\n ? mint(thumbRaw)\n : undefined;\n\n const publicUrl = permanentPublicUrl(meta);\n if (publicUrl) {\n const resolution: FilesResolution<TSrc> = {\n src: mint(publicUrl),\n kind,\n transport: \"element\",\n isCdn: true,\n recoverable: true,\n };\n if (mime) resolution.mimeType = mime;\n if (thumbnailSrc !== undefined) resolution.thumbnailSrc = thumbnailSrc;\n return resolution;\n }\n\n // Private/unknown-visibility pixels: images (and file-kinds rendered\n // as <img>) ride the bearer-authenticated blob lane. Video/audio bind\n // the durable URL directly (the mx_files_session cookie authenticates\n // GET byte routes).\n const transport = kind === \"video\" || kind === \"audio\" ? \"element\" : \"blob\";\n const resolution: FilesResolution<TSrc> = {\n src: mint(fileUrls(filesBase(), fileId).inline),\n kind,\n transport,\n recoverable: true,\n };\n if (mime) resolution.mimeType = mime;\n if (thumbnailSrc !== undefined) resolution.thumbnailSrc = thumbnailSrc;\n if (config.attachAuthHeadersToResolution && headerSnapshot) {\n resolution.headers = { ...headerSnapshot };\n }\n return resolution;\n },\n\n async getBlob(ref: MediaRefLike): Promise<FilesBlobHandle> {\n const norm = normalizeRef(ref);\n if (!norm) throw new FileError(\"internal\", \"getBlob: empty media ref\");\n if (norm.kind === \"file_id\") {\n const { url, blob } = await fileIdBlob(norm.fileId);\n // The blob cache owns the object URL — it revokes on eviction,\n // never on consumer release.\n return { url, blob, release: () => {} };\n }\n if (classifyMediaUrl(norm.url) === \"expiring\") {\n refuseExpiringUrl(norm.url, \"files-client.getBlob\");\n }\n let response: Response;\n try {\n response = await fetchImpl(norm.url);\n } catch (err) {\n throw new ExternalFetchError(undefined, { cause: err });\n }\n if (!response.ok) {\n throw new ExternalFetchError(\n `Failed to fetch external URL (${response.status})`,\n );\n }\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n return {\n url: objectUrl,\n blob,\n release: () => URL.revokeObjectURL(objectUrl),\n };\n },\n\n async recoverLoadError(\n _src: TSrc,\n attempt: number,\n ): Promise<FilesRecoveryVerdict | FilesRecoveryReport> {\n // Bare verdict against older servers (no secret_source, no failed-mint\n // status); the structured report otherwise — same verdict either way.\n const finish = (\n verdict: FilesRecoveryVerdict,\n ): FilesRecoveryVerdict | FilesRecoveryReport => {\n const snap = session.snapshot();\n if (snap.secretSource === undefined && snap.status === undefined) {\n return verdict;\n }\n return { verdict, status: snap.status, secretSource: snap.secretSource };\n };\n if (attempt > 1) return finish(\"terminal\");\n // Session refresh, not URL re-mint: re-request the SAME durable URL\n // after forcing a fresh mx_files_session cookie. ensure() never\n // throws; per-base failures already screamed through the sink.\n await session.ensure({ force: true });\n return finish(\"retry\");\n },\n\n async healLoadError(\n ref: MediaRefLike,\n context: { src: TSrc; attempts: FilesHealAttempt[] },\n ): Promise<FilesHealResult> {\n const norm = normalizeRef(ref);\n // A URL ref on OUR host still carries a file id in its path.\n const fileId =\n norm?.kind === \"file_id\"\n ? norm.fileId\n : norm?.kind === \"url\"\n ? recognizer.fileIdFromFileEndpointUrl(norm.url)\n : null;\n const primary = filesBase();\n return healLoadErrorLadder({\n fileId,\n priorAttempts: context.attempts,\n primaryBase: primary,\n alternateBases: session.bases().filter((b) => b !== primary),\n mintStatus: session.snapshot().status,\n fetchBytes: async (base, id) => {\n const headers = await authHeaders();\n return fetchImpl(fileUrls(base, id).download, { headers });\n },\n cacheBlob: (id, blob) => {\n const already = blobCache.get(id);\n const objectUrl = already?.url ?? URL.createObjectURL(blob);\n if (!already) blobCache.set(id, blob, objectUrl, { mimeType: blob.type });\n return objectUrl;\n },\n });\n },\n\n async recoverBlobLoadError(\n ref: MediaRefLike,\n attempt: number,\n ): Promise<FilesRecoveryVerdict> {\n if (attempt > 1) return \"terminal\";\n const norm = normalizeRef(ref);\n if (!norm || norm.kind !== \"file_id\") return \"terminal\";\n // A failed object URL is not an identity and a session refresh cannot\n // repair it. Evict only this durable file's cached handle; the render\n // layer will re-enter getBlob(ref), which owns the actual byte path.\n blobCache.invalidate?.(norm.fileId);\n return \"retry\";\n },\n\n upload,\n\n uploadMany(files: File[], opts: MediaUploadOptions = {}): Promise<MediaMultiUploadResult> {\n if (config.uploadMany) return config.uploadMany(files, opts);\n return uploadManySequential(files, opts);\n },\n\n async shareableUrl(ref: MediaRefLike): Promise<string | null> {\n const norm = normalizeRef(ref);\n if (!norm) return null;\n if (norm.kind === \"url\") return shareableMediaUrl(norm.url);\n const { fileId } = norm;\n if (!isUuidShape(fileId)) return null;\n // Hydrate published-to-web/CDN fields before deciding to mint — an\n // unhydrated public file must never get a redundant share link.\n const meta = await metadata.hydrate(fileId);\n const permanent = permanentPublicUrl(meta);\n if (permanent) return permanent;\n return mintOrReuseShareLink(fileId);\n },\n\n shareableUrlNoMint(ref: MediaRefLike): string | null {\n const norm = normalizeRef(ref);\n if (!norm) return null;\n if (norm.kind === \"url\") return shareableMediaUrl(norm.url);\n return permanentPublicUrl(metadata.peek(norm.fileId));\n },\n\n classifyError(err: unknown): MediaUnavailableReason {\n return classifyUnavailable(err);\n },\n\n ensureSession(opts?: { force?: boolean | undefined }): Promise<void> {\n return session.ensure(opts);\n },\n\n async primeAuth(): Promise<void> {\n primeHeadersLazily();\n await refreshHeaderSnapshot();\n },\n };\n\n return client;\n}\n","/**\n * @ai-matrx/data/files — the `mx_files_session` cookie bootstrap.\n *\n * Ported behavior-verbatim from matrx-frontend `features/files/handler/\n * session.ts`; verified against the server route\n * (`packages/matrx-files/matrx_files/api/router_files.py` `POST /session` →\n * `{\"ok\":true,\"expires_in\":<seconds>}` + `Set-Cookie: mx_files_session=…`).\n *\n * Durable file URLs never expire and carry only the file id. A plain\n * `<img src>` / `<video src>` can't attach an Authorization header, so\n * private files authenticate through the HttpOnly `mx_files_session` cookie:\n * `POST /files/session` with normal auth headers AND `credentials:'include'`\n * sets it (SameSite=None; Secure; HMAC-signed), and the backend accepts it\n * ONLY on GET byte routes.\n *\n * The durable URLs the backend emits point at the MAIN backend base, while\n * client routing can also target the standalone files host. Cookies are\n * per-host, so when the two bases differ the session is established on\n * BOTH — deduped, independent, non-fatal on failure (the render path retries\n * via `force` from `recoverLoadError`).\n *\n * State is per-instance and in-memory only (expiry per base + in-flight\n * dedupe) — a fresh page load just POSTs again. No module-level state: the\n * client instance owns its session tracker, so dual-loader graphs can never\n * split it (the singleton decision belongs to the host that constructs the\n * ONE client).\n */\n\nimport type { FileSessionResponseWire, FilesDiagnosticsSink } from \"./types\";\n\n/**\n * What the session lane learned from its most recent mints — the feed for\n * `recoverLoadError`'s structured report. `secretSource` aggregates across\n * bases with `\"ephemeral\"` winning (one ephemeral-secret base is enough to\n * scream); `status` is the HTTP status of the most recent FAILED mint, when\n * one is known. Both absent against older servers / before any mint.\n */\nexport interface FilesSessionSnapshot {\n secretSource?: \"stable\" | \"ephemeral\" | undefined;\n status?: number | undefined;\n}\n\n/** Refresh when less than this much of the cookie's TTL remains. */\nconst REFRESH_SAFETY_MARGIN_MS = 60 * 60 * 1000; // 1h of the 7-day TTL\n\ninterface BaseState {\n /** ms epoch when the cookie for this base should be considered stale. */\n freshUntil: number;\n inflight: Promise<void> | null;\n /** Report each base's failure once per session, not once per render. */\n reportedFailure: boolean;\n /** `secret_source` from this base's latest successful mint (wire-optional). */\n secretSource: \"stable\" | \"ephemeral\" | undefined;\n /** HTTP status of this base's latest FAILED mint (cleared on success). */\n lastFailureStatus: number | undefined;\n}\n\nexport interface FilesSessionOptions {\n /** Every backend base that serves durable file bytes to this client. */\n resolveBases(): string[];\n /** Auth headers for the POST (credential mapping is the caller's job). */\n buildHeaders(): Promise<Record<string, string>>;\n fetchImpl: typeof fetch;\n diagnostics: FilesDiagnosticsSink;\n now?: (() => number) | undefined;\n}\n\nexport interface FilesSession {\n /**\n * Ensure the cookie is set on every base. Idempotent and deduped; a fresh\n * session is a no-op unless `force` is passed (use `force` from a media\n * element's error-retry path, where the cookie may have been revoked\n * server-side despite looking fresh here). NEVER throws — per-base\n * failures are independent and reported once through the sink.\n */\n ensure(opts?: { force?: boolean | undefined }): Promise<void>;\n /** What the latest mints taught us — see {@link FilesSessionSnapshot}. */\n snapshot(): FilesSessionSnapshot;\n /** Every base the session lane mints on — the hosts that serve bytes. */\n bases(): string[];\n /** Test/teardown hook: drop all per-base state. */\n reset(): void;\n}\n\nexport function createFilesSession(options: FilesSessionOptions): FilesSession {\n const now = options.now ?? Date.now;\n const stateByBase = new Map<string, BaseState>();\n\n function baseState(base: string): BaseState {\n let s = stateByBase.get(base);\n if (!s) {\n s = {\n freshUntil: 0,\n inflight: null,\n reportedFailure: false,\n secretSource: undefined,\n lastFailureStatus: undefined,\n };\n stateByBase.set(base, s);\n }\n return s;\n }\n\n async function establishOnBase(base: string): Promise<void> {\n const s = baseState(base);\n // Each attempt reports its OWN outcome — a network error must not wear\n // the previous attempt's HTTP status.\n s.lastFailureStatus = undefined;\n try {\n const headers = await options.buildHeaders();\n const response = await options.fetchImpl(`${base}/files/session`, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n });\n if (!response.ok) {\n s.lastFailureStatus = response.status;\n throw new Error(`POST ${base}/files/session → HTTP ${response.status}`);\n }\n const body = (await response.json()) as FileSessionResponseWire;\n const ttlMs = (body.expires_in ?? 0) * 1000;\n s.freshUntil = now() + Math.max(0, ttlMs - REFRESH_SAFETY_MARGIN_MS);\n s.reportedFailure = false;\n s.lastFailureStatus = undefined;\n // Wire-optional (older servers omit it); anything unrecognized is\n // treated as absent rather than forwarded blind.\n s.secretSource =\n body.secret_source === \"ephemeral\" || body.secret_source === \"stable\"\n ? body.secret_source\n : undefined;\n } catch (err) {\n // Non-fatal by design: public files render without the cookie, and the\n // media error-retry path calls ensure({ force: true }).\n if (!s.reportedFailure) {\n s.reportedFailure = true;\n options.diagnostics({\n source: \"files-session\",\n context: base,\n message: `failed to establish the file session on ${base}`,\n detail: err instanceof Error ? err.message : String(err),\n });\n }\n throw err;\n }\n }\n\n function ensureOnBase(base: string, force: boolean): Promise<void> {\n const s = baseState(base);\n if (s.inflight) return s.inflight;\n if (!force && s.freshUntil > now()) return Promise.resolve();\n const promise = establishOnBase(base).finally(() => {\n s.inflight = null;\n });\n s.inflight = promise;\n return promise;\n }\n\n return {\n async ensure(opts?: { force?: boolean | undefined }): Promise<void> {\n const force = opts?.force ?? false;\n let bases: string[] = [];\n try {\n bases = options.resolveBases();\n } catch {\n // No backend configured (SSR/early boot) — nothing to establish.\n }\n await Promise.all(\n [...new Set(bases)].map((base) =>\n ensureOnBase(base, force).catch(() => undefined),\n ),\n );\n },\n snapshot(): FilesSessionSnapshot {\n let secretSource: \"stable\" | \"ephemeral\" | undefined;\n let status: number | undefined;\n for (const s of stateByBase.values()) {\n if (s.secretSource === \"ephemeral\") secretSource = \"ephemeral\";\n else if (s.secretSource === \"stable\" && secretSource === undefined) {\n secretSource = \"stable\";\n }\n if (s.lastFailureStatus !== undefined) status = s.lastFailureStatus;\n }\n return { secretSource, status };\n },\n bases(): string[] {\n return [...new Set(options.resolveBases())];\n },\n reset(): void {\n stateByBase.clear();\n },\n };\n}\n","/**\n * data/src/files/heal.ts — the heal ladder behind `healLoadError`.\n *\n * Runs AFTER the ONE retry policy (session re-mint + same-URL retry) is\n * exhausted. The point: the user is never denied while ANY lane can serve\n * the bytes, and the incident is written with the lanes tried, their\n * timings, and a NAMED root cause — so a render that heals itself is still\n * a loud, countable row, never the quiet norm.\n *\n * Lanes, in order:\n * 1. `session-refresh-retry` — already ran (the caller passes its attempt).\n * 2. `bearer-blob` — bearer-authenticated byte fetch on the\n * primary files host → object URL. Needs NO\n * cookie: this is the lane that serves a\n * browser blocking third-party cookies.\n * 3. `bearer-blob-alternate-host` — the same fetch on the other configured\n * host (the 2026-08-30 class: one host dead).\n *\n * Pure function over injected I/O so it is testable without a browser.\n */\n\nimport type {\n FilesHealAttempt,\n FilesHealDiagnosis,\n FilesHealLane,\n FilesHealResult,\n} from \"./types\";\n\nexport interface HealLadderInput {\n /** The durable identity. Null → no byte lane can run (URL is not ours). */\n fileId: string | null;\n /** The lanes that already ran (the ONE retry), oldest first. */\n priorAttempts: FilesHealAttempt[];\n primaryBase: string;\n /** Other hosts that serve the same bytes (order = preference). */\n alternateBases: string[];\n /** HTTP status of the most recent FAILED cookie mint, when one is known. */\n mintStatus: number | undefined;\n /** Bearer-authenticated `GET /files/{id}/download` on `base`. */\n fetchBytes: (base: string, fileId: string) => Promise<Response>;\n /** Park the blob in the host cache and return its object URL. */\n cacheBlob: (fileId: string, blob: Blob) => string;\n now?: (() => number) | undefined;\n}\n\nfunction originOf(base: string): string {\n try {\n return new URL(base).origin;\n } catch {\n return base;\n }\n}\n\nasync function runByteLane(\n lane: FilesHealLane,\n base: string,\n input: HealLadderInput,\n): Promise<{ attempt: FilesHealAttempt; src: string | null }> {\n const now = input.now ?? Date.now;\n const started = now();\n const host = originOf(base);\n if (!input.fileId) {\n return {\n attempt: {\n lane,\n outcome: \"skipped\",\n ms: 0,\n host,\n detail: \"no file id — the source is not one of our durable file URLs\",\n },\n src: null,\n };\n }\n let response: Response;\n try {\n response = await input.fetchBytes(base, input.fileId);\n } catch (err) {\n return {\n attempt: {\n lane,\n outcome: \"network-error\",\n ms: now() - started,\n host,\n detail: err instanceof Error ? err.message : String(err ?? \"fetch failed\"),\n },\n src: null,\n };\n }\n if (!response.ok) {\n return {\n attempt: {\n lane,\n outcome: \"http-error\",\n status: response.status,\n ms: now() - started,\n host,\n detail: `GET /files/{id}/download → HTTP ${response.status}`,\n },\n src: null,\n };\n }\n try {\n const blob = await response.blob();\n const src = input.cacheBlob(input.fileId, blob);\n return {\n attempt: {\n lane,\n outcome: \"ok\",\n status: response.status,\n ms: now() - started,\n host,\n detail: `${blob.size} bytes via bearer token → object URL`,\n },\n src,\n };\n } catch (err) {\n return {\n attempt: {\n lane,\n outcome: \"network-error\",\n ms: now() - started,\n host,\n detail: `body read failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n src: null,\n };\n }\n}\n\n/** Name the root cause from the attempt pattern. Exported for tests. */\nexport function diagnoseHeal(\n attempts: FilesHealAttempt[],\n mintStatus: number | undefined,\n): FilesHealDiagnosis {\n const lane1 = attempts.find((a) => a.lane === \"session-refresh-retry\");\n const bearer = attempts.find((a) => a.lane === \"bearer-blob\");\n const alternate = attempts.find((a) => a.lane === \"bearer-blob-alternate-host\");\n const healedBy = attempts.find((a) => a.outcome === \"ok\");\n\n if (healedBy?.lane === \"bearer-blob\") {\n // The element lane died with a fresh cookie mint that answered 200 (no\n // failed-mint status), yet the SAME identity fetched the bytes fine with\n // a bearer token. The only difference between the two requests is the\n // cookie — and the browser never sent it.\n if (lane1 && mintStatus === undefined) return \"cookie-not-sent-by-browser\";\n if (mintStatus !== undefined) return \"cookie-mint-failed\";\n return \"unknown\";\n }\n if (healedBy?.lane === \"bearer-blob-alternate-host\") {\n if (bearer && (bearer.outcome === \"network-error\" || (bearer.status ?? 0) >= 500)) {\n return \"primary-host-down-alternate-served\";\n }\n return \"unknown\";\n }\n // Nothing healed — read the byte lanes' refusals.\n const byteLanes = [bearer, alternate].filter(Boolean) as FilesHealAttempt[];\n if (byteLanes.some((a) => a.status === 401)) return \"bearer-rejected\";\n if (byteLanes.some((a) => a.status === 403)) return \"access-denied\";\n if (byteLanes.some((a) => a.status === 404)) return \"file-missing\";\n if (byteLanes.some((a) => (a.status ?? 0) >= 500)) return \"files-host-error\";\n if (byteLanes.length > 0 && byteLanes.every((a) => a.outcome === \"network-error\")) {\n return \"files-host-unreachable\";\n }\n if (mintStatus !== undefined) return \"cookie-mint-failed\";\n return \"unknown\";\n}\n\nconst SUMMARY: Record<FilesHealDiagnosis, string> = {\n \"cookie-not-sent-by-browser\":\n \"the files-session cookie was minted (200) but the browser did not send it back on the media element request — third-party cookies are blocked in this browser (Chrome setting / Incognito, Safari, Brave)\",\n \"cookie-mint-failed\": \"the files-session cookie mint itself failed\",\n \"bearer-rejected\": \"the bearer token was refused (401): the sign-in on this tab is stale\",\n \"access-denied\": \"this identity may not read this file (403)\",\n \"file-missing\": \"the file is gone (404)\",\n \"files-host-error\": \"the files host answered with a server error (5xx)\",\n \"files-host-unreachable\": \"no files host answered (network/DNS/CORS)\",\n \"primary-host-down-alternate-served\":\n \"the primary files host did not serve the bytes; the alternate host did\",\n unknown: \"the attempt pattern matches no known cause\",\n};\n\nexport async function healLoadErrorLadder(input: HealLadderInput): Promise<FilesHealResult> {\n const attempts: FilesHealAttempt[] = [...input.priorAttempts];\n let src: string | null = null;\n let healedBy: FilesHealLane | undefined;\n\n const primary = await runByteLane(\"bearer-blob\", input.primaryBase, input);\n attempts.push(primary.attempt);\n if (primary.src) {\n src = primary.src;\n healedBy = \"bearer-blob\";\n } else {\n for (const base of input.alternateBases) {\n const alt = await runByteLane(\"bearer-blob-alternate-host\", base, input);\n attempts.push(alt.attempt);\n if (alt.src) {\n src = alt.src;\n healedBy = \"bearer-blob-alternate-host\";\n break;\n }\n }\n }\n\n const diagnosis = diagnoseHeal(attempts, input.mintStatus);\n const cause = SUMMARY[diagnosis];\n const served = healedBy\n ? `served via ${healedBy} in ${attempts.find((a) => a.outcome === \"ok\")?.ms ?? 0}ms`\n : `every lane died (${attempts.map((a) => `${a.lane}:${a.outcome}${a.status ? ` ${a.status}` : \"\"}`).join(\", \")})`;\n const ifMint = input.mintStatus !== undefined ? ` (last cookie mint status ${input.mintStatus})` : \"\";\n return {\n src,\n healedBy,\n attempts,\n diagnosis,\n summary: `${cause}${ifMint}; ${served}`,\n };\n}\n"],"mappings":"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","names":["isUuidShape","isUuidShape","mime","resolution","blob"]}
|
|
1
|
+
{"version":3,"sources":["../src/credentials.ts","../src/files/errors.ts","../src/files/recognize.ts","../src/files/client.ts","../src/files/session.ts","../src/files/heal.ts","../src/files/dedup.ts"],"sourcesContent":["/**\n * The credentials port — the ONE credential→header mapping for every client\n * package (DATA-PACKAGE-DESIGN.md \"The credentials port\"; API-key lane ratified\n * as C16, 2026-08-29).\n *\n * Three credential kinds, one mapping, in exactly one place:\n *\n * - `user` — a Supabase JWT: `Authorization: Bearer <jwt>` (both lanes)\n * - `guest` — a fingerprint: `X-Fingerprint-ID: <id>` (API lane only)\n * - `api_key` — an AI Matrx API key: `Authorization: Bearer mx_live_...`.\n * The server's ONE AuthMiddleware disambiguates by shape (`mx_` prefix vs\n * three-dot JWT), so both `user` and `api_key` ride the same header.\n *\n * No polling, no readiness loop: readiness is the port's promise.\n */\n\n/** A credential a Matrx client can present. */\nexport type MatrxCredential =\n | { kind: \"user\"; accessToken: string }\n | { kind: \"guest\"; fingerprintId: string }\n | { kind: \"api_key\"; apiKey: string };\n\n/**\n * The async source of the current credential. `get()` resolves when the host\n * knows its auth state (null = anonymous); `onChange` lets transports refresh\n * cached headers when the credential rotates.\n */\nexport interface CredentialsPort {\n get(): Promise<MatrxCredential | null>;\n onChange?(cb: () => void): () => void;\n}\n\n/** Prefix that marks a bearer token as an AI Matrx API key, never a JWT. */\nexport const API_KEY_PREFIX = \"mx_\";\n\n/**\n * Map a credential to the exact request headers the AI Matrx platform expects.\n * This is the single place that knowledge lives — transports spread the result\n * into their request headers and add `X-Organization-Id` from the org context\n * binding separately.\n */\nexport function credentialToHeaders(\n credential: MatrxCredential | null,\n): Record<string, string> {\n if (credential === null) return {};\n switch (credential.kind) {\n case \"user\":\n return { Authorization: `Bearer ${credential.accessToken}` };\n case \"guest\":\n return { \"X-Fingerprint-ID\": credential.fingerprintId };\n case \"api_key\": {\n if (!credential.apiKey.startsWith(API_KEY_PREFIX)) {\n throw new Error(\n `credentialToHeaders: an api_key credential must start with \"${API_KEY_PREFIX}\" ` +\n \"(AI Matrx API keys look like mx_live_...)\",\n );\n }\n return { Authorization: `Bearer ${credential.apiKey}` };\n }\n }\n}\n","/**\n * @ai-matrx/data/files — error taxonomy + the ONE unavailable-state classifier.\n *\n * Ported from matrx-frontend `features/files/handler/errors.ts`. The crucial\n * distinction this lane enforces:\n *\n * - `FileAccessDeniedError` → the user does NOT have access (a proven 403\n * from our own file server). Reject. Never retry.\n * - a transient auth failure on a durable URL → refresh the file session\n * and retry the SAME URL (the client's `recoverLoadError`); never a\n * terminal error.\n *\n * \"Unavailable is a rendered state, not an incident\" (handler law 15): the\n * typed states classify via {@link classifyUnavailable} and render as error\n * panels — this module never logs.\n */\n\nimport type { MediaUnavailableReason } from \"./types\";\n\nexport type FileErrorCode =\n | \"access_denied\"\n | \"not_found\"\n | \"deleted\"\n | \"share_link_invalid\"\n | \"external_fetch_failed\"\n | \"cors_blocked\"\n | \"mime_unknown\"\n | \"upload_failed\"\n | \"upload_cancelled\"\n | \"quota_exceeded\"\n | \"in_flight\"\n | \"internal\";\n\nexport class FileError extends Error {\n readonly code: FileErrorCode;\n readonly fileId?: string | undefined;\n override readonly cause?: unknown;\n\n constructor(\n code: FileErrorCode,\n message: string,\n opts?: { fileId?: string | undefined; cause?: unknown },\n ) {\n super(message);\n this.name = \"FileError\";\n this.code = code;\n this.fileId = opts?.fileId;\n this.cause = opts?.cause;\n }\n}\n\nexport class FileAccessDeniedError extends FileError {\n constructor(\n // A real 403 from our own file server — a proven refusal, not a guess.\n message = \"This file isn't available to you.\",\n opts?: { fileId?: string | undefined },\n ) {\n super(\"access_denied\", message, opts);\n this.name = \"FileAccessDeniedError\";\n }\n}\n\nexport class FileNotFoundError extends FileError {\n // A 404 from the file server: the server could not retrieve the file —\n // NOT that it was deleted (that is FileDeletedError) and not that it\n // never existed.\n constructor(\n message = \"We couldn't retrieve this file.\",\n opts?: { fileId?: string | undefined },\n ) {\n super(\"not_found\", message, opts);\n this.name = \"FileNotFoundError\";\n }\n}\n\nexport class FileDeletedError extends FileError {\n constructor(message = \"File is in trash\", opts?: { fileId?: string | undefined }) {\n super(\"deleted\", message, opts);\n this.name = \"FileDeletedError\";\n }\n}\n\nexport class ShareLinkInvalidError extends FileError {\n constructor(message = \"Share link is invalid, expired, or revoked\") {\n super(\"share_link_invalid\", message);\n this.name = \"ShareLinkInvalidError\";\n }\n}\n\nexport class ExternalFetchError extends FileError {\n constructor(message = \"Failed to fetch external URL\", opts?: { cause?: unknown }) {\n super(\"external_fetch_failed\", message, opts);\n this.name = \"ExternalFetchError\";\n }\n}\n\nexport class FileUploadError extends FileError {\n constructor(message: string, opts?: { cause?: unknown }) {\n super(\"upload_failed\", message, opts);\n this.name = \"FileUploadError\";\n }\n}\n\n/**\n * Map an HTTP status from OUR file server's byte/record routes onto a typed\n * error. 401 is deliberately NOT access-denied — a 401 on a durable URL is\n * the session-refresh lane, never a proven refusal.\n */\nexport function fileErrorFromStatus(\n status: number,\n fileId?: string,\n): FileError | null {\n if (status === 403)\n return new FileAccessDeniedError(undefined, fileId === undefined ? {} : { fileId });\n if (status === 404)\n return new FileNotFoundError(undefined, fileId === undefined ? {} : { fileId });\n if (status === 410) return new ShareLinkInvalidError();\n return null;\n}\n\n/**\n * The typed-unavailable classifier behind `MediaClient.classifyError`.\n * Matches this package's own classes AND — structurally, by `code` — any\n * host error taxonomy that speaks the same vocabulary (the origin handler's\n * `FileHandlerError` classifies without an instanceof relationship).\n */\nexport function classifyUnavailable(err: unknown): MediaUnavailableReason {\n if (err instanceof FileAccessDeniedError) return \"access_denied\";\n if (err instanceof FileNotFoundError) return \"not_found\";\n if (err instanceof FileDeletedError) return \"deleted\";\n if (err && typeof err === \"object\" && \"code\" in err) {\n const code = (err as { code?: unknown }).code;\n if (code === \"access_denied\" || code === \"not_found\" || code === \"deleted\") {\n return code;\n }\n }\n return \"unknown\";\n}\n","/**\n * @ai-matrx/data/files — URL classification and recognition.\n *\n * Three origin modules collapse here, behavior-verbatim (the D108 scar\n * tissue): matrx-frontend `lib/media/signed-url.ts` (the ONE legacy\n * signed-URL classifier, both AWS dialects), `lib/media/durability.ts`\n * (durable-vs-expiring classification + the fail-closed share guard), and\n * the identity-recovering pieces of `lib/media/our-file-sources.ts`\n * (byte-endpoint promotion, signed-bucket file_id recovery, CDN detection).\n *\n * What generalized at port time: the HOST NAMES. The origin hardcoded\n * `matrxserver.com` / `matrx-user-files.s3`; here the origin list is\n * injected as a VALUE ({@link FileOrigins}) with the production topology as\n * the shipped default. The signed-URL classification itself is\n * host-independent and deliberately non-configurable.\n */\n\nimport type { FileOrigins } from \"./types\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\n\n// ---------------------------------------------------------------------------\n// Signed / expiring URL classification (host-independent, non-configurable)\n// ---------------------------------------------------------------------------\n\n/**\n * Matches either AWS signing dialect (SigV2 query auth, SigV4 presigned).\n * Intentionally broad: presence of any of these query keys means the URL\n * carries a time-limited signature and must be treated as a legacy expiring\n * URL — never durable, never persisted as identity.\n */\nexport const SIGNED_URL_RE =\n /[?&](x-amz-signature|x-amz-credential|x-amz-date|x-amz-expires|awsaccesskeyid|signature|expires)=/i;\n\n/** True when `url` is a legacy signed/expiring URL in either AWS dialect. */\nexport function isSignedUrl(url: string | null | undefined): boolean {\n if (!url) return false;\n return SIGNED_URL_RE.test(url);\n}\n\n/**\n * A revocable share link (`…/share/<token>/download|view|raw`) is never a\n * durable identity — being revocable is the entire POINT of a share link.\n * It carries no signature, so the signed-URL test alone called it durable;\n * that is exactly how D108's screenshots became dead pointers. Kept in\n * parity with the DB twin `public.mtx_is_durable_media_url`.\n */\nconst REVOCABLE_SHARE_URL_RE =\n /\\/share\\/[0-9a-f-]{8,}\\/(download|view|raw)(\\?|$)/i;\n\nexport type MediaUrlKind = \"empty\" | \"durable\" | \"expiring\";\n\n/** Mirror of the DB `mtx_is_durable_media_url` classifier. */\nexport function classifyMediaUrl(url: string | null | undefined): MediaUrlKind {\n if (!url) return \"empty\";\n if (isSignedUrl(url) || REVOCABLE_SHARE_URL_RE.test(url)) return \"expiring\";\n return \"durable\";\n}\n\nexport function isDurableMediaUrl(url: string | null | undefined): boolean {\n return classifyMediaUrl(url) !== \"expiring\";\n}\n\n/**\n * Return a URL only when it is safe to expose through a share/copy action.\n * A presigned object-store URL is a bearer credential; sharing one is\n * banned. FAILS CLOSED: the caller mints a canonical share link (or uses an\n * internal viewer URL) when this returns null.\n */\nexport function shareableMediaUrl(\n url: string | null | undefined,\n): string | null {\n if (!url || !isDurableMediaUrl(url)) return null;\n return url;\n}\n\n// ---------------------------------------------------------------------------\n// Origin-aware recognition (the injected origin list)\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_FILE_ENDPOINT_HOST_RE =\n /(^|\\.)matrxserver\\.com$|^localhost$|^127\\.0\\.0\\.1$/i;\nconst DEFAULT_USER_FILES_HOST_RE =\n /(^|\\.)matrx-user-files\\.s3|s3[.-].*amazonaws\\.com/i;\nconst DEFAULT_CDN_HOST_RE = /(^|\\.)cdn\\.matrxserver\\.com$/i;\n\nconst FILE_ENDPOINT_PATH_RE =\n /^\\/(?:files\\/([0-9a-f-]{36})\\/download|media\\/([0-9a-f-]{36})\\/v\\/[^/]+)\\/?$/i;\n\nexport interface FileUrlRecognizer {\n /**\n * `{base}/files/{file_id}/download[?…]` or `{base}/media/{file_id}/v/{class}`\n * on one of OUR hosts — an IDENTITY in disguise (QA F2): rendered as an\n * opaque external URL it fails for everyone whose file-session cookie\n * isn't fresh, and canvas consumers fetch it with no Authorization header.\n * Returns the file_id so callers promote it to the file_id lane.\n */\n fileIdFromFileEndpointUrl(url: string): string | null;\n /**\n * Recover the file_id from a legacy signed user-files URL\n * (`…/{user_id}/{file_id}?X-Amz-…`) so a durable URL can be re-minted\n * from identity forever.\n */\n fileIdFromUserFilesUrl(url: string): string | null;\n /** Permanent public CDN URL (injected CDN hosts + the generic heuristic). */\n looksLikeCdnUrl(url: string): boolean;\n /** True when the URL points at any of OUR recognized origins. */\n isOurUrl(url: string): boolean;\n}\n\nexport function createFileUrlRecognizer(\n origins: FileOrigins = {},\n): FileUrlRecognizer {\n const endpointHosts = origins.fileEndpointHosts ?? DEFAULT_FILE_ENDPOINT_HOST_RE;\n const userFilesHosts = origins.userFilesHosts ?? DEFAULT_USER_FILES_HOST_RE;\n const cdnHosts = origins.cdnHosts ?? DEFAULT_CDN_HOST_RE;\n\n function parse(url: string): URL | null {\n try {\n return new URL(url);\n } catch {\n return null;\n }\n }\n\n function fileIdFromFileEndpointUrl(url: string): string | null {\n const u = parse(url);\n if (!u || !endpointHosts.test(u.hostname)) return null;\n const m = u.pathname.match(FILE_ENDPOINT_PATH_RE);\n const candidate = m ? (m[1] ?? m[2]) : null;\n return candidate && isUuidShape(candidate) ? candidate : null;\n }\n\n function fileIdFromUserFilesUrl(url: string): string | null {\n const u = parse(url);\n if (!u || !userFilesHosts.test(u.hostname)) return null;\n const segs = u.pathname.split(\"/\").filter(Boolean); // [user_id, file_id, …]\n const candidate = segs[1] ?? segs[segs.length - 1];\n return candidate && isUuidShape(candidate) ? candidate : null;\n }\n\n function looksLikeCdnUrl(url: string): boolean {\n const u = parse(url);\n if (u && cdnHosts.test(u.hostname)) return true;\n // The origin's generic heuristic (media-client `looksLikeCdnUrl`).\n return url.startsWith(\"https://cdn.\") || url.includes(\"/cdn/\");\n }\n\n function isOurUrl(url: string): boolean {\n return (\n fileIdFromFileEndpointUrl(url) !== null ||\n fileIdFromUserFilesUrl(url) !== null ||\n (() => {\n const u = parse(url);\n return !!u && cdnHosts.test(u.hostname);\n })()\n );\n }\n\n return { fileIdFromFileEndpointUrl, fileIdFromUserFilesUrl, looksLikeCdnUrl, isOurUrl };\n}\n\n// ---------------------------------------------------------------------------\n// Mime sniffing (origin: our-file-sources.ts)\n// ---------------------------------------------------------------------------\n\nconst EXT_MIME: Record<string, string> = {\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n svg: \"image/svg+xml\",\n pdf: \"application/pdf\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n m4a: \"audio/mp4\",\n ogg: \"audio/ogg\",\n mp4: \"video/mp4\",\n webm: \"video/webm\",\n mov: \"video/quicktime\",\n csv: \"text/csv\",\n json: \"application/json\",\n md: \"text/markdown\",\n txt: \"text/plain\",\n html: \"text/html\",\n};\n\n/**\n * Best-effort mime from a URL: prefer an explicit `response-content-type`\n * query param (our legacy signed URLs carry it), else map a path extension.\n * Returns null when nothing is recognizable — the caller then hydrates the\n * file record for the real type.\n */\nexport function mimeFromUrl(url: string): string | null {\n try {\n const u = new URL(url);\n const ct = u.searchParams.get(\"response-content-type\");\n if (ct) return decodeURIComponent(ct);\n } catch {\n // fall through to extension sniffing on the raw string\n }\n const path = url.split(/[?#]/)[0] ?? \"\";\n const ext = path.match(/\\.([a-z0-9]+)$/i)?.[1]?.toLowerCase();\n return ext ? (EXT_MIME[ext] ?? null) : null;\n}\n\n/**\n * Best-effort file NAME from a URL: prefer the `filename=\"…\"` carried in our\n * legacy signed URLs' `response-content-disposition`, else the last path\n * segment when it actually looks like a name (has an extension). Returns null\n * otherwise — a UUID path segment is an id, not a name, and must never be\n * shown as one.\n */\nexport function fileNameFromUrl(url: string): string | null {\n try {\n const u = new URL(url);\n const disposition = u.searchParams.get(\"response-content-disposition\");\n const named = disposition?.match(/filename\\*?=(?:UTF-8'')?\"?([^\";]+)\"?/i);\n if (named?.[1]) return decodeURIComponent(named[1]).trim() || null;\n } catch {\n // fall through to path sniffing on the raw string\n }\n const last = url.split(/[?#]/)[0]?.split(\"/\").filter(Boolean).pop();\n if (!last || !/\\.[a-z0-9]{1,5}$/i.test(last)) return null;\n return decodeURIComponent(last);\n}\n\n// ---------------------------------------------------------------------------\n// Durable URL builders (origin: handler/utils/python-base.ts). THE one URL\n// contract — no other module in this package concatenates byte-route URLs.\n// ---------------------------------------------------------------------------\n\nexport interface FileUrls {\n /** Authenticated direct-download endpoint (`Content-Disposition: attachment`). */\n download: string;\n /** Same bytes, `Content-Disposition: inline` — works as `<img src>` etc. */\n inline: string;\n}\n\n/**\n * Build every durable URL variant for a cld_files id. These URLs never\n * expire and carry only the file id; auth is an Authorization header on\n * `fetch()` or the `mx_files_session` cookie for element bindings.\n * `?inline=1` matches the exact spelling the backend emits on\n * `FileRecord.url`, keeping cache keys identical either way.\n */\nexport function buildFileUrls(filesBaseUrl: string, fileId: string): FileUrls {\n const id = encodeURIComponent(fileId);\n const base = `${filesBaseUrl.replace(/\\/$/, \"\")}/files/${id}/download`;\n return { download: base, inline: `${base}?inline=1` };\n}\n\n\n/**\n * The clean public byte-streaming share endpoint (`GET /share/{token}`,\n * inline disposition) — the canonical embeddable URL for a minted link.\n */\nexport function shareUrl(filesBaseUrl: string, token: string): string {\n return `${filesBaseUrl.replace(/\\/$/, \"\")}/share/${encodeURIComponent(token)}`;\n}\n","/**\n * @ai-matrx/data/files — THE Matrx file-communication client.\n *\n * The client SDK for the AI Dream file contract — the communication half of\n * the media seam (C13). Behavior is ported verbatim from matrx-frontend's\n * `features/files/media-client/client.ts` (the 436-line host adapter this\n * tier exists to collapse) + `features/files/handler/{session,errors}.ts` +\n * `lib/media/{durability,signed-url,our-file-sources}.ts`; only the coupling\n * seams inverted — injected values and ports instead of Redux singletons and\n * `NEXT_PUBLIC_*` reads. Wire shapes are verified against the aidream server\n * source (`packages/matrx-files/matrx_files/api/router_files.py`,\n * `records.py`, `router_share.py`) — see `types.ts` `*Wire` interfaces.\n *\n * The client structurally satisfies `@ai-matrx/media`'s `MediaClient` port\n * (compile-time proof: media's own `port-satisfaction.test.ts`) and adds the doors media never\n * needs as separate calls: `ensureSession`, `shareableUrlNoMint` (the\n * copy-never-mints door), and the recognizer.\n *\n * Law enforcement lives behind `resolve()`:\n * - a signed/expiring URL is REFUSED (screams via the diagnostics sink,\n * then throws) — law 3 / D108;\n * - private image pixels ride the bearer-authenticated blob lane\n * (`transport: \"blob\"`);\n * - video/audio and public CDN URLs bind durable URLs directly\n * (`mx_files_session` cookie / permanent CDN).\n */\n\nimport { credentialToHeaders, type CredentialsPort } from \"../credentials\";\nimport {\n classifyUnavailable,\n ExternalFetchError,\n FileError,\n fileErrorFromStatus,\n FileUploadError,\n} from \"./errors\";\nimport {\n classifyMediaUrl,\n createFileUrlRecognizer,\n buildFileUrls,\n mimeFromUrl,\n shareableMediaUrl,\n shareUrl,\n type FileUrlRecognizer,\n} from \"./recognize\";\nimport { isUuidShape } from \"@ai-matrx/kit/uuid\";\nimport { createFilesSession, type FilesSession } from \"./session\";\nimport { healLoadErrorLadder } from \"./heal\";\nimport type {\n BlobCacheEntry,\n BlobCachePort,\n DurableSrc,\n FileMetadata,\n FileMetadataPort,\n FileOrigins,\n FileRecordWire,\n FilesBlobHandle,\n FilesDiagnosticsSink,\n FilesHealAttempt,\n FilesHealResult,\n FilesRecoveryReport,\n FilesRecoveryVerdict,\n FilesResolution,\n FileShownTo,\n FileUploadResponseWire,\n MediaKind,\n MediaMultiUploadResult,\n MediaRef,\n MediaRefLike,\n MediaUnavailableReason,\n MediaUploadOptions,\n MediaUploadResult,\n ShareLinkRecordWire,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Transport policy (origin: upload/cloudUpload.ts — the ONE decision point)\n// ---------------------------------------------------------------------------\n\n/** Files at or above this size must go resumable (TUS). */\nexport const TUS_TRANSPORT_THRESHOLD_BYTES = 80 * 1024 * 1024;\n\nexport type UploadTransport = \"buffered\" | \"tus\";\n\n/** The ONE place the buffered-vs-TUS decision is made. */\nexport function resolveUploadTransport(\n fileSizeBytes: number,\n override?: UploadTransport,\n): UploadTransport {\n if (override) return override;\n return fileSizeBytes >= TUS_TRANSPORT_THRESHOLD_BYTES ? \"tus\" : \"buffered\";\n}\n\n// ---------------------------------------------------------------------------\n// Configuration\n// ---------------------------------------------------------------------------\n\nexport interface MatrxFilesClientConfig {\n /** The credentials port (core) — maps to Bearer / X-Fingerprint-ID headers. */\n credentials: CredentialsPort;\n /**\n * The backend base serving the durable file byte routes (main backend or\n * the standalone files host). A function is re-resolved per use — pass one\n * when the host's routing ladder can change at runtime. May throw while\n * unresolved (SSR/early boot); the session lane treats that as \"nothing to\n * establish\" and the resolve lane surfaces it as a plain error.\n */\n filesBaseUrl: string | (() => string);\n /**\n * Additional bases the `mx_files_session` cookie must be established on\n * (cookies are per-host; the main backend and the standalone files host\n * both serve durable URLs). Defaults to just `filesBaseUrl`.\n */\n sessionBases?: (string | (() => string))[] | undefined;\n /** The host's URL identity — every member has a Matrx-topology default. */\n origins?: FileOrigins | undefined;\n /** Failure/violation sink. Default: a loud `console.error` scream. */\n diagnostics?: FilesDiagnosticsSink | undefined;\n /** Injected transport. Default: `globalThis.fetch`. */\n fetchImpl?: typeof fetch | undefined;\n /** Metadata source override (host stores). Default: `GET /files/{id}` cache. */\n metadata?: FileMetadataPort | undefined;\n /** Byte-cache override (persistent tiers). Default: in-memory LRU (250 MB). */\n blobCache?: BlobCachePort | undefined;\n /** Byte budget for the DEFAULT in-memory blob cache. */\n blobCacheBudgetBytes?: number | undefined;\n /**\n * RN's cookie-less lane: when true, `resolve()` attaches the current auth\n * headers to non-CDN resolutions (`<Image source={{uri, headers}}>`).\n * `resolve()` is synchronous, so the header snapshot is primed lazily from\n * the credentials port (and refreshed on `onChange`); call\n * `client.primeAuth()` at boot to guarantee the first resolve carries them.\n * Web hosts leave this off — the cookie session authenticates elements.\n */\n attachAuthHeadersToResolution?: boolean | undefined;\n /**\n * The resumable transport for files ≥ {@link TUS_TRANSPORT_THRESHOLD_BYTES}.\n * v1 of this tier ships the buffered multipart transport only — the TUS\n * transport (16 MiB chunks, IndexedDB resume URLs, `router_tus.py`) is\n * live-unverified at the origin (handler FEATURE.md invariant 10) and\n * lands with real browser proof; until then a host that needs it injects\n * its own. Absent + oversized file → typed `FileUploadError`.\n */\n largeUploadTransport?:\n | ((file: File, opts: MediaUploadOptions) => Promise<MediaUploadResult>)\n | undefined;\n /**\n * Host batch-upload door (dedup pre-flight dialogs, folder-id targeting).\n * Default: sequential `upload()` per file, never cancelled.\n */\n uploadMany?:\n | ((files: File[], opts: MediaUploadOptions) => Promise<MediaMultiUploadResult>)\n | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// The client surface\n// ---------------------------------------------------------------------------\n\nexport interface MatrxFilesClient<TSrc extends string = DurableSrc> {\n /**\n * Synchronous durable-URL build + classification. Returns null when the\n * ref carries no usable identity. THROWS (and screams through the sink)\n * on a signed/expiring URL — the D108 guard lives behind this door.\n */\n resolve(ref: MediaRefLike): FilesResolution<TSrc> | null;\n /** Bytes for canvas/crossOrigin/private-pixel consumers. */\n getBlob(ref: MediaRefLike): Promise<FilesBlobHandle>;\n /**\n * The ONE retry policy: session refresh → same-URL retry, second failure\n * terminal. Called by the render layer's onError path, never re-implemented.\n * Returns a {@link FilesRecoveryReport} (media 0.3.0 `MediaRecoveryReport`\n * twin) when the session lane learned the mint's `secret_source` and/or a\n * failed mint's HTTP status — so a host wiring this client into\n * `@ai-matrx/media` inherits the ephemeral-secret scream automatically.\n * Against an older server it stays the bare string verdict.\n */\n recoverLoadError(\n src: TSrc,\n attempt: number,\n ): Promise<FilesRecoveryVerdict | FilesRecoveryReport>;\n /** ONE retry policy for a failed object URL backed by a durable file ref. */\n recoverBlobLoadError(\n ref: MediaRefLike,\n attempt: number,\n ): Promise<FilesRecoveryVerdict>;\n /**\n * The heal ladder (media `MediaClient.healLoadError` twin): runs AFTER the\n * ONE retry is exhausted. Bearer byte fetch on the primary host, then on\n * the alternate host; every attempt recorded; the root cause named. Never\n * throws — a dead ladder is a result with `src: null`.\n */\n healLoadError(\n ref: MediaRefLike,\n context: { src: TSrc; attempts: FilesHealAttempt[] },\n ): Promise<FilesHealResult>;\n /** Buffered multipart upload → durable identity (`POST /files/upload`). */\n upload(file: File | Blob, opts?: MediaUploadOptions): Promise<MediaUploadResult>;\n uploadMany(files: File[], opts?: MediaUploadOptions): Promise<MediaMultiUploadResult>;\n /**\n * THE public-link door (media 0.2.2 semantics): permanent CDN URL for\n * public files, else reuse-or-mint a no-expiry read-only share link.\n * Minting is a click-time side effect — call this ON CLICK, never on\n * render. Fails closed (null), never emits a signed URL (law 4).\n */\n shareableUrl(ref: MediaRefLike): Promise<string | null>;\n /**\n * Side-effect-free twin for the quick COPY action: returns a durable\n * public URL when one already exists and null otherwise — it NEVER mints\n * a share link (minting is the SHARE door's job).\n */\n shareableUrlNoMint(ref: MediaRefLike): string | null;\n /** Typed unavailable states — render-as-state, never thrown as incidents. */\n classifyError(err: unknown): MediaUnavailableReason;\n /** The `mx_files_session` bootstrap — idempotent, deduped, never throws. */\n ensureSession(opts?: { force?: boolean | undefined }): Promise<void>;\n /** Prime the auth-header snapshot for the RN resolution lane. */\n primeAuth(): Promise<void>;\n /** The origin recognizer bound to this client's injected origin list. */\n recognizer: FileUrlRecognizer;\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nfunction defaultDiagnostics(event: {\n source: string;\n context: string;\n message: string;\n detail?: string | undefined;\n}): void {\n if (event.source === \"media-durability\") {\n // Verbatim scream from the origin durability guard: a non-public,\n // expiring URL reaching a render/store path is a server-side defect —\n // never papered over silently.\n console.error(\n \"\\n================ MEDIA-DURABILITY VIOLATION ================\\n\" +\n `A non-public, EXPIRING media URL reached \"${event.context}\".\\n` +\n \"This must never be persisted/rendered for public or owned media — it WILL\\n\" +\n \"break when the signature expires. The media should have been saved PUBLIC\\n\" +\n \"at generation (durable CDN/public-bucket URL), or resolved from its\\n\" +\n \"file_id (which re-mints a durable URL forever).\\n\" +\n `URL: ${(event.detail ?? \"\").slice(0, 180)}\\n` +\n \"===========================================================\\n\",\n );\n return;\n }\n console.error(`[files:${event.source}] ${event.message}`, event.detail ?? \"\");\n}\n\nconst DEFAULT_BLOB_BUDGET_BYTES = 250 * 1024 * 1024;\n\n/**\n * The default byte cache: the origin's tier-1 in-memory LRU (blob-cache.ts),\n * byte-budgeted; the cache OWNS the object URLs and revokes on eviction.\n * Persistent tiers (IndexedDB, the service worker) stay host-injected.\n */\nexport function createMemoryBlobCache(\n budgetBytes = DEFAULT_BLOB_BUDGET_BYTES,\n): BlobCachePort {\n interface Entry extends BlobCacheEntry {\n bytes: number;\n }\n const cache = new Map<string, Entry>();\n let totalBytes = 0;\n\n return {\n get(fileId: string): BlobCacheEntry | null {\n const entry = cache.get(fileId);\n if (!entry) return null;\n // Bump the LRU position (Map iteration order = insertion order).\n cache.delete(fileId);\n cache.set(fileId, entry);\n return entry;\n },\n set(fileId: string, blob: Blob, url: string): void {\n const existing = cache.get(fileId);\n if (existing) {\n totalBytes -= existing.bytes;\n cache.delete(fileId);\n if (existing.url !== url) {\n try {\n URL.revokeObjectURL(existing.url);\n } catch {\n /* revocation must never break the caller */\n }\n }\n }\n const bytes = blob.size;\n cache.set(fileId, { blob, url, bytes });\n totalBytes += bytes;\n for (const [key, entry] of cache) {\n if (totalBytes <= budgetBytes || key === fileId) break;\n cache.delete(key);\n totalBytes -= entry.bytes;\n try {\n URL.revokeObjectURL(entry.url);\n } catch {\n /* revocation must never break the caller */\n }\n }\n },\n invalidate(fileId: string): void {\n const existing = cache.get(fileId);\n if (!existing) return;\n cache.delete(fileId);\n totalBytes -= existing.bytes;\n try {\n URL.revokeObjectURL(existing.url);\n } catch {\n /* revocation must never break recovery */\n }\n },\n };\n}\n\n/**\n * The upload's row-access form fields (access ladder T-13). The two words are written\n * explicitly on every upload — never left to a server default: not published, shown to\n * only me, unless the caller says otherwise. A caller still on the retiring level word\n * is translated here, once.\n */\nexport function uploadAccessFields(opts: MediaUploadOptions): Record<string, string> {\n const legacy = opts.visibility; // T-13 transitional wire input\n const published = opts.publishedToWeb ?? (legacy === undefined ? false : legacy === \"public\");\n const shownTo: FileShownTo | null =\n opts.shownTo ??\n (opts.publishedToWeb !== undefined || legacy === undefined || legacy === \"personal\" ? \"only_me\" : null);\n const fields: Record<string, string> = { published_to_web: String(published) };\n if (shownTo) fields.shown_to = shownTo;\n return fields;\n}\n\nfunction recordToMetadata(record: FileRecordWire): FileMetadata {\n return {\n mimeType: record.mime_type ?? null,\n publishedToWeb: record.published_to_web ?? null,\n cdnUrl: record.cdn_url ?? null,\n publicUrl: record.public_url ?? null,\n thumbnailUrl: record.thumbnail_url ?? null,\n deletedAt: record.deleted_at ?? null,\n fileName: record.file_name ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createMatrxFilesClient<TSrc extends string = DurableSrc>(\n config: MatrxFilesClientConfig,\n): MatrxFilesClient<TSrc> {\n const diagnostics: FilesDiagnosticsSink =\n config.diagnostics ?? defaultDiagnostics;\n const fetchImpl: typeof fetch =\n config.fetchImpl ?? ((...args) => globalThis.fetch(...args));\n const recognizer = createFileUrlRecognizer(config.origins ?? {});\n const blobCache = config.blobCache ?? createMemoryBlobCache(config.blobCacheBudgetBytes);\n\n function resolveBase(entry: string | (() => string)): string {\n return typeof entry === \"function\" ? entry() : entry;\n }\n\n function filesBase(): string {\n return resolveBase(config.filesBaseUrl).replace(/\\/$/, \"\");\n }\n\n async function authHeaders(\n extra?: Record<string, string>,\n ): Promise<Record<string, string>> {\n const credential = await config.credentials.get();\n return { ...credentialToHeaders(credential), ...extra };\n }\n\n // --- session ------------------------------------------------------------\n\n const session: FilesSession = createFilesSession({\n resolveBases: () => {\n const entries = config.sessionBases ?? [config.filesBaseUrl];\n const bases: string[] = [];\n for (const entry of entries) {\n try {\n bases.push(resolveBase(entry).replace(/\\/$/, \"\"));\n } catch {\n // This base is unresolved (SSR/early boot) — others still count.\n }\n }\n return bases;\n },\n buildHeaders: () => authHeaders(),\n fetchImpl,\n diagnostics,\n });\n\n // --- auth-header snapshot (RN resolution lane) --------------------------\n\n let headerSnapshot: Record<string, string> | null = null;\n let headerPrimeStarted = false;\n\n async function refreshHeaderSnapshot(): Promise<void> {\n try {\n headerSnapshot = await authHeaders();\n } catch {\n headerSnapshot = null;\n }\n }\n\n function primeHeadersLazily(): void {\n if (headerPrimeStarted) return;\n headerPrimeStarted = true;\n void refreshHeaderSnapshot();\n config.credentials.onChange?.(() => {\n void refreshHeaderSnapshot();\n });\n }\n\n // --- metadata -----------------------------------------------------------\n\n const metadata: FileMetadataPort =\n config.metadata ?? createDefaultMetadataPort();\n\n function createDefaultMetadataPort(): FileMetadataPort {\n const cache = new Map<string, FileMetadata>();\n const inflight = new Map<string, Promise<FileMetadata | undefined>>();\n return {\n peek(fileId: string): FileMetadata | undefined {\n return cache.get(fileId);\n },\n hydrate(fileId: string): Promise<FileMetadata | undefined> {\n const cached = cache.get(fileId);\n if (cached) return Promise.resolve(cached);\n let pending = inflight.get(fileId);\n if (!pending) {\n pending = (async () => {\n const headers = await authHeaders({ Accept: \"application/json\" });\n const response = await fetchImpl(\n `${filesBase()}/files/${encodeURIComponent(fileId)}`,\n { headers },\n );\n if (!response.ok) {\n const typed = fileErrorFromStatus(response.status, fileId);\n if (typed) throw typed;\n throw new FileError(\n \"internal\",\n `GET /files/${fileId} → HTTP ${response.status}`,\n { fileId },\n );\n }\n const record = (await response.json()) as FileRecordWire;\n const meta = recordToMetadata(record);\n cache.set(fileId, meta);\n return meta;\n })().finally(() => {\n inflight.delete(fileId);\n });\n inflight.set(fileId, pending);\n }\n return pending;\n },\n };\n }\n\n /**\n * Fire-and-forget metadata hydration for a file id (public/CDN detection,\n * mime, thumbnail). Deduped per id; scheduled on a microtask so a\n * render-time `resolve()` never performs I/O in the render phase. A failed\n * attempt allows a later re-request (mirrors the origin's scheduleHydration).\n */\n const hydrationRequested = new Set<string>();\n function scheduleHydration(fileId: string): void {\n if (hydrationRequested.has(fileId)) return;\n if (metadata.peek(fileId)) return;\n hydrationRequested.add(fileId);\n queueMicrotask(() => {\n void metadata\n .hydrate(fileId)\n .then((meta) => {\n if (!meta) hydrationRequested.delete(fileId);\n })\n .catch(() => {\n hydrationRequested.delete(fileId);\n });\n });\n }\n\n // --- ref normalization (origin: media-client normalizeRef) --------------\n\n type NormalizedRef =\n | { kind: \"file_id\"; fileId: string; mime: string | null }\n | { kind: \"url\"; url: string; mime: string | null }\n | null;\n\n function normalizeUrl(url: string, mime: string | null): NormalizedRef {\n // A URL pointing at one of OUR authenticated byte endpoints is an\n // IDENTITY in disguise (QA F2) — promote it to the file_id lane so\n // pixels ride the bearer-authenticated blob transport.\n const endpointFileId = recognizer.fileIdFromFileEndpointUrl(url);\n if (endpointFileId) {\n return {\n kind: \"file_id\",\n fileId: endpointFileId,\n mime: mime ?? mimeFromUrl(url),\n };\n }\n return { kind: \"url\", url, mime };\n }\n\n function normalizeRef(ref: MediaRefLike | null | undefined): NormalizedRef {\n if (!ref) return null;\n if (typeof ref === \"string\") {\n if (isUuidShape(ref)) return { kind: \"file_id\", fileId: ref, mime: null };\n if (!ref.trim()) return null;\n return normalizeUrl(ref, null);\n }\n if (ref.file_id) {\n return { kind: \"file_id\", fileId: ref.file_id, mime: ref.mime_type ?? null };\n }\n if (ref.url) return normalizeUrl(ref.url, ref.mime_type ?? null);\n return null;\n }\n\n function mimeToKind(mime: string | null): MediaKind {\n if (!mime) return \"image\"; // matches the origin's <img> default\n if (mime.startsWith(\"image/\")) return \"image\";\n if (mime.startsWith(\"video/\")) return \"video\";\n if (mime.startsWith(\"audio/\")) return \"audio\";\n return \"file\";\n }\n\n function refuseExpiringUrl(url: string, context: string): never {\n diagnostics({\n source: \"media-durability\",\n context,\n message: `Expiring media URL reached \"${context}\" — should be public/durable`,\n detail: String(url).slice(0, 300),\n });\n throw new FileError(\n \"internal\",\n \"Expiring/signed media URL refused — a signed URL is a handoff, never an identity. Pass the file_id.\",\n );\n }\n\n function mint(url: string): TSrc {\n // The brand exists to stop UNCLASSIFIED strings; every call site here\n // sits behind the classifier/normalizer, which is the entire point.\n return url as string as TSrc;\n }\n\n // --- blob lane ----------------------------------------------------------\n\n const inflightDownloads = new Map<string, Promise<Blob>>();\n\n async function fetchFileBytes(fileId: string): Promise<Blob> {\n const headers = await authHeaders();\n const response = await fetchImpl(buildFileUrls(filesBase(), fileId).download, {\n headers,\n });\n if (!response.ok) {\n const typed = fileErrorFromStatus(response.status, fileId);\n if (typed) throw typed;\n throw new FileError(\n \"internal\",\n `GET /files/${fileId}/download → HTTP ${response.status}`,\n { fileId },\n );\n }\n return response.blob();\n }\n\n async function fileIdBlob(fileId: string): Promise<{ url: string; blob: Blob }> {\n if (!isUuidShape(fileId)) {\n throw new FileError(\n \"internal\",\n `getBlob: \"${fileId}\" is not a cld_files id — host-namespaced/virtual ids are resolved by the host before this client.`,\n { fileId },\n );\n }\n const cached = blobCache.get(fileId);\n if (cached) return { url: cached.url, blob: cached.blob };\n const hydrated = (await blobCache.hydrate?.(fileId)) ?? null;\n if (hydrated) return { url: hydrated.url, blob: hydrated.blob };\n // In-flight dedup: two simultaneous consumers of the same fileId share\n // ONE network download (origin: hooks/useFileBlob.ts).\n let download = inflightDownloads.get(fileId);\n if (!download) {\n download = fetchFileBytes(fileId);\n inflightDownloads.set(fileId, download);\n void download\n .finally(() => {\n if (inflightDownloads.get(fileId) === download) {\n inflightDownloads.delete(fileId);\n }\n })\n .catch(() => {\n // Every awaiting consumer handles the rejection; this only stops\n // the void'd cleanup chain from re-surfacing it as unhandled.\n });\n }\n const blob = await download;\n const already = blobCache.get(fileId);\n const objectUrl = already?.url ?? URL.createObjectURL(blob);\n if (!already) blobCache.set(fileId, blob, objectUrl, { mimeType: blob.type });\n return { url: objectUrl, blob };\n }\n\n // --- upload lane --------------------------------------------------------\n\n function newRequestId(): string {\n const c = globalThis.crypto as Crypto | undefined;\n if (c?.randomUUID) return c.randomUUID();\n return `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n }\n\n function toUploadFile(file: File | Blob, opts: MediaUploadOptions): File {\n if (typeof File !== \"undefined\" && file instanceof File && !opts.fileName) {\n return file;\n }\n const mime =\n opts.mimeType ?? (file.type || \"application/octet-stream\");\n const name =\n opts.fileName ??\n (typeof File !== \"undefined\" && file instanceof File\n ? file.name\n : `upload-${Date.now()}.${mime.split(\"/\")[1]?.split(\";\")[0] ?? \"bin\"}`);\n return new File([file], name, { type: mime });\n }\n\n async function postMultipart(\n form: FormData,\n opts: MediaUploadOptions,\n requestId: string,\n ): Promise<FileUploadResponseWire> {\n const url = `${filesBase()}/files/upload`;\n const headers = await authHeaders({\n Accept: \"application/json\",\n \"X-Request-Id\": requestId,\n // Reuse requestId as the idempotency key — single intended upload,\n // single key; the backend stores it so retries don't double-create\n // version rows (origin: cloudUpload.ts).\n \"X-Idempotency-Key\": requestId,\n });\n\n // XHR path only when byte progress is requested and XHR exists (fetch\n // has no upload progress). Everything else rides the injected fetch.\n if (opts.onProgress && typeof XMLHttpRequest !== \"undefined\") {\n const onProgress = opts.onProgress;\n return new Promise<FileUploadResponseWire>((resolvePromise, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open(\"POST\", url);\n for (const [key, value] of Object.entries(headers)) {\n xhr.setRequestHeader(key, value);\n }\n xhr.upload.onprogress = (event) => {\n if (event.lengthComputable) onProgress(event.loaded, event.total);\n };\n xhr.onerror = () =>\n reject(new FileUploadError(\"Upload failed: network error\"));\n xhr.onabort = () => reject(new FileUploadError(\"Upload cancelled\"));\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n resolvePromise(JSON.parse(xhr.responseText) as FileUploadResponseWire);\n } catch (err) {\n reject(\n new FileUploadError(\"Upload response was not JSON\", { cause: err }),\n );\n }\n } else {\n reject(\n new FileUploadError(\n `Upload failed: HTTP ${xhr.status}${xhr.responseText ? ` — ${xhr.responseText.slice(0, 300)}` : \"\"}`,\n ),\n );\n }\n };\n if (opts.signal) {\n opts.signal.addEventListener(\"abort\", () => xhr.abort(), { once: true });\n }\n xhr.send(form);\n });\n }\n\n const init: RequestInit = { method: \"POST\", headers, body: form };\n if (opts.signal) init.signal = opts.signal;\n const response = await fetchImpl(url, init);\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new FileUploadError(\n `Upload failed: HTTP ${response.status}${text ? ` — ${text.slice(0, 300)}` : \"\"}`,\n );\n }\n return (await response.json()) as FileUploadResponseWire;\n }\n\n async function upload(\n file: File | Blob,\n opts: MediaUploadOptions = {},\n ): Promise<MediaUploadResult> {\n const asFile = toUploadFile(file, opts);\n\n // A target folder ID only exists on the host batch door (which owns\n // id→path resolution and the dedup pre-flight) — the wire contract\n // addresses folders by PATH. Route through it so the file lands where\n // the caller asked (origin: media-client upload → requestUpload).\n if (opts.parentFolderId != null) {\n if (!config.uploadMany) {\n throw new FileUploadError(\n \"upload: `parentFolderId` names a host folder id, but no `uploadMany` \" +\n \"port is injected. The wire contract addresses folders by PATH \" +\n \"(`folderPath`); inject the host batch door to upload by folder id.\",\n );\n }\n const result = await config.uploadMany([asFile], opts);\n if (result.cancelled) throw new FileUploadError(\"Upload cancelled\");\n const fileId = result.uploaded[0];\n if (!fileId) {\n throw new FileUploadError(result.failed[0]?.error ?? \"Upload failed\");\n }\n return { fileId, ref: { file_id: fileId } };\n }\n\n if (resolveUploadTransport(asFile.size) === \"tus\") {\n if (config.largeUploadTransport) {\n return config.largeUploadTransport(asFile, opts);\n }\n throw new FileUploadError(\n `File is ${asFile.size} bytes — at or over the resumable-transport threshold ` +\n `(${TUS_TRANSPORT_THRESHOLD_BYTES}). This tier ships the buffered multipart ` +\n \"transport; inject `largeUploadTransport` (the host's TUS lane) for files this size.\",\n );\n }\n\n const folder = (opts.folderPath ?? \"Inbox\").replace(/^\\/+|\\/+$/g, \"\");\n const form = new FormData();\n form.append(\"file\", asFile);\n form.append(\"file_path\", `${folder}/${asFile.name}`);\n for (const [key, value] of Object.entries(uploadAccessFields(opts))) form.append(key, value);\n if (opts.metadata) form.append(\"metadata_json\", JSON.stringify(opts.metadata));\n\n const response = await postMultipart(form, opts, newRequestId());\n if (!response.file_id) {\n throw new FileUploadError(\n \"Upload completed without a durable file identity; refusing to attach a temporary URL\",\n );\n }\n const ref: MediaRef = { file_id: response.file_id };\n if (response.url) ref.url = response.url;\n if (asFile.type) ref.mime_type = asFile.type;\n return { fileId: response.file_id, ref };\n }\n\n async function uploadManySequential(\n files: File[],\n opts: MediaUploadOptions,\n ): Promise<MediaMultiUploadResult> {\n const uploaded: string[] = [];\n const failed: Array<{ name: string; error: string }> = [];\n for (const file of files) {\n try {\n const result = await upload(file, opts);\n uploaded.push(result.fileId);\n } catch (err) {\n failed.push({\n name: file.name,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n return { uploaded, failed, cancelled: false };\n }\n\n // --- share lane ---------------------------------------------------------\n\n function permanentPublicUrl(meta: FileMetadata | undefined): string | null {\n if (meta?.publishedToWeb !== true) return null;\n const candidate = shareableMediaUrl(meta.cdnUrl ?? meta.publicUrl ?? null);\n if (!candidate) return null;\n // A public row is not proof that every URL stored on it is anonymously\n // renderable. During CDN backfill, public_url may still be our\n // authenticated /files/{id}/download endpoint. Treating that endpoint as\n // public element transport bypasses the bearer-authenticated blob lane and\n // leaves the media element permanently broken after a session refresh.\n return recognizer.fileIdFromFileEndpointUrl(candidate) ? null : candidate;\n }\n\n async function mintOrReuseShareLink(fileId: string): Promise<string | null> {\n const base = filesBase();\n const headers = await authHeaders({ Accept: \"application/json\" });\n const listResponse = await fetchImpl(\n `${base}/files/${encodeURIComponent(fileId)}/share-links`,\n { headers },\n );\n if (!listResponse.ok) {\n const typed = fileErrorFromStatus(listResponse.status, fileId);\n if (typed) throw typed;\n throw new FileError(\n \"internal\",\n `GET /files/${fileId}/share-links → HTTP ${listResponse.status}`,\n { fileId },\n );\n }\n const links = (await listResponse.json()) as ShareLinkRecordWire[];\n // Reuse an existing no-expiry read-only link (the origin popover's exact\n // filter), or mint one.\n const existing = links.find(\n (l) =>\n l.permission_level === \"viewer\" && !l.expires_at && !l.max_uses && l.token,\n );\n if (existing?.token) return shareUrl(base, existing.token);\n\n const createResponse = await fetchImpl(\n `${base}/files/${encodeURIComponent(fileId)}/share-links`,\n {\n method: \"POST\",\n headers: { ...headers, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ permission_level: \"viewer\" }),\n },\n );\n if (!createResponse.ok) {\n const typed = fileErrorFromStatus(createResponse.status, fileId);\n if (typed) throw typed;\n throw new FileError(\n \"internal\",\n `POST /files/${fileId}/share-links → HTTP ${createResponse.status}`,\n { fileId },\n );\n }\n const created = (await createResponse.json()) as ShareLinkRecordWire;\n return created.token ? shareUrl(base, created.token) : null;\n }\n\n // --- the client ---------------------------------------------------------\n\n const client: MatrxFilesClient<TSrc> = {\n recognizer,\n\n resolve(ref: MediaRefLike): FilesResolution<TSrc> | null {\n if (config.attachAuthHeadersToResolution) primeHeadersLazily();\n const norm = normalizeRef(ref);\n if (!norm) return null;\n\n if (norm.kind === \"url\") {\n if (classifyMediaUrl(norm.url) === \"expiring\") {\n refuseExpiringUrl(norm.url, \"files-client.resolve\");\n }\n const mime = norm.mime ?? mimeFromUrl(norm.url);\n const ours = recognizer.isOurUrl(norm.url);\n const resolution: FilesResolution<TSrc> = {\n src: mint(norm.url),\n kind: mimeToKind(mime),\n transport: \"element\",\n isCdn: recognizer.looksLikeCdnUrl(norm.url),\n // A session refresh can't resurrect someone else's link rot.\n recoverable: ours,\n };\n if (mime) resolution.mimeType = mime;\n return resolution;\n }\n\n const { fileId } = norm;\n const meta = metadata.peek(fileId);\n if (isUuidShape(fileId)) scheduleHydration(fileId);\n\n const mime = norm.mime ?? meta?.mimeType ?? null;\n const kind = mimeToKind(mime);\n const thumbRaw = meta?.thumbnailUrl ?? null;\n const thumbnailSrc =\n thumbRaw && classifyMediaUrl(thumbRaw) !== \"expiring\"\n ? mint(thumbRaw)\n : undefined;\n\n const publicUrl = permanentPublicUrl(meta);\n if (publicUrl) {\n const resolution: FilesResolution<TSrc> = {\n src: mint(publicUrl),\n kind,\n transport: \"element\",\n isCdn: true,\n recoverable: true,\n };\n if (mime) resolution.mimeType = mime;\n if (thumbnailSrc !== undefined) resolution.thumbnailSrc = thumbnailSrc;\n return resolution;\n }\n\n // Private/unknown-visibility pixels: images (and file-kinds rendered\n // as <img>) ride the bearer-authenticated blob lane. Video/audio bind\n // the durable URL directly (the mx_files_session cookie authenticates\n // GET byte routes).\n const transport = kind === \"video\" || kind === \"audio\" ? \"element\" : \"blob\";\n const resolution: FilesResolution<TSrc> = {\n src: mint(buildFileUrls(filesBase(), fileId).inline),\n kind,\n transport,\n recoverable: true,\n };\n if (mime) resolution.mimeType = mime;\n if (thumbnailSrc !== undefined) resolution.thumbnailSrc = thumbnailSrc;\n if (config.attachAuthHeadersToResolution && headerSnapshot) {\n resolution.headers = { ...headerSnapshot };\n }\n return resolution;\n },\n\n async getBlob(ref: MediaRefLike): Promise<FilesBlobHandle> {\n const norm = normalizeRef(ref);\n if (!norm) throw new FileError(\"internal\", \"getBlob: empty media ref\");\n if (norm.kind === \"file_id\") {\n const { url, blob } = await fileIdBlob(norm.fileId);\n // The blob cache owns the object URL — it revokes on eviction,\n // never on consumer release.\n return { url, blob, release: () => {} };\n }\n if (classifyMediaUrl(norm.url) === \"expiring\") {\n refuseExpiringUrl(norm.url, \"files-client.getBlob\");\n }\n let response: Response;\n try {\n response = await fetchImpl(norm.url);\n } catch (err) {\n throw new ExternalFetchError(undefined, { cause: err });\n }\n if (!response.ok) {\n throw new ExternalFetchError(\n `Failed to fetch external URL (${response.status})`,\n );\n }\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n return {\n url: objectUrl,\n blob,\n release: () => URL.revokeObjectURL(objectUrl),\n };\n },\n\n async recoverLoadError(\n _src: TSrc,\n attempt: number,\n ): Promise<FilesRecoveryVerdict | FilesRecoveryReport> {\n // Bare verdict against older servers (no secret_source, no failed-mint\n // status); the structured report otherwise — same verdict either way.\n const finish = (\n verdict: FilesRecoveryVerdict,\n ): FilesRecoveryVerdict | FilesRecoveryReport => {\n const snap = session.snapshot();\n if (snap.secretSource === undefined && snap.status === undefined) {\n return verdict;\n }\n return { verdict, status: snap.status, secretSource: snap.secretSource };\n };\n if (attempt > 1) return finish(\"terminal\");\n // Session refresh, not URL re-mint: re-request the SAME durable URL\n // after forcing a fresh mx_files_session cookie. ensure() never\n // throws; per-base failures already screamed through the sink.\n await session.ensure({ force: true });\n return finish(\"retry\");\n },\n\n async healLoadError(\n ref: MediaRefLike,\n context: { src: TSrc; attempts: FilesHealAttempt[] },\n ): Promise<FilesHealResult> {\n const norm = normalizeRef(ref);\n // A URL ref on OUR host still carries a file id in its path.\n const fileId =\n norm?.kind === \"file_id\"\n ? norm.fileId\n : norm?.kind === \"url\"\n ? recognizer.fileIdFromFileEndpointUrl(norm.url)\n : null;\n const primary = filesBase();\n return healLoadErrorLadder({\n fileId,\n priorAttempts: context.attempts,\n primaryBase: primary,\n alternateBases: session.bases().filter((b) => b !== primary),\n mintStatus: session.snapshot().status,\n fetchBytes: async (base, id) => {\n const headers = await authHeaders();\n return fetchImpl(buildFileUrls(base, id).download, { headers });\n },\n cacheBlob: (id, blob) => {\n const already = blobCache.get(id);\n const objectUrl = already?.url ?? URL.createObjectURL(blob);\n if (!already) blobCache.set(id, blob, objectUrl, { mimeType: blob.type });\n return objectUrl;\n },\n });\n },\n\n async recoverBlobLoadError(\n ref: MediaRefLike,\n attempt: number,\n ): Promise<FilesRecoveryVerdict> {\n if (attempt > 1) return \"terminal\";\n const norm = normalizeRef(ref);\n if (!norm || norm.kind !== \"file_id\") return \"terminal\";\n // A failed object URL is not an identity and a session refresh cannot\n // repair it. Evict only this durable file's cached handle; the render\n // layer will re-enter getBlob(ref), which owns the actual byte path.\n blobCache.invalidate?.(norm.fileId);\n return \"retry\";\n },\n\n upload,\n\n uploadMany(files: File[], opts: MediaUploadOptions = {}): Promise<MediaMultiUploadResult> {\n if (config.uploadMany) return config.uploadMany(files, opts);\n return uploadManySequential(files, opts);\n },\n\n async shareableUrl(ref: MediaRefLike): Promise<string | null> {\n const norm = normalizeRef(ref);\n if (!norm) return null;\n if (norm.kind === \"url\") return shareableMediaUrl(norm.url);\n const { fileId } = norm;\n if (!isUuidShape(fileId)) return null;\n // Hydrate published-to-web/CDN fields before deciding to mint — an\n // unhydrated public file must never get a redundant share link.\n const meta = await metadata.hydrate(fileId);\n const permanent = permanentPublicUrl(meta);\n if (permanent) return permanent;\n return mintOrReuseShareLink(fileId);\n },\n\n shareableUrlNoMint(ref: MediaRefLike): string | null {\n const norm = normalizeRef(ref);\n if (!norm) return null;\n if (norm.kind === \"url\") return shareableMediaUrl(norm.url);\n return permanentPublicUrl(metadata.peek(norm.fileId));\n },\n\n classifyError(err: unknown): MediaUnavailableReason {\n return classifyUnavailable(err);\n },\n\n ensureSession(opts?: { force?: boolean | undefined }): Promise<void> {\n return session.ensure(opts);\n },\n\n async primeAuth(): Promise<void> {\n primeHeadersLazily();\n await refreshHeaderSnapshot();\n },\n };\n\n return client;\n}\n","/**\n * @ai-matrx/data/files — the `mx_files_session` cookie bootstrap.\n *\n * Ported behavior-verbatim from matrx-frontend `features/files/handler/\n * session.ts`; verified against the server route\n * (`packages/matrx-files/matrx_files/api/router_files.py` `POST /session` →\n * `{\"ok\":true,\"expires_in\":<seconds>}` + `Set-Cookie: mx_files_session=…`).\n *\n * Durable file URLs never expire and carry only the file id. A plain\n * `<img src>` / `<video src>` can't attach an Authorization header, so\n * private files authenticate through the HttpOnly `mx_files_session` cookie:\n * `POST /files/session` with normal auth headers AND `credentials:'include'`\n * sets it (SameSite=None; Secure; HMAC-signed), and the backend accepts it\n * ONLY on GET byte routes.\n *\n * The durable URLs the backend emits point at the MAIN backend base, while\n * client routing can also target the standalone files host. Cookies are\n * per-host, so when the two bases differ the session is established on\n * BOTH — deduped, independent, non-fatal on failure (the render path retries\n * via `force` from `recoverLoadError`).\n *\n * State is per-instance and in-memory only (expiry per base + in-flight\n * dedupe) — a fresh page load just POSTs again. No module-level state: the\n * client instance owns its session tracker, so dual-loader graphs can never\n * split it (the singleton decision belongs to the host that constructs the\n * ONE client).\n */\n\nimport type { FileSessionResponseWire, FilesDiagnosticsSink } from \"./types\";\n\n/**\n * What the session lane learned from its most recent mints — the feed for\n * `recoverLoadError`'s structured report. `secretSource` aggregates across\n * bases with `\"ephemeral\"` winning (one ephemeral-secret base is enough to\n * scream); `status` is the HTTP status of the most recent FAILED mint, when\n * one is known. Both absent against older servers / before any mint.\n */\nexport interface FilesSessionSnapshot {\n secretSource?: \"stable\" | \"ephemeral\" | undefined;\n status?: number | undefined;\n}\n\n/** Refresh when less than this much of the cookie's TTL remains. */\nconst REFRESH_SAFETY_MARGIN_MS = 60 * 60 * 1000; // 1h of the 7-day TTL\n\ninterface BaseState {\n /** ms epoch when the cookie for this base should be considered stale. */\n freshUntil: number;\n inflight: Promise<void> | null;\n /** Report each base's failure once per session, not once per render. */\n reportedFailure: boolean;\n /** `secret_source` from this base's latest successful mint (wire-optional). */\n secretSource: \"stable\" | \"ephemeral\" | undefined;\n /** HTTP status of this base's latest FAILED mint (cleared on success). */\n lastFailureStatus: number | undefined;\n}\n\nexport interface FilesSessionOptions {\n /** Every backend base that serves durable file bytes to this client. */\n resolveBases(): string[];\n /** Auth headers for the POST (credential mapping is the caller's job). */\n buildHeaders(): Promise<Record<string, string>>;\n fetchImpl: typeof fetch;\n diagnostics: FilesDiagnosticsSink;\n now?: (() => number) | undefined;\n}\n\nexport interface FilesSession {\n /**\n * Ensure the cookie is set on every base. Idempotent and deduped; a fresh\n * session is a no-op unless `force` is passed (use `force` from a media\n * element's error-retry path, where the cookie may have been revoked\n * server-side despite looking fresh here). NEVER throws — per-base\n * failures are independent and reported once through the sink.\n */\n ensure(opts?: { force?: boolean | undefined }): Promise<void>;\n /** What the latest mints taught us — see {@link FilesSessionSnapshot}. */\n snapshot(): FilesSessionSnapshot;\n /** Every base the session lane mints on — the hosts that serve bytes. */\n bases(): string[];\n /** Test/teardown hook: drop all per-base state. */\n reset(): void;\n}\n\nexport function createFilesSession(options: FilesSessionOptions): FilesSession {\n const now = options.now ?? Date.now;\n const stateByBase = new Map<string, BaseState>();\n\n function baseState(base: string): BaseState {\n let s = stateByBase.get(base);\n if (!s) {\n s = {\n freshUntil: 0,\n inflight: null,\n reportedFailure: false,\n secretSource: undefined,\n lastFailureStatus: undefined,\n };\n stateByBase.set(base, s);\n }\n return s;\n }\n\n async function establishOnBase(base: string): Promise<void> {\n const s = baseState(base);\n // Each attempt reports its OWN outcome — a network error must not wear\n // the previous attempt's HTTP status.\n s.lastFailureStatus = undefined;\n try {\n const headers = await options.buildHeaders();\n const response = await options.fetchImpl(`${base}/files/session`, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n });\n if (!response.ok) {\n s.lastFailureStatus = response.status;\n throw new Error(`POST ${base}/files/session → HTTP ${response.status}`);\n }\n const body = (await response.json()) as FileSessionResponseWire;\n const ttlMs = (body.expires_in ?? 0) * 1000;\n s.freshUntil = now() + Math.max(0, ttlMs - REFRESH_SAFETY_MARGIN_MS);\n s.reportedFailure = false;\n s.lastFailureStatus = undefined;\n // Wire-optional (older servers omit it); anything unrecognized is\n // treated as absent rather than forwarded blind.\n s.secretSource =\n body.secret_source === \"ephemeral\" || body.secret_source === \"stable\"\n ? body.secret_source\n : undefined;\n } catch (err) {\n // Non-fatal by design: public files render without the cookie, and the\n // media error-retry path calls ensure({ force: true }).\n if (!s.reportedFailure) {\n s.reportedFailure = true;\n options.diagnostics({\n source: \"files-session\",\n context: base,\n message: `failed to establish the file session on ${base}`,\n detail: err instanceof Error ? err.message : String(err),\n });\n }\n throw err;\n }\n }\n\n function ensureOnBase(base: string, force: boolean): Promise<void> {\n const s = baseState(base);\n if (s.inflight) return s.inflight;\n if (!force && s.freshUntil > now()) return Promise.resolve();\n const promise = establishOnBase(base).finally(() => {\n s.inflight = null;\n });\n s.inflight = promise;\n return promise;\n }\n\n return {\n async ensure(opts?: { force?: boolean | undefined }): Promise<void> {\n const force = opts?.force ?? false;\n let bases: string[] = [];\n try {\n bases = options.resolveBases();\n } catch {\n // No backend configured (SSR/early boot) — nothing to establish.\n }\n await Promise.all(\n [...new Set(bases)].map((base) =>\n ensureOnBase(base, force).catch(() => undefined),\n ),\n );\n },\n snapshot(): FilesSessionSnapshot {\n let secretSource: \"stable\" | \"ephemeral\" | undefined;\n let status: number | undefined;\n for (const s of stateByBase.values()) {\n if (s.secretSource === \"ephemeral\") secretSource = \"ephemeral\";\n else if (s.secretSource === \"stable\" && secretSource === undefined) {\n secretSource = \"stable\";\n }\n if (s.lastFailureStatus !== undefined) status = s.lastFailureStatus;\n }\n return { secretSource, status };\n },\n bases(): string[] {\n return [...new Set(options.resolveBases())];\n },\n reset(): void {\n stateByBase.clear();\n },\n };\n}\n","/**\n * data/src/files/heal.ts — the heal ladder behind `healLoadError`.\n *\n * Runs AFTER the ONE retry policy (session re-mint + same-URL retry) is\n * exhausted. The point: the user is never denied while ANY lane can serve\n * the bytes, and the incident is written with the lanes tried, their\n * timings, and a NAMED root cause — so a render that heals itself is still\n * a loud, countable row, never the quiet norm.\n *\n * Lanes, in order:\n * 1. `session-refresh-retry` — already ran (the caller passes its attempt).\n * 2. `bearer-blob` — bearer-authenticated byte fetch on the\n * primary files host → object URL. Needs NO\n * cookie: this is the lane that serves a\n * browser blocking third-party cookies.\n * 3. `bearer-blob-alternate-host` — the same fetch on the other configured\n * host (the 2026-08-30 class: one host dead).\n *\n * Pure function over injected I/O so it is testable without a browser.\n */\n\nimport type {\n FilesHealAttempt,\n FilesHealDiagnosis,\n FilesHealLane,\n FilesHealResult,\n} from \"./types\";\n\nexport interface HealLadderInput {\n /** The durable identity. Null → no byte lane can run (URL is not ours). */\n fileId: string | null;\n /** The lanes that already ran (the ONE retry), oldest first. */\n priorAttempts: FilesHealAttempt[];\n primaryBase: string;\n /** Other hosts that serve the same bytes (order = preference). */\n alternateBases: string[];\n /** HTTP status of the most recent FAILED cookie mint, when one is known. */\n mintStatus: number | undefined;\n /** Bearer-authenticated `GET /files/{id}/download` on `base`. */\n fetchBytes: (base: string, fileId: string) => Promise<Response>;\n /** Park the blob in the host cache and return its object URL. */\n cacheBlob: (fileId: string, blob: Blob) => string;\n now?: (() => number) | undefined;\n}\n\nfunction originOf(base: string): string {\n try {\n return new URL(base).origin;\n } catch {\n return base;\n }\n}\n\nasync function runByteLane(\n lane: FilesHealLane,\n base: string,\n input: HealLadderInput,\n): Promise<{ attempt: FilesHealAttempt; src: string | null }> {\n const now = input.now ?? Date.now;\n const started = now();\n const host = originOf(base);\n if (!input.fileId) {\n return {\n attempt: {\n lane,\n outcome: \"skipped\",\n ms: 0,\n host,\n detail: \"no file id — the source is not one of our durable file URLs\",\n },\n src: null,\n };\n }\n let response: Response;\n try {\n response = await input.fetchBytes(base, input.fileId);\n } catch (err) {\n return {\n attempt: {\n lane,\n outcome: \"network-error\",\n ms: now() - started,\n host,\n detail: err instanceof Error ? err.message : String(err ?? \"fetch failed\"),\n },\n src: null,\n };\n }\n if (!response.ok) {\n return {\n attempt: {\n lane,\n outcome: \"http-error\",\n status: response.status,\n ms: now() - started,\n host,\n detail: `GET /files/{id}/download → HTTP ${response.status}`,\n },\n src: null,\n };\n }\n try {\n const blob = await response.blob();\n const src = input.cacheBlob(input.fileId, blob);\n return {\n attempt: {\n lane,\n outcome: \"ok\",\n status: response.status,\n ms: now() - started,\n host,\n detail: `${blob.size} bytes via bearer token → object URL`,\n },\n src,\n };\n } catch (err) {\n return {\n attempt: {\n lane,\n outcome: \"network-error\",\n ms: now() - started,\n host,\n detail: `body read failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n src: null,\n };\n }\n}\n\n/** Name the root cause from the attempt pattern. Exported for tests. */\nexport function diagnoseHeal(\n attempts: FilesHealAttempt[],\n mintStatus: number | undefined,\n): FilesHealDiagnosis {\n const lane1 = attempts.find((a) => a.lane === \"session-refresh-retry\");\n const bearer = attempts.find((a) => a.lane === \"bearer-blob\");\n const alternate = attempts.find((a) => a.lane === \"bearer-blob-alternate-host\");\n const healedBy = attempts.find((a) => a.outcome === \"ok\");\n\n if (healedBy?.lane === \"bearer-blob\") {\n // The element lane died with a fresh cookie mint that answered 200 (no\n // failed-mint status), yet the SAME identity fetched the bytes fine with\n // a bearer token. The only difference between the two requests is the\n // cookie — and the browser never sent it.\n if (lane1 && mintStatus === undefined) return \"cookie-not-sent-by-browser\";\n if (mintStatus !== undefined) return \"cookie-mint-failed\";\n return \"unknown\";\n }\n if (healedBy?.lane === \"bearer-blob-alternate-host\") {\n if (bearer && (bearer.outcome === \"network-error\" || (bearer.status ?? 0) >= 500)) {\n return \"primary-host-down-alternate-served\";\n }\n return \"unknown\";\n }\n // Nothing healed — read the byte lanes' refusals.\n const byteLanes = [bearer, alternate].filter(Boolean) as FilesHealAttempt[];\n if (byteLanes.some((a) => a.status === 401)) return \"bearer-rejected\";\n if (byteLanes.some((a) => a.status === 403)) return \"access-denied\";\n if (byteLanes.some((a) => a.status === 404)) return \"file-missing\";\n if (byteLanes.some((a) => (a.status ?? 0) >= 500)) return \"files-host-error\";\n if (byteLanes.length > 0 && byteLanes.every((a) => a.outcome === \"network-error\")) {\n return \"files-host-unreachable\";\n }\n if (mintStatus !== undefined) return \"cookie-mint-failed\";\n return \"unknown\";\n}\n\nconst SUMMARY: Record<FilesHealDiagnosis, string> = {\n \"cookie-not-sent-by-browser\":\n \"the files-session cookie was minted (200) but the browser did not send it back on the media element request — third-party cookies are blocked in this browser (Chrome setting / Incognito, Safari, Brave)\",\n \"cookie-mint-failed\": \"the files-session cookie mint itself failed\",\n \"bearer-rejected\": \"the bearer token was refused (401): the sign-in on this tab is stale\",\n \"access-denied\": \"this identity may not read this file (403)\",\n \"file-missing\": \"the file is gone (404)\",\n \"files-host-error\": \"the files host answered with a server error (5xx)\",\n \"files-host-unreachable\": \"no files host answered (network/DNS/CORS)\",\n \"primary-host-down-alternate-served\":\n \"the primary files host did not serve the bytes; the alternate host did\",\n unknown: \"the attempt pattern matches no known cause\",\n};\n\nexport async function healLoadErrorLadder(input: HealLadderInput): Promise<FilesHealResult> {\n const attempts: FilesHealAttempt[] = [...input.priorAttempts];\n let src: string | null = null;\n let healedBy: FilesHealLane | undefined;\n\n const primary = await runByteLane(\"bearer-blob\", input.primaryBase, input);\n attempts.push(primary.attempt);\n if (primary.src) {\n src = primary.src;\n healedBy = \"bearer-blob\";\n } else {\n for (const base of input.alternateBases) {\n const alt = await runByteLane(\"bearer-blob-alternate-host\", base, input);\n attempts.push(alt.attempt);\n if (alt.src) {\n src = alt.src;\n healedBy = \"bearer-blob-alternate-host\";\n break;\n }\n }\n }\n\n const diagnosis = diagnoseHeal(attempts, input.mintStatus);\n const cause = SUMMARY[diagnosis];\n const served = healedBy\n ? `served via ${healedBy} in ${attempts.find((a) => a.outcome === \"ok\")?.ms ?? 0}ms`\n : `every lane died (${attempts.map((a) => `${a.lane}:${a.outcome}${a.status ? ` ${a.status}` : \"\"}`).join(\", \")})`;\n const ifMint = input.mintStatus !== undefined ? ` (last cookie mint status ${input.mintStatus})` : \"\";\n return {\n src,\n healedBy,\n attempts,\n diagnosis,\n summary: `${cause}${ifMint}; ${served}`,\n };\n}\n","/**\n * Upload dedup guard — one in-flight/just-landed claim per (folder, name, size).\n * Origin: @ai-matrx/media files/engine/upload/uploadDedupGuard.ts (architecture step 14).\n *\n * Root cause (`common-docs/projects/acquisition-frontier/own-files/VERIFICATION.md`\n * §12, 2026-09-18): in a 7-file pile, both zero-byte files were uploaded\n * TWICE, 18s apart, the second auto-renamed \"(1)\" by `uniqueName()` in\n * `features/files/redux/thunks.ts` — right after the dev server had refused\n * connections. A client-side retry of the whole `uploadFiles()` dispatch\n * after a transient failure re-sends a file whose first attempt may already\n * have landed server-side (the client only knows its OWN request failed —\n * a connection reset gives no proof the server never received the bytes).\n *\n * The obvious fix is the backend's OWN `X-Idempotency-Key` mechanism\n * (`lib/python-client.ts` already sends it, `aidream` already stores it in\n * `metadata._idempotency_key`) — but it is not the full answer here: the\n * key is only reused *within* a single `uploadFiles()` dispatch\n * (`newRequestId()` mints a fresh one per worker() call in thunks.ts), and\n * server-side `_replayable_412` in `aidream/aidream/api/routers/files/__init__.py`\n * only replays a 412 (precondition-failed) response under the key — a\n * SUCCESSFUL upload is never deduped by the server on replay. So a second,\n * independent `uploadFiles()` dispatch for the same intended file — the\n * shape a caller-level retry takes — is indistinguishable from a genuinely\n * new upload to both client and server.\n *\n * This guard closes the gap client-side, per (folder, name, size): the\n * FIRST claim for a signature performs the real network request; any\n * SECOND claim for the same signature while that request is still in\n * flight, or shortly after it landed, reuses the first attempt's outcome\n * instead of sending the bytes again. A claim that ends in failure is\n * cleared immediately, so a genuine \"it never landed, please retry\" case is\n * never blocked — only a request whose outcome is still pending, or already\n * known-good, is deduped.\n */\n\nexport interface DedupedUploadOutcome {\n fileId: string;\n filePath: string;\n}\n\ninterface Claim {\n promise: Promise<DedupedUploadOutcome>;\n expiresAt: number;\n}\n\n/**\n * How long a landed (or in-flight) upload's signature stays claimed. Long\n * enough to cover the 18s gap actually observed between the two identical\n * uploads in the verifier's run, with wide margin for a slower connection.\n */\nconst CLAIM_WINDOW_MS = 3 * 60 * 1000;\n\nconst claims = new Map<string, Claim>();\n\n/** The dedup key: same folder, same original name, same byte count. */\nexport function uploadDedupKey(\n folderPath: string,\n fileName: string,\n fileSize: number,\n): string {\n return [folderPath, fileName, String(fileSize)].join(\"\\\\0\");\n}\n\nexport type UploadClaim =\n | { shared: true; promise: Promise<DedupedUploadOutcome> }\n | {\n shared: false;\n /**\n * The caller that owns this claim MUST call `settle` exactly once\n * with the real upload's outcome (or a rejected promise on failure).\n */\n settle: (outcome: Promise<DedupedUploadOutcome>) => void;\n };\n\n/**\n * Claim a signature before starting an upload. See module doc for the\n * contract: a `shared: true` result means someone else already owns this\n * signature — await its `promise` instead of sending the file again.\n */\nexport function claimUpload(key: string): UploadClaim {\n const now = Date.now();\n const existing = claims.get(key);\n if (existing && existing.expiresAt > now) {\n return { shared: true, promise: existing.promise };\n }\n\n let settleFn!: (outcome: Promise<DedupedUploadOutcome>) => void;\n const publicPromise = new Promise<DedupedUploadOutcome>(\n (resolve, reject) => {\n settleFn = (outcome) => {\n outcome.then(\n (value) => {\n // Success stays claimed for the full window — a landed upload\n // must keep deduping late-arriving retries too.\n resolve(value);\n },\n (err) => {\n // A failed attempt must never haunt a real retry: clear the\n // slot immediately so the next claim for this signature runs\n // for real instead of replaying a failure forever.\n claims.delete(key);\n reject(err instanceof Error ? err : new Error(String(err)));\n },\n );\n };\n },\n );\n // Every claim gets a silent subscriber of its own: a claim nobody ever\n // shares (the common case — most uploads never race a retry) must not\n // trip Node's unhandled-rejection detector when it fails. This does not\n // swallow the failure for a real shared consumer — each `.then()`/`.catch()`\n // on the same promise still sees the rejection independently.\n publicPromise.catch(() => {});\n claims.set(key, { promise: publicPromise, expiresAt: now + CLAIM_WINDOW_MS });\n return { shared: false, settle: settleFn };\n}\n\n/** Test-only: clears every claim so suites don't leak state between tests. */\nexport function __resetUploadDedupGuardForTests(): void {\n claims.clear();\n}\n"],"mappings":"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","names":["isUuidShape","isUuidShape","mime","resolution","blob"]}
|
package/dist/types.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// src/types.ts
|
|
17
|
+
var types_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(types_exports);
|
|
19
|
+
//# sourceMappingURL=types.cjs.map
|