@astrale-os/adapter-cloudflare 0.1.10 → 0.2.1
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/package.json +2 -2
- package/template/.agents/skills/astrale-cli/SKILL.md +1 -1
- package/template/.agents/skills/astrale-domain/SKILL.md +2 -1
- package/template/client/README.md +25 -23
- package/template/client/__tests__/app.test.tsx +44 -88
- package/template/client/__tests__/harness.ts +11 -16
- package/template/client/__tests__/kernel.test.ts +9 -35
- package/template/client/__tests__/seam.test.tsx +22 -18
- package/template/client/src/app.tsx +11 -17
- package/template/client/src/shell/use-capability.ts +1 -3
- package/template/client/src/shell/use-node.ts +2 -2
- package/template/client/src/shell/view-router.tsx +3 -4
- package/template/client/src/status/components/StatusCard.tsx +50 -0
- package/template/client/src/status/components/index.ts +1 -0
- package/template/client/src/status/hooks/index.ts +3 -0
- package/template/client/src/{monitor → status}/hooks/useCheck.mutation.ts +2 -2
- package/template/client/src/{monitor/hooks/useMonitor.query.ts → status/hooks/useCheckable.query.ts} +8 -8
- package/template/client/src/status/index.ts +7 -0
- package/template/client/src/status/status.api.ts +12 -0
- package/template/client/src/status/status.mappers.ts +19 -0
- package/template/client/src/status/status.types.ts +11 -0
- package/template/client/src/styles.css +5 -0
- package/template/client/src/ui/StatusBadge.tsx +31 -0
- package/template/client/src/ui/index.ts +6 -2
- package/template/client/src/views/status.tsx +28 -0
- package/template/client/vite.config.ts +2 -3
- package/template/client/vitest.config.ts +1 -2
- package/template/core/monitor/health.ts +19 -4
- package/template/core/monitor/keys.ts +14 -2
- package/template/core/monitor/node.ts +27 -21
- package/template/deps.ts +2 -1
- package/template/integrations/prober/http.ts +4 -15
- package/template/integrations/prober/mock.ts +1 -5
- package/template/integrations/prober/port.ts +0 -2
- package/template/integrations/prober/registry.ts +6 -7
- package/template/package.json +2 -2
- package/template/pnpm-workspace.yaml +2 -0
- package/template/runtime/index.ts +51 -39
- package/template/runtime/monitor/check.ts +9 -9
- package/template/runtime/monitor/index.ts +4 -7
- package/template/runtime/monitor/seed.ts +67 -46
- package/template/runtime/monitor/watch.ts +6 -12
- package/template/runtime/{monitor/shared.ts → shared.ts} +7 -3
- package/template/runtime/status-page/add.ts +21 -0
- package/template/runtime/status-page/check.ts +50 -0
- package/template/runtime/status-page/create.ts +24 -0
- package/template/runtime/status-page/index.ts +8 -0
- package/template/schema/index.ts +5 -5
- package/template/schema/monitor.ts +62 -48
- package/template/views/index.ts +4 -5
- package/template/views/status-page.ts +16 -0
- package/template/client/src/monitor/components/MonitorCard.tsx +0 -50
- package/template/client/src/monitor/components/index.ts +0 -1
- package/template/client/src/monitor/hooks/index.ts +0 -3
- package/template/client/src/monitor/index.ts +0 -6
- package/template/client/src/monitor/monitor.api.ts +0 -11
- package/template/client/src/monitor/monitor.mappers.ts +0 -38
- package/template/client/src/monitor/monitor.types.ts +0 -23
- package/template/client/src/monitor/ui/MonitorDetails.UI.tsx +0 -38
- package/template/client/src/monitor/ui/StatusBadge.UI.tsx +0 -14
- package/template/client/src/monitor/ui/index.ts +0 -8
- package/template/client/src/views/monitor.tsx +0 -30
- package/template/pnpm-lock.yaml +0 -2766
- package/template/runtime/monitor/dependsOn.ts +0 -16
- package/template/views/monitor.ts +0 -22
|
@@ -1,57 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Monitoring context — the domain's single bounded slice: a `Checkable` contract
|
|
3
|
+
* implemented by two classes, plus the edge that binds them.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
12
|
-
* -
|
|
13
|
-
* `watch` and adding instance methods `check`
|
|
14
|
-
* (probe + record live status) and `dependsOn`
|
|
15
|
-
* (links this Monitor to one it relies on).
|
|
16
|
-
* - Edge `depends_on` Monitor → Monitor. A dependency graph of checks,
|
|
17
|
-
* materialized by `dependsOn` (and by `seed`).
|
|
5
|
+
* - Interface `Checkable` one `abstract` method, `check()`. Abstract → each
|
|
6
|
+
* implementing class brings its OWN body, and the
|
|
7
|
+
* kernel dispatches by the node's class.
|
|
8
|
+
* - Class `Monitor` a single HTTP check. `check()` probes `url`;
|
|
9
|
+
* `watch` creates one; `seed` lays down the demo set.
|
|
10
|
+
* - Class `StatusPage` a roll-up. `check()` re-checks the monitors it
|
|
11
|
+
* `watches` and aggregates; `create` makes a page;
|
|
12
|
+
* `add` watches a monitor.
|
|
13
|
+
* - Edge `watches` StatusPage → Monitor, with a `critical` flag.
|
|
18
14
|
*/
|
|
19
|
-
import { edgeClass,
|
|
15
|
+
import { edgeClass, nodeClass, nodeInterface } from '@astrale-os/kernel-core'
|
|
20
16
|
import { fn } from '@astrale-os/kernel-dsl'
|
|
21
17
|
import { z } from 'zod'
|
|
22
18
|
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
|
|
26
|
-
* not round-trip over the worker wire).
|
|
27
|
-
*/
|
|
28
|
-
export const MonitorRef = z.object({ id: z.string(), path: z.string() })
|
|
19
|
+
/** Thin ref to a created node — what the static factories return (a full Node
|
|
20
|
+
* value does not round-trip over the worker wire). */
|
|
21
|
+
export const NodeRef = z.object({ id: z.string(), path: z.string() })
|
|
29
22
|
|
|
30
|
-
/** What
|
|
31
|
-
export const
|
|
32
|
-
status: z.string(),
|
|
33
|
-
statusCode: z.number().int(),
|
|
34
|
-
latencyMs: z.number().int(),
|
|
35
|
-
})
|
|
23
|
+
/** What `check()` reports — the subject's current health verdict. */
|
|
24
|
+
export const CheckResult = z.object({ status: z.string() })
|
|
36
25
|
|
|
37
|
-
export const
|
|
26
|
+
export const Checkable = nodeInterface({
|
|
38
27
|
methods: {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
returns: MonitorRef,
|
|
43
|
-
}),
|
|
28
|
+
// `abstract` → no shared body; every implementer supplies its own `check`,
|
|
29
|
+
// wired per-class in `runtime/`. `@<node>::check` dispatches by the node's class.
|
|
30
|
+
check: fn({ inheritance: 'abstract', returns: CheckResult }),
|
|
44
31
|
},
|
|
45
32
|
})
|
|
46
33
|
|
|
47
34
|
export const Monitor = nodeClass({
|
|
48
|
-
implements: [
|
|
35
|
+
implements: [Checkable],
|
|
49
36
|
props: {
|
|
50
37
|
/** The target URL this monitor probes. */
|
|
51
38
|
url: z.string(),
|
|
52
39
|
// Live status, written by `check`. PLAIN STRING, not `z.enum()`: `::update`
|
|
53
|
-
//
|
|
54
|
-
// Values: 'up' | 'down' | 'unknown' (the initial value before the first check).
|
|
40
|
+
// silently drops `z.enum()` props. Values: 'up' | 'down' | 'unknown'.
|
|
55
41
|
status: z.string().optional(),
|
|
56
42
|
/** Last observed HTTP status code (0 = host unreachable). */
|
|
57
43
|
statusCode: z.number().int().optional(),
|
|
@@ -61,20 +47,48 @@ export const Monitor = nodeClass({
|
|
|
61
47
|
lastCheckedAt: z.string().optional(),
|
|
62
48
|
},
|
|
63
49
|
methods: {
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
check
|
|
67
|
-
//
|
|
68
|
-
|
|
50
|
+
// Implements `Checkable.check` for a single endpoint (probe `url`). Redeclared
|
|
51
|
+
// here — a concrete class must declare each abstract method it implements, so
|
|
52
|
+
// the compiler materializes Monitor's OWN `check` node for per-class dispatch.
|
|
53
|
+
// The body lives in `runtime/monitor/check.ts`.
|
|
54
|
+
check: fn({ returns: CheckResult }),
|
|
55
|
+
/** Create a Monitor under `/monitors`. */
|
|
56
|
+
watch: fn({
|
|
57
|
+
static: true,
|
|
58
|
+
params: { url: z.string(), name: z.string().optional() },
|
|
59
|
+
returns: NodeRef,
|
|
60
|
+
}),
|
|
69
61
|
// Post-install bootstrap (wired as `postInstall` in domain.ts). Static: the
|
|
70
|
-
// kernel calls it ONCE after install, as __SYSTEM__
|
|
71
|
-
// stay idempotent — a re-install runs it again.
|
|
62
|
+
// kernel calls it ONCE after install, as __SYSTEM__. Must stay idempotent.
|
|
72
63
|
seed: fn({ static: true, returns: z.object({ seeded: z.number().int() }) }),
|
|
73
64
|
},
|
|
74
65
|
})
|
|
75
66
|
|
|
76
|
-
export const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
export const StatusPage = nodeClass({
|
|
68
|
+
implements: [Checkable],
|
|
69
|
+
props: {
|
|
70
|
+
/** Rolled-up status, written by `check`. 'up' | 'degraded' | 'down' | 'unknown'. */
|
|
71
|
+
status: z.string().optional(),
|
|
72
|
+
},
|
|
73
|
+
methods: {
|
|
74
|
+
// Implements `Checkable.check` for a page (roll up the watched monitors).
|
|
75
|
+
// Redeclared so StatusPage materializes its OWN `check` node. Body in
|
|
76
|
+
// `runtime/status-page/check.ts`.
|
|
77
|
+
check: fn({ returns: CheckResult }),
|
|
78
|
+
/** Create a StatusPage under `/status-pages`. */
|
|
79
|
+
create: fn({ static: true, params: { name: z.string() }, returns: NodeRef }),
|
|
80
|
+
/** Watch a monitor (a `watches` edge); `critical` decides the roll-up weight. */
|
|
81
|
+
add: fn({
|
|
82
|
+
params: { monitor: z.string(), critical: z.boolean().optional() },
|
|
83
|
+
returns: z.object({ watched: z.string() }),
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
export const watches = edgeClass(
|
|
89
|
+
{ as: 'page', types: [StatusPage] },
|
|
90
|
+
{ as: 'monitor', types: [Monitor] },
|
|
91
|
+
// `critical` drives the roll-up: a critical monitor down ⇒ page `down`; a
|
|
92
|
+
// non-critical one down ⇒ `degraded`.
|
|
93
|
+
{ props: { critical: z.boolean() } },
|
|
80
94
|
)
|
package/template/views/index.ts
CHANGED
|
@@ -4,14 +4,13 @@
|
|
|
4
4
|
* binding the SDK stamps with the worker's live serving URL when it builds the
|
|
5
5
|
* install bundle.
|
|
6
6
|
*
|
|
7
|
-
* `welcome` is a worker-rendered inline-HTML view (no SPA). `ui-
|
|
8
|
-
*
|
|
9
|
-
* mount path (`/ui/monitor` vs `/ui/monitor-badge`) to pick which view to render.
|
|
7
|
+
* `welcome` is a worker-rendered inline-HTML view (no SPA). `ui-status-page` is
|
|
8
|
+
* the `client/` SPA (mounted at `/ui/status-page`, offered for any StatusPage).
|
|
10
9
|
*/
|
|
11
|
-
import {
|
|
10
|
+
import { statusPage } from './status-page'
|
|
12
11
|
import { welcome } from './welcome'
|
|
13
12
|
|
|
14
13
|
export const views = {
|
|
15
14
|
welcome,
|
|
16
|
-
'ui-
|
|
15
|
+
'ui-status-page': statusPage,
|
|
17
16
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ui-status-page` — the domain's SPA view, mounted at `/ui/status-page` and
|
|
3
|
+
* served from the `client/` bundle. `viewFor: selfOf(StatusPage)` attaches a
|
|
4
|
+
* `view_for` edge to the `StatusPage` class meta-node, so the GUI offers this
|
|
5
|
+
* view for any StatusPage instance.
|
|
6
|
+
*/
|
|
7
|
+
import { selfOf } from '@astrale-os/kernel-dsl'
|
|
8
|
+
import { defineView } from '@astrale-os/sdk'
|
|
9
|
+
|
|
10
|
+
import { StatusPage } from '../schema/monitor'
|
|
11
|
+
|
|
12
|
+
export const statusPage = defineView({
|
|
13
|
+
auth: 'public',
|
|
14
|
+
mount: '/ui/status-page',
|
|
15
|
+
viewFor: selfOf(StatusPage),
|
|
16
|
+
})
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MonitorCard — the Monitor feature's one container. It owns the data/logic:
|
|
3
|
-
* loads the node (`useMonitor`), wires the `check` write (`useCheck`), and
|
|
4
|
-
* composes the presentation. Handles the query's loading/error/ok states; on
|
|
5
|
-
* `ok` it renders a `Panel` whose body is the check-error banner (if any) plus
|
|
6
|
-
* the pure `MonitorDetails` view, with a "Check now" button that runs the probe
|
|
7
|
-
* then reloads the node so the fresh values render.
|
|
8
|
-
*
|
|
9
|
-
* Thin by design: the record's layout lives in `monitor/ui` (`MonitorDetails`);
|
|
10
|
-
* the generic surfaces come from the `@/ui` design system.
|
|
11
|
-
*/
|
|
12
|
-
import type { KernelClient } from '@/shell'
|
|
13
|
-
|
|
14
|
-
import { ErrorBanner, Panel, Spinner } from '@/ui'
|
|
15
|
-
|
|
16
|
-
import { useCheck, useMonitor } from '../hooks'
|
|
17
|
-
import { MonitorDetails } from '../ui'
|
|
18
|
-
|
|
19
|
-
export function MonitorCard({ session, nodeId }: { session: KernelClient; nodeId: string }) {
|
|
20
|
-
const monitor = useMonitor(session, nodeId)
|
|
21
|
-
const probe = useCheck(session, nodeId)
|
|
22
|
-
|
|
23
|
-
async function checkNow() {
|
|
24
|
-
if (await probe.run()) monitor.reload()
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (monitor.state === 'idle' || monitor.state === 'loading') {
|
|
28
|
-
return <Spinner label="Loading the Monitor…" />
|
|
29
|
-
}
|
|
30
|
-
if (monitor.state === 'error') {
|
|
31
|
-
return <ErrorBanner>Failed to load the Monitor: {monitor.message}</ErrorBanner>
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const record = monitor.record!
|
|
35
|
-
const checking = probe.phase === 'running'
|
|
36
|
-
const checkButton = (
|
|
37
|
-
<button type="button" className="check-btn" onClick={checkNow} disabled={checking}>
|
|
38
|
-
{checking ? 'Checking…' : 'Check now'}
|
|
39
|
-
</button>
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<Panel title={record.name} actions={checkButton}>
|
|
44
|
-
{probe.phase === 'failed' && probe.error && (
|
|
45
|
-
<ErrorBanner>Check failed: {probe.error}</ErrorBanner>
|
|
46
|
-
)}
|
|
47
|
-
<MonitorDetails record={record} />
|
|
48
|
-
</Panel>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { MonitorCard } from './MonitorCard'
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/** The Monitor feature — its api, types, mappers, hooks and components. */
|
|
2
|
-
export * from './components'
|
|
3
|
-
export * from './hooks'
|
|
4
|
-
export { check } from './monitor.api'
|
|
5
|
-
export { monitorFromNode, normalizeStatus } from './monitor.mappers'
|
|
6
|
-
export type { MonitorRecord } from './monitor.types'
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/** Raw kernel calls for the Monitor feature — node instance methods. */
|
|
2
|
-
import { invokeNode, type KernelClient } from '@/shell'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Run the Monitor's `check` instance method (`@<id>::check`). Probes the target
|
|
6
|
-
* URL server-side and updates the node's `status`/`statusCode`/`latencyMs`/
|
|
7
|
-
* `lastCheckedAt` props; the caller reloads the node to render the fresh values.
|
|
8
|
-
*/
|
|
9
|
-
export function check(session: KernelClient, nodeId: string): Promise<unknown> {
|
|
10
|
-
return invokeNode(session, nodeId, 'check', {})
|
|
11
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/** Node → typed record transforms for the Monitor feature. */
|
|
2
|
-
import { type KernelNode, PROP, readProp, readPropBySuffix } from '@/shell'
|
|
3
|
-
|
|
4
|
-
import type { MonitorRecord, MonitorStatus } from './monitor.types'
|
|
5
|
-
|
|
6
|
-
const lastSegment = (path: string): string => path.split('/').filter(Boolean).pop() ?? path
|
|
7
|
-
|
|
8
|
-
/** Coerce a string prop to a finite number, or `undefined`. */
|
|
9
|
-
function readNumberBySuffix(props: Record<string, unknown>, suffix: string): number | undefined {
|
|
10
|
-
const raw = readPropBySuffix(props, suffix)
|
|
11
|
-
if (raw === undefined) return undefined
|
|
12
|
-
const n = Number(raw)
|
|
13
|
-
return Number.isFinite(n) ? n : undefined
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Normalize the node's `status` prop to the three known states. */
|
|
17
|
-
export function normalizeStatus(raw: string | undefined): MonitorStatus {
|
|
18
|
-
return raw === 'up' || raw === 'down' ? raw : 'unknown'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Project a Monitor `KernelNode` into a `MonitorRecord`. The name comes from the
|
|
23
|
-
* kernel `Named.name` key; the domain props (`url`/`status`/`statusCode`/
|
|
24
|
-
* `latencyMs`/`lastCheckedAt`) are domain-qualified, so read them by suffix.
|
|
25
|
-
*/
|
|
26
|
-
export function monitorFromNode(node: KernelNode): MonitorRecord {
|
|
27
|
-
const p = node.props ?? {}
|
|
28
|
-
return {
|
|
29
|
-
id: node.id,
|
|
30
|
-
path: node.path ?? '',
|
|
31
|
-
name: readProp(p, PROP.named.name) ?? lastSegment(node.path ?? '') ?? node.id,
|
|
32
|
-
url: readPropBySuffix(p, '.property.url') ?? '',
|
|
33
|
-
status: normalizeStatus(readPropBySuffix(p, '.property.status')),
|
|
34
|
-
statusCode: readNumberBySuffix(p, '.property.statusCode'),
|
|
35
|
-
latencyMs: readNumberBySuffix(p, '.property.latencyMs'),
|
|
36
|
-
lastCheckedAt: readPropBySuffix(p, '.property.lastCheckedAt'),
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/** The Monitor feature's client types. */
|
|
2
|
-
|
|
3
|
-
/** The three known states of a Monitor's normalized status. */
|
|
4
|
-
export type MonitorStatus = 'up' | 'down' | 'unknown'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Client-side projection of a Monitor node. Domain props are read off the
|
|
8
|
-
* kernel node by suffix (see `monitor.mappers.ts`); numeric props are coerced.
|
|
9
|
-
*/
|
|
10
|
-
export type MonitorRecord = {
|
|
11
|
-
id: string
|
|
12
|
-
path: string
|
|
13
|
-
name: string
|
|
14
|
-
url: string
|
|
15
|
-
/** up | down | unknown — normalized from the node's `status` prop. */
|
|
16
|
-
status: MonitorStatus
|
|
17
|
-
/** HTTP status code from the last check, if any. */
|
|
18
|
-
statusCode?: number
|
|
19
|
-
/** Round-trip latency of the last check, in milliseconds. */
|
|
20
|
-
latencyMs?: number
|
|
21
|
-
/** ISO timestamp of the last check, if any. */
|
|
22
|
-
lastCheckedAt?: string
|
|
23
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MonitorDetails — the Monitor record's PRESENTATION, extracted pure. Given a
|
|
3
|
-
* projected `MonitorRecord`, it lays out the StatusBadge + the url/latency/
|
|
4
|
-
* statusCode/last-checked KV rows using the feature-agnostic `@/ui` primitives.
|
|
5
|
-
*
|
|
6
|
-
* Pure: no hooks, no kernel, no data loading — props in, DOM out. The container
|
|
7
|
-
* (`MonitorCard`) owns loading the record and wiring the "Check now" write.
|
|
8
|
-
*/
|
|
9
|
-
import { ExternalLink, KV, Mono, relativeTime } from '@/ui'
|
|
10
|
-
|
|
11
|
-
import type { MonitorRecord } from '../monitor.types'
|
|
12
|
-
|
|
13
|
-
import { StatusBadge } from './StatusBadge.UI'
|
|
14
|
-
|
|
15
|
-
export function MonitorDetails({ record }: { record: MonitorRecord }) {
|
|
16
|
-
return (
|
|
17
|
-
<>
|
|
18
|
-
<div className="status-row">
|
|
19
|
-
<StatusBadge status={record.status} />
|
|
20
|
-
</div>
|
|
21
|
-
|
|
22
|
-
<div className="kv">
|
|
23
|
-
<KV label="url">
|
|
24
|
-
<ExternalLink url={record.url || undefined} />
|
|
25
|
-
</KV>
|
|
26
|
-
<KV label="latency">
|
|
27
|
-
<Mono value={record.latencyMs !== undefined ? `${record.latencyMs}ms` : undefined} />
|
|
28
|
-
</KV>
|
|
29
|
-
<KV label="status code">
|
|
30
|
-
<Mono value={record.statusCode !== undefined ? String(record.statusCode) : undefined} />
|
|
31
|
-
</KV>
|
|
32
|
-
<KV label="last checked">
|
|
33
|
-
<Mono value={relativeTime(record.lastCheckedAt)} title={record.lastCheckedAt} />
|
|
34
|
-
</KV>
|
|
35
|
-
</div>
|
|
36
|
-
</>
|
|
37
|
-
)
|
|
38
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/** Monitor status chip — pure presentational; styling lives in `styles.css`. */
|
|
2
|
-
|
|
3
|
-
import type { MonitorStatus } from '../monitor.types'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Color-coded status pill: up = green, down = red, unknown = muted. The label is
|
|
7
|
-
* the uppercased status (`UP` / `DOWN` / `UNKNOWN`). Monitor-specific — it knows
|
|
8
|
-
* the feature's up/down/unknown vocabulary — so it lives in `monitor/ui`, not the
|
|
9
|
-
* feature-agnostic `@/ui` design system.
|
|
10
|
-
*/
|
|
11
|
-
export function StatusBadge({ status }: { status: MonitorStatus }) {
|
|
12
|
-
const label = status === 'up' ? 'UP' : status === 'down' ? 'DOWN' : 'UNKNOWN'
|
|
13
|
-
return <span className={`status-badge status-${status}`}>{label}</span>
|
|
14
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Monitor feature's OWN presentation — monitor-specific, feature-aware UI
|
|
3
|
-
* (it knows the up/down/unknown status vocabulary and the record shape). Pure
|
|
4
|
-
* components, built on the feature-agnostic `@/ui` primitives. Import within the
|
|
5
|
-
* feature: `import { StatusBadge, MonitorDetails } from '../ui'`.
|
|
6
|
-
*/
|
|
7
|
-
export { StatusBadge } from './StatusBadge.UI'
|
|
8
|
-
export { MonitorDetails } from './MonitorDetails.UI'
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The `ui-monitor` view (mount path `/ui/monitor`) — the Monitor detail panel.
|
|
3
|
-
*
|
|
4
|
-
* Mounted by the Astrale shell as a sandboxed iframe; `@/shell` (built on the
|
|
5
|
-
* real `@astrale-os/shell`) completes the handshake and hands over the kernel
|
|
6
|
-
* session + the target node id. `ViewFrame` gates the handshake (loading /
|
|
7
|
-
* standalone / ready); the `ready` body delegates to `MonitorCard`, the feature
|
|
8
|
-
* container that loads the node, renders its status/url/latency, and exposes a
|
|
9
|
-
* "Check now" probe.
|
|
10
|
-
*
|
|
11
|
-
* Pure composition — no data/transport logic lives here (that's `@/monitor` +
|
|
12
|
-
* `@/shell`).
|
|
13
|
-
*/
|
|
14
|
-
import { MonitorCard } from '@/monitor'
|
|
15
|
-
import { type ShellState, ViewFrame } from '@/shell'
|
|
16
|
-
|
|
17
|
-
export function MonitorView(shell: ShellState) {
|
|
18
|
-
return (
|
|
19
|
-
<ViewFrame shell={shell} title="Status monitor" subline="ui-monitor · Astrale view SPA">
|
|
20
|
-
{(session, nodeId) =>
|
|
21
|
-
nodeId ? (
|
|
22
|
-
// Keyed by node id so a target hot-swap remounts with fresh state.
|
|
23
|
-
<MonitorCard key={nodeId} session={session} nodeId={nodeId} />
|
|
24
|
-
) : (
|
|
25
|
-
<div className="banner">No target Monitor — open this view from a Monitor node.</div>
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
</ViewFrame>
|
|
29
|
-
)
|
|
30
|
-
}
|