@bitmagic/cli 0.1.55-dev.0 → 0.1.55-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 (73) hide show
  1. package/README.md +45 -3
  2. package/dist/cli.d.ts +23 -1
  3. package/dist/cli.js +4 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/cover.d.ts +13 -2
  6. package/dist/commands/cover.js +21 -7
  7. package/dist/commands/cover.js.map +1 -1
  8. package/dist/commands/forge.d.ts +14 -0
  9. package/dist/commands/forge.js +41 -3
  10. package/dist/commands/forge.js.map +1 -1
  11. package/dist/commands/generate.d.ts +12 -0
  12. package/dist/commands/generate.js +77 -24
  13. package/dist/commands/generate.js.map +1 -1
  14. package/dist/commands/init.js +4 -0
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/ios.d.ts +2 -0
  17. package/dist/commands/ios.js +333 -0
  18. package/dist/commands/ios.js.map +1 -0
  19. package/dist/commands/reference.d.ts +41 -0
  20. package/dist/commands/reference.js +398 -0
  21. package/dist/commands/reference.js.map +1 -0
  22. package/dist/generate/stream.d.ts +6 -0
  23. package/dist/generate/stream.js +2 -1
  24. package/dist/generate/stream.js.map +1 -1
  25. package/dist/ios/app-config.d.ts +54 -0
  26. package/dist/ios/app-config.js +109 -0
  27. package/dist/ios/app-config.js.map +1 -0
  28. package/dist/ios/collect-urls.d.ts +73 -0
  29. package/dist/ios/collect-urls.js +230 -0
  30. package/dist/ios/collect-urls.js.map +1 -0
  31. package/dist/ios/icon.d.ts +12 -0
  32. package/dist/ios/icon.js +70 -0
  33. package/dist/ios/icon.js.map +1 -0
  34. package/dist/ios/local-server.d.ts +11 -0
  35. package/dist/ios/local-server.js +88 -0
  36. package/dist/ios/local-server.js.map +1 -0
  37. package/dist/ios/paths.d.ts +21 -0
  38. package/dist/ios/paths.js +23 -0
  39. package/dist/ios/paths.js.map +1 -0
  40. package/dist/ios/record-validate.d.ts +25 -0
  41. package/dist/ios/record-validate.js +98 -0
  42. package/dist/ios/record-validate.js.map +1 -0
  43. package/dist/ios/transform.d.ts +47 -0
  44. package/dist/ios/transform.js +67 -0
  45. package/dist/ios/transform.js.map +1 -0
  46. package/dist/ios/vendor.d.ts +29 -0
  47. package/dist/ios/vendor.js +113 -0
  48. package/dist/ios/vendor.js.map +1 -0
  49. package/dist/ios/xcode-template.d.ts +11 -0
  50. package/dist/ios/xcode-template.js +615 -0
  51. package/dist/ios/xcode-template.js.map +1 -0
  52. package/dist/ios/xcodebuild.d.ts +71 -0
  53. package/dist/ios/xcodebuild.js +216 -0
  54. package/dist/ios/xcodebuild.js.map +1 -0
  55. package/dist/project/jobs.d.ts +1 -1
  56. package/dist/project/reference-file.d.ts +44 -0
  57. package/dist/project/reference-file.js +94 -0
  58. package/dist/project/reference-file.js.map +1 -0
  59. package/dist/project/reference.d.ts +78 -0
  60. package/dist/project/reference.js +175 -0
  61. package/dist/project/reference.js.map +1 -0
  62. package/dist/scaffold/project-files.js +19 -16
  63. package/dist/scaffold/project-files.js.map +1 -1
  64. package/dist/scaffold/reference-guidance.d.ts +20 -0
  65. package/dist/scaffold/reference-guidance.js +79 -0
  66. package/dist/scaffold/reference-guidance.js.map +1 -0
  67. package/dist/update/self-install.d.ts +15 -6
  68. package/dist/update/self-install.js +56 -15
  69. package/dist/update/self-install.js.map +1 -1
  70. package/dist/verify/browser.d.ts +8 -0
  71. package/dist/verify/browser.js +2 -0
  72. package/dist/verify/browser.js.map +1 -1
  73. package/package.json +4 -4
@@ -0,0 +1,98 @@
1
+ /**
2
+ * §4e/f of docs/ios-export-design.md: boot the staged payload the way the app will run it —
3
+ * headless system Chrome as an iPhone, `?platform=mobile` — twice.
4
+ *
5
+ * - `record`: remote requests are allowed through and every one on a non-denylisted host is
6
+ * reported as a miss for the caller to vendor. This is the empirical net under the static
7
+ * collector: whatever the running game actually fetches, however the URL was constructed.
8
+ * - `offline`: every remote request is aborted. The run must still pass `classifyVerifyRun`
9
+ * (the same "did the game render" verdict verify and the publish smoke check use) with zero
10
+ * remote asset requests — that is the design's acceptance criterion for "no internet needed".
11
+ *
12
+ * Denylisted endpoints are the online-only features the engine already degrades without
13
+ * (game-server, play-auth, play sessions, fonts, analytics) — aborted in BOTH passes so the
14
+ * record run observes the same world the device will.
15
+ */
16
+ import * as fs from 'fs';
17
+ import * as path from 'path';
18
+ import { browserLaunchPlan } from '../browser-gl.js';
19
+ import { DEFAULT_START_INTERACTION, runHeadlessCheck } from '../verify/browser.js';
20
+ import { classifyVerifyRun } from '../verify/classify.js';
21
+ import { mobileContextOptions } from '../verify/device.js';
22
+ import { startPayloadServer } from './local-server.js';
23
+ /** Long enough for a forged level's worth of local loads; adaptive settle exits earlier. */
24
+ export const PACKAGED_CHECK_SETTLE_MS = 25_000;
25
+ /**
26
+ * Online-only endpoints the packaged game may talk to when a network exists, and must live
27
+ * without when one does not. Never vendored, aborted during both passes.
28
+ */
29
+ export function isOnlineOnlyUrl(url) {
30
+ let parsed;
31
+ try {
32
+ parsed = new URL(url);
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ const host = parsed.hostname;
38
+ return (host.includes('game-server')
39
+ || parsed.pathname.endsWith('/play-auth.html')
40
+ || host === 'fonts.googleapis.com'
41
+ || host === 'fonts.gstatic.com'
42
+ || host === 'esm.sh'
43
+ || host === 'esm.run'
44
+ || host.endsWith('.sentry.io')
45
+ || host.endsWith('google-analytics.com')
46
+ || host.endsWith('googletagmanager.com')
47
+ // Local dev services (api-server 3002, game-server 1666, agent 4111, lid relay 8765) —
48
+ // NOT the payload server, whose exact origin is allowed before this predicate runs.
49
+ || host === 'localhost'
50
+ || host === '127.0.0.1');
51
+ }
52
+ export async function runPackagedCheck(options) {
53
+ fs.mkdirSync(options.artifactDir, { recursive: true });
54
+ const consolePath = path.join(options.artifactDir, 'console.log');
55
+ const screenshotPath = path.join(options.artifactDir, 'screenshot.png');
56
+ const server = await startPayloadServer(options.payloadDir);
57
+ const misses = new Set();
58
+ const routes = async (context) => {
59
+ await context.route('**/*', (route) => {
60
+ const url = route.request().url();
61
+ if (url.startsWith(server.origin)) {
62
+ void route.continue();
63
+ return;
64
+ }
65
+ if (isOnlineOnlyUrl(url)) {
66
+ void route.abort('internetdisconnected');
67
+ return;
68
+ }
69
+ misses.add(url);
70
+ if (options.mode === 'record')
71
+ void route.continue();
72
+ else
73
+ void route.abort('internetdisconnected');
74
+ });
75
+ };
76
+ try {
77
+ const observations = await runHeadlessCheck({
78
+ url: `${server.origin}/index.html?platform=mobile&renderer=webgl`,
79
+ screenshotPath,
80
+ consolePath,
81
+ settleMs: options.settleMs ?? PACKAGED_CHECK_SETTLE_MS,
82
+ startInteraction: DEFAULT_START_INTERACTION,
83
+ launch: browserLaunchPlan(process.env, 'webgl'),
84
+ device: mobileContextOptions(options.orientation ?? 'portrait'),
85
+ routes,
86
+ });
87
+ return {
88
+ result: classifyVerifyRun(observations),
89
+ misses: [...misses],
90
+ consolePath,
91
+ screenshotPath,
92
+ };
93
+ }
94
+ finally {
95
+ await server.close();
96
+ }
97
+ }
98
+ //# sourceMappingURL=record-validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-validate.js","sourceRoot":"","sources":["../../src/ios/record-validate.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
+ /** Original absolute URL → document-relative vendor path (`vendor/<host>/<path>`). */
19
+ assetMap: ReadonlyMap<string, string>;
20
+ /** Base-URL constant → relative base (see vendor.ts vendorBasePathFor). */
21
+ baseRewrites: ReadonlyMap<string, string>;
22
+ }
23
+ export interface TransformResult {
24
+ html: string;
25
+ /** How many URL literals were rewritten, for the build's notes. */
26
+ rewrites: number;
27
+ }
28
+ export declare const DISTRIBUTION_META = "<meta name=\"bm:distribution\" content=\"ios\">";
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,67 @@
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 DISTRIBUTION_META = '<meta name="bm:distribution" content="ios">';
17
+ export const BUNDLED_ASSETS_ID = 'bm-bundled-assets';
18
+ function escapeRegExp(value) {
19
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
20
+ }
21
+ /**
22
+ * Rewrite every occurrence of the given URLs/bases in one pass. Longest key first, so an exact
23
+ * asset URL wins over a base that prefixes it — both rewrite into the same vendor layout, but
24
+ * the ordering keeps the replacement deterministic.
25
+ */
26
+ export function rewriteUrlLiterals(html, replacements) {
27
+ if (replacements.size === 0)
28
+ return { html, rewrites: 0 };
29
+ const keys = [...replacements.keys()].sort((a, b) => b.length - a.length);
30
+ const pattern = new RegExp(keys.map(escapeRegExp).join('|'), 'g');
31
+ let rewrites = 0;
32
+ const rewritten = html.replace(pattern, (match) => {
33
+ rewrites += 1;
34
+ // The alternation only matches keys of the map, so the lookup cannot miss.
35
+ return replacements.get(match) ?? match;
36
+ });
37
+ return { html: rewritten, rewrites };
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 function renderAssetMapJson(assetMap) {
42
+ return JSON.stringify(Object.fromEntries(assetMap)).replace(/</g, '\\u003c');
43
+ }
44
+ /**
45
+ * The full §4d transform. Throws when the bundle carries no `<head>`/`</head>` markers — that
46
+ * is not a page this lane understands, and silently skipping the injection would ship an app
47
+ * that fetches from the network.
48
+ */
49
+ export function transformBundleHtml(options) {
50
+ const combined = new Map();
51
+ for (const [url, rel] of options.assetMap)
52
+ combined.set(url, rel);
53
+ for (const [base, rel] of options.baseRewrites)
54
+ combined.set(base, rel);
55
+ const { html: rewritten, rewrites } = rewriteUrlLiterals(options.html, combined);
56
+ if (!rewritten.includes('<head>') || !rewritten.includes('</head>')) {
57
+ throw new Error('The built bundle has no <head> section to inject the asset map into.');
58
+ }
59
+ const mapBlock = `<script type="application/json" id="${BUNDLED_ASSETS_ID}">`
60
+ + renderAssetMapJson(options.assetMap)
61
+ + '</script>';
62
+ const html = rewritten
63
+ .replace('<head>', `<head>\n ${DISTRIBUTION_META}`)
64
+ .replace('</head>', ` ${mapBlock}\n</head>`);
65
+ return { html, rewrites };
66
+ }
67
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/ios/transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,MAAM,CAAC,MAAM,iBAAiB,GAAG,6CAA6C,CAAC;AAC/E,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,EAAkB,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ;QAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAClE,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,YAAY;QAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAExE,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,eAAe,iBAAiB,EAAE,CAAC;SACrD,OAAO,CAAC,SAAS,EAAE,OAAO,QAAQ,WAAW,CAAC,CAAC;IAElD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,29 @@
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
+ }
28
+ /** Download (or copy from cache) every URL into the payload's vendor tree. */
29
+ export declare function vendorUrls(options: VendorOptions): Promise<VendorResult>;
@@ -0,0 +1,113 @@
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 === '..')
46
+ return null;
47
+ segments.push(segment);
48
+ }
49
+ if (segments.length === 0)
50
+ return null;
51
+ return ['vendor', parsed.host, ...segments].join('/');
52
+ }
53
+ /** Where a BASE url's rewrite points: the same mapping, minus the appended 'index'. */
54
+ export function vendorBasePathFor(baseUrl) {
55
+ const mapped = vendorPathFor(baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`);
56
+ if (mapped === null)
57
+ return null;
58
+ return mapped.replace(/\/index$/, '');
59
+ }
60
+ function cacheFileFor(cacheDir, url) {
61
+ return path.join(cacheDir, createHash('sha256').update(url).digest('hex'));
62
+ }
63
+ /** Download (or copy from cache) every URL into the payload's vendor tree. */
64
+ export async function vendorUrls(options) {
65
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
66
+ const log = options.log ?? (() => { });
67
+ const entries = new Map();
68
+ const failures = [];
69
+ let cacheHits = 0;
70
+ fs.mkdirSync(options.cacheDir, { recursive: true });
71
+ const queue = [...new Set(options.urls)];
72
+ const worker = async () => {
73
+ for (;;) {
74
+ const url = queue.shift();
75
+ if (url === undefined)
76
+ return;
77
+ const relPath = vendorPathFor(url);
78
+ if (relPath === null) {
79
+ failures.push({ url, error: 'not a vendorable URL' });
80
+ continue;
81
+ }
82
+ const cacheFile = cacheFileFor(options.cacheDir, url);
83
+ try {
84
+ if (fs.existsSync(cacheFile)) {
85
+ cacheHits += 1;
86
+ }
87
+ else {
88
+ const response = await fetchImpl(url);
89
+ if (!response.ok)
90
+ throw new Error(`HTTP ${response.status}`);
91
+ const bytes = Buffer.from(await response.arrayBuffer());
92
+ // Write-then-rename so a crashed download never leaves a truncated cache entry that
93
+ // every later build would trust.
94
+ const tmp = `${cacheFile}.tmp-${process.pid}`;
95
+ fs.writeFileSync(tmp, bytes);
96
+ fs.renameSync(tmp, cacheFile);
97
+ log(` fetched ${url} (${bytes.byteLength} bytes)`);
98
+ }
99
+ const target = path.join(options.payloadDir, ...relPath.split('/'));
100
+ fs.mkdirSync(path.dirname(target), { recursive: true });
101
+ fs.copyFileSync(cacheFile, target);
102
+ entries.set(url, relPath);
103
+ }
104
+ catch (error) {
105
+ failures.push({ url, error: error instanceof Error ? error.message : String(error) });
106
+ }
107
+ }
108
+ };
109
+ const width = Math.max(1, Math.min(options.concurrency ?? 6, queue.length));
110
+ await Promise.all(Array.from({ length: width }, worker));
111
+ return { entries, failures, cacheHits };
112
+ }
113
+ //# sourceMappingURL=vendor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendor.js","sourceRoot":"","sources":["../../src/ios/vendor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { IosAppConfig } from './app-config.js';
2
+ export declare const TARGET_NAME = "Game";
3
+ export declare const PAYLOAD_DIR_NAME = "GamePayload";
4
+ export declare const URL_SCHEME = "bmapp";
5
+ /** The Info.plist orientation array for the project's declaration (§6). */
6
+ export declare function orientationValues(orientation: 'portrait' | 'landscape' | undefined): string[];
7
+ /**
8
+ * Write the whole project (everything except the payload contents and the icon PNG, which have
9
+ * their own producers). Existing files are overwritten — the project is CLI-owned.
10
+ */
11
+ export declare function stampXcodeProject(projectDir: string, config: IosAppConfig): void;