@tailor-platform/sdk 1.40.1 → 1.44.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 (91) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +23 -0
  3. package/dist/{actor-B2oEmlTc.d.mts → actor-DzCuoMlP.d.mts} +2 -2
  4. package/dist/application-BZRbA1pL.mjs +4 -0
  5. package/dist/{application-EvhIIVg0.mjs → application-DkVNbIuh.mjs} +95 -16
  6. package/dist/application-DkVNbIuh.mjs.map +1 -0
  7. package/dist/brand-Ll48SMXe.mjs.map +1 -1
  8. package/dist/cli/index.mjs +56 -43
  9. package/dist/cli/index.mjs.map +1 -1
  10. package/dist/cli/lib.d.mts +75 -18
  11. package/dist/cli/lib.mjs +9 -9
  12. package/dist/cli/lib.mjs.map +1 -1
  13. package/dist/cli/skills.mjs.map +1 -1
  14. package/dist/{client-CcV6Jjds.mjs → client-DQl5NPG9.mjs} +103 -43
  15. package/dist/{client-CcV6Jjds.mjs.map → client-DQl5NPG9.mjs.map} +1 -1
  16. package/dist/{client-Cn9SqhZT.mjs → client-EI4pMzvm.mjs} +1 -1
  17. package/dist/configure/index.d.mts +5 -5
  18. package/dist/configure/index.mjs +84 -4
  19. package/dist/configure/index.mjs.map +1 -1
  20. package/dist/{crash-report-CUbm1ErM.mjs → crash-report-CSSrblUv.mjs} +4 -4
  21. package/dist/{crash-report-CUbm1ErM.mjs.map → crash-report-CSSrblUv.mjs.map} +1 -1
  22. package/dist/{crash-report-CSYupJ0T.mjs → crash-report-DtkFq9JF.mjs} +1 -1
  23. package/dist/{enum-constants-Dx82rSjf.mjs → enum-constants-C3KSpsYj.mjs} +1 -1
  24. package/dist/{enum-constants-Dx82rSjf.mjs.map → enum-constants-C3KSpsYj.mjs.map} +1 -1
  25. package/dist/{errors-ChWX5ZG8.mjs → errors-_M2TVoWh.mjs} +1 -1
  26. package/dist/{errors-ChWX5ZG8.mjs.map → errors-_M2TVoWh.mjs.map} +1 -1
  27. package/dist/{file-utils-DeWpvq3T.mjs → file-utils-775qWKoo.mjs} +1 -1
  28. package/dist/{file-utils-DeWpvq3T.mjs.map → file-utils-775qWKoo.mjs.map} +1 -1
  29. package/dist/{index-Chvw1Eod.d.mts → index-0Dk-fDWi.d.mts} +2 -2
  30. package/dist/{index-CiNNNpuH.d.mts → index-BEEL1-6Z.d.mts} +2 -2
  31. package/dist/{index-D_ezppY7.d.mts → index-Br4XCvX1.d.mts} +103 -86
  32. package/dist/{index-BtXZdz-F.d.mts → index-DdsUV-aA.d.mts} +2 -2
  33. package/dist/{index-reFAYSX7.d.mts → index-ZZYEd_0R.d.mts} +2 -2
  34. package/dist/interceptor-4UC-KTno.mjs.map +1 -1
  35. package/dist/{job-p6zf8Qpg.mjs → job-BOvKyNdT.mjs} +15 -9
  36. package/dist/job-BOvKyNdT.mjs.map +1 -0
  37. package/dist/kysely/index.mjs.map +1 -1
  38. package/dist/{kysely-type-CwtvQuxh.mjs → kysely-type-BQsYEZoY.mjs} +1 -1
  39. package/dist/kysely-type-BQsYEZoY.mjs.map +1 -0
  40. package/dist/logger-DTNAMYGy.mjs.map +1 -1
  41. package/dist/{package-json-CWp8s9dE.mjs → package-json-CRzw5eUf.mjs} +1 -1
  42. package/dist/{package-json-CWp8s9dE.mjs.map → package-json-CRzw5eUf.mjs.map} +1 -1
  43. package/dist/package-json-DR_mqrCW.mjs +4 -0
  44. package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
  45. package/dist/plugin/builtin/enum-constants/index.mjs +1 -1
  46. package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
  47. package/dist/plugin/builtin/file-utils/index.mjs +1 -1
  48. package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
  49. package/dist/plugin/builtin/kysely-type/index.mjs +1 -1
  50. package/dist/plugin/builtin/seed/index.d.mts +1 -1
  51. package/dist/plugin/builtin/seed/index.mjs +1 -1
  52. package/dist/plugin/index.d.mts +2 -2
  53. package/dist/plugin/index.mjs.map +1 -1
  54. package/dist/repl-editor-DjycioU-.mjs.map +1 -1
  55. package/dist/{runtime-B9R1TzLD.mjs → runtime-l7GFD3Xg.mjs} +503 -196
  56. package/dist/runtime-l7GFD3Xg.mjs.map +1 -0
  57. package/dist/{schema-CEcfEyPN.mjs → schema-DBq6hr6h.mjs} +13 -2
  58. package/dist/{schema-CEcfEyPN.mjs.map → schema-DBq6hr6h.mjs.map} +1 -1
  59. package/dist/{seed-CyYPhvNL.mjs → seed-BkVKgsxf.mjs} +1 -1
  60. package/dist/{seed-CyYPhvNL.mjs.map → seed-BkVKgsxf.mjs.map} +1 -1
  61. package/dist/{service-SrG26B9T.mjs → service-CCnx_IFw.mjs} +2 -2
  62. package/dist/{service-SrG26B9T.mjs.map → service-CCnx_IFw.mjs.map} +1 -1
  63. package/dist/{tailor-db-field-CoFKRCYW.d.mts → tailor-db-field-D_z185oq.d.mts} +36 -6
  64. package/dist/{telemetry-BuDto_2q.mjs → telemetry-B6Le9XT-.mjs} +2 -2
  65. package/dist/{telemetry-BuDto_2q.mjs.map → telemetry-B6Le9XT-.mjs.map} +1 -1
  66. package/dist/telemetry-C_WXxIo0.mjs +4 -0
  67. package/dist/types-Duhhsx3R.mjs.map +1 -1
  68. package/dist/utils/test/index.d.mts +36 -3
  69. package/dist/utils/test/index.mjs +78 -9
  70. package/dist/utils/test/index.mjs.map +1 -1
  71. package/dist/{workflow.generated-Btz6srLR.d.mts → workflow.generated-CDCnZNkH.d.mts} +2 -2
  72. package/docs/cli/application.md +130 -5
  73. package/docs/cli/crash-report.md +12 -1
  74. package/docs/cli/executor.md +6 -4
  75. package/docs/cli/function.md +83 -1
  76. package/docs/cli/organization.md +6 -5
  77. package/docs/cli/workspace.md +16 -13
  78. package/docs/cli-reference.md +13 -10
  79. package/docs/services/executor.md +4 -0
  80. package/docs/services/idp.md +16 -0
  81. package/docs/services/resolver.md +4 -2
  82. package/docs/services/workflow.md +117 -3
  83. package/docs/testing.md +95 -7
  84. package/package.json +24 -24
  85. package/dist/application-CE2s_a6w.mjs +0 -4
  86. package/dist/application-EvhIIVg0.mjs.map +0 -1
  87. package/dist/job-p6zf8Qpg.mjs.map +0 -1
  88. package/dist/kysely-type-CwtvQuxh.mjs.map +0 -1
  89. package/dist/package-json-CPR7s5hf.mjs +0 -4
  90. package/dist/runtime-B9R1TzLD.mjs.map +0 -1
  91. package/dist/telemetry-C1hzFaiV.mjs +0 -4
@@ -1,5 +1,5 @@
1
1
  /// <reference types="@tailor-platform/function-types" />
2
- import { o as Plugin } from "./tailor-db-field-CoFKRCYW.mjs";
2
+ import { o as Plugin } from "./tailor-db-field-D_z185oq.mjs";
3
3
 
4
4
  //#region src/plugin/builtin/enum-constants/index.d.ts
5
5
  /** Unique identifier for the enum constants generator plugin. */
@@ -16,4 +16,4 @@ type EnumConstantsPluginOptions = {
16
16
  declare function enumConstantsPlugin(options: EnumConstantsPluginOptions): Plugin<unknown, EnumConstantsPluginOptions>;
17
17
  //#endregion
18
18
  export { enumConstantsPlugin as n, EnumConstantsGeneratorID as t };
19
- //# sourceMappingURL=index-reFAYSX7.d.mts.map
19
+ //# sourceMappingURL=index-ZZYEd_0R.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interceptor-4UC-KTno.mjs","names":[],"sources":["../src/cli/telemetry/interceptor.ts"],"sourcesContent":["import { trace, SpanStatusCode } from \"@opentelemetry/api\";\nimport type { Interceptor } from \"@connectrpc/connect\";\n\n/**\n * Create a Connect-RPC interceptor that records OTLP spans for each RPC call.\n * When no TracerProvider is registered, the OTel API automatically provides\n * noop spans with zero overhead.\n * @returns Tracing interceptor\n */\nexport function createTracingInterceptor(): Interceptor {\n return (next) => async (req) => {\n const tracer = trace.getTracer(\"tailor-sdk\");\n\n return tracer.startActiveSpan(`rpc.${req.method.name}`, async (span) => {\n span.setAttribute(\"rpc.method\", req.method.name);\n span.setAttribute(\"rpc.service\", \"OperatorService\");\n span.setAttribute(\"rpc.system\", \"connect-rpc\");\n\n try {\n const response = await next(req);\n span.setStatus({ code: SpanStatusCode.OK });\n return response;\n } catch (error) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n if (error instanceof Error) {\n span.recordException(error);\n }\n throw error;\n } finally {\n span.end();\n }\n });\n };\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,2BAAwC;AACtD,SAAQ,SAAS,OAAO,QAAQ;AAG9B,SAFe,MAAM,UAAU,aAAa,CAE9B,gBAAgB,OAAO,IAAI,OAAO,QAAQ,OAAO,SAAS;AACtE,QAAK,aAAa,cAAc,IAAI,OAAO,KAAK;AAChD,QAAK,aAAa,eAAe,kBAAkB;AACnD,QAAK,aAAa,cAAc,cAAc;AAE9C,OAAI;IACF,MAAM,WAAW,MAAM,KAAK,IAAI;AAChC,SAAK,UAAU,EAAE,MAAM,eAAe,IAAI,CAAC;AAC3C,WAAO;YACA,OAAO;AACd,SAAK,UAAU,EAAE,MAAM,eAAe,OAAO,CAAC;AAC9C,QAAI,iBAAiB,MACnB,MAAK,gBAAgB,MAAM;AAE7B,UAAM;aACE;AACR,SAAK,KAAK;;IAEZ"}
1
+ {"version":3,"file":"interceptor-4UC-KTno.mjs","names":[],"sources":["../src/cli/telemetry/interceptor.ts"],"sourcesContent":["import { trace, SpanStatusCode } from \"@opentelemetry/api\";\nimport type { Interceptor } from \"@connectrpc/connect\";\n\n/**\n * Create a Connect-RPC interceptor that records OTLP spans for each RPC call.\n * When no TracerProvider is registered, the OTel API automatically provides\n * noop spans with zero overhead.\n * @returns Tracing interceptor\n */\nexport function createTracingInterceptor(): Interceptor {\n return (next) => async (req) => {\n const tracer = trace.getTracer(\"tailor-sdk\");\n\n return tracer.startActiveSpan(`rpc.${req.method.name}`, async (span) => {\n span.setAttribute(\"rpc.method\", req.method.name);\n span.setAttribute(\"rpc.service\", \"OperatorService\");\n span.setAttribute(\"rpc.system\", \"connect-rpc\");\n\n try {\n const response = await next(req);\n span.setStatus({ code: SpanStatusCode.OK });\n return response;\n } catch (error) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n if (error instanceof Error) {\n span.recordException(error);\n }\n throw error;\n } finally {\n span.end();\n }\n });\n };\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,2BAAwC;AACtD,SAAQ,SAAS,OAAO,QAAQ;AAG9B,SAFe,MAAM,UAAU,aAElB,CAAC,gBAAgB,OAAO,IAAI,OAAO,QAAQ,OAAO,SAAS;AACtE,QAAK,aAAa,cAAc,IAAI,OAAO,KAAK;AAChD,QAAK,aAAa,eAAe,kBAAkB;AACnD,QAAK,aAAa,cAAc,cAAc;AAE9C,OAAI;IACF,MAAM,WAAW,MAAM,KAAK,IAAI;AAChC,SAAK,UAAU,EAAE,MAAM,eAAe,IAAI,CAAC;AAC3C,WAAO;YACA,OAAO;AACd,SAAK,UAAU,EAAE,MAAM,eAAe,OAAO,CAAC;AAC9C,QAAI,iBAAiB,MACnB,MAAK,gBAAgB,MAAM;AAE7B,UAAM;aACE;AACR,SAAK,KAAK;;IAEZ"}
@@ -12,10 +12,14 @@ const WORKFLOW_TEST_ENV_KEY = "TAILOR_TEST_WORKFLOW_ENV";
12
12
  *
13
13
  * All jobs must be named exports from the workflow file.
14
14
  * Job names must be unique across the entire project.
15
- * @param config - Job configuration with name and body function
16
- * @param config.name - Unique job name across the project
17
- * @param config.body - Async function that processes the job input
18
- * @returns A WorkflowJob that can be triggered from other jobs
15
+ *
16
+ * Input and output must be JsonValue-compatible (primitives, plain objects, arrays).
17
+ * Functions and objects with a `toJSON` method are rejected at the type level;
18
+ * class instances exposing methods are rejected via the property walk.
19
+ * @param config - Job configuration with name and body function.
20
+ * @param config.name - Unique job name across the project.
21
+ * @param config.body - Async function that processes the job input.
22
+ * @returns A WorkflowJob that can be triggered from other jobs.
19
23
  * @example
20
24
  * // Simple job with async body:
21
25
  * export const fetchData = createWorkflowJob({
@@ -38,17 +42,19 @@ const WORKFLOW_TEST_ENV_KEY = "TAILOR_TEST_WORKFLOW_ENV";
38
42
  * });
39
43
  */
40
44
  const createWorkflowJob = (config) => {
45
+ const body = config.body;
41
46
  return brandValue({
42
47
  name: config.name,
43
48
  trigger: async (args) => {
44
- const env = JSON.parse(process.env["TAILOR_TEST_WORKFLOW_ENV"] || "{}");
45
- const result = await config.body(args, { env });
46
- return result ? JSON.parse(JSON.stringify(result)) : result;
49
+ return await body(args, {
50
+ env: JSON.parse(process.env["TAILOR_TEST_WORKFLOW_ENV"] || "{}"),
51
+ invoker: null
52
+ });
47
53
  },
48
- body: config.body
54
+ body
49
55
  }, "workflow-job");
50
56
  };
51
57
 
52
58
  //#endregion
53
59
  export { createWorkflowJob as n, WORKFLOW_TEST_ENV_KEY as t };
54
- //# sourceMappingURL=job-p6zf8Qpg.mjs.map
60
+ //# sourceMappingURL=job-BOvKyNdT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"job-BOvKyNdT.mjs","names":[],"sources":["../src/configure/services/workflow/job.ts"],"sourcesContent":["import { brandValue } from \"@/utils/brand\";\nimport type { TailorEnv } from \"@/types/env\";\nimport type { JsonCompatible } from \"@/types/helpers\";\nimport type { TailorInvoker } from \"@/types/user\";\n\n/**\n * Context object passed as the second argument to workflow job body functions.\n */\nexport type WorkflowJobContext = {\n env: TailorEnv;\n invoker?: TailorInvoker;\n};\n\n/**\n * The body function type for a workflow job.\n * Resolves to the callable signature when `I` / `O` are JsonValue-compatible,\n * or to a template-literal error string that surfaces at the `body:` property.\n */\ntype JobBody<I, O> = [null] extends [I]\n ? \"ERROR: Input cannot be null at the top level\"\n : [I] extends [undefined]\n ? [O] extends [JsonCompatible<O> | undefined | void]\n ? (input: I, context: WorkflowJobContext) => O | Promise<O>\n : \"ERROR: Output must be JsonValue-compatible (plain objects/arrays; no class instances or functions)\"\n : [undefined] extends [I]\n ? \"ERROR: Input cannot include undefined at the top level\"\n : [I] extends [JsonCompatible<I>]\n ? [O] extends [JsonCompatible<O> | undefined | void]\n ? (input: I, context: WorkflowJobContext) => O | Promise<O>\n : \"ERROR: Output must be JsonValue-compatible (plain objects/arrays; no class instances or functions)\"\n : \"ERROR: Input must be JsonValue-compatible (plain objects/arrays; no class instances or functions)\";\n\n/**\n * WorkflowJob represents a job that can be triggered in a workflow.\n *\n * Type constraints:\n * - Input: Must be JsonValue-compatible (plain objects/arrays; no class instances or functions) or undefined.\n * - Output: Must be JsonValue-compatible (plain objects/arrays; no class instances or functions), undefined, or void.\n * - Trigger returns `Awaited<Output>` as-is (no Jsonify transformation).\n */\nexport interface WorkflowJob<Name extends string = string, Input = undefined, Output = undefined> {\n name: Name;\n /**\n * Trigger this job with the given input.\n * At runtime, this is a placeholder that calls the body function.\n * During bundling, calls to .trigger() are transformed to\n * tailor.workflow.triggerJobFunction(\"<job-name>\", args).\n *\n * Inside a workflow job body, .trigger() calls are transformed by the bundler\n * into synchronous `triggerJobFunction` calls. You may use `await` for\n * readability — the bundler strips it automatically at build time.\n * @example\n * // Both styles work — await is stripped by the bundler:\n * body: async (input) => {\n * const a = await jobA.trigger({ id: input.id });\n * const b = await jobB.trigger({ id: input.id });\n * return { a, b };\n * }\n */\n trigger: [Input] extends [undefined]\n ? () => Promise<Awaited<Output>>\n : (input: Input) => Promise<Awaited<Output>>;\n body: (input: Input, context: WorkflowJobContext) => Output | Promise<Output>;\n}\n\n/**\n * Environment variable key for workflow testing.\n * Contains JSON-serialized TailorEnv object.\n */\nexport const WORKFLOW_TEST_ENV_KEY = \"TAILOR_TEST_WORKFLOW_ENV\";\n\ninterface CreateWorkflowJobConfig<Name extends string, I, O> {\n readonly name: Name;\n readonly body: JobBody<I, O>;\n}\n\n/**\n * Create a workflow job definition.\n *\n * All jobs must be named exports from the workflow file.\n * Job names must be unique across the entire project.\n *\n * Input and output must be JsonValue-compatible (primitives, plain objects, arrays).\n * Functions and objects with a `toJSON` method are rejected at the type level;\n * class instances exposing methods are rejected via the property walk.\n * @param config - Job configuration with name and body function.\n * @param config.name - Unique job name across the project.\n * @param config.body - Async function that processes the job input.\n * @returns A WorkflowJob that can be triggered from other jobs.\n * @example\n * // Simple job with async body:\n * export const fetchData = createWorkflowJob({\n * name: \"fetch-data\",\n * body: async (input: { id: string }) => {\n * const db = getDB(\"tailordb\");\n * return await db.selectFrom(\"Table\").selectAll().where(\"id\", \"=\", input.id).executeTakeFirst();\n * },\n * });\n * @example\n * // Orchestrator job that fans out to other jobs.\n * // await is optional — the bundler strips it at build time.\n * export const orchestrate = createWorkflowJob({\n * name: \"orchestrate\",\n * body: async (input: { orderId: string }) => {\n * const inventory = await checkInventory.trigger({ orderId: input.orderId });\n * const payment = await processPayment.trigger({ orderId: input.orderId });\n * return { inventory, payment };\n * },\n * });\n */\nexport const createWorkflowJob = <const Name extends string, I = undefined, O = undefined>(\n config: CreateWorkflowJobConfig<Name, I, O>,\n): WorkflowJob<Name, I, Awaited<O>> => {\n const body = config.body as (input: I, context: WorkflowJobContext) => O | Promise<O>;\n return brandValue(\n {\n name: config.name,\n trigger: async (args?: unknown) => {\n const env: TailorEnv = JSON.parse(process.env[WORKFLOW_TEST_ENV_KEY] || \"{}\");\n return await body(args as I, { env, invoker: null });\n },\n body,\n } as WorkflowJob<Name, I, Awaited<O>>,\n \"workflow-job\",\n );\n};\n"],"mappings":";;;;;;;;AAqEA,MAAa,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCrC,MAAa,qBACX,WACqC;CACrC,MAAM,OAAO,OAAO;AACpB,QAAO,WACL;EACE,MAAM,OAAO;EACb,SAAS,OAAO,SAAmB;AAEjC,UAAO,MAAM,KAAK,MAAW;IAAE,KADR,KAAK,MAAM,QAAQ,mCAA8B,KACtC;IAAE,SAAS;IAAM,CAAC;;EAEtD;EACD,EACD,eACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["Kysely","TailordbDialect"],"sources":["../../src/kysely/index.ts"],"sourcesContent":["/**\n * Kysely integration module for generated TailorDB code.\n *\n * Re-exports kysely and function-kysely-tailordb types through a single import path\n * to avoid phantom dependency issues with pnpm, and provides namespace-aware\n * utility types and factory functions used by the code generator.\n */\n\nimport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\nimport {\n type ColumnType,\n Kysely,\n type Insertable,\n type KyselyConfig,\n type Selectable,\n sql,\n type Transaction as KyselyTransaction,\n type Updateable,\n} from \"kysely\";\n\nexport {\n type ColumnType,\n Kysely,\n type KyselyConfig,\n type Transaction,\n type Insertable,\n type Selectable,\n sql,\n type Updateable,\n} from \"kysely\";\n\nexport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\n\nexport type Timestamp = ColumnType<Date, Date | string, Date | string>;\ntype ResolveSelect<T> = T extends ColumnType<infer S, unknown, unknown> ? S : T;\ntype ResolveInsert<T> = T extends ColumnType<unknown, infer I, unknown> ? I : T;\ntype ResolveUpdate<T> = T extends ColumnType<unknown, unknown, infer U> ? U : T;\nexport type ObjectColumnType<T> = ColumnType<\n { [K in keyof T]-?: Exclude<ResolveSelect<T[K]>, undefined> },\n { [K in keyof T]: ResolveInsert<T[K]> },\n { [K in keyof T]: ResolveUpdate<T[K]> }\n>;\nexport type ArrayColumnType<T> = ColumnType<\n ResolveSelect<T>[],\n ResolveInsert<T>[],\n ResolveUpdate<T>[]\n>;\nexport type Generated<T> =\n T extends ColumnType<infer S, infer I, infer U>\n ? ColumnType<S, I | undefined, U>\n : ColumnType<T, T | undefined, T>;\nexport type Serial<T = string | number> = ColumnType<T, never, never>;\n\nexport type TailordbKysely<DB> = Kysely<DB>;\nexport type NamespaceDB<NS, N extends keyof NS = keyof NS> = TailordbKysely<NS[N]>;\n\n/**\n * Create a namespace-aware getDB function for generated code.\n * @returns A getDB function that creates Kysely instances for specific namespaces\n */\nexport function createGetDB<NS>() {\n return function getDB<const N extends keyof NS & string>(\n namespace: N,\n config?: Omit<KyselyConfig, \"dialect\">,\n ): TailordbKysely<NS[N]> {\n const client = new tailordb.Client({ namespace });\n return new Kysely<NS[N]>({\n dialect: new TailordbDialect(client),\n ...config,\n });\n };\n}\n\nexport type NamespaceTransaction<NS, K extends keyof NS | TailordbKysely<NS[keyof NS]> = keyof NS> =\n K extends TailordbKysely<infer DB>\n ? KyselyTransaction<DB>\n : K extends keyof NS\n ? KyselyTransaction<NS[K]>\n : never;\n\nexport type NamespaceTableName<NS> = {\n [N in keyof NS]: keyof NS[N];\n}[keyof NS];\n\nexport type NamespaceTable<NS, T extends NamespaceTableName<NS>> = {\n [N in keyof NS]: T extends keyof NS[N] ? NS[N][T] : never;\n}[keyof NS];\n\nexport type NamespaceInsertable<NS, T extends NamespaceTableName<NS>> = Insertable<\n NamespaceTable<NS, T>\n>;\nexport type NamespaceSelectable<NS, T extends NamespaceTableName<NS>> = Selectable<\n NamespaceTable<NS, T>\n>;\nexport type NamespaceUpdateable<NS, T extends NamespaceTableName<NS>> = Updateable<\n NamespaceTable<NS, T>\n>;\n"],"mappings":";;;;;;;;;;;;;;;;AA4DA,SAAgB,cAAkB;AAChC,QAAO,SAAS,MACd,WACA,QACuB;AAEvB,SAAO,IAAIA,SAAc;GACvB,SAAS,IAAIC,kBAFA,IAAI,SAAS,OAAO,EAAE,WAAW,CAAC,CAEX;GACpC,GAAG;GACJ,CAAC"}
1
+ {"version":3,"file":"index.mjs","names":["Kysely","TailordbDialect"],"sources":["../../src/kysely/index.ts"],"sourcesContent":["/**\n * Kysely integration module for generated TailorDB code.\n *\n * Re-exports kysely and function-kysely-tailordb types through a single import path\n * to avoid phantom dependency issues with pnpm, and provides namespace-aware\n * utility types and factory functions used by the code generator.\n */\n\nimport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\nimport {\n type ColumnType,\n Kysely,\n type Insertable,\n type KyselyConfig,\n type Selectable,\n sql,\n type Transaction as KyselyTransaction,\n type Updateable,\n} from \"kysely\";\n\nexport {\n type ColumnType,\n Kysely,\n type KyselyConfig,\n type Transaction,\n type Insertable,\n type Selectable,\n sql,\n type Updateable,\n} from \"kysely\";\n\nexport { TailordbDialect } from \"@tailor-platform/function-kysely-tailordb\";\n\nexport type Timestamp = ColumnType<Date, Date | string, Date | string>;\ntype ResolveSelect<T> = T extends ColumnType<infer S, unknown, unknown> ? S : T;\ntype ResolveInsert<T> = T extends ColumnType<unknown, infer I, unknown> ? I : T;\ntype ResolveUpdate<T> = T extends ColumnType<unknown, unknown, infer U> ? U : T;\nexport type ObjectColumnType<T> = ColumnType<\n { [K in keyof T]-?: Exclude<ResolveSelect<T[K]>, undefined> },\n { [K in keyof T]: ResolveInsert<T[K]> },\n { [K in keyof T]: ResolveUpdate<T[K]> }\n>;\nexport type ArrayColumnType<T> = ColumnType<\n ResolveSelect<T>[],\n ResolveInsert<T>[],\n ResolveUpdate<T>[]\n>;\nexport type Generated<T> =\n T extends ColumnType<infer S, infer I, infer U>\n ? ColumnType<S, I | undefined, U>\n : ColumnType<T, T | undefined, T>;\nexport type Serial<T = string | number> = ColumnType<T, never, never>;\n\nexport type TailordbKysely<DB> = Kysely<DB>;\nexport type NamespaceDB<NS, N extends keyof NS = keyof NS> = TailordbKysely<NS[N]>;\n\n/**\n * Create a namespace-aware getDB function for generated code.\n * @returns A getDB function that creates Kysely instances for specific namespaces\n */\nexport function createGetDB<NS>() {\n return function getDB<const N extends keyof NS & string>(\n namespace: N,\n config?: Omit<KyselyConfig, \"dialect\">,\n ): TailordbKysely<NS[N]> {\n const client = new tailordb.Client({ namespace });\n return new Kysely<NS[N]>({\n dialect: new TailordbDialect(client),\n ...config,\n });\n };\n}\n\nexport type NamespaceTransaction<NS, K extends keyof NS | TailordbKysely<NS[keyof NS]> = keyof NS> =\n K extends TailordbKysely<infer DB>\n ? KyselyTransaction<DB>\n : K extends keyof NS\n ? KyselyTransaction<NS[K]>\n : never;\n\nexport type NamespaceTableName<NS> = {\n [N in keyof NS]: keyof NS[N];\n}[keyof NS];\n\nexport type NamespaceTable<NS, T extends NamespaceTableName<NS>> = {\n [N in keyof NS]: T extends keyof NS[N] ? NS[N][T] : never;\n}[keyof NS];\n\nexport type NamespaceInsertable<NS, T extends NamespaceTableName<NS>> = Insertable<\n NamespaceTable<NS, T>\n>;\nexport type NamespaceSelectable<NS, T extends NamespaceTableName<NS>> = Selectable<\n NamespaceTable<NS, T>\n>;\nexport type NamespaceUpdateable<NS, T extends NamespaceTableName<NS>> = Updateable<\n NamespaceTable<NS, T>\n>;\n"],"mappings":";;;;;;;;;;;;;;;;AA4DA,SAAgB,cAAkB;AAChC,QAAO,SAAS,MACd,WACA,QACuB;AAEvB,SAAO,IAAIA,SAAc;GACvB,SAAS,IAAIC,kBAAgB,IAFZ,SAAS,OAAO,EAAE,WAAW,CAEX,CAAC;GACpC,GAAG;GACJ,CAAC"}
@@ -272,4 +272,4 @@ function kyselyTypePlugin(options) {
272
272
 
273
273
  //#endregion
274
274
  export { kyselyTypePlugin as n, KyselyGeneratorID as t };
275
- //# sourceMappingURL=kysely-type-CwtvQuxh.mjs.map
275
+ //# sourceMappingURL=kysely-type-BQsYEZoY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely-type-BQsYEZoY.mjs","names":["multiline"],"sources":["../src/plugin/builtin/kysely-type/type-processor.ts","../src/plugin/builtin/kysely-type/index.ts"],"sourcesContent":["import multiline from \"multiline-ts\";\nimport { type KyselyNamespaceMetadata, type KyselyTypeMetadata } from \"./types\";\nimport type { OperatorFieldConfig, TailorDBType } from \"@/types/tailordb\";\n\ntype UsedUtilityTypes = { Timestamp: boolean; Serial: boolean };\n\ntype FieldTypeResult = {\n type: string;\n usedUtilityTypes: UsedUtilityTypes;\n};\n\n/**\n * Get the enum type definition.\n * @param fieldConfig - The field configuration\n * @returns The enum type as a string union\n */\nfunction getEnumType(fieldConfig: OperatorFieldConfig): string {\n const allowedValues = fieldConfig.allowedValues;\n\n if (allowedValues && Array.isArray(allowedValues)) {\n return allowedValues\n .map((v: string | { value: string }) => {\n const value = typeof v === \"string\" ? v : v.value;\n return `\"${value}\"`;\n })\n .join(\" | \");\n }\n return \"string\";\n}\n\n/**\n * Get the nested object type definition.\n * @param fieldConfig - The field configuration\n * @returns The nested type with used utility types\n */\nfunction getNestedType(fieldConfig: OperatorFieldConfig): FieldTypeResult {\n const fields = fieldConfig.fields;\n if (!fields || typeof fields !== \"object\") {\n return {\n type: \"string\",\n usedUtilityTypes: { Timestamp: false, Serial: false },\n };\n }\n\n const fieldResults = Object.entries(fields).map(([fieldName, config]) => {\n const result = generateFieldType(config);\n const optional = config.required !== true ? \"?\" : \"\";\n return {\n fieldType: `${fieldName}${optional}: ${result.type}`,\n usedUtilityTypes: result.usedUtilityTypes,\n };\n });\n\n const aggregatedUtilityTypes = fieldResults.reduce(\n (acc, result) => ({\n Timestamp: acc.Timestamp || result.usedUtilityTypes.Timestamp,\n Serial: acc.Serial || result.usedUtilityTypes.Serial,\n }),\n { Timestamp: false, Serial: false },\n );\n\n const fieldTypes = fieldResults.map((r) => r.fieldType);\n const obj = `{\\n ${fieldTypes.join(\";\\n \")}${fieldTypes.length > 0 ? \";\" : \"\"}\\n}`;\n\n const hasOptionalFields = Object.values(fields).some((config) => config.required !== true);\n if (aggregatedUtilityTypes.Timestamp || hasOptionalFields) {\n return { type: `ObjectColumnType<${obj}>`, usedUtilityTypes: aggregatedUtilityTypes };\n }\n return { type: obj, usedUtilityTypes: aggregatedUtilityTypes };\n}\n\n/**\n * Get the base Kysely type for a field (without array/null modifiers).\n * @param fieldConfig - The field configuration\n * @returns The base type with used utility types\n */\nfunction getBaseType(fieldConfig: OperatorFieldConfig): FieldTypeResult {\n const fieldType = fieldConfig.type;\n const usedUtilityTypes = { Timestamp: false, Serial: false };\n\n let type: string;\n switch (fieldType) {\n case \"uuid\":\n case \"string\":\n case \"decimal\":\n type = \"string\";\n break;\n case \"integer\":\n case \"float\":\n type = \"number\";\n break;\n case \"date\":\n case \"datetime\":\n usedUtilityTypes.Timestamp = true;\n type = \"Timestamp\";\n break;\n case \"bool\":\n case \"boolean\":\n type = \"boolean\";\n break;\n case \"enum\":\n type = getEnumType(fieldConfig);\n break;\n case \"nested\": {\n const nestedResult = getNestedType(fieldConfig);\n return nestedResult;\n }\n default:\n type = \"string\";\n break;\n }\n\n return { type, usedUtilityTypes };\n}\n\n/**\n * Generate the complete field type including array and null modifiers.\n * @param fieldConfig - The field configuration\n * @returns The complete field type with used utility types\n */\nfunction generateFieldType(fieldConfig: OperatorFieldConfig): FieldTypeResult {\n const baseTypeResult = getBaseType(fieldConfig);\n const usedUtilityTypes = { ...baseTypeResult.usedUtilityTypes };\n\n const isArray = fieldConfig.array === true;\n const isNullable = fieldConfig.required !== true;\n\n // Types that use ColumnType internally (Timestamp, ObjectColumnType) cannot be\n // directly wrapped with [] for arrays, because Kysely only resolves ColumnType at\n // the top-level table property. Use ArrayColumnType/ObjectArrayColumnType to keep\n // the ColumnType at the top level with arrays inside.\n const columnTypeBaseTypes = new Set([\"Timestamp\"]);\n const isColumnTypeBase = columnTypeBaseTypes.has(baseTypeResult.type);\n\n let finalType = baseTypeResult.type;\n if (isArray) {\n if (isColumnTypeBase || finalType.startsWith(\"ObjectColumnType<\")) {\n finalType = `ArrayColumnType<${baseTypeResult.type}>`;\n } else {\n const needsParens = fieldConfig.type === \"enum\";\n finalType = needsParens ? `(${baseTypeResult.type})[]` : `${baseTypeResult.type}[]`;\n }\n }\n if (isNullable) {\n finalType = `${finalType} | null`;\n }\n\n if (fieldConfig.serial) {\n usedUtilityTypes.Serial = true;\n finalType = `Serial<${finalType}>`;\n }\n if (fieldConfig.hooks?.create) {\n finalType = `Generated<${finalType}>`;\n }\n\n return { type: finalType, usedUtilityTypes };\n}\n\n/**\n * Generate the table interface.\n * @param type - The parsed TailorDB type\n * @returns The type definition and used utility types\n */\nfunction generateTableInterface(type: TailorDBType): {\n typeDef: string;\n usedUtilityTypes: UsedUtilityTypes;\n} {\n const fieldEntries = Object.entries(type.fields).filter(([fieldName]) => fieldName !== \"id\");\n\n const fieldResults = fieldEntries.map(([fieldName, parsedField]) => ({\n fieldName,\n ...generateFieldType(parsedField.config),\n }));\n\n const fields = [\n \"id: Generated<string>;\",\n ...fieldResults.map((result) => `${result.fieldName}: ${result.type};`),\n ];\n\n const aggregatedUtilityTypes = fieldResults.reduce(\n (acc, result) => ({\n Timestamp: acc.Timestamp || result.usedUtilityTypes.Timestamp,\n\n Serial: acc.Serial || result.usedUtilityTypes.Serial,\n }),\n { Timestamp: false, Serial: false },\n );\n\n const typeDef = multiline /* ts */ `\n ${type.name}: {\n ${fields.join(\"\\n\")}\n }\n `;\n\n return { typeDef, usedUtilityTypes: aggregatedUtilityTypes };\n}\n\n/**\n * Convert a TailorDBType into KyselyTypeMetadata.\n * @param type - Parsed TailorDB type\n * @returns Generated Kysely type metadata\n */\nexport async function processKyselyType(type: TailorDBType): Promise<KyselyTypeMetadata> {\n const result = generateTableInterface(type);\n\n return {\n name: type.name,\n typeDef: result.typeDef,\n usedUtilityTypes: result.usedUtilityTypes,\n };\n}\n\n/**\n * Generate unified types file from multiple namespaces.\n * @param namespaceData - Namespace metadata\n * @returns Generated types file contents\n */\nexport function generateUnifiedKyselyTypes(namespaceData: KyselyNamespaceMetadata[]): string {\n if (namespaceData.length === 0) {\n return \"\";\n }\n\n // Aggregate used utility types from all namespaces\n const globalUsedUtilityTypes = namespaceData.reduce(\n (acc, ns) => ({\n Timestamp: acc.Timestamp || ns.usedUtilityTypes.Timestamp,\n Serial: acc.Serial || ns.usedUtilityTypes.Serial,\n }),\n { Timestamp: false, Serial: false },\n );\n\n const utilityTypeImports: string[] = [\"type Generated\"];\n if (globalUsedUtilityTypes.Timestamp) {\n utilityTypeImports.push(\"type Timestamp\");\n }\n const hasObjectColumnType = namespaceData.some((ns) =>\n ns.types.some((t) => t.typeDef.includes(\"ObjectColumnType<\")),\n );\n if (hasObjectColumnType) {\n utilityTypeImports.push(\"type ObjectColumnType\");\n }\n const hasArrayColumnType = namespaceData.some((ns) =>\n ns.types.some((t) => t.typeDef.includes(\"ArrayColumnType<\")),\n );\n if (hasArrayColumnType) {\n utilityTypeImports.push(\"type ArrayColumnType\");\n }\n if (globalUsedUtilityTypes.Serial) {\n utilityTypeImports.push(\"type Serial\");\n }\n\n const importsSection = multiline /* ts */ `\n import {\n createGetDB,\n ${utilityTypeImports.join(\",\\n\")},\n type NamespaceDB,\n type NamespaceInsertable,\n type NamespaceSelectable,\n type NamespaceTable,\n type NamespaceTableName,\n type NamespaceTransaction,\n type NamespaceUpdateable,\n } from \"@tailor-platform/sdk/kysely\";\n `;\n\n // Generate Namespace interface with multiple namespaces\n const namespaceInterfaces = namespaceData\n .map(({ namespace, types }) => {\n const typeDefsWithIndent = types\n .map((type) => {\n return type.typeDef\n .split(\"\\n\")\n .map((line) => (line.trim() ? ` ${line}` : \"\"))\n .join(\"\\n\");\n })\n .join(\"\\n\\n\");\n\n return ` \"${namespace}\": {\\n${typeDefsWithIndent}\\n }`;\n })\n .join(\",\\n\");\n\n const namespaceInterface = `export interface Namespace {\\n${namespaceInterfaces}\\n}`;\n\n const getDBFunction = multiline /* ts */ `\n export const getDB = createGetDB<Namespace>();\n\n export type DB<N extends keyof Namespace = keyof Namespace> = NamespaceDB<Namespace, N>;\n `;\n\n const utilityTypeExports = multiline /* ts */ `\n export type Transaction<K extends keyof Namespace | DB = keyof Namespace> =\n NamespaceTransaction<Namespace, K>;\n\n type TableName = NamespaceTableName<Namespace>;\n export type Table<T extends TableName> = NamespaceTable<Namespace, T>;\n\n export type Insertable<T extends TableName> = NamespaceInsertable<Namespace, T>;\n export type Selectable<T extends TableName> = NamespaceSelectable<Namespace, T>;\n export type Updateable<T extends TableName> = NamespaceUpdateable<Namespace, T>;\n `;\n\n return (\n [importsSection, namespaceInterface, getDBFunction, utilityTypeExports].join(\"\\n\\n\") + \"\\n\"\n );\n}\n","import { processKyselyType, generateUnifiedKyselyTypes } from \"./type-processor\";\nimport type { KyselyTypeMetadata, KyselyNamespaceMetadata } from \"./types\";\nimport type { Plugin } from \"@/types/plugin\";\nimport type { GeneratorResult, TailorDBReadyContext } from \"@/types/plugin-generation\";\n\n/** Unique identifier for the Kysely type generator plugin. */\nexport const KyselyGeneratorID = \"@tailor-platform/kysely-type\";\n\ntype KyselyTypePluginOptions = {\n distPath: string;\n};\n\n/**\n * Plugin that generates Kysely type definitions for TailorDB types.\n * @param options - Plugin options\n * @param options.distPath - Output file path for generated types\n * @returns Plugin instance with onTailorDBReady hook\n */\nexport function kyselyTypePlugin(\n options: KyselyTypePluginOptions,\n): Plugin<unknown, KyselyTypePluginOptions> {\n return {\n id: KyselyGeneratorID,\n description: \"Generates Kysely type definitions for TailorDB types\",\n pluginConfig: options,\n\n async onTailorDBReady(\n ctx: TailorDBReadyContext<KyselyTypePluginOptions>,\n ): Promise<GeneratorResult> {\n const allNamespaceData: KyselyNamespaceMetadata[] = [];\n\n for (const ns of ctx.tailordb) {\n const typeMetadataList: KyselyTypeMetadata[] = [];\n\n for (const type of Object.values(ns.types)) {\n const metadata = await processKyselyType(type);\n typeMetadataList.push(metadata);\n }\n\n if (typeMetadataList.length === 0) continue;\n\n const usedUtilityTypes = typeMetadataList.reduce(\n (acc, type) => ({\n Timestamp: acc.Timestamp || type.usedUtilityTypes.Timestamp,\n Serial: acc.Serial || type.usedUtilityTypes.Serial,\n }),\n { Timestamp: false, Serial: false },\n );\n\n allNamespaceData.push({\n namespace: ns.namespace,\n types: typeMetadataList,\n usedUtilityTypes,\n });\n }\n\n const files: GeneratorResult[\"files\"] = [];\n if (allNamespaceData.length > 0) {\n const content = generateUnifiedKyselyTypes(allNamespaceData);\n files.push({\n path: ctx.pluginConfig.distPath,\n content,\n });\n }\n\n return { files };\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger-DTNAMYGy.mjs","names":[],"sources":["../src/cli/shared/parse-boolean.ts","../src/cli/shared/logger.ts"],"sourcesContent":["const TRUTHY_VALUES = new Set([\"true\", \"t\", \"yes\", \"y\", \"on\", \"1\"]);\nconst FALSY_VALUES = new Set([\"false\", \"f\", \"no\", \"n\", \"off\", \"0\"]);\n\n/**\n * Parse a string value as a boolean.\n *\n * Recognized values (case-insensitive, trimmed) follow Python's\n * `distutils.util.strtobool` convention:\n * - truthy: `true, t, yes, y, on, 1`\n * - falsy: `false, f, no, n, off, 0`\n *\n * Undefined, empty strings, and unrecognized values return `undefined` so\n * that callers can fall back to their own defaults.\n * @param value - The input string (e.g. an environment variable or CLI flag value)\n * @returns `true`, `false`, or `undefined` when the value is unset or unrecognized\n */\nexport function parseBoolean(value: string | undefined): boolean | undefined {\n if (value === undefined) return undefined;\n const normalized = value.trim().toLowerCase();\n if (normalized === \"\") return undefined;\n if (TRUTHY_VALUES.has(normalized)) return true;\n if (FALSY_VALUES.has(normalized)) return false;\n return undefined;\n}\n","import { formatWithOptions, type InspectOptions } from \"node:util\";\nimport chalk from \"chalk\";\nimport { formatDistanceToNowStrict } from \"date-fns\";\nimport { getBorderCharacters, table } from \"table\";\nimport { parseBoolean } from \"./parse-boolean\";\n\n/**\n * Error thrown when a prompt is attempted in a CI environment\n */\nexport class CIPromptError extends Error {\n constructor(message?: string) {\n super(\n message ??\n \"Interactive prompts are not available in CI environments. Use --yes flag to skip confirmation prompts.\",\n );\n this.name = \"CIPromptError\";\n }\n}\n\n/**\n * Semantic style functions for inline text styling\n */\nexport const styles = {\n // Status colors\n success: chalk.green,\n error: chalk.red,\n warning: chalk.yellow,\n info: chalk.cyan,\n\n // Action colors (for change sets)\n create: chalk.green,\n update: chalk.yellow,\n delete: chalk.red,\n unchanged: chalk.gray,\n\n // Emphasis\n bold: chalk.bold,\n dim: chalk.gray,\n highlight: chalk.cyanBright,\n successBright: chalk.greenBright,\n errorBright: chalk.redBright,\n\n // Resource types\n resourceType: chalk.bold,\n resourceName: chalk.cyan,\n\n // File paths\n path: chalk.cyan,\n\n // Values\n value: chalk.white,\n placeholder: chalk.gray.italic,\n};\n\n/**\n * Standardized symbols for CLI output\n */\nexport const symbols = {\n success: chalk.green(\"\\u2713\"),\n error: chalk.red(\"\\u2716\"),\n warning: chalk.yellow(\"\\u26a0\"),\n info: chalk.cyan(\"i\"),\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\n replace: chalk.magenta(\"\\u00b1\"),\n bullet: chalk.gray(\"\\u2022\"),\n arrow: chalk.gray(\"\\u2192\"),\n};\n\n/**\n * Log output modes\n */\nexport type LogMode = \"default\" | \"stream\" | \"plain\";\n\nexport interface LogOptions {\n /** Output mode (default: \"default\") */\n mode?: LogMode;\n /** Number of spaces to indent the entire line (default: 0) */\n indent?: number;\n}\n\n/** Field transformer function. null excludes the field from table output. */\nexport type FieldTransformer = ((value: unknown, item: object) => string) | null;\n\nexport interface OutOptions {\n /** Table display field transform/exclude settings. Only applied in table mode (not JSON). */\n display?: Record<string, FieldTransformer>;\n\n /** Show null values in table output (default: false) */\n showNull?: boolean;\n}\n\n// In JSON mode, all logs go to stderr to keep stdout clean for JSON data\nlet _jsonMode = false;\n\n// Type icons for log output\nconst TYPE_ICONS: Record<string, string> = {\n info: \"ℹ\",\n success: \"✔\",\n warn: \"⚠\",\n error: \"✖\",\n debug: \"⚙\",\n trace: \"→\",\n log: \"\",\n};\n\n// Color functions for icon and message text\nconst TYPE_COLORS: Record<string, (text: string) => string> = {\n info: chalk.cyan,\n success: chalk.green,\n warn: chalk.yellow,\n error: chalk.red,\n debug: chalk.gray,\n trace: chalk.gray,\n log: (text) => text,\n};\n\ninterface FormatLogLineOptions {\n mode: string;\n indent: number;\n type: string;\n message: string;\n timestamp?: string;\n}\n\n/**\n * Formats a log line with the appropriate prefix and indentation\n * @param opts - Formatting options\n * @returns Formatted log line\n */\nexport function formatLogLine(opts: FormatLogLineOptions): string {\n const { mode, indent, type, message, timestamp } = opts;\n const indentPrefix = indent > 0 ? \" \".repeat(indent) : \"\";\n const colorFn = TYPE_COLORS[type] || ((text: string) => text);\n\n // Plain mode: color only, no icon, no timestamp\n if (mode === \"plain\") {\n return `${indentPrefix}${colorFn(message)}\\n`;\n }\n\n // Default/Stream mode: with icon and color\n const icon = TYPE_ICONS[type] || \"\";\n const prefix = icon ? `${icon} ` : \"\";\n const coloredOutput = colorFn(`${prefix}${message}`);\n const timestampPrefix = timestamp ?? \"\";\n\n return `${indentPrefix}${timestampPrefix}${coloredOutput}\\n`;\n}\n\n/**\n * Writes a formatted log line to stderr.\n * @param type - Log type (info, success, warn, error, log)\n * @param message - Log message\n * @param opts - Log options (mode and indent)\n */\nfunction writeLog(type: string, message: string, opts?: LogOptions): void {\n const mode = opts?.mode ?? \"default\";\n const indent = opts?.indent ?? 0;\n const inspectOpts: InspectOptions = {\n breakLength: process.stdout.columns || 80,\n };\n const formattedMessage = formatWithOptions(inspectOpts, message);\n const timestamp = mode === \"stream\" ? `${new Date().toLocaleTimeString()} ` : \"\";\n const output = formatLogLine({ mode, indent, type, message: formattedMessage, timestamp });\n process.stderr.write(output);\n}\n\nexport const logger = {\n get jsonMode(): boolean {\n return _jsonMode;\n },\n set jsonMode(value: boolean) {\n _jsonMode = value;\n },\n\n info(message: string, opts?: LogOptions): void {\n writeLog(\"info\", message, opts);\n },\n\n success(message: string, opts?: LogOptions): void {\n writeLog(\"success\", message, opts);\n },\n\n warn(message: string, opts?: LogOptions): void {\n writeLog(\"warn\", message, opts);\n },\n\n error(message: string, opts?: LogOptions): void {\n writeLog(\"error\", message, opts);\n },\n\n log(message: string): void {\n writeLog(\"log\", message, { mode: \"plain\" });\n },\n\n newline(): void {\n process.stderr.write(\"\\n\");\n },\n\n debug(message: string): void {\n if (parseBoolean(process.env.DEBUG) === true) {\n writeLog(\"log\", styles.dim(message), { mode: \"plain\" });\n }\n },\n\n out(data: string | object | object[], options?: OutOptions): void {\n if (typeof data === \"string\") {\n process.stdout.write(data.endsWith(\"\\n\") ? data : data + \"\\n\");\n return;\n }\n\n if (this.jsonMode) {\n // eslint-disable-next-line no-restricted-syntax\n console.log(JSON.stringify(data));\n return;\n }\n\n const display = options?.display;\n\n // Helper to format a value for table display\n const formatValue = (value: unknown, pretty = false): string => {\n if (options?.showNull && value === null) return \"NULL\";\n if (value === null || value === undefined) return \"N/A\";\n if (value instanceof Date) {\n return formatDistanceToNowStrict(value, { addSuffix: true });\n }\n if (typeof value === \"object\") {\n return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);\n }\n return String(value);\n };\n\n // Helper to check if field should be excluded\n const isExcluded = (key: string): boolean => {\n return display !== undefined && key in display && display[key] === null;\n };\n\n // Helper to apply transformer or default formatting\n const transformValue = (key: string, value: unknown, item: object, pretty = false): string => {\n if (display && key in display) {\n const transformer = display[key];\n if (transformer) {\n return transformer(value, item);\n }\n }\n return formatValue(value, pretty);\n };\n\n if (!Array.isArray(data)) {\n const entries = Object.entries(data).filter(([key]) => !isExcluded(key));\n const formattedEntries = entries.map(([key, value]) => [\n key,\n transformValue(key, value, data, true),\n ]);\n const t = table(formattedEntries, {\n singleLine: false,\n border: getBorderCharacters(\"norc\"),\n });\n process.stdout.write(t);\n return;\n }\n\n if (data.length === 0) {\n return;\n }\n\n const allHeaders = Array.from(new Set(data.flatMap((item) => Object.keys(item))));\n const headers = allHeaders.filter((h) => !isExcluded(h));\n const rows = data.map((item) =>\n headers.map((header) =>\n transformValue(header, (item as Record<string, unknown>)[header], item),\n ),\n );\n\n const t = table([headers, ...rows], {\n border: getBorderCharacters(\"norc\"),\n drawHorizontalLine: (lineIndex, rowCount) => {\n return lineIndex === 0 || lineIndex === 1 || lineIndex === rowCount;\n },\n });\n process.stdout.write(t);\n },\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"logger-DTNAMYGy.mjs","names":[],"sources":["../src/cli/shared/parse-boolean.ts","../src/cli/shared/logger.ts"],"sourcesContent":["const TRUTHY_VALUES = new Set([\"true\", \"t\", \"yes\", \"y\", \"on\", \"1\"]);\nconst FALSY_VALUES = new Set([\"false\", \"f\", \"no\", \"n\", \"off\", \"0\"]);\n\n/**\n * Parse a string value as a boolean.\n *\n * Recognized values (case-insensitive, trimmed) follow Python's\n * `distutils.util.strtobool` convention:\n * - truthy: `true, t, yes, y, on, 1`\n * - falsy: `false, f, no, n, off, 0`\n *\n * Undefined, empty strings, and unrecognized values return `undefined` so\n * that callers can fall back to their own defaults.\n * @param value - The input string (e.g. an environment variable or CLI flag value)\n * @returns `true`, `false`, or `undefined` when the value is unset or unrecognized\n */\nexport function parseBoolean(value: string | undefined): boolean | undefined {\n if (value === undefined) return undefined;\n const normalized = value.trim().toLowerCase();\n if (normalized === \"\") return undefined;\n if (TRUTHY_VALUES.has(normalized)) return true;\n if (FALSY_VALUES.has(normalized)) return false;\n return undefined;\n}\n","import { formatWithOptions, type InspectOptions } from \"node:util\";\nimport chalk from \"chalk\";\nimport { formatDistanceToNowStrict } from \"date-fns\";\nimport { getBorderCharacters, table } from \"table\";\nimport { parseBoolean } from \"./parse-boolean\";\n\n/**\n * Error thrown when a prompt is attempted in a CI environment\n */\nexport class CIPromptError extends Error {\n constructor(message?: string) {\n super(\n message ??\n \"Interactive prompts are not available in CI environments. Use --yes flag to skip confirmation prompts.\",\n );\n this.name = \"CIPromptError\";\n }\n}\n\n/**\n * Semantic style functions for inline text styling\n */\nexport const styles = {\n // Status colors\n success: chalk.green,\n error: chalk.red,\n warning: chalk.yellow,\n info: chalk.cyan,\n\n // Action colors (for change sets)\n create: chalk.green,\n update: chalk.yellow,\n delete: chalk.red,\n unchanged: chalk.gray,\n\n // Emphasis\n bold: chalk.bold,\n dim: chalk.gray,\n highlight: chalk.cyanBright,\n successBright: chalk.greenBright,\n errorBright: chalk.redBright,\n\n // Resource types\n resourceType: chalk.bold,\n resourceName: chalk.cyan,\n\n // File paths\n path: chalk.cyan,\n\n // Values\n value: chalk.white,\n placeholder: chalk.gray.italic,\n};\n\n/**\n * Standardized symbols for CLI output\n */\nexport const symbols = {\n success: chalk.green(\"\\u2713\"),\n error: chalk.red(\"\\u2716\"),\n warning: chalk.yellow(\"\\u26a0\"),\n info: chalk.cyan(\"i\"),\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\n replace: chalk.magenta(\"\\u00b1\"),\n bullet: chalk.gray(\"\\u2022\"),\n arrow: chalk.gray(\"\\u2192\"),\n};\n\n/**\n * Log output modes\n */\nexport type LogMode = \"default\" | \"stream\" | \"plain\";\n\nexport interface LogOptions {\n /** Output mode (default: \"default\") */\n mode?: LogMode;\n /** Number of spaces to indent the entire line (default: 0) */\n indent?: number;\n}\n\n/** Field transformer function. null excludes the field from table output. */\nexport type FieldTransformer = ((value: unknown, item: object) => string) | null;\n\nexport interface OutOptions {\n /** Table display field transform/exclude settings. Only applied in table mode (not JSON). */\n display?: Record<string, FieldTransformer>;\n\n /** Show null values in table output (default: false) */\n showNull?: boolean;\n}\n\n// In JSON mode, all logs go to stderr to keep stdout clean for JSON data\nlet _jsonMode = false;\n\n// Type icons for log output\nconst TYPE_ICONS: Record<string, string> = {\n info: \"ℹ\",\n success: \"✔\",\n warn: \"⚠\",\n error: \"✖\",\n debug: \"⚙\",\n trace: \"→\",\n log: \"\",\n};\n\n// Color functions for icon and message text\nconst TYPE_COLORS: Record<string, (text: string) => string> = {\n info: chalk.cyan,\n success: chalk.green,\n warn: chalk.yellow,\n error: chalk.red,\n debug: chalk.gray,\n trace: chalk.gray,\n log: (text) => text,\n};\n\ninterface FormatLogLineOptions {\n mode: string;\n indent: number;\n type: string;\n message: string;\n timestamp?: string;\n}\n\n/**\n * Formats a log line with the appropriate prefix and indentation\n * @param opts - Formatting options\n * @returns Formatted log line\n */\nexport function formatLogLine(opts: FormatLogLineOptions): string {\n const { mode, indent, type, message, timestamp } = opts;\n const indentPrefix = indent > 0 ? \" \".repeat(indent) : \"\";\n const colorFn = TYPE_COLORS[type] || ((text: string) => text);\n\n // Plain mode: color only, no icon, no timestamp\n if (mode === \"plain\") {\n return `${indentPrefix}${colorFn(message)}\\n`;\n }\n\n // Default/Stream mode: with icon and color\n const icon = TYPE_ICONS[type] || \"\";\n const prefix = icon ? `${icon} ` : \"\";\n const coloredOutput = colorFn(`${prefix}${message}`);\n const timestampPrefix = timestamp ?? \"\";\n\n return `${indentPrefix}${timestampPrefix}${coloredOutput}\\n`;\n}\n\n/**\n * Writes a formatted log line to stderr.\n * @param type - Log type (info, success, warn, error, log)\n * @param message - Log message\n * @param opts - Log options (mode and indent)\n */\nfunction writeLog(type: string, message: string, opts?: LogOptions): void {\n const mode = opts?.mode ?? \"default\";\n const indent = opts?.indent ?? 0;\n const inspectOpts: InspectOptions = {\n breakLength: process.stdout.columns || 80,\n };\n const formattedMessage = formatWithOptions(inspectOpts, message);\n const timestamp = mode === \"stream\" ? `${new Date().toLocaleTimeString()} ` : \"\";\n const output = formatLogLine({ mode, indent, type, message: formattedMessage, timestamp });\n process.stderr.write(output);\n}\n\nexport const logger = {\n get jsonMode(): boolean {\n return _jsonMode;\n },\n set jsonMode(value: boolean) {\n _jsonMode = value;\n },\n\n info(message: string, opts?: LogOptions): void {\n writeLog(\"info\", message, opts);\n },\n\n success(message: string, opts?: LogOptions): void {\n writeLog(\"success\", message, opts);\n },\n\n warn(message: string, opts?: LogOptions): void {\n writeLog(\"warn\", message, opts);\n },\n\n error(message: string, opts?: LogOptions): void {\n writeLog(\"error\", message, opts);\n },\n\n log(message: string): void {\n writeLog(\"log\", message, { mode: \"plain\" });\n },\n\n newline(): void {\n process.stderr.write(\"\\n\");\n },\n\n debug(message: string): void {\n if (parseBoolean(process.env.DEBUG) === true) {\n writeLog(\"log\", styles.dim(message), { mode: \"plain\" });\n }\n },\n\n out(data: string | object | object[], options?: OutOptions): void {\n if (typeof data === \"string\") {\n process.stdout.write(data.endsWith(\"\\n\") ? data : data + \"\\n\");\n return;\n }\n\n if (this.jsonMode) {\n // eslint-disable-next-line no-restricted-syntax\n console.log(JSON.stringify(data));\n return;\n }\n\n const display = options?.display;\n\n // Helper to format a value for table display\n const formatValue = (value: unknown, pretty = false): string => {\n if (options?.showNull && value === null) return \"NULL\";\n if (value === null || value === undefined) return \"N/A\";\n if (value instanceof Date) {\n return formatDistanceToNowStrict(value, { addSuffix: true });\n }\n if (typeof value === \"object\") {\n return pretty ? JSON.stringify(value, null, 2) : JSON.stringify(value);\n }\n return String(value);\n };\n\n // Helper to check if field should be excluded\n const isExcluded = (key: string): boolean => {\n return display !== undefined && key in display && display[key] === null;\n };\n\n // Helper to apply transformer or default formatting\n const transformValue = (key: string, value: unknown, item: object, pretty = false): string => {\n if (display && key in display) {\n const transformer = display[key];\n if (transformer) {\n return transformer(value, item);\n }\n }\n return formatValue(value, pretty);\n };\n\n if (!Array.isArray(data)) {\n const entries = Object.entries(data).filter(([key]) => !isExcluded(key));\n const formattedEntries = entries.map(([key, value]) => [\n key,\n transformValue(key, value, data, true),\n ]);\n const t = table(formattedEntries, {\n singleLine: false,\n border: getBorderCharacters(\"norc\"),\n });\n process.stdout.write(t);\n return;\n }\n\n if (data.length === 0) {\n return;\n }\n\n const allHeaders = Array.from(new Set(data.flatMap((item) => Object.keys(item))));\n const headers = allHeaders.filter((h) => !isExcluded(h));\n const rows = data.map((item) =>\n headers.map((header) =>\n transformValue(header, (item as Record<string, unknown>)[header], item),\n ),\n );\n\n const t = table([headers, ...rows], {\n border: getBorderCharacters(\"norc\"),\n drawHorizontalLine: (lineIndex, rowCount) => {\n return lineIndex === 0 || lineIndex === 1 || lineIndex === rowCount;\n },\n });\n process.stdout.write(t);\n },\n};\n"],"mappings":"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"}
@@ -15,4 +15,4 @@ async function readPackageJson() {
15
15
 
16
16
  //#endregion
17
17
  export { readPackageJson as t };
18
- //# sourceMappingURL=package-json-CWp8s9dE.mjs.map
18
+ //# sourceMappingURL=package-json-CRzw5eUf.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"package-json-CWp8s9dE.mjs","names":[],"sources":["../src/cli/shared/package-json.ts"],"sourcesContent":["import { readPackageJSON, type PackageJson } from \"pkg-types\";\n\nlet packageJson: PackageJson | null = null;\n\n/**\n * Read and cache the package.json of the SDK package.\n * @returns Parsed package.json contents\n */\nexport async function readPackageJson() {\n if (packageJson) {\n return packageJson;\n }\n packageJson = await readPackageJSON(import.meta.url);\n return packageJson;\n}\n"],"mappings":";;;;AAEA,IAAI,cAAkC;;;;;AAMtC,eAAsB,kBAAkB;AACtC,KAAI,YACF,QAAO;AAET,eAAc,MAAM,gBAAgB,OAAO,KAAK,IAAI;AACpD,QAAO"}
1
+ {"version":3,"file":"package-json-CRzw5eUf.mjs","names":[],"sources":["../src/cli/shared/package-json.ts"],"sourcesContent":["import { readPackageJSON, type PackageJson } from \"pkg-types\";\n\nlet packageJson: PackageJson | null = null;\n\n/**\n * Read and cache the package.json of the SDK package.\n * @returns Parsed package.json contents\n */\nexport async function readPackageJson() {\n if (packageJson) {\n return packageJson;\n }\n packageJson = await readPackageJSON(import.meta.url);\n return packageJson;\n}\n"],"mappings":";;;;AAEA,IAAI,cAAkC;;;;;AAMtC,eAAsB,kBAAkB;AACtC,KAAI,YACF,QAAO;AAET,eAAc,MAAM,gBAAgB,OAAO,KAAK,IAAI;AACpD,QAAO"}
@@ -0,0 +1,4 @@
1
+
2
+ import { t as readPackageJson } from "./package-json-CRzw5eUf.mjs";
3
+
4
+ export { readPackageJson };
@@ -1,3 +1,3 @@
1
1
  /// <reference types="@tailor-platform/function-types" />
2
- import { n as enumConstantsPlugin, t as EnumConstantsGeneratorID } from "../../../index-reFAYSX7.mjs";
2
+ import { n as enumConstantsPlugin, t as EnumConstantsGeneratorID } from "../../../index-ZZYEd_0R.mjs";
3
3
  export { EnumConstantsGeneratorID, enumConstantsPlugin };
@@ -1,4 +1,4 @@
1
1
 
2
- import { n as enumConstantsPlugin, t as EnumConstantsGeneratorID } from "../../../enum-constants-Dx82rSjf.mjs";
2
+ import { n as enumConstantsPlugin, t as EnumConstantsGeneratorID } from "../../../enum-constants-C3KSpsYj.mjs";
3
3
 
4
4
  export { EnumConstantsGeneratorID, enumConstantsPlugin };
@@ -1,3 +1,3 @@
1
1
  /// <reference types="@tailor-platform/function-types" />
2
- import { n as fileUtilsPlugin, t as FileUtilsGeneratorID } from "../../../index-BtXZdz-F.mjs";
2
+ import { n as fileUtilsPlugin, t as FileUtilsGeneratorID } from "../../../index-DdsUV-aA.mjs";
3
3
  export { FileUtilsGeneratorID, fileUtilsPlugin };
@@ -1,4 +1,4 @@
1
1
 
2
- import { n as fileUtilsPlugin, t as FileUtilsGeneratorID } from "../../../file-utils-DeWpvq3T.mjs";
2
+ import { n as fileUtilsPlugin, t as FileUtilsGeneratorID } from "../../../file-utils-775qWKoo.mjs";
3
3
 
4
4
  export { FileUtilsGeneratorID, fileUtilsPlugin };
@@ -1,3 +1,3 @@
1
1
  /// <reference types="@tailor-platform/function-types" />
2
- import { n as kyselyTypePlugin, t as KyselyGeneratorID } from "../../../index-Chvw1Eod.mjs";
2
+ import { n as kyselyTypePlugin, t as KyselyGeneratorID } from "../../../index-0Dk-fDWi.mjs";
3
3
  export { KyselyGeneratorID, kyselyTypePlugin };
@@ -1,4 +1,4 @@
1
1
 
2
- import { n as kyselyTypePlugin, t as KyselyGeneratorID } from "../../../kysely-type-CwtvQuxh.mjs";
2
+ import { n as kyselyTypePlugin, t as KyselyGeneratorID } from "../../../kysely-type-BQsYEZoY.mjs";
3
3
 
4
4
  export { KyselyGeneratorID, kyselyTypePlugin };
@@ -1,3 +1,3 @@
1
1
  /// <reference types="@tailor-platform/function-types" />
2
- import { n as seedPlugin, t as SeedGeneratorID } from "../../../index-CiNNNpuH.mjs";
2
+ import { n as seedPlugin, t as SeedGeneratorID } from "../../../index-BEEL1-6Z.mjs";
3
3
  export { SeedGeneratorID, seedPlugin };
@@ -1,4 +1,4 @@
1
1
 
2
- import { n as seedPlugin, t as SeedGeneratorID } from "../../../seed-CyYPhvNL.mjs";
2
+ import { n as seedPlugin, t as SeedGeneratorID } from "../../../seed-BkVKgsxf.mjs";
3
3
 
4
4
  export { SeedGeneratorID, seedPlugin };
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@tailor-platform/function-types" />
2
- import { n as TailorAnyDBType } from "../tailor-db-field-CoFKRCYW.mjs";
3
- import { r as TailorEnv, t as TailorActor } from "../actor-B2oEmlTc.mjs";
2
+ import { n as TailorAnyDBType } from "../tailor-db-field-D_z185oq.mjs";
3
+ import { r as TailorEnv, t as TailorActor } from "../actor-DzCuoMlP.mjs";
4
4
 
5
5
  //#region src/plugin/with-context.d.ts
6
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/plugin/with-context.ts","../../src/plugin/get-generated-type.ts"],"sourcesContent":["/**\n * Plugin executor context support for defining plugin executors in separate files.\n * This module provides utilities for creating type-safe plugin executors that receive\n * context (like type references and namespace) at runtime.\n */\n\nimport type { TailorActor, TailorEnv } from \"@/types/runtime\";\n\n/**\n * Plugin executor factory function type.\n * Takes context and returns an executor configuration.\n * Returns unknown since the exact return type depends on createExecutor's generic params.\n */\nexport type PluginExecutorFactory<Ctx> = (ctx: Ctx) => unknown;\n\n// ============================================================================\n// Plugin Executor Args Types\n// ============================================================================\n\n/**\n * Base args for plugin executor function operations.\n * Provides typed access to runtime context without requiring specific record types.\n */\nexport interface PluginFunctionArgs {\n /** Workspace ID where the executor runs */\n workspaceId: string;\n /** Application namespace */\n appNamespace: string;\n /** Environment variables */\n env: TailorEnv;\n /** Actor (user) who triggered the event, null for system events */\n actor: TailorActor | null;\n /** Name of the TailorDB type */\n typeName: string;\n /** TailorDB connections by namespace */\n tailordb: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record creation.\n */\nexport interface PluginRecordCreatedArgs extends PluginFunctionArgs {\n /** The newly created record */\n newRecord: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record update.\n */\nexport interface PluginRecordUpdatedArgs extends PluginFunctionArgs {\n /** The record after update */\n newRecord: Record<string, unknown>;\n /** The record before update */\n oldRecord: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record deletion.\n */\nexport interface PluginRecordDeletedArgs extends PluginFunctionArgs {\n /** The deleted record */\n oldRecord: Record<string, unknown>;\n}\n\n/**\n * Database schema type for plugins.\n * Since plugins work with dynamic types, the schema uses Record types.\n */\nexport type PluginDBSchema = Record<string, Record<string, unknown>>;\n\n/**\n * Base record type for TailorDB records.\n * All records have an id field.\n */\nexport type PluginRecord = { id: string } & Record<string, unknown>;\n\n/**\n * Define a plugin executor that receives context at runtime.\n * This allows executor definitions to be in separate files while\n * still receiving dynamic values like typeName, generated types, and namespace.\n * @param factory - Function that takes context and returns executor configuration\n * @returns The same factory function (for type inference)\n * @example\n * ```typescript\n * // executors/on-create.ts\n * import { withPluginContext } from \"@tailor-platform/sdk/plugin\";\n * import { createExecutor, recordCreatedTrigger } from \"@tailor-platform/sdk\";\n * import { getDB } from \"@tailor-platform/function-kysely-tailordb\";\n *\n * interface MyContext {\n * sourceType: TailorAnyDBType;\n * historyType: TailorAnyDBType;\n * namespace: string;\n * }\n *\n * export default withPluginContext<MyContext>((ctx) =>\n * createExecutor({\n * name: `${ctx.sourceType.name.toLowerCase()}-on-create`,\n * trigger: recordCreatedTrigger({ type: ctx.sourceType }),\n * operation: {\n * kind: \"function\",\n * body: async (args) => {\n * const db = getDB(ctx.namespace);\n * await db.insertInto(ctx.historyType.name).values({\n * recordId: args.newRecord.id,\n * // ...\n * }).execute();\n * },\n * },\n * })\n * );\n * ```\n */\nexport function withPluginContext<Ctx>(\n factory: PluginExecutorFactory<Ctx>,\n): PluginExecutorFactory<Ctx> {\n return factory;\n}\n","import * as fs from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport * as path from \"pathe\";\nimport type { Plugin, PluginOutput, TypePluginOutput } from \"@/types/plugin\";\nimport type { TailorAnyDBType } from \"@/types/tailordb\";\n\n// ========================================\n// Config loading and caching\n// ========================================\n\ninterface PluginEntry {\n plugin: Plugin;\n pluginConfig: unknown;\n}\n\ninterface ConfigCache {\n config: { db?: Record<string, unknown> };\n plugins: Map<string, PluginEntry>;\n configDir: string;\n}\n\n/** Cache: resolved config path -> loaded config data */\nconst configCacheMap = new Map<string, ConfigCache>();\n\n/**\n * Check if a value is a Plugin instance.\n * @param value - Value to check\n * @returns True if value has the shape of Plugin\n */\nfunction isPlugin(value: unknown): value is Plugin {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Record<string, unknown>).id === \"string\" &&\n typeof (value as Record<string, unknown>).description === \"string\"\n );\n}\n\n/**\n * Load and cache config module from the given path.\n * Extracts plugins from all array exports using definePlugins() format.\n * Returns null if the config file does not exist (e.g., in bundled executor on platform server).\n * @param configPath - Absolute or relative path to tailor.config.ts\n * @returns Cached config data with plugins map, or null if config file is not available\n */\nasync function loadAndCacheConfig(configPath: string): Promise<ConfigCache | null> {\n const resolvedPath = path.resolve(configPath);\n\n const cached = configCacheMap.get(resolvedPath);\n if (cached) return cached;\n\n // Config file may not exist in bundled environments (e.g., platform server)\n if (!fs.existsSync(resolvedPath)) {\n return null;\n }\n\n const configModule = await import(pathToFileURL(resolvedPath).href);\n if (!configModule?.default) {\n throw new Error(`Invalid config module at \"${resolvedPath}\": default export not found`);\n }\n\n const config = configModule.default as { db?: Record<string, unknown> };\n const configDir = path.dirname(resolvedPath);\n const plugins = new Map<string, PluginEntry>();\n\n // Find plugin arrays from exports (definePlugins returns PluginConfig[])\n for (const value of Object.values(configModule)) {\n if (!Array.isArray(value)) continue;\n\n for (const item of value) {\n if (isPlugin(item)) {\n plugins.set(item.id, { plugin: item, pluginConfig: item.pluginConfig });\n }\n }\n }\n\n const result: ConfigCache = { config, plugins, configDir };\n configCacheMap.set(resolvedPath, result);\n return result;\n}\n\n// ========================================\n// Namespace resolution\n// ========================================\n\ninterface DbNamespaceConfig {\n files?: string[];\n external?: boolean;\n}\n\n/**\n * Resolve the namespace for a type-attached sourceType by checking config.db file patterns.\n * Uses ESM module cache identity: same file path yields same object references.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param configDir - Directory containing the config file\n * @param sourceType - The TailorDB type to look up\n * @returns The namespace name\n */\nasync function resolveNamespaceForType(\n config: { db?: Record<string, unknown> },\n configDir: string,\n sourceType: TailorAnyDBType,\n): Promise<string> {\n if (!config.db) {\n throw new Error(`No db configuration found in config`);\n }\n\n for (const [namespace, nsConfig] of Object.entries(config.db)) {\n const dbConfig = nsConfig as DbNamespaceConfig;\n // Skip external namespaces (no files to resolve)\n if (dbConfig.external || !dbConfig.files) continue;\n\n for (const pattern of dbConfig.files) {\n const absolutePattern = path.resolve(configDir, pattern);\n let matchedFiles: string[];\n try {\n matchedFiles = fs.globSync(absolutePattern);\n } catch {\n continue;\n }\n\n for (const file of matchedFiles) {\n const mod = await import(pathToFileURL(file).href);\n for (const exported of Object.values(mod)) {\n if (exported === sourceType) {\n return namespace;\n }\n }\n }\n }\n }\n\n throw new Error(\n `Could not resolve namespace for type \"${sourceType.name}\". ` +\n `Ensure the type file is included in a db namespace's files pattern.`,\n );\n}\n\n/**\n * Resolve the namespace for a namespace plugin by trying each namespace.\n * Calls onNamespaceLoaded() for each and returns the first whose output contains the requested kind.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param plugin - Plugin instance\n * @param kind - The generated type kind to look for\n * @param pluginConfig - Plugin-level configuration\n * @returns The namespace name\n */\nasync function resolveNamespaceForNamespacePlugin(\n config: { db?: Record<string, unknown> },\n plugin: Plugin,\n kind: string,\n pluginConfig: unknown,\n): Promise<{ namespace: string; output: PluginOutput }> {\n if (!config.db) {\n throw new Error(`No db configuration found in config`);\n }\n\n if (!plugin.onNamespaceLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onNamespaceLoaded() method`);\n }\n\n for (const namespace of Object.keys(config.db)) {\n const dbConfig = config.db[namespace] as DbNamespaceConfig;\n if (dbConfig.external) continue;\n\n const output = await plugin.onNamespaceLoaded({\n pluginConfig,\n namespace,\n });\n\n if (output.types?.[kind]) {\n return { namespace, output };\n }\n }\n\n throw new Error(\n `Could not resolve namespace for plugin \"${plugin.id}\" with kind \"${kind}\". ` +\n `No namespace produced a type with that kind.`,\n );\n}\n\n// ========================================\n// Process caching\n// ========================================\n\n// Cache: plugin -> cacheKey -> TypePluginOutput\nconst processCache = new WeakMap<Plugin, Map<string, TypePluginOutput>>();\n\n// Cache for namespace plugins: plugin -> cacheKey -> PluginOutput\nconst namespaceProcessCache = new WeakMap<Plugin, Map<string, PluginOutput>>();\n\n/**\n * Generate a cache key that includes pluginConfig.\n * @param baseKey - Base key for the cache\n * @param pluginConfig - Plugin configuration to include in the key\n * @returns Cache key string\n */\nfunction getCacheKey(baseKey: string, pluginConfig: unknown): string {\n if (pluginConfig === undefined) {\n return baseKey;\n }\n try {\n return `${baseKey}:${JSON.stringify(pluginConfig)}`;\n } catch {\n throw new Error(\n `pluginConfig must be JSON-serializable for caching. Received non-serializable value.`,\n );\n }\n}\n\n// ========================================\n// Main API\n// ========================================\n\n/**\n * Get a generated type from a plugin by loading the config and resolving everything automatically.\n * For type-attached plugins, calls onTypeLoaded() with the sourceType.\n * For namespace plugins, calls onNamespaceLoaded() with auto-resolved namespace.\n * Results are cached per config path, plugin, namespace, and pluginConfig to avoid redundant processing.\n * @param configPath - Path to tailor.config.ts (absolute or relative to cwd)\n * @param pluginId - The plugin's unique identifier\n * @param sourceType - The source TailorDB type (null for namespace plugins)\n * @param kind - The generated type kind (e.g., \"request\", \"step\")\n * @returns The generated TailorDB type\n */\nexport async function getGeneratedType(\n configPath: string,\n pluginId: string,\n sourceType: TailorAnyDBType | null,\n kind: string,\n): Promise<TailorAnyDBType> {\n const cache = await loadAndCacheConfig(configPath);\n\n if (!cache) {\n // Config not available (e.g., running in bundled executor on platform server).\n // Return a placeholder. The actual type is resolved at generate/apply time.\n return { name: `__placeholder_${kind}__`, fields: {} } as TailorAnyDBType;\n }\n\n const { config, configDir, plugins } = cache;\n\n const pluginEntry = plugins.get(pluginId);\n if (!pluginEntry) {\n throw new Error(\n `Plugin \"${pluginId}\" not found in config at \"${configPath}\". ` +\n `Ensure the plugin is registered via definePlugins().`,\n );\n }\n\n const { plugin, pluginConfig } = pluginEntry;\n\n if (sourceType === null) {\n return getGeneratedTypeForNamespacePlugin(config, plugin, kind, pluginConfig);\n }\n\n const namespace = await resolveNamespaceForType(config, configDir, sourceType);\n return getGeneratedTypeForTypeAttachedPlugin(plugin, sourceType, kind, pluginConfig, namespace);\n}\n\n/**\n * Get a generated type from a type-attached plugin.\n * @param plugin - The plugin instance (must have onTypeLoaded() method)\n * @param sourceType - The source TailorDB type\n * @param kind - The generated type kind\n * @param pluginConfig - Plugin-level configuration\n * @param namespace - Resolved namespace\n * @returns The generated TailorDB type\n */\nasync function getGeneratedTypeForTypeAttachedPlugin(\n plugin: Plugin,\n sourceType: TailorAnyDBType,\n kind: string,\n pluginConfig: unknown,\n namespace: string,\n): Promise<TailorAnyDBType> {\n if (!plugin.onTypeLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onTypeLoaded() method`);\n }\n\n // Check cache first\n let pluginCache = processCache.get(plugin);\n if (!pluginCache) {\n pluginCache = new Map();\n processCache.set(plugin, pluginCache);\n }\n\n const cacheKey = getCacheKey(`${sourceType.name}:ns=${namespace}`, pluginConfig);\n let output = pluginCache.get(cacheKey);\n\n if (!output) {\n const typeConfig = sourceType.plugins?.find((p) => p.pluginId === plugin.id)?.config;\n output = await plugin.onTypeLoaded({\n type: sourceType,\n typeConfig: typeConfig ?? {},\n pluginConfig,\n namespace,\n });\n pluginCache.set(cacheKey, output);\n }\n\n const generatedType = output.types?.[kind];\n if (!generatedType) {\n throw new Error(\n `Generated type not found: plugin=${plugin.id}, sourceType=${sourceType.name}, kind=${kind}`,\n );\n }\n\n return generatedType as TailorAnyDBType;\n}\n\n/**\n * Get a generated type from a namespace plugin.\n * Auto-resolves the namespace by trying each one.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param plugin - The plugin instance (must have onNamespaceLoaded() method)\n * @param kind - The generated type kind\n * @param pluginConfig - Plugin-level configuration\n * @returns The generated TailorDB type\n */\nasync function getGeneratedTypeForNamespacePlugin(\n config: { db?: Record<string, unknown> },\n plugin: Plugin,\n kind: string,\n pluginConfig: unknown,\n): Promise<TailorAnyDBType> {\n if (!plugin.onNamespaceLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onNamespaceLoaded() method`);\n }\n\n // Check cache first - try all namespaces\n let pluginCache = namespaceProcessCache.get(plugin);\n if (!pluginCache) {\n pluginCache = new Map();\n namespaceProcessCache.set(plugin, pluginCache);\n }\n\n // Try cached results first\n if (config.db) {\n for (const namespace of Object.keys(config.db)) {\n const dbConfig = config.db[namespace] as DbNamespaceConfig;\n if (dbConfig.external) continue;\n\n const cacheKey = getCacheKey(`namespace:ns=${namespace}`, pluginConfig);\n const cached = pluginCache.get(cacheKey);\n if (cached?.types?.[kind]) {\n return cached.types[kind] as TailorAnyDBType;\n }\n }\n }\n\n // Not in cache - resolve namespace and process\n const { namespace, output } = await resolveNamespaceForNamespacePlugin(\n config,\n plugin,\n kind,\n pluginConfig,\n );\n\n const cacheKey = getCacheKey(`namespace:ns=${namespace}`, pluginConfig);\n pluginCache.set(cacheKey, output);\n\n const generatedType = output.types?.[kind];\n if (!generatedType) {\n throw new Error(`Generated type not found: plugin=${plugin.id}, kind=${kind}`);\n }\n\n return generatedType as TailorAnyDBType;\n}\n\n/**\n * Clear all internal caches. For testing only.\n */\nexport function _clearCacheForTesting(): void {\n configCacheMap.clear();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/plugin/with-context.ts","../../src/plugin/get-generated-type.ts"],"sourcesContent":["/**\n * Plugin executor context support for defining plugin executors in separate files.\n * This module provides utilities for creating type-safe plugin executors that receive\n * context (like type references and namespace) at runtime.\n */\n\nimport type { TailorActor, TailorEnv } from \"@/types/runtime\";\n\n/**\n * Plugin executor factory function type.\n * Takes context and returns an executor configuration.\n * Returns unknown since the exact return type depends on createExecutor's generic params.\n */\nexport type PluginExecutorFactory<Ctx> = (ctx: Ctx) => unknown;\n\n// ============================================================================\n// Plugin Executor Args Types\n// ============================================================================\n\n/**\n * Base args for plugin executor function operations.\n * Provides typed access to runtime context without requiring specific record types.\n */\nexport interface PluginFunctionArgs {\n /** Workspace ID where the executor runs */\n workspaceId: string;\n /** Application namespace */\n appNamespace: string;\n /** Environment variables */\n env: TailorEnv;\n /** Actor (user) who triggered the event, null for system events */\n actor: TailorActor | null;\n /** Name of the TailorDB type */\n typeName: string;\n /** TailorDB connections by namespace */\n tailordb: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record creation.\n */\nexport interface PluginRecordCreatedArgs extends PluginFunctionArgs {\n /** The newly created record */\n newRecord: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record update.\n */\nexport interface PluginRecordUpdatedArgs extends PluginFunctionArgs {\n /** The record after update */\n newRecord: Record<string, unknown>;\n /** The record before update */\n oldRecord: Record<string, unknown>;\n}\n\n/**\n * Args for plugin executors triggered on record deletion.\n */\nexport interface PluginRecordDeletedArgs extends PluginFunctionArgs {\n /** The deleted record */\n oldRecord: Record<string, unknown>;\n}\n\n/**\n * Database schema type for plugins.\n * Since plugins work with dynamic types, the schema uses Record types.\n */\nexport type PluginDBSchema = Record<string, Record<string, unknown>>;\n\n/**\n * Base record type for TailorDB records.\n * All records have an id field.\n */\nexport type PluginRecord = { id: string } & Record<string, unknown>;\n\n/**\n * Define a plugin executor that receives context at runtime.\n * This allows executor definitions to be in separate files while\n * still receiving dynamic values like typeName, generated types, and namespace.\n * @param factory - Function that takes context and returns executor configuration\n * @returns The same factory function (for type inference)\n * @example\n * ```typescript\n * // executors/on-create.ts\n * import { withPluginContext } from \"@tailor-platform/sdk/plugin\";\n * import { createExecutor, recordCreatedTrigger } from \"@tailor-platform/sdk\";\n * import { getDB } from \"@tailor-platform/function-kysely-tailordb\";\n *\n * interface MyContext {\n * sourceType: TailorAnyDBType;\n * historyType: TailorAnyDBType;\n * namespace: string;\n * }\n *\n * export default withPluginContext<MyContext>((ctx) =>\n * createExecutor({\n * name: `${ctx.sourceType.name.toLowerCase()}-on-create`,\n * trigger: recordCreatedTrigger({ type: ctx.sourceType }),\n * operation: {\n * kind: \"function\",\n * body: async (args) => {\n * const db = getDB(ctx.namespace);\n * await db.insertInto(ctx.historyType.name).values({\n * recordId: args.newRecord.id,\n * // ...\n * }).execute();\n * },\n * },\n * })\n * );\n * ```\n */\nexport function withPluginContext<Ctx>(\n factory: PluginExecutorFactory<Ctx>,\n): PluginExecutorFactory<Ctx> {\n return factory;\n}\n","import * as fs from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\nimport * as path from \"pathe\";\nimport type { Plugin, PluginOutput, TypePluginOutput } from \"@/types/plugin\";\nimport type { TailorAnyDBType } from \"@/types/tailordb\";\n\n// ========================================\n// Config loading and caching\n// ========================================\n\ninterface PluginEntry {\n plugin: Plugin;\n pluginConfig: unknown;\n}\n\ninterface ConfigCache {\n config: { db?: Record<string, unknown> };\n plugins: Map<string, PluginEntry>;\n configDir: string;\n}\n\n/** Cache: resolved config path -> loaded config data */\nconst configCacheMap = new Map<string, ConfigCache>();\n\n/**\n * Check if a value is a Plugin instance.\n * @param value - Value to check\n * @returns True if value has the shape of Plugin\n */\nfunction isPlugin(value: unknown): value is Plugin {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Record<string, unknown>).id === \"string\" &&\n typeof (value as Record<string, unknown>).description === \"string\"\n );\n}\n\n/**\n * Load and cache config module from the given path.\n * Extracts plugins from all array exports using definePlugins() format.\n * Returns null if the config file does not exist (e.g., in bundled executor on platform server).\n * @param configPath - Absolute or relative path to tailor.config.ts\n * @returns Cached config data with plugins map, or null if config file is not available\n */\nasync function loadAndCacheConfig(configPath: string): Promise<ConfigCache | null> {\n const resolvedPath = path.resolve(configPath);\n\n const cached = configCacheMap.get(resolvedPath);\n if (cached) return cached;\n\n // Config file may not exist in bundled environments (e.g., platform server)\n if (!fs.existsSync(resolvedPath)) {\n return null;\n }\n\n const configModule = await import(pathToFileURL(resolvedPath).href);\n if (!configModule?.default) {\n throw new Error(`Invalid config module at \"${resolvedPath}\": default export not found`);\n }\n\n const config = configModule.default as { db?: Record<string, unknown> };\n const configDir = path.dirname(resolvedPath);\n const plugins = new Map<string, PluginEntry>();\n\n // Find plugin arrays from exports (definePlugins returns PluginConfig[])\n for (const value of Object.values(configModule)) {\n if (!Array.isArray(value)) continue;\n\n for (const item of value) {\n if (isPlugin(item)) {\n plugins.set(item.id, { plugin: item, pluginConfig: item.pluginConfig });\n }\n }\n }\n\n const result: ConfigCache = { config, plugins, configDir };\n configCacheMap.set(resolvedPath, result);\n return result;\n}\n\n// ========================================\n// Namespace resolution\n// ========================================\n\ninterface DbNamespaceConfig {\n files?: string[];\n external?: boolean;\n}\n\n/**\n * Resolve the namespace for a type-attached sourceType by checking config.db file patterns.\n * Uses ESM module cache identity: same file path yields same object references.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param configDir - Directory containing the config file\n * @param sourceType - The TailorDB type to look up\n * @returns The namespace name\n */\nasync function resolveNamespaceForType(\n config: { db?: Record<string, unknown> },\n configDir: string,\n sourceType: TailorAnyDBType,\n): Promise<string> {\n if (!config.db) {\n throw new Error(`No db configuration found in config`);\n }\n\n for (const [namespace, nsConfig] of Object.entries(config.db)) {\n const dbConfig = nsConfig as DbNamespaceConfig;\n // Skip external namespaces (no files to resolve)\n if (dbConfig.external || !dbConfig.files) continue;\n\n for (const pattern of dbConfig.files) {\n const absolutePattern = path.resolve(configDir, pattern);\n let matchedFiles: string[];\n try {\n matchedFiles = fs.globSync(absolutePattern);\n } catch {\n continue;\n }\n\n for (const file of matchedFiles) {\n const mod = await import(pathToFileURL(file).href);\n for (const exported of Object.values(mod)) {\n if (exported === sourceType) {\n return namespace;\n }\n }\n }\n }\n }\n\n throw new Error(\n `Could not resolve namespace for type \"${sourceType.name}\". ` +\n `Ensure the type file is included in a db namespace's files pattern.`,\n );\n}\n\n/**\n * Resolve the namespace for a namespace plugin by trying each namespace.\n * Calls onNamespaceLoaded() for each and returns the first whose output contains the requested kind.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param plugin - Plugin instance\n * @param kind - The generated type kind to look for\n * @param pluginConfig - Plugin-level configuration\n * @returns The namespace name\n */\nasync function resolveNamespaceForNamespacePlugin(\n config: { db?: Record<string, unknown> },\n plugin: Plugin,\n kind: string,\n pluginConfig: unknown,\n): Promise<{ namespace: string; output: PluginOutput }> {\n if (!config.db) {\n throw new Error(`No db configuration found in config`);\n }\n\n if (!plugin.onNamespaceLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onNamespaceLoaded() method`);\n }\n\n for (const namespace of Object.keys(config.db)) {\n const dbConfig = config.db[namespace] as DbNamespaceConfig;\n if (dbConfig.external) continue;\n\n const output = await plugin.onNamespaceLoaded({\n pluginConfig,\n namespace,\n });\n\n if (output.types?.[kind]) {\n return { namespace, output };\n }\n }\n\n throw new Error(\n `Could not resolve namespace for plugin \"${plugin.id}\" with kind \"${kind}\". ` +\n `No namespace produced a type with that kind.`,\n );\n}\n\n// ========================================\n// Process caching\n// ========================================\n\n// Cache: plugin -> cacheKey -> TypePluginOutput\nconst processCache = new WeakMap<Plugin, Map<string, TypePluginOutput>>();\n\n// Cache for namespace plugins: plugin -> cacheKey -> PluginOutput\nconst namespaceProcessCache = new WeakMap<Plugin, Map<string, PluginOutput>>();\n\n/**\n * Generate a cache key that includes pluginConfig.\n * @param baseKey - Base key for the cache\n * @param pluginConfig - Plugin configuration to include in the key\n * @returns Cache key string\n */\nfunction getCacheKey(baseKey: string, pluginConfig: unknown): string {\n if (pluginConfig === undefined) {\n return baseKey;\n }\n try {\n return `${baseKey}:${JSON.stringify(pluginConfig)}`;\n } catch {\n throw new Error(\n `pluginConfig must be JSON-serializable for caching. Received non-serializable value.`,\n );\n }\n}\n\n// ========================================\n// Main API\n// ========================================\n\n/**\n * Get a generated type from a plugin by loading the config and resolving everything automatically.\n * For type-attached plugins, calls onTypeLoaded() with the sourceType.\n * For namespace plugins, calls onNamespaceLoaded() with auto-resolved namespace.\n * Results are cached per config path, plugin, namespace, and pluginConfig to avoid redundant processing.\n * @param configPath - Path to tailor.config.ts (absolute or relative to cwd)\n * @param pluginId - The plugin's unique identifier\n * @param sourceType - The source TailorDB type (null for namespace plugins)\n * @param kind - The generated type kind (e.g., \"request\", \"step\")\n * @returns The generated TailorDB type\n */\nexport async function getGeneratedType(\n configPath: string,\n pluginId: string,\n sourceType: TailorAnyDBType | null,\n kind: string,\n): Promise<TailorAnyDBType> {\n const cache = await loadAndCacheConfig(configPath);\n\n if (!cache) {\n // Config not available (e.g., running in bundled executor on platform server).\n // Return a placeholder. The actual type is resolved at generate/apply time.\n return { name: `__placeholder_${kind}__`, fields: {} } as TailorAnyDBType;\n }\n\n const { config, configDir, plugins } = cache;\n\n const pluginEntry = plugins.get(pluginId);\n if (!pluginEntry) {\n throw new Error(\n `Plugin \"${pluginId}\" not found in config at \"${configPath}\". ` +\n `Ensure the plugin is registered via definePlugins().`,\n );\n }\n\n const { plugin, pluginConfig } = pluginEntry;\n\n if (sourceType === null) {\n return getGeneratedTypeForNamespacePlugin(config, plugin, kind, pluginConfig);\n }\n\n const namespace = await resolveNamespaceForType(config, configDir, sourceType);\n return getGeneratedTypeForTypeAttachedPlugin(plugin, sourceType, kind, pluginConfig, namespace);\n}\n\n/**\n * Get a generated type from a type-attached plugin.\n * @param plugin - The plugin instance (must have onTypeLoaded() method)\n * @param sourceType - The source TailorDB type\n * @param kind - The generated type kind\n * @param pluginConfig - Plugin-level configuration\n * @param namespace - Resolved namespace\n * @returns The generated TailorDB type\n */\nasync function getGeneratedTypeForTypeAttachedPlugin(\n plugin: Plugin,\n sourceType: TailorAnyDBType,\n kind: string,\n pluginConfig: unknown,\n namespace: string,\n): Promise<TailorAnyDBType> {\n if (!plugin.onTypeLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onTypeLoaded() method`);\n }\n\n // Check cache first\n let pluginCache = processCache.get(plugin);\n if (!pluginCache) {\n pluginCache = new Map();\n processCache.set(plugin, pluginCache);\n }\n\n const cacheKey = getCacheKey(`${sourceType.name}:ns=${namespace}`, pluginConfig);\n let output = pluginCache.get(cacheKey);\n\n if (!output) {\n const typeConfig = sourceType.plugins?.find((p) => p.pluginId === plugin.id)?.config;\n output = await plugin.onTypeLoaded({\n type: sourceType,\n typeConfig: typeConfig ?? {},\n pluginConfig,\n namespace,\n });\n pluginCache.set(cacheKey, output);\n }\n\n const generatedType = output.types?.[kind];\n if (!generatedType) {\n throw new Error(\n `Generated type not found: plugin=${plugin.id}, sourceType=${sourceType.name}, kind=${kind}`,\n );\n }\n\n return generatedType as TailorAnyDBType;\n}\n\n/**\n * Get a generated type from a namespace plugin.\n * Auto-resolves the namespace by trying each one.\n * @param config - App config with db namespace definitions\n * @param config.db - DB namespace definitions\n * @param plugin - The plugin instance (must have onNamespaceLoaded() method)\n * @param kind - The generated type kind\n * @param pluginConfig - Plugin-level configuration\n * @returns The generated TailorDB type\n */\nasync function getGeneratedTypeForNamespacePlugin(\n config: { db?: Record<string, unknown> },\n plugin: Plugin,\n kind: string,\n pluginConfig: unknown,\n): Promise<TailorAnyDBType> {\n if (!plugin.onNamespaceLoaded) {\n throw new Error(`Plugin \"${plugin.id}\" does not have a onNamespaceLoaded() method`);\n }\n\n // Check cache first - try all namespaces\n let pluginCache = namespaceProcessCache.get(plugin);\n if (!pluginCache) {\n pluginCache = new Map();\n namespaceProcessCache.set(plugin, pluginCache);\n }\n\n // Try cached results first\n if (config.db) {\n for (const namespace of Object.keys(config.db)) {\n const dbConfig = config.db[namespace] as DbNamespaceConfig;\n if (dbConfig.external) continue;\n\n const cacheKey = getCacheKey(`namespace:ns=${namespace}`, pluginConfig);\n const cached = pluginCache.get(cacheKey);\n if (cached?.types?.[kind]) {\n return cached.types[kind] as TailorAnyDBType;\n }\n }\n }\n\n // Not in cache - resolve namespace and process\n const { namespace, output } = await resolveNamespaceForNamespacePlugin(\n config,\n plugin,\n kind,\n pluginConfig,\n );\n\n const cacheKey = getCacheKey(`namespace:ns=${namespace}`, pluginConfig);\n pluginCache.set(cacheKey, output);\n\n const generatedType = output.types?.[kind];\n if (!generatedType) {\n throw new Error(`Generated type not found: plugin=${plugin.id}, kind=${kind}`);\n }\n\n return generatedType as TailorAnyDBType;\n}\n\n/**\n * Clear all internal caches. For testing only.\n */\nexport function _clearCacheForTesting(): void {\n configCacheMap.clear();\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"repl-editor-DjycioU-.mjs","names":[],"sources":["../src/cli/query/repl-editor.ts"],"sourcesContent":["import { GraphQLError, Lexer, Source, TokenKind } from \"graphql\";\nimport { getSegments } from \"sql-highlight\";\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 = lines[row];\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 = lines[row];\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 = lines[row];\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 = lines[row - 1];\n const baseIndent = prevLine.match(/^(\\s*)/)?.[1] ?? \"\";\n const endsWithOpen = /[{([]$/.test(prevLine.trimEnd());\n const startsWithClose = /^[}\\])]/.test(lines[row].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 newLines[row] = innerIndent;\n newLines.splice(row + 1, 0, baseIndent + lines[row].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 newLines[row] = indent + lines[row];\n newLines.splice(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 + lines[row]), row, col: baseIndent.length };\n }\n }\n\n return undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"repl-editor-DjycioU-.mjs","names":[],"sources":["../src/cli/query/repl-editor.ts"],"sourcesContent":["import { GraphQLError, Lexer, Source, TokenKind } from \"graphql\";\nimport { getSegments } from \"sql-highlight\";\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 = lines[row];\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 = lines[row];\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 = lines[row];\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 = lines[row - 1];\n const baseIndent = prevLine.match(/^(\\s*)/)?.[1] ?? \"\";\n const endsWithOpen = /[{([]$/.test(prevLine.trimEnd());\n const startsWithClose = /^[}\\])]/.test(lines[row].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 newLines[row] = innerIndent;\n newLines.splice(row + 1, 0, baseIndent + lines[row].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 newLines[row] = indent + lines[row];\n newLines.splice(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 + lines[row]), row, col: baseIndent.length };\n }\n }\n\n return undefined;\n}\n"],"mappings":"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"}