@spfn/core 0.2.0-beta.9 → 0.3.0-beta.10
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/LICENSE +1 -1
- package/README.md +693 -279
- package/dist/app-config/index.d.ts +96 -0
- package/dist/app-config/index.js +58 -0
- package/dist/app-config/index.js.map +1 -0
- package/dist/authz/index.d.ts +34 -0
- package/dist/authz/index.js +430 -0
- package/dist/authz/index.js.map +1 -0
- package/dist/boss-BiDCbbAQ.d.ts +337 -0
- package/dist/cache/index.js +42 -30
- package/dist/cache/index.js.map +1 -1
- package/dist/codegen/index.d.ts +121 -13
- package/dist/codegen/index.js +212 -15
- package/dist/codegen/index.js.map +1 -1
- package/dist/config/index.d.ts +576 -30
- package/dist/config/index.js +136 -17
- package/dist/config/index.js.map +1 -1
- package/dist/contract/index.d.ts +221 -0
- package/dist/contract/index.js +558 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/db/index.d.ts +704 -94
- package/dist/db/index.js +1105 -260
- package/dist/db/index.js.map +1 -1
- package/dist/define-middleware-CVKgqo8S.d.ts +167 -0
- package/dist/env/index.d.ts +26 -2
- package/dist/env/index.js +15 -5
- package/dist/env/index.js.map +1 -1
- package/dist/env/loader.d.ts +26 -19
- package/dist/env/loader.js +55 -25
- package/dist/env/loader.js.map +1 -1
- package/dist/errors/index.d.ts +10 -0
- package/dist/errors/index.js +40 -7
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +33 -3
- package/dist/event/index.js +24 -3
- package/dist/event/index.js.map +1 -1
- package/dist/event/sse/client.d.ts +55 -3
- package/dist/event/sse/client.js +164 -45
- package/dist/event/sse/client.js.map +1 -1
- package/dist/event/sse/index.d.ts +12 -5
- package/dist/event/sse/index.js +280 -32
- package/dist/event/sse/index.js.map +1 -1
- package/dist/event/ws/client.d.ts +59 -0
- package/dist/event/ws/client.js +273 -0
- package/dist/event/ws/client.js.map +1 -0
- package/dist/event/ws/index.d.ts +94 -0
- package/dist/event/ws/index.js +272 -0
- package/dist/event/ws/index.js.map +1 -0
- package/dist/job/index.d.ts +48 -3
- package/dist/job/index.js +265 -74
- package/dist/job/index.js.map +1 -1
- package/dist/logger/index.d.ts +5 -0
- package/dist/logger/index.js +14 -12
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.d.ts +352 -10
- package/dist/middleware/index.js +1079 -13
- package/dist/middleware/index.js.map +1 -1
- package/dist/nextjs/index.d.ts +39 -3
- package/dist/nextjs/index.js +82 -29
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/server.d.ts +69 -52
- package/dist/nextjs/server.js +140 -60
- package/dist/nextjs/server.js.map +1 -1
- package/dist/ops/index.d.ts +208 -0
- package/dist/ops/index.js +800 -0
- package/dist/ops/index.js.map +1 -0
- package/dist/route/index.d.ts +10 -694
- package/dist/route/index.js +112 -23
- package/dist/route/index.js.map +1 -1
- package/dist/route-builder-2ani2jEI.d.ts +553 -0
- package/dist/router-DJdpwuB6.d.ts +127 -0
- package/dist/security/index.d.ts +83 -0
- package/dist/security/index.js +173 -0
- package/dist/security/index.js.map +1 -0
- package/dist/server/index.d.ts +619 -31
- package/dist/server/index.js +2583 -360
- package/dist/server/index.js.map +1 -1
- package/dist/token-manager-BT5EnUAR.d.ts +278 -0
- package/dist/types-2AbaW4Ie.d.ts +205 -0
- package/dist/{types-BOPTApC2.d.ts → types-9oszaJqp.d.ts} +7 -2
- package/dist/types-ClQVomgV.d.ts +115 -0
- package/dist/types-ZQODsBft.d.ts +282 -0
- package/docs/file-upload.md +195 -333
- package/package.json +250 -208
- package/src/cache/README.md +330 -0
- package/src/codegen/README.md +516 -0
- package/src/config/README.md +326 -0
- package/src/contract/README.md +326 -0
- package/src/db/README.md +589 -0
- package/src/db/manager/README.md +500 -0
- package/src/db/schema/README.md +354 -0
- package/src/db/transaction/README.md +822 -0
- package/src/env/README.md +651 -0
- package/src/errors/README.md +429 -0
- package/src/event/README.md +741 -0
- package/src/job/README.md +613 -0
- package/src/logger/README.md +321 -0
- package/src/middleware/README.md +634 -0
- package/src/nextjs/README.md +608 -0
- package/src/route/README.md +738 -0
- package/src/security/README.md +100 -0
- package/src/server/README.md +704 -0
- package/dist/boss-DI1r4kTS.d.ts +0 -244
- package/dist/router-Di7ENoah.d.ts +0 -151
- package/dist/types-B-e_f2dQ.d.ts +0 -121
- package/docs/cache.md +0 -133
- package/docs/codegen.md +0 -74
- package/docs/database.md +0 -346
- package/docs/entity.md +0 -539
- package/docs/env.md +0 -477
- package/docs/errors.md +0 -319
- package/docs/event.md +0 -116
- package/docs/job.md +0 -131
- package/docs/logger.md +0 -108
- package/docs/middleware.md +0 -337
- package/docs/nextjs.md +0 -241
- package/docs/repository.md +0 -496
- package/docs/route.md +0 -497
- package/docs/server.md +0 -307
package/dist/server/index.d.ts
CHANGED
|
@@ -1,26 +1,59 @@
|
|
|
1
|
-
|
|
1
|
+
export { loadEnv } from '../env/loader.js';
|
|
2
|
+
import { Hono, MiddlewareHandler } from 'hono';
|
|
2
3
|
import { cors } from 'hono/cors';
|
|
3
4
|
import { serve } from '@hono/node-server';
|
|
4
5
|
import { NamedMiddleware, Router } from '@spfn/core/route';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import '
|
|
6
|
+
import { OnErrorContext, ProxyGuardConfig, RateLimitOptions } from '@spfn/core/middleware';
|
|
7
|
+
import { SafeFetchPolicy } from '@spfn/core/security';
|
|
8
|
+
import { c as JobRouter, f as BossOptions } from '../boss-BiDCbbAQ.js';
|
|
9
|
+
import { b as EventRouterDef, E as EventDef } from '../token-manager-BT5EnUAR.js';
|
|
10
|
+
import { d as SSEHandlerConfig, e as SSEAuthConfig } from '../types-ZQODsBft.js';
|
|
11
|
+
import { W as WSRouterDef, f as WSHandlerConfig, e as WSMessageHandlers, g as WSAuthConfig } from '../types-2AbaW4Ie.js';
|
|
12
|
+
import { DatabaseProvider, MigrationStatus, MigrationStatusDb } from '@spfn/core/db';
|
|
13
|
+
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
14
|
+
import { Static } from '@sinclair/typebox';
|
|
15
|
+
import { R as RouteDef } from '../route-builder-2ani2jEI.js';
|
|
9
16
|
import 'pg-boss';
|
|
17
|
+
import '../define-middleware-CVKgqo8S.js';
|
|
18
|
+
import 'hono/utils/http-status';
|
|
19
|
+
import '../route/types.js';
|
|
10
20
|
|
|
11
21
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*
|
|
17
|
-
* 3. .env.{NODE_ENV}.local
|
|
18
|
-
* 4. .env.local - Local overrides (gitignored)
|
|
19
|
-
* 5. .env.{NODE_ENV}
|
|
20
|
-
* 6. .env - Defaults
|
|
22
|
+
* @deprecated Use `loadEnv` from '@spfn/core/env/loader' instead.
|
|
23
|
+
* This module will be removed in the next major version.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated Use `loadEnv()` from '@spfn/core/env/loader' instead.
|
|
21
27
|
*/
|
|
22
28
|
declare function loadEnvFiles(): void;
|
|
23
29
|
|
|
30
|
+
/** Stable operation identity used by separately deployed clients. */
|
|
31
|
+
declare const CORE_TIME_OPERATION_ID = "core.time";
|
|
32
|
+
/**
|
|
33
|
+
* Closed response shape for the server-time wire capability.
|
|
34
|
+
*
|
|
35
|
+
* Millisecond timestamps use integers throughout SPFN's external contracts.
|
|
36
|
+
*/
|
|
37
|
+
declare const ServerTimeResponseSchema: _sinclair_typebox.TObject<{
|
|
38
|
+
serverTimeMillis: _sinclair_typebox.TInteger;
|
|
39
|
+
}>;
|
|
40
|
+
type ServerTimeResponse = Static<typeof ServerTimeResponseSchema>;
|
|
41
|
+
/** Injectable source of Unix epoch milliseconds. */
|
|
42
|
+
interface ServerClock {
|
|
43
|
+
now(): number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build the route from a clock so tests and alternate runtimes can supply the
|
|
47
|
+
* epoch source without replacing global time.
|
|
48
|
+
*/
|
|
49
|
+
declare function createCoreTimeRoute(clock?: ServerClock): RouteDef<{}, {}, {
|
|
50
|
+
serverTimeMillis: number;
|
|
51
|
+
}>;
|
|
52
|
+
/** The production route and the public wire-contract source of truth. */
|
|
53
|
+
declare const CORE_TIME_ROUTE: RouteDef<{}, {}, {
|
|
54
|
+
serverTimeMillis: number;
|
|
55
|
+
}>;
|
|
56
|
+
|
|
24
57
|
/**
|
|
25
58
|
* Workflow router interface for @spfn/core integration
|
|
26
59
|
*
|
|
@@ -77,11 +110,86 @@ interface ServerConfig {
|
|
|
77
110
|
* Error handler (default: true)
|
|
78
111
|
*/
|
|
79
112
|
errorHandler?: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Callback invoked when an error occurs (passed to ErrorHandler)
|
|
115
|
+
*
|
|
116
|
+
* Called asynchronously without blocking the response.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* import { createErrorSlackNotifier } from '@spfn/notification/server';
|
|
121
|
+
*
|
|
122
|
+
* middleware: {
|
|
123
|
+
* onError: createErrorSlackNotifier({ minStatusCode: 500 }),
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
onError?: (err: Error, context: OnErrorContext) => Promise<void> | void;
|
|
80
128
|
};
|
|
81
129
|
/**
|
|
82
130
|
* Additional custom middleware
|
|
83
131
|
*/
|
|
84
132
|
use?: MiddlewareHandler[];
|
|
133
|
+
/**
|
|
134
|
+
* Proxy-guard: verify requests came through the trusted Next.js RPC proxy
|
|
135
|
+
* (HMAC signature) and/or an allowed browser origin, then tag `clientType`.
|
|
136
|
+
* Lets the backend reject direct-to-backend calls that bypass the proxy.
|
|
137
|
+
*
|
|
138
|
+
* Disabled by default (`mode: 'off'`). Requires the same `SPFN_PROXY_SECRET`
|
|
139
|
+
* on the proxy and the backend. See PROXY-BACKEND-AUTH-SPEC.md.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* .proxyGuard({ mode: 'strict', allowedOrigins: ['https://app.example.com'] })
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
proxyGuard?: Omit<ProxyGuardConfig, 'nonceStore'> & {
|
|
147
|
+
/**
|
|
148
|
+
* Enable hard replay rejection via a Redis nonce store. Evaluated in BOTH modes
|
|
149
|
+
* (tag observes replays, strict rejects). Requires a cache (CACHE_URL); without
|
|
150
|
+
* one, falls back to the timestamp window. Degrades to the window if the store
|
|
151
|
+
* is briefly unavailable. @default false
|
|
152
|
+
*/
|
|
153
|
+
nonce?: boolean;
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Rate limiting: an optional global default limiter plus named policies.
|
|
157
|
+
*
|
|
158
|
+
* `mode: 'on'` applies `default` to every named-middleware route (opt out
|
|
159
|
+
* with `.skip(['rateLimit'])`); `policies` lets packages tag sensitive routes
|
|
160
|
+
* via `rateLimitPolicy(name, fallback)` while this app tunes the numbers in
|
|
161
|
+
* one place. Backed by the shared cache (CACHE_URL); without a cache it fails
|
|
162
|
+
* open unless `default.failClosed` is set. Disabled by default (`mode: 'off'`).
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* .rateLimit({
|
|
167
|
+
* mode: 'on',
|
|
168
|
+
* default: { limit: 100, windowMs: 60_000 },
|
|
169
|
+
* policies: { 'auth-login': { limit: 5, windowMs: 60_000 } },
|
|
170
|
+
* })
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
rateLimit?: {
|
|
174
|
+
/** 'on' applies the default limiter to every route. @default 'off' */
|
|
175
|
+
mode?: 'off' | 'on';
|
|
176
|
+
/** Default policy applied to all routes when `mode` is 'on'. */
|
|
177
|
+
default?: RateLimitOptions;
|
|
178
|
+
/** Named policies referenced by `rateLimitPolicy(name, fallback)` tags. */
|
|
179
|
+
policies?: Record<string, RateLimitOptions>;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* SSRF policy for outbound requests made via `safeFetch` (`@spfn/core/security`).
|
|
183
|
+
* Sets the process-wide default used by webhook/callback senders. Private and
|
|
184
|
+
* reserved IPs are blocked by default; set `allowHosts` to restrict to a known
|
|
185
|
+
* set of upstreams, or `blockPrivateIps: false` for trusted internal calls.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* .outboundFetch({ allowHosts: ['hooks.slack.com'] })
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
outboundFetch?: SafeFetchPolicy;
|
|
85
193
|
/**
|
|
86
194
|
* Global middlewares with names for route-level skip control
|
|
87
195
|
* Use defineMiddleware() for type-safe middleware definitions
|
|
@@ -126,6 +234,8 @@ interface ServerConfig {
|
|
|
126
234
|
* Jobs defined with job()...handler() style
|
|
127
235
|
* Uses pg-boss for PostgreSQL-based job queue
|
|
128
236
|
*
|
|
237
|
+
* Merged across every `.jobs()` call, so this holds all of them.
|
|
238
|
+
*
|
|
129
239
|
* @example
|
|
130
240
|
* ```typescript
|
|
131
241
|
* import { job, defineJobRouter } from '@spfn/core/job';
|
|
@@ -180,6 +290,30 @@ interface ServerConfig {
|
|
|
180
290
|
*/
|
|
181
291
|
path?: string;
|
|
182
292
|
};
|
|
293
|
+
/**
|
|
294
|
+
* WebSocket router for bidirectional real-time communication
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```typescript
|
|
298
|
+
* import { defineWSRouter } from '@spfn/core/event/ws';
|
|
299
|
+
*
|
|
300
|
+
* export default defineServerConfig()
|
|
301
|
+
* .websockets(wsRouter) // → WS /ws
|
|
302
|
+
* .build();
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
websockets?: WSRouterDef<any, any>;
|
|
306
|
+
/**
|
|
307
|
+
* WebSocket configuration options
|
|
308
|
+
* Only used if websockets router is provided
|
|
309
|
+
*/
|
|
310
|
+
websocketsConfig?: WSHandlerConfig & {
|
|
311
|
+
/**
|
|
312
|
+
* WebSocket endpoint path
|
|
313
|
+
* @default '/ws'
|
|
314
|
+
*/
|
|
315
|
+
path?: string;
|
|
316
|
+
};
|
|
183
317
|
/**
|
|
184
318
|
* Enable debug mode (default: NODE_ENV === 'development')
|
|
185
319
|
*/
|
|
@@ -188,6 +322,13 @@ interface ServerConfig {
|
|
|
188
322
|
* Database configuration
|
|
189
323
|
*/
|
|
190
324
|
database?: {
|
|
325
|
+
/**
|
|
326
|
+
* Externally owned PostgreSQL Drizzle provider.
|
|
327
|
+
*
|
|
328
|
+
* When supplied, SPFN skips DATABASE_URL/postgres.js initialization
|
|
329
|
+
* and closes the provider during graceful shutdown.
|
|
330
|
+
*/
|
|
331
|
+
provider?: DatabaseProvider;
|
|
191
332
|
/**
|
|
192
333
|
* Connection pool configuration
|
|
193
334
|
* Overrides environment variables and defaults
|
|
@@ -299,6 +440,34 @@ interface ServerConfig {
|
|
|
299
440
|
*/
|
|
300
441
|
headers?: number;
|
|
301
442
|
};
|
|
443
|
+
/**
|
|
444
|
+
* Fetch (outbound HTTP) timeout configuration
|
|
445
|
+
* Controls Node.js undici global dispatcher timeouts for fetch() calls
|
|
446
|
+
* Applies to all outbound HTTP requests made via fetch() in this process
|
|
447
|
+
*/
|
|
448
|
+
fetchTimeout?: {
|
|
449
|
+
/**
|
|
450
|
+
* TCP connection timeout in milliseconds
|
|
451
|
+
* Time to establish socket connection to upstream server
|
|
452
|
+
* @default 10000 (10 seconds)
|
|
453
|
+
* @env FETCH_CONNECT_TIMEOUT
|
|
454
|
+
*/
|
|
455
|
+
connect?: number;
|
|
456
|
+
/**
|
|
457
|
+
* Response headers timeout in milliseconds
|
|
458
|
+
* Time to receive complete response headers after request sent
|
|
459
|
+
* @default 300000 (5 minutes)
|
|
460
|
+
* @env FETCH_HEADERS_TIMEOUT
|
|
461
|
+
*/
|
|
462
|
+
headers?: number;
|
|
463
|
+
/**
|
|
464
|
+
* Body data timeout in milliseconds
|
|
465
|
+
* Maximum time between body data chunks from upstream server
|
|
466
|
+
* @default 300000 (5 minutes)
|
|
467
|
+
* @env FETCH_BODY_TIMEOUT
|
|
468
|
+
*/
|
|
469
|
+
body?: number;
|
|
470
|
+
};
|
|
302
471
|
/**
|
|
303
472
|
* Graceful shutdown configuration
|
|
304
473
|
* Controls server shutdown behavior during SIGTERM/SIGINT signals
|
|
@@ -306,16 +475,22 @@ interface ServerConfig {
|
|
|
306
475
|
shutdown?: {
|
|
307
476
|
/**
|
|
308
477
|
* Graceful shutdown timeout in milliseconds
|
|
309
|
-
* Maximum time to wait for
|
|
310
|
-
* After timeout, forces process
|
|
311
|
-
*
|
|
478
|
+
* Maximum time to wait for in-flight operations to drain and resource cleanup
|
|
479
|
+
* After timeout, forces process.exit() before k8s SIGKILL
|
|
480
|
+
*
|
|
481
|
+
* Formula: terminationGracePeriodSeconds - preStopSleep - safetyMargin
|
|
482
|
+
* Default: 300s - 5s - 15s = 280s
|
|
483
|
+
*
|
|
484
|
+
* @default 280000 (280 seconds)
|
|
312
485
|
* @env SHUTDOWN_TIMEOUT
|
|
313
486
|
*/
|
|
314
487
|
timeout?: number;
|
|
315
488
|
};
|
|
316
489
|
/**
|
|
317
490
|
* Health check endpoint configuration
|
|
318
|
-
*
|
|
491
|
+
*
|
|
492
|
+
* The endpoint answers at `/_core/health`, which no app route can take from
|
|
493
|
+
* it. Point Kubernetes probes and load balancers there.
|
|
319
494
|
*/
|
|
320
495
|
healthCheck?: {
|
|
321
496
|
/**
|
|
@@ -325,9 +500,19 @@ interface ServerConfig {
|
|
|
325
500
|
*/
|
|
326
501
|
enabled?: boolean;
|
|
327
502
|
/**
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
503
|
+
* An additional path to answer on, for a deployment whose probe path is
|
|
504
|
+
* fixed somewhere you cannot change it.
|
|
505
|
+
*
|
|
506
|
+
* BREAKING (@spfn/core 0.4): unset, the endpoint answers at
|
|
507
|
+
* `/_core/health` and nowhere else. It used to default to `/health` and
|
|
508
|
+
* swallow any app route declared there. `/health` now belongs to your
|
|
509
|
+
* app; set this to `'/health'` to have the built-in answer there again.
|
|
510
|
+
*
|
|
511
|
+
* The path is registered before app routes, so an app route on the same
|
|
512
|
+
* path will not run and the server says so at boot.
|
|
513
|
+
*
|
|
514
|
+
* @default undefined — `/_core/health` only
|
|
515
|
+
* @example '/health'
|
|
331
516
|
*/
|
|
332
517
|
path?: string;
|
|
333
518
|
/**
|
|
@@ -338,20 +523,56 @@ interface ServerConfig {
|
|
|
338
523
|
*/
|
|
339
524
|
detailed?: boolean;
|
|
340
525
|
};
|
|
526
|
+
/**
|
|
527
|
+
* Server-time capability dependencies.
|
|
528
|
+
*
|
|
529
|
+
* The endpoint is always enabled at `GET /_core/time`. Supplying a clock is
|
|
530
|
+
* primarily a deterministic test seam; production defaults to `Date.now()`.
|
|
531
|
+
*/
|
|
532
|
+
serverTime?: {
|
|
533
|
+
clock?: ServerClock;
|
|
534
|
+
};
|
|
535
|
+
/**
|
|
536
|
+
* Migration boot gate
|
|
537
|
+
*
|
|
538
|
+
* Before serving, the server compares the migrations shipped by installed
|
|
539
|
+
* function packages (and by `src/server/drizzle`) against what the database
|
|
540
|
+
* records as applied, and refuses to start when any are still pending —
|
|
541
|
+
* otherwise the mismatch surfaces only at request time, as an opaque 500.
|
|
542
|
+
*
|
|
543
|
+
* Skipped when the app initializes no database or ships no migrations.
|
|
544
|
+
*/
|
|
545
|
+
migrations?: {
|
|
546
|
+
/**
|
|
547
|
+
* Start anyway when migrations are pending, logging a warning that lists
|
|
548
|
+
* them. The environment equivalent is `SPFN_ALLOW_PENDING_MIGRATIONS=true`;
|
|
549
|
+
* this field wins over it.
|
|
550
|
+
*
|
|
551
|
+
* @default false
|
|
552
|
+
* @env SPFN_ALLOW_PENDING_MIGRATIONS
|
|
553
|
+
*/
|
|
554
|
+
allowPending?: boolean;
|
|
555
|
+
};
|
|
341
556
|
/**
|
|
342
557
|
* Infrastructure initialization control
|
|
343
558
|
* Controls automatic initialization of database and Redis
|
|
344
|
-
*
|
|
559
|
+
*
|
|
560
|
+
* Both are initialized unless set to `false` — the credentials are not
|
|
561
|
+
* sniffed. A server that needs no database must say so, otherwise boot
|
|
562
|
+
* fails with `No database configuration found`. A component set to `false`
|
|
563
|
+
* is reported as `disabled` by the health endpoint and never degrades it.
|
|
564
|
+
*
|
|
565
|
+
* @default Both enabled
|
|
345
566
|
*/
|
|
346
567
|
infrastructure?: {
|
|
347
568
|
/**
|
|
348
569
|
* Enable/disable automatic database initialization
|
|
349
|
-
* @default true
|
|
570
|
+
* @default true
|
|
350
571
|
*/
|
|
351
572
|
database?: boolean;
|
|
352
573
|
/**
|
|
353
574
|
* Enable/disable automatic Redis initialization
|
|
354
|
-
* @default true
|
|
575
|
+
* @default true
|
|
355
576
|
*/
|
|
356
577
|
redis?: boolean;
|
|
357
578
|
};
|
|
@@ -550,6 +771,43 @@ declare module 'hono' {
|
|
|
550
771
|
*/
|
|
551
772
|
declare function createServer(config?: ServerConfig): Promise<Hono>;
|
|
552
773
|
|
|
774
|
+
/**
|
|
775
|
+
* The paths `@spfn/core` registers for itself.
|
|
776
|
+
*
|
|
777
|
+
* Its own module because both `create-server.ts` (which registers them) and
|
|
778
|
+
* `helpers.ts` (which reports them in the startup banner) need them, and
|
|
779
|
+
* `create-server.ts` already imports `helpers.ts` — putting them in either file
|
|
780
|
+
* would close a cycle, which `pnpm check:circular` fails the build on.
|
|
781
|
+
*
|
|
782
|
+
* @module server/namespace
|
|
783
|
+
*/
|
|
784
|
+
/**
|
|
785
|
+
* The path prefix `@spfn/core` registers its own endpoints under.
|
|
786
|
+
*
|
|
787
|
+
* An app declaring a route in here is declaring something it does not own, and
|
|
788
|
+
* the route will not run — core's endpoints are registered before app routes.
|
|
789
|
+
* `@spfn/auth` (`/_auth/`) and ops routes (`/_ops/`) follow the same convention,
|
|
790
|
+
* and neither has ever had a shadowing defect.
|
|
791
|
+
*/
|
|
792
|
+
declare const CORE_NAMESPACE = "/_core";
|
|
793
|
+
/**
|
|
794
|
+
* Where the built-in health endpoint always answers.
|
|
795
|
+
*
|
|
796
|
+
* Point a readiness probe, a Dockerfile `HEALTHCHECK` or an uptime monitor here
|
|
797
|
+
* rather than at `/health`: no app route can claim this path, so the answer does
|
|
798
|
+
* not depend on what the app happens to declare. A probe's path is fixed in
|
|
799
|
+
* places this repository cannot change — a GitOps manifest, a Dockerfile, a load
|
|
800
|
+
* balancer console — and a version bump migrates none of them.
|
|
801
|
+
*/
|
|
802
|
+
declare const CORE_HEALTH_PATH = "/_core/health";
|
|
803
|
+
/**
|
|
804
|
+
* Where clients obtain the server's current Unix epoch in milliseconds.
|
|
805
|
+
*
|
|
806
|
+
* This endpoint is registered before application routes and application auth
|
|
807
|
+
* middleware so a client can call it before it has a proof or session.
|
|
808
|
+
*/
|
|
809
|
+
declare const CORE_TIME_PATH = "/_core/time";
|
|
810
|
+
|
|
553
811
|
/**
|
|
554
812
|
* Start SPFN Server
|
|
555
813
|
*
|
|
@@ -567,6 +825,236 @@ declare function createServer(config?: ServerConfig): Promise<Hono>;
|
|
|
567
825
|
*/
|
|
568
826
|
declare function startServer(config?: ServerConfig): Promise<ServerInstance>;
|
|
569
827
|
|
|
828
|
+
/**
|
|
829
|
+
* Shutdown Manager
|
|
830
|
+
*
|
|
831
|
+
* Manages graceful shutdown with drain behavior.
|
|
832
|
+
* All tracked operations must complete before shutdown proceeds.
|
|
833
|
+
*
|
|
834
|
+
* Features:
|
|
835
|
+
* - Hook registry: Multiple modules can register independent cleanup handlers
|
|
836
|
+
* - Operation tracking: Long-running tasks are awaited during shutdown (drain)
|
|
837
|
+
* - State management: isShuttingDown() for rejecting new work
|
|
838
|
+
*/
|
|
839
|
+
interface ShutdownHookOptions {
|
|
840
|
+
/**
|
|
841
|
+
* Timeout for this hook in milliseconds
|
|
842
|
+
* If the hook exceeds this time, it is skipped and the next hook runs
|
|
843
|
+
* @default 10000 (10s)
|
|
844
|
+
*/
|
|
845
|
+
timeout?: number;
|
|
846
|
+
/**
|
|
847
|
+
* Execution order (lower runs first)
|
|
848
|
+
* @default 100
|
|
849
|
+
*/
|
|
850
|
+
order?: number;
|
|
851
|
+
}
|
|
852
|
+
declare class ShutdownManager {
|
|
853
|
+
private state;
|
|
854
|
+
private hooks;
|
|
855
|
+
private operations;
|
|
856
|
+
private operationCounter;
|
|
857
|
+
/**
|
|
858
|
+
* Register a shutdown hook
|
|
859
|
+
*
|
|
860
|
+
* Hooks run in order during shutdown, after all tracked operations drain.
|
|
861
|
+
* Each hook has its own timeout — failure does not block subsequent hooks.
|
|
862
|
+
*
|
|
863
|
+
* @example
|
|
864
|
+
* shutdown.onShutdown('ai-service', async () => {
|
|
865
|
+
* await aiService.cancelPending();
|
|
866
|
+
* }, { timeout: 30000, order: 10 });
|
|
867
|
+
*/
|
|
868
|
+
onShutdown(name: string, handler: () => Promise<void>, options?: ShutdownHookOptions): void;
|
|
869
|
+
/**
|
|
870
|
+
* Track a long-running operation
|
|
871
|
+
*
|
|
872
|
+
* During shutdown (drain phase), the process waits for ALL tracked
|
|
873
|
+
* operations to complete before proceeding with cleanup.
|
|
874
|
+
*
|
|
875
|
+
* If shutdown has already started, the operation is rejected immediately.
|
|
876
|
+
*
|
|
877
|
+
* @returns The operation result (pass-through)
|
|
878
|
+
*
|
|
879
|
+
* @example
|
|
880
|
+
* const result = await shutdown.trackOperation(
|
|
881
|
+
* 'ai-generate',
|
|
882
|
+
* aiService.generate(prompt)
|
|
883
|
+
* );
|
|
884
|
+
*/
|
|
885
|
+
trackOperation<T>(name: string, operation: Promise<T>): Promise<T>;
|
|
886
|
+
/**
|
|
887
|
+
* Whether the server is shutting down
|
|
888
|
+
*
|
|
889
|
+
* Use this to reject new work early (e.g., return 503 in route handlers).
|
|
890
|
+
*/
|
|
891
|
+
isShuttingDown(): boolean;
|
|
892
|
+
/**
|
|
893
|
+
* Number of currently active tracked operations
|
|
894
|
+
*/
|
|
895
|
+
getActiveOperationCount(): number;
|
|
896
|
+
/**
|
|
897
|
+
* Mark shutdown as started immediately
|
|
898
|
+
*
|
|
899
|
+
* Call this at the very beginning of the shutdown sequence so that:
|
|
900
|
+
* - Health check returns 503 right away
|
|
901
|
+
* - trackOperation() rejects new work
|
|
902
|
+
* - isShuttingDown() returns true
|
|
903
|
+
*/
|
|
904
|
+
beginShutdown(): void;
|
|
905
|
+
/**
|
|
906
|
+
* Execute the full shutdown sequence
|
|
907
|
+
*
|
|
908
|
+
* 1. State → draining (reject new operations)
|
|
909
|
+
* 2. Wait for all tracked operations to complete (drain)
|
|
910
|
+
* 3. Run shutdown hooks in order
|
|
911
|
+
* 4. State → closed
|
|
912
|
+
*
|
|
913
|
+
* @param drainTimeout - Max time to wait for operations to drain (ms)
|
|
914
|
+
*/
|
|
915
|
+
execute(drainTimeout: number): Promise<void>;
|
|
916
|
+
/**
|
|
917
|
+
* Wait for all tracked operations to complete, up to drainTimeout
|
|
918
|
+
*/
|
|
919
|
+
private drain;
|
|
920
|
+
/**
|
|
921
|
+
* Execute registered shutdown hooks in order
|
|
922
|
+
*/
|
|
923
|
+
private executeHooks;
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Get the global ShutdownManager instance
|
|
927
|
+
*
|
|
928
|
+
* Available after server starts. Use this to register shutdown hooks
|
|
929
|
+
* or track long-running operations.
|
|
930
|
+
*
|
|
931
|
+
* @example
|
|
932
|
+
* import { getShutdownManager } from '@spfn/core/server';
|
|
933
|
+
*
|
|
934
|
+
* const shutdown = getShutdownManager();
|
|
935
|
+
*
|
|
936
|
+
* // Register cleanup
|
|
937
|
+
* shutdown.onShutdown('my-service', async () => {
|
|
938
|
+
* await myService.close();
|
|
939
|
+
* });
|
|
940
|
+
*
|
|
941
|
+
* // Track long operation
|
|
942
|
+
* await shutdown.trackOperation('ai-task', longRunningPromise);
|
|
943
|
+
*/
|
|
944
|
+
declare function getShutdownManager(): ShutdownManager;
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Serverless target for SPFN.
|
|
948
|
+
*
|
|
949
|
+
* Produces a listen-free, initialized Hono app for serverless platforms (Vercel,
|
|
950
|
+
* AWS Lambda, Cloudflare) — wrap the result with a hono platform adapter, e.g.
|
|
951
|
+
* `handle(app)` from `hono/vercel`.
|
|
952
|
+
*
|
|
953
|
+
* Unlike {@link startServer} (which serve()s a long-lived process and is the
|
|
954
|
+
* always-on / container path), this:
|
|
955
|
+
* - initializes the database in-handler (startServer welds DB init to serve()),
|
|
956
|
+
* - runs at most once per warm container (memoized),
|
|
957
|
+
* - disables the periodic DB health-check (pointless — and a timer leak — on
|
|
958
|
+
* frozen invocations),
|
|
959
|
+
* - does NOT start the in-process pg-boss worker (it cannot run on a serverless
|
|
960
|
+
* platform); enqueue still works, but nothing drains the queue here,
|
|
961
|
+
* - does NOT run seed/RBAC provisioning per cold start — that moves to a
|
|
962
|
+
* deploy-time step, see {@link provisionInfrastructure}.
|
|
963
|
+
*
|
|
964
|
+
* `startServer()` / `spfn start` (the always-on path) is unchanged by this module.
|
|
965
|
+
*/
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Build — once per warm container — the initialized, listen-free Hono app for a
|
|
969
|
+
* serverless platform. Wrap the result with the platform adapter:
|
|
970
|
+
*
|
|
971
|
+
* ```ts
|
|
972
|
+
* import { handle } from 'hono/vercel';
|
|
973
|
+
* import { createServerlessApp } from '@spfn/core/server';
|
|
974
|
+
* import serverConfig from '@/server/server.config';
|
|
975
|
+
*
|
|
976
|
+
* const handler = async (req: Request) => handle(await createServerlessApp(serverConfig))(req);
|
|
977
|
+
* export const GET = handler;
|
|
978
|
+
* export const POST = handler;
|
|
979
|
+
* ```
|
|
980
|
+
*/
|
|
981
|
+
declare function createServerlessApp(config?: ServerConfig): Promise<Hono>;
|
|
982
|
+
/**
|
|
983
|
+
* Reset the memoized serverless app. Tests only.
|
|
984
|
+
*/
|
|
985
|
+
declare function resetServerlessApp(): void;
|
|
986
|
+
/**
|
|
987
|
+
* Deploy-time provisioning — run ONCE per deploy, not per request.
|
|
988
|
+
*
|
|
989
|
+
* Initializes the database and runs the config's provisioning lifecycle hooks
|
|
990
|
+
* (`beforeInfrastructure` / `afterInfrastructure`, e.g. admin seeding + RBAC init).
|
|
991
|
+
* Intended for a build/deploy step (`spfn provision`), keeping per-cold-start work
|
|
992
|
+
* out of the serverless handler. On always-on targets this is equally useful: it
|
|
993
|
+
* avoids re-seeding on every pod restart / replica.
|
|
994
|
+
*
|
|
995
|
+
* Does NOT start the HTTP server, jobs worker, or health-check.
|
|
996
|
+
*/
|
|
997
|
+
declare function provisionInfrastructure(config?: ServerConfig): Promise<void>;
|
|
998
|
+
|
|
999
|
+
/**
|
|
1000
|
+
* Migration Boot Gate
|
|
1001
|
+
*
|
|
1002
|
+
* A server that boots with pending migrations passes its health check and then
|
|
1003
|
+
* fails every request that touches a missing column, as an opaque 500. The gate
|
|
1004
|
+
* moves that failure to boot, where it is one line to read and one command to
|
|
1005
|
+
* fix.
|
|
1006
|
+
*
|
|
1007
|
+
* The check runs on the database the server already connected to. When no
|
|
1008
|
+
* database was initialized — an app that uses none — there is nothing to check
|
|
1009
|
+
* and boot proceeds. When the database is configured but unreachable,
|
|
1010
|
+
* `initDatabase()` has already failed before the gate runs, so the gate never
|
|
1011
|
+
* turns a database outage into a migration message.
|
|
1012
|
+
*/
|
|
1013
|
+
|
|
1014
|
+
type MigrationSnapshot =
|
|
1015
|
+
/** Checked successfully — `status` carries per-target applied/pending counts. */
|
|
1016
|
+
{
|
|
1017
|
+
state: 'ok';
|
|
1018
|
+
checkedAt: string;
|
|
1019
|
+
status: MigrationStatus;
|
|
1020
|
+
pending: number;
|
|
1021
|
+
}
|
|
1022
|
+
/** Nothing to check: no database in use, or no migrations shipped. */
|
|
1023
|
+
| {
|
|
1024
|
+
state: 'skipped';
|
|
1025
|
+
checkedAt: string;
|
|
1026
|
+
reason: string;
|
|
1027
|
+
}
|
|
1028
|
+
/** Could not check — distinct from "checked and pending". */
|
|
1029
|
+
| {
|
|
1030
|
+
state: 'unavailable';
|
|
1031
|
+
checkedAt: string;
|
|
1032
|
+
reason: string;
|
|
1033
|
+
};
|
|
1034
|
+
/**
|
|
1035
|
+
* Thrown when the gate refuses a boot. Not an HTTP error — it never reaches a
|
|
1036
|
+
* request.
|
|
1037
|
+
*/
|
|
1038
|
+
declare class PendingMigrationsError extends Error {
|
|
1039
|
+
readonly targets: string[];
|
|
1040
|
+
constructor(message: string, targets: string[]);
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Forget the cached snapshot — used by tests and after a manual migration run.
|
|
1044
|
+
*/
|
|
1045
|
+
declare function resetMigrationSnapshot(): void;
|
|
1046
|
+
/**
|
|
1047
|
+
* Current migration snapshot, recomputed at most once per TTL.
|
|
1048
|
+
*
|
|
1049
|
+
* The boot gate seeds it, so the first health probe after startup costs nothing.
|
|
1050
|
+
*/
|
|
1051
|
+
declare function getMigrationSnapshot(options?: {
|
|
1052
|
+
cwd?: string;
|
|
1053
|
+
db?: MigrationStatusDb;
|
|
1054
|
+
force?: boolean;
|
|
1055
|
+
ttlMs?: number;
|
|
1056
|
+
}): Promise<MigrationSnapshot>;
|
|
1057
|
+
|
|
570
1058
|
/**
|
|
571
1059
|
* Server Config Builder
|
|
572
1060
|
*
|
|
@@ -600,12 +1088,47 @@ declare class ServerConfigBuilder {
|
|
|
600
1088
|
* Add named middlewares for route-level skip control
|
|
601
1089
|
*/
|
|
602
1090
|
middlewares(middlewares: ServerConfig['middlewares']): this;
|
|
1091
|
+
/**
|
|
1092
|
+
* Configure proxy-guard (verify trusted-proxy signature + origin → clientType)
|
|
1093
|
+
*/
|
|
1094
|
+
proxyGuard(proxyGuard: ServerConfig['proxyGuard']): this;
|
|
1095
|
+
/**
|
|
1096
|
+
* Configure rate limiting: an optional global default limiter plus the named
|
|
1097
|
+
* policies that `rateLimitPolicy(name, fallback)` tags resolve against.
|
|
1098
|
+
*
|
|
1099
|
+
* @example
|
|
1100
|
+
* ```typescript
|
|
1101
|
+
* .rateLimit({
|
|
1102
|
+
* mode: 'on',
|
|
1103
|
+
* default: { limit: 100, windowMs: 60_000 },
|
|
1104
|
+
* policies: { 'auth-login': { limit: 5, windowMs: 60_000 } },
|
|
1105
|
+
* })
|
|
1106
|
+
* ```
|
|
1107
|
+
*/
|
|
1108
|
+
rateLimit(rateLimit: ServerConfig['rateLimit']): this;
|
|
1109
|
+
/**
|
|
1110
|
+
* Configure the SSRF policy for outbound `safeFetch` calls (webhooks,
|
|
1111
|
+
* callbacks). Private/reserved IPs are blocked by default.
|
|
1112
|
+
*
|
|
1113
|
+
* @example
|
|
1114
|
+
* ```typescript
|
|
1115
|
+
* .outboundFetch({ allowHosts: ['hooks.slack.com'] })
|
|
1116
|
+
* ```
|
|
1117
|
+
*/
|
|
1118
|
+
outboundFetch(outboundFetch: ServerConfig['outboundFetch']): this;
|
|
603
1119
|
/**
|
|
604
1120
|
* Register define-route based router
|
|
605
1121
|
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
608
|
-
*
|
|
1122
|
+
* Router-level middleware (`.use()`) and package routers (`.packages()`) travel
|
|
1123
|
+
* with the router itself and are applied by `registerRoutes` when the routes are
|
|
1124
|
+
* mounted — this method only records which router to mount.
|
|
1125
|
+
*
|
|
1126
|
+
* It deliberately does **not** copy `router._globalMiddlewares` into
|
|
1127
|
+
* `config.middlewares`: that copy used to make `registerRoutes` see the same
|
|
1128
|
+
* middleware twice (once from the config list, once from the router it was
|
|
1129
|
+
* handed) and attach both to every route. Middleware that verifies a JWT survives
|
|
1130
|
+
* running twice; middleware that consumes one-shot state — a nonce replay ledger —
|
|
1131
|
+
* rejects its own request the second time round.
|
|
609
1132
|
*
|
|
610
1133
|
* @example
|
|
611
1134
|
* ```typescript
|
|
@@ -616,7 +1139,7 @@ declare class ServerConfigBuilder {
|
|
|
616
1139
|
* .use([authMiddleware]);
|
|
617
1140
|
*
|
|
618
1141
|
* export default defineServerConfig()
|
|
619
|
-
* .routes(appRouter) //
|
|
1142
|
+
* .routes(appRouter) // .use() middleware applied once, at registration
|
|
620
1143
|
* .build();
|
|
621
1144
|
* ```
|
|
622
1145
|
*/
|
|
@@ -624,6 +1147,20 @@ declare class ServerConfigBuilder {
|
|
|
624
1147
|
/**
|
|
625
1148
|
* Register background jobs router
|
|
626
1149
|
*
|
|
1150
|
+
* May be called once per domain router: repeated calls **merge** into one
|
|
1151
|
+
* router, the way `.routes()` composes. Calling it several times used to
|
|
1152
|
+
* keep only the last router and register nothing else, without warning.
|
|
1153
|
+
*
|
|
1154
|
+
* The pg-boss `config` is per server, so pass it on one call only.
|
|
1155
|
+
*
|
|
1156
|
+
* Two names can collide, and both fail loudly:
|
|
1157
|
+
* - a router **key** present in two calls throws here, at config time;
|
|
1158
|
+
* - two jobs sharing a **`job.name`** (the pg-boss queue name) throw in
|
|
1159
|
+
* `registerJobs()` at boot, before any queue or worker is created.
|
|
1160
|
+
*
|
|
1161
|
+
* @throws if a router key was already registered by an earlier call
|
|
1162
|
+
* @throws if `config` is given when an earlier call already gave one
|
|
1163
|
+
*
|
|
627
1164
|
* @example
|
|
628
1165
|
* ```typescript
|
|
629
1166
|
* import { job, defineJobRouter } from '@spfn/core/job';
|
|
@@ -636,7 +1173,8 @@ declare class ServerConfigBuilder {
|
|
|
636
1173
|
*
|
|
637
1174
|
* export default defineServerConfig()
|
|
638
1175
|
* .routes(appRouter)
|
|
639
|
-
* .jobs(jobRouter)
|
|
1176
|
+
* .jobs(jobRouter, { clearOnStart: isDev }) // config on one call only
|
|
1177
|
+
* .jobs(billingJobRouter) // merged, not replaced
|
|
640
1178
|
* .build();
|
|
641
1179
|
* ```
|
|
642
1180
|
*/
|
|
@@ -669,8 +1207,40 @@ declare class ServerConfigBuilder {
|
|
|
669
1207
|
* .events(eventRouter, { path: '/sse' })
|
|
670
1208
|
* ```
|
|
671
1209
|
*/
|
|
672
|
-
events
|
|
1210
|
+
events<TRouter extends EventRouterDef<any>>(router: TRouter, config?: Omit<SSEHandlerConfig, 'auth'> & {
|
|
673
1211
|
path?: string;
|
|
1212
|
+
auth?: SSEAuthConfig<TRouter>;
|
|
1213
|
+
}): this;
|
|
1214
|
+
/**
|
|
1215
|
+
* Register WebSocket router for bidirectional real-time communication
|
|
1216
|
+
*
|
|
1217
|
+
* Enables type-safe WebSocket connections with:
|
|
1218
|
+
* - Server→client event push (via defineEvent + emit)
|
|
1219
|
+
* - Client→server message handling (via messages in defineWSRouter)
|
|
1220
|
+
*
|
|
1221
|
+
* @example
|
|
1222
|
+
* ```typescript
|
|
1223
|
+
* // src/server/ws.ts
|
|
1224
|
+
* export const wsRouter = defineWSRouter({
|
|
1225
|
+
* events: { userUpdated, notification },
|
|
1226
|
+
* messages: {
|
|
1227
|
+
* ping: ({ ws }) => ws.send('pong', {}),
|
|
1228
|
+
* },
|
|
1229
|
+
* });
|
|
1230
|
+
*
|
|
1231
|
+
* // server.config.ts
|
|
1232
|
+
* export default defineServerConfig()
|
|
1233
|
+
* .websockets(wsRouter) // → WS /ws
|
|
1234
|
+
* .websockets(wsRouter, {
|
|
1235
|
+
* path: '/realtime', // custom path
|
|
1236
|
+
* auth: { enabled: true }, // token authentication
|
|
1237
|
+
* })
|
|
1238
|
+
* .build();
|
|
1239
|
+
* ```
|
|
1240
|
+
*/
|
|
1241
|
+
websockets<TEvents extends Record<string, EventDef<any>>, TMessages extends WSMessageHandlers>(router: WSRouterDef<TEvents, TMessages>, config?: Omit<WSHandlerConfig, 'auth'> & {
|
|
1242
|
+
path?: string;
|
|
1243
|
+
auth?: WSAuthConfig<WSRouterDef<TEvents, TMessages>>;
|
|
674
1244
|
}): this;
|
|
675
1245
|
/**
|
|
676
1246
|
* Enable/disable debug mode
|
|
@@ -692,10 +1262,28 @@ declare class ServerConfigBuilder {
|
|
|
692
1262
|
* Configure health check endpoint
|
|
693
1263
|
*/
|
|
694
1264
|
healthCheck(healthCheck: ServerConfig['healthCheck']): this;
|
|
1265
|
+
/**
|
|
1266
|
+
* Supply the clock used by the built-in `GET /_core/time` capability.
|
|
1267
|
+
* Production servers normally keep the default `Date.now()` clock; this is
|
|
1268
|
+
* exposed so tests can assert an exact wire value without replacing globals.
|
|
1269
|
+
*/
|
|
1270
|
+
serverTime(serverTime: ServerConfig['serverTime']): this;
|
|
695
1271
|
/**
|
|
696
1272
|
* Configure infrastructure initialization
|
|
697
1273
|
*/
|
|
698
1274
|
infrastructure(infrastructure: ServerConfig['infrastructure']): this;
|
|
1275
|
+
/**
|
|
1276
|
+
* Configure the migration boot gate
|
|
1277
|
+
*
|
|
1278
|
+
* @example
|
|
1279
|
+
* ```typescript
|
|
1280
|
+
* // A harness that applies migrations itself, after the server is up
|
|
1281
|
+
* export default defineServerConfig()
|
|
1282
|
+
* .migrations({ allowPending: true })
|
|
1283
|
+
* .build();
|
|
1284
|
+
* ```
|
|
1285
|
+
*/
|
|
1286
|
+
migrations(migrations: ServerConfig['migrations']): this;
|
|
699
1287
|
/**
|
|
700
1288
|
* Register workflow router for workflow orchestration
|
|
701
1289
|
*
|
|
@@ -756,4 +1344,4 @@ declare class ServerConfigBuilder {
|
|
|
756
1344
|
*/
|
|
757
1345
|
declare function defineServerConfig(): ServerConfigBuilder;
|
|
758
1346
|
|
|
759
|
-
export { type AppFactory, type ServerConfig, type ServerInstance, createServer, defineServerConfig, loadEnvFiles, startServer };
|
|
1347
|
+
export { type AppFactory, CORE_HEALTH_PATH, CORE_NAMESPACE, CORE_TIME_OPERATION_ID, CORE_TIME_PATH, CORE_TIME_ROUTE, type MigrationSnapshot, PendingMigrationsError, type ServerClock, type ServerConfig, type ServerInstance, type ServerTimeResponse, ServerTimeResponseSchema, type ShutdownHookOptions, createCoreTimeRoute, createServer, createServerlessApp, defineServerConfig, getMigrationSnapshot, getShutdownManager, loadEnvFiles, provisionInfrastructure, resetMigrationSnapshot, resetServerlessApp, startServer };
|