@abbox/inspector 0.0.0-stage → 0.2.1

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 (72) hide show
  1. package/README.md +51 -2
  2. package/dist/browser.d.ts +9 -0
  3. package/dist/browser.d.ts.map +1 -0
  4. package/dist/browser.js +8 -0
  5. package/dist/host/client-bridge.d.ts +13 -0
  6. package/dist/host/client-bridge.d.ts.map +1 -0
  7. package/dist/host/client-bridge.js +75 -0
  8. package/dist/host/host-bridge.d.ts +34 -0
  9. package/dist/host/host-bridge.d.ts.map +1 -0
  10. package/dist/host/host-bridge.js +59 -0
  11. package/dist/host/origin-aliases.d.ts +3 -0
  12. package/dist/host/origin-aliases.d.ts.map +1 -0
  13. package/dist/host/origin-aliases.js +13 -0
  14. package/dist/host/validate.d.ts +8 -0
  15. package/dist/host/validate.d.ts.map +1 -0
  16. package/dist/host/validate.js +13 -0
  17. package/dist/index.d.ts +11 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +10 -0
  20. package/dist/instrument/babel-plugin.d.ts +8 -0
  21. package/dist/instrument/babel-plugin.d.ts.map +1 -0
  22. package/dist/instrument/babel-plugin.js +26 -0
  23. package/dist/instrument/constants.d.ts +10 -0
  24. package/dist/instrument/constants.d.ts.map +1 -0
  25. package/dist/instrument/constants.js +17 -0
  26. package/dist/ir-match.d.ts +34 -0
  27. package/dist/ir-match.d.ts.map +1 -0
  28. package/dist/ir-match.js +129 -0
  29. package/dist/next/babel-config.cjs +39 -0
  30. package/dist/next/index.d.ts +3 -0
  31. package/dist/next/index.d.ts.map +1 -0
  32. package/dist/next/index.js +2 -0
  33. package/dist/next/preview-env.d.ts +8 -0
  34. package/dist/next/preview-env.d.ts.map +1 -0
  35. package/dist/next/preview-env.js +22 -0
  36. package/dist/next/webpack-instrumentation.cjs +82 -0
  37. package/dist/next/with-abbox-inspector.d.ts +26 -0
  38. package/dist/next/with-abbox-inspector.d.ts.map +1 -0
  39. package/dist/next/with-abbox-inspector.js +80 -0
  40. package/dist/protocol.d.ts +92 -0
  41. package/dist/protocol.d.ts.map +1 -0
  42. package/dist/protocol.js +12 -0
  43. package/dist/react/AbboxInspectorBoot.d.ts +3 -0
  44. package/dist/react/AbboxInspectorBoot.d.ts.map +1 -0
  45. package/dist/react/AbboxInspectorBoot.js +10 -0
  46. package/dist/runtime/bootstrap.d.ts +10 -0
  47. package/dist/runtime/bootstrap.d.ts.map +1 -0
  48. package/dist/runtime/bootstrap.js +49 -0
  49. package/dist/runtime/element-ref.d.ts +4 -0
  50. package/dist/runtime/element-ref.d.ts.map +1 -0
  51. package/dist/runtime/element-ref.js +14 -0
  52. package/dist/runtime/inspector.d.ts +18 -0
  53. package/dist/runtime/inspector.d.ts.map +1 -0
  54. package/dist/runtime/inspector.js +137 -0
  55. package/dist/runtime/metadata.d.ts +6 -0
  56. package/dist/runtime/metadata.d.ts.map +1 -0
  57. package/dist/runtime/metadata.js +81 -0
  58. package/dist/runtime/overlay.d.ts +12 -0
  59. package/dist/runtime/overlay.d.ts.map +1 -0
  60. package/dist/runtime/overlay.js +104 -0
  61. package/dist/vite-plugin.d.ts +8 -0
  62. package/dist/vite-plugin.d.ts.map +1 -0
  63. package/dist/vite-plugin.js +29 -0
  64. package/docs/TRUST.md +18 -0
  65. package/docs/architecture.md +21 -0
  66. package/docs/implementation-report.md +23 -0
  67. package/docs/integration.md +21 -0
  68. package/docs/known-limitations.md +8 -0
  69. package/docs/nextjs-integration.md +35 -0
  70. package/package.json +90 -4
  71. package/src/next/babel-config.cjs +39 -0
  72. package/src/next/webpack-instrumentation.cjs +82 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * When true, Inspector instrumentation, CSP frame-ancestors, and client bootstrap are allowed.
3
+ * Production Vercel deploys (VERCEL_ENV=production) stay off unless ABBBOX_INSPECTOR=1 is set explicitly.
4
+ */
5
+ export declare function isAbboxInspectorBuildEnabled(): boolean;
6
+ /** Client-side: Inspector may connect to the Viewer iframe host. */
7
+ export declare function isAbboxInspectorRuntimeEnabled(): boolean;
8
+ //# sourceMappingURL=preview-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-env.d.ts","sourceRoot":"","sources":["../../src/next/preview-env.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,OAAO,CAItD;AAED,oEAAoE;AACpE,wBAAgB,8BAA8B,IAAI,OAAO,CAOxD"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * When true, Inspector instrumentation, CSP frame-ancestors, and client bootstrap are allowed.
3
+ * Production Vercel deploys (VERCEL_ENV=production) stay off unless ABBBOX_INSPECTOR=1 is set explicitly.
4
+ */
5
+ export function isAbboxInspectorBuildEnabled() {
6
+ if (process.env.ABBBOX_INSPECTOR === "1")
7
+ return true;
8
+ if (process.env.VERCEL_ENV === "preview")
9
+ return true;
10
+ return false;
11
+ }
12
+ /** Client-side: Inspector may connect to the Viewer iframe host. */
13
+ export function isAbboxInspectorRuntimeEnabled() {
14
+ if (process.env.NEXT_PUBLIC_ABBOX_INSPECTOR === "1")
15
+ return true;
16
+ if (process.env.VERCEL_ENV === "preview")
17
+ return true;
18
+ if (typeof process.env.NEXT_PUBLIC_VERCEL_ENV === "string") {
19
+ return process.env.NEXT_PUBLIC_VERCEL_ENV === "preview";
20
+ }
21
+ return false;
22
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Apply Abbox JSX instrumentation via webpack + babel-loader on selected app sources.
3
+ * Does not use babel.config.js, so Next.js keeps SWC (and next/font) for excluded files
4
+ * such as app/layout.tsx.
5
+ */
6
+ const fs = require("node:fs");
7
+ const path = require("node:path");
8
+ const { createRequire } = require("node:module");
9
+
10
+ const LAYOUT_BASENAMES = new Set(["layout.tsx", "layout.jsx"]);
11
+
12
+ function resolveInspectorBabelPlugin(rootDir) {
13
+ const req = createRequire(path.join(rootDir, "package.json"));
14
+ try {
15
+ return req.resolve("@abbox/inspector/babel");
16
+ } catch {
17
+ return req.resolve("abbox-inspector/babel");
18
+ }
19
+ }
20
+
21
+ function resolveBabelLoader(rootDir) {
22
+ const req = createRequire(path.join(rootDir, "package.json"));
23
+ return req.resolve("babel-loader");
24
+ }
25
+
26
+ function resolveNextBabelPreset(rootDir) {
27
+ const req = createRequire(path.join(rootDir, "package.json"));
28
+ return req.resolve("next/babel");
29
+ }
30
+
31
+ function collectIncludeDirs(rootDir) {
32
+ return ["app", "components", "src", "pages"]
33
+ .map((segment) => path.join(rootDir, segment))
34
+ .filter((dir) => fs.existsSync(dir));
35
+ }
36
+
37
+ function isRootLayoutResource(resourcePath) {
38
+ const base = path.basename(resourcePath);
39
+ if (!LAYOUT_BASENAMES.has(base)) return false;
40
+ const parent = path.basename(path.dirname(resourcePath));
41
+ return parent === "app" || parent === "src/app";
42
+ }
43
+
44
+ /**
45
+ * @param {import('webpack').Configuration} webpackConfig
46
+ * @param {{ dir: string }} options
47
+ */
48
+ function applyAbboxWebpackInstrumentation(webpackConfig, options) {
49
+ const rootDir = options.dir ?? process.cwd();
50
+ const pluginPath = resolveInspectorBabelPlugin(rootDir);
51
+ const babelLoader = resolveBabelLoader(rootDir);
52
+ const nextBabel = resolveNextBabelPreset(rootDir);
53
+ const includeDirs = collectIncludeDirs(rootDir);
54
+
55
+ if (includeDirs.length === 0) {
56
+ return webpackConfig;
57
+ }
58
+
59
+ const rule = {
60
+ test: /\.(tsx|jsx)$/,
61
+ include: (resourcePath) =>
62
+ includeDirs.some((dir) => resourcePath.startsWith(dir)) &&
63
+ !isRootLayoutResource(resourcePath),
64
+ exclude: [/node_modules/],
65
+ use: {
66
+ loader: babelLoader,
67
+ options: {
68
+ babelrc: false,
69
+ configFile: false,
70
+ presets: [nextBabel],
71
+ plugins: [[pluginPath, { rootDir }]],
72
+ },
73
+ },
74
+ };
75
+
76
+ webpackConfig.module = webpackConfig.module ?? { rules: [] };
77
+ webpackConfig.module.rules = webpackConfig.module.rules ?? [];
78
+ webpackConfig.module.rules.unshift(rule);
79
+ return webpackConfig;
80
+ }
81
+
82
+ module.exports = { applyAbboxWebpackInstrumentation, isRootLayoutResource };
@@ -0,0 +1,26 @@
1
+ export type NextConfigLike = {
2
+ env?: Record<string, string | undefined>;
3
+ headers?: unknown;
4
+ webpack?: (config: Record<string, unknown>, options: Record<string, unknown>) => Record<string, unknown> | void;
5
+ transpilePackages?: unknown;
6
+ [key: string]: unknown;
7
+ };
8
+ export type WithAbboxInspectorOptions = {
9
+ /** When false, returns config unchanged (default: ABBBOX_INSPECTOR=1). */
10
+ enabled?: boolean;
11
+ /**
12
+ * Origins allowed to embed this app in an iframe (Abbox Viewer).
13
+ * Defaults to common local Viewer ports.
14
+ */
15
+ frameAncestors?: string[];
16
+ };
17
+ export declare const DEFAULT_VIEWER_FRAME_ANCESTORS: string[];
18
+ /**
19
+ * Merge Next.js config for local Abbox Live Preview: iframe embedding headers
20
+ * and public env flag for client bootstrap.
21
+ *
22
+ * JSX instrumentation uses a webpack babel-loader rule (no root babel.config.js),
23
+ * so `next/font` in app/layout.tsx can keep using SWC.
24
+ */
25
+ export declare function withAbboxInspector<T>(config: T, options?: WithAbboxInspectorOptions): T;
26
+ //# sourceMappingURL=with-abbox-inspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-abbox-inspector.d.ts","sourceRoot":"","sources":["../../src/next/with-abbox-inspector.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,CACR,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAIF,eAAO,MAAM,8BAA8B,UAM1C,CAAC;AAOF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,MAAM,EAAE,CAAC,EACT,OAAO,GAAE,yBAA8B,GACtC,CAAC,CA0EH"}
@@ -0,0 +1,80 @@
1
+ import { createRequire } from "node:module";
2
+ import { fileURLToPath } from "node:url";
3
+ const require = createRequire(fileURLToPath(import.meta.url));
4
+ import { isAbboxInspectorBuildEnabled } from "./preview-env.js";
5
+ export const DEFAULT_VIEWER_FRAME_ANCESTORS = [
6
+ "https://viewer.abbox.com",
7
+ "http://localhost:5173",
8
+ "http://127.0.0.1:5173",
9
+ "http://localhost:4173",
10
+ "http://127.0.0.1:4173",
11
+ ];
12
+ function inspectorEnabled(explicit) {
13
+ if (explicit !== undefined)
14
+ return explicit;
15
+ return isAbboxInspectorBuildEnabled();
16
+ }
17
+ /**
18
+ * Merge Next.js config for local Abbox Live Preview: iframe embedding headers
19
+ * and public env flag for client bootstrap.
20
+ *
21
+ * JSX instrumentation uses a webpack babel-loader rule (no root babel.config.js),
22
+ * so `next/font` in app/layout.tsx can keep using SWC.
23
+ */
24
+ export function withAbboxInspector(config, options = {}) {
25
+ if (!inspectorEnabled(options.enabled)) {
26
+ return config;
27
+ }
28
+ const base = config;
29
+ const { applyAbboxWebpackInstrumentation } = require("./webpack-instrumentation.cjs");
30
+ const ancestors = options.frameAncestors ?? DEFAULT_VIEWER_FRAME_ANCESTORS;
31
+ const csp = `frame-ancestors 'self' ${ancestors.join(" ")}`;
32
+ const headers = async () => {
33
+ const existing = typeof base.headers === "function"
34
+ ? await base.headers()
35
+ : (base.headers ?? []);
36
+ return [
37
+ ...existing,
38
+ {
39
+ source: "/:path*",
40
+ headers: [
41
+ {
42
+ key: "Content-Security-Policy",
43
+ value: csp,
44
+ },
45
+ ],
46
+ },
47
+ ];
48
+ };
49
+ const transpile = new Set([
50
+ ...(Array.isArray(base.transpilePackages)
51
+ ? base.transpilePackages
52
+ : []),
53
+ "@abbox/inspector",
54
+ "abbox-inspector",
55
+ ]);
56
+ const userWebpack = base.webpack;
57
+ const webpack = (webpackConfig, webpackOptions) => {
58
+ let cfg = webpackConfig;
59
+ if (typeof userWebpack === "function") {
60
+ const nextCfg = userWebpack(cfg, webpackOptions);
61
+ if (nextCfg)
62
+ cfg = nextCfg;
63
+ }
64
+ const dir = typeof webpackOptions.dir === "string"
65
+ ? webpackOptions.dir
66
+ : process.cwd();
67
+ return applyAbboxWebpackInstrumentation(cfg, { dir });
68
+ };
69
+ return {
70
+ ...base,
71
+ transpilePackages: [...transpile],
72
+ env: {
73
+ ...(base.env ?? {}),
74
+ NEXT_PUBLIC_ABBOX_INSPECTOR: "1",
75
+ NEXT_PUBLIC_VERCEL_ENV: process.env.VERCEL_ENV ?? "",
76
+ },
77
+ headers,
78
+ webpack,
79
+ };
80
+ }
@@ -0,0 +1,92 @@
1
+ /** Wire protocol between Abbox Viewer (host) and instrumented preview (client). */
2
+ export declare const PROTOCOL_VERSION: 1;
3
+ export declare const ABBBOX_INSPECTOR_CHANNEL: "abbox-inspector";
4
+ export type InspectorMessageBase = {
5
+ channel: typeof ABBBOX_INSPECTOR_CHANNEL;
6
+ version: typeof PROTOCOL_VERSION;
7
+ sessionId: string;
8
+ };
9
+ export type InspectorReadyMessage = InspectorMessageBase & {
10
+ type: "INSPECTOR_READY";
11
+ previewUrl: string;
12
+ };
13
+ export type InspectorEnableMessage = InspectorMessageBase & {
14
+ type: "INSPECTOR_ENABLE";
15
+ };
16
+ export type InspectorDisableMessage = InspectorMessageBase & {
17
+ type: "INSPECTOR_DISABLE";
18
+ };
19
+ /** Host asks the preview client to resend INSPECTOR_READY (e.g. after iframe load). */
20
+ export type InspectorHostHelloMessage = InspectorMessageBase & {
21
+ type: "INSPECTOR_HOST_HELLO";
22
+ };
23
+ export type ElementRect = {
24
+ x: number;
25
+ y: number;
26
+ width: number;
27
+ height: number;
28
+ };
29
+ export type SourceLocation = {
30
+ /** Project-relative path (no absolute filesystem paths). */
31
+ file: string;
32
+ line: number;
33
+ column: number;
34
+ /** Raw attribute value when present on DOM. */
35
+ raw?: string;
36
+ };
37
+ export type SourceMappingStatus = "resolved" | "unresolved" | "partial";
38
+ export type ElementMetadata = {
39
+ tagName: string;
40
+ id: string | null;
41
+ classList: string[];
42
+ role: string | null;
43
+ ariaLabel: string | null;
44
+ textPreview: string | null;
45
+ attributes: Record<string, string>;
46
+ };
47
+ export type RuntimeElementRef = {
48
+ /** Stable within a preview session for a given DOM node identity. */
49
+ id: string;
50
+ sourceKey: string | null;
51
+ };
52
+ export type ElementHoverPayload = {
53
+ elementRef: RuntimeElementRef;
54
+ rect: ElementRect;
55
+ metadata: ElementMetadata;
56
+ source: SourceLocation | null;
57
+ mappingStatus: SourceMappingStatus;
58
+ };
59
+ export type ElementSelectedPayload = ElementHoverPayload;
60
+ export type ElementHoveredMessage = InspectorMessageBase & {
61
+ type: "ELEMENT_HOVERED";
62
+ payload: ElementHoverPayload;
63
+ };
64
+ export type ElementSelectedMessage = InspectorMessageBase & {
65
+ type: "ELEMENT_SELECTED";
66
+ payload: ElementSelectedPayload;
67
+ };
68
+ export type ElementClearedMessage = InspectorMessageBase & {
69
+ type: "ELEMENT_CLEARED";
70
+ };
71
+ export type InspectorErrorMessage = InspectorMessageBase & {
72
+ type: "INSPECTOR_ERROR";
73
+ code: string;
74
+ message: string;
75
+ };
76
+ export type ChangeMarkerKind = "mock" | "pr-preview";
77
+ /** Optional overlay markers supplied by the host (diff engine lives outside inspector). */
78
+ export type ChangeMarker = {
79
+ elementRefId: string;
80
+ label: string;
81
+ kind: ChangeMarkerKind;
82
+ pulse?: boolean;
83
+ };
84
+ export type ApplyChangeMarkersMessage = InspectorMessageBase & {
85
+ type: "APPLY_CHANGE_MARKERS";
86
+ markers: ChangeMarker[];
87
+ };
88
+ export type HostToClientMessage = InspectorEnableMessage | InspectorDisableMessage | InspectorHostHelloMessage | ApplyChangeMarkersMessage;
89
+ export type ClientToHostMessage = InspectorReadyMessage | ElementHoveredMessage | ElementSelectedMessage | ElementClearedMessage | InspectorErrorMessage;
90
+ export type InspectorMessage = HostToClientMessage | ClientToHostMessage;
91
+ export declare function isInspectorMessage(value: unknown): value is InspectorMessage;
92
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /** Wire protocol between Abbox Viewer (host) and instrumented preview (client). */
2
+ export const PROTOCOL_VERSION = 1;
3
+ export const ABBBOX_INSPECTOR_CHANNEL = "abbox-inspector";
4
+ export function isInspectorMessage(value) {
5
+ if (!value || typeof value !== "object")
6
+ return false;
7
+ const o = value;
8
+ return (o.channel === ABBBOX_INSPECTOR_CHANNEL &&
9
+ o.version === PROTOCOL_VERSION &&
10
+ typeof o.sessionId === "string" &&
11
+ typeof o.type === "string");
12
+ }
@@ -0,0 +1,3 @@
1
+ /** Mount once in the root layout so the Viewer iframe can connect via `abboxSession`. */
2
+ export declare function AbboxInspectorBoot(): null;
3
+ //# sourceMappingURL=AbboxInspectorBoot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbboxInspectorBoot.d.ts","sourceRoot":"","sources":["../../src/react/AbboxInspectorBoot.tsx"],"names":[],"mappings":"AAKA,yFAAyF;AACzF,wBAAgB,kBAAkB,SAKjC"}
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { useEffect } from "react";
3
+ import { bootstrapInspectorPreview } from "../runtime/bootstrap.js";
4
+ /** Mount once in the root layout so the Viewer iframe can connect via `abboxSession`. */
5
+ export function AbboxInspectorBoot() {
6
+ useEffect(() => {
7
+ bootstrapInspectorPreview();
8
+ }, []);
9
+ return null;
10
+ }
@@ -0,0 +1,10 @@
1
+ export type BootstrapOptions = {
2
+ sessionId?: string;
3
+ allowedHostOrigins?: string[];
4
+ };
5
+ /**
6
+ * Entry for instrumented preview apps. Activates when `abboxSession` is in the
7
+ * URL on authorized preview deployments (Vercel preview or local `dev:abbox`).
8
+ */
9
+ export declare function bootstrapInspectorPreview(options?: BootstrapOptions): void;
10
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/runtime/bootstrap.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gBAAqB,GAC7B,IAAI,CAyBN"}
@@ -0,0 +1,49 @@
1
+ import { withLocalhostAliases } from "../host/origin-aliases.js";
2
+ import { createInspectorClient } from "../host/client-bridge.js";
3
+ import { isAbboxInspectorRuntimeEnabled } from "../next/preview-env.js";
4
+ /** Viewer origins used when `abboxHostOrigin` is missing from the iframe URL. */
5
+ const DEFAULT_VIEWER_ORIGINS = [
6
+ "https://viewer.abbox.com",
7
+ "http://localhost:5173",
8
+ "http://127.0.0.1:5173",
9
+ "http://localhost:4173",
10
+ "http://127.0.0.1:4173",
11
+ ];
12
+ function viewerOriginsFromReferrer() {
13
+ if (typeof document === "undefined" || !document.referrer)
14
+ return [];
15
+ try {
16
+ return [new URL(document.referrer).origin];
17
+ }
18
+ catch {
19
+ return [];
20
+ }
21
+ }
22
+ /**
23
+ * Entry for instrumented preview apps. Activates when `abboxSession` is in the
24
+ * URL on authorized preview deployments (Vercel preview or local `dev:abbox`).
25
+ */
26
+ export function bootstrapInspectorPreview(options = {}) {
27
+ if (typeof window === "undefined")
28
+ return;
29
+ if (!isAbboxInspectorRuntimeEnabled())
30
+ return;
31
+ const params = new URLSearchParams(window.location.search);
32
+ const sessionFromUrl = params.get("abboxSession");
33
+ if (!sessionFromUrl)
34
+ return;
35
+ const sessionId = options.sessionId ?? sessionFromUrl ?? "local-dev";
36
+ const hostParam = params.get("abboxHostOrigin");
37
+ const allowedHostOrigins = withLocalhostAliases(options.allowedHostOrigins ??
38
+ (hostParam
39
+ ? [hostParam]
40
+ : [
41
+ ...viewerOriginsFromReferrer(),
42
+ ...DEFAULT_VIEWER_ORIGINS,
43
+ ]));
44
+ const client = createInspectorClient({
45
+ sessionId,
46
+ allowedHostOrigins,
47
+ });
48
+ client.start();
49
+ }
@@ -0,0 +1,4 @@
1
+ import type { RuntimeElementRef } from "../protocol.js";
2
+ export declare function runtimeElementRefFor(el: Element, sourceKey: string | null): RuntimeElementRef;
3
+ export declare function resetElementRefCounter(): void;
4
+ //# sourceMappingURL=element-ref.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"element-ref.d.ts","sourceRoot":"","sources":["../../src/runtime/element-ref.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAKxD,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,OAAO,EACX,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,iBAAiB,CAQnB;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
@@ -0,0 +1,14 @@
1
+ let counter = 0;
2
+ const weakToId = new WeakMap();
3
+ export function runtimeElementRefFor(el, sourceKey) {
4
+ let id = weakToId.get(el);
5
+ if (!id) {
6
+ counter += 1;
7
+ id = `el_${counter}`;
8
+ weakToId.set(el, id);
9
+ }
10
+ return { id, sourceKey };
11
+ }
12
+ export function resetElementRefCounter() {
13
+ counter = 0;
14
+ }
@@ -0,0 +1,18 @@
1
+ import type { ChangeMarker, ElementHoverPayload } from "../protocol.js";
2
+ export type InspectorRuntimeOptions = {
3
+ document: Document;
4
+ window: Window;
5
+ onHover?: (payload: ElementHoverPayload) => void;
6
+ onSelect?: (payload: ElementHoverPayload) => void;
7
+ onClear?: () => void;
8
+ };
9
+ export type InspectorRuntime = {
10
+ enable: () => void;
11
+ disable: () => void;
12
+ isEnabled: () => boolean;
13
+ setChangeMarkers: (markers: ChangeMarker[]) => void;
14
+ getElementByRefId: (refId: string) => Element | null;
15
+ destroy: () => void;
16
+ };
17
+ export declare function createInspectorRuntime(options: InspectorRuntimeOptions): InspectorRuntime;
18
+ //# sourceMappingURL=inspector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspector.d.ts","sourceRoot":"","sources":["../../src/runtime/inspector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAgBxE,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACjD,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,gBAAgB,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IACpD,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,CAAC;IACrD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,uBAAuB,GAC/B,gBAAgB,CAoIlB"}
@@ -0,0 +1,137 @@
1
+ import { runtimeElementRefFor } from "./element-ref.js";
2
+ import { extractMetadata, findSourceLocation, sourceKeyFor, sourceMappingStatus, } from "./metadata.js";
3
+ import { createOverlay, hideHighlight, positionHighlight, rectForElement, renderChangeMarkers, } from "./overlay.js";
4
+ export function createInspectorRuntime(options) {
5
+ const { document: doc, window: win } = options;
6
+ const overlay = createOverlay(doc);
7
+ let enabled = false;
8
+ let hovered = null;
9
+ const refIdToElement = new Map();
10
+ let markers = [];
11
+ let raf = 0;
12
+ let pendingTarget = null;
13
+ const teardownListeners = () => {
14
+ win.removeEventListener("mousemove", onMove, true);
15
+ win.removeEventListener("click", onClickCapture, true);
16
+ win.removeEventListener("scroll", onScrollOrResize, true);
17
+ win.removeEventListener("resize", onScrollOrResize);
18
+ };
19
+ const onMove = (event) => {
20
+ if (!enabled)
21
+ return;
22
+ const target = deepestElementAt(doc, event.clientX, event.clientY, event.target);
23
+ if (!target || !doc.body.contains(target))
24
+ return;
25
+ pendingTarget = target;
26
+ if (raf)
27
+ return;
28
+ raf = win.requestAnimationFrame(() => {
29
+ raf = 0;
30
+ if (!enabled || !pendingTarget)
31
+ return;
32
+ setHover(pendingTarget);
33
+ pendingTarget = null;
34
+ });
35
+ };
36
+ const onClickCapture = (event) => {
37
+ if (!enabled)
38
+ return;
39
+ event.preventDefault();
40
+ event.stopPropagation();
41
+ const target = deepestElementAt(doc, event.clientX, event.clientY, event.target);
42
+ if (!target || !doc.body.contains(target))
43
+ return;
44
+ select(target);
45
+ };
46
+ const onScrollOrResize = () => {
47
+ if (hovered)
48
+ positionHighlight(overlay.highlight, rectForElement(hovered));
49
+ if (markers.length) {
50
+ renderChangeMarkers(overlay.markerLayer, markers, (id) => refIdToElement.get(id) ?? null);
51
+ }
52
+ };
53
+ function payloadFor(el) {
54
+ const source = findSourceLocation(el);
55
+ const sourceKey = sourceKeyFor(source);
56
+ const elementRef = runtimeElementRefFor(el, sourceKey);
57
+ refIdToElement.set(elementRef.id, el);
58
+ return {
59
+ elementRef,
60
+ rect: rectForElement(el),
61
+ metadata: extractMetadata(el),
62
+ source,
63
+ mappingStatus: sourceMappingStatus(source),
64
+ };
65
+ }
66
+ function setHover(el) {
67
+ if (hovered === el)
68
+ return;
69
+ hovered = el;
70
+ positionHighlight(overlay.highlight, rectForElement(el));
71
+ options.onHover?.(payloadFor(el));
72
+ }
73
+ function select(el) {
74
+ hovered = el;
75
+ positionHighlight(overlay.highlight, rectForElement(el));
76
+ options.onSelect?.(payloadFor(el));
77
+ }
78
+ function clearSelection() {
79
+ hovered = null;
80
+ hideHighlight(overlay.highlight);
81
+ options.onClear?.();
82
+ }
83
+ return {
84
+ enable: () => {
85
+ if (enabled)
86
+ return;
87
+ enabled = true;
88
+ win.addEventListener("mousemove", onMove, true);
89
+ win.addEventListener("click", onClickCapture, true);
90
+ win.addEventListener("scroll", onScrollOrResize, true);
91
+ win.addEventListener("resize", onScrollOrResize);
92
+ },
93
+ disable: () => {
94
+ if (!enabled)
95
+ return;
96
+ enabled = false;
97
+ teardownListeners();
98
+ hovered = null;
99
+ hideHighlight(overlay.highlight);
100
+ clearSelection();
101
+ },
102
+ isEnabled: () => enabled,
103
+ setChangeMarkers: (next) => {
104
+ markers = next;
105
+ renderChangeMarkers(overlay.markerLayer, markers, (id) => refIdToElement.get(id) ?? null);
106
+ },
107
+ getElementByRefId: (id) => refIdToElement.get(id) ?? null,
108
+ destroy: () => {
109
+ if (enabled) {
110
+ enabled = false;
111
+ teardownListeners();
112
+ }
113
+ hovered = null;
114
+ hideHighlight(overlay.highlight);
115
+ overlay.destroy();
116
+ refIdToElement.clear();
117
+ },
118
+ };
119
+ }
120
+ function deepestElementAt(doc, x, y, fallbackTarget) {
121
+ if (typeof doc.elementsFromPoint === "function") {
122
+ const stack = doc.elementsFromPoint(x, y);
123
+ for (const node of stack) {
124
+ if (node instanceof Element) {
125
+ if (node.closest("[data-abbox-inspector]"))
126
+ continue;
127
+ return node;
128
+ }
129
+ }
130
+ }
131
+ if (fallbackTarget instanceof Element) {
132
+ if (fallbackTarget.closest("[data-abbox-inspector]"))
133
+ return null;
134
+ return fallbackTarget;
135
+ }
136
+ return null;
137
+ }
@@ -0,0 +1,6 @@
1
+ import type { ElementMetadata, SourceLocation, SourceMappingStatus } from "../protocol.js";
2
+ export declare function findSourceLocation(el: Element): SourceLocation | null;
3
+ export declare function sourceMappingStatus(source: SourceLocation | null): SourceMappingStatus;
4
+ export declare function extractMetadata(el: Element): ElementMetadata;
5
+ export declare function sourceKeyFor(source: SourceLocation | null): string | null;
6
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/runtime/metadata.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAYxB,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAarE;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,cAAc,GAAG,IAAI,GAC5B,mBAAmB,CAGrB;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,eAAe,CAgC5D;AAcD,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAGzE"}