@hollis-labs/kit-admin 0.0.0-stage → 0.1.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/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +129 -2
- package/dist/admin.js +160 -0
- package/dist/admin.js.map +1 -0
- package/dist/charts.js +9 -0
- package/dist/charts.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/model.js +24 -0
- package/dist/model.js.map +1 -0
- package/dist/src/admin.d.ts +7 -0
- package/dist/src/charts.d.ts +3 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/model.d.ts +4 -0
- package/dist/src/types.d.ts +93 -0
- package/package.json +93 -4
- package/src/styles/source.css +1 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - 2026-10-03 - first release
|
|
4
|
+
|
|
5
|
+
- Render callback-free Settings as read-only text even for writable declarations,
|
|
6
|
+
using a presentation copy without changing host data or backend capabilities.
|
|
7
|
+
|
|
8
|
+
- Controlled admin navigation/content/standalone composition per the
|
|
9
|
+
[admin shell spec](../../docs/admin-shell-spec.md), over existing dashboard
|
|
10
|
+
layouts, settings provenance/setup and observation components. Host-owned
|
|
11
|
+
data, routes and commands remain controlled inputs.
|
|
12
|
+
- Require design-components, design-tokens and kit-dashboard `^0.4.0`,
|
|
13
|
+
kit-settings `^0.2.0` and kit-observe `^0.1.1`.
|
|
14
|
+
- Keep Dashboard a declaration directory and enforce canonical resource sections,
|
|
15
|
+
unavailable discovery/group states and host-owned context.
|
|
16
|
+
- Include opt-in `/charts` delegation, fixture demos and behavioral/browser evidence.
|
|
17
|
+
- Use optional settings text presentation when the host supplies no settings
|
|
18
|
+
callbacks, so read-only preferences render labelled values without disabled
|
|
19
|
+
editor affordances. Keep source, locks and apply state in canonical Settings.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Chrispian Burks / Hollis Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,130 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @hollis-labs/kit-admin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**0.1.0** is the first release of controlled admin composition, implemented
|
|
4
|
+
from the approved [admin shell spec](../../docs/admin-shell-spec.md). The host
|
|
5
|
+
owns data, routes and commands.
|
|
6
|
+
|
|
7
|
+
`AdminNavigation` projects declared destinations through dashboard `NavRail`.
|
|
8
|
+
`AdminContent` supplies the inner page region for an existing host shell.
|
|
9
|
+
`AdminShell` combines those with the existing base `AppShell`, dashboard
|
|
10
|
+
`PageHeader` and `ListPageLayout`. Use one viewport shell and one page scroll
|
|
11
|
+
owner; embedded apps use `AdminContent` rather than nesting `AdminShell`.
|
|
12
|
+
|
|
13
|
+
| Page | Composition |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Dashboard | App identity, declaration revision and links to canonical pages/groups; no config values or runtime KPIs. |
|
|
16
|
+
| Settings | One `SettingsProvenanceRenderer` for the host-selected group; source, locks, scope and pending application stay here. |
|
|
17
|
+
| Settings, `mode: 'setup'` | Existing `SettingsWizard` over the same groups/drafts, replacing the normal editor. Host controls step/checks/results; optional `setupContext` supplies provenance summaries beside it. Group links return to normal Settings. |
|
|
18
|
+
| Status | Existing `HealthSummary` and `StatCollection`; no configuration mutation controls. |
|
|
19
|
+
| Diagnostics | Existing `DiagnosticPanel`; an explicitly supplied series renderer receives existing `SampleSeriesViewProps`. |
|
|
20
|
+
|
|
21
|
+
## Controlled host inputs
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { AdminShell } from '@hollis-labs/kit-admin'
|
|
25
|
+
import type { AdminContentProps } from '@hollis-labs/kit-admin'
|
|
26
|
+
|
|
27
|
+
// The application supplies validated projections and its own adapters.
|
|
28
|
+
export function AppAdmin({ state }: { state: AdminContentProps }) {
|
|
29
|
+
return <AdminShell {...state} />
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The host supplies `contextKey`, discovery phase/manifest, page/group/mode
|
|
34
|
+
selection, `destination(target)` returning either an `href` or `onSelect`,
|
|
35
|
+
per-group settings read states, optional settings callbacks, observation maps
|
|
36
|
+
and `nowMs`. `AdminManifest` is a minimal **presentation projection**, not a wire
|
|
37
|
+
registry or complete manifest validator. Transport fields/endpoints and wire
|
|
38
|
+
identity remain with the host and go-envelopes. `adminManifestProblem` fences
|
|
39
|
+
presentation identity, required arrays, contract version, resource placement
|
|
40
|
+
and observation age; supported settings schemas/provenance are checked by
|
|
41
|
+
kit-settings. The host validates all wire schemas, units, bounds, capabilities,
|
|
42
|
+
auth and diagnostic payloads before supplying these projections.
|
|
43
|
+
|
|
44
|
+
Keep all supplied data scoped to the current authorized `contextKey`; a discovery
|
|
45
|
+
context mismatch hides old content globally. Clear old snapshots/drafts and
|
|
46
|
+
cancel requests on auth/context changes. Host-owned routes/history, fetching,
|
|
47
|
+
retry/poll schedules, clocks, effective permissions, per-group revisions/ETags,
|
|
48
|
+
409/412 reconciliation and uncertain write outcomes never move into this kit.
|
|
49
|
+
Accept and clear only successful group drafts. Reset means host resolution,
|
|
50
|
+
not a browser-computed fallback. Secret replacements remain transient in host
|
|
51
|
+
memory; never persist/log them or infer their values from declarations.
|
|
52
|
+
|
|
53
|
+
No writes occur on mount. Callbacks receive existing controlled settings intents.
|
|
54
|
+
`onApply` is omitted by default; an authorized host may explicitly supply its own
|
|
55
|
+
workflow. Save/setup completion never implies restart, readiness or health. The
|
|
56
|
+
host preserves per-field pending state independently of this command's
|
|
57
|
+
`restart_required` metadata, including no-op outcomes.
|
|
58
|
+
|
|
59
|
+
Failed initial discovery is globally unavailable. Retained declarations during
|
|
60
|
+
refresh/error are labelled and disable editing and command callbacks. A failed
|
|
61
|
+
individual group read disables only that group; sibling destinations remain
|
|
62
|
+
available. Contradictory sections are rejected, never relocated. Missing arrays
|
|
63
|
+
are malformed; empty arrays omit optional nav items and explain unsupported deep
|
|
64
|
+
links. All-empty Dashboard says “No admin resources declared”. Missing observation
|
|
65
|
+
snapshots stay unavailable, never fabricated healthy or zero values.
|
|
66
|
+
|
|
67
|
+
## Optional charts and CSS
|
|
68
|
+
|
|
69
|
+
Root imports no chart implementation and re-exports no sibling components.
|
|
70
|
+
Opt in explicitly, or supply another renderer with the same existing props:
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { renderAdminSeries } from '@hollis-labs/kit-admin/charts'
|
|
74
|
+
// <AdminShell {...state} renderSeries={renderAdminSeries} />
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The adapter delegates to kit-observe `/charts`, which delegates to dashboard
|
|
78
|
+
`TimestampSampleChart`; it does not copy chart code or synthesize history.
|
|
79
|
+
Dashboard remains a dependency for navigation/layout even without series.
|
|
80
|
+
A host using Tailwind v4 imports the theme and each composition's class sources:
|
|
81
|
+
|
|
82
|
+
```css
|
|
83
|
+
@import '@hollis-labs/kit-dashboard/theme.css';
|
|
84
|
+
@import '@hollis-labs/kit-settings/source.css';
|
|
85
|
+
@import '@hollis-labs/kit-observe/source.css';
|
|
86
|
+
@import '@hollis-labs/kit-admin/source.css';
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Set the host's theme and document shell reset as usual. Style evidence uses
|
|
90
|
+
`sysop-p4-white` and includes dashboard CSS. Internal ranges are components and
|
|
91
|
+
dashboard `^0.4.0`, settings `^0.2.0`, observe `^0.1.1`, tokens `^0.4.0`; React 19,
|
|
92
|
+
Tailwind 4, Base UI and lucide use the workspace's existing peers.
|
|
93
|
+
|
|
94
|
+
## Local fixture review
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm run build --workspace=@hollis-labs/kit-admin
|
|
98
|
+
npm run demo:build --workspace=@hollis-labs/kit-admin
|
|
99
|
+
npm run demo --workspace=@hollis-labs/kit-admin -- --host 127.0.0.1
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Package-local `prebuild` builds dashboard/settings/observe prerequisites because
|
|
103
|
+
npm's root workspace order visits kit-admin first. No root build script changes.
|
|
104
|
+
The demo imports the built kit-admin entrypoints. Both approved manifest examples
|
|
105
|
+
render all four pages; `?app=nanite|tachyon|tether&page=...` chooses a fixture.
|
|
106
|
+
`mode=setup`, `failure=first|refresh|group` and `embedded=yes` exercise alternate
|
|
107
|
+
composition/failure modes. The separate `settings-only.html` entry imports root
|
|
108
|
+
only. Build emits a manual module inventory for reviewing chart isolation.
|
|
109
|
+
Fixture settings snapshots are explicit existing kit-settings fixtures, not
|
|
110
|
+
manifest-derived defaults. Command callbacks record intent only, without saved
|
|
111
|
+
outcomes, persistence, actual validation, secret logging or restart.
|
|
112
|
+
|
|
113
|
+
[Browser evidence and reproduction](docs/evidence.md) covers the approved
|
|
114
|
+
examples at 1280/390 and a Tether-style desired-config/runtime split. Unit tests
|
|
115
|
+
exercise fail-closed behavior, controlled callbacks, alternate setup, field
|
|
116
|
+
focus and canonical placement. Fixtures establish page/state mapping,
|
|
117
|
+
independently of the real-backend proof below. App-owned wire adapters, Folio
|
|
118
|
+
wiring and application adoption remain separate work.
|
|
119
|
+
|
|
120
|
+
## Proof and limits
|
|
121
|
+
|
|
122
|
+
Packed consumers have been exercised in Chromium against fixtures and against
|
|
123
|
+
one real Nanite backend in a **read-only configuration**: the page issues only
|
|
124
|
+
GET requests. The real-backend proof used an isolated scratch instance built
|
|
125
|
+
from production code, not a deployed production instance.
|
|
126
|
+
|
|
127
|
+
Not proven: writes through kit-admin against a real backend; Tachyon integration;
|
|
128
|
+
secret, wizard or conflict flows on a real backend; other browsers; or a shared
|
|
129
|
+
typed read client. Backend atomicity and two-app acceptance are not established
|
|
130
|
+
by the read-only Nanite proof.
|
package/dist/admin.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { jsx as e, jsxs as m, Fragment as $ } from "react/jsx-runtime";
|
|
2
|
+
import { SquareTerminal as j, Activity as O, Settings as V, LayoutDashboard as z } from "lucide-react";
|
|
3
|
+
import { Callout as v, AppShell as I, Button as E } from "@hollis-labs/design-components";
|
|
4
|
+
import { NavRail as M, PageHeader as W } from "@hollis-labs/kit-dashboard/ui";
|
|
5
|
+
import { ListPageLayout as G } from "@hollis-labs/kit-dashboard/layout";
|
|
6
|
+
import { settingsMetadataProblem as D, SettingsProvenanceRenderer as H, SettingsWizard as q } from "@hollis-labs/kit-settings";
|
|
7
|
+
import { HealthSummary as B, ObservationStatus as S, StatCollection as F, DiagnosticPanel as U } from "@hollis-labs/kit-observe";
|
|
8
|
+
import { adminManifestProblem as P, adminPages as R } from "./model.js";
|
|
9
|
+
const y = { dashboard: "Dashboard", settings: "Settings", status: "Status", diagnostics: "Diagnostics" }, J = { dashboard: z, settings: V, status: O, diagnostics: j };
|
|
10
|
+
function K(i) {
|
|
11
|
+
const { discovery: n, contextKey: c } = i;
|
|
12
|
+
if (!(n.contextKey !== c || P(n.manifest)))
|
|
13
|
+
return n.manifest;
|
|
14
|
+
}
|
|
15
|
+
function L({ destination: i, children: n }) {
|
|
16
|
+
return i.href !== void 0 ? /* @__PURE__ */ e("a", { className: "break-words text-control text-primary underline underline-offset-2 focus-visible:outline-ring", href: i.href, children: n }) : /* @__PURE__ */ e(E, { type: "button", size: "sm", variant: "outline", onClick: i.onSelect, children: n });
|
|
17
|
+
}
|
|
18
|
+
function f({ children: i }) {
|
|
19
|
+
return /* @__PURE__ */ e("p", { role: "status", className: "text-control text-fg-secondary", children: i });
|
|
20
|
+
}
|
|
21
|
+
function T({ manifest: i, props: n }) {
|
|
22
|
+
return /* @__PURE__ */ e("nav", { "aria-label": "Settings groups", className: "flex flex-wrap gap-3", children: i.settings.map((c) => /* @__PURE__ */ e(L, { destination: n.destination({ page: "settings", groupId: c.id }), children: c.label }, c.id)) });
|
|
23
|
+
}
|
|
24
|
+
function Q(i) {
|
|
25
|
+
const n = K(i), c = n ? R(n) : ["dashboard"];
|
|
26
|
+
return /* @__PURE__ */ e(M, { logoLabel: (n == null ? void 0 : n.app.label) ?? "Admin", items: c.map((l) => {
|
|
27
|
+
const a = J[l];
|
|
28
|
+
return { key: l, label: y[l], icon: /* @__PURE__ */ e(a, { className: "size-4", "aria-hidden": !0 }), active: i.selection.page === l, ...i.destination({ page: l }) };
|
|
29
|
+
}) });
|
|
30
|
+
}
|
|
31
|
+
function X({ props: i, manifest: n }) {
|
|
32
|
+
var N, A, _, C, k;
|
|
33
|
+
const { selection: c, settings: l = {}, settingsActions: a, setup: p } = i, o = i.discovery.phase === "ready", u = (d) => o && (d == null ? void 0 : d.phase) === "ready" && !!d.state, s = n.settings.find((d) => d.id === c.groupId), g = Object.fromEntries(n.settings.map((d) => {
|
|
34
|
+
var b;
|
|
35
|
+
return [d.id, (b = l[d.id]) == null ? void 0 : b.state];
|
|
36
|
+
})), t = (d, b) => {
|
|
37
|
+
u(l[d]) && (a == null || a.onDraftChange(d, b));
|
|
38
|
+
}, r = c.mode !== "setup";
|
|
39
|
+
let h = s, w = g;
|
|
40
|
+
const x = s && g[s.id];
|
|
41
|
+
return r && !a && s && x && !D(s, x) && (h = { ...s, capabilities: {
|
|
42
|
+
...s.capabilities,
|
|
43
|
+
can_validate: !1,
|
|
44
|
+
can_update: !1,
|
|
45
|
+
can_reset: !1
|
|
46
|
+
} }, w = { ...g, [s.id]: { ...x, draft: {}, values: Object.fromEntries(
|
|
47
|
+
Object.entries(x.values).map(([d, b]) => [d, b.editable ? { ...b, editable: !1, read_only_reason: "This view has no editing actions." } : b])
|
|
48
|
+
) } }), n.settings.length ? /* @__PURE__ */ m("section", { "aria-label": "Admin Settings", className: "flex min-w-0 flex-col gap-4", children: [
|
|
49
|
+
/* @__PURE__ */ e(T, { manifest: n, props: i }),
|
|
50
|
+
r && (s != null && s.scope) ? /* @__PURE__ */ m(f, { children: [
|
|
51
|
+
"Scope: ",
|
|
52
|
+
s.scope.kind,
|
|
53
|
+
s.scope.id ? ` / ${s.scope.id}` : ""
|
|
54
|
+
] }) : null,
|
|
55
|
+
r ? c.groupId ? s ? /* @__PURE__ */ m($, { children: [
|
|
56
|
+
((N = l[s.id]) == null ? void 0 : N.phase) === "error" ? /* @__PURE__ */ m(v, { tone: "danger", title: "Settings read failed", children: [
|
|
57
|
+
((A = l[s.id]) == null ? void 0 : A.error) ?? "The host could not read this group.",
|
|
58
|
+
" ",
|
|
59
|
+
(_ = l[s.id]) != null && _.state ? "Previous snapshot retained; writes suspended." : "No successful snapshot is available."
|
|
60
|
+
] }) : null,
|
|
61
|
+
((C = l[s.id]) == null ? void 0 : C.phase) === "loading" ? /* @__PURE__ */ e(f, { children: (k = l[s.id]) != null && k.state ? "Refreshing settings snapshot; writes suspended." : "Waiting for a settings snapshot." }) : null,
|
|
62
|
+
/* @__PURE__ */ e("fieldset", { disabled: !u(l[s.id]) || !a, className: "min-w-0 border-0 p-0", children: /* @__PURE__ */ e(
|
|
63
|
+
H,
|
|
64
|
+
{
|
|
65
|
+
contractVersion: n.contract_version,
|
|
66
|
+
groups: [h],
|
|
67
|
+
states: w,
|
|
68
|
+
onDraftChange: t,
|
|
69
|
+
readOnlyContext: !a,
|
|
70
|
+
onSave: u(l[s.id]) && (a != null && a.onSave) ? a.onSave : void 0,
|
|
71
|
+
onReset: u(l[s.id]) && (a != null && a.onReset) ? a.onReset : void 0,
|
|
72
|
+
onValidate: u(l[s.id]) && (a != null && a.onValidate) ? a.onValidate : void 0,
|
|
73
|
+
onApply: u(l[s.id]) && (a != null && a.onApply) ? a.onApply : void 0
|
|
74
|
+
},
|
|
75
|
+
`${i.contextKey}:${n.app.id}:${n.revision}:${s.id}`
|
|
76
|
+
) })
|
|
77
|
+
] }) : /* @__PURE__ */ e(v, { tone: "warning", title: "Settings group unavailable", children: "This declaration does not contain the selected group." }) : /* @__PURE__ */ e(f, { children: "Choose a settings group." }) : p ? /* @__PURE__ */ m($, { children: [
|
|
78
|
+
/* @__PURE__ */ e(f, { children: "Setup edits the same Settings groups. Source, locks and pending application remain in canonical Settings." }),
|
|
79
|
+
i.setupContext,
|
|
80
|
+
n.settings.some((d) => !u(l[d.id]) || g[d.id] && D(d, g[d.id])) ? /* @__PURE__ */ e(v, { tone: "warning", title: "Setup unavailable", children: "Every setup group needs a current, supported settings snapshot and provenance. Normal Settings remains available per group." }) : /* @__PURE__ */ e("fieldset", { disabled: !o || !a, className: "min-w-0 border-0 p-0", children: /* @__PURE__ */ e(
|
|
81
|
+
q,
|
|
82
|
+
{
|
|
83
|
+
...p,
|
|
84
|
+
contractVersion: n.contract_version,
|
|
85
|
+
groups: n.settings,
|
|
86
|
+
states: g,
|
|
87
|
+
onDraftChange: t,
|
|
88
|
+
busy: !o || !a || p.busy,
|
|
89
|
+
onComplete: (d) => {
|
|
90
|
+
o && a && p.onComplete(d);
|
|
91
|
+
},
|
|
92
|
+
onCheck: p.onCheck ? (d) => {
|
|
93
|
+
var b;
|
|
94
|
+
o && a && ((b = p.onCheck) == null || b.call(p, d));
|
|
95
|
+
} : void 0
|
|
96
|
+
},
|
|
97
|
+
`${i.contextKey}:${n.app.id}:${n.revision}:setup`
|
|
98
|
+
) })
|
|
99
|
+
] }) : /* @__PURE__ */ e(f, { children: "Setup unavailable: the host has not supplied setup controls." })
|
|
100
|
+
] }) : /* @__PURE__ */ e(f, { children: "Settings unsupported: no settings groups declared." });
|
|
101
|
+
}
|
|
102
|
+
function Y(i) {
|
|
103
|
+
const { discovery: n, selection: c, observations: l = {}, nowMs: a } = i;
|
|
104
|
+
if (n.contextKey !== i.contextKey) return /* @__PURE__ */ e(v, { tone: "warning", title: "Admin context unavailable", children: "The host must supply discovery and snapshots for the current authorized context." });
|
|
105
|
+
if (!n.manifest) return n.phase === "loading" ? /* @__PURE__ */ e(f, { children: "Loading admin declarations." }) : /* @__PURE__ */ e(v, { tone: "danger", title: "Admin unavailable", children: n.error ?? "No admin declaration is available." });
|
|
106
|
+
const p = P(n.manifest);
|
|
107
|
+
if (p) return /* @__PURE__ */ e(v, { tone: "danger", title: "Admin declaration unavailable", children: p });
|
|
108
|
+
const o = n.manifest, u = (t) => ({ phase: "idle", nowMs: a, staleAfterMs: t }), s = R(o);
|
|
109
|
+
let g;
|
|
110
|
+
return c.page === "dashboard" ? g = /* @__PURE__ */ m("section", { "aria-label": "Admin directory", className: "flex min-w-0 flex-col gap-4", children: [
|
|
111
|
+
/* @__PURE__ */ e("h2", { className: "text-heading text-fg", children: o.app.label }),
|
|
112
|
+
/* @__PURE__ */ m(f, { children: [
|
|
113
|
+
"Declaration revision: ",
|
|
114
|
+
o.revision
|
|
115
|
+
] }),
|
|
116
|
+
s.length === 1 ? /* @__PURE__ */ e(f, { children: "No admin resources declared" }) : /* @__PURE__ */ e("nav", { "aria-label": "Admin directory links", className: "flex flex-wrap gap-3", children: s.filter((t) => t !== "dashboard").map((t) => /* @__PURE__ */ e(L, { destination: i.destination({ page: t }), children: y[t] }, t)) }),
|
|
117
|
+
o.settings.length ? /* @__PURE__ */ e(T, { manifest: o, props: i }) : null
|
|
118
|
+
] }) : c.page === "settings" ? g = /* @__PURE__ */ e(X, { props: i, manifest: o }) : c.page === "status" ? g = !o.health.length && !o.stats.length ? /* @__PURE__ */ e(f, { children: "Status unsupported: no health or stats resources declared." }) : /* @__PURE__ */ m("section", { "aria-label": "Admin Status", className: "flex min-w-0 flex-col gap-4", children: [
|
|
119
|
+
o.health.map((t) => {
|
|
120
|
+
var h;
|
|
121
|
+
const r = (h = l.health) == null ? void 0 : h[t.id];
|
|
122
|
+
return r ? /* @__PURE__ */ e(B, { ...r, label: t.label }, t.id) : /* @__PURE__ */ e(S, { label: t.label, observation: u(t.stale_after_ms) }, t.id);
|
|
123
|
+
}),
|
|
124
|
+
o.stats.length ? /* @__PURE__ */ e(F, { label: "Stats", rows: o.stats.map((t) => {
|
|
125
|
+
var h;
|
|
126
|
+
const r = (h = l.stats) == null ? void 0 : h[t.id];
|
|
127
|
+
return { id: t.id, label: t.label, unit: t.unit, kind: t.kind, value: (r == null ? void 0 : r.value) ?? null, observation: (r == null ? void 0 : r.observation) ?? u(t.stale_after_ms) };
|
|
128
|
+
}) }) : null
|
|
129
|
+
] }) : c.page === "diagnostics" ? g = !o.series.length && !o.diagnostics.length ? /* @__PURE__ */ e(f, { children: "Diagnostics unsupported: no series or diagnostic resources declared." }) : /* @__PURE__ */ m("section", { "aria-label": "Admin Diagnostics", className: "flex min-w-0 flex-col gap-4", children: [
|
|
130
|
+
o.series.map((t) => {
|
|
131
|
+
var h;
|
|
132
|
+
const r = (h = l.series) == null ? void 0 : h[t.id];
|
|
133
|
+
return /* @__PURE__ */ e("section", { className: "min-w-0", children: i.renderSeries ? r ? i.renderSeries({ ...r, label: t.label, unit: t.unit, kind: t.kind, bounds: { maxPoints: t.max_points, maxWindowSeconds: t.max_window_seconds } }) : /* @__PURE__ */ e(S, { label: t.label, observation: u(t.stale_after_ms) }) : /* @__PURE__ */ m(v, { tone: "warning", title: "Series renderer unavailable", children: [
|
|
134
|
+
t.label,
|
|
135
|
+
": the host has not supplied a series renderer."
|
|
136
|
+
] }) }, t.id);
|
|
137
|
+
}),
|
|
138
|
+
o.diagnostics.map((t) => {
|
|
139
|
+
var h;
|
|
140
|
+
const r = (h = l.diagnostics) == null ? void 0 : h[t.id];
|
|
141
|
+
return r ? /* @__PURE__ */ e(U, { ...r, label: t.label, schema: t.schema }, t.id) : /* @__PURE__ */ e(S, { label: t.label, observation: u(t.stale_after_ms) }, t.id);
|
|
142
|
+
})
|
|
143
|
+
] }) : g = /* @__PURE__ */ e(v, { tone: "warning", title: "Admin page unavailable", children: "The host selected an unsupported page." }), /* @__PURE__ */ m("div", { className: "mx-auto flex w-full min-w-0 max-w-5xl flex-col gap-4 p-4 text-control text-fg", children: [
|
|
144
|
+
n.phase !== "ready" ? /* @__PURE__ */ m(v, { tone: "warning", title: "Previous declarations retained", children: [
|
|
145
|
+
n.error ?? "Refreshing admin declarations.",
|
|
146
|
+
" Content uses the last declaration; writes are suspended until discovery succeeds."
|
|
147
|
+
] }) : null,
|
|
148
|
+
g
|
|
149
|
+
] }, `${i.contextKey}:${o.app.id}:${o.revision}`);
|
|
150
|
+
}
|
|
151
|
+
function oe(i) {
|
|
152
|
+
const n = K(i);
|
|
153
|
+
return /* @__PURE__ */ e(I, { nav: /* @__PURE__ */ e(Q, { ...i }), children: /* @__PURE__ */ e(G, { header: /* @__PURE__ */ e(W, { title: `${(n == null ? void 0 : n.app.label) ?? "Admin"} / ${y[i.selection.page] ?? "Unavailable"}` }), children: /* @__PURE__ */ e(Y, { ...i }) }) });
|
|
154
|
+
}
|
|
155
|
+
export {
|
|
156
|
+
Y as AdminContent,
|
|
157
|
+
Q as AdminNavigation,
|
|
158
|
+
oe as AdminShell
|
|
159
|
+
};
|
|
160
|
+
//# sourceMappingURL=admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin.js","sources":["../src/admin.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { Activity, LayoutDashboard, Settings, SquareTerminal } from 'lucide-react'\nimport { AppShell, Button, Callout } from '@hollis-labs/design-components'\nimport { NavRail, PageHeader } from '@hollis-labs/kit-dashboard/ui'\nimport { ListPageLayout } from '@hollis-labs/kit-dashboard/layout'\nimport { SettingsProvenanceRenderer, SettingsWizard, settingsMetadataProblem } from '@hollis-labs/kit-settings'\nimport { DiagnosticPanel, HealthSummary, ObservationStatus, StatCollection } from '@hollis-labs/kit-observe'\nimport type { ObservationState } from '@hollis-labs/kit-observe'\nimport { adminManifestProblem, adminPages } from './model'\nimport type { AdminContentProps, AdminDestination, AdminManifest, AdminPage, AdminSettingsRead } from './types'\n\nconst labels = { dashboard: 'Dashboard', settings: 'Settings', status: 'Status', diagnostics: 'Diagnostics' } as const\nconst icons = { dashboard: LayoutDashboard, settings: Settings, status: Activity, diagnostics: SquareTerminal }\nfunction available(props: AdminContentProps): AdminManifest | undefined {\n const { discovery, contextKey } = props\n if (discovery.contextKey !== contextKey || adminManifestProblem(discovery.manifest)) return undefined\n return discovery.manifest\n}\nfunction Destination({ destination, children }: { destination: AdminDestination; children: ReactNode }) {\n return destination.href !== undefined\n ? <a className=\"break-words text-control text-primary underline underline-offset-2 focus-visible:outline-ring\" href={destination.href}>{children}</a>\n : <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={destination.onSelect}>{children}</Button>\n}\nfunction Notice({ children }: { children: ReactNode }) {\n return <p role=\"status\" className=\"text-control text-fg-secondary\">{children}</p>\n}\nfunction GroupLinks({ manifest, props }: { manifest: AdminManifest; props: AdminContentProps }) {\n return <nav aria-label=\"Settings groups\" className=\"flex flex-wrap gap-3\">{manifest.settings.map(group => <Destination key={group.id} destination={props.destination({ page: 'settings', groupId: group.id })}>{group.label}</Destination>)}</nav>\n}\n\n/** Navigation is only a projection of declared sections; the host owns routing/history. */\nexport function AdminNavigation(props: AdminContentProps) {\n const manifest = available(props)\n const pages: readonly AdminPage[] = manifest ? adminPages(manifest) : ['dashboard']\n return <NavRail logoLabel={manifest?.app.label ?? 'Admin'} items={pages.map(page => {\n const Icon = icons[page]\n return { key: page, label: labels[page], icon: <Icon className=\"size-4\" aria-hidden />, active: props.selection.page === page, ...props.destination({ page }) }\n })} />\n}\n\nfunction SettingsPage({ props, manifest }: { props: AdminContentProps; manifest: AdminManifest }) {\n const { selection, settings = {}, settingsActions: actions, setup } = props\n const contextReady = props.discovery.phase === 'ready'\n const readReady = (read: AdminSettingsRead | undefined) => contextReady && read?.phase === 'ready' && !!read.state\n const selected = manifest.settings.find(group => group.id === selection.groupId)\n const states = Object.fromEntries(manifest.settings.map(group => [group.id, settings[group.id]?.state]))\n const change: NonNullable<AdminContentProps['settingsActions']>['onDraftChange'] = (id, draft) => { if (readReady(settings[id])) actions?.onDraftChange(id, draft) }\n const normal = selection.mode !== 'setup'\n let presentedGroup = selected, presentedStates = states\n const state = selected && states[selected.id]\n // Limit only this view; validate the host inputs before copying so malformed\n // permissions/provenance stay unavailable rather than being repaired here.\n if (normal && !actions && selected && state && !settingsMetadataProblem(selected, state)) {\n presentedGroup = { ...selected, capabilities: {\n ...(selected.capabilities as Record<string, unknown>), can_validate: false, can_update: false, can_reset: false,\n } }\n presentedStates = { ...states, [selected.id]: { ...state, draft: {}, values: Object.fromEntries(\n Object.entries(state.values).map(([key, value]) => [key, value.editable\n ? { ...value, editable: false, read_only_reason: 'This view has no editing actions.' } : value]),\n ) } }\n }\n if (!manifest.settings.length) return <Notice>Settings unsupported: no settings groups declared.</Notice>\n return <section aria-label=\"Admin Settings\" className=\"flex min-w-0 flex-col gap-4\">\n <GroupLinks manifest={manifest} props={props} />\n {normal && selected?.scope ? <Notice>Scope: {selected.scope.kind}{selected.scope.id ? ` / ${selected.scope.id}` : ''}</Notice> : null}\n {normal ? !selection.groupId ? <Notice>Choose a settings group.</Notice> : !selected ? <Callout tone=\"warning\" title=\"Settings group unavailable\">This declaration does not contain the selected group.</Callout> : <>\n {settings[selected.id]?.phase === 'error' ? <Callout tone=\"danger\" title=\"Settings read failed\">{settings[selected.id]?.error ?? 'The host could not read this group.'} {settings[selected.id]?.state ? 'Previous snapshot retained; writes suspended.' : 'No successful snapshot is available.'}</Callout> : null}\n {settings[selected.id]?.phase === 'loading' ? <Notice>{settings[selected.id]?.state ? 'Refreshing settings snapshot; writes suspended.' : 'Waiting for a settings snapshot.'}</Notice> : null}\n <fieldset disabled={!readReady(settings[selected.id]) || !actions} className=\"min-w-0 border-0 p-0\">\n <SettingsProvenanceRenderer key={`${props.contextKey}:${manifest.app.id}:${manifest.revision}:${selected.id}`}\n contractVersion={manifest.contract_version} groups={[presentedGroup!]} states={presentedStates} onDraftChange={change} readOnlyContext={!actions}\n onSave={readReady(settings[selected.id]) && actions?.onSave ? actions.onSave : undefined}\n onReset={readReady(settings[selected.id]) && actions?.onReset ? actions.onReset : undefined}\n onValidate={readReady(settings[selected.id]) && actions?.onValidate ? actions.onValidate : undefined}\n onApply={readReady(settings[selected.id]) && actions?.onApply ? actions.onApply : undefined} />\n </fieldset>\n </> : !setup ? <Notice>Setup unavailable: the host has not supplied setup controls.</Notice> : <>\n <Notice>Setup edits the same Settings groups. Source, locks and pending application remain in canonical Settings.</Notice>\n {props.setupContext}\n {manifest.settings.some(group => !readReady(settings[group.id]) || (states[group.id] && settingsMetadataProblem(group, states[group.id]!))) ? <Callout tone=\"warning\" title=\"Setup unavailable\">Every setup group needs a current, supported settings snapshot and provenance. Normal Settings remains available per group.</Callout> : <fieldset disabled={!contextReady || !actions} className=\"min-w-0 border-0 p-0\">\n <SettingsWizard key={`${props.contextKey}:${manifest.app.id}:${manifest.revision}:setup`} {...setup}\n contractVersion={manifest.contract_version} groups={manifest.settings} states={states} onDraftChange={change}\n busy={!contextReady || !actions || setup.busy}\n onComplete={plan => { if (contextReady && actions) setup.onComplete(plan) }}\n onCheck={setup.onCheck ? id => { if (contextReady && actions) setup.onCheck?.(id) } : undefined} />\n </fieldset>}\n </>}\n </section>\n}\n\n/** Inner region for an existing host shell; no viewport wrapper, effects or local data store. */\nexport function AdminContent(props: AdminContentProps) {\n const { discovery, selection, observations = {}, nowMs } = props\n if (discovery.contextKey !== props.contextKey) return <Callout tone=\"warning\" title=\"Admin context unavailable\">The host must supply discovery and snapshots for the current authorized context.</Callout>\n if (!discovery.manifest) return discovery.phase === 'loading' ? <Notice>Loading admin declarations.</Notice> : <Callout tone=\"danger\" title=\"Admin unavailable\">{discovery.error ?? 'No admin declaration is available.'}</Callout>\n const problem = adminManifestProblem(discovery.manifest)\n if (problem) return <Callout tone=\"danger\" title=\"Admin declaration unavailable\">{problem}</Callout>\n const manifest = discovery.manifest\n const observation = (staleAfterMs: number): ObservationState => ({ phase: 'idle', nowMs, staleAfterMs })\n const pages = adminPages(manifest)\n let content: ReactNode\n if (selection.page === 'dashboard') content = <section aria-label=\"Admin directory\" className=\"flex min-w-0 flex-col gap-4\">\n <h2 className=\"text-heading text-fg\">{manifest.app.label}</h2>\n <Notice>Declaration revision: {manifest.revision}</Notice>\n {pages.length === 1 ? <Notice>No admin resources declared</Notice> : <nav aria-label=\"Admin directory links\" className=\"flex flex-wrap gap-3\">{pages.filter(page => page !== 'dashboard').map(page => <Destination key={page} destination={props.destination({ page })}>{labels[page]}</Destination>)}</nav>}\n {manifest.settings.length ? <GroupLinks manifest={manifest} props={props} /> : null}\n </section>\n else if (selection.page === 'settings') content = <SettingsPage props={props} manifest={manifest} />\n else if (selection.page === 'status') content = !manifest.health.length && !manifest.stats.length ? <Notice>Status unsupported: no health or stats resources declared.</Notice> : <section aria-label=\"Admin Status\" className=\"flex min-w-0 flex-col gap-4\">\n {manifest.health.map(resource => {\n const state = observations.health?.[resource.id]\n return state ? <HealthSummary key={resource.id} {...state} label={resource.label} /> : <ObservationStatus key={resource.id} label={resource.label} observation={observation(resource.stale_after_ms)} />\n })}\n {manifest.stats.length ? <StatCollection label=\"Stats\" rows={manifest.stats.map(resource => {\n const state = observations.stats?.[resource.id]\n return { id: resource.id, label: resource.label, unit: resource.unit, kind: resource.kind, value: state?.value ?? null, observation: state?.observation ?? observation(resource.stale_after_ms) }\n })} /> : null}\n </section>\n else if (selection.page === 'diagnostics') content = !manifest.series.length && !manifest.diagnostics.length ? <Notice>Diagnostics unsupported: no series or diagnostic resources declared.</Notice> : <section aria-label=\"Admin Diagnostics\" className=\"flex min-w-0 flex-col gap-4\">\n {manifest.series.map(resource => {\n const state = observations.series?.[resource.id]\n return <section key={resource.id} className=\"min-w-0\">{!props.renderSeries ? <Callout tone=\"warning\" title=\"Series renderer unavailable\">{resource.label}: the host has not supplied a series renderer.</Callout> : !state ? <ObservationStatus label={resource.label} observation={observation(resource.stale_after_ms)} /> : props.renderSeries({ ...state, label: resource.label, unit: resource.unit, kind: resource.kind, bounds: { maxPoints: resource.max_points, maxWindowSeconds: resource.max_window_seconds } })}</section>\n })}\n {manifest.diagnostics.map(resource => {\n const state = observations.diagnostics?.[resource.id]\n return state ? <DiagnosticPanel key={resource.id} {...state} label={resource.label} schema={resource.schema} /> : <ObservationStatus key={resource.id} label={resource.label} observation={observation(resource.stale_after_ms)} />\n })}\n </section>\n else content = <Callout tone=\"warning\" title=\"Admin page unavailable\">The host selected an unsupported page.</Callout>\n return <div key={`${props.contextKey}:${manifest.app.id}:${manifest.revision}`} className=\"mx-auto flex w-full min-w-0 max-w-5xl flex-col gap-4 p-4 text-control text-fg\">\n {discovery.phase !== 'ready' ? <Callout tone=\"warning\" title=\"Previous declarations retained\">{discovery.error ?? 'Refreshing admin declarations.'} Content uses the last declaration; writes are suspended until discovery succeeds.</Callout> : null}\n {content}\n </div>\n}\n\n/** Standalone viewport composition. Use AdminContent inside an existing AppShell instead. */\nexport function AdminShell(props: AdminContentProps) {\n const manifest = available(props)\n return <AppShell nav={<AdminNavigation {...props} />}>\n <ListPageLayout header={<PageHeader title={`${manifest?.app.label ?? 'Admin'} / ${labels[props.selection.page] ?? 'Unavailable'}`} />}>\n <AdminContent {...props} />\n </ListPageLayout>\n </AppShell>\n}\n"],"names":["labels","icons","LayoutDashboard","Settings","Activity","SquareTerminal","available","props","discovery","contextKey","adminManifestProblem","Destination","destination","children","jsx","Button","Notice","GroupLinks","manifest","group","AdminNavigation","pages","adminPages","NavRail","page","Icon","SettingsPage","selection","settings","actions","setup","contextReady","readReady","read","selected","states","_a","change","id","draft","normal","presentedGroup","presentedStates","state","settingsMetadataProblem","key","value","jsxs","Fragment","Callout","_b","_c","_d","_e","SettingsProvenanceRenderer","SettingsWizard","plan","AdminContent","observations","nowMs","problem","observation","staleAfterMs","content","resource","HealthSummary","ObservationStatus","StatCollection","DiagnosticPanel","AdminShell","AppShell","ListPageLayout","PageHeader"],"mappings":"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"}
|
package/dist/charts.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"charts.js","sources":["../src/charts.tsx"],"sourcesContent":["import { SampleSeriesView, type SampleSeriesViewProps } from '@hollis-labs/kit-observe/charts'\n\n/** Supply to AdminContent/AdminShell renderSeries. The root never imports this module. */\nexport function renderAdminSeries(props: SampleSeriesViewProps) {\n return <SampleSeriesView {...props} />\n}\n"],"names":["renderAdminSeries","props","jsx","SampleSeriesView"],"mappings":";;AAGO,SAASA,EAAkBC,GAA8B;AAC9D,SAAO,gBAAAC,EAACC,GAAA,EAAkB,GAAGF,EAAA,CAAO;AACtC;"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AdminContent as m, AdminNavigation as e, AdminShell as o } from "./admin.js";
|
|
2
|
+
import { adminManifestProblem as t, adminPages as d } from "./model.js";
|
|
3
|
+
export {
|
|
4
|
+
m as AdminContent,
|
|
5
|
+
e as AdminNavigation,
|
|
6
|
+
o as AdminShell,
|
|
7
|
+
t as adminManifestProblem,
|
|
8
|
+
d as adminPages
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const n = (t) => typeof t == "object" && t !== null && !Array.isArray(t), r = (t) => typeof t == "string" && t.trim().length > 0, d = { settings: "settings", health: "status", stats: "status", series: "diagnostics", diagnostics: "diagnostics" };
|
|
2
|
+
function c(t) {
|
|
3
|
+
if (!n(t)) return "Malformed admin manifest.";
|
|
4
|
+
if (t.contract_version !== 1) return "Unsupported admin contract version.";
|
|
5
|
+
if (!n(t.app) || !r(t.app.id) || !r(t.app.label) || !r(t.revision)) return "Malformed admin identity or declaration revision.";
|
|
6
|
+
for (const [e, i] of Object.entries(d)) {
|
|
7
|
+
const o = t[e];
|
|
8
|
+
if (!Array.isArray(o)) return `Malformed admin manifest: missing ${e} array.`;
|
|
9
|
+
const a = /* @__PURE__ */ new Set();
|
|
10
|
+
for (const s of o) {
|
|
11
|
+
if (!n(s) || !r(s.id) || !r(s.label) || a.has(s.id)) return `Malformed ${e} resource identity.`;
|
|
12
|
+
if (a.add(s.id), s.section !== i) return `Contradictory ${e} section: resources must declare ${i}.`;
|
|
13
|
+
if (e !== "settings" && (typeof s.stale_after_ms != "number" || !Number.isFinite(s.stale_after_ms) || s.stale_after_ms <= 0)) return `Unsupported ${e} observation age declaration.`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function f(t) {
|
|
18
|
+
return ["dashboard", ...t.settings.length ? ["settings"] : [], ...t.health.length || t.stats.length ? ["status"] : [], ...t.series.length || t.diagnostics.length ? ["diagnostics"] : []];
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
c as adminManifestProblem,
|
|
22
|
+
f as adminPages
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sources":["../src/model.ts"],"sourcesContent":["import type { AdminManifest, AdminPage } from './types'\n\nconst record = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v)\nconst text = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0\nconst sections = { settings: 'settings', health: 'status', stats: 'status', series: 'diagnostics', diagnostics: 'diagnostics' } as const\n\n/** Presentation fence, not a wire validator. Never relocate a contradictory resource. */\nexport function adminManifestProblem(value: unknown): string | undefined {\n if (!record(value)) return 'Malformed admin manifest.'\n if (value.contract_version !== 1) return 'Unsupported admin contract version.'\n if (!record(value.app) || !text(value.app.id) || !text(value.app.label) || !text(value.revision)) return 'Malformed admin identity or declaration revision.'\n for (const [kind, section] of Object.entries(sections)) {\n const resources = value[kind]\n if (!Array.isArray(resources)) return `Malformed admin manifest: missing ${kind} array.`\n const ids = new Set<string>()\n for (const resource of resources) {\n if (!record(resource) || !text(resource.id) || !text(resource.label) || ids.has(resource.id)) return `Malformed ${kind} resource identity.`\n ids.add(resource.id)\n if (resource.section !== section) return `Contradictory ${kind} section: resources must declare ${section}.`\n if (kind !== 'settings' && (typeof resource.stale_after_ms !== 'number' || !Number.isFinite(resource.stale_after_ms) || resource.stale_after_ms <= 0)) return `Unsupported ${kind} observation age declaration.`\n }\n }\n}\nexport function adminPages(manifest: AdminManifest): readonly AdminPage[] {\n return ['dashboard', ...(manifest.settings.length ? ['settings' as const] : []), ...(manifest.health.length || manifest.stats.length ? ['status' as const] : []), ...(manifest.series.length || manifest.diagnostics.length ? ['diagnostics' as const] : [])]\n}\n"],"names":["record","v","text","sections","adminManifestProblem","value","kind","section","resources","ids","resource","adminPages","manifest"],"mappings":"AAEA,MAAMA,IAAS,CAACC,MAA6C,OAAOA,KAAM,YAAYA,MAAM,QAAQ,CAAC,MAAM,QAAQA,CAAC,GAC9GC,IAAO,CAACD,MAA4B,OAAOA,KAAM,YAAYA,EAAE,OAAO,SAAS,GAC/EE,IAAW,EAAE,UAAU,YAAY,QAAQ,UAAU,OAAO,UAAU,QAAQ,eAAe,aAAa,cAAA;AAGzG,SAASC,EAAqBC,GAAoC;AACvE,MAAI,CAACL,EAAOK,CAAK,EAAG,QAAO;AAC3B,MAAIA,EAAM,qBAAqB,EAAG,QAAO;AACzC,MAAI,CAACL,EAAOK,EAAM,GAAG,KAAK,CAACH,EAAKG,EAAM,IAAI,EAAE,KAAK,CAACH,EAAKG,EAAM,IAAI,KAAK,KAAK,CAACH,EAAKG,EAAM,QAAQ,EAAG,QAAO;AACzG,aAAW,CAACC,GAAMC,CAAO,KAAK,OAAO,QAAQJ,CAAQ,GAAG;AACtD,UAAMK,IAAYH,EAAMC,CAAI;AAC5B,QAAI,CAAC,MAAM,QAAQE,CAAS,EAAG,QAAO,qCAAqCF,CAAI;AAC/E,UAAMG,wBAAU,IAAA;AAChB,eAAWC,KAAYF,GAAW;AAChC,UAAI,CAACR,EAAOU,CAAQ,KAAK,CAACR,EAAKQ,EAAS,EAAE,KAAK,CAACR,EAAKQ,EAAS,KAAK,KAAKD,EAAI,IAAIC,EAAS,EAAE,EAAG,QAAO,aAAaJ,CAAI;AAEtH,UADAG,EAAI,IAAIC,EAAS,EAAE,GACfA,EAAS,YAAYH,UAAgB,iBAAiBD,CAAI,oCAAoCC,CAAO;AACzG,UAAID,MAAS,eAAe,OAAOI,EAAS,kBAAmB,YAAY,CAAC,OAAO,SAASA,EAAS,cAAc,KAAKA,EAAS,kBAAkB,GAAI,QAAO,eAAeJ,CAAI;AAAA,IACnL;AAAA,EACF;AACF;AACO,SAASK,EAAWC,GAA+C;AACxE,SAAO,CAAC,aAAa,GAAIA,EAAS,SAAS,SAAS,CAAC,UAAmB,IAAI,CAAA,GAAK,GAAIA,EAAS,OAAO,UAAUA,EAAS,MAAM,SAAS,CAAC,QAAiB,IAAI,IAAK,GAAIA,EAAS,OAAO,UAAUA,EAAS,YAAY,SAAS,CAAC,aAAsB,IAAI,CAAA,CAAG;AAC9P;"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { AdminContentProps } from './types';
|
|
2
|
+
/** Navigation is only a projection of declared sections; the host owns routing/history. */
|
|
3
|
+
export declare function AdminNavigation(props: AdminContentProps): import("react").JSX.Element;
|
|
4
|
+
/** Inner region for an existing host shell; no viewport wrapper, effects or local data store. */
|
|
5
|
+
export declare function AdminContent(props: AdminContentProps): import("react").JSX.Element;
|
|
6
|
+
/** Standalone viewport composition. Use AdminContent inside an existing AppShell instead. */
|
|
7
|
+
export declare function AdminShell(props: AdminContentProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { AdminContent, AdminNavigation, AdminShell } from './admin';
|
|
2
|
+
export { adminManifestProblem, adminPages } from './model';
|
|
3
|
+
export type { AdminContentProps, AdminDeclaration, AdminObservationDeclaration, AdminSettingsDeclaration, AdminStatDeclaration, AdminSeriesDeclaration, AdminDiagnosticDeclaration, AdminManifest, AdminPage, AdminTarget, AdminDestination, AdminSelection, AdminDiscovery, AdminSettingsRead, AdminObservations } from './types';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { AdminManifest, AdminPage } from './types';
|
|
2
|
+
/** Presentation fence, not a wire validator. Never relocate a contradictory resource. */
|
|
3
|
+
export declare function adminManifestProblem(value: unknown): string | undefined;
|
|
4
|
+
export declare function adminPages(manifest: AdminManifest): readonly AdminPage[];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { SettingsGroup, SettingsProvenanceRendererProps, SettingsProvenanceState, SettingsWizardProps } from '@hollis-labs/kit-settings';
|
|
3
|
+
import { HealthSummaryProps, StatObservation, DiagnosticPanelProps, ObservationUnit, ObservationKind } from '@hollis-labs/kit-observe';
|
|
4
|
+
import { SampleSeriesViewProps } from '@hollis-labs/kit-observe/charts';
|
|
5
|
+
/** View projections only. The host validates wire schemas, endpoints, auth and bounds. */
|
|
6
|
+
export interface AdminDeclaration {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly label: string;
|
|
9
|
+
readonly section: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AdminObservationDeclaration extends AdminDeclaration {
|
|
12
|
+
readonly stale_after_ms: number;
|
|
13
|
+
}
|
|
14
|
+
export interface AdminSettingsDeclaration extends SettingsGroup {
|
|
15
|
+
readonly section: string;
|
|
16
|
+
readonly scope?: {
|
|
17
|
+
readonly kind: string;
|
|
18
|
+
readonly id?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface AdminStatDeclaration extends AdminObservationDeclaration {
|
|
22
|
+
readonly unit: ObservationUnit;
|
|
23
|
+
readonly kind: ObservationKind;
|
|
24
|
+
}
|
|
25
|
+
export interface AdminSeriesDeclaration extends AdminStatDeclaration {
|
|
26
|
+
readonly max_points: number;
|
|
27
|
+
readonly max_window_seconds: number;
|
|
28
|
+
}
|
|
29
|
+
export interface AdminDiagnosticDeclaration extends AdminObservationDeclaration {
|
|
30
|
+
readonly schema: DiagnosticPanelProps['schema'];
|
|
31
|
+
}
|
|
32
|
+
export interface AdminManifest {
|
|
33
|
+
readonly contract_version: number;
|
|
34
|
+
readonly app: {
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly label: string;
|
|
37
|
+
};
|
|
38
|
+
readonly revision: string;
|
|
39
|
+
readonly settings: readonly AdminSettingsDeclaration[];
|
|
40
|
+
readonly health: readonly AdminObservationDeclaration[];
|
|
41
|
+
readonly stats: readonly AdminStatDeclaration[];
|
|
42
|
+
readonly series: readonly AdminSeriesDeclaration[];
|
|
43
|
+
readonly diagnostics: readonly AdminDiagnosticDeclaration[];
|
|
44
|
+
}
|
|
45
|
+
export type AdminPage = 'dashboard' | 'settings' | 'status' | 'diagnostics';
|
|
46
|
+
export interface AdminTarget {
|
|
47
|
+
readonly page: AdminPage;
|
|
48
|
+
readonly groupId?: string;
|
|
49
|
+
}
|
|
50
|
+
export type AdminDestination = {
|
|
51
|
+
readonly href: string;
|
|
52
|
+
readonly onSelect?: never;
|
|
53
|
+
} | {
|
|
54
|
+
readonly onSelect: () => void;
|
|
55
|
+
readonly href?: never;
|
|
56
|
+
};
|
|
57
|
+
export interface AdminSelection extends AdminTarget {
|
|
58
|
+
readonly mode?: 'edit' | 'setup';
|
|
59
|
+
}
|
|
60
|
+
export interface AdminDiscovery {
|
|
61
|
+
readonly phase: 'loading' | 'ready' | 'error';
|
|
62
|
+
/** Must match the active authorized host context; never retain a prior principal. */
|
|
63
|
+
readonly contextKey: string;
|
|
64
|
+
readonly manifest?: AdminManifest;
|
|
65
|
+
readonly error?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface AdminSettingsRead {
|
|
68
|
+
readonly phase: 'loading' | 'ready' | 'error';
|
|
69
|
+
readonly state?: SettingsProvenanceState;
|
|
70
|
+
readonly error?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface AdminObservations {
|
|
73
|
+
readonly health?: Readonly<Record<string, Omit<HealthSummaryProps, 'label'> | undefined>>;
|
|
74
|
+
readonly stats?: Readonly<Record<string, Pick<StatObservation, 'value' | 'observation'> | undefined>>;
|
|
75
|
+
readonly series?: Readonly<Record<string, Omit<SampleSeriesViewProps, 'label' | 'unit' | 'kind' | 'bounds'> | undefined>>;
|
|
76
|
+
readonly diagnostics?: Readonly<Record<string, Omit<DiagnosticPanelProps, 'label' | 'schema'> | undefined>>;
|
|
77
|
+
}
|
|
78
|
+
export interface AdminContentProps {
|
|
79
|
+
readonly contextKey: string;
|
|
80
|
+
readonly discovery: AdminDiscovery;
|
|
81
|
+
readonly selection: AdminSelection;
|
|
82
|
+
/** Host maps canonical targets to its routes or controlled navigation callbacks. */
|
|
83
|
+
readonly destination: (target: AdminTarget) => AdminDestination;
|
|
84
|
+
readonly settings?: Readonly<Record<string, AdminSettingsRead | undefined>>;
|
|
85
|
+
readonly settingsActions?: Pick<SettingsProvenanceRendererProps, 'onDraftChange' | 'onSave' | 'onReset' | 'onValidate' | 'onApply'>;
|
|
86
|
+
readonly setup?: Omit<SettingsWizardProps, 'contractVersion' | 'groups' | 'states' | 'onDraftChange'>;
|
|
87
|
+
/** Context/metadata beside the Wizard; never a second editable form. */
|
|
88
|
+
readonly setupContext?: ReactNode;
|
|
89
|
+
readonly observations?: AdminObservations;
|
|
90
|
+
readonly nowMs: number;
|
|
91
|
+
/** Explicit opt-in: root imports no chart implementation. See /charts. */
|
|
92
|
+
readonly renderSeries?: (props: SampleSeriesViewProps) => ReactNode;
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,95 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hollis-labs/kit-admin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public",
|
|
6
|
+
"registry": "https://registry.npmjs.org/"
|
|
7
|
+
},
|
|
8
|
+
"description": "Controlled manifest-driven admin navigation and page composition; host-owned data, routes and commands.",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"react",
|
|
11
|
+
"admin",
|
|
12
|
+
"design-system"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/hollis-labs/design-kit.git",
|
|
18
|
+
"directory": "packages/kit-admin"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/hollis-labs/design-kit/tree/main/packages/kit-admin#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/hollis-labs/design-kit/issues"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": [
|
|
26
|
+
"**/*.css"
|
|
27
|
+
],
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"src/styles",
|
|
31
|
+
"CHANGELOG.md"
|
|
32
|
+
],
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/src/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./source.css": "./src/styles/source.css",
|
|
39
|
+
"./charts": {
|
|
40
|
+
"types": "./dist/src/charts.d.ts",
|
|
41
|
+
"import": "./dist/charts.js"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"main": "./dist/index.js",
|
|
45
|
+
"module": "./dist/index.js",
|
|
46
|
+
"types": "./dist/src/index.d.ts",
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.build.json && vite build",
|
|
49
|
+
"prepack": "npm run build",
|
|
50
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
51
|
+
"lint": "eslint .",
|
|
52
|
+
"test": "vitest",
|
|
53
|
+
"test:run": "vitest run",
|
|
54
|
+
"demo": "vite --config demo/vite.config.ts",
|
|
55
|
+
"demo:build": "vite build --config demo/vite.config.ts",
|
|
56
|
+
"prebuild": "node scripts/build-dependencies.mjs"
|
|
57
|
+
},
|
|
58
|
+
"peerDependencies": {
|
|
59
|
+
"tailwindcss": "^4.0.0",
|
|
60
|
+
"@base-ui/react": "^1.3.0",
|
|
61
|
+
"lucide-react": "^1.7.0",
|
|
62
|
+
"react": "^19.0.0",
|
|
63
|
+
"react-dom": "^19.0.0"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@hollis-labs/design-components": "^0.4.0",
|
|
67
|
+
"@hollis-labs/design-tokens": "^0.4.0",
|
|
68
|
+
"@hollis-labs/kit-dashboard": "^0.4.0",
|
|
69
|
+
"@hollis-labs/kit-settings": "^0.2.0",
|
|
70
|
+
"@hollis-labs/kit-observe": "^0.1.1"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@base-ui/react": "^1.3.0",
|
|
74
|
+
"@eslint/js": "^9.39.4",
|
|
75
|
+
"@testing-library/react": "^16.3.2",
|
|
76
|
+
"@types/node": "^24.12.2",
|
|
77
|
+
"@types/react": "^19.2.14",
|
|
78
|
+
"@types/react-dom": "^19.2.3",
|
|
79
|
+
"@vitejs/plugin-react": "4.7.0",
|
|
80
|
+
"eslint": "^9.39.4",
|
|
81
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
82
|
+
"eslint-plugin-react-refresh": "^0.5.2",
|
|
83
|
+
"globals": "^17.4.0",
|
|
84
|
+
"jsdom": "^25.0.1",
|
|
85
|
+
"lucide-react": "^1.7.0",
|
|
86
|
+
"react": "^19.2.4",
|
|
87
|
+
"react-dom": "^19.2.4",
|
|
88
|
+
"typescript": "~6.0.2",
|
|
89
|
+
"vite": "5.4.21",
|
|
90
|
+
"vite-plugin-dts": "^4.5.4",
|
|
91
|
+
"vitest": "^2.1.9",
|
|
92
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
93
|
+
"typescript-eslint": "^8.58.0"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@source "../../dist";
|