@astrale-os/adapter-cloudflare 0.3.1 → 0.4.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 (63) hide show
  1. package/LICENSE +202 -0
  2. package/package.json +4 -4
  3. package/template/.agents/skills/astrale-cli/SKILL.md +633 -0
  4. package/template/.domain-studio/comments.json +4 -0
  5. package/template/.env.example +3 -6
  6. package/template/CLAUDE.md +8 -6
  7. package/template/README.md +28 -20
  8. package/template/client/__tests__/app.test.tsx +39 -248
  9. package/template/client/__tests__/harness.ts +0 -47
  10. package/template/client/__tests__/shell.test.ts +46 -0
  11. package/template/client/package.json +1 -1
  12. package/template/client/src/app.tsx +6 -8
  13. package/template/client/src/shell/index.ts +1 -0
  14. package/template/client/src/shell/transformers.ts +36 -33
  15. package/template/client/src/shell/use-node.ts +21 -13
  16. package/template/client/src/ui/index.ts +4 -7
  17. package/template/core/README.md +23 -0
  18. package/template/deps.ts +10 -9
  19. package/template/domain.ts +11 -9
  20. package/template/env.ts +3 -5
  21. package/template/functions/index.ts +14 -37
  22. package/template/icons.ts +25 -0
  23. package/template/integrations/README.md +22 -0
  24. package/template/package.json +3 -3
  25. package/template/runtime/index.ts +25 -72
  26. package/template/schema/index.ts +10 -10
  27. package/template/tsconfig.json +1 -0
  28. package/template/views/index.ts +15 -12
  29. package/template/client/__tests__/kernel.test.ts +0 -77
  30. package/template/client/__tests__/seam.test.tsx +0 -115
  31. package/template/client/src/status/components/StatusCard.tsx +0 -160
  32. package/template/client/src/status/components/index.ts +0 -1
  33. package/template/client/src/status/hooks/index.ts +0 -3
  34. package/template/client/src/status/hooks/useCheck.mutation.ts +0 -16
  35. package/template/client/src/status/hooks/useCheckable.query.ts +0 -72
  36. package/template/client/src/status/index.ts +0 -7
  37. package/template/client/src/status/status.api.ts +0 -29
  38. package/template/client/src/status/status.mappers.ts +0 -102
  39. package/template/client/src/status/status.types.ts +0 -26
  40. package/template/client/src/ui/StatusBadge.tsx +0 -31
  41. package/template/client/src/views/status.tsx +0 -28
  42. package/template/core/monitor/health.ts +0 -34
  43. package/template/core/monitor/index.ts +0 -9
  44. package/template/core/monitor/keys.ts +0 -41
  45. package/template/core/monitor/node.ts +0 -63
  46. package/template/integrations/prober/http.ts +0 -32
  47. package/template/integrations/prober/mock.ts +0 -18
  48. package/template/integrations/prober/port.ts +0 -26
  49. package/template/integrations/prober/registry.ts +0 -65
  50. package/template/pnpm-lock.yaml +0 -2780
  51. package/template/runtime/monitoring/index.ts +0 -8
  52. package/template/runtime/monitoring/monitor/check.ts +0 -29
  53. package/template/runtime/monitoring/monitor/index.ts +0 -10
  54. package/template/runtime/monitoring/monitor/seed.ts +0 -104
  55. package/template/runtime/monitoring/monitor/watch.ts +0 -31
  56. package/template/runtime/monitoring/page/add.ts +0 -21
  57. package/template/runtime/monitoring/page/check.ts +0 -50
  58. package/template/runtime/monitoring/page/create.ts +0 -24
  59. package/template/runtime/monitoring/page/index.ts +0 -9
  60. package/template/runtime/shared.ts +0 -21
  61. package/template/schema/monitor.ts +0 -92
  62. package/template/views/status-page.ts +0 -16
  63. package/template/views/welcome.ts +0 -35
@@ -1,8 +0,0 @@
1
- /**
2
- * Monitoring runtime context — method implementations grouped by the domain
3
- * slice they serve. `runtime/index.ts` wires these into SDK method handlers.
4
- */
5
- import * as monitor from './monitor'
6
- import * as page from './page'
7
-
8
- export { monitor, page }
@@ -1,29 +0,0 @@
1
- import type { Prober } from '../../../integrations/prober/port'
2
- import type { CallableKernel } from '../../shared'
3
-
4
- /**
5
- * `Monitor.check` (instance) — implements `Checkable.check` for a single endpoint:
6
- * probe the target via the resolved `Prober` port, classify (pure, `core/monitor`),
7
- * record status/latency on the node, and return the verdict. `url` + `prober` are
8
- * resolved by the wiring (`runtime/index.ts`) from `self.node()`.
9
- */
10
- import { classify, MONITOR_KEYS } from '../../../core/monitor'
11
-
12
- export async function check(
13
- kernel: CallableKernel,
14
- prober: Prober,
15
- selfPathRaw: string,
16
- url: string,
17
- ): Promise<{ status: string }> {
18
- const result = await prober.probe(url)
19
- const status = classify(result.statusCode)
20
- await kernel.call(`${selfPathRaw}::update`, {
21
- props: {
22
- [MONITOR_KEYS.status]: status,
23
- [MONITOR_KEYS.statusCode]: result.statusCode,
24
- [MONITOR_KEYS.latencyMs]: result.latencyMs,
25
- [MONITOR_KEYS.lastCheckedAt]: new Date().toISOString(),
26
- },
27
- })
28
- return { status }
29
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Monitor operations inside the monitoring bounded context — one file per
3
- * method, assembled here for the composition root (`runtime/index.ts`). Each is
4
- * transport-agnostic logic over a
5
- * `CallableKernel` (+ the `Prober` port where it probes), delegating pure
6
- * decisions to `core/monitor` — so all are unit-testable with fakes.
7
- */
8
- export { check } from './check'
9
- export { seed } from './seed'
10
- export { watch } from './watch'
@@ -1,104 +0,0 @@
1
- import type { Prober } from '../../../integrations/prober/port'
2
-
3
- /**
4
- * The domain's post-install bootstrap — exposed as the standalone `seed` function
5
- * (`functions/`) and wired as `postInstall` in `domain.ts`. The kernel calls it
6
- * ONCE after install, as __SYSTEM__, so the
7
- * domain comes up demonstrable: a `/monitoring` folder, `/monitoring/monitors`
8
- * starter Monitors (each probed once), and `/monitoring/pages/status`, a
9
- * StatusPage that `watches` them (its initial status rolled up from the probes).
10
- * Idempotent: node creates swallow `PATH_CONFLICT` (fixed paths) and re-linking
11
- * a `watches` edge is a graph no-op.
12
- * It grants nothing — the installing owner reaches these nodes via root-propagated
13
- * perms; grant explicitly here for non-owner access.
14
- */
15
- import {
16
- classify,
17
- FOLDER_CLASS,
18
- MONITOR_CLASS,
19
- MONITOR_KEYS,
20
- MONITORING_PARENT,
21
- MONITORS_PARENT,
22
- NAME_KEY,
23
- NODE_CREATE,
24
- rollup,
25
- STARTER_PAGE,
26
- STARTERS,
27
- STATUS_PAGE_CLASS,
28
- STATUS_PAGES_PARENT,
29
- PAGE_KEYS,
30
- WATCHES_EDGE,
31
- WATCHES_KEYS,
32
- } from '../../../core/monitor'
33
- import { type CallableKernel, isPathConflict } from '../../shared'
34
-
35
- /** Create a node, swallowing a re-seed's `PATH_CONFLICT`. Returns true if newly created. */
36
- async function ensure(kernel: CallableKernel, params: unknown): Promise<boolean> {
37
- try {
38
- await kernel.call(NODE_CREATE, params)
39
- return true
40
- } catch (e) {
41
- if (!isPathConflict(e)) throw e
42
- return false
43
- }
44
- }
45
-
46
- export async function seed(kernel: CallableKernel, prober: Prober): Promise<{ seeded: number }> {
47
- await ensure(kernel, {
48
- class: FOLDER_CLASS,
49
- path: MONITORING_PARENT,
50
- props: { [NAME_KEY]: 'monitoring' },
51
- })
52
-
53
- await ensure(kernel, {
54
- class: FOLDER_CLASS,
55
- path: MONITORS_PARENT,
56
- props: { [NAME_KEY]: 'monitors' },
57
- })
58
-
59
- // Probe + create each starter Monitor; collect the verdict for the page roll-up.
60
- const members: { status: string; critical: boolean }[] = []
61
- let seeded = 0
62
- for (const s of STARTERS) {
63
- const result = await prober.probe(s.url)
64
- const status = classify(result.statusCode)
65
- members.push({ status, critical: s.critical })
66
- const created = await ensure(kernel, {
67
- class: MONITOR_CLASS,
68
- path: `${MONITORS_PARENT}/${s.slug}`,
69
- props: {
70
- [NAME_KEY]: s.name,
71
- [MONITOR_KEYS.url]: s.url,
72
- [MONITOR_KEYS.status]: status,
73
- [MONITOR_KEYS.statusCode]: result.statusCode,
74
- [MONITOR_KEYS.latencyMs]: result.latencyMs,
75
- [MONITOR_KEYS.lastCheckedAt]: new Date().toISOString(),
76
- },
77
- })
78
- if (created) seeded++
79
- }
80
-
81
- // A StatusPage watching every starter, its status rolled up from the probes.
82
- await ensure(kernel, {
83
- class: FOLDER_CLASS,
84
- path: STATUS_PAGES_PARENT,
85
- props: { [NAME_KEY]: 'pages' },
86
- })
87
- const pagePath = `${STATUS_PAGES_PARENT}/${STARTER_PAGE.slug}`
88
- await ensure(kernel, {
89
- class: STATUS_PAGE_CLASS,
90
- path: pagePath,
91
- props: { [NAME_KEY]: STARTER_PAGE.name, [PAGE_KEYS.status]: rollup(members) },
92
- })
93
- // Re-linking the same (page, monitor) `watches` edge is a graph no-op, so this
94
- // needs no conflict guard.
95
- for (const s of STARTERS) {
96
- await kernel.call(`${pagePath}::link`, {
97
- edgeClass: WATCHES_EDGE,
98
- target: `${MONITORS_PARENT}/${s.slug}`,
99
- props: { [WATCHES_KEYS.critical]: s.critical },
100
- })
101
- }
102
-
103
- return { seeded }
104
- }
@@ -1,31 +0,0 @@
1
- /**
2
- * `Monitor.watch` (static) — create a Monitor node under `/monitoring/monitors`.
3
- * The slug format is pure (`core/monitor`'s `uniqueSlug`); the entropy suffix
4
- * (impure, clock/RNG) comes from `runtime/shared`, keeping core deterministic.
5
- */
6
- import {
7
- MONITOR_CLASS,
8
- MONITOR_KEYS,
9
- MONITORS_PARENT,
10
- NAME_KEY,
11
- NODE_CREATE,
12
- uniqueSlug,
13
- } from '../../../core/monitor'
14
- import { type CallableKernel, randomSuffix } from '../../shared'
15
-
16
- export async function watch(
17
- kernel: CallableKernel,
18
- params: { url: string; name?: string },
19
- ): Promise<{ id: string; path: string }> {
20
- const path = `${MONITORS_PARENT}/${uniqueSlug(params.url, randomSuffix())}`
21
- const created = (await kernel.call(NODE_CREATE, {
22
- class: MONITOR_CLASS,
23
- path,
24
- props: {
25
- [NAME_KEY]: params.name ?? params.url,
26
- [MONITOR_KEYS.url]: params.url,
27
- [MONITOR_KEYS.status]: 'unknown',
28
- },
29
- })) as { id: string }
30
- return { id: created.id, path }
31
- }
@@ -1,21 +0,0 @@
1
- import type { CallableKernel } from '../../shared'
2
-
3
- /**
4
- * `StatusPage.add` (instance) — watch a Monitor: create a `watches` edge from
5
- * this page to `monitor`, tagged `critical` (default false). `monitor` is any
6
- * node address (a tree path or `@<id>`).
7
- */
8
- import { WATCHES_EDGE, WATCHES_KEYS } from '../../../core/monitor'
9
-
10
- export async function add(
11
- kernel: CallableKernel,
12
- selfPathRaw: string,
13
- params: { monitor: string; critical?: boolean },
14
- ): Promise<{ watched: string }> {
15
- await kernel.call(`${selfPathRaw}::link`, {
16
- edgeClass: WATCHES_EDGE,
17
- target: params.monitor,
18
- props: { [WATCHES_KEYS.critical]: params.critical ?? false },
19
- })
20
- return { watched: params.monitor }
21
- }
@@ -1,50 +0,0 @@
1
- import type { CallableKernel } from '../../shared'
2
-
3
- /**
4
- * `StatusPage.check` (instance) — implements `Checkable.check` for a page: walk
5
- * the `watches` edges, re-check each monitor (a cross-node `@<id>::check` call),
6
- * roll the verdicts up (pure, `core/monitor`), record the page status, return it.
7
- *
8
- * `getLinks` returns raw `Edge`s whose endpoint/prop accessors aren't typed yet
9
- * (an SDK gap) — `parseWatches` is the small cast that bridges it.
10
- */
11
- import { PAGE_KEYS, rollup, WATCHES_EDGE, WATCHES_KEYS } from '../../../core/monitor'
12
-
13
- /** A raw `watches` edge as `getLinks` returns it (endpoints are string-or-`{raw}`). */
14
- type RawEdge = {
15
- class?: string | { raw: string }
16
- target?: string | { raw: string }
17
- props?: Record<string, unknown>
18
- }
19
-
20
- function rawOf(p: RawEdge['target']): string | undefined {
21
- if (typeof p === 'string') return p
22
- return typeof p?.raw === 'string' ? p.raw : undefined
23
- }
24
-
25
- /** The monitors this page watches: each target ref + its `critical` flag. */
26
- function parseWatches(raw: unknown): { monitor: string; critical: boolean }[] {
27
- const edges = (Array.isArray(raw) ? raw : []) as RawEdge[]
28
- const out: { monitor: string; critical: boolean }[] = []
29
- for (const e of edges) {
30
- if (rawOf(e.class) !== WATCHES_EDGE) continue
31
- const monitor = rawOf(e.target)
32
- if (monitor) out.push({ monitor, critical: e.props?.[WATCHES_KEYS.critical] === true })
33
- }
34
- return out
35
- }
36
-
37
- export async function check(
38
- kernel: CallableKernel,
39
- selfPathRaw: string,
40
- ): Promise<{ status: string }> {
41
- const links = await kernel.call(`${selfPathRaw}::getLinks`, { direction: 'out' })
42
- const members = []
43
- for (const w of parseWatches(links)) {
44
- const { status } = (await kernel.call(`${w.monitor}::check`, {})) as { status: string }
45
- members.push({ status, critical: w.critical })
46
- }
47
- const status = rollup(members)
48
- await kernel.call(`${selfPathRaw}::update`, { props: { [PAGE_KEYS.status]: status } })
49
- return { status }
50
- }
@@ -1,24 +0,0 @@
1
- /**
2
- * `StatusPage.create` (static) — create a StatusPage node under `/monitoring/pages`.
3
- */
4
- import {
5
- NAME_KEY,
6
- NODE_CREATE,
7
- STATUS_PAGE_CLASS,
8
- STATUS_PAGES_PARENT,
9
- uniqueSlug,
10
- } from '../../../core/monitor'
11
- import { type CallableKernel, randomSuffix } from '../../shared'
12
-
13
- export async function create(
14
- kernel: CallableKernel,
15
- params: { name: string },
16
- ): Promise<{ id: string; path: string }> {
17
- const path = `${STATUS_PAGES_PARENT}/${uniqueSlug(params.name, randomSuffix())}`
18
- const created = (await kernel.call(NODE_CREATE, {
19
- class: STATUS_PAGE_CLASS,
20
- path,
21
- props: { [NAME_KEY]: params.name },
22
- })) as { id: string }
23
- return { id: created.id, path }
24
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * StatusPage operations inside the monitoring bounded context — one file per
3
- * method, assembled here for the composition root (`runtime/index.ts`).
4
- * Transport-agnostic logic over a `CallableKernel`, delegating the roll-up
5
- * decision to `core/monitor`.
6
- */
7
- export { add } from './add'
8
- export { check } from './check'
9
- export { create } from './create'
@@ -1,21 +0,0 @@
1
- /**
2
- * Runtime-internal helpers shared across the context's operations. Not exported
3
- * from the domain — just the seam the operation files agree on.
4
- */
5
-
6
- /**
7
- * The minimal kernel surface the operations need — kept narrow so each is
8
- * unit-testable with a fake `{ call }`. The SDK hands the real client at the
9
- * `runtime/index.ts` seam.
10
- */
11
- export type CallableKernel = { call(path: string, params: unknown): Promise<unknown> }
12
-
13
- /** True for the kernel's create-collision error — lets `seed` stay idempotent. */
14
- export function isPathConflict(e: unknown): boolean {
15
- return e instanceof Error && e.message.includes('PATH_CONFLICT')
16
- }
17
-
18
- /** Short clock+RNG entropy to dodge same-ms / same-stem slug collisions (impure). */
19
- export function randomSuffix(): string {
20
- return `${Date.now().toString(36).slice(-4)}${Math.random().toString(36).slice(2, 6)}`
21
- }
@@ -1,92 +0,0 @@
1
- /**
2
- * Monitoring context — the domain's single bounded slice: a `Checkable` contract
3
- * implemented by two classes, plus the edge that binds them.
4
- *
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. (The demo set is laid down by
10
- * the standalone `seed` function — see `functions/`.)
11
- * - Class `StatusPage` a roll-up. `check()` re-checks the monitors it
12
- * `watches` and aggregates; `create` makes a page;
13
- * `add` watches a monitor.
14
- * - Edge `watches` StatusPage → Monitor, with a `critical` flag.
15
- */
16
- import { edgeClass, nodeClass, nodeInterface } from '@astrale-os/kernel-core'
17
- import { fn } from '@astrale-os/kernel-dsl'
18
- import { z } from 'zod'
19
-
20
- /** Thin ref to a created node — what the static factories return (a full Node
21
- * value does not round-trip over the worker wire). */
22
- export const NodeRef = z.object({ id: z.string(), path: z.string() })
23
-
24
- /** What `check()` reports — the subject's current health verdict. */
25
- export const CheckResult = z.object({ status: z.string() })
26
-
27
- export const Checkable = nodeInterface({
28
- methods: {
29
- // `abstract` → no shared body; every implementer supplies its own `check`,
30
- // wired per-class in `runtime/`. `@<node>::check` dispatches by the node's class.
31
- check: fn({ inheritance: 'abstract', returns: CheckResult }),
32
- },
33
- })
34
-
35
- export const Monitor = nodeClass({
36
- implements: [Checkable],
37
- props: {
38
- /** The target URL this monitor probes. */
39
- url: z.string(),
40
- // Live status, written by `check`. PLAIN STRING, not `z.enum()`: `::update`
41
- // silently drops `z.enum()` props. Values: 'up' | 'down' | 'unknown'.
42
- status: z.string().optional(),
43
- /** Last observed HTTP status code (0 = host unreachable). */
44
- statusCode: z.number().int().optional(),
45
- /** Last observed round-trip latency, in ms. */
46
- latencyMs: z.number().int().optional(),
47
- /** ISO timestamp of the last check. */
48
- lastCheckedAt: z.string().optional(),
49
- },
50
- methods: {
51
- // Implements `Checkable.check` for a single endpoint (probe `url`). Redeclared
52
- // here — a concrete class must declare each abstract method it implements, so
53
- // the compiler materializes Monitor's OWN `check` node for per-class dispatch.
54
- // The body lives in `runtime/monitoring/monitor/check.ts`.
55
- check: fn({ returns: CheckResult }),
56
- /** Create a Monitor under `/monitoring/monitors`. */
57
- watch: fn({
58
- static: true,
59
- params: { url: z.string(), name: z.string().optional() },
60
- returns: NodeRef,
61
- }),
62
- },
63
- })
64
-
65
- export const StatusPage = nodeClass({
66
- implements: [Checkable],
67
- props: {
68
- /** Rolled-up status, written by `check`. 'up' | 'degraded' | 'down' | 'unknown'. */
69
- status: z.string().optional(),
70
- },
71
- methods: {
72
- // Implements `Checkable.check` for a page (roll up the watched monitors).
73
- // Redeclared so StatusPage materializes its OWN `check` node. Body in
74
- // `runtime/monitoring/page/check.ts`.
75
- check: fn({ returns: CheckResult }),
76
- /** Create a StatusPage under `/monitoring/pages`. */
77
- create: fn({ static: true, params: { name: z.string() }, returns: NodeRef }),
78
- /** Watch a monitor (a `watches` edge); `critical` decides the roll-up weight. */
79
- add: fn({
80
- params: { monitor: z.string(), critical: z.boolean().optional() },
81
- returns: z.object({ watched: z.string() }),
82
- }),
83
- },
84
- })
85
-
86
- export const watches = edgeClass(
87
- { as: 'page', types: [StatusPage] },
88
- { as: 'monitor', types: [Monitor] },
89
- // `critical` drives the roll-up: a critical monitor down ⇒ page `down`; a
90
- // non-critical one down ⇒ `degraded`.
91
- { props: { critical: z.boolean() } },
92
- )
@@ -1,16 +0,0 @@
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,35 +0,0 @@
1
- /**
2
- * `welcome` — a lightweight inline-HTML View, rendered straight from the worker
3
- * (no SPA, no client bundle, no shell handshake). Because it has a `render`, the
4
- * SDK mounts a worker route and the View node's iframe points at
5
- * `<publicUrl>/views/welcome`. Add a rich, shell-connected view by dropping a
6
- * `client/` SPA in and binding a View to `/ui/<route>` instead.
7
- */
8
- import { defineView } from '@astrale-os/sdk'
9
-
10
- export const welcome = defineView({
11
- auth: 'public',
12
- render: ({ c }) =>
13
- c.html(
14
- `<!doctype html>
15
- <html lang="en">
16
- <head>
17
- <meta charset="utf-8" />
18
- <meta name="viewport" content="width=device-width, initial-scale=1" />
19
- <title>astrale domain</title>
20
- <style>
21
- body { font: 14px/1.6 system-ui, sans-serif; margin: 2rem; color: #111; max-width: 40rem; }
22
- code { background: #f4f4f5; padding: 0.1rem 0.35rem; border-radius: 4px; }
23
- h1 { font-size: 1.25rem; }
24
- </style>
25
- </head>
26
- <body>
27
- <h1>Your Astrale domain is live</h1>
28
- <p>This inline-HTML view is rendered by the worker. The RPC surface and the
29
- <code>seed</code> post-install hook run alongside it.</p>
30
- <p>Edit <code>views/welcome.ts</code> to change this page — it hot-reloads at
31
- the same URL.</p>
32
- </body>
33
- </html>`,
34
- ),
35
- })