@weatherboard/annotation 0.1.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/dist/sdk.d.ts ADDED
@@ -0,0 +1,52 @@
1
+ import { type AnnotationPayload, type BuildInfo } from "./protocol.js";
2
+ import { type ScreenshotOptions } from "./screenshot.js";
3
+ /**
4
+ * The annotation SDK (A-36).
5
+ *
6
+ * Lives in the product bundle, listens for the management app's instructions,
7
+ * and turns a click into a payload pinned to a code location.
8
+ */
9
+ export interface AnnotationState {
10
+ consoleErrors: AnnotationPayload["state"]["consoleErrors"];
11
+ failedRequests: AnnotationPayload["state"]["failedRequests"];
12
+ featureFlags: Record<string, unknown>;
13
+ }
14
+ export interface AnnotationSdkOptions {
15
+ /**
16
+ * The management app's origin. Required and concrete — see
17
+ * `assertConcreteOrigin`. Messages from anywhere else are ignored, and we
18
+ * never post to anywhere else.
19
+ */
20
+ parentOrigin: string;
21
+ build: BuildInfo;
22
+ /**
23
+ * The matched route pattern, e.g. `/pub/[slug]`.
24
+ *
25
+ * Injected rather than derived: only the app knows its own route patterns,
26
+ * and `location.pathname` is the resolved URL, which is what the spec
27
+ * explicitly does not want — `/pub/the-eagle` and `/pub/the-crown` are the
28
+ * same route and should group as one.
29
+ */
30
+ getRoute: () => string;
31
+ /**
32
+ * Page state at capture time. Injected so A-35's ring buffers can supply the
33
+ * real thing without this module knowing how they are collected.
34
+ */
35
+ getState: () => AnnotationState;
36
+ /**
37
+ * Screenshot capture (A-31). Omit for the default, or pass `false` to turn it
38
+ * off entirely. See `screenshot.ts` for what it can and cannot capture — the
39
+ * short version is that it captures a media element's own bitmap and nothing
40
+ * else, so most clicks produce no screenshot at all, by design.
41
+ */
42
+ screenshot?: ScreenshotOptions | false;
43
+ }
44
+ export interface AnnotationSdk {
45
+ enable(): void;
46
+ disable(): void;
47
+ isEnabled(): boolean;
48
+ destroy(): void;
49
+ }
50
+ /** State until A-35 lands its ring buffers. Empty is honest; invented is not. */
51
+ export declare const emptyAnnotationState: () => AnnotationState;
52
+ export declare function createAnnotationSdk(options: AnnotationSdkOptions): AnnotationSdk;
@@ -0,0 +1,62 @@
1
+ import type { AnnotationState } from "./sdk.cjs";
2
+ /**
3
+ * Page state, captured continuously (A-35).
4
+ *
5
+ * The timing is the whole point. By the time someone notices something is wrong
6
+ * and clicks it, the interesting error has already happened — reading
7
+ * `console` at capture time gets you nothing, because there is nothing to read.
8
+ * So the buffers fill from page load and the click just takes a copy.
9
+ *
10
+ * Which means this code runs on every page, for every user in the enabled
11
+ * environments, in front of the app's own error handling. It is written to be
12
+ * boring: patch, record, call through, never throw.
13
+ */
14
+ /** Twenty each, per the spec. Enough for context, small enough to post. */
15
+ export declare const DEFAULT_LIMIT = 20;
16
+ /**
17
+ * A fixed-size window over the most recent N items.
18
+ *
19
+ * Oldest-out rather than newest-dropped: a page that logs steadily would
20
+ * otherwise pin the buffer to whatever happened at boot and never show the
21
+ * error the reviewer is looking at.
22
+ */
23
+ export declare class RingBuffer<T> {
24
+ #private;
25
+ constructor(limit?: number);
26
+ push(item: T): void;
27
+ /** A copy, so a consumer holding the payload cannot see it mutate underneath them. */
28
+ toArray(): T[];
29
+ get size(): number;
30
+ }
31
+ /**
32
+ * Render one console argument as text, without ever throwing.
33
+ *
34
+ * Console arguments are arbitrary: DOM nodes, circular objects, proxies that
35
+ * throw on property access. Anything that fails here would fail *inside the
36
+ * app's own `console.error` call*, turning a logged error into a thrown one.
37
+ */
38
+ export declare function describeArg(arg: unknown): string;
39
+ export declare function formatConsoleArgs(args: unknown[]): string;
40
+ /** The first `Error` among the arguments carries the only stack worth keeping. */
41
+ export declare function firstStack(args: unknown[]): string | undefined;
42
+ /** A response is interesting when it is not 2xx. `0` marks a request that never got one. */
43
+ export declare function isFailedStatus(status: number): boolean;
44
+ export interface StateCollector {
45
+ getState(): AnnotationState;
46
+ /** Restore every patched global. Exposed mainly so tests can prove it is reversible. */
47
+ destroy(): void;
48
+ }
49
+ export interface StateCollectorOptions {
50
+ limit: number;
51
+ /** Feature flags are the app's to know; this module does not guess at them. */
52
+ getFeatureFlags: () => Record<string, unknown>;
53
+ now: () => string;
54
+ }
55
+ export declare const defaultStateCollectorOptions: () => StateCollectorOptions;
56
+ /**
57
+ * Patch `console`, `fetch` and `XMLHttpRequest`, and record what goes wrong.
58
+ *
59
+ * Install this as early as the bundle allows — an error thrown before the patch
60
+ * is an error the payload will not carry.
61
+ */
62
+ export declare function installStateCollector(win: Window & typeof globalThis, options?: StateCollectorOptions): StateCollector;
@@ -0,0 +1,62 @@
1
+ import type { AnnotationState } from "./sdk.mjs";
2
+ /**
3
+ * Page state, captured continuously (A-35).
4
+ *
5
+ * The timing is the whole point. By the time someone notices something is wrong
6
+ * and clicks it, the interesting error has already happened — reading
7
+ * `console` at capture time gets you nothing, because there is nothing to read.
8
+ * So the buffers fill from page load and the click just takes a copy.
9
+ *
10
+ * Which means this code runs on every page, for every user in the enabled
11
+ * environments, in front of the app's own error handling. It is written to be
12
+ * boring: patch, record, call through, never throw.
13
+ */
14
+ /** Twenty each, per the spec. Enough for context, small enough to post. */
15
+ export declare const DEFAULT_LIMIT = 20;
16
+ /**
17
+ * A fixed-size window over the most recent N items.
18
+ *
19
+ * Oldest-out rather than newest-dropped: a page that logs steadily would
20
+ * otherwise pin the buffer to whatever happened at boot and never show the
21
+ * error the reviewer is looking at.
22
+ */
23
+ export declare class RingBuffer<T> {
24
+ #private;
25
+ constructor(limit?: number);
26
+ push(item: T): void;
27
+ /** A copy, so a consumer holding the payload cannot see it mutate underneath them. */
28
+ toArray(): T[];
29
+ get size(): number;
30
+ }
31
+ /**
32
+ * Render one console argument as text, without ever throwing.
33
+ *
34
+ * Console arguments are arbitrary: DOM nodes, circular objects, proxies that
35
+ * throw on property access. Anything that fails here would fail *inside the
36
+ * app's own `console.error` call*, turning a logged error into a thrown one.
37
+ */
38
+ export declare function describeArg(arg: unknown): string;
39
+ export declare function formatConsoleArgs(args: unknown[]): string;
40
+ /** The first `Error` among the arguments carries the only stack worth keeping. */
41
+ export declare function firstStack(args: unknown[]): string | undefined;
42
+ /** A response is interesting when it is not 2xx. `0` marks a request that never got one. */
43
+ export declare function isFailedStatus(status: number): boolean;
44
+ export interface StateCollector {
45
+ getState(): AnnotationState;
46
+ /** Restore every patched global. Exposed mainly so tests can prove it is reversible. */
47
+ destroy(): void;
48
+ }
49
+ export interface StateCollectorOptions {
50
+ limit: number;
51
+ /** Feature flags are the app's to know; this module does not guess at them. */
52
+ getFeatureFlags: () => Record<string, unknown>;
53
+ now: () => string;
54
+ }
55
+ export declare const defaultStateCollectorOptions: () => StateCollectorOptions;
56
+ /**
57
+ * Patch `console`, `fetch` and `XMLHttpRequest`, and record what goes wrong.
58
+ *
59
+ * Install this as early as the bundle allows — an error thrown before the patch
60
+ * is an error the payload will not carry.
61
+ */
62
+ export declare function installStateCollector(win: Window & typeof globalThis, options?: StateCollectorOptions): StateCollector;
@@ -0,0 +1,62 @@
1
+ import type { AnnotationState } from "./sdk.js";
2
+ /**
3
+ * Page state, captured continuously (A-35).
4
+ *
5
+ * The timing is the whole point. By the time someone notices something is wrong
6
+ * and clicks it, the interesting error has already happened — reading
7
+ * `console` at capture time gets you nothing, because there is nothing to read.
8
+ * So the buffers fill from page load and the click just takes a copy.
9
+ *
10
+ * Which means this code runs on every page, for every user in the enabled
11
+ * environments, in front of the app's own error handling. It is written to be
12
+ * boring: patch, record, call through, never throw.
13
+ */
14
+ /** Twenty each, per the spec. Enough for context, small enough to post. */
15
+ export declare const DEFAULT_LIMIT = 20;
16
+ /**
17
+ * A fixed-size window over the most recent N items.
18
+ *
19
+ * Oldest-out rather than newest-dropped: a page that logs steadily would
20
+ * otherwise pin the buffer to whatever happened at boot and never show the
21
+ * error the reviewer is looking at.
22
+ */
23
+ export declare class RingBuffer<T> {
24
+ #private;
25
+ constructor(limit?: number);
26
+ push(item: T): void;
27
+ /** A copy, so a consumer holding the payload cannot see it mutate underneath them. */
28
+ toArray(): T[];
29
+ get size(): number;
30
+ }
31
+ /**
32
+ * Render one console argument as text, without ever throwing.
33
+ *
34
+ * Console arguments are arbitrary: DOM nodes, circular objects, proxies that
35
+ * throw on property access. Anything that fails here would fail *inside the
36
+ * app's own `console.error` call*, turning a logged error into a thrown one.
37
+ */
38
+ export declare function describeArg(arg: unknown): string;
39
+ export declare function formatConsoleArgs(args: unknown[]): string;
40
+ /** The first `Error` among the arguments carries the only stack worth keeping. */
41
+ export declare function firstStack(args: unknown[]): string | undefined;
42
+ /** A response is interesting when it is not 2xx. `0` marks a request that never got one. */
43
+ export declare function isFailedStatus(status: number): boolean;
44
+ export interface StateCollector {
45
+ getState(): AnnotationState;
46
+ /** Restore every patched global. Exposed mainly so tests can prove it is reversible. */
47
+ destroy(): void;
48
+ }
49
+ export interface StateCollectorOptions {
50
+ limit: number;
51
+ /** Feature flags are the app's to know; this module does not guess at them. */
52
+ getFeatureFlags: () => Record<string, unknown>;
53
+ now: () => string;
54
+ }
55
+ export declare const defaultStateCollectorOptions: () => StateCollectorOptions;
56
+ /**
57
+ * Patch `console`, `fetch` and `XMLHttpRequest`, and record what goes wrong.
58
+ *
59
+ * Install this as early as the bundle allows — an error thrown before the patch
60
+ * is an error the payload will not carry.
61
+ */
62
+ export declare function installStateCollector(win: Window & typeof globalThis, options?: StateCollectorOptions): StateCollector;
@@ -0,0 +1,74 @@
1
+ import { NAME_ATTR, SRC_ATTR, type SourceRef, type TargetInfo } from "./protocol.cjs";
2
+ /**
3
+ * Turning a clicked DOM element into something a coding agent can act on.
4
+ *
5
+ * Everything here is written against the narrowest possible slice of the DOM so
6
+ * it can be tested without a browser — see `ElementLike`. The parts that need a
7
+ * real browser (hit-testing, layout) live in `sdk.ts`.
8
+ */
9
+ /**
10
+ * Re-exported, not declared. They moved to `protocol.ts` (A-186) because the
11
+ * build transform in `@weatherboard/annotation-next` writes what this file
12
+ * reads, and the two packages install separately. Kept exported from here so
13
+ * every existing importer is unaffected.
14
+ */
15
+ export { NAME_ATTR, SRC_ATTR };
16
+ export declare const MAX_TEXT = 200;
17
+ /** Deep trees are common; a chain long enough to be useless is not worth sending. */
18
+ export declare const MAX_CHAIN = 8;
19
+ /** The subset of `Element` this module reads. */
20
+ export interface ElementLike {
21
+ tagName: string;
22
+ id?: string;
23
+ getAttribute(name: string): string | null;
24
+ parentElement: ElementLike | null;
25
+ classList?: {
26
+ value?: string;
27
+ } | ArrayLike<string>;
28
+ textContent?: string | null;
29
+ }
30
+ /**
31
+ * Parse `path/to/File.tsx:42:7` into its parts.
32
+ *
33
+ * Split from the right: the line and column never contain a colon, and a path
34
+ * conceivably might. Returns null rather than a half-parsed ref — a ref we
35
+ * cannot read is worse than no ref, because downstream will believe it.
36
+ */
37
+ export declare function parseSourceRef(value: string | null): SourceRef | null;
38
+ /**
39
+ * Every source ref from `el` upwards, innermost first.
40
+ *
41
+ * Consecutive duplicates are collapsed: one JSX element often produces several
42
+ * nested DOM nodes carrying the same ref, and repeating it says nothing.
43
+ */
44
+ export declare function collectSourceChain(el: ElementLike | null, max?: number): SourceRef[];
45
+ /** The nearest ancestor (or self) carrying a ref, which is the element a human means. */
46
+ export declare function findAnnotatedAncestor(el: ElementLike | null): ElementLike | null;
47
+ /**
48
+ * A CSS-ish path to the element, used only when no ancestor carries a ref.
49
+ *
50
+ * Deliberately not guaranteed unique. It is a hint for a human reading the
51
+ * payload, not a selector we promise to re-resolve — StyleX class names are
52
+ * hashed and change between builds, so a path that looks precise would be
53
+ * quietly wrong on the next deploy. IDs are kept because they survive; classes
54
+ * are capped at two for readability.
55
+ */
56
+ export declare function domPath(el: ElementLike | null, maxDepth?: number): string;
57
+ /** Collapse whitespace and truncate, so the payload carries a label rather than a page. */
58
+ export declare function summariseText(text: string | null | undefined): string | undefined;
59
+ export interface ResolveTargetOptions {
60
+ boundingBox: {
61
+ x: number;
62
+ y: number;
63
+ w: number;
64
+ h: number;
65
+ };
66
+ }
67
+ /**
68
+ * Build the `TargetInfo` for a clicked or hovered element.
69
+ *
70
+ * `source` is the nearest ref, which names the file that writes the markup —
71
+ * for a composed design-system component that is the design-system file, not
72
+ * the call site. `sourceChain` carries the rest so the agent can choose.
73
+ */
74
+ export declare function resolveTarget(el: ElementLike, options: ResolveTargetOptions): TargetInfo;
@@ -0,0 +1,74 @@
1
+ import { NAME_ATTR, SRC_ATTR, type SourceRef, type TargetInfo } from "./protocol.mjs";
2
+ /**
3
+ * Turning a clicked DOM element into something a coding agent can act on.
4
+ *
5
+ * Everything here is written against the narrowest possible slice of the DOM so
6
+ * it can be tested without a browser — see `ElementLike`. The parts that need a
7
+ * real browser (hit-testing, layout) live in `sdk.ts`.
8
+ */
9
+ /**
10
+ * Re-exported, not declared. They moved to `protocol.ts` (A-186) because the
11
+ * build transform in `@weatherboard/annotation-next` writes what this file
12
+ * reads, and the two packages install separately. Kept exported from here so
13
+ * every existing importer is unaffected.
14
+ */
15
+ export { NAME_ATTR, SRC_ATTR };
16
+ export declare const MAX_TEXT = 200;
17
+ /** Deep trees are common; a chain long enough to be useless is not worth sending. */
18
+ export declare const MAX_CHAIN = 8;
19
+ /** The subset of `Element` this module reads. */
20
+ export interface ElementLike {
21
+ tagName: string;
22
+ id?: string;
23
+ getAttribute(name: string): string | null;
24
+ parentElement: ElementLike | null;
25
+ classList?: {
26
+ value?: string;
27
+ } | ArrayLike<string>;
28
+ textContent?: string | null;
29
+ }
30
+ /**
31
+ * Parse `path/to/File.tsx:42:7` into its parts.
32
+ *
33
+ * Split from the right: the line and column never contain a colon, and a path
34
+ * conceivably might. Returns null rather than a half-parsed ref — a ref we
35
+ * cannot read is worse than no ref, because downstream will believe it.
36
+ */
37
+ export declare function parseSourceRef(value: string | null): SourceRef | null;
38
+ /**
39
+ * Every source ref from `el` upwards, innermost first.
40
+ *
41
+ * Consecutive duplicates are collapsed: one JSX element often produces several
42
+ * nested DOM nodes carrying the same ref, and repeating it says nothing.
43
+ */
44
+ export declare function collectSourceChain(el: ElementLike | null, max?: number): SourceRef[];
45
+ /** The nearest ancestor (or self) carrying a ref, which is the element a human means. */
46
+ export declare function findAnnotatedAncestor(el: ElementLike | null): ElementLike | null;
47
+ /**
48
+ * A CSS-ish path to the element, used only when no ancestor carries a ref.
49
+ *
50
+ * Deliberately not guaranteed unique. It is a hint for a human reading the
51
+ * payload, not a selector we promise to re-resolve — StyleX class names are
52
+ * hashed and change between builds, so a path that looks precise would be
53
+ * quietly wrong on the next deploy. IDs are kept because they survive; classes
54
+ * are capped at two for readability.
55
+ */
56
+ export declare function domPath(el: ElementLike | null, maxDepth?: number): string;
57
+ /** Collapse whitespace and truncate, so the payload carries a label rather than a page. */
58
+ export declare function summariseText(text: string | null | undefined): string | undefined;
59
+ export interface ResolveTargetOptions {
60
+ boundingBox: {
61
+ x: number;
62
+ y: number;
63
+ w: number;
64
+ h: number;
65
+ };
66
+ }
67
+ /**
68
+ * Build the `TargetInfo` for a clicked or hovered element.
69
+ *
70
+ * `source` is the nearest ref, which names the file that writes the markup —
71
+ * for a composed design-system component that is the design-system file, not
72
+ * the call site. `sourceChain` carries the rest so the agent can choose.
73
+ */
74
+ export declare function resolveTarget(el: ElementLike, options: ResolveTargetOptions): TargetInfo;
@@ -0,0 +1,74 @@
1
+ import { NAME_ATTR, SRC_ATTR, type SourceRef, type TargetInfo } from "./protocol.js";
2
+ /**
3
+ * Turning a clicked DOM element into something a coding agent can act on.
4
+ *
5
+ * Everything here is written against the narrowest possible slice of the DOM so
6
+ * it can be tested without a browser — see `ElementLike`. The parts that need a
7
+ * real browser (hit-testing, layout) live in `sdk.ts`.
8
+ */
9
+ /**
10
+ * Re-exported, not declared. They moved to `protocol.ts` (A-186) because the
11
+ * build transform in `@weatherboard/annotation-next` writes what this file
12
+ * reads, and the two packages install separately. Kept exported from here so
13
+ * every existing importer is unaffected.
14
+ */
15
+ export { NAME_ATTR, SRC_ATTR };
16
+ export declare const MAX_TEXT = 200;
17
+ /** Deep trees are common; a chain long enough to be useless is not worth sending. */
18
+ export declare const MAX_CHAIN = 8;
19
+ /** The subset of `Element` this module reads. */
20
+ export interface ElementLike {
21
+ tagName: string;
22
+ id?: string;
23
+ getAttribute(name: string): string | null;
24
+ parentElement: ElementLike | null;
25
+ classList?: {
26
+ value?: string;
27
+ } | ArrayLike<string>;
28
+ textContent?: string | null;
29
+ }
30
+ /**
31
+ * Parse `path/to/File.tsx:42:7` into its parts.
32
+ *
33
+ * Split from the right: the line and column never contain a colon, and a path
34
+ * conceivably might. Returns null rather than a half-parsed ref — a ref we
35
+ * cannot read is worse than no ref, because downstream will believe it.
36
+ */
37
+ export declare function parseSourceRef(value: string | null): SourceRef | null;
38
+ /**
39
+ * Every source ref from `el` upwards, innermost first.
40
+ *
41
+ * Consecutive duplicates are collapsed: one JSX element often produces several
42
+ * nested DOM nodes carrying the same ref, and repeating it says nothing.
43
+ */
44
+ export declare function collectSourceChain(el: ElementLike | null, max?: number): SourceRef[];
45
+ /** The nearest ancestor (or self) carrying a ref, which is the element a human means. */
46
+ export declare function findAnnotatedAncestor(el: ElementLike | null): ElementLike | null;
47
+ /**
48
+ * A CSS-ish path to the element, used only when no ancestor carries a ref.
49
+ *
50
+ * Deliberately not guaranteed unique. It is a hint for a human reading the
51
+ * payload, not a selector we promise to re-resolve — StyleX class names are
52
+ * hashed and change between builds, so a path that looks precise would be
53
+ * quietly wrong on the next deploy. IDs are kept because they survive; classes
54
+ * are capped at two for readability.
55
+ */
56
+ export declare function domPath(el: ElementLike | null, maxDepth?: number): string;
57
+ /** Collapse whitespace and truncate, so the payload carries a label rather than a page. */
58
+ export declare function summariseText(text: string | null | undefined): string | undefined;
59
+ export interface ResolveTargetOptions {
60
+ boundingBox: {
61
+ x: number;
62
+ y: number;
63
+ w: number;
64
+ h: number;
65
+ };
66
+ }
67
+ /**
68
+ * Build the `TargetInfo` for a clicked or hovered element.
69
+ *
70
+ * `source` is the nearest ref, which names the file that writes the markup —
71
+ * for a composed design-system component that is the design-system file, not
72
+ * the call site. `sourceChain` carries the rest so the agent can choose.
73
+ */
74
+ export declare function resolveTarget(el: ElementLike, options: ResolveTargetOptions): TargetInfo;
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@weatherboard/annotation",
3
+ "version": "0.1.0",
4
+ "description": "Click an element in a running app, get the source location that rendered it.",
5
+ "keywords": [
6
+ "annotation",
7
+ "feedback",
8
+ "source-maps",
9
+ "review",
10
+ "weatherboard"
11
+ ],
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/Another-Iteration/another-iteration.git",
16
+ "directory": "packages/annotation"
17
+ },
18
+ "type": "module",
19
+ "sideEffects": false,
20
+ "//exports": "IN THE WORKSPACE these point at TypeScript SOURCE, and `publishConfig` below swaps them for `dist/` at publish time. Two reasons. Source in the workspace means no build step stands between an edit here and a typecheck in `apps/manager` — the alternative is a stale `dist/` that typechecks green against code nobody is running, which is the failure this repository keeps finding. And the published shape is the one thing this arrangement cannot exercise, so it is not trusted: `.github/workflows/verify.yml` packs the real tarball, installs it into a bare project and imports it. That job is the only proof this map is right, and it is the reason the map is allowed to differ.",
21
+ "exports": {
22
+ ".": {
23
+ "import": {
24
+ "types": "./dist/index.d.mts",
25
+ "default": "./dist/index.mjs"
26
+ },
27
+ "require": {
28
+ "types": "./dist/index.d.cts",
29
+ "default": "./dist/index.cjs"
30
+ }
31
+ },
32
+ "./protocol": {
33
+ "import": {
34
+ "types": "./dist/protocol.d.mts",
35
+ "default": "./dist/protocol.mjs"
36
+ },
37
+ "require": {
38
+ "types": "./dist/protocol.d.cts",
39
+ "default": "./dist/protocol.cjs"
40
+ }
41
+ },
42
+ "./host": {
43
+ "import": {
44
+ "types": "./dist/host.d.mts",
45
+ "default": "./dist/host.mjs"
46
+ },
47
+ "require": {
48
+ "types": "./dist/host.d.cts",
49
+ "default": "./dist/host.cjs"
50
+ }
51
+ },
52
+ "./package.json": "./package.json"
53
+ },
54
+ "main": "./dist/index.cjs",
55
+ "types": "./dist/index.d.ts",
56
+ "publishConfig": {
57
+ "access": "public",
58
+ "//exports": "`types` INSIDE each condition, not above it. A single declaration shared by both formats hands a `require()` consumer types describing a module shape they do not get; and the ESM and CJS declarations differ in the extension their relative imports carry, which is what `moduleResolution: node16` checks. `build.mjs` explains the three spellings."
59
+ },
60
+ "files": [
61
+ "dist",
62
+ "README.md",
63
+ "ADOPTING.md",
64
+ "LICENSE"
65
+ ],
66
+ "//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.",
67
+ "sizeBudget": {
68
+ "protocol": 650,
69
+ "index": 5400,
70
+ "host": 660
71
+ },
72
+ "engines": {
73
+ "node": ">=18"
74
+ },
75
+ "devDependencies": {
76
+ "@playwright/test": "^1.62.1",
77
+ "@types/node": "^22.0.0",
78
+ "esbuild": "^0.28.2",
79
+ "tsx": "^4.21.0",
80
+ "typescript": "~5.7.2"
81
+ },
82
+ "scripts": {
83
+ "build": "node build.mjs",
84
+ "typecheck": "tsc --noEmit",
85
+ "//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.",
86
+ "lint": "eslint . --max-warnings 0",
87
+ "test": "tsx --test src/*.test.ts",
88
+ "e2e": "playwright test",
89
+ "//smoke": "The consumer test — pack the real tarball, install it outside the workspace, import it four ways. NOT part of `pnpm test`, and not because it is slow: it shells out to `npm install`, which needs the network, and `verify` is forbidden to acquire one. It runs as its own CI job. See the header of smoke.mjs for why nothing else in this repository can catch what it catches.",
90
+ "smoke": "node smoke.mjs",
91
+ "//prepack": "Runs on `npm pack` and `npm publish`, which is what makes the `publishConfig` swap above safe: the tarball cannot be built without `dist/` having just been produced from this source. Without it the two could disagree and the failure would be a published package pointing at files that are not there."
92
+ },
93
+ "module": "./dist/index.mjs"
94
+ }