@dreamboard-games/cli 0.1.30-alpha.2 → 0.1.30-alpha.4
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/dist/agent-verifier/agent-workspace-verifier.mjs +227 -0
- package/dist/agent-verifier/agent-workspace-verifier.mjs.map +1 -0
- package/dist/agent-verifier/chunk-27EEIZCI.mjs +185 -0
- package/dist/agent-verifier/chunk-27EEIZCI.mjs.map +1 -0
- package/dist/agent-verifier/chunk-5NYBTZB4.mjs +226 -0
- package/dist/agent-verifier/chunk-5NYBTZB4.mjs.map +1 -0
- package/dist/agent-verifier/chunk-776W3UGV.mjs +167 -0
- package/dist/agent-verifier/chunk-776W3UGV.mjs.map +1 -0
- package/dist/agent-verifier/chunk-C3VW3DTA.mjs +2909 -0
- package/dist/agent-verifier/chunk-C3VW3DTA.mjs.map +1 -0
- package/dist/agent-verifier/chunk-F2DIOJJZ.mjs +302 -0
- package/dist/agent-verifier/chunk-F2DIOJJZ.mjs.map +1 -0
- package/dist/agent-verifier/chunk-G42BGGG2.mjs +70 -0
- package/dist/agent-verifier/chunk-G42BGGG2.mjs.map +1 -0
- package/dist/agent-verifier/chunk-H6XDQJ3N.mjs +11 -0
- package/dist/agent-verifier/chunk-H76MT5UR.mjs +57 -0
- package/dist/agent-verifier/chunk-H76MT5UR.mjs.map +1 -0
- package/dist/agent-verifier/chunk-IAYRNVUC.mjs +49 -0
- package/dist/agent-verifier/chunk-IAYRNVUC.mjs.map +1 -0
- package/dist/agent-verifier/chunk-IDVQXGAO.mjs +222 -0
- package/dist/agent-verifier/chunk-IDVQXGAO.mjs.map +1 -0
- package/dist/agent-verifier/chunk-JO5AMVZU.mjs +1744 -0
- package/dist/agent-verifier/chunk-JO5AMVZU.mjs.map +1 -0
- package/dist/agent-verifier/chunk-JZTH3EMV.mjs +14523 -0
- package/dist/agent-verifier/chunk-JZTH3EMV.mjs.map +1 -0
- package/dist/agent-verifier/chunk-KDBSVLCF.mjs +624 -0
- package/dist/agent-verifier/chunk-KDBSVLCF.mjs.map +1 -0
- package/dist/agent-verifier/chunk-MW2QIWWA.mjs +729 -0
- package/dist/agent-verifier/chunk-MW2QIWWA.mjs.map +1 -0
- package/dist/agent-verifier/chunk-NAK77WXW.mjs +767 -0
- package/dist/agent-verifier/chunk-NAK77WXW.mjs.map +1 -0
- package/dist/agent-verifier/chunk-ON62IGWK.mjs +3137 -0
- package/dist/agent-verifier/chunk-ON62IGWK.mjs.map +1 -0
- package/dist/agent-verifier/chunk-QBAF7EYR.mjs +214 -0
- package/dist/agent-verifier/chunk-QBAF7EYR.mjs.map +1 -0
- package/dist/agent-verifier/chunk-QZH6IEZS.mjs +39 -0
- package/dist/agent-verifier/chunk-QZH6IEZS.mjs.map +1 -0
- package/dist/agent-verifier/chunk-TAEQKBJB.mjs +107 -0
- package/dist/agent-verifier/chunk-TAEQKBJB.mjs.map +1 -0
- package/dist/agent-verifier/chunk-UIOLGH4A.mjs +150 -0
- package/dist/agent-verifier/chunk-UIOLGH4A.mjs.map +1 -0
- package/dist/agent-verifier/chunk-XKCJBIRY.mjs +75 -0
- package/dist/agent-verifier/chunk-XKCJBIRY.mjs.map +1 -0
- package/dist/agent-verifier/chunk-XQXDOBYB.mjs +382 -0
- package/dist/agent-verifier/chunk-XQXDOBYB.mjs.map +1 -0
- package/dist/agent-verifier/chunk-YDIOW2BO.mjs +45 -0
- package/dist/agent-verifier/chunk-YDIOW2BO.mjs.map +1 -0
- package/dist/agent-verifier/chunk-YE7UAO3T.mjs +129 -0
- package/dist/agent-verifier/chunk-YE7UAO3T.mjs.map +1 -0
- package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs +261 -0
- package/dist/agent-verifier/chunk-Z6OZWUIZ.mjs.map +1 -0
- package/dist/agent-verifier/chunk-ZEELHSY3.mjs +20 -0
- package/dist/agent-verifier/chunk-ZEELHSY3.mjs.map +1 -0
- package/dist/agent-verifier/compile-576O7TYP.mjs +312 -0
- package/dist/agent-verifier/compile-576O7TYP.mjs.map +1 -0
- package/dist/agent-verifier/global-config-NYCSCAUI.mjs +18 -0
- package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs +135 -0
- package/dist/agent-verifier/keychain-backend-A3MRWLPF.mjs.map +1 -0
- package/dist/agent-verifier/local-files-QVJ2H3MH.mjs +45 -0
- package/dist/agent-verifier/local-files-QVJ2H3MH.mjs.map +1 -0
- package/dist/agent-verifier/local-typecheck-2JWG5IGL.mjs +10 -0
- package/dist/agent-verifier/local-typecheck-2JWG5IGL.mjs.map +1 -0
- package/dist/agent-verifier/materialize-workspace-OZKOQCSQ.mjs +89 -0
- package/dist/agent-verifier/materialize-workspace-OZKOQCSQ.mjs.map +1 -0
- package/dist/agent-verifier/project-state-XKUSCFSV.mjs +33 -0
- package/dist/agent-verifier/project-state-XKUSCFSV.mjs.map +1 -0
- package/dist/agent-verifier/prompt-VKHMCQT6.mjs +756 -0
- package/dist/agent-verifier/prompt-VKHMCQT6.mjs.map +1 -0
- package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs +20 -0
- package/dist/agent-verifier/reducer-bundle-preflight-7NYZF5ZT.mjs.map +1 -0
- package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs +11 -0
- package/dist/agent-verifier/reducer-contract-preflight-COD2CO22.mjs.map +1 -0
- package/dist/agent-verifier/reducer-native-test-harness-QC7HZUK4.mjs +50 -0
- package/dist/agent-verifier/reducer-native-test-harness-QC7HZUK4.mjs.map +1 -0
- package/dist/agent-verifier/static-scaffold-JBUE3ROP.mjs +27 -0
- package/dist/agent-verifier/static-scaffold-JBUE3ROP.mjs.map +1 -0
- package/dist/agent-verifier/sync-C6S3OGCD.mjs +588 -0
- package/dist/agent-verifier/sync-C6S3OGCD.mjs.map +1 -0
- package/dist/agent-verifier/test-Y5UGQV7J.mjs +353 -0
- package/dist/agent-verifier/test-Y5UGQV7J.mjs.map +1 -0
- package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs +10 -0
- package/dist/agent-verifier/workspace-codegen-WPZHMATU.mjs.map +1 -0
- package/dist/agent-verifier/workspace-dependencies-B6A2ZX55.mjs +15 -0
- package/dist/agent-verifier/workspace-dependencies-B6A2ZX55.mjs.map +1 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/{chunk-N7XPNNUI.js → chunk-3NRROR4P.js} +3 -3
- package/dist/{chunk-TAQKH67O.js → chunk-M4SCKH5M.js} +8 -2224
- package/dist/chunk-M4SCKH5M.js.map +1 -0
- package/dist/{global-config-S4ZIPECE.js → global-config-YBFEGJQG.js} +3 -3
- package/dist/global-config-YBFEGJQG.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/internal.js +3 -3
- package/dist/{keychain-backend-HDF4TZDL.js → keychain-backend-JHTXAKWC.js} +2 -2
- package/dist/{prompt-NDV3AE5L.js → prompt-GMZABCJC.js} +2 -2
- package/package.json +3 -2
- package/dist/chunk-SEGVTWSK.js +0 -44
- package/dist/chunk-TAQKH67O.js.map +0 -1
- /package/dist/{chunk-SEGVTWSK.js.map → agent-verifier/chunk-H6XDQJ3N.mjs.map} +0 -0
- /package/dist/{global-config-S4ZIPECE.js.map → agent-verifier/global-config-NYCSCAUI.mjs.map} +0 -0
- /package/dist/{chunk-N7XPNNUI.js.map → chunk-3NRROR4P.js.map} +0 -0
- /package/dist/{keychain-backend-HDF4TZDL.js.map → keychain-backend-JHTXAKWC.js.map} +0 -0
- /package/dist/{prompt-NDV3AE5L.js.map → prompt-GMZABCJC.js.map} +0 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
importTypeScriptModule
|
|
4
|
+
} from "./chunk-XKCJBIRY.mjs";
|
|
5
|
+
import {
|
|
6
|
+
external_exports
|
|
7
|
+
} from "./chunk-JZTH3EMV.mjs";
|
|
8
|
+
|
|
9
|
+
// src/services/project/reducer-bundle-preflight.ts
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { isPerPlayer, perPlayerSchema } from "@dreamboard-games/sdk/reducer";
|
|
12
|
+
import "@dreamboard-games/sdk/reducer-contract";
|
|
13
|
+
import { materializeManifestTable } from "@dreamboard-games/sdk/codegen";
|
|
14
|
+
globalThis.__DREAMBOARD_AUTHORING_WARNINGS__ = true;
|
|
15
|
+
var REDUCER_BUNDLE_ENTRY_PATH = path.join("app", "index.ts");
|
|
16
|
+
var PREFLIGHT_RNG_SEED = 1337;
|
|
17
|
+
function buildPlayerIds(playerCount) {
|
|
18
|
+
const ids = [];
|
|
19
|
+
for (let index = 1; index <= playerCount; index += 1) {
|
|
20
|
+
ids.push(`player-${index}`);
|
|
21
|
+
}
|
|
22
|
+
return ids;
|
|
23
|
+
}
|
|
24
|
+
function buildScenarios(manifest) {
|
|
25
|
+
const rawCounts = [
|
|
26
|
+
manifest.players?.minPlayers,
|
|
27
|
+
manifest.players?.maxPlayers
|
|
28
|
+
].filter(
|
|
29
|
+
(count) => typeof count === "number" && Number.isFinite(count) && count > 0
|
|
30
|
+
);
|
|
31
|
+
const playerCounts = Array.from(new Set(rawCounts));
|
|
32
|
+
if (playerCounts.length === 0) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
const profiles = manifest.setupProfiles ?? [];
|
|
36
|
+
const profileIds = profiles.length > 0 ? profiles.map((profile) => profile.id) : [null];
|
|
37
|
+
const scenarios = [];
|
|
38
|
+
const seen = /* @__PURE__ */ new Set();
|
|
39
|
+
for (const profileId of profileIds) {
|
|
40
|
+
for (const count of playerCounts) {
|
|
41
|
+
const key = `${profileId ?? "<default>"}:${count}`;
|
|
42
|
+
if (seen.has(key)) continue;
|
|
43
|
+
seen.add(key);
|
|
44
|
+
scenarios.push({ setupProfileId: profileId, playerCount: count });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return scenarios;
|
|
48
|
+
}
|
|
49
|
+
function describeScenario(scenario) {
|
|
50
|
+
const profileDesc = scenario.setupProfileId ? `setup profile '${scenario.setupProfileId}'` : "no setup profile";
|
|
51
|
+
return `${profileDesc}, ${scenario.playerCount} player${scenario.playerCount === 1 ? "" : "s"}`;
|
|
52
|
+
}
|
|
53
|
+
function summarizeError(error) {
|
|
54
|
+
if (error instanceof Error) {
|
|
55
|
+
const firstLine = error.message.split("\n").map((line) => line.trim()).find((line) => line.length > 0) ?? "Unknown failure";
|
|
56
|
+
const detail = error.stack && error.stack !== error.message ? error.stack : void 0;
|
|
57
|
+
return { headline: firstLine, detail };
|
|
58
|
+
}
|
|
59
|
+
return { headline: String(error ?? "Unknown failure") };
|
|
60
|
+
}
|
|
61
|
+
function assertViewPerPlayerSeatsValid(view, expectedPlayerIds, breadcrumb = []) {
|
|
62
|
+
if (view === null || view === void 0) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
if (isPerPlayer(view)) {
|
|
66
|
+
const schema = perPlayerSchema(external_exports.unknown(), {
|
|
67
|
+
players: expectedPlayerIds
|
|
68
|
+
});
|
|
69
|
+
const result = schema.safeParse(view);
|
|
70
|
+
if (!result.success) {
|
|
71
|
+
const firstIssue = result.error.issues[0];
|
|
72
|
+
const location = breadcrumb.length > 0 ? ` at view.${breadcrumb.join(".")}` : "";
|
|
73
|
+
const message = firstIssue?.message ?? "PerPlayer seat mismatch";
|
|
74
|
+
return `PerPlayer seat mismatch${location}: ${message}`;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(view)) {
|
|
79
|
+
for (let index = 0; index < view.length; index += 1) {
|
|
80
|
+
const result = assertViewPerPlayerSeatsValid(
|
|
81
|
+
view[index],
|
|
82
|
+
expectedPlayerIds,
|
|
83
|
+
[...breadcrumb, `[${index}]`]
|
|
84
|
+
);
|
|
85
|
+
if (result) return result;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (typeof view === "object") {
|
|
90
|
+
for (const [key, value] of Object.entries(
|
|
91
|
+
view
|
|
92
|
+
)) {
|
|
93
|
+
const result = assertViewPerPlayerSeatsValid(value, expectedPlayerIds, [
|
|
94
|
+
...breadcrumb,
|
|
95
|
+
key
|
|
96
|
+
]);
|
|
97
|
+
if (result) return result;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
function identityShuffle(values) {
|
|
104
|
+
return [...values];
|
|
105
|
+
}
|
|
106
|
+
async function driveReducerBundleThroughScenarios(options) {
|
|
107
|
+
const { manifest, bundle, scenarios } = options;
|
|
108
|
+
const rngSeed = options.rngSeed ?? PREFLIGHT_RNG_SEED;
|
|
109
|
+
const failures = [];
|
|
110
|
+
const tableCache = /* @__PURE__ */ new Map();
|
|
111
|
+
for (const scenario of scenarios) {
|
|
112
|
+
let tableEntry = tableCache.get(scenario.playerCount);
|
|
113
|
+
if (!tableEntry) {
|
|
114
|
+
try {
|
|
115
|
+
const playerIds2 = buildPlayerIds(scenario.playerCount);
|
|
116
|
+
const table2 = JSON.parse(
|
|
117
|
+
JSON.stringify(
|
|
118
|
+
materializeManifestTable({
|
|
119
|
+
manifest,
|
|
120
|
+
playerIds: playerIds2,
|
|
121
|
+
shuffleItems: identityShuffle
|
|
122
|
+
})
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
tableEntry = { table: table2, playerIds: playerIds2 };
|
|
126
|
+
} catch (error) {
|
|
127
|
+
tableEntry = { error };
|
|
128
|
+
}
|
|
129
|
+
tableCache.set(scenario.playerCount, tableEntry);
|
|
130
|
+
}
|
|
131
|
+
if ("error" in tableEntry) {
|
|
132
|
+
const summary = summarizeError(tableEntry.error);
|
|
133
|
+
failures.push({
|
|
134
|
+
scenario,
|
|
135
|
+
phase: "BUILD_GAME_STATE",
|
|
136
|
+
headline: summary.headline,
|
|
137
|
+
detail: summary.detail
|
|
138
|
+
});
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const { table, playerIds } = tableEntry;
|
|
142
|
+
let sessionState;
|
|
143
|
+
try {
|
|
144
|
+
sessionState = await bundle.initialize({
|
|
145
|
+
table,
|
|
146
|
+
playerIds: [...playerIds],
|
|
147
|
+
rngSeed,
|
|
148
|
+
setup: scenario.setupProfileId ? { profileId: scenario.setupProfileId, optionValues: {} } : null
|
|
149
|
+
});
|
|
150
|
+
} catch (error) {
|
|
151
|
+
const summary = summarizeError(error);
|
|
152
|
+
failures.push({
|
|
153
|
+
scenario,
|
|
154
|
+
phase: "INITIALIZE",
|
|
155
|
+
headline: summary.headline,
|
|
156
|
+
detail: summary.detail
|
|
157
|
+
});
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
let projection;
|
|
161
|
+
try {
|
|
162
|
+
projection = bundle.projectSeatsDynamic({
|
|
163
|
+
state: sessionState,
|
|
164
|
+
playerIds: [...playerIds]
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const summary = summarizeError(error);
|
|
168
|
+
failures.push({
|
|
169
|
+
scenario,
|
|
170
|
+
phase: "PROJECT_SEATS",
|
|
171
|
+
headline: summary.headline,
|
|
172
|
+
detail: summary.detail
|
|
173
|
+
});
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
const seats = projection?.seats ?? {};
|
|
177
|
+
for (const playerId of playerIds) {
|
|
178
|
+
const seat = seats[playerId];
|
|
179
|
+
if (!seat) {
|
|
180
|
+
failures.push({
|
|
181
|
+
scenario,
|
|
182
|
+
phase: "PROJECT_SEATS",
|
|
183
|
+
playerId,
|
|
184
|
+
headline: `projectSeatsDynamic() did not return an entry for seat '${playerId}'.`
|
|
185
|
+
});
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const mismatch = assertViewPerPlayerSeatsValid(seat.view, playerIds);
|
|
189
|
+
if (mismatch) {
|
|
190
|
+
failures.push({
|
|
191
|
+
scenario,
|
|
192
|
+
phase: "VALIDATE_VIEW",
|
|
193
|
+
playerId,
|
|
194
|
+
headline: mismatch
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return failures;
|
|
200
|
+
}
|
|
201
|
+
async function runReducerBundleSmoke(options) {
|
|
202
|
+
const { projectRoot, manifest } = options;
|
|
203
|
+
const scenarios = buildScenarios(manifest);
|
|
204
|
+
if (scenarios.length === 0) {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
const entryPath = path.join(projectRoot, REDUCER_BUNDLE_ENTRY_PATH);
|
|
208
|
+
let module;
|
|
209
|
+
try {
|
|
210
|
+
module = await importTypeScriptModule(
|
|
211
|
+
entryPath
|
|
212
|
+
);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
const summary = summarizeError(error);
|
|
215
|
+
throw new Error(
|
|
216
|
+
[
|
|
217
|
+
`Dreamboard could not import \`${REDUCER_BUNDLE_ENTRY_PATH}\` during \`dreamboard sync\`.`,
|
|
218
|
+
"Fix the reducer bundle entry so it can be imported locally, then run `dreamboard sync` again.",
|
|
219
|
+
`Original error: ${summary.headline}`
|
|
220
|
+
].join(" ")
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
const bundle = module.default;
|
|
224
|
+
if (!bundle || typeof bundle.initialize !== "function") {
|
|
225
|
+
throw new Error(
|
|
226
|
+
[
|
|
227
|
+
`\`${REDUCER_BUNDLE_ENTRY_PATH}\` does not export a reducer bundle as its default export.`,
|
|
228
|
+
"Export `createReducerBundle(game)` from `app/index.ts` so the compile pipeline can smoke-test it."
|
|
229
|
+
].join(" ")
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
return driveReducerBundleThroughScenarios({ manifest, bundle, scenarios });
|
|
233
|
+
}
|
|
234
|
+
function formatFailureLines(failures) {
|
|
235
|
+
return failures.map((failure) => {
|
|
236
|
+
const location = failure.playerId ? ` (seat ${failure.playerId})` : "";
|
|
237
|
+
return `\u2022 [${failure.phase}] ${describeScenario(failure.scenario)}${location}: ${failure.headline}`;
|
|
238
|
+
}).join("\n");
|
|
239
|
+
}
|
|
240
|
+
async function assertReducerBundleSmoke(options) {
|
|
241
|
+
const failures = await runReducerBundleSmoke(options);
|
|
242
|
+
if (failures.length === 0) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
throw new Error(
|
|
246
|
+
[
|
|
247
|
+
"Reducer bundle preflight failed during `dreamboard sync`.",
|
|
248
|
+
"Fix the reported scenarios locally before syncing so the backend does not catch them after start-game:",
|
|
249
|
+
formatFailureLines(failures)
|
|
250
|
+
].join("\n")
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export {
|
|
255
|
+
buildScenarios,
|
|
256
|
+
assertViewPerPlayerSeatsValid,
|
|
257
|
+
driveReducerBundleThroughScenarios,
|
|
258
|
+
runReducerBundleSmoke,
|
|
259
|
+
assertReducerBundleSmoke
|
|
260
|
+
};
|
|
261
|
+
//# sourceMappingURL=chunk-Z6OZWUIZ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/project/reducer-bundle-preflight.ts"],"sourcesContent":["import path from \"node:path\";\nimport type {\n GameTopologyManifest,\n SetupProfileSpec,\n} from \"@dreamboard-games/sdk/types\";\nimport { isPerPlayer, perPlayerSchema } from \"@dreamboard-games/sdk/reducer\";\nimport {\n type ReducerBundleContract,\n type ReducerWire as Wire,\n} from \"@dreamboard-games/sdk/reducer-contract\";\nimport { materializeManifestTable } from \"@dreamboard-games/sdk/codegen\";\nimport { z } from \"zod\";\nimport { importTypeScriptModule } from \"../../utils/ts-module-loader.js\";\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 ReducerBundleContract,\n \"initialize\" | \"projectSeatsDynamic\"\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 breadcrumb: string[] = [],\n): string | null {\n if (view === null || view === undefined) {\n return null;\n }\n\n if (isPerPlayer(view)) {\n const schema = 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 [...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(value, expectedPlayerIds, [\n ...breadcrumb,\n 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 rngSeed?: number;\n}): Promise<ReducerBundleSmokeFailure[]> {\n const { manifest, bundle, scenarios } = 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 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(seat.view, playerIds);\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}\\` during \\`dreamboard sync\\`.`,\n \"Fix the reducer bundle entry so it can be imported locally, then run `dreamboard sync` again.\",\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 return driveReducerBundleThroughScenarios({ manifest, bundle, scenarios });\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 by `dreamboard sync`. 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 * Sync 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 during `dreamboard sync`.\",\n \"Fix the reported scenarios locally before syncing so the backend does not catch them after start-game:\",\n formatFailureLines(failures),\n ].join(\"\\n\"),\n );\n}\n"],"mappings":"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","names":["playerIds","table"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/services/project/framework-dependencies.ts
|
|
4
|
+
var FRAMEWORK_REACT_VERSION = "19.2.6";
|
|
5
|
+
var FRAMEWORK_ZOD_VERSION = "4.4.3";
|
|
6
|
+
var FRAMEWORK_REACT_DEPENDENCIES = {
|
|
7
|
+
react: FRAMEWORK_REACT_VERSION,
|
|
8
|
+
"react-dom": FRAMEWORK_REACT_VERSION
|
|
9
|
+
};
|
|
10
|
+
var FRAMEWORK_PNPM_OVERRIDES = {
|
|
11
|
+
...FRAMEWORK_REACT_DEPENDENCIES,
|
|
12
|
+
zod: FRAMEWORK_ZOD_VERSION
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
FRAMEWORK_ZOD_VERSION,
|
|
17
|
+
FRAMEWORK_REACT_DEPENDENCIES,
|
|
18
|
+
FRAMEWORK_PNPM_OVERRIDES
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=chunk-ZEELHSY3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/project/framework-dependencies.ts"],"sourcesContent":["export const FRAMEWORK_REACT_VERSION = \"19.2.6\";\nexport const FRAMEWORK_ZOD_VERSION = \"4.4.3\";\n\nexport const FRAMEWORK_REACT_DEPENDENCIES = {\n react: FRAMEWORK_REACT_VERSION,\n \"react-dom\": FRAMEWORK_REACT_VERSION,\n} as const;\n\nexport const FRAMEWORK_PNPM_OVERRIDES = {\n ...FRAMEWORK_REACT_DEPENDENCIES,\n zod: FRAMEWORK_ZOD_VERSION,\n} as const;\n"],"mappings":";;;AAAO,IAAM,0BAA0B;AAChC,IAAM,wBAAwB;AAE9B,IAAM,+BAA+B;AAAA,EAC1C,OAAO;AAAA,EACP,aAAa;AACf;AAEO,IAAM,2BAA2B;AAAA,EACtC,GAAG;AAAA,EACH,KAAK;AACP;","names":[]}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
resolveRemoteProject
|
|
4
|
+
} from "./chunk-QZH6IEZS.mjs";
|
|
5
|
+
import {
|
|
6
|
+
CONFIG_FLAG_ARGS,
|
|
7
|
+
defineCommand,
|
|
8
|
+
findProjectCompiledResultsForRevision,
|
|
9
|
+
parseCompileCommandArgs,
|
|
10
|
+
queueProjectRevisionCompileSdk,
|
|
11
|
+
waitForCompiledResultJobSdk
|
|
12
|
+
} from "./chunk-KDBSVLCF.mjs";
|
|
13
|
+
import {
|
|
14
|
+
getProjectAuthoringState,
|
|
15
|
+
getProjectPendingAuthoringSync,
|
|
16
|
+
setLatestCompileAttempt
|
|
17
|
+
} from "./chunk-YE7UAO3T.mjs";
|
|
18
|
+
import {
|
|
19
|
+
runLocalTypecheck
|
|
20
|
+
} from "./chunk-UIOLGH4A.mjs";
|
|
21
|
+
import {
|
|
22
|
+
formatCliError
|
|
23
|
+
} from "./chunk-IDVQXGAO.mjs";
|
|
24
|
+
import {
|
|
25
|
+
assertCompilerPortableDependencies,
|
|
26
|
+
assertReleaseEnvironmentPortableDependencies,
|
|
27
|
+
consola,
|
|
28
|
+
resolveProjectContext
|
|
29
|
+
} from "./chunk-JO5AMVZU.mjs";
|
|
30
|
+
import "./chunk-5NYBTZB4.mjs";
|
|
31
|
+
import {
|
|
32
|
+
getLocalDiff
|
|
33
|
+
} from "./chunk-27EEIZCI.mjs";
|
|
34
|
+
import {
|
|
35
|
+
assertCliStaticScaffoldComplete
|
|
36
|
+
} from "./chunk-MW2QIWWA.mjs";
|
|
37
|
+
import "./chunk-F2DIOJJZ.mjs";
|
|
38
|
+
import {
|
|
39
|
+
updateProjectState
|
|
40
|
+
} from "./chunk-776W3UGV.mjs";
|
|
41
|
+
import "./chunk-C3VW3DTA.mjs";
|
|
42
|
+
import "./chunk-JZTH3EMV.mjs";
|
|
43
|
+
import "./chunk-QBAF7EYR.mjs";
|
|
44
|
+
import "./chunk-NAK77WXW.mjs";
|
|
45
|
+
import "./chunk-TAEQKBJB.mjs";
|
|
46
|
+
import "./chunk-IAYRNVUC.mjs";
|
|
47
|
+
import "./chunk-ZEELHSY3.mjs";
|
|
48
|
+
import "./chunk-H76MT5UR.mjs";
|
|
49
|
+
import "./chunk-H6XDQJ3N.mjs";
|
|
50
|
+
|
|
51
|
+
// src/commands/compile.ts
|
|
52
|
+
function formatDiagnosticsSummary(diagnostics) {
|
|
53
|
+
const firstMessage = diagnostics?.find(
|
|
54
|
+
(diagnostic) => diagnostic.message
|
|
55
|
+
)?.message;
|
|
56
|
+
return firstMessage?.trim() || void 0;
|
|
57
|
+
}
|
|
58
|
+
function formatCompileJobProgressMessage(job) {
|
|
59
|
+
const phase = job.phase ? ` [${job.phase}]` : "";
|
|
60
|
+
const detail = job.message ? ` ${job.message}` : "";
|
|
61
|
+
if (job.status === "PENDING") {
|
|
62
|
+
const queue = typeof job.queuePosition === "number" ? ` (queue ${job.queuePosition + 1})` : "";
|
|
63
|
+
return `Compile queued${queue}${phase}${detail}`.trim();
|
|
64
|
+
}
|
|
65
|
+
if (job.status === "RUNNING") {
|
|
66
|
+
return `Compile running${phase}${detail}`.trim();
|
|
67
|
+
}
|
|
68
|
+
if (job.status === "FAILED") {
|
|
69
|
+
return `Compile failed${phase}${detail}`.trim();
|
|
70
|
+
}
|
|
71
|
+
return `Compile ${job.status.toLowerCase()}${phase}${detail}`.trim();
|
|
72
|
+
}
|
|
73
|
+
function formatFailedCompileJobSummary(job) {
|
|
74
|
+
return formatCompileJobProgressMessage({
|
|
75
|
+
status: "FAILED",
|
|
76
|
+
phase: job.phase,
|
|
77
|
+
message: job.errorMessage ?? job.message ?? void 0
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function formatFailedCompileJobWithCompiledResultMessage(options) {
|
|
81
|
+
return `${formatFailedCompileJobSummary(options.job)}. The backend created compiled result ${options.compiledResultId}, but the compile job did not complete cleanly. Run 'dreamboard compile' again after fixing the backend/compiler issue.`;
|
|
82
|
+
}
|
|
83
|
+
function formatRemoteCompileCommandError(options) {
|
|
84
|
+
const detail = options.message.trim();
|
|
85
|
+
const hasActionableTerminalContext = /^Compile\s+(failed|completed|cancelled|interrupted)\b/i.test(detail);
|
|
86
|
+
if (options.jobId) {
|
|
87
|
+
if (hasActionableTerminalContext) {
|
|
88
|
+
return `Remote compile job ${options.jobId} could not be completed. ${detail}`;
|
|
89
|
+
}
|
|
90
|
+
return `Remote compile job ${options.jobId} could not be completed. ${detail} Check backend health and try 'dreamboard compile' again.`;
|
|
91
|
+
}
|
|
92
|
+
return `Remote compile could not be started. ${detail} Check backend health and try 'dreamboard compile' again.`;
|
|
93
|
+
}
|
|
94
|
+
async function persistFailedCompileAttempt(options) {
|
|
95
|
+
const nextProjectConfig = setLatestCompileAttempt(options.projectConfig, {
|
|
96
|
+
resultId: void 0,
|
|
97
|
+
jobId: options.jobId,
|
|
98
|
+
revisionDigest: options.revisionDigest,
|
|
99
|
+
authoringStateId: options.authoringStateId ?? options.revisionDigest,
|
|
100
|
+
status: "failed",
|
|
101
|
+
diagnosticsSummary: options.diagnosticsSummary
|
|
102
|
+
});
|
|
103
|
+
await updateProjectState(options.projectRoot, nextProjectConfig);
|
|
104
|
+
}
|
|
105
|
+
var compile_default = defineCommand({
|
|
106
|
+
meta: {
|
|
107
|
+
name: "compile",
|
|
108
|
+
description: "Compile the current remote authoring head"
|
|
109
|
+
},
|
|
110
|
+
args: {
|
|
111
|
+
debug: {
|
|
112
|
+
type: "boolean",
|
|
113
|
+
description: "Print additional compile progress context",
|
|
114
|
+
default: false
|
|
115
|
+
},
|
|
116
|
+
"skip-local-check": {
|
|
117
|
+
type: "boolean",
|
|
118
|
+
description: "Skip the best-effort local typecheck before compiling",
|
|
119
|
+
default: false
|
|
120
|
+
},
|
|
121
|
+
...CONFIG_FLAG_ARGS
|
|
122
|
+
},
|
|
123
|
+
async run({ args }) {
|
|
124
|
+
const parsedArgs = parseCompileCommandArgs(args);
|
|
125
|
+
const { projectRoot, projectConfig, config } = await resolveProjectContext(parsedArgs);
|
|
126
|
+
let nextProjectConfig = projectConfig;
|
|
127
|
+
await assertReleaseEnvironmentPortableDependencies({
|
|
128
|
+
projectRoot,
|
|
129
|
+
projectConfig: nextProjectConfig,
|
|
130
|
+
environment: config.environment
|
|
131
|
+
});
|
|
132
|
+
await assertCompilerPortableDependencies({
|
|
133
|
+
projectRoot,
|
|
134
|
+
projectConfig: nextProjectConfig
|
|
135
|
+
});
|
|
136
|
+
const diff = await getLocalDiff(projectRoot);
|
|
137
|
+
await assertCliStaticScaffoldComplete(projectRoot, diff.deleted);
|
|
138
|
+
if (diff.modified.length > 0 || diff.added.length > 0 || diff.deleted.length > 0) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
"Local authored changes are not synced yet. Run 'dreamboard sync' before compiling."
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
const remoteProject = await resolveRemoteProject({
|
|
144
|
+
projectRoot,
|
|
145
|
+
projectConfig: nextProjectConfig,
|
|
146
|
+
config
|
|
147
|
+
});
|
|
148
|
+
nextProjectConfig = remoteProject.projectConfig;
|
|
149
|
+
const localAuthoring = getProjectAuthoringState(nextProjectConfig);
|
|
150
|
+
const pendingSync = getProjectPendingAuthoringSync(nextProjectConfig);
|
|
151
|
+
if (pendingSync) {
|
|
152
|
+
if (pendingSync.phase === "authoring_state_created") {
|
|
153
|
+
throw new Error(
|
|
154
|
+
"Previous sync reached the remote authored head, but local scaffold finalization did not complete. Run 'dreamboard sync' again before compiling."
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
throw new Error(
|
|
158
|
+
"Previous sync uploaded source changes but did not finish creating the authored head. Run 'dreamboard sync' again before compiling."
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const localRevisionDigest = localAuthoring.revisionDigest ?? nextProjectConfig.remoteHeadDigest;
|
|
162
|
+
if (!localRevisionDigest) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
"This workspace does not know its authored revision yet. Run 'dreamboard sync' first."
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const remoteRevisionDigest = remoteProject.project.head?.revisionDigest;
|
|
168
|
+
if (!remoteRevisionDigest) {
|
|
169
|
+
throw new Error("Remote has no authored project revision to compile yet.");
|
|
170
|
+
}
|
|
171
|
+
if (remoteRevisionDigest !== localRevisionDigest) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`Remote project head is ${remoteRevisionDigest} but this workspace is based on ${localRevisionDigest}. Run 'dreamboard pull' before compiling.`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
if (!parsedArgs["skip-local-check"]) {
|
|
177
|
+
consola.start("Running local typecheck...");
|
|
178
|
+
const typecheckResult = await runLocalTypecheck(projectRoot);
|
|
179
|
+
if (typecheckResult.skipped) {
|
|
180
|
+
if (typecheckResult.output) {
|
|
181
|
+
consola.warn(typecheckResult.output);
|
|
182
|
+
}
|
|
183
|
+
} else if (!typecheckResult.success) {
|
|
184
|
+
if (typecheckResult.output) {
|
|
185
|
+
consola.error(typecheckResult.output);
|
|
186
|
+
}
|
|
187
|
+
throw new Error(
|
|
188
|
+
"Local typecheck failed. Fix the diagnostics or re-run with --skip-local-check."
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
consola.success("Local typecheck passed.");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const existingCompiledResult = (await findProjectCompiledResultsForRevision({
|
|
195
|
+
projectId: nextProjectConfig.projectId,
|
|
196
|
+
revisionDigest: localRevisionDigest
|
|
197
|
+
})).find((result) => result.success);
|
|
198
|
+
if (existingCompiledResult) {
|
|
199
|
+
nextProjectConfig = setLatestCompileAttempt(nextProjectConfig, {
|
|
200
|
+
resultId: existingCompiledResult.id,
|
|
201
|
+
jobId: void 0,
|
|
202
|
+
revisionDigest: localRevisionDigest,
|
|
203
|
+
authoringStateId: existingCompiledResult.authoringStateId ?? localRevisionDigest,
|
|
204
|
+
status: "successful",
|
|
205
|
+
diagnosticsSummary: void 0
|
|
206
|
+
});
|
|
207
|
+
await updateProjectState(projectRoot, nextProjectConfig);
|
|
208
|
+
consola.success(
|
|
209
|
+
`Reusing compiled ${existingCompiledResult.id} for authored state ${existingCompiledResult.authoringStateId}.`
|
|
210
|
+
);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
let compileJobId;
|
|
214
|
+
let compileJobStatus;
|
|
215
|
+
let compileJobPhase;
|
|
216
|
+
let compileJobMessage;
|
|
217
|
+
let compileJobErrorMessage;
|
|
218
|
+
let compiledResult;
|
|
219
|
+
try {
|
|
220
|
+
const compileJob = await queueProjectRevisionCompileSdk({
|
|
221
|
+
projectId: nextProjectConfig.projectId,
|
|
222
|
+
revisionDigest: localRevisionDigest
|
|
223
|
+
});
|
|
224
|
+
compileJobId = compileJob.jobId;
|
|
225
|
+
if (!compileJobId) {
|
|
226
|
+
throw new Error("Failed to create compile job: missing jobId.");
|
|
227
|
+
}
|
|
228
|
+
({
|
|
229
|
+
job: {
|
|
230
|
+
status: compileJobStatus,
|
|
231
|
+
phase: compileJobPhase,
|
|
232
|
+
message: compileJobMessage,
|
|
233
|
+
errorMessage: compileJobErrorMessage
|
|
234
|
+
},
|
|
235
|
+
compiledResult
|
|
236
|
+
} = await waitForCompiledResultJobSdk({
|
|
237
|
+
projectId: nextProjectConfig.projectId,
|
|
238
|
+
jobId: compileJobId,
|
|
239
|
+
onProgress: (job) => {
|
|
240
|
+
const message = formatCompileJobProgressMessage(job);
|
|
241
|
+
if (parsedArgs.debug) {
|
|
242
|
+
consola.info(message);
|
|
243
|
+
} else {
|
|
244
|
+
consola.start(message);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}));
|
|
248
|
+
} catch (error) {
|
|
249
|
+
const message = formatCliError(error);
|
|
250
|
+
if (compileJobId) {
|
|
251
|
+
await persistFailedCompileAttempt({
|
|
252
|
+
projectRoot,
|
|
253
|
+
projectConfig: nextProjectConfig,
|
|
254
|
+
revisionDigest: localRevisionDigest,
|
|
255
|
+
authoringStateId: localAuthoring.authoringStateId,
|
|
256
|
+
diagnosticsSummary: message,
|
|
257
|
+
jobId: compileJobId
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
throw new Error(
|
|
261
|
+
formatRemoteCompileCommandError({
|
|
262
|
+
message,
|
|
263
|
+
jobId: compileJobId
|
|
264
|
+
})
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
const failedJobProducedCompiledResult = compileJobStatus === "FAILED" && compiledResult.success;
|
|
268
|
+
const failedJobWithCompiledResultSummary = failedJobProducedCompiledResult ? formatFailedCompileJobSummary({
|
|
269
|
+
phase: compileJobPhase,
|
|
270
|
+
message: compileJobMessage,
|
|
271
|
+
errorMessage: compileJobErrorMessage
|
|
272
|
+
}) : void 0;
|
|
273
|
+
nextProjectConfig = setLatestCompileAttempt(nextProjectConfig, {
|
|
274
|
+
resultId: compiledResult.id,
|
|
275
|
+
jobId: compileJobId,
|
|
276
|
+
revisionDigest: localRevisionDigest,
|
|
277
|
+
authoringStateId: compiledResult.authoringStateId ?? localRevisionDigest,
|
|
278
|
+
status: compiledResult.success && !failedJobProducedCompiledResult ? "successful" : "failed",
|
|
279
|
+
diagnosticsSummary: failedJobWithCompiledResultSummary ?? formatDiagnosticsSummary(compiledResult.diagnostics)
|
|
280
|
+
});
|
|
281
|
+
await updateProjectState(projectRoot, nextProjectConfig);
|
|
282
|
+
if (failedJobProducedCompiledResult) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
formatFailedCompileJobWithCompiledResultMessage({
|
|
285
|
+
compiledResultId: compiledResult.id,
|
|
286
|
+
job: {
|
|
287
|
+
phase: compileJobPhase,
|
|
288
|
+
message: compileJobMessage,
|
|
289
|
+
errorMessage: compileJobErrorMessage
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
if (!compiledResult.success) {
|
|
295
|
+
for (const diagnostic of compiledResult.diagnostics ?? []) {
|
|
296
|
+
if (diagnostic.message) {
|
|
297
|
+
consola.error(diagnostic.message);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
throw new Error(
|
|
301
|
+
"Remote compile failed, but your authored state is synced. Fix the diagnostics and run 'dreamboard compile' again."
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
consola.success(
|
|
305
|
+
`Compiled ${compiledResult.id} for revision ${localRevisionDigest}.`
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
export {
|
|
310
|
+
compile_default as default
|
|
311
|
+
};
|
|
312
|
+
//# sourceMappingURL=compile-576O7TYP.mjs.map
|