@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,729 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
REDUCER_TESTING_TYPES_WRAPPER_CONTENT
|
|
4
|
+
} from "./chunk-F2DIOJJZ.mjs";
|
|
5
|
+
import {
|
|
6
|
+
IS_PUBLISHED_BUILD,
|
|
7
|
+
loadProjectConfig
|
|
8
|
+
} from "./chunk-776W3UGV.mjs";
|
|
9
|
+
import {
|
|
10
|
+
isDynamicSeedPath,
|
|
11
|
+
materializeManifest
|
|
12
|
+
} from "./chunk-C3VW3DTA.mjs";
|
|
13
|
+
import {
|
|
14
|
+
resolveCliRepoRoot
|
|
15
|
+
} from "./chunk-QBAF7EYR.mjs";
|
|
16
|
+
import {
|
|
17
|
+
ensureDir,
|
|
18
|
+
exists,
|
|
19
|
+
readJsonFile,
|
|
20
|
+
readTextFile,
|
|
21
|
+
readTextFileIfExists,
|
|
22
|
+
writeTextFile
|
|
23
|
+
} from "./chunk-IAYRNVUC.mjs";
|
|
24
|
+
import {
|
|
25
|
+
FRAMEWORK_PNPM_OVERRIDES,
|
|
26
|
+
FRAMEWORK_REACT_DEPENDENCIES,
|
|
27
|
+
FRAMEWORK_ZOD_VERSION
|
|
28
|
+
} from "./chunk-ZEELHSY3.mjs";
|
|
29
|
+
import {
|
|
30
|
+
MANIFEST_TYPECHECK_CONFIG_FILE,
|
|
31
|
+
PROJECT_CONFIG_FILE,
|
|
32
|
+
PROJECT_DIR_NAME
|
|
33
|
+
} from "./chunk-H76MT5UR.mjs";
|
|
34
|
+
|
|
35
|
+
// src/services/project/static-scaffold.ts
|
|
36
|
+
import { existsSync, readFileSync } from "fs";
|
|
37
|
+
import { readdir, readFile, rm, rmdir, unlink } from "fs/promises";
|
|
38
|
+
import path from "path";
|
|
39
|
+
import { fileURLToPath } from "url";
|
|
40
|
+
var DREAMBOARD_SYNC_COMMAND = "dreamboard sync";
|
|
41
|
+
var DREAMBOARD_GITIGNORE_BLOCK = [
|
|
42
|
+
"# Dreamboard local state",
|
|
43
|
+
".dreamboard/state.json",
|
|
44
|
+
".dreamboard/snapshot.json",
|
|
45
|
+
".dreamboard/dev/",
|
|
46
|
+
".dreamboard/generated/",
|
|
47
|
+
""
|
|
48
|
+
].join("\n");
|
|
49
|
+
var TESTING_TYPES_STUB = "export function defineScenario(scenario) { return scenario; }\n";
|
|
50
|
+
var GENERATED_TESTING_TYPES_PREFIX = "// Generated by dreamboard";
|
|
51
|
+
var GENERATED_SCENARIO_PREFIX = "// Generated by dreamboard scaffold.";
|
|
52
|
+
var LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT = `export {
|
|
53
|
+
ErrorBoundary,
|
|
54
|
+
type ErrorBoundaryProps,
|
|
55
|
+
} from "@dreamboard-games/sdk/ui";
|
|
56
|
+
export { PluginRuntime, type PluginRuntimeProps } from "@dreamboard-games/sdk/runtime";
|
|
57
|
+
`;
|
|
58
|
+
var OLD_LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT = [
|
|
59
|
+
"export {",
|
|
60
|
+
" ErrorBoundary,",
|
|
61
|
+
" PluginRuntime,",
|
|
62
|
+
" type ErrorBoundaryProps,",
|
|
63
|
+
" type PluginRuntimeProps,",
|
|
64
|
+
`} from "@dreamboard/ui-${"sdk"}";`,
|
|
65
|
+
""
|
|
66
|
+
].join("\n");
|
|
67
|
+
var OLD_PUBLIC_DREAMBOARD_COMPONENT_INDEX_CONTENT = [
|
|
68
|
+
"export {",
|
|
69
|
+
" ErrorBoundary,",
|
|
70
|
+
" PluginRuntime,",
|
|
71
|
+
" type ErrorBoundaryProps,",
|
|
72
|
+
" type PluginRuntimeProps,",
|
|
73
|
+
`} from "@dreamboard-games/ui-${"sdk"}";`,
|
|
74
|
+
""
|
|
75
|
+
].join("\n");
|
|
76
|
+
var INITIAL_SCENARIO_CONTENT = `${GENERATED_SCENARIO_PREFIX}
|
|
77
|
+
import { defineScenario } from "../testing-types";
|
|
78
|
+
|
|
79
|
+
export default defineScenario({
|
|
80
|
+
id: "smoke-initial-turn",
|
|
81
|
+
description:
|
|
82
|
+
"Sanity check that the scaffolded workspace boots into its initial phase.",
|
|
83
|
+
from: "initial-turn",
|
|
84
|
+
when: async () => undefined,
|
|
85
|
+
then: ({ expect, interactions, players, state }) => {
|
|
86
|
+
const playerIds = players();
|
|
87
|
+
expect(playerIds).toHaveLength(playerIds.length);
|
|
88
|
+
expect(playerIds.length).toBeGreaterThanOrEqual(1);
|
|
89
|
+
expect(state()).toBe("setup");
|
|
90
|
+
for (const playerId of playerIds) {
|
|
91
|
+
expect(interactions(playerId)).toEqual([]);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
`;
|
|
96
|
+
var STATIC_ASSET_ROOT = resolveStaticAssetRoot();
|
|
97
|
+
var SDK_PACKAGE_PATHS = {
|
|
98
|
+
"@dreamboard-games/sdk": ["packages", "sdk", "package.json"]
|
|
99
|
+
};
|
|
100
|
+
var DEFAULT_SDK_DEPENDENCY_RANGES = {
|
|
101
|
+
"@dreamboard-games/sdk": "0.4.0-alpha.0"
|
|
102
|
+
};
|
|
103
|
+
var SDK_DEPENDENCY_RANGES = {
|
|
104
|
+
"@dreamboard-games/sdk": resolveSdkDependencyRange("@dreamboard-games/sdk")
|
|
105
|
+
};
|
|
106
|
+
var FRAMEWORK_SCRIPTS = {
|
|
107
|
+
dev: "dreamboard dev",
|
|
108
|
+
"test:ui": "tsx --tsconfig test/tsconfig.tsx-runtime.json --test test/ui/**/*.test.tsx",
|
|
109
|
+
typecheck: `tsc --noEmit -p ${MANIFEST_TYPECHECK_CONFIG_FILE} && tsc --noEmit -p app/tsconfig.json && tsc --noEmit -p ui/tsconfig.json`,
|
|
110
|
+
"typecheck:manifest": `tsc --noEmit -p ${MANIFEST_TYPECHECK_CONFIG_FILE}`,
|
|
111
|
+
"typecheck:app": "tsc --noEmit -p app/tsconfig.json",
|
|
112
|
+
"typecheck:ui": "tsc --noEmit -p ui/tsconfig.json"
|
|
113
|
+
};
|
|
114
|
+
var SHARED_DEPENDENCIES = {
|
|
115
|
+
...FRAMEWORK_REACT_DEPENDENCIES
|
|
116
|
+
};
|
|
117
|
+
var ROOT_APP_DEPENDENCIES = {
|
|
118
|
+
zod: FRAMEWORK_ZOD_VERSION
|
|
119
|
+
};
|
|
120
|
+
var SHARED_DEV_DEPENDENCIES = {
|
|
121
|
+
typescript: "^5.9.2",
|
|
122
|
+
"@types/node": "^24.5.2",
|
|
123
|
+
"@types/react": "^19.0.0",
|
|
124
|
+
"@types/react-dom": "^19.0.0",
|
|
125
|
+
csstype: "^3.1.3",
|
|
126
|
+
tsx: "^4.20.5"
|
|
127
|
+
};
|
|
128
|
+
async function scaffoldStaticWorkspace(projectRoot, mode, options = {}) {
|
|
129
|
+
await writeFrameworkStaticFiles(projectRoot, mode, options);
|
|
130
|
+
await ensureDreamboardGitignore(projectRoot);
|
|
131
|
+
await writeManifestTypecheckTsconfig(
|
|
132
|
+
path.join(projectRoot, MANIFEST_TYPECHECK_CONFIG_FILE)
|
|
133
|
+
);
|
|
134
|
+
await removeLegacyVendoredSdkPaths(projectRoot);
|
|
135
|
+
await removeLegacyDreamboardComponentPath(projectRoot);
|
|
136
|
+
const testDir = path.join(projectRoot, "test");
|
|
137
|
+
const basesDir = path.join(testDir, "bases");
|
|
138
|
+
const scenariosDir = path.join(testDir, "scenarios");
|
|
139
|
+
const generatedDir = path.join(testDir, "generated");
|
|
140
|
+
await ensureDir(basesDir);
|
|
141
|
+
await ensureDir(scenariosDir);
|
|
142
|
+
await ensureDir(generatedDir);
|
|
143
|
+
await writeTestReadme(path.join(testDir, "README.md"));
|
|
144
|
+
await writeGeneratedTestingStubs(generatedDir, mode);
|
|
145
|
+
const initialTestPlayerCount = await inferInitialTestPlayerCount(projectRoot);
|
|
146
|
+
await writeInitialBase(
|
|
147
|
+
path.join(basesDir, "initial-turn.base.ts"),
|
|
148
|
+
mode,
|
|
149
|
+
initialTestPlayerCount
|
|
150
|
+
);
|
|
151
|
+
await writeInitialScenario(
|
|
152
|
+
path.join(scenariosDir, "smoke-initial-turn.scenario.ts"),
|
|
153
|
+
mode
|
|
154
|
+
);
|
|
155
|
+
await writeTestingTypes(path.join(testDir, "testing-types.ts"), mode);
|
|
156
|
+
await writeTestTsconfig(path.join(testDir, "tsconfig.json"));
|
|
157
|
+
const staleDtsPath = path.join(testDir, "testing-types.d.ts");
|
|
158
|
+
if (await exists(staleDtsPath)) {
|
|
159
|
+
await unlink(staleDtsPath);
|
|
160
|
+
}
|
|
161
|
+
const staleBaseScenariosPath = path.join(testDir, "base-scenarios.json");
|
|
162
|
+
if (await exists(staleBaseScenariosPath)) {
|
|
163
|
+
await unlink(staleBaseScenariosPath);
|
|
164
|
+
}
|
|
165
|
+
await migrateLegacyScenarioImports(projectRoot);
|
|
166
|
+
}
|
|
167
|
+
async function ensureDreamboardGitignore(projectRoot) {
|
|
168
|
+
const gitignorePath = path.join(projectRoot, ".gitignore");
|
|
169
|
+
const existing = await readTextFileIfExists(gitignorePath);
|
|
170
|
+
if (existing?.includes(".dreamboard/state.json")) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
await writeTextFile(
|
|
174
|
+
gitignorePath,
|
|
175
|
+
`${existing ? `${existing.trimEnd()}
|
|
176
|
+
|
|
177
|
+
` : ""}${DREAMBOARD_GITIGNORE_BLOCK}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
async function assertCliStaticScaffoldComplete(projectRoot, deletedPaths = []) {
|
|
181
|
+
const expectedEntries = await getExpectedStaticEntries(projectRoot);
|
|
182
|
+
const missingOrBlankPaths = [];
|
|
183
|
+
for (const entry of expectedEntries) {
|
|
184
|
+
const fullPath = path.join(projectRoot, entry.targetPath);
|
|
185
|
+
const content = await readTextFileIfExists(fullPath);
|
|
186
|
+
if (content === null || content.trim().length === 0) {
|
|
187
|
+
missingOrBlankPaths.push(entry.targetPath);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const staticPaths = new Set(expectedEntries.map((entry) => entry.targetPath));
|
|
191
|
+
const deletedStaticPaths = deletedPaths.map(normalizeProjectPath).filter((filePath) => staticPaths.has(filePath)).sort();
|
|
192
|
+
if (missingOrBlankPaths.length === 0 && deletedStaticPaths.length === 0) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const problems = [];
|
|
196
|
+
if (missingOrBlankPaths.length > 0) {
|
|
197
|
+
problems.push(
|
|
198
|
+
`missing or blank: ${summarizePaths(missingOrBlankPaths.sort())}`
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
if (deletedStaticPaths.length > 0) {
|
|
202
|
+
problems.push(`deleted: ${summarizePaths(deletedStaticPaths)}`);
|
|
203
|
+
}
|
|
204
|
+
throw new Error(
|
|
205
|
+
`CLI static scaffold is incomplete (${problems.join("; ")}). Run \`${DREAMBOARD_SYNC_COMMAND}\` to refresh the scaffold before compiling.`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
async function writeFrameworkStaticFiles(projectRoot, mode, options) {
|
|
209
|
+
const assetEntries = await getStaticAssetEntries();
|
|
210
|
+
for (const entry of assetEntries) {
|
|
211
|
+
const fullPath = path.join(projectRoot, entry.targetPath);
|
|
212
|
+
if (mode === "update" && isDynamicSeedPath(entry.targetPath)) {
|
|
213
|
+
const existing = await readTextFileIfExists(fullPath);
|
|
214
|
+
if (existing !== null && existing.trim().length > 0) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
await writeTextFile(fullPath, entry.content);
|
|
219
|
+
}
|
|
220
|
+
for (const entry of await getDynamicStaticEntries(
|
|
221
|
+
projectRoot,
|
|
222
|
+
mode,
|
|
223
|
+
options
|
|
224
|
+
)) {
|
|
225
|
+
await writeTextFile(
|
|
226
|
+
path.join(projectRoot, entry.targetPath),
|
|
227
|
+
entry.content
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
if (!options.localMaintainerRegistry) {
|
|
231
|
+
await rm(path.join(projectRoot, ".npmrc"), { force: true });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function removeLegacyVendoredSdkPaths(projectRoot) {
|
|
235
|
+
await rm(path.join(projectRoot, "app", "sdk"), {
|
|
236
|
+
recursive: true,
|
|
237
|
+
force: true
|
|
238
|
+
});
|
|
239
|
+
await rm(path.join(projectRoot, "ui", "sdk"), {
|
|
240
|
+
recursive: true,
|
|
241
|
+
force: true
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
async function removeLegacyDreamboardComponentPath(projectRoot) {
|
|
245
|
+
const legacyDirPath = path.join(
|
|
246
|
+
projectRoot,
|
|
247
|
+
"ui",
|
|
248
|
+
"components",
|
|
249
|
+
"dreamboard"
|
|
250
|
+
);
|
|
251
|
+
const legacyIndexPath = path.join(legacyDirPath, "index.ts");
|
|
252
|
+
const existing = await readTextFileIfExists(legacyIndexPath);
|
|
253
|
+
const removableLegacyContents = /* @__PURE__ */ new Set([
|
|
254
|
+
LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim(),
|
|
255
|
+
OLD_LEGACY_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim(),
|
|
256
|
+
OLD_PUBLIC_DREAMBOARD_COMPONENT_INDEX_CONTENT.trim()
|
|
257
|
+
]);
|
|
258
|
+
if (existing === null || !removableLegacyContents.has(existing.trim())) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
await unlink(legacyIndexPath);
|
|
262
|
+
const remainingEntries = await readdir(legacyDirPath).catch(() => []);
|
|
263
|
+
if (remainingEntries.length === 0) {
|
|
264
|
+
await rmdir(legacyDirPath);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function writeTestReadme(filePath) {
|
|
268
|
+
await writeTextFile(
|
|
269
|
+
filePath,
|
|
270
|
+
"# Dreamboard Test Workspace\n\nTypeScript bases live in `test/bases/*.base.ts` and scenarios live in `test/scenarios/*.scenario.ts`.\n\n1. Define reusable seeded bases with `defineBase({ id, seed, players, setupProfileId?, setup })`.\n2. Define scenarios with `defineScenario({ id, from, when, then })`.\n3. Scenario assertions can read `players()`, `state()`, `view(playerId)`, and `interactions(playerId)`.\n4. Generate deterministic base snapshots: `dreamboard test generate`.\n5. Run tests: `dreamboard test run`.\n\nImport test helpers from `../testing-types`.\n\nGenerated artifacts are written to `test/generated/*` and should not be edited manually.\n"
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
async function writeInitialBase(filePath, mode, players) {
|
|
274
|
+
if (mode === "update") {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
await writeTextFile(
|
|
278
|
+
filePath,
|
|
279
|
+
`import { defineBase } from "../testing-types";
|
|
280
|
+
|
|
281
|
+
export default defineBase({
|
|
282
|
+
id: "initial-turn",
|
|
283
|
+
seed: 1337,
|
|
284
|
+
players: ${players},
|
|
285
|
+
setup: async () => undefined,
|
|
286
|
+
});
|
|
287
|
+
`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
async function writeInitialScenario(filePath, mode) {
|
|
291
|
+
if (mode === "new") {
|
|
292
|
+
await writeTextFile(filePath, INITIAL_SCENARIO_CONTENT);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const existing = await readTextFileIfExists(filePath);
|
|
296
|
+
if (existing === null || existing.trim().length === 0 || existing === INITIAL_SCENARIO_CONTENT) {
|
|
297
|
+
await writeTextFile(filePath, INITIAL_SCENARIO_CONTENT);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async function writeTestingTypes(filePath, mode) {
|
|
301
|
+
if (mode === "new") {
|
|
302
|
+
await writeTextFile(filePath, REDUCER_TESTING_TYPES_WRAPPER_CONTENT);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const existing = await readTextFileIfExists(filePath);
|
|
306
|
+
if (shouldRefreshGeneratedTestingTypes(existing)) {
|
|
307
|
+
await writeTextFile(filePath, REDUCER_TESTING_TYPES_WRAPPER_CONTENT);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async function writeGeneratedTestingStubs(generatedDir, mode) {
|
|
311
|
+
const header = "// Generated by dreamboard scaffold. Do not edit by hand.\n";
|
|
312
|
+
await writeGeneratedTestingStubFile(
|
|
313
|
+
path.join(generatedDir, "base-states.generated.ts"),
|
|
314
|
+
`${header}export const BASE_STATES = {} as const;
|
|
315
|
+
export const BASE_STATES_CONTRACT_FINGERPRINT = undefined;
|
|
316
|
+
`,
|
|
317
|
+
mode
|
|
318
|
+
);
|
|
319
|
+
await writeGeneratedTestingStubFile(
|
|
320
|
+
path.join(generatedDir, "base-states.generated.d.ts"),
|
|
321
|
+
`${header}export declare const BASE_STATES: Record<string, unknown>;
|
|
322
|
+
export declare const BASE_STATES_CONTRACT_FINGERPRINT: string | undefined;
|
|
323
|
+
`,
|
|
324
|
+
mode
|
|
325
|
+
);
|
|
326
|
+
await writeGeneratedTestingStubFile(
|
|
327
|
+
path.join(generatedDir, "testing-contract.ts"),
|
|
328
|
+
`${header}export type BaseId = string;
|
|
329
|
+
export type GameView = unknown;
|
|
330
|
+
export type InteractionId = string;
|
|
331
|
+
export type InteractionParamsOf<_Id extends string> = Record<string, unknown>;
|
|
332
|
+
export type PhaseName = string;
|
|
333
|
+
export type PlayerId = string;
|
|
334
|
+
export type RejectionCode = string;
|
|
335
|
+
export type StateName = string;
|
|
336
|
+
export type TestRunner = "reducer" | "remote" | "browser";
|
|
337
|
+
export type ViewByPhase = Record<string, GameView>;
|
|
338
|
+
export type WorkspaceStageName<_Phase extends string = string> = string;
|
|
339
|
+
export type Expectation = { [matcher: string]: (...args: unknown[]) => unknown; not: Expectation };
|
|
340
|
+
export type ExpectFn = (actual: unknown) => Expectation;
|
|
341
|
+
export type InteractionExplanation = { interactionId: string; phase: string; step: string | null; availability: "available" | "notYourTurn" | "wrongPhase" | "wrongStep" | "blocked"; rules: readonly { ruleId: string; outcome: "passed" | "failed" | "notEvaluated"; errorCode?: string; message?: string; }[]; actor: { required: readonly string[]; playerIsActor: boolean }; inputs: readonly { key: string; kind: string; eligibleCount: number | "lazy"; }[]; };
|
|
342
|
+
export interface InteractionDescriptorFor<Id extends string = string> { interactionId: Id; [key: string]: unknown; }
|
|
343
|
+
export interface ScenarioGameApi { start(): Promise<void>; submit<Id extends InteractionId>(playerId: PlayerId, interactionId: Id, params?: InteractionParamsOf<Id>): Promise<void>; }
|
|
344
|
+
export interface BaseContext { game: ScenarioGameApi; players(): readonly PlayerId[]; seat(index: number): PlayerId; }
|
|
345
|
+
export interface SharedScenarioContext { game: ScenarioGameApi; players(): readonly PlayerId[]; seat(index: number): PlayerId; state(): StateName; view(playerId: PlayerId): GameView; interactions(playerId: PlayerId): readonly InteractionDescriptorFor[]; explain(playerId: PlayerId, interactionId: InteractionId): InteractionExplanation; expect: ExpectFn; }
|
|
346
|
+
export type ScenarioContext<Phase extends PhaseName | undefined = undefined> = Omit<SharedScenarioContext, "state" | "view"> & { state(): Phase extends PhaseName ? Phase : StateName; view(playerId: PlayerId): Phase extends PhaseName ? ViewByPhase[Phase] : GameView; };
|
|
347
|
+
export type ScenarioThenContext<_Runners extends readonly TestRunner[] = readonly ["reducer"], Phase extends PhaseName | undefined = undefined> = ScenarioContext<Phase>;
|
|
348
|
+
export interface BaseDefinition { id: string; seed?: number; players?: number; setupProfileId?: string; extends?: BaseId | string; setup: (ctx: BaseContext) => void | Promise<void>; }
|
|
349
|
+
export interface ScenarioDefinition<Runners extends readonly TestRunner[] = readonly ["reducer"], Phase extends PhaseName | undefined = undefined> { id: string; description?: string; from: BaseId | string; runners?: Runners; phase?: Phase; stage?: Phase extends PhaseName ? WorkspaceStageName<Phase> : never; when: (ctx: ScenarioContext<Phase>) => void | Promise<void>; then: (ctx: ScenarioThenContext<Runners, Phase>) => void | Promise<void>; }
|
|
350
|
+
`,
|
|
351
|
+
mode
|
|
352
|
+
);
|
|
353
|
+
await writeGeneratedTestingStubFile(
|
|
354
|
+
path.join(generatedDir, "scenario-manifest.generated.ts"),
|
|
355
|
+
`${header}export const SCENARIO_MANIFEST = [] as const;
|
|
356
|
+
`,
|
|
357
|
+
mode
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
async function writeGeneratedTestingStubFile(filePath, content, mode) {
|
|
361
|
+
if (mode === "new") {
|
|
362
|
+
await writeTextFile(filePath, content);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const existing = await readTextFileIfExists(filePath);
|
|
366
|
+
if (existing === null || existing.trim().length === 0 || existing.startsWith(GENERATED_SCENARIO_PREFIX)) {
|
|
367
|
+
await writeTextFile(filePath, content);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async function writeTestTsconfig(filePath) {
|
|
371
|
+
await writeTextFile(
|
|
372
|
+
filePath,
|
|
373
|
+
`${JSON.stringify(
|
|
374
|
+
{
|
|
375
|
+
compilerOptions: {
|
|
376
|
+
target: "ES2022",
|
|
377
|
+
module: "ESNext",
|
|
378
|
+
moduleResolution: "bundler",
|
|
379
|
+
strict: true,
|
|
380
|
+
esModuleInterop: true,
|
|
381
|
+
skipLibCheck: true,
|
|
382
|
+
noEmit: true
|
|
383
|
+
},
|
|
384
|
+
include: [
|
|
385
|
+
"./**/*.ts",
|
|
386
|
+
"./**/*.d.ts",
|
|
387
|
+
"../shared/**/*.ts",
|
|
388
|
+
"../shared/**/*.d.ts"
|
|
389
|
+
]
|
|
390
|
+
},
|
|
391
|
+
null,
|
|
392
|
+
2
|
|
393
|
+
)}
|
|
394
|
+
`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
async function writeManifestTypecheckTsconfig(filePath) {
|
|
398
|
+
await writeTextFile(
|
|
399
|
+
filePath,
|
|
400
|
+
`${JSON.stringify(
|
|
401
|
+
{
|
|
402
|
+
compilerOptions: {
|
|
403
|
+
target: "ES2022",
|
|
404
|
+
module: "ESNext",
|
|
405
|
+
moduleResolution: "bundler",
|
|
406
|
+
strict: true,
|
|
407
|
+
esModuleInterop: true,
|
|
408
|
+
skipLibCheck: true,
|
|
409
|
+
noEmit: true,
|
|
410
|
+
allowImportingTsExtensions: true
|
|
411
|
+
},
|
|
412
|
+
include: ["./manifest.ts"]
|
|
413
|
+
},
|
|
414
|
+
null,
|
|
415
|
+
2
|
|
416
|
+
)}
|
|
417
|
+
`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
async function migrateLegacyScenarioImports(projectRoot) {
|
|
421
|
+
const scenariosRoot = path.join(projectRoot, "test", "scenarios");
|
|
422
|
+
if (!await exists(scenariosRoot)) return;
|
|
423
|
+
const scenarioFiles = await collectScenarioFiles(scenariosRoot);
|
|
424
|
+
for (const filePath of scenarioFiles) {
|
|
425
|
+
const content = await readTextFile(filePath);
|
|
426
|
+
if (!content.includes("@dreamboard/cli/testing")) continue;
|
|
427
|
+
const relativeToTestingTypes = normalizeImportPath(
|
|
428
|
+
path.relative(
|
|
429
|
+
path.dirname(filePath),
|
|
430
|
+
path.join(projectRoot, "test", "testing-types")
|
|
431
|
+
)
|
|
432
|
+
);
|
|
433
|
+
const migrated = content.replaceAll('"@dreamboard/cli/testing"', `"${relativeToTestingTypes}"`).replaceAll("'@dreamboard/cli/testing'", `'${relativeToTestingTypes}'`);
|
|
434
|
+
if (migrated !== content) {
|
|
435
|
+
await writeTextFile(filePath, migrated);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function shouldRefreshGeneratedTestingTypes(existingContent) {
|
|
440
|
+
if (existingContent === null || existingContent.trim().length === 0) {
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
if (existingContent === TESTING_TYPES_STUB) {
|
|
444
|
+
return true;
|
|
445
|
+
}
|
|
446
|
+
return existingContent.startsWith(GENERATED_TESTING_TYPES_PREFIX);
|
|
447
|
+
}
|
|
448
|
+
async function inferInitialTestPlayerCount(projectRoot) {
|
|
449
|
+
const manifestPath = path.join(projectRoot, "manifest.ts");
|
|
450
|
+
if (!await exists(manifestPath)) {
|
|
451
|
+
return 4;
|
|
452
|
+
}
|
|
453
|
+
try {
|
|
454
|
+
const manifest = await materializeManifest(projectRoot);
|
|
455
|
+
return manifest.players.optimalPlayers ?? manifest.players.minPlayers ?? 4;
|
|
456
|
+
} catch {
|
|
457
|
+
return 4;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async function collectScenarioFiles(rootDir) {
|
|
461
|
+
const files = [];
|
|
462
|
+
const stack = [rootDir];
|
|
463
|
+
while (stack.length > 0) {
|
|
464
|
+
const dir = stack.pop();
|
|
465
|
+
if (!dir) continue;
|
|
466
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
467
|
+
for (const entry of entries) {
|
|
468
|
+
const fullPath = path.join(dir, entry.name);
|
|
469
|
+
if (entry.isDirectory()) {
|
|
470
|
+
stack.push(fullPath);
|
|
471
|
+
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
472
|
+
files.push(fullPath);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return files;
|
|
477
|
+
}
|
|
478
|
+
function resolveStaticAssetRoot(importUrl = import.meta.url) {
|
|
479
|
+
const candidates = [
|
|
480
|
+
fileURLToPath(new URL("../../scaffold/assets/static/", importUrl)),
|
|
481
|
+
fileURLToPath(new URL("./scaffold/assets/static/", importUrl)),
|
|
482
|
+
fileURLToPath(new URL("../scaffold/assets/static/", importUrl))
|
|
483
|
+
];
|
|
484
|
+
for (const candidate of candidates) {
|
|
485
|
+
if (existsSync(candidate)) {
|
|
486
|
+
return candidate;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
throw new Error(
|
|
490
|
+
`Unable to locate CLI static scaffold assets. Checked: ${candidates.join(", ")}`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
async function getStaticAssetEntries() {
|
|
494
|
+
const files = await walkFiles(STATIC_ASSET_ROOT);
|
|
495
|
+
const entries = [];
|
|
496
|
+
for (const filePath of files) {
|
|
497
|
+
const targetPath = normalizeProjectPath(
|
|
498
|
+
path.relative(STATIC_ASSET_ROOT, filePath)
|
|
499
|
+
);
|
|
500
|
+
entries.push({
|
|
501
|
+
targetPath,
|
|
502
|
+
content: await readFile(filePath, "utf8")
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
entries.sort(
|
|
506
|
+
(left, right) => left.targetPath.localeCompare(right.targetPath)
|
|
507
|
+
);
|
|
508
|
+
return entries;
|
|
509
|
+
}
|
|
510
|
+
async function getDynamicStaticEntries(projectRoot, mode, options = {}) {
|
|
511
|
+
const entries = [
|
|
512
|
+
{
|
|
513
|
+
targetPath: "package.json",
|
|
514
|
+
content: await buildRootPackageJson(projectRoot, mode, options)
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
targetPath: "ui/package.json",
|
|
518
|
+
content: buildUiPackageJson()
|
|
519
|
+
}
|
|
520
|
+
];
|
|
521
|
+
if (options.localMaintainerRegistry) {
|
|
522
|
+
entries.push({
|
|
523
|
+
targetPath: ".npmrc",
|
|
524
|
+
content: buildWorkspaceNpmrc(options.localMaintainerRegistry.registryUrl)
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return entries;
|
|
528
|
+
}
|
|
529
|
+
async function readProjectLocalMaintainerRegistry(projectRoot) {
|
|
530
|
+
const projectConfigPath = path.join(
|
|
531
|
+
projectRoot,
|
|
532
|
+
PROJECT_DIR_NAME,
|
|
533
|
+
PROJECT_CONFIG_FILE
|
|
534
|
+
);
|
|
535
|
+
if (!await exists(projectConfigPath)) {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
const projectConfig = await loadProjectConfig(projectRoot);
|
|
539
|
+
return projectConfig.localMaintainerRegistry ?? null;
|
|
540
|
+
}
|
|
541
|
+
async function getExpectedStaticEntries(projectRoot) {
|
|
542
|
+
const entries = [
|
|
543
|
+
...await getStaticAssetEntries(),
|
|
544
|
+
...await getDynamicStaticEntries(projectRoot, "update", {
|
|
545
|
+
localMaintainerRegistry: await readProjectLocalMaintainerRegistry(projectRoot)
|
|
546
|
+
})
|
|
547
|
+
];
|
|
548
|
+
entries.sort(
|
|
549
|
+
(left, right) => left.targetPath.localeCompare(right.targetPath)
|
|
550
|
+
);
|
|
551
|
+
return entries;
|
|
552
|
+
}
|
|
553
|
+
async function walkFiles(rootDir) {
|
|
554
|
+
const files = [];
|
|
555
|
+
const stack = [rootDir];
|
|
556
|
+
while (stack.length > 0) {
|
|
557
|
+
const currentDir = stack.pop();
|
|
558
|
+
if (!currentDir) continue;
|
|
559
|
+
const entries = await readdir(currentDir, { withFileTypes: true });
|
|
560
|
+
for (const entry of entries) {
|
|
561
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
562
|
+
if (entry.isDirectory()) {
|
|
563
|
+
stack.push(fullPath);
|
|
564
|
+
} else if (entry.isFile()) {
|
|
565
|
+
files.push(fullPath);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
files.sort((left, right) => left.localeCompare(right));
|
|
570
|
+
return files;
|
|
571
|
+
}
|
|
572
|
+
async function buildRootPackageJson(projectRoot, mode, options) {
|
|
573
|
+
const sdkPackageRanges = {
|
|
574
|
+
...SDK_DEPENDENCY_RANGES,
|
|
575
|
+
...options.localMaintainerRegistry?.packages ?? {}
|
|
576
|
+
};
|
|
577
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
578
|
+
const existingPackageJson = mode === "update" && await exists(packageJsonPath) ? await readJsonFile(packageJsonPath) : null;
|
|
579
|
+
const {
|
|
580
|
+
dreamboardFrameworkVersion: _legacyFrameworkVersion,
|
|
581
|
+
...existingPackageJsonWithoutLegacyVersion
|
|
582
|
+
} = existingPackageJson ?? {};
|
|
583
|
+
const frameworkDependencies = {
|
|
584
|
+
"@dreamboard-games/sdk": sdkPackageRanges["@dreamboard-games/sdk"] ?? SDK_DEPENDENCY_RANGES["@dreamboard-games/sdk"],
|
|
585
|
+
...SHARED_DEPENDENCIES,
|
|
586
|
+
...ROOT_APP_DEPENDENCIES
|
|
587
|
+
};
|
|
588
|
+
const frameworkDevDependencies = {
|
|
589
|
+
...SHARED_DEV_DEPENDENCIES
|
|
590
|
+
};
|
|
591
|
+
const nextPackageJson = {
|
|
592
|
+
...existingPackageJsonWithoutLegacyVersion,
|
|
593
|
+
private: true,
|
|
594
|
+
scripts: {
|
|
595
|
+
...existingPackageJson?.scripts ?? {},
|
|
596
|
+
...FRAMEWORK_SCRIPTS
|
|
597
|
+
},
|
|
598
|
+
dependencies: {
|
|
599
|
+
...existingPackageJson?.dependencies ?? {},
|
|
600
|
+
...frameworkDependencies
|
|
601
|
+
},
|
|
602
|
+
devDependencies: {
|
|
603
|
+
...existingPackageJson?.devDependencies ?? {},
|
|
604
|
+
...frameworkDevDependencies
|
|
605
|
+
},
|
|
606
|
+
pnpm: mergePnpmConfig(existingPackageJson?.pnpm)
|
|
607
|
+
};
|
|
608
|
+
return `${JSON.stringify(nextPackageJson, null, 2)}
|
|
609
|
+
`;
|
|
610
|
+
}
|
|
611
|
+
function mergePnpmConfig(existingPnpm) {
|
|
612
|
+
const existingOverrides = existingPnpm?.overrides && typeof existingPnpm.overrides === "object" && !Array.isArray(existingPnpm.overrides) ? existingPnpm.overrides : {};
|
|
613
|
+
return {
|
|
614
|
+
...existingPnpm ?? {},
|
|
615
|
+
overrides: {
|
|
616
|
+
...existingOverrides,
|
|
617
|
+
...FRAMEWORK_PNPM_OVERRIDES
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
function buildWorkspaceNpmrc(registryUrl) {
|
|
622
|
+
return `@dreamboard-games:registry=${registryUrl}
|
|
623
|
+
`;
|
|
624
|
+
}
|
|
625
|
+
function buildUiPackageJson() {
|
|
626
|
+
return `${JSON.stringify(
|
|
627
|
+
{
|
|
628
|
+
private: true,
|
|
629
|
+
dependencies: SHARED_DEPENDENCIES,
|
|
630
|
+
devDependencies: SHARED_DEV_DEPENDENCIES
|
|
631
|
+
},
|
|
632
|
+
null,
|
|
633
|
+
2
|
|
634
|
+
)}
|
|
635
|
+
`;
|
|
636
|
+
}
|
|
637
|
+
function readPackageVersion(packageJsonPath, packageName) {
|
|
638
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
639
|
+
if (typeof packageJson.version !== "string" || packageJson.version.trim().length === 0) {
|
|
640
|
+
return null;
|
|
641
|
+
}
|
|
642
|
+
return `^${packageJson.version.trim()}`;
|
|
643
|
+
}
|
|
644
|
+
function findNearestPackageJsonPath(importMetaUrl = import.meta.url) {
|
|
645
|
+
let current = path.dirname(fileURLToPath(importMetaUrl));
|
|
646
|
+
while (true) {
|
|
647
|
+
const candidate = path.join(current, "package.json");
|
|
648
|
+
if (existsSync(candidate)) {
|
|
649
|
+
return candidate;
|
|
650
|
+
}
|
|
651
|
+
const parent = path.dirname(current);
|
|
652
|
+
if (parent === current) {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
current = parent;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
function isSourceCheckoutCliPackageJsonPath(packageJsonPath, importMetaUrl = import.meta.url) {
|
|
659
|
+
try {
|
|
660
|
+
return path.resolve(packageJsonPath) === path.join(
|
|
661
|
+
resolveCliRepoRoot(importMetaUrl),
|
|
662
|
+
"apps",
|
|
663
|
+
"dreamboard-cli",
|
|
664
|
+
"package.json"
|
|
665
|
+
);
|
|
666
|
+
} catch {
|
|
667
|
+
return false;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
function readPackagedSdkDependencyRange(packageName, importMetaUrl = import.meta.url) {
|
|
671
|
+
const packageJsonPath = findNearestPackageJsonPath(importMetaUrl);
|
|
672
|
+
if (!packageJsonPath) {
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
676
|
+
const packagedRange = packageJson.dependencies?.[packageName];
|
|
677
|
+
if (typeof packagedRange !== "string" || packagedRange.trim().length === 0 || packagedRange.startsWith("workspace:") || packagedRange.startsWith("link:") || packagedRange.startsWith("file:") && !IS_PUBLISHED_BUILD && isSourceCheckoutCliPackageJsonPath(packageJsonPath, importMetaUrl)) {
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
return packagedRange.trim();
|
|
681
|
+
}
|
|
682
|
+
function stripLocalSnapshotSuffix(range) {
|
|
683
|
+
return range.replace(/-local\..*$/, "");
|
|
684
|
+
}
|
|
685
|
+
function resolveSdkDependencyRange(packageName, importMetaUrl = import.meta.url) {
|
|
686
|
+
const packagedRange = readPackagedSdkDependencyRange(
|
|
687
|
+
packageName,
|
|
688
|
+
importMetaUrl
|
|
689
|
+
);
|
|
690
|
+
if (packagedRange) {
|
|
691
|
+
return stripLocalSnapshotSuffix(packagedRange);
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
const repoRoot = resolveCliRepoRoot(importMetaUrl);
|
|
695
|
+
const packageJsonPath = path.join(
|
|
696
|
+
repoRoot,
|
|
697
|
+
...SDK_PACKAGE_PATHS[packageName]
|
|
698
|
+
);
|
|
699
|
+
const repoRange = readPackageVersion(packageJsonPath, packageName);
|
|
700
|
+
if (repoRange) {
|
|
701
|
+
return stripLocalSnapshotSuffix(repoRange);
|
|
702
|
+
}
|
|
703
|
+
} catch {
|
|
704
|
+
}
|
|
705
|
+
return DEFAULT_SDK_DEPENDENCY_RANGES[packageName];
|
|
706
|
+
}
|
|
707
|
+
function normalizeImportPath(relativePath) {
|
|
708
|
+
const normalized = relativePath.replaceAll("\\", "/");
|
|
709
|
+
if (normalized.startsWith(".")) return normalized;
|
|
710
|
+
return `./${normalized}`;
|
|
711
|
+
}
|
|
712
|
+
function normalizeProjectPath(filePath) {
|
|
713
|
+
return filePath.replace(/^\.\//, "").replace(/^\/+/, "").replace(/\\/g, "/");
|
|
714
|
+
}
|
|
715
|
+
function summarizePaths(paths) {
|
|
716
|
+
const maxShown = 5;
|
|
717
|
+
const shown = paths.slice(0, maxShown).join(", ");
|
|
718
|
+
if (paths.length <= maxShown) return shown;
|
|
719
|
+
return `${shown}, and ${paths.length - maxShown} more`;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
export {
|
|
723
|
+
scaffoldStaticWorkspace,
|
|
724
|
+
assertCliStaticScaffoldComplete,
|
|
725
|
+
migrateLegacyScenarioImports,
|
|
726
|
+
resolveStaticAssetRoot,
|
|
727
|
+
resolveSdkDependencyRange
|
|
728
|
+
};
|
|
729
|
+
//# sourceMappingURL=chunk-MW2QIWWA.mjs.map
|