@luckystack/core 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +11 -0
- package/CHANGELOG.md +13 -0
- package/README.md +2 -88
- package/dist/apiInterceptors.d.ts +36 -0
- package/dist/apiMethodMapRegistry.d.ts +5 -0
- package/dist/apiRequest.d.ts +71 -0
- package/dist/apiTypeStubs.d.ts +3 -5
- package/dist/avatarConfig.d.ts +22 -0
- package/dist/bindAddress.d.ts +15 -0
- package/dist/bootUuid.d.ts +24 -0
- package/dist/cancelRegistry.d.ts +7 -0
- package/dist/checkOrigin.d.ts +6 -0
- package/dist/client.d.ts +46 -335
- package/dist/client.js +2 -2
- package/dist/client.js.map +1 -1
- package/dist/clientHookBus.d.ts +62 -0
- package/dist/clients.d.ts +16 -0
- package/dist/config.d.ts +8 -2
- package/dist/configUtils.d.ts +16 -0
- package/dist/consoleLog.d.ts +1 -0
- package/dist/cookies.d.ts +28 -0
- package/dist/createRegistry.d.ts +39 -0
- package/dist/csrf.d.ts +30 -0
- package/dist/csrfConfig.d.ts +32 -0
- package/dist/db.d.ts +12 -0
- package/dist/dbHealthCheck.d.ts +6 -0
- package/dist/{config-CwW3KY9r.d.ts → deployConfigRegistry.d.ts} +8 -43
- package/dist/emailRegistry.d.ts +139 -0
- package/dist/env.d.ts +38 -0
- package/dist/errorFormatterRegistry.d.ts +54 -0
- package/dist/errorTrackerIdentity.d.ts +14 -0
- package/dist/errorTrackerRegistry.d.ts +85 -0
- package/dist/escapeHtml.d.ts +6 -0
- package/dist/eslint/index.d.ts +5 -8
- package/dist/eslint/internal/hasPackage.d.ts +1 -0
- package/dist/eslint/internal/ruleTypes.d.ts +4 -0
- package/dist/eslint/rules/no-arbitrary-tailwind-color.d.ts +3 -0
- package/dist/eslint/rules/no-direct-prisma-import-in-components.d.ts +3 -0
- package/dist/eslint/rules/no-local-socket-enumeration.d.ts +3 -0
- package/dist/eslint/rules/no-raw-fetch-in-src.d.ts +3 -0
- package/dist/eslint/rules/no-raw-try-catch.d.ts +3 -0
- package/dist/eslint/rules/no-unsafe-api-wrappers.d.ts +3 -0
- package/dist/eslint/rules/no-unsafe-sync-wrappers.d.ts +3 -0
- package/dist/eslint/rules/prefer-luckystack-confirm.d.ts +3 -0
- package/dist/eslint/rules/prefer-luckystack-dropdown.d.ts +3 -0
- package/dist/eslint/rules/prefer-luckystack-notify.d.ts +3 -0
- package/dist/extractToken.d.ts +12 -0
- package/dist/extractTokenFromRequest.d.ts +12 -0
- package/dist/getParams.d.ts +9 -0
- package/dist/hooks/registry.d.ts +12 -0
- package/dist/hooks/types.d.ts +446 -0
- package/dist/httpApiUtils.d.ts +54 -0
- package/dist/index.d.ts +110 -1886
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/lease.d.ts +18 -0
- package/dist/localSocketEnumerationGuard.d.ts +22 -0
- package/dist/localeReloader.d.ts +3 -0
- package/dist/localesRegistry.d.ts +8 -0
- package/dist/localizedNormalizer.d.ts +50 -0
- package/dist/loggerRegistry.d.ts +12 -0
- package/dist/middlewareRegistry.d.ts +45 -0
- package/dist/notifier.d.ts +17 -0
- package/dist/offlineQueue.d.ts +48 -0
- package/dist/oneTimeToken.d.ts +43 -0
- package/dist/pageRouteValidation.d.ts +37 -0
- package/dist/paths.d.ts +35 -0
- package/dist/peerDeps.d.ts +23 -0
- package/dist/portOverrideRegistry.d.ts +3 -0
- package/dist/processUpload.d.ts +44 -0
- package/dist/{sessionTypes-DanjuPaE.d.ts → projectConfig.d.ts} +32 -129
- package/dist/rateLimiter.d.ts +90 -0
- package/dist/react/AvatarProvider.d.ts +11 -0
- package/dist/react/Middleware.d.ts +10 -0
- package/dist/react/Router.d.ts +1 -0
- package/dist/react/TranslationProvider.d.ts +22 -0
- package/dist/react/notify.d.ts +12 -0
- package/dist/react/sessionContext.d.ts +31 -0
- package/dist/react/useTheme.d.ts +5 -0
- package/dist/redactedLogKeys.d.ts +22 -0
- package/dist/redis.d.ts +15 -0
- package/dist/redisKeyFormatter.d.ts +7 -0
- package/dist/resolveClientIp.d.ts +51 -0
- package/dist/responseNormalizer.d.ts +33 -0
- package/dist/roomNameFormatterRegistry.d.ts +36 -0
- package/dist/roomSockets.d.ts +23 -0
- package/dist/routedDataQuery.d.ts +7 -0
- package/dist/routedHttpInvocation.d.ts +20 -0
- package/dist/runtimeMapsRegistry.d.ts +18 -0
- package/dist/runtimeTypeValidation.d.ts +13 -0
- package/dist/secretsResolved.d.ts +3 -0
- package/dist/sentrySetup.d.ts +21 -0
- package/dist/serveAvatars.d.ts +5 -0
- package/dist/serviceRoute.d.ts +27 -0
- package/dist/servicesConfigRegistry.d.ts +22 -0
- package/dist/sessionProviderRegistry.d.ts +46 -0
- package/dist/sessionTypes.d.ts +79 -0
- package/dist/sleep.d.ts +1 -0
- package/dist/socketEvents.d.ts +37 -0
- package/dist/socketMiddlewareRegistry.d.ts +20 -0
- package/dist/socketRedisAdapter.d.ts +21 -0
- package/dist/socketState.d.ts +5 -0
- package/dist/socketStatusTypes.d.ts +6 -0
- package/dist/socketTypes.d.ts +34 -0
- package/dist/synchronizedEnvHashes.d.ts +66 -0
- package/dist/testFileConvention.d.ts +11 -0
- package/dist/testResetTargets.d.ts +5 -0
- package/dist/tryCatch.d.ts +1 -0
- package/dist/tryCatchClient.d.ts +1 -0
- package/dist/tryCatchSync.d.ts +1 -0
- package/dist/validateRequest.d.ts +50 -0
- package/docs/app-bootstrap.md +4 -449
- package/docs/config-registry.md +3 -346
- package/docs/csrf-config.md +3 -86
- package/docs/error-tracker-registry.md +3 -192
- package/docs/hooks.md +2 -295
- package/docs/rate-limit-strategy.md +3 -168
- package/docs/redis-adapter.md +5 -110
- package/docs/session-types.md +3 -178
- package/docs/socket-bootstrap.md +5 -385
- package/package.json +2 -2
- package/CLAUDE.md +0 -256
- package/dist/pageRouteValidation-B4Cmtfjv.d.ts +0 -280
package/dist/index.d.ts
CHANGED
|
@@ -1,1886 +1,110 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
|
|
7
|
-
export {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
*
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
* rooms). `'join'`/`'leave'` remain in the union for backward-compat but the
|
|
112
|
-
* framework no longer emits them for room-NAME formatting — treat them, if ever
|
|
113
|
-
* seen, identically to `'broadcast'`.
|
|
114
|
-
*/
|
|
115
|
-
purpose: 'join' | 'leave' | 'broadcast' | 'presence';
|
|
116
|
-
/**
|
|
117
|
-
* Session user id of whoever is performing THIS operation, when known, else
|
|
118
|
-
* null — CONTEXT ONLY. For a shared content room under `'broadcast'` the
|
|
119
|
-
* framework passes the joiner on join/rejoin, the SENDER on fan-out and the
|
|
120
|
-
* originator on `broadcastStream`, so the value differs per call for the SAME
|
|
121
|
-
* room. A formatter must therefore never fold `userId` into a content room's
|
|
122
|
-
* physical name: join and fan-out would resolve to different rooms and every
|
|
123
|
-
* broadcast would reach nobody, silently. Per-user isolation belongs in the
|
|
124
|
-
* raw room code the consumer chooses, or in the separate `'presence'` family.
|
|
125
|
-
*/
|
|
126
|
-
userId?: string | null;
|
|
127
|
-
}
|
|
128
|
-
type RoomNameFormatter = (rawRoomName: string, ctx: RoomNameFormatterContext) => string;
|
|
129
|
-
/** Override how every framework room name is namespaced (multi-tenant prefixing). */
|
|
130
|
-
declare const registerRoomNameFormatter: (formatter: RoomNameFormatter) => void;
|
|
131
|
-
/** Read the active formatter (defaults to identity). */
|
|
132
|
-
declare const getRoomNameFormatter: () => RoomNameFormatter;
|
|
133
|
-
/** Apply the active formatter — the single helper framework room-sites call. */
|
|
134
|
-
declare const formatRoomName: (rawRoomName: string, ctx: RoomNameFormatterContext) => string;
|
|
135
|
-
/** The built-in identity formatter (exported for tests / explicit reset). */
|
|
136
|
-
declare const defaultRoomNameFormatter: RoomNameFormatter;
|
|
137
|
-
|
|
138
|
-
type LanguageCode = string;
|
|
139
|
-
interface TranslateInput {
|
|
140
|
-
language: LanguageCode;
|
|
141
|
-
key: string;
|
|
142
|
-
params?: ErrorParam[];
|
|
143
|
-
}
|
|
144
|
-
type TranslateFunction = (input: TranslateInput) => string;
|
|
145
|
-
interface NormalizeErrorResponseInput {
|
|
146
|
-
response: ErrorResponseInput;
|
|
147
|
-
preferredLocale?: string | null;
|
|
148
|
-
userLanguage?: string | null;
|
|
149
|
-
fallbackHttpStatus?: number;
|
|
150
|
-
}
|
|
151
|
-
interface LocalizedNormalizer {
|
|
152
|
-
normalizeErrorResponse: (input: NormalizeErrorResponseInput) => NormalizedErrorResponse;
|
|
153
|
-
resolveErrorMessage: (input: {
|
|
154
|
-
errorCode: string;
|
|
155
|
-
errorParams?: ErrorParam[];
|
|
156
|
-
preferredLocale?: string | null;
|
|
157
|
-
userLanguage?: string | null;
|
|
158
|
-
}) => string;
|
|
159
|
-
extractLanguageFromHeader: (header?: string | string[]) => LanguageCode | null;
|
|
160
|
-
}
|
|
161
|
-
interface CreateLocalizedNormalizerInput {
|
|
162
|
-
translate: TranslateFunction;
|
|
163
|
-
/**
|
|
164
|
-
* Language the framework falls back to when the caller provides no
|
|
165
|
-
* language hints. Defaults to 'en'.
|
|
166
|
-
*/
|
|
167
|
-
defaultLanguage?: LanguageCode;
|
|
168
|
-
/**
|
|
169
|
-
* Decide whether a given string is an acceptable language code. Defaults
|
|
170
|
-
* to accepting any non-empty short-form code (ISO 639-1 style). Projects
|
|
171
|
-
* that only support a closed set should pass a narrower predicate.
|
|
172
|
-
*/
|
|
173
|
-
isSupportedLanguage?: (code: string) => boolean;
|
|
174
|
-
}
|
|
175
|
-
declare const createLocalizedNormalizer: (input: CreateLocalizedNormalizerInput) => LocalizedNormalizer;
|
|
176
|
-
declare const registerLocalizedNormalizer: (normalizer: LocalizedNormalizer) => void;
|
|
177
|
-
declare const getLocalizedNormalizer: () => LocalizedNormalizer;
|
|
178
|
-
declare const isLocalizedNormalizerRegistered: () => boolean;
|
|
179
|
-
declare const normalizeErrorResponse: (input: NormalizeErrorResponseInput) => NormalizedErrorResponse;
|
|
180
|
-
declare const resolveErrorMessage: (input: {
|
|
181
|
-
errorCode: string;
|
|
182
|
-
errorParams?: ErrorParam[];
|
|
183
|
-
preferredLocale?: string | null;
|
|
184
|
-
userLanguage?: string | null;
|
|
185
|
-
}) => string;
|
|
186
|
-
declare const extractLanguageFromHeader: (header?: string | string[]) => LanguageCode | null;
|
|
187
|
-
|
|
188
|
-
interface ErrorFormatterContext {
|
|
189
|
-
/** Resolved route name (`api/billing/getInvoice/v1`, `sync/...`). */
|
|
190
|
-
routeName: string;
|
|
191
|
-
/** Transport that triggered the error. */
|
|
192
|
-
transport: 'socket' | 'http';
|
|
193
|
-
/** Session id when available. */
|
|
194
|
-
userId?: string | null;
|
|
195
|
-
}
|
|
196
|
-
/** The normalized error envelope handed to every formatter. */
|
|
197
|
-
interface FormatterErrorEnvelope {
|
|
198
|
-
status: 'error';
|
|
199
|
-
errorCode?: string;
|
|
200
|
-
message?: string;
|
|
201
|
-
httpStatus?: number;
|
|
202
|
-
[key: string]: unknown;
|
|
203
|
-
}
|
|
204
|
-
type ErrorFormatter = (error: FormatterErrorEnvelope, ctx: ErrorFormatterContext) => Record<string, unknown>;
|
|
205
|
-
/**
|
|
206
|
-
* Minimal structural contract every formatter-eligible envelope satisfies:
|
|
207
|
-
* a discriminant `status` plus an open string-index so formatters can read /
|
|
208
|
-
* add arbitrary fields. Generic helpers below constrain to this so transport
|
|
209
|
-
* handlers no longer need `as unknown as` double-casts at the formatter
|
|
210
|
-
* boundary — see `applyErrorFormatter` / `buildFormattedError`.
|
|
211
|
-
*/
|
|
212
|
-
interface FormatterEnvelope {
|
|
213
|
-
status?: string;
|
|
214
|
-
[key: string]: unknown;
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Register a global error-response formatter. Receives the normalized
|
|
218
|
-
* error envelope + context; return a (possibly extended) object that the
|
|
219
|
-
* framework will emit. Pass `null` to unregister.
|
|
220
|
-
*
|
|
221
|
-
* For per-endpoint overrides, export `errorFormatter` from your
|
|
222
|
-
* `_api/foo_v1.ts` file directly — the api/sync handler imports + invokes
|
|
223
|
-
* it through `applyErrorFormatter`.
|
|
224
|
-
*/
|
|
225
|
-
declare const registerErrorFormatter: (formatter: ErrorFormatter | null) => void;
|
|
226
|
-
/** Read the active global formatter (or null). */
|
|
227
|
-
declare const getErrorFormatter: () => ErrorFormatter | null;
|
|
228
|
-
/**
|
|
229
|
-
* Apply per-route → global → identity formatter chain to a normalized
|
|
230
|
-
* error envelope. Returns the envelope unchanged when `status !== 'error'`
|
|
231
|
-
* so it's safe to wrap every emit call. Errors thrown inside a formatter
|
|
232
|
-
* are caught and logged; the un-formatted envelope is returned in that
|
|
233
|
-
* case to keep the error path crash-resistant.
|
|
234
|
-
*/
|
|
235
|
-
declare const applyErrorFormatter: <T extends FormatterEnvelope>(input: {
|
|
236
|
-
response: T;
|
|
237
|
-
routeName: string;
|
|
238
|
-
transport: "socket" | "http";
|
|
239
|
-
userId?: string | null;
|
|
240
|
-
perRouteFormatter?: ErrorFormatter | undefined | null;
|
|
241
|
-
}) => T;
|
|
242
|
-
|
|
243
|
-
interface apiMessage {
|
|
244
|
-
name: string;
|
|
245
|
-
data: object;
|
|
246
|
-
responseIndex: number;
|
|
247
|
-
}
|
|
248
|
-
interface syncMessage {
|
|
249
|
-
name: string;
|
|
250
|
-
data: object;
|
|
251
|
-
cb: string;
|
|
252
|
-
receiver: string;
|
|
253
|
-
responseIndex?: number;
|
|
254
|
-
ignoreSelf?: boolean;
|
|
255
|
-
}
|
|
256
|
-
declare const setIoInstance: (io: Server | null) => void;
|
|
257
|
-
interface GetIoInstanceOptions {
|
|
258
|
-
/**
|
|
259
|
-
* Return the raw Socket.io server even outside production. Reach for this
|
|
260
|
-
* ONLY when the per-instance view is what you want (backpressure sampling, a
|
|
261
|
-
* sweep cleaning up this process's own connections) — and say why in a
|
|
262
|
-
* comment, because the lint rule `luckystack/no-local-socket-enumeration`
|
|
263
|
-
* will still flag the access.
|
|
264
|
-
*/
|
|
265
|
-
raw?: boolean;
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Read the running Socket.io server. Outside production the returned value is
|
|
269
|
-
* a guarded view that THROWS on the per-instance surfaces
|
|
270
|
-
* (`sockets.adapter.rooms`, `sockets.adapter.sids`, enumerating
|
|
271
|
-
* `sockets.sockets`) — see `localSocketEnumerationGuard.ts` for why. Pass
|
|
272
|
-
* `{ raw: true }` for the unwrapped server. In production the raw server is
|
|
273
|
-
* always returned; the guard costs nothing there.
|
|
274
|
-
*/
|
|
275
|
-
declare const getIoInstance: (options?: GetIoInstanceOptions) => Server | null;
|
|
276
|
-
|
|
277
|
-
/** Thrown (outside production) when consumer code touches a per-instance socket map. */
|
|
278
|
-
declare class LocalSocketEnumerationError extends Error {
|
|
279
|
-
constructor(surface: string);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/** A recipient as returned by Socket.io's adapter-aware `fetchSockets()`. */
|
|
283
|
-
type RoomSocket = Awaited<ReturnType<Server['fetchSockets']>>[number];
|
|
284
|
-
interface GetRoomSocketsOptions {
|
|
285
|
-
/**
|
|
286
|
-
* User id forwarded to the room-name formatter as CONTEXT. Leave it out (or
|
|
287
|
-
* pass `null`) for a shared room — the formatter contract forbids folding it
|
|
288
|
-
* into a content room's physical name, so it only matters to a formatter that
|
|
289
|
-
* logs or branches on it.
|
|
290
|
-
*/
|
|
291
|
-
userId?: string | null;
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Resolve every socket currently in `room`, on EVERY instance sharing the
|
|
295
|
-
* Redis adapter. `room` is the LOGICAL code (what the client joined); it is
|
|
296
|
-
* routed through the registered room-name formatter under the canonical
|
|
297
|
-
* `'broadcast'` purpose exactly like the sync fan-out. The `'all'` sentinel
|
|
298
|
-
* returns every connected socket everywhere.
|
|
299
|
-
*
|
|
300
|
-
* Throws when no Socket.io server is registered: a silent `[]` is precisely the
|
|
301
|
-
* failure class this helper exists to remove.
|
|
302
|
-
*/
|
|
303
|
-
declare const getRoomSockets: (room: string, options?: GetRoomSocketsOptions) => Promise<RoomSocket[]>;
|
|
304
|
-
|
|
305
|
-
type RuntimeMapRecord = Record<string, unknown>;
|
|
306
|
-
interface RuntimeApiMapsResult {
|
|
307
|
-
apisObject: RuntimeMapRecord;
|
|
308
|
-
functionsObject: RuntimeMapRecord;
|
|
309
|
-
}
|
|
310
|
-
interface RuntimeSyncMapsResult {
|
|
311
|
-
syncObject: RuntimeMapRecord;
|
|
312
|
-
functionsObject: RuntimeMapRecord;
|
|
313
|
-
}
|
|
314
|
-
interface RuntimeMapsProvider {
|
|
315
|
-
getRuntimeApiMaps: () => Promise<RuntimeApiMapsResult>;
|
|
316
|
-
getRuntimeSyncMaps: () => Promise<RuntimeSyncMapsResult>;
|
|
317
|
-
}
|
|
318
|
-
declare const registerRuntimeMapsProvider: (provider: RuntimeMapsProvider) => void;
|
|
319
|
-
declare const isRuntimeMapsProviderRegistered: () => boolean;
|
|
320
|
-
declare const getRuntimeApiMaps: () => Promise<RuntimeApiMapsResult>;
|
|
321
|
-
declare const getRuntimeSyncMaps: () => Promise<RuntimeSyncMapsResult>;
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* A single email attachment (email F2). Adapters (Resend, SMTP/Nodemailer, SES)
|
|
325
|
-
* thread these into their provider payload. `content` is the raw bytes
|
|
326
|
-
* (`Buffer`/`Uint8Array`) or a base64 string; provide a `path`/`href` instead
|
|
327
|
-
* for adapters that fetch externally. Shapes are intentionally permissive so a
|
|
328
|
-
* single attachment object works across providers — each adapter maps the
|
|
329
|
-
* fields it supports.
|
|
330
|
-
*/
|
|
331
|
-
interface EmailAttachment {
|
|
332
|
-
/** File name shown to the recipient (e.g. `invoice.pdf`). */
|
|
333
|
-
filename: string;
|
|
334
|
-
/** Inline bytes or base64 string. Mutually exclusive with `path`/`href`. */
|
|
335
|
-
content?: Buffer | Uint8Array | string;
|
|
336
|
-
/**
|
|
337
|
-
* Local file path the adapter reads (when it supports path-based sends).
|
|
338
|
-
*
|
|
339
|
-
* @security Trusted-input field — do NOT derive from client-supplied data
|
|
340
|
-
* without validation. Framework-level path-traversal or safe-directory checks
|
|
341
|
-
* are NOT applied automatically; callers must reject paths that escape the
|
|
342
|
-
* intended directory (e.g. containing `..` or absolute paths outside a known
|
|
343
|
-
* root) before passing them here.
|
|
344
|
-
*/
|
|
345
|
-
path?: string;
|
|
346
|
-
/**
|
|
347
|
-
* Remote URL the adapter fetches (when it supports href-based sends).
|
|
348
|
-
*
|
|
349
|
-
* @security Trusted-input field — do NOT derive from client-supplied data
|
|
350
|
-
* without validation. Only `https:` and `http:` URLs are safe for
|
|
351
|
-
* framework-internal use; arbitrary schemes (e.g. `file:`, `data:`) may be
|
|
352
|
-
* forwarded to nodemailer/Resend without restriction. Validate against an
|
|
353
|
-
* allowlist before passing here.
|
|
354
|
-
*/
|
|
355
|
-
href?: string;
|
|
356
|
-
/** MIME type (e.g. `application/pdf`). Adapter may infer from `filename` if omitted. */
|
|
357
|
-
contentType?: string;
|
|
358
|
-
/** `Content-ID` for inline/embedded images referenced by `cid:` in the HTML. */
|
|
359
|
-
cid?: string;
|
|
360
|
-
}
|
|
361
|
-
interface EmailMessage {
|
|
362
|
-
to: string | string[];
|
|
363
|
-
subject: string;
|
|
364
|
-
html: string;
|
|
365
|
-
text?: string;
|
|
366
|
-
from?: string;
|
|
367
|
-
replyTo?: string;
|
|
368
|
-
cc?: string | string[];
|
|
369
|
-
bcc?: string | string[];
|
|
370
|
-
/**
|
|
371
|
-
* File attachments (email F2). Owned by core (not `@luckystack/email`) so the
|
|
372
|
-
* type is the single source of truth across the framework and every adapter;
|
|
373
|
-
* adapters thread these into their provider's attachment payload. Optional —
|
|
374
|
-
* a message without attachments behaves exactly as before.
|
|
375
|
-
*/
|
|
376
|
-
attachments?: EmailAttachment[];
|
|
377
|
-
/**
|
|
378
|
-
* Custom message headers (email F2) — e.g. `X-Entity-Ref-ID`,
|
|
379
|
-
* `List-Unsubscribe`, idempotency keys. Adapters merge these over the headers
|
|
380
|
-
* they set themselves (adapter-reserved headers win to avoid breaking
|
|
381
|
-
* delivery). Optional.
|
|
382
|
-
*
|
|
383
|
-
* @security CR and LF characters in header keys or values enable SMTP header
|
|
384
|
-
* injection. `sendEmail` strips `\r` and `\n` from every key and value before
|
|
385
|
-
* passing them to the adapter (EMAIL-O7). Do NOT derive keys or values from
|
|
386
|
-
* unsanitized client input.
|
|
387
|
-
*/
|
|
388
|
-
headers?: Record<string, string>;
|
|
389
|
-
}
|
|
390
|
-
type EmailDeliveryOutcome = 'not-sent' | 'unknown';
|
|
391
|
-
type EmailResult = {
|
|
392
|
-
ok: true;
|
|
393
|
-
id: string;
|
|
394
|
-
} | {
|
|
395
|
-
ok: false;
|
|
396
|
-
reason: string;
|
|
397
|
-
cause?: unknown;
|
|
398
|
-
/**
|
|
399
|
-
* `unknown` means the caller stopped waiting after dispatch began; retry
|
|
400
|
-
* only with the same idempotency key. Omitted on legacy/provider failures
|
|
401
|
-
* whose delivery semantics are adapter-specific.
|
|
402
|
-
*/
|
|
403
|
-
deliveryOutcome?: EmailDeliveryOutcome;
|
|
404
|
-
};
|
|
405
|
-
interface EmailSendContext {
|
|
406
|
-
/** Cooperative cancellation. Custom adapters should abort provider I/O when supported. */
|
|
407
|
-
signal: AbortSignal;
|
|
408
|
-
/** Stable caller-provided key reused across retries of the same logical email. */
|
|
409
|
-
idempotencyKey?: string;
|
|
410
|
-
}
|
|
411
|
-
interface EmailSender {
|
|
412
|
-
/** Adapter identifier for logs + diagnostics ("console", "resend", "smtp", etc.). */
|
|
413
|
-
name: string;
|
|
414
|
-
/**
|
|
415
|
-
* The optional context preserves source compatibility with pre-0.7 adapters.
|
|
416
|
-
* New adapters should honor `signal` and provider-native idempotency where available.
|
|
417
|
-
*/
|
|
418
|
-
send: (message: EmailMessage, context?: EmailSendContext) => Promise<EmailResult>;
|
|
419
|
-
}
|
|
420
|
-
/**
|
|
421
|
-
* Registry keyed by adapter-slot name. Reserved slots used by the
|
|
422
|
-
* framework:
|
|
423
|
-
*
|
|
424
|
-
* - `'default'` — fallback when no specific slot matches.
|
|
425
|
-
* - `'transactional'` — login password-reset, account-confirmation,
|
|
426
|
-
* invoice-receipt, security-critical emails.
|
|
427
|
-
* - `'marketing'` — newsletters, announcements; consumers MAY route
|
|
428
|
-
* bulk sends here.
|
|
429
|
-
* - `'diagnostics'` — diagnostic-test endpoints (e.g. playground).
|
|
430
|
-
*
|
|
431
|
-
* Custom slot names ('billing', 'support', etc.) are allowed; consumers
|
|
432
|
-
* resolve them explicitly via `sendEmail({ adapter: 'billing', ... })`.
|
|
433
|
-
*/
|
|
434
|
-
type EmailSenderRegistry = Partial<Record<string, EmailSender>>;
|
|
435
|
-
/**
|
|
436
|
-
* Register a single global email sender. Kept for backwards compatibility.
|
|
437
|
-
* For new code, prefer `registerEmailSenders({ ... })` which lets the
|
|
438
|
-
* framework route transactional vs. marketing through different adapters.
|
|
439
|
-
*/
|
|
440
|
-
declare const registerEmailSender: (sender: EmailSender) => void;
|
|
441
|
-
/**
|
|
442
|
-
* Register multiple named email senders. Replaces the entire registry
|
|
443
|
-
* (last-write-wins). Use this when your app routes different message
|
|
444
|
-
* types through different adapters — e.g. Resend for marketing and SMTP
|
|
445
|
-
* for transactional. The framework picks adapter slots by convention
|
|
446
|
-
* (see `EmailSenderRegistry`); consumer code can pick explicitly via
|
|
447
|
-
* `sendEmail({ adapter: 'foo', ... })`.
|
|
448
|
-
*/
|
|
449
|
-
declare const registerEmailSenders: (senders: EmailSenderRegistry) => void;
|
|
450
|
-
/** Read the legacy single sender (or the `default` slot of the registry). */
|
|
451
|
-
declare const getEmailSender: () => EmailSender | null;
|
|
452
|
-
/**
|
|
453
|
-
* Read a specific named sender. Falls back to the legacy single sender
|
|
454
|
-
* when the requested slot is absent. Returns null when neither exists.
|
|
455
|
-
* Pass the convention-based slots (`'transactional'`, `'marketing'`,
|
|
456
|
-
* `'diagnostics'`, `'default'`) or any custom slot you registered.
|
|
457
|
-
*/
|
|
458
|
-
declare const getEmailSenderByName: (name: string) => EmailSender | null;
|
|
459
|
-
/** Read every registered slot name. */
|
|
460
|
-
declare const listEmailSenderNames: () => string[];
|
|
461
|
-
declare const isEmailSenderRegistered: () => boolean;
|
|
462
|
-
|
|
463
|
-
type LocaleReloader = () => void | Promise<void>;
|
|
464
|
-
declare const registerLocaleReloader: (reloader: LocaleReloader) => LocaleReloader;
|
|
465
|
-
declare const getLocaleReloader: () => LocaleReloader | null;
|
|
466
|
-
|
|
467
|
-
interface SentryInstance {
|
|
468
|
-
captureException: (exception: unknown, ...args: unknown[]) => string;
|
|
469
|
-
captureMessage: (message: string, ...args: unknown[]) => string;
|
|
470
|
-
setUser: (user: unknown) => void;
|
|
471
|
-
setContext: (key: string, context: unknown) => void;
|
|
472
|
-
startInactiveSpan?: (context: unknown) => unknown;
|
|
473
|
-
}
|
|
474
|
-
/**
|
|
475
|
-
* @deprecated Use `registerErrorTracker` from `@luckystack/error-tracking` instead.
|
|
476
|
-
* Will be removed in a future major version.
|
|
477
|
-
*/
|
|
478
|
-
declare const initSharedSentry: (instance: SentryInstance) => void;
|
|
479
|
-
declare const captureException: (error: unknown, context?: Record<string, unknown>) => void;
|
|
480
|
-
declare const captureMessage: (message: string, level?: "info" | "warning" | "error" | "fatal", context?: Record<string, unknown>) => void;
|
|
481
|
-
declare const setSentryUser: (user: {
|
|
482
|
-
id?: string;
|
|
483
|
-
email?: string;
|
|
484
|
-
username?: string;
|
|
485
|
-
} | null) => void;
|
|
486
|
-
declare const startSpan: (name: string, op: string) => unknown;
|
|
487
|
-
|
|
488
|
-
interface ErrorTrackerUser {
|
|
489
|
-
id?: string;
|
|
490
|
-
email?: string;
|
|
491
|
-
username?: string;
|
|
492
|
-
[key: string]: unknown;
|
|
493
|
-
}
|
|
494
|
-
/** Open an isolated request identity scope before the first async boundary. */
|
|
495
|
-
declare const runWithErrorTrackerIdentityScope: <T>(fn: () => T) => T;
|
|
496
|
-
/** Open an isolated scope with a user that is already known. */
|
|
497
|
-
declare const runWithErrorTrackerIdentity: <T>(user: ErrorTrackerUser | null, fn: () => T) => T;
|
|
498
|
-
/** Set the user inside the active request scope; no-op outside a scope. */
|
|
499
|
-
declare const setCurrentErrorTrackerIdentity: (user: ErrorTrackerUser | null) => void;
|
|
500
|
-
/** Read the current request identity, or null outside a scope. */
|
|
501
|
-
declare const getCurrentErrorTrackerIdentity: () => ErrorTrackerUser | null;
|
|
502
|
-
|
|
503
|
-
type ErrorTrackerContext = Record<string, unknown>;
|
|
504
|
-
declare const sanitizeErrorString: (value: string) => string;
|
|
505
|
-
declare const sanitizeErrorStrings: (error: unknown) => {
|
|
506
|
-
message: string;
|
|
507
|
-
stack: string | undefined;
|
|
508
|
-
} | null;
|
|
509
|
-
interface ErrorTrackerEvent {
|
|
510
|
-
/** When false, the adapter must not forward this event (beforeSend opt-out). */
|
|
511
|
-
forwarded: boolean;
|
|
512
|
-
kind: 'exception' | 'message';
|
|
513
|
-
payload: Record<string, unknown>;
|
|
514
|
-
}
|
|
515
|
-
type SpanResult<T> = T;
|
|
516
|
-
interface ErrorTracker {
|
|
517
|
-
/** Human-readable identifier (logs, diagnostics). */
|
|
518
|
-
name: string;
|
|
519
|
-
captureException: (error: unknown, context?: ErrorTrackerContext) => void;
|
|
520
|
-
captureMessage: (message: string, level: 'info' | 'warning' | 'error' | 'fatal', context?: ErrorTrackerContext) => void;
|
|
521
|
-
setUser: (user: ErrorTrackerUser | null) => void;
|
|
522
|
-
setContext?: (key: string, context: ErrorTrackerContext | null) => void;
|
|
523
|
-
startSpan?: <T>(name: string, op: string, fn: () => T) => T;
|
|
524
|
-
/**
|
|
525
|
-
* Optional handle-style span (#62): open a span NOW and close it later via the
|
|
526
|
-
* returned handle's `finish()`. Unlike {@link startSpan} (which wraps a single
|
|
527
|
-
* synchronous `fn`), this fits an open-at-preExecute / close-at-postExecute
|
|
528
|
-
* request lifecycle that spans awaits. When present, the registry-level
|
|
529
|
-
* {@link startSpanHandle} delegates to the FIRST registered adapter that
|
|
530
|
-
* implements it so the request span reaches an adapter-only backend
|
|
531
|
-
* (Datadog/PostHog/custom), not just the legacy Sentry SDK path.
|
|
532
|
-
*/
|
|
533
|
-
startSpanHandle?: (name: string, op: string) => {
|
|
534
|
-
finish: () => void;
|
|
535
|
-
};
|
|
536
|
-
recordMetric?: (name: string, value: number, tags?: Record<string, string>) => void;
|
|
537
|
-
beforeSend?: (event: ErrorTrackerEvent) => ErrorTrackerEvent | null;
|
|
538
|
-
/**
|
|
539
|
-
* Optional flush lifecycle hook (error-tracking ET batch). Called by
|
|
540
|
-
* {@link flushErrorTrackers} on graceful shutdown so a buffered adapter
|
|
541
|
-
* (PostHog batch, Sentry transport) can drain in-flight events before exit.
|
|
542
|
-
* Returns a promise that resolves when the adapter has flushed.
|
|
543
|
-
*/
|
|
544
|
-
flush?: () => Promise<void>;
|
|
545
|
-
}
|
|
546
|
-
type PreCaptureFilter = (event: ErrorTrackerEvent) => boolean;
|
|
547
|
-
declare const registerPreCaptureFilter: (filter: PreCaptureFilter | null) => void;
|
|
548
|
-
declare const registerErrorTracker: (tracker: ErrorTracker) => void;
|
|
549
|
-
declare const registerErrorTrackers: (trackers: ErrorTracker[]) => void;
|
|
550
|
-
/**
|
|
551
|
-
* APPEND primitive (ET-05): add a tracker WITHOUT clobbering already-registered
|
|
552
|
-
* ones. Fixes the async-PostHog-vs-consumer-overlay race where the loser of
|
|
553
|
-
* `registerErrorTracker`'s replace silently vanished. De-duplicates by `name`
|
|
554
|
-
* (re-appending the same adapter name replaces that one entry in place) so a
|
|
555
|
-
* lazy adapter that registers a proxy then the real client doesn't double-fire.
|
|
556
|
-
*/
|
|
557
|
-
declare const appendErrorTracker: (tracker: ErrorTracker) => void;
|
|
558
|
-
declare const getActiveErrorTrackers: () => ErrorTracker[];
|
|
559
|
-
/**
|
|
560
|
-
* Flush lifecycle (error-tracking ET batch): drain every adapter that exposes a
|
|
561
|
-
* `flush()` before shutdown. Per-adapter failures are swallowed + logged so one
|
|
562
|
-
* stuck adapter can't block graceful shutdown. Safe to call when no adapter has
|
|
563
|
-
* `flush`.
|
|
564
|
-
*/
|
|
565
|
-
declare const flushErrorTrackers: () => Promise<void>;
|
|
566
|
-
declare const captureExceptionAcrossTrackers: (error: unknown, context?: ErrorTrackerContext) => void;
|
|
567
|
-
declare const captureMessageAcrossTrackers: (message: string, level: "info" | "warning" | "error" | "fatal", context?: ErrorTrackerContext) => void;
|
|
568
|
-
declare const setErrorTrackerUser: (user: ErrorTrackerUser | null) => void;
|
|
569
|
-
declare const recordMetricAcrossTrackers: (name: string, value: number, tags?: Record<string, string>) => void;
|
|
570
|
-
declare const startSpanAcrossTrackers: <T>(name: string, op: string, fn: () => T) => T;
|
|
571
|
-
/**
|
|
572
|
-
* Handle-style span (error-tracking ET batch): for callers that can't wrap the
|
|
573
|
-
* measured work in a single synchronous `fn` (streaming, cross-await spans).
|
|
574
|
-
* Returns a handle whose `finish()` resolves the span's duration. The default
|
|
575
|
-
* implementation is a lightweight wall-clock timer (works with any backend);
|
|
576
|
-
* an adapter can supply a richer span via the existing `startSpan` callback
|
|
577
|
-
* form when it needs native instrumentation. `finish()` is idempotent.
|
|
578
|
-
*/
|
|
579
|
-
interface SpanHandle {
|
|
580
|
-
/** Stop the span; safe to call more than once (subsequent calls are no-ops). */
|
|
581
|
-
finish: () => void;
|
|
582
|
-
/** Milliseconds elapsed at the moment `finish()` first ran (0 before). */
|
|
583
|
-
readonly durationMs: number;
|
|
584
|
-
}
|
|
585
|
-
declare const startSpanHandle: (name: string, op: string) => SpanHandle;
|
|
586
|
-
|
|
587
|
-
declare const EnvSchema: z.ZodObject<{
|
|
588
|
-
NODE_ENV: z.ZodDefault<z.ZodEnum<{
|
|
589
|
-
development: "development";
|
|
590
|
-
production: "production";
|
|
591
|
-
test: "test";
|
|
592
|
-
}>>;
|
|
593
|
-
SERVER_IP: z.ZodDefault<z.ZodString>;
|
|
594
|
-
SECURE: z.ZodDefault<z.ZodEnum<{
|
|
595
|
-
true: "true";
|
|
596
|
-
false: "false";
|
|
597
|
-
}>>;
|
|
598
|
-
REDIS_HOST: z.ZodDefault<z.ZodString>;
|
|
599
|
-
REDIS_PORT: z.ZodDefault<z.ZodString>;
|
|
600
|
-
PROJECT_NAME: z.ZodDefault<z.ZodString>;
|
|
601
|
-
}, z.core.$loose>;
|
|
602
|
-
type RuntimeEnv = z.infer<typeof EnvSchema>;
|
|
603
|
-
declare const DEFAULT_ENV_FILES: string[];
|
|
604
|
-
declare const getEnvFiles: () => string[];
|
|
605
|
-
declare const loadEnvFiles: () => void;
|
|
606
|
-
declare const bootstrapEnv: () => RuntimeEnv;
|
|
607
|
-
declare const env: {
|
|
608
|
-
[x: string]: unknown;
|
|
609
|
-
NODE_ENV: "development" | "production" | "test";
|
|
610
|
-
SERVER_IP: string;
|
|
611
|
-
SECURE: "true" | "false";
|
|
612
|
-
REDIS_HOST: string;
|
|
613
|
-
REDIS_PORT: string;
|
|
614
|
-
PROJECT_NAME: string;
|
|
615
|
-
};
|
|
616
|
-
declare const isProduction: boolean;
|
|
617
|
-
declare const getEnv: () => RuntimeEnv;
|
|
618
|
-
/** Application runtime mode. Keep separate from `resolveEnvKey()`, which is a
|
|
619
|
-
* deploy-topology identity and may be `staging`, `dockerSplit`, etc. */
|
|
620
|
-
declare const resolveRuntimeMode: () => RuntimeEnv["NODE_ENV"];
|
|
621
|
-
declare const isProductionRuntime: () => boolean;
|
|
622
|
-
declare const isTestRuntime: () => boolean;
|
|
623
|
-
|
|
624
|
-
declare global {
|
|
625
|
-
var __luckystackPrisma: PrismaClient | undefined;
|
|
626
|
-
}
|
|
627
|
-
/**
|
|
628
|
-
* Is a generated `@prisma/client` present in this install? Used by the
|
|
629
|
-
* default resolver's error path and by `/readyz` to decide whether a missing
|
|
630
|
-
* DB ping means "broken" or "this project has no Prisma by design".
|
|
631
|
-
*/
|
|
632
|
-
declare const isPrismaClientResolvable: () => boolean;
|
|
633
|
-
declare const prisma: PrismaClient<_prisma_client.Prisma.PrismaClientOptions, never, _prisma_client_runtime_library.DefaultArgs>;
|
|
634
|
-
|
|
635
|
-
type DbHealthResult = boolean | 'skipped';
|
|
636
|
-
type DbHealthCheck = () => Promise<DbHealthResult> | DbHealthResult;
|
|
637
|
-
declare const registerDbHealthCheck: (check: DbHealthCheck) => void;
|
|
638
|
-
declare const getDbHealthCheck: () => DbHealthCheck | null;
|
|
639
|
-
declare const isDbHealthCheckRegistered: () => boolean;
|
|
640
|
-
declare const resetDbHealthCheckForTests: () => void;
|
|
641
|
-
|
|
642
|
-
declare const resetDefaultRedisClient: () => void;
|
|
643
|
-
declare const rebuildDefaultRedisClient: () => Redis;
|
|
644
|
-
interface RedisConnectionOptions {
|
|
645
|
-
host: string;
|
|
646
|
-
port: number;
|
|
647
|
-
username?: string;
|
|
648
|
-
password?: string;
|
|
649
|
-
}
|
|
650
|
-
declare const getRedisConnectionOptions: () => RedisConnectionOptions;
|
|
651
|
-
declare const registerStrayPrefixCommand: (...commands: string[]) => void;
|
|
652
|
-
declare const redis: Redis$1;
|
|
653
|
-
|
|
654
|
-
type RedisKeyFormatter = (namespace: string, suffix: string) => string;
|
|
655
|
-
declare const defaultRedisKeyFormatter: RedisKeyFormatter;
|
|
656
|
-
declare const registerRedisKeyFormatter: (fn: RedisKeyFormatter) => void;
|
|
657
|
-
declare const getRedisKeyFormatter: () => RedisKeyFormatter;
|
|
658
|
-
declare const resetRedisKeyFormatterForTests: () => void;
|
|
659
|
-
declare const formatKey: (namespace: string, suffix?: string) => string;
|
|
660
|
-
declare const applyStrayKeyPrefix: (key: string) => string;
|
|
661
|
-
|
|
662
|
-
/**
|
|
663
|
-
* Acquire an exclusive lease named `name` for `ttlMs` milliseconds. Returns an
|
|
664
|
-
* opaque owner token on success, or `null` when another owner already holds it.
|
|
665
|
-
* `SET key token PX ttl NX` is atomic, so concurrent acquirers race safely.
|
|
666
|
-
* Keep the returned token to `renewLease` / `releaseLease`.
|
|
667
|
-
*/
|
|
668
|
-
declare const acquireLease: (name: string, ttlMs: number) => Promise<string | null>;
|
|
669
|
-
/**
|
|
670
|
-
* Extend a held lease by `ttlMs` (sliding ownership). Returns true when the
|
|
671
|
-
* supplied `token` still owns the lease and the TTL was applied, false
|
|
672
|
-
* otherwise (lost ownership, expired, or Redis error).
|
|
673
|
-
*/
|
|
674
|
-
declare const renewLease: (name: string, token: string, ttlMs: number) => Promise<boolean>;
|
|
675
|
-
/**
|
|
676
|
-
* Release a held lease. Owner-checked: only succeeds (returns true) when
|
|
677
|
-
* `token` is the current holder. Returns false on mismatch or Redis error.
|
|
678
|
-
*/
|
|
679
|
-
declare const releaseLease: (name: string, token: string) => Promise<boolean>;
|
|
680
|
-
|
|
681
|
-
declare const DEFAULT_CLIENT_KEY = "default";
|
|
682
|
-
declare const registerPrismaClient: (client: PrismaClient, key?: string) => PrismaClient;
|
|
683
|
-
declare const registerRedisClient: (client: Redis, key?: string) => Redis;
|
|
684
|
-
declare const isPrismaClientRegistered: () => boolean;
|
|
685
|
-
declare const isRedisClientRegistered: () => boolean;
|
|
686
|
-
declare const getPrismaClientKeys: () => string[];
|
|
687
|
-
declare const getRedisClientKeys: () => string[];
|
|
688
|
-
declare const getPrismaClientFor: (key?: string) => PrismaClient;
|
|
689
|
-
declare const getRedisClientFor: (key?: string) => Redis;
|
|
690
|
-
declare const getPrismaClient: () => PrismaClient;
|
|
691
|
-
declare const getRedisClient: () => Redis;
|
|
692
|
-
declare const resetClientsForTests: () => void;
|
|
693
|
-
|
|
694
|
-
interface AttachSocketRedisAdapterOptions {
|
|
695
|
-
/**
|
|
696
|
-
* Options forwarded to `@socket.io/redis-adapter`'s `createAdapter`
|
|
697
|
-
* (e.g. `key` prefix, `requestsTimeout`, `publishOnSpecificResponseChannel`).
|
|
698
|
-
* `key` defaults to {@link resolveSocketAdapterKey} when omitted.
|
|
699
|
-
*/
|
|
700
|
-
adapterOptions?: Partial<RedisAdapterOptions>;
|
|
701
|
-
/**
|
|
702
|
-
* Supply a pre-built pub client instead of `redis.duplicate()`. Use this for
|
|
703
|
-
* a dedicated adapter connection, custom auth, or a sentinel/cluster handle.
|
|
704
|
-
* When omitted the framework duplicates the default `redis` handle.
|
|
705
|
-
*/
|
|
706
|
-
pubClient?: Redis;
|
|
707
|
-
/** Pre-built sub client (see `pubClient`). */
|
|
708
|
-
subClient?: Redis;
|
|
709
|
-
}
|
|
710
|
-
declare const resolveSocketAdapterKey: () => string;
|
|
711
|
-
declare const attachSocketRedisAdapter: (io: Server, options?: AttachSocketRedisAdapterOptions) => void;
|
|
712
|
-
|
|
713
|
-
declare const BOOT_KEY_PREFIX = "luckystack:boot:";
|
|
714
|
-
declare const resolveEnvKey: () => string;
|
|
715
|
-
declare const writeBootUuid: (envKey?: string) => Promise<string>;
|
|
716
|
-
declare const readBootUuid: (envKey?: string) => Promise<string | null>;
|
|
717
|
-
/**
|
|
718
|
-
* Extends the current environment boot UUID without rotating its value.
|
|
719
|
-
*
|
|
720
|
-
* Every healthy backend may renew the environment-level key, so multi-instance
|
|
721
|
-
* deployments do not depend on whichever process happened to write it last. If
|
|
722
|
-
* Redis recovered after the key expired, the first refresher writes a new UUID.
|
|
723
|
-
*/
|
|
724
|
-
declare const refreshBootUuid: (envKey?: string) => Promise<void>;
|
|
725
|
-
interface BootUuidHeartbeat {
|
|
726
|
-
stop: () => void;
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* Keeps the Redis boot UUID alive for as long as this backend is running.
|
|
730
|
-
*
|
|
731
|
-
* The self-rescheduling timeout waits for each Redis operation to settle before
|
|
732
|
-
* scheduling another, so an outage cannot accumulate overlapping refreshes.
|
|
733
|
-
* The timer is unref'd and therefore never keeps an otherwise-finished process
|
|
734
|
-
* alive. Failures are logged and retried on the next interval.
|
|
735
|
-
*/
|
|
736
|
-
declare const startBootUuidHeartbeat: (envKey?: string) => BootUuidHeartbeat;
|
|
737
|
-
|
|
738
|
-
/**
|
|
739
|
-
* Hash a synchronized value under an EXPLICIT `{ mode, salt }` (SEC-13). The
|
|
740
|
-
* shared primitive the router calls with the same config the backend used, so
|
|
741
|
-
* the cross-env compare still holds:
|
|
742
|
-
* - `'plain'` → `sha256(value)` (today's behavior; `salt` ignored).
|
|
743
|
-
* - `'salted'` → `sha256(salt + value)`.
|
|
744
|
-
* - `'hmac'` → `HMAC-SHA256(key=salt, value)`.
|
|
745
|
-
* A `'salted'`/`'hmac'` mode with an empty resolved salt falls back to `'plain'`
|
|
746
|
-
* (and is the caller's responsibility to avoid) rather than silently hashing
|
|
747
|
-
* with an empty key.
|
|
748
|
-
*/
|
|
749
|
-
declare const hashSynchronizedValueWith: (config: Pick<HealthHashConfig, "mode"> & {
|
|
750
|
-
salt: string;
|
|
751
|
-
}, value: string) => string;
|
|
752
|
-
/**
|
|
753
|
-
* Resolve the effective health-hash config from `http.healthHash`, expanding the
|
|
754
|
-
* `'@bootUuid'` salt sentinel to the supplied boot UUID. Honoured for BOTH
|
|
755
|
-
* `'salted'` and `'hmac'` (the 0.2.0 default is `hmac` + `'@bootUuid'`, so the
|
|
756
|
-
* per-boot UUID becomes the HMAC key — the synchronized-env fingerprint rotates
|
|
757
|
-
* every restart instead of being a stable, dictionary-attackable `sha256`). The
|
|
758
|
-
* server passes its current boot UUID; the router, comparing, passes the same
|
|
759
|
-
* value it read from the backend's handshake. When the sentinel is in play but
|
|
760
|
-
* no boot UUID is available the config collapses to `'plain'` so the cross-env
|
|
761
|
-
* compare never silently diverges.
|
|
762
|
-
*/
|
|
763
|
-
declare const resolveHealthHashConfig: (bootUuid?: string | null) => {
|
|
764
|
-
mode: HealthHashConfig["mode"];
|
|
765
|
-
salt: string;
|
|
766
|
-
};
|
|
767
|
-
/**
|
|
768
|
-
* Safe-to-expose descriptor of the resolved health-hash config. The router needs
|
|
769
|
-
* to hash with the SAME `{ mode, salt }` the backend used, but a static `salt`
|
|
770
|
-
* (mode `'salted'`/`'hmac'` with a non-sentinel salt) is a SECRET and must never
|
|
771
|
-
* leave the backend. So `/_health` exposes only the `mode` + whether the salt is
|
|
772
|
-
* the `'@bootUuid'` sentinel (the bootUuid itself is already in the response).
|
|
773
|
-
* From this the router can reproduce the hash for `'plain'` and `'@bootUuid'`
|
|
774
|
-
* configs, and KNOWS it cannot reproduce a static-salt config (so it skips the
|
|
775
|
-
* compare instead of reporting a false drift).
|
|
776
|
-
*/
|
|
777
|
-
interface HealthHashDescriptor {
|
|
778
|
-
mode: HealthHashConfig['mode'];
|
|
779
|
-
bootUuidSalt: boolean;
|
|
780
|
-
}
|
|
781
|
-
/** Describe the active health-hash config for the `/_health` payload (no secret salt). */
|
|
782
|
-
declare const describeHealthHashConfig: () => HealthHashDescriptor;
|
|
783
|
-
/**
|
|
784
|
-
* Reproduce the backend's resolved `{ mode, salt }` from its `/_health`
|
|
785
|
-
* descriptor + the shared boot UUID. Returns `null` when the backend uses a
|
|
786
|
-
* STATIC salt the router cannot see — the caller must then skip the compare
|
|
787
|
-
* rather than hash with a different (default) config and report a false drift.
|
|
788
|
-
*/
|
|
789
|
-
declare const resolveHealthHashConfigFromDescriptor: (descriptor: HealthHashDescriptor, bootUuid: string) => {
|
|
790
|
-
mode: HealthHashConfig["mode"];
|
|
791
|
-
salt: string;
|
|
792
|
-
} | null;
|
|
793
|
-
declare const collectSynchronizedEnvKeys: () => string[];
|
|
794
|
-
/**
|
|
795
|
-
* Compute the `/_health` hash map for every synchronized env var. Reads
|
|
796
|
-
* `http.healthHash` (call-time) so a consumer's salt/HMAC opt-in applies;
|
|
797
|
-
* `bootUuid` is consulted only when the salt is the `'@bootUuid'` sentinel.
|
|
798
|
-
* The 0.2.0 DEFAULT (`{ mode: 'hmac', salt: '@bootUuid' }`) HMAC-keys the output
|
|
799
|
-
* on `bootUuid`; pass `mode: 'plain'` to restore the historical unsalted output.
|
|
800
|
-
*/
|
|
801
|
-
declare const computeSynchronizedEnvHashes: (bootUuid?: string | null) => Record<string, string | null>;
|
|
802
|
-
declare const hashSynchronizedValue: (value: string, bootUuid?: string | null) => string;
|
|
803
|
-
|
|
804
|
-
declare const initConsolelog: () => void;
|
|
805
|
-
|
|
806
|
-
declare const DEFAULT_REDACTED_LOG_KEYS: readonly string[];
|
|
807
|
-
declare const registerRedactedLogKeys: (keys: readonly string[]) => void;
|
|
808
|
-
declare const getRedactedLogKeys: () => readonly string[];
|
|
809
|
-
declare const isRedactedLogKey: (key: string) => boolean;
|
|
810
|
-
declare const resetRedactedLogKeysForTests: () => void;
|
|
811
|
-
/** Placeholder substituted for a redacted value. */
|
|
812
|
-
declare const REDACTED_PLACEHOLDER = "[redacted]";
|
|
813
|
-
declare const DEPTH_TRUNCATED_PLACEHOLDER = "[truncated: max depth]";
|
|
814
|
-
/**
|
|
815
|
-
* Recursively deep-copy `value`, replacing any property whose key matches the
|
|
816
|
-
* registered redacted-keys set (case-insensitive) with {@link REDACTED_PLACEHOLDER}
|
|
817
|
-
* (SYNC-17 defense-in-depth). Use it at raw log / `captureException` context
|
|
818
|
-
* sites so a token/password nested anywhere in a payload never lands verbatim in
|
|
819
|
-
* a logger sink or error-tracker breadcrumb.
|
|
820
|
-
*
|
|
821
|
-
* - Primitives pass through unchanged.
|
|
822
|
-
* - Arrays are mapped element-wise (array indices are never "keys", so an
|
|
823
|
-
* element is only redacted by its own nested object keys).
|
|
824
|
-
* - Cycles / over-deep graphs are cut at {@link MAX_SANITIZE_DEPTH} with
|
|
825
|
-
* {@link DEPTH_TRUNCATED_PLACEHOLDER}; the input is never mutated.
|
|
826
|
-
*/
|
|
827
|
-
declare const sanitizeForLog: (value: unknown, depth?: number, _seen?: WeakSet<object>) => unknown;
|
|
828
|
-
|
|
829
|
-
declare const getCookieValue: (cookieHeader: string | undefined, cookieName: string) => string | null;
|
|
830
|
-
declare const hasCookie: (cookieHeader: string | undefined, cookieName: string) => boolean;
|
|
831
|
-
/** Attributes a cookie-builder must honour, after applying any name prefix. */
|
|
832
|
-
interface CookiePrefixConstraints {
|
|
833
|
-
/** Effective cookie name (prefix prepended when applicable). */
|
|
834
|
-
name: string;
|
|
835
|
-
/** Whether `Secure` must be set (forced true by either prefix). */
|
|
836
|
-
secure: boolean;
|
|
837
|
-
/** Whether `Path=/` is forced (true under `__Host-`). */
|
|
838
|
-
forcePathRoot: boolean;
|
|
839
|
-
/** Whether a `Domain` attribute is FORBIDDEN (true under `__Host-`). */
|
|
840
|
-
forbidDomain: boolean;
|
|
841
|
-
}
|
|
842
|
-
/**
|
|
843
|
-
* Compute the attribute constraints a `__Host-` / `__Secure-` cookie-name prefix
|
|
844
|
-
* imposes (CORE-10/39), so the server's session + CSRF cookie builders apply the
|
|
845
|
-
* browser-enforced rules consistently:
|
|
846
|
-
*
|
|
847
|
-
* - `'__Host-'` → name prefixed, `Secure` forced, `Path=/` forced, `Domain` forbidden.
|
|
848
|
-
* - `'__Secure-'` → name prefixed, `Secure` forced.
|
|
849
|
-
* - `undefined` → name unchanged, no forced attributes (today's behavior).
|
|
850
|
-
*
|
|
851
|
-
* Pure + deterministic — the builder still owns serialization; this only
|
|
852
|
-
* resolves the constraints so server and any other cookie-site can't drift.
|
|
853
|
-
* `secureOverride` lets the caller force `Secure` independent of a prefix
|
|
854
|
-
* (mirrors `http.sessionCookieSecure`).
|
|
855
|
-
*/
|
|
856
|
-
declare const applyCookiePrefixConstraints: (baseName: string, prefix: "__Host-" | "__Secure-" | undefined, secureOverride?: boolean) => CookiePrefixConstraints;
|
|
857
|
-
|
|
858
|
-
/**
|
|
859
|
-
* HTTP API Utilities
|
|
860
|
-
*
|
|
861
|
-
* Helpers for HTTP API request handling including:
|
|
862
|
-
* - Smart HTTP method detection based on API name
|
|
863
|
-
* - Method validation
|
|
864
|
-
*/
|
|
865
|
-
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
866
|
-
/**
|
|
867
|
-
* Single source of truth for the GET-prefix naming heuristic
|
|
868
|
-
* (`get` / `fetch` / `list`). Used by both `inferHttpMethod` here and the
|
|
869
|
-
* client transport's `isGetMethod` fallback in `apiRequest.ts`, so the two
|
|
870
|
-
* prefix heuristics can't drift apart. Expects an already-lowercased leaf name.
|
|
871
|
-
*/
|
|
872
|
-
declare const isGetMethodName: (methodNameLower: string) => boolean;
|
|
873
|
-
/**
|
|
874
|
-
* Infer the HTTP method from an API name based on common naming conventions.
|
|
875
|
-
*
|
|
876
|
-
* - Names starting with "get" → GET
|
|
877
|
-
* - Names starting with "delete" or "remove" → DELETE
|
|
878
|
-
* - Names starting with "update" or "edit" → PUT
|
|
879
|
-
* - Everything else → POST (create operations, actions)
|
|
880
|
-
*
|
|
881
|
-
* @example
|
|
882
|
-
* ```typescript
|
|
883
|
-
* inferHttpMethod('getUserData') // → 'GET'
|
|
884
|
-
* inferHttpMethod('deleteAccount') // → 'DELETE'
|
|
885
|
-
* inferHttpMethod('updateProfile') // → 'PUT'
|
|
886
|
-
* inferHttpMethod('createUser') // → 'POST'
|
|
887
|
-
* inferHttpMethod('sendEmail') // → 'POST'
|
|
888
|
-
* ```
|
|
889
|
-
*/
|
|
890
|
-
declare const inferHttpMethod: (apiName: string) => HttpMethod;
|
|
891
|
-
/**
|
|
892
|
-
* Get the effective HTTP method for an API.
|
|
893
|
-
* Uses explicit export if available, otherwise infers from name.
|
|
894
|
-
*/
|
|
895
|
-
declare const getEffectiveHttpMethod: (apiConfig: {
|
|
896
|
-
httpMethod?: HttpMethod;
|
|
897
|
-
}, apiName: string) => HttpMethod;
|
|
898
|
-
/**
|
|
899
|
-
* Check if an HTTP method is valid for an API.
|
|
900
|
-
*
|
|
901
|
-
* @param requestMethod - The method from the HTTP request
|
|
902
|
-
* @param allowedMethod - The method the API accepts
|
|
903
|
-
* @returns true only when the method matches the route's allowed method.
|
|
904
|
-
*
|
|
905
|
-
* NOTE: `OPTIONS` is intentionally NOT treated as allowed here. A custom-route
|
|
906
|
-
* author wiring this into their dispatch must answer CORS preflights BEFORE the
|
|
907
|
-
* route check — letting `OPTIONS` through would execute the handler on a request
|
|
908
|
-
* the CSRF middleware treats as non-state-changing (CSRF-exempt), opening a
|
|
909
|
-
* bypass on method-locked routes.
|
|
910
|
-
*/
|
|
911
|
-
declare const isMethodAllowed: (requestMethod: string, allowedMethod: HttpMethod) => boolean;
|
|
912
|
-
|
|
913
|
-
declare const ROOT_DIR: string;
|
|
914
|
-
declare const getSrcDir: () => string;
|
|
915
|
-
declare const getServerDir: () => string;
|
|
916
|
-
declare const getSharedDir: () => string;
|
|
917
|
-
declare const getUploadsDir: () => string;
|
|
918
|
-
declare const getPublicDir: () => string;
|
|
919
|
-
declare const getServerFunctionDirs: () => string[];
|
|
920
|
-
/**
|
|
921
|
-
* @deprecated Use `getServerFunctionDirs()` instead. Returns the first
|
|
922
|
-
* directory from the array, or `'functions'` resolved against the workspace
|
|
923
|
-
* root when no directories are configured.
|
|
924
|
-
*/
|
|
925
|
-
declare const getServerFunctionsDir: () => string;
|
|
926
|
-
declare const getGeneratedSocketTypesPath: () => string;
|
|
927
|
-
declare const getGeneratedApiSchemasPath: () => string;
|
|
928
|
-
declare const getGeneratedApiDocsPath: () => string;
|
|
929
|
-
declare const SRC_DIR: string;
|
|
930
|
-
/** @deprecated use `getServerDir()` instead. */
|
|
931
|
-
declare const SERVER_DIR: string;
|
|
932
|
-
/** @deprecated use `getSharedDir()` instead. */
|
|
933
|
-
declare const SHARED_DIR: string;
|
|
934
|
-
/** @deprecated use `getUploadsDir()` instead. */
|
|
935
|
-
declare const UPLOADS_DIR: string;
|
|
936
|
-
/** @deprecated use `getPublicDir()` instead. */
|
|
937
|
-
declare const PUBLIC_DIR: string;
|
|
938
|
-
/** @deprecated use `getServerFunctionsDir()` instead. */
|
|
939
|
-
declare const SERVER_FUNCTIONS_DIR: string;
|
|
940
|
-
/** @deprecated use `getGeneratedSocketTypesPath()` instead. */
|
|
941
|
-
declare const GENERATED_SOCKET_TYPES_PATH: string;
|
|
942
|
-
/** @deprecated use `getGeneratedApiSchemasPath()` instead. */
|
|
943
|
-
declare const GENERATED_API_SCHEMAS_PATH: string;
|
|
944
|
-
/** @deprecated use `getGeneratedApiDocsPath()` instead. */
|
|
945
|
-
declare const GENERATED_API_DOCS_PATH: string;
|
|
946
|
-
declare const TSCONFIG_ALIAS_FILES: string[];
|
|
947
|
-
declare const resolveFromRoot: (...segments: string[]) => string;
|
|
948
|
-
|
|
949
|
-
type SocketMiddleware = (socket: Socket, next: (err?: Error) => void) => void;
|
|
950
|
-
/**
|
|
951
|
-
* Register a Socket.io middleware that will be wired via `io.use(...)` when
|
|
952
|
-
* the framework boots the socket server. Multiple middlewares can be
|
|
953
|
-
* registered — they run in registration order before any `connect`
|
|
954
|
-
* handler fires.
|
|
955
|
-
*/
|
|
956
|
-
declare const registerSocketMiddleware: (mw: SocketMiddleware) => void;
|
|
957
|
-
/** Read the registered middleware list (read-only view). */
|
|
958
|
-
declare const getSocketMiddlewares: () => readonly SocketMiddleware[];
|
|
959
|
-
/** Test-only / hot-reload helper. Drops every registered middleware. */
|
|
960
|
-
declare const clearSocketMiddlewares: () => void;
|
|
961
|
-
/**
|
|
962
|
-
* Wire every registered middleware into the running Socket.io server.
|
|
963
|
-
* Called by `@luckystack/server`'s `loadSocket` after the `SocketIOServer`
|
|
964
|
-
* is constructed and before any `connect` handler is attached, so custom
|
|
965
|
-
* middlewares run before framework handlers see the socket.
|
|
966
|
-
*/
|
|
967
|
-
declare const applySocketMiddlewares: (io: Server) => void;
|
|
968
|
-
|
|
969
|
-
interface CsrfCookieOptions {
|
|
970
|
-
sameSite?: 'strict' | 'lax' | 'none';
|
|
971
|
-
secure?: boolean;
|
|
972
|
-
/**
|
|
973
|
-
* Whether the login-absent double-submit cookie is HttpOnly. The client gets
|
|
974
|
-
* the matching header value from the same-origin `GET /auth/csrf` JSON body,
|
|
975
|
-
* so either value works; `false` remains the compatibility default.
|
|
976
|
-
*/
|
|
977
|
-
httpOnly?: boolean;
|
|
978
|
-
path?: string;
|
|
979
|
-
maxAgeMs?: number;
|
|
980
|
-
}
|
|
981
|
-
interface CsrfConfig {
|
|
982
|
-
/** Cookie name used to deliver the CSRF token to the browser. */
|
|
983
|
-
cookieName: string;
|
|
984
|
-
/** Request-header name the client attaches on state-changing requests. */
|
|
985
|
-
headerName: string;
|
|
986
|
-
/** Length in bytes of the random token minted by the session layer. */
|
|
987
|
-
tokenLength: number;
|
|
988
|
-
cookieOptions: CsrfCookieOptions;
|
|
989
|
-
}
|
|
990
|
-
declare const DEFAULT_CSRF_CONFIG: CsrfConfig;
|
|
991
|
-
/**
|
|
992
|
-
* Override one or more CSRF settings. Merges shallowly with the current
|
|
993
|
-
* config; `cookieOptions` is deep-merged so a partial override of (say)
|
|
994
|
-
* `sameSite` does not clobber `path` / `maxAgeMs`. Last-write-wins.
|
|
995
|
-
*/
|
|
996
|
-
declare const registerCsrfConfig: (input: Partial<CsrfConfig>) => void;
|
|
997
|
-
/** Read the active config at call time (never at module load). */
|
|
998
|
-
declare const getCsrfConfig: () => CsrfConfig;
|
|
999
|
-
/** Test-only — restore defaults between scenarios. */
|
|
1000
|
-
declare const resetCsrfConfigForTests: () => void;
|
|
1001
|
-
|
|
1002
|
-
declare const serveAvatar: ({ routePath, res, }: {
|
|
1003
|
-
routePath: string;
|
|
1004
|
-
res: ServerResponse;
|
|
1005
|
-
}) => Promise<void>;
|
|
1006
|
-
|
|
1007
|
-
interface ProcessUploadInput {
|
|
1008
|
-
/** User the upload is associated with. */
|
|
1009
|
-
userId: string;
|
|
1010
|
-
/** Content type from the source (e.g. parsed from a data URL prefix). */
|
|
1011
|
-
contentType: string;
|
|
1012
|
-
/** Raw upload bytes — used for `sizeBytes` and passed to the encoder. */
|
|
1013
|
-
buffer: Buffer;
|
|
1014
|
-
/** Identifier for the upload category. Defaults to `'avatar'`. */
|
|
1015
|
-
uploadKind?: string;
|
|
1016
|
-
/** Final file name written to disk (used in the `onUploadComplete` payload). */
|
|
1017
|
-
fileName: string;
|
|
1018
|
-
/**
|
|
1019
|
-
* Project-supplied encode + write step. Receives the raw buffer, does
|
|
1020
|
-
* whatever encoding/resizing the installer wants (sharp, ffmpeg, raw fs
|
|
1021
|
-
* write, S3 upload, etc.), and returns the final byte size on disk —
|
|
1022
|
-
* pass through `buffer.byteLength` if you don't have a meaningful
|
|
1023
|
-
* post-encode size.
|
|
1024
|
-
*/
|
|
1025
|
-
encodeAndSave: (buffer: Buffer) => Promise<number>;
|
|
1026
|
-
}
|
|
1027
|
-
type ProcessUploadResult = {
|
|
1028
|
-
status: 'success';
|
|
1029
|
-
sizeBytes: number;
|
|
1030
|
-
} | {
|
|
1031
|
-
status: 'rejected';
|
|
1032
|
-
errorCode: string;
|
|
1033
|
-
} | {
|
|
1034
|
-
status: 'error';
|
|
1035
|
-
reason: string;
|
|
1036
|
-
cause?: unknown;
|
|
1037
|
-
};
|
|
1038
|
-
/**
|
|
1039
|
-
* Run a project-side upload through the framework's hook contract.
|
|
1040
|
-
*
|
|
1041
|
-
* Order of operations:
|
|
1042
|
-
* 1. Dispatch `onUploadStart` — handlers may return a stop signal to reject
|
|
1043
|
-
* the upload (content moderation, virus scanner, quota check).
|
|
1044
|
-
* 2. Run the project-supplied `encodeAndSave` callback (wrapped in tryCatch).
|
|
1045
|
-
* 3. Dispatch `onUploadComplete` with the final `sizeBytes`.
|
|
1046
|
-
*
|
|
1047
|
-
* Returns one of three statuses so the caller can map to its own response
|
|
1048
|
-
* envelope without inspecting hook internals.
|
|
1049
|
-
*/
|
|
1050
|
-
declare const processUpload: (input: ProcessUploadInput) => Promise<ProcessUploadResult>;
|
|
1051
|
-
|
|
1052
|
-
interface BindAddress {
|
|
1053
|
-
ip: string;
|
|
1054
|
-
port: number;
|
|
1055
|
-
}
|
|
1056
|
-
interface IntendedBindAddress extends BindAddress {
|
|
1057
|
-
configuredPort?: number;
|
|
1058
|
-
}
|
|
1059
|
-
declare const registerBindAddress: (address: IntendedBindAddress) => void;
|
|
1060
|
-
declare const registerBoundAddress: (address: BindAddress) => void;
|
|
1061
|
-
declare const getBindAddress: () => {
|
|
1062
|
-
ip: string;
|
|
1063
|
-
port: string;
|
|
1064
|
-
};
|
|
1065
|
-
declare const resolveDevCallbackUrl: (callbackUrl: string) => string;
|
|
1066
|
-
|
|
1067
|
-
interface AvatarConfig {
|
|
1068
|
-
/**
|
|
1069
|
-
* File format(s) the framework will look for on disk, in order. The first
|
|
1070
|
-
* one that exists wins. Each entry is `{ extension, contentType }` so the
|
|
1071
|
-
* `Content-Type` response header matches what the file actually is.
|
|
1072
|
-
* Default: `[{ extension: 'webp', contentType: 'image/webp' }]`.
|
|
1073
|
-
*/
|
|
1074
|
-
formats: {
|
|
1075
|
-
extension: string;
|
|
1076
|
-
contentType: string;
|
|
1077
|
-
}[];
|
|
1078
|
-
/**
|
|
1079
|
-
* `Cache-Control` value sent with the avatar response.
|
|
1080
|
-
* Default: `'public, max-age=86400'` (24 hours).
|
|
1081
|
-
*/
|
|
1082
|
-
cacheControl: string;
|
|
1083
|
-
}
|
|
1084
|
-
declare const DEFAULT_AVATAR_CONFIG: AvatarConfig;
|
|
1085
|
-
type AvatarConfigInput = DeepPartial<AvatarConfig>;
|
|
1086
|
-
declare const registerAvatarConfig: (config: AvatarConfigInput) => void;
|
|
1087
|
-
declare const getAvatarConfig: () => AvatarConfig;
|
|
1088
|
-
|
|
1089
|
-
interface getParamsType {
|
|
1090
|
-
method: string;
|
|
1091
|
-
req: IncomingMessage;
|
|
1092
|
-
res: ServerResponse;
|
|
1093
|
-
queryString?: string;
|
|
1094
|
-
}
|
|
1095
|
-
declare function getParams({ method, req, res: _res, queryString }: getParamsType): Promise<Record<string, unknown> | null>;
|
|
1096
|
-
|
|
1097
|
-
/**
|
|
1098
|
-
* Extract the authentication token from a Socket.io connection.
|
|
1099
|
-
*
|
|
1100
|
-
* Checks `config.sessionBasedToken` to determine where to look:
|
|
1101
|
-
* - `true`: Token is in `socket.handshake.auth.token` (sessionStorage on client)
|
|
1102
|
-
* - `false`: Token is in cookies via `socket.handshake.headers.cookie`
|
|
1103
|
-
*
|
|
1104
|
-
* @param socket - The Socket.io socket instance
|
|
1105
|
-
* @returns The token string or null if not found
|
|
1106
|
-
*/
|
|
1107
|
-
declare const extractTokenFromSocket: (socket: Pick<Socket, "handshake">) => string | null;
|
|
1108
|
-
|
|
1109
|
-
/**
|
|
1110
|
-
* Extract the authentication token from an HTTP request.
|
|
1111
|
-
*
|
|
1112
|
-
* Supports tokens from:
|
|
1113
|
-
* - `Authorization: Bearer <token>` header
|
|
1114
|
-
* - the configured session cookie
|
|
1115
|
-
*
|
|
1116
|
-
* @param req - The HTTP incoming message
|
|
1117
|
-
* @returns The token string or null if not found
|
|
1118
|
-
*/
|
|
1119
|
-
declare const extractTokenFromRequest: (req: IncomingMessage) => string | null;
|
|
1120
|
-
|
|
1121
|
-
/**
|
|
1122
|
-
* Stable sentinel returned when no usable client address could be resolved.
|
|
1123
|
-
* Kept as a single exported constant so every rate-limit key-site funnels
|
|
1124
|
-
* missing addresses into one deterministic bucket instead of ad-hoc literals.
|
|
1125
|
-
*/
|
|
1126
|
-
declare const UNKNOWN_CLIENT_IP = "unknown";
|
|
1127
|
-
interface ResolveClientIpParams {
|
|
1128
|
-
/**
|
|
1129
|
-
* The raw transport peer address — `socket.handshake.address` (Socket.io)
|
|
1130
|
-
* or `req.socket.remoteAddress` (raw HTTP). May be `undefined`/`null` when
|
|
1131
|
-
* the connection has already been torn down.
|
|
1132
|
-
*/
|
|
1133
|
-
rawAddress: string | null | undefined;
|
|
1134
|
-
/** Request / handshake headers. Only consulted when `trustProxy` is true. */
|
|
1135
|
-
headers: IncomingHttpHeaders;
|
|
1136
|
-
/**
|
|
1137
|
-
* Whether a known reverse proxy sits in front of this server. DEFAULT false.
|
|
1138
|
-
* When false the raw peer address is returned verbatim (only IPv4-mapped
|
|
1139
|
-
* IPv6 canonicalization is applied), preserving historical behaviour. Only
|
|
1140
|
-
* enable when a trusted proxy populates `X-Forwarded-For` / `X-Real-IP`,
|
|
1141
|
-
* otherwise a client can spoof its own IP via those headers.
|
|
1142
|
-
*/
|
|
1143
|
-
trustProxy?: boolean;
|
|
1144
|
-
/**
|
|
1145
|
-
* Number of proxy hops to skip from the RIGHT of `X-Forwarded-For` when
|
|
1146
|
-
* `trustProxy` is on (CORE-O3). The rightmost entries are appended by your own
|
|
1147
|
-
* trusted proxies; the resolved client IP is the entry that many hops in from
|
|
1148
|
-
* the end. The leftmost hop is client-controlled and is never trusted. DEFAULT
|
|
1149
|
-
* 1 (the immediate upstream proxy). Clamped to the list length so an over-large
|
|
1150
|
-
* count falls back to the leftmost trusted hop rather than the spoofable entry.
|
|
1151
|
-
*/
|
|
1152
|
-
trustedProxyHopCount?: number;
|
|
1153
|
-
}
|
|
1154
|
-
/**
|
|
1155
|
-
* Whether a resolved client IP is a loopback / unresolved address — used by the
|
|
1156
|
-
* api/sync transports to honour `rateLimiting.skipLoopbackInDev` (api F5/F11).
|
|
1157
|
-
* Matches IPv4 loopback (`127.0.0.0/8`), IPv6 loopback (`::1`), the IPv4-mapped
|
|
1158
|
-
* form (`::ffff:127.0.0.1`, already canonicalized away by {@link resolveClientIp}),
|
|
1159
|
-
* and the {@link UNKNOWN_CLIENT_IP} sentinel. Pass an already-resolved IP.
|
|
1160
|
-
*/
|
|
1161
|
-
declare const isLoopbackIp: (ip: string) => boolean;
|
|
1162
|
-
/**
|
|
1163
|
-
* Derive the deterministic, non-reversible bucket component for a session
|
|
1164
|
-
* token. The same token always yields the same value (bucket identity is
|
|
1165
|
-
* preserved) while the raw token never appears in the returned string.
|
|
1166
|
-
* Used by api AND sync rate-limit key builders — lives in core so both
|
|
1167
|
-
* packages can share one implementation without a cross-package dependency.
|
|
1168
|
-
*/
|
|
1169
|
-
declare const deriveTokenBucketId: (token: string) => string;
|
|
1170
|
-
declare const resolveClientIp: ({ rawAddress, headers, trustProxy, trustedProxyHopCount }: ResolveClientIpParams) => string;
|
|
1171
|
-
|
|
1172
|
-
interface SessionSaveResult {
|
|
1173
|
-
ok: boolean;
|
|
1174
|
-
errorCode?: string;
|
|
1175
|
-
}
|
|
1176
|
-
interface SessionLogoutInput {
|
|
1177
|
-
token: string | null;
|
|
1178
|
-
socket?: Socket;
|
|
1179
|
-
userId?: string | null;
|
|
1180
|
-
skipSessionDelete?: boolean;
|
|
1181
|
-
}
|
|
1182
|
-
interface SessionProvider {
|
|
1183
|
-
getSession: (token: string | null) => Promise<BaseSessionLayout | null>;
|
|
1184
|
-
saveSession: (token: string, data: BaseSessionLayout, newUser?: boolean, options?: {
|
|
1185
|
-
supersedeToken?: string;
|
|
1186
|
-
}) => Promise<SessionSaveResult>;
|
|
1187
|
-
deleteSession: (token: string, options?: {
|
|
1188
|
-
skipSocketLogout?: boolean;
|
|
1189
|
-
}) => Promise<boolean>;
|
|
1190
|
-
logout: (input: SessionLogoutInput) => Promise<void>;
|
|
1191
|
-
}
|
|
1192
|
-
declare const registerSessionProvider: (next: SessionProvider) => void;
|
|
1193
|
-
declare const getRegisteredSessionProvider: () => SessionProvider | null;
|
|
1194
|
-
declare const isSessionProviderRegistered: () => boolean;
|
|
1195
|
-
declare const resetSessionProviderForTests: () => void;
|
|
1196
|
-
declare const readSession: (token: string | null) => Promise<BaseSessionLayout | null>;
|
|
1197
|
-
/**
|
|
1198
|
-
* Persist a session via the registered provider, or no-op if no login provider
|
|
1199
|
-
* is registered (unauthenticated-app case).
|
|
1200
|
-
*
|
|
1201
|
-
* **WARNING**: when no session provider is registered, this returns `{ ok: true }`
|
|
1202
|
-
* as a no-op — the session is NOT actually persisted anywhere. Callers that
|
|
1203
|
-
* check `result.ok` before considering a user logged in will incorrectly
|
|
1204
|
-
* proceed as if the session was saved. This is intentional for apps that do not
|
|
1205
|
-
* use `@luckystack/login`, but on a partial install (login package misconfigured
|
|
1206
|
-
* or not yet registered) the failure is silent rather than loud.
|
|
1207
|
-
* Use `isSessionProviderRegistered()` at boot to verify the provider is present.
|
|
1208
|
-
*/
|
|
1209
|
-
declare const writeSession: (token: string, data: BaseSessionLayout, newUser?: boolean, options?: {
|
|
1210
|
-
supersedeToken?: string;
|
|
1211
|
-
}) => Promise<SessionSaveResult>;
|
|
1212
|
-
declare const removeSession: (token: string, options?: {
|
|
1213
|
-
skipSocketLogout?: boolean;
|
|
1214
|
-
}) => Promise<boolean>;
|
|
1215
|
-
declare const performLogout: (input: SessionLogoutInput) => Promise<void>;
|
|
1216
|
-
|
|
1217
|
-
/**
|
|
1218
|
-
* Shared validation utilities for API and Sync request handlers.
|
|
1219
|
-
* Extracted to avoid code duplication between handleApiRequest and handleSyncRequest.
|
|
1220
|
-
*/
|
|
1221
|
-
|
|
1222
|
-
/**
|
|
1223
|
-
* Check if a value is falsy (false, 0, 0n, '', null, undefined, or NaN)
|
|
1224
|
-
*/
|
|
1225
|
-
declare const isFalsy: (value: unknown) => boolean;
|
|
1226
|
-
interface ValidationResult$1 {
|
|
1227
|
-
status: 'success' | 'error';
|
|
1228
|
-
errorCode?: string;
|
|
1229
|
-
errorParams?: {
|
|
1230
|
-
key: string;
|
|
1231
|
-
value: string | number | boolean;
|
|
1232
|
-
}[];
|
|
1233
|
-
httpStatus?: number;
|
|
1234
|
-
}
|
|
1235
|
-
/**
|
|
1236
|
-
* Validate a request against authentication requirements.
|
|
1237
|
-
*
|
|
1238
|
-
* Multiple constraints on a single `additional` entry are AND'd: every
|
|
1239
|
-
* specified field (`nullish`, `type`, `value`, `mustBeFalsy`) must pass
|
|
1240
|
-
* for that entry to validate. Each entry across the array is also AND'd:
|
|
1241
|
-
* the request must pass every entry to succeed.
|
|
1242
|
-
*
|
|
1243
|
-
* Constraint semantics:
|
|
1244
|
-
* - `nullish: true` — `val` must be `null` or `undefined`.
|
|
1245
|
-
* - `nullish: false` — `val` must NOT be `null` or `undefined`.
|
|
1246
|
-
* - `type: 'string'` — `typeof val === 'string'` (skipped when val is null/undefined).
|
|
1247
|
-
* - `value: <x>` — strict equality `val === x`. Omit the field entirely if you
|
|
1248
|
-
* don't want this check; setting `value: undefined`
|
|
1249
|
-
* explicitly means "val must be undefined".
|
|
1250
|
-
* - `mustBeFalsy: true` — `isFalsy(val)`.
|
|
1251
|
-
* - `mustBeFalsy: false` — `!isFalsy(val)`.
|
|
1252
|
-
*
|
|
1253
|
-
* **SECURITY CONTRACT**: this function does NOT check `auth.login`. Login
|
|
1254
|
-
* enforcement is the responsibility of the surrounding API/sync handler — it
|
|
1255
|
-
* must verify the session exists before calling `validateRequest`. Callers
|
|
1256
|
-
* that omit the login check will allow unauthenticated requests through the
|
|
1257
|
-
* `additional[]` predicates even when `auth.login: true` is declared, because
|
|
1258
|
-
* `validateRequest` short-circuits to success when `additional` is empty.
|
|
1259
|
-
* The expected call pattern is:
|
|
1260
|
-
* 1. Check session exists (enforce `auth.login`).
|
|
1261
|
-
* 2. Call `validateRequest({ auth, user })` to enforce `auth.additional[]`.
|
|
1262
|
-
*/
|
|
1263
|
-
declare const validateRequest: ({ auth, user }: {
|
|
1264
|
-
auth: AuthProps;
|
|
1265
|
-
user?: BaseSessionLayout | null;
|
|
1266
|
-
}) => ValidationResult$1;
|
|
1267
|
-
|
|
1268
|
-
/**
|
|
1269
|
-
* Rate Limiter
|
|
1270
|
-
*
|
|
1271
|
-
* The framework's rate-limit dispatch is pluggable via
|
|
1272
|
-
* `registerRateLimitStrategy()`. The built-in `defaultRateLimitStrategy`
|
|
1273
|
-
* implements in-memory counters with optional Redis backing for
|
|
1274
|
-
* multi-instance deployments. Consumers can plug in alternative strategies
|
|
1275
|
-
* (token-bucket, sliding-window, per-user tiers, edge-KV-backed, etc.) by
|
|
1276
|
-
* implementing the `RateLimitStrategy` interface.
|
|
1277
|
-
*/
|
|
1278
|
-
interface CheckRateLimitParams {
|
|
1279
|
-
/** Unique key for rate limiting (e.g., "user:123:api:getData") */
|
|
1280
|
-
key: string;
|
|
1281
|
-
/** Maximum requests allowed in window */
|
|
1282
|
-
limit: number;
|
|
1283
|
-
/** Time window in milliseconds (default: 60_000 = 1 minute) */
|
|
1284
|
-
windowMs?: number;
|
|
1285
|
-
}
|
|
1286
|
-
interface RateLimitResult {
|
|
1287
|
-
/** Whether the request is allowed */
|
|
1288
|
-
allowed: boolean;
|
|
1289
|
-
/** Remaining requests in current window */
|
|
1290
|
-
remaining: number;
|
|
1291
|
-
/** Seconds until rate limit resets */
|
|
1292
|
-
resetIn: number;
|
|
1293
|
-
}
|
|
1294
|
-
/**
|
|
1295
|
-
* Pluggable rate-limit backend. The framework calls these methods through
|
|
1296
|
-
* the active strategy slot. Default implementation is memory + Redis.
|
|
1297
|
-
* Replace via `registerRateLimitStrategy(...)` at boot.
|
|
1298
|
-
*/
|
|
1299
|
-
interface RateLimitStrategy {
|
|
1300
|
-
/** Human-readable identifier (used in logs/diagnostics). */
|
|
1301
|
-
name: string;
|
|
1302
|
-
/** Increment the counter for `key` and return whether the request is allowed. */
|
|
1303
|
-
check(params: CheckRateLimitParams): Promise<RateLimitResult>;
|
|
1304
|
-
/** Read current status without incrementing — useful for response headers. */
|
|
1305
|
-
getStatus(key: string, limit: number): Promise<RateLimitResult>;
|
|
1306
|
-
/** Reset the counter for a single key. */
|
|
1307
|
-
clear(key: string): Promise<void>;
|
|
1308
|
-
/** Reset all counters under this strategy's namespace. */
|
|
1309
|
-
clearAll(): Promise<void>;
|
|
1310
|
-
}
|
|
1311
|
-
/**
|
|
1312
|
-
* Built-in strategy: in-memory counters with optional Redis backing for
|
|
1313
|
-
* multi-instance deployments. Mode is controlled by
|
|
1314
|
-
* `projectConfig.rateLimiting.store` (`'memory'` or `'redis'`). When `redis`
|
|
1315
|
-
* mode fails (e.g. Redis disconnect), the strategy silently degrades to
|
|
1316
|
-
* in-memory and logs a one-shot warning.
|
|
1317
|
-
*/
|
|
1318
|
-
declare const defaultRateLimitStrategy: RateLimitStrategy;
|
|
1319
|
-
/**
|
|
1320
|
-
* Replace the active rate-limit backend. Pass a strategy that implements
|
|
1321
|
-
* `RateLimitStrategy`. Common use cases:
|
|
1322
|
-
*
|
|
1323
|
-
* - Token-bucket / sliding-window algorithms for smoother burst handling.
|
|
1324
|
-
* - Per-user tier limits (premium accounts get higher caps).
|
|
1325
|
-
* - Cloudflare-Workers-KV or upstash-rest-backed counters for edge.
|
|
1326
|
-
* - No-op strategy for test environments.
|
|
1327
|
-
*
|
|
1328
|
-
* Call this once at boot (typically from `luckystack/server/index.ts`)
|
|
1329
|
-
* BEFORE the first request lands. Subsequent calls overwrite the previous
|
|
1330
|
-
* registration (last-write-wins).
|
|
1331
|
-
*/
|
|
1332
|
-
declare const registerRateLimitStrategy: (strategy: RateLimitStrategy) => void;
|
|
1333
|
-
/** Read the currently-active strategy (defaults to the built-in). */
|
|
1334
|
-
declare const getRateLimitStrategy: () => RateLimitStrategy;
|
|
1335
|
-
/**
|
|
1336
|
-
* Check if a request is allowed under rate limiting rules.
|
|
1337
|
-
* Increments the counter for the key if allowed.
|
|
1338
|
-
*/
|
|
1339
|
-
declare const checkRateLimit: (params: CheckRateLimitParams) => Promise<RateLimitResult>;
|
|
1340
|
-
/**
|
|
1341
|
-
* Get current rate limit status without incrementing counter.
|
|
1342
|
-
* Useful for rate limit headers in responses.
|
|
1343
|
-
*/
|
|
1344
|
-
declare const getRateLimitStatus: (key: string, limit: number) => Promise<RateLimitResult>;
|
|
1345
|
-
/**
|
|
1346
|
-
* Clear rate limit for a specific key.
|
|
1347
|
-
* Useful for admin overrides or testing.
|
|
1348
|
-
*/
|
|
1349
|
-
declare const clearRateLimit: (key: string) => Promise<void>;
|
|
1350
|
-
/**
|
|
1351
|
-
* Clear all rate limits.
|
|
1352
|
-
* Intended for testing or dev-mode server reset only.
|
|
1353
|
-
* CORE-O8: throws in production to prevent accidental global counter wipe across
|
|
1354
|
-
* all tenants. If you need a production-safe reset, pass a scoped key to
|
|
1355
|
-
* `clearRateLimit` or implement a tenant-namespaced strategy.
|
|
1356
|
-
*/
|
|
1357
|
-
declare const clearAllRateLimits: () => Promise<void>;
|
|
1358
|
-
|
|
1359
|
-
type ValidationResult = {
|
|
1360
|
-
status: 'success';
|
|
1361
|
-
} | {
|
|
1362
|
-
status: 'error';
|
|
1363
|
-
message: string;
|
|
1364
|
-
};
|
|
1365
|
-
declare const validateInputByType: ({ typeText, value, rootKey, filePath, }: {
|
|
1366
|
-
typeText?: string;
|
|
1367
|
-
value: unknown;
|
|
1368
|
-
rootKey: string;
|
|
1369
|
-
filePath?: string;
|
|
1370
|
-
}) => Promise<ValidationResult>;
|
|
1371
|
-
|
|
1372
|
-
declare const normalizeOrigin: ({ value, secure }: {
|
|
1373
|
-
value: string;
|
|
1374
|
-
secure: boolean;
|
|
1375
|
-
}) => string;
|
|
1376
|
-
declare const allowedOrigin: (origin: string) => boolean;
|
|
1377
|
-
|
|
1378
|
-
declare const registerSyncAbortController: (socketId: string, cb: string, controller: AbortController) => string;
|
|
1379
|
-
declare const unregisterSyncAbortController: (key: string) => void;
|
|
1380
|
-
declare const abortSyncByCb: (socketId: string, cb: string) => boolean;
|
|
1381
|
-
declare const registerApiAbortController: (socketId: string, responseIndex: number | string, controller: AbortController) => string;
|
|
1382
|
-
declare const unregisterApiAbortController: (key: string) => void;
|
|
1383
|
-
declare const abortApiByResponseIndex: (socketId: string, responseIndex: number | string) => boolean;
|
|
1384
|
-
declare const abortAllForSocket: (socketId: string) => void;
|
|
1385
|
-
|
|
1386
|
-
/**
|
|
1387
|
-
* Handle returned by {@link issueOneTimeToken}. `token` is the RAW token to hand
|
|
1388
|
-
* to the caller (embed in the emailed URL); call `store()` to persist its hash
|
|
1389
|
-
* to Redis with the configured TTL. Persisting is a separate step so the caller
|
|
1390
|
-
* can run veto logic (hooks, validation) between minting and committing without
|
|
1391
|
-
* leaving an orphan Redis key.
|
|
1392
|
-
*/
|
|
1393
|
-
interface OneTimeTokenHandle {
|
|
1394
|
-
/** The raw, single-use token. Returned exactly once; never stored verbatim. */
|
|
1395
|
-
token: string;
|
|
1396
|
-
/** Persist `sha256(token)` -> `payload` in Redis with the issue TTL. */
|
|
1397
|
-
store(): Promise<void>;
|
|
1398
|
-
}
|
|
1399
|
-
/**
|
|
1400
|
-
* Mint a one-time token in `namespace` carrying `payload`, redeemable for `ttl`
|
|
1401
|
-
* seconds. Returns the raw token plus a `store()` committer. The payload is
|
|
1402
|
-
* serialised to a string (objects via JSON); on redemption it is returned as a
|
|
1403
|
-
* string and the caller parses it (or use {@link consumeOneTimeTokenJson}).
|
|
1404
|
-
*
|
|
1405
|
-
* @param namespace Key namespace (e.g. `'-pwreset'`, `'-email-change'`) — routed
|
|
1406
|
-
* through `formatKey` so the active key formatter / project prefix applies.
|
|
1407
|
-
* @param ttlSeconds Time-to-live in seconds for the stored hash.
|
|
1408
|
-
* @param payload Value bound to the token. A string is stored verbatim; any
|
|
1409
|
-
* other value is `JSON.stringify`-d.
|
|
1410
|
-
*/
|
|
1411
|
-
declare const issueOneTimeToken: (namespace: string, ttlSeconds: number, payload: string | Record<string, unknown>) => OneTimeTokenHandle;
|
|
1412
|
-
/**
|
|
1413
|
-
* Atomically validate + consume a one-time token from `namespace`. Returns the
|
|
1414
|
-
* stored payload STRING on success (and deletes the entry, so it can never be
|
|
1415
|
-
* reused), or `null` when the token is missing / expired / malformed / already
|
|
1416
|
-
* consumed. A single `MULTI` `GET`+`DEL` guarantees at-most-once redemption.
|
|
1417
|
-
*/
|
|
1418
|
-
declare const consumeOneTimeToken: (namespace: string, token: string) => Promise<string | null>;
|
|
1419
|
-
/**
|
|
1420
|
-
* Convenience wrapper over {@link consumeOneTimeToken} that JSON-parses the
|
|
1421
|
-
* stored payload into `T`. Returns `null` on miss OR on a malformed JSON
|
|
1422
|
-
* payload. Use when the token was issued with an object payload.
|
|
1423
|
-
*/
|
|
1424
|
-
declare const consumeOneTimeTokenJson: <T>(namespace: string, token: string) => Promise<T | null>;
|
|
1425
|
-
declare const oneTimeTokenKey: (namespace: string, token: string) => string;
|
|
1426
|
-
|
|
1427
|
-
/**
|
|
1428
|
-
* Core hook contract: framework-generic payload shapes + augmentable registry.
|
|
1429
|
-
*
|
|
1430
|
-
* Feature packages (login/sync/api/presence/...) extend `HookPayloads` via
|
|
1431
|
-
* TypeScript module augmentation:
|
|
1432
|
-
*
|
|
1433
|
-
* ```ts
|
|
1434
|
-
* // packages/<feature>/src/hookPayloads.ts
|
|
1435
|
-
* declare module '@luckystack/core' {
|
|
1436
|
-
* interface HookPayloads {
|
|
1437
|
-
* myFeatureHook: { ... };
|
|
1438
|
-
* }
|
|
1439
|
-
* }
|
|
1440
|
-
* export {};
|
|
1441
|
-
* ```
|
|
1442
|
-
*
|
|
1443
|
-
* Module-augmentation files MUST be in a tsconfig `include` path for TS to
|
|
1444
|
-
* pick up the merge; a side-effect import from the package's `index.ts`
|
|
1445
|
-
* (`import './hookPayloads';`) guarantees this.
|
|
1446
|
-
*/
|
|
1447
|
-
|
|
1448
|
-
/**
|
|
1449
|
-
* Minimal session shape used inside core-owned hook payloads. Defined here
|
|
1450
|
-
* (instead of imported from `@luckystack/login`) so `@luckystack/core` stays
|
|
1451
|
-
* independent of login. Any concrete session type (`BaseSessionLayout`,
|
|
1452
|
-
* project-level `SessionLayout` extending Prisma User, etc.) is structurally
|
|
1453
|
-
* assignable to this shape.
|
|
1454
|
-
*/
|
|
1455
|
-
interface HookSessionShape {
|
|
1456
|
-
id: string;
|
|
1457
|
-
token?: string;
|
|
1458
|
-
email?: string | null;
|
|
1459
|
-
name?: string | null;
|
|
1460
|
-
avatar?: string | null;
|
|
1461
|
-
avatarFallback?: string | null;
|
|
1462
|
-
admin?: boolean | null;
|
|
1463
|
-
language?: string | null;
|
|
1464
|
-
roomCodes?: string[];
|
|
1465
|
-
}
|
|
1466
|
-
interface HookStopSignal {
|
|
1467
|
-
stop: true;
|
|
1468
|
-
errorCode: string;
|
|
1469
|
-
httpStatus?: number;
|
|
1470
|
-
/**
|
|
1471
|
-
* Optional output override for per-recipient hooks (`preSyncRecipient`).
|
|
1472
|
-
* When present on a stop signal the framework DOES NOT skip the recipient —
|
|
1473
|
-
* instead it sends this value in place of the full `serverOutput`. Use to
|
|
1474
|
-
* redact PII before delivery without excluding the socket from the fanout.
|
|
1475
|
-
* Ignored by all other hook dispatch sites.
|
|
1476
|
-
*/
|
|
1477
|
-
overrideOutput?: unknown;
|
|
1478
|
-
}
|
|
1479
|
-
/** Handlers return undefined / void to continue, or a stop signal to abort the main flow. */
|
|
1480
|
-
type HookResult = void | undefined | HookStopSignal;
|
|
1481
|
-
type HookHandler<TPayload> = (payload: TPayload) => Promise<HookResult> | HookResult;
|
|
1482
|
-
interface PreApiValidatePayload {
|
|
1483
|
-
routeName: string;
|
|
1484
|
-
data: Record<string, unknown>;
|
|
1485
|
-
user: HookSessionShape | null;
|
|
1486
|
-
/**
|
|
1487
|
-
* Optional transport tag — populated by the framework's socket/HTTP API
|
|
1488
|
-
* handlers so hook subscribers can branch on it (e.g. open span with a
|
|
1489
|
-
* different op name). Optional so consumer-side dispatchers don't have to
|
|
1490
|
-
* set it explicitly.
|
|
1491
|
-
*/
|
|
1492
|
-
transport?: 'socket' | 'http';
|
|
1493
|
-
}
|
|
1494
|
-
interface PostApiValidatePayload extends PreApiValidatePayload {
|
|
1495
|
-
validation: {
|
|
1496
|
-
status: 'success';
|
|
1497
|
-
} | {
|
|
1498
|
-
status: 'error';
|
|
1499
|
-
message: string;
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
1502
|
-
interface PreApiExecutePayload {
|
|
1503
|
-
routeName: string;
|
|
1504
|
-
data: Record<string, unknown>;
|
|
1505
|
-
user: HookSessionShape | null;
|
|
1506
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1507
|
-
transport?: 'socket' | 'http';
|
|
1508
|
-
}
|
|
1509
|
-
interface PostApiExecutePayload {
|
|
1510
|
-
routeName: string;
|
|
1511
|
-
data: Record<string, unknown>;
|
|
1512
|
-
user: HookSessionShape | null;
|
|
1513
|
-
result: unknown;
|
|
1514
|
-
error: Error | null;
|
|
1515
|
-
durationMs: number;
|
|
1516
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1517
|
-
transport?: 'socket' | 'http';
|
|
1518
|
-
}
|
|
1519
|
-
interface ApiResponseEnvelope {
|
|
1520
|
-
status: 'success' | 'error';
|
|
1521
|
-
httpStatus?: number;
|
|
1522
|
-
errorCode?: string;
|
|
1523
|
-
message?: string;
|
|
1524
|
-
[key: string]: unknown;
|
|
1525
|
-
}
|
|
1526
|
-
interface PreApiRespondPayload {
|
|
1527
|
-
routeName: string;
|
|
1528
|
-
user: HookSessionShape | null;
|
|
1529
|
-
response: ApiResponseEnvelope;
|
|
1530
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1531
|
-
transport?: 'socket' | 'http';
|
|
1532
|
-
}
|
|
1533
|
-
interface PostApiRespondPayload {
|
|
1534
|
-
routeName: string;
|
|
1535
|
-
user: HookSessionShape | null;
|
|
1536
|
-
response: ApiResponseEnvelope;
|
|
1537
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1538
|
-
transport?: 'socket' | 'http';
|
|
1539
|
-
}
|
|
1540
|
-
interface PreSyncAuthorizePayload {
|
|
1541
|
-
routeName: string;
|
|
1542
|
-
/** Raw (un-validated) data payload. May be reshape-able by the consumer. */
|
|
1543
|
-
data: Record<string, unknown>;
|
|
1544
|
-
user: HookSessionShape | null;
|
|
1545
|
-
/** Room code / receiver token — the sync's intended audience. */
|
|
1546
|
-
receiver: string;
|
|
1547
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1548
|
-
transport?: 'socket' | 'http';
|
|
1549
|
-
}
|
|
1550
|
-
interface PostSyncAuthorizePayload {
|
|
1551
|
-
routeName: string;
|
|
1552
|
-
data: Record<string, unknown>;
|
|
1553
|
-
user: HookSessionShape | null;
|
|
1554
|
-
receiver: string;
|
|
1555
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1556
|
-
transport?: 'socket' | 'http';
|
|
1557
|
-
}
|
|
1558
|
-
interface PreSyncValidatePayload {
|
|
1559
|
-
routeName: string;
|
|
1560
|
-
data: Record<string, unknown>;
|
|
1561
|
-
user: HookSessionShape | null;
|
|
1562
|
-
receiver: string;
|
|
1563
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1564
|
-
transport?: 'socket' | 'http';
|
|
1565
|
-
}
|
|
1566
|
-
interface PostSyncValidatePayload extends PreSyncValidatePayload {
|
|
1567
|
-
validation: {
|
|
1568
|
-
status: 'success';
|
|
1569
|
-
} | {
|
|
1570
|
-
status: 'error';
|
|
1571
|
-
message: string;
|
|
1572
|
-
};
|
|
1573
|
-
}
|
|
1574
|
-
interface PreSyncExecutePayload {
|
|
1575
|
-
routeName: string;
|
|
1576
|
-
data: Record<string, unknown>;
|
|
1577
|
-
user: HookSessionShape | null;
|
|
1578
|
-
receiver: string;
|
|
1579
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1580
|
-
transport?: 'socket' | 'http';
|
|
1581
|
-
}
|
|
1582
|
-
interface PostSyncExecutePayload {
|
|
1583
|
-
routeName: string;
|
|
1584
|
-
data: Record<string, unknown>;
|
|
1585
|
-
user: HookSessionShape | null;
|
|
1586
|
-
receiver: string;
|
|
1587
|
-
result: unknown;
|
|
1588
|
-
error: Error | null;
|
|
1589
|
-
durationMs: number;
|
|
1590
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1591
|
-
transport?: 'socket' | 'http';
|
|
1592
|
-
}
|
|
1593
|
-
interface PreSyncFanoutPayload {
|
|
1594
|
-
routeName: string;
|
|
1595
|
-
data: Record<string, unknown>;
|
|
1596
|
-
user: HookSessionShape | null;
|
|
1597
|
-
receiver: string;
|
|
1598
|
-
serverOutput: unknown;
|
|
1599
|
-
/** Optional transport tag — see {@link PreApiValidatePayload.transport}. */
|
|
1600
|
-
transport?: 'socket' | 'http';
|
|
1601
|
-
}
|
|
1602
|
-
interface PostSyncFanoutPayload extends PreSyncFanoutPayload {
|
|
1603
|
-
recipientCount: number;
|
|
1604
|
-
}
|
|
1605
|
-
interface PreSyncStreamPayload {
|
|
1606
|
-
routeName: string;
|
|
1607
|
-
/** Stream chunk being sent (already serialised by the framework). */
|
|
1608
|
-
chunk: unknown;
|
|
1609
|
-
/** Final recipient — socket id when known, room code otherwise. */
|
|
1610
|
-
recipient: string;
|
|
1611
|
-
}
|
|
1612
|
-
interface PostSyncStreamPayload extends PreSyncStreamPayload {
|
|
1613
|
-
/** Total chunks streamed so far in this sync invocation. */
|
|
1614
|
-
chunkIndex: number;
|
|
1615
|
-
}
|
|
1616
|
-
interface ApiErrorPayload {
|
|
1617
|
-
route: string;
|
|
1618
|
-
method?: string;
|
|
1619
|
-
requestId?: string;
|
|
1620
|
-
user?: HookSessionShape | null;
|
|
1621
|
-
error: Error;
|
|
1622
|
-
}
|
|
1623
|
-
interface SyncErrorPayload {
|
|
1624
|
-
route: string;
|
|
1625
|
-
method?: string;
|
|
1626
|
-
requestId?: string;
|
|
1627
|
-
user?: HookSessionShape | null;
|
|
1628
|
-
error: Error;
|
|
1629
|
-
}
|
|
1630
|
-
interface RateLimitExceededPayload {
|
|
1631
|
-
/** Where the limit was hit — IP, user, or route. */
|
|
1632
|
-
scope: 'ip' | 'user' | 'route' | 'auth';
|
|
1633
|
-
/** The key that exceeded its limit (sanitized — no tokens). */
|
|
1634
|
-
key: string;
|
|
1635
|
-
/** Configured limit count for this key. */
|
|
1636
|
-
limit: number;
|
|
1637
|
-
/** Window in milliseconds. */
|
|
1638
|
-
windowMs: number;
|
|
1639
|
-
/** Current bucket count after the rejected request. */
|
|
1640
|
-
count: number;
|
|
1641
|
-
/** Optional route or transport context. */
|
|
1642
|
-
route?: string;
|
|
1643
|
-
ip?: string;
|
|
1644
|
-
userId?: string;
|
|
1645
|
-
}
|
|
1646
|
-
interface CorsRejectedPayload {
|
|
1647
|
-
/** Origin header sent by the client. */
|
|
1648
|
-
origin: string;
|
|
1649
|
-
/** Origin after framework normalization (scheme + host[:port]). */
|
|
1650
|
-
normalizedOrigin: string;
|
|
1651
|
-
/** Effective allowed-origins set at the time of rejection. */
|
|
1652
|
-
allowedOrigins: string[];
|
|
1653
|
-
/** Whether `allowLocalhost` was on. */
|
|
1654
|
-
allowLocalhost: boolean;
|
|
1655
|
-
/** Optional route the rejected request was for. */
|
|
1656
|
-
route?: string;
|
|
1657
|
-
/**
|
|
1658
|
-
* Why the request was rejected (server HOK-27). Distinguishes a present-but-
|
|
1659
|
-
* disallowed Origin (`'origin-not-allowed'`) from a MISSING Origin header
|
|
1660
|
-
* (`'origin-missing'`, the 403 dispatched by the server's origin gate) and the
|
|
1661
|
-
* normalization-failure case. Optional so existing `allowedOrigin()` callers
|
|
1662
|
-
* that don't set it stay source-compatible.
|
|
1663
|
-
*/
|
|
1664
|
-
reason?: 'origin-not-allowed' | 'origin-missing' | 'origin-malformed';
|
|
1665
|
-
}
|
|
1666
|
-
interface PreSessionRefreshPayload {
|
|
1667
|
-
token: string;
|
|
1668
|
-
userId: string | null;
|
|
1669
|
-
oldTtl: number | null;
|
|
1670
|
-
newTtl: number;
|
|
1671
|
-
}
|
|
1672
|
-
interface PostSessionRefreshPayload {
|
|
1673
|
-
token: string;
|
|
1674
|
-
userId: string | null;
|
|
1675
|
-
oldTtl: number | null;
|
|
1676
|
-
newTtl: number;
|
|
1677
|
-
/** True if Redis EXPIRE succeeded; false on failure or non-existent key. */
|
|
1678
|
-
applied: boolean;
|
|
1679
|
-
}
|
|
1680
|
-
interface SessionCreatedPayload {
|
|
1681
|
-
token: string;
|
|
1682
|
-
userId: string;
|
|
1683
|
-
/** How the session came to exist (credentials login, OAuth, refresh-mint, ...). */
|
|
1684
|
-
via?: string;
|
|
1685
|
-
}
|
|
1686
|
-
interface SessionRevokedPayload {
|
|
1687
|
-
token: string;
|
|
1688
|
-
userId: string | null;
|
|
1689
|
-
/** Why the session was revoked (logout, kicked-by-new-device, admin, expiry, ...). */
|
|
1690
|
-
reason?: string;
|
|
1691
|
-
}
|
|
1692
|
-
interface OnUploadStartPayload {
|
|
1693
|
-
userId: string;
|
|
1694
|
-
contentType: string;
|
|
1695
|
-
sizeBytes: number;
|
|
1696
|
-
uploadKind: 'avatar' | string;
|
|
1697
|
-
}
|
|
1698
|
-
interface OnUploadCompletePayload {
|
|
1699
|
-
userId: string;
|
|
1700
|
-
fileName: string;
|
|
1701
|
-
sizeBytes: number;
|
|
1702
|
-
uploadKind: 'avatar' | string;
|
|
1703
|
-
}
|
|
1704
|
-
interface PreAvatarServePayload {
|
|
1705
|
-
/** The route path requested (`/avatars/:fileId`). */
|
|
1706
|
-
routePath: string;
|
|
1707
|
-
/** The allowlisted file id resolved from the route path. */
|
|
1708
|
-
fileId: string;
|
|
1709
|
-
}
|
|
1710
|
-
interface PostAvatarServePayload {
|
|
1711
|
-
routePath: string;
|
|
1712
|
-
fileId: string;
|
|
1713
|
-
/** Disk extension of the format that was served (e.g. `'webp'`). */
|
|
1714
|
-
extension: string;
|
|
1715
|
-
/** Content-Type header sent for the served file. */
|
|
1716
|
-
contentType: string;
|
|
1717
|
-
}
|
|
1718
|
-
interface CsrfMismatchPayload {
|
|
1719
|
-
/** Path of the rejected request. */
|
|
1720
|
-
route: string;
|
|
1721
|
-
/** HTTP method of the rejected request. */
|
|
1722
|
-
method?: string;
|
|
1723
|
-
/** Request id (X-Request-Id) for log correlation. */
|
|
1724
|
-
requestId?: string;
|
|
1725
|
-
/** User id from the session that the request claimed, if any. */
|
|
1726
|
-
userId?: string;
|
|
1727
|
-
/** Whether a token was provided in `x-csrf-token` (presence only — never the value). */
|
|
1728
|
-
providedToken: boolean;
|
|
1729
|
-
}
|
|
1730
|
-
interface PreSocketMessagePayload {
|
|
1731
|
-
/** Which socket pipeline received the message. */
|
|
1732
|
-
channel: 'api' | 'sync';
|
|
1733
|
-
/** Socket.io connection id of the originator. */
|
|
1734
|
-
socketId: string;
|
|
1735
|
-
/** Handshake address of the originator (best-effort; honours no proxy trust). */
|
|
1736
|
-
ip: string;
|
|
1737
|
-
/** Whether the originating socket carried a session token (presence only — never the value). */
|
|
1738
|
-
authenticated: boolean;
|
|
1739
|
-
/** Raw route name from the message envelope, when present (un-parsed, un-trusted). */
|
|
1740
|
-
routeName?: string;
|
|
1741
|
-
}
|
|
1742
|
-
interface PreHttpRequestPayload {
|
|
1743
|
-
/** HTTP method (always uppercase: GET/POST/...). */
|
|
1744
|
-
method: string;
|
|
1745
|
-
/** Request URL (path + query). */
|
|
1746
|
-
url: string;
|
|
1747
|
-
/** Caller-supplied or framework-generated `X-Request-Id`. */
|
|
1748
|
-
requestId: string;
|
|
1749
|
-
/** Origin header value (or empty string when absent). */
|
|
1750
|
-
origin: string;
|
|
1751
|
-
/**
|
|
1752
|
-
* Subset of headers — `authorization`, `cookie`, etc. are NOT included
|
|
1753
|
-
* to keep payloads safe for logging. Use the full request object via
|
|
1754
|
-
* a custom route if you need raw access.
|
|
1755
|
-
*/
|
|
1756
|
-
headers: Record<string, string>;
|
|
1757
|
-
}
|
|
1758
|
-
interface PostHttpRequestPayload {
|
|
1759
|
-
/** HTTP method (always uppercase). */
|
|
1760
|
-
method: string;
|
|
1761
|
-
/** Request URL (path + query). */
|
|
1762
|
-
url: string;
|
|
1763
|
-
/** Caller-supplied or framework-generated `X-Request-Id`. */
|
|
1764
|
-
requestId: string;
|
|
1765
|
-
/** Final HTTP status code written to the response. */
|
|
1766
|
-
statusCode: number;
|
|
1767
|
-
/** Wall-clock duration from request receipt to response, in ms. */
|
|
1768
|
-
durationMs: number;
|
|
1769
|
-
}
|
|
1770
|
-
interface ApiAuthRejectedPayload {
|
|
1771
|
-
/** Resolved route name (`api/billing/getInvoice/v1`). */
|
|
1772
|
-
routeName: string;
|
|
1773
|
-
/** Why auth failed. `'login-required'` = no/invalid session on a login route;
|
|
1774
|
-
* `'additional-failed'` = a session existed but an `additional[]` predicate
|
|
1775
|
-
* rejected; `'invalid-condition'` = a misconfigured predicate (setup error). */
|
|
1776
|
-
reason: 'login-required' | 'additional-failed' | 'invalid-condition';
|
|
1777
|
-
/** Session user id when a (insufficient) session was present, else null. */
|
|
1778
|
-
userId: string | null;
|
|
1779
|
-
/** Resolved client IP (honours `http.trustProxy`). */
|
|
1780
|
-
ip?: string;
|
|
1781
|
-
/** Transport the request arrived on. */
|
|
1782
|
-
transport?: 'socket' | 'http';
|
|
1783
|
-
/** The specific `additional[]` key that failed, when `reason === 'additional-failed'`. */
|
|
1784
|
-
failedKey?: string;
|
|
1785
|
-
}
|
|
1786
|
-
interface PreSyncRecipientPayload {
|
|
1787
|
-
/** Resolved route name (`sync/board/moveCard/v1`). */
|
|
1788
|
-
routeName: string;
|
|
1789
|
-
/** Room code / receiver this fanout is for. */
|
|
1790
|
-
receiver: string;
|
|
1791
|
-
/** Socket id of THIS recipient. */
|
|
1792
|
-
recipientSocketId: string;
|
|
1793
|
-
/**
|
|
1794
|
-
* Session token of THIS recipient, as read from its socket handshake — the
|
|
1795
|
-
* same value the fan-out uses for `ignoreSelf`. `null` for an anonymous
|
|
1796
|
-
* socket. Cheap: no session read is performed to produce it.
|
|
1797
|
-
*/
|
|
1798
|
-
recipientToken: string | null;
|
|
1799
|
-
/**
|
|
1800
|
-
* Always `null`. Kept on the payload for backward compatibility; resolve the
|
|
1801
|
-
* user from `recipientToken` when you need it.
|
|
1802
|
-
*/
|
|
1803
|
-
recipientUserId: string | null;
|
|
1804
|
-
/** The server-validated output about to be sent. */
|
|
1805
|
-
serverOutput: unknown;
|
|
1806
|
-
}
|
|
1807
|
-
interface PreServerStopPayload {
|
|
1808
|
-
/** Why the server is stopping — the signal name, or `'manual'` for a programmatic stop. */
|
|
1809
|
-
reason: 'SIGTERM' | 'SIGINT' | 'SIGHUP' | 'manual';
|
|
1810
|
-
/**
|
|
1811
|
-
* Soft budget (ms) the server intends to wait for shutdown handlers + in-flight
|
|
1812
|
-
* requests to drain before forcing exit. Handlers should not block longer than
|
|
1813
|
-
* this. `undefined` when the server applies no deadline.
|
|
1814
|
-
*/
|
|
1815
|
-
timeoutMs?: number;
|
|
1816
|
-
}
|
|
1817
|
-
interface HookPayloads {
|
|
1818
|
-
preHttpRequest: PreHttpRequestPayload;
|
|
1819
|
-
postHttpRequest: PostHttpRequestPayload;
|
|
1820
|
-
preSocketMessage: PreSocketMessagePayload;
|
|
1821
|
-
preApiValidate: PreApiValidatePayload;
|
|
1822
|
-
postApiValidate: PostApiValidatePayload;
|
|
1823
|
-
preApiExecute: PreApiExecutePayload;
|
|
1824
|
-
postApiExecute: PostApiExecutePayload;
|
|
1825
|
-
preApiRespond: PreApiRespondPayload;
|
|
1826
|
-
transformApiResponse: PreApiRespondPayload;
|
|
1827
|
-
postApiRespond: PostApiRespondPayload;
|
|
1828
|
-
preSyncAuthorize: PreSyncAuthorizePayload;
|
|
1829
|
-
postSyncAuthorize: PostSyncAuthorizePayload;
|
|
1830
|
-
preSyncValidate: PreSyncValidatePayload;
|
|
1831
|
-
postSyncValidate: PostSyncValidatePayload;
|
|
1832
|
-
preSyncExecute: PreSyncExecutePayload;
|
|
1833
|
-
postSyncExecute: PostSyncExecutePayload;
|
|
1834
|
-
preSyncFanout: PreSyncFanoutPayload;
|
|
1835
|
-
postSyncFanout: PostSyncFanoutPayload;
|
|
1836
|
-
preSyncRecipient: PreSyncRecipientPayload;
|
|
1837
|
-
preSyncStream: PreSyncStreamPayload;
|
|
1838
|
-
postSyncStream: PostSyncStreamPayload;
|
|
1839
|
-
apiError: ApiErrorPayload;
|
|
1840
|
-
syncError: SyncErrorPayload;
|
|
1841
|
-
apiAuthRejected: ApiAuthRejectedPayload;
|
|
1842
|
-
rateLimitExceeded: RateLimitExceededPayload;
|
|
1843
|
-
corsRejected: CorsRejectedPayload;
|
|
1844
|
-
csrfMismatch: CsrfMismatchPayload;
|
|
1845
|
-
preSessionRefresh: PreSessionRefreshPayload;
|
|
1846
|
-
postSessionRefresh: PostSessionRefreshPayload;
|
|
1847
|
-
sessionCreated: SessionCreatedPayload;
|
|
1848
|
-
sessionRevoked: SessionRevokedPayload;
|
|
1849
|
-
onUploadStart: OnUploadStartPayload;
|
|
1850
|
-
onUploadComplete: OnUploadCompletePayload;
|
|
1851
|
-
preAvatarServe: PreAvatarServePayload;
|
|
1852
|
-
postAvatarServe: PostAvatarServePayload;
|
|
1853
|
-
preServerStop: PreServerStopPayload;
|
|
1854
|
-
}
|
|
1855
|
-
interface PreErrorNormalizePayload {
|
|
1856
|
-
response: ErrorResponseInput;
|
|
1857
|
-
preferredLocale?: string | null;
|
|
1858
|
-
userLanguage?: string | null;
|
|
1859
|
-
fallbackHttpStatus?: number;
|
|
1860
|
-
}
|
|
1861
|
-
interface PostErrorNormalizePayload {
|
|
1862
|
-
normalized: NormalizedErrorResponse;
|
|
1863
|
-
preferredLocale?: string | null;
|
|
1864
|
-
userLanguage?: string | null;
|
|
1865
|
-
}
|
|
1866
|
-
interface SyncHookPayloads {
|
|
1867
|
-
preErrorNormalize: PreErrorNormalizePayload;
|
|
1868
|
-
postErrorNormalize: PostErrorNormalizePayload;
|
|
1869
|
-
}
|
|
1870
|
-
type SyncHookName = keyof SyncHookPayloads;
|
|
1871
|
-
type SyncHookHandler<TPayload> = (payload: TPayload) => void;
|
|
1872
|
-
type HookName = keyof HookPayloads;
|
|
1873
|
-
|
|
1874
|
-
type DispatchResult = {
|
|
1875
|
-
stopped: false;
|
|
1876
|
-
} | {
|
|
1877
|
-
stopped: true;
|
|
1878
|
-
signal: HookStopSignal;
|
|
1879
|
-
};
|
|
1880
|
-
declare const registerHook: <TName extends HookName>(name: TName, handler: HookHandler<HookPayloads[TName]>) => (() => void);
|
|
1881
|
-
declare const dispatchHook: <TName extends HookName>(name: TName, payload: HookPayloads[TName]) => Promise<DispatchResult>;
|
|
1882
|
-
declare const clearAllHooks: () => void;
|
|
1883
|
-
declare const registerSyncHook: <TName extends SyncHookName>(name: TName, handler: SyncHookHandler<SyncHookPayloads[TName]>) => (() => void);
|
|
1884
|
-
declare const dispatchSyncHook: <TName extends SyncHookName>(name: TName, payload: SyncHookPayloads[TName]) => void;
|
|
1885
|
-
|
|
1886
|
-
export { type ApiAuthRejectedPayload, type ApiErrorPayload, type ApiResponseEnvelope, AuthProps, type AvatarConfig, type AvatarConfigInput, BOOT_KEY_PREFIX, BaseSessionLayout, type BootUuidHeartbeat, type CheckRateLimitParams, type CookiePrefixConstraints, type CorsRejectedPayload, type CreateLocalizedNormalizerInput, type CsrfConfig, type CsrfCookieOptions, type CsrfMismatchPayload, DEFAULT_AVATAR_CONFIG, DEFAULT_CLIENT_KEY, DEFAULT_CSRF_CONFIG, DEFAULT_ENV_FILES, DEFAULT_REDACTED_LOG_KEYS, DEPTH_TRUNCATED_PLACEHOLDER, type DbHealthCheck, type DbHealthResult, DeepPartial, type DispatchResult, type EmailAttachment, type EmailDeliveryOutcome, type EmailMessage, type EmailResult, type EmailSendContext, type EmailSender, type EmailSenderRegistry, type ErrorFormatter, type ErrorFormatterContext, ErrorParam, ErrorResponseInput, type ErrorTracker, type ErrorTrackerContext, type ErrorTrackerEvent, type ErrorTrackerUser, type FormatterEnvelope, GENERATED_API_DOCS_PATH, GENERATED_API_SCHEMAS_PATH, GENERATED_SOCKET_TYPES_PATH, type GetIoInstanceOptions, type GetRoomSocketsOptions, HealthHashConfig, type HealthHashDescriptor, type HookHandler, type HookName, type HookPayloads, type HookResult, type HookSessionShape, type HookStopSignal, type HttpMethod, type LanguageCode, LocalSocketEnumerationError, type LocaleReloader, type LocalizedNormalizer, type NormalizeErrorResponseInput, NormalizedErrorResponse, type OnUploadCompletePayload, type OnUploadStartPayload, type OneTimeTokenHandle, PUBLIC_DIR, type PeerRequire, type PostApiExecutePayload, type PostApiRespondPayload, type PostApiValidatePayload, type PostAvatarServePayload, type PostErrorNormalizePayload, type PostHttpRequestPayload, type PostSessionRefreshPayload, type PostSyncAuthorizePayload, type PostSyncExecutePayload, type PostSyncFanoutPayload, type PostSyncValidatePayload, type PreApiExecutePayload, type PreApiRespondPayload, type PreApiValidatePayload, type PreAvatarServePayload, type PreCaptureFilter, type PreErrorNormalizePayload, type PreHttpRequestPayload, type PreServerStopPayload, type PreSessionRefreshPayload, type PreSocketMessagePayload, type PreSyncAuthorizePayload, type PreSyncExecutePayload, type PreSyncFanoutPayload, type PreSyncRecipientPayload, type PreSyncValidatePayload, type ProcessUploadInput, type ProcessUploadResult, REDACTED_PLACEHOLDER, ROOT_DIR, type RateLimitExceededPayload, type RateLimitResult, type RateLimitStrategy, type RedisConnectionOptions, type RedisKeyFormatter, type Registry, type RegistryOptions, type ResolveClientIpParams, type RoomNameFormatter, type RoomNameFormatterContext, type RoomSocket, type RuntimeApiMapsResult, type RuntimeEnv, type RuntimeMapsProvider, type RuntimeSyncMapsResult, SERVER_DIR, SERVER_FUNCTIONS_DIR, SHARED_DIR, SRC_DIR, type SessionCreatedPayload, type SessionLogoutInput, type SessionProvider, type SessionRevokedPayload, type SessionSaveResult, type SocketMiddleware, type SpanHandle, type SpanResult, type SyncErrorPayload, type SyncHookHandler, type SyncHookName, type SyncHookPayloads, TEST_DIRECTORY_NAMES, TEST_FILE_PATTERN, TSCONFIG_ALIAS_FILES, type TranslateFunction, type TranslateInput, UNKNOWN_CLIENT_IP, UPLOADS_DIR, type ValidationResult$1 as ValidationResult, abortAllForSocket, abortApiByResponseIndex, abortSyncByCb, acquireLease, allowedOrigin, type apiMessage, appendErrorTracker, applyCookiePrefixConstraints, applyErrorFormatter, applySocketMiddlewares, applyStrayKeyPrefix, attachSocketRedisAdapter, bootstrapEnv, captureException, captureExceptionAcrossTrackers, captureMessage, captureMessageAcrossTrackers, checkRateLimit, clearAllHooks, clearAllRateLimits, clearRateLimit, clearSocketMiddlewares, collectSynchronizedEnvKeys, computeSynchronizedEnvHashes, consumeOneTimeToken, consumeOneTimeTokenJson, createLocalizedNormalizer, createRegistry, defaultRateLimitStrategy, defaultRedisKeyFormatter, defaultRoomNameFormatter, deriveTokenBucketId, describeHealthHashConfig, dispatchHook, dispatchSyncHook, ensurePeerDepInstalled, env, escapeHtml, extractLanguageFromHeader, extractTokenFromRequest, extractTokenFromSocket, flushErrorTrackers, formatKey, formatRoomName, getActiveErrorTrackers, getAvatarConfig, getBindAddress, getCookieValue, getCsrfConfig, getCurrentErrorTrackerIdentity, getDbHealthCheck, getEffectiveHttpMethod, getEmailSender, getEmailSenderByName, getEnv, getEnvFiles, getErrorFormatter, getGeneratedApiDocsPath, getGeneratedApiSchemasPath, getGeneratedSocketTypesPath, getIoInstance, getLocaleReloader, getLocalizedNormalizer, getParams, getPrismaClient, getPrismaClientFor, getPrismaClientKeys, getPublicDir, getRateLimitStatus, getRateLimitStrategy, getRedactedLogKeys, getRedisClient, getRedisClientFor, getRedisClientKeys, getRedisConnectionOptions, getRedisKeyFormatter, getRegisteredSessionProvider, getRoomNameFormatter, getRoomSockets, getRuntimeApiMaps, getRuntimeSyncMaps, getServerDir, getServerFunctionDirs, getServerFunctionsDir, getSharedDir, getSocketMiddlewares, getSrcDir, getUploadsDir, hasCookie, hashSynchronizedValue, hashSynchronizedValueWith, inferHttpMethod, initConsolelog, initSharedSentry, isDbHealthCheckRegistered, isEmailSenderRegistered, isFalsy, isGetMethodName, isLocalizedNormalizerRegistered, isLoopbackIp, isMethodAllowed, isPrismaClientRegistered, isPrismaClientResolvable, isProduction, isProductionRuntime, isRedactedLogKey, isRedisClientRegistered, isRuntimeMapsProviderRegistered, isSessionProviderRegistered, isTestDirectory, isTestFile, isTestRuntime, issueOneTimeToken, listEmailSenderNames, loadEnvFiles, loadPeer, normalizeErrorResponse, normalizeOrigin, oneTimeTokenKey, performLogout, prisma, processUpload, readBootUuid, readSession, rebuildDefaultRedisClient, recordMetricAcrossTrackers, redis, refreshBootUuid, registerApiAbortController, registerAvatarConfig, registerBindAddress, registerBoundAddress, registerCsrfConfig, registerDbHealthCheck, registerEmailSender, registerEmailSenders, registerErrorFormatter, registerErrorTracker, registerErrorTrackers, registerHook, registerLocaleReloader, registerLocalizedNormalizer, registerPreCaptureFilter, registerPrismaClient, registerRateLimitStrategy, registerRedactedLogKeys, registerRedisClient, registerRedisKeyFormatter, registerRoomNameFormatter, registerRuntimeMapsProvider, registerSessionProvider, registerSocketMiddleware, registerStrayPrefixCommand, registerSyncAbortController, registerSyncHook, releaseLease, removeSession, renewLease, resetClientsForTests, resetCsrfConfigForTests, resetDbHealthCheckForTests, resetDefaultRedisClient, resetRedactedLogKeysForTests, resetRedisKeyFormatterForTests, resetSessionProviderForTests, resolveClientIp, resolveDevCallbackUrl, resolveEnvKey, resolveErrorMessage, resolveFromRoot, resolveHealthHashConfig, resolveHealthHashConfigFromDescriptor, resolveRuntimeMode, resolveSocketAdapterKey, runWithErrorTrackerIdentity, runWithErrorTrackerIdentityScope, sanitizeErrorString, sanitizeErrorStrings, sanitizeForLog, serveAvatar, setCurrentErrorTrackerIdentity, setErrorTrackerUser, setIoInstance, setSentryUser, startBootUuidHeartbeat, startSpan, startSpanAcrossTrackers, startSpanHandle, type syncMessage, tryCatch, unregisterApiAbortController, unregisterSyncAbortController, validateInputByType, validateRequest, writeBootUuid, writeSession };
|
|
1
|
+
export { default as sleep } from './sleep.js';
|
|
2
|
+
export { default as tryCatch } from './tryCatch.js';
|
|
3
|
+
export { default as tryCatchSync } from './tryCatchSync.js';
|
|
4
|
+
export { deepMerge, isPlainObject } from './configUtils.js';
|
|
5
|
+
export type { DeepPartial } from './configUtils.js';
|
|
6
|
+
export { createRegistry } from './createRegistry.js';
|
|
7
|
+
export type { Registry, RegistryOptions } from './createRegistry.js';
|
|
8
|
+
export { escapeHtml } from './escapeHtml.js';
|
|
9
|
+
export { ROUTED_DATA_QUERY_KEY, buildRoutedGetUrl } from './routedDataQuery.js';
|
|
10
|
+
export { ensurePeerDepInstalled, loadPeer } from './peerDeps.js';
|
|
11
|
+
export type { PeerRequire } from './peerDeps.js';
|
|
12
|
+
export * from './serviceRoute.js';
|
|
13
|
+
export * from './socketEvents.js';
|
|
14
|
+
export { isTestFile, isTestDirectory, TEST_FILE_PATTERN, TEST_DIRECTORY_NAMES, } from './testFileConvention.js';
|
|
15
|
+
export { registerRoomNameFormatter, getRoomNameFormatter, formatRoomName, defaultRoomNameFormatter, } from './roomNameFormatterRegistry.js';
|
|
16
|
+
export type { RoomNameFormatter, RoomNameFormatterContext } from './roomNameFormatterRegistry.js';
|
|
17
|
+
export * from './responseNormalizer.js';
|
|
18
|
+
export * from './localizedNormalizer.js';
|
|
19
|
+
export { registerErrorFormatter, getErrorFormatter, applyErrorFormatter, } from './errorFormatterRegistry.js';
|
|
20
|
+
export type { ErrorFormatter, ErrorFormatterContext, FormatterEnvelope } from './errorFormatterRegistry.js';
|
|
21
|
+
export { validatePagePath, DEFAULT_PAGE_ROUTE_RULES, } from './pageRouteValidation.js';
|
|
22
|
+
export type { PageRouteRules, PagePathValidationResult, } from './pageRouteValidation.js';
|
|
23
|
+
export type { ApiTypeMap, SyncTypeMap, StreamPayload } from './apiTypeStubs.js';
|
|
24
|
+
export type { apiMessage, syncMessage } from './socketTypes.js';
|
|
25
|
+
export { setIoInstance, getIoInstance } from './socketTypes.js';
|
|
26
|
+
export type { GetIoInstanceOptions } from './socketTypes.js';
|
|
27
|
+
export { LocalSocketEnumerationError } from './localSocketEnumerationGuard.js';
|
|
28
|
+
export { getRoomSockets } from './roomSockets.js';
|
|
29
|
+
export type { GetRoomSocketsOptions, RoomSocket } from './roomSockets.js';
|
|
30
|
+
export { registerProjectConfig, getProjectConfig, getProjectName, isProjectConfigRegistered, DEFAULT_PROJECT_CONFIG, } from './projectConfig.js';
|
|
31
|
+
export { registerPortOverride, getPortOverride, resetPortOverrideForTests, } from './portOverrideRegistry.js';
|
|
32
|
+
export type { ProjectConfig, ProjectConfigInput, AppConfig, LoggingConfig, RateLimitingConfig, SessionConfig, HttpConfig, HttpStreamConfig, HealthHashConfig, SecurityHeadersConfig, CorsConfig, AuthConfig, AuthRateLimitConfig, RateLimitIdentity, RateLimitIdentityParams, PasswordPolicyConfig, SocketConfig, ApiConfig, ValidationConfig, SyncConfig, SyncStreamThrottleConfig, SyncFlushPressureConfig, OfflineQueueConfig, DevConfig, PathsConfig, } from './projectConfig.js';
|
|
33
|
+
export { registerRuntimeMapsProvider, getRuntimeApiMaps, getRuntimeSyncMaps, isRuntimeMapsProviderRegistered, } from './runtimeMapsRegistry.js';
|
|
34
|
+
export { registerApiMethodMap, getRegisteredApiMethod, isApiMethodMapRegistered, } from './apiMethodMapRegistry.js';
|
|
35
|
+
export type { HttpMethodLiteral, ApiMethodMap } from './apiMethodMapRegistry.js';
|
|
36
|
+
export type { RuntimeMapsProvider, RuntimeApiMapsResult, RuntimeSyncMapsResult, } from './runtimeMapsRegistry.js';
|
|
37
|
+
export { registerNotifier, getNotifier, notify, } from './notifier.js';
|
|
38
|
+
export type { Notifier, NotifyInput, NotifyParam } from './notifier.js';
|
|
39
|
+
export { registerEmailSender, registerEmailSenders, getEmailSender, getEmailSenderByName, listEmailSenderNames, isEmailSenderRegistered, } from './emailRegistry.js';
|
|
40
|
+
export type { EmailDeliveryOutcome, EmailSendContext, EmailSender, EmailMessage, EmailAttachment, EmailResult, EmailSenderRegistry, } from './emailRegistry.js';
|
|
41
|
+
export { registerDeployConfig, getDeployConfig, isDeployConfigRegistered, } from './deployConfigRegistry.js';
|
|
42
|
+
export type { DeployConfigShape, DeployResourceShape, DeployEnvironmentShape, DeployRoutingShape, DeployDevelopmentShape, } from './deployConfigRegistry.js';
|
|
43
|
+
export { registerServicesConfig, getServicesConfig, isServicesConfigRegistered, } from './servicesConfigRegistry.js';
|
|
44
|
+
export type { ServicesConfigShape, ServiceDefinition, PresetDefinition, } from './servicesConfigRegistry.js';
|
|
45
|
+
export { registerLocaleReloader, getLocaleReloader, } from './localeReloader.js';
|
|
46
|
+
export type { LocaleReloader } from './localeReloader.js';
|
|
47
|
+
export type { statusContent, SOCKETSTATUS } from './socketStatusTypes.js';
|
|
48
|
+
export type { BaseSessionLayout, SessionLocation, AuthProps, Jsonify } from './sessionTypes.js';
|
|
49
|
+
export * from './sentrySetup.js';
|
|
50
|
+
export { registerErrorTracker, registerErrorTrackers, appendErrorTracker, getActiveErrorTrackers, captureExceptionAcrossTrackers, captureMessageAcrossTrackers, setErrorTrackerUser, recordMetricAcrossTrackers, startSpanAcrossTrackers, startSpanHandle, registerPreCaptureFilter, flushErrorTrackers, sanitizeErrorString, sanitizeErrorStrings, } from './errorTrackerRegistry.js';
|
|
51
|
+
export type { ErrorTracker, ErrorTrackerContext, ErrorTrackerUser, ErrorTrackerEvent, SpanResult, SpanHandle, PreCaptureFilter, } from './errorTrackerRegistry.js';
|
|
52
|
+
export { runWithErrorTrackerIdentity, runWithErrorTrackerIdentityScope, setCurrentErrorTrackerIdentity, getCurrentErrorTrackerIdentity, } from './errorTrackerIdentity.js';
|
|
53
|
+
export * from './env.js';
|
|
54
|
+
export * from './db.js';
|
|
55
|
+
export { registerDbHealthCheck, getDbHealthCheck, isDbHealthCheckRegistered, resetDbHealthCheckForTests, } from './dbHealthCheck.js';
|
|
56
|
+
export type { DbHealthCheck, DbHealthResult } from './dbHealthCheck.js';
|
|
57
|
+
export { redis, getRedisConnectionOptions, registerStrayPrefixCommand, resetDefaultRedisClient, rebuildDefaultRedisClient } from './redis.js';
|
|
58
|
+
export type { RedisConnectionOptions } from './redis.js';
|
|
59
|
+
export { registerSecretsResolvedListener, notifySecretsResolved } from './secretsResolved.js';
|
|
60
|
+
export type { SecretsResolvedListener } from './secretsResolved.js';
|
|
61
|
+
export { registerRedisKeyFormatter, getRedisKeyFormatter, resetRedisKeyFormatterForTests, defaultRedisKeyFormatter, formatKey, applyStrayKeyPrefix, } from './redisKeyFormatter.js';
|
|
62
|
+
export type { RedisKeyFormatter } from './redisKeyFormatter.js';
|
|
63
|
+
export { acquireLease, renewLease, releaseLease } from './lease.js';
|
|
64
|
+
export { DEFAULT_CLIENT_KEY, registerPrismaClient, registerRedisClient, getPrismaClient, getRedisClient, getPrismaClientFor, getRedisClientFor, getPrismaClientKeys, getRedisClientKeys, isPrismaClientRegistered, isRedisClientRegistered, resetClientsForTests, } from './clients.js';
|
|
65
|
+
export { attachSocketRedisAdapter, resolveSocketAdapterKey } from './socketRedisAdapter.js';
|
|
66
|
+
export { writeBootUuid, readBootUuid, refreshBootUuid, startBootUuidHeartbeat, resolveEnvKey, BOOT_KEY_PREFIX, type BootUuidHeartbeat, } from './bootUuid.js';
|
|
67
|
+
export { collectSynchronizedEnvKeys, computeSynchronizedEnvHashes, hashSynchronizedValue, hashSynchronizedValueWith, resolveHealthHashConfig, describeHealthHashConfig, resolveHealthHashConfigFromDescriptor, } from './synchronizedEnvHashes.js';
|
|
68
|
+
export type { HealthHashDescriptor } from './synchronizedEnvHashes.js';
|
|
69
|
+
export { initConsolelog } from './consoleLog.js';
|
|
70
|
+
export { registerLogger, getLogger, isLoggerRegistered, resetLoggerForTests, createDevLogger, } from './loggerRegistry.js';
|
|
71
|
+
export type { Logger, LoggerContext } from './loggerRegistry.js';
|
|
72
|
+
export { registerRedactedLogKeys, getRedactedLogKeys, isRedactedLogKey, resetRedactedLogKeysForTests, sanitizeForLog, DEFAULT_REDACTED_LOG_KEYS, REDACTED_PLACEHOLDER, DEPTH_TRUNCATED_PLACEHOLDER, } from './redactedLogKeys.js';
|
|
73
|
+
export * from './cookies.js';
|
|
74
|
+
export * from './httpApiUtils.js';
|
|
75
|
+
export * from './paths.js';
|
|
76
|
+
export { registerSocketMiddleware, getSocketMiddlewares, clearSocketMiddlewares, applySocketMiddlewares, } from './socketMiddlewareRegistry.js';
|
|
77
|
+
export type { SocketMiddleware } from './socketMiddlewareRegistry.js';
|
|
78
|
+
export { registerCsrfConfig, getCsrfConfig, resetCsrfConfigForTests, DEFAULT_CSRF_CONFIG, } from './csrfConfig.js';
|
|
79
|
+
export type { CsrfConfig, CsrfCookieOptions } from './csrfConfig.js';
|
|
80
|
+
export { serveAvatar } from './serveAvatars.js';
|
|
81
|
+
export { processUpload } from './processUpload.js';
|
|
82
|
+
export type { ProcessUploadInput, ProcessUploadResult } from './processUpload.js';
|
|
83
|
+
export { registerBindAddress, registerBoundAddress, getBindAddress, resolveDevCallbackUrl } from './bindAddress.js';
|
|
84
|
+
export { registerAvatarConfig, getAvatarConfig, DEFAULT_AVATAR_CONFIG, } from './avatarConfig.js';
|
|
85
|
+
export type { AvatarConfig, AvatarConfigInput } from './avatarConfig.js';
|
|
86
|
+
export { default as getParams } from './getParams.js';
|
|
87
|
+
export { extractTokenFromSocket } from './extractToken.js';
|
|
88
|
+
export { extractTokenFromRequest } from './extractTokenFromRequest.js';
|
|
89
|
+
export { resolveClientIp, isLoopbackIp, UNKNOWN_CLIENT_IP, deriveTokenBucketId } from './resolveClientIp.js';
|
|
90
|
+
export type { ResolveClientIpParams } from './resolveClientIp.js';
|
|
91
|
+
export { registerSessionProvider, getRegisteredSessionProvider, isSessionProviderRegistered, resetSessionProviderForTests, readSession, writeSession, removeSession, performLogout, } from './sessionProviderRegistry.js';
|
|
92
|
+
export type { SessionProvider, SessionSaveResult, SessionLogoutInput } from './sessionProviderRegistry.js';
|
|
93
|
+
export { validateRequest, isFalsy } from './validateRequest.js';
|
|
94
|
+
export type { ValidationResult } from './validateRequest.js';
|
|
95
|
+
export { checkRateLimit, getRateLimitStatus, clearRateLimit, clearAllRateLimits, registerRateLimitStrategy, getRateLimitStrategy, defaultRateLimitStrategy, } from './rateLimiter.js';
|
|
96
|
+
export type { RateLimitStrategy, CheckRateLimitParams, RateLimitResult } from './rateLimiter.js';
|
|
97
|
+
export { validateInputByType } from './runtimeTypeValidation.js';
|
|
98
|
+
export { default as allowedOrigin, normalizeOrigin } from './checkOrigin.js';
|
|
99
|
+
export { isOnline, enqueueApiRequest, enqueueSyncRequest, removeApiQueueItem, removeSyncQueueItem, removeApiQueueItemsByKey, flushApiQueue, flushSyncQueue, getApiQueueSize, getSyncQueueSize, } from './offlineQueue.js';
|
|
100
|
+
export type { QueueDropReason } from './offlineQueue.js';
|
|
101
|
+
export { socket, setSocket, incrementResponseIndex, waitForSocket } from './socketState.js';
|
|
102
|
+
export { registerSyncAbortController, unregisterSyncAbortController, abortSyncByCb, registerApiAbortController, unregisterApiAbortController, abortApiByResponseIndex, abortAllForSocket, } from './cancelRegistry.js';
|
|
103
|
+
export { getCsrfToken, clearCsrfToken, httpFetch } from './csrf.js';
|
|
104
|
+
export { TEST_RESET_TARGETS, TEST_RESET_ONLY_PARAM, isTestResetTarget, } from './testResetTargets.js';
|
|
105
|
+
export type { TestResetTarget } from './testResetTargets.js';
|
|
106
|
+
export { issueOneTimeToken, consumeOneTimeToken, consumeOneTimeTokenJson, oneTimeTokenKey, } from './oneTimeToken.js';
|
|
107
|
+
export type { OneTimeTokenHandle } from './oneTimeToken.js';
|
|
108
|
+
export { registerHook, dispatchHook, clearAllHooks, registerSyncHook, dispatchSyncHook } from './hooks/registry.js';
|
|
109
|
+
export type { DispatchResult } from './hooks/registry.js';
|
|
110
|
+
export type { HookSessionShape, HookStopSignal, HookResult, HookHandler, HookName, HookPayloads, PreApiValidatePayload, PostApiValidatePayload, PreApiExecutePayload, PostApiExecutePayload, PreApiRespondPayload, PostApiRespondPayload, ApiResponseEnvelope, PreHttpRequestPayload, PostHttpRequestPayload, PreSocketMessagePayload, PreSyncAuthorizePayload, PostSyncAuthorizePayload, PreSyncValidatePayload, PostSyncValidatePayload, PreSyncExecutePayload, PostSyncExecutePayload, PreSyncFanoutPayload, PostSyncFanoutPayload, PreSyncRecipientPayload, ApiErrorPayload, SyncErrorPayload, ApiAuthRejectedPayload, RateLimitExceededPayload, CorsRejectedPayload, CsrfMismatchPayload, PreSessionRefreshPayload, PostSessionRefreshPayload, SessionCreatedPayload, SessionRevokedPayload, OnUploadStartPayload, OnUploadCompletePayload, PreAvatarServePayload, PostAvatarServePayload, PreServerStopPayload, SyncHookName, SyncHookHandler, SyncHookPayloads, PreErrorNormalizePayload, PostErrorNormalizePayload, } from './hooks/types.js';
|