@spotpatch/runtime 1.8.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-7ES63LA7.js +28 -0
- package/dist/chunk-7ES63LA7.js.map +1 -0
- package/dist/chunk-NII5OBAN.js +14 -0
- package/dist/chunk-NII5OBAN.js.map +1 -0
- package/dist/chunk-QY5T4DPA.js +552 -0
- package/dist/chunk-QY5T4DPA.js.map +1 -0
- package/dist/chunk-XBD55BCF.js +14 -0
- package/dist/chunk-XBD55BCF.js.map +1 -0
- package/dist/data-flow-panel.cjs +333 -0
- package/dist/data-flow-panel.cjs.map +1 -0
- package/dist/data-flow-panel.d.cts +38 -0
- package/dist/data-flow-panel.d.ts +38 -0
- package/dist/data-flow-panel.js +283 -0
- package/dist/data-flow-panel.js.map +1 -0
- package/dist/data-flow-runtime-B3itbieT.d.cts +67 -0
- package/dist/data-flow-runtime-B3itbieT.d.ts +67 -0
- package/dist/data-flow.cjs +575 -0
- package/dist/data-flow.cjs.map +1 -0
- package/dist/data-flow.d.cts +8 -0
- package/dist/data-flow.d.ts +8 -0
- package/dist/data-flow.js +15 -0
- package/dist/data-flow.js.map +1 -0
- package/dist/external-handoff-panel.cjs +1002 -0
- package/dist/external-handoff-panel.cjs.map +1 -0
- package/dist/external-handoff-panel.d.cts +36 -0
- package/dist/external-handoff-panel.d.ts +36 -0
- package/dist/external-handoff-panel.js +959 -0
- package/dist/external-handoff-panel.js.map +1 -0
- package/dist/index.cjs +871 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +335 -31
- package/dist/index.js.map +1 -1
- package/package.json +46 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/ui/ui-constants.ts
|
|
2
|
+
var UI_MARKER_ATTRIBUTE = "data-spotpatch-ui";
|
|
3
|
+
var UI_Z_INDEX = Object.freeze({
|
|
4
|
+
highlight: 2147483646,
|
|
5
|
+
controls: 2147483647
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/ui/dom.ts
|
|
9
|
+
function createMarkedElement(document, tagName) {
|
|
10
|
+
const element = document.createElement(tagName);
|
|
11
|
+
element.setAttribute(UI_MARKER_ATTRIBUTE, "");
|
|
12
|
+
return element;
|
|
13
|
+
}
|
|
14
|
+
function createButton(document, label, className = "") {
|
|
15
|
+
const button = createMarkedElement(document, "button");
|
|
16
|
+
button.type = "button";
|
|
17
|
+
button.className = className;
|
|
18
|
+
button.textContent = label;
|
|
19
|
+
return button;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
UI_MARKER_ATTRIBUTE,
|
|
24
|
+
UI_Z_INDEX,
|
|
25
|
+
createMarkedElement,
|
|
26
|
+
createButton
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=chunk-7ES63LA7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/ui-constants.ts","../src/ui/dom.ts"],"sourcesContent":["export const UI_MARKER_ATTRIBUTE = \"data-spotpatch-ui\" as const;\n\nexport const UI_Z_INDEX = Object.freeze({\n highlight: 2_147_483_646,\n controls: 2_147_483_647,\n});\n","import { UI_MARKER_ATTRIBUTE } from \"./ui-constants.js\";\n\nexport function createMarkedElement<K extends keyof HTMLElementTagNameMap>(\n document: Document,\n tagName: K,\n): HTMLElementTagNameMap[K] {\n const element = document.createElement(tagName);\n element.setAttribute(UI_MARKER_ATTRIBUTE, \"\");\n return element;\n}\n\nexport function createButton(\n document: Document,\n label: string,\n className = \"\",\n): HTMLButtonElement {\n const button = createMarkedElement(document, \"button\");\n button.type = \"button\";\n button.className = className;\n button.textContent = label;\n return button;\n}\n"],"mappings":";AAAO,IAAM,sBAAsB;AAE5B,IAAM,aAAa,OAAO,OAAO;AAAA,EACtC,WAAW;AAAA,EACX,UAAU;AACZ,CAAC;;;ACHM,SAAS,oBACd,UACA,SAC0B;AAC1B,QAAM,UAAU,SAAS,cAAc,OAAO;AAC9C,UAAQ,aAAa,qBAAqB,EAAE;AAC5C,SAAO;AACT;AAEO,SAAS,aACd,UACA,OACA,YAAY,IACO;AACnB,QAAM,SAAS,oBAAoB,UAAU,QAAQ;AACrD,SAAO,OAAO;AACd,SAAO,YAAY;AACnB,SAAO,cAAc;AACrB,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/ui/data-flow-panel-contract.ts
|
|
2
|
+
var DATA_FLOW_EXTENSION_KEY = /* @__PURE__ */ Symbol.for("spotpatch.data-flow.extension.v1");
|
|
3
|
+
function registerDataFlowExtension(extension, target = globalThis) {
|
|
4
|
+
target[DATA_FLOW_EXTENSION_KEY] = extension;
|
|
5
|
+
}
|
|
6
|
+
function getDataFlowExtension(target = globalThis) {
|
|
7
|
+
return target[DATA_FLOW_EXTENSION_KEY];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
registerDataFlowExtension,
|
|
12
|
+
getDataFlowExtension
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=chunk-NII5OBAN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/data-flow-panel-contract.ts"],"sourcesContent":["import type {\n ComponentDataFlowReport,\n NetworkObservation,\n PageDataFlowReport,\n SpotPatchLocale,\n} from \"@spotpatch/shared\";\n\nimport type { DataFlowComponentRegistration } from \"../data-flow/data-flow-runtime.js\";\n\nexport type DataFlowPanelStatus = \"disabled\" | \"idle\" | \"loading\" | \"ready\" | \"error\";\n\nexport interface DataFlowPanelSnapshot {\n readonly status: DataFlowPanelStatus;\n readonly report?: ComponentDataFlowReport | PageDataFlowReport;\n readonly message?: string;\n}\n\nexport interface DataFlowViewState {\n readonly component: DataFlowPanelSnapshot;\n readonly page: DataFlowPanelSnapshot;\n readonly observationCount: number;\n}\n\nexport interface DataFlowPanel {\n readonly root: HTMLElement;\n readonly refreshButton: HTMLButtonElement;\n readonly styles: HTMLStyleElement;\n readonly dispose: () => void;\n readonly render: (state: DataFlowViewState) => void;\n readonly resetView: () => void;\n}\n\nexport type DataFlowPanelFactory = (\n document: Document,\n enabled: boolean,\n locale: () => SpotPatchLocale,\n changesRoot: HTMLElement,\n diagnosticsRoot: HTMLElement,\n onViewChange: () => void,\n) => DataFlowPanel;\n\nexport interface DataFlowExtension {\n readonly createPanel: DataFlowPanelFactory;\n readonly getComponentRegistration: (\n component: object,\n ) => DataFlowComponentRegistration | undefined;\n readonly observations: (routeKey: string) => readonly NetworkObservation[];\n readonly mergeComponentReport: (\n report: ComponentDataFlowReport,\n observations: readonly NetworkObservation[],\n ) => ComponentDataFlowReport;\n readonly mergePageReport: (\n report: PageDataFlowReport,\n observations: readonly NetworkObservation[],\n ) => PageDataFlowReport;\n}\n\nconst DATA_FLOW_EXTENSION_KEY = Symbol.for(\"spotpatch.data-flow.extension.v1\");\ntype ExtensionStore = Partial<Record<symbol, DataFlowExtension>>;\n\nexport function registerDataFlowExtension(\n extension: DataFlowExtension,\n target: ExtensionStore = globalThis,\n): void {\n target[DATA_FLOW_EXTENSION_KEY] = extension;\n}\n\nexport function getDataFlowExtension(\n target: ExtensionStore = globalThis,\n): DataFlowExtension | undefined {\n return target[DATA_FLOW_EXTENSION_KEY];\n}\n"],"mappings":";AAyDA,IAAM,0BAA0B,uBAAO,IAAI,kCAAkC;AAGtE,SAAS,0BACd,WACA,SAAyB,YACnB;AACN,SAAO,uBAAuB,IAAI;AACpC;AAEO,SAAS,qBACd,SAAyB,YACM;AAC/B,SAAO,OAAO,uBAAuB;AACvC;","names":[]}
|
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
// src/data-flow/data-flow-runtime.ts
|
|
2
|
+
import {
|
|
3
|
+
DATA_FLOW_SCHEMA_VERSION,
|
|
4
|
+
DATA_FLOW_URL_QUERY_KEY_LIMIT,
|
|
5
|
+
SPOTPATCH_API_BASE
|
|
6
|
+
} from "@spotpatch/shared/data-flow-runtime";
|
|
7
|
+
var RUNTIME_KEY = /* @__PURE__ */ Symbol.for(
|
|
8
|
+
"spotpatch.data-flow.runtime.v1"
|
|
9
|
+
);
|
|
10
|
+
var REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo");
|
|
11
|
+
var REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref");
|
|
12
|
+
function positiveNow(target) {
|
|
13
|
+
return target.performance?.now() ?? Date.now();
|
|
14
|
+
}
|
|
15
|
+
function createOpaqueSequence(prefix) {
|
|
16
|
+
let sequence = 0;
|
|
17
|
+
const randomPrefix = (() => {
|
|
18
|
+
try {
|
|
19
|
+
const bytes = new Uint8Array(8);
|
|
20
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
21
|
+
return Array.from(bytes, (value) => value.toString(36).padStart(2, "0")).join("");
|
|
22
|
+
} catch {
|
|
23
|
+
return "local";
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
return () => {
|
|
27
|
+
sequence += 1;
|
|
28
|
+
return `${prefix}_${randomPrefix}_${sequence.toString(36)}`;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function freezeUrl(value, baseUrl) {
|
|
32
|
+
try {
|
|
33
|
+
const url = new URL(value, baseUrl);
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
origin: url.origin,
|
|
36
|
+
pathname: url.pathname,
|
|
37
|
+
queryKeys: Object.freeze(
|
|
38
|
+
[...new Set(url.searchParams.keys())].sort().slice(0, DATA_FLOW_URL_QUERY_KEY_LIMIT)
|
|
39
|
+
)
|
|
40
|
+
});
|
|
41
|
+
} catch {
|
|
42
|
+
return Object.freeze({
|
|
43
|
+
pathname: value.split(/[?#]/u, 1)[0] ?? "{invalid}",
|
|
44
|
+
queryKeys: Object.freeze([])
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function readFetchUrl(input) {
|
|
49
|
+
if (typeof input === "string") return input;
|
|
50
|
+
if (input instanceof URL) return input.toString();
|
|
51
|
+
return input.url;
|
|
52
|
+
}
|
|
53
|
+
function readFetchMethod(input, init) {
|
|
54
|
+
if (init?.method !== void 0) return init.method.toUpperCase();
|
|
55
|
+
return typeof Request !== "undefined" && input instanceof Request ? input.method.toUpperCase() : "GET";
|
|
56
|
+
}
|
|
57
|
+
function isSpotPatchInternalUrl(value, baseUrl) {
|
|
58
|
+
try {
|
|
59
|
+
const pathname = new URL(value, baseUrl).pathname;
|
|
60
|
+
return pathname === SPOTPATCH_API_BASE || pathname.startsWith(`${SPOTPATCH_API_BASE}/`);
|
|
61
|
+
} catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function approximateBytes(value) {
|
|
66
|
+
return new TextEncoder().encode(JSON.stringify(value)).byteLength;
|
|
67
|
+
}
|
|
68
|
+
function createRingStore(limits, target) {
|
|
69
|
+
const entries = [];
|
|
70
|
+
let totalBytes = 0;
|
|
71
|
+
function removeExpired(now) {
|
|
72
|
+
while (entries[0] !== void 0 && now - entries[0].recordedAt > limits.observationTtlMs) {
|
|
73
|
+
const removed = entries.shift();
|
|
74
|
+
if (removed !== void 0) totalBytes -= removed.bytes;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return Object.freeze({
|
|
78
|
+
add(observation) {
|
|
79
|
+
const now = positiveNow(target);
|
|
80
|
+
removeExpired(now);
|
|
81
|
+
const bytes = approximateBytes(observation);
|
|
82
|
+
if (bytes > limits.observationMaxBytes) return;
|
|
83
|
+
entries.push(Object.freeze({ bytes, observation, recordedAt: now }));
|
|
84
|
+
totalBytes += bytes;
|
|
85
|
+
while (entries.length > limits.observationMaxEntries || totalBytes > limits.observationMaxBytes) {
|
|
86
|
+
const removed = entries.shift();
|
|
87
|
+
if (removed !== void 0) totalBytes -= removed.bytes;
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
clear() {
|
|
91
|
+
entries.length = 0;
|
|
92
|
+
totalBytes = 0;
|
|
93
|
+
},
|
|
94
|
+
values() {
|
|
95
|
+
removeExpired(positiveNow(target));
|
|
96
|
+
return Object.freeze(entries.map(({ observation }) => observation));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function ownPropertyDescriptor(target, key) {
|
|
101
|
+
try {
|
|
102
|
+
return Object.getOwnPropertyDescriptor(target, key);
|
|
103
|
+
} catch {
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function nestedReactWrapperComponent(candidate) {
|
|
108
|
+
const marker = ownPropertyDescriptor(candidate, "$$typeof")?.value;
|
|
109
|
+
const key = marker === REACT_MEMO_TYPE ? "type" : marker === REACT_FORWARD_REF_TYPE ? "render" : void 0;
|
|
110
|
+
if (key === void 0) return void 0;
|
|
111
|
+
const nested = ownPropertyDescriptor(candidate, key)?.value;
|
|
112
|
+
return typeof nested === "object" && nested !== null || typeof nested === "function" ? nested : void 0;
|
|
113
|
+
}
|
|
114
|
+
function installWritableDataProperty(target, key, replacement, original = ownPropertyDescriptor(target, key)) {
|
|
115
|
+
if (original === void 0 || !("value" in original) || !original.writable) {
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
Object.defineProperty(target, key, { ...original, value: replacement });
|
|
120
|
+
} catch {
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
return () => {
|
|
124
|
+
try {
|
|
125
|
+
if (ownPropertyDescriptor(target, key)?.value === replacement) {
|
|
126
|
+
Object.defineProperty(target, key, original);
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function recordWithoutAffectingHost(record) {
|
|
133
|
+
try {
|
|
134
|
+
record();
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function createDataFlowRuntime(config, target = globalThis) {
|
|
139
|
+
const componentRegistry = /* @__PURE__ */ new WeakMap();
|
|
140
|
+
const xhrMetadata = /* @__PURE__ */ new WeakMap();
|
|
141
|
+
const store = createRingStore(config.limits, target);
|
|
142
|
+
const nextInvocationId = createOpaqueSequence("invocation");
|
|
143
|
+
const nextObservationId = createOpaqueSequence("observation");
|
|
144
|
+
const pageEpoch = createOpaqueSequence("page")();
|
|
145
|
+
const nextRouteEpoch = createOpaqueSequence("route");
|
|
146
|
+
let routeEpoch = nextRouteEpoch();
|
|
147
|
+
let routeKey;
|
|
148
|
+
let currentInvocation;
|
|
149
|
+
let currentRequestFrame;
|
|
150
|
+
let disposed = false;
|
|
151
|
+
const originalFetch = (() => {
|
|
152
|
+
try {
|
|
153
|
+
return target.fetch;
|
|
154
|
+
} catch {
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
function spotPatchFetch(input, init) {
|
|
159
|
+
if (originalFetch === void 0) {
|
|
160
|
+
throw new TypeError("Fetch is unavailable.");
|
|
161
|
+
}
|
|
162
|
+
const result = Reflect.apply(originalFetch, this, [input, init]);
|
|
163
|
+
recordWithoutAffectingHost(() => {
|
|
164
|
+
const frame = currentRequestFrame;
|
|
165
|
+
const token = frame?.invocationToken;
|
|
166
|
+
const rawUrl = readFetchUrl(input);
|
|
167
|
+
if (isSpotPatchInternalUrl(
|
|
168
|
+
rawUrl,
|
|
169
|
+
target.location?.href ?? "http://spotpatch.invalid/"
|
|
170
|
+
)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
store.add(
|
|
174
|
+
Object.freeze({
|
|
175
|
+
schemaVersion: DATA_FLOW_SCHEMA_VERSION,
|
|
176
|
+
id: nextObservationId(),
|
|
177
|
+
pageEpoch,
|
|
178
|
+
routeEpoch,
|
|
179
|
+
...frame === void 0 ? {} : {
|
|
180
|
+
requestCallsiteId: frame.requestCallsiteId,
|
|
181
|
+
sourceVersion: frame.sourceVersion
|
|
182
|
+
},
|
|
183
|
+
...token === void 0 ? {} : {
|
|
184
|
+
invocationId: token.invocationId,
|
|
185
|
+
componentSourceId: token.componentSourceId,
|
|
186
|
+
triggerCallsiteId: token.triggerCallsiteId
|
|
187
|
+
},
|
|
188
|
+
transport: "fetch",
|
|
189
|
+
method: readFetchMethod(input, init),
|
|
190
|
+
url: freezeUrl(rawUrl, target.location?.href ?? "http://spotpatch.invalid/"),
|
|
191
|
+
outcome: "dispatched",
|
|
192
|
+
freshness: "current",
|
|
193
|
+
diagnosticIds: Object.freeze([])
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
const restoreFetch = originalFetch === void 0 ? void 0 : installWritableDataProperty(target, "fetch", spotPatchFetch);
|
|
200
|
+
const xhrPrototype = (() => {
|
|
201
|
+
try {
|
|
202
|
+
return target.XMLHttpRequest?.prototype;
|
|
203
|
+
} catch {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
})();
|
|
207
|
+
const originalOpenDescriptor = xhrPrototype === void 0 ? void 0 : ownPropertyDescriptor(xhrPrototype, "open");
|
|
208
|
+
const originalSendDescriptor = xhrPrototype === void 0 ? void 0 : ownPropertyDescriptor(xhrPrototype, "send");
|
|
209
|
+
function spotPatchOpen(method, url, ...rest) {
|
|
210
|
+
const original = originalOpenDescriptor?.value;
|
|
211
|
+
if (typeof original === "function") {
|
|
212
|
+
Reflect.apply(original, this, [method, url, ...rest]);
|
|
213
|
+
}
|
|
214
|
+
recordWithoutAffectingHost(() => {
|
|
215
|
+
xhrMetadata.set(this, Object.freeze({ method, url: String(url) }));
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function spotPatchSend(body) {
|
|
219
|
+
const original = originalSendDescriptor?.value;
|
|
220
|
+
if (typeof original === "function") Reflect.apply(original, this, [body]);
|
|
221
|
+
recordWithoutAffectingHost(() => {
|
|
222
|
+
const metadata = xhrMetadata.get(this);
|
|
223
|
+
if (metadata === void 0 || isSpotPatchInternalUrl(
|
|
224
|
+
metadata.url,
|
|
225
|
+
target.location?.href ?? "http://spotpatch.invalid/"
|
|
226
|
+
)) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const frame = currentRequestFrame;
|
|
230
|
+
const token = frame?.invocationToken;
|
|
231
|
+
store.add(
|
|
232
|
+
Object.freeze({
|
|
233
|
+
schemaVersion: DATA_FLOW_SCHEMA_VERSION,
|
|
234
|
+
id: nextObservationId(),
|
|
235
|
+
pageEpoch,
|
|
236
|
+
routeEpoch,
|
|
237
|
+
...frame === void 0 ? {} : {
|
|
238
|
+
requestCallsiteId: frame.requestCallsiteId,
|
|
239
|
+
sourceVersion: frame.sourceVersion
|
|
240
|
+
},
|
|
241
|
+
...token === void 0 ? {} : {
|
|
242
|
+
invocationId: token.invocationId,
|
|
243
|
+
componentSourceId: token.componentSourceId,
|
|
244
|
+
triggerCallsiteId: token.triggerCallsiteId
|
|
245
|
+
},
|
|
246
|
+
transport: "xhr",
|
|
247
|
+
method: metadata.method.toUpperCase(),
|
|
248
|
+
url: freezeUrl(
|
|
249
|
+
metadata.url,
|
|
250
|
+
target.location?.href ?? "http://spotpatch.invalid/"
|
|
251
|
+
),
|
|
252
|
+
outcome: "dispatched",
|
|
253
|
+
freshness: "current",
|
|
254
|
+
diagnosticIds: Object.freeze([])
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const restoreXhrOpen = xhrPrototype === void 0 || typeof originalOpenDescriptor?.value !== "function" ? void 0 : installWritableDataProperty(
|
|
260
|
+
xhrPrototype,
|
|
261
|
+
"open",
|
|
262
|
+
spotPatchOpen,
|
|
263
|
+
originalOpenDescriptor
|
|
264
|
+
);
|
|
265
|
+
const restoreXhrSend = xhrPrototype === void 0 || typeof originalSendDescriptor?.value !== "function" ? void 0 : installWritableDataProperty(
|
|
266
|
+
xhrPrototype,
|
|
267
|
+
"send",
|
|
268
|
+
spotPatchSend,
|
|
269
|
+
originalSendDescriptor
|
|
270
|
+
);
|
|
271
|
+
const runtime = Object.freeze({
|
|
272
|
+
beginInvocation(metadata) {
|
|
273
|
+
return Object.freeze({
|
|
274
|
+
invocationId: nextInvocationId(),
|
|
275
|
+
componentSourceId: metadata.componentSourceId,
|
|
276
|
+
triggerCallsiteId: metadata.triggerCallsiteId,
|
|
277
|
+
sourceVersion: metadata.sourceVersion
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
bindInvocation(token, callback) {
|
|
281
|
+
return function boundInvocation(...args) {
|
|
282
|
+
return runtime.withInvocation(token, () => Reflect.apply(callback, this, args));
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
bindTrigger(metadata, callback) {
|
|
286
|
+
if (typeof callback !== "function") return callback;
|
|
287
|
+
const callable = callback;
|
|
288
|
+
return function boundTrigger(...args) {
|
|
289
|
+
const token = runtime.beginInvocation(metadata);
|
|
290
|
+
return runtime.withInvocation(token, () => Reflect.apply(callable, this, args));
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
captureInvocation: () => currentInvocation,
|
|
294
|
+
clear: store.clear,
|
|
295
|
+
createTrpcLink() {
|
|
296
|
+
return () => (options) => {
|
|
297
|
+
recordWithoutAffectingHost(() => {
|
|
298
|
+
const operation = options.op.path;
|
|
299
|
+
const operationType = options.op.type;
|
|
300
|
+
if (typeof operation !== "string" || operation.length === 0 || operation.length > 512 || operationType !== "query" && operationType !== "mutation" && operationType !== "subscription") {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const frame = currentRequestFrame;
|
|
304
|
+
const token = frame?.invocationToken;
|
|
305
|
+
store.add(
|
|
306
|
+
Object.freeze({
|
|
307
|
+
schemaVersion: DATA_FLOW_SCHEMA_VERSION,
|
|
308
|
+
id: nextObservationId(),
|
|
309
|
+
pageEpoch,
|
|
310
|
+
routeEpoch,
|
|
311
|
+
...frame === void 0 ? {} : {
|
|
312
|
+
requestCallsiteId: frame.requestCallsiteId,
|
|
313
|
+
sourceVersion: frame.sourceVersion
|
|
314
|
+
},
|
|
315
|
+
...token === void 0 ? {} : {
|
|
316
|
+
invocationId: token.invocationId,
|
|
317
|
+
componentSourceId: token.componentSourceId,
|
|
318
|
+
triggerCallsiteId: token.triggerCallsiteId
|
|
319
|
+
},
|
|
320
|
+
transport: "trpc",
|
|
321
|
+
method: operationType.toUpperCase(),
|
|
322
|
+
operation,
|
|
323
|
+
url: Object.freeze({
|
|
324
|
+
pathname: operation,
|
|
325
|
+
queryKeys: Object.freeze([])
|
|
326
|
+
}),
|
|
327
|
+
outcome: "dispatched",
|
|
328
|
+
freshness: "current",
|
|
329
|
+
diagnosticIds: Object.freeze([])
|
|
330
|
+
})
|
|
331
|
+
);
|
|
332
|
+
});
|
|
333
|
+
return options.next(options.op);
|
|
334
|
+
};
|
|
335
|
+
},
|
|
336
|
+
dispose() {
|
|
337
|
+
if (disposed) return;
|
|
338
|
+
disposed = true;
|
|
339
|
+
store.clear();
|
|
340
|
+
restoreFetch?.();
|
|
341
|
+
restoreXhrOpen?.();
|
|
342
|
+
restoreXhrSend?.();
|
|
343
|
+
},
|
|
344
|
+
getComponentRegistration: (component) => componentRegistry.get(component),
|
|
345
|
+
getCurrentRequestFrame: () => currentRequestFrame,
|
|
346
|
+
observations: () => Object.freeze(
|
|
347
|
+
store.values().map(
|
|
348
|
+
(observation) => observation.routeEpoch === routeEpoch ? observation : Object.freeze({ ...observation, freshness: "stale-route" })
|
|
349
|
+
)
|
|
350
|
+
),
|
|
351
|
+
registerComponent(component, componentSourceId, registeredSourceVersion) {
|
|
352
|
+
const registration = Object.freeze({
|
|
353
|
+
componentSourceId,
|
|
354
|
+
sourceVersion: registeredSourceVersion
|
|
355
|
+
});
|
|
356
|
+
const pending = [component];
|
|
357
|
+
const registered = /* @__PURE__ */ new Set();
|
|
358
|
+
while (pending.length > 0) {
|
|
359
|
+
const candidate = pending.pop();
|
|
360
|
+
if (candidate === void 0 || registered.has(candidate)) continue;
|
|
361
|
+
registered.add(candidate);
|
|
362
|
+
componentRegistry.set(candidate, registration);
|
|
363
|
+
const nested = nestedReactWrapperComponent(candidate);
|
|
364
|
+
if (nested !== void 0) pending.push(nested);
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
updateRoute(nextRouteKey) {
|
|
368
|
+
if (routeKey === void 0) {
|
|
369
|
+
routeKey = nextRouteKey;
|
|
370
|
+
} else if (routeKey !== nextRouteKey) {
|
|
371
|
+
routeKey = nextRouteKey;
|
|
372
|
+
routeEpoch = nextRouteEpoch();
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
withInvocation(token, callback) {
|
|
376
|
+
const parent = currentInvocation;
|
|
377
|
+
currentInvocation = token;
|
|
378
|
+
try {
|
|
379
|
+
return callback();
|
|
380
|
+
} finally {
|
|
381
|
+
currentInvocation = parent;
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
withRequestFrame(token, metadata, callback) {
|
|
385
|
+
const parent = currentRequestFrame;
|
|
386
|
+
currentRequestFrame = Object.freeze({
|
|
387
|
+
requestCallsiteId: metadata.requestCallsiteId,
|
|
388
|
+
sourceVersion: metadata.sourceVersion,
|
|
389
|
+
...token === void 0 ? {} : { invocationToken: token }
|
|
390
|
+
});
|
|
391
|
+
try {
|
|
392
|
+
return callback();
|
|
393
|
+
} finally {
|
|
394
|
+
currentRequestFrame = parent;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
return runtime;
|
|
399
|
+
}
|
|
400
|
+
function installDataFlowPrelude(config, target = globalThis) {
|
|
401
|
+
if (!config.enabled) return void 0;
|
|
402
|
+
const runtime = target[RUNTIME_KEY] ?? createDataFlowRuntime(config, target);
|
|
403
|
+
target[RUNTIME_KEY] = runtime;
|
|
404
|
+
return runtime;
|
|
405
|
+
}
|
|
406
|
+
function getDataFlowRuntime(target = globalThis) {
|
|
407
|
+
return target[RUNTIME_KEY];
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/data-flow/report-merger.ts
|
|
411
|
+
import {
|
|
412
|
+
isSensitiveName,
|
|
413
|
+
limitDataFlowReportCollections
|
|
414
|
+
} from "@spotpatch/shared";
|
|
415
|
+
function observationMatchesDependency(observation, dependency) {
|
|
416
|
+
const origin = dependency.origin;
|
|
417
|
+
const transportMatches = dependency.kind === "rpc" ? observation.transport === "trpc" && dependency.operation !== void 0 && observation.operation === dependency.operation : dependency.kind === "http" ? observation.transport === "fetch" || observation.transport === "xhr" : false;
|
|
418
|
+
if (!transportMatches || observation.freshness !== "current" || origin === void 0 || observation.requestCallsiteId !== origin.requestCallsiteId || observation.sourceVersion !== origin.sourceVersion || dependency.method !== void 0 && observation.method.toUpperCase() !== dependency.method.toUpperCase() || dependency.url !== void 0 && observation.url.pathname !== dependency.url.pathname || dependency.url?.origin !== void 0 && observation.url.origin !== dependency.url.origin || origin.componentSourceId !== void 0 && observation.componentSourceId !== void 0 && observation.componentSourceId !== origin.componentSourceId || origin.triggerCallsiteId !== void 0 && observation.triggerCallsiteId !== void 0 && observation.triggerCallsiteId !== origin.triggerCallsiteId) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
return dependency.association !== "transitive" || observation.componentSourceId === origin.componentSourceId && observation.triggerCallsiteId === origin.triggerCallsiteId;
|
|
422
|
+
}
|
|
423
|
+
function runtimeEvidence(observation) {
|
|
424
|
+
return Object.freeze({
|
|
425
|
+
id: observation.id,
|
|
426
|
+
kind: "runtime-observation",
|
|
427
|
+
summaryKey: "dataFlow.evidence.runtimeDispatch"
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
function mergeDependencies(dependencies, observations) {
|
|
431
|
+
const matchedObservationIds = /* @__PURE__ */ new Set();
|
|
432
|
+
const merged = dependencies.map((dependency) => {
|
|
433
|
+
const matches = observations.filter(
|
|
434
|
+
(observation) => observationMatchesDependency(observation, dependency)
|
|
435
|
+
);
|
|
436
|
+
for (const observation of matches) matchedObservationIds.add(observation.id);
|
|
437
|
+
if (matches.length === 0) return dependency;
|
|
438
|
+
const observedOrigins = [
|
|
439
|
+
...new Set(
|
|
440
|
+
matches.flatMap(({ url }) => url.origin === void 0 ? [] : [url.origin])
|
|
441
|
+
)
|
|
442
|
+
];
|
|
443
|
+
const observedOrigin = observedOrigins.length === 1 ? observedOrigins[0] : void 0;
|
|
444
|
+
return Object.freeze({
|
|
445
|
+
...dependency,
|
|
446
|
+
...dependency.url === void 0 || dependency.url.origin !== void 0 || observedOrigin === void 0 ? {} : {
|
|
447
|
+
url: Object.freeze({
|
|
448
|
+
...dependency.url,
|
|
449
|
+
origin: observedOrigin
|
|
450
|
+
})
|
|
451
|
+
},
|
|
452
|
+
execution: "observed",
|
|
453
|
+
observationIds: Object.freeze([
|
|
454
|
+
.../* @__PURE__ */ new Set([...dependency.observationIds, ...matches.map(({ id }) => id)])
|
|
455
|
+
]),
|
|
456
|
+
evidenceIds: Object.freeze([
|
|
457
|
+
.../* @__PURE__ */ new Set([...dependency.evidenceIds, ...matches.map(({ id }) => id)])
|
|
458
|
+
])
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
return Object.freeze({
|
|
462
|
+
dependencies: Object.freeze(merged),
|
|
463
|
+
matchedObservationIds
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
function appendRuntimeEvidence(evidence, observations, matchedIds) {
|
|
467
|
+
const existing = new Set(evidence.map(({ id }) => id));
|
|
468
|
+
return Object.freeze([
|
|
469
|
+
...evidence,
|
|
470
|
+
...observations.flatMap(
|
|
471
|
+
(observation) => matchedIds.has(observation.id) && !existing.has(observation.id) ? [runtimeEvidence(observation)] : []
|
|
472
|
+
)
|
|
473
|
+
]);
|
|
474
|
+
}
|
|
475
|
+
function mergeComponentDataFlowReport(report, observations) {
|
|
476
|
+
const componentObservations = observations.filter(
|
|
477
|
+
(observation) => observation.componentSourceId === void 0 || observation.componentSourceId === report.component.componentSourceId
|
|
478
|
+
);
|
|
479
|
+
const merged = mergeDependencies(report.dependencies, componentObservations);
|
|
480
|
+
return limitDataFlowReportCollections(
|
|
481
|
+
Object.freeze({
|
|
482
|
+
...report,
|
|
483
|
+
dependencies: merged.dependencies,
|
|
484
|
+
evidence: appendRuntimeEvidence(
|
|
485
|
+
report.evidence,
|
|
486
|
+
componentObservations,
|
|
487
|
+
merged.matchedObservationIds
|
|
488
|
+
)
|
|
489
|
+
}),
|
|
490
|
+
{ mode: "observation" }
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
function unassignedDependency(observation) {
|
|
494
|
+
const isRpc = observation.transport === "trpc";
|
|
495
|
+
return Object.freeze({
|
|
496
|
+
id: observation.id,
|
|
497
|
+
kind: isRpc ? "rpc" : "http",
|
|
498
|
+
direction: observation.method === "GET" || observation.method === "HEAD" || observation.method === "QUERY" || observation.method === "SUBSCRIPTION" ? "read" : "write",
|
|
499
|
+
execution: "observed",
|
|
500
|
+
proof: "unavailable",
|
|
501
|
+
association: "unassigned",
|
|
502
|
+
method: observation.method,
|
|
503
|
+
...isRpc ? observation.operation === void 0 ? {} : { operation: observation.operation } : { url: observation.url },
|
|
504
|
+
parameters: Object.freeze(
|
|
505
|
+
(isRpc ? [] : observation.url.queryKeys).map(
|
|
506
|
+
(path) => Object.freeze({
|
|
507
|
+
path,
|
|
508
|
+
position: "query",
|
|
509
|
+
sensitive: isSensitiveName(path),
|
|
510
|
+
valueState: "not-collected",
|
|
511
|
+
evidenceIds: Object.freeze([observation.id])
|
|
512
|
+
})
|
|
513
|
+
)
|
|
514
|
+
),
|
|
515
|
+
response: Object.freeze({
|
|
516
|
+
consumedFields: Object.freeze([])
|
|
517
|
+
}),
|
|
518
|
+
suppliedBindings: Object.freeze([]),
|
|
519
|
+
locationIds: Object.freeze([]),
|
|
520
|
+
evidenceIds: Object.freeze([observation.id]),
|
|
521
|
+
observationIds: Object.freeze([observation.id])
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
function mergePageDataFlowReport(report, observations) {
|
|
525
|
+
const currentObservations = observations.filter(
|
|
526
|
+
({ freshness }) => freshness === "current"
|
|
527
|
+
);
|
|
528
|
+
const merged = mergeDependencies(report.dependencies, currentObservations);
|
|
529
|
+
const unassigned = currentObservations.filter(({ id }) => !merged.matchedObservationIds.has(id)).map(unassignedDependency);
|
|
530
|
+
const allObservationIds = new Set(currentObservations.map(({ id }) => id));
|
|
531
|
+
return limitDataFlowReportCollections(
|
|
532
|
+
Object.freeze({
|
|
533
|
+
...report,
|
|
534
|
+
dependencies: Object.freeze([...merged.dependencies, ...unassigned]),
|
|
535
|
+
evidence: appendRuntimeEvidence(
|
|
536
|
+
report.evidence,
|
|
537
|
+
currentObservations,
|
|
538
|
+
allObservationIds
|
|
539
|
+
)
|
|
540
|
+
}),
|
|
541
|
+
{ mode: "observation" }
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export {
|
|
546
|
+
createDataFlowRuntime,
|
|
547
|
+
installDataFlowPrelude,
|
|
548
|
+
getDataFlowRuntime,
|
|
549
|
+
mergeComponentDataFlowReport,
|
|
550
|
+
mergePageDataFlowReport
|
|
551
|
+
};
|
|
552
|
+
//# sourceMappingURL=chunk-QY5T4DPA.js.map
|