@tailor-platform/sdk 1.60.3 → 1.63.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 (61) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/{application-pusdxz35.mjs → application-BezXGbrU.mjs} +73 -509
  3. package/dist/application-BezXGbrU.mjs.map +1 -0
  4. package/dist/application-DSXntqnV.mjs +4 -0
  5. package/dist/assert-CKfwrmCV.mjs +10 -0
  6. package/dist/assert-CKfwrmCV.mjs.map +1 -0
  7. package/dist/cli/index.mjs +819 -239
  8. package/dist/cli/index.mjs.map +1 -1
  9. package/dist/cli/lib.d.mts +3 -1
  10. package/dist/cli/lib.mjs +13 -6
  11. package/dist/cli/lib.mjs.map +1 -1
  12. package/dist/{client-B-jRdlC_.mjs → client-C68VWo4g.mjs} +1 -1
  13. package/dist/{client-W5P4NYYX.mjs → client-CobIRHl-.mjs} +207 -2
  14. package/dist/{client-W5P4NYYX.mjs.map → client-CobIRHl-.mjs.map} +1 -1
  15. package/dist/configure/index.mjs +2 -2
  16. package/dist/{crashreport-D3DvAzdg.mjs → crashreport-BhD0y14F.mjs} +2 -2
  17. package/dist/{crashreport-D3DvAzdg.mjs.map → crashreport-BhD0y14F.mjs.map} +1 -1
  18. package/dist/{crashreport-lnVTnbB5.mjs → crashreport-D1wKBJ8N.mjs} +1 -1
  19. package/dist/{mock-Dpu__UeJ.mjs → mock-DMgIygjE.mjs} +3 -2
  20. package/dist/mock-DMgIygjE.mjs.map +1 -0
  21. package/dist/plugin/builtin/seed/index.mjs +1 -1
  22. package/dist/plugin/index.mjs +1 -1
  23. package/dist/plugin/index.mjs.map +1 -1
  24. package/dist/registry-D0uB0OrK.mjs.map +1 -1
  25. package/dist/{repl-editor-Y9QJDL0K.mjs → repl-editor-CJG3sz7A.mjs} +11 -9
  26. package/dist/repl-editor-CJG3sz7A.mjs.map +1 -0
  27. package/dist/{runtime-C0_FZWdE.mjs → runtime-CW3jcQCc.mjs} +979 -584
  28. package/dist/runtime-CW3jcQCc.mjs.map +1 -0
  29. package/dist/{schema-DYKNTu-n.mjs → schema-1msIhXwA.mjs} +12 -7
  30. package/dist/schema-1msIhXwA.mjs.map +1 -0
  31. package/dist/{seed-C0fE2sJB.mjs → seed-BH2FbrPV.mjs} +4 -3
  32. package/dist/seed-BH2FbrPV.mjs.map +1 -0
  33. package/dist/service-BHQIerYh.mjs +4 -0
  34. package/dist/{service-aPT0fx3y.mjs → service-DMohAx8a2.mjs} +3 -3
  35. package/dist/service-DMohAx8a2.mjs.map +1 -0
  36. package/dist/service-wI3Hvrgx.mjs +460 -0
  37. package/dist/service-wI3Hvrgx.mjs.map +1 -0
  38. package/dist/{types-Ccwchyj5.mjs → types-2Be3wSMc.mjs} +1 -1
  39. package/dist/{types-BwGth3a1.mjs → types-CmzfQP_m.mjs} +3 -3
  40. package/dist/types-CmzfQP_m.mjs.map +1 -0
  41. package/dist/utils/test/index.mjs +1 -1
  42. package/dist/utils/test/index.mjs.map +1 -1
  43. package/dist/vitest/index.mjs +7 -4
  44. package/dist/vitest/index.mjs.map +1 -1
  45. package/dist/vitest/setup.mjs +1 -1
  46. package/docs/cli/application.md +11 -10
  47. package/docs/cli/setup.md +18 -12
  48. package/docs/cli/tailordb.md +54 -0
  49. package/docs/cli-reference.md +4 -3
  50. package/docs/github-actions.md +337 -0
  51. package/docs/services/tailordb-migration.md +17 -1
  52. package/package.json +4 -3
  53. package/dist/application-D4tRNn90.mjs +0 -4
  54. package/dist/application-pusdxz35.mjs.map +0 -1
  55. package/dist/mock-Dpu__UeJ.mjs.map +0 -1
  56. package/dist/repl-editor-Y9QJDL0K.mjs.map +0 -1
  57. package/dist/runtime-C0_FZWdE.mjs.map +0 -1
  58. package/dist/schema-DYKNTu-n.mjs.map +0 -1
  59. package/dist/seed-C0fE2sJB.mjs.map +0 -1
  60. package/dist/service-aPT0fx3y.mjs.map +0 -1
  61. package/dist/types-BwGth3a1.mjs.map +0 -1
@@ -32,7 +32,7 @@ function setupTailordbMock(resolver = () => []) {
32
32
  query,
33
33
  params
34
34
  });
35
- return { rows: resolver(query, params) ?? [] };
35
+ return { rows: resolver(query, params) };
36
36
  }
37
37
  }
38
38
  GlobalThis.tailordb = { Client: MockTailordbClient };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/utils/test/mock.ts","../../../src/utils/test/index.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { ContextInvoker } from \"@/runtime/context\";\nimport type { TailorInvoker } from \"@/types/user\";\n\ntype MainFunction = (args: Record<string, unknown>) => unknown | Promise<unknown>;\ntype QueryResolver = (query: string, params: unknown[]) => unknown[];\ntype JobHandler = (jobName: string, args: unknown) => unknown;\ntype WaitHandler = (key: string, payload: unknown) => unknown;\ntype ResolveHandler = (\n executionId: string,\n key: string,\n callback: (payload: unknown) => unknown,\n) => Promise<void> | void;\n\ninterface TailordbGlobal {\n tailordb?: {\n Client: new (config: { namespace?: string }) => {\n connect(): Promise<void> | void;\n end(): Promise<void> | void;\n queryObject(\n query: string,\n params?: unknown[],\n ): Promise<{ rows: unknown[] }> | { rows: unknown[] };\n };\n };\n tailor?: {\n workflow: {\n triggerJobFunction: (jobName: string, args: unknown) => unknown;\n wait?: (key: string, payload?: unknown) => unknown;\n resolve?: (\n executionId: string,\n key: string,\n callback: (payload: unknown) => unknown,\n ) => Promise<void>;\n };\n context: {\n getInvoker: () => ContextInvoker | null;\n };\n };\n}\n\ninterface TailorErrorItem {\n message: string;\n path: (string | number)[];\n}\n\ninterface TailorErrorsGlobal {\n TailorErrors?: new (errors: TailorErrorItem[]) => Error;\n}\n\nconst GlobalThis = globalThis as TailordbGlobal & TailorErrorsGlobal;\n\n/**\n * Sets up a mock for `globalThis.tailordb.Client` used in bundled resolver/executor tests.\n * @deprecated Use `mockTailordb` from `@tailor-platform/sdk/vitest` with the `tailor-runtime` environment instead.\n * @param resolver - Optional function to resolve query results. Defaults to returning empty arrays.\n * @returns Object containing arrays of executed queries and created clients for assertions.\n */\nexport function setupTailordbMock(resolver: QueryResolver = () => []): {\n executedQueries: { query: string; params: unknown[] }[];\n createdClients: { namespace?: string; ended: boolean }[];\n} {\n const executedQueries: { query: string; params: unknown[] }[] = [];\n const createdClients: { namespace?: string; ended: boolean }[] = [];\n\n class MockTailordbClient {\n private record: { namespace?: string; ended: boolean };\n\n constructor({ namespace }: { namespace?: string }) {\n this.record = { namespace, ended: false };\n createdClients.push(this.record);\n }\n\n async connect(): Promise<void> {\n /* noop */\n }\n\n async end(): Promise<void> {\n this.record.ended = true;\n }\n\n async queryObject(query: string, params: unknown[] = []): Promise<{ rows: unknown[] }> {\n executedQueries.push({ query, params });\n return { rows: resolver(query, params) ?? [] };\n }\n }\n\n GlobalThis.tailordb = {\n Client: MockTailordbClient,\n } as typeof GlobalThis.tailordb;\n\n return { executedQueries, createdClients };\n}\n\n/**\n * Sets up a mock for `globalThis.tailor.workflow.triggerJobFunction` used in bundled workflow tests.\n * `wait`/`resolve` are stubbed to throw a helpful error directing to `mockWorkflow`,\n * so mistakenly calling wait without wait-point mocks produces a clear message instead of a TypeError.\n * @deprecated Use `mockWorkflow` from `@tailor-platform/sdk/vitest` with the `tailor-runtime` environment instead.\n * @param handler - Function that handles triggered job calls and returns results.\n * @returns Object containing an array of triggered jobs for assertions.\n */\nexport function setupWorkflowMock(handler: JobHandler): {\n triggeredJobs: { jobName: string; args: unknown }[];\n} {\n const triggeredJobs: { jobName: string; args: unknown }[] = [];\n\n GlobalThis.tailor = {\n ...GlobalThis.tailor,\n workflow: {\n wait: () => {\n throw new Error(\n \"tailor.workflow.wait is not mocked. Use mockWorkflow from @tailor-platform/sdk/vitest in tests.\",\n );\n },\n resolve: async () => {\n throw new Error(\n \"tailor.workflow.resolve is not mocked. Use mockWorkflow from @tailor-platform/sdk/vitest in tests.\",\n );\n },\n ...GlobalThis.tailor?.workflow,\n triggerJobFunction: (jobName: string, args: unknown) => {\n triggeredJobs.push({ jobName, args });\n return handler(jobName, args);\n },\n },\n } as typeof GlobalThis.tailor;\n\n return { triggeredJobs };\n}\n\n/**\n * Sets up a mock for `globalThis.tailor.context.getInvoker` used in bundled\n * resolver/executor/workflow tests.\n * @deprecated With the `tailor-runtime` environment from `@tailor-platform/sdk/vitest`, drive the invoker via `vi.spyOn(globalThis.tailor.context, \"getInvoker\").mockReturnValue(...)` for bundled tests, or pass `invoker` directly to `.body()` when unit-testing resolvers/executors/workflow jobs against the TypeScript source.\n * @param invoker - The `TailorInvoker` value to return, or `null` for anonymous.\n */\nexport function setupInvokerMock(invoker: TailorInvoker): void {\n const raw: ContextInvoker | null = invoker\n ? {\n id: invoker.id,\n type: invoker.type,\n workspaceId: invoker.workspaceId,\n attributes: invoker.attributeList as string[],\n attributeMap: invoker.attributes as Record<string, unknown>,\n }\n : null;\n\n GlobalThis.tailor = {\n ...GlobalThis.tailor,\n context: {\n getInvoker: () => raw,\n },\n } as typeof GlobalThis.tailor;\n}\n\n/**\n * Sets up a mock for `globalThis.TailorErrors` used in bundled resolver tests.\n * Mimics the PF runtime's TailorErrors class that serializes errors with the `TailorErrors: ` prefix.\n * @deprecated Use the `tailor-runtime` environment from `@tailor-platform/sdk/vitest` which auto-injects TailorErrors.\n */\nexport function setupTailorErrorsMock(): void {\n GlobalThis.TailorErrors = class TailorErrors extends Error {\n errors: TailorErrorItem[];\n\n constructor(errors: TailorErrorItem[]) {\n super(`TailorErrors: ${JSON.stringify({ errors })}`);\n this.name = \"TailorErrors\";\n this.errors = errors;\n }\n };\n}\n\n/**\n * Sets up mocks for `globalThis.tailor.workflow.wait` and `.resolve` used in bundled workflow tests.\n * `triggerJobFunction` is stubbed to throw a helpful error directing to `setupWorkflowMock()`,\n * so mistakenly triggering a job without job mocks produces a clear message instead of silently returning undefined.\n * @deprecated Use `mockWorkflow` from `@tailor-platform/sdk/vitest` with the `tailor-runtime` environment instead.\n * `setWaitHandler` / `setResolveHandler` cover wait/resolve, and `waitCalls` / `resolveCalls` give the same assertion shape.\n * @param config - Optional handlers for wait and resolve calls.\n * @param config.onWait - Handler called when wait is invoked.\n * @param config.onResolve - Handler called when resolve is invoked.\n * @returns Object containing arrays of wait and resolve calls for assertions.\n */\nexport function setupWaitPointMock(config?: { onWait?: WaitHandler; onResolve?: ResolveHandler }): {\n waitCalls: { key: string; payload: unknown }[];\n resolveCalls: { executionId: string; key: string }[];\n} {\n const waitCalls: { key: string; payload: unknown }[] = [];\n const resolveCalls: { executionId: string; key: string }[] = [];\n\n GlobalThis.tailor = {\n ...GlobalThis.tailor,\n workflow: {\n triggerJobFunction: () => {\n throw new Error(\n \"tailor.workflow.triggerJobFunction is not mocked. Use setupWorkflowMock() in tests.\",\n );\n },\n ...GlobalThis.tailor?.workflow,\n wait: (key: string, payload?: unknown) => {\n waitCalls.push({ key, payload });\n return config?.onWait?.(key, payload);\n },\n resolve: async (\n executionId: string,\n key: string,\n callback: (payload: unknown) => unknown,\n ) => {\n resolveCalls.push({ executionId, key });\n await config?.onResolve?.(executionId, key, callback);\n },\n },\n } as typeof GlobalThis.tailor;\n\n return { waitCalls, resolveCalls };\n}\n\n/**\n * Creates a function that imports a bundled JS file and returns its `main` export.\n * Used to test bundled output from `apply --buildOnly`.\n * @param baseDir - Base directory where bundled files are located.\n * @returns An async function that takes a relative path and returns the `main` function.\n * @deprecated This is an SDK-internal testing helper. Bundling integrity is the SDK's responsibility,\n * not the application's — verify your code through unit tests against the TypeScript source and\n * E2E tests against a deployed application instead. This export will be removed in a future release.\n */\nexport function createImportMain(baseDir: string): (relativePath: string) => Promise<MainFunction> {\n return async (relativePath: string): Promise<MainFunction> => {\n const fileUrl = pathToFileURL(path.join(baseDir, relativePath));\n fileUrl.searchParams.set(\"v\", `${Date.now()}-${Math.random()}`);\n const module = await import(fileUrl.href);\n const main = module.main;\n if (typeof main !== \"function\") {\n throw new Error(`Expected \"main\" to be a function in ${relativePath}, got ${typeof main}`);\n }\n return main;\n };\n}\n","import type { output, TailorUser } from \"@/configure\";\nimport type { TailorDBType } from \"@/configure/services/tailordb/schema\";\nimport type { TailorField } from \"@/configure/types/type\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nexport { WORKFLOW_TEST_ENV_KEY } from \"@/configure/services/workflow/job\";\nexport {\n setupTailordbMock,\n setupTailorErrorsMock,\n setupWorkflowMock,\n setupInvokerMock,\n setupWaitPointMock,\n createImportMain,\n} from \"./mock\";\n\n/** Represents an unauthenticated user in the Tailor platform. */\nexport const unauthenticatedTailorUser = {\n id: \"00000000-0000-0000-0000-000000000000\",\n type: \"\",\n workspaceId: \"00000000-0000-0000-0000-000000000000\",\n attributes: null,\n attributeList: [],\n} as const satisfies TailorUser;\n\n/**\n * Creates a hook function that processes TailorDB type fields\n * - Uses existing id from data if provided, otherwise generates UUID for id fields\n * - Recursively processes nested types\n * - Executes hooks.create for fields with create hooks\n * @template T - The output type of the hook function\n * @param type - TailorDB type definition\n * @returns A function that transforms input data according to field hooks\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function createTailorDBHook<T extends TailorDBType<any, any>>(type: T) {\n return (data: unknown) => {\n return Object.entries(type.fields).reduce(\n (hooked, [key, value]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const field = value as TailorField<any, any, any>;\n if (key === \"id\") {\n // Use existing id from data if provided, otherwise generate new UUID\n const existingId =\n data && typeof data === \"object\" ? (data as Record<string, unknown>)[key] : undefined;\n hooked[key] = existingId ?? crypto.randomUUID();\n } else if (field.type === \"nested\") {\n const nestedValue =\n data && typeof data === \"object\" ? (data as Record<string, unknown>)[key] : undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const nestedHook = createTailorDBHook({ fields: field.fields } as any);\n if (field.metadata.array) {\n // For nested array fields, recurse per element and pass through non-array values\n // (e.g. null/undefined for optional fields) so validation sees the original value.\n hooked[key] = Array.isArray(nestedValue)\n ? nestedValue.map((item) => nestedHook(item))\n : nestedValue;\n } else {\n hooked[key] = nestedHook(nestedValue);\n }\n } else if (field.metadata.hooks?.create) {\n hooked[key] = field.metadata.hooks.create({\n value: (data as Record<string, unknown>)[key],\n data: data,\n user: unauthenticatedTailorUser,\n });\n if (hooked[key] instanceof Date) {\n hooked[key] = hooked[key].toISOString();\n }\n } else if (data && typeof data === \"object\") {\n hooked[key] = (data as Record<string, unknown>)[key];\n }\n return hooked;\n },\n {} as Record<string, unknown>,\n ) as Partial<output<T>>;\n };\n}\n\n/**\n * Creates the standard schema definition for lines-db\n * This returns the first argument for defineSchema with the ~standard section\n * @template T - The output type after validation\n * @param schemaType - TailorDB field schema for validation\n * @param hook - Hook function to transform data before validation\n * @returns Schema object with ~standard section for defineSchema\n */\nexport function createStandardSchema<T = Record<string, unknown>>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schemaType: TailorField<any, T>,\n hook: (data: unknown) => Partial<T>,\n) {\n return {\n \"~standard\": {\n version: 1,\n vendor: \"@tailor-platform/sdk\",\n validate: (value: unknown) => {\n const hooked = hook(value);\n const result = schemaType.parse({\n value: hooked,\n data: hooked,\n user: unauthenticatedTailorUser,\n });\n if (result.issues) {\n return result;\n }\n return { value: hooked as T };\n },\n },\n } as const satisfies StandardSchemaV1<T>;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/utils/test/mock.ts","../../../src/utils/test/index.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { ContextInvoker } from \"@/runtime/context\";\nimport type { TailorInvoker } from \"@/types/user\";\n\ntype MainFunction = (args: Record<string, unknown>) => unknown | Promise<unknown>;\ntype QueryResolver = (query: string, params: unknown[]) => unknown[];\ntype JobHandler = (jobName: string, args: unknown) => unknown;\ntype WaitHandler = (key: string, payload: unknown) => unknown;\ntype ResolveHandler = (\n executionId: string,\n key: string,\n callback: (payload: unknown) => unknown,\n) => Promise<void> | void;\n\ninterface TailordbGlobal {\n tailordb?: {\n Client: new (config: { namespace?: string }) => {\n connect(): Promise<void> | void;\n end(): Promise<void> | void;\n queryObject(\n query: string,\n params?: unknown[],\n ): Promise<{ rows: unknown[] }> | { rows: unknown[] };\n };\n };\n tailor?: {\n workflow: {\n triggerJobFunction: (jobName: string, args: unknown) => unknown;\n wait?: (key: string, payload?: unknown) => unknown;\n resolve?: (\n executionId: string,\n key: string,\n callback: (payload: unknown) => unknown,\n ) => Promise<void>;\n };\n context: {\n getInvoker: () => ContextInvoker | null;\n };\n };\n}\n\ninterface TailorErrorItem {\n message: string;\n path: (string | number)[];\n}\n\ninterface TailorErrorsGlobal {\n TailorErrors?: new (errors: TailorErrorItem[]) => Error;\n}\n\nconst GlobalThis = globalThis as TailordbGlobal & TailorErrorsGlobal;\n\n/**\n * Sets up a mock for `globalThis.tailordb.Client` used in bundled resolver/executor tests.\n * @deprecated Use `mockTailordb` from `@tailor-platform/sdk/vitest` with the `tailor-runtime` environment instead.\n * @param resolver - Optional function to resolve query results. Defaults to returning empty arrays.\n * @returns Object containing arrays of executed queries and created clients for assertions.\n */\nexport function setupTailordbMock(resolver: QueryResolver = () => []): {\n executedQueries: { query: string; params: unknown[] }[];\n createdClients: { namespace?: string; ended: boolean }[];\n} {\n const executedQueries: { query: string; params: unknown[] }[] = [];\n const createdClients: { namespace?: string; ended: boolean }[] = [];\n\n class MockTailordbClient {\n private record: { namespace?: string; ended: boolean };\n\n constructor({ namespace }: { namespace?: string }) {\n this.record = { namespace, ended: false };\n createdClients.push(this.record);\n }\n\n async connect(): Promise<void> {\n /* noop */\n }\n\n async end(): Promise<void> {\n this.record.ended = true;\n }\n\n async queryObject(query: string, params: unknown[] = []): Promise<{ rows: unknown[] }> {\n executedQueries.push({ query, params });\n return { rows: resolver(query, params) };\n }\n }\n\n GlobalThis.tailordb = {\n Client: MockTailordbClient,\n } as typeof GlobalThis.tailordb;\n\n return { executedQueries, createdClients };\n}\n\n/**\n * Sets up a mock for `globalThis.tailor.workflow.triggerJobFunction` used in bundled workflow tests.\n * `wait`/`resolve` are stubbed to throw a helpful error directing to `mockWorkflow`,\n * so mistakenly calling wait without wait-point mocks produces a clear message instead of a TypeError.\n * @deprecated Use `mockWorkflow` from `@tailor-platform/sdk/vitest` with the `tailor-runtime` environment instead.\n * @param handler - Function that handles triggered job calls and returns results.\n * @returns Object containing an array of triggered jobs for assertions.\n */\nexport function setupWorkflowMock(handler: JobHandler): {\n triggeredJobs: { jobName: string; args: unknown }[];\n} {\n const triggeredJobs: { jobName: string; args: unknown }[] = [];\n\n GlobalThis.tailor = {\n ...GlobalThis.tailor,\n workflow: {\n wait: () => {\n throw new Error(\n \"tailor.workflow.wait is not mocked. Use mockWorkflow from @tailor-platform/sdk/vitest in tests.\",\n );\n },\n resolve: async () => {\n throw new Error(\n \"tailor.workflow.resolve is not mocked. Use mockWorkflow from @tailor-platform/sdk/vitest in tests.\",\n );\n },\n ...GlobalThis.tailor?.workflow,\n triggerJobFunction: (jobName: string, args: unknown) => {\n triggeredJobs.push({ jobName, args });\n return handler(jobName, args);\n },\n },\n } as typeof GlobalThis.tailor;\n\n return { triggeredJobs };\n}\n\n/**\n * Sets up a mock for `globalThis.tailor.context.getInvoker` used in bundled\n * resolver/executor/workflow tests.\n * @deprecated With the `tailor-runtime` environment from `@tailor-platform/sdk/vitest`, drive the invoker via `vi.spyOn(globalThis.tailor.context, \"getInvoker\").mockReturnValue(...)` for bundled tests, or pass `invoker` directly to `.body()` when unit-testing resolvers/executors/workflow jobs against the TypeScript source.\n * @param invoker - The `TailorInvoker` value to return, or `null` for anonymous.\n */\nexport function setupInvokerMock(invoker: TailorInvoker): void {\n const raw: ContextInvoker | null = invoker\n ? {\n id: invoker.id,\n type: invoker.type,\n workspaceId: invoker.workspaceId,\n attributes: invoker.attributeList as string[],\n attributeMap: invoker.attributes as Record<string, unknown>,\n }\n : null;\n\n GlobalThis.tailor = {\n ...GlobalThis.tailor,\n context: {\n getInvoker: () => raw,\n },\n } as typeof GlobalThis.tailor;\n}\n\n/**\n * Sets up a mock for `globalThis.TailorErrors` used in bundled resolver tests.\n * Mimics the PF runtime's TailorErrors class that serializes errors with the `TailorErrors: ` prefix.\n * @deprecated Use the `tailor-runtime` environment from `@tailor-platform/sdk/vitest` which auto-injects TailorErrors.\n */\nexport function setupTailorErrorsMock(): void {\n GlobalThis.TailorErrors = class TailorErrors extends Error {\n errors: TailorErrorItem[];\n\n constructor(errors: TailorErrorItem[]) {\n super(`TailorErrors: ${JSON.stringify({ errors })}`);\n this.name = \"TailorErrors\";\n this.errors = errors;\n }\n };\n}\n\n/**\n * Sets up mocks for `globalThis.tailor.workflow.wait` and `.resolve` used in bundled workflow tests.\n * `triggerJobFunction` is stubbed to throw a helpful error directing to `setupWorkflowMock()`,\n * so mistakenly triggering a job without job mocks produces a clear message instead of silently returning undefined.\n * @deprecated Use `mockWorkflow` from `@tailor-platform/sdk/vitest` with the `tailor-runtime` environment instead.\n * `setWaitHandler` / `setResolveHandler` cover wait/resolve, and `waitCalls` / `resolveCalls` give the same assertion shape.\n * @param config - Optional handlers for wait and resolve calls.\n * @param config.onWait - Handler called when wait is invoked.\n * @param config.onResolve - Handler called when resolve is invoked.\n * @returns Object containing arrays of wait and resolve calls for assertions.\n */\nexport function setupWaitPointMock(config?: { onWait?: WaitHandler; onResolve?: ResolveHandler }): {\n waitCalls: { key: string; payload: unknown }[];\n resolveCalls: { executionId: string; key: string }[];\n} {\n const waitCalls: { key: string; payload: unknown }[] = [];\n const resolveCalls: { executionId: string; key: string }[] = [];\n\n GlobalThis.tailor = {\n ...GlobalThis.tailor,\n workflow: {\n triggerJobFunction: () => {\n throw new Error(\n \"tailor.workflow.triggerJobFunction is not mocked. Use setupWorkflowMock() in tests.\",\n );\n },\n ...GlobalThis.tailor?.workflow,\n wait: (key: string, payload?: unknown) => {\n waitCalls.push({ key, payload });\n return config?.onWait?.(key, payload);\n },\n resolve: async (\n executionId: string,\n key: string,\n callback: (payload: unknown) => unknown,\n ) => {\n resolveCalls.push({ executionId, key });\n await config?.onResolve?.(executionId, key, callback);\n },\n },\n } as typeof GlobalThis.tailor;\n\n return { waitCalls, resolveCalls };\n}\n\n/**\n * Creates a function that imports a bundled JS file and returns its `main` export.\n * Used to test bundled output from `apply --buildOnly`.\n * @param baseDir - Base directory where bundled files are located.\n * @returns An async function that takes a relative path and returns the `main` function.\n * @deprecated This is an SDK-internal testing helper. Bundling integrity is the SDK's responsibility,\n * not the application's — verify your code through unit tests against the TypeScript source and\n * E2E tests against a deployed application instead. This export will be removed in a future release.\n */\nexport function createImportMain(baseDir: string): (relativePath: string) => Promise<MainFunction> {\n return async (relativePath: string): Promise<MainFunction> => {\n const fileUrl = pathToFileURL(path.join(baseDir, relativePath));\n fileUrl.searchParams.set(\"v\", `${Date.now()}-${Math.random()}`);\n const module = await import(fileUrl.href);\n const main = module.main;\n if (typeof main !== \"function\") {\n throw new Error(`Expected \"main\" to be a function in ${relativePath}, got ${typeof main}`);\n }\n return main;\n };\n}\n","import type { output, TailorUser } from \"@/configure\";\nimport type { TailorDBType } from \"@/configure/services/tailordb/schema\";\nimport type { TailorField } from \"@/configure/types/type\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nexport { WORKFLOW_TEST_ENV_KEY } from \"@/configure/services/workflow/job\";\nexport {\n setupTailordbMock,\n setupTailorErrorsMock,\n setupWorkflowMock,\n setupInvokerMock,\n setupWaitPointMock,\n createImportMain,\n} from \"./mock\";\n\n/** Represents an unauthenticated user in the Tailor platform. */\nexport const unauthenticatedTailorUser = {\n id: \"00000000-0000-0000-0000-000000000000\",\n type: \"\",\n workspaceId: \"00000000-0000-0000-0000-000000000000\",\n attributes: null,\n attributeList: [],\n} as const satisfies TailorUser;\n\n/**\n * Creates a hook function that processes TailorDB type fields\n * - Uses existing id from data if provided, otherwise generates UUID for id fields\n * - Recursively processes nested types\n * - Executes hooks.create for fields with create hooks\n * @template T - The output type of the hook function\n * @param type - TailorDB type definition\n * @returns A function that transforms input data according to field hooks\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function createTailorDBHook<T extends TailorDBType<any, any>>(type: T) {\n return (data: unknown) => {\n return Object.entries(type.fields).reduce(\n (hooked, [key, value]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const field = value as TailorField<any, any, any>;\n if (key === \"id\") {\n // Use existing id from data if provided, otherwise generate new UUID\n const existingId =\n data && typeof data === \"object\" ? (data as Record<string, unknown>)[key] : undefined;\n hooked[key] = existingId ?? crypto.randomUUID();\n } else if (field.type === \"nested\") {\n const nestedValue =\n data && typeof data === \"object\" ? (data as Record<string, unknown>)[key] : undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const nestedHook = createTailorDBHook({ fields: field.fields } as any);\n if (field.metadata.array) {\n // For nested array fields, recurse per element and pass through non-array values\n // (e.g. null/undefined for optional fields) so validation sees the original value.\n hooked[key] = Array.isArray(nestedValue)\n ? nestedValue.map((item) => nestedHook(item))\n : nestedValue;\n } else {\n hooked[key] = nestedHook(nestedValue);\n }\n } else if (field.metadata.hooks?.create) {\n hooked[key] = field.metadata.hooks.create({\n value: (data as Record<string, unknown>)[key],\n data: data,\n user: unauthenticatedTailorUser,\n });\n if (hooked[key] instanceof Date) {\n hooked[key] = hooked[key].toISOString();\n }\n } else if (data && typeof data === \"object\") {\n hooked[key] = (data as Record<string, unknown>)[key];\n }\n return hooked;\n },\n {} as Record<string, unknown>,\n ) as Partial<output<T>>;\n };\n}\n\n/**\n * Creates the standard schema definition for lines-db\n * This returns the first argument for defineSchema with the ~standard section\n * @template T - The output type after validation\n * @param schemaType - TailorDB field schema for validation\n * @param hook - Hook function to transform data before validation\n * @returns Schema object with ~standard section for defineSchema\n */\nexport function createStandardSchema<T = Record<string, unknown>>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schemaType: TailorField<any, T>,\n hook: (data: unknown) => Partial<T>,\n) {\n return {\n \"~standard\": {\n version: 1,\n vendor: \"@tailor-platform/sdk\",\n validate: (value: unknown) => {\n const hooked = hook(value);\n const result = schemaType.parse({\n value: hooked,\n data: hooked,\n user: unauthenticatedTailorUser,\n });\n if (result.issues) {\n return result;\n }\n return { value: hooked as T };\n },\n },\n } as const satisfies StandardSchemaV1<T>;\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
 
2
- import { a as mockSecretmanager, i as mockIdp, n as mockFile, o as mockTailordb, r as mockIconv, s as mockWorkflow, t as mockAuthconnection } from "../mock-Dpu__UeJ.mjs";
2
+ import { t as assertDefined } from "../assert-CKfwrmCV.mjs";
3
+ import { a as mockSecretmanager, i as mockIdp, n as mockFile, o as mockTailordb, r as mockIconv, s as mockWorkflow, t as mockAuthconnection } from "../mock-DMgIygjE.mjs";
3
4
  import { builtinModules } from "node:module";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { dirname, isAbsolute, matchesGlob, relative, resolve } from "node:path";
@@ -117,7 +118,9 @@ const ID_CONT = /^[A-Za-z0-9_$]*$/;
117
118
  function isSafeBindingName(name) {
118
119
  if (UNSAFE_BINDING_NAMES.has(name)) return false;
119
120
  if (name.length === 0) return false;
120
- return ID_START.test(name[0] ?? "") && ID_CONT.test(name.slice(1));
121
+ const firstChar = name[0];
122
+ if (firstChar === void 0) return false;
123
+ return ID_START.test(firstChar) && ID_CONT.test(name.slice(1));
121
124
  }
122
125
  function buildBlockedReplacement(node, message) {
123
126
  const literal = JSON.stringify(message);
@@ -186,7 +189,7 @@ function createBlockPlugin() {
186
189
  patterns: testConfig?.include ?? DEFAULT_TEST_INCLUDE
187
190
  }];
188
191
  for (const project of testConfig?.projects ?? []) {
189
- const projectTest = project?.test;
192
+ const projectTest = project.test;
190
193
  if (!projectTest) continue;
191
194
  const projectRoot = projectTest.root ?? root;
192
195
  for (const f of toAbsolutePaths(projectTest.setupFiles, projectRoot)) exemptHostFiles.add(f);
@@ -316,7 +319,7 @@ function insertRows(node) {
316
319
  function insertValues(node) {
317
320
  const rows = insertRows(node);
318
321
  if (rows.length !== 1) throw new Error(`insertValues: query inserts ${rows.length} rows; use insertRows() for multi-row inserts`);
319
- return rows[0];
322
+ return assertDefined(rows[0], "insertValues: first row missing");
320
323
  }
321
324
  function updateValues(node) {
322
325
  if (!UpdateQueryNode.is(node)) throw new Error(`updateValues: expected UpdateQueryNode, got ${node.kind}`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/vitest/blocked-modules.ts","../../src/vitest/plugin.ts","../../src/vitest/mock-kysely.ts","../../src/vitest/index.ts"],"sourcesContent":["/**\n * Blocked Node.js built-in modules and their Web Standard API alternatives.\n *\n * The Tailor Platform runtime only provides Web Standard APIs.\n * These Node.js modules are not available and should be replaced with\n * the suggested alternatives.\n */\nimport { builtinModules } from \"node:module\";\n\n// Suggestions keyed by bare specifier. Lookup also checks with \"node:\" prefix stripped.\nconst SUGGESTIONS: Record<string, string> = {\n crypto: \"Use the Web Crypto API (globalThis.crypto) instead.\",\n buffer: \"Use Uint8Array or ArrayBuffer instead.\",\n fs: \"File system access is not available in the Tailor Platform runtime.\",\n \"fs/promises\": \"File system access is not available in the Tailor Platform runtime.\",\n path: \"Use URL or URLPattern for path manipulation.\",\n http: \"Use the Fetch API (globalThis.fetch) for HTTP requests instead.\",\n https: \"Use the Fetch API (globalThis.fetch) for HTTPS requests instead.\",\n url: \"Use the URL and URLSearchParams Web APIs instead.\",\n util: \"Use Web Standard APIs instead.\",\n stream: \"Use Web Streams API (ReadableStream, WritableStream, TransformStream) instead.\",\n \"stream/web\": \"Use Web Streams API (ReadableStream, WritableStream, TransformStream) instead.\",\n events: \"Use EventTarget instead.\",\n zlib: \"Use CompressionStream and DecompressionStream Web APIs instead.\",\n querystring: \"Use URLSearchParams instead.\",\n string_decoder: \"Use TextDecoder instead.\",\n};\n\nconst BLOCKED_MODULES = new Set<string>();\nfor (const mod of builtinModules) {\n BLOCKED_MODULES.add(mod);\n BLOCKED_MODULES.add(`node:${mod}`);\n}\n\n/**\n * Check if a module specifier is a blocked Node.js built-in.\n * @param specifier - Module specifier to check (e.g. \"node:crypto\", \"fs\")\n * @returns Whether the specifier is blocked\n */\nexport function isBlockedModule(specifier: string): boolean {\n return BLOCKED_MODULES.has(specifier);\n}\n\n/**\n * Get the error message for a blocked module import.\n * @param specifier - Module specifier that was blocked\n * @returns Error message with optional suggestion for the Web Standard API alternative\n */\nexport function getBlockedMessage(specifier: string): string {\n const bare = specifier.startsWith(\"node:\") ? specifier.slice(5) : specifier;\n const suggestion = SUGGESTIONS[bare];\n const base = `\"${specifier}\" is not available in the Tailor Platform runtime.`;\n return suggestion ? `${base} ${suggestion}` : base;\n}\n","import { dirname, isAbsolute, matchesGlob, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { isBlockedModule, getBlockedMessage } from \"./blocked-modules\";\nimport type { Plugin } from \"vitest/config\";\n\nconst DEFAULT_TEST_INCLUDE = [\"**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}\"];\n\ninterface ExportSpecifierNode {\n type?: string;\n exported?: { name?: unknown } | null;\n}\n\ninterface ImportLikeNode {\n type: string;\n start: number;\n end: number;\n source?: { value?: unknown } | null;\n specifiers?: ExportSpecifierNode[] | null;\n exported?: { name?: unknown } | null;\n}\n\nconst IMPORT_LIKE_TYPES = new Set([\n \"ImportDeclaration\",\n \"ExportNamedDeclaration\",\n \"ExportAllDeclaration\",\n]);\n\n// Re-export specifiers (`export { x as Y } from \"...\"`) accept any\n// `IdentifierName` for `Y` — including reserved words like `delete`. But\n// `export const Y = ...` requires a `BindingIdentifier`, which forbids\n// reserved words and the strict-mode-banned `arguments` / `eval`. Synthesizing\n// `export const delete = ...` would yield a syntax error, so we fall back to\n// plain `throw` for unsafe names.\nconst UNSAFE_BINDING_NAMES = new Set([\n // ReservedWord (ES2022+)\n \"break\",\n \"case\",\n \"catch\",\n \"class\",\n \"const\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"delete\",\n \"do\",\n \"else\",\n \"enum\",\n \"export\",\n \"extends\",\n \"false\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"import\",\n \"in\",\n \"instanceof\",\n \"new\",\n \"null\",\n \"return\",\n \"super\",\n \"switch\",\n \"this\",\n \"throw\",\n \"true\",\n \"try\",\n \"typeof\",\n \"var\",\n \"void\",\n \"while\",\n \"with\",\n \"yield\",\n // Strict-mode reserved (ESM is always strict)\n \"let\",\n \"static\",\n \"implements\",\n \"interface\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n // Module-specific reserved\n \"await\",\n // Banned as binding names in strict mode\n \"arguments\",\n \"eval\",\n]);\n\nconst ID_START = /^[A-Za-z_$]/;\nconst ID_CONT = /^[A-Za-z0-9_$]*$/;\n\nfunction isSafeBindingName(name: string): boolean {\n if (UNSAFE_BINDING_NAMES.has(name)) return false;\n if (name.length === 0) return false;\n // Restrict to ASCII identifiers — Unicode bindings are valid JS but rare\n // for re-exports of node:* modules, and a regex over the full\n // ID_Start/ID_Continue sets adds substantial weight for marginal gain.\n return ID_START.test(name[0] ?? \"\") && ID_CONT.test(name.slice(1));\n}\n\nfunction buildBlockedReplacement(node: ImportLikeNode, message: string): string {\n // JSON.stringify yields a fully-escaped string literal (including the\n // surrounding quotes), so we don't need to manually handle backslashes,\n // newlines, or other control characters that may appear in the message.\n const literal = JSON.stringify(message);\n const throwStmt = `throw new Error(${literal});`;\n const throwExpr = `(() => { throw new Error(${literal}); })()`;\n\n if (node.type === \"ExportNamedDeclaration\") {\n const specs = node.specifiers ?? [];\n const stubs: string[] = [];\n for (const spec of specs) {\n const exportedName = spec.exported?.name;\n if (typeof exportedName !== \"string\") continue;\n if (exportedName === \"default\") {\n stubs.push(`export default ${throwExpr};`);\n continue;\n }\n // Reserved words can be re-export names but not binding names.\n // Bail to a plain throw rather than emit invalid syntax.\n if (!isSafeBindingName(exportedName)) return throwStmt;\n stubs.push(`export const ${exportedName} = ${throwExpr};`);\n }\n return stubs.length > 0 ? stubs.join(\" \") : throwStmt;\n }\n\n if (node.type === \"ExportAllDeclaration\") {\n const exportedName = node.exported?.name;\n if (typeof exportedName === \"string\" && isSafeBindingName(exportedName)) {\n return `export const ${exportedName} = ${throwExpr};`;\n }\n return throwStmt;\n }\n\n return throwStmt;\n}\n\n/**\n * Vite plugin that blocks Node.js built-in module imports from production code.\n *\n * Uses the `transform` hook to walk the Rollup-provided AST of non-test source\n * files for static `node:*` imports and re-exports.\n * `ImportDeclaration` and bare `export * from \"...\"` are replaced with a\n * `throw new Error(...)` statement so the failure surfaces at evaluation time.\n * `ExportNamedDeclaration` (`export { x, y as z } from \"...\"`) and namespaced\n * `export * as ns from \"...\"` are rewritten to per-binding stub exports\n * (`export const x = (() => { throw new Error(...) })();`). The IIFE throws\n * eagerly during module evaluation (same timing as a top-level `throw`), but\n * preserving the declared export bindings ensures the surfaced error is the\n * actual \"node:* not available\" message rather than an opaque\n * \"missing export\" raised by the loader.\n * Vitest treats `node:*` as external SSR modules (skipping `resolveId`), so\n * source-level transformation is the only reliable interception point.\n * Runs in the default phase (no `enforce: \"pre\"`) so esbuild's TypeScript\n * transform strips `import type` first; only runtime imports reach this hook.\n * Node.js globals not in the platform runtime are removed by the environment (whitelist-based).\n * Test file patterns are read from the resolved Vitest config (`test.include`).\n * Vitest setup files (`test.setupFiles`) and global-setup files\n * (`test.globalSetup`) are also exempted: they run in the test runner host,\n * not in the emulated platform runtime, so they may freely use `node:*`\n * modules (e.g. `node:url` for `pathToFileURL`).\n * @returns Vite plugin\n */\nexport function createBlockPlugin(): Plugin {\n let isTestFile: (id: string) => boolean = () => false;\n let isUserSourceFile: (id: string) => boolean = () => false;\n\n return {\n name: \"tailor-runtime-block-node\",\n\n configResolved(config) {\n type HostFileTestConfig = {\n include?: string[];\n setupFiles?: string | string[];\n globalSetup?: string | string[];\n root?: string;\n };\n const testConfig = (\n config as typeof config & {\n test?: HostFileTestConfig & {\n projects?: { test?: HostFileTestConfig }[];\n };\n }\n ).test;\n const root = testConfig?.root ?? config.root;\n // Setup files and global-setup files run in the Vitest host (not the\n // emulated runtime), so they may freely import node:* modules. Collect\n // them from the top-level config AND from each `test.projects[i]` —\n // per-project setup files run in the host too and would otherwise be\n // transformed as production code, breaking node:* imports inside them.\n const toAbsolutePaths = (value: string | string[] | undefined, baseRoot: string) =>\n (Array.isArray(value) ? value : value ? [value] : []).map((f) => resolve(baseRoot, f));\n const exemptHostFiles = new Set<string>([\n ...toAbsolutePaths(testConfig?.setupFiles, root),\n ...toAbsolutePaths(testConfig?.globalSetup, root),\n ]);\n // Vitest projects can each define their own `test.include` (and root).\n // A project that uses non-default patterns (e.g. `tests/**/*.spec.ts`)\n // must also be considered when classifying test files — otherwise its\n // tests would be treated as production code and have node:* imports\n // rewritten. Build a list of (root, patterns) pairs covering top-level\n // + every project, and accept a file if any pair matches.\n const includePairs: { root: string; patterns: string[] }[] = [\n { root, patterns: testConfig?.include ?? DEFAULT_TEST_INCLUDE },\n ];\n for (const project of testConfig?.projects ?? []) {\n const projectTest = project?.test;\n if (!projectTest) continue;\n const projectRoot = projectTest.root ?? root;\n for (const f of toAbsolutePaths(projectTest.setupFiles, projectRoot)) {\n exemptHostFiles.add(f);\n }\n for (const f of toAbsolutePaths(projectTest.globalSetup, projectRoot)) {\n exemptHostFiles.add(f);\n }\n includePairs.push({\n root: projectRoot,\n patterns: projectTest.include ?? DEFAULT_TEST_INCLUDE,\n });\n }\n isTestFile = (id: string) => {\n if (exemptHostFiles.has(id)) return true;\n return includePairs.some(({ root: r, patterns }) => {\n const candidate = isAbsolute(id) ? relative(r, id) : id;\n return patterns.some((pattern) => matchesGlob(candidate, pattern));\n });\n };\n // Only transform files inside the project root. With pnpm workspaces,\n // dependencies are symlinked and Vite resolves them to absolute paths\n // outside `node_modules`, so the substring check alone is insufficient.\n // Non-absolute ids are Vite-internal: virtual modules (`\\0...`,\n // `virtual:...`), bare specifiers, etc. Those are never user source\n // files and must not be parsed/transformed.\n isUserSourceFile = (id: string) => {\n if (!isAbsolute(id)) return false;\n const rel = relative(root, id);\n return rel !== \"\" && !rel.startsWith(\"..\") && !isAbsolute(rel);\n };\n },\n\n transform(code, id) {\n // Vite can pass ids with query/hash suffixes (e.g. `file.ts?import`,\n // `file.ts?v=hash`). Strip them so exact-path lookups (Set membership,\n // glob matching, absolute-path checks) match what callers configured.\n const queryIdx = id.search(/[?#]/);\n const cleanId = queryIdx === -1 ? id : id.slice(0, queryIdx);\n\n if (isTestFile(cleanId)) return undefined;\n if (cleanId.includes(\"node_modules\")) return undefined;\n if (!isUserSourceFile(cleanId)) return undefined;\n\n let ast: { body: ImportLikeNode[] };\n try {\n ast = this.parse(code) as unknown as { body: ImportLikeNode[] };\n } catch {\n // Not parseable as ESM (e.g. JSON, asset). Let other plugins handle it.\n return undefined;\n }\n\n const replacements: { start: number; end: number; replacement: string }[] = [];\n for (const node of ast.body) {\n if (!IMPORT_LIKE_TYPES.has(node.type)) continue;\n const specifier = node.source?.value;\n if (typeof specifier !== \"string\") continue;\n if (isBlockedModule(specifier)) {\n replacements.push({\n start: node.start,\n end: node.end,\n replacement: buildBlockedReplacement(node, getBlockedMessage(specifier)),\n });\n }\n }\n\n if (replacements.length === 0) return undefined;\n\n let transformed = code;\n for (const r of replacements.toSorted((a, b) => b.start - a.start)) {\n transformed = transformed.slice(0, r.start) + r.replacement + transformed.slice(r.end);\n }\n\n return { code: transformed, map: null };\n },\n };\n}\n\nconst ENVIRONMENT_NAME = \"tailor-runtime\";\n\n/**\n * Vite plugin that resolves the tailor-runtime environment and injects setup files.\n *\n * Vitest resolves environments starting with \".\" or \"/\" as file paths.\n * This plugin rewrites `environment: \"tailor-runtime\"` to the absolute path\n * of the bundled environment module, both at the top-level and per-project.\n * It also injects the setup file that removes Vitest-dependent globals\n * (like `performance`) per-test via beforeEach/afterEach hooks.\n * @param options - Optional configuration\n * @param options.config - Path to tailor.config.ts to load SecretManager values into mock\n * @returns Vite plugin\n */\nexport function createEnvironmentPlugin(options?: { config?: string }): Plugin {\n const currentDir = dirname(fileURLToPath(import.meta.url));\n const environmentPath = resolve(currentDir, \"environment.mjs\");\n const setupPath = resolve(currentDir, \"setup.mjs\");\n\n return {\n name: \"tailor-runtime-environment\",\n\n config(config) {\n const testConfig = config.test as\n | (Record<string, unknown> & {\n projects?: Record<string, unknown>[];\n setupFiles?: string | string[];\n })\n | undefined;\n\n // Rewrite environment name to absolute path at top-level\n let usesTailorRuntime = false;\n if (testConfig?.environment === ENVIRONMENT_NAME) {\n testConfig.environment = environmentPath;\n usesTailorRuntime = true;\n }\n\n // Rewrite in each project config\n if (testConfig?.projects) {\n for (const project of testConfig.projects) {\n const projectTest = project.test as Record<string, unknown> | undefined;\n if (projectTest?.environment === ENVIRONMENT_NAME) {\n projectTest.environment = environmentPath;\n usesTailorRuntime = true;\n }\n }\n }\n\n // Pass config path to setup.ts via env var (cross-process compatible).\n // Always clear first, then set only when tailor-runtime is actually\n // selected. This makes the env var deterministic across Vite config\n // reloads (watch mode, programmatic re-init): a stale value from a\n // prior iteration cannot make setup.ts load secrets from an old config.\n // The leading `__` marks this as plugin-private, so deleting any\n // pre-existing value is safe.\n delete process.env.__TAILOR_RUNTIME_CONFIG;\n if (options?.config && usesTailorRuntime) {\n // Resolve against the user-provided Vite root when present (falling\n // back to cwd). Vitest projects with a non-cwd `root` would otherwise\n // resolve a relative options.config against the wrong directory.\n const configRoot = (config.root as string | undefined) ?? process.cwd();\n const configAbsPath = resolve(configRoot, options.config);\n process.env.__TAILOR_RUNTIME_CONFIG = configAbsPath;\n }\n\n // Normalize a user-provided string `setupFiles` into an array so Vite's\n // array-concat merge sees both sides as arrays (the string form would\n // otherwise be replaced rather than concatenated by some merge paths).\n // Vite then concatenates the user's array with our [setupPath].\n if (testConfig && typeof testConfig.setupFiles === \"string\") {\n testConfig.setupFiles = [testConfig.setupFiles];\n }\n\n return {\n test: {\n setupFiles: [setupPath],\n },\n };\n },\n };\n}\n","/**\n * Kysely-layer mock for unit testing.\n *\n * Builds a real Kysely instance backed by a mock driver: queries compile and\n * type-check normally, but execution returns staged rows and records every\n * query for assertions.\n */\n\nimport {\n ColumnNode,\n type CompiledQuery,\n type DatabaseConnection,\n type Dialect,\n type Driver,\n InsertQueryNode,\n Kysely,\n type OperationNode,\n type OperationNodeKind,\n PostgresAdapter,\n PostgresIntrospector,\n PostgresQueryCompiler,\n PrimitiveValueListNode,\n type QueryResult,\n ReferenceNode,\n type Transaction,\n UpdateQueryNode,\n ValueListNode,\n ValueNode,\n ValuesNode,\n} from \"kysely\";\n\nfunction unwrapValue(node: OperationNode): unknown {\n return ValueNode.is(node) ? node.value : node;\n}\n\nfunction insertRows(node: OperationNode): Record<string, unknown>[] {\n if (!InsertQueryNode.is(node)) {\n throw new Error(`insertRows: expected InsertQueryNode, got ${node.kind}`);\n }\n const columns = node.columns;\n const valuesNode = node.values;\n if (columns === undefined || valuesNode === undefined || !ValuesNode.is(valuesNode)) {\n throw new Error(\"insertRows: unsupported insert shape; inspect query.node instead\");\n }\n return valuesNode.values.map((row) => {\n if (!PrimitiveValueListNode.is(row) && !ValueListNode.is(row)) {\n throw new Error(\"insertRows: unsupported insert shape; inspect query.node instead\");\n }\n const values = PrimitiveValueListNode.is(row) ? row.values : row.values.map(unwrapValue);\n const result: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n result[col.column.name] = values[i];\n });\n return result;\n });\n}\n\nfunction insertValues(node: OperationNode): Record<string, unknown> {\n const rows = insertRows(node);\n if (rows.length !== 1) {\n throw new Error(\n `insertValues: query inserts ${rows.length} rows; use insertRows() for multi-row inserts`,\n );\n }\n return rows[0];\n}\n\nfunction updateValues(node: OperationNode): Record<string, unknown> {\n if (!UpdateQueryNode.is(node)) {\n throw new Error(`updateValues: expected UpdateQueryNode, got ${node.kind}`);\n }\n if (node.updates === undefined) {\n throw new Error(\"updateValues: unsupported update shape; inspect query.node instead\");\n }\n const result: Record<string, unknown> = {};\n for (const update of node.updates) {\n const col = update.column;\n const name = ColumnNode.is(col)\n ? col.column.name\n : ReferenceNode.is(col) && ColumnNode.is(col.column)\n ? col.column.column.name\n : undefined;\n if (name === undefined) {\n throw new Error(\"updateValues: unsupported update shape; inspect query.node instead\");\n }\n result[name] = unwrapValue(update.value);\n }\n return result;\n}\n\n/** A single statement executed against the mock, captured in order. */\nexport interface ExecutedQuery {\n /** The Kysely operation node kind, e.g. `\"SelectQueryNode\"`. */\n kind: OperationNodeKind;\n /** The compiled SQL string. */\n sql: string;\n /** The bound parameter values, in positional order. */\n parameters: readonly unknown[];\n /** The compiled Kysely operation node. */\n node: OperationNode;\n /** One `{ column: value }` map per row written by an insert. */\n insertRows: () => Record<string, unknown>[];\n /** The `{ column: value }` map written by a single-row insert. */\n insertValues: () => Record<string, unknown>;\n /** The `{ column: value }` map written by an update's SET clause. */\n updateValues: () => Record<string, unknown>;\n}\n\nfunction toExecutedQuery(compiledQuery: CompiledQuery): ExecutedQuery {\n const node = compiledQuery.query;\n return {\n kind: node.kind,\n sql: compiledQuery.sql,\n parameters: compiledQuery.parameters,\n node,\n insertRows: () => insertRows(node),\n insertValues: () => insertValues(node),\n updateValues: () => updateValues(node),\n };\n}\n\ntype MockRow = Record<string, unknown>;\ntype MockResult = MockRow[] | { rows?: MockRow[]; numAffectedRows?: number | bigint };\ntype QueryResolver = (query: ExecutedQuery) => MockResult | undefined;\n\ninterface StagedResult {\n rows: MockRow[];\n numAffectedRows: bigint | undefined;\n}\n\nfunction toStagedResult(result: MockResult): StagedResult {\n if (Array.isArray(result)) return { rows: result, numAffectedRows: undefined };\n return {\n rows: result.rows ?? [],\n numAffectedRows:\n result.numAffectedRows === undefined ? undefined : BigInt(result.numAffectedRows),\n };\n}\n\n/** Controls and assertions for a {@link createKyselyMock} instance. */\nexport interface KyselyMock<DB> {\n /** The mock Kysely instance to run queries against. */\n db: Kysely<DB>;\n /** Every recorded query, in execution order. */\n executedQueries: ExecutedQuery[];\n /** Recorded SELECT queries. */\n selects: ExecutedQuery[];\n /** Recorded INSERT queries. */\n inserts: ExecutedQuery[];\n /** Recorded UPDATE queries. */\n updates: ExecutedQuery[];\n /** Recorded DELETE queries. */\n deletes: ExecutedQuery[];\n /** Stage the rows the next query returns. */\n enqueueResult: (result: MockResult) => void;\n /** Stage the rows for several upcoming queries, consumed in order. */\n enqueueResults: (...results: MockResult[]) => void;\n /** Set a resolver that returns rows by inspecting each query. */\n setQueryResolver: (resolver: QueryResolver) => void;\n /** Run `fn` inside a real transaction and return its result. */\n withTx: <R>(fn: (trx: Transaction<DB>) => Promise<R>) => Promise<R>;\n /** Clear recorded queries and staged results. */\n reset: () => void;\n /** Same as {@link KyselyMock.reset}; enables `using` disposal. */\n [Symbol.dispose]: () => void;\n}\n\nclass MockState {\n readonly executed: ExecutedQuery[] = [];\n private readonly queue: MockResult[] = [];\n private resolver: QueryResolver | undefined;\n\n enqueue(...results: MockResult[]): void {\n this.queue.push(...results);\n }\n\n setResolver(resolver: QueryResolver): void {\n this.resolver = resolver;\n }\n\n next(query: ExecutedQuery): StagedResult {\n const resolved = this.resolver?.(query);\n if (resolved !== undefined) return toStagedResult(resolved);\n const queued = this.queue.shift();\n return queued === undefined ? { rows: [], numAffectedRows: undefined } : toStagedResult(queued);\n }\n\n reset(): void {\n this.executed.length = 0;\n this.queue.length = 0;\n this.resolver = undefined;\n }\n}\n\nclass MockConnection implements DatabaseConnection {\n constructor(private readonly state: MockState) {}\n\n async executeQuery<R>(compiledQuery: CompiledQuery): Promise<QueryResult<R>> {\n const query = toExecutedQuery(compiledQuery);\n this.state.executed.push(query);\n const { rows, numAffectedRows } = this.state.next(query);\n return {\n rows: rows as R[],\n numAffectedRows: numAffectedRows ?? BigInt(rows.length),\n };\n }\n\n streamQuery<R>(): AsyncIterableIterator<QueryResult<R>> {\n throw new Error(\"createKyselyMock: streaming is not supported\");\n }\n}\n\nclass MockDriver implements Driver {\n constructor(private readonly state: MockState) {}\n\n async init(): Promise<void> {}\n\n async acquireConnection(): Promise<DatabaseConnection> {\n return new MockConnection(this.state);\n }\n\n // No-ops so begin/commit/rollback never enter `executed` and pollute counts.\n async beginTransaction(): Promise<void> {}\n async commitTransaction(): Promise<void> {}\n async rollbackTransaction(): Promise<void> {}\n\n async releaseConnection(): Promise<void> {}\n async destroy(): Promise<void> {}\n}\n\nfunction byKind(state: MockState, kind: OperationNodeKind): ExecutedQuery[] {\n return state.executed.filter((query) => query.kind === kind);\n}\n\n/**\n * Create a mock Kysely instance for unit-testing code that runs Kysely queries.\n * Pass the namespace schema as the type argument, e.g.\n * `createKyselyMock<Namespace[\"main-db\"]>()`.\n * @returns A {@link KyselyMock} with the mock `db`, recorded queries, and result staging.\n */\nexport function createKyselyMock<DB = Record<string, never>>(): KyselyMock<DB> {\n const state = new MockState();\n const dialect: Dialect = {\n createDriver: () => new MockDriver(state),\n createQueryCompiler: () => new PostgresQueryCompiler(),\n createAdapter: () => new PostgresAdapter(),\n createIntrospector: (db) => new PostgresIntrospector(db),\n };\n const kysely = new Kysely<DB>({ dialect });\n\n return {\n db: kysely,\n get executedQueries() {\n return state.executed;\n },\n get selects() {\n return byKind(state, \"SelectQueryNode\");\n },\n get inserts() {\n return byKind(state, \"InsertQueryNode\");\n },\n get updates() {\n return byKind(state, \"UpdateQueryNode\");\n },\n get deletes() {\n return byKind(state, \"DeleteQueryNode\");\n },\n enqueueResult: (result) => state.enqueue(result),\n enqueueResults: (...results) => state.enqueue(...results),\n setQueryResolver: (resolver) => state.setResolver(resolver),\n withTx: (fn) => kysely.transaction().execute(fn),\n reset: () => state.reset(),\n [Symbol.dispose]: () => state.reset(),\n };\n}\n","import { createBlockPlugin, createEnvironmentPlugin } from \"./plugin\";\nimport type { Plugin } from \"vitest/config\";\n\n/**\n * Creates Vitest plugins that emulate the Tailor Platform function runtime environment.\n *\n * **Beta:** This API may change in future releases.\n *\n * ## What it does\n *\n * 1. **Node.js module blocking** (transform hook) — Imports of `node:*` modules\n * (and bare builtins like `crypto`, `fs`) in non-test source files are replaced\n * with code that throws an error with a suggestion for the Web Standard API alternative.\n * Test files are exempt and can use `node:*` freely. Test file patterns are read\n * from the resolved Vitest config (`test.include`).\n *\n * 2. **Node.js globals removal** (environment + setup) — Only globals available in the\n * Tailor Platform runtime are kept (whitelist: ECMAScript standard, Web Standard APIs\n * from bootstrap.js, platform mocks). All others (`Buffer`, `global`, `setImmediate`,\n * `__dirname`, `__filename`, etc.) are removed. `performance` is removed per-test\n * via beforeEach/afterEach since Vitest needs it during initialization.\n *\n * 3. **Platform API mocks** (environment) — All platform APIs are auto-injected with\n * control objects: `mockTailordb`, `mockWorkflow`, `mockSecretmanager`,\n * `mockAuthconnection`, `mockIdp`, `mockFile`, `mockIconv`. Each\n * provides response configuration, call recording, and reset.\n *\n * 4. **Environment resolution** — Rewrites `environment: \"tailor-runtime\"` to the\n * absolute path of the bundled environment module via the config hook.\n *\n * ## Known limitations\n *\n * - **`process`** and **`require`** are NOT removed or blocked. Vitest's internal\n * runner depends on them. On the real Tailor Platform runtime, they do not exist.\n * - **Dynamic `import()`** of bundled files (via `createImportMain()`) bypasses\n * the transform hook since those files are loaded through Node.js native loader.\n * ## Options\n *\n * - **`config`** — Path to `tailor.config.ts`. Loads `defineSecretManager()` values\n * into `mockSecretmanager` so `getSecret()` returns the configured values.\n * @example\n * ```typescript\n * // vitest.config.ts\n * import { defineConfig } from \"vitest/config\";\n * import { tailorRuntime } from \"@tailor-platform/sdk/vitest\";\n *\n * export default defineConfig({\n * plugins: [tailorRuntime({ config: \"./tailor.config.ts\" })],\n * test: {\n * environment: \"tailor-runtime\",\n * },\n * });\n * ```\n * @param options - Optional configuration\n * @param options.config - Path to tailor.config.ts to load SecretManager values into mock\n * @returns Array of Vite plugins\n */\nexport function tailorRuntime(options?: { config?: string }): Plugin[] {\n return [createBlockPlugin(), createEnvironmentPlugin(options)];\n}\n\nexport {\n mockTailordb,\n mockWorkflow,\n mockSecretmanager,\n mockAuthconnection,\n mockIdp,\n mockFile,\n mockIconv,\n} from \"./mock\";\n\nexport { createKyselyMock, type KyselyMock, type ExecutedQuery } from \"./mock-kysely\";\n"],"mappings":";;;;;;;;;;;;;;;AAUA,MAAM,cAAsC;CAC1C,QAAQ;CACR,QAAQ;CACR,IAAI;CACJ,eAAe;CACf,MAAM;CACN,MAAM;CACN,OAAO;CACP,KAAK;CACL,MAAM;CACN,QAAQ;CACR,cAAc;CACd,QAAQ;CACR,MAAM;CACN,aAAa;CACb,gBAAgB;AAClB;AAEA,MAAM,kCAAkB,IAAI,IAAY;AACxC,KAAK,MAAM,OAAO,gBAAgB;CAChC,gBAAgB,IAAI,GAAG;CACvB,gBAAgB,IAAI,QAAQ,KAAK;AACnC;;;;;;AAOA,SAAgB,gBAAgB,WAA4B;CAC1D,OAAO,gBAAgB,IAAI,SAAS;AACtC;;;;;;AAOA,SAAgB,kBAAkB,WAA2B;CAE3D,MAAM,aAAa,YADN,UAAU,WAAW,OAAO,IAAI,UAAU,MAAM,CAAC,IAAI;CAElE,MAAM,OAAO,IAAI,UAAU;CAC3B,OAAO,aAAa,GAAG,KAAK,GAAG,eAAe;AAChD;;;;AChDA,MAAM,uBAAuB,CAAC,kDAAkD;AAgBhF,MAAM,oBAAoB,IAAI,IAAI;CAChC;CACA;CACA;AACF,CAAC;AAQD,MAAM,uBAAuB,IAAI,IAAI;CAEnC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CAEA;CACA;AACF,CAAC;AAED,MAAM,WAAW;AACjB,MAAM,UAAU;AAEhB,SAAS,kBAAkB,MAAuB;CAChD,IAAI,qBAAqB,IAAI,IAAI,GAAG,OAAO;CAC3C,IAAI,KAAK,WAAW,GAAG,OAAO;CAI9B,OAAO,SAAS,KAAK,KAAK,MAAM,EAAE,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC;AACnE;AAEA,SAAS,wBAAwB,MAAsB,SAAyB;CAI9E,MAAM,UAAU,KAAK,UAAU,OAAO;CACtC,MAAM,YAAY,mBAAmB,QAAQ;CAC7C,MAAM,YAAY,4BAA4B,QAAQ;CAEtD,IAAI,KAAK,SAAS,0BAA0B;EAC1C,MAAM,QAAQ,KAAK,cAAc,CAAC;EAClC,MAAM,QAAkB,CAAC;EACzB,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,eAAe,KAAK,UAAU;GACpC,IAAI,OAAO,iBAAiB,UAAU;GACtC,IAAI,iBAAiB,WAAW;IAC9B,MAAM,KAAK,kBAAkB,UAAU,EAAE;IACzC;GACF;GAGA,IAAI,CAAC,kBAAkB,YAAY,GAAG,OAAO;GAC7C,MAAM,KAAK,gBAAgB,aAAa,KAAK,UAAU,EAAE;EAC3D;EACA,OAAO,MAAM,SAAS,IAAI,MAAM,KAAK,GAAG,IAAI;CAC9C;CAEA,IAAI,KAAK,SAAS,wBAAwB;EACxC,MAAM,eAAe,KAAK,UAAU;EACpC,IAAI,OAAO,iBAAiB,YAAY,kBAAkB,YAAY,GACpE,OAAO,gBAAgB,aAAa,KAAK,UAAU;EAErD,OAAO;CACT;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,oBAA4B;CAC1C,IAAI,mBAA4C;CAChD,IAAI,yBAAkD;CAEtD,OAAO;EACL,MAAM;EAEN,eAAe,QAAQ;GAOrB,MAAM,aACJ,OAKA;GACF,MAAM,OAAO,YAAY,QAAQ,OAAO;GAMxC,MAAM,mBAAmB,OAAsC,cAC5D,MAAM,QAAQ,KAAK,IAAI,QAAQ,QAAQ,CAAC,KAAK,IAAI,CAAC,EAAC,CAAE,KAAK,MAAM,QAAQ,UAAU,CAAC,CAAC;GACvF,MAAM,kBAAkB,IAAI,IAAY,CACtC,GAAG,gBAAgB,YAAY,YAAY,IAAI,GAC/C,GAAG,gBAAgB,YAAY,aAAa,IAAI,CAClD,CAAC;GAOD,MAAM,eAAuD,CAC3D;IAAE;IAAM,UAAU,YAAY,WAAW;GAAqB,CAChE;GACA,KAAK,MAAM,WAAW,YAAY,YAAY,CAAC,GAAG;IAChD,MAAM,cAAc,SAAS;IAC7B,IAAI,CAAC,aAAa;IAClB,MAAM,cAAc,YAAY,QAAQ;IACxC,KAAK,MAAM,KAAK,gBAAgB,YAAY,YAAY,WAAW,GACjE,gBAAgB,IAAI,CAAC;IAEvB,KAAK,MAAM,KAAK,gBAAgB,YAAY,aAAa,WAAW,GAClE,gBAAgB,IAAI,CAAC;IAEvB,aAAa,KAAK;KAChB,MAAM;KACN,UAAU,YAAY,WAAW;IACnC,CAAC;GACH;GACA,cAAc,OAAe;IAC3B,IAAI,gBAAgB,IAAI,EAAE,GAAG,OAAO;IACpC,OAAO,aAAa,MAAM,EAAE,MAAM,GAAG,eAAe;KAClD,MAAM,YAAY,WAAW,EAAE,IAAI,SAAS,GAAG,EAAE,IAAI;KACrD,OAAO,SAAS,MAAM,YAAY,YAAY,WAAW,OAAO,CAAC;IACnE,CAAC;GACH;GAOA,oBAAoB,OAAe;IACjC,IAAI,CAAC,WAAW,EAAE,GAAG,OAAO;IAC5B,MAAM,MAAM,SAAS,MAAM,EAAE;IAC7B,OAAO,QAAQ,MAAM,CAAC,IAAI,WAAW,IAAI,KAAK,CAAC,WAAW,GAAG;GAC/D;EACF;EAEA,UAAU,MAAM,IAAI;GAIlB,MAAM,WAAW,GAAG,OAAO,MAAM;GACjC,MAAM,UAAU,aAAa,KAAK,KAAK,GAAG,MAAM,GAAG,QAAQ;GAE3D,IAAI,WAAW,OAAO,GAAG,OAAO;GAChC,IAAI,QAAQ,SAAS,cAAc,GAAG,OAAO;GAC7C,IAAI,CAAC,iBAAiB,OAAO,GAAG,OAAO;GAEvC,IAAI;GACJ,IAAI;IACF,MAAM,KAAK,MAAM,IAAI;GACvB,QAAQ;IAEN;GACF;GAEA,MAAM,eAAsE,CAAC;GAC7E,KAAK,MAAM,QAAQ,IAAI,MAAM;IAC3B,IAAI,CAAC,kBAAkB,IAAI,KAAK,IAAI,GAAG;IACvC,MAAM,YAAY,KAAK,QAAQ;IAC/B,IAAI,OAAO,cAAc,UAAU;IACnC,IAAI,gBAAgB,SAAS,GAC3B,aAAa,KAAK;KAChB,OAAO,KAAK;KACZ,KAAK,KAAK;KACV,aAAa,wBAAwB,MAAM,kBAAkB,SAAS,CAAC;IACzE,CAAC;GAEL;GAEA,IAAI,aAAa,WAAW,GAAG,OAAO;GAEtC,IAAI,cAAc;GAClB,KAAK,MAAM,KAAK,aAAa,UAAU,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,GAC/D,cAAc,YAAY,MAAM,GAAG,EAAE,KAAK,IAAI,EAAE,cAAc,YAAY,MAAM,EAAE,GAAG;GAGvF,OAAO;IAAE,MAAM;IAAa,KAAK;GAAK;EACxC;CACF;AACF;AAEA,MAAM,mBAAmB;;;;;;;;;;;;;AAczB,SAAgB,wBAAwB,SAAuC;CAC7E,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;CACzD,MAAM,kBAAkB,QAAQ,YAAY,iBAAiB;CAC7D,MAAM,YAAY,QAAQ,YAAY,WAAW;CAEjD,OAAO;EACL,MAAM;EAEN,OAAO,QAAQ;GACb,MAAM,aAAa,OAAO;GAQ1B,IAAI,oBAAoB;GACxB,IAAI,YAAY,gBAAgB,kBAAkB;IAChD,WAAW,cAAc;IACzB,oBAAoB;GACtB;GAGA,IAAI,YAAY,UACd,KAAK,MAAM,WAAW,WAAW,UAAU;IACzC,MAAM,cAAc,QAAQ;IAC5B,IAAI,aAAa,gBAAgB,kBAAkB;KACjD,YAAY,cAAc;KAC1B,oBAAoB;IACtB;GACF;GAUF,OAAO,QAAQ,IAAI;GACnB,IAAI,SAAS,UAAU,mBAAmB;IAKxC,MAAM,gBAAgB,QADF,OAAO,QAA+B,QAAQ,IAAI,GAC5B,QAAQ,MAAM;IACxD,QAAQ,IAAI,0BAA0B;GACxC;GAMA,IAAI,cAAc,OAAO,WAAW,eAAe,UACjD,WAAW,aAAa,CAAC,WAAW,UAAU;GAGhD,OAAO,EACL,MAAM,EACJ,YAAY,CAAC,SAAS,EACxB,EACF;EACF;CACF;AACF;;;;;;;;;;;AC9UA,SAAS,YAAY,MAA8B;CACjD,OAAO,UAAU,GAAG,IAAI,IAAI,KAAK,QAAQ;AAC3C;AAEA,SAAS,WAAW,MAAgD;CAClE,IAAI,CAAC,gBAAgB,GAAG,IAAI,GAC1B,MAAM,IAAI,MAAM,6CAA6C,KAAK,MAAM;CAE1E,MAAM,UAAU,KAAK;CACrB,MAAM,aAAa,KAAK;CACxB,IAAI,YAAY,UAAa,eAAe,UAAa,CAAC,WAAW,GAAG,UAAU,GAChF,MAAM,IAAI,MAAM,kEAAkE;CAEpF,OAAO,WAAW,OAAO,KAAK,QAAQ;EACpC,IAAI,CAAC,uBAAuB,GAAG,GAAG,KAAK,CAAC,cAAc,GAAG,GAAG,GAC1D,MAAM,IAAI,MAAM,kEAAkE;EAEpF,MAAM,SAAS,uBAAuB,GAAG,GAAG,IAAI,IAAI,SAAS,IAAI,OAAO,IAAI,WAAW;EACvF,MAAM,SAAkC,CAAC;EACzC,QAAQ,SAAS,KAAK,MAAM;GAC1B,OAAO,IAAI,OAAO,QAAQ,OAAO;EACnC,CAAC;EACD,OAAO;CACT,CAAC;AACH;AAEA,SAAS,aAAa,MAA8C;CAClE,MAAM,OAAO,WAAW,IAAI;CAC5B,IAAI,KAAK,WAAW,GAClB,MAAM,IAAI,MACR,+BAA+B,KAAK,OAAO,8CAC7C;CAEF,OAAO,KAAK;AACd;AAEA,SAAS,aAAa,MAA8C;CAClE,IAAI,CAAC,gBAAgB,GAAG,IAAI,GAC1B,MAAM,IAAI,MAAM,+CAA+C,KAAK,MAAM;CAE5E,IAAI,KAAK,YAAY,QACnB,MAAM,IAAI,MAAM,oEAAoE;CAEtF,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,UAAU,KAAK,SAAS;EACjC,MAAM,MAAM,OAAO;EACnB,MAAM,OAAO,WAAW,GAAG,GAAG,IAC1B,IAAI,OAAO,OACX,cAAc,GAAG,GAAG,KAAK,WAAW,GAAG,IAAI,MAAM,IAC/C,IAAI,OAAO,OAAO,OAClB;EACN,IAAI,SAAS,QACX,MAAM,IAAI,MAAM,oEAAoE;EAEtF,OAAO,QAAQ,YAAY,OAAO,KAAK;CACzC;CACA,OAAO;AACT;AAoBA,SAAS,gBAAgB,eAA6C;CACpE,MAAM,OAAO,cAAc;CAC3B,OAAO;EACL,MAAM,KAAK;EACX,KAAK,cAAc;EACnB,YAAY,cAAc;EAC1B;EACA,kBAAkB,WAAW,IAAI;EACjC,oBAAoB,aAAa,IAAI;EACrC,oBAAoB,aAAa,IAAI;CACvC;AACF;AAWA,SAAS,eAAe,QAAkC;CACxD,IAAI,MAAM,QAAQ,MAAM,GAAG,OAAO;EAAE,MAAM;EAAQ,iBAAiB;CAAU;CAC7E,OAAO;EACL,MAAM,OAAO,QAAQ,CAAC;EACtB,iBACE,OAAO,oBAAoB,SAAY,SAAY,OAAO,OAAO,eAAe;CACpF;AACF;AA8BA,IAAM,YAAN,MAAgB;CACd,AAAS,WAA4B,CAAC;CACtC,AAAiB,QAAsB,CAAC;CACxC,AAAQ;CAER,QAAQ,GAAG,SAA6B;EACtC,KAAK,MAAM,KAAK,GAAG,OAAO;CAC5B;CAEA,YAAY,UAA+B;EACzC,KAAK,WAAW;CAClB;CAEA,KAAK,OAAoC;EACvC,MAAM,WAAW,KAAK,WAAW,KAAK;EACtC,IAAI,aAAa,QAAW,OAAO,eAAe,QAAQ;EAC1D,MAAM,SAAS,KAAK,MAAM,MAAM;EAChC,OAAO,WAAW,SAAY;GAAE,MAAM,CAAC;GAAG,iBAAiB;EAAU,IAAI,eAAe,MAAM;CAChG;CAEA,QAAc;EACZ,KAAK,SAAS,SAAS;EACvB,KAAK,MAAM,SAAS;EACpB,KAAK,WAAW;CAClB;AACF;AAEA,IAAM,iBAAN,MAAmD;CACpB;CAA7B,YAAY,AAAiB,OAAkB;EAAlB;CAAmB;CAEhD,MAAM,aAAgB,eAAuD;EAC3E,MAAM,QAAQ,gBAAgB,aAAa;EAC3C,KAAK,MAAM,SAAS,KAAK,KAAK;EAC9B,MAAM,EAAE,MAAM,oBAAoB,KAAK,MAAM,KAAK,KAAK;EACvD,OAAO;GACC;GACN,iBAAiB,mBAAmB,OAAO,KAAK,MAAM;EACxD;CACF;CAEA,cAAwD;EACtD,MAAM,IAAI,MAAM,8CAA8C;CAChE;AACF;AAEA,IAAM,aAAN,MAAmC;CACJ;CAA7B,YAAY,AAAiB,OAAkB;EAAlB;CAAmB;CAEhD,MAAM,OAAsB,CAAC;CAE7B,MAAM,oBAAiD;EACrD,OAAO,IAAI,eAAe,KAAK,KAAK;CACtC;CAGA,MAAM,mBAAkC,CAAC;CACzC,MAAM,oBAAmC,CAAC;CAC1C,MAAM,sBAAqC,CAAC;CAE5C,MAAM,oBAAmC,CAAC;CAC1C,MAAM,UAAyB,CAAC;AAClC;AAEA,SAAS,OAAO,OAAkB,MAA0C;CAC1E,OAAO,MAAM,SAAS,QAAQ,UAAU,MAAM,SAAS,IAAI;AAC7D;;;;;;;AAQA,SAAgB,mBAA+D;CAC7E,MAAM,QAAQ,IAAI,UAAU;CAO5B,MAAM,SAAS,IAAI,OAAW,EAAE;EAL9B,oBAAoB,IAAI,WAAW,KAAK;EACxC,2BAA2B,IAAI,sBAAsB;EACrD,qBAAqB,IAAI,gBAAgB;EACzC,qBAAqB,OAAO,IAAI,qBAAqB,EAAE;CAEnB,EAAE,CAAC;CAEzC,OAAO;EACL,IAAI;EACJ,IAAI,kBAAkB;GACpB,OAAO,MAAM;EACf;EACA,IAAI,UAAU;GACZ,OAAO,OAAO,OAAO,iBAAiB;EACxC;EACA,IAAI,UAAU;GACZ,OAAO,OAAO,OAAO,iBAAiB;EACxC;EACA,IAAI,UAAU;GACZ,OAAO,OAAO,OAAO,iBAAiB;EACxC;EACA,IAAI,UAAU;GACZ,OAAO,OAAO,OAAO,iBAAiB;EACxC;EACA,gBAAgB,WAAW,MAAM,QAAQ,MAAM;EAC/C,iBAAiB,GAAG,YAAY,MAAM,QAAQ,GAAG,OAAO;EACxD,mBAAmB,aAAa,MAAM,YAAY,QAAQ;EAC1D,SAAS,OAAO,OAAO,YAAY,CAAC,CAAC,QAAQ,EAAE;EAC/C,aAAa,MAAM,MAAM;GACxB,OAAO,gBAAgB,MAAM,MAAM;CACtC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzNA,SAAgB,cAAc,SAAyC;CACrE,OAAO,CAAC,kBAAkB,GAAG,wBAAwB,OAAO,CAAC;AAC/D"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/vitest/blocked-modules.ts","../../src/vitest/plugin.ts","../../src/vitest/mock-kysely.ts","../../src/vitest/index.ts"],"sourcesContent":["/**\n * Blocked Node.js built-in modules and their Web Standard API alternatives.\n *\n * The Tailor Platform runtime only provides Web Standard APIs.\n * These Node.js modules are not available and should be replaced with\n * the suggested alternatives.\n */\nimport { builtinModules } from \"node:module\";\n\n// Suggestions keyed by bare specifier. Lookup also checks with \"node:\" prefix stripped.\nconst SUGGESTIONS: Record<string, string> = {\n crypto: \"Use the Web Crypto API (globalThis.crypto) instead.\",\n buffer: \"Use Uint8Array or ArrayBuffer instead.\",\n fs: \"File system access is not available in the Tailor Platform runtime.\",\n \"fs/promises\": \"File system access is not available in the Tailor Platform runtime.\",\n path: \"Use URL or URLPattern for path manipulation.\",\n http: \"Use the Fetch API (globalThis.fetch) for HTTP requests instead.\",\n https: \"Use the Fetch API (globalThis.fetch) for HTTPS requests instead.\",\n url: \"Use the URL and URLSearchParams Web APIs instead.\",\n util: \"Use Web Standard APIs instead.\",\n stream: \"Use Web Streams API (ReadableStream, WritableStream, TransformStream) instead.\",\n \"stream/web\": \"Use Web Streams API (ReadableStream, WritableStream, TransformStream) instead.\",\n events: \"Use EventTarget instead.\",\n zlib: \"Use CompressionStream and DecompressionStream Web APIs instead.\",\n querystring: \"Use URLSearchParams instead.\",\n string_decoder: \"Use TextDecoder instead.\",\n};\n\nconst BLOCKED_MODULES = new Set<string>();\nfor (const mod of builtinModules) {\n BLOCKED_MODULES.add(mod);\n BLOCKED_MODULES.add(`node:${mod}`);\n}\n\n/**\n * Check if a module specifier is a blocked Node.js built-in.\n * @param specifier - Module specifier to check (e.g. \"node:crypto\", \"fs\")\n * @returns Whether the specifier is blocked\n */\nexport function isBlockedModule(specifier: string): boolean {\n return BLOCKED_MODULES.has(specifier);\n}\n\n/**\n * Get the error message for a blocked module import.\n * @param specifier - Module specifier that was blocked\n * @returns Error message with optional suggestion for the Web Standard API alternative\n */\nexport function getBlockedMessage(specifier: string): string {\n const bare = specifier.startsWith(\"node:\") ? specifier.slice(5) : specifier;\n const suggestion = SUGGESTIONS[bare];\n const base = `\"${specifier}\" is not available in the Tailor Platform runtime.`;\n return suggestion ? `${base} ${suggestion}` : base;\n}\n","import { dirname, isAbsolute, matchesGlob, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { isBlockedModule, getBlockedMessage } from \"./blocked-modules\";\nimport type { Plugin } from \"vitest/config\";\n\nconst DEFAULT_TEST_INCLUDE = [\"**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}\"];\n\ninterface ExportSpecifierNode {\n type?: string;\n exported?: { name?: unknown } | null;\n}\n\ninterface ImportLikeNode {\n type: string;\n start: number;\n end: number;\n source?: { value?: unknown } | null;\n specifiers?: ExportSpecifierNode[] | null;\n exported?: { name?: unknown } | null;\n}\n\nconst IMPORT_LIKE_TYPES = new Set([\n \"ImportDeclaration\",\n \"ExportNamedDeclaration\",\n \"ExportAllDeclaration\",\n]);\n\n// Re-export specifiers (`export { x as Y } from \"...\"`) accept any\n// `IdentifierName` for `Y` — including reserved words like `delete`. But\n// `export const Y = ...` requires a `BindingIdentifier`, which forbids\n// reserved words and the strict-mode-banned `arguments` / `eval`. Synthesizing\n// `export const delete = ...` would yield a syntax error, so we fall back to\n// plain `throw` for unsafe names.\nconst UNSAFE_BINDING_NAMES = new Set([\n // ReservedWord (ES2022+)\n \"break\",\n \"case\",\n \"catch\",\n \"class\",\n \"const\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"delete\",\n \"do\",\n \"else\",\n \"enum\",\n \"export\",\n \"extends\",\n \"false\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"import\",\n \"in\",\n \"instanceof\",\n \"new\",\n \"null\",\n \"return\",\n \"super\",\n \"switch\",\n \"this\",\n \"throw\",\n \"true\",\n \"try\",\n \"typeof\",\n \"var\",\n \"void\",\n \"while\",\n \"with\",\n \"yield\",\n // Strict-mode reserved (ESM is always strict)\n \"let\",\n \"static\",\n \"implements\",\n \"interface\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n // Module-specific reserved\n \"await\",\n // Banned as binding names in strict mode\n \"arguments\",\n \"eval\",\n]);\n\nconst ID_START = /^[A-Za-z_$]/;\nconst ID_CONT = /^[A-Za-z0-9_$]*$/;\n\nfunction isSafeBindingName(name: string): boolean {\n if (UNSAFE_BINDING_NAMES.has(name)) return false;\n if (name.length === 0) return false;\n // Restrict to ASCII identifiers — Unicode bindings are valid JS but rare\n // for re-exports of node:* modules, and a regex over the full\n // ID_Start/ID_Continue sets adds substantial weight for marginal gain.\n const firstChar = name[0];\n if (firstChar === undefined) return false;\n return ID_START.test(firstChar) && ID_CONT.test(name.slice(1));\n}\n\nfunction buildBlockedReplacement(node: ImportLikeNode, message: string): string {\n // JSON.stringify yields a fully-escaped string literal (including the\n // surrounding quotes), so we don't need to manually handle backslashes,\n // newlines, or other control characters that may appear in the message.\n const literal = JSON.stringify(message);\n const throwStmt = `throw new Error(${literal});`;\n const throwExpr = `(() => { throw new Error(${literal}); })()`;\n\n if (node.type === \"ExportNamedDeclaration\") {\n const specs = node.specifiers ?? [];\n const stubs: string[] = [];\n for (const spec of specs) {\n const exportedName = spec.exported?.name;\n if (typeof exportedName !== \"string\") continue;\n if (exportedName === \"default\") {\n stubs.push(`export default ${throwExpr};`);\n continue;\n }\n // Reserved words can be re-export names but not binding names.\n // Bail to a plain throw rather than emit invalid syntax.\n if (!isSafeBindingName(exportedName)) return throwStmt;\n stubs.push(`export const ${exportedName} = ${throwExpr};`);\n }\n return stubs.length > 0 ? stubs.join(\" \") : throwStmt;\n }\n\n if (node.type === \"ExportAllDeclaration\") {\n const exportedName = node.exported?.name;\n if (typeof exportedName === \"string\" && isSafeBindingName(exportedName)) {\n return `export const ${exportedName} = ${throwExpr};`;\n }\n return throwStmt;\n }\n\n return throwStmt;\n}\n\n/**\n * Vite plugin that blocks Node.js built-in module imports from production code.\n *\n * Uses the `transform` hook to walk the Rollup-provided AST of non-test source\n * files for static `node:*` imports and re-exports.\n * `ImportDeclaration` and bare `export * from \"...\"` are replaced with a\n * `throw new Error(...)` statement so the failure surfaces at evaluation time.\n * `ExportNamedDeclaration` (`export { x, y as z } from \"...\"`) and namespaced\n * `export * as ns from \"...\"` are rewritten to per-binding stub exports\n * (`export const x = (() => { throw new Error(...) })();`). The IIFE throws\n * eagerly during module evaluation (same timing as a top-level `throw`), but\n * preserving the declared export bindings ensures the surfaced error is the\n * actual \"node:* not available\" message rather than an opaque\n * \"missing export\" raised by the loader.\n * Vitest treats `node:*` as external SSR modules (skipping `resolveId`), so\n * source-level transformation is the only reliable interception point.\n * Runs in the default phase (no `enforce: \"pre\"`) so esbuild's TypeScript\n * transform strips `import type` first; only runtime imports reach this hook.\n * Node.js globals not in the platform runtime are removed by the environment (whitelist-based).\n * Test file patterns are read from the resolved Vitest config (`test.include`).\n * Vitest setup files (`test.setupFiles`) and global-setup files\n * (`test.globalSetup`) are also exempted: they run in the test runner host,\n * not in the emulated platform runtime, so they may freely use `node:*`\n * modules (e.g. `node:url` for `pathToFileURL`).\n * @returns Vite plugin\n */\nexport function createBlockPlugin(): Plugin {\n let isTestFile: (id: string) => boolean = () => false;\n let isUserSourceFile: (id: string) => boolean = () => false;\n\n return {\n name: \"tailor-runtime-block-node\",\n\n configResolved(config) {\n type HostFileTestConfig = {\n include?: string[];\n setupFiles?: string | string[];\n globalSetup?: string | string[];\n root?: string;\n };\n const testConfig = (\n config as typeof config & {\n test?: HostFileTestConfig & {\n projects?: { test?: HostFileTestConfig }[];\n };\n }\n ).test;\n const root = testConfig?.root ?? config.root;\n // Setup files and global-setup files run in the Vitest host (not the\n // emulated runtime), so they may freely import node:* modules. Collect\n // them from the top-level config AND from each `test.projects[i]` —\n // per-project setup files run in the host too and would otherwise be\n // transformed as production code, breaking node:* imports inside them.\n const toAbsolutePaths = (value: string | string[] | undefined, baseRoot: string) =>\n (Array.isArray(value) ? value : value ? [value] : []).map((f) => resolve(baseRoot, f));\n const exemptHostFiles = new Set<string>([\n ...toAbsolutePaths(testConfig?.setupFiles, root),\n ...toAbsolutePaths(testConfig?.globalSetup, root),\n ]);\n // Vitest projects can each define their own `test.include` (and root).\n // A project that uses non-default patterns (e.g. `tests/**/*.spec.ts`)\n // must also be considered when classifying test files — otherwise its\n // tests would be treated as production code and have node:* imports\n // rewritten. Build a list of (root, patterns) pairs covering top-level\n // + every project, and accept a file if any pair matches.\n const includePairs: { root: string; patterns: string[] }[] = [\n { root, patterns: testConfig?.include ?? DEFAULT_TEST_INCLUDE },\n ];\n for (const project of testConfig?.projects ?? []) {\n const projectTest = project.test;\n if (!projectTest) continue;\n const projectRoot = projectTest.root ?? root;\n for (const f of toAbsolutePaths(projectTest.setupFiles, projectRoot)) {\n exemptHostFiles.add(f);\n }\n for (const f of toAbsolutePaths(projectTest.globalSetup, projectRoot)) {\n exemptHostFiles.add(f);\n }\n includePairs.push({\n root: projectRoot,\n patterns: projectTest.include ?? DEFAULT_TEST_INCLUDE,\n });\n }\n isTestFile = (id: string) => {\n if (exemptHostFiles.has(id)) return true;\n return includePairs.some(({ root: r, patterns }) => {\n const candidate = isAbsolute(id) ? relative(r, id) : id;\n return patterns.some((pattern) => matchesGlob(candidate, pattern));\n });\n };\n // Only transform files inside the project root. With pnpm workspaces,\n // dependencies are symlinked and Vite resolves them to absolute paths\n // outside `node_modules`, so the substring check alone is insufficient.\n // Non-absolute ids are Vite-internal: virtual modules (`\\0...`,\n // `virtual:...`), bare specifiers, etc. Those are never user source\n // files and must not be parsed/transformed.\n isUserSourceFile = (id: string) => {\n if (!isAbsolute(id)) return false;\n const rel = relative(root, id);\n return rel !== \"\" && !rel.startsWith(\"..\") && !isAbsolute(rel);\n };\n },\n\n transform(code, id) {\n // Vite can pass ids with query/hash suffixes (e.g. `file.ts?import`,\n // `file.ts?v=hash`). Strip them so exact-path lookups (Set membership,\n // glob matching, absolute-path checks) match what callers configured.\n const queryIdx = id.search(/[?#]/);\n const cleanId = queryIdx === -1 ? id : id.slice(0, queryIdx);\n\n if (isTestFile(cleanId)) return undefined;\n if (cleanId.includes(\"node_modules\")) return undefined;\n if (!isUserSourceFile(cleanId)) return undefined;\n\n let ast: { body: ImportLikeNode[] };\n try {\n ast = this.parse(code) as unknown as { body: ImportLikeNode[] };\n } catch {\n // Not parseable as ESM (e.g. JSON, asset). Let other plugins handle it.\n return undefined;\n }\n\n const replacements: { start: number; end: number; replacement: string }[] = [];\n for (const node of ast.body) {\n if (!IMPORT_LIKE_TYPES.has(node.type)) continue;\n const specifier = node.source?.value;\n if (typeof specifier !== \"string\") continue;\n if (isBlockedModule(specifier)) {\n replacements.push({\n start: node.start,\n end: node.end,\n replacement: buildBlockedReplacement(node, getBlockedMessage(specifier)),\n });\n }\n }\n\n if (replacements.length === 0) return undefined;\n\n let transformed = code;\n for (const r of replacements.toSorted((a, b) => b.start - a.start)) {\n transformed = transformed.slice(0, r.start) + r.replacement + transformed.slice(r.end);\n }\n\n return { code: transformed, map: null };\n },\n };\n}\n\nconst ENVIRONMENT_NAME = \"tailor-runtime\";\n\n/**\n * Vite plugin that resolves the tailor-runtime environment and injects setup files.\n *\n * Vitest resolves environments starting with \".\" or \"/\" as file paths.\n * This plugin rewrites `environment: \"tailor-runtime\"` to the absolute path\n * of the bundled environment module, both at the top-level and per-project.\n * It also injects the setup file that removes Vitest-dependent globals\n * (like `performance`) per-test via beforeEach/afterEach hooks.\n * @param options - Optional configuration\n * @param options.config - Path to tailor.config.ts to load SecretManager values into mock\n * @returns Vite plugin\n */\nexport function createEnvironmentPlugin(options?: { config?: string }): Plugin {\n const currentDir = dirname(fileURLToPath(import.meta.url));\n const environmentPath = resolve(currentDir, \"environment.mjs\");\n const setupPath = resolve(currentDir, \"setup.mjs\");\n\n return {\n name: \"tailor-runtime-environment\",\n\n config(config) {\n const testConfig = config.test as\n | (Record<string, unknown> & {\n projects?: Record<string, unknown>[];\n setupFiles?: string | string[];\n })\n | undefined;\n\n // Rewrite environment name to absolute path at top-level\n let usesTailorRuntime = false;\n if (testConfig?.environment === ENVIRONMENT_NAME) {\n testConfig.environment = environmentPath;\n usesTailorRuntime = true;\n }\n\n // Rewrite in each project config\n if (testConfig?.projects) {\n for (const project of testConfig.projects) {\n const projectTest = project.test as Record<string, unknown> | undefined;\n if (projectTest?.environment === ENVIRONMENT_NAME) {\n projectTest.environment = environmentPath;\n usesTailorRuntime = true;\n }\n }\n }\n\n // Pass config path to setup.ts via env var (cross-process compatible).\n // Always clear first, then set only when tailor-runtime is actually\n // selected. This makes the env var deterministic across Vite config\n // reloads (watch mode, programmatic re-init): a stale value from a\n // prior iteration cannot make setup.ts load secrets from an old config.\n // The leading `__` marks this as plugin-private, so deleting any\n // pre-existing value is safe.\n delete process.env.__TAILOR_RUNTIME_CONFIG;\n if (options?.config && usesTailorRuntime) {\n // Resolve against the user-provided Vite root when present (falling\n // back to cwd). Vitest projects with a non-cwd `root` would otherwise\n // resolve a relative options.config against the wrong directory.\n const configRoot = (config.root as string | undefined) ?? process.cwd();\n const configAbsPath = resolve(configRoot, options.config);\n process.env.__TAILOR_RUNTIME_CONFIG = configAbsPath;\n }\n\n // Normalize a user-provided string `setupFiles` into an array so Vite's\n // array-concat merge sees both sides as arrays (the string form would\n // otherwise be replaced rather than concatenated by some merge paths).\n // Vite then concatenates the user's array with our [setupPath].\n if (testConfig && typeof testConfig.setupFiles === \"string\") {\n testConfig.setupFiles = [testConfig.setupFiles];\n }\n\n return {\n test: {\n setupFiles: [setupPath],\n },\n };\n },\n };\n}\n","/**\n * Kysely-layer mock for unit testing.\n *\n * Builds a real Kysely instance backed by a mock driver: queries compile and\n * type-check normally, but execution returns staged rows and records every\n * query for assertions.\n */\n\nimport {\n ColumnNode,\n type CompiledQuery,\n type DatabaseConnection,\n type Dialect,\n type Driver,\n InsertQueryNode,\n Kysely,\n type OperationNode,\n type OperationNodeKind,\n PostgresAdapter,\n PostgresIntrospector,\n PostgresQueryCompiler,\n PrimitiveValueListNode,\n type QueryResult,\n ReferenceNode,\n type Transaction,\n UpdateQueryNode,\n ValueListNode,\n ValueNode,\n ValuesNode,\n} from \"kysely\";\nimport { assertDefined } from \"@/utils/assert\";\n\nfunction unwrapValue(node: OperationNode): unknown {\n return ValueNode.is(node) ? node.value : node;\n}\n\nfunction insertRows(node: OperationNode): Record<string, unknown>[] {\n if (!InsertQueryNode.is(node)) {\n throw new Error(`insertRows: expected InsertQueryNode, got ${node.kind}`);\n }\n const columns = node.columns;\n const valuesNode = node.values;\n if (columns === undefined || valuesNode === undefined || !ValuesNode.is(valuesNode)) {\n throw new Error(\"insertRows: unsupported insert shape; inspect query.node instead\");\n }\n return valuesNode.values.map((row) => {\n if (!PrimitiveValueListNode.is(row) && !ValueListNode.is(row)) {\n throw new Error(\"insertRows: unsupported insert shape; inspect query.node instead\");\n }\n const values = PrimitiveValueListNode.is(row) ? row.values : row.values.map(unwrapValue);\n const result: Record<string, unknown> = {};\n columns.forEach((col, i) => {\n result[col.column.name] = values[i];\n });\n return result;\n });\n}\n\nfunction insertValues(node: OperationNode): Record<string, unknown> {\n const rows = insertRows(node);\n if (rows.length !== 1) {\n throw new Error(\n `insertValues: query inserts ${rows.length} rows; use insertRows() for multi-row inserts`,\n );\n }\n return assertDefined(rows[0], \"insertValues: first row missing\");\n}\n\nfunction updateValues(node: OperationNode): Record<string, unknown> {\n if (!UpdateQueryNode.is(node)) {\n throw new Error(`updateValues: expected UpdateQueryNode, got ${node.kind}`);\n }\n if (node.updates === undefined) {\n throw new Error(\"updateValues: unsupported update shape; inspect query.node instead\");\n }\n const result: Record<string, unknown> = {};\n for (const update of node.updates) {\n const col = update.column;\n const name = ColumnNode.is(col)\n ? col.column.name\n : ReferenceNode.is(col) && ColumnNode.is(col.column)\n ? col.column.column.name\n : undefined;\n if (name === undefined) {\n throw new Error(\"updateValues: unsupported update shape; inspect query.node instead\");\n }\n result[name] = unwrapValue(update.value);\n }\n return result;\n}\n\n/** A single statement executed against the mock, captured in order. */\nexport interface ExecutedQuery {\n /** The Kysely operation node kind, e.g. `\"SelectQueryNode\"`. */\n kind: OperationNodeKind;\n /** The compiled SQL string. */\n sql: string;\n /** The bound parameter values, in positional order. */\n parameters: readonly unknown[];\n /** The compiled Kysely operation node. */\n node: OperationNode;\n /** One `{ column: value }` map per row written by an insert. */\n insertRows: () => Record<string, unknown>[];\n /** The `{ column: value }` map written by a single-row insert. */\n insertValues: () => Record<string, unknown>;\n /** The `{ column: value }` map written by an update's SET clause. */\n updateValues: () => Record<string, unknown>;\n}\n\nfunction toExecutedQuery(compiledQuery: CompiledQuery): ExecutedQuery {\n const node = compiledQuery.query;\n return {\n kind: node.kind,\n sql: compiledQuery.sql,\n parameters: compiledQuery.parameters,\n node,\n insertRows: () => insertRows(node),\n insertValues: () => insertValues(node),\n updateValues: () => updateValues(node),\n };\n}\n\ntype MockRow = Record<string, unknown>;\ntype MockResult = MockRow[] | { rows?: MockRow[]; numAffectedRows?: number | bigint };\ntype QueryResolver = (query: ExecutedQuery) => MockResult | undefined;\n\ninterface StagedResult {\n rows: MockRow[];\n numAffectedRows: bigint | undefined;\n}\n\nfunction toStagedResult(result: MockResult): StagedResult {\n if (Array.isArray(result)) return { rows: result, numAffectedRows: undefined };\n return {\n rows: result.rows ?? [],\n numAffectedRows:\n result.numAffectedRows === undefined ? undefined : BigInt(result.numAffectedRows),\n };\n}\n\n/** Controls and assertions for a {@link createKyselyMock} instance. */\nexport interface KyselyMock<DB> {\n /** The mock Kysely instance to run queries against. */\n db: Kysely<DB>;\n /** Every recorded query, in execution order. */\n executedQueries: ExecutedQuery[];\n /** Recorded SELECT queries. */\n selects: ExecutedQuery[];\n /** Recorded INSERT queries. */\n inserts: ExecutedQuery[];\n /** Recorded UPDATE queries. */\n updates: ExecutedQuery[];\n /** Recorded DELETE queries. */\n deletes: ExecutedQuery[];\n /** Stage the rows the next query returns. */\n enqueueResult: (result: MockResult) => void;\n /** Stage the rows for several upcoming queries, consumed in order. */\n enqueueResults: (...results: MockResult[]) => void;\n /** Set a resolver that returns rows by inspecting each query. */\n setQueryResolver: (resolver: QueryResolver) => void;\n /** Run `fn` inside a real transaction and return its result. */\n withTx: <R>(fn: (trx: Transaction<DB>) => Promise<R>) => Promise<R>;\n /** Clear recorded queries and staged results. */\n reset: () => void;\n /** Same as {@link KyselyMock.reset}; enables `using` disposal. */\n [Symbol.dispose]: () => void;\n}\n\nclass MockState {\n readonly executed: ExecutedQuery[] = [];\n private readonly queue: MockResult[] = [];\n private resolver: QueryResolver | undefined;\n\n enqueue(...results: MockResult[]): void {\n this.queue.push(...results);\n }\n\n setResolver(resolver: QueryResolver): void {\n this.resolver = resolver;\n }\n\n next(query: ExecutedQuery): StagedResult {\n const resolved = this.resolver?.(query);\n if (resolved !== undefined) return toStagedResult(resolved);\n const queued = this.queue.shift();\n return queued === undefined ? { rows: [], numAffectedRows: undefined } : toStagedResult(queued);\n }\n\n reset(): void {\n this.executed.length = 0;\n this.queue.length = 0;\n this.resolver = undefined;\n }\n}\n\nclass MockConnection implements DatabaseConnection {\n constructor(private readonly state: MockState) {}\n\n async executeQuery<R>(compiledQuery: CompiledQuery): Promise<QueryResult<R>> {\n const query = toExecutedQuery(compiledQuery);\n this.state.executed.push(query);\n const { rows, numAffectedRows } = this.state.next(query);\n return {\n rows: rows as R[],\n numAffectedRows: numAffectedRows ?? BigInt(rows.length),\n };\n }\n\n streamQuery<R>(): AsyncIterableIterator<QueryResult<R>> {\n throw new Error(\"createKyselyMock: streaming is not supported\");\n }\n}\n\nclass MockDriver implements Driver {\n constructor(private readonly state: MockState) {}\n\n async init(): Promise<void> {}\n\n async acquireConnection(): Promise<DatabaseConnection> {\n return new MockConnection(this.state);\n }\n\n // No-ops so begin/commit/rollback never enter `executed` and pollute counts.\n async beginTransaction(): Promise<void> {}\n async commitTransaction(): Promise<void> {}\n async rollbackTransaction(): Promise<void> {}\n\n async releaseConnection(): Promise<void> {}\n async destroy(): Promise<void> {}\n}\n\nfunction byKind(state: MockState, kind: OperationNodeKind): ExecutedQuery[] {\n return state.executed.filter((query) => query.kind === kind);\n}\n\n/**\n * Create a mock Kysely instance for unit-testing code that runs Kysely queries.\n * Pass the namespace schema as the type argument, e.g.\n * `createKyselyMock<Namespace[\"main-db\"]>()`.\n * @returns A {@link KyselyMock} with the mock `db`, recorded queries, and result staging.\n */\nexport function createKyselyMock<DB = Record<string, never>>(): KyselyMock<DB> {\n const state = new MockState();\n const dialect: Dialect = {\n createDriver: () => new MockDriver(state),\n createQueryCompiler: () => new PostgresQueryCompiler(),\n createAdapter: () => new PostgresAdapter(),\n createIntrospector: (db) => new PostgresIntrospector(db),\n };\n const kysely = new Kysely<DB>({ dialect });\n\n return {\n db: kysely,\n get executedQueries() {\n return state.executed;\n },\n get selects() {\n return byKind(state, \"SelectQueryNode\");\n },\n get inserts() {\n return byKind(state, \"InsertQueryNode\");\n },\n get updates() {\n return byKind(state, \"UpdateQueryNode\");\n },\n get deletes() {\n return byKind(state, \"DeleteQueryNode\");\n },\n enqueueResult: (result) => state.enqueue(result),\n enqueueResults: (...results) => state.enqueue(...results),\n setQueryResolver: (resolver) => state.setResolver(resolver),\n withTx: (fn) => kysely.transaction().execute(fn),\n reset: () => state.reset(),\n [Symbol.dispose]: () => state.reset(),\n };\n}\n","import { createBlockPlugin, createEnvironmentPlugin } from \"./plugin\";\nimport type { Plugin } from \"vitest/config\";\n\n/**\n * Creates Vitest plugins that emulate the Tailor Platform function runtime environment.\n *\n * **Beta:** This API may change in future releases.\n *\n * ## What it does\n *\n * 1. **Node.js module blocking** (transform hook) — Imports of `node:*` modules\n * (and bare builtins like `crypto`, `fs`) in non-test source files are replaced\n * with code that throws an error with a suggestion for the Web Standard API alternative.\n * Test files are exempt and can use `node:*` freely. Test file patterns are read\n * from the resolved Vitest config (`test.include`).\n *\n * 2. **Node.js globals removal** (environment + setup) — Only globals available in the\n * Tailor Platform runtime are kept (whitelist: ECMAScript standard, Web Standard APIs\n * from bootstrap.js, platform mocks). All others (`Buffer`, `global`, `setImmediate`,\n * `__dirname`, `__filename`, etc.) are removed. `performance` is removed per-test\n * via beforeEach/afterEach since Vitest needs it during initialization.\n *\n * 3. **Platform API mocks** (environment) — All platform APIs are auto-injected with\n * control objects: `mockTailordb`, `mockWorkflow`, `mockSecretmanager`,\n * `mockAuthconnection`, `mockIdp`, `mockFile`, `mockIconv`. Each\n * provides response configuration, call recording, and reset.\n *\n * 4. **Environment resolution** — Rewrites `environment: \"tailor-runtime\"` to the\n * absolute path of the bundled environment module via the config hook.\n *\n * ## Known limitations\n *\n * - **`process`** and **`require`** are NOT removed or blocked. Vitest's internal\n * runner depends on them. On the real Tailor Platform runtime, they do not exist.\n * - **Dynamic `import()`** of bundled files (via `createImportMain()`) bypasses\n * the transform hook since those files are loaded through Node.js native loader.\n * ## Options\n *\n * - **`config`** — Path to `tailor.config.ts`. Loads `defineSecretManager()` values\n * into `mockSecretmanager` so `getSecret()` returns the configured values.\n * @example\n * ```typescript\n * // vitest.config.ts\n * import { defineConfig } from \"vitest/config\";\n * import { tailorRuntime } from \"@tailor-platform/sdk/vitest\";\n *\n * export default defineConfig({\n * plugins: [tailorRuntime({ config: \"./tailor.config.ts\" })],\n * test: {\n * environment: \"tailor-runtime\",\n * },\n * });\n * ```\n * @param options - Optional configuration\n * @param options.config - Path to tailor.config.ts to load SecretManager values into mock\n * @returns Array of Vite plugins\n */\nexport function tailorRuntime(options?: { config?: string }): Plugin[] {\n return [createBlockPlugin(), createEnvironmentPlugin(options)];\n}\n\nexport {\n mockTailordb,\n mockWorkflow,\n mockSecretmanager,\n mockAuthconnection,\n mockIdp,\n mockFile,\n mockIconv,\n} from \"./mock\";\n\nexport { createKyselyMock, type KyselyMock, type ExecutedQuery } from \"./mock-kysely\";\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { t as RUNTIME_FLAG_KEY } from "../globals-ByrCoDip.mjs";
3
- import { a as mockSecretmanager } from "../mock-Dpu__UeJ.mjs";
3
+ import { a as mockSecretmanager } from "../mock-DMgIygjE.mjs";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { afterEach, beforeAll, beforeEach } from "vitest";
6
6
 
@@ -117,16 +117,17 @@ tailor-sdk deploy [options]
117
117
 
118
118
  **Options**
119
119
 
120
- | Option | Alias | Description | Required | Default | Env |
121
- | ------------------------------- | ----- | -------------------------------------------------- | -------- | -------------------- | --------------------------------- |
122
- | `--workspace-id <WORKSPACE_ID>` | `-w` | Workspace ID | No | - | `TAILOR_PLATFORM_WORKSPACE_ID` |
123
- | `--profile <PROFILE>` | `-p` | Workspace profile | No | - | `TAILOR_PLATFORM_PROFILE` |
124
- | `--config <CONFIG>` | `-c` | Path to SDK config file | No | `"tailor.config.ts"` | `TAILOR_PLATFORM_SDK_CONFIG_PATH` |
125
- | `--yes` | `-y` | Skip confirmation prompts | No | `false` | - |
126
- | `--dry-run` | `-d` | Run the command without making any changes | No | - | - |
127
- | `--no-schema-check` | - | Skip schema diff check against migration snapshots | No | - | - |
128
- | `--no-cache` | - | Disable bundle caching for this run | No | - | - |
129
- | `--clean-cache` | - | Clean the bundle cache before building | No | - | - |
120
+ | Option | Alias | Description | Required | Default | Env |
121
+ | ------------------------------- | ----- | ----------------------------------------------------------------- | -------- | -------------------- | --------------------------------- |
122
+ | `--workspace-id <WORKSPACE_ID>` | `-w` | Workspace ID | No | - | `TAILOR_PLATFORM_WORKSPACE_ID` |
123
+ | `--profile <PROFILE>` | `-p` | Workspace profile | No | - | `TAILOR_PLATFORM_PROFILE` |
124
+ | `--config <CONFIG>` | `-c` | Path to SDK config file | No | `"tailor.config.ts"` | `TAILOR_PLATFORM_SDK_CONFIG_PATH` |
125
+ | `--yes` | `-y` | Skip confirmation prompts | No | `false` | - |
126
+ | `--dry-run` | `-d` | Run the command without making any changes | No | - | - |
127
+ | `--no-schema-check` | - | Skip schema diff check against migration snapshots | No | - | - |
128
+ | `--no-validate` | - | Skip client-side validation against platform resource constraints | No | - | - |
129
+ | `--no-cache` | - | Disable bundle caching for this run | No | - | - |
130
+ | `--clean-cache` | - | Clean the bundle cache before building | No | - | - |
130
131
 
131
132
  <!-- politty:command:deploy:options:end -->
132
133
 
package/docs/cli/setup.md CHANGED
@@ -28,9 +28,9 @@ tailor-sdk setup [command]
28
28
 
29
29
  **Commands**
30
30
 
31
- | Command | Description |
32
- | ------------------------------- | ------------------------------------------------------- |
33
- | [`setup github`](#setup-github) | Generate GitHub Actions workflow for deployment. (beta) |
31
+ | Command | Description |
32
+ | ------------------------------- | ------------------------------------------------- |
33
+ | [`setup github`](#setup-github) | Generate a GitHub Actions deploy workflow. (beta) |
34
34
 
35
35
  <!-- politty:command:setup:subcommands:end -->
36
36
 
@@ -47,7 +47,7 @@ See [Global Options](../cli-reference.md#global-options) for options available t
47
47
 
48
48
  <!-- politty:command:setup github:description:start -->
49
49
 
50
- Generate GitHub Actions workflow for deployment. (beta)
50
+ Generate a GitHub Actions deploy workflow. (beta)
51
51
 
52
52
  <!-- politty:command:setup github:description:end -->
53
53
 
@@ -65,14 +65,16 @@ tailor-sdk setup github [options]
65
65
 
66
66
  **Options**
67
67
 
68
- | Option | Alias | Description | Required | Default |
69
- | --------------------------------------- | ----- | ----------------------------------- | -------- | ------- |
70
- | `--workspace-name <WORKSPACE_NAME>` | `-n` | Workspace name | Yes | - |
71
- | `--workspace-region <WORKSPACE_REGION>` | `-r` | Workspace region | Yes | - |
72
- | `--organization-id <ORGANIZATION_ID>` | `-o` | Organization ID | Yes | - |
73
- | `--folder-id <FOLDER_ID>` | `-f` | Folder ID | Yes | - |
74
- | `--dir <DIR>` | `-d` | App directory (for monorepo setups) | No | `"."` |
75
- | `--with-plan` | `-p` | Include plan job for PR previews | No | `false` |
68
+ | Option | Alias | Description | Required | Default |
69
+ | ----------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- |
70
+ | `--workspace-name <WORKSPACE_NAME>` | `-n` | Workspace name (defaults to the config 'name') | No | - |
71
+ | `--branch <BRANCH>` | - | Branch target: deploy trigger branch (defaults to the detected default branch). Tag target: tag-reachability guard branch (no guard when omitted) | No | - |
72
+ | `--tag` | - | Generate a tag target (deploy on tag push) | No | `false` |
73
+ | `--tag-pattern <TAG_PATTERN>` | - | Tag glob to match (requires --tag; defaults to v\*) | No | - |
74
+ | `--environment <ENVIRONMENT>` | - | GitHub Environment for the plan/deploy jobs (defaults to the workspace name) | No | - |
75
+ | `--no-plan` | - | Disable the plan job for a branch target (cannot be combined with --tag) | No | `false` |
76
+ | `--dir <DIR>` | `-d` | App directory (for monorepo setups) | No | `"."` |
77
+ | `--force` | - | Discard hand edits / take over unmanaged files and regenerate | No | `false` |
76
78
 
77
79
  <!-- politty:command:setup github:options:end -->
78
80
 
@@ -81,3 +83,7 @@ tailor-sdk setup github [options]
81
83
  See [Global Options](../cli-reference.md#global-options) for options available to all commands.
82
84
 
83
85
  <!-- politty:command:setup github:global-options-link:end -->
86
+
87
+ ## Further reading
88
+
89
+ - [GitHub Actions Integration](../github-actions.md) — usage guide: targets, generated files, secrets, approval gates, and rollback.
@@ -41,6 +41,7 @@ tailor-sdk tailordb [command]
41
41
  See [Global Options](../cli-reference.md#global-options) for options available to all commands.
42
42
 
43
43
  <!-- politty:command:tailordb:global-options-link:end -->
44
+
44
45
  <!-- politty:command:tailordb truncate:heading:start -->
45
46
 
46
47
  ### tailordb truncate
@@ -158,6 +159,7 @@ tailor-sdk tailordb migration [command]
158
159
  | [`tailordb migration script`](#tailordb-migration-script) | Add a migration script (migrate.ts) template to an existing migration directory. |
159
160
  | [`tailordb migration set`](#tailordb-migration-set) | Set migration checkpoint to a specific number. |
160
161
  | [`tailordb migration status`](#tailordb-migration-status) | Show the current migration status for TailorDB namespaces, including applied and pending migrations. |
162
+ | [`tailordb migration sync`](#tailordb-migration-sync) | Sync remote TailorDB schema to a specific migration snapshot (recovery from --no-schema-check drift). |
161
163
 
162
164
  <!-- politty:command:tailordb migration:subcommands:end -->
163
165
 
@@ -347,6 +349,58 @@ See [Global Options](../cli-reference.md#global-options) for options available t
347
349
 
348
350
  <!-- politty:command:tailordb migration status:global-options-link:end -->
349
351
 
352
+ <!-- politty:command:tailordb migration sync:heading:start -->
353
+
354
+ #### tailordb migration sync
355
+
356
+ <!-- politty:command:tailordb migration sync:heading:end -->
357
+
358
+ <!-- politty:command:tailordb migration sync:description:start -->
359
+
360
+ Sync remote TailorDB schema to a specific migration snapshot (recovery from --no-schema-check drift).
361
+
362
+ <!-- politty:command:tailordb migration sync:description:end -->
363
+
364
+ <!-- politty:command:tailordb migration sync:usage:start -->
365
+
366
+ **Usage**
367
+
368
+ ```
369
+ tailor-sdk tailordb migration sync [options] <number>
370
+ ```
371
+
372
+ <!-- politty:command:tailordb migration sync:usage:end -->
373
+
374
+ <!-- politty:command:tailordb migration sync:arguments:start -->
375
+
376
+ **Arguments**
377
+
378
+ | Argument | Description | Required |
379
+ | -------- | ------------------------------------------------------------------------------ | -------- |
380
+ | `number` | Migration number to sync to (e.g., 0001 or 1; 0 targets the baseline snapshot) | Yes |
381
+
382
+ <!-- politty:command:tailordb migration sync:arguments:end -->
383
+
384
+ <!-- politty:command:tailordb migration sync:options:start -->
385
+
386
+ **Options**
387
+
388
+ | Option | Alias | Description | Required | Default | Env |
389
+ | ------------------------------- | ----- | ----------------------------------------------------------------- | -------- | -------------------- | --------------------------------- |
390
+ | `--workspace-id <WORKSPACE_ID>` | `-w` | Workspace ID | No | - | `TAILOR_PLATFORM_WORKSPACE_ID` |
391
+ | `--profile <PROFILE>` | `-p` | Workspace profile | No | - | `TAILOR_PLATFORM_PROFILE` |
392
+ | `--config <CONFIG>` | `-c` | Path to SDK config file | No | `"tailor.config.ts"` | `TAILOR_PLATFORM_SDK_CONFIG_PATH` |
393
+ | `--yes` | `-y` | Skip confirmation prompts | No | `false` | - |
394
+ | `--namespace <NAMESPACE>` | `-n` | Target TailorDB namespace (required if multiple namespaces exist) | No | - | - |
395
+
396
+ <!-- politty:command:tailordb migration sync:options:end -->
397
+
398
+ <!-- politty:command:tailordb migration sync:global-options-link:start -->
399
+
400
+ See [Global Options](../cli-reference.md#global-options) for options available to all commands.
401
+
402
+ <!-- politty:command:tailordb migration sync:global-options-link:end -->
403
+
350
404
  **See also:** For migration concepts, configuration, workflow, and troubleshooting, see the [TailorDB Migrations guide](../services/tailordb-migration.md).
351
405
 
352
406
  <!-- politty:command:tailordb erd:heading:start -->
@@ -130,6 +130,7 @@ Commands for managing TailorDB tables, data, and schema migrations.
130
130
  | [tailordb migration script](./cli/tailordb.md#tailordb-migration-script) | Add a migration script (migrate.ts) template to an existing migration directory. |
131
131
  | [tailordb migration set](./cli/tailordb.md#tailordb-migration-set) | Set migration checkpoint to a specific number. |
132
132
  | [tailordb migration status](./cli/tailordb.md#tailordb-migration-status) | Show the current migration status for TailorDB namespaces, including applied and pending migrations. |
133
+ | [tailordb migration sync](./cli/tailordb.md#tailordb-migration-sync) | Sync remote TailorDB schema to a specific migration snapshot (recovery from --no-schema-check drift). |
133
134
  | [tailordb erd export](./cli/tailordb.md#tailordb-erd-export) | Export TailorDB ERD static viewer from local TailorDB schema. |
134
135
  | [tailordb erd serve](./cli/tailordb.md#tailordb-erd-serve) | Generate and serve TailorDB ERD locally with watch reload. (beta) |
135
136
  | [tailordb erd deploy](./cli/tailordb.md#tailordb-erd-deploy) | Deploy ERD static website for TailorDB namespace(s). |
@@ -286,9 +287,9 @@ Commands for managing crash reports.
286
287
 
287
288
  Commands for setting up project infrastructure.
288
289
 
289
- | Command | Description |
290
- | ------------------------------------------- | ------------------------------------------------------- |
291
- | [setup github](./cli/setup.md#setup-github) | Generate GitHub Actions workflow for deployment. (beta) |
290
+ | Command | Description |
291
+ | ------------------------------------------- | ------------------------------------------------- |
292
+ | [setup github](./cli/setup.md#setup-github) | Generate a GitHub Actions deploy workflow. (beta) |
292
293
 
293
294
  ### [Upgrade Commands](./cli/upgrade.md)
294
295