{"version":3,"file":"file-DKBOj5q4.mjs","names":[],"sources":["../src/runtime/file.ts"],"sourcesContent":["/**\n * TailorDB file (BLOB) utilities.\n *\n * Thin typed wrapper around the platform-provided `tailordb.file` runtime API.\n * At runtime this delegates to `globalThis.tailordb.file`. Use `mockFile` from\n * `@tailor-platform/sdk/vitest` to mock these calls in unit tests.\n * @example\n * import { file } from \"@tailor-platform/sdk/runtime\";\n *\n * const { metadata } = await file.upload(\n *   \"my-namespace\",\n *   \"Document\",\n *   \"attachment\",\n *   recordId,\n *   bytes,\n * );\n */\n\n/** Upload response metadata. */\nexport interface UploadMetadata {\n  fileSize: number;\n  sha256sum: string;\n}\n\n/** Download response metadata. */\nexport interface DownloadMetadata {\n  contentType: string;\n  fileSize: number;\n  sha256sum: string;\n  lastUploadedAt: string;\n}\n\n/** File metadata (for {@link TailorDBFileAPI.getMetadata}). */\nexport interface FileMetadata {\n  contentType: string;\n  fileSize: number;\n  sha256sum: string;\n  urlPath: string;\n  lastUploadedAt?: string;\n}\n\n/** Upload options. */\nexport interface FileUploadOptions {\n  contentType?: string;\n  /** How to interpret string data. Ignored for byte arrays and buffers. */\n  encoding?: \"utf8\" | \"base64\";\n}\n\n/** Upload options with an explicit interpretation for string data. */\nexport interface FileUploadStringOptions extends FileUploadOptions {\n  encoding: \"utf8\" | \"base64\";\n}\n\n/** Binary contents accepted by {@link file.upload}. */\nexport type FileUploadBytes = ArrayBuffer | Uint8Array | number[];\n\n/** Upload stream options. */\nexport interface FileUploadStreamOptions {\n  contentType?: string;\n  fileSize?: number;\n}\n\n/** Upload response. */\nexport interface FileUploadResponse {\n  metadata: UploadMetadata;\n}\n\n/** Download response. */\nexport interface FileDownloadResponse {\n  data: Uint8Array;\n  metadata: DownloadMetadata;\n}\n\n/** Download-as-Base64 response. */\nexport interface FileDownloadAsBase64Response {\n  data: string;\n  metadata: DownloadMetadata;\n}\n\n/** Download stream response. */\nexport interface FileDownloadStreamResponse {\n  body: ReadableStream<Uint8Array>;\n  metadata: DownloadMetadata;\n}\n\n/** Error code emitted by {@link TailorDBFileError}. */\nexport type TailorDBFileErrorCode =\n  | \"INVALID_PARAMS\"\n  | \"INVALID_DATA_TYPE\"\n  | \"OPERATION_FAILED\"\n  | \"DELETE_FAILED\"\n  | \"STREAM_OPEN_FAILED\"\n  | \"STREAM_READ_ERROR\"\n  | \"STREAM_ERROR\"\n  | \"FILE_TOO_LARGE\";\n\n/**\n * Type-only shape of the `TailorDBFileError` runtime class. The class itself\n * is provided by the platform runtime (and by `injectMocks` in tests); this\n * interface mirrors it so callers can `import type { TailorDBFileError }` from\n * the wrapper module without depending on any ambient declaration.\n */\nexport interface TailorDBFileError extends Error {\n  name: \"TailorDBFileError\";\n  code?: TailorDBFileErrorCode;\n  cause?: unknown;\n}\n\n/**\n * Platform API surface for `tailordb.file`. Describes the shape the platform\n * runtime injects on `globalThis.tailordb.file`.\n */\nexport interface TailorDBFileAPI {\n  /**\n   * Upload a file to TailorDB.\n   * @param namespace - TailorDB namespace\n   * @param tableName - TailorDB table name\n   * @param fieldName - File field name on the table\n   * @param recordId - Record ID owning the field\n   * @param data - File contents\n   * @param options - Upload options (e.g. `contentType`)\n   * @returns Upload response containing the file metadata\n   */\n  upload(\n    namespace: string,\n    tableName: string,\n    fieldName: string,\n    recordId: string,\n    data: string | FileUploadBytes,\n    options?: Omit<FileUploadOptions, \"encoding\">,\n  ): Promise<FileUploadResponse>;\n\n  /**\n   * Download a file from TailorDB.\n   *\n   * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file\n   * exceeds 10MB — use {@link downloadStream} for large files.\n   * @param namespace - TailorDB namespace\n   * @param tableName - TailorDB table name\n   * @param fieldName - File field name on the table\n   * @param recordId - Record ID owning the field\n   * @returns Bytes and metadata for the file\n   */\n  download(\n    namespace: string,\n    tableName: string,\n    fieldName: string,\n    recordId: string,\n  ): Promise<FileDownloadResponse>;\n\n  /**\n   * Download a file from TailorDB as a Base64-encoded string.\n   *\n   * Throws `TailorDBFileError` with code `FILE_TOO_LARGE` when the file\n   * exceeds 10MB — use {@link downloadStream} for large files.\n   * @param namespace - TailorDB namespace\n   * @param tableName - TailorDB table name\n   * @param fieldName - File field name on the table\n   * @param recordId - Record ID owning the field\n   * @returns Base64-encoded contents and metadata for the file\n   */\n  downloadAsBase64(\n    namespace: string,\n    tableName: string,\n    fieldName: string,\n    recordId: string,\n  ): Promise<FileDownloadAsBase64Response>;\n\n  /**\n   * Delete a file from TailorDB.\n   * @param namespace - TailorDB namespace\n   * @param tableName - TailorDB table name\n   * @param fieldName - File field name on the table\n   * @param recordId - Record ID owning the field\n   * @returns Resolves once the file has been deleted\n   */\n  delete(namespace: string, tableName: string, fieldName: string, recordId: string): Promise<void>;\n\n  /**\n   * Get file metadata from TailorDB.\n   * @param namespace - TailorDB namespace\n   * @param tableName - TailorDB table name\n   * @param fieldName - File field name on the table\n   * @param recordId - Record ID owning the field\n   * @returns Metadata for the stored file\n   */\n  getMetadata(\n    namespace: string,\n    tableName: string,\n    fieldName: string,\n    recordId: string,\n  ): Promise<FileMetadata>;\n\n  /**\n   * Download a file as a ReadableStream.\n   * @param namespace - TailorDB namespace\n   * @param tableName - TailorDB table name\n   * @param fieldName - File field name on the table\n   * @param recordId - Record ID owning the field\n   * @returns ReadableStream body and metadata for the file\n   */\n  downloadStream(\n    namespace: string,\n    tableName: string,\n    fieldName: string,\n    recordId: string,\n  ): Promise<FileDownloadStreamResponse>;\n\n  /**\n   * Upload a file using a ReadableStream.\n   * @param namespace - TailorDB namespace\n   * @param tableName - TailorDB table name\n   * @param fieldName - File field name on the table\n   * @param recordId - Record ID owning the field\n   * @param readableStream - ReadableStream providing the file data\n   * @param options - Upload stream options (e.g. `contentType`, `fileSize`)\n   * @returns Upload response containing the file metadata\n   */\n  uploadStream(\n    namespace: string,\n    tableName: string,\n    fieldName: string,\n    recordId: string,\n    readableStream: ReadableStream<Uint8Array | ArrayBuffer>,\n    options?: FileUploadStreamOptions,\n  ): Promise<FileUploadResponse>;\n}\n\nconst api = (): TailorDBFileAPI =>\n  (globalThis as unknown as { tailordb: { file: TailorDBFileAPI } }).tailordb.file;\n\nconst DATA_URL_PREFIX = /^data:([^,]*);base64,/i;\n\nfunction stripBase64DataUrl(data: string): { contentType?: string; payload: string } {\n  const match = DATA_URL_PREFIX.exec(data);\n  if (!match) return { payload: data };\n  return { contentType: match[1] || undefined, payload: data.slice(match[0].length) };\n}\n\nfunction decodeBase64(data: string): Uint8Array {\n  if (typeof Uint8Array.fromBase64 === \"function\") {\n    try {\n      return Uint8Array.fromBase64(data);\n    } catch {\n      throw new TypeError(\"Invalid Base64 file data.\");\n    }\n  }\n\n  // Uint8Array.fromBase64 is unflagged only from Node 25 (V8 14.1); decode manually before that.\n  const base64 = data.replace(/[\\t\\n\\f\\r ]/g, \"\");\n  if (\n    !/^[A-Za-z0-9+/]*={0,2}$/.test(base64) ||\n    base64.length % 4 === 1 ||\n    (base64.includes(\"=\") && base64.length % 4 !== 0)\n  ) {\n    throw new TypeError(\"Invalid Base64 file data.\");\n  }\n  const decoded = atob(base64);\n  const bytes = new Uint8Array(decoded.length);\n  for (let index = 0; index < decoded.length; index++) {\n    bytes[index] = decoded.charCodeAt(index);\n  }\n  return bytes;\n}\n\n/**\n * Upload file bytes without encoding or decoding them.\n * @param namespace - TailorDB namespace\n * @param tableName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @param data - File bytes\n * @param options - Upload options; encoding is ignored for bytes\n * @returns Upload response containing the file metadata\n */\nfunction upload(\n  namespace: string,\n  tableName: string,\n  fieldName: string,\n  recordId: string,\n  data: FileUploadBytes,\n  options?: FileUploadOptions,\n): Promise<FileUploadResponse>;\n/**\n * Upload text as UTF-8 or decode Base64 into file bytes.\n * @param namespace - TailorDB namespace\n * @param tableName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @param data - String to encode or decode, or file bytes to upload unchanged; a base64 value\n * may be a `data:<contentType>;base64,<payload>` URL, whose content type is extracted\n * @param options - String encoding and optional content type; utf8 defaults contentType to text/plain; charset=utf-8 when omitted\n * @returns Upload response containing the file metadata\n * @throws {TypeError} If the encoding is unsupported or Base64 data is invalid\n */\nfunction upload(\n  namespace: string,\n  tableName: string,\n  fieldName: string,\n  recordId: string,\n  data: string | FileUploadBytes,\n  options: FileUploadStringOptions,\n): Promise<FileUploadResponse>;\n/**\n * Upload file contents, treating strings without encoding as text.\n * @deprecated since 2.15.0 — pass encoding: \"utf8\" for text or \"base64\" for Base64 strings. codemod: v3/file-upload-encoding\n * @param namespace - TailorDB namespace\n * @param tableName - TailorDB table name\n * @param fieldName - File field name on the table\n * @param recordId - Record ID owning the field\n * @param data - File contents\n * @param options - Upload options\n * @returns Upload response containing the file metadata\n */\nfunction upload(\n  namespace: string,\n  tableName: string,\n  fieldName: string,\n  recordId: string,\n  data: string | FileUploadBytes,\n  options?: FileUploadOptions,\n): Promise<FileUploadResponse>;\nasync function upload(\n  namespace: string,\n  tableName: string,\n  fieldName: string,\n  recordId: string,\n  data: string | FileUploadBytes,\n  options?: FileUploadOptions,\n): Promise<FileUploadResponse> {\n  if (options?.encoding === undefined) {\n    return api().upload(namespace, tableName, fieldName, recordId, data, options);\n  }\n\n  const { encoding, ...uploadOptions } = options;\n  let contents = data;\n  if (typeof data === \"string\") {\n    switch (encoding) {\n      case \"utf8\":\n        contents = new TextEncoder().encode(data);\n        uploadOptions.contentType ??= \"text/plain; charset=utf-8\";\n        break;\n      case \"base64\": {\n        const { contentType, payload } = stripBase64DataUrl(data);\n        contents = decodeBase64(payload);\n        uploadOptions.contentType ??= contentType;\n        break;\n      }\n      default:\n        throw new TypeError(`Unsupported file upload encoding: ${encoding}`);\n    }\n  }\n  return api().upload(namespace, tableName, fieldName, recordId, contents, uploadOptions);\n}\n\n/**\n * See {@link TailorDBFileAPI.download}.\n * @param args - Forwarded to {@link TailorDBFileAPI.download}\n * @returns Bytes and metadata for the file\n */\nconst download: TailorDBFileAPI[\"download\"] = (...args) => api().download(...args);\n\n/**\n * See {@link TailorDBFileAPI.downloadAsBase64}.\n * @param args - Forwarded to {@link TailorDBFileAPI.downloadAsBase64}\n * @returns Base64-encoded contents and metadata for the file\n */\nconst downloadAsBase64: TailorDBFileAPI[\"downloadAsBase64\"] = (...args) =>\n  api().downloadAsBase64(...args);\n\n/**\n * See {@link TailorDBFileAPI.delete}.\n * @param args - Forwarded to {@link TailorDBFileAPI.delete}\n * @returns Resolves once the file has been deleted\n */\nconst deleteFile: TailorDBFileAPI[\"delete\"] = (...args) => api().delete(...args);\n\n/**\n * See {@link TailorDBFileAPI.getMetadata}.\n * @param args - Forwarded to {@link TailorDBFileAPI.getMetadata}\n * @returns Metadata for the stored file\n */\nconst getMetadata: TailorDBFileAPI[\"getMetadata\"] = (...args) => api().getMetadata(...args);\n\n/**\n * See {@link TailorDBFileAPI.downloadStream}.\n * @param args - Forwarded to {@link TailorDBFileAPI.downloadStream}\n * @returns ReadableStream body and metadata for the file\n */\nconst downloadStream: TailorDBFileAPI[\"downloadStream\"] = (...args) =>\n  api().downloadStream(...args);\n\n/**\n * See {@link TailorDBFileAPI.uploadStream}.\n * @param args - Forwarded to {@link TailorDBFileAPI.uploadStream}\n * @returns Upload response containing the file metadata\n */\nconst uploadStream: TailorDBFileAPI[\"uploadStream\"] = (...args) => api().uploadStream(...args);\n\n/** Runtime wrapper namespace for `tailordb.file`. */\nexport const file = {\n  // oxlint-disable-next-line typescript/no-deprecated -- Only the legacy overload is deprecated.\n  upload,\n  download,\n  downloadAsBase64,\n  delete: deleteFile,\n  getMetadata,\n  downloadStream,\n  uploadStream,\n} as const satisfies TailorDBFileAPI;\n"],"mappings":"AAoOA,MAAM,QACH,WAAkE,SAAS,KAExE,EAAkB,yBAExB,SAAS,mBAAmB,EAAyD,CACnF,IAAM,EAAQ,EAAgB,KAAK,CAAI,EAEvC,OADK,EACE,CAAE,YAAa,EAAM,IAAM,IAAA,GAAW,QAAS,EAAK,MAAM,EAAM,EAAE,CAAC,MAAM,CAAE,EAD/D,CAAE,QAAS,CAAK,CAErC,CAEA,SAAS,aAAa,EAA0B,CAC9C,GAAI,OAAO,WAAW,YAAe,WACnC,GAAI,CACF,OAAO,WAAW,WAAW,CAAI,CACnC,MAAQ,CACN,MAAU,UAAU,2BAA2B,CACjD,CAIF,IAAM,EAAS,EAAK,QAAQ,eAAgB,EAAE,EAC9C,GACE,CAAC,yBAAyB,KAAK,CAAM,GACrC,EAAO,OAAS,GAAM,GACrB,EAAO,SAAS,GAAG,GAAK,EAAO,OAAS,GAAM,EAE/C,MAAU,UAAU,2BAA2B,EAEjD,IAAM,EAAU,KAAK,CAAM,EACrB,EAAQ,IAAI,WAAW,EAAQ,MAAM,EAC3C,IAAK,IAAI,EAAQ,EAAG,EAAQ,EAAQ,OAAQ,IAC1C,EAAM,GAAS,EAAQ,WAAW,CAAK,EAEzC,OAAO,CACT,CA2DA,eAAe,OACb,EACA,EACA,EACA,EACA,EACA,EAC6B,CAC7B,GAAI,GAAS,WAAa,IAAA,GACxB,OAAO,IAAI,CAAC,CAAC,OAAO,EAAW,EAAW,EAAW,EAAU,EAAM,CAAO,EAG9E,GAAM,CAAE,WAAU,GAAG,GAAkB,EACnC,EAAW,EACf,GAAI,OAAO,GAAS,SAClB,OAAQ,EAAR,CACE,IAAK,OACH,EAAW,IAAI,YAAY,CAAC,CAAC,OAAO,CAAI,EACxC,EAAc,cAAgB,4BAC9B,MACF,IAAK,SAAU,CACb,GAAM,CAAE,cAAa,WAAY,mBAAmB,CAAI,EACxD,EAAW,aAAa,CAAO,EAC/B,EAAc,cAAgB,EAC9B,KACF,CACA,QACE,MAAU,UAAU,qCAAqC,GAAU,CACvE,CAEF,OAAO,IAAI,CAAC,CAAC,OAAO,EAAW,EAAW,EAAW,EAAU,EAAU,CAAa,CACxF,CA+CA,MAAa,EAAO,CAElB,OACA,UA3C6C,GAAG,IAAS,IAAI,CAAC,CAAC,SAAS,GAAG,CAAI,EA4C/E,kBArC6D,GAAG,IAChE,IAAI,CAAC,CAAC,iBAAiB,GAAG,CAAI,EAqC9B,QA9B6C,GAAG,IAAS,IAAI,CAAC,CAAC,OAAO,GAAG,CAAI,EA+B7E,aAxBmD,GAAG,IAAS,IAAI,CAAC,CAAC,YAAY,GAAG,CAAI,EAyBxF,gBAlByD,GAAG,IAC5D,IAAI,CAAC,CAAC,eAAe,GAAG,CAAI,EAkB5B,cAXqD,GAAG,IAAS,IAAI,CAAC,CAAC,aAAa,GAAG,CAAI,CAY7F"}