@plitzi/sdk-dev-tools 0.36.1 → 0.36.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/DevToolsContainer.mjs +54 -53
  3. package/dist/DevToolsRoot.d.ts +18 -0
  4. package/dist/DevToolsRoot.mjs +43 -0
  5. package/dist/components/DevToolsPanel/DevToolsBody.mjs +33 -31
  6. package/dist/components/DevToolsPanel/DevToolsHeader.mjs +5 -0
  7. package/dist/components/DevToolsPanel/tabs/ActionsViewer/components/RunDetail/RunDetail.mjs +14 -14
  8. package/dist/components/DevToolsPanel/tabs/ActionsViewer/helpers.d.ts +0 -1
  9. package/dist/components/DevToolsPanel/tabs/ActionsViewer/helpers.mjs +1 -1
  10. package/dist/components/DevToolsPanel/tabs/CacheViewer/CacheViewer.d.ts +12 -0
  11. package/dist/components/DevToolsPanel/tabs/CacheViewer/CacheViewer.mjs +91 -0
  12. package/dist/components/DevToolsPanel/tabs/CacheViewer/components/CacheRow/CacheRow.d.ts +16 -0
  13. package/dist/components/DevToolsPanel/tabs/CacheViewer/components/CacheRow/CacheRow.mjs +87 -0
  14. package/dist/components/DevToolsPanel/tabs/CacheViewer/components/CacheRow/index.d.ts +3 -0
  15. package/dist/components/DevToolsPanel/tabs/CacheViewer/components/CacheRow/index.mjs +5 -0
  16. package/dist/components/DevToolsPanel/tabs/CacheViewer/index.d.ts +2 -0
  17. package/dist/components/DevToolsPanel/tabs/CacheViewer/index.mjs +5 -0
  18. package/dist/components/DevToolsPanel/tabs/StoreViewer/StoreViewer.mjs +52 -54
  19. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessMark/FreshnessMark.d.ts +16 -0
  20. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessMark/FreshnessMark.mjs +13 -0
  21. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessMark/index.d.ts +3 -0
  22. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/{FreshnessPanel → FreshnessMark}/index.mjs +2 -2
  23. package/dist/components/DevToolsPanel/tabs/StoreViewer/helpers/renderFreshnessKey.d.ts +14 -0
  24. package/dist/components/DevToolsPanel/tabs/StoreViewer/helpers/renderFreshnessKey.mjs +18 -0
  25. package/dist/components/DevToolsPanel/tabs/TracingViewer/helpers.d.ts +5 -0
  26. package/dist/components/DevToolsPanel/tabs/TracingViewer/helpers.mjs +65 -55
  27. package/dist/components/PayloadView/PayloadView.d.ts +11 -0
  28. package/dist/components/{DevToolsPanel/tabs/ActionsViewer/components/PayloadView → PayloadView}/PayloadView.mjs +1 -1
  29. package/dist/components/PayloadView/index.mjs +5 -0
  30. package/dist/freshness/describeQueryPath.d.ts +22 -0
  31. package/dist/freshness/describeQueryPath.mjs +22 -0
  32. package/dist/freshness/formatDuration.d.ts +3 -0
  33. package/dist/freshness/formatDuration.mjs +9 -0
  34. package/dist/freshness/index.d.ts +8 -0
  35. package/dist/freshness/index.mjs +6 -0
  36. package/dist/freshness/rows.d.ts +42 -0
  37. package/dist/freshness/rows.mjs +29 -0
  38. package/dist/freshness/useFreshnessByStore.d.ts +15 -0
  39. package/dist/freshness/useFreshnessByStore.mjs +20 -0
  40. package/dist/freshness/useSecondsClock.d.ts +8 -0
  41. package/dist/freshness/useSecondsClock.mjs +12 -0
  42. package/dist/scope/helpers.d.ts +1 -0
  43. package/dist/scope/helpers.mjs +7 -7
  44. package/dist/scope/useScope.d.ts +3 -1
  45. package/dist/scope/useScope.mjs +21 -18
  46. package/dist/useRegisterQueriesStore.mjs +3 -3
  47. package/package.json +89 -49
  48. package/dist/components/DevToolsPanel/tabs/ActionsViewer/components/PayloadView/PayloadView.d.ts +0 -6
  49. package/dist/components/DevToolsPanel/tabs/ActionsViewer/components/PayloadView/index.mjs +0 -5
  50. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/FreshnessPanel.d.ts +0 -11
  51. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/FreshnessPanel.mjs +0 -49
  52. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/FreshnessRow.d.ts +0 -7
  53. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/FreshnessRow.mjs +0 -43
  54. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.d.ts +0 -3
  55. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.mjs +0 -5
  56. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/helpers.d.ts +0 -14
  57. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/helpers.mjs +0 -18
  58. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.d.ts +0 -3
  59. package/dist/scope/useFreshnessRecords.d.ts +0 -3
  60. package/dist/scope/useFreshnessRecords.mjs +0 -8
  61. /package/dist/components/{DevToolsPanel/tabs/ActionsViewer/components/PayloadView → PayloadView}/index.d.ts +0 -0
@@ -1,64 +1,62 @@
1
- import e from "./components/FreshnessPanel/index.mjs";
2
- import t from "./helpers/mapFunctionValues.mjs";
3
- import n from "./helpers/renderFunctionValue.mjs";
4
- import { useSelectedStore as r } from "../../../../scope/useScope.mjs";
5
- import i from "../../../../scope/useStoreState.mjs";
6
- import a from "@plitzi/plitzi-ui/hooks/useStorage";
7
- import o from "clsx";
8
- import { use as s, useCallback as c, useMemo as l } from "react";
9
- import u from "@plitzi/sdk-shared/theme/useTheme";
10
- import d from "@plitzi/sdk-shared/devTools/DevToolsContext";
11
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
12
- import m from "@uiw/react-json-view";
13
- import * as h from "@uiw/react-json-view/vscode";
14
- import { emptyObject as g } from "@plitzi/sdk-shared/helpers/utils";
1
+ import e from "../../../../freshness/useFreshnessByStore.mjs";
2
+ import t from "../../../../freshness/useSecondsClock.mjs";
3
+ import { useSelectedStore as n, useSelectedStoreEntry as r } from "../../../../scope/useScope.mjs";
4
+ import i from "./helpers/mapFunctionValues.mjs";
5
+ import a from "./helpers/renderFreshnessKey.mjs";
6
+ import o from "./helpers/renderFunctionValue.mjs";
7
+ import s from "../../../../scope/useStoreState.mjs";
8
+ import c from "@plitzi/plitzi-ui/hooks/useStorage";
9
+ import l from "clsx";
10
+ import { use as u, useCallback as d, useMemo as f } from "react";
11
+ import p from "@plitzi/sdk-shared/theme/useTheme";
12
+ import m from "@plitzi/sdk-shared/devTools/DevToolsContext";
13
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
14
+ import _ from "@uiw/react-json-view";
15
+ import * as v from "@uiw/react-json-view/vscode";
16
+ import { emptyObject as y } from "@plitzi/sdk-shared/helpers/utils";
15
17
  //#region src/components/DevToolsPanel/tabs/StoreViewer/StoreViewer.tsx
16
- var _ = {
17
- ...h.vscodeTheme,
18
+ var b = {
19
+ ...v.vscodeTheme,
18
20
  width: "100%",
19
21
  height: "100%",
20
22
  overflow: "auto",
21
23
  padding: "8px",
22
24
  fontSize: "14px"
23
- }, v = (e) => o("cursor-pointer rounded px-2 py-0.5 font-medium transition-colors", e ? "bg-violet-500/15 text-violet-600 dark:text-violet-300" : "text-zinc-500 hover:bg-zinc-200/70 hover:text-zinc-800 dark:text-zinc-400 dark:hover:bg-zinc-700/50 dark:hover:text-zinc-100"), y = ({ elementSelected: o }) => {
24
- let { resolvedTheme: h } = u(), { getData: y } = s(d), b = r(), [x, S] = a("plitzi-sdk.dev-tools.store.view", "own"), C = i(o ? void 0 : b, x === "merged"), w = c(() => S("own"), [S]), T = c(() => S("merged"), [S]), E = o ? y?.(`getElementDataSource-${o}`) : C, D = l(() => t(E), [E]);
25
- return /* @__PURE__ */ p("div", {
25
+ }, x = (e) => l("cursor-pointer rounded px-2 py-0.5 font-medium transition-colors", e ? "bg-violet-500/15 text-violet-600 dark:text-violet-300" : "text-zinc-500 hover:bg-zinc-200/70 hover:text-zinc-800 dark:text-zinc-400 dark:hover:bg-zinc-700/50 dark:hover:text-zinc-100"), S = {}, C = [], w = ({ elementSelected: l }) => {
26
+ let { resolvedTheme: v } = p(), { getData: w } = u(m), T = r(), E = n(), [D, O] = c("plitzi-sdk.dev-tools.store.view", "own"), k = s(l ? void 0 : E, D === "merged"), A = d(() => O("own"), [O]), j = d(() => O("merged"), [O]), M = l ? w?.(`getElementDataSource-${l}`) : k, N = f(() => i(M), [M]), P = f(() => T ? [T] : C, [T]), F = e(P)[0]?.records ?? S, I = f(() => Object.values(F).reduce((e, t) => Math.max(e, t.updatedAt), 0), [F]), L = t(Object.keys(F).length > 0, I), R = f(() => a(F, L), [F, L]);
27
+ return /* @__PURE__ */ g("div", {
26
28
  className: "flex h-full w-full flex-col",
27
- children: [
28
- !o && /* @__PURE__ */ p("div", {
29
- className: "flex shrink-0 items-center gap-1 border-b border-zinc-200 px-2 py-1 text-[11px] dark:border-zinc-800",
30
- children: [
31
- /* @__PURE__ */ f("span", {
32
- className: "mr-1 text-zinc-400 dark:text-zinc-500",
33
- children: "View"
34
- }),
35
- /* @__PURE__ */ f("button", {
36
- className: v(x === "own"),
37
- onClick: w,
38
- title: "Only this scope's own layer",
39
- children: "Own"
40
- }),
41
- /* @__PURE__ */ f("button", {
42
- className: v(x === "merged"),
43
- onClick: T,
44
- title: "Own layer merged over the parent scope chain",
45
- children: "Merged"
46
- })
47
- ]
48
- }),
49
- !o && /* @__PURE__ */ f(e, { store: b }),
50
- /* @__PURE__ */ f(m, {
51
- value: D ?? g,
52
- style: h === "dark" ? _ : void 0,
53
- enableClipboard: !1,
54
- indentWidth: 15,
55
- collapsed: 2,
56
- displayObjectSize: !1,
57
- displayDataTypes: !1,
58
- children: /* @__PURE__ */ f(m.String, { render: n })
59
- })
60
- ]
29
+ children: [!l && /* @__PURE__ */ g("div", {
30
+ className: "flex shrink-0 items-center gap-1 border-b border-zinc-200 px-2 py-1 text-[11px] dark:border-zinc-800",
31
+ children: [
32
+ /* @__PURE__ */ h("span", {
33
+ className: "mr-1 text-zinc-400 dark:text-zinc-500",
34
+ children: "View"
35
+ }),
36
+ /* @__PURE__ */ h("button", {
37
+ className: x(D === "own"),
38
+ onClick: A,
39
+ title: "Only this scope's own layer",
40
+ children: "Own"
41
+ }),
42
+ /* @__PURE__ */ h("button", {
43
+ className: x(D === "merged"),
44
+ onClick: j,
45
+ title: "Own layer merged over the parent scope chain",
46
+ children: "Merged"
47
+ })
48
+ ]
49
+ }), /* @__PURE__ */ g(_, {
50
+ value: N ?? y,
51
+ style: v === "dark" ? b : void 0,
52
+ enableClipboard: !1,
53
+ indentWidth: 15,
54
+ collapsed: 2,
55
+ displayObjectSize: !1,
56
+ displayDataTypes: !1,
57
+ children: [/* @__PURE__ */ h(_.String, { render: o }), /* @__PURE__ */ h(_.KeyName, { render: R })]
58
+ })]
61
59
  });
62
60
  };
63
61
  //#endregion
64
- export { y as default };
62
+ export { w as default };
@@ -0,0 +1,16 @@
1
+ import { PathFreshness } from '@plitzi/nexus';
2
+ export type FreshnessMarkProps = {
3
+ record: PathFreshness;
4
+ /** A clock the caller moves, so every mark on the tree counts down in step. */
5
+ now: number;
6
+ };
7
+ /**
8
+ * What a path in the state tree is worth in time: how long before it stops being current, or that it already has.
9
+ *
10
+ * On the path itself, because the question a reader arrives at the tree with is about the state in front of them —
11
+ * which of these is cached, and for how much longer. A path with no mark was written with no `ttl` at all, which is
12
+ * every path unless somebody asked for one. The rest of what there is to say about it is the Cache tab's job, so
13
+ * nothing here hides behind a tooltip.
14
+ */
15
+ declare const FreshnessMark: ({ record: { expiresAt }, now }: FreshnessMarkProps) => import("react").JSX.Element;
16
+ export default FreshnessMark;
@@ -0,0 +1,13 @@
1
+ import e from "../../../../../../freshness/formatDuration.mjs";
2
+ import t from "clsx";
3
+ import { jsx as n } from "react/jsx-runtime";
4
+ //#region src/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessMark/FreshnessMark.tsx
5
+ var r = ({ record: { expiresAt: r }, now: i }) => {
6
+ let a = i >= r, o = Number.isFinite(r) ? `${e(r - i)} left` : "no expiry";
7
+ return /* @__PURE__ */ n("span", {
8
+ className: t("ml-1 rounded px-1 align-middle text-[10px] font-medium", a ? "bg-amber-500/15 text-amber-700 dark:text-amber-300" : "bg-emerald-500/15 text-emerald-700 dark:text-emerald-300"),
9
+ children: a ? "stale" : o
10
+ });
11
+ };
12
+ //#endregion
13
+ export { r as default };
@@ -0,0 +1,3 @@
1
+ import { default as FreshnessMark } from './FreshnessMark';
2
+ export * from './FreshnessMark';
3
+ export default FreshnessMark;
@@ -1,5 +1,5 @@
1
- import e from "./FreshnessPanel.mjs";
2
- //#region src/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.ts
1
+ import e from "./FreshnessMark.mjs";
2
+ //#region src/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessMark/index.ts
3
3
  var t = e;
4
4
  //#endregion
5
5
  export { t as default };
@@ -0,0 +1,14 @@
1
+ import { PathFreshness } from '@plitzi/nexus';
2
+ import { default as JsonView } from '@uiw/react-json-view';
3
+ import { ComponentProps } from 'react';
4
+ /** Taken from the component rather than from a type the package does not export at its root. */
5
+ export type FreshnessKeyRenderer = NonNullable<ComponentProps<typeof JsonView.KeyName>['render']>;
6
+ /**
7
+ * The tree's key names, with a countdown on the paths that have one.
8
+ *
9
+ * Only the path a record was written AT is marked, never the ones it covers: a record answers for its whole subtree,
10
+ * so marking those too would say that every field under a cached answer had a TTL of its own. Returning nothing
11
+ * leaves the key to the viewer's own renderer, which is every path nobody gave a `ttl`.
12
+ */
13
+ declare const renderFreshnessKey: (records: Readonly<Partial<Record<string, PathFreshness>>>, now: number) => FreshnessKeyRenderer;
14
+ export default renderFreshnessKey;
@@ -0,0 +1,18 @@
1
+ import e from "../components/FreshnessMark/index.mjs";
2
+ import { Fragment as t, jsx as n, jsxs as r } from "react/jsx-runtime";
3
+ //#region src/components/DevToolsPanel/tabs/StoreViewer/helpers/renderFreshnessKey.tsx
4
+ var i = (i, a) => (o, { keys: s }) => {
5
+ let c = i[(s ?? []).join(".")];
6
+ if (!c) return null;
7
+ let { className: l, style: u, children: d } = o;
8
+ return /* @__PURE__ */ r(t, { children: [/* @__PURE__ */ n("span", {
9
+ className: l,
10
+ style: u,
11
+ children: d
12
+ }), /* @__PURE__ */ n(e, {
13
+ record: c,
14
+ now: a
15
+ })] });
16
+ };
17
+ //#endregion
18
+ export { i as default };
@@ -97,6 +97,11 @@ export type TreeIndex = {
97
97
  base: Map<string, number>;
98
98
  children: Map<string, string[]>;
99
99
  elementOf: Map<string, string>;
100
+ /** When each instance was on the page, for the ones whose presence is not the whole timeline. */
101
+ lifetime: Map<string, {
102
+ mountedAt: number;
103
+ unmountedAt: number;
104
+ }>;
100
105
  };
101
106
  export declare const buildTreeIndex: (tree: TracingTree) => TreeIndex;
102
107
  export declare const buildFlameModel: (commit: CommitEntry, tree: TracingTree, flat: Record<string, Element> | undefined, index?: TreeIndex) => FlameModel;
@@ -66,21 +66,31 @@ var t = (e) => {
66
66
  }, g = (e, t) => t?.[e]?.definition.type ?? "unknown", _ = (t, n) => {
67
67
  let r = e(n?.[t], "definition.initialState.visibility", !0);
68
68
  return r !== !1 && r !== "false";
69
- }, v = (e, t) => t === "self" ? e.selfDuration : e.actualDuration, y = .01, b = 1e-6, x = (e) => {
70
- let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map();
71
- for (let i of Object.keys(e)) t.set(i, e[i].parentId), n.set(i, e[i].baseDuration), r.set(i, e[i].elementId);
72
- let i = /* @__PURE__ */ new Map();
69
+ }, v = (e, t) => t === "self" ? e.selfDuration : e.actualDuration, y = .01, b = 1e-6, x = (e, t, n) => {
70
+ let r = e.lifetime.get(t);
71
+ return !r || r.mountedAt <= n && n < r.unmountedAt;
72
+ }, S = (e) => {
73
+ let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Map();
74
+ for (let a of Object.keys(e)) {
75
+ let { parentId: o, baseDuration: s, elementId: c, mountedAt: l, unmountedAt: u } = e[a];
76
+ t.set(a, o), n.set(a, s), r.set(a, c), (l !== void 0 || u !== void 0) && i.set(a, {
77
+ mountedAt: l ?? 0,
78
+ unmountedAt: u ?? Infinity
79
+ });
80
+ }
81
+ let a = /* @__PURE__ */ new Map();
73
82
  for (let [e, n] of t) if (n !== void 0 && t.has(n)) {
74
- let t = i.get(n);
75
- t ? t.push(e) : i.set(n, [e]);
83
+ let t = a.get(n);
84
+ t ? t.push(e) : a.set(n, [e]);
76
85
  }
77
86
  return {
78
87
  parent: t,
79
88
  base: n,
80
- children: i,
81
- elementOf: r
89
+ children: a,
90
+ elementOf: r,
91
+ lifetime: i
82
92
  };
83
- }, S = (e, t) => {
93
+ }, C = (e, t) => {
84
94
  let { parent: n } = t, r = new Map(e.elements.map((e) => [e.id, e])), i = /* @__PURE__ */ new Map();
85
95
  for (let t of e.elements) i.set(t.id, t.actualDuration);
86
96
  for (let t of e.elements) {
@@ -100,62 +110,62 @@ var t = (e) => {
100
110
  self: i,
101
111
  roots: [...a]
102
112
  };
103
- }, C = (e, t, n, r = x(t)) => {
104
- let { children: i, base: a, elementOf: o } = r, { rendered: s, self: c, roots: l } = S(e, r), u = (e) => {
113
+ }, w = (e, t, n, r = S(t)) => {
114
+ let { children: i, base: a, elementOf: o } = r, { rendered: s, self: c, roots: l } = C(e, r), u = (e) => {
105
115
  let t = a.get(e) ?? 0;
106
116
  if (t > 0) return t;
107
117
  let n = s.get(e)?.actualDuration ?? 0;
108
118
  return n > 0 ? n : y;
109
- }, d = [], f = [], p = 0, m = 0, v = 0, C = [], w = l.map(u), T = w.reduce((e, t) => e + t, 0) || 1, E = 0;
119
+ }, d = [], f = [], p = 0, m = 0, v = 0, w = [], T = l.map(u), E = T.reduce((e, t) => e + t, 0) || 1, D = 0;
110
120
  for (l.forEach((e, t) => {
111
- let n = (w[t] || y) / T;
112
- C.push({
121
+ let n = (T[t] || y) / E;
122
+ w.push({
113
123
  id: e,
114
124
  parentId: void 0,
115
- x: E,
125
+ x: D,
116
126
  width: n,
117
127
  depth: 0,
118
128
  ancestorRendered: !1
119
- }), E += n;
120
- }), C.reverse(); C.length > 0;) {
121
- let e = C.pop();
122
- if (e === void 0) break;
123
- let { id: t, parentId: r, x: l, width: x, depth: S, ancestorRendered: w } = e, T = s.get(t), E = c.get(t) ?? 0, D = T ? E > b ? "rendered" : "bubbled" : "hatched", O = D === "rendered" && !w;
124
- D === "rendered" && (m += E, v += 1), O && f.push(t), p = Math.max(p, S);
125
- let k = o.get(t) ?? T?.elementId ?? t;
129
+ }), D += n;
130
+ }), w.reverse(); w.length > 0;) {
131
+ let t = w.pop();
132
+ if (t === void 0) break;
133
+ let { id: l, parentId: S, x: C, width: T, depth: E, ancestorRendered: D } = t, O = s.get(l), k = c.get(l) ?? 0, A = O ? k > b ? "rendered" : "bubbled" : "hatched", j = A === "rendered" && !D;
134
+ A === "rendered" && (m += k, v += 1), j && f.push(l), p = Math.max(p, E);
135
+ let M = o.get(l) ?? O?.elementId ?? l;
126
136
  d.push({
127
- id: t,
128
- elementId: k,
129
- name: h(k, n),
130
- type: g(k, n),
131
- state: D,
132
- visible: _(k, n),
133
- trigger: O,
134
- phase: T?.phase,
135
- actualDuration: T?.actualDuration ?? 0,
136
- baseDuration: a.get(t) ?? T?.baseDuration ?? 0,
137
- selfDuration: E,
138
- depth: S,
139
- parentId: r,
140
- changedProps: T?.changedProps,
141
- x: l,
142
- width: x
137
+ id: l,
138
+ elementId: M,
139
+ name: h(M, n),
140
+ type: g(M, n),
141
+ state: A,
142
+ visible: _(M, n),
143
+ trigger: j,
144
+ phase: O?.phase,
145
+ actualDuration: O?.actualDuration ?? 0,
146
+ baseDuration: a.get(l) ?? O?.baseDuration ?? 0,
147
+ selfDuration: k,
148
+ depth: E,
149
+ parentId: S,
150
+ changedProps: O?.changedProps,
151
+ x: C,
152
+ width: T
143
153
  });
144
- let A = i.get(t) ?? [];
145
- if (A.length === 0) continue;
146
- let j = A.map(u), M = j.reduce((e, t) => e + t, 0), N = Math.max(u(t), M) || 1, P = l, F = [];
147
- A.forEach((e, n) => {
148
- let r = (j[n] || y) / N * x;
149
- F.push({
154
+ let N = (i.get(l) ?? []).filter((t) => s.has(t) || x(r, t, e.commitId));
155
+ if (N.length === 0) continue;
156
+ let P = N.map(u), F = P.reduce((e, t) => e + t, 0), I = Math.max(u(l), F) || 1, L = C, R = [];
157
+ N.forEach((e, t) => {
158
+ let n = (P[t] || y) / I * T;
159
+ R.push({
150
160
  id: e,
151
- parentId: t,
152
- x: P,
153
- width: r,
154
- depth: S + 1,
155
- ancestorRendered: w || D === "rendered"
156
- }), P += r;
161
+ parentId: l,
162
+ x: L,
163
+ width: n,
164
+ depth: E + 1,
165
+ ancestorRendered: D || A === "rendered"
166
+ }), L += n;
157
167
  });
158
- for (let e = F.length - 1; e >= 0; --e) C.push(F[e]);
168
+ for (let e = R.length - 1; e >= 0; --e) w.push(R[e]);
159
169
  }
160
170
  return {
161
171
  nodes: d,
@@ -164,10 +174,10 @@ var t = (e) => {
164
174
  renderedCount: v,
165
175
  triggers: f
166
176
  };
167
- }, w = (e, t, n) => {
168
- let r = x(t), i = /* @__PURE__ */ new Map();
177
+ }, T = (e, t, n) => {
178
+ let r = S(t), i = /* @__PURE__ */ new Map();
169
179
  for (let t of e) {
170
- let { self: e } = S(t, r);
180
+ let { self: e } = C(t, r);
171
181
  for (let n of t.elements) {
172
182
  let t = e.get(n.id) ?? 0;
173
183
  if (t <= b) continue;
@@ -194,4 +204,4 @@ var t = (e) => {
194
204
  }));
195
205
  };
196
206
  //#endregion
197
- export { d as BUBBLED_COLOR, a as COMMIT_ORIGIN_BADGE, i as COMMIT_ORIGIN_LABEL, o as COMMIT_ORIGIN_LEGEND, u as DURATION_LEGEND, f as HATCH_STYLE, n as SSR_COMMIT_ID, C as buildFlameModel, w as buildHotspots, x as buildTreeIndex, r as commitOrigin, l as durationColor, _ as elementVisible, s as formatMs, c as formatPercent, p as frameColor, m as frameTextColor, v as rowDuration };
207
+ export { d as BUBBLED_COLOR, a as COMMIT_ORIGIN_BADGE, i as COMMIT_ORIGIN_LABEL, o as COMMIT_ORIGIN_LEGEND, u as DURATION_LEGEND, f as HATCH_STYLE, n as SSR_COMMIT_ID, w as buildFlameModel, T as buildHotspots, S as buildTreeIndex, r as commitOrigin, l as durationColor, _ as elementVisible, s as formatMs, c as formatPercent, p as frameColor, m as frameTextColor, v as rowDuration };
@@ -0,0 +1,11 @@
1
+ export type PayloadViewProps = {
2
+ value: unknown;
3
+ };
4
+ /**
5
+ * A value, read the way the panel reads values: the same JSON viewer everywhere, wearing the panel's theme.
6
+ *
7
+ * Shared rather than per tab because the viewer ships a light palette and nothing else — on a dark panel its keys
8
+ * come out near-black on near-black — and every tab that shows a payload had to remember to dress it.
9
+ */
10
+ declare const PayloadView: ({ value }: PayloadViewProps) => import("react").JSX.Element;
11
+ export default PayloadView;
@@ -2,7 +2,7 @@ import e from "@plitzi/sdk-shared/theme/useTheme";
2
2
  import { jsx as t } from "react/jsx-runtime";
3
3
  import n from "@uiw/react-json-view";
4
4
  import * as r from "@uiw/react-json-view/vscode";
5
- //#region src/components/DevToolsPanel/tabs/ActionsViewer/components/PayloadView/PayloadView.tsx
5
+ //#region src/components/PayloadView/PayloadView.tsx
6
6
  var i = {
7
7
  ...r.vscodeTheme,
8
8
  width: "100%",
@@ -0,0 +1,5 @@
1
+ import e from "./PayloadView.mjs";
2
+ //#region src/components/PayloadView/index.ts
3
+ var t = e;
4
+ //#endregion
5
+ export { t as default };
@@ -0,0 +1,22 @@
1
+ import { DescribePath } from './rows';
2
+ /**
3
+ * A query cache path, read as the request it holds and as what is keeping it.
4
+ *
5
+ * The cache keys its entries by a hash of the request, because a URL is full of the store's path separator — so the
6
+ * path alone (`entries.22yn2ezdarx`) says nothing about which provider asked. The entry itself carries the URL and the
7
+ * tags, and a provider's tag is its element id.
8
+ *
9
+ * The usage is the other half, and the one that stops a panel reading as a leak: an answer nobody renders is kept on
10
+ * purpose, so that coming back within the grace period paints at once. Saying which of the two a row is — and when the
11
+ * grace period runs out — is the difference between "this page is holding rubbish" and "this page is holding this".
12
+ */
13
+ declare const describeQueryPath: DescribePath;
14
+ /**
15
+ * Lets go of a cached answer now, rather than at the end of its grace period.
16
+ *
17
+ * Here rather than in the panel because only this file knows a query path from any other: the cache has to be told,
18
+ * so that the request still out for it is dropped too and the next reader asks afresh. Answers false for a path this
19
+ * does not own, and for a query something still renders — that one has nothing to ask again with.
20
+ */
21
+ export declare const forgetQueryPath: (store: object, path: string) => boolean;
22
+ export default describeQueryPath;
@@ -0,0 +1,22 @@
1
+ import { queryCache as e } from "@plitzi/sdk-shared/queries";
2
+ //#region src/freshness/describeQueryPath.ts
3
+ var t = e.store, n = "entries.", r = (r, i) => {
4
+ if (r === t && i.startsWith(n)) return e.store.getState().entries[i.slice(n.length)];
5
+ }, i = (t, n) => {
6
+ let i = r(t.entry.store, n);
7
+ if (!i) return;
8
+ let a = e.usage(i.key);
9
+ return {
10
+ label: i.url,
11
+ tags: i.tags,
12
+ usage: a && {
13
+ inUse: a.observers > 0 || a.holds > 0,
14
+ collectAt: a.collectAt
15
+ }
16
+ };
17
+ }, a = (t, n) => {
18
+ let i = r(t, n);
19
+ return i ? e.remove(i.key) : !1;
20
+ };
21
+ //#endregion
22
+ export { i as default, a as forgetQueryPath };
@@ -0,0 +1,3 @@
1
+ /** A span of milliseconds as a countdown reads it: `18s`, `2m 5s`, `1h 12m`. */
2
+ declare const formatDuration: (ms: number) => string;
3
+ export default formatDuration;
@@ -0,0 +1,9 @@
1
+ //#region src/freshness/formatDuration.ts
2
+ var e = (e) => {
3
+ let t = Math.max(0, Math.round(e / 1e3));
4
+ if (t < 60) return `${t}s`;
5
+ let n = Math.floor(t / 60);
6
+ return n < 60 ? `${n}m ${t % 60}s` : `${Math.floor(n / 60)}h ${n % 60}m`;
7
+ };
8
+ //#endregion
9
+ export { e as default };
@@ -0,0 +1,8 @@
1
+ import { default as describeQueryPath } from './describeQueryPath';
2
+ import { default as formatDuration } from './formatDuration';
3
+ import { default as useFreshnessByStore } from './useFreshnessByStore';
4
+ import { default as useSecondsClock } from './useSecondsClock';
5
+ export * from './describeQueryPath';
6
+ export * from './rows';
7
+ export * from './useFreshnessByStore';
8
+ export { describeQueryPath, formatDuration, useFreshnessByStore, useSecondsClock };
@@ -0,0 +1,6 @@
1
+ import e, { forgetQueryPath as t } from "./describeQueryPath.mjs";
2
+ import n from "./formatDuration.mjs";
3
+ import r from "./useFreshnessByStore.mjs";
4
+ import i from "./useSecondsClock.mjs";
5
+ import { toRows as a } from "./rows.mjs";
6
+ export { e as describeQueryPath, t as forgetQueryPath, n as formatDuration, a as toRows, r as useFreshnessByStore, i as useSecondsClock };
@@ -0,0 +1,42 @@
1
+ import { FreshnessGroup } from './useFreshnessByStore';
2
+ /** Whether anything is rendering what a path holds, and when it is let go of if nothing starts. */
3
+ export type PathUsage = {
4
+ inUse: boolean;
5
+ collectAt?: number;
6
+ };
7
+ /** What a path stands for, when the path itself is an opaque key — a query cache entry is a hash of its request. */
8
+ export type PathDescription = {
9
+ label: string;
10
+ tags: string[];
11
+ usage?: PathUsage;
12
+ };
13
+ export type DescribePath = (group: FreshnessGroup, path: string) => PathDescription | undefined;
14
+ export type FreshnessRowModel = {
15
+ /** The store and path together: two stores can hold the same path. */
16
+ key: string;
17
+ uid: string;
18
+ storeLabel: string;
19
+ path: string;
20
+ label: string;
21
+ tags: string[];
22
+ isStale: boolean;
23
+ /** How long ago it was written, e.g. `12s ago`. */
24
+ age: string;
25
+ /** `18s left` while current, `stale` once it is not. */
26
+ status: string;
27
+ /** The TTL it was written with — not what is left of it, which is what `status` says. */
28
+ ttl: string;
29
+ /** What is left of the TTL, `1` down to `0` — a bar says "half gone" in a glance that a countdown does not. */
30
+ progress: number;
31
+ /** Whether anything renders this, where that is known: only what can be let go of can be offered to. */
32
+ inUse?: boolean;
33
+ /** `forgotten in 3m 58s`, while a grace period is running. */
34
+ kept?: string;
35
+ };
36
+ /**
37
+ * One row per record of every store, stale ones last so what is still being served is what the panel shows first.
38
+ *
39
+ * `now` is never earlier than the newest write: the clock is a render-time snapshot, and a record written after it
40
+ * read as written in the future — "0s ago" and "0s left" on an answer that was never current at all.
41
+ */
42
+ export declare const toRows: (groups: ReadonlyArray<FreshnessGroup>, labelOf: (group: FreshnessGroup) => string, now: number, describe?: DescribePath) => FreshnessRowModel[];
@@ -0,0 +1,29 @@
1
+ import e from "./formatDuration.mjs";
2
+ //#region src/freshness/rows.ts
3
+ var t = (t, n, r, { updatedAt: i, expiresAt: a, ttl: o }, s, c) => {
4
+ let l = s >= a, u = Number.isFinite(a), d = "no expiry";
5
+ return l ? d = "stale" : u && (d = `${e(a - s)} left`), {
6
+ key: `${t.entry.uid}:${r}`,
7
+ uid: t.entry.uid,
8
+ storeLabel: n,
9
+ path: r,
10
+ label: c?.label ?? r,
11
+ tags: c?.tags ?? [],
12
+ isStale: l,
13
+ age: `${e(s - i)} ago`,
14
+ status: d,
15
+ ttl: Number.isFinite(o) ? e(o) : "∞",
16
+ inUse: c?.usage?.inUse,
17
+ kept: c?.usage?.collectAt ? `forgotten in ${e(c.usage.collectAt - s)}` : void 0,
18
+ progress: l ? 0 : Math.min(1, Number.isFinite(o) ? (a - s) / o : 1)
19
+ };
20
+ }, n = (e, n, r, i) => {
21
+ let a = r;
22
+ for (let t of e) for (let e of Object.values(t.records)) a = Math.max(a, e.updatedAt);
23
+ return e.flatMap((e) => {
24
+ let r = n(e);
25
+ return Object.entries(e.records).map(([n, o]) => t(e, r, n, o, a, i?.(e, n)));
26
+ }).sort((e, t) => Number(e.isStale) - Number(t.isStale) || e.storeLabel.localeCompare(t.storeLabel) || e.label.localeCompare(t.label));
27
+ };
28
+ //#endregion
29
+ export { n as toRows };
@@ -0,0 +1,15 @@
1
+ import { DevStoreEntry, PathFreshness } from '@plitzi/nexus';
2
+ export type FreshnessGroup = {
3
+ entry: DevStoreEntry;
4
+ records: Readonly<Record<string, PathFreshness>>;
5
+ };
6
+ /**
7
+ * The paths written with a TTL, across every store given — the stores that hold none are left out.
8
+ *
9
+ * Across all of them because the one holding a TTL is rarely the one being looked at: a provider's answers live in the
10
+ * query cache, not in the provider's own scope, and a panel that only read the picked store showed nothing unless its
11
+ * reader already knew where to look. Each store hands out the same records object until one changes, so the list is
12
+ * rebuilt only when something did.
13
+ */
14
+ declare const useFreshnessByStore: (entries: ReadonlyArray<DevStoreEntry>) => ReadonlyArray<FreshnessGroup>;
15
+ export default useFreshnessByStore;
@@ -0,0 +1,20 @@
1
+ import { useCallback as e, useRef as t, useSyncExternalStore as n } from "react";
2
+ //#region src/freshness/useFreshnessByStore.ts
3
+ var r = [], i = (e, t) => e.length === t.length && e.every((e, n) => e.entry === t[n].entry && e.records === t[n].records), a = (e) => Object.fromEntries(Object.entries(e).filter(([, e]) => e.ttl > 0)), o = (o) => {
4
+ let s = t(r), c = t(/* @__PURE__ */ new WeakMap()), l = e((e) => {
5
+ let t = o.map(({ store: t }) => t.watchFreshness(e));
6
+ return () => t.forEach((e) => e());
7
+ }, [o]), u = e(() => {
8
+ let e = o.map((e) => {
9
+ let t = e.store.getFreshnessRecords(), n = c.current.get(t);
10
+ return n || (n = a(t), c.current.set(t, n)), {
11
+ entry: e,
12
+ records: n
13
+ };
14
+ }).filter((e) => Object.keys(e.records).length > 0);
15
+ return i(s.current, e) || (s.current = e.length > 0 ? e : r), s.current;
16
+ }, [o]);
17
+ return n(l, u, u);
18
+ };
19
+ //#endregion
20
+ export { o as default };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `Date.now()`, moving once a second while `active` — the finest step a countdown shows.
3
+ *
4
+ * Never earlier than `floor`: the clock is a render-time snapshot, and a value written after it read as written in
5
+ * the future — "0s ago" and "0s left" on an answer that was never current at all.
6
+ */
7
+ declare const useSecondsClock: (active: boolean, floor?: number) => number;
8
+ export default useSecondsClock;
@@ -0,0 +1,12 @@
1
+ import { useEffect as e, useState as t } from "react";
2
+ //#region src/freshness/useSecondsClock.ts
3
+ var n = (n, r = 0) => {
4
+ let [i, a] = t(() => Date.now());
5
+ return e(() => {
6
+ if (!n) return;
7
+ let e = setInterval(() => a(Date.now()), 1e3);
8
+ return () => clearInterval(e);
9
+ }, [n]), Math.max(i, r);
10
+ };
11
+ //#endregion
12
+ export { n as default };