@tailor-platform/sdk 2.0.0-next.5 → 2.0.0-next.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/{application-Bf8wxOi6.mjs → application-BJXRpQj5.mjs} +190 -1258
  3. package/dist/application-BJXRpQj5.mjs.map +1 -0
  4. package/dist/application-BV-AXawv.mjs +3 -0
  5. package/dist/cli/commands/deploy/deploy.d.mts +8 -3
  6. package/dist/cli/commands/generate/options.d.mts +0 -1
  7. package/dist/cli/commands/generate/service.d.mts +1 -1
  8. package/dist/cli/commands/staticwebsite/deploy.d.mts +19 -0
  9. package/dist/cli/commands/workspace/create.d.mts +3 -0
  10. package/dist/cli/commands/workspace/list.d.mts +1 -0
  11. package/dist/cli/commands/workspace/transform.d.mts +2 -0
  12. package/dist/cli/index.mjs +776 -1551
  13. package/dist/cli/index.mjs.map +1 -1
  14. package/dist/cli/lib.d.mts +7 -3
  15. package/dist/cli/lib.mjs +49 -3
  16. package/dist/cli/lib.mjs.map +1 -1
  17. package/dist/cli/shared/config-loader.d.mts +1 -1
  18. package/dist/cli/shared/readonly-guard.d.mts +25 -0
  19. package/dist/cli/shared/tailordb-namespaces.d.mts +37 -0
  20. package/dist/cli/shared/workspace-name.d.mts +1 -0
  21. package/dist/completion/zsh-worker.zsh +15 -303
  22. package/dist/configure/index.mjs +7 -6
  23. package/dist/configure/index.mjs.map +1 -1
  24. package/dist/configure/services/executor/executor.d.mts +2 -13
  25. package/dist/configure/services/executor/operation.d.mts +7 -2
  26. package/dist/configure/services/tailordb/schema.d.mts +1 -7
  27. package/dist/configure/services/tailordb/types.d.mts +1 -1
  28. package/dist/configure/services/workflow/execution-policy.d.mts +2 -2
  29. package/dist/configure/types/type.d.mts +1 -9
  30. package/dist/{crashreport-D3RjuSYb.mjs → crashreport-0ioNRZ4K.mjs} +1 -1
  31. package/dist/{crashreport-Cg-pqRJV.mjs → crashreport-CxHne11E.mjs} +4 -3
  32. package/dist/{crashreport-Cg-pqRJV.mjs.map → crashreport-CxHne11E.mjs.map} +1 -1
  33. package/dist/errors-DfnF4ibq.mjs +100 -0
  34. package/dist/errors-DfnF4ibq.mjs.map +1 -0
  35. package/dist/{globals-BjhJJ2dN.mjs → globals-D-YbJKW-.mjs} +16 -10
  36. package/dist/globals-D-YbJKW-.mjs.map +1 -0
  37. package/dist/{logger-BEiZZ3qT.mjs → logger-BwS4ppwO.mjs} +3 -3
  38. package/dist/logger-BwS4ppwO.mjs.map +1 -0
  39. package/dist/parser/service/tailordb/type-source.d.mts +10 -0
  40. package/dist/{register-ts-hook-BhzVkej0.mjs → register-ts-hook-DL31O2W9.mjs} +2623 -1645
  41. package/dist/register-ts-hook-DL31O2W9.mjs.map +1 -0
  42. package/dist/{registry-BSbq6SPo.mjs → registry-CC3CbQiF.mjs} +3 -3
  43. package/dist/registry-CC3CbQiF.mjs.map +1 -0
  44. package/dist/runtime/field-parse.d.mts +10 -0
  45. package/dist/runtime/globals.d.mts +5 -2
  46. package/dist/runtime/index.mjs +1 -1
  47. package/dist/runtime/workflow.d.mts +70 -10
  48. package/dist/runtime/workflow.mjs +1 -1
  49. package/dist/{schema-iCd1ugGp.mjs → schema--xYWRGfe.mjs} +42 -24
  50. package/dist/schema--xYWRGfe.mjs.map +1 -0
  51. package/dist/{secret-file-aoNXJmKa.mjs → secret-file-C3J4Gy8r.mjs} +2 -27
  52. package/dist/secret-file-C3J4Gy8r.mjs.map +1 -0
  53. package/dist/{secretmanager-Bd45j7an.mjs → secretmanager-IY4UvinW.mjs} +42 -9
  54. package/dist/secretmanager-IY4UvinW.mjs.map +1 -0
  55. package/dist/{service-CzZEVrp9.mjs → service-CnHz9rwz.mjs} +34 -12
  56. package/dist/service-CnHz9rwz.mjs.map +1 -0
  57. package/dist/{service-DeZeYa06.mjs → service-DaeJlBoH.mjs} +3 -3
  58. package/dist/{service-DeZeYa06.mjs.map → service-DaeJlBoH.mjs.map} +1 -1
  59. package/dist/service-nU6ITOHL.mjs +3 -0
  60. package/dist/service_pb-BoLM0X4K.mjs +167 -0
  61. package/dist/service_pb-BoLM0X4K.mjs.map +1 -0
  62. package/dist/{service_pb-DZkfjB_6.mjs → service_pb-r1T7kpJc.mjs} +1 -1
  63. package/dist/types/executor.generated.d.mts +60 -42
  64. package/dist/user-agent-Bgsszb5I.mjs +28 -0
  65. package/dist/user-agent-Bgsszb5I.mjs.map +1 -0
  66. package/dist/vitest/environment.mjs +1 -1
  67. package/dist/vitest/index.d.mts +8 -8
  68. package/dist/vitest/index.mjs +591 -173
  69. package/dist/vitest/index.mjs.map +1 -1
  70. package/dist/vitest/mock.d.mts +8 -8
  71. package/dist/vitest/mocks/aigateway.d.mts +14 -4
  72. package/dist/vitest/mocks/authconnection.d.mts +14 -4
  73. package/dist/vitest/mocks/file.d.mts +23 -6
  74. package/dist/vitest/mocks/iconv.d.mts +28 -4
  75. package/dist/vitest/mocks/idp.d.mts +25 -9
  76. package/dist/vitest/mocks/secretmanager.d.mts +13 -4
  77. package/dist/vitest/mocks/tailordb.d.mts +48 -7
  78. package/dist/vitest/mocks/workflow.d.mts +68 -22
  79. package/dist/vitest/setup.mjs +2 -2
  80. package/dist/workflow-9kHGKxF2.mjs +64 -0
  81. package/dist/workflow-9kHGKxF2.mjs.map +1 -0
  82. package/dist/{service_pb-Bk7dL-yT.mjs → workspace_resource_pb-Db3fv68L.mjs} +47 -165
  83. package/dist/workspace_resource_pb-Db3fv68L.mjs.map +1 -0
  84. package/docs/cli/application.md +61 -15
  85. package/docs/cli/tailordb.md +8 -140
  86. package/docs/cli/workspace.md +6 -4
  87. package/docs/cli-reference.md +11 -8
  88. package/docs/github-actions.md +8 -0
  89. package/docs/migration/v2.md +41 -0
  90. package/docs/runtime.md +5 -6
  91. package/docs/services/executor.md +5 -0
  92. package/docs/services/tailordb.md +44 -0
  93. package/docs/services/workflow.md +2 -2
  94. package/docs/testing.md +105 -89
  95. package/package.json +2 -5
  96. package/dist/application-Bf8wxOi6.mjs.map +0 -1
  97. package/dist/application-CUkjzfij.mjs +0 -3
  98. package/dist/cli/erd-viewer-assets/app.js +0 -1424
  99. package/dist/cli/erd-viewer-assets/index.html +0 -77
  100. package/dist/cli/erd-viewer-assets/serve.json +0 -13
  101. package/dist/cli/erd-viewer-assets/styles.css +0 -1036
  102. package/dist/configure/types/field-runtime.d.mts +0 -16
  103. package/dist/errors-118wUDRr.mjs +0 -42
  104. package/dist/errors-118wUDRr.mjs.map +0 -1
  105. package/dist/globals-BjhJJ2dN.mjs.map +0 -1
  106. package/dist/logger-BEiZZ3qT.mjs.map +0 -1
  107. package/dist/register-ts-hook-BhzVkej0.mjs.map +0 -1
  108. package/dist/registry-BSbq6SPo.mjs.map +0 -1
  109. package/dist/resource_pb-BRv7AMXK.mjs +0 -49
  110. package/dist/resource_pb-BRv7AMXK.mjs.map +0 -1
  111. package/dist/schema-iCd1ugGp.mjs.map +0 -1
  112. package/dist/secret-file-aoNXJmKa.mjs.map +0 -1
  113. package/dist/secretmanager-Bd45j7an.mjs.map +0 -1
  114. package/dist/service-BT0GPd01.mjs +0 -3
  115. package/dist/service-CzZEVrp9.mjs.map +0 -1
  116. package/dist/service_pb-Bk7dL-yT.mjs.map +0 -1
  117. package/dist/workflow-GNuSnsTx.mjs +0 -22
  118. package/dist/workflow-GNuSnsTx.mjs.map +0 -1
@@ -1,9 +1,10 @@
1
- import { i as getRegisteredJob, t as TRIGGER_DEFAULT } from "../registry-BSbq6SPo.mjs";
1
+ import { i as getRegisteredJob, t as TRIGGER_DEFAULT } from "../registry-CC3CbQiF.mjs";
2
2
  import { a as writeWorkflowTestEnv, n as clearWorkflowTestEnv, r as readWorkflowTestEnv, t as buildJobContext } from "../test-env-key-D7UkZp99.mjs";
3
3
  import { t as assertDefined } from "../assert-DBxo8jPo.mjs";
4
4
  import { t as platformSerialize } from "../platform-serialize-RoRtBS0v.mjs";
5
- import { i as withDispose, n as tailorRoot, r as tailordbRoot, t as mockSecretmanager } from "../secretmanager-Bd45j7an.mjs";
5
+ import { i as withDispose, n as tailorRoot, r as tailordbRoot, t as mockSecretmanager } from "../secretmanager-IY4UvinW.mjs";
6
6
  import { builtinModules } from "node:module";
7
+ import { isEqual } from "es-toolkit";
7
8
  import { fileURLToPath } from "node:url";
8
9
  import { dirname, isAbsolute, matchesGlob, relative, resolve } from "node:path";
9
10
  import { ColumnNode, InsertQueryNode, Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler, PrimitiveValueListNode, ReferenceNode, UpdateQueryNode, ValueListNode, ValueNode, ValuesNode } from "kysely";
@@ -297,27 +298,31 @@ function createEnvironmentPlugin(options) {
297
298
  //#region src/vitest/mocks/aigateway.ts
298
299
  /**
299
300
  * Acquire a disposable mock for `tailor.aigateway`. Restored on dispose.
301
+ * @param options - Initial AI Gateway URL fixtures
300
302
  * @returns Disposable AI Gateway mock control object
301
303
  * @example
302
304
  * ```typescript
303
305
  * import { mockAigateway } from "@tailor-platform/sdk/vitest";
304
306
  *
305
307
  * test("resolves an AI Gateway URL", async () => {
306
- * using aigateway = mockAigateway();
307
- * aigateway.setUrls({ "my-aigateway": "https://my-aigateway.example.com" });
308
+ * using aigateway = mockAigateway({
309
+ * urls: { "my-aigateway": "https://my-aigateway.example.com" },
310
+ * });
311
+ * aigateway.setUrl("my-aigateway", "https://replacement.example.com");
308
312
  * // …
309
313
  * });
310
314
  * ```
311
315
  */
312
- function mockAigateway() {
316
+ function mockAigateway(options = {}) {
313
317
  const root = tailorRoot();
314
318
  const prev = root.aigateway;
315
- let urls = {};
316
- const get = vi.fn(async (name) => {
319
+ let urls = { ...options.urls };
320
+ async function defaultGet(name) {
317
321
  const url = urls[name];
318
322
  if (url === void 0) throw new Error(`No AI Gateway registered for "${name}". Acquire mockAigateway() and call setUrls(...).`);
319
323
  return { url };
320
- });
324
+ }
325
+ const get = vi.fn(defaultGet);
321
326
  root.aigateway = { get };
322
327
  return withDispose({
323
328
  /** The `get` `vi.fn`. */
@@ -325,12 +330,22 @@ function mockAigateway() {
325
330
  setUrls(value) {
326
331
  urls = value;
327
332
  },
333
+ setUrl(name, url) {
334
+ urls = {
335
+ ...urls,
336
+ [name]: url
337
+ };
338
+ },
328
339
  get calls() {
329
340
  return get.mock.calls.map(([name]) => ({ name }));
330
341
  },
342
+ clear() {
343
+ get.mockClear();
344
+ },
331
345
  reset() {
332
346
  urls = {};
333
- get.mockClear();
347
+ get.mockReset();
348
+ get.mockImplementation(defaultGet);
334
349
  }
335
350
  }, () => {
336
351
  root.aigateway = prev;
@@ -341,23 +356,30 @@ function mockAigateway() {
341
356
  //#region src/vitest/mocks/authconnection.ts
342
357
  /**
343
358
  * Acquire a disposable mock for `tailor.authconnection`. Restored on dispose.
359
+ * @param options - Initial token fixtures and fallback behavior
344
360
  * @returns Disposable AuthConnection mock control object
345
361
  * @example
346
362
  * ```typescript
347
363
  * import { mockAuthconnection } from "@tailor-platform/sdk/vitest";
348
364
  *
349
365
  * test("returns configured token", async () => {
350
- * using ac = mockAuthconnection();
351
- * ac.setTokens({ google: { access_token: "ya29.xxx" } });
366
+ * using ac = mockAuthconnection({ tokens: { google: { access_token: "ya29.xxx" } } });
367
+ * ac.setToken("google", { access_token: "replacement" });
352
368
  * // …
353
369
  * });
354
370
  * ```
355
371
  */
356
- function mockAuthconnection() {
372
+ function mockAuthconnection(options = {}) {
357
373
  const root = tailorRoot();
358
374
  const prev = root.authconnection;
359
- let tokens = {};
360
- const getConnectionToken = vi.fn(async (connectionName) => tokens[connectionName] ?? { access_token: "mock-token" });
375
+ let tokens = { ...options.tokens };
376
+ async function defaultGetConnectionToken(connectionName) {
377
+ const token = tokens[connectionName];
378
+ if (token) return token;
379
+ if (options.onUnhandled === "error") throw new Error(`No AuthConnection token configured for "${connectionName}"`);
380
+ return { access_token: "mock-token" };
381
+ }
382
+ const getConnectionToken = vi.fn(defaultGetConnectionToken);
361
383
  root.authconnection = { getConnectionToken };
362
384
  return withDispose({
363
385
  /** The `getConnectionToken` `vi.fn`. */
@@ -365,12 +387,22 @@ function mockAuthconnection() {
365
387
  setTokens(value) {
366
388
  tokens = value;
367
389
  },
390
+ setToken(connectionName, token) {
391
+ tokens = {
392
+ ...tokens,
393
+ [connectionName]: token
394
+ };
395
+ },
368
396
  get calls() {
369
397
  return getConnectionToken.mock.calls.map(([connectionName]) => ({ connectionName }));
370
398
  },
399
+ clear() {
400
+ getConnectionToken.mockClear();
401
+ },
371
402
  reset() {
372
403
  tokens = {};
373
- getConnectionToken.mockClear();
404
+ getConnectionToken.mockReset();
405
+ getConnectionToken.mockImplementation(defaultGetConnectionToken);
374
406
  }
375
407
  }, () => {
376
408
  root.authconnection = prev;
@@ -379,6 +411,15 @@ function mockAuthconnection() {
379
411
 
380
412
  //#endregion
381
413
  //#region src/vitest/mocks/file.ts
414
+ const FILE_METHODS = [
415
+ "upload",
416
+ "download",
417
+ "downloadAsBase64",
418
+ "delete",
419
+ "getMetadata",
420
+ "downloadStream",
421
+ "uploadStream"
422
+ ];
382
423
  const FILE_DEFAULTS = {
383
424
  upload: { metadata: {
384
425
  fileSize: 0,
@@ -402,7 +443,6 @@ const FILE_DEFAULTS = {
402
443
  lastUploadedAt: ""
403
444
  }
404
445
  },
405
- delete: void 0,
406
446
  getMetadata: {
407
447
  contentType: "",
408
448
  fileSize: 0,
@@ -417,6 +457,7 @@ const FILE_DEFAULTS = {
417
457
  };
418
458
  /**
419
459
  * Acquire a disposable mock for `tailordb.file`. Restored on dispose.
460
+ * @param options - Controls behavior for calls without a configured result
420
461
  * @returns Disposable File mock control object
421
462
  * @example
422
463
  * ```typescript
@@ -424,74 +465,102 @@ const FILE_DEFAULTS = {
424
465
  *
425
466
  * test("mock file download", async () => {
426
467
  * using file = mockFile();
427
- * file.enqueueResult({ data: new Uint8Array([1, 2, 3]), metadata: { ... } });
468
+ * file.download.mockResolvedValue({ data: new Uint8Array(), metadata: { ... } });
428
469
  * // …
429
470
  * });
430
471
  * ```
431
472
  */
432
- function mockFile() {
473
+ function mockFile(options = {}) {
433
474
  const root = tailordbRoot();
434
475
  const prev = root.file;
476
+ const { onUnhandled = "fallback" } = options;
435
477
  const queue = [];
436
- let resolver = () => null;
437
478
  const calls = [];
479
+ let resolver = () => null;
438
480
  function handle(method, namespace, typeName, fieldName, recordId) {
439
- const call = {
481
+ if (queue.length > 0) return queue.shift();
482
+ const resolved = resolver(method, {
440
483
  method,
441
484
  namespace,
442
485
  typeName,
443
486
  fieldName,
444
487
  recordId
445
- };
446
- calls.push(call);
447
- if (queue.length > 0) return queue.shift();
448
- const resolved = resolver(method, call);
488
+ });
449
489
  if (resolved != null) return resolved;
490
+ if (onUnhandled === "error") throw new Error(`No File mock configured for "${method}"`);
450
491
  const fallback = FILE_DEFAULTS[method];
451
492
  return fallback === void 0 ? void 0 : structuredClone(fallback);
452
493
  }
494
+ const upload = vi.fn(async (...args) => {
495
+ const [namespace, typeName, fieldName, recordId] = args;
496
+ return handle("upload", namespace, typeName, fieldName, recordId);
497
+ });
498
+ const download = vi.fn(async (...args) => handle("download", ...args));
499
+ const downloadAsBase64 = vi.fn(async (...args) => handle("downloadAsBase64", ...args));
500
+ const deleteFile = vi.fn(async (...args) => {
501
+ handle("delete", ...args);
502
+ });
503
+ const getMetadata = vi.fn(async (...args) => handle("getMetadata", ...args));
504
+ const downloadStream = vi.fn(async (...args) => {
505
+ const resolved = handle("downloadStream", ...args);
506
+ if (resolved != null) return resolved;
507
+ return {
508
+ body: new ReadableStream({ start(controller) {
509
+ controller.close();
510
+ } }),
511
+ metadata: {
512
+ contentType: "",
513
+ fileSize: 0,
514
+ sha256sum: "",
515
+ lastUploadedAt: ""
516
+ }
517
+ };
518
+ });
519
+ const uploadStream = vi.fn(async (...args) => {
520
+ const [namespace, typeName, fieldName, recordId] = args;
521
+ return handle("uploadStream", namespace, typeName, fieldName, recordId);
522
+ });
523
+ const mocks = {
524
+ upload,
525
+ download,
526
+ downloadAsBase64,
527
+ delete: deleteFile,
528
+ getMetadata,
529
+ downloadStream,
530
+ uploadStream
531
+ };
532
+ function track(method, operation) {
533
+ return function(...args) {
534
+ calls.push({
535
+ method,
536
+ namespace: args[0],
537
+ typeName: args[1],
538
+ fieldName: args[2],
539
+ recordId: args[3]
540
+ });
541
+ return operation.apply(this, args);
542
+ };
543
+ }
453
544
  root.file = {
454
- async upload(namespace, typeName, fieldName, recordId) {
455
- return handle("upload", namespace, typeName, fieldName, recordId);
456
- },
457
- async download(namespace, typeName, fieldName, recordId) {
458
- return handle("download", namespace, typeName, fieldName, recordId);
459
- },
460
- async downloadAsBase64(namespace, typeName, fieldName, recordId) {
461
- return handle("downloadAsBase64", namespace, typeName, fieldName, recordId);
462
- },
463
- async delete(namespace, typeName, fieldName, recordId) {
464
- handle("delete", namespace, typeName, fieldName, recordId);
465
- },
466
- async getMetadata(namespace, typeName, fieldName, recordId) {
467
- return handle("getMetadata", namespace, typeName, fieldName, recordId);
468
- },
469
- async downloadStream(namespace, typeName, fieldName, recordId) {
470
- const resolved = handle("downloadStream", namespace, typeName, fieldName, recordId);
471
- if (resolved != null) return resolved;
472
- return {
473
- body: new ReadableStream({ start(c) {
474
- c.close();
475
- } }),
476
- metadata: {
477
- contentType: "",
478
- fileSize: 0,
479
- sha256sum: "",
480
- lastUploadedAt: ""
481
- }
482
- };
483
- },
484
- async uploadStream(namespace, typeName, fieldName, recordId) {
485
- return handle("uploadStream", namespace, typeName, fieldName, recordId);
486
- }
545
+ upload: track("upload", upload),
546
+ download: track("download", download),
547
+ downloadAsBase64: track("downloadAsBase64", downloadAsBase64),
548
+ delete: track("delete", deleteFile),
549
+ getMetadata: track("getMetadata", getMetadata),
550
+ downloadStream: track("downloadStream", downloadStream),
551
+ uploadStream: track("uploadStream", uploadStream)
487
552
  };
553
+ function allMocks() {
554
+ return FILE_METHODS.map((method) => mocks[method]);
555
+ }
488
556
  return withDispose({
557
+ ...mocks,
489
558
  setResolver(value) {
490
559
  resolver = value;
491
560
  },
492
561
  /**
493
- * Enqueue a single result for the next `tailordb.file` call (FIFO; falls
494
- * back to `setResolver` when exhausted).
562
+ * Enqueue a single result for the next `tailordb.file` call.
563
+ * The queue is shared across all methods and namespaces.
495
564
  * @param result - Result to return from the next file call
496
565
  */
497
566
  enqueueResult(result) {
@@ -499,6 +568,7 @@ function mockFile() {
499
568
  },
500
569
  /**
501
570
  * Enqueue results for multiple subsequent `tailordb.file` calls.
571
+ * The queue is shared across all methods and namespaces.
502
572
  * @param results - Results to enqueue, one per upcoming call
503
573
  */
504
574
  enqueueResults(...results) {
@@ -507,10 +577,15 @@ function mockFile() {
507
577
  get calls() {
508
578
  return calls;
509
579
  },
580
+ clear() {
581
+ calls.length = 0;
582
+ for (const mock of allMocks()) mock.mockClear();
583
+ },
510
584
  reset() {
511
585
  queue.length = 0;
512
- resolver = () => null;
513
586
  calls.length = 0;
587
+ resolver = () => null;
588
+ for (const mock of allMocks()) mock.mockReset();
514
589
  }
515
590
  }, () => {
516
591
  root.file = prev;
@@ -529,11 +604,11 @@ function defaultIconvResult(method, args) {
529
604
  case "decode": return "";
530
605
  case "encode": return isUtf8(args[1]) ? "" : /* @__PURE__ */ new Uint8Array();
531
606
  case "encodings": return [];
532
- default: return;
533
607
  }
534
608
  }
535
609
  /**
536
610
  * Acquire a disposable mock for `tailor.iconv`. Restored on dispose.
611
+ * @param options - Fallback behavior for unconfigured operations
537
612
  * @returns Disposable Iconv mock control object
538
613
  * @example
539
614
  * ```typescript
@@ -541,27 +616,66 @@ function defaultIconvResult(method, args) {
541
616
  *
542
617
  * test("mock encoding conversion", () => {
543
618
  * using iconv = mockIconv();
544
- * iconv.setResolver((method) => (method === "decode" ? "decoded-text" : null));
619
+ * iconv.decode.mockReturnValue("decoded-text");
545
620
  * // …
546
621
  * });
547
622
  * ```
548
623
  */
549
- function mockIconv() {
624
+ function mockIconv(options = {}) {
550
625
  const root = tailorRoot();
551
626
  const prev = root.iconv;
552
627
  let resolver = null;
553
628
  const calls = [];
554
- function handle(method, args) {
555
- calls.push({
556
- method,
557
- args: [...args]
558
- });
629
+ function resolve(method, args) {
559
630
  if (resolver) {
560
631
  const result = resolver(method, args);
561
632
  if (result != null) return result;
562
633
  }
634
+ if (options.onUnhandled === "error") throw new Error(`No Iconv mock behavior configured for "${method}"`);
563
635
  return defaultIconvResult(method, args);
564
636
  }
637
+ function defaultConvert(input, fromEncoding, toEncoding) {
638
+ return resolve("convert", [
639
+ input,
640
+ fromEncoding,
641
+ toEncoding
642
+ ]);
643
+ }
644
+ function defaultConvertBuffer(input, fromEncoding, toEncoding) {
645
+ return resolve("convertBuffer", [
646
+ input,
647
+ fromEncoding,
648
+ toEncoding
649
+ ]);
650
+ }
651
+ function defaultDecode(input, encoding) {
652
+ return resolve("decode", [input, encoding]);
653
+ }
654
+ function defaultEncode(input, encoding) {
655
+ return resolve("encode", [input, encoding]);
656
+ }
657
+ function defaultEncodings() {
658
+ return resolve("encodings", []);
659
+ }
660
+ const convert = vi.fn(defaultConvert);
661
+ const convertBuffer = vi.fn(defaultConvertBuffer);
662
+ const decode = vi.fn(defaultDecode);
663
+ const encode = vi.fn(defaultEncode);
664
+ const encodings = vi.fn(defaultEncodings);
665
+ function track(method, operation) {
666
+ return function(...args) {
667
+ calls.push({
668
+ method,
669
+ args: [...args]
670
+ });
671
+ return operation.apply(this, args);
672
+ };
673
+ }
674
+ const trackedConvert = track("convert", convert);
675
+ const trackedConvertBuffer = track("convertBuffer", convertBuffer);
676
+ const trackedDecode = track("decode", decode);
677
+ const trackedEncode = track("encode", encode);
678
+ const trackedEncodings = track("encodings", encodings);
565
679
  class MockIconv {
566
680
  #fromEncoding;
567
681
  #toEncoding;
@@ -570,39 +684,56 @@ function mockIconv() {
570
684
  this.#toEncoding = toEncoding;
571
685
  }
572
686
  convert(input) {
573
- return handle("convert", [
574
- input,
575
- this.#fromEncoding,
576
- this.#toEncoding
577
- ]);
687
+ return trackedConvert.call(this, input, this.#fromEncoding, this.#toEncoding);
578
688
  }
579
689
  }
580
690
  root.iconv = {
581
- convert: (str, from, to) => handle("convert", [
582
- str,
583
- from,
584
- to
585
- ]),
586
- convertBuffer: (buf, from, to) => handle("convertBuffer", [
587
- buf,
588
- from,
589
- to
590
- ]),
591
- decode: (buf, encoding) => handle("decode", [buf, encoding]),
592
- encode: (str, encoding) => handle("encode", [str, encoding]),
593
- encodings: () => handle("encodings", []),
691
+ convert: trackedConvert,
692
+ convertBuffer: trackedConvertBuffer,
693
+ decode: trackedDecode,
694
+ encode: trackedEncode,
695
+ encodings: trackedEncodings,
594
696
  Iconv: MockIconv
595
697
  };
698
+ function clear() {
699
+ calls.length = 0;
700
+ convert.mockClear();
701
+ convertBuffer.mockClear();
702
+ decode.mockClear();
703
+ encode.mockClear();
704
+ encodings.mockClear();
705
+ }
596
706
  return withDispose({
707
+ /** The `convert` `vi.fn`. */
708
+ convert,
709
+ /** The `convertBuffer` `vi.fn`. */
710
+ convertBuffer,
711
+ /** The `decode` `vi.fn`. */
712
+ decode,
713
+ /** The `encode` `vi.fn`. */
714
+ encode,
715
+ /** The `encodings` `vi.fn`. */
716
+ encodings,
597
717
  setResolver(value) {
598
718
  resolver = value;
599
719
  },
600
720
  get calls() {
601
721
  return calls;
602
722
  },
723
+ clear,
603
724
  reset() {
604
725
  resolver = null;
605
726
  calls.length = 0;
727
+ convert.mockReset();
728
+ convert.mockImplementation(defaultConvert);
729
+ convertBuffer.mockReset();
730
+ convertBuffer.mockImplementation(defaultConvertBuffer);
731
+ decode.mockReset();
732
+ decode.mockImplementation(defaultDecode);
733
+ encode.mockReset();
734
+ encode.mockImplementation(defaultEncode);
735
+ encodings.mockReset();
736
+ encodings.mockImplementation(defaultEncodings);
606
737
  }
607
738
  }, () => {
608
739
  root.iconv = prev;
@@ -611,99 +742,131 @@ function mockIconv() {
611
742
 
612
743
  //#endregion
613
744
  //#region src/vitest/mocks/idp.ts
745
+ const IDP_METHODS = [
746
+ "users",
747
+ "user",
748
+ "userByName",
749
+ "createUser",
750
+ "updateUser",
751
+ "deleteUser",
752
+ "sendPasswordResetEmail",
753
+ "unenrollMfa"
754
+ ];
755
+ const IDP_USER_DEFAULT = {
756
+ id: "mock-id",
757
+ name: "mock-user",
758
+ disabled: false,
759
+ mfaEnrolled: false,
760
+ mfaFactorIds: []
761
+ };
614
762
  const IDP_DEFAULTS = {
615
763
  users: {
616
764
  users: [],
617
765
  nextPageToken: null,
618
766
  totalCount: 0
619
767
  },
620
- user: {
621
- id: "mock-id",
622
- name: "mock-user",
623
- disabled: false,
624
- mfaEnrolled: false,
625
- mfaFactorIds: []
626
- },
627
- userByName: {
628
- id: "mock-id",
629
- name: "mock-user",
630
- disabled: false,
631
- mfaEnrolled: false,
632
- mfaFactorIds: []
633
- },
634
- createUser: {
635
- id: "mock-id",
636
- name: "mock-user",
637
- disabled: false,
638
- mfaEnrolled: false,
639
- mfaFactorIds: []
640
- },
641
- updateUser: {
642
- id: "mock-id",
643
- name: "mock-user",
644
- disabled: false,
645
- mfaEnrolled: false,
646
- mfaFactorIds: []
647
- },
768
+ user: IDP_USER_DEFAULT,
769
+ userByName: IDP_USER_DEFAULT,
770
+ createUser: IDP_USER_DEFAULT,
771
+ updateUser: IDP_USER_DEFAULT,
648
772
  deleteUser: true,
649
773
  sendPasswordResetEmail: true,
650
774
  unenrollMfa: true
651
775
  };
652
776
  /**
653
777
  * Acquire a disposable mock for `tailor.idp`. Restored on dispose.
778
+ * @param options - Controls behavior for calls without a configured result
654
779
  * @returns Disposable IDP mock control object
655
780
  * @example
656
781
  * ```typescript
657
782
  * import { mockIdp } from "@tailor-platform/sdk/vitest";
658
783
  *
659
- * test("resolver-based", async () => {
784
+ * test("returns a user", async () => {
660
785
  * using idp = mockIdp();
661
- * idp.setResolver((method) =>
662
- * method === "user" ? { id: "u-1", name: "alice", disabled: false } : null,
663
- * );
786
+ * idp.namespace("my-idp").user.mockResolvedValue({
787
+ * id: "u-1",
788
+ * name: "alice",
789
+ * disabled: false,
790
+ * mfaEnrolled: false,
791
+ * mfaFactorIds: [],
792
+ * });
664
793
  * // …
665
794
  * });
666
795
  * ```
667
796
  */
668
- function mockIdp() {
797
+ function mockIdp(options = {}) {
669
798
  const root = tailorRoot();
670
799
  const prev = root.idp;
800
+ const { onUnhandled = "fallback" } = options;
671
801
  const queue = [];
672
- let resolver = () => null;
673
802
  const calls = [];
803
+ let resolver = () => null;
804
+ const namespaces = /* @__PURE__ */ new Map();
805
+ function compatibilityArgs(method, args) {
806
+ return method === "users" && args.length === 0 ? [void 0] : args;
807
+ }
674
808
  function handle(method, args, namespace) {
675
- calls.push({
676
- method,
677
- args,
678
- namespace
679
- });
680
809
  if (queue.length > 0) return queue.shift();
681
- const resolved = resolver(method, args, namespace);
810
+ const resolved = resolver(method, compatibilityArgs(method, args), namespace);
682
811
  if (resolved != null) return resolved;
683
- const fallback = IDP_DEFAULTS[method];
684
- return fallback === void 0 ? void 0 : structuredClone(fallback);
812
+ if (onUnhandled === "error") throw new Error(`No IdP mock configured for "${namespace}.${method}"`);
813
+ return structuredClone(IDP_DEFAULTS[method]);
685
814
  }
686
- const Client = vi.fn(function(config) {
687
- const namespace = config.namespace;
688
- this.users = async (options) => handle("users", [options], namespace);
689
- this.user = async (userId) => handle("user", [userId], namespace);
690
- this.userByName = async (name) => handle("userByName", [name], namespace);
691
- this.createUser = async (input) => handle("createUser", [input], namespace);
692
- this.updateUser = async (input) => handle("updateUser", [input], namespace);
693
- this.deleteUser = async (userId) => handle("deleteUser", [userId], namespace);
694
- this.sendPasswordResetEmail = async (input) => handle("sendPasswordResetEmail", [input], namespace);
695
- this.unenrollMfa = async (input) => handle("unenrollMfa", [input], namespace);
696
- });
815
+ function createNamespaceMocks(namespace) {
816
+ return {
817
+ users: vi.fn(async (...args) => handle("users", args, namespace)),
818
+ user: vi.fn(async (...args) => handle("user", args, namespace)),
819
+ userByName: vi.fn(async (...args) => handle("userByName", args, namespace)),
820
+ createUser: vi.fn(async (...args) => handle("createUser", args, namespace)),
821
+ updateUser: vi.fn(async (...args) => handle("updateUser", args, namespace)),
822
+ deleteUser: vi.fn(async (...args) => handle("deleteUser", args, namespace)),
823
+ sendPasswordResetEmail: vi.fn(async (...args) => handle("sendPasswordResetEmail", args, namespace)),
824
+ unenrollMfa: vi.fn(async (...args) => handle("unenrollMfa", args, namespace))
825
+ };
826
+ }
827
+ function namespace(name) {
828
+ const existing = namespaces.get(name);
829
+ if (existing) return existing;
830
+ const mocks = createNamespaceMocks(name);
831
+ namespaces.set(name, mocks);
832
+ return mocks;
833
+ }
834
+ function track(method, mock, namespaceName) {
835
+ return function(...args) {
836
+ calls.push({
837
+ method,
838
+ args: [...compatibilityArgs(method, args)],
839
+ namespace: namespaceName
840
+ });
841
+ return mock.apply(this, args);
842
+ };
843
+ }
844
+ const defaultClient = function(config) {
845
+ const mocks = namespace(config.namespace);
846
+ this.users = track("users", mocks.users, config.namespace);
847
+ this.user = track("user", mocks.user, config.namespace);
848
+ this.userByName = track("userByName", mocks.userByName, config.namespace);
849
+ this.createUser = track("createUser", mocks.createUser, config.namespace);
850
+ this.updateUser = track("updateUser", mocks.updateUser, config.namespace);
851
+ this.deleteUser = track("deleteUser", mocks.deleteUser, config.namespace);
852
+ this.sendPasswordResetEmail = track("sendPasswordResetEmail", mocks.sendPasswordResetEmail, config.namespace);
853
+ this.unenrollMfa = track("unenrollMfa", mocks.unenrollMfa, config.namespace);
854
+ };
855
+ const Client = vi.fn(defaultClient);
697
856
  root.idp = { Client };
857
+ function allMocks() {
858
+ return [...namespaces.values()].flatMap((mocks) => IDP_METHODS.map((method) => mocks[method]));
859
+ }
698
860
  return withDispose({
699
861
  /** The mock IDP `Client` constructor (`vi.fn`). */
700
862
  Client,
863
+ namespace,
701
864
  setResolver(value) {
702
865
  resolver = value;
703
866
  },
704
867
  /**
705
- * Enqueue a single result for the next IDP call (FIFO; falls back to
706
- * `setResolver` when exhausted).
868
+ * Enqueue a single result for the next IDP call.
869
+ * The queue is shared across all methods and namespaces.
707
870
  * @param result - Result to return from the next IDP call
708
871
  */
709
872
  enqueueResult(result) {
@@ -711,6 +874,7 @@ function mockIdp() {
711
874
  },
712
875
  /**
713
876
  * Enqueue results for multiple subsequent IDP calls.
877
+ * The queue is shared across all methods and namespaces.
714
878
  * @param results - Results to enqueue, one per upcoming call
715
879
  */
716
880
  enqueueResults(...results) {
@@ -719,10 +883,18 @@ function mockIdp() {
719
883
  get calls() {
720
884
  return calls;
721
885
  },
886
+ clear() {
887
+ calls.length = 0;
888
+ Client.mockClear();
889
+ for (const mock of allMocks()) mock.mockClear();
890
+ },
722
891
  reset() {
723
892
  queue.length = 0;
724
- resolver = () => null;
725
893
  calls.length = 0;
894
+ resolver = () => null;
895
+ Client.mockReset();
896
+ Client.mockImplementation(defaultClient);
897
+ for (const mock of allMocks()) mock.mockReset();
726
898
  }
727
899
  }, () => {
728
900
  root.idp = prev;
@@ -731,6 +903,15 @@ function mockIdp() {
731
903
 
732
904
  //#endregion
733
905
  //#region src/vitest/mocks/tailordb.ts
906
+ function testRegex(regex, value) {
907
+ const lastIndex = regex.lastIndex;
908
+ regex.lastIndex = 0;
909
+ try {
910
+ return regex.test(value);
911
+ } finally {
912
+ regex.lastIndex = lastIndex;
913
+ }
914
+ }
734
915
  var MockQueryResult = class {
735
916
  command;
736
917
  rowCount;
@@ -746,27 +927,62 @@ var MockQueryResult = class {
746
927
  * `tailordb.Client` whose `queryObject` is a shared `vi.fn()` (so query
747
928
  * responses can be staged before the client is constructed). Restored on
748
929
  * dispose.
930
+ * @param options - Query fallback behavior
749
931
  * @returns Disposable TailorDB mock control object
750
932
  * @example
751
933
  * ```typescript
752
934
  * import { mockTailordb } from "@tailor-platform/sdk/vitest";
753
935
  *
754
- * test("order-based", async () => {
936
+ * test("query-based", async () => {
755
937
  * using db = mockTailordb();
756
- * db.enqueueResults([], [{ age: 30 }], []); // BEGIN / SELECT / COMMIT
938
+ * db.onQuery({ sql: /FROM users/, params: ["u-1"] }).returnsRows([{ age: 30 }]);
757
939
  * // …
758
- * expect(db.queryObject).toHaveBeenCalledTimes(3);
940
+ * expect(db.queryObject).toHaveBeenCalled();
759
941
  * expect(db.Client).toHaveBeenCalledWith({ namespace: "tailordb" });
760
942
  * });
761
943
  * ```
762
944
  */
763
- function mockTailordb() {
945
+ function mockTailordb(options = {}) {
764
946
  const root = tailordbRoot();
765
947
  const prevClient = root.Client;
766
- const queryObject = vi.fn(async (_query, _params = []) => new MockQueryResult([]));
767
- const connect = vi.fn(async () => {});
948
+ const rules = [];
949
+ let queryResolver;
950
+ function matchesQuery(matcher, query, params) {
951
+ if (typeof matcher === "function") return matcher(query, params);
952
+ if (typeof matcher === "string") return query === matcher;
953
+ if (matcher instanceof RegExp) return testRegex(matcher, query);
954
+ let sqlMatches;
955
+ if (typeof matcher.sql === "string") sqlMatches = query === matcher.sql;
956
+ else sqlMatches = testRegex(matcher.sql, query);
957
+ if (!sqlMatches || matcher.params === void 0) return sqlMatches;
958
+ if (typeof matcher.params === "function") return matcher.params(params);
959
+ const expectedParams = matcher.params;
960
+ return params.length === expectedParams.length && params.every((value, index) => isEqual(value, expectedParams[index]));
961
+ }
962
+ function queryResponse(response) {
963
+ if (response.type === "error") throw response.error;
964
+ return new MockQueryResult(response.rows);
965
+ }
966
+ async function defaultQuery(query, params = []) {
967
+ for (let i = rules.length - 1; i >= 0; i -= 1) {
968
+ const rule = rules[i];
969
+ if (!rule || !matchesQuery(rule.matcher, query, params)) continue;
970
+ const once = rule.once.shift();
971
+ if (once) return queryResponse(once);
972
+ if (rule.fallback) return queryResponse(rule.fallback);
973
+ }
974
+ if (queryResolver) return new MockQueryResult(queryResolver(query, params) ?? []);
975
+ if (options.onUnhandled === "error") throw new Error(`No TailorDB query behavior matched: ${query}`);
976
+ return new MockQueryResult([]);
977
+ }
978
+ const queryObject = vi.fn(defaultQuery);
979
+ const defaultConnect = async () => {};
980
+ const connect = vi.fn(defaultConnect);
768
981
  const createdClients = [];
769
- const Client = vi.fn(function(config) {
982
+ function enqueueRowsList(rowsList) {
983
+ for (const rows of rowsList) queryObject.mockImplementationOnce(async () => new MockQueryResult(rows));
984
+ }
985
+ const defaultClient = function(config) {
770
986
  const record = {
771
987
  namespace: config?.namespace,
772
988
  ended: false
@@ -786,7 +1002,8 @@ function mockTailordb() {
786
1002
  queryObject
787
1003
  };
788
1004
  };
789
- });
1005
+ };
1006
+ const Client = vi.fn(defaultClient);
790
1007
  root.Client = Client;
791
1008
  return withDispose({
792
1009
  /** The mock `tailordb.Client` constructor (`vi.fn`). */
@@ -798,7 +1015,53 @@ function mockTailordb() {
798
1015
  * @param resolver - Function that returns rows for a given query and params
799
1016
  */
800
1017
  setQueryResolver(resolver) {
801
- queryObject.mockImplementation(async (query, params = []) => new MockQueryResult(resolver(query, params) ?? []));
1018
+ queryResolver = resolver;
1019
+ queryObject.mockImplementation(defaultQuery);
1020
+ },
1021
+ /**
1022
+ * Configure responses for queries matching SQL text, parameters, or a predicate.
1023
+ * More recently registered matchers take precedence.
1024
+ * Do not combine matchers with a direct `queryObject.mockImplementation()` override.
1025
+ * @param matcher - Query matcher
1026
+ * @returns Chainable query behavior
1027
+ */
1028
+ onQuery(matcher) {
1029
+ const rule = {
1030
+ matcher,
1031
+ once: []
1032
+ };
1033
+ rules.push(rule);
1034
+ const behavior = {
1035
+ returnsRows(rows) {
1036
+ rule.fallback = {
1037
+ type: "rows",
1038
+ rows
1039
+ };
1040
+ return behavior;
1041
+ },
1042
+ returnsRowsOnce(rows) {
1043
+ rule.once.push({
1044
+ type: "rows",
1045
+ rows
1046
+ });
1047
+ return behavior;
1048
+ },
1049
+ rejects(error) {
1050
+ rule.fallback = {
1051
+ type: "error",
1052
+ error
1053
+ };
1054
+ return behavior;
1055
+ },
1056
+ rejectsOnce(error) {
1057
+ rule.once.push({
1058
+ type: "error",
1059
+ error
1060
+ });
1061
+ return behavior;
1062
+ }
1063
+ };
1064
+ return behavior;
802
1065
  },
803
1066
  /**
804
1067
  * Enqueue rows for the next `queryObject` call (FIFO; takes priority over
@@ -806,14 +1069,21 @@ function mockTailordb() {
806
1069
  * @param rows - Row objects to return from the next `queryObject` call
807
1070
  */
808
1071
  enqueueResult(...rows) {
809
- queryObject.mockImplementationOnce(async () => new MockQueryResult(rows));
1072
+ enqueueRowsList([rows]);
810
1073
  },
811
1074
  /**
812
1075
  * Enqueue rows for multiple subsequent `queryObject` calls (FIFO).
813
1076
  * @param rowsList - Rows arrays, one per upcoming query
814
1077
  */
815
1078
  enqueueResults(...rowsList) {
816
- for (const rows of rowsList) queryObject.mockImplementationOnce(async () => new MockQueryResult(rows));
1079
+ enqueueRowsList(rowsList);
1080
+ },
1081
+ /**
1082
+ * Enqueue row arrays for subsequent queries whose exact order is under test.
1083
+ * @param rowsList - Rows arrays, one per upcoming query
1084
+ */
1085
+ enqueueRows(...rowsList) {
1086
+ enqueueRowsList(rowsList);
817
1087
  },
818
1088
  /**
819
1089
  * All queries executed via `queryObject`, in order, derived from the vi.fn
@@ -833,13 +1103,24 @@ function mockTailordb() {
833
1103
  get createdClients() {
834
1104
  return createdClients;
835
1105
  },
1106
+ /** Clear recorded calls while preserving configured query behavior. */
1107
+ clear() {
1108
+ queryObject.mockClear();
1109
+ connect.mockClear();
1110
+ Client.mockClear();
1111
+ createdClients.length = 0;
1112
+ },
836
1113
  /** Reset query responses and recorded calls (keeps the mock installed). */
837
1114
  reset() {
838
1115
  queryObject.mockReset();
839
- queryObject.mockImplementation(async () => new MockQueryResult([]));
840
- connect.mockClear();
841
- Client.mockClear();
1116
+ queryObject.mockImplementation(defaultQuery);
1117
+ connect.mockReset();
1118
+ connect.mockImplementation(defaultConnect);
1119
+ Client.mockReset();
1120
+ Client.mockImplementation(defaultClient);
842
1121
  createdClients.length = 0;
1122
+ rules.length = 0;
1123
+ queryResolver = void 0;
843
1124
  }
844
1125
  }, () => {
845
1126
  root.Client = prevClient;
@@ -848,25 +1129,60 @@ function mockTailordb() {
848
1129
 
849
1130
  //#endregion
850
1131
  //#region src/vitest/mocks/workflow.ts
1132
+ const originalProcedures = /* @__PURE__ */ new WeakMap();
1133
+ function replaceProcedure(target, key, current) {
1134
+ const original = originalProcedures.get(current) ?? current;
1135
+ const defaultImplementation = function(...args) {
1136
+ return Reflect.apply(original, this, args);
1137
+ };
1138
+ const mock = vi.fn(defaultImplementation);
1139
+ originalProcedures.set(mock, original);
1140
+ const record = target;
1141
+ record[key] = mock;
1142
+ return {
1143
+ mock,
1144
+ scoped: {
1145
+ mockClear: () => mock.mockClear(),
1146
+ mockReset: () => mock.mockReset(),
1147
+ restore: () => {
1148
+ if (record[key] === mock) record[key] = current;
1149
+ }
1150
+ }
1151
+ };
1152
+ }
1153
+ function replaceTrigger(definition) {
1154
+ return replaceProcedure(definition, "trigger", definition.trigger);
1155
+ }
851
1156
  /**
852
1157
  * Acquire a disposable mock for workflow operations (`tailor.workflow`).
853
1158
  * Restored on dispose.
1159
+ *
1160
+ * Canonical names (`startWorkflow`, `startJobFunction`, `resumeWorkflowExecution`)
1161
+ * and their frozen aliases (`triggerWorkflow`, `triggerJobFunction`, `resumeWorkflow`)
1162
+ * share the same underlying `vi.fn`, so calls through either name are recorded
1163
+ * once and handlers configured on either name apply to both.
854
1164
  * @returns Disposable workflow mock control object
855
1165
  * @example
856
1166
  * ```typescript
857
1167
  * import { mockWorkflow } from "@tailor-platform/sdk/vitest";
858
1168
  *
859
- * test("job handler", async () => {
1169
+ * test("job trigger", async () => {
860
1170
  * using wf = mockWorkflow();
861
- * wf.setJobHandler((name) => (name === "validate" ? { valid: true } : null));
862
- * await runWorkflowUnderTest(); // calls tailor.workflow.triggerJobFunction("validate", {})
863
- * expect(wf.triggerJobFunction).toHaveBeenCalledWith("validate", {});
1171
+ * const job = wf.job(validateOrder);
1172
+ * job.mockResolvedValue({ valid: true });
1173
+ * await runWorkflowUnderTest();
1174
+ * expect(job).toHaveBeenCalled();
864
1175
  * });
865
1176
  * ```
866
1177
  */
867
1178
  function mockWorkflow() {
868
1179
  const root = tailorRoot();
869
1180
  const prev = root.workflow;
1181
+ const prevEnv = readWorkflowTestEnv();
1182
+ const jobSpies = /* @__PURE__ */ new Map();
1183
+ const workflowSpies = /* @__PURE__ */ new Map();
1184
+ const waitPointMocks = /* @__PURE__ */ new Map();
1185
+ const scopedMocks = /* @__PURE__ */ new Set();
870
1186
  const defaultTriggerJob = (jobName, _args, _options) => {
871
1187
  throw new Error(`No workflow job mock for "${jobName}". Call mockWorkflow().setJobHandler(...) or enqueueResult(...), or use runWorkflowLocally() for local workflow execution.`);
872
1188
  };
@@ -879,13 +1195,19 @@ function mockWorkflow() {
879
1195
  const resumeWorkflow = vi.fn(defaultResumeWorkflow);
880
1196
  const wait = vi.fn((_key, _payload) => null);
881
1197
  const resolve = vi.fn(async (_executionId, _key, _callback) => {});
1198
+ const jobFunctionShim = (...call) => {
1199
+ const out = call.length >= 3 ? triggerJobFunction(call[0], platformSerialize(call[1]), call[2]) : triggerJobFunction(call[0], platformSerialize(call[1]));
1200
+ return out instanceof Promise ? out.then((v) => platformSerialize(v)) : platformSerialize(out);
1201
+ };
1202
+ const workflowShim = (...call) => call.length >= 3 ? triggerWorkflow(call[0], platformSerialize(call[1]), call[2]) : triggerWorkflow(call[0], platformSerialize(call[1]));
1203
+ const resumeShim = (executionId) => resumeWorkflow(executionId);
882
1204
  root.workflow = {
883
- triggerJobFunction: (...call) => {
884
- const out = call.length >= 3 ? triggerJobFunction(call[0], platformSerialize(call[1]), call[2]) : triggerJobFunction(call[0], platformSerialize(call[1]));
885
- return out instanceof Promise ? out.then((v) => platformSerialize(v)) : platformSerialize(out);
886
- },
887
- triggerWorkflow: (...call) => call.length >= 3 ? triggerWorkflow(call[0], platformSerialize(call[1]), call[2]) : triggerWorkflow(call[0], platformSerialize(call[1])),
888
- resumeWorkflow: (executionId) => resumeWorkflow(executionId),
1205
+ startJobFunction: jobFunctionShim,
1206
+ triggerJobFunction: jobFunctionShim,
1207
+ startWorkflow: workflowShim,
1208
+ triggerWorkflow: workflowShim,
1209
+ resumeWorkflowExecution: resumeShim,
1210
+ resumeWorkflow: resumeShim,
889
1211
  wait: (key, payload) => wait(key, platformSerialize(payload)),
890
1212
  resolve: (executionId, key, callback) => resolve(executionId, key, (payload) => {
891
1213
  const out = callback(payload);
@@ -893,17 +1215,90 @@ function mockWorkflow() {
893
1215
  })
894
1216
  };
895
1217
  return withDispose({
896
- /** The `triggerJobFunction` `vi.fn`. */
1218
+ /** The `startJobFunction` `vi.fn`. */
1219
+ startJobFunction: triggerJobFunction,
1220
+ /**
1221
+ * Frozen alias of `startJobFunction` (same `vi.fn` reference).
1222
+ * @deprecated Use `startJobFunction` instead.
1223
+ */
897
1224
  triggerJobFunction,
898
- /** The `triggerWorkflow` `vi.fn`. */
1225
+ /** The `startWorkflow` `vi.fn`. */
1226
+ startWorkflow: triggerWorkflow,
1227
+ /**
1228
+ * Frozen alias of `startWorkflow` (same `vi.fn` reference).
1229
+ * @deprecated Use `startWorkflow` instead.
1230
+ */
899
1231
  triggerWorkflow,
900
- /** The `resumeWorkflow` `vi.fn`. */
1232
+ /** The `resumeWorkflowExecution` `vi.fn`. */
1233
+ resumeWorkflowExecution: resumeWorkflow,
1234
+ /**
1235
+ * Frozen alias of `resumeWorkflowExecution` (same `vi.fn` reference).
1236
+ * @deprecated Use `resumeWorkflowExecution` instead.
1237
+ */
901
1238
  resumeWorkflow,
902
1239
  /** The `wait` `vi.fn`. */
903
1240
  wait,
904
1241
  /** The `resolve` `vi.fn`. */
905
1242
  resolve,
906
1243
  /**
1244
+ * Get a stable, typed mock for a workflow job's `trigger` method.
1245
+ * The real trigger behavior is used until an implementation or result is configured.
1246
+ * @param definition - Workflow job definition to mock
1247
+ * @returns Typed `trigger` mock for the definition
1248
+ */
1249
+ job(definition) {
1250
+ const existing = jobSpies.get(definition);
1251
+ if (existing) return existing;
1252
+ const { mock, scoped } = replaceTrigger(definition);
1253
+ scopedMocks.add(scoped);
1254
+ jobSpies.set(definition, mock);
1255
+ return mock;
1256
+ },
1257
+ /**
1258
+ * Get a stable, typed mock for a workflow definition's `trigger` method.
1259
+ * The real trigger behavior is used until an implementation or result is configured.
1260
+ * @param definition - Workflow definition to mock
1261
+ * @returns Typed `trigger` mock for the definition
1262
+ */
1263
+ workflow(definition) {
1264
+ const existing = workflowSpies.get(definition);
1265
+ if (existing) return existing;
1266
+ const { mock, scoped } = replaceTrigger(definition);
1267
+ workflowSpies.set(definition, mock);
1268
+ scopedMocks.add(scoped);
1269
+ return mock;
1270
+ },
1271
+ /**
1272
+ * Get stable, typed mocks for a wait point's `wait` and `resolve` methods.
1273
+ * @param definition - Wait point definition to mock
1274
+ * @returns Typed wait point mock control object
1275
+ */
1276
+ waitPoint(definition) {
1277
+ const existing = waitPointMocks.get(definition);
1278
+ if (existing) return existing;
1279
+ const waitReplacement = replaceProcedure(definition, "wait", definition.wait);
1280
+ const resolveReplacement = replaceProcedure(definition, "resolve", definition.resolve);
1281
+ const waitSpy = waitReplacement.mock;
1282
+ const resolveSpy = resolveReplacement.mock;
1283
+ scopedMocks.add(waitReplacement.scoped);
1284
+ scopedMocks.add(resolveReplacement.scoped);
1285
+ const waitPointMock = {
1286
+ wait: waitSpy,
1287
+ resolve: resolveSpy,
1288
+ /**
1289
+ * Invoke the next and subsequent resolve callbacks with a wait payload.
1290
+ * @param payload - Payload originally supplied to the wait point
1291
+ */
1292
+ setResolvePayload(payload) {
1293
+ resolveSpy.mockImplementation(async (_executionId, callback) => {
1294
+ platformSerialize(await callback(platformSerialize(payload)));
1295
+ });
1296
+ }
1297
+ };
1298
+ waitPointMocks.set(definition, waitPointMock);
1299
+ return waitPointMock;
1300
+ },
1301
+ /**
907
1302
  * Set a fallback job handler. Called when the enqueue queue is empty.
908
1303
  * @param handler - Function returning a result for a job name, args, and options
909
1304
  */
@@ -998,6 +1393,15 @@ function mockWorkflow() {
998
1393
  key
999
1394
  }));
1000
1395
  },
1396
+ /** Clear recorded calls while preserving configured responses. */
1397
+ clear() {
1398
+ triggerJobFunction.mockClear();
1399
+ triggerWorkflow.mockClear();
1400
+ resumeWorkflow.mockClear();
1401
+ wait.mockClear();
1402
+ resolve.mockClear();
1403
+ for (const mock of scopedMocks) mock.mockClear();
1404
+ },
1001
1405
  /** Reset all workflow responses and recorded calls (keeps the mock installed). */
1002
1406
  reset() {
1003
1407
  triggerJobFunction.mockReset();
@@ -1010,11 +1414,14 @@ function mockWorkflow() {
1010
1414
  wait.mockImplementation(() => null);
1011
1415
  resolve.mockReset();
1012
1416
  resolve.mockImplementation(async () => {});
1417
+ for (const mock of scopedMocks) mock.mockReset();
1013
1418
  clearWorkflowTestEnv();
1014
1419
  }
1015
1420
  }, () => {
1421
+ for (const mock of scopedMocks) mock.restore();
1016
1422
  root.workflow = prev;
1017
- clearWorkflowTestEnv();
1423
+ if (prevEnv !== void 0) writeWorkflowTestEnv(prevEnv);
1424
+ else clearWorkflowTestEnv();
1018
1425
  });
1019
1426
  }
1020
1427
 
@@ -1124,16 +1531,27 @@ function assertSameTrigger(record, jobName, args) {
1124
1531
  throw new Error(`Workflow job trigger sequence changed while replaying. Expected ${record.jobName}(${JSON.stringify(record.args)}), but got ${jobName}(${JSON.stringify(args)}).`);
1125
1532
  }
1126
1533
  function createLocalWorkflowRuntime(previous, triggerJobFunction) {
1534
+ const startWorkflow = async (name, args, options) => {
1535
+ if (previous) return await previous.startWorkflow(name, args, options);
1536
+ platformSerialize(args);
1537
+ return TRIGGER_DEFAULT;
1538
+ };
1539
+ const resumeWorkflowExecution = async (executionId) => {
1540
+ if (previous) return await previous.resumeWorkflowExecution(executionId);
1541
+ return executionId;
1542
+ };
1127
1543
  return {
1128
1544
  triggerJobFunction,
1545
+ startJobFunction: triggerJobFunction,
1546
+ startWorkflow,
1129
1547
  triggerWorkflow: async (name, args, options) => {
1130
1548
  if (previous) return await previous.triggerWorkflow(name, args, options);
1131
- platformSerialize(args);
1132
- return TRIGGER_DEFAULT;
1549
+ return await startWorkflow(name, args);
1133
1550
  },
1551
+ resumeWorkflowExecution,
1134
1552
  resumeWorkflow: async (executionId) => {
1135
1553
  if (previous) return await previous.resumeWorkflow(executionId);
1136
- return executionId;
1554
+ return await resumeWorkflowExecution(executionId);
1137
1555
  },
1138
1556
  wait: (key, payload) => {
1139
1557
  if (previous) return previous.wait(key, payload);