@spfn/core 0.2.0-beta.8 → 0.3.0-beta.1

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 (95) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +444 -305
  3. package/dist/authz/index.d.ts +34 -0
  4. package/dist/authz/index.js +810 -0
  5. package/dist/authz/index.js.map +1 -0
  6. package/dist/{boss-DI1r4kTS.d.ts → boss-D16fO2oG.d.ts} +41 -1
  7. package/dist/cache/index.js +42 -30
  8. package/dist/cache/index.js.map +1 -1
  9. package/dist/codegen/index.d.ts +121 -13
  10. package/dist/codegen/index.js +212 -15
  11. package/dist/codegen/index.js.map +1 -1
  12. package/dist/config/index.d.ts +615 -6
  13. package/dist/config/index.js +124 -5
  14. package/dist/config/index.js.map +1 -1
  15. package/dist/contract/index.d.ts +220 -0
  16. package/dist/contract/index.js +558 -0
  17. package/dist/contract/index.js.map +1 -0
  18. package/dist/db/index.d.ts +528 -85
  19. package/dist/db/index.js +831 -122
  20. package/dist/db/index.js.map +1 -1
  21. package/dist/define-middleware-DfDP39Nq.d.ts +167 -0
  22. package/dist/env/index.d.ts +26 -2
  23. package/dist/env/index.js +15 -5
  24. package/dist/env/index.js.map +1 -1
  25. package/dist/env/loader.d.ts +26 -19
  26. package/dist/env/loader.js +32 -25
  27. package/dist/env/loader.js.map +1 -1
  28. package/dist/errors/index.d.ts +10 -0
  29. package/dist/errors/index.js +418 -5
  30. package/dist/errors/index.js.map +1 -1
  31. package/dist/event/index.d.ts +33 -3
  32. package/dist/event/index.js +24 -3
  33. package/dist/event/index.js.map +1 -1
  34. package/dist/event/sse/client.d.ts +42 -3
  35. package/dist/event/sse/client.js +128 -45
  36. package/dist/event/sse/client.js.map +1 -1
  37. package/dist/event/sse/index.d.ts +12 -5
  38. package/dist/event/sse/index.js +280 -32
  39. package/dist/event/sse/index.js.map +1 -1
  40. package/dist/event/ws/client.d.ts +59 -0
  41. package/dist/event/ws/client.js +273 -0
  42. package/dist/event/ws/client.js.map +1 -0
  43. package/dist/event/ws/index.d.ts +94 -0
  44. package/dist/event/ws/index.js +272 -0
  45. package/dist/event/ws/index.js.map +1 -0
  46. package/dist/job/index.d.ts +2 -2
  47. package/dist/job/index.js +155 -42
  48. package/dist/job/index.js.map +1 -1
  49. package/dist/logger/index.d.ts +5 -0
  50. package/dist/logger/index.js +14 -0
  51. package/dist/logger/index.js.map +1 -1
  52. package/dist/middleware/index.d.ts +347 -9
  53. package/dist/middleware/index.js +1462 -15
  54. package/dist/middleware/index.js.map +1 -1
  55. package/dist/nextjs/index.d.ts +2 -2
  56. package/dist/nextjs/index.js +42 -28
  57. package/dist/nextjs/index.js.map +1 -1
  58. package/dist/nextjs/server.d.ts +35 -51
  59. package/dist/nextjs/server.js +126 -60
  60. package/dist/nextjs/server.js.map +1 -1
  61. package/dist/ops/index.d.ts +107 -0
  62. package/dist/ops/index.js +476 -0
  63. package/dist/ops/index.js.map +1 -0
  64. package/dist/route/index.d.ts +8 -694
  65. package/dist/route/index.js +111 -22
  66. package/dist/route/index.js.map +1 -1
  67. package/dist/router-ukNdAZcN.d.ts +676 -0
  68. package/dist/security/index.d.ts +83 -0
  69. package/dist/security/index.js +173 -0
  70. package/dist/security/index.js.map +1 -0
  71. package/dist/server/index.d.ts +491 -22
  72. package/dist/server/index.js +1887 -308
  73. package/dist/server/index.js.map +1 -1
  74. package/dist/token-manager-BT5EnUAR.d.ts +278 -0
  75. package/dist/types-2AbaW4Ie.d.ts +205 -0
  76. package/dist/{types-BOPTApC2.d.ts → types-9oszaJqp.d.ts} +7 -2
  77. package/dist/types-Bvvig_tT.d.ts +115 -0
  78. package/dist/types-ZQODsBft.d.ts +282 -0
  79. package/package.json +244 -208
  80. package/dist/router-Di7ENoah.d.ts +0 -151
  81. package/dist/types-B-e_f2dQ.d.ts +0 -121
  82. package/docs/cache.md +0 -133
  83. package/docs/codegen.md +0 -74
  84. package/docs/database.md +0 -346
  85. package/docs/entity.md +0 -539
  86. package/docs/env.md +0 -477
  87. package/docs/errors.md +0 -319
  88. package/docs/event.md +0 -116
  89. package/docs/job.md +0 -131
  90. package/docs/logger.md +0 -108
  91. package/docs/middleware.md +0 -337
  92. package/docs/nextjs.md +0 -241
  93. package/docs/repository.md +0 -496
  94. package/docs/route.md +0 -497
  95. package/docs/server.md +0 -307
@@ -0,0 +1,278 @@
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
+ * Job queue sender function type (used by job module)
26
+ */
27
+ type JobQueueSender = (queueName: string, payload: unknown) => Promise<void>;
28
+ /**
29
+ * Event definition interface
30
+ */
31
+ interface EventDef<TPayload = void> {
32
+ /**
33
+ * Unique event name
34
+ */
35
+ readonly name: string;
36
+ /**
37
+ * TypeBox payload schema (optional)
38
+ */
39
+ readonly schema?: TSchema;
40
+ /**
41
+ * Subscribe to this event (in-memory handler)
42
+ */
43
+ subscribe: (handler: EventHandler<TPayload>) => () => void;
44
+ /**
45
+ * Unsubscribe all handlers
46
+ */
47
+ unsubscribeAll: () => void;
48
+ /**
49
+ * Emit the event (triggers all subscribers and queued jobs)
50
+ */
51
+ emit: TPayload extends void ? () => Promise<void> : (payload: TPayload) => Promise<void>;
52
+ /**
53
+ * Enable cache-based pub/sub for multi-instance support
54
+ * Must await before emitting events to ensure subscription is ready
55
+ */
56
+ useCache: (cache: PubSubCache) => Promise<EventDef<TPayload>>;
57
+ /**
58
+ * Internal: Register a job queue to receive this event
59
+ * Called by job registration system
60
+ */
61
+ _registerJobQueue: (queueName: string, sender: JobQueueSender) => void;
62
+ /**
63
+ * Internal: Drop the cache binding (cache + subscribed flag).
64
+ * Called by the event cache transport on shutdown so a later useCache can
65
+ * rebind a fresh cache in the same process.
66
+ */
67
+ _resetCache: () => void;
68
+ /**
69
+ * Type inference helper
70
+ */
71
+ _payload: TPayload;
72
+ }
73
+ /**
74
+ * Infer payload type from EventDef
75
+ */
76
+ type InferEventPayload$1<TEvent> = TEvent extends EventDef<infer TPayload> ? TPayload : never;
77
+
78
+ /**
79
+ * Event Router
80
+ *
81
+ * Type-safe event router for SSE subscription
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * import { defineEvent, defineEventRouter } from '@spfn/core/event';
86
+ * import { Type } from '@sinclair/typebox';
87
+ *
88
+ * const userCreated = defineEvent('user.created', Type.Object({
89
+ * userId: Type.String(),
90
+ * }));
91
+ *
92
+ * const orderPlaced = defineEvent('order.placed', Type.Object({
93
+ * orderId: Type.String(),
94
+ * amount: Type.Number(),
95
+ * }));
96
+ *
97
+ * export const eventRouter = defineEventRouter({
98
+ * userCreated,
99
+ * orderPlaced,
100
+ * });
101
+ *
102
+ * export type EventRouter = typeof eventRouter;
103
+ * ```
104
+ */
105
+
106
+ /**
107
+ * Event Router Definition
108
+ */
109
+ interface EventRouterDef<TEvents extends Record<string, EventDef<any>>> {
110
+ /**
111
+ * Event definitions
112
+ */
113
+ readonly events: TEvents;
114
+ /**
115
+ * Event names as array
116
+ */
117
+ readonly eventNames: (keyof TEvents)[];
118
+ /**
119
+ * Type inference helper - payload types by event name
120
+ */
121
+ readonly _types: {
122
+ [K in keyof TEvents]: TEvents[K]['_payload'];
123
+ };
124
+ }
125
+ /**
126
+ * Infer event names from EventRouter
127
+ */
128
+ type InferEventNames<T> = T extends EventRouterDef<infer E> ? keyof E & string : never;
129
+ /**
130
+ * Infer payload type for specific event
131
+ */
132
+ type InferEventPayload<T extends EventRouterDef<any>, K extends InferEventNames<T>> = T['_types'][K];
133
+ /**
134
+ * Infer all event payloads map
135
+ */
136
+ type InferEventPayloads<T extends EventRouterDef<any>> = T['_types'];
137
+ /**
138
+ * Define an event router for SSE subscription
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * export const eventRouter = defineEventRouter({
143
+ * userCreated,
144
+ * orderPlaced,
145
+ * });
146
+ *
147
+ * // Type inference
148
+ * type Names = InferEventNames<typeof eventRouter>;
149
+ * // 'userCreated' | 'orderPlaced'
150
+ *
151
+ * type Payload = InferEventPayload<typeof eventRouter, 'userCreated'>;
152
+ * // { userId: string }
153
+ * ```
154
+ */
155
+ declare function defineEventRouter<TEvents extends Record<string, EventDef<any>>>(events: TEvents): EventRouterDef<TEvents>;
156
+
157
+ /**
158
+ * SSE Token Manager
159
+ *
160
+ * Auth-agnostic token issuance and verification for SSE connections.
161
+ * Issues one-time-use tokens with TTL for Token Exchange pattern.
162
+ *
163
+ * @example
164
+ * ```typescript
165
+ * const manager = new SSETokenManager({ ttl: 30000 });
166
+ *
167
+ * // Issue token for authenticated user
168
+ * const token = await manager.issue('user-123');
169
+ *
170
+ * // Verify and consume token (one-time use)
171
+ * const subject = await manager.verify(token); // 'user-123'
172
+ * const again = await manager.verify(token); // null (already consumed)
173
+ *
174
+ * // Cleanup on shutdown
175
+ * manager.destroy();
176
+ * ```
177
+ */
178
+ /**
179
+ * Minimal cache client interface (compatible with ioredis Redis | Cluster)
180
+ */
181
+ type CacheClient = {
182
+ set(key: string, value: string, ...args: any[]): Promise<any>;
183
+ getdel?(key: string): Promise<string | null>;
184
+ get(key: string): Promise<string | null>;
185
+ del(...keys: string[]): Promise<number>;
186
+ };
187
+ /**
188
+ * Stored SSE token data
189
+ */
190
+ interface SSEToken {
191
+ subject: string;
192
+ expiresAt: number;
193
+ }
194
+ /**
195
+ * Token storage interface
196
+ *
197
+ * Implement this for custom storage backends (e.g., Redis for multi-instance).
198
+ */
199
+ interface SSETokenStore {
200
+ /** Store a token */
201
+ set(token: string, data: SSEToken): Promise<void>;
202
+ /** Get and delete a token (one-time use) */
203
+ consume(token: string): Promise<SSEToken | null>;
204
+ /** Remove expired tokens */
205
+ cleanup(): Promise<void>;
206
+ }
207
+ /**
208
+ * SSETokenManager configuration
209
+ */
210
+ interface SSETokenManagerConfig {
211
+ /**
212
+ * Token time-to-live in milliseconds
213
+ * @default 30000
214
+ */
215
+ ttl?: number;
216
+ /**
217
+ * Custom token store (default: in-memory Map)
218
+ */
219
+ store?: SSETokenStore;
220
+ /**
221
+ * Cleanup interval in milliseconds
222
+ * @default 60000
223
+ */
224
+ cleanupInterval?: number;
225
+ }
226
+ /**
227
+ * Redis/Valkey-backed token store for multi-instance deployments.
228
+ *
229
+ * Uses SET EX for automatic TTL expiry and GETDEL for atomic one-time consumption.
230
+ * No cleanup needed — Redis handles expiration automatically.
231
+ *
232
+ * @example
233
+ * ```typescript
234
+ * import { getCache } from '@spfn/core/cache';
235
+ *
236
+ * const cache = getCache();
237
+ * if (cache) {
238
+ * const store = new CacheTokenStore(cache);
239
+ * const manager = new SSETokenManager({ store });
240
+ * }
241
+ * ```
242
+ */
243
+ declare class CacheTokenStore implements SSETokenStore {
244
+ private cache;
245
+ private prefix;
246
+ constructor(cache: CacheClient);
247
+ set(token: string, data: SSEToken): Promise<void>;
248
+ consume(token: string): Promise<SSEToken | null>;
249
+ cleanup(): Promise<void>;
250
+ }
251
+ declare class SSETokenManager {
252
+ private store;
253
+ private ttl;
254
+ private cleanupTimer;
255
+ constructor(config?: SSETokenManagerConfig);
256
+ /**
257
+ * How long an issued token stays valid, in milliseconds.
258
+ *
259
+ * Exposed so a caller that has to report an expiry to its client computes the same
260
+ * number this manager stamps on the token, instead of assuming the default.
261
+ */
262
+ get ttlMs(): number;
263
+ /**
264
+ * Issue a new one-time-use token for the given subject
265
+ */
266
+ issue(subject: string): Promise<string>;
267
+ /**
268
+ * Verify and consume a token
269
+ * @returns subject string if valid, null if invalid/expired/already consumed
270
+ */
271
+ verify(token: string): Promise<string | null>;
272
+ /**
273
+ * Cleanup timer and resources
274
+ */
275
+ destroy(): void;
276
+ }
277
+
278
+ export { CacheTokenStore as C, type EventDef as E, type InferEventNames as I, type JobQueueSender as J, type PubSubCache as P, SSETokenManager as S, type EventHandler as a, type EventRouterDef as b, type InferEventPayload$1 as c, type InferEventPayloads as d, type InferEventPayload as e, defineEventRouter as f, type SSEToken as g, type SSETokenManagerConfig as h, type SSETokenStore as i };
@@ -0,0 +1,205 @@
1
+ import { Context } from 'hono';
2
+ import { E as EventDef, b as EventRouterDef, I as InferEventNames, e as InferEventPayload, i as SSETokenStore, S as SSETokenManager } from './token-manager-BT5EnUAR.js';
3
+
4
+ /**
5
+ * WebSocket Types
6
+ */
7
+
8
+ /**
9
+ * WebSocket Router Definition
10
+ *
11
+ * Extends EventRouterDef with client→server message handlers.
12
+ */
13
+ interface WSRouterDef<TEvents extends Record<string, EventDef<any>>, TMessages extends WSMessageHandlers = WSMessageHandlers> extends EventRouterDef<TEvents> {
14
+ messages: TMessages;
15
+ }
16
+ /**
17
+ * Low-level WS connection handle passed to message handlers
18
+ */
19
+ interface WSRawConnection {
20
+ send(type: string, payload: unknown): void;
21
+ close(code?: number, reason?: string): void;
22
+ }
23
+ /**
24
+ * Context passed to each client→server message handler
25
+ */
26
+ interface WSMessageContext<TPayload = unknown> {
27
+ payload: TPayload;
28
+ subject?: string;
29
+ ws: WSRawConnection;
30
+ }
31
+ /**
32
+ * Single message handler function
33
+ */
34
+ type WSMessageHandlerFn<TPayload = unknown> = (ctx: WSMessageContext<TPayload>) => void | Promise<void>;
35
+ /**
36
+ * Map of message type name → handler
37
+ */
38
+ type WSMessageHandlers = Record<string, WSMessageHandlerFn<any>>;
39
+ /**
40
+ * WebSocket auth configuration (internal, non-generic)
41
+ */
42
+ interface WSHandlerAuthConfig {
43
+ enabled?: boolean;
44
+ tokenTtl?: number;
45
+ store?: SSETokenStore;
46
+ tokenManager?: SSETokenManager | (() => SSETokenManager);
47
+ /**
48
+ * Extract subject from Hono context (used on token-issue endpoint)
49
+ * @default (c) => c.get('auth')?.userId ?? null
50
+ */
51
+ getSubject?: (c: Context) => string | null;
52
+ /**
53
+ * Authorize event subscriptions on connect
54
+ * Return allowed events subset. Empty array = 403 rejection.
55
+ */
56
+ authorize?: (subject: string, events: string[]) => Promise<string[]> | string[];
57
+ /**
58
+ * Per-event payload filter (called on every emission)
59
+ * Return false to skip sending the event to this client.
60
+ */
61
+ filter?: Record<string, (subject: string, payload: unknown) => boolean>;
62
+ }
63
+ /**
64
+ * WebSocket auth configuration (user-facing, generic)
65
+ */
66
+ interface WSAuthConfig<TRouter extends WSRouterDef<any, any>> {
67
+ enabled?: boolean;
68
+ tokenTtl?: number;
69
+ store?: SSETokenStore;
70
+ tokenManager?: SSETokenManager | (() => SSETokenManager);
71
+ getSubject?: (c: Context) => string | null;
72
+ authorize?: (subject: string, events: InferEventNames<TRouter>[]) => Promise<InferEventNames<TRouter>[]> | InferEventNames<TRouter>[];
73
+ filter?: {
74
+ [K in InferEventNames<TRouter>]?: (subject: string, payload: InferEventPayload<TRouter, K>) => boolean;
75
+ };
76
+ }
77
+ /**
78
+ * Configuration for the WebSocket server handler
79
+ */
80
+ interface WSHandlerConfig {
81
+ /**
82
+ * Keep-alive ping interval in ms
83
+ * @default 30000
84
+ */
85
+ pingInterval?: number;
86
+ /**
87
+ * Cross-pod event broadcast via the cache (Redis/Valkey) pub/sub.
88
+ *
89
+ * When `true` (default) and a cache is configured (`CACHE_URL`), each event
90
+ * is auto-wired so an `emit` on one pod reaches subscribers on every pod.
91
+ * Without a cache this is a no-op (events stay in-process). Set `false` to
92
+ * force in-process even when a cache is present.
93
+ *
94
+ * @default true
95
+ */
96
+ multiInstance?: boolean;
97
+ /**
98
+ * Pub/sub channel prefix for cross-pod broadcast.
99
+ *
100
+ * Defaults to env `SPFN_SSE_CHANNEL_PREFIX`, else `spfn:sse:`. Use distinct
101
+ * prefixes to isolate apps/tenants that share one Redis instance.
102
+ */
103
+ channelPrefix?: string;
104
+ /**
105
+ * Maximum inbound message size in bytes. Frames larger than this are
106
+ * rejected by the ws library before they are buffered or parsed.
107
+ * @default 1048576 (1 MiB)
108
+ */
109
+ maxPayload?: number;
110
+ /**
111
+ * Backpressure cap: if a connection's outbound buffer (`bufferedAmount`)
112
+ * exceeds this many bytes, the connection is closed (1013) instead of
113
+ * buffering more — a slow consumer cannot drive the process to OOM.
114
+ * @default 1048576 (1 MiB)
115
+ */
116
+ maxBufferedBytes?: number;
117
+ /**
118
+ * Maximum number of concurrent connections accepted by this server. New
119
+ * connections beyond the cap are rejected with close code 1013.
120
+ * @default 10000
121
+ */
122
+ maxConnections?: number;
123
+ /**
124
+ * Maximum concurrent connections per authenticated subject (0 = unlimited).
125
+ * @default 0
126
+ */
127
+ maxConnectionsPerSubject?: number;
128
+ /**
129
+ * Allow-list of `Origin` header values permitted to open a WebSocket. A
130
+ * browser sends Origin on the upgrade, so this blocks cross-site scripts from
131
+ * connecting with the user's ambient cookies (WebSocket has no same-origin
132
+ * policy / CORS of its own). Connections whose Origin isn't listed are closed
133
+ * with 1008. Undefined/empty = no Origin check (default; non-browser clients
134
+ * such as native apps send no Origin and are always allowed).
135
+ *
136
+ * @example ['https://app.example.com', 'https://admin.example.com']
137
+ * @default undefined (disabled)
138
+ */
139
+ allowedOrigins?: string[];
140
+ /**
141
+ * Authentication and authorization configuration
142
+ */
143
+ auth?: WSHandlerAuthConfig;
144
+ }
145
+ /**
146
+ * WebSocket client configuration
147
+ */
148
+ interface WSClientConfig {
149
+ /**
150
+ * Backend API host URL (ws:// or wss://)
151
+ * @default derived from NEXT_PUBLIC_SPFN_API_URL
152
+ */
153
+ host?: string;
154
+ /**
155
+ * WS endpoint pathname
156
+ * @default '/ws'
157
+ */
158
+ pathname?: string;
159
+ /**
160
+ * Auto reconnect on disconnect
161
+ * @default true
162
+ */
163
+ reconnect?: boolean;
164
+ /**
165
+ * Reconnect delay in ms
166
+ * @default 3000
167
+ */
168
+ reconnectDelay?: number;
169
+ /**
170
+ * Maximum reconnect attempts (0 = infinite)
171
+ * @default 0
172
+ */
173
+ maxReconnectAttempts?: number;
174
+ /**
175
+ * Acquire a one-time token before connecting
176
+ */
177
+ acquireToken?: () => Promise<string>;
178
+ }
179
+ /**
180
+ * WebSocket connection state
181
+ */
182
+ type WSConnectionState = 'connecting' | 'open' | 'closed' | 'error';
183
+ /**
184
+ * Event handlers map for WSRouterDef
185
+ */
186
+ type WSEventHandlers<TRouter extends WSRouterDef<any, any>> = {
187
+ [K in InferEventNames<TRouter>]?: (payload: InferEventPayload<TRouter, K>) => void;
188
+ };
189
+ /**
190
+ * Subscribe options
191
+ */
192
+ interface WSSubscribeOptions<TRouter extends WSRouterDef<any, any>> {
193
+ events: InferEventNames<TRouter>[];
194
+ handlers: WSEventHandlers<TRouter>;
195
+ onOpen?: () => void;
196
+ onError?: (error: Event) => void;
197
+ onClose?: () => void;
198
+ onReconnect?: (attempt: number) => void;
199
+ }
200
+ /**
201
+ * Unsubscribe function
202
+ */
203
+ type WSUnsubscribe = () => void;
204
+
205
+ export type { WSRouterDef as W, WSSubscribeOptions as a, WSUnsubscribe as b, WSConnectionState as c, WSClientConfig as d, WSMessageHandlers as e, WSHandlerConfig as f, WSAuthConfig as g, WSEventHandlers as h, WSHandlerAuthConfig as i, WSMessageContext as j, WSMessageHandlerFn as k, WSRawConnection as l };
@@ -159,7 +159,7 @@ interface ApiConfig {
159
159
  /**
160
160
  * Request timeout in milliseconds
161
161
  *
162
- * @default 30000
162
+ * @default env.SERVER_TIMEOUT (120000)
163
163
  */
164
164
  timeout?: number;
165
165
  /**
@@ -202,6 +202,11 @@ interface ApiConfig {
202
202
  * Per-call options
203
203
  */
204
204
  interface CallOptions {
205
+ /**
206
+ * Request timeout in milliseconds
207
+ * Overrides the global timeout set in ApiConfig
208
+ */
209
+ timeout?: number;
205
210
  /**
206
211
  * Additional headers for this request
207
212
  */
@@ -242,4 +247,4 @@ interface CallOptions {
242
247
  };
243
248
  }
244
249
 
245
- export type { ApiConfig as A, CallOptions as C, InferRouteInput as I, RequestInterceptor as R, StructuredInput as S, ResponseInterceptor as a, InferRouteOutput as b, RouterInput as c, RouterOutput as d, CookieOptions as e, SetCookie as f };
250
+ export type { ApiConfig as A, CallOptions as C, InferRouteInput as I, RequestInterceptor as R, SetCookie as S, ResponseInterceptor as a, InferRouteOutput as b, CookieOptions as c, RouterInput as d, RouterOutput as e, StructuredInput as f };
@@ -0,0 +1,115 @@
1
+ import { b as RouteAuthProfile } from './router-ukNdAZcN.js';
2
+ import { HttpMethod } from './route/types.js';
3
+
4
+ /**
5
+ * Contract Document Types
6
+ *
7
+ * The shape of `contracts/current.json` and of a released snapshot under
8
+ * `contracts/released/<version>.json`.
9
+ *
10
+ * An operation is identified by its **name** — the key it holds in the router —
11
+ * not by method and path. That is what lets a changed path be reported as a
12
+ * broken promise instead of read as one operation disappearing and another
13
+ * appearing.
14
+ */
15
+
16
+ /** A JSON Schema object as TypeBox serializes it. */
17
+ type JsonSchema = Record<string, unknown>;
18
+ /** Request schemas, one per part of the request. */
19
+ interface ContractRequest {
20
+ params?: JsonSchema;
21
+ query?: JsonSchema;
22
+ body?: JsonSchema;
23
+ /**
24
+ * Never produced: a contracted route carrying multipart is refused at
25
+ * collection. Kept because a snapshot released before that rule may still
26
+ * have one, and the compatibility comparison has to read it.
27
+ */
28
+ formData?: JsonSchema;
29
+ headers?: JsonSchema;
30
+ cookies?: JsonSchema;
31
+ }
32
+ /** One contracted operation. */
33
+ interface ContractOperation {
34
+ /** Router key. The operation's identity across versions. */
35
+ name: string;
36
+ method: HttpMethod;
37
+ path: string;
38
+ /** Contract version the operation first appeared in. */
39
+ since: string;
40
+ auth: RouteAuthProfile;
41
+ requiresSession: boolean;
42
+ /** Present only when the operation is announced for removal. */
43
+ deprecatedIn?: string;
44
+ /**
45
+ * Present only when the operation is gone.
46
+ *
47
+ * A client generated before this version may still call it, so the record
48
+ * outlives the route: without it a caller sees an operation that simply
49
+ * stopped existing, with nothing saying when or that it was announced.
50
+ */
51
+ removedIn?: string;
52
+ /** What the client sends. */
53
+ request: ContractRequest;
54
+ /**
55
+ * What middleware injects into the request before the handler sees it.
56
+ *
57
+ * A web client never sends these — an interceptor fills them in. A client
58
+ * that talks to the route directly does send them, so they are part of the
59
+ * published request shape and are compared under the request rules.
60
+ */
61
+ interceptor: ContractRequest;
62
+ /** What the client reads. */
63
+ response: JsonSchema;
64
+ }
65
+ /**
66
+ * How a client's version is judged against the server's.
67
+ *
68
+ * - `allOrNothing` — one contract version is the whole surface's pass or
69
+ * refusal. Right for an auth primitive, where admitting a client that agrees
70
+ * about part of the admission sequence and not the rest is not a safe middle.
71
+ * - `perOperation` — availability is recorded per operation, so the verdict
72
+ * narrows to the operations a client actually calls. Deleting a response field
73
+ * from one route then stops blocking a client that never calls it.
74
+ *
75
+ * Stated rather than inferred: an app contract and @spfn/auth's mobile contract
76
+ * share this format under different rules, and a consumer must not have to guess
77
+ * which one it is holding.
78
+ */
79
+ type CompatibilityPolicy = 'allOrNothing' | 'perOperation';
80
+ /** The generated contract. */
81
+ interface ContractDocument {
82
+ /** Shape version of this document, not of the API it describes. */
83
+ documentVersion: 1;
84
+ /**
85
+ * The version this document publishes, from `.contractVersion()` on the
86
+ * router. Absent when the router declares none — the gate still runs, but
87
+ * nothing can be released or announced.
88
+ */
89
+ contractVersion?: string;
90
+ /** Always `perOperation` for an app contract. See the type. */
91
+ compatibilityPolicy: CompatibilityPolicy;
92
+ /** Sorted by name, so the file does not churn on router reordering. */
93
+ operations: ContractOperation[];
94
+ }
95
+ /** A released snapshot: the document plus the digest that pins it. */
96
+ interface ContractSnapshot {
97
+ version: string;
98
+ /** SHA-256 over the canonical encoding of `document`. */
99
+ sha256: string;
100
+ document: ContractDocument;
101
+ }
102
+ /** What a gate violation is about. */
103
+ type ContractViolationKind = 'operation.removed' | 'operation.path-changed' | 'operation.method-changed' | 'request.required-field-added' | 'request.field-became-required' | 'request.type-changed' | 'response.field-removed' | 'response.field-became-optional' | 'response.type-changed' | 'usage.undecidable' | 'usage.still-called' | 'snapshot.digest-mismatch';
104
+ /** One reason the build refuses. */
105
+ interface ContractViolation {
106
+ kind: ContractViolationKind;
107
+ /** Operation name, when the violation belongs to one. */
108
+ operation?: string;
109
+ /** Where inside the operation, e.g. `request.body.email`. */
110
+ location?: string;
111
+ /** What went wrong, in one line. */
112
+ detail: string;
113
+ }
114
+
115
+ export type { ContractDocument as C, JsonSchema as J, ContractViolation as a, ContractOperation as b, ContractSnapshot as c, CompatibilityPolicy as d, ContractRequest as e, ContractViolationKind as f };