@abbox/inspector 0.0.0-stage → 0.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 +51 -2
- package/dist/browser.d.ts +9 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +8 -0
- package/dist/host/client-bridge.d.ts +13 -0
- package/dist/host/client-bridge.d.ts.map +1 -0
- package/dist/host/client-bridge.js +75 -0
- package/dist/host/host-bridge.d.ts +34 -0
- package/dist/host/host-bridge.d.ts.map +1 -0
- package/dist/host/host-bridge.js +59 -0
- package/dist/host/origin-aliases.d.ts +3 -0
- package/dist/host/origin-aliases.d.ts.map +1 -0
- package/dist/host/origin-aliases.js +13 -0
- package/dist/host/validate.d.ts +8 -0
- package/dist/host/validate.d.ts.map +1 -0
- package/dist/host/validate.js +13 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/instrument/babel-plugin.d.ts +8 -0
- package/dist/instrument/babel-plugin.d.ts.map +1 -0
- package/dist/instrument/babel-plugin.js +26 -0
- package/dist/instrument/constants.d.ts +10 -0
- package/dist/instrument/constants.d.ts.map +1 -0
- package/dist/instrument/constants.js +17 -0
- package/dist/ir-match.d.ts +34 -0
- package/dist/ir-match.d.ts.map +1 -0
- package/dist/ir-match.js +129 -0
- package/dist/next/babel-config.cjs +40 -0
- package/dist/next/index.d.ts +3 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +2 -0
- package/dist/next/preview-env.d.ts +15 -0
- package/dist/next/preview-env.d.ts.map +1 -0
- package/dist/next/preview-env.js +39 -0
- package/dist/next/webpack-instrumentation.cjs +82 -0
- package/dist/next/with-abbox-inspector.d.ts +31 -0
- package/dist/next/with-abbox-inspector.d.ts.map +1 -0
- package/dist/next/with-abbox-inspector.js +92 -0
- package/dist/protocol.d.ts +92 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +12 -0
- package/dist/react/AbboxInspectorBoot.d.ts +3 -0
- package/dist/react/AbboxInspectorBoot.d.ts.map +1 -0
- package/dist/react/AbboxInspectorBoot.js +10 -0
- package/dist/runtime/bootstrap.d.ts +10 -0
- package/dist/runtime/bootstrap.d.ts.map +1 -0
- package/dist/runtime/bootstrap.js +66 -0
- package/dist/runtime/element-ref.d.ts +4 -0
- package/dist/runtime/element-ref.d.ts.map +1 -0
- package/dist/runtime/element-ref.js +14 -0
- package/dist/runtime/inspector.d.ts +18 -0
- package/dist/runtime/inspector.d.ts.map +1 -0
- package/dist/runtime/inspector.js +137 -0
- package/dist/runtime/metadata.d.ts +6 -0
- package/dist/runtime/metadata.d.ts.map +1 -0
- package/dist/runtime/metadata.js +81 -0
- package/dist/runtime/overlay.d.ts +12 -0
- package/dist/runtime/overlay.d.ts.map +1 -0
- package/dist/runtime/overlay.js +104 -0
- package/dist/vite-plugin.d.ts +8 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +29 -0
- package/docs/TRUST.md +25 -0
- package/docs/architecture.md +21 -0
- package/docs/implementation-report.md +23 -0
- package/docs/integration.md +21 -0
- package/docs/known-limitations.md +8 -0
- package/docs/nextjs-integration.md +35 -0
- package/package.json +90 -4
- package/src/next/babel-config.cjs +40 -0
- package/src/next/webpack-instrumentation.cjs +82 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type AbboxInspectionMode = "preview" | "production";
|
|
2
|
+
/**
|
|
3
|
+
* When true, Inspector instrumentation, CSP frame-ancestors, and client bootstrap are allowed.
|
|
4
|
+
* Preview Vercel deploys stay on by default; production deploys require explicit
|
|
5
|
+
* `ABBBOX_INSPECTION_MODE=production` (set by @abbox/cli production mode).
|
|
6
|
+
*/
|
|
7
|
+
export declare function isAbboxInspectorBuildEnabled(): boolean;
|
|
8
|
+
/** Baked inspection mode in the client bundle (not query parameters). */
|
|
9
|
+
export declare function getBakedInspectionMode(): AbboxInspectionMode | "off";
|
|
10
|
+
/** Client-side: Inspector may connect to the Viewer iframe host. */
|
|
11
|
+
export declare function isAbboxInspectorRuntimeEnabled(): boolean;
|
|
12
|
+
export declare function isAbboxInspectorProductionRuntime(): boolean;
|
|
13
|
+
/** Primary Viewer origin baked at install time (production mode host validation). */
|
|
14
|
+
export declare function getBakedViewerOrigin(): string | undefined;
|
|
15
|
+
//# 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,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,YAAY,CAAC;AAE3D;;;;GAIG;AACH,wBAAgB,4BAA4B,IAAI,OAAO,CAKtD;AAED,yEAAyE;AACzE,wBAAgB,sBAAsB,IAAI,mBAAmB,GAAG,KAAK,CAOpE;AAED,oEAAoE;AACpE,wBAAgB,8BAA8B,IAAI,OAAO,CAExD;AAED,wBAAgB,iCAAiC,IAAI,OAAO,CAE3D;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,SAAS,CAGzD"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When true, Inspector instrumentation, CSP frame-ancestors, and client bootstrap are allowed.
|
|
3
|
+
* Preview Vercel deploys stay on by default; production deploys require explicit
|
|
4
|
+
* `ABBBOX_INSPECTION_MODE=production` (set by @abbox/cli production mode).
|
|
5
|
+
*/
|
|
6
|
+
export function isAbboxInspectorBuildEnabled() {
|
|
7
|
+
if (process.env.ABBBOX_INSPECTOR === "1")
|
|
8
|
+
return true;
|
|
9
|
+
if (process.env.ABBBOX_INSPECTION_MODE === "production")
|
|
10
|
+
return true;
|
|
11
|
+
if (process.env.VERCEL_ENV === "preview")
|
|
12
|
+
return true;
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
/** Baked inspection mode in the client bundle (not query parameters). */
|
|
16
|
+
export function getBakedInspectionMode() {
|
|
17
|
+
const explicit = process.env.NEXT_PUBLIC_ABBOX_INSPECTION_MODE;
|
|
18
|
+
if (explicit === "production")
|
|
19
|
+
return "production";
|
|
20
|
+
if (explicit === "preview")
|
|
21
|
+
return "preview";
|
|
22
|
+
if (process.env.NEXT_PUBLIC_ABBOX_INSPECTOR === "1")
|
|
23
|
+
return "preview";
|
|
24
|
+
if (process.env.NEXT_PUBLIC_VERCEL_ENV === "preview")
|
|
25
|
+
return "preview";
|
|
26
|
+
return "off";
|
|
27
|
+
}
|
|
28
|
+
/** Client-side: Inspector may connect to the Viewer iframe host. */
|
|
29
|
+
export function isAbboxInspectorRuntimeEnabled() {
|
|
30
|
+
return getBakedInspectionMode() !== "off";
|
|
31
|
+
}
|
|
32
|
+
export function isAbboxInspectorProductionRuntime() {
|
|
33
|
+
return getBakedInspectionMode() === "production";
|
|
34
|
+
}
|
|
35
|
+
/** Primary Viewer origin baked at install time (production mode host validation). */
|
|
36
|
+
export function getBakedViewerOrigin() {
|
|
37
|
+
const raw = process.env.NEXT_PUBLIC_ABBOX_VIEWER_ORIGIN;
|
|
38
|
+
return raw?.trim() ? raw.replace(/\/$/, "") : undefined;
|
|
39
|
+
}
|
|
@@ -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,31 @@
|
|
|
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: build gate in preview-env). */
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Origins allowed to embed this app in an iframe (Abbox Viewer).
|
|
13
|
+
* Defaults to hosted viewer + common local Viewer ports.
|
|
14
|
+
*/
|
|
15
|
+
frameAncestors?: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Primary Viewer origin for production-mode postMessage validation.
|
|
18
|
+
* Defaults to the first `frameAncestors` entry.
|
|
19
|
+
*/
|
|
20
|
+
viewerOrigin?: string;
|
|
21
|
+
};
|
|
22
|
+
export declare const DEFAULT_VIEWER_FRAME_ANCESTORS: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Merge Next.js config for Abbox inspection: iframe embedding headers
|
|
25
|
+
* and public env flags for client bootstrap.
|
|
26
|
+
*
|
|
27
|
+
* JSX instrumentation uses a webpack babel-loader rule (no root babel.config.js),
|
|
28
|
+
* so `next/font` in app/layout.tsx can keep using SWC.
|
|
29
|
+
*/
|
|
30
|
+
export declare function withAbboxInspector<T>(config: T, options?: WithAbboxInspectorOptions): T;
|
|
31
|
+
//# 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,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAIF,eAAO,MAAM,8BAA8B,UAM1C,CAAC;AAcF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,MAAM,EAAE,CAAC,EACT,OAAO,GAAE,yBAA8B,GACtC,CAAC,CAiFH"}
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
function bakedInspectionMode() {
|
|
18
|
+
if (process.env.ABBBOX_INSPECTION_MODE === "production") {
|
|
19
|
+
return "production";
|
|
20
|
+
}
|
|
21
|
+
return "preview";
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Merge Next.js config for Abbox inspection: iframe embedding headers
|
|
25
|
+
* and public env flags for client bootstrap.
|
|
26
|
+
*
|
|
27
|
+
* JSX instrumentation uses a webpack babel-loader rule (no root babel.config.js),
|
|
28
|
+
* so `next/font` in app/layout.tsx can keep using SWC.
|
|
29
|
+
*/
|
|
30
|
+
export function withAbboxInspector(config, options = {}) {
|
|
31
|
+
if (!inspectorEnabled(options.enabled)) {
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
34
|
+
const base = config;
|
|
35
|
+
const { applyAbboxWebpackInstrumentation } = require("./webpack-instrumentation.cjs");
|
|
36
|
+
const ancestors = options.frameAncestors ?? DEFAULT_VIEWER_FRAME_ANCESTORS;
|
|
37
|
+
const viewerOrigin = options.viewerOrigin?.replace(/\/$/, "") ??
|
|
38
|
+
ancestors.find((a) => a.startsWith("https://")) ??
|
|
39
|
+
ancestors[0];
|
|
40
|
+
const csp = `frame-ancestors 'self' ${ancestors.join(" ")}`;
|
|
41
|
+
const mode = bakedInspectionMode();
|
|
42
|
+
const headers = async () => {
|
|
43
|
+
const existing = typeof base.headers === "function"
|
|
44
|
+
? await base.headers()
|
|
45
|
+
: (base.headers ?? []);
|
|
46
|
+
return [
|
|
47
|
+
...existing,
|
|
48
|
+
{
|
|
49
|
+
source: "/:path*",
|
|
50
|
+
headers: [
|
|
51
|
+
{
|
|
52
|
+
key: "Content-Security-Policy",
|
|
53
|
+
value: csp,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
};
|
|
59
|
+
const transpile = new Set([
|
|
60
|
+
...(Array.isArray(base.transpilePackages)
|
|
61
|
+
? base.transpilePackages
|
|
62
|
+
: []),
|
|
63
|
+
"@abbox/inspector",
|
|
64
|
+
"abbox-inspector",
|
|
65
|
+
]);
|
|
66
|
+
const userWebpack = base.webpack;
|
|
67
|
+
const webpack = (webpackConfig, webpackOptions) => {
|
|
68
|
+
let cfg = webpackConfig;
|
|
69
|
+
if (typeof userWebpack === "function") {
|
|
70
|
+
const nextCfg = userWebpack(cfg, webpackOptions);
|
|
71
|
+
if (nextCfg)
|
|
72
|
+
cfg = nextCfg;
|
|
73
|
+
}
|
|
74
|
+
const dir = typeof webpackOptions.dir === "string"
|
|
75
|
+
? webpackOptions.dir
|
|
76
|
+
: process.cwd();
|
|
77
|
+
return applyAbboxWebpackInstrumentation(cfg, { dir });
|
|
78
|
+
};
|
|
79
|
+
return {
|
|
80
|
+
...base,
|
|
81
|
+
transpilePackages: [...transpile],
|
|
82
|
+
env: {
|
|
83
|
+
...(base.env ?? {}),
|
|
84
|
+
NEXT_PUBLIC_ABBOX_INSPECTOR: "1",
|
|
85
|
+
NEXT_PUBLIC_ABBOX_INSPECTION_MODE: mode,
|
|
86
|
+
NEXT_PUBLIC_ABBOX_VIEWER_ORIGIN: viewerOrigin,
|
|
87
|
+
NEXT_PUBLIC_VERCEL_ENV: process.env.VERCEL_ENV ?? "",
|
|
88
|
+
},
|
|
89
|
+
headers,
|
|
90
|
+
webpack,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -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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"}
|
package/dist/protocol.js
ADDED
|
@@ -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 @@
|
|
|
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 apps. Activates only when the build baked an authorized
|
|
7
|
+
* inspection mode and `abboxSession` is present (not sufficient on its own for security).
|
|
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":"AAsCA,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,CAmCN"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { withLocalhostAliases } from "../host/origin-aliases.js";
|
|
2
|
+
import { createInspectorClient } from "../host/client-bridge.js";
|
|
3
|
+
import { getBakedViewerOrigin, isAbboxInspectorProductionRuntime, isAbboxInspectorRuntimeEnabled, } from "../next/preview-env.js";
|
|
4
|
+
/** Viewer origins used when `abboxHostOrigin` is missing (preview mode only). */
|
|
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
|
+
function normalizeOrigin(origin) {
|
|
23
|
+
return origin.replace(/\/$/, "");
|
|
24
|
+
}
|
|
25
|
+
function isAllowedHostOrigin(hostOrigin, allowed) {
|
|
26
|
+
const normalized = normalizeOrigin(hostOrigin);
|
|
27
|
+
return allowed.some((o) => normalizeOrigin(o) === normalized);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Entry for instrumented apps. Activates only when the build baked an authorized
|
|
31
|
+
* inspection mode and `abboxSession` is present (not sufficient on its own for security).
|
|
32
|
+
*/
|
|
33
|
+
export function bootstrapInspectorPreview(options = {}) {
|
|
34
|
+
if (typeof window === "undefined")
|
|
35
|
+
return;
|
|
36
|
+
if (!isAbboxInspectorRuntimeEnabled())
|
|
37
|
+
return;
|
|
38
|
+
const params = new URLSearchParams(window.location.search);
|
|
39
|
+
const sessionFromUrl = params.get("abboxSession");
|
|
40
|
+
if (!sessionFromUrl)
|
|
41
|
+
return;
|
|
42
|
+
const sessionId = options.sessionId ?? sessionFromUrl;
|
|
43
|
+
const hostParam = params.get("abboxHostOrigin");
|
|
44
|
+
let allowedHostOrigins;
|
|
45
|
+
if (isAbboxInspectorProductionRuntime()) {
|
|
46
|
+
if (!hostParam)
|
|
47
|
+
return;
|
|
48
|
+
const bakedViewer = getBakedViewerOrigin();
|
|
49
|
+
const allowed = withLocalhostAliases(options.allowedHostOrigins ??
|
|
50
|
+
(bakedViewer ? [bakedViewer] : DEFAULT_VIEWER_ORIGINS));
|
|
51
|
+
if (!isAllowedHostOrigin(hostParam, allowed))
|
|
52
|
+
return;
|
|
53
|
+
allowedHostOrigins = withLocalhostAliases([hostParam]);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
allowedHostOrigins = withLocalhostAliases(options.allowedHostOrigins ??
|
|
57
|
+
(hostParam
|
|
58
|
+
? [hostParam]
|
|
59
|
+
: [...viewerOriginsFromReferrer(), ...DEFAULT_VIEWER_ORIGINS]));
|
|
60
|
+
}
|
|
61
|
+
const client = createInspectorClient({
|
|
62
|
+
sessionId,
|
|
63
|
+
allowedHostOrigins,
|
|
64
|
+
});
|
|
65
|
+
client.start();
|
|
66
|
+
}
|
|
@@ -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"}
|