@tailor-platform/sdk 2.0.0-next.0 → 2.0.0-next.2
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 +161 -0
- package/dist/application-Dtqap5jM.mjs +3 -0
- package/dist/{client-CobIRHl-.mjs → application-XuMWK4eq.mjs} +5869 -25
- package/dist/application-XuMWK4eq.mjs.map +1 -0
- package/dist/{assert-CKfwrmCV.mjs → assert-DBxo8jPo.mjs} +1 -2
- package/dist/{assert-CKfwrmCV.mjs.map → assert-DBxo8jPo.mjs.map} +1 -1
- package/dist/{authconnection-D8SJGMpj.mjs → authconnection-D2MhtTN5.mjs} +2 -3
- package/dist/{authconnection-D8SJGMpj.mjs.map → authconnection-D2MhtTN5.mjs.map} +1 -1
- package/dist/{authconnection-BIYzEh2p.d.mts → authconnection-DvUQAjQS.d.mts} +1 -1
- package/dist/{brand-DlnJ375c.mjs → brand-Eo4pLXPJ.mjs} +1 -2
- package/dist/{brand-DlnJ375c.mjs.map → brand-Eo4pLXPJ.mjs.map} +1 -1
- package/dist/cli/index.mjs +241 -237
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +547 -620
- package/dist/cli/lib.mjs +9 -11
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/completion/zsh-worker.zsh +4108 -0
- package/dist/configure/index.d.mts +9 -7
- package/dist/configure/index.mjs +76 -40
- package/dist/configure/index.mjs.map +1 -1
- package/dist/{context-s0lxhu8_.mjs → context-Bd266-ru.mjs} +2 -3
- package/dist/context-Bd266-ru.mjs.map +1 -0
- package/dist/{context-CUBwSBq4.d.mts → context-C2lEi9uw.d.mts} +7 -28
- package/dist/{crashreport-D1wKBJ8N.mjs → crashreport-BMWcxeSE.mjs} +1 -2
- package/dist/{crashreport-BhD0y14F.mjs → crashreport-DFq-vsU0.mjs} +21 -14
- package/dist/{crashreport-BhD0y14F.mjs.map → crashreport-DFq-vsU0.mjs.map} +1 -1
- package/dist/{enum-constants-C7DaWeQo.mjs → enum-constants-j9QBF0cB.mjs} +1 -2
- package/dist/enum-constants-j9QBF0cB.mjs.map +1 -0
- package/dist/{errors-EsY4XO6O.mjs → errors-Dtf2WPaW.mjs} +1 -2
- package/dist/{errors-EsY4XO6O.mjs.map → errors-Dtf2WPaW.mjs.map} +1 -1
- package/dist/{field-C4zdJLW5.mjs → field-DOsJCPFa.mjs} +1 -2
- package/dist/field-DOsJCPFa.mjs.map +1 -0
- package/dist/{file-B58Dm-2P.mjs → file-BbdFGdMV.mjs} +3 -12
- package/dist/file-BbdFGdMV.mjs.map +1 -0
- package/dist/{file-BzK8z3X-.d.mts → file-Dq3NIt_F.d.mts} +3 -42
- package/dist/{file-utils-BHPxPXmn.mjs → file-utils-CYZnO1pX.mjs} +6 -7
- package/dist/file-utils-CYZnO1pX.mjs.map +1 -0
- package/dist/{globals-ByrCoDip.mjs → globals-Cf0sxIt8.mjs} +53 -5
- package/dist/globals-Cf0sxIt8.mjs.map +1 -0
- package/dist/http-adapter.generated-DFsXDdm5.d.mts +581 -0
- package/dist/{iconv-kwrmd1U_.d.mts → iconv-Co-TOPuH.d.mts} +1 -1
- package/dist/{iconv-DreIffeM.mjs → iconv-D2vi8G36.mjs} +2 -3
- package/dist/{iconv-DreIffeM.mjs.map → iconv-D2vi8G36.mjs.map} +1 -1
- package/dist/{idp-Ch95ag8h.mjs → idp-BDbK5gjm.mjs} +2 -3
- package/dist/{idp-Ch95ag8h.mjs.map → idp-BDbK5gjm.mjs.map} +1 -1
- package/dist/{idp-BlBPtXJ-.d.mts → idp-DrhVrLmV.d.mts} +1 -1
- package/dist/{index-CLxubakC.d.mts → index-BI-_j9Z3.d.mts} +49 -261
- package/dist/{index-CPRnOjjt.d.mts → index-C4JirJH8.d.mts} +2 -2
- package/dist/{index-CQZVJ5SX.d.mts → index-CZfWhr0a.d.mts} +2 -2
- package/dist/{index-DRhMpdnA.d.mts → index-Cg8VKAdN.d.mts} +8 -8
- package/dist/{index-CfRFkXIO.d.mts → index-DYRjoLXD.d.mts} +2 -2
- package/dist/index-lFpcjHPU.d.mts +201 -0
- package/dist/{index-DUupuPhZ.d.mts → index-nW7hE6oE.d.mts} +2 -2
- package/dist/{interceptor-DOqRkCya.mjs → interceptor-D-q1rvRl.mjs} +1 -2
- package/dist/{interceptor-DOqRkCya.mjs.map → interceptor-D-q1rvRl.mjs.map} +1 -1
- package/dist/kysely/index.mjs +0 -1
- package/dist/kysely/index.mjs.map +1 -1
- package/dist/{kysely-type-D1e0Vwkd.mjs → kysely-type-DR8uzZTA.mjs} +2 -3
- package/dist/kysely-type-DR8uzZTA.mjs.map +1 -0
- package/dist/{logger-DpJyJvNz.mjs → logger-CxF-Ex5d.mjs} +1 -2
- package/dist/{logger-DpJyJvNz.mjs.map → logger-CxF-Ex5d.mjs.map} +1 -1
- package/dist/{mock-DMgIygjE.mjs → mock-FPxmnt-y.mjs} +9 -56
- package/dist/mock-FPxmnt-y.mjs.map +1 -0
- package/dist/{multiline-Cf9ODpr1.mjs → multiline-sfHpTZZK.mjs} +1 -2
- package/dist/{multiline-Cf9ODpr1.mjs.map → multiline-sfHpTZZK.mjs.map} +1 -1
- package/dist/{package-json-DcQApfPQ.mjs → package-json-8b0O9TlX.mjs} +1 -2
- package/dist/{package-json-DcQApfPQ.mjs.map → package-json-8b0O9TlX.mjs.map} +1 -1
- package/dist/package-json-Cv2Z-TqQ.mjs +3 -0
- package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
- package/dist/plugin/builtin/enum-constants/index.mjs +1 -2
- package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
- package/dist/plugin/builtin/file-utils/index.mjs +1 -2
- package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
- package/dist/plugin/builtin/kysely-type/index.mjs +1 -2
- package/dist/plugin/builtin/seed/index.d.mts +1 -1
- package/dist/plugin/builtin/seed/index.mjs +1 -2
- package/dist/plugin/index.d.mts +4 -5
- package/dist/plugin/index.mjs +0 -1
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/registry-DH4m7eYo.mjs +53 -0
- package/dist/registry-DH4m7eYo.mjs.map +1 -0
- package/dist/{repl-editor-CJG3sz7A.mjs → repl-editor-DmGr9zMw.mjs} +2 -3
- package/dist/{repl-editor-CJG3sz7A.mjs.map → repl-editor-DmGr9zMw.mjs.map} +1 -1
- package/dist/{chunk-BkoGK1jX.mjs → rolldown-runtime-DXywRVcq.mjs} +0 -1
- package/dist/runtime/authconnection.d.mts +1 -1
- package/dist/runtime/authconnection.mjs +1 -2
- package/dist/runtime/context.d.mts +1 -1
- package/dist/runtime/context.mjs +1 -2
- package/dist/runtime/file.d.mts +2 -2
- package/dist/runtime/file.mjs +2 -3
- package/dist/runtime/globals.d.mts +8 -41
- package/dist/runtime/globals.mjs +0 -1
- package/dist/runtime/iconv.d.mts +1 -1
- package/dist/runtime/iconv.mjs +1 -2
- package/dist/runtime/idp.d.mts +1 -1
- package/dist/runtime/idp.mjs +1 -2
- package/dist/runtime/index.d.mts +8 -8
- package/dist/runtime/index.mjs +7 -8
- package/dist/runtime/secretmanager.d.mts +1 -1
- package/dist/runtime/secretmanager.mjs +1 -2
- package/dist/runtime/workflow.d.mts +2 -2
- package/dist/runtime/workflow.mjs +1 -2
- package/dist/{runtime-C7qTBDD2.mjs → runtime-CY4JvrDj.mjs} +1069 -542
- package/dist/runtime-CY4JvrDj.mjs.map +1 -0
- package/dist/{schema-1msIhXwA.mjs → schema-Dtw9Orye.mjs} +18 -16
- package/dist/schema-Dtw9Orye.mjs.map +1 -0
- package/dist/{secret-file-CWzF8rry.mjs → secret-file-VSVGy1V0.mjs} +27 -3
- package/dist/{secret-file-CWzF8rry.mjs.map → secret-file-VSVGy1V0.mjs.map} +1 -1
- package/dist/{secretmanager-CKLB3wAQ.d.mts → secretmanager-B3n4KHfm.d.mts} +1 -1
- package/dist/{secretmanager-B9h-U_8U.mjs → secretmanager-BVxw3ih_.mjs} +2 -3
- package/dist/{secretmanager-B9h-U_8U.mjs.map → secretmanager-BVxw3ih_.mjs.map} +1 -1
- package/dist/seed/index.mjs +0 -1
- package/dist/seed/index.mjs.map +1 -1
- package/dist/{seed-BH2FbrPV.mjs → seed-izIEyP3z.mjs} +7 -19
- package/dist/seed-izIEyP3z.mjs.map +1 -0
- package/dist/service-DCqIWibD.mjs +3 -0
- package/dist/{service-DMohAx8a2.mjs → service-DU1mVzri2.mjs} +3 -4
- package/dist/{service-DMohAx8a2.mjs.map → service-DU1mVzri2.mjs.map} +1 -1
- package/dist/{service-wI3Hvrgx.mjs → service-DjyqbCaJ.mjs} +9 -10
- package/dist/service-DjyqbCaJ.mjs.map +1 -0
- package/dist/{telemetry-BQbbVo2t.mjs → telemetry-CdqJEzkj.mjs} +2 -3
- package/dist/{telemetry-BQbbVo2t.mjs.map → telemetry-CdqJEzkj.mjs.map} +1 -1
- package/dist/telemetry-ClwW5ohF.mjs +3 -0
- package/dist/test-env-key-D7UkZp99.mjs +75 -0
- package/dist/test-env-key-D7UkZp99.mjs.map +1 -0
- package/dist/type-source-DH_LH20p.mjs +13 -0
- package/dist/type-source-DH_LH20p.mjs.map +1 -0
- package/dist/types-74etvaxy.mjs +4 -0
- package/dist/{plugin-C_FyVSdl.d.mts → types-BDRml5C3.d.mts} +128 -188
- package/dist/{types-CmzfQP_m.mjs → types-BQijbo4m.mjs} +10 -11
- package/dist/types-BQijbo4m.mjs.map +1 -0
- package/dist/types-BX4q6Mo6.d.mts +339 -0
- package/dist/types-BZ7QKVE8.d.mts +21 -0
- package/dist/{tailordb-BlBGmQK-.d.mts → types-CdcQh4Z2.d.mts} +92 -242
- package/dist/utils/test/index.d.mts +6 -14
- package/dist/utils/test/index.mjs +4 -14
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/vitest/environment.mjs +1 -2
- package/dist/vitest/environment.mjs.map +1 -1
- package/dist/vitest/index.d.mts +42 -5
- package/dist/vitest/index.mjs +133 -4
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/setup.mjs +2 -3
- package/dist/vitest/setup.mjs.map +1 -1
- package/dist/{workflow--aPbA8Uq.mjs → workflow-BOmaZwwG.mjs} +9 -5
- package/dist/workflow-BOmaZwwG.mjs.map +1 -0
- package/dist/{workflow-CMamswkK.d.mts → workflow-BVy4XWjS.d.mts} +15 -10
- package/dist/workflow.generated-ClEjBYhm.d.mts +671 -0
- package/docs/cli/application.md +0 -2
- package/docs/cli/completion.md +3 -0
- package/docs/cli/crashreport.md +0 -2
- package/docs/cli/executor.md +53 -0
- package/docs/cli/function.md +1 -1
- package/docs/cli/setup.md +35 -33
- package/docs/cli/user.md +3 -3
- package/docs/cli/workflow.md +157 -20
- package/docs/cli/workspace.md +3 -3
- package/docs/cli-reference.md +26 -20
- package/docs/configuration.md +0 -2
- package/docs/github-actions.md +29 -16
- package/docs/migration/v2.md +475 -0
- package/docs/plugin/custom.md +2 -2
- package/docs/plugin/index.md +1 -1
- package/docs/runtime.md +4 -4
- package/docs/services/aigateway.md +97 -0
- package/docs/services/auth.md +31 -14
- package/docs/services/executor.md +3 -5
- package/docs/services/resolver.md +8 -10
- package/docs/services/tailordb.md +15 -13
- package/docs/services/workflow.md +17 -19
- package/docs/testing.md +75 -56
- package/package.json +18 -17
- package/dist/actor-J2gJ0eK5.d.mts +0 -24
- package/dist/application-76hhIhnJ.mjs +0 -5594
- package/dist/application-76hhIhnJ.mjs.map +0 -1
- package/dist/application-av2raLs6.mjs +0 -4
- package/dist/cli/skills.d.mts +0 -1
- package/dist/cli/skills.mjs +0 -22
- package/dist/cli/skills.mjs.map +0 -1
- package/dist/client-C68VWo4g.mjs +0 -4
- package/dist/client-CobIRHl-.mjs.map +0 -1
- package/dist/context-s0lxhu8_.mjs.map +0 -1
- package/dist/enum-constants-C7DaWeQo.mjs.map +0 -1
- package/dist/env-B-g-qgE4.d.mts +0 -7
- package/dist/field-C4zdJLW5.mjs.map +0 -1
- package/dist/file-B58Dm-2P.mjs.map +0 -1
- package/dist/file-utils-BHPxPXmn.mjs.map +0 -1
- package/dist/globals-ByrCoDip.mjs.map +0 -1
- package/dist/job-BpsFXPbi.mjs +0 -54
- package/dist/job-BpsFXPbi.mjs.map +0 -1
- package/dist/kysely-type-D1e0Vwkd.mjs.map +0 -1
- package/dist/mock-DMgIygjE.mjs.map +0 -1
- package/dist/package-json-wzO6nV9O.mjs +0 -4
- package/dist/registry-D0uB0OrK.mjs +0 -178
- package/dist/registry-D0uB0OrK.mjs.map +0 -1
- package/dist/runtime-C7qTBDD2.mjs.map +0 -1
- package/dist/schema-1msIhXwA.mjs.map +0 -1
- package/dist/seed-BH2FbrPV.mjs.map +0 -1
- package/dist/service-BHQIerYh.mjs +0 -4
- package/dist/service-wI3Hvrgx.mjs.map +0 -1
- package/dist/telemetry-w92bvGdC.mjs +0 -4
- package/dist/types-2Be3wSMc.mjs +0 -5
- package/dist/types-CmzfQP_m.mjs.map +0 -1
- package/dist/workflow--aPbA8Uq.mjs.map +0 -1
- package/dist/workflow.generated-Bf1tWylx.d.mts +0 -1416
- package/docs/generator/builtin.md +0 -257
- package/docs/generator/custom.md +0 -147
- package/docs/generator/index.md +0 -66
package/dist/vitest/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as TailorEnv } from "../types-BZ7QKVE8.mjs";
|
|
2
|
+
import { i as Workflow, s as WorkflowJob } from "../index-lFpcjHPU.mjs";
|
|
2
3
|
import { Kysely, OperationNode, OperationNodeKind, Transaction } from "kysely";
|
|
3
4
|
import { Mock } from "vitest";
|
|
4
5
|
import { Plugin } from "vitest/config";
|
|
@@ -135,8 +136,8 @@ declare function mockTailordb(): {
|
|
|
135
136
|
* ```
|
|
136
137
|
*/
|
|
137
138
|
declare function mockWorkflow(): {
|
|
138
|
-
/** The `triggerJobFunction` `vi.fn`. */triggerJobFunction: Mock<(jobName: string,
|
|
139
|
-
triggerWorkflow: Mock<(
|
|
139
|
+
/** The `triggerJobFunction` `vi.fn`. */triggerJobFunction: Mock<(jobName: string, _args?: unknown) => unknown>; /** The `triggerWorkflow` `vi.fn`. */
|
|
140
|
+
triggerWorkflow: Mock<(_workflowName: string, _args?: unknown, _options?: TriggerWorkflowOptions) => Promise<string>>; /** The `wait` `vi.fn`. */
|
|
140
141
|
wait: Mock<(_key: string, _payload?: unknown) => unknown>; /** The `resolve` `vi.fn`. */
|
|
141
142
|
resolve: Mock<(_executionId: string, _key: string, _callback: (payload: unknown) => unknown) => Promise<void>>;
|
|
142
143
|
/**
|
|
@@ -173,7 +174,7 @@ declare function mockWorkflow(): {
|
|
|
173
174
|
*/
|
|
174
175
|
setWaitHandler: SetWaitHandler;
|
|
175
176
|
/**
|
|
176
|
-
* Set the `env` passed to job bodies invoked via `
|
|
177
|
+
* Set the `env` passed to job bodies invoked via `runWorkflowLocally()`.
|
|
177
178
|
* Cleared on dispose / reset.
|
|
178
179
|
* @param env - Env passed to job bodies.
|
|
179
180
|
*/
|
|
@@ -329,6 +330,42 @@ declare function mockFile(): {
|
|
|
329
330
|
reset(): void;
|
|
330
331
|
} & Disposable;
|
|
331
332
|
//#endregion
|
|
333
|
+
//#region src/vitest/workflow-local.d.ts
|
|
334
|
+
type AnyWorkflowJob = WorkflowJob<string, any, any>;
|
|
335
|
+
type AnyWorkflow = Workflow<AnyWorkflowJob>;
|
|
336
|
+
type WorkflowInput<W extends AnyWorkflow> = W["mainJob"] extends WorkflowJob<string, infer I, any> ? I : never;
|
|
337
|
+
type WorkflowOutput<W extends AnyWorkflow> = W["mainJob"] extends WorkflowJob<string, any, infer O> ? Awaited<O> : never;
|
|
338
|
+
interface RunWorkflowLocallyOptions {
|
|
339
|
+
/** Env passed to workflow job bodies during this local run. */
|
|
340
|
+
env?: TailorEnv;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Run a workflow's main job and dependent job triggers locally with real job bodies.
|
|
344
|
+
*
|
|
345
|
+
* Use this for local full-chain workflow tests. Regular `.trigger()` calls
|
|
346
|
+
* delegate to the platform workflow runtime and should be mocked with
|
|
347
|
+
* `mockWorkflow()` when you are not intentionally running the local chain.
|
|
348
|
+
* @param workflow - Workflow definition to run
|
|
349
|
+
* @returns The main job output
|
|
350
|
+
*/
|
|
351
|
+
declare function runWorkflowLocally<W extends Workflow<WorkflowJob<string, undefined, any>>>(workflow: W): Promise<WorkflowOutput<W>>;
|
|
352
|
+
/**
|
|
353
|
+
* Run a no-input workflow locally with optional runner settings.
|
|
354
|
+
* @param workflow - Workflow definition to run
|
|
355
|
+
* @param args - Must be `undefined` for no-input workflows
|
|
356
|
+
* @param options - Local runner options
|
|
357
|
+
* @returns The main job output
|
|
358
|
+
*/
|
|
359
|
+
declare function runWorkflowLocally<W extends Workflow<WorkflowJob<string, undefined, any>>>(workflow: W, args: undefined, options?: RunWorkflowLocallyOptions): Promise<WorkflowOutput<W>>;
|
|
360
|
+
/**
|
|
361
|
+
* Run a workflow locally with real job bodies.
|
|
362
|
+
* @param workflow - Workflow definition to run
|
|
363
|
+
* @param args - Arguments passed to the workflow's main job
|
|
364
|
+
* @param options - Local runner options
|
|
365
|
+
* @returns The main job output
|
|
366
|
+
*/
|
|
367
|
+
declare function runWorkflowLocally<W extends AnyWorkflow>(workflow: W, args: WorkflowInput<W>, options?: RunWorkflowLocallyOptions): Promise<WorkflowOutput<W>>;
|
|
368
|
+
//#endregion
|
|
332
369
|
//#region src/vitest/mock-kysely.d.ts
|
|
333
370
|
/** A single statement executed against the mock, captured in order. */
|
|
334
371
|
interface ExecutedQuery {
|
|
@@ -447,5 +484,5 @@ declare function tailorRuntime(options?: {
|
|
|
447
484
|
config?: string;
|
|
448
485
|
}): Plugin[];
|
|
449
486
|
//#endregion
|
|
450
|
-
export { type ExecutedQuery, type KyselyMock, createKyselyMock, mockAuthconnection, mockFile, mockIconv, mockIdp, mockSecretmanager, mockTailordb, mockWorkflow, tailorRuntime };
|
|
487
|
+
export { type ExecutedQuery, type KyselyMock, type RunWorkflowLocallyOptions, createKyselyMock, mockAuthconnection, mockFile, mockIconv, mockIdp, mockSecretmanager, mockTailordb, mockWorkflow, runWorkflowLocally, tailorRuntime };
|
|
451
488
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/vitest/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
1
|
+
import { i as getRegisteredJob, t as TRIGGER_DEFAULT } from "../registry-DH4m7eYo.mjs";
|
|
2
|
+
import { a as writeWorkflowTestEnv, n as clearWorkflowTestEnv, r as readWorkflowTestEnv, t as buildJobContext } from "../test-env-key-D7UkZp99.mjs";
|
|
3
|
+
import { t as assertDefined } from "../assert-DBxo8jPo.mjs";
|
|
4
|
+
import { i as platformSerialize } from "../globals-Cf0sxIt8.mjs";
|
|
5
|
+
import { a as mockSecretmanager, i as mockIdp, n as mockFile, o as mockTailordb, r as mockIconv, s as mockWorkflow, t as mockAuthconnection } from "../mock-FPxmnt-y.mjs";
|
|
4
6
|
import { builtinModules } from "node:module";
|
|
5
7
|
import { fileURLToPath } from "node:url";
|
|
6
8
|
import { dirname, isAbsolute, matchesGlob, relative, resolve } from "node:path";
|
|
@@ -289,6 +291,133 @@ function createEnvironmentPlugin(options) {
|
|
|
289
291
|
};
|
|
290
292
|
}
|
|
291
293
|
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/vitest/workflow-local.ts
|
|
296
|
+
var PendingTrigger = class {
|
|
297
|
+
jobName;
|
|
298
|
+
args;
|
|
299
|
+
constructor(jobName, args) {
|
|
300
|
+
this.jobName = jobName;
|
|
301
|
+
this.args = args;
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
async function runWorkflowLocally(workflow, args, options) {
|
|
305
|
+
const root = globalThis;
|
|
306
|
+
const previousTailor = root.tailor;
|
|
307
|
+
const previousEnv = readWorkflowTestEnv();
|
|
308
|
+
const hasPreviousEnv = previousEnv !== void 0;
|
|
309
|
+
const runner = createLocalJobRunner();
|
|
310
|
+
if (options?.env !== void 0) writeWorkflowTestEnv({ ...options.env });
|
|
311
|
+
root.tailor = {
|
|
312
|
+
...previousTailor,
|
|
313
|
+
workflow: createLocalWorkflowRuntime(previousTailor?.workflow, runner.triggerJobFunction)
|
|
314
|
+
};
|
|
315
|
+
try {
|
|
316
|
+
return await runner.runJob(workflow.mainJob.name, args);
|
|
317
|
+
} finally {
|
|
318
|
+
if (previousTailor) root.tailor = previousTailor;
|
|
319
|
+
else delete root.tailor;
|
|
320
|
+
if (options?.env !== void 0) if (hasPreviousEnv) writeWorkflowTestEnv(previousEnv);
|
|
321
|
+
else clearWorkflowTestEnv();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function createLocalJobRunner() {
|
|
325
|
+
let activeExecution;
|
|
326
|
+
const triggerJobFunction = (jobName, args) => {
|
|
327
|
+
if (!activeExecution) throw new Error(`Cannot trigger workflow job "${jobName}" outside runWorkflowLocally() job execution.`);
|
|
328
|
+
if (activeExecution.pending) throw activeExecution.pending;
|
|
329
|
+
const serializedArgs = platformSerialize(args);
|
|
330
|
+
const index = activeExecution.cursor;
|
|
331
|
+
activeExecution.cursor += 1;
|
|
332
|
+
const cached = activeExecution.records[index];
|
|
333
|
+
if (cached) {
|
|
334
|
+
assertSameTrigger(cached, jobName, serializedArgs);
|
|
335
|
+
if (cached.status === "rejected") throw cached.error;
|
|
336
|
+
return platformSerialize(cached.result);
|
|
337
|
+
}
|
|
338
|
+
const pending = new PendingTrigger(jobName, serializedArgs);
|
|
339
|
+
activeExecution.pending = pending;
|
|
340
|
+
throw pending;
|
|
341
|
+
};
|
|
342
|
+
const runJob = async (name, args) => {
|
|
343
|
+
const body = getRegisteredJob(name);
|
|
344
|
+
if (!body) return null;
|
|
345
|
+
const records = [];
|
|
346
|
+
for (;;) {
|
|
347
|
+
const execution = {
|
|
348
|
+
records,
|
|
349
|
+
cursor: 0
|
|
350
|
+
};
|
|
351
|
+
const previousExecution = activeExecution;
|
|
352
|
+
activeExecution = execution;
|
|
353
|
+
try {
|
|
354
|
+
const out = await body(platformSerialize(args), buildJobContext());
|
|
355
|
+
if (execution.pending) {
|
|
356
|
+
await settlePendingTrigger(records, execution.pending, runJob);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (execution.cursor !== records.length) throw new Error(`Workflow job trigger sequence changed while replaying "${name}". Expected ${records.length} trigger(s), but replay reached ${execution.cursor}.`);
|
|
360
|
+
return platformSerialize(out);
|
|
361
|
+
} catch (cause) {
|
|
362
|
+
const pending = cause instanceof PendingTrigger ? cause : execution.pending;
|
|
363
|
+
if (pending) {
|
|
364
|
+
await settlePendingTrigger(records, pending, runJob);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
throw cause;
|
|
368
|
+
} finally {
|
|
369
|
+
activeExecution = previousExecution;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
return {
|
|
374
|
+
runJob,
|
|
375
|
+
triggerJobFunction
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
async function settlePendingTrigger(records, pending, runJob) {
|
|
379
|
+
try {
|
|
380
|
+
records.push({
|
|
381
|
+
jobName: pending.jobName,
|
|
382
|
+
args: pending.args,
|
|
383
|
+
status: "fulfilled",
|
|
384
|
+
result: await runJob(pending.jobName, pending.args)
|
|
385
|
+
});
|
|
386
|
+
} catch (error) {
|
|
387
|
+
records.push({
|
|
388
|
+
jobName: pending.jobName,
|
|
389
|
+
args: pending.args,
|
|
390
|
+
status: "rejected",
|
|
391
|
+
error
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function assertSameTrigger(record, jobName, args) {
|
|
396
|
+
if (record.jobName === jobName && JSON.stringify(record.args) === JSON.stringify(args)) return;
|
|
397
|
+
throw new Error(`Workflow job trigger sequence changed while replaying. Expected ${record.jobName}(${JSON.stringify(record.args)}), but got ${jobName}(${JSON.stringify(args)}).`);
|
|
398
|
+
}
|
|
399
|
+
function createLocalWorkflowRuntime(previous, triggerJobFunction) {
|
|
400
|
+
return {
|
|
401
|
+
triggerJobFunction,
|
|
402
|
+
triggerWorkflow: async (name, args, options) => {
|
|
403
|
+
if (previous) return await previous.triggerWorkflow(name, args, options);
|
|
404
|
+
platformSerialize(args);
|
|
405
|
+
return TRIGGER_DEFAULT;
|
|
406
|
+
},
|
|
407
|
+
wait: (key, payload) => {
|
|
408
|
+
if (previous) return previous.wait(key, payload);
|
|
409
|
+
throw new Error(`No wait handler for "${key}". Acquire mockWorkflow() and call setWaitHandler(...).`);
|
|
410
|
+
},
|
|
411
|
+
resolve: async (executionId, key, callback) => {
|
|
412
|
+
if (previous) {
|
|
413
|
+
await previous.resolve(executionId, key, callback);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
throw new Error("No resolve handler. Acquire mockWorkflow() and call setResolveHandler(...).");
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
292
421
|
//#endregion
|
|
293
422
|
//#region src/vitest/mock-kysely.ts
|
|
294
423
|
/**
|
|
@@ -517,5 +646,5 @@ function tailorRuntime(options) {
|
|
|
517
646
|
}
|
|
518
647
|
|
|
519
648
|
//#endregion
|
|
520
|
-
export { createKyselyMock, mockAuthconnection, mockFile, mockIconv, mockIdp, mockSecretmanager, mockTailordb, mockWorkflow, tailorRuntime };
|
|
649
|
+
export { createKyselyMock, mockAuthconnection, mockFile, mockIconv, mockIdp, mockSecretmanager, mockTailordb, mockWorkflow, runWorkflowLocally, tailorRuntime };
|
|
521
650
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -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 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":";;;;;;;;;;;;;;;;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,MAAM,YAAY,KAAK;CACvB,IAAI,cAAc,QAAW,OAAO;CACpC,OAAO,SAAS,KAAK,SAAS,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC;AAC/D;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,QAAQ;IAC5B,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;;;;;;;;;;;AC/UA,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,cAAc,KAAK,IAAI,iCAAiC;AACjE;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1NA,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/workflow-local.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","/* oxlint-disable typescript/no-explicit-any */\nimport { TRIGGER_DEFAULT, getRegisteredJob } from \"../configure/services/workflow/registry\";\nimport {\n buildJobContext,\n clearWorkflowTestEnv,\n readWorkflowTestEnv,\n writeWorkflowTestEnv,\n} from \"../configure/services/workflow/test-env-key\";\nimport { platformSerialize } from \"../utils/test/platform-serialize\";\nimport type { Workflow, WorkflowJob } from \"../configure/services/workflow\";\nimport type { TailorEnv } from \"../runtime/types\";\nimport type { TailorWorkflowAPI } from \"../runtime/workflow\";\n\ntype AnyWorkflowJob = WorkflowJob<string, any, any>;\ntype AnyWorkflow = Workflow<AnyWorkflowJob>;\n\ntype WorkflowInput<W extends AnyWorkflow> =\n W[\"mainJob\"] extends WorkflowJob<string, infer I, any> ? I : never;\ntype WorkflowOutput<W extends AnyWorkflow> =\n W[\"mainJob\"] extends WorkflowJob<string, any, infer O> ? Awaited<O> : never;\n\ntype GlobalWithTailor = {\n tailor?: {\n workflow?: TailorWorkflowAPI;\n };\n};\n\ntype TriggerRecord = {\n jobName: string;\n args: unknown;\n} & (\n | {\n status: \"fulfilled\";\n result: unknown;\n }\n | {\n status: \"rejected\";\n error: unknown;\n }\n);\n\ninterface LocalExecution {\n records: TriggerRecord[];\n cursor: number;\n pending?: PendingTrigger;\n}\n\nclass PendingTrigger {\n constructor(\n readonly jobName: string,\n readonly args: unknown,\n ) {}\n}\n\nexport interface RunWorkflowLocallyOptions {\n /** Env passed to workflow job bodies during this local run. */\n env?: TailorEnv;\n}\n\n/**\n * Run a workflow's main job and dependent job triggers locally with real job bodies.\n *\n * Use this for local full-chain workflow tests. Regular `.trigger()` calls\n * delegate to the platform workflow runtime and should be mocked with\n * `mockWorkflow()` when you are not intentionally running the local chain.\n * @param workflow - Workflow definition to run\n * @returns The main job output\n */\nexport function runWorkflowLocally<W extends Workflow<WorkflowJob<string, undefined, any>>>(\n workflow: W,\n): Promise<WorkflowOutput<W>>;\n/**\n * Run a no-input workflow locally with optional runner settings.\n * @param workflow - Workflow definition to run\n * @param args - Must be `undefined` for no-input workflows\n * @param options - Local runner options\n * @returns The main job output\n */\nexport function runWorkflowLocally<W extends Workflow<WorkflowJob<string, undefined, any>>>(\n workflow: W,\n args: undefined,\n options?: RunWorkflowLocallyOptions,\n): Promise<WorkflowOutput<W>>;\n/**\n * Run a workflow locally with real job bodies.\n * @param workflow - Workflow definition to run\n * @param args - Arguments passed to the workflow's main job\n * @param options - Local runner options\n * @returns The main job output\n */\nexport function runWorkflowLocally<W extends AnyWorkflow>(\n workflow: W,\n args: WorkflowInput<W>,\n options?: RunWorkflowLocallyOptions,\n): Promise<WorkflowOutput<W>>;\nexport async function runWorkflowLocally<W extends AnyWorkflow>(\n workflow: W,\n args?: WorkflowInput<W>,\n options?: RunWorkflowLocallyOptions,\n): Promise<WorkflowOutput<W>> {\n const root = globalThis as unknown as GlobalWithTailor;\n const previousTailor = root.tailor;\n const previousEnv = readWorkflowTestEnv();\n const hasPreviousEnv = previousEnv !== undefined;\n const runner = createLocalJobRunner();\n\n if (options?.env !== undefined) {\n writeWorkflowTestEnv({ ...options.env });\n }\n\n root.tailor = {\n ...previousTailor,\n workflow: createLocalWorkflowRuntime(previousTailor?.workflow, runner.triggerJobFunction),\n };\n\n try {\n return (await runner.runJob(workflow.mainJob.name, args)) as WorkflowOutput<W>;\n } finally {\n if (previousTailor) {\n root.tailor = previousTailor;\n } else {\n delete root.tailor;\n }\n\n if (options?.env !== undefined) {\n if (hasPreviousEnv) {\n writeWorkflowTestEnv(previousEnv);\n } else {\n clearWorkflowTestEnv();\n }\n }\n }\n}\n\nfunction createLocalJobRunner(): {\n runJob: (name: string, args?: unknown) => Promise<unknown>;\n triggerJobFunction: (name: string, args?: unknown) => unknown;\n} {\n let activeExecution: LocalExecution | undefined;\n\n const triggerJobFunction = (jobName: string, args?: unknown): unknown => {\n if (!activeExecution) {\n throw new Error(\n `Cannot trigger workflow job \"${jobName}\" outside runWorkflowLocally() job execution.`,\n );\n }\n if (activeExecution.pending) {\n throw activeExecution.pending;\n }\n\n const serializedArgs = platformSerialize(args);\n const index = activeExecution.cursor;\n activeExecution.cursor += 1;\n\n const cached = activeExecution.records[index];\n if (cached) {\n assertSameTrigger(cached, jobName, serializedArgs);\n if (cached.status === \"rejected\") {\n throw cached.error;\n }\n return platformSerialize(cached.result);\n }\n\n const pending = new PendingTrigger(jobName, serializedArgs);\n activeExecution.pending = pending;\n throw pending;\n };\n\n const runJob = async (name: string, args?: unknown): Promise<unknown> => {\n const body = getRegisteredJob(name);\n if (!body) {\n return null;\n }\n\n const records: TriggerRecord[] = [];\n\n for (;;) {\n const execution: LocalExecution = { records, cursor: 0 };\n const previousExecution = activeExecution;\n activeExecution = execution;\n\n try {\n const out = await body(platformSerialize(args), buildJobContext());\n if (execution.pending) {\n await settlePendingTrigger(records, execution.pending, runJob);\n continue;\n }\n if (execution.cursor !== records.length) {\n throw new Error(\n `Workflow job trigger sequence changed while replaying \"${name}\". Expected ${records.length} trigger(s), but replay reached ${execution.cursor}.`,\n );\n }\n return platformSerialize(out);\n } catch (cause) {\n const pending = cause instanceof PendingTrigger ? cause : execution.pending;\n if (pending) {\n await settlePendingTrigger(records, pending, runJob);\n continue;\n }\n throw cause;\n } finally {\n activeExecution = previousExecution;\n }\n }\n };\n\n return { runJob, triggerJobFunction };\n}\n\nasync function settlePendingTrigger(\n records: TriggerRecord[],\n pending: PendingTrigger,\n runJob: (name: string, args?: unknown) => Promise<unknown>,\n): Promise<void> {\n try {\n records.push({\n jobName: pending.jobName,\n args: pending.args,\n status: \"fulfilled\",\n result: await runJob(pending.jobName, pending.args),\n });\n } catch (error) {\n records.push({\n jobName: pending.jobName,\n args: pending.args,\n status: \"rejected\",\n error,\n });\n }\n}\n\nfunction assertSameTrigger(record: TriggerRecord, jobName: string, args: unknown): void {\n if (record.jobName === jobName && JSON.stringify(record.args) === JSON.stringify(args)) {\n return;\n }\n\n throw new Error(\n `Workflow job trigger sequence changed while replaying. Expected ${record.jobName}(${JSON.stringify(record.args)}), but got ${jobName}(${JSON.stringify(args)}).`,\n );\n}\n\nfunction createLocalWorkflowRuntime(\n previous: TailorWorkflowAPI | undefined,\n triggerJobFunction: (name: string, args?: unknown) => unknown,\n): TailorWorkflowAPI {\n return {\n triggerJobFunction,\n triggerWorkflow: async (name, args, options) => {\n if (previous) {\n return await previous.triggerWorkflow(name, args, options);\n }\n platformSerialize(args);\n return TRIGGER_DEFAULT;\n },\n wait: (key, payload) => {\n if (previous) {\n return previous.wait(key, payload);\n }\n throw new Error(\n `No wait handler for \"${key}\". Acquire mockWorkflow() and call setWaitHandler(...).`,\n );\n },\n resolve: async (executionId, key, callback) => {\n if (previous) {\n await previous.resolve(executionId, key, callback);\n return;\n }\n throw new Error(\n \"No resolve handler. Acquire mockWorkflow() and call setResolveHandler(...).\",\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 { runWorkflowLocally, type RunWorkflowLocallyOptions } from \"./workflow-local\";\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,MAAM,YAAY,KAAK;CACvB,IAAI,cAAc,QAAW,OAAO;CACpC,OAAO,SAAS,KAAK,SAAS,KAAK,QAAQ,KAAK,KAAK,MAAM,CAAC,CAAC;AAC/D;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,QAAQ;IAC5B,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;;;;AChUA,IAAM,iBAAN,MAAqB;CAER;CACA;CAFX,YACE,AAAS,SACT,AAAS,MACT;EAFS;EACA;CACR;AACL;AA2CA,eAAsB,mBACpB,UACA,MACA,SAC4B;CAC5B,MAAM,OAAO;CACb,MAAM,iBAAiB,KAAK;CAC5B,MAAM,cAAc,oBAAoB;CACxC,MAAM,iBAAiB,gBAAgB;CACvC,MAAM,SAAS,qBAAqB;CAEpC,IAAI,SAAS,QAAQ,QACnB,qBAAqB,EAAE,GAAG,QAAQ,IAAI,CAAC;CAGzC,KAAK,SAAS;EACZ,GAAG;EACH,UAAU,2BAA2B,gBAAgB,UAAU,OAAO,kBAAkB;CAC1F;CAEA,IAAI;EACF,OAAQ,MAAM,OAAO,OAAO,SAAS,QAAQ,MAAM,IAAI;CACzD,UAAU;EACR,IAAI,gBACF,KAAK,SAAS;OAEd,OAAO,KAAK;EAGd,IAAI,SAAS,QAAQ,QACnB,IAAI,gBACF,qBAAqB,WAAW;OAEhC,qBAAqB;CAG3B;AACF;AAEA,SAAS,uBAGP;CACA,IAAI;CAEJ,MAAM,sBAAsB,SAAiB,SAA4B;EACvE,IAAI,CAAC,iBACH,MAAM,IAAI,MACR,gCAAgC,QAAQ,8CAC1C;EAEF,IAAI,gBAAgB,SAClB,MAAM,gBAAgB;EAGxB,MAAM,iBAAiB,kBAAkB,IAAI;EAC7C,MAAM,QAAQ,gBAAgB;EAC9B,gBAAgB,UAAU;EAE1B,MAAM,SAAS,gBAAgB,QAAQ;EACvC,IAAI,QAAQ;GACV,kBAAkB,QAAQ,SAAS,cAAc;GACjD,IAAI,OAAO,WAAW,YACpB,MAAM,OAAO;GAEf,OAAO,kBAAkB,OAAO,MAAM;EACxC;EAEA,MAAM,UAAU,IAAI,eAAe,SAAS,cAAc;EAC1D,gBAAgB,UAAU;EAC1B,MAAM;CACR;CAEA,MAAM,SAAS,OAAO,MAAc,SAAqC;EACvE,MAAM,OAAO,iBAAiB,IAAI;EAClC,IAAI,CAAC,MACH,OAAO;EAGT,MAAM,UAA2B,CAAC;EAElC,SAAS;GACP,MAAM,YAA4B;IAAE;IAAS,QAAQ;GAAE;GACvD,MAAM,oBAAoB;GAC1B,kBAAkB;GAElB,IAAI;IACF,MAAM,MAAM,MAAM,KAAK,kBAAkB,IAAI,GAAG,gBAAgB,CAAC;IACjE,IAAI,UAAU,SAAS;KACrB,MAAM,qBAAqB,SAAS,UAAU,SAAS,MAAM;KAC7D;IACF;IACA,IAAI,UAAU,WAAW,QAAQ,QAC/B,MAAM,IAAI,MACR,0DAA0D,KAAK,cAAc,QAAQ,OAAO,kCAAkC,UAAU,OAAO,EACjJ;IAEF,OAAO,kBAAkB,GAAG;GAC9B,SAAS,OAAO;IACd,MAAM,UAAU,iBAAiB,iBAAiB,QAAQ,UAAU;IACpE,IAAI,SAAS;KACX,MAAM,qBAAqB,SAAS,SAAS,MAAM;KACnD;IACF;IACA,MAAM;GACR,UAAU;IACR,kBAAkB;GACpB;EACF;CACF;CAEA,OAAO;EAAE;EAAQ;CAAmB;AACtC;AAEA,eAAe,qBACb,SACA,SACA,QACe;CACf,IAAI;EACF,QAAQ,KAAK;GACX,SAAS,QAAQ;GACjB,MAAM,QAAQ;GACd,QAAQ;GACR,QAAQ,MAAM,OAAO,QAAQ,SAAS,QAAQ,IAAI;EACpD,CAAC;CACH,SAAS,OAAO;EACd,QAAQ,KAAK;GACX,SAAS,QAAQ;GACjB,MAAM,QAAQ;GACd,QAAQ;GACR;EACF,CAAC;CACH;AACF;AAEA,SAAS,kBAAkB,QAAuB,SAAiB,MAAqB;CACtF,IAAI,OAAO,YAAY,WAAW,KAAK,UAAU,OAAO,IAAI,MAAM,KAAK,UAAU,IAAI,GACnF;CAGF,MAAM,IAAI,MACR,mEAAmE,OAAO,QAAQ,GAAG,KAAK,UAAU,OAAO,IAAI,EAAE,aAAa,QAAQ,GAAG,KAAK,UAAU,IAAI,EAAE,GAChK;AACF;AAEA,SAAS,2BACP,UACA,oBACmB;CACnB,OAAO;EACL;EACA,iBAAiB,OAAO,MAAM,MAAM,YAAY;GAC9C,IAAI,UACF,OAAO,MAAM,SAAS,gBAAgB,MAAM,MAAM,OAAO;GAE3D,kBAAkB,IAAI;GACtB,OAAO;EACT;EACA,OAAO,KAAK,YAAY;GACtB,IAAI,UACF,OAAO,SAAS,KAAK,KAAK,OAAO;GAEnC,MAAM,IAAI,MACR,wBAAwB,IAAI,wDAC9B;EACF;EACA,SAAS,OAAO,aAAa,KAAK,aAAa;GAC7C,IAAI,UAAU;IACZ,MAAM,SAAS,QAAQ,aAAa,KAAK,QAAQ;IACjD;GACF;GACA,MAAM,IAAI,MACR,6EACF;EACF;CACF;AACF;;;;;;;;;;;AChPA,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,cAAc,KAAK,IAAI,iCAAiC;AACjE;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1NA,SAAgB,cAAc,SAAyC;CACrE,OAAO,CAAC,kBAAkB,GAAG,wBAAwB,OAAO,CAAC;AAC/D"}
|
package/dist/vitest/setup.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { a as mockSecretmanager } from "../mock-DMgIygjE.mjs";
|
|
1
|
+
import { t as RUNTIME_FLAG_KEY } from "../globals-Cf0sxIt8.mjs";
|
|
2
|
+
import { a as mockSecretmanager } from "../mock-FPxmnt-y.mjs";
|
|
4
3
|
import { pathToFileURL } from "node:url";
|
|
5
4
|
import { afterEach, beforeAll, beforeEach } from "vitest";
|
|
6
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.mjs","names":[],"sources":["../../src/vitest/setup.ts"],"sourcesContent":["/**\n * Vitest setup file that removes Node.js globals which Vitest depends on\n * but are not available in the Tailor Platform runtime.\n *\n * These globals cannot be removed in the environment's setup() because\n * Vitest's runner needs them during initialization. By using beforeEach/afterEach,\n * they are only removed during user test code execution.\n *\n * This file is auto-injected by tailorRuntime() but only activates when\n * the tailor-runtime environment is active (detected via __tailorRuntimeActive,\n * a flag set by injectMocks() during environment setup).\n */\nimport { pathToFileURL } from \"node:url\";\nimport { afterEach, beforeAll, beforeEach } from \"vitest\";\nimport { RUNTIME_FLAG_KEY, mockSecretmanager } from \"./mock\";\n\n// Globals that Vitest internals depend on but don't exist in the platform runtime.\n// Removed before each test, restored after.\nconst BLOCKED_GLOBALS = [\"performance\"] as const;\n\ntype SavedGlobals = Record<string, PropertyDescriptor | undefined>;\n\n/**\n * Reference-counted lifecycle for blocked globals.\n *\n * Concurrent tests in the same Vitest worker (`test.concurrent`) interleave\n * their beforeEach/afterEach hooks: a naive `let saved` shared across hooks\n * would let one test's `afterEach` restore `performance` mid-execution of\n * another test. Reference counting keeps the globals removed for the union\n * of all overlapping test scopes — the property is removed on first entry\n * and restored only when the last test exits.\n * @returns Lifecycle hooks\n */\nexport function createBlockedGlobalsLifecycle(): {\n enter: (globalObj: Record<string, unknown>, keys: readonly string[]) => void;\n exit: (globalObj: Record<string, unknown>) => void;\n readonly active: number;\n} {\n let active = 0;\n let saved: SavedGlobals = {};\n return {\n get active() {\n return active;\n },\n enter(globalObj, keys) {\n if (active === 0) saved = removeBlockedGlobals(globalObj, keys);\n active++;\n },\n exit(globalObj) {\n // Defensive guard — in practice every `enter` is paired with `exit`,\n // but a hook crash could desync the counter.\n if (active === 0) return;\n active--;\n if (active === 0) {\n restoreBlockedGlobals(globalObj, saved);\n saved = {};\n }\n },\n };\n}\n\nconst lifecycle = createBlockedGlobalsLifecycle();\n\nfunction isTailorRuntime(): boolean {\n return RUNTIME_FLAG_KEY in globalThis;\n}\n\n/**\n * Extract a vault store from a secrets-shaped value.\n *\n * `defineSecretManager()` returns `{ vaults, options, get, getAll }` (get/getAll\n * are non-enumerable). When that shape is present, the actual vaults live\n * under `.vaults`. Otherwise fall back to treating the object itself as the\n * vault map (for plain object configs).\n * @param secrets - Value from `appConfig.secrets` or `config.secrets`\n * @returns Vault store, or null if the value is unusable\n */\nexport function extractVaultStore(secrets: unknown): Record<string, Record<string, string>> | null {\n if (!secrets || typeof secrets !== \"object\") return null;\n\n const source =\n \"vaults\" in secrets &&\n typeof (secrets as { vaults?: unknown }).vaults === \"object\" &&\n (secrets as { vaults?: unknown }).vaults !== null\n ? ((secrets as { vaults: Record<string, unknown> }).vaults as Record<string, unknown>)\n : (secrets as Record<string, unknown>);\n\n const store: Record<string, Record<string, string>> = {};\n for (const [vaultName, vaultData] of Object.entries(source)) {\n if (typeof vaultData === \"object\" && vaultData !== null) {\n store[vaultName] = { ...(vaultData as Record<string, string>) };\n }\n }\n return Object.keys(store).length > 0 ? store : null;\n}\n\n/**\n * Load and parse secrets from a tailor.config.ts file.\n *\n * Returns a vault store on success, or `null` on any failure (missing config,\n * import failure, missing/invalid secrets shape). Errors are swallowed so a\n * misconfigured project still boots — the user can set secrets manually via\n * `mockSecretmanager().setSecrets()`.\n * @param configPath - Absolute path to tailor.config.ts\n * @returns Vault store keyed by vault name, or null if unavailable\n */\nexport async function loadSecretsFromConfig(\n configPath: string,\n): Promise<Record<string, Record<string, string>> | null> {\n try {\n // Convert to file URL so absolute Windows paths (e.g. \"C:\\...\") parse as\n // valid ESM specifiers.\n const config = await import(pathToFileURL(configPath).href);\n const appConfig = config.default;\n const secrets = appConfig?.secrets ?? config.secrets;\n return extractVaultStore(secrets);\n } catch {\n return null;\n }\n}\n\n// Load secrets from tailor.config.ts if config path is provided via env var\nbeforeAll(async () => {\n if (!isTailorRuntime()) return;\n const configPath = process.env.__TAILOR_RUNTIME_CONFIG;\n if (!configPath) return;\n\n const store = await loadSecretsFromConfig(configPath);\n if (store) {\n // Acquire without `using`: this is a one-time global seed that must persist\n // across tests, so we deliberately do not dispose (which would reset it).\n mockSecretmanager().setSecrets(store);\n }\n});\n\n/**\n * Remove the given globals from `globalObj`, returning the descriptors that\n * were actually deleted so `restoreBlockedGlobals` can put them back.\n *\n * Mirrors environment.ts: non-configurable properties are skipped instead of\n * deleted, so `delete` never throws in strict-mode runtimes that lock them\n * down. Only properties whose deletion actually happened are returned, so the\n * caller restores nothing for the skipped case.\n * @param globalObj - Target object (typically `globalThis`)\n * @param keys - Property names to remove\n * @returns Map of removed descriptors keyed by property name\n */\nexport function removeBlockedGlobals(\n globalObj: Record<string, unknown>,\n keys: readonly string[],\n): SavedGlobals {\n const removed: SavedGlobals = {};\n for (const key of keys) {\n const descriptor = Object.getOwnPropertyDescriptor(globalObj, key);\n if (descriptor?.configurable) {\n removed[key] = descriptor;\n delete globalObj[key];\n }\n }\n return removed;\n}\n\n/**\n * Restore previously-removed globals onto `globalObj` from a `SavedGlobals`\n * map produced by `removeBlockedGlobals`.\n * @param globalObj - Target object (typically `globalThis`)\n * @param saved - Descriptors to re-define\n */\nexport function restoreBlockedGlobals(\n globalObj: Record<string, unknown>,\n saved: SavedGlobals,\n): void {\n for (const [key, descriptor] of Object.entries(saved)) {\n if (descriptor) {\n Object.defineProperty(globalObj, key, descriptor);\n }\n }\n}\n\nbeforeEach(() => {\n if (!isTailorRuntime()) return;\n lifecycle.enter(globalThis as Record<string, unknown>, BLOCKED_GLOBALS);\n});\n\nafterEach(() => {\n if (!isTailorRuntime()) return;\n lifecycle.exit(globalThis as Record<string, unknown>);\n});\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup.mjs","names":[],"sources":["../../src/vitest/setup.ts"],"sourcesContent":["/**\n * Vitest setup file that removes Node.js globals which Vitest depends on\n * but are not available in the Tailor Platform runtime.\n *\n * These globals cannot be removed in the environment's setup() because\n * Vitest's runner needs them during initialization. By using beforeEach/afterEach,\n * they are only removed during user test code execution.\n *\n * This file is auto-injected by tailorRuntime() but only activates when\n * the tailor-runtime environment is active (detected via __tailorRuntimeActive,\n * a flag set by injectMocks() during environment setup).\n */\nimport { pathToFileURL } from \"node:url\";\nimport { afterEach, beforeAll, beforeEach } from \"vitest\";\nimport { RUNTIME_FLAG_KEY, mockSecretmanager } from \"./mock\";\n\n// Globals that Vitest internals depend on but don't exist in the platform runtime.\n// Removed before each test, restored after.\nconst BLOCKED_GLOBALS = [\"performance\"] as const;\n\ntype SavedGlobals = Record<string, PropertyDescriptor | undefined>;\n\n/**\n * Reference-counted lifecycle for blocked globals.\n *\n * Concurrent tests in the same Vitest worker (`test.concurrent`) interleave\n * their beforeEach/afterEach hooks: a naive `let saved` shared across hooks\n * would let one test's `afterEach` restore `performance` mid-execution of\n * another test. Reference counting keeps the globals removed for the union\n * of all overlapping test scopes — the property is removed on first entry\n * and restored only when the last test exits.\n * @returns Lifecycle hooks\n */\nexport function createBlockedGlobalsLifecycle(): {\n enter: (globalObj: Record<string, unknown>, keys: readonly string[]) => void;\n exit: (globalObj: Record<string, unknown>) => void;\n readonly active: number;\n} {\n let active = 0;\n let saved: SavedGlobals = {};\n return {\n get active() {\n return active;\n },\n enter(globalObj, keys) {\n if (active === 0) saved = removeBlockedGlobals(globalObj, keys);\n active++;\n },\n exit(globalObj) {\n // Defensive guard — in practice every `enter` is paired with `exit`,\n // but a hook crash could desync the counter.\n if (active === 0) return;\n active--;\n if (active === 0) {\n restoreBlockedGlobals(globalObj, saved);\n saved = {};\n }\n },\n };\n}\n\nconst lifecycle = createBlockedGlobalsLifecycle();\n\nfunction isTailorRuntime(): boolean {\n return RUNTIME_FLAG_KEY in globalThis;\n}\n\n/**\n * Extract a vault store from a secrets-shaped value.\n *\n * `defineSecretManager()` returns `{ vaults, options, get, getAll }` (get/getAll\n * are non-enumerable). When that shape is present, the actual vaults live\n * under `.vaults`. Otherwise fall back to treating the object itself as the\n * vault map (for plain object configs).\n * @param secrets - Value from `appConfig.secrets` or `config.secrets`\n * @returns Vault store, or null if the value is unusable\n */\nexport function extractVaultStore(secrets: unknown): Record<string, Record<string, string>> | null {\n if (!secrets || typeof secrets !== \"object\") return null;\n\n const source =\n \"vaults\" in secrets &&\n typeof (secrets as { vaults?: unknown }).vaults === \"object\" &&\n (secrets as { vaults?: unknown }).vaults !== null\n ? ((secrets as { vaults: Record<string, unknown> }).vaults as Record<string, unknown>)\n : (secrets as Record<string, unknown>);\n\n const store: Record<string, Record<string, string>> = {};\n for (const [vaultName, vaultData] of Object.entries(source)) {\n if (typeof vaultData === \"object\" && vaultData !== null) {\n store[vaultName] = { ...(vaultData as Record<string, string>) };\n }\n }\n return Object.keys(store).length > 0 ? store : null;\n}\n\n/**\n * Load and parse secrets from a tailor.config.ts file.\n *\n * Returns a vault store on success, or `null` on any failure (missing config,\n * import failure, missing/invalid secrets shape). Errors are swallowed so a\n * misconfigured project still boots — the user can set secrets manually via\n * `mockSecretmanager().setSecrets()`.\n * @param configPath - Absolute path to tailor.config.ts\n * @returns Vault store keyed by vault name, or null if unavailable\n */\nexport async function loadSecretsFromConfig(\n configPath: string,\n): Promise<Record<string, Record<string, string>> | null> {\n try {\n // Convert to file URL so absolute Windows paths (e.g. \"C:\\...\") parse as\n // valid ESM specifiers.\n const config = await import(pathToFileURL(configPath).href);\n const appConfig = config.default;\n const secrets = appConfig?.secrets ?? config.secrets;\n return extractVaultStore(secrets);\n } catch {\n return null;\n }\n}\n\n// Load secrets from tailor.config.ts if config path is provided via env var\nbeforeAll(async () => {\n if (!isTailorRuntime()) return;\n const configPath = process.env.__TAILOR_RUNTIME_CONFIG;\n if (!configPath) return;\n\n const store = await loadSecretsFromConfig(configPath);\n if (store) {\n // Acquire without `using`: this is a one-time global seed that must persist\n // across tests, so we deliberately do not dispose (which would reset it).\n mockSecretmanager().setSecrets(store);\n }\n});\n\n/**\n * Remove the given globals from `globalObj`, returning the descriptors that\n * were actually deleted so `restoreBlockedGlobals` can put them back.\n *\n * Mirrors environment.ts: non-configurable properties are skipped instead of\n * deleted, so `delete` never throws in strict-mode runtimes that lock them\n * down. Only properties whose deletion actually happened are returned, so the\n * caller restores nothing for the skipped case.\n * @param globalObj - Target object (typically `globalThis`)\n * @param keys - Property names to remove\n * @returns Map of removed descriptors keyed by property name\n */\nexport function removeBlockedGlobals(\n globalObj: Record<string, unknown>,\n keys: readonly string[],\n): SavedGlobals {\n const removed: SavedGlobals = {};\n for (const key of keys) {\n const descriptor = Object.getOwnPropertyDescriptor(globalObj, key);\n if (descriptor?.configurable) {\n removed[key] = descriptor;\n delete globalObj[key];\n }\n }\n return removed;\n}\n\n/**\n * Restore previously-removed globals onto `globalObj` from a `SavedGlobals`\n * map produced by `removeBlockedGlobals`.\n * @param globalObj - Target object (typically `globalThis`)\n * @param saved - Descriptors to re-define\n */\nexport function restoreBlockedGlobals(\n globalObj: Record<string, unknown>,\n saved: SavedGlobals,\n): void {\n for (const [key, descriptor] of Object.entries(saved)) {\n if (descriptor) {\n Object.defineProperty(globalObj, key, descriptor);\n }\n }\n}\n\nbeforeEach(() => {\n if (!isTailorRuntime()) return;\n lifecycle.enter(globalThis as Record<string, unknown>, BLOCKED_GLOBALS);\n});\n\nafterEach(() => {\n if (!isTailorRuntime()) return;\n lifecycle.exit(globalThis as Record<string, unknown>);\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;AAkBA,MAAM,kBAAkB,CAAC,aAAa;;;;;;;;;;;;AAetC,SAAgB,gCAId;CACA,IAAI,SAAS;CACb,IAAI,QAAsB,CAAC;CAC3B,OAAO;EACL,IAAI,SAAS;GACX,OAAO;EACT;EACA,MAAM,WAAW,MAAM;GACrB,IAAI,WAAW,GAAG,QAAQ,qBAAqB,WAAW,IAAI;GAC9D;EACF;EACA,KAAK,WAAW;GAGd,IAAI,WAAW,GAAG;GAClB;GACA,IAAI,WAAW,GAAG;IAChB,sBAAsB,WAAW,KAAK;IACtC,QAAQ,CAAC;GACX;EACF;CACF;AACF;AAEA,MAAM,YAAY,8BAA8B;AAEhD,SAAS,kBAA2B;CAClC,OAAO,oBAAoB;AAC7B;;;;;;;;;;;AAYA,SAAgB,kBAAkB,SAAiE;CACjG,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU,OAAO;CAEpD,MAAM,SACJ,YAAY,WACZ,OAAQ,QAAiC,WAAW,YACnD,QAAiC,WAAW,OACvC,QAAgD,SACjD;CAEP,MAAM,QAAgD,CAAC;CACvD,KAAK,MAAM,CAAC,WAAW,cAAc,OAAO,QAAQ,MAAM,GACxD,IAAI,OAAO,cAAc,YAAY,cAAc,MACjD,MAAM,aAAa,EAAE,GAAI,UAAqC;CAGlE,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,SAAS,IAAI,QAAQ;AACjD;;;;;;;;;;;AAYA,eAAsB,sBACpB,YACwD;CACxD,IAAI;EAGF,MAAM,SAAS,MAAM,OAAO,cAAc,UAAU,CAAC,CAAC;EAGtD,OAAO,kBAFW,OAAO,SACE,WAAW,OAAO,OACb;CAClC,QAAQ;EACN,OAAO;CACT;AACF;AAGA,UAAU,YAAY;CACpB,IAAI,CAAC,gBAAgB,GAAG;CACxB,MAAM,aAAa,QAAQ,IAAI;CAC/B,IAAI,CAAC,YAAY;CAEjB,MAAM,QAAQ,MAAM,sBAAsB,UAAU;CACpD,IAAI,OAGF,kBAAkB,CAAC,CAAC,WAAW,KAAK;AAExC,CAAC;;;;;;;;;;;;;AAcD,SAAgB,qBACd,WACA,MACc;CACd,MAAM,UAAwB,CAAC;CAC/B,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,aAAa,OAAO,yBAAyB,WAAW,GAAG;EACjE,IAAI,YAAY,cAAc;GAC5B,QAAQ,OAAO;GACf,OAAO,UAAU;EACnB;CACF;CACA,OAAO;AACT;;;;;;;AAQA,SAAgB,sBACd,WACA,OACM;CACN,KAAK,MAAM,CAAC,KAAK,eAAe,OAAO,QAAQ,KAAK,GAClD,IAAI,YACF,OAAO,eAAe,WAAW,KAAK,UAAU;AAGtD;AAEA,iBAAiB;CACf,IAAI,CAAC,gBAAgB,GAAG;CACxB,UAAU,MAAM,YAAuC,eAAe;AACxE,CAAC;AAED,gBAAgB;CACd,IAAI,CAAC,gBAAgB,GAAG;CACxB,UAAU,KAAK,UAAqC;AACtD,CAAC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import { t as __exportAll } from "./chunk-BkoGK1jX.mjs";
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-DXywRVcq.mjs";
|
|
3
2
|
|
|
4
3
|
//#region src/runtime/workflow.ts
|
|
5
4
|
var workflow_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -11,10 +10,15 @@ var workflow_exports = /* @__PURE__ */ __exportAll({
|
|
|
11
10
|
const api = () => globalThis.tailor.workflow;
|
|
12
11
|
/**
|
|
13
12
|
* See {@link TailorWorkflowAPI.triggerWorkflow}.
|
|
14
|
-
* @param
|
|
13
|
+
* @param workflowName - Workflow name as defined in tailor.config
|
|
14
|
+
* @param args - Arguments forwarded to the workflow's main job
|
|
15
|
+
* @param options - Optional trigger options
|
|
15
16
|
* @returns The execution ID of the triggered workflow
|
|
16
17
|
*/
|
|
17
|
-
|
|
18
|
+
function triggerWorkflow(workflowName, args, options) {
|
|
19
|
+
if (options?.invoker === void 0) return api().triggerWorkflow(workflowName, args);
|
|
20
|
+
return api().triggerWorkflow(workflowName, args, { authInvoker: options.invoker });
|
|
21
|
+
}
|
|
18
22
|
/**
|
|
19
23
|
* See {@link TailorWorkflowAPI.triggerJobFunction}.
|
|
20
24
|
* @param args - Forwarded to {@link TailorWorkflowAPI.triggerJobFunction}
|
|
@@ -36,4 +40,4 @@ const resolve = (...args) => api().resolve(...args);
|
|
|
36
40
|
|
|
37
41
|
//#endregion
|
|
38
42
|
export { workflow_exports as a, wait as i, triggerJobFunction as n, triggerWorkflow as r, resolve as t };
|
|
39
|
-
//# sourceMappingURL=workflow
|
|
43
|
+
//# sourceMappingURL=workflow-BOmaZwwG.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-BOmaZwwG.mjs","names":[],"sources":["../src/runtime/workflow.ts"],"sourcesContent":["/**\n * Workflow utilities.\n *\n * Thin typed wrapper around the platform-provided `tailor.workflow` runtime API.\n * At runtime this delegates to `globalThis.tailor.workflow`. Use `mockWorkflow`\n * from `@tailor-platform/sdk/vitest` to mock these calls in unit tests.\n * @example\n * import { workflow } from \"@tailor-platform/sdk/runtime\";\n *\n * const executionId = await workflow.triggerWorkflow(\"myWorkflow\", { data: \"value\" });\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Specifies the machine user that should be used to execute the workflow.\n * This allows workflows to run with specific authentication context.\n */\nexport interface Invoker {\n /** The namespace where the machine user is defined */\n namespace: string;\n /** The name of the machine user to use for workflow execution */\n machineUserName: string;\n}\n\n/** Options for {@link triggerWorkflow}. */\nexport interface TriggerWorkflowOptions {\n /** Optional invoker to specify which machine user should execute the workflow */\n invoker?: Invoker;\n}\n\nexport interface PlatformTriggerWorkflowOptions {\n authInvoker?: Invoker;\n}\n\n/**\n * Platform API surface for `tailor.workflow`. Describes the shape the platform\n * runtime injects on `globalThis.tailor.workflow`.\n *\n * Each method below is also re-exported as a top-level named export from this\n * module so callers can either `import * as workflow from\n * \"@tailor-platform/sdk/runtime/workflow\"` or pick individual methods.\n */\nexport interface TailorWorkflowAPI {\n /**\n * Triggers a workflow and returns its execution ID.\n * @param workflowName - Workflow name as defined in tailor.config\n * @param args - Arguments forwarded to the workflow's main job\n * @param options - Optional platform trigger options\n * @returns The execution ID of the triggered workflow\n */\n triggerWorkflow(\n workflowName: string,\n args?: any,\n options?: PlatformTriggerWorkflowOptions,\n ): Promise<string>;\n\n /**\n * Triggers a job function and returns its result.\n * @param jobName - Job name as defined in the workflow\n * @param args - Arguments forwarded to the job\n * @returns The job's return value\n */\n triggerJobFunction(jobName: string, args?: any): any;\n\n /**\n * Suspends the current workflow execution and waits for an external signal to resume.\n * @param key - Wait point key\n * @param payload - Optional payload to record with the wait point\n * @returns The payload supplied by the corresponding `resolve` call\n */\n wait(key: string, payload?: any): any;\n\n /**\n * Resolves a waiting workflow execution, causing it to resume.\n * @param executionId - The execution to resume\n * @param key - Wait point key to resolve\n * @param callback - Callback receiving the wait payload; its return value is forwarded to `wait`\n * @returns A promise that resolves once the resolve has been recorded\n */\n resolve(executionId: string, key: string, callback: (waitPayload: any) => any): Promise<void>;\n}\n\nconst api = (): TailorWorkflowAPI =>\n (globalThis as { tailor: { workflow: TailorWorkflowAPI } }).tailor.workflow;\n\n/**\n * See {@link TailorWorkflowAPI.triggerWorkflow}.\n * @param workflowName - Workflow name as defined in tailor.config\n * @param args - Arguments forwarded to the workflow's main job\n * @param options - Optional trigger options\n * @returns The execution ID of the triggered workflow\n */\nexport function triggerWorkflow(\n workflowName: string,\n args?: any,\n options?: TriggerWorkflowOptions,\n): Promise<string> {\n if (options?.invoker === undefined) {\n return api().triggerWorkflow(workflowName, args);\n }\n return api().triggerWorkflow(workflowName, args, { authInvoker: options.invoker });\n}\n\n/**\n * See {@link TailorWorkflowAPI.triggerJobFunction}.\n * @param args - Forwarded to {@link TailorWorkflowAPI.triggerJobFunction}\n * @returns The job's return value\n */\nexport const triggerJobFunction: TailorWorkflowAPI[\"triggerJobFunction\"] = (...args) =>\n api().triggerJobFunction(...args);\n\n/**\n * See {@link TailorWorkflowAPI.wait}.\n * @param args - Forwarded to {@link TailorWorkflowAPI.wait}\n * @returns The payload supplied by the corresponding `resolve` call\n */\nexport const wait: TailorWorkflowAPI[\"wait\"] = (...args) => api().wait(...args);\n\n/**\n * See {@link TailorWorkflowAPI.resolve}.\n * @param args - Forwarded to {@link TailorWorkflowAPI.resolve}\n * @returns A promise that resolves once the resolve has been recorded\n */\nexport const resolve: TailorWorkflowAPI[\"resolve\"] = (...args) => api().resolve(...args);\n"],"mappings":";;;;;;;;;AAmFA,MAAM,YACH,WAA2D,OAAO;;;;;;;;AASrE,SAAgB,gBACd,cACA,MACA,SACiB;CACjB,IAAI,SAAS,YAAY,QACvB,OAAO,IAAI,CAAC,CAAC,gBAAgB,cAAc,IAAI;CAEjD,OAAO,IAAI,CAAC,CAAC,gBAAgB,cAAc,MAAM,EAAE,aAAa,QAAQ,QAAQ,CAAC;AACnF;;;;;;AAOA,MAAa,sBAA+D,GAAG,SAC7E,IAAI,CAAC,CAAC,mBAAmB,GAAG,IAAI;;;;;;AAOlC,MAAa,QAAmC,GAAG,SAAS,IAAI,CAAC,CAAC,KAAK,GAAG,IAAI;;;;;;AAO9E,MAAa,WAAyC,GAAG,SAAS,IAAI,CAAC,CAAC,QAAQ,GAAG,IAAI"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare namespace workflow_d_exports {
|
|
2
|
-
export {
|
|
2
|
+
export { Invoker, PlatformTriggerWorkflowOptions, TailorWorkflowAPI, TriggerWorkflowOptions, resolve, triggerJobFunction, triggerWorkflow, wait };
|
|
3
3
|
}
|
|
4
4
|
/**
|
|
5
5
|
* Workflow utilities.
|
|
@@ -16,7 +16,7 @@ declare namespace workflow_d_exports {
|
|
|
16
16
|
* Specifies the machine user that should be used to execute the workflow.
|
|
17
17
|
* This allows workflows to run with specific authentication context.
|
|
18
18
|
*/
|
|
19
|
-
interface
|
|
19
|
+
interface Invoker {
|
|
20
20
|
/** The namespace where the machine user is defined */
|
|
21
21
|
namespace: string;
|
|
22
22
|
/** The name of the machine user to use for workflow execution */
|
|
@@ -24,8 +24,11 @@ interface AuthInvoker {
|
|
|
24
24
|
}
|
|
25
25
|
/** Options for {@link triggerWorkflow}. */
|
|
26
26
|
interface TriggerWorkflowOptions {
|
|
27
|
-
/** Optional
|
|
28
|
-
|
|
27
|
+
/** Optional invoker to specify which machine user should execute the workflow */
|
|
28
|
+
invoker?: Invoker;
|
|
29
|
+
}
|
|
30
|
+
interface PlatformTriggerWorkflowOptions {
|
|
31
|
+
authInvoker?: Invoker;
|
|
29
32
|
}
|
|
30
33
|
/**
|
|
31
34
|
* Platform API surface for `tailor.workflow`. Describes the shape the platform
|
|
@@ -40,10 +43,10 @@ interface TailorWorkflowAPI {
|
|
|
40
43
|
* Triggers a workflow and returns its execution ID.
|
|
41
44
|
* @param workflowName - Workflow name as defined in tailor.config
|
|
42
45
|
* @param args - Arguments forwarded to the workflow's main job
|
|
43
|
-
* @param options - Optional trigger options
|
|
46
|
+
* @param options - Optional platform trigger options
|
|
44
47
|
* @returns The execution ID of the triggered workflow
|
|
45
48
|
*/
|
|
46
|
-
triggerWorkflow(workflowName: string, args?: any, options?:
|
|
49
|
+
triggerWorkflow(workflowName: string, args?: any, options?: PlatformTriggerWorkflowOptions): Promise<string>;
|
|
47
50
|
/**
|
|
48
51
|
* Triggers a job function and returns its result.
|
|
49
52
|
* @param jobName - Job name as defined in the workflow
|
|
@@ -69,10 +72,12 @@ interface TailorWorkflowAPI {
|
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
71
74
|
* See {@link TailorWorkflowAPI.triggerWorkflow}.
|
|
72
|
-
* @param
|
|
75
|
+
* @param workflowName - Workflow name as defined in tailor.config
|
|
76
|
+
* @param args - Arguments forwarded to the workflow's main job
|
|
77
|
+
* @param options - Optional trigger options
|
|
73
78
|
* @returns The execution ID of the triggered workflow
|
|
74
79
|
*/
|
|
75
|
-
declare
|
|
80
|
+
declare function triggerWorkflow(workflowName: string, args?: any, options?: TriggerWorkflowOptions): Promise<string>;
|
|
76
81
|
/**
|
|
77
82
|
* See {@link TailorWorkflowAPI.triggerJobFunction}.
|
|
78
83
|
* @param args - Forwarded to {@link TailorWorkflowAPI.triggerJobFunction}
|
|
@@ -92,5 +97,5 @@ declare const wait: TailorWorkflowAPI["wait"];
|
|
|
92
97
|
*/
|
|
93
98
|
declare const resolve: TailorWorkflowAPI["resolve"];
|
|
94
99
|
//#endregion
|
|
95
|
-
export {
|
|
96
|
-
//# sourceMappingURL=workflow-
|
|
100
|
+
export { resolve as a, wait as c, TriggerWorkflowOptions as i, workflow_d_exports as l, PlatformTriggerWorkflowOptions as n, triggerJobFunction as o, TailorWorkflowAPI as r, triggerWorkflow as s, Invoker as t };
|
|
101
|
+
//# sourceMappingURL=workflow-BVy4XWjS.d.mts.map
|