fidelity-kit 0.1.0 → 0.2.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 (88) hide show
  1. package/README.md +133 -5
  2. package/dist/commands/build.d.ts +2 -0
  3. package/dist/commands/build.d.ts.map +1 -1
  4. package/dist/commands/build.js +6 -2
  5. package/dist/commands/build.js.map +1 -1
  6. package/dist/commands/dev.d.ts +7 -1
  7. package/dist/commands/dev.d.ts.map +1 -1
  8. package/dist/commands/dev.js +9 -2
  9. package/dist/commands/dev.js.map +1 -1
  10. package/dist/commands/hash.d.ts +2 -0
  11. package/dist/commands/hash.d.ts.map +1 -1
  12. package/dist/commands/hash.js +7 -3
  13. package/dist/commands/hash.js.map +1 -1
  14. package/dist/commands/process.d.ts +6 -0
  15. package/dist/commands/process.d.ts.map +1 -1
  16. package/dist/commands/process.js +35 -7
  17. package/dist/commands/process.js.map +1 -1
  18. package/dist/commands/serve.d.ts +3 -1
  19. package/dist/commands/serve.d.ts.map +1 -1
  20. package/dist/commands/serve.js +2 -1
  21. package/dist/commands/serve.js.map +1 -1
  22. package/dist/core/compare.d.ts +4 -2
  23. package/dist/core/compare.d.ts.map +1 -1
  24. package/dist/core/compare.js +7 -5
  25. package/dist/core/compare.js.map +1 -1
  26. package/dist/core/hash.d.ts +2 -0
  27. package/dist/core/hash.d.ts.map +1 -1
  28. package/dist/core/hash.js +37 -10
  29. package/dist/core/hash.js.map +1 -1
  30. package/dist/core/hash.test.js +8 -1
  31. package/dist/core/hash.test.js.map +1 -1
  32. package/dist/core/process.d.ts +52 -3
  33. package/dist/core/process.d.ts.map +1 -1
  34. package/dist/core/process.js +450 -51
  35. package/dist/core/process.js.map +1 -1
  36. package/dist/core/process.test.d.ts +2 -0
  37. package/dist/core/process.test.d.ts.map +1 -0
  38. package/dist/core/process.test.js +269 -0
  39. package/dist/core/process.test.js.map +1 -0
  40. package/dist/core/progress.d.ts +8 -0
  41. package/dist/core/progress.d.ts.map +1 -0
  42. package/dist/core/progress.js +2 -0
  43. package/dist/core/progress.js.map +1 -0
  44. package/dist/core/scan.d.ts +8 -1
  45. package/dist/core/scan.d.ts.map +1 -1
  46. package/dist/core/scan.js +72 -7
  47. package/dist/core/scan.js.map +1 -1
  48. package/dist/core/schema.d.ts.map +1 -1
  49. package/dist/core/schema.js +8 -2
  50. package/dist/core/schema.js.map +1 -1
  51. package/dist/core/suite.test.js +61 -2
  52. package/dist/core/suite.test.js.map +1 -1
  53. package/dist/progress.d.ts +12 -0
  54. package/dist/progress.d.ts.map +1 -0
  55. package/dist/progress.js +46 -0
  56. package/dist/progress.js.map +1 -0
  57. package/dist/progress.test.d.ts +2 -0
  58. package/dist/progress.test.d.ts.map +1 -0
  59. package/dist/progress.test.js +36 -0
  60. package/dist/progress.test.js.map +1 -0
  61. package/dist/run.d.ts +4 -1
  62. package/dist/run.d.ts.map +1 -1
  63. package/dist/run.js +34 -6
  64. package/dist/run.js.map +1 -1
  65. package/dist/server.d.ts +1 -1
  66. package/dist/server.d.ts.map +1 -1
  67. package/dist/server.js +25 -2
  68. package/dist/server.js.map +1 -1
  69. package/dist/server.test.js +55 -2
  70. package/dist/server.test.js.map +1 -1
  71. package/dist/watch.d.ts +9 -0
  72. package/dist/watch.d.ts.map +1 -0
  73. package/dist/watch.js +96 -0
  74. package/dist/watch.js.map +1 -0
  75. package/dist/watch.test.d.ts +2 -0
  76. package/dist/watch.test.d.ts.map +1 -0
  77. package/dist/watch.test.js +138 -0
  78. package/dist/watch.test.js.map +1 -0
  79. package/package.json +11 -1
  80. package/viewer/assets/{ViewControls-hn4PfGV3.js → ViewControls-Ck9smy3A.js} +2 -2
  81. package/viewer/assets/_-Bn1LyOgm.js +1 -0
  82. package/viewer/assets/{index-D9G4C6Ns.js → index-BZCd8VsK.js} +2 -2
  83. package/viewer/assets/index-VKD_ZeZu.css +2 -0
  84. package/viewer/assets/{routes-DmeR6gGl.js → routes-sEPFkb5w.js} +1 -1
  85. package/viewer/index.html +2 -2
  86. package/dist/.tsbuildinfo +0 -1
  87. package/viewer/assets/_-BAkV94Fq.js +0 -1
  88. package/viewer/assets/index-D8YJNCs_.css +0 -2
package/README.md CHANGED
@@ -1,15 +1,143 @@
1
1
  # fidelity-kit
2
2
 
3
- Toolkit for renderer fidelity suites: point it at a results folder and get metrics, delta images and a viewer website.
3
+ [![NPM Package][npm]][npm-url]
4
+ [![NPM Downloads][npm-downloads]][npmtrends-url]
5
+ [![Tests][tests-badge]][tests-url]
6
+ [![Coverage][coverage-badge]][coverage-url]
7
+ [![Discord][discord-badge]][discord-url]
8
+
9
+ **Compare renderer output without building a comparison site.** fidelity-kit turns a folder of AVIF renders into image quality metrics, visual difference images, and a browsable website. Use it for rendering fidelity suites, visual regression reviews, and side-by-side comparisons of a renderer against one or more references.
10
+
11
+ Your renderer writes images; fidelity-kit handles the comparisons and viewer. Run the viewer locally while developing, serve the results from a container, or export a static site.
12
+
13
+ ## Quick start
14
+
15
+ If you already have a results folder in the format below:
16
+
17
+ ```sh
18
+ npx fidelity-kit dev results
19
+ ```
20
+
21
+ Open the URL printed by the command (usually <http://localhost:3000>). `dev` processes new or changed images before starting and serves files without browser caching, so a reload shows your latest render. See the [working example](https://github.com/bhouston/fidelity-kit/tree/main/examples/demo) for a complete suite.
22
+
23
+ ## Adopt it in your suite
24
+
25
+ Create a `results` directory with a `fidelity.json` file. Put each scene in its own folder, with one AVIF image per renderer under each output name:
26
+
27
+ ```text
28
+ results/
29
+ fidelity.json
30
+ README.md optional introduction shown in the viewer
31
+ materials/
32
+ brushed-metal/
33
+ README.md optional scene description
34
+ scene.json optional title and tags
35
+ beauty/
36
+ reference.avif
37
+ my-renderer.avif
38
+ ao/
39
+ reference.avif
40
+ my-renderer.avif
41
+ ```
42
+
43
+ Folders can have as many group levels as you need. A scene is any folder containing an image at `<output>/<renderer>.avif` for names declared in `fidelity.json`. Missing renderer images are allowed; the viewer marks them as missing. Other files, such as scene sources and textures, can live alongside the images.
44
+
45
+ Start with this configuration:
46
+
47
+ ```json
48
+ {
49
+ "title": "My Renderer Comparisons",
50
+ "renderers": [
51
+ { "id": "reference", "label": "Reference", "reference": true },
52
+ { "id": "my-renderer", "label": "My Renderer" }
53
+ ],
54
+ "outputs": [{ "id": "beauty" }, { "id": "ao", "label": "Ambient occlusion" }],
55
+ "delta": true
56
+ }
57
+ ```
58
+
59
+ Mark at least one renderer as a reference. You can declare multiple references; the viewer lets readers choose which one to compare against. Renderer and output IDs must use lowercase letters, numbers, `.`, `_`, or `-`, and start with a letter or number. `outputs` defaults to `beauty` if omitted. Set `delta` to `false` if you only want metrics and the original images.
60
+
61
+ For searchable scene labels, add a `scene.json` next to a scene's images:
62
+
63
+ ```json
64
+ { "title": "Brushed metal", "tags": ["metal", "roughness"] }
65
+ ```
66
+
67
+ ## Use it during development
68
+
69
+ Install the CLI in your suite repository, or use `npx fidelity-kit` for occasional runs:
4
70
 
5
71
  ```sh
6
- npx fidelity-kit process <results-dir> # metrics + delta images + index.json
7
- npx fidelity-kit serve <results-dir> # serve the viewer
8
- npx fidelity-kit build <results-dir> --out site/ # static export
72
+ pnpm add -D fidelity-kit
73
+ pnpm exec fidelity-kit dev results
9
74
  ```
10
75
 
11
- See the [repository](https://github.com/bhouston/fidelity-kit) for the folder contract and full documentation ([docs/ADOPTING.md](https://github.com/bhouston/fidelity-kit/blob/main/docs/ADOPTING.md)).
76
+ The CLI supports this workflow:
77
+
78
+ | Command | Use |
79
+ | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
80
+ | `fidelity-kit process results` | Generate comparison metrics, optional delta images, and the viewer index. Reruns update only stale comparisons; add `--force` to recompute all. |
81
+ | `fidelity-kit dev results` | Process stale comparisons, then open a local viewer with caching disabled. |
82
+ | `fidelity-kit serve results` | Process stale comparisons, then serve the viewer with browser and CDN caching. |
83
+ | `fidelity-kit build results --out site/` | Process stale comparisons and export a site for static hosting. |
84
+
85
+ `process` computes PSNR, RMSE, MAE, and maximum error for each available comparison. Reference and renderer images in a pair must have the same dimensions. Both `dev` and `serve` start at `localhost:3000` and try higher ports if one is occupied; use `--port` to require a specific port and `--host` to change the bind address. The actual viewer URL is printed at startup. To serve results already processed during a build, pass `--no-process`.
86
+
87
+ `process`, `dev`, and `hash` show scanning and processing progress on one updating terminal line, including the remaining count and an approximate ETA. Use `--quiet` to suppress progress and summary output; errors are still reported.
88
+
89
+ For example, add scripts to your suite's `package.json`:
90
+
91
+ ```json
92
+ {
93
+ "scripts": {
94
+ "fidelity:dev": "fidelity-kit dev results",
95
+ "fidelity:build": "fidelity-kit build results --out site/"
96
+ }
97
+ }
98
+ ```
99
+
100
+ You can deploy `site/` to any static host. The site includes the viewer and its data, so it does not need a Node server.
101
+
102
+ ## Serve it from Docker
103
+
104
+ Process and hash the results while building the image, then start the server without repeating that work:
105
+
106
+ ```dockerfile
107
+ FROM node:24-slim
108
+
109
+ RUN npm install -g fidelity-kit
110
+ COPY results /data
111
+ RUN fidelity-kit process /data && fidelity-kit hash /data
112
+
113
+ EXPOSE 8080
114
+ CMD ["fidelity-kit", "serve", "/data", "--host", "0.0.0.0", "--port", "8080", "--no-process"]
115
+ ```
116
+
117
+ ```sh
118
+ docker build -t my-fidelity-site .
119
+ docker run --rm -p 8080:8080 my-fidelity-site
120
+ ```
121
+
122
+ Open <http://localhost:8080>. `hash` prepares versioned image URLs so repeat visits can reuse cached images from the first request. If your results change at runtime, run `serve` without `--no-process` to refresh comparisons at startup; `hash` is optional in that case.
12
123
 
13
124
  ## Author and sponsor
14
125
 
15
126
  Created by [Ben Houston](https://github.com/bhouston) and sponsored by [Land of Assets](https://landofassets.com).
127
+
128
+ [npm]: https://img.shields.io/npm/v/fidelity-kit
129
+ [npm-url]: https://www.npmjs.com/package/fidelity-kit
130
+ [npm-downloads]: https://img.shields.io/npm/dw/fidelity-kit
131
+ [npmtrends-url]: https://www.npmtrends.com/fidelity-kit
132
+ [tests-badge]: https://github.com/bhouston/fidelity-kit/actions/workflows/ci.yml/badge.svg
133
+ [tests-url]: https://github.com/bhouston/fidelity-kit/actions/workflows/ci.yml
134
+ [coverage-badge]: https://codecov.io/gh/bhouston/fidelity-kit/branch/main/graph/badge.svg
135
+ [coverage-url]: https://codecov.io/gh/bhouston/fidelity-kit
136
+ [discord-badge]: https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white
137
+ [discord-url]: https://discord.gg/5J5Ur3F6Z2
138
+
139
+ ### Incremental processing
140
+
141
+ `process --watch` and `dev` retain per-scene dependencies, batch source changes, and share the configured `--concurrency` limit. Scenes and renderer/reference images can be added or removed while watching. Restart after changing renderer IDs, output/pass IDs, or other settings in `fidelity.json`.
142
+
143
+ Input freshness uses modification time and byte size recorded in metrics files. Older-dated replacements are detected; changes preserving both values require `--force`. Metrics without saved input signatures are regenerated once. Generated metrics, delta images, index files, and temporary writes are ignored by the watcher. Removed comparisons are dropped from the index; generated files may remain on disk.
@@ -2,6 +2,8 @@ export declare const command: import("yargs-file-commands").DefineCommandResult<
2
2
  root: string;
3
3
  } & {
4
4
  out: string;
5
+ } & {
6
+ concurrency: number | undefined;
5
7
  } & {
6
8
  process: boolean;
7
9
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,OAAO;;;;;;EAwBlB,CAAC"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,OAAO;;;;;;;;EA+BlB,CAAC"}
@@ -14,13 +14,17 @@ export const command = defineCommand({
14
14
  describe: 'Suite results directory (contains fidelity.json)',
15
15
  })
16
16
  .option('out', { type: 'string', demandOption: true, describe: 'Output directory' })
17
+ .option('concurrency', { type: 'number', describe: 'Image pairs compared in parallel (default: CPU count)' })
17
18
  .option('process', { type: 'boolean', default: true, describe: 'Refresh stale metrics/deltas first' }),
18
19
  handler: async (argv) => {
19
20
  assertViewerBuilt();
20
21
  const root = resolve(argv.root);
21
22
  const out = resolve(argv.out);
22
- if (argv.process)
23
- await processSuite(root);
23
+ if (argv.process) {
24
+ const result = await processSuite(root, { concurrency: argv.concurrency });
25
+ if (result.failed.length)
26
+ throw new Error(`Failed to compare ${result.failed.length} image pair(s): ${result.failed.map((f) => `${f.file}: ${f.error}`).join('; ')}`);
27
+ }
24
28
  await cp(viewerDir, out, { recursive: true });
25
29
  await cp(root, `${out}/data`, {
26
30
  recursive: true,
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAExE,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,cAAc;IACvB,QAAQ,EAAE,kEAAkE;IAC5E,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;SACnF,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,oCAAoC,EAAE,CAAC;IAC1G,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,iBAAiB,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,OAAO;YAAE,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,EAAE;YAC5B,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;SAChG,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;IAC7C,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAExE,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,cAAc;IACvB,QAAQ,EAAE,kEAAkE;IAC5E,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;SACnF,MAAM,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,uDAAuD,EAAE,CAAC;SAC5G,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,oCAAoC,EAAE,CAAC;IAC1G,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,iBAAiB,EAAE,CAAC;QACpB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YAC3E,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM;gBACtB,MAAM,IAAI,KAAK,CACb,qBAAqB,MAAM,CAAC,MAAM,CAAC,MAAM,mBAAmB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3H,CAAC;QACN,CAAC;QACD,MAAM,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,EAAE;YAC5B,SAAS,EAAE,IAAI;YACf,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;SAChG,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,EAAE,CAAC,CAAC;IAC7C,CAAC;CACF,CAAC,CAAC"}
@@ -1,10 +1,16 @@
1
1
  export declare const command: import("yargs-file-commands").DefineCommandResult<{}, {
2
2
  root: string;
3
3
  } & {
4
- port: number;
4
+ port: number | undefined;
5
5
  } & {
6
6
  host: string;
7
+ } & {
8
+ concurrency: number | undefined;
9
+ } & {
10
+ quiet: boolean;
7
11
  } & {
8
12
  process: boolean;
13
+ } & {
14
+ watch: boolean;
9
15
  }>;
10
16
  //# sourceMappingURL=dev.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO;;;;;;;;EAclB,CAAC"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO;;;;;;;;;;;;;;EAqBlB,CAAC"}
@@ -9,9 +9,16 @@ export const command = defineCommand({
9
9
  demandOption: true,
10
10
  describe: 'Suite results directory (contains fidelity.json)',
11
11
  })
12
- .option('port', { type: 'number', default: 3000, describe: 'Port to listen on' })
12
+ .option('port', { type: 'number', describe: 'Port to listen on (default: first open port from 3000)' })
13
13
  .option('host', { type: 'string', default: 'localhost', describe: 'Interface to bind (0.0.0.0 for all)' })
14
- .option('process', { type: 'boolean', default: true, describe: 'Refresh stale metrics/deltas before serving' }),
14
+ .option('concurrency', { type: 'number', describe: 'Image pairs compared in parallel (default: CPU count)' })
15
+ .option('quiet', { type: 'boolean', default: false, describe: 'Suppress progress and summary output' })
16
+ .option('process', { type: 'boolean', default: true, describe: 'Refresh stale metrics/deltas before serving' })
17
+ .option('watch', {
18
+ type: 'boolean',
19
+ default: true,
20
+ describe: 'Keep metrics, deltas, and index updated as results change',
21
+ }),
15
22
  handler: (argv) => run(argv, true),
16
23
  });
17
24
  //# sourceMappingURL=dev.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,kGAAkG;IAC5G,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAC;SAChF,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,qCAAqC,EAAE,CAAC;SACzG,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,6CAA6C,EAAE,CAAC;IACnH,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;CACnC,CAAC,CAAC"}
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,YAAY;IACrB,QAAQ,EAAE,kGAAkG;IAC5G,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,wDAAwD,EAAE,CAAC;SACtG,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,qCAAqC,EAAE,CAAC;SACzG,MAAM,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,uDAAuD,EAAE,CAAC;SAC5G,MAAM,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,sCAAsC,EAAE,CAAC;SACtG,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,6CAA6C,EAAE,CAAC;SAC9G,MAAM,CAAC,OAAO,EAAE;QACf,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,2DAA2D;KACtE,CAAC;IACN,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC;CACnC,CAAC,CAAC"}
@@ -2,5 +2,7 @@ export declare const command: import("yargs-file-commands").DefineCommandResult<
2
2
  root: string;
3
3
  } & {
4
4
  concurrency: number;
5
+ } & {
6
+ quiet: boolean;
5
7
  }>;
6
8
  //# sourceMappingURL=hash.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/commands/hash.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO;;;;EAmBlB,CAAC"}
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/commands/hash.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,OAAO;;;;;;EAuBlB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { availableParallelism } from 'node:os';
2
2
  import { defineCommand } from 'yargs-file-commands';
3
3
  import { hashSuite } from '../core/index.js';
4
+ import { createProgress } from '../progress.js';
4
5
  export const command = defineCommand({
5
6
  command: 'hash <root>',
6
7
  describe: 'Write image-hashes.json so `serve` can use immutable, content-hashed image URLs from the first request',
@@ -14,10 +15,13 @@ export const command = defineCommand({
14
15
  type: 'number',
15
16
  default: availableParallelism(),
16
17
  describe: 'Files hashed in parallel (default: os.availableParallelism())',
17
- }),
18
+ })
19
+ .option('quiet', { type: 'boolean', default: false, describe: 'Suppress progress and summary output' }),
18
20
  handler: async (argv) => {
19
- const r = await hashSuite(argv.root, { concurrency: argv.concurrency });
20
- console.log(`${r.hashed} hashed, ${r.reused} reused, ${r.total} total`);
21
+ const progress = createProgress('hash', argv.quiet);
22
+ const r = await hashSuite(argv.root, { concurrency: argv.concurrency, onProgress: progress.update }).finally(progress.finish);
23
+ if (!argv.quiet)
24
+ console.log(`${r.hashed} hashed, ${r.reused} reused, ${r.total} total`);
21
25
  },
22
26
  });
23
27
  //# sourceMappingURL=hash.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/commands/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,aAAa;IACtB,QAAQ,EAAE,wGAAwG;IAClH,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,aAAa,EAAE;QACrB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,oBAAoB,EAAE;QAC/B,QAAQ,EAAE,+DAA+D;KAC1E,CAAC;IACN,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACxE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC1E,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/commands/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,aAAa;IACtB,QAAQ,EAAE,wGAAwG;IAClH,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,aAAa,EAAE;QACrB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,oBAAoB,EAAE;QAC/B,QAAQ,EAAE,+DAA+D;KAC1E,CAAC;SACD,MAAM,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,sCAAsC,EAAE,CAAC;IAC3G,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACpD,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAC1G,QAAQ,CAAC,MAAM,CAChB,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,MAAM,YAAY,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC;IAC3F,CAAC;CACF,CAAC,CAAC"}
@@ -1,6 +1,12 @@
1
1
  export declare const command: import("yargs-file-commands").DefineCommandResult<{}, {
2
2
  root: string;
3
+ } & {
4
+ concurrency: number | undefined;
5
+ } & {
6
+ quiet: boolean;
3
7
  } & {
4
8
  force: boolean;
9
+ } & {
10
+ watch: boolean;
5
11
  }>;
6
12
  //# sourceMappingURL=process.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/commands/process.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO;;;;EAiBlB,CAAC"}
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/commands/process.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,OAAO;;;;;;;;;;EAkClB,CAAC"}
@@ -1,5 +1,15 @@
1
1
  import { processSuite } from '../core/index.js';
2
2
  import { defineCommand } from 'yargs-file-commands';
3
+ import { watchResults } from '../watch.js';
4
+ import { createProgress } from '../progress.js';
5
+ function report(r, quiet) {
6
+ if (!quiet)
7
+ console.log(`${r.computed} computed, ${r.skipped} up to date, ${r.failed.length} failed`);
8
+ for (const f of r.failed)
9
+ console.error(`FAILED ${f.file}: ${f.error}`);
10
+ if (r.failed.length)
11
+ process.exitCode = 1;
12
+ }
3
13
  export const command = defineCommand({
4
14
  command: 'process <root>',
5
15
  describe: 'Compute metrics + delta images for stale pairs and write index.json',
@@ -9,14 +19,32 @@ export const command = defineCommand({
9
19
  demandOption: true,
10
20
  describe: 'Suite results directory (contains fidelity.json)',
11
21
  })
12
- .option('force', { type: 'boolean', default: false, describe: 'Recompute even when up to date' }),
22
+ .option('concurrency', { type: 'number', describe: 'Image pairs compared in parallel (default: CPU count)' })
23
+ .option('quiet', { type: 'boolean', default: false, describe: 'Suppress progress and summary output' })
24
+ .option('force', { type: 'boolean', default: false, describe: 'Recompute even when up to date' })
25
+ .option('watch', {
26
+ type: 'boolean',
27
+ default: false,
28
+ describe: 'Keep metrics, deltas, and index updated as results change',
29
+ }),
13
30
  handler: async (argv) => {
14
- const r = await processSuite(argv.root, { force: argv.force, onCompute: (f) => console.log('wrote', f) });
15
- console.log(`${r.computed} computed, ${r.skipped} up to date, ${r.failed.length} failed`);
16
- for (const f of r.failed)
17
- console.error(`FAILED ${f.file}: ${f.error}`);
18
- if (r.failed.length)
19
- process.exitCode = 1;
31
+ const progress = createProgress('process', argv.quiet);
32
+ const options = {
33
+ force: argv.force,
34
+ concurrency: argv.concurrency,
35
+ onProgress: progress.update,
36
+ };
37
+ const onResult = (result) => {
38
+ progress.finish();
39
+ report(result, argv.quiet);
40
+ };
41
+ if (argv.watch) {
42
+ await watchResults(argv.root, onResult, options);
43
+ if (!argv.quiet)
44
+ console.log(`Watching ${argv.root}`);
45
+ }
46
+ else
47
+ onResult(await processSuite(argv.root, options).finally(progress.finish));
20
48
  },
21
49
  });
22
50
  //# sourceMappingURL=process.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/commands/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,gBAAgB;IACzB,QAAQ,EAAE,qEAAqE;IAC/E,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,gCAAgC,EAAE,CAAC;IACrG,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,MAAM,CAAC,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC1G,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;QAC1F,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;YAAE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;QACxE,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM;YAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC5C,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/commands/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsB,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,SAAS,MAAM,CAAC,CAAgB,EAAE,KAAc;IAC9C,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,MAAM,SAAS,CAAC,CAAC;IACtG,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACxE,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,gBAAgB;IACzB,QAAQ,EAAE,qEAAqE;IAC/E,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,uDAAuD,EAAE,CAAC;SAC5G,MAAM,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,sCAAsC,EAAE,CAAC;SACtG,MAAM,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,gCAAgC,EAAE,CAAC;SAChG,MAAM,CAAC,OAAO,EAAE;QACf,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,2DAA2D;KACtE,CAAC;IACN,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG;YACd,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,UAAU,EAAE,QAAQ,CAAC,MAAM;SAC5B,CAAC;QACF,MAAM,QAAQ,GAAG,CAAC,MAAqB,EAAE,EAAE;YACzC,QAAQ,CAAC,MAAM,EAAE,CAAC;YAClB,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC,CAAC;QACF,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjD,IAAI,CAAC,IAAI,CAAC,KAAK;gBAAE,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;;YAAM,QAAQ,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACnF,CAAC;CACF,CAAC,CAAC"}
@@ -1,13 +1,15 @@
1
1
  export declare const command: import("yargs-file-commands").DefineCommandResult<{}, {
2
2
  root: string;
3
3
  } & {
4
- port: number;
4
+ port: number | undefined;
5
5
  } & {
6
6
  host: string;
7
7
  } & {
8
8
  "max-age": number;
9
9
  } & {
10
10
  "stale-while-revalidate": number;
11
+ } & {
12
+ concurrency: number | undefined;
11
13
  } & {
12
14
  process: boolean;
13
15
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO;;;;;;;;;;;;EAwBlB,CAAC"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO;;;;;;;;;;;;;;EAyBlB,CAAC"}
@@ -10,7 +10,7 @@ export const command = defineCommand({
10
10
  demandOption: true,
11
11
  describe: 'Suite results directory (contains fidelity.json)',
12
12
  })
13
- .option('port', { type: 'number', default: 3000, describe: 'Port to listen on' })
13
+ .option('port', { type: 'number', describe: 'Port to listen on (default: first open port from 3000)' })
14
14
  .option('host', { type: 'string', default: 'localhost', describe: 'Interface to bind (0.0.0.0 for all)' })
15
15
  .option('max-age', {
16
16
  type: 'number',
@@ -22,6 +22,7 @@ export const command = defineCommand({
22
22
  default: defaultCachePolicy.staleWhileRevalidate,
23
23
  describe: 'Seconds a stale image may be served while it refreshes in the background',
24
24
  })
25
+ .option('concurrency', { type: 'number', describe: 'Image pairs compared in parallel (default: CPU count)' })
25
26
  .option('process', { type: 'boolean', default: true, describe: 'Refresh stale metrics/deltas before serving' }),
26
27
  handler: (argv) => run(argv, false),
27
28
  });
@@ -1 +1 @@
1
- {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,cAAc;IACvB,QAAQ,EAAE,gFAAgF;IAC1F,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,mBAAmB,EAAE,CAAC;SAChF,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,qCAAqC,EAAE,CAAC;SACzG,MAAM,CAAC,SAAS,EAAE;QACjB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,kBAAkB,CAAC,MAAM;QAClC,QAAQ,EAAE,oEAAoE;KAC/E,CAAC;SACD,MAAM,CAAC,wBAAwB,EAAE;QAChC,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,kBAAkB,CAAC,oBAAoB;QAChD,QAAQ,EAAE,0EAA0E;KACrF,CAAC;SACD,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,6CAA6C,EAAE,CAAC;IACnH,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC;CACpC,CAAC,CAAC"}
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC;IACnC,OAAO,EAAE,cAAc;IACvB,QAAQ,EAAE,gFAAgF;IAC1F,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CACjB,KAAK;SACF,UAAU,CAAC,MAAM,EAAE;QAClB,IAAI,EAAE,QAAQ;QACd,YAAY,EAAE,IAAI;QAClB,QAAQ,EAAE,kDAAkD;KAC7D,CAAC;SACD,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,wDAAwD,EAAE,CAAC;SACtG,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,qCAAqC,EAAE,CAAC;SACzG,MAAM,CAAC,SAAS,EAAE;QACjB,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,kBAAkB,CAAC,MAAM;QAClC,QAAQ,EAAE,oEAAoE;KAC/E,CAAC;SACD,MAAM,CAAC,wBAAwB,EAAE;QAChC,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,kBAAkB,CAAC,oBAAoB;QAChD,QAAQ,EAAE,0EAA0E;KACrF,CAAC;SACD,MAAM,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,uDAAuD,EAAE,CAAC;SAC5G,MAAM,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,6CAA6C,EAAE,CAAC;IACnH,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC;CACpC,CAAC,CAAC"}
@@ -22,8 +22,10 @@ export declare function compareRgb(reference: RawImage, test: RawImage): {
22
22
  metrics: ImageMetrics;
23
23
  delta: RawImage;
24
24
  };
25
- /** Compares two encoded images (paths or buffers); returns metrics and the delta encoded like the renders. */
26
- export declare function compareImages(reference: string | Buffer, test: string | Buffer): Promise<{
25
+ /** Compares two encoded images (paths or buffers); returns metrics and the delta encoded like the renders (an empty buffer when disabled). */
26
+ export declare function compareImages(reference: string | Buffer, test: string | Buffer, options?: {
27
+ delta?: boolean;
28
+ }): Promise<{
27
29
  metrics: ImageMetrics;
28
30
  width: number;
29
31
  height: number;
@@ -1 +1 @@
1
- {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/core/compare.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kIAAkI;AAClI,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,gJAAgJ;AAChJ,eAAO,MAAM,WAAW;aAAK,OAAO,EAAE,EAAE;aAAE,iBAAiB,EAAE,OAAO;CAAW,CAAC;AAEhF,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAOvE;AA0BD,2GAA2G;AAC3G,wBAAgB,UAAU,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,GAAG;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAmC1G;AAED,8GAA8G;AAC9G,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,IAAI,EAAE,MAAM,GAAG,MAAM,GACpB,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAOvF"}
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/core/compare.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kIAAkI;AAClI,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,gJAAgJ;AAChJ,eAAO,MAAM,WAAW;aAAK,OAAO,EAAE,EAAE;aAAE,iBAAiB,EAAE,OAAO;CAAW,CAAC;AAEhF,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAOvE;AA0BD,2GAA2G;AAC3G,wBAAgB,UAAU,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,GAAG;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAmC1G;AAED,8IAA8I;AAC9I,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAChC,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAUvF"}
@@ -69,13 +69,15 @@ export function compareRgb(reference, test) {
69
69
  delta: { data: delta, width: reference.width, height: reference.height },
70
70
  };
71
71
  }
72
- /** Compares two encoded images (paths or buffers); returns metrics and the delta encoded like the renders. */
73
- export async function compareImages(reference, test) {
72
+ /** Compares two encoded images (paths or buffers); returns metrics and the delta encoded like the renders (an empty buffer when disabled). */
73
+ export async function compareImages(reference, test, options = {}) {
74
74
  const [ref, tst] = await Promise.all([readRgb(reference), readRgb(test)]);
75
75
  const { metrics, delta } = compareRgb(ref, tst);
76
- const deltaImage = await sharp(delta.data, { raw: { width: delta.width, height: delta.height, channels: 3 } })
77
- .avif(RESULT_AVIF)
78
- .toBuffer();
76
+ const deltaImage = options.delta === false
77
+ ? Buffer.alloc(0)
78
+ : await sharp(delta.data, { raw: { width: delta.width, height: delta.height, channels: 3 } })
79
+ .avif(RESULT_AVIF)
80
+ .toBuffer();
79
81
  return { metrics, width: ref.width, height: ref.height, deltaImage };
80
82
  }
81
83
  //# sourceMappingURL=compare.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/core/compare.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/core/compare.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { ProgressCallback } from './progress.js';
1
2
  export declare const HASHES_FILE = "image-hashes.json";
2
3
  export interface HashEntry {
3
4
  hash: string;
@@ -16,6 +17,7 @@ export interface HashSuiteOptions {
16
17
  concurrency?: number;
17
18
  /** Injectable for tests. */
18
19
  hash?: (path: string) => Promise<string>;
20
+ onProgress?: ProgressCallback;
19
21
  }
20
22
  export interface HashSuiteResult {
21
23
  hashed: number;
@@ -1 +1 @@
1
- {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/core/hash.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5D;AAED,gHAAgH;AAChH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAMnF;AAED,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C;AACD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA4BnG"}
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/core/hash.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAOD;;;GAGG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5D;AAED,gHAAgH;AAChH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAMnF;AAED,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AACD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CAoDnG"}
package/dist/core/hash.js CHANGED
@@ -42,24 +42,51 @@ export async function hashSuite(root, opts = {}) {
42
42
  const { concurrency = availableParallelism(), hash = hashFile } = opts;
43
43
  const limit = pLimit(Math.max(1, Math.floor(concurrency)));
44
44
  const previous = await readHashFile(root);
45
- const rels = (await readdir(root, { recursive: true }))
46
- .map((p) => p.split('\\').join('/'))
47
- .filter((rel) => rel.endsWith('.avif') && isDataFile(rel))
48
- .toSorted();
45
+ const rels = [];
46
+ const pending = [''];
47
+ let scanned = 0;
48
+ const scanProgress = () => opts.onProgress?.({ phase: 'Scanning', completed: scanned, total: scanned + pending.length, unit: 'directories' });
49
+ scanProgress();
50
+ while (pending.length) {
51
+ const dir = pending.pop();
52
+ for (const entry of await readdir(join(root, dir), { withFileTypes: true })) {
53
+ const rel = dir ? `${dir}/${entry.name}` : entry.name;
54
+ if (entry.name.startsWith('.'))
55
+ continue;
56
+ if (entry.isDirectory())
57
+ pending.push(rel);
58
+ else if (entry.isFile() && rel.endsWith('.avif') && isDataFile(rel))
59
+ rels.push(rel);
60
+ }
61
+ scanned++;
62
+ scanProgress();
63
+ }
64
+ rels.sort();
49
65
  let reused = 0;
66
+ let completed = 0;
67
+ const hashProgress = () => opts.onProgress?.({ phase: 'Hashing', completed, total: rels.length, unit: 'images' });
68
+ hashProgress();
50
69
  const entries = await Promise.all(rels.map((rel) => limit(async () => {
51
- const s = await stat(join(root, rel));
52
- const old = previous[rel];
53
- if (old && old.size === s.size && old.mtimeMs === s.mtimeMs) {
54
- reused++;
55
- return [rel, old];
70
+ try {
71
+ const s = await stat(join(root, rel));
72
+ const old = previous[rel];
73
+ if (old && old.size === s.size && old.mtimeMs === s.mtimeMs) {
74
+ reused++;
75
+ return [rel, old];
76
+ }
77
+ return [rel, { hash: await hash(join(root, rel)), size: s.size, mtimeMs: s.mtimeMs }];
78
+ }
79
+ finally {
80
+ completed++;
81
+ hashProgress();
56
82
  }
57
- return [rel, { hash: await hash(join(root, rel)), size: s.size, mtimeMs: s.mtimeMs }];
58
83
  })));
59
84
  const out = join(root, HASHES_FILE);
85
+ opts.onProgress?.({ phase: 'Writing hashes', completed: 0, total: 1, unit: 'files' });
60
86
  const tmp = `${out}.${process.pid}.tmp`;
61
87
  await writeFile(tmp, `${JSON.stringify({ version: 1, files: Object.fromEntries(entries) }, null, 2)}\n`);
62
88
  await rename(tmp, out);
89
+ opts.onProgress?.({ phase: 'Writing hashes', completed: 1, total: 1, unit: 'files' });
63
90
  return { hashed: entries.length - reused, reused, total: entries.length };
64
91
  }
65
92
  //# sourceMappingURL=hash.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/core/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,MAAM,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAQ/C,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;CACnG,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACxB,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC;IACvB,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AACpD,CAAC;AAED,gHAAgH;AAChH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAcD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,IAAI,GAAqB,EAAE;IACvE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,MAAM,EAAE,WAAW,GAAG,oBAAoB,EAAE,EAAE,IAAI,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC;IACvE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;SACpD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SACnC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;SACzD,QAAQ,EAAE,CAAC;IACd,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACf,KAAK,CAAC,KAAK,IAAkC,EAAE;QAC7C,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5D,MAAM,EAAE,CAAC;YACT,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACxF,CAAC,CAAC,CACH,CACF,CAAC;IACF,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IACxC,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACzG,MAAM,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;AAC5E,CAAC"}
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/core/hash.ts"],"names":[],"mappings":"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"}