@weatherboard/annotation 0.3.0 → 0.6.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.
- package/ADOPTING.md +146 -11
- package/COMPATIBILITY.md +15 -0
- package/README.md +33 -8
- package/dist/anchor.cjs +1 -1
- package/dist/anchor.cjs.map +2 -2
- package/dist/anchor.mjs +1 -1
- package/dist/anchor.mjs.map +2 -2
- package/dist/arming.d.cts +1 -3
- package/dist/arming.d.mts +1 -3
- package/dist/arming.d.ts +1 -3
- package/dist/bootstrap-runtime.d.cts +30 -0
- package/dist/bootstrap-runtime.d.mts +30 -0
- package/dist/bootstrap-runtime.d.ts +30 -0
- package/dist/bootstrap.cjs +2 -0
- package/dist/bootstrap.cjs.map +7 -0
- package/dist/bootstrap.d.cts +7 -0
- package/dist/bootstrap.d.mts +7 -0
- package/dist/bootstrap.d.ts +7 -0
- package/dist/bootstrap.mjs +2 -0
- package/dist/bootstrap.mjs.map +7 -0
- package/dist/host.cjs +1 -1
- package/dist/host.cjs.map +3 -3
- package/dist/host.mjs +1 -1
- package/dist/host.mjs.map +3 -3
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +4 -4
- package/dist/index.mjs.map +4 -4
- package/dist/markers.cjs +11 -4
- package/dist/markers.cjs.map +3 -3
- package/dist/markers.d.cts +39 -2
- package/dist/markers.d.mts +39 -2
- package/dist/markers.d.ts +39 -2
- package/dist/markers.mjs +11 -4
- package/dist/markers.mjs.map +3 -3
- package/dist/protocol.cjs +1 -1
- package/dist/protocol.cjs.map +3 -3
- package/dist/protocol.d.cts +7 -0
- package/dist/protocol.d.mts +7 -0
- package/dist/protocol.d.ts +7 -0
- package/dist/protocol.mjs +1 -1
- package/dist/protocol.mjs.map +3 -3
- package/dist/read.cjs +1 -1
- package/dist/read.cjs.map +3 -3
- package/dist/read.d.cts +46 -1
- package/dist/read.d.mts +46 -1
- package/dist/read.d.ts +46 -1
- package/dist/read.mjs +1 -1
- package/dist/read.mjs.map +3 -3
- package/dist/selection.cjs +5 -0
- package/dist/selection.cjs.map +7 -0
- package/dist/selection.d.cts +27 -0
- package/dist/selection.d.mts +27 -0
- package/dist/selection.d.ts +27 -0
- package/dist/selection.mjs +5 -0
- package/dist/selection.mjs.map +7 -0
- package/dist/session.d.cts +10 -0
- package/dist/session.d.mts +10 -0
- package/dist/session.d.ts +10 -0
- package/dist/transport.d.cts +10 -0
- package/dist/transport.d.mts +10 -0
- package/dist/transport.d.ts +10 -0
- package/package.json +37 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weatherboard/annotation",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Click an element in a running app, get the source location that rendered it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotation",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "git+https://github.com/
|
|
15
|
+
"url": "git+https://github.com/Weatherboard-Studio/weatherboard-studio.git",
|
|
16
16
|
"directory": "packages/annotation"
|
|
17
17
|
},
|
|
18
18
|
"type": "module",
|
|
@@ -29,6 +29,16 @@
|
|
|
29
29
|
"default": "./dist/index.cjs"
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
|
+
"./bootstrap": {
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/bootstrap.d.mts",
|
|
35
|
+
"default": "./dist/bootstrap.mjs"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/bootstrap.d.cts",
|
|
39
|
+
"default": "./dist/bootstrap.cjs"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
32
42
|
"./protocol": {
|
|
33
43
|
"import": {
|
|
34
44
|
"types": "./dist/protocol.d.mts",
|
|
@@ -79,7 +89,17 @@
|
|
|
79
89
|
"default": "./dist/markers.cjs"
|
|
80
90
|
}
|
|
81
91
|
},
|
|
82
|
-
"./package.json": "./package.json"
|
|
92
|
+
"./package.json": "./package.json",
|
|
93
|
+
"./selection": {
|
|
94
|
+
"import": {
|
|
95
|
+
"types": "./dist/selection.d.mts",
|
|
96
|
+
"default": "./dist/selection.mjs"
|
|
97
|
+
},
|
|
98
|
+
"require": {
|
|
99
|
+
"types": "./dist/selection.d.cts",
|
|
100
|
+
"default": "./dist/selection.cjs"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
83
103
|
},
|
|
84
104
|
"main": "./dist/index.cjs",
|
|
85
105
|
"types": "./dist/index.d.ts",
|
|
@@ -98,14 +118,19 @@
|
|
|
98
118
|
],
|
|
99
119
|
"//sizeBudget": "Gzipped bytes per published entry point, checked by `build.mjs` on every build and therefore in CI. A budget is here rather than in a separate config because the number is part of the package's contract: this code runs on every page of every managed product, and 'it grew a bit each time' is how a 4KB SDK becomes a 40KB one with nobody having decided. Raising a number is allowed; doing it in the same commit as the growth, where a reviewer sees both, is the point.",
|
|
100
120
|
"//sizeBudget-anchor": "A-275, and the reason it is a FOURTH ENTRY POINT rather than a re-export from `.`: the marker layer (A-277) runs on the product page and needs `resolveAnchor`, but the SDK that only CAPTURES does not, and folding it into index would put it in the bundle of every managed product whether or not that product renders markers. THE NUMBER HERE WAS WRONG ONCE AND THAT IS WHY IT IS SPELLED OUT: the first commit of this note said 961, measured by bundling `src/anchor.ts` alone with esbuild, and `build.mjs` printed 996 for the same commit - the standalone bundle and the real build are not the same measurement, and a stated figure that disagrees with the gate is worse than no figure. MEASURED BY RUNNING `node build.mjs` AND READING ITS OUTPUT, which is the only number the budget is ever compared against: 1036 as shipped, after A-275's review cycle added `RouteAnchor.path` and the path check. Budget 1100, so headroom is 1100/1036 = 6.2%. Deliberately not raised to restore the slack the review spent: this entry point exists to grow when A-277 needs it to, and the budget's job is to make that a decision somebody makes. Looser in PROPORTION than index (0.83%) for the reason protocol is: 64 bytes is 6% here and would be 0.7% of index.",
|
|
101
|
-
"//sizeBudget-index": "A-241 adds arming: 8700 -> 9400 for index and 700 -> 850 for protocol. MEASURED by rebuilding, four numbers. Baseline at A-240's merge commit fceb183: index 8562, protocol 646 - the two figures the previous note recorded, re-measured rather than trusted. As shipped: index 9281, protocol 788. And with the ./arming re-export block deleted from src/index.ts but everything else present: 8913. SO readArming AND ITS TYPES ARE 368 OF THE 719, AND 351 ARE NOT: 142 of that is protocol's own growth (armedCookie, clearedCookie, ANNOTATION_ARMED_VALUE, REVIEW_RUN_PARAM), which index pays too because it re-exports protocol whole, and the rest is isAnnotatableBuild with its ANNOTATABLE_ENVS allowlist, the two-gate expression in mountAnnotation, and Done's teardown. That 351 is the number worth knowing: a consumer who never calls readArming still pays it, because the kill-switch is not optional and must not be tree-shakeable. TWO REVIEW CYCLES MOVED IT AND BOTH COSTS ARE MEASURED: the fail-closed allowlist +27 (9239 -> 9266) and Done's teardown +15 (9266 -> 9281). HEADROOM IS PER ENTRY POINT AND THE TWO ARE STATED SEPARATELY, because one figure for both was false here once already: index 9400/9281 = 1.28%, against A-240's 8700/8562 = 1.61%, so tighter and DELIBERATELY NOT TOPPED UP - the budget's job is to make the next growth a decision, and quietly restoring slack each time is how it stops doing that; protocol 850/788 = 7.87%, against its previous 700/646 = 8.36%. protocol runs looser in PROPORTION only because it is small - 62 bytes is 7.87% there and would be 0.67% of index. The argument for the toolbar's own 2553 bytes is in docs/decisions/a-240-annotation-toolbar-size.md, extended there for this slice rather than restated here. A-254 MOVED IT AGAIN AND THE BUDGET IS DELIBERATELY NOT RAISED: index 9281 -> 9300, +19 gzipped bytes for the armed-before-persist early return in readArming, re-measured by running build.mjs before and after. Headroom is now 9400/9300 = 1.06%. That is tight on purpose - 19 bytes bought the end of an unarmed page storing an attacker-supplied capture token, and the next growth after it should be a decision somebody makes rather than slack somebody spends. A-277 MOVED IT AGAIN, TWICE, AND BOTH ARE MEASURED BY REBUILDING: index 9471 -> 9531, +60 gzipped bytes, and protocol 788 -> 800, +12. The 12 is READ_TOKEN_PARAM and its comment-free constant; the remaining 48 in index is arming carrying the read token - a fourth stripped parameter, a third storage key, a `readToken` field on `Arming`, and the read/write branches for it in `readArming`. THAT 48 IS PAID BY EVERY CONSUMER INCLUDING ONES THAT NEVER MOUNT A MARKER, and it is paid deliberately rather than moved into ./markers: the strip is the thing that gets a signed credential out of an address bar, it has to run in the first milliseconds of the page, and a product that mounts only the toolbar still receives an `ai_read` in its URL and still must not leave it there. Budgets 9550 -> 9650 and 850 -> 870. 9650 rather than 9600: at 9600 the headroom is 69 bytes, which is a budget that fails on the next sentence somebody writes rather than on the next decision somebody makes. 9650/9531 = 1.25% and 870/800 = 8.75%, so index is LOOSER than A-285 left it (0.83%) and that is stated rather than hidden - the slack was restored on purpose this once, because A-279 (replies) is the next slice into this file and a budget with 19 bytes in it makes that slice's first commit a budget argument instead of a design one. A-285 MOVED IT AGAIN AND THE BUDGET IS RAISED, WITH THE COST SPLIT AND MEASURED: index 9300 -> 9471, +171 gzipped bytes, by rebuilding three times. 9300 is the baseline at this branch point, re-measured rather than trusted. 9426 with transport.ts alone - safeRefusalReason, the IngestRefusal fields and the body read on the refusal branch, 126 bytes. 9471 with the toolbar rendering it and index re-exporting the two functions, a further 45. THAT 171 IS WHAT IT COSTS FOR A REFUSAL TO REACH A HUMAN AT ALL: before it, every sentence the ingest composed - the link issued for another deployment, the empty comment - was thrown away by the only client that could show it, and every failure read 'That did not send'. 9550 rather than 9500: 9500 would leave 29 bytes, which is a budget that fails on the next comment somebody writes rather than on the next decision somebody makes. 9550/9471 = 0.83%, against A-254's 9400/9300 = 1.06% - so still tighter than it was, and deliberately not topped up.",
|
|
121
|
+
"//sizeBudget-index": "A-385: the persistent progress receipt link measures 9919 gzip bytes under Node 22.19.0 (CI agrees), versus 9892 under Node 26.7.0. Budget 9900 -> 9950 accepts the 19-byte CI overage and leaves 31 bytes (0.31%) of headroom. Bounded receipt compaction did not improve the gzip measurement; keep the clear validation and receipt shape rather than distort them for compression. A-383: durable saved-capture receipt and persistent recovery link increase index to 9806 gzip bytes as measured by pnpm build; budget 9700 -> 9900. A-241 adds arming: 8700 -> 9400 for index and 700 -> 850 for protocol. MEASURED by rebuilding, four numbers. Baseline at A-240's merge commit fceb183: index 8562, protocol 646 - the two figures the previous note recorded, re-measured rather than trusted. As shipped: index 9281, protocol 788. And with the ./arming re-export block deleted from src/index.ts but everything else present: 8913. SO readArming AND ITS TYPES ARE 368 OF THE 719, AND 351 ARE NOT: 142 of that is protocol's own growth (armedCookie, clearedCookie, ANNOTATION_ARMED_VALUE, REVIEW_RUN_PARAM), which index pays too because it re-exports protocol whole, and the rest is isAnnotatableBuild with its ANNOTATABLE_ENVS allowlist, the two-gate expression in mountAnnotation, and Done's teardown. That 351 is the number worth knowing: a consumer who never calls readArming still pays it, because the kill-switch is not optional and must not be tree-shakeable. TWO REVIEW CYCLES MOVED IT AND BOTH COSTS ARE MEASURED: the fail-closed allowlist +27 (9239 -> 9266) and Done's teardown +15 (9266 -> 9281). HEADROOM IS PER ENTRY POINT AND THE TWO ARE STATED SEPARATELY, because one figure for both was false here once already: index 9400/9281 = 1.28%, against A-240's 8700/8562 = 1.61%, so tighter and DELIBERATELY NOT TOPPED UP - the budget's job is to make the next growth a decision, and quietly restoring slack each time is how it stops doing that; protocol 850/788 = 7.87%, against its previous 700/646 = 8.36%. protocol runs looser in PROPORTION only because it is small - 62 bytes is 7.87% there and would be 0.67% of index. The argument for the toolbar's own 2553 bytes is in docs/decisions/a-240-annotation-toolbar-size.md, extended there for this slice rather than restated here. A-254 MOVED IT AGAIN AND THE BUDGET IS DELIBERATELY NOT RAISED: index 9281 -> 9300, +19 gzipped bytes for the armed-before-persist early return in readArming, re-measured by running build.mjs before and after. Headroom is now 9400/9300 = 1.06%. That is tight on purpose - 19 bytes bought the end of an unarmed page storing an attacker-supplied capture token, and the next growth after it should be a decision somebody makes rather than slack somebody spends. A-277 MOVED IT AGAIN, TWICE, AND BOTH ARE MEASURED BY REBUILDING: index 9471 -> 9531, +60 gzipped bytes, and protocol 788 -> 800, +12. The 12 is READ_TOKEN_PARAM and its comment-free constant; the remaining 48 in index is arming carrying the read token - a fourth stripped parameter, a third storage key, a `readToken` field on `Arming`, and the read/write branches for it in `readArming`. THAT 48 IS PAID BY EVERY CONSUMER INCLUDING ONES THAT NEVER MOUNT A MARKER, and it is paid deliberately rather than moved into ./markers: the strip is the thing that gets a signed credential out of an address bar, it has to run in the first milliseconds of the page, and a product that mounts only the toolbar still receives an `ai_read` in its URL and still must not leave it there. Budgets 9550 -> 9650 and 850 -> 870. 9650 rather than 9600: at 9600 the headroom is 69 bytes, which is a budget that fails on the next sentence somebody writes rather than on the next decision somebody makes. 9650/9531 = 1.25% and 870/800 = 8.75%, so index is LOOSER than A-285 left it (0.83%) and that is stated rather than hidden - the slack was restored on purpose this once, because A-279 (replies) is the next slice into this file and a budget with 19 bytes in it makes that slice's first commit a budget argument instead of a design one. A-285 MOVED IT AGAIN AND THE BUDGET IS RAISED, WITH THE COST SPLIT AND MEASURED: index 9300 -> 9471, +171 gzipped bytes, by rebuilding three times. 9300 is the baseline at this branch point, re-measured rather than trusted. 9426 with transport.ts alone - safeRefusalReason, the IngestRefusal fields and the body read on the refusal branch, 126 bytes. 9471 with the toolbar rendering it and index re-exporting the two functions, a further 45. THAT 171 IS WHAT IT COSTS FOR A REFUSAL TO REACH A HUMAN AT ALL: before it, every sentence the ingest composed - the link issued for another deployment, the empty comment - was thrown away by the only client that could show it, and every failure read 'That did not send'. 9550 rather than 9500: 9500 would leave 29 bytes, which is a budget that fails on the next comment somebody writes rather than on the next decision somebody makes. 9550/9471 = 0.83%, against A-254's 9400/9300 = 1.06% - so still tighter than it was, and deliberately not topped up.",
|
|
122
|
+
"//sizeBudget-host": "A-359, and the first note this entry point has had. MEASURED BY RUNNING `node build.mjs` AND READING ITS OUTPUT, three entry points, before and after, on this branch rather than trusted from the last note: host 566 -> 649 (+83), protocol 800 -> 869 (+69), index 9531 -> 9599 (+68). WHAT THE BYTES ARE: `unhandledMessage` in protocol.ts, which is the whole of protocol's 69 and is re-exported into index for index's 68; host pays that 69 plus 14 for its own `default` branch. The `const unhandled: never` assertions cost NOTHING - they are erased at build, which is the good half of this trade. WHAT THEY BUY: host.ts and sdk.ts each dispatched on a discriminated union with no `default`, so a message type added to `ParentToFrame` or `FrameToParent` was DROPPED by whichever half had not been taught about it, with no error anywhere. Measured before the fix: adding a member to each union and running `tsc --noEmit` gave exit 0 and zero errors. These two files are the only things in the world that can disagree about what a message on this wire means, and they deploy from different repositories. THE FIRST DRAFT OF THE SENTENCE COST 230 CHARACTERS AND PUT ALL THREE ENTRY POINTS OVER AT ONCE - index 9717/9650, protocol 991/870, host 771/660 - which is the gate working, and is why the shipped string is one line: which half, what arrived, and that nothing was done with it. Nothing beyond that survived the budget, and nothing beyond that is load-bearing. BUDGETS RAISED: protocol 870 -> 890, index 9650 -> 9700, host 660 -> 680. Every one of the three was ALREADY UNDER its old budget after the string was trimmed - 869/870, 9599/9650 and 649/660 - so this is not a raise to make a failure go away, and the numbers above are the gate's own output at both ends. It is a raise because 1, 51 and 11 bytes of headroom is the state the A-277 note names as the thing to avoid: 'a budget that fails on the next sentence somebody writes rather than on the next decision somebody makes'. Protocol at one byte would have failed the next contributor over a comma. New headroom: protocol 890/869 = 2.4%, host 680/649 = 4.8%, index 9700/9599 = 1.05% - index tightest in proportion for the reason every note here gives, that a percentage of index is a lot of bytes, and the two small entry points looser for the reason protocol has always run looser, that 20 bytes is 2% there and would be 0.2% of index.",
|
|
123
|
+
"//sizeBudget-host-a359-cycle2": "The guard on `unhandledMessage`'s stringify, and this raise IS forced by the gate rather than headroom-tidying like the one above it. MEASURED BY RUNNING `node build.mjs`, every figure its own output: protocol 869 -> 908 (+39), host 649 -> 686 (+37), index 9599 -> 9615 (+16). protocol and host both went OVER their A-359 budgets (908/890, 686/680) and are raised to 940 and 720; index needs nothing and is NOT raised, so it stays at 9700 with 9615 against it. WHAT THE BYTES BUY: `unhandledMessage` interpolated `JSON.stringify(message)` unguarded, and postMessage serialises with STRUCTURED CLONE, which PRESERVES CYCLES where JSON cannot represent them - so a circular `event.data` made the function throw `Converting circular structure to JSON` from inside a `message` listener on a page this SDK is a guest on, once per message, at the sender's discretion. That is the exact outcome the forty-line comment on that function argues against; the implementation defeated its own argument. Both the circular case and a throwing-getter Proxy were confirmed RED against the previous implementation before the fix and are pinned in exhaustive.test.ts. THE EXPENSIVE ALTERNATIVE WAS TRIED AND MEASURED RATHER THAN REASONED ABOUT: reusing `describeArg` from state.ts - which solves the same problem and whose own comment names the same hazard - costs protocol 869 -> 1008 (+139) and host 649 -> 793 (+144), a hundred bytes per entry point more than the local version, because describeArg is built for console ARGUMENTS (strings, Errors, a prototype walk) and almost none of it is reachable for a wire message. It was reverted for a second reason that would stand even at equal cost: `protocol.ts` currently has ZERO imports, `state.ts` imports FROM it, and importing back would make the pure wire contract depend on the console-patching state collector and close a module cycle that is invisible only because state.ts's side is `import type`. The cost of that choice is two safe-stringify helpers in this package instead of one; they cannot drift in a way that matters, each being a total function with one behaviour and its own test, and the note on `safeJson` says what to do if a third ever appears. New headroom: protocol 940/908 = 3.5%, host 720/686 = 5.0%, index 9700/9615 = 0.88%.",
|
|
124
|
+
"//sizeBudget-a1375": "A-1375 adds an eighth, deliberately small ./bootstrap entry point for the synchronous pre-hydration script, arming reads and token-free startup status. It is separate so an ordinary visitor does not download the toolbar, state collector or transport before the page knows it is armed. REPRODUCED WITH NODE 22.19.0 by running `pnpm --filter @weatherboard/annotation build` on the unmodified origin/main baseline 4d1df98e and on this source, then reading build.mjs's ESM gzip rows: index 9919 -> 10082 (+163), protocol 908 -> 967 (+59), and bootstrap absent (0 emitted bytes) -> 2321 (+2321). The index increase pays for the hosted-mode constants re-export and Done clearing shared bootstrap state; protocol pays for the two token-free capability constants; bootstrap contains the new early runtime itself. The index budget moves 9950 -> 10150 (+200, 2.01%) and leaves 68 bytes, 0.67% of its current artifact. The protocol budget moves 940 -> 1000 (+60, 6.38%) and leaves 33 bytes, 3.41% of its current artifact, effectively preserving its previous 32-byte allowance. The new bootstrap budget is 2350 and leaves 29 bytes, 1.25% of its current artifact. Bootstrap is deliberately tighter than protocol because it runs synchronously in every adopting document; all three limits remain close enough that the next material feature requires an explicit increase rather than spending unrecorded slack.",
|
|
102
125
|
"sizeBudget": {
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
126
|
+
"bootstrap": 2350,
|
|
127
|
+
"protocol": 1000,
|
|
128
|
+
"index": 10150,
|
|
129
|
+
"host": 720,
|
|
106
130
|
"anchor": 1100,
|
|
107
131
|
"read": 1700,
|
|
108
|
-
"markers":
|
|
132
|
+
"markers": 4250,
|
|
133
|
+
"selection": 3500
|
|
109
134
|
},
|
|
110
135
|
"engines": {
|
|
111
136
|
"node": ">=18"
|
|
@@ -118,13 +143,13 @@
|
|
|
118
143
|
"tsx": "^4.21.0",
|
|
119
144
|
"typescript": "~5.7.2"
|
|
120
145
|
},
|
|
121
|
-
"//sizeBudget-markers": "A-277, and a SIXTH ENTRY POINT for ./anchor and ./read's reason, which applies here with the most weight of the three: the marker layer, its panel and its stylesheet run ONLY in a product that renders markers, and folding them into index would put all of it in the bundle of every page of every managed product including the ones that will never show one. MEASURED BY RUNNING `node build.mjs` AND READING ITS OUTPUT, which is the only number the budget is ever compared against: 3449 as shipped. The first draft of this note said 3400 and the build printed 3449 on the same commit - the number is written down AFTER the gate has been run, never before, because a stated figure that disagrees with the gate is worse than no figure. 3449 was the figure BEFORE a browser was pointed at it; the clamp-into-viewport, the collision nudge and the date formatting cost a further 181, so the shipped number is 3630 and every one of those 181 bytes bought a defect that no fixture test could see (a pin at y:-11, two pins overlapping by 22 of their 22 pixels, and an ISO instant rendered raw beside a name). Tokenising the CSS onto `:host` custom properties - the same names and values `toolbar.ts` uses, so the two surfaces a reviewer sees on one page are one design - cost a further 168 and cleared the design audit's untokenised-colour and untokenised-space findings, which must not be answered by lengthening `gyde-allowance.json`. Shipped at 3800 (rebuilt after two 16px offsets also became a var). A REVIEW CYCLE THEN ADDED 79 MORE, and they are the most load-bearing bytes in this entry point: `freeSpot`'s bound. The collision walk it replaced was a bare `while` with no exit but success, and on a viewport measuring zero - a hidden iframe, a prerender, a stubbed `win` - two pins were enough to make it spin forever, LOCKING THE MAIN THREAD OF A CUSTOMER'S PAGE. Shipped at 3879. Budget 3950, so headroom is 3950/3879 = 1.8% - TIGHTER in proportion than anchor (6.2%) and read (6.4%) on purpose, because this is the largest entry point after index and a percentage of it is a lot of bytes. What is IN it: the CSS string and the DOM construction are most of it, and neither is optional - the layer lives in a shadow root so the product's stylesheet cannot reach in and it cannot reach out, and every value reaches the DOM through textContent because everything rendered here came off the network from a console this page does not control. placeMarkers, the clustering and the count-line sentence are small; resolveAnchor is pulled in from ./anchor by tree-shaking and is 1036 of the total on its own, which is why this is not 2400.",
|
|
122
|
-
"//sizeBudget-read": "A-276, and a FIFTH ENTRY POINT for ./anchor reason: the read client runs only in a product that RENDERS markers, and folding it into index would tax every page of every managed product with code most of them never call. MEASURED BY RUNNING node build.mjs AND READING ITS OUTPUT, which is the only number the budget is ever compared against: 1597 as shipped. Budget 1700, so headroom is 1700/1597 = 6.4% - deliberately in line with anchor 1100/1036 = 6.2% rather than tighter, because A-277 is the slice that will grow this one and the budget job is to make that growth a decision somebody makes. It is looser in PROPORTION than index 0.83% for the reason protocol is: 100 bytes is 6% here and would be 1% of index. What is IN it: the envelope and marker types are erased at build, so the bytes are parseAnnotationsResponse and its three shape checks (the anchor union, the marker, the counts), createAnnotationReader, and assertConcreteEndpoint pulled in from transport.ts by tree-shaking. The sanitising is most of it and is not optional - comment and reviewer are rendered inside the customers own page, and safeIssueUrl header has the argument.",
|
|
146
|
+
"//sizeBudget-markers": "A-277, and a SIXTH ENTRY POINT for ./anchor and ./read's reason, which applies here with the most weight of the three: the marker layer, its panel and its stylesheet run ONLY in a product that renders markers, and folding them into index would put all of it in the bundle of every page of every managed product including the ones that will never show one. MEASURED BY RUNNING `node build.mjs` AND READING ITS OUTPUT, which is the only number the budget is ever compared against: 3449 as shipped. The first draft of this note said 3400 and the build printed 3449 on the same commit - the number is written down AFTER the gate has been run, never before, because a stated figure that disagrees with the gate is worse than no figure. 3449 was the figure BEFORE a browser was pointed at it; the clamp-into-viewport, the collision nudge and the date formatting cost a further 181, so the shipped number is 3630 and every one of those 181 bytes bought a defect that no fixture test could see (a pin at y:-11, two pins overlapping by 22 of their 22 pixels, and an ISO instant rendered raw beside a name). Tokenising the CSS onto `:host` custom properties - the same names and values `toolbar.ts` uses, so the two surfaces a reviewer sees on one page are one design - cost a further 168 and cleared the design audit's untokenised-colour and untokenised-space findings, which must not be answered by lengthening `gyde-allowance.json`. Shipped at 3800 (rebuilt after two 16px offsets also became a var). A REVIEW CYCLE THEN ADDED 79 MORE, and they are the most load-bearing bytes in this entry point: `freeSpot`'s bound. The collision walk it replaced was a bare `while` with no exit but success, and on a viewport measuring zero - a hidden iframe, a prerender, a stubbed `win` - two pins were enough to make it spin forever, LOCKING THE MAIN THREAD OF A CUSTOMER'S PAGE. Shipped at 3879. Budget 3950, so headroom is 3950/3879 = 1.8% - TIGHTER in proportion than anchor (6.2%) and read (6.4%) on purpose, because this is the largest entry point after index and a percentage of it is a lot of bytes. What is IN it: the CSS string and the DOM construction are most of it, and neither is optional - the layer lives in a shadow root so the product's stylesheet cannot reach in and it cannot reach out, and every value reaches the DOM through textContent because everything rendered here came off the network from a console this page does not control. placeMarkers, the clustering and the count-line sentence are small; resolveAnchor is pulled in from ./anchor by tree-shaking and is 1036 of the total on its own, which is why this is not 2400. A-323 MOVED IT AND THE BUDGET IS RAISED, WITH THE COST SPLIT AND MEASURED BY RUNNING `node build.mjs` THREE TIMES: markers 3879 -> 4186, +307 gzipped bytes. 3879 is the baseline at this branch point, re-measured rather than trusted. 4149 with the `.toggle` CSS rules deleted but everything else present, so THE STYLESHEET IS 37 OF THE 307 AND THE OTHER 270 IS NOT: those 270 are the three label strings (showResolved, hideResolved, and the resolved clause on count), the paintToggle state function, the toggle node with its click handler and its aria-pressed, and the .count-text span that lets the sentence be rewritten without removing the button. WHAT THE 307 BUYS: A-288 made the console suppress done and cancelled by default, and until this entry point could COUNT what was suppressed and ASK for it back, deploying that console would have made resolved markers vanish from a live product page silently - a marker silently absent is indistinguishable from a marker never made, which is the failure the counted envelope exists to prevent, reintroduced by a display default. A 728-BYTE MISTAKE IS RECORDED IN markers.ts ABOVE PIN BECAUSE IT IS THE NON-OBVIOUS COST IN THIS FILE: the placement argument was first written as a comment INSIDE the CSS template literal, where a comment is a string in the shipped bundle - it measured 4607, 657 over budget, and moving the identical prose four lines out to a TypeScript comment took it to 4186 with no behaviour change at all. Budget 3950 -> 4250, so headroom is 4250/4186 = 1.53%, TIGHTER in proportion than A-277 left it (3950/3879 = 1.8%) and deliberately not topped up: A-279 (replies) is the next slice into this file and its bytes should be a decision somebody makes rather than slack somebody restored. Prose in this file is free; only what lands inside CSS is payload.",
|
|
147
|
+
"//sizeBudget-read": "A-276, and a FIFTH ENTRY POINT for ./anchor reason: the read client runs only in a product that RENDERS markers, and folding it into index would tax every page of every managed product with code most of them never call. MEASURED BY RUNNING node build.mjs AND READING ITS OUTPUT, which is the only number the budget is ever compared against: 1597 as shipped. Budget 1700, so headroom is 1700/1597 = 6.4% - deliberately in line with anchor 1100/1036 = 6.2% rather than tighter, because A-277 is the slice that will grow this one and the budget job is to make that growth a decision somebody makes. It is looser in PROPORTION than index 0.83% for the reason protocol is: 100 bytes is 6% here and would be 1% of index. What is IN it: the envelope and marker types are erased at build, so the bytes are parseAnnotationsResponse and its three shape checks (the anchor union, the marker, the counts), createAnnotationReader, and assertConcreteEndpoint pulled in from transport.ts by tree-shaking. The sanitising is most of it and is not optional - comment and reviewer are rendered inside the customers own page, and safeIssueUrl header has the argument. A-323 MOVED IT AND THE BUDGET IS DELIBERATELY NOT RAISED: read 1593 -> 1652, +59 gzipped bytes, re-measured by running build.mjs before and after. What the 59 buys is the ONLY way an SDK can see a finished capture: the resolved=include search param behind an includeResolved option, the RESOLVED_INCLUDE constant, and resolvedHidden on the parser. The constant is not an indulgence - the console compares params.get('resolved') === 'include' and its own tests pin that true, 1, yes, Include, INCLUDE and a trailing space all get the DEFAULT, so a client sending any near-miss would widen nothing, report nothing wrong, and leave a page saying '2 resolved' beside a button that does not work. THE FIELD IS ADDITIVE AND wireVersion STAYS AT 1: parseAnnotationsResponse already ignores unknown fields and answers 0 for an absent count, so an older SDK meeting the A-288 console is unaffected and this SDK meeting a pre-A-288 console reads resolvedHidden: 0, which is the honest reading of a console that hides nothing. Headroom is now 1700/1652 = 2.9%, down from 6.4% - the slack A-276 left for A-277 has been spent by A-323 instead, which is what it was for.",
|
|
123
148
|
"scripts": {
|
|
124
149
|
"build": "node build.mjs",
|
|
125
|
-
"typecheck": "tsc --noEmit",
|
|
150
|
+
"typecheck": "node ../../node_modules/@typescript/native/bin/tsc --noEmit --incremental --tsBuildInfoFile .typecheck.tsbuildinfo",
|
|
126
151
|
"//lint": "A-97. The root `eslint.config.mjs` is the single rule set — ESLint finds it by walking up from here, so there is nothing per-package to keep in sync. It is a script in EVERY package on purpose: A-52's verify tier runs `pnpm -r --if-present lint` and counts how many packages DECLARED it, so a package without this line is invisible to that gate rather than merely unlinted. `--max-warnings 0` because the React and Next plugins report at `warn` by default, and a gate that exits 0 while printing problems is the exact failure this repository keeps finding.",
|
|
127
|
-
"lint": "eslint . --max-warnings 0",
|
|
152
|
+
"lint": "eslint . --max-warnings 0 --cache --cache-strategy content",
|
|
128
153
|
"//test": "`--test-timeout` is not decoration (A-240). Without it, breaking `httpTransport`'s abort did not turn this suite RED — it hung `pnpm test` forever, because the pending fetch keeps the event loop alive and node:test has no default deadline. CI times out, so it never read as a pass; but a wall-clock stall names no test and blames whatever was printed last, and this repository's standard is a NAMED FAILING TEST. 30s is a ceiling and not a budget: MEASURED, the slowest test in this suite is 203ms (`a 500 reaches the SDK's error handler`, which sleeps 200ms on purpose), so the limit is ~150x it and no honest test can reach it.",
|
|
129
154
|
"test": "tsx --test --test-timeout=30000 src/*.test.ts",
|
|
130
155
|
"e2e": "playwright test",
|