@plitzi/sdk-dev-tools 0.35.9 → 0.36.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.
Files changed (18) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/dist/DevToolsContainer.mjs +51 -50
  3. package/dist/components/DevToolsPanel/tabs/StoreViewer/StoreViewer.mjs +54 -49
  4. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/FreshnessPanel.d.ts +11 -0
  5. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/FreshnessPanel.mjs +49 -0
  6. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/FreshnessRow.d.ts +7 -0
  7. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/FreshnessRow.mjs +43 -0
  8. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.d.ts +3 -0
  9. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.mjs +5 -0
  10. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/helpers.d.ts +14 -0
  11. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/helpers.mjs +18 -0
  12. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.d.ts +3 -0
  13. package/dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.mjs +5 -0
  14. package/dist/scope/useFreshnessRecords.d.ts +3 -0
  15. package/dist/scope/useFreshnessRecords.mjs +8 -0
  16. package/dist/useRegisterQueriesStore.d.ts +10 -0
  17. package/dist/useRegisterQueriesStore.mjs +11 -0
  18. package/package.json +42 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # @plitzi/sdk-dev-tools
2
2
 
3
+ ## 0.36.0
4
+
5
+ ### Minor Changes
6
+
7
+ - - **An `apiContainer` can keep its browser requests in a query cache, the way react-query does.** Moving between
8
+ sections or pages used to ask the API again every time a provider was shown: a provider in a hidden section is
9
+ mounted but disabled, so showing it fired a fresh request, and a page navigation remounted it and did the same.
10
+ With the new `cache: true` an answer is kept for `staleTime` seconds (30 by default) and shared by every cached
11
+ provider asking the same thing — same method, URL, credentials and headers, the token included, so two visitors
12
+ never share one. Past its time the answer is still drawn at once and a fresh one is fetched behind it; an answer
13
+ nobody shows is forgotten after `gcTime` seconds (300 by default). A refused request (`4xx`/`5xx`) is shown but
14
+ never kept. **The cache is off unless an element asks for it**: an uncached provider behaves as before, asking on
15
+ every mount and sharing nothing.
16
+
17
+ The cache is `@plitzi/sdk-shared/queries` (`queryCache`, `useQuery`, `invalidateQueries`,
18
+ `invalidateQueriesForWrite`), a nexus store written with the per-path `ttl` of `@plitzi/nexus` 1.2.0, which every
19
+ consumer now requires. It reacts to the store's own freshness events, so anything that expires a query's path
20
+ makes the providers on screen ask again.
21
+
22
+ An answer stops counting as current before its time when the element's `performQuery` runs (it always asks
23
+ again), when a flow runs the new global `invalidateQueries` step (source `queries`: `elements`, api container ids
24
+ — a container's requests are tagged with its own id — and/or a `url` prefix), and after a write. Both write steps
25
+ gained `invalidateQueries` / `invalidateElements`: a `webHook` sent with anything but `GET`/`HEAD` refreshes the
26
+ requests to its own site by default, a completed `runServerAction` refreshes all of them by default, and either
27
+ can name containers instead or refresh nothing. A `writeRecord` refreshes all. Providers on screen ask again at
28
+ once; the rest when they are next shown. A sign-in, sign-out or change of account drops everything held. Server-driven
29
+ providers and RSC are untouched.
30
+
31
+ - **A `webHook` that reads can be cached** (`cache`, `staleTime`), in the same cache and under the same key as an api
32
+ container asking the same thing. Its declaration now lives beside it (`utility/webHookSpec`) and the authoring
33
+ catalog gathers it instead of keeping a copy. A `HEAD` is no longer sent with a body, which `fetch` refused.
34
+
35
+ - **Step params can pick several elements.** A new param type, `elementIds` (with an `elementType` filter), is drawn
36
+ in the flow editor as a picker: the matching elements of the space to add, and the picked ones as chips to remove —
37
+ an id no element answers to any more is kept and flagged. The value is a list of ids, so nobody types a
38
+ comma-separated list; `invalidateQueries.elements` and the write steps' `invalidateElements` use it, and the MCP
39
+ catalog says which element type each such param takes.
40
+
41
+ - The dev-tools' Store tab lists the paths a store holds with a TTL — how long ago each was written, what is left
42
+ of it, and a button to expire one or all of them. The query cache appears there as "Queries".
43
+
44
+ - `useApi` no longer takes `params`: nothing passed them, and a GET cannot carry a body anyway. A mock now answers
45
+ synchronously, without a loading frame, and a provider whose URL has not resolved asks for nothing.
46
+
47
+ ### Patch Changes
48
+
49
+ - Updated dependencies
50
+ - @plitzi/sdk-navigation@0.36.0
51
+ - @plitzi/sdk-schema@0.36.0
52
+ - @plitzi/sdk-shared@0.36.0
53
+ - @plitzi/sdk-style@0.36.0
54
+
55
+ ## 0.35.10
56
+
57
+ ### Patch Changes
58
+
59
+ - **Breaking:** the space setting that lets a published site open the dev tools is now `settings.debugMode`, and
60
+ `settings.devTools` is gone.
61
+
62
+ It was always the same decision the SDK and the page server call `debugMode` — whether the dev-tools panel is
63
+ authorized — and having a third name for it invited confusing it with `devMode`, which is a different thing: it turns a
64
+ deployment into a development server (the unminified bundles, request timings, and the full action trace with every
65
+ step's results). A space can authorize debugging for its own site; it can never make its server a development one.
66
+
67
+ A space that stored `settings.devTools` needs it moved to `settings.debugMode`; nothing reads the old key.
68
+
69
+ - Updated dependencies
70
+ - @plitzi/sdk-navigation@0.35.10
71
+ - @plitzi/sdk-schema@0.35.10
72
+ - @plitzi/sdk-shared@0.35.10
73
+ - @plitzi/sdk-style@0.35.10
74
+
3
75
  ## 0.35.9
4
76
 
5
77
  ### Patch Changes
@@ -2,58 +2,59 @@ import { useIsSelectedInstance as e } from "./instanceRegistry.mjs";
2
2
  import t from "./components/DevToolsOverlay/index.mjs";
3
3
  import n from "./DevToolsContextProvider.mjs";
4
4
  import r from "./useHydrated.mjs";
5
- import i from "./useRegisterRootStore.mjs";
6
- import a from "@plitzi/plitzi-ui/ContainerShadow";
7
- import o from "@plitzi/plitzi-ui/hooks/useStorage";
8
- import s from "clsx";
9
- import { useCallback as c, useRef as l, useState as u } from "react";
10
- import { DevStoreScopeContext as d } from "@plitzi/nexus/react";
11
- import f from "@plitzi/sdk-shared/theme/useTheme";
12
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
5
+ import i from "./useRegisterQueriesStore.mjs";
6
+ import a from "./useRegisterRootStore.mjs";
7
+ import o from "@plitzi/plitzi-ui/ContainerShadow";
8
+ import s from "@plitzi/plitzi-ui/hooks/useStorage";
9
+ import c from "clsx";
10
+ import { useCallback as l, useRef as u, useState as d } from "react";
11
+ import { DevStoreScopeContext as f } from "@plitzi/nexus/react";
12
+ import p from "@plitzi/sdk-shared/theme/useTheme";
13
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
13
14
  //#region src/DevToolsContainer.tsx
14
- var h = "horizontal", g = 0, _ = ({ children: _, className: v, innerClassName: y, enabled: b = !1, instanceId: x, renderMode: S = "default", devToolsStyle: C = "", devToolsStyleLink: w = "" }) => {
15
- let { resolvedTheme: T } = f(), [E, D] = o("plitzi-sdk.dev-tools.orientation", h), [O, k] = o("plitzi-sdk.dev-tools.collapsed", !0), [A, j] = o("plitzi-sdk.dev-tools.tab", "logs"), [M, N] = u(), P = l(void 0);
16
- P.current ||= `sdk-instance-${++g}`;
17
- let F = x || P.current, I = r(), L = I ? E : h, R = e(F, b);
18
- i(b);
19
- let z = c((e) => D(e), [D]), B = c((e) => {
20
- e && j("logs"), N(e), k(!1);
21
- }, [k, j]), V = c(() => k(!0), [k]), H = c((e) => {
22
- N(void 0), j(e);
23
- }, [j]);
24
- return b ? /* @__PURE__ */ m("div", {
25
- className: s("relative flex grow overflow-auto", {
26
- "flex-col": L === "horizontal",
27
- "h-screen": L === "vertical"
28
- }, v),
29
- children: [/* @__PURE__ */ p(d, {
30
- value: F,
31
- children: /* @__PURE__ */ p("div", {
32
- className: s("grow basis-0 flex-col overflow-auto", y),
33
- children: _
15
+ var g = "horizontal", _ = 0, v = ({ children: v, className: y, innerClassName: b, enabled: x = !1, instanceId: S, renderMode: C = "default", devToolsStyle: w = "", devToolsStyleLink: T = "" }) => {
16
+ let { resolvedTheme: E } = p(), [D, O] = s("plitzi-sdk.dev-tools.orientation", g), [k, A] = s("plitzi-sdk.dev-tools.collapsed", !0), [j, M] = s("plitzi-sdk.dev-tools.tab", "logs"), [N, P] = d(), F = u(void 0);
17
+ F.current ||= `sdk-instance-${++_}`;
18
+ let I = S || F.current, L = r(), R = L ? D : g, z = e(I, x);
19
+ a(x), i(z, I);
20
+ let B = l((e) => O(e), [O]), V = l((e) => {
21
+ e && M("logs"), P(e), A(!1);
22
+ }, [A, M]), H = l(() => A(!0), [A]), U = l((e) => {
23
+ P(void 0), M(e);
24
+ }, [M]);
25
+ return x ? /* @__PURE__ */ h("div", {
26
+ className: c("relative flex grow overflow-auto", {
27
+ "flex-col": R === "horizontal",
28
+ "h-screen": R === "vertical"
29
+ }, y),
30
+ children: [/* @__PURE__ */ m(f, {
31
+ value: I,
32
+ children: /* @__PURE__ */ m("div", {
33
+ className: c("grow basis-0 flex-col overflow-auto", b),
34
+ children: v
34
35
  })
35
- }), R && I && /* @__PURE__ */ m(n, { children: [S === "default" && /* @__PURE__ */ p(t, {
36
- className: s({ dark: T === "dark" }),
37
- collapsed: O,
38
- orientation: L,
39
- tabSelected: A,
40
- logTypeFilter: M,
41
- onOpen: B,
42
- onCollapse: V,
43
- onTabSelect: H,
44
- onChangeOrientation: z
45
- }), S === "shadow" && /* @__PURE__ */ m(a, { children: [w && /* @__PURE__ */ p(a.Link, { href: w }), /* @__PURE__ */ m(a.Content, { children: [/* @__PURE__ */ p("style", { dangerouslySetInnerHTML: { __html: C } }), /* @__PURE__ */ p(t, {
46
- className: s({ dark: T === "dark" }),
47
- collapsed: O,
48
- orientation: L,
49
- tabSelected: A,
50
- logTypeFilter: M,
51
- onOpen: B,
52
- onCollapse: V,
53
- onTabSelect: H,
54
- onChangeOrientation: z
36
+ }), z && L && /* @__PURE__ */ h(n, { children: [C === "default" && /* @__PURE__ */ m(t, {
37
+ className: c({ dark: E === "dark" }),
38
+ collapsed: k,
39
+ orientation: R,
40
+ tabSelected: j,
41
+ logTypeFilter: N,
42
+ onOpen: V,
43
+ onCollapse: H,
44
+ onTabSelect: U,
45
+ onChangeOrientation: B
46
+ }), C === "shadow" && /* @__PURE__ */ h(o, { children: [T && /* @__PURE__ */ m(o.Link, { href: T }), /* @__PURE__ */ h(o.Content, { children: [/* @__PURE__ */ m("style", { dangerouslySetInnerHTML: { __html: w } }), /* @__PURE__ */ m(t, {
47
+ className: c({ dark: E === "dark" }),
48
+ collapsed: k,
49
+ orientation: R,
50
+ tabSelected: j,
51
+ logTypeFilter: N,
52
+ onOpen: V,
53
+ onCollapse: H,
54
+ onTabSelect: U,
55
+ onChangeOrientation: B
55
56
  })] })] })] })]
56
- }) : _;
57
+ }) : v;
57
58
  };
58
59
  //#endregion
59
- export { _ as default };
60
+ export { v as default };
@@ -1,59 +1,64 @@
1
- import e from "./helpers/mapFunctionValues.mjs";
2
- import t from "./helpers/renderFunctionValue.mjs";
3
- import { useSelectedStore as n } from "../../../../scope/useScope.mjs";
4
- import r from "../../../../scope/useStoreState.mjs";
5
- import i from "@plitzi/plitzi-ui/hooks/useStorage";
6
- import a from "clsx";
7
- import { use as o, useCallback as s, useMemo as c } from "react";
8
- import l from "@plitzi/sdk-shared/theme/useTheme";
9
- import u from "@plitzi/sdk-shared/devTools/DevToolsContext";
10
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
11
- import p from "@uiw/react-json-view";
12
- import * as m from "@uiw/react-json-view/vscode";
13
- import { emptyObject as h } from "@plitzi/sdk-shared/helpers/utils";
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";
14
15
  //#region src/components/DevToolsPanel/tabs/StoreViewer/StoreViewer.tsx
15
- var g = {
16
- ...m.vscodeTheme,
16
+ var _ = {
17
+ ...h.vscodeTheme,
17
18
  width: "100%",
18
19
  height: "100%",
19
20
  overflow: "auto",
20
21
  padding: "8px",
21
22
  fontSize: "14px"
22
- }, _ = (e) => a("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"), v = ({ elementSelected: a }) => {
23
- let { resolvedTheme: m } = l(), { getData: v } = o(u), y = n(), [b, x] = i("plitzi-sdk.dev-tools.store.view", "own"), S = r(a ? void 0 : y, b === "merged"), C = s(() => x("own"), [x]), w = s(() => x("merged"), [x]), T = a ? v?.(`getElementDataSource-${a}`) : S, E = c(() => e(T), [T]);
24
- return /* @__PURE__ */ f("div", {
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
26
  className: "flex h-full w-full flex-col",
26
- children: [!a && /* @__PURE__ */ f("div", {
27
- className: "flex shrink-0 items-center gap-1 border-b border-zinc-200 px-2 py-1 text-[11px] dark:border-zinc-800",
28
- children: [
29
- /* @__PURE__ */ d("span", {
30
- className: "mr-1 text-zinc-400 dark:text-zinc-500",
31
- children: "View"
32
- }),
33
- /* @__PURE__ */ d("button", {
34
- className: _(b === "own"),
35
- onClick: C,
36
- title: "Only this scope's own layer",
37
- children: "Own"
38
- }),
39
- /* @__PURE__ */ d("button", {
40
- className: _(b === "merged"),
41
- onClick: w,
42
- title: "Own layer merged over the parent scope chain",
43
- children: "Merged"
44
- })
45
- ]
46
- }), /* @__PURE__ */ d(p, {
47
- value: E ?? h,
48
- style: m === "dark" ? g : void 0,
49
- enableClipboard: !1,
50
- indentWidth: 15,
51
- collapsed: 2,
52
- displayObjectSize: !1,
53
- displayDataTypes: !1,
54
- children: /* @__PURE__ */ d(p.String, { render: t })
55
- })]
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
+ ]
56
61
  });
57
62
  };
58
63
  //#endregion
59
- export { v as default };
64
+ export { y as default };
@@ -0,0 +1,11 @@
1
+ import { DevStore } from '@plitzi/nexus';
2
+ export type FreshnessPanelProps = {
3
+ store: DevStore | undefined;
4
+ };
5
+ /**
6
+ * The paths of the selected store that were written with a TTL: how old each one is, how long it has left, and a
7
+ * way to expire it — which is how a developer checks that whatever depends on it refreshes. Absent for a store that
8
+ * holds none, so it costs nothing to look at the others.
9
+ */
10
+ declare const FreshnessPanel: ({ store }: FreshnessPanelProps) => import("react").JSX.Element | null;
11
+ export default FreshnessPanel;
@@ -0,0 +1,49 @@
1
+ import e from "./components/FreshnessRow/index.mjs";
2
+ import { toRows as t } from "./helpers.mjs";
3
+ import n from "../../../../../../scope/useFreshnessRecords.mjs";
4
+ import { useCallback as r, useEffect as i, useMemo as a, useState as o } from "react";
5
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
+ //#region src/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/FreshnessPanel.tsx
7
+ var l = ({ store: l }) => {
8
+ let u = n(l), d = Object.keys(u).length, [f, p] = o(() => Date.now());
9
+ i(() => l?.watchFreshness(() => p(Date.now())), [l]), i(() => {
10
+ if (d === 0) return;
11
+ let e = setInterval(() => p(Date.now()), 1e3);
12
+ return () => clearInterval(e);
13
+ }, [d]);
14
+ let m = a(() => t(u, f), [u, f]), h = r((e) => {
15
+ l?.expire(e);
16
+ }, [l]), g = r(() => {
17
+ l?.expire();
18
+ }, [l]);
19
+ return d === 0 ? null : /* @__PURE__ */ c("div", {
20
+ className: "shrink-0 border-b border-zinc-200 text-[11px] dark:border-zinc-800",
21
+ children: [/* @__PURE__ */ c("div", {
22
+ className: "flex items-center gap-2 px-2 py-1",
23
+ children: [
24
+ /* @__PURE__ */ s("span", {
25
+ className: "font-medium text-zinc-600 dark:text-zinc-300",
26
+ children: "TTL"
27
+ }),
28
+ /* @__PURE__ */ s("span", {
29
+ className: "text-zinc-400 tabular-nums dark:text-zinc-500",
30
+ children: d
31
+ }),
32
+ /* @__PURE__ */ s("button", {
33
+ className: "ml-auto cursor-pointer rounded px-1.5 text-violet-600 hover:bg-violet-500/15 dark:text-violet-300",
34
+ onClick: g,
35
+ title: "Mark every path stale now",
36
+ children: "Expire all"
37
+ })
38
+ ]
39
+ }), /* @__PURE__ */ s("ul", {
40
+ className: "max-h-40 overflow-auto pb-1",
41
+ children: m.map((t) => /* @__PURE__ */ s(e, {
42
+ row: t,
43
+ onExpire: h
44
+ }, t.path))
45
+ })]
46
+ });
47
+ };
48
+ //#endregion
49
+ export { l as default };
@@ -0,0 +1,7 @@
1
+ import { FreshnessRowModel } from '../../helpers';
2
+ export type FreshnessRowProps = {
3
+ row: FreshnessRowModel;
4
+ onExpire: (path: string) => void;
5
+ };
6
+ declare const FreshnessRow: ({ row, onExpire }: FreshnessRowProps) => import("react").JSX.Element;
7
+ export default FreshnessRow;
@@ -0,0 +1,43 @@
1
+ import e from "clsx";
2
+ import { useCallback as t } from "react";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ //#region src/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/FreshnessRow.tsx
5
+ var i = ({ row: i, onExpire: a }) => {
6
+ let o = t(() => a(i.path), [a, i.path]);
7
+ return /* @__PURE__ */ r("li", {
8
+ className: "flex items-center gap-2 px-2 py-0.5",
9
+ children: [
10
+ /* @__PURE__ */ n("span", {
11
+ className: e("h-1.5 w-1.5 shrink-0 rounded-full", i.isStale ? "bg-zinc-400" : "bg-emerald-500"),
12
+ title: i.isStale ? "Stale" : "Current"
13
+ }),
14
+ /* @__PURE__ */ n("span", {
15
+ className: "min-w-0 grow truncate font-mono text-zinc-700 dark:text-zinc-200",
16
+ title: i.path,
17
+ children: i.path
18
+ }),
19
+ /* @__PURE__ */ n("span", {
20
+ className: "shrink-0 text-zinc-400 tabular-nums dark:text-zinc-500",
21
+ title: "Written",
22
+ children: i.age
23
+ }),
24
+ /* @__PURE__ */ r("span", {
25
+ className: "shrink-0 text-zinc-400 tabular-nums dark:text-zinc-500",
26
+ title: "TTL",
27
+ children: ["ttl ", i.ttl]
28
+ }),
29
+ /* @__PURE__ */ n("span", {
30
+ className: e("w-20 shrink-0 text-right tabular-nums", i.isStale ? "text-zinc-400 dark:text-zinc-500" : "text-emerald-600 dark:text-emerald-400"),
31
+ children: i.status
32
+ }),
33
+ /* @__PURE__ */ n("button", {
34
+ className: "shrink-0 cursor-pointer rounded px-1.5 text-violet-600 hover:bg-violet-500/15 dark:text-violet-300",
35
+ onClick: o,
36
+ title: "Mark stale now — anything listening refreshes it",
37
+ children: "Expire"
38
+ })
39
+ ]
40
+ });
41
+ };
42
+ //#endregion
43
+ export { i as default };
@@ -0,0 +1,3 @@
1
+ import { default as FreshnessRow } from './FreshnessRow';
2
+ export * from './FreshnessRow';
3
+ export default FreshnessRow;
@@ -0,0 +1,5 @@
1
+ import e from "./FreshnessRow.mjs";
2
+ //#region src/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.ts
3
+ var t = e;
4
+ //#endregion
5
+ export { t as default };
@@ -0,0 +1,14 @@
1
+ import { PathFreshness } from '@plitzi/nexus';
2
+ export type FreshnessRowModel = {
3
+ path: string;
4
+ isStale: boolean;
5
+ /** How long ago it was written, e.g. `12s ago`. */
6
+ age: string;
7
+ /** `18s left` while current, `stale` once it is not. */
8
+ status: string;
9
+ /** The TTL it was written with, or what was left of it when an `expire` cut it short. */
10
+ ttl: string;
11
+ };
12
+ export declare const formatDuration: (ms: number) => string;
13
+ /** One row per record, stale ones last so what is still being served is what the panel shows first. */
14
+ export declare const toRows: (records: Readonly<Record<string, PathFreshness>>, now: number) => FreshnessRowModel[];
@@ -0,0 +1,18 @@
1
+ //#region src/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/helpers.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
+ }, t = (t, n) => Object.entries(t).map(([t, { updatedAt: r, expiresAt: i }]) => {
8
+ let a = n >= i, o = Number.isFinite(i), s = "no expiry";
9
+ return a ? s = "stale" : o && (s = `${e(i - n)} left`), {
10
+ path: t,
11
+ isStale: a,
12
+ age: `${e(n - r)} ago`,
13
+ status: s,
14
+ ttl: o ? e(i - r) : "∞"
15
+ };
16
+ }).sort((e, t) => Number(e.isStale) - Number(t.isStale) || e.path.localeCompare(t.path));
17
+ //#endregion
18
+ export { e as formatDuration, t as toRows };
@@ -0,0 +1,3 @@
1
+ import { default as FreshnessPanel } from './FreshnessPanel';
2
+ export * from './FreshnessPanel';
3
+ export default FreshnessPanel;
@@ -0,0 +1,5 @@
1
+ import e from "./FreshnessPanel.mjs";
2
+ //#region src/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.ts
3
+ var t = e;
4
+ //#endregion
5
+ export { t as default };
@@ -0,0 +1,3 @@
1
+ import { DevStore, PathFreshness } from '@plitzi/nexus';
2
+ declare const useFreshnessRecords: (store: DevStore | undefined) => Readonly<Record<string, PathFreshness>>;
3
+ export default useFreshnessRecords;
@@ -0,0 +1,8 @@
1
+ import { useCallback as e, useSyncExternalStore as t } from "react";
2
+ //#region src/scope/useFreshnessRecords.ts
3
+ var n = {}, r = () => () => {}, i = (i) => {
4
+ let a = e((e) => i ? i.watchFreshness(e) : r(), [i]), o = e(() => i ? i.getFreshnessRecords() : n, [i]);
5
+ return t(a, o, o);
6
+ };
7
+ //#endregion
8
+ export { i as default };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Puts the page's cached browser requests in the dev store registry, as "Queries".
3
+ *
4
+ * The cache is a store of its own, created by no provider, so nexus never registers it. It is tagged with the
5
+ * instance showing the panel: the panel reads an untagged store as the app root and opens it by default. One page
6
+ * has one cache, so only the selected instance registers it — a second registration of the same store would drop
7
+ * both when either unmounts.
8
+ */
9
+ declare const useRegisterQueriesStore: (enabled: boolean, instanceId: string) => void;
10
+ export default useRegisterQueriesStore;
@@ -0,0 +1,11 @@
1
+ import { useEffect as e } from "react";
2
+ import { registerDevStore as t } from "@plitzi/nexus";
3
+ import { queryCache as n } from "@plitzi/sdk-shared/queries";
4
+ //#region src/useRegisterQueriesStore.ts
5
+ var r = (r, i) => {
6
+ e(() => {
7
+ if (r) return t(n.store, i, "Queries");
8
+ }, [r, i]);
9
+ };
10
+ //#endregion
11
+ export { r as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plitzi/sdk-dev-tools",
3
- "version": "0.35.9",
3
+ "version": "0.36.0",
4
4
  "license": "AGPL-3.0",
5
5
  "files": [
6
6
  "dist"
@@ -478,6 +478,34 @@
478
478
  "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/StoreViewer.d.ts",
479
479
  "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/StoreViewer.mjs"
480
480
  },
481
+ "./components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel": {
482
+ "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.d.ts",
483
+ "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.mjs"
484
+ },
485
+ "./components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/FreshnessPanel": {
486
+ "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/FreshnessPanel.d.ts",
487
+ "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/FreshnessPanel.mjs"
488
+ },
489
+ "./components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow": {
490
+ "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.d.ts",
491
+ "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.mjs"
492
+ },
493
+ "./components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/FreshnessRow": {
494
+ "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/FreshnessRow.d.ts",
495
+ "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/FreshnessRow.mjs"
496
+ },
497
+ "./components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index": {
498
+ "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.d.ts",
499
+ "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/components/FreshnessRow/index.mjs"
500
+ },
501
+ "./components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/helpers": {
502
+ "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/helpers.d.ts",
503
+ "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/helpers.mjs"
504
+ },
505
+ "./components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index": {
506
+ "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.d.ts",
507
+ "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/components/FreshnessPanel/index.mjs"
508
+ },
481
509
  "./components/DevToolsPanel/tabs/StoreViewer/helpers/mapFunctionValues": {
482
510
  "types": "./dist/components/DevToolsPanel/tabs/StoreViewer/helpers/mapFunctionValues.d.ts",
483
511
  "import": "./dist/components/DevToolsPanel/tabs/StoreViewer/helpers/mapFunctionValues.mjs"
@@ -726,6 +754,10 @@
726
754
  "types": "./dist/scope/useDevStores.d.ts",
727
755
  "import": "./dist/scope/useDevStores.mjs"
728
756
  },
757
+ "./scope/useFreshnessRecords": {
758
+ "types": "./dist/scope/useFreshnessRecords.d.ts",
759
+ "import": "./dist/scope/useFreshnessRecords.mjs"
760
+ },
729
761
  "./scope/useScope": {
730
762
  "types": "./dist/scope/useScope.d.ts",
731
763
  "import": "./dist/scope/useScope.mjs"
@@ -738,6 +770,10 @@
738
770
  "types": "./dist/useHydrated.d.ts",
739
771
  "import": "./dist/useHydrated.mjs"
740
772
  },
773
+ "./useRegisterQueriesStore": {
774
+ "types": "./dist/useRegisterQueriesStore.d.ts",
775
+ "import": "./dist/useRegisterQueriesStore.mjs"
776
+ },
741
777
  "./useRegisterRootStore": {
742
778
  "types": "./dist/useRegisterRootStore.d.ts",
743
779
  "import": "./dist/useRegisterRootStore.mjs"
@@ -769,12 +805,12 @@
769
805
  "react-dom": "^19"
770
806
  },
771
807
  "dependencies": {
772
- "@plitzi/nexus": "^1.1.4",
808
+ "@plitzi/nexus": "^1.2.0",
773
809
  "@plitzi/plitzi-ui": "^1.6.21",
774
- "@plitzi/sdk-navigation": "0.35.9",
775
- "@plitzi/sdk-schema": "0.35.9",
776
- "@plitzi/sdk-shared": "0.35.9",
777
- "@plitzi/sdk-style": "0.35.9",
810
+ "@plitzi/sdk-navigation": "0.36.0",
811
+ "@plitzi/sdk-schema": "0.36.0",
812
+ "@plitzi/sdk-shared": "0.36.0",
813
+ "@plitzi/sdk-style": "0.36.0",
778
814
  "@uiw/react-json-view": "^2.0.0-alpha.43",
779
815
  "clsx": "^2.1.1"
780
816
  }