@spfn/core 0.3.0-beta.3 → 0.3.0-beta.31
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/README.md +304 -12
- package/dist/app-config/index.d.ts +39 -1
- package/dist/app-config/index.js.map +1 -1
- package/dist/authz/index.js +47 -391
- package/dist/authz/index.js.map +1 -1
- package/dist/{boss-D16fO2oG.d.ts → boss-BiDCbbAQ.d.ts} +60 -7
- package/dist/codegen/index.d.ts +157 -16
- package/dist/codegen/index.js +772 -166
- package/dist/codegen/index.js.map +1 -1
- package/dist/config/index.js +4 -4
- package/dist/config/index.js.map +1 -1
- package/dist/contract/index.d.ts +39 -16
- package/dist/contract/index.js +147 -23
- package/dist/contract/index.js.map +1 -1
- package/dist/db/index.d.ts +230 -35
- package/dist/db/index.js +316 -160
- package/dist/db/index.js.map +1 -1
- package/dist/env/index.d.ts +6 -442
- package/dist/env/index.js +13 -16
- package/dist/env/index.js.map +1 -1
- package/dist/env/loader.js +24 -1
- package/dist/env/loader.js.map +1 -1
- package/dist/error-CIZlYJlL.d.ts +157 -0
- package/dist/errors/index.d.ts +18 -1
- package/dist/errors/index.js +47 -391
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +5 -5
- package/dist/event/index.js +202 -3
- package/dist/event/index.js.map +1 -1
- package/dist/event/sse/client.d.ts +18 -4
- package/dist/event/sse/client.js +94 -18
- package/dist/event/sse/client.js.map +1 -1
- package/dist/event/sse/index.d.ts +18 -5
- package/dist/event/sse/index.js +452 -114
- package/dist/event/sse/index.js.map +1 -1
- package/dist/event/ws/client.d.ts +3 -2
- package/dist/event/ws/index.d.ts +4 -3
- package/dist/job/index.d.ts +48 -3
- package/dist/job/index.js +131 -52
- package/dist/job/index.js.map +1 -1
- package/dist/logger/index.js +0 -12
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.js +52 -397
- package/dist/middleware/index.js.map +1 -1
- package/dist/nextjs/index.d.ts +75 -14
- package/dist/nextjs/index.js +200 -84
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/server.d.ts +46 -8
- package/dist/nextjs/server.js +15 -1
- package/dist/nextjs/server.js.map +1 -1
- package/dist/ops/index.d.ts +61 -42
- package/dist/ops/index.js +648 -97
- package/dist/ops/index.js.map +1 -1
- package/dist/registry-C2KQCh0m.d.ts +473 -0
- package/dist/route/index.d.ts +5 -2
- package/dist/route/index.js +107 -28
- package/dist/route/index.js.map +1 -1
- package/dist/route/types.d.ts +7 -1
- package/dist/{router-Cy7rAfmj.d.ts → route-builder-DRW-MSCJ.d.ts} +51 -126
- package/dist/router-C57y-vz1.d.ts +184 -0
- package/dist/router-CX5uVjmU.d.ts +406 -0
- package/dist/server/index.d.ts +130 -7
- package/dist/server/index.js +1174 -164
- package/dist/server/index.js.map +1 -1
- package/dist/state/client.d.ts +206 -0
- package/dist/state/client.js +678 -0
- package/dist/state/client.js.map +1 -0
- package/dist/state/index.d.ts +443 -0
- package/dist/state/index.js +644 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/react.d.ts +49 -0
- package/dist/state/react.js +50 -0
- package/dist/state/react.js.map +1 -0
- package/dist/surface/index.d.ts +115 -0
- package/dist/surface/index.js +1013 -0
- package/dist/surface/index.js.map +1 -0
- package/dist/token-manager-Ckjt2-qq.d.ts +122 -0
- package/dist/{types-CfYVhIQ9.d.ts → types-5Rl11_Nb.d.ts} +43 -4
- package/dist/types-CKH0D3Dy.d.ts +441 -0
- package/dist/{types-ZQODsBft.d.ts → types-CurMab2Q.d.ts} +52 -2
- package/dist/{types-2AbaW4Ie.d.ts → types-DncqXfWN.d.ts} +4 -3
- package/docs/file-upload.md +195 -333
- package/package.json +41 -11
- package/src/cache/README.md +330 -0
- package/src/codegen/README.md +721 -0
- package/src/config/README.md +333 -0
- package/src/contract/README.md +442 -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 +845 -0
- package/src/env/README.md +787 -0
- package/src/errors/README.md +445 -0
- package/src/event/README.md +994 -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 +756 -0
- package/src/route/README.md +763 -0
- package/src/security/README.md +100 -0
- package/src/server/README.md +704 -0
- package/src/state/README.md +806 -0
- package/dist/token-manager-BT5EnUAR.d.ts +0 -278
- package/dist/types-9oszaJqp.d.ts +0 -250
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { TSchema } from '@sinclair/typebox';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Event System Types
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Pub/Sub capable cache interface for multi-instance events
|
|
9
|
+
*/
|
|
10
|
+
interface PubSubCache {
|
|
11
|
+
/**
|
|
12
|
+
* Publish a message to a channel
|
|
13
|
+
*/
|
|
14
|
+
publish(channel: string, message: unknown): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Subscribe to a channel
|
|
17
|
+
*/
|
|
18
|
+
subscribe(channel: string, handler: (message: unknown) => void | Promise<void>): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Event handler function type
|
|
22
|
+
*/
|
|
23
|
+
type EventHandler<TPayload> = (payload: TPayload) => void | Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Options for `EventDef.subscribe`.
|
|
26
|
+
*/
|
|
27
|
+
interface EventSubscribeOptions {
|
|
28
|
+
/**
|
|
29
|
+
* Run only for an `emit` made in this process, never for a delivery the
|
|
30
|
+
* cache transport received from another pod. For a handler whose work must
|
|
31
|
+
* happen once per emit, not once per pod (it would otherwise run on every
|
|
32
|
+
* pod the event is cached to). Without a cache it runs on every `emit`.
|
|
33
|
+
*/
|
|
34
|
+
localOnly?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Job queue sender function type (used by job module)
|
|
38
|
+
*/
|
|
39
|
+
type JobQueueSender = (queueName: string, payload: unknown) => Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* What a contracted event promises to clients compiled separately from the
|
|
42
|
+
* server, written into `contracts/current.json` by `@spfn/core:contract`.
|
|
43
|
+
*
|
|
44
|
+
* The same version markers a route's `.contract()` carries; the payload shape
|
|
45
|
+
* is the event's own schema, so it is not repeated here.
|
|
46
|
+
*/
|
|
47
|
+
interface EventContract {
|
|
48
|
+
/** Contract version the event first appeared in. */
|
|
49
|
+
since: string;
|
|
50
|
+
/** Contract version the event was announced for removal in. */
|
|
51
|
+
deprecatedIn?: string;
|
|
52
|
+
/** Contract version the event was removed in. */
|
|
53
|
+
removedIn?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* What the stream request asked for, copied once at connect.
|
|
57
|
+
*
|
|
58
|
+
* A plain snapshot, never the live Hono context, so a frame filter can read it
|
|
59
|
+
* on every frame long after the request object is gone. Header names are
|
|
60
|
+
* lowercased. It may hold `cookie` and `authorization` values — never log it.
|
|
61
|
+
*/
|
|
62
|
+
interface SubscribeRequest {
|
|
63
|
+
/** Query parameters of the stream URL (`events`, `token` and any the app adds). */
|
|
64
|
+
query: Record<string, string>;
|
|
65
|
+
/** Request headers, names lowercased. */
|
|
66
|
+
headers: Record<string, string>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* What `.canSubscribe()` decides on, once per connection per requested event.
|
|
70
|
+
*
|
|
71
|
+
* `P` is whatever the SSE auth config's `resolvePrincipal` returns — core never
|
|
72
|
+
* knows what a principal is.
|
|
73
|
+
*/
|
|
74
|
+
interface SubscribeContext<P = unknown> {
|
|
75
|
+
/** The subject the one-time token was issued to. */
|
|
76
|
+
subject: string;
|
|
77
|
+
/** The value `resolvePrincipal` returned for this connection, `undefined` without one. */
|
|
78
|
+
principal: P | undefined;
|
|
79
|
+
/** The connect request snapshot. */
|
|
80
|
+
request: SubscribeRequest;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Decides at connect whether a subject may receive an event. `false` or a
|
|
84
|
+
* throw denies the event on that connection; the connection stays open.
|
|
85
|
+
*/
|
|
86
|
+
type SubscribePredicate<P = unknown> = (ctx: SubscribeContext<P>) => boolean | Promise<boolean>;
|
|
87
|
+
/**
|
|
88
|
+
* The frames a connection receives right after `connected`, for its own
|
|
89
|
+
* subject and query. Asked once per connection, after `canSubscribe` allowed
|
|
90
|
+
* the event; a throw or a rejection sends nothing for the event.
|
|
91
|
+
*/
|
|
92
|
+
type InitialProducer<TPayload, P = unknown> = (ctx: SubscribeContext<P>) => TPayload[] | Promise<TPayload[]>;
|
|
93
|
+
/**
|
|
94
|
+
* What a per-event `.filter()` sees on every frame.
|
|
95
|
+
*/
|
|
96
|
+
interface FrameContext {
|
|
97
|
+
/** The connection's subject, `undefined` when the stream runs without auth. */
|
|
98
|
+
subject: string | undefined;
|
|
99
|
+
/** The connect request snapshot. */
|
|
100
|
+
request: SubscribeRequest;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Decides per frame whether one connection receives one payload. Sync: it runs
|
|
104
|
+
* for every connection on every emit, so it matches the payload against what
|
|
105
|
+
* the connection asked for and never reads storage.
|
|
106
|
+
*/
|
|
107
|
+
type FramePredicate<TPayload> = (payload: TPayload, ctx: FrameContext) => boolean;
|
|
108
|
+
/**
|
|
109
|
+
* Which of a subject's connections a `grant` / `revoke` reaches: those whose
|
|
110
|
+
* connect query carries every named field with exactly that value. A
|
|
111
|
+
* connection that declared no such field never matches. Plain JSON, so it
|
|
112
|
+
* travels to other pods unchanged.
|
|
113
|
+
*/
|
|
114
|
+
interface SubscriptionMatch {
|
|
115
|
+
query: Record<string, string>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* How an SSE handler lets `grant` / `revoke` reach its open connections.
|
|
119
|
+
* Each handler registers one per router key of the event.
|
|
120
|
+
*/
|
|
121
|
+
interface SubscriptionControl {
|
|
122
|
+
/** Activate the event on every open connection of `subject` (matching `match`, when given) where it is denied. */
|
|
123
|
+
grant: (subject: string, match?: SubscriptionMatch) => void;
|
|
124
|
+
/** Deactivate the event on every open connection of `subject` (matching `match`, when given) where it is allowed. */
|
|
125
|
+
revoke: (subject: string, match?: SubscriptionMatch) => void;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Event definition interface
|
|
129
|
+
*/
|
|
130
|
+
interface EventDef<TPayload = void> {
|
|
131
|
+
/**
|
|
132
|
+
* Unique event name
|
|
133
|
+
*/
|
|
134
|
+
readonly name: string;
|
|
135
|
+
/**
|
|
136
|
+
* TypeBox payload schema (optional)
|
|
137
|
+
*/
|
|
138
|
+
readonly schema?: TSchema;
|
|
139
|
+
/**
|
|
140
|
+
* The contract declared with `.contract()`, absent when the event is not
|
|
141
|
+
* part of the published contract.
|
|
142
|
+
*
|
|
143
|
+
* Named with an underscore because `.contract()` holds the plain name, as
|
|
144
|
+
* a router's `_contractVersion` sits beside `.contractVersion()`.
|
|
145
|
+
*/
|
|
146
|
+
readonly _contract?: EventContract;
|
|
147
|
+
/**
|
|
148
|
+
* Publish this event in `contracts/current.json`.
|
|
149
|
+
*
|
|
150
|
+
* Sets the contract on this definition and returns it. An event is defined
|
|
151
|
+
* once and shared, so a second call throws instead of one of the two
|
|
152
|
+
* declarations silently winning.
|
|
153
|
+
*/
|
|
154
|
+
contract: (contract: EventContract) => EventDef<TPayload>;
|
|
155
|
+
/**
|
|
156
|
+
* The predicate declared with `.canSubscribe()`, absent when any subject
|
|
157
|
+
* that passes the global `authorize` may subscribe.
|
|
158
|
+
*/
|
|
159
|
+
readonly _canSubscribe?: SubscribePredicate<any>;
|
|
160
|
+
/**
|
|
161
|
+
* Declare who may subscribe to this event over SSE.
|
|
162
|
+
*
|
|
163
|
+
* Runs once per connection, after the global `authorize`, only when the
|
|
164
|
+
* connection has a subject. `P` is the type of the principal the SSE auth
|
|
165
|
+
* config's `resolvePrincipal` returns. Set once: a second call throws.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```typescript
|
|
169
|
+
* export const auditLogged = defineEvent('audit.logged', payload)
|
|
170
|
+
* .canSubscribe<AuthPrincipal>(({ principal }) => principal?.role === 'admin');
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
canSubscribe: <P = unknown>(predicate: SubscribePredicate<P>) => EventDef<TPayload>;
|
|
174
|
+
/**
|
|
175
|
+
* The per-frame predicate declared with `.filter()`.
|
|
176
|
+
*/
|
|
177
|
+
readonly _filter?: FramePredicate<TPayload>;
|
|
178
|
+
/**
|
|
179
|
+
* Declare which frames of this event a connection receives.
|
|
180
|
+
*
|
|
181
|
+
* Runs per frame on every connection subscribed to the event, with or
|
|
182
|
+
* without a subject, beside the global `filter` map (both must pass). Sync.
|
|
183
|
+
* Set once: a second call throws.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* export const runtimeChanged = defineEvent('runtime.changed', payload)
|
|
188
|
+
* .filter((payload, { request }) => payload.workspaceId === request.query.workspaceId);
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
filter: (predicate: FramePredicate<TPayload>) => EventDef<TPayload>;
|
|
192
|
+
/**
|
|
193
|
+
* The producer declared with `.initial()`, absent when a connection
|
|
194
|
+
* receives only the frames emitted after it opened.
|
|
195
|
+
*/
|
|
196
|
+
readonly _initial?: InitialProducer<TPayload, any>;
|
|
197
|
+
/**
|
|
198
|
+
* Declare the frames a connection receives right after `connected`, before
|
|
199
|
+
* any live frame — the event's current value for that connection.
|
|
200
|
+
*
|
|
201
|
+
* Runs once per connection with a subject, for an event `canSubscribe`
|
|
202
|
+
* allowed, with the same context `canSubscribe` receives. Its frames skip
|
|
203
|
+
* the frame filters: they were produced for this connection. A throw sends
|
|
204
|
+
* nothing for the event and leaves it out of `connected.initial`. Never on
|
|
205
|
+
* a `grant`. Set once: a second call throws.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* export const runtimeChanged = defineEvent('runtime.changed', payload)
|
|
210
|
+
* .initial(async ({ request }) => [await readRuntime(request.query.workspaceId)]);
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
initial: <P = unknown>(producer: InitialProducer<TPayload, P>) => EventDef<TPayload>;
|
|
214
|
+
/**
|
|
215
|
+
* Subscribe to this event (in-memory handler). With the cache transport a
|
|
216
|
+
* handler runs on every pod for every emit, unless `{ localOnly: true }`
|
|
217
|
+
* keeps it to emits made in this process.
|
|
218
|
+
*/
|
|
219
|
+
subscribe: (handler: EventHandler<TPayload>, options?: EventSubscribeOptions) => () => void;
|
|
220
|
+
/**
|
|
221
|
+
* Unsubscribe all handlers, `localOnly` ones included
|
|
222
|
+
*/
|
|
223
|
+
unsubscribeAll: () => void;
|
|
224
|
+
/**
|
|
225
|
+
* Emit the event (triggers all subscribers and queued jobs)
|
|
226
|
+
*/
|
|
227
|
+
emit: TPayload extends void ? () => Promise<void> : (payload: TPayload) => Promise<void>;
|
|
228
|
+
/**
|
|
229
|
+
* Activate this event on every open SSE connection of `subject` where it is
|
|
230
|
+
* denied — by `canSubscribe` or `authorize` at connect, or by a `revoke`.
|
|
231
|
+
*
|
|
232
|
+
* Reaches every pod when the cache transport is attached. Never re-runs
|
|
233
|
+
* `canSubscribe`: it is an operator's statement about an open connection.
|
|
234
|
+
* An event the connection never requested cannot be granted.
|
|
235
|
+
*
|
|
236
|
+
* With `match`, only the connections whose connect query carries every
|
|
237
|
+
* field of `match.query` with that value are activated.
|
|
238
|
+
*/
|
|
239
|
+
grant: (subject: string, match?: SubscriptionMatch) => Promise<void>;
|
|
240
|
+
/**
|
|
241
|
+
* Deactivate this event on every open SSE connection of `subject` where it
|
|
242
|
+
* is allowed; the client receives a `subscription.revoked` frame.
|
|
243
|
+
*
|
|
244
|
+
* Reaches every pod when the cache transport is attached. Affects open
|
|
245
|
+
* connections only — the next connect asks `canSubscribe` afresh.
|
|
246
|
+
*
|
|
247
|
+
* With `match`, only the connections whose connect query carries every
|
|
248
|
+
* field of `match.query` with that value are deactivated.
|
|
249
|
+
*/
|
|
250
|
+
revoke: (subject: string, match?: SubscriptionMatch) => Promise<void>;
|
|
251
|
+
/**
|
|
252
|
+
* Enable cache-based pub/sub for multi-instance support
|
|
253
|
+
* Must await before emitting events to ensure subscription is ready
|
|
254
|
+
*/
|
|
255
|
+
useCache: (cache: PubSubCache) => Promise<EventDef<TPayload>>;
|
|
256
|
+
/**
|
|
257
|
+
* Internal: Register a job queue to receive this event
|
|
258
|
+
* Called by job registration system
|
|
259
|
+
*/
|
|
260
|
+
_registerJobQueue: (queueName: string, sender: JobQueueSender) => void;
|
|
261
|
+
/**
|
|
262
|
+
* Internal: Let an SSE handler's connection registry receive `grant` /
|
|
263
|
+
* `revoke`. Called by `createSSEHandler` once per router key; every
|
|
264
|
+
* registered control receives every call. Returns the unregister function.
|
|
265
|
+
*/
|
|
266
|
+
_registerSubscriptionControl: (control: SubscriptionControl) => () => void;
|
|
267
|
+
/**
|
|
268
|
+
* Internal: Drop the cache binding (cache + subscribed flag).
|
|
269
|
+
* Called by the event cache transport on shutdown so a later useCache can
|
|
270
|
+
* rebind a fresh cache in the same process.
|
|
271
|
+
*/
|
|
272
|
+
_resetCache: () => void;
|
|
273
|
+
/**
|
|
274
|
+
* Type inference helper
|
|
275
|
+
*/
|
|
276
|
+
_payload: TPayload;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Infer payload type from EventDef
|
|
280
|
+
*/
|
|
281
|
+
type InferEventPayload$1<TEvent> = TEvent extends EventDef<infer TPayload> ? TPayload : never;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Event Router
|
|
285
|
+
*
|
|
286
|
+
* Type-safe event router for SSE subscription
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```typescript
|
|
290
|
+
* import { defineEvent, defineEventRouter } from '@spfn/core/event';
|
|
291
|
+
* import { Type } from '@sinclair/typebox';
|
|
292
|
+
*
|
|
293
|
+
* const userCreated = defineEvent('user.created', Type.Object({
|
|
294
|
+
* userId: Type.String(),
|
|
295
|
+
* }));
|
|
296
|
+
*
|
|
297
|
+
* const orderPlaced = defineEvent('order.placed', Type.Object({
|
|
298
|
+
* orderId: Type.String(),
|
|
299
|
+
* amount: Type.Number(),
|
|
300
|
+
* }));
|
|
301
|
+
*
|
|
302
|
+
* export const eventRouter = defineEventRouter({
|
|
303
|
+
* userCreated,
|
|
304
|
+
* orderPlaced,
|
|
305
|
+
* });
|
|
306
|
+
*
|
|
307
|
+
* export type EventRouter = typeof eventRouter;
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* How a client reaches the contracted events of a router.
|
|
313
|
+
*
|
|
314
|
+
* Declared on the router because it is the stream's promise, not one event's:
|
|
315
|
+
* `createServer` checks it against `.events()` at boot, so the document cannot
|
|
316
|
+
* describe a path or an auth mode the server does not serve.
|
|
317
|
+
*/
|
|
318
|
+
interface EventRouterContract {
|
|
319
|
+
/**
|
|
320
|
+
* `'tokenExchange'` when `.events()` enables `auth` — the client first posts
|
|
321
|
+
* to the token path and opens the stream with `?token=`. `'none'` otherwise.
|
|
322
|
+
*/
|
|
323
|
+
auth: 'none' | 'tokenExchange';
|
|
324
|
+
/**
|
|
325
|
+
* The stream path `.events()` registers.
|
|
326
|
+
* @default '/events/stream'
|
|
327
|
+
*/
|
|
328
|
+
streamPath?: string;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* What every router of events carries — the SSE event router and the
|
|
332
|
+
* WebSocket router alike.
|
|
333
|
+
*
|
|
334
|
+
* The WebSocket router builds on this rather than on `EventRouterDef`, because
|
|
335
|
+
* only an SSE event router can be contracted.
|
|
336
|
+
*/
|
|
337
|
+
interface EventRouterBase<TEvents extends Record<string, EventDef<any>>> {
|
|
338
|
+
/**
|
|
339
|
+
* Event definitions
|
|
340
|
+
*/
|
|
341
|
+
readonly events: TEvents;
|
|
342
|
+
/**
|
|
343
|
+
* Event names as array
|
|
344
|
+
*/
|
|
345
|
+
readonly eventNames: (keyof TEvents)[];
|
|
346
|
+
/**
|
|
347
|
+
* Type inference helper - payload types by event name
|
|
348
|
+
*/
|
|
349
|
+
readonly _types: {
|
|
350
|
+
[K in keyof TEvents]: TEvents[K]['_payload'];
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Event Router Definition
|
|
355
|
+
*/
|
|
356
|
+
interface EventRouterDef<TEvents extends Record<string, EventDef<any>>> extends EventRouterBase<TEvents> {
|
|
357
|
+
/**
|
|
358
|
+
* The contract declared with `.contract()`, absent when the router declares
|
|
359
|
+
* none. Underscored for the same reason as `EventDef._contract`.
|
|
360
|
+
*/
|
|
361
|
+
readonly _contract?: EventRouterContract;
|
|
362
|
+
/**
|
|
363
|
+
* Declare how a client reaches this router's contracted events.
|
|
364
|
+
*
|
|
365
|
+
* Sets the contract on this router and returns it. A `streamPath` that does
|
|
366
|
+
* not start with `/` throws here, where the typo is.
|
|
367
|
+
*/
|
|
368
|
+
contract: (contract: EventRouterContract) => EventRouterDef<TEvents>;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Infer event names from EventRouter
|
|
372
|
+
*/
|
|
373
|
+
type InferEventNames<T> = T extends EventRouterBase<infer E> ? keyof E & string : never;
|
|
374
|
+
/**
|
|
375
|
+
* Infer payload type for specific event
|
|
376
|
+
*/
|
|
377
|
+
type InferEventPayload<T extends EventRouterBase<any>, K extends InferEventNames<T>> = T['_types'][K];
|
|
378
|
+
/**
|
|
379
|
+
* Infer all event payloads map
|
|
380
|
+
*/
|
|
381
|
+
type InferEventPayloads<T extends EventRouterBase<any>> = T['_types'];
|
|
382
|
+
/**
|
|
383
|
+
* Define an event router for SSE subscription
|
|
384
|
+
*
|
|
385
|
+
* `.contract({ auth })` on the router, together with `.contract({ since })` on
|
|
386
|
+
* its events, publishes those events in `contracts/current.json` — see the
|
|
387
|
+
* contract README.
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```typescript
|
|
391
|
+
* export const eventRouter = defineEventRouter({
|
|
392
|
+
* userCreated,
|
|
393
|
+
* orderPlaced,
|
|
394
|
+
* });
|
|
395
|
+
*
|
|
396
|
+
* // Type inference
|
|
397
|
+
* type Names = InferEventNames<typeof eventRouter>;
|
|
398
|
+
* // 'userCreated' | 'orderPlaced'
|
|
399
|
+
*
|
|
400
|
+
* type Payload = InferEventPayload<typeof eventRouter, 'userCreated'>;
|
|
401
|
+
* // { userId: string }
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
declare function defineEventRouter<TEvents extends Record<string, EventDef<any>>>(events: TEvents): EventRouterDef<TEvents>;
|
|
405
|
+
|
|
406
|
+
export { type EventRouterDef as E, type FrameContext as F, type InferEventNames as I, type JobQueueSender as J, type PubSubCache as P, type SubscribeContext as S, type EventDef as a, type EventContract as b, type EventHandler as c, type EventRouterContract as d, type EventSubscribeOptions as e, type FramePredicate as f, type InferEventPayload$1 as g, type InferEventPayloads as h, type InferEventPayload as i, type InitialProducer as j, type SubscribePredicate as k, type SubscribeRequest as l, type SubscriptionMatch as m, defineEventRouter as n, type EventRouterBase as o };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -5,13 +5,20 @@ import { serve } from '@hono/node-server';
|
|
|
5
5
|
import { NamedMiddleware, Router } from '@spfn/core/route';
|
|
6
6
|
import { OnErrorContext, ProxyGuardConfig, RateLimitOptions } from '@spfn/core/middleware';
|
|
7
7
|
import { SafeFetchPolicy } from '@spfn/core/security';
|
|
8
|
-
import { c as JobRouter,
|
|
9
|
-
import {
|
|
10
|
-
import { d as SSEHandlerConfig, e as SSEAuthConfig } from '../types-
|
|
11
|
-
import { W as WSRouterDef, f as WSHandlerConfig, e as WSMessageHandlers, g as WSAuthConfig } from '../types-
|
|
8
|
+
import { c as JobRouter, f as BossOptions } from '../boss-BiDCbbAQ.js';
|
|
9
|
+
import { E as EventRouterDef, a as EventDef } from '../router-CX5uVjmU.js';
|
|
10
|
+
import { d as SSEHandlerConfig, e as SSEAuthConfig } from '../types-CurMab2Q.js';
|
|
11
|
+
import { W as WSRouterDef, f as WSHandlerConfig, e as WSMessageHandlers, g as WSAuthConfig } from '../types-DncqXfWN.js';
|
|
12
12
|
import { DatabaseProvider, MigrationStatus, MigrationStatusDb } from '@spfn/core/db';
|
|
13
|
-
import '@sinclair/typebox';
|
|
13
|
+
import * as _sinclair_typebox from '@sinclair/typebox';
|
|
14
|
+
import { Static } from '@sinclair/typebox';
|
|
15
|
+
import { R as RouteDef } from '../route-builder-DRW-MSCJ.js';
|
|
16
|
+
import { a as EnvRegistry } from '../registry-C2KQCh0m.js';
|
|
14
17
|
import 'pg-boss';
|
|
18
|
+
import '../token-manager-Ckjt2-qq.js';
|
|
19
|
+
import '../define-middleware-CVKgqo8S.js';
|
|
20
|
+
import 'hono/utils/http-status';
|
|
21
|
+
import '../route/types.js';
|
|
15
22
|
|
|
16
23
|
/**
|
|
17
24
|
* @deprecated Use `loadEnv` from '@spfn/core/env/loader' instead.
|
|
@@ -22,6 +29,33 @@ import 'pg-boss';
|
|
|
22
29
|
*/
|
|
23
30
|
declare function loadEnvFiles(): void;
|
|
24
31
|
|
|
32
|
+
/** Stable operation identity used by separately deployed clients. */
|
|
33
|
+
declare const CORE_TIME_OPERATION_ID = "core.time";
|
|
34
|
+
/**
|
|
35
|
+
* Closed response shape for the server-time wire capability.
|
|
36
|
+
*
|
|
37
|
+
* Millisecond timestamps use integers throughout SPFN's external contracts.
|
|
38
|
+
*/
|
|
39
|
+
declare const ServerTimeResponseSchema: _sinclair_typebox.TObject<{
|
|
40
|
+
serverTimeMillis: _sinclair_typebox.TInteger;
|
|
41
|
+
}>;
|
|
42
|
+
type ServerTimeResponse = Static<typeof ServerTimeResponseSchema>;
|
|
43
|
+
/** Injectable source of Unix epoch milliseconds. */
|
|
44
|
+
interface ServerClock {
|
|
45
|
+
now(): number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the route from a clock so tests and alternate runtimes can supply the
|
|
49
|
+
* epoch source without replacing global time.
|
|
50
|
+
*/
|
|
51
|
+
declare function createCoreTimeRoute(clock?: ServerClock): RouteDef<{}, {}, {
|
|
52
|
+
serverTimeMillis: number;
|
|
53
|
+
}>;
|
|
54
|
+
/** The production route and the public wire-contract source of truth. */
|
|
55
|
+
declare const CORE_TIME_ROUTE: RouteDef<{}, {}, {
|
|
56
|
+
serverTimeMillis: number;
|
|
57
|
+
}>;
|
|
58
|
+
|
|
25
59
|
/**
|
|
26
60
|
* Workflow router interface for @spfn/core integration
|
|
27
61
|
*
|
|
@@ -202,6 +236,8 @@ interface ServerConfig {
|
|
|
202
236
|
* Jobs defined with job()...handler() style
|
|
203
237
|
* Uses pg-boss for PostgreSQL-based job queue
|
|
204
238
|
*
|
|
239
|
+
* Merged across every `.jobs()` call, so this holds all of them.
|
|
240
|
+
*
|
|
205
241
|
* @example
|
|
206
242
|
* ```typescript
|
|
207
243
|
* import { job, defineJobRouter } from '@spfn/core/job';
|
|
@@ -489,6 +525,15 @@ interface ServerConfig {
|
|
|
489
525
|
*/
|
|
490
526
|
detailed?: boolean;
|
|
491
527
|
};
|
|
528
|
+
/**
|
|
529
|
+
* Server-time capability dependencies.
|
|
530
|
+
*
|
|
531
|
+
* The endpoint is always enabled at `GET /_core/time`. Supplying a clock is
|
|
532
|
+
* primarily a deterministic test seam; production defaults to `Date.now()`.
|
|
533
|
+
*/
|
|
534
|
+
serverTime?: {
|
|
535
|
+
clock?: ServerClock;
|
|
536
|
+
};
|
|
492
537
|
/**
|
|
493
538
|
* Migration boot gate
|
|
494
539
|
*
|
|
@@ -510,6 +555,35 @@ interface ServerConfig {
|
|
|
510
555
|
*/
|
|
511
556
|
allowPending?: boolean;
|
|
512
557
|
};
|
|
558
|
+
/**
|
|
559
|
+
* Environment boot check
|
|
560
|
+
*
|
|
561
|
+
* Before serving, the server validates every variable of `@spfn/core`'s
|
|
562
|
+
* registry and of the registries listed here: a required variable must be
|
|
563
|
+
* set, and a set variable must pass its `validator`. On failure it logs
|
|
564
|
+
* each key with the reason — never a value — and refuses to start.
|
|
565
|
+
* `SKIP_ENV_VALIDATION` does not skip this check.
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* ```typescript
|
|
569
|
+
* // src/server/config/env.config.ts
|
|
570
|
+
* export const envSchema = defineEnvSchema({ ... });
|
|
571
|
+
* export const envRegistry = createEnvRegistry(envSchema);
|
|
572
|
+
* export const env = envRegistry.validate();
|
|
573
|
+
*
|
|
574
|
+
* // src/server/server.config.ts
|
|
575
|
+
* export default defineServerConfig()
|
|
576
|
+
* .env({ registries: [envRegistry] })
|
|
577
|
+
* .build();
|
|
578
|
+
* ```
|
|
579
|
+
*/
|
|
580
|
+
env?: {
|
|
581
|
+
/**
|
|
582
|
+
* Registries checked alongside `@spfn/core`'s: the app's own, and any
|
|
583
|
+
* package registry the app wants held to the same gate.
|
|
584
|
+
*/
|
|
585
|
+
registries?: EnvRegistry<any>[];
|
|
586
|
+
};
|
|
513
587
|
/**
|
|
514
588
|
* Infrastructure initialization control
|
|
515
589
|
* Controls automatic initialization of database and Redis
|
|
@@ -757,6 +831,13 @@ declare const CORE_NAMESPACE = "/_core";
|
|
|
757
831
|
* balancer console — and a version bump migrates none of them.
|
|
758
832
|
*/
|
|
759
833
|
declare const CORE_HEALTH_PATH = "/_core/health";
|
|
834
|
+
/**
|
|
835
|
+
* Where clients obtain the server's current Unix epoch in milliseconds.
|
|
836
|
+
*
|
|
837
|
+
* This endpoint is registered before application routes and application auth
|
|
838
|
+
* middleware so a client can call it before it has a proof or session.
|
|
839
|
+
*/
|
|
840
|
+
declare const CORE_TIME_PATH = "/_core/time";
|
|
760
841
|
|
|
761
842
|
/**
|
|
762
843
|
* Start SPFN Server
|
|
@@ -768,6 +849,8 @@ declare const CORE_HEALTH_PATH = "/_core/health";
|
|
|
768
849
|
* Start SPFN server
|
|
769
850
|
*
|
|
770
851
|
* Automatically loads server.config.ts if exists
|
|
852
|
+
* Validates the environment (core's registry + `config.env.registries`) and
|
|
853
|
+
* exits with code 1 when a variable is missing or invalid
|
|
771
854
|
* Automatically initializes Database and Redis from environment
|
|
772
855
|
* Sets up graceful shutdown handlers for SIGTERM and SIGINT
|
|
773
856
|
*
|
|
@@ -911,6 +994,9 @@ declare function getShutdownManager(): ShutdownManager;
|
|
|
911
994
|
* - does NOT run seed/RBAC provisioning per cold start — that moves to a
|
|
912
995
|
* deploy-time step, see {@link provisionInfrastructure}.
|
|
913
996
|
*
|
|
997
|
+
* Like {@link startServer}, it runs the environment boot check before building
|
|
998
|
+
* the app; a failure rejects the returned promise instead of exiting.
|
|
999
|
+
*
|
|
914
1000
|
* `startServer()` / `spfn start` (the always-on path) is unchanged by this module.
|
|
915
1001
|
*/
|
|
916
1002
|
|
|
@@ -1097,6 +1183,20 @@ declare class ServerConfigBuilder {
|
|
|
1097
1183
|
/**
|
|
1098
1184
|
* Register background jobs router
|
|
1099
1185
|
*
|
|
1186
|
+
* May be called once per domain router: repeated calls **merge** into one
|
|
1187
|
+
* router, the way `.routes()` composes. Calling it several times used to
|
|
1188
|
+
* keep only the last router and register nothing else, without warning.
|
|
1189
|
+
*
|
|
1190
|
+
* The pg-boss `config` is per server, so pass it on one call only.
|
|
1191
|
+
*
|
|
1192
|
+
* Two names can collide, and both fail loudly:
|
|
1193
|
+
* - a router **key** present in two calls throws here, at config time;
|
|
1194
|
+
* - two jobs sharing a **`job.name`** (the pg-boss queue name) throw in
|
|
1195
|
+
* `registerJobs()` at boot, before any queue or worker is created.
|
|
1196
|
+
*
|
|
1197
|
+
* @throws if a router key was already registered by an earlier call
|
|
1198
|
+
* @throws if `config` is given when an earlier call already gave one
|
|
1199
|
+
*
|
|
1100
1200
|
* @example
|
|
1101
1201
|
* ```typescript
|
|
1102
1202
|
* import { job, defineJobRouter } from '@spfn/core/job';
|
|
@@ -1109,7 +1209,8 @@ declare class ServerConfigBuilder {
|
|
|
1109
1209
|
*
|
|
1110
1210
|
* export default defineServerConfig()
|
|
1111
1211
|
* .routes(appRouter)
|
|
1112
|
-
* .jobs(jobRouter)
|
|
1212
|
+
* .jobs(jobRouter, { clearOnStart: isDev }) // config on one call only
|
|
1213
|
+
* .jobs(billingJobRouter) // merged, not replaced
|
|
1113
1214
|
* .build();
|
|
1114
1215
|
* ```
|
|
1115
1216
|
*/
|
|
@@ -1197,6 +1298,12 @@ declare class ServerConfigBuilder {
|
|
|
1197
1298
|
* Configure health check endpoint
|
|
1198
1299
|
*/
|
|
1199
1300
|
healthCheck(healthCheck: ServerConfig['healthCheck']): this;
|
|
1301
|
+
/**
|
|
1302
|
+
* Supply the clock used by the built-in `GET /_core/time` capability.
|
|
1303
|
+
* Production servers normally keep the default `Date.now()` clock; this is
|
|
1304
|
+
* exposed so tests can assert an exact wire value without replacing globals.
|
|
1305
|
+
*/
|
|
1306
|
+
serverTime(serverTime: ServerConfig['serverTime']): this;
|
|
1200
1307
|
/**
|
|
1201
1308
|
* Configure infrastructure initialization
|
|
1202
1309
|
*/
|
|
@@ -1213,6 +1320,22 @@ declare class ServerConfigBuilder {
|
|
|
1213
1320
|
* ```
|
|
1214
1321
|
*/
|
|
1215
1322
|
migrations(migrations: ServerConfig['migrations']): this;
|
|
1323
|
+
/**
|
|
1324
|
+
* Hand the app's env registries to the boot check
|
|
1325
|
+
*
|
|
1326
|
+
* `@spfn/core`'s own registry is always checked; the registries given here
|
|
1327
|
+
* are checked with it, before the server listens.
|
|
1328
|
+
*
|
|
1329
|
+
* @example
|
|
1330
|
+
* ```typescript
|
|
1331
|
+
* import { envRegistry } from '@/server/config/env.config';
|
|
1332
|
+
*
|
|
1333
|
+
* export default defineServerConfig()
|
|
1334
|
+
* .env({ registries: [envRegistry] })
|
|
1335
|
+
* .build();
|
|
1336
|
+
* ```
|
|
1337
|
+
*/
|
|
1338
|
+
env(env: ServerConfig['env']): this;
|
|
1216
1339
|
/**
|
|
1217
1340
|
* Register workflow router for workflow orchestration
|
|
1218
1341
|
*
|
|
@@ -1273,4 +1396,4 @@ declare class ServerConfigBuilder {
|
|
|
1273
1396
|
*/
|
|
1274
1397
|
declare function defineServerConfig(): ServerConfigBuilder;
|
|
1275
1398
|
|
|
1276
|
-
export { type AppFactory, CORE_HEALTH_PATH, CORE_NAMESPACE, type MigrationSnapshot, PendingMigrationsError, type ServerConfig, type ServerInstance, type ShutdownHookOptions, createServer, createServerlessApp, defineServerConfig, getMigrationSnapshot, getShutdownManager, loadEnvFiles, provisionInfrastructure, resetMigrationSnapshot, resetServerlessApp, startServer };
|
|
1399
|
+
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 };
|