@dreamboard-games/cli 0.1.30-alpha.19 → 0.1.30-alpha.20
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 +22 -102
- package/dist/agent-verifier/agent-workspace-verifier.mjs +1508 -55
- package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -1
- package/dist/agent-verifier/{chunk-V7ABTZXW.mjs → chunk-4I2WWAPK.mjs} +26 -7
- package/dist/agent-verifier/chunk-4I2WWAPK.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-7LFDFXLS.mjs → chunk-BWBN2TDJ.mjs} +338 -133
- package/dist/agent-verifier/chunk-BWBN2TDJ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-AWZ4M4NS.mjs → chunk-DQUYBIGQ.mjs} +5 -6
- package/dist/agent-verifier/chunk-DQUYBIGQ.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-H5L4KK4Y.mjs → chunk-GCFGAFYC.mjs} +7 -7
- package/dist/agent-verifier/chunk-GCFGAFYC.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-TAEQKBJB.mjs → chunk-GWRZRWCF.mjs} +1 -1
- package/dist/agent-verifier/chunk-GWRZRWCF.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-6AKXIY37.mjs → chunk-IWB4L2HV.mjs} +3 -3
- package/dist/agent-verifier/chunk-IWB4L2HV.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-334H4LE4.mjs → chunk-KDAQ4CZY.mjs} +3 -3
- package/dist/agent-verifier/{chunk-WSIYUUSD.mjs → chunk-TIDX3YLW.mjs} +2 -2
- package/dist/agent-verifier/{chunk-WSIYUUSD.mjs.map → chunk-TIDX3YLW.mjs.map} +1 -1
- package/dist/agent-verifier/{chunk-UMW24KZI.mjs → chunk-UXGTT25Q.mjs} +3 -3
- package/dist/agent-verifier/{global-config-SXR6X3OZ.mjs → global-config-IXZLY4BS.mjs} +4 -4
- package/dist/agent-verifier/{local-files-DAFIR7SN.mjs → local-files-OF4QFISU.mjs} +4 -4
- package/dist/agent-verifier/{chunk-POBFNXD4.mjs → local-typecheck-DHVLM37Z.mjs} +3 -3
- package/dist/agent-verifier/local-typecheck-DHVLM37Z.mjs.map +1 -0
- package/dist/agent-verifier/{materialize-workspace-PWNT6HQK.mjs → materialize-workspace-VS5RHSBO.mjs} +7 -7
- package/dist/agent-verifier/{chunk-HLHT57AW.mjs → reducer-bundle-preflight-GLUJKTWU.mjs} +13 -10
- package/dist/agent-verifier/reducer-bundle-preflight-GLUJKTWU.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-INIK6LHK.mjs → reducer-contract-preflight-WVQQPW5F.mjs} +6 -5
- package/dist/agent-verifier/reducer-contract-preflight-WVQQPW5F.mjs.map +1 -0
- package/dist/agent-verifier/{chunk-3Y4FRMTK.mjs → reducer-native-test-harness-H6G6RBRY.mjs} +150 -34
- package/dist/agent-verifier/reducer-native-test-harness-H6G6RBRY.mjs.map +1 -0
- package/dist/agent-verifier/{static-scaffold-HXUQLJVN.mjs → static-scaffold-C36KROJA.mjs} +3 -3
- package/dist/agent-verifier/{workspace-dependencies-ZMHPHVQV.mjs → workspace-dependencies-5HEEKZFP.mjs} +4 -2
- package/dist/authoring-compatibility-internal.js +2 -2
- package/dist/{chunk-R6RB4EKH.js → chunk-5PJWUB6W.js} +179 -210
- package/dist/chunk-5PJWUB6W.js.map +1 -0
- package/dist/{chunk-YRSE5DLH.js → chunk-EJGB3IR7.js} +422 -264
- package/dist/chunk-EJGB3IR7.js.map +1 -0
- package/dist/{chunk-FFO2IJL3.js → chunk-EQNBQVIW.js} +1 -1
- package/dist/chunk-EQNBQVIW.js.map +1 -0
- package/dist/{chunk-VWMKJL4A.js → chunk-UI7NWSYA.js} +3 -3
- package/dist/chunk-UI7NWSYA.js.map +1 -0
- package/dist/{global-config-UHGWFJIK.js → global-config-GK2UC2X6.js} +3 -3
- package/dist/index.js +2691 -3953
- package/dist/index.js.map +1 -1
- package/dist/internal.js +4 -4
- package/package.json +1 -1
- package/release/authoring-release-set.json +2 -2
- package/skills/dreamboard/SKILL.md +30 -28
- package/skills/dreamboard/references/building-your-first-game.md +15 -15
- package/skills/dreamboard/references/cli.md +48 -47
- package/skills/dreamboard/references/quickstart.md +16 -13
- package/skills/dreamboard/references/testing.md +10 -10
- package/dist/agent-verifier/chunk-3Y4FRMTK.mjs.map +0 -1
- package/dist/agent-verifier/chunk-5D3OJBDT.mjs +0 -1547
- package/dist/agent-verifier/chunk-5D3OJBDT.mjs.map +0 -1
- package/dist/agent-verifier/chunk-6AKXIY37.mjs.map +0 -1
- package/dist/agent-verifier/chunk-7LFDFXLS.mjs.map +0 -1
- package/dist/agent-verifier/chunk-AWZ4M4NS.mjs.map +0 -1
- package/dist/agent-verifier/chunk-H5L4KK4Y.mjs.map +0 -1
- package/dist/agent-verifier/chunk-HLHT57AW.mjs.map +0 -1
- package/dist/agent-verifier/chunk-INIK6LHK.mjs.map +0 -1
- package/dist/agent-verifier/chunk-LEWM26XR.mjs +0 -618
- package/dist/agent-verifier/chunk-LEWM26XR.mjs.map +0 -1
- package/dist/agent-verifier/chunk-PLXXH5LY.mjs +0 -222
- package/dist/agent-verifier/chunk-PLXXH5LY.mjs.map +0 -1
- package/dist/agent-verifier/chunk-POBFNXD4.mjs.map +0 -1
- package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +0 -1
- package/dist/agent-verifier/chunk-V7ABTZXW.mjs.map +0 -1
- package/dist/agent-verifier/chunk-ZOR5FTIG.mjs +0 -39
- package/dist/agent-verifier/chunk-ZOR5FTIG.mjs.map +0 -1
- package/dist/agent-verifier/compile-MO2URO5Z.mjs +0 -317
- package/dist/agent-verifier/compile-MO2URO5Z.mjs.map +0 -1
- package/dist/agent-verifier/local-typecheck-3JXL2NMG.mjs +0 -10
- package/dist/agent-verifier/reducer-bundle-preflight-3DSXIELT.mjs +0 -20
- package/dist/agent-verifier/reducer-contract-preflight-FQB7M4PU.mjs +0 -11
- package/dist/agent-verifier/reducer-native-test-harness-X2KQYSCD.mjs +0 -53
- package/dist/agent-verifier/reducer-native-test-harness-X2KQYSCD.mjs.map +0 -1
- package/dist/agent-verifier/static-scaffold-HXUQLJVN.mjs.map +0 -1
- package/dist/agent-verifier/sync-5YM4CSXL.mjs +0 -598
- package/dist/agent-verifier/sync-5YM4CSXL.mjs.map +0 -1
- package/dist/agent-verifier/test-CNNVTFIG.mjs +0 -356
- package/dist/agent-verifier/test-CNNVTFIG.mjs.map +0 -1
- package/dist/agent-verifier/workspace-dependencies-ZMHPHVQV.mjs.map +0 -1
- package/dist/chunk-FFO2IJL3.js.map +0 -1
- package/dist/chunk-R6RB4EKH.js.map +0 -1
- package/dist/chunk-VWMKJL4A.js.map +0 -1
- package/dist/chunk-YRSE5DLH.js.map +0 -1
- package/dist/global-config-UHGWFJIK.js.map +0 -1
- package/skills/dreamboard/scripts/events-extract.mjs +0 -218
- /package/dist/agent-verifier/{chunk-334H4LE4.mjs.map → chunk-KDAQ4CZY.mjs.map} +0 -0
- /package/dist/agent-verifier/{chunk-UMW24KZI.mjs.map → chunk-UXGTT25Q.mjs.map} +0 -0
- /package/dist/agent-verifier/{global-config-SXR6X3OZ.mjs.map → global-config-IXZLY4BS.mjs.map} +0 -0
- /package/dist/agent-verifier/{local-files-DAFIR7SN.mjs.map → local-files-OF4QFISU.mjs.map} +0 -0
- /package/dist/agent-verifier/{materialize-workspace-PWNT6HQK.mjs.map → materialize-workspace-VS5RHSBO.mjs.map} +0 -0
- /package/dist/agent-verifier/{local-typecheck-3JXL2NMG.mjs.map → static-scaffold-C36KROJA.mjs.map} +0 -0
- /package/dist/agent-verifier/{reducer-bundle-preflight-3DSXIELT.mjs.map → workspace-dependencies-5HEEKZFP.mjs.map} +0 -0
- /package/dist/{agent-verifier/reducer-contract-preflight-FQB7M4PU.mjs.map → global-config-GK2UC2X6.js.map} +0 -0
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
external_exports
|
|
7
7
|
} from "./chunk-JZTH3EMV.mjs";
|
|
8
|
+
import "./chunk-TTB7AIHZ.mjs";
|
|
9
|
+
import "./chunk-H6XDQJ3N.mjs";
|
|
8
10
|
|
|
9
11
|
// src/services/project/reducer-bundle-preflight.ts
|
|
10
12
|
import path from "path";
|
|
@@ -27,7 +29,9 @@ async function loadProjectReducerPreflightModules(projectRoot) {
|
|
|
27
29
|
const requireFromProject = createRequire(
|
|
28
30
|
path.join(projectRoot, "package.json")
|
|
29
31
|
);
|
|
30
|
-
const reducerPath = requireFromProject.resolve(
|
|
32
|
+
const reducerPath = requireFromProject.resolve(
|
|
33
|
+
"@dreamboard-games/sdk/reducer"
|
|
34
|
+
);
|
|
31
35
|
const reducerContractPath = requireFromProject.resolve(
|
|
32
36
|
"@dreamboard-games/sdk/reducer-contract"
|
|
33
37
|
);
|
|
@@ -256,8 +260,8 @@ async function runReducerBundleSmoke(options) {
|
|
|
256
260
|
const summary = summarizeError(error);
|
|
257
261
|
throw new Error(
|
|
258
262
|
[
|
|
259
|
-
`Dreamboard could not import \`${REDUCER_BUNDLE_ENTRY_PATH}
|
|
260
|
-
"Fix the reducer bundle entry so it can be imported locally, then
|
|
263
|
+
`Dreamboard could not import \`${REDUCER_BUNDLE_ENTRY_PATH}\`.`,
|
|
264
|
+
"Fix the reducer bundle entry so it can be imported locally, then rerun the command.",
|
|
261
265
|
`Original error: ${summary.headline}`
|
|
262
266
|
].join(" ")
|
|
263
267
|
);
|
|
@@ -292,18 +296,17 @@ async function assertReducerBundleSmoke(options) {
|
|
|
292
296
|
}
|
|
293
297
|
throw new Error(
|
|
294
298
|
[
|
|
295
|
-
"Reducer bundle preflight failed
|
|
296
|
-
"Fix the reported scenarios locally before
|
|
299
|
+
"Reducer bundle preflight failed.",
|
|
300
|
+
"Fix the reported scenarios locally before building or starting dev:",
|
|
297
301
|
formatFailureLines(failures)
|
|
298
302
|
].join("\n")
|
|
299
303
|
);
|
|
300
304
|
}
|
|
301
|
-
|
|
302
305
|
export {
|
|
303
|
-
|
|
306
|
+
assertReducerBundleSmoke,
|
|
304
307
|
assertViewPerPlayerSeatsValid,
|
|
308
|
+
buildScenarios,
|
|
305
309
|
driveReducerBundleThroughScenarios,
|
|
306
|
-
runReducerBundleSmoke
|
|
307
|
-
assertReducerBundleSmoke
|
|
310
|
+
runReducerBundleSmoke
|
|
308
311
|
};
|
|
309
|
-
//# sourceMappingURL=
|
|
312
|
+
//# sourceMappingURL=reducer-bundle-preflight-GLUJKTWU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/project/reducer-bundle-preflight.ts"],"sourcesContent":["import path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { pathToFileURL } from \"node:url\";\nimport type {\n GameTopologyManifest,\n SetupProfileSpec,\n} from \"@dreamboard-games/sdk/types\";\nimport { z } from \"zod\";\nimport { importTypeScriptModule } from \"../../utils/ts-module-loader.js\";\n\nnamespace Wire {\n export type JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue };\n export type ReducerSessionState = Record<string, unknown>;\n export type SeatProjection = { view: unknown };\n export type SeatProjectionBundle = {\n seats?: Record<string, SeatProjection | undefined>;\n };\n}\n\n// Opt in to SDK authoring warnings (e.g. concrete dependent-choice defaults).\n// Trusted reducer code cannot read process.env, so the SDK gates authoring\n// warnings on this host-set global instead.\n(globalThis as Record<string, unknown>).__DREAMBOARD_AUTHORING_WARNINGS__ =\n true;\n\n/**\n * Path to the reducer bundle entry, relative to the project root. Matches\n * the scaffold produced by `dreamboard new`.\n */\nconst REDUCER_BUNDLE_ENTRY_PATH = path.join(\"app\", \"index.ts\");\n\n/**\n * Deterministic rng seed used to drive the reducer during preflight.\n * Kept stable so local preflight failures reproduce across CLI runs.\n */\nconst PREFLIGHT_RNG_SEED = 1337;\n\n/**\n * Phase of the preflight smoke test that a failure occurred in.\n */\nexport type ReducerBundleSmokePhase =\n | \"BUILD_GAME_STATE\"\n | \"INITIALIZE\"\n | \"PROJECT_SEATS\"\n | \"VALIDATE_VIEW\";\n\nexport interface ReducerBundleSmokeScenario {\n readonly setupProfileId: string | null;\n readonly playerCount: number;\n}\n\nexport interface ReducerBundleSmokeFailure {\n readonly scenario: ReducerBundleSmokeScenario;\n readonly phase: ReducerBundleSmokePhase;\n readonly playerId?: string;\n readonly headline: string;\n readonly detail?: string;\n}\n\n/**\n * Shape of a single seat's slice in `projectSeatsDynamic(...)`. Only `view`\n * is consumed by the preflight, but the full wire DTO keeps local tests\n * aligned with the canonical reducer boundary.\n */\nexport type ReducerBundleSeatProjection = Wire.SeatProjection;\n\n/** Result of calling `bundle.projectSeatsDynamic({ state, playerIds })`. */\nexport type ReducerBundleSeatProjectionBundle = Wire.SeatProjectionBundle;\n\n/**\n * Minimal structural shape the preflight needs from the authored reducer\n * bundle. Matches the subset of `createReducerBundle(game)` used by the\n * reducer runtime.\n */\nexport type ReducerBundleLike = Pick<\n {\n initialize(input: {\n table: Wire.JsonValue;\n playerIds: readonly string[];\n rngSeed?: number | null;\n setup?: unknown;\n }): unknown | Promise<unknown>;\n projectSeatsDynamic(input: {\n state: unknown;\n playerIds: readonly string[];\n }): ReducerBundleSeatProjectionBundle;\n },\n \"initialize\" | \"projectSeatsDynamic\"\n>;\n\ntype PerPlayerValidationHelpers = {\n isPerPlayer(value: unknown): boolean;\n perPlayerSchema(\n valueSchema: z.ZodTypeAny,\n options: { players: readonly string[] },\n ): z.ZodTypeAny;\n};\n\ntype ProjectReducerPreflightModules = PerPlayerValidationHelpers & {\n materializeManifestTable(input: {\n manifest: GameTopologyManifest;\n playerIds: readonly string[];\n shuffleItems<Value>(values: readonly Value[]): Value[];\n }): unknown;\n};\n\nfunction isStructuralPerPlayer(value: unknown): boolean {\n return (\n typeof value === \"object\" &&\n value !== null &&\n (value as { __perPlayer?: unknown }).__perPlayer === true &&\n Array.isArray((value as { entries?: unknown }).entries)\n );\n}\n\nfunction fallbackPerPlayerSchema(\n valueSchema: z.ZodTypeAny,\n options: { players: readonly string[] },\n): z.ZodTypeAny {\n const playerSchema =\n options.players.length > 0\n ? z.enum(options.players as [string, ...string[]])\n : z.string();\n return z\n .object({\n __perPlayer: z.literal(true),\n entries: z.array(z.tuple([playerSchema, valueSchema])),\n })\n .strict();\n}\n\nasync function loadProjectReducerPreflightModules(\n projectRoot: string,\n): Promise<ProjectReducerPreflightModules> {\n const requireFromProject = createRequire(\n path.join(projectRoot, \"package.json\"),\n );\n const reducerPath = requireFromProject.resolve(\n \"@dreamboard-games/sdk/reducer\",\n );\n const reducerContractPath = requireFromProject.resolve(\n \"@dreamboard-games/sdk/reducer-contract\",\n );\n const [reducerModule, reducerContractModule] = (await Promise.all([\n import(pathToFileURL(reducerPath).href),\n import(pathToFileURL(reducerContractPath).href),\n ])) as [\n Partial<PerPlayerValidationHelpers>,\n { materializeManifestTable?: unknown },\n ];\n\n if (\n typeof reducerModule.isPerPlayer !== \"function\" ||\n typeof reducerModule.perPlayerSchema !== \"function\" ||\n typeof reducerContractModule.materializeManifestTable !== \"function\"\n ) {\n throw new Error(\n \"Installed @dreamboard-games/sdk does not expose the reducer preflight helpers required by this CLI.\",\n );\n }\n\n return {\n isPerPlayer: reducerModule.isPerPlayer,\n perPlayerSchema: reducerModule.perPlayerSchema,\n materializeManifestTable:\n reducerContractModule.materializeManifestTable as ProjectReducerPreflightModules[\"materializeManifestTable\"],\n };\n}\n\nfunction buildPlayerIds(playerCount: number): string[] {\n const ids: string[] = [];\n for (let index = 1; index <= playerCount; index += 1) {\n ids.push(`player-${index}`);\n }\n return ids;\n}\n\n/**\n * Build the matrix of scenarios to drive. Exported for tests so we can\n * assert scenario generation without loading a full reducer bundle.\n */\nexport function buildScenarios(\n manifest: GameTopologyManifest,\n): ReducerBundleSmokeScenario[] {\n const rawCounts = [\n manifest.players?.minPlayers,\n manifest.players?.maxPlayers,\n ].filter(\n (count): count is number =>\n typeof count === \"number\" && Number.isFinite(count) && count > 0,\n );\n const playerCounts = Array.from(new Set(rawCounts));\n if (playerCounts.length === 0) {\n return [];\n }\n const profiles: SetupProfileSpec[] = manifest.setupProfiles ?? [];\n const profileIds: Array<string | null> =\n profiles.length > 0 ? profiles.map((profile) => profile.id) : [null];\n\n const scenarios: ReducerBundleSmokeScenario[] = [];\n const seen = new Set<string>();\n for (const profileId of profileIds) {\n for (const count of playerCounts) {\n const key = `${profileId ?? \"<default>\"}:${count}`;\n if (seen.has(key)) continue;\n seen.add(key);\n scenarios.push({ setupProfileId: profileId, playerCount: count });\n }\n }\n return scenarios;\n}\n\nfunction describeScenario(scenario: ReducerBundleSmokeScenario): string {\n const profileDesc = scenario.setupProfileId\n ? `setup profile '${scenario.setupProfileId}'`\n : \"no setup profile\";\n return `${profileDesc}, ${scenario.playerCount} player${\n scenario.playerCount === 1 ? \"\" : \"s\"\n }`;\n}\n\nfunction summarizeError(error: unknown): { headline: string; detail?: string } {\n if (error instanceof Error) {\n const firstLine =\n error.message\n .split(\"\\n\")\n .map((line) => line.trim())\n .find((line) => line.length > 0) ?? \"Unknown failure\";\n const detail =\n error.stack && error.stack !== error.message ? error.stack : undefined;\n return { headline: firstLine, detail };\n }\n return { headline: String(error ?? \"Unknown failure\") };\n}\n\n/**\n * Walk `view` recursively and, for every embedded `PerPlayer<T>` shape,\n * assert its seat list matches `expectedPlayerIds` using\n * `perPlayerSchema({ players })`. Returns `null` on success, or a headline\n * describing the first mismatch.\n *\n * This catches the class of bug where an author returns a `PerPlayer<T>`\n * that was built from the wrong seat list (e.g. a manifest-derived\n * `maxPlayers` literal union) while the runtime session has a different\n * number of seats. The Zod bound schema produces an identical error shape\n * to the one the backend raises in production so authors see the same\n * diagnostic locally.\n */\nexport function assertViewPerPlayerSeatsValid(\n view: unknown,\n expectedPlayerIds: readonly string[],\n helpers: PerPlayerValidationHelpers = {\n isPerPlayer: isStructuralPerPlayer,\n perPlayerSchema: fallbackPerPlayerSchema,\n },\n breadcrumb: string[] = [],\n): string | null {\n if (view === null || view === undefined) {\n return null;\n }\n\n if (helpers.isPerPlayer(view)) {\n const schema = helpers.perPlayerSchema(z.unknown(), {\n players: expectedPlayerIds as never,\n });\n const result = schema.safeParse(view);\n if (!result.success) {\n const firstIssue = result.error.issues[0];\n const location =\n breadcrumb.length > 0 ? ` at view.${breadcrumb.join(\".\")}` : \"\";\n const message = firstIssue?.message ?? \"PerPlayer seat mismatch\";\n return `PerPlayer seat mismatch${location}: ${message}`;\n }\n return null;\n }\n\n if (Array.isArray(view)) {\n for (let index = 0; index < view.length; index += 1) {\n const result = assertViewPerPlayerSeatsValid(\n view[index],\n expectedPlayerIds,\n helpers,\n [...breadcrumb, `[${index}]`],\n );\n if (result) return result;\n }\n return null;\n }\n\n if (typeof view === \"object\") {\n for (const [key, value] of Object.entries(\n view as Record<string, unknown>,\n )) {\n const result = assertViewPerPlayerSeatsValid(\n value,\n expectedPlayerIds,\n helpers,\n [...breadcrumb, key],\n );\n if (result) return result;\n }\n return null;\n }\n\n return null;\n}\n\nfunction identityShuffle<Value>(values: readonly Value[]): Value[] {\n return [...values];\n}\n\n/**\n * Drive an already-loaded reducer bundle through the provided scenarios.\n * Shared between the full `runReducerBundleSmoke` entrypoint and the\n * unit tests, which feed a fake bundle directly.\n *\n * For each (setup profile × player count) scenario this:\n *\n * 1. materializes an initial table via `materializeManifestTable`,\n * 2. calls `bundle.initialize({ table, playerIds, rngSeed, setup })`,\n * 3. calls `bundle.projectSeatsDynamic({ state, playerIds })` once, and\n * 4. validates any embedded `PerPlayer<T>` views match the runtime seat\n * list using `perPlayerSchema({ players: playerIds })`.\n *\n * Failures are collected instead of thrown so authors get the full list\n * of broken scenarios in one preflight pass.\n */\nexport async function driveReducerBundleThroughScenarios(options: {\n manifest: GameTopologyManifest;\n bundle: ReducerBundleLike;\n scenarios: readonly ReducerBundleSmokeScenario[];\n preflightModules: ProjectReducerPreflightModules;\n rngSeed?: number;\n}): Promise<ReducerBundleSmokeFailure[]> {\n const { manifest, bundle, scenarios, preflightModules } = options;\n const rngSeed = options.rngSeed ?? PREFLIGHT_RNG_SEED;\n\n const failures: ReducerBundleSmokeFailure[] = [];\n const tableCache = new Map<\n number,\n { table: unknown; playerIds: string[] } | { error: unknown }\n >();\n\n for (const scenario of scenarios) {\n let tableEntry = tableCache.get(scenario.playerCount);\n if (!tableEntry) {\n try {\n const playerIds = buildPlayerIds(scenario.playerCount);\n // JSON-roundtrip the materialized table to mirror the backend wire\n // boundary: real flows serialize the table over HTTP, which drops\n // explicit `undefined` property values (e.g. optional card text) that\n // the bundle's strict JSON state schema would otherwise reject.\n const table: unknown = JSON.parse(\n JSON.stringify(\n preflightModules.materializeManifestTable({\n manifest,\n playerIds,\n shuffleItems: identityShuffle,\n }),\n ),\n );\n tableEntry = { table, playerIds };\n } catch (error) {\n tableEntry = { error };\n }\n tableCache.set(scenario.playerCount, tableEntry);\n }\n if (\"error\" in tableEntry) {\n const summary = summarizeError(tableEntry.error);\n failures.push({\n scenario,\n phase: \"BUILD_GAME_STATE\",\n headline: summary.headline,\n detail: summary.detail,\n });\n continue;\n }\n\n const { table, playerIds } = tableEntry;\n\n let sessionState: unknown;\n try {\n sessionState = await bundle.initialize({\n table: table as Wire.JsonValue,\n playerIds: [...playerIds],\n rngSeed,\n setup: scenario.setupProfileId\n ? { profileId: scenario.setupProfileId, optionValues: {} }\n : null,\n });\n } catch (error) {\n const summary = summarizeError(error);\n failures.push({\n scenario,\n phase: \"INITIALIZE\",\n headline: summary.headline,\n detail: summary.detail,\n });\n continue;\n }\n\n let projection: ReducerBundleSeatProjectionBundle | undefined;\n try {\n projection = bundle.projectSeatsDynamic({\n state: sessionState as Wire.ReducerSessionState,\n playerIds: [...playerIds],\n });\n } catch (error) {\n const summary = summarizeError(error);\n failures.push({\n scenario,\n phase: \"PROJECT_SEATS\",\n headline: summary.headline,\n detail: summary.detail,\n });\n continue;\n }\n\n const seats = projection?.seats ?? {};\n for (const playerId of playerIds) {\n const seat = seats[playerId];\n if (!seat) {\n failures.push({\n scenario,\n phase: \"PROJECT_SEATS\",\n playerId,\n headline: `projectSeatsDynamic() did not return an entry for seat '${playerId}'.`,\n });\n continue;\n }\n const mismatch = assertViewPerPlayerSeatsValid(\n seat.view,\n playerIds,\n preflightModules,\n );\n if (mismatch) {\n failures.push({\n scenario,\n phase: \"VALIDATE_VIEW\",\n playerId,\n headline: mismatch,\n });\n }\n }\n }\n\n return failures;\n}\n\n/**\n * Import the authored reducer bundle from `projectRoot/app/index.ts` and\n * drive it through every (setup profile × player count) scenario the\n * manifest declares. Returns the collected failures without throwing so\n * callers can decide whether to surface them.\n */\nexport async function runReducerBundleSmoke(options: {\n projectRoot: string;\n manifest: GameTopologyManifest;\n}): Promise<ReducerBundleSmokeFailure[]> {\n const { projectRoot, manifest } = options;\n const scenarios = buildScenarios(manifest);\n if (scenarios.length === 0) {\n return [];\n }\n\n const entryPath = path.join(projectRoot, REDUCER_BUNDLE_ENTRY_PATH);\n let module: { default?: ReducerBundleLike };\n try {\n module = await importTypeScriptModule<{ default?: ReducerBundleLike }>(\n entryPath,\n );\n } catch (error) {\n const summary = summarizeError(error);\n throw new Error(\n [\n `Dreamboard could not import \\`${REDUCER_BUNDLE_ENTRY_PATH}\\`.`,\n \"Fix the reducer bundle entry so it can be imported locally, then rerun the command.\",\n `Original error: ${summary.headline}`,\n ].join(\" \"),\n );\n }\n const bundle = module.default;\n if (!bundle || typeof bundle.initialize !== \"function\") {\n throw new Error(\n [\n `\\`${REDUCER_BUNDLE_ENTRY_PATH}\\` does not export a reducer bundle as its default export.`,\n \"Export `createReducerBundle(game)` from `app/index.ts` so the compile pipeline can smoke-test it.\",\n ].join(\" \"),\n );\n }\n\n const preflightModules =\n await loadProjectReducerPreflightModules(projectRoot);\n return driveReducerBundleThroughScenarios({\n manifest,\n bundle,\n scenarios,\n preflightModules,\n });\n}\n\nfunction formatFailureLines(\n failures: readonly ReducerBundleSmokeFailure[],\n): string {\n return failures\n .map((failure) => {\n const location = failure.playerId ? ` (seat ${failure.playerId})` : \"\";\n return `• [${failure.phase}] ${describeScenario(failure.scenario)}${location}: ${failure.headline}`;\n })\n .join(\"\\n\");\n}\n\n/**\n * Preflight wrapper used before build, verify, test, and dev workflows. Runs\n * `runReducerBundleSmoke` and throws an aggregated error if any scenario\n * failed so the author sees every broken seat/profile in one shot.\n *\n * The surrounding workflow already guarantees that the authored contract imports cleanly\n * (via `assertReducerContractPreflight`) and that `tsc --noEmit` is\n * green (via `runLocalTypecheck`) before this runs, so failures from\n * this step are always runtime-shaped (`initialize`/`projectSeatsDynamic` rejecting,\n * `perPlayer` seat mismatches, …) rather than static type or import\n * errors.\n */\nexport async function assertReducerBundleSmoke(options: {\n projectRoot: string;\n manifest: GameTopologyManifest;\n}): Promise<void> {\n const failures = await runReducerBundleSmoke(options);\n if (failures.length === 0) {\n return;\n }\n throw new Error(\n [\n \"Reducer bundle preflight failed.\",\n \"Fix the reported scenarios locally before building or starting dev:\",\n formatFailureLines(failures),\n ].join(\"\\n\"),\n );\n}\n"],"mappings":"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","names":["playerIds","table"]}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import {
|
|
3
3
|
importTypeScriptModule
|
|
4
4
|
} from "./chunk-QD4SQNUP.mjs";
|
|
5
|
+
import "./chunk-TTB7AIHZ.mjs";
|
|
6
|
+
import "./chunk-H6XDQJ3N.mjs";
|
|
5
7
|
|
|
6
8
|
// src/services/project/reducer-contract-preflight.ts
|
|
7
9
|
import path from "path";
|
|
@@ -22,7 +24,7 @@ async function assertReducerContractPreflight(projectRoot) {
|
|
|
22
24
|
if (isManifestScopedIdBrandingError(message)) {
|
|
23
25
|
throw new Error(
|
|
24
26
|
[
|
|
25
|
-
`Dreamboard could not validate \`${GAME_CONTRACT_ENTRY_PATH}
|
|
27
|
+
`Dreamboard could not validate \`${GAME_CONTRACT_ENTRY_PATH}\`.`,
|
|
26
28
|
"This happens because a state field name looks like a manifest-scoped id, but the schema uses a plain string instead of the manifest-backed id schema.",
|
|
27
29
|
"Workaround: use `gameContract.schemas.<id>` (or `manifest.ids.<id>`) for manifest ids. If the field is intentionally free-form text, rename it so it does not look like a manifest id field.",
|
|
28
30
|
`Original error: ${message}`
|
|
@@ -31,15 +33,14 @@ async function assertReducerContractPreflight(projectRoot) {
|
|
|
31
33
|
}
|
|
32
34
|
throw new Error(
|
|
33
35
|
[
|
|
34
|
-
`Dreamboard could not validate \`${GAME_CONTRACT_ENTRY_PATH}
|
|
35
|
-
"Fix the authored reducer contract module so it can be imported locally, then
|
|
36
|
+
`Dreamboard could not validate \`${GAME_CONTRACT_ENTRY_PATH}\`.`,
|
|
37
|
+
"Fix the authored reducer contract module so it can be imported locally, then rerun the command.",
|
|
36
38
|
`Original error: ${message}`
|
|
37
39
|
].join(" ")
|
|
38
40
|
);
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
|
-
|
|
42
43
|
export {
|
|
43
44
|
assertReducerContractPreflight
|
|
44
45
|
};
|
|
45
|
-
//# sourceMappingURL=
|
|
46
|
+
//# sourceMappingURL=reducer-contract-preflight-WVQQPW5F.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/project/reducer-contract-preflight.ts"],"sourcesContent":["import path from \"node:path\";\nimport { importTypeScriptModule } from \"../../utils/ts-module-loader.js\";\n\nconst GAME_CONTRACT_ENTRY_PATH = path.join(\"app\", \"game-contract.ts\");\n\nfunction normalizeErrorMessage(error: unknown): string {\n const rawMessage =\n error instanceof Error ? error.message : String(error ?? \"Unknown error\");\n return (\n rawMessage\n .split(\"\\n\")\n .map((line) => line.trim())\n .find(Boolean)\n ?.replace(/^Error:\\s*/u, \"\") ?? \"Unknown error\"\n );\n}\n\nfunction isManifestScopedIdBrandingError(message: string): boolean {\n return (\n message.includes(\"defineGameContract:\") &&\n message.includes(\"manifest-scoped\") &&\n (message.includes(\"uses a raw z.string()\") ||\n message.includes(\"uses z.array(z.string())\"))\n );\n}\n\nexport async function assertReducerContractPreflight(\n projectRoot: string,\n): Promise<void> {\n const entryPath = path.join(projectRoot, GAME_CONTRACT_ENTRY_PATH);\n\n try {\n await importTypeScriptModule(entryPath);\n } catch (error) {\n const message = normalizeErrorMessage(error);\n if (isManifestScopedIdBrandingError(message)) {\n throw new Error(\n [\n `Dreamboard could not validate \\`${GAME_CONTRACT_ENTRY_PATH}\\`.`,\n \"This happens because a state field name looks like a manifest-scoped id, but the schema uses a plain string instead of the manifest-backed id schema.\",\n \"Workaround: use `gameContract.schemas.<id>` (or `manifest.ids.<id>`) for manifest ids. If the field is intentionally free-form text, rename it so it does not look like a manifest id field.\",\n `Original error: ${message}`,\n ].join(\" \"),\n );\n }\n\n throw new Error(\n [\n `Dreamboard could not validate \\`${GAME_CONTRACT_ENTRY_PATH}\\`.`,\n \"Fix the authored reducer contract module so it can be imported locally, then rerun the command.\",\n `Original error: ${message}`,\n ].join(\" \"),\n );\n }\n}\n"],"mappings":";;;;;;;;AAAA,OAAO,UAAU;AAGjB,IAAM,2BAA2B,KAAK,KAAK,OAAO,kBAAkB;AAEpE,SAAS,sBAAsB,OAAwB;AACrD,QAAM,aACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,SAAS,eAAe;AAC1E,SACE,WACG,MAAM,IAAI,EACV,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,KAAK,OAAO,GACX,QAAQ,eAAe,EAAE,KAAK;AAEtC;AAEA,SAAS,gCAAgC,SAA0B;AACjE,SACE,QAAQ,SAAS,qBAAqB,KACtC,QAAQ,SAAS,iBAAiB,MACjC,QAAQ,SAAS,uBAAuB,KACvC,QAAQ,SAAS,0BAA0B;AAEjD;AAEA,eAAsB,+BACpB,aACe;AACf,QAAM,YAAY,KAAK,KAAK,aAAa,wBAAwB;AAEjE,MAAI;AACF,UAAM,uBAAuB,SAAS;AAAA,EACxC,SAAS,OAAO;AACd,UAAM,UAAU,sBAAsB,KAAK;AAC3C,QAAI,gCAAgC,OAAO,GAAG;AAC5C,YAAM,IAAI;AAAA,QACR;AAAA,UACE,mCAAmC,wBAAwB;AAAA,UAC3D;AAAA,UACA;AAAA,UACA,mBAAmB,OAAO;AAAA,QAC5B,EAAE,KAAK,GAAG;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,IAAI;AAAA,MACR;AAAA,QACE,mCAAmC,wBAAwB;AAAA,QAC3D;AAAA,QACA,mBAAmB,OAAO;AAAA,MAC5B,EAAE,KAAK,GAAG;AAAA,IACZ;AAAA,EACF;AACF;","names":[]}
|
|
@@ -3,18 +3,15 @@ import {
|
|
|
3
3
|
bundleTypeScriptModuleText,
|
|
4
4
|
importTypeScriptModule
|
|
5
5
|
} from "./chunk-QD4SQNUP.mjs";
|
|
6
|
-
import {
|
|
7
|
-
STALE_CONTRACT_ARTIFACT_CODE,
|
|
8
|
-
isStaleContractArtifactError,
|
|
9
|
-
toDreamboardApiError
|
|
10
|
-
} from "./chunk-PLXXH5LY.mjs";
|
|
11
6
|
import {
|
|
12
7
|
createUserTokenManager,
|
|
13
8
|
resolveLocalHarnessAccessToken
|
|
14
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DQUYBIGQ.mjs";
|
|
10
|
+
import "./chunk-IWB4L2HV.mjs";
|
|
15
11
|
import {
|
|
16
12
|
loadManifest
|
|
17
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-KDAQ4CZY.mjs";
|
|
14
|
+
import "./chunk-GWRZRWCF.mjs";
|
|
18
15
|
import {
|
|
19
16
|
REDUCER_TESTING_TYPES_WRAPPER_CONTENT,
|
|
20
17
|
buildReducerTestingContractContent
|
|
@@ -25,16 +22,21 @@ import {
|
|
|
25
22
|
writeJsonFile
|
|
26
23
|
} from "./chunk-RDYXWXXC.mjs";
|
|
27
24
|
import {
|
|
25
|
+
CLIENT_PROBLEM_TYPES,
|
|
26
|
+
SERVER_PROBLEM_TYPES,
|
|
28
27
|
createGameplayCapability,
|
|
29
28
|
createProjectSession,
|
|
30
29
|
createProjectSessionFromReducerSnapshot,
|
|
31
30
|
getSessionSnapshot,
|
|
32
31
|
hashContent,
|
|
33
|
-
startGame
|
|
34
|
-
|
|
32
|
+
startGame,
|
|
33
|
+
zProblemDetails
|
|
34
|
+
} from "./chunk-BWBN2TDJ.mjs";
|
|
35
35
|
import {
|
|
36
36
|
external_exports
|
|
37
37
|
} from "./chunk-JZTH3EMV.mjs";
|
|
38
|
+
import "./chunk-TTB7AIHZ.mjs";
|
|
39
|
+
import "./chunk-MYMVXTZT.mjs";
|
|
38
40
|
import {
|
|
39
41
|
readWorkspaceTextFileIfExists,
|
|
40
42
|
resolveWorkspacePath,
|
|
@@ -45,6 +47,7 @@ import {
|
|
|
45
47
|
import {
|
|
46
48
|
PROJECT_DIR_NAME
|
|
47
49
|
} from "./chunk-M7UVBANQ.mjs";
|
|
50
|
+
import "./chunk-H6XDQJ3N.mjs";
|
|
48
51
|
|
|
49
52
|
// src/services/testing/reducer-native-test-harness.ts
|
|
50
53
|
import path3 from "path";
|
|
@@ -765,7 +768,7 @@ async function resolveDevHostBearer(config) {
|
|
|
765
768
|
if (!config.refreshToken) {
|
|
766
769
|
return {
|
|
767
770
|
kind: "permanent_invalid",
|
|
768
|
-
message: "Stored Dreamboard session is expired or invalid. Run `dreamboard login` to authenticate again."
|
|
771
|
+
message: "Stored Dreamboard session is expired or invalid. Run `dreamboard auth login` to authenticate again."
|
|
769
772
|
};
|
|
770
773
|
}
|
|
771
774
|
const resolved = await createUserTokenManager(config).resolveApiToken();
|
|
@@ -813,6 +816,120 @@ function resolveSetupProfileSelection(options) {
|
|
|
813
816
|
};
|
|
814
817
|
}
|
|
815
818
|
|
|
819
|
+
// src/utils/problem-types.ts
|
|
820
|
+
var CLI_PROBLEM_TYPES = {
|
|
821
|
+
...SERVER_PROBLEM_TYPES,
|
|
822
|
+
...CLIENT_PROBLEM_TYPES
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
// src/utils/errors.ts
|
|
826
|
+
var STALE_CONTRACT_ARTIFACT_CODE = "STALE_CONTRACT_ARTIFACT";
|
|
827
|
+
function isProblemViolationArray(value) {
|
|
828
|
+
return Array.isArray(value) && value.every(
|
|
829
|
+
(entry) => typeof entry === "object" && entry !== null && typeof entry.message === "string"
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
function isProblemDetails(value) {
|
|
833
|
+
return zProblemDetails.safeParse(value).success;
|
|
834
|
+
}
|
|
835
|
+
function coerceViolations(value) {
|
|
836
|
+
if (isProblemViolationArray(value)) {
|
|
837
|
+
return value;
|
|
838
|
+
}
|
|
839
|
+
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) {
|
|
840
|
+
return value.map((message) => ({ message }));
|
|
841
|
+
}
|
|
842
|
+
return void 0;
|
|
843
|
+
}
|
|
844
|
+
function getRequestId(response) {
|
|
845
|
+
return response?.headers?.get?.("X-Correlation-ID") ?? response?.headers?.get?.("x-correlation-id") ?? void 0;
|
|
846
|
+
}
|
|
847
|
+
function toApiProblem(error, response, fallback) {
|
|
848
|
+
if (isProblemDetails(error)) {
|
|
849
|
+
return {
|
|
850
|
+
...error,
|
|
851
|
+
status: error.status || response?.status || 0,
|
|
852
|
+
requestId: error.requestId ?? getRequestId(response)
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
if (error instanceof Error) {
|
|
856
|
+
return {
|
|
857
|
+
type: CLI_PROBLEM_TYPES.TRANSPORT_ERROR,
|
|
858
|
+
title: response?.statusText || "API error",
|
|
859
|
+
status: response?.status ?? 0,
|
|
860
|
+
detail: error.message || fallback,
|
|
861
|
+
requestId: getRequestId(response)
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
if (error && typeof error === "object") {
|
|
865
|
+
const obj = error;
|
|
866
|
+
const detail2 = typeof obj.detail === "string" ? obj.detail : typeof obj.message === "string" ? obj.message : void 0;
|
|
867
|
+
const title = typeof obj.title === "string" ? obj.title : response?.statusText || "API error";
|
|
868
|
+
const violations = coerceViolations(obj.violations) ?? coerceViolations(obj.errors);
|
|
869
|
+
if (detail2) {
|
|
870
|
+
return {
|
|
871
|
+
type: typeof obj.type === "string" ? obj.type : CLI_PROBLEM_TYPES.UNKNOWN_API_ERROR,
|
|
872
|
+
title,
|
|
873
|
+
status: typeof obj.status === "number" ? obj.status : response?.status ?? 0,
|
|
874
|
+
detail: detail2,
|
|
875
|
+
requestId: typeof obj.requestId === "string" ? obj.requestId : getRequestId(response),
|
|
876
|
+
retryable: typeof obj.retryable === "boolean" ? obj.retryable : void 0,
|
|
877
|
+
context: typeof obj.context === "object" && obj.context !== null ? obj.context : void 0,
|
|
878
|
+
violations,
|
|
879
|
+
timestamp: typeof obj.timestamp === "string" ? obj.timestamp : void 0,
|
|
880
|
+
instance: typeof obj.instance === "string" ? obj.instance : void 0
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
const detail = typeof error === "string" ? error.trim() || fallback : fallback;
|
|
885
|
+
return {
|
|
886
|
+
type: CLI_PROBLEM_TYPES.UNKNOWN_API_ERROR,
|
|
887
|
+
title: response?.statusText || "API error",
|
|
888
|
+
status: response?.status ?? 0,
|
|
889
|
+
detail,
|
|
890
|
+
requestId: getRequestId(response)
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
function formatProblem(problem) {
|
|
894
|
+
const base = problem.detail || problem.title;
|
|
895
|
+
const violations = problem.violations && problem.violations.length > 0 ? ` (${problem.violations.map((entry) => entry.message).join("; ")})` : "";
|
|
896
|
+
const statusSuffix = problem.status && problem.status > 0 ? ` (HTTP ${problem.status})` : "";
|
|
897
|
+
return `${base}${violations}${statusSuffix}`;
|
|
898
|
+
}
|
|
899
|
+
var DreamboardApiError = class extends Error {
|
|
900
|
+
problem;
|
|
901
|
+
status;
|
|
902
|
+
requestId;
|
|
903
|
+
retryable;
|
|
904
|
+
constructor(problem, cause) {
|
|
905
|
+
super(formatProblem(problem), { cause });
|
|
906
|
+
this.name = "DreamboardApiError";
|
|
907
|
+
this.problem = problem;
|
|
908
|
+
this.status = problem.status;
|
|
909
|
+
this.requestId = problem.requestId;
|
|
910
|
+
this.retryable = problem.retryable;
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
function toDreamboardApiError(error, response, fallback) {
|
|
914
|
+
return new DreamboardApiError(toApiProblem(error, response, fallback), error);
|
|
915
|
+
}
|
|
916
|
+
function getObjectStringProperty(value, property) {
|
|
917
|
+
return value && typeof value === "object" && typeof value[property] === "string" ? value[property] : void 0;
|
|
918
|
+
}
|
|
919
|
+
function isStaleContractArtifactMessage(message) {
|
|
920
|
+
return message.includes(STALE_CONTRACT_ARTIFACT_CODE) || message.includes("StaleContractArtifactError") || message.toLowerCase().includes("stale contract artifact");
|
|
921
|
+
}
|
|
922
|
+
function isStaleContractArtifactError(error) {
|
|
923
|
+
if (getObjectStringProperty(error, "code") === STALE_CONTRACT_ARTIFACT_CODE) {
|
|
924
|
+
return true;
|
|
925
|
+
}
|
|
926
|
+
if (getObjectStringProperty(error, "name") === "StaleContractArtifactError") {
|
|
927
|
+
return true;
|
|
928
|
+
}
|
|
929
|
+
const message = getObjectStringProperty(error, "message");
|
|
930
|
+
return message ? isStaleContractArtifactMessage(message) : false;
|
|
931
|
+
}
|
|
932
|
+
|
|
816
933
|
// src/utils/session-game-source.ts
|
|
817
934
|
function projectIdFromSessionGameSource(source) {
|
|
818
935
|
if (source.kind === "USER_COMPILED") {
|
|
@@ -1029,7 +1146,7 @@ async function ensureReducerNativeTestingFiles(projectRoot) {
|
|
|
1029
1146
|
testingContractPath,
|
|
1030
1147
|
buildReducerTestingContractContent({ rejectionCodes })
|
|
1031
1148
|
);
|
|
1032
|
-
const header = "// Generated by dreamboard test
|
|
1149
|
+
const header = "// Generated by dreamboard test. Do not edit by hand.\n";
|
|
1033
1150
|
if (!await workspacePathExists(projectRoot, baseStatesPath)) {
|
|
1034
1151
|
await writeWorkspaceTextFile(
|
|
1035
1152
|
projectRoot,
|
|
@@ -2061,12 +2178,12 @@ async function materializeScenarioReducerState(options) {
|
|
|
2061
2178
|
const generatedBase = generatedBaseStates?.[baseStateKey(base.definition.id)];
|
|
2062
2179
|
if (!generatedBase) {
|
|
2063
2180
|
throw new Error(
|
|
2064
|
-
`Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test
|
|
2181
|
+
`Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test' before using --from-scenario.`
|
|
2065
2182
|
);
|
|
2066
2183
|
}
|
|
2067
2184
|
if (typeof generatedBase.version !== "number") {
|
|
2068
2185
|
throw new Error(
|
|
2069
|
-
`Generated base artifact for '${base.definition.id}' is stale. Run 'dreamboard test
|
|
2186
|
+
`Generated base artifact for '${base.definition.id}' is stale. Run 'dreamboard test' before using --from-scenario.`
|
|
2070
2187
|
);
|
|
2071
2188
|
}
|
|
2072
2189
|
const canTrustGeneratedFingerprint = options.trustGeneratedFingerprint === true && generatedBase.fingerprint.compiledResultId === options.compiledResultId && generatedBase.fingerprint.gameId === options.gameId;
|
|
@@ -2210,7 +2327,7 @@ async function replayScenarioThroughBackend(options) {
|
|
|
2210
2327
|
const generatedBase = generatedBaseStates?.[baseStateKey(base.definition.id)];
|
|
2211
2328
|
if (!generatedBase) {
|
|
2212
2329
|
throw new Error(
|
|
2213
|
-
`Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test
|
|
2330
|
+
`Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test' before replaying a scenario.`
|
|
2214
2331
|
);
|
|
2215
2332
|
}
|
|
2216
2333
|
validateGeneratedFingerprint({
|
|
@@ -2244,7 +2361,7 @@ async function replayScenarioThroughBackend(options) {
|
|
|
2244
2361
|
const parentArtifact = generatedBaseStates?.[baseStateKey(base.definition.extends)];
|
|
2245
2362
|
if (!parentArtifact || typeof parentArtifact.version !== "number") {
|
|
2246
2363
|
throw new Error(
|
|
2247
|
-
`Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing or stale. Run 'dreamboard test
|
|
2364
|
+
`Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing or stale. Run 'dreamboard test' first.`
|
|
2248
2365
|
);
|
|
2249
2366
|
}
|
|
2250
2367
|
shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
|
|
@@ -2368,7 +2485,7 @@ async function createScenarioActionPlan(options) {
|
|
|
2368
2485
|
const generatedBase = generatedBaseStates?.[baseStateKey(base.definition.id)];
|
|
2369
2486
|
if (!generatedBase) {
|
|
2370
2487
|
throw new Error(
|
|
2371
|
-
`Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test
|
|
2488
|
+
`Missing generated base artifact for '${base.definition.id}'. Run 'dreamboard test' before replaying a scenario.`
|
|
2372
2489
|
);
|
|
2373
2490
|
}
|
|
2374
2491
|
validateGeneratedFingerprint({
|
|
@@ -2402,7 +2519,7 @@ async function createScenarioActionPlan(options) {
|
|
|
2402
2519
|
const parentArtifact = generatedBaseStates?.[baseStateKey(base.definition.extends)];
|
|
2403
2520
|
if (!parentArtifact || typeof parentArtifact.version !== "number") {
|
|
2404
2521
|
throw new Error(
|
|
2405
|
-
`Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing or stale. Run 'dreamboard test
|
|
2522
|
+
`Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing or stale. Run 'dreamboard test' first.`
|
|
2406
2523
|
);
|
|
2407
2524
|
}
|
|
2408
2525
|
shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
|
|
@@ -2799,7 +2916,7 @@ async function writeReducerNativeGeneratedFiles(options) {
|
|
|
2799
2916
|
shadow
|
|
2800
2917
|
});
|
|
2801
2918
|
}
|
|
2802
|
-
const header = "// Generated by dreamboard test
|
|
2919
|
+
const header = "// Generated by dreamboard test. Do not edit by hand.\n";
|
|
2803
2920
|
await writeWorkspaceTextFile(
|
|
2804
2921
|
options.projectRoot,
|
|
2805
2922
|
"test/generated/base-states.generated.ts",
|
|
@@ -2879,7 +2996,7 @@ function validateGeneratedFingerprint(options) {
|
|
|
2879
2996
|
const mismatches = [];
|
|
2880
2997
|
if (options.generated.contractFingerprint && options.current.contractFingerprint && options.generated.contractFingerprint !== options.current.contractFingerprint) {
|
|
2881
2998
|
const error = new Error(
|
|
2882
|
-
`Base states were generated for contract ${options.generated.contractFingerprint} but the current contract is ${options.current.contractFingerprint}. Remedy: run \`dreamboard test
|
|
2999
|
+
`Base states were generated for contract ${options.generated.contractFingerprint} but the current contract is ${options.current.contractFingerprint}. Remedy: run \`dreamboard test\`, then re-run the tests.`
|
|
2883
3000
|
);
|
|
2884
3001
|
error.code = STALE_CONTRACT_ARTIFACT_CODE;
|
|
2885
3002
|
throw error;
|
|
@@ -2910,7 +3027,7 @@ function validateGeneratedFingerprint(options) {
|
|
|
2910
3027
|
}
|
|
2911
3028
|
if (mismatches.length > 0) {
|
|
2912
3029
|
throw new Error(
|
|
2913
|
-
`${mismatches.join("; ")}. Run 'dreamboard test
|
|
3030
|
+
`${mismatches.join("; ")}. Run 'dreamboard test' to refresh reducer-native base artifacts.`
|
|
2914
3031
|
);
|
|
2915
3032
|
}
|
|
2916
3033
|
}
|
|
@@ -3070,7 +3187,7 @@ async function runReducerNativeScenarios(options) {
|
|
|
3070
3187
|
});
|
|
3071
3188
|
} else if (options.runner !== "reducer") {
|
|
3072
3189
|
throw new Error(
|
|
3073
|
-
"Missing reducer-native generated base artifacts. Run 'dreamboard test
|
|
3190
|
+
"Missing reducer-native generated base artifacts. Run 'dreamboard test' first."
|
|
3074
3191
|
);
|
|
3075
3192
|
}
|
|
3076
3193
|
let scenarioBrowser = null;
|
|
@@ -3096,7 +3213,7 @@ async function runReducerNativeScenarios(options) {
|
|
|
3096
3213
|
const parentArtifact = generatedBaseStates?.[baseStateKey(base.definition.extends)];
|
|
3097
3214
|
if (!parentArtifact) {
|
|
3098
3215
|
throw new Error(
|
|
3099
|
-
`Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing. Run 'dreamboard test
|
|
3216
|
+
`Base '${base.definition.id}' extends '${base.definition.extends}', but the parent artifact is missing. Run 'dreamboard test' first.`
|
|
3100
3217
|
);
|
|
3101
3218
|
}
|
|
3102
3219
|
shadow.hydrate(parentArtifact.snapshot, parentArtifact.version);
|
|
@@ -3298,23 +3415,22 @@ async function runReducerNativeScenarios(options) {
|
|
|
3298
3415
|
}
|
|
3299
3416
|
return { passed, failed, results };
|
|
3300
3417
|
}
|
|
3301
|
-
|
|
3302
3418
|
export {
|
|
3419
|
+
assertDispatchResultWireContract,
|
|
3420
|
+
createActionPlanReplaySession,
|
|
3421
|
+
createScenarioActionPlan,
|
|
3422
|
+
createSessionFromScenario,
|
|
3423
|
+
ensureReducerNativeTestingFiles,
|
|
3303
3424
|
formatScenarioErrorForDisplay,
|
|
3425
|
+
generateReducerNativeArtifacts,
|
|
3304
3426
|
isReducerNativeTestingWorkspace,
|
|
3305
|
-
ensureReducerNativeTestingFiles,
|
|
3306
3427
|
loadTypedBases,
|
|
3307
3428
|
loadTypedScenarios,
|
|
3308
|
-
assertDispatchResultWireContract,
|
|
3309
|
-
createSessionFromScenario,
|
|
3310
3429
|
materializeScenarioReducerState,
|
|
3311
|
-
replayScenarioThroughBackend,
|
|
3312
|
-
createScenarioActionPlan,
|
|
3313
|
-
replayActionPlanThroughBackend,
|
|
3314
|
-
createActionPlanReplaySession,
|
|
3315
3430
|
replayActionPlanInSession,
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
runReducerNativeScenarios
|
|
3431
|
+
replayActionPlanThroughBackend,
|
|
3432
|
+
replayScenarioThroughBackend,
|
|
3433
|
+
runReducerNativeScenarios,
|
|
3434
|
+
writeReducerNativeGeneratedFiles
|
|
3319
3435
|
};
|
|
3320
|
-
//# sourceMappingURL=
|
|
3436
|
+
//# sourceMappingURL=reducer-native-test-harness-H6G6RBRY.mjs.map
|