@juxtapose/core 0.2.0 → 0.5.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 (141) hide show
  1. package/dist/agent/handoff.d.ts +8 -1
  2. package/dist/agent/handoff.js +75 -7
  3. package/dist/agent/handoff.js.map +1 -1
  4. package/dist/analysis/affected.d.ts +58 -0
  5. package/dist/analysis/affected.js +134 -22
  6. package/dist/analysis/affected.js.map +1 -1
  7. package/dist/analysis/cause.d.ts +32 -0
  8. package/dist/analysis/cause.js +187 -0
  9. package/dist/analysis/cause.js.map +1 -0
  10. package/dist/baseline/approve.d.ts +24 -0
  11. package/dist/baseline/approve.js +54 -0
  12. package/dist/baseline/approve.js.map +1 -0
  13. package/dist/baseline/git-store.d.ts +4 -0
  14. package/dist/baseline/git-store.js +22 -0
  15. package/dist/baseline/git-store.js.map +1 -1
  16. package/dist/browser/acquire.d.ts +11 -0
  17. package/dist/browser/acquire.js +15 -0
  18. package/dist/browser/acquire.js.map +1 -0
  19. package/dist/browser/browser.d.ts +53 -0
  20. package/dist/browser/browser.js +122 -0
  21. package/dist/browser/browser.js.map +1 -0
  22. package/dist/browser/cdp.d.ts +81 -0
  23. package/dist/browser/cdp.js +230 -0
  24. package/dist/browser/cdp.js.map +1 -0
  25. package/dist/browser/index.d.ts +9 -0
  26. package/dist/browser/index.js +10 -0
  27. package/dist/browser/index.js.map +1 -0
  28. package/dist/browser/install.d.ts +33 -0
  29. package/dist/browser/install.js +167 -0
  30. package/dist/browser/install.js.map +1 -0
  31. package/dist/browser/launch.d.ts +37 -0
  32. package/dist/browser/launch.js +265 -0
  33. package/dist/browser/launch.js.map +1 -0
  34. package/dist/browser/page.d.ts +125 -0
  35. package/dist/browser/page.js +564 -0
  36. package/dist/browser/page.js.map +1 -0
  37. package/dist/browser/pins.d.ts +12 -0
  38. package/dist/browser/pins.js +57 -0
  39. package/dist/browser/pins.js.map +1 -0
  40. package/dist/browser/resolve.d.ts +25 -0
  41. package/dist/browser/resolve.js +115 -0
  42. package/dist/browser/resolve.js.map +1 -0
  43. package/dist/browser/selectors.d.ts +10 -0
  44. package/dist/browser/selectors.js +60 -0
  45. package/dist/browser/selectors.js.map +1 -0
  46. package/dist/browser/transport.d.ts +39 -0
  47. package/dist/browser/transport.js +99 -0
  48. package/dist/browser/transport.js.map +1 -0
  49. package/dist/browser/zip.d.ts +27 -0
  50. package/dist/browser/zip.js +241 -0
  51. package/dist/browser/zip.js.map +1 -0
  52. package/dist/capture/capture.d.ts +7 -1
  53. package/dist/capture/capture.js +27 -3
  54. package/dist/capture/capture.js.map +1 -1
  55. package/dist/capture/env.d.ts +8 -0
  56. package/dist/capture/env.js +38 -0
  57. package/dist/capture/env.js.map +1 -0
  58. package/dist/capture/launch.d.ts +19 -1
  59. package/dist/capture/launch.js +91 -1
  60. package/dist/capture/launch.js.map +1 -1
  61. package/dist/capture/pool.d.ts +1 -1
  62. package/dist/capture/pool.js +41 -8
  63. package/dist/capture/pool.js.map +1 -1
  64. package/dist/capture/settle.d.ts +1 -1
  65. package/dist/capture/settle.js +2 -0
  66. package/dist/capture/settle.js.map +1 -1
  67. package/dist/capture/snap.d.ts +43 -0
  68. package/dist/capture/snap.js +103 -0
  69. package/dist/capture/snap.js.map +1 -0
  70. package/dist/capture/structure.d.ts +77 -0
  71. package/dist/capture/structure.js +498 -0
  72. package/dist/capture/structure.js.map +1 -0
  73. package/dist/diff/compare.d.ts +19 -0
  74. package/dist/diff/compare.js +19 -0
  75. package/dist/diff/compare.js.map +1 -0
  76. package/dist/diff/crops.d.ts +26 -0
  77. package/dist/diff/crops.js +92 -0
  78. package/dist/diff/crops.js.map +1 -0
  79. package/dist/diff/structural/classify.d.ts +29 -0
  80. package/dist/diff/structural/classify.js +113 -0
  81. package/dist/diff/structural/classify.js.map +1 -0
  82. package/dist/diff/structural/coverage.d.ts +11 -0
  83. package/dist/diff/structural/coverage.js +37 -0
  84. package/dist/diff/structural/coverage.js.map +1 -0
  85. package/dist/diff/structural/deltas.d.ts +29 -0
  86. package/dist/diff/structural/deltas.js +218 -0
  87. package/dist/diff/structural/deltas.js.map +1 -0
  88. package/dist/diff/structural/disambiguate.d.ts +3 -0
  89. package/dist/diff/structural/disambiguate.js +89 -0
  90. package/dist/diff/structural/disambiguate.js.map +1 -0
  91. package/dist/diff/structural/label.d.ts +15 -0
  92. package/dist/diff/structural/label.js +64 -0
  93. package/dist/diff/structural/label.js.map +1 -0
  94. package/dist/diff/structural/match.d.ts +13 -0
  95. package/dist/diff/structural/match.js +135 -0
  96. package/dist/diff/structural/match.js.map +1 -0
  97. package/dist/diff/structural/shift.d.ts +2 -0
  98. package/dist/diff/structural/shift.js +144 -0
  99. package/dist/diff/structural/shift.js.map +1 -0
  100. package/dist/diff/structural/summary.d.ts +31 -0
  101. package/dist/diff/structural/summary.js +420 -0
  102. package/dist/diff/structural/summary.js.map +1 -0
  103. package/dist/diff/structural.d.ts +40 -0
  104. package/dist/diff/structural.js +219 -0
  105. package/dist/diff/structural.js.map +1 -0
  106. package/dist/index.d.ts +34 -12
  107. package/dist/index.js +55 -11
  108. package/dist/index.js.map +1 -1
  109. package/dist/platform.d.ts +1 -1
  110. package/dist/platforms/urls.d.ts +3 -0
  111. package/dist/platforms/urls.js +89 -0
  112. package/dist/platforms/urls.js.map +1 -0
  113. package/dist/report/comment.d.ts +2 -1
  114. package/dist/report/comment.js +305 -57
  115. package/dist/report/comment.js.map +1 -1
  116. package/dist/report/format.d.ts +20 -0
  117. package/dist/report/format.js +32 -0
  118. package/dist/report/format.js.map +1 -0
  119. package/dist/report/github.d.ts +12 -0
  120. package/dist/report/github.js +21 -0
  121. package/dist/report/github.js.map +1 -1
  122. package/dist/report/html.d.ts +12 -0
  123. package/dist/report/html.js +202 -0
  124. package/dist/report/html.js.map +1 -0
  125. package/dist/run/groups.d.ts +2 -0
  126. package/dist/run/groups.js +45 -0
  127. package/dist/run/groups.js.map +1 -0
  128. package/dist/run/phases.d.ts +19 -0
  129. package/dist/run/phases.js +50 -0
  130. package/dist/run/phases.js.map +1 -0
  131. package/dist/run/run.d.ts +10 -2
  132. package/dist/run/run.js +97 -9
  133. package/dist/run/run.js.map +1 -1
  134. package/dist/shell-quote.d.ts +2 -0
  135. package/dist/shell-quote.js +10 -0
  136. package/dist/shell-quote.js.map +1 -0
  137. package/dist/types.d.ts +234 -3
  138. package/dist/url-platform.d.ts +2 -2
  139. package/dist/url-platform.js +2 -2
  140. package/dist/url-platform.js.map +1 -1
  141. package/package.json +14 -10
@@ -0,0 +1,219 @@
1
+ // Structural diff → tier-0 verdict. Pure, no browser, no I/O.
2
+ //
3
+ // Composition: match → facts → shifts → unique labels → coverage → classify → summary, then a
4
+ // signature over the abstracted facts. Every list is sorted by a documented
5
+ // key and the result is built as one literal, so the same inputs always
6
+ // serialize to the same bytes (the CLI promises that in its JSON contract).
7
+ import { createHash } from 'node:crypto';
8
+ import { classify, computeFlags } from './structural/classify.js';
9
+ import { computeCoverage } from './structural/coverage.js';
10
+ import { disambiguate } from './structural/disambiguate.js';
11
+ import { compareCodeUnits, computeFacts } from './structural/deltas.js';
12
+ import { matchElements } from './structural/match.js';
13
+ import { synthesizeShifts } from './structural/shift.js';
14
+ import { buildChange, buildSummary } from './structural/summary.js';
15
+ export const MAX_DELTAS = 20;
16
+ /** JSON.stringify(verdict).length never exceeds this. */
17
+ export const MAX_VERDICT = 1500;
18
+ /** `cause.files` is capped here as well as in the index: the budget is a
19
+ * guarantee for any input. */
20
+ export const MAX_CAUSE_FILES = 5;
21
+ /** Longest `cause.files[].path`. The tail is kept: the file name is what a
22
+ * reader greps for; a 10-deep monorepo prefix is not. */
23
+ export const MAX_CAUSE_PATH = 80;
24
+ /** The verdict plus every unexplained region: the verdict's own list is the
25
+ * prefix that fits the budget, and crops must order and label from the whole. */
26
+ export function structuralDiffWithCoverage(input) {
27
+ const { baseline, current, pixels, imagesIdentical } = input;
28
+ const comparable = baseline !== undefined &&
29
+ current !== undefined &&
30
+ !pixels.dimensionMismatch &&
31
+ baseline.viewport.width === current.viewport.width &&
32
+ baseline.viewport.height === current.viewport.height;
33
+ // With a truncated side, the elements past the cap exist but were not
34
+ // recorded; reporting them as removed would be a lie the summary already
35
+ // tells more honestly as a count.
36
+ // Labels are made unique last, so only elements still named after the
37
+ // shift collapse — the ones a reader will see — are renamed.
38
+ const facts = comparable
39
+ ? disambiguate(synthesizeShifts(computeFacts(baseline, current, matchElements(baseline, current)).filter((f) => !(f.delta.kind === 'removed' && current.truncated) &&
40
+ !(f.delta.kind === 'added' && baseline.truncated))), baseline, current)
41
+ : [];
42
+ const cov = computeCoverage(pixels.changedRegions, facts);
43
+ const ctx = {
44
+ baseline,
45
+ current,
46
+ facts,
47
+ explained: cov.explained,
48
+ unexplainedRegions: cov.unexplainedRegions,
49
+ pixels: {
50
+ imagesIdentical,
51
+ dimensionMismatch: pixels.dimensionMismatch,
52
+ hasRegions: cov.regionCount > 0,
53
+ subThreshold: !imagesIdentical && !pixels.dimensionMismatch && cov.regionCount === 0,
54
+ },
55
+ };
56
+ const { classification, styleTags } = classify(ctx);
57
+ const flags = computeFlags(ctx);
58
+ const deltas = facts.slice(0, MAX_DELTAS).map((f) => f.delta);
59
+ const summary = buildSummary({
60
+ classification,
61
+ styleTags,
62
+ deltas,
63
+ deltaCount: facts.length,
64
+ regionCount: cov.regionCount,
65
+ unexplainedRegions: cov.unexplainedRegions,
66
+ flags,
67
+ imagesIdentical,
68
+ baseline,
69
+ current,
70
+ });
71
+ // The same clauses with `el` swapped for role|tag, so it reads the same on
72
+ // every page that made the change. Absent when there is nothing to state.
73
+ const change = facts.length > 0
74
+ ? buildChange({
75
+ classification,
76
+ styleTags,
77
+ deltas: facts
78
+ .slice(0, MAX_DELTAS)
79
+ .map((f) => f.delta.kind === 'shift'
80
+ ? { ...f.delta, after: f.abstract }
81
+ : { ...f.delta, el: f.abstract }),
82
+ deltaCount: facts.length,
83
+ regionCount: 0,
84
+ unexplainedRegions: [],
85
+ flags,
86
+ imagesIdentical,
87
+ })
88
+ : undefined;
89
+ const sig = signature(classification, facts);
90
+ // Fitted here, not trusted from the caller: the budget is a guarantee for
91
+ // any input, and the index's cap is a courtesy.
92
+ const cause = input.cause && fitCause(input.cause);
93
+ // One literal, one key order: the CLI's byte-stability rests on it.
94
+ const build = (ds, regions) => ({
95
+ schema: 'juxtapose.verdict/v1',
96
+ classification,
97
+ styleTags,
98
+ summary,
99
+ ...(change ? { change } : {}),
100
+ deltas: ds,
101
+ deltaCount: facts.length,
102
+ coverage: cov.coverage,
103
+ explained: cov.explained,
104
+ unexplainedRegions: regions,
105
+ flags,
106
+ ...(cause ? { cause } : {}),
107
+ signature: sig,
108
+ escalate: { command: input.escalate.command },
109
+ });
110
+ // The tier-0 budget is a guarantee, not a corpus statistic. Labels (60)
111
+ // and values (200) are capped, but one delta is still 70–400 chars, so 20
112
+ // of them (or a diff with hundreds of regions) would pass 1,500: one button
113
+ // with 10 realistic style changes measured 1,701. Each list keeps the
114
+ // prefix that fits — deltas first, they are the "what" — while deltaCount,
115
+ // coverage and explained still describe the whole. Holds for any input
116
+ // except an escalate command that alone exceeds the budget.
117
+ let room = MAX_VERDICT - JSON.stringify(build([], [])).length;
118
+ const fit = (items) => {
119
+ const out = [];
120
+ for (const x of items) {
121
+ const n = JSON.stringify(x).length + (out.length > 0 ? 1 : 0);
122
+ if (n > room)
123
+ break;
124
+ room -= n;
125
+ out.push(x);
126
+ }
127
+ return out;
128
+ };
129
+ const kept = fit(deltas);
130
+ return {
131
+ verdict: build(kept, fit(cov.unexplainedRegions)),
132
+ unexplainedRegions: cov.unexplainedRegions,
133
+ elementRegions: elementRegions(facts),
134
+ };
135
+ }
136
+ /** Facts are grouped by element identity (`source`), not label: two
137
+ * `div.card`s are two boxes. A shift carries its anchor's source but its
138
+ * run's rects, so it is a box of its own rather than swelling the anchor's.
139
+ * Zero-area unions carry no pixels and are dropped. */
140
+ function elementRegions(facts) {
141
+ const boxes = new Map();
142
+ for (const f of facts) {
143
+ const key = `${f.delta.kind === 'shift' ? 'shift:' : ''}${f.source.side}:${f.source.index}`;
144
+ for (const r of f.rects) {
145
+ const b = boxes.get(key);
146
+ const x0 = Math.floor(r.x);
147
+ const y0 = Math.floor(r.y);
148
+ const x1 = Math.ceil(r.x + r.w);
149
+ const y1 = Math.ceil(r.y + r.h);
150
+ boxes.set(key, b
151
+ ? {
152
+ x0: Math.min(b.x0, x0),
153
+ y0: Math.min(b.y0, y0),
154
+ x1: Math.max(b.x1, x1),
155
+ y1: Math.max(b.y1, y1),
156
+ }
157
+ : { x0, y0, x1, y1 });
158
+ }
159
+ }
160
+ return [...boxes.values()]
161
+ .map((b) => ({ x: b.x0, y: b.y0, width: b.x1 - b.x0, height: b.y1 - b.y0 }))
162
+ .filter((r) => r.width > 0 && r.height > 0);
163
+ }
164
+ /** The verdict alone; what every caller but `--crops` wants. */
165
+ export function structuralDiff(input) {
166
+ return structuralDiffWithCoverage(input).verdict;
167
+ }
168
+ /** sha256 of the classification, then the facts with each element reduced
169
+ * to role|tag and the values kept: "every button background
170
+ * #2563eb→#1d4ed8" hashes the same on any page that made that change. The
171
+ * classification leads so that verdicts with no facts — identical, a
172
+ * canvas redraw, a size change — do not all share one signature. Facts are
173
+ * sorted (by code unit) so order of discovery cannot leak in. */
174
+ function signature(classification, facts) {
175
+ const items = facts
176
+ .map((f) => {
177
+ const d = f.delta;
178
+ const rest = d.kind === 'text' || d.kind === 'style' || d.kind === 'attr'
179
+ ? [
180
+ d.kind === 'style' ? d.prop : d.kind === 'attr' ? d.attr : 'text',
181
+ d.from,
182
+ d.to,
183
+ ]
184
+ : d.kind === 'moved'
185
+ ? ['moved', d.dx, d.dy]
186
+ : d.kind === 'resized'
187
+ ? ['resized', d.dw, d.dh]
188
+ : d.kind === 'shift'
189
+ ? ['shift', d.dx, d.dy, d.count]
190
+ : [d.kind];
191
+ return JSON.stringify([f.abstract, ...rest]);
192
+ })
193
+ .sort(compareCodeUnits);
194
+ return createHash('sha256')
195
+ .update([classification, ...items].join('\n'))
196
+ .digest('hex');
197
+ }
198
+ function fitCause(c) {
199
+ return {
200
+ kind: c.kind,
201
+ files: c.files.slice(0, MAX_CAUSE_FILES).map((f) => ({
202
+ path: clipHead(f.path, MAX_CAUSE_PATH),
203
+ distance: f.distance,
204
+ })),
205
+ ...(c.partial ? { partial: true } : {}),
206
+ };
207
+ }
208
+ /** `…` plus at most the last `max - 1` code units, never starting on the
209
+ * low half of a surrogate pair: a lone surrogate is not valid UTF-8. */
210
+ function clipHead(s, max) {
211
+ if (s.length <= max)
212
+ return s;
213
+ let from = s.length - (max - 1);
214
+ const c = s.charCodeAt(from);
215
+ if (c >= 0xdc00 && c <= 0xdfff)
216
+ from++;
217
+ return '…' + s.slice(from);
218
+ }
219
+ //# sourceMappingURL=structural.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structural.js","sourceRoot":"","sources":["../../src/diff/structural.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,17 +1,39 @@
1
- export type { StoryParams, Viewport, StoryManifest, Snapshot, ChangedRegion, DiffStatus, DiffResult, RunReport, ApprovalMeta, BaselineEntry, BaselineManifest, ProjectConfig, StorageConfig, BaselineStore, } from './types.js';
1
+ export type { StoryParams, Viewport, StoryManifest, Snapshot, StructureSnapshot, StructureElement, StructureRect, CaptureEnv, ChangedRegion, Delta, Classification, StyleTag, Flag, StructuralVerdict, Cause, CauseKind, DiffStatus, DiffResult, RunReport, RunGroup, ApprovalMeta, BaselineEntry, BaselineManifest, ProjectConfig, UrlEntry, StorageConfig, BaselineStore, } from './types.js';
2
2
  export type { Platform, EnumerateTarget, SettleContext } from './platform.js';
3
3
  export { navigateToStory } from './url-platform.js';
4
- export { hashImage, resolveStatus, toDiffStatus, type BaselineStatus, type AncestryCheck, } from './baseline/status.js';
4
+ export { createUrlPlatform } from './platforms/urls.js';
5
+ export { waitForSelector } from './capture/settle.js';
5
6
  export { GitBaselineStore, encodeStoryId } from './baseline/git-store.js';
7
+ export { hashImage } from './baseline/status.js';
8
+ export type { BaselineStatus, AncestryCheck } from './baseline/status.js';
6
9
  export { createAncestryCheck, alwaysAncestor } from './git/ancestry.js';
7
- export { armSettle, drain, waitForSelector, waitForPaint, FREEZE_ANIMATIONS_CSS, DOM_IDLE_MS, DOM_IDLE_TIMEOUT_MS, } from './capture/settle.js';
8
- export { captureStory, type CaptureDeps } from './capture/capture.js';
9
- export { deterministicLaunchOptions, DETERMINISTIC_CHROMIUM_ARGS, type LaunchOptions, } from './capture/launch.js';
10
- export { capturePool, DEFAULT_CONCURRENCY, type PoolOptions, type CaptureOutcome, } from './capture/pool.js';
11
- export { diffImages, resolveThreshold, resolveMinDiffPercent, type DiffOutput, } from './diff/diff.js';
12
- export { computeChangedRegions } from './diff/regions.js';
13
- export { resolveAffected, type AffectedOptions, type AffectedResult, } from './analysis/affected.js';
10
+ export { encodeStructure, decodeStructure } from './capture/structure.js';
11
+ export { approveCapture, readRunStructure, type RunStructure, } from './baseline/approve.js';
12
+ export { structuralDiff, structuralDiffWithCoverage, type StructuralDiffInput, } from './diff/structural.js';
13
+ export { compareSnaps, type SnapSide, type SnapComparison } from './diff/compare.js';
14
+ export { renderCrops, MAX_CROPS, MAX_CROP_SIDE, type Crop } from './diff/crops.js';
15
+ export { describeDelta, describeDeltas } from './diff/structural/summary.js';
16
+ export { compareCodeUnits } from './diff/structural/deltas.js';
17
+ export { shellQuote } from './shell-quote.js';
14
18
  export { runCapture, DEFAULT_VIEWPORT, type RunOptions, type RunArtifact, } from './run/run.js';
15
- export { buildHandoffPrompt, describeRegion, type HandoffPayload, } from './agent/handoff.js';
16
- export { buildComment, COMMENT_MARKER, type CommentOptions } from './report/comment.js';
17
- export { resolveGitHubContext, postComment, type GitHubContext, } from './report/github.js';
19
+ export { computeGroups } from './run/groups.js';
20
+ export { phases, formatPhases, type Phases } from './run/phases.js';
21
+ export { snapPage, snapPages, type SnapOptions, type SnapResult, type SnapPagesOptions, type SnapPageResult, } from './capture/snap.js';
22
+ export { deterministicLaunchOptions, type LaunchOptions } from './capture/launch.js';
23
+ export { chromeArgs, PARITY_CHROMIUM_ARGS } from './capture/launch.js';
24
+ export type { Browser, BrowserContext } from './browser/browser.js';
25
+ export { BrowserDiedError } from './browser/browser.js';
26
+ export { TargetClosedError } from './browser/cdp.js';
27
+ export { PageCrashedError, TimeoutError, type Page } from './browser/page.js';
28
+ export { acquireBrowser, type AcquireOptions } from './browser/acquire.js';
29
+ export { resolveChrome, juxCacheDir, noBrowserMessage, NO_BROWSER_MESSAGE, type ChromeSource, type ResolvedChrome, } from './browser/resolve.js';
30
+ export { installChrome, proxyReexecArgs, stderrProgress, type InstallOptions, type InstallResult, } from './browser/install.js';
31
+ export { CHROME_VERSION, CHROME_PINS, chromePlatform } from './browser/pins.js';
32
+ export { assertCssSelector } from './browser/selectors.js';
33
+ export { resolveAffected, resolveAffectedFromPaths, gitIsCommit, type AffectedOptions, type AffectedResult, } from './analysis/affected.js';
34
+ export { buildCauseIndex, prepareCause, type CauseIndex, type CauseSources, type ChangedFile, } from './analysis/cause.js';
35
+ export { buildHandoffPrompt, describeCause, type HandoffPayload, } from './agent/handoff.js';
36
+ export { buildComment, type CommentOptions } from './report/comment.js';
37
+ export { buildHtmlReport, type HtmlReportOptions } from './report/html.js';
38
+ export { formatPercent } from './report/format.js';
39
+ export { resolveArtifactUrl, resolveGitHubContext, postComment, type GitHubContext, } from './report/github.js';
package/dist/index.js CHANGED
@@ -1,16 +1,60 @@
1
+ // The public surface of @juxtapose/core.
2
+ //
3
+ // Everything here is a semver commitment once this publishes, so it is chosen
4
+ // rather than accumulated. The settle pipeline, the diff internals and the
5
+ // worker pool are deliberately NOT exported: CLAUDE.md describes them as
6
+ // load-bearing and easy to break, and exporting them invites downstream code
7
+ // to pin exactly the parts that most need freedom to change.
8
+ //
9
+ // If you are writing a third-party adapter or baseline store, the types and
10
+ // helpers below are what you need. If something is missing for that, the seam
11
+ // needs widening — say so rather than reaching into the package.
1
12
  export { navigateToStory } from './url-platform.js';
2
- export { hashImage, resolveStatus, toDiffStatus, } from './baseline/status.js';
13
+ export { createUrlPlatform } from './platforms/urls.js';
14
+ export { waitForSelector } from './capture/settle.js';
15
+ // Implementing a baseline store. `encodeStoryId` matters for anyone building
16
+ // keys from a story id — they are platform-supplied and not path-safe.
3
17
  export { GitBaselineStore, encodeStoryId } from './baseline/git-store.js';
18
+ // hashImage is exported because anything comparing against a manifest entry
19
+ // must use the same function the manifest was written with, not its own copy.
20
+ export { hashImage } from './baseline/status.js';
4
21
  export { createAncestryCheck, alwaysAncestor } from './git/ancestry.js';
5
- export { armSettle, drain, waitForSelector, waitForPaint, FREEZE_ANIMATIONS_CSS, DOM_IDLE_MS, DOM_IDLE_TIMEOUT_MS, } from './capture/settle.js';
6
- export { captureStory } from './capture/capture.js';
7
- export { deterministicLaunchOptions, DETERMINISTIC_CHROMIUM_ARGS, } from './capture/launch.js';
8
- export { capturePool, DEFAULT_CONCURRENCY, } from './capture/pool.js';
9
- export { diffImages, resolveThreshold, resolveMinDiffPercent, } from './diff/diff.js';
10
- export { computeChangedRegions } from './diff/regions.js';
11
- export { resolveAffected, } from './analysis/affected.js';
22
+ // Structure sidecars: read and write the gzipped snapshot beside an image.
23
+ export { encodeStructure, decodeStructure } from './capture/structure.js';
24
+ // Approving a run capture with its sidecar: every approval path (CLI approve,
25
+ // init, the review server) goes through this so they cannot disagree.
26
+ export { approveCapture, readRunStructure, } from './baseline/approve.js';
27
+ // Structural diff: two sidecars + a pixel diff → the tier-0 verdict.
28
+ export { structuralDiff, structuralDiffWithCoverage, } from './diff/structural.js';
29
+ export { compareSnaps } from './diff/compare.js';
30
+ // Tier-3 crops: two images + regions → side-by-side PNG strips, no browser.
31
+ export { renderCrops, MAX_CROPS, MAX_CROP_SIDE } from './diff/crops.js';
32
+ export { describeDelta, describeDeltas } from './diff/structural/summary.js';
33
+ export { compareCodeUnits } from './diff/structural/deltas.js';
34
+ export { shellQuote } from './shell-quote.js';
35
+ // Driving a run.
12
36
  export { runCapture, DEFAULT_VIEWPORT, } from './run/run.js';
13
- export { buildHandoffPrompt, describeRegion, } from './agent/handoff.js';
14
- export { buildComment, COMMENT_MARKER } from './report/comment.js';
15
- export { resolveGitHubContext, postComment, } from './report/github.js';
37
+ export { computeGroups } from './run/groups.js';
38
+ // Per-phase timings, so a slow run can say which phase was slow.
39
+ export { phases, formatPhases } from './run/phases.js';
40
+ export { snapPage, snapPages, } from './capture/snap.js';
41
+ export { deterministicLaunchOptions } from './capture/launch.js';
42
+ export { chromeArgs, PARITY_CHROMIUM_ARGS } from './capture/launch.js';
43
+ export { BrowserDiedError } from './browser/browser.js';
44
+ export { TargetClosedError } from './browser/cdp.js';
45
+ export { PageCrashedError, TimeoutError } from './browser/page.js';
46
+ export { acquireBrowser } from './browser/acquire.js';
47
+ export { resolveChrome, juxCacheDir, noBrowserMessage, NO_BROWSER_MESSAGE, } from './browser/resolve.js';
48
+ export { installChrome, proxyReexecArgs, stderrProgress, } from './browser/install.js';
49
+ export { CHROME_VERSION, CHROME_PINS, chromePlatform } from './browser/pins.js';
50
+ export { assertCssSelector } from './browser/selectors.js';
51
+ export { resolveAffected, resolveAffectedFromPaths, gitIsCommit, } from './analysis/affected.js';
52
+ // Cause: the story's import closure intersected with the changed files.
53
+ export { buildCauseIndex, prepareCause, } from './analysis/cause.js';
54
+ // Reporting on one.
55
+ export { buildHandoffPrompt, describeCause, } from './agent/handoff.js';
56
+ export { buildComment } from './report/comment.js';
57
+ export { buildHtmlReport } from './report/html.js';
58
+ export { formatPercent } from './report/format.js';
59
+ export { resolveArtifactUrl, resolveGitHubContext, postComment, } from './report/github.js';
16
60
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EACL,SAAS,EACT,aAAa,EACb,YAAY,GAGb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExE,OAAO,EACL,SAAS,EACT,KAAK,EACL,eAAe,EACf,YAAY,EACZ,qBAAqB,EACrB,WAAW,EACX,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAoB,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,GAE5B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,EACX,mBAAmB,GAGpB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EACL,eAAe,GAGhB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,UAAU,EACV,gBAAgB,GAGjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,kBAAkB,EAClB,cAAc,GAEf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,cAAc,EAAuB,MAAM,qBAAqB,CAAC;AACxF,OAAO,EACL,oBAAoB,EACpB,WAAW,GAEZ,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Page } from 'playwright';
1
+ import type { Page } from './browser/page.js';
2
2
  import type { StoryManifest, StoryParams } from './types.js';
3
3
  export interface EnumerateTarget {
4
4
  /** Built workbench directory, when capturing a static build. */
@@ -0,0 +1,3 @@
1
+ import type { Platform } from '../platform.js';
2
+ import type { UrlEntry } from '../types.js';
3
+ export declare function createUrlPlatform(urls: UrlEntry[]): Platform;
@@ -0,0 +1,89 @@
1
+ // The null platform: an explicit list of URLs, no workbench (issue #11).
2
+ //
3
+ // Everything a workbench adapter contributes — enumeration, a render signal,
4
+ // componentPath — is absent here, which is exactly what the Platform seam was
5
+ // designed to allow: settling falls entirely to core's generic pipeline (DOM
6
+ // quiescence, fonts, images, animation freeze, paint barrier), and affected
7
+ // analysis sees an empty componentPath and falls back to capturing everything.
8
+ //
9
+ // Lives in core rather than its own adapter package because it has no
10
+ // knowledge of any workbench and `jux snap` must always have it.
11
+ export function createUrlPlatform(urls) {
12
+ const notHttp = (u) => new Error(`jux: url '${u.url}' for id '${u.id}' must be http(s)`);
13
+ const isHttp = (url) => url.protocol === 'http:' || url.protocol === 'https:';
14
+ // Ids are baseline keys. A Map would silently keep the last duplicate and
15
+ // two pages would share one baseline, so reject them up front.
16
+ const byId = new Map();
17
+ for (const u of urls) {
18
+ if (!u.id)
19
+ throw new Error('jux: url entry has an empty id');
20
+ // Validate with the same parser navigation uses. A regex disagrees with it:
21
+ // WHATWG strips leading/trailing C0 controls and spaces and removes tabs and
22
+ // newlines, so ' file:///x' or 'fi\tle:///x' looks relative to a regex but
23
+ // navigates to file:. A file:/javascript:/data: url would let whoever
24
+ // controls the config (a fork PR in CI) publish local file contents in the
25
+ // image and structure sidecar. 'localhost:3000/x' (scheme "localhost:")
26
+ // also becomes a clear error instead of a confusing navigation failure.
27
+ let abs = null;
28
+ try {
29
+ abs = new URL(u.url);
30
+ }
31
+ catch {
32
+ // relative; checked again against the origin in resolve()
33
+ }
34
+ if (abs && !isHttp(abs))
35
+ throw notHttp(u);
36
+ if (byId.has(u.id))
37
+ throw new Error(`jux: duplicate url id '${u.id}'`);
38
+ byId.set(u.id, u);
39
+ }
40
+ const resolve = (id, baseUrl) => {
41
+ const entry = byId.get(id);
42
+ if (!entry)
43
+ throw new Error(`jux: no url with id '${id}'`);
44
+ let final;
45
+ try {
46
+ final = new URL(entry.url);
47
+ }
48
+ catch {
49
+ if (!/^https?:/i.test(baseUrl)) {
50
+ throw new Error(`jux: relative url '${entry.url}' needs --origin`);
51
+ }
52
+ final = new URL(entry.url, baseUrl);
53
+ }
54
+ // Navigate to exactly what was validated.
55
+ if (!isHttp(final))
56
+ throw notHttp(entry);
57
+ return final.href;
58
+ };
59
+ return {
60
+ name: 'urls',
61
+ async detect() {
62
+ return false;
63
+ },
64
+ async enumerate() {
65
+ return urls.map((u) => ({
66
+ storyId: u.id,
67
+ title: u.id,
68
+ name: u.id,
69
+ componentPath: '',
70
+ params: u.params ?? {},
71
+ }));
72
+ },
73
+ async showStory(page, storyId, baseUrl) {
74
+ await page.goto(resolve(storyId, baseUrl));
75
+ },
76
+ previewUrl() {
77
+ return 'about:blank';
78
+ },
79
+ storyUrl(baseUrl, storyId) {
80
+ return resolve(storyId, baseUrl);
81
+ },
82
+ async settle() {
83
+ // No platform render signal. Core's generic settle runs after this.
84
+ },
85
+ configDirs: [],
86
+ storyFileGlobs: [],
87
+ };
88
+ }
89
+ //# sourceMappingURL=urls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"urls.js","sourceRoot":"","sources":["../../src/platforms/urls.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,8 @@ export declare const COMMENT_MARKER = "<!-- juxtapose-bot -->";
10
10
  export interface CommentOptions {
11
11
  report: RunReport;
12
12
  /** Where to point someone who wants the images. Omitted when there is no
13
- * artifact to link to. */
13
+ * artifact to link to — a link to an empty artifacts list is worse than no
14
+ * link, so callers pass this only when they actually wrote a report. */
14
15
  artifactUrl?: string;
15
16
  }
16
17
  export declare function buildComment({ report, artifactUrl }: CommentOptions): string;