@devaccel-labs/app-comments-toolbar 1.0.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +152 -18
- package/dist/react.js +516 -61
- package/dist/server.js +342 -14
- package/dist/types/capture/api.d.ts +30 -0
- package/dist/types/capture/editor.d.ts +10 -0
- package/dist/types/capture/ignore.d.ts +19 -0
- package/dist/types/capture/index.d.ts +1 -0
- package/dist/types/capture/picker.d.ts +9 -0
- package/dist/types/capture/picture.d.ts +29 -0
- package/dist/types/capture/surface.d.ts +39 -0
- package/dist/types/core/index.d.ts +2 -2
- package/dist/types/core/resolve.d.ts +2 -1
- package/dist/types/core/types.d.ts +11 -0
- package/dist/types/debug-context.d.ts +7 -0
- package/dist/types/dictation.d.ts +9 -0
- package/dist/types/embed/controller.d.ts +61 -0
- package/dist/types/embed/index.d.ts +1 -0
- package/dist/types/embed/options.d.ts +35 -0
- package/dist/types/help/index.d.ts +1 -0
- package/dist/types/help/panel-index.d.ts +1 -0
- package/dist/types/help/panel-styles.d.ts +1 -0
- package/dist/types/help/panel.d.ts +4 -0
- package/dist/types/help/pill-styles.d.ts +11 -0
- package/dist/types/help/pill.d.ts +44 -0
- package/dist/types/help/react.d.ts +12 -0
- package/dist/types/help/requests.d.ts +2 -0
- package/dist/types/help/types.d.ts +119 -0
- package/dist/types/help/wire.d.ts +61 -0
- package/dist/types/native-wire.d.ts +63 -0
- package/dist/types/native.d.ts +43 -0
- package/dist/types/react.d.ts +8 -1
- package/dist/types/replay.d.ts +6 -0
- package/dist/types/runtime/api.d.ts +250 -0
- package/dist/types/runtime/attachment-model.d.ts +16 -0
- package/dist/types/runtime/attachments.d.ts +43 -7
- package/dist/types/runtime/boot.d.ts +39 -0
- package/dist/types/runtime/browser-label.d.ts +5 -0
- package/dist/types/runtime/build-id.d.ts +7 -0
- package/dist/types/runtime/capture-ui.d.ts +45 -21
- package/dist/types/runtime/clarity-model.d.ts +260 -0
- package/dist/types/runtime/clarity-ui.d.ts +62 -0
- package/dist/types/runtime/client.d.ts +6 -197
- package/dist/types/runtime/composer-size.d.ts +30 -0
- package/dist/types/runtime/dashboard.d.ts +17 -2
- package/dist/types/runtime/debug-context-core.d.ts +88 -0
- package/dist/types/runtime/debug-context-host.d.ts +39 -0
- package/dist/types/runtime/debug-context-model.d.ts +352 -0
- package/dist/types/runtime/debug-context-redact.d.ts +48 -0
- package/dist/types/runtime/debug-context.d.ts +36 -0
- package/dist/types/runtime/details-model.d.ts +7 -0
- package/dist/types/runtime/details.d.ts +7 -5
- package/dist/types/runtime/dictation-engine.d.ts +98 -0
- package/dist/types/runtime/dictation-model.d.ts +28 -0
- package/dist/types/runtime/dictation-ui.d.ts +56 -0
- package/dist/types/runtime/dictation.d.ts +66 -0
- package/dist/types/runtime/draft-dock.d.ts +32 -0
- package/dist/types/runtime/draft-model.d.ts +104 -0
- package/dist/types/runtime/draft-store.d.ts +15 -0
- package/dist/types/runtime/edits.d.ts +1 -1
- package/dist/types/runtime/font.d.ts +16 -0
- package/dist/types/runtime/format.d.ts +30 -1
- package/dist/types/runtime/host-watch.d.ts +20 -0
- package/dist/types/runtime/icons.d.ts +16 -2
- package/dist/types/runtime/index.d.ts +3 -1
- package/dist/types/runtime/ink.d.ts +8 -0
- package/dist/types/runtime/kind-model.d.ts +14 -0
- package/dist/types/runtime/live-model.d.ts +25 -0
- package/dist/types/runtime/loader.d.ts +5 -11
- package/dist/types/runtime/markup-editor.d.ts +54 -0
- package/dist/types/runtime/markup-model.d.ts +126 -0
- package/dist/types/runtime/markup.d.ts +69 -0
- package/dist/types/runtime/mention-parse.d.ts +18 -0
- package/dist/types/runtime/mentions.d.ts +4 -4
- package/dist/types/runtime/page-background.d.ts +9 -0
- package/dist/types/runtime/page-image.d.ts +123 -0
- package/dist/types/runtime/placement.d.ts +6 -3
- package/dist/types/runtime/record.d.ts +18 -2
- package/dist/types/runtime/related.d.ts +1 -1
- package/dist/types/runtime/replay-model.d.ts +80 -0
- package/dist/types/runtime/replay.d.ts +5 -38
- package/dist/types/runtime/runtime.d.ts +6 -0
- package/dist/types/runtime/screen.d.ts +47 -0
- package/dist/types/runtime/screenshot.d.ts +60 -6
- package/dist/types/runtime/session-source.d.ts +45 -0
- package/dist/types/runtime/session.d.ts +20 -18
- package/dist/types/runtime/shield.d.ts +9 -0
- package/dist/types/runtime/shot.d.ts +45 -0
- package/dist/types/runtime/sketch.d.ts +26 -0
- package/dist/types/runtime/styles.d.ts +1 -1
- package/dist/types/runtime/sync.d.ts +1 -0
- package/dist/types/runtime/thread-model.d.ts +18 -0
- package/dist/types/runtime/ticket-form-model.d.ts +57 -0
- package/dist/types/runtime/ticket-form.d.ts +9 -0
- package/dist/types/runtime/toolbar.d.ts +37 -1
- package/dist/types/runtime/ui-scale.d.ts +17 -0
- package/dist/types/runtime/update.d.ts +37 -0
- package/dist/types/runtime/use-debug-context.d.ts +27 -0
- package/dist/types/server/index.d.ts +22 -0
- package/dist/types/server/support.d.ts +20 -0
- package/dist/types/server.d.ts +2 -0
- package/dist/types/vite/plugin.d.ts +15 -0
- package/dist/vite.js +121 -41
- package/package.json +26 -13
- package/src/capture/api.ts +128 -0
- package/src/capture/editor.tsx +178 -0
- package/src/capture/ignore.ts +38 -0
- package/src/capture/index.ts +27 -0
- package/src/capture/picker.tsx +171 -0
- package/src/capture/picture.ts +90 -0
- package/src/capture/surface.tsx +185 -0
- package/src/compiler/annotate.ts +202 -0
- package/src/compiler/babel.d.ts +11 -0
- package/src/compiler/enabled.ts +55 -0
- package/src/compiler/index.ts +2 -0
- package/src/core/index.ts +8 -0
- package/src/core/resolve.ts +307 -0
- package/src/core/types.ts +58 -0
- package/src/debug-context.ts +47 -0
- package/src/dictation.ts +17 -0
- package/src/embed/controller.ts +215 -0
- package/src/embed/index.ts +68 -0
- package/src/embed/options.ts +100 -0
- package/src/help/index.ts +38 -0
- package/src/help/panel-index.ts +17 -0
- package/src/help/panel-styles.ts +123 -0
- package/src/help/panel.ts +840 -0
- package/src/help/pill-styles.ts +75 -0
- package/src/help/pill.ts +338 -0
- package/src/help/react.tsx +143 -0
- package/src/help/requests.ts +111 -0
- package/src/help/types.ts +124 -0
- package/src/help/wire.ts +55 -0
- package/src/native-wire.ts +124 -0
- package/src/native.ts +282 -0
- package/src/react.ts +27 -0
- package/src/replay.ts +13 -0
- package/src/runtime/api.ts +531 -0
- package/src/runtime/attachment-model.ts +42 -0
- package/src/runtime/attachments.tsx +1260 -0
- package/src/runtime/boot.ts +95 -0
- package/src/runtime/browser-label.ts +31 -0
- package/src/runtime/build-id.ts +10 -0
- package/src/runtime/capture-ui.tsx +306 -0
- package/src/runtime/capture.ts +508 -0
- package/src/runtime/clarity-model.ts +634 -0
- package/src/runtime/clarity-ui.tsx +454 -0
- package/src/runtime/client.ts +151 -0
- package/src/runtime/composer-size.tsx +146 -0
- package/src/runtime/dashboard.tsx +1130 -0
- package/src/runtime/debug-context-core.ts +681 -0
- package/src/runtime/debug-context-host.ts +56 -0
- package/src/runtime/debug-context-model.ts +1319 -0
- package/src/runtime/debug-context-redact.ts +259 -0
- package/src/runtime/debug-context.ts +613 -0
- package/src/runtime/details-model.ts +30 -0
- package/src/runtime/details.tsx +395 -0
- package/src/runtime/dictation-engine.ts +616 -0
- package/src/runtime/dictation-model.ts +79 -0
- package/src/runtime/dictation-ui.tsx +363 -0
- package/src/runtime/dictation.ts +271 -0
- package/src/runtime/dock.ts +108 -0
- package/src/runtime/draft-dock.tsx +124 -0
- package/src/runtime/draft-model.ts +251 -0
- package/src/runtime/draft-store.ts +149 -0
- package/src/runtime/edits.tsx +165 -0
- package/src/runtime/font.ts +42 -0
- package/src/runtime/format.ts +129 -0
- package/src/runtime/freshness.ts +23 -0
- package/src/runtime/handoff.ts +76 -0
- package/src/runtime/host-watch.ts +64 -0
- package/src/runtime/icons.tsx +295 -0
- package/src/runtime/idle.ts +20 -0
- package/src/runtime/index.ts +11 -0
- package/src/runtime/ink.ts +6 -0
- package/src/runtime/kind-model.ts +20 -0
- package/src/runtime/live-model.ts +32 -0
- package/src/runtime/loader.tsx +87 -0
- package/src/runtime/markup-editor.tsx +1333 -0
- package/src/runtime/markup-model.ts +485 -0
- package/src/runtime/markup.ts +382 -0
- package/src/runtime/members.ts +58 -0
- package/src/runtime/mention-parse.ts +56 -0
- package/src/runtime/mentions.tsx +205 -0
- package/src/runtime/page-background.ts +51 -0
- package/src/runtime/page-image.ts +1592 -0
- package/src/runtime/placement.ts +336 -0
- package/src/runtime/record.ts +402 -0
- package/src/runtime/redact.ts +30 -0
- package/src/runtime/related.ts +34 -0
- package/src/runtime/render-page.ts +2 -0
- package/src/runtime/replay-model.ts +232 -0
- package/src/runtime/replay.ts +173 -0
- package/src/runtime/runtime-version.ts +7 -0
- package/src/runtime/runtime.tsx +51 -0
- package/src/runtime/screen.ts +182 -0
- package/src/runtime/screenshot.ts +260 -0
- package/src/runtime/session-source.ts +112 -0
- package/src/runtime/session.ts +108 -0
- package/src/runtime/shield.ts +34 -0
- package/src/runtime/shot.tsx +422 -0
- package/src/runtime/sketch.ts +139 -0
- package/src/runtime/styles.ts +1541 -0
- package/src/runtime/sync.ts +300 -0
- package/src/runtime/thread-model.ts +71 -0
- package/src/runtime/ticket-form-model.ts +128 -0
- package/src/runtime/ticket-form.tsx +342 -0
- package/src/runtime/toolbar.tsx +4427 -0
- package/src/runtime/ui-scale.ts +75 -0
- package/src/runtime/update.ts +98 -0
- package/src/runtime/use-debug-context.ts +81 -0
- package/src/server/index.ts +133 -0
- package/src/server/support.ts +499 -0
- package/src/server.ts +4 -0
- package/src/vite/plugin.ts +102 -0
- package/src/vite.ts +1 -0
- package/dist/chunks/chunk-CSKXLF33.js +0 -153
- package/dist/chunks/chunk-HUM5FQII.js +0 -160
- package/dist/chunks/render-page-76MLIT6M.js +0 -1421
- package/dist/chunks/runtime-JX75TKER.js +0 -9267
- package/dist/chunks/screenshot-66ARTBGR.js +0 -12
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import generateModule from "@babel/generator";
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
import * as t from "@babel/types";
|
|
4
|
+
|
|
5
|
+
/** A parsed Babel node, walked by its `VISITOR_KEYS` without `@babel/traverse`. */
|
|
6
|
+
type WalkNode = { type: string; [key: string]: any };
|
|
7
|
+
|
|
8
|
+
interface BabelGenerateOptions {
|
|
9
|
+
retainLines?: boolean;
|
|
10
|
+
comments?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function generate() {
|
|
14
|
+
const mod = generateModule as unknown as {
|
|
15
|
+
default?: (ast: unknown, options?: BabelGenerateOptions, code?: string) => { code: string };
|
|
16
|
+
(ast: unknown, options?: BabelGenerateOptions, code?: string): { code: string };
|
|
17
|
+
};
|
|
18
|
+
return mod.default ?? mod;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isWalkNode(value: unknown): value is WalkNode {
|
|
22
|
+
return (
|
|
23
|
+
value !== null &&
|
|
24
|
+
typeof value === "object" &&
|
|
25
|
+
typeof (value as { type?: unknown }).type === "string"
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function toProjectPath(filename: string, root: string) {
|
|
30
|
+
const clean = filename.split("?")[0].replaceAll("\\", "/");
|
|
31
|
+
const rootClean = root.replaceAll("\\", "/").replace(/\/$/, "");
|
|
32
|
+
let relative = clean.startsWith(`${rootClean}/`) ? clean.slice(rootClean.length + 1) : clean;
|
|
33
|
+
if (relative.startsWith("/")) relative = relative.slice(1);
|
|
34
|
+
return relative;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* `ancestors` is the chain from `File` down to the element's parent. Same checks as walking
|
|
39
|
+
* `path.parentPath` up from the element: the nearest named function or class declaration, or a
|
|
40
|
+
* function expression assigned to a variable, object property, or class property.
|
|
41
|
+
*/
|
|
42
|
+
function componentName(ancestors: readonly WalkNode[], fallback: string) {
|
|
43
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
44
|
+
const current = ancestors[i];
|
|
45
|
+
if (
|
|
46
|
+
(current.type === "FunctionDeclaration" || current.type === "ClassDeclaration") &&
|
|
47
|
+
current.id
|
|
48
|
+
) {
|
|
49
|
+
return current.id.name;
|
|
50
|
+
}
|
|
51
|
+
if (current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
52
|
+
const parent = i > 0 ? ancestors[i - 1] : null;
|
|
53
|
+
if (parent?.type === "VariableDeclarator" && parent.id.type === "Identifier")
|
|
54
|
+
return parent.id.name;
|
|
55
|
+
if (
|
|
56
|
+
(parent?.type === "ObjectProperty" || parent?.type === "ClassProperty") &&
|
|
57
|
+
parent.key.type === "Identifier"
|
|
58
|
+
) {
|
|
59
|
+
return parent.key.name;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return fallback;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** No JSX element sits between this one and its enclosing function (`path.getFunctionParent()`). */
|
|
67
|
+
function isRootJsx(ancestors: readonly WalkNode[]) {
|
|
68
|
+
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
69
|
+
const current = ancestors[i];
|
|
70
|
+
if (t.isFunction(current as t.Node)) return true;
|
|
71
|
+
if (current.type === "JSXElement") return false;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function hasAttr(node: WalkNode, name: string) {
|
|
77
|
+
return node.openingElement.attributes.some(
|
|
78
|
+
(attr: { type?: string; name?: { type?: string; name?: string } }) =>
|
|
79
|
+
attr.type === "JSXAttribute" &&
|
|
80
|
+
attr.name?.type === "JSXIdentifier" &&
|
|
81
|
+
attr.name.name === name,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function setAttr(node: WalkNode, name: string, value: string) {
|
|
86
|
+
if (hasAttr(node, name)) return;
|
|
87
|
+
node.openingElement.attributes.push(
|
|
88
|
+
t.jsxAttribute(t.jsxIdentifier(name), t.stringLiteral(value)),
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** React built-ins that reject or ignore extra props. */
|
|
93
|
+
const REACT_BUILTINS = new Set([
|
|
94
|
+
"Fragment",
|
|
95
|
+
"Suspense",
|
|
96
|
+
"StrictMode",
|
|
97
|
+
"Profiler",
|
|
98
|
+
"Activity",
|
|
99
|
+
"ViewTransition",
|
|
100
|
+
]);
|
|
101
|
+
const contextComponent = /\.(?:Provider|Consumer)$/;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The names this file uses for React's built-ins: `Fragment`, `React.Fragment`, and whatever the
|
|
105
|
+
* file imports them as (`import { Fragment as Frag }`, `import * as R from "react"` → `R.Fragment`).
|
|
106
|
+
*/
|
|
107
|
+
function reactBuiltinTags(program: WalkNode): ReadonlySet<string> {
|
|
108
|
+
const tags = new Set<string>();
|
|
109
|
+
const namespaces = new Set(["React"]);
|
|
110
|
+
for (const statement of (program.body ?? []) as WalkNode[]) {
|
|
111
|
+
if (statement.type !== "ImportDeclaration" || statement.source?.value !== "react") continue;
|
|
112
|
+
for (const specifier of (statement.specifiers ?? []) as WalkNode[]) {
|
|
113
|
+
const local = specifier.local?.name;
|
|
114
|
+
if (typeof local !== "string") continue;
|
|
115
|
+
if (
|
|
116
|
+
specifier.type === "ImportNamespaceSpecifier" ||
|
|
117
|
+
specifier.type === "ImportDefaultSpecifier"
|
|
118
|
+
) {
|
|
119
|
+
namespaces.add(local);
|
|
120
|
+
} else if (specifier.type === "ImportSpecifier") {
|
|
121
|
+
const imported = specifier.imported?.name ?? specifier.imported?.value;
|
|
122
|
+
if (REACT_BUILTINS.has(imported)) tags.add(local);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const builtin of REACT_BUILTINS) {
|
|
127
|
+
tags.add(builtin);
|
|
128
|
+
for (const namespace of namespaces) tags.add(`${namespace}.${builtin}`);
|
|
129
|
+
}
|
|
130
|
+
return tags;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function jsxName(name: any): string | null {
|
|
134
|
+
if (t.isJSXIdentifier(name)) return name.name;
|
|
135
|
+
if (t.isJSXMemberExpression(name)) {
|
|
136
|
+
const object = jsxName(name.object);
|
|
137
|
+
return object ? `${object}.${name.property.name}` : null;
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Stamp the root node of each component of each component with a stable file + component name.
|
|
144
|
+
* Line numbers are intentionally not included: moving the component in the file
|
|
145
|
+
* or on the page must not change the stamp. A root that is itself a component
|
|
146
|
+
* (`<DropdownMenu>`, `<Menu.Root>`) gets the stamp as a prop: it reaches the DOM when
|
|
147
|
+
* the component spreads its props, and the toolbar reads it from React's tree when
|
|
148
|
+
* the component renders into a portal.
|
|
149
|
+
*/
|
|
150
|
+
export function annotateSource(code: string, filename: string, root: string) {
|
|
151
|
+
if (!filename.match(/\.[jt]sx$/)) return code;
|
|
152
|
+
if (filename.includes("node_modules")) return code;
|
|
153
|
+
let ast: ReturnType<typeof parse>;
|
|
154
|
+
try {
|
|
155
|
+
ast = parse(code, {
|
|
156
|
+
sourceType: "module",
|
|
157
|
+
plugins: ["jsx", "typescript"],
|
|
158
|
+
});
|
|
159
|
+
} catch {
|
|
160
|
+
return code;
|
|
161
|
+
}
|
|
162
|
+
const file = toProjectPath(filename, root);
|
|
163
|
+
const fallback =
|
|
164
|
+
file
|
|
165
|
+
.split("/")
|
|
166
|
+
.pop()
|
|
167
|
+
?.replace(/\.[jt]sx$/, "") || "component";
|
|
168
|
+
let changed = false;
|
|
169
|
+
const ancestors: WalkNode[] = [];
|
|
170
|
+
const builtins = reactBuiltinTags((ast as unknown as WalkNode).program as WalkNode);
|
|
171
|
+
const stamp = (node: WalkNode) => {
|
|
172
|
+
const tag = jsxName(node.openingElement.name);
|
|
173
|
+
if (!tag || builtins.has(tag) || contextComponent.test(tag)) return;
|
|
174
|
+
if (!isRootJsx(ancestors)) return;
|
|
175
|
+
const name = componentName(ancestors, fallback);
|
|
176
|
+
const before = node.openingElement.attributes.length;
|
|
177
|
+
setAttr(node, "data-cs-cmp", name);
|
|
178
|
+
setAttr(node, "data-cs-file", file);
|
|
179
|
+
if (node.openingElement.attributes.length !== before) changed = true;
|
|
180
|
+
};
|
|
181
|
+
// Walks the AST directly instead of through `@babel/traverse`: its NodePath and scope
|
|
182
|
+
// bookkeeping was most of the plugin's build cost (2026-10-24: 2.67 s → 0.86 s over
|
|
183
|
+
// apps/workspace). Same visit order (`VISITOR_KEYS`, pre-order, an element is stamped before
|
|
184
|
+
// its children are walked), so the output is byte-identical to the traverse version.
|
|
185
|
+
const visit = (node: WalkNode) => {
|
|
186
|
+
if (node.type === "JSXElement") stamp(node);
|
|
187
|
+
ancestors.push(node);
|
|
188
|
+
for (const key of t.VISITOR_KEYS[node.type] ?? []) {
|
|
189
|
+
const child = node[key];
|
|
190
|
+
if (Array.isArray(child)) {
|
|
191
|
+
for (const item of child) if (isWalkNode(item)) visit(item);
|
|
192
|
+
} else if (isWalkNode(child)) {
|
|
193
|
+
visit(child);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
ancestors.pop();
|
|
197
|
+
};
|
|
198
|
+
visit(ast as unknown as WalkNode);
|
|
199
|
+
if (!changed) return code;
|
|
200
|
+
const printed = generate()(ast, { retainLines: true, comments: true }, code);
|
|
201
|
+
return printed.code;
|
|
202
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/** A host app that serves the toolbar sets this, in its env files, its Wrangler `vars`, or the build's environment. */
|
|
5
|
+
export const CONFIG_KEY = "COMMENT_SERVICE_URL";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Whether to stamp components. An explicit option or COMMENT_SERVICE_ENABLED wins; otherwise the dev server always
|
|
9
|
+
* stamps, and a build stamps only when it can see comment-service configuration.
|
|
10
|
+
*/
|
|
11
|
+
export function stampingEnabled({
|
|
12
|
+
option,
|
|
13
|
+
flag,
|
|
14
|
+
command,
|
|
15
|
+
configured,
|
|
16
|
+
}: {
|
|
17
|
+
option: boolean | undefined;
|
|
18
|
+
flag: string | undefined;
|
|
19
|
+
command: "build" | "serve";
|
|
20
|
+
configured: () => boolean;
|
|
21
|
+
}) {
|
|
22
|
+
if (option !== undefined) return option;
|
|
23
|
+
if (flag === "0" || flag === "false") return false;
|
|
24
|
+
if (flag === "1" || flag === "true") return true;
|
|
25
|
+
return command === "serve" || configured();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Comment-service configuration a build can see: its environment, Vite env files for the mode, or Wrangler config. */
|
|
29
|
+
export function buildConfigured(
|
|
30
|
+
root: string,
|
|
31
|
+
mode: string,
|
|
32
|
+
env: Record<string, string | undefined> = process.env,
|
|
33
|
+
) {
|
|
34
|
+
if (env[CONFIG_KEY]) return true;
|
|
35
|
+
const read = (file: string) => {
|
|
36
|
+
try {
|
|
37
|
+
return fs.readFileSync(path.join(root, file), "utf8");
|
|
38
|
+
} catch {
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const assignment = new RegExp(`^\\s*(?:export\\s+)?${CONFIG_KEY}\\s*=\\s*\\S`, "m");
|
|
43
|
+
if (
|
|
44
|
+
[".env", ".env.local", `.env.${mode}`, `.env.${mode}.local`].some((file) =>
|
|
45
|
+
assignment.test(read(file)),
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
return true;
|
|
49
|
+
const jsonKey = new RegExp(`^\\s*"${CONFIG_KEY}"\\s*:`, "m");
|
|
50
|
+
const tomlKey = new RegExp(`^\\s*${CONFIG_KEY}\\s*=`, "m");
|
|
51
|
+
return (
|
|
52
|
+
jsonKey.test(read("wrangler.jsonc") + read("wrangler.json")) ||
|
|
53
|
+
tomlKey.test(read("wrangler.toml"))
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import type { Anchor, Candidate, Resolution, ResolutionStatus, Sketch } from "./types.js";
|
|
2
|
+
|
|
3
|
+
const norm = (value: string | null | undefined) =>
|
|
4
|
+
(value ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
5
|
+
|
|
6
|
+
const labelOf = (value: { accName: string | null; text: string | null }) =>
|
|
7
|
+
norm(value.accName) || norm(value.text);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A path is an ancestor of another when it is a strict prefix ("0/1" contains "0/1/4").
|
|
11
|
+
* Equal paths are the same element, not a nesting relationship.
|
|
12
|
+
*/
|
|
13
|
+
function isAncestorPath(ancestor: string, descendant: string) {
|
|
14
|
+
if (!ancestor || ancestor === descendant) return false;
|
|
15
|
+
return descendant.startsWith(`${ancestor}/`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Drop elements that contain another match, so a wrapping div doesn't steal the button.
|
|
20
|
+
*
|
|
21
|
+
* Sorted by path, an element's descendants ("0/1/…") come directly after it: "/" sorts before every digit, so
|
|
22
|
+
* nothing else can sit between "0/1" and "0/1/4". One pass over the sorted list finds every ancestor, instead of
|
|
23
|
+
* comparing every pair; a page section with thousands of elements used to take seconds here.
|
|
24
|
+
*/
|
|
25
|
+
export function dropAncestors(candidates: Candidate[]) {
|
|
26
|
+
if (candidates.length < 2) return candidates;
|
|
27
|
+
const order = candidates.map((candidate, index) => ({ index, path: candidate.path }));
|
|
28
|
+
order.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
29
|
+
const dropped = new Set<number>();
|
|
30
|
+
for (const [i, { index, path }] of order.entries()) {
|
|
31
|
+
let next = i + 1;
|
|
32
|
+
while (order[next]?.path === path) next++;
|
|
33
|
+
const after = order[next];
|
|
34
|
+
if (after && isAncestorPath(path, after.path)) dropped.add(index);
|
|
35
|
+
}
|
|
36
|
+
return dropped.size ? candidates.filter((_, index) => !dropped.has(index)) : candidates;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The one candidate in `list`, or null when there are none or several. */
|
|
40
|
+
function single(list: readonly Candidate[]): Candidate | null {
|
|
41
|
+
return list.length === 1 ? (list[0] ?? null) : null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function finish(
|
|
45
|
+
status: ResolutionStatus,
|
|
46
|
+
candidate: Candidate | null,
|
|
47
|
+
score: number,
|
|
48
|
+
reason: string,
|
|
49
|
+
): Resolution {
|
|
50
|
+
if (candidate && !candidate.visible && (status === "pinned" || status === "drifted")) {
|
|
51
|
+
return { status: "hidden", key: candidate.key, score, reason: `hidden: ${reason}` };
|
|
52
|
+
}
|
|
53
|
+
return { status, key: candidate?.key ?? null, score, reason };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function sameExplicit(anchor: Anchor, candidate: Candidate) {
|
|
57
|
+
return anchor.explicit.some((expected) =>
|
|
58
|
+
candidate.explicit.some(
|
|
59
|
+
(actual) => actual.attr === expected.attr && actual.value === expected.value,
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function sameComponent(anchor: Anchor, candidate: Candidate, requireFile: boolean) {
|
|
65
|
+
if (!anchor.component || !candidate.component) return false;
|
|
66
|
+
if (anchor.component.name !== candidate.component.name) return false;
|
|
67
|
+
if (requireFile) {
|
|
68
|
+
return Boolean(anchor.component.file) && anchor.component.file === candidate.component.file;
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function sameRole(anchor: Anchor, candidate: Candidate) {
|
|
74
|
+
if (!anchor.role || !candidate.role) return !anchor.role;
|
|
75
|
+
return anchor.role === candidate.role;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function sameLabel(anchor: Anchor, candidate: Candidate) {
|
|
79
|
+
const expected = labelOf(anchor);
|
|
80
|
+
if (!expected) return false;
|
|
81
|
+
return expected === labelOf(candidate);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function unavailable(reason: string): Resolution {
|
|
85
|
+
return { status: "unavailable", key: null, score: 0, reason };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a stored anchor against the elements currently on the route.
|
|
90
|
+
*
|
|
91
|
+
* Order is deliberate:
|
|
92
|
+
* 1. An explicit id (`data-cs-anchor`, `data-testid`, stable `id`) wins.
|
|
93
|
+
* 2. Otherwise the match is component file + component name + role + label.
|
|
94
|
+
* 3. Sibling index and pixel position are ignored, so inserting, reordering,
|
|
95
|
+
* or moving the element (including out of a sheet) does not retarget the comment.
|
|
96
|
+
* 4. A tie is ambiguous. We do not fall back to "the first one" or the old index.
|
|
97
|
+
* 5. A unique component whose label changed is "drifted": still shown, marked uncertain.
|
|
98
|
+
* 6. A match that exists but is inside a closed dialog is "hidden", not deleted.
|
|
99
|
+
* 7. No match is "unavailable". The thread stays in the inbox.
|
|
100
|
+
*
|
|
101
|
+
* `candidates` may be a targeted subset of the page, as long as it holds every element with one of the anchor's
|
|
102
|
+
* explicit ids and every element owned by a component of the anchor's name. The drift check looks at everything
|
|
103
|
+
* inside the component's root, so such callers pass `inside` to supply those candidates on demand.
|
|
104
|
+
*/
|
|
105
|
+
export function resolveAnchor(
|
|
106
|
+
anchor: Anchor,
|
|
107
|
+
candidates: Candidate[],
|
|
108
|
+
inside: (root: Candidate) => Candidate[] = () => candidates,
|
|
109
|
+
): Resolution {
|
|
110
|
+
const pool = candidates.filter((candidate) => candidate.route === anchor.route);
|
|
111
|
+
if (anchor.explicit.length > 0) {
|
|
112
|
+
const hits = dropAncestors(pool.filter((candidate) => sameExplicit(anchor, candidate)));
|
|
113
|
+
const hit = single(hits);
|
|
114
|
+
if (hit) return finish("pinned", hit, 100, "explicit id");
|
|
115
|
+
if (hits.length > 1) {
|
|
116
|
+
return {
|
|
117
|
+
status: "ambiguous",
|
|
118
|
+
key: null,
|
|
119
|
+
score: 40,
|
|
120
|
+
reason: "explicit id matches more than one element",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const componentScoped = pool.filter(
|
|
126
|
+
(candidate) => sameComponent(anchor, candidate, true) && sameRole(anchor, candidate),
|
|
127
|
+
);
|
|
128
|
+
const labeled = dropAncestors(
|
|
129
|
+
componentScoped.filter((candidate) => sameLabel(anchor, candidate)),
|
|
130
|
+
);
|
|
131
|
+
const label = single(labeled);
|
|
132
|
+
if (label) return finish("pinned", label, 100, "component file, role, and label");
|
|
133
|
+
if (labeled.length > 1) {
|
|
134
|
+
return {
|
|
135
|
+
status: "ambiguous",
|
|
136
|
+
key: null,
|
|
137
|
+
score: 50,
|
|
138
|
+
reason: "several elements in this component share the same label",
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (anchor.component?.file && anchor.component.name) {
|
|
143
|
+
const roots = pool.filter(
|
|
144
|
+
(candidate) => candidate.componentRoot && sameComponent(anchor, candidate, true),
|
|
145
|
+
);
|
|
146
|
+
const root = single(roots);
|
|
147
|
+
if (root) {
|
|
148
|
+
const within = dropAncestors(
|
|
149
|
+
inside(root).filter(
|
|
150
|
+
(candidate) =>
|
|
151
|
+
candidate.route === anchor.route &&
|
|
152
|
+
(candidate.path === root.path || isAncestorPath(root.path, candidate.path)) &&
|
|
153
|
+
sameRole(anchor, candidate),
|
|
154
|
+
),
|
|
155
|
+
);
|
|
156
|
+
const moved = single(within);
|
|
157
|
+
if (moved) return finish("drifted", moved, 70, "same component and role, label changed");
|
|
158
|
+
const where = anchor.overlay
|
|
159
|
+
? ` Last seen in ${anchor.overlay.role} “${anchor.overlay.name}”.`
|
|
160
|
+
: "";
|
|
161
|
+
return unavailable(`label no longer matches inside ${anchor.component.name}.${where}`.trim());
|
|
162
|
+
}
|
|
163
|
+
if (roots.length === 0) {
|
|
164
|
+
const where = anchor.overlay
|
|
165
|
+
? ` Last seen in ${anchor.overlay.role} “${anchor.overlay.name}”.`
|
|
166
|
+
: "";
|
|
167
|
+
return unavailable(
|
|
168
|
+
`component ${anchor.component.name} is not on ${anchor.route}.${where}`.trim(),
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
status: "ambiguous",
|
|
173
|
+
key: null,
|
|
174
|
+
score: 45,
|
|
175
|
+
reason: "component is on the page more than once and the label no longer matches",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Dev fallback when the compiler didn't stamp a file: component name + label.
|
|
180
|
+
if (anchor.component?.name) {
|
|
181
|
+
const named = dropAncestors(
|
|
182
|
+
pool.filter(
|
|
183
|
+
(candidate) =>
|
|
184
|
+
sameComponent(anchor, candidate, false) &&
|
|
185
|
+
sameRole(anchor, candidate) &&
|
|
186
|
+
sameLabel(anchor, candidate),
|
|
187
|
+
),
|
|
188
|
+
);
|
|
189
|
+
const name = single(named);
|
|
190
|
+
if (name) return finish("pinned", name, 80, "component name and label");
|
|
191
|
+
if (named.length > 1) {
|
|
192
|
+
return {
|
|
193
|
+
status: "ambiguous",
|
|
194
|
+
key: null,
|
|
195
|
+
score: 40,
|
|
196
|
+
reason: "component name and label match more than one element",
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const where = anchor.overlay
|
|
202
|
+
? ` It was inside ${anchor.overlay.role} “${anchor.overlay.name}”.`
|
|
203
|
+
: "";
|
|
204
|
+
return unavailable(`no element on ${anchor.route} matches this comment.${where}`.trim());
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function describeAnchor(anchor: Anchor) {
|
|
208
|
+
const component = anchor.component
|
|
209
|
+
? `${anchor.component.name}${anchor.component.file ? ` · ${anchor.component.file}` : ""}`
|
|
210
|
+
: anchor.tag;
|
|
211
|
+
const label = anchor.accName || anchor.text;
|
|
212
|
+
return label ? `${component} — ${label}` : component;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function validateAnchor(input: unknown): Anchor | null {
|
|
216
|
+
if (!input || typeof input !== "object") return null;
|
|
217
|
+
const value = input as Partial<Anchor>;
|
|
218
|
+
if (value.v !== 1) return null;
|
|
219
|
+
if (typeof value.route !== "string" || !value.route.startsWith("/") || value.route.length > 500)
|
|
220
|
+
return null;
|
|
221
|
+
if (typeof value.tag !== "string" || value.tag.length > 40) return null;
|
|
222
|
+
const explicit = Array.isArray(value.explicit) ? value.explicit : [];
|
|
223
|
+
if (explicit.length > 8) return null;
|
|
224
|
+
if (
|
|
225
|
+
explicit.some(
|
|
226
|
+
(item) =>
|
|
227
|
+
!item ||
|
|
228
|
+
typeof item !== "object" ||
|
|
229
|
+
typeof item.attr !== "string" ||
|
|
230
|
+
typeof item.value !== "string" ||
|
|
231
|
+
item.attr.length > 40 ||
|
|
232
|
+
item.value.length > 200,
|
|
233
|
+
)
|
|
234
|
+
) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
const component = value.component;
|
|
238
|
+
if (
|
|
239
|
+
component !== null &&
|
|
240
|
+
component !== undefined &&
|
|
241
|
+
(typeof component !== "object" ||
|
|
242
|
+
typeof component.name !== "string" ||
|
|
243
|
+
(component.file !== null &&
|
|
244
|
+
component.file !== undefined &&
|
|
245
|
+
typeof component.file !== "string"))
|
|
246
|
+
) {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
const clip = (text: unknown, max: number) =>
|
|
250
|
+
typeof text === "string" ? text.slice(0, max) : null;
|
|
251
|
+
const sketch = value.sketch === undefined ? null : validateSketch(value.sketch);
|
|
252
|
+
if (value.sketch !== undefined && !sketch) return null;
|
|
253
|
+
return {
|
|
254
|
+
v: 1,
|
|
255
|
+
route: value.route,
|
|
256
|
+
overlay:
|
|
257
|
+
value.overlay &&
|
|
258
|
+
typeof value.overlay === "object" &&
|
|
259
|
+
typeof value.overlay.role === "string" &&
|
|
260
|
+
typeof value.overlay.name === "string"
|
|
261
|
+
? { role: value.overlay.role.slice(0, 40), name: value.overlay.name.slice(0, 120) }
|
|
262
|
+
: null,
|
|
263
|
+
explicit: explicit.map((item) => ({ attr: item.attr, value: item.value })),
|
|
264
|
+
component: component
|
|
265
|
+
? {
|
|
266
|
+
name: component.name.slice(0, 120),
|
|
267
|
+
file: component.file ? component.file.slice(0, 300) : null,
|
|
268
|
+
}
|
|
269
|
+
: null,
|
|
270
|
+
stack: Array.isArray(value.stack)
|
|
271
|
+
? value.stack.filter((item): item is string => typeof item === "string").slice(0, 16)
|
|
272
|
+
: [],
|
|
273
|
+
role: clip(value.role, 40),
|
|
274
|
+
accName: clip(value.accName, 160),
|
|
275
|
+
text: clip(value.text, 160),
|
|
276
|
+
tag: value.tag,
|
|
277
|
+
intra: clip(value.intra, 300),
|
|
278
|
+
...(sketch ? { sketch } : {}),
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Most strokes, and numbers per stroke, a drawing keeps. */
|
|
283
|
+
const SKETCH_PATHS = 8;
|
|
284
|
+
const SKETCH_NUMBERS = 600;
|
|
285
|
+
/** Coordinates and sizes outside this many pixels either way are not a drawing on a page. */
|
|
286
|
+
const SKETCH_RANGE = 20_000;
|
|
287
|
+
|
|
288
|
+
export function validateSketch(input: unknown): Sketch | null {
|
|
289
|
+
if (!input || typeof input !== "object") return null;
|
|
290
|
+
const value = input as Partial<Sketch>;
|
|
291
|
+
const size = (n: unknown) =>
|
|
292
|
+
typeof n === "number" && Number.isFinite(n) && n >= 0 && n <= SKETCH_RANGE;
|
|
293
|
+
if (!size(value.w) || !size(value.h)) return null;
|
|
294
|
+
if (!Array.isArray(value.paths) || !value.paths.length || value.paths.length > SKETCH_PATHS)
|
|
295
|
+
return null;
|
|
296
|
+
const paths: number[][] = [];
|
|
297
|
+
for (const path of value.paths) {
|
|
298
|
+
if (!Array.isArray(path) || path.length < 4 || path.length > SKETCH_NUMBERS || path.length % 2)
|
|
299
|
+
return null;
|
|
300
|
+
if (
|
|
301
|
+
path.some((n) => typeof n !== "number" || !Number.isFinite(n) || Math.abs(n) > SKETCH_RANGE)
|
|
302
|
+
)
|
|
303
|
+
return null;
|
|
304
|
+
paths.push(path.map((n: number) => Math.round(n)));
|
|
305
|
+
}
|
|
306
|
+
return { w: Math.round(value.w as number), h: Math.round(value.h as number), paths };
|
|
307
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** Stable identity for a commented element. Pixel and DOM-index position are not part of the match. */
|
|
2
|
+
export type Anchor = {
|
|
3
|
+
v: 1;
|
|
4
|
+
/** Pathname only, no query or hash. */
|
|
5
|
+
route: string;
|
|
6
|
+
/**
|
|
7
|
+
* Dialog or sheet the element lived in when the comment was written.
|
|
8
|
+
* Used to explain a hidden thread. Not required to match after the element moves out of the overlay.
|
|
9
|
+
*/
|
|
10
|
+
overlay: { role: string; name: string } | null;
|
|
11
|
+
/** Opt-in stable ids: data-cs-anchor, data-testid, or a human id. Highest priority. */
|
|
12
|
+
explicit: Array<{ attr: string; value: string }>;
|
|
13
|
+
/** React component that owns the element. File path is relative and has no line number. */
|
|
14
|
+
component: { name: string; file: string | null } | null;
|
|
15
|
+
/** Component names from the tree, outer to inner. Diagnostic. */
|
|
16
|
+
stack: string[];
|
|
17
|
+
role: string | null;
|
|
18
|
+
accName: string | null;
|
|
19
|
+
text: string | null;
|
|
20
|
+
tag: string;
|
|
21
|
+
/** Sibling path inside the component. Stored for agents, never used to choose a match. */
|
|
22
|
+
intra: string | null;
|
|
23
|
+
/** A freehand drawing the comment was left with. Never used to choose a match. */
|
|
24
|
+
sketch?: Sketch;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Strokes drawn on the page, in CSS pixels from the top-left corner of the anchored element.
|
|
29
|
+
* Each path is a flat list of x, y pairs. `w` and `h` are the element's size when it was drawn.
|
|
30
|
+
*/
|
|
31
|
+
export type Sketch = { w: number; h: number; paths: number[][] };
|
|
32
|
+
|
|
33
|
+
export type Candidate = {
|
|
34
|
+
key: string;
|
|
35
|
+
route: string;
|
|
36
|
+
visible: boolean;
|
|
37
|
+
/** Index path from the document body. Used only to tell ancestors from descendants. */
|
|
38
|
+
path: string;
|
|
39
|
+
componentRoot: boolean;
|
|
40
|
+
overlay: { role: string; name: string } | null;
|
|
41
|
+
explicit: Array<{ attr: string; value: string }>;
|
|
42
|
+
component: { name: string; file: string | null } | null;
|
|
43
|
+
stack: string[];
|
|
44
|
+
role: string | null;
|
|
45
|
+
accName: string | null;
|
|
46
|
+
text: string | null;
|
|
47
|
+
tag: string;
|
|
48
|
+
intra: string | null;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type ResolutionStatus = "pinned" | "drifted" | "hidden" | "ambiguous" | "unavailable";
|
|
52
|
+
|
|
53
|
+
export type Resolution = {
|
|
54
|
+
status: ResolutionStatus;
|
|
55
|
+
key: string | null;
|
|
56
|
+
score: number;
|
|
57
|
+
reason: string;
|
|
58
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The debug context file a comment carries (`context-<stamp>.json`), for an inbox, a destination and the
|
|
3
|
+
* service: read it back safely, redact it again, and turn it into the brief a coding agent reads. DOM-free, so
|
|
4
|
+
* a Worker can import it. Source only, like `./replay`: not built into `dist`.
|
|
5
|
+
*/
|
|
6
|
+
export {
|
|
7
|
+
DEBUG_CONTEXT_MAX_BYTES,
|
|
8
|
+
DEBUG_CONTEXT_PREFIX,
|
|
9
|
+
DEBUG_SIGNALS,
|
|
10
|
+
DEFAULT_SIGNALS,
|
|
11
|
+
OPT_IN_SIGNALS,
|
|
12
|
+
debugContextBrief,
|
|
13
|
+
debugContextBytes,
|
|
14
|
+
debugContextKey,
|
|
15
|
+
debugContextName,
|
|
16
|
+
fitDebugContext,
|
|
17
|
+
isDebugContextName,
|
|
18
|
+
liveDebugOptions,
|
|
19
|
+
newReportId,
|
|
20
|
+
resolveDebugContext,
|
|
21
|
+
sanitizeDebugContext,
|
|
22
|
+
type BriefMeta,
|
|
23
|
+
type DebugAudience,
|
|
24
|
+
type DebugContextConfig,
|
|
25
|
+
type DebugContextFile,
|
|
26
|
+
type DebugContextOptions,
|
|
27
|
+
type DebugError,
|
|
28
|
+
type DebugRequest,
|
|
29
|
+
type DebugSignal,
|
|
30
|
+
type DebugStep,
|
|
31
|
+
type HostBuild,
|
|
32
|
+
type HostContext,
|
|
33
|
+
type QuerySummary,
|
|
34
|
+
type RealtimeState,
|
|
35
|
+
type ResolvedDebugContext,
|
|
36
|
+
} from "./runtime/debug-context-model.js";
|
|
37
|
+
export {
|
|
38
|
+
ID_HEADERS,
|
|
39
|
+
idHeaderNames,
|
|
40
|
+
newTally,
|
|
41
|
+
scrubId,
|
|
42
|
+
scrubText,
|
|
43
|
+
scrubUrl,
|
|
44
|
+
scrubValue,
|
|
45
|
+
type JsonBag,
|
|
46
|
+
type RedactionTally,
|
|
47
|
+
} from "./runtime/debug-context-redact.js";
|