@dimina-kit/devtools 0.4.0-dev.20260616102751 → 0.4.0-dev.20260624040247
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 +30 -25
- package/dist/main/app/app.js +14 -2
- package/dist/main/index.bundle.js +559 -111
- package/dist/main/ipc/bridge-router.js +57 -9
- package/dist/main/ipc/projects.js +1 -1
- package/dist/main/ipc/simulator.js +1 -1
- package/dist/main/runtime/devtools-backend.d.ts +1 -1
- package/dist/main/runtime/devtools-backend.js +1 -1
- package/dist/main/runtime/miniapp-runtime.d.ts +5 -5
- package/dist/main/services/console-forward/index.js +8 -1
- package/dist/main/services/elements-forward/index.js +36 -12
- package/dist/main/services/layout/index.d.ts +12 -0
- package/dist/main/services/layout/index.js +14 -2
- package/dist/main/services/network-forward/index.d.ts +5 -6
- package/dist/main/services/network-forward/index.js +9 -10
- package/dist/main/services/notifications/renderer-notifier.d.ts +3 -4
- package/dist/main/services/projects/index.js +1 -1
- package/dist/main/services/projects/types.d.ts +1 -1
- package/dist/main/services/projects/types.js +1 -1
- package/dist/main/services/service-console/console-api.d.ts +77 -0
- package/dist/main/services/service-console/console-api.js +103 -0
- package/dist/main/services/service-console/index.d.ts +47 -0
- package/dist/main/services/service-console/index.js +139 -0
- package/dist/main/services/service-host-pool/pool.d.ts +2 -3
- package/dist/main/services/simulator-storage/index.d.ts +2 -4
- package/dist/main/services/simulator-storage/index.js +2 -4
- package/dist/main/services/simulator-temp-files/index.js +2 -2
- package/dist/main/services/update/update-manager.d.ts +1 -1
- package/dist/main/services/update/update-manager.js +1 -1
- package/dist/main/services/views/host-toolbar-session-runtime.d.ts +4 -4
- package/dist/main/services/views/host-toolbar-session-runtime.js +4 -4
- package/dist/main/services/views/view-manager.d.ts +24 -1
- package/dist/main/services/views/view-manager.js +120 -41
- package/dist/main/services/workbench-context.d.ts +7 -6
- package/dist/main/services/workspace/workspace-service.js +11 -0
- package/dist/main/windows/service-host-window/create.d.ts +1 -0
- package/dist/main/windows/service-host-window/create.js +1 -0
- package/dist/preload/runtime/host-toolbar-port.d.ts +1 -1
- package/dist/preload/runtime/host-toolbar-port.js +1 -1
- package/dist/preload/runtime/host-toolbar-runtime.d.ts +8 -7
- package/dist/preload/runtime/host-toolbar-runtime.js +8 -7
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs.map +2 -2
- package/dist/preload/windows/simulator.cjs +1 -1
- package/dist/preload/windows/simulator.cjs.map +1 -1
- package/dist/preload/windows/simulator.js +1 -1
- package/dist/renderer/assets/index-PtsdWlGu.js +50 -0
- package/dist/renderer/assets/{input-B8NDCYv3.js → input-DiCxYlqY.js} +2 -2
- package/dist/renderer/assets/{ipc-transport-DhrajiC5.js → ipc-transport-DSBmaWpJ.js} +1 -1
- package/dist/renderer/assets/ipc-transport-DZxf2YoM.css +1 -0
- package/dist/renderer/assets/{popover-DbZOg03s.js → popover-CyL_uffj.js} +2 -2
- package/dist/renderer/assets/{select-pW3Ysc9T.js → select-BSwvDMtA.js} +2 -2
- package/dist/renderer/assets/settings-Ch8XbFxf.js +2 -0
- package/dist/renderer/assets/{settings-api-BSKzKiWd.js → settings-api-e9ILe2JO.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-BGxjl25x.js → workbenchSettings-DZsHf1g_.js} +2 -2
- package/dist/renderer/entries/main/index.html +6 -6
- package/dist/renderer/entries/popover/index.html +5 -5
- package/dist/renderer/entries/settings/index.html +11 -5
- package/dist/renderer/entries/workbench-settings/index.html +4 -4
- package/dist/service-host/preload.cjs +13 -15
- package/dist/service-host/sourcemap-rewrite.cjs +21 -12
- package/dist/shared/appdata-accumulator.d.ts +2 -2
- package/dist/shared/appdata-accumulator.js +3 -3
- package/dist/shared/constants.d.ts +1 -1
- package/dist/shared/constants.js +1 -1
- package/dist/shared/ipc-channels.d.ts +3 -3
- package/dist/shared/ipc-channels.js +3 -3
- package/dist/shared/open-in-editor.d.ts +20 -1
- package/dist/shared/open-in-editor.js +246 -3
- package/dist/shared/vpath.d.ts +2 -2
- package/dist/shared/vpath.js +2 -2
- package/dist/simulator/assets/{device-shell-BWX7Yopg.js → device-shell-TtcSXwhb.js} +2 -2
- package/dist/simulator/assets/{simulator-sf-D0mhw.js → simulator-CtyXt_4V.js} +3 -3
- package/dist/simulator/simulator.html +1 -1
- package/package.json +8 -6
- package/dist/renderer/assets/index-Z4Rh52G8.js +0 -50
- package/dist/renderer/assets/ipc-transport-9agi76dX.css +0 -1
- package/dist/renderer/assets/settings-CZJOHt8b.js +0 -2
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
<!-- See main/index.html for CSP rationale. -->
|
|
6
6
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http: https:; font-src 'self' data:; media-src 'self' blob: http: https:; connect-src 'self' http: https: ws: wss:;" />
|
|
7
7
|
<title>Settings</title>
|
|
8
|
-
<
|
|
8
|
+
<style>
|
|
9
|
+
/* The view spans the full content area as a transparent backdrop (only the
|
|
10
|
+
panel is opaque) so a click outside the panel reaches the backdrop and
|
|
11
|
+
closes the overlay — mirrors the popover entry. */
|
|
12
|
+
html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; background: transparent !important; }
|
|
13
|
+
</style>
|
|
14
|
+
<script type="module" crossorigin src="../../assets/settings-Ch8XbFxf.js"></script>
|
|
9
15
|
<link rel="modulepreload" crossorigin href="../../assets/chunk-DPI0Y_ll.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="../../assets/ipc-transport-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="../../assets/input-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="../../assets/settings-api-
|
|
13
|
-
<link rel="stylesheet" crossorigin href="../../assets/ipc-transport-
|
|
16
|
+
<link rel="modulepreload" crossorigin href="../../assets/ipc-transport-DSBmaWpJ.js">
|
|
17
|
+
<link rel="modulepreload" crossorigin href="../../assets/input-DiCxYlqY.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="../../assets/settings-api-e9ILe2JO.js">
|
|
19
|
+
<link rel="stylesheet" crossorigin href="../../assets/ipc-transport-DZxf2YoM.css">
|
|
14
20
|
</head>
|
|
15
21
|
<body>
|
|
16
22
|
<div id="root"></div>
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
<!-- See entries/main/index.html for CSP rationale. -->
|
|
7
7
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http: https:; font-src 'self' data:; media-src 'self' blob: http: https:; connect-src 'self' http: https: ws: wss:;" />
|
|
8
8
|
<title>开发工具设置</title>
|
|
9
|
-
<script type="module" crossorigin src="../../assets/workbenchSettings-
|
|
9
|
+
<script type="module" crossorigin src="../../assets/workbenchSettings-DZsHf1g_.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="../../assets/chunk-DPI0Y_ll.js">
|
|
11
|
-
<link rel="modulepreload" crossorigin href="../../assets/ipc-transport-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="../../assets/settings-api-
|
|
11
|
+
<link rel="modulepreload" crossorigin href="../../assets/ipc-transport-DSBmaWpJ.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="../../assets/settings-api-e9ILe2JO.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="../../assets/constants-DI382mTP.js">
|
|
14
|
-
<link rel="stylesheet" crossorigin href="../../assets/ipc-transport-
|
|
14
|
+
<link rel="stylesheet" crossorigin href="../../assets/ipc-transport-DZxf2YoM.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
|
@@ -192,14 +192,21 @@ ipcRenderer.on(CHANNELS.TO_SERVICE, (_event, payload) => {
|
|
|
192
192
|
deliver(payload && payload.msg)
|
|
193
193
|
})
|
|
194
194
|
|
|
195
|
-
// ──
|
|
195
|
+
// ── Uncaught error capture (native-host) ────────────────────────────────────
|
|
196
196
|
// Placed AFTER the no-bridgeId early-return (pool warming) and AFTER
|
|
197
197
|
// DiminaServiceBridge is defined, so it only runs for a real spawn and can use
|
|
198
|
-
// the bridge.
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
198
|
+
// the bridge.
|
|
199
|
+
//
|
|
200
|
+
// NOTE: `console.*` is deliberately NOT patched here. A wrapper adds a stack
|
|
201
|
+
// frame, so the embedded Chrome DevTools (attached natively to this service
|
|
202
|
+
// host) would attribute every log to the wrapper line instead of the
|
|
203
|
+
// developer's source. Service-layer console output is captured in main via CDP
|
|
204
|
+
// `Runtime.consoleAPICalled` (services/service-console) — no extra frame, native
|
|
205
|
+
// attribution + sourcemaps preserved. Only WINDOW error / unhandledrejection
|
|
206
|
+
// events are posted here: they carry no console call site to preserve and CDP's
|
|
207
|
+
// `consoleAPICalled` does not report them. Each is forwarded to main as a
|
|
208
|
+
// `consoleLog` container message (source:'service') → bridge-router →
|
|
209
|
+
// ctx.guestConsole → automation `App.logAdded`.
|
|
203
210
|
|
|
204
211
|
function safeSerializeArg(val) {
|
|
205
212
|
if (val === null || val === undefined) return val
|
|
@@ -230,15 +237,6 @@ function emitConsoleLog(level, args) {
|
|
|
230
237
|
} catch (_) { /* never let console capture break the guest */ }
|
|
231
238
|
}
|
|
232
239
|
|
|
233
|
-
;['log', 'warn', 'error', 'info', 'debug'].forEach((level) => {
|
|
234
|
-
const original = typeof console[level] === 'function' ? console[level].bind(console) : null
|
|
235
|
-
if (!original) return
|
|
236
|
-
console[level] = (...args) => {
|
|
237
|
-
original(...args)
|
|
238
|
-
emitConsoleLog(level, args)
|
|
239
|
-
}
|
|
240
|
-
})
|
|
241
|
-
|
|
242
240
|
globalThis.addEventListener('error', (event) => {
|
|
243
241
|
emitConsoleLog('error', [{
|
|
244
242
|
message: event.message,
|
|
@@ -5,18 +5,27 @@
|
|
|
5
5
|
* ABSOLUTE URL resolved against the script's own fetch URL.
|
|
6
6
|
*
|
|
7
7
|
* Why this exists (native-host console file links):
|
|
8
|
-
* The
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* (
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
8
|
+
* The compiled `logic.js` ships a RELATIVE `//# sourceMappingURL=logic.js.map`
|
|
9
|
+
* (compiler/core/logic-compiler.js). Whoever injects it must give DevTools a
|
|
10
|
+
* resolvable base for that relative map, or DevTools resolves it against the
|
|
11
|
+
* service-host DOCUMENT (`file://…/service-host/service.html`) and 404s —
|
|
12
|
+
* console file:line links then point at the compiled bundle, not the
|
|
13
|
+
* developer's original source. Rewriting the directive to an absolute
|
|
14
|
+
* dev-server URL restores sourcemapped console frames + Sources links.
|
|
15
|
+
*
|
|
16
|
+
* Consumer: the `importScripts` shim in preload.cjs below (this file is COPIED,
|
|
17
|
+
* not bundled, so a plain relative `require('./sourcemap-rewrite.cjs')` works).
|
|
18
|
+
* The main process needs the same rewrite in `bridge-router.injectLogicBundle`
|
|
19
|
+
* but keeps its OWN inlined copy there — the shipped main entry is the flat
|
|
20
|
+
* esbuild bundle and a runtime `require` of this relative path resolved wrong
|
|
21
|
+
* against the bundle location (crashed the packaged app). KEEP THE TWO IN SYNC.
|
|
22
|
+
*
|
|
23
|
+
* Legacy consumer (web-worker render only): the `importScripts` shim in
|
|
24
|
+
* preload.cjs (synchronous XHR + `(0, eval)(...)`). Under native-host the
|
|
25
|
+
* service host is a BrowserWindow, not a Web Worker (`isWebWorker` false), so
|
|
26
|
+
* that shim never runs — kept for the worker render path. A real
|
|
27
|
+
* `importScripts(url)` would base the relative map on the script URL itself,
|
|
28
|
+
* but `(0, eval)(...)` does not, hence the same rewrite applies there too.
|
|
20
29
|
*
|
|
21
30
|
* Contract:
|
|
22
31
|
* - Operates on the LAST `//# sourceMappingURL=` (or legacy `//@`) directive
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Default (dimina-fe) path: the simulator preload sniffs these as Worker
|
|
12
12
|
* `message` events (app-data.ts). Native-host path: they flow service→render
|
|
13
13
|
* through bridge-router (SERVICE_PUBLISH). Both feed this one accumulator so the
|
|
14
|
-
* decode/merge/page-only/
|
|
14
|
+
* decode/merge/page-only/init-gate policy can't drift between modes.
|
|
15
15
|
*/
|
|
16
16
|
/**
|
|
17
17
|
* The AppData snapshot — the full, cumulative panel state.
|
|
@@ -74,7 +74,7 @@ export declare class AppDataAccumulator {
|
|
|
74
74
|
private recordBridge;
|
|
75
75
|
/**
|
|
76
76
|
* Apply one entry. Returns true if it was accepted (a mutation worth
|
|
77
|
-
* republishing), false if dropped (missing ids, or the
|
|
77
|
+
* republishing), false if dropped (missing ids, or the init-gate).
|
|
78
78
|
*/
|
|
79
79
|
apply(input: AppDataInput): boolean;
|
|
80
80
|
/** Evict every entry for a bridge (page teardown). */
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Default (dimina-fe) path: the simulator preload sniffs these as Worker
|
|
12
12
|
* `message` events (app-data.ts). Native-host path: they flow service→render
|
|
13
13
|
* through bridge-router (SERVICE_PUBLISH). Both feed this one accumulator so the
|
|
14
|
-
* decode/merge/page-only/
|
|
14
|
+
* decode/merge/page-only/init-gate policy can't drift between modes.
|
|
15
15
|
*/
|
|
16
16
|
/**
|
|
17
17
|
* Decode a service→render message into AppData entries, or null when it is not
|
|
@@ -116,12 +116,12 @@ export class AppDataAccumulator {
|
|
|
116
116
|
}
|
|
117
117
|
/**
|
|
118
118
|
* Apply one entry. Returns true if it was accepted (a mutation worth
|
|
119
|
-
* republishing), false if dropped (missing ids, or the
|
|
119
|
+
* republishing), false if dropped (missing ids, or the init-gate).
|
|
120
120
|
*/
|
|
121
121
|
apply(input) {
|
|
122
122
|
if (!input.bridgeId || !input.moduleId)
|
|
123
123
|
return false;
|
|
124
|
-
//
|
|
124
|
+
// Drop ub patches whose bridge has never been initialised. dimina
|
|
125
125
|
// dispatches pageUnload → onUnload, whose setData produces a late `ub`
|
|
126
126
|
// arriving AFTER clearBridge; without this gate it would resurrect the
|
|
127
127
|
// unloaded bridge as a ghost tab.
|
|
@@ -18,7 +18,7 @@ export declare const HEADER_H = 40;
|
|
|
18
18
|
* defaultSession renderer; its guard activates the height advertiser only when
|
|
19
19
|
* `process.argv` carries this marker AND `process.isMainFrame` is true (the
|
|
20
20
|
* marker is process-level, so subframes of the toolbar window see it too —
|
|
21
|
-
* both guard wings are required
|
|
21
|
+
* both guard wings are required).
|
|
22
22
|
*/
|
|
23
23
|
export declare const HOST_TOOLBAR_RUNTIME_MARKER = "--dimina-host-toolbar";
|
|
24
24
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/shared/constants.js
CHANGED
|
@@ -18,7 +18,7 @@ export const HEADER_H = 40;
|
|
|
18
18
|
* defaultSession renderer; its guard activates the height advertiser only when
|
|
19
19
|
* `process.argv` carries this marker AND `process.isMainFrame` is true (the
|
|
20
20
|
* marker is process-level, so subframes of the toolbar window see it too —
|
|
21
|
-
* both guard wings are required
|
|
21
|
+
* both guard wings are required).
|
|
22
22
|
*/
|
|
23
23
|
export const HOST_TOOLBAR_RUNTIME_MARKER = '--dimina-host-toolbar';
|
|
24
24
|
//# sourceMappingURL=constants.js.map
|
|
@@ -174,11 +174,11 @@ export declare const ProjectsChannel: {
|
|
|
174
174
|
readonly List: "projects:list";
|
|
175
175
|
readonly Add: "projects:add";
|
|
176
176
|
readonly Remove: "projects:remove";
|
|
177
|
-
/**
|
|
177
|
+
/** Merged + sanitized template catalog for the create-project dialog. */
|
|
178
178
|
readonly ListTemplates: "projects:listTemplates";
|
|
179
|
-
/**
|
|
179
|
+
/** Host-supplied create-project dialog hook (returns input or null). */
|
|
180
180
|
readonly OpenCreateDialog: "projects:openCreateDialog";
|
|
181
|
-
/**
|
|
181
|
+
/** Server-side scaffold + register (delegates to create-project-service). */
|
|
182
182
|
readonly Create: "projects:create";
|
|
183
183
|
/** Default values used to pre-fill the create-project dialog (baseDir). */
|
|
184
184
|
readonly GetCreateDefaults: "projects:getCreateDefaults";
|
|
@@ -161,11 +161,11 @@ export const ProjectsChannel = {
|
|
|
161
161
|
List: 'projects:list',
|
|
162
162
|
Add: 'projects:add',
|
|
163
163
|
Remove: 'projects:remove',
|
|
164
|
-
/**
|
|
164
|
+
/** Merged + sanitized template catalog for the create-project dialog. */
|
|
165
165
|
ListTemplates: 'projects:listTemplates',
|
|
166
|
-
/**
|
|
166
|
+
/** Host-supplied create-project dialog hook (returns input or null). */
|
|
167
167
|
OpenCreateDialog: 'projects:openCreateDialog',
|
|
168
|
-
/**
|
|
168
|
+
/** Server-side scaffold + register (delegates to create-project-service). */
|
|
169
169
|
Create: 'projects:create',
|
|
170
170
|
/** Default values used to pre-fill the create-project dialog (baseDir). */
|
|
171
171
|
GetCreateDefaults: 'projects:getCreateDefaults',
|
|
@@ -32,6 +32,15 @@ export interface OpenInEditorRequest {
|
|
|
32
32
|
/** 0-based column as reported by DevTools (may be undefined). */
|
|
33
33
|
column?: number;
|
|
34
34
|
}
|
|
35
|
+
export interface ProjectSourceContext {
|
|
36
|
+
/** Absolute project root on disk. */
|
|
37
|
+
projectRoot: string;
|
|
38
|
+
/** Resource server base URL used by the compiled miniapp. */
|
|
39
|
+
resourceBaseUrl: string;
|
|
40
|
+
appId: string;
|
|
41
|
+
/** Compiler output package root (`main` for the primary package). */
|
|
42
|
+
outputRoot: string;
|
|
43
|
+
}
|
|
35
44
|
/**
|
|
36
45
|
* Encode an open-in-editor request as a sentinel URL the DevTools front-end can
|
|
37
46
|
* hand to `InspectorFrontendHost.openInNewTab(...)`. All fields ride in the
|
|
@@ -67,5 +76,15 @@ export declare function decodeOpenInEditorUrl(raw: string): OpenInEditorRequest
|
|
|
67
76
|
* @returns the project-relative POSIX path, or null when the URL is not a
|
|
68
77
|
* mappable dev-server source.
|
|
69
78
|
*/
|
|
70
|
-
export declare function resourceUrlToProjectRelativePath(resourceUrl: string,
|
|
79
|
+
export declare function resourceUrlToProjectRelativePath(resourceUrl: string, expectedOriginOrContext?: string | ProjectSourceContext): string | null;
|
|
80
|
+
/** Read the project/source routing metadata carried by a service-host spawn URL. */
|
|
81
|
+
export declare function projectSourceContextFromServiceHostUrl(serviceHostUrl: string, activeProjectRoot?: string): ProjectSourceContext | null;
|
|
82
|
+
/**
|
|
83
|
+
* Build the DevTools-front-end glue for project source links.
|
|
84
|
+
*
|
|
85
|
+
* It keeps the existing `setOpenResourceHandler` transport, but only forwards
|
|
86
|
+
* locations that map to the active project. A DOM observer expands Chromium's
|
|
87
|
+
* basename-only console labels to project-relative paths.
|
|
88
|
+
*/
|
|
89
|
+
export declare function buildDevtoolsProjectSourceLinksScript(context: ProjectSourceContext): string;
|
|
71
90
|
//# sourceMappingURL=open-in-editor.d.ts.map
|
|
@@ -96,9 +96,12 @@ export function decodeOpenInEditorUrl(raw) {
|
|
|
96
96
|
* @returns the project-relative POSIX path, or null when the URL is not a
|
|
97
97
|
* mappable dev-server source.
|
|
98
98
|
*/
|
|
99
|
-
export function resourceUrlToProjectRelativePath(resourceUrl,
|
|
99
|
+
export function resourceUrlToProjectRelativePath(resourceUrl, expectedOriginOrContext) {
|
|
100
100
|
if (typeof resourceUrl !== 'string' || !resourceUrl)
|
|
101
101
|
return null;
|
|
102
|
+
if (expectedOriginOrContext && typeof expectedOriginOrContext === 'object') {
|
|
103
|
+
return projectAwareResourcePath(resourceUrl, expectedOriginOrContext);
|
|
104
|
+
}
|
|
102
105
|
let resource;
|
|
103
106
|
try {
|
|
104
107
|
resource = new URL(resourceUrl);
|
|
@@ -110,10 +113,10 @@ export function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
|
|
|
110
113
|
// `webpack://`, `node:`, `data:` are framework/runtime frames with no file.
|
|
111
114
|
if (resource.protocol !== 'http:' && resource.protocol !== 'https:')
|
|
112
115
|
return null;
|
|
113
|
-
if (
|
|
116
|
+
if (expectedOriginOrContext) {
|
|
114
117
|
let base;
|
|
115
118
|
try {
|
|
116
|
-
base = new URL(
|
|
119
|
+
base = new URL(expectedOriginOrContext);
|
|
117
120
|
}
|
|
118
121
|
catch {
|
|
119
122
|
return null;
|
|
@@ -135,4 +138,244 @@ export function resourceUrlToProjectRelativePath(resourceUrl, expectedOrigin) {
|
|
|
135
138
|
return null;
|
|
136
139
|
return segments.slice(1).join('/');
|
|
137
140
|
}
|
|
141
|
+
function decodePathname(pathname) {
|
|
142
|
+
return pathname.split('/').map((segment) => {
|
|
143
|
+
try {
|
|
144
|
+
return decodeURIComponent(segment);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return segment;
|
|
148
|
+
}
|
|
149
|
+
}).join('/');
|
|
150
|
+
}
|
|
151
|
+
function normalizeSlashPath(value) {
|
|
152
|
+
return decodePathname(value.replace(/\\/g, '/')).replace(/\/+/g, '/');
|
|
153
|
+
}
|
|
154
|
+
function safeRelativePath(segments) {
|
|
155
|
+
if (segments.length === 0 || segments.some((segment) => !segment || segment === '.' || segment === '..')) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return segments.join('/');
|
|
159
|
+
}
|
|
160
|
+
function projectAwareResourcePath(resourceUrl, context) {
|
|
161
|
+
let base;
|
|
162
|
+
try {
|
|
163
|
+
base = new URL(context.resourceBaseUrl);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
const projectRoot = normalizeSlashPath(context.projectRoot).replace(/\/$/, '');
|
|
169
|
+
const raw = resourceUrl.trim();
|
|
170
|
+
if (!raw)
|
|
171
|
+
return null;
|
|
172
|
+
// Chromium may hand the open-resource hook a raw absolute filesystem path.
|
|
173
|
+
const normalizedRaw = normalizeSlashPath(raw);
|
|
174
|
+
if (projectRoot && (normalizedRaw === projectRoot || normalizedRaw.startsWith(`${projectRoot}/`))) {
|
|
175
|
+
const relative = normalizedRaw.slice(projectRoot.length).replace(/^\/+/, '');
|
|
176
|
+
return safeRelativePath(relative.split('/').filter(Boolean));
|
|
177
|
+
}
|
|
178
|
+
// Sourcemap sources commonly use virtual root paths such as `/pages/x.js`.
|
|
179
|
+
// Real host filesystem paths use well-known absolute roots; when they are
|
|
180
|
+
// outside the active project they are runtime/devtools frames, not sources.
|
|
181
|
+
if (/^\/(?:Volumes|Users|home|private|tmp|var|opt|Applications)\//.test(normalizedRaw)) {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
// Reject relative runtime labels such as `service.js` or
|
|
185
|
+
// `electron/js2c/renderer_init`; project source locations must be an
|
|
186
|
+
// absolute URL/path so they can be scoped to the active project.
|
|
187
|
+
if (!raw.startsWith('/') && !/^[a-z][a-z0-9+.-]*:/i.test(raw))
|
|
188
|
+
return null;
|
|
189
|
+
let resource;
|
|
190
|
+
try {
|
|
191
|
+
resource = raw.startsWith('/') && !raw.startsWith('//')
|
|
192
|
+
? new URL(raw, base.origin)
|
|
193
|
+
: new URL(raw);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
if (resource.protocol === 'file:') {
|
|
199
|
+
const filePath = normalizeSlashPath(resource.pathname);
|
|
200
|
+
if (!projectRoot || (filePath !== projectRoot && !filePath.startsWith(`${projectRoot}/`)))
|
|
201
|
+
return null;
|
|
202
|
+
return safeRelativePath(filePath.slice(projectRoot.length).replace(/^\/+/, '').split('/').filter(Boolean));
|
|
203
|
+
}
|
|
204
|
+
if (resource.protocol !== 'http:' && resource.protocol !== 'https:')
|
|
205
|
+
return null;
|
|
206
|
+
if (resource.origin !== base.origin)
|
|
207
|
+
return null;
|
|
208
|
+
let segments = decodePathname(resource.pathname).split('/').filter(Boolean);
|
|
209
|
+
if (context.appId && segments[0] === context.appId) {
|
|
210
|
+
segments = segments.slice(1);
|
|
211
|
+
if (context.outputRoot && segments[0] === context.outputRoot)
|
|
212
|
+
segments = segments.slice(1);
|
|
213
|
+
}
|
|
214
|
+
return safeRelativePath(segments);
|
|
215
|
+
}
|
|
216
|
+
/** Read the project/source routing metadata carried by a service-host spawn URL. */
|
|
217
|
+
export function projectSourceContextFromServiceHostUrl(serviceHostUrl, activeProjectRoot) {
|
|
218
|
+
let url;
|
|
219
|
+
try {
|
|
220
|
+
url = new URL(serviceHostUrl);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const projectRoot = url.searchParams.get('pkgRoot') || '';
|
|
226
|
+
const resourceBaseUrl = url.searchParams.get('resourceBaseUrl') ?? '';
|
|
227
|
+
const appId = url.searchParams.get('appId') ?? '';
|
|
228
|
+
const outputRoot = url.searchParams.get('root') ?? 'main';
|
|
229
|
+
if (!projectRoot || !resourceBaseUrl || !appId)
|
|
230
|
+
return null;
|
|
231
|
+
if (activeProjectRoot) {
|
|
232
|
+
const authoritativeRoot = normalizeSlashPath(projectRoot).replace(/\/+$/, '');
|
|
233
|
+
const currentRoot = normalizeSlashPath(activeProjectRoot).replace(/\/+$/, '');
|
|
234
|
+
if (!authoritativeRoot || authoritativeRoot !== currentRoot)
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
new URL(resourceBaseUrl);
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
return { projectRoot, resourceBaseUrl, appId, outputRoot };
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Build the DevTools-front-end glue for project source links.
|
|
247
|
+
*
|
|
248
|
+
* It keeps the existing `setOpenResourceHandler` transport, but only forwards
|
|
249
|
+
* locations that map to the active project. A DOM observer expands Chromium's
|
|
250
|
+
* basename-only console labels to project-relative paths.
|
|
251
|
+
*/
|
|
252
|
+
export function buildDevtoolsProjectSourceLinksScript(context) {
|
|
253
|
+
const config = JSON.stringify(context);
|
|
254
|
+
const scheme = JSON.stringify(OPEN_IN_EDITOR_SCHEME);
|
|
255
|
+
return `
|
|
256
|
+
(function() {
|
|
257
|
+
try {
|
|
258
|
+
const stateKey = '__diminaProjectSourceLinksState__'
|
|
259
|
+
const previousState = globalThis[stateKey]
|
|
260
|
+
if (previousState && typeof previousState.dispose === 'function') {
|
|
261
|
+
try { previousState.dispose() } catch (_) {}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const cfg = ${config}
|
|
265
|
+
const diminaProjectSourcePath = ${projectAwareResourcePath.toString()}
|
|
266
|
+
const decodePathname = ${decodePathname.toString()}
|
|
267
|
+
const normalizeSlashPath = ${normalizeSlashPath.toString()}
|
|
268
|
+
const safeRelativePath = ${safeRelativePath.toString()}
|
|
269
|
+
const observedRoots = new Set()
|
|
270
|
+
const observers = []
|
|
271
|
+
let timer = null
|
|
272
|
+
const state = {
|
|
273
|
+
dispose() {
|
|
274
|
+
if (timer !== null) {
|
|
275
|
+
clearInterval(timer)
|
|
276
|
+
timer = null
|
|
277
|
+
}
|
|
278
|
+
for (const observer of observers.splice(0)) {
|
|
279
|
+
try { observer.disconnect() } catch (_) {}
|
|
280
|
+
}
|
|
281
|
+
observedRoots.clear()
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
globalThis[stateKey] = state
|
|
285
|
+
|
|
286
|
+
function splitLocation(value) {
|
|
287
|
+
const raw = String(value || '')
|
|
288
|
+
const match = raw.match(/^(.*?)(?::(\\d+))(?::(\\d+))?$/)
|
|
289
|
+
return match
|
|
290
|
+
? { url: match[1], suffix: ':' + match[2] + (match[3] ? ':' + match[3] : '') }
|
|
291
|
+
: { url: raw, suffix: '' }
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function rewriteLink(link) {
|
|
295
|
+
if (!link || link.dataset.diminaProjectSourcePath) return
|
|
296
|
+
const candidates = [
|
|
297
|
+
link.getAttribute && link.getAttribute('title'),
|
|
298
|
+
link.getAttribute && link.getAttribute('href'),
|
|
299
|
+
link.getAttribute && link.getAttribute('data-url'),
|
|
300
|
+
]
|
|
301
|
+
for (const candidate of candidates) {
|
|
302
|
+
if (!candidate) continue
|
|
303
|
+
const location = splitLocation(candidate)
|
|
304
|
+
const relative = diminaProjectSourcePath(location.url, cfg)
|
|
305
|
+
if (!relative) continue
|
|
306
|
+
const textSuffix = location.suffix || ((String(link.textContent || '').match(/(:\\d+(?::\\d+)?)$/) || [])[1] || '')
|
|
307
|
+
link.textContent = relative + textSuffix
|
|
308
|
+
link.dataset.diminaProjectSourcePath = relative
|
|
309
|
+
return
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function observeRoot(root) {
|
|
314
|
+
if (!root || !root.querySelectorAll || observedRoots.has(root)) return
|
|
315
|
+
observedRoots.add(root)
|
|
316
|
+
|
|
317
|
+
const observer = new MutationObserver((records) => {
|
|
318
|
+
for (const record of records) {
|
|
319
|
+
if (record.type === 'attributes') rewriteLink(record.target)
|
|
320
|
+
for (const node of record.addedNodes || []) visitNode(node)
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
observer.observe(root, {
|
|
324
|
+
subtree: true,
|
|
325
|
+
childList: true,
|
|
326
|
+
attributes: true,
|
|
327
|
+
attributeFilter: ['title', 'href', 'data-url'],
|
|
328
|
+
})
|
|
329
|
+
observers.push(observer)
|
|
330
|
+
|
|
331
|
+
for (const link of root.querySelectorAll('.devtools-link')) rewriteLink(link)
|
|
332
|
+
for (const element of root.querySelectorAll('*')) {
|
|
333
|
+
if (element.shadowRoot) observeRoot(element.shadowRoot)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function visitNode(node) {
|
|
338
|
+
if (!node) return
|
|
339
|
+
if (node.matches && node.matches('.devtools-link')) rewriteLink(node)
|
|
340
|
+
if (node.shadowRoot) observeRoot(node.shadowRoot)
|
|
341
|
+
if (!node.querySelectorAll) return
|
|
342
|
+
for (const link of node.querySelectorAll('.devtools-link')) rewriteLink(link)
|
|
343
|
+
for (const element of node.querySelectorAll('*')) {
|
|
344
|
+
if (element.shadowRoot) observeRoot(element.shadowRoot)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
observeRoot(document)
|
|
349
|
+
|
|
350
|
+
let tries = 0
|
|
351
|
+
timer = setInterval(() => {
|
|
352
|
+
tries++
|
|
353
|
+
try {
|
|
354
|
+
const Host = globalThis.Host
|
|
355
|
+
const host = Host && Host.InspectorFrontendHost
|
|
356
|
+
if (host && typeof host.setOpenResourceHandler === 'function'
|
|
357
|
+
&& typeof host.openInNewTab === 'function') {
|
|
358
|
+
host.setOpenResourceHandler((url, lineNumber, columnNumber) => {
|
|
359
|
+
try {
|
|
360
|
+
if (!diminaProjectSourcePath(String(url), cfg)) return
|
|
361
|
+
const p = new URLSearchParams()
|
|
362
|
+
p.set('u', String(url))
|
|
363
|
+
if (typeof lineNumber === 'number') p.set('l', String(lineNumber))
|
|
364
|
+
if (typeof columnNumber === 'number') p.set('c', String(columnNumber))
|
|
365
|
+
host.openInNewTab(${scheme} + ':?' + p.toString())
|
|
366
|
+
} catch (_) {}
|
|
367
|
+
})
|
|
368
|
+
clearInterval(timer)
|
|
369
|
+
timer = null
|
|
370
|
+
}
|
|
371
|
+
} catch (_) {}
|
|
372
|
+
if (tries > 80 && timer !== null) {
|
|
373
|
+
clearInterval(timer)
|
|
374
|
+
timer = null
|
|
375
|
+
}
|
|
376
|
+
}, 50)
|
|
377
|
+
} catch (_) {}
|
|
378
|
+
})()
|
|
379
|
+
`;
|
|
380
|
+
}
|
|
138
381
|
//# sourceMappingURL=open-in-editor.js.map
|
package/dist/shared/vpath.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* vpath resolver.
|
|
3
3
|
*
|
|
4
4
|
* Spec: `docs/file-system.md` §3.3 — single resolver shared by every FSM
|
|
5
|
-
* entry, the renderer-side temp store, and
|
|
5
|
+
* entry, the renderer-side temp store, and the main-process
|
|
6
6
|
* protocol.handle / disk reader.
|
|
7
7
|
*
|
|
8
8
|
* resolveVPath(url): { kind, writable, realPath? } | null
|
package/dist/shared/vpath.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* vpath resolver.
|
|
3
3
|
*
|
|
4
4
|
* Spec: `docs/file-system.md` §3.3 — single resolver shared by every FSM
|
|
5
|
-
* entry, the renderer-side temp store, and
|
|
5
|
+
* entry, the renderer-side temp store, and the main-process
|
|
6
6
|
* protocol.handle / disk reader.
|
|
7
7
|
*
|
|
8
8
|
* resolveVPath(url): { kind, writable, realPath? } | null
|