@tailor-platform/sdk 2.30.0 → 2.32.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 (79) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/ai/index.d.mts +112 -0
  3. package/dist/ai/index.mjs +2 -0
  4. package/dist/ai/index.mjs.map +1 -0
  5. package/dist/aigateway-DsWDjzk4.mjs.map +1 -1
  6. package/dist/application-D0xNJbag.mjs.map +1 -1
  7. package/dist/assert-WeXvmG4j.mjs.map +1 -1
  8. package/dist/authconnection-CynFBIv8.mjs.map +1 -1
  9. package/dist/brand-C8nMKhJC.mjs.map +1 -1
  10. package/dist/cli/lib.mjs +1 -1
  11. package/dist/cli/lib.mjs.map +1 -1
  12. package/dist/cli/main.mjs +6 -6
  13. package/dist/cli/main.mjs.map +1 -1
  14. package/dist/completion/zsh-worker.zsh +6 -2
  15. package/dist/configure/index.mjs.map +1 -1
  16. package/dist/context-D0QjfxzD.mjs.map +1 -1
  17. package/dist/crashreport-BCIy33Cz.mjs.map +1 -1
  18. package/dist/date-CgP7gMG_.mjs.map +1 -1
  19. package/dist/date-representation-Cr7W7Pn2.mjs.map +1 -1
  20. package/dist/errors-BOF68LJ5.mjs.map +1 -1
  21. package/dist/es-builtins-SfvXRVoG.mjs.map +1 -1
  22. package/dist/field-parse-C41e3UAF.mjs.map +1 -1
  23. package/dist/file-DKBOj5q4.mjs.map +1 -1
  24. package/dist/globals-BCmGX05J.mjs.map +1 -1
  25. package/dist/guards-yDXyKC9F.mjs.map +1 -1
  26. package/dist/iconv-DlFMt2gW.mjs.map +1 -1
  27. package/dist/idp-G_ojPBB5.mjs.map +1 -1
  28. package/dist/interceptor-DQg3cR_9.mjs.map +1 -1
  29. package/dist/kysely/index.mjs.map +1 -1
  30. package/dist/{kysely-type-BWMfV301.mjs → kysely-type-CBwsdx16.mjs} +6 -3
  31. package/dist/kysely-type-CBwsdx16.mjs.map +1 -0
  32. package/dist/logger-DP2BjQ93.mjs.map +1 -1
  33. package/dist/logger-RgtbhJ83.mjs.map +1 -1
  34. package/dist/manager-Bk-7vkwd.mjs.map +1 -1
  35. package/dist/migration-steps-C6F3zNdQ.mjs.map +1 -1
  36. package/dist/multiline-EyzjEwn9.mjs.map +1 -1
  37. package/dist/node-builtins-DYfhPBnz.mjs.map +1 -1
  38. package/dist/package-json-C690ceex.mjs.map +1 -1
  39. package/dist/platform-serialize-DkiTdHOt.mjs.map +1 -1
  40. package/dist/plugin/builtin/enum-constants/index.mjs.map +1 -1
  41. package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
  42. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  43. package/dist/plugin/index.mjs.map +1 -1
  44. package/dist/{register-ts-hook-CDnN_Jx2.mjs → register-ts-hook-_dHIREtU.mjs} +127 -89
  45. package/dist/register-ts-hook-_dHIREtU.mjs.map +1 -0
  46. package/dist/registry-BijIDRVA.mjs.map +1 -1
  47. package/dist/repl-editor-DGJBfIt1.mjs.map +1 -1
  48. package/dist/schema-B6hJQWp6.mjs.map +1 -1
  49. package/dist/secret-file-C9wp_FCX.mjs.map +1 -1
  50. package/dist/secretmanager-vHQoXdQz.mjs.map +1 -1
  51. package/dist/seed/index.mjs.map +1 -1
  52. package/dist/seed-Cn-3JApy.mjs.map +1 -1
  53. package/dist/service-9SBPNpK8.mjs.map +1 -1
  54. package/dist/service-9rwi_I4d.mjs.map +1 -1
  55. package/dist/service_pb-Bbu2QSIr.mjs.map +1 -1
  56. package/dist/telemetry-DfT2UfI4.mjs +2 -0
  57. package/dist/telemetry-DfT2UfI4.mjs.map +1 -0
  58. package/dist/{telemetry-DpkFGBmu.mjs → telemetry-nqOtDWNO.mjs} +1 -1
  59. package/dist/type-source--ZNcV8RJ.mjs.map +1 -1
  60. package/dist/user-agent-vdHYF3QL.mjs.map +1 -1
  61. package/dist/utils/test/index.mjs.map +1 -1
  62. package/dist/vitest/environment.mjs.map +1 -1
  63. package/dist/vitest/index.mjs.map +1 -1
  64. package/dist/vitest/setup.mjs.map +1 -1
  65. package/dist/wait-point-invoker-eiP-IIux.mjs.map +1 -1
  66. package/dist/wait-point-registry-B-ESkTZX.mjs.map +1 -1
  67. package/dist/workflow-Cs9ISw6j.mjs.map +1 -1
  68. package/dist/workspace_resource_pb-CaoM2npP.mjs.map +1 -1
  69. package/docs/ai.md +92 -0
  70. package/docs/cli/tailordb.md +17 -15
  71. package/docs/cli/workspace.md +1 -1
  72. package/docs/github-actions.md +98 -11
  73. package/docs/services/tailordb/migration.md +45 -31
  74. package/docs/testing.md +2 -1
  75. package/package.json +16 -9
  76. package/dist/kysely-type-BWMfV301.mjs.map +0 -1
  77. package/dist/register-ts-hook-CDnN_Jx2.mjs.map +0 -1
  78. package/dist/telemetry-Xk7J829P.mjs +0 -2
  79. package/dist/telemetry-Xk7J829P.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"context-D0QjfxzD.mjs","names":["raw","context"],"sources":["../src/runtime/context.ts"],"sourcesContent":["/**\n * Execution context utilities.\n *\n * Thin typed wrapper around the platform-provided `tailor.context` runtime API.\n * At runtime this delegates to `globalThis.tailor.context`.\n * @example\n * import { context } from \"@tailor-platform/sdk/runtime\";\n *\n * const invoker = context.getInvoker();\n * if (invoker) {\n * console.log(invoker.id, invoker.type, invoker.attributes, invoker.attributeList);\n * }\n */\n\nimport type { TailorPrincipal } from \"#/runtime/types\";\n\n/**\n * Information about the invoker of the current function execution.\n *\n * Matches the public `TailorPrincipal` shape — `attributes` is the attribute\n * map and `attributeList` is the array of attribute IDs.\n */\nexport type Invoker = TailorPrincipal;\n\n/**\n * Raw platform-side invoker payload returned by `tailor.context.getInvoker()`.\n * The wrapper normalizes this into {@link Invoker}.\n * @internal\n */\nexport interface ContextInvoker {\n /** The invoker's ID */\n id: string;\n /** The invoker's type */\n type: \"user\" | \"machine_user\";\n /** The workspace ID */\n workspaceId: string;\n /** The invoker's attribute IDs */\n attributes: string[];\n /** The invoker's attribute map */\n attributeMap: Record<string, unknown>;\n}\n\n/**\n * Platform API surface for `tailor.context`. Describes the shape the platform\n * runtime injects on `globalThis.tailor.context`.\n * @internal\n */\nexport interface PlatformContextAPI {\n getInvoker(): ContextInvoker | null;\n}\n\n/** Runtime wrapper API for execution context utilities. */\nexport interface TailorContextAPI {\n /**\n * Returns information about the current invoker.\n * @returns Invoker details, or `null` for anonymous invocations\n */\n getInvoker(): Invoker | null;\n}\n\n/**\n * Returns information about the invoker of the current function execution,\n * or `null` for anonymous invocations.\n * @returns Invoker details, or `null` when the call is anonymous\n */\nfunction getInvoker(): Invoker | null {\n const raw = (\n globalThis as unknown as { tailor: { context: PlatformContextAPI } }\n ).tailor.context.getInvoker();\n if (!raw) return null;\n return {\n id: raw.id,\n type: raw.type,\n workspaceId: raw.workspaceId,\n attributes: raw.attributeMap as Invoker[\"attributes\"],\n attributeList: raw.attributes as Invoker[\"attributeList\"],\n };\n}\n\n/** Runtime wrapper namespace for `tailor.context`. */\nexport const context = { getInvoker } as const satisfies TailorContextAPI;\n"],"mappings":"AAiEA,SAAS,YAA6B,CACpC,IAAMA,EACJ,WACA,OAAO,QAAQ,WAAW,EAE5B,OADKA,EACE,CACL,GAAIA,EAAI,GACR,KAAMA,EAAI,KACV,YAAaA,EAAI,YACjB,WAAYA,EAAI,aAChB,cAAeA,EAAI,UACrB,EAPiB,IAQnB,CAGA,MAAaC,EAAU,CAAE,UAAW"}
1
+ {"version":3,"file":"context-D0QjfxzD.mjs","names":["raw","context"],"sources":["../src/runtime/context.ts"],"sourcesContent":["/**\n * Execution context utilities.\n *\n * Thin typed wrapper around the platform-provided `tailor.context` runtime API.\n * At runtime this delegates to `globalThis.tailor.context`.\n * @example\n * import { context } from \"@tailor-platform/sdk/runtime\";\n *\n * const invoker = context.getInvoker();\n * if (invoker) {\n * console.log(invoker.id, invoker.type, invoker.attributes, invoker.attributeList);\n * }\n */\n\nimport type { TailorPrincipal } from \"#/runtime/types\";\n\n/**\n * Information about the invoker of the current function execution.\n *\n * Matches the public `TailorPrincipal` shape — `attributes` is the attribute\n * map and `attributeList` is the array of attribute IDs.\n */\nexport type Invoker = TailorPrincipal;\n\n/**\n * Raw platform-side invoker payload returned by `tailor.context.getInvoker()`.\n * The wrapper normalizes this into {@link Invoker}.\n * @internal\n */\nexport interface ContextInvoker {\n /** The invoker's ID */\n id: string;\n /** The invoker's type */\n type: \"user\" | \"machine_user\";\n /** The workspace ID */\n workspaceId: string;\n /** The invoker's attribute IDs */\n attributes: string[];\n /** The invoker's attribute map */\n attributeMap: Record<string, unknown>;\n}\n\n/**\n * Platform API surface for `tailor.context`. Describes the shape the platform\n * runtime injects on `globalThis.tailor.context`.\n * @internal\n */\nexport interface PlatformContextAPI {\n getInvoker(): ContextInvoker | null;\n}\n\n/** Runtime wrapper API for execution context utilities. */\nexport interface TailorContextAPI {\n /**\n * Returns information about the current invoker.\n * @returns Invoker details, or `null` for anonymous invocations\n */\n getInvoker(): Invoker | null;\n}\n\n/**\n * Returns information about the invoker of the current function execution,\n * or `null` for anonymous invocations.\n * @returns Invoker details, or `null` when the call is anonymous\n */\nfunction getInvoker(): Invoker | null {\n const raw = (\n globalThis as unknown as { tailor: { context: PlatformContextAPI } }\n ).tailor.context.getInvoker();\n if (!raw) return null;\n return {\n id: raw.id,\n type: raw.type,\n workspaceId: raw.workspaceId,\n attributes: raw.attributeMap as Invoker[\"attributes\"],\n attributeList: raw.attributes as Invoker[\"attributeList\"],\n };\n}\n\n/** Runtime wrapper namespace for `tailor.context`. */\nexport const context = { getInvoker } as const satisfies TailorContextAPI;\n"],"mappings":"AAiEA,SAAS,YAA6B,CACpC,IAAMA,EACJ,WACA,OAAO,QAAQ,WAAW,EAE5B,OADKA,EACE,CACL,GAAIA,EAAI,GACR,KAAMA,EAAI,KACV,YAAaA,EAAI,YACjB,WAAYA,EAAI,aAChB,cAAeA,EAAI,UACrB,EAPiB,IAQnB,CAGA,MAAaC,EAAU,CAAE,UAAW,E"}
@@ -1 +1 @@
1
- {"version":3,"file":"crashreport-BCIy33Cz.mjs","names":["logger","redactSecrets","readPackageJson","userAgentFromVersion","writeSecretFile","ensureSecretDir","path","fs","crypto","os","isCI","localEnabled","remoteEnabled","localDir","xdgConfig","JSON_FOOTER_MARKER","CRASH_LOG_EXTENSION","report","files","dir","f","file","filename","filePath","content","endpoint","response","ua","data","HOME_DIR","UUID_PATTERN","LONG_HEX_PATTERN","EMAIL_PATTERN","ABSOLUTE_PATH_PATTERN","WINDOWS_PATH_PATTERN","URL_QUERY_PATTERN","EMAIL_TEST_PATTERN","WINDOWS_DRIVE_TEST_PATTERN","SDK_PACKAGE_MARKER","separator","firstFrameIndex","stack","result","sdkIndex","match","normalized","message","redactNext","arg","argv","eqIndex","args","commandParts","options","isError","error","rawMessage","rawStack","errorName","currentUser","configPath","raw","parseYAML","email","user","config","sdkVersion","errorType","reason"],"sources":["../src/cli/crashreport/config.ts","../src/cli/crashreport/writer.ts","../src/cli/crashreport/sender.ts","../src/cli/crashreport/sanitize.ts","../src/cli/crashreport/report.ts","../src/cli/crashreport/index.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { isCI } from \"std-env\";\nimport { xdgConfig } from \"xdg-basedir\";\n\nexport interface CrashReportConfig {\n readonly localEnabled: boolean;\n readonly remoteEnabled: boolean;\n readonly localDir: string;\n}\n\n/**\n * Parse crash report configuration from environment variables.\n * Local crash log writing is enabled by default (opt-out via TAILOR_CRASH_REPORTS_LOCAL=off).\n * Remote sending is disabled by default (opt-in via TAILOR_CRASH_REPORTS_REMOTE=on).\n * Both are auto-disabled in CI environments.\n * @returns Crash report configuration\n */\nexport function parseCrashReportConfig(): CrashReportConfig {\n if (isCI) {\n return {\n localEnabled: false,\n remoteEnabled: false,\n localDir: \"\",\n };\n }\n\n const localEnabled = (process.env.TAILOR_CRASH_REPORTS_LOCAL ?? \"on\").toLowerCase() !== \"off\";\n const remoteEnabled = (process.env.TAILOR_CRASH_REPORTS_REMOTE ?? \"off\").toLowerCase() === \"on\";\n const localDir = xdgConfig ? path.join(xdgConfig, \"tailor-platform\", \"crash-reports\") : \"\";\n\n return {\n localEnabled: localEnabled && localDir !== \"\",\n remoteEnabled,\n localDir,\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\nimport { ensureSecretDir, writeSecretFile } from \"#/cli/shared/secret-file\";\nimport type { CrashReport } from \"./report\";\n\nconst MAX_CRASH_FILES = 10;\n\n/** Marker line that separates human-readable content from the JSON footer. */\nexport const JSON_FOOTER_MARKER = \"--- JSON ---\";\n\n/** File extension for crash log files. */\nexport const CRASH_LOG_EXTENSION = \".crash.log\";\n\n/**\n * Format a CrashReport as human-readable text for local crash log files.\n * @param report - Crash report to format\n * @returns Formatted text content\n */\nexport function formatCrashReport(report: CrashReport): string {\n const lines = [\n `Crash Report: ${report.id}`,\n `Timestamp: ${report.timestamp}`,\n `Error Type: ${report.errorType}`,\n \"\",\n \"--- Environment ---\",\n `SDK Version: ${report.sdkVersion}`,\n `Node Version: ${report.nodeVersion}`,\n `OS: ${report.osPlatform} ${report.osRelease}`,\n `Arch: ${report.arch}`,\n \"\",\n \"--- Command ---\",\n `Command: ${report.command}`,\n `Arguments: ${JSON.stringify(report.argv)}`,\n \"\",\n \"--- Error ---\",\n `Name: ${report.errorName}`,\n `Message: ${report.errorMessage}`,\n \"\",\n \"--- Stack Trace ---\",\n report.stackTrace || \"(no stack trace available)\",\n \"\",\n JSON_FOOTER_MARKER,\n JSON.stringify(report),\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate a filename for a crash log file.\n * Format: {timestamp}-{shortId}.crash.log\n * @param report - Crash report to generate filename for\n * @returns Filename string\n */\nfunction generateFilename(report: CrashReport): string {\n const safeTimestamp = report.timestamp.replace(/[:.]/g, \"-\");\n const shortId = report.id.slice(0, 8);\n return `${safeTimestamp}-${shortId}${CRASH_LOG_EXTENSION}`;\n}\n\n/**\n * Remove old crash log files, keeping only the most recent ones.\n * @param dir - Crash log directory\n */\nfunction cleanupOldFiles(dir: string): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(CRASH_LOG_EXTENSION))\n .toSorted()\n .toReversed();\n\n for (const file of files.slice(MAX_CRASH_FILES)) {\n fs.unlinkSync(path.join(dir, file));\n }\n } catch {\n // Best-effort cleanup, ignore errors\n }\n}\n\n/**\n * Write a crash report to a local file.\n * Creates the directory if it doesn't exist. Keeps only the last 10 crash files.\n * Never throws - returns the file path on success or undefined on failure.\n * @param report - Crash report to write\n * @param dir - Directory to write the crash log file to\n * @returns File path on success, undefined on failure\n */\nexport function writeCrashReport(report: CrashReport, dir: string): string | undefined {\n try {\n ensureSecretDir(dir);\n\n const filename = generateFilename(report);\n const filePath = path.join(dir, filename);\n const content = formatCrashReport(report);\n\n writeSecretFile(filePath, content);\n cleanupOldFiles(dir);\n\n return filePath;\n } catch {\n return undefined;\n }\n}\n","import type { CrashReport } from \"./report\";\n\nconst SEND_TIMEOUT_MS = 5000;\nconst PRODUCTION_ENDPOINT = \"https://sdk-error-tracking-926vh9t4cl.erp.dev/query\";\n\nconst SUBMIT_MUTATION = `\nmutation SubmitCrashReport(\n $id: String!\n $timestamp: String!\n $sdkVersion: String!\n $nodeVersion: String!\n $osPlatform: String!\n $osRelease: String!\n $arch: String!\n $command: String!\n $argv: [String]\n $errorName: String!\n $errorMessage: String!\n $stackTrace: String\n $errorType: String!\n $userId: String\n $userEmail: String\n) {\n submitCrashReport(\n id: $id\n timestamp: $timestamp\n sdkVersion: $sdkVersion\n nodeVersion: $nodeVersion\n osPlatform: $osPlatform\n osRelease: $osRelease\n arch: $arch\n command: $command\n argv: $argv\n errorName: $errorName\n errorMessage: $errorMessage\n stackTrace: $stackTrace\n errorType: $errorType\n userId: $userId\n userEmail: $userEmail\n ) {\n success\n }\n}`;\n\n/**\n * Send a crash report to the remote endpoint via GraphQL mutation.\n * Best-effort: never throws, returns boolean success.\n * @param report - Crash report to send\n * @param ua - User-Agent header value\n * @returns true if the request succeeded, false otherwise\n */\nexport async function sendCrashReport(report: CrashReport, ua: string): Promise<boolean> {\n try {\n const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": ua,\n },\n body: JSON.stringify({\n query: SUBMIT_MUTATION,\n variables: report,\n }),\n signal: AbortSignal.timeout(SEND_TIMEOUT_MS),\n });\n\n if (!response.ok) return false;\n\n const data = (await response.json()) as {\n errors?: unknown[];\n data?: { submitCrashReport: { success: boolean } };\n };\n if (data.errors?.length) return false;\n return data.data?.submitCrashReport.success === true;\n } catch {\n return false;\n }\n}\n","import * as os from \"node:os\";\n\nconst HOME_DIR = os.homedir();\n\n// Patterns for sanitization (global variants for use with .replace())\nconst UUID_PATTERN = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-fA-F]{32,}\\b/g;\nconst EMAIL_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g;\nconst ABSOLUTE_PATH_PATTERN = /(?:\\/(?:[\\w.@\\- ]+\\/)+[\\w.@\\- ]+)/g;\nconst WINDOWS_PATH_PATTERN = /(?:[A-Za-z]:\\\\(?:[\\w.@\\- ]+\\\\)+[\\w.@\\- ]+)/g;\nconst URL_QUERY_PATTERN = /[?&][^?\\s]*/g;\n\n// Non-global variants for single-match .test() calls (avoids lastIndex state issues)\nconst EMAIL_TEST_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/;\nconst WINDOWS_DRIVE_TEST_PATTERN = /^[A-Za-z]:\\\\/;\n\n// SDK package path marker for relative paths\nconst SDK_PACKAGE_MARKER = \"packages/sdk/\";\n\nfunction lastSegment(filePath: string, separator: string): string {\n return filePath.split(separator).pop() ?? filePath;\n}\n\n/**\n * Sanitize a stack trace by replacing absolute paths with relative SDK paths.\n * External paths are replaced with `<external>/filename.ext`.\n * Home directories are replaced with `~/<redacted>/`.\n * @param stack - Raw stack trace string\n * @returns Sanitized stack trace\n */\nexport function sanitizeStackTrace(stack: string): string {\n // V8 stack traces start with \"ErrorType: message\\n at ...\".\n // The error message may span multiple lines before the first \" at \" frame.\n // Apply message sanitization to all message lines so secrets embedded in\n // multiline error messages are redacted consistently with errorMessage.\n const firstFrameIndex = stack.search(/\\n\\s+at /);\n let result: string;\n if (firstFrameIndex !== -1) {\n result = sanitizeMessage(stack.slice(0, firstFrameIndex)) + stack.slice(firstFrameIndex);\n } else {\n result = sanitizeMessage(stack);\n }\n\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => {\n const sdkIndex = match.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return match.slice(sdkIndex);\n }\n\n if (match.startsWith(HOME_DIR)) {\n return `~/<redacted>/${lastSegment(match, \"/\")}`;\n }\n\n return `<external>/${lastSegment(match, \"/\")}`;\n });\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => {\n const normalized = match.replace(/\\\\/g, \"/\");\n const sdkIndex = normalized.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return normalized.slice(sdkIndex);\n }\n return `<external>/${lastSegment(match, \"\\\\\")}`;\n });\n return result;\n}\n\n/**\n * Sanitize an error message by redacting sensitive information.\n * Redacts: UUIDs, long hex tokens, email addresses, absolute paths, URL query strings.\n * @param message - Raw error message\n * @returns Sanitized error message\n */\nexport function sanitizeMessage(message: string): string {\n let result = message;\n // Strip serialized request/response bodies that may contain secrets\n result = result.replace(/\\nRequest:\\s*[\\s\\S]*$/, \"\\nRequest: <redacted>\");\n result = result.replace(UUID_PATTERN, \"<uuid>\");\n result = result.replace(LONG_HEX_PATTERN, \"<redacted>\");\n result = result.replace(EMAIL_PATTERN, \"<email>\");\n result = result.replace(URL_QUERY_PATTERN, \"?<redacted>\");\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"/\")}`);\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"\\\\\")}`);\n\n return result;\n}\n\n/**\n * Sanitize process.argv by keeping command/subcommand names and redacting\n * values of sensitive flags.\n * @param argv - Raw process.argv array\n * @returns Sanitized argv array\n */\nexport function sanitizeArgv(argv: string[]): string[] {\n const result: string[] = [];\n let redactNext = false;\n\n for (const arg of argv) {\n if (redactNext) {\n // If the next token is itself a flag, treat it as a new flag rather\n // than consuming it as the previous flag's value. This avoids leaking\n // the *next* flag's value (e.g., `--verbose --workspace-id secret`\n // would otherwise expose `secret`).\n if (!arg.startsWith(\"-\")) {\n result.push(\"<redacted>\");\n redactNext = false;\n continue;\n }\n redactNext = false;\n }\n\n if (arg.startsWith(\"-\")) {\n // --flag=value: keep flag name, redact value\n const eqIndex = arg.indexOf(\"=\");\n if (eqIndex !== -1) {\n result.push(`${arg.slice(0, eqIndex)}=<redacted>`);\n continue;\n }\n\n // --flag / -f: keep flag name, redact next arg as its value\n result.push(arg);\n redactNext = true;\n continue;\n }\n\n // Redact absolute paths\n if (arg.startsWith(\"/\") && arg.includes(\"/\", 1)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact Windows-style absolute paths\n if (WINDOWS_DRIVE_TEST_PATTERN.test(arg)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact email addresses\n if (EMAIL_TEST_PATTERN.test(arg)) {\n result.push(\"<email>\");\n continue;\n }\n\n result.push(arg);\n }\n\n return result;\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport { parseYAML } from \"confbox\";\nimport * as path from \"pathe\";\nimport { xdgConfig } from \"xdg-basedir\";\nimport { redactSecrets } from \"#/cli/shared/logger\";\nimport { sanitizeArgv, sanitizeMessage, sanitizeStackTrace } from \"./sanitize\";\n\nexport type ErrorType = \"uncaughtException\" | \"unhandledRejection\" | \"handledError\";\n\nexport interface CrashReport {\n id: string;\n timestamp: string;\n sdkVersion: string;\n nodeVersion: string;\n osPlatform: string;\n osRelease: string;\n arch: string;\n command: string;\n argv: string[];\n errorName: string;\n errorMessage: string;\n stackTrace: string;\n errorType: ErrorType;\n userId: string | null;\n userEmail: string | null;\n}\n\ninterface BuildCrashReportOptions {\n error: unknown;\n sdkVersion: string;\n errorType: ErrorType;\n}\n\n// Maximum subcommand depth to keep (e.g., \"tailordb migrate generate\" = 3 tokens).\n// Positional arguments beyond this are potentially sensitive user input.\n// Accepted trade-off: plain-text positional args that don't match known patterns\n// (UUIDs, hex tokens, emails, paths) pass through to `command` and `argv`.\n// Full redaction would require embedding the CLI command tree here, which is fragile.\nconst MAX_COMMAND_TOKENS = 3;\n\n/**\n * Parse the command name from process.argv.\n * Extracts up to MAX_COMMAND_TOKENS non-flag arguments after the script name.\n * @returns Parsed command string\n */\nfunction parseCommand(): string {\n const args = process.argv.slice(2);\n const commandParts: string[] = [];\n for (const arg of args) {\n if (arg.startsWith(\"-\") || commandParts.length >= MAX_COMMAND_TOKENS) break;\n commandParts.push(arg);\n }\n return commandParts.join(\" \") || \"<unknown>\";\n}\n\n/**\n * Build a CrashReport data structure from an error and context.\n * All sensitive data is sanitized before inclusion: `redactSecrets` masks every registered\n * secret first, then the pattern-based sanitizers below strip known-shape values (UUIDs,\n * long hex, emails, paths) from what's left. `redactSecrets` must run first — the path\n * sanitizer intentionally keeps a path's basename (e.g. `/home/user/.../key.json` becomes\n * `<path>/key.json`), so a registered secret shaped like a path would have that basename\n * survive if the pattern sanitizer ran on it first. This report is written to a local file\n * (and optionally sent remotely) outside the CLI's normal stderr path.\n * @param options - Error, SDK version, and crash type\n * @returns Sanitized crash report\n */\nexport function buildCrashReport(options: BuildCrashReportOptions): CrashReport {\n const { error, sdkVersion, errorType } = options;\n\n const isError = error instanceof Error;\n const rawMessage = isError ? error.message : String(error);\n const rawStack = isError && error.stack ? error.stack : \"\";\n const errorName = isError ? error.name : \"UnknownError\";\n\n const currentUser = readCurrentUser();\n\n return {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n sdkVersion,\n nodeVersion: process.version,\n osPlatform: process.platform,\n osRelease: os.release(),\n arch: process.arch,\n command: sanitizeMessage(redactSecrets(parseCommand())),\n argv: sanitizeArgv(process.argv.map(redactSecrets)),\n errorName: redactSecrets(errorName),\n errorMessage: sanitizeMessage(redactSecrets(rawMessage)),\n stackTrace: sanitizeStackTrace(redactSecrets(rawStack)),\n errorType,\n userId: currentUser?.id ? redactSecrets(currentUser.id) : null,\n userEmail: currentUser?.email ? redactSecrets(currentUser.email) : null,\n };\n}\n\ntype CurrentUser = {\n id: string;\n email: string | null;\n};\n\n/**\n * Read current_user from Tailor Platform config without side effects.\n * Unlike readPlatformConfig(), this never triggers migration or logs warnings.\n * @returns The current user ID and email, or null if unavailable\n */\nfunction readCurrentUser(): CurrentUser | null {\n try {\n if (!xdgConfig) return null;\n const configPath = path.join(xdgConfig, \"tailor-platform\", \"config.yaml\");\n if (!fs.existsSync(configPath)) return null;\n const raw = parseYAML(fs.readFileSync(configPath, \"utf-8\")) as {\n current_user?: string | null;\n users?: Record<string, { email?: unknown } | undefined>;\n };\n // parseYAML returns null for empty documents\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n const currentUser = raw?.current_user ?? null;\n if (!currentUser) return null;\n const email = raw.users?.[currentUser]?.email;\n return {\n id: currentUser,\n email: typeof email === \"string\" ? email : legacyEmail(currentUser),\n };\n } catch {\n return null;\n }\n}\n\nfunction legacyEmail(user: string): string | null {\n return user.includes(\"@\") ? user : null;\n}\n","import { logger } from \"#/cli/shared/logger\";\nimport { readPackageJson } from \"#/cli/shared/package-json\";\nimport { userAgentFromVersion } from \"#/cli/shared/user-agent\";\nimport { parseCrashReportConfig } from \"./config\";\nimport { buildCrashReport, type ErrorType } from \"./report\";\nimport { sendCrashReport } from \"./sender\";\nimport { writeCrashReport } from \"./writer\";\n\n/**\n * Report an unexpected crash. Writes a local crash log file and optionally\n * sends the report to a remote endpoint. Displays a user-facing message\n * with the crash log path and a command to submit the report.\n *\n * Never throws - all errors are silently caught.\n * @param error - The error that caused the crash\n * @param errorType - How the error was caught\n */\nexport async function reportCrash(error: unknown, errorType: ErrorType): Promise<void> {\n try {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const packageJson = await readPackageJson();\n const sdkVersion = packageJson.version ?? \"unknown\";\n\n const report = buildCrashReport({ error, sdkVersion, errorType });\n\n if (config.localEnabled) {\n const filePath = writeCrashReport(report, config.localDir);\n if (filePath) {\n logger.log(\n [\n \"\",\n \"An unexpected error occurred. A crash report has been saved to:\",\n ` ${filePath}`,\n \"\",\n \"To submit this report:\",\n ` tailor crashreport send --file \"${filePath}\"`,\n ].join(\"\\n\"),\n );\n }\n }\n\n if (config.remoteEnabled) {\n const ua = userAgentFromVersion(sdkVersion);\n await sendCrashReport(report, ua);\n }\n } catch {\n // Never throw from crash reporting\n }\n}\n\n/**\n * Register global uncaughtException and unhandledRejection handlers.\n * These catch errors outside the normal cleanup flow (e.g., during\n * argument parsing). Should be called once at CLI startup before runMain.\n */\nexport function initCrashReporting(): void {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const handleFatal = (error: unknown, errorType: ErrorType) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(message);\n void reportCrash(error, errorType).finally(() => {\n process.exit(1);\n });\n };\n\n process.on(\"uncaughtException\", (error) => handleFatal(error, \"uncaughtException\"));\n process.on(\"unhandledRejection\", (reason) => handleFatal(reason, \"unhandledRejection\"));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"crashreport-BCIy33Cz.mjs","names":["logger","redactSecrets","readPackageJson","userAgentFromVersion","writeSecretFile","ensureSecretDir","path","fs","crypto","os","isCI","localEnabled","remoteEnabled","localDir","xdgConfig","JSON_FOOTER_MARKER","CRASH_LOG_EXTENSION","report","files","dir","f","file","filename","filePath","content","endpoint","response","ua","data","HOME_DIR","UUID_PATTERN","LONG_HEX_PATTERN","EMAIL_PATTERN","ABSOLUTE_PATH_PATTERN","WINDOWS_PATH_PATTERN","URL_QUERY_PATTERN","EMAIL_TEST_PATTERN","WINDOWS_DRIVE_TEST_PATTERN","SDK_PACKAGE_MARKER","separator","firstFrameIndex","stack","result","sdkIndex","match","normalized","message","redactNext","arg","argv","eqIndex","args","commandParts","options","isError","error","rawMessage","rawStack","errorName","currentUser","configPath","raw","parseYAML","email","user","config","sdkVersion","errorType","reason"],"sources":["../src/cli/crashreport/config.ts","../src/cli/crashreport/writer.ts","../src/cli/crashreport/sender.ts","../src/cli/crashreport/sanitize.ts","../src/cli/crashreport/report.ts","../src/cli/crashreport/index.ts"],"sourcesContent":["import * as path from \"pathe\";\nimport { isCI } from \"std-env\";\nimport { xdgConfig } from \"xdg-basedir\";\n\nexport interface CrashReportConfig {\n readonly localEnabled: boolean;\n readonly remoteEnabled: boolean;\n readonly localDir: string;\n}\n\n/**\n * Parse crash report configuration from environment variables.\n * Local crash log writing is enabled by default (opt-out via TAILOR_CRASH_REPORTS_LOCAL=off).\n * Remote sending is disabled by default (opt-in via TAILOR_CRASH_REPORTS_REMOTE=on).\n * Both are auto-disabled in CI environments.\n * @returns Crash report configuration\n */\nexport function parseCrashReportConfig(): CrashReportConfig {\n if (isCI) {\n return {\n localEnabled: false,\n remoteEnabled: false,\n localDir: \"\",\n };\n }\n\n const localEnabled = (process.env.TAILOR_CRASH_REPORTS_LOCAL ?? \"on\").toLowerCase() !== \"off\";\n const remoteEnabled = (process.env.TAILOR_CRASH_REPORTS_REMOTE ?? \"off\").toLowerCase() === \"on\";\n const localDir = xdgConfig ? path.join(xdgConfig, \"tailor-platform\", \"crash-reports\") : \"\";\n\n return {\n localEnabled: localEnabled && localDir !== \"\",\n remoteEnabled,\n localDir,\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\nimport { ensureSecretDir, writeSecretFile } from \"#/cli/shared/secret-file\";\nimport type { CrashReport } from \"./report\";\n\nconst MAX_CRASH_FILES = 10;\n\n/** Marker line that separates human-readable content from the JSON footer. */\nexport const JSON_FOOTER_MARKER = \"--- JSON ---\";\n\n/** File extension for crash log files. */\nexport const CRASH_LOG_EXTENSION = \".crash.log\";\n\n/**\n * Format a CrashReport as human-readable text for local crash log files.\n * @param report - Crash report to format\n * @returns Formatted text content\n */\nexport function formatCrashReport(report: CrashReport): string {\n const lines = [\n `Crash Report: ${report.id}`,\n `Timestamp: ${report.timestamp}`,\n `Error Type: ${report.errorType}`,\n \"\",\n \"--- Environment ---\",\n `SDK Version: ${report.sdkVersion}`,\n `Node Version: ${report.nodeVersion}`,\n `OS: ${report.osPlatform} ${report.osRelease}`,\n `Arch: ${report.arch}`,\n \"\",\n \"--- Command ---\",\n `Command: ${report.command}`,\n `Arguments: ${JSON.stringify(report.argv)}`,\n \"\",\n \"--- Error ---\",\n `Name: ${report.errorName}`,\n `Message: ${report.errorMessage}`,\n \"\",\n \"--- Stack Trace ---\",\n report.stackTrace || \"(no stack trace available)\",\n \"\",\n JSON_FOOTER_MARKER,\n JSON.stringify(report),\n \"\",\n ];\n return lines.join(\"\\n\");\n}\n\n/**\n * Generate a filename for a crash log file.\n * Format: {timestamp}-{shortId}.crash.log\n * @param report - Crash report to generate filename for\n * @returns Filename string\n */\nfunction generateFilename(report: CrashReport): string {\n const safeTimestamp = report.timestamp.replace(/[:.]/g, \"-\");\n const shortId = report.id.slice(0, 8);\n return `${safeTimestamp}-${shortId}${CRASH_LOG_EXTENSION}`;\n}\n\n/**\n * Remove old crash log files, keeping only the most recent ones.\n * @param dir - Crash log directory\n */\nfunction cleanupOldFiles(dir: string): void {\n try {\n const files = fs\n .readdirSync(dir)\n .filter((f) => f.endsWith(CRASH_LOG_EXTENSION))\n .toSorted()\n .toReversed();\n\n for (const file of files.slice(MAX_CRASH_FILES)) {\n fs.unlinkSync(path.join(dir, file));\n }\n } catch {\n // Best-effort cleanup, ignore errors\n }\n}\n\n/**\n * Write a crash report to a local file.\n * Creates the directory if it doesn't exist. Keeps only the last 10 crash files.\n * Never throws - returns the file path on success or undefined on failure.\n * @param report - Crash report to write\n * @param dir - Directory to write the crash log file to\n * @returns File path on success, undefined on failure\n */\nexport function writeCrashReport(report: CrashReport, dir: string): string | undefined {\n try {\n ensureSecretDir(dir);\n\n const filename = generateFilename(report);\n const filePath = path.join(dir, filename);\n const content = formatCrashReport(report);\n\n writeSecretFile(filePath, content);\n cleanupOldFiles(dir);\n\n return filePath;\n } catch {\n return undefined;\n }\n}\n","import type { CrashReport } from \"./report\";\n\nconst SEND_TIMEOUT_MS = 5000;\nconst PRODUCTION_ENDPOINT = \"https://sdk-error-tracking-926vh9t4cl.erp.dev/query\";\n\nconst SUBMIT_MUTATION = `\nmutation SubmitCrashReport(\n $id: String!\n $timestamp: String!\n $sdkVersion: String!\n $nodeVersion: String!\n $osPlatform: String!\n $osRelease: String!\n $arch: String!\n $command: String!\n $argv: [String]\n $errorName: String!\n $errorMessage: String!\n $stackTrace: String\n $errorType: String!\n $userId: String\n $userEmail: String\n) {\n submitCrashReport(\n id: $id\n timestamp: $timestamp\n sdkVersion: $sdkVersion\n nodeVersion: $nodeVersion\n osPlatform: $osPlatform\n osRelease: $osRelease\n arch: $arch\n command: $command\n argv: $argv\n errorName: $errorName\n errorMessage: $errorMessage\n stackTrace: $stackTrace\n errorType: $errorType\n userId: $userId\n userEmail: $userEmail\n ) {\n success\n }\n}`;\n\n/**\n * Send a crash report to the remote endpoint via GraphQL mutation.\n * Best-effort: never throws, returns boolean success.\n * @param report - Crash report to send\n * @param ua - User-Agent header value\n * @returns true if the request succeeded, false otherwise\n */\nexport async function sendCrashReport(report: CrashReport, ua: string): Promise<boolean> {\n try {\n const endpoint = process.env.TAILOR_CRASH_REPORT_ENDPOINT || PRODUCTION_ENDPOINT;\n const response = await fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": ua,\n },\n body: JSON.stringify({\n query: SUBMIT_MUTATION,\n variables: report,\n }),\n signal: AbortSignal.timeout(SEND_TIMEOUT_MS),\n });\n\n if (!response.ok) return false;\n\n const data = (await response.json()) as {\n errors?: unknown[];\n data?: { submitCrashReport: { success: boolean } };\n };\n if (data.errors?.length) return false;\n return data.data?.submitCrashReport.success === true;\n } catch {\n return false;\n }\n}\n","import * as os from \"node:os\";\n\nconst HOME_DIR = os.homedir();\n\n// Patterns for sanitization (global variants for use with .replace())\nconst UUID_PATTERN = /\\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\b/gi;\nconst LONG_HEX_PATTERN = /\\b[0-9a-fA-F]{32,}\\b/g;\nconst EMAIL_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/g;\nconst ABSOLUTE_PATH_PATTERN = /(?:\\/(?:[\\w.@\\- ]+\\/)+[\\w.@\\- ]+)/g;\nconst WINDOWS_PATH_PATTERN = /(?:[A-Za-z]:\\\\(?:[\\w.@\\- ]+\\\\)+[\\w.@\\- ]+)/g;\nconst URL_QUERY_PATTERN = /[?&][^?\\s]*/g;\n\n// Non-global variants for single-match .test() calls (avoids lastIndex state issues)\nconst EMAIL_TEST_PATTERN = /\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}\\b/;\nconst WINDOWS_DRIVE_TEST_PATTERN = /^[A-Za-z]:\\\\/;\n\n// SDK package path marker for relative paths\nconst SDK_PACKAGE_MARKER = \"packages/sdk/\";\n\nfunction lastSegment(filePath: string, separator: string): string {\n return filePath.split(separator).pop() ?? filePath;\n}\n\n/**\n * Sanitize a stack trace by replacing absolute paths with relative SDK paths.\n * External paths are replaced with `<external>/filename.ext`.\n * Home directories are replaced with `~/<redacted>/`.\n * @param stack - Raw stack trace string\n * @returns Sanitized stack trace\n */\nexport function sanitizeStackTrace(stack: string): string {\n // V8 stack traces start with \"ErrorType: message\\n at ...\".\n // The error message may span multiple lines before the first \" at \" frame.\n // Apply message sanitization to all message lines so secrets embedded in\n // multiline error messages are redacted consistently with errorMessage.\n const firstFrameIndex = stack.search(/\\n\\s+at /);\n let result: string;\n if (firstFrameIndex !== -1) {\n result = sanitizeMessage(stack.slice(0, firstFrameIndex)) + stack.slice(firstFrameIndex);\n } else {\n result = sanitizeMessage(stack);\n }\n\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => {\n const sdkIndex = match.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return match.slice(sdkIndex);\n }\n\n if (match.startsWith(HOME_DIR)) {\n return `~/<redacted>/${lastSegment(match, \"/\")}`;\n }\n\n return `<external>/${lastSegment(match, \"/\")}`;\n });\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => {\n const normalized = match.replace(/\\\\/g, \"/\");\n const sdkIndex = normalized.indexOf(SDK_PACKAGE_MARKER);\n if (sdkIndex !== -1) {\n return normalized.slice(sdkIndex);\n }\n return `<external>/${lastSegment(match, \"\\\\\")}`;\n });\n return result;\n}\n\n/**\n * Sanitize an error message by redacting sensitive information.\n * Redacts: UUIDs, long hex tokens, email addresses, absolute paths, URL query strings.\n * @param message - Raw error message\n * @returns Sanitized error message\n */\nexport function sanitizeMessage(message: string): string {\n let result = message;\n // Strip serialized request/response bodies that may contain secrets\n result = result.replace(/\\nRequest:\\s*[\\s\\S]*$/, \"\\nRequest: <redacted>\");\n result = result.replace(UUID_PATTERN, \"<uuid>\");\n result = result.replace(LONG_HEX_PATTERN, \"<redacted>\");\n result = result.replace(EMAIL_PATTERN, \"<email>\");\n result = result.replace(URL_QUERY_PATTERN, \"?<redacted>\");\n result = result.replace(ABSOLUTE_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"/\")}`);\n result = result.replace(WINDOWS_PATH_PATTERN, (match) => `<path>/${lastSegment(match, \"\\\\\")}`);\n\n return result;\n}\n\n/**\n * Sanitize process.argv by keeping command/subcommand names and redacting\n * values of sensitive flags.\n * @param argv - Raw process.argv array\n * @returns Sanitized argv array\n */\nexport function sanitizeArgv(argv: string[]): string[] {\n const result: string[] = [];\n let redactNext = false;\n\n for (const arg of argv) {\n if (redactNext) {\n // If the next token is itself a flag, treat it as a new flag rather\n // than consuming it as the previous flag's value. This avoids leaking\n // the *next* flag's value (e.g., `--verbose --workspace-id secret`\n // would otherwise expose `secret`).\n if (!arg.startsWith(\"-\")) {\n result.push(\"<redacted>\");\n redactNext = false;\n continue;\n }\n redactNext = false;\n }\n\n if (arg.startsWith(\"-\")) {\n // --flag=value: keep flag name, redact value\n const eqIndex = arg.indexOf(\"=\");\n if (eqIndex !== -1) {\n result.push(`${arg.slice(0, eqIndex)}=<redacted>`);\n continue;\n }\n\n // --flag / -f: keep flag name, redact next arg as its value\n result.push(arg);\n redactNext = true;\n continue;\n }\n\n // Redact absolute paths\n if (arg.startsWith(\"/\") && arg.includes(\"/\", 1)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact Windows-style absolute paths\n if (WINDOWS_DRIVE_TEST_PATTERN.test(arg)) {\n result.push(\"<path>\");\n continue;\n }\n\n // Redact email addresses\n if (EMAIL_TEST_PATTERN.test(arg)) {\n result.push(\"<email>\");\n continue;\n }\n\n result.push(arg);\n }\n\n return result;\n}\n","import * as crypto from \"node:crypto\";\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport { parseYAML } from \"confbox\";\nimport * as path from \"pathe\";\nimport { xdgConfig } from \"xdg-basedir\";\nimport { redactSecrets } from \"#/cli/shared/logger\";\nimport { sanitizeArgv, sanitizeMessage, sanitizeStackTrace } from \"./sanitize\";\n\nexport type ErrorType = \"uncaughtException\" | \"unhandledRejection\" | \"handledError\";\n\nexport interface CrashReport {\n id: string;\n timestamp: string;\n sdkVersion: string;\n nodeVersion: string;\n osPlatform: string;\n osRelease: string;\n arch: string;\n command: string;\n argv: string[];\n errorName: string;\n errorMessage: string;\n stackTrace: string;\n errorType: ErrorType;\n userId: string | null;\n userEmail: string | null;\n}\n\ninterface BuildCrashReportOptions {\n error: unknown;\n sdkVersion: string;\n errorType: ErrorType;\n}\n\n// Maximum subcommand depth to keep (e.g., \"tailordb migrate generate\" = 3 tokens).\n// Positional arguments beyond this are potentially sensitive user input.\n// Accepted trade-off: plain-text positional args that don't match known patterns\n// (UUIDs, hex tokens, emails, paths) pass through to `command` and `argv`.\n// Full redaction would require embedding the CLI command tree here, which is fragile.\nconst MAX_COMMAND_TOKENS = 3;\n\n/**\n * Parse the command name from process.argv.\n * Extracts up to MAX_COMMAND_TOKENS non-flag arguments after the script name.\n * @returns Parsed command string\n */\nfunction parseCommand(): string {\n const args = process.argv.slice(2);\n const commandParts: string[] = [];\n for (const arg of args) {\n if (arg.startsWith(\"-\") || commandParts.length >= MAX_COMMAND_TOKENS) break;\n commandParts.push(arg);\n }\n return commandParts.join(\" \") || \"<unknown>\";\n}\n\n/**\n * Build a CrashReport data structure from an error and context.\n * All sensitive data is sanitized before inclusion: `redactSecrets` masks every registered\n * secret first, then the pattern-based sanitizers below strip known-shape values (UUIDs,\n * long hex, emails, paths) from what's left. `redactSecrets` must run first — the path\n * sanitizer intentionally keeps a path's basename (e.g. `/home/user/.../key.json` becomes\n * `<path>/key.json`), so a registered secret shaped like a path would have that basename\n * survive if the pattern sanitizer ran on it first. This report is written to a local file\n * (and optionally sent remotely) outside the CLI's normal stderr path.\n * @param options - Error, SDK version, and crash type\n * @returns Sanitized crash report\n */\nexport function buildCrashReport(options: BuildCrashReportOptions): CrashReport {\n const { error, sdkVersion, errorType } = options;\n\n const isError = error instanceof Error;\n const rawMessage = isError ? error.message : String(error);\n const rawStack = isError && error.stack ? error.stack : \"\";\n const errorName = isError ? error.name : \"UnknownError\";\n\n const currentUser = readCurrentUser();\n\n return {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n sdkVersion,\n nodeVersion: process.version,\n osPlatform: process.platform,\n osRelease: os.release(),\n arch: process.arch,\n command: sanitizeMessage(redactSecrets(parseCommand())),\n argv: sanitizeArgv(process.argv.map(redactSecrets)),\n errorName: redactSecrets(errorName),\n errorMessage: sanitizeMessage(redactSecrets(rawMessage)),\n stackTrace: sanitizeStackTrace(redactSecrets(rawStack)),\n errorType,\n userId: currentUser?.id ? redactSecrets(currentUser.id) : null,\n userEmail: currentUser?.email ? redactSecrets(currentUser.email) : null,\n };\n}\n\ntype CurrentUser = {\n id: string;\n email: string | null;\n};\n\n/**\n * Read current_user from Tailor Platform config without side effects.\n * Unlike readPlatformConfig(), this never triggers migration or logs warnings.\n * @returns The current user ID and email, or null if unavailable\n */\nfunction readCurrentUser(): CurrentUser | null {\n try {\n if (!xdgConfig) return null;\n const configPath = path.join(xdgConfig, \"tailor-platform\", \"config.yaml\");\n if (!fs.existsSync(configPath)) return null;\n const raw = parseYAML(fs.readFileSync(configPath, \"utf-8\")) as {\n current_user?: string | null;\n users?: Record<string, { email?: unknown } | undefined>;\n };\n // parseYAML returns null for empty documents\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n const currentUser = raw?.current_user ?? null;\n if (!currentUser) return null;\n const email = raw.users?.[currentUser]?.email;\n return {\n id: currentUser,\n email: typeof email === \"string\" ? email : legacyEmail(currentUser),\n };\n } catch {\n return null;\n }\n}\n\nfunction legacyEmail(user: string): string | null {\n return user.includes(\"@\") ? user : null;\n}\n","import { logger } from \"#/cli/shared/logger\";\nimport { readPackageJson } from \"#/cli/shared/package-json\";\nimport { userAgentFromVersion } from \"#/cli/shared/user-agent\";\nimport { parseCrashReportConfig } from \"./config\";\nimport { buildCrashReport, type ErrorType } from \"./report\";\nimport { sendCrashReport } from \"./sender\";\nimport { writeCrashReport } from \"./writer\";\n\n/**\n * Report an unexpected crash. Writes a local crash log file and optionally\n * sends the report to a remote endpoint. Displays a user-facing message\n * with the crash log path and a command to submit the report.\n *\n * Never throws - all errors are silently caught.\n * @param error - The error that caused the crash\n * @param errorType - How the error was caught\n */\nexport async function reportCrash(error: unknown, errorType: ErrorType): Promise<void> {\n try {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const packageJson = await readPackageJson();\n const sdkVersion = packageJson.version ?? \"unknown\";\n\n const report = buildCrashReport({ error, sdkVersion, errorType });\n\n if (config.localEnabled) {\n const filePath = writeCrashReport(report, config.localDir);\n if (filePath) {\n logger.log(\n [\n \"\",\n \"An unexpected error occurred. A crash report has been saved to:\",\n ` ${filePath}`,\n \"\",\n \"To submit this report:\",\n ` tailor crashreport send --file \"${filePath}\"`,\n ].join(\"\\n\"),\n );\n }\n }\n\n if (config.remoteEnabled) {\n const ua = userAgentFromVersion(sdkVersion);\n await sendCrashReport(report, ua);\n }\n } catch {\n // Never throw from crash reporting\n }\n}\n\n/**\n * Register global uncaughtException and unhandledRejection handlers.\n * These catch errors outside the normal cleanup flow (e.g., during\n * argument parsing). Should be called once at CLI startup before runMain.\n */\nexport function initCrashReporting(): void {\n const config = parseCrashReportConfig();\n if (!config.localEnabled && !config.remoteEnabled) return;\n\n const handleFatal = (error: unknown, errorType: ErrorType) => {\n const message = error instanceof Error ? error.message : String(error);\n logger.error(message);\n void reportCrash(error, errorType).finally(() => {\n process.exit(1);\n });\n };\n\n process.on(\"uncaughtException\", (error) => handleFatal(error, \"uncaughtException\"));\n process.on(\"unhandledRejection\", (reason) => handleFatal(reason, \"unhandledRejection\"));\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"date-CgP7gMG_.mjs","names":["temporal","Temporal","date","year","received","hour","minute","value","iso","isInstant","field","converted","item","path","index","text","sample","elementTypes","type","gate","resolved","as","defaultRepresentation","representation","reason","error","expected","dateSerializers","serializeAs","record","result","key","child"],"sources":["../src/runtime/temporal.ts","../src/runtime/date.ts"],"sourcesContent":["import type { Temporal as TemporalTypes } from \"temporal-spec\";\n\n/**\n * Get the host Temporal implementation when a value is needed.\n * @returns Host Temporal implementation\n * @internal\n */\nexport function getTemporal(): typeof TemporalTypes {\n const temporal = (globalThis as unknown as { Temporal?: typeof TemporalTypes }).Temporal;\n if (!temporal) {\n throw new ReferenceError(\n \"Temporal is unavailable. Use the Tailor Platform runtime or the SDK tailor-runtime Vitest environment.\",\n );\n }\n return temporal;\n}\n\n/**\n * Typed access to the runtime's Temporal API, without installing a polyfill.\n * Constructors are resolved on access so configuration files can load in Node.js.\n * @example\n * import { Temporal } from \"@tailor-platform/sdk/runtime\";\n * const tomorrow: Temporal.PlainDate = Temporal.Now.plainDateISO().add({ days: 1 });\n */\nexport const Temporal: typeof TemporalTypes = {\n get PlainDate() {\n return getTemporal().PlainDate;\n },\n get PlainTime() {\n return getTemporal().PlainTime;\n },\n get PlainDateTime() {\n return getTemporal().PlainDateTime;\n },\n get PlainYearMonth() {\n return getTemporal().PlainYearMonth;\n },\n get PlainMonthDay() {\n return getTemporal().PlainMonthDay;\n },\n get ZonedDateTime() {\n return getTemporal().ZonedDateTime;\n },\n get Duration() {\n return getTemporal().Duration;\n },\n get Instant() {\n return getTemporal().Instant;\n },\n get Now() {\n return getTemporal().Now;\n },\n};\n\n/** Temporal instance types supplied by the SDK, independent of TypeScript's lib settings. */\n// Type-only merging supports Temporal.PlainDate annotations on the value import.\n// oxlint-disable-next-line typescript/no-namespace\nexport declare namespace Temporal {\n /** The runtime Temporal.PlainDate instance. */\n type PlainDate = TemporalTypes.PlainDate;\n /** The runtime Temporal.PlainTime instance. */\n type PlainTime = TemporalTypes.PlainTime;\n /** The runtime Temporal.PlainDateTime instance. */\n type PlainDateTime = TemporalTypes.PlainDateTime;\n /** The runtime Temporal.PlainYearMonth instance. */\n type PlainYearMonth = TemporalTypes.PlainYearMonth;\n /** The runtime Temporal.PlainMonthDay instance. */\n type PlainMonthDay = TemporalTypes.PlainMonthDay;\n /** The runtime Temporal.ZonedDateTime instance. */\n type ZonedDateTime = TemporalTypes.ZonedDateTime;\n /** The runtime Temporal.Duration instance. */\n type Duration = TemporalTypes.Duration;\n /** The runtime Temporal.Instant instance. */\n type Instant = TemporalTypes.Instant;\n}\n","import { getTemporal } from \"./temporal\";\nimport type { FieldMetadata, TailorFieldType } from \"#/configure/types/field.types\";\nimport type { EffectiveDateDefault } from \"./types\";\nimport type { Temporal } from \"temporal-spec\";\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 return formatDateTime(date).slice(0, 10);\n}\n\nfunction formatDateTime(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 date.toISOString();\n}\n\nfunction formatHoursMinutes(hour: number, minute: number): string {\n return `${String(hour).padStart(2, \"0\")}:${String(minute).padStart(2, \"0\")}`;\n}\n\nfunction formatTime(date: Date): string {\n if (!Number.isFinite(date.getTime())) {\n throw new RangeError(\"Expected a valid Date, but received an invalid Date\");\n }\n return formatHoursMinutes(date.getUTCHours(), date.getUTCMinutes());\n}\n\nfunction formatTemporal(value: Temporal.PlainDate | Temporal.Instant | Temporal.PlainTime): string {\n const Temporal = getTemporal();\n if (value instanceof Temporal.PlainTime) return formatHoursMinutes(value.hour, value.minute);\n const iso = value.toString({ calendarName: \"never\" });\n if (!/^\\d{4}-/.test(iso)) {\n const isInstant = value instanceof Temporal.Instant;\n throw new RangeError(\n `Expected a Temporal.${isInstant ? \"Instant\" : \"PlainDate\"} with a 4-digit ${isInstant ? \"UTC \" : \"\"}year (0000-9999), but received ${iso}`,\n );\n }\n return iso;\n}\n\ntype DateField = {\n readonly type: TailorFieldType;\n readonly metadata: FieldMetadata;\n readonly fields: Record<string, DateField>;\n};\n\ntype DateRepresentationField = {\n readonly type: TailorFieldType;\n readonly metadata: Pick<FieldMetadata, \"as\">;\n readonly fields: Record<string, DateRepresentationField>;\n};\n\nfunction serialize(field: DateField, value: unknown, path: string): unknown {\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\ntype DateRepresentation = \"date\" | \"temporal\";\n\ntype MaybeProcessGlobal = { process?: { env: Record<string, string | undefined> } };\n\n// Read per call rather than at module load: the Vitest setup file sets the gate\n// after the runtime modules are evaluated. Bundles fold it to a literal.\nfunction bundledDateDefault(): EffectiveDateDefault {\n const gate = (globalThis as MaybeProcessGlobal).process?.env\n .__TAILOR_PLATFORM_BUNDLE_DATE_DEFAULT;\n return gate === \"temporal\" || gate === \"date\" ? gate : \"legacy\";\n}\n\n/**\n * Get the Date or Temporal representation a field converts its values to.\n * @param type - Field type\n * @param as - The field's `as` option\n * @param defaultRepresentation - Representation for fields whose `as` is `\"default\"`; read from the bundle when omitted\n * @returns The representation, or undefined when values stay strings\n * @internal\n */\nexport function dateRepresentationOf(\n type: TailorFieldType,\n as: FieldMetadata[\"as\"],\n defaultRepresentation: EffectiveDateDefault = bundledDateDefault(),\n): DateRepresentation | undefined {\n if (type !== \"date\" && type !== \"datetime\" && type !== \"time\") return undefined;\n const resolved = as === \"default\" ? defaultRepresentation : as;\n return resolved === \"date\" || resolved === \"temporal\" ? resolved : undefined;\n}\n\nfunction isDateRepresentationField(\n field: DateRepresentationField,\n representation?: DateRepresentation,\n defaultRepresentation?: EffectiveDateDefault,\n): boolean {\n const as = dateRepresentationOf(field.type, field.metadata.as, defaultRepresentation);\n return as !== undefined && (representation === undefined || as === representation);\n}\n\ntype DateSerializer = (type: TailorFieldType, value: unknown, path: string) => string;\n\nfunction throwInvalidDate(error: unknown, path: string): never {\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\nfunction serializeAsDate(type: TailorFieldType, value: unknown, path: string): string {\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 if (type === \"datetime\") return formatDateTime(value);\n if (type === \"time\") return formatTime(value);\n return formatDate(value);\n } catch (error) {\n return throwInvalidDate(error, path);\n }\n}\n\nfunction serializeAsTemporal(type: TailorFieldType, value: unknown, path: string): string {\n const Temporal = getTemporal();\n const expected =\n type === \"date\"\n ? Temporal.PlainDate\n : type === \"datetime\"\n ? Temporal.Instant\n : Temporal.PlainTime;\n if (!(value instanceof expected)) {\n throw new TypeError(\n `Expected a Temporal.${expected.name} instance at ${describePathTarget(path)}, but received ${describeReceivedValue(value)}`,\n );\n }\n try {\n return formatTemporal(value);\n } catch (error) {\n return throwInvalidDate(error, path);\n }\n}\n\nconst dateSerializers: Record<DateRepresentation, DateSerializer | undefined> = {\n date: (globalThis as MaybeProcessGlobal).process?.env.__TAILOR_PLATFORM_BUNDLE_WITHOUT_DATE\n ? undefined\n : serializeAsDate,\n temporal: (globalThis as MaybeProcessGlobal).process?.env\n .__TAILOR_PLATFORM_BUNDLE_WITHOUT_TEMPORAL\n ? undefined\n : serializeAsTemporal,\n};\n\nfunction serializeValue(field: DateField, value: unknown, path: string): unknown {\n if (value === null || value === undefined) return value;\n const { type } = field;\n const as = dateRepresentationOf(type, field.metadata.as);\n if (as) {\n const serializeAs = dateSerializers[as];\n if (!serializeAs) {\n throw new Error(\n `Serializing fields declared with as: \"${as}\" is not included in this bundle.`,\n );\n }\n return serializeAs(type, value, path);\n }\n if (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 * Check whether a field or any of its nested fields uses a Date or Temporal representation.\n * @param field - Field to inspect\n * @param representation - Only match fields using this representation\n * @param defaultRepresentation - Representation for fields whose `as` is `\"default\"`; read from the bundle when omitted\n * @returns Whether serializeDateFields would convert any value of the field\n * @internal\n */\nexport function hasDateRepresentationFields(\n field: DateRepresentationField,\n representation?: DateRepresentation,\n defaultRepresentation?: EffectiveDateDefault,\n): boolean {\n return (\n isDateRepresentationField(field, representation, defaultRepresentation) ||\n Object.values(field.fields).some((child) =>\n hasDateRepresentationFields(child, representation, defaultRepresentation),\n )\n );\n}\n\n/**\n * Convert fields using Date or Temporal representations to date, datetime, or time strings.\n * @param field - Field defining the value's shape\n * @param value - Value to serialize\n * @returns Value with Date and Temporal 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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"}
1
+ {"version":3,"file":"date-CgP7gMG_.mjs","names":["temporal","Temporal","date","year","received","hour","minute","value","iso","isInstant","field","converted","item","path","index","text","sample","elementTypes","type","gate","resolved","as","defaultRepresentation","representation","reason","error","expected","dateSerializers","serializeAs","record","result","key","child"],"sources":["../src/runtime/temporal.ts","../src/runtime/date.ts"],"sourcesContent":["import type { Temporal as TemporalTypes } from \"temporal-spec\";\n\n/**\n * Get the host Temporal implementation when a value is needed.\n * @returns Host Temporal implementation\n * @internal\n */\nexport function getTemporal(): typeof TemporalTypes {\n const temporal = (globalThis as unknown as { Temporal?: typeof TemporalTypes }).Temporal;\n if (!temporal) {\n throw new ReferenceError(\n \"Temporal is unavailable. Use the Tailor Platform runtime or the SDK tailor-runtime Vitest environment.\",\n );\n }\n return temporal;\n}\n\n/**\n * Typed access to the runtime's Temporal API, without installing a polyfill.\n * Constructors are resolved on access so configuration files can load in Node.js.\n * @example\n * import { Temporal } from \"@tailor-platform/sdk/runtime\";\n * const tomorrow: Temporal.PlainDate = Temporal.Now.plainDateISO().add({ days: 1 });\n */\nexport const Temporal: typeof TemporalTypes = {\n get PlainDate() {\n return getTemporal().PlainDate;\n },\n get PlainTime() {\n return getTemporal().PlainTime;\n },\n get PlainDateTime() {\n return getTemporal().PlainDateTime;\n },\n get PlainYearMonth() {\n return getTemporal().PlainYearMonth;\n },\n get PlainMonthDay() {\n return getTemporal().PlainMonthDay;\n },\n get ZonedDateTime() {\n return getTemporal().ZonedDateTime;\n },\n get Duration() {\n return getTemporal().Duration;\n },\n get Instant() {\n return getTemporal().Instant;\n },\n get Now() {\n return getTemporal().Now;\n },\n};\n\n/** Temporal instance types supplied by the SDK, independent of TypeScript's lib settings. */\n// Type-only merging supports Temporal.PlainDate annotations on the value import.\n// oxlint-disable-next-line typescript/no-namespace\nexport declare namespace Temporal {\n /** The runtime Temporal.PlainDate instance. */\n type PlainDate = TemporalTypes.PlainDate;\n /** The runtime Temporal.PlainTime instance. */\n type PlainTime = TemporalTypes.PlainTime;\n /** The runtime Temporal.PlainDateTime instance. */\n type PlainDateTime = TemporalTypes.PlainDateTime;\n /** The runtime Temporal.PlainYearMonth instance. */\n type PlainYearMonth = TemporalTypes.PlainYearMonth;\n /** The runtime Temporal.PlainMonthDay instance. */\n type PlainMonthDay = TemporalTypes.PlainMonthDay;\n /** The runtime Temporal.ZonedDateTime instance. */\n type ZonedDateTime = TemporalTypes.ZonedDateTime;\n /** The runtime Temporal.Duration instance. */\n type Duration = TemporalTypes.Duration;\n /** The runtime Temporal.Instant instance. */\n type Instant = TemporalTypes.Instant;\n}\n","import { getTemporal } from \"./temporal\";\nimport type { FieldMetadata, TailorFieldType } from \"#/configure/types/field.types\";\nimport type { EffectiveDateDefault } from \"./types\";\nimport type { Temporal } from \"temporal-spec\";\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 return formatDateTime(date).slice(0, 10);\n}\n\nfunction formatDateTime(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 date.toISOString();\n}\n\nfunction formatHoursMinutes(hour: number, minute: number): string {\n return `${String(hour).padStart(2, \"0\")}:${String(minute).padStart(2, \"0\")}`;\n}\n\nfunction formatTime(date: Date): string {\n if (!Number.isFinite(date.getTime())) {\n throw new RangeError(\"Expected a valid Date, but received an invalid Date\");\n }\n return formatHoursMinutes(date.getUTCHours(), date.getUTCMinutes());\n}\n\nfunction formatTemporal(value: Temporal.PlainDate | Temporal.Instant | Temporal.PlainTime): string {\n const Temporal = getTemporal();\n if (value instanceof Temporal.PlainTime) return formatHoursMinutes(value.hour, value.minute);\n const iso = value.toString({ calendarName: \"never\" });\n if (!/^\\d{4}-/.test(iso)) {\n const isInstant = value instanceof Temporal.Instant;\n throw new RangeError(\n `Expected a Temporal.${isInstant ? \"Instant\" : \"PlainDate\"} with a 4-digit ${isInstant ? \"UTC \" : \"\"}year (0000-9999), but received ${iso}`,\n );\n }\n return iso;\n}\n\ntype DateField = {\n readonly type: TailorFieldType;\n readonly metadata: FieldMetadata;\n readonly fields: Record<string, DateField>;\n};\n\ntype DateRepresentationField = {\n readonly type: TailorFieldType;\n readonly metadata: Pick<FieldMetadata, \"as\">;\n readonly fields: Record<string, DateRepresentationField>;\n};\n\nfunction serialize(field: DateField, value: unknown, path: string): unknown {\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\ntype DateRepresentation = \"date\" | \"temporal\";\n\ntype MaybeProcessGlobal = { process?: { env: Record<string, string | undefined> } };\n\n// Read per call rather than at module load: the Vitest setup file sets the gate\n// after the runtime modules are evaluated. Bundles fold it to a literal.\nfunction bundledDateDefault(): EffectiveDateDefault {\n const gate = (globalThis as MaybeProcessGlobal).process?.env\n .__TAILOR_PLATFORM_BUNDLE_DATE_DEFAULT;\n return gate === \"temporal\" || gate === \"date\" ? gate : \"legacy\";\n}\n\n/**\n * Get the Date or Temporal representation a field converts its values to.\n * @param type - Field type\n * @param as - The field's `as` option\n * @param defaultRepresentation - Representation for fields whose `as` is `\"default\"`; read from the bundle when omitted\n * @returns The representation, or undefined when values stay strings\n * @internal\n */\nexport function dateRepresentationOf(\n type: TailorFieldType,\n as: FieldMetadata[\"as\"],\n defaultRepresentation: EffectiveDateDefault = bundledDateDefault(),\n): DateRepresentation | undefined {\n if (type !== \"date\" && type !== \"datetime\" && type !== \"time\") return undefined;\n const resolved = as === \"default\" ? defaultRepresentation : as;\n return resolved === \"date\" || resolved === \"temporal\" ? resolved : undefined;\n}\n\nfunction isDateRepresentationField(\n field: DateRepresentationField,\n representation?: DateRepresentation,\n defaultRepresentation?: EffectiveDateDefault,\n): boolean {\n const as = dateRepresentationOf(field.type, field.metadata.as, defaultRepresentation);\n return as !== undefined && (representation === undefined || as === representation);\n}\n\ntype DateSerializer = (type: TailorFieldType, value: unknown, path: string) => string;\n\nfunction throwInvalidDate(error: unknown, path: string): never {\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\nfunction serializeAsDate(type: TailorFieldType, value: unknown, path: string): string {\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 if (type === \"datetime\") return formatDateTime(value);\n if (type === \"time\") return formatTime(value);\n return formatDate(value);\n } catch (error) {\n return throwInvalidDate(error, path);\n }\n}\n\nfunction serializeAsTemporal(type: TailorFieldType, value: unknown, path: string): string {\n const Temporal = getTemporal();\n const expected =\n type === \"date\"\n ? Temporal.PlainDate\n : type === \"datetime\"\n ? Temporal.Instant\n : Temporal.PlainTime;\n if (!(value instanceof expected)) {\n throw new TypeError(\n `Expected a Temporal.${expected.name} instance at ${describePathTarget(path)}, but received ${describeReceivedValue(value)}`,\n );\n }\n try {\n return formatTemporal(value);\n } catch (error) {\n return throwInvalidDate(error, path);\n }\n}\n\nconst dateSerializers: Record<DateRepresentation, DateSerializer | undefined> = {\n date: (globalThis as MaybeProcessGlobal).process?.env.__TAILOR_PLATFORM_BUNDLE_WITHOUT_DATE\n ? undefined\n : serializeAsDate,\n temporal: (globalThis as MaybeProcessGlobal).process?.env\n .__TAILOR_PLATFORM_BUNDLE_WITHOUT_TEMPORAL\n ? undefined\n : serializeAsTemporal,\n};\n\nfunction serializeValue(field: DateField, value: unknown, path: string): unknown {\n if (value === null || value === undefined) return value;\n const { type } = field;\n const as = dateRepresentationOf(type, field.metadata.as);\n if (as) {\n const serializeAs = dateSerializers[as];\n if (!serializeAs) {\n throw new Error(\n `Serializing fields declared with as: \"${as}\" is not included in this bundle.`,\n );\n }\n return serializeAs(type, value, path);\n }\n if (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 * Check whether a field or any of its nested fields uses a Date or Temporal representation.\n * @param field - Field to inspect\n * @param representation - Only match fields using this representation\n * @param defaultRepresentation - Representation for fields whose `as` is `\"default\"`; read from the bundle when omitted\n * @returns Whether serializeDateFields would convert any value of the field\n * @internal\n */\nexport function hasDateRepresentationFields(\n field: DateRepresentationField,\n representation?: DateRepresentation,\n defaultRepresentation?: EffectiveDateDefault,\n): boolean {\n return (\n isDateRepresentationField(field, representation, defaultRepresentation) ||\n Object.values(field.fields).some((child) =>\n hasDateRepresentationFields(child, representation, defaultRepresentation),\n )\n );\n}\n\n/**\n * Convert fields using Date or Temporal representations to date, datetime, or time strings.\n * @param field - Field defining the value's shape\n * @param value - Value to serialize\n * @returns Value with Date and Temporal 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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"date-representation-Cr7W7Pn2.mjs","names":["assertDefined","facade","dispose","g","SECRET_STORE","root","prev","holder","vault","secrets","options","name","vaultData","result","names","getSecret","vi","getSecrets","value","i","args","a","b","DATE_DEFAULT_GATE","previous","dateDefault","representation"],"sources":["../src/vitest/mocks/shared.ts","../src/vitest/mocks/secretmanager.ts","../src/vitest/date-representation.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\n// Attach a non-enumerable `Symbol.dispose` to a facade so it works with `using`.\nexport function withDispose<T extends object>(facade: T, dispose: () => void): T & Disposable {\n Object.defineProperty(facade, Symbol.dispose, {\n value: dispose,\n enumerable: false,\n writable: true,\n configurable: true,\n });\n return facade as T & Disposable;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function tailorRoot(): Record<string, any> {\n const g = globalThis as Record<string, unknown>;\n if (!g.tailor) {\n // Ensure the container (and the always-present context stub) exists even if\n // the base globals were not installed (e.g. a unit test that only acquires\n // a single mock without the tailor-runtime environment).\n g.tailor = { context: { getInvoker: () => null } };\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return g.tailor as Record<string, any>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function tailordbRoot(): Record<string, any> {\n const g = globalThis as Record<string, unknown>;\n if (!g.tailordb) {\n g.tailordb = {};\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return g.tailordb as Record<string, any>;\n}\n","import { vi } from \"vitest\";\nimport { assertDefined } from \"#/utils/assert\";\nimport { tailorRoot, withDispose } from \"./shared\";\n\ninterface SecretCall {\n method: \"getSecret\" | \"getSecrets\";\n vault: string;\n name?: string;\n names?: readonly string[];\n}\n\n/** Initial fixtures for a Secret Manager mock. */\nexport interface MockSecretmanagerOptions {\n /** Secrets to merge over fixtures inherited from the currently installed mock. */\n secrets?: Record<string, Record<string, string>>;\n}\n\n// ---------------------------------------------------------------------------\n// SecretManager Mock\n// ---------------------------------------------------------------------------\n\n// Hidden accessor key used to inherit the previous scope's secret store on\n// acquisition (so secrets seeded once outside tests — e.g. from tailor.config.ts\n// via setup.ts — remain visible) while still isolating per-test overrides.\nconst SECRET_STORE = Symbol(\"tailorSecretStore\");\n\n/**\n * Acquire a disposable mock for `tailor.secretmanager`. The secret store is\n * inherited (cloned) from the currently-installed mock on acquisition and\n * restored on dispose, so secrets seeded outside the test survive across\n * `using` scopes while per-test `setSecrets()` overrides stay isolated.\n * @param options - Initial Secret Manager fixtures\n * @returns Disposable SecretManager mock control object\n * @example\n * ```typescript\n * import { mockSecretmanager } from \"@tailor-platform/sdk/vitest\";\n *\n * test(\"reads secrets from vault\", async () => {\n * using sm = mockSecretmanager({ secrets: { \"my-vault\": { API_KEY: \"sk-123\" } } });\n * sm.setSecret(\"my-vault\", \"API_KEY\", \"replacement\");\n * // …\n * });\n * ```\n */\nexport function mockSecretmanager(options: MockSecretmanagerOptions = {}) {\n const root = tailorRoot();\n const prev = root.secretmanager;\n\n const holder: { store: Record<string, Record<string, string>> } = {\n // prior mock state may be absent\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n store: structuredClone((prev?.[SECRET_STORE]?.store as typeof holder.store) ?? {}),\n };\n for (const [vault, secrets] of Object.entries(options.secrets ?? {})) {\n holder.store[vault] = { ...holder.store[vault], ...secrets };\n }\n\n async function defaultGetSecret(vault: string, name: string): Promise<string | undefined> {\n return holder.store[vault]?.[name];\n }\n\n async function defaultGetSecrets<const T extends readonly string[]>(\n vault: string,\n names: T,\n ): Promise<Partial<Record<T[number], string>>> {\n const vaultData = holder.store[vault] ?? {};\n const result: Record<string, string> = {};\n for (const name of names) {\n if (name in vaultData) {\n result[name] = assertDefined(vaultData[name], `vault entry missing for: ${name}`);\n }\n }\n return result as Partial<Record<T[number], string>>;\n }\n\n const getSecret = vi.fn(defaultGetSecret);\n const getSecrets = vi.fn(defaultGetSecrets);\n\n root.secretmanager = { getSecret, getSecrets, [SECRET_STORE]: holder };\n\n const facade = {\n /** The `getSecret` `vi.fn`. */\n getSecret,\n /** The `getSecrets` `vi.fn`. */\n getSecrets,\n\n setSecrets(secrets: Record<string, Record<string, string>>): void {\n holder.store = secrets;\n },\n\n setSecret(vault: string, name: string, value: string): void {\n holder.store = {\n ...holder.store,\n [vault]: { ...holder.store[vault], [name]: value },\n };\n },\n\n mergeSecrets(vault: string, secrets: Record<string, string>): void {\n holder.store = {\n ...holder.store,\n [vault]: { ...holder.store[vault], ...secrets },\n };\n },\n\n get calls(): SecretCall[] {\n // Merge both methods' calls back into chronological order via vi.fn's\n // global invocationCallOrder, so a test mixing getSecret/getSecrets sees\n // them in the order they actually ran (not all getSecret, then all getSecrets).\n const entries: { order: number; call: SecretCall }[] = [\n ...getSecret.mock.calls.map((args, i) => ({\n order: getSecret.mock.invocationCallOrder[i] ?? 0,\n call: { method: \"getSecret\" as const, vault: args[0], name: args[1] },\n })),\n ...getSecrets.mock.calls.map((args, i) => ({\n order: getSecrets.mock.invocationCallOrder[i] ?? 0,\n call: {\n method: \"getSecrets\" as const,\n vault: args[0],\n names: args[1],\n },\n })),\n ];\n return entries.toSorted((a, b) => a.order - b.order).map((e) => e.call);\n },\n\n clear(): void {\n getSecret.mockClear();\n getSecrets.mockClear();\n },\n\n reset(): void {\n holder.store = {};\n getSecret.mockReset();\n getSecret.mockImplementation(defaultGetSecret);\n getSecrets.mockReset();\n getSecrets.mockImplementation(defaultGetSecrets);\n },\n };\n\n return withDispose(facade, () => {\n root.secretmanager = prev;\n });\n}\n","import type { DateRepresentationOption } from \"#/configure/types/field.types\";\nimport type { EffectiveDateDefault } from \"#/runtime/types\";\n\nconst DATE_DEFAULT_GATE = \"__TAILOR_PLATFORM_BUNDLE_DATE_DEFAULT\";\n\n/**\n * Expose the date default to `t` date fields in this worker, the way the\n * define plugin folds it into deployed bundles.\n * @param dateDefault - The representation `t` date fields without `as` follow\n * @returns A function that restores the previous value\n */\nexport function applyDateDefault(dateDefault: EffectiveDateDefault): () => void {\n const previous = process.env[DATE_DEFAULT_GATE];\n if (dateDefault === \"legacy\") {\n delete process.env[DATE_DEFAULT_GATE];\n } else {\n process.env[DATE_DEFAULT_GATE] = dateDefault;\n }\n return () => {\n if (previous === undefined) delete process.env[DATE_DEFAULT_GATE];\n else process.env[DATE_DEFAULT_GATE] = previous;\n };\n}\n\n/**\n * Give `t.date()`, `t.datetime()`, and `t.time()` fields that omit `as` the\n * given representation for the rest of this test worker, as if the code under\n * test were bundled under that `defaultDateRepresentation`; `\"string\"` is the\n * representation of a project that leaves the setting unset. Generated\n * migration tests call it with the representation recorded in the migration's\n * `diff.json`, so a later change to `tailor.config.ts` does not alter what the\n * test exercises.\n * @param representation - The representation to apply, as `as` names it\n * @returns A function that restores the previous representation\n */\nexport function applyDateRepresentation(representation: DateRepresentationOption): () => void {\n return applyDateDefault(representation === \"string\" ? \"legacy\" : representation);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"date-representation-Cr7W7Pn2.mjs","names":["assertDefined","facade","dispose","g","SECRET_STORE","root","prev","holder","vault","secrets","options","name","vaultData","result","names","getSecret","vi","getSecrets","value","i","args","a","b","DATE_DEFAULT_GATE","previous","dateDefault","representation"],"sources":["../src/vitest/mocks/shared.ts","../src/vitest/mocks/secretmanager.ts","../src/vitest/date-representation.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\n// Attach a non-enumerable `Symbol.dispose` to a facade so it works with `using`.\nexport function withDispose<T extends object>(facade: T, dispose: () => void): T & Disposable {\n Object.defineProperty(facade, Symbol.dispose, {\n value: dispose,\n enumerable: false,\n writable: true,\n configurable: true,\n });\n return facade as T & Disposable;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function tailorRoot(): Record<string, any> {\n const g = globalThis as Record<string, unknown>;\n if (!g.tailor) {\n // Ensure the container (and the always-present context stub) exists even if\n // the base globals were not installed (e.g. a unit test that only acquires\n // a single mock without the tailor-runtime environment).\n g.tailor = { context: { getInvoker: () => null } };\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return g.tailor as Record<string, any>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function tailordbRoot(): Record<string, any> {\n const g = globalThis as Record<string, unknown>;\n if (!g.tailordb) {\n g.tailordb = {};\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return g.tailordb as Record<string, any>;\n}\n","import { vi } from \"vitest\";\nimport { assertDefined } from \"#/utils/assert\";\nimport { tailorRoot, withDispose } from \"./shared\";\n\ninterface SecretCall {\n method: \"getSecret\" | \"getSecrets\";\n vault: string;\n name?: string;\n names?: readonly string[];\n}\n\n/** Initial fixtures for a Secret Manager mock. */\nexport interface MockSecretmanagerOptions {\n /** Secrets to merge over fixtures inherited from the currently installed mock. */\n secrets?: Record<string, Record<string, string>>;\n}\n\n// ---------------------------------------------------------------------------\n// SecretManager Mock\n// ---------------------------------------------------------------------------\n\n// Hidden accessor key used to inherit the previous scope's secret store on\n// acquisition (so secrets seeded once outside tests — e.g. from tailor.config.ts\n// via setup.ts — remain visible) while still isolating per-test overrides.\nconst SECRET_STORE = Symbol(\"tailorSecretStore\");\n\n/**\n * Acquire a disposable mock for `tailor.secretmanager`. The secret store is\n * inherited (cloned) from the currently-installed mock on acquisition and\n * restored on dispose, so secrets seeded outside the test survive across\n * `using` scopes while per-test `setSecrets()` overrides stay isolated.\n * @param options - Initial Secret Manager fixtures\n * @returns Disposable SecretManager mock control object\n * @example\n * ```typescript\n * import { mockSecretmanager } from \"@tailor-platform/sdk/vitest\";\n *\n * test(\"reads secrets from vault\", async () => {\n * using sm = mockSecretmanager({ secrets: { \"my-vault\": { API_KEY: \"sk-123\" } } });\n * sm.setSecret(\"my-vault\", \"API_KEY\", \"replacement\");\n * // …\n * });\n * ```\n */\nexport function mockSecretmanager(options: MockSecretmanagerOptions = {}) {\n const root = tailorRoot();\n const prev = root.secretmanager;\n\n const holder: { store: Record<string, Record<string, string>> } = {\n // prior mock state may be absent\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n store: structuredClone((prev?.[SECRET_STORE]?.store as typeof holder.store) ?? {}),\n };\n for (const [vault, secrets] of Object.entries(options.secrets ?? {})) {\n holder.store[vault] = { ...holder.store[vault], ...secrets };\n }\n\n async function defaultGetSecret(vault: string, name: string): Promise<string | undefined> {\n return holder.store[vault]?.[name];\n }\n\n async function defaultGetSecrets<const T extends readonly string[]>(\n vault: string,\n names: T,\n ): Promise<Partial<Record<T[number], string>>> {\n const vaultData = holder.store[vault] ?? {};\n const result: Record<string, string> = {};\n for (const name of names) {\n if (name in vaultData) {\n result[name] = assertDefined(vaultData[name], `vault entry missing for: ${name}`);\n }\n }\n return result as Partial<Record<T[number], string>>;\n }\n\n const getSecret = vi.fn(defaultGetSecret);\n const getSecrets = vi.fn(defaultGetSecrets);\n\n root.secretmanager = { getSecret, getSecrets, [SECRET_STORE]: holder };\n\n const facade = {\n /** The `getSecret` `vi.fn`. */\n getSecret,\n /** The `getSecrets` `vi.fn`. */\n getSecrets,\n\n setSecrets(secrets: Record<string, Record<string, string>>): void {\n holder.store = secrets;\n },\n\n setSecret(vault: string, name: string, value: string): void {\n holder.store = {\n ...holder.store,\n [vault]: { ...holder.store[vault], [name]: value },\n };\n },\n\n mergeSecrets(vault: string, secrets: Record<string, string>): void {\n holder.store = {\n ...holder.store,\n [vault]: { ...holder.store[vault], ...secrets },\n };\n },\n\n get calls(): SecretCall[] {\n // Merge both methods' calls back into chronological order via vi.fn's\n // global invocationCallOrder, so a test mixing getSecret/getSecrets sees\n // them in the order they actually ran (not all getSecret, then all getSecrets).\n const entries: { order: number; call: SecretCall }[] = [\n ...getSecret.mock.calls.map((args, i) => ({\n order: getSecret.mock.invocationCallOrder[i] ?? 0,\n call: { method: \"getSecret\" as const, vault: args[0], name: args[1] },\n })),\n ...getSecrets.mock.calls.map((args, i) => ({\n order: getSecrets.mock.invocationCallOrder[i] ?? 0,\n call: {\n method: \"getSecrets\" as const,\n vault: args[0],\n names: args[1],\n },\n })),\n ];\n return entries.toSorted((a, b) => a.order - b.order).map((e) => e.call);\n },\n\n clear(): void {\n getSecret.mockClear();\n getSecrets.mockClear();\n },\n\n reset(): void {\n holder.store = {};\n getSecret.mockReset();\n getSecret.mockImplementation(defaultGetSecret);\n getSecrets.mockReset();\n getSecrets.mockImplementation(defaultGetSecrets);\n },\n };\n\n return withDispose(facade, () => {\n root.secretmanager = prev;\n });\n}\n","import type { DateRepresentationOption } from \"#/configure/types/field.types\";\nimport type { EffectiveDateDefault } from \"#/runtime/types\";\n\nconst DATE_DEFAULT_GATE = \"__TAILOR_PLATFORM_BUNDLE_DATE_DEFAULT\";\n\n/**\n * Expose the date default to `t` date fields in this worker, the way the\n * define plugin folds it into deployed bundles.\n * @param dateDefault - The representation `t` date fields without `as` follow\n * @returns A function that restores the previous value\n */\nexport function applyDateDefault(dateDefault: EffectiveDateDefault): () => void {\n const previous = process.env[DATE_DEFAULT_GATE];\n if (dateDefault === \"legacy\") {\n delete process.env[DATE_DEFAULT_GATE];\n } else {\n process.env[DATE_DEFAULT_GATE] = dateDefault;\n }\n return () => {\n if (previous === undefined) delete process.env[DATE_DEFAULT_GATE];\n else process.env[DATE_DEFAULT_GATE] = previous;\n };\n}\n\n/**\n * Give `t.date()`, `t.datetime()`, and `t.time()` fields that omit `as` the\n * given representation for the rest of this test worker, as if the code under\n * test were bundled under that `defaultDateRepresentation`; `\"string\"` is the\n * representation of a project that leaves the setting unset. Generated\n * migration tests call it with the representation recorded in the migration's\n * `diff.json`, so a later change to `tailor.config.ts` does not alter what the\n * test exercises.\n * @param representation - The representation to apply, as `as` names it\n * @returns A function that restores the previous representation\n */\nexport function applyDateRepresentation(representation: DateRepresentationOption): () => void {\n return applyDateDefault(representation === \"string\" ? \"legacy\" : representation);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"errors-BOF68LJ5.mjs","names":["redactSecrets","styles","POSIX_BARE_ARG","CMD_BARE_ARG","WINDOWS_DOUBLE_QUOTE_UNSAFE","POWERSHELL_VERSION_SENSITIVE","value","quoted","backslashes","char","argv","cmd","commandLines","action","arg","text","parts","error","options","message","MISSING_NAMED_EXPORT_PATTERN","name"],"sources":["../src/cli/shared/shell-quote.ts","../src/cli/shared/errors.ts"],"sourcesContent":["/**\n * Command lines for a suggested command: one line when every supported shell reads it the same\n * way, otherwise one line per Windows shell.\n */\nexport type ShellCommandLines =\n | { kind: \"shared\"; commandLine: string }\n | { kind: \"perShell\"; powershell: string; cmd: string };\n\nconst POSIX_BARE_ARG = /^[A-Za-z0-9_./:=@+-]+$/;\nconst CMD_BARE_ARG = /^[A-Za-z0-9_./:=@+\\\\-]+$/;\n// PowerShell expands `$` and backtick escapes inside double quotes and reads `“ ” „` as `\"`,\n// cmd.exe expands `%` even inside quotes, a `\"` or a trailing backslash changes where the\n// quoted argument ends, and a leading `~` is version-sensitive (below).\nconst WINDOWS_DOUBLE_QUOTE_UNSAFE = /[%$\"`“”„\\p{Cc}]|\\\\$|^~(?:[\\\\/]|$)/u;\n// PowerShell versions pass these to programs differently: Windows PowerShell 5.1 and 7.3+\n// disagree on quotes and trailing backslashes, and 7.6+ expands a leading `~` inside the `.ps1`\n// shim however it was quoted.\nconst POWERSHELL_VERSION_SENSITIVE = /\"|\\s.*\\\\$|^~(?:[\\\\/]|$)/;\n\nfunction quotePosixArg(value: string): string {\n if (POSIX_BARE_ARG.test(value)) return value;\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n\nfunction isPowerShellBare(value: string): boolean {\n // A leading `@` splats, a lone `--` ends parameters, a leading digit reads as a number, and a\n // single-dash token with `.` or `:` reads as parameter syntax.\n return (\n CMD_BARE_ARG.test(value) &&\n !/^(@|[+-]?\\.?\\d)/.test(value) &&\n value !== \"--\" &&\n !/^-[^-].*[.:]/.test(value)\n );\n}\n\nfunction quotePowerShellArg(value: string): string {\n if (isPowerShellBare(value)) return value;\n return `'${value.replace(/['‘’‚‛]/g, \"$&$&\")}'`;\n}\n\nfunction quoteCmdArg(value: string): string {\n // Quoting a trailing backslash keeps a command routed through `cmd /c` from ending in one,\n // which PowerShell 7 doubles when it quotes the line.\n if (CMD_BARE_ARG.test(value) && !value.endsWith(\"\\\\\")) return value;\n let quoted = '\"';\n let backslashes = 0;\n for (const char of value) {\n if (char === \"\\\\\") {\n backslashes++;\n continue;\n }\n if (char === '\"') {\n quoted += `${\"\\\\\".repeat(backslashes * 2)}\"\"`;\n } else {\n // `%%cd:~,%` reads back as a literal `%` without cmd.exe expanding a `%NAME%` around it.\n quoted += `${\"\\\\\".repeat(backslashes)}${char === \"%\" ? \"%%cd:~,%\" : char}`;\n }\n backslashes = 0;\n }\n return `${quoted}${\"\\\\\".repeat(backslashes * 2)}\"`;\n}\n\nfunction formatWindowsCommandLines(argv: readonly string[]): ShellCommandLines {\n if (\n argv.every(\n (value) =>\n isPowerShellBare(value) || (value !== \"\" && !WINDOWS_DOUBLE_QUOTE_UNSAFE.test(value)),\n )\n ) {\n return {\n kind: \"shared\",\n commandLine: argv.map((value) => (isPowerShellBare(value) ? value : `\"${value}\"`)).join(\" \"),\n };\n }\n const cmd = argv.map(quoteCmdArg).join(\" \");\n // Both PowerShell versions pass a `cmd` argument the same legacy way, so routing the cmd.exe\n // line through `cmd /c` keeps the arguments identical where their native quoting differs.\n const powershell = argv.some((value) => value === \"\" || POWERSHELL_VERSION_SENSITIVE.test(value))\n ? `cmd /d /s /c ${quotePowerShellArg(cmd)}`\n : argv.map(quotePowerShellArg).join(\" \");\n return { kind: \"perShell\", powershell, cmd };\n}\n\n/**\n * Render an argv array as command lines that the current platform's shells run with every\n * argument delivered unchanged\n * @param {readonly string[]} argv - Executable name followed by its arguments\n * @returns {ShellCommandLines} One shared command line, or one per Windows shell\n */\nexport function formatShellCommandLines(argv: readonly string[]): ShellCommandLines {\n if (process.platform === \"win32\") return formatWindowsCommandLines(argv);\n return { kind: \"shared\", commandLine: argv.map(quotePosixArg).join(\" \") };\n}\n","import { redactSecrets, styles } from \"./logger\";\nimport { formatShellCommandLines } from \"./shell-quote\";\nimport type { Jsonifiable } from \"type-fest\";\n\n/**\n * Options for creating a CLI error\n */\ninterface CLIErrorOptions {\n /** Stable machine-readable failure code, such as `WORKSPACE_NOT_FOUND`. */\n code: string;\n message: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n cause?: unknown;\n}\n\nexport type CLIErrorNextAction = {\n /** Executable name, such as `tailor`. */\n command: string;\n /** Arguments passed directly to the executable. */\n args: readonly string[];\n};\n\n/**\n * CLI error interface with formatted output\n */\nexport interface CLIError extends Error {\n readonly code: string;\n readonly details?: string;\n readonly suggestion?: string;\n readonly command?: string;\n readonly next?: CLIErrorNextAction;\n readonly context?: Readonly<Record<string, Jsonifiable | undefined>>;\n format(): string;\n}\n\ntype CLIErrorInternal = Error & {\n code: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n format(): string;\n};\n\n/**\n * Render a command as a user-facing hint that can be copied into the current platform's shells:\n * one backticked command line, or on Windows, when no single quoting suits both shells, a\n * PowerShell and a cmd.exe line (`` `…` in PowerShell or `…` in cmd.exe ``).\n * Registered secrets are redacted before quoting, which could otherwise split them.\n * @param {CLIErrorNextAction} action - Executable and arguments to suggest\n * @returns {string} The command part of the hint, to follow a verb such as \"Run\"\n */\nexport function formatCommandHint(action: CLIErrorNextAction): string {\n const commandLines = formatShellCommandLines(\n [action.command, ...action.args].map((arg) => redactSecrets(arg)),\n );\n if (commandLines.kind === \"shared\") {\n return `\\`${commandLines.commandLine}\\``;\n }\n return `\\`${commandLines.powershell}\\` in PowerShell or \\`${commandLines.cmd}\\` in cmd.exe`;\n}\n\n/**\n * Redact registered secrets, then indent every line after the first.\n * Redacting first keeps the indent from splitting a multi-line secret.\n * @param text - Details or suggestion text\n * @returns Redacted text with its continuation lines indented\n */\nfunction redactAndIndent(text: string): string {\n return redactSecrets(text).split(\"\\n\").join(\"\\n \");\n}\n\n/**\n * Format CLI error for output\n * @param error - CLIError instance to format\n * @returns Formatted error message\n */\nfunction formatError(error: CLIError): string {\n const parts: string[] = [\n styles.error(`Error${error.code ? ` [${error.code}]` : \"\"}: ${error.message}`),\n ];\n\n if (error.details) {\n parts.push(`\\n ${styles.dim(\"Details:\")} ${redactAndIndent(error.details)}`);\n }\n\n if (error.suggestion) {\n parts.push(`\\n ${styles.info(\"Suggestion:\")} ${redactAndIndent(error.suggestion)}`);\n }\n\n if (error.command) {\n parts.push(\n `\\n ${styles.dim(\"Help:\")} Run \\`tailor ${error.command} --help\\` for usage information.`,\n );\n }\n\n if (error.next) {\n parts.push(`\\n ${styles.info(\"Next:\")} Run ${formatCommandHint(error.next)}.`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Create a CLI error with formatted output\n * @param options - Options to construct a CLIError\n * @returns Constructed CLIError instance\n */\nexport function CLIError(options: CLIErrorOptions): CLIError {\n const error = new Error(\n options.message,\n options.cause === undefined ? undefined : { cause: options.cause },\n ) as CLIErrorInternal;\n error.name = \"CLIError\";\n error.code = options.code;\n error.details = options.details;\n error.suggestion = options.suggestion;\n error.command = options.command;\n error.next = options.next;\n error.context = options.context;\n error.format = () => formatError(error);\n return error;\n}\n\n/**\n * Type guard to check if an error is a CLIError\n * @param error - Error to check\n * @returns True if the error is a CLIError\n */\nexport function isCLIError(error: unknown): error is CLIError {\n return error instanceof Error && error.name === \"CLIError\";\n}\n\n/**\n * Summarize a failure as plain text for reports that carry a single error string.\n * CLI errors contribute their details and suggestion so the remediation survives.\n * @param error - Failure to summarize\n * @returns Message followed by any details and suggestion, one per line\n */\nexport function errorSummary(error: unknown): string {\n if (isCLIError(error)) {\n return [error.message, error.details, error.suggestion].filter(Boolean).join(\"\\n\");\n }\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Create an error for an SDK invariant violation that no user action can fix.\n * The result is a plain Error, so JSON output reports it as `UNEXPECTED_ERROR`\n * and crash reporting treats it like any other unexpected failure.\n * @param message - Description of the violated invariant\n * @param options - Standard Error options, such as `cause`\n * @returns Plain Error instance\n */\nexport function internalError(message: string, options?: ErrorOptions): Error {\n return new Error(message, options);\n}\n\n/**\n * Convert a caught value into an Error, keeping Error instances as-is\n * @param value - Caught value\n * @returns The value itself when it is an Error, otherwise an Error of its string form with the value as `cause`\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value), { cause: value });\n}\n\nconst MISSING_NAMED_EXPORT_PATTERN = /does not provide an export named '(?!default')([^']+)'/;\n\n/**\n * Suggest `import type` when importing user code fails on a missing named\n * export. The CLI strips types from each file in isolation, so a type-only\n * export does not exist at runtime and a plain import of it fails to link.\n * @param error - Error thrown while importing user modules\n * @returns Suggestion text, or undefined when the error is not that failure\n */\nexport function typeOnlyImportHint(error: unknown): string | undefined {\n if (!(error instanceof SyntaxError)) return undefined;\n const name = MISSING_NAMED_EXPORT_PATTERN.exec(error.message)?.[1];\n if (!name) return undefined;\n return (\n `If '${name}' is a type, import it with \\`import type\\` (or the inline \\`type\\` modifier). ` +\n \"The CLI runs TypeScript by stripping types from each file in isolation, so type-only exports do not exist at runtime. \" +\n 'Set \"verbatimModuleSyntax\": true in tsconfig.json to catch this at typecheck.'\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"errors-BOF68LJ5.mjs","names":["redactSecrets","styles","POSIX_BARE_ARG","CMD_BARE_ARG","WINDOWS_DOUBLE_QUOTE_UNSAFE","POWERSHELL_VERSION_SENSITIVE","value","quoted","backslashes","char","argv","cmd","commandLines","action","arg","text","parts","error","options","message","MISSING_NAMED_EXPORT_PATTERN","name"],"sources":["../src/cli/shared/shell-quote.ts","../src/cli/shared/errors.ts"],"sourcesContent":["/**\n * Command lines for a suggested command: one line when every supported shell reads it the same\n * way, otherwise one line per Windows shell.\n */\nexport type ShellCommandLines =\n | { kind: \"shared\"; commandLine: string }\n | { kind: \"perShell\"; powershell: string; cmd: string };\n\nconst POSIX_BARE_ARG = /^[A-Za-z0-9_./:=@+-]+$/;\nconst CMD_BARE_ARG = /^[A-Za-z0-9_./:=@+\\\\-]+$/;\n// PowerShell expands `$` and backtick escapes inside double quotes and reads `“ ” „` as `\"`,\n// cmd.exe expands `%` even inside quotes, a `\"` or a trailing backslash changes where the\n// quoted argument ends, and a leading `~` is version-sensitive (below).\nconst WINDOWS_DOUBLE_QUOTE_UNSAFE = /[%$\"`“”„\\p{Cc}]|\\\\$|^~(?:[\\\\/]|$)/u;\n// PowerShell versions pass these to programs differently: Windows PowerShell 5.1 and 7.3+\n// disagree on quotes and trailing backslashes, and 7.6+ expands a leading `~` inside the `.ps1`\n// shim however it was quoted.\nconst POWERSHELL_VERSION_SENSITIVE = /\"|\\s.*\\\\$|^~(?:[\\\\/]|$)/;\n\nfunction quotePosixArg(value: string): string {\n if (POSIX_BARE_ARG.test(value)) return value;\n return `'${value.replaceAll(\"'\", `'\"'\"'`)}'`;\n}\n\nfunction isPowerShellBare(value: string): boolean {\n // A leading `@` splats, a lone `--` ends parameters, a leading digit reads as a number, and a\n // single-dash token with `.` or `:` reads as parameter syntax.\n return (\n CMD_BARE_ARG.test(value) &&\n !/^(@|[+-]?\\.?\\d)/.test(value) &&\n value !== \"--\" &&\n !/^-[^-].*[.:]/.test(value)\n );\n}\n\nfunction quotePowerShellArg(value: string): string {\n if (isPowerShellBare(value)) return value;\n return `'${value.replace(/['‘’‚‛]/g, \"$&$&\")}'`;\n}\n\nfunction quoteCmdArg(value: string): string {\n // Quoting a trailing backslash keeps a command routed through `cmd /c` from ending in one,\n // which PowerShell 7 doubles when it quotes the line.\n if (CMD_BARE_ARG.test(value) && !value.endsWith(\"\\\\\")) return value;\n let quoted = '\"';\n let backslashes = 0;\n for (const char of value) {\n if (char === \"\\\\\") {\n backslashes++;\n continue;\n }\n if (char === '\"') {\n quoted += `${\"\\\\\".repeat(backslashes * 2)}\"\"`;\n } else {\n // `%%cd:~,%` reads back as a literal `%` without cmd.exe expanding a `%NAME%` around it.\n quoted += `${\"\\\\\".repeat(backslashes)}${char === \"%\" ? \"%%cd:~,%\" : char}`;\n }\n backslashes = 0;\n }\n return `${quoted}${\"\\\\\".repeat(backslashes * 2)}\"`;\n}\n\nfunction formatWindowsCommandLines(argv: readonly string[]): ShellCommandLines {\n if (\n argv.every(\n (value) =>\n isPowerShellBare(value) || (value !== \"\" && !WINDOWS_DOUBLE_QUOTE_UNSAFE.test(value)),\n )\n ) {\n return {\n kind: \"shared\",\n commandLine: argv.map((value) => (isPowerShellBare(value) ? value : `\"${value}\"`)).join(\" \"),\n };\n }\n const cmd = argv.map(quoteCmdArg).join(\" \");\n // Both PowerShell versions pass a `cmd` argument the same legacy way, so routing the cmd.exe\n // line through `cmd /c` keeps the arguments identical where their native quoting differs.\n const powershell = argv.some((value) => value === \"\" || POWERSHELL_VERSION_SENSITIVE.test(value))\n ? `cmd /d /s /c ${quotePowerShellArg(cmd)}`\n : argv.map(quotePowerShellArg).join(\" \");\n return { kind: \"perShell\", powershell, cmd };\n}\n\n/**\n * Render an argv array as command lines that the current platform's shells run with every\n * argument delivered unchanged\n * @param {readonly string[]} argv - Executable name followed by its arguments\n * @returns {ShellCommandLines} One shared command line, or one per Windows shell\n */\nexport function formatShellCommandLines(argv: readonly string[]): ShellCommandLines {\n if (process.platform === \"win32\") return formatWindowsCommandLines(argv);\n return { kind: \"shared\", commandLine: argv.map(quotePosixArg).join(\" \") };\n}\n","import { redactSecrets, styles } from \"./logger\";\nimport { formatShellCommandLines } from \"./shell-quote\";\nimport type { Jsonifiable } from \"type-fest\";\n\n/**\n * Options for creating a CLI error\n */\ninterface CLIErrorOptions {\n /** Stable machine-readable failure code, such as `WORKSPACE_NOT_FOUND`. */\n code: string;\n message: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n cause?: unknown;\n}\n\nexport type CLIErrorNextAction = {\n /** Executable name, such as `tailor`. */\n command: string;\n /** Arguments passed directly to the executable. */\n args: readonly string[];\n};\n\n/**\n * CLI error interface with formatted output\n */\nexport interface CLIError extends Error {\n readonly code: string;\n readonly details?: string;\n readonly suggestion?: string;\n readonly command?: string;\n readonly next?: CLIErrorNextAction;\n readonly context?: Readonly<Record<string, Jsonifiable | undefined>>;\n format(): string;\n}\n\ntype CLIErrorInternal = Error & {\n code: string;\n details?: string;\n suggestion?: string;\n command?: string;\n next?: CLIErrorNextAction;\n context?: Readonly<Record<string, Jsonifiable | undefined>>;\n format(): string;\n};\n\n/**\n * Render a command as a user-facing hint that can be copied into the current platform's shells:\n * one backticked command line, or on Windows, when no single quoting suits both shells, a\n * PowerShell and a cmd.exe line (`` `…` in PowerShell or `…` in cmd.exe ``).\n * Registered secrets are redacted before quoting, which could otherwise split them.\n * @param {CLIErrorNextAction} action - Executable and arguments to suggest\n * @returns {string} The command part of the hint, to follow a verb such as \"Run\"\n */\nexport function formatCommandHint(action: CLIErrorNextAction): string {\n const commandLines = formatShellCommandLines(\n [action.command, ...action.args].map((arg) => redactSecrets(arg)),\n );\n if (commandLines.kind === \"shared\") {\n return `\\`${commandLines.commandLine}\\``;\n }\n return `\\`${commandLines.powershell}\\` in PowerShell or \\`${commandLines.cmd}\\` in cmd.exe`;\n}\n\n/**\n * Redact registered secrets, then indent every line after the first.\n * Redacting first keeps the indent from splitting a multi-line secret.\n * @param text - Details or suggestion text\n * @returns Redacted text with its continuation lines indented\n */\nfunction redactAndIndent(text: string): string {\n return redactSecrets(text).split(\"\\n\").join(\"\\n \");\n}\n\n/**\n * Format CLI error for output\n * @param error - CLIError instance to format\n * @returns Formatted error message\n */\nfunction formatError(error: CLIError): string {\n const parts: string[] = [\n styles.error(`Error${error.code ? ` [${error.code}]` : \"\"}: ${error.message}`),\n ];\n\n if (error.details) {\n parts.push(`\\n ${styles.dim(\"Details:\")} ${redactAndIndent(error.details)}`);\n }\n\n if (error.suggestion) {\n parts.push(`\\n ${styles.info(\"Suggestion:\")} ${redactAndIndent(error.suggestion)}`);\n }\n\n if (error.command) {\n parts.push(\n `\\n ${styles.dim(\"Help:\")} Run \\`tailor ${error.command} --help\\` for usage information.`,\n );\n }\n\n if (error.next) {\n parts.push(`\\n ${styles.info(\"Next:\")} Run ${formatCommandHint(error.next)}.`);\n }\n\n return parts.join(\"\");\n}\n\n/**\n * Create a CLI error with formatted output\n * @param options - Options to construct a CLIError\n * @returns Constructed CLIError instance\n */\nexport function CLIError(options: CLIErrorOptions): CLIError {\n const error = new Error(\n options.message,\n options.cause === undefined ? undefined : { cause: options.cause },\n ) as CLIErrorInternal;\n error.name = \"CLIError\";\n error.code = options.code;\n error.details = options.details;\n error.suggestion = options.suggestion;\n error.command = options.command;\n error.next = options.next;\n error.context = options.context;\n error.format = () => formatError(error);\n return error;\n}\n\n/**\n * Type guard to check if an error is a CLIError\n * @param error - Error to check\n * @returns True if the error is a CLIError\n */\nexport function isCLIError(error: unknown): error is CLIError {\n return error instanceof Error && error.name === \"CLIError\";\n}\n\n/**\n * Summarize a failure as plain text for reports that carry a single error string.\n * CLI errors contribute their details and suggestion so the remediation survives.\n * @param error - Failure to summarize\n * @returns Message followed by any details and suggestion, one per line\n */\nexport function errorSummary(error: unknown): string {\n if (isCLIError(error)) {\n return [error.message, error.details, error.suggestion].filter(Boolean).join(\"\\n\");\n }\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Create an error for an SDK invariant violation that no user action can fix.\n * The result is a plain Error, so JSON output reports it as `UNEXPECTED_ERROR`\n * and crash reporting treats it like any other unexpected failure.\n * @param message - Description of the violated invariant\n * @param options - Standard Error options, such as `cause`\n * @returns Plain Error instance\n */\nexport function internalError(message: string, options?: ErrorOptions): Error {\n return new Error(message, options);\n}\n\n/**\n * Convert a caught value into an Error, keeping Error instances as-is\n * @param value - Caught value\n * @returns The value itself when it is an Error, otherwise an Error of its string form with the value as `cause`\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value), { cause: value });\n}\n\nconst MISSING_NAMED_EXPORT_PATTERN = /does not provide an export named '(?!default')([^']+)'/;\n\n/**\n * Suggest `import type` when importing user code fails on a missing named\n * export. The CLI strips types from each file in isolation, so a type-only\n * export does not exist at runtime and a plain import of it fails to link.\n * @param error - Error thrown while importing user modules\n * @returns Suggestion text, or undefined when the error is not that failure\n */\nexport function typeOnlyImportHint(error: unknown): string | undefined {\n if (!(error instanceof SyntaxError)) return undefined;\n const name = MISSING_NAMED_EXPORT_PATTERN.exec(error.message)?.[1];\n if (!name) return undefined;\n return (\n `If '${name}' is a type, import it with \\`import type\\` (or the inline \\`type\\` modifier). ` +\n \"The CLI runs TypeScript by stripping types from each file in isolation, so type-only exports do not exist at runtime. \" +\n 'Set \"verbatimModuleSyntax\": true in tsconfig.json to catch this at typecheck.'\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"es-builtins-SfvXRVoG.mjs","names":["globals","globalsMap","ES_BUILTINS","NODE_ONLY_GLOBALS","name"],"sources":["../src/utils/es-builtins.ts"],"sourcesContent":["import * as globals from \"globals\";\n\ntype GlobalsShape = {\n builtin?: Record<string, boolean>;\n \"shared-node-browser\"?: Record<string, boolean>;\n node?: Record<string, boolean>;\n};\n\nconst globalsMap: GlobalsShape =\n (globals as unknown as { default?: GlobalsShape }).default ?? globals;\n\n/**\n * Runtime globals available in the PF execution environment.\n * Identifiers in this set are excluded from free variable detection\n * since they are always available in the runtime environment.\n *\n * Combines globals.builtin (ECMAScript language builtins) and\n * globals['shared-node-browser'] (shared runtime globals like\n * console, fetch, setTimeout, etc.) from the `globals` npm package.\n */\nexport const ES_BUILTINS = new Set([\n ...Object.keys(globalsMap.builtin ?? {}),\n ...Object.keys(globalsMap[\"shared-node-browser\"] ?? {}),\n]);\n\n/**\n * Node-only ambient globals (`process`, `Buffer`, `require`, `setImmediate`,\n * etc.) that `@types/node` puts in the global scope but that are absent from\n * `ES_BUILTINS`, so the Tailor Platform runtime never defines them. Sourced\n * from the same `globals` package as `ES_BUILTINS` instead of a hand-curated\n * list, so a newly recognized Node-only global can't be silently omitted.\n */\nexport const NODE_ONLY_GLOBALS = new Set(\n Object.keys(globalsMap.node ?? {}).filter((name) => !ES_BUILTINS.has(name)),\n);\n"],"mappings":"0BAQA,MAAMC,EACHD,EAAkD,SAAWA,EAWnDE,kBAAc,IAAI,IAAI,CACjC,GAAG,OAAO,KAAKD,EAAW,SAAW,CAAC,CAAC,EACvC,GAAG,OAAO,KAAKA,EAAW,wBAA0B,CAAC,CAAC,CACxD,CAAC,EASYE,EAAoB,IAAI,IACnC,OAAO,KAAKF,EAAW,MAAQ,CAAC,CAAC,CAAC,CAAC,OAAQ,GAAS,CAACC,EAAY,IAAIE,CAAI,CAAC,CAC5E"}
1
+ {"version":3,"file":"es-builtins-SfvXRVoG.mjs","names":["globals","globalsMap","ES_BUILTINS","NODE_ONLY_GLOBALS","name"],"sources":["../src/utils/es-builtins.ts"],"sourcesContent":["import * as globals from \"globals\";\n\ntype GlobalsShape = {\n builtin?: Record<string, boolean>;\n \"shared-node-browser\"?: Record<string, boolean>;\n node?: Record<string, boolean>;\n};\n\nconst globalsMap: GlobalsShape =\n (globals as unknown as { default?: GlobalsShape }).default ?? globals;\n\n/**\n * Runtime globals available in the PF execution environment.\n * Identifiers in this set are excluded from free variable detection\n * since they are always available in the runtime environment.\n *\n * Combines globals.builtin (ECMAScript language builtins) and\n * globals['shared-node-browser'] (shared runtime globals like\n * console, fetch, setTimeout, etc.) from the `globals` npm package.\n */\nexport const ES_BUILTINS = new Set([\n ...Object.keys(globalsMap.builtin ?? {}),\n ...Object.keys(globalsMap[\"shared-node-browser\"] ?? {}),\n]);\n\n/**\n * Node-only ambient globals (`process`, `Buffer`, `require`, `setImmediate`,\n * etc.) that `@types/node` puts in the global scope but that are absent from\n * `ES_BUILTINS`, so the Tailor Platform runtime never defines them. Sourced\n * from the same `globals` package as `ES_BUILTINS` instead of a hand-curated\n * list, so a newly recognized Node-only global can't be silently omitted.\n */\nexport const NODE_ONLY_GLOBALS = new Set(\n Object.keys(globalsMap.node ?? {}).filter((name) => !ES_BUILTINS.has(name)),\n);\n"],"mappings":"A,0BAQA,MAAMC,EACHD,EAAkD,SAAWA,EAWnDE,kBAAc,IAAI,IAAI,CACjC,GAAG,OAAO,KAAKD,EAAW,SAAW,CAAC,CAAC,EACvC,GAAG,OAAO,KAAKA,EAAW,wBAA0B,CAAC,CAAC,CACxD,CAAC,EASYE,EAAoB,IAAI,IACnC,OAAO,KAAKF,EAAW,MAAQ,CAAC,CAAC,CAAC,CAAC,OAAQ,GAAS,CAACC,EAAY,IAAIE,CAAI,CAAC,CAC5E,E"}
@@ -1 +1 @@
1
- {"version":3,"file":"field-parse-C41e3UAF.mjs","names":["formatDate","getTemporal","dateRepresentationOf","regex","value","args","path","pathArray","initialIssueCount","issues","field","allowedValues","v","nestedBaseValid","fieldName","nestedField","fieldValue","fn","validateFns","result","isNullOrUndefined","baseValid","i","records","record","recordPath","bundledDateParsers","validationArgs","dateParsers","type","text","date","calendarDate","Temporal","allDateParsers","as","parse","item","error","itemPath","key","child","converted","values","index","parseArgs"],"sources":["../src/runtime/field-parse.ts"],"sourcesContent":["import { dateRepresentationOf, formatDate } from \"./date\";\nimport { getTemporal } from \"./temporal\";\nimport type { FieldMetadata, TailorFieldType } from \"#/configure/types/field.types\";\nimport type { TailorPrincipal } from \"#/runtime/types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nconst regex = {\n uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n date: /^(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})$/,\n time: /^(?<hour>[01]\\d|2[0-3]):(?<minute>[0-5]\\d)$/,\n datetime:\n /^(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})[Tt](?<hour>[01]\\d|2[0-3]):(?<minute>[0-5]\\d):(?<second>[0-5]\\d|60)(\\.(?<fraction>\\d+))?(?<offset>[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$/,\n decimal: /^-?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$/,\n} as const;\n\n/**\n * Whether a value is an acceptable shape for a nested field. Runtime input may\n * not match the declared field type, so anything this rejects is reported\n * against the field rather than recursed into.\n * @param value - The value to check\n * @returns `true` when the value is a plain object the parser recurses into\n */\nexport function isNestedObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" && value !== null && !Array.isArray(value) && !(value instanceof Date)\n );\n}\n\nexport type FieldParseArgs = {\n value: unknown;\n data: unknown;\n invoker: TailorPrincipal | null;\n};\n\nexport type FieldParseInternalArgs = {\n value: unknown;\n data: unknown;\n invoker: TailorPrincipal | null;\n pathArray: string[];\n};\n\nexport type FieldRuntime<T extends TailorFieldType = TailorFieldType> = {\n readonly type: T;\n readonly fields: Record<string, FieldRuntime>;\n _metadata: FieldMetadata;\n};\n\ntype FieldParseRuntimeArgs<T extends TailorFieldType> = FieldParseInternalArgs & {\n field: FieldRuntime<T>;\n};\n\ntype FieldValidationArgs<T extends TailorFieldType> = FieldParseRuntimeArgs<T> & {\n issues: StandardSchemaV1.Issue[];\n};\n\nfunction validateBaseValue<T extends TailorFieldType>(args: FieldValidationArgs<T>): boolean {\n const { field, value, pathArray, issues } = args;\n const path = pathArray.length > 0 ? pathArray : undefined;\n const initialIssueCount = issues.length;\n\n switch (field.type) {\n case \"string\":\n if (typeof value !== \"string\") {\n issues.push({\n message: `Expected a string: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"integer\":\n if (typeof value !== \"number\" || !Number.isInteger(value)) {\n issues.push({\n message: `Expected an integer: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"float\":\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n issues.push({\n message: `Expected a number: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n issues.push({\n message: `Expected a boolean: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"uuid\":\n if (typeof value !== \"string\" || !regex.uuid.test(value)) {\n issues.push({\n message: `Expected a valid UUID: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"date\":\n if (typeof value !== \"string\" || !regex.date.test(value)) {\n issues.push({\n message: `Expected to match \"yyyy-MM-dd\" format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"datetime\":\n if (typeof value !== \"string\" || !regex.datetime.test(value)) {\n issues.push({\n message: `Expected to match ISO format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"time\":\n if (typeof value !== \"string\" || !regex.time.test(value)) {\n issues.push({\n message: `Expected to match \"HH:mm\" format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"decimal\":\n if (typeof value !== \"string\" || !regex.decimal.test(value)) {\n issues.push({\n message: `Expected a decimal string: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"enum\":\n if (field._metadata.allowedValues) {\n const allowedValues = field._metadata.allowedValues.map((v) => v.value);\n if (typeof value !== \"string\" || !allowedValues.includes(value)) {\n issues.push({\n message: `Must be one of [${allowedValues.join(\", \")}]: received ${String(value)}`,\n path,\n });\n }\n }\n break;\n\n case \"nested\": {\n if (!isNestedObject(value)) {\n issues.push({\n message: `Expected an object: received ${String(value)}`,\n path,\n });\n return false;\n }\n\n let nestedBaseValid = true;\n for (const [fieldName, nestedField] of Object.entries(field.fields)) {\n const fieldValue = value[fieldName];\n if (\n !validateBaseField({\n ...args,\n field: nestedField,\n value: fieldValue,\n pathArray: pathArray.concat(fieldName),\n })\n ) {\n nestedBaseValid = false;\n }\n }\n return nestedBaseValid;\n }\n }\n\n return issues.length === initialIssueCount;\n}\n\nfunction validateCustomValue<T extends TailorFieldType>(\n args: FieldValidationArgs<T>,\n validateFns: NonNullable<FieldMetadata[\"validate\"]>,\n): void {\n const { value, pathArray, issues } = args;\n const path = pathArray.length > 0 ? pathArray : undefined;\n\n for (const fn of validateFns) {\n const result = fn({ value });\n if (typeof result === \"string\") {\n issues.push({\n message: result,\n path,\n });\n }\n }\n}\n\nfunction validateBaseField<T extends TailorFieldType>(args: FieldValidationArgs<T>): boolean {\n const { field, value, pathArray, issues } = args;\n const path = pathArray.length > 0 ? pathArray : undefined;\n\n const isNullOrUndefined = value === null || value === undefined;\n if (field._metadata.required && isNullOrUndefined) {\n issues.push({\n message: \"Required field is missing\",\n path,\n });\n return false;\n }\n\n if (!field._metadata.required && isNullOrUndefined) {\n return true;\n }\n\n let baseValid = true;\n if (field._metadata.array) {\n if (!Array.isArray(value)) {\n issues.push({\n message: \"Expected an array\",\n path,\n });\n return false;\n }\n\n for (let i = 0; i < value.length; i++) {\n if (\n !validateBaseValue({\n ...args,\n value: value[i],\n pathArray: pathArray.concat(`[${i}]`),\n })\n ) {\n baseValid = false;\n }\n }\n } else {\n baseValid = validateBaseValue(args);\n }\n\n return baseValid;\n}\n\nfunction validateCustomField<T extends TailorFieldType>(args: FieldValidationArgs<T>): void {\n const { field, value, pathArray } = args;\n if (value === null || value === undefined) {\n return;\n }\n\n if (field.type === \"nested\") {\n const records = field._metadata.array\n ? (value as Record<string, unknown>[])\n : [value as Record<string, unknown>];\n\n for (let i = 0; i < records.length; i++) {\n const record = records[i] as Record<string, unknown>;\n const recordPath = field._metadata.array ? pathArray.concat(`[${i}]`) : pathArray;\n for (const [fieldName, nestedField] of Object.entries(field.fields)) {\n validateCustomField({\n ...args,\n field: nestedField,\n value: record[fieldName],\n pathArray: recordPath.concat(fieldName),\n });\n }\n }\n }\n\n const validateFns = field._metadata.validate;\n if (validateFns?.length) {\n validateCustomValue(args, validateFns);\n }\n}\n\nexport function parseInternal<T extends TailorFieldType, Output>(\n args: FieldParseRuntimeArgs<T>,\n dateParsers: DateParsers = bundledDateParsers,\n): StandardSchemaV1.Result<Output> {\n let { value } = args;\n const issues: StandardSchemaV1.Issue[] = [];\n const validationArgs = { ...args, issues };\n const baseValid = validateBaseField(validationArgs);\n if (baseValid) {\n value = deserializeDates(validationArgs, dateParsers);\n if (issues.length === 0) {\n validateCustomField({ ...validationArgs, value });\n }\n }\n if (issues.length > 0) {\n return { issues };\n }\n\n return { value: (value ?? null) as Output };\n}\n\ntype DateParser = (type: TailorFieldType, text: string) => unknown;\n\ntype DateParsers = {\n readonly date: DateParser | undefined;\n readonly temporal: DateParser | undefined;\n};\n\nfunction rejectLeapSecond(type: TailorFieldType, text: string): void {\n if (type === \"datetime\" && text.slice(17, 19) === \"60\") {\n throw new RangeError(\"Leap seconds are not supported\");\n }\n}\n\nfunction parseAsDate(type: TailorFieldType, text: string): Date {\n rejectLeapSecond(type, text);\n const date = new Date(\n type === \"date\"\n ? `${text}T00:00:00.000Z`\n : type === \"time\"\n ? `1970-01-01T${text}:00.000Z`\n : text,\n );\n if (!Number.isFinite(date.getTime())) throw new RangeError(\"Invalid Date\");\n if (type !== \"time\") {\n const calendarDate = text.slice(0, 10);\n if (formatDate(new Date(`${calendarDate}T00:00:00.000Z`)) !== calendarDate) {\n throw new RangeError(\"Invalid calendar date\");\n }\n }\n return date;\n}\n\nfunction parseAsTemporal(type: TailorFieldType, text: string): unknown {\n rejectLeapSecond(type, text);\n const Temporal = getTemporal();\n if (type === \"date\") return Temporal.PlainDate.from(text);\n if (type === \"datetime\") return Temporal.Instant.from(text);\n return Temporal.PlainTime.from(text);\n}\n\nconst allDateParsers: DateParsers = { date: parseAsDate, temporal: parseAsTemporal };\n\ntype MaybeProcessGlobal = { process?: { env: Record<string, string | undefined> } };\n\nconst bundledDateParsers: DateParsers = {\n date: (globalThis as MaybeProcessGlobal).process?.env.__TAILOR_PLATFORM_BUNDLE_WITHOUT_DATE\n ? undefined\n : parseAsDate,\n temporal: (globalThis as MaybeProcessGlobal).process?.env\n .__TAILOR_PLATFORM_BUNDLE_WITHOUT_TEMPORAL\n ? undefined\n : parseAsTemporal,\n};\n\nfunction deserializeDates(\n args: FieldValidationArgs<TailorFieldType>,\n dateParsers: DateParsers,\n): unknown {\n const { field, value, issues, pathArray } = args;\n if (value === null || value === undefined) return value;\n const convert = (item: unknown, itemPath: string[]): unknown => {\n const { type } = field;\n const as = dateRepresentationOf(type, field._metadata.as);\n if (as) {\n const parse = dateParsers[as];\n if (!parse) {\n throw new Error(\n `Parsing fields declared with as: \"${as}\" is not included in this bundle. Parse them with parseDateFields, exported by \"@tailor-platform/sdk/runtime\".`,\n );\n }\n try {\n return parse(type, String(item));\n } catch (error) {\n if (!(error instanceof RangeError)) throw error;\n issues.push({\n message: `Expected a valid ${type === \"date\" ? \"calendar date\" : type}: received ${item}`,\n path: itemPath.length > 0 ? itemPath : undefined,\n });\n return item;\n }\n }\n if (field.type !== \"nested\") return item;\n const record = item as Record<string, unknown>;\n let result = record;\n for (const [key, child] of Object.entries(field.fields)) {\n const converted = deserializeDates(\n {\n ...args,\n field: child,\n value: record[key],\n pathArray: itemPath.concat(key),\n },\n dateParsers,\n );\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 if (!field._metadata.array) return convert(value, pathArray);\n const values = value as unknown[];\n const converted = values.map((item, index) => convert(item, pathArray.concat(`[${index}]`)));\n return converted.every((item, index) => item === values[index]) ? value : converted;\n}\n\ntype ParseInputFieldsArgs = FieldParseArgs & {\n fields: Record<string, FieldRuntime>;\n};\n\n/**\n * Validate a value against a record of input fields, treating the record as a\n * single required object — the same code path deployed functions run via\n * `t.object(fields).parse(...)`, so local validation matches platform behavior.\n * @param args - Input field definitions plus the value, context data, and user\n * @returns Validation result\n */\nexport function parseInputFields(args: ParseInputFieldsArgs): StandardSchemaV1.Result<unknown> {\n const { fields, ...parseArgs } = args;\n return parseInternal({\n ...parseArgs,\n field: { type: \"nested\", fields, _metadata: { required: true } },\n pathArray: [],\n });\n}\n\ntype ParsableField<Output> = FieldRuntime & {\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n};\n\n/**\n * Parse a value like `field.parse`, converting fields declared with `as: \"date\"` or\n * `as: \"temporal\"` even in bundles that leave that conversion out of `field.parse`.\n * @param field - Field defining the value's shape\n * @param args - Value to parse, with the context data and invoker passed to validators\n * @returns Validation result with Date and Temporal fields converted\n */\nexport function parseDateFields<Output>(\n field: ParsableField<Output>,\n args: FieldParseArgs,\n): StandardSchemaV1.Result<Output> {\n return parseInternal({ ...args, field, pathArray: [] }, allDateParsers);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"field-parse-C41e3UAF.mjs","names":["formatDate","getTemporal","dateRepresentationOf","regex","value","args","path","pathArray","initialIssueCount","issues","field","allowedValues","v","nestedBaseValid","fieldName","nestedField","fieldValue","fn","validateFns","result","isNullOrUndefined","baseValid","i","records","record","recordPath","bundledDateParsers","validationArgs","dateParsers","type","text","date","calendarDate","Temporal","allDateParsers","as","parse","item","error","itemPath","key","child","converted","values","index","parseArgs"],"sources":["../src/runtime/field-parse.ts"],"sourcesContent":["import { dateRepresentationOf, formatDate } from \"./date\";\nimport { getTemporal } from \"./temporal\";\nimport type { FieldMetadata, TailorFieldType } from \"#/configure/types/field.types\";\nimport type { TailorPrincipal } from \"#/runtime/types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nconst regex = {\n uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,\n date: /^(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})$/,\n time: /^(?<hour>[01]\\d|2[0-3]):(?<minute>[0-5]\\d)$/,\n datetime:\n /^(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})[Tt](?<hour>[01]\\d|2[0-3]):(?<minute>[0-5]\\d):(?<second>[0-5]\\d|60)(\\.(?<fraction>\\d+))?(?<offset>[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$/,\n decimal: /^-?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$/,\n} as const;\n\n/**\n * Whether a value is an acceptable shape for a nested field. Runtime input may\n * not match the declared field type, so anything this rejects is reported\n * against the field rather than recursed into.\n * @param value - The value to check\n * @returns `true` when the value is a plain object the parser recurses into\n */\nexport function isNestedObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" && value !== null && !Array.isArray(value) && !(value instanceof Date)\n );\n}\n\nexport type FieldParseArgs = {\n value: unknown;\n data: unknown;\n invoker: TailorPrincipal | null;\n};\n\nexport type FieldParseInternalArgs = {\n value: unknown;\n data: unknown;\n invoker: TailorPrincipal | null;\n pathArray: string[];\n};\n\nexport type FieldRuntime<T extends TailorFieldType = TailorFieldType> = {\n readonly type: T;\n readonly fields: Record<string, FieldRuntime>;\n _metadata: FieldMetadata;\n};\n\ntype FieldParseRuntimeArgs<T extends TailorFieldType> = FieldParseInternalArgs & {\n field: FieldRuntime<T>;\n};\n\ntype FieldValidationArgs<T extends TailorFieldType> = FieldParseRuntimeArgs<T> & {\n issues: StandardSchemaV1.Issue[];\n};\n\nfunction validateBaseValue<T extends TailorFieldType>(args: FieldValidationArgs<T>): boolean {\n const { field, value, pathArray, issues } = args;\n const path = pathArray.length > 0 ? pathArray : undefined;\n const initialIssueCount = issues.length;\n\n switch (field.type) {\n case \"string\":\n if (typeof value !== \"string\") {\n issues.push({\n message: `Expected a string: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"integer\":\n if (typeof value !== \"number\" || !Number.isInteger(value)) {\n issues.push({\n message: `Expected an integer: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"float\":\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n issues.push({\n message: `Expected a number: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n issues.push({\n message: `Expected a boolean: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"uuid\":\n if (typeof value !== \"string\" || !regex.uuid.test(value)) {\n issues.push({\n message: `Expected a valid UUID: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"date\":\n if (typeof value !== \"string\" || !regex.date.test(value)) {\n issues.push({\n message: `Expected to match \"yyyy-MM-dd\" format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"datetime\":\n if (typeof value !== \"string\" || !regex.datetime.test(value)) {\n issues.push({\n message: `Expected to match ISO format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"time\":\n if (typeof value !== \"string\" || !regex.time.test(value)) {\n issues.push({\n message: `Expected to match \"HH:mm\" format: received ${String(value)}`,\n path,\n });\n }\n break;\n case \"decimal\":\n if (typeof value !== \"string\" || !regex.decimal.test(value)) {\n issues.push({\n message: `Expected a decimal string: received ${String(value)}`,\n path,\n });\n }\n break;\n\n case \"enum\":\n if (field._metadata.allowedValues) {\n const allowedValues = field._metadata.allowedValues.map((v) => v.value);\n if (typeof value !== \"string\" || !allowedValues.includes(value)) {\n issues.push({\n message: `Must be one of [${allowedValues.join(\", \")}]: received ${String(value)}`,\n path,\n });\n }\n }\n break;\n\n case \"nested\": {\n if (!isNestedObject(value)) {\n issues.push({\n message: `Expected an object: received ${String(value)}`,\n path,\n });\n return false;\n }\n\n let nestedBaseValid = true;\n for (const [fieldName, nestedField] of Object.entries(field.fields)) {\n const fieldValue = value[fieldName];\n if (\n !validateBaseField({\n ...args,\n field: nestedField,\n value: fieldValue,\n pathArray: pathArray.concat(fieldName),\n })\n ) {\n nestedBaseValid = false;\n }\n }\n return nestedBaseValid;\n }\n }\n\n return issues.length === initialIssueCount;\n}\n\nfunction validateCustomValue<T extends TailorFieldType>(\n args: FieldValidationArgs<T>,\n validateFns: NonNullable<FieldMetadata[\"validate\"]>,\n): void {\n const { value, pathArray, issues } = args;\n const path = pathArray.length > 0 ? pathArray : undefined;\n\n for (const fn of validateFns) {\n const result = fn({ value });\n if (typeof result === \"string\") {\n issues.push({\n message: result,\n path,\n });\n }\n }\n}\n\nfunction validateBaseField<T extends TailorFieldType>(args: FieldValidationArgs<T>): boolean {\n const { field, value, pathArray, issues } = args;\n const path = pathArray.length > 0 ? pathArray : undefined;\n\n const isNullOrUndefined = value === null || value === undefined;\n if (field._metadata.required && isNullOrUndefined) {\n issues.push({\n message: \"Required field is missing\",\n path,\n });\n return false;\n }\n\n if (!field._metadata.required && isNullOrUndefined) {\n return true;\n }\n\n let baseValid = true;\n if (field._metadata.array) {\n if (!Array.isArray(value)) {\n issues.push({\n message: \"Expected an array\",\n path,\n });\n return false;\n }\n\n for (let i = 0; i < value.length; i++) {\n if (\n !validateBaseValue({\n ...args,\n value: value[i],\n pathArray: pathArray.concat(`[${i}]`),\n })\n ) {\n baseValid = false;\n }\n }\n } else {\n baseValid = validateBaseValue(args);\n }\n\n return baseValid;\n}\n\nfunction validateCustomField<T extends TailorFieldType>(args: FieldValidationArgs<T>): void {\n const { field, value, pathArray } = args;\n if (value === null || value === undefined) {\n return;\n }\n\n if (field.type === \"nested\") {\n const records = field._metadata.array\n ? (value as Record<string, unknown>[])\n : [value as Record<string, unknown>];\n\n for (let i = 0; i < records.length; i++) {\n const record = records[i] as Record<string, unknown>;\n const recordPath = field._metadata.array ? pathArray.concat(`[${i}]`) : pathArray;\n for (const [fieldName, nestedField] of Object.entries(field.fields)) {\n validateCustomField({\n ...args,\n field: nestedField,\n value: record[fieldName],\n pathArray: recordPath.concat(fieldName),\n });\n }\n }\n }\n\n const validateFns = field._metadata.validate;\n if (validateFns?.length) {\n validateCustomValue(args, validateFns);\n }\n}\n\nexport function parseInternal<T extends TailorFieldType, Output>(\n args: FieldParseRuntimeArgs<T>,\n dateParsers: DateParsers = bundledDateParsers,\n): StandardSchemaV1.Result<Output> {\n let { value } = args;\n const issues: StandardSchemaV1.Issue[] = [];\n const validationArgs = { ...args, issues };\n const baseValid = validateBaseField(validationArgs);\n if (baseValid) {\n value = deserializeDates(validationArgs, dateParsers);\n if (issues.length === 0) {\n validateCustomField({ ...validationArgs, value });\n }\n }\n if (issues.length > 0) {\n return { issues };\n }\n\n return { value: (value ?? null) as Output };\n}\n\ntype DateParser = (type: TailorFieldType, text: string) => unknown;\n\ntype DateParsers = {\n readonly date: DateParser | undefined;\n readonly temporal: DateParser | undefined;\n};\n\nfunction rejectLeapSecond(type: TailorFieldType, text: string): void {\n if (type === \"datetime\" && text.slice(17, 19) === \"60\") {\n throw new RangeError(\"Leap seconds are not supported\");\n }\n}\n\nfunction parseAsDate(type: TailorFieldType, text: string): Date {\n rejectLeapSecond(type, text);\n const date = new Date(\n type === \"date\"\n ? `${text}T00:00:00.000Z`\n : type === \"time\"\n ? `1970-01-01T${text}:00.000Z`\n : text,\n );\n if (!Number.isFinite(date.getTime())) throw new RangeError(\"Invalid Date\");\n if (type !== \"time\") {\n const calendarDate = text.slice(0, 10);\n if (formatDate(new Date(`${calendarDate}T00:00:00.000Z`)) !== calendarDate) {\n throw new RangeError(\"Invalid calendar date\");\n }\n }\n return date;\n}\n\nfunction parseAsTemporal(type: TailorFieldType, text: string): unknown {\n rejectLeapSecond(type, text);\n const Temporal = getTemporal();\n if (type === \"date\") return Temporal.PlainDate.from(text);\n if (type === \"datetime\") return Temporal.Instant.from(text);\n return Temporal.PlainTime.from(text);\n}\n\nconst allDateParsers: DateParsers = { date: parseAsDate, temporal: parseAsTemporal };\n\ntype MaybeProcessGlobal = { process?: { env: Record<string, string | undefined> } };\n\nconst bundledDateParsers: DateParsers = {\n date: (globalThis as MaybeProcessGlobal).process?.env.__TAILOR_PLATFORM_BUNDLE_WITHOUT_DATE\n ? undefined\n : parseAsDate,\n temporal: (globalThis as MaybeProcessGlobal).process?.env\n .__TAILOR_PLATFORM_BUNDLE_WITHOUT_TEMPORAL\n ? undefined\n : parseAsTemporal,\n};\n\nfunction deserializeDates(\n args: FieldValidationArgs<TailorFieldType>,\n dateParsers: DateParsers,\n): unknown {\n const { field, value, issues, pathArray } = args;\n if (value === null || value === undefined) return value;\n const convert = (item: unknown, itemPath: string[]): unknown => {\n const { type } = field;\n const as = dateRepresentationOf(type, field._metadata.as);\n if (as) {\n const parse = dateParsers[as];\n if (!parse) {\n throw new Error(\n `Parsing fields declared with as: \"${as}\" is not included in this bundle. Parse them with parseDateFields, exported by \"@tailor-platform/sdk/runtime\".`,\n );\n }\n try {\n return parse(type, String(item));\n } catch (error) {\n if (!(error instanceof RangeError)) throw error;\n issues.push({\n message: `Expected a valid ${type === \"date\" ? \"calendar date\" : type}: received ${item}`,\n path: itemPath.length > 0 ? itemPath : undefined,\n });\n return item;\n }\n }\n if (field.type !== \"nested\") return item;\n const record = item as Record<string, unknown>;\n let result = record;\n for (const [key, child] of Object.entries(field.fields)) {\n const converted = deserializeDates(\n {\n ...args,\n field: child,\n value: record[key],\n pathArray: itemPath.concat(key),\n },\n dateParsers,\n );\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 if (!field._metadata.array) return convert(value, pathArray);\n const values = value as unknown[];\n const converted = values.map((item, index) => convert(item, pathArray.concat(`[${index}]`)));\n return converted.every((item, index) => item === values[index]) ? value : converted;\n}\n\ntype ParseInputFieldsArgs = FieldParseArgs & {\n fields: Record<string, FieldRuntime>;\n};\n\n/**\n * Validate a value against a record of input fields, treating the record as a\n * single required object — the same code path deployed functions run via\n * `t.object(fields).parse(...)`, so local validation matches platform behavior.\n * @param args - Input field definitions plus the value, context data, and user\n * @returns Validation result\n */\nexport function parseInputFields(args: ParseInputFieldsArgs): StandardSchemaV1.Result<unknown> {\n const { fields, ...parseArgs } = args;\n return parseInternal({\n ...parseArgs,\n field: { type: \"nested\", fields, _metadata: { required: true } },\n pathArray: [],\n });\n}\n\ntype ParsableField<Output> = FieldRuntime & {\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n};\n\n/**\n * Parse a value like `field.parse`, converting fields declared with `as: \"date\"` or\n * `as: \"temporal\"` even in bundles that leave that conversion out of `field.parse`.\n * @param field - Field defining the value's shape\n * @param args - Value to parse, with the context data and invoker passed to validators\n * @returns Validation result with Date and Temporal fields converted\n */\nexport function parseDateFields<Output>(\n field: ParsableField<Output>,\n args: FieldParseArgs,\n): StandardSchemaV1.Result<Output> {\n return parseInternal({ ...args, field, pathArray: [] }, allDateParsers);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-DKBOj5q4.mjs","names":["DATA_URL_PREFIX","match","data","base64","decoded","bytes","index","options","namespace","tableName","fieldName","recordId","uploadOptions","contents","encoding","payload","contentType","file","args"],"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,KAExEA,EAAkB,yBAExB,SAAS,mBAAmB,EAAyD,CACnF,IAAMC,EAAQD,EAAgB,KAAKE,CAAI,EAEvC,OADKD,EACE,CAAE,YAAaA,EAAM,IAAM,IAAA,GAAW,QAASC,EAAK,MAAMD,EAAM,EAAE,CAAC,MAAM,CAAE,EAD/D,CAAE,QAASC,CAAK,CAErC,CAEA,SAAS,aAAa,EAA0B,CAC9C,GAAI,OAAO,WAAW,YAAe,WACnC,GAAI,CACF,OAAO,WAAW,WAAWA,CAAI,CACnC,MAAQ,CACN,MAAU,UAAU,2BAA2B,CACjD,CAIF,IAAMC,EAASD,EAAK,QAAQ,eAAgB,EAAE,EAC9C,GACE,CAAC,yBAAyB,KAAKC,CAAM,GACrCA,EAAO,OAAS,GAAM,GACrBA,EAAO,SAAS,GAAG,GAAKA,EAAO,OAAS,GAAM,EAE/C,MAAU,UAAU,2BAA2B,EAEjD,IAAMC,EAAU,KAAKD,CAAM,EACrBE,EAAQ,IAAI,WAAWD,EAAQ,MAAM,EAC3C,IAAK,IAAIE,EAAQ,EAAGA,EAAQF,EAAQ,OAAQ,IAC1C,EAAME,GAASF,EAAQ,WAAWE,CAAK,EAEzC,OAAOD,CACT,CA2DA,eAAe,OACb,EACA,EACA,EACA,EACA,EACA,EAC6B,CAC7B,GAAIE,GAAS,WAAa,IAAA,GACxB,OAAO,IAAI,CAAC,CAAC,OAAOC,EAAWC,EAAWC,EAAWC,EAAUT,EAAMK,CAAO,EAG9E,GAAM,CAAE,WAAU,GAAGK,GAAkBL,EACnCM,EAAWX,EACf,GAAI,OAAOA,GAAS,SAClB,OAAQY,EAAR,CACE,IAAK,OACH,EAAW,IAAI,YAAY,CAAC,CAAC,OAAOZ,CAAI,EACxC,EAAc,cAAgB,4BAC9B,MACF,IAAK,SAAU,CACb,GAAM,CAAE,cAAa,WAAY,mBAAmBA,CAAI,EACxD,EAAW,aAAaa,CAAO,EAC/B,EAAc,cAAgBC,EAC9B,KACF,CACA,QACE,MAAU,UAAU,qCAAqCF,GAAU,CACvE,CAEF,OAAO,IAAI,CAAC,CAAC,OAAON,EAAWC,EAAWC,EAAWC,EAAUE,EAAUD,CAAa,CACxF,CA+CA,MAAaK,EAAO,CAElB,OACA,UA3C6C,GAAGC,IAAS,IAAI,CAAC,CAAC,SAAS,GAAGA,CAAI,EA4C/E,kBArC6D,GAAGA,IAChE,IAAI,CAAC,CAAC,iBAAiB,GAAGA,CAAI,EAqC9B,QA9B6C,GAAGA,IAAS,IAAI,CAAC,CAAC,OAAO,GAAGA,CAAI,EA+B7E,aAxBmD,GAAGA,IAAS,IAAI,CAAC,CAAC,YAAY,GAAGA,CAAI,EAyBxF,gBAlByD,GAAGA,IAC5D,IAAI,CAAC,CAAC,eAAe,GAAGA,CAAI,EAkB5B,cAXqD,GAAGA,IAAS,IAAI,CAAC,CAAC,aAAa,GAAGA,CAAI,CAY7F"}
1
+ {"version":3,"file":"file-DKBOj5q4.mjs","names":["DATA_URL_PREFIX","match","data","base64","decoded","bytes","index","options","namespace","tableName","fieldName","recordId","uploadOptions","contents","encoding","payload","contentType","file","args"],"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,KAExEA,EAAkB,yBAExB,SAAS,mBAAmB,EAAyD,CACnF,IAAMC,EAAQD,EAAgB,KAAKE,CAAI,EAEvC,OADKD,EACE,CAAE,YAAaA,EAAM,IAAM,IAAA,GAAW,QAASC,EAAK,MAAMD,EAAM,EAAE,CAAC,MAAM,CAAE,EAD/D,CAAE,QAASC,CAAK,CAErC,CAEA,SAAS,aAAa,EAA0B,CAC9C,GAAI,OAAO,WAAW,YAAe,WACnC,GAAI,CACF,OAAO,WAAW,WAAWA,CAAI,CACnC,MAAQ,CACN,MAAU,UAAU,2BAA2B,CACjD,CAIF,IAAMC,EAASD,EAAK,QAAQ,eAAgB,EAAE,EAC9C,GACE,CAAC,yBAAyB,KAAKC,CAAM,GACrCA,EAAO,OAAS,GAAM,GACrBA,EAAO,SAAS,GAAG,GAAKA,EAAO,OAAS,GAAM,EAE/C,MAAU,UAAU,2BAA2B,EAEjD,IAAMC,EAAU,KAAKD,CAAM,EACrBE,EAAQ,IAAI,WAAWD,EAAQ,MAAM,EAC3C,IAAK,IAAIE,EAAQ,EAAGA,EAAQF,EAAQ,OAAQ,IAC1C,EAAME,GAASF,EAAQ,WAAWE,CAAK,EAEzC,OAAOD,CACT,CA2DA,eAAe,OACb,EACA,EACA,EACA,EACA,EACA,EAC6B,CAC7B,GAAIE,GAAS,WAAa,IAAA,GACxB,OAAO,IAAI,CAAC,CAAC,OAAOC,EAAWC,EAAWC,EAAWC,EAAUT,EAAMK,CAAO,EAG9E,GAAM,CAAE,WAAU,GAAGK,GAAkBL,EACnCM,EAAWX,EACf,GAAI,OAAOA,GAAS,SAClB,OAAQY,EAAR,CACE,IAAK,OACH,EAAW,IAAI,YAAY,CAAC,CAAC,OAAOZ,CAAI,EACxC,EAAc,cAAgB,4BAC9B,MACF,IAAK,SAAU,CACb,GAAM,CAAE,cAAa,WAAY,mBAAmBA,CAAI,EACxD,EAAW,aAAaa,CAAO,EAC/B,EAAc,cAAgBC,EAC9B,KACF,CACA,QACE,MAAU,UAAU,qCAAqCF,GAAU,CACvE,CAEF,OAAO,IAAI,CAAC,CAAC,OAAON,EAAWC,EAAWC,EAAWC,EAAUE,EAAUD,CAAa,CACxF,CA+CA,MAAaK,EAAO,CAElB,OACA,UA3C6C,GAAGC,IAAS,IAAI,CAAC,CAAC,SAAS,GAAGA,CAAI,EA4C/E,kBArC6D,GAAGA,IAChE,IAAI,CAAC,CAAC,iBAAiB,GAAGA,CAAI,EAqC9B,QA9B6C,GAAGA,IAAS,IAAI,CAAC,CAAC,OAAO,GAAGA,CAAI,EA+B7E,aAxBmD,GAAGA,IAAS,IAAI,CAAC,CAAC,YAAY,GAAGA,CAAI,EAyBxF,gBAlByD,GAAGA,IAC5D,IAAI,CAAC,CAAC,eAAe,GAAGA,CAAI,EAkB5B,cAXqD,GAAGA,IAAS,IAAI,CAAC,CAAC,aAAa,GAAGA,CAAI,CAY7F,E"}