@weatherboard/annotation 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/ADOPTING.md +96 -0
  2. package/README.md +9 -1
  3. package/dist/anchor.cjs +2 -0
  4. package/dist/anchor.cjs.map +7 -0
  5. package/dist/anchor.d.cts +295 -0
  6. package/dist/anchor.d.mts +295 -0
  7. package/dist/anchor.d.ts +295 -0
  8. package/dist/anchor.mjs +2 -0
  9. package/dist/anchor.mjs.map +7 -0
  10. package/dist/arming.d.cts +30 -2
  11. package/dist/arming.d.mts +30 -2
  12. package/dist/arming.d.ts +30 -2
  13. package/dist/host.cjs +1 -1
  14. package/dist/host.cjs.map +1 -1
  15. package/dist/host.mjs.map +1 -1
  16. package/dist/index.cjs +4 -4
  17. package/dist/index.cjs.map +3 -3
  18. package/dist/index.d.cts +2 -2
  19. package/dist/index.d.mts +2 -2
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.mjs +4 -4
  22. package/dist/index.mjs.map +3 -3
  23. package/dist/markers.cjs +64 -0
  24. package/dist/markers.cjs.map +7 -0
  25. package/dist/markers.d.cts +283 -0
  26. package/dist/markers.d.mts +283 -0
  27. package/dist/markers.d.ts +283 -0
  28. package/dist/markers.mjs +64 -0
  29. package/dist/markers.mjs.map +7 -0
  30. package/dist/protocol.cjs +1 -1
  31. package/dist/protocol.cjs.map +3 -3
  32. package/dist/protocol.d.cts +29 -0
  33. package/dist/protocol.d.mts +29 -0
  34. package/dist/protocol.d.ts +29 -0
  35. package/dist/protocol.mjs +1 -1
  36. package/dist/protocol.mjs.map +3 -3
  37. package/dist/read.cjs +2 -0
  38. package/dist/read.cjs.map +7 -0
  39. package/dist/read.d.cts +194 -0
  40. package/dist/read.d.mts +194 -0
  41. package/dist/read.d.ts +194 -0
  42. package/dist/read.mjs +2 -0
  43. package/dist/read.mjs.map +7 -0
  44. package/dist/transport.d.cts +7 -1
  45. package/dist/transport.d.mts +7 -1
  46. package/dist/transport.d.ts +7 -1
  47. package/package.json +48 -5
package/dist/host.mjs.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/protocol.ts", "../src/host.ts"],
4
- "sourcesContent": ["/**\n * The annotation postMessage protocol (A-36).\n *\n * Mirrors `docs/contracts/types.ts`. That file is the\n * cross-component contract; this is the runtime copy the product bundle ships,\n * because the product cannot import from a docs directory. Changing a shape\n * here is a change to both \u2014 say so in the PR.\n */\n\nexport const SDK_VERSION = \"1\";\n\n/**\n * ===========================================================================\n * THE ATTRIBUTES \u2014 THE OTHER WIRE (A-186)\n * ===========================================================================\n *\n * These live in `protocol.ts`, not in `target.ts` where they are read, because\n * they are a contract between TWO PACKAGES that a consumer installs separately:\n * `@weatherboard/annotation-next` WRITES them at build time and this package\n * READS them at runtime. Two spellings of `data-src` is not a bug anybody\n * catches \u2014 the transform runs, the attributes appear, and every capture comes\n * back with no source ref at all, which is indistinguishable from the transform\n * not running.\n *\n * `annotation-next` imports these from `@weatherboard/annotation/protocol`\n * rather than declaring its own, which is why the subpath has no DOM in it: a\n * build tool importing the page SDK to learn two strings would be absurd.\n */\nexport const SRC_ATTR = \"data-src\";\nexport const NAME_ATTR = \"data-src-name\";\n\nexport interface SourceRef {\n file: string;\n line: number;\n column?: number;\n}\n\nexport interface BuildInfo {\n /** Mandatory. Feedback not pinned to a revision is ambiguous the moment someone merges. */\n gitSha: string;\n /**\n * Where this build came from.\n *\n * `\"development\"` was added in A-37, when the SDK was first mounted in a\n * real app and the two halves turned out not to agree: `resolveBuildInfo` in\n * `resolveBuildInfo` in the product's own Next config package \u2014 the module\n * that stamps the build-env variable, and the only producer of this field \u2014\n * emits\n * `production | preview | development`, mirroring `VERCEL_ENV`. This union\n * instead offered `\"staging\"`, which nothing produces: a staging deploy IS a\n * Vercel preview and reports itself as one. Widening rather than forcing the\n * mount to lie: a locally-annotated build calling itself a preview would put\n * someone's laptop in the corpus as though it were a deployment.\n */\n env: \"production\" | \"preview\" | \"staging\" | \"development\";\n branch?: string;\n /** Set when reviewing an agent's preview deploy, so feedback routes to that run's session. */\n previewRunId?: string;\n}\n\nexport interface ConsoleEntry {\n level: \"error\" | \"warn\";\n message: string;\n stack?: string;\n at: string;\n}\n\nexport interface NetworkEntry {\n url: string;\n method: string;\n status: number;\n at: string;\n}\n\nexport interface TargetInfo {\n /**\n * What the reviewer selected \u2014 one element, or an area (A-179).\n *\n * -------------------------------------------------------------------------\n * WHY THIS EXISTS RATHER THAN BEING INFERRED\n * -------------------------------------------------------------------------\n *\n * A region capture and an element capture are the same SHAPE: a bounding box,\n * a chain of source refs, a DOM path. Nothing in the values distinguishes\n * them, and the two mean opposite things about the chain \u2014 for an element it\n * is ANCESTORS, innermost first, so entry two contains entry one; for a\n * region it is SIBLINGS the rectangle happened to cross, which contain\n * nothing of each other.\n *\n * A reader that guesses will describe a region's siblings as a call-site\n * chain, which is a confident false statement about the page. `describeCapture`\n * hands that description to a model that writes acceptance criteria from it.\n *\n * -------------------------------------------------------------------------\n * WHY IT IS OPTIONAL, AND WHY ABSENT MEANS `element`\n * -------------------------------------------------------------------------\n *\n * The two halves of this protocol live in different repositories and deploy\n * independently (see the note on the legacy parameter names below). Every\n * capture in the store today, and every capture from a product bundle that\n * predates region selection, has no such field \u2014 and all of them are element\n * captures. Absent therefore has to mean `element`, or this field would\n * retroactively make the entire history ambiguous.\n *\n * Required-with-a-default would have the same effect and a worse failure: an\n * older bundle omits it, the type says it is there, and every reader believes\n * a value nobody sent.\n */\n selection?: \"element\" | \"region\";\n source?: SourceRef;\n /**\n * Every `data-src` from the clicked element up to the document root, innermost\n * first \u2014 an addition to the contract, and the reason is structural.\n *\n * Only host elements carry a ref (A-33), so `source` names the file that\n * *writes the markup*. For a composed design-system component that is\n * `MediaCard.tsx`, not the `GuideDetailCard.tsx` a human would say they\n * clicked. The chain is what lets the PM agent and the coding agent see both,\n * and pick the call site over the leaf when the feedback is about content\n * rather than the component itself.\n */\n sourceChain?: SourceRef[];\n componentName?: string;\n /** CSS selector fallback, for when no ancestor carries a ref at all. */\n domPath: string;\n boundingBox: { x: number; y: number; w: number; h: number };\n /** Truncated to 200 characters. */\n textContent?: string;\n}\n\n/**\n * What kind of capture this is, with the default applied once (A-189).\n *\n * `selection` is optional and **absent means `element`** \u2014 every capture taken\n * before A-179, and everything the managed product's live bundle emits today,\n * has no such field. Any other default would retroactively reclassify the whole\n * history as regions, silently.\n *\n * A function rather than a documented convention because the default is the\n * kind of thing two readers implement slightly differently and neither notices:\n * one writes `=== \"region\"`, another writes `?? \"element\"`, a third forgets and\n * treats `undefined` as falsy in a branch that meant something else. This is\n * the protocol's answer, and `compatibility.test.ts` replays every recorded\n * payload through it.\n */\nexport function selectionOf(target: Pick<TargetInfo, \"selection\">): \"element\" | \"region\" {\n return target.selection === \"region\" ? \"region\" : \"element\";\n}\n\nexport interface AnnotationPayload {\n schemaVersion: 1;\n capturedAt: string;\n build: BuildInfo;\n page: {\n url: string;\n /** The matched route pattern, not the resolved URL. */\n route: string;\n viewport: { w: number; h: number; dpr: number };\n userAgent: string;\n };\n target: TargetInfo;\n state: {\n consoleErrors: ConsoleEntry[];\n failedRequests: NetworkEntry[];\n featureFlags: Record<string, unknown>;\n };\n screenshot?: { dataUrl: string; region: \"element\" | \"viewport\" };\n /** The human's words, verbatim. The parent frame fills this in; we never rewrite it. */\n comment: string;\n}\n\nexport type ParentToFrame =\n | { type: \"annotate:enable\" }\n | { type: \"annotate:disable\" }\n | { type: \"annotate:ping\" };\n\nexport type FrameToParent =\n | { type: \"annotate:ready\"; sdkVersion: string; build: BuildInfo }\n | { type: \"annotate:hover\"; target: TargetInfo }\n | { type: \"annotate:capture\"; payload: AnnotationPayload }\n | { type: \"annotate:cancel\" };\n\nconst PARENT_TYPES = new Set([\"annotate:enable\", \"annotate:disable\", \"annotate:ping\"]);\n\n/**\n * Is this a message we recognise?\n *\n * Origin is checked separately and first \u2014 a well-formed message from the wrong\n * origin is the case this guard must not be trusted to catch.\n */\nexport function isParentMessage(data: unknown): data is ParentToFrame {\n if (typeof data !== \"object\" || data === null) return false;\n const type = (data as { type?: unknown }).type;\n return typeof type === \"string\" && PARENT_TYPES.has(type);\n}\n\n/**\n * Reject wildcards outright rather than treating them as \"any origin\".\n *\n * `postMessage(msg, \"*\")` broadcasts the payload to whatever happens to be\n * framing us, and an annotation payload carries page content, console errors and\n * failed request URLs. The spec's rule is no wildcards in either direction; this\n * makes it impossible to configure one by accident.\n */\nexport function assertConcreteOrigin(origin: string): string {\n if (!origin || origin === \"*\") {\n throw new Error(\n \"[annotation] a concrete parent origin is required; '*' would broadcast \" +\n \"annotation payloads to any embedder.\",\n );\n }\n return origin;\n}\n\n/**\n * The query parameter that arms the SDK in a managed product, and the cookie\n * that records it.\n *\n * THESE LIVE IN THE PROTOCOL BECAUSE THE TWO HALVES ARE NOW IN DIFFERENT\n * REPOSITORIES. The managed product's middleware looks for the parameter and\n * sets the cookie; the management app appends the parameter and the SDK reads\n * the cookie. While both halves were one monorepo, a test could read the other\n * side's source and assert the strings matched \u2014 that gate does not survive a\n * repository split, and a mismatch produces no error at all: the frame loads,\n * the cookie is never set, the SDK never boots, and the reviewer sees \"waiting\n * for the page to announce itself\" with nothing to go on.\n *\n * So they stop being two spellings that agree and become one exported\n * constant. A managed product that imports this package cannot drift from it.\n */\nexport const ANNOTATION_TOKEN_PARAM = \"ai_annotate\";\nexport const ANNOTATION_ARMED_COOKIE = \"ai_annotate_armed\";\n\n/**\n * The CAPTURE token's query parameter (A-240).\n *\n * -------------------------------------------------------------------------\n * WHY IT IS HERE AND NOT WHERE IT IS WRITTEN\n * -------------------------------------------------------------------------\n *\n * A second name on the same wire, and a second credential \u2014 see\n * `docs/plans/first-party-annotation.md` \u00A7 *Authentication*. `ai_annotate`\n * answers *should this page offer an annotation UI at all*; this one answers\n * *may this capture become an issue*. Different secrets, different lifetimes,\n * different blast radii, and deliberately not folded into one token.\n *\n * It lives beside `ANNOTATION_TOKEN_PARAM` for that constant's own reason,\n * stated at length above: the console WRITES it into a deep link and the\n * product's bundle READS it, the two halves ship from different repositories,\n * and a mismatch produces no error anywhere \u2014 the reviewer writes a post-it,\n * clicks send, and the ingest rejects an unsigned capture. One exported\n * constant is the only thing that makes those two spellings unable to drift.\n *\n * It has no legacy spelling because it has never had another one: nothing has\n * shipped it yet. It is exported here BEFORE its writer needs it precisely so\n * the writer never has a reason to spell it itself.\n */\nexport const CAPTURE_TOKEN_PARAM = \"ai_capture\";\n\n/**\n * The run under review, when the console knows of one (A-241).\n *\n * Third name on the same wire, here for the reason the two above are: the\n * console writes it and the product's bundle reads it, from two repositories.\n * `apps/manager/src/lib/request-changes.ts` spells it itself today and its own\n * comment asks for exactly this \u2014 the constant to move into the package so the\n * writer has no reason to spell it. **That deletion is not this slice's**\n * (A-243/A-244 are merged and settled), so for now there are two definitions of\n * one string, and `protocol.test.ts` is the only thing standing between them\n * and a silent divergence. Nothing about it is signed: the ingest already\n * treats it as attacker-supplied text and files on it rather than routing on it.\n */\nexport const REVIEW_RUN_PARAM = \"ai_run\";\n\n/**\n * The arming cookie, as a `Set-Cookie` value (A-241).\n *\n * ===========================================================================\n * IT GOT SIMPLER BY MOVING FIRST-PARTY, AND THAT IS THE WHOLE POINT\n * ===========================================================================\n *\n * Framed, this had to be `SameSite=None; Secure` with no `Partitioned` \u2014 a\n * genuine cross-site cookie, which A-211 records would have broken on its own\n * under Chrome's third-party cookie work, independently of the auth problem\n * that killed the frame. Top-level at the product's own origin it is an\n * ordinary cookie and none of that applies.\n *\n * Each attribute, and why:\n *\n * - **`SameSite=Lax`.** The reviewer arrives by clicking *Request changes* in\n * the console, which is a top-level cross-site GET navigation \u2014 the one case\n * `Lax` still sends on. `Strict` would withhold the cookie on that very first\n * render, so the toolbar would be missing on the page the link opened and\n * appear only after an internal click; that reads as a broken deploy.\n * - **`Secure`.** It is a marker rather than a credential, but a marker that\n * travels in clear is a marker an attacker on the path can set.\n * - **`Path=/`.** Arming survives the reviewer navigating the whole app, which\n * is the point of a cookie rather than a URL parameter.\n * - **No `Max-Age` / `Expires`.** Session-scoped: closing the tab disarms.\n *\n * ===========================================================================\n * WHAT IS *NOT* HERE: `HttpOnly`, AND THE TOKEN\n * ===========================================================================\n *\n * No `HttpOnly`, deliberately. Page script both READS this (`isAnnotationArmed`\n * decides whether a toolbar exists) and CLEARS it (*Done* disarms). An\n * `HttpOnly` arming cookie makes *Done* impossible from the page, which is the\n * one control the reviewer has.\n *\n * That is affordable only because THE VALUE IS A MARKER AND NEVER THE TOKEN.\n * The value is a constant and there is no argument that supplies one: the\n * middleware compares the URL parameter against its secret and then calls this,\n * and what it stores is *yes*, not *what was presented*. The single parameter\n * is the cookie's NAME, and it exists only so a product mid-migration can write\n * the legacy spelling \u2014 it is not a value slot wearing a different label, and\n * `arming.test.ts` pins which side of the `=` it lands on.\n *\n * This is a shape argument, not a guarantee about callers. Nothing stops a\n * determined caller passing a secret as the name; what the signature removes is\n * the ACCIDENT \u2014 the middleware author with a verified token in hand and one\n * parameter available, reaching for the obvious.\n */\nexport const ANNOTATION_ARMED_VALUE = \"1\";\n\nexport function armedCookie(name: string = ANNOTATION_ARMED_COOKIE): string {\n return `${name}=${ANNOTATION_ARMED_VALUE}; Path=/; SameSite=Lax; Secure`;\n}\n\n/**\n * The same cookie, expired \u2014 what *Done* and a disarm write.\n *\n * `Max-Age=0` AND an `Expires` in the past, because they are read by different\n * things and the pair is what every browser honours. The path must match the\n * one set above or the browser stores a second cookie and the first survives \u2014\n * a *Done* button that does nothing and reports success.\n */\nexport function clearedCookie(name: string): string {\n return `${name}=; Path=/; SameSite=Lax; Secure; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\n\n/**\n * The names these used to have (A-98).\n *\n * -------------------------------------------------------------------------\n * WHY THE RENAME, AND WHY IT COULD NOT JUST BE DONE\n * -------------------------------------------------------------------------\n *\n * They were `pits_annotate` and `pits_annotate_armed` \u2014 one managed product's\n * initialism, in a contract every managed product's bundle has to implement.\n * Onboarding a second product meant asking an unrelated codebase to adopt the\n * first product's name in its cookies and its query strings. That is the bug\n * A-98 is about, and it is a bug in a wire protocol rather than in a variable\n * name, which is what makes it expensive.\n *\n * The two halves live in DIFFERENT REPOSITORIES and deploy independently. A\n * clean rename is therefore a breaking change to every product bundle\n * simultaneously, and its failure mode is silence: the frame loads, the cookie\n * is never set, the SDK never boots, and the reviewer sees \"waiting for the\n * page to announce itself\" with nothing to go on. Nothing throws, nothing logs,\n * and the natural reading is that the agent broke the site.\n *\n * -------------------------------------------------------------------------\n * THE MIGRATION: READ BOTH, WRITE BOTH, THEN STOP WRITING THE OLD ONE\n * -------------------------------------------------------------------------\n *\n * 1. **Now.** Every reader accepts either name. Every writer emits BOTH, so a\n * product still running the old bundle arms on the legacy name and one\n * running the new bundle arms on the new one. Deploy order stops mattering,\n * which is the whole point \u2014 there is no window in which a reviewer sees the\n * silent failure above.\n * 2. **After every managed product's bundle is on this version** (A-102). Drop\n * the legacy names from the writers, then from the readers. Two steps, not\n * one: a reader that stops accepting the old name before the writers stop\n * emitting it re-creates the outage in the other direction.\n *\n * The legacy names are exported rather than inlined so the deletion in step 2\n * is `git grep LEGACY_` rather than an archaeology exercise.\n */\nexport const LEGACY_ANNOTATION_TOKEN_PARAM = \"pits_annotate\";\nexport const LEGACY_ANNOTATION_ARMED_COOKIE = \"pits_annotate_armed\";\n\n/**\n * ===========================================================================\n * AND THE NAMES ARE NOT CHANGING AGAIN FOR THE PUBLISHED PACKAGE (A-184)\n * ===========================================================================\n *\n * When this becomes `@weatherboard/annotation` the obvious next thought is that\n * the wire names should match the package \u2014 `weatherboard_annotate`. They\n * should not, and the reason is the migration described above rather than\n * taste.\n *\n * That migration is STILL IN FLIGHT. Readers accept both spellings; writers\n * emit both; step 2 waits on every managed product's bundle being current.\n * A third spelling introduced now does not replace the second, it joins it:\n * every reader accepts three names, every writer emits three, and the window in\n * which a product can be on any of them widens instead of closing.\n *\n * The cost is not the code. It is that each rename buys another instance of the\n * failure this file exists to prevent \u2014 the frame loads, the cookie is never\n * set, the SDK never boots, and the reviewer sees \"waiting for the page to\n * announce itself\" with nothing to go on. Twice is a migration. Three times is\n * a habit, and the third one would be paid by consumers who never asked for\n * either.\n *\n * `ai_annotate` is already product-neutral, which was the entire point of A-98.\n * A published package needs names that name no product; it does not need names\n * that name the package. Nobody reading a query string cares which npm scope\n * the library came from.\n *\n * The one real objection is collision: `ai_` is a common prefix and some other\n * tool could plausibly use it. That is survivable by construction \u2014 arming\n * requires the parameter to carry a token the product compares exactly, so a\n * coincidental `?ai_annotate=` from an unrelated tool arms nothing. A colliding\n * COOKIE cannot false-positive either; `isAnnotationArmed` matches on the\n * delimiter rather than by substring, for exactly this reason.\n *\n * If a rename is ever genuinely warranted, it happens ONCE, after step 2 has\n * landed and the reader accepts one name again \u2014 not layered on top of an\n * unfinished one.\n */\n\n/** Both spellings of the query parameter, current first. */\nexport const ANNOTATION_TOKEN_PARAMS = [\n ANNOTATION_TOKEN_PARAM,\n LEGACY_ANNOTATION_TOKEN_PARAM,\n] as const;\n\n/** Both spellings of the cookie, current first. */\nexport const ANNOTATION_ARMED_COOKIES = [\n ANNOTATION_ARMED_COOKIE,\n LEGACY_ANNOTATION_ARMED_COOKIE,\n] as const;\n\n/**\n * Whether a cookie jar carries the armed cookie, under either name.\n *\n * Delimiter-aware rather than a substring test: a cookie named\n * `not_ai_annotate_armed`, or another cookie whose VALUE contains the name,\n * satisfies `includes` \u2014 and arming an annotation channel on a coincidence is\n * not a failure anyone would think to look for.\n */\nexport function isAnnotationArmed(cookie: string): boolean {\n return cookie\n .split(\";\")\n .some((entry) => {\n const trimmed = entry.trimStart();\n return ANNOTATION_ARMED_COOKIES.some((name) => trimmed.startsWith(`${name}=`));\n });\n}\n\n/**\n * The arming token from a URL's query string, under either name.\n *\n * Current name wins when both are present. They should never disagree \u2014 the\n * management app writes both from one value \u2014 but if they ever do, the one this\n * version of the protocol owns is the one to believe.\n */\nexport function readAnnotationToken(params: URLSearchParams): string | null {\n for (const name of ANNOTATION_TOKEN_PARAMS) {\n const value = params.get(name);\n if (value !== null && value.length > 0) return value;\n }\n return null;\n}\n", "import {\n assertConcreteOrigin,\n type AnnotationPayload,\n type BuildInfo,\n type FrameToParent,\n type ParentToFrame,\n type TargetInfo,\n} from \"./protocol\";\n\n/**\n * The parent half of the annotation channel (A-30).\n *\n * WHY THIS LIVES IN THE SDK AND NOT IN `apps/admin`.\n *\n * The protocol is this package's domain. It already owns the message union,\n * `isParentMessage` and `assertConcreteOrigin`; a second implementation of the\n * same handshake, written host-side because that is where the UI is, is exactly\n * the shape SHARED-PACKAGES.md warns about. The two halves of a protocol\n * drifting apart is not a hypothetical \u2014 it is the normal outcome when they are\n * maintained in different packages by different changes.\n *\n * Concretely: the frame's `sdk.ts` and this file are the only two things that\n * can disagree about what `annotate:capture` means, and keeping them in one\n * package means a change to the union is a compile error in both.\n *\n * Admin gets the UI. This gets the wire.\n */\n\nexport interface AnnotationHostOptions {\n /**\n * The window being framed. Passed in rather than looked up, because the host\n * knows which iframe it means and this module must never guess.\n */\n frame: Window;\n /**\n * The frame's origin \u2014 where the product is served from. Required and\n * concrete, checked by `assertConcreteOrigin`: posting to `*` would hand an\n * annotation payload, including console errors from a real session, to\n * whatever happens to be loaded.\n */\n frameOrigin: string;\n /** The window that receives messages. Injected so tests need no real iframe. */\n hostWindow: Window;\n\n onReady(info: { sdkVersion: string; build: BuildInfo }): void;\n onHover(target: TargetInfo): void;\n onCapture(payload: AnnotationPayload): void;\n onCancel(): void;\n}\n\nexport interface AnnotationHost {\n enable(): void;\n disable(): void;\n ping(): void;\n /** True once the frame has announced itself. */\n isReady(): boolean;\n destroy(): void;\n}\n\nconst FRAME_TYPES = new Set([\n \"annotate:ready\",\n \"annotate:hover\",\n \"annotate:capture\",\n \"annotate:cancel\",\n]);\n\n/**\n * Is this one of ours?\n *\n * The mirror of `isParentMessage`, and it carries the same warning: origin is\n * checked separately and FIRST. A well-formed message from the wrong origin is\n * precisely the case this guard must not be trusted to catch \u2014 anything can\n * post `{type: \"annotate:capture\"}` to a window.\n */\nexport function isFrameMessage(data: unknown): data is FrameToParent {\n if (typeof data !== \"object\" || data === null) return false;\n const type = (data as { type?: unknown }).type;\n return typeof type === \"string\" && FRAME_TYPES.has(type);\n}\n\nexport function createAnnotationHost(options: AnnotationHostOptions): AnnotationHost {\n const frameOrigin = assertConcreteOrigin(options.frameOrigin);\n let ready = false;\n let destroyed = false;\n\n const post = (message: ParentToFrame) => {\n if (destroyed) return;\n // Targeted, never `*`. `enable` is not sensitive in itself, but a habit of\n // posting to `*` is what eventually posts something that is.\n options.frame.postMessage(message, frameOrigin);\n };\n\n const onMessage = (event: MessageEvent) => {\n /**\n * Origin first, and by exact match.\n *\n * Not `endsWith`, not a prefix: for a product served from `example.com`,\n * `https://evil-example.com` ends with nothing suspicious and\n * `https://example.com.evil.test` starts with the right characters. The browser hands us a normalised origin, so an\n * equality test is both correct and the only one that cannot be widened by\n * a hostile hostname.\n */\n if (event.origin !== frameOrigin) return;\n\n /**\n * And the source must be the frame we are talking to.\n *\n * Origin alone is not enough: any other window on the same origin \u2014 a popup\n * the product opened, another iframe \u2014 can post a well-formed message from\n * an origin that matches. This is the check that makes \"the frame said so\"\n * mean the frame.\n */\n if (event.source !== options.frame) return;\n\n if (!isFrameMessage(event.data)) return;\n\n switch (event.data.type) {\n case \"annotate:ready\":\n ready = true;\n options.onReady({ sdkVersion: event.data.sdkVersion, build: event.data.build });\n return;\n case \"annotate:hover\":\n options.onHover(event.data.target);\n return;\n case \"annotate:capture\":\n options.onCapture(event.data.payload);\n return;\n case \"annotate:cancel\":\n options.onCancel();\n return;\n }\n };\n\n options.hostWindow.addEventListener(\"message\", onMessage);\n\n return {\n enable: () => post({ type: \"annotate:enable\" }),\n disable: () => post({ type: \"annotate:disable\" }),\n ping: () => post({ type: \"annotate:ping\" }),\n isReady: () => ready,\n destroy() {\n destroyed = true;\n ready = false;\n options.hostWindow.removeEventListener(\"message\", onMessage);\n },\n };\n}\n"],
4
+ "sourcesContent": ["/**\n * The annotation postMessage protocol (A-36).\n *\n * Mirrors `docs/contracts/types.ts`. That file is the\n * cross-component contract; this is the runtime copy the product bundle ships,\n * because the product cannot import from a docs directory. Changing a shape\n * here is a change to both \u2014 say so in the PR.\n */\n\nexport const SDK_VERSION = \"1\";\n\n/**\n * ===========================================================================\n * THE ATTRIBUTES \u2014 THE OTHER WIRE (A-186)\n * ===========================================================================\n *\n * These live in `protocol.ts`, not in `target.ts` where they are read, because\n * they are a contract between TWO PACKAGES that a consumer installs separately:\n * `@weatherboard/annotation-next` WRITES them at build time and this package\n * READS them at runtime. Two spellings of `data-src` is not a bug anybody\n * catches \u2014 the transform runs, the attributes appear, and every capture comes\n * back with no source ref at all, which is indistinguishable from the transform\n * not running.\n *\n * `annotation-next` imports these from `@weatherboard/annotation/protocol`\n * rather than declaring its own, which is why the subpath has no DOM in it: a\n * build tool importing the page SDK to learn two strings would be absurd.\n */\nexport const SRC_ATTR = \"data-src\";\nexport const NAME_ATTR = \"data-src-name\";\n\nexport interface SourceRef {\n file: string;\n line: number;\n column?: number;\n}\n\nexport interface BuildInfo {\n /** Mandatory. Feedback not pinned to a revision is ambiguous the moment someone merges. */\n gitSha: string;\n /**\n * Where this build came from.\n *\n * `\"development\"` was added in A-37, when the SDK was first mounted in a\n * real app and the two halves turned out not to agree: `resolveBuildInfo` in\n * `resolveBuildInfo` in the product's own Next config package \u2014 the module\n * that stamps the build-env variable, and the only producer of this field \u2014\n * emits\n * `production | preview | development`, mirroring `VERCEL_ENV`. This union\n * instead offered `\"staging\"`, which nothing produces: a staging deploy IS a\n * Vercel preview and reports itself as one. Widening rather than forcing the\n * mount to lie: a locally-annotated build calling itself a preview would put\n * someone's laptop in the corpus as though it were a deployment.\n */\n env: \"production\" | \"preview\" | \"staging\" | \"development\";\n branch?: string;\n /** Set when reviewing an agent's preview deploy, so feedback routes to that run's session. */\n previewRunId?: string;\n}\n\nexport interface ConsoleEntry {\n level: \"error\" | \"warn\";\n message: string;\n stack?: string;\n at: string;\n}\n\nexport interface NetworkEntry {\n url: string;\n method: string;\n status: number;\n at: string;\n}\n\nexport interface TargetInfo {\n /**\n * What the reviewer selected \u2014 one element, or an area (A-179).\n *\n * -------------------------------------------------------------------------\n * WHY THIS EXISTS RATHER THAN BEING INFERRED\n * -------------------------------------------------------------------------\n *\n * A region capture and an element capture are the same SHAPE: a bounding box,\n * a chain of source refs, a DOM path. Nothing in the values distinguishes\n * them, and the two mean opposite things about the chain \u2014 for an element it\n * is ANCESTORS, innermost first, so entry two contains entry one; for a\n * region it is SIBLINGS the rectangle happened to cross, which contain\n * nothing of each other.\n *\n * A reader that guesses will describe a region's siblings as a call-site\n * chain, which is a confident false statement about the page. `describeCapture`\n * hands that description to a model that writes acceptance criteria from it.\n *\n * -------------------------------------------------------------------------\n * WHY IT IS OPTIONAL, AND WHY ABSENT MEANS `element`\n * -------------------------------------------------------------------------\n *\n * The two halves of this protocol live in different repositories and deploy\n * independently (see the note on the legacy parameter names below). Every\n * capture in the store today, and every capture from a product bundle that\n * predates region selection, has no such field \u2014 and all of them are element\n * captures. Absent therefore has to mean `element`, or this field would\n * retroactively make the entire history ambiguous.\n *\n * Required-with-a-default would have the same effect and a worse failure: an\n * older bundle omits it, the type says it is there, and every reader believes\n * a value nobody sent.\n */\n selection?: \"element\" | \"region\";\n source?: SourceRef;\n /**\n * Every `data-src` from the clicked element up to the document root, innermost\n * first \u2014 an addition to the contract, and the reason is structural.\n *\n * Only host elements carry a ref (A-33), so `source` names the file that\n * *writes the markup*. For a composed design-system component that is\n * `MediaCard.tsx`, not the `GuideDetailCard.tsx` a human would say they\n * clicked. The chain is what lets the PM agent and the coding agent see both,\n * and pick the call site over the leaf when the feedback is about content\n * rather than the component itself.\n */\n sourceChain?: SourceRef[];\n componentName?: string;\n /** CSS selector fallback, for when no ancestor carries a ref at all. */\n domPath: string;\n boundingBox: { x: number; y: number; w: number; h: number };\n /** Truncated to 200 characters. */\n textContent?: string;\n}\n\n/**\n * What kind of capture this is, with the default applied once (A-189).\n *\n * `selection` is optional and **absent means `element`** \u2014 every capture taken\n * before A-179, and everything the managed product's live bundle emits today,\n * has no such field. Any other default would retroactively reclassify the whole\n * history as regions, silently.\n *\n * A function rather than a documented convention because the default is the\n * kind of thing two readers implement slightly differently and neither notices:\n * one writes `=== \"region\"`, another writes `?? \"element\"`, a third forgets and\n * treats `undefined` as falsy in a branch that meant something else. This is\n * the protocol's answer, and `compatibility.test.ts` replays every recorded\n * payload through it.\n */\nexport function selectionOf(target: Pick<TargetInfo, \"selection\">): \"element\" | \"region\" {\n return target.selection === \"region\" ? \"region\" : \"element\";\n}\n\nexport interface AnnotationPayload {\n schemaVersion: 1;\n capturedAt: string;\n build: BuildInfo;\n page: {\n url: string;\n /** The matched route pattern, not the resolved URL. */\n route: string;\n viewport: { w: number; h: number; dpr: number };\n userAgent: string;\n };\n target: TargetInfo;\n state: {\n consoleErrors: ConsoleEntry[];\n failedRequests: NetworkEntry[];\n featureFlags: Record<string, unknown>;\n };\n screenshot?: { dataUrl: string; region: \"element\" | \"viewport\" };\n /** The human's words, verbatim. The parent frame fills this in; we never rewrite it. */\n comment: string;\n}\n\nexport type ParentToFrame =\n | { type: \"annotate:enable\" }\n | { type: \"annotate:disable\" }\n | { type: \"annotate:ping\" };\n\nexport type FrameToParent =\n | { type: \"annotate:ready\"; sdkVersion: string; build: BuildInfo }\n | { type: \"annotate:hover\"; target: TargetInfo }\n | { type: \"annotate:capture\"; payload: AnnotationPayload }\n | { type: \"annotate:cancel\" };\n\nconst PARENT_TYPES = new Set([\"annotate:enable\", \"annotate:disable\", \"annotate:ping\"]);\n\n/**\n * Is this a message we recognise?\n *\n * Origin is checked separately and first \u2014 a well-formed message from the wrong\n * origin is the case this guard must not be trusted to catch.\n */\nexport function isParentMessage(data: unknown): data is ParentToFrame {\n if (typeof data !== \"object\" || data === null) return false;\n const type = (data as { type?: unknown }).type;\n return typeof type === \"string\" && PARENT_TYPES.has(type);\n}\n\n/**\n * Reject wildcards outright rather than treating them as \"any origin\".\n *\n * `postMessage(msg, \"*\")` broadcasts the payload to whatever happens to be\n * framing us, and an annotation payload carries page content, console errors and\n * failed request URLs. The spec's rule is no wildcards in either direction; this\n * makes it impossible to configure one by accident.\n */\nexport function assertConcreteOrigin(origin: string): string {\n if (!origin || origin === \"*\") {\n throw new Error(\n \"[annotation] a concrete parent origin is required; '*' would broadcast \" +\n \"annotation payloads to any embedder.\",\n );\n }\n return origin;\n}\n\n/**\n * The query parameter that arms the SDK in a managed product, and the cookie\n * that records it.\n *\n * THESE LIVE IN THE PROTOCOL BECAUSE THE TWO HALVES ARE NOW IN DIFFERENT\n * REPOSITORIES. The managed product's middleware looks for the parameter and\n * sets the cookie; the management app appends the parameter and the SDK reads\n * the cookie. While both halves were one monorepo, a test could read the other\n * side's source and assert the strings matched \u2014 that gate does not survive a\n * repository split, and a mismatch produces no error at all: the frame loads,\n * the cookie is never set, the SDK never boots, and the reviewer sees \"waiting\n * for the page to announce itself\" with nothing to go on.\n *\n * So they stop being two spellings that agree and become one exported\n * constant. A managed product that imports this package cannot drift from it.\n */\nexport const ANNOTATION_TOKEN_PARAM = \"ai_annotate\";\nexport const ANNOTATION_ARMED_COOKIE = \"ai_annotate_armed\";\n\n/**\n * The CAPTURE token's query parameter (A-240).\n *\n * -------------------------------------------------------------------------\n * WHY IT IS HERE AND NOT WHERE IT IS WRITTEN\n * -------------------------------------------------------------------------\n *\n * A second name on the same wire, and a second credential \u2014 see\n * `docs/plans/first-party-annotation.md` \u00A7 *Authentication*. `ai_annotate`\n * answers *should this page offer an annotation UI at all*; this one answers\n * *may this capture become an issue*. Different secrets, different lifetimes,\n * different blast radii, and deliberately not folded into one token.\n *\n * It lives beside `ANNOTATION_TOKEN_PARAM` for that constant's own reason,\n * stated at length above: the console WRITES it into a deep link and the\n * product's bundle READS it, the two halves ship from different repositories,\n * and a mismatch produces no error anywhere \u2014 the reviewer writes a post-it,\n * clicks send, and the ingest rejects an unsigned capture. One exported\n * constant is the only thing that makes those two spellings unable to drift.\n *\n * It has no legacy spelling because it has never had another one: nothing has\n * shipped it yet. It is exported here BEFORE its writer needs it precisely so\n * the writer never has a reason to spell it itself.\n */\nexport const CAPTURE_TOKEN_PARAM = \"ai_capture\";\n\n/**\n * The run under review, when the console knows of one (A-241).\n *\n * Third name on the same wire, here for the reason the two above are: the\n * console writes it and the product's bundle reads it, from two repositories.\n * `apps/manager/src/lib/request-changes.ts` spells it itself today and its own\n * comment asks for exactly this \u2014 the constant to move into the package so the\n * writer has no reason to spell it. **That deletion is not this slice's**\n * (A-243/A-244 are merged and settled), so for now there are two definitions of\n * one string, and `protocol.test.ts` is the only thing standing between them\n * and a silent divergence. Nothing about it is signed: the ingest already\n * treats it as attacker-supplied text and files on it rather than routing on it.\n */\nexport const REVIEW_RUN_PARAM = \"ai_run\";\n\n/**\n * The READ token's query parameter (A-277).\n *\n * ===========================================================================\n * WHY THE MARKER LAYER GETS ITS OWN CREDENTIAL AND NOT `ai_capture`\n * ===========================================================================\n *\n * Fourth name on the same wire, and the reason it is a fourth is A-276's\n * disclosure decision rather than tidiness. The capture token is minted for\n * WRITING and lives four hours so a reviewer can work through a session; reusing\n * it for the read would make one leaked deep link a write capability AND a read\n * capability over every reviewer's comments on that origin for the same window.\n * The scope is a signed field, `verifyCaptureToken` requires an expectation at\n * every call site, and the read endpoint refuses a write claim \u2014 so handing\n * `ai_capture` to the reader does not degrade to a read, it fails.\n *\n * The read claim's TTL is ONE hour rather than four (`READ_TOKEN_TTL_S`), which\n * is the practical consequence a reader of this constant should know: the\n * toolbar outlives the markers, so a reviewer who leaves a tab open all\n * afternoon will find the count line saying it cannot read before they find the\n * Send button broken. That is the intended ordering \u2014 the shorter credential is\n * the one that discloses other people's words.\n *\n * Exported here for the reason the three above are: the console writes it into a\n * deep link and the product's bundle reads it, from two repositories, and a\n * mismatch produces no error anywhere \u2014 just a page with no markers, which is\n * indistinguishable from nine other causes.\n */\nexport const READ_TOKEN_PARAM = \"ai_read\";\n\n/**\n * The arming cookie, as a `Set-Cookie` value (A-241).\n *\n * ===========================================================================\n * IT GOT SIMPLER BY MOVING FIRST-PARTY, AND THAT IS THE WHOLE POINT\n * ===========================================================================\n *\n * Framed, this had to be `SameSite=None; Secure` with no `Partitioned` \u2014 a\n * genuine cross-site cookie, which A-211 records would have broken on its own\n * under Chrome's third-party cookie work, independently of the auth problem\n * that killed the frame. Top-level at the product's own origin it is an\n * ordinary cookie and none of that applies.\n *\n * Each attribute, and why:\n *\n * - **`SameSite=Lax`.** The reviewer arrives by clicking *Request changes* in\n * the console, which is a top-level cross-site GET navigation \u2014 the one case\n * `Lax` still sends on. `Strict` would withhold the cookie on that very first\n * render, so the toolbar would be missing on the page the link opened and\n * appear only after an internal click; that reads as a broken deploy.\n * - **`Secure`.** It is a marker rather than a credential, but a marker that\n * travels in clear is a marker an attacker on the path can set.\n * - **`Path=/`.** Arming survives the reviewer navigating the whole app, which\n * is the point of a cookie rather than a URL parameter.\n * - **No `Max-Age` / `Expires`.** Session-scoped: closing the tab disarms.\n *\n * ===========================================================================\n * WHAT IS *NOT* HERE: `HttpOnly`, AND THE TOKEN\n * ===========================================================================\n *\n * No `HttpOnly`, deliberately. Page script both READS this (`isAnnotationArmed`\n * decides whether a toolbar exists) and CLEARS it (*Done* disarms). An\n * `HttpOnly` arming cookie makes *Done* impossible from the page, which is the\n * one control the reviewer has.\n *\n * That is affordable only because THE VALUE IS A MARKER AND NEVER THE TOKEN.\n * The value is a constant and there is no argument that supplies one: the\n * middleware compares the URL parameter against its secret and then calls this,\n * and what it stores is *yes*, not *what was presented*. The single parameter\n * is the cookie's NAME, and it exists only so a product mid-migration can write\n * the legacy spelling \u2014 it is not a value slot wearing a different label, and\n * `arming.test.ts` pins which side of the `=` it lands on.\n *\n * This is a shape argument, not a guarantee about callers. Nothing stops a\n * determined caller passing a secret as the name; what the signature removes is\n * the ACCIDENT \u2014 the middleware author with a verified token in hand and one\n * parameter available, reaching for the obvious.\n */\nexport const ANNOTATION_ARMED_VALUE = \"1\";\n\nexport function armedCookie(name: string = ANNOTATION_ARMED_COOKIE): string {\n return `${name}=${ANNOTATION_ARMED_VALUE}; Path=/; SameSite=Lax; Secure`;\n}\n\n/**\n * The same cookie, expired \u2014 what *Done* and a disarm write.\n *\n * `Max-Age=0` AND an `Expires` in the past, because they are read by different\n * things and the pair is what every browser honours. The path must match the\n * one set above or the browser stores a second cookie and the first survives \u2014\n * a *Done* button that does nothing and reports success.\n */\nexport function clearedCookie(name: string): string {\n return `${name}=; Path=/; SameSite=Lax; Secure; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\n\n/**\n * The names these used to have (A-98).\n *\n * -------------------------------------------------------------------------\n * WHY THE RENAME, AND WHY IT COULD NOT JUST BE DONE\n * -------------------------------------------------------------------------\n *\n * They were `pits_annotate` and `pits_annotate_armed` \u2014 one managed product's\n * initialism, in a contract every managed product's bundle has to implement.\n * Onboarding a second product meant asking an unrelated codebase to adopt the\n * first product's name in its cookies and its query strings. That is the bug\n * A-98 is about, and it is a bug in a wire protocol rather than in a variable\n * name, which is what makes it expensive.\n *\n * The two halves live in DIFFERENT REPOSITORIES and deploy independently. A\n * clean rename is therefore a breaking change to every product bundle\n * simultaneously, and its failure mode is silence: the frame loads, the cookie\n * is never set, the SDK never boots, and the reviewer sees \"waiting for the\n * page to announce itself\" with nothing to go on. Nothing throws, nothing logs,\n * and the natural reading is that the agent broke the site.\n *\n * -------------------------------------------------------------------------\n * THE MIGRATION: READ BOTH, WRITE BOTH, THEN STOP WRITING THE OLD ONE\n * -------------------------------------------------------------------------\n *\n * 1. **Now.** Every reader accepts either name. Every writer emits BOTH, so a\n * product still running the old bundle arms on the legacy name and one\n * running the new bundle arms on the new one. Deploy order stops mattering,\n * which is the whole point \u2014 there is no window in which a reviewer sees the\n * silent failure above.\n * 2. **After every managed product's bundle is on this version** (A-102). Drop\n * the legacy names from the writers, then from the readers. Two steps, not\n * one: a reader that stops accepting the old name before the writers stop\n * emitting it re-creates the outage in the other direction.\n *\n * The legacy names are exported rather than inlined so the deletion in step 2\n * is `git grep LEGACY_` rather than an archaeology exercise.\n */\nexport const LEGACY_ANNOTATION_TOKEN_PARAM = \"pits_annotate\";\nexport const LEGACY_ANNOTATION_ARMED_COOKIE = \"pits_annotate_armed\";\n\n/**\n * ===========================================================================\n * AND THE NAMES ARE NOT CHANGING AGAIN FOR THE PUBLISHED PACKAGE (A-184)\n * ===========================================================================\n *\n * When this becomes `@weatherboard/annotation` the obvious next thought is that\n * the wire names should match the package \u2014 `weatherboard_annotate`. They\n * should not, and the reason is the migration described above rather than\n * taste.\n *\n * That migration is STILL IN FLIGHT. Readers accept both spellings; writers\n * emit both; step 2 waits on every managed product's bundle being current.\n * A third spelling introduced now does not replace the second, it joins it:\n * every reader accepts three names, every writer emits three, and the window in\n * which a product can be on any of them widens instead of closing.\n *\n * The cost is not the code. It is that each rename buys another instance of the\n * failure this file exists to prevent \u2014 the frame loads, the cookie is never\n * set, the SDK never boots, and the reviewer sees \"waiting for the page to\n * announce itself\" with nothing to go on. Twice is a migration. Three times is\n * a habit, and the third one would be paid by consumers who never asked for\n * either.\n *\n * `ai_annotate` is already product-neutral, which was the entire point of A-98.\n * A published package needs names that name no product; it does not need names\n * that name the package. Nobody reading a query string cares which npm scope\n * the library came from.\n *\n * The one real objection is collision: `ai_` is a common prefix and some other\n * tool could plausibly use it. That is survivable by construction \u2014 arming\n * requires the parameter to carry a token the product compares exactly, so a\n * coincidental `?ai_annotate=` from an unrelated tool arms nothing. A colliding\n * COOKIE cannot false-positive either; `isAnnotationArmed` matches on the\n * delimiter rather than by substring, for exactly this reason.\n *\n * If a rename is ever genuinely warranted, it happens ONCE, after step 2 has\n * landed and the reader accepts one name again \u2014 not layered on top of an\n * unfinished one.\n */\n\n/** Both spellings of the query parameter, current first. */\nexport const ANNOTATION_TOKEN_PARAMS = [\n ANNOTATION_TOKEN_PARAM,\n LEGACY_ANNOTATION_TOKEN_PARAM,\n] as const;\n\n/** Both spellings of the cookie, current first. */\nexport const ANNOTATION_ARMED_COOKIES = [\n ANNOTATION_ARMED_COOKIE,\n LEGACY_ANNOTATION_ARMED_COOKIE,\n] as const;\n\n/**\n * Whether a cookie jar carries the armed cookie, under either name.\n *\n * Delimiter-aware rather than a substring test: a cookie named\n * `not_ai_annotate_armed`, or another cookie whose VALUE contains the name,\n * satisfies `includes` \u2014 and arming an annotation channel on a coincidence is\n * not a failure anyone would think to look for.\n */\nexport function isAnnotationArmed(cookie: string): boolean {\n return cookie\n .split(\";\")\n .some((entry) => {\n const trimmed = entry.trimStart();\n return ANNOTATION_ARMED_COOKIES.some((name) => trimmed.startsWith(`${name}=`));\n });\n}\n\n/**\n * The arming token from a URL's query string, under either name.\n *\n * Current name wins when both are present. They should never disagree \u2014 the\n * management app writes both from one value \u2014 but if they ever do, the one this\n * version of the protocol owns is the one to believe.\n */\nexport function readAnnotationToken(params: URLSearchParams): string | null {\n for (const name of ANNOTATION_TOKEN_PARAMS) {\n const value = params.get(name);\n if (value !== null && value.length > 0) return value;\n }\n return null;\n}\n", "import {\n assertConcreteOrigin,\n type AnnotationPayload,\n type BuildInfo,\n type FrameToParent,\n type ParentToFrame,\n type TargetInfo,\n} from \"./protocol\";\n\n/**\n * The parent half of the annotation channel (A-30).\n *\n * WHY THIS LIVES IN THE SDK AND NOT IN `apps/admin`.\n *\n * The protocol is this package's domain. It already owns the message union,\n * `isParentMessage` and `assertConcreteOrigin`; a second implementation of the\n * same handshake, written host-side because that is where the UI is, is exactly\n * the shape SHARED-PACKAGES.md warns about. The two halves of a protocol\n * drifting apart is not a hypothetical \u2014 it is the normal outcome when they are\n * maintained in different packages by different changes.\n *\n * Concretely: the frame's `sdk.ts` and this file are the only two things that\n * can disagree about what `annotate:capture` means, and keeping them in one\n * package means a change to the union is a compile error in both.\n *\n * Admin gets the UI. This gets the wire.\n */\n\nexport interface AnnotationHostOptions {\n /**\n * The window being framed. Passed in rather than looked up, because the host\n * knows which iframe it means and this module must never guess.\n */\n frame: Window;\n /**\n * The frame's origin \u2014 where the product is served from. Required and\n * concrete, checked by `assertConcreteOrigin`: posting to `*` would hand an\n * annotation payload, including console errors from a real session, to\n * whatever happens to be loaded.\n */\n frameOrigin: string;\n /** The window that receives messages. Injected so tests need no real iframe. */\n hostWindow: Window;\n\n onReady(info: { sdkVersion: string; build: BuildInfo }): void;\n onHover(target: TargetInfo): void;\n onCapture(payload: AnnotationPayload): void;\n onCancel(): void;\n}\n\nexport interface AnnotationHost {\n enable(): void;\n disable(): void;\n ping(): void;\n /** True once the frame has announced itself. */\n isReady(): boolean;\n destroy(): void;\n}\n\nconst FRAME_TYPES = new Set([\n \"annotate:ready\",\n \"annotate:hover\",\n \"annotate:capture\",\n \"annotate:cancel\",\n]);\n\n/**\n * Is this one of ours?\n *\n * The mirror of `isParentMessage`, and it carries the same warning: origin is\n * checked separately and FIRST. A well-formed message from the wrong origin is\n * precisely the case this guard must not be trusted to catch \u2014 anything can\n * post `{type: \"annotate:capture\"}` to a window.\n */\nexport function isFrameMessage(data: unknown): data is FrameToParent {\n if (typeof data !== \"object\" || data === null) return false;\n const type = (data as { type?: unknown }).type;\n return typeof type === \"string\" && FRAME_TYPES.has(type);\n}\n\nexport function createAnnotationHost(options: AnnotationHostOptions): AnnotationHost {\n const frameOrigin = assertConcreteOrigin(options.frameOrigin);\n let ready = false;\n let destroyed = false;\n\n const post = (message: ParentToFrame) => {\n if (destroyed) return;\n // Targeted, never `*`. `enable` is not sensitive in itself, but a habit of\n // posting to `*` is what eventually posts something that is.\n options.frame.postMessage(message, frameOrigin);\n };\n\n const onMessage = (event: MessageEvent) => {\n /**\n * Origin first, and by exact match.\n *\n * Not `endsWith`, not a prefix: for a product served from `example.com`,\n * `https://evil-example.com` ends with nothing suspicious and\n * `https://example.com.evil.test` starts with the right characters. The browser hands us a normalised origin, so an\n * equality test is both correct and the only one that cannot be widened by\n * a hostile hostname.\n */\n if (event.origin !== frameOrigin) return;\n\n /**\n * And the source must be the frame we are talking to.\n *\n * Origin alone is not enough: any other window on the same origin \u2014 a popup\n * the product opened, another iframe \u2014 can post a well-formed message from\n * an origin that matches. This is the check that makes \"the frame said so\"\n * mean the frame.\n */\n if (event.source !== options.frame) return;\n\n if (!isFrameMessage(event.data)) return;\n\n switch (event.data.type) {\n case \"annotate:ready\":\n ready = true;\n options.onReady({ sdkVersion: event.data.sdkVersion, build: event.data.build });\n return;\n case \"annotate:hover\":\n options.onHover(event.data.target);\n return;\n case \"annotate:capture\":\n options.onCapture(event.data.payload);\n return;\n case \"annotate:cancel\":\n options.onCancel();\n return;\n }\n };\n\n options.hostWindow.addEventListener(\"message\", onMessage);\n\n return {\n enable: () => post({ type: \"annotate:enable\" }),\n disable: () => post({ type: \"annotate:disable\" }),\n ping: () => post({ type: \"annotate:ping\" }),\n isReady: () => ready,\n destroy() {\n destroyed = true;\n ready = false;\n options.hostWindow.removeEventListener(\"message\", onMessage);\n },\n };\n}\n"],
5
5
  "mappings": "AA4MO,SAASA,EAAqBC,EAAwB,CAC3D,GAAI,CAACA,GAAUA,IAAW,IACxB,MAAM,IAAI,MACR,6GAEF,EAEF,OAAOA,CACT,CCzJA,IAAMC,EAAc,IAAI,IAAI,CAC1B,iBACA,iBACA,mBACA,iBACF,CAAC,EAUM,SAASC,EAAeC,EAAsC,CACnE,GAAI,OAAOA,GAAS,UAAYA,IAAS,KAAM,MAAO,GACtD,IAAMC,EAAQD,EAA4B,KAC1C,OAAO,OAAOC,GAAS,UAAYH,EAAY,IAAIG,CAAI,CACzD,CAEO,SAASC,EAAqBC,EAAgD,CACnF,IAAMC,EAAcC,EAAqBF,EAAQ,WAAW,EACxDG,EAAQ,GACRC,EAAY,GAEVC,EAAQC,GAA2B,CACnCF,GAGJJ,EAAQ,MAAM,YAAYM,EAASL,CAAW,CAChD,EAEMM,EAAaC,GAAwB,CAUzC,GAAIA,EAAM,SAAWP,GAUjBO,EAAM,SAAWR,EAAQ,OAExBJ,EAAeY,EAAM,IAAI,EAE9B,OAAQA,EAAM,KAAK,KAAM,CACvB,IAAK,iBACHL,EAAQ,GACRH,EAAQ,QAAQ,CAAE,WAAYQ,EAAM,KAAK,WAAY,MAAOA,EAAM,KAAK,KAAM,CAAC,EAC9E,OACF,IAAK,iBACHR,EAAQ,QAAQQ,EAAM,KAAK,MAAM,EACjC,OACF,IAAK,mBACHR,EAAQ,UAAUQ,EAAM,KAAK,OAAO,EACpC,OACF,IAAK,kBACHR,EAAQ,SAAS,EACjB,MACJ,CACF,EAEA,OAAAA,EAAQ,WAAW,iBAAiB,UAAWO,CAAS,EAEjD,CACL,OAAQ,IAAMF,EAAK,CAAE,KAAM,iBAAkB,CAAC,EAC9C,QAAS,IAAMA,EAAK,CAAE,KAAM,kBAAmB,CAAC,EAChD,KAAM,IAAMA,EAAK,CAAE,KAAM,eAAgB,CAAC,EAC1C,QAAS,IAAMF,EACf,SAAU,CACRC,EAAY,GACZD,EAAQ,GACRH,EAAQ,WAAW,oBAAoB,UAAWO,CAAS,CAC7D,CACF,CACF",
6
6
  "names": ["assertConcreteOrigin", "origin", "FRAME_TYPES", "isFrameMessage", "data", "type", "createAnnotationHost", "options", "frameOrigin", "assertConcreteOrigin", "ready", "destroyed", "post", "message", "onMessage", "event"]
7
7
  }
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var Se=Object.defineProperty;var At=Object.getOwnPropertyDescriptor;var xt=Object.getOwnPropertyNames;var Tt=Object.prototype.hasOwnProperty;var ze=e=>{throw TypeError(e)};var vt=(e,t)=>{for(var n in t)Se(e,n,{get:t[n],enumerable:!0})},St=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of xt(t))!Tt.call(e,o)&&o!==n&&Se(e,o,{get:()=>t[o],enumerable:!(r=At(t,o))||r.enumerable});return e};var Ct=e=>St(Se({},"__esModule",{value:!0}),e);var Je=(e,t,n)=>t.has(e)||ze("Cannot "+n);var L=(e,t,n)=>(Je(e,t,"read from private field"),n?n.call(e):t.get(e)),Ce=(e,t,n)=>t.has(e)?ze("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),Ze=(e,t,n,r)=>(Je(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n);var $t={};vt($t,{ANNOTATION_ARMED_COOKIE:()=>ie,ANNOTATION_ARMED_COOKIES:()=>z,ANNOTATION_ARMED_VALUE:()=>Oe,ANNOTATION_TOKEN_PARAM:()=>we,ANNOTATION_TOKEN_PARAMS:()=>Y,CAPTURE_TOKEN_PARAM:()=>j,CAPTURE_TOKEN_STORAGE_KEY:()=>ee,DEFAULT_LABELS:()=>qe,DEFAULT_LIMIT:()=>ft,HTTP_TRANSPORT_TIMEOUT_MS:()=>Ue,LEGACY_ANNOTATION_ARMED_COOKIE:()=>ke,LEGACY_ANNOTATION_TOKEN_PARAM:()=>Re,MAX_CHAIN:()=>Me,MAX_SCREENSHOT_BYTES:()=>ot,MAX_SCREENSHOT_EDGE:()=>at,MAX_TEXT:()=>ue,NAME_ATTR:()=>oe,OVERLAY_ID:()=>_e,REVIEW_RUN_PARAM:()=>V,REVIEW_RUN_STORAGE_KEY:()=>te,RingBuffer:()=>K,SCREENSHOT_TIMEOUT_MS:()=>it,SDK_VERSION:()=>G,SRC_ATTR:()=>W,TOOLBAR_ID:()=>Xe,armedCookie:()=>et,assertConcreteEndpoint:()=>Fe,assertConcreteOrigin:()=>D,attachScreenshot:()=>Q,captureScreenshot:()=>Be,captureScreenshotSync:()=>$e,clearedCookie:()=>se,collectSourceChain:()=>Ne,createAnnotationSdk:()=>ye,createAnnotationToolbar:()=>Ge,createEgress:()=>he,createOverlay:()=>ce,defaultStateCollectorOptions:()=>Ve,describeArg:()=>We,domPath:()=>Le,emptyAnnotationState:()=>st,findAnnotatedAncestor:()=>Ie,findCaptureSource:()=>De,firstStack:()=>Ae,fitWithin:()=>Ke,formatConsoleArgs:()=>je,httpTransport:()=>rt,installStateCollector:()=>gt,intrinsicSize:()=>He,isAnnotatableBuild:()=>Ee,isAnnotationArmed:()=>le,isFailedStatus:()=>xe,isMediaElement:()=>Z,isParentMessage:()=>ae,mountAnnotation:()=>pt,parseSourceRef:()=>de,postMessageTransport:()=>ge,readAnnotationToken:()=>tt,readArming:()=>ct,refusalReason:()=>me,resolveScreenshotOptions:()=>be,resolveTarget:()=>pe,safeIssueUrl:()=>J,safeRefusalReason:()=>fe,selectionOf:()=>Qe,summariseText:()=>Pe});module.exports=Ct($t);var G="1",W="data-src",oe="data-src-name";function Qe(e){return e.selection==="region"?"region":"element"}var wt=new Set(["annotate:enable","annotate:disable","annotate:ping"]);function ae(e){if(typeof e!="object"||e===null)return!1;let t=e.type;return typeof t=="string"&&wt.has(t)}function D(e){if(!e||e==="*")throw new Error("[annotation] a concrete parent origin is required; '*' would broadcast annotation payloads to any embedder.");return e}var we="ai_annotate",ie="ai_annotate_armed",j="ai_capture",V="ai_run",Oe="1";function et(e=ie){return`${e}=${Oe}; Path=/; SameSite=Lax; Secure`}function se(e){return`${e}=; Path=/; SameSite=Lax; Secure; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT`}var Re="pits_annotate",ke="pits_annotate_armed",Y=[we,Re],z=[ie,ke];function le(e){return e.split(";").some(t=>{let n=t.trimStart();return z.some(r=>n.startsWith(`${r}=`))})}function tt(e){for(let t of Y){let n=e.get(t);if(n!==null&&n.length>0)return n}return null}var _e="ai-annotation-overlay",Ot="2147483000";function ce(e){let t=e.createElement("div");t.id=_e,t.style.cssText=["position:fixed","top:0","left:0","pointer-events:none",`z-index:${Ot}`,"display:none","box-sizing:border-box","border:2px solid #2563eb","background:rgba(37,99,235,0.12)","border-radius:2px"].join(";");let n=e.createElement("div");return n.style.cssText=["position:absolute","left:0","top:-20px","font:11px/16px ui-monospace,SFMono-Regular,Menlo,monospace","color:#fff","background:#2563eb","padding:1px 5px","border-radius:2px","white-space:nowrap","max-width:60vw","overflow:hidden","text-overflow:ellipsis"].join(";"),t.appendChild(n),e.body.appendChild(t),{show(r,o){t.style.display="block",t.style.transform=`translate(${r.x}px, ${r.y}px)`,t.style.width=`${r.w}px`,t.style.height=`${r.h}px`,n.style.top=r.y<20?"100%":"-20px",n.textContent=o??"",n.style.display=o?"block":"none"},hide(){t.style.display="none"},destroy(){t.remove()},contains(r){return r instanceof Node&&t.contains(r)}}}var ue=200,Me=8;function de(e){if(!e)return null;let t=e.lastIndexOf(":");if(t<=0)return null;let n=e.lastIndexOf(":",t-1);if(n<=0)return null;let r=e.slice(0,n),o=Number(e.slice(n+1,t)),a=Number(e.slice(t+1));return!r||!Number.isFinite(o)||!Number.isFinite(a)?null:{file:r,line:o,column:a}}function Ne(e,t=Me){let n=[],r=e;for(;r&&n.length<t;){let o=de(r.getAttribute(W));if(o){let a=n[n.length-1];a&&a.file===o.file&&a.line===o.line||n.push(o)}r=r.parentElement}return n}function Ie(e){let t=e;for(;t;){if(de(t.getAttribute(W)))return t;t=t.parentElement}return null}function Rt(e){let t=e.classList;return(typeof t?.value=="string"?t.value:"").split(/\s+/).filter(Boolean)}function Le(e,t=6){let n=[],r=e;for(;r&&n.length<t;){let o=(r.tagName||"").toLowerCase();if(!o||o==="html")break;let a=o;if(r.id)return n.unshift(`${o}#${r.id}`),n.join(" > ");let s=Rt(r).slice(0,2);if(s.length>0&&(a+=`.${s.join(".")}`),n.unshift(a),o==="body")break;r=r.parentElement}return n.join(" > ")}function Pe(e){if(!e)return;let t=e.replace(/\s+/g," ").trim();if(t)return t.length>ue?`${t.slice(0,ue-1)}\u2026`:t}function pe(e,t){let n=Ie(e),r=Ne(e),o=r[0],a={domPath:Le(e),boundingBox:t.boundingBox};o&&(a.source=o),r.length>0&&(a.sourceChain=r);let s=n?.getAttribute(oe);s&&(a.componentName=s);let c=Pe(e.textContent);return c&&(a.textContent=c),a}var kt=2048;function J(e){if(typeof e!="string"||e.length===0||e.length>kt)return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="https:"&&t.protocol!=="http:"))return t.href}var nt=300;function fe(e){if(typeof e!="string")return;let t=e.replace(/[\u0000-\u001f\u007f-\u009f]+/g," ").replace(/\s+/g," ").trim();if(t.length!==0)return t.length>nt?`${t.slice(0,nt-1).trimEnd()}\u2026`:t}function me(e){return fe(e?.reason)}function ge(e,t=window){let n=D(e);return{name:"postMessage",carries:()=>!0,send(r){t.parent.postMessage(r,n)}}}function Fe(e){if(!e||e==="*")throw new Error("[annotation] a concrete ingest endpoint is required; '*' is not an address and an empty one posts nowhere.");let t;try{t=new URL(e)}catch{throw new Error(`[annotation] the ingest endpoint must be an absolute URL, got ${JSON.stringify(e)}; a relative one resolves against the product's own origin and the capture is lost there.`)}if(t.protocol!=="https:"&&t.protocol!=="http:")throw new Error(`[annotation] the ingest endpoint must be http(s), got ${t.protocol}`);return t.toString()}var Ue=8e3;function rt(e){let t=Fe(e.endpoint);if(!e.captureToken)throw new Error("[annotation] a capture token is required; the ingest rejects an unsigned capture and the reviewer would only find out after writing one.");let n=e.captureToken,r=e.timeoutMs??Ue,o=e.fetch??globalThis.fetch;return{name:"http",carries:a=>a==="annotate:capture",async send(a){if(!o)throw new Error("[annotation] no fetch implementation is available for the ingest");let s=new AbortController,c=setTimeout(()=>s.abort(),r);try{let m=await o(t,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${n}`},credentials:"omit",body:JSON.stringify(a),signal:s.signal});if(!m.ok){let f;try{let d=await m.json?.();f=fe(d?.error)}catch{f=void 0}let g=new Error(`[annotation] the ingest rejected the capture: ${f??`HTTP ${m.status}`}`);throw g.status=m.status,f!==void 0&&(g.reason=f),g}try{let f=await m.json?.(),g=J(f?.issueUrl);if(g!==void 0)return{issueUrl:g}}catch{}return{}}catch(m){throw s.signal.aborted?new Error(`[annotation] the capture timed out after ${r}ms`,{cause:m}):m}finally{clearTimeout(c)}}}}function he(e,t){let n=(r,o)=>{let a={message:r,transport:e.name,error:o};if(t){t(a);return}console.error("[annotation] a capture could not be delivered",a)};return async r=>{if(!e.carries(r.type))return{status:"declined"};try{return{status:"sent",receipt:await e.send(r)??{}}}catch(o){return n(r,o),{status:"failed",error:o}}}}var ot=49152,at=480,it=250,_t=[{edge:480,quality:.7},{edge:240,quality:.5}],Mt=["image/webp","image/jpeg"];function be(e,t){let n=e===!1?{enabled:!1}:e??{};return{enabled:n.enabled??!0,maxBytes:n.maxBytes??49152,maxEdge:n.maxEdge??480,timeoutMs:n.timeoutMs??250,createCanvas:n.createCanvas??(()=>t?.createElement("canvas")??null)}}var Nt=new Set(["IMG","CANVAS","VIDEO"]),It="img,canvas,video",Lt=4;function Z(e){return!!e&&Nt.has(e.tagName.toUpperCase())}function De(e,t=Lt){if(!e)return null;if(Z(e))return e;let n=e.querySelector?.(It)??null;if(Z(n))return n;let r=e.parentElement??null;for(let o=0;r&&o<t;o++){if(Z(r))return r;r=r.parentElement??null}return null}function He(e){let t=e.tagName.toUpperCase(),n=t==="IMG"?{w:e.naturalWidth,h:e.naturalHeight}:t==="VIDEO"?{w:e.videoWidth,h:e.videoHeight}:{w:e.width,h:e.height},r=Math.floor(n.w??0),o=Math.floor(n.h??0);return!Number.isFinite(r)||!Number.isFinite(o)||r<1||o<1||t==="IMG"&&e.complete===!1?null:{w:r,h:o}}function Ke(e,t){let n=Math.min(1,t/Math.max(e.w,e.h));return{w:Math.max(1,Math.round(e.w*n)),h:Math.max(1,Math.round(e.h*n))}}function Pt(e,t,n,r){let o;try{o=r.createCanvas()}catch{return null}if(!o)return null;try{o.width=t.w,o.height=t.h;let a=o.getContext("2d");if(!a)return null;a.drawImage(e,0,0,t.w,t.h);for(let s of Mt){let c;try{c=o.toDataURL(s,n)}catch{return null}if(!(typeof c!="string"||!c.startsWith(`data:${s}`)))return c.length<=r.maxBytes?c:null}return null}catch{return null}}function $e(e,t){if(!t.enabled)return null;let n=De(e);if(!n)return null;let r=He(n);if(!r)return null;for(let o of _t){let a=Ke(r,Math.min(t.maxEdge,o.edge)),s=Pt(n,a,o.quality,t);if(s)return{dataUrl:s,region:"element"}}return null}function Be(e,t){return t.enabled?new Promise(n=>{let r=!1,o=s=>{r||(r=!0,n(s))},a=setTimeout(()=>o(null),t.timeoutMs);a.unref?.(),Promise.resolve().then(()=>$e(e,t)).then(o,()=>o(null)).finally(()=>clearTimeout(a))}):Promise.resolve(null)}async function Q(e,t,n){let r;try{r=await Be(t,n)}catch{r=null}return r?{...e,screenshot:r}:e}var Ft={consoleErrors:[],failedRequests:[],featureFlags:{}},st=()=>({...Ft});function ye(e){let t=e.parentOrigin===void 0?null:D(e.parentOrigin),n=window,r=n.document,o=be(e.screenshot,r),a=!1,s=null,c=0,m=null,f=!1,g=e.transport??(t===null?null:ge(t,n));if(g===null)throw new Error("[annotation] the SDK needs a `transport` or a `parentOrigin`; with neither there is nowhere for a capture to go and every one of them would be lost in silence.");let d=he(g,e.onEgressError),p=l=>{let u=l.getBoundingClientRect();return{x:u.x,y:u.y,w:u.width,h:u.height}},T=l=>pe(l,{boundingBox:p(l)}),x=l=>{if(l.componentName&&l.source){let u=l.source.file.split("/").pop()??l.source.file;return`${l.componentName} \xB7 ${u}:${l.source.line}`}return l.source?`${l.source.file}:${l.source.line}`:l.componentName??null},A=l=>{!a||f||c||(c=n.requestAnimationFrame(()=>{c=0;let u=r.elementFromPoint(l.clientX,l.clientY);if(!u||s&&s.contains(u)||e.ignoreElement?.(u)||u===m)return;m=u;let b=T(u);s?.show(b.boundingBox,x(b)),d({type:"annotate:hover",target:b})}))},R=(l,u)=>({schemaVersion:1,capturedAt:new Date().toISOString(),build:e.build,page:{url:n.location.href,route:e.getRoute(),viewport:{w:n.innerWidth,h:n.innerHeight,dpr:n.devicePixelRatio},userAgent:n.navigator.userAgent},target:T(l),state:{consoleErrors:u.consoleErrors,failedRequests:u.failedRequests,featureFlags:u.featureFlags},comment:""}),N=l=>{let u={type:"annotate:capture",payload:l},b=()=>{d(u).then(O=>e.onCaptureResult?.(O,b))};b()},k=l=>{if(!a)return;let u=l.target;if(!(u instanceof Element)||e.ignoreElement?.(u)||(l.preventDefault(),l.stopPropagation(),f))return;let b=R(u,e.getState()),O=e.collectComment;if(!O){if(!o.enabled){N(b);return}Q(b,u,o).then(S=>N(S),()=>N(b));return}let $={x:l.clientX,y:l.clientY},B=o.enabled?Q(b,u,o).then(S=>S,()=>b):Promise.resolve(b),I=()=>{f=!0;let S=null,re=O({target:b.target,point:$}).then(E=>E,E=>(S={error:E},null));Promise.all([B,re]).then(([E,U])=>{if(f=!1,S!==null){e.onCaptureResult?.({status:"failed",error:S.error},I);return}U!==null&&N(U===""?E:{...E,comment:U})})};I()},P=l=>{!a||f||l.key!=="Escape"||(d({type:"annotate:cancel"}),_())};function F(){a||(a=!0,s??(s=ce(r)),r.addEventListener("mousemove",A,!0),r.addEventListener("click",k,!0),r.addEventListener("keydown",P,!0))}function _(){a&&(a=!1,m=null,c&&(n.cancelAnimationFrame(c),c=0),r.removeEventListener("mousemove",A,!0),r.removeEventListener("click",k,!0),r.removeEventListener("keydown",P,!0),s?.hide())}let v=l=>{if(l.origin===t&&ae(l.data))switch(l.data.type){case"annotate:enable":F();break;case"annotate:disable":_();break;case"annotate:ping":d({type:"annotate:ready",sdkVersion:"1",build:e.build});break}};return t!==null&&n.addEventListener("message",v),d({type:"annotate:ready",sdkVersion:"1",build:e.build}),{enable:F,disable:_,isEnabled:()=>a,destroy(){_(),t!==null&&n.removeEventListener("message",v),s?.destroy(),s=null}}}var ee="ai.annotation.capture",te="ai.annotation.run",lt=[...Y,j,V];function w(e,t){try{return e()}catch{return t}}function Ut(e){return w(()=>e?.sessionStorage??null,null)}function ct(e={}){let t=e.doc??document,n=t.defaultView??null,r=e.storage===void 0?Ut(n):e.storage,o=e.url??t.URL,a=w(()=>new URL(o),null),s=null,c=null;if(a!==null&&(s=a.searchParams.get(j)||null,c=a.searchParams.get(V)||null,lt.some(d=>a.searchParams.has(d)))){for(let p of lt)a.searchParams.delete(p);let d=e.history??n?.history;w(()=>{d?.replaceState(n?.history?.state??null,"",a.toString())},void 0)}let m=()=>{for(let g of z)w(()=>{t.cookie=se(g)},void 0);r&&(w(()=>r.removeItem(ee),void 0),w(()=>r.removeItem(te),void 0))},f=le(w(()=>t.cookie,""));return f?(r&&(s!==null?w(()=>r.setItem(ee,s),void 0):s=w(()=>r.getItem(ee),null),c!==null?w(()=>r.setItem(te,c),void 0):c=w(()=>r.getItem(te),null)),{armed:f,captureToken:s,runId:c,disarm:m}):{armed:!1,captureToken:null,runId:null,disarm:m}}var Dt=["preview","staging","development"];function Ee(e){return Dt.includes(e.env)}var qe={region:"Annotation",expand:"Open the annotation toolbar",collapse:"Close the annotation toolbar",start:"Start annotating",stop:"Stop annotating",done:"Done",comment:"What is wrong here?",placeholder:"The heading wraps onto three lines on mobile\u2026",send:"Send",cancel:"Cancel",sent:"Sent",issue:"View issue",failed:"That did not send",retry:"Retry"},Xe="ai-annotation-toolbar",Ht="2147483001",dt=288,ut=190,H=12,Kt=`
1
+ "use strict";var we=Object.defineProperty;var Tt=Object.getOwnPropertyDescriptor;var vt=Object.getOwnPropertyNames;var St=Object.prototype.hasOwnProperty;var Ze=e=>{throw TypeError(e)};var Ct=(e,t)=>{for(var n in t)we(e,n,{get:t[n],enumerable:!0})},wt=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of vt(t))!St.call(e,o)&&o!==n&&we(e,o,{get:()=>t[o],enumerable:!(r=Tt(t,o))||r.enumerable});return e};var Ot=e=>wt(we({},"__esModule",{value:!0}),e);var Qe=(e,t,n)=>t.has(e)||Ze("Cannot "+n);var L=(e,t,n)=>(Qe(e,t,"read from private field"),n?n.call(e):t.get(e)),Oe=(e,t,n)=>t.has(e)?Ze("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n),et=(e,t,n,r)=>(Qe(e,t,"write to private field"),r?r.call(e,n):t.set(e,n),n);var qt={};Ct(qt,{ANNOTATION_ARMED_COOKIE:()=>le,ANNOTATION_ARMED_COOKIES:()=>J,ANNOTATION_ARMED_VALUE:()=>_e,ANNOTATION_TOKEN_PARAM:()=>Re,ANNOTATION_TOKEN_PARAMS:()=>z,CAPTURE_TOKEN_PARAM:()=>j,CAPTURE_TOKEN_STORAGE_KEY:()=>te,DEFAULT_LABELS:()=>Ge,DEFAULT_LIMIT:()=>gt,HTTP_TRANSPORT_TIMEOUT_MS:()=>He,LEGACY_ANNOTATION_ARMED_COOKIE:()=>Me,LEGACY_ANNOTATION_TOKEN_PARAM:()=>ke,MAX_CHAIN:()=>Ie,MAX_SCREENSHOT_BYTES:()=>it,MAX_SCREENSHOT_EDGE:()=>st,MAX_TEXT:()=>pe,NAME_ATTR:()=>ie,OVERLAY_ID:()=>Ne,READ_TOKEN_PARAM:()=>Y,READ_TOKEN_STORAGE_KEY:()=>re,REVIEW_RUN_PARAM:()=>V,REVIEW_RUN_STORAGE_KEY:()=>ne,RingBuffer:()=>K,SCREENSHOT_TIMEOUT_MS:()=>lt,SDK_VERSION:()=>G,SRC_ATTR:()=>W,TOOLBAR_ID:()=>We,armedCookie:()=>nt,assertConcreteEndpoint:()=>De,assertConcreteOrigin:()=>D,attachScreenshot:()=>ee,captureScreenshot:()=>Xe,captureScreenshotSync:()=>qe,clearedCookie:()=>ce,collectSourceChain:()=>Le,createAnnotationSdk:()=>Ae,createAnnotationToolbar:()=>je,createEgress:()=>Ee,createOverlay:()=>de,defaultStateCollectorOptions:()=>ze,describeArg:()=>Ve,domPath:()=>Fe,emptyAnnotationState:()=>ct,findAnnotatedAncestor:()=>Pe,findCaptureSource:()=>Ke,firstStack:()=>Te,fitWithin:()=>Be,formatConsoleArgs:()=>Ye,httpTransport:()=>at,installStateCollector:()=>bt,intrinsicSize:()=>$e,isAnnotatableBuild:()=>xe,isAnnotationArmed:()=>ue,isFailedStatus:()=>ve,isMediaElement:()=>Q,isParentMessage:()=>se,mountAnnotation:()=>mt,parseSourceRef:()=>fe,postMessageTransport:()=>be,readAnnotationToken:()=>rt,readArming:()=>dt,refusalReason:()=>he,resolveScreenshotOptions:()=>ye,resolveTarget:()=>me,safeIssueUrl:()=>Z,safeRefusalReason:()=>ge,selectionOf:()=>tt,summariseText:()=>Ue});module.exports=Ot(qt);var G="1",W="data-src",ie="data-src-name";function tt(e){return e.selection==="region"?"region":"element"}var Rt=new Set(["annotate:enable","annotate:disable","annotate:ping"]);function se(e){if(typeof e!="object"||e===null)return!1;let t=e.type;return typeof t=="string"&&Rt.has(t)}function D(e){if(!e||e==="*")throw new Error("[annotation] a concrete parent origin is required; '*' would broadcast annotation payloads to any embedder.");return e}var Re="ai_annotate",le="ai_annotate_armed",j="ai_capture",V="ai_run",Y="ai_read",_e="1";function nt(e=le){return`${e}=${_e}; Path=/; SameSite=Lax; Secure`}function ce(e){return`${e}=; Path=/; SameSite=Lax; Secure; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT`}var ke="pits_annotate",Me="pits_annotate_armed",z=[Re,ke],J=[le,Me];function ue(e){return e.split(";").some(t=>{let n=t.trimStart();return J.some(r=>n.startsWith(`${r}=`))})}function rt(e){for(let t of z){let n=e.get(t);if(n!==null&&n.length>0)return n}return null}var Ne="ai-annotation-overlay",_t="2147483000";function de(e){let t=e.createElement("div");t.id=Ne,t.style.cssText=["position:fixed","top:0","left:0","pointer-events:none",`z-index:${_t}`,"display:none","box-sizing:border-box","border:2px solid #2563eb","background:rgba(37,99,235,0.12)","border-radius:2px"].join(";");let n=e.createElement("div");return n.style.cssText=["position:absolute","left:0","top:-20px","font:11px/16px ui-monospace,SFMono-Regular,Menlo,monospace","color:#fff","background:#2563eb","padding:1px 5px","border-radius:2px","white-space:nowrap","max-width:60vw","overflow:hidden","text-overflow:ellipsis"].join(";"),t.appendChild(n),e.body.appendChild(t),{show(r,o){t.style.display="block",t.style.transform=`translate(${r.x}px, ${r.y}px)`,t.style.width=`${r.w}px`,t.style.height=`${r.h}px`,n.style.top=r.y<20?"100%":"-20px",n.textContent=o??"",n.style.display=o?"block":"none"},hide(){t.style.display="none"},destroy(){t.remove()},contains(r){return r instanceof Node&&t.contains(r)}}}var pe=200,Ie=8;function fe(e){if(!e)return null;let t=e.lastIndexOf(":");if(t<=0)return null;let n=e.lastIndexOf(":",t-1);if(n<=0)return null;let r=e.slice(0,n),o=Number(e.slice(n+1,t)),a=Number(e.slice(t+1));return!r||!Number.isFinite(o)||!Number.isFinite(a)?null:{file:r,line:o,column:a}}function Le(e,t=Ie){let n=[],r=e;for(;r&&n.length<t;){let o=fe(r.getAttribute(W));if(o){let a=n[n.length-1];a&&a.file===o.file&&a.line===o.line||n.push(o)}r=r.parentElement}return n}function Pe(e){let t=e;for(;t;){if(fe(t.getAttribute(W)))return t;t=t.parentElement}return null}function kt(e){let t=e.classList;return(typeof t?.value=="string"?t.value:"").split(/\s+/).filter(Boolean)}function Fe(e,t=6){let n=[],r=e;for(;r&&n.length<t;){let o=(r.tagName||"").toLowerCase();if(!o||o==="html")break;let a=o;if(r.id)return n.unshift(`${o}#${r.id}`),n.join(" > ");let s=kt(r).slice(0,2);if(s.length>0&&(a+=`.${s.join(".")}`),n.unshift(a),o==="body")break;r=r.parentElement}return n.join(" > ")}function Ue(e){if(!e)return;let t=e.replace(/\s+/g," ").trim();if(t)return t.length>pe?`${t.slice(0,pe-1)}\u2026`:t}function me(e,t){let n=Pe(e),r=Le(e),o=r[0],a={domPath:Fe(e),boundingBox:t.boundingBox};o&&(a.source=o),r.length>0&&(a.sourceChain=r);let s=n?.getAttribute(ie);s&&(a.componentName=s);let c=Ue(e.textContent);return c&&(a.textContent=c),a}var Mt=2048;function Z(e){if(typeof e!="string"||e.length===0||e.length>Mt)return;let t;try{t=new URL(e)}catch{return}if(!(t.protocol!=="https:"&&t.protocol!=="http:"))return t.href}var ot=300;function ge(e){if(typeof e!="string")return;let t=e.replace(/[\u0000-\u001f\u007f-\u009f]+/g," ").replace(/\s+/g," ").trim();if(t.length!==0)return t.length>ot?`${t.slice(0,ot-1).trimEnd()}\u2026`:t}function he(e){return ge(e?.reason)}function be(e,t=window){let n=D(e);return{name:"postMessage",carries:()=>!0,send(r){t.parent.postMessage(r,n)}}}function De(e){if(!e||e==="*")throw new Error("[annotation] a concrete ingest endpoint is required; '*' is not an address and an empty one posts nowhere.");let t;try{t=new URL(e)}catch{throw new Error(`[annotation] the ingest endpoint must be an absolute URL, got ${JSON.stringify(e)}; a relative one resolves against the product's own origin and the capture is lost there.`)}if(t.protocol!=="https:"&&t.protocol!=="http:")throw new Error(`[annotation] the ingest endpoint must be http(s), got ${t.protocol}`);return t.toString()}var He=8e3;function at(e){let t=De(e.endpoint);if(!e.captureToken)throw new Error("[annotation] a capture token is required; the ingest rejects an unsigned capture and the reviewer would only find out after writing one.");let n=e.captureToken,r=e.timeoutMs??He,o=e.fetch??globalThis.fetch;return{name:"http",carries:a=>a==="annotate:capture",async send(a){if(!o)throw new Error("[annotation] no fetch implementation is available for the ingest");let s=new AbortController,c=setTimeout(()=>s.abort(),r);try{let f=await o(t,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${n}`},credentials:"omit",body:JSON.stringify(a),signal:s.signal});if(!f.ok){let m;try{let p=await f.json?.();m=ge(p?.error)}catch{m=void 0}let g=new Error(`[annotation] the ingest rejected the capture: ${m??`HTTP ${f.status}`}`);throw g.status=f.status,m!==void 0&&(g.reason=m),g}try{let m=await f.json?.(),g=Z(m?.issueUrl);if(g!==void 0)return{issueUrl:g}}catch{}return{}}catch(f){throw s.signal.aborted?new Error(`[annotation] the capture timed out after ${r}ms`,{cause:f}):f}finally{clearTimeout(c)}}}}function Ee(e,t){let n=(r,o)=>{let a={message:r,transport:e.name,error:o};if(t){t(a);return}console.error("[annotation] a capture could not be delivered",a)};return async r=>{if(!e.carries(r.type))return{status:"declined"};try{return{status:"sent",receipt:await e.send(r)??{}}}catch(o){return n(r,o),{status:"failed",error:o}}}}var it=49152,st=480,lt=250,Nt=[{edge:480,quality:.7},{edge:240,quality:.5}],It=["image/webp","image/jpeg"];function ye(e,t){let n=e===!1?{enabled:!1}:e??{};return{enabled:n.enabled??!0,maxBytes:n.maxBytes??49152,maxEdge:n.maxEdge??480,timeoutMs:n.timeoutMs??250,createCanvas:n.createCanvas??(()=>t?.createElement("canvas")??null)}}var Lt=new Set(["IMG","CANVAS","VIDEO"]),Pt="img,canvas,video",Ft=4;function Q(e){return!!e&&Lt.has(e.tagName.toUpperCase())}function Ke(e,t=Ft){if(!e)return null;if(Q(e))return e;let n=e.querySelector?.(Pt)??null;if(Q(n))return n;let r=e.parentElement??null;for(let o=0;r&&o<t;o++){if(Q(r))return r;r=r.parentElement??null}return null}function $e(e){let t=e.tagName.toUpperCase(),n=t==="IMG"?{w:e.naturalWidth,h:e.naturalHeight}:t==="VIDEO"?{w:e.videoWidth,h:e.videoHeight}:{w:e.width,h:e.height},r=Math.floor(n.w??0),o=Math.floor(n.h??0);return!Number.isFinite(r)||!Number.isFinite(o)||r<1||o<1||t==="IMG"&&e.complete===!1?null:{w:r,h:o}}function Be(e,t){let n=Math.min(1,t/Math.max(e.w,e.h));return{w:Math.max(1,Math.round(e.w*n)),h:Math.max(1,Math.round(e.h*n))}}function Ut(e,t,n,r){let o;try{o=r.createCanvas()}catch{return null}if(!o)return null;try{o.width=t.w,o.height=t.h;let a=o.getContext("2d");if(!a)return null;a.drawImage(e,0,0,t.w,t.h);for(let s of It){let c;try{c=o.toDataURL(s,n)}catch{return null}if(!(typeof c!="string"||!c.startsWith(`data:${s}`)))return c.length<=r.maxBytes?c:null}return null}catch{return null}}function qe(e,t){if(!t.enabled)return null;let n=Ke(e);if(!n)return null;let r=$e(n);if(!r)return null;for(let o of Nt){let a=Be(r,Math.min(t.maxEdge,o.edge)),s=Ut(n,a,o.quality,t);if(s)return{dataUrl:s,region:"element"}}return null}function Xe(e,t){return t.enabled?new Promise(n=>{let r=!1,o=s=>{r||(r=!0,n(s))},a=setTimeout(()=>o(null),t.timeoutMs);a.unref?.(),Promise.resolve().then(()=>qe(e,t)).then(o,()=>o(null)).finally(()=>clearTimeout(a))}):Promise.resolve(null)}async function ee(e,t,n){let r;try{r=await Xe(t,n)}catch{r=null}return r?{...e,screenshot:r}:e}var Dt={consoleErrors:[],failedRequests:[],featureFlags:{}},ct=()=>({...Dt});function Ae(e){let t=e.parentOrigin===void 0?null:D(e.parentOrigin),n=window,r=n.document,o=ye(e.screenshot,r),a=!1,s=null,c=0,f=null,m=!1,g=e.transport??(t===null?null:be(t,n));if(g===null)throw new Error("[annotation] the SDK needs a `transport` or a `parentOrigin`; with neither there is nowhere for a capture to go and every one of them would be lost in silence.");let p=Ee(g,e.onEgressError),u=l=>{let d=l.getBoundingClientRect();return{x:d.x,y:d.y,w:d.width,h:d.height}},A=l=>me(l,{boundingBox:u(l)}),T=l=>{if(l.componentName&&l.source){let d=l.source.file.split("/").pop()??l.source.file;return`${l.componentName} \xB7 ${d}:${l.source.line}`}return l.source?`${l.source.file}:${l.source.line}`:l.componentName??null},x=l=>{!a||m||c||(c=n.requestAnimationFrame(()=>{c=0;let d=r.elementFromPoint(l.clientX,l.clientY);if(!d||s&&s.contains(d)||e.ignoreElement?.(d)||d===f)return;f=d;let b=A(d);s?.show(b.boundingBox,T(b)),p({type:"annotate:hover",target:b})}))},R=(l,d)=>({schemaVersion:1,capturedAt:new Date().toISOString(),build:e.build,page:{url:n.location.href,route:e.getRoute(),viewport:{w:n.innerWidth,h:n.innerHeight,dpr:n.devicePixelRatio},userAgent:n.navigator.userAgent},target:A(l),state:{consoleErrors:d.consoleErrors,failedRequests:d.failedRequests,featureFlags:d.featureFlags},comment:""}),N=l=>{let d={type:"annotate:capture",payload:l},b=()=>{p(d).then(O=>e.onCaptureResult?.(O,b))};b()},_=l=>{if(!a)return;let d=l.target;if(!(d instanceof Element)||e.ignoreElement?.(d)||(l.preventDefault(),l.stopPropagation(),m))return;let b=R(d,e.getState()),O=e.collectComment;if(!O){if(!o.enabled){N(b);return}ee(b,d,o).then(C=>N(C),()=>N(b));return}let $={x:l.clientX,y:l.clientY},B=o.enabled?ee(b,d,o).then(C=>C,()=>b):Promise.resolve(b),I=()=>{m=!0;let C=null,ae=O({target:b.target,point:$}).then(y=>y,y=>(C={error:y},null));Promise.all([B,ae]).then(([y,U])=>{if(m=!1,C!==null){e.onCaptureResult?.({status:"failed",error:C.error},I);return}U!==null&&N(U===""?y:{...y,comment:U})})};I()},P=l=>{!a||m||l.key!=="Escape"||(p({type:"annotate:cancel"}),k())};function F(){a||(a=!0,s??(s=de(r)),r.addEventListener("mousemove",x,!0),r.addEventListener("click",_,!0),r.addEventListener("keydown",P,!0))}function k(){a&&(a=!1,f=null,c&&(n.cancelAnimationFrame(c),c=0),r.removeEventListener("mousemove",x,!0),r.removeEventListener("click",_,!0),r.removeEventListener("keydown",P,!0),s?.hide())}let S=l=>{if(l.origin===t&&se(l.data))switch(l.data.type){case"annotate:enable":F();break;case"annotate:disable":k();break;case"annotate:ping":p({type:"annotate:ready",sdkVersion:"1",build:e.build});break}};return t!==null&&n.addEventListener("message",S),p({type:"annotate:ready",sdkVersion:"1",build:e.build}),{enable:F,disable:k,isEnabled:()=>a,destroy(){k(),t!==null&&n.removeEventListener("message",S),s?.destroy(),s=null}}}var te="ai.annotation.capture",ne="ai.annotation.run",re="ai.annotation.read",ut=[...z,j,Y,V];function v(e,t){try{return e()}catch{return t}}function Ht(e){return v(()=>e?.sessionStorage??null,null)}function dt(e={}){let t=e.doc??document,n=t.defaultView??null,r=e.storage===void 0?Ht(n):e.storage,o=e.url??t.URL,a=v(()=>new URL(o),null),s=null,c=null,f=null;if(a!==null&&(s=a.searchParams.get(j)||null,c=a.searchParams.get(Y)||null,f=a.searchParams.get(V)||null,ut.some(u=>a.searchParams.has(u)))){for(let A of ut)a.searchParams.delete(A);let u=e.history??n?.history;v(()=>{u?.replaceState(n?.history?.state??null,"",a.toString())},void 0)}let m=()=>{for(let p of J)v(()=>{t.cookie=ce(p)},void 0);r&&(v(()=>r.removeItem(te),void 0),v(()=>r.removeItem(re),void 0),v(()=>r.removeItem(ne),void 0))},g=ue(v(()=>t.cookie,""));return g?(r&&(s!==null?v(()=>r.setItem(te,s),void 0):s=v(()=>r.getItem(te),null),c!==null?v(()=>r.setItem(re,c),void 0):c=v(()=>r.getItem(re),null),f!==null?v(()=>r.setItem(ne,f),void 0):f=v(()=>r.getItem(ne),null)),{armed:g,captureToken:s,readToken:c,runId:f,disarm:m}):{armed:!1,captureToken:null,readToken:null,runId:null,disarm:m}}var Kt=["preview","staging","development"];function xe(e){return Kt.includes(e.env)}var Ge={region:"Annotation",expand:"Open the annotation toolbar",collapse:"Close the annotation toolbar",start:"Start annotating",stop:"Stop annotating",done:"Done",comment:"What is wrong here?",placeholder:"The heading wraps onto three lines on mobile\u2026",send:"Send",cancel:"Cancel",sent:"Sent",issue:"View issue",failed:"That did not send",retry:"Retry"},We="ai-annotation-toolbar",$t="2147483001",ft=288,pt=190,H=12,Bt=`
2
2
  :host{
3
3
  --ai-bg:#fff;
4
4
  --ai-fg:#18181b;
@@ -18,7 +18,7 @@
18
18
  --ai-radius-control:6px;
19
19
  --ai-radius-card:8px;
20
20
  --ai-radius-pill:999px;
21
- all:initial;position:fixed;right:16px;bottom:16px;z-index:${Ht};
21
+ all:initial;position:fixed;right:16px;bottom:16px;z-index:${$t};
22
22
  font:13px/1.45 ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
23
23
  color-scheme:light}
24
24
  *,*::before,*::after{box-sizing:border-box}
@@ -41,7 +41,7 @@ button:focus-visible{outline:2px solid var(--ai-accent);outline-offset:2px}
41
41
  padding:var(--ai-space-2)}
42
42
  .toggle[aria-pressed="true"]{background:var(--ai-accent);border-color:var(--ai-accent);
43
43
  color:var(--ai-invert-fg)}
44
- .composer{position:fixed;width:${dt}px;background:var(--ai-bg);color:var(--ai-fg);
44
+ .composer{position:fixed;width:${ft}px;background:var(--ai-bg);color:var(--ai-fg);
45
45
  border:1px solid var(--ai-line);border-radius:var(--ai-radius-card);
46
46
  padding:var(--ai-space-4);display:flex;flex-direction:column;gap:var(--ai-space-3)}
47
47
  .where{margin:0;font:11px/16px ui-monospace,SFMono-Regular,Menlo,monospace;
@@ -61,5 +61,5 @@ textarea:focus-visible{outline:2px solid var(--ai-accent);outline-offset:1px}
61
61
  .toast a{color:var(--ai-invert-fg)}
62
62
  .toast button{background:transparent;border-color:var(--ai-invert-line);
63
63
  color:var(--ai-invert-fg)}
64
- `;function h(e,t,n){let r=e.createElement(t);if(n)for(let o of Object.keys(n))r.setAttribute(o,n[o]??"");return r}function Ge(e={}){let t={...qe,...e.labels},n=e.doc??document,r=e.win??window;if(e.armed===!1)return{owns:()=>!1,attach:()=>{},collectComment:()=>Promise.resolve(""),showResult:()=>{},isExpanded:()=>!1,destroy:()=>{}};let o=h(n,"div",{id:Xe}),a=o.attachShadow({mode:"open"}),s=h(n,"style");s.textContent=Kt,a.appendChild(s);let c=h(n,"style");a.appendChild(c);let m=h(n,"div",{id:"ai-annotation-bar",class:"bar",role:"region","aria-label":t.region}),f=h(n,"div",{class:"panel",id:"ai-annotation-panel",hidden:""}),g=h(n,"button",{type:"button",id:"ai-annotation-toggle",class:"toggle","aria-pressed":"false"});g.textContent=t.start;let d=h(n,"button",{type:"button",id:"ai-annotation-done"});d.textContent=t.done,f.appendChild(g),f.appendChild(d);let p=h(n,"button",{type:"button",id:"ai-annotation-pill",class:"pill","aria-expanded":"false","aria-controls":"ai-annotation-panel","aria-label":t.expand}),T=h(n,"span",{class:"dot","aria-hidden":"true"}),x=h(n,"span");x.textContent=t.region,p.appendChild(T),p.appendChild(x),m.appendChild(p),m.appendChild(f),a.appendChild(m);let A=h(n,"div",{id:"ai-annotation-composer",class:"composer",role:"dialog","aria-modal":"false","aria-labelledby":"ai-annotation-comment",hidden:""}),R=h(n,"p",{class:"where"}),N=h(n,"label",{id:"ai-annotation-comment",for:"ai-annotation-input"});N.textContent=t.comment;let k=h(n,"textarea",{id:"ai-annotation-input",placeholder:t.placeholder,rows:"3"}),P=h(n,"button",{type:"button",id:"ai-annotation-cancel"});P.textContent=t.cancel;let F=h(n,"button",{type:"button",id:"ai-annotation-send",class:"primary"});F.textContent=t.send;let _=h(n,"div",{class:"row"});_.appendChild(P),_.appendChild(F),A.appendChild(R),A.appendChild(N),A.appendChild(k),A.appendChild(_),a.appendChild(A);let v=h(n,"div",{id:"ai-annotation-toast",class:"toast",role:"status",hidden:""}),l=h(n,"span"),u=h(n,"a",{id:"ai-annotation-issue",target:"_blank",rel:"noopener noreferrer",hidden:""}),b=h(n,"button",{type:"button",id:"ai-annotation-retry",hidden:""});b.textContent=t.retry,v.appendChild(l),v.appendChild(u),v.appendChild(b),a.appendChild(v),n.body.appendChild(o);let O=null,$=!1,B=null,I=null,S=null,re=()=>{},E=(i,y)=>{y?i.removeAttribute("hidden"):i.setAttribute("hidden","")},U=i=>{$=i,E(f,i),p.setAttribute("aria-expanded",String(i)),p.setAttribute("aria-label",i?t.collapse:t.expand)},Te=i=>{g.setAttribute("aria-pressed",String(i)),g.textContent=i?t.stop:t.start,m.setAttribute("class",i?"bar on":"bar"),i?O?.enable():O?.disable()};p.addEventListener("click",()=>U(!$)),g.addEventListener("click",()=>{Te(O?.isEnabled()!==!0)});let Ye=()=>{S!==null&&clearTimeout(S),q(null),o.remove()};d.addEventListener("click",()=>{Te(!1),U(!1),e.onDone?.(),Ye()});let q=i=>{let y=B;B=null,E(A,!1),y&&y(i),I instanceof HTMLElement&&I.focus(),I=null};P.addEventListener("click",()=>q(null)),F.addEventListener("click",()=>q(k.value)),A.addEventListener("keydown",i=>{if(i.key==="Escape"){i.stopPropagation(),q(null);return}i.key==="Enter"&&(i.metaKey||i.ctrlKey)&&(i.preventDefault(),q(k.value))});let ht=i=>new Promise(y=>{B=y,k.value="",R.textContent=bt(i),yt(i),E(v,!1),E(A,!0),I=n.activeElement,k.focus()});function bt(i){let{source:y,componentName:C}=i.target;return y?`${y.file}:${y.line}`:C??""}function yt(i){let y=i.target.boundingBox,C=y.y+y.h+H,X=C+ut<=r.innerHeight?C:Math.max(H,y.y-ut-H),ve=Math.min(Math.max(H,y.x),Math.max(H,r.innerWidth-dt-H));c.textContent=`.composer{left:${Math.round(ve)}px;top:${Math.round(X)}px}`}let Et=(i,y)=>{S!==null&&clearTimeout(S);let C=i.status==="sent",X=i.status==="sent"?J(i.receipt.issueUrl):void 0;v.setAttribute("class",C?"toast":"toast bad"),v.setAttribute("role",C?"status":"alert");let ve=C?void 0:me(i.error);l.textContent=C?t.sent:ve??t.failed,E(u,C&&X!==void 0),X!==void 0&&(u.setAttribute("href",X),u.textContent=t.issue),E(b,!C),E(v,!0),C&&(S=setTimeout(()=>E(v,!1),6e3)),re=y};return b.addEventListener("click",()=>{E(v,!1),re()}),{owns:i=>i instanceof Node&&(i===o||o.contains(i)),attach:i=>{O=i,Te(i.isEnabled())},collectComment:ht,showResult:Et,isExpanded:()=>$,destroy:Ye}}function pt(e){let t=e.toolbar?.armed;if(e.arming&&t!==void 0)throw new Error("[annotation] pass `arming` OR `toolbar.armed`, not both; two answers to whether this page may offer an annotation UI is how one of them stops being checked.");let n=(e.arming?e.arming.armed:t)!==!1&&Ee(e.build),r=Ge({...e.toolbar,armed:n,onDone:()=>{e.arming?.disarm(),o.destroy(),e.toolbar?.onDone?.()}}),o=ye({...e,collectComment:r.collectComment,onCaptureResult:r.showResult,ignoreElement:r.owns});return r.attach(o),{sdk:o,toolbar:r,destroy(){r.destroy(),o.destroy()}}}var ft=20;var M,ne,K=class{constructor(t=20){Ce(this,M,[]);Ce(this,ne);Ze(this,ne,Math.max(1,t))}push(t){L(this,M).push(t),L(this,M).length>L(this,ne)&&L(this,M).shift()}toArray(){return[...L(this,M)]}get size(){return L(this,M).length}};M=new WeakMap,ne=new WeakMap;function We(e){if(typeof e=="string")return e;if(e instanceof Error)return`${e.name}: ${e.message}`;if(e===null)return"null";if(e===void 0)return"undefined";if(typeof e=="object")try{return JSON.stringify(e)??String(e)}catch{try{return`[${Object.getPrototypeOf(e)?.constructor?.name??"object"}]`}catch{return"[object]"}}try{return String(e)}catch{return"[unrenderable]"}}var mt=(e,t)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e;function je(e){return mt(e.map(We).join(" "),500)}function Ae(e){for(let t of e)if(t instanceof Error&&typeof t.stack=="string")return mt(t.stack,2e3)}function xe(e){return e===0||e<200||e>=300}var Ve=()=>({limit:20,getFeatureFlags:()=>({}),now:()=>new Date().toISOString()});function gt(e,t=Ve()){let n=new K(t.limit),r=new K(t.limit),o=(d,p)=>{n.push({level:d,message:je(p),...Ae(p)?{stack:Ae(p)}:{},at:t.now()})},a=e.console.error,s=e.console.warn;e.console.error=function(...p){try{o("error",p)}catch{}return a.apply(e.console,p)},e.console.warn=function(...p){try{o("warn",p)}catch{}return s.apply(e.console,p)};let c=e.fetch,m=(d,p)=>{let T=typeof d=="string"?d:d instanceof URL?d.href:d?.url??String(d),x=p?.method??d?.method??"GET";return{url:T,method:x.toUpperCase()}};typeof c=="function"&&(e.fetch=async function(p,T){try{let x=await c.call(e,p,T);if(xe(x.status)){let{url:A,method:R}=m(p,T);r.push({url:A,method:R,status:x.status,at:t.now()})}return x}catch(x){let{url:A,method:R}=m(p,T);throw r.push({url:A,method:R,status:0,at:t.now()}),x}});let f=e.XMLHttpRequest,g=f?.prototype?.open;return typeof g=="function"&&(f.prototype.open=function(p,T,...x){return this.__aiAnnotateMethod=p.toUpperCase(),this.__aiAnnotateUrl=String(T),this.addEventListener("loadend",()=>{try{xe(this.status)&&r.push({url:this.__aiAnnotateUrl??"",method:this.__aiAnnotateMethod??"GET",status:this.status,at:t.now()})}catch{}}),g.apply(this,[p,T,...x])}),{getState(){return{consoleErrors:n.toArray(),failedRequests:r.toArray(),featureFlags:t.getFeatureFlags()}},destroy(){e.console.error=a,e.console.warn=s,typeof c=="function"&&(e.fetch=c),typeof g=="function"&&(f.prototype.open=g)}}}
64
+ `;function h(e,t,n){let r=e.createElement(t);if(n)for(let o of Object.keys(n))r.setAttribute(o,n[o]??"");return r}function je(e={}){let t={...Ge,...e.labels},n=e.doc??document,r=e.win??window;if(e.armed===!1)return{owns:()=>!1,attach:()=>{},collectComment:()=>Promise.resolve(""),showResult:()=>{},isExpanded:()=>!1,destroy:()=>{}};let o=h(n,"div",{id:We}),a=o.attachShadow({mode:"open"}),s=h(n,"style");s.textContent=Bt,a.appendChild(s);let c=h(n,"style");a.appendChild(c);let f=h(n,"div",{id:"ai-annotation-bar",class:"bar",role:"region","aria-label":t.region}),m=h(n,"div",{class:"panel",id:"ai-annotation-panel",hidden:""}),g=h(n,"button",{type:"button",id:"ai-annotation-toggle",class:"toggle","aria-pressed":"false"});g.textContent=t.start;let p=h(n,"button",{type:"button",id:"ai-annotation-done"});p.textContent=t.done,m.appendChild(g),m.appendChild(p);let u=h(n,"button",{type:"button",id:"ai-annotation-pill",class:"pill","aria-expanded":"false","aria-controls":"ai-annotation-panel","aria-label":t.expand}),A=h(n,"span",{class:"dot","aria-hidden":"true"}),T=h(n,"span");T.textContent=t.region,u.appendChild(A),u.appendChild(T),f.appendChild(u),f.appendChild(m),a.appendChild(f);let x=h(n,"div",{id:"ai-annotation-composer",class:"composer",role:"dialog","aria-modal":"false","aria-labelledby":"ai-annotation-comment",hidden:""}),R=h(n,"p",{class:"where"}),N=h(n,"label",{id:"ai-annotation-comment",for:"ai-annotation-input"});N.textContent=t.comment;let _=h(n,"textarea",{id:"ai-annotation-input",placeholder:t.placeholder,rows:"3"}),P=h(n,"button",{type:"button",id:"ai-annotation-cancel"});P.textContent=t.cancel;let F=h(n,"button",{type:"button",id:"ai-annotation-send",class:"primary"});F.textContent=t.send;let k=h(n,"div",{class:"row"});k.appendChild(P),k.appendChild(F),x.appendChild(R),x.appendChild(N),x.appendChild(_),x.appendChild(k),a.appendChild(x);let S=h(n,"div",{id:"ai-annotation-toast",class:"toast",role:"status",hidden:""}),l=h(n,"span"),d=h(n,"a",{id:"ai-annotation-issue",target:"_blank",rel:"noopener noreferrer",hidden:""}),b=h(n,"button",{type:"button",id:"ai-annotation-retry",hidden:""});b.textContent=t.retry,S.appendChild(l),S.appendChild(d),S.appendChild(b),a.appendChild(S),n.body.appendChild(o);let O=null,$=!1,B=null,I=null,C=null,ae=()=>{},y=(i,E)=>{E?i.removeAttribute("hidden"):i.setAttribute("hidden","")},U=i=>{$=i,y(m,i),u.setAttribute("aria-expanded",String(i)),u.setAttribute("aria-label",i?t.collapse:t.expand)},Se=i=>{g.setAttribute("aria-pressed",String(i)),g.textContent=i?t.stop:t.start,f.setAttribute("class",i?"bar on":"bar"),i?O?.enable():O?.disable()};u.addEventListener("click",()=>U(!$)),g.addEventListener("click",()=>{Se(O?.isEnabled()!==!0)});let Je=()=>{C!==null&&clearTimeout(C),q(null),o.remove()};p.addEventListener("click",()=>{Se(!1),U(!1),e.onDone?.(),Je()});let q=i=>{let E=B;B=null,y(x,!1),E&&E(i),I instanceof HTMLElement&&I.focus(),I=null};P.addEventListener("click",()=>q(null)),F.addEventListener("click",()=>q(_.value)),x.addEventListener("keydown",i=>{if(i.key==="Escape"){i.stopPropagation(),q(null);return}i.key==="Enter"&&(i.metaKey||i.ctrlKey)&&(i.preventDefault(),q(_.value))});let Et=i=>new Promise(E=>{B=E,_.value="",R.textContent=yt(i),At(i),y(S,!1),y(x,!0),I=n.activeElement,_.focus()});function yt(i){let{source:E,componentName:w}=i.target;return E?`${E.file}:${E.line}`:w??""}function At(i){let E=i.target.boundingBox,w=E.y+E.h+H,X=w+pt<=r.innerHeight?w:Math.max(H,E.y-pt-H),Ce=Math.min(Math.max(H,E.x),Math.max(H,r.innerWidth-ft-H));c.textContent=`.composer{left:${Math.round(Ce)}px;top:${Math.round(X)}px}`}let xt=(i,E)=>{C!==null&&clearTimeout(C);let w=i.status==="sent",X=i.status==="sent"?Z(i.receipt.issueUrl):void 0;S.setAttribute("class",w?"toast":"toast bad"),S.setAttribute("role",w?"status":"alert");let Ce=w?void 0:he(i.error);l.textContent=w?t.sent:Ce??t.failed,y(d,w&&X!==void 0),X!==void 0&&(d.setAttribute("href",X),d.textContent=t.issue),y(b,!w),y(S,!0),w&&(C=setTimeout(()=>y(S,!1),6e3)),ae=E};return b.addEventListener("click",()=>{y(S,!1),ae()}),{owns:i=>i instanceof Node&&(i===o||o.contains(i)),attach:i=>{O=i,Se(i.isEnabled())},collectComment:Et,showResult:xt,isExpanded:()=>$,destroy:Je}}function mt(e){let t=e.toolbar?.armed;if(e.arming&&t!==void 0)throw new Error("[annotation] pass `arming` OR `toolbar.armed`, not both; two answers to whether this page may offer an annotation UI is how one of them stops being checked.");let n=(e.arming?e.arming.armed:t)!==!1&&xe(e.build),r=je({...e.toolbar,armed:n,onDone:()=>{e.arming?.disarm(),o.destroy(),e.toolbar?.onDone?.()}}),o=Ae({...e,collectComment:r.collectComment,onCaptureResult:r.showResult,ignoreElement:r.owns});return r.attach(o),{sdk:o,toolbar:r,destroy(){r.destroy(),o.destroy()}}}var gt=20;var M,oe,K=class{constructor(t=20){Oe(this,M,[]);Oe(this,oe);et(this,oe,Math.max(1,t))}push(t){L(this,M).push(t),L(this,M).length>L(this,oe)&&L(this,M).shift()}toArray(){return[...L(this,M)]}get size(){return L(this,M).length}};M=new WeakMap,oe=new WeakMap;function Ve(e){if(typeof e=="string")return e;if(e instanceof Error)return`${e.name}: ${e.message}`;if(e===null)return"null";if(e===void 0)return"undefined";if(typeof e=="object")try{return JSON.stringify(e)??String(e)}catch{try{return`[${Object.getPrototypeOf(e)?.constructor?.name??"object"}]`}catch{return"[object]"}}try{return String(e)}catch{return"[unrenderable]"}}var ht=(e,t)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e;function Ye(e){return ht(e.map(Ve).join(" "),500)}function Te(e){for(let t of e)if(t instanceof Error&&typeof t.stack=="string")return ht(t.stack,2e3)}function ve(e){return e===0||e<200||e>=300}var ze=()=>({limit:20,getFeatureFlags:()=>({}),now:()=>new Date().toISOString()});function bt(e,t=ze()){let n=new K(t.limit),r=new K(t.limit),o=(p,u)=>{n.push({level:p,message:Ye(u),...Te(u)?{stack:Te(u)}:{},at:t.now()})},a=e.console.error,s=e.console.warn;e.console.error=function(...u){try{o("error",u)}catch{}return a.apply(e.console,u)},e.console.warn=function(...u){try{o("warn",u)}catch{}return s.apply(e.console,u)};let c=e.fetch,f=(p,u)=>{let A=typeof p=="string"?p:p instanceof URL?p.href:p?.url??String(p),T=u?.method??p?.method??"GET";return{url:A,method:T.toUpperCase()}};typeof c=="function"&&(e.fetch=async function(u,A){try{let T=await c.call(e,u,A);if(ve(T.status)){let{url:x,method:R}=f(u,A);r.push({url:x,method:R,status:T.status,at:t.now()})}return T}catch(T){let{url:x,method:R}=f(u,A);throw r.push({url:x,method:R,status:0,at:t.now()}),T}});let m=e.XMLHttpRequest,g=m?.prototype?.open;return typeof g=="function"&&(m.prototype.open=function(u,A,...T){return this.__aiAnnotateMethod=u.toUpperCase(),this.__aiAnnotateUrl=String(A),this.addEventListener("loadend",()=>{try{ve(this.status)&&r.push({url:this.__aiAnnotateUrl??"",method:this.__aiAnnotateMethod??"GET",status:this.status,at:t.now()})}catch{}}),g.apply(this,[u,A,...T])}),{getState(){return{consoleErrors:n.toArray(),failedRequests:r.toArray(),featureFlags:t.getFeatureFlags()}},destroy(){e.console.error=a,e.console.warn=s,typeof c=="function"&&(e.fetch=c),typeof g=="function"&&(m.prototype.open=g)}}}
65
65
  //# sourceMappingURL=index.cjs.map