@bitmagic/cli 0.1.70-dev.1 → 0.1.70-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 (60) hide show
  1. package/README.md +47 -1
  2. package/assets/skills/publishing-to-poki/SKILL.md +50 -0
  3. package/dist/cli.d.ts +8 -0
  4. package/dist/commands/publish.d.ts +11 -0
  5. package/dist/commands/publish.js +18 -0
  6. package/dist/commands/publish.js.map +1 -1
  7. package/dist/ios/collect-urls.d.ts +1 -75
  8. package/dist/ios/collect-urls.js +2 -219
  9. package/dist/ios/collect-urls.js.map +1 -1
  10. package/dist/ios/local-server.d.ts +1 -11
  11. package/dist/ios/local-server.js +2 -87
  12. package/dist/ios/local-server.js.map +1 -1
  13. package/dist/ios/transform.d.ts +4 -45
  14. package/dist/ios/transform.js +3 -59
  15. package/dist/ios/transform.js.map +1 -1
  16. package/dist/ios/vendor.d.ts +1 -29
  17. package/dist/ios/vendor.js +2 -112
  18. package/dist/ios/vendor.js.map +1 -1
  19. package/dist/package/collect-urls.d.ts +75 -0
  20. package/dist/package/collect-urls.js +220 -0
  21. package/dist/package/collect-urls.js.map +1 -0
  22. package/dist/package/local-server.d.ts +13 -0
  23. package/dist/package/local-server.js +102 -0
  24. package/dist/package/local-server.js.map +1 -0
  25. package/dist/package/transform.d.ts +47 -0
  26. package/dist/package/transform.js +62 -0
  27. package/dist/package/transform.js.map +1 -0
  28. package/dist/package/vendor.d.ts +33 -0
  29. package/dist/package/vendor.js +114 -0
  30. package/dist/package/vendor.js.map +1 -0
  31. package/dist/poki/assets.d.ts +20 -0
  32. package/dist/poki/assets.js +154 -0
  33. package/dist/poki/assets.js.map +1 -0
  34. package/dist/poki/check.d.ts +21 -0
  35. package/dist/poki/check.js +134 -0
  36. package/dist/poki/check.js.map +1 -0
  37. package/dist/poki/publish.d.ts +22 -0
  38. package/dist/poki/publish.js +207 -0
  39. package/dist/poki/publish.js.map +1 -0
  40. package/dist/poki/transform.d.ts +6 -0
  41. package/dist/poki/transform.js +30 -0
  42. package/dist/poki/transform.js.map +1 -0
  43. package/dist/publish/bundle.d.ts +1 -0
  44. package/dist/publish/bundle.js +2 -2
  45. package/dist/publish/bundle.js.map +1 -1
  46. package/dist/scaffold/poki-skill-files.d.ts +2 -0
  47. package/dist/scaffold/poki-skill-files.js +7 -0
  48. package/dist/scaffold/poki-skill-files.js.map +1 -0
  49. package/dist/scaffold/project-files.js +1 -1
  50. package/dist/scaffold/project.js +2 -0
  51. package/dist/scaffold/project.js.map +1 -1
  52. package/dist/scaffold/skill-files.js +6 -4
  53. package/dist/scaffold/skill-files.js.map +1 -1
  54. package/dist/verify/browser.d.ts +3 -1
  55. package/dist/verify/browser.js +14 -9
  56. package/dist/verify/browser.js.map +1 -1
  57. package/dist/verify/classify.d.ts +2 -0
  58. package/dist/verify/classify.js +4 -1
  59. package/dist/verify/classify.js.map +1 -1
  60. package/package.json +4 -4
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Every remote URL the packaged game may load, collected up front (§4b of
3
+ * docs/ios-export-design.md).
4
+ *
5
+ * Deliberately NOT a port of game/scripts/generate-asset-manifest.mjs's field list — that list
6
+ * demonstrably misses fields the engine loads (glb/gltf assets by id, lodVariants, splat
7
+ * configs, heightmaps, HUD images, three of the four animation packs). Instead the work-copy
8
+ * JSON is deep-walked for anything URL-shaped, with a small deny-list of fields that never load
9
+ * at runtime. An unknown future URL field is vendored by default: too much beats a network
10
+ * fetch on a device in airplane mode.
11
+ *
12
+ * The record pass (record-validate.ts) is the empirical net under all of this — anything the
13
+ * running game fetches that this module missed still gets vendored.
14
+ */
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import { pathToFileURL } from 'url';
18
+ import { aliasSourceDir } from '../scaffold/aliases.js';
19
+ /** Fields whose URLs are editor/publish provenance, never loaded by the running game. */
20
+ const DENY_FIELDS = new Set([
21
+ 'screenshotUrl',
22
+ 'thumbnailUrl',
23
+ 'thumbnailUrlOverride',
24
+ 'faviconUrlOverride',
25
+ 'sourceGlbUrl',
26
+ 'sourceModelUrl',
27
+ 'sourceVxlMasterUrl',
28
+ 'sourceUrl',
29
+ ]);
30
+ function isRemoteUrl(value) {
31
+ return /^https?:\/\/[^\s"']+$/.test(value);
32
+ }
33
+ /** Walk any JSON value, collecting remote-URL strings outside the deny-listed fields. */
34
+ export function deepWalkUrls(value, out) {
35
+ if (typeof value === 'string') {
36
+ if (isRemoteUrl(value))
37
+ out.add(value);
38
+ return;
39
+ }
40
+ if (Array.isArray(value)) {
41
+ for (const item of value)
42
+ deepWalkUrls(item, out);
43
+ return;
44
+ }
45
+ if (typeof value === 'object' && value !== null) {
46
+ for (const [key, child] of Object.entries(value)) {
47
+ if (DENY_FIELDS.has(key))
48
+ continue;
49
+ deepWalkUrls(child, out);
50
+ }
51
+ }
52
+ }
53
+ /** The same compiled-code .glb scan the standalone lane runs, over the pro project's dist. */
54
+ export function scanCompiledCodeUrls(compiledCode) {
55
+ const urls = new Set();
56
+ for (const match of compiledCode.match(/https?:\/\/[^"'`\s)\\]+\.glb\b/g) ?? []) {
57
+ urls.add(match);
58
+ }
59
+ return urls;
60
+ }
61
+ /** All compiled game JS under dist/src/work, concatenated — for reference scans. */
62
+ export function readCompiledGameCode(root) {
63
+ const workDir = path.join(root, 'dist', aliasSourceDir('work'));
64
+ let combined = '';
65
+ const walk = (dir) => {
66
+ let entries;
67
+ try {
68
+ entries = fs.readdirSync(dir, { withFileTypes: true });
69
+ }
70
+ catch {
71
+ return;
72
+ }
73
+ for (const entry of entries) {
74
+ const full = path.join(dir, entry.name);
75
+ if (entry.isDirectory())
76
+ walk(full);
77
+ else if (entry.name.endsWith('.js'))
78
+ combined += fs.readFileSync(full, 'utf-8');
79
+ }
80
+ };
81
+ walk(workDir);
82
+ return combined;
83
+ }
84
+ function isAnimationAssets(value) {
85
+ if (typeof value !== 'object' || value === null)
86
+ return false;
87
+ const record = value;
88
+ return (typeof record.animationBaseUrl === 'string'
89
+ && typeof record.generatedAnimationBaseUrl === 'string'
90
+ && typeof record.characterUrls === 'object'
91
+ && Array.isArray(record.coreAnimations));
92
+ }
93
+ /**
94
+ * Import the vendored engine's compiled AnimationAssets — a pure data module with no imports,
95
+ * which is what makes this safe from plain node. Null (with the caller noting it) when the
96
+ * engine predates the module or the shape moved: the record pass still catches whatever the
97
+ * running game actually fetches.
98
+ */
99
+ export async function loadAnimationAssets(root) {
100
+ const compiled = path.join(root, 'dist', 'engine', 'engine', 'AnimationAssets.js');
101
+ if (!fs.existsSync(compiled))
102
+ return null;
103
+ try {
104
+ const loaded = await import(pathToFileURL(compiled).href);
105
+ const assets = loaded.animationAssets;
106
+ return isAnimationAssets(assets) ? assets : null;
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }
112
+ /**
113
+ * The built-in animation URLs this game can load. The ACTIVE library ships whole — its optional
114
+ * packs (melee/ranged/directional/posture) are loaded on demand by engine systems whose names
115
+ * game code never contains, so reference-scanning cannot bound them. The inactive library ships
116
+ * only clips the compiled game code names (`getBuiltinAnimationDef` resolves ids from either).
117
+ */
118
+ export function collectAnimationUrls(assets, worldData, compiledCode) {
119
+ const urls = new Set();
120
+ const bases = new Set([
121
+ assets.animationBaseUrl,
122
+ assets.generatedAnimationBaseUrl,
123
+ assets.animalBaseUrl,
124
+ assets.voxelAnimalBaseUrl,
125
+ assets.voxelFishBaseUrl,
126
+ ]);
127
+ // The rig loads for any humanoid: player (default present), NPCs, or multiplayer avatars —
128
+ // the same test the standalone collector applies.
129
+ const needsHumanoid = worldData.worldProfileData?.hasPlayerCharacter !== false
130
+ || compiledCode.includes('registerNpc')
131
+ || compiledCode.includes('MultiplayerSetup');
132
+ if (needsHumanoid) {
133
+ urls.add(assets.characterUrls.default);
134
+ urls.add(assets.characterUrls.defaultFallback);
135
+ }
136
+ const referenced = (clip) => compiledCode.includes(clip.motionId)
137
+ || (clip.file !== undefined && compiledCode.includes(clip.file));
138
+ // Each library flattened to clips carrying their own resolved URL, so the active/inactive rule
139
+ // below can be stated once instead of once per library.
140
+ const cdnLibrary = [
141
+ ...assets.coreAnimations.map((entry) => ({
142
+ ...entry,
143
+ url: entry.url ?? `${assets.animationBaseUrl}/${entry.file}`,
144
+ })),
145
+ ...assets.meleeWeaponAnimations,
146
+ ];
147
+ const generatedLibrary = [
148
+ assets.generatedAnimations,
149
+ assets.generatedMeleeWeaponAnimations,
150
+ assets.generatedRangedWeaponAnimations,
151
+ assets.generatedDirectionalAnimations,
152
+ assets.generatedPostureAnimations,
153
+ ].flatMap((pack) => pack.map((entry) => ({ ...entry, url: entry.url ?? `${assets.generatedAnimationBaseUrl}/${entry.file}` })));
154
+ const generatedActive = assets.activeLibrary === 'generated';
155
+ const active = generatedActive ? generatedLibrary : cdnLibrary;
156
+ const inactive = generatedActive ? cdnLibrary : generatedLibrary;
157
+ // The active library ships whole — but only to a game that animates a humanoid at all.
158
+ if (needsHumanoid) {
159
+ for (const clip of active)
160
+ urls.add(clip.url);
161
+ }
162
+ for (const clip of inactive) {
163
+ if (referenced(clip))
164
+ urls.add(clip.url);
165
+ }
166
+ // Skeleton rigs for skeleton-based animals in world data. Voxel animal/fish BODIES resolve
167
+ // through a registry inside the engine (species → file); those load at spawn during the
168
+ // record pass, which vendors them — the bases above are rewritten either way.
169
+ const declared = worldData.worldProfileData?.animals;
170
+ const animals = Array.isArray(declared) ? declared : [];
171
+ for (const animal of animals) {
172
+ if (typeof animal !== 'object' || animal === null)
173
+ continue;
174
+ const skeleton = animal.skeleton;
175
+ if (typeof skeleton === 'string') {
176
+ const file = assets.animalSkeletons[skeleton];
177
+ if (file !== undefined)
178
+ urls.add(`${assets.animalBaseUrl}/${file}`);
179
+ }
180
+ }
181
+ return { urls, bases };
182
+ }
183
+ function readWorkJson(root, file) {
184
+ try {
185
+ return JSON.parse(fs.readFileSync(path.join(root, aliasSourceDir('work'), file), 'utf-8'));
186
+ }
187
+ catch {
188
+ return null;
189
+ }
190
+ }
191
+ /** The full §4b collection: deep walk + compiled-code scan + built-in animation surface. */
192
+ export async function collectUrls(root) {
193
+ const urls = new Set();
194
+ const bases = new Set();
195
+ const notes = [];
196
+ const worldData = readWorkJson(root, 'world.json');
197
+ const gameData = readWorkJson(root, 'game.json');
198
+ deepWalkUrls(worldData, urls);
199
+ deepWalkUrls(gameData, urls);
200
+ const compiledCode = readCompiledGameCode(root);
201
+ if (compiledCode.length === 0) {
202
+ notes.push('No compiled game code under dist/ — code-referenced assets cannot be scanned.');
203
+ }
204
+ for (const url of scanCompiledCodeUrls(compiledCode))
205
+ urls.add(url);
206
+ const animationAssets = await loadAnimationAssets(root);
207
+ if (animationAssets === null) {
208
+ notes.push('The vendored engine\'s AnimationAssets could not be read — built-in animations will be '
209
+ + 'collected by the record pass instead.');
210
+ }
211
+ else {
212
+ const collected = collectAnimationUrls(animationAssets, (worldData ?? {}), compiledCode);
213
+ for (const url of collected.urls)
214
+ urls.add(url);
215
+ for (const base of collected.bases)
216
+ bases.add(base);
217
+ }
218
+ return { urls, bases, notes };
219
+ }
220
+ //# sourceMappingURL=collect-urls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-urls.js","sourceRoot":"","sources":["../../src/package/collect-urls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /** Extension → content type, shared by this server and the generated Swift handler. */
2
+ export declare const MIME_TYPES: ReadonlyMap<string, string>;
3
+ export declare const FALLBACK_MIME = "application/octet-stream";
4
+ export declare function mimeTypeFor(filePath: string): string;
5
+ export interface PayloadServer {
6
+ port: number;
7
+ origin: string;
8
+ close: () => Promise<void>;
9
+ }
10
+ /** Serve `dir` on an ephemeral localhost port. */
11
+ export declare function startPayloadServer(dir: string, options?: {
12
+ mountPath?: string;
13
+ }): Promise<PayloadServer>;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * A minimal static server over the staged payload directory, for the record and offline
3
+ * validation passes. GET only, no caching, path-traversal guarded. The MIME table is exported
4
+ * because the Swift scheme handler (xcode-template.ts) is generated from the SAME table — the
5
+ * validation browser and the shipped app must answer identically or the validation proves
6
+ * nothing.
7
+ */
8
+ import * as fs from 'fs';
9
+ import * as http from 'http';
10
+ import * as path from 'path';
11
+ /** Extension → content type, shared by this server and the generated Swift handler. */
12
+ export const MIME_TYPES = new Map([
13
+ ['html', 'text/html; charset=utf-8'],
14
+ ['js', 'text/javascript; charset=utf-8'],
15
+ ['mjs', 'text/javascript; charset=utf-8'],
16
+ ['css', 'text/css; charset=utf-8'],
17
+ ['json', 'application/json'],
18
+ ['wasm', 'application/wasm'],
19
+ ['glb', 'model/gltf-binary'],
20
+ ['gltf', 'model/gltf+json'],
21
+ ['png', 'image/png'],
22
+ ['webp', 'image/webp'],
23
+ ['jpg', 'image/jpeg'],
24
+ ['jpeg', 'image/jpeg'],
25
+ ['gif', 'image/gif'],
26
+ ['svg', 'image/svg+xml'],
27
+ ['ico', 'image/png'],
28
+ ['opus', 'audio/ogg'],
29
+ ['ogg', 'audio/ogg'],
30
+ ['mp3', 'audio/mpeg'],
31
+ ['wav', 'audio/wav'],
32
+ ['mp4', 'video/mp4'],
33
+ ['webm', 'video/webm'],
34
+ ['txt', 'text/plain; charset=utf-8'],
35
+ ]);
36
+ export const FALLBACK_MIME = 'application/octet-stream';
37
+ export function mimeTypeFor(filePath) {
38
+ const ext = filePath.split('?')[0].split('.').pop()?.toLowerCase() ?? '';
39
+ return MIME_TYPES.get(ext) ?? FALLBACK_MIME;
40
+ }
41
+ /** Serve `dir` on an ephemeral localhost port. */
42
+ export function startPayloadServer(dir, options = {}) {
43
+ const rootDir = path.resolve(dir);
44
+ const server = http.createServer((req, res) => {
45
+ if (req.method !== 'GET' && req.method !== 'HEAD') {
46
+ res.writeHead(405).end();
47
+ return;
48
+ }
49
+ let requestPath;
50
+ try {
51
+ requestPath = decodeURIComponent((req.url ?? '/').split('?')[0]);
52
+ }
53
+ catch {
54
+ res.writeHead(400).end();
55
+ return;
56
+ }
57
+ if (options.mountPath) {
58
+ if (!requestPath.startsWith(options.mountPath)) {
59
+ res.writeHead(404).end();
60
+ return;
61
+ }
62
+ requestPath = '/' + requestPath.slice(options.mountPath.length);
63
+ }
64
+ const resolved = path.resolve(rootDir, `.${requestPath}`);
65
+ if (resolved !== rootDir && !resolved.startsWith(rootDir + path.sep)) {
66
+ res.writeHead(403).end();
67
+ return;
68
+ }
69
+ let filePath = resolved;
70
+ try {
71
+ if (fs.statSync(filePath).isDirectory())
72
+ filePath = path.join(filePath, 'index.html');
73
+ const bytes = fs.readFileSync(filePath);
74
+ res
75
+ .writeHead(200, { 'Content-Type': mimeTypeFor(filePath), 'Cache-Control': 'no-store' })
76
+ .end(req.method === 'HEAD' ? undefined : bytes);
77
+ }
78
+ catch {
79
+ res.writeHead(404, { 'Content-Type': 'text/plain' }).end('not found');
80
+ }
81
+ });
82
+ return new Promise((resolve, reject) => {
83
+ server.once('error', reject);
84
+ server.listen(0, '127.0.0.1', () => {
85
+ const address = server.address();
86
+ if (address === null || typeof address === 'string') {
87
+ reject(new Error('The payload server did not report a port.'));
88
+ return;
89
+ }
90
+ resolve({
91
+ port: address.port,
92
+ origin: `http://127.0.0.1:${address.port}`,
93
+ close: () => new Promise((resolveClose) => {
94
+ server.close(() => resolveClose());
95
+ // Sockets Chrome keeps alive would otherwise hold close() open past the command.
96
+ server.closeAllConnections();
97
+ }),
98
+ });
99
+ });
100
+ });
101
+ }
102
+ //# sourceMappingURL=local-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-server.js","sourceRoot":"","sources":["../../src/package/local-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The pure HTML surgery of §4d (docs/ios-export-design.md): given the pro-lane bundle, produce
3
+ * the payload page. Two deliberately redundant mechanisms:
4
+ *
5
+ * - the `bm-bundled-assets` map (original URL → relative vendor path), which the engine's own
6
+ * ASSET_MAP mechanism resolves through — every three.js loader plus the explicit
7
+ * `ASSET_MAP.get` fetch sites;
8
+ * - a literal rewrite of every vendored URL AND the animation base-URL constants, which fixes
9
+ * the sites that bypass the map (raw fetch in PlacedObjectSystem/HeightmapSystem/physics2d/
10
+ * splat loaders, and the StartScreen/SelectionSteps/AchievementToast DOM urls): the world
11
+ * data is inlined in this HTML, so rewriting the literals fixes what those sites read.
12
+ *
13
+ * No DOM parser — the bundle is one multi-MB HTML string and the three edits are anchored on
14
+ * markers the scaffold's own template guarantees (`<head>`, `</head>`).
15
+ */
16
+ export interface TransformOptions {
17
+ html: string;
18
+ distribution: 'ios' | 'poki';
19
+ /** Original absolute URL → document-relative vendor path (`vendor/<host>/<path>`). */
20
+ assetMap: ReadonlyMap<string, string>;
21
+ /** Base-URL constant → relative base (see vendor.ts vendorBasePathFor). */
22
+ baseRewrites: ReadonlyMap<string, string>;
23
+ }
24
+ export interface TransformResult {
25
+ html: string;
26
+ /** How many URL literals were rewritten, for the build's notes. */
27
+ rewrites: number;
28
+ }
29
+ export declare const BUNDLED_ASSETS_ID = "bm-bundled-assets";
30
+ /**
31
+ * Rewrite every occurrence of the given URLs/bases in one pass. Longest key first, so an exact
32
+ * asset URL wins over a base that prefixes it — both rewrite into the same vendor layout, but
33
+ * the ordering keeps the replacement deterministic.
34
+ */
35
+ export declare function rewriteUrlLiterals(html: string, replacements: ReadonlyMap<string, string>): {
36
+ html: string;
37
+ rewrites: number;
38
+ };
39
+ /** Serialize the asset map for the inline block. `<` is escaped so no URL can ever terminate
40
+ * the script element early — standard inline-JSON hygiene, and still valid JSON. */
41
+ export declare function renderAssetMapJson(assetMap: ReadonlyMap<string, string>): string;
42
+ /**
43
+ * The full §4d transform. Throws when the bundle carries no `<head>`/`</head>` markers — that
44
+ * is not a page this lane understands, and silently skipping the injection would ship an app
45
+ * that fetches from the network.
46
+ */
47
+ export declare function transformBundleHtml(options: TransformOptions): TransformResult;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The pure HTML surgery of §4d (docs/ios-export-design.md): given the pro-lane bundle, produce
3
+ * the payload page. Two deliberately redundant mechanisms:
4
+ *
5
+ * - the `bm-bundled-assets` map (original URL → relative vendor path), which the engine's own
6
+ * ASSET_MAP mechanism resolves through — every three.js loader plus the explicit
7
+ * `ASSET_MAP.get` fetch sites;
8
+ * - a literal rewrite of every vendored URL AND the animation base-URL constants, which fixes
9
+ * the sites that bypass the map (raw fetch in PlacedObjectSystem/HeightmapSystem/physics2d/
10
+ * splat loaders, and the StartScreen/SelectionSteps/AchievementToast DOM urls): the world
11
+ * data is inlined in this HTML, so rewriting the literals fixes what those sites read.
12
+ *
13
+ * No DOM parser — the bundle is one multi-MB HTML string and the three edits are anchored on
14
+ * markers the scaffold's own template guarantees (`<head>`, `</head>`).
15
+ */
16
+ export const BUNDLED_ASSETS_ID = 'bm-bundled-assets';
17
+ function escapeRegExp(value) {
18
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
19
+ }
20
+ /**
21
+ * Rewrite every occurrence of the given URLs/bases in one pass. Longest key first, so an exact
22
+ * asset URL wins over a base that prefixes it — both rewrite into the same vendor layout, but
23
+ * the ordering keeps the replacement deterministic.
24
+ */
25
+ export function rewriteUrlLiterals(html, replacements) {
26
+ if (replacements.size === 0)
27
+ return { html, rewrites: 0 };
28
+ const keys = [...replacements.keys()].sort((a, b) => b.length - a.length);
29
+ const pattern = new RegExp(keys.map(escapeRegExp).join('|'), 'g');
30
+ let rewrites = 0;
31
+ const rewritten = html.replace(pattern, (match) => {
32
+ rewrites += 1;
33
+ // The alternation only matches keys of the map, so the lookup cannot miss.
34
+ return replacements.get(match) ?? match;
35
+ });
36
+ return { html: rewritten, rewrites };
37
+ }
38
+ /** Serialize the asset map for the inline block. `<` is escaped so no URL can ever terminate
39
+ * the script element early — standard inline-JSON hygiene, and still valid JSON. */
40
+ export function renderAssetMapJson(assetMap) {
41
+ return JSON.stringify(Object.fromEntries(assetMap)).replace(/</g, '\\u003c');
42
+ }
43
+ /**
44
+ * The full §4d transform. Throws when the bundle carries no `<head>`/`</head>` markers — that
45
+ * is not a page this lane understands, and silently skipping the injection would ship an app
46
+ * that fetches from the network.
47
+ */
48
+ export function transformBundleHtml(options) {
49
+ const combined = new Map([...options.assetMap, ...options.baseRewrites]);
50
+ const { html: rewritten, rewrites } = rewriteUrlLiterals(options.html, combined);
51
+ if (!rewritten.includes('<head>') || !rewritten.includes('</head>')) {
52
+ throw new Error('The built bundle has no <head> section to inject the asset map into.');
53
+ }
54
+ const mapBlock = `<script type="application/json" id="${BUNDLED_ASSETS_ID}">`
55
+ + renderAssetMapJson(options.assetMap)
56
+ + '</script>';
57
+ const html = rewritten
58
+ .replace('<head>', `<head>\n <meta name="bm:distribution" content="${options.distribution}">`)
59
+ .replace('</head>', ` ${mapBlock}\n</head>`);
60
+ return { html, rewrites };
61
+ }
62
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/package/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAiBH,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAErD,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,YAAyC;IAEzC,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC1D,MAAM,IAAI,GAAG,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAClE,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,QAAQ,IAAI,CAAC,CAAC;QACd,2EAA2E;QAC3E,OAAO,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC;IAC1C,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACvC,CAAC;AAED;qFACqF;AACrF,MAAM,UAAU,kBAAkB,CAAC,QAAqC;IACtE,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAC/E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAyB;IAC3D,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC;IACzE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAEjF,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;IAC1F,CAAC;IAED,MAAM,QAAQ,GACZ,uCAAuC,iBAAiB,IAAI;UAC1D,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC;UACpC,WAAW,CAAC;IAEhB,MAAM,IAAI,GAAG,SAAS;SACnB,OAAO,CAAC,QAAQ,EAAE,qDAAqD,OAAO,CAAC,YAAY,IAAI,CAAC;SAChG,OAAO,CAAC,SAAS,EAAE,OAAO,QAAQ,WAAW,CAAC,CAAC;IAElD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The document-relative path a URL is vendored at, or null for a URL this lane cannot mirror
3
+ * (unparseable, or a path that escapes the tree). The query string is dropped — asset URLs do
4
+ * not carry meaningful queries, and a `?` cannot survive as a same-file request path anyway.
5
+ */
6
+ export declare function vendorPathFor(url: string): string | null;
7
+ /** Where a BASE url's rewrite points: the same mapping, minus the appended 'index'. */
8
+ export declare function vendorBasePathFor(baseUrl: string): string | null;
9
+ export interface VendorFailure {
10
+ url: string;
11
+ error: string;
12
+ }
13
+ export interface VendorResult {
14
+ /** URL → document-relative vendor path, for every URL that is now on disk. */
15
+ entries: Map<string, string>;
16
+ failures: VendorFailure[];
17
+ /** How many came from the cache rather than the network. */
18
+ cacheHits: number;
19
+ }
20
+ export interface VendorOptions {
21
+ urls: Iterable<string>;
22
+ cacheDir: string;
23
+ payloadDir: string;
24
+ fetchImpl?: typeof globalThis.fetch;
25
+ log?: (message: string) => void;
26
+ concurrency?: number;
27
+ /** Target-specific layout; iOS retains its historical host/path layout. */
28
+ pathForUrl?: (url: string) => string | null;
29
+ /** Mutable resources must be fetched again rather than trusting a URL-only cache. */
30
+ reuseCache?: boolean;
31
+ }
32
+ /** Download (or copy from cache) every URL into the payload's vendor tree. */
33
+ export declare function vendorUrls(options: VendorOptions): Promise<VendorResult>;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Mirror remote assets into the payload's `vendor/` tree.
3
+ *
4
+ * The layout preserves `<host>/<pathname>` so that rewriting a BASE url in the bundle (see
5
+ * transform.ts) makes runtime-constructed `${base}/${file}` URLs land on the mirrored file —
6
+ * exact-URL map entries alone could never cover those.
7
+ *
8
+ * Downloads are cached under `.bitmagic/ios/cache` keyed by URL hash: generated-asset URLs are
9
+ * immutable in practice (uuid/content-addressed paths), and a rebuild should not re-download a
10
+ * hundred GLBs. Bytes are stored post-Content-Encoding (node fetch inflates); the engine's
11
+ * gzip-sniffing loaders accept raw bytes unchanged (fetchVwld.ts).
12
+ */
13
+ import { createHash } from 'crypto';
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ /**
17
+ * The document-relative path a URL is vendored at, or null for a URL this lane cannot mirror
18
+ * (unparseable, or a path that escapes the tree). The query string is dropped — asset URLs do
19
+ * not carry meaningful queries, and a `?` cannot survive as a same-file request path anyway.
20
+ */
21
+ export function vendorPathFor(url) {
22
+ let parsed;
23
+ try {
24
+ parsed = new URL(url);
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
30
+ return null;
31
+ let pathname = parsed.pathname;
32
+ if (pathname.endsWith('/') || pathname === '')
33
+ pathname += 'index';
34
+ const segments = [];
35
+ for (const rawSegment of pathname.split('/')) {
36
+ if (rawSegment === '')
37
+ continue;
38
+ let segment;
39
+ try {
40
+ segment = decodeURIComponent(rawSegment);
41
+ }
42
+ catch {
43
+ segment = rawSegment;
44
+ }
45
+ if (segment === '.' || segment === '..' || /[\\/]/.test(segment)
46
+ || [...segment].some((char) => char.charCodeAt(0) < 32))
47
+ return null;
48
+ segments.push(segment);
49
+ }
50
+ if (segments.length === 0)
51
+ return null;
52
+ return ['vendor', parsed.host, ...segments].join('/');
53
+ }
54
+ /** Where a BASE url's rewrite points: the same mapping, minus the appended 'index'. */
55
+ export function vendorBasePathFor(baseUrl) {
56
+ const mapped = vendorPathFor(baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`);
57
+ if (mapped === null)
58
+ return null;
59
+ return mapped.replace(/\/index$/, '');
60
+ }
61
+ function cacheFileFor(cacheDir, url) {
62
+ return path.join(cacheDir, createHash('sha256').update(url).digest('hex'));
63
+ }
64
+ /** Download (or copy from cache) every URL into the payload's vendor tree. */
65
+ export async function vendorUrls(options) {
66
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
67
+ const log = options.log ?? (() => { });
68
+ const entries = new Map();
69
+ const failures = [];
70
+ let cacheHits = 0;
71
+ fs.mkdirSync(options.cacheDir, { recursive: true });
72
+ const queue = [...new Set(options.urls)];
73
+ const worker = async () => {
74
+ for (;;) {
75
+ const url = queue.shift();
76
+ if (url === undefined)
77
+ return;
78
+ const relPath = (options.pathForUrl ?? vendorPathFor)(url);
79
+ if (relPath === null) {
80
+ failures.push({ url, error: 'not a vendorable URL' });
81
+ continue;
82
+ }
83
+ const cacheFile = cacheFileFor(options.cacheDir, url);
84
+ try {
85
+ if (options.reuseCache !== false && fs.existsSync(cacheFile)) {
86
+ cacheHits += 1;
87
+ }
88
+ else {
89
+ const response = await fetchImpl(url, { signal: AbortSignal.timeout(60_000) });
90
+ if (!response.ok)
91
+ throw new Error(`HTTP ${response.status}`);
92
+ const bytes = Buffer.from(await response.arrayBuffer());
93
+ // Write-then-rename so a crashed download never leaves a truncated cache entry that
94
+ // every later build would trust.
95
+ const tmp = `${cacheFile}.tmp-${process.pid}`;
96
+ fs.writeFileSync(tmp, bytes);
97
+ fs.renameSync(tmp, cacheFile);
98
+ log(` fetched ${url} (${bytes.byteLength} bytes)`);
99
+ }
100
+ const target = path.join(options.payloadDir, ...relPath.split('/'));
101
+ fs.mkdirSync(path.dirname(target), { recursive: true });
102
+ fs.copyFileSync(cacheFile, target);
103
+ entries.set(url, relPath);
104
+ }
105
+ catch (error) {
106
+ failures.push({ url, error: error instanceof Error ? error.message : String(error) });
107
+ }
108
+ }
109
+ };
110
+ const width = Math.max(1, Math.min(options.concurrency ?? 6, queue.length));
111
+ await Promise.all(Array.from({ length: width }, worker));
112
+ return { entries, failures, cacheHits };
113
+ }
114
+ //# sourceMappingURL=vendor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendor.js","sourceRoot":"","sources":["../../src/package/vendor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /** Only static resources may be downloaded by discovery; never vendor API responses. */
2
+ export declare function isStaticAsset(url: string): boolean;
3
+ export declare function scanAssetUrls(text: string): string[];
4
+ /** HTML resource attributes, excluding dormant URL strings in bundled engine code. */
5
+ export declare function scanHtmlAssets(html: string): string[];
6
+ /** Preserve sibling layout, but never merge distinct query resources or unsafe decoded paths. */
7
+ export declare function pokiAssetPath(url: string): string | null;
8
+ /** References inside a sidecar, retained with their original spelling for rewriting. */
9
+ export declare function sidecarReferences(text: string, url: string): Map<string, string>;
10
+ /** Rewrite resource references only; short names must not replace arbitrary CSS or GLTF text. */
11
+ export declare function rewriteSidecar(text: string, url: string, replacements: ReadonlyMap<string, string>): string;
12
+ export interface PackageAssetsOptions {
13
+ urls: Iterable<string>;
14
+ payloadDir: string;
15
+ cacheDir: string;
16
+ fetch: typeof globalThis.fetch;
17
+ log: (message: string) => void;
18
+ }
19
+ /** Walk GLTF/CSS dependencies as well as direct URLs; a missing declared asset is an error. */
20
+ export declare function packageAssets(options: PackageAssetsOptions): Promise<Map<string, string>>;