@graphorin/server 0.6.1 → 0.7.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 (181) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +3 -3
  3. package/dist/app-audit-binding.d.ts +15 -0
  4. package/dist/app-audit-binding.d.ts.map +1 -0
  5. package/dist/app-audit-binding.js +136 -0
  6. package/dist/app-audit-binding.js.map +1 -0
  7. package/dist/app-daemons.d.ts +29 -0
  8. package/dist/app-daemons.d.ts.map +1 -0
  9. package/dist/app-daemons.js +27 -0
  10. package/dist/app-daemons.js.map +1 -0
  11. package/dist/app-lifecycle.js +272 -0
  12. package/dist/app-lifecycle.js.map +1 -0
  13. package/dist/app-metrics.js +79 -0
  14. package/dist/app-metrics.js.map +1 -0
  15. package/dist/app-middleware.js +92 -0
  16. package/dist/app-middleware.js.map +1 -0
  17. package/dist/app-routes.js +131 -0
  18. package/dist/app-routes.js.map +1 -0
  19. package/dist/app-ws.js +65 -0
  20. package/dist/app-ws.js.map +1 -0
  21. package/dist/app.d.ts +16 -16
  22. package/dist/app.d.ts.map +1 -1
  23. package/dist/app.js +35 -607
  24. package/dist/app.js.map +1 -1
  25. package/dist/commentary/built-in-patterns.d.ts +1 -1
  26. package/dist/commentary/built-in-patterns.js +1 -1
  27. package/dist/commentary/built-in-patterns.js.map +1 -1
  28. package/dist/config.d.ts +100 -1
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +30 -2
  31. package/dist/config.js.map +1 -1
  32. package/dist/health/checks.d.ts +15 -1
  33. package/dist/health/checks.d.ts.map +1 -1
  34. package/dist/health/checks.js +21 -0
  35. package/dist/health/checks.js.map +1 -1
  36. package/dist/health/routes.js +1 -1
  37. package/dist/index.d.ts +8 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +19 -24
  40. package/dist/index.js.map +1 -1
  41. package/dist/internal/context.d.ts +2 -2
  42. package/dist/internal/wire-error.js +20 -0
  43. package/dist/internal/wire-error.js.map +1 -0
  44. package/dist/lifecycle/pre-bind.d.ts +1 -1
  45. package/dist/metrics/catalog.d.ts.map +1 -1
  46. package/dist/metrics/catalog.js.map +1 -1
  47. package/dist/metrics/tools-bridge.d.ts +15 -0
  48. package/dist/metrics/tools-bridge.d.ts.map +1 -0
  49. package/dist/metrics/tools-bridge.js +103 -0
  50. package/dist/metrics/tools-bridge.js.map +1 -0
  51. package/dist/middleware/audit.d.ts +1 -1
  52. package/dist/middleware/auth.js +1 -1
  53. package/dist/middleware/csrf.js +1 -1
  54. package/dist/middleware/index.js +1 -1
  55. package/dist/middleware/scope.d.ts.map +1 -1
  56. package/dist/middleware/scope.js +44 -4
  57. package/dist/middleware/scope.js.map +1 -1
  58. package/dist/package.js +1 -1
  59. package/dist/package.js.map +1 -1
  60. package/dist/registry/index.d.ts +33 -0
  61. package/dist/registry/index.d.ts.map +1 -1
  62. package/dist/registry/index.js.map +1 -1
  63. package/dist/replay/routes.d.ts +1 -1
  64. package/dist/replay/routes.js +2 -2
  65. package/dist/routes/agents.d.ts.map +1 -1
  66. package/dist/routes/agents.js +64 -18
  67. package/dist/routes/agents.js.map +1 -1
  68. package/dist/routes/auth.js +2 -2
  69. package/dist/routes/auth.js.map +1 -1
  70. package/dist/routes/sessions.js +5 -5
  71. package/dist/routes/sessions.js.map +1 -1
  72. package/dist/routes/tokens.d.ts +1 -1
  73. package/dist/routes/tokens.d.ts.map +1 -1
  74. package/dist/routes/tokens.js +21 -1
  75. package/dist/routes/tokens.js.map +1 -1
  76. package/dist/routes/workflows.d.ts.map +1 -1
  77. package/dist/routes/workflows.js +165 -19
  78. package/dist/routes/workflows.js.map +1 -1
  79. package/dist/runtime/retention.d.ts +105 -0
  80. package/dist/runtime/retention.d.ts.map +1 -0
  81. package/dist/runtime/retention.js +154 -0
  82. package/dist/runtime/retention.js.map +1 -0
  83. package/dist/runtime/run-state.d.ts +2 -0
  84. package/dist/runtime/run-state.d.ts.map +1 -1
  85. package/dist/runtime/run-state.js +34 -2
  86. package/dist/runtime/run-state.js.map +1 -1
  87. package/dist/sse/events.js +3 -4
  88. package/dist/sse/events.js.map +1 -1
  89. package/dist/tools-audit-bridge.d.ts +29 -0
  90. package/dist/tools-audit-bridge.d.ts.map +1 -0
  91. package/dist/tools-audit-bridge.js +103 -0
  92. package/dist/tools-audit-bridge.js.map +1 -0
  93. package/dist/workflows/timer-daemon.d.ts +69 -0
  94. package/dist/workflows/timer-daemon.d.ts.map +1 -0
  95. package/dist/workflows/timer-daemon.js +37 -0
  96. package/dist/workflows/timer-daemon.js.map +1 -0
  97. package/dist/ws/dispatcher.d.ts +1 -1
  98. package/dist/ws/dispatcher.js +19 -12
  99. package/dist/ws/dispatcher.js.map +1 -1
  100. package/dist/ws/index.d.ts +2 -2
  101. package/dist/ws/index.js +3 -3
  102. package/dist/ws/replay-buffer.d.ts +35 -1
  103. package/dist/ws/replay-buffer.d.ts.map +1 -1
  104. package/dist/ws/replay-buffer.js +35 -3
  105. package/dist/ws/replay-buffer.js.map +1 -1
  106. package/dist/ws/upgrade.d.ts.map +1 -1
  107. package/dist/ws/upgrade.js +24 -18
  108. package/dist/ws/upgrade.js.map +1 -1
  109. package/package.json +30 -29
  110. package/src/app-audit-binding.ts +227 -0
  111. package/src/app-daemons.ts +73 -0
  112. package/src/app-lifecycle.ts +476 -0
  113. package/src/app-metrics.ts +144 -0
  114. package/src/app-middleware.ts +149 -0
  115. package/src/app-routes.ts +305 -0
  116. package/src/app-ws.ts +111 -0
  117. package/src/app.ts +317 -0
  118. package/src/commentary/audit-bridge.ts +135 -0
  119. package/src/commentary/built-in-patterns.ts +79 -0
  120. package/src/commentary/index.ts +32 -0
  121. package/src/commentary/sanitizer.ts +238 -0
  122. package/src/commentary/types.ts +130 -0
  123. package/src/config.ts +472 -0
  124. package/src/consolidator/daemon.ts +141 -0
  125. package/src/consolidator/index.ts +14 -0
  126. package/src/errors/index.ts +247 -0
  127. package/src/health/checks.ts +322 -0
  128. package/src/health/index.ts +34 -0
  129. package/src/health/routes.ts +154 -0
  130. package/src/index.ts +243 -0
  131. package/src/internal/context.ts +77 -0
  132. package/src/internal/ids.ts +17 -0
  133. package/src/internal/json.ts +47 -0
  134. package/src/internal/wire-error.ts +44 -0
  135. package/src/lifecycle/hooks.ts +66 -0
  136. package/src/lifecycle/index.ts +16 -0
  137. package/src/lifecycle/pre-bind.ts +138 -0
  138. package/src/metrics/catalog.ts +112 -0
  139. package/src/metrics/index.ts +12 -0
  140. package/src/metrics/registry.ts +337 -0
  141. package/src/metrics/tools-bridge.ts +124 -0
  142. package/src/middleware/audit.ts +114 -0
  143. package/src/middleware/auth.ts +195 -0
  144. package/src/middleware/cors.ts +72 -0
  145. package/src/middleware/csrf.ts +98 -0
  146. package/src/middleware/idempotency.ts +336 -0
  147. package/src/middleware/index.ts +38 -0
  148. package/src/middleware/rate-limit.ts +71 -0
  149. package/src/middleware/request-state.ts +79 -0
  150. package/src/middleware/scope.ts +161 -0
  151. package/src/registry/index.ts +278 -0
  152. package/src/replay/index.ts +14 -0
  153. package/src/replay/routes.ts +255 -0
  154. package/src/routes/agents.ts +363 -0
  155. package/src/routes/audit.ts +207 -0
  156. package/src/routes/auth.ts +80 -0
  157. package/src/routes/health.ts +42 -0
  158. package/src/routes/index.ts +16 -0
  159. package/src/routes/mcp.ts +97 -0
  160. package/src/routes/memory.ts +152 -0
  161. package/src/routes/sessions.ts +250 -0
  162. package/src/routes/skills.ts +91 -0
  163. package/src/routes/tokens.ts +166 -0
  164. package/src/routes/workflows.ts +616 -0
  165. package/src/runtime/retention.ts +284 -0
  166. package/src/runtime/run-state.ts +422 -0
  167. package/src/sse/events.ts +303 -0
  168. package/src/sse/index.ts +7 -0
  169. package/src/tools-audit-bridge.ts +120 -0
  170. package/src/triggers/daemon.ts +198 -0
  171. package/src/triggers/index.ts +16 -0
  172. package/src/triggers/routes.ts +139 -0
  173. package/src/workflows/timer-daemon.ts +102 -0
  174. package/src/ws/dispatcher.ts +537 -0
  175. package/src/ws/index.ts +45 -0
  176. package/src/ws/replay-buffer.ts +209 -0
  177. package/src/ws/subjects.ts +162 -0
  178. package/src/ws/ticket.ts +174 -0
  179. package/src/ws/upgrade.ts +507 -0
  180. package/dist/lifecycle/index.js +0 -3
  181. package/dist/routes/index.js +0 -12
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Health-family REST routes.
3
+ *
4
+ * GET /health (unauthenticated; liveness)
5
+ * GET /health/secrets (admin scope)
6
+ * GET /metrics (Prometheus exposition)
7
+ *
8
+ * `/health` returns 200 even when the rollup is `'degraded'`; only
9
+ * `'failing'` short-circuits with 503 so liveness probes do not flap
10
+ * on minor degradations (e.g. WAL above the warn threshold).
11
+ *
12
+ * @packageDocumentation
13
+ */
14
+
15
+ import { getSecretsStoreStatus } from '@graphorin/security/secrets';
16
+ import type { Context } from 'hono';
17
+ import { Hono } from 'hono';
18
+
19
+ import type { ServerVariables } from '../internal/context.js';
20
+ import type { MetricRegistry } from '../metrics/registry.js';
21
+ import { createScopeMiddleware } from '../middleware/scope.js';
22
+ import { collectHealth, type HealthCheckOptions } from './checks.js';
23
+
24
+ /**
25
+ * @stable
26
+ */
27
+ export interface HealthRouteOptions {
28
+ readonly version: string;
29
+ readonly startedAt: number;
30
+ readonly now?: () => number;
31
+ readonly probes: () => HealthCheckOptions | Promise<HealthCheckOptions>;
32
+ }
33
+
34
+ /**
35
+ * Public health route (anonymous; mounted before auth middleware).
36
+ * Returns the rollup + per-check breakdown; HTTP 200 even when the
37
+ * rollup is `'degraded'` so liveness probes do not flap on minor
38
+ * degradations. Only `'failing'` short-circuits with 503.
39
+ *
40
+ * @stable
41
+ */
42
+ export function createExtendedHealthRoutes(
43
+ options: HealthRouteOptions,
44
+ ): Hono<{ Variables: ServerVariables }> {
45
+ const app = new Hono<{ Variables: ServerVariables }>();
46
+ const now = options.now ?? Date.now;
47
+
48
+ app.get('/', async (c) => {
49
+ const probes = await options.probes();
50
+ const summary = await collectHealth(probes);
51
+ const body = {
52
+ status: summary.status,
53
+ version: options.version,
54
+ uptimeSeconds: Math.max(0, Math.floor((now() - options.startedAt) / 1000)),
55
+ checks: summary.checks,
56
+ };
57
+ if (summary.status === 'failing') {
58
+ return c.json(body, 503);
59
+ }
60
+ return c.json(body, 200);
61
+ });
62
+
63
+ return app;
64
+ }
65
+
66
+ /**
67
+ * Authed health route. Mounted at `${base}/health/secrets` AFTER the
68
+ * auth middleware so the scope check has a verified token to inspect.
69
+ * Returns the active secrets store + fallback chain + downgrade
70
+ * reason per the secrets capability matrix.
71
+ *
72
+ * @stable
73
+ */
74
+ export function createSecretsHealthRoutes(): Hono<{ Variables: ServerVariables }> {
75
+ const app = new Hono<{ Variables: ServerVariables }>();
76
+
77
+ app.get('/', createScopeMiddleware('secrets:read'), (c) => {
78
+ const status = getSecretsStoreStatus();
79
+ if (status === undefined) {
80
+ return c.json(
81
+ {
82
+ active: 'unknown',
83
+ fallbackChain: [] as ReadonlyArray<string>,
84
+ strictMode: false,
85
+ headlessReasons: [] as ReadonlyArray<string>,
86
+ message: 'Secrets store has not been activated yet.',
87
+ },
88
+ 200,
89
+ );
90
+ }
91
+ return c.json(
92
+ {
93
+ active: status.active,
94
+ fallbackChain: [...status.fallbackChain],
95
+ ...(status.downgradedFrom !== undefined ? { downgradedFrom: status.downgradedFrom } : {}),
96
+ ...(status.downgradeReason !== undefined
97
+ ? { downgradeReason: status.downgradeReason }
98
+ : {}),
99
+ strictMode: status.strictMode,
100
+ headlessReasons: [...status.headlessReasons],
101
+ },
102
+ 200,
103
+ );
104
+ });
105
+
106
+ return app;
107
+ }
108
+
109
+ /**
110
+ * @stable
111
+ */
112
+ export interface MetricsRoutesOptions {
113
+ readonly registry: MetricRegistry;
114
+ readonly requireAuth?: boolean;
115
+ /**
116
+ * Optional refresh callback invoked before every scrape. Use it to
117
+ * sample live signals (WAL size, in-flight runs, daemon status,
118
+ * replay buffer occupancy) into the registry so the scraped output
119
+ * reflects the moment of the request.
120
+ *
121
+ * Refresh failures are swallowed - a broken probe never blocks a
122
+ * Prometheus scrape - and surfaced through the optional `onError`
123
+ * sink.
124
+ */
125
+ readonly refresh?: () => void | Promise<void>;
126
+ readonly onError?: (err: unknown) => void;
127
+ }
128
+
129
+ /**
130
+ * @stable
131
+ */
132
+ export function createMetricsRoutes(
133
+ options: MetricsRoutesOptions,
134
+ ): Hono<{ Variables: ServerVariables }> {
135
+ const app = new Hono<{ Variables: ServerVariables }>();
136
+ const handler = async (c: Context<{ Variables: ServerVariables }>) => {
137
+ if (options.refresh !== undefined) {
138
+ try {
139
+ await options.refresh();
140
+ } catch (err) {
141
+ options.onError?.(err);
142
+ }
143
+ }
144
+ const body = options.registry.render();
145
+ c.header('Content-Type', options.registry.contentType());
146
+ return c.body(body, 200);
147
+ };
148
+ if (options.requireAuth === true) {
149
+ app.get('/', createScopeMiddleware('admin:metrics:read'), handler);
150
+ } else {
151
+ app.get('/', handler);
152
+ }
153
+ return app;
154
+ }
package/src/index.ts ADDED
@@ -0,0 +1,243 @@
1
+ /**
2
+ * `@graphorin/server` - standalone server runtime for the Graphorin
3
+ * framework.
4
+ *
5
+ * Phase 14a entry point. Ships:
6
+ *
7
+ * - {@link createServer} - the single programmatic entry that wires
8
+ * the Hono app, the auth + scope + idempotency + audit middleware
9
+ * stack, REST endpoints (agents / workflows / sessions / memory /
10
+ * skills / mcp / tokens / audit / health), the lifecycle hooks
11
+ * (`beforeStart` / `onReady` / `beforeShutdown` / `onError`), the
12
+ * pre-bind secrets-resolution + storage-migration runner, and the
13
+ * graceful SIGTERM drain.
14
+ * - {@link defineConfig} + {@link parseServerConfig} - typed
15
+ * configuration loader with a Zod-validated schema; missing fields
16
+ * fall back to documented production-ready defaults.
17
+ * - {@link AgentRegistry} + {@link WorkflowRegistry} - read/write
18
+ * registries the route handlers consult to look up user-defined
19
+ * agents / workflows.
20
+ * - {@link RunStateTracker} - in-memory bookkeeping for in-flight
21
+ * runs (run id, status, AbortController). Phase 14b/c promote the
22
+ * tracker to a SQLite-backed durable variant.
23
+ * - The full middleware factory suite - re-exported from
24
+ * `@graphorin/server/middleware` so operators can compose a custom
25
+ * Hono app on top of the same primitives.
26
+ *
27
+ * @packageDocumentation
28
+ */
29
+
30
+ /** Canonical version constant, derived from `package.json` at build time. */
31
+ import pkg from '../package.json' with { type: 'json' };
32
+
33
+ export const VERSION: string = pkg.version;
34
+
35
+ export {
36
+ type CreateServerOptions,
37
+ createServer,
38
+ ensureStoreAuditBinding,
39
+ type GraphorinServer,
40
+ type TriggersDaemonInput,
41
+ type WorkflowTimersInput,
42
+ } from './app.js';
43
+ export {
44
+ createDeliveryCommentarySanitizer,
45
+ DEFAULT_APPLY_TO_EVENTS,
46
+ DEFAULT_DELIVERY_COMMENTARY_PATTERNS,
47
+ type DeliveryCommentaryConfig,
48
+ type DeliveryCommentaryDecision,
49
+ type DeliveryCommentaryPattern,
50
+ type DeliveryCommentaryPolicy,
51
+ type DeliveryCommentaryReason,
52
+ type DeliveryCommentarySanitizer,
53
+ type DeliveryCommentarySink,
54
+ type DeliveryCommentaryTransport,
55
+ } from './commentary/index.js';
56
+ export {
57
+ type DeliveryCommentaryPolicyConfig,
58
+ defineConfig,
59
+ type IdempotencyRequireKeyMode,
60
+ parseServerConfig,
61
+ type SecretRefString,
62
+ type SecretsSource,
63
+ type ServerConfigInput,
64
+ ServerConfigSchema,
65
+ type ServerConfigSpec,
66
+ } from './config.js';
67
+ export {
68
+ type ConsolidatorDaemon,
69
+ type ConsolidatorLike,
70
+ type ConsolidatorStatusLike,
71
+ type CreateConsolidatorDaemonOptions,
72
+ createConsolidatorDaemon,
73
+ } from './consolidator/index.js';
74
+ export * from './errors/index.js';
75
+ export {
76
+ type BaseHealthCheck,
77
+ type ConsolidatorCheck,
78
+ collectHealth,
79
+ createExtendedHealthRoutes,
80
+ createMetricsRoutes,
81
+ createSecretsHealthRoutes,
82
+ type EmbedderCheck,
83
+ type EncryptionCheck,
84
+ type HealthCheck,
85
+ type HealthCheckOptions,
86
+ type HealthChecks,
87
+ type HealthRollup,
88
+ type HealthRouteOptions,
89
+ type HealthStatus,
90
+ type HealthSummary,
91
+ type MetricsRoutesOptions,
92
+ type ReplayBufferCheck,
93
+ type ReplayBufferProbe,
94
+ rollup,
95
+ type SecretsCheck,
96
+ type StorageCheck,
97
+ type TriggersCheck,
98
+ } from './health/index.js';
99
+ export type { RequestToken, ServerRequestState, ServerVariables } from './internal/context.js';
100
+ export {
101
+ type BeforeShutdownContext,
102
+ type BeforeStartContext,
103
+ type LifecycleHooks,
104
+ type OnErrorContext,
105
+ type OnReadyContext,
106
+ type PreBindResult,
107
+ type RunPreBindOptions,
108
+ runPreBind,
109
+ } from './lifecycle/index.js';
110
+ export {
111
+ createServerMetricRegistry,
112
+ type LabelSet,
113
+ type MetricKind,
114
+ MetricRegistry,
115
+ SERVER_METRIC_NAMES,
116
+ } from './metrics/index.js';
117
+ export { syncToolCounters } from './metrics/tools-bridge.js';
118
+ export {
119
+ type AuditErrorSink,
120
+ type AuditMiddlewareOptions,
121
+ type AuthMiddlewareOptions,
122
+ createAuditMiddleware,
123
+ createAuthMiddleware,
124
+ createCorsMiddleware,
125
+ createCsrfMiddleware,
126
+ createIdempotencyMiddleware,
127
+ createRateLimitMiddleware,
128
+ createRequestStateMiddleware,
129
+ createScopeMiddleware,
130
+ HTTP_REQUEST_AUDIT_ACTION,
131
+ type IdempotencyMiddlewareOptions,
132
+ type RequestStateMiddlewareOptions,
133
+ type ScopeRequirement,
134
+ } from './middleware/index.js';
135
+ export {
136
+ type AgentRegistration,
137
+ AgentRegistry,
138
+ type AgentSummary,
139
+ type ServerAgentLike,
140
+ type ServerWorkflowLike,
141
+ type WorkflowRegistration,
142
+ WorkflowRegistry,
143
+ type WorkflowSummary,
144
+ } from './registry/index.js';
145
+ export {
146
+ createReplayRoutes,
147
+ type ReplayApi,
148
+ type ReplayMode,
149
+ type ReplayResponse,
150
+ type ReplayRoutesDeps,
151
+ } from './replay/index.js';
152
+ export {
153
+ type AgentRoutesDeps,
154
+ type AuditApi,
155
+ type AuditRoutesDeps,
156
+ type AuthRoutesDeps,
157
+ createAgentRoutes,
158
+ createAuditRoutes,
159
+ createAuthRoutes,
160
+ createHealthRoutes,
161
+ createMcpRoutes,
162
+ createMemoryRoutes,
163
+ createRunRoutes,
164
+ createSessionRoutes,
165
+ createSkillsRoutes,
166
+ createTokensRoutes,
167
+ createWorkflowRoutes,
168
+ type HealthRoutesDeps,
169
+ type McpApi,
170
+ type McpRoutesDeps,
171
+ type MemoryApi,
172
+ type MemoryRoutesDeps,
173
+ type SessionApi,
174
+ type SessionRoutesDeps,
175
+ type SkillsApi,
176
+ type SkillsRoutesDeps,
177
+ type TokensRoutesDeps,
178
+ type WorkflowRoutesDeps,
179
+ } from './routes/index.js';
180
+ export {
181
+ createConsoleRetentionLog,
182
+ type RetentionConfig,
183
+ type RetentionLog,
184
+ type RetentionLogLevel,
185
+ type RetentionStoreLike,
186
+ type ScheduleRetentionOptions,
187
+ scheduleRetentionSweeps,
188
+ } from './runtime/retention.js';
189
+ export {
190
+ type RunDescriptor,
191
+ type RunHandle,
192
+ type RunStateSnapshot,
193
+ RunStateTracker,
194
+ type RunStatus,
195
+ } from './runtime/run-state.js';
196
+ export { createSseRoutes, type SseRoutesDeps } from './sse/index.js';
197
+ export {
198
+ bridgeToolAuditToAudit,
199
+ type ToolAuditBridge,
200
+ type ToolEventsAuditPolicy,
201
+ toolAuditEventToAuditInput,
202
+ } from './tools-audit-bridge.js';
203
+ export {
204
+ type CreateTriggersDaemonOptions,
205
+ createTriggersDaemon,
206
+ createTriggersRoutes,
207
+ defaultCatchupPolicy,
208
+ type TriggersDaemon,
209
+ type TriggersDaemonMetrics,
210
+ type TriggersDaemonStatus,
211
+ type TriggersRoutesDeps,
212
+ } from './triggers/index.js';
213
+ export {
214
+ type CreateWorkflowTimerDaemonOptions,
215
+ createWorkflowTimerDaemon,
216
+ type WorkflowTimerDaemon,
217
+ type WorkflowTimerDaemonStatus,
218
+ type WorkflowTimerDriverLike,
219
+ } from './workflows/timer-daemon.js';
220
+ export {
221
+ type BareEventFrame,
222
+ createReplayBuffer,
223
+ createWsDispatcher,
224
+ createWsTicketStore,
225
+ createWsUpgradeEvents,
226
+ type ParsedSubject,
227
+ type ReplayBuffer,
228
+ type ReplayBufferOptions,
229
+ type ReplayBufferSlice,
230
+ requiredScopeFor,
231
+ type SubscribeResult,
232
+ tryParseSubject,
233
+ type WsDispatcher,
234
+ type WsDispatcherOptions,
235
+ type WsDispatcherWarning,
236
+ type WsSubscriberHandle,
237
+ type WsSubscriptionSnapshot,
238
+ type WsTicket,
239
+ type WsTicketConsumeResult,
240
+ type WsTicketStore,
241
+ type WsTicketStoreOptions,
242
+ type WsUpgradeOptions,
243
+ } from './ws/index.js';
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Per-request context shape stored on the Hono `c.var` slot. Consumers
3
+ * (route handlers, audit middleware, idempotency middleware) read these
4
+ * fields through type-safe accessors defined in this module.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+
9
+ import type { ParsedScope, VerifiedToken } from '@graphorin/security/auth';
10
+
11
+ /**
12
+ * Discriminator for the request authentication state.
13
+ *
14
+ * @stable
15
+ */
16
+ export type AuthState =
17
+ | { readonly kind: 'unauthenticated' }
18
+ | {
19
+ readonly kind: 'token';
20
+ readonly token: VerifiedToken;
21
+ readonly grantedScopes: ReadonlyArray<ParsedScope>;
22
+ }
23
+ // IP-13: authentication is disabled server-wide (`auth.kind = 'none'`,
24
+ // the documented trusted-loopback / single-operator mode). There is no
25
+ // token, but the request is fully authorized - `grantedScopes` carries
26
+ // `admin:*` so every scope check passes uniformly.
27
+ | {
28
+ readonly kind: 'anonymous';
29
+ readonly grantedScopes: ReadonlyArray<ParsedScope>;
30
+ };
31
+
32
+ /**
33
+ * Request-scoped variables surfaced through `c.var` in Hono. The
34
+ * server's middleware populates these fields incrementally; route
35
+ * handlers consume them through `getRequestState`.
36
+ *
37
+ * @stable
38
+ */
39
+ export interface ServerRequestState {
40
+ readonly requestId: string;
41
+ readonly receivedAt: number;
42
+ readonly clientIp: string | undefined;
43
+ readonly auth: AuthState;
44
+ readonly idempotencyKey?: string;
45
+ readonly idempotencyReplay?: boolean;
46
+ }
47
+
48
+ /**
49
+ * Convenience snapshot of the verified token surfaced on `c.var.token`
50
+ * once the auth middleware has resolved the bearer credential. Mirrors
51
+ * the contract documented in the runtime architecture (Phase 14a §
52
+ * Authentication / authorization middleware: "populates `c.var.token:
53
+ * { id, label, scopes, env }`").
54
+ *
55
+ * Read this through `getRequestToken` so consumers do not have
56
+ * to remember the variable key.
57
+ *
58
+ * @stable
59
+ */
60
+ export interface RequestToken {
61
+ readonly id: string;
62
+ readonly label: string | undefined;
63
+ readonly scopes: ReadonlyArray<ParsedScope>;
64
+ readonly env: string;
65
+ readonly expiresAt: number | undefined;
66
+ }
67
+
68
+ /**
69
+ * Hono variable map. Exported so consumers can type their own
70
+ * middleware against the same surface.
71
+ *
72
+ * @stable
73
+ */
74
+ export interface ServerVariables extends Record<string, unknown> {
75
+ readonly state: ServerRequestState;
76
+ readonly token?: RequestToken;
77
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tiny id helpers shared across middleware + routes.
3
+ *
4
+ * @internal
5
+ */
6
+
7
+ import { randomUUID } from 'node:crypto';
8
+
9
+ /**
10
+ * Generate an opaque per-request id. UUID v4 keeps the surface area
11
+ * minimal - operators that already pin a different scheme (Datadog
12
+ * trace ids, AWS request ids, …) can override the generator on
13
+ * `createServer({ requestIdGenerator })`.
14
+ */
15
+ export function newRequestId(): string {
16
+ return randomUUID();
17
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Helpers for the strict JSON contract every REST endpoint speaks.
3
+ *
4
+ * @internal
5
+ */
6
+
7
+ import { createHash } from 'node:crypto';
8
+
9
+ /**
10
+ * Stable, recursive JSON canonicalisation: object keys sorted
11
+ * lexicographically, arrays preserved as-is. Used by the idempotency
12
+ * middleware so two semantically-identical bodies produce the same
13
+ * fingerprint.
14
+ */
15
+ export function canonicalJson(value: unknown): string {
16
+ return JSON.stringify(canonicalize(value));
17
+ }
18
+
19
+ function canonicalize(value: unknown): unknown {
20
+ if (value === null) return null;
21
+ if (typeof value !== 'object') return value;
22
+ if (Array.isArray(value)) return value.map(canonicalize);
23
+ const entries = Object.entries(value as Record<string, unknown>).sort(([a], [b]) =>
24
+ a < b ? -1 : a > b ? 1 : 0,
25
+ );
26
+ const out: Record<string, unknown> = {};
27
+ for (const [key, val] of entries) out[key] = canonicalize(val);
28
+ return out;
29
+ }
30
+
31
+ /**
32
+ * SHA-256 hex digest of the supplied bytes. Returns the lowercase
33
+ * hex string so it can be compared against the persisted fingerprint
34
+ * without further normalisation.
35
+ */
36
+ export function sha256Hex(input: string | Uint8Array): string {
37
+ return createHash('sha256').update(input).digest('hex');
38
+ }
39
+
40
+ /**
41
+ * SHA-256 fingerprint of a (method, path, body) tuple. The body is
42
+ * canonicalised before hashing so trivial whitespace differences do
43
+ * not register as a payload mismatch.
44
+ */
45
+ export function fingerprintRequest(method: string, path: string, body: unknown): string {
46
+ return sha256Hex(`${method.toUpperCase()} ${path}\n${canonicalJson(body)}`);
47
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * W-052: machine-readable error envelope for the server boundary.
3
+ *
4
+ * The packages disagree on the error discriminator by history:
5
+ * `@graphorin/agent` / `@graphorin/workflow` use `code`,
6
+ * `@graphorin/tools` / `@graphorin/memory` / `@graphorin/provider` /
7
+ * the server's own errors use `kind`. Renaming either family is a
8
+ * breaking change to no benefit - NORMALIZATION IS THE SERVER
9
+ * BOUNDARY'S JOB. Every wire payload that reports a failure funnels
10
+ * through {@link toWireError}, so clients branch on one stable `code`
11
+ * field (e.g. retry `checkpoint-version-conflict`, abandon
12
+ * `node-execution-failed`) instead of parsing English prose.
13
+ *
14
+ * @packageDocumentation
15
+ */
16
+
17
+ /** Normalized error envelope for wire payloads. */
18
+ export interface WireError {
19
+ /** `err.code` if a string, else `err.kind` if a string, else `'unknown'`. */
20
+ readonly code: string;
21
+ readonly message: string;
22
+ /** Operator hint when the source error carries one (`WorkflowError.hint`). */
23
+ readonly hint?: string;
24
+ }
25
+
26
+ /** Normalize any thrown value into the wire envelope (W-052). */
27
+ export function toWireError(err: unknown): WireError {
28
+ if (err !== null && typeof err === 'object') {
29
+ const rec = err as { code?: unknown; kind?: unknown; message?: unknown; hint?: unknown };
30
+ const code =
31
+ typeof rec.code === 'string' && rec.code.length > 0
32
+ ? rec.code
33
+ : typeof rec.kind === 'string' && rec.kind.length > 0
34
+ ? rec.kind
35
+ : 'unknown';
36
+ const message = typeof rec.message === 'string' ? rec.message : String(err);
37
+ return {
38
+ code,
39
+ message,
40
+ ...(typeof rec.hint === 'string' && rec.hint.length > 0 ? { hint: rec.hint } : {}),
41
+ };
42
+ }
43
+ return { code: 'unknown', message: String(err) };
44
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Lifecycle hook contract used by `createServer({...})`. Hooks fire
3
+ * in the documented order:
4
+ *
5
+ * beforeStart -> pre-bind -> migrations -> bind -> onReady
6
+ *
7
+ * On shutdown:
8
+ *
9
+ * SIGTERM -> beforeShutdown -> drain -> onError? -> exit
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+
14
+ import type { ServerConfigSpec } from '../config.js';
15
+
16
+ /**
17
+ * Snapshot passed to {@link LifecycleHooks.beforeStart}.
18
+ *
19
+ * @stable
20
+ */
21
+ export interface BeforeStartContext {
22
+ readonly config: ServerConfigSpec;
23
+ }
24
+
25
+ /**
26
+ * Snapshot passed to {@link LifecycleHooks.onReady}.
27
+ *
28
+ * @stable
29
+ */
30
+ export interface OnReadyContext {
31
+ readonly config: ServerConfigSpec;
32
+ readonly listeningOn: { readonly host: string; readonly port: number };
33
+ }
34
+
35
+ /**
36
+ * Snapshot passed to {@link LifecycleHooks.beforeShutdown}.
37
+ *
38
+ * @stable
39
+ */
40
+ export interface BeforeShutdownContext {
41
+ readonly config: ServerConfigSpec;
42
+ readonly inflight: number;
43
+ readonly drainTimeoutMs: number;
44
+ }
45
+
46
+ /**
47
+ * Snapshot passed to {@link LifecycleHooks.onError}. Errors raised
48
+ * outside the request hot path (lifecycle, audit append, etc.) flow
49
+ * here so operators can fan them into Sentry / Datadog / etc.
50
+ *
51
+ * @stable
52
+ */
53
+ export interface OnErrorContext {
54
+ readonly error: unknown;
55
+ readonly phase: 'beforeStart' | 'onReady' | 'beforeShutdown' | 'request' | 'background';
56
+ }
57
+
58
+ /**
59
+ * @stable
60
+ */
61
+ export interface LifecycleHooks {
62
+ readonly beforeStart?: (ctx: BeforeStartContext) => void | Promise<void>;
63
+ readonly onReady?: (ctx: OnReadyContext) => void | Promise<void>;
64
+ readonly beforeShutdown?: (ctx: BeforeShutdownContext) => void | Promise<void>;
65
+ readonly onError?: (ctx: OnErrorContext) => void | Promise<void>;
66
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Public lifecycle surface for `@graphorin/server`. Re-exports the
3
+ * hook contract + the pre-bind helper so consumers can wire bespoke
4
+ * lifecycles around the standard server.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+
9
+ export type {
10
+ BeforeShutdownContext,
11
+ BeforeStartContext,
12
+ LifecycleHooks,
13
+ OnErrorContext,
14
+ OnReadyContext,
15
+ } from './hooks.js';
16
+ export { type PreBindResult, type RunPreBindOptions, runPreBind } from './pre-bind.js';