@dimina-kit/devtools 0.4.0-dev.20260907055341 → 0.5.0-dev.20260908120530

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/dist/main/app/window-runtime-services.d.ts +12 -12
  3. package/dist/main/app/window-runtime-services.js +34 -15
  4. package/dist/main/index.bundle.js +6307 -6050
  5. package/dist/main/services/elements-forward/index.d.ts +2 -2
  6. package/dist/main/services/elements-forward/index.js +6 -6
  7. package/dist/main/services/network-forward/body-cache.d.ts +2 -0
  8. package/dist/main/services/network-forward/body-cache.js +4 -0
  9. package/dist/main/services/network-forward/global-body-gate.d.ts +1 -1
  10. package/dist/main/services/network-forward/global-body-gate.js +4 -4
  11. package/dist/main/services/network-forward/http.d.ts +47 -0
  12. package/dist/main/services/network-forward/http.js +162 -0
  13. package/dist/main/services/network-forward/index.d.ts +19 -13
  14. package/dist/main/services/network-forward/index.js +220 -134
  15. package/dist/main/services/network-forward/request-ids.d.ts +5 -0
  16. package/dist/main/services/network-forward/request-ids.js +5 -0
  17. package/dist/main/services/safe-area/index.d.ts +7 -9
  18. package/dist/main/services/safe-area/index.js +7 -14
  19. package/dist/main/services/views/native-simulator-view.js +7 -8
  20. package/dist/main/services/workbench-context.d.ts +3 -3
  21. package/dist/main/services/workbench-context.js +7 -1
  22. package/dist/native-host/render/render.js +525 -482
  23. package/dist/native-host/service/service.js +2 -2
  24. package/dist/preload/shared/api-compat.js +63 -41
  25. package/dist/preload/windows/main.cjs +15 -1
  26. package/dist/preload/windows/main.cjs.map +2 -2
  27. package/dist/preload/windows/simulator.cjs +55 -134
  28. package/dist/preload/windows/simulator.cjs.map +4 -4
  29. package/dist/preload/windows/simulator.js +55 -134
  30. package/dist/renderer/assets/workbench-C0Jkdlps.js +5 -0
  31. package/dist/renderer/entries/workbench/index.html +1 -1
  32. package/dist/shared/types.d.ts +2 -0
  33. package/dist/simulator/assets/device-shell-DucLepkm.js +431 -0
  34. package/dist/simulator/assets/simulator-B4rbcOVt.js +10 -0
  35. package/dist/simulator/assets/simulator-ui-i3GCc7YJ.js +2 -0
  36. package/dist/simulator/simulator.html +2 -2
  37. package/package.json +12 -12
  38. package/dist/renderer/assets/workbench-BLJ0wKsg.js +0 -5
  39. package/dist/shared/request-core.d.ts +0 -2
  40. package/dist/shared/request-core.js +0 -2
  41. package/dist/simulator/assets/device-shell-pxnZZp10.js +0 -431
  42. package/dist/simulator/assets/simulator-Dwp7lnvu.js +0 -10
  43. package/dist/simulator/assets/simulator-ui-B2ddJB1R.js +0 -2
@@ -60,7 +60,7 @@
60
60
  import type { WebContents } from 'electron';
61
61
  import type { ConnectionRegistry } from '@dimina-kit/electron-deck/main';
62
62
  import type { BridgeRouterHandle } from '../../ipc/bridge-router.js';
63
- import { type NetworkBodyProvider } from '../network-forward/index.js';
63
+ import type { NetworkBodyProvider } from '../network-forward/index.js';
64
64
  import { type CdpSessionBroker } from '../cdp-session/index.js';
65
65
  /**
66
66
  * Which target a front-end CDP command is routed to.
@@ -108,7 +108,7 @@ export declare const NETWORK_BODY_METHODS: readonly string[];
108
108
  * equivalent test driven by the same literals, so the two cannot drift.
109
109
  *
110
110
  * A non-string / missing requestId routes to 'service': only the
111
- * `dimina:sim:` namespace is ours to answer, and the real backend is the
111
+ * simulator and native HTTP namespaces is ours to answer, and the real backend is the
112
112
  * correct authority for its own ids.
113
113
  */
114
114
  export declare function routeOutboundCommand(method: string, params: unknown): CdpRoute;
@@ -1,5 +1,5 @@
1
1
  import { isFrontendSettled } from '../views/inject-when-ready.js';
2
- import { VIRTUAL_REQUEST_ID_PREFIX } from '../network-forward/index.js';
2
+ import { BODY_REQUEST_ID_PREFIXES } from '../network-forward/request-ids.js';
3
3
  import { buildSingleDispatchScript, createFrontendReplyChannel, answerNetworkBodyCommand, drainOutboundBatch } from '../network-forward/frontend-dispatch.js';
4
4
  import { createCdpSessionBroker } from '../cdp-session/index.js';
5
5
  /**
@@ -56,7 +56,7 @@ export const NETWORK_BODY_METHODS = [
56
56
  * equivalent test driven by the same literals, so the two cannot drift.
57
57
  *
58
58
  * A non-string / missing requestId routes to 'service': only the
59
- * `dimina:sim:` namespace is ours to answer, and the real backend is the
59
+ * simulator and native HTTP namespaces is ours to answer, and the real backend is the
60
60
  * correct authority for its own ids.
61
61
  */
62
62
  export function routeOutboundCommand(method, params) {
@@ -64,7 +64,7 @@ export function routeOutboundCommand(method, params) {
64
64
  return 'render';
65
65
  if (NETWORK_BODY_METHODS.includes(method)) {
66
66
  const requestId = params?.requestId;
67
- if (typeof requestId === 'string' && requestId.startsWith(VIRTUAL_REQUEST_ID_PREFIX)) {
67
+ if (typeof requestId === 'string' && BODY_REQUEST_ID_PREFIXES.some((prefix) => requestId.startsWith(prefix))) {
68
68
  return 'network';
69
69
  }
70
70
  }
@@ -87,13 +87,13 @@ export function routeOutboundCommand(method, params) {
87
87
  export function buildElementsHookScript() {
88
88
  const prefixes = JSON.stringify(RENDER_DOMAIN_PREFIXES);
89
89
  const netMethods = JSON.stringify(NETWORK_BODY_METHODS);
90
- const vprefix = JSON.stringify(VIRTUAL_REQUEST_ID_PREFIX);
90
+ const vprefix = JSON.stringify(BODY_REQUEST_ID_PREFIXES);
91
91
  return `(function(){try{
92
92
  if (globalThis.__diminaElementsHookInstalled) return 'already';
93
93
  var OUT = (globalThis.__diminaElementsOutbound = globalThis.__diminaElementsOutbound || []);
94
94
  var PREFIXES = ${prefixes};
95
95
  var NET_METHODS = ${netMethods};
96
- var VPREFIX = ${vprefix};
96
+ var VPREFIXES = ${vprefix};
97
97
  function isRender(method){
98
98
  if (!method) return false;
99
99
  for (var i=0;i<PREFIXES.length;i++){ if (method.indexOf(PREFIXES[i])===0) return true; }
@@ -104,7 +104,7 @@ export function buildElementsHookScript() {
104
104
  if (isRender(m.method)) return 'render';
105
105
  if (NET_METHODS.indexOf(m.method) >= 0 && m.params
106
106
  && typeof m.params.requestId === 'string'
107
- && m.params.requestId.indexOf(VPREFIX) === 0) return 'network';
107
+ && VPREFIXES.some(function(prefix){ return m.params.requestId.indexOf(prefix) === 0; })) return 'network';
108
108
  return 'service';
109
109
  }
110
110
  var IFH = globalThis.InspectorFrontendHost;
@@ -60,6 +60,8 @@ export declare class PrefetchCache<V> {
60
60
  */
61
61
  prime(id: string, fetch: () => Promise<V>): boolean;
62
62
  lookup(id: string): Promise<V>;
63
+ /** Retire a previous redirect hop without disturbing unrelated cached requests. */
64
+ delete(id: string): void;
63
65
  clear(): void;
64
66
  get size(): number;
65
67
  private isExpired;
@@ -134,6 +134,10 @@ export class PrefetchCache {
134
134
  this.map.set(id, e);
135
135
  return Promise.resolve(v);
136
136
  }
137
+ /** Retire a previous redirect hop without disturbing unrelated cached requests. */
138
+ delete(id) {
139
+ this.map.delete(id);
140
+ }
137
141
  clear() {
138
142
  this.map.clear();
139
143
  }
@@ -12,7 +12,7 @@
12
12
  * Installing the full elements-forward gate here would silently reroute the
13
13
  * global window's Elements panel to the wrong target. This module intercepts
14
14
  * ONLY `Network.getResponseBody` / `Network.getRequestPostData` for
15
- * `dimina:sim:` virtual requestIds; every other command (including all
15
+ * `dimina:sim:` and `dimina:http:` virtual requestIds; every other command (including all
16
16
  * DOM/CSS/Overlay/Runtime/Debugger/…) passes straight through untouched.
17
17
  *
18
18
  * ── Mechanism (same two-way poll bridge as elements-forward) ───────────────
@@ -1,5 +1,5 @@
1
1
  import { isFrontendSettled } from '../views/inject-when-ready.js';
2
- import { VIRTUAL_REQUEST_ID_PREFIX } from './index.js';
2
+ import { BODY_REQUEST_ID_PREFIXES } from './request-ids.js';
3
3
  import { createFrontendReplyChannel, answerNetworkBodyCommand, drainOutboundBatch } from './frontend-dispatch.js';
4
4
  /** The only two Network.* commands this gate ever intercepts. */
5
5
  const NETWORK_BODY_METHODS = ['Network.getResponseBody', 'Network.getRequestPostData'];
@@ -12,16 +12,16 @@ const DRAIN_INTERVAL_MS = 150;
12
12
  */
13
13
  export function buildNetworkOnlyHookScript() {
14
14
  const netMethods = JSON.stringify(NETWORK_BODY_METHODS);
15
- const vprefix = JSON.stringify(VIRTUAL_REQUEST_ID_PREFIX);
15
+ const vprefix = JSON.stringify(BODY_REQUEST_ID_PREFIXES);
16
16
  return `(function(){try{
17
17
  if (globalThis.__diminaGlobalNetworkHookInstalled) return 'already';
18
18
  var OUT = (globalThis.__diminaGlobalNetworkOutbound = globalThis.__diminaGlobalNetworkOutbound || []);
19
19
  var NET_METHODS = ${netMethods};
20
- var VPREFIX = ${vprefix};
20
+ var VPREFIXES = ${vprefix};
21
21
  function isNetworkBody(m){
22
22
  if (!m || !m.method) return false;
23
23
  if (NET_METHODS.indexOf(m.method) < 0) return false;
24
- return !!(m.params && typeof m.params.requestId === 'string' && m.params.requestId.indexOf(VPREFIX) === 0);
24
+ return !!(m.params && typeof m.params.requestId === 'string' && VPREFIXES.some(function(prefix){ return m.params.requestId.indexOf(prefix) === 0; }));
25
25
  }
26
26
  var IFH = globalThis.InspectorFrontendHost;
27
27
  if (IFH && typeof IFH.sendMessageToBackend === 'function' && !IFH.__diminaGlobalNetworkWrapped){
@@ -0,0 +1,47 @@
1
+ import type { NativeRequestTrace } from "../../ipc/bridge-router.js";
2
+ /** One CDP message ready for `DevToolsAPI.dispatchMessage` injection. */
3
+ export interface SynthesizedRequestMessage {
4
+ method: string;
5
+ params: unknown;
6
+ /**
7
+ * The verdict `isUserFacingRequest` produced for this request's url at
8
+ * `sent` time, cached for the request's whole lifetime — every later event
9
+ * reuses it (they carry no url of their own). The user-facing sink gates
10
+ * on this; the global mirror ignores it.
11
+ */
12
+ userFacing: boolean;
13
+ /** Response body ready to prime the forwarder's body-cache, present only
14
+ * on the message synthesized from a `finished` trace event. */
15
+ body?: {
16
+ base64Encoded: boolean;
17
+ body: string;
18
+ };
19
+ /** Request post data ready to prime the forwarder's post-data cache,
20
+ * present only on the message synthesized from the `sent` trace event when
21
+ * the request carried a body. */
22
+ postData?: string;
23
+ }
24
+ export interface RequestTraceSynthesizerOptions {
25
+ /** Per-forwarder instance tag keeping virtual ids collision-free. */
26
+ epoch: string;
27
+ /**
28
+ * Origins the app itself serves (resource server / simulator shell), the
29
+ * same inputs `resolveUserFacing` feeds `isUserFacingRequest` for
30
+ * simulator-captured HTTP traffic. Re-read at each initial `sent`; redirects retain that verdict.
31
+ */
32
+ internalOrigins?: () => ReadonlyArray<string | null | undefined>;
33
+ }
34
+ export declare class RequestTraceSynthesizer {
35
+ private readonly options;
36
+ private readonly requests;
37
+ private seq;
38
+ constructor(options: RequestTraceSynthesizerOptions);
39
+ /**
40
+ * Map one trace event to its CDP message, or null when the event must be
41
+ * dropped (a non-`sent` event for a requestId this synthesizer never saw
42
+ * sent — defensive against out-of-order delivery).
43
+ */
44
+ synthesize(sessionId: string, event: NativeRequestTrace): SynthesizedRequestMessage | null;
45
+ private synthesizeRequest;
46
+ }
47
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Synthesize Chrome DevTools Protocol `Network.request*`/`Network.loading*`
3
+ * messages from the main-process HTTP request trace stream (see
4
+ * electron-runtime `native-request/trace.ts`).
5
+ *
6
+ * Why this exists: `wx.request` runs on Node http/https in the MAIN process
7
+ * (see native-request's design notes — this replaced a renderer `fetch()`
8
+ * specifically to stop Chromium's Fetch/CORS algorithm from attaching a
9
+ * spurious OPTIONS preflight to it) — no `webContents.debugger` can observe
10
+ * it, so the embedded DevTools Network tab would otherwise show nothing for
11
+ * every wx.request call. The trace stream gives one ordered fact per
12
+ * lifecycle moment; this module re-shapes each fact into the CDP events the
13
+ * front-end already renders natively for an XHR-classified resource
14
+ * (`requestWillBeSent` creates the row, `responseReceived` fills in
15
+ * status/headers, `loadingFinished`/`loadingFailed` settles it).
16
+ *
17
+ * Ordering contract this module relies on (guaranteed by the trace layer):
18
+ * `sent` strictly precedes every other event for its requestId, and exactly
19
+ * one of `loadingFinished`/`loadingFailed` terminates each sent request. The
20
+ * front-end silently drops loading events for an unknown requestId, so this
21
+ * module mirrors that discipline defensively: any non-`sent` event for an
22
+ * unknown requestId is dropped rather than synthesized.
23
+ *
24
+ * Pure and self-contained (no Electron imports) so it is unit-testable.
25
+ */
26
+ import { NATIVE_HTTP_REQUEST_ID_PREFIX } from "./request-ids.js";
27
+ import { isUserFacingRequest } from "./user-facing.js";
28
+ /** CDP `Network.ResourceType` this forwarder classifies every wx.request
29
+ * call as — the same bucket the real front-end uses for `XMLHttpRequest`/
30
+ * `fetch()` business calls, so it renders with the matching icon/filter. */
31
+ const RESOURCE_TYPE = "XHR";
32
+ export class RequestTraceSynthesizer {
33
+ options;
34
+ requests = new Map();
35
+ seq = 0;
36
+ constructor(options) {
37
+ this.options = options;
38
+ }
39
+ /**
40
+ * Map one trace event to its CDP message, or null when the event must be
41
+ * dropped (a non-`sent` event for a requestId this synthesizer never saw
42
+ * sent — defensive against out-of-order delivery).
43
+ */
44
+ synthesize(sessionId, event) {
45
+ const key = `${sessionId} ${event.requestId}`;
46
+ if (event.type === "sent" || event.type === "redirect") {
47
+ return this.synthesizeRequest(key, event);
48
+ }
49
+ const state = this.requests.get(key);
50
+ if (!state)
51
+ return null;
52
+ const { requestId, userFacing } = state;
53
+ const timestamp = event.time / 1000;
54
+ switch (event.type) {
55
+ case "response":
56
+ return {
57
+ method: "Network.responseReceived",
58
+ params: {
59
+ requestId,
60
+ loaderId: requestId,
61
+ timestamp,
62
+ type: RESOURCE_TYPE,
63
+ response: {
64
+ url: state.url,
65
+ status: event.status,
66
+ statusText: event.statusText,
67
+ headers: event.headers,
68
+ // Chromium uses these together to replace provisional headers
69
+ // and expose the verbatim source. ExtraInfo has no raw request
70
+ // text and would override this response snapshot in the frontend.
71
+ ...(event.requestHeaders && event.requestHeadersText ? {
72
+ requestHeaders: event.requestHeaders,
73
+ requestHeadersText: event.requestHeadersText,
74
+ } : {}),
75
+ mimeType: mimeTypeOf(event.headers),
76
+ connectionReused: false,
77
+ connectionId: 0,
78
+ encodedDataLength: 0,
79
+ fromDiskCache: false,
80
+ fromServiceWorker: false,
81
+ },
82
+ },
83
+ userFacing,
84
+ };
85
+ case "finished":
86
+ // Terminal: release the id mapping so a long-lived owner can't grow it.
87
+ this.requests.delete(key);
88
+ return {
89
+ method: "Network.loadingFinished",
90
+ params: {
91
+ requestId,
92
+ timestamp,
93
+ encodedDataLength: event.encodedDataLength,
94
+ },
95
+ userFacing,
96
+ ...(event.body !== undefined ? { body: { base64Encoded: event.bodyBase64Encoded, body: event.body } } : {}),
97
+ };
98
+ case "failed":
99
+ this.requests.delete(key);
100
+ return {
101
+ method: "Network.loadingFailed",
102
+ params: {
103
+ requestId,
104
+ timestamp,
105
+ type: RESOURCE_TYPE,
106
+ errorText: event.errorText,
107
+ },
108
+ userFacing,
109
+ };
110
+ }
111
+ }
112
+ synthesizeRequest(key, event) {
113
+ const previous = this.requests.get(key);
114
+ if (event.type === "redirect" && !previous)
115
+ return null;
116
+ const requestId = event.type === "redirect" ? previous.requestId
117
+ : `${NATIVE_HTTP_REQUEST_ID_PREFIX}${this.options.epoch}:${this.seq++}`;
118
+ const userFacing = event.type === "redirect" ? previous.userFacing : isUserFacingRequest(event.url, this.options.internalOrigins?.());
119
+ this.requests.set(key, { requestId, url: event.url, userFacing });
120
+ const timestamp = event.time / 1000;
121
+ const hasPostData = event.hasPostData ?? event.postData !== undefined;
122
+ const message = {
123
+ method: "Network.requestWillBeSent",
124
+ params: {
125
+ requestId,
126
+ loaderId: requestId,
127
+ documentURL: event.url,
128
+ request: {
129
+ url: event.url,
130
+ method: event.method,
131
+ headers: event.headers,
132
+ hasPostData,
133
+ ...(hasPostData ? { postData: event.postData } : {}),
134
+ },
135
+ timestamp,
136
+ wallTime: timestamp,
137
+ initiator: { type: "script" },
138
+ type: RESOURCE_TYPE,
139
+ ...(event.type === "redirect" ? { redirectResponse: {
140
+ ...event.redirectResponse,
141
+ mimeType: mimeTypeOf(event.redirectResponse.headers),
142
+ connectionReused: false, connectionId: 0, encodedDataLength: 0,
143
+ } } : {}),
144
+ },
145
+ userFacing,
146
+ };
147
+ if (hasPostData)
148
+ message.postData = event.postData;
149
+ return message;
150
+ }
151
+ }
152
+ /** `Content-Type: application/json; charset=utf-8` → `application/json`. Case
153
+ * -insensitive header lookup, matching HTTP semantics. */
154
+ function mimeTypeOf(headers) {
155
+ for (const [name, value] of Object.entries(headers)) {
156
+ if (name.toLowerCase() !== "content-type")
157
+ continue;
158
+ return value.split(";")[0]?.trim() ?? "";
159
+ }
160
+ return "";
161
+ }
162
+ //# sourceMappingURL=http.js.map
@@ -74,18 +74,11 @@
74
74
  * transport) arrive as trace events via `reportWebSocketTrace` and are
75
75
  * synthesized into `Network.webSocket*` CDP messages (see websocket.ts).
76
76
  */
77
- import type { WebContents } from 'electron';
78
- import { type ConnectionRegistry, type Disposable } from '@dimina-kit/electron-deck/main';
79
- import { type CdpSessionBroker } from '../cdp-session/index.js';
80
- import type { NativeWebSocketTrace } from '../../ipc/bridge-router.js';
81
- /**
82
- * Namespace prefix of every virtual requestId this forwarder injects into the
83
- * DevTools front-end. SINGLE SOURCE for the literal: the front-end outbound
84
- * gate (elements-forward) keys its `Network.getResponseBody` /
85
- * `Network.getRequestPostData` interception on this exact prefix, so the two
86
- * modules can never drift apart on what counts as "one of ours".
87
- */
88
- export declare const VIRTUAL_REQUEST_ID_PREFIX = "dimina:sim:";
77
+ import type { WebContents } from "electron";
78
+ import { type ConnectionRegistry, type Disposable } from "@dimina-kit/electron-deck/main";
79
+ import { type CdpSessionBroker } from "../cdp-session/index.js";
80
+ import type { NativeRequestTrace, NativeWebSocketTrace } from "../../ipc/bridge-router.js";
81
+ export { VIRTUAL_REQUEST_ID_PREFIX } from "./request-ids.js";
89
82
  /** CDP `Network.getResponseBody` result shape (served from the prefetch cache). */
90
83
  export interface CdpResponseBody {
91
84
  body: string;
@@ -106,7 +99,7 @@ export interface NetworkBodyProvider {
106
99
  }>;
107
100
  }
108
101
  /** Which layer a captured request came from (tags the fallback log line). */
109
- export type NetworkSource = 'service' | 'render';
102
+ export type NetworkSource = "service" | "render";
110
103
  /** A normalized completed network request, used only by the console fallback. */
111
104
  export interface NetworkRequestRecord {
112
105
  source: NetworkSource;
@@ -227,6 +220,19 @@ export interface NetworkForwarder extends Disposable {
227
220
  * user-facing. Best-effort like every injection point — never throws.
228
221
  */
229
222
  reportWebSocketTrace(sessionId: string, event: NativeWebSocketTrace): void;
223
+ /**
224
+ * Surface one main-process HTTP request trace event (wx.request traffic
225
+ * runs on Node http/https, invisible to any debugger, precisely so it
226
+ * skips Chromium's Fetch/CORS algorithm and its OPTIONS preflight) as a
227
+ * synthesized `Network.request*`/`Network.loading*` CDP event, through the
228
+ * SAME channels as forwarded simulator traffic: the global mirror
229
+ * unfiltered, plus the user-facing native sink when the request's url
230
+ * classified user-facing. `finished` primes the body cache (the response is
231
+ * already fully buffered in-process, no CDP round-trip needed); `sent`
232
+ * primes the post-data cache when the request carried a body. Best-effort
233
+ * like every injection point — never throws.
234
+ */
235
+ reportNativeRequestTrace(sessionId: string, event: NativeRequestTrace): void;
230
236
  /**
231
237
  * Body/post-data lookups for the virtual requestIds this forwarder injected,
232
238
  * backed by the loadingFinished-time prefetch cache. Keyed by virtual id, so