@dreamboard-games/cli 0.1.30-alpha.3 → 0.1.30-alpha.31

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 (173) hide show
  1. package/README.md +27 -108
  2. package/dist/agent-verifier/agent-workspace-verifier.mjs +1988 -57
  3. package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -1
  4. package/dist/agent-verifier/{chunk-XQXDOBYB.mjs → chunk-4I2WWAPK.mjs} +27 -10
  5. package/dist/agent-verifier/chunk-4I2WWAPK.mjs.map +1 -0
  6. package/dist/agent-verifier/{chunk-O4YCPU7C.mjs → chunk-BWBN2TDJ.mjs} +539 -641
  7. package/dist/agent-verifier/chunk-BWBN2TDJ.mjs.map +1 -0
  8. package/dist/agent-verifier/{chunk-TAEQKBJB.mjs → chunk-GWRZRWCF.mjs} +1 -1
  9. package/dist/agent-verifier/chunk-GWRZRWCF.mjs.map +1 -0
  10. package/dist/agent-verifier/chunk-H6XDQJ3N.mjs +11 -0
  11. package/dist/agent-verifier/chunk-HUBV22JQ.mjs +89 -0
  12. package/dist/agent-verifier/chunk-HUBV22JQ.mjs.map +1 -0
  13. package/dist/agent-verifier/{chunk-VS573ERH.mjs → chunk-JZTH3EMV.mjs} +2 -2
  14. package/dist/agent-verifier/{chunk-XGWCY624.mjs → chunk-KDAQ4CZY.mjs} +34 -27
  15. package/dist/agent-verifier/chunk-KDAQ4CZY.mjs.map +1 -0
  16. package/dist/agent-verifier/{chunk-IAYRNVUC.mjs → chunk-LMW66VBH.mjs} +2 -13
  17. package/dist/agent-verifier/{chunk-IAYRNVUC.mjs.map → chunk-LMW66VBH.mjs.map} +1 -1
  18. package/dist/agent-verifier/{chunk-776W3UGV.mjs → chunk-M6YNQZCC.mjs} +4 -13
  19. package/dist/agent-verifier/chunk-M6YNQZCC.mjs.map +1 -0
  20. package/dist/agent-verifier/{chunk-H76MT5UR.mjs → chunk-M7UVBANQ.mjs} +2 -1
  21. package/dist/agent-verifier/chunk-M7UVBANQ.mjs.map +1 -0
  22. package/dist/agent-verifier/{chunk-SH5JKYOB.mjs → chunk-MIRGCMUC.mjs} +112 -26
  23. package/dist/agent-verifier/chunk-MIRGCMUC.mjs.map +1 -0
  24. package/dist/agent-verifier/{chunk-NAK77WXW.mjs → chunk-MYMVXTZT.mjs} +4 -5
  25. package/dist/agent-verifier/chunk-MYMVXTZT.mjs.map +1 -0
  26. package/dist/agent-verifier/{chunk-7WWGFAAU.mjs → chunk-NBRUEJUK.mjs} +215 -223
  27. package/dist/agent-verifier/chunk-NBRUEJUK.mjs.map +1 -0
  28. package/dist/agent-verifier/chunk-OJFZVGEL.mjs +492 -0
  29. package/dist/agent-verifier/chunk-OJFZVGEL.mjs.map +1 -0
  30. package/dist/agent-verifier/{chunk-LUZ7KE6H.mjs → chunk-QD4SQNUP.mjs} +4 -8
  31. package/dist/agent-verifier/{chunk-LUZ7KE6H.mjs.map → chunk-QD4SQNUP.mjs.map} +1 -1
  32. package/dist/agent-verifier/chunk-TTB7AIHZ.mjs +214 -0
  33. package/dist/agent-verifier/chunk-TTB7AIHZ.mjs.map +1 -0
  34. package/dist/agent-verifier/{chunk-F2DIOJJZ.mjs → chunk-XCQQIPCO.mjs} +5 -46
  35. package/dist/agent-verifier/chunk-XCQQIPCO.mjs.map +1 -0
  36. package/dist/agent-verifier/{global-config-Y2NTSK4R.mjs → global-config-2NUESNEQ.mjs} +6 -6
  37. package/dist/agent-verifier/{keychain-backend-SPQWGKZN.mjs → keychain-backend-FF4I6ODB.mjs} +12 -7
  38. package/dist/agent-verifier/keychain-backend-FF4I6ODB.mjs.map +1 -0
  39. package/dist/agent-verifier/{local-files-JFOQQZDL.mjs → local-files-OF4QFISU.mjs} +10 -10
  40. package/dist/agent-verifier/{chunk-UIOLGH4A.mjs → local-typecheck-DHVLM37Z.mjs} +4 -4
  41. package/dist/agent-verifier/local-typecheck-DHVLM37Z.mjs.map +1 -0
  42. package/dist/agent-verifier/{materialize-workspace-ZAVGQQSF.mjs → materialize-workspace-MAGKDMK5.mjs} +23 -22
  43. package/dist/agent-verifier/materialize-workspace-MAGKDMK5.mjs.map +1 -0
  44. package/dist/agent-verifier/{project-state-K576C2TE.mjs → project-state-XKUSCFSV.mjs} +2 -2
  45. package/dist/agent-verifier/{prompt-MJRJMOGQ.mjs → prompt-VKHMCQT6.mjs} +2 -2
  46. package/dist/agent-verifier/{chunk-A64ZZUZV.mjs → reducer-bundle-preflight-GLUJKTWU.mjs} +76 -25
  47. package/dist/agent-verifier/reducer-bundle-preflight-GLUJKTWU.mjs.map +1 -0
  48. package/dist/agent-verifier/{chunk-JGT4P4UD.mjs → reducer-contract-preflight-WVQQPW5F.mjs} +7 -6
  49. package/dist/agent-verifier/reducer-contract-preflight-WVQQPW5F.mjs.map +1 -0
  50. package/dist/agent-verifier/{chunk-E7SSWJXJ.mjs → reducer-native-test-harness-UFMSNNDY.mjs} +463 -686
  51. package/dist/agent-verifier/reducer-native-test-harness-UFMSNNDY.mjs.map +1 -0
  52. package/dist/agent-verifier/static-scaffold-CLRRWXON.mjs +24 -0
  53. package/dist/agent-verifier/workspace-codegen-SPPVHURX.mjs +10 -0
  54. package/dist/agent-verifier/{workspace-dependencies-NOOQBK6I.mjs → workspace-dependencies-5HEEKZFP.mjs} +6 -4
  55. package/dist/authoring-compatibility-internal.js +12 -0
  56. package/dist/chunk-2H7UOFLK.js +11 -0
  57. package/dist/chunk-6NYVJYN4.js +313 -0
  58. package/dist/chunk-6NYVJYN4.js.map +1 -0
  59. package/dist/chunk-EQNBQVIW.js +204 -0
  60. package/dist/chunk-EQNBQVIW.js.map +1 -0
  61. package/dist/chunk-X244CUU4.js +3815 -0
  62. package/dist/chunk-X244CUU4.js.map +1 -0
  63. package/dist/{chunk-TAQKH67O.js → chunk-YNJVKC2T.js} +2587 -7278
  64. package/dist/chunk-YNJVKC2T.js.map +1 -0
  65. package/dist/{global-config-S4ZIPECE.js → global-config-RBMW7IVA.js} +4 -3
  66. package/dist/index.js +3099 -6187
  67. package/dist/index.js.map +1 -1
  68. package/dist/internal.js +36 -10
  69. package/dist/internal.js.map +1 -1
  70. package/dist/{keychain-backend-HDF4TZDL.js → keychain-backend-FSNTNTZE.js} +12 -7
  71. package/dist/keychain-backend-FSNTNTZE.js.map +1 -0
  72. package/dist/{prompt-NDV3AE5L.js → prompt-GMZABCJC.js} +2 -2
  73. package/package.json +9 -19
  74. package/release/authoring-release-set.json +38 -0
  75. package/skills/dreamboard/SKILL.md +30 -28
  76. package/skills/dreamboard/references/building-your-first-game.md +15 -15
  77. package/skills/dreamboard/references/cli.md +46 -47
  78. package/skills/dreamboard/references/manifest-authoring.md +11 -3
  79. package/skills/dreamboard/references/quickstart.md +16 -13
  80. package/skills/dreamboard/references/testing.md +6 -13
  81. package/dist/agent-verifier/chunk-3UKQVWLV.mjs +0 -1744
  82. package/dist/agent-verifier/chunk-3UKQVWLV.mjs.map +0 -1
  83. package/dist/agent-verifier/chunk-776W3UGV.mjs.map +0 -1
  84. package/dist/agent-verifier/chunk-7WWGFAAU.mjs.map +0 -1
  85. package/dist/agent-verifier/chunk-A64ZZUZV.mjs.map +0 -1
  86. package/dist/agent-verifier/chunk-E7SSWJXJ.mjs.map +0 -1
  87. package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +0 -1
  88. package/dist/agent-verifier/chunk-G42BGGG2.mjs +0 -70
  89. package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +0 -1
  90. package/dist/agent-verifier/chunk-H76MT5UR.mjs.map +0 -1
  91. package/dist/agent-verifier/chunk-HGMUAL33.mjs +0 -39
  92. package/dist/agent-verifier/chunk-HGMUAL33.mjs.map +0 -1
  93. package/dist/agent-verifier/chunk-JGT4P4UD.mjs.map +0 -1
  94. package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +0 -1
  95. package/dist/agent-verifier/chunk-O4YCPU7C.mjs.map +0 -1
  96. package/dist/agent-verifier/chunk-S34FRJHS.mjs +0 -222
  97. package/dist/agent-verifier/chunk-S34FRJHS.mjs.map +0 -1
  98. package/dist/agent-verifier/chunk-SH5JKYOB.mjs.map +0 -1
  99. package/dist/agent-verifier/chunk-SKI2ESE5.mjs +0 -44
  100. package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +0 -1
  101. package/dist/agent-verifier/chunk-UIOLGH4A.mjs.map +0 -1
  102. package/dist/agent-verifier/chunk-UIZNWRM6.mjs +0 -2432
  103. package/dist/agent-verifier/chunk-UIZNWRM6.mjs.map +0 -1
  104. package/dist/agent-verifier/chunk-W3N3QJ4V.mjs +0 -624
  105. package/dist/agent-verifier/chunk-W3N3QJ4V.mjs.map +0 -1
  106. package/dist/agent-verifier/chunk-XGWCY624.mjs.map +0 -1
  107. package/dist/agent-verifier/chunk-XQXDOBYB.mjs.map +0 -1
  108. package/dist/agent-verifier/compile-TEQVA46V.mjs +0 -312
  109. package/dist/agent-verifier/compile-TEQVA46V.mjs.map +0 -1
  110. package/dist/agent-verifier/keychain-backend-SPQWGKZN.mjs.map +0 -1
  111. package/dist/agent-verifier/local-typecheck-XVGWI75X.mjs +0 -10
  112. package/dist/agent-verifier/materialize-workspace-ZAVGQQSF.mjs.map +0 -1
  113. package/dist/agent-verifier/reducer-bundle-preflight-LXNJUBKL.mjs +0 -20
  114. package/dist/agent-verifier/reducer-contract-preflight-TUMQ43JV.mjs +0 -11
  115. package/dist/agent-verifier/reducer-native-test-harness-CHX5MBL5.mjs +0 -50
  116. package/dist/agent-verifier/static-scaffold-R7SVDRQI.mjs +0 -27
  117. package/dist/agent-verifier/sync-THAI546U.mjs +0 -588
  118. package/dist/agent-verifier/sync-THAI546U.mjs.map +0 -1
  119. package/dist/agent-verifier/test-AFAQFKOB.mjs +0 -353
  120. package/dist/agent-verifier/test-AFAQFKOB.mjs.map +0 -1
  121. package/dist/agent-verifier/workspace-codegen-2ZMQRIKJ.mjs +0 -10
  122. package/dist/agent-verifier/workspace-dependencies-NOOQBK6I.mjs.map +0 -1
  123. package/dist/chunk-N7XPNNUI.js +0 -432
  124. package/dist/chunk-N7XPNNUI.js.map +0 -1
  125. package/dist/chunk-SEGVTWSK.js +0 -44
  126. package/dist/chunk-SEGVTWSK.js.map +0 -1
  127. package/dist/chunk-TAQKH67O.js.map +0 -1
  128. package/dist/dev-host/components/drawer.tsx +0 -132
  129. package/dist/dev-host/components/input.tsx +0 -21
  130. package/dist/dev-host/dev-api-proxy-plugin.ts +0 -328
  131. package/dist/dev-host/dev-author-dom-warnings.ts +0 -100
  132. package/dist/dev-host/dev-diagnostics.ts +0 -62
  133. package/dist/dev-host/dev-fallback-stylesheet.ts +0 -53
  134. package/dist/dev-host/dev-hmr-guard-plugin.ts +0 -47
  135. package/dist/dev-host/dev-host-controller.ts +0 -674
  136. package/dist/dev-host/dev-host-player-query.ts +0 -17
  137. package/dist/dev-host/dev-host-session-transport.ts +0 -52
  138. package/dist/dev-host/dev-host-storage.ts +0 -56
  139. package/dist/dev-host/dev-log-relay-plugin.ts +0 -510
  140. package/dist/dev-host/dev-runtime-config.ts +0 -14
  141. package/dist/dev-host/dev-runtime-platform.ts +0 -335
  142. package/dist/dev-host/dev-virtual-modules-plugin.ts +0 -64
  143. package/dist/dev-host/host-main.css +0 -224
  144. package/dist/dev-host/host-main.tsx +0 -948
  145. package/dist/dev-host/index.html +0 -56
  146. package/dist/dev-host/lib/utils.ts +0 -6
  147. package/dist/dev-host/plugin-main.ts +0 -61
  148. package/dist/dev-host/plugin.html +0 -24
  149. package/dist/dev-host/shared-styles.css +0 -144
  150. package/dist/dev-host/start-dev-server.ts +0 -140
  151. package/dist/dev-host/virtual-modules.d.ts +0 -27
  152. package/dist/global-config-S4ZIPECE.js.map +0 -1
  153. package/dist/keychain-backend-HDF4TZDL.js.map +0 -1
  154. package/skills/dreamboard/scripts/events-extract.mjs +0 -218
  155. /package/dist/agent-verifier/{chunk-SKI2ESE5.mjs.map → chunk-H6XDQJ3N.mjs.map} +0 -0
  156. /package/dist/agent-verifier/{chunk-VS573ERH.mjs.map → chunk-JZTH3EMV.mjs.map} +0 -0
  157. /package/dist/agent-verifier/{global-config-Y2NTSK4R.mjs.map → global-config-2NUESNEQ.mjs.map} +0 -0
  158. /package/dist/agent-verifier/{local-files-JFOQQZDL.mjs.map → local-files-OF4QFISU.mjs.map} +0 -0
  159. /package/dist/agent-verifier/{local-typecheck-XVGWI75X.mjs.map → project-state-XKUSCFSV.mjs.map} +0 -0
  160. /package/dist/agent-verifier/{prompt-MJRJMOGQ.mjs.map → prompt-VKHMCQT6.mjs.map} +0 -0
  161. /package/dist/agent-verifier/{project-state-K576C2TE.mjs.map → static-scaffold-CLRRWXON.mjs.map} +0 -0
  162. /package/dist/agent-verifier/{reducer-bundle-preflight-LXNJUBKL.mjs.map → workspace-codegen-SPPVHURX.mjs.map} +0 -0
  163. /package/dist/agent-verifier/{reducer-contract-preflight-TUMQ43JV.mjs.map → workspace-dependencies-5HEEKZFP.mjs.map} +0 -0
  164. /package/dist/{agent-verifier/reducer-native-test-harness-CHX5MBL5.mjs.map → authoring-compatibility-internal.js.map} +0 -0
  165. /package/dist/{agent-verifier/static-scaffold-R7SVDRQI.mjs.map → chunk-2H7UOFLK.js.map} +0 -0
  166. /package/dist/{agent-verifier/workspace-codegen-2ZMQRIKJ.mjs.map → global-config-RBMW7IVA.js.map} +0 -0
  167. /package/dist/{prompt-NDV3AE5L.js.map → prompt-GMZABCJC.js.map} +0 -0
  168. /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.framework.json +0 -0
  169. /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.json +0 -0
  170. /package/{dist/scaffold → scaffold}/assets/static/ui/index.tsx +0 -0
  171. /package/{dist/scaffold → scaffold}/assets/static/ui/style.css +0 -0
  172. /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.framework.json +0 -0
  173. /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.json +0 -0
@@ -2,44 +2,52 @@
2
2
  import {
3
3
  bundleTypeScriptModuleText,
4
4
  importTypeScriptModule
5
- } from "./chunk-LUZ7KE6H.mjs";
6
- import {
7
- STALE_CONTRACT_ARTIFACT_CODE,
8
- isStaleContractArtifactError,
9
- toDreamboardApiError
10
- } from "./chunk-S34FRJHS.mjs";
5
+ } from "./chunk-QD4SQNUP.mjs";
11
6
  import {
12
7
  loadManifest
13
- } from "./chunk-XGWCY624.mjs";
8
+ } from "./chunk-KDAQ4CZY.mjs";
9
+ import "./chunk-GWRZRWCF.mjs";
14
10
  import {
15
11
  REDUCER_TESTING_TYPES_WRAPPER_CONTENT,
16
12
  buildReducerTestingContractContent
17
- } from "./chunk-F2DIOJJZ.mjs";
13
+ } from "./chunk-XCQQIPCO.mjs";
14
+ import {
15
+ ensureDir,
16
+ exists
17
+ } from "./chunk-LMW66VBH.mjs";
18
18
  import {
19
+ CLIENT_PROBLEM_TYPES,
20
+ SERVER_PROBLEM_TYPES,
19
21
  createGameplayCapability,
20
22
  createProjectSession,
21
23
  createProjectSessionFromReducerSnapshot,
22
24
  getSessionSnapshot,
23
25
  hashContent,
24
- startGame
25
- } from "./chunk-O4YCPU7C.mjs";
26
+ startGame,
27
+ zProblemDetails
28
+ } from "./chunk-BWBN2TDJ.mjs";
26
29
  import {
27
30
  external_exports
28
- } from "./chunk-VS573ERH.mjs";
31
+ } from "./chunk-JZTH3EMV.mjs";
32
+ import "./chunk-TTB7AIHZ.mjs";
33
+ import "./chunk-MYMVXTZT.mjs";
29
34
  import {
30
- ensureDir,
31
- exists,
32
- readTextFileIfExists,
33
- writeJsonFile,
34
- writeTextFile
35
- } from "./chunk-IAYRNVUC.mjs";
35
+ readWorkspaceTextFileIfExists,
36
+ resolveWorkspacePath,
37
+ workspacePathExists,
38
+ writeWorkspaceJsonFile,
39
+ writeWorkspaceTextFile
40
+ } from "./chunk-OJFZVGEL.mjs";
36
41
  import {
37
42
  PROJECT_DIR_NAME
38
- } from "./chunk-H76MT5UR.mjs";
43
+ } from "./chunk-M7UVBANQ.mjs";
44
+ import "./chunk-H6XDQJ3N.mjs";
39
45
 
40
46
  // src/services/testing/reducer-native-test-harness.ts
41
- import path2 from "path";
47
+ import path from "path";
42
48
  import { randomUUID as randomUUID2 } from "crypto";
49
+ import { createRequire } from "module";
50
+ import { pathToFileURL } from "url";
43
51
  import {
44
52
  existsSync,
45
53
  mkdirSync,
@@ -49,14 +57,6 @@ import {
49
57
  } from "fs";
50
58
  import { readdir } from "fs/promises";
51
59
  import { isDeepStrictEqual } from "util";
52
- import {
53
- contractFingerprint,
54
- createReducerBundle
55
- } from "@dreamboard-games/sdk/reducer";
56
- import {
57
- ReducerWireZod as ReducerContractZod
58
- } from "@dreamboard-games/sdk/reducer-contract";
59
- import { materializeManifestTable } from "@dreamboard-games/sdk/codegen";
60
60
 
61
61
  // src/services/gameplay-authority-submit.ts
62
62
  import { randomUUID } from "crypto";
@@ -636,36 +636,6 @@ async function submitGameplayAuthorityAction(options) {
636
636
  }
637
637
  }
638
638
 
639
- // src/ui/playwright-runner.ts
640
- import os from "os";
641
- import path from "path";
642
- function configurePlaywrightBrowsersPath() {
643
- if (process.env.PLAYWRIGHT_BROWSERS_PATH) {
644
- return;
645
- }
646
- const runtimeHome = process.env.HOME;
647
- let realHome;
648
- try {
649
- realHome = os.userInfo().homedir;
650
- } catch {
651
- return;
652
- }
653
- if (!runtimeHome || path.resolve(runtimeHome) === path.resolve(realHome)) {
654
- return;
655
- }
656
- const browserCachePath = process.platform === "darwin" ? path.join(realHome, "Library", "Caches", "ms-playwright") : process.platform === "win32" ? path.join(realHome, "AppData", "Local", "ms-playwright") : path.join(realHome, ".cache", "ms-playwright");
657
- process.env.PLAYWRIGHT_BROWSERS_PATH = browserCachePath;
658
- }
659
- async function buildBrowserAuthInitScript(config) {
660
- if (!config.authToken) return null;
661
- return `(function(){localStorage.setItem('dreamboard_auth_token',${JSON.stringify(config.authToken)});})();`;
662
- }
663
- async function waitForGameReady(page, timeoutMs = 6e4) {
664
- await page.waitForSelector('iframe[title="Game UI Plugin"]', {
665
- timeout: timeoutMs
666
- });
667
- }
668
-
669
639
  // src/services/workflows/resolve-setup-profile.ts
670
640
  function resolveSetupProfileSelection(options) {
671
641
  const setupProfiles = options.manifest.setupProfiles ?? [];
@@ -707,6 +677,120 @@ function resolveSetupProfileSelection(options) {
707
677
  };
708
678
  }
709
679
 
680
+ // src/utils/problem-types.ts
681
+ var CLI_PROBLEM_TYPES = {
682
+ ...SERVER_PROBLEM_TYPES,
683
+ ...CLIENT_PROBLEM_TYPES
684
+ };
685
+
686
+ // src/utils/errors.ts
687
+ var STALE_CONTRACT_ARTIFACT_CODE = "STALE_CONTRACT_ARTIFACT";
688
+ function isProblemViolationArray(value) {
689
+ return Array.isArray(value) && value.every(
690
+ (entry) => typeof entry === "object" && entry !== null && typeof entry.message === "string"
691
+ );
692
+ }
693
+ function isProblemDetails(value) {
694
+ return zProblemDetails.safeParse(value).success;
695
+ }
696
+ function coerceViolations(value) {
697
+ if (isProblemViolationArray(value)) {
698
+ return value;
699
+ }
700
+ if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
701
+ return value.map((message) => ({ message }));
702
+ }
703
+ return void 0;
704
+ }
705
+ function getRequestId(response) {
706
+ return response?.headers?.get?.("X-Correlation-ID") ?? response?.headers?.get?.("x-correlation-id") ?? void 0;
707
+ }
708
+ function toApiProblem(error, response, fallback) {
709
+ if (isProblemDetails(error)) {
710
+ return {
711
+ ...error,
712
+ status: error.status || response?.status || 0,
713
+ requestId: error.requestId ?? getRequestId(response)
714
+ };
715
+ }
716
+ if (error instanceof Error) {
717
+ return {
718
+ type: CLI_PROBLEM_TYPES.TRANSPORT_ERROR,
719
+ title: response?.statusText || "API error",
720
+ status: response?.status ?? 0,
721
+ detail: error.message || fallback,
722
+ requestId: getRequestId(response)
723
+ };
724
+ }
725
+ if (error && typeof error === "object") {
726
+ const obj = error;
727
+ const detail2 = typeof obj.detail === "string" ? obj.detail : typeof obj.message === "string" ? obj.message : void 0;
728
+ const title = typeof obj.title === "string" ? obj.title : response?.statusText || "API error";
729
+ const violations = coerceViolations(obj.violations) ?? coerceViolations(obj.errors);
730
+ if (detail2) {
731
+ return {
732
+ type: typeof obj.type === "string" ? obj.type : CLI_PROBLEM_TYPES.UNKNOWN_API_ERROR,
733
+ title,
734
+ status: typeof obj.status === "number" ? obj.status : response?.status ?? 0,
735
+ detail: detail2,
736
+ requestId: typeof obj.requestId === "string" ? obj.requestId : getRequestId(response),
737
+ retryable: typeof obj.retryable === "boolean" ? obj.retryable : void 0,
738
+ context: typeof obj.context === "object" && obj.context !== null ? obj.context : void 0,
739
+ violations,
740
+ timestamp: typeof obj.timestamp === "string" ? obj.timestamp : void 0,
741
+ instance: typeof obj.instance === "string" ? obj.instance : void 0
742
+ };
743
+ }
744
+ }
745
+ const detail = typeof error === "string" ? error.trim() || fallback : fallback;
746
+ return {
747
+ type: CLI_PROBLEM_TYPES.UNKNOWN_API_ERROR,
748
+ title: response?.statusText || "API error",
749
+ status: response?.status ?? 0,
750
+ detail,
751
+ requestId: getRequestId(response)
752
+ };
753
+ }
754
+ function formatProblem(problem) {
755
+ const base = problem.detail || problem.title;
756
+ const violations = problem.violations && problem.violations.length > 0 ? ` (${problem.violations.map((entry) => entry.message).join("; ")})` : "";
757
+ const statusSuffix = problem.status && problem.status > 0 ? ` (HTTP ${problem.status})` : "";
758
+ return `${base}${violations}${statusSuffix}`;
759
+ }
760
+ var DreamboardApiError = class extends Error {
761
+ problem;
762
+ status;
763
+ requestId;
764
+ retryable;
765
+ constructor(problem, cause) {
766
+ super(formatProblem(problem), { cause });
767
+ this.name = "DreamboardApiError";
768
+ this.problem = problem;
769
+ this.status = problem.status;
770
+ this.requestId = problem.requestId;
771
+ this.retryable = problem.retryable;
772
+ }
773
+ };
774
+ function toDreamboardApiError(error, response, fallback) {
775
+ return new DreamboardApiError(toApiProblem(error, response, fallback), error);
776
+ }
777
+ function getObjectStringProperty(value, property) {
778
+ return value && typeof value === "object" && typeof value[property] === "string" ? value[property] : void 0;
779
+ }
780
+ function isStaleContractArtifactMessage(message) {
781
+ return message.includes(STALE_CONTRACT_ARTIFACT_CODE) || message.includes("StaleContractArtifactError") || message.toLowerCase().includes("stale contract artifact");
782
+ }
783
+ function isStaleContractArtifactError(error) {
784
+ if (getObjectStringProperty(error, "code") === STALE_CONTRACT_ARTIFACT_CODE) {
785
+ return true;
786
+ }
787
+ if (getObjectStringProperty(error, "name") === "StaleContractArtifactError") {
788
+ return true;
789
+ }
790
+ const message = getObjectStringProperty(error, "message");
791
+ return message ? isStaleContractArtifactMessage(message) : false;
792
+ }
793
+
710
794
  // src/utils/session-game-source.ts
711
795
  function projectIdFromSessionGameSource(source) {
712
796
  if (source.kind === "USER_COMPILED") {
@@ -719,7 +803,6 @@ function projectIdFromSessionGameSource(source) {
719
803
  globalThis.__DREAMBOARD_AUTHORING_WARNINGS__ = true;
720
804
  var GENERATED_TESTING_TYPES_PREFIX = "// Generated by dreamboard";
721
805
  var TESTING_TYPES_STUB = "export function defineScenario(scenario) { return scenario; }\n";
722
- var DEFAULT_TIMEOUT_MS2 = 1e4;
723
806
  var BASE_SUFFIX = ".base.ts";
724
807
  var SCENARIO_SUFFIX = ".scenario.ts";
725
808
  var SDK_UI_RUNTIME_EXTERNALS = [
@@ -751,15 +834,15 @@ function findScenarioStackFrame(options) {
751
834
  if (!options.stack) {
752
835
  return null;
753
836
  }
754
- const absolutePath = path2.resolve(options.scenarioFilePath);
755
- const relativePath = path2.relative(options.projectRoot, absolutePath);
756
- const normalizedRelativePath = relativePath.split(path2.sep).join("/");
837
+ const absolutePath = path.resolve(options.scenarioFilePath);
838
+ const relativePath = path.relative(options.projectRoot, absolutePath);
839
+ const normalizedRelativePath = relativePath.split(path.sep).join("/");
757
840
  const escapedAbsolutePath = escapeRegExp(absolutePath);
758
841
  const escapedRelativePath = escapeRegExp(normalizedRelativePath);
759
842
  const absoluteFrame = new RegExp(`${escapedAbsolutePath}:(\\d+):(\\d+)`);
760
843
  const relativeFrame = new RegExp(`${escapedRelativePath}:(\\d+):(\\d+)`);
761
844
  for (const line of options.stack.split("\n")) {
762
- const normalizedLine = line.split(path2.sep).join("/");
845
+ const normalizedLine = line.split(path.sep).join("/");
763
846
  const match = normalizedLine.match(absoluteFrame) ?? normalizedLine.match(relativeFrame);
764
847
  if (match?.[1] && match?.[2]) {
765
848
  return `at ${normalizedRelativePath}:${match[1]}:${match[2]}`;
@@ -770,12 +853,45 @@ function findScenarioStackFrame(options) {
770
853
  function escapeRegExp(value) {
771
854
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
772
855
  }
773
- var testingExpectApiFactoryPromise;
774
- async function loadTestingExpectApiFactory() {
775
- testingExpectApiFactoryPromise ??= import("@dreamboard-games/sdk/testing").then(
776
- (module) => module.createExpectApi
856
+ var projectReducerNativeModules = /* @__PURE__ */ new Map();
857
+ function resolveProjectSdkModule(projectRoot, specifier) {
858
+ const requireFromProject = createRequire(
859
+ path.join(projectRoot, "package.json")
777
860
  );
778
- return testingExpectApiFactoryPromise;
861
+ return requireFromProject.resolve(specifier);
862
+ }
863
+ async function importProjectSdkModule(projectRoot, specifier) {
864
+ const modulePath = resolveProjectSdkModule(projectRoot, specifier);
865
+ return await import(pathToFileURL(modulePath).href);
866
+ }
867
+ async function loadProjectReducerNativeModules(projectRoot) {
868
+ const cacheKey = path.resolve(projectRoot);
869
+ const cached = projectReducerNativeModules.get(cacheKey);
870
+ if (cached) {
871
+ return cached;
872
+ }
873
+ const promise = Promise.all([
874
+ importProjectSdkModule(cacheKey, "@dreamboard-games/sdk/reducer"),
875
+ importProjectSdkModule(cacheKey, "@dreamboard-games/sdk/reducer-contract"),
876
+ importProjectSdkModule(
877
+ cacheKey,
878
+ "@dreamboard-games/sdk/testing"
879
+ )
880
+ ]).then(([reducerModule, reducerContractModule, testingModule]) => {
881
+ if (typeof reducerModule.createReducerBundle !== "function" || typeof reducerModule.contractFingerprint !== "function" || typeof reducerContractModule.materializeManifestTable !== "function" || typeof testingModule.createExpectApi !== "function") {
882
+ throw new Error(
883
+ "Installed @dreamboard-games/sdk does not expose the reducer-native test helpers required by this CLI."
884
+ );
885
+ }
886
+ return {
887
+ createReducerBundle: reducerModule.createReducerBundle,
888
+ contractFingerprint: reducerModule.contractFingerprint,
889
+ materializeManifestTable: reducerContractModule.materializeManifestTable,
890
+ createExpectApi: testingModule.createExpectApi
891
+ };
892
+ });
893
+ projectReducerNativeModules.set(cacheKey, promise);
894
+ return promise;
779
895
  }
780
896
  function createSubmissionError(errorCode, message, fallbackMessage) {
781
897
  const error = new Error(message ?? fallbackMessage);
@@ -783,22 +899,9 @@ function createSubmissionError(errorCode, message, fallbackMessage) {
783
899
  error.errorCode = errorCode;
784
900
  return error;
785
901
  }
786
- function parseJsonValue(value) {
787
- if (typeof value !== "string") {
788
- return value;
789
- }
790
- try {
791
- return JSON.parse(value);
792
- } catch {
793
- return value;
794
- }
795
- }
796
902
  function deepEqual(left, right) {
797
903
  return isDeepStrictEqual(left, right);
798
904
  }
799
- function normalizeScenarioRunners(runners) {
800
- return runners && runners.length > 0 ? runners : ["reducer"];
801
- }
802
905
  function shouldRefreshReducerTestingTypes(existingContent) {
803
906
  if (existingContent === null || existingContent.trim().length === 0 || existingContent === TESTING_TYPES_STUB || existingContent.startsWith(GENERATED_TESTING_TYPES_PREFIX)) {
804
907
  return true;
@@ -812,7 +915,7 @@ async function discoverFiles(root, suffix) {
812
915
  const entries = await readdir(root, { withFileTypes: true });
813
916
  const files = [];
814
917
  for (const entry of entries) {
815
- const entryPath = path2.join(root, entry.name);
918
+ const entryPath = path.join(root, entry.name);
816
919
  if (entry.isDirectory()) {
817
920
  files.push(...await discoverFiles(entryPath, suffix));
818
921
  continue;
@@ -861,62 +964,52 @@ function parseTypedScenarioDefinition(value) {
861
964
  };
862
965
  }
863
966
  async function isReducerNativeTestingWorkspace(projectRoot) {
864
- return await exists(path2.join(projectRoot, "app", "game.ts")) && await exists(
865
- path2.join(projectRoot, "shared", "generated", "ui-contract.ts")
967
+ return await exists(path.join(projectRoot, "app", "game.ts")) && await exists(
968
+ path.join(projectRoot, "shared", "generated", "ui-contract.ts")
866
969
  );
867
970
  }
868
971
  async function ensureReducerNativeTestingFiles(projectRoot) {
869
- const testingTypesPath = path2.join(projectRoot, "test", "testing-types.ts");
870
- const testingContractPath = path2.join(
871
- projectRoot,
872
- "test",
873
- "generated",
874
- "testing-contract.ts"
875
- );
876
- const baseStatesPath = path2.join(
877
- projectRoot,
878
- "test",
879
- "generated",
880
- "base-states.generated.ts"
881
- );
882
- const baseStatesDtsPath = path2.join(
972
+ const testingTypesPath = "test/testing-types.ts";
973
+ const testingContractPath = "test/generated/testing-contract.ts";
974
+ const baseStatesPath = "test/generated/base-states.generated.ts";
975
+ const baseStatesDtsPath = "test/generated/base-states.generated.d.ts";
976
+ const scenarioManifestPath = "test/generated/scenario-manifest.generated.ts";
977
+ await ensureDir(resolveWorkspacePath(projectRoot, "test"));
978
+ await ensureDir(resolveWorkspacePath(projectRoot, "test/generated"));
979
+ const existingTestingTypes = await readWorkspaceTextFileIfExists(
883
980
  projectRoot,
884
- "test",
885
- "generated",
886
- "base-states.generated.d.ts"
981
+ testingTypesPath
887
982
  );
888
- const scenarioManifestPath = path2.join(
889
- projectRoot,
890
- "test",
891
- "generated",
892
- "scenario-manifest.generated.ts"
893
- );
894
- await ensureDir(path2.dirname(testingTypesPath));
895
- await ensureDir(path2.dirname(testingContractPath));
896
- const existingTestingTypes = await readTextFileIfExists(testingTypesPath);
897
983
  if (shouldRefreshReducerTestingTypes(existingTestingTypes)) {
898
- await writeTextFile(
984
+ await writeWorkspaceTextFile(
985
+ projectRoot,
899
986
  testingTypesPath,
900
987
  REDUCER_TESTING_TYPES_WRAPPER_CONTENT
901
988
  );
902
989
  }
903
990
  const rejectionCodes = await collectKnownRejectionCodes(projectRoot);
904
- await writeTextFile(
991
+ await writeWorkspaceTextFile(
992
+ projectRoot,
905
993
  testingContractPath,
906
994
  buildReducerTestingContractContent({ rejectionCodes })
907
995
  );
908
- const header = "// Generated by dreamboard test generate. Do not edit by hand.\n";
909
- if (!await exists(baseStatesPath)) {
910
- await writeTextFile(
996
+ const header = "// Generated by dreamboard test. Do not edit by hand.\n";
997
+ if (!await workspacePathExists(projectRoot, baseStatesPath)) {
998
+ await writeWorkspaceTextFile(
999
+ projectRoot,
911
1000
  baseStatesPath,
912
1001
  `${header}export const BASE_STATES = {} as const;
913
1002
  export const BASE_STATES_CONTRACT_FINGERPRINT = undefined;
914
1003
  `
915
1004
  );
916
1005
  } else {
917
- const existingBaseStates = await readTextFileIfExists(baseStatesPath);
1006
+ const existingBaseStates = await readWorkspaceTextFileIfExists(
1007
+ projectRoot,
1008
+ baseStatesPath
1009
+ );
918
1010
  if (existingBaseStates && !existingBaseStates.includes("BASE_STATES_CONTRACT_FINGERPRINT")) {
919
- await writeTextFile(
1011
+ await writeWorkspaceTextFile(
1012
+ projectRoot,
920
1013
  baseStatesPath,
921
1014
  `${existingBaseStates.trimEnd()}
922
1015
  export const BASE_STATES_CONTRACT_FINGERPRINT = undefined;
@@ -924,17 +1017,22 @@ export const BASE_STATES_CONTRACT_FINGERPRINT = undefined;
924
1017
  );
925
1018
  }
926
1019
  }
927
- if (!await exists(baseStatesDtsPath)) {
928
- await writeTextFile(
1020
+ if (!await workspacePathExists(projectRoot, baseStatesDtsPath)) {
1021
+ await writeWorkspaceTextFile(
1022
+ projectRoot,
929
1023
  baseStatesDtsPath,
930
1024
  `${header}export declare const BASE_STATES: Record<string, unknown>;
931
1025
  export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
932
1026
  `
933
1027
  );
934
1028
  } else {
935
- const existingBaseStatesDts = await readTextFileIfExists(baseStatesDtsPath);
1029
+ const existingBaseStatesDts = await readWorkspaceTextFileIfExists(
1030
+ projectRoot,
1031
+ baseStatesDtsPath
1032
+ );
936
1033
  if (existingBaseStatesDts && !existingBaseStatesDts.includes("BASE_STATES_CONTRACT_FINGERPRINT")) {
937
- await writeTextFile(
1034
+ await writeWorkspaceTextFile(
1035
+ projectRoot,
938
1036
  baseStatesDtsPath,
939
1037
  `${existingBaseStatesDts.trimEnd()}
940
1038
  export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
@@ -942,8 +1040,9 @@ export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
942
1040
  );
943
1041
  }
944
1042
  }
945
- if (!await exists(scenarioManifestPath)) {
946
- await writeTextFile(
1043
+ if (!await workspacePathExists(projectRoot, scenarioManifestPath)) {
1044
+ await writeWorkspaceTextFile(
1045
+ projectRoot,
947
1046
  scenarioManifestPath,
948
1047
  `${header}export const SCENARIO_MANIFEST = [] as const;
949
1048
  `
@@ -958,7 +1057,7 @@ var DEFAULT_REJECTION_CODES = [
958
1057
  ];
959
1058
  async function collectKnownRejectionCodes(projectRoot) {
960
1059
  const knownCodes = new Set(DEFAULT_REJECTION_CODES);
961
- const gamePath = path2.join(projectRoot, "app", "game.ts");
1060
+ const gamePath = path.join(projectRoot, "app", "game.ts");
962
1061
  if (!await exists(gamePath)) {
963
1062
  return Array.from(knownCodes).sort(
964
1063
  (left, right) => left.localeCompare(right)
@@ -985,7 +1084,7 @@ async function collectKnownRejectionCodes(projectRoot) {
985
1084
  }
986
1085
  async function loadTypedBases(projectRoot) {
987
1086
  const baseFiles = await discoverFiles(
988
- path2.join(projectRoot, "test", "bases"),
1087
+ resolveWorkspacePath(projectRoot, "test/bases"),
989
1088
  BASE_SUFFIX
990
1089
  );
991
1090
  const loaded = [];
@@ -1004,8 +1103,8 @@ async function loadTypedBases(projectRoot) {
1004
1103
  return loaded;
1005
1104
  }
1006
1105
  async function loadTypedScenarios(projectRoot, options) {
1007
- const scenarioFiles = options.scenarioPath ? [path2.resolve(projectRoot, options.scenarioPath)] : await discoverFiles(
1008
- path2.join(projectRoot, "test", "scenarios"),
1106
+ const scenarioFiles = options.scenarioPath ? [resolveWorkspacePath(projectRoot, options.scenarioPath)] : await discoverFiles(
1107
+ resolveWorkspacePath(projectRoot, "test/scenarios"),
1009
1108
  SCENARIO_SUFFIX
1010
1109
  );
1011
1110
  const loaded = [];
@@ -1024,8 +1123,8 @@ async function loadTypedScenarios(projectRoot, options) {
1024
1123
  return loaded;
1025
1124
  }
1026
1125
  function reducerNativeTestHelperExternals(projectRoot, filePath) {
1027
- const testingTypesPath = path2.join(projectRoot, "test", "testing-types");
1028
- const relative = path2.relative(path2.dirname(filePath), testingTypesPath).replaceAll("\\", "/");
1126
+ const testingTypesPath = path.join(projectRoot, "test", "testing-types");
1127
+ const relative = path.relative(path.dirname(filePath), testingTypesPath).replaceAll("\\", "/");
1029
1128
  const specifier = relative.startsWith(".") ? relative : `./${relative}`;
1030
1129
  return [specifier, `${specifier}.ts`, ...SDK_UI_RUNTIME_EXTERNALS];
1031
1130
  }
@@ -1206,7 +1305,8 @@ function summarizeTableValidationError(manifest, error) {
1206
1305
  ].join("\n");
1207
1306
  }
1208
1307
  var ShadowReducerRuntime = class {
1209
- constructor(manifest, gameModuleDefault, createInitialTable, seed, players, setupProfileId, debug = false) {
1308
+ constructor(modules, manifest, gameModuleDefault, createInitialTable, seed, players, setupProfileId, debug = false) {
1309
+ this.modules = modules;
1210
1310
  this.manifest = manifest;
1211
1311
  this.gameModuleDefault = gameModuleDefault;
1212
1312
  this.createInitialTable = createInitialTable;
@@ -1219,20 +1319,13 @@ var ShadowReducerRuntime = class {
1219
1319
  "app/game.ts must export a reducer-native game definition."
1220
1320
  );
1221
1321
  }
1222
- this.bundle = createReducerBundle(gameModuleDefault);
1322
+ this.bundle = this.modules.createReducerBundle(gameModuleDefault);
1223
1323
  this.runtime = this.bundle.createInProcessRuntime();
1224
1324
  this.playerIds = Array.from(
1225
1325
  { length: players },
1226
1326
  (_, index) => `player-${index + 1}`
1227
1327
  );
1228
1328
  }
1229
- manifest;
1230
- gameModuleDefault;
1231
- createInitialTable;
1232
- seed;
1233
- players;
1234
- setupProfileId;
1235
- debug;
1236
1329
  bundle;
1237
1330
  runtime;
1238
1331
  playerIds;
@@ -1250,7 +1343,7 @@ var ShadowReducerRuntime = class {
1250
1343
  this.createInitialTable?.({
1251
1344
  playerIds: this.playerIds,
1252
1345
  shuffleItems
1253
- }) ?? materializeManifestTable({
1346
+ }) ?? this.modules.materializeManifestTable({
1254
1347
  manifest: this.manifest,
1255
1348
  playerIds: this.playerIds,
1256
1349
  shuffleItems
@@ -1405,197 +1498,13 @@ function toReducerInput(input) {
1405
1498
  params: input.params
1406
1499
  };
1407
1500
  }
1408
- var RemoteGameplayTracker = class {
1409
- constructor(sessionId, playerId) {
1410
- this.sessionId = sessionId;
1411
- this.playerId = playerId;
1412
- this.state.playerId = playerId;
1413
- }
1414
- sessionId;
1415
- playerId;
1416
- state = {
1417
- version: -1,
1418
- actionSetVersion: "",
1419
- currentPhase: null,
1420
- playerId: "player-1",
1421
- view: null,
1422
- availableInteractions: []
1423
- };
1424
- liveBootstrap = false;
1425
- async bootstrap() {
1426
- const { data, error, response } = await getSessionSnapshot({
1427
- path: { sessionId: this.sessionId },
1428
- query: { playerId: this.playerId }
1429
- });
1430
- if (error || !data || data.type !== "gameplay") {
1431
- throw toDreamboardApiError(
1432
- error ?? { detail: "Gameplay snapshot was unavailable." },
1433
- response,
1434
- "Failed to load the remote gameplay bootstrap snapshot"
1435
- );
1436
- }
1437
- if (data.gameplay.actionSetVersion === "authority") {
1438
- return;
1439
- }
1440
- this.liveBootstrap = true;
1441
- this.applyGameplay(data.gameplay);
1442
- }
1443
- hasLiveBootstrap() {
1444
- return this.liveBootstrap;
1445
- }
1446
- snapshot() {
1447
- return {
1448
- ...this.state,
1449
- availableInteractions: [...this.state.availableInteractions]
1450
- };
1451
- }
1452
- applyShadow(shadow) {
1453
- const projection = shadow.projectAllSeats();
1454
- const playerId = this.playerId;
1455
- const seat = projection.seats[playerId];
1456
- this.state.version = shadow.currentVersion();
1457
- this.state.actionSetVersion = "";
1458
- this.state.playerId = playerId;
1459
- this.state.currentPhase = shadow.phase();
1460
- this.state.view = seat?.view ?? null;
1461
- this.state.availableInteractions = shadow.availableInteractionsForPlayer(playerId);
1462
- }
1463
- applyGameplay(gameplay) {
1464
- const playerId = gameplay.perspectivePlayerId;
1465
- const seat = gameplay.seats[playerId];
1466
- this.state.version = gameplay.version;
1467
- this.state.actionSetVersion = gameplay.actionSetVersion;
1468
- this.state.playerId = playerId;
1469
- this.state.currentPhase = gameplay.shared.currentPhase;
1470
- this.state.view = parseSeatView(seat?.view ?? "null");
1471
- this.state.availableInteractions = (seat?.availableInteractionRefs ?? []).map((ref) => gameplay.interactionsByRef[ref]).filter(
1472
- (interaction) => Boolean(interaction)
1473
- ).map((interaction) => interaction.interactionId);
1474
- }
1475
- };
1476
- function parseSeatView(raw) {
1477
- return raw ? parseJsonValue(raw) : null;
1478
- }
1479
- function sleep(ms) {
1480
- return new Promise((resolve) => setTimeout(resolve, ms));
1481
- }
1482
- function formatIssuePath(label, path3) {
1483
- let formatted = label;
1484
- for (const segment of path3) {
1485
- formatted += typeof segment === "number" ? `[${segment}]` : `.${String(segment)}`;
1486
- }
1487
- return formatted;
1488
- }
1489
- function parseWirePayload(methodName, value, label, schema) {
1490
- const parsed = schema.safeParse(value);
1491
- if (parsed.success) {
1492
- return parsed.data;
1493
- }
1494
- const firstIssue = parsed.error.issues[0];
1495
- const formattedPath = formatIssuePath(label, firstIssue?.path ?? []);
1496
- throw new Error(
1497
- `Reducer bundle returned invalid payload for '${methodName}': ${formattedPath} ${firstIssue?.message ?? "failed schema validation"}.`
1498
- );
1499
- }
1500
1501
  function assertDispatchResultWireContract(result) {
1501
- return parseWirePayload(
1502
- "dispatch",
1503
- result,
1504
- "DispatchResult",
1505
- ReducerContractZod.DispatchResultSchema
1506
- );
1507
- }
1508
- async function createBrowserBridgeClient(page) {
1509
- const bridgeExists = async () => page.evaluate(
1510
- () => Boolean(
1511
- window.__dreamboardTestBridge__
1512
- )
1513
- );
1514
- const startedAt = Date.now();
1515
- while (!await bridgeExists()) {
1516
- if (Date.now() - startedAt > DEFAULT_TIMEOUT_MS2) {
1517
- const bodyText = await page.locator("body").innerText().catch(() => "");
1518
- const diagnostic = bodyText.trim().replace(/\s+/g, " ").slice(0, 400);
1519
- throw new Error(
1520
- diagnostic.length > 0 ? `Timed out waiting for browser test bridge. Page text: ${diagnostic}` : "Timed out waiting for browser test bridge."
1521
- );
1522
- }
1523
- await sleep(50);
1524
- }
1525
- return {
1526
- snapshot: () => page.evaluate(
1527
- () => window.__dreamboardTestBridge__.snapshot()
1528
- ),
1529
- submitInteraction: (playerId, interactionId, params) => page.evaluate(
1530
- ([nextPlayerId, nextInteractionId, nextParams]) => window.__dreamboardTestBridge__.submitInteraction(
1531
- nextPlayerId,
1532
- nextInteractionId,
1533
- nextParams
1534
- ),
1535
- [playerId, interactionId, params]
1536
- ),
1537
- waitForVersionChange: async (previousVersion, timeoutMs = DEFAULT_TIMEOUT_MS2) => {
1538
- const started = Date.now();
1539
- while (Date.now() - started < timeoutMs) {
1540
- const snapshot = await page.evaluate(
1541
- () => window.__dreamboardTestBridge__.snapshot()
1542
- );
1543
- if (snapshot.version > previousVersion) {
1544
- return snapshot;
1545
- }
1546
- await sleep(50);
1547
- }
1548
- throw new Error("Timed out waiting for browser gameplay version change.");
1549
- }
1550
- };
1551
- }
1552
- async function assertLiveMatchesShadow(runner, shadow, live) {
1553
- if (runner === "reducer") {
1554
- return;
1555
- }
1556
- const currentPhase = "currentPhase" in live ? live.currentPhase : shadow.phase();
1557
- if (currentPhase !== shadow.phase()) {
1558
- throw new Error(
1559
- `Live phase '${String(currentPhase)}' diverged from reducer phase '${shadow.phase()}'.`
1560
- );
1561
- }
1562
- const livePlayerId = "playerId" in live ? live.playerId : live.controllingPlayerId;
1563
- const liveView = "view" in live ? live.view : null;
1564
- if (livePlayerId && !deepEqual(liveView, await shadow.view(livePlayerId))) {
1565
- throw new Error("Live projected views diverged from reducer shadow views.");
1566
- }
1567
- const liveInteractions = "availableInteractions" in live ? live.availableInteractions : null;
1568
- if (livePlayerId && liveInteractions && !deepEqual(
1569
- liveInteractions,
1570
- shadow.availableInteractionsForPlayer(livePlayerId)
1571
- )) {
1502
+ if (typeof result !== "object" || result === null) {
1572
1503
  throw new Error(
1573
- "Live available-interactions metadata diverged from reducer shadow state."
1504
+ "Reducer bundle returned invalid payload for 'dispatch': DispatchResult must be an object."
1574
1505
  );
1575
1506
  }
1576
- }
1577
- async function loadBrowserDriver(projectRoot) {
1578
- const filePath = path2.join(projectRoot, "test", "browser-driver.ts");
1579
- if (!await exists(filePath)) {
1580
- return null;
1581
- }
1582
- const module = await importTypeScriptModule(filePath);
1583
- return module.default ?? module;
1584
- }
1585
- async function openBrowserPage(playUrl, config) {
1586
- configurePlaywrightBrowsersPath();
1587
- const { chromium } = await import("playwright");
1588
- const browser = await chromium.launch({ headless: true });
1589
- const context = await browser.newContext({
1590
- viewport: { width: 1440, height: 900 }
1591
- });
1592
- const initScript = await buildBrowserAuthInitScript(config);
1593
- if (initScript) {
1594
- await context.addInitScript({ content: initScript });
1595
- }
1596
- const page = await context.newPage();
1597
- await page.goto(playUrl, { waitUntil: "domcontentloaded" });
1598
- return { browser, page };
1507
+ return result;
1599
1508
  }
1600
1509
  function sanitizeSnapshotSegment(value) {
1601
1510
  return value.replace(/[^a-zA-Z0-9._-]+/g, "-");
@@ -1603,19 +1512,16 @@ function sanitizeSnapshotSegment(value) {
1603
1512
  function createScenarioSnapshotMatcher(options) {
1604
1513
  return (filename, actual) => {
1605
1514
  const suffix = filename ? `.${sanitizeSnapshotSegment(filename)}` : "";
1606
- const snapshotPath = path2.join(
1515
+ const snapshotPath = resolveWorkspacePath(
1607
1516
  options.projectRoot,
1608
- "test",
1609
- "generated",
1610
- "snapshots",
1611
- `${sanitizeSnapshotSegment(options.scenarioId)}${suffix}.snapshot.json`
1517
+ `test/generated/snapshots/${sanitizeSnapshotSegment(options.scenarioId)}${suffix}.snapshot.json`
1612
1518
  );
1613
1519
  const wrappedValue = {
1614
1520
  value: actual
1615
1521
  };
1616
1522
  const serialized = `${JSON.stringify(wrappedValue, null, 2)}
1617
1523
  `;
1618
- mkdirSync(path2.dirname(snapshotPath), { recursive: true });
1524
+ mkdirSync(path.dirname(snapshotPath), { recursive: true });
1619
1525
  if (!existsSync(snapshotPath) || options.updateSnapshots) {
1620
1526
  writeFileSync(snapshotPath, serialized, "utf8");
1621
1527
  return;
@@ -1623,7 +1529,7 @@ function createScenarioSnapshotMatcher(options) {
1623
1529
  const previous = JSON.parse(readFileSync(snapshotPath, "utf8"));
1624
1530
  if (!deepEqual(previous.value, actual)) {
1625
1531
  throw new Error(
1626
- `Snapshot mismatch for scenario '${options.scenarioId}'. Re-run with --update-snapshots to refresh ${path2.relative(options.projectRoot, snapshotPath)}.`
1532
+ `Snapshot mismatch for scenario '${options.scenarioId}'. Re-run with --update-snapshots to refresh ${path.relative(options.projectRoot, snapshotPath)}.`
1627
1533
  );
1628
1534
  }
1629
1535
  };
@@ -1632,24 +1538,12 @@ async function createScenarioContext(options) {
1632
1538
  const api = {
1633
1539
  start: async () => {
1634
1540
  await options.shadow.start();
1635
- if (options.runner === "remote" && options.remote) {
1636
- if (options.remote.tracker.hasLiveBootstrap()) {
1637
- const live = options.remote.tracker.snapshot();
1638
- await assertLiveMatchesShadow("remote", options.shadow, live);
1639
- } else {
1640
- options.remote.tracker.applyShadow(options.shadow);
1641
- }
1642
- }
1643
- if (options.runner === "browser" && options.browser) {
1644
- const live = await options.browser.bridge.snapshot();
1645
- await assertLiveMatchesShadow("browser", options.shadow, live);
1646
- }
1647
1541
  },
1648
1542
  patchState: async (mutator) => {
1649
1543
  await api.start();
1650
- if (options.live || options.remote || options.browser || options.actionPlan) {
1544
+ if (options.live || options.actionPlan) {
1651
1545
  throw new Error(
1652
- "game.patchState is only supported for reducer snapshot scenarios. Use it to materialize a state before --from-scenario or reducer tests, not inside live replay/browser runners."
1546
+ "game.patchState is only supported for reducer snapshot scenarios. Use it to materialize a state before --from-scenario or reducer tests, not inside live replay."
1653
1547
  );
1654
1548
  }
1655
1549
  options.shadow.patchState(mutator);
@@ -1658,7 +1552,7 @@ async function createScenarioContext(options) {
1658
1552
  await api.start();
1659
1553
  const interactionParams = params ?? {};
1660
1554
  const normalizedInputs = interactionParams && typeof interactionParams === "object" && !Array.isArray(interactionParams) ? interactionParams : {};
1661
- const previousVersion = options.live ? options.live.version : options.actionPlan ? options.shadow.currentVersion() : options.runner === "remote" ? options.remote?.tracker.snapshot().version ?? 0 : options.runner === "browser" ? (await options.browser?.bridge.snapshot())?.version ?? 0 : 0;
1555
+ const previousVersion = options.live ? options.live.version : options.actionPlan ? options.shadow.currentVersion() : 0;
1662
1556
  if (options.live) {
1663
1557
  if (!options.live.actionSetVersion) {
1664
1558
  options.live.actionSetVersion = await fetchLiveActionSetVersion(
@@ -1725,20 +1619,6 @@ async function createScenarioContext(options) {
1725
1619
  }
1726
1620
  options.live.version = typeof data?.version === "number" ? data.version : previousVersion + 1;
1727
1621
  options.live.actionSetVersion = data?.actionSetVersion ?? options.live.actionSetVersion;
1728
- } else if (options.runner === "remote" && options.remote) {
1729
- } else if (options.runner === "browser" && options.browser) {
1730
- const handled = await options.browser.driver?.interaction?.(options.browser.bridge, {
1731
- playerId,
1732
- interactionId,
1733
- params: interactionParams
1734
- }) === true;
1735
- if (!handled) {
1736
- await options.browser.bridge.submitInteraction(
1737
- playerId,
1738
- interactionId,
1739
- interactionParams
1740
- );
1741
- }
1742
1622
  }
1743
1623
  await options.shadow.submitInteraction(
1744
1624
  playerId,
@@ -1758,18 +1638,6 @@ async function createScenarioContext(options) {
1758
1638
  });
1759
1639
  options.actionPlan.submitIndex = (options.actionPlan.submitIndex ?? 0) + 1;
1760
1640
  }
1761
- if (options.runner === "remote" && options.remote) {
1762
- options.remote.tracker.applyShadow(options.shadow);
1763
- await assertLiveMatchesShadow(
1764
- "remote",
1765
- options.shadow,
1766
- options.remote.tracker.snapshot()
1767
- );
1768
- }
1769
- if (options.runner === "browser" && options.browser) {
1770
- const live = await options.browser.bridge.waitForVersionChange(previousVersion);
1771
- await assertLiveMatchesShadow("browser", options.shadow, live);
1772
- }
1773
1641
  }
1774
1642
  };
1775
1643
  return {
@@ -1853,19 +1721,26 @@ async function createSessionFromScenario(options) {
1853
1721
  }
1854
1722
  var MATERIALIZED_SCENARIO_CACHE_VERSION = 1;
1855
1723
  function materializedScenarioCachePath(options) {
1856
- return path2.join(
1857
- options.projectRoot,
1724
+ return [
1858
1725
  PROJECT_DIR_NAME,
1859
1726
  "dev",
1860
1727
  "scenario-cache",
1861
1728
  `${sanitizeSnapshotSegment(options.baseId)}.${sanitizeSnapshotSegment(
1862
1729
  options.scenarioId
1863
1730
  )}.${options.fingerprintHash}.json`
1731
+ ].join("/");
1732
+ }
1733
+ function materializedScenarioCacheFilePath(options) {
1734
+ return resolveWorkspacePath(
1735
+ options.projectRoot,
1736
+ materializedScenarioCachePath(options)
1864
1737
  );
1865
1738
  }
1866
1739
  async function readMaterializedScenarioCache(options) {
1867
- const cachePath = materializedScenarioCachePath(options);
1868
- const text = await readTextFileIfExists(cachePath);
1740
+ const text = await readWorkspaceTextFileIfExists(
1741
+ options.projectRoot,
1742
+ materializedScenarioCachePath(options)
1743
+ );
1869
1744
  if (!text) return null;
1870
1745
  try {
1871
1746
  const payload = JSON.parse(
@@ -1880,13 +1755,17 @@ async function readMaterializedScenarioCache(options) {
1880
1755
  }
1881
1756
  }
1882
1757
  async function writeMaterializedScenarioCache(options) {
1883
- const cachePath = materializedScenarioCachePath(options);
1884
- await ensureDir(path2.dirname(cachePath));
1885
- await writeJsonFile(cachePath, {
1886
- cacheVersion: MATERIALIZED_SCENARIO_CACHE_VERSION,
1887
- fingerprintHash: options.fingerprintHash,
1888
- materialized: options.materialized
1889
- });
1758
+ const cachePath = materializedScenarioCacheFilePath(options);
1759
+ await ensureDir(path.dirname(cachePath));
1760
+ await writeWorkspaceJsonFile(
1761
+ options.projectRoot,
1762
+ materializedScenarioCachePath(options),
1763
+ {
1764
+ cacheVersion: MATERIALIZED_SCENARIO_CACHE_VERSION,
1765
+ fingerprintHash: options.fingerprintHash,
1766
+ materialized: options.materialized
1767
+ }
1768
+ );
1890
1769
  }
1891
1770
  async function materializeScenarioReducerState(options) {
1892
1771
  await ensureReducerNativeTestingFiles(options.projectRoot);
@@ -1944,12 +1823,12 @@ async function materializeScenarioReducerState(options) {
1944
1823
  const generatedBase = generatedBaseStates?.[baseStateKey(base.definition.id)];
1945
1824
  if (!generatedBase) {
1946
1825
  throw new Error(
1947
- `Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test generate' before using --from-scenario.`
1826
+ `Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test' before using --from-scenario.`
1948
1827
  );
1949
1828
  }
1950
1829
  if (typeof generatedBase.version !== "number") {
1951
1830
  throw new Error(
1952
- `Generated base artifact for '${base.definition.id}' is stale. Run 'dreamboard test generate' before using --from-scenario.`
1831
+ `Generated base artifact for '${base.definition.id}' is stale. Run 'dreamboard test' before using --from-scenario.`
1953
1832
  );
1954
1833
  }
1955
1834
  const canTrustGeneratedFingerprint = options.trustGeneratedFingerprint === true && generatedBase.fingerprint.compiledResultId === options.compiledResultId && generatedBase.fingerprint.gameId === options.gameId;
@@ -1976,13 +1855,14 @@ async function materializeScenarioReducerState(options) {
1976
1855
  if (cached) {
1977
1856
  return cached;
1978
1857
  }
1979
- const [gameModule, manifestContractModule] = await Promise.all([
1858
+ const [gameModule, manifestContractModule, modules] = await Promise.all([
1980
1859
  importTypeScriptModule(
1981
- path2.join(options.projectRoot, "app", "game.ts")
1860
+ path.join(options.projectRoot, "app", "game.ts")
1982
1861
  ),
1983
1862
  importTypeScriptModule(
1984
- path2.join(options.projectRoot, "shared", "manifest-contract.ts")
1985
- )
1863
+ path.join(options.projectRoot, "shared", "manifest-contract.ts")
1864
+ ),
1865
+ loadProjectReducerNativeModules(options.projectRoot)
1986
1866
  ]);
1987
1867
  const createInitialTable = typeof manifestContractModule.createInitialTable === "function" ? manifestContractModule.createInitialTable : null;
1988
1868
  const resolvedBase = resolveBaseDefinition(base, basesById);
@@ -1992,6 +1872,7 @@ async function materializeScenarioReducerState(options) {
1992
1872
  basesById
1993
1873
  });
1994
1874
  const shadow = new ShadowReducerRuntime(
1875
+ modules,
1995
1876
  manifest,
1996
1877
  gameModule.default,
1997
1878
  createInitialTable,
@@ -2002,9 +1883,8 @@ async function materializeScenarioReducerState(options) {
2002
1883
  );
2003
1884
  shadow.hydrate(generatedBase.snapshot, generatedBase.version);
2004
1885
  const context = await createScenarioContext({
2005
- runner: "reducer",
2006
1886
  shadow,
2007
- expect: (await loadTestingExpectApiFactory())()
1887
+ expect: modules.createExpectApi()
2008
1888
  });
2009
1889
  shadow.clearHistory();
2010
1890
  await scenario.definition.when(context);
@@ -2056,18 +1936,20 @@ async function replayScenarioThroughBackend(options) {
2056
1936
  generatedBaseStates,
2057
1937
  manifest,
2058
1938
  gameModule,
2059
- manifestContractModule
1939
+ manifestContractModule,
1940
+ modules
2060
1941
  ] = await Promise.all([
2061
1942
  loadTypedBases(options.projectRoot),
2062
1943
  loadTypedScenarios(options.projectRoot, {}),
2063
1944
  loadGeneratedBaseStates(options.projectRoot),
2064
1945
  loadManifest(options.projectRoot),
2065
1946
  importTypeScriptModule(
2066
- path2.join(options.projectRoot, "app", "game.ts")
1947
+ path.join(options.projectRoot, "app", "game.ts")
2067
1948
  ),
2068
1949
  importTypeScriptModule(
2069
- path2.join(options.projectRoot, "shared", "manifest-contract.ts")
2070
- )
1950
+ path.join(options.projectRoot, "shared", "manifest-contract.ts")
1951
+ ),
1952
+ loadProjectReducerNativeModules(options.projectRoot)
2071
1953
  ]);
2072
1954
  const matchingScenarios = scenarios.filter(
2073
1955
  (scenario2) => scenario2.definition.id === options.scenarioId
@@ -2089,7 +1971,7 @@ async function replayScenarioThroughBackend(options) {
2089
1971
  const generatedBase = generatedBaseStates?.[baseStateKey(base.definition.id)];
2090
1972
  if (!generatedBase) {
2091
1973
  throw new Error(
2092
- `Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test generate' before replaying a scenario.`
1974
+ `Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test' before replaying a scenario.`
2093
1975
  );
2094
1976
  }
2095
1977
  validateGeneratedFingerprint({
@@ -2110,6 +1992,7 @@ async function replayScenarioThroughBackend(options) {
2110
1992
  basesById
2111
1993
  });
2112
1994
  const shadow = new ShadowReducerRuntime(
1995
+ await loadProjectReducerNativeModules(options.projectRoot),
2113
1996
  manifest,
2114
1997
  gameModule.default,
2115
1998
  createInitialTable,
@@ -2122,7 +2005,7 @@ async function replayScenarioThroughBackend(options) {
2122
2005
  const parentArtifact = generatedBaseStates?.[baseStateKey(base.definition.extends)];
2123
2006
  if (!parentArtifact || typeof parentArtifact.version !== "number") {
2124
2007
  throw new Error(
2125
- `Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing or stale. Run 'dreamboard test generate' first.`
2008
+ `Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing or stale. Run 'dreamboard test' first.`
2126
2009
  );
2127
2010
  }
2128
2011
  shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
@@ -2160,9 +2043,8 @@ async function replayScenarioThroughBackend(options) {
2160
2043
  }
2161
2044
  const diagnostics = [];
2162
2045
  const context = await createScenarioContext({
2163
- runner: "reducer",
2164
2046
  shadow,
2165
- expect: (await loadTestingExpectApiFactory())(),
2047
+ expect: (await loadProjectReducerNativeModules(options.projectRoot)).createExpectApi(),
2166
2048
  live: {
2167
2049
  sessionId: session.sessionId,
2168
2050
  version: 0,
@@ -2211,18 +2093,20 @@ async function createScenarioActionPlan(options) {
2211
2093
  generatedBaseStates,
2212
2094
  manifest,
2213
2095
  gameModule,
2214
- manifestContractModule
2096
+ manifestContractModule,
2097
+ modules
2215
2098
  ] = await Promise.all([
2216
2099
  loadTypedBases(options.projectRoot),
2217
2100
  loadTypedScenarios(options.projectRoot, {}),
2218
2101
  loadGeneratedBaseStates(options.projectRoot),
2219
2102
  loadManifest(options.projectRoot),
2220
2103
  importTypeScriptModule(
2221
- path2.join(options.projectRoot, "app", "game.ts")
2104
+ path.join(options.projectRoot, "app", "game.ts")
2222
2105
  ),
2223
2106
  importTypeScriptModule(
2224
- path2.join(options.projectRoot, "shared", "manifest-contract.ts")
2225
- )
2107
+ path.join(options.projectRoot, "shared", "manifest-contract.ts")
2108
+ ),
2109
+ loadProjectReducerNativeModules(options.projectRoot)
2226
2110
  ]);
2227
2111
  const matchingScenarios = scenarios.filter(
2228
2112
  (scenario2) => scenario2.definition.id === options.scenarioId
@@ -2244,7 +2128,7 @@ async function createScenarioActionPlan(options) {
2244
2128
  const generatedBase = generatedBaseStates?.[baseStateKey(base.definition.id)];
2245
2129
  if (!generatedBase) {
2246
2130
  throw new Error(
2247
- `Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test generate' before replaying a scenario.`
2131
+ `Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test' before replaying a scenario.`
2248
2132
  );
2249
2133
  }
2250
2134
  validateGeneratedFingerprint({
@@ -2265,6 +2149,7 @@ async function createScenarioActionPlan(options) {
2265
2149
  basesById
2266
2150
  });
2267
2151
  const shadow = new ShadowReducerRuntime(
2152
+ await loadProjectReducerNativeModules(options.projectRoot),
2268
2153
  manifest,
2269
2154
  gameModule.default,
2270
2155
  createInitialTable,
@@ -2277,16 +2162,15 @@ async function createScenarioActionPlan(options) {
2277
2162
  const parentArtifact = generatedBaseStates?.[baseStateKey(base.definition.extends)];
2278
2163
  if (!parentArtifact || typeof parentArtifact.version !== "number") {
2279
2164
  throw new Error(
2280
- `Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing or stale. Run 'dreamboard test generate' first.`
2165
+ `Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing or stale. Run 'dreamboard test' first.`
2281
2166
  );
2282
2167
  }
2283
2168
  shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
2284
2169
  }
2285
2170
  const diagnostics = [];
2286
2171
  const context = await createScenarioContext({
2287
- runner: "reducer",
2288
2172
  shadow,
2289
- expect: (await loadTestingExpectApiFactory())(),
2173
+ expect: (await loadProjectReducerNativeModules(options.projectRoot)).createExpectApi(),
2290
2174
  actionPlan: {
2291
2175
  submitIndex: 0,
2292
2176
  diagnostics
@@ -2509,18 +2393,18 @@ function sortBasesForArtifacts(bases, basesById) {
2509
2393
  return ordered;
2510
2394
  }
2511
2395
  function writeProjectionSnapshots(options) {
2512
- const projectionDir = path2.join(
2396
+ const projectionDir = resolveWorkspacePath(
2513
2397
  options.projectRoot,
2514
- "test",
2515
- "generated",
2516
- "bases",
2517
- options.baseId
2398
+ `test/generated/bases/${sanitizeSnapshotSegment(options.baseId)}`
2518
2399
  );
2519
2400
  rmSync(projectionDir, { recursive: true, force: true });
2520
2401
  mkdirSync(projectionDir, { recursive: true });
2521
2402
  const projection = options.shadow.projectAllSeats();
2522
2403
  for (const [playerId, seatProjection] of Object.entries(projection.seats)) {
2523
- const outputPath = path2.join(projectionDir, `${playerId}.projection.json`);
2404
+ const outputPath = resolveWorkspacePath(
2405
+ options.projectRoot,
2406
+ `test/generated/bases/${sanitizeSnapshotSegment(options.baseId)}/${sanitizeSnapshotSegment(playerId)}.projection.json`
2407
+ );
2524
2408
  writeFileSync(
2525
2409
  outputPath,
2526
2410
  `${JSON.stringify(
@@ -2540,7 +2424,7 @@ function writeProjectionSnapshots(options) {
2540
2424
  function validateTypedScenarioBases(scenarios, basesById) {
2541
2425
  const available = new Set(basesById.keys());
2542
2426
  const invalid = scenarios.filter((scenario) => !available.has(scenario.definition.from)).map(
2543
- (scenario) => path2.relative(process.cwd(), scenario.filePath) + ` -> '${scenario.definition.from}'`
2427
+ (scenario) => path.relative(process.cwd(), scenario.filePath) + ` -> '${scenario.definition.from}'`
2544
2428
  );
2545
2429
  if (invalid.length > 0) {
2546
2430
  throw new Error(
@@ -2554,28 +2438,32 @@ async function writeReducerNativeGeneratedFiles(options) {
2554
2438
  const manifestHash = hashContent(JSON.stringify(manifest));
2555
2439
  const appBundleHash = hashContent(
2556
2440
  await bundleTypeScriptModuleText(
2557
- path2.join(options.projectRoot, "app", "game.ts")
2441
+ path.join(options.projectRoot, "app", "game.ts")
2558
2442
  )
2559
2443
  );
2560
2444
  const uiContractHash = hashContent(
2561
2445
  await bundleTypeScriptModuleText(
2562
- path2.join(options.projectRoot, "shared", "generated", "ui-contract.ts"),
2446
+ path.join(options.projectRoot, "shared", "generated", "ui-contract.ts"),
2563
2447
  { external: SDK_UI_RUNTIME_EXTERNALS }
2564
2448
  )
2565
2449
  );
2566
- const generatedDir = path2.join(options.projectRoot, "test", "generated");
2450
+ const generatedDir = resolveWorkspacePath(
2451
+ options.projectRoot,
2452
+ "test/generated"
2453
+ );
2567
2454
  await ensureDir(generatedDir);
2568
2455
  const baseStates = {};
2569
- const [gameModule, manifestContractModule] = await Promise.all([
2456
+ const [gameModule, manifestContractModule, modules] = await Promise.all([
2570
2457
  importTypeScriptModule(
2571
- path2.join(options.projectRoot, "app", "game.ts")
2458
+ path.join(options.projectRoot, "app", "game.ts")
2572
2459
  ),
2573
2460
  importTypeScriptModule(
2574
- path2.join(options.projectRoot, "shared", "manifest-contract.ts")
2575
- )
2461
+ path.join(options.projectRoot, "shared", "manifest-contract.ts")
2462
+ ),
2463
+ loadProjectReducerNativeModules(options.projectRoot)
2576
2464
  ]);
2577
2465
  const createInitialTable = typeof manifestContractModule.createInitialTable === "function" ? manifestContractModule.createInitialTable : null;
2578
- const contractFingerprintValue = contractFingerprint(
2466
+ const contractFingerprintValue = modules.contractFingerprint(
2579
2467
  gameModule.default
2580
2468
  ).value;
2581
2469
  const basesById = new Map(
@@ -2589,6 +2477,7 @@ async function writeReducerNativeGeneratedFiles(options) {
2589
2477
  basesById
2590
2478
  });
2591
2479
  const shadow = new ShadowReducerRuntime(
2480
+ modules,
2592
2481
  manifest,
2593
2482
  gameModule.default,
2594
2483
  createInitialTable,
@@ -2624,16 +2513,23 @@ async function writeReducerNativeGeneratedFiles(options) {
2624
2513
  shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
2625
2514
  }
2626
2515
  const context = await createScenarioContext({
2627
- runner: "reducer",
2628
2516
  shadow,
2629
- expect: (await loadTestingExpectApiFactory())()
2517
+ expect: modules.createExpectApi()
2630
2518
  });
2631
2519
  await context.game.start();
2632
- await base.definition.setup({
2633
- game: context.game,
2634
- players: context.players,
2635
- seat: context.seat
2636
- });
2520
+ try {
2521
+ await base.definition.setup({
2522
+ game: context.game,
2523
+ players: context.players,
2524
+ seat: context.seat
2525
+ });
2526
+ } catch (error) {
2527
+ const message = error instanceof Error ? error.message : String(error);
2528
+ throw new Error(
2529
+ `Base '${base.definition.id}' setup failed with setupProfileId '${effectiveSetup.setupProfileId ?? "none"}': ${message}`,
2530
+ { cause: error instanceof Error ? error : void 0 }
2531
+ );
2532
+ }
2637
2533
  baseStates[baseStateKey(base.definition.id)] = {
2638
2534
  key: baseStateKey(base.definition.id),
2639
2535
  base: base.definition.id,
@@ -2661,42 +2557,48 @@ async function writeReducerNativeGeneratedFiles(options) {
2661
2557
  shadow
2662
2558
  });
2663
2559
  }
2664
- const header = "// Generated by dreamboard test generate. Do not edit by hand.\n";
2665
- await writeTextFile(
2666
- path2.join(generatedDir, "base-states.generated.ts"),
2560
+ const header = "// Generated by dreamboard test. Do not edit by hand.\n";
2561
+ await writeWorkspaceTextFile(
2562
+ options.projectRoot,
2563
+ "test/generated/base-states.generated.ts",
2667
2564
  `${header}export const BASE_STATES = ${JSON.stringify(baseStates, null, 2)} as const;
2668
2565
  export const BASE_STATES_CONTRACT_FINGERPRINT = ${JSON.stringify(contractFingerprintValue)};
2669
2566
  `
2670
2567
  );
2671
- await writeTextFile(
2672
- path2.join(generatedDir, "base-states.generated.d.ts"),
2568
+ await writeWorkspaceTextFile(
2569
+ options.projectRoot,
2570
+ "test/generated/base-states.generated.d.ts",
2673
2571
  `${header}export declare const BASE_STATES: Record<string, unknown>;
2674
2572
  export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
2675
2573
  `
2676
2574
  );
2677
- await writeTextFile(
2678
- path2.join(generatedDir, "scenario-manifest.generated.ts"),
2575
+ await writeWorkspaceTextFile(
2576
+ options.projectRoot,
2577
+ "test/generated/scenario-manifest.generated.ts",
2679
2578
  `${header}export const SCENARIO_MANIFEST = ${JSON.stringify(
2680
2579
  options.scenarios.map((scenario) => ({
2681
2580
  id: scenario.definition.id,
2682
- filePath: path2.relative(generatedDir, scenario.filePath),
2683
- base: scenario.definition.from,
2684
- runners: normalizeScenarioRunners(scenario.definition.runners)
2581
+ filePath: path.relative(generatedDir, scenario.filePath),
2582
+ base: scenario.definition.from
2685
2583
  })),
2686
2584
  null,
2687
2585
  2
2688
2586
  )} as const;
2689
2587
  `
2690
2588
  );
2691
- await writeJsonFile(path2.join(generatedDir, ".generation-meta.json"), {
2692
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2693
- contractFingerprint: contractFingerprintValue,
2694
- scenarioCount: options.scenarios.length,
2695
- baseStateCount: options.bases.length
2696
- });
2589
+ await writeWorkspaceJsonFile(
2590
+ options.projectRoot,
2591
+ "test/generated/.generation-meta.json",
2592
+ {
2593
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2594
+ contractFingerprint: contractFingerprintValue,
2595
+ scenarioCount: options.scenarios.length,
2596
+ baseStateCount: options.bases.length
2597
+ }
2598
+ );
2697
2599
  }
2698
2600
  async function loadGeneratedBaseStates(projectRoot) {
2699
- const filePath = path2.join(
2601
+ const filePath = path.join(
2700
2602
  projectRoot,
2701
2603
  "test",
2702
2604
  "generated",
@@ -2709,7 +2611,7 @@ async function loadGeneratedBaseStates(projectRoot) {
2709
2611
  return module.BASE_STATES ?? null;
2710
2612
  }
2711
2613
  async function loadGeneratedScenarioManifest(projectRoot) {
2712
- const filePath = path2.join(
2614
+ const filePath = path.join(
2713
2615
  projectRoot,
2714
2616
  "test",
2715
2617
  "generated",
@@ -2734,7 +2636,7 @@ function validateGeneratedFingerprint(options) {
2734
2636
  const mismatches = [];
2735
2637
  if (options.generated.contractFingerprint && options.current.contractFingerprint && options.generated.contractFingerprint !== options.current.contractFingerprint) {
2736
2638
  const error = new Error(
2737
- `Base states were generated for contract ${options.generated.contractFingerprint} but the current contract is ${options.current.contractFingerprint}. Remedy: run \`dreamboard test generate\`, then re-run the tests.`
2639
+ `Base states were generated for contract ${options.generated.contractFingerprint} but the current contract is ${options.current.contractFingerprint}. Remedy: run \`dreamboard test\`, then re-run the tests.`
2738
2640
  );
2739
2641
  error.code = STALE_CONTRACT_ARTIFACT_CODE;
2740
2642
  throw error;
@@ -2765,16 +2667,19 @@ function validateGeneratedFingerprint(options) {
2765
2667
  }
2766
2668
  if (mismatches.length > 0) {
2767
2669
  throw new Error(
2768
- `${mismatches.join("; ")}. Run 'dreamboard test generate' to refresh reducer-native base artifacts.`
2670
+ `${mismatches.join("; ")}. Run 'dreamboard test' to refresh reducer-native base artifacts.`
2769
2671
  );
2770
2672
  }
2771
2673
  }
2772
2674
  async function currentFingerprint(options) {
2773
2675
  const manifest = await loadManifest(options.projectRoot);
2774
- const gameModule = await importTypeScriptModule(
2775
- path2.join(options.projectRoot, "app", "game.ts")
2776
- );
2777
- const contractFingerprintValue = contractFingerprint(
2676
+ const [gameModule, modules] = await Promise.all([
2677
+ importTypeScriptModule(
2678
+ path.join(options.projectRoot, "app", "game.ts")
2679
+ ),
2680
+ loadProjectReducerNativeModules(options.projectRoot)
2681
+ ]);
2682
+ const contractFingerprintValue = modules.contractFingerprint(
2778
2683
  gameModule.default
2779
2684
  ).value;
2780
2685
  const resolvedBase = resolveBaseDefinition(
@@ -2812,12 +2717,12 @@ async function currentFingerprint(options) {
2812
2717
  manifestHash: hashContent(JSON.stringify(manifest)),
2813
2718
  appBundleHash: hashContent(
2814
2719
  await bundleTypeScriptModuleText(
2815
- path2.join(options.projectRoot, "app", "game.ts")
2720
+ path.join(options.projectRoot, "app", "game.ts")
2816
2721
  )
2817
2722
  ),
2818
2723
  uiContractHash: hashContent(
2819
2724
  await bundleTypeScriptModuleText(
2820
- path2.join(options.projectRoot, "shared", "generated", "ui-contract.ts"),
2725
+ path.join(options.projectRoot, "shared", "generated", "ui-contract.ts"),
2821
2726
  { external: SDK_UI_RUNTIME_EXTERNALS }
2822
2727
  )
2823
2728
  ),
@@ -2857,7 +2762,8 @@ async function runReducerNativeScenarios(options) {
2857
2762
  initialGeneratedBaseStates,
2858
2763
  manifest,
2859
2764
  gameModule,
2860
- manifestContractModule
2765
+ manifestContractModule,
2766
+ modules
2861
2767
  ] = await Promise.all([
2862
2768
  loadTypedBases(options.projectRoot),
2863
2769
  loadTypedScenarios(options.projectRoot, {
@@ -2866,11 +2772,12 @@ async function runReducerNativeScenarios(options) {
2866
2772
  loadGeneratedBaseStates(options.projectRoot),
2867
2773
  loadManifest(options.projectRoot),
2868
2774
  importTypeScriptModule(
2869
- path2.join(options.projectRoot, "app", "game.ts")
2775
+ path.join(options.projectRoot, "app", "game.ts")
2870
2776
  ),
2871
2777
  importTypeScriptModule(
2872
- path2.join(options.projectRoot, "shared", "manifest-contract.ts")
2873
- )
2778
+ path.join(options.projectRoot, "shared", "manifest-contract.ts")
2779
+ ),
2780
+ loadProjectReducerNativeModules(options.projectRoot)
2874
2781
  ]);
2875
2782
  let generatedBaseStates = initialGeneratedBaseStates;
2876
2783
  const createInitialTable = typeof manifestContractModule.createInitialTable === "function" ? manifestContractModule.createInitialTable : null;
@@ -2887,251 +2794,121 @@ async function runReducerNativeScenarios(options) {
2887
2794
  });
2888
2795
  generatedBaseStates = await loadGeneratedBaseStates(options.projectRoot);
2889
2796
  }
2890
- const runnerScenarios = scenarios.filter(
2891
- (scenario) => normalizeScenarioRunners(scenario.definition.runners).includes(
2892
- options.runner
2893
- )
2894
- );
2895
- let browser = null;
2896
- let page = null;
2897
- let browserBridge = null;
2898
- let browserDriver = null;
2899
- if (options.runner === "browser") {
2900
- const webBaseUrl = options.webBaseUrl ?? options.projectConfig.webBaseUrl;
2901
- if (!webBaseUrl) {
2797
+ let passed = 0;
2798
+ let failed = 0;
2799
+ const results = [];
2800
+ for (const scenario of scenarios) {
2801
+ const base = basesById.get(scenario.definition.from);
2802
+ if (!base) {
2902
2803
  throw new Error(
2903
- "Browser runner requires a local webBaseUrl. Start the local web stack and configure the project for env local."
2804
+ `Missing typed base '${scenario.definition.from}' for scenario '${scenario.definition.id}'.`
2904
2805
  );
2905
2806
  }
2906
- browserDriver = await loadBrowserDriver(options.projectRoot);
2907
- const opened = await openBrowserPage(
2908
- `${webBaseUrl}/_dev`,
2909
- options.resolvedConfig
2910
- );
2911
- browser = opened.browser;
2912
- page = opened.page;
2913
- }
2914
- try {
2915
- let passed = 0;
2916
- let failed = 0;
2917
- const results = [];
2918
- for (const scenario of runnerScenarios) {
2919
- const base = basesById.get(scenario.definition.from);
2920
- if (!base) {
2921
- throw new Error(
2922
- `Missing typed base '${scenario.definition.from}' for scenario '${scenario.definition.id}'.`
2923
- );
2807
+ if (generatedBaseStates?.[baseStateKey(base.definition.id)]) {
2808
+ validateGeneratedFingerprint({
2809
+ generated: generatedBaseStates[baseStateKey(base.definition.id)].fingerprint,
2810
+ current: await currentFingerprint({
2811
+ projectRoot: options.projectRoot,
2812
+ base,
2813
+ basesById,
2814
+ compiledResultId: options.compiledResultId,
2815
+ gameId: options.gameId
2816
+ })
2817
+ });
2818
+ }
2819
+ try {
2820
+ const resolvedBase = resolveBaseDefinition(base, basesById);
2821
+ const effectiveSetup = resolveEffectiveBaseSetup({
2822
+ manifest,
2823
+ base: base.definition,
2824
+ basesById
2825
+ });
2826
+ const shadow = new ShadowReducerRuntime(
2827
+ modules,
2828
+ manifest,
2829
+ gameModule.default,
2830
+ createInitialTable,
2831
+ resolvedBase.seed,
2832
+ resolvedBase.players,
2833
+ effectiveSetup.setupProfileId,
2834
+ options.debug ?? false
2835
+ );
2836
+ if (base.definition.extends) {
2837
+ const parentArtifact = generatedBaseStates?.[baseStateKey(base.definition.extends)];
2838
+ if (!parentArtifact) {
2839
+ throw new Error(
2840
+ `Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing. Run 'dreamboard test' first.`
2841
+ );
2842
+ }
2843
+ shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
2924
2844
  }
2925
- if (generatedBaseStates?.[baseStateKey(base.definition.id)]) {
2926
- validateGeneratedFingerprint({
2927
- generated: generatedBaseStates[baseStateKey(base.definition.id)].fingerprint,
2928
- current: await currentFingerprint({
2845
+ const context = await createScenarioContext({
2846
+ shadow,
2847
+ expect: modules.createExpectApi({
2848
+ matchSnapshot: createScenarioSnapshotMatcher({
2929
2849
  projectRoot: options.projectRoot,
2930
- base,
2931
- basesById,
2932
- compiledResultId: options.compiledResultId,
2933
- gameId: options.gameId
2850
+ scenarioId: scenario.definition.id,
2851
+ updateSnapshots: options.updateSnapshots ?? false
2934
2852
  })
2935
- });
2936
- } else if (options.runner !== "reducer") {
2853
+ })
2854
+ });
2855
+ await context.game.start();
2856
+ await base.definition.setup({
2857
+ game: context.game,
2858
+ players: context.players,
2859
+ seat: context.seat
2860
+ });
2861
+ shadow.clearHistory();
2862
+ await scenario.definition.when(context);
2863
+ if (scenario.definition.phase && shadow.phase() !== scenario.definition.phase) {
2937
2864
  throw new Error(
2938
- "Missing reducer-native generated base artifacts. Run 'dreamboard test generate' first."
2865
+ `Scenario '${scenario.definition.id}' expected phase '${scenario.definition.phase}' but reached '${shadow.phase()}'.`
2939
2866
  );
2940
2867
  }
2941
- try {
2942
- const resolvedBase = resolveBaseDefinition(base, basesById);
2943
- const effectiveSetup = resolveEffectiveBaseSetup({
2944
- manifest,
2945
- base: base.definition,
2946
- basesById
2947
- });
2948
- const shadow = new ShadowReducerRuntime(
2949
- manifest,
2950
- gameModule.default,
2951
- createInitialTable,
2952
- resolvedBase.seed,
2953
- resolvedBase.players,
2954
- effectiveSetup.setupProfileId,
2955
- options.debug ?? false
2868
+ if (scenario.definition.stage && shadow.currentStage() !== scenario.definition.stage) {
2869
+ throw new Error(
2870
+ `Scenario '${scenario.definition.id}' expected stage '${scenario.definition.stage}' but reached '${shadow.currentStage() ?? "null"}'.`
2956
2871
  );
2957
- if (base.definition.extends) {
2958
- const parentArtifact = generatedBaseStates?.[baseStateKey(base.definition.extends)];
2959
- if (!parentArtifact) {
2960
- throw new Error(
2961
- `Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing. Run 'dreamboard test generate' first.`
2962
- );
2963
- }
2964
- shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
2965
- }
2966
- let remote;
2967
- if (options.runner === "remote") {
2968
- const compiledResultId = options.compiledResultId ?? options.projectConfig.compile?.latestSuccessful?.resultId;
2969
- if (!compiledResultId) {
2970
- throw new Error(
2971
- "Remote runner requires a compiled result. Compile the workspace first."
2972
- );
2973
- }
2974
- const {
2975
- data: session,
2976
- error: sessionError,
2977
- response: sessionResponse
2978
- } = await createProjectSession({
2979
- path: { projectId: options.projectConfig.projectId },
2980
- body: {
2981
- compiledResultId,
2982
- seed: resolvedBase.seed,
2983
- playerCount: resolvedBase.players,
2984
- autoAssignSeats: true,
2985
- setupProfileId: effectiveSetup.setupProfileId ?? void 0
2986
- }
2987
- });
2988
- if (!session || sessionError) {
2989
- throw toDreamboardApiError(
2990
- sessionError,
2991
- sessionResponse,
2992
- "Failed to create remote-runner session."
2993
- );
2994
- }
2995
- const tracker = new RemoteGameplayTracker(
2996
- session.sessionId,
2997
- "player-1"
2998
- );
2999
- const { error: startError, response: startResponse } = await startGame({
3000
- path: { sessionId: session.sessionId }
3001
- });
3002
- if (startError) {
3003
- throw toDreamboardApiError(
3004
- startError,
3005
- startResponse,
3006
- "Failed to start remote-runner session."
3007
- );
3008
- }
3009
- await tracker.bootstrap();
3010
- remote = {
3011
- sessionId: session.sessionId,
3012
- tracker
3013
- };
3014
- }
3015
- if (options.runner === "browser" && page) {
3016
- const compiledResultId = options.compiledResultId ?? options.projectConfig.compile?.latestSuccessful?.resultId;
3017
- if (!compiledResultId) {
3018
- throw new Error(
3019
- "Browser runner requires a compiled result. Compile the workspace first."
3020
- );
3021
- }
3022
- const {
3023
- data: session,
3024
- error: sessionError,
3025
- response: sessionResponse
3026
- } = await createProjectSession({
3027
- path: { projectId: options.projectConfig.projectId },
3028
- body: {
3029
- compiledResultId,
3030
- seed: resolvedBase.seed,
3031
- playerCount: resolvedBase.players,
3032
- autoAssignSeats: true,
3033
- setupProfileId: effectiveSetup.setupProfileId ?? void 0
3034
- }
3035
- });
3036
- if (!session || sessionError) {
3037
- throw toDreamboardApiError(
3038
- sessionError,
3039
- sessionResponse,
3040
- "Failed to create browser-runner session."
3041
- );
3042
- }
3043
- const { error: startError, data: started } = await startGame({
3044
- path: { sessionId: session.sessionId }
3045
- });
3046
- if (startError || !started) {
3047
- throw new Error("Failed to start browser-runner session.");
3048
- }
3049
- await page.goto(
3050
- `${options.webBaseUrl ?? options.projectConfig.webBaseUrl}/_dev/play/${started.context.shortCode}`,
3051
- { waitUntil: "domcontentloaded" }
3052
- );
3053
- await waitForGameReady(page);
3054
- browserBridge = await createBrowserBridgeClient(page);
3055
- await browserDriver?.onReady?.(browserBridge);
3056
- }
3057
- const context = await createScenarioContext({
3058
- runner: options.runner,
3059
- shadow,
3060
- expect: (await loadTestingExpectApiFactory())({
3061
- matchSnapshot: createScenarioSnapshotMatcher({
3062
- projectRoot: options.projectRoot,
3063
- scenarioId: scenario.definition.id,
3064
- updateSnapshots: options.updateSnapshots ?? false
3065
- })
3066
- }),
3067
- remote,
3068
- browser: options.runner === "browser" && browserBridge ? {
3069
- bridge: browserBridge,
3070
- driver: browserDriver
3071
- } : void 0
3072
- });
3073
- await context.game.start();
3074
- await base.definition.setup({
3075
- game: context.game,
3076
- players: context.players,
3077
- seat: context.seat
3078
- });
3079
- shadow.clearHistory();
3080
- await scenario.definition.when(context);
3081
- if (scenario.definition.phase && shadow.phase() !== scenario.definition.phase) {
3082
- throw new Error(
3083
- `Scenario '${scenario.definition.id}' expected phase '${scenario.definition.phase}' but reached '${shadow.phase()}'.`
3084
- );
3085
- }
3086
- if (scenario.definition.stage && shadow.currentStage() !== scenario.definition.stage) {
3087
- throw new Error(
3088
- `Scenario '${scenario.definition.id}' expected stage '${scenario.definition.stage}' but reached '${shadow.currentStage() ?? "null"}'.`
3089
- );
3090
- }
3091
- await scenario.definition.then(context);
3092
- passed += 1;
3093
- results.push({
3094
- id: scenario.definition.id,
3095
- success: true
3096
- });
3097
- } catch (error) {
3098
- failed += 1;
3099
- results.push({
3100
- id: scenario.definition.id,
3101
- success: false,
3102
- errorCode: isStaleContractArtifactError(error) ? STALE_CONTRACT_ARTIFACT_CODE : void 0,
3103
- error: error instanceof Error ? formatScenarioErrorForDisplay({
3104
- error,
3105
- projectRoot: options.projectRoot,
3106
- scenarioFilePath: scenario.filePath
3107
- }) : `Scenario '${scenario.definition.id}' failed.`
3108
- });
3109
2872
  }
3110
- }
3111
- return { passed, failed, results };
3112
- } finally {
3113
- if (browser) {
3114
- await browser.close();
2873
+ await scenario.definition.then(context);
2874
+ passed += 1;
2875
+ results.push({
2876
+ id: scenario.definition.id,
2877
+ success: true
2878
+ });
2879
+ } catch (error) {
2880
+ failed += 1;
2881
+ results.push({
2882
+ id: scenario.definition.id,
2883
+ success: false,
2884
+ errorCode: isStaleContractArtifactError(error) ? STALE_CONTRACT_ARTIFACT_CODE : void 0,
2885
+ error: error instanceof Error ? formatScenarioErrorForDisplay({
2886
+ error,
2887
+ projectRoot: options.projectRoot,
2888
+ scenarioFilePath: scenario.filePath
2889
+ }) : `Scenario '${scenario.definition.id}' failed.`
2890
+ });
2891
+ } finally {
3115
2892
  }
3116
2893
  }
2894
+ return { passed, failed, results };
3117
2895
  }
3118
-
3119
2896
  export {
2897
+ assertDispatchResultWireContract,
2898
+ createActionPlanReplaySession,
2899
+ createScenarioActionPlan,
2900
+ createSessionFromScenario,
2901
+ ensureReducerNativeTestingFiles,
3120
2902
  formatScenarioErrorForDisplay,
2903
+ generateReducerNativeArtifacts,
3121
2904
  isReducerNativeTestingWorkspace,
3122
- ensureReducerNativeTestingFiles,
3123
2905
  loadTypedBases,
3124
2906
  loadTypedScenarios,
3125
- assertDispatchResultWireContract,
3126
- createSessionFromScenario,
3127
2907
  materializeScenarioReducerState,
3128
- replayScenarioThroughBackend,
3129
- createScenarioActionPlan,
3130
- replayActionPlanThroughBackend,
3131
- createActionPlanReplaySession,
3132
2908
  replayActionPlanInSession,
3133
- writeReducerNativeGeneratedFiles,
3134
- generateReducerNativeArtifacts,
3135
- runReducerNativeScenarios
2909
+ replayActionPlanThroughBackend,
2910
+ replayScenarioThroughBackend,
2911
+ runReducerNativeScenarios,
2912
+ writeReducerNativeGeneratedFiles
3136
2913
  };
3137
- //# sourceMappingURL=chunk-E7SSWJXJ.mjs.map
2914
+ //# sourceMappingURL=reducer-native-test-harness-UFMSNNDY.mjs.map