@dreamboard-games/cli 0.1.30-alpha.29 → 0.1.30-alpha.30
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 -1
- package/dist/agent-verifier/agent-workspace-verifier.mjs +360 -17
- package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -1
- package/dist/agent-verifier/{chunk-IWB4L2HV.mjs → chunk-FNSHNMDY.mjs} +51 -5
- package/dist/agent-verifier/chunk-FNSHNMDY.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-RDYXWXXC.mjs → chunk-LMW66VBH.mjs} +2 -11
- package/dist/agent-verifier/{chunk-RDYXWXXC.mjs.map → chunk-LMW66VBH.mjs.map} +1 -1
- package/dist/agent-verifier/{chunk-TIDX3YLW.mjs → chunk-M6YNQZCC.mjs} +2 -2
- package/dist/agent-verifier/{chunk-Z7UBAREF.mjs → chunk-QMOBTQ5G.mjs} +7 -9
- package/dist/agent-verifier/chunk-QMOBTQ5G.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-F2DIOJJZ.mjs → chunk-XCQQIPCO.mjs} +5 -46
- package/dist/agent-verifier/chunk-XCQQIPCO.mjs.map +1 -0
- package/dist/agent-verifier/{global-config-IXZLY4BS.mjs → global-config-SWWR2LP4.mjs} +3 -4
- package/dist/agent-verifier/{materialize-workspace-XYYCQQGB.mjs → materialize-workspace-K4WYFG5E.mjs} +5 -5
- package/dist/agent-verifier/{reducer-native-test-harness-BY5SZ7XE.mjs → reducer-native-test-harness-UFMSNNDY.mjs} +49 -576
- package/dist/agent-verifier/reducer-native-test-harness-UFMSNNDY.mjs.map +1 -0
- package/dist/agent-verifier/{static-scaffold-M7QPX76Z.mjs → static-scaffold-MHVM63HU.mjs} +4 -4
- package/dist/authoring-compatibility-internal.js +1 -1
- package/dist/{chunk-QIVDPQME.js → chunk-I4SZ7FA4.js} +9 -64
- package/dist/{chunk-QIVDPQME.js.map → chunk-I4SZ7FA4.js.map} +1 -1
- package/dist/{chunk-NFCRMXEV.js → chunk-RTNKVNQA.js} +52 -624
- package/dist/chunk-RTNKVNQA.js.map +1 -0
- package/dist/index.js +318 -369
- package/dist/index.js.map +1 -1
- package/dist/internal.js +23 -3
- package/dist/internal.js.map +1 -1
- package/package.json +1 -1
- package/release/authoring-release-set.json +2 -2
- package/skills/dreamboard/SKILL.md +1 -1
- package/skills/dreamboard/references/cli.md +2 -3
- package/skills/dreamboard/references/quickstart.md +1 -1
- package/skills/dreamboard/references/testing.md +0 -7
- package/dist/agent-verifier/chunk-B7M2TJSP.mjs +0 -363
- package/dist/agent-verifier/chunk-B7M2TJSP.mjs.map +0 -1
- package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +0 -1
- package/dist/agent-verifier/chunk-IWB4L2HV.mjs.map +0 -1
- package/dist/agent-verifier/chunk-UXGTT25Q.mjs +0 -59
- package/dist/agent-verifier/chunk-UXGTT25Q.mjs.map +0 -1
- package/dist/agent-verifier/chunk-Z7UBAREF.mjs.map +0 -1
- package/dist/agent-verifier/reducer-native-test-harness-BY5SZ7XE.mjs.map +0 -1
- package/dist/chunk-NFCRMXEV.js.map +0 -1
- /package/dist/agent-verifier/{chunk-TIDX3YLW.mjs.map → chunk-M6YNQZCC.mjs.map} +0 -0
- /package/dist/agent-verifier/{global-config-IXZLY4BS.mjs.map → global-config-SWWR2LP4.mjs.map} +0 -0
- /package/dist/agent-verifier/{materialize-workspace-XYYCQQGB.mjs.map → materialize-workspace-K4WYFG5E.mjs.map} +0 -0
- /package/dist/agent-verifier/{static-scaffold-M7QPX76Z.mjs.map → static-scaffold-MHVM63HU.mjs.map} +0 -0
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
buildReducerTestingContractContent,
|
|
7
7
|
client,
|
|
8
8
|
createGameplayCapability,
|
|
9
|
-
createProjectSession,
|
|
10
9
|
createProjectSessionFromReducerSnapshot,
|
|
11
10
|
createRepoLocalPackageResolutionPlugin,
|
|
12
11
|
external_exports,
|
|
@@ -21,12 +20,11 @@ import {
|
|
|
21
20
|
readWorkspaceTextFileIfExists,
|
|
22
21
|
resolveCliRepoRoot,
|
|
23
22
|
resolveWorkspacePath,
|
|
24
|
-
startGame,
|
|
25
23
|
workspacePathExists,
|
|
26
24
|
writeWorkspaceJsonFile,
|
|
27
25
|
writeWorkspaceTextFile,
|
|
28
26
|
zProblemDetails
|
|
29
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-I4SZ7FA4.js";
|
|
30
28
|
import {
|
|
31
29
|
clearCredentials,
|
|
32
30
|
getStoredSession,
|
|
@@ -40,8 +38,7 @@ import {
|
|
|
40
38
|
ENVIRONMENT_CONFIGS,
|
|
41
39
|
PROJECT_DIR_NAME,
|
|
42
40
|
ensureDir,
|
|
43
|
-
exists
|
|
44
|
-
writeJsonFile
|
|
41
|
+
exists
|
|
45
42
|
} from "./chunk-EQNBQVIW.js";
|
|
46
43
|
|
|
47
44
|
// src/build-target.ts
|
|
@@ -1132,23 +1129,6 @@ async function getCompiledResultSdk(gameId, compiledResultId) {
|
|
|
1132
1129
|
void compiledResultId;
|
|
1133
1130
|
throw new Error("Game-scoped compiled result lookup is no longer supported.");
|
|
1134
1131
|
}
|
|
1135
|
-
async function findProjectCompiledResultsForRevision(options) {
|
|
1136
|
-
const { projectId, revisionDigest } = options;
|
|
1137
|
-
const { data, error, response } = await listProjectCompiledResults({
|
|
1138
|
-
path: { projectId },
|
|
1139
|
-
query: { limit: 100 }
|
|
1140
|
-
});
|
|
1141
|
-
if (error || !data) {
|
|
1142
|
-
throw toDreamboardApiError(
|
|
1143
|
-
error,
|
|
1144
|
-
response,
|
|
1145
|
-
"Failed to list compiled results"
|
|
1146
|
-
);
|
|
1147
|
-
}
|
|
1148
|
-
return data.results.filter(
|
|
1149
|
-
(result) => result.revisionDigest === revisionDigest
|
|
1150
|
-
);
|
|
1151
|
-
}
|
|
1152
1132
|
async function getProjectCompiledResultSdk(projectId, compiledResultId) {
|
|
1153
1133
|
const { data, error, response } = await getProjectCompiledResult({
|
|
1154
1134
|
path: { projectId, compiledResultId }
|
|
@@ -1245,42 +1225,11 @@ function isTransientJobPollMessage(message) {
|
|
|
1245
1225
|
return normalized.includes("fetch failed") || normalized.includes("network") || normalized.includes("timeout") || normalized.includes("econnreset") || normalized.includes("econnrefused") || normalized.includes("socket");
|
|
1246
1226
|
}
|
|
1247
1227
|
|
|
1248
|
-
// src/ui/playwright-runner.ts
|
|
1249
|
-
import os from "os";
|
|
1250
|
-
import path2 from "path";
|
|
1251
|
-
function configurePlaywrightBrowsersPath() {
|
|
1252
|
-
if (process.env.PLAYWRIGHT_BROWSERS_PATH) {
|
|
1253
|
-
return;
|
|
1254
|
-
}
|
|
1255
|
-
const runtimeHome = process.env.HOME;
|
|
1256
|
-
let realHome;
|
|
1257
|
-
try {
|
|
1258
|
-
realHome = os.userInfo().homedir;
|
|
1259
|
-
} catch {
|
|
1260
|
-
return;
|
|
1261
|
-
}
|
|
1262
|
-
if (!runtimeHome || path2.resolve(runtimeHome) === path2.resolve(realHome)) {
|
|
1263
|
-
return;
|
|
1264
|
-
}
|
|
1265
|
-
const browserCachePath = process.platform === "darwin" ? path2.join(realHome, "Library", "Caches", "ms-playwright") : process.platform === "win32" ? path2.join(realHome, "AppData", "Local", "ms-playwright") : path2.join(realHome, ".cache", "ms-playwright");
|
|
1266
|
-
process.env.PLAYWRIGHT_BROWSERS_PATH = browserCachePath;
|
|
1267
|
-
}
|
|
1268
|
-
async function buildBrowserAuthInitScript(config) {
|
|
1269
|
-
const resolvedToken = resolveLocalHarnessAccessToken(config) ?? (await createUserTokenManager(config).resolveApiToken())?.token;
|
|
1270
|
-
if (!resolvedToken) return null;
|
|
1271
|
-
return `(function(){localStorage.setItem('dreamboard_auth_token',${JSON.stringify(resolvedToken)});})();`;
|
|
1272
|
-
}
|
|
1273
|
-
async function waitForGameReady(page, timeoutMs = 6e4) {
|
|
1274
|
-
await page.waitForSelector('iframe[title="Game UI Plugin"]', {
|
|
1275
|
-
timeout: timeoutMs
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
1228
|
// src/services/testing/reducer-native-test-harness.ts
|
|
1280
|
-
import
|
|
1229
|
+
import path3 from "path";
|
|
1281
1230
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1282
|
-
import { createRequire
|
|
1283
|
-
import { pathToFileURL as
|
|
1231
|
+
import { createRequire } from "module";
|
|
1232
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1284
1233
|
import {
|
|
1285
1234
|
existsSync,
|
|
1286
1235
|
mkdirSync,
|
|
@@ -1291,64 +1240,10 @@ import {
|
|
|
1291
1240
|
import { readdir } from "fs/promises";
|
|
1292
1241
|
import { isDeepStrictEqual } from "util";
|
|
1293
1242
|
|
|
1294
|
-
// src/utils/dev-session.ts
|
|
1295
|
-
import { randomInt } from "crypto";
|
|
1296
|
-
var DEFAULT_DEV_SEED = 1337;
|
|
1297
|
-
var MIN_KOTLIN_LONG = -9223372036854775808n;
|
|
1298
|
-
var MAX_KOTLIN_LONG = 9223372036854775807n;
|
|
1299
|
-
var MIN_SAFE_SEED = BigInt(Number.MIN_SAFE_INTEGER);
|
|
1300
|
-
var MAX_SAFE_SEED = BigInt(Number.MAX_SAFE_INTEGER);
|
|
1301
|
-
function createPersistedDevSession(input) {
|
|
1302
|
-
return {
|
|
1303
|
-
sessionId: input.sessionId
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
function parseDevSeed(rawSeed) {
|
|
1307
|
-
return parseOptionalDevSeed(rawSeed) ?? DEFAULT_DEV_SEED;
|
|
1308
|
-
}
|
|
1309
|
-
function parseOptionalDevSeed(rawSeed) {
|
|
1310
|
-
const value = rawSeed?.trim();
|
|
1311
|
-
if (!value) {
|
|
1312
|
-
return void 0;
|
|
1313
|
-
}
|
|
1314
|
-
if (!/^-?\d+$/.test(value)) {
|
|
1315
|
-
throw new Error("seed must be an integer");
|
|
1316
|
-
}
|
|
1317
|
-
const parsed = BigInt(value);
|
|
1318
|
-
if (parsed < MIN_KOTLIN_LONG || parsed > MAX_KOTLIN_LONG) {
|
|
1319
|
-
throw new Error("seed must be within signed 64-bit integer range");
|
|
1320
|
-
}
|
|
1321
|
-
if (parsed < MIN_SAFE_SEED || parsed > MAX_SAFE_SEED) {
|
|
1322
|
-
throw new Error(
|
|
1323
|
-
`seed must be within JavaScript safe integer range (${Number.MIN_SAFE_INTEGER}..${Number.MAX_SAFE_INTEGER})`
|
|
1324
|
-
);
|
|
1325
|
-
}
|
|
1326
|
-
return Number(parsed);
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
// src/utils/jwt.ts
|
|
1330
|
-
function extractUserIdFromJwt(token) {
|
|
1331
|
-
if (!token) {
|
|
1332
|
-
return null;
|
|
1333
|
-
}
|
|
1334
|
-
const parts = token.split(".");
|
|
1335
|
-
if (parts.length < 2) {
|
|
1336
|
-
return null;
|
|
1337
|
-
}
|
|
1338
|
-
try {
|
|
1339
|
-
const payload = JSON.parse(
|
|
1340
|
-
Buffer.from(parts[1], "base64url").toString("utf8")
|
|
1341
|
-
);
|
|
1342
|
-
return typeof payload.sub === "string" ? payload.sub : null;
|
|
1343
|
-
} catch {
|
|
1344
|
-
return null;
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
|
|
1348
1243
|
// src/utils/ts-module-loader.ts
|
|
1349
1244
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
1350
1245
|
import { tmpdir } from "os";
|
|
1351
|
-
import
|
|
1246
|
+
import path2 from "path";
|
|
1352
1247
|
import { pathToFileURL } from "url";
|
|
1353
1248
|
import { build } from "esbuild";
|
|
1354
1249
|
var ESBUILD_EXTERNALS = [
|
|
@@ -1362,14 +1257,14 @@ function resolveSourceCheckoutBuildContext() {
|
|
|
1362
1257
|
const repoRoot = resolveCliRepoRoot(import.meta.url);
|
|
1363
1258
|
return {
|
|
1364
1259
|
nodePaths: [
|
|
1365
|
-
|
|
1366
|
-
|
|
1260
|
+
path2.join(repoRoot, "apps", "dreamboard-cli", "node_modules"),
|
|
1261
|
+
path2.join(repoRoot, "node_modules")
|
|
1367
1262
|
],
|
|
1368
1263
|
plugins: [createRepoLocalPackageResolutionPlugin({ repoRoot })]
|
|
1369
1264
|
};
|
|
1370
1265
|
} catch {
|
|
1371
1266
|
return {
|
|
1372
|
-
nodePaths: [
|
|
1267
|
+
nodePaths: [path2.join(process.cwd(), "node_modules")],
|
|
1373
1268
|
plugins: []
|
|
1374
1269
|
};
|
|
1375
1270
|
}
|
|
@@ -1395,10 +1290,10 @@ async function bundleTypeScriptModuleText(entryPath, options = {}) {
|
|
|
1395
1290
|
return output.text;
|
|
1396
1291
|
}
|
|
1397
1292
|
async function importTypeScriptModule(entryPath) {
|
|
1398
|
-
const tempDir = await mkdtemp(
|
|
1399
|
-
const outfile =
|
|
1293
|
+
const tempDir = await mkdtemp(path2.join(tmpdir(), "dreamboard-ts-module-"));
|
|
1294
|
+
const outfile = path2.join(
|
|
1400
1295
|
tempDir,
|
|
1401
|
-
`${
|
|
1296
|
+
`${path2.basename(entryPath).replace(/\.[^.]+$/u, "")}.mjs`
|
|
1402
1297
|
);
|
|
1403
1298
|
try {
|
|
1404
1299
|
const bundledText = await bundleTypeScriptModuleText(entryPath);
|
|
@@ -1987,79 +1882,6 @@ async function submitGameplayAuthorityAction(options) {
|
|
|
1987
1882
|
}
|
|
1988
1883
|
}
|
|
1989
1884
|
|
|
1990
|
-
// src/services/dev-host/loader.ts
|
|
1991
|
-
import path4 from "path";
|
|
1992
|
-
import { createRequire } from "module";
|
|
1993
|
-
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1994
|
-
async function loadProjectDevHost(projectRoot) {
|
|
1995
|
-
const requireFromProject = createRequire(path4.join(projectRoot, "package.json"));
|
|
1996
|
-
let packageJsonPath;
|
|
1997
|
-
let entryPath;
|
|
1998
|
-
try {
|
|
1999
|
-
packageJsonPath = requireFromProject.resolve(
|
|
2000
|
-
"@dreamboard-games/dev-host/package.json"
|
|
2001
|
-
);
|
|
2002
|
-
entryPath = requireFromProject.resolve("@dreamboard-games/dev-host");
|
|
2003
|
-
} catch (error) {
|
|
2004
|
-
throw new Error(
|
|
2005
|
-
"Install @dreamboard-games/dev-host in this workspace before running dreamboard dev or browser tests.",
|
|
2006
|
-
{ cause: error }
|
|
2007
|
-
);
|
|
2008
|
-
}
|
|
2009
|
-
const packageRoot = path4.dirname(packageJsonPath);
|
|
2010
|
-
if (!isPathInside(packageRoot, entryPath)) {
|
|
2011
|
-
throw new Error(
|
|
2012
|
-
"@dreamboard-games/dev-host resolved outside its installed package."
|
|
2013
|
-
);
|
|
2014
|
-
}
|
|
2015
|
-
const packageJson = requireFromProject(packageJsonPath);
|
|
2016
|
-
if (packageJson.name !== "@dreamboard-games/dev-host" || typeof packageJson.version !== "string" || packageJson.version.length === 0) {
|
|
2017
|
-
throw new Error("Installed @dreamboard-games/dev-host metadata is invalid.");
|
|
2018
|
-
}
|
|
2019
|
-
const loaded = await import(pathToFileURL2(entryPath).href);
|
|
2020
|
-
if (loaded.protocolVersion !== 1 || typeof loaded.start !== "function") {
|
|
2021
|
-
throw new Error(
|
|
2022
|
-
"Installed @dreamboard-games/dev-host does not expose DevHostModuleV1."
|
|
2023
|
-
);
|
|
2024
|
-
}
|
|
2025
|
-
return {
|
|
2026
|
-
packageRoot,
|
|
2027
|
-
packageVersion: packageJson.version,
|
|
2028
|
-
module: {
|
|
2029
|
-
protocolVersion: loaded.protocolVersion,
|
|
2030
|
-
start: loaded.start
|
|
2031
|
-
}
|
|
2032
|
-
};
|
|
2033
|
-
}
|
|
2034
|
-
function isPathInside(parent, candidate) {
|
|
2035
|
-
const relativePath = path4.relative(parent, candidate);
|
|
2036
|
-
return relativePath === "" || !relativePath.startsWith("..") && !path4.isAbsolute(relativePath);
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
// src/services/dev-host/platform.ts
|
|
2040
|
-
function createCliDevHostPlatform(config) {
|
|
2041
|
-
return {
|
|
2042
|
-
resolveBearer: () => resolveDevHostBearer(config)
|
|
2043
|
-
};
|
|
2044
|
-
}
|
|
2045
|
-
async function resolveDevHostBearer(config) {
|
|
2046
|
-
const localHarnessToken = resolveLocalHarnessAccessToken(config);
|
|
2047
|
-
if (localHarnessToken) {
|
|
2048
|
-
return { kind: "ok", token: localHarnessToken };
|
|
2049
|
-
}
|
|
2050
|
-
if (config.refreshTokenSource !== "global") {
|
|
2051
|
-
return { kind: "ok", token: config.authToken ?? null };
|
|
2052
|
-
}
|
|
2053
|
-
if (!config.refreshToken) {
|
|
2054
|
-
return {
|
|
2055
|
-
kind: "permanent_invalid",
|
|
2056
|
-
message: "Stored Dreamboard session is expired or invalid. Run `dreamboard auth login` to authenticate again."
|
|
2057
|
-
};
|
|
2058
|
-
}
|
|
2059
|
-
const resolved = await createUserTokenManager(config).resolveApiToken();
|
|
2060
|
-
return { kind: "ok", token: resolved?.token ?? null };
|
|
2061
|
-
}
|
|
2062
|
-
|
|
2063
1885
|
// src/services/workflows/resolve-setup-profile.ts
|
|
2064
1886
|
function resolveSetupProfileSelection(options) {
|
|
2065
1887
|
const setupProfiles = options.manifest.setupProfiles ?? [];
|
|
@@ -2120,7 +1942,6 @@ function projectIdFromSessionGameSource(source) {
|
|
|
2120
1942
|
globalThis.__DREAMBOARD_AUTHORING_WARNINGS__ = true;
|
|
2121
1943
|
var GENERATED_TESTING_TYPES_PREFIX = "// Generated by dreamboard";
|
|
2122
1944
|
var TESTING_TYPES_STUB = "export function defineScenario(scenario) { return scenario; }\n";
|
|
2123
|
-
var DEFAULT_TIMEOUT_MS2 = 1e4;
|
|
2124
1945
|
var BASE_SUFFIX = ".base.ts";
|
|
2125
1946
|
var SCENARIO_SUFFIX = ".scenario.ts";
|
|
2126
1947
|
var SDK_UI_RUNTIME_EXTERNALS = [
|
|
@@ -2152,15 +1973,15 @@ function findScenarioStackFrame(options) {
|
|
|
2152
1973
|
if (!options.stack) {
|
|
2153
1974
|
return null;
|
|
2154
1975
|
}
|
|
2155
|
-
const absolutePath =
|
|
2156
|
-
const relativePath =
|
|
2157
|
-
const normalizedRelativePath = relativePath.split(
|
|
1976
|
+
const absolutePath = path3.resolve(options.scenarioFilePath);
|
|
1977
|
+
const relativePath = path3.relative(options.projectRoot, absolutePath);
|
|
1978
|
+
const normalizedRelativePath = relativePath.split(path3.sep).join("/");
|
|
2158
1979
|
const escapedAbsolutePath = escapeRegExp(absolutePath);
|
|
2159
1980
|
const escapedRelativePath = escapeRegExp(normalizedRelativePath);
|
|
2160
1981
|
const absoluteFrame = new RegExp(`${escapedAbsolutePath}:(\\d+):(\\d+)`);
|
|
2161
1982
|
const relativeFrame = new RegExp(`${escapedRelativePath}:(\\d+):(\\d+)`);
|
|
2162
1983
|
for (const line of options.stack.split("\n")) {
|
|
2163
|
-
const normalizedLine = line.split(
|
|
1984
|
+
const normalizedLine = line.split(path3.sep).join("/");
|
|
2164
1985
|
const match = normalizedLine.match(absoluteFrame) ?? normalizedLine.match(relativeFrame);
|
|
2165
1986
|
if (match?.[1] && match?.[2]) {
|
|
2166
1987
|
return `at ${normalizedRelativePath}:${match[1]}:${match[2]}`;
|
|
@@ -2173,17 +1994,17 @@ function escapeRegExp(value) {
|
|
|
2173
1994
|
}
|
|
2174
1995
|
var projectReducerNativeModules = /* @__PURE__ */ new Map();
|
|
2175
1996
|
function resolveProjectSdkModule(projectRoot, specifier) {
|
|
2176
|
-
const requireFromProject =
|
|
2177
|
-
|
|
1997
|
+
const requireFromProject = createRequire(
|
|
1998
|
+
path3.join(projectRoot, "package.json")
|
|
2178
1999
|
);
|
|
2179
2000
|
return requireFromProject.resolve(specifier);
|
|
2180
2001
|
}
|
|
2181
2002
|
async function importProjectSdkModule(projectRoot, specifier) {
|
|
2182
2003
|
const modulePath = resolveProjectSdkModule(projectRoot, specifier);
|
|
2183
|
-
return await import(
|
|
2004
|
+
return await import(pathToFileURL2(modulePath).href);
|
|
2184
2005
|
}
|
|
2185
2006
|
async function loadProjectReducerNativeModules(projectRoot) {
|
|
2186
|
-
const cacheKey =
|
|
2007
|
+
const cacheKey = path3.resolve(projectRoot);
|
|
2187
2008
|
const cached = projectReducerNativeModules.get(cacheKey);
|
|
2188
2009
|
if (cached) {
|
|
2189
2010
|
return cached;
|
|
@@ -2217,22 +2038,9 @@ function createSubmissionError(errorCode, message, fallbackMessage) {
|
|
|
2217
2038
|
error.errorCode = errorCode;
|
|
2218
2039
|
return error;
|
|
2219
2040
|
}
|
|
2220
|
-
function parseJsonValue(value) {
|
|
2221
|
-
if (typeof value !== "string") {
|
|
2222
|
-
return value;
|
|
2223
|
-
}
|
|
2224
|
-
try {
|
|
2225
|
-
return JSON.parse(value);
|
|
2226
|
-
} catch {
|
|
2227
|
-
return value;
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
2041
|
function deepEqual(left, right) {
|
|
2231
2042
|
return isDeepStrictEqual(left, right);
|
|
2232
2043
|
}
|
|
2233
|
-
function normalizeScenarioRunners(runners) {
|
|
2234
|
-
return runners && runners.length > 0 ? runners : ["reducer"];
|
|
2235
|
-
}
|
|
2236
2044
|
function shouldRefreshReducerTestingTypes(existingContent) {
|
|
2237
2045
|
if (existingContent === null || existingContent.trim().length === 0 || existingContent === TESTING_TYPES_STUB || existingContent.startsWith(GENERATED_TESTING_TYPES_PREFIX)) {
|
|
2238
2046
|
return true;
|
|
@@ -2246,7 +2054,7 @@ async function discoverFiles(root, suffix) {
|
|
|
2246
2054
|
const entries = await readdir(root, { withFileTypes: true });
|
|
2247
2055
|
const files = [];
|
|
2248
2056
|
for (const entry of entries) {
|
|
2249
|
-
const entryPath =
|
|
2057
|
+
const entryPath = path3.join(root, entry.name);
|
|
2250
2058
|
if (entry.isDirectory()) {
|
|
2251
2059
|
files.push(...await discoverFiles(entryPath, suffix));
|
|
2252
2060
|
continue;
|
|
@@ -2295,8 +2103,8 @@ function parseTypedScenarioDefinition(value) {
|
|
|
2295
2103
|
};
|
|
2296
2104
|
}
|
|
2297
2105
|
async function isReducerNativeTestingWorkspace(projectRoot) {
|
|
2298
|
-
return await exists(
|
|
2299
|
-
|
|
2106
|
+
return await exists(path3.join(projectRoot, "app", "game.ts")) && await exists(
|
|
2107
|
+
path3.join(projectRoot, "shared", "generated", "ui-contract.ts")
|
|
2300
2108
|
);
|
|
2301
2109
|
}
|
|
2302
2110
|
async function ensureReducerNativeTestingFiles(projectRoot) {
|
|
@@ -2388,7 +2196,7 @@ var DEFAULT_REJECTION_CODES = [
|
|
|
2388
2196
|
];
|
|
2389
2197
|
async function collectKnownRejectionCodes(projectRoot) {
|
|
2390
2198
|
const knownCodes = new Set(DEFAULT_REJECTION_CODES);
|
|
2391
|
-
const gamePath =
|
|
2199
|
+
const gamePath = path3.join(projectRoot, "app", "game.ts");
|
|
2392
2200
|
if (!await exists(gamePath)) {
|
|
2393
2201
|
return Array.from(knownCodes).sort(
|
|
2394
2202
|
(left, right) => left.localeCompare(right)
|
|
@@ -2454,8 +2262,8 @@ async function loadTypedScenarios(projectRoot, options) {
|
|
|
2454
2262
|
return loaded;
|
|
2455
2263
|
}
|
|
2456
2264
|
function reducerNativeTestHelperExternals(projectRoot, filePath) {
|
|
2457
|
-
const testingTypesPath =
|
|
2458
|
-
const relative =
|
|
2265
|
+
const testingTypesPath = path3.join(projectRoot, "test", "testing-types");
|
|
2266
|
+
const relative = path3.relative(path3.dirname(filePath), testingTypesPath).replaceAll("\\", "/");
|
|
2459
2267
|
const specifier = relative.startsWith(".") ? relative : `./${relative}`;
|
|
2460
2268
|
return [specifier, `${specifier}.ts`, ...SDK_UI_RUNTIME_EXTERNALS];
|
|
2461
2269
|
}
|
|
@@ -2829,170 +2637,6 @@ function toReducerInput(input) {
|
|
|
2829
2637
|
params: input.params
|
|
2830
2638
|
};
|
|
2831
2639
|
}
|
|
2832
|
-
var RemoteGameplayTracker = class {
|
|
2833
|
-
constructor(sessionId, playerId) {
|
|
2834
|
-
this.sessionId = sessionId;
|
|
2835
|
-
this.playerId = playerId;
|
|
2836
|
-
this.state.playerId = playerId;
|
|
2837
|
-
}
|
|
2838
|
-
state = {
|
|
2839
|
-
version: -1,
|
|
2840
|
-
actionSetVersion: "",
|
|
2841
|
-
currentPhase: null,
|
|
2842
|
-
playerId: "player-1",
|
|
2843
|
-
view: null,
|
|
2844
|
-
availableInteractions: []
|
|
2845
|
-
};
|
|
2846
|
-
liveBootstrap = false;
|
|
2847
|
-
async bootstrap() {
|
|
2848
|
-
const { data, error, response } = await getSessionSnapshot({
|
|
2849
|
-
path: { sessionId: this.sessionId },
|
|
2850
|
-
query: { playerId: this.playerId }
|
|
2851
|
-
});
|
|
2852
|
-
if (error || !data || data.type !== "gameplay") {
|
|
2853
|
-
throw toDreamboardApiError(
|
|
2854
|
-
error ?? { detail: "Gameplay snapshot was unavailable." },
|
|
2855
|
-
response,
|
|
2856
|
-
"Failed to load the remote gameplay bootstrap snapshot"
|
|
2857
|
-
);
|
|
2858
|
-
}
|
|
2859
|
-
if (data.gameplay.actionSetVersion === "authority") {
|
|
2860
|
-
return;
|
|
2861
|
-
}
|
|
2862
|
-
this.liveBootstrap = true;
|
|
2863
|
-
this.applyGameplay(data.gameplay);
|
|
2864
|
-
}
|
|
2865
|
-
hasLiveBootstrap() {
|
|
2866
|
-
return this.liveBootstrap;
|
|
2867
|
-
}
|
|
2868
|
-
snapshot() {
|
|
2869
|
-
return {
|
|
2870
|
-
...this.state,
|
|
2871
|
-
availableInteractions: [...this.state.availableInteractions]
|
|
2872
|
-
};
|
|
2873
|
-
}
|
|
2874
|
-
applyShadow(shadow) {
|
|
2875
|
-
const projection = shadow.projectAllSeats();
|
|
2876
|
-
const playerId = this.playerId;
|
|
2877
|
-
const seat = projection.seats[playerId];
|
|
2878
|
-
this.state.version = shadow.currentVersion();
|
|
2879
|
-
this.state.actionSetVersion = "";
|
|
2880
|
-
this.state.playerId = playerId;
|
|
2881
|
-
this.state.currentPhase = shadow.phase();
|
|
2882
|
-
this.state.view = seat?.view ?? null;
|
|
2883
|
-
this.state.availableInteractions = shadow.availableInteractionsForPlayer(playerId);
|
|
2884
|
-
}
|
|
2885
|
-
applyGameplay(gameplay) {
|
|
2886
|
-
const playerId = gameplay.perspectivePlayerId;
|
|
2887
|
-
const seat = gameplay.seats[playerId];
|
|
2888
|
-
this.state.version = gameplay.version;
|
|
2889
|
-
this.state.actionSetVersion = gameplay.actionSetVersion;
|
|
2890
|
-
this.state.playerId = playerId;
|
|
2891
|
-
this.state.currentPhase = gameplay.shared.currentPhase;
|
|
2892
|
-
this.state.view = parseSeatView(seat?.view ?? "null");
|
|
2893
|
-
this.state.availableInteractions = (seat?.availableInteractionRefs ?? []).map((ref) => gameplay.interactionsByRef[ref]).filter(
|
|
2894
|
-
(interaction) => Boolean(interaction)
|
|
2895
|
-
).map((interaction) => interaction.interactionId);
|
|
2896
|
-
}
|
|
2897
|
-
};
|
|
2898
|
-
function parseSeatView(raw) {
|
|
2899
|
-
return raw ? parseJsonValue(raw) : null;
|
|
2900
|
-
}
|
|
2901
|
-
function sleep3(ms) {
|
|
2902
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2903
|
-
}
|
|
2904
|
-
async function createBrowserBridgeClient(page) {
|
|
2905
|
-
const bridgeExists = async () => page.evaluate(
|
|
2906
|
-
() => Boolean(
|
|
2907
|
-
window.__dreamboardTestBridge__
|
|
2908
|
-
)
|
|
2909
|
-
);
|
|
2910
|
-
const startedAt = Date.now();
|
|
2911
|
-
while (!await bridgeExists()) {
|
|
2912
|
-
if (Date.now() - startedAt > DEFAULT_TIMEOUT_MS2) {
|
|
2913
|
-
const bodyText = await page.locator("body").innerText().catch(() => "");
|
|
2914
|
-
const diagnostic = bodyText.trim().replace(/\s+/g, " ").slice(0, 400);
|
|
2915
|
-
throw new Error(
|
|
2916
|
-
diagnostic.length > 0 ? `Timed out waiting for browser test bridge. Page text: ${diagnostic}` : "Timed out waiting for browser test bridge."
|
|
2917
|
-
);
|
|
2918
|
-
}
|
|
2919
|
-
await sleep3(50);
|
|
2920
|
-
}
|
|
2921
|
-
return {
|
|
2922
|
-
snapshot: () => page.evaluate(
|
|
2923
|
-
() => window.__dreamboardTestBridge__.snapshot()
|
|
2924
|
-
),
|
|
2925
|
-
submitInteraction: (playerId, interactionId, params) => page.evaluate(
|
|
2926
|
-
([nextPlayerId, nextInteractionId, nextParams]) => window.__dreamboardTestBridge__.submitInteraction(
|
|
2927
|
-
nextPlayerId,
|
|
2928
|
-
nextInteractionId,
|
|
2929
|
-
nextParams
|
|
2930
|
-
),
|
|
2931
|
-
[playerId, interactionId, params]
|
|
2932
|
-
),
|
|
2933
|
-
waitForVersionChange: async (previousVersion, timeoutMs = DEFAULT_TIMEOUT_MS2) => {
|
|
2934
|
-
const started = Date.now();
|
|
2935
|
-
while (Date.now() - started < timeoutMs) {
|
|
2936
|
-
const snapshot = await page.evaluate(
|
|
2937
|
-
() => window.__dreamboardTestBridge__.snapshot()
|
|
2938
|
-
);
|
|
2939
|
-
if (snapshot.version > previousVersion) {
|
|
2940
|
-
return snapshot;
|
|
2941
|
-
}
|
|
2942
|
-
await sleep3(50);
|
|
2943
|
-
}
|
|
2944
|
-
throw new Error("Timed out waiting for browser gameplay version change.");
|
|
2945
|
-
}
|
|
2946
|
-
};
|
|
2947
|
-
}
|
|
2948
|
-
async function assertLiveMatchesShadow(runner, shadow, live) {
|
|
2949
|
-
if (runner === "reducer") {
|
|
2950
|
-
return;
|
|
2951
|
-
}
|
|
2952
|
-
const currentPhase = "currentPhase" in live ? live.currentPhase : shadow.phase();
|
|
2953
|
-
if (currentPhase !== shadow.phase()) {
|
|
2954
|
-
throw new Error(
|
|
2955
|
-
`Live phase '${String(currentPhase)}' diverged from reducer phase '${shadow.phase()}'.`
|
|
2956
|
-
);
|
|
2957
|
-
}
|
|
2958
|
-
const livePlayerId = "playerId" in live ? live.playerId : live.controllingPlayerId;
|
|
2959
|
-
const liveView = "view" in live ? live.view : null;
|
|
2960
|
-
if (livePlayerId && !deepEqual(liveView, await shadow.view(livePlayerId))) {
|
|
2961
|
-
throw new Error("Live projected views diverged from reducer shadow views.");
|
|
2962
|
-
}
|
|
2963
|
-
const liveInteractions = "availableInteractions" in live ? live.availableInteractions : null;
|
|
2964
|
-
if (livePlayerId && liveInteractions && !deepEqual(
|
|
2965
|
-
liveInteractions,
|
|
2966
|
-
shadow.availableInteractionsForPlayer(livePlayerId)
|
|
2967
|
-
)) {
|
|
2968
|
-
throw new Error(
|
|
2969
|
-
"Live available-interactions metadata diverged from reducer shadow state."
|
|
2970
|
-
);
|
|
2971
|
-
}
|
|
2972
|
-
}
|
|
2973
|
-
async function loadBrowserDriver(projectRoot) {
|
|
2974
|
-
const filePath = path5.join(projectRoot, "test", "browser-driver.ts");
|
|
2975
|
-
if (!await exists(filePath)) {
|
|
2976
|
-
return null;
|
|
2977
|
-
}
|
|
2978
|
-
const module = await importTypeScriptModule(filePath);
|
|
2979
|
-
return module.default ?? module;
|
|
2980
|
-
}
|
|
2981
|
-
async function openBrowserPage(playUrl, config) {
|
|
2982
|
-
configurePlaywrightBrowsersPath();
|
|
2983
|
-
const { chromium } = await import("playwright");
|
|
2984
|
-
const browser = await chromium.launch({ headless: true });
|
|
2985
|
-
const context = await browser.newContext({
|
|
2986
|
-
viewport: { width: 1440, height: 900 }
|
|
2987
|
-
});
|
|
2988
|
-
const initScript = await buildBrowserAuthInitScript(config);
|
|
2989
|
-
if (initScript) {
|
|
2990
|
-
await context.addInitScript({ content: initScript });
|
|
2991
|
-
}
|
|
2992
|
-
const page = await context.newPage();
|
|
2993
|
-
await page.goto(playUrl, { waitUntil: "domcontentloaded" });
|
|
2994
|
-
return { browser, page };
|
|
2995
|
-
}
|
|
2996
2640
|
function sanitizeSnapshotSegment(value) {
|
|
2997
2641
|
return value.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
2998
2642
|
}
|
|
@@ -3008,7 +2652,7 @@ function createScenarioSnapshotMatcher(options) {
|
|
|
3008
2652
|
};
|
|
3009
2653
|
const serialized = `${JSON.stringify(wrappedValue, null, 2)}
|
|
3010
2654
|
`;
|
|
3011
|
-
mkdirSync(
|
|
2655
|
+
mkdirSync(path3.dirname(snapshotPath), { recursive: true });
|
|
3012
2656
|
if (!existsSync(snapshotPath) || options.updateSnapshots) {
|
|
3013
2657
|
writeFileSync(snapshotPath, serialized, "utf8");
|
|
3014
2658
|
return;
|
|
@@ -3016,7 +2660,7 @@ function createScenarioSnapshotMatcher(options) {
|
|
|
3016
2660
|
const previous = JSON.parse(readFileSync(snapshotPath, "utf8"));
|
|
3017
2661
|
if (!deepEqual(previous.value, actual)) {
|
|
3018
2662
|
throw new Error(
|
|
3019
|
-
`Snapshot mismatch for scenario '${options.scenarioId}'. Re-run with --update-snapshots to refresh ${
|
|
2663
|
+
`Snapshot mismatch for scenario '${options.scenarioId}'. Re-run with --update-snapshots to refresh ${path3.relative(options.projectRoot, snapshotPath)}.`
|
|
3020
2664
|
);
|
|
3021
2665
|
}
|
|
3022
2666
|
};
|
|
@@ -3025,24 +2669,12 @@ async function createScenarioContext(options) {
|
|
|
3025
2669
|
const api = {
|
|
3026
2670
|
start: async () => {
|
|
3027
2671
|
await options.shadow.start();
|
|
3028
|
-
if (options.runner === "remote" && options.remote) {
|
|
3029
|
-
if (options.remote.tracker.hasLiveBootstrap()) {
|
|
3030
|
-
const live = options.remote.tracker.snapshot();
|
|
3031
|
-
await assertLiveMatchesShadow("remote", options.shadow, live);
|
|
3032
|
-
} else {
|
|
3033
|
-
options.remote.tracker.applyShadow(options.shadow);
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
if (options.runner === "browser" && options.browser) {
|
|
3037
|
-
const live = await options.browser.bridge.snapshot();
|
|
3038
|
-
await assertLiveMatchesShadow("browser", options.shadow, live);
|
|
3039
|
-
}
|
|
3040
2672
|
},
|
|
3041
2673
|
patchState: async (mutator) => {
|
|
3042
2674
|
await api.start();
|
|
3043
|
-
if (options.live || options.
|
|
2675
|
+
if (options.live || options.actionPlan) {
|
|
3044
2676
|
throw new Error(
|
|
3045
|
-
"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
|
|
2677
|
+
"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."
|
|
3046
2678
|
);
|
|
3047
2679
|
}
|
|
3048
2680
|
options.shadow.patchState(mutator);
|
|
@@ -3051,7 +2683,7 @@ async function createScenarioContext(options) {
|
|
|
3051
2683
|
await api.start();
|
|
3052
2684
|
const interactionParams = params ?? {};
|
|
3053
2685
|
const normalizedInputs = interactionParams && typeof interactionParams === "object" && !Array.isArray(interactionParams) ? interactionParams : {};
|
|
3054
|
-
const previousVersion = options.live ? options.live.version : options.actionPlan ? options.shadow.currentVersion() :
|
|
2686
|
+
const previousVersion = options.live ? options.live.version : options.actionPlan ? options.shadow.currentVersion() : 0;
|
|
3055
2687
|
if (options.live) {
|
|
3056
2688
|
if (!options.live.actionSetVersion) {
|
|
3057
2689
|
options.live.actionSetVersion = await fetchLiveActionSetVersion(
|
|
@@ -3118,20 +2750,6 @@ async function createScenarioContext(options) {
|
|
|
3118
2750
|
}
|
|
3119
2751
|
options.live.version = typeof data?.version === "number" ? data.version : previousVersion + 1;
|
|
3120
2752
|
options.live.actionSetVersion = data?.actionSetVersion ?? options.live.actionSetVersion;
|
|
3121
|
-
} else if (options.runner === "remote" && options.remote) {
|
|
3122
|
-
} else if (options.runner === "browser" && options.browser) {
|
|
3123
|
-
const handled = await options.browser.driver?.interaction?.(options.browser.bridge, {
|
|
3124
|
-
playerId,
|
|
3125
|
-
interactionId,
|
|
3126
|
-
params: interactionParams
|
|
3127
|
-
}) === true;
|
|
3128
|
-
if (!handled) {
|
|
3129
|
-
await options.browser.bridge.submitInteraction(
|
|
3130
|
-
playerId,
|
|
3131
|
-
interactionId,
|
|
3132
|
-
interactionParams
|
|
3133
|
-
);
|
|
3134
|
-
}
|
|
3135
2753
|
}
|
|
3136
2754
|
await options.shadow.submitInteraction(
|
|
3137
2755
|
playerId,
|
|
@@ -3151,18 +2769,6 @@ async function createScenarioContext(options) {
|
|
|
3151
2769
|
});
|
|
3152
2770
|
options.actionPlan.submitIndex = (options.actionPlan.submitIndex ?? 0) + 1;
|
|
3153
2771
|
}
|
|
3154
|
-
if (options.runner === "remote" && options.remote) {
|
|
3155
|
-
options.remote.tracker.applyShadow(options.shadow);
|
|
3156
|
-
await assertLiveMatchesShadow(
|
|
3157
|
-
"remote",
|
|
3158
|
-
options.shadow,
|
|
3159
|
-
options.remote.tracker.snapshot()
|
|
3160
|
-
);
|
|
3161
|
-
}
|
|
3162
|
-
if (options.runner === "browser" && options.browser) {
|
|
3163
|
-
const live = await options.browser.bridge.waitForVersionChange(previousVersion);
|
|
3164
|
-
await assertLiveMatchesShadow("browser", options.shadow, live);
|
|
3165
|
-
}
|
|
3166
2772
|
}
|
|
3167
2773
|
};
|
|
3168
2774
|
return {
|
|
@@ -3281,7 +2887,7 @@ async function readMaterializedScenarioCache(options) {
|
|
|
3281
2887
|
}
|
|
3282
2888
|
async function writeMaterializedScenarioCache(options) {
|
|
3283
2889
|
const cachePath = materializedScenarioCacheFilePath(options);
|
|
3284
|
-
await ensureDir(
|
|
2890
|
+
await ensureDir(path3.dirname(cachePath));
|
|
3285
2891
|
await writeWorkspaceJsonFile(
|
|
3286
2892
|
options.projectRoot,
|
|
3287
2893
|
materializedScenarioCachePath(options),
|
|
@@ -3382,10 +2988,10 @@ async function materializeScenarioReducerState(options) {
|
|
|
3382
2988
|
}
|
|
3383
2989
|
const [gameModule, manifestContractModule, modules] = await Promise.all([
|
|
3384
2990
|
importTypeScriptModule(
|
|
3385
|
-
|
|
2991
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
3386
2992
|
),
|
|
3387
2993
|
importTypeScriptModule(
|
|
3388
|
-
|
|
2994
|
+
path3.join(options.projectRoot, "shared", "manifest-contract.ts")
|
|
3389
2995
|
),
|
|
3390
2996
|
loadProjectReducerNativeModules(options.projectRoot)
|
|
3391
2997
|
]);
|
|
@@ -3408,7 +3014,6 @@ async function materializeScenarioReducerState(options) {
|
|
|
3408
3014
|
);
|
|
3409
3015
|
shadow.hydrate(generatedBase.snapshot, generatedBase.version);
|
|
3410
3016
|
const context = await createScenarioContext({
|
|
3411
|
-
runner: "reducer",
|
|
3412
3017
|
shadow,
|
|
3413
3018
|
expect: modules.createExpectApi()
|
|
3414
3019
|
});
|
|
@@ -3538,7 +3143,7 @@ function writeProjectionSnapshots(options) {
|
|
|
3538
3143
|
function validateTypedScenarioBases(scenarios, basesById) {
|
|
3539
3144
|
const available = new Set(basesById.keys());
|
|
3540
3145
|
const invalid = scenarios.filter((scenario) => !available.has(scenario.definition.from)).map(
|
|
3541
|
-
(scenario) =>
|
|
3146
|
+
(scenario) => path3.relative(process.cwd(), scenario.filePath) + ` -> '${scenario.definition.from}'`
|
|
3542
3147
|
);
|
|
3543
3148
|
if (invalid.length > 0) {
|
|
3544
3149
|
throw new Error(
|
|
@@ -3552,12 +3157,12 @@ async function writeReducerNativeGeneratedFiles(options) {
|
|
|
3552
3157
|
const manifestHash = hashContent(JSON.stringify(manifest));
|
|
3553
3158
|
const appBundleHash = hashContent(
|
|
3554
3159
|
await bundleTypeScriptModuleText(
|
|
3555
|
-
|
|
3160
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
3556
3161
|
)
|
|
3557
3162
|
);
|
|
3558
3163
|
const uiContractHash = hashContent(
|
|
3559
3164
|
await bundleTypeScriptModuleText(
|
|
3560
|
-
|
|
3165
|
+
path3.join(options.projectRoot, "shared", "generated", "ui-contract.ts"),
|
|
3561
3166
|
{ external: SDK_UI_RUNTIME_EXTERNALS }
|
|
3562
3167
|
)
|
|
3563
3168
|
);
|
|
@@ -3569,10 +3174,10 @@ async function writeReducerNativeGeneratedFiles(options) {
|
|
|
3569
3174
|
const baseStates = {};
|
|
3570
3175
|
const [gameModule, manifestContractModule, modules] = await Promise.all([
|
|
3571
3176
|
importTypeScriptModule(
|
|
3572
|
-
|
|
3177
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
3573
3178
|
),
|
|
3574
3179
|
importTypeScriptModule(
|
|
3575
|
-
|
|
3180
|
+
path3.join(options.projectRoot, "shared", "manifest-contract.ts")
|
|
3576
3181
|
),
|
|
3577
3182
|
loadProjectReducerNativeModules(options.projectRoot)
|
|
3578
3183
|
]);
|
|
@@ -3627,7 +3232,6 @@ async function writeReducerNativeGeneratedFiles(options) {
|
|
|
3627
3232
|
shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
|
|
3628
3233
|
}
|
|
3629
3234
|
const context = await createScenarioContext({
|
|
3630
|
-
runner: "reducer",
|
|
3631
3235
|
shadow,
|
|
3632
3236
|
expect: modules.createExpectApi()
|
|
3633
3237
|
});
|
|
@@ -3693,9 +3297,8 @@ export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
|
|
|
3693
3297
|
`${header}export const SCENARIO_MANIFEST = ${JSON.stringify(
|
|
3694
3298
|
options.scenarios.map((scenario) => ({
|
|
3695
3299
|
id: scenario.definition.id,
|
|
3696
|
-
filePath:
|
|
3697
|
-
base: scenario.definition.from
|
|
3698
|
-
runners: normalizeScenarioRunners(scenario.definition.runners)
|
|
3300
|
+
filePath: path3.relative(generatedDir, scenario.filePath),
|
|
3301
|
+
base: scenario.definition.from
|
|
3699
3302
|
})),
|
|
3700
3303
|
null,
|
|
3701
3304
|
2
|
|
@@ -3714,7 +3317,7 @@ export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
|
|
|
3714
3317
|
);
|
|
3715
3318
|
}
|
|
3716
3319
|
async function loadGeneratedBaseStates(projectRoot) {
|
|
3717
|
-
const filePath =
|
|
3320
|
+
const filePath = path3.join(
|
|
3718
3321
|
projectRoot,
|
|
3719
3322
|
"test",
|
|
3720
3323
|
"generated",
|
|
@@ -3727,7 +3330,7 @@ async function loadGeneratedBaseStates(projectRoot) {
|
|
|
3727
3330
|
return module.BASE_STATES ?? null;
|
|
3728
3331
|
}
|
|
3729
3332
|
async function loadGeneratedScenarioManifest(projectRoot) {
|
|
3730
|
-
const filePath =
|
|
3333
|
+
const filePath = path3.join(
|
|
3731
3334
|
projectRoot,
|
|
3732
3335
|
"test",
|
|
3733
3336
|
"generated",
|
|
@@ -3791,7 +3394,7 @@ async function currentFingerprint(options) {
|
|
|
3791
3394
|
const manifest = await loadManifest(options.projectRoot);
|
|
3792
3395
|
const [gameModule, modules] = await Promise.all([
|
|
3793
3396
|
importTypeScriptModule(
|
|
3794
|
-
|
|
3397
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
3795
3398
|
),
|
|
3796
3399
|
loadProjectReducerNativeModules(options.projectRoot)
|
|
3797
3400
|
]);
|
|
@@ -3833,12 +3436,12 @@ async function currentFingerprint(options) {
|
|
|
3833
3436
|
manifestHash: hashContent(JSON.stringify(manifest)),
|
|
3834
3437
|
appBundleHash: hashContent(
|
|
3835
3438
|
await bundleTypeScriptModuleText(
|
|
3836
|
-
|
|
3439
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
3837
3440
|
)
|
|
3838
3441
|
),
|
|
3839
3442
|
uiContractHash: hashContent(
|
|
3840
3443
|
await bundleTypeScriptModuleText(
|
|
3841
|
-
|
|
3444
|
+
path3.join(options.projectRoot, "shared", "generated", "ui-contract.ts"),
|
|
3842
3445
|
{ external: SDK_UI_RUNTIME_EXTERNALS }
|
|
3843
3446
|
)
|
|
3844
3447
|
),
|
|
@@ -3888,10 +3491,10 @@ async function runReducerNativeScenarios(options) {
|
|
|
3888
3491
|
loadGeneratedBaseStates(options.projectRoot),
|
|
3889
3492
|
loadManifest(options.projectRoot),
|
|
3890
3493
|
importTypeScriptModule(
|
|
3891
|
-
|
|
3494
|
+
path3.join(options.projectRoot, "app", "game.ts")
|
|
3892
3495
|
),
|
|
3893
3496
|
importTypeScriptModule(
|
|
3894
|
-
|
|
3497
|
+
path3.join(options.projectRoot, "shared", "manifest-contract.ts")
|
|
3895
3498
|
),
|
|
3896
3499
|
loadProjectReducerNativeModules(options.projectRoot)
|
|
3897
3500
|
]);
|
|
@@ -3910,25 +3513,10 @@ async function runReducerNativeScenarios(options) {
|
|
|
3910
3513
|
});
|
|
3911
3514
|
generatedBaseStates = await loadGeneratedBaseStates(options.projectRoot);
|
|
3912
3515
|
}
|
|
3913
|
-
const runnerScenarios = scenarios.filter(
|
|
3914
|
-
(scenario) => normalizeScenarioRunners(scenario.definition.runners).includes(
|
|
3915
|
-
options.runner
|
|
3916
|
-
)
|
|
3917
|
-
);
|
|
3918
|
-
if (runnerScenarios.length === 0) {
|
|
3919
|
-
throw new Error(
|
|
3920
|
-
`No scenarios found for runner '${options.runner}'. Add runners: ["${options.runner}"] to at least one scenario in test/scenarios/*.scenario.ts.`
|
|
3921
|
-
);
|
|
3922
|
-
}
|
|
3923
|
-
let browserBridge = null;
|
|
3924
|
-
let browserDriver = null;
|
|
3925
|
-
if (options.runner === "browser") {
|
|
3926
|
-
browserDriver = await loadBrowserDriver(options.projectRoot);
|
|
3927
|
-
}
|
|
3928
3516
|
let passed = 0;
|
|
3929
3517
|
let failed = 0;
|
|
3930
3518
|
const results = [];
|
|
3931
|
-
for (const scenario of
|
|
3519
|
+
for (const scenario of scenarios) {
|
|
3932
3520
|
const base = basesById.get(scenario.definition.from);
|
|
3933
3521
|
if (!base) {
|
|
3934
3522
|
throw new Error(
|
|
@@ -3946,13 +3534,7 @@ async function runReducerNativeScenarios(options) {
|
|
|
3946
3534
|
gameId: options.gameId
|
|
3947
3535
|
})
|
|
3948
3536
|
});
|
|
3949
|
-
} else if (options.runner !== "reducer") {
|
|
3950
|
-
throw new Error(
|
|
3951
|
-
"Missing reducer-native generated base artifacts. Run 'dreamboard test' first."
|
|
3952
|
-
);
|
|
3953
3537
|
}
|
|
3954
|
-
let scenarioBrowser = null;
|
|
3955
|
-
let scenarioDevHost = null;
|
|
3956
3538
|
try {
|
|
3957
3539
|
const resolvedBase = resolveBaseDefinition(base, basesById);
|
|
3958
3540
|
const effectiveSetup = resolveEffectiveBaseSetup({
|
|
@@ -3979,141 +3561,7 @@ async function runReducerNativeScenarios(options) {
|
|
|
3979
3561
|
}
|
|
3980
3562
|
shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
|
|
3981
3563
|
}
|
|
3982
|
-
let remote;
|
|
3983
|
-
if (options.runner === "remote") {
|
|
3984
|
-
const compiledResultId = options.compiledResultId ?? options.projectConfig.compile?.latestSuccessful?.resultId;
|
|
3985
|
-
if (!compiledResultId) {
|
|
3986
|
-
throw new Error(
|
|
3987
|
-
"Remote runner requires a compiled result. Compile the workspace first."
|
|
3988
|
-
);
|
|
3989
|
-
}
|
|
3990
|
-
const {
|
|
3991
|
-
data: session,
|
|
3992
|
-
error: sessionError,
|
|
3993
|
-
response: sessionResponse
|
|
3994
|
-
} = await createProjectSession({
|
|
3995
|
-
path: { projectId: options.projectConfig.projectId },
|
|
3996
|
-
body: {
|
|
3997
|
-
compiledResultId,
|
|
3998
|
-
seed: resolvedBase.seed,
|
|
3999
|
-
playerCount: resolvedBase.players,
|
|
4000
|
-
autoAssignSeats: true,
|
|
4001
|
-
setupProfileId: effectiveSetup.setupProfileId ?? void 0
|
|
4002
|
-
}
|
|
4003
|
-
});
|
|
4004
|
-
if (!session || sessionError) {
|
|
4005
|
-
throw toDreamboardApiError(
|
|
4006
|
-
sessionError,
|
|
4007
|
-
sessionResponse,
|
|
4008
|
-
"Failed to create remote-runner session."
|
|
4009
|
-
);
|
|
4010
|
-
}
|
|
4011
|
-
const tracker = new RemoteGameplayTracker(
|
|
4012
|
-
session.sessionId,
|
|
4013
|
-
"player-1"
|
|
4014
|
-
);
|
|
4015
|
-
const { error: startError, response: startResponse } = await startGame({
|
|
4016
|
-
path: { sessionId: session.sessionId }
|
|
4017
|
-
});
|
|
4018
|
-
if (startError) {
|
|
4019
|
-
throw toDreamboardApiError(
|
|
4020
|
-
startError,
|
|
4021
|
-
startResponse,
|
|
4022
|
-
"Failed to start remote-runner session."
|
|
4023
|
-
);
|
|
4024
|
-
}
|
|
4025
|
-
await tracker.bootstrap();
|
|
4026
|
-
remote = {
|
|
4027
|
-
sessionId: session.sessionId,
|
|
4028
|
-
tracker
|
|
4029
|
-
};
|
|
4030
|
-
}
|
|
4031
|
-
if (options.runner === "browser") {
|
|
4032
|
-
const compiledResultId = options.compiledResultId ?? options.projectConfig.compile?.latestSuccessful?.resultId;
|
|
4033
|
-
if (!compiledResultId) {
|
|
4034
|
-
throw new Error(
|
|
4035
|
-
"Browser runner requires a compiled result. Compile the workspace first."
|
|
4036
|
-
);
|
|
4037
|
-
}
|
|
4038
|
-
const {
|
|
4039
|
-
data: session,
|
|
4040
|
-
error: sessionError,
|
|
4041
|
-
response: sessionResponse
|
|
4042
|
-
} = await createProjectSession({
|
|
4043
|
-
path: { projectId: options.projectConfig.projectId },
|
|
4044
|
-
body: {
|
|
4045
|
-
compiledResultId,
|
|
4046
|
-
seed: resolvedBase.seed,
|
|
4047
|
-
playerCount: resolvedBase.players,
|
|
4048
|
-
autoAssignSeats: true,
|
|
4049
|
-
setupProfileId: effectiveSetup.setupProfileId ?? void 0
|
|
4050
|
-
}
|
|
4051
|
-
});
|
|
4052
|
-
if (!session || sessionError) {
|
|
4053
|
-
throw toDreamboardApiError(
|
|
4054
|
-
sessionError,
|
|
4055
|
-
sessionResponse,
|
|
4056
|
-
"Failed to create browser-runner session."
|
|
4057
|
-
);
|
|
4058
|
-
}
|
|
4059
|
-
const { error: startError, data: started } = await startGame({
|
|
4060
|
-
path: { sessionId: session.sessionId }
|
|
4061
|
-
});
|
|
4062
|
-
if (startError || !started) {
|
|
4063
|
-
throw new Error("Failed to start browser-runner session.");
|
|
4064
|
-
}
|
|
4065
|
-
const devDir = path5.join(options.projectRoot, PROJECT_DIR_NAME, "dev");
|
|
4066
|
-
await ensureDir(devDir);
|
|
4067
|
-
const sessionFilePath = path5.join(devDir, "session.json");
|
|
4068
|
-
await writeJsonFile(
|
|
4069
|
-
sessionFilePath,
|
|
4070
|
-
createPersistedDevSession({ sessionId: session.sessionId })
|
|
4071
|
-
);
|
|
4072
|
-
const devHostPlatform = createCliDevHostPlatform(
|
|
4073
|
-
options.resolvedConfig
|
|
4074
|
-
);
|
|
4075
|
-
const bearer = await devHostPlatform.resolveBearer();
|
|
4076
|
-
if (bearer.kind === "permanent_invalid") {
|
|
4077
|
-
throw new Error(bearer.message);
|
|
4078
|
-
}
|
|
4079
|
-
const loadedDevHost = await loadProjectDevHost(options.projectRoot);
|
|
4080
|
-
scenarioDevHost = await loadedDevHost.module.start(
|
|
4081
|
-
{
|
|
4082
|
-
projectRoot: options.projectRoot,
|
|
4083
|
-
sessionFilePath,
|
|
4084
|
-
apiBaseUrl: options.resolvedConfig.apiBaseUrl,
|
|
4085
|
-
runtimeConfig: {
|
|
4086
|
-
apiBaseUrl: options.resolvedConfig.apiBaseUrl,
|
|
4087
|
-
userId: extractUserIdFromJwt(bearer.token),
|
|
4088
|
-
gameId: options.projectConfig.gameId,
|
|
4089
|
-
compiledResultId,
|
|
4090
|
-
setupProfileId: effectiveSetup.setupProfileId ?? null,
|
|
4091
|
-
playerCount: resolvedBase.players,
|
|
4092
|
-
debug: options.debug ?? false,
|
|
4093
|
-
slug: options.projectConfig.slug,
|
|
4094
|
-
autoStartGame: false,
|
|
4095
|
-
initialSession: {
|
|
4096
|
-
sessionId: session.sessionId,
|
|
4097
|
-
shortCode: started.context.shortCode,
|
|
4098
|
-
gameId: options.projectConfig.gameId,
|
|
4099
|
-
seed: resolvedBase.seed
|
|
4100
|
-
}
|
|
4101
|
-
}
|
|
4102
|
-
},
|
|
4103
|
-
devHostPlatform
|
|
4104
|
-
);
|
|
4105
|
-
const opened = await openBrowserPage(
|
|
4106
|
-
scenarioDevHost.url,
|
|
4107
|
-
options.resolvedConfig
|
|
4108
|
-
);
|
|
4109
|
-
scenarioBrowser = opened.browser;
|
|
4110
|
-
const page = opened.page;
|
|
4111
|
-
await waitForGameReady(page);
|
|
4112
|
-
browserBridge = await createBrowserBridgeClient(page);
|
|
4113
|
-
await browserDriver?.onReady?.(browserBridge);
|
|
4114
|
-
}
|
|
4115
3564
|
const context = await createScenarioContext({
|
|
4116
|
-
runner: options.runner,
|
|
4117
3565
|
shadow,
|
|
4118
3566
|
expect: modules.createExpectApi({
|
|
4119
3567
|
matchSnapshot: createScenarioSnapshotMatcher({
|
|
@@ -4121,12 +3569,7 @@ async function runReducerNativeScenarios(options) {
|
|
|
4121
3569
|
scenarioId: scenario.definition.id,
|
|
4122
3570
|
updateSnapshots: options.updateSnapshots ?? false
|
|
4123
3571
|
})
|
|
4124
|
-
})
|
|
4125
|
-
remote,
|
|
4126
|
-
browser: options.runner === "browser" && browserBridge ? {
|
|
4127
|
-
bridge: browserBridge,
|
|
4128
|
-
driver: browserDriver
|
|
4129
|
-
} : void 0
|
|
3572
|
+
})
|
|
4130
3573
|
});
|
|
4131
3574
|
await context.game.start();
|
|
4132
3575
|
await base.definition.setup({
|
|
@@ -4165,13 +3608,6 @@ async function runReducerNativeScenarios(options) {
|
|
|
4165
3608
|
}) : `Scenario '${scenario.definition.id}' failed.`
|
|
4166
3609
|
});
|
|
4167
3610
|
} finally {
|
|
4168
|
-
browserBridge = null;
|
|
4169
|
-
if (scenarioBrowser) {
|
|
4170
|
-
await scenarioBrowser.close();
|
|
4171
|
-
}
|
|
4172
|
-
if (scenarioDevHost) {
|
|
4173
|
-
await scenarioDevHost.close();
|
|
4174
|
-
}
|
|
4175
3611
|
}
|
|
4176
3612
|
}
|
|
4177
3613
|
return { passed, failed, results };
|
|
@@ -4179,7 +3615,7 @@ async function runReducerNativeScenarios(options) {
|
|
|
4179
3615
|
|
|
4180
3616
|
// src/services/project/local-maintainer-registry-shared.ts
|
|
4181
3617
|
import crypto2 from "crypto";
|
|
4182
|
-
import
|
|
3618
|
+
import path4 from "path";
|
|
4183
3619
|
var localRegistryAddress = readLocalRegistryAddress();
|
|
4184
3620
|
var LOCAL_REGISTRY_HOST = localRegistryAddress.host;
|
|
4185
3621
|
var LOCAL_REGISTRY_PORT = localRegistryAddress.port;
|
|
@@ -4282,16 +3718,8 @@ export {
|
|
|
4282
3718
|
titleFromSlug,
|
|
4283
3719
|
parsePositiveInt,
|
|
4284
3720
|
findCompiledResultsForAuthoringState,
|
|
4285
|
-
findProjectCompiledResultsForRevision,
|
|
4286
|
-
getProjectCompiledResultSdk,
|
|
4287
3721
|
waitForCompiledResultJobSdk,
|
|
4288
|
-
createPersistedDevSession,
|
|
4289
|
-
parseDevSeed,
|
|
4290
|
-
extractUserIdFromJwt,
|
|
4291
|
-
loadProjectDevHost,
|
|
4292
|
-
createCliDevHostPlatform,
|
|
4293
3722
|
importTypeScriptModule,
|
|
4294
|
-
configurePlaywrightBrowsersPath,
|
|
4295
3723
|
resolveSetupProfileSelectionForSession,
|
|
4296
3724
|
projectIdFromSessionGameSource,
|
|
4297
3725
|
isReducerNativeTestingWorkspace,
|
|
@@ -4304,4 +3732,4 @@ export {
|
|
|
4304
3732
|
isLocalMaintainerRegistryEnabled,
|
|
4305
3733
|
didLocalMaintainerSnapshotChange
|
|
4306
3734
|
};
|
|
4307
|
-
//# sourceMappingURL=chunk-
|
|
3735
|
+
//# sourceMappingURL=chunk-RTNKVNQA.js.map
|