@yali-engine/cli 0.0.0-stage → 0.4.0

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 (75) hide show
  1. package/bin/yali.mjs +12 -0
  2. package/dist/budgets.d.ts +25 -0
  3. package/dist/budgets.js +27 -0
  4. package/dist/budgets.js.map +1 -0
  5. package/dist/cdp.d.ts +25 -0
  6. package/dist/cdp.js +106 -0
  7. package/dist/cdp.js.map +1 -0
  8. package/dist/cli.d.ts +7 -0
  9. package/dist/cli.js +83 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/cmd-bench.d.ts +17 -0
  12. package/dist/cmd-bench.js +74 -0
  13. package/dist/cmd-bench.js.map +1 -0
  14. package/dist/cmd-build.d.ts +13 -0
  15. package/dist/cmd-build.js +40 -0
  16. package/dist/cmd-build.js.map +1 -0
  17. package/dist/cmd-convert.d.ts +27 -0
  18. package/dist/cmd-convert.js +456 -0
  19. package/dist/cmd-convert.js.map +1 -0
  20. package/dist/cmd-create.d.ts +8 -0
  21. package/dist/cmd-create.js +248 -0
  22. package/dist/cmd-create.js.map +1 -0
  23. package/dist/cmd-dev.d.ts +9 -0
  24. package/dist/cmd-dev.js +24 -0
  25. package/dist/cmd-dev.js.map +1 -0
  26. package/dist/cmd-game.d.ts +24 -0
  27. package/dist/cmd-game.js +127 -0
  28. package/dist/cmd-game.js.map +1 -0
  29. package/dist/cmd-headless.d.ts +15 -0
  30. package/dist/cmd-headless.js +59 -0
  31. package/dist/cmd-headless.js.map +1 -0
  32. package/dist/cmd-inspect.d.ts +13 -0
  33. package/dist/cmd-inspect.js +64 -0
  34. package/dist/cmd-inspect.js.map +1 -0
  35. package/dist/cmd-test.d.ts +9 -0
  36. package/dist/cmd-test.js +68 -0
  37. package/dist/cmd-test.js.map +1 -0
  38. package/dist/collision.d.ts +31 -0
  39. package/dist/collision.js +471 -0
  40. package/dist/collision.js.map +1 -0
  41. package/dist/commands.d.ts +18 -0
  42. package/dist/commands.js +105 -0
  43. package/dist/commands.js.map +1 -0
  44. package/dist/encoder.d.ts +63 -0
  45. package/dist/encoder.js +119 -0
  46. package/dist/encoder.js.map +1 -0
  47. package/dist/index.d.ts +18 -0
  48. package/dist/index.js +19 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/main.d.ts +1 -0
  51. package/dist/main.js +9 -0
  52. package/dist/main.js.map +1 -0
  53. package/dist/native-encoder.d.ts +39 -0
  54. package/dist/native-encoder.js +142 -0
  55. package/dist/native-encoder.js.map +1 -0
  56. package/dist/output.d.ts +28 -0
  57. package/dist/output.js +27 -0
  58. package/dist/output.js.map +1 -0
  59. package/dist/png.d.ts +26 -0
  60. package/dist/png.js +134 -0
  61. package/dist/png.js.map +1 -0
  62. package/dist/project.d.ts +15 -0
  63. package/dist/project.js +35 -0
  64. package/dist/project.js.map +1 -0
  65. package/dist/size.d.ts +47 -0
  66. package/dist/size.js +100 -0
  67. package/dist/size.js.map +1 -0
  68. package/package.json +43 -3
  69. package/vendor/ENCODER-BUILD.json +33 -0
  70. package/vendor/LICENSE-basis_universal +201 -0
  71. package/vendor/LICENSE-zstd +30 -0
  72. package/vendor/NOTICE +33 -0
  73. package/vendor/basis_encoder.mjs +2 -0
  74. package/vendor/basis_encoder.wasm +0 -0
  75. package/README.md +0 -3
@@ -0,0 +1,142 @@
1
+ var _a;
2
+ // The native backend of the texture encoder (ADR-0097): Binomial's own `basisu` v1.60
3
+ // executable, run per texture with the same settings the WASM build uses (UASTC level, RDO, mips,
4
+ // sRGB, Zstandard supercompression), and the choice between it and WASM (`--encoder`).
5
+ import { execFile } from 'node:child_process';
6
+ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
7
+ import { createRequire } from 'node:module';
8
+ import { availableParallelism, tmpdir } from 'node:os';
9
+ import { delimiter, join } from 'node:path';
10
+ import { promisify } from 'node:util';
11
+ import { YaliError } from '@yali-engine/core';
12
+ import { WasmEncoder } from "./encoder.js";
13
+ import { encodePng } from "./png.js";
14
+ const run = promisify(execFile);
15
+ /** The Basis version every backend is pinned to (ADR-0078). */
16
+ const PINNED = '1.60';
17
+ /** The platform package that would carry a prebuilt basisu for this machine. */
18
+ export function platformPackage(platform = process.platform, arch = process.arch) {
19
+ return `@yali-engine/basisu-${platform}-${arch}`;
20
+ }
21
+ async function versionOf(path) {
22
+ try {
23
+ const { stdout } = await run(path, ['-version'], { timeout: 10_000, windowsHide: true });
24
+ return /v(\d+\.\d+)(\.\d+)?/.exec(stdout)?.[1] ?? null;
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ }
30
+ /**
31
+ * Finds a native basisu, in ADR-0078's order: `YALI_BASISU` (a path), the optional platform
32
+ * package, `basisu` on PATH. Only v1.60 is used (the pinned version); `toktx` is not, since it
33
+ * carries an older Basis.
34
+ */
35
+ export async function probeNative(env = process.env) {
36
+ const exe = process.platform === 'win32' ? 'basisu.exe' : 'basisu';
37
+ const candidates = [];
38
+ if (env.YALI_BASISU)
39
+ candidates.push({ path: env.YALI_BASISU, source: 'env' });
40
+ try {
41
+ const pkg = createRequire(import.meta.url).resolve(`${platformPackage()}/package.json`);
42
+ candidates.push({ path: join(pkg, '..', 'bin', exe), source: 'npm' });
43
+ }
44
+ catch {
45
+ // the optional package is not installed
46
+ }
47
+ for (const dir of (env.PATH ?? env.Path ?? '').split(delimiter))
48
+ if (dir)
49
+ candidates.push({ path: join(dir, exe), source: 'path' });
50
+ let found = null;
51
+ for (const c of candidates) {
52
+ if (!existsSync(c.path))
53
+ continue;
54
+ const v = await versionOf(c.path);
55
+ if (v === PINNED)
56
+ return { path: c.path, version: v, source: c.source, reason: `basisu ${v} from ${c.source === 'env' ? 'YALI_BASISU' : c.source === 'npm' ? platformPackage() : 'PATH'}` };
57
+ found ??= `${c.path} is basisu ${v ?? '(unknown version)'}, not ${PINNED}`;
58
+ }
59
+ return { path: null, version: null, source: null, reason: found ?? `no basisu ${PINNED} (set YALI_BASISU, install ${platformPackage()}, or put basisu on PATH)` };
60
+ }
61
+ /** Basis's own command-line encoder as a `TextureEncoder`. */
62
+ export class NativeEncoder {
63
+ path;
64
+ #version;
65
+ #queue = Promise.resolve();
66
+ constructor(path, version) {
67
+ this.path = path;
68
+ this.#version = version;
69
+ }
70
+ capabilities() {
71
+ return { modes: ['uastc', 'etc1s'], inputs: ['png', 'jpeg', 'rgba8'], threads: availableParallelism() };
72
+ }
73
+ version() {
74
+ return { backend: 'native', basis: '1.60', build: `basisu ${this.#version}` };
75
+ }
76
+ /** The command line for `settings` (exported for the tests: the flags mirror basis-encoder.cpp). */
77
+ static args(input, output, s) {
78
+ const level = s.level ?? (s.mode === 'uastc' ? 1 : 128);
79
+ const a = ['-ktx2', '-mipmap'];
80
+ if (s.mode === 'uastc') {
81
+ a.push('-uastc', '-uastc_level', String(level));
82
+ if (s.rdo)
83
+ a.push('-uastc_rdo_l', String(s.rdo));
84
+ }
85
+ else
86
+ a.push('-q', String(level));
87
+ if (!s.srgb)
88
+ a.push('-linear');
89
+ a.push('-output_file', output, input);
90
+ return a;
91
+ }
92
+ encode(image, settings) {
93
+ const job = this.#queue.then(() => this.#encodeNow(image, settings));
94
+ this.#queue = job.catch(() => undefined);
95
+ return job;
96
+ }
97
+ async #encodeNow(image, s) {
98
+ const dir = mkdtempSync(join(tmpdir(), 'yali-basisu-'));
99
+ try {
100
+ const input = join(dir, image.kind === 'jpeg' ? 'in.jpg' : 'in.png');
101
+ if (image.kind === 'rgba8') {
102
+ if (image.data.length !== image.width * image.height * 4) {
103
+ throw new YaliError('BAD_IMAGE', `RGBA8 data is ${image.data.length} bytes, expected ${image.width} × ${image.height} × 4.`, 'Pass width × height × 4 bytes.');
104
+ }
105
+ writeFileSync(input, encodePng({ width: image.width, height: image.height, rgba: image.data }));
106
+ }
107
+ else
108
+ writeFileSync(input, image.bytes);
109
+ const output = join(dir, 'out.ktx2');
110
+ try {
111
+ await run(this.path, _a.args(input, output, s), { cwd: dir, timeout: 600_000, windowsHide: true, maxBuffer: 64 << 20 });
112
+ }
113
+ catch (e) {
114
+ throw new YaliError('ENCODE_FAILED', `basisu failed: ${e instanceof Error ? e.message.split('\n')[0] : String(e)}`, 'Run with --encoder=wasm, or check the basisu install.');
115
+ }
116
+ if (!existsSync(output))
117
+ throw new YaliError('ENCODE_FAILED', 'basisu wrote no file.', 'Run with --encoder=wasm, or check the basisu install.');
118
+ return new Uint8Array(readFileSync(output));
119
+ }
120
+ finally {
121
+ rmSync(dir, { recursive: true, force: true });
122
+ }
123
+ }
124
+ }
125
+ _a = NativeEncoder;
126
+ /**
127
+ * The encoder for `choice`, and one line saying which and why: `auto` takes a native basisu 1.60
128
+ * if one is found, else WASM; `native` fails without one; `wasm` never looks.
129
+ */
130
+ export async function selectEncoder(choice, env = process.env) {
131
+ const wasm = await WasmEncoder.create();
132
+ if (choice === 'wasm')
133
+ return { encoder: wasm, wasm, line: 'encoder: wasm (asked for)' };
134
+ const probe = await probeNative(env);
135
+ if (probe.path && probe.version)
136
+ return { encoder: new NativeEncoder(probe.path, probe.version), wasm, line: `encoder: native (${probe.reason})` };
137
+ if (choice === 'native') {
138
+ throw Object.assign(new YaliError('NO_NATIVE_ENCODER', `--encoder=native, but ${probe.reason}.`, `Set YALI_BASISU to a basisu ${PINNED} executable, or use --encoder=auto.`), { path: 'encoder' });
139
+ }
140
+ return { encoder: wasm, wasm, line: `encoder: wasm (${probe.reason})` };
141
+ }
142
+ //# sourceMappingURL=native-encoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-encoder.js","sourceRoot":"","sources":["../src/native-encoder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /** What every command prints with `--json`: exactly one JSON document on stdout. */
2
+ export type Envelope = {
3
+ ok: true;
4
+ command: string;
5
+ result: unknown;
6
+ } | {
7
+ ok: false;
8
+ command: string;
9
+ error: {
10
+ code: string;
11
+ message: string;
12
+ fix: string;
13
+ path?: string;
14
+ };
15
+ };
16
+ /** A command's outcome before printing. `exitCode` defaults to 0 when ok, 1 when not. */
17
+ export interface Outcome {
18
+ readonly envelope: Envelope;
19
+ /** Human-readable lines for the non-JSON mode. */
20
+ readonly text: readonly string[];
21
+ readonly exitCode?: number;
22
+ }
23
+ /** A successful outcome. */
24
+ export declare function ok(command: string, result: unknown, text: readonly string[], exitCode?: number): Outcome;
25
+ /** Turns any thrown value into a failed outcome with a code, a message and a fix. */
26
+ export declare function failure(command: string, err: unknown): Outcome;
27
+ /** Prints an outcome and returns its exit code. */
28
+ export declare function print(o: Outcome, json: boolean, write?: (s: string) => void): number;
package/dist/output.js ADDED
@@ -0,0 +1,27 @@
1
+ import { YaliError } from '@yali-engine/core';
2
+ /** A successful outcome. */
3
+ export function ok(command, result, text, exitCode = 0) {
4
+ return { envelope: { ok: true, command, result }, text, exitCode };
5
+ }
6
+ /** Turns any thrown value into a failed outcome with a code, a message and a fix. */
7
+ export function failure(command, err) {
8
+ const e = err instanceof YaliError
9
+ ? err
10
+ : new YaliError('UNEXPECTED', err instanceof Error ? err.message : String(err), 'This is a bug in the CLI or a tool it ran; the message says which.');
11
+ const path = e.path;
12
+ return {
13
+ envelope: { ok: false, command, error: { code: e.code, message: e.detail, fix: e.fix, ...(path ? { path } : {}) } },
14
+ text: [`error ${e.code}: ${e.detail}`, `fix: ${e.fix}`],
15
+ exitCode: 1,
16
+ };
17
+ }
18
+ /** Prints an outcome and returns its exit code. */
19
+ export function print(o, json, write = (s) => process.stdout.write(s)) {
20
+ if (json)
21
+ write(`${JSON.stringify(o.envelope)}\n`);
22
+ else
23
+ for (const line of o.text)
24
+ write(`${line}\n`);
25
+ return o.exitCode ?? (o.envelope.ok ? 0 : 1);
26
+ }
27
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAe9C,4BAA4B;AAC5B,MAAM,UAAU,EAAE,CAAC,OAAe,EAAE,MAAe,EAAE,IAAuB,EAAE,QAAQ,GAAG,CAAC;IACxF,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACrE,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,OAAO,CAAC,OAAe,EAAE,GAAY;IACnD,MAAM,CAAC,GACL,GAAG,YAAY,SAAS;QACtB,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,IAAI,SAAS,CAAC,YAAY,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,oEAAoE,CAAC,CAAC;IAC1J,MAAM,IAAI,GAAI,CAAmC,CAAC,IAAI,CAAC;IACvD,OAAO;QACL,QAAQ,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACnH,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC;QACvD,QAAQ,EAAE,CAAC;KACZ,CAAC;AACJ,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,KAAK,CAAC,CAAU,EAAE,IAAa,EAAE,QAA6B,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1G,IAAI,IAAI;QAAE,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;;QAC9C,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,IAAI;YAAE,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IACnD,OAAO,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC"}
package/dist/png.d.ts ADDED
@@ -0,0 +1,26 @@
1
+ /** Decoded pixels: RGBA8, rows top to bottom. */
2
+ export interface Image {
3
+ readonly width: number;
4
+ readonly height: number;
5
+ readonly rgba: Uint8Array;
6
+ }
7
+ /** Encodes RGBA8 as a PNG (no filters, zlib level 9). */
8
+ export declare function encodePng(img: Image): Buffer;
9
+ /** Decodes an 8-bit RGBA or RGB, non-interlaced PNG (all five row filters). */
10
+ export declare function decodePng(file: Uint8Array): Image;
11
+ /** The result of comparing two images pixel by pixel. */
12
+ export interface PixelDiff {
13
+ /** Pixels where some channel differs by more than `threshold`. */
14
+ readonly differing: number;
15
+ readonly fraction: number;
16
+ /** Largest channel difference anywhere. */
17
+ readonly maxDelta: number;
18
+ /** A visualisation: the expected image dimmed, differing pixels in red. */
19
+ readonly image: Image;
20
+ }
21
+ /**
22
+ * Compares decoded pixels (never file bytes). A pixel differs when any of R, G, B or A
23
+ * differs by more than `threshold` (default 8 of 255), which absorbs driver-level
24
+ * rounding while catching real changes.
25
+ */
26
+ export declare function diffPixels(expected: Image, actual: Image, threshold?: number): PixelDiff;
package/dist/png.js ADDED
@@ -0,0 +1,134 @@
1
+ import { deflateSync, inflateSync } from 'node:zlib';
2
+ import { YaliError } from '@yali-engine/core';
3
+ const SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
4
+ const CRC_TABLE = new Int32Array(256).map((_, n) => {
5
+ let c = n;
6
+ for (let k = 0; k < 8; k++)
7
+ c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
8
+ return c;
9
+ });
10
+ const crc32 = (buf) => {
11
+ let c = -1;
12
+ for (let i = 0; i < buf.length; i++)
13
+ c = CRC_TABLE[(c ^ buf[i]) & 0xff] ^ (c >>> 8);
14
+ return (c ^ -1) >>> 0;
15
+ };
16
+ function chunk(type, data) {
17
+ const head = Buffer.alloc(8);
18
+ head.writeUInt32BE(data.length, 0);
19
+ head.write(type, 4, 'ascii');
20
+ const crc = Buffer.alloc(4);
21
+ crc.writeUInt32BE(crc32(Buffer.concat([head.subarray(4), data])), 0);
22
+ return Buffer.concat([head, data, crc]);
23
+ }
24
+ /** Encodes RGBA8 as a PNG (no filters, zlib level 9). */
25
+ export function encodePng(img) {
26
+ const ihdr = Buffer.alloc(13);
27
+ ihdr.writeUInt32BE(img.width, 0);
28
+ ihdr.writeUInt32BE(img.height, 4);
29
+ ihdr.set([8, 6, 0, 0, 0], 8); // 8-bit, RGBA, deflate, adaptive filtering, no interlace
30
+ const stride = img.width * 4;
31
+ const raw = Buffer.alloc((stride + 1) * img.height);
32
+ for (let y = 0; y < img.height; y++)
33
+ raw.set(img.rgba.subarray(y * stride, (y + 1) * stride), y * (stride + 1) + 1);
34
+ return Buffer.concat([SIGNATURE, chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw, { level: 9 })), chunk('IEND', new Uint8Array())]);
35
+ }
36
+ /** Decodes an 8-bit RGBA or RGB, non-interlaced PNG (all five row filters). */
37
+ export function decodePng(file) {
38
+ const buf = Buffer.from(file.buffer, file.byteOffset, file.byteLength);
39
+ if (!buf.subarray(0, 8).equals(SIGNATURE))
40
+ throw new YaliError('PNG_INVALID', 'Not a PNG file.', 'Pass a .png file.');
41
+ let width = 0;
42
+ let height = 0;
43
+ let channels = 0;
44
+ const idat = [];
45
+ for (let at = 8; at < buf.length;) {
46
+ const len = buf.readUInt32BE(at);
47
+ const type = buf.toString('ascii', at + 4, at + 8);
48
+ const data = buf.subarray(at + 8, at + 8 + len);
49
+ if (type === 'IHDR') {
50
+ width = data.readUInt32BE(0);
51
+ height = data.readUInt32BE(4);
52
+ const depth = data[8];
53
+ const color = data[9];
54
+ if (depth !== 8 || (color !== 6 && color !== 2) || data[12] !== 0) {
55
+ throw new YaliError('PNG_UNSUPPORTED', `PNG with bit depth ${depth}, colour type ${color}, interlace ${data[12]}.`, 'Use 8-bit RGB or RGBA without interlacing.');
56
+ }
57
+ channels = color === 6 ? 4 : 3;
58
+ }
59
+ else if (type === 'IDAT')
60
+ idat.push(data);
61
+ else if (type === 'IEND')
62
+ break;
63
+ at += 12 + len;
64
+ }
65
+ const raw = inflateSync(Buffer.concat(idat));
66
+ const stride = width * channels;
67
+ const out = new Uint8Array(width * height * 4);
68
+ const prev = new Uint8Array(stride);
69
+ const cur = new Uint8Array(stride);
70
+ for (let y = 0; y < height; y++) {
71
+ const filter = raw[y * (stride + 1)];
72
+ const line = raw.subarray(y * (stride + 1) + 1, (y + 1) * (stride + 1));
73
+ for (let i = 0; i < stride; i++) {
74
+ const a = i >= channels ? cur[i - channels] : 0;
75
+ const b = prev[i];
76
+ const c = i >= channels ? prev[i - channels] : 0;
77
+ let pred = 0;
78
+ if (filter === 1)
79
+ pred = a;
80
+ else if (filter === 2)
81
+ pred = b;
82
+ else if (filter === 3)
83
+ pred = (a + b) >> 1;
84
+ else if (filter === 4) {
85
+ const p = a + b - c;
86
+ const pa = Math.abs(p - a);
87
+ const pb = Math.abs(p - b);
88
+ const pc = Math.abs(p - c);
89
+ pred = pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
90
+ }
91
+ cur[i] = (line[i] + pred) & 0xff;
92
+ }
93
+ for (let x = 0; x < width; x++) {
94
+ const o = (y * width + x) * 4;
95
+ out[o] = cur[x * channels];
96
+ out[o + 1] = cur[x * channels + 1];
97
+ out[o + 2] = cur[x * channels + 2];
98
+ out[o + 3] = channels === 4 ? cur[x * channels + 3] : 255;
99
+ }
100
+ prev.set(cur);
101
+ }
102
+ return { width, height, rgba: out };
103
+ }
104
+ /**
105
+ * Compares decoded pixels (never file bytes). A pixel differs when any of R, G, B or A
106
+ * differs by more than `threshold` (default 8 of 255), which absorbs driver-level
107
+ * rounding while catching real changes.
108
+ */
109
+ export function diffPixels(expected, actual, threshold = 8) {
110
+ if (expected.width !== actual.width || expected.height !== actual.height) {
111
+ throw new YaliError('IMAGE_SIZE', `Expected ${expected.width}×${expected.height}, got ${actual.width}×${actual.height}.`, 'Capture at the golden size (or update the golden if the size changed on purpose).');
112
+ }
113
+ const n = expected.width * expected.height;
114
+ const vis = new Uint8Array(n * 4);
115
+ let differing = 0;
116
+ let maxDelta = 0;
117
+ for (let p = 0; p < n; p++) {
118
+ const o = p * 4;
119
+ let d = 0;
120
+ for (let c = 0; c < 4; c++)
121
+ d = Math.max(d, Math.abs(expected.rgba[o + c] - actual.rgba[o + c]));
122
+ maxDelta = Math.max(maxDelta, d);
123
+ if (d > threshold) {
124
+ differing++;
125
+ vis.set([255, 0, 0, 255], o);
126
+ }
127
+ else {
128
+ const g = (expected.rgba[o] + expected.rgba[o + 1] + expected.rgba[o + 2]) / 9;
129
+ vis.set([g, g, g, 255], o);
130
+ }
131
+ }
132
+ return { differing, fraction: differing / n, maxDelta, image: { width: expected.width, height: expected.height, rgba: vis } };
133
+ }
134
+ //# sourceMappingURL=png.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"png.js","sourceRoot":"","sources":["../src/png.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAS9C,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;AACjE,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;IACjD,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;QAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACzE,OAAO,CAAC,CAAC;AACX,CAAC,CAAC,CAAC;AACH,MAAM,KAAK,GAAG,CAAC,GAAe,EAAE,EAAE;IAChC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACpF,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC,CAAC;AAEF,SAAS,KAAK,CAAC,IAAY,EAAE,IAAgB;IAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5B,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,SAAS,CAAC,GAAU;IAClC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC9B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACjC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,yDAAyD;IACvF,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpH,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;AACzI,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,SAAS,CAAC,IAAgB;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACvE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,aAAa,EAAE,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;IACtH,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,GAAI,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;QACnD,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;QAChD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACpB,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC7B,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClE,MAAM,IAAI,SAAS,CAAC,iBAAiB,EAAE,sBAAsB,KAAK,iBAAiB,KAAK,eAAe,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,4CAA4C,CAAC,CAAC;YACpK,CAAC;YACD,QAAQ,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,IAAI,KAAK,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACvC,IAAI,IAAI,KAAK,MAAM;YAAE,MAAM;QAChC,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;IACjB,CAAC;IACD,MAAM,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAChC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QACxE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,MAAM,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACjD,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,IAAI,MAAM,KAAK,CAAC;gBAAE,IAAI,GAAG,CAAC,CAAC;iBACtB,IAAI,MAAM,KAAK,CAAC;gBAAE,IAAI,GAAG,CAAC,CAAC;iBAC3B,IAAI,MAAM,KAAK,CAAC;gBAAE,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;iBACtC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACpB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3B,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACrD,CAAC;YACD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;QACnC,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YAC9B,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC;YAC3B,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC;YACnC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC;YACnC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC5D,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AACtC,CAAC;AAaD;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,QAAe,EAAE,MAAa,EAAE,SAAS,GAAG,CAAC;IACtE,IAAI,QAAQ,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,IAAI,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QACzE,MAAM,IAAI,SAAS,CACjB,YAAY,EACZ,YAAY,QAAQ,CAAC,KAAK,IAAI,QAAQ,CAAC,MAAM,SAAS,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,GAAG,EACtF,mFAAmF,CACpF,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;YAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACjG,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG,SAAS,EAAE,CAAC;YAClB,SAAS,EAAE,CAAC;YACZ,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/E,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;AAChI,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { validateConfig } from '@yali-engine/vite';
2
+ /** A project directory with its validated `yali.config`. */
3
+ export interface Project {
4
+ readonly root: string;
5
+ readonly config: ReturnType<typeof validateConfig>;
6
+ readonly configFile: string | null;
7
+ /** The project's `vite.config.*`, or null (the CLI then uses `yali()` alone). */
8
+ readonly viteConfig: string | null;
9
+ }
10
+ /** Loads the project at `root`. Throws `NO_PROJECT` when there is no `package.json`. */
11
+ export declare function loadProject(rootArg: string | undefined): Promise<Project>;
12
+ /** The page the CLI measures: `--url`, else the configured dev port. */
13
+ export declare function pageUrl(project: Project, url: string | undefined, query: string | undefined): string;
14
+ /** The CDP port: `--cdp-port`, `$CDP_PORT`, else 9224. */
15
+ export declare function cdpPort(arg: string | undefined): number;
@@ -0,0 +1,35 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { YaliError } from '@yali-engine/core';
4
+ import { loadConfig, validateConfig } from '@yali-engine/vite';
5
+ /** Loads the project at `root`. Throws `NO_PROJECT` when there is no `package.json`. */
6
+ export async function loadProject(rootArg) {
7
+ const root = resolve(rootArg ?? process.cwd());
8
+ if (!existsSync(join(root, 'package.json'))) {
9
+ throw new YaliError('NO_PROJECT', `${root} has no package.json.`, 'Run the command in a project directory, pass --root, or create one with `yali create <dir>`.');
10
+ }
11
+ const { config, file } = await loadConfig(root);
12
+ const viteConfig = ['vite.config.ts', 'vite.config.mts', 'vite.config.js', 'vite.config.mjs'].map((f) => join(root, f)).find((f) => existsSync(f)) ?? null;
13
+ return { root, config: validateConfig(config), configFile: file, viteConfig };
14
+ }
15
+ /** The page the CLI measures: `--url`, else the configured dev port. */
16
+ export function pageUrl(project, url, query) {
17
+ let base = url;
18
+ if (!base) {
19
+ if (project.config.port === undefined) {
20
+ throw new YaliError('NO_URL', 'No --url given and yali.config has no port.', 'Pass --url http://localhost:<port>/ or set `port` in yali.config.ts.');
21
+ }
22
+ base = `http://localhost:${project.config.port}/`;
23
+ }
24
+ if (!query)
25
+ return base;
26
+ return `${base}${base.includes('?') ? '&' : '?'}${query}`;
27
+ }
28
+ /** The CDP port: `--cdp-port`, `$CDP_PORT`, else 9224. */
29
+ export function cdpPort(arg) {
30
+ const n = Number(arg ?? process.env.CDP_PORT ?? 9224);
31
+ if (!Number.isInteger(n) || n <= 0)
32
+ throw new YaliError('BAD_OPTION', `--cdp-port must be a port number, got ${arg}.`, 'Pass the port Chrome listens on, e.g. --cdp-port 9224.');
33
+ return n;
34
+ }
35
+ //# sourceMappingURL=project.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAW/D,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA2B;IAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,SAAS,CAAC,YAAY,EAAE,GAAG,IAAI,uBAAuB,EAAE,8FAA8F,CAAC,CAAC;IACpK,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,CAAC,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAC3J,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAChF,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,OAAO,CAAC,OAAgB,EAAE,GAAuB,EAAE,KAAyB;IAC1F,IAAI,IAAI,GAAG,GAAG,CAAC;IACf,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACtC,MAAM,IAAI,SAAS,CAAC,QAAQ,EAAE,6CAA6C,EAAE,sEAAsE,CAAC,CAAC;QACvJ,CAAC;QACD,IAAI,GAAG,oBAAoB,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IACpD,CAAC;IACD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,EAAE,CAAC;AAC5D,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,OAAO,CAAC,GAAuB;IAC7C,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,YAAY,EAAE,yCAAyC,GAAG,GAAG,EAAE,wDAAwD,CAAC,CAAC;IACjL,OAAO,CAAC,CAAC;AACX,CAAC"}
package/dist/size.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ import type { Budgets } from './budgets.ts';
2
+ /** One shipped file. */
3
+ export interface SizedFile {
4
+ readonly path: string;
5
+ readonly bytes: number;
6
+ readonly brotliKb: number;
7
+ }
8
+ /** A build's size against the budgets (DESIGN.md §21). */
9
+ export interface SizeReport {
10
+ readonly files: readonly SizedFile[];
11
+ /**
12
+ * Everything a single-threaded page can load (the threads WASM excluded): every non-JS
13
+ * runtime file, and the JS the page's scripts reach through imports (static or dynamic).
14
+ */
15
+ readonly totalKb: number;
16
+ /** Everything a threaded page loads (the single-threaded WASM excluded). */
17
+ readonly threadedTotalKb: number;
18
+ /** The largest core WASM. */
19
+ readonly wasmKb: number;
20
+ /** Content the app loads (models, textures, data): reported, not part of the runtime budget. */
21
+ readonly contentKb: number;
22
+ /**
23
+ * JS only workers load (reached from no page script by an import; a worker starts from a
24
+ * `new URL(...)`), and WASM only such JS names (a decode worker's transcoder, ADR-0061):
25
+ * reported, not part of the page's budget (ADR-0051).
26
+ */
27
+ readonly workerKb: number;
28
+ /** The files counted in `workerKb`. */
29
+ readonly workerFiles: readonly string[];
30
+ /**
31
+ * Capability fallbacks (ADR-0106): chunks only some devices load, each against its own budget,
32
+ * left out of the totals (a device that needs one pays it on top).
33
+ */
34
+ readonly fallbacks: readonly {
35
+ readonly name: string;
36
+ readonly files: readonly string[];
37
+ readonly kb: number;
38
+ readonly budgetKb: number;
39
+ }[];
40
+ readonly budgetKb: number;
41
+ /** The threaded page's budget (ADR-0106). */
42
+ readonly threadedBudgetKb: number;
43
+ readonly wasmBudgetKb: number;
44
+ readonly withinBudget: boolean;
45
+ }
46
+ /** Brotli sizes of every file under `dir` (source maps excluded), checked against the budgets. */
47
+ export declare function sizeReport(dir: string, budgets: Budgets): SizeReport;
package/dist/size.js ADDED
@@ -0,0 +1,100 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
+ import { join, posix, relative, sep } from 'node:path';
3
+ import { brotliCompressSync, constants } from 'node:zlib';
4
+ const brotliKb = (buf) => brotliCompressSync(buf, { params: { [constants.BROTLI_PARAM_QUALITY]: 11 } }).length / 1024;
5
+ /** Brotli sizes of every file under `dir` (source maps excluded), checked against the budgets. */
6
+ export function sizeReport(dir, budgets) {
7
+ const files = [];
8
+ const walk = (d) => {
9
+ for (const e of readdirSync(d)) {
10
+ const p = join(d, e);
11
+ if (statSync(p).isDirectory())
12
+ walk(p);
13
+ else if (!p.endsWith('.map')) {
14
+ const buf = readFileSync(p);
15
+ files.push({ path: relative(dir, p).split(sep).join('/'), bytes: buf.length, brotliKb: Math.round(brotliKb(buf) * 10) / 10 });
16
+ }
17
+ }
18
+ };
19
+ walk(dir);
20
+ files.sort((a, b) => b.brotliKb - a.brotliKb);
21
+ // The budget is the runtime (DESIGN.md §21: core + render + core WASM); a scene's models and
22
+ // textures are content, reported separately (ADR-0047).
23
+ const isContent = (f) => /\.(glb|gltf|bin|ktx2|png|jpe?g|webp|spz|ply)$/i.test(f.path);
24
+ const contentKb = Math.round(files.filter(isContent).reduce((a, f) => a + f.brotliKb, 0) * 10) / 10;
25
+ const pageJs = pageScripts(dir);
26
+ // Without an index.html there is no page to start from: every JS file counts.
27
+ const isWorkerJs = (f) => pageJs !== null && /\.m?js$/.test(f.path) && !pageJs.has(f.path);
28
+ // A WASM file a worker-only script names and no page script does; one nothing names stays counted.
29
+ const text = (fs) => fs.map((f) => readFileSync(join(dir, f.path), 'latin1')).join(' ');
30
+ const workerText = pageJs ? text(files.filter(isWorkerJs)) : '';
31
+ const pageText = pageJs ? text(files.filter((f) => pageJs.has(f.path))) : '';
32
+ const isWorkerWasm = (f) => {
33
+ const name = posix.basename(f.path);
34
+ return pageJs !== null && f.path.endsWith('.wasm') && workerText.includes(name) && !pageText.includes(name);
35
+ };
36
+ const isWorkerOnly = (f) => isWorkerJs(f) || isWorkerWasm(f);
37
+ const workerFiles = files.filter((f) => !isContent(f) && isWorkerOnly(f));
38
+ const workerKb = Math.round(workerFiles.reduce((a, f) => a + f.brotliKb, 0) * 10) / 10;
39
+ // Capability fallbacks: by file name, apart from the totals (ADR-0106).
40
+ const fallbackOf = (f) => Object.keys(budgets.size_brotli_kb.fallback_chunks ?? {}).find((k) => /\.m?js$/.test(f.path) && posix.basename(f.path).startsWith(k));
41
+ const fallbacks = Object.entries(budgets.size_brotli_kb.fallback_chunks ?? {}).map(([name, budgetKb]) => {
42
+ const fs = files.filter((f) => !isContent(f) && !isWorkerOnly(f) && fallbackOf(f) === name);
43
+ return { name, files: fs.map((f) => f.path), kb: Math.round(fs.reduce((a, f) => a + f.brotliKb, 0) * 10) / 10, budgetKb };
44
+ });
45
+ const runtime = files.filter((f) => !isContent(f) && !isWorkerOnly(f) && fallbackOf(f) === undefined);
46
+ const isWasm = (f) => f.path.endsWith('.wasm');
47
+ const isThreads = (f) => isWasm(f) && /\.threads\b/.test(f.path);
48
+ const sum = (fs) => Math.round(fs.reduce((a, f) => a + f.brotliKb, 0) * 10) / 10;
49
+ const totalKb = sum(runtime.filter((f) => !isThreads(f)));
50
+ // A threaded page swaps the core WASM for the threads build; without one it is the same page.
51
+ const threadedTotalKb = runtime.some(isThreads) ? sum(runtime.filter((f) => !isWasm(f) || isThreads(f))) : totalKb;
52
+ const wasmKb = Math.max(0, ...runtime.filter(isWasm).map((f) => f.brotliKb));
53
+ const budgetKb = budgets.size_brotli_kb.core_runtime_total;
54
+ const threadedBudgetKb = budgets.size_brotli_kb.core_runtime_threaded ?? budgetKb;
55
+ const wasmBudgetKb = budgets.size_brotli_kb.core_wasm;
56
+ return {
57
+ files,
58
+ totalKb,
59
+ threadedTotalKb,
60
+ wasmKb,
61
+ contentKb,
62
+ workerKb,
63
+ workerFiles: workerFiles.map((f) => f.path),
64
+ fallbacks,
65
+ budgetKb,
66
+ threadedBudgetKb,
67
+ wasmBudgetKb,
68
+ withinBudget: totalKb <= budgetKb && threadedTotalKb <= threadedBudgetKb && wasmKb <= wasmBudgetKb && fallbacks.every((f) => f.kb <= f.budgetKb),
69
+ };
70
+ }
71
+ /**
72
+ * The JS files a page can load, relative to `dir`: the scripts and module preloads of
73
+ * `index.html`, then every file reached from them by `import ... from`, `import "..."` or
74
+ * `import("...")`. Null when there is no `index.html`.
75
+ */
76
+ function pageScripts(dir) {
77
+ const html = join(dir, 'index.html');
78
+ if (!existsSync(html))
79
+ return null;
80
+ const toPath = (from, spec) => (spec.startsWith('/') ? spec.slice(1) : posix.normalize(posix.join(posix.dirname(from), spec)));
81
+ const seen = new Set();
82
+ const queue = [];
83
+ const visit = (p) => {
84
+ if (!seen.has(p) && /\.m?js$/.test(p) && existsSync(join(dir, p))) {
85
+ seen.add(p);
86
+ queue.push(p);
87
+ }
88
+ };
89
+ const page = readFileSync(html, 'utf8');
90
+ for (const m of page.matchAll(/<(?:script|link)\b[^>]*?\b(?:src|href)=["']([^"']+)["']/g))
91
+ visit(toPath('index.html', m[1]));
92
+ while (queue.length) {
93
+ const p = queue.pop();
94
+ const js = readFileSync(join(dir, p), 'utf8');
95
+ for (const m of js.matchAll(/(?:\bfrom\s*|\bimport\s*\(?\s*)["'`](\.{0,2}\/[^"'`]+)["'`]/g))
96
+ visit(toPath(p, m[1]));
97
+ }
98
+ return seen;
99
+ }
100
+ //# sourceMappingURL=size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"size.js","sourceRoot":"","sources":["../src/size.ts"],"names":[],"mappings":"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"}