@tailor-platform/sdk 2.14.2 → 2.15.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bin/tailor.mjs +2 -2
  3. package/dist/{application-BAqZFMWP.mjs → application-BpoMu4Af.mjs} +18 -18
  4. package/dist/application-BpoMu4Af.mjs.map +1 -0
  5. package/dist/application-fIhVKX78.mjs +1 -0
  6. package/dist/cli/commands/deploy/app-id-lock.d.mts +97 -0
  7. package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +15 -1
  8. package/dist/cli/commands/tailordb/migrate/generate.d.mts +5 -2
  9. package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +13 -0
  10. package/dist/cli/commands/tailordb/migrate/snapshot-comparison.d.mts +7 -1
  11. package/dist/cli/commands/workflow/executions.d.mts +2 -0
  12. package/dist/cli/lib.d.mts +4 -2
  13. package/dist/cli/lib.mjs +1 -1
  14. package/dist/cli/lib.mjs.map +1 -1
  15. package/dist/cli/main.d.mts +8 -8
  16. package/dist/cli/main.mjs +42 -44
  17. package/dist/cli/main.mjs.map +1 -1
  18. package/dist/cli/shared/command.d.mts +1 -1
  19. package/dist/cli/shared/function-execution.d.mts +14 -0
  20. package/dist/completion/zsh-worker.zsh +8 -3
  21. package/dist/configure/config/types.d.mts +23 -6
  22. package/dist/configure/index.mjs +1 -1
  23. package/dist/configure/index.mjs.map +1 -1
  24. package/dist/configure/services/executor/trigger/event.d.mts +5 -2
  25. package/dist/date-CGBZbMW5.mjs +2 -0
  26. package/dist/date-CGBZbMW5.mjs.map +1 -0
  27. package/dist/file-DKBOj5q4.mjs +2 -0
  28. package/dist/file-DKBOj5q4.mjs.map +1 -0
  29. package/dist/plugin/builtin/file-utils/index.mjs +27 -1
  30. package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
  31. package/dist/register-ts-hook-CAUcxCt4.mjs +711 -0
  32. package/dist/register-ts-hook-CAUcxCt4.mjs.map +1 -0
  33. package/dist/runtime/file.d.mts +46 -2
  34. package/dist/runtime/file.mjs +1 -1
  35. package/dist/runtime/index.mjs +1 -1
  36. package/dist/{schema-DMlLMsWA.mjs → schema-BTioi2dP.mjs} +2 -2
  37. package/dist/{schema-DMlLMsWA.mjs.map → schema-BTioi2dP.mjs.map} +1 -1
  38. package/dist/service_pb-DKrsO1_u.mjs +1 -0
  39. package/dist/service_pb-DprmLNsq.mjs +2 -0
  40. package/dist/{service_pb-BdzjiHfC.mjs.map → service_pb-DprmLNsq.mjs.map} +1 -1
  41. package/dist/types/auth.generated.d.mts +1 -19
  42. package/dist/types/executor.generated.d.mts +5 -3
  43. package/dist/types/field.generated.d.mts +40 -0
  44. package/dist/types/helpers.d.mts +9 -1
  45. package/dist/types/resolver.generated.d.mts +5 -40
  46. package/dist/vitest/index.d.mts +2 -3
  47. package/dist/vitest/index.mjs +1 -1
  48. package/dist/vitest/index.mjs.map +1 -1
  49. package/dist/vitest/mocks/file.d.mts +2 -2
  50. package/dist/vitest/setup.d.mts +0 -37
  51. package/dist/vitest/setup.mjs +1 -1
  52. package/dist/vitest/setup.mjs.map +1 -1
  53. package/dist/{workspace_resource_pb-C4EY-Gns.mjs → workspace_resource_pb-BOoRts_z.mjs} +2 -2
  54. package/dist/{workspace_resource_pb-C4EY-Gns.mjs.map → workspace_resource_pb-BOoRts_z.mjs.map} +1 -1
  55. package/docs/cli/application.md +1 -1
  56. package/docs/cli/function.md +19 -6
  57. package/docs/cli/tailordb.md +11 -11
  58. package/docs/configuration.md +20 -3
  59. package/docs/github-actions.md +45 -13
  60. package/docs/migration/v3.md +38 -0
  61. package/docs/runtime.md +39 -0
  62. package/docs/services/resolver.md +3 -1
  63. package/docs/services/tailordb-migration.md +25 -5
  64. package/docs/testing.md +3 -7
  65. package/package.json +7 -7
  66. package/dist/application-BAqZFMWP.mjs.map +0 -1
  67. package/dist/application-BKlOiD1x.mjs +0 -1
  68. package/dist/date-DrUO8rOJ.mjs +0 -2
  69. package/dist/date-DrUO8rOJ.mjs.map +0 -1
  70. package/dist/file-COPYfju_.mjs +0 -2
  71. package/dist/file-COPYfju_.mjs.map +0 -1
  72. package/dist/register-ts-hook-Cphn4r8s.mjs +0 -642
  73. package/dist/register-ts-hook-Cphn4r8s.mjs.map +0 -1
  74. package/dist/service_pb-BdzjiHfC.mjs +0 -2
  75. package/dist/service_pb-p68oLtp9.mjs +0 -1
@@ -1,5 +1,5 @@
1
1
  import { TailorEnv, TailorPrincipal } from "../../../../runtime/types.mjs";
2
- import { output } from "../../../../types/helpers.mjs";
2
+ import { SerializeDates, output } from "../../../../types/helpers.mjs";
3
3
  import { AuthAccessTokenTrigger as AuthAccessTokenTrigger$1, IdpUserTrigger as IdpUserTrigger$1, ResolverExecutedTrigger as ResolverExecutedTrigger$1, TailorDBTrigger as TailorDBTrigger$1, WorkflowExecutionTrigger as WorkflowExecutionTrigger$1, WorkflowJobExecutionTrigger as WorkflowJobExecutionTrigger$1 } from "../../../../types/executor.generated.mjs";
4
4
  import { TailorDBType } from "../../tailordb/schema.mjs";
5
5
  import { IdpName } from "../../../types/idp-name.mjs";
@@ -38,6 +38,9 @@ export interface RecordDeletedArgs<T extends TailorDBType> extends RecordArgs {
38
38
  * When `success` is false, `error` contains the error message and `result` is never.
39
39
  *
40
40
  * Narrow on `success` to safely access either `result` or `error`.
41
+ *
42
+ * The event payload arrives as JSON, so a `t.date({ as: "date" })` field in the
43
+ * resolver's output reaches `result` as its serialized `YYYY-MM-DD` string.
41
44
  * @example
42
45
  * body: async (args) => {
43
46
  * if (args.success) {
@@ -51,7 +54,7 @@ export type ResolverExecutedArgs<R extends ResolverConfig> = EventArgs & {
51
54
  resolverName: string;
52
55
  } & ({
53
56
  success: true;
54
- result: output<R["output"]>;
57
+ result: SerializeDates<output<R["output"]>>;
55
58
  error?: never;
56
59
  } | {
57
60
  success: false;
@@ -0,0 +1,2 @@
1
+ function formatDate(e){let t=e.getUTCFullYear();if(!Number.isFinite(t)||t<0||t>9999){let n=Number.isNaN(e.getTime())?`an invalid Date`:`year ${t}`;throw RangeError(`Expected a Date with a 4-digit year (0000-9999), but received ${n}`)}return`${String(t).padStart(4,`0`)}-${String(e.getUTCMonth()+1).padStart(2,`0`)}-${String(e.getUTCDate()).padStart(2,`0`)}`}function serialize(e,t,n){if(t==null)return t;if(e.metadata.array){if(!Array.isArray(t))return t;let r=t.map((t,r)=>serializeValue(e,t,`${n}[${r}]`));return r.every((e,n)=>e===t[n])?t:r}return serializeValue(e,t,n)}function describePathTarget(e){return e||`the top-level value`}function truncateForDescription(e){return e.length>100?`${e.slice(0,100)}...`:e}function describeReceivedValue(e){if(Array.isArray(e)){let t=e.slice(0,100),n=[...new Set(Array.from(t,e=>typeof e))];return n.length>0?`an array of ${n.join(`/`)}`:`an empty array`}if(typeof e==`string`)return`a string (${JSON.stringify(truncateForDescription(e))})`;let t=typeof e;return t===`object`?`an object`:t===`function`?`a function`:`a ${t} (${truncateForDescription(String(e))})`}function serializeValue(e,t,n){if(t==null)return t;if(e.type===`date`&&e.metadata.as===`date`){if(!(t instanceof Date))throw TypeError(`Expected a Date instance at ${describePathTarget(n)}, but received ${describeReceivedValue(t)}`);try{return formatDate(t)}catch(e){let t=e instanceof Error?e.message:String(e);throw RangeError(`Invalid date at ${describePathTarget(n)}: ${t}`,{cause:e})}}if(e.type!==`nested`||typeof t!=`object`||Array.isArray(t))return t;let r=t,i=r;for(let[t,a]of Object.entries(e.fields)){let e=serialize(a,r[t],n?`${n}.${t}`:t);e!==r[t]&&(i===r&&(i={...r}),Object.defineProperty(i,t,{value:e,enumerable:!0,configurable:!0,writable:!0}))}return i}function serializeDateFields(e,t){return serialize(e,t,``)}export{serializeDateFields as n,formatDate as t};
2
+ //# sourceMappingURL=date-CGBZbMW5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-CGBZbMW5.mjs","names":[],"sources":["../src/runtime/date.ts"],"sourcesContent":["import type { FieldMetadata, TailorFieldType } from \"#/configure/types/field.types\";\n\n/**\n * Format a Date using its UTC calendar date.\n * @param date - Date to format\n * @returns Date string in YYYY-MM-DD format\n */\nexport function formatDate(date: Date): string {\n const year = date.getUTCFullYear();\n if (!Number.isFinite(year) || year < 0 || year > 9999) {\n const received = Number.isNaN(date.getTime()) ? \"an invalid Date\" : `year ${year}`;\n throw new RangeError(\n `Expected a Date with a 4-digit year (0000-9999), but received ${received}`,\n );\n }\n return `${String(year).padStart(4, \"0\")}-${String(date.getUTCMonth() + 1).padStart(2, \"0\")}-${String(date.getUTCDate()).padStart(2, \"0\")}`;\n}\n\ntype DateField = {\n readonly type: TailorFieldType;\n readonly metadata: FieldMetadata;\n readonly fields: Record<string, DateField>;\n};\n\nfunction serialize(field: DateField, value: unknown, path: string): unknown {\n if (value === null || value === undefined) return value;\n if (field.metadata.array) {\n if (!Array.isArray(value)) return value;\n const converted = value.map((item, index) => serializeValue(field, item, `${path}[${index}]`));\n return converted.every((item, index) => item === value[index]) ? value : converted;\n }\n return serializeValue(field, value, path);\n}\n\nfunction describePathTarget(path: string): string {\n return path || \"the top-level value\";\n}\n\nconst MAX_DESCRIBED_VALUE_LENGTH = 100;\nconst MAX_DESCRIBED_ARRAY_SAMPLE = 100;\n\nfunction truncateForDescription(text: string): string {\n return text.length > MAX_DESCRIBED_VALUE_LENGTH\n ? `${text.slice(0, MAX_DESCRIBED_VALUE_LENGTH)}...`\n : text;\n}\n\nfunction describeReceivedValue(value: unknown): string {\n if (Array.isArray(value)) {\n const sample = value.slice(0, MAX_DESCRIBED_ARRAY_SAMPLE);\n const elementTypes = [...new Set(Array.from(sample, (item) => typeof item))];\n return elementTypes.length > 0 ? `an array of ${elementTypes.join(\"/\")}` : \"an empty array\";\n }\n if (typeof value === \"string\")\n return `a string (${JSON.stringify(truncateForDescription(value))})`;\n const type = typeof value;\n if (type === \"object\") return \"an object\";\n if (type === \"function\") return \"a function\";\n return `a ${type} (${truncateForDescription(String(value))})`;\n}\n\nfunction serializeValue(field: DateField, value: unknown, path: string): unknown {\n if (value === null || value === undefined) return value;\n if (field.type === \"date\" && field.metadata.as === \"date\") {\n if (!(value instanceof Date)) {\n throw new TypeError(\n `Expected a Date instance at ${describePathTarget(path)}, but received ${describeReceivedValue(value)}`,\n );\n }\n try {\n return formatDate(value);\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new RangeError(`Invalid date at ${describePathTarget(path)}: ${reason}`, {\n cause: error,\n });\n }\n }\n if (field.type !== \"nested\" || typeof value !== \"object\" || Array.isArray(value)) return value;\n const record = value as Record<string, unknown>;\n let result = record;\n for (const [key, child] of Object.entries(field.fields)) {\n const converted = serialize(child, record[key], path ? `${path}.${key}` : key);\n if (converted !== record[key]) {\n if (result === record) result = { ...record };\n Object.defineProperty(result, key, {\n value: converted,\n enumerable: true,\n configurable: true,\n writable: true,\n });\n }\n }\n return result;\n}\n\n/**\n * Convert fields using the Date representation to YYYY-MM-DD strings in UTC.\n * @param field - Date or object field defining the value's shape\n * @param value - Value to serialize\n * @returns Value with Date fields converted to strings\n * @internal\n */\nexport function serializeDateFields(field: DateField, value: unknown): unknown {\n return serialize(field, value, \"\");\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const api=()=>globalThis.tailordb.file,e=/^data:([^,]*);base64,/i;function stripBase64DataUrl(t){let n=e.exec(t);return n?{contentType:n[1]||void 0,payload:t.slice(n[0].length)}:{payload:t}}function decodeBase64(e){if(typeof Uint8Array.fromBase64==`function`)try{return Uint8Array.fromBase64(e)}catch{throw TypeError(`Invalid Base64 file data.`)}let t=e.replace(/[\t\n\f\r ]/g,``);if(!/^[A-Za-z0-9+/]*={0,2}$/.test(t)||t.length%4==1||t.includes(`=`)&&t.length%4!=0)throw TypeError(`Invalid Base64 file data.`);let n=atob(t),r=new Uint8Array(n.length);for(let e=0;e<n.length;e++)r[e]=n.charCodeAt(e);return r}async function upload(e,t,n,r,i,a){if(a?.encoding===void 0)return api().upload(e,t,n,r,i,a);let{encoding:o,...s}=a,c=i;if(typeof i==`string`)switch(o){case`utf8`:c=new TextEncoder().encode(i),s.contentType??=`text/plain; charset=utf-8`;break;case`base64`:{let{contentType:e,payload:t}=stripBase64DataUrl(i);c=decodeBase64(t),s.contentType??=e;break}default:throw TypeError(`Unsupported file upload encoding: ${o}`)}return api().upload(e,t,n,r,c,s)}const t={upload,download:(...e)=>api().download(...e),downloadAsBase64:(...e)=>api().downloadAsBase64(...e),delete:(...e)=>api().delete(...e),getMetadata:(...e)=>api().getMetadata(...e),downloadStream:(...e)=>api().downloadStream(...e),uploadStream:(...e)=>api().uploadStream(...e)};export{t};
2
+ //# sourceMappingURL=file-DKBOj5q4.mjs.map
@@ -0,0 +1 @@
1
+ {"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"}
@@ -3,6 +3,8 @@ import{t as e}from"../../../multiline-EyzjEwn9.mjs";function generateUnifiedFile
3
3
  import { file } from "@tailor-platform/sdk/runtime/file";
4
4
  import type {
5
5
  FileUploadOptions,
6
+ FileUploadStringOptions,
7
+ FileUploadBytes,
6
8
  FileUploadResponse,
7
9
  FileMetadata,
8
10
  FileDownloadStreamResponse,
@@ -29,11 +31,35 @@ import{t as e}from"../../../multiline-EyzjEwn9.mjs";function generateUnifiedFile
29
31
  }
30
32
  `+`
31
33
  `,e`
34
+ export function uploadFile<T extends keyof TypeWithFiles>(
35
+ type: T,
36
+ field: TypeWithFiles[T]["fields"],
37
+ recordId: string,
38
+ data: FileUploadBytes,
39
+ options?: FileUploadOptions,
40
+ ): Promise<FileUploadResponse>;
41
+ export function uploadFile<T extends keyof TypeWithFiles>(
42
+ type: T,
43
+ field: TypeWithFiles[T]["fields"],
44
+ recordId: string,
45
+ data: string | FileUploadBytes,
46
+ options: FileUploadStringOptions,
47
+ ): Promise<FileUploadResponse>;
48
+ /**
49
+ * @deprecated since NEXT_RELEASE — pass encoding: "utf8" for text or "base64" for Base64 strings. codemod: v3/file-upload-encoding
50
+ */
51
+ export function uploadFile<T extends keyof TypeWithFiles>(
52
+ type: T,
53
+ field: TypeWithFiles[T]["fields"],
54
+ recordId: string,
55
+ data: string | FileUploadBytes,
56
+ options?: FileUploadOptions,
57
+ ): Promise<FileUploadResponse>;
32
58
  export async function uploadFile<T extends keyof TypeWithFiles>(
33
59
  type: T,
34
60
  field: TypeWithFiles[T]["fields"],
35
61
  recordId: string,
36
- data: string | ArrayBuffer | Uint8Array<ArrayBufferLike> | number[],
62
+ data: string | FileUploadBytes,
37
63
  options?: FileUploadOptions,
38
64
  ): Promise<FileUploadResponse> {
39
65
  return await file.upload(namespaces[type], type, field, recordId, data, options);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugin/builtin/file-utils/generate-file-utils.ts","../../../../src/plugin/builtin/file-utils/process-file-type.ts","../../../../src/plugin/builtin/file-utils/index.ts"],"sourcesContent":["import multiline from \"#/utils/multiline\";\nimport type { FileUtilMetadata } from \"./types\";\n\n/**\n * Generate unified file utility functions from collected metadata.\n * @param namespaceData - Namespace data with file utility metadata\n * @returns Generated file utility code\n */\nexport function generateUnifiedFileUtils(\n namespaceData: { namespace: string; types: FileUtilMetadata[] }[],\n): string {\n if (namespaceData.length === 0) {\n return \"\";\n }\n\n // Collect all tables with their namespace\n const tableNamespaceMap = new Map<string, string>();\n const typeFieldsMap = new Map<string, string[]>();\n\n for (const { namespace, types } of namespaceData) {\n for (const type of types) {\n tableNamespaceMap.set(type.name, namespace);\n typeFieldsMap.set(type.name, type.fileFields);\n }\n }\n\n if (tableNamespaceMap.size === 0) {\n return \"\";\n }\n\n // Generate interface fields\n const interfaceFields = Array.from(typeFieldsMap.entries())\n .map(([tableName, fields]) => {\n const fieldNamesUnion = fields.map((field) => `\"${field}\"`).join(\" | \");\n return ` ${tableName}: {\\n fields: ${fieldNamesUnion};\\n };`;\n })\n .join(\"\\n\");\n\n const importStatement =\n multiline /* ts */ `\n import { file } from \"@tailor-platform/sdk/runtime/file\";\n import type {\n FileUploadOptions,\n FileUploadResponse,\n FileMetadata,\n FileDownloadStreamResponse,\n } from \"@tailor-platform/sdk/runtime/file\";\n ` + \"\\n\";\n\n const interfaceDefinition =\n multiline /* ts */ `\n export interface TypeWithFiles {\n ${interfaceFields}\n }\n ` + \"\\n\";\n\n // Generate namespaces object\n const namespaceEntries = Array.from(tableNamespaceMap.entries())\n .map(([tableName, namespace]) => ` ${tableName}: \"${namespace}\"`)\n .join(\",\\n\");\n\n const namespacesDefinition =\n multiline /* ts */ `\n const namespaces: Record<keyof TypeWithFiles, string> = {\n ${namespaceEntries},\n };\n ` + \"\\n\";\n\n // Generate downloadFile helper function\n const downloadFunction =\n multiline /* ts */ `\n export async function downloadFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n ) {\n return await file.download(namespaces[type], type, field, recordId);\n }\n ` + \"\\n\";\n\n // Generate uploadFile helper function\n const uploadFunction =\n multiline /* ts */ `\n export async function uploadFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n data: string | ArrayBuffer | Uint8Array<ArrayBufferLike> | number[],\n options?: FileUploadOptions,\n ): Promise<FileUploadResponse> {\n return await file.upload(namespaces[type], type, field, recordId, data, options);\n }\n ` + \"\\n\";\n\n // Generate deleteFile helper function\n const deleteFunction =\n multiline /* ts */ `\n export async function deleteFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n ): Promise<void> {\n return await file.delete(namespaces[type], type, field, recordId);\n }\n ` + \"\\n\";\n\n // Generate getFileMetadata helper function\n const getMetadataFunction =\n multiline /* ts */ `\n export async function getFileMetadata<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n ): Promise<FileMetadata> {\n return await file.getMetadata(namespaces[type], type, field, recordId);\n }\n ` + \"\\n\";\n\n // Generate downloadFileStream helper function\n const downloadStreamFunction =\n multiline /* ts */ `\n export async function downloadFileStream<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n ): Promise<FileDownloadStreamResponse> {\n return await file.downloadStream(namespaces[type], type, field, recordId);\n }\n ` + \"\\n\";\n\n return [\n importStatement,\n interfaceDefinition,\n namespacesDefinition,\n downloadFunction,\n uploadFunction,\n deleteFunction,\n getMetadataFunction,\n downloadStreamFunction,\n ].join(\"\\n\");\n}\n","import type { TailorDBType } from \"#/parser/service/tailordb/types\";\nimport type { FileUtilMetadata } from \"./types\";\n\n/**\n * Process a TailorDB table and extract file field metadata.\n * @param type - The parsed TailorDB table to process\n * @returns File utility metadata for the table\n */\nexport async function processFileType(type: TailorDBType): Promise<FileUtilMetadata> {\n const fileFields: string[] = [];\n\n if (type.files) {\n for (const fileFieldName of Object.keys(type.files)) {\n fileFields.push(fileFieldName);\n }\n }\n\n return {\n name: type.name,\n fileFields,\n };\n}\n","import { generateUnifiedFileUtils } from \"./generate-file-utils\";\nimport { processFileType } from \"./process-file-type\";\nimport type { Plugin, GeneratorResult, TailorDBReadyContext } from \"#/plugin/types\";\nimport type { FileUtilMetadata } from \"./types\";\n\n/** Unique identifier for the file utilities generator plugin. */\nexport const FileUtilsGeneratorID = \"@tailor-platform/file-utils\";\n\ntype FileUtilsPluginOptions = {\n distPath: string;\n};\n\n/**\n * Plugin that generates TypeWithFiles interface from TailorDB table definitions.\n * @param options - Plugin options\n * @param options.distPath - Output file path for generated file utilities\n * @returns Plugin instance with onTailorDBReady hook\n */\nexport function fileUtilsPlugin(\n options: FileUtilsPluginOptions,\n): Plugin<unknown, FileUtilsPluginOptions> {\n return {\n id: FileUtilsGeneratorID,\n description: \"Generates TypeWithFiles interface from TailorDB table definitions\",\n pluginConfig: options,\n\n async onTailorDBReady(\n ctx: TailorDBReadyContext<FileUtilsPluginOptions>,\n ): Promise<GeneratorResult> {\n const namespaceData: { namespace: string; types: FileUtilMetadata[] }[] = [];\n\n for (const ns of ctx.tailordb) {\n const typesWithFiles: FileUtilMetadata[] = [];\n\n for (const type of Object.values(ns.tables)) {\n const metadata = await processFileType(type);\n if (metadata.fileFields.length > 0) {\n typesWithFiles.push(metadata);\n }\n }\n\n if (typesWithFiles.length > 0) {\n namespaceData.push({\n namespace: ns.namespace,\n types: typesWithFiles,\n });\n }\n }\n\n const files: GeneratorResult[\"files\"] = [];\n if (namespaceData.length > 0) {\n const content = generateUnifiedFileUtils(namespaceData);\n if (content) {\n files.push({\n path: ctx.pluginConfig.distPath,\n content,\n });\n }\n }\n\n return { files };\n },\n };\n}\n"],"mappings":"oDAQA,SAAgB,yBACd,EACQ,CACR,GAAI,EAAc,SAAW,EAC3B,MAAO,GAIT,IAAM,EAAoB,IAAI,IACxB,EAAgB,IAAI,IAE1B,IAAK,GAAM,CAAE,YAAW,WAAW,EACjC,IAAK,IAAM,KAAQ,EACjB,EAAkB,IAAI,EAAK,KAAM,CAAS,EAC1C,EAAc,IAAI,EAAK,KAAM,EAAK,UAAU,EAIhD,GAAI,EAAkB,OAAS,EAC7B,MAAO,GAIT,IAAM,EAAkB,MAAM,KAAK,EAAc,QAAQ,CAAC,CAAC,CACxD,KAAK,CAAC,EAAW,KAET,KAAK,EAAU,mBADE,EAAO,IAAK,GAAU,IAAI,EAAM,EAAE,CAAC,CAAC,KAAK,KACV,EAAE,QAC1D,CAAC,CACD,KAAK;CAAI,EAEN,EACJ,CAAmB;;;;;;;;MAQf;EAEA,EACJ,CAAmB;;QAEf,EAAgB;;MAEhB;EAGA,EAAmB,MAAM,KAAK,EAAkB,QAAQ,CAAC,CAAC,CAC7D,KAAK,CAAC,EAAW,KAAe,KAAK,EAAU,KAAK,EAAU,EAAE,CAAC,CACjE,KAAK;CAAK,EAuEb,MAAO,CACL,EACA,EAtEA,CAAmB;;QAEf,EAAiB;;MAEjB;EAIJ,CAAmB;;;;;;;;MAQf;EAIJ,CAAmB;;;;;;;;;;MAUf;EAIJ,CAAmB;;;;;;;;MAQf;EAIJ,CAAmB;;;;;;;;MAQf;EAIJ,CAAmB;;;;;;;;MAQf;CAWN,CAAC,CAAC,KAAK;CAAI,CACb,CCpIA,eAAsB,gBAAgB,EAA+C,CACnF,IAAM,EAAuB,CAAC,EAE9B,GAAI,EAAK,MACP,IAAK,IAAM,KAAiB,OAAO,KAAK,EAAK,KAAK,EAChD,EAAW,KAAK,CAAa,EAIjC,MAAO,CACL,KAAM,EAAK,KACX,YACF,CACF,CCfA,MAAa,EAAuB,8BAYpC,SAAgB,gBACd,EACyC,CACzC,MAAO,CACL,GAAI,EACJ,YAAa,oEACb,aAAc,EAEd,MAAM,gBACJ,EAC0B,CAC1B,IAAM,EAAoE,CAAC,EAE3E,IAAK,IAAM,KAAM,EAAI,SAAU,CAC7B,IAAM,EAAqC,CAAC,EAE5C,IAAK,IAAM,KAAQ,OAAO,OAAO,EAAG,MAAM,EAAG,CAC3C,IAAM,EAAW,MAAM,gBAAgB,CAAI,EACvC,EAAS,WAAW,OAAS,GAC/B,EAAe,KAAK,CAAQ,CAEhC,CAEI,EAAe,OAAS,GAC1B,EAAc,KAAK,CACjB,UAAW,EAAG,UACd,MAAO,CACT,CAAC,CAEL,CAEA,IAAM,EAAkC,CAAC,EACzC,GAAI,EAAc,OAAS,EAAG,CAC5B,IAAM,EAAU,yBAAyB,CAAa,EAClD,GACF,EAAM,KAAK,CACT,KAAM,EAAI,aAAa,SACvB,SACF,CAAC,CAEL,CAEA,MAAO,CAAE,OAAM,CACjB,CACF,CACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugin/builtin/file-utils/generate-file-utils.ts","../../../../src/plugin/builtin/file-utils/process-file-type.ts","../../../../src/plugin/builtin/file-utils/index.ts"],"sourcesContent":["import multiline from \"#/utils/multiline\";\nimport type { FileUtilMetadata } from \"./types\";\n\n/**\n * Generate unified file utility functions from collected metadata.\n * @param namespaceData - Namespace data with file utility metadata\n * @returns Generated file utility code\n */\nexport function generateUnifiedFileUtils(\n namespaceData: { namespace: string; types: FileUtilMetadata[] }[],\n): string {\n if (namespaceData.length === 0) {\n return \"\";\n }\n\n // Collect all tables with their namespace\n const tableNamespaceMap = new Map<string, string>();\n const typeFieldsMap = new Map<string, string[]>();\n\n for (const { namespace, types } of namespaceData) {\n for (const type of types) {\n tableNamespaceMap.set(type.name, namespace);\n typeFieldsMap.set(type.name, type.fileFields);\n }\n }\n\n if (tableNamespaceMap.size === 0) {\n return \"\";\n }\n\n // Generate interface fields\n const interfaceFields = Array.from(typeFieldsMap.entries())\n .map(([tableName, fields]) => {\n const fieldNamesUnion = fields.map((field) => `\"${field}\"`).join(\" | \");\n return ` ${tableName}: {\\n fields: ${fieldNamesUnion};\\n };`;\n })\n .join(\"\\n\");\n\n const importStatement =\n multiline /* ts */ `\n import { file } from \"@tailor-platform/sdk/runtime/file\";\n import type {\n FileUploadOptions,\n FileUploadStringOptions,\n FileUploadBytes,\n FileUploadResponse,\n FileMetadata,\n FileDownloadStreamResponse,\n } from \"@tailor-platform/sdk/runtime/file\";\n ` + \"\\n\";\n\n const interfaceDefinition =\n multiline /* ts */ `\n export interface TypeWithFiles {\n ${interfaceFields}\n }\n ` + \"\\n\";\n\n // Generate namespaces object\n const namespaceEntries = Array.from(tableNamespaceMap.entries())\n .map(([tableName, namespace]) => ` ${tableName}: \"${namespace}\"`)\n .join(\",\\n\");\n\n const namespacesDefinition =\n multiline /* ts */ `\n const namespaces: Record<keyof TypeWithFiles, string> = {\n ${namespaceEntries},\n };\n ` + \"\\n\";\n\n // Generate downloadFile helper function\n const downloadFunction =\n multiline /* ts */ `\n export async function downloadFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n ) {\n return await file.download(namespaces[type], type, field, recordId);\n }\n ` + \"\\n\";\n\n // Generate uploadFile helper function\n const uploadFunction =\n multiline /* ts */ `\n export function uploadFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n data: FileUploadBytes,\n options?: FileUploadOptions,\n ): Promise<FileUploadResponse>;\n export function uploadFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n data: string | FileUploadBytes,\n options: FileUploadStringOptions,\n ): Promise<FileUploadResponse>;\n /**\n * @deprecated since NEXT_RELEASE — pass encoding: \"utf8\" for text or \"base64\" for Base64 strings. codemod: v3/file-upload-encoding\n */\n export function uploadFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n data: string | FileUploadBytes,\n options?: FileUploadOptions,\n ): Promise<FileUploadResponse>;\n export async function uploadFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n data: string | FileUploadBytes,\n options?: FileUploadOptions,\n ): Promise<FileUploadResponse> {\n return await file.upload(namespaces[type], type, field, recordId, data, options);\n }\n ` + \"\\n\";\n\n // Generate deleteFile helper function\n const deleteFunction =\n multiline /* ts */ `\n export async function deleteFile<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n ): Promise<void> {\n return await file.delete(namespaces[type], type, field, recordId);\n }\n ` + \"\\n\";\n\n // Generate getFileMetadata helper function\n const getMetadataFunction =\n multiline /* ts */ `\n export async function getFileMetadata<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n ): Promise<FileMetadata> {\n return await file.getMetadata(namespaces[type], type, field, recordId);\n }\n ` + \"\\n\";\n\n // Generate downloadFileStream helper function\n const downloadStreamFunction =\n multiline /* ts */ `\n export async function downloadFileStream<T extends keyof TypeWithFiles>(\n type: T,\n field: TypeWithFiles[T][\"fields\"],\n recordId: string,\n ): Promise<FileDownloadStreamResponse> {\n return await file.downloadStream(namespaces[type], type, field, recordId);\n }\n ` + \"\\n\";\n\n return [\n importStatement,\n interfaceDefinition,\n namespacesDefinition,\n downloadFunction,\n uploadFunction,\n deleteFunction,\n getMetadataFunction,\n downloadStreamFunction,\n ].join(\"\\n\");\n}\n","import type { TailorDBType } from \"#/parser/service/tailordb/types\";\nimport type { FileUtilMetadata } from \"./types\";\n\n/**\n * Process a TailorDB table and extract file field metadata.\n * @param type - The parsed TailorDB table to process\n * @returns File utility metadata for the table\n */\nexport async function processFileType(type: TailorDBType): Promise<FileUtilMetadata> {\n const fileFields: string[] = [];\n\n if (type.files) {\n for (const fileFieldName of Object.keys(type.files)) {\n fileFields.push(fileFieldName);\n }\n }\n\n return {\n name: type.name,\n fileFields,\n };\n}\n","import { generateUnifiedFileUtils } from \"./generate-file-utils\";\nimport { processFileType } from \"./process-file-type\";\nimport type { Plugin, GeneratorResult, TailorDBReadyContext } from \"#/plugin/types\";\nimport type { FileUtilMetadata } from \"./types\";\n\n/** Unique identifier for the file utilities generator plugin. */\nexport const FileUtilsGeneratorID = \"@tailor-platform/file-utils\";\n\ntype FileUtilsPluginOptions = {\n distPath: string;\n};\n\n/**\n * Plugin that generates TypeWithFiles interface from TailorDB table definitions.\n * @param options - Plugin options\n * @param options.distPath - Output file path for generated file utilities\n * @returns Plugin instance with onTailorDBReady hook\n */\nexport function fileUtilsPlugin(\n options: FileUtilsPluginOptions,\n): Plugin<unknown, FileUtilsPluginOptions> {\n return {\n id: FileUtilsGeneratorID,\n description: \"Generates TypeWithFiles interface from TailorDB table definitions\",\n pluginConfig: options,\n\n async onTailorDBReady(\n ctx: TailorDBReadyContext<FileUtilsPluginOptions>,\n ): Promise<GeneratorResult> {\n const namespaceData: { namespace: string; types: FileUtilMetadata[] }[] = [];\n\n for (const ns of ctx.tailordb) {\n const typesWithFiles: FileUtilMetadata[] = [];\n\n for (const type of Object.values(ns.tables)) {\n const metadata = await processFileType(type);\n if (metadata.fileFields.length > 0) {\n typesWithFiles.push(metadata);\n }\n }\n\n if (typesWithFiles.length > 0) {\n namespaceData.push({\n namespace: ns.namespace,\n types: typesWithFiles,\n });\n }\n }\n\n const files: GeneratorResult[\"files\"] = [];\n if (namespaceData.length > 0) {\n const content = generateUnifiedFileUtils(namespaceData);\n if (content) {\n files.push({\n path: ctx.pluginConfig.distPath,\n content,\n });\n }\n }\n\n return { files };\n },\n };\n}\n"],"mappings":"oDAQA,SAAgB,yBACd,EACQ,CACR,GAAI,EAAc,SAAW,EAC3B,MAAO,GAIT,IAAM,EAAoB,IAAI,IACxB,EAAgB,IAAI,IAE1B,IAAK,GAAM,CAAE,YAAW,WAAW,EACjC,IAAK,IAAM,KAAQ,EACjB,EAAkB,IAAI,EAAK,KAAM,CAAS,EAC1C,EAAc,IAAI,EAAK,KAAM,EAAK,UAAU,EAIhD,GAAI,EAAkB,OAAS,EAC7B,MAAO,GAIT,IAAM,EAAkB,MAAM,KAAK,EAAc,QAAQ,CAAC,CAAC,CACxD,KAAK,CAAC,EAAW,KAET,KAAK,EAAU,mBADE,EAAO,IAAK,GAAU,IAAI,EAAM,EAAE,CAAC,CAAC,KAAK,KACV,EAAE,QAC1D,CAAC,CACD,KAAK;CAAI,EAEN,EACJ,CAAmB;;;;;;;;;;MAUf;EAEA,EACJ,CAAmB;;QAEf,EAAgB;;MAEhB;EAGA,EAAmB,MAAM,KAAK,EAAkB,QAAQ,CAAC,CAAC,CAC7D,KAAK,CAAC,EAAW,KAAe,KAAK,EAAU,KAAK,EAAU,EAAE,CAAC,CACjE,KAAK;CAAK,EA+Fb,MAAO,CACL,EACA,EA9FA,CAAmB;;QAEf,EAAiB;;MAEjB;EAIJ,CAAmB;;;;;;;;MAQf;EAIJ,CAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAkCf;EAIJ,CAAmB;;;;;;;;MAQf;EAIJ,CAAmB;;;;;;;;MAQf;EAIJ,CAAmB;;;;;;;;MAQf;CAWN,CAAC,CAAC,KAAK;CAAI,CACb,CC9JA,eAAsB,gBAAgB,EAA+C,CACnF,IAAM,EAAuB,CAAC,EAE9B,GAAI,EAAK,MACP,IAAK,IAAM,KAAiB,OAAO,KAAK,EAAK,KAAK,EAChD,EAAW,KAAK,CAAa,EAIjC,MAAO,CACL,KAAM,EAAK,KACX,YACF,CACF,CCfA,MAAa,EAAuB,8BAYpC,SAAgB,gBACd,EACyC,CACzC,MAAO,CACL,GAAI,EACJ,YAAa,oEACb,aAAc,EAEd,MAAM,gBACJ,EAC0B,CAC1B,IAAM,EAAoE,CAAC,EAE3E,IAAK,IAAM,KAAM,EAAI,SAAU,CAC7B,IAAM,EAAqC,CAAC,EAE5C,IAAK,IAAM,KAAQ,OAAO,OAAO,EAAG,MAAM,EAAG,CAC3C,IAAM,EAAW,MAAM,gBAAgB,CAAI,EACvC,EAAS,WAAW,OAAS,GAC/B,EAAe,KAAK,CAAQ,CAEhC,CAEI,EAAe,OAAS,GAC1B,EAAc,KAAK,CACjB,UAAW,EAAG,UACd,MAAO,CACT,CAAC,CAEL,CAEA,IAAM,EAAkC,CAAC,EACzC,GAAI,EAAc,OAAS,EAAG,CAC5B,IAAM,EAAU,yBAAyB,CAAa,EAClD,GACF,EAAM,KAAK,CACT,KAAM,EAAI,aAAa,SACvB,SACF,CAAC,CAEL,CAEA,MAAO,CAAE,OAAM,CACjB,CACF,CACF"}