@party-stack/ontology-devtools 0.6.2 → 0.7.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/README.md +195 -0
- package/lib/DisclosureSummary.d.ts +7 -0
- package/lib/DisclosureSummary.d.ts.map +1 -0
- package/lib/DisclosureSummary.js +20 -0
- package/lib/DisclosureSummary.js.map +1 -0
- package/lib/NativeQueryCode.d.ts +7 -0
- package/lib/NativeQueryCode.d.ts.map +1 -0
- package/lib/NativeQueryCode.js +35 -0
- package/lib/NativeQueryCode.js.map +1 -0
- package/lib/OntologyDevtools.d.ts +11 -50
- package/lib/OntologyDevtools.d.ts.map +1 -1
- package/lib/OntologyDevtools.js +310 -626
- package/lib/OntologyDevtools.js.map +1 -1
- package/lib/OntologyIcons.d.ts +14 -0
- package/lib/OntologyIcons.d.ts.map +1 -0
- package/lib/OntologyIcons.js +22 -0
- package/lib/OntologyIcons.js.map +1 -0
- package/lib/PanelBoundary.d.ts +10 -0
- package/lib/PanelBoundary.d.ts.map +1 -0
- package/lib/PanelBoundary.js +67 -0
- package/lib/PanelBoundary.js.map +1 -0
- package/lib/Pinned.d.ts +14 -0
- package/lib/Pinned.d.ts.map +1 -0
- package/lib/Pinned.js +142 -0
- package/lib/Pinned.js.map +1 -0
- package/lib/PinnedFamily.d.ts +13 -0
- package/lib/PinnedFamily.d.ts.map +1 -0
- package/lib/PinnedFamily.js +105 -0
- package/lib/PinnedFamily.js.map +1 -0
- package/lib/Queries.d.ts +12 -0
- package/lib/Queries.d.ts.map +1 -0
- package/lib/Queries.js +147 -0
- package/lib/Queries.js.map +1 -0
- package/lib/QueryOwners.d.ts +7 -0
- package/lib/QueryOwners.d.ts.map +1 -0
- package/lib/QueryOwners.js +46 -0
- package/lib/QueryOwners.js.map +1 -0
- package/lib/QueryPills.d.ts +17 -0
- package/lib/QueryPills.d.ts.map +1 -0
- package/lib/QueryPills.js +37 -0
- package/lib/QueryPills.js.map +1 -0
- package/lib/QueryPushdown.d.ts +26 -0
- package/lib/QueryPushdown.d.ts.map +1 -0
- package/lib/QueryPushdown.js +380 -0
- package/lib/QueryPushdown.js.map +1 -0
- package/lib/QueryTokens.d.ts +25 -0
- package/lib/QueryTokens.d.ts.map +1 -0
- package/lib/QueryTokens.js +49 -0
- package/lib/QueryTokens.js.map +1 -0
- package/lib/QueryView.d.ts +37 -0
- package/lib/QueryView.d.ts.map +1 -0
- package/lib/QueryView.js +547 -0
- package/lib/QueryView.js.map +1 -0
- package/lib/SchemaView.d.ts +8 -0
- package/lib/SchemaView.d.ts.map +1 -0
- package/lib/SchemaView.js +213 -0
- package/lib/SchemaView.js.map +1 -0
- package/lib/Storage.d.ts +17 -0
- package/lib/Storage.d.ts.map +1 -0
- package/lib/Storage.js +205 -0
- package/lib/Storage.js.map +1 -0
- package/lib/Tracing.d.ts +7 -0
- package/lib/Tracing.d.ts.map +1 -0
- package/lib/Tracing.js +344 -0
- package/lib/Tracing.js.map +1 -0
- package/lib/attribution/Highlight.d.ts +9 -0
- package/lib/attribution/Highlight.d.ts.map +1 -0
- package/lib/attribution/Highlight.js +73 -0
- package/lib/attribution/Highlight.js.map +1 -0
- package/lib/attribution/index.d.ts +12 -0
- package/lib/attribution/index.d.ts.map +1 -0
- package/lib/attribution/index.js +31 -0
- package/lib/attribution/index.js.map +1 -0
- package/lib/attribution/registry.d.ts +16 -0
- package/lib/attribution/registry.d.ts.map +1 -0
- package/lib/attribution/registry.js +31 -0
- package/lib/attribution/registry.js.map +1 -0
- package/lib/attribution/tanstack.d.ts +11 -0
- package/lib/attribution/tanstack.d.ts.map +1 -0
- package/lib/attribution/tanstack.js +81 -0
- package/lib/attribution/tanstack.js.map +1 -0
- package/lib/chrome.d.ts +9 -0
- package/lib/chrome.d.ts.map +1 -0
- package/lib/chrome.js +41 -0
- package/lib/chrome.js.map +1 -0
- package/lib/diagnostics.d.ts +15 -0
- package/lib/diagnostics.d.ts.map +1 -0
- package/lib/diagnostics.js +31 -0
- package/lib/diagnostics.js.map +1 -0
- package/lib/index.d.ts +4 -2
- package/lib/index.js +3 -1
- package/lib/plugin.d.ts +11 -0
- package/lib/plugin.d.ts.map +1 -0
- package/lib/plugin.js +62 -0
- package/lib/plugin.js.map +1 -0
- package/lib/queryIR.d.ts +9 -0
- package/lib/queryIR.d.ts.map +1 -0
- package/lib/queryIR.js +69 -0
- package/lib/queryIR.js.map +1 -0
- package/lib/replicationActivity.d.ts +52 -0
- package/lib/replicationActivity.d.ts.map +1 -0
- package/lib/replicationActivity.js +54 -0
- package/lib/replicationActivity.js.map +1 -0
- package/lib/schemaLayout.d.ts +35 -0
- package/lib/schemaLayout.d.ts.map +1 -0
- package/lib/schemaLayout.js +75 -0
- package/lib/schemaLayout.js.map +1 -0
- package/lib/styles.js +6 -0
- package/lib/styles.js.map +1 -0
- package/lib/traceTimeline.d.ts +15 -0
- package/lib/traceTimeline.d.ts.map +1 -0
- package/lib/traceTimeline.js +36 -0
- package/lib/traceTimeline.js.map +1 -0
- package/lib/traceWaterfall.d.ts +25 -0
- package/lib/traceWaterfall.d.ts.map +1 -0
- package/lib/traceWaterfall.js +70 -0
- package/lib/traceWaterfall.js.map +1 -0
- package/lib/typeDisplayName.d.ts +5 -0
- package/lib/typeDisplayName.d.ts.map +1 -0
- package/lib/typeDisplayName.js +28 -0
- package/lib/typeDisplayName.js.map +1 -0
- package/lib/types.d.ts +17 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +1 -0
- package/lib/vite/index.d.ts +18 -0
- package/lib/vite/index.d.ts.map +1 -0
- package/lib/vite/index.js +148 -0
- package/lib/vite/index.js.map +1 -0
- package/package.json +25 -8
- package/lib/styles.css +0 -2
package/lib/Tracing.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { diagnosticSpans } from "./replicationActivity.js";
|
|
2
|
+
import { diagnosticTargets, ms, useReplicaStatus, useTraceSpans } from "./diagnostics.js";
|
|
3
|
+
import { QueryPills } from "./QueryPills.js";
|
|
4
|
+
import { traceWaterfall } from "./traceWaterfall.js";
|
|
5
|
+
import { isSubscription, operationRows, spanKey, spanSubtree, traceStatus } from "./traceTimeline.js";
|
|
6
|
+
import { Box, ChevronDown, Database, Filter, Layers, Plug, Radio, RefreshCw, Send, Trash2, Workflow, X } from "lucide-react";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { useEffect, useMemo, useState } from "react";
|
|
9
|
+
//#region src/Tracing.tsx
|
|
10
|
+
function Tracing({ ontology }) {
|
|
11
|
+
const targets = diagnosticTargets(ontology.diagnostics);
|
|
12
|
+
const [index, setIndex] = useState(() => ontology.diagnostics?.parent ? 1 : 0);
|
|
13
|
+
const target = targets[index] ?? targets[0];
|
|
14
|
+
return /* @__PURE__ */ jsx("div", {
|
|
15
|
+
className: "ps-diagnostics-screen",
|
|
16
|
+
"aria-label": "Tracing inspector",
|
|
17
|
+
children: /* @__PURE__ */ jsx(Timeline, {
|
|
18
|
+
diagnostics: target?.value,
|
|
19
|
+
traces: target ? target.value.traces : ontology.traces,
|
|
20
|
+
ontology,
|
|
21
|
+
source: targets.length > 1 ? /* @__PURE__ */ jsxs("span", {
|
|
22
|
+
className: "ps-trace-source-select",
|
|
23
|
+
children: [/* @__PURE__ */ jsx("select", {
|
|
24
|
+
"aria-label": "Trace source",
|
|
25
|
+
value: index,
|
|
26
|
+
onChange: (event) => setIndex(Number(event.target.value)),
|
|
27
|
+
children: targets.map((item, key) => /* @__PURE__ */ jsx("option", {
|
|
28
|
+
value: key,
|
|
29
|
+
children: item.label
|
|
30
|
+
}, key))
|
|
31
|
+
}), /* @__PURE__ */ jsx(ChevronDown, {
|
|
32
|
+
size: 12,
|
|
33
|
+
"aria-hidden": true
|
|
34
|
+
})]
|
|
35
|
+
}) : /* @__PURE__ */ jsx("strong", { children: target?.label ?? "This tab" })
|
|
36
|
+
}, index)
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function Timeline({ diagnostics, traces, ontology, source }) {
|
|
40
|
+
const { status, error } = useReplicaStatus(diagnostics);
|
|
41
|
+
const recorded = useTraceSpans(traces);
|
|
42
|
+
const incoming = useMemo(() => diagnosticSpans(recorded, status?.loads ?? []), [recorded, status?.loads]);
|
|
43
|
+
const [paused, pause] = useState();
|
|
44
|
+
const [now, setNow] = useState(() => Date.now());
|
|
45
|
+
const [selected, select] = useState();
|
|
46
|
+
const [all, showAll] = useState(false);
|
|
47
|
+
const [sort, setSort] = useState({
|
|
48
|
+
by: "startedAt",
|
|
49
|
+
desc: true
|
|
50
|
+
});
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (paused) return;
|
|
53
|
+
const timer = setInterval(() => setNow(Date.now()), 250);
|
|
54
|
+
return () => clearInterval(timer);
|
|
55
|
+
}, [paused]);
|
|
56
|
+
const spans = paused?.spans ?? incoming;
|
|
57
|
+
const clock = paused?.now ?? now;
|
|
58
|
+
const loads = paused?.loads ?? status?.loads ?? [];
|
|
59
|
+
const requests = new Map(loads.filter((load) => load.spanId && load.traceId).map((load) => [`${load.traceId}:${load.spanId}`, load]));
|
|
60
|
+
const chart = useMemo(() => operationRows(spans, clock), [spans, clock]);
|
|
61
|
+
const groups = [];
|
|
62
|
+
for (const row of chart) {
|
|
63
|
+
if (!row.depth || !groups.length) groups.push([]);
|
|
64
|
+
groups[groups.length - 1].push(row);
|
|
65
|
+
}
|
|
66
|
+
groups.sort((a, b) => {
|
|
67
|
+
const left = a[0], right = b[0];
|
|
68
|
+
const diff = sort.by === "name" ? left.span.name.localeCompare(right.span.name) : sort.by === "status" ? left.span.outcome.localeCompare(right.span.outcome) : sort.by === "duration" ? left.durationMs - right.durationMs : left.span.startedAt - right.span.startedAt;
|
|
69
|
+
return (sort.desc ? -1 : 1) * diff;
|
|
70
|
+
});
|
|
71
|
+
const rows = groups.flatMap((group) => all ? group : group.slice(0, 1));
|
|
72
|
+
const current = selected ? spans.find((span) => spanKey(span) === spanKey(selected)) ?? selected : void 0;
|
|
73
|
+
const sortHeader = (by, title) => /* @__PURE__ */ jsx("th", {
|
|
74
|
+
className: by === "duration" ? "ps-trace-numeric" : void 0,
|
|
75
|
+
"aria-sort": sort.by === by ? sort.desc ? "descending" : "ascending" : "none",
|
|
76
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
77
|
+
type: "button",
|
|
78
|
+
onClick: () => setSort({
|
|
79
|
+
by,
|
|
80
|
+
desc: sort.by === by ? !sort.desc : by !== "name"
|
|
81
|
+
}),
|
|
82
|
+
children: [
|
|
83
|
+
by === "duration" && /* @__PURE__ */ jsx("span", {
|
|
84
|
+
"aria-hidden": true,
|
|
85
|
+
children: sort.by === by ? sort.desc ? "↓" : "↑" : "↕"
|
|
86
|
+
}),
|
|
87
|
+
/* @__PURE__ */ jsx("span", { children: title }),
|
|
88
|
+
by !== "duration" && /* @__PURE__ */ jsx("span", {
|
|
89
|
+
"aria-hidden": true,
|
|
90
|
+
children: sort.by === by ? sort.desc ? "↓" : "↑" : "↕"
|
|
91
|
+
})
|
|
92
|
+
]
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
96
|
+
/* @__PURE__ */ jsxs("header", {
|
|
97
|
+
className: "ps-diagnostics-toolbar ps-trace-toolbar",
|
|
98
|
+
children: [
|
|
99
|
+
/* @__PURE__ */ jsxs("div", {
|
|
100
|
+
className: "ps-trace-source",
|
|
101
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Source" }), source]
|
|
102
|
+
}),
|
|
103
|
+
/* @__PURE__ */ jsx("span", {
|
|
104
|
+
className: "ps-trace-toolbar-separator",
|
|
105
|
+
"aria-hidden": true
|
|
106
|
+
}),
|
|
107
|
+
/* @__PURE__ */ jsxs("label", { children: [/* @__PURE__ */ jsx("input", {
|
|
108
|
+
type: "checkbox",
|
|
109
|
+
checked: all,
|
|
110
|
+
onChange: (event) => showAll(event.target.checked)
|
|
111
|
+
}), "Child spans"] }),
|
|
112
|
+
/* @__PURE__ */ jsx("span", {
|
|
113
|
+
className: "ps-trace-toolbar-separator",
|
|
114
|
+
"aria-hidden": true
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ jsx("button", {
|
|
117
|
+
type: "button",
|
|
118
|
+
"aria-pressed": Boolean(paused),
|
|
119
|
+
onClick: () => {
|
|
120
|
+
if (paused) {
|
|
121
|
+
setNow(Date.now());
|
|
122
|
+
pause(void 0);
|
|
123
|
+
} else pause({
|
|
124
|
+
spans: incoming,
|
|
125
|
+
loads,
|
|
126
|
+
now: Date.now()
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
children: paused ? "Resume live" : "Pause"
|
|
130
|
+
})
|
|
131
|
+
]
|
|
132
|
+
}),
|
|
133
|
+
error && /* @__PURE__ */ jsxs("p", {
|
|
134
|
+
role: "alert",
|
|
135
|
+
className: "ps-replication-error",
|
|
136
|
+
children: [error, " · trace data may be stale"]
|
|
137
|
+
}),
|
|
138
|
+
/* @__PURE__ */ jsxs("div", {
|
|
139
|
+
className: "ps-timeline-scroll ps-object-table-scroll",
|
|
140
|
+
"aria-label": "Tracing timeline viewport",
|
|
141
|
+
children: [/* @__PURE__ */ jsxs("table", {
|
|
142
|
+
className: "ps-timeline",
|
|
143
|
+
"aria-label": "Tracing timeline",
|
|
144
|
+
children: [
|
|
145
|
+
/* @__PURE__ */ jsxs("colgroup", { children: [
|
|
146
|
+
/* @__PURE__ */ jsx("col", { style: { width: 330 } }),
|
|
147
|
+
/* @__PURE__ */ jsx("col", { style: { width: 100 } }),
|
|
148
|
+
/* @__PURE__ */ jsx("col", { style: { width: 110 } }),
|
|
149
|
+
/* @__PURE__ */ jsx("col", { style: { width: 110 } }),
|
|
150
|
+
/* @__PURE__ */ jsx("col", { style: { width: 85 } }),
|
|
151
|
+
/* @__PURE__ */ jsx("col", {})
|
|
152
|
+
] }),
|
|
153
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
154
|
+
sortHeader("name", "Operation"),
|
|
155
|
+
sortHeader("status", "Status"),
|
|
156
|
+
sortHeader("startedAt", "Started"),
|
|
157
|
+
sortHeader("duration", "Duration"),
|
|
158
|
+
/* @__PURE__ */ jsx("th", {
|
|
159
|
+
className: "ps-trace-numeric",
|
|
160
|
+
title: "Recorded subscription lifecycle and invalidation events; not row updates, network frames or consumer counts",
|
|
161
|
+
children: "Events"
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ jsx("th", { "aria-hidden": true })
|
|
164
|
+
] }) }),
|
|
165
|
+
/* @__PURE__ */ jsx("tbody", { children: rows.map((row) => {
|
|
166
|
+
const id = spanKey(row.span), subscription = isSubscription(row.span);
|
|
167
|
+
const request = requests.get(id);
|
|
168
|
+
return /* @__PURE__ */ jsxs("tr", {
|
|
169
|
+
"data-selected": current && spanKey(current) === id,
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("button", {
|
|
172
|
+
type: "button",
|
|
173
|
+
className: "ps-timeline-name",
|
|
174
|
+
style: { paddingLeft: 10 + Math.min(row.depth, 8) * 12 },
|
|
175
|
+
title: row.span.name,
|
|
176
|
+
"aria-label": row.span.name,
|
|
177
|
+
onClick: () => select(row.span),
|
|
178
|
+
children: [
|
|
179
|
+
/* @__PURE__ */ jsx(ActivityIcon, { span: row.span }),
|
|
180
|
+
row.depth > 0 && /* @__PURE__ */ jsx("span", {
|
|
181
|
+
className: "ps-timeline-branch",
|
|
182
|
+
children: "↳"
|
|
183
|
+
}),
|
|
184
|
+
row.span.name,
|
|
185
|
+
request?.subset && /* @__PURE__ */ jsxs("small", { children: [" · ", request.subset.objectType] })
|
|
186
|
+
]
|
|
187
|
+
}) }),
|
|
188
|
+
/* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("span", {
|
|
189
|
+
className: "ps-trace-status",
|
|
190
|
+
"data-outcome": row.span.outcome,
|
|
191
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
192
|
+
className: "ps-trace-status-dot",
|
|
193
|
+
"aria-hidden": true
|
|
194
|
+
}), traceStatus(row.span)]
|
|
195
|
+
}) }),
|
|
196
|
+
/* @__PURE__ */ jsx("td", {
|
|
197
|
+
className: "ps-timeline-time",
|
|
198
|
+
children: new Date(row.span.startedAt).toLocaleTimeString()
|
|
199
|
+
}),
|
|
200
|
+
/* @__PURE__ */ jsx("td", {
|
|
201
|
+
className: "ps-timeline-duration",
|
|
202
|
+
children: ms(row.durationMs)
|
|
203
|
+
}),
|
|
204
|
+
/* @__PURE__ */ jsx("td", {
|
|
205
|
+
className: "ps-timeline-messages",
|
|
206
|
+
children: subscription && row.span.eventCount !== void 0 ? row.span.eventCount : "—"
|
|
207
|
+
}),
|
|
208
|
+
/* @__PURE__ */ jsx("td", { "aria-hidden": true })
|
|
209
|
+
]
|
|
210
|
+
}, id);
|
|
211
|
+
}) })
|
|
212
|
+
]
|
|
213
|
+
}), !rows.length && /* @__PURE__ */ jsx("p", {
|
|
214
|
+
className: "ps-replication-empty",
|
|
215
|
+
children: "No recorded operations."
|
|
216
|
+
})]
|
|
217
|
+
}),
|
|
218
|
+
current && /* @__PURE__ */ jsx(TraceDetails, {
|
|
219
|
+
span: current,
|
|
220
|
+
spans,
|
|
221
|
+
load: requests.get(spanKey(current)),
|
|
222
|
+
now: clock,
|
|
223
|
+
ontology,
|
|
224
|
+
onClose: () => select(void 0)
|
|
225
|
+
})
|
|
226
|
+
] });
|
|
227
|
+
}
|
|
228
|
+
function ActivityIcon({ span }) {
|
|
229
|
+
const operation = span.name.split(".").at(-1)?.toLowerCase() ?? "";
|
|
230
|
+
const Icon = operation === "collectgarbage" ? Trash2 : operation === "sync" || operation === "syncparent" ? RefreshCw : operation === "demand" ? Filter : operation.startsWith("watch") || isSubscription(span) ? Radio : operation.startsWith("connect") ? Plug : operation === "replay" || operation === "rebase" ? Layers : operation === "dispatch" || operation.startsWith("submit") || operation === "enqueue" ? Send : operation.startsWith("load") || operation.startsWith("read") || operation === "pullchanges" ? Database : operation === "make" || operation === "open" || operation === "view" ? Box : Workflow;
|
|
231
|
+
return /* @__PURE__ */ jsx(Icon, {
|
|
232
|
+
className: "ps-trace-icon",
|
|
233
|
+
"data-outcome": span.outcome,
|
|
234
|
+
"aria-hidden": true,
|
|
235
|
+
size: 13
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function TraceDetails({ span, spans, load, now, ontology, onClose }) {
|
|
239
|
+
const object = ontology.ir.objectTypes.find((type) => type.name === load?.subset?.objectType);
|
|
240
|
+
const subscription = isSubscription(span);
|
|
241
|
+
const subtree = spanSubtree(spans, span);
|
|
242
|
+
const chart = traceWaterfall(subtree, now);
|
|
243
|
+
return /* @__PURE__ */ jsxs("aside", {
|
|
244
|
+
className: "ps-timeline-details",
|
|
245
|
+
"aria-label": "Trace details",
|
|
246
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
247
|
+
className: "ps-timeline-details-heading",
|
|
248
|
+
children: [
|
|
249
|
+
/* @__PURE__ */ jsx("button", {
|
|
250
|
+
type: "button",
|
|
251
|
+
"aria-label": "Close trace details",
|
|
252
|
+
onClick: onClose,
|
|
253
|
+
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
254
|
+
}),
|
|
255
|
+
/* @__PURE__ */ jsx(ActivityIcon, { span }),
|
|
256
|
+
/* @__PURE__ */ jsx("strong", { children: span.name })
|
|
257
|
+
]
|
|
258
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
259
|
+
className: "ps-trace-detail-body",
|
|
260
|
+
children: [
|
|
261
|
+
/* @__PURE__ */ jsxs("div", {
|
|
262
|
+
className: "ps-timeline-attributes",
|
|
263
|
+
children: [
|
|
264
|
+
/* @__PURE__ */ jsx("span", {
|
|
265
|
+
className: "ps-trace-status",
|
|
266
|
+
"data-outcome": span.outcome,
|
|
267
|
+
children: traceStatus(span)
|
|
268
|
+
}),
|
|
269
|
+
/* @__PURE__ */ jsx("span", { children: ms(span.outcome === "running" ? Math.max(0, now - span.startedAt) : span.durationMs) }),
|
|
270
|
+
load?.fetchedRows !== void 0 && /* @__PURE__ */ jsxs("span", { children: [load.fetchedRows, " fetched"] }),
|
|
271
|
+
load?.rows !== void 0 && /* @__PURE__ */ jsxs("span", { children: [load.rows, " returned"] }),
|
|
272
|
+
Object.entries(span.attributes).filter(([key]) => key !== "activity").map(([key, value]) => /* @__PURE__ */ jsxs("span", { children: [
|
|
273
|
+
key,
|
|
274
|
+
": ",
|
|
275
|
+
String(value)
|
|
276
|
+
] }, key))
|
|
277
|
+
]
|
|
278
|
+
}),
|
|
279
|
+
span.outcome === "interrupted" && /* @__PURE__ */ jsxs("p", {
|
|
280
|
+
className: "ps-trace-note",
|
|
281
|
+
children: [subscription ? "Subscription stopped" : "Operation cancelled", ": Effect was interrupted, for example when its consumer unsubscribes or its scope closes. No error was reported."]
|
|
282
|
+
}),
|
|
283
|
+
span.outcome === "failure" && /* @__PURE__ */ jsxs("p", {
|
|
284
|
+
className: "ps-replication-error",
|
|
285
|
+
children: [load?.error?.message ?? span.error?.message ?? "Effect failed; no error message was captured for this span.", span.error?.kind === "Defect" && " (unexpected defect)"]
|
|
286
|
+
}),
|
|
287
|
+
load?.subset && /* @__PURE__ */ jsx(QueryPills, {
|
|
288
|
+
query: load.subset.query,
|
|
289
|
+
object,
|
|
290
|
+
objectType: load.subset.objectType
|
|
291
|
+
}),
|
|
292
|
+
subscription ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
293
|
+
/* @__PURE__ */ jsxs("h4", { children: ["Events ", /* @__PURE__ */ jsx("span", { children: span.eventCount ?? 0 })] }),
|
|
294
|
+
(span.eventCount ?? 0) > (span.events?.length ?? 0) && /* @__PURE__ */ jsxs("p", {
|
|
295
|
+
className: "ps-trace-note",
|
|
296
|
+
children: [
|
|
297
|
+
"Latest ",
|
|
298
|
+
span.events?.length,
|
|
299
|
+
" events"
|
|
300
|
+
]
|
|
301
|
+
}),
|
|
302
|
+
/* @__PURE__ */ jsxs("table", {
|
|
303
|
+
className: "ps-trace-messages",
|
|
304
|
+
"aria-label": "Subscription events",
|
|
305
|
+
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", { children: "Time" }), /* @__PURE__ */ jsx("th", { children: "Event" })] }) }), /* @__PURE__ */ jsx("tbody", { children: span.events?.map((event, index) => /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("td", { children: new Date(event.at).toLocaleTimeString() }), /* @__PURE__ */ jsx("td", { children: event.name.replace("replica.", "") })] }, index)) })]
|
|
306
|
+
}),
|
|
307
|
+
!span.events?.length && /* @__PURE__ */ jsx("p", {
|
|
308
|
+
className: "ps-trace-note",
|
|
309
|
+
children: "No events recorded."
|
|
310
|
+
})
|
|
311
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("h4", { children: "Waterfall" }), /* @__PURE__ */ jsx("table", {
|
|
312
|
+
className: "ps-trace-waterfall",
|
|
313
|
+
"aria-label": "Operation waterfall",
|
|
314
|
+
children: /* @__PURE__ */ jsx("tbody", { children: chart.rows.map((row) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
315
|
+
/* @__PURE__ */ jsx("td", {
|
|
316
|
+
style: { paddingLeft: row.depth * 10 },
|
|
317
|
+
title: row.span.name,
|
|
318
|
+
children: row.span.name
|
|
319
|
+
}),
|
|
320
|
+
/* @__PURE__ */ jsx("td", {
|
|
321
|
+
"aria-label": `${ms(row.offset)} after start, ${ms(row.duration)} duration`,
|
|
322
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
323
|
+
className: "ps-timeline-hit",
|
|
324
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
325
|
+
className: "ps-timeline-bar",
|
|
326
|
+
"data-outcome": row.span.outcome,
|
|
327
|
+
style: {
|
|
328
|
+
left: `${row.offset / chart.duration * 100}%`,
|
|
329
|
+
width: `${row.duration / chart.duration * 100}%`
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
}),
|
|
334
|
+
/* @__PURE__ */ jsx("td", { children: ms(row.duration) })
|
|
335
|
+
] }, row.span.id)) })
|
|
336
|
+
})] })
|
|
337
|
+
]
|
|
338
|
+
})]
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
//#endregion
|
|
342
|
+
export { Tracing };
|
|
343
|
+
|
|
344
|
+
//# sourceMappingURL=Tracing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tracing.js","names":[],"sources":["../src/Tracing.tsx"],"sourcesContent":["import {\n Box,\n ChevronDown,\n Database,\n Filter,\n Layers,\n Plug,\n Radio,\n RefreshCw,\n Send,\n Trash2,\n Workflow,\n X,\n} from \"lucide-react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { diagnosticTargets, ms, useReplicaStatus, useTraceSpans } from \"./diagnostics.js\";\nimport { QueryPills } from \"./QueryPills.js\";\nimport { diagnosticSpans } from \"./replicationActivity.js\";\nimport { operationRows, spanKey, spanSubtree, isSubscription, traceStatus } from \"./traceTimeline.js\";\nimport { traceWaterfall } from \"./traceWaterfall.js\";\nimport type { Diagnostics, Load } from \"./diagnostics.js\";\nimport type { PerformanceSpan } from \"./traceWaterfall.js\";\nimport type { LiveOntology } from \"@party-stack/ontology\";\n\ntype Sort = \"name\" | \"startedAt\" | \"duration\" | \"status\";\nexport function Tracing({ ontology }: { ontology: LiveOntology }) {\n const targets = diagnosticTargets(ontology.diagnostics);\n const [index, setIndex] = useState(() => (ontology.diagnostics?.parent ? 1 : 0));\n const target = targets[index] ?? targets[0];\n return (\n <div className=\"ps-diagnostics-screen\" aria-label=\"Tracing inspector\">\n <Timeline\n key={index}\n diagnostics={target?.value}\n traces={target ? target.value.traces : ontology.traces}\n ontology={ontology}\n source={\n targets.length > 1 ? (\n <span className=\"ps-trace-source-select\">\n <select\n aria-label=\"Trace source\"\n value={index}\n onChange={(event) => setIndex(Number(event.target.value))}\n >\n {targets.map((item, key) => (\n <option value={key} key={key}>\n {item.label}\n </option>\n ))}\n </select>\n <ChevronDown size={12} aria-hidden />\n </span>\n ) : (\n <strong>{target?.label ?? \"This tab\"}</strong>\n )\n }\n />\n </div>\n );\n}\nfunction Timeline({\n diagnostics,\n traces,\n ontology,\n source,\n}: {\n diagnostics?: Diagnostics;\n traces?: LiveOntology[\"traces\"];\n ontology: LiveOntology;\n source: React.ReactNode;\n}) {\n const { status, error } = useReplicaStatus(diagnostics);\n const recorded = useTraceSpans(traces);\n const incoming = useMemo(() => diagnosticSpans(recorded, status?.loads ?? []), [recorded, status?.loads]);\n const [paused, pause] = useState<{\n spans: readonly PerformanceSpan[];\n loads: readonly Load[];\n now: number;\n }>();\n const [now, setNow] = useState(() => Date.now());\n const [selected, select] = useState<PerformanceSpan>();\n const [all, showAll] = useState(false);\n const [sort, setSort] = useState<{ by: Sort; desc: boolean }>({ by: \"startedAt\", desc: true });\n useEffect(() => {\n if (paused) return;\n const timer = setInterval(() => setNow(Date.now()), 250);\n return () => clearInterval(timer);\n }, [paused]);\n const spans = paused?.spans ?? incoming;\n const clock = paused?.now ?? now;\n const loads = paused?.loads ?? status?.loads ?? [];\n const requests = new Map(\n loads\n .filter((load) => load.spanId && load.traceId)\n .map((load) => [`${load.traceId}:${load.spanId}`, load])\n );\n const chart = useMemo(() => operationRows(spans, clock), [spans, clock]);\n const groups: (typeof chart)[] = [];\n for (const row of chart) {\n if (!row.depth || !groups.length) groups.push([]);\n groups[groups.length - 1]!.push(row);\n }\n groups.sort((a, b) => {\n const left = a[0]!,\n right = b[0]!;\n const diff =\n sort.by === \"name\"\n ? left.span.name.localeCompare(right.span.name)\n : sort.by === \"status\"\n ? left.span.outcome.localeCompare(right.span.outcome)\n : sort.by === \"duration\"\n ? left.durationMs - right.durationMs\n : left.span.startedAt - right.span.startedAt;\n return (sort.desc ? -1 : 1) * diff;\n });\n const rows = groups.flatMap((group) => (all ? group : group.slice(0, 1)));\n const current = selected\n ? (spans.find((span) => spanKey(span) === spanKey(selected)) ?? selected)\n : undefined;\n const sortHeader = (by: Sort, title: string) => (\n <th\n className={by === \"duration\" ? \"ps-trace-numeric\" : undefined}\n aria-sort={sort.by === by ? (sort.desc ? \"descending\" : \"ascending\") : \"none\"}\n >\n <button\n type=\"button\"\n onClick={() => setSort({ by, desc: sort.by === by ? !sort.desc : by !== \"name\" })}\n >\n {by === \"duration\" && (\n <span aria-hidden>{sort.by === by ? (sort.desc ? \"↓\" : \"↑\") : \"↕\"}</span>\n )}\n <span>{title}</span>\n {by !== \"duration\" && (\n <span aria-hidden>{sort.by === by ? (sort.desc ? \"↓\" : \"↑\") : \"↕\"}</span>\n )}\n </button>\n </th>\n );\n return (\n <>\n <header className=\"ps-diagnostics-toolbar ps-trace-toolbar\">\n <div className=\"ps-trace-source\">\n <span>Source</span>\n {source}\n </div>\n <span className=\"ps-trace-toolbar-separator\" aria-hidden />\n <label>\n <input\n type=\"checkbox\"\n checked={all}\n onChange={(event) => showAll(event.target.checked)}\n />\n Child spans\n </label>\n <span className=\"ps-trace-toolbar-separator\" aria-hidden />\n <button\n type=\"button\"\n aria-pressed={Boolean(paused)}\n onClick={() => {\n if (paused) {\n setNow(Date.now());\n pause(undefined);\n } else pause({ spans: incoming, loads, now: Date.now() });\n }}\n >\n {paused ? \"Resume live\" : \"Pause\"}\n </button>\n </header>\n {error && (\n <p role=\"alert\" className=\"ps-replication-error\">\n {error} · trace data may be stale\n </p>\n )}\n <div className=\"ps-timeline-scroll ps-object-table-scroll\" aria-label=\"Tracing timeline viewport\">\n <table className=\"ps-timeline\" aria-label=\"Tracing timeline\">\n <colgroup>\n <col style={{ width: 330 }} />\n <col style={{ width: 100 }} />\n <col style={{ width: 110 }} />\n <col style={{ width: 110 }} />\n <col style={{ width: 85 }} />\n <col />\n </colgroup>\n <thead>\n <tr>\n {sortHeader(\"name\", \"Operation\")}\n {sortHeader(\"status\", \"Status\")}\n {sortHeader(\"startedAt\", \"Started\")}\n {sortHeader(\"duration\", \"Duration\")}\n <th\n className=\"ps-trace-numeric\"\n title=\"Recorded subscription lifecycle and invalidation events; not row updates, network frames or consumer counts\"\n >\n Events\n </th>\n <th aria-hidden />\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => {\n const id = spanKey(row.span),\n subscription = isSubscription(row.span);\n const request = requests.get(id);\n return (\n <tr key={id} data-selected={current && spanKey(current) === id}>\n <td>\n <button\n type=\"button\"\n className=\"ps-timeline-name\"\n style={{ paddingLeft: 10 + Math.min(row.depth, 8) * 12 }}\n title={row.span.name}\n aria-label={row.span.name}\n onClick={() => select(row.span)}\n >\n <ActivityIcon span={row.span} />\n {row.depth > 0 && <span className=\"ps-timeline-branch\">↳</span>}\n {row.span.name}\n {request?.subset && <small> · {request.subset.objectType}</small>}\n </button>\n </td>\n <td>\n <span className=\"ps-trace-status\" data-outcome={row.span.outcome}>\n <span className=\"ps-trace-status-dot\" aria-hidden />\n {traceStatus(row.span)}\n </span>\n </td>\n <td className=\"ps-timeline-time\">\n {new Date(row.span.startedAt).toLocaleTimeString()}\n </td>\n <td className=\"ps-timeline-duration\">{ms(row.durationMs)}</td>\n <td className=\"ps-timeline-messages\">\n {subscription && row.span.eventCount !== undefined\n ? row.span.eventCount\n : \"—\"}\n </td>\n <td aria-hidden />\n </tr>\n );\n })}\n </tbody>\n </table>\n {!rows.length && <p className=\"ps-replication-empty\">No recorded operations.</p>}\n </div>\n {current && (\n <TraceDetails\n span={current}\n spans={spans}\n load={requests.get(spanKey(current))}\n now={clock}\n ontology={ontology}\n onClose={() => select(undefined)}\n />\n )}\n </>\n );\n}\nfunction ActivityIcon({ span }: { span: PerformanceSpan }) {\n const operation = span.name.split(\".\").at(-1)?.toLowerCase() ?? \"\";\n const Icon =\n operation === \"collectgarbage\"\n ? Trash2\n : operation === \"sync\" || operation === \"syncparent\"\n ? RefreshCw\n : operation === \"demand\"\n ? Filter\n : operation.startsWith(\"watch\") || isSubscription(span)\n ? Radio\n : operation.startsWith(\"connect\")\n ? Plug\n : operation === \"replay\" || operation === \"rebase\"\n ? Layers\n : operation === \"dispatch\" || operation.startsWith(\"submit\") || operation === \"enqueue\"\n ? Send\n : operation.startsWith(\"load\") ||\n operation.startsWith(\"read\") ||\n operation === \"pullchanges\"\n ? Database\n : operation === \"make\" || operation === \"open\" || operation === \"view\"\n ? Box\n : Workflow;\n return <Icon className=\"ps-trace-icon\" data-outcome={span.outcome} aria-hidden size={13} />;\n}\nfunction TraceDetails({\n span,\n spans,\n load,\n now,\n ontology,\n onClose,\n}: {\n span: PerformanceSpan;\n spans: readonly PerformanceSpan[];\n load?: Load;\n now: number;\n ontology: LiveOntology;\n onClose: () => void;\n}) {\n const object = ontology.ir.objectTypes.find((type) => type.name === load?.subset?.objectType);\n const subscription = isSubscription(span);\n const subtree = spanSubtree(spans, span);\n const chart = traceWaterfall(subtree, now);\n return (\n <aside className=\"ps-timeline-details\" aria-label=\"Trace details\">\n <div className=\"ps-timeline-details-heading\">\n <button type=\"button\" aria-label=\"Close trace details\" onClick={onClose}>\n <X size={14} />\n </button>\n <ActivityIcon span={span} />\n <strong>{span.name}</strong>\n </div>\n <div className=\"ps-trace-detail-body\">\n <div className=\"ps-timeline-attributes\">\n <span className=\"ps-trace-status\" data-outcome={span.outcome}>\n {traceStatus(span)}\n </span>\n <span>\n {ms(span.outcome === \"running\" ? Math.max(0, now - span.startedAt) : span.durationMs)}\n </span>\n {load?.fetchedRows !== undefined && <span>{load.fetchedRows} fetched</span>}\n {load?.rows !== undefined && <span>{load.rows} returned</span>}\n {Object.entries(span.attributes)\n .filter(([key]) => key !== \"activity\")\n .map(([key, value]) => (\n <span key={key}>\n {key}: {String(value)}\n </span>\n ))}\n </div>\n {span.outcome === \"interrupted\" && (\n <p className=\"ps-trace-note\">\n {subscription ? \"Subscription stopped\" : \"Operation cancelled\"}: Effect was\n interrupted, for example when its consumer unsubscribes or its scope closes. No error\n was reported.\n </p>\n )}\n {span.outcome === \"failure\" && (\n <p className=\"ps-replication-error\">\n {load?.error?.message ??\n span.error?.message ??\n \"Effect failed; no error message was captured for this span.\"}\n {span.error?.kind === \"Defect\" && \" (unexpected defect)\"}\n </p>\n )}\n {load?.subset && (\n <QueryPills\n query={load.subset.query}\n object={object}\n objectType={load.subset.objectType}\n />\n )}\n {subscription ? (\n <>\n <h4>\n Events <span>{span.eventCount ?? 0}</span>\n </h4>\n {(span.eventCount ?? 0) > (span.events?.length ?? 0) && (\n <p className=\"ps-trace-note\">Latest {span.events?.length} events</p>\n )}\n <table className=\"ps-trace-messages\" aria-label=\"Subscription events\">\n <thead>\n <tr>\n <th>Time</th>\n <th>Event</th>\n </tr>\n </thead>\n <tbody>\n {span.events?.map((event, index) => (\n <tr key={index}>\n <td>{new Date(event.at).toLocaleTimeString()}</td>\n <td>{event.name.replace(\"replica.\", \"\")}</td>\n </tr>\n ))}\n </tbody>\n </table>\n {!span.events?.length && <p className=\"ps-trace-note\">No events recorded.</p>}\n </>\n ) : (\n <>\n <h4>Waterfall</h4>\n <table className=\"ps-trace-waterfall\" aria-label=\"Operation waterfall\">\n <tbody>\n {chart.rows.map((row) => (\n <tr key={row.span.id}>\n <td style={{ paddingLeft: row.depth * 10 }} title={row.span.name}>\n {row.span.name}\n </td>\n <td\n aria-label={`${ms(row.offset)} after start, ${ms(row.duration)} duration`}\n >\n <div className=\"ps-timeline-hit\">\n <span\n className=\"ps-timeline-bar\"\n data-outcome={row.span.outcome}\n style={{\n left: `${(row.offset / chart.duration) * 100}%`,\n width: `${(row.duration / chart.duration) * 100}%`,\n }}\n />\n </div>\n </td>\n <td>{ms(row.duration)}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </>\n )}\n </div>\n </aside>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { DemandOwner } from "./registry.js";
|
|
2
|
+
//#region src/attribution/Highlight.d.ts
|
|
3
|
+
export declare function ownerElements(owners: readonly DemandOwner[], root?: ParentNode): HTMLElement[];
|
|
4
|
+
export declare function revealDemandOwners(owners: readonly DemandOwner[]): void;
|
|
5
|
+
export declare function DemandHighlight({ owners }: {
|
|
6
|
+
owners: readonly DemandOwner[];
|
|
7
|
+
}): import("react").ReactPortal | null;
|
|
8
|
+
//#endregion
|
|
9
|
+
//# sourceMappingURL=Highlight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Highlight.d.ts","names":[],"sources":["../../src/attribution/Highlight.tsx"],"mappings":";;wBAKgB,cAAc,iBAAiB,eAAe,OAAM,aAAwB;wBAgB5E,mBAAmB,iBAAiB;wBAqBpC,kBAAkB;EAAY,iBAAiB;oBAAe"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { usePanelPortal } from "../PanelBoundary.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
//#region src/attribution/Highlight.tsx
|
|
6
|
+
function ownerElements(owners, root = document) {
|
|
7
|
+
const ids = new Set(owners.map((owner) => owner.id));
|
|
8
|
+
const panels = [...root.querySelectorAll("[data-ps-devtools]")];
|
|
9
|
+
const candidates = [...root.querySelectorAll("[data-ps-demand-owner]")].filter((element) => ids.has(element.dataset.psDemandOwner ?? "") && !element.closest("[data-ps-devtools]") && !panels.some((panel) => element.contains(panel)));
|
|
10
|
+
const selected = new Set(candidates);
|
|
11
|
+
return candidates.filter((element) => {
|
|
12
|
+
for (let parent = element.parentElement; parent; parent = parent.parentElement) if (selected.has(parent)) return false;
|
|
13
|
+
return true;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
function revealDemandOwners(owners) {
|
|
17
|
+
const elements = ownerElements(owners);
|
|
18
|
+
(elements.find((element) => {
|
|
19
|
+
const rect = element.getBoundingClientRect();
|
|
20
|
+
return rect.bottom < 0 || rect.top > window.innerHeight;
|
|
21
|
+
}) ?? elements[0])?.scrollIntoView({
|
|
22
|
+
block: "center",
|
|
23
|
+
inline: "nearest",
|
|
24
|
+
behavior: window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "instant" : "smooth"
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function DemandHighlight({ owners }) {
|
|
28
|
+
const portal = usePanelPortal();
|
|
29
|
+
const [boxes, setBoxes] = useState([]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!owners.length) return;
|
|
32
|
+
let frame;
|
|
33
|
+
const measure = () => {
|
|
34
|
+
const next = ownerElements(owners).flatMap((element, index) => {
|
|
35
|
+
const rect = element.getBoundingClientRect();
|
|
36
|
+
if (!rect.width || !rect.height || rect.bottom <= 0 || rect.top >= window.innerHeight) return [];
|
|
37
|
+
return [{
|
|
38
|
+
id: `${element.dataset.psDemandOwner}:${index}`,
|
|
39
|
+
name: owners.find((owner) => owner.id === element.dataset.psDemandOwner).name,
|
|
40
|
+
left: rect.left,
|
|
41
|
+
top: rect.top,
|
|
42
|
+
width: rect.width,
|
|
43
|
+
height: rect.height
|
|
44
|
+
}];
|
|
45
|
+
});
|
|
46
|
+
setBoxes((current) => JSON.stringify(current) === JSON.stringify(next) ? current : next);
|
|
47
|
+
frame = requestAnimationFrame(measure);
|
|
48
|
+
};
|
|
49
|
+
frame = requestAnimationFrame(measure);
|
|
50
|
+
return () => cancelAnimationFrame(frame);
|
|
51
|
+
}, [owners]);
|
|
52
|
+
if (!owners.length || typeof document === "undefined") return null;
|
|
53
|
+
return createPortal(/* @__PURE__ */ jsx("div", {
|
|
54
|
+
className: "ps-demand-highlights",
|
|
55
|
+
"data-ps-demand-highlights": true,
|
|
56
|
+
"aria-hidden": true,
|
|
57
|
+
children: boxes.map((box) => /* @__PURE__ */ jsx("div", {
|
|
58
|
+
className: "ps-demand-highlight",
|
|
59
|
+
"data-owner-id": box.id,
|
|
60
|
+
style: {
|
|
61
|
+
left: box.left,
|
|
62
|
+
top: box.top,
|
|
63
|
+
width: box.width,
|
|
64
|
+
height: box.height
|
|
65
|
+
},
|
|
66
|
+
children: /* @__PURE__ */ jsx("span", { children: box.name })
|
|
67
|
+
}, box.id))
|
|
68
|
+
}), portal ?? document.body);
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { DemandHighlight, ownerElements, revealDemandOwners };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=Highlight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Highlight.js","names":[],"sources":["../../src/attribution/Highlight.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { usePanelPortal } from \"../PanelBoundary.js\";\nimport type { DemandOwner } from \"./registry.js\";\n\nexport function ownerElements(owners: readonly DemandOwner[], root: ParentNode = document): HTMLElement[] {\n const ids = new Set(owners.map((owner) => owner.id));\n const panels = [...root.querySelectorAll(\"[data-ps-devtools]\")];\n const candidates = [...root.querySelectorAll<HTMLElement>(\"[data-ps-demand-owner]\")].filter(\n (element) =>\n ids.has(element.dataset.psDemandOwner ?? \"\") &&\n !element.closest(\"[data-ps-devtools]\") &&\n !panels.some((panel) => element.contains(panel))\n );\n const selected = new Set(candidates);\n return candidates.filter((element) => {\n for (let parent = element.parentElement; parent; parent = parent.parentElement)\n if (selected.has(parent)) return false;\n return true;\n });\n}\nexport function revealDemandOwners(owners: readonly DemandOwner[]) {\n const elements = ownerElements(owners);\n const target =\n elements.find((element) => {\n const rect = element.getBoundingClientRect();\n return rect.bottom < 0 || rect.top > window.innerHeight;\n }) ?? elements[0];\n target?.scrollIntoView({\n block: \"center\",\n inline: \"nearest\",\n behavior: window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches ? \"instant\" : \"smooth\",\n });\n}\ninterface Box {\n id: string;\n name: string;\n left: number;\n top: number;\n width: number;\n height: number;\n}\nexport function DemandHighlight({ owners }: { owners: readonly DemandOwner[] }) {\n const portal = usePanelPortal();\n const [boxes, setBoxes] = useState<readonly Box[]>([]);\n useEffect(() => {\n if (!owners.length) return;\n let frame: number;\n const measure = () => {\n const next = ownerElements(owners).flatMap((element, index) => {\n const rect = element.getBoundingClientRect();\n if (!rect.width || !rect.height || rect.bottom <= 0 || rect.top >= window.innerHeight)\n return [];\n return [\n {\n id: `${element.dataset.psDemandOwner}:${index}`,\n name: owners.find((owner) => owner.id === element.dataset.psDemandOwner)!.name,\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n },\n ];\n });\n setBoxes((current) => (JSON.stringify(current) === JSON.stringify(next) ? current : next));\n frame = requestAnimationFrame(measure);\n };\n frame = requestAnimationFrame(measure);\n return () => cancelAnimationFrame(frame);\n }, [owners]);\n if (!owners.length || typeof document === \"undefined\") return null;\n return createPortal(\n <div className=\"ps-demand-highlights\" data-ps-demand-highlights aria-hidden>\n {boxes.map((box) => (\n <div\n className=\"ps-demand-highlight\"\n key={box.id}\n data-owner-id={box.id}\n style={{ left: box.left, top: box.top, width: box.width, height: box.height }}\n >\n <span>{box.name}</span>\n </div>\n ))}\n </div>,\n portal ?? document.body\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,cAAc,QAAgC,OAAmB,UAAyB;CACtG,MAAM,MAAM,IAAI,IAAI,OAAO,KAAK,UAAU,MAAM,EAAE,CAAC;CACnD,MAAM,SAAS,CAAC,GAAG,KAAK,iBAAiB,oBAAoB,CAAC;CAC9D,MAAM,aAAa,CAAC,GAAG,KAAK,iBAA8B,wBAAwB,CAAC,CAAC,CAAC,QAChF,YACG,IAAI,IAAI,QAAQ,QAAQ,iBAAiB,EAAE,KAC3C,CAAC,QAAQ,QAAQ,oBAAoB,KACrC,CAAC,OAAO,MAAM,UAAU,QAAQ,SAAS,KAAK,CAAC,CACvD;CACA,MAAM,WAAW,IAAI,IAAI,UAAU;CACnC,OAAO,WAAW,QAAQ,YAAY;EAClC,KAAK,IAAI,SAAS,QAAQ,eAAe,QAAQ,SAAS,OAAO,eAC7D,IAAI,SAAS,IAAI,MAAM,GAAG,OAAO;EACrC,OAAO;CACX,CAAC;AACL;AACA,SAAgB,mBAAmB,QAAgC;CAC/D,MAAM,WAAW,cAAc,MAAM;CAMrC,CAJI,SAAS,MAAM,YAAY;EACvB,MAAM,OAAO,QAAQ,sBAAsB;EAC3C,OAAO,KAAK,SAAS,KAAK,KAAK,MAAM,OAAO;CAChD,CAAC,KAAK,SAAS,GAAA,EACX,eAAe;EACnB,OAAO;EACP,QAAQ;EACR,UAAU,OAAO,WAAW,kCAAkC,CAAC,CAAC,UAAU,YAAY;CAC1F,CAAC;AACL;AASA,SAAgB,gBAAgB,EAAE,UAA8C;CAC5E,MAAM,SAAS,eAAe;CAC9B,MAAM,CAAC,OAAO,YAAY,SAAyB,CAAC,CAAC;CACrD,gBAAgB;EACZ,IAAI,CAAC,OAAO,QAAQ;EACpB,IAAI;EACJ,MAAM,gBAAgB;GAClB,MAAM,OAAO,cAAc,MAAM,CAAC,CAAC,SAAS,SAAS,UAAU;IAC3D,MAAM,OAAO,QAAQ,sBAAsB;IAC3C,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,UAAU,KAAK,UAAU,KAAK,KAAK,OAAO,OAAO,aACtE,OAAO,CAAC;IACZ,OAAO,CACH;KACI,IAAI,GAAG,QAAQ,QAAQ,cAAc,GAAG;KACxC,MAAM,OAAO,MAAM,UAAU,MAAM,OAAO,QAAQ,QAAQ,aAAa,CAAC,CAAE;KAC1E,MAAM,KAAK;KACX,KAAK,KAAK;KACV,OAAO,KAAK;KACZ,QAAQ,KAAK;IACjB,CACJ;GACJ,CAAC;GACD,UAAU,YAAa,KAAK,UAAU,OAAO,MAAM,KAAK,UAAU,IAAI,IAAI,UAAU,IAAK;GACzF,QAAQ,sBAAsB,OAAO;EACzC;EACA,QAAQ,sBAAsB,OAAO;EACrC,aAAa,qBAAqB,KAAK;CAC3C,GAAG,CAAC,MAAM,CAAC;CACX,IAAI,CAAC,OAAO,UAAU,OAAO,aAAa,aAAa,OAAO;CAC9D,OAAO,aACH,oBAAC,OAAD;EAAK,WAAU;EAAuB,6BAAA;EAA0B,eAAA;EAC3D,UAAA,MAAM,KAAK,QACR,oBAAC,OAAD;GACI,WAAU;GAEV,iBAAe,IAAI;GACnB,OAAO;IAAE,MAAM,IAAI;IAAM,KAAK,IAAI;IAAK,OAAO,IAAI;IAAO,QAAQ,IAAI;GAAO;GAE5E,UAAA,oBAAC,QAAD,EAAA,UAAO,IAAI,KAAW,CAAA;EACrB,GALI,IAAI,EAKR,CACR;CACA,CAAA,GACL,UAAU,SAAS,IACvB;AACJ"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DemandOwner } from "./registry.js";
|
|
2
|
+
//#region src/attribution/index.d.ts
|
|
3
|
+
/** Injected by the development-only Vite plugin. IDs distinguish mounted instances. */
|
|
4
|
+
export declare function useDemandOwner(name: string, source: string): DemandOwner;
|
|
5
|
+
/** Preserve the hook's result and query identity; register only committed consumers. */
|
|
6
|
+
export declare function useDemandQuery<A extends {
|
|
7
|
+
readonly collection?: object;
|
|
8
|
+
}>(owner: DemandOwner, result: A): A;
|
|
9
|
+
/** Singular fragments return their selected value, including an absent row. */
|
|
10
|
+
export declare function useDemandFragment<A>(owner: DemandOwner, result: A, collection: object): A;
|
|
11
|
+
//#endregion
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/attribution/index.ts"],"mappings":";;;wBAIgB,eAAe,cAAc,iBAAiB;;wBAK9C,eAAe;WAAqB;GAAuB,OAAO,aAAa,QAAQ,IAAI;;wBAO3F,kBAAkB,GAAG,OAAO,aAAa,QAAQ,GAAG,qBAAqB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { registerQueryConsumer } from "./registry.js";
|
|
2
|
+
import { useEffect, useId, useMemo } from "react";
|
|
3
|
+
//#region src/attribution/index.ts
|
|
4
|
+
/** Injected by the development-only Vite plugin. IDs distinguish mounted instances. */
|
|
5
|
+
function useDemandOwner(name, source) {
|
|
6
|
+
const id = useId();
|
|
7
|
+
return useMemo(() => ({
|
|
8
|
+
id,
|
|
9
|
+
name,
|
|
10
|
+
source
|
|
11
|
+
}), [
|
|
12
|
+
id,
|
|
13
|
+
name,
|
|
14
|
+
source
|
|
15
|
+
]);
|
|
16
|
+
}
|
|
17
|
+
/** Preserve the hook's result and query identity; register only committed consumers. */
|
|
18
|
+
function useDemandQuery(owner, result) {
|
|
19
|
+
const collection = result.collection;
|
|
20
|
+
useEffect(() => collection ? registerQueryConsumer(owner, collection) : void 0, [owner, collection]);
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
/** Singular fragments return their selected value, including an absent row. */
|
|
24
|
+
function useDemandFragment(owner, result, collection) {
|
|
25
|
+
useEffect(() => registerQueryConsumer(owner, collection), [owner, collection]);
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { useDemandFragment, useDemandOwner, useDemandQuery };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/attribution/index.ts"],"sourcesContent":["import { useEffect, useId, useMemo } from \"react\";\nimport { registerQueryConsumer, type DemandOwner } from \"./registry.js\";\n\n/** Injected by the development-only Vite plugin. IDs distinguish mounted instances. */\nexport function useDemandOwner(name: string, source: string): DemandOwner {\n const id = useId();\n return useMemo(() => ({ id, name, source }), [id, name, source]);\n}\n/** Preserve the hook's result and query identity; register only committed consumers. */\nexport function useDemandQuery<A extends { readonly collection?: object }>(owner: DemandOwner, result: A): A {\n const collection = result.collection;\n useEffect(() => (collection ? registerQueryConsumer(owner, collection) : undefined), [owner, collection]);\n return result;\n}\n\n/** Singular fragments return their selected value, including an absent row. */\nexport function useDemandFragment<A>(owner: DemandOwner, result: A, collection: object): A {\n useEffect(() => registerQueryConsumer(owner, collection), [owner, collection]);\n return result;\n}\n"],"mappings":";;;;AAIA,SAAgB,eAAe,MAAc,QAA6B;CACtE,MAAM,KAAK,MAAM;CACjB,OAAO,eAAe;EAAE;EAAI;EAAM;CAAO,IAAI;EAAC;EAAI;EAAM;CAAM,CAAC;AACnE;;AAEA,SAAgB,eAA2D,OAAoB,QAAc;CACzG,MAAM,aAAa,OAAO;CAC1B,gBAAiB,aAAa,sBAAsB,OAAO,UAAU,IAAI,KAAA,GAAY,CAAC,OAAO,UAAU,CAAC;CACxG,OAAO;AACX;;AAGA,SAAgB,kBAAqB,OAAoB,QAAW,YAAuB;CACvF,gBAAgB,sBAAsB,OAAO,UAAU,GAAG,CAAC,OAAO,UAAU,CAAC;CAC7E,OAAO;AACX"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/attribution/registry.d.ts
|
|
2
|
+
export interface DemandOwner {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly source: string;
|
|
6
|
+
}
|
|
7
|
+
export interface QueryConsumer {
|
|
8
|
+
readonly owner: DemandOwner;
|
|
9
|
+
readonly collection: object;
|
|
10
|
+
}
|
|
11
|
+
export declare function registerQueryConsumer(owner: DemandOwner, collection: object): () => void;
|
|
12
|
+
export declare const queryConsumers: () => QueryConsumer[];
|
|
13
|
+
export declare const attributionRevision: () => number;
|
|
14
|
+
export declare function subscribeAttribution(listener: () => void): () => void;
|
|
15
|
+
//#endregion
|
|
16
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","names":[],"sources":["../../src/attribution/registry.ts"],"mappings":";iBAAiB;WACJ;WACA;WACA;;iBAEI;WACJ,OAAO;WACP;;wBASG,sBAAsB,OAAO,aAAa;qBAQ7C,sBAAc;qBACd;wBACG,qBAAqB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/attribution/registry.ts
|
|
2
|
+
const consumers = /* @__PURE__ */ new Set();
|
|
3
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
4
|
+
let revision = 0;
|
|
5
|
+
const changed = () => {
|
|
6
|
+
revision++;
|
|
7
|
+
for (const listener of listeners) listener();
|
|
8
|
+
};
|
|
9
|
+
function registerQueryConsumer(owner, collection) {
|
|
10
|
+
const consumer = {
|
|
11
|
+
owner,
|
|
12
|
+
collection
|
|
13
|
+
};
|
|
14
|
+
consumers.add(consumer);
|
|
15
|
+
changed();
|
|
16
|
+
return () => {
|
|
17
|
+
if (consumers.delete(consumer)) changed();
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const queryConsumers = () => [...consumers];
|
|
21
|
+
const attributionRevision = () => revision;
|
|
22
|
+
function subscribeAttribution(listener) {
|
|
23
|
+
listeners.add(listener);
|
|
24
|
+
return () => {
|
|
25
|
+
listeners.delete(listener);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { attributionRevision, queryConsumers, registerQueryConsumer, subscribeAttribution };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","names":[],"sources":["../../src/attribution/registry.ts"],"sourcesContent":["export interface DemandOwner {\n readonly id: string;\n readonly name: string;\n readonly source: string;\n}\nexport interface QueryConsumer {\n readonly owner: DemandOwner;\n readonly collection: object;\n}\nconst consumers = new Set<QueryConsumer>();\nconst listeners = new Set<() => void>();\nlet revision = 0;\nconst changed = () => {\n revision++;\n for (const listener of listeners) listener();\n};\nexport function registerQueryConsumer(owner: DemandOwner, collection: object) {\n const consumer = { owner, collection };\n consumers.add(consumer);\n changed();\n return () => {\n if (consumers.delete(consumer)) changed();\n };\n}\nexport const queryConsumers = () => [...consumers];\nexport const attributionRevision = () => revision;\nexport function subscribeAttribution(listener: () => void) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n"],"mappings":";AASA,MAAM,4BAAY,IAAI,IAAmB;AACzC,MAAM,4BAAY,IAAI,IAAgB;AACtC,IAAI,WAAW;AACf,MAAM,gBAAgB;CAClB;CACA,KAAK,MAAM,YAAY,WAAW,SAAS;AAC/C;AACA,SAAgB,sBAAsB,OAAoB,YAAoB;CAC1E,MAAM,WAAW;EAAE;EAAO;CAAW;CACrC,UAAU,IAAI,QAAQ;CACtB,QAAQ;CACR,aAAa;EACT,IAAI,UAAU,OAAO,QAAQ,GAAG,QAAQ;CAC5C;AACJ;AACA,MAAa,uBAAuB,CAAC,GAAG,SAAS;AACjD,MAAa,4BAA4B;AACzC,SAAgB,qBAAqB,UAAsB;CACvD,UAAU,IAAI,QAAQ;CACtB,aAAa;EACT,UAAU,OAAO,QAAQ;CAC7B;AACJ"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ReplicationDemand } from "../replicationActivity.js";
|
|
2
|
+
import { DemandOwner } from "./registry.js";
|
|
3
|
+
import { CollectionDemandDiagnostic } from "@party-stack/ontology/tanstack";
|
|
4
|
+
//#region src/attribution/tanstack.d.ts
|
|
5
|
+
export declare function queryDemandGraph(collection: object): readonly (CollectionDemandDiagnostic & {
|
|
6
|
+
collection: object;
|
|
7
|
+
})[];
|
|
8
|
+
export declare function demandOwners(demand: Pick<ReplicationDemand, "objectType" | "query">, sources: readonly object[]): readonly DemandOwner[];
|
|
9
|
+
export declare function demandRequests(demand: Pick<ReplicationDemand, "objectType" | "query">, sources: readonly object[]): CollectionDemandDiagnostic[];
|
|
10
|
+
//#endregion
|
|
11
|
+
//# sourceMappingURL=tanstack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tanstack.d.ts","names":[],"sources":["../../src/attribution/tanstack.ts"],"mappings":";;;;wBA4BgB,iBACZ,+BACS;EAA+B;;wBAyC5B,aACZ,QAAQ,KAAK,4CACb,sCACQ;wBAcI,eACZ,QAAQ,KAAK,4CACb,6BAA0B"}
|