@astrale-os/adapter-cloudflare 0.1.9 → 0.2.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 (81) hide show
  1. package/package.json +1 -1
  2. package/template/.agents/skills/astrale-cli/SKILL.md +1 -1
  3. package/template/.agents/skills/astrale-domain/SKILL.md +5 -5
  4. package/template/.env.example +5 -7
  5. package/template/README.md +2 -2
  6. package/template/client/README.md +81 -62
  7. package/template/client/__tests__/app.test.tsx +143 -98
  8. package/template/client/__tests__/harness.ts +62 -12
  9. package/template/client/__tests__/kernel.test.ts +40 -51
  10. package/template/client/__tests__/seam.test.tsx +115 -0
  11. package/template/client/index.html +1 -1
  12. package/template/client/package.json +1 -0
  13. package/template/client/src/app.tsx +34 -83
  14. package/template/client/src/main.tsx +2 -2
  15. package/template/client/src/shell/client.ts +67 -0
  16. package/template/client/src/shell/index.ts +20 -0
  17. package/template/client/src/shell/invoke.ts +35 -0
  18. package/template/client/src/shell/transformers.ts +72 -0
  19. package/template/client/src/shell/use-async.ts +56 -0
  20. package/template/client/src/shell/use-capability.ts +59 -0
  21. package/template/client/src/shell/use-node.ts +61 -0
  22. package/template/client/src/shell/use-shell.ts +91 -0
  23. package/template/client/src/shell/view-router.tsx +97 -0
  24. package/template/client/src/status/components/StatusCard.tsx +50 -0
  25. package/template/client/src/status/components/index.ts +1 -0
  26. package/template/client/src/status/hooks/index.ts +3 -0
  27. package/template/client/src/status/hooks/useCheck.mutation.ts +16 -0
  28. package/template/client/src/status/hooks/useCheckable.query.ts +64 -0
  29. package/template/client/src/status/index.ts +7 -0
  30. package/template/client/src/status/status.api.ts +12 -0
  31. package/template/client/src/status/status.mappers.ts +19 -0
  32. package/template/client/src/status/status.types.ts +11 -0
  33. package/template/client/src/styles.css +182 -4
  34. package/template/client/src/ui/StatusBadge.tsx +31 -0
  35. package/template/client/src/ui/format.ts +24 -0
  36. package/template/client/src/ui/index.ts +13 -0
  37. package/template/client/src/ui/surface.tsx +56 -0
  38. package/template/client/src/ui/value.tsx +32 -0
  39. package/template/client/src/views/status.tsx +28 -0
  40. package/template/client/tsconfig.json +2 -1
  41. package/template/client/vite.config.ts +11 -13
  42. package/template/client/vitest.config.ts +11 -5
  43. package/template/core/monitor/health.ts +34 -0
  44. package/template/core/monitor/index.ts +9 -0
  45. package/template/core/monitor/keys.ts +41 -0
  46. package/template/core/monitor/node.ts +57 -0
  47. package/template/deps.ts +10 -9
  48. package/template/domain.ts +1 -1
  49. package/template/env.ts +2 -9
  50. package/template/integrations/prober/http.ts +32 -0
  51. package/template/integrations/prober/mock.ts +18 -0
  52. package/template/integrations/prober/port.ts +26 -0
  53. package/template/integrations/prober/registry.ts +65 -0
  54. package/template/package.json +1 -1
  55. package/template/pnpm-lock.yaml +2766 -0
  56. package/template/runtime/index.ts +63 -34
  57. package/template/runtime/monitor/check.ts +29 -0
  58. package/template/runtime/monitor/index.ts +9 -0
  59. package/template/runtime/monitor/seed.ts +95 -0
  60. package/template/runtime/monitor/watch.ts +31 -0
  61. package/template/runtime/shared.ts +21 -0
  62. package/template/runtime/status-page/add.ts +21 -0
  63. package/template/runtime/status-page/check.ts +50 -0
  64. package/template/runtime/status-page/create.ts +24 -0
  65. package/template/runtime/status-page/index.ts +8 -0
  66. package/template/schema/index.ts +11 -4
  67. package/template/schema/monitor.ts +94 -0
  68. package/template/views/index.ts +8 -2
  69. package/template/views/status-page.ts +16 -0
  70. package/template/client/src/lib/kernel.ts +0 -135
  71. package/template/client/src/lib/shell.ts +0 -197
  72. package/template/client/src/lib/use-node.ts +0 -66
  73. package/template/client/src/lib/use-shell.ts +0 -85
  74. package/template/core/keys.ts +0 -28
  75. package/template/core/note.ts +0 -148
  76. package/template/integrations/summary/heuristic.ts +0 -25
  77. package/template/integrations/summary/http.ts +0 -69
  78. package/template/integrations/summary/port.ts +0 -21
  79. package/template/integrations/summary/registry.ts +0 -52
  80. package/template/schema/note.ts +0 -67
  81. package/template/views/note.ts +0 -21
package/template/deps.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { Env } from './env'
2
+
1
3
  /**
2
4
  * env → handler dependency container — the ONE place the worker env becomes the
3
5
  * typed `ctx.deps` every method reads. `defineDomain({ deps })` mounts it; the
@@ -5,21 +7,20 @@
5
7
  * it's where ports/clients are wired once instead of being re-derived in every
6
8
  * handler.
7
9
  *
8
- * The summarizer port is resolved ON-REQUEST (lazy `summarizer()` via the
9
- * registry), not here — so this stays a cheap synchronous wiring step and a
10
- * worker never validates a provider's env until a handler actually uses it.
10
+ * The prober port is resolved PER-REQUEST and PER-NODE (each `prober(target)`
11
+ * call), not here — so this stays a cheap synchronous wiring step and the backend
12
+ * can be chosen from the monitor being probed, not just the env.
11
13
  *
12
14
  * Omit the `deps` field in `domain.ts` for the trivial case (handlers then get
13
- * raw `Env`). Transform here the moment a handler should depend on a PORT
14
- * instead of raw config — that's the whole point of this seam.
15
+ * raw `Env`). Transform here the moment a handler should depend on a PORT instead
16
+ * of raw config — that's the whole point of this seam.
15
17
  */
16
- import { buildSummarizerRegistry, type SummarizerRegistry } from './integrations/summary/registry'
17
- import type { Env } from './env'
18
+ import { buildProberRegistry, type ProberRegistry } from './integrations/prober/registry'
18
19
 
19
20
  /** Typed dependency container handed to every method as `ctx.deps`. */
20
- export interface Deps extends SummarizerRegistry {}
21
+ export interface Deps extends ProberRegistry {}
21
22
 
22
23
  /** Map the worker env to the handler deps (the seam `defineDomain({ deps })` mounts). */
23
24
  export function deps(env: Env): Deps {
24
- return buildSummarizerRegistry(env)
25
+ return buildProberRegistry(env)
25
26
  }
@@ -28,6 +28,6 @@ export const domain = defineDomain({
28
28
  views,
29
29
  functions,
30
30
  client: { dir: 'client' },
31
- postInstall: `/:${schema.domain}:class.Note:seed`,
31
+ postInstall: `/:${schema.domain}:class.Monitor:seed`,
32
32
  requires: [],
33
33
  })
package/template/env.ts CHANGED
@@ -19,15 +19,8 @@ export interface Env {
19
19
  /** Dev-only: forward /ui/* to a running Vite dev server. */
20
20
  VIEW_DEV_URL?: string
21
21
 
22
- // ── Summarizer (the example external-API integration) ─────────────
23
- /** Which summarizer adapter to bind: `heuristic` (default, no secret) | `http`. */
24
- NOTE_SUMMARIZER?: string
25
- /** `http` only — bearer token for the OpenAI-compatible upstream (a secret). */
26
- SUMMARIZER_API_KEY?: string
27
- /** `http` only — OpenAI-compatible base URL (e.g. https://api.openai.com/v1). */
28
- SUMMARIZER_BASE_URL?: string
29
- /** `http` only — model id (default gpt-4o-mini). */
30
- SUMMARIZER_MODEL?: string
22
+ /** `http` only per-probe timeout in ms (default 10000). */
23
+ PROBE_TIMEOUT_MS?: string
31
24
 
32
25
  [key: string]: unknown
33
26
  }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * HTTP prober — the REAL external adapter: a single keyless `fetch` to the
3
+ * target, timing the round-trip. This is the shape your domain's external calls
4
+ * take — a timeout via `AbortSignal`, failures mapped to a domain result rather
5
+ * than thrown. NO API key: a fresh scaffold makes a true external call out of
6
+ * the box (it's the registry default).
7
+ */
8
+ import type { Prober, ProbeResult } from './port'
9
+
10
+ export interface HttpProberConfig {
11
+ /** Per-probe timeout in ms (default 10000). */
12
+ timeoutMs?: number
13
+ }
14
+
15
+ const DEFAULT_TIMEOUT_MS = 10_000
16
+
17
+ /** Build the keyless HTTP prober (a `GET` with a timeout). */
18
+ export function createHttpProber(config: HttpProberConfig = {}): Prober {
19
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS
20
+ return {
21
+ async probe(url): Promise<ProbeResult> {
22
+ const start = Date.now()
23
+ try {
24
+ const res = await fetch(url, { redirect: 'follow', signal: AbortSignal.timeout(timeoutMs) })
25
+ return { statusCode: res.status, latencyMs: Date.now() - start }
26
+ } catch {
27
+ // Unreachable / timeout / DNS failure — a `down` result, not an error.
28
+ return { statusCode: 0, latencyMs: Date.now() - start }
29
+ }
30
+ },
31
+ }
32
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Mock prober — offline + deterministic, no network. Used by tests (construct it
3
+ * directly with the status you want) and by `PROBER=mock` for an offline dev
4
+ * loop. Returns a fixed result regardless of URL.
5
+ */
6
+ import type { Prober, ProbeResult } from './port'
7
+
8
+ /** Build an offline prober that always returns `opts` (default: a healthy 200). */
9
+ export function createMockProber(opts: { statusCode?: number; latencyMs?: number } = {}): Prober {
10
+ const statusCode = opts.statusCode ?? 200
11
+ const latencyMs = opts.latencyMs ?? 1
12
+ const result: ProbeResult = { statusCode, latencyMs }
13
+ return {
14
+ probe() {
15
+ return Promise.resolve(result)
16
+ },
17
+ }
18
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Prober — the PORT between the domain's logic and whatever performs the actual
3
+ * uptime probe. This is the external-API seam every real domain has, reduced to
4
+ * the narrowest interface the logic needs.
5
+ *
6
+ * `runtime/` logic depends on THIS interface only — never on `fetch`, an SDK, or
7
+ * `env`. Adapters in this folder implement it: `http.ts` (a real, keyless HTTP
8
+ * request) and `mock.ts` (offline + deterministic, for tests). The registry
9
+ * picks one from `env`; the handler logic only ever sees the resolved port. Swap
10
+ * or add a backend = one adapter + one arm in `registry.ts`.
11
+ */
12
+ export interface ProbeResult {
13
+ /** Observed HTTP status code, or `0` if the host was unreachable. */
14
+ statusCode: number
15
+ /** Round-trip time in ms. */
16
+ latencyMs: number
17
+ }
18
+
19
+ export interface Prober {
20
+ /**
21
+ * Probe a URL once. MUST resolve, never reject: an unreachable host or a
22
+ * timeout is a `down` RESULT (`statusCode: 0`), not an error — a probe failing
23
+ * is the normal case a monitor exists to observe.
24
+ */
25
+ probe(url: string): Promise<ProbeResult>
26
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Prober registry — the ONE place the worker env (and the probed node) become the
3
+ * live `Prober` port. Adding a backend = one more arm in `selectProber`; handler
4
+ * logic only ever sees the resolved port.
5
+ *
6
+ * Resolution is PER-REQUEST and PER-NODE: each `prober(target)` call selects +
7
+ * builds the port from env AND the monitor being probed — nothing is built at
8
+ * `deps()` / isolate setup. A per-node choice can't be memoized at the isolate
9
+ * level, which is exactly why `deps` exposes `prober()` as a FUNCTION, not a
10
+ * value. The default is the keyless `http` prober (so the scaffold makes a real
11
+ * probe out of the box); `PROBER=mock` forces the offline prober.
12
+ */
13
+ import type { Env } from '../../env'
14
+ import type { Prober } from './port'
15
+
16
+ import { createHttpProber } from './http'
17
+ import { createMockProber } from './mock'
18
+
19
+ /**
20
+ * The node a probe is for — what the registry may branch on. Mirrors how the
21
+ * `services` domain picks a provider from a node's class; here we also look at
22
+ * the target url. The handler passes this from `self.node()`; omit it for
23
+ * node-less callers (e.g. `seed`, which just wants the env default).
24
+ */
25
+ export interface ProbeTarget {
26
+ /** The monitor node's class path (`node.class.raw`). */
27
+ class: string
28
+ /** The url this monitor probes (`node.props[MONITOR_KEYS.url]`). */
29
+ url: string
30
+ }
31
+
32
+ export interface ProberRegistry {
33
+ /** Resolve the prober for a probe — built per call from env + the target node. */
34
+ prober(target?: ProbeTarget): Prober
35
+ }
36
+
37
+ /** Build the registry from the worker env. The port is resolved per request. */
38
+ export function buildProberRegistry(env: Env): ProberRegistry {
39
+ return {
40
+ prober(target) {
41
+ return selectProber(env, target)
42
+ },
43
+ }
44
+ }
45
+
46
+ /** Bind the abstract prober port to the concrete adapter for this env + target. */
47
+ function selectProber(env: Env, target?: ProbeTarget): Prober {
48
+ if (target && isLocalTarget(target.url)) {
49
+ return createMockProber()
50
+ }
51
+ // Default: the real, keyless HTTP prober. Pass the env override (if any); the
52
+ // adapter owns the default timeout, so there's no second copy of it here.
53
+ return createHttpProber({ timeoutMs: parsePositiveInt(env.PROBE_TIMEOUT_MS) })
54
+ }
55
+
56
+ /** A loopback / unspecified host the public edge can't reach. */
57
+ function isLocalTarget(url: string): boolean {
58
+ return /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(:|\/|$)/i.test(url)
59
+ }
60
+
61
+ function parsePositiveInt(value: string | undefined): number | undefined {
62
+ if (!value) return undefined
63
+ const n = Number.parseInt(value, 10)
64
+ return Number.isFinite(n) && n > 0 ? n : undefined
65
+ }
@@ -14,7 +14,7 @@
14
14
  "typecheck": "tsgo --noEmit"
15
15
  },
16
16
  "dependencies": {
17
- "@astrale-os/adapter-cloudflare": ">=0.1.9 <1.0.0",
17
+ "@astrale-os/adapter-cloudflare": ">=0.2.0 <1.0.0",
18
18
  "@astrale-os/kernel-core": ">=0.4.3 <1.0.0",
19
19
  "@astrale-os/kernel-dsl": ">=0.1.2 <1.0.0",
20
20
  "@astrale-os/sdk": ">=0.1.7 <1.0.0",