@tailor-platform/sdk 1.46.0 → 1.47.1
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 +48 -0
- package/dist/{actor-BmxQeMFP.d.mts → actor-jk4-f0yp.d.mts} +3 -3
- package/dist/{application-B4zVVNRS.mjs → application-C7H7y0hS.mjs} +66 -103
- package/dist/application-C7H7y0hS.mjs.map +1 -0
- package/dist/application-Csq5jxYP.mjs +4 -0
- package/dist/{brand-Ll48SMXe.mjs → brand-D-d15jx3.mjs} +1 -1
- package/dist/{brand-Ll48SMXe.mjs.map → brand-D-d15jx3.mjs.map} +1 -1
- package/dist/cli/index.mjs +36 -18
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +6 -6
- package/dist/cli/lib.mjs +3 -3
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/skills.mjs.map +1 -1
- package/dist/{client-DTaArWQr.mjs → client-DCqdtFte.mjs} +1 -1
- package/dist/client-DbyKSN1F.mjs +1061 -0
- package/dist/client-DbyKSN1F.mjs.map +1 -0
- package/dist/configure/index.d.mts +4 -4
- package/dist/configure/index.mjs +8 -47
- package/dist/configure/index.mjs.map +1 -1
- package/dist/{crashreport-DGeGj9BF.mjs → crashreport-CNSw_BrJ.mjs} +2 -2
- package/dist/{crashreport-DGeGj9BF.mjs.map → crashreport-CNSw_BrJ.mjs.map} +1 -1
- package/dist/{crashreport-6mcMyWu4.mjs → crashreport-DXGFd16F.mjs} +1 -1
- package/dist/enum-constants-C3KSpsYj.mjs.map +1 -1
- package/dist/errors-wNQxQQBH.mjs.map +1 -1
- package/dist/{field-BY2vbJ8f.mjs → field-DLSIuMTu.mjs} +1 -1
- package/dist/{field-BY2vbJ8f.mjs.map → field-DLSIuMTu.mjs.map} +1 -1
- package/dist/file-utils-DjNi_3U_.mjs.map +1 -1
- package/dist/{index-DV-5OIEv.d.mts → index-BRvNi5q9.d.mts} +2 -2
- package/dist/{index-BBvPd9Uv.d.mts → index-BXyS7xKC.d.mts} +2 -2
- package/dist/{index-Dxe6alSZ.d.mts → index-BbOTbZFf.d.mts} +2 -2
- package/dist/{index-PB0otrDj.d.mts → index-DB8EapT-.d.mts} +7 -15
- package/dist/{index-B5_4Tzm2.d.mts → index-iy-hNfGp.d.mts} +2 -2
- package/dist/{interceptor-CrcDfLPq.mjs → interceptor-CBsqEWDK.mjs} +1 -1
- package/dist/{interceptor-CrcDfLPq.mjs.map → interceptor-CBsqEWDK.mjs.map} +1 -1
- package/dist/{job-BOvKyNdT.mjs → job-R5C2Hfcc.mjs} +2 -3
- package/dist/job-R5C2Hfcc.mjs.map +1 -0
- package/dist/kysely/index.mjs.map +1 -1
- package/dist/kysely-type-B8aRz_oC.mjs.map +1 -1
- package/dist/logger-DTNAMYGy.mjs.map +1 -1
- package/dist/mock-BP-9O5On.mjs +796 -0
- package/dist/mock-BP-9O5On.mjs.map +1 -0
- package/dist/multiline-e3IpANmS.mjs.map +1 -1
- package/dist/package-json-6Px8bDpG.mjs.map +1 -1
- package/dist/plugin/builtin/enum-constants/index.d.mts +1 -1
- package/dist/plugin/builtin/file-utils/index.d.mts +1 -1
- package/dist/plugin/builtin/kysely-type/index.d.mts +1 -1
- package/dist/plugin/builtin/seed/index.d.mts +1 -1
- package/dist/plugin/index.d.mts +2 -2
- package/dist/plugin/index.mjs.map +1 -1
- package/dist/{repl-editor-BlT2dFtm.mjs → repl-editor-CZpLlOBj.mjs} +1 -1
- package/dist/{repl-editor-BlT2dFtm.mjs.map → repl-editor-CZpLlOBj.mjs.map} +1 -1
- package/dist/{runtime-B67skpW-.mjs → runtime-XjP6JMmP.mjs} +91 -10
- package/dist/runtime-XjP6JMmP.mjs.map +1 -0
- package/dist/seed/index.mjs.map +1 -1
- package/dist/seed-DrKY5yIF.mjs.map +1 -1
- package/dist/{service-CCgw66c6.mjs → service-obEU5gSM.mjs} +1 -1
- package/dist/{service-CCgw66c6.mjs.map → service-obEU5gSM.mjs.map} +1 -1
- package/dist/{tailor-db-field-Hx9OqPWY.d.mts → tailor-db-field-Bn8ZC5lK.d.mts} +1 -1
- package/dist/{schema-DBq6hr6h.mjs → tailordb-DjlNUV6u.mjs} +44 -4
- package/dist/tailordb-DjlNUV6u.mjs.map +1 -0
- package/dist/telemetry-DcL8Fsm_.mjs.map +1 -1
- package/dist/types-DoIG6Nij.mjs +5 -0
- package/dist/{types-Duhhsx3R.mjs → types-sir9UPht.mjs} +2 -2
- package/dist/{types-Duhhsx3R.mjs.map → types-sir9UPht.mjs.map} +1 -1
- package/dist/utils/test/index.d.mts +13 -4
- package/dist/utils/test/index.mjs +13 -4
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/vitest/environment.d.mts +12 -0
- package/dist/vitest/environment.mjs +44 -0
- package/dist/vitest/environment.mjs.map +1 -0
- package/dist/vitest/index.d.mts +345 -0
- package/dist/vitest/index.mjs +350 -0
- package/dist/vitest/index.mjs.map +1 -0
- package/dist/vitest/setup.d.mts +64 -0
- package/dist/vitest/setup.mjs +141 -0
- package/dist/vitest/setup.mjs.map +1 -0
- package/dist/{workflow.generated-DFljpJh7.d.mts → workflow.generated-i7PK4fg-.d.mts} +2 -2
- package/docs/cli/setup.md +1 -0
- package/docs/services/tailordb.md +43 -12
- package/docs/services/workflow.md +1 -6
- package/docs/testing.md +530 -243
- package/package.json +37 -10
- package/dist/application-B4zVVNRS.mjs.map +0 -1
- package/dist/application-Boa_11Nv.mjs +0 -4
- package/dist/client-BwXkoiMq.mjs +0 -16536
- package/dist/client-BwXkoiMq.mjs.map +0 -1
- package/dist/job-BOvKyNdT.mjs.map +0 -1
- package/dist/runtime-B67skpW-.mjs.map +0 -1
- package/dist/schema-DBq6hr6h.mjs.map +0 -1
- package/dist/types-BnphjkIJ.mjs +0 -5
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/// <reference types="@tailor-platform/function-types" />
|
|
2
|
+
import { Plugin } from "vitest/config";
|
|
3
|
+
|
|
4
|
+
//#region src/vitest/mock.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Mock implementations for Tailor Platform APIs.
|
|
7
|
+
*
|
|
8
|
+
* Provides singleton mock objects that are automatically injected into
|
|
9
|
+
* globalThis by the tailor-runtime Vitest environment. Tests can configure
|
|
10
|
+
* responses and assert on recorded calls via the exported mock objects.
|
|
11
|
+
*/
|
|
12
|
+
type QueryResolver = (query: string, params: unknown[]) => unknown[];
|
|
13
|
+
type JobHandler = (jobName: string, args: unknown) => unknown;
|
|
14
|
+
type IdpResolver = (method: string, args: unknown[], namespace: string) => unknown;
|
|
15
|
+
type FileResolver = (method: string, call: FileCall) => unknown;
|
|
16
|
+
type IconvResolver = (method: string, args: unknown[]) => unknown;
|
|
17
|
+
type TriggerWorkflowOptions = {
|
|
18
|
+
authInvoker?: {
|
|
19
|
+
namespace: string;
|
|
20
|
+
machineUserName: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
type TriggerHandlerFn = (workflowName: string, args: unknown, options?: TriggerWorkflowOptions) => string;
|
|
24
|
+
type WaitHandlerFn = (key: string, payload: unknown) => unknown;
|
|
25
|
+
type ResolveHandler = (executionId: string, key: string, callback: (payload: unknown) => unknown) => unknown | Promise<unknown>;
|
|
26
|
+
type SetWaitHandler = {
|
|
27
|
+
(handler: WaitHandlerFn): void;
|
|
28
|
+
(handler: unknown): void;
|
|
29
|
+
};
|
|
30
|
+
interface ExecutedQuery {
|
|
31
|
+
query: string;
|
|
32
|
+
params: unknown[];
|
|
33
|
+
}
|
|
34
|
+
interface CreatedClient {
|
|
35
|
+
namespace: string | undefined;
|
|
36
|
+
ended: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface TriggeredJob {
|
|
39
|
+
jobName: string;
|
|
40
|
+
args: unknown;
|
|
41
|
+
}
|
|
42
|
+
interface SecretCall {
|
|
43
|
+
method: "getSecret" | "getSecrets";
|
|
44
|
+
vault: string;
|
|
45
|
+
name?: string;
|
|
46
|
+
names?: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
interface AuthConnectionCall {
|
|
49
|
+
connectionName: string;
|
|
50
|
+
}
|
|
51
|
+
interface IdpCall {
|
|
52
|
+
method: string;
|
|
53
|
+
args: unknown[];
|
|
54
|
+
namespace: string;
|
|
55
|
+
}
|
|
56
|
+
interface FileCall {
|
|
57
|
+
method: string;
|
|
58
|
+
namespace: string;
|
|
59
|
+
typeName: string;
|
|
60
|
+
fieldName: string;
|
|
61
|
+
recordId: string;
|
|
62
|
+
}
|
|
63
|
+
interface IconvCall {
|
|
64
|
+
method: string;
|
|
65
|
+
args: unknown[];
|
|
66
|
+
}
|
|
67
|
+
interface WorkflowCall {
|
|
68
|
+
method: "triggerWorkflow" | "wait" | "resolve";
|
|
69
|
+
args: unknown[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Mock control object for TailorDB operations.
|
|
73
|
+
*
|
|
74
|
+
* Automatically injected into `globalThis.tailordb` by the tailor-runtime environment.
|
|
75
|
+
* Use this object to configure query responses and assert on executed queries.
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* import { tailordbMock } from "@tailor-platform/sdk/vitest";
|
|
79
|
+
*
|
|
80
|
+
* beforeEach(() => tailordbMock.reset());
|
|
81
|
+
*
|
|
82
|
+
* test("content-based", () => {
|
|
83
|
+
* tailordbMock.setQueryResolver((query) => {
|
|
84
|
+
* if (query.includes("SELECT")) return [{ id: "1" }];
|
|
85
|
+
* return [];
|
|
86
|
+
* });
|
|
87
|
+
* });
|
|
88
|
+
*
|
|
89
|
+
* test("order-based", () => {
|
|
90
|
+
* tailordbMock.enqueueResults(
|
|
91
|
+
* [], // BEGIN (empty result)
|
|
92
|
+
* [{ age: 30 }], // SELECT (one row)
|
|
93
|
+
* [], // COMMIT (empty result)
|
|
94
|
+
* );
|
|
95
|
+
* });
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
declare const tailordbMock: {
|
|
99
|
+
/**
|
|
100
|
+
* Set a fallback query resolver. Called when the result queue is empty.
|
|
101
|
+
* @param resolver - Function that returns rows for a given query and params
|
|
102
|
+
*/
|
|
103
|
+
setQueryResolver(resolver: QueryResolver): void;
|
|
104
|
+
/**
|
|
105
|
+
* Enqueue rows for the next `queryObject` call. Arguments are the row objects returned
|
|
106
|
+
* by that single query. Call with no arguments for an empty result. Consumed in FIFO
|
|
107
|
+
* order; when the queue is exhausted, subsequent calls fall back to `setQueryResolver`
|
|
108
|
+
* (default: empty rows). Use `enqueueResults` to stage rows for multiple queries in one
|
|
109
|
+
* call.
|
|
110
|
+
* @param rows - Row objects to return from the next `queryObject` call
|
|
111
|
+
*/
|
|
112
|
+
enqueueResult(...rows: unknown[]): void;
|
|
113
|
+
/**
|
|
114
|
+
* Enqueue rows for multiple subsequent `queryObject` calls. Each argument is a rows
|
|
115
|
+
* array for one query, consumed in FIFO order.
|
|
116
|
+
* @param rowsList - Rows arrays, one per upcoming query
|
|
117
|
+
*/
|
|
118
|
+
enqueueResults(...rowsList: unknown[][]): void;
|
|
119
|
+
/**
|
|
120
|
+
* All queries executed via `queryObject`, in order.
|
|
121
|
+
* @returns Executed queries array
|
|
122
|
+
*/
|
|
123
|
+
readonly executedQueries: ExecutedQuery[];
|
|
124
|
+
/**
|
|
125
|
+
* All TailorDB clients created, with their namespace and end state.
|
|
126
|
+
* @returns Created clients array
|
|
127
|
+
*/
|
|
128
|
+
readonly createdClients: CreatedClient[]; /** Reset all TailorDB mock state. Call in `beforeEach`. */
|
|
129
|
+
reset(): void;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Mock control object for workflow operations.
|
|
133
|
+
*
|
|
134
|
+
* Automatically injected into `globalThis.tailor.workflow` by the tailor-runtime environment.
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* import { workflowMock } from "@tailor-platform/sdk/vitest";
|
|
138
|
+
*
|
|
139
|
+
* beforeEach(() => workflowMock.reset());
|
|
140
|
+
*
|
|
141
|
+
* test("job handler", () => {
|
|
142
|
+
* workflowMock.setJobHandler((jobName, args) => {
|
|
143
|
+
* if (jobName === "validate") return { valid: true };
|
|
144
|
+
* return null;
|
|
145
|
+
* });
|
|
146
|
+
* });
|
|
147
|
+
*
|
|
148
|
+
* test("wait point", () => {
|
|
149
|
+
* workflowMock.setWaitHandler(() => ({ approved: true }));
|
|
150
|
+
* // …
|
|
151
|
+
* expect(workflowMock.waitCalls).toEqual([{ key: "approval", payload: undefined }]);
|
|
152
|
+
* });
|
|
153
|
+
*
|
|
154
|
+
* test("resolve point", () => {
|
|
155
|
+
* workflowMock.setResolveHandler((_executionId, _key, callback) =>
|
|
156
|
+
* callback({ approved: true }),
|
|
157
|
+
* );
|
|
158
|
+
* // …
|
|
159
|
+
* expect(workflowMock.resolveCalls).toEqual([
|
|
160
|
+
* { executionId: "mock-execution-id", key: "approval" },
|
|
161
|
+
* ]);
|
|
162
|
+
* });
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare const workflowMock: {
|
|
166
|
+
/**
|
|
167
|
+
* Set a fallback job handler. Called when the result queue is empty.
|
|
168
|
+
* @param handler - Function that returns a result for a given job name and args
|
|
169
|
+
*/
|
|
170
|
+
setJobHandler(handler: JobHandler): void;
|
|
171
|
+
/**
|
|
172
|
+
* Enqueue a single result for the next `triggerJobFunction` call. Consumed in FIFO
|
|
173
|
+
* order; when the queue is exhausted, subsequent calls fall back to `setJobHandler`
|
|
174
|
+
* (default: null). Use `enqueueResults` to stage multiple results in one call.
|
|
175
|
+
* @param result - Result to return from the next `triggerJobFunction` call
|
|
176
|
+
*/
|
|
177
|
+
enqueueResult(result: unknown): void;
|
|
178
|
+
/**
|
|
179
|
+
* Enqueue results for multiple subsequent `triggerJobFunction` calls.
|
|
180
|
+
* @param results - Results to enqueue, one per upcoming call
|
|
181
|
+
*/
|
|
182
|
+
enqueueResults(...results: unknown[]): void;
|
|
183
|
+
/**
|
|
184
|
+
* All jobs triggered via `triggerJobFunction`, in order.
|
|
185
|
+
* @returns Triggered jobs array
|
|
186
|
+
*/
|
|
187
|
+
readonly triggeredJobs: TriggeredJob[];
|
|
188
|
+
/**
|
|
189
|
+
* Configure what `tailor.workflow.triggerWorkflow` returns. Pass a string to return
|
|
190
|
+
* the same execution ID for every call, or a function `(name, args, options) => string`
|
|
191
|
+
* to compute one per call. Default: `"mock-execution-id"`.
|
|
192
|
+
* @param handler - Static execution ID or a function that returns one
|
|
193
|
+
*/
|
|
194
|
+
setTriggerHandler(handler: string | TriggerHandlerFn): void;
|
|
195
|
+
/**
|
|
196
|
+
* Configure what `tailor.workflow.wait` returns. Pass a function `(key, payload) => unknown`
|
|
197
|
+
* to compute one per call, or any other value to return it for every call. Default: `null`.
|
|
198
|
+
* @param handler - Static value or a function that returns one
|
|
199
|
+
*/
|
|
200
|
+
setWaitHandler: SetWaitHandler;
|
|
201
|
+
/**
|
|
202
|
+
* Configure how `tailor.workflow.resolve` runs the user-supplied callback. The handler
|
|
203
|
+
* receives `(executionId, key, callback)` — invoke `callback(payload)` to drive
|
|
204
|
+
* resolve→wait wiring in tests. Default: callback is not invoked (records the call only).
|
|
205
|
+
* @param handler - Function invoked per `resolve` call
|
|
206
|
+
*/
|
|
207
|
+
setResolveHandler(handler: ResolveHandler): void;
|
|
208
|
+
/**
|
|
209
|
+
* Calls to triggerWorkflow, wait, resolve (not triggerJobFunction — use triggeredJobs).
|
|
210
|
+
* @returns Workflow calls array
|
|
211
|
+
*/
|
|
212
|
+
readonly calls: WorkflowCall[];
|
|
213
|
+
/**
|
|
214
|
+
* `tailor.workflow.wait` calls reshaped as `{ key, payload }` for assertions.
|
|
215
|
+
* @returns Wait call records
|
|
216
|
+
*/
|
|
217
|
+
readonly waitCalls: {
|
|
218
|
+
key: string;
|
|
219
|
+
payload: unknown;
|
|
220
|
+
}[];
|
|
221
|
+
/**
|
|
222
|
+
* `tailor.workflow.resolve` calls reshaped as `{ executionId, key }` for assertions.
|
|
223
|
+
* @returns Resolve call records
|
|
224
|
+
*/
|
|
225
|
+
readonly resolveCalls: {
|
|
226
|
+
executionId: string;
|
|
227
|
+
key: string;
|
|
228
|
+
}[]; /** Reset all workflow mock state. Call in `beforeEach`. */
|
|
229
|
+
reset(): void;
|
|
230
|
+
};
|
|
231
|
+
/** Mock control for `tailor.secretmanager` — secret store and call recording. */
|
|
232
|
+
declare const secretmanagerMock: {
|
|
233
|
+
setSecrets(secrets: Record<string, Record<string, string>>): void;
|
|
234
|
+
readonly calls: SecretCall[];
|
|
235
|
+
reset(): void;
|
|
236
|
+
};
|
|
237
|
+
/** Mock control for `tailor.authconnection` — token store and call recording. */
|
|
238
|
+
declare const authconnectionMock: {
|
|
239
|
+
setTokens(tokens: Record<string, unknown>): void;
|
|
240
|
+
readonly calls: AuthConnectionCall[];
|
|
241
|
+
reset(): void;
|
|
242
|
+
};
|
|
243
|
+
/** Mock control for `tailor.idp` — IDP client responses and call recording. */
|
|
244
|
+
declare const idpMock: {
|
|
245
|
+
setResolver(resolver: IdpResolver): void;
|
|
246
|
+
/**
|
|
247
|
+
* Enqueue a single result for the next IDP call. Consumed in FIFO order; falls back
|
|
248
|
+
* to `setResolver` when exhausted. Use `enqueueResults` to stage multiple in one call.
|
|
249
|
+
* @param result - Result to return from the next IDP call
|
|
250
|
+
*/
|
|
251
|
+
enqueueResult(result: unknown): void;
|
|
252
|
+
/**
|
|
253
|
+
* Enqueue results for multiple subsequent IDP calls.
|
|
254
|
+
* @param results - Results to enqueue, one per upcoming call
|
|
255
|
+
*/
|
|
256
|
+
enqueueResults(...results: unknown[]): void;
|
|
257
|
+
readonly calls: IdpCall[];
|
|
258
|
+
reset(): void;
|
|
259
|
+
};
|
|
260
|
+
/** Mock control for `tailordb.file` — file operation responses and call recording. */
|
|
261
|
+
declare const fileMock: {
|
|
262
|
+
setResolver(resolver: FileResolver): void;
|
|
263
|
+
/**
|
|
264
|
+
* Enqueue a single result for the next `tailordb.file` call. Consumed in FIFO order;
|
|
265
|
+
* falls back to `setResolver` when exhausted. Use `enqueueResults` to stage multiple
|
|
266
|
+
* in one call.
|
|
267
|
+
* @param result - Result to return from the next file call
|
|
268
|
+
*/
|
|
269
|
+
enqueueResult(result: unknown): void;
|
|
270
|
+
/**
|
|
271
|
+
* Enqueue results for multiple subsequent `tailordb.file` calls.
|
|
272
|
+
* @param results - Results to enqueue, one per upcoming call
|
|
273
|
+
*/
|
|
274
|
+
enqueueResults(...results: unknown[]): void;
|
|
275
|
+
readonly calls: FileCall[];
|
|
276
|
+
reset(): void;
|
|
277
|
+
};
|
|
278
|
+
/** Mock control for `tailor.iconv` — encoding call recording. */
|
|
279
|
+
declare const iconvMock: {
|
|
280
|
+
setResolver(resolver: IconvResolver): void;
|
|
281
|
+
readonly calls: IconvCall[];
|
|
282
|
+
reset(): void;
|
|
283
|
+
};
|
|
284
|
+
//#endregion
|
|
285
|
+
//#region src/vitest/index.d.ts
|
|
286
|
+
/**
|
|
287
|
+
* Creates Vitest plugins that emulate the Tailor Platform function runtime environment.
|
|
288
|
+
*
|
|
289
|
+
* **Beta:** This API may change in future releases.
|
|
290
|
+
*
|
|
291
|
+
* ## What it does
|
|
292
|
+
*
|
|
293
|
+
* 1. **Node.js module blocking** (transform hook) — Imports of `node:*` modules
|
|
294
|
+
* (and bare builtins like `crypto`, `fs`) in non-test source files are replaced
|
|
295
|
+
* with code that throws an error with a suggestion for the Web Standard API alternative.
|
|
296
|
+
* Test files are exempt and can use `node:*` freely. Test file patterns are read
|
|
297
|
+
* from the resolved Vitest config (`test.include`).
|
|
298
|
+
*
|
|
299
|
+
* 2. **Node.js globals removal** (environment + setup) — Only globals available in the
|
|
300
|
+
* Tailor Platform runtime are kept (whitelist: ECMAScript standard, Web Standard APIs
|
|
301
|
+
* from bootstrap.js, platform mocks). All others (`Buffer`, `global`, `setImmediate`,
|
|
302
|
+
* `__dirname`, `__filename`, etc.) are removed. `performance` is removed per-test
|
|
303
|
+
* via beforeEach/afterEach since Vitest needs it during initialization.
|
|
304
|
+
*
|
|
305
|
+
* 3. **Platform API mocks** (environment) — All platform APIs are auto-injected with
|
|
306
|
+
* control objects: `tailordbMock`, `workflowMock`, `secretmanagerMock`,
|
|
307
|
+
* `authconnectionMock`, `idpMock`, `fileMock`, `iconvMock`. Each provides response
|
|
308
|
+
* configuration, call recording, and reset.
|
|
309
|
+
*
|
|
310
|
+
* 4. **Environment resolution** — Rewrites `environment: "tailor-runtime"` to the
|
|
311
|
+
* absolute path of the bundled environment module via the config hook.
|
|
312
|
+
*
|
|
313
|
+
* ## Known limitations
|
|
314
|
+
*
|
|
315
|
+
* - **`process`** and **`require`** are NOT removed or blocked. Vitest's internal
|
|
316
|
+
* runner depends on them. On the real Tailor Platform runtime, they do not exist.
|
|
317
|
+
* - **Dynamic `import()`** of bundled files (via `createImportMain()`) bypasses
|
|
318
|
+
* the transform hook since those files are loaded through Node.js native loader.
|
|
319
|
+
* ## Options
|
|
320
|
+
*
|
|
321
|
+
* - **`config`** — Path to `tailor.config.ts`. Loads `defineSecretManager()` values
|
|
322
|
+
* into `secretmanagerMock` so `getSecret()` returns the configured values.
|
|
323
|
+
* @example
|
|
324
|
+
* ```typescript
|
|
325
|
+
* // vitest.config.ts
|
|
326
|
+
* import { defineConfig } from "vitest/config";
|
|
327
|
+
* import { tailorRuntime } from "@tailor-platform/sdk/vitest";
|
|
328
|
+
*
|
|
329
|
+
* export default defineConfig({
|
|
330
|
+
* plugins: [tailorRuntime({ config: "./tailor.config.ts" })],
|
|
331
|
+
* test: {
|
|
332
|
+
* environment: "tailor-runtime",
|
|
333
|
+
* },
|
|
334
|
+
* });
|
|
335
|
+
* ```
|
|
336
|
+
* @param options - Optional configuration
|
|
337
|
+
* @param options.config - Path to tailor.config.ts to load SecretManager values into mock
|
|
338
|
+
* @returns Array of Vite plugins
|
|
339
|
+
*/
|
|
340
|
+
declare function tailorRuntime(options?: {
|
|
341
|
+
config?: string;
|
|
342
|
+
}): Plugin[];
|
|
343
|
+
//#endregion
|
|
344
|
+
export { authconnectionMock, fileMock, iconvMock, idpMock, secretmanagerMock, tailorRuntime, tailordbMock, workflowMock };
|
|
345
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
|
|
2
|
+
import { a as fileMock, d as workflowMock, l as secretmanagerMock, o as iconvMock, r as authconnectionMock, s as idpMock, u as tailordbMock } from "../mock-BP-9O5On.mjs";
|
|
3
|
+
import { builtinModules } from "node:module";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { dirname, isAbsolute, matchesGlob, relative, resolve } from "node:path";
|
|
6
|
+
|
|
7
|
+
//#region src/vitest/blocked-modules.ts
|
|
8
|
+
/**
|
|
9
|
+
* Blocked Node.js built-in modules and their Web Standard API alternatives.
|
|
10
|
+
*
|
|
11
|
+
* The Tailor Platform runtime only provides Web Standard APIs.
|
|
12
|
+
* These Node.js modules are not available and should be replaced with
|
|
13
|
+
* the suggested alternatives.
|
|
14
|
+
*/
|
|
15
|
+
const SUGGESTIONS = {
|
|
16
|
+
crypto: "Use the Web Crypto API (globalThis.crypto) instead.",
|
|
17
|
+
buffer: "Use Uint8Array or ArrayBuffer instead.",
|
|
18
|
+
fs: "File system access is not available in the Tailor Platform runtime.",
|
|
19
|
+
"fs/promises": "File system access is not available in the Tailor Platform runtime.",
|
|
20
|
+
path: "Use URL or URLPattern for path manipulation.",
|
|
21
|
+
http: "Use the Fetch API (globalThis.fetch) for HTTP requests instead.",
|
|
22
|
+
https: "Use the Fetch API (globalThis.fetch) for HTTPS requests instead.",
|
|
23
|
+
url: "Use the URL and URLSearchParams Web APIs instead.",
|
|
24
|
+
util: "Use Web Standard APIs instead.",
|
|
25
|
+
stream: "Use Web Streams API (ReadableStream, WritableStream, TransformStream) instead.",
|
|
26
|
+
"stream/web": "Use Web Streams API (ReadableStream, WritableStream, TransformStream) instead.",
|
|
27
|
+
events: "Use EventTarget instead.",
|
|
28
|
+
zlib: "Use CompressionStream and DecompressionStream Web APIs instead.",
|
|
29
|
+
querystring: "Use URLSearchParams instead.",
|
|
30
|
+
string_decoder: "Use TextDecoder instead."
|
|
31
|
+
};
|
|
32
|
+
const BLOCKED_MODULES = /* @__PURE__ */ new Set();
|
|
33
|
+
for (const mod of builtinModules) {
|
|
34
|
+
BLOCKED_MODULES.add(mod);
|
|
35
|
+
BLOCKED_MODULES.add(`node:${mod}`);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if a module specifier is a blocked Node.js built-in.
|
|
39
|
+
* @param specifier - Module specifier to check (e.g. "node:crypto", "fs")
|
|
40
|
+
* @returns Whether the specifier is blocked
|
|
41
|
+
*/
|
|
42
|
+
function isBlockedModule(specifier) {
|
|
43
|
+
return BLOCKED_MODULES.has(specifier);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Get the error message for a blocked module import.
|
|
47
|
+
* @param specifier - Module specifier that was blocked
|
|
48
|
+
* @returns Error message with optional suggestion for the Web Standard API alternative
|
|
49
|
+
*/
|
|
50
|
+
function getBlockedMessage(specifier) {
|
|
51
|
+
const suggestion = SUGGESTIONS[specifier.startsWith("node:") ? specifier.slice(5) : specifier];
|
|
52
|
+
const base = `"${specifier}" is not available in the Tailor Platform runtime.`;
|
|
53
|
+
return suggestion ? `${base} ${suggestion}` : base;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/vitest/plugin.ts
|
|
58
|
+
const DEFAULT_TEST_INCLUDE = ["**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"];
|
|
59
|
+
const IMPORT_LIKE_TYPES = new Set([
|
|
60
|
+
"ImportDeclaration",
|
|
61
|
+
"ExportNamedDeclaration",
|
|
62
|
+
"ExportAllDeclaration"
|
|
63
|
+
]);
|
|
64
|
+
const UNSAFE_BINDING_NAMES = new Set([
|
|
65
|
+
"break",
|
|
66
|
+
"case",
|
|
67
|
+
"catch",
|
|
68
|
+
"class",
|
|
69
|
+
"const",
|
|
70
|
+
"continue",
|
|
71
|
+
"debugger",
|
|
72
|
+
"default",
|
|
73
|
+
"delete",
|
|
74
|
+
"do",
|
|
75
|
+
"else",
|
|
76
|
+
"enum",
|
|
77
|
+
"export",
|
|
78
|
+
"extends",
|
|
79
|
+
"false",
|
|
80
|
+
"finally",
|
|
81
|
+
"for",
|
|
82
|
+
"function",
|
|
83
|
+
"if",
|
|
84
|
+
"import",
|
|
85
|
+
"in",
|
|
86
|
+
"instanceof",
|
|
87
|
+
"new",
|
|
88
|
+
"null",
|
|
89
|
+
"return",
|
|
90
|
+
"super",
|
|
91
|
+
"switch",
|
|
92
|
+
"this",
|
|
93
|
+
"throw",
|
|
94
|
+
"true",
|
|
95
|
+
"try",
|
|
96
|
+
"typeof",
|
|
97
|
+
"var",
|
|
98
|
+
"void",
|
|
99
|
+
"while",
|
|
100
|
+
"with",
|
|
101
|
+
"yield",
|
|
102
|
+
"let",
|
|
103
|
+
"static",
|
|
104
|
+
"implements",
|
|
105
|
+
"interface",
|
|
106
|
+
"package",
|
|
107
|
+
"private",
|
|
108
|
+
"protected",
|
|
109
|
+
"public",
|
|
110
|
+
"await",
|
|
111
|
+
"arguments",
|
|
112
|
+
"eval"
|
|
113
|
+
]);
|
|
114
|
+
const ID_START = /^[A-Za-z_$]/;
|
|
115
|
+
const ID_CONT = /^[A-Za-z0-9_$]*$/;
|
|
116
|
+
function isSafeBindingName(name) {
|
|
117
|
+
if (UNSAFE_BINDING_NAMES.has(name)) return false;
|
|
118
|
+
if (name.length === 0) return false;
|
|
119
|
+
return ID_START.test(name[0] ?? "") && ID_CONT.test(name.slice(1));
|
|
120
|
+
}
|
|
121
|
+
function buildBlockedReplacement(node, message) {
|
|
122
|
+
const literal = JSON.stringify(message);
|
|
123
|
+
const throwStmt = `throw new Error(${literal});`;
|
|
124
|
+
const throwExpr = `(() => { throw new Error(${literal}); })()`;
|
|
125
|
+
if (node.type === "ExportNamedDeclaration") {
|
|
126
|
+
const specs = node.specifiers ?? [];
|
|
127
|
+
const stubs = [];
|
|
128
|
+
for (const spec of specs) {
|
|
129
|
+
const exportedName = spec.exported?.name;
|
|
130
|
+
if (typeof exportedName !== "string") continue;
|
|
131
|
+
if (exportedName === "default") {
|
|
132
|
+
stubs.push(`export default ${throwExpr};`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (!isSafeBindingName(exportedName)) return throwStmt;
|
|
136
|
+
stubs.push(`export const ${exportedName} = ${throwExpr};`);
|
|
137
|
+
}
|
|
138
|
+
return stubs.length > 0 ? stubs.join(" ") : throwStmt;
|
|
139
|
+
}
|
|
140
|
+
if (node.type === "ExportAllDeclaration") {
|
|
141
|
+
const exportedName = node.exported?.name;
|
|
142
|
+
if (typeof exportedName === "string" && isSafeBindingName(exportedName)) return `export const ${exportedName} = ${throwExpr};`;
|
|
143
|
+
return throwStmt;
|
|
144
|
+
}
|
|
145
|
+
return throwStmt;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Vite plugin that blocks Node.js built-in module imports from production code.
|
|
149
|
+
*
|
|
150
|
+
* Uses the `transform` hook to walk the Rollup-provided AST of non-test source
|
|
151
|
+
* files for static `node:*` imports and re-exports.
|
|
152
|
+
* `ImportDeclaration` and bare `export * from "..."` are replaced with a
|
|
153
|
+
* `throw new Error(...)` statement so the failure surfaces at evaluation time.
|
|
154
|
+
* `ExportNamedDeclaration` (`export { x, y as z } from "..."`) and namespaced
|
|
155
|
+
* `export * as ns from "..."` are rewritten to per-binding stub exports
|
|
156
|
+
* (`export const x = (() => { throw new Error(...) })();`). The IIFE throws
|
|
157
|
+
* eagerly during module evaluation (same timing as a top-level `throw`), but
|
|
158
|
+
* preserving the declared export bindings ensures the surfaced error is the
|
|
159
|
+
* actual "node:* not available" message rather than an opaque
|
|
160
|
+
* "missing export" raised by the loader.
|
|
161
|
+
* Vitest treats `node:*` as external SSR modules (skipping `resolveId`), so
|
|
162
|
+
* source-level transformation is the only reliable interception point.
|
|
163
|
+
* Runs in the default phase (no `enforce: "pre"`) so esbuild's TypeScript
|
|
164
|
+
* transform strips `import type` first; only runtime imports reach this hook.
|
|
165
|
+
* Node.js globals not in the platform runtime are removed by the environment (whitelist-based).
|
|
166
|
+
* Test file patterns are read from the resolved Vitest config (`test.include`).
|
|
167
|
+
* Vitest setup files (`test.setupFiles`) and global-setup files
|
|
168
|
+
* (`test.globalSetup`) are also exempted: they run in the test runner host,
|
|
169
|
+
* not in the emulated platform runtime, so they may freely use `node:*`
|
|
170
|
+
* modules (e.g. `node:url` for `pathToFileURL`).
|
|
171
|
+
* @returns Vite plugin
|
|
172
|
+
*/
|
|
173
|
+
function createBlockPlugin() {
|
|
174
|
+
let isTestFile = () => false;
|
|
175
|
+
let isUserSourceFile = () => false;
|
|
176
|
+
return {
|
|
177
|
+
name: "tailor-runtime-block-node",
|
|
178
|
+
configResolved(config) {
|
|
179
|
+
const testConfig = config.test;
|
|
180
|
+
const root = testConfig?.root ?? config.root;
|
|
181
|
+
const toAbsolutePaths = (value, baseRoot) => (Array.isArray(value) ? value : value ? [value] : []).map((f) => resolve(baseRoot, f));
|
|
182
|
+
const exemptHostFiles = new Set([...toAbsolutePaths(testConfig?.setupFiles, root), ...toAbsolutePaths(testConfig?.globalSetup, root)]);
|
|
183
|
+
const includePairs = [{
|
|
184
|
+
root,
|
|
185
|
+
patterns: testConfig?.include ?? DEFAULT_TEST_INCLUDE
|
|
186
|
+
}];
|
|
187
|
+
for (const project of testConfig?.projects ?? []) {
|
|
188
|
+
const projectTest = project?.test;
|
|
189
|
+
if (!projectTest) continue;
|
|
190
|
+
const projectRoot = projectTest.root ?? root;
|
|
191
|
+
for (const f of toAbsolutePaths(projectTest.setupFiles, projectRoot)) exemptHostFiles.add(f);
|
|
192
|
+
for (const f of toAbsolutePaths(projectTest.globalSetup, projectRoot)) exemptHostFiles.add(f);
|
|
193
|
+
includePairs.push({
|
|
194
|
+
root: projectRoot,
|
|
195
|
+
patterns: projectTest.include ?? DEFAULT_TEST_INCLUDE
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
isTestFile = (id) => {
|
|
199
|
+
if (exemptHostFiles.has(id)) return true;
|
|
200
|
+
return includePairs.some(({ root: r, patterns }) => {
|
|
201
|
+
const candidate = isAbsolute(id) ? relative(r, id) : id;
|
|
202
|
+
return patterns.some((pattern) => matchesGlob(candidate, pattern));
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
isUserSourceFile = (id) => {
|
|
206
|
+
if (!isAbsolute(id)) return false;
|
|
207
|
+
const rel = relative(root, id);
|
|
208
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
transform(code, id) {
|
|
212
|
+
const queryIdx = id.search(/[?#]/);
|
|
213
|
+
const cleanId = queryIdx === -1 ? id : id.slice(0, queryIdx);
|
|
214
|
+
if (isTestFile(cleanId)) return void 0;
|
|
215
|
+
if (cleanId.includes("node_modules")) return void 0;
|
|
216
|
+
if (!isUserSourceFile(cleanId)) return void 0;
|
|
217
|
+
let ast;
|
|
218
|
+
try {
|
|
219
|
+
ast = this.parse(code);
|
|
220
|
+
} catch {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const replacements = [];
|
|
224
|
+
for (const node of ast.body) {
|
|
225
|
+
if (!IMPORT_LIKE_TYPES.has(node.type)) continue;
|
|
226
|
+
const specifier = node.source?.value;
|
|
227
|
+
if (typeof specifier !== "string") continue;
|
|
228
|
+
if (isBlockedModule(specifier)) replacements.push({
|
|
229
|
+
start: node.start,
|
|
230
|
+
end: node.end,
|
|
231
|
+
replacement: buildBlockedReplacement(node, getBlockedMessage(specifier))
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (replacements.length === 0) return void 0;
|
|
235
|
+
let transformed = code;
|
|
236
|
+
for (const r of replacements.sort((a, b) => b.start - a.start)) transformed = transformed.slice(0, r.start) + r.replacement + transformed.slice(r.end);
|
|
237
|
+
return {
|
|
238
|
+
code: transformed,
|
|
239
|
+
map: null
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const ENVIRONMENT_NAME = "tailor-runtime";
|
|
245
|
+
/**
|
|
246
|
+
* Vite plugin that resolves the tailor-runtime environment and injects setup files.
|
|
247
|
+
*
|
|
248
|
+
* Vitest resolves environments starting with "." or "/" as file paths.
|
|
249
|
+
* This plugin rewrites `environment: "tailor-runtime"` to the absolute path
|
|
250
|
+
* of the bundled environment module, both at the top-level and per-project.
|
|
251
|
+
* It also injects the setup file that removes Vitest-dependent globals
|
|
252
|
+
* (like `performance`) per-test via beforeEach/afterEach hooks.
|
|
253
|
+
* @param options - Optional configuration
|
|
254
|
+
* @param options.config - Path to tailor.config.ts to load SecretManager values into mock
|
|
255
|
+
* @returns Vite plugin
|
|
256
|
+
*/
|
|
257
|
+
function createEnvironmentPlugin(options) {
|
|
258
|
+
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
259
|
+
const environmentPath = resolve(currentDir, "environment.mjs");
|
|
260
|
+
const setupPath = resolve(currentDir, "setup.mjs");
|
|
261
|
+
return {
|
|
262
|
+
name: "tailor-runtime-environment",
|
|
263
|
+
config(config) {
|
|
264
|
+
const testConfig = config.test;
|
|
265
|
+
let usesTailorRuntime = false;
|
|
266
|
+
if (testConfig?.environment === ENVIRONMENT_NAME) {
|
|
267
|
+
testConfig.environment = environmentPath;
|
|
268
|
+
usesTailorRuntime = true;
|
|
269
|
+
}
|
|
270
|
+
if (testConfig?.projects) for (const project of testConfig.projects) {
|
|
271
|
+
const projectTest = project.test;
|
|
272
|
+
if (projectTest?.environment === ENVIRONMENT_NAME) {
|
|
273
|
+
projectTest.environment = environmentPath;
|
|
274
|
+
usesTailorRuntime = true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
delete process.env.__TAILOR_RUNTIME_CONFIG;
|
|
278
|
+
if (options?.config && usesTailorRuntime) {
|
|
279
|
+
const configAbsPath = resolve(config.root ?? process.cwd(), options.config);
|
|
280
|
+
process.env.__TAILOR_RUNTIME_CONFIG = configAbsPath;
|
|
281
|
+
}
|
|
282
|
+
if (testConfig && typeof testConfig.setupFiles === "string") testConfig.setupFiles = [testConfig.setupFiles];
|
|
283
|
+
return { test: { setupFiles: [setupPath] } };
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
//#endregion
|
|
289
|
+
//#region src/vitest/index.ts
|
|
290
|
+
/**
|
|
291
|
+
* Creates Vitest plugins that emulate the Tailor Platform function runtime environment.
|
|
292
|
+
*
|
|
293
|
+
* **Beta:** This API may change in future releases.
|
|
294
|
+
*
|
|
295
|
+
* ## What it does
|
|
296
|
+
*
|
|
297
|
+
* 1. **Node.js module blocking** (transform hook) — Imports of `node:*` modules
|
|
298
|
+
* (and bare builtins like `crypto`, `fs`) in non-test source files are replaced
|
|
299
|
+
* with code that throws an error with a suggestion for the Web Standard API alternative.
|
|
300
|
+
* Test files are exempt and can use `node:*` freely. Test file patterns are read
|
|
301
|
+
* from the resolved Vitest config (`test.include`).
|
|
302
|
+
*
|
|
303
|
+
* 2. **Node.js globals removal** (environment + setup) — Only globals available in the
|
|
304
|
+
* Tailor Platform runtime are kept (whitelist: ECMAScript standard, Web Standard APIs
|
|
305
|
+
* from bootstrap.js, platform mocks). All others (`Buffer`, `global`, `setImmediate`,
|
|
306
|
+
* `__dirname`, `__filename`, etc.) are removed. `performance` is removed per-test
|
|
307
|
+
* via beforeEach/afterEach since Vitest needs it during initialization.
|
|
308
|
+
*
|
|
309
|
+
* 3. **Platform API mocks** (environment) — All platform APIs are auto-injected with
|
|
310
|
+
* control objects: `tailordbMock`, `workflowMock`, `secretmanagerMock`,
|
|
311
|
+
* `authconnectionMock`, `idpMock`, `fileMock`, `iconvMock`. Each provides response
|
|
312
|
+
* configuration, call recording, and reset.
|
|
313
|
+
*
|
|
314
|
+
* 4. **Environment resolution** — Rewrites `environment: "tailor-runtime"` to the
|
|
315
|
+
* absolute path of the bundled environment module via the config hook.
|
|
316
|
+
*
|
|
317
|
+
* ## Known limitations
|
|
318
|
+
*
|
|
319
|
+
* - **`process`** and **`require`** are NOT removed or blocked. Vitest's internal
|
|
320
|
+
* runner depends on them. On the real Tailor Platform runtime, they do not exist.
|
|
321
|
+
* - **Dynamic `import()`** of bundled files (via `createImportMain()`) bypasses
|
|
322
|
+
* the transform hook since those files are loaded through Node.js native loader.
|
|
323
|
+
* ## Options
|
|
324
|
+
*
|
|
325
|
+
* - **`config`** — Path to `tailor.config.ts`. Loads `defineSecretManager()` values
|
|
326
|
+
* into `secretmanagerMock` so `getSecret()` returns the configured values.
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* // vitest.config.ts
|
|
330
|
+
* import { defineConfig } from "vitest/config";
|
|
331
|
+
* import { tailorRuntime } from "@tailor-platform/sdk/vitest";
|
|
332
|
+
*
|
|
333
|
+
* export default defineConfig({
|
|
334
|
+
* plugins: [tailorRuntime({ config: "./tailor.config.ts" })],
|
|
335
|
+
* test: {
|
|
336
|
+
* environment: "tailor-runtime",
|
|
337
|
+
* },
|
|
338
|
+
* });
|
|
339
|
+
* ```
|
|
340
|
+
* @param options - Optional configuration
|
|
341
|
+
* @param options.config - Path to tailor.config.ts to load SecretManager values into mock
|
|
342
|
+
* @returns Array of Vite plugins
|
|
343
|
+
*/
|
|
344
|
+
function tailorRuntime(options) {
|
|
345
|
+
return [createBlockPlugin(), createEnvironmentPlugin(options)];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
//#endregion
|
|
349
|
+
export { authconnectionMock, fileMock, iconvMock, idpMock, secretmanagerMock, tailorRuntime, tailordbMock, workflowMock };
|
|
350
|
+
//# sourceMappingURL=index.mjs.map
|