@tailor-platform/sdk 2.22.0 → 2.23.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 (88) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/aigateway-DsWDjzk4.mjs.map +1 -1
  3. package/dist/application-BtZ8hmx9.mjs +1 -0
  4. package/dist/{application-BzO9ywXQ.mjs → application-m2G91kKI.mjs} +2 -2
  5. package/dist/application-m2G91kKI.mjs.map +1 -0
  6. package/dist/assert-WeXvmG4j.mjs.map +1 -1
  7. package/dist/authconnection-CynFBIv8.mjs.map +1 -1
  8. package/dist/brand-C8nMKhJC.mjs.map +1 -1
  9. package/dist/cli/lib.mjs +1 -1
  10. package/dist/cli/lib.mjs.map +1 -1
  11. package/dist/cli/main.mjs +33 -33
  12. package/dist/cli/main.mjs.map +1 -1
  13. package/dist/cli/shared/client.d.mts +17 -2
  14. package/dist/completion/zsh-worker.zsh +13 -7
  15. package/dist/configure/index.mjs.map +1 -1
  16. package/dist/context-D0QjfxzD.mjs.map +1 -1
  17. package/dist/crashreport-DFpRn-LS.mjs.map +1 -1
  18. package/dist/date-Dri6Yas-.mjs.map +1 -1
  19. package/dist/errors-CpvSChyL.mjs +7 -0
  20. package/dist/errors-CpvSChyL.mjs.map +1 -0
  21. package/dist/es-builtins-SfvXRVoG.mjs.map +1 -1
  22. package/dist/field-parse-DhnFs9WG.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-C-iyFFH7.mjs.map +1 -1
  31. package/dist/logger-DP2BjQ93.mjs.map +1 -1
  32. package/dist/logger-Db-YRTwK.mjs.map +1 -1
  33. package/dist/manager-8-JOvuLl.mjs.map +1 -1
  34. package/dist/multiline-EyzjEwn9.mjs.map +1 -1
  35. package/dist/node-builtins-DYfhPBnz.mjs.map +1 -1
  36. package/dist/package-json-C690ceex.mjs.map +1 -1
  37. package/dist/platform-serialize-DkiTdHOt.mjs.map +1 -1
  38. package/dist/plugin/builtin/enum-constants/index.mjs.map +1 -1
  39. package/dist/plugin/builtin/file-utils/index.mjs.map +1 -1
  40. package/dist/plugin/index.mjs.map +1 -1
  41. package/dist/{register-ts-hook-z7ggXQy4.mjs → register-ts-hook-ztnEFW6n.mjs} +49 -49
  42. package/dist/register-ts-hook-ztnEFW6n.mjs.map +1 -0
  43. package/dist/registry-BijIDRVA.mjs.map +1 -1
  44. package/dist/repl-editor-DGJBfIt1.mjs.map +1 -1
  45. package/dist/schema-CRtAmDcl.mjs.map +1 -1
  46. package/dist/secret-file-C9wp_FCX.mjs.map +1 -1
  47. package/dist/secretmanager-5olfnI1b.mjs.map +1 -1
  48. package/dist/secretmanager-vHQoXdQz.mjs.map +1 -1
  49. package/dist/seed/index.mjs.map +1 -1
  50. package/dist/seed-C3P_T_Eh.mjs.map +1 -1
  51. package/dist/service-CL1Bp4It.mjs.map +1 -1
  52. package/dist/{service-DHVuH7-X.mjs → service-DFmt9UJP.mjs} +2 -2
  53. package/dist/service-DFmt9UJP.mjs.map +1 -0
  54. package/dist/service_pb-ZClWtj1X.mjs +2 -0
  55. package/dist/service_pb-ZClWtj1X.mjs.map +1 -0
  56. package/dist/service_pb-dB3RrGeh.mjs +1 -0
  57. package/dist/tailor-proto/src/tailor/v1/function_pb.d.mts +83 -1
  58. package/dist/tailor-proto/src/tailor/v1/function_resource_pb.d.mts +7 -1
  59. package/dist/tailor-proto/src/tailor/v1/service_pb.d.mts +79 -7
  60. package/dist/tailor-proto/src/tailor/v1/workflow_pb.d.mts +40 -1
  61. package/dist/tailor-proto/src/tailor/v1/workflow_resource_pb.d.mts +119 -2
  62. package/dist/tailor-proto/src/tailor/v1/workspace_pb.d.mts +7 -0
  63. package/dist/tailordb-ddl-DqInYupv.mjs.map +1 -1
  64. package/dist/telemetry-Bklv9kQY.mjs.map +1 -1
  65. package/dist/type-source--ZNcV8RJ.mjs.map +1 -1
  66. package/dist/user-agent-vdHYF3QL.mjs.map +1 -1
  67. package/dist/utils/test/index.mjs.map +1 -1
  68. package/dist/vitest/environment.mjs.map +1 -1
  69. package/dist/vitest/index.mjs.map +1 -1
  70. package/dist/vitest/setup.mjs.map +1 -1
  71. package/dist/wait-point-invoker-eiP-IIux.mjs.map +1 -1
  72. package/dist/wait-point-registry-B-ESkTZX.mjs.map +1 -1
  73. package/dist/workflow-Cs9ISw6j.mjs.map +1 -1
  74. package/dist/{workspace_resource_pb-D2njwgsE.mjs → workspace_resource_pb-CxR_6fyN.mjs} +2 -2
  75. package/dist/workspace_resource_pb-CxR_6fyN.mjs.map +1 -0
  76. package/docs/cli/application.md +23 -5
  77. package/docs/cli/function.md +2 -2
  78. package/package.json +3 -3
  79. package/dist/application-BE4vehXW.mjs +0 -1
  80. package/dist/application-BzO9ywXQ.mjs.map +0 -1
  81. package/dist/errors-C9zGf4nz.mjs +0 -7
  82. package/dist/errors-C9zGf4nz.mjs.map +0 -1
  83. package/dist/register-ts-hook-z7ggXQy4.mjs.map +0 -1
  84. package/dist/service-DHVuH7-X.mjs.map +0 -1
  85. package/dist/service_pb-DNskuJQC.mjs +0 -1
  86. package/dist/service_pb-y2GYIgfs.mjs +0 -2
  87. package/dist/service_pb-y2GYIgfs.mjs.map +0 -1
  88. package/dist/workspace_resource_pb-D2njwgsE.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"registry-BijIDRVA.mjs","names":[],"sources":["../src/configure/services/workflow/registry.ts"],"sourcesContent":["import type { TailorEnv, TailorPrincipal } from \"#/runtime/types\";\nimport type { ExecJobFunctionOptions, StartWorkflowOptions } from \"#/runtime/workflow\";\n\n/**\n * Body signature shared by workflow jobs at registry-write time.\n * The user's `createWorkflowJob`/`createWorkflow` body uses concrete types,\n * but the registry erases them for storage.\n */\nexport type RegisteredJobBody = (\n args: unknown,\n context: { env: TailorEnv; invoker: TailorPrincipal | null },\n) => unknown | Promise<unknown>;\n\nconst JOB_REGISTRY_KEY: unique symbol = Symbol.for(\"tailor-platform/sdk:job-registry\");\n\ntype PlatformWorkflow = {\n startWorkflow: (name: string, args?: unknown, options?: StartWorkflowOptions) => Promise<string>;\n execJobFunction: (name: string, args?: unknown, options?: ExecJobFunctionOptions) => unknown;\n};\n\ntype GlobalWithRegistry = typeof globalThis & {\n [JOB_REGISTRY_KEY]?: Map<string, RegisteredJobBody>;\n tailor?: { workflow?: PlatformWorkflow };\n};\n\nfunction jobs(): Map<string, RegisteredJobBody> {\n const g = globalThis as GlobalWithRegistry;\n let map = g[JOB_REGISTRY_KEY];\n if (!map) {\n map = new Map();\n g[JOB_REGISTRY_KEY] = map;\n }\n return map;\n}\n\n/**\n * Register a job body keyed by job name. Called as a side effect by\n * `createWorkflowJob` so `runWorkflowLocally()` can execute dependent job\n * bodies when `globalThis.tailor.workflow.execJobFunction(name, args)` is invoked.\n *\n * In production builds the bundler rewrites `.start()` calls so this registry\n * is never read; the gated write is dropped as dead code.\n * @param name - Job name\n * @param body - Job body function\n */\nexport function registerJob(name: string, body: RegisteredJobBody): void {\n jobs().set(name, body);\n}\n\n/**\n * Look up a registered job body by name.\n * @param name - Job name\n * @returns The registered body, or undefined when no job is registered\n */\nexport function getRegisteredJob(name: string): RegisteredJobBody | undefined {\n return jobs().get(name);\n}\n\nfunction currentPlatformWorkflow(): PlatformWorkflow | undefined {\n // globalThis may not have the tailor property at runtime\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n return (globalThis as GlobalWithRegistry).tailor?.workflow;\n}\n\nfunction requirePlatformWorkflow(): PlatformWorkflow {\n const workflow = currentPlatformWorkflow();\n if (!workflow) {\n throw new Error(\n \"tailor.workflow is not available. Run tests in the `tailor-runtime` Vitest environment and use mockWorkflow(), or use runWorkflowLocally() from @tailor-platform/sdk/vitest for local workflow execution.\",\n );\n }\n return workflow;\n}\n\n// A valid placeholder UUID, so callers that validate the execution id behave the\n// same locally as against the platform.\nexport const START_DEFAULT = \"00000000-0000-4000-8000-000000000000\";\n\n// `.start()` routes through the installed `tailor.workflow` shim. Local body\n// execution is intentionally available only through `runWorkflowLocally()`.\n// Preserve arity: the shim sees a 2-argument call when the caller supplied no\n// options, and a 3-argument call otherwise, mirroring the bundler rewrite so\n// mocks observe the same shape in local execution and in bundled workflows.\nexport function dispatchStartJob(\n name: string,\n args?: unknown,\n options?: ExecJobFunctionOptions,\n): unknown {\n const workflow = requirePlatformWorkflow();\n // oxlint-disable-next-line prefer-rest-params\n return arguments.length >= 3\n ? workflow.execJobFunction(name, args, options)\n : workflow.execJobFunction(name, args);\n}\n\n// Accepts `unknown` because the SDK-side `.start()` accepts a wider options\n// shape than the platform surface (e.g. `authInvoker` may be a machine-user\n// name string that the bundler normalizes at build time). Local execution\n// forwards the value verbatim; only the bundled path enforces the platform\n// contract.\nexport function dispatchStartWorkflow(\n name: string,\n args?: unknown,\n options?: { invoker?: unknown },\n): Promise<string> {\n const workflow = requirePlatformWorkflow();\n // oxlint-disable-next-line prefer-rest-params\n if (arguments.length < 3) {\n return workflow.startWorkflow(name, args);\n }\n return workflow.startWorkflow(\n name,\n args,\n options?.invoker === undefined\n ? (options as StartWorkflowOptions | undefined)\n : ({ authInvoker: options.invoker } as StartWorkflowOptions),\n );\n}\n"],"mappings":"AAaA,MAAM,EAAkC,OAAO,IAAI,kCAAkC,EAYrF,SAAS,MAAuC,CAC9C,IAAM,EAAI,WACN,EAAM,EAAE,GAKZ,OAJK,IACH,kBAAM,IAAI,IACV,EAAE,GAAoB,GAEjB,CACT,CAYA,SAAgB,YAAY,EAAc,EAA+B,CACvE,KAAK,CAAC,CAAC,IAAI,EAAM,CAAI,CACvB,CAOA,SAAgB,iBAAiB,EAA6C,CAC5E,OAAO,KAAK,CAAC,CAAC,IAAI,CAAI,CACxB,CAEA,SAAS,yBAAwD,CAG/D,OAAQ,WAAkC,QAAQ,QACpD,CAEA,SAAS,yBAA4C,CACnD,IAAM,EAAW,wBAAwB,EACzC,GAAI,CAAC,EACH,MAAU,MACR,2MACF,EAEF,OAAO,CACT,CAIA,MAAa,EAAgB,uCAO7B,SAAgB,iBACd,EACA,EACA,EACS,CACT,IAAM,EAAW,wBAAwB,EAEzC,OAAO,UAAU,QAAU,EACvB,EAAS,gBAAgB,EAAM,EAAM,CAAO,EAC5C,EAAS,gBAAgB,EAAM,CAAI,CACzC,CAOA,SAAgB,sBACd,EACA,EACA,EACiB,CACjB,IAAM,EAAW,wBAAwB,EAKzC,OAHI,UAAU,OAAS,EACd,EAAS,cAAc,EAAM,CAAI,EAEnC,EAAS,cACd,EACA,EACA,GAAS,UAAY,IAAA,GAChB,EACA,CAAE,YAAa,EAAQ,OAAQ,CACtC,CACF"}
1
+ {"version":3,"file":"registry-BijIDRVA.mjs","names":["JOB_REGISTRY_KEY","g","map","name","body","workflow","START_DEFAULT","args","options"],"sources":["../src/configure/services/workflow/registry.ts"],"sourcesContent":["import type { TailorEnv, TailorPrincipal } from \"#/runtime/types\";\nimport type { ExecJobFunctionOptions, StartWorkflowOptions } from \"#/runtime/workflow\";\n\n/**\n * Body signature shared by workflow jobs at registry-write time.\n * The user's `createWorkflowJob`/`createWorkflow` body uses concrete types,\n * but the registry erases them for storage.\n */\nexport type RegisteredJobBody = (\n args: unknown,\n context: { env: TailorEnv; invoker: TailorPrincipal | null },\n) => unknown | Promise<unknown>;\n\nconst JOB_REGISTRY_KEY: unique symbol = Symbol.for(\"tailor-platform/sdk:job-registry\");\n\ntype PlatformWorkflow = {\n startWorkflow: (name: string, args?: unknown, options?: StartWorkflowOptions) => Promise<string>;\n execJobFunction: (name: string, args?: unknown, options?: ExecJobFunctionOptions) => unknown;\n};\n\ntype GlobalWithRegistry = typeof globalThis & {\n [JOB_REGISTRY_KEY]?: Map<string, RegisteredJobBody>;\n tailor?: { workflow?: PlatformWorkflow };\n};\n\nfunction jobs(): Map<string, RegisteredJobBody> {\n const g = globalThis as GlobalWithRegistry;\n let map = g[JOB_REGISTRY_KEY];\n if (!map) {\n map = new Map();\n g[JOB_REGISTRY_KEY] = map;\n }\n return map;\n}\n\n/**\n * Register a job body keyed by job name. Called as a side effect by\n * `createWorkflowJob` so `runWorkflowLocally()` can execute dependent job\n * bodies when `globalThis.tailor.workflow.execJobFunction(name, args)` is invoked.\n *\n * In production builds the bundler rewrites `.start()` calls so this registry\n * is never read; the gated write is dropped as dead code.\n * @param name - Job name\n * @param body - Job body function\n */\nexport function registerJob(name: string, body: RegisteredJobBody): void {\n jobs().set(name, body);\n}\n\n/**\n * Look up a registered job body by name.\n * @param name - Job name\n * @returns The registered body, or undefined when no job is registered\n */\nexport function getRegisteredJob(name: string): RegisteredJobBody | undefined {\n return jobs().get(name);\n}\n\nfunction currentPlatformWorkflow(): PlatformWorkflow | undefined {\n // globalThis may not have the tailor property at runtime\n // oxlint-disable-next-line typescript/no-unnecessary-condition\n return (globalThis as GlobalWithRegistry).tailor?.workflow;\n}\n\nfunction requirePlatformWorkflow(): PlatformWorkflow {\n const workflow = currentPlatformWorkflow();\n if (!workflow) {\n throw new Error(\n \"tailor.workflow is not available. Run tests in the `tailor-runtime` Vitest environment and use mockWorkflow(), or use runWorkflowLocally() from @tailor-platform/sdk/vitest for local workflow execution.\",\n );\n }\n return workflow;\n}\n\n// A valid placeholder UUID, so callers that validate the execution id behave the\n// same locally as against the platform.\nexport const START_DEFAULT = \"00000000-0000-4000-8000-000000000000\";\n\n// `.start()` routes through the installed `tailor.workflow` shim. Local body\n// execution is intentionally available only through `runWorkflowLocally()`.\n// Preserve arity: the shim sees a 2-argument call when the caller supplied no\n// options, and a 3-argument call otherwise, mirroring the bundler rewrite so\n// mocks observe the same shape in local execution and in bundled workflows.\nexport function dispatchStartJob(\n name: string,\n args?: unknown,\n options?: ExecJobFunctionOptions,\n): unknown {\n const workflow = requirePlatformWorkflow();\n // oxlint-disable-next-line prefer-rest-params\n return arguments.length >= 3\n ? workflow.execJobFunction(name, args, options)\n : workflow.execJobFunction(name, args);\n}\n\n// Accepts `unknown` because the SDK-side `.start()` accepts a wider options\n// shape than the platform surface (e.g. `authInvoker` may be a machine-user\n// name string that the bundler normalizes at build time). Local execution\n// forwards the value verbatim; only the bundled path enforces the platform\n// contract.\nexport function dispatchStartWorkflow(\n name: string,\n args?: unknown,\n options?: { invoker?: unknown },\n): Promise<string> {\n const workflow = requirePlatformWorkflow();\n // oxlint-disable-next-line prefer-rest-params\n if (arguments.length < 3) {\n return workflow.startWorkflow(name, args);\n }\n return workflow.startWorkflow(\n name,\n args,\n options?.invoker === undefined\n ? (options as StartWorkflowOptions | undefined)\n : ({ authInvoker: options.invoker } as StartWorkflowOptions),\n );\n}\n"],"mappings":"AAaA,MAAMA,EAAkC,OAAO,IAAI,kCAAkC,EAYrF,SAAS,MAAuC,CAC9C,IAAMC,EAAI,WACNC,EAAMD,EAAED,GAKZ,OAJKE,IACH,kBAAM,IAAI,IACV,EAAEF,GAAoBE,GAEjBA,CACT,CAYA,SAAgB,YAAY,EAAc,EAA+B,CACvE,KAAK,CAAC,CAAC,IAAIC,EAAMC,CAAI,CACvB,CAOA,SAAgB,iBAAiB,EAA6C,CAC5E,OAAO,KAAK,CAAC,CAAC,IAAID,CAAI,CACxB,CAEA,SAAS,yBAAwD,CAG/D,OAAQ,WAAkC,QAAQ,QACpD,CAEA,SAAS,yBAA4C,CACnD,IAAME,EAAW,wBAAwB,EACzC,GAAI,CAACA,EACH,MAAU,MACR,2MACF,EAEF,OAAOA,CACT,CAIA,MAAaC,EAAgB,uCAO7B,SAAgB,iBACd,EACA,EACA,EACS,CACT,IAAMD,EAAW,wBAAwB,EAEzC,OAAO,UAAU,QAAU,EACvBA,EAAS,gBAAgBF,EAAMI,EAAMC,CAAO,EAC5CH,EAAS,gBAAgBF,EAAMI,CAAI,CACzC,CAOA,SAAgB,sBACd,EACA,EACA,EACiB,CACjB,IAAMF,EAAW,wBAAwB,EAKzC,OAHI,UAAU,OAAS,EACdA,EAAS,cAAcF,EAAMI,CAAI,EAEnCF,EAAS,cACdF,EACAI,EACAC,GAAS,UAAY,IAAA,GAChBA,EACA,CAAE,YAAaA,EAAQ,OAAQ,CACtC,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"repl-editor-DGJBfIt1.mjs","names":[],"sources":["../src/cli/query/repl-editor.ts"],"sourcesContent":["import { GraphQLError, Lexer, Source, TokenKind } from \"graphql\";\nimport { getSegments } from \"sql-highlight\";\nimport { assertDefined } from \"#/utils/assert\";\nimport type { TransformEvent, TransformState } from \"@toiroakr/read-multiline\";\n\n// ANSI colour sequences. Kept inline (rather than going through `node:util`\n// styleText) so that tests running outside a TTY still produce deterministic\n// escape sequences regardless of detected colour support.\nconst RESET = \"\\x1b[0m\";\nconst BLUE = \"\\x1b[34m\";\nconst MAGENTA = \"\\x1b[35m\";\nconst YELLOW = \"\\x1b[33m\";\nconst BRIGHT_GREEN = \"\\x1b[92m\";\nconst CYAN = \"\\x1b[36m\";\nconst BRIGHT_BLUE = \"\\x1b[94m\";\nconst BOLD_CYAN = \"\\x1b[1;36m\";\nconst BOLD_MAGENTA = \"\\x1b[1;35m\";\nconst ITALIC_YELLOW = \"\\x1b[3;33m\";\nconst GREEN = \"\\x1b[32m\";\nconst DIM = \"\\x1b[90m\";\nconst DIM_YELLOW = \"\\x1b[2;33m\";\n\nconst SQL_STYLE_MAP: Record<string, string> = {\n keyword: BLUE,\n function: MAGENTA,\n identifier: YELLOW,\n string: BRIGHT_GREEN,\n number: CYAN,\n bracket: DIM_YELLOW,\n special: DIM,\n};\n\n/**\n * Highlight a single SQL line using the `sql-highlight` tokenizer.\n * @param line - SQL text for a single editor line\n * @returns ANSI-decorated line safe for terminal output\n */\nexport function highlightSqlLine(line: string): string {\n const segments = getSegments(line);\n let result = \"\";\n for (const seg of segments) {\n const style = SQL_STYLE_MAP[seg.name];\n result += style ? style + seg.content + RESET : seg.content;\n }\n return result;\n}\n\nconst GQL_KEYWORDS = new Set([\n \"query\",\n \"mutation\",\n \"subscription\",\n \"fragment\",\n \"on\",\n \"type\",\n \"input\",\n \"enum\",\n \"interface\",\n \"union\",\n \"scalar\",\n \"extend\",\n \"schema\",\n \"directive\",\n \"implements\",\n]);\n\n// Keywords that introduce a definition name (next NAME token is the def).\nconst GQL_DEF_KEYWORDS = new Set([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\nconst GQL_BUILTINS = new Set([\"true\", \"false\", \"null\"]);\n\n/**\n * Highlight a single GraphQL line using the official `graphql` Lexer. Tracks\n * paren depth and the previous token to provide semantic-level colouring\n * (field names vs argument names vs types).\n * @param line - GraphQL text for a single editor line\n * @returns ANSI-decorated line, or the input unchanged when the lexer rejects it\n */\nexport function highlightGraphqlLine(line: string): string {\n if (line.trimStart().startsWith(\"#\")) {\n return `${DIM}${line}${RESET}`;\n }\n\n try {\n const source = new Source(line);\n const lexer = new Lexer(source);\n let result = \"\";\n let pos = 0;\n\n let parenDepth = 0;\n let prevKind: string = \"\";\n let prevText = \"\";\n let afterColon = false;\n\n let token = lexer.advance();\n while (token.kind !== TokenKind.EOF) {\n if (token.start > pos) {\n result += line.slice(pos, token.start);\n }\n\n const text = line.slice(token.start, token.end);\n switch (token.kind) {\n case TokenKind.NAME: {\n if (prevKind === TokenKind.DOLLAR) {\n result += `${MAGENTA}${text}${RESET}`;\n } else if (prevKind === TokenKind.AT) {\n result += `${BOLD_MAGENTA}${text}${RESET}`;\n } else if (GQL_BUILTINS.has(text) || GQL_KEYWORDS.has(text)) {\n result += `${BLUE}${text}${RESET}`;\n } else if (GQL_DEF_KEYWORDS.has(prevText)) {\n result += `${BOLD_CYAN}${text}${RESET}`;\n } else if (afterColon) {\n result += `${CYAN}${text}${RESET}`;\n } else if (parenDepth > 0) {\n result += `${ITALIC_YELLOW}${text}${RESET}`;\n } else {\n result += `${BRIGHT_BLUE}${text}${RESET}`;\n }\n afterColon = false;\n break;\n }\n case TokenKind.INT:\n case TokenKind.FLOAT:\n result += `${BLUE}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.STRING:\n case TokenKind.BLOCK_STRING:\n result += `${GREEN}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.DOLLAR:\n result += `${MAGENTA}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.AT:\n result += `${BOLD_MAGENTA}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.BRACE_L:\n case TokenKind.BRACE_R:\n result += `${YELLOW}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.PAREN_L:\n parenDepth += 1;\n result += `${DIM_YELLOW}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.PAREN_R:\n parenDepth = Math.max(0, parenDepth - 1);\n result += `${DIM_YELLOW}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.BRACKET_L:\n case TokenKind.BRACKET_R:\n result += `${DIM_YELLOW}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.COLON:\n result += `${DIM}${text}${RESET}`;\n afterColon = true;\n break;\n case TokenKind.BANG:\n case TokenKind.EQUALS:\n case TokenKind.PIPE:\n case TokenKind.AMP:\n case TokenKind.SPREAD:\n result += `${DIM}${text}${RESET}`;\n afterColon = false;\n break;\n default:\n result += text;\n afterColon = false;\n }\n\n prevKind = token.kind;\n prevText = text;\n pos = token.end;\n token = lexer.advance();\n }\n\n if (pos < line.length) {\n result += line.slice(pos);\n }\n return result;\n } catch (error) {\n // The lexer throws GraphQLError on partial or invalid input (e.g. an\n // unterminated string while the user is still typing). Fall back to the\n // raw line so the editor keeps rendering without colour until the input\n // is valid. Any other error is a real bug and should surface.\n if (error instanceof GraphQLError) {\n return line;\n }\n throw error;\n }\n}\n\nconst BRACKET_PAIRS: Record<string, string> = { \"(\": \")\", \"[\": \"]\", \"{\": \"}\" };\nconst CLOSE_BRACKETS = new Set(Object.values(BRACKET_PAIRS));\n\n/**\n * Apply auto-close brackets and auto-indent on newline. Works for both SQL\n * and GraphQL because both languages share the `()`, `[]`, `{}` bracket set.\n * @param state - Editor state after the last edit\n * @param event - Event describing the edit that just occurred\n * @returns A new editor state to apply, or `undefined` to leave the state unchanged\n */\nexport function replTransform(\n state: TransformState,\n event: TransformEvent,\n): TransformState | undefined {\n const { lines, row, col } = state;\n\n if (event.type === \"insert\" && event.char in BRACKET_PAIRS) {\n const close = BRACKET_PAIRS[event.char];\n const line = assertDefined(lines[row], `line at row ${row} missing`);\n const newLine = line.slice(0, col) + close + line.slice(col);\n return { lines: lines.with(row, newLine), row, col };\n }\n\n if (event.type === \"insert\" && CLOSE_BRACKETS.has(event.char)) {\n const line = assertDefined(lines[row], `line at row ${row} missing`);\n if (line[col] === event.char) {\n const newLine = line.slice(0, col) + line.slice(col + 1);\n return { lines: lines.with(row, newLine), row, col };\n }\n }\n\n if (event.type === \"backspace\") {\n const line = assertDefined(lines[row], `line at row ${row} missing`);\n const beforeCursor = line.slice(0, col);\n if (beforeCursor.length >= 1 && /^ +$/.test(beforeCursor)) {\n const newIndent = beforeCursor.slice(0, -1);\n const newLine = newIndent + line.slice(col);\n return { lines: lines.with(row, newLine), row, col: newIndent.length };\n }\n }\n\n if (event.type === \"newline\" && row > 0) {\n const prevLine = assertDefined(lines[row - 1], `line at row ${row - 1} missing`);\n const baseIndent = prevLine.match(/^(\\s*)/)?.[1] ?? \"\";\n const endsWithOpen = /[{([]$/.test(prevLine.trimEnd());\n const currentLine = assertDefined(lines[row], `line at row ${row} missing`);\n const startsWithClose = /^[}\\])]/.test(currentLine.trimStart());\n\n if (endsWithOpen && startsWithClose) {\n // Bracket expansion: the cursor sits between a matching open/close\n // pair (e.g. `{|}`). Expand into a three-line block with the cursor\n // on an indented middle line.\n const innerIndent = baseIndent + \" \";\n const newLines = lines\n .with(row, innerIndent)\n .toSpliced(row + 1, 0, baseIndent + currentLine.trimStart());\n return { lines: newLines, row, col: innerIndent.length };\n }\n if (endsWithOpen) {\n // A lone open bracket on the previous line: drop an extra indent for\n // the new line and auto-insert the matching closing bracket below.\n const openChar = prevLine.trimEnd().slice(-1);\n const closeChar = BRACKET_PAIRS[openChar] ?? \"}\";\n const indent = baseIndent + \" \";\n const newLines = lines\n .with(row, indent + currentLine)\n .toSpliced(row + 1, 0, baseIndent + closeChar);\n return { lines: newLines, row, col: indent.length };\n }\n if (baseIndent && col === 0) {\n return { lines: lines.with(row, baseIndent + currentLine), row, col: baseIndent.length };\n }\n }\n\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"repl-editor-DGJBfIt1.mjs","names":["assertDefined","RESET","BLUE","MAGENTA","YELLOW","CYAN","BOLD_MAGENTA","DIM","DIM_YELLOW","SQL_STYLE_MAP","segments","getSegments","line","result","seg","style","GQL_KEYWORDS","GQL_DEF_KEYWORDS","GQL_BUILTINS","source","Source","lexer","Lexer","pos","parenDepth","prevKind","prevText","afterColon","token","TokenKind","text","error","GraphQLError","BRACKET_PAIRS","CLOSE_BRACKETS","state","event","close","lines","row","newLine","col","beforeCursor","newIndent","prevLine","baseIndent","endsWithOpen","currentLine","startsWithClose","innerIndent","openChar","closeChar","indent"],"sources":["../src/cli/query/repl-editor.ts"],"sourcesContent":["import { GraphQLError, Lexer, Source, TokenKind } from \"graphql\";\nimport { getSegments } from \"sql-highlight\";\nimport { assertDefined } from \"#/utils/assert\";\nimport type { TransformEvent, TransformState } from \"@toiroakr/read-multiline\";\n\n// ANSI colour sequences. Kept inline (rather than going through `node:util`\n// styleText) so that tests running outside a TTY still produce deterministic\n// escape sequences regardless of detected colour support.\nconst RESET = \"\\x1b[0m\";\nconst BLUE = \"\\x1b[34m\";\nconst MAGENTA = \"\\x1b[35m\";\nconst YELLOW = \"\\x1b[33m\";\nconst BRIGHT_GREEN = \"\\x1b[92m\";\nconst CYAN = \"\\x1b[36m\";\nconst BRIGHT_BLUE = \"\\x1b[94m\";\nconst BOLD_CYAN = \"\\x1b[1;36m\";\nconst BOLD_MAGENTA = \"\\x1b[1;35m\";\nconst ITALIC_YELLOW = \"\\x1b[3;33m\";\nconst GREEN = \"\\x1b[32m\";\nconst DIM = \"\\x1b[90m\";\nconst DIM_YELLOW = \"\\x1b[2;33m\";\n\nconst SQL_STYLE_MAP: Record<string, string> = {\n keyword: BLUE,\n function: MAGENTA,\n identifier: YELLOW,\n string: BRIGHT_GREEN,\n number: CYAN,\n bracket: DIM_YELLOW,\n special: DIM,\n};\n\n/**\n * Highlight a single SQL line using the `sql-highlight` tokenizer.\n * @param line - SQL text for a single editor line\n * @returns ANSI-decorated line safe for terminal output\n */\nexport function highlightSqlLine(line: string): string {\n const segments = getSegments(line);\n let result = \"\";\n for (const seg of segments) {\n const style = SQL_STYLE_MAP[seg.name];\n result += style ? style + seg.content + RESET : seg.content;\n }\n return result;\n}\n\nconst GQL_KEYWORDS = new Set([\n \"query\",\n \"mutation\",\n \"subscription\",\n \"fragment\",\n \"on\",\n \"type\",\n \"input\",\n \"enum\",\n \"interface\",\n \"union\",\n \"scalar\",\n \"extend\",\n \"schema\",\n \"directive\",\n \"implements\",\n]);\n\n// Keywords that introduce a definition name (next NAME token is the def).\nconst GQL_DEF_KEYWORDS = new Set([\"query\", \"mutation\", \"subscription\", \"fragment\"]);\n\nconst GQL_BUILTINS = new Set([\"true\", \"false\", \"null\"]);\n\n/**\n * Highlight a single GraphQL line using the official `graphql` Lexer. Tracks\n * paren depth and the previous token to provide semantic-level colouring\n * (field names vs argument names vs types).\n * @param line - GraphQL text for a single editor line\n * @returns ANSI-decorated line, or the input unchanged when the lexer rejects it\n */\nexport function highlightGraphqlLine(line: string): string {\n if (line.trimStart().startsWith(\"#\")) {\n return `${DIM}${line}${RESET}`;\n }\n\n try {\n const source = new Source(line);\n const lexer = new Lexer(source);\n let result = \"\";\n let pos = 0;\n\n let parenDepth = 0;\n let prevKind: string = \"\";\n let prevText = \"\";\n let afterColon = false;\n\n let token = lexer.advance();\n while (token.kind !== TokenKind.EOF) {\n if (token.start > pos) {\n result += line.slice(pos, token.start);\n }\n\n const text = line.slice(token.start, token.end);\n switch (token.kind) {\n case TokenKind.NAME: {\n if (prevKind === TokenKind.DOLLAR) {\n result += `${MAGENTA}${text}${RESET}`;\n } else if (prevKind === TokenKind.AT) {\n result += `${BOLD_MAGENTA}${text}${RESET}`;\n } else if (GQL_BUILTINS.has(text) || GQL_KEYWORDS.has(text)) {\n result += `${BLUE}${text}${RESET}`;\n } else if (GQL_DEF_KEYWORDS.has(prevText)) {\n result += `${BOLD_CYAN}${text}${RESET}`;\n } else if (afterColon) {\n result += `${CYAN}${text}${RESET}`;\n } else if (parenDepth > 0) {\n result += `${ITALIC_YELLOW}${text}${RESET}`;\n } else {\n result += `${BRIGHT_BLUE}${text}${RESET}`;\n }\n afterColon = false;\n break;\n }\n case TokenKind.INT:\n case TokenKind.FLOAT:\n result += `${BLUE}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.STRING:\n case TokenKind.BLOCK_STRING:\n result += `${GREEN}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.DOLLAR:\n result += `${MAGENTA}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.AT:\n result += `${BOLD_MAGENTA}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.BRACE_L:\n case TokenKind.BRACE_R:\n result += `${YELLOW}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.PAREN_L:\n parenDepth += 1;\n result += `${DIM_YELLOW}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.PAREN_R:\n parenDepth = Math.max(0, parenDepth - 1);\n result += `${DIM_YELLOW}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.BRACKET_L:\n case TokenKind.BRACKET_R:\n result += `${DIM_YELLOW}${text}${RESET}`;\n afterColon = false;\n break;\n case TokenKind.COLON:\n result += `${DIM}${text}${RESET}`;\n afterColon = true;\n break;\n case TokenKind.BANG:\n case TokenKind.EQUALS:\n case TokenKind.PIPE:\n case TokenKind.AMP:\n case TokenKind.SPREAD:\n result += `${DIM}${text}${RESET}`;\n afterColon = false;\n break;\n default:\n result += text;\n afterColon = false;\n }\n\n prevKind = token.kind;\n prevText = text;\n pos = token.end;\n token = lexer.advance();\n }\n\n if (pos < line.length) {\n result += line.slice(pos);\n }\n return result;\n } catch (error) {\n // The lexer throws GraphQLError on partial or invalid input (e.g. an\n // unterminated string while the user is still typing). Fall back to the\n // raw line so the editor keeps rendering without colour until the input\n // is valid. Any other error is a real bug and should surface.\n if (error instanceof GraphQLError) {\n return line;\n }\n throw error;\n }\n}\n\nconst BRACKET_PAIRS: Record<string, string> = { \"(\": \")\", \"[\": \"]\", \"{\": \"}\" };\nconst CLOSE_BRACKETS = new Set(Object.values(BRACKET_PAIRS));\n\n/**\n * Apply auto-close brackets and auto-indent on newline. Works for both SQL\n * and GraphQL because both languages share the `()`, `[]`, `{}` bracket set.\n * @param state - Editor state after the last edit\n * @param event - Event describing the edit that just occurred\n * @returns A new editor state to apply, or `undefined` to leave the state unchanged\n */\nexport function replTransform(\n state: TransformState,\n event: TransformEvent,\n): TransformState | undefined {\n const { lines, row, col } = state;\n\n if (event.type === \"insert\" && event.char in BRACKET_PAIRS) {\n const close = BRACKET_PAIRS[event.char];\n const line = assertDefined(lines[row], `line at row ${row} missing`);\n const newLine = line.slice(0, col) + close + line.slice(col);\n return { lines: lines.with(row, newLine), row, col };\n }\n\n if (event.type === \"insert\" && CLOSE_BRACKETS.has(event.char)) {\n const line = assertDefined(lines[row], `line at row ${row} missing`);\n if (line[col] === event.char) {\n const newLine = line.slice(0, col) + line.slice(col + 1);\n return { lines: lines.with(row, newLine), row, col };\n }\n }\n\n if (event.type === \"backspace\") {\n const line = assertDefined(lines[row], `line at row ${row} missing`);\n const beforeCursor = line.slice(0, col);\n if (beforeCursor.length >= 1 && /^ +$/.test(beforeCursor)) {\n const newIndent = beforeCursor.slice(0, -1);\n const newLine = newIndent + line.slice(col);\n return { lines: lines.with(row, newLine), row, col: newIndent.length };\n }\n }\n\n if (event.type === \"newline\" && row > 0) {\n const prevLine = assertDefined(lines[row - 1], `line at row ${row - 1} missing`);\n const baseIndent = prevLine.match(/^(\\s*)/)?.[1] ?? \"\";\n const endsWithOpen = /[{([]$/.test(prevLine.trimEnd());\n const currentLine = assertDefined(lines[row], `line at row ${row} missing`);\n const startsWithClose = /^[}\\])]/.test(currentLine.trimStart());\n\n if (endsWithOpen && startsWithClose) {\n // Bracket expansion: the cursor sits between a matching open/close\n // pair (e.g. `{|}`). Expand into a three-line block with the cursor\n // on an indented middle line.\n const innerIndent = baseIndent + \" \";\n const newLines = lines\n .with(row, innerIndent)\n .toSpliced(row + 1, 0, baseIndent + currentLine.trimStart());\n return { lines: newLines, row, col: innerIndent.length };\n }\n if (endsWithOpen) {\n // A lone open bracket on the previous line: drop an extra indent for\n // the new line and auto-insert the matching closing bracket below.\n const openChar = prevLine.trimEnd().slice(-1);\n const closeChar = BRACKET_PAIRS[openChar] ?? \"}\";\n const indent = baseIndent + \" \";\n const newLines = lines\n .with(row, indent + currentLine)\n .toSpliced(row + 1, 0, baseIndent + closeChar);\n return { lines: newLines, row, col: indent.length };\n }\n if (baseIndent && col === 0) {\n return { lines: lines.with(row, baseIndent + currentLine), row, col: baseIndent.length };\n }\n }\n\n return undefined;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"schema-CRtAmDcl.mjs","names":["parseInternal","parseFieldInternal"],"sources":["../src/configure/types/field.ts","../src/configure/services/tailordb/schema.ts"],"sourcesContent":["import type { EnumValue } from \"#/configure/types/field.types\";\n\nexport type AllowedValues = readonly [string | EnumValue, ...(string | EnumValue)[]];\n\n/**\n * Normalize allowed values into EnumValue objects with descriptions.\n * @param values - Allowed values as strings or EnumValue objects\n * @returns Normalized allowed values\n */\nexport function mapAllowedValues(values: AllowedValues): EnumValue[] {\n return values.map((value) => {\n if (typeof value === \"string\") {\n return { value, description: \"\" };\n }\n return { ...value, description: value.description ?? \"\" };\n });\n}\n\nexport type AllowedValuesOutput<V extends AllowedValues> = V[number] extends infer T\n ? T extends string\n ? T\n : T extends { value: infer K }\n ? K\n : never\n : never;\n","import { cloneDeep } from \"es-toolkit\";\nimport {\n type AllowedValues,\n type AllowedValuesOutput,\n mapAllowedValues,\n} from \"#/configure/types/field\";\nimport {\n parseInternal as parseFieldInternal,\n type FieldParseArgs,\n type FieldParseInternalArgs,\n} from \"#/runtime/field-parse\";\nimport { brandValue } from \"#/utils/brand\";\nimport type {\n FieldOptions,\n FieldOutput,\n TailorFieldType,\n TailorToTs,\n FieldValidateInput,\n} from \"#/configure/types/field.types\";\nimport type {\n PrecompiledScriptExprKey,\n PrecompiledScriptExprMap,\n} from \"#/parser/service/tailordb/types\";\nimport type { PluginAttachment, PluginConfigs, PluginFieldExtensions } from \"#/plugin/types\";\nimport type { InferredAttributes } from \"#/runtime/types\";\nimport type {\n output,\n InferFieldsOutput,\n IsUnion,\n TypeLevelError,\n UnionToIntersection,\n} from \"#/types/helpers\";\nimport type { RawPermissions } from \"#/types/tailordb.generated\";\nimport type { TailorTypeGqlPermission, TailorTypePermission } from \"./permission\";\nimport type {\n TailorDBField as TailorDBFieldBase,\n TailorDBType as TailorDBTypeBase,\n DBFieldMetadata,\n DefinedDBFieldMetadata,\n SerialConfig,\n IndexDef,\n TailorDBTypeMetadata,\n RawRelationConfig,\n RelationType,\n Hook,\n TypeHook,\n UpdateHookFn,\n ExcludeNestedDBFields,\n ExcludeHookedDBFields,\n ExcludeDefaultedDBFields,\n TypeFeatures,\n TypeValidateFn,\n} from \"./types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// Erased DB fields stay assignable across builder method-state changes.\n// oxlint-disable-next-line no-explicit-any\ntype AnyBuilderMethod = any;\n\nexport type TailorAnyDBField = Omit<\n TailorDBFieldBase<AnyBuilderMethod, AnyBuilderMethod>,\n \"fields\"\n> & {\n readonly fields: Record<string, AnyBuilderMethod>;\n _metadata: DBFieldMetadata;\n parse: AnyBuilderMethod;\n readonly typeName: TypeLevelError<string>;\n description: AnyBuilderMethod;\n relation: AnyBuilderMethod;\n index: AnyBuilderMethod;\n unique: AnyBuilderMethod;\n vector: AnyBuilderMethod;\n default: AnyBuilderMethod;\n hooks: AnyBuilderMethod;\n validate: AnyBuilderMethod;\n serial: AnyBuilderMethod;\n clone: AnyBuilderMethod;\n};\n\n// Helper alias\n// oxlint-disable-next-line no-explicit-any\nexport type TailorAnyDBType = TailorDBType<any, any, any>;\n\ntype IsAny<T> = 0 extends 1 & T ? true : false;\ntype DBFieldTypeNameMethod<Defined extends DefinedDBFieldMetadata> =\n IsAny<Defined> extends true\n ? TypeLevelError<string>\n : TypeLevelError<\"typeName cannot be used on TailorDB fields\">;\n\ntype WithDBFieldDescription<Defined> = Defined & { description: true };\ntype WithDBFieldRelation<Defined, S extends RelationType | RelationSelfConfig> = S extends\n | \"oneToOne\"\n | \"1-1\"\n ? Defined & { unique: true; index: true; relation: true }\n : S extends { type: \"oneToOne\" | \"1-1\" }\n ? Defined & { unique: true; index: true; relation: true }\n : Defined & { index: true; relation: true };\ntype WithDBFieldIndex<Defined> = Defined & { index: true };\ntype WithDBFieldUnique<Defined> = Defined & { unique: true; index: true };\ntype WithDBFieldVector<Defined> = Defined & { vector: true };\ntype WithDBFieldHooks<Defined, H> = Defined & {\n hooks: {\n create: H extends { create: unknown } ? true : false;\n update: H extends { update: unknown } ? true : false;\n };\n serial: false;\n};\ntype WithDBFieldDefault<Defined> = Defined & { default: true };\ntype WithDBFieldValidate<Defined> = Defined & { validate: true };\ntype WithDBFieldSerial<Defined> = Defined & {\n serial: true;\n hooks: { create: false; update: false };\n};\ntype WithDBFieldCloneOptions<Defined extends DefinedDBFieldMetadata, NewOpt extends FieldOptions> =\n IsAny<Defined> extends true\n ? Defined\n : Omit<Defined, \"array\"> & {\n array: NewOpt extends { array: true }\n ? true\n : NewOpt extends { array: false }\n ? false\n : Defined[\"array\"];\n };\ntype NonNullableDBFieldOutput<Output> = Exclude<Output, null>;\ntype DBFieldScalarOutput<Output> =\n NonNullableDBFieldOutput<Output> extends (infer Item)[] ? Item : NonNullableDBFieldOutput<Output>;\ntype DBFieldCloneArrayOutput<Output, NewOpt extends FieldOptions> = NewOpt extends {\n array: true;\n}\n ? DBFieldScalarOutput<Output>[]\n : NewOpt extends { array: false }\n ? DBFieldScalarOutput<Output>\n : NonNullableDBFieldOutput<Output>;\ntype DBFieldCloneOutput<Output, NewOpt extends FieldOptions> = NewOpt extends { optional: true }\n ? DBFieldCloneArrayOutput<Output, NewOpt> | null\n : NewOpt extends { optional: false }\n ? DBFieldCloneArrayOutput<Output, NewOpt>\n : null extends Output\n ? DBFieldCloneArrayOutput<Output, NewOpt> | null\n : DBFieldCloneArrayOutput<Output, NewOpt>;\ntype DBFieldCloneOptions<Defined extends DefinedDBFieldMetadata> = Omit<FieldOptions, \"array\"> & {\n array?: Defined extends { validate: unknown } ? Defined[\"array\"] : boolean;\n};\ntype InvalidValidatedArrayCloneKeys<\n Fields extends Record<string, TailorAnyDBField>,\n K extends keyof Fields,\n Opt extends FieldOptions,\n> = Opt extends { array: infer ArrayOption }\n ? {\n [P in K]: Fields[P] extends TailorDBField<infer Defined, infer _Output>\n ? Defined extends { validate: unknown }\n ? ArrayOption extends Defined[\"array\"]\n ? never\n : P\n : never\n : never;\n }[K]\n : never;\ntype DBFieldsCloneOptionsGuard<\n Fields extends Record<string, TailorAnyDBField>,\n K extends keyof Fields,\n Opt extends FieldOptions,\n> = [InvalidValidatedArrayCloneKeys<Fields, K, Opt>] extends [never]\n ? unknown\n : TypeLevelError<\"array cannot be changed on fields with custom validation\">;\ntype DefinedDBTypeMetadata = {\n hooks?: true;\n validate?: true;\n features?: true;\n indexes?: true;\n files?: true;\n permission?: true;\n gqlPermission?: true;\n description?: true;\n};\ntype WithDBTypeMetadata<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n> = Defined & Record<Key, true>;\ntype DBTypeDuplicateInputGuard<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n Input,\n Message extends string,\n> =\n IsAny<Defined> extends true\n ? Input\n : Defined extends Record<Key, unknown>\n ? TypeLevelError<Message>\n : Input;\ntype DBTypeDuplicateRestGuard<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n Input extends unknown[],\n Message extends string,\n> =\n IsAny<Defined> extends true\n ? Input\n : Defined extends Record<Key, unknown>\n ? [TypeLevelError<Message>, ...TypeLevelError<Message>[]]\n : Input;\ntype FileKeyConflictError<\n Fields extends Record<string, TailorAnyDBField>,\n User extends object,\n> = Partial<\n Record<\n keyof output<TailorDBType<Fields, User>> & string,\n TypeLevelError<\"file keys cannot use existing field names\">\n >\n>;\n// A plugin id with no PluginFieldExtensions entry contributes no keys — using\n// Record<never, never> (rather than Record<string, never>) keeps its `keyof`\n// empty so it can't spuriously collide with every existing field.\ntype PluginFieldExtensionFor<\n Fields extends Record<string, TailorAnyDBField>,\n Id extends string,\n IdConfig,\n> = Id extends keyof PluginFieldExtensions<keyof Fields & string, IdConfig>\n ? PluginFieldExtensions<keyof Fields & string, IdConfig>[Id]\n : Record<never, never>;\ntype PluginFieldExtensionsUnion<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n> = {\n [Id in keyof Config & string]: PluginFieldExtensionFor<Fields, Id, Config[Id]>;\n}[keyof Config & string];\ntype AllExtensionKeys<Ext> = Ext extends unknown ? keyof Ext : never;\n// True when plugin `Id` has a PluginFieldExtensions entry whose type does not\n// extend Record<string, TailorAnyDBField> — e.g. a plugin author registered\n// a non-record shape by mistake. Checked ahead of PluginFieldConflict, since\n// `keyof` on a non-record shape (e.g. `keyof string`) would otherwise produce\n// nonsense candidate keys for the collision check.\n// `Id extends string` (not `keyof Config & string`) deliberately: a mapped\n// type's key variable, used inside a nested conditional branch and then\n// intersected with `string`, does not structurally satisfy a `keyof Config &\n// string`-constrained type parameter under `skipLibCheck: false` (verified\n// against a standalone consumer — TS2344 on both call sites otherwise, even\n// for code that never calls .plugin()). `Config[Id & keyof Config]` recovers\n// the same indexed-access result without that constraint.\ntype PluginFieldExtensionShapeError<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n> =\n PluginFieldExtensionFor<Fields, Id, Config[Id & keyof Config]> extends Record<\n string,\n TailorAnyDBField\n >\n ? false\n : true;\n// True when the fields plugin `Id` would inject collide with an existing\n// field, or with a field injected by another plugin id attached in the same\n// .plugin() call.\ntype PluginFieldConflict<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n FileKeys extends string,\n> = [\n AllExtensionKeys<PluginFieldExtensionFor<Fields, Id, Config[Id & keyof Config]>> &\n (\n | keyof Fields\n | FileKeys\n | AllExtensionKeys<PluginFieldExtensionsUnion<Fields, Omit<Config, Id>>>\n ),\n] extends [never]\n ? false\n : true;\n// Rejects a property on the caller's config literal for plugin `Id` that\n// isn't part of its registered PluginConfigs shape. `Config` is captured by\n// a `const` type parameter from the argument itself, so intersecting the\n// success branch with `PluginConfigs<...>[Id]` alone would not trigger\n// TypeScript's excess-property check — that check only fires when an object\n// literal is validated directly against a target type, not after its shape\n// has already been inferred through a generic parameter.\ntype PluginConfigExcessProps<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n> = Record<\n Exclude<\n keyof Config[Id & keyof Config],\n keyof PluginConfigs<keyof Fields & string>[Id & keyof PluginConfigs<keyof Fields & string>]\n >,\n never\n>;\ntype PluginExtendedFields<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n> =\n IsAny<Fields> extends true\n ? Fields\n : // For `Config = {}`, PluginFieldExtensionsUnion is `never`, and\n // UnionToIntersection<never> is `unknown` (not `never`), so this\n // correctly reduces to `Fields` — see the \"empty config\" test.\n Fields & UnionToIntersection<PluginFieldExtensionsUnion<Fields, Config>>;\n// Validates each key of the passed config object against PluginConfigs and\n// flags field-extension conflicts (see PluginFieldConflict above), keeping\n// type errors localized to the offending plugin id instead of requiring\n// every registered plugin id to be present (as a `P extends keyof\n// PluginConfigs<...>` generic would need, defeating inference of which\n// plugin ids were actually passed).\ntype PluginConfigGuard<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n FileKeys extends string,\n> =\n IsAny<Fields> extends true\n ? unknown\n : {\n [K in keyof Config]: K extends keyof PluginConfigs<keyof Fields & string>\n ? // A union config (e.g. from a ternary passed directly as a plugin's\n // value) distributes through PluginFieldExtensionFor's conditional\n // and silently collapses the injected field to `never` once\n // intersected back together — reject it here instead, before it\n // can produce that confusing downstream error.\n IsUnion<Config[K]> extends true\n ? TypeLevelError<\"plugin config must be a single object literal, not a union — assign the config to a variable first if it comes from a conditional expression\">\n : PluginFieldExtensionShapeError<Fields, Config, K & string> extends true\n ? TypeLevelError<\"PluginFieldExtensions entry must be a Record<string, TailorAnyDBField>\">\n : PluginFieldConflict<Fields, Config, K & string, FileKeys> extends true\n ? TypeLevelError<\"plugin field extension conflicts with an existing field, a file key declared via .files(), or another plugin's field\">\n : PluginConfigs<keyof Fields & string>[K] &\n PluginConfigExcessProps<Fields, Config, K & string>\n : TypeLevelError<\"unknown plugin id\">;\n };\ntype DBFieldDescriptionFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (description: string) => TailorDBField<WithDBFieldDescription<Defined>, Output, Nested>;\ntype DBFieldRelationFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = {\n <S extends RelationType, T extends TailorAnyDBType>(\n config: RelationConfig<S, T>,\n ): TailorDBField<WithDBFieldRelation<Defined, S>, Output, Nested>;\n <S extends RelationSelfConfig>(\n config: S,\n ): TailorDBField<WithDBFieldRelation<Defined, S>, Output, Nested>;\n};\ntype DBFieldIndexFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldIndex<Defined>, Output, Nested>;\ntype DBFieldUniqueFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldUnique<Defined>, Output, Nested>;\ntype DBFieldVectorFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldVector<Defined>, Output, Nested>;\ntype DBFieldHooksFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = <\n const H extends Hook<\n Output,\n Defined extends { default: true } ? Output | null | undefined : Output\n >,\n>(\n hooks: H,\n) => TailorDBField<WithDBFieldHooks<Defined, H>, Output, Nested>;\ntype DBFieldValidateFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n ...validate: FieldValidateInput<Output>[]\n) => TailorDBField<WithDBFieldValidate<Defined>, Output, Nested>;\ntype DBFieldSerialFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n config: SerialConfig<Defined[\"type\"] & (\"integer\" | \"string\")>,\n) => TailorDBField<WithDBFieldSerial<Defined>, Output, Nested>;\ntype DBFieldDescriptionMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldDescriptionFn<Defined, Output, Nested>\n : Defined extends { description: unknown }\n ? TypeLevelError<\".description() has already been set\">\n : DBFieldDescriptionFn<Defined, Output, Nested>;\ntype DBFieldRelationMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldRelationFn<Defined, Output, Nested>\n : Defined extends { relation: unknown }\n ? TypeLevelError<\".relation() has already been set\">\n : DBFieldRelationFn<Defined, Output, Nested>;\ntype DBFieldArrayCheck<A extends boolean, Ok, Msg extends string> = A extends true\n ? TypeLevelError<Msg>\n : Ok;\ntype DBFieldIndexMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldIndexFn<Defined, Output, Nested>\n : Defined extends { index: unknown }\n ? TypeLevelError<\".index() has already been set\">\n : DBFieldArrayCheck<\n Defined[\"array\"],\n DBFieldIndexFn<Defined, Output, Nested>,\n \"index cannot be set on array fields\"\n >;\ntype DBFieldUniqueMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldUniqueFn<Defined, Output, Nested>\n : Defined extends { unique: unknown }\n ? TypeLevelError<\".unique() has already been set\">\n : DBFieldArrayCheck<\n Defined[\"array\"],\n DBFieldUniqueFn<Defined, Output, Nested>,\n \"unique cannot be set on array fields\"\n >;\ntype DBFieldVectorMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldVectorFn<Defined, Output, Nested>\n : Defined extends { vector: unknown }\n ? TypeLevelError<\".vector() has already been set\">\n : Defined extends { type: \"string\"; array: false }\n ? DBFieldVectorFn<Defined, Output, Nested>\n : TypeLevelError<\"vector can only be set on non-array string fields\">;\ntype DBFieldHooksMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldHooksFn<Defined, Output, Nested>\n : Defined extends {\n serial: true;\n hooks: { create: false; update: false };\n }\n ? TypeLevelError<\"hooks cannot be set after serial\">\n : Defined extends {\n hooks: unknown;\n }\n ? TypeLevelError<\".hooks() has already been set\">\n : Defined extends { type: \"nested\" }\n ? TypeLevelError<\"hooks cannot be set on nested type fields\">\n : DBFieldHooksFn<Defined, Output, Nested>;\ntype DBFieldValidateMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldValidateFn<Defined, Output, Nested>\n : Defined extends { validate: unknown }\n ? TypeLevelError<\".validate() has already been set\">\n : DBFieldValidateFn<Defined, Output, Nested>;\ntype DBFieldSerialMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldSerialFn<Defined, Output, Nested>\n : Defined extends { serial: true }\n ? TypeLevelError<\".serial() has already been set\">\n : Defined extends { serial: false }\n ? TypeLevelError<\"serial cannot be set after hooks\">\n : IsAny<Output> extends true\n ? Defined extends { type: \"integer\" | \"string\"; array: false }\n ? DBFieldSerialFn<Defined, Output, Nested>\n : TypeLevelError<\"serial can only be set on non-array integer or string fields\">\n : null extends Output\n ? TypeLevelError<\"serial can only be set on non-array integer or string fields\">\n : Defined extends { type: \"integer\" | \"string\"; array: false }\n ? DBFieldSerialFn<Defined, Output, Nested>\n : TypeLevelError<\"serial can only be set on non-array integer or string fields\">;\ntype DBFieldDefaultFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n value: Output extends null ? NonNullable<Output> : Output,\n) => TailorDBField<WithDBFieldDefault<Defined>, Output, Nested>;\ntype DBFieldDefaultMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldDefaultFn<Defined, Output, Nested>\n : Defined extends { default: unknown }\n ? TypeLevelError<\".default() has already been set\">\n : Defined extends { type: \"nested\" }\n ? TypeLevelError<\"default cannot be set on nested type fields\">\n : Defined extends { serial: true }\n ? TypeLevelError<\"default cannot be set on serial fields\">\n : null extends Output\n ? TypeLevelError<\"default cannot be set on optional fields\">\n : DBFieldDefaultFn<Defined, Output, Nested>;\n\n/**\n * Full TailorDBField interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorDBField<\n Defined extends DefinedDBFieldMetadata = DefinedDBFieldMetadata,\n // oxlint-disable-next-line no-explicit-any\n Output = any,\n // Nested object fields, so a `db.object()` field keeps the shape it was declared with.\n // Every builder method passes it through; dropping it here would erase the shape as\n // soon as one is chained.\n Nested extends Record<string, TailorAnyDBField> = Record<string, TailorAnyDBField>,\n> extends Omit<TailorDBFieldBase<Defined, Output, Nested>, \"fields\"> {\n readonly fields: Nested;\n _metadata: DBFieldMetadata;\n\n /**\n * Parse and validate a value against this field's validation rules\n */\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n\n /**\n * typeName is not available on TailorDB fields.\n * Use typeName on pipeline fields (t.enum / t.object) instead.\n */\n typeName: DBFieldTypeNameMethod<Defined>;\n\n /**\n * Set a description for the field\n */\n description: DBFieldDescriptionMethod<Defined, Output, Nested>;\n\n /**\n * Define a relation to another table.\n */\n relation: DBFieldRelationMethod<Defined, Output, Nested>;\n\n /**\n * Add an index to the field\n */\n index: DBFieldIndexMethod<Defined, Output, Nested>;\n\n /**\n * Make the field unique (also adds an index)\n */\n unique: DBFieldUniqueMethod<Defined, Output, Nested>;\n\n /**\n * Enable vector search on the field (string type only)\n */\n vector: DBFieldVectorMethod<Defined, Output, Nested>;\n\n /**\n * Set a default value for the field on create. When the field is required,\n * this makes it optional in the Create input — the default fills in when\n * no value (or a nullish hook result) is provided.\n *\n * For datetime/date/time fields, pass `\"now\"` to use the operation timestamp.\n */\n default: DBFieldDefaultMethod<Defined, Output, Nested>;\n\n /**\n * Add hooks for create/update operations on this field.\n */\n hooks: DBFieldHooksMethod<Defined, Output, Nested>;\n\n /**\n * Add validation functions to the field.\n *\n * Validators receive `{ value, data, user }` and run after hooks and\n * built-in type validation; they are skipped when built-in validation\n * fails. For array fields, `value` is the complete array.\n */\n validate: DBFieldValidateMethod<Defined, Output, Nested>;\n\n /**\n * Configure serial/auto-increment behavior\n */\n serial: DBFieldSerialMethod<Defined, Output, Nested>;\n\n /**\n * Clone the field with optional overrides for field options.\n * The `array` option cannot change on fields with custom validation.\n */\n clone<const NewOpt extends DBFieldCloneOptions<Defined>>(\n options?: NewOpt,\n ): TailorDBField<\n WithDBFieldCloneOptions<Defined, NewOpt>,\n DBFieldCloneOutput<Output, NewOpt>,\n Nested\n >;\n}\n\n/**\n * Full TailorDBType interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorDBType<\n // oxlint-disable-next-line no-explicit-any\n Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n // oxlint-disable-next-line no-explicit-any\n Defined extends DefinedDBTypeMetadata = any,\n FileKeys extends string = never,\n> extends TailorDBTypeBase<Fields, User> {\n _description?: string;\n\n hooks(\n hook: DBTypeDuplicateInputGuard<\n Defined,\n \"hooks\",\n TypeHook<Fields>,\n \".hooks() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"hooks\">, FileKeys>;\n validate(\n fn: DBTypeDuplicateInputGuard<\n Defined,\n \"validate\",\n TypeValidateFn<Fields>,\n \".validate() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"validate\">, FileKeys>;\n features(\n features: DBTypeDuplicateInputGuard<\n Defined,\n \"features\",\n Omit<TypeFeatures, \"pluralForm\">,\n \".features() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"features\">, FileKeys>;\n indexes(\n ...indexes: DBTypeDuplicateRestGuard<\n Defined,\n \"indexes\",\n IndexDef<TailorDBType<Fields, User, Defined, FileKeys>>[],\n \".indexes() has already been set\"\n >\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"indexes\">, FileKeys>;\n files<const F extends string>(\n files: DBTypeDuplicateInputGuard<\n Defined,\n \"files\",\n Record<F, string> & FileKeyConflictError<Fields, User>,\n \".files() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"files\">, FileKeys | F>;\n permission<\n U extends object = User,\n P extends TailorTypePermission<U, output<TailorDBType<Fields, User, Defined>>> =\n TailorTypePermission<U, output<TailorDBType<Fields, User, Defined>>>,\n >(\n permission: DBTypeDuplicateInputGuard<\n Defined,\n \"permission\",\n P,\n \".permission() has already been set\"\n >,\n ): TailorDBType<Fields, U, WithDBTypeMetadata<Defined, \"permission\">, FileKeys>;\n gqlPermission<\n U extends object = User,\n P extends TailorTypeGqlPermission<U> = TailorTypeGqlPermission<U>,\n >(\n permission: DBTypeDuplicateInputGuard<\n Defined,\n \"gqlPermission\",\n P,\n \".gqlPermission() has already been set\"\n >,\n ): TailorDBType<Fields, U, WithDBTypeMetadata<Defined, \"gqlPermission\">, FileKeys>;\n description(\n description: DBTypeDuplicateInputGuard<\n Defined,\n \"description\",\n string,\n \".description() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"description\">, FileKeys>;\n pickFields<K extends keyof Fields>(keys: K[]): Pick<Fields, K>;\n pickFields<K extends keyof Fields, const Opt extends FieldOptions>(\n keys: K[],\n options: Opt & DBFieldsCloneOptionsGuard<Fields, K, Opt>,\n ): {\n [P in K]: Fields[P] extends TailorDBField<infer D, infer O>\n ? TailorDBField<WithDBFieldCloneOptions<D, Opt>, DBFieldCloneOutput<O, Opt>>\n : never;\n };\n omitFields<K extends keyof Fields>(keys: K[]): Omit<Fields, K>;\n plugin<const Config extends Record<string, unknown>>(\n config: Config & PluginConfigGuard<Fields, Config, FileKeys>,\n ): TailorDBType<PluginExtendedFields<Fields, Config>, User, Defined, FileKeys>;\n}\n\nexport type TailorDBInstance<\n // oxlint-disable-next-line no-explicit-any\n Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n // oxlint-disable-next-line no-explicit-any\n Defined extends DefinedDBTypeMetadata = any,\n FileKeys extends string = never,\n> = TailorDBType<Fields, User, Defined, FileKeys>;\n\ninterface RelationConfig<S extends RelationType, T extends TailorDBType> {\n type: S;\n toward:\n | {\n table: T;\n as?: string;\n key?: keyof T[\"fields\"] & string;\n type?: never;\n }\n | {\n /**\n * @deprecated since 2.6.0 — use `table` instead. codemod: v3/relation-toward-table\n */\n type: T;\n as?: string;\n key?: keyof T[\"fields\"] & string;\n table?: never;\n };\n backward?: string;\n}\n\n// Special config variant for self-referencing relations\ntype RelationSelfConfig = {\n type: RelationType;\n toward:\n | {\n table: \"self\";\n as?: string;\n key?: string;\n type?: never;\n }\n | {\n /**\n * @deprecated since 2.6.0 — use `table` instead. codemod: v3/relation-toward-table\n */\n type: \"self\";\n as?: string;\n key?: string;\n table?: never;\n };\n backward?: string;\n};\n\ntype DBFieldDefined<T extends TailorFieldType, Opt extends FieldOptions> = {\n type: T;\n array: Opt extends { array: true } ? true : false;\n};\ntype DBFieldOutput<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = FieldOutput<OutputBase, Opt>;\ntype TailorDBFieldInstance<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = TailorDBField<DBFieldDefined<T, Opt>, DBFieldOutput<T, Opt, OutputBase>>;\ntype TailorDBFieldRuntimeInstance<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = TailorDBFieldRuntime<DBFieldDefined<T, Opt>, DBFieldOutput<T, Opt, OutputBase>>;\ntype TailorDBFieldRuntime<Defined extends DefinedDBFieldMetadata, Output> = Omit<\n TailorDBFieldBase<Defined, Output>,\n \"fields\"\n> & {\n readonly fields: Record<string, TailorAnyDBField>;\n _metadata: DBFieldMetadata;\n description(description: string): object;\n typeName(typeName: string): object;\n validate(...validate: FieldValidateInput<Output>[]): object;\n relation(config: RelationConfig<RelationType, TailorDBType> | RelationSelfConfig): object;\n index(): object;\n unique(): object;\n vector(): object;\n default(value: unknown): object;\n hooks(hooks: Hook<Output>): object;\n serial(config: SerialConfig): object;\n clone(options?: FieldOptions): TailorDBFieldRuntime<DefinedDBFieldMetadata, AnyBuilderMethod>;\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n _setRawRelation(relation: RawRelationConfig): void;\n};\n\n/**\n * Creates a new TailorDBField instance.\n * @param type - Field type\n * @param options - Field options\n * @param fields - Nested fields for object-like types\n * @param values - Allowed values for enum-like fields\n * @returns A new TailorDBField\n */\nfunction createTailorDBField<\n const T extends TailorFieldType,\n const TOptions extends FieldOptions,\n const OutputBase = TailorToTs[T],\n>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyDBField>,\n values?: AllowedValues,\n): TailorDBFieldInstance<T, TOptions, OutputBase>;\nfunction createTailorDBField<const T extends TailorFieldType, const TOptions extends FieldOptions>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyDBField>,\n values?: AllowedValues,\n): object {\n return createTailorDBFieldRuntime(type, options, fields, values);\n}\n\nfunction createTailorDBFieldRuntime<\n const T extends TailorFieldType,\n const TOptions extends FieldOptions,\n const OutputBase = TailorToTs[T],\n>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyDBField>,\n values?: AllowedValues,\n): TailorDBFieldRuntimeInstance<T, TOptions, OutputBase> {\n type FieldValue = DBFieldOutput<T, TOptions, OutputBase>;\n type FieldType = TailorDBFieldRuntimeInstance<T, TOptions, OutputBase>;\n\n const _metadata: DBFieldMetadata = { required: true };\n let _rawRelation: RawRelationConfig | undefined;\n\n if (options) {\n if (options.optional === true) {\n _metadata.required = false;\n }\n if (options.array === true) {\n _metadata.array = true;\n }\n }\n if (values) {\n _metadata.allowedValues = mapAllowedValues(values);\n }\n\n function parseInternal(args: FieldParseInternalArgs): StandardSchemaV1.Result<FieldValue> {\n return parseFieldInternal<T, FieldValue>({\n ...args,\n field,\n });\n }\n\n function cloneWith(metadataUpdates: Partial<DBFieldMetadata>) {\n const cloned = field.clone();\n Object.assign(cloned._metadata, metadataUpdates);\n return cloned;\n }\n\n const field: FieldType = {\n type,\n fields: fields ?? {},\n _defined: undefined as unknown as DBFieldDefined<T, TOptions>,\n _output: undefined as FieldValue,\n _metadata,\n\n get metadata() {\n return { ...this._metadata };\n },\n\n get rawRelation(): Readonly<RawRelationConfig> | undefined {\n return _rawRelation ? { ..._rawRelation, toward: { ..._rawRelation.toward } } : undefined;\n },\n\n description(description: string) {\n return cloneWith({ description });\n },\n\n typeName(typeName: string) {\n return cloneWith({ typeName });\n },\n\n validate(...validateInputs: FieldValidateInput<FieldValue>[]) {\n return cloneWith({ validate: validateInputs });\n },\n\n parse(args: FieldParseArgs): StandardSchemaV1.Result<FieldValue> {\n return parseInternal({\n value: args.value,\n data: args.data,\n invoker: args.invoker,\n pathArray: [],\n });\n },\n\n // TailorDBField specific methods\n relation(config: RelationConfig<RelationType, TailorDBType> | RelationSelfConfig) {\n const cloned = field.clone();\n // The public type is a nested union (RelationConfig | RelationSelfConfig,\n // each with a `{ table } | { type }` toward), which TS's \"in\" narrowing\n // can't discriminate across cleanly. Re-view it as the flat two-branch\n // shape it always is at runtime — the `table?: never` / `type?: never`\n // markers above already forbid both keys from being set together.\n const toward = config.toward as\n | { table: TailorDBType | \"self\" }\n | { type: TailorDBType | \"self\" };\n const towardTarget = \"table\" in toward ? toward.table : toward.type;\n const targetTable = towardTarget === \"self\" ? \"self\" : towardTarget.name;\n cloned._setRawRelation({\n type: config.type,\n toward: {\n table: targetTable,\n as: config.toward.as,\n key: config.toward.key,\n },\n backward: config.backward,\n });\n return cloned;\n },\n\n index() {\n return cloneWith({ index: true });\n },\n\n unique() {\n return cloneWith({ unique: true, index: true });\n },\n\n vector() {\n return cloneWith({ vector: true });\n },\n\n // oxlint-disable-next-line no-explicit-any\n default(value: any) {\n // oxlint-disable-next-line no-explicit-any\n return cloneWith({ default: value }) as any;\n },\n\n hooks(hooks: Hook<FieldValue>) {\n return cloneWith({ hooks });\n },\n\n serial(config: SerialConfig) {\n return cloneWith({ serial: config });\n },\n\n clone(cloneOptions?: FieldOptions) {\n if (\n this._metadata.validate?.length &&\n cloneOptions?.array !== undefined &&\n cloneOptions.array !== (this._metadata.array === true)\n ) {\n throw new Error(\"Cannot change the array option on a field with custom validation\");\n }\n\n // Deep clone nested object fields if present\n let clonedFields = fields;\n if (fields) {\n const cloned: Record<string, TailorAnyDBField> = {};\n for (const [key, field] of Object.entries(fields)) {\n cloned[key] = field.clone();\n }\n clonedFields = cloned;\n }\n\n // Create a new field with cloned configuration\n const clonedField = createTailorDBFieldRuntime(type, options, clonedFields, values);\n\n // Deep copy metadata using cloneDeep (preserves function references)\n Object.assign(clonedField._metadata, cloneDeep(this._metadata));\n\n // Apply new options if provided\n if (cloneOptions) {\n if (cloneOptions.optional !== undefined) {\n clonedField._metadata.required = !cloneOptions.optional;\n }\n if (cloneOptions.array !== undefined) {\n clonedField._metadata.array = cloneOptions.array;\n }\n }\n\n // Copy raw relation if exists\n if (_rawRelation) {\n clonedField._setRawRelation(cloneDeep(_rawRelation));\n }\n\n return clonedField;\n },\n\n _setRawRelation(relation: RawRelationConfig) {\n _rawRelation = relation;\n },\n };\n\n return field;\n}\n\nconst createField = createTailorDBField;\n\n/**\n * Create a UUID field.\n * @param options - Field configuration options\n * @returns A UUID field\n * @example db.uuid()\n * @example db.uuid({ optional: true })\n */\nfunction uuid<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"uuid\", options);\n}\n\n/**\n * Create a string field.\n * @param options - Field configuration options\n * @returns A string field\n * @example db.string()\n * @example db.string({ optional: true })\n */\nfunction string<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"string\", options);\n}\n\n/**\n * Create a boolean field.\n * Note: The method name is `bool` but creates a `boolean` type field.\n * @param options - Field configuration options\n * @returns A boolean field\n * @example db.bool()\n * @example db.bool({ optional: true })\n */\nfunction bool<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"boolean\", options);\n}\n\n/**\n * Create an integer field.\n * @param options - Field configuration options\n * @returns An integer field\n * @example db.int()\n * @example db.int({ optional: true })\n */\nfunction int<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"integer\", options);\n}\n\n/**\n * Create a float (decimal number) field.\n * @param options - Field configuration options\n * @returns A float field\n * @example db.float()\n * @example db.float({ optional: true })\n */\nfunction float<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"float\", options);\n}\n\ninterface DecimalFieldOptions extends FieldOptions {\n scale?: number;\n}\n\n/**\n * Create a decimal field (stored as string for precision).\n * @param options - Field configuration options including optional scale (0-12)\n * @returns A decimal field\n * @example db.decimal()\n * @example db.decimal({ scale: 2 })\n * @example db.decimal({ scale: 2, optional: true })\n */\nfunction decimal<const Opt extends DecimalFieldOptions>(options?: Opt) {\n if (options?.scale !== undefined) {\n if (!Number.isInteger(options.scale) || options.scale < 0 || options.scale > 12) {\n throw new Error(\"scale must be an integer between 0 and 12\");\n }\n }\n const field = createField(\"decimal\", options);\n if (options?.scale !== undefined) {\n field._metadata.scale = options.scale;\n }\n return field;\n}\n\n/**\n * Create a date field (date only, no time component).\n * Format: \"yyyy-MM-dd\"\n * @param options - Field configuration options\n * @returns A date field\n * @example db.date()\n */\nfunction date<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"date\", options);\n}\n\n/**\n * Create a datetime field (date and time).\n * Format: ISO 8601, such as \"yyyy-MM-ddTHH:mm:ssZ\" or \"yyyy-MM-ddTHH:mm:ss+09:00\"\n * @param options - Field configuration options\n * @returns A datetime field\n * @example db.datetime()\n */\nfunction datetime<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"datetime\", options);\n}\n\n/**\n * Create a time field (time only, no date component).\n * Format: \"HH:mm\"\n * @param options - Field configuration options\n * @returns A time field\n * @example db.time()\n */\nfunction time<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"time\", options);\n}\n\n/**\n * Create an enum field with at least one allowed string value.\n * @param values - Array of allowed string values, or array of `{ value, description }` objects\n * @param options - Field configuration options\n * @returns An enum field\n * @example db.enum([\"active\", \"inactive\", \"suspended\"])\n * @example db.enum([\"small\", \"medium\", \"large\"], { optional: true })\n */\nfunction _enum<const V extends AllowedValues, const Opt extends FieldOptions>(\n values: V,\n options?: Opt,\n): TailorDBField<\n { type: \"enum\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<AllowedValuesOutput<V>, Opt>\n> {\n return createField<\"enum\", Opt, AllowedValuesOutput<V>>(\"enum\", options, undefined, values);\n}\n\n/**\n * Create a nested object field with sub-fields.\n * @param fields - Record of nested field definitions\n * @param options - Field configuration options\n * @returns A nested object field\n * @example db.object({ street: db.string(), city: db.string(), zip: db.string() })\n * @example db.object({ name: db.string() }, { optional: true })\n */\nfunction object<\n const F extends Record<string, TailorAnyDBField> &\n ExcludeNestedDBFields<F> &\n ExcludeHookedDBFields<F> &\n ExcludeDefaultedDBFields<F>,\n const Opt extends FieldOptions,\n>(fields: F, options?: Opt) {\n return createField(\"nested\", options, fields) as unknown as TailorDBField<\n { type: \"nested\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<InferFieldsOutput<F>, Opt>,\n F\n >;\n}\n\n/**\n * Creates a new TailorDBType instance.\n * @param name - Table name\n * @param fields - Field definitions\n * @param options - Table options\n * @param options.pluralForm - Optional plural form\n * @param options.description - Optional description\n * @returns A new TailorDBType\n */\nfunction createTailorDBType<\n // oxlint-disable-next-line no-explicit-any\n const Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n>(\n name: string,\n fields: Fields,\n options: { pluralForm?: string; description?: string },\n): TailorDBType<Fields, User, DefinedDBTypeMetadata> {\n let _description = options.description;\n let _settings: TypeFeatures = {};\n let _indexes: IndexDef<TailorDBType<Fields, User, DefinedDBTypeMetadata>>[] = [];\n const _permissions: RawPermissions = {};\n let _files: Record<string, string> = {};\n const _plugins: PluginAttachment[] = [];\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n let _typeHook: { create?: Function; update?: Function } | undefined;\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n let _typeValidate: Function | undefined;\n const _definedMethods = new Set<keyof DefinedDBTypeMetadata>();\n if (options.description !== undefined) {\n _definedMethods.add(\"description\");\n }\n\n function runMethodOnce<T>(method: keyof DefinedDBTypeMetadata, action: () => T): T {\n if (_definedMethods.has(method)) {\n throw new Error(`.${method}() has already been set`);\n }\n const result = action();\n _definedMethods.add(method);\n return result;\n }\n type TypeAfter<Key extends keyof DefinedDBTypeMetadata> = TailorDBType<\n Fields,\n User,\n WithDBTypeMetadata<DefinedDBTypeMetadata, Key>\n >;\n type TypeAfterUser<\n NextUser extends object,\n Key extends keyof DefinedDBTypeMetadata,\n > = TailorDBType<Fields, NextUser, WithDBTypeMetadata<DefinedDBTypeMetadata, Key>>;\n type TypeAfterPlugin<Config extends Record<string, unknown>> = TailorDBType<\n PluginExtendedFields<Fields, Config>,\n User,\n DefinedDBTypeMetadata\n >;\n\n if (options.pluralForm) {\n if (name === options.pluralForm) {\n throw new Error(`The name and the plural form must be different. name=${name}`);\n }\n _settings.pluralForm = options.pluralForm;\n }\n\n const dbType: TailorDBType<Fields, User, DefinedDBTypeMetadata> = {\n name,\n fields: { ...fields },\n _output: null as unknown as InferFieldsOutput<Fields>,\n _description,\n\n get metadata(): TailorDBTypeMetadata {\n // Convert indexes to the format expected by the manifest\n const indexes: Record<string, { fields: string[]; unique?: boolean }> = {};\n if (_indexes.length > 0) {\n _indexes.forEach((index) => {\n const fieldNames = index.fields.map((field) => String(field));\n const key = index.name || `idx_${fieldNames.join(\"_\")}`;\n indexes[key] = {\n fields: fieldNames,\n unique: index.unique,\n };\n });\n }\n\n return {\n name: this.name,\n description: _description,\n settings: _settings,\n permissions: _permissions,\n files: _files,\n ...(Object.keys(indexes).length > 0 && { indexes }),\n ...(_typeHook && { typeHook: _typeHook }),\n ...(_typeValidate && { typeValidate: _typeValidate }),\n };\n },\n\n hooks(hook: TypeHook<Fields>): TypeAfter<\"hooks\"> {\n return runMethodOnce(\"hooks\", () => {\n _typeHook = hook;\n return this as TypeAfter<\"hooks\">;\n });\n },\n\n validate(fn: TypeValidateFn<Fields>): TypeAfter<\"validate\"> {\n return runMethodOnce(\"validate\", () => {\n _typeValidate = fn;\n return this as TypeAfter<\"validate\">;\n });\n },\n\n features(features: Omit<TypeFeatures, \"pluralForm\">): TypeAfter<\"features\"> {\n return runMethodOnce(\"features\", () => {\n _settings = {\n ..._settings,\n ...features,\n };\n return this as TypeAfter<\"features\">;\n });\n },\n\n indexes(\n ...indexes: IndexDef<TailorDBType<Fields, User, DefinedDBTypeMetadata>>[]\n ): TypeAfter<\"indexes\"> {\n return runMethodOnce(\"indexes\", () => {\n _indexes = indexes;\n return this as TypeAfter<\"indexes\">;\n });\n },\n\n files<const F extends string>(\n files: Record<F, string> & FileKeyConflictError<Fields, User>,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<DefinedDBTypeMetadata, \"files\">, F> {\n return runMethodOnce(\"files\", () => {\n _files = files;\n return this as TailorDBType<\n Fields,\n User,\n WithDBTypeMetadata<DefinedDBTypeMetadata, \"files\">,\n F\n >;\n });\n },\n\n permission<\n U extends object = User,\n P extends TailorTypePermission<U, output<TailorDBType<Fields, User, DefinedDBTypeMetadata>>> =\n TailorTypePermission<U, output<TailorDBType<Fields, User, DefinedDBTypeMetadata>>>,\n >(permission: P): TypeAfterUser<U, \"permission\"> {\n return runMethodOnce(\"permission\", () => {\n const ret = this as unknown as TypeAfterUser<U, \"permission\">;\n _permissions.record = permission as RawPermissions[\"record\"];\n return ret;\n });\n },\n\n gqlPermission<\n U extends object = User,\n P extends TailorTypeGqlPermission<U> = TailorTypeGqlPermission<U>,\n >(permission: P): TypeAfterUser<U, \"gqlPermission\"> {\n return runMethodOnce(\"gqlPermission\", () => {\n const ret = this as unknown as TypeAfterUser<U, \"gqlPermission\">;\n _permissions.gql = permission as RawPermissions[\"gql\"];\n return ret;\n });\n },\n\n description(description: string): TypeAfter<\"description\"> {\n return runMethodOnce(\"description\", () => {\n _description = description;\n this._description = description;\n return this as TypeAfter<\"description\">;\n });\n },\n\n pickFields<K extends keyof Fields, const Opt extends FieldOptions>(keys: K[], options?: Opt) {\n const result = {} as Record<K, TailorAnyDBField>;\n for (const key of keys) {\n const field = this.fields[key] as TailorAnyDBField | undefined;\n if (!field) {\n // A plugin-injected field is only added to `fields` after `tailor\n // generate` runs — see the \"Injecting fields into the attached\n // table's type\" section of the plugin docs.\n throw new Error(\n `pickFields(): field \"${String(key)}\" does not exist on this table yet. If it comes from a plugin's .plugin() call, it is only added to the table after \\`tailor generate\\` runs.`,\n );\n }\n if (options) {\n result[key] = field.clone(options);\n } else {\n result[key] = field;\n }\n }\n // oxlint-disable-next-line no-explicit-any\n return result as any;\n },\n\n omitFields<K extends keyof Fields>(keys: K[]): Omit<Fields, K> {\n const keysSet = new Set(keys);\n const result = {} as Record<string, TailorAnyDBField>;\n for (const key in this.fields) {\n if (Object.hasOwn(this.fields, key) && !keysSet.has(key as unknown as K)) {\n result[key] = this.fields[key] as TailorAnyDBField;\n }\n }\n return result as Omit<Fields, K>;\n },\n\n get plugins(): PluginAttachment[] {\n return _plugins;\n },\n\n plugin<const Config extends Record<string, unknown>>(\n config: Config & PluginConfigGuard<Fields, Config, never>,\n ): TypeAfterPlugin<Config> {\n for (const [pluginId, pluginConfig] of Object.entries(config)) {\n _plugins.push({ pluginId, config: pluginConfig });\n }\n return this as unknown as TypeAfterPlugin<Config>;\n },\n };\n\n return brandValue(dbType, \"tailordb-type\");\n}\n\nfunction createIdField() {\n return uuid();\n}\ntype idField = ReturnType<typeof createIdField>;\nlet idField: idField | undefined;\ntype DBTable<\n F extends { id?: never } & Record<string, TailorAnyDBField>,\n Defined extends DefinedDBTypeMetadata = DefinedDBTypeMetadata,\n> = TailorDBInstance<{ id: idField } & F, InferredAttributes, Defined>;\n\n/**\n * Creates a new database table with the specified fields.\n * An `id` field (UUID) is automatically added to every table.\n * @param name - The name of the table, or a tuple of [name, pluralForm]\n * @param fields - The field definitions for the table\n * @returns A new TailorDB table instance\n * @example\n * export const user = db.table(\"User\", {\n * name: db.string(),\n * email: db.string(),\n * age: db.int({ optional: true }),\n * role: db.enum([\"admin\", \"member\"]),\n * ...db.fields.timestamps(),\n * });\n * // Always export both the value and type:\n * export type user = typeof user;\n */\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n fields: F,\n): DBTable<F>;\n/**\n * Creates a new database table with the specified fields and description.\n * An `id` field (UUID) is automatically added to every table.\n * @param name - The name of the table, or a tuple of [name, pluralForm]\n * @param description - A description of the table\n * @param fields - The field definitions for the table\n * @returns A new TailorDB table instance\n */\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n description: string,\n fields: F,\n): DBTable<F, WithDBTypeMetadata<DefinedDBTypeMetadata, \"description\">>;\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n fieldsOrDescription: string | F,\n fields?: F,\n): DBTable<F> | DBTable<F, WithDBTypeMetadata<DefinedDBTypeMetadata, \"description\">> {\n const typeName = Array.isArray(name) ? name[0] : name;\n const pluralForm = Array.isArray(name) ? name[1] : undefined;\n\n let description: string | undefined;\n let fieldDef: F;\n if (typeof fieldsOrDescription === \"string\") {\n description = fieldsOrDescription;\n fieldDef = fields as F;\n } else {\n fieldDef = fieldsOrDescription;\n }\n return createTailorDBType<{ id: idField } & F>(\n typeName,\n {\n id: (idField ??= createIdField()),\n ...fieldDef,\n },\n { pluralForm, description },\n );\n}\n\n// `Function.prototype.toString()` of this hook is embedded verbatim into deployed\n// schemas and migration diffs (see parser/service/tailordb/hooks-validate-precompiled-expr.ts\n// and field.ts). Its source text depends on how the SDK itself was built (e.g.\n// minification), so a fixed expression is pinned onto it directly here instead -\n// configure cannot import parser's `setPrecompiledScriptExpr` runtime helper across\n// the module boundary, only the symbol registry key and map types. Keep\n// this literal in sync with the \"timestamps() updatedAt hook resolves to the pinned\n// expr\" test in parser/service/tailordb/field.precompiled.test.ts, which fails if it\n// ever drifts from what this hook's own source naturally produces.\ntype TimestampsUpdatedAtHookFn = UpdateHookFn<string | Date | null, string | Date>;\nconst PRECOMPILED_EXPR_KEY: PrecompiledScriptExprKey =\n \"tailor-platform/sdk:precompiled-script-expr\";\nconst precompiledExprs: PrecompiledScriptExprMap = {\n \"hooks.update\":\n \"(({ input, now }) => input ?? now)({ input: _value, oldValue: _oldValue, invoker: _principal, now: _now })\",\n};\nlet timestampsUpdatedAtHook: TimestampsUpdatedAtHookFn | undefined;\nfunction getTimestampsUpdatedAtHook(): TimestampsUpdatedAtHookFn {\n return (timestampsUpdatedAtHook ??= Object.assign(\n ({ input, now }: Parameters<TimestampsUpdatedAtHookFn>[0]) => input ?? now,\n { [Symbol.for(PRECOMPILED_EXPR_KEY)]: precompiledExprs },\n ));\n}\n\n/** TailorDB schema builder utilities for defining tables and fields. */\nexport const db = {\n table: dbTable,\n uuid,\n string,\n bool,\n int,\n float,\n decimal,\n date,\n datetime,\n time,\n enum: _enum,\n object,\n fields: {\n /**\n * Creates standard timestamp fields (createdAt, updatedAt) with automatic defaults.\n * Both fields default to the current time on create. updatedAt is also refreshed on update.\n * User-specified values are respected when provided (e.g. seeding historical records).\n * @returns An object with createdAt and updatedAt fields\n * @example\n * const model = db.table(\"Model\", {\n * name: db.string(),\n * ...db.fields.timestamps(),\n * });\n */\n timestamps: () => ({\n createdAt: datetime().default(\"now\").description(\"Record creation timestamp\"),\n updatedAt: datetime()\n .default(\"now\")\n .hooks({ update: getTimestampsUpdatedAtHook() })\n .description(\"Record update timestamp\"),\n }),\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"schema-CRtAmDcl.mjs","names":["parseInternal","parseFieldInternal","parseInternal","values","value","type","options","fields","_metadata","_rawRelation","args","cloned","field","metadataUpdates","validateInputs","toward","config","towardTarget","targetTable","cloneOptions","clonedFields","key","clonedField","cloneDeep","relation","createField","_description","_settings","_indexes","_permissions","_files","_plugins","_typeHook","_typeValidate","_definedMethods","method","result","action","name","dbType","indexes","fieldNames","index","hook","fn","features","files","ret","permission","description","keys","keysSet","pluginId","pluginConfig","brandValue","idField","typeName","pluralForm","fieldDef","fieldsOrDescription","precompiledExprs","timestampsUpdatedAtHook","input","now","db"],"sources":["../src/configure/types/field.ts","../src/configure/services/tailordb/schema.ts"],"sourcesContent":["import type { EnumValue } from \"#/configure/types/field.types\";\n\nexport type AllowedValues = readonly [string | EnumValue, ...(string | EnumValue)[]];\n\n/**\n * Normalize allowed values into EnumValue objects with descriptions.\n * @param values - Allowed values as strings or EnumValue objects\n * @returns Normalized allowed values\n */\nexport function mapAllowedValues(values: AllowedValues): EnumValue[] {\n return values.map((value) => {\n if (typeof value === \"string\") {\n return { value, description: \"\" };\n }\n return { ...value, description: value.description ?? \"\" };\n });\n}\n\nexport type AllowedValuesOutput<V extends AllowedValues> = V[number] extends infer T\n ? T extends string\n ? T\n : T extends { value: infer K }\n ? K\n : never\n : never;\n","import { cloneDeep } from \"es-toolkit\";\nimport {\n type AllowedValues,\n type AllowedValuesOutput,\n mapAllowedValues,\n} from \"#/configure/types/field\";\nimport {\n parseInternal as parseFieldInternal,\n type FieldParseArgs,\n type FieldParseInternalArgs,\n} from \"#/runtime/field-parse\";\nimport { brandValue } from \"#/utils/brand\";\nimport type {\n FieldOptions,\n FieldOutput,\n TailorFieldType,\n TailorToTs,\n FieldValidateInput,\n} from \"#/configure/types/field.types\";\nimport type {\n PrecompiledScriptExprKey,\n PrecompiledScriptExprMap,\n} from \"#/parser/service/tailordb/types\";\nimport type { PluginAttachment, PluginConfigs, PluginFieldExtensions } from \"#/plugin/types\";\nimport type { InferredAttributes } from \"#/runtime/types\";\nimport type {\n output,\n InferFieldsOutput,\n IsUnion,\n TypeLevelError,\n UnionToIntersection,\n} from \"#/types/helpers\";\nimport type { RawPermissions } from \"#/types/tailordb.generated\";\nimport type { TailorTypeGqlPermission, TailorTypePermission } from \"./permission\";\nimport type {\n TailorDBField as TailorDBFieldBase,\n TailorDBType as TailorDBTypeBase,\n DBFieldMetadata,\n DefinedDBFieldMetadata,\n SerialConfig,\n IndexDef,\n TailorDBTypeMetadata,\n RawRelationConfig,\n RelationType,\n Hook,\n TypeHook,\n UpdateHookFn,\n ExcludeNestedDBFields,\n ExcludeHookedDBFields,\n ExcludeDefaultedDBFields,\n TypeFeatures,\n TypeValidateFn,\n} from \"./types\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// Erased DB fields stay assignable across builder method-state changes.\n// oxlint-disable-next-line no-explicit-any\ntype AnyBuilderMethod = any;\n\nexport type TailorAnyDBField = Omit<\n TailorDBFieldBase<AnyBuilderMethod, AnyBuilderMethod>,\n \"fields\"\n> & {\n readonly fields: Record<string, AnyBuilderMethod>;\n _metadata: DBFieldMetadata;\n parse: AnyBuilderMethod;\n readonly typeName: TypeLevelError<string>;\n description: AnyBuilderMethod;\n relation: AnyBuilderMethod;\n index: AnyBuilderMethod;\n unique: AnyBuilderMethod;\n vector: AnyBuilderMethod;\n default: AnyBuilderMethod;\n hooks: AnyBuilderMethod;\n validate: AnyBuilderMethod;\n serial: AnyBuilderMethod;\n clone: AnyBuilderMethod;\n};\n\n// Helper alias\n// oxlint-disable-next-line no-explicit-any\nexport type TailorAnyDBType = TailorDBType<any, any, any>;\n\ntype IsAny<T> = 0 extends 1 & T ? true : false;\ntype DBFieldTypeNameMethod<Defined extends DefinedDBFieldMetadata> =\n IsAny<Defined> extends true\n ? TypeLevelError<string>\n : TypeLevelError<\"typeName cannot be used on TailorDB fields\">;\n\ntype WithDBFieldDescription<Defined> = Defined & { description: true };\ntype WithDBFieldRelation<Defined, S extends RelationType | RelationSelfConfig> = S extends\n | \"oneToOne\"\n | \"1-1\"\n ? Defined & { unique: true; index: true; relation: true }\n : S extends { type: \"oneToOne\" | \"1-1\" }\n ? Defined & { unique: true; index: true; relation: true }\n : Defined & { index: true; relation: true };\ntype WithDBFieldIndex<Defined> = Defined & { index: true };\ntype WithDBFieldUnique<Defined> = Defined & { unique: true; index: true };\ntype WithDBFieldVector<Defined> = Defined & { vector: true };\ntype WithDBFieldHooks<Defined, H> = Defined & {\n hooks: {\n create: H extends { create: unknown } ? true : false;\n update: H extends { update: unknown } ? true : false;\n };\n serial: false;\n};\ntype WithDBFieldDefault<Defined> = Defined & { default: true };\ntype WithDBFieldValidate<Defined> = Defined & { validate: true };\ntype WithDBFieldSerial<Defined> = Defined & {\n serial: true;\n hooks: { create: false; update: false };\n};\ntype WithDBFieldCloneOptions<Defined extends DefinedDBFieldMetadata, NewOpt extends FieldOptions> =\n IsAny<Defined> extends true\n ? Defined\n : Omit<Defined, \"array\"> & {\n array: NewOpt extends { array: true }\n ? true\n : NewOpt extends { array: false }\n ? false\n : Defined[\"array\"];\n };\ntype NonNullableDBFieldOutput<Output> = Exclude<Output, null>;\ntype DBFieldScalarOutput<Output> =\n NonNullableDBFieldOutput<Output> extends (infer Item)[] ? Item : NonNullableDBFieldOutput<Output>;\ntype DBFieldCloneArrayOutput<Output, NewOpt extends FieldOptions> = NewOpt extends {\n array: true;\n}\n ? DBFieldScalarOutput<Output>[]\n : NewOpt extends { array: false }\n ? DBFieldScalarOutput<Output>\n : NonNullableDBFieldOutput<Output>;\ntype DBFieldCloneOutput<Output, NewOpt extends FieldOptions> = NewOpt extends { optional: true }\n ? DBFieldCloneArrayOutput<Output, NewOpt> | null\n : NewOpt extends { optional: false }\n ? DBFieldCloneArrayOutput<Output, NewOpt>\n : null extends Output\n ? DBFieldCloneArrayOutput<Output, NewOpt> | null\n : DBFieldCloneArrayOutput<Output, NewOpt>;\ntype DBFieldCloneOptions<Defined extends DefinedDBFieldMetadata> = Omit<FieldOptions, \"array\"> & {\n array?: Defined extends { validate: unknown } ? Defined[\"array\"] : boolean;\n};\ntype InvalidValidatedArrayCloneKeys<\n Fields extends Record<string, TailorAnyDBField>,\n K extends keyof Fields,\n Opt extends FieldOptions,\n> = Opt extends { array: infer ArrayOption }\n ? {\n [P in K]: Fields[P] extends TailorDBField<infer Defined, infer _Output>\n ? Defined extends { validate: unknown }\n ? ArrayOption extends Defined[\"array\"]\n ? never\n : P\n : never\n : never;\n }[K]\n : never;\ntype DBFieldsCloneOptionsGuard<\n Fields extends Record<string, TailorAnyDBField>,\n K extends keyof Fields,\n Opt extends FieldOptions,\n> = [InvalidValidatedArrayCloneKeys<Fields, K, Opt>] extends [never]\n ? unknown\n : TypeLevelError<\"array cannot be changed on fields with custom validation\">;\ntype DefinedDBTypeMetadata = {\n hooks?: true;\n validate?: true;\n features?: true;\n indexes?: true;\n files?: true;\n permission?: true;\n gqlPermission?: true;\n description?: true;\n};\ntype WithDBTypeMetadata<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n> = Defined & Record<Key, true>;\ntype DBTypeDuplicateInputGuard<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n Input,\n Message extends string,\n> =\n IsAny<Defined> extends true\n ? Input\n : Defined extends Record<Key, unknown>\n ? TypeLevelError<Message>\n : Input;\ntype DBTypeDuplicateRestGuard<\n Defined extends DefinedDBTypeMetadata,\n Key extends keyof DefinedDBTypeMetadata,\n Input extends unknown[],\n Message extends string,\n> =\n IsAny<Defined> extends true\n ? Input\n : Defined extends Record<Key, unknown>\n ? [TypeLevelError<Message>, ...TypeLevelError<Message>[]]\n : Input;\ntype FileKeyConflictError<\n Fields extends Record<string, TailorAnyDBField>,\n User extends object,\n> = Partial<\n Record<\n keyof output<TailorDBType<Fields, User>> & string,\n TypeLevelError<\"file keys cannot use existing field names\">\n >\n>;\n// A plugin id with no PluginFieldExtensions entry contributes no keys — using\n// Record<never, never> (rather than Record<string, never>) keeps its `keyof`\n// empty so it can't spuriously collide with every existing field.\ntype PluginFieldExtensionFor<\n Fields extends Record<string, TailorAnyDBField>,\n Id extends string,\n IdConfig,\n> = Id extends keyof PluginFieldExtensions<keyof Fields & string, IdConfig>\n ? PluginFieldExtensions<keyof Fields & string, IdConfig>[Id]\n : Record<never, never>;\ntype PluginFieldExtensionsUnion<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n> = {\n [Id in keyof Config & string]: PluginFieldExtensionFor<Fields, Id, Config[Id]>;\n}[keyof Config & string];\ntype AllExtensionKeys<Ext> = Ext extends unknown ? keyof Ext : never;\n// True when plugin `Id` has a PluginFieldExtensions entry whose type does not\n// extend Record<string, TailorAnyDBField> — e.g. a plugin author registered\n// a non-record shape by mistake. Checked ahead of PluginFieldConflict, since\n// `keyof` on a non-record shape (e.g. `keyof string`) would otherwise produce\n// nonsense candidate keys for the collision check.\n// `Id extends string` (not `keyof Config & string`) deliberately: a mapped\n// type's key variable, used inside a nested conditional branch and then\n// intersected with `string`, does not structurally satisfy a `keyof Config &\n// string`-constrained type parameter under `skipLibCheck: false` (verified\n// against a standalone consumer — TS2344 on both call sites otherwise, even\n// for code that never calls .plugin()). `Config[Id & keyof Config]` recovers\n// the same indexed-access result without that constraint.\ntype PluginFieldExtensionShapeError<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n> =\n PluginFieldExtensionFor<Fields, Id, Config[Id & keyof Config]> extends Record<\n string,\n TailorAnyDBField\n >\n ? false\n : true;\n// True when the fields plugin `Id` would inject collide with an existing\n// field, or with a field injected by another plugin id attached in the same\n// .plugin() call.\ntype PluginFieldConflict<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n FileKeys extends string,\n> = [\n AllExtensionKeys<PluginFieldExtensionFor<Fields, Id, Config[Id & keyof Config]>> &\n (\n | keyof Fields\n | FileKeys\n | AllExtensionKeys<PluginFieldExtensionsUnion<Fields, Omit<Config, Id>>>\n ),\n] extends [never]\n ? false\n : true;\n// Rejects a property on the caller's config literal for plugin `Id` that\n// isn't part of its registered PluginConfigs shape. `Config` is captured by\n// a `const` type parameter from the argument itself, so intersecting the\n// success branch with `PluginConfigs<...>[Id]` alone would not trigger\n// TypeScript's excess-property check — that check only fires when an object\n// literal is validated directly against a target type, not after its shape\n// has already been inferred through a generic parameter.\ntype PluginConfigExcessProps<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n Id extends string,\n> = Record<\n Exclude<\n keyof Config[Id & keyof Config],\n keyof PluginConfigs<keyof Fields & string>[Id & keyof PluginConfigs<keyof Fields & string>]\n >,\n never\n>;\ntype PluginExtendedFields<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n> =\n IsAny<Fields> extends true\n ? Fields\n : // For `Config = {}`, PluginFieldExtensionsUnion is `never`, and\n // UnionToIntersection<never> is `unknown` (not `never`), so this\n // correctly reduces to `Fields` — see the \"empty config\" test.\n Fields & UnionToIntersection<PluginFieldExtensionsUnion<Fields, Config>>;\n// Validates each key of the passed config object against PluginConfigs and\n// flags field-extension conflicts (see PluginFieldConflict above), keeping\n// type errors localized to the offending plugin id instead of requiring\n// every registered plugin id to be present (as a `P extends keyof\n// PluginConfigs<...>` generic would need, defeating inference of which\n// plugin ids were actually passed).\ntype PluginConfigGuard<\n Fields extends Record<string, TailorAnyDBField>,\n Config extends Record<string, unknown>,\n FileKeys extends string,\n> =\n IsAny<Fields> extends true\n ? unknown\n : {\n [K in keyof Config]: K extends keyof PluginConfigs<keyof Fields & string>\n ? // A union config (e.g. from a ternary passed directly as a plugin's\n // value) distributes through PluginFieldExtensionFor's conditional\n // and silently collapses the injected field to `never` once\n // intersected back together — reject it here instead, before it\n // can produce that confusing downstream error.\n IsUnion<Config[K]> extends true\n ? TypeLevelError<\"plugin config must be a single object literal, not a union — assign the config to a variable first if it comes from a conditional expression\">\n : PluginFieldExtensionShapeError<Fields, Config, K & string> extends true\n ? TypeLevelError<\"PluginFieldExtensions entry must be a Record<string, TailorAnyDBField>\">\n : PluginFieldConflict<Fields, Config, K & string, FileKeys> extends true\n ? TypeLevelError<\"plugin field extension conflicts with an existing field, a file key declared via .files(), or another plugin's field\">\n : PluginConfigs<keyof Fields & string>[K] &\n PluginConfigExcessProps<Fields, Config, K & string>\n : TypeLevelError<\"unknown plugin id\">;\n };\ntype DBFieldDescriptionFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (description: string) => TailorDBField<WithDBFieldDescription<Defined>, Output, Nested>;\ntype DBFieldRelationFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = {\n <S extends RelationType, T extends TailorAnyDBType>(\n config: RelationConfig<S, T>,\n ): TailorDBField<WithDBFieldRelation<Defined, S>, Output, Nested>;\n <S extends RelationSelfConfig>(\n config: S,\n ): TailorDBField<WithDBFieldRelation<Defined, S>, Output, Nested>;\n};\ntype DBFieldIndexFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldIndex<Defined>, Output, Nested>;\ntype DBFieldUniqueFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldUnique<Defined>, Output, Nested>;\ntype DBFieldVectorFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = () => TailorDBField<WithDBFieldVector<Defined>, Output, Nested>;\ntype DBFieldHooksFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = <\n const H extends Hook<\n Output,\n Defined extends { default: true } ? Output | null | undefined : Output\n >,\n>(\n hooks: H,\n) => TailorDBField<WithDBFieldHooks<Defined, H>, Output, Nested>;\ntype DBFieldValidateFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n ...validate: FieldValidateInput<Output>[]\n) => TailorDBField<WithDBFieldValidate<Defined>, Output, Nested>;\ntype DBFieldSerialFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n config: SerialConfig<Defined[\"type\"] & (\"integer\" | \"string\")>,\n) => TailorDBField<WithDBFieldSerial<Defined>, Output, Nested>;\ntype DBFieldDescriptionMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldDescriptionFn<Defined, Output, Nested>\n : Defined extends { description: unknown }\n ? TypeLevelError<\".description() has already been set\">\n : DBFieldDescriptionFn<Defined, Output, Nested>;\ntype DBFieldRelationMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldRelationFn<Defined, Output, Nested>\n : Defined extends { relation: unknown }\n ? TypeLevelError<\".relation() has already been set\">\n : DBFieldRelationFn<Defined, Output, Nested>;\ntype DBFieldArrayCheck<A extends boolean, Ok, Msg extends string> = A extends true\n ? TypeLevelError<Msg>\n : Ok;\ntype DBFieldIndexMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldIndexFn<Defined, Output, Nested>\n : Defined extends { index: unknown }\n ? TypeLevelError<\".index() has already been set\">\n : DBFieldArrayCheck<\n Defined[\"array\"],\n DBFieldIndexFn<Defined, Output, Nested>,\n \"index cannot be set on array fields\"\n >;\ntype DBFieldUniqueMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldUniqueFn<Defined, Output, Nested>\n : Defined extends { unique: unknown }\n ? TypeLevelError<\".unique() has already been set\">\n : DBFieldArrayCheck<\n Defined[\"array\"],\n DBFieldUniqueFn<Defined, Output, Nested>,\n \"unique cannot be set on array fields\"\n >;\ntype DBFieldVectorMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldVectorFn<Defined, Output, Nested>\n : Defined extends { vector: unknown }\n ? TypeLevelError<\".vector() has already been set\">\n : Defined extends { type: \"string\"; array: false }\n ? DBFieldVectorFn<Defined, Output, Nested>\n : TypeLevelError<\"vector can only be set on non-array string fields\">;\ntype DBFieldHooksMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldHooksFn<Defined, Output, Nested>\n : Defined extends {\n serial: true;\n hooks: { create: false; update: false };\n }\n ? TypeLevelError<\"hooks cannot be set after serial\">\n : Defined extends {\n hooks: unknown;\n }\n ? TypeLevelError<\".hooks() has already been set\">\n : Defined extends { type: \"nested\" }\n ? TypeLevelError<\"hooks cannot be set on nested type fields\">\n : DBFieldHooksFn<Defined, Output, Nested>;\ntype DBFieldValidateMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldValidateFn<Defined, Output, Nested>\n : Defined extends { validate: unknown }\n ? TypeLevelError<\".validate() has already been set\">\n : DBFieldValidateFn<Defined, Output, Nested>;\ntype DBFieldSerialMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldSerialFn<Defined, Output, Nested>\n : Defined extends { serial: true }\n ? TypeLevelError<\".serial() has already been set\">\n : Defined extends { serial: false }\n ? TypeLevelError<\"serial cannot be set after hooks\">\n : IsAny<Output> extends true\n ? Defined extends { type: \"integer\" | \"string\"; array: false }\n ? DBFieldSerialFn<Defined, Output, Nested>\n : TypeLevelError<\"serial can only be set on non-array integer or string fields\">\n : null extends Output\n ? TypeLevelError<\"serial can only be set on non-array integer or string fields\">\n : Defined extends { type: \"integer\" | \"string\"; array: false }\n ? DBFieldSerialFn<Defined, Output, Nested>\n : TypeLevelError<\"serial can only be set on non-array integer or string fields\">;\ntype DBFieldDefaultFn<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> = (\n value: Output extends null ? NonNullable<Output> : Output,\n) => TailorDBField<WithDBFieldDefault<Defined>, Output, Nested>;\ntype DBFieldDefaultMethod<\n Defined extends DefinedDBFieldMetadata,\n Output,\n Nested extends Record<string, TailorAnyDBField>,\n> =\n IsAny<Defined> extends true\n ? DBFieldDefaultFn<Defined, Output, Nested>\n : Defined extends { default: unknown }\n ? TypeLevelError<\".default() has already been set\">\n : Defined extends { type: \"nested\" }\n ? TypeLevelError<\"default cannot be set on nested type fields\">\n : Defined extends { serial: true }\n ? TypeLevelError<\"default cannot be set on serial fields\">\n : null extends Output\n ? TypeLevelError<\"default cannot be set on optional fields\">\n : DBFieldDefaultFn<Defined, Output, Nested>;\n\n/**\n * Full TailorDBField interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorDBField<\n Defined extends DefinedDBFieldMetadata = DefinedDBFieldMetadata,\n // oxlint-disable-next-line no-explicit-any\n Output = any,\n // Nested object fields, so a `db.object()` field keeps the shape it was declared with.\n // Every builder method passes it through; dropping it here would erase the shape as\n // soon as one is chained.\n Nested extends Record<string, TailorAnyDBField> = Record<string, TailorAnyDBField>,\n> extends Omit<TailorDBFieldBase<Defined, Output, Nested>, \"fields\"> {\n readonly fields: Nested;\n _metadata: DBFieldMetadata;\n\n /**\n * Parse and validate a value against this field's validation rules\n */\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n\n /**\n * typeName is not available on TailorDB fields.\n * Use typeName on pipeline fields (t.enum / t.object) instead.\n */\n typeName: DBFieldTypeNameMethod<Defined>;\n\n /**\n * Set a description for the field\n */\n description: DBFieldDescriptionMethod<Defined, Output, Nested>;\n\n /**\n * Define a relation to another table.\n */\n relation: DBFieldRelationMethod<Defined, Output, Nested>;\n\n /**\n * Add an index to the field\n */\n index: DBFieldIndexMethod<Defined, Output, Nested>;\n\n /**\n * Make the field unique (also adds an index)\n */\n unique: DBFieldUniqueMethod<Defined, Output, Nested>;\n\n /**\n * Enable vector search on the field (string type only)\n */\n vector: DBFieldVectorMethod<Defined, Output, Nested>;\n\n /**\n * Set a default value for the field on create. When the field is required,\n * this makes it optional in the Create input — the default fills in when\n * no value (or a nullish hook result) is provided.\n *\n * For datetime/date/time fields, pass `\"now\"` to use the operation timestamp.\n */\n default: DBFieldDefaultMethod<Defined, Output, Nested>;\n\n /**\n * Add hooks for create/update operations on this field.\n */\n hooks: DBFieldHooksMethod<Defined, Output, Nested>;\n\n /**\n * Add validation functions to the field.\n *\n * Validators receive `{ value, data, user }` and run after hooks and\n * built-in type validation; they are skipped when built-in validation\n * fails. For array fields, `value` is the complete array.\n */\n validate: DBFieldValidateMethod<Defined, Output, Nested>;\n\n /**\n * Configure serial/auto-increment behavior\n */\n serial: DBFieldSerialMethod<Defined, Output, Nested>;\n\n /**\n * Clone the field with optional overrides for field options.\n * The `array` option cannot change on fields with custom validation.\n */\n clone<const NewOpt extends DBFieldCloneOptions<Defined>>(\n options?: NewOpt,\n ): TailorDBField<\n WithDBFieldCloneOptions<Defined, NewOpt>,\n DBFieldCloneOutput<Output, NewOpt>,\n Nested\n >;\n}\n\n/**\n * Full TailorDBType interface with builder methods.\n * Extends the minimal structural interface from types/ with fluent API methods.\n */\nexport interface TailorDBType<\n // oxlint-disable-next-line no-explicit-any\n Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n // oxlint-disable-next-line no-explicit-any\n Defined extends DefinedDBTypeMetadata = any,\n FileKeys extends string = never,\n> extends TailorDBTypeBase<Fields, User> {\n _description?: string;\n\n hooks(\n hook: DBTypeDuplicateInputGuard<\n Defined,\n \"hooks\",\n TypeHook<Fields>,\n \".hooks() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"hooks\">, FileKeys>;\n validate(\n fn: DBTypeDuplicateInputGuard<\n Defined,\n \"validate\",\n TypeValidateFn<Fields>,\n \".validate() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"validate\">, FileKeys>;\n features(\n features: DBTypeDuplicateInputGuard<\n Defined,\n \"features\",\n Omit<TypeFeatures, \"pluralForm\">,\n \".features() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"features\">, FileKeys>;\n indexes(\n ...indexes: DBTypeDuplicateRestGuard<\n Defined,\n \"indexes\",\n IndexDef<TailorDBType<Fields, User, Defined, FileKeys>>[],\n \".indexes() has already been set\"\n >\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"indexes\">, FileKeys>;\n files<const F extends string>(\n files: DBTypeDuplicateInputGuard<\n Defined,\n \"files\",\n Record<F, string> & FileKeyConflictError<Fields, User>,\n \".files() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"files\">, FileKeys | F>;\n permission<\n U extends object = User,\n P extends TailorTypePermission<U, output<TailorDBType<Fields, User, Defined>>> =\n TailorTypePermission<U, output<TailorDBType<Fields, User, Defined>>>,\n >(\n permission: DBTypeDuplicateInputGuard<\n Defined,\n \"permission\",\n P,\n \".permission() has already been set\"\n >,\n ): TailorDBType<Fields, U, WithDBTypeMetadata<Defined, \"permission\">, FileKeys>;\n gqlPermission<\n U extends object = User,\n P extends TailorTypeGqlPermission<U> = TailorTypeGqlPermission<U>,\n >(\n permission: DBTypeDuplicateInputGuard<\n Defined,\n \"gqlPermission\",\n P,\n \".gqlPermission() has already been set\"\n >,\n ): TailorDBType<Fields, U, WithDBTypeMetadata<Defined, \"gqlPermission\">, FileKeys>;\n description(\n description: DBTypeDuplicateInputGuard<\n Defined,\n \"description\",\n string,\n \".description() has already been set\"\n >,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<Defined, \"description\">, FileKeys>;\n pickFields<K extends keyof Fields>(keys: K[]): Pick<Fields, K>;\n pickFields<K extends keyof Fields, const Opt extends FieldOptions>(\n keys: K[],\n options: Opt & DBFieldsCloneOptionsGuard<Fields, K, Opt>,\n ): {\n [P in K]: Fields[P] extends TailorDBField<infer D, infer O>\n ? TailorDBField<WithDBFieldCloneOptions<D, Opt>, DBFieldCloneOutput<O, Opt>>\n : never;\n };\n omitFields<K extends keyof Fields>(keys: K[]): Omit<Fields, K>;\n plugin<const Config extends Record<string, unknown>>(\n config: Config & PluginConfigGuard<Fields, Config, FileKeys>,\n ): TailorDBType<PluginExtendedFields<Fields, Config>, User, Defined, FileKeys>;\n}\n\nexport type TailorDBInstance<\n // oxlint-disable-next-line no-explicit-any\n Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n // oxlint-disable-next-line no-explicit-any\n Defined extends DefinedDBTypeMetadata = any,\n FileKeys extends string = never,\n> = TailorDBType<Fields, User, Defined, FileKeys>;\n\ninterface RelationConfig<S extends RelationType, T extends TailorDBType> {\n type: S;\n toward:\n | {\n table: T;\n as?: string;\n key?: keyof T[\"fields\"] & string;\n type?: never;\n }\n | {\n /**\n * @deprecated since 2.6.0 — use `table` instead. codemod: v3/relation-toward-table\n */\n type: T;\n as?: string;\n key?: keyof T[\"fields\"] & string;\n table?: never;\n };\n backward?: string;\n}\n\n// Special config variant for self-referencing relations\ntype RelationSelfConfig = {\n type: RelationType;\n toward:\n | {\n table: \"self\";\n as?: string;\n key?: string;\n type?: never;\n }\n | {\n /**\n * @deprecated since 2.6.0 — use `table` instead. codemod: v3/relation-toward-table\n */\n type: \"self\";\n as?: string;\n key?: string;\n table?: never;\n };\n backward?: string;\n};\n\ntype DBFieldDefined<T extends TailorFieldType, Opt extends FieldOptions> = {\n type: T;\n array: Opt extends { array: true } ? true : false;\n};\ntype DBFieldOutput<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = FieldOutput<OutputBase, Opt>;\ntype TailorDBFieldInstance<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = TailorDBField<DBFieldDefined<T, Opt>, DBFieldOutput<T, Opt, OutputBase>>;\ntype TailorDBFieldRuntimeInstance<\n T extends TailorFieldType,\n Opt extends FieldOptions,\n OutputBase = TailorToTs[T],\n> = TailorDBFieldRuntime<DBFieldDefined<T, Opt>, DBFieldOutput<T, Opt, OutputBase>>;\ntype TailorDBFieldRuntime<Defined extends DefinedDBFieldMetadata, Output> = Omit<\n TailorDBFieldBase<Defined, Output>,\n \"fields\"\n> & {\n readonly fields: Record<string, TailorAnyDBField>;\n _metadata: DBFieldMetadata;\n description(description: string): object;\n typeName(typeName: string): object;\n validate(...validate: FieldValidateInput<Output>[]): object;\n relation(config: RelationConfig<RelationType, TailorDBType> | RelationSelfConfig): object;\n index(): object;\n unique(): object;\n vector(): object;\n default(value: unknown): object;\n hooks(hooks: Hook<Output>): object;\n serial(config: SerialConfig): object;\n clone(options?: FieldOptions): TailorDBFieldRuntime<DefinedDBFieldMetadata, AnyBuilderMethod>;\n parse(args: FieldParseArgs): StandardSchemaV1.Result<Output>;\n _setRawRelation(relation: RawRelationConfig): void;\n};\n\n/**\n * Creates a new TailorDBField instance.\n * @param type - Field type\n * @param options - Field options\n * @param fields - Nested fields for object-like types\n * @param values - Allowed values for enum-like fields\n * @returns A new TailorDBField\n */\nfunction createTailorDBField<\n const T extends TailorFieldType,\n const TOptions extends FieldOptions,\n const OutputBase = TailorToTs[T],\n>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyDBField>,\n values?: AllowedValues,\n): TailorDBFieldInstance<T, TOptions, OutputBase>;\nfunction createTailorDBField<const T extends TailorFieldType, const TOptions extends FieldOptions>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyDBField>,\n values?: AllowedValues,\n): object {\n return createTailorDBFieldRuntime(type, options, fields, values);\n}\n\nfunction createTailorDBFieldRuntime<\n const T extends TailorFieldType,\n const TOptions extends FieldOptions,\n const OutputBase = TailorToTs[T],\n>(\n type: T,\n options?: TOptions,\n fields?: Record<string, TailorAnyDBField>,\n values?: AllowedValues,\n): TailorDBFieldRuntimeInstance<T, TOptions, OutputBase> {\n type FieldValue = DBFieldOutput<T, TOptions, OutputBase>;\n type FieldType = TailorDBFieldRuntimeInstance<T, TOptions, OutputBase>;\n\n const _metadata: DBFieldMetadata = { required: true };\n let _rawRelation: RawRelationConfig | undefined;\n\n if (options) {\n if (options.optional === true) {\n _metadata.required = false;\n }\n if (options.array === true) {\n _metadata.array = true;\n }\n }\n if (values) {\n _metadata.allowedValues = mapAllowedValues(values);\n }\n\n function parseInternal(args: FieldParseInternalArgs): StandardSchemaV1.Result<FieldValue> {\n return parseFieldInternal<T, FieldValue>({\n ...args,\n field,\n });\n }\n\n function cloneWith(metadataUpdates: Partial<DBFieldMetadata>) {\n const cloned = field.clone();\n Object.assign(cloned._metadata, metadataUpdates);\n return cloned;\n }\n\n const field: FieldType = {\n type,\n fields: fields ?? {},\n _defined: undefined as unknown as DBFieldDefined<T, TOptions>,\n _output: undefined as FieldValue,\n _metadata,\n\n get metadata() {\n return { ...this._metadata };\n },\n\n get rawRelation(): Readonly<RawRelationConfig> | undefined {\n return _rawRelation ? { ..._rawRelation, toward: { ..._rawRelation.toward } } : undefined;\n },\n\n description(description: string) {\n return cloneWith({ description });\n },\n\n typeName(typeName: string) {\n return cloneWith({ typeName });\n },\n\n validate(...validateInputs: FieldValidateInput<FieldValue>[]) {\n return cloneWith({ validate: validateInputs });\n },\n\n parse(args: FieldParseArgs): StandardSchemaV1.Result<FieldValue> {\n return parseInternal({\n value: args.value,\n data: args.data,\n invoker: args.invoker,\n pathArray: [],\n });\n },\n\n // TailorDBField specific methods\n relation(config: RelationConfig<RelationType, TailorDBType> | RelationSelfConfig) {\n const cloned = field.clone();\n // The public type is a nested union (RelationConfig | RelationSelfConfig,\n // each with a `{ table } | { type }` toward), which TS's \"in\" narrowing\n // can't discriminate across cleanly. Re-view it as the flat two-branch\n // shape it always is at runtime — the `table?: never` / `type?: never`\n // markers above already forbid both keys from being set together.\n const toward = config.toward as\n | { table: TailorDBType | \"self\" }\n | { type: TailorDBType | \"self\" };\n const towardTarget = \"table\" in toward ? toward.table : toward.type;\n const targetTable = towardTarget === \"self\" ? \"self\" : towardTarget.name;\n cloned._setRawRelation({\n type: config.type,\n toward: {\n table: targetTable,\n as: config.toward.as,\n key: config.toward.key,\n },\n backward: config.backward,\n });\n return cloned;\n },\n\n index() {\n return cloneWith({ index: true });\n },\n\n unique() {\n return cloneWith({ unique: true, index: true });\n },\n\n vector() {\n return cloneWith({ vector: true });\n },\n\n // oxlint-disable-next-line no-explicit-any\n default(value: any) {\n // oxlint-disable-next-line no-explicit-any\n return cloneWith({ default: value }) as any;\n },\n\n hooks(hooks: Hook<FieldValue>) {\n return cloneWith({ hooks });\n },\n\n serial(config: SerialConfig) {\n return cloneWith({ serial: config });\n },\n\n clone(cloneOptions?: FieldOptions) {\n if (\n this._metadata.validate?.length &&\n cloneOptions?.array !== undefined &&\n cloneOptions.array !== (this._metadata.array === true)\n ) {\n throw new Error(\"Cannot change the array option on a field with custom validation\");\n }\n\n // Deep clone nested object fields if present\n let clonedFields = fields;\n if (fields) {\n const cloned: Record<string, TailorAnyDBField> = {};\n for (const [key, field] of Object.entries(fields)) {\n cloned[key] = field.clone();\n }\n clonedFields = cloned;\n }\n\n // Create a new field with cloned configuration\n const clonedField = createTailorDBFieldRuntime(type, options, clonedFields, values);\n\n // Deep copy metadata using cloneDeep (preserves function references)\n Object.assign(clonedField._metadata, cloneDeep(this._metadata));\n\n // Apply new options if provided\n if (cloneOptions) {\n if (cloneOptions.optional !== undefined) {\n clonedField._metadata.required = !cloneOptions.optional;\n }\n if (cloneOptions.array !== undefined) {\n clonedField._metadata.array = cloneOptions.array;\n }\n }\n\n // Copy raw relation if exists\n if (_rawRelation) {\n clonedField._setRawRelation(cloneDeep(_rawRelation));\n }\n\n return clonedField;\n },\n\n _setRawRelation(relation: RawRelationConfig) {\n _rawRelation = relation;\n },\n };\n\n return field;\n}\n\nconst createField = createTailorDBField;\n\n/**\n * Create a UUID field.\n * @param options - Field configuration options\n * @returns A UUID field\n * @example db.uuid()\n * @example db.uuid({ optional: true })\n */\nfunction uuid<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"uuid\", options);\n}\n\n/**\n * Create a string field.\n * @param options - Field configuration options\n * @returns A string field\n * @example db.string()\n * @example db.string({ optional: true })\n */\nfunction string<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"string\", options);\n}\n\n/**\n * Create a boolean field.\n * Note: The method name is `bool` but creates a `boolean` type field.\n * @param options - Field configuration options\n * @returns A boolean field\n * @example db.bool()\n * @example db.bool({ optional: true })\n */\nfunction bool<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"boolean\", options);\n}\n\n/**\n * Create an integer field.\n * @param options - Field configuration options\n * @returns An integer field\n * @example db.int()\n * @example db.int({ optional: true })\n */\nfunction int<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"integer\", options);\n}\n\n/**\n * Create a float (decimal number) field.\n * @param options - Field configuration options\n * @returns A float field\n * @example db.float()\n * @example db.float({ optional: true })\n */\nfunction float<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"float\", options);\n}\n\ninterface DecimalFieldOptions extends FieldOptions {\n scale?: number;\n}\n\n/**\n * Create a decimal field (stored as string for precision).\n * @param options - Field configuration options including optional scale (0-12)\n * @returns A decimal field\n * @example db.decimal()\n * @example db.decimal({ scale: 2 })\n * @example db.decimal({ scale: 2, optional: true })\n */\nfunction decimal<const Opt extends DecimalFieldOptions>(options?: Opt) {\n if (options?.scale !== undefined) {\n if (!Number.isInteger(options.scale) || options.scale < 0 || options.scale > 12) {\n throw new Error(\"scale must be an integer between 0 and 12\");\n }\n }\n const field = createField(\"decimal\", options);\n if (options?.scale !== undefined) {\n field._metadata.scale = options.scale;\n }\n return field;\n}\n\n/**\n * Create a date field (date only, no time component).\n * Format: \"yyyy-MM-dd\"\n * @param options - Field configuration options\n * @returns A date field\n * @example db.date()\n */\nfunction date<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"date\", options);\n}\n\n/**\n * Create a datetime field (date and time).\n * Format: ISO 8601, such as \"yyyy-MM-ddTHH:mm:ssZ\" or \"yyyy-MM-ddTHH:mm:ss+09:00\"\n * @param options - Field configuration options\n * @returns A datetime field\n * @example db.datetime()\n */\nfunction datetime<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"datetime\", options);\n}\n\n/**\n * Create a time field (time only, no date component).\n * Format: \"HH:mm\"\n * @param options - Field configuration options\n * @returns A time field\n * @example db.time()\n */\nfunction time<const Opt extends FieldOptions>(options?: Opt) {\n return createField(\"time\", options);\n}\n\n/**\n * Create an enum field with at least one allowed string value.\n * @param values - Array of allowed string values, or array of `{ value, description }` objects\n * @param options - Field configuration options\n * @returns An enum field\n * @example db.enum([\"active\", \"inactive\", \"suspended\"])\n * @example db.enum([\"small\", \"medium\", \"large\"], { optional: true })\n */\nfunction _enum<const V extends AllowedValues, const Opt extends FieldOptions>(\n values: V,\n options?: Opt,\n): TailorDBField<\n { type: \"enum\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<AllowedValuesOutput<V>, Opt>\n> {\n return createField<\"enum\", Opt, AllowedValuesOutput<V>>(\"enum\", options, undefined, values);\n}\n\n/**\n * Create a nested object field with sub-fields.\n * @param fields - Record of nested field definitions\n * @param options - Field configuration options\n * @returns A nested object field\n * @example db.object({ street: db.string(), city: db.string(), zip: db.string() })\n * @example db.object({ name: db.string() }, { optional: true })\n */\nfunction object<\n const F extends Record<string, TailorAnyDBField> &\n ExcludeNestedDBFields<F> &\n ExcludeHookedDBFields<F> &\n ExcludeDefaultedDBFields<F>,\n const Opt extends FieldOptions,\n>(fields: F, options?: Opt) {\n return createField(\"nested\", options, fields) as unknown as TailorDBField<\n { type: \"nested\"; array: Opt extends { array: true } ? true : false },\n FieldOutput<InferFieldsOutput<F>, Opt>,\n F\n >;\n}\n\n/**\n * Creates a new TailorDBType instance.\n * @param name - Table name\n * @param fields - Field definitions\n * @param options - Table options\n * @param options.pluralForm - Optional plural form\n * @param options.description - Optional description\n * @returns A new TailorDBType\n */\nfunction createTailorDBType<\n // oxlint-disable-next-line no-explicit-any\n const Fields extends Record<string, TailorAnyDBField> = any,\n User extends object = InferredAttributes,\n>(\n name: string,\n fields: Fields,\n options: { pluralForm?: string; description?: string },\n): TailorDBType<Fields, User, DefinedDBTypeMetadata> {\n let _description = options.description;\n let _settings: TypeFeatures = {};\n let _indexes: IndexDef<TailorDBType<Fields, User, DefinedDBTypeMetadata>>[] = [];\n const _permissions: RawPermissions = {};\n let _files: Record<string, string> = {};\n const _plugins: PluginAttachment[] = [];\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n let _typeHook: { create?: Function; update?: Function } | undefined;\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n let _typeValidate: Function | undefined;\n const _definedMethods = new Set<keyof DefinedDBTypeMetadata>();\n if (options.description !== undefined) {\n _definedMethods.add(\"description\");\n }\n\n function runMethodOnce<T>(method: keyof DefinedDBTypeMetadata, action: () => T): T {\n if (_definedMethods.has(method)) {\n throw new Error(`.${method}() has already been set`);\n }\n const result = action();\n _definedMethods.add(method);\n return result;\n }\n type TypeAfter<Key extends keyof DefinedDBTypeMetadata> = TailorDBType<\n Fields,\n User,\n WithDBTypeMetadata<DefinedDBTypeMetadata, Key>\n >;\n type TypeAfterUser<\n NextUser extends object,\n Key extends keyof DefinedDBTypeMetadata,\n > = TailorDBType<Fields, NextUser, WithDBTypeMetadata<DefinedDBTypeMetadata, Key>>;\n type TypeAfterPlugin<Config extends Record<string, unknown>> = TailorDBType<\n PluginExtendedFields<Fields, Config>,\n User,\n DefinedDBTypeMetadata\n >;\n\n if (options.pluralForm) {\n if (name === options.pluralForm) {\n throw new Error(`The name and the plural form must be different. name=${name}`);\n }\n _settings.pluralForm = options.pluralForm;\n }\n\n const dbType: TailorDBType<Fields, User, DefinedDBTypeMetadata> = {\n name,\n fields: { ...fields },\n _output: null as unknown as InferFieldsOutput<Fields>,\n _description,\n\n get metadata(): TailorDBTypeMetadata {\n // Convert indexes to the format expected by the manifest\n const indexes: Record<string, { fields: string[]; unique?: boolean }> = {};\n if (_indexes.length > 0) {\n _indexes.forEach((index) => {\n const fieldNames = index.fields.map((field) => String(field));\n const key = index.name || `idx_${fieldNames.join(\"_\")}`;\n indexes[key] = {\n fields: fieldNames,\n unique: index.unique,\n };\n });\n }\n\n return {\n name: this.name,\n description: _description,\n settings: _settings,\n permissions: _permissions,\n files: _files,\n ...(Object.keys(indexes).length > 0 && { indexes }),\n ...(_typeHook && { typeHook: _typeHook }),\n ...(_typeValidate && { typeValidate: _typeValidate }),\n };\n },\n\n hooks(hook: TypeHook<Fields>): TypeAfter<\"hooks\"> {\n return runMethodOnce(\"hooks\", () => {\n _typeHook = hook;\n return this as TypeAfter<\"hooks\">;\n });\n },\n\n validate(fn: TypeValidateFn<Fields>): TypeAfter<\"validate\"> {\n return runMethodOnce(\"validate\", () => {\n _typeValidate = fn;\n return this as TypeAfter<\"validate\">;\n });\n },\n\n features(features: Omit<TypeFeatures, \"pluralForm\">): TypeAfter<\"features\"> {\n return runMethodOnce(\"features\", () => {\n _settings = {\n ..._settings,\n ...features,\n };\n return this as TypeAfter<\"features\">;\n });\n },\n\n indexes(\n ...indexes: IndexDef<TailorDBType<Fields, User, DefinedDBTypeMetadata>>[]\n ): TypeAfter<\"indexes\"> {\n return runMethodOnce(\"indexes\", () => {\n _indexes = indexes;\n return this as TypeAfter<\"indexes\">;\n });\n },\n\n files<const F extends string>(\n files: Record<F, string> & FileKeyConflictError<Fields, User>,\n ): TailorDBType<Fields, User, WithDBTypeMetadata<DefinedDBTypeMetadata, \"files\">, F> {\n return runMethodOnce(\"files\", () => {\n _files = files;\n return this as TailorDBType<\n Fields,\n User,\n WithDBTypeMetadata<DefinedDBTypeMetadata, \"files\">,\n F\n >;\n });\n },\n\n permission<\n U extends object = User,\n P extends TailorTypePermission<U, output<TailorDBType<Fields, User, DefinedDBTypeMetadata>>> =\n TailorTypePermission<U, output<TailorDBType<Fields, User, DefinedDBTypeMetadata>>>,\n >(permission: P): TypeAfterUser<U, \"permission\"> {\n return runMethodOnce(\"permission\", () => {\n const ret = this as unknown as TypeAfterUser<U, \"permission\">;\n _permissions.record = permission as RawPermissions[\"record\"];\n return ret;\n });\n },\n\n gqlPermission<\n U extends object = User,\n P extends TailorTypeGqlPermission<U> = TailorTypeGqlPermission<U>,\n >(permission: P): TypeAfterUser<U, \"gqlPermission\"> {\n return runMethodOnce(\"gqlPermission\", () => {\n const ret = this as unknown as TypeAfterUser<U, \"gqlPermission\">;\n _permissions.gql = permission as RawPermissions[\"gql\"];\n return ret;\n });\n },\n\n description(description: string): TypeAfter<\"description\"> {\n return runMethodOnce(\"description\", () => {\n _description = description;\n this._description = description;\n return this as TypeAfter<\"description\">;\n });\n },\n\n pickFields<K extends keyof Fields, const Opt extends FieldOptions>(keys: K[], options?: Opt) {\n const result = {} as Record<K, TailorAnyDBField>;\n for (const key of keys) {\n const field = this.fields[key] as TailorAnyDBField | undefined;\n if (!field) {\n // A plugin-injected field is only added to `fields` after `tailor\n // generate` runs — see the \"Injecting fields into the attached\n // table's type\" section of the plugin docs.\n throw new Error(\n `pickFields(): field \"${String(key)}\" does not exist on this table yet. If it comes from a plugin's .plugin() call, it is only added to the table after \\`tailor generate\\` runs.`,\n );\n }\n if (options) {\n result[key] = field.clone(options);\n } else {\n result[key] = field;\n }\n }\n // oxlint-disable-next-line no-explicit-any\n return result as any;\n },\n\n omitFields<K extends keyof Fields>(keys: K[]): Omit<Fields, K> {\n const keysSet = new Set(keys);\n const result = {} as Record<string, TailorAnyDBField>;\n for (const key in this.fields) {\n if (Object.hasOwn(this.fields, key) && !keysSet.has(key as unknown as K)) {\n result[key] = this.fields[key] as TailorAnyDBField;\n }\n }\n return result as Omit<Fields, K>;\n },\n\n get plugins(): PluginAttachment[] {\n return _plugins;\n },\n\n plugin<const Config extends Record<string, unknown>>(\n config: Config & PluginConfigGuard<Fields, Config, never>,\n ): TypeAfterPlugin<Config> {\n for (const [pluginId, pluginConfig] of Object.entries(config)) {\n _plugins.push({ pluginId, config: pluginConfig });\n }\n return this as unknown as TypeAfterPlugin<Config>;\n },\n };\n\n return brandValue(dbType, \"tailordb-type\");\n}\n\nfunction createIdField() {\n return uuid();\n}\ntype idField = ReturnType<typeof createIdField>;\nlet idField: idField | undefined;\ntype DBTable<\n F extends { id?: never } & Record<string, TailorAnyDBField>,\n Defined extends DefinedDBTypeMetadata = DefinedDBTypeMetadata,\n> = TailorDBInstance<{ id: idField } & F, InferredAttributes, Defined>;\n\n/**\n * Creates a new database table with the specified fields.\n * An `id` field (UUID) is automatically added to every table.\n * @param name - The name of the table, or a tuple of [name, pluralForm]\n * @param fields - The field definitions for the table\n * @returns A new TailorDB table instance\n * @example\n * export const user = db.table(\"User\", {\n * name: db.string(),\n * email: db.string(),\n * age: db.int({ optional: true }),\n * role: db.enum([\"admin\", \"member\"]),\n * ...db.fields.timestamps(),\n * });\n * // Always export both the value and type:\n * export type user = typeof user;\n */\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n fields: F,\n): DBTable<F>;\n/**\n * Creates a new database table with the specified fields and description.\n * An `id` field (UUID) is automatically added to every table.\n * @param name - The name of the table, or a tuple of [name, pluralForm]\n * @param description - A description of the table\n * @param fields - The field definitions for the table\n * @returns A new TailorDB table instance\n */\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n description: string,\n fields: F,\n): DBTable<F, WithDBTypeMetadata<DefinedDBTypeMetadata, \"description\">>;\nfunction dbTable<const F extends { id?: never } & Record<string, TailorAnyDBField>>(\n name: string | [string, string],\n fieldsOrDescription: string | F,\n fields?: F,\n): DBTable<F> | DBTable<F, WithDBTypeMetadata<DefinedDBTypeMetadata, \"description\">> {\n const typeName = Array.isArray(name) ? name[0] : name;\n const pluralForm = Array.isArray(name) ? name[1] : undefined;\n\n let description: string | undefined;\n let fieldDef: F;\n if (typeof fieldsOrDescription === \"string\") {\n description = fieldsOrDescription;\n fieldDef = fields as F;\n } else {\n fieldDef = fieldsOrDescription;\n }\n return createTailorDBType<{ id: idField } & F>(\n typeName,\n {\n id: (idField ??= createIdField()),\n ...fieldDef,\n },\n { pluralForm, description },\n );\n}\n\n// `Function.prototype.toString()` of this hook is embedded verbatim into deployed\n// schemas and migration diffs (see parser/service/tailordb/hooks-validate-precompiled-expr.ts\n// and field.ts). Its source text depends on how the SDK itself was built (e.g.\n// minification), so a fixed expression is pinned onto it directly here instead -\n// configure cannot import parser's `setPrecompiledScriptExpr` runtime helper across\n// the module boundary, only the symbol registry key and map types. Keep\n// this literal in sync with the \"timestamps() updatedAt hook resolves to the pinned\n// expr\" test in parser/service/tailordb/field.precompiled.test.ts, which fails if it\n// ever drifts from what this hook's own source naturally produces.\ntype TimestampsUpdatedAtHookFn = UpdateHookFn<string | Date | null, string | Date>;\nconst PRECOMPILED_EXPR_KEY: PrecompiledScriptExprKey =\n \"tailor-platform/sdk:precompiled-script-expr\";\nconst precompiledExprs: PrecompiledScriptExprMap = {\n \"hooks.update\":\n \"(({ input, now }) => input ?? now)({ input: _value, oldValue: _oldValue, invoker: _principal, now: _now })\",\n};\nlet timestampsUpdatedAtHook: TimestampsUpdatedAtHookFn | undefined;\nfunction getTimestampsUpdatedAtHook(): TimestampsUpdatedAtHookFn {\n return (timestampsUpdatedAtHook ??= Object.assign(\n ({ input, now }: Parameters<TimestampsUpdatedAtHookFn>[0]) => input ?? now,\n { [Symbol.for(PRECOMPILED_EXPR_KEY)]: precompiledExprs },\n ));\n}\n\n/** TailorDB schema builder utilities for defining tables and fields. */\nexport const db = {\n table: dbTable,\n uuid,\n string,\n bool,\n int,\n float,\n decimal,\n date,\n datetime,\n time,\n enum: _enum,\n object,\n fields: {\n /**\n * Creates standard timestamp fields (createdAt, updatedAt) with automatic defaults.\n * Both fields default to the current time on create. updatedAt is also refreshed on update.\n * User-specified values are respected when provided (e.g. seeding historical records).\n * @returns An object with createdAt and updatedAt fields\n * @example\n * const model = db.table(\"Model\", {\n * name: db.string(),\n * ...db.fields.timestamps(),\n * });\n */\n timestamps: () => ({\n createdAt: datetime().default(\"now\").description(\"Record creation timestamp\"),\n updatedAt: datetime()\n .default(\"now\")\n .hooks({ update: getTimestampsUpdatedAtHook() })\n .description(\"Record update timestamp\"),\n }),\n },\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"secret-file-C9wp_FCX.mjs","names":[],"sources":["../src/cli/shared/secret-file.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\n\nconst DIR_MODE = 0o700;\nconst FILE_MODE = 0o600;\n\n/**\n * Best-effort chmod that only fires when the current mode differs from the\n * target. No-op on Windows (where POSIX mode bits are advisory and ACLs\n * govern access) and on missing paths / permission errors.\n * @param target - Path to chmod\n * @param mode - Desired POSIX mode bits\n */\nfunction chmodIfDifferent(target: string, mode: number): void {\n if (process.platform === \"win32\") return;\n try {\n if ((fs.statSync(target).mode & 0o777) !== mode) {\n fs.chmodSync(target, mode);\n }\n } catch {\n // Missing path or permission error — best-effort.\n }\n}\n\n/**\n * Write a file that may contain secrets with restrictive permissions.\n * Creates the parent directory with 0o700 and the file with 0o600 on POSIX\n * systems so other users on the host cannot read access tokens, refresh\n * tokens, or crash report payloads. On Windows the POSIX mode bits are\n * effectively ignored and ACLs govern access, so the chmod calls are\n * best-effort and silently skipped.\n * @param filePath - Absolute path to write\n * @param content - File content\n */\nexport function writeSecretFile(filePath: string, content: string | Buffer): void {\n ensureSecretDir(path.dirname(filePath));\n fs.writeFileSync(filePath, content, { mode: FILE_MODE });\n chmodIfDifferent(filePath, FILE_MODE);\n}\n\n/**\n * Ensure a directory exists with 0o700 permissions on POSIX systems.\n * `mkdirSync({ recursive: true })` does not chmod existing directories,\n * so this also tightens permissions on directories that were previously\n * created with looser modes.\n * @param dir - Directory path\n */\nexport function ensureSecretDir(dir: string): void {\n fs.mkdirSync(dir, { recursive: true, mode: DIR_MODE });\n chmodIfDifferent(dir, DIR_MODE);\n}\n\n/**\n * Tighten an existing file and its parent directory to secret-file modes\n * (0o600 / 0o700) if they are looser. Used by read paths that may not\n * trigger a subsequent write, so that legacy world-readable files are\n * still secured the next time the CLI runs. Silent no-op on Windows and\n * on missing files / permission errors.\n * @param filePath - Absolute path that should be 0o600 and live under a 0o700 directory\n */\nexport function tightenSecretFilePermissions(filePath: string): void {\n chmodIfDifferent(filePath, FILE_MODE);\n chmodIfDifferent(path.dirname(filePath), DIR_MODE);\n}\n"],"mappings":"kDAaA,SAAS,iBAAiB,EAAgB,EAAoB,CACxD,WAAQ,WAAa,QACzB,GAAI,EACG,EAAG,SAAS,CAAM,CAAC,CAAC,KAAO,OAAW,GACzC,EAAG,UAAU,EAAQ,CAAI,CAE7B,MAAQ,CAER,CACF,CAYA,SAAgB,gBAAgB,EAAkB,EAAgC,CAChF,gBAAgB,EAAK,QAAQ,CAAQ,CAAC,EACtC,EAAG,cAAc,EAAU,EAAS,CAAE,KAAM,GAAU,CAAC,EACvD,iBAAiB,EAAU,GAAS,CACtC,CASA,SAAgB,gBAAgB,EAAmB,CACjD,EAAG,UAAU,EAAK,CAAE,UAAW,GAAM,KAAM,GAAS,CAAC,EACrD,iBAAiB,EAAK,GAAQ,CAChC,CAUA,SAAgB,6BAA6B,EAAwB,CACnE,iBAAiB,EAAU,GAAS,EACpC,iBAAiB,EAAK,QAAQ,CAAQ,EAAG,GAAQ,CACnD"}
1
+ {"version":3,"file":"secret-file-C9wp_FCX.mjs","names":["path","fs","target","mode","filePath","content","dir"],"sources":["../src/cli/shared/secret-file.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"pathe\";\n\nconst DIR_MODE = 0o700;\nconst FILE_MODE = 0o600;\n\n/**\n * Best-effort chmod that only fires when the current mode differs from the\n * target. No-op on Windows (where POSIX mode bits are advisory and ACLs\n * govern access) and on missing paths / permission errors.\n * @param target - Path to chmod\n * @param mode - Desired POSIX mode bits\n */\nfunction chmodIfDifferent(target: string, mode: number): void {\n if (process.platform === \"win32\") return;\n try {\n if ((fs.statSync(target).mode & 0o777) !== mode) {\n fs.chmodSync(target, mode);\n }\n } catch {\n // Missing path or permission error — best-effort.\n }\n}\n\n/**\n * Write a file that may contain secrets with restrictive permissions.\n * Creates the parent directory with 0o700 and the file with 0o600 on POSIX\n * systems so other users on the host cannot read access tokens, refresh\n * tokens, or crash report payloads. On Windows the POSIX mode bits are\n * effectively ignored and ACLs govern access, so the chmod calls are\n * best-effort and silently skipped.\n * @param filePath - Absolute path to write\n * @param content - File content\n */\nexport function writeSecretFile(filePath: string, content: string | Buffer): void {\n ensureSecretDir(path.dirname(filePath));\n fs.writeFileSync(filePath, content, { mode: FILE_MODE });\n chmodIfDifferent(filePath, FILE_MODE);\n}\n\n/**\n * Ensure a directory exists with 0o700 permissions on POSIX systems.\n * `mkdirSync({ recursive: true })` does not chmod existing directories,\n * so this also tightens permissions on directories that were previously\n * created with looser modes.\n * @param dir - Directory path\n */\nexport function ensureSecretDir(dir: string): void {\n fs.mkdirSync(dir, { recursive: true, mode: DIR_MODE });\n chmodIfDifferent(dir, DIR_MODE);\n}\n\n/**\n * Tighten an existing file and its parent directory to secret-file modes\n * (0o600 / 0o700) if they are looser. Used by read paths that may not\n * trigger a subsequent write, so that legacy world-readable files are\n * still secured the next time the CLI runs. Silent no-op on Windows and\n * on missing files / permission errors.\n * @param filePath - Absolute path that should be 0o600 and live under a 0o700 directory\n */\nexport function tightenSecretFilePermissions(filePath: string): void {\n chmodIfDifferent(filePath, FILE_MODE);\n chmodIfDifferent(path.dirname(filePath), DIR_MODE);\n}\n"],"mappings":"kDAaA,SAAS,iBAAiB,EAAgB,EAAoB,CAC5D,GAAI,QAAQ,WAAa,QACzB,GAAI,EACGC,EAAG,SAASC,CAAM,CAAC,CAAC,KAAO,OAAWC,GACzCF,EAAG,UAAUC,EAAQC,CAAI,CAE7B,MAAQ,CAER,CACF,CAYA,SAAgB,gBAAgB,EAAkB,EAAgC,CAChF,gBAAgBH,EAAK,QAAQI,CAAQ,CAAC,EACtCH,EAAG,cAAcG,EAAUC,EAAS,CAAE,KAAM,GAAU,CAAC,EACvD,iBAAiBD,EAAU,GAAS,CACtC,CASA,SAAgB,gBAAgB,EAAmB,CACjDH,EAAG,UAAUK,EAAK,CAAE,UAAW,GAAM,KAAM,GAAS,CAAC,EACrD,iBAAiBA,EAAK,GAAQ,CAChC,CAUA,SAAgB,6BAA6B,EAAwB,CACnE,iBAAiBF,EAAU,GAAS,EACpC,iBAAiBJ,EAAK,QAAQI,CAAQ,EAAG,GAAQ,CACnD"}
@@ -1 +1 @@
1
- {"version":3,"file":"secretmanager-5olfnI1b.mjs","names":[],"sources":["../src/vitest/mocks/shared.ts","../src/vitest/mocks/secretmanager.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"],"mappings":"sEAKA,SAAgB,YAA8B,EAAW,EAAqC,CAO5F,OANA,OAAO,eAAe,EAAQ,OAAO,QAAS,CAC5C,MAAO,EACP,WAAY,GACZ,SAAU,GACV,aAAc,EAChB,CAAC,EACM,CACT,CAGA,SAAgB,YAAkC,CAChD,IAAM,EAAI,WAQV,MAPA,CAIE,EAAE,SAAS,CAAE,QAAS,CAAE,eAAkB,IAAK,CAAE,EAG5C,EAAE,MACX,CAGA,SAAgB,cAAoC,CAClD,IAAM,EAAI,WAKV,MAJA,CACE,EAAE,WAAW,CAAC,EAGT,EAAE,QACX,CCZA,MAAM,EAAe,OAAO,mBAAmB,EAoB/C,SAAgB,kBAAkB,EAAoC,CAAC,EAAG,CACxE,IAAM,EAAO,WAAW,EAClB,EAAO,EAAK,cAEZ,EAA4D,CAGhE,MAAO,gBAAiB,IAAO,EAAa,EAAE,OAAiC,CAAC,CAAC,CACnF,EACA,IAAK,GAAM,CAAC,EAAO,KAAY,OAAO,QAAQ,EAAQ,SAAW,CAAC,CAAC,EACjE,EAAO,MAAM,GAAS,CAAE,GAAG,EAAO,MAAM,GAAQ,GAAG,CAAQ,EAG7D,eAAe,iBAAiB,EAAe,EAA2C,CACxF,OAAO,EAAO,MAAM,EAAM,GAAG,EAC/B,CAEA,eAAe,kBACb,EACA,EAC6C,CAC7C,IAAM,EAAY,EAAO,MAAM,IAAU,CAAC,EACpC,EAAiC,CAAC,EACxC,IAAK,IAAM,KAAQ,EACb,KAAQ,IACV,EAAO,GAAQ,EAAc,EAAU,GAAO,4BAA4B,GAAM,GAGpF,OAAO,CACT,CAEA,IAAM,EAAY,EAAG,GAAG,gBAAgB,EAClC,EAAa,EAAG,GAAG,iBAAiB,EA+D1C,MA7DA,GAAK,cAAgB,CAAE,YAAW,cAAa,GAAe,CAAO,EA6D9D,YAAY,CAzDjB,YAEA,aAEA,WAAW,EAAuD,CAChE,EAAO,MAAQ,CACjB,EAEA,UAAU,EAAe,EAAc,EAAqB,CAC1D,EAAO,MAAQ,CACb,GAAG,EAAO,OACT,GAAQ,CAAE,GAAG,EAAO,MAAM,IAAS,GAAO,CAAM,CACnD,CACF,EAEA,aAAa,EAAe,EAAuC,CACjE,EAAO,MAAQ,CACb,GAAG,EAAO,OACT,GAAQ,CAAE,GAAG,EAAO,MAAM,GAAQ,GAAG,CAAQ,CAChD,CACF,EAEA,IAAI,OAAsB,CAkBxB,MAAO,CAbL,GAAG,EAAU,KAAK,MAAM,KAAK,EAAM,KAAO,CACxC,MAAO,EAAU,KAAK,oBAAoB,IAAM,EAChD,KAAM,CAAE,OAAQ,YAAsB,MAAO,EAAK,GAAI,KAAM,EAAK,EAAG,CACtE,EAAE,EACF,GAAG,EAAW,KAAK,MAAM,KAAK,EAAM,KAAO,CACzC,MAAO,EAAW,KAAK,oBAAoB,IAAM,EACjD,KAAM,CACJ,OAAQ,aACR,MAAO,EAAK,GACZ,MAAO,EAAK,EACd,CACF,EAAE,CAES,CAAC,CAAC,UAAU,EAAG,IAAM,EAAE,MAAQ,EAAE,KAAK,CAAC,CAAC,IAAK,GAAM,EAAE,IAAI,CACxE,EAEA,OAAc,CACZ,EAAU,UAAU,EACpB,EAAW,UAAU,CACvB,EAEA,OAAc,CACZ,EAAO,MAAQ,CAAC,EAChB,EAAU,UAAU,EACpB,EAAU,mBAAmB,gBAAgB,EAC7C,EAAW,UAAU,EACrB,EAAW,mBAAmB,iBAAiB,CACjD,CAGsB,MAAS,CAC/B,EAAK,cAAgB,CACvB,CAAC,CACH"}
1
+ {"version":3,"file":"secretmanager-5olfnI1b.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"],"sources":["../src/vitest/mocks/shared.ts","../src/vitest/mocks/secretmanager.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"],"mappings":"sEAKA,SAAgB,YAA8B,EAAW,EAAqC,CAO5F,OANA,OAAO,eAAeC,EAAQ,OAAO,QAAS,CAC5C,MAAOC,EACP,WAAY,GACZ,SAAU,GACV,aAAc,EAChB,CAAC,EACMD,CACT,CAGA,SAAgB,YAAkC,CAChD,IAAME,EAAI,WAQV,MAPA,CAIEA,EAAE,SAAS,CAAE,QAAS,CAAE,eAAkB,IAAK,CAAE,EAG5CA,EAAE,MACX,CAGA,SAAgB,cAAoC,CAClD,IAAMA,EAAI,WAKV,MAJA,CACEA,EAAE,WAAW,CAAC,EAGTA,EAAE,QACX,CCZA,MAAMC,EAAe,OAAO,mBAAmB,EAoB/C,SAAgB,kBAAkB,EAAoC,CAAC,EAAG,CACxE,IAAMC,EAAO,WAAW,EAClBC,EAAOD,EAAK,cAEZE,EAA4D,CAGhE,MAAO,gBAAiBD,IAAOF,EAAa,EAAE,OAAiC,CAAC,CAAC,CACnF,EACA,IAAK,GAAM,CAACI,EAAOC,KAAY,OAAO,QAAQC,EAAQ,SAAW,CAAC,CAAC,EACjE,EAAO,MAAMF,GAAS,CAAE,GAAGD,EAAO,MAAMC,GAAQ,GAAGC,CAAQ,EAG7D,eAAe,iBAAiB,EAAe,EAA2C,CACxF,OAAOF,EAAO,MAAMC,EAAM,GAAGG,EAC/B,CAEA,eAAe,kBACb,EACA,EAC6C,CAC7C,IAAMC,EAAYL,EAAO,MAAMC,IAAU,CAAC,EACpCK,EAAiC,CAAC,EACxC,IAAK,IAAMF,KAAQG,EACbH,KAAQC,IACV,EAAOD,GAAQX,EAAcY,EAAUD,GAAO,4BAA4BA,GAAM,GAGpF,OAAOE,CACT,CAEA,IAAME,EAAYC,EAAG,GAAG,gBAAgB,EAClCC,EAAaD,EAAG,GAAG,iBAAiB,EA+D1C,MA7DA,GAAK,cAAgB,CAAE,YAAW,cAAaZ,GAAeG,CAAO,EA6D9D,YAAY,CAzDjB,YAEA,aAEA,WAAW,EAAuD,CAChE,EAAO,MAAQE,CACjB,EAEA,UAAU,EAAe,EAAc,EAAqB,CAC1D,EAAO,MAAQ,CACb,GAAGF,EAAO,OACTC,GAAQ,CAAE,GAAGD,EAAO,MAAMC,IAASG,GAAOO,CAAM,CACnD,CACF,EAEA,aAAa,EAAe,EAAuC,CACjE,EAAO,MAAQ,CACb,GAAGX,EAAO,OACTC,GAAQ,CAAE,GAAGD,EAAO,MAAMC,GAAQ,GAAGC,CAAQ,CAChD,CACF,EAEA,IAAI,OAAsB,CAkBxB,MAAO,CAbL,GAAGM,EAAU,KAAK,MAAM,KAAK,EAAM,KAAO,CACxC,MAAOA,EAAU,KAAK,oBAAoBI,IAAM,EAChD,KAAM,CAAE,OAAQ,YAAsB,MAAOC,EAAK,GAAI,KAAMA,EAAK,EAAG,CACtE,EAAE,EACF,GAAGH,EAAW,KAAK,MAAM,KAAK,EAAM,KAAO,CACzC,MAAOA,EAAW,KAAK,oBAAoBE,IAAM,EACjD,KAAM,CACJ,OAAQ,aACR,MAAOC,EAAK,GACZ,MAAOA,EAAK,EACd,CACF,EAAE,CAES,CAAC,CAAC,UAAU,EAAG,IAAMC,EAAE,MAAQC,EAAE,KAAK,CAAC,CAAC,IAAK,GAAM,EAAE,IAAI,CACxE,EAEA,OAAc,CACZP,EAAU,UAAU,EACpBE,EAAW,UAAU,CACvB,EAEA,OAAc,CACZ,EAAO,MAAQ,CAAC,EAChBF,EAAU,UAAU,EACpBA,EAAU,mBAAmB,gBAAgB,EAC7CE,EAAW,UAAU,EACrBA,EAAW,mBAAmB,iBAAiB,CACjD,CAGsB,MAAS,CAC/B,EAAK,cAAgBX,CACvB,CAAC,CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"secretmanager-vHQoXdQz.mjs","names":[],"sources":["../src/runtime/secretmanager.ts"],"sourcesContent":["/**\n * Secret manager utilities.\n *\n * Thin typed wrapper around the platform-provided `tailor.secretmanager` runtime API.\n * At runtime this delegates to `globalThis.tailor.secretmanager`. Use\n * `mockSecretmanager` from `@tailor-platform/sdk/vitest` to mock these calls\n * in unit tests.\n *\n * `vault` is narrowed to the vault names declared via `defineSecretManager()` once\n * `tailor.d.ts` has been generated (via `tailor deploy`/`generate`), and `name`/`names`\n * are further narrowed to that vault's declared secret names.\n * @example\n * import { secretmanager } from \"@tailor-platform/sdk/runtime\";\n *\n * const apiKey = await secretmanager.getSecret(\"my-vault\", \"API_KEY\");\n * const all = await secretmanager.getSecrets(\"my-vault\", [\"A\", \"B\"]);\n */\n\n// Import from the public entry (not `#/configure/types/secret-vault-name`) so this d.ts\n// references `@tailor-platform/sdk` externally instead of inlining the registry — the\n// same generated `declare module \"@tailor-platform/sdk\"` that narrows\n// `aigateway.get`/`authconnection.getConnectionToken` then also narrows this entry.\nimport type { SecretNameFor, SecretVaultName } from \"@tailor-platform/sdk\";\n\n/**\n * Platform API surface for `tailor.secretmanager`. Describes the shape the\n * platform runtime injects on `globalThis.tailor.secretmanager`.\n */\nexport interface TailorSecretmanagerAPI {\n /**\n * Returns multiple secrets from a vault. Missing names are omitted from the result.\n * @param vault - Vault name, as passed to `defineSecretManager()`\n * @param names - Secret names to fetch\n * @returns Partial record keyed by the requested names\n */\n getSecrets<const V extends SecretVaultName, const T extends readonly SecretNameFor<V>[]>(\n vault: V,\n names: T,\n ): Promise<Partial<Record<T[number], string>>>;\n\n /**\n * Returns a single secret from a vault, or `undefined` when missing.\n * @param vault - Vault name, as passed to `defineSecretManager()`\n * @param name - Secret name\n * @returns The secret value, or `undefined` if not present\n */\n getSecret<V extends SecretVaultName>(\n vault: V,\n name: SecretNameFor<V>,\n ): Promise<string | undefined>;\n}\n\nconst api = (): TailorSecretmanagerAPI =>\n (globalThis as unknown as { tailor: { secretmanager: TailorSecretmanagerAPI } }).tailor\n .secretmanager;\n\nconst getSecrets: TailorSecretmanagerAPI[\"getSecrets\"] = (...args) => api().getSecrets(...args);\n\nconst getSecret: TailorSecretmanagerAPI[\"getSecret\"] = (...args) => api().getSecret(...args);\n\n/** Runtime wrapper namespace for `tailor.secretmanager`. */\nexport const secretmanager = {\n getSecrets,\n getSecret,\n} as const satisfies TailorSecretmanagerAPI;\n"],"mappings":"AAoDA,MAAM,QACH,WAAgF,OAC9E,cAOQ,EAAgB,CAC3B,YANwD,GAAG,IAAS,IAAI,CAAC,CAAC,WAAW,GAAG,CAAI,EAO5F,WALsD,GAAG,IAAS,IAAI,CAAC,CAAC,UAAU,GAAG,CAAI,CAM3F"}
1
+ {"version":3,"file":"secretmanager-vHQoXdQz.mjs","names":["secretmanager","args"],"sources":["../src/runtime/secretmanager.ts"],"sourcesContent":["/**\n * Secret manager utilities.\n *\n * Thin typed wrapper around the platform-provided `tailor.secretmanager` runtime API.\n * At runtime this delegates to `globalThis.tailor.secretmanager`. Use\n * `mockSecretmanager` from `@tailor-platform/sdk/vitest` to mock these calls\n * in unit tests.\n *\n * `vault` is narrowed to the vault names declared via `defineSecretManager()` once\n * `tailor.d.ts` has been generated (via `tailor deploy`/`generate`), and `name`/`names`\n * are further narrowed to that vault's declared secret names.\n * @example\n * import { secretmanager } from \"@tailor-platform/sdk/runtime\";\n *\n * const apiKey = await secretmanager.getSecret(\"my-vault\", \"API_KEY\");\n * const all = await secretmanager.getSecrets(\"my-vault\", [\"A\", \"B\"]);\n */\n\n// Import from the public entry (not `#/configure/types/secret-vault-name`) so this d.ts\n// references `@tailor-platform/sdk` externally instead of inlining the registry — the\n// same generated `declare module \"@tailor-platform/sdk\"` that narrows\n// `aigateway.get`/`authconnection.getConnectionToken` then also narrows this entry.\nimport type { SecretNameFor, SecretVaultName } from \"@tailor-platform/sdk\";\n\n/**\n * Platform API surface for `tailor.secretmanager`. Describes the shape the\n * platform runtime injects on `globalThis.tailor.secretmanager`.\n */\nexport interface TailorSecretmanagerAPI {\n /**\n * Returns multiple secrets from a vault. Missing names are omitted from the result.\n * @param vault - Vault name, as passed to `defineSecretManager()`\n * @param names - Secret names to fetch\n * @returns Partial record keyed by the requested names\n */\n getSecrets<const V extends SecretVaultName, const T extends readonly SecretNameFor<V>[]>(\n vault: V,\n names: T,\n ): Promise<Partial<Record<T[number], string>>>;\n\n /**\n * Returns a single secret from a vault, or `undefined` when missing.\n * @param vault - Vault name, as passed to `defineSecretManager()`\n * @param name - Secret name\n * @returns The secret value, or `undefined` if not present\n */\n getSecret<V extends SecretVaultName>(\n vault: V,\n name: SecretNameFor<V>,\n ): Promise<string | undefined>;\n}\n\nconst api = (): TailorSecretmanagerAPI =>\n (globalThis as unknown as { tailor: { secretmanager: TailorSecretmanagerAPI } }).tailor\n .secretmanager;\n\nconst getSecrets: TailorSecretmanagerAPI[\"getSecrets\"] = (...args) => api().getSecrets(...args);\n\nconst getSecret: TailorSecretmanagerAPI[\"getSecret\"] = (...args) => api().getSecret(...args);\n\n/** Runtime wrapper namespace for `tailor.secretmanager`. */\nexport const secretmanager = {\n getSecrets,\n getSecret,\n} as const satisfies TailorSecretmanagerAPI;\n"],"mappings":"AAoDA,MAAM,QACH,WAAgF,OAC9E,cAOQA,EAAgB,CAC3B,YANwD,GAAGC,IAAS,IAAI,CAAC,CAAC,WAAW,GAAGA,CAAI,EAO5F,WALsD,GAAGA,IAAS,IAAI,CAAC,CAAC,UAAU,GAAGA,CAAI,CAM3F"}