@bitmagic/cli 0.1.60-dev.0 → 0.1.60-dev.2

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.
Files changed (68) hide show
  1. package/README.md +60 -4
  2. package/dist/cli.d.ts +36 -0
  3. package/dist/cli.js +2 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/forge.js +15 -25
  6. package/dist/commands/forge.js.map +1 -1
  7. package/dist/commands/generate.d.ts +2 -0
  8. package/dist/commands/generate.js +28 -1
  9. package/dist/commands/generate.js.map +1 -1
  10. package/dist/commands/import.d.ts +75 -0
  11. package/dist/commands/import.js +338 -0
  12. package/dist/commands/import.js.map +1 -0
  13. package/dist/commands/init.js +1 -45
  14. package/dist/commands/init.js.map +1 -1
  15. package/dist/commands/verify.js +17 -29
  16. package/dist/commands/verify.js.map +1 -1
  17. package/dist/editor/animation-preview-page.js +1 -1
  18. package/dist/editor/motion-panel.d.ts +3 -3
  19. package/dist/editor/motion-panel.js +272 -2
  20. package/dist/editor/motion-panel.js.map +1 -1
  21. package/dist/editor/motion-sink.d.ts +10 -2
  22. package/dist/editor/motion-sink.js +27 -2
  23. package/dist/editor/motion-sink.js.map +1 -1
  24. package/dist/editor/server.js +100 -1
  25. package/dist/editor/server.js.map +1 -1
  26. package/dist/generate/animation-from-video.d.ts +12 -0
  27. package/dist/generate/animation-from-video.js +15 -6
  28. package/dist/generate/animation-from-video.js.map +1 -1
  29. package/dist/generate/trim-analysis.d.ts +22 -0
  30. package/dist/generate/trim-analysis.js +31 -0
  31. package/dist/generate/trim-analysis.js.map +1 -0
  32. package/dist/local-port.d.ts +23 -4
  33. package/dist/local-port.js +44 -13
  34. package/dist/local-port.js.map +1 -1
  35. package/dist/scaffold/export-zip.d.ts +48 -0
  36. package/dist/scaffold/export-zip.js +167 -0
  37. package/dist/scaffold/export-zip.js.map +1 -0
  38. package/dist/scaffold/import-project.d.ts +34 -0
  39. package/dist/scaffold/import-project.js +108 -0
  40. package/dist/scaffold/import-project.js.map +1 -0
  41. package/dist/scaffold/project-files.js +9 -1
  42. package/dist/scaffold/project-files.js.map +1 -1
  43. package/dist/scaffold/project.d.ts +34 -0
  44. package/dist/scaffold/project.js +53 -22
  45. package/dist/scaffold/project.js.map +1 -1
  46. package/dist/scaffold/rehost-assets.d.ts +63 -0
  47. package/dist/scaffold/rehost-assets.js +270 -0
  48. package/dist/scaffold/rehost-assets.js.map +1 -0
  49. package/dist/scaffold/scaffold-run.d.ts +17 -0
  50. package/dist/scaffold/scaffold-run.js +57 -0
  51. package/dist/scaffold/scaffold-run.js.map +1 -0
  52. package/dist/theme/hud-theme-catalog.d.ts +0 -1
  53. package/dist/theme/hud-theme-catalog.js +9 -6
  54. package/dist/theme/hud-theme-catalog.js.map +1 -1
  55. package/dist/theme/theme-io.js +2 -1
  56. package/dist/theme/theme-io.js.map +1 -1
  57. package/dist/verify/browser.js +87 -18
  58. package/dist/verify/browser.js.map +1 -1
  59. package/dist/verify/classify.d.ts +12 -0
  60. package/dist/verify/classify.js +24 -3
  61. package/dist/verify/classify.js.map +1 -1
  62. package/dist/verify/start.d.ts +23 -3
  63. package/dist/verify/start.js +13 -1
  64. package/dist/verify/start.js.map +1 -1
  65. package/dist/verify/wait-for-server.d.ts +11 -0
  66. package/dist/verify/wait-for-server.js +29 -1
  67. package/dist/verify/wait-for-server.js.map +1 -1
  68. package/package.json +7 -5
@@ -209,8 +209,16 @@ function copyContents(source, dest) {
209
209
  fs.cpSync(path.join(source, entry), path.join(dest, entry), { recursive: true });
210
210
  }
211
211
  }
212
- export function scaffoldProject(options) {
213
- const { targetDir, extractedEngineDir, templatesDir, template, metadata, projectName, idea } = options;
212
+ /**
213
+ * Puts the platform's own code into the project: the vendored `engine/` tree and the service
214
+ * worker that has to sit beside it at the root.
215
+ *
216
+ * Split out of `scaffoldProject` so `importProject` gets the identical tree — including the
217
+ * "missing is a corrupt tarball" / "missing is just an older tarball" distinction the two
218
+ * constant lists above spend most of their comments on. A second copy of this loop would be a
219
+ * second place for that distinction to be got wrong.
220
+ */
221
+ export function vendorEngine(extractedEngineDir, targetDir) {
214
222
  const engineDir = path.join(targetDir, 'engine');
215
223
  fs.mkdirSync(engineDir, { recursive: true });
216
224
  for (const dir of VENDORED_DIRS) {
@@ -221,26 +229,18 @@ export function scaffoldProject(options) {
221
229
  fs.cpSync(source, path.join(engineDir, dir), { recursive: true });
222
230
  }
223
231
  copyOptionalEngineEntries(extractedEngineDir, engineDir);
224
- // The game: the genre baseline, then the template overlaid on top. Contents are copied, not
225
- // the directory itself — a nested src/work/<genre>/ would break every relative import in
226
- // Game.ts. This is a second copy of the same baseline vendored above as engine/genres/: that
227
- // copy feeds the engine's debug tooling, this one seeds the creator's game.
228
- //
229
- // Derived from the 'work' alias rather than restated as a literal, so this can't drift from
230
- // aliases.ts's sourceDir — which must stay 'src/work' because worldDataLoader.ts fetches that
231
- // literal path (see the comment there). copyContents creates parent directories, so the
232
- // intermediate src/ appears for free.
233
- const srcDir = path.join(targetDir, aliasSourceDir('work'));
234
- const genreDir = path.join(extractedEngineDir, 'genres', template.genre);
235
- if (!fs.existsSync(genreDir)) {
236
- throw new CliError(`The engine bundle has no baseline for genre "${template.genre}".`);
237
- }
238
- copyContents(genreDir, srcDir);
239
- const overlayDir = path.join(templatesDir, template.id, 'source');
240
- if (fs.existsSync(overlayDir)) {
241
- copyContents(overlayDir, srcDir);
242
- }
243
232
  copyServiceWorker(extractedEngineDir, targetDir);
233
+ }
234
+ /**
235
+ * Writes every generated file a Bitmagic project has — the platform-owned ones, and the
236
+ * creator-owned ones seeded once.
237
+ *
238
+ * Shared by `scaffoldProject` and `importProject` so the two lanes cannot drift on what a project
239
+ * contains. What belongs in PLATFORM_GENERATED_FILES, and what deliberately stays out of it, is
240
+ * argued on that constant.
241
+ */
242
+ export function writeProjectFiles(options) {
243
+ const { targetDir, metadata, projectName, idea, gameDesign = 'render' } = options;
244
244
  // Stamped as it is written, so a later `bitmagic upgrade` can tell whether the creator has
245
245
  // edited it since — see scaffold/agents-md.ts for what that decides. The pin goes in because the
246
246
  // file tells the agent which npm line this project's CLI comes from, and that is the environment's
@@ -253,7 +253,9 @@ export function scaffoldProject(options) {
253
253
  ['AGENTS.md', agentsMd],
254
254
  // Creator-owned from here on (see PLATFORM_GENERATED_FILES): the agent rewrites it as the
255
255
  // game takes shape, and `bitmagic cover` reads whatever it says at the time.
256
- ['GAME-DESIGN.md', renderGameDesignMd(idea)],
256
+ ...(gameDesign === 'render'
257
+ ? [['GAME-DESIGN.md', renderGameDesignMd(idea)]]
258
+ : []),
257
259
  ['CLAUDE.md', renderClaudeMd()],
258
260
  ['.gitignore', renderGitignore()],
259
261
  // Also creator-owned, and merged rather than rewritten by `upgrade` for that reason — this is
@@ -265,4 +267,33 @@ export function scaffoldProject(options) {
265
267
  writeProjectFile(targetDir, name, contents);
266
268
  }
267
269
  }
270
+ export function scaffoldProject(options) {
271
+ const { targetDir, extractedEngineDir, templatesDir, template, metadata, projectName, idea } = options;
272
+ vendorEngine(extractedEngineDir, targetDir);
273
+ // The game: the genre baseline, then the template overlaid on top. Contents are copied, not
274
+ // the directory itself — a nested src/work/<genre>/ would break every relative import in
275
+ // Game.ts. This is a second copy of the same baseline vendored above as engine/genres/: that
276
+ // copy feeds the engine's debug tooling, this one seeds the creator's game.
277
+ //
278
+ // Derived from the 'work' alias rather than restated as a literal, so this can't drift from
279
+ // aliases.ts's sourceDir — which must stay 'src/work' because worldDataLoader.ts fetches that
280
+ // literal path (see the comment there). copyContents creates parent directories, so the
281
+ // intermediate src/ appears for free.
282
+ const srcDir = path.join(targetDir, aliasSourceDir('work'));
283
+ const genreDir = path.join(extractedEngineDir, 'genres', template.genre);
284
+ if (!fs.existsSync(genreDir)) {
285
+ throw new CliError(`The engine bundle has no baseline for genre "${template.genre}".`);
286
+ }
287
+ copyContents(genreDir, srcDir);
288
+ const overlayDir = path.join(templatesDir, template.id, 'source');
289
+ if (fs.existsSync(overlayDir)) {
290
+ copyContents(overlayDir, srcDir);
291
+ }
292
+ writeProjectFiles({
293
+ targetDir,
294
+ metadata,
295
+ projectName,
296
+ ...(idea !== undefined ? { idea } : {}),
297
+ });
298
+ }
268
299
  //# sourceMappingURL=project.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/scaffold/project.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/scaffold/project.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ import type { ApiDeps } from '../http/client.js';
2
+ import type { Environment } from '../config/environments.js';
3
+ export interface RehostResult {
4
+ /** URLs successfully copied onto the target environment. */
5
+ rehosted: number;
6
+ /** URLs left pointing at the foreign bucket, with the reason each one failed. */
7
+ failed: Array<{
8
+ url: string;
9
+ reason: string;
10
+ }>;
11
+ /** Bytes actually stored on the target. */
12
+ storedBytes: number;
13
+ /** Project files whose contents changed. */
14
+ filesRewritten: string[];
15
+ }
16
+ /** Reports progress as `completed` of `total` assets finish, in either direction. */
17
+ export type RehostProgress = (completed: number, total: number) => void;
18
+ /**
19
+ * The group an environment's assets belong on.
20
+ *
21
+ * `local` maps to dev because a local api-server signs uploads into the dev bucket — its games'
22
+ * assets are dev assets, and treating localhost as its own group would copy the whole dev bucket
23
+ * into itself on every local import.
24
+ */
25
+ export declare function environmentGroup(environment: Environment): 'prod' | 'dev';
26
+ /**
27
+ * The content type to upload with: a specific `Content-Type` from the source response, else the
28
+ * extension's. Generic octet-stream headers are ignored so the extension wins — a `.vxl` served
29
+ * as `application/octet-stream` and a `.png` served the same way are not the same file, and
30
+ * api-server chooses whether to gzip from this value.
31
+ */
32
+ export declare function inferContentType(url: string, headerValue: string | null): string;
33
+ /**
34
+ * A bare, storage-safe basename for the copied object. api-server refuses a path separator
35
+ * outright and prefixes its own unique component, so this only has to be recognisable — two
36
+ * assets that reduce to the same name do not collide.
37
+ */
38
+ export declare function safeBasename(url: string): string;
39
+ export interface RehostOptions {
40
+ /** The imported project's root. */
41
+ root: string;
42
+ /** The environment the project belongs to — the destination for every foreign asset. */
43
+ environment: Environment;
44
+ token: string;
45
+ /** The game the copies are stored under. Must be owned by the caller on `environment`. */
46
+ gameId: string;
47
+ deps: ApiDeps;
48
+ onProgress?: RehostProgress;
49
+ }
50
+ /**
51
+ * Which of the project's asset URLs live on an environment other than this project's.
52
+ *
53
+ * Exported for the command, which reports the count before spending several minutes on the copy —
54
+ * and for the tests, which is where the "a foreign host, not merely a remote one" rule is pinned.
55
+ */
56
+ export declare function foreignAssetUrls(root: string, environment: Environment): string[];
57
+ /**
58
+ * Copy every foreign asset onto `environment` and rewrite the project's references to it.
59
+ *
60
+ * Returns without touching a file when there is nothing foreign to move, which is the ordinary
61
+ * case for an import that stayed on one environment.
62
+ */
63
+ export declare function rehostForeignAssets(options: RehostOptions): Promise<RehostResult>;
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Copying an imported game's assets onto the environment the project now belongs to.
3
+ *
4
+ * A Creator game references its assets by absolute URL on whichever api-server built it — prod
5
+ * assets on `*.bitmagic.ai`, dev and sandbox assets on `*.bitmagic.cloud`. A project imported into
6
+ * the OTHER environment inherits those URLs, and inheriting them is not survivable: the origin
7
+ * bucket's CORS policy does not allow the importing environment's origin, so the game loads its
8
+ * own assets in development (where the CLI serves from localhost, which both buckets allow) and
9
+ * then fails for every player once published. It also leaves a prod game depending on the dev
10
+ * cluster's bucket for as long as it exists.
11
+ *
12
+ * So the bytes move. Each foreign URL is downloaded and re-uploaded through the CLI's own signed
13
+ * -upload lane, which forces the object under `worlds/v3/<gameId>/` on the target environment and
14
+ * checks ownership before it mints anything; then every reference to the old URL is rewritten.
15
+ *
16
+ * This is the CLI-lane counterpart of game-play-agent's `rehost-imported-assets.ts`, and shares its
17
+ * rule deliberately: a "managed host suffix" is one environment group (`bitmagic.ai` = prod,
18
+ * `bitmagic.cloud` = dev/sandbox), only assets from a group other than the target's are copied, and
19
+ * everything else — a creator's own endpoint, the ImageKit CDN the default skybox is served from —
20
+ * is left exactly where it is. Two lanes doing this differently would put half a game's assets in
21
+ * one place and half in another.
22
+ *
23
+ * **Failures degrade rather than abort.** A URL that cannot be fetched or uploaded keeps its
24
+ * original value, so the game still loads from the foreign bucket instead of losing the asset
25
+ * entirely — and the caller is handed the count and the list so it can say so out loud. Aborting
26
+ * after a hundred successful copies would leave a project split across two buckets with no record
27
+ * of which half moved.
28
+ */
29
+ import * as fs from 'fs';
30
+ import * as path from 'path';
31
+ import { uploadProjectFile } from '../http/signed-upload.js';
32
+ import { aliasSourceDir } from './aliases.js';
33
+ /**
34
+ * Host suffixes that identify a bitmagic-controlled asset host, each standing for one environment
35
+ * group. Kept identical to `IMPORT_REHOST_HOSTS`'s default in game-play-agent — see the file
36
+ * header for why the two lanes must agree.
37
+ */
38
+ const MANAGED_HOST_SUFFIXES = ['bitmagic.ai', 'bitmagic.cloud'];
39
+ /** The environment group each managed suffix belongs to. */
40
+ const GROUP_OF = {
41
+ 'bitmagic.ai': 'prod',
42
+ 'bitmagic.cloud': 'dev',
43
+ };
44
+ /** The project files that may carry asset URLs, relative to `src/work/`. */
45
+ const SOURCE_FILES = ['game.json', 'world.json', 'level.json'];
46
+ /** How many assets are downloaded and re-uploaded at once. */
47
+ const POOL_SIZE = 4;
48
+ const CONTENT_TYPE_BY_EXT = {
49
+ glb: 'model/gltf-binary',
50
+ gltf: 'model/gltf-binary',
51
+ vxl: 'application/octet-stream',
52
+ vwld: 'application/octet-stream',
53
+ bin: 'application/octet-stream',
54
+ png: 'image/png',
55
+ jpg: 'image/jpeg',
56
+ jpeg: 'image/jpeg',
57
+ webp: 'image/webp',
58
+ json: 'application/json',
59
+ mp3: 'audio/mpeg',
60
+ m4a: 'audio/mp4',
61
+ ogg: 'audio/ogg',
62
+ opus: 'audio/ogg',
63
+ wav: 'audio/wav',
64
+ };
65
+ /** The environment group `host` belongs to, or null when it is not a managed host. */
66
+ function groupOf(host) {
67
+ const lower = host.toLowerCase();
68
+ let best = null;
69
+ for (const suffix of MANAGED_HOST_SUFFIXES) {
70
+ if (lower === suffix || lower.endsWith(`.${suffix}`)) {
71
+ // Longest match wins, so an added suffix that is a subdomain of another cannot be shadowed
72
+ // by the more general one purely through declaration order.
73
+ if (best === null || suffix.length > best.length)
74
+ best = suffix;
75
+ }
76
+ }
77
+ return best === null ? null : (GROUP_OF[best] ?? null);
78
+ }
79
+ /**
80
+ * The group an environment's assets belong on.
81
+ *
82
+ * `local` maps to dev because a local api-server signs uploads into the dev bucket — its games'
83
+ * assets are dev assets, and treating localhost as its own group would copy the whole dev bucket
84
+ * into itself on every local import.
85
+ */
86
+ export function environmentGroup(environment) {
87
+ return environment.name === 'prod' ? 'prod' : 'dev';
88
+ }
89
+ /** `value`'s hostname when it is an http(s) URL, else null. */
90
+ function urlHost(value) {
91
+ try {
92
+ const parsed = new URL(value);
93
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
94
+ return null;
95
+ return parsed.hostname;
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
101
+ /**
102
+ * The content type to upload with: a specific `Content-Type` from the source response, else the
103
+ * extension's. Generic octet-stream headers are ignored so the extension wins — a `.vxl` served
104
+ * as `application/octet-stream` and a `.png` served the same way are not the same file, and
105
+ * api-server chooses whether to gzip from this value.
106
+ */
107
+ export function inferContentType(url, headerValue) {
108
+ if (headerValue !== null && headerValue !== '') {
109
+ const type = (headerValue.split(';')[0] ?? '').trim().toLowerCase();
110
+ if (type !== '' && type !== 'application/octet-stream' && type !== 'binary/octet-stream') {
111
+ return type;
112
+ }
113
+ }
114
+ const withoutQuery = (url.split('?')[0] ?? '');
115
+ const ext = (withoutQuery.split('.').pop() ?? '').toLowerCase();
116
+ return CONTENT_TYPE_BY_EXT[ext] ?? 'application/octet-stream';
117
+ }
118
+ /**
119
+ * A bare, storage-safe basename for the copied object. api-server refuses a path separator
120
+ * outright and prefixes its own unique component, so this only has to be recognisable — two
121
+ * assets that reduce to the same name do not collide.
122
+ */
123
+ export function safeBasename(url) {
124
+ let pathname;
125
+ try {
126
+ pathname = new URL(url).pathname;
127
+ }
128
+ catch {
129
+ pathname = url;
130
+ }
131
+ const base = pathname.split('/').filter(Boolean).pop() ?? 'asset';
132
+ const cleaned = base.replace(/[^\w\-.]/g, '_');
133
+ return cleaned === '' ? 'asset' : cleaned;
134
+ }
135
+ /** Every string anywhere in a parsed JSON value. */
136
+ function collectStrings(value, out) {
137
+ if (typeof value === 'string') {
138
+ out.add(value);
139
+ }
140
+ else if (Array.isArray(value)) {
141
+ for (const item of value)
142
+ collectStrings(item, out);
143
+ }
144
+ else if (value !== null && typeof value === 'object') {
145
+ for (const item of Object.values(value))
146
+ collectStrings(item, out);
147
+ }
148
+ }
149
+ /** A copy of `value` with every string replaced by `map`'s entry for it, where there is one. */
150
+ function rewriteStrings(value, map) {
151
+ if (typeof value === 'string')
152
+ return map.get(value) ?? value;
153
+ if (Array.isArray(value))
154
+ return value.map((item) => rewriteStrings(item, map));
155
+ if (value !== null && typeof value === 'object') {
156
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, rewriteStrings(item, map)]));
157
+ }
158
+ return value;
159
+ }
160
+ /** Run `worker` over `items` at bounded concurrency. */
161
+ async function pooled(items, size, worker) {
162
+ let cursor = 0;
163
+ const runners = Array.from({ length: Math.min(size, items.length) }, async () => {
164
+ for (;;) {
165
+ const index = cursor++;
166
+ const item = items[index];
167
+ if (item === undefined)
168
+ return;
169
+ await worker(item);
170
+ }
171
+ });
172
+ await Promise.all(runners);
173
+ }
174
+ /**
175
+ * Which of the project's asset URLs live on an environment other than this project's.
176
+ *
177
+ * Exported for the command, which reports the count before spending several minutes on the copy —
178
+ * and for the tests, which is where the "a foreign host, not merely a remote one" rule is pinned.
179
+ */
180
+ export function foreignAssetUrls(root, environment) {
181
+ const target = environmentGroup(environment);
182
+ const workDir = path.join(root, aliasSourceDir('work'));
183
+ const urls = new Set();
184
+ for (const name of SOURCE_FILES) {
185
+ const filePath = path.join(workDir, name);
186
+ if (!fs.existsSync(filePath))
187
+ continue;
188
+ let parsed;
189
+ try {
190
+ parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
191
+ }
192
+ catch {
193
+ // A file that is not JSON carries no rewritable references. It is the engine's problem to
194
+ // report, not this pass's to fail on.
195
+ continue;
196
+ }
197
+ const strings = new Set();
198
+ collectStrings(parsed, strings);
199
+ for (const value of strings) {
200
+ const host = urlHost(value);
201
+ if (host === null)
202
+ continue;
203
+ const group = groupOf(host);
204
+ if (group !== null && group !== target)
205
+ urls.add(value);
206
+ }
207
+ }
208
+ return [...urls].sort();
209
+ }
210
+ /**
211
+ * Copy every foreign asset onto `environment` and rewrite the project's references to it.
212
+ *
213
+ * Returns without touching a file when there is nothing foreign to move, which is the ordinary
214
+ * case for an import that stayed on one environment.
215
+ */
216
+ export async function rehostForeignAssets(options) {
217
+ const { root, environment, token, gameId, deps, onProgress } = options;
218
+ const result = { rehosted: 0, failed: [], storedBytes: 0, filesRewritten: [] };
219
+ const urls = foreignAssetUrls(root, environment);
220
+ if (urls.length === 0)
221
+ return result;
222
+ const rewrites = new Map();
223
+ let completed = 0;
224
+ onProgress?.(0, urls.length);
225
+ await pooled(urls, POOL_SIZE, async (url) => {
226
+ try {
227
+ const response = await deps.fetch(url);
228
+ if (!response.ok) {
229
+ result.failed.push({ url, reason: `HTTP ${response.status}` });
230
+ return;
231
+ }
232
+ const bytes = new Uint8Array(await response.arrayBuffer());
233
+ const contentType = inferContentType(url, response.headers.get('content-type'));
234
+ const uploaded = await uploadProjectFile(environment, token, { gameId, filename: safeBasename(url), contentType, bytes }, deps);
235
+ rewrites.set(url, uploaded.url);
236
+ result.rehosted++;
237
+ result.storedBytes += uploaded.storedBytes;
238
+ }
239
+ catch (error) {
240
+ result.failed.push({ url, reason: error instanceof Error ? error.message : String(error) });
241
+ }
242
+ finally {
243
+ onProgress?.(++completed, urls.length);
244
+ }
245
+ });
246
+ if (rewrites.size === 0)
247
+ return result;
248
+ const workDir = path.join(root, aliasSourceDir('work'));
249
+ for (const name of SOURCE_FILES) {
250
+ const filePath = path.join(workDir, name);
251
+ if (!fs.existsSync(filePath))
252
+ continue;
253
+ let parsed;
254
+ try {
255
+ parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
256
+ }
257
+ catch {
258
+ continue;
259
+ }
260
+ const rewritten = rewriteStrings(parsed, rewrites);
261
+ const before = JSON.stringify(parsed);
262
+ const after = JSON.stringify(rewritten);
263
+ if (before === after)
264
+ continue;
265
+ fs.writeFileSync(filePath, `${JSON.stringify(rewritten, null, 2)}\n`);
266
+ result.filesRewritten.push(name);
267
+ }
268
+ return result;
269
+ }
270
+ //# sourceMappingURL=rehost-assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehost-assets.js","sourceRoot":"","sources":["../../src/scaffold/rehost-assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Output } from '../output.js';
2
+ import { type PackageManager } from '../project/package-manager.js';
3
+ /** Throws a CliError naming the directory if it exists, is non-empty, and `force` was not given. */
4
+ export declare function assertScaffoldable(dir: string, force: boolean): void;
5
+ /**
6
+ * Installing dependencies is the least reliable step: network flakiness or a broken local
7
+ * package-manager config are common and have nothing to do with whether the scaffold itself is
8
+ * correct. Its failure must not be reported as a scaffold failure — the project on disk is
9
+ * already complete and valid, only its dependencies are missing. Returns whether it succeeded so
10
+ * callers can adjust what they tell the creator to do next — `bitmagic check` cannot run without
11
+ * node_modules.
12
+ *
13
+ * The manager is chosen per machine rather than fixed (`preferredInstallManager`), and named out
14
+ * loud on the way past: which one ran decides the lockfile, and the lockfile decides what every
15
+ * later command tells this creator to type.
16
+ */
17
+ export declare function installDependencies(output: Output, dir: string, manager: PackageManager): boolean;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The two steps `init` and `import` both take around a scaffold: refusing to write into a
3
+ * directory that already holds someone's work, and installing the project's dependencies.
4
+ *
5
+ * Shared rather than copied because both are load-bearing and both are easy to get subtly
6
+ * different. `assertScaffoldable` is the only thing standing between a mistyped path and an
7
+ * overwritten project, and `installDependencies` encodes the rule that a failed install is NOT a
8
+ * failed scaffold — a second copy of either would eventually disagree with this one.
9
+ */
10
+ import * as fs from 'fs';
11
+ import { spawnSync } from 'child_process';
12
+ import { CliError } from '../errors.js';
13
+ import { installAllCommand } from '../project/package-manager.js';
14
+ /** Throws a CliError naming the directory if it exists, is non-empty, and `force` was not given. */
15
+ export function assertScaffoldable(dir, force) {
16
+ if (force)
17
+ return;
18
+ let entries;
19
+ try {
20
+ entries = fs.readdirSync(dir);
21
+ }
22
+ catch (err) {
23
+ const code = err.code;
24
+ if (code === 'ENOENT') {
25
+ // Directory doesn't exist yet: nothing to protect against.
26
+ return;
27
+ }
28
+ throw new CliError(`Cannot read directory ${dir}: ${code ?? String(err)}`);
29
+ }
30
+ if (entries.length > 0) {
31
+ throw new CliError(`${dir} already exists and is not empty. Pass --force to scaffold into it anyway.`);
32
+ }
33
+ }
34
+ /**
35
+ * Installing dependencies is the least reliable step: network flakiness or a broken local
36
+ * package-manager config are common and have nothing to do with whether the scaffold itself is
37
+ * correct. Its failure must not be reported as a scaffold failure — the project on disk is
38
+ * already complete and valid, only its dependencies are missing. Returns whether it succeeded so
39
+ * callers can adjust what they tell the creator to do next — `bitmagic check` cannot run without
40
+ * node_modules.
41
+ *
42
+ * The manager is chosen per machine rather than fixed (`preferredInstallManager`), and named out
43
+ * loud on the way past: which one ran decides the lockfile, and the lockfile decides what every
44
+ * later command tells this creator to type.
45
+ */
46
+ export function installDependencies(output, dir, manager) {
47
+ output.info(`Installing dependencies with ${manager}.`);
48
+ const result = spawnSync(manager, ['install'], { cwd: dir, stdio: 'inherit' });
49
+ if (result.error || result.status !== 0) {
50
+ const command = installAllCommand(manager);
51
+ output.info(`Warning: \`${command}\` failed. The project was scaffolded successfully — only its ` +
52
+ `dependencies are missing. cd into ${dir} and run \`${command}\` yourself.`);
53
+ return false;
54
+ }
55
+ return true;
56
+ }
57
+ //# sourceMappingURL=scaffold-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold-run.js","sourceRoot":"","sources":["../../src/scaffold/scaffold-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,iBAAiB,EAAuB,MAAM,+BAA+B,CAAC;AAEvF,oGAAoG;AACpG,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,KAAc;IAC5D,IAAI,KAAK;QAAE,OAAO;IAClB,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;QAC7C,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,2DAA2D;YAC3D,OAAO;QACT,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,yBAAyB,GAAG,KAAK,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,QAAQ,CAChB,GAAG,GAAG,4EAA4E,CACnF,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,GAAW,EAAE,OAAuB;IACtF,MAAM,CAAC,IAAI,CAAC,gCAAgC,OAAO,GAAG,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/E,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CACT,cAAc,OAAO,gEAAgE;YACnF,qCAAqC,GAAG,cAAc,OAAO,cAAc,CAC9E,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -233,4 +233,3 @@ export interface CatalogValidationIssue {
233
233
  message: string;
234
234
  suggestion?: string;
235
235
  }
236
- export { validateThemeStrict, validateThemeStrict as lightValidateTheme, isEngineFatalIssue, suggestClosest } from './theme-validate.js';
@@ -377,10 +377,13 @@ export const HUD_ICON_SLOTS = [
377
377
  { key: 'arrow', description: 'Movement-key arrow used in the controls overlay. ONE SVG — CSS rotates it for down/left/right. Default: thin chevron pointing up.' },
378
378
  ];
379
379
  export const HUD_ICON_KEYS = HUD_ICON_SLOTS.map(s => s.key);
380
- // The strict validator lives in theme-validate.ts — everything the old
381
- // lightValidateTheme checked plus glow keys, numeric ranges, the elements
382
- // block, slot suitability and decoration size/position/repeat. Re-exported
383
- // under the old name so existing imports keep working; new code should import
384
- // validateThemeStrict directly.
385
- export { validateThemeStrict, validateThemeStrict as lightValidateTheme, isEngineFatalIssue, suggestClosest } from './theme-validate.js';
380
+ // The strict validator lives next door in theme-validate.ts — everything the
381
+ // old lightValidateTheme checked plus glow keys, numeric ranges, the elements
382
+ // block, slot suitability and decoration size/position/repeat.
383
+ //
384
+ // It is deliberately NOT re-exported from here. theme-validate.ts imports this
385
+ // catalog's vocabulary, so a re-export back out of the catalog closes the loop
386
+ // into a circular import that the Mastra/Rollup bundler warns about on every
387
+ // dev rebuild. Import validateThemeStrict, isEngineFatalIssue and
388
+ // suggestClosest from './theme-validate.js' directly.
386
389
  //# sourceMappingURL=hud-theme-catalog.js.map