@dreamboard-games/cli 0.1.30-alpha.12 → 0.1.30-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -6
- package/dist/agent-verifier/agent-workspace-verifier.mjs +18 -17
- package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -1
- package/dist/agent-verifier/{chunk-VLOIZDR6.mjs → chunk-4BECKTAF.mjs} +199 -190
- package/dist/agent-verifier/chunk-4BECKTAF.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-TLYGTHXU.mjs → chunk-5GCZZ6NW.mjs} +3 -3
- package/dist/agent-verifier/{chunk-YR664DJX.mjs → chunk-A67WUYN2.mjs} +42 -68
- package/dist/agent-verifier/chunk-A67WUYN2.mjs.map +1 -0
- package/dist/agent-verifier/chunk-AXXUGU7Q.mjs +255 -0
- package/dist/agent-verifier/chunk-AXXUGU7Q.mjs.map +1 -0
- package/dist/agent-verifier/chunk-CO3BRUD6.mjs +342 -0
- package/dist/agent-verifier/chunk-CO3BRUD6.mjs.map +1 -0
- package/dist/agent-verifier/chunk-DPYC2NDB.mjs +59 -0
- package/dist/agent-verifier/chunk-DPYC2NDB.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-4GU3PCHV.mjs → chunk-DWLTCUUX.mjs} +576 -393
- package/dist/agent-verifier/chunk-DWLTCUUX.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-COB56ESI.mjs → chunk-G2ECODRB.mjs} +2 -2
- package/dist/agent-verifier/{chunk-6XRC5PWB.mjs → chunk-H3XNWKJU.mjs} +217 -232
- package/dist/agent-verifier/chunk-H3XNWKJU.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-Z6OZWUIZ.mjs → chunk-HLHT57AW.mjs} +64 -16
- package/dist/agent-verifier/chunk-HLHT57AW.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-YDIOW2BO.mjs → chunk-INIK6LHK.mjs} +2 -2
- package/dist/agent-verifier/{chunk-UWJIZML3.mjs → chunk-LKQ557TJ.mjs} +30 -23
- package/dist/agent-verifier/chunk-LKQ557TJ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-NAK77WXW.mjs → chunk-MYMVXTZT.mjs} +4 -5
- package/dist/agent-verifier/chunk-MYMVXTZT.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-UIJ2NDG6.mjs → chunk-NFL3Z4Z7.mjs} +31 -238
- package/dist/agent-verifier/chunk-NFL3Z4Z7.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-XKCJBIRY.mjs → chunk-QD4SQNUP.mjs} +2 -2
- package/dist/agent-verifier/{chunk-IAYRNVUC.mjs → chunk-RDYXWXXC.mjs} +1 -3
- package/dist/agent-verifier/{chunk-QBAF7EYR.mjs → chunk-TTB7AIHZ.mjs} +4 -4
- package/dist/agent-verifier/{chunk-QBAF7EYR.mjs.map → chunk-TTB7AIHZ.mjs.map} +1 -1
- package/dist/agent-verifier/chunk-V6AQDR7W.mjs +89 -0
- package/dist/agent-verifier/chunk-V6AQDR7W.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-RHI6S4SU.mjs → chunk-V7ABTZXW.mjs} +1 -3
- package/dist/agent-verifier/{chunk-RHI6S4SU.mjs.map → chunk-V7ABTZXW.mjs.map} +1 -1
- package/dist/agent-verifier/chunk-WAFBU5U7.mjs +467 -0
- package/dist/agent-verifier/chunk-WAFBU5U7.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-3IJBOLGT.mjs → chunk-WSIYUUSD.mjs} +2 -2
- package/dist/agent-verifier/{compile-WZ7X6I2A.mjs → compile-6G6GENLP.mjs} +22 -18
- package/dist/agent-verifier/compile-6G6GENLP.mjs.map +1 -0
- package/dist/agent-verifier/{global-config-XHL7BCKN.mjs → global-config-6UGFPLDA.mjs} +4 -3
- package/dist/agent-verifier/{keychain-backend-A3MRWLPF.mjs → keychain-backend-BQLW5VEC.mjs} +11 -6
- package/dist/agent-verifier/keychain-backend-BQLW5VEC.mjs.map +1 -0
- package/dist/agent-verifier/{local-files-ZW52HSVT.mjs → local-files-WPHUV6GU.mjs} +6 -6
- package/dist/agent-verifier/{materialize-workspace-BKZLLFI4.mjs → materialize-workspace-S24JA46C.mjs} +17 -17
- package/dist/agent-verifier/materialize-workspace-S24JA46C.mjs.map +1 -0
- package/dist/agent-verifier/{reducer-bundle-preflight-7NYZF5ZT.mjs → reducer-bundle-preflight-3DSXIELT.mjs} +4 -4
- package/dist/agent-verifier/reducer-contract-preflight-FQB7M4PU.mjs +11 -0
- package/dist/agent-verifier/{reducer-native-test-harness-D4VWPIAC.mjs → reducer-native-test-harness-GY2CCQWN.mjs} +12 -9
- package/dist/agent-verifier/{static-scaffold-JCRBDKEH.mjs → static-scaffold-5YD6QHIS.mjs} +7 -9
- package/dist/agent-verifier/{sync-ELLJEWMB.mjs → sync-3OZBFABA.mjs} +24 -22
- package/dist/agent-verifier/{sync-ELLJEWMB.mjs.map → sync-3OZBFABA.mjs.map} +1 -1
- package/dist/agent-verifier/{test-OSXBPLSP.mjs → test-LQAGEQLY.mjs} +19 -17
- package/dist/agent-verifier/test-LQAGEQLY.mjs.map +1 -0
- package/dist/agent-verifier/{workspace-codegen-WPZHMATU.mjs → workspace-codegen-4IWICKLB.mjs} +3 -3
- package/dist/agent-verifier/{workspace-dependencies-ULZZZPNX.mjs → workspace-dependencies-ZMHPHVQV.mjs} +2 -2
- package/dist/authoring-compatibility-internal.js +12 -0
- package/dist/{agent-verifier/chunk-W2MDP5ZN.mjs → chunk-AVOAT522.js} +118 -21
- package/dist/chunk-AVOAT522.js.map +1 -0
- package/dist/chunk-FFO2IJL3.js +204 -0
- package/dist/chunk-FFO2IJL3.js.map +1 -0
- package/dist/chunk-JMV5ZFMK.js +4298 -0
- package/dist/chunk-JMV5ZFMK.js.map +1 -0
- package/dist/{chunk-P5TITCD3.js → chunk-JSF7PMIF.js} +2240 -4554
- package/dist/chunk-JSF7PMIF.js.map +1 -0
- package/dist/{global-config-WPJRXVDO.js → global-config-NLGAFSRU.js} +3 -2
- package/dist/global-config-NLGAFSRU.js.map +1 -0
- package/dist/index.js +1371 -3545
- package/dist/index.js.map +1 -1
- package/dist/internal.js +14 -8
- package/dist/{keychain-backend-JHTXAKWC.js → keychain-backend-47LZ5IX5.js} +11 -6
- package/dist/keychain-backend-47LZ5IX5.js.map +1 -0
- package/package.json +9 -19
- package/release/authoring-release-set.json +38 -0
- package/skills/dreamboard/references/manifest-authoring.md +11 -3
- package/dist/agent-verifier/chunk-4GU3PCHV.mjs.map +0 -1
- package/dist/agent-verifier/chunk-6XRC5PWB.mjs.map +0 -1
- package/dist/agent-verifier/chunk-G42BGGG2.mjs +0 -70
- package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +0 -1
- package/dist/agent-verifier/chunk-KK47X7RV.mjs +0 -14
- package/dist/agent-verifier/chunk-KK47X7RV.mjs.map +0 -1
- package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +0 -1
- package/dist/agent-verifier/chunk-UIJ2NDG6.mjs.map +0 -1
- package/dist/agent-verifier/chunk-UWJIZML3.mjs.map +0 -1
- package/dist/agent-verifier/chunk-VLOIZDR6.mjs.map +0 -1
- package/dist/agent-verifier/chunk-W2MDP5ZN.mjs.map +0 -1
- package/dist/agent-verifier/chunk-YR664DJX.mjs.map +0 -1
- package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +0 -1
- package/dist/agent-verifier/compile-WZ7X6I2A.mjs.map +0 -1
- package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +0 -1
- package/dist/agent-verifier/materialize-workspace-BKZLLFI4.mjs.map +0 -1
- package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +0 -11
- package/dist/agent-verifier/test-OSXBPLSP.mjs.map +0 -1
- package/dist/chunk-GXM7RRZJ.js +0 -433
- package/dist/chunk-GXM7RRZJ.js.map +0 -1
- package/dist/chunk-P5TITCD3.js.map +0 -1
- package/dist/dev-host/components/drawer.tsx +0 -132
- package/dist/dev-host/components/input.tsx +0 -21
- package/dist/dev-host/dev-api-proxy-plugin.ts +0 -328
- package/dist/dev-host/dev-author-dom-warnings.ts +0 -100
- package/dist/dev-host/dev-diagnostics.ts +0 -62
- package/dist/dev-host/dev-fallback-stylesheet.ts +0 -53
- package/dist/dev-host/dev-hmr-guard-plugin.ts +0 -47
- package/dist/dev-host/dev-host-controller.ts +0 -674
- package/dist/dev-host/dev-host-player-query.ts +0 -17
- package/dist/dev-host/dev-host-session-transport.ts +0 -52
- package/dist/dev-host/dev-host-storage.ts +0 -56
- package/dist/dev-host/dev-log-relay-plugin.ts +0 -510
- package/dist/dev-host/dev-runtime-config.ts +0 -14
- package/dist/dev-host/dev-runtime-platform.ts +0 -335
- package/dist/dev-host/dev-virtual-modules-plugin.ts +0 -64
- package/dist/dev-host/host-main.css +0 -224
- package/dist/dev-host/host-main.tsx +0 -954
- package/dist/dev-host/index.html +0 -56
- package/dist/dev-host/lib/utils.ts +0 -6
- package/dist/dev-host/plugin-main.ts +0 -61
- package/dist/dev-host/plugin.html +0 -24
- package/dist/dev-host/shared-styles.css +0 -144
- package/dist/dev-host/start-dev-server.ts +0 -140
- package/dist/dev-host/virtual-modules.d.ts +0 -27
- package/dist/keychain-backend-JHTXAKWC.js.map +0 -1
- /package/dist/agent-verifier/{chunk-TLYGTHXU.mjs.map → chunk-5GCZZ6NW.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-COB56ESI.mjs.map → chunk-G2ECODRB.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-YDIOW2BO.mjs.map → chunk-INIK6LHK.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-XKCJBIRY.mjs.map → chunk-QD4SQNUP.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-IAYRNVUC.mjs.map → chunk-RDYXWXXC.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-3IJBOLGT.mjs.map → chunk-WSIYUUSD.mjs.map} +0 -0
- /package/dist/agent-verifier/{global-config-XHL7BCKN.mjs.map → global-config-6UGFPLDA.mjs.map} +0 -0
- /package/dist/agent-verifier/{local-files-ZW52HSVT.mjs.map → local-files-WPHUV6GU.mjs.map} +0 -0
- /package/dist/agent-verifier/{reducer-bundle-preflight-7NYZF5ZT.mjs.map → reducer-bundle-preflight-3DSXIELT.mjs.map} +0 -0
- /package/dist/agent-verifier/{reducer-contract-preflight-COD2CO22.mjs.map → reducer-contract-preflight-FQB7M4PU.mjs.map} +0 -0
- /package/dist/agent-verifier/{reducer-native-test-harness-D4VWPIAC.mjs.map → reducer-native-test-harness-GY2CCQWN.mjs.map} +0 -0
- /package/dist/agent-verifier/{static-scaffold-JCRBDKEH.mjs.map → static-scaffold-5YD6QHIS.mjs.map} +0 -0
- /package/dist/agent-verifier/{workspace-codegen-WPZHMATU.mjs.map → workspace-codegen-4IWICKLB.mjs.map} +0 -0
- /package/dist/agent-verifier/{workspace-dependencies-ULZZZPNX.mjs.map → workspace-dependencies-ZMHPHVQV.mjs.map} +0 -0
- /package/dist/{global-config-WPJRXVDO.js.map → authoring-compatibility-internal.js.map} +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.framework.json +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/app/tsconfig.json +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/ui/index.tsx +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/ui/style.css +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.framework.json +0 -0
- /package/{dist/scaffold → scaffold}/assets/static/ui/tsconfig.json +0 -0
|
@@ -2,19 +2,28 @@
|
|
|
2
2
|
import {
|
|
3
3
|
bundleTypeScriptModuleText,
|
|
4
4
|
importTypeScriptModule
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-QD4SQNUP.mjs";
|
|
6
6
|
import {
|
|
7
7
|
STALE_CONTRACT_ARTIFACT_CODE,
|
|
8
8
|
isStaleContractArtifactError,
|
|
9
9
|
toDreamboardApiError
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-G2ECODRB.mjs";
|
|
11
|
+
import {
|
|
12
|
+
createUserTokenManager,
|
|
13
|
+
resolveLocalHarnessAccessToken
|
|
14
|
+
} from "./chunk-CO3BRUD6.mjs";
|
|
11
15
|
import {
|
|
12
16
|
loadManifest
|
|
13
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-LKQ557TJ.mjs";
|
|
14
18
|
import {
|
|
15
19
|
REDUCER_TESTING_TYPES_WRAPPER_CONTENT,
|
|
16
20
|
buildReducerTestingContractContent
|
|
17
21
|
} from "./chunk-F2DIOJJZ.mjs";
|
|
22
|
+
import {
|
|
23
|
+
ensureDir,
|
|
24
|
+
exists,
|
|
25
|
+
writeJsonFile
|
|
26
|
+
} from "./chunk-RDYXWXXC.mjs";
|
|
18
27
|
import {
|
|
19
28
|
createGameplayCapability,
|
|
20
29
|
createProjectSession,
|
|
@@ -22,24 +31,26 @@ import {
|
|
|
22
31
|
getSessionSnapshot,
|
|
23
32
|
hashContent,
|
|
24
33
|
startGame
|
|
25
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-H3XNWKJU.mjs";
|
|
26
35
|
import {
|
|
27
36
|
external_exports
|
|
28
37
|
} from "./chunk-JZTH3EMV.mjs";
|
|
29
38
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
} from "./chunk-
|
|
39
|
+
readWorkspaceTextFileIfExists,
|
|
40
|
+
resolveWorkspacePath,
|
|
41
|
+
workspacePathExists,
|
|
42
|
+
writeWorkspaceJsonFile,
|
|
43
|
+
writeWorkspaceTextFile
|
|
44
|
+
} from "./chunk-WAFBU5U7.mjs";
|
|
36
45
|
import {
|
|
37
46
|
PROJECT_DIR_NAME
|
|
38
47
|
} from "./chunk-M7UVBANQ.mjs";
|
|
39
48
|
|
|
40
49
|
// src/services/testing/reducer-native-test-harness.ts
|
|
41
|
-
import
|
|
50
|
+
import path3 from "path";
|
|
42
51
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
52
|
+
import { createRequire as createRequire2 } from "module";
|
|
53
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
43
54
|
import {
|
|
44
55
|
existsSync,
|
|
45
56
|
mkdirSync,
|
|
@@ -49,14 +60,35 @@ import {
|
|
|
49
60
|
} from "fs";
|
|
50
61
|
import { readdir } from "fs/promises";
|
|
51
62
|
import { isDeepStrictEqual } from "util";
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
|
|
64
|
+
// src/utils/dev-session.ts
|
|
65
|
+
import { randomInt } from "crypto";
|
|
66
|
+
var MIN_SAFE_SEED = BigInt(Number.MIN_SAFE_INTEGER);
|
|
67
|
+
var MAX_SAFE_SEED = BigInt(Number.MAX_SAFE_INTEGER);
|
|
68
|
+
function createPersistedDevSession(input) {
|
|
69
|
+
return {
|
|
70
|
+
sessionId: input.sessionId
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/utils/jwt.ts
|
|
75
|
+
function extractUserIdFromJwt(token) {
|
|
76
|
+
if (!token) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const parts = token.split(".");
|
|
80
|
+
if (parts.length < 2) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const payload = JSON.parse(
|
|
85
|
+
Buffer.from(parts[1], "base64url").toString("utf8")
|
|
86
|
+
);
|
|
87
|
+
return typeof payload.sub === "string" ? payload.sub : null;
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
60
92
|
|
|
61
93
|
// src/services/gameplay-authority-submit.ts
|
|
62
94
|
import { randomUUID } from "crypto";
|
|
@@ -657,8 +689,9 @@ function configurePlaywrightBrowsersPath() {
|
|
|
657
689
|
process.env.PLAYWRIGHT_BROWSERS_PATH = browserCachePath;
|
|
658
690
|
}
|
|
659
691
|
async function buildBrowserAuthInitScript(config) {
|
|
660
|
-
|
|
661
|
-
|
|
692
|
+
const resolvedToken = resolveLocalHarnessAccessToken(config) ?? (await createUserTokenManager(config).resolveApiToken())?.token;
|
|
693
|
+
if (!resolvedToken) return null;
|
|
694
|
+
return `(function(){localStorage.setItem('dreamboard_auth_token',${JSON.stringify(resolvedToken)});})();`;
|
|
662
695
|
}
|
|
663
696
|
async function waitForGameReady(page, timeoutMs = 6e4) {
|
|
664
697
|
await page.waitForSelector('iframe[title="Game UI Plugin"]', {
|
|
@@ -666,6 +699,79 @@ async function waitForGameReady(page, timeoutMs = 6e4) {
|
|
|
666
699
|
});
|
|
667
700
|
}
|
|
668
701
|
|
|
702
|
+
// src/services/dev-host/loader.ts
|
|
703
|
+
import path2 from "path";
|
|
704
|
+
import { createRequire } from "module";
|
|
705
|
+
import { pathToFileURL } from "url";
|
|
706
|
+
async function loadProjectDevHost(projectRoot) {
|
|
707
|
+
const requireFromProject = createRequire(path2.join(projectRoot, "package.json"));
|
|
708
|
+
let packageJsonPath;
|
|
709
|
+
let entryPath;
|
|
710
|
+
try {
|
|
711
|
+
packageJsonPath = requireFromProject.resolve(
|
|
712
|
+
"@dreamboard-games/dev-host/package.json"
|
|
713
|
+
);
|
|
714
|
+
entryPath = requireFromProject.resolve("@dreamboard-games/dev-host");
|
|
715
|
+
} catch (error) {
|
|
716
|
+
throw new Error(
|
|
717
|
+
"Install @dreamboard-games/dev-host in this workspace before running dreamboard dev or browser tests.",
|
|
718
|
+
{ cause: error }
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
const packageRoot = path2.dirname(packageJsonPath);
|
|
722
|
+
if (!isPathInside(packageRoot, entryPath)) {
|
|
723
|
+
throw new Error(
|
|
724
|
+
"@dreamboard-games/dev-host resolved outside its installed package."
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
const packageJson = requireFromProject(packageJsonPath);
|
|
728
|
+
if (packageJson.name !== "@dreamboard-games/dev-host" || typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
729
|
+
throw new Error("Installed @dreamboard-games/dev-host metadata is invalid.");
|
|
730
|
+
}
|
|
731
|
+
const loaded = await import(pathToFileURL(entryPath).href);
|
|
732
|
+
if (loaded.protocolVersion !== 1 || typeof loaded.start !== "function") {
|
|
733
|
+
throw new Error(
|
|
734
|
+
"Installed @dreamboard-games/dev-host does not expose DevHostModuleV1."
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
packageRoot,
|
|
739
|
+
packageVersion: packageJson.version,
|
|
740
|
+
module: {
|
|
741
|
+
protocolVersion: loaded.protocolVersion,
|
|
742
|
+
start: loaded.start
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function isPathInside(parent, candidate) {
|
|
747
|
+
const relativePath = path2.relative(parent, candidate);
|
|
748
|
+
return relativePath === "" || !relativePath.startsWith("..") && !path2.isAbsolute(relativePath);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// src/services/dev-host/platform.ts
|
|
752
|
+
function createCliDevHostPlatform(config) {
|
|
753
|
+
return {
|
|
754
|
+
resolveBearer: () => resolveDevHostBearer(config)
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
async function resolveDevHostBearer(config) {
|
|
758
|
+
const localHarnessToken = resolveLocalHarnessAccessToken(config);
|
|
759
|
+
if (localHarnessToken) {
|
|
760
|
+
return { kind: "ok", token: localHarnessToken };
|
|
761
|
+
}
|
|
762
|
+
if (config.refreshTokenSource !== "global") {
|
|
763
|
+
return { kind: "ok", token: config.authToken ?? null };
|
|
764
|
+
}
|
|
765
|
+
if (!config.refreshToken) {
|
|
766
|
+
return {
|
|
767
|
+
kind: "permanent_invalid",
|
|
768
|
+
message: "Stored Dreamboard session is expired or invalid. Run `dreamboard login` to authenticate again."
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
const resolved = await createUserTokenManager(config).resolveApiToken();
|
|
772
|
+
return { kind: "ok", token: resolved?.token ?? null };
|
|
773
|
+
}
|
|
774
|
+
|
|
669
775
|
// src/services/workflows/resolve-setup-profile.ts
|
|
670
776
|
function resolveSetupProfileSelection(options) {
|
|
671
777
|
const setupProfiles = options.manifest.setupProfiles ?? [];
|
|
@@ -751,15 +857,15 @@ function findScenarioStackFrame(options) {
|
|
|
751
857
|
if (!options.stack) {
|
|
752
858
|
return null;
|
|
753
859
|
}
|
|
754
|
-
const absolutePath =
|
|
755
|
-
const relativePath =
|
|
756
|
-
const normalizedRelativePath = relativePath.split(
|
|
860
|
+
const absolutePath = path3.resolve(options.scenarioFilePath);
|
|
861
|
+
const relativePath = path3.relative(options.projectRoot, absolutePath);
|
|
862
|
+
const normalizedRelativePath = relativePath.split(path3.sep).join("/");
|
|
757
863
|
const escapedAbsolutePath = escapeRegExp(absolutePath);
|
|
758
864
|
const escapedRelativePath = escapeRegExp(normalizedRelativePath);
|
|
759
865
|
const absoluteFrame = new RegExp(`${escapedAbsolutePath}:(\\d+):(\\d+)`);
|
|
760
866
|
const relativeFrame = new RegExp(`${escapedRelativePath}:(\\d+):(\\d+)`);
|
|
761
867
|
for (const line of options.stack.split("\n")) {
|
|
762
|
-
const normalizedLine = line.split(
|
|
868
|
+
const normalizedLine = line.split(path3.sep).join("/");
|
|
763
869
|
const match = normalizedLine.match(absoluteFrame) ?? normalizedLine.match(relativeFrame);
|
|
764
870
|
if (match?.[1] && match?.[2]) {
|
|
765
871
|
return `at ${normalizedRelativePath}:${match[1]}:${match[2]}`;
|
|
@@ -770,12 +876,45 @@ function findScenarioStackFrame(options) {
|
|
|
770
876
|
function escapeRegExp(value) {
|
|
771
877
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
772
878
|
}
|
|
773
|
-
var
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
(
|
|
879
|
+
var projectReducerNativeModules = /* @__PURE__ */ new Map();
|
|
880
|
+
function resolveProjectSdkModule(projectRoot, specifier) {
|
|
881
|
+
const requireFromProject = createRequire2(
|
|
882
|
+
path3.join(projectRoot, "package.json")
|
|
777
883
|
);
|
|
778
|
-
return
|
|
884
|
+
return requireFromProject.resolve(specifier);
|
|
885
|
+
}
|
|
886
|
+
async function importProjectSdkModule(projectRoot, specifier) {
|
|
887
|
+
const modulePath = resolveProjectSdkModule(projectRoot, specifier);
|
|
888
|
+
return await import(pathToFileURL2(modulePath).href);
|
|
889
|
+
}
|
|
890
|
+
async function loadProjectReducerNativeModules(projectRoot) {
|
|
891
|
+
const cacheKey = path3.resolve(projectRoot);
|
|
892
|
+
const cached = projectReducerNativeModules.get(cacheKey);
|
|
893
|
+
if (cached) {
|
|
894
|
+
return cached;
|
|
895
|
+
}
|
|
896
|
+
const promise = Promise.all([
|
|
897
|
+
importProjectSdkModule(cacheKey, "@dreamboard-games/sdk/reducer"),
|
|
898
|
+
importProjectSdkModule(cacheKey, "@dreamboard-games/sdk/reducer-contract"),
|
|
899
|
+
importProjectSdkModule(
|
|
900
|
+
cacheKey,
|
|
901
|
+
"@dreamboard-games/sdk/testing"
|
|
902
|
+
)
|
|
903
|
+
]).then(([reducerModule, reducerContractModule, testingModule]) => {
|
|
904
|
+
if (typeof reducerModule.createReducerBundle !== "function" || typeof reducerModule.contractFingerprint !== "function" || typeof reducerContractModule.materializeManifestTable !== "function" || typeof testingModule.createExpectApi !== "function") {
|
|
905
|
+
throw new Error(
|
|
906
|
+
"Installed @dreamboard-games/sdk does not expose the reducer-native test helpers required by this CLI."
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
return {
|
|
910
|
+
createReducerBundle: reducerModule.createReducerBundle,
|
|
911
|
+
contractFingerprint: reducerModule.contractFingerprint,
|
|
912
|
+
materializeManifestTable: reducerContractModule.materializeManifestTable,
|
|
913
|
+
createExpectApi: testingModule.createExpectApi
|
|
914
|
+
};
|
|
915
|
+
});
|
|
916
|
+
projectReducerNativeModules.set(cacheKey, promise);
|
|
917
|
+
return promise;
|
|
779
918
|
}
|
|
780
919
|
function createSubmissionError(errorCode, message, fallbackMessage) {
|
|
781
920
|
const error = new Error(message ?? fallbackMessage);
|
|
@@ -812,7 +951,7 @@ async function discoverFiles(root, suffix) {
|
|
|
812
951
|
const entries = await readdir(root, { withFileTypes: true });
|
|
813
952
|
const files = [];
|
|
814
953
|
for (const entry of entries) {
|
|
815
|
-
const entryPath =
|
|
954
|
+
const entryPath = path3.join(root, entry.name);
|
|
816
955
|
if (entry.isDirectory()) {
|
|
817
956
|
files.push(...await discoverFiles(entryPath, suffix));
|
|
818
957
|
continue;
|
|
@@ -861,62 +1000,52 @@ function parseTypedScenarioDefinition(value) {
|
|
|
861
1000
|
};
|
|
862
1001
|
}
|
|
863
1002
|
async function isReducerNativeTestingWorkspace(projectRoot) {
|
|
864
|
-
return await exists(
|
|
865
|
-
|
|
1003
|
+
return await exists(path3.join(projectRoot, "app", "game.ts")) && await exists(
|
|
1004
|
+
path3.join(projectRoot, "shared", "generated", "ui-contract.ts")
|
|
866
1005
|
);
|
|
867
1006
|
}
|
|
868
1007
|
async function ensureReducerNativeTestingFiles(projectRoot) {
|
|
869
|
-
const testingTypesPath =
|
|
870
|
-
const testingContractPath =
|
|
1008
|
+
const testingTypesPath = "test/testing-types.ts";
|
|
1009
|
+
const testingContractPath = "test/generated/testing-contract.ts";
|
|
1010
|
+
const baseStatesPath = "test/generated/base-states.generated.ts";
|
|
1011
|
+
const baseStatesDtsPath = "test/generated/base-states.generated.d.ts";
|
|
1012
|
+
const scenarioManifestPath = "test/generated/scenario-manifest.generated.ts";
|
|
1013
|
+
await ensureDir(resolveWorkspacePath(projectRoot, "test"));
|
|
1014
|
+
await ensureDir(resolveWorkspacePath(projectRoot, "test/generated"));
|
|
1015
|
+
const existingTestingTypes = await readWorkspaceTextFileIfExists(
|
|
871
1016
|
projectRoot,
|
|
872
|
-
|
|
873
|
-
"generated",
|
|
874
|
-
"testing-contract.ts"
|
|
1017
|
+
testingTypesPath
|
|
875
1018
|
);
|
|
876
|
-
const baseStatesPath = path2.join(
|
|
877
|
-
projectRoot,
|
|
878
|
-
"test",
|
|
879
|
-
"generated",
|
|
880
|
-
"base-states.generated.ts"
|
|
881
|
-
);
|
|
882
|
-
const baseStatesDtsPath = path2.join(
|
|
883
|
-
projectRoot,
|
|
884
|
-
"test",
|
|
885
|
-
"generated",
|
|
886
|
-
"base-states.generated.d.ts"
|
|
887
|
-
);
|
|
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
1019
|
if (shouldRefreshReducerTestingTypes(existingTestingTypes)) {
|
|
898
|
-
await
|
|
1020
|
+
await writeWorkspaceTextFile(
|
|
1021
|
+
projectRoot,
|
|
899
1022
|
testingTypesPath,
|
|
900
1023
|
REDUCER_TESTING_TYPES_WRAPPER_CONTENT
|
|
901
1024
|
);
|
|
902
1025
|
}
|
|
903
1026
|
const rejectionCodes = await collectKnownRejectionCodes(projectRoot);
|
|
904
|
-
await
|
|
1027
|
+
await writeWorkspaceTextFile(
|
|
1028
|
+
projectRoot,
|
|
905
1029
|
testingContractPath,
|
|
906
1030
|
buildReducerTestingContractContent({ rejectionCodes })
|
|
907
1031
|
);
|
|
908
1032
|
const header = "// Generated by dreamboard test generate. Do not edit by hand.\n";
|
|
909
|
-
if (!await
|
|
910
|
-
await
|
|
1033
|
+
if (!await workspacePathExists(projectRoot, baseStatesPath)) {
|
|
1034
|
+
await writeWorkspaceTextFile(
|
|
1035
|
+
projectRoot,
|
|
911
1036
|
baseStatesPath,
|
|
912
1037
|
`${header}export const BASE_STATES = {} as const;
|
|
913
1038
|
export const BASE_STATES_CONTRACT_FINGERPRINT = undefined;
|
|
914
1039
|
`
|
|
915
1040
|
);
|
|
916
1041
|
} else {
|
|
917
|
-
const existingBaseStates = await
|
|
1042
|
+
const existingBaseStates = await readWorkspaceTextFileIfExists(
|
|
1043
|
+
projectRoot,
|
|
1044
|
+
baseStatesPath
|
|
1045
|
+
);
|
|
918
1046
|
if (existingBaseStates && !existingBaseStates.includes("BASE_STATES_CONTRACT_FINGERPRINT")) {
|
|
919
|
-
await
|
|
1047
|
+
await writeWorkspaceTextFile(
|
|
1048
|
+
projectRoot,
|
|
920
1049
|
baseStatesPath,
|
|
921
1050
|
`${existingBaseStates.trimEnd()}
|
|
922
1051
|
export const BASE_STATES_CONTRACT_FINGERPRINT = undefined;
|
|
@@ -924,17 +1053,22 @@ export const BASE_STATES_CONTRACT_FINGERPRINT = undefined;
|
|
|
924
1053
|
);
|
|
925
1054
|
}
|
|
926
1055
|
}
|
|
927
|
-
if (!await
|
|
928
|
-
await
|
|
1056
|
+
if (!await workspacePathExists(projectRoot, baseStatesDtsPath)) {
|
|
1057
|
+
await writeWorkspaceTextFile(
|
|
1058
|
+
projectRoot,
|
|
929
1059
|
baseStatesDtsPath,
|
|
930
1060
|
`${header}export declare const BASE_STATES: Record<string, unknown>;
|
|
931
1061
|
export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
|
|
932
1062
|
`
|
|
933
1063
|
);
|
|
934
1064
|
} else {
|
|
935
|
-
const existingBaseStatesDts = await
|
|
1065
|
+
const existingBaseStatesDts = await readWorkspaceTextFileIfExists(
|
|
1066
|
+
projectRoot,
|
|
1067
|
+
baseStatesDtsPath
|
|
1068
|
+
);
|
|
936
1069
|
if (existingBaseStatesDts && !existingBaseStatesDts.includes("BASE_STATES_CONTRACT_FINGERPRINT")) {
|
|
937
|
-
await
|
|
1070
|
+
await writeWorkspaceTextFile(
|
|
1071
|
+
projectRoot,
|
|
938
1072
|
baseStatesDtsPath,
|
|
939
1073
|
`${existingBaseStatesDts.trimEnd()}
|
|
940
1074
|
export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
|
|
@@ -942,8 +1076,9 @@ export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
|
|
|
942
1076
|
);
|
|
943
1077
|
}
|
|
944
1078
|
}
|
|
945
|
-
if (!await
|
|
946
|
-
await
|
|
1079
|
+
if (!await workspacePathExists(projectRoot, scenarioManifestPath)) {
|
|
1080
|
+
await writeWorkspaceTextFile(
|
|
1081
|
+
projectRoot,
|
|
947
1082
|
scenarioManifestPath,
|
|
948
1083
|
`${header}export const SCENARIO_MANIFEST = [] as const;
|
|
949
1084
|
`
|
|
@@ -958,7 +1093,7 @@ var DEFAULT_REJECTION_CODES = [
|
|
|
958
1093
|
];
|
|
959
1094
|
async function collectKnownRejectionCodes(projectRoot) {
|
|
960
1095
|
const knownCodes = new Set(DEFAULT_REJECTION_CODES);
|
|
961
|
-
const gamePath =
|
|
1096
|
+
const gamePath = path3.join(projectRoot, "app", "game.ts");
|
|
962
1097
|
if (!await exists(gamePath)) {
|
|
963
1098
|
return Array.from(knownCodes).sort(
|
|
964
1099
|
(left, right) => left.localeCompare(right)
|
|
@@ -985,7 +1120,7 @@ async function collectKnownRejectionCodes(projectRoot) {
|
|
|
985
1120
|
}
|
|
986
1121
|
async function loadTypedBases(projectRoot) {
|
|
987
1122
|
const baseFiles = await discoverFiles(
|
|
988
|
-
|
|
1123
|
+
resolveWorkspacePath(projectRoot, "test/bases"),
|
|
989
1124
|
BASE_SUFFIX
|
|
990
1125
|
);
|
|
991
1126
|
const loaded = [];
|
|
@@ -1004,8 +1139,8 @@ async function loadTypedBases(projectRoot) {
|
|
|
1004
1139
|
return loaded;
|
|
1005
1140
|
}
|
|
1006
1141
|
async function loadTypedScenarios(projectRoot, options) {
|
|
1007
|
-
const scenarioFiles = options.scenarioPath ? [
|
|
1008
|
-
|
|
1142
|
+
const scenarioFiles = options.scenarioPath ? [resolveWorkspacePath(projectRoot, options.scenarioPath)] : await discoverFiles(
|
|
1143
|
+
resolveWorkspacePath(projectRoot, "test/scenarios"),
|
|
1009
1144
|
SCENARIO_SUFFIX
|
|
1010
1145
|
);
|
|
1011
1146
|
const loaded = [];
|
|
@@ -1024,8 +1159,8 @@ async function loadTypedScenarios(projectRoot, options) {
|
|
|
1024
1159
|
return loaded;
|
|
1025
1160
|
}
|
|
1026
1161
|
function reducerNativeTestHelperExternals(projectRoot, filePath) {
|
|
1027
|
-
const testingTypesPath =
|
|
1028
|
-
const relative =
|
|
1162
|
+
const testingTypesPath = path3.join(projectRoot, "test", "testing-types");
|
|
1163
|
+
const relative = path3.relative(path3.dirname(filePath), testingTypesPath).replaceAll("\\", "/");
|
|
1029
1164
|
const specifier = relative.startsWith(".") ? relative : `./${relative}`;
|
|
1030
1165
|
return [specifier, `${specifier}.ts`, ...SDK_UI_RUNTIME_EXTERNALS];
|
|
1031
1166
|
}
|
|
@@ -1206,7 +1341,8 @@ function summarizeTableValidationError(manifest, error) {
|
|
|
1206
1341
|
].join("\n");
|
|
1207
1342
|
}
|
|
1208
1343
|
var ShadowReducerRuntime = class {
|
|
1209
|
-
constructor(manifest, gameModuleDefault, createInitialTable, seed, players, setupProfileId, debug = false) {
|
|
1344
|
+
constructor(modules, manifest, gameModuleDefault, createInitialTable, seed, players, setupProfileId, debug = false) {
|
|
1345
|
+
this.modules = modules;
|
|
1210
1346
|
this.manifest = manifest;
|
|
1211
1347
|
this.gameModuleDefault = gameModuleDefault;
|
|
1212
1348
|
this.createInitialTable = createInitialTable;
|
|
@@ -1219,20 +1355,13 @@ var ShadowReducerRuntime = class {
|
|
|
1219
1355
|
"app/game.ts must export a reducer-native game definition."
|
|
1220
1356
|
);
|
|
1221
1357
|
}
|
|
1222
|
-
this.bundle = createReducerBundle(gameModuleDefault);
|
|
1358
|
+
this.bundle = this.modules.createReducerBundle(gameModuleDefault);
|
|
1223
1359
|
this.runtime = this.bundle.createInProcessRuntime();
|
|
1224
1360
|
this.playerIds = Array.from(
|
|
1225
1361
|
{ length: players },
|
|
1226
1362
|
(_, index) => `player-${index + 1}`
|
|
1227
1363
|
);
|
|
1228
1364
|
}
|
|
1229
|
-
manifest;
|
|
1230
|
-
gameModuleDefault;
|
|
1231
|
-
createInitialTable;
|
|
1232
|
-
seed;
|
|
1233
|
-
players;
|
|
1234
|
-
setupProfileId;
|
|
1235
|
-
debug;
|
|
1236
1365
|
bundle;
|
|
1237
1366
|
runtime;
|
|
1238
1367
|
playerIds;
|
|
@@ -1250,7 +1379,7 @@ var ShadowReducerRuntime = class {
|
|
|
1250
1379
|
this.createInitialTable?.({
|
|
1251
1380
|
playerIds: this.playerIds,
|
|
1252
1381
|
shuffleItems
|
|
1253
|
-
}) ?? materializeManifestTable({
|
|
1382
|
+
}) ?? this.modules.materializeManifestTable({
|
|
1254
1383
|
manifest: this.manifest,
|
|
1255
1384
|
playerIds: this.playerIds,
|
|
1256
1385
|
shuffleItems
|
|
@@ -1411,8 +1540,6 @@ var RemoteGameplayTracker = class {
|
|
|
1411
1540
|
this.playerId = playerId;
|
|
1412
1541
|
this.state.playerId = playerId;
|
|
1413
1542
|
}
|
|
1414
|
-
sessionId;
|
|
1415
|
-
playerId;
|
|
1416
1543
|
state = {
|
|
1417
1544
|
version: -1,
|
|
1418
1545
|
actionSetVersion: "",
|
|
@@ -1479,31 +1606,13 @@ function parseSeatView(raw) {
|
|
|
1479
1606
|
function sleep(ms) {
|
|
1480
1607
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1481
1608
|
}
|
|
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
1609
|
function assertDispatchResultWireContract(result) {
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1610
|
+
if (typeof result !== "object" || result === null) {
|
|
1611
|
+
throw new Error(
|
|
1612
|
+
"Reducer bundle returned invalid payload for 'dispatch': DispatchResult must be an object."
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
return result;
|
|
1507
1616
|
}
|
|
1508
1617
|
async function createBrowserBridgeClient(page) {
|
|
1509
1618
|
const bridgeExists = async () => page.evaluate(
|
|
@@ -1575,7 +1684,7 @@ async function assertLiveMatchesShadow(runner, shadow, live) {
|
|
|
1575
1684
|
}
|
|
1576
1685
|
}
|
|
1577
1686
|
async function loadBrowserDriver(projectRoot) {
|
|
1578
|
-
const filePath =
|
|
1687
|
+
const filePath = path3.join(projectRoot, "test", "browser-driver.ts");
|
|
1579
1688
|
if (!await exists(filePath)) {
|
|
1580
1689
|
return null;
|
|
1581
1690
|
}
|
|
@@ -1603,19 +1712,16 @@ function sanitizeSnapshotSegment(value) {
|
|
|
1603
1712
|
function createScenarioSnapshotMatcher(options) {
|
|
1604
1713
|
return (filename, actual) => {
|
|
1605
1714
|
const suffix = filename ? `.${sanitizeSnapshotSegment(filename)}` : "";
|
|
1606
|
-
const snapshotPath =
|
|
1715
|
+
const snapshotPath = resolveWorkspacePath(
|
|
1607
1716
|
options.projectRoot,
|
|
1608
|
-
|
|
1609
|
-
"generated",
|
|
1610
|
-
"snapshots",
|
|
1611
|
-
`${sanitizeSnapshotSegment(options.scenarioId)}${suffix}.snapshot.json`
|
|
1717
|
+
`test/generated/snapshots/${sanitizeSnapshotSegment(options.scenarioId)}${suffix}.snapshot.json`
|
|
1612
1718
|
);
|
|
1613
1719
|
const wrappedValue = {
|
|
1614
1720
|
value: actual
|
|
1615
1721
|
};
|
|
1616
1722
|
const serialized = `${JSON.stringify(wrappedValue, null, 2)}
|
|
1617
1723
|
`;
|
|
1618
|
-
mkdirSync(
|
|
1724
|
+
mkdirSync(path3.dirname(snapshotPath), { recursive: true });
|
|
1619
1725
|
if (!existsSync(snapshotPath) || options.updateSnapshots) {
|
|
1620
1726
|
writeFileSync(snapshotPath, serialized, "utf8");
|
|
1621
1727
|
return;
|
|
@@ -1623,7 +1729,7 @@ function createScenarioSnapshotMatcher(options) {
|
|
|
1623
1729
|
const previous = JSON.parse(readFileSync(snapshotPath, "utf8"));
|
|
1624
1730
|
if (!deepEqual(previous.value, actual)) {
|
|
1625
1731
|
throw new Error(
|
|
1626
|
-
`Snapshot mismatch for scenario '${options.scenarioId}'. Re-run with --update-snapshots to refresh ${
|
|
1732
|
+
`Snapshot mismatch for scenario '${options.scenarioId}'. Re-run with --update-snapshots to refresh ${path3.relative(options.projectRoot, snapshotPath)}.`
|
|
1627
1733
|
);
|
|
1628
1734
|
}
|
|
1629
1735
|
};
|
|
@@ -1853,19 +1959,26 @@ async function createSessionFromScenario(options) {
|
|
|
1853
1959
|
}
|
|
1854
1960
|
var MATERIALIZED_SCENARIO_CACHE_VERSION = 1;
|
|
1855
1961
|
function materializedScenarioCachePath(options) {
|
|
1856
|
-
return
|
|
1857
|
-
options.projectRoot,
|
|
1962
|
+
return [
|
|
1858
1963
|
PROJECT_DIR_NAME,
|
|
1859
1964
|
"dev",
|
|
1860
1965
|
"scenario-cache",
|
|
1861
1966
|
`${sanitizeSnapshotSegment(options.baseId)}.${sanitizeSnapshotSegment(
|
|
1862
1967
|
options.scenarioId
|
|
1863
1968
|
)}.${options.fingerprintHash}.json`
|
|
1969
|
+
].join("/");
|
|
1970
|
+
}
|
|
1971
|
+
function materializedScenarioCacheFilePath(options) {
|
|
1972
|
+
return resolveWorkspacePath(
|
|
1973
|
+
options.projectRoot,
|
|
1974
|
+
materializedScenarioCachePath(options)
|
|
1864
1975
|
);
|
|
1865
1976
|
}
|
|
1866
1977
|
async function readMaterializedScenarioCache(options) {
|
|
1867
|
-
const
|
|
1868
|
-
|
|
1978
|
+
const text = await readWorkspaceTextFileIfExists(
|
|
1979
|
+
options.projectRoot,
|
|
1980
|
+
materializedScenarioCachePath(options)
|
|
1981
|
+
);
|
|
1869
1982
|
if (!text) return null;
|
|
1870
1983
|
try {
|
|
1871
1984
|
const payload = JSON.parse(
|
|
@@ -1880,13 +1993,17 @@ async function readMaterializedScenarioCache(options) {
|
|
|
1880
1993
|
}
|
|
1881
1994
|
}
|
|
1882
1995
|
async function writeMaterializedScenarioCache(options) {
|
|
1883
|
-
const cachePath =
|
|
1884
|
-
await ensureDir(
|
|
1885
|
-
await
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1996
|
+
const cachePath = materializedScenarioCacheFilePath(options);
|
|
1997
|
+
await ensureDir(path3.dirname(cachePath));
|
|
1998
|
+
await writeWorkspaceJsonFile(
|
|
1999
|
+
options.projectRoot,
|
|
2000
|
+
materializedScenarioCachePath(options),
|
|
2001
|
+
{
|
|
2002
|
+
cacheVersion: MATERIALIZED_SCENARIO_CACHE_VERSION,
|
|
2003
|
+
fingerprintHash: options.fingerprintHash,
|
|
2004
|
+
materialized: options.materialized
|
|
2005
|
+
}
|
|
2006
|
+
);
|
|
1890
2007
|
}
|
|
1891
2008
|
async function materializeScenarioReducerState(options) {
|
|
1892
2009
|
await ensureReducerNativeTestingFiles(options.projectRoot);
|
|
@@ -1976,13 +2093,14 @@ async function materializeScenarioReducerState(options) {
|
|
|
1976
2093
|
if (cached) {
|
|
1977
2094
|
return cached;
|
|
1978
2095
|
}
|
|
1979
|
-
const [gameModule, manifestContractModule] = await Promise.all([
|
|
2096
|
+
const [gameModule, manifestContractModule, modules] = await Promise.all([
|
|
1980
2097
|
importTypeScriptModule(
|
|
1981
|
-
|
|
2098
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
1982
2099
|
),
|
|
1983
2100
|
importTypeScriptModule(
|
|
1984
|
-
|
|
1985
|
-
)
|
|
2101
|
+
path3.join(options.projectRoot, "shared", "manifest-contract.ts")
|
|
2102
|
+
),
|
|
2103
|
+
loadProjectReducerNativeModules(options.projectRoot)
|
|
1986
2104
|
]);
|
|
1987
2105
|
const createInitialTable = typeof manifestContractModule.createInitialTable === "function" ? manifestContractModule.createInitialTable : null;
|
|
1988
2106
|
const resolvedBase = resolveBaseDefinition(base, basesById);
|
|
@@ -1992,6 +2110,7 @@ async function materializeScenarioReducerState(options) {
|
|
|
1992
2110
|
basesById
|
|
1993
2111
|
});
|
|
1994
2112
|
const shadow = new ShadowReducerRuntime(
|
|
2113
|
+
modules,
|
|
1995
2114
|
manifest,
|
|
1996
2115
|
gameModule.default,
|
|
1997
2116
|
createInitialTable,
|
|
@@ -2004,7 +2123,7 @@ async function materializeScenarioReducerState(options) {
|
|
|
2004
2123
|
const context = await createScenarioContext({
|
|
2005
2124
|
runner: "reducer",
|
|
2006
2125
|
shadow,
|
|
2007
|
-
expect: (
|
|
2126
|
+
expect: modules.createExpectApi()
|
|
2008
2127
|
});
|
|
2009
2128
|
shadow.clearHistory();
|
|
2010
2129
|
await scenario.definition.when(context);
|
|
@@ -2056,18 +2175,20 @@ async function replayScenarioThroughBackend(options) {
|
|
|
2056
2175
|
generatedBaseStates,
|
|
2057
2176
|
manifest,
|
|
2058
2177
|
gameModule,
|
|
2059
|
-
manifestContractModule
|
|
2178
|
+
manifestContractModule,
|
|
2179
|
+
modules
|
|
2060
2180
|
] = await Promise.all([
|
|
2061
2181
|
loadTypedBases(options.projectRoot),
|
|
2062
2182
|
loadTypedScenarios(options.projectRoot, {}),
|
|
2063
2183
|
loadGeneratedBaseStates(options.projectRoot),
|
|
2064
2184
|
loadManifest(options.projectRoot),
|
|
2065
2185
|
importTypeScriptModule(
|
|
2066
|
-
|
|
2186
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
2067
2187
|
),
|
|
2068
2188
|
importTypeScriptModule(
|
|
2069
|
-
|
|
2070
|
-
)
|
|
2189
|
+
path3.join(options.projectRoot, "shared", "manifest-contract.ts")
|
|
2190
|
+
),
|
|
2191
|
+
loadProjectReducerNativeModules(options.projectRoot)
|
|
2071
2192
|
]);
|
|
2072
2193
|
const matchingScenarios = scenarios.filter(
|
|
2073
2194
|
(scenario2) => scenario2.definition.id === options.scenarioId
|
|
@@ -2110,6 +2231,7 @@ async function replayScenarioThroughBackend(options) {
|
|
|
2110
2231
|
basesById
|
|
2111
2232
|
});
|
|
2112
2233
|
const shadow = new ShadowReducerRuntime(
|
|
2234
|
+
await loadProjectReducerNativeModules(options.projectRoot),
|
|
2113
2235
|
manifest,
|
|
2114
2236
|
gameModule.default,
|
|
2115
2237
|
createInitialTable,
|
|
@@ -2162,7 +2284,7 @@ async function replayScenarioThroughBackend(options) {
|
|
|
2162
2284
|
const context = await createScenarioContext({
|
|
2163
2285
|
runner: "reducer",
|
|
2164
2286
|
shadow,
|
|
2165
|
-
expect: (await
|
|
2287
|
+
expect: (await loadProjectReducerNativeModules(options.projectRoot)).createExpectApi(),
|
|
2166
2288
|
live: {
|
|
2167
2289
|
sessionId: session.sessionId,
|
|
2168
2290
|
version: 0,
|
|
@@ -2211,18 +2333,20 @@ async function createScenarioActionPlan(options) {
|
|
|
2211
2333
|
generatedBaseStates,
|
|
2212
2334
|
manifest,
|
|
2213
2335
|
gameModule,
|
|
2214
|
-
manifestContractModule
|
|
2336
|
+
manifestContractModule,
|
|
2337
|
+
modules
|
|
2215
2338
|
] = await Promise.all([
|
|
2216
2339
|
loadTypedBases(options.projectRoot),
|
|
2217
2340
|
loadTypedScenarios(options.projectRoot, {}),
|
|
2218
2341
|
loadGeneratedBaseStates(options.projectRoot),
|
|
2219
2342
|
loadManifest(options.projectRoot),
|
|
2220
2343
|
importTypeScriptModule(
|
|
2221
|
-
|
|
2344
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
2222
2345
|
),
|
|
2223
2346
|
importTypeScriptModule(
|
|
2224
|
-
|
|
2225
|
-
)
|
|
2347
|
+
path3.join(options.projectRoot, "shared", "manifest-contract.ts")
|
|
2348
|
+
),
|
|
2349
|
+
loadProjectReducerNativeModules(options.projectRoot)
|
|
2226
2350
|
]);
|
|
2227
2351
|
const matchingScenarios = scenarios.filter(
|
|
2228
2352
|
(scenario2) => scenario2.definition.id === options.scenarioId
|
|
@@ -2265,6 +2389,7 @@ async function createScenarioActionPlan(options) {
|
|
|
2265
2389
|
basesById
|
|
2266
2390
|
});
|
|
2267
2391
|
const shadow = new ShadowReducerRuntime(
|
|
2392
|
+
await loadProjectReducerNativeModules(options.projectRoot),
|
|
2268
2393
|
manifest,
|
|
2269
2394
|
gameModule.default,
|
|
2270
2395
|
createInitialTable,
|
|
@@ -2286,7 +2411,7 @@ async function createScenarioActionPlan(options) {
|
|
|
2286
2411
|
const context = await createScenarioContext({
|
|
2287
2412
|
runner: "reducer",
|
|
2288
2413
|
shadow,
|
|
2289
|
-
expect: (await
|
|
2414
|
+
expect: (await loadProjectReducerNativeModules(options.projectRoot)).createExpectApi(),
|
|
2290
2415
|
actionPlan: {
|
|
2291
2416
|
submitIndex: 0,
|
|
2292
2417
|
diagnostics
|
|
@@ -2509,18 +2634,18 @@ function sortBasesForArtifacts(bases, basesById) {
|
|
|
2509
2634
|
return ordered;
|
|
2510
2635
|
}
|
|
2511
2636
|
function writeProjectionSnapshots(options) {
|
|
2512
|
-
const projectionDir =
|
|
2637
|
+
const projectionDir = resolveWorkspacePath(
|
|
2513
2638
|
options.projectRoot,
|
|
2514
|
-
|
|
2515
|
-
"generated",
|
|
2516
|
-
"bases",
|
|
2517
|
-
options.baseId
|
|
2639
|
+
`test/generated/bases/${sanitizeSnapshotSegment(options.baseId)}`
|
|
2518
2640
|
);
|
|
2519
2641
|
rmSync(projectionDir, { recursive: true, force: true });
|
|
2520
2642
|
mkdirSync(projectionDir, { recursive: true });
|
|
2521
2643
|
const projection = options.shadow.projectAllSeats();
|
|
2522
2644
|
for (const [playerId, seatProjection] of Object.entries(projection.seats)) {
|
|
2523
|
-
const outputPath =
|
|
2645
|
+
const outputPath = resolveWorkspacePath(
|
|
2646
|
+
options.projectRoot,
|
|
2647
|
+
`test/generated/bases/${sanitizeSnapshotSegment(options.baseId)}/${sanitizeSnapshotSegment(playerId)}.projection.json`
|
|
2648
|
+
);
|
|
2524
2649
|
writeFileSync(
|
|
2525
2650
|
outputPath,
|
|
2526
2651
|
`${JSON.stringify(
|
|
@@ -2540,7 +2665,7 @@ function writeProjectionSnapshots(options) {
|
|
|
2540
2665
|
function validateTypedScenarioBases(scenarios, basesById) {
|
|
2541
2666
|
const available = new Set(basesById.keys());
|
|
2542
2667
|
const invalid = scenarios.filter((scenario) => !available.has(scenario.definition.from)).map(
|
|
2543
|
-
(scenario) =>
|
|
2668
|
+
(scenario) => path3.relative(process.cwd(), scenario.filePath) + ` -> '${scenario.definition.from}'`
|
|
2544
2669
|
);
|
|
2545
2670
|
if (invalid.length > 0) {
|
|
2546
2671
|
throw new Error(
|
|
@@ -2554,28 +2679,32 @@ async function writeReducerNativeGeneratedFiles(options) {
|
|
|
2554
2679
|
const manifestHash = hashContent(JSON.stringify(manifest));
|
|
2555
2680
|
const appBundleHash = hashContent(
|
|
2556
2681
|
await bundleTypeScriptModuleText(
|
|
2557
|
-
|
|
2682
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
2558
2683
|
)
|
|
2559
2684
|
);
|
|
2560
2685
|
const uiContractHash = hashContent(
|
|
2561
2686
|
await bundleTypeScriptModuleText(
|
|
2562
|
-
|
|
2687
|
+
path3.join(options.projectRoot, "shared", "generated", "ui-contract.ts"),
|
|
2563
2688
|
{ external: SDK_UI_RUNTIME_EXTERNALS }
|
|
2564
2689
|
)
|
|
2565
2690
|
);
|
|
2566
|
-
const generatedDir =
|
|
2691
|
+
const generatedDir = resolveWorkspacePath(
|
|
2692
|
+
options.projectRoot,
|
|
2693
|
+
"test/generated"
|
|
2694
|
+
);
|
|
2567
2695
|
await ensureDir(generatedDir);
|
|
2568
2696
|
const baseStates = {};
|
|
2569
|
-
const [gameModule, manifestContractModule] = await Promise.all([
|
|
2697
|
+
const [gameModule, manifestContractModule, modules] = await Promise.all([
|
|
2570
2698
|
importTypeScriptModule(
|
|
2571
|
-
|
|
2699
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
2572
2700
|
),
|
|
2573
2701
|
importTypeScriptModule(
|
|
2574
|
-
|
|
2575
|
-
)
|
|
2702
|
+
path3.join(options.projectRoot, "shared", "manifest-contract.ts")
|
|
2703
|
+
),
|
|
2704
|
+
loadProjectReducerNativeModules(options.projectRoot)
|
|
2576
2705
|
]);
|
|
2577
2706
|
const createInitialTable = typeof manifestContractModule.createInitialTable === "function" ? manifestContractModule.createInitialTable : null;
|
|
2578
|
-
const contractFingerprintValue = contractFingerprint(
|
|
2707
|
+
const contractFingerprintValue = modules.contractFingerprint(
|
|
2579
2708
|
gameModule.default
|
|
2580
2709
|
).value;
|
|
2581
2710
|
const basesById = new Map(
|
|
@@ -2589,6 +2718,7 @@ async function writeReducerNativeGeneratedFiles(options) {
|
|
|
2589
2718
|
basesById
|
|
2590
2719
|
});
|
|
2591
2720
|
const shadow = new ShadowReducerRuntime(
|
|
2721
|
+
modules,
|
|
2592
2722
|
manifest,
|
|
2593
2723
|
gameModule.default,
|
|
2594
2724
|
createInitialTable,
|
|
@@ -2626,14 +2756,22 @@ async function writeReducerNativeGeneratedFiles(options) {
|
|
|
2626
2756
|
const context = await createScenarioContext({
|
|
2627
2757
|
runner: "reducer",
|
|
2628
2758
|
shadow,
|
|
2629
|
-
expect: (
|
|
2759
|
+
expect: modules.createExpectApi()
|
|
2630
2760
|
});
|
|
2631
2761
|
await context.game.start();
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2762
|
+
try {
|
|
2763
|
+
await base.definition.setup({
|
|
2764
|
+
game: context.game,
|
|
2765
|
+
players: context.players,
|
|
2766
|
+
seat: context.seat
|
|
2767
|
+
});
|
|
2768
|
+
} catch (error) {
|
|
2769
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2770
|
+
throw new Error(
|
|
2771
|
+
`Base '${base.definition.id}' setup failed with setupProfileId '${effectiveSetup.setupProfileId ?? "none"}': ${message}`,
|
|
2772
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2637
2775
|
baseStates[baseStateKey(base.definition.id)] = {
|
|
2638
2776
|
key: baseStateKey(base.definition.id),
|
|
2639
2777
|
base: base.definition.id,
|
|
@@ -2662,24 +2800,27 @@ async function writeReducerNativeGeneratedFiles(options) {
|
|
|
2662
2800
|
});
|
|
2663
2801
|
}
|
|
2664
2802
|
const header = "// Generated by dreamboard test generate. Do not edit by hand.\n";
|
|
2665
|
-
await
|
|
2666
|
-
|
|
2803
|
+
await writeWorkspaceTextFile(
|
|
2804
|
+
options.projectRoot,
|
|
2805
|
+
"test/generated/base-states.generated.ts",
|
|
2667
2806
|
`${header}export const BASE_STATES = ${JSON.stringify(baseStates, null, 2)} as const;
|
|
2668
2807
|
export const BASE_STATES_CONTRACT_FINGERPRINT = ${JSON.stringify(contractFingerprintValue)};
|
|
2669
2808
|
`
|
|
2670
2809
|
);
|
|
2671
|
-
await
|
|
2672
|
-
|
|
2810
|
+
await writeWorkspaceTextFile(
|
|
2811
|
+
options.projectRoot,
|
|
2812
|
+
"test/generated/base-states.generated.d.ts",
|
|
2673
2813
|
`${header}export declare const BASE_STATES: Record<string, unknown>;
|
|
2674
2814
|
export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
|
|
2675
2815
|
`
|
|
2676
2816
|
);
|
|
2677
|
-
await
|
|
2678
|
-
|
|
2817
|
+
await writeWorkspaceTextFile(
|
|
2818
|
+
options.projectRoot,
|
|
2819
|
+
"test/generated/scenario-manifest.generated.ts",
|
|
2679
2820
|
`${header}export const SCENARIO_MANIFEST = ${JSON.stringify(
|
|
2680
2821
|
options.scenarios.map((scenario) => ({
|
|
2681
2822
|
id: scenario.definition.id,
|
|
2682
|
-
filePath:
|
|
2823
|
+
filePath: path3.relative(generatedDir, scenario.filePath),
|
|
2683
2824
|
base: scenario.definition.from,
|
|
2684
2825
|
runners: normalizeScenarioRunners(scenario.definition.runners)
|
|
2685
2826
|
})),
|
|
@@ -2688,15 +2829,19 @@ export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
|
|
|
2688
2829
|
)} as const;
|
|
2689
2830
|
`
|
|
2690
2831
|
);
|
|
2691
|
-
await
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2832
|
+
await writeWorkspaceJsonFile(
|
|
2833
|
+
options.projectRoot,
|
|
2834
|
+
"test/generated/.generation-meta.json",
|
|
2835
|
+
{
|
|
2836
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2837
|
+
contractFingerprint: contractFingerprintValue,
|
|
2838
|
+
scenarioCount: options.scenarios.length,
|
|
2839
|
+
baseStateCount: options.bases.length
|
|
2840
|
+
}
|
|
2841
|
+
);
|
|
2697
2842
|
}
|
|
2698
2843
|
async function loadGeneratedBaseStates(projectRoot) {
|
|
2699
|
-
const filePath =
|
|
2844
|
+
const filePath = path3.join(
|
|
2700
2845
|
projectRoot,
|
|
2701
2846
|
"test",
|
|
2702
2847
|
"generated",
|
|
@@ -2709,7 +2854,7 @@ async function loadGeneratedBaseStates(projectRoot) {
|
|
|
2709
2854
|
return module.BASE_STATES ?? null;
|
|
2710
2855
|
}
|
|
2711
2856
|
async function loadGeneratedScenarioManifest(projectRoot) {
|
|
2712
|
-
const filePath =
|
|
2857
|
+
const filePath = path3.join(
|
|
2713
2858
|
projectRoot,
|
|
2714
2859
|
"test",
|
|
2715
2860
|
"generated",
|
|
@@ -2771,10 +2916,13 @@ function validateGeneratedFingerprint(options) {
|
|
|
2771
2916
|
}
|
|
2772
2917
|
async function currentFingerprint(options) {
|
|
2773
2918
|
const manifest = await loadManifest(options.projectRoot);
|
|
2774
|
-
const gameModule = await
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2919
|
+
const [gameModule, modules] = await Promise.all([
|
|
2920
|
+
importTypeScriptModule(
|
|
2921
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
2922
|
+
),
|
|
2923
|
+
loadProjectReducerNativeModules(options.projectRoot)
|
|
2924
|
+
]);
|
|
2925
|
+
const contractFingerprintValue = modules.contractFingerprint(
|
|
2778
2926
|
gameModule.default
|
|
2779
2927
|
).value;
|
|
2780
2928
|
const resolvedBase = resolveBaseDefinition(
|
|
@@ -2812,12 +2960,12 @@ async function currentFingerprint(options) {
|
|
|
2812
2960
|
manifestHash: hashContent(JSON.stringify(manifest)),
|
|
2813
2961
|
appBundleHash: hashContent(
|
|
2814
2962
|
await bundleTypeScriptModuleText(
|
|
2815
|
-
|
|
2963
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
2816
2964
|
)
|
|
2817
2965
|
),
|
|
2818
2966
|
uiContractHash: hashContent(
|
|
2819
2967
|
await bundleTypeScriptModuleText(
|
|
2820
|
-
|
|
2968
|
+
path3.join(options.projectRoot, "shared", "generated", "ui-contract.ts"),
|
|
2821
2969
|
{ external: SDK_UI_RUNTIME_EXTERNALS }
|
|
2822
2970
|
)
|
|
2823
2971
|
),
|
|
@@ -2857,7 +3005,8 @@ async function runReducerNativeScenarios(options) {
|
|
|
2857
3005
|
initialGeneratedBaseStates,
|
|
2858
3006
|
manifest,
|
|
2859
3007
|
gameModule,
|
|
2860
|
-
manifestContractModule
|
|
3008
|
+
manifestContractModule,
|
|
3009
|
+
modules
|
|
2861
3010
|
] = await Promise.all([
|
|
2862
3011
|
loadTypedBases(options.projectRoot),
|
|
2863
3012
|
loadTypedScenarios(options.projectRoot, {
|
|
@@ -2866,11 +3015,12 @@ async function runReducerNativeScenarios(options) {
|
|
|
2866
3015
|
loadGeneratedBaseStates(options.projectRoot),
|
|
2867
3016
|
loadManifest(options.projectRoot),
|
|
2868
3017
|
importTypeScriptModule(
|
|
2869
|
-
|
|
3018
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
2870
3019
|
),
|
|
2871
3020
|
importTypeScriptModule(
|
|
2872
|
-
|
|
2873
|
-
)
|
|
3021
|
+
path3.join(options.projectRoot, "shared", "manifest-contract.ts")
|
|
3022
|
+
),
|
|
3023
|
+
loadProjectReducerNativeModules(options.projectRoot)
|
|
2874
3024
|
]);
|
|
2875
3025
|
let generatedBaseStates = initialGeneratedBaseStates;
|
|
2876
3026
|
const createInitialTable = typeof manifestContractModule.createInitialTable === "function" ? manifestContractModule.createInitialTable : null;
|
|
@@ -2892,228 +3042,261 @@ async function runReducerNativeScenarios(options) {
|
|
|
2892
3042
|
options.runner
|
|
2893
3043
|
)
|
|
2894
3044
|
);
|
|
2895
|
-
let browser = null;
|
|
2896
|
-
let page = null;
|
|
2897
3045
|
let browserBridge = null;
|
|
2898
3046
|
let browserDriver = null;
|
|
2899
3047
|
if (options.runner === "browser") {
|
|
2900
|
-
|
|
2901
|
-
|
|
3048
|
+
browserDriver = await loadBrowserDriver(options.projectRoot);
|
|
3049
|
+
}
|
|
3050
|
+
let passed = 0;
|
|
3051
|
+
let failed = 0;
|
|
3052
|
+
const results = [];
|
|
3053
|
+
for (const scenario of runnerScenarios) {
|
|
3054
|
+
const base = basesById.get(scenario.definition.from);
|
|
3055
|
+
if (!base) {
|
|
2902
3056
|
throw new Error(
|
|
2903
|
-
|
|
3057
|
+
`Missing typed base '${scenario.definition.from}' for scenario '${scenario.definition.id}'.`
|
|
2904
3058
|
);
|
|
2905
3059
|
}
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
const
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
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
|
|
2956
|
-
);
|
|
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);
|
|
3060
|
+
if (generatedBaseStates?.[baseStateKey(base.definition.id)]) {
|
|
3061
|
+
validateGeneratedFingerprint({
|
|
3062
|
+
generated: generatedBaseStates[baseStateKey(base.definition.id)].fingerprint,
|
|
3063
|
+
current: await currentFingerprint({
|
|
3064
|
+
projectRoot: options.projectRoot,
|
|
3065
|
+
base,
|
|
3066
|
+
basesById,
|
|
3067
|
+
compiledResultId: options.compiledResultId,
|
|
3068
|
+
gameId: options.gameId
|
|
3069
|
+
})
|
|
3070
|
+
});
|
|
3071
|
+
} else if (options.runner !== "reducer") {
|
|
3072
|
+
throw new Error(
|
|
3073
|
+
"Missing reducer-native generated base artifacts. Run 'dreamboard test generate' first."
|
|
3074
|
+
);
|
|
3075
|
+
}
|
|
3076
|
+
let scenarioBrowser = null;
|
|
3077
|
+
let scenarioDevHost = null;
|
|
3078
|
+
try {
|
|
3079
|
+
const resolvedBase = resolveBaseDefinition(base, basesById);
|
|
3080
|
+
const effectiveSetup = resolveEffectiveBaseSetup({
|
|
3081
|
+
manifest,
|
|
3082
|
+
base: base.definition,
|
|
3083
|
+
basesById
|
|
3084
|
+
});
|
|
3085
|
+
const shadow = new ShadowReducerRuntime(
|
|
3086
|
+
modules,
|
|
3087
|
+
manifest,
|
|
3088
|
+
gameModule.default,
|
|
3089
|
+
createInitialTable,
|
|
3090
|
+
resolvedBase.seed,
|
|
3091
|
+
resolvedBase.players,
|
|
3092
|
+
effectiveSetup.setupProfileId,
|
|
3093
|
+
options.debug ?? false
|
|
3094
|
+
);
|
|
3095
|
+
if (base.definition.extends) {
|
|
3096
|
+
const parentArtifact = generatedBaseStates?.[baseStateKey(base.definition.extends)];
|
|
3097
|
+
if (!parentArtifact) {
|
|
3098
|
+
throw new Error(
|
|
3099
|
+
`Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing. Run 'dreamboard test generate' first.`
|
|
3100
|
+
);
|
|
2965
3101
|
}
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
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"
|
|
3102
|
+
shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
|
|
3103
|
+
}
|
|
3104
|
+
let remote;
|
|
3105
|
+
if (options.runner === "remote") {
|
|
3106
|
+
const compiledResultId = options.compiledResultId ?? options.projectConfig.compile?.latestSuccessful?.resultId;
|
|
3107
|
+
if (!compiledResultId) {
|
|
3108
|
+
throw new Error(
|
|
3109
|
+
"Remote runner requires a compiled result. Compile the workspace first."
|
|
2998
3110
|
);
|
|
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
3111
|
}
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
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.");
|
|
3112
|
+
const {
|
|
3113
|
+
data: session,
|
|
3114
|
+
error: sessionError,
|
|
3115
|
+
response: sessionResponse
|
|
3116
|
+
} = await createProjectSession({
|
|
3117
|
+
path: { projectId: options.projectConfig.projectId },
|
|
3118
|
+
body: {
|
|
3119
|
+
compiledResultId,
|
|
3120
|
+
seed: resolvedBase.seed,
|
|
3121
|
+
playerCount: resolvedBase.players,
|
|
3122
|
+
autoAssignSeats: true,
|
|
3123
|
+
setupProfileId: effectiveSetup.setupProfileId ?? void 0
|
|
3048
3124
|
}
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3125
|
+
});
|
|
3126
|
+
if (!session || sessionError) {
|
|
3127
|
+
throw toDreamboardApiError(
|
|
3128
|
+
sessionError,
|
|
3129
|
+
sessionResponse,
|
|
3130
|
+
"Failed to create remote-runner session."
|
|
3052
3131
|
);
|
|
3053
|
-
await waitForGameReady(page);
|
|
3054
|
-
browserBridge = await createBrowserBridgeClient(page);
|
|
3055
|
-
await browserDriver?.onReady?.(browserBridge);
|
|
3056
3132
|
}
|
|
3057
|
-
const
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
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
|
|
3133
|
+
const tracker = new RemoteGameplayTracker(
|
|
3134
|
+
session.sessionId,
|
|
3135
|
+
"player-1"
|
|
3136
|
+
);
|
|
3137
|
+
const { error: startError, response: startResponse } = await startGame({
|
|
3138
|
+
path: { sessionId: session.sessionId }
|
|
3078
3139
|
});
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3140
|
+
if (startError) {
|
|
3141
|
+
throw toDreamboardApiError(
|
|
3142
|
+
startError,
|
|
3143
|
+
startResponse,
|
|
3144
|
+
"Failed to start remote-runner session."
|
|
3084
3145
|
);
|
|
3085
3146
|
}
|
|
3086
|
-
|
|
3147
|
+
await tracker.bootstrap();
|
|
3148
|
+
remote = {
|
|
3149
|
+
sessionId: session.sessionId,
|
|
3150
|
+
tracker
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
if (options.runner === "browser") {
|
|
3154
|
+
const compiledResultId = options.compiledResultId ?? options.projectConfig.compile?.latestSuccessful?.resultId;
|
|
3155
|
+
if (!compiledResultId) {
|
|
3087
3156
|
throw new Error(
|
|
3088
|
-
|
|
3157
|
+
"Browser runner requires a compiled result. Compile the workspace first."
|
|
3089
3158
|
);
|
|
3090
3159
|
}
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3160
|
+
const {
|
|
3161
|
+
data: session,
|
|
3162
|
+
error: sessionError,
|
|
3163
|
+
response: sessionResponse
|
|
3164
|
+
} = await createProjectSession({
|
|
3165
|
+
path: { projectId: options.projectConfig.projectId },
|
|
3166
|
+
body: {
|
|
3167
|
+
compiledResultId,
|
|
3168
|
+
seed: resolvedBase.seed,
|
|
3169
|
+
playerCount: resolvedBase.players,
|
|
3170
|
+
autoAssignSeats: true,
|
|
3171
|
+
setupProfileId: effectiveSetup.setupProfileId ?? void 0
|
|
3172
|
+
}
|
|
3096
3173
|
});
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
scenarioFilePath: scenario.filePath
|
|
3107
|
-
}) : `Scenario '${scenario.definition.id}' failed.`
|
|
3174
|
+
if (!session || sessionError) {
|
|
3175
|
+
throw toDreamboardApiError(
|
|
3176
|
+
sessionError,
|
|
3177
|
+
sessionResponse,
|
|
3178
|
+
"Failed to create browser-runner session."
|
|
3179
|
+
);
|
|
3180
|
+
}
|
|
3181
|
+
const { error: startError, data: started } = await startGame({
|
|
3182
|
+
path: { sessionId: session.sessionId }
|
|
3108
3183
|
});
|
|
3184
|
+
if (startError || !started) {
|
|
3185
|
+
throw new Error("Failed to start browser-runner session.");
|
|
3186
|
+
}
|
|
3187
|
+
const devDir = path3.join(options.projectRoot, PROJECT_DIR_NAME, "dev");
|
|
3188
|
+
await ensureDir(devDir);
|
|
3189
|
+
const sessionFilePath = path3.join(devDir, "session.json");
|
|
3190
|
+
await writeJsonFile(
|
|
3191
|
+
sessionFilePath,
|
|
3192
|
+
createPersistedDevSession({ sessionId: session.sessionId })
|
|
3193
|
+
);
|
|
3194
|
+
const devHostPlatform = createCliDevHostPlatform(
|
|
3195
|
+
options.resolvedConfig
|
|
3196
|
+
);
|
|
3197
|
+
const bearer = await devHostPlatform.resolveBearer();
|
|
3198
|
+
if (bearer.kind === "permanent_invalid") {
|
|
3199
|
+
throw new Error(bearer.message);
|
|
3200
|
+
}
|
|
3201
|
+
const loadedDevHost = await loadProjectDevHost(options.projectRoot);
|
|
3202
|
+
scenarioDevHost = await loadedDevHost.module.start(
|
|
3203
|
+
{
|
|
3204
|
+
projectRoot: options.projectRoot,
|
|
3205
|
+
sessionFilePath,
|
|
3206
|
+
apiBaseUrl: options.resolvedConfig.apiBaseUrl,
|
|
3207
|
+
runtimeConfig: {
|
|
3208
|
+
apiBaseUrl: options.resolvedConfig.apiBaseUrl,
|
|
3209
|
+
userId: extractUserIdFromJwt(bearer.token),
|
|
3210
|
+
gameId: options.projectConfig.gameId,
|
|
3211
|
+
compiledResultId,
|
|
3212
|
+
setupProfileId: effectiveSetup.setupProfileId ?? null,
|
|
3213
|
+
playerCount: resolvedBase.players,
|
|
3214
|
+
debug: options.debug ?? false,
|
|
3215
|
+
slug: options.projectConfig.slug,
|
|
3216
|
+
autoStartGame: false,
|
|
3217
|
+
initialSession: {
|
|
3218
|
+
sessionId: session.sessionId,
|
|
3219
|
+
shortCode: started.context.shortCode,
|
|
3220
|
+
gameId: options.projectConfig.gameId,
|
|
3221
|
+
seed: resolvedBase.seed
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
},
|
|
3225
|
+
devHostPlatform
|
|
3226
|
+
);
|
|
3227
|
+
const opened = await openBrowserPage(
|
|
3228
|
+
scenarioDevHost.url,
|
|
3229
|
+
options.resolvedConfig
|
|
3230
|
+
);
|
|
3231
|
+
scenarioBrowser = opened.browser;
|
|
3232
|
+
const page = opened.page;
|
|
3233
|
+
await waitForGameReady(page);
|
|
3234
|
+
browserBridge = await createBrowserBridgeClient(page);
|
|
3235
|
+
await browserDriver?.onReady?.(browserBridge);
|
|
3236
|
+
}
|
|
3237
|
+
const context = await createScenarioContext({
|
|
3238
|
+
runner: options.runner,
|
|
3239
|
+
shadow,
|
|
3240
|
+
expect: modules.createExpectApi({
|
|
3241
|
+
matchSnapshot: createScenarioSnapshotMatcher({
|
|
3242
|
+
projectRoot: options.projectRoot,
|
|
3243
|
+
scenarioId: scenario.definition.id,
|
|
3244
|
+
updateSnapshots: options.updateSnapshots ?? false
|
|
3245
|
+
})
|
|
3246
|
+
}),
|
|
3247
|
+
remote,
|
|
3248
|
+
browser: options.runner === "browser" && browserBridge ? {
|
|
3249
|
+
bridge: browserBridge,
|
|
3250
|
+
driver: browserDriver
|
|
3251
|
+
} : void 0
|
|
3252
|
+
});
|
|
3253
|
+
await context.game.start();
|
|
3254
|
+
await base.definition.setup({
|
|
3255
|
+
game: context.game,
|
|
3256
|
+
players: context.players,
|
|
3257
|
+
seat: context.seat
|
|
3258
|
+
});
|
|
3259
|
+
shadow.clearHistory();
|
|
3260
|
+
await scenario.definition.when(context);
|
|
3261
|
+
if (scenario.definition.phase && shadow.phase() !== scenario.definition.phase) {
|
|
3262
|
+
throw new Error(
|
|
3263
|
+
`Scenario '${scenario.definition.id}' expected phase '${scenario.definition.phase}' but reached '${shadow.phase()}'.`
|
|
3264
|
+
);
|
|
3265
|
+
}
|
|
3266
|
+
if (scenario.definition.stage && shadow.currentStage() !== scenario.definition.stage) {
|
|
3267
|
+
throw new Error(
|
|
3268
|
+
`Scenario '${scenario.definition.id}' expected stage '${scenario.definition.stage}' but reached '${shadow.currentStage() ?? "null"}'.`
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3271
|
+
await scenario.definition.then(context);
|
|
3272
|
+
passed += 1;
|
|
3273
|
+
results.push({
|
|
3274
|
+
id: scenario.definition.id,
|
|
3275
|
+
success: true
|
|
3276
|
+
});
|
|
3277
|
+
} catch (error) {
|
|
3278
|
+
failed += 1;
|
|
3279
|
+
results.push({
|
|
3280
|
+
id: scenario.definition.id,
|
|
3281
|
+
success: false,
|
|
3282
|
+
errorCode: isStaleContractArtifactError(error) ? STALE_CONTRACT_ARTIFACT_CODE : void 0,
|
|
3283
|
+
error: error instanceof Error ? formatScenarioErrorForDisplay({
|
|
3284
|
+
error,
|
|
3285
|
+
projectRoot: options.projectRoot,
|
|
3286
|
+
scenarioFilePath: scenario.filePath
|
|
3287
|
+
}) : `Scenario '${scenario.definition.id}' failed.`
|
|
3288
|
+
});
|
|
3289
|
+
} finally {
|
|
3290
|
+
browserBridge = null;
|
|
3291
|
+
if (scenarioBrowser) {
|
|
3292
|
+
await scenarioBrowser.close();
|
|
3293
|
+
}
|
|
3294
|
+
if (scenarioDevHost) {
|
|
3295
|
+
await scenarioDevHost.close();
|
|
3109
3296
|
}
|
|
3110
|
-
}
|
|
3111
|
-
return { passed, failed, results };
|
|
3112
|
-
} finally {
|
|
3113
|
-
if (browser) {
|
|
3114
|
-
await browser.close();
|
|
3115
3297
|
}
|
|
3116
3298
|
}
|
|
3299
|
+
return { passed, failed, results };
|
|
3117
3300
|
}
|
|
3118
3301
|
|
|
3119
3302
|
export {
|
|
@@ -3134,4 +3317,4 @@ export {
|
|
|
3134
3317
|
generateReducerNativeArtifacts,
|
|
3135
3318
|
runReducerNativeScenarios
|
|
3136
3319
|
};
|
|
3137
|
-
//# sourceMappingURL=chunk-
|
|
3320
|
+
//# sourceMappingURL=chunk-DWLTCUUX.mjs.map
|