@hakam-aldeen-kh/blix 0.3.2
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/LICENSE +21 -0
- package/README.md +720 -0
- package/dist/DevToolsPanel-GBMCZRMW.js +5895 -0
- package/dist/capture/index.d.ts +548 -0
- package/dist/capture/index.js +18 -0
- package/dist/chunk-MRTSIXR7.js +1076 -0
- package/dist/chunk-OQHKPRNI.js +287 -0
- package/dist/chunk-PQIOIMAQ.js +7 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +40 -0
- package/package.json +106 -0
|
@@ -0,0 +1,1076 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
|
|
21
|
+
// src/capture/monitorConfig.ts
|
|
22
|
+
var MONITOR_ENABLED = process.env.NODE_ENV === "development" && typeof window !== "undefined";
|
|
23
|
+
var NM_SCHEMA_VERSION = 1;
|
|
24
|
+
var NM_DB_NAME = "nm-devtools";
|
|
25
|
+
var NM_DB_VERSION = 1;
|
|
26
|
+
var _activeDbName = NM_DB_NAME;
|
|
27
|
+
function configureDbName(name) {
|
|
28
|
+
_activeDbName = name;
|
|
29
|
+
}
|
|
30
|
+
function getActiveDbName() {
|
|
31
|
+
return _activeDbName;
|
|
32
|
+
}
|
|
33
|
+
var POOL_OF = {
|
|
34
|
+
http: "net",
|
|
35
|
+
ws: "net",
|
|
36
|
+
redux: "redux",
|
|
37
|
+
query: "query"
|
|
38
|
+
};
|
|
39
|
+
var POOL_QUOTA = {
|
|
40
|
+
// Raised from the original 100 because the list is virtualized.
|
|
41
|
+
net: 200,
|
|
42
|
+
redux: 300,
|
|
43
|
+
query: 150
|
|
44
|
+
};
|
|
45
|
+
var MAX_ENTRIES = POOL_QUOTA.net;
|
|
46
|
+
var MAX_REDUX_PAYLOAD_BYTES = 4 * 1024;
|
|
47
|
+
var MAX_REDUX_DIFF_VALUE_BYTES = 2 * 1024;
|
|
48
|
+
var MAX_REDUX_ACTIONS_PER_SEC = 300;
|
|
49
|
+
var REDUX_COALESCE_MS = 120;
|
|
50
|
+
var MAX_QUERY_DATA_BYTES = 32 * 1024;
|
|
51
|
+
var PERSIST_MAX_ENTRIES = 200;
|
|
52
|
+
var PERSIST_MAX_BYTES = 24 * 1024 * 1024;
|
|
53
|
+
var PERSIST_MAX_ENTRY_BYTES = 512 * 1024;
|
|
54
|
+
var STRING_CAP = 32 * 1024;
|
|
55
|
+
var ARRAY_CAP = 200;
|
|
56
|
+
var SEARCH_INDEX_CAP = 64 * 1024;
|
|
57
|
+
var PENDING_WINDOW_CAP_MS = 3e4;
|
|
58
|
+
var TIME_ORIGIN = typeof performance !== "undefined" && typeof performance.timeOrigin === "number" ? performance.timeOrigin : Date.now();
|
|
59
|
+
var PAGE_LOAD_AT = Date.now();
|
|
60
|
+
function makeLoadId() {
|
|
61
|
+
try {
|
|
62
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
63
|
+
return crypto.randomUUID();
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
}
|
|
67
|
+
return `${PAGE_LOAD_AT}-${Math.random().toString(36).slice(2, 10)}`;
|
|
68
|
+
}
|
|
69
|
+
var LOAD_ID = makeLoadId();
|
|
70
|
+
var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
71
|
+
|
|
72
|
+
// src/capture/monitorSerialize.ts
|
|
73
|
+
function serializeBody(data) {
|
|
74
|
+
if (data == null) return data;
|
|
75
|
+
if (typeof FormData !== "undefined" && data instanceof FormData) {
|
|
76
|
+
const obj = {};
|
|
77
|
+
data.forEach((value, key) => {
|
|
78
|
+
if (typeof File !== "undefined" && value instanceof File) {
|
|
79
|
+
obj[key] = `\xABFile: ${value.name} \u2014 ${value.size} bytes\xBB`;
|
|
80
|
+
} else {
|
|
81
|
+
obj[key] = value;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return obj;
|
|
85
|
+
}
|
|
86
|
+
if (typeof data === "string") {
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(data);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return data;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return data;
|
|
94
|
+
}
|
|
95
|
+
var SENSITIVE_HEADERS = /* @__PURE__ */ new Set([
|
|
96
|
+
"authorization",
|
|
97
|
+
"cookie",
|
|
98
|
+
"set-cookie",
|
|
99
|
+
"x-api-key"
|
|
100
|
+
]);
|
|
101
|
+
function maskHeaderValue(key, value) {
|
|
102
|
+
if (!SENSITIVE_HEADERS.has(key.toLowerCase())) return value;
|
|
103
|
+
if (value.length <= 12) return "\u2022\u2022\u2022\u2022";
|
|
104
|
+
return `${value.slice(0, 8)}\u2026${value.slice(-4)} (masked)`;
|
|
105
|
+
}
|
|
106
|
+
function serializeHeaders(headers) {
|
|
107
|
+
const out = {};
|
|
108
|
+
if (!headers || typeof headers !== "object") return out;
|
|
109
|
+
const source = typeof headers.toJSON === "function" ? headers.toJSON() : headers;
|
|
110
|
+
if (!source || typeof source !== "object") return out;
|
|
111
|
+
for (const [key, value] of Object.entries(source)) {
|
|
112
|
+
if (value == null) continue;
|
|
113
|
+
if (typeof value === "object" && !Array.isArray(value)) continue;
|
|
114
|
+
const str = Array.isArray(value) ? value.join(", ") : String(value);
|
|
115
|
+
out[key] = maskHeaderValue(key, str);
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
var ESTIMATE_CEILING = 8 * 1024 * 1024;
|
|
120
|
+
function estimateBytes(value) {
|
|
121
|
+
let total = 0;
|
|
122
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
123
|
+
const walk = (node) => {
|
|
124
|
+
if (total >= ESTIMATE_CEILING) return;
|
|
125
|
+
if (node === null || node === void 0) {
|
|
126
|
+
total += 4;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
switch (typeof node) {
|
|
130
|
+
case "string":
|
|
131
|
+
total += node.length + 2;
|
|
132
|
+
return;
|
|
133
|
+
case "number":
|
|
134
|
+
case "boolean":
|
|
135
|
+
total += 8;
|
|
136
|
+
return;
|
|
137
|
+
case "bigint":
|
|
138
|
+
case "symbol":
|
|
139
|
+
case "function":
|
|
140
|
+
total += 8;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (typeof node !== "object") return;
|
|
144
|
+
if (seen.has(node)) {
|
|
145
|
+
total += 8;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
seen.add(node);
|
|
149
|
+
if (Array.isArray(node)) {
|
|
150
|
+
total += 2 + node.length;
|
|
151
|
+
for (const item of node) {
|
|
152
|
+
if (total >= ESTIMATE_CEILING) return;
|
|
153
|
+
walk(item);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const entries = Object.entries(node);
|
|
158
|
+
total += 2 + entries.length * 2;
|
|
159
|
+
for (const [key, val] of entries) {
|
|
160
|
+
if (total >= ESTIMATE_CEILING) return;
|
|
161
|
+
total += key.length + 2;
|
|
162
|
+
walk(val);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
try {
|
|
166
|
+
walk(value);
|
|
167
|
+
} catch (e) {
|
|
168
|
+
}
|
|
169
|
+
return Math.min(total, ESTIMATE_CEILING);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/capture/networkMonitor.ts
|
|
173
|
+
var MAX_FRAMES_PER_CONNECTION = 500;
|
|
174
|
+
function evictOldest(entries) {
|
|
175
|
+
var _a;
|
|
176
|
+
const totalQuota = POOL_QUOTA.net + POOL_QUOTA.redux + POOL_QUOTA.query;
|
|
177
|
+
if (entries.length <= totalQuota) return { kept: entries, evicted: [] };
|
|
178
|
+
const kept = [];
|
|
179
|
+
const evicted = [];
|
|
180
|
+
const budget = __spreadValues({}, POOL_QUOTA);
|
|
181
|
+
for (const entry of entries) {
|
|
182
|
+
const pool = POOL_OF[(_a = entry.kind) != null ? _a : "http"];
|
|
183
|
+
const protectedEntry = entry.pinned || entry.kind === "ws" && entry.state === "pending";
|
|
184
|
+
if (budget[pool] > 0 || protectedEntry) {
|
|
185
|
+
kept.push(entry);
|
|
186
|
+
if (!protectedEntry) budget[pool] -= 1;
|
|
187
|
+
} else {
|
|
188
|
+
evicted.push(entry.id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return { kept, evicted };
|
|
192
|
+
}
|
|
193
|
+
var EMPTY = [];
|
|
194
|
+
var NetworkMonitor = class {
|
|
195
|
+
constructor() {
|
|
196
|
+
this.entries = [];
|
|
197
|
+
/** id → index into `entries`. Avoids an O(n) `map()` with a closure per
|
|
198
|
+
* element on every patch; each request produces 2–4 patches. */
|
|
199
|
+
this.index = /* @__PURE__ */ new Map();
|
|
200
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
201
|
+
this.counter = 0;
|
|
202
|
+
this.paused = false;
|
|
203
|
+
this.sink = null;
|
|
204
|
+
/** Set while an emit is already queued, so a burst of parallel requests
|
|
205
|
+
* produces one re-render rather than one per request. */
|
|
206
|
+
this.emitQueued = false;
|
|
207
|
+
/** Stable reference between mutations so useSyncExternalStore is happy. */
|
|
208
|
+
this.getSnapshot = () => this.entries;
|
|
209
|
+
this.getServerSnapshot = () => EMPTY;
|
|
210
|
+
this.subscribe = (listener) => {
|
|
211
|
+
this.listeners.add(listener);
|
|
212
|
+
return () => {
|
|
213
|
+
this.listeners.delete(listener);
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/** Whether capturing + the panel are active. Development only. */
|
|
218
|
+
get enabled() {
|
|
219
|
+
return MONITOR_ENABLED;
|
|
220
|
+
}
|
|
221
|
+
/** When paused, new requests are dropped (existing entries still update). */
|
|
222
|
+
get isPaused() {
|
|
223
|
+
return this.paused;
|
|
224
|
+
}
|
|
225
|
+
setPaused(value) {
|
|
226
|
+
if (this.paused === value) return;
|
|
227
|
+
this.paused = value;
|
|
228
|
+
this.emit();
|
|
229
|
+
}
|
|
230
|
+
setSink(sink) {
|
|
231
|
+
this.sink = sink;
|
|
232
|
+
}
|
|
233
|
+
nextId() {
|
|
234
|
+
this.counter += 1;
|
|
235
|
+
return `${Date.now()}-${this.counter}`;
|
|
236
|
+
}
|
|
237
|
+
start(input) {
|
|
238
|
+
var _a, _b, _c;
|
|
239
|
+
if (!this.enabled || this.paused) return;
|
|
240
|
+
const next = __spreadProps(__spreadValues({}, input), {
|
|
241
|
+
state: (_a = input.state) != null ? _a : "pending",
|
|
242
|
+
seq: this.counter,
|
|
243
|
+
rev: 0,
|
|
244
|
+
timeOrigin: TIME_ORIGIN,
|
|
245
|
+
startAbs: TIME_ORIGIN + input.startTime,
|
|
246
|
+
loadId: LOAD_ID
|
|
247
|
+
});
|
|
248
|
+
const combined = [next, ...this.entries];
|
|
249
|
+
const { kept, evicted } = evictOldest(combined);
|
|
250
|
+
this.entries = kept;
|
|
251
|
+
this.reindex();
|
|
252
|
+
if (next.replayOf) this.bumpReplayCount(next.replayOf);
|
|
253
|
+
(_b = this.sink) == null ? void 0 : _b.onDirty(next.id);
|
|
254
|
+
if (evicted.length) (_c = this.sink) == null ? void 0 : _c.onEvict(evicted);
|
|
255
|
+
this.emit();
|
|
256
|
+
}
|
|
257
|
+
update(id, patch) {
|
|
258
|
+
var _a;
|
|
259
|
+
if (!this.enabled) return;
|
|
260
|
+
const at = this.index.get(id);
|
|
261
|
+
if (at === void 0) return;
|
|
262
|
+
const prev = this.entries[at];
|
|
263
|
+
const merged = __spreadProps(__spreadValues(__spreadValues({}, prev), patch), { rev: prev.rev + 1 });
|
|
264
|
+
if (patch.marks) merged.marks = __spreadValues(__spreadValues({}, prev.marks), patch.marks);
|
|
265
|
+
if (patch.endTime != null && merged.startTime != null) {
|
|
266
|
+
merged.durationMs = Math.round(patch.endTime - merged.startTime);
|
|
267
|
+
merged.endAbs = merged.timeOrigin + patch.endTime;
|
|
268
|
+
}
|
|
269
|
+
const next = this.entries.slice();
|
|
270
|
+
next[at] = merged;
|
|
271
|
+
this.entries = next;
|
|
272
|
+
(_a = this.sink) == null ? void 0 : _a.onDirty(id);
|
|
273
|
+
this.emit();
|
|
274
|
+
}
|
|
275
|
+
/** Increments the parent's replay counter, in the same mutation as the child
|
|
276
|
+
* being added. Silently ignores an unknown parent (it may have been evicted). */
|
|
277
|
+
bumpReplayCount(parentId) {
|
|
278
|
+
var _a, _b;
|
|
279
|
+
const at = this.index.get(parentId);
|
|
280
|
+
if (at === void 0) return;
|
|
281
|
+
const prev = this.entries[at];
|
|
282
|
+
const next = this.entries.slice();
|
|
283
|
+
next[at] = __spreadProps(__spreadValues({}, prev), {
|
|
284
|
+
rev: prev.rev + 1,
|
|
285
|
+
replayCount: ((_a = prev.replayCount) != null ? _a : 0) + 1
|
|
286
|
+
});
|
|
287
|
+
this.entries = next;
|
|
288
|
+
(_b = this.sink) == null ? void 0 : _b.onDirty(parentId);
|
|
289
|
+
}
|
|
290
|
+
/** Clears the log, keeping pinned entries — that is what pinning is for. */
|
|
291
|
+
clear() {
|
|
292
|
+
var _a, _b;
|
|
293
|
+
const survivors = this.entries.filter((e) => e.pinned);
|
|
294
|
+
const removed = this.entries.filter((e) => !e.pinned).map((e) => e.id);
|
|
295
|
+
this.entries = survivors;
|
|
296
|
+
this.reindex();
|
|
297
|
+
if (survivors.length === 0) {
|
|
298
|
+
(_a = this.sink) == null ? void 0 : _a.onClear();
|
|
299
|
+
} else if (removed.length) {
|
|
300
|
+
(_b = this.sink) == null ? void 0 : _b.onEvict(removed);
|
|
301
|
+
}
|
|
302
|
+
this.emit();
|
|
303
|
+
}
|
|
304
|
+
togglePin(id) {
|
|
305
|
+
var _a;
|
|
306
|
+
const at = this.index.get(id);
|
|
307
|
+
if (at === void 0) return;
|
|
308
|
+
const prev = this.entries[at];
|
|
309
|
+
const next = this.entries.slice();
|
|
310
|
+
next[at] = __spreadProps(__spreadValues({}, prev), { rev: prev.rev + 1, pinned: !prev.pinned });
|
|
311
|
+
this.entries = next;
|
|
312
|
+
(_a = this.sink) == null ? void 0 : _a.onDirty(id);
|
|
313
|
+
this.emit();
|
|
314
|
+
}
|
|
315
|
+
/** O(1) lookup by id. Used by capture sources (Redux/Query) that need to
|
|
316
|
+
* read an entry's current fields before patching it — e.g. to fold a
|
|
317
|
+
* repeated action into its existing row's `batchCount`. */
|
|
318
|
+
getById(id) {
|
|
319
|
+
const at = this.index.get(id);
|
|
320
|
+
return at === void 0 ? void 0 : this.entries[at];
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Records that `childId` (an HTTP entry) was caused by the query/mutation
|
|
324
|
+
* entry `ownerId`. A no-op if the owner isn't a query/mutation row, or has
|
|
325
|
+
* been evicted — this is a best-effort correlation, never load-bearing.
|
|
326
|
+
*/
|
|
327
|
+
linkChild(ownerId, childId) {
|
|
328
|
+
var _a, _b;
|
|
329
|
+
const at = this.index.get(ownerId);
|
|
330
|
+
if (at === void 0) return;
|
|
331
|
+
const prev = this.entries[at];
|
|
332
|
+
if (!prev.query) return;
|
|
333
|
+
const causedIds = [...(_a = prev.query.causedIds) != null ? _a : [], childId].slice(-20);
|
|
334
|
+
const next = this.entries.slice();
|
|
335
|
+
next[at] = __spreadProps(__spreadValues({}, prev), { rev: prev.rev + 1, query: __spreadProps(__spreadValues({}, prev.query), { causedIds }) });
|
|
336
|
+
this.entries = next;
|
|
337
|
+
(_b = this.sink) == null ? void 0 : _b.onDirty(ownerId);
|
|
338
|
+
this.emit();
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Appends a frame to a realtime connection.
|
|
342
|
+
*
|
|
343
|
+
* Frames mutate a long-lived entry rather than creating rows of their own, so
|
|
344
|
+
* the buffer stays dominated by HTTP requests. The per-connection cap keeps a
|
|
345
|
+
* chatty channel from growing without bound.
|
|
346
|
+
*/
|
|
347
|
+
appendFrame(id, frame2) {
|
|
348
|
+
var _a, _b, _c;
|
|
349
|
+
if (!this.enabled || this.paused) return;
|
|
350
|
+
const at = this.index.get(id);
|
|
351
|
+
if (at === void 0) return;
|
|
352
|
+
const prev = this.entries[at];
|
|
353
|
+
const frames = [...(_a = prev.frames) != null ? _a : [], frame2].slice(
|
|
354
|
+
-MAX_FRAMES_PER_CONNECTION
|
|
355
|
+
);
|
|
356
|
+
const next = this.entries.slice();
|
|
357
|
+
next[at] = __spreadProps(__spreadValues({}, prev), {
|
|
358
|
+
rev: prev.rev + 1,
|
|
359
|
+
frames,
|
|
360
|
+
sizeBytes: ((_b = prev.sizeBytes) != null ? _b : 0) + frame2.sizeBytes
|
|
361
|
+
});
|
|
362
|
+
this.entries = next;
|
|
363
|
+
(_c = this.sink) == null ? void 0 : _c.onDirty(id);
|
|
364
|
+
this.emit();
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Folds entries restored from IndexedDB into the live buffer. Called once,
|
|
368
|
+
* from the panel's boot sequence, after any requests that fired while the DB
|
|
369
|
+
* was opening — hence the merge rather than a plain assignment.
|
|
370
|
+
*/
|
|
371
|
+
mergeHydrated(records) {
|
|
372
|
+
if (!this.enabled || records.length === 0) return;
|
|
373
|
+
const live = new Set(this.entries.map((e) => e.id));
|
|
374
|
+
const restored = records.filter((r) => !live.has(r.id)).map(
|
|
375
|
+
(r) => (
|
|
376
|
+
// A request left pending by a previous page load can never complete.
|
|
377
|
+
// Leaving it `pending` would spin a forever-spinner, inflate the
|
|
378
|
+
// pending count, and keep the waterfall ticker running indefinitely.
|
|
379
|
+
r.state === "pending" && r.loadId !== LOAD_ID ? __spreadProps(__spreadValues({}, r), { state: "aborted" }) : r
|
|
380
|
+
)
|
|
381
|
+
);
|
|
382
|
+
const combined = [...this.entries, ...restored].sort((a, b) => {
|
|
383
|
+
if (b.startAbs !== a.startAbs) return b.startAbs - a.startAbs;
|
|
384
|
+
return b.seq - a.seq;
|
|
385
|
+
});
|
|
386
|
+
this.entries = combined.slice(0, MAX_ENTRIES);
|
|
387
|
+
this.reindex();
|
|
388
|
+
const maxSeq = this.entries.reduce((m, e) => Math.max(m, e.seq), 0);
|
|
389
|
+
this.counter = Math.max(this.counter, maxSeq + 1);
|
|
390
|
+
this.emit();
|
|
391
|
+
}
|
|
392
|
+
reindex() {
|
|
393
|
+
this.index.clear();
|
|
394
|
+
for (let i = 0; i < this.entries.length; i += 1) {
|
|
395
|
+
this.index.set(this.entries[i].id, i);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Coalesced notification. Twenty parallel requests would otherwise fire
|
|
400
|
+
* twenty synchronous emits, each forcing `useSyncExternalStore` into a
|
|
401
|
+
* separate re-render; batching to a microtask makes that one render.
|
|
402
|
+
*/
|
|
403
|
+
emit() {
|
|
404
|
+
if (this.emitQueued) return;
|
|
405
|
+
this.emitQueued = true;
|
|
406
|
+
queueMicrotask(() => {
|
|
407
|
+
this.emitQueued = false;
|
|
408
|
+
this.listeners.forEach((listener) => listener());
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
var networkMonitor = new NetworkMonitor();
|
|
413
|
+
|
|
414
|
+
// src/capture/monitorTruncate.ts
|
|
415
|
+
function truncateForPersist(value, maxBytes) {
|
|
416
|
+
let used = 0;
|
|
417
|
+
let truncated = false;
|
|
418
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
419
|
+
const walk = (node) => {
|
|
420
|
+
if (node === null || node === void 0) {
|
|
421
|
+
used += 4;
|
|
422
|
+
return node;
|
|
423
|
+
}
|
|
424
|
+
if (typeof node === "string") {
|
|
425
|
+
if (node.length > STRING_CAP) {
|
|
426
|
+
truncated = true;
|
|
427
|
+
used += STRING_CAP + 64;
|
|
428
|
+
const marker = {
|
|
429
|
+
__nmTruncated: "string",
|
|
430
|
+
bytes: node.length,
|
|
431
|
+
head: node.slice(0, STRING_CAP)
|
|
432
|
+
};
|
|
433
|
+
return marker;
|
|
434
|
+
}
|
|
435
|
+
used += node.length + 2;
|
|
436
|
+
return node;
|
|
437
|
+
}
|
|
438
|
+
if (typeof node === "number" || typeof node === "boolean") {
|
|
439
|
+
used += 8;
|
|
440
|
+
return node;
|
|
441
|
+
}
|
|
442
|
+
if (typeof node !== "object") {
|
|
443
|
+
used += 8;
|
|
444
|
+
return String(node);
|
|
445
|
+
}
|
|
446
|
+
if (seen.has(node)) {
|
|
447
|
+
truncated = true;
|
|
448
|
+
return { __nmTruncated: "object", omitted: 0 };
|
|
449
|
+
}
|
|
450
|
+
seen.add(node);
|
|
451
|
+
if (Array.isArray(node)) {
|
|
452
|
+
const limit = Math.min(node.length, ARRAY_CAP);
|
|
453
|
+
const out3 = [];
|
|
454
|
+
used += 2;
|
|
455
|
+
for (let i = 0; i < limit; i += 1) {
|
|
456
|
+
if (used >= maxBytes) {
|
|
457
|
+
truncated = true;
|
|
458
|
+
out3.push({
|
|
459
|
+
__nmTruncated: "array",
|
|
460
|
+
total: node.length,
|
|
461
|
+
omitted: node.length - i
|
|
462
|
+
});
|
|
463
|
+
return out3;
|
|
464
|
+
}
|
|
465
|
+
out3.push(walk(node[i]));
|
|
466
|
+
used += 1;
|
|
467
|
+
}
|
|
468
|
+
if (node.length > limit) {
|
|
469
|
+
truncated = true;
|
|
470
|
+
out3.push({
|
|
471
|
+
__nmTruncated: "array",
|
|
472
|
+
total: node.length,
|
|
473
|
+
omitted: node.length - limit
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
return out3;
|
|
477
|
+
}
|
|
478
|
+
const entries = Object.entries(node);
|
|
479
|
+
const out2 = {};
|
|
480
|
+
used += 2;
|
|
481
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
482
|
+
if (used >= maxBytes) {
|
|
483
|
+
truncated = true;
|
|
484
|
+
out2.__nmTruncated = "object";
|
|
485
|
+
out2.omitted = entries.length - i;
|
|
486
|
+
return out2;
|
|
487
|
+
}
|
|
488
|
+
const [key, val] = entries[i];
|
|
489
|
+
used += key.length + 3;
|
|
490
|
+
out2[key] = walk(val);
|
|
491
|
+
}
|
|
492
|
+
return out2;
|
|
493
|
+
};
|
|
494
|
+
let out;
|
|
495
|
+
try {
|
|
496
|
+
out = walk(value);
|
|
497
|
+
} catch (e) {
|
|
498
|
+
return {
|
|
499
|
+
value: { __nmTruncated: "object", omitted: 0 },
|
|
500
|
+
bytes: 32,
|
|
501
|
+
truncated: true
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
return { value: out, bytes: used, truncated };
|
|
505
|
+
}
|
|
506
|
+
function isTruncationMarker(value) {
|
|
507
|
+
return typeof value === "object" && value !== null && "__nmTruncated" in value;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/capture/stateDiff.ts
|
|
511
|
+
var DEFAULTS = {
|
|
512
|
+
maxDepth: 6,
|
|
513
|
+
maxNodes: 2e3,
|
|
514
|
+
maxChanges: 100,
|
|
515
|
+
maxValueBytes: MAX_REDUX_DIFF_VALUE_BYTES,
|
|
516
|
+
arrayScanCap: 64
|
|
517
|
+
};
|
|
518
|
+
function isPlainObject(value) {
|
|
519
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
520
|
+
}
|
|
521
|
+
function capture(value, maxBytes) {
|
|
522
|
+
return truncateForPersist(value, maxBytes).value;
|
|
523
|
+
}
|
|
524
|
+
function diffState(prev, next, options) {
|
|
525
|
+
const o = __spreadValues(__spreadValues({}, DEFAULTS), options);
|
|
526
|
+
const changes = [];
|
|
527
|
+
const slices = /* @__PURE__ */ new Set();
|
|
528
|
+
let truncated = false;
|
|
529
|
+
let nodesVisited = 0;
|
|
530
|
+
const record = (path, op, before, after) => {
|
|
531
|
+
if (changes.length >= o.maxChanges) {
|
|
532
|
+
truncated = true;
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const entry = { path, op };
|
|
536
|
+
if (before !== void 0) entry.before = capture(before, o.maxValueBytes);
|
|
537
|
+
if (after !== void 0) entry.after = capture(after, o.maxValueBytes);
|
|
538
|
+
changes.push(entry);
|
|
539
|
+
const top = path.split(/[.[]/)[0];
|
|
540
|
+
if (top) slices.add(top);
|
|
541
|
+
};
|
|
542
|
+
const walk = (a, b, path, depth) => {
|
|
543
|
+
if (Object.is(a, b)) return;
|
|
544
|
+
if (changes.length >= o.maxChanges) {
|
|
545
|
+
truncated = true;
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
nodesVisited += 1;
|
|
549
|
+
if (nodesVisited > o.maxNodes || depth > o.maxDepth) {
|
|
550
|
+
truncated = true;
|
|
551
|
+
record(path || "(root)", "change", a, b);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
555
|
+
if (a.length !== b.length) {
|
|
556
|
+
record(path || "(root)", "change", `length ${a.length}`, `length ${b.length}`);
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
let refDifferent = 0;
|
|
560
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
561
|
+
if (!Object.is(a[i], b[i])) refDifferent += 1;
|
|
562
|
+
}
|
|
563
|
+
if (refDifferent > o.arrayScanCap) {
|
|
564
|
+
truncated = true;
|
|
565
|
+
record(
|
|
566
|
+
path || "(root)",
|
|
567
|
+
"change",
|
|
568
|
+
void 0,
|
|
569
|
+
`${refDifferent} of ${a.length} elements changed`
|
|
570
|
+
);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
574
|
+
if (!Object.is(a[i], b[i])) walk(a[i], b[i], `${path}[${i}]`, depth + 1);
|
|
575
|
+
}
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
579
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
580
|
+
for (const key of keys) {
|
|
581
|
+
const childPath = path ? `${path}.${key}` : key;
|
|
582
|
+
const av = a[key];
|
|
583
|
+
const bv = b[key];
|
|
584
|
+
if (Object.is(av, bv)) continue;
|
|
585
|
+
if (!(key in a)) record(childPath, "add", void 0, bv);
|
|
586
|
+
else if (!(key in b)) record(childPath, "remove", av, void 0);
|
|
587
|
+
else walk(av, bv, childPath, depth + 1);
|
|
588
|
+
}
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
record(path || "(root)", "change", a, b);
|
|
592
|
+
};
|
|
593
|
+
try {
|
|
594
|
+
walk(prev, next, "", 0);
|
|
595
|
+
} catch (e) {
|
|
596
|
+
truncated = true;
|
|
597
|
+
}
|
|
598
|
+
return { changes, truncated, slices: Array.from(slices) };
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/capture/reduxCapture.ts
|
|
602
|
+
var DEFAULT_IGNORE = ["@@redux/INIT", "@@INIT"];
|
|
603
|
+
var ignoreExact = new Set(DEFAULT_IGNORE);
|
|
604
|
+
var ignorePrefixes = [];
|
|
605
|
+
function setIgnoreList(types) {
|
|
606
|
+
ignoreExact = new Set(types.filter((t) => !t.endsWith("/*")));
|
|
607
|
+
ignorePrefixes = types.filter((t) => t.endsWith("/*")).map((t) => t.slice(0, -2));
|
|
608
|
+
}
|
|
609
|
+
setIgnoreList(DEFAULT_IGNORE);
|
|
610
|
+
function setReduxIgnoreList(types) {
|
|
611
|
+
setIgnoreList([...DEFAULT_IGNORE, ...types]);
|
|
612
|
+
}
|
|
613
|
+
function getReduxIgnoreList() {
|
|
614
|
+
return [...ignoreExact].filter((t) => !DEFAULT_IGNORE.includes(t));
|
|
615
|
+
}
|
|
616
|
+
function isIgnored(type) {
|
|
617
|
+
if (ignoreExact.has(type)) return true;
|
|
618
|
+
return ignorePrefixes.some((p) => type.startsWith(p));
|
|
619
|
+
}
|
|
620
|
+
var windowStartedAt = 0;
|
|
621
|
+
var windowCount = 0;
|
|
622
|
+
function isFlooding(maxPerSecond) {
|
|
623
|
+
const t = Date.now();
|
|
624
|
+
if (t - windowStartedAt > 1e3) {
|
|
625
|
+
windowStartedAt = t;
|
|
626
|
+
windowCount = 0;
|
|
627
|
+
}
|
|
628
|
+
windowCount += 1;
|
|
629
|
+
return windowCount > maxPerSecond;
|
|
630
|
+
}
|
|
631
|
+
function toEntryState(isError) {
|
|
632
|
+
return isError ? "error" : "success";
|
|
633
|
+
}
|
|
634
|
+
var replayOfId = null;
|
|
635
|
+
function markReplaying(entryId, fn) {
|
|
636
|
+
const prev = replayOfId;
|
|
637
|
+
replayOfId = entryId;
|
|
638
|
+
try {
|
|
639
|
+
return fn();
|
|
640
|
+
} finally {
|
|
641
|
+
replayOfId = prev;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
function createReduxMonitorMiddleware(options = {}) {
|
|
645
|
+
var _a, _b;
|
|
646
|
+
if (options.ignore) setReduxIgnoreList(options.ignore);
|
|
647
|
+
const coalesceMs = (_a = options.coalesceMs) != null ? _a : REDUX_COALESCE_MS;
|
|
648
|
+
const maxPerSecond = (_b = options.maxActionsPerSecond) != null ? _b : MAX_REDUX_ACTIONS_PER_SEC;
|
|
649
|
+
if (!MONITOR_ENABLED) {
|
|
650
|
+
return () => (next) => (action) => next(action);
|
|
651
|
+
}
|
|
652
|
+
let lastEntryId = null;
|
|
653
|
+
let lastType = null;
|
|
654
|
+
let lastAt = 0;
|
|
655
|
+
return (api) => (next) => (action) => {
|
|
656
|
+
var _a2, _b2;
|
|
657
|
+
const type = action == null ? void 0 : action.type;
|
|
658
|
+
if (typeof type !== "string" || networkMonitor.isPaused || isIgnored(type)) {
|
|
659
|
+
return next(action);
|
|
660
|
+
}
|
|
661
|
+
const prevState = api.getState();
|
|
662
|
+
const t0 = now();
|
|
663
|
+
const result = next(action);
|
|
664
|
+
const reducerMs = now() - t0;
|
|
665
|
+
try {
|
|
666
|
+
const nextState = api.getState();
|
|
667
|
+
const changed = prevState !== nextState;
|
|
668
|
+
const flooding = isFlooding(maxPerSecond);
|
|
669
|
+
const isError = action.error === true;
|
|
670
|
+
const at = Date.now();
|
|
671
|
+
const truncatedPayload = truncateForPersist(
|
|
672
|
+
{
|
|
673
|
+
payload: action.payload,
|
|
674
|
+
meta: action.meta
|
|
675
|
+
},
|
|
676
|
+
MAX_REDUX_PAYLOAD_BYTES
|
|
677
|
+
);
|
|
678
|
+
const payload = truncatedPayload.value;
|
|
679
|
+
const replayable = !truncatedPayload.truncated;
|
|
680
|
+
const diff = changed && !flooding ? diffState(prevState, nextState) : void 0;
|
|
681
|
+
const slice = type.includes("/") ? type.slice(0, type.indexOf("/")) : type;
|
|
682
|
+
const canCoalesce = !replayOfId && lastEntryId != null && lastType === type && at - lastAt < coalesceMs;
|
|
683
|
+
if (canCoalesce && lastEntryId) {
|
|
684
|
+
const prevEntry = networkMonitor.getById(lastEntryId);
|
|
685
|
+
if (prevEntry) {
|
|
686
|
+
networkMonitor.update(lastEntryId, {
|
|
687
|
+
state: toEntryState(isError),
|
|
688
|
+
endTime: t0 + reducerMs,
|
|
689
|
+
sizeBytes: estimateBytes(payload),
|
|
690
|
+
// Reused generically by the detail pane's "Action" tab (same
|
|
691
|
+
// field HTTP entries use for their request body).
|
|
692
|
+
requestPayload: payload,
|
|
693
|
+
redux: {
|
|
694
|
+
type,
|
|
695
|
+
payload,
|
|
696
|
+
isError,
|
|
697
|
+
diff,
|
|
698
|
+
reducerMs,
|
|
699
|
+
replayable,
|
|
700
|
+
batchCount: ((_b2 = (_a2 = prevEntry.redux) == null ? void 0 : _a2.batchCount) != null ? _b2 : 1) + 1
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
lastAt = at;
|
|
704
|
+
return result;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
const id = networkMonitor.nextId();
|
|
708
|
+
networkMonitor.start({
|
|
709
|
+
id,
|
|
710
|
+
kind: "redux",
|
|
711
|
+
method: slice,
|
|
712
|
+
url: type,
|
|
713
|
+
at,
|
|
714
|
+
startTime: t0,
|
|
715
|
+
state: toEntryState(isError),
|
|
716
|
+
sizeBytes: estimateBytes(payload),
|
|
717
|
+
requestPayload: payload,
|
|
718
|
+
replayOf: replayOfId != null ? replayOfId : void 0,
|
|
719
|
+
redux: { type, payload, isError, diff, reducerMs, replayable }
|
|
720
|
+
});
|
|
721
|
+
networkMonitor.update(id, { endTime: t0 + reducerMs });
|
|
722
|
+
lastEntryId = id;
|
|
723
|
+
lastType = type;
|
|
724
|
+
lastAt = at;
|
|
725
|
+
} catch (e) {
|
|
726
|
+
}
|
|
727
|
+
return result;
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/capture/monitorContext.ts
|
|
732
|
+
var owner = null;
|
|
733
|
+
function openOwnerWindow(next) {
|
|
734
|
+
owner = next;
|
|
735
|
+
queueMicrotask(() => {
|
|
736
|
+
if (owner === next) owner = null;
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
function getOwner() {
|
|
740
|
+
return owner;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/capture/queryCapture.ts
|
|
744
|
+
var tapped = /* @__PURE__ */ new WeakSet();
|
|
745
|
+
var activeClient = null;
|
|
746
|
+
function getTappedQueryClient() {
|
|
747
|
+
return activeClient;
|
|
748
|
+
}
|
|
749
|
+
var queryEntryIds = /* @__PURE__ */ new Map();
|
|
750
|
+
var mutationEntryIds = /* @__PURE__ */ new Map();
|
|
751
|
+
var fetchCounts = /* @__PURE__ */ new Map();
|
|
752
|
+
var frameCounter = 0;
|
|
753
|
+
function frame(direction, event) {
|
|
754
|
+
frameCounter += 1;
|
|
755
|
+
return { id: `qf${frameCounter}`, at: Date.now(), direction, event, data: void 0, sizeBytes: 0 };
|
|
756
|
+
}
|
|
757
|
+
function keyLabel(key) {
|
|
758
|
+
try {
|
|
759
|
+
return JSON.stringify(key);
|
|
760
|
+
} catch (e) {
|
|
761
|
+
return String(key);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
function toEntryState2(status) {
|
|
765
|
+
if (status === "error") return "error";
|
|
766
|
+
if (status === "success") return "success";
|
|
767
|
+
return "pending";
|
|
768
|
+
}
|
|
769
|
+
function toQueryMeta(q) {
|
|
770
|
+
var _a, _b, _c;
|
|
771
|
+
return {
|
|
772
|
+
sub: "query",
|
|
773
|
+
hash: q.queryHash,
|
|
774
|
+
key: [...q.queryKey],
|
|
775
|
+
status: toEntryState2(q.state.status),
|
|
776
|
+
fetchStatus: q.state.fetchStatus,
|
|
777
|
+
observers: (_b = (_a = q.getObserversCount) == null ? void 0 : _a.call(q)) != null ? _b : 0,
|
|
778
|
+
isInvalidated: q.state.isInvalidated,
|
|
779
|
+
dataUpdatedAt: q.state.dataUpdatedAt,
|
|
780
|
+
errorUpdatedAt: q.state.errorUpdatedAt,
|
|
781
|
+
failureCount: q.state.fetchFailureCount,
|
|
782
|
+
fetchCount: (_c = fetchCounts.get(q.queryHash)) != null ? _c : 0
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
function handleQueryEvent(event) {
|
|
786
|
+
var _a, _b;
|
|
787
|
+
const q = event.query;
|
|
788
|
+
if (!q) return;
|
|
789
|
+
if (event.type === "observerResultsUpdated" || event.type === "observerOptionsUpdated") {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (event.type === "observerAdded" || event.type === "observerRemoved") {
|
|
793
|
+
const id2 = queryEntryIds.get(q.queryHash);
|
|
794
|
+
if (id2 && networkMonitor.getById(id2)) {
|
|
795
|
+
networkMonitor.update(id2, { query: toQueryMeta(q) });
|
|
796
|
+
}
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
if (event.type === "removed") {
|
|
800
|
+
const id2 = queryEntryIds.get(q.queryHash);
|
|
801
|
+
if (id2 && networkMonitor.getById(id2)) {
|
|
802
|
+
networkMonitor.appendFrame(id2, frame("system", "removed"));
|
|
803
|
+
networkMonitor.update(id2, { query: __spreadProps(__spreadValues({}, toQueryMeta(q)), { gcRemoved: true }) });
|
|
804
|
+
}
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (event.type !== "added" && event.type !== "updated") return;
|
|
808
|
+
const existingId = queryEntryIds.get(q.queryHash);
|
|
809
|
+
const isNew = !existingId || !networkMonitor.getById(existingId);
|
|
810
|
+
const id = isNew ? networkMonitor.nextId() : existingId;
|
|
811
|
+
if (isNew) {
|
|
812
|
+
queryEntryIds.set(q.queryHash, id);
|
|
813
|
+
networkMonitor.start({
|
|
814
|
+
id,
|
|
815
|
+
kind: "query",
|
|
816
|
+
method: "QUERY",
|
|
817
|
+
url: keyLabel(q.queryKey),
|
|
818
|
+
at: Date.now(),
|
|
819
|
+
startTime: now(),
|
|
820
|
+
state: toEntryState2(q.state.status),
|
|
821
|
+
sizeBytes: 0,
|
|
822
|
+
frames: [],
|
|
823
|
+
requestPayload: { key: [...q.queryKey] },
|
|
824
|
+
query: toQueryMeta(q)
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
const actionType = (_a = event.action) == null ? void 0 : _a.type;
|
|
828
|
+
switch (actionType) {
|
|
829
|
+
case "fetch":
|
|
830
|
+
fetchCounts.set(q.queryHash, ((_b = fetchCounts.get(q.queryHash)) != null ? _b : 0) + 1);
|
|
831
|
+
networkMonitor.appendFrame(id, frame("out", "fetch"));
|
|
832
|
+
openOwnerWindow({ id, kind: "query", label: keyLabel(q.queryKey) });
|
|
833
|
+
networkMonitor.update(id, { state: "pending", query: toQueryMeta(q) });
|
|
834
|
+
break;
|
|
835
|
+
case "success":
|
|
836
|
+
networkMonitor.appendFrame(id, frame("in", "success"));
|
|
837
|
+
networkMonitor.update(id, {
|
|
838
|
+
state: "success",
|
|
839
|
+
endTime: now(),
|
|
840
|
+
responsePayload: truncateForPersist(q.state.data, MAX_QUERY_DATA_BYTES).value,
|
|
841
|
+
query: toQueryMeta(q)
|
|
842
|
+
});
|
|
843
|
+
break;
|
|
844
|
+
case "error":
|
|
845
|
+
case "failed":
|
|
846
|
+
networkMonitor.appendFrame(id, frame("in", "error"));
|
|
847
|
+
networkMonitor.update(id, {
|
|
848
|
+
state: "error",
|
|
849
|
+
endTime: now(),
|
|
850
|
+
error: truncateForPersist(q.state.error, MAX_QUERY_DATA_BYTES).value,
|
|
851
|
+
query: toQueryMeta(q)
|
|
852
|
+
});
|
|
853
|
+
break;
|
|
854
|
+
case "invalidate":
|
|
855
|
+
networkMonitor.appendFrame(id, frame("system", "invalidate"));
|
|
856
|
+
networkMonitor.update(id, { query: toQueryMeta(q) });
|
|
857
|
+
break;
|
|
858
|
+
default:
|
|
859
|
+
if (!isNew) networkMonitor.update(id, { query: toQueryMeta(q) });
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
function handleMutationEvent(event) {
|
|
863
|
+
const m = event.mutation;
|
|
864
|
+
if (!m) return;
|
|
865
|
+
if (event.type === "observerResultsUpdated" || event.type === "observerOptionsUpdated") return;
|
|
866
|
+
const existingId = mutationEntryIds.get(m.mutationId);
|
|
867
|
+
const isNew = !existingId;
|
|
868
|
+
const id = isNew ? networkMonitor.nextId() : existingId;
|
|
869
|
+
if (isNew) {
|
|
870
|
+
mutationEntryIds.set(m.mutationId, id);
|
|
871
|
+
networkMonitor.start({
|
|
872
|
+
id,
|
|
873
|
+
kind: "query",
|
|
874
|
+
method: "MUTATE",
|
|
875
|
+
url: "mutation",
|
|
876
|
+
at: Date.now(),
|
|
877
|
+
startTime: now(),
|
|
878
|
+
state: toEntryState2(m.state.status),
|
|
879
|
+
sizeBytes: 0,
|
|
880
|
+
requestPayload: truncateForPersist(m.state.variables, MAX_QUERY_DATA_BYTES).value,
|
|
881
|
+
query: {
|
|
882
|
+
sub: "mutation",
|
|
883
|
+
hash: String(m.mutationId),
|
|
884
|
+
key: [],
|
|
885
|
+
status: toEntryState2(m.state.status),
|
|
886
|
+
observers: 0
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
if (m.state.status === "pending") {
|
|
890
|
+
openOwnerWindow({ id, kind: "mutation", label: `mutation #${m.mutationId}` });
|
|
891
|
+
}
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
if (m.state.status === "success") {
|
|
895
|
+
networkMonitor.update(id, {
|
|
896
|
+
state: "success",
|
|
897
|
+
endTime: now(),
|
|
898
|
+
responsePayload: truncateForPersist(m.state.data, MAX_QUERY_DATA_BYTES).value,
|
|
899
|
+
query: { sub: "mutation", hash: String(m.mutationId), key: [], status: "success", observers: 0 }
|
|
900
|
+
});
|
|
901
|
+
} else if (m.state.status === "error") {
|
|
902
|
+
networkMonitor.update(id, {
|
|
903
|
+
state: "error",
|
|
904
|
+
endTime: now(),
|
|
905
|
+
error: truncateForPersist(m.state.error, MAX_QUERY_DATA_BYTES).value,
|
|
906
|
+
query: { sub: "mutation", hash: String(m.mutationId), key: [], status: "error", observers: 0 }
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
function tapQueryClient(client) {
|
|
911
|
+
if (!MONITOR_ENABLED) return () => {
|
|
912
|
+
};
|
|
913
|
+
if (tapped.has(client)) return () => {
|
|
914
|
+
};
|
|
915
|
+
tapped.add(client);
|
|
916
|
+
activeClient = client;
|
|
917
|
+
try {
|
|
918
|
+
for (const q of client.getQueryCache().getAll()) {
|
|
919
|
+
handleQueryEvent({ type: "added", query: q });
|
|
920
|
+
}
|
|
921
|
+
} catch (e) {
|
|
922
|
+
}
|
|
923
|
+
const unsubQuery = client.getQueryCache().subscribe((event) => {
|
|
924
|
+
try {
|
|
925
|
+
handleQueryEvent(event);
|
|
926
|
+
} catch (e) {
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
const unsubMutation = client.getMutationCache().subscribe((event) => {
|
|
930
|
+
try {
|
|
931
|
+
handleMutationEvent(event);
|
|
932
|
+
} catch (e) {
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
return () => {
|
|
936
|
+
unsubQuery();
|
|
937
|
+
unsubMutation();
|
|
938
|
+
tapped.delete(client);
|
|
939
|
+
if (activeClient === client) activeClient = null;
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// src/capture/monitorInitiator.ts
|
|
944
|
+
var NOISE = [
|
|
945
|
+
/[\\/]node_modules[\\/]axios[\\/]/,
|
|
946
|
+
/[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
|
|
947
|
+
/[\\/]node_modules[\\/]@tanstack[\\/]/,
|
|
948
|
+
/monitorInitiator|networkMonitor|monitorPersistence/,
|
|
949
|
+
/[\\/]core[\\/]network[\\/]axios\.ts/,
|
|
950
|
+
/node:internal|next[\\/]dist/,
|
|
951
|
+
/^\s*at (?:Object\.)?(?:Promise|process|async)\b/
|
|
952
|
+
];
|
|
953
|
+
var FRAME_RE = /^\s*at (?:(.+?) \()?(.+?):(\d+):(\d+)\)?$/;
|
|
954
|
+
function tidyPath(file) {
|
|
955
|
+
const cleaned = file.replace(/^webpack-internal:\/{3}\(.*?\)\/\.?/, "").replace(/^rsc:\/{3}/, "").replace(/\?.*$/, "");
|
|
956
|
+
const at = cleaned.lastIndexOf("/src/");
|
|
957
|
+
if (at >= 0) return cleaned.slice(at + 1);
|
|
958
|
+
const backslash = cleaned.lastIndexOf("\\src\\");
|
|
959
|
+
if (backslash >= 0) return cleaned.slice(backslash + 1).replace(/\\/g, "/");
|
|
960
|
+
return cleaned;
|
|
961
|
+
}
|
|
962
|
+
var MAX_FRAMES = 8;
|
|
963
|
+
function parseStack(stack) {
|
|
964
|
+
if (!stack) return [];
|
|
965
|
+
const out = [];
|
|
966
|
+
for (const line of stack.split("\n").slice(1)) {
|
|
967
|
+
if (NOISE.some((re) => re.test(line))) continue;
|
|
968
|
+
const match = FRAME_RE.exec(line);
|
|
969
|
+
if (!match) continue;
|
|
970
|
+
const [, fn, file, lineNo, col] = match;
|
|
971
|
+
out.push({
|
|
972
|
+
fn: (fn == null ? void 0 : fn.trim()) || void 0,
|
|
973
|
+
file: tidyPath(file),
|
|
974
|
+
line: Number(lineNo),
|
|
975
|
+
col: Number(col)
|
|
976
|
+
});
|
|
977
|
+
if (out.length >= MAX_FRAMES) break;
|
|
978
|
+
}
|
|
979
|
+
return out;
|
|
980
|
+
}
|
|
981
|
+
function summarizeInitiator(frames) {
|
|
982
|
+
var _a, _b;
|
|
983
|
+
if (frames.length === 0) return "";
|
|
984
|
+
const own = (_a = frames.find((f) => /^src\/(features|shared|app)\//.test(f.file))) != null ? _a : frames[0];
|
|
985
|
+
const name = own.fn ? `${own.fn} \xB7 ` : "";
|
|
986
|
+
const file = (_b = own.file.split("/").pop()) != null ? _b : own.file;
|
|
987
|
+
return `${name}${file}${own.line ? `:${own.line}` : ""}`;
|
|
988
|
+
}
|
|
989
|
+
var captureEnabled = true;
|
|
990
|
+
function setInitiatorCapture(on) {
|
|
991
|
+
captureEnabled = on;
|
|
992
|
+
}
|
|
993
|
+
function captureFrames() {
|
|
994
|
+
if (!captureEnabled) return void 0;
|
|
995
|
+
const previousLimit = Error.stackTraceLimit;
|
|
996
|
+
try {
|
|
997
|
+
Error.stackTraceLimit = 24;
|
|
998
|
+
const stack = new Error().stack;
|
|
999
|
+
return parseStack(stack);
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
return void 0;
|
|
1002
|
+
} finally {
|
|
1003
|
+
Error.stackTraceLimit = previousLimit;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
function withInitiatorCapture(instance) {
|
|
1007
|
+
if (!MONITOR_ENABLED) return instance;
|
|
1008
|
+
const attach = (config) => {
|
|
1009
|
+
if (!config) return config;
|
|
1010
|
+
if (!config.__monitorInitiator) config.__monitorInitiator = captureFrames();
|
|
1011
|
+
return config;
|
|
1012
|
+
};
|
|
1013
|
+
const proxy = new Proxy(instance, {
|
|
1014
|
+
apply(target, thisArg, args) {
|
|
1015
|
+
const [config] = args;
|
|
1016
|
+
return Reflect.apply(
|
|
1017
|
+
target,
|
|
1018
|
+
thisArg,
|
|
1019
|
+
[attach(config)]
|
|
1020
|
+
);
|
|
1021
|
+
},
|
|
1022
|
+
get(target, prop, receiver) {
|
|
1023
|
+
const value = Reflect.get(target, prop, receiver);
|
|
1024
|
+
if (typeof value !== "function") return value;
|
|
1025
|
+
if (prop === "request") {
|
|
1026
|
+
return (config) => value.call(target, attach(config));
|
|
1027
|
+
}
|
|
1028
|
+
if (prop === "post" || prop === "put" || prop === "patch") {
|
|
1029
|
+
return (url, data, config) => value.call(
|
|
1030
|
+
target,
|
|
1031
|
+
url,
|
|
1032
|
+
data,
|
|
1033
|
+
attach(config != null ? config : {})
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
if (prop === "get" || prop === "delete" || prop === "head") {
|
|
1037
|
+
return (url, config) => value.call(target, url, attach(config != null ? config : {}));
|
|
1038
|
+
}
|
|
1039
|
+
return value.bind(target);
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
return proxy;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
export {
|
|
1046
|
+
__spreadValues,
|
|
1047
|
+
__spreadProps,
|
|
1048
|
+
MONITOR_ENABLED,
|
|
1049
|
+
NM_SCHEMA_VERSION,
|
|
1050
|
+
NM_DB_VERSION,
|
|
1051
|
+
configureDbName,
|
|
1052
|
+
getActiveDbName,
|
|
1053
|
+
MAX_ENTRIES,
|
|
1054
|
+
PERSIST_MAX_ENTRIES,
|
|
1055
|
+
PERSIST_MAX_BYTES,
|
|
1056
|
+
PERSIST_MAX_ENTRY_BYTES,
|
|
1057
|
+
SEARCH_INDEX_CAP,
|
|
1058
|
+
PENDING_WINDOW_CAP_MS,
|
|
1059
|
+
now,
|
|
1060
|
+
getOwner,
|
|
1061
|
+
serializeBody,
|
|
1062
|
+
serializeHeaders,
|
|
1063
|
+
estimateBytes,
|
|
1064
|
+
networkMonitor,
|
|
1065
|
+
truncateForPersist,
|
|
1066
|
+
isTruncationMarker,
|
|
1067
|
+
setReduxIgnoreList,
|
|
1068
|
+
getReduxIgnoreList,
|
|
1069
|
+
markReplaying,
|
|
1070
|
+
createReduxMonitorMiddleware,
|
|
1071
|
+
getTappedQueryClient,
|
|
1072
|
+
tapQueryClient,
|
|
1073
|
+
summarizeInitiator,
|
|
1074
|
+
setInitiatorCapture,
|
|
1075
|
+
withInitiatorCapture
|
|
1076
|
+
};
|