@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.
- package/README.md +60 -4
- package/dist/cli.d.ts +36 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/forge.js +15 -25
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.d.ts +2 -0
- package/dist/commands/generate.js +28 -1
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/import.d.ts +75 -0
- package/dist/commands/import.js +338 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/init.js +1 -45
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/verify.js +17 -29
- package/dist/commands/verify.js.map +1 -1
- package/dist/editor/animation-preview-page.js +1 -1
- package/dist/editor/motion-panel.d.ts +3 -3
- package/dist/editor/motion-panel.js +272 -2
- package/dist/editor/motion-panel.js.map +1 -1
- package/dist/editor/motion-sink.d.ts +10 -2
- package/dist/editor/motion-sink.js +27 -2
- package/dist/editor/motion-sink.js.map +1 -1
- package/dist/editor/server.js +100 -1
- package/dist/editor/server.js.map +1 -1
- package/dist/generate/animation-from-video.d.ts +12 -0
- package/dist/generate/animation-from-video.js +15 -6
- package/dist/generate/animation-from-video.js.map +1 -1
- package/dist/generate/trim-analysis.d.ts +22 -0
- package/dist/generate/trim-analysis.js +31 -0
- package/dist/generate/trim-analysis.js.map +1 -0
- package/dist/local-port.d.ts +23 -4
- package/dist/local-port.js +44 -13
- package/dist/local-port.js.map +1 -1
- package/dist/scaffold/export-zip.d.ts +48 -0
- package/dist/scaffold/export-zip.js +167 -0
- package/dist/scaffold/export-zip.js.map +1 -0
- package/dist/scaffold/import-project.d.ts +34 -0
- package/dist/scaffold/import-project.js +108 -0
- package/dist/scaffold/import-project.js.map +1 -0
- package/dist/scaffold/project-files.js +9 -1
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.d.ts +34 -0
- package/dist/scaffold/project.js +53 -22
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/rehost-assets.d.ts +63 -0
- package/dist/scaffold/rehost-assets.js +270 -0
- package/dist/scaffold/rehost-assets.js.map +1 -0
- package/dist/scaffold/scaffold-run.d.ts +17 -0
- package/dist/scaffold/scaffold-run.js +57 -0
- package/dist/scaffold/scaffold-run.js.map +1 -0
- package/dist/theme/hud-theme-catalog.d.ts +0 -1
- package/dist/theme/hud-theme-catalog.js +9 -6
- package/dist/theme/hud-theme-catalog.js.map +1 -1
- package/dist/theme/theme-io.js +2 -1
- package/dist/theme/theme-io.js.map +1 -1
- package/dist/verify/browser.js +87 -18
- package/dist/verify/browser.js.map +1 -1
- package/dist/verify/classify.d.ts +12 -0
- package/dist/verify/classify.js +24 -3
- package/dist/verify/classify.js.map +1 -1
- package/dist/verify/start.d.ts +23 -3
- package/dist/verify/start.js +13 -1
- package/dist/verify/start.js.map +1 -1
- package/dist/verify/wait-for-server.d.ts +11 -0
- package/dist/verify/wait-for-server.js +29 -1
- package/dist/verify/wait-for-server.js.map +1 -1
- package/package.json +7 -5
package/dist/scaffold/project.js
CHANGED
|
@@ -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
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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"}
|
|
@@ -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
|
|
381
|
-
// lightValidateTheme checked plus glow keys, numeric ranges, the elements
|
|
382
|
-
// block, slot suitability and decoration size/position/repeat.
|
|
383
|
-
//
|
|
384
|
-
//
|
|
385
|
-
|
|
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
|