@archie/devtools 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client/client.d.mts
CHANGED
|
@@ -46,15 +46,33 @@ type InjectOptions = {
|
|
|
46
46
|
localScriptUrl?: string;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
type ArchieRouteFrame = 'phone' | 'tablet' | 'desktop';
|
|
50
|
+
/**
|
|
51
|
+
* Whitelisted subset of a React Router `handle` that is forwarded in
|
|
52
|
+
* ARCHIE_ROUTE_UPDATE. The router's `handle` is free-form (functions, React
|
|
53
|
+
* elements...) and is NOT structured-cloneable in general, so only `surface`
|
|
54
|
+
* (non-empty string) and `frame` ('phone' | 'tablet' | 'desktop') are copied;
|
|
55
|
+
* any other key and any invalid value is dropped. When neither survives the
|
|
56
|
+
* `handle` key is omitted from the entry entirely.
|
|
57
|
+
*/
|
|
58
|
+
type ArchieRouteHandle = {
|
|
59
|
+
surface?: string;
|
|
60
|
+
frame?: ArchieRouteFrame;
|
|
61
|
+
};
|
|
49
62
|
interface ArchieRouteObjectLike {
|
|
50
63
|
id?: string;
|
|
64
|
+
handle?: unknown;
|
|
51
65
|
path?: string;
|
|
52
66
|
index?: boolean;
|
|
67
|
+
Component?: unknown;
|
|
68
|
+
element?: unknown;
|
|
69
|
+
lazy?: unknown;
|
|
53
70
|
children?: readonly ArchieRouteObjectLike[];
|
|
54
71
|
}
|
|
55
72
|
interface ArchieRouteMatchLike {
|
|
56
73
|
route: {
|
|
57
74
|
id?: string;
|
|
75
|
+
handle?: unknown;
|
|
58
76
|
};
|
|
59
77
|
pathname: string;
|
|
60
78
|
params: Record<string, string | undefined>;
|
|
@@ -66,27 +84,78 @@ interface ArchieRouterStateLike {
|
|
|
66
84
|
hash: string;
|
|
67
85
|
};
|
|
68
86
|
matches: readonly ArchieRouteMatchLike[];
|
|
87
|
+
navigation?: {
|
|
88
|
+
state: string;
|
|
89
|
+
};
|
|
90
|
+
fetchers?: ReadonlyMap<string, {
|
|
91
|
+
state: string;
|
|
92
|
+
}>;
|
|
69
93
|
}
|
|
70
94
|
interface ArchieRouterLike {
|
|
71
95
|
routes: readonly ArchieRouteObjectLike[];
|
|
72
96
|
state: ArchieRouterStateLike;
|
|
97
|
+
navigate?(to: string, opts?: {
|
|
98
|
+
replace?: boolean;
|
|
99
|
+
}): Promise<void> | void;
|
|
73
100
|
subscribe(listener: (state: ArchieRouterStateLike) => void): () => void;
|
|
74
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Route information for the sitemap
|
|
104
|
+
*/
|
|
105
|
+
interface ArchieGuardEvidence {
|
|
106
|
+
componentName: string;
|
|
107
|
+
roles?: string[];
|
|
108
|
+
source: 'self' | 'ancestor';
|
|
109
|
+
ancestorId?: string;
|
|
110
|
+
}
|
|
111
|
+
interface RouteInfo {
|
|
112
|
+
id: string | undefined;
|
|
113
|
+
path: string;
|
|
114
|
+
handle?: ArchieRouteHandle;
|
|
115
|
+
hasChildren: boolean;
|
|
116
|
+
parentId: string | null;
|
|
117
|
+
index: boolean;
|
|
118
|
+
isLayout: boolean;
|
|
119
|
+
componentName: string | null;
|
|
120
|
+
guardEvidence: ArchieGuardEvidence[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Type definition for the route update message
|
|
124
|
+
*/
|
|
125
|
+
interface ArchieRouteUpdateMessage {
|
|
126
|
+
type: 'ARCHIE_ROUTE_UPDATE';
|
|
127
|
+
payload: {
|
|
128
|
+
path: string;
|
|
129
|
+
search: string;
|
|
130
|
+
hash: string;
|
|
131
|
+
matches: Array<{
|
|
132
|
+
id: string | undefined;
|
|
133
|
+
pathname: string;
|
|
134
|
+
params: Record<string, string | undefined>;
|
|
135
|
+
handle?: ArchieRouteHandle;
|
|
136
|
+
}>;
|
|
137
|
+
allRoutes: RouteInfo[];
|
|
138
|
+
layoutRoutes: RouteInfo[];
|
|
139
|
+
timestamp: number;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
75
142
|
|
|
76
143
|
interface ArchieDevToolsRuntimeOptions {
|
|
77
144
|
router: ArchieRouterLike;
|
|
78
145
|
inspector?: boolean;
|
|
79
146
|
inspectorOptions?: InjectOptions;
|
|
147
|
+
/** Origins allowed to send NAVIGATE / SNAPSHOT_DOM to the route listener. */
|
|
148
|
+
routeListenerOrigins?: readonly string[];
|
|
80
149
|
}
|
|
81
150
|
|
|
82
151
|
interface ArchieDevToolProviderProps extends ArchieDevToolsRuntimeOptions {
|
|
83
152
|
children: ReactNode;
|
|
84
153
|
}
|
|
85
|
-
declare function ArchieDevToolProvider({ children, router, inspector, inspectorOptions, }: ArchieDevToolProviderProps): react_jsx_runtime.JSX.Element;
|
|
154
|
+
declare function ArchieDevToolProvider({ children, router, inspector, inspectorOptions, routeListenerOrigins, }: ArchieDevToolProviderProps): react_jsx_runtime.JSX.Element;
|
|
86
155
|
|
|
87
156
|
interface DndProviderProps {
|
|
88
157
|
children: React.ReactNode;
|
|
89
158
|
}
|
|
90
159
|
declare function DndProvider({ children }: DndProviderProps): react_jsx_runtime.JSX.Element;
|
|
91
160
|
|
|
92
|
-
export { ArchieDevToolProvider, type ArchieDevToolProviderProps, DndProvider };
|
|
161
|
+
export { ArchieDevToolProvider, type ArchieDevToolProviderProps, type ArchieRouteFrame, type ArchieRouteHandle, type ArchieRouteUpdateMessage, DndProvider, type RouteInfo };
|
package/dist/client/client.d.ts
CHANGED
|
@@ -46,15 +46,33 @@ type InjectOptions = {
|
|
|
46
46
|
localScriptUrl?: string;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
type ArchieRouteFrame = 'phone' | 'tablet' | 'desktop';
|
|
50
|
+
/**
|
|
51
|
+
* Whitelisted subset of a React Router `handle` that is forwarded in
|
|
52
|
+
* ARCHIE_ROUTE_UPDATE. The router's `handle` is free-form (functions, React
|
|
53
|
+
* elements...) and is NOT structured-cloneable in general, so only `surface`
|
|
54
|
+
* (non-empty string) and `frame` ('phone' | 'tablet' | 'desktop') are copied;
|
|
55
|
+
* any other key and any invalid value is dropped. When neither survives the
|
|
56
|
+
* `handle` key is omitted from the entry entirely.
|
|
57
|
+
*/
|
|
58
|
+
type ArchieRouteHandle = {
|
|
59
|
+
surface?: string;
|
|
60
|
+
frame?: ArchieRouteFrame;
|
|
61
|
+
};
|
|
49
62
|
interface ArchieRouteObjectLike {
|
|
50
63
|
id?: string;
|
|
64
|
+
handle?: unknown;
|
|
51
65
|
path?: string;
|
|
52
66
|
index?: boolean;
|
|
67
|
+
Component?: unknown;
|
|
68
|
+
element?: unknown;
|
|
69
|
+
lazy?: unknown;
|
|
53
70
|
children?: readonly ArchieRouteObjectLike[];
|
|
54
71
|
}
|
|
55
72
|
interface ArchieRouteMatchLike {
|
|
56
73
|
route: {
|
|
57
74
|
id?: string;
|
|
75
|
+
handle?: unknown;
|
|
58
76
|
};
|
|
59
77
|
pathname: string;
|
|
60
78
|
params: Record<string, string | undefined>;
|
|
@@ -66,27 +84,78 @@ interface ArchieRouterStateLike {
|
|
|
66
84
|
hash: string;
|
|
67
85
|
};
|
|
68
86
|
matches: readonly ArchieRouteMatchLike[];
|
|
87
|
+
navigation?: {
|
|
88
|
+
state: string;
|
|
89
|
+
};
|
|
90
|
+
fetchers?: ReadonlyMap<string, {
|
|
91
|
+
state: string;
|
|
92
|
+
}>;
|
|
69
93
|
}
|
|
70
94
|
interface ArchieRouterLike {
|
|
71
95
|
routes: readonly ArchieRouteObjectLike[];
|
|
72
96
|
state: ArchieRouterStateLike;
|
|
97
|
+
navigate?(to: string, opts?: {
|
|
98
|
+
replace?: boolean;
|
|
99
|
+
}): Promise<void> | void;
|
|
73
100
|
subscribe(listener: (state: ArchieRouterStateLike) => void): () => void;
|
|
74
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Route information for the sitemap
|
|
104
|
+
*/
|
|
105
|
+
interface ArchieGuardEvidence {
|
|
106
|
+
componentName: string;
|
|
107
|
+
roles?: string[];
|
|
108
|
+
source: 'self' | 'ancestor';
|
|
109
|
+
ancestorId?: string;
|
|
110
|
+
}
|
|
111
|
+
interface RouteInfo {
|
|
112
|
+
id: string | undefined;
|
|
113
|
+
path: string;
|
|
114
|
+
handle?: ArchieRouteHandle;
|
|
115
|
+
hasChildren: boolean;
|
|
116
|
+
parentId: string | null;
|
|
117
|
+
index: boolean;
|
|
118
|
+
isLayout: boolean;
|
|
119
|
+
componentName: string | null;
|
|
120
|
+
guardEvidence: ArchieGuardEvidence[];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Type definition for the route update message
|
|
124
|
+
*/
|
|
125
|
+
interface ArchieRouteUpdateMessage {
|
|
126
|
+
type: 'ARCHIE_ROUTE_UPDATE';
|
|
127
|
+
payload: {
|
|
128
|
+
path: string;
|
|
129
|
+
search: string;
|
|
130
|
+
hash: string;
|
|
131
|
+
matches: Array<{
|
|
132
|
+
id: string | undefined;
|
|
133
|
+
pathname: string;
|
|
134
|
+
params: Record<string, string | undefined>;
|
|
135
|
+
handle?: ArchieRouteHandle;
|
|
136
|
+
}>;
|
|
137
|
+
allRoutes: RouteInfo[];
|
|
138
|
+
layoutRoutes: RouteInfo[];
|
|
139
|
+
timestamp: number;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
75
142
|
|
|
76
143
|
interface ArchieDevToolsRuntimeOptions {
|
|
77
144
|
router: ArchieRouterLike;
|
|
78
145
|
inspector?: boolean;
|
|
79
146
|
inspectorOptions?: InjectOptions;
|
|
147
|
+
/** Origins allowed to send NAVIGATE / SNAPSHOT_DOM to the route listener. */
|
|
148
|
+
routeListenerOrigins?: readonly string[];
|
|
80
149
|
}
|
|
81
150
|
|
|
82
151
|
interface ArchieDevToolProviderProps extends ArchieDevToolsRuntimeOptions {
|
|
83
152
|
children: ReactNode;
|
|
84
153
|
}
|
|
85
|
-
declare function ArchieDevToolProvider({ children, router, inspector, inspectorOptions, }: ArchieDevToolProviderProps): react_jsx_runtime.JSX.Element;
|
|
154
|
+
declare function ArchieDevToolProvider({ children, router, inspector, inspectorOptions, routeListenerOrigins, }: ArchieDevToolProviderProps): react_jsx_runtime.JSX.Element;
|
|
86
155
|
|
|
87
156
|
interface DndProviderProps {
|
|
88
157
|
children: React.ReactNode;
|
|
89
158
|
}
|
|
90
159
|
declare function DndProvider({ children }: DndProviderProps): react_jsx_runtime.JSX.Element;
|
|
91
160
|
|
|
92
|
-
export { ArchieDevToolProvider, type ArchieDevToolProviderProps, DndProvider };
|
|
161
|
+
export { ArchieDevToolProvider, type ArchieDevToolProviderProps, type ArchieRouteFrame, type ArchieRouteHandle, type ArchieRouteUpdateMessage, DndProvider, type RouteInfo };
|
package/dist/client/client.js
CHANGED
|
@@ -10271,7 +10271,7 @@ function isPreviewOrigin(origin) {
|
|
|
10271
10271
|
var DEFAULT_SCRIPT_ID = "archie-inspector-script";
|
|
10272
10272
|
var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
|
|
10273
10273
|
function readBuildVersion() {
|
|
10274
|
-
return "0.1.
|
|
10274
|
+
return "0.1.14" ? "0.1.14" : null;
|
|
10275
10275
|
}
|
|
10276
10276
|
var REMOTE_INSPECTOR_VERSION = readBuildVersion();
|
|
10277
10277
|
var isRemoteInspectorUrlVersioned = REMOTE_INSPECTOR_VERSION !== null;
|
|
@@ -10351,25 +10351,257 @@ function injectInspector(opts = {}) {
|
|
|
10351
10351
|
document.head.appendChild(script);
|
|
10352
10352
|
}
|
|
10353
10353
|
|
|
10354
|
+
// src/client/route-listener/domSnapshot.ts
|
|
10355
|
+
var STRIP_SELECTOR = [
|
|
10356
|
+
"script",
|
|
10357
|
+
"noscript",
|
|
10358
|
+
"iframe",
|
|
10359
|
+
"[data-inspector-badge]",
|
|
10360
|
+
"[data-inspector-context-menu]",
|
|
10361
|
+
"#archie-inspector-styles"
|
|
10362
|
+
].join(",");
|
|
10363
|
+
var PALETTE_SELECTOR = "#archie-design-palette";
|
|
10364
|
+
var CSS_FETCH_TIMEOUT_MS = 2e3;
|
|
10365
|
+
var IDLE_TIMEOUT_MS = 500;
|
|
10366
|
+
var CSS_URL_RE = /url\(\s*(['"]?)([^'")]+)\1\s*\)/g;
|
|
10367
|
+
function absolutizeCssUrls(css, baseHref) {
|
|
10368
|
+
return css.replace(CSS_URL_RE, (full, _quote, raw) => {
|
|
10369
|
+
if (!raw || raw.startsWith("data:") || raw.startsWith("#")) return full;
|
|
10370
|
+
try {
|
|
10371
|
+
return `url(${new URL(raw, baseHref).href})`;
|
|
10372
|
+
} catch {
|
|
10373
|
+
return full;
|
|
10374
|
+
}
|
|
10375
|
+
});
|
|
10376
|
+
}
|
|
10377
|
+
function isExcludedSheet(sheet) {
|
|
10378
|
+
const owner = sheet.ownerNode;
|
|
10379
|
+
return !!owner?.matches?.(`${STRIP_SELECTOR},${PALETTE_SELECTOR}`);
|
|
10380
|
+
}
|
|
10381
|
+
async function fetchCssText(href) {
|
|
10382
|
+
const controller = new AbortController();
|
|
10383
|
+
const timer = setTimeout(() => controller.abort(), CSS_FETCH_TIMEOUT_MS);
|
|
10384
|
+
try {
|
|
10385
|
+
const response = await fetch(href, {
|
|
10386
|
+
mode: "cors",
|
|
10387
|
+
signal: controller.signal
|
|
10388
|
+
});
|
|
10389
|
+
return response.ok ? await response.text() : "";
|
|
10390
|
+
} catch {
|
|
10391
|
+
return "";
|
|
10392
|
+
} finally {
|
|
10393
|
+
clearTimeout(timer);
|
|
10394
|
+
}
|
|
10395
|
+
}
|
|
10396
|
+
async function collectSheetCss(sheet, inlinedHrefs) {
|
|
10397
|
+
if (sheet.disabled || isExcludedSheet(sheet)) return "";
|
|
10398
|
+
const baseHref = sheet.href || document.baseURI;
|
|
10399
|
+
let css = "";
|
|
10400
|
+
try {
|
|
10401
|
+
css = Array.from(sheet.cssRules, (rule) => rule.cssText).join("\n");
|
|
10402
|
+
} catch {
|
|
10403
|
+
css = sheet.href ? await fetchCssText(sheet.href) : "";
|
|
10404
|
+
}
|
|
10405
|
+
if (!css) {
|
|
10406
|
+
const owner = sheet.ownerNode;
|
|
10407
|
+
if (owner?.tagName === "STYLE") css = owner.textContent ?? "";
|
|
10408
|
+
}
|
|
10409
|
+
if (!css) return "";
|
|
10410
|
+
if (sheet.href) inlinedHrefs.add(sheet.href);
|
|
10411
|
+
css = absolutizeCssUrls(css, baseHref);
|
|
10412
|
+
const media = sheet.media?.mediaText;
|
|
10413
|
+
return media && media !== "all" ? `@media ${media}{${css}}` : css;
|
|
10414
|
+
}
|
|
10415
|
+
async function collectDocumentCss(inlinedHrefs) {
|
|
10416
|
+
const sheets = Array.from(document.styleSheets);
|
|
10417
|
+
const parts = await Promise.all(
|
|
10418
|
+
sheets.map((sheet) => collectSheetCss(sheet, inlinedHrefs))
|
|
10419
|
+
);
|
|
10420
|
+
return parts.filter(Boolean).join("\n");
|
|
10421
|
+
}
|
|
10422
|
+
function waitForIdle() {
|
|
10423
|
+
return new Promise((resolve) => {
|
|
10424
|
+
const w = window;
|
|
10425
|
+
if (typeof w.requestIdleCallback === "function") {
|
|
10426
|
+
w.requestIdleCallback(() => resolve(), { timeout: IDLE_TIMEOUT_MS });
|
|
10427
|
+
} else {
|
|
10428
|
+
setTimeout(resolve, 50);
|
|
10429
|
+
}
|
|
10430
|
+
});
|
|
10431
|
+
}
|
|
10432
|
+
async function captureDomSnapshot() {
|
|
10433
|
+
if (document.fonts?.ready) {
|
|
10434
|
+
await document.fonts.ready;
|
|
10435
|
+
}
|
|
10436
|
+
await waitForIdle();
|
|
10437
|
+
const inlinedHrefs = /* @__PURE__ */ new Set();
|
|
10438
|
+
const css = await collectDocumentCss(inlinedHrefs);
|
|
10439
|
+
const clone = document.documentElement.cloneNode(true);
|
|
10440
|
+
clone.querySelectorAll(`${STRIP_SELECTOR}, style:not(${PALETTE_SELECTOR})`).forEach((el) => el.remove());
|
|
10441
|
+
clone.querySelectorAll('link[rel~="stylesheet"]').forEach((link) => {
|
|
10442
|
+
const href = link.href;
|
|
10443
|
+
if (inlinedHrefs.has(href)) {
|
|
10444
|
+
link.remove();
|
|
10445
|
+
} else {
|
|
10446
|
+
link.setAttribute("href", href);
|
|
10447
|
+
}
|
|
10448
|
+
});
|
|
10449
|
+
for (const el of [clone, ...Array.from(clone.querySelectorAll("*"))]) {
|
|
10450
|
+
for (const attr of Array.from(el.attributes)) {
|
|
10451
|
+
if (/^on[a-z]+$/i.test(attr.name)) {
|
|
10452
|
+
el.removeAttribute(attr.name);
|
|
10453
|
+
}
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
let head = clone.querySelector("head");
|
|
10457
|
+
if (!head) {
|
|
10458
|
+
head = document.createElement("head");
|
|
10459
|
+
clone.insertBefore(head, clone.firstChild);
|
|
10460
|
+
}
|
|
10461
|
+
const style = document.createElement("style");
|
|
10462
|
+
style.textContent = css;
|
|
10463
|
+
head.insertBefore(style, head.firstChild);
|
|
10464
|
+
if (!head.querySelector("base")) {
|
|
10465
|
+
const base = document.createElement("base");
|
|
10466
|
+
base.setAttribute("href", document.baseURI);
|
|
10467
|
+
head.insertBefore(base, head.firstChild);
|
|
10468
|
+
}
|
|
10469
|
+
return {
|
|
10470
|
+
html: "<!doctype html>" + clone.outerHTML,
|
|
10471
|
+
path: location.pathname + location.search + location.hash,
|
|
10472
|
+
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
10473
|
+
capturedAt: Date.now()
|
|
10474
|
+
};
|
|
10475
|
+
}
|
|
10476
|
+
|
|
10354
10477
|
// src/client/route-listener/routeListener.ts
|
|
10355
|
-
|
|
10478
|
+
var ROUTE_FRAMES = ["phone", "tablet", "desktop"];
|
|
10479
|
+
function pickRouteHandle(h) {
|
|
10480
|
+
try {
|
|
10481
|
+
if (typeof h !== "object" || h === null) return void 0;
|
|
10482
|
+
const raw = h;
|
|
10483
|
+
const surface = raw.surface;
|
|
10484
|
+
const frame = raw.frame;
|
|
10485
|
+
const out = {};
|
|
10486
|
+
if (typeof surface === "string" && surface !== "") out.surface = surface;
|
|
10487
|
+
if (typeof frame === "string" && ROUTE_FRAMES.includes(frame)) {
|
|
10488
|
+
out.frame = frame;
|
|
10489
|
+
}
|
|
10490
|
+
return out.surface !== void 0 || out.frame !== void 0 ? out : void 0;
|
|
10491
|
+
} catch {
|
|
10492
|
+
return void 0;
|
|
10493
|
+
}
|
|
10494
|
+
}
|
|
10495
|
+
var GUARD_NAME_RE = /guard|protect|require|auth(?!or)/i;
|
|
10496
|
+
var GUARD_PRIVATE_RE = /\bprivate|Private(?=[A-Z]|$)/;
|
|
10497
|
+
var NON_GUARD_SUFFIX_RE = /(Layout|Provider|Context|Page|Form|Screen|View)$/;
|
|
10498
|
+
function isGuardName(name) {
|
|
10499
|
+
return !NON_GUARD_SUFFIX_RE.test(name) && (GUARD_NAME_RE.test(name) || GUARD_PRIVATE_RE.test(name));
|
|
10500
|
+
}
|
|
10501
|
+
var GUARD_PROP_NAMES = [
|
|
10502
|
+
"roles",
|
|
10503
|
+
"allowedRoles",
|
|
10504
|
+
"requiredRoles",
|
|
10505
|
+
"permission",
|
|
10506
|
+
"permissions"
|
|
10507
|
+
];
|
|
10508
|
+
var MAX_ELEMENT_DEPTH = 6;
|
|
10509
|
+
function typeName(type) {
|
|
10510
|
+
if (typeof type === "string") return type;
|
|
10511
|
+
if (typeof type === "function") {
|
|
10512
|
+
const fn = type;
|
|
10513
|
+
return fn.displayName || fn.name || null;
|
|
10514
|
+
}
|
|
10515
|
+
if (type && typeof type === "object") {
|
|
10516
|
+
const obj = type;
|
|
10517
|
+
return obj.displayName || typeName(obj.render) || typeName(obj.type);
|
|
10518
|
+
}
|
|
10519
|
+
return null;
|
|
10520
|
+
}
|
|
10521
|
+
function isElementLike(node) {
|
|
10522
|
+
return !!node && typeof node === "object" && "type" in node && "props" in node;
|
|
10523
|
+
}
|
|
10524
|
+
function literalRoles(value) {
|
|
10525
|
+
if (typeof value === "string") return [value];
|
|
10526
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
|
|
10527
|
+
return value;
|
|
10528
|
+
}
|
|
10529
|
+
return void 0;
|
|
10530
|
+
}
|
|
10531
|
+
function collectGuards(node, depth, out) {
|
|
10532
|
+
if (depth > MAX_ELEMENT_DEPTH || node == null) return;
|
|
10533
|
+
if (Array.isArray(node)) {
|
|
10534
|
+
for (const child of node) collectGuards(child, depth, out);
|
|
10535
|
+
return;
|
|
10536
|
+
}
|
|
10537
|
+
if (!isElementLike(node)) return;
|
|
10538
|
+
const props = node.props ?? {};
|
|
10539
|
+
const name = typeName(node.type);
|
|
10540
|
+
const roleProps = GUARD_PROP_NAMES.filter((p) => p in props);
|
|
10541
|
+
const isNamedGuard = typeof node.type !== "string" && !!name && isGuardName(name);
|
|
10542
|
+
if (isNamedGuard || roleProps.length > 0) {
|
|
10543
|
+
const roles = roleProps.flatMap((p) => literalRoles(props[p]) ?? []);
|
|
10544
|
+
out.push({
|
|
10545
|
+
componentName: name ?? "Anonymous",
|
|
10546
|
+
...roles.length > 0 ? { roles } : {}
|
|
10547
|
+
});
|
|
10548
|
+
}
|
|
10549
|
+
collectGuards(props.children, depth + 1, out);
|
|
10550
|
+
}
|
|
10551
|
+
function routeComponentName(route) {
|
|
10552
|
+
if (route.Component) return typeName(route.Component);
|
|
10553
|
+
if (isElementLike(route.element)) return typeName(route.element.type);
|
|
10554
|
+
return null;
|
|
10555
|
+
}
|
|
10556
|
+
function extractAllRoutes(routes, parentPath = "", parentId = null, inheritedGuards = []) {
|
|
10356
10557
|
const result = [];
|
|
10357
10558
|
for (const route of routes) {
|
|
10358
10559
|
const path = route.path ? (parentPath + "/" + route.path).replace(/\/+/g, "/") : parentPath || "/";
|
|
10359
|
-
|
|
10560
|
+
const hasChildren = !!(route.children && route.children.length > 0);
|
|
10561
|
+
const isLayout = !route.path && !route.index && hasChildren;
|
|
10562
|
+
const own = [];
|
|
10563
|
+
collectGuards(route.element, 0, own);
|
|
10564
|
+
const handle = pickRouteHandle(route.handle);
|
|
10565
|
+
if (route.path !== void 0 || route.index || isLayout) {
|
|
10360
10566
|
result.push({
|
|
10361
10567
|
id: route.id,
|
|
10362
10568
|
path: route.index ? parentPath || "/" : path,
|
|
10363
|
-
|
|
10569
|
+
...handle ? { handle } : {},
|
|
10570
|
+
hasChildren,
|
|
10571
|
+
parentId,
|
|
10572
|
+
index: !!route.index,
|
|
10573
|
+
isLayout,
|
|
10574
|
+
componentName: routeComponentName(route),
|
|
10575
|
+
guardEvidence: [
|
|
10576
|
+
...own.map((g) => ({ ...g, source: "self" })),
|
|
10577
|
+
...inheritedGuards
|
|
10578
|
+
]
|
|
10364
10579
|
});
|
|
10365
10580
|
}
|
|
10366
10581
|
if (route.children) {
|
|
10367
|
-
|
|
10582
|
+
const forChildren = [
|
|
10583
|
+
...own.map(
|
|
10584
|
+
(g) => ({
|
|
10585
|
+
...g,
|
|
10586
|
+
source: "ancestor",
|
|
10587
|
+
...route.id !== void 0 ? { ancestorId: route.id } : {}
|
|
10588
|
+
})
|
|
10589
|
+
),
|
|
10590
|
+
...inheritedGuards
|
|
10591
|
+
];
|
|
10592
|
+
result.push(
|
|
10593
|
+
...extractAllRoutes(
|
|
10594
|
+
route.children,
|
|
10595
|
+
path,
|
|
10596
|
+
route.id ?? parentId,
|
|
10597
|
+
forChildren
|
|
10598
|
+
)
|
|
10599
|
+
);
|
|
10368
10600
|
}
|
|
10369
10601
|
}
|
|
10370
10602
|
return result;
|
|
10371
10603
|
}
|
|
10372
|
-
function broadcastRouteState(target, state,
|
|
10604
|
+
function broadcastRouteState(target, state, routes) {
|
|
10373
10605
|
try {
|
|
10374
10606
|
const message = {
|
|
10375
10607
|
type: "ARCHIE_ROUTE_UPDATE",
|
|
@@ -10377,12 +10609,18 @@ function broadcastRouteState(target, state, allRoutes) {
|
|
|
10377
10609
|
path: state.location.pathname,
|
|
10378
10610
|
search: state.location.search,
|
|
10379
10611
|
hash: state.location.hash,
|
|
10380
|
-
matches: state.matches.map((m) =>
|
|
10381
|
-
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10612
|
+
matches: state.matches.map((m) => {
|
|
10613
|
+
const handle = pickRouteHandle(m.route.handle);
|
|
10614
|
+
return {
|
|
10615
|
+
id: m.route.id,
|
|
10616
|
+
pathname: m.pathname,
|
|
10617
|
+
params: m.params,
|
|
10618
|
+
...handle ? { handle } : {}
|
|
10619
|
+
};
|
|
10620
|
+
}),
|
|
10621
|
+
// Layouts stay out of allRoutes so consumers that predate them don't list duplicates.
|
|
10622
|
+
allRoutes: routes.filter((r) => !r.isLayout),
|
|
10623
|
+
layoutRoutes: routes.filter((r) => r.isLayout),
|
|
10386
10624
|
timestamp: Date.now()
|
|
10387
10625
|
}
|
|
10388
10626
|
};
|
|
@@ -10390,21 +10628,174 @@ function broadcastRouteState(target, state, allRoutes) {
|
|
|
10390
10628
|
} catch {
|
|
10391
10629
|
}
|
|
10392
10630
|
}
|
|
10393
|
-
|
|
10631
|
+
var SETTLE_QUIET_MS = 400;
|
|
10632
|
+
var SETTLE_TIMEOUT_MS = 8e3;
|
|
10633
|
+
var SETTLE_POLL_MS = 50;
|
|
10634
|
+
function readPackageVersion() {
|
|
10635
|
+
return true ? "0.1.14" : "unknown";
|
|
10636
|
+
}
|
|
10637
|
+
var DEFAULT_ARCHIE_COMMAND_ORIGINS = getAllowedOrigins(true);
|
|
10638
|
+
function originPatternToRegExp(pattern) {
|
|
10639
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/:\*$/, ":\\d+").replace(/\*/g, "[a-z0-9.-]+");
|
|
10640
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
10641
|
+
}
|
|
10642
|
+
function isAllowedCommandOrigin(origin, allowed) {
|
|
10643
|
+
return allowed.some((pattern) => originPatternToRegExp(pattern).test(origin));
|
|
10644
|
+
}
|
|
10645
|
+
function postToTarget(target, message, targetOrigin = "*") {
|
|
10646
|
+
try {
|
|
10647
|
+
target.postMessage(message, targetOrigin);
|
|
10648
|
+
} catch {
|
|
10649
|
+
}
|
|
10650
|
+
}
|
|
10651
|
+
function isRouterBusy(router) {
|
|
10652
|
+
const { navigation, fetchers } = router.state;
|
|
10653
|
+
if (navigation && navigation.state !== "idle") return true;
|
|
10654
|
+
if (fetchers) {
|
|
10655
|
+
for (const fetcher of fetchers.values()) {
|
|
10656
|
+
if (fetcher.state !== "idle") return true;
|
|
10657
|
+
}
|
|
10658
|
+
}
|
|
10659
|
+
return false;
|
|
10660
|
+
}
|
|
10661
|
+
function waitForSettled(router, navigation) {
|
|
10662
|
+
return new Promise((resolve) => {
|
|
10663
|
+
const startedAt = Date.now();
|
|
10664
|
+
let lastMutationAt = Date.now();
|
|
10665
|
+
let navigationDone = false;
|
|
10666
|
+
const observer = new MutationObserver(() => {
|
|
10667
|
+
lastMutationAt = Date.now();
|
|
10668
|
+
});
|
|
10669
|
+
observer.observe(document, {
|
|
10670
|
+
subtree: true,
|
|
10671
|
+
childList: true,
|
|
10672
|
+
attributes: true,
|
|
10673
|
+
characterData: true
|
|
10674
|
+
});
|
|
10675
|
+
navigation.then(
|
|
10676
|
+
() => {
|
|
10677
|
+
navigationDone = true;
|
|
10678
|
+
},
|
|
10679
|
+
() => {
|
|
10680
|
+
navigationDone = true;
|
|
10681
|
+
}
|
|
10682
|
+
);
|
|
10683
|
+
const finish = (settled) => {
|
|
10684
|
+
clearInterval(timer);
|
|
10685
|
+
observer.disconnect();
|
|
10686
|
+
resolve(settled);
|
|
10687
|
+
};
|
|
10688
|
+
const timer = setInterval(() => {
|
|
10689
|
+
const now = Date.now();
|
|
10690
|
+
if (navigationDone && !isRouterBusy(router) && now - lastMutationAt >= SETTLE_QUIET_MS) {
|
|
10691
|
+
finish(true);
|
|
10692
|
+
} else if (now - startedAt >= SETTLE_TIMEOUT_MS) {
|
|
10693
|
+
finish(false);
|
|
10694
|
+
}
|
|
10695
|
+
}, SETTLE_POLL_MS);
|
|
10696
|
+
});
|
|
10697
|
+
}
|
|
10698
|
+
function pathnameOf(path) {
|
|
10699
|
+
return path.split(/[?#]/, 1)[0];
|
|
10700
|
+
}
|
|
10701
|
+
async function handleNavigate(router, target, targetOrigin, data) {
|
|
10702
|
+
if (typeof router.navigate !== "function") {
|
|
10703
|
+
const { location: location3 } = router.state;
|
|
10704
|
+
postToTarget(target, {
|
|
10705
|
+
type: "ARCHIE_NAVIGATE_RESULT",
|
|
10706
|
+
requestId: data.requestId,
|
|
10707
|
+
requestedPath: data.path,
|
|
10708
|
+
finalPath: location3.pathname + location3.search + location3.hash,
|
|
10709
|
+
matchedRouteIds: [],
|
|
10710
|
+
redirected: false,
|
|
10711
|
+
settled: false
|
|
10712
|
+
}, targetOrigin);
|
|
10713
|
+
return;
|
|
10714
|
+
}
|
|
10715
|
+
let navigation = Promise.resolve();
|
|
10716
|
+
try {
|
|
10717
|
+
navigation = Promise.resolve(
|
|
10718
|
+
router.navigate(data.path, { replace: data.replace })
|
|
10719
|
+
);
|
|
10720
|
+
} catch {
|
|
10721
|
+
navigation = Promise.resolve();
|
|
10722
|
+
}
|
|
10723
|
+
const settled = await waitForSettled(router, navigation);
|
|
10724
|
+
const { location: location2, matches } = router.state;
|
|
10725
|
+
postToTarget(target, {
|
|
10726
|
+
type: "ARCHIE_NAVIGATE_RESULT",
|
|
10727
|
+
requestId: data.requestId,
|
|
10728
|
+
requestedPath: data.path,
|
|
10729
|
+
finalPath: location2.pathname + location2.search + location2.hash,
|
|
10730
|
+
matchedRouteIds: matches.map((m) => m.route.id).filter((id) => typeof id === "string"),
|
|
10731
|
+
redirected: location2.pathname !== pathnameOf(data.path),
|
|
10732
|
+
settled
|
|
10733
|
+
}, targetOrigin);
|
|
10734
|
+
}
|
|
10735
|
+
async function handleSnapshot(target, targetOrigin, requestId) {
|
|
10736
|
+
try {
|
|
10737
|
+
const snapshot = await captureDomSnapshot();
|
|
10738
|
+
postToTarget(target, {
|
|
10739
|
+
type: "ARCHIE_SNAPSHOT_DOM_RESULT",
|
|
10740
|
+
requestId,
|
|
10741
|
+
...snapshot
|
|
10742
|
+
}, targetOrigin);
|
|
10743
|
+
} catch (error) {
|
|
10744
|
+
postToTarget(target, {
|
|
10745
|
+
type: "ARCHIE_SNAPSHOT_DOM_FAILED",
|
|
10746
|
+
requestId,
|
|
10747
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10748
|
+
}, targetOrigin);
|
|
10749
|
+
}
|
|
10750
|
+
}
|
|
10751
|
+
function broadcastCapabilities(router, target) {
|
|
10752
|
+
postToTarget(target, {
|
|
10753
|
+
type: "ARCHIE_CAPABILITIES",
|
|
10754
|
+
version: readPackageVersion(),
|
|
10755
|
+
capabilities: {
|
|
10756
|
+
navigate: typeof router.navigate === "function",
|
|
10757
|
+
snapshot: true,
|
|
10758
|
+
identityOverride: false
|
|
10759
|
+
}
|
|
10760
|
+
});
|
|
10761
|
+
}
|
|
10762
|
+
function setupArchieRouteListener(router, options = {}) {
|
|
10763
|
+
const allowedOrigins = options.allowedOrigins ?? DEFAULT_ARCHIE_COMMAND_ORIGINS;
|
|
10394
10764
|
const target = window.parent !== window ? window.parent : window.opener;
|
|
10395
10765
|
if (!target) {
|
|
10396
10766
|
return () => {
|
|
10397
10767
|
};
|
|
10398
10768
|
}
|
|
10399
10769
|
broadcastRouteState(target, router.state, extractAllRoutes(router.routes));
|
|
10770
|
+
broadcastCapabilities(router, target);
|
|
10400
10771
|
const unsubscribe = router.subscribe((state) => {
|
|
10401
10772
|
const currentRoutes = extractAllRoutes(router.routes);
|
|
10402
10773
|
broadcastRouteState(target, state, currentRoutes);
|
|
10403
10774
|
});
|
|
10404
10775
|
const handleMessage = (event) => {
|
|
10405
|
-
if (event.
|
|
10406
|
-
|
|
10407
|
-
|
|
10776
|
+
if (event.source !== target) return;
|
|
10777
|
+
const data = event.data;
|
|
10778
|
+
switch (data?.type) {
|
|
10779
|
+
case "ARCHIE_QUERY_ROUTES":
|
|
10780
|
+
broadcastRouteState(
|
|
10781
|
+
target,
|
|
10782
|
+
router.state,
|
|
10783
|
+
extractAllRoutes(router.routes)
|
|
10784
|
+
);
|
|
10785
|
+
break;
|
|
10786
|
+
case "ARCHIE_QUERY_CAPABILITIES":
|
|
10787
|
+
broadcastCapabilities(router, target);
|
|
10788
|
+
break;
|
|
10789
|
+
case "ARCHIE_NAVIGATE":
|
|
10790
|
+
if (isAllowedCommandOrigin(event.origin, allowedOrigins) && typeof data.requestId === "string" && typeof data.path === "string") {
|
|
10791
|
+
void handleNavigate(router, target, event.origin, data);
|
|
10792
|
+
}
|
|
10793
|
+
break;
|
|
10794
|
+
case "ARCHIE_SNAPSHOT_DOM":
|
|
10795
|
+
if (isAllowedCommandOrigin(event.origin, allowedOrigins) && typeof data.requestId === "string") {
|
|
10796
|
+
void handleSnapshot(target, event.origin, data.requestId);
|
|
10797
|
+
}
|
|
10798
|
+
break;
|
|
10408
10799
|
}
|
|
10409
10800
|
};
|
|
10410
10801
|
window.addEventListener("message", handleMessage);
|
|
@@ -10444,7 +10835,8 @@ function installDomMetaSanitizer() {
|
|
|
10444
10835
|
function useArchieDevToolsRuntime({
|
|
10445
10836
|
router,
|
|
10446
10837
|
inspector,
|
|
10447
|
-
inspectorOptions
|
|
10838
|
+
inspectorOptions,
|
|
10839
|
+
routeListenerOrigins
|
|
10448
10840
|
}) {
|
|
10449
10841
|
installDomMetaSanitizer();
|
|
10450
10842
|
const stableOptionsKey = JSON.stringify(inspectorOptions ?? null);
|
|
@@ -10454,7 +10846,9 @@ function useArchieDevToolsRuntime({
|
|
|
10454
10846
|
if (process.env.NODE_ENV !== "development") {
|
|
10455
10847
|
return;
|
|
10456
10848
|
}
|
|
10457
|
-
const unsubscribe = setupArchieRouteListener(router
|
|
10849
|
+
const unsubscribe = setupArchieRouteListener(router, {
|
|
10850
|
+
allowedOrigins: routeListenerOrigins
|
|
10851
|
+
});
|
|
10458
10852
|
if (inspector !== false) {
|
|
10459
10853
|
const useLocalInspector = readArchieLocalInspectorFlag();
|
|
10460
10854
|
injectInspector({
|
|
@@ -10472,9 +10866,15 @@ function ArchieDevToolProvider({
|
|
|
10472
10866
|
children,
|
|
10473
10867
|
router,
|
|
10474
10868
|
inspector,
|
|
10475
|
-
inspectorOptions
|
|
10869
|
+
inspectorOptions,
|
|
10870
|
+
routeListenerOrigins
|
|
10476
10871
|
}) {
|
|
10477
|
-
useArchieDevToolsRuntime({
|
|
10872
|
+
useArchieDevToolsRuntime({
|
|
10873
|
+
router,
|
|
10874
|
+
inspector,
|
|
10875
|
+
inspectorOptions,
|
|
10876
|
+
routeListenerOrigins
|
|
10877
|
+
});
|
|
10478
10878
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DndProvider, { children });
|
|
10479
10879
|
}
|
|
10480
10880
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/client/client.mjs
CHANGED
|
@@ -10236,7 +10236,7 @@ function isPreviewOrigin(origin) {
|
|
|
10236
10236
|
var DEFAULT_SCRIPT_ID = "archie-inspector-script";
|
|
10237
10237
|
var R2_INSPECTOR_BASE_URL = "https://pub-bf238c278d9644b39ec34ecaca1d315c.r2.dev";
|
|
10238
10238
|
function readBuildVersion() {
|
|
10239
|
-
return "0.1.
|
|
10239
|
+
return "0.1.14" ? "0.1.14" : null;
|
|
10240
10240
|
}
|
|
10241
10241
|
var REMOTE_INSPECTOR_VERSION = readBuildVersion();
|
|
10242
10242
|
var isRemoteInspectorUrlVersioned = REMOTE_INSPECTOR_VERSION !== null;
|
|
@@ -10316,25 +10316,257 @@ function injectInspector(opts = {}) {
|
|
|
10316
10316
|
document.head.appendChild(script);
|
|
10317
10317
|
}
|
|
10318
10318
|
|
|
10319
|
+
// src/client/route-listener/domSnapshot.ts
|
|
10320
|
+
var STRIP_SELECTOR = [
|
|
10321
|
+
"script",
|
|
10322
|
+
"noscript",
|
|
10323
|
+
"iframe",
|
|
10324
|
+
"[data-inspector-badge]",
|
|
10325
|
+
"[data-inspector-context-menu]",
|
|
10326
|
+
"#archie-inspector-styles"
|
|
10327
|
+
].join(",");
|
|
10328
|
+
var PALETTE_SELECTOR = "#archie-design-palette";
|
|
10329
|
+
var CSS_FETCH_TIMEOUT_MS = 2e3;
|
|
10330
|
+
var IDLE_TIMEOUT_MS = 500;
|
|
10331
|
+
var CSS_URL_RE = /url\(\s*(['"]?)([^'")]+)\1\s*\)/g;
|
|
10332
|
+
function absolutizeCssUrls(css, baseHref) {
|
|
10333
|
+
return css.replace(CSS_URL_RE, (full, _quote, raw) => {
|
|
10334
|
+
if (!raw || raw.startsWith("data:") || raw.startsWith("#")) return full;
|
|
10335
|
+
try {
|
|
10336
|
+
return `url(${new URL(raw, baseHref).href})`;
|
|
10337
|
+
} catch {
|
|
10338
|
+
return full;
|
|
10339
|
+
}
|
|
10340
|
+
});
|
|
10341
|
+
}
|
|
10342
|
+
function isExcludedSheet(sheet) {
|
|
10343
|
+
const owner = sheet.ownerNode;
|
|
10344
|
+
return !!owner?.matches?.(`${STRIP_SELECTOR},${PALETTE_SELECTOR}`);
|
|
10345
|
+
}
|
|
10346
|
+
async function fetchCssText(href) {
|
|
10347
|
+
const controller = new AbortController();
|
|
10348
|
+
const timer = setTimeout(() => controller.abort(), CSS_FETCH_TIMEOUT_MS);
|
|
10349
|
+
try {
|
|
10350
|
+
const response = await fetch(href, {
|
|
10351
|
+
mode: "cors",
|
|
10352
|
+
signal: controller.signal
|
|
10353
|
+
});
|
|
10354
|
+
return response.ok ? await response.text() : "";
|
|
10355
|
+
} catch {
|
|
10356
|
+
return "";
|
|
10357
|
+
} finally {
|
|
10358
|
+
clearTimeout(timer);
|
|
10359
|
+
}
|
|
10360
|
+
}
|
|
10361
|
+
async function collectSheetCss(sheet, inlinedHrefs) {
|
|
10362
|
+
if (sheet.disabled || isExcludedSheet(sheet)) return "";
|
|
10363
|
+
const baseHref = sheet.href || document.baseURI;
|
|
10364
|
+
let css = "";
|
|
10365
|
+
try {
|
|
10366
|
+
css = Array.from(sheet.cssRules, (rule) => rule.cssText).join("\n");
|
|
10367
|
+
} catch {
|
|
10368
|
+
css = sheet.href ? await fetchCssText(sheet.href) : "";
|
|
10369
|
+
}
|
|
10370
|
+
if (!css) {
|
|
10371
|
+
const owner = sheet.ownerNode;
|
|
10372
|
+
if (owner?.tagName === "STYLE") css = owner.textContent ?? "";
|
|
10373
|
+
}
|
|
10374
|
+
if (!css) return "";
|
|
10375
|
+
if (sheet.href) inlinedHrefs.add(sheet.href);
|
|
10376
|
+
css = absolutizeCssUrls(css, baseHref);
|
|
10377
|
+
const media = sheet.media?.mediaText;
|
|
10378
|
+
return media && media !== "all" ? `@media ${media}{${css}}` : css;
|
|
10379
|
+
}
|
|
10380
|
+
async function collectDocumentCss(inlinedHrefs) {
|
|
10381
|
+
const sheets = Array.from(document.styleSheets);
|
|
10382
|
+
const parts = await Promise.all(
|
|
10383
|
+
sheets.map((sheet) => collectSheetCss(sheet, inlinedHrefs))
|
|
10384
|
+
);
|
|
10385
|
+
return parts.filter(Boolean).join("\n");
|
|
10386
|
+
}
|
|
10387
|
+
function waitForIdle() {
|
|
10388
|
+
return new Promise((resolve) => {
|
|
10389
|
+
const w = window;
|
|
10390
|
+
if (typeof w.requestIdleCallback === "function") {
|
|
10391
|
+
w.requestIdleCallback(() => resolve(), { timeout: IDLE_TIMEOUT_MS });
|
|
10392
|
+
} else {
|
|
10393
|
+
setTimeout(resolve, 50);
|
|
10394
|
+
}
|
|
10395
|
+
});
|
|
10396
|
+
}
|
|
10397
|
+
async function captureDomSnapshot() {
|
|
10398
|
+
if (document.fonts?.ready) {
|
|
10399
|
+
await document.fonts.ready;
|
|
10400
|
+
}
|
|
10401
|
+
await waitForIdle();
|
|
10402
|
+
const inlinedHrefs = /* @__PURE__ */ new Set();
|
|
10403
|
+
const css = await collectDocumentCss(inlinedHrefs);
|
|
10404
|
+
const clone = document.documentElement.cloneNode(true);
|
|
10405
|
+
clone.querySelectorAll(`${STRIP_SELECTOR}, style:not(${PALETTE_SELECTOR})`).forEach((el) => el.remove());
|
|
10406
|
+
clone.querySelectorAll('link[rel~="stylesheet"]').forEach((link) => {
|
|
10407
|
+
const href = link.href;
|
|
10408
|
+
if (inlinedHrefs.has(href)) {
|
|
10409
|
+
link.remove();
|
|
10410
|
+
} else {
|
|
10411
|
+
link.setAttribute("href", href);
|
|
10412
|
+
}
|
|
10413
|
+
});
|
|
10414
|
+
for (const el of [clone, ...Array.from(clone.querySelectorAll("*"))]) {
|
|
10415
|
+
for (const attr of Array.from(el.attributes)) {
|
|
10416
|
+
if (/^on[a-z]+$/i.test(attr.name)) {
|
|
10417
|
+
el.removeAttribute(attr.name);
|
|
10418
|
+
}
|
|
10419
|
+
}
|
|
10420
|
+
}
|
|
10421
|
+
let head = clone.querySelector("head");
|
|
10422
|
+
if (!head) {
|
|
10423
|
+
head = document.createElement("head");
|
|
10424
|
+
clone.insertBefore(head, clone.firstChild);
|
|
10425
|
+
}
|
|
10426
|
+
const style = document.createElement("style");
|
|
10427
|
+
style.textContent = css;
|
|
10428
|
+
head.insertBefore(style, head.firstChild);
|
|
10429
|
+
if (!head.querySelector("base")) {
|
|
10430
|
+
const base = document.createElement("base");
|
|
10431
|
+
base.setAttribute("href", document.baseURI);
|
|
10432
|
+
head.insertBefore(base, head.firstChild);
|
|
10433
|
+
}
|
|
10434
|
+
return {
|
|
10435
|
+
html: "<!doctype html>" + clone.outerHTML,
|
|
10436
|
+
path: location.pathname + location.search + location.hash,
|
|
10437
|
+
viewport: { width: window.innerWidth, height: window.innerHeight },
|
|
10438
|
+
capturedAt: Date.now()
|
|
10439
|
+
};
|
|
10440
|
+
}
|
|
10441
|
+
|
|
10319
10442
|
// src/client/route-listener/routeListener.ts
|
|
10320
|
-
|
|
10443
|
+
var ROUTE_FRAMES = ["phone", "tablet", "desktop"];
|
|
10444
|
+
function pickRouteHandle(h) {
|
|
10445
|
+
try {
|
|
10446
|
+
if (typeof h !== "object" || h === null) return void 0;
|
|
10447
|
+
const raw = h;
|
|
10448
|
+
const surface = raw.surface;
|
|
10449
|
+
const frame = raw.frame;
|
|
10450
|
+
const out = {};
|
|
10451
|
+
if (typeof surface === "string" && surface !== "") out.surface = surface;
|
|
10452
|
+
if (typeof frame === "string" && ROUTE_FRAMES.includes(frame)) {
|
|
10453
|
+
out.frame = frame;
|
|
10454
|
+
}
|
|
10455
|
+
return out.surface !== void 0 || out.frame !== void 0 ? out : void 0;
|
|
10456
|
+
} catch {
|
|
10457
|
+
return void 0;
|
|
10458
|
+
}
|
|
10459
|
+
}
|
|
10460
|
+
var GUARD_NAME_RE = /guard|protect|require|auth(?!or)/i;
|
|
10461
|
+
var GUARD_PRIVATE_RE = /\bprivate|Private(?=[A-Z]|$)/;
|
|
10462
|
+
var NON_GUARD_SUFFIX_RE = /(Layout|Provider|Context|Page|Form|Screen|View)$/;
|
|
10463
|
+
function isGuardName(name) {
|
|
10464
|
+
return !NON_GUARD_SUFFIX_RE.test(name) && (GUARD_NAME_RE.test(name) || GUARD_PRIVATE_RE.test(name));
|
|
10465
|
+
}
|
|
10466
|
+
var GUARD_PROP_NAMES = [
|
|
10467
|
+
"roles",
|
|
10468
|
+
"allowedRoles",
|
|
10469
|
+
"requiredRoles",
|
|
10470
|
+
"permission",
|
|
10471
|
+
"permissions"
|
|
10472
|
+
];
|
|
10473
|
+
var MAX_ELEMENT_DEPTH = 6;
|
|
10474
|
+
function typeName(type) {
|
|
10475
|
+
if (typeof type === "string") return type;
|
|
10476
|
+
if (typeof type === "function") {
|
|
10477
|
+
const fn = type;
|
|
10478
|
+
return fn.displayName || fn.name || null;
|
|
10479
|
+
}
|
|
10480
|
+
if (type && typeof type === "object") {
|
|
10481
|
+
const obj = type;
|
|
10482
|
+
return obj.displayName || typeName(obj.render) || typeName(obj.type);
|
|
10483
|
+
}
|
|
10484
|
+
return null;
|
|
10485
|
+
}
|
|
10486
|
+
function isElementLike(node) {
|
|
10487
|
+
return !!node && typeof node === "object" && "type" in node && "props" in node;
|
|
10488
|
+
}
|
|
10489
|
+
function literalRoles(value) {
|
|
10490
|
+
if (typeof value === "string") return [value];
|
|
10491
|
+
if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
|
|
10492
|
+
return value;
|
|
10493
|
+
}
|
|
10494
|
+
return void 0;
|
|
10495
|
+
}
|
|
10496
|
+
function collectGuards(node, depth, out) {
|
|
10497
|
+
if (depth > MAX_ELEMENT_DEPTH || node == null) return;
|
|
10498
|
+
if (Array.isArray(node)) {
|
|
10499
|
+
for (const child of node) collectGuards(child, depth, out);
|
|
10500
|
+
return;
|
|
10501
|
+
}
|
|
10502
|
+
if (!isElementLike(node)) return;
|
|
10503
|
+
const props = node.props ?? {};
|
|
10504
|
+
const name = typeName(node.type);
|
|
10505
|
+
const roleProps = GUARD_PROP_NAMES.filter((p) => p in props);
|
|
10506
|
+
const isNamedGuard = typeof node.type !== "string" && !!name && isGuardName(name);
|
|
10507
|
+
if (isNamedGuard || roleProps.length > 0) {
|
|
10508
|
+
const roles = roleProps.flatMap((p) => literalRoles(props[p]) ?? []);
|
|
10509
|
+
out.push({
|
|
10510
|
+
componentName: name ?? "Anonymous",
|
|
10511
|
+
...roles.length > 0 ? { roles } : {}
|
|
10512
|
+
});
|
|
10513
|
+
}
|
|
10514
|
+
collectGuards(props.children, depth + 1, out);
|
|
10515
|
+
}
|
|
10516
|
+
function routeComponentName(route) {
|
|
10517
|
+
if (route.Component) return typeName(route.Component);
|
|
10518
|
+
if (isElementLike(route.element)) return typeName(route.element.type);
|
|
10519
|
+
return null;
|
|
10520
|
+
}
|
|
10521
|
+
function extractAllRoutes(routes, parentPath = "", parentId = null, inheritedGuards = []) {
|
|
10321
10522
|
const result = [];
|
|
10322
10523
|
for (const route of routes) {
|
|
10323
10524
|
const path = route.path ? (parentPath + "/" + route.path).replace(/\/+/g, "/") : parentPath || "/";
|
|
10324
|
-
|
|
10525
|
+
const hasChildren = !!(route.children && route.children.length > 0);
|
|
10526
|
+
const isLayout = !route.path && !route.index && hasChildren;
|
|
10527
|
+
const own = [];
|
|
10528
|
+
collectGuards(route.element, 0, own);
|
|
10529
|
+
const handle = pickRouteHandle(route.handle);
|
|
10530
|
+
if (route.path !== void 0 || route.index || isLayout) {
|
|
10325
10531
|
result.push({
|
|
10326
10532
|
id: route.id,
|
|
10327
10533
|
path: route.index ? parentPath || "/" : path,
|
|
10328
|
-
|
|
10534
|
+
...handle ? { handle } : {},
|
|
10535
|
+
hasChildren,
|
|
10536
|
+
parentId,
|
|
10537
|
+
index: !!route.index,
|
|
10538
|
+
isLayout,
|
|
10539
|
+
componentName: routeComponentName(route),
|
|
10540
|
+
guardEvidence: [
|
|
10541
|
+
...own.map((g) => ({ ...g, source: "self" })),
|
|
10542
|
+
...inheritedGuards
|
|
10543
|
+
]
|
|
10329
10544
|
});
|
|
10330
10545
|
}
|
|
10331
10546
|
if (route.children) {
|
|
10332
|
-
|
|
10547
|
+
const forChildren = [
|
|
10548
|
+
...own.map(
|
|
10549
|
+
(g) => ({
|
|
10550
|
+
...g,
|
|
10551
|
+
source: "ancestor",
|
|
10552
|
+
...route.id !== void 0 ? { ancestorId: route.id } : {}
|
|
10553
|
+
})
|
|
10554
|
+
),
|
|
10555
|
+
...inheritedGuards
|
|
10556
|
+
];
|
|
10557
|
+
result.push(
|
|
10558
|
+
...extractAllRoutes(
|
|
10559
|
+
route.children,
|
|
10560
|
+
path,
|
|
10561
|
+
route.id ?? parentId,
|
|
10562
|
+
forChildren
|
|
10563
|
+
)
|
|
10564
|
+
);
|
|
10333
10565
|
}
|
|
10334
10566
|
}
|
|
10335
10567
|
return result;
|
|
10336
10568
|
}
|
|
10337
|
-
function broadcastRouteState(target, state,
|
|
10569
|
+
function broadcastRouteState(target, state, routes) {
|
|
10338
10570
|
try {
|
|
10339
10571
|
const message = {
|
|
10340
10572
|
type: "ARCHIE_ROUTE_UPDATE",
|
|
@@ -10342,12 +10574,18 @@ function broadcastRouteState(target, state, allRoutes) {
|
|
|
10342
10574
|
path: state.location.pathname,
|
|
10343
10575
|
search: state.location.search,
|
|
10344
10576
|
hash: state.location.hash,
|
|
10345
|
-
matches: state.matches.map((m) =>
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10577
|
+
matches: state.matches.map((m) => {
|
|
10578
|
+
const handle = pickRouteHandle(m.route.handle);
|
|
10579
|
+
return {
|
|
10580
|
+
id: m.route.id,
|
|
10581
|
+
pathname: m.pathname,
|
|
10582
|
+
params: m.params,
|
|
10583
|
+
...handle ? { handle } : {}
|
|
10584
|
+
};
|
|
10585
|
+
}),
|
|
10586
|
+
// Layouts stay out of allRoutes so consumers that predate them don't list duplicates.
|
|
10587
|
+
allRoutes: routes.filter((r) => !r.isLayout),
|
|
10588
|
+
layoutRoutes: routes.filter((r) => r.isLayout),
|
|
10351
10589
|
timestamp: Date.now()
|
|
10352
10590
|
}
|
|
10353
10591
|
};
|
|
@@ -10355,21 +10593,174 @@ function broadcastRouteState(target, state, allRoutes) {
|
|
|
10355
10593
|
} catch {
|
|
10356
10594
|
}
|
|
10357
10595
|
}
|
|
10358
|
-
|
|
10596
|
+
var SETTLE_QUIET_MS = 400;
|
|
10597
|
+
var SETTLE_TIMEOUT_MS = 8e3;
|
|
10598
|
+
var SETTLE_POLL_MS = 50;
|
|
10599
|
+
function readPackageVersion() {
|
|
10600
|
+
return true ? "0.1.14" : "unknown";
|
|
10601
|
+
}
|
|
10602
|
+
var DEFAULT_ARCHIE_COMMAND_ORIGINS = getAllowedOrigins(true);
|
|
10603
|
+
function originPatternToRegExp(pattern) {
|
|
10604
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/:\*$/, ":\\d+").replace(/\*/g, "[a-z0-9.-]+");
|
|
10605
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
10606
|
+
}
|
|
10607
|
+
function isAllowedCommandOrigin(origin, allowed) {
|
|
10608
|
+
return allowed.some((pattern) => originPatternToRegExp(pattern).test(origin));
|
|
10609
|
+
}
|
|
10610
|
+
function postToTarget(target, message, targetOrigin = "*") {
|
|
10611
|
+
try {
|
|
10612
|
+
target.postMessage(message, targetOrigin);
|
|
10613
|
+
} catch {
|
|
10614
|
+
}
|
|
10615
|
+
}
|
|
10616
|
+
function isRouterBusy(router) {
|
|
10617
|
+
const { navigation, fetchers } = router.state;
|
|
10618
|
+
if (navigation && navigation.state !== "idle") return true;
|
|
10619
|
+
if (fetchers) {
|
|
10620
|
+
for (const fetcher of fetchers.values()) {
|
|
10621
|
+
if (fetcher.state !== "idle") return true;
|
|
10622
|
+
}
|
|
10623
|
+
}
|
|
10624
|
+
return false;
|
|
10625
|
+
}
|
|
10626
|
+
function waitForSettled(router, navigation) {
|
|
10627
|
+
return new Promise((resolve) => {
|
|
10628
|
+
const startedAt = Date.now();
|
|
10629
|
+
let lastMutationAt = Date.now();
|
|
10630
|
+
let navigationDone = false;
|
|
10631
|
+
const observer = new MutationObserver(() => {
|
|
10632
|
+
lastMutationAt = Date.now();
|
|
10633
|
+
});
|
|
10634
|
+
observer.observe(document, {
|
|
10635
|
+
subtree: true,
|
|
10636
|
+
childList: true,
|
|
10637
|
+
attributes: true,
|
|
10638
|
+
characterData: true
|
|
10639
|
+
});
|
|
10640
|
+
navigation.then(
|
|
10641
|
+
() => {
|
|
10642
|
+
navigationDone = true;
|
|
10643
|
+
},
|
|
10644
|
+
() => {
|
|
10645
|
+
navigationDone = true;
|
|
10646
|
+
}
|
|
10647
|
+
);
|
|
10648
|
+
const finish = (settled) => {
|
|
10649
|
+
clearInterval(timer);
|
|
10650
|
+
observer.disconnect();
|
|
10651
|
+
resolve(settled);
|
|
10652
|
+
};
|
|
10653
|
+
const timer = setInterval(() => {
|
|
10654
|
+
const now = Date.now();
|
|
10655
|
+
if (navigationDone && !isRouterBusy(router) && now - lastMutationAt >= SETTLE_QUIET_MS) {
|
|
10656
|
+
finish(true);
|
|
10657
|
+
} else if (now - startedAt >= SETTLE_TIMEOUT_MS) {
|
|
10658
|
+
finish(false);
|
|
10659
|
+
}
|
|
10660
|
+
}, SETTLE_POLL_MS);
|
|
10661
|
+
});
|
|
10662
|
+
}
|
|
10663
|
+
function pathnameOf(path) {
|
|
10664
|
+
return path.split(/[?#]/, 1)[0];
|
|
10665
|
+
}
|
|
10666
|
+
async function handleNavigate(router, target, targetOrigin, data) {
|
|
10667
|
+
if (typeof router.navigate !== "function") {
|
|
10668
|
+
const { location: location3 } = router.state;
|
|
10669
|
+
postToTarget(target, {
|
|
10670
|
+
type: "ARCHIE_NAVIGATE_RESULT",
|
|
10671
|
+
requestId: data.requestId,
|
|
10672
|
+
requestedPath: data.path,
|
|
10673
|
+
finalPath: location3.pathname + location3.search + location3.hash,
|
|
10674
|
+
matchedRouteIds: [],
|
|
10675
|
+
redirected: false,
|
|
10676
|
+
settled: false
|
|
10677
|
+
}, targetOrigin);
|
|
10678
|
+
return;
|
|
10679
|
+
}
|
|
10680
|
+
let navigation = Promise.resolve();
|
|
10681
|
+
try {
|
|
10682
|
+
navigation = Promise.resolve(
|
|
10683
|
+
router.navigate(data.path, { replace: data.replace })
|
|
10684
|
+
);
|
|
10685
|
+
} catch {
|
|
10686
|
+
navigation = Promise.resolve();
|
|
10687
|
+
}
|
|
10688
|
+
const settled = await waitForSettled(router, navigation);
|
|
10689
|
+
const { location: location2, matches } = router.state;
|
|
10690
|
+
postToTarget(target, {
|
|
10691
|
+
type: "ARCHIE_NAVIGATE_RESULT",
|
|
10692
|
+
requestId: data.requestId,
|
|
10693
|
+
requestedPath: data.path,
|
|
10694
|
+
finalPath: location2.pathname + location2.search + location2.hash,
|
|
10695
|
+
matchedRouteIds: matches.map((m) => m.route.id).filter((id) => typeof id === "string"),
|
|
10696
|
+
redirected: location2.pathname !== pathnameOf(data.path),
|
|
10697
|
+
settled
|
|
10698
|
+
}, targetOrigin);
|
|
10699
|
+
}
|
|
10700
|
+
async function handleSnapshot(target, targetOrigin, requestId) {
|
|
10701
|
+
try {
|
|
10702
|
+
const snapshot = await captureDomSnapshot();
|
|
10703
|
+
postToTarget(target, {
|
|
10704
|
+
type: "ARCHIE_SNAPSHOT_DOM_RESULT",
|
|
10705
|
+
requestId,
|
|
10706
|
+
...snapshot
|
|
10707
|
+
}, targetOrigin);
|
|
10708
|
+
} catch (error) {
|
|
10709
|
+
postToTarget(target, {
|
|
10710
|
+
type: "ARCHIE_SNAPSHOT_DOM_FAILED",
|
|
10711
|
+
requestId,
|
|
10712
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10713
|
+
}, targetOrigin);
|
|
10714
|
+
}
|
|
10715
|
+
}
|
|
10716
|
+
function broadcastCapabilities(router, target) {
|
|
10717
|
+
postToTarget(target, {
|
|
10718
|
+
type: "ARCHIE_CAPABILITIES",
|
|
10719
|
+
version: readPackageVersion(),
|
|
10720
|
+
capabilities: {
|
|
10721
|
+
navigate: typeof router.navigate === "function",
|
|
10722
|
+
snapshot: true,
|
|
10723
|
+
identityOverride: false
|
|
10724
|
+
}
|
|
10725
|
+
});
|
|
10726
|
+
}
|
|
10727
|
+
function setupArchieRouteListener(router, options = {}) {
|
|
10728
|
+
const allowedOrigins = options.allowedOrigins ?? DEFAULT_ARCHIE_COMMAND_ORIGINS;
|
|
10359
10729
|
const target = window.parent !== window ? window.parent : window.opener;
|
|
10360
10730
|
if (!target) {
|
|
10361
10731
|
return () => {
|
|
10362
10732
|
};
|
|
10363
10733
|
}
|
|
10364
10734
|
broadcastRouteState(target, router.state, extractAllRoutes(router.routes));
|
|
10735
|
+
broadcastCapabilities(router, target);
|
|
10365
10736
|
const unsubscribe = router.subscribe((state) => {
|
|
10366
10737
|
const currentRoutes = extractAllRoutes(router.routes);
|
|
10367
10738
|
broadcastRouteState(target, state, currentRoutes);
|
|
10368
10739
|
});
|
|
10369
10740
|
const handleMessage = (event) => {
|
|
10370
|
-
if (event.
|
|
10371
|
-
|
|
10372
|
-
|
|
10741
|
+
if (event.source !== target) return;
|
|
10742
|
+
const data = event.data;
|
|
10743
|
+
switch (data?.type) {
|
|
10744
|
+
case "ARCHIE_QUERY_ROUTES":
|
|
10745
|
+
broadcastRouteState(
|
|
10746
|
+
target,
|
|
10747
|
+
router.state,
|
|
10748
|
+
extractAllRoutes(router.routes)
|
|
10749
|
+
);
|
|
10750
|
+
break;
|
|
10751
|
+
case "ARCHIE_QUERY_CAPABILITIES":
|
|
10752
|
+
broadcastCapabilities(router, target);
|
|
10753
|
+
break;
|
|
10754
|
+
case "ARCHIE_NAVIGATE":
|
|
10755
|
+
if (isAllowedCommandOrigin(event.origin, allowedOrigins) && typeof data.requestId === "string" && typeof data.path === "string") {
|
|
10756
|
+
void handleNavigate(router, target, event.origin, data);
|
|
10757
|
+
}
|
|
10758
|
+
break;
|
|
10759
|
+
case "ARCHIE_SNAPSHOT_DOM":
|
|
10760
|
+
if (isAllowedCommandOrigin(event.origin, allowedOrigins) && typeof data.requestId === "string") {
|
|
10761
|
+
void handleSnapshot(target, event.origin, data.requestId);
|
|
10762
|
+
}
|
|
10763
|
+
break;
|
|
10373
10764
|
}
|
|
10374
10765
|
};
|
|
10375
10766
|
window.addEventListener("message", handleMessage);
|
|
@@ -10409,7 +10800,8 @@ function installDomMetaSanitizer() {
|
|
|
10409
10800
|
function useArchieDevToolsRuntime({
|
|
10410
10801
|
router,
|
|
10411
10802
|
inspector,
|
|
10412
|
-
inspectorOptions
|
|
10803
|
+
inspectorOptions,
|
|
10804
|
+
routeListenerOrigins
|
|
10413
10805
|
}) {
|
|
10414
10806
|
installDomMetaSanitizer();
|
|
10415
10807
|
const stableOptionsKey = JSON.stringify(inspectorOptions ?? null);
|
|
@@ -10419,7 +10811,9 @@ function useArchieDevToolsRuntime({
|
|
|
10419
10811
|
if (process.env.NODE_ENV !== "development") {
|
|
10420
10812
|
return;
|
|
10421
10813
|
}
|
|
10422
|
-
const unsubscribe = setupArchieRouteListener(router
|
|
10814
|
+
const unsubscribe = setupArchieRouteListener(router, {
|
|
10815
|
+
allowedOrigins: routeListenerOrigins
|
|
10816
|
+
});
|
|
10423
10817
|
if (inspector !== false) {
|
|
10424
10818
|
const useLocalInspector = readArchieLocalInspectorFlag();
|
|
10425
10819
|
injectInspector({
|
|
@@ -10437,9 +10831,15 @@ function ArchieDevToolProvider({
|
|
|
10437
10831
|
children,
|
|
10438
10832
|
router,
|
|
10439
10833
|
inspector,
|
|
10440
|
-
inspectorOptions
|
|
10834
|
+
inspectorOptions,
|
|
10835
|
+
routeListenerOrigins
|
|
10441
10836
|
}) {
|
|
10442
|
-
useArchieDevToolsRuntime({
|
|
10837
|
+
useArchieDevToolsRuntime({
|
|
10838
|
+
router,
|
|
10839
|
+
inspector,
|
|
10840
|
+
inspectorOptions,
|
|
10841
|
+
routeListenerOrigins
|
|
10842
|
+
});
|
|
10443
10843
|
return /* @__PURE__ */ jsx6(DndProvider, { children });
|
|
10444
10844
|
}
|
|
10445
10845
|
export {
|
package/package.json
CHANGED
|
File without changes
|