@tailor-platform/sdk 2.1.0 → 2.3.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.
- package/CHANGELOG.md +199 -0
- package/{dist/cli/index.mjs → bin/tailor.mjs} +1 -1
- package/dist/application-BZfk4HKm.mjs +3 -0
- package/dist/{application-D77KJFKD.mjs → application-F-nl107y.mjs} +121 -27
- package/dist/application-F-nl107y.mjs.map +1 -0
- package/dist/cli/commands/deploy/tailordb/index.d.mts +2 -1
- package/dist/cli/commands/deploy/types.d.mts +2 -1
- package/dist/cli/commands/tailordb/migrate/diff-calculator.d.mts +52 -22
- package/dist/cli/commands/tailordb/migrate/file-state.d.mts +1 -0
- package/dist/cli/commands/tailordb/migrate/generate.d.mts +6 -0
- package/dist/cli/commands/tailordb/migrate/rename-detection.d.mts +22 -0
- package/dist/cli/commands/tailordb/migrate/schema-checks.d.mts +2 -2
- package/dist/cli/commands/tailordb/migrate/snapshot-types.d.mts +9 -2
- package/dist/cli/commands/tailordb/migrate/snapshot.d.mts +30 -9
- package/dist/cli/lib.d.mts +4 -4
- package/dist/cli/lib.mjs +3 -482
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/main.mjs +1151 -208
- package/dist/cli/main.mjs.map +1 -1
- package/dist/cli/ts-hook.mjs +52 -7
- package/dist/completion/zsh-worker.zsh +167 -4
- package/dist/configure/index.d.mts +3 -3
- package/dist/configure/index.mjs +90 -23
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/index.d.mts +3 -2
- package/dist/configure/services/tailordb/index.d.mts +2 -1
- package/dist/configure/services/tailordb/schema.d.mts +46 -46
- package/dist/configure/services/tailordb/types.d.mts +30 -4
- package/dist/configure/services/workflow/index.d.mts +2 -2
- package/dist/configure/services/workflow/wait-point.d.mts +72 -13
- package/dist/{crashreport-B5LqCTfF.mjs → crashreport-CIIfLnoO.mjs} +2 -2
- package/dist/{crashreport-B5LqCTfF.mjs.map → crashreport-CIIfLnoO.mjs.map} +1 -1
- package/dist/{crashreport-eI7ty7ok.mjs → crashreport-CINZ5dVN.mjs} +1 -1
- package/dist/errors-BVb6vYGy.mjs +89 -0
- package/dist/errors-BVb6vYGy.mjs.map +1 -0
- package/dist/kysely/index.d.mts +53 -5
- package/dist/kysely/index.mjs +7 -2
- package/dist/kysely/index.mjs.map +1 -1
- package/dist/{logger-CXQq9YIp.mjs → logger-DSdExfor.mjs} +83 -42
- package/dist/logger-DSdExfor.mjs.map +1 -0
- package/dist/plugin/builtin/seed/index.mjs +1 -1
- package/dist/{register-ts-hook-BU18uU44.mjs → register-ts-hook-ClI226n2.mjs} +3541 -703
- package/dist/register-ts-hook-ClI226n2.mjs.map +1 -0
- package/dist/schema--xYWRGfe.mjs.map +1 -1
- package/dist/seed/index.d.mts +52 -3
- package/dist/seed/index.mjs +200 -44
- package/dist/seed/index.mjs.map +1 -1
- package/dist/{seed-B-yckgQY.mjs → seed-xqM9WIHP.mjs} +8 -8
- package/dist/seed-xqM9WIHP.mjs.map +1 -0
- package/dist/{service-k1xy_A5G.mjs → service-C_WpbKHu.mjs} +39 -5
- package/dist/service-C_WpbKHu.mjs.map +1 -0
- package/dist/service-D1RCdzIL.mjs +3 -0
- package/dist/{service-B5WynBHQ.mjs → service-hZskxZmg.mjs} +3 -3
- package/dist/{service-B5WynBHQ.mjs.map → service-hZskxZmg.mjs.map} +1 -1
- package/dist/{service_pb-D5--My_M.mjs → service_pb-BEWj1pOV.mjs} +2 -2
- package/dist/{service_pb-D5--My_M.mjs.map → service_pb-BEWj1pOV.mjs.map} +1 -1
- package/dist/{service_pb-i8CmnQP4.mjs → service_pb-BjlhvK9E.mjs} +1 -1
- package/dist/utils/test/index.d.mts +6 -3
- package/dist/utils/test/index.mjs +25 -13
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/vitest/index.d.mts +2 -1
- package/dist/vitest/index.mjs +167 -3
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mocks/workflow.d.mts +13 -1
- package/dist/vitest/pglite-kysely.d.mts +43 -0
- package/dist/wait-point-invoker-__oE88_P.mjs +148 -0
- package/dist/wait-point-invoker-__oE88_P.mjs.map +1 -0
- package/dist/wait-point-registry-TL99zotw.mjs +47 -0
- package/dist/wait-point-registry-TL99zotw.mjs.map +1 -0
- package/dist/{workspace_resource_pb-DXnO2AT-.mjs → workspace_resource_pb-HNIL_Vfj.mjs} +10 -2
- package/dist/{workspace_resource_pb-DXnO2AT-.mjs.map → workspace_resource_pb-HNIL_Vfj.mjs.map} +1 -1
- package/docs/cli/setup.md +14 -1
- package/docs/cli/tailordb.md +96 -28
- package/docs/cli-reference.md +15 -12
- package/docs/configuration.md +1 -1
- package/docs/github-actions.md +73 -28
- package/docs/migration/v2.md +55 -1
- package/docs/plugin/custom.md +48 -48
- package/docs/plugin/index.md +24 -24
- package/docs/services/auth.md +10 -10
- package/docs/services/executor.md +1 -1
- package/docs/services/resolver.md +2 -2
- package/docs/services/tailordb-migration.md +485 -84
- package/docs/services/tailordb.md +28 -28
- package/docs/services/workflow.md +52 -2
- package/docs/testing.md +14 -0
- package/package.json +12 -7
- package/dist/application-Af1zIqSI.mjs +0 -3
- package/dist/application-D77KJFKD.mjs.map +0 -1
- package/dist/errors-D7583Zz7.mjs +0 -58
- package/dist/errors-D7583Zz7.mjs.map +0 -1
- package/dist/logger-CXQq9YIp.mjs.map +0 -1
- package/dist/register-ts-hook-BU18uU44.mjs.map +0 -1
- package/dist/seed-B-yckgQY.mjs.map +0 -1
- package/dist/service-M7OYziQP.mjs +0 -3
- package/dist/service-k1xy_A5G.mjs.map +0 -1
- package/dist/test-env-key-D7UkZp99.mjs +0 -75
- package/dist/test-env-key-D7UkZp99.mjs.map +0 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { i as file_tailor_v1_service, n as PingRequestSchema, r as PingResponseSchema, t as OperatorService } from "./service_pb-
|
|
1
|
+
import { i as file_tailor_v1_service, n as PingRequestSchema, r as PingResponseSchema, t as OperatorService } from "./service_pb-BEWj1pOV.mjs";
|
|
2
2
|
|
|
3
3
|
export { OperatorService, PingRequestSchema, PingResponseSchema, file_tailor_v1_service };
|
|
@@ -17,14 +17,17 @@ import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
|
17
17
|
*/
|
|
18
18
|
declare function createTailorDBHook<T extends TailorDBType<any, any>>(type: T): (data: unknown, now?: Date) => Partial<output<T>>;
|
|
19
19
|
/**
|
|
20
|
-
* Creates the standard schema definition
|
|
21
|
-
*
|
|
20
|
+
* Creates the standard schema definition used to validate seed rows.
|
|
21
|
+
* Runs the hook, then the type's own `validate`, and the field schema only when
|
|
22
|
+
* that reported nothing, so both levels of validation report as issues rather
|
|
23
|
+
* than by throwing.
|
|
22
24
|
* @template T - The output type after validation
|
|
23
25
|
* @param schemaType - TailorDB field schema for validation
|
|
24
26
|
* @param hook - Hook function to transform data before validation
|
|
27
|
+
* @param type - TailorDB type definition, when it carries a type-level `validate`
|
|
25
28
|
* @returns Schema object with ~standard section for defineSchema
|
|
26
29
|
*/
|
|
27
|
-
declare function createStandardSchema<T = Record<string, unknown>>(schemaType: TailorField<any, T>, hook: (data: unknown) => Partial<T>): {
|
|
30
|
+
declare function createStandardSchema<T = Record<string, unknown>>(schemaType: TailorField<any, T>, hook: (data: unknown) => Partial<T>, type?: TailorDBType<any, any>): {
|
|
28
31
|
readonly "~standard": {
|
|
29
32
|
readonly version: 1;
|
|
30
33
|
readonly vendor: "@tailor-platform/sdk";
|
|
@@ -55,33 +55,45 @@ function createTailorDBHook(type) {
|
|
|
55
55
|
});
|
|
56
56
|
if (overrides && typeof overrides === "object") for (const [key, value] of Object.entries(overrides)) setField(hooked, key, value instanceof Date ? value.toISOString() : value);
|
|
57
57
|
}
|
|
58
|
-
if (type.metadata?.typeValidate) {
|
|
59
|
-
const { id: _id, ...newRecord } = hooked;
|
|
60
|
-
type.metadata.typeValidate({
|
|
61
|
-
newRecord,
|
|
62
|
-
oldRecord: null,
|
|
63
|
-
invoker: null
|
|
64
|
-
}, (field, message) => {
|
|
65
|
-
throw new Error(`Validation failed on field '${field}': ${message}`);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
58
|
return hooked;
|
|
69
59
|
};
|
|
70
60
|
}
|
|
61
|
+
function typeLevelIssues(type, hooked) {
|
|
62
|
+
const typeValidate = type?.metadata?.typeValidate;
|
|
63
|
+
if (!typeValidate) return [];
|
|
64
|
+
const { id: _id, ...newRecord } = hooked;
|
|
65
|
+
const issues = [];
|
|
66
|
+
typeValidate({
|
|
67
|
+
newRecord,
|
|
68
|
+
oldRecord: null,
|
|
69
|
+
invoker: null
|
|
70
|
+
}, (field, message) => {
|
|
71
|
+
issues.push({
|
|
72
|
+
message,
|
|
73
|
+
path: [field]
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
return issues;
|
|
77
|
+
}
|
|
71
78
|
/**
|
|
72
|
-
* Creates the standard schema definition
|
|
73
|
-
*
|
|
79
|
+
* Creates the standard schema definition used to validate seed rows.
|
|
80
|
+
* Runs the hook, then the type's own `validate`, and the field schema only when
|
|
81
|
+
* that reported nothing, so both levels of validation report as issues rather
|
|
82
|
+
* than by throwing.
|
|
74
83
|
* @template T - The output type after validation
|
|
75
84
|
* @param schemaType - TailorDB field schema for validation
|
|
76
85
|
* @param hook - Hook function to transform data before validation
|
|
86
|
+
* @param type - TailorDB type definition, when it carries a type-level `validate`
|
|
77
87
|
* @returns Schema object with ~standard section for defineSchema
|
|
78
88
|
*/
|
|
79
|
-
function createStandardSchema(schemaType, hook) {
|
|
89
|
+
function createStandardSchema(schemaType, hook, type) {
|
|
80
90
|
return { "~standard": {
|
|
81
91
|
version: 1,
|
|
82
92
|
vendor: "@tailor-platform/sdk",
|
|
83
93
|
validate: (value) => {
|
|
84
94
|
const hooked = hook(value);
|
|
95
|
+
const issues = typeLevelIssues(type, hooked);
|
|
96
|
+
if (issues.length > 0) return { issues };
|
|
85
97
|
const result = schemaType.parse({
|
|
86
98
|
value: hooked,
|
|
87
99
|
data: hooked,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/utils/test/index.ts"],"sourcesContent":["import type { output } from \"#/configure/index\";\nimport type { TailorDBType } from \"#/configure/services/tailordb/schema\";\nimport type { TailorField } from \"#/configure/types/type\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// Not `record[key] = value`: assigning to `__proto__` goes through the inherited\n// setter, which mutates the prototype instead of recording the field and leaves\n// no own property behind for the value to be read from.\nfunction setField(record: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(record, key, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n}\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 * - Takes each field from the data's own properties, so a field named after a\n * member of `Object` such as `toString` is read from the record rather than\n * from the prototype\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, now: Date = new Date()) => {\n const obj = data && typeof data === \"object\" ? (data as Record<string, unknown>) : undefined;\n const hooked = 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 // `Object.hasOwn`, not `obj?.[key]`: a field named after an Object member\n // such as `toString` would otherwise read the inherited value.\n const input = obj && Object.hasOwn(obj, key) ? obj[key] : undefined;\n let hookedValue: unknown;\n if (key === \"id\") {\n hookedValue = input ?? crypto.randomUUID();\n } else if (field.type === \"nested\") {\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 hookedValue = Array.isArray(input) ? input.map((item) => nestedHook(item, now)) : input;\n } else {\n hookedValue = nestedHook(input, now);\n }\n } else if (field.metadata.hooks?.create) {\n hookedValue = field.metadata.hooks.create({ input, invoker: null, now });\n if (hookedValue instanceof Date) {\n hookedValue = hookedValue.toISOString();\n }\n } else {\n hookedValue = input;\n }\n if (hookedValue == null && field.metadata.default !== undefined) {\n const isTimeType =\n field.type === \"datetime\" || field.type === \"date\" || field.type === \"time\";\n hookedValue =\n field.metadata.default === \"now\" && isTimeType\n ? now.toISOString()\n : field.metadata.default;\n }\n // Set even when there is no value: the key carrying `undefined` is what\n // tells a schema inferred from the record that the column is nullable,\n // and it shadows a same-named member of `Object.prototype`.\n setField(hooked, key, hookedValue);\n return hooked;\n },\n {} as Record<string, unknown>,\n );\n\n // oxlint-disable-next-line typescript/no-unnecessary-condition -- metadata absent in recursive nested calls\n if (type.metadata?.typeHook?.create) {\n const { id: _id, ...typeHookInput } = hooked;\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n const overrides = (type.metadata.typeHook.create as Function)({\n input: typeHookInput,\n invoker: null,\n now,\n });\n if (overrides && typeof overrides === \"object\") {\n for (const [key, value] of Object.entries(overrides as Record<string, unknown>)) {\n setField(hooked, key, value instanceof Date ? value.toISOString() : value);\n }\n }\n }\n\n // oxlint-disable-next-line typescript/no-unnecessary-condition --
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/utils/test/index.ts"],"sourcesContent":["import type { output } from \"#/configure/index\";\nimport type { TailorDBType } from \"#/configure/services/tailordb/schema\";\nimport type { TailorField } from \"#/configure/types/type\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n// Not `record[key] = value`: assigning to `__proto__` goes through the inherited\n// setter, which mutates the prototype instead of recording the field and leaves\n// no own property behind for the value to be read from.\nfunction setField(record: Record<string, unknown>, key: string, value: unknown): void {\n Object.defineProperty(record, key, {\n value,\n enumerable: true,\n writable: true,\n configurable: true,\n });\n}\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 * - Takes each field from the data's own properties, so a field named after a\n * member of `Object` such as `toString` is read from the record rather than\n * from the prototype\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, now: Date = new Date()) => {\n const obj = data && typeof data === \"object\" ? (data as Record<string, unknown>) : undefined;\n const hooked = 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 // `Object.hasOwn`, not `obj?.[key]`: a field named after an Object member\n // such as `toString` would otherwise read the inherited value.\n const input = obj && Object.hasOwn(obj, key) ? obj[key] : undefined;\n let hookedValue: unknown;\n if (key === \"id\") {\n hookedValue = input ?? crypto.randomUUID();\n } else if (field.type === \"nested\") {\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 hookedValue = Array.isArray(input) ? input.map((item) => nestedHook(item, now)) : input;\n } else {\n hookedValue = nestedHook(input, now);\n }\n } else if (field.metadata.hooks?.create) {\n hookedValue = field.metadata.hooks.create({ input, invoker: null, now });\n if (hookedValue instanceof Date) {\n hookedValue = hookedValue.toISOString();\n }\n } else {\n hookedValue = input;\n }\n if (hookedValue == null && field.metadata.default !== undefined) {\n const isTimeType =\n field.type === \"datetime\" || field.type === \"date\" || field.type === \"time\";\n hookedValue =\n field.metadata.default === \"now\" && isTimeType\n ? now.toISOString()\n : field.metadata.default;\n }\n // Set even when there is no value: the key carrying `undefined` is what\n // tells a schema inferred from the record that the column is nullable,\n // and it shadows a same-named member of `Object.prototype`.\n setField(hooked, key, hookedValue);\n return hooked;\n },\n {} as Record<string, unknown>,\n );\n\n // oxlint-disable-next-line typescript/no-unnecessary-condition -- metadata absent in recursive nested calls\n if (type.metadata?.typeHook?.create) {\n const { id: _id, ...typeHookInput } = hooked;\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n const overrides = (type.metadata.typeHook.create as Function)({\n input: typeHookInput,\n invoker: null,\n now,\n });\n if (overrides && typeof overrides === \"object\") {\n for (const [key, value] of Object.entries(overrides as Record<string, unknown>)) {\n setField(hooked, key, value instanceof Date ? value.toISOString() : value);\n }\n }\n }\n\n return hooked as Partial<output<T>>;\n };\n}\n\n// Collect the issues the type's own `validate` reports for a record, so they\n// surface the same way a field's do instead of ending the run.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction typeLevelIssues(type: TailorDBType<any, any> | undefined, hooked: unknown) {\n // oxlint-disable-next-line typescript/no-unnecessary-condition -- absent on a nested type\n const typeValidate = type?.metadata?.typeValidate;\n if (!typeValidate) {\n return [];\n }\n const { id: _id, ...newRecord } = hooked as Record<string, unknown>;\n const issues: StandardSchemaV1.Issue[] = [];\n // oxlint-disable-next-line typescript/no-unsafe-function-type\n (typeValidate as Function)(\n { newRecord, oldRecord: null, invoker: null },\n (field: string, message: string) => {\n issues.push({ message, path: [field] });\n },\n );\n return issues;\n}\n\n/**\n * Creates the standard schema definition used to validate seed rows.\n * Runs the hook, then the type's own `validate`, and the field schema only when\n * that reported nothing, so both levels of validation report as issues rather\n * than by throwing.\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 * @param type - TailorDB type definition, when it carries a type-level `validate`\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 // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type?: TailorDBType<any, any>,\n) {\n return {\n \"~standard\": {\n version: 1,\n vendor: \"@tailor-platform/sdk\",\n validate: (value: unknown) => {\n const hooked = hook(value);\n const issues = typeLevelIssues(type, hooked);\n if (issues.length > 0) {\n return { issues };\n }\n const result = schemaType.parse({\n value: hooked,\n data: hooked,\n invoker: null,\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":";AAQA,SAAS,SAAS,QAAiC,KAAa,OAAsB;CACpF,OAAO,eAAe,QAAQ,KAAK;EACjC;EACA,YAAY;EACZ,UAAU;EACV,cAAc;CAChB,CAAC;AACH;;;;;;;;;;;;;AAeA,SAAgB,mBAAqD,MAAS;CAC5E,QAAQ,MAAe,sBAAY,IAAI,KAAK,MAAM;EAChD,MAAM,MAAM,QAAQ,OAAO,SAAS,WAAY,OAAmC;EACnF,MAAM,SAAS,OAAO,QAAQ,KAAK,MAAM,CAAC,CAAC,QACxC,QAAQ,CAAC,KAAK,WAAW;GAExB,MAAM,QAAQ;GAGd,MAAM,QAAQ,OAAO,OAAO,OAAO,KAAK,GAAG,IAAI,IAAI,OAAO;GAC1D,IAAI;GACJ,IAAI,QAAQ,MACV,cAAc,SAAS,OAAO,WAAW;QACpC,IAAI,MAAM,SAAS,UAAU;IAElC,MAAM,aAAa,mBAAmB,EAAE,QAAQ,MAAM,OAAO,CAAQ;IACrE,IAAI,MAAM,SAAS,OACjB,cAAc,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,SAAS,WAAW,MAAM,GAAG,CAAC,IAAI;SAElF,cAAc,WAAW,OAAO,GAAG;GAEvC,OAAO,IAAI,MAAM,SAAS,OAAO,QAAQ;IACvC,cAAc,MAAM,SAAS,MAAM,OAAO;KAAE;KAAO,SAAS;KAAM;IAAI,CAAC;IACvE,IAAI,uBAAuB,MACzB,cAAc,YAAY,YAAY;GAE1C,OACE,cAAc;GAEhB,IAAI,eAAe,QAAQ,MAAM,SAAS,YAAY,QAAW;IAC/D,MAAM,aACJ,MAAM,SAAS,cAAc,MAAM,SAAS,UAAU,MAAM,SAAS;IACvE,cACE,MAAM,SAAS,YAAY,SAAS,aAChC,IAAI,YAAY,IAChB,MAAM,SAAS;GACvB;GAIA,SAAS,QAAQ,KAAK,WAAW;GACjC,OAAO;EACT,GACA,CAAC,CACH;EAGA,IAAI,KAAK,UAAU,UAAU,QAAQ;GACnC,MAAM,EAAE,IAAI,KAAK,GAAG,kBAAkB;GAEtC,MAAM,YAAa,KAAK,SAAS,SAAS,OAAoB;IAC5D,OAAO;IACP,SAAS;IACT;GACF,CAAC;GACD,IAAI,aAAa,OAAO,cAAc,UACpC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAoC,GAC5E,SAAS,QAAQ,KAAK,iBAAiB,OAAO,MAAM,YAAY,IAAI,KAAK;EAG/E;EAEA,OAAO;CACT;AACF;AAKA,SAAS,gBAAgB,MAA0C,QAAiB;CAElF,MAAM,eAAe,MAAM,UAAU;CACrC,IAAI,CAAC,cACH,OAAO,CAAC;CAEV,MAAM,EAAE,IAAI,KAAK,GAAG,cAAc;CAClC,MAAM,SAAmC,CAAC;CAE1C,AAAC,aACC;EAAE;EAAW,WAAW;EAAM,SAAS;CAAK,IAC3C,OAAe,YAAoB;EAClC,OAAO,KAAK;GAAE;GAAS,MAAM,CAAC,KAAK;EAAE,CAAC;CACxC,CACF;CACA,OAAO;AACT;;;;;;;;;;;;AAaA,SAAgB,qBAEd,YACA,MAEA,MACA;CACA,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,UAAmB;GAC5B,MAAM,SAAS,KAAK,KAAK;GACzB,MAAM,SAAS,gBAAgB,MAAM,MAAM;GAC3C,IAAI,OAAO,SAAS,GAClB,OAAO,EAAE,OAAO;GAElB,MAAM,SAAS,WAAW,MAAM;IAC9B,OAAO;IACP,MAAM;IACN,SAAS;GACX,CAAC;GACD,IAAI,OAAO,QACT,OAAO;GAET,OAAO,EAAE,OAAO,OAAY;EAC9B;CACF,EACF;AACF"}
|
package/dist/vitest/index.d.mts
CHANGED
|
@@ -10,6 +10,7 @@ import { mockWorkflow } from "./mocks/workflow.mjs";
|
|
|
10
10
|
import "./mock.mjs";
|
|
11
11
|
import { RunWorkflowLocallyOptions, runWorkflowLocally } from "./workflow-local.mjs";
|
|
12
12
|
import { ExecutedQuery, KyselyMock, createKyselyMock } from "./mock-kysely.mjs";
|
|
13
|
+
import { PGliteClient, createKyselyPGlite } from "./pglite-kysely.mjs";
|
|
13
14
|
import { Plugin } from "vitest/config";
|
|
14
15
|
//#region src/vitest/index.d.ts
|
|
15
16
|
/**
|
|
@@ -71,4 +72,4 @@ declare function tailorRuntime(options?: {
|
|
|
71
72
|
config?: string;
|
|
72
73
|
}): Plugin[];
|
|
73
74
|
//#endregion
|
|
74
|
-
export { type ExecutedQuery, type KyselyMock, type MockAigatewayOptions, type MockAuthconnectionOptions, type MockFileOptions, type MockIconvOptions, type MockIdpOptions, type MockLoggerOptions, type MockSecretmanagerOptions, type MockTailordbOptions, type QueryBehavior, type QueryMatch, type QueryMatcher, type RunWorkflowLocallyOptions, createKyselyMock, mockAigateway, mockAuthconnection, mockFile, mockIconv, mockIdp, mockLogger, mockSecretmanager, mockTailordb, mockWorkflow, runWorkflowLocally, tailorRuntime };
|
|
75
|
+
export { type ExecutedQuery, type KyselyMock, type MockAigatewayOptions, type MockAuthconnectionOptions, type MockFileOptions, type MockIconvOptions, type MockIdpOptions, type MockLoggerOptions, type MockSecretmanagerOptions, type MockTailordbOptions, type PGliteClient, type QueryBehavior, type QueryMatch, type QueryMatcher, type RunWorkflowLocallyOptions, createKyselyMock, createKyselyPGlite, mockAigateway, mockAuthconnection, mockFile, mockIconv, mockIdp, mockLogger, mockSecretmanager, mockTailordb, mockWorkflow, runWorkflowLocally, tailorRuntime };
|
package/dist/vitest/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { i as getRegisteredJob, t as START_DEFAULT } from "../registry-BIGVUrMB.mjs";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as getWaitPointKey, c as readWorkflowTestEnv, i as getWaitPointInvoker, o as buildJobContext, s as clearWorkflowTestEnv, u as writeWorkflowTestEnv } from "../wait-point-invoker-__oE88_P.mjs";
|
|
3
3
|
import { t as assertDefined } from "../assert-DBxo8jPo.mjs";
|
|
4
4
|
import { n as isNodeBuiltinImport, t as getNodeBuiltinMessage } from "../node-builtins-CmaL2Cbq.mjs";
|
|
5
5
|
import { t as platformSerialize } from "../platform-serialize-RoRtBS0v.mjs";
|
|
6
6
|
import { i as withDispose, n as tailorRoot, r as tailordbRoot, t as mockSecretmanager } from "../secretmanager-IY4UvinW.mjs";
|
|
7
7
|
import { isEqual } from "es-toolkit";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { ColumnNode, InsertQueryNode, Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler, PrimitiveValueListNode, ReferenceNode, UpdateQueryNode, ValueListNode, ValueNode, ValuesNode } from "kysely";
|
|
9
|
+
import { ColumnNode, CompiledQuery, InsertQueryNode, Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler, PrimitiveValueListNode, ReferenceNode, UpdateQueryNode, ValueListNode, ValueNode, ValuesNode } from "kysely";
|
|
10
10
|
import { dirname, isAbsolute, matchesGlob, relative, resolve } from "node:path";
|
|
11
11
|
import { vi } from "vitest";
|
|
12
12
|
|
|
@@ -1177,6 +1177,8 @@ function mockTailordb(options = {}) {
|
|
|
1177
1177
|
|
|
1178
1178
|
//#endregion
|
|
1179
1179
|
//#region src/vitest/mocks/workflow.ts
|
|
1180
|
+
const baseInvokerWait = /* @__PURE__ */ new WeakMap();
|
|
1181
|
+
const baseInvokerResolve = /* @__PURE__ */ new WeakMap();
|
|
1180
1182
|
const originalProcedures = /* @__PURE__ */ new WeakMap();
|
|
1181
1183
|
function replaceProcedure(target, key, current) {
|
|
1182
1184
|
const original = originalProcedures.get(current) ?? current;
|
|
@@ -1226,7 +1228,56 @@ function mockWorkflow() {
|
|
|
1226
1228
|
const jobSpies = /* @__PURE__ */ new Map();
|
|
1227
1229
|
const workflowSpies = /* @__PURE__ */ new Map();
|
|
1228
1230
|
const waitPointMocks = /* @__PURE__ */ new Map();
|
|
1231
|
+
const keyDispatchers = /* @__PURE__ */ new Map();
|
|
1229
1232
|
const scopedMocks = /* @__PURE__ */ new Set();
|
|
1233
|
+
const installKeyDispatcher = (definition) => {
|
|
1234
|
+
const cached = keyDispatchers.get(definition);
|
|
1235
|
+
if (cached) return cached;
|
|
1236
|
+
const invoker = getWaitPointInvoker(definition);
|
|
1237
|
+
if (!invoker) throw new Error("waitPointWith expects a wait point definition created by createWaitPoint or createWaitPoints.");
|
|
1238
|
+
const record = invoker;
|
|
1239
|
+
const prevWait = invoker.wait;
|
|
1240
|
+
const prevResolve = invoker.resolve;
|
|
1241
|
+
const baseWait = baseInvokerWait.get(prevWait) ?? prevWait;
|
|
1242
|
+
const baseResolve = baseInvokerResolve.get(prevResolve) ?? prevResolve;
|
|
1243
|
+
const slot = {
|
|
1244
|
+
perKey: /* @__PURE__ */ new Map(),
|
|
1245
|
+
originalWait: (key, payload) => baseWait.call(invoker, key, payload),
|
|
1246
|
+
originalResolve: (key, executionId, callback) => baseResolve.call(invoker, key, executionId, callback)
|
|
1247
|
+
};
|
|
1248
|
+
const dispatchWait = (key, payload) => {
|
|
1249
|
+
const mock = slot.perKey.get(key);
|
|
1250
|
+
if (!mock) return slot.originalWait(key, payload);
|
|
1251
|
+
return payload === void 0 ? mock.wait() : mock.wait(payload);
|
|
1252
|
+
};
|
|
1253
|
+
const dispatchResolve = async (key, executionId, callback) => {
|
|
1254
|
+
const mock = slot.perKey.get(key);
|
|
1255
|
+
if (!mock) return slot.originalResolve(key, executionId, callback);
|
|
1256
|
+
await mock.resolve(executionId, callback);
|
|
1257
|
+
};
|
|
1258
|
+
baseInvokerWait.set(dispatchWait, baseWait);
|
|
1259
|
+
baseInvokerResolve.set(dispatchResolve, baseResolve);
|
|
1260
|
+
record.wait = dispatchWait;
|
|
1261
|
+
record.resolve = dispatchResolve;
|
|
1262
|
+
const eachSpy = (fn) => {
|
|
1263
|
+
for (const mock of slot.perKey.values()) {
|
|
1264
|
+
const pair = mock;
|
|
1265
|
+
fn(pair.wait);
|
|
1266
|
+
fn(pair.resolve);
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
scopedMocks.add({
|
|
1270
|
+
mockClear: () => eachSpy((spy) => spy.mockClear()),
|
|
1271
|
+
mockReset: () => eachSpy((spy) => spy.mockReset()),
|
|
1272
|
+
restore: () => {
|
|
1273
|
+
record.wait = prevWait;
|
|
1274
|
+
record.resolve = prevResolve;
|
|
1275
|
+
keyDispatchers.delete(definition);
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
keyDispatchers.set(definition, slot);
|
|
1279
|
+
return slot;
|
|
1280
|
+
};
|
|
1230
1281
|
const defaultExecJob = (jobName, _args, _options) => {
|
|
1231
1282
|
throw new Error(`No workflow job mock for "${jobName}". Call mockWorkflow().setJobHandler(...) or enqueueResult(...), or use runWorkflowLocally() for local workflow execution.`);
|
|
1232
1283
|
};
|
|
@@ -1325,6 +1376,37 @@ function mockWorkflow() {
|
|
|
1325
1376
|
return waitPointMock;
|
|
1326
1377
|
},
|
|
1327
1378
|
/**
|
|
1379
|
+
* Get stable, typed mocks for one param binding of a parameterized wait point.
|
|
1380
|
+
* Calls made with other bindings fall through to the platform mock.
|
|
1381
|
+
* @param definition - Parameterized wait point definition to mock
|
|
1382
|
+
* @param params - Param binding to intercept, as passed to `.with()`
|
|
1383
|
+
* @returns Typed wait point mock control object
|
|
1384
|
+
*/
|
|
1385
|
+
waitPointWith(definition, params) {
|
|
1386
|
+
const key = getWaitPointKey(definition.with(params));
|
|
1387
|
+
if (key === void 0) throw new Error("waitPointWith expects a wait point declared with $params. Use waitPoint() for a wait point with a fixed key.");
|
|
1388
|
+
const slot = installKeyDispatcher(definition);
|
|
1389
|
+
const existing = slot.perKey.get(key);
|
|
1390
|
+
if (existing) return existing;
|
|
1391
|
+
const waitSpy = vi.fn((payload) => Promise.resolve(slot.originalWait(key, payload)));
|
|
1392
|
+
const resolveSpy = vi.fn((executionId, callback) => slot.originalResolve(key, executionId, callback));
|
|
1393
|
+
const waitPointMock = {
|
|
1394
|
+
wait: waitSpy,
|
|
1395
|
+
resolve: resolveSpy,
|
|
1396
|
+
/**
|
|
1397
|
+
* Invoke the next and subsequent resolve callbacks with a wait payload.
|
|
1398
|
+
* @param payload - Payload originally supplied to the wait point
|
|
1399
|
+
*/
|
|
1400
|
+
setResolvePayload(payload) {
|
|
1401
|
+
resolveSpy.mockImplementation(async (_executionId, callback) => {
|
|
1402
|
+
platformSerialize(await callback(platformSerialize(payload)));
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
slot.perKey.set(key, waitPointMock);
|
|
1407
|
+
return waitPointMock;
|
|
1408
|
+
},
|
|
1409
|
+
/**
|
|
1328
1410
|
* Set a fallback job handler. Called when the enqueue queue is empty.
|
|
1329
1411
|
* @param handler - Function returning a result for a job name, args, and options
|
|
1330
1412
|
*/
|
|
@@ -1751,6 +1833,88 @@ function createKyselyMock() {
|
|
|
1751
1833
|
};
|
|
1752
1834
|
}
|
|
1753
1835
|
|
|
1836
|
+
//#endregion
|
|
1837
|
+
//#region src/vitest/pglite-kysely.ts
|
|
1838
|
+
/**
|
|
1839
|
+
* Kysely adapter for running migration scripts against PGlite.
|
|
1840
|
+
*
|
|
1841
|
+
* Accepts the PGlite client structurally so the SDK does not depend on
|
|
1842
|
+
* `@electric-sql/pglite`; users install it themselves as a devDependency.
|
|
1843
|
+
*/
|
|
1844
|
+
var PGliteConnection = class {
|
|
1845
|
+
#client;
|
|
1846
|
+
constructor(client) {
|
|
1847
|
+
this.#client = client;
|
|
1848
|
+
}
|
|
1849
|
+
async executeQuery(compiledQuery) {
|
|
1850
|
+
const result = await this.#client.query(compiledQuery.sql, [...compiledQuery.parameters]);
|
|
1851
|
+
return {
|
|
1852
|
+
rows: result.rows,
|
|
1853
|
+
numAffectedRows: BigInt(result.affectedRows ?? 0)
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
streamQuery() {
|
|
1857
|
+
throw new Error("createKyselyPGlite: streaming is not supported");
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
var PGliteDriver = class {
|
|
1861
|
+
#client;
|
|
1862
|
+
constructor(client) {
|
|
1863
|
+
this.#client = client;
|
|
1864
|
+
}
|
|
1865
|
+
async init() {}
|
|
1866
|
+
async acquireConnection() {
|
|
1867
|
+
return new PGliteConnection(this.#client);
|
|
1868
|
+
}
|
|
1869
|
+
async beginTransaction(connection, settings) {
|
|
1870
|
+
const parts = settings.isolationLevel ? ["start transaction", `isolation level ${settings.isolationLevel}`] : ["begin"];
|
|
1871
|
+
if (settings.accessMode) parts.push(settings.accessMode);
|
|
1872
|
+
await connection.executeQuery(CompiledQuery.raw(parts.join(" ")));
|
|
1873
|
+
}
|
|
1874
|
+
async commitTransaction(connection) {
|
|
1875
|
+
await connection.executeQuery(CompiledQuery.raw("commit"));
|
|
1876
|
+
}
|
|
1877
|
+
async rollbackTransaction(connection) {
|
|
1878
|
+
await connection.executeQuery(CompiledQuery.raw("rollback"));
|
|
1879
|
+
}
|
|
1880
|
+
async releaseConnection() {}
|
|
1881
|
+
async destroy() {
|
|
1882
|
+
await this.#client.close();
|
|
1883
|
+
}
|
|
1884
|
+
};
|
|
1885
|
+
/**
|
|
1886
|
+
* Create a Kysely instance backed by a PGlite in-memory Postgres, for
|
|
1887
|
+
* executing a migration script's queries against real data in tests.
|
|
1888
|
+
* Pass the migration's schema as the type argument, e.g.
|
|
1889
|
+
* `createKyselyPGlite<Database>(new PGlite())`.
|
|
1890
|
+
*
|
|
1891
|
+
* PGlite runs full PostgreSQL while TailorDB supports a subset of it, so a
|
|
1892
|
+
* statement passing here can still be rejected by the platform; keep a
|
|
1893
|
+
* statement-level test (see `createKyselyMock`) alongside.
|
|
1894
|
+
* @param client - A `PGlite` instance from `@electric-sql/pglite`
|
|
1895
|
+
* @returns A Kysely instance that executes queries on the client and closes it on `destroy()`
|
|
1896
|
+
* @example
|
|
1897
|
+
* ```typescript
|
|
1898
|
+
* // migrations/0005/migrate.test.ts
|
|
1899
|
+
* import { PGlite } from "@electric-sql/pglite";
|
|
1900
|
+
* import { createKyselyPGlite } from "@tailor-platform/sdk/vitest";
|
|
1901
|
+
* import type { Database } from "./db";
|
|
1902
|
+
* import { main } from "./migrate";
|
|
1903
|
+
*
|
|
1904
|
+
* const db = createKyselyPGlite<Database>(new PGlite());
|
|
1905
|
+
* // create tables matching db.ts, insert rows, then:
|
|
1906
|
+
* await db.transaction().execute((trx) => main(trx));
|
|
1907
|
+
* ```
|
|
1908
|
+
*/
|
|
1909
|
+
function createKyselyPGlite(client) {
|
|
1910
|
+
return new Kysely({ dialect: {
|
|
1911
|
+
createAdapter: () => new PostgresAdapter(),
|
|
1912
|
+
createDriver: () => new PGliteDriver(client),
|
|
1913
|
+
createIntrospector: (db) => new PostgresIntrospector(db),
|
|
1914
|
+
createQueryCompiler: () => new PostgresQueryCompiler()
|
|
1915
|
+
} });
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1754
1918
|
//#endregion
|
|
1755
1919
|
//#region src/vitest/index.ts
|
|
1756
1920
|
/**
|
|
@@ -1813,5 +1977,5 @@ function tailorRuntime(options) {
|
|
|
1813
1977
|
}
|
|
1814
1978
|
|
|
1815
1979
|
//#endregion
|
|
1816
|
-
export { createKyselyMock, mockAigateway, mockAuthconnection, mockFile, mockIconv, mockIdp, mockLogger, mockSecretmanager, mockTailordb, mockWorkflow, runWorkflowLocally, tailorRuntime };
|
|
1980
|
+
export { createKyselyMock, createKyselyPGlite, mockAigateway, mockAuthconnection, mockFile, mockIconv, mockIdp, mockLogger, mockSecretmanager, mockTailordb, mockWorkflow, runWorkflowLocally, tailorRuntime };
|
|
1817
1981
|
//# sourceMappingURL=index.mjs.map
|