@luckystack/core 0.6.6 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +148 -107
- package/CLAUDE.md +245 -245
- package/LICENSE +21 -21
- package/README.md +93 -93
- package/dist/{chunk-WFABOO6J.js → chunk-AFG7KKMS.js} +3 -3
- package/dist/chunk-AFG7KKMS.js.map +1 -0
- package/dist/{chunk-3AWP2YEA.js → chunk-J3UOWYT4.js} +2 -28
- package/dist/chunk-J3UOWYT4.js.map +1 -0
- package/dist/{chunk-LU3IPFTR.js → chunk-LTCDQU5Z.js} +1 -1
- package/dist/chunk-LTCDQU5Z.js.map +1 -0
- package/dist/chunk-R7IRJFVC.js +67 -0
- package/dist/chunk-R7IRJFVC.js.map +1 -0
- package/dist/client.d.ts +3 -2
- package/dist/client.js +4 -4
- package/dist/client.js.map +1 -1
- package/dist/config-DZCzBNwP.d.ts +122 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +31 -0
- package/dist/eslint/index.js.map +1 -1
- package/dist/index.d.ts +21 -149
- package/dist/index.js +66 -70
- package/dist/index.js.map +1 -1
- package/dist/pageRouteValidation-sJJCX-YV.d.ts +270 -0
- package/dist/{projectConfig-XL5VB76A.js → projectConfig-DBLSQ3NY.js} +2 -2
- package/dist/{sentrySetup-4NTWLN2A.js → sentrySetup-MF6JSVWA.js} +3 -3
- package/dist/sentrySetup-MF6JSVWA.js.map +1 -0
- package/dist/{pageRouteValidation-gqktrJci.d.ts → sessionTypes-Dzen_rPK.d.ts} +23 -265
- package/docs/app-bootstrap.md +418 -418
- package/docs/config-registry.md +348 -348
- package/docs/csrf-config.md +88 -88
- package/docs/error-tracker-registry.md +194 -174
- package/docs/hooks.md +298 -298
- package/docs/rate-limit-strategy.md +170 -170
- package/docs/redis-adapter.md +111 -111
- package/docs/session-types.md +180 -180
- package/docs/socket-bootstrap.md +366 -366
- package/package.json +6 -2
- package/dist/chunk-3AWP2YEA.js.map +0 -1
- package/dist/chunk-LU3IPFTR.js.map +0 -1
- package/dist/chunk-WFABOO6J.js.map +0 -1
- /package/dist/{projectConfig-XL5VB76A.js.map → config.js.map} +0 -0
- /package/dist/{sentrySetup-4NTWLN2A.js.map → projectConfig-DBLSQ3NY.js.map} +0 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { Socket } from 'socket.io-client';
|
|
2
|
+
|
|
3
|
+
declare function sleep(ms: number): Promise<void>;
|
|
4
|
+
|
|
5
|
+
interface NotifyParam {
|
|
6
|
+
key: string;
|
|
7
|
+
value: string | number | boolean;
|
|
8
|
+
}
|
|
9
|
+
interface NotifyInput {
|
|
10
|
+
key: string;
|
|
11
|
+
params?: NotifyParam[];
|
|
12
|
+
}
|
|
13
|
+
interface Notifier {
|
|
14
|
+
success: (input: NotifyInput) => void;
|
|
15
|
+
error: (input: NotifyInput) => void;
|
|
16
|
+
info: (input: NotifyInput) => void;
|
|
17
|
+
warning: (input: NotifyInput) => void;
|
|
18
|
+
}
|
|
19
|
+
declare const registerNotifier: (notifier: Notifier) => void;
|
|
20
|
+
declare const getNotifier: () => Notifier;
|
|
21
|
+
declare const notify: Notifier;
|
|
22
|
+
|
|
23
|
+
declare let socket: Socket | null;
|
|
24
|
+
declare const setSocket: (next: Socket | null) => void;
|
|
25
|
+
declare const incrementResponseIndex: () => number;
|
|
26
|
+
declare const waitForSocket: () => Promise<Socket | null>;
|
|
27
|
+
|
|
28
|
+
/** Why a queued item was dropped (handed to its `onDrop` callback). */
|
|
29
|
+
type QueueDropReason = 'expired' | 'queue-full';
|
|
30
|
+
interface ApiQueueItem {
|
|
31
|
+
id: string;
|
|
32
|
+
key: string;
|
|
33
|
+
run: (socketInstance: Socket) => void;
|
|
34
|
+
createdAt: number;
|
|
35
|
+
/**
|
|
36
|
+
* Per-request override of `projectConfig.offlineQueue.dropPolicy`. See
|
|
37
|
+
* `SyncQueueItem.dropPolicy` for details.
|
|
38
|
+
*/
|
|
39
|
+
dropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';
|
|
40
|
+
/**
|
|
41
|
+
* Optional callback invoked when THIS item is dropped (evicted for age or
|
|
42
|
+
* because the queue was full / rejected) instead of ever running (SYNC-09).
|
|
43
|
+
* The transport wires this to settle the pending `syncRequest`/`apiRequest`
|
|
44
|
+
* promise with an error envelope, so an evicted offline request resolves
|
|
45
|
+
* rather than hanging forever. Invoked at most once; failures are swallowed.
|
|
46
|
+
*/
|
|
47
|
+
onDrop?: (reason: QueueDropReason) => void;
|
|
48
|
+
}
|
|
49
|
+
interface SyncQueueItem {
|
|
50
|
+
id: string;
|
|
51
|
+
key: string;
|
|
52
|
+
run: (socketInstance: Socket) => void;
|
|
53
|
+
createdAt: number;
|
|
54
|
+
/**
|
|
55
|
+
* Per-request override of `projectConfig.offlineQueue.dropPolicy`. Lets a
|
|
56
|
+
* specific sync ("editor cursor move") pick `'drop-oldest'` while the app
|
|
57
|
+
* default stays `'reject'` for safer sends. When omitted, falls back to
|
|
58
|
+
* the global config.
|
|
59
|
+
*/
|
|
60
|
+
dropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';
|
|
61
|
+
/** See {@link ApiQueueItem.onDrop} (SYNC-09). */
|
|
62
|
+
onDrop?: (reason: QueueDropReason) => void;
|
|
63
|
+
}
|
|
64
|
+
declare const isOnline: () => boolean;
|
|
65
|
+
declare const enqueueApiRequest: (item: ApiQueueItem) => boolean;
|
|
66
|
+
declare const enqueueSyncRequest: (item: SyncQueueItem) => boolean;
|
|
67
|
+
declare const removeApiQueueItem: (id: string) => void;
|
|
68
|
+
declare const removeSyncQueueItem: (id: string) => void;
|
|
69
|
+
declare const removeApiQueueItemsByKey: (key: string) => void;
|
|
70
|
+
declare const flushApiQueue: (canRun: () => boolean, socketInstance: Socket) => void;
|
|
71
|
+
declare const flushSyncQueue: (canRun: () => boolean, socketInstance: Socket) => void;
|
|
72
|
+
declare const getApiQueueSize: () => number;
|
|
73
|
+
declare const getSyncQueueSize: () => number;
|
|
74
|
+
|
|
75
|
+
interface ErrorParam {
|
|
76
|
+
key: string;
|
|
77
|
+
value: string | number | boolean;
|
|
78
|
+
}
|
|
79
|
+
interface ErrorResponseInput {
|
|
80
|
+
status?: unknown;
|
|
81
|
+
errorCode?: unknown;
|
|
82
|
+
errorParams?: unknown;
|
|
83
|
+
httpStatus?: unknown;
|
|
84
|
+
}
|
|
85
|
+
interface NormalizedErrorResponse {
|
|
86
|
+
status: 'error';
|
|
87
|
+
message: string;
|
|
88
|
+
errorCode: string;
|
|
89
|
+
errorParams?: ErrorParam[];
|
|
90
|
+
httpStatus?: number;
|
|
91
|
+
}
|
|
92
|
+
declare const INVALID_ERROR_RESPONSE_CODE = "error.invalidResponse";
|
|
93
|
+
declare const isErrorParamArray: (value: unknown) => value is ErrorParam[];
|
|
94
|
+
declare const normalizeErrorResponseCore: ({ response, fallbackHttpStatus, fallbackErrorCode, resolveMessage, }: {
|
|
95
|
+
response: ErrorResponseInput;
|
|
96
|
+
fallbackHttpStatus?: number;
|
|
97
|
+
fallbackErrorCode?: string;
|
|
98
|
+
resolveMessage?: (args: {
|
|
99
|
+
errorCode: string;
|
|
100
|
+
errorParams?: ErrorParam[];
|
|
101
|
+
}) => string;
|
|
102
|
+
}) => NormalizedErrorResponse;
|
|
103
|
+
declare const defaultHttpStatusForResponse: ({ status, explicitHttpStatus, fallbackErrorStatus, }: {
|
|
104
|
+
status: "success" | "error";
|
|
105
|
+
explicitHttpStatus?: number;
|
|
106
|
+
fallbackErrorStatus?: number;
|
|
107
|
+
}) => number;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Fetch (or return the cached) CSRF token for the active session. Returns
|
|
111
|
+
* null in token mode or when no session exists. Callers should attach the
|
|
112
|
+
* returned value as the `X-CSRF-Token` request header.
|
|
113
|
+
*/
|
|
114
|
+
declare const getCsrfToken: () => Promise<string | null>;
|
|
115
|
+
/** Drop the cached token. Call this on logout or when a 403 csrfMismatch is seen. */
|
|
116
|
+
declare const clearCsrfToken: () => void;
|
|
117
|
+
/**
|
|
118
|
+
* Drop-in replacement for `fetch` that automatically attaches the framework's
|
|
119
|
+
* `X-CSRF-Token` and `X-Request-Id` headers to state-changing requests in
|
|
120
|
+
* cookie mode. Use this for any project-side HTTP call that hits a
|
|
121
|
+
* LuckyStack `/api/*`, `/sync/*`, or `/auth/api/*` endpoint.
|
|
122
|
+
*
|
|
123
|
+
* Behavior:
|
|
124
|
+
* - GET / OPTIONS / HEAD: passes through without CSRF (read-only).
|
|
125
|
+
* - Token mode: passes through without CSRF (immune by design).
|
|
126
|
+
* - Cookie mode + state-changing method: lazily fetches `/auth/csrf` once,
|
|
127
|
+
* caches, attaches as `x-csrf-token`. On a 403 `auth.csrfMismatch`
|
|
128
|
+
* response, clears the cache and retries once.
|
|
129
|
+
* - `credentials: 'include'` is forced unless the caller overrode it.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* import { httpFetch } from '@luckystack/core/client';
|
|
133
|
+
* const res = await httpFetch('/api/system/myRoute/v1', {
|
|
134
|
+
* method: 'POST',
|
|
135
|
+
* body: JSON.stringify({ ... }),
|
|
136
|
+
* });
|
|
137
|
+
*/
|
|
138
|
+
declare const httpFetch: typeof fetch;
|
|
139
|
+
|
|
140
|
+
interface ServiceRouteParseSuccess {
|
|
141
|
+
status: 'success';
|
|
142
|
+
normalizedRouteName: string;
|
|
143
|
+
service: string;
|
|
144
|
+
routeName: string;
|
|
145
|
+
}
|
|
146
|
+
interface ServiceRouteParseError {
|
|
147
|
+
status: 'error';
|
|
148
|
+
reason: string;
|
|
149
|
+
}
|
|
150
|
+
type ServiceRouteParseResult = ServiceRouteParseSuccess | ServiceRouteParseError;
|
|
151
|
+
interface TransportRouteParseSuccess {
|
|
152
|
+
status: 'success';
|
|
153
|
+
normalizedFullName: string;
|
|
154
|
+
version: string;
|
|
155
|
+
serviceRoute: ServiceRouteParseSuccess;
|
|
156
|
+
}
|
|
157
|
+
interface TransportRouteParseError {
|
|
158
|
+
status: 'error';
|
|
159
|
+
reason: string;
|
|
160
|
+
}
|
|
161
|
+
type TransportRouteParseResult = TransportRouteParseSuccess | TransportRouteParseError;
|
|
162
|
+
declare const parseServiceRouteName: (value: string) => ServiceRouteParseResult;
|
|
163
|
+
declare const parseTransportRouteName: ({ value, prefix, }: {
|
|
164
|
+
value: string;
|
|
165
|
+
prefix: "api" | "sync";
|
|
166
|
+
}) => TransportRouteParseResult;
|
|
167
|
+
|
|
168
|
+
declare const socketEventNames: {
|
|
169
|
+
readonly apiRequest: "apiRequest";
|
|
170
|
+
readonly apiCancel: "apiCancel";
|
|
171
|
+
readonly sync: "sync";
|
|
172
|
+
readonly syncCancel: "syncCancel";
|
|
173
|
+
readonly joinRoom: "joinRoom";
|
|
174
|
+
readonly leaveRoom: "leaveRoom";
|
|
175
|
+
readonly getJoinedRooms: "getJoinedRooms";
|
|
176
|
+
readonly updateLocation: "updateLocation";
|
|
177
|
+
readonly updateSession: "updateSession";
|
|
178
|
+
readonly sessionReplaced: "sessionReplaced";
|
|
179
|
+
readonly logout: "logout";
|
|
180
|
+
readonly intentionalDisconnect: "intentionalDisconnect";
|
|
181
|
+
readonly intentionalReconnect: "intentionalReconnect";
|
|
182
|
+
readonly activity: "activity";
|
|
183
|
+
readonly userAfk: "userAfk";
|
|
184
|
+
readonly userBack: "userBack";
|
|
185
|
+
readonly userLeft: "userLeft";
|
|
186
|
+
readonly connect: "connect";
|
|
187
|
+
readonly disconnect: "disconnect";
|
|
188
|
+
readonly reconnectAttempt: "reconnect_attempt";
|
|
189
|
+
readonly connectError: "connect_error";
|
|
190
|
+
readonly apiResponsePrefix: "apiResponse-";
|
|
191
|
+
readonly apiStreamPrefix: "apiStream-";
|
|
192
|
+
readonly syncResponsePrefix: "sync-";
|
|
193
|
+
readonly syncProgressPrefix: "sync-progress-";
|
|
194
|
+
readonly joinRoomResponsePrefix: "joinRoom-";
|
|
195
|
+
readonly leaveRoomResponsePrefix: "leaveRoom-";
|
|
196
|
+
readonly getJoinedRoomsResponsePrefix: "getJoinedRooms-";
|
|
197
|
+
};
|
|
198
|
+
declare const buildApiResponseEventName: (responseIndex: number | string) => string;
|
|
199
|
+
declare const buildApiStreamEventName: (responseIndex: number | string) => string;
|
|
200
|
+
declare const buildSyncResponseEventName: (responseIndex: number | string) => string;
|
|
201
|
+
declare const buildSyncProgressEventName: (responseIndex: number | string) => string;
|
|
202
|
+
declare const buildJoinRoomResponseEventName: (responseIndex: number | string) => string;
|
|
203
|
+
declare const buildLeaveRoomResponseEventName: (responseIndex: number | string) => string;
|
|
204
|
+
declare const buildGetJoinedRoomsResponseEventName: (responseIndex: number | string) => string;
|
|
205
|
+
|
|
206
|
+
type SOCKETSTATUS = "CONNECTED" | "DISCONNECTED" | "AFK" | "RECONNECTING" | "STARTUP";
|
|
207
|
+
interface statusContent {
|
|
208
|
+
status: SOCKETSTATUS;
|
|
209
|
+
reconnectAttempt?: number;
|
|
210
|
+
endTime?: number;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
type LoggerContext = Record<string, unknown>;
|
|
214
|
+
interface Logger {
|
|
215
|
+
debug: (message: string, context?: LoggerContext) => void;
|
|
216
|
+
info: (message: string, context?: LoggerContext) => void;
|
|
217
|
+
warn: (message: string, context?: LoggerContext) => void;
|
|
218
|
+
error: (message: string, error?: unknown, context?: LoggerContext) => void;
|
|
219
|
+
}
|
|
220
|
+
declare const createDevLogger: () => Logger;
|
|
221
|
+
declare const registerLogger: (logger: Logger) => void;
|
|
222
|
+
declare const getLogger: () => Logger;
|
|
223
|
+
declare const isLoggerRegistered: () => boolean;
|
|
224
|
+
declare const resetLoggerForTests: () => void;
|
|
225
|
+
|
|
226
|
+
type HttpMethodLiteral = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
227
|
+
type ApiMethodMap = Record<string, Record<string, Record<string, HttpMethodLiteral>>>;
|
|
228
|
+
declare const registerApiMethodMap: (map: ApiMethodMap) => void;
|
|
229
|
+
declare const getRegisteredApiMethod: (pagePath: string, apiName: string, version: string) => HttpMethodLiteral | undefined;
|
|
230
|
+
declare const isApiMethodMapRegistered: () => boolean;
|
|
231
|
+
|
|
232
|
+
interface PageRouteRules {
|
|
233
|
+
/**
|
|
234
|
+
* Single-character prefix that marks a folder as **invisible-parent** for
|
|
235
|
+
* page routing. Default: `'_'`.
|
|
236
|
+
*/
|
|
237
|
+
privateFolderPrefix: string;
|
|
238
|
+
/**
|
|
239
|
+
* Folder names that are reserved for framework-internal use and may NEVER
|
|
240
|
+
* host a `page.tsx`, nested or otherwise. Scaffold + page-discovery emit
|
|
241
|
+
* a hard error when a page is placed inside one.
|
|
242
|
+
*/
|
|
243
|
+
scaffoldIgnoredFolders: readonly string[];
|
|
244
|
+
}
|
|
245
|
+
declare const DEFAULT_PAGE_ROUTE_RULES: PageRouteRules;
|
|
246
|
+
interface PagePathValidationResult {
|
|
247
|
+
/** `true` when the page file produces a routeable URL. */
|
|
248
|
+
valid: boolean;
|
|
249
|
+
/** Computed URL when `valid`. Always begins with `/`. `/` for root page. */
|
|
250
|
+
route?: string;
|
|
251
|
+
/** Human-readable explanation when `!valid`. */
|
|
252
|
+
reason?: string;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Validate a `page.tsx` file path relative to `src/`. Accepts both
|
|
256
|
+
* forward and back slashes, with or without a leading `./` or `/`.
|
|
257
|
+
* The filename **must** be `page.tsx` (or `page.jsx`); other filenames
|
|
258
|
+
* return `valid: false`.
|
|
259
|
+
*
|
|
260
|
+
* Examples (defaults):
|
|
261
|
+
* admin/page.tsx -> { valid: true, route: '/admin' }
|
|
262
|
+
* _housing/renting/page.tsx -> { valid: true, route: '/renting' }
|
|
263
|
+
* _housing/page.tsx -> { valid: false, reason: 'no URL segment left after stripping underscore folders' }
|
|
264
|
+
* _api/something/page.tsx -> { valid: false, reason: 'page.tsx cannot live inside reserved folder _api' }
|
|
265
|
+
* page.tsx -> { valid: true, route: '/' }
|
|
266
|
+
* some/route.tsx -> { valid: false, reason: 'not a page file (expected page.tsx)' }
|
|
267
|
+
*/
|
|
268
|
+
declare const validatePagePath: (srcRelativePath: string, rules?: PageRouteRules) => PagePathValidationResult;
|
|
269
|
+
|
|
270
|
+
export { type QueueDropReason as $, type ApiMethodMap as A, normalizeErrorResponseCore as B, notify as C, DEFAULT_PAGE_ROUTE_RULES as D, parseServiceRouteName as E, registerApiMethodMap as F, registerLogger as G, type HttpMethodLiteral as H, registerNotifier as I, removeApiQueueItem as J, removeApiQueueItemsByKey as K, type Logger as L, removeSyncQueueItem as M, type Notifier as N, setSocket as O, type PagePathValidationResult as P, sleep as Q, socket as R, type SOCKETSTATUS as S, socketEventNames as T, type statusContent as U, validatePagePath as V, waitForSocket as W, type ErrorParam as X, type ErrorResponseInput as Y, type NormalizedErrorResponse as Z, INVALID_ERROR_RESPONSE_CODE as _, type LoggerContext as a, type ServiceRouteParseError as a0, type ServiceRouteParseResult as a1, type ServiceRouteParseSuccess as a2, type TransportRouteParseError as a3, type TransportRouteParseResult as a4, type TransportRouteParseSuccess as a5, createDevLogger as a6, defaultHttpStatusForResponse as a7, isErrorParamArray as a8, isLoggerRegistered as a9, parseTransportRouteName as aa, resetLoggerForTests as ab, type NotifyInput as b, type NotifyParam as c, type PageRouteRules as d, buildApiResponseEventName as e, buildApiStreamEventName as f, buildGetJoinedRoomsResponseEventName as g, buildJoinRoomResponseEventName as h, buildLeaveRoomResponseEventName as i, buildSyncProgressEventName as j, buildSyncResponseEventName as k, clearCsrfToken as l, enqueueApiRequest as m, enqueueSyncRequest as n, flushApiQueue as o, flushSyncQueue as p, getApiQueueSize as q, getCsrfToken as r, getLogger as s, getNotifier as t, getRegisteredApiMethod as u, getSyncQueueSize as v, httpFetch as w, incrementResponseIndex as x, isApiMethodMapRegistered as y, isOnline as z };
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getProjectName,
|
|
5
5
|
isProjectConfigRegistered,
|
|
6
6
|
registerProjectConfig
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LTCDQU5Z.js";
|
|
8
8
|
export {
|
|
9
9
|
DEFAULT_PROJECT_CONFIG,
|
|
10
10
|
getProjectConfig,
|
|
@@ -12,4 +12,4 @@ export {
|
|
|
12
12
|
isProjectConfigRegistered,
|
|
13
13
|
registerProjectConfig
|
|
14
14
|
};
|
|
15
|
-
//# sourceMappingURL=projectConfig-
|
|
15
|
+
//# sourceMappingURL=projectConfig-DBLSQ3NY.js.map
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
initSharedSentry,
|
|
5
5
|
setSentryUser,
|
|
6
6
|
startSpan
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-J3UOWYT4.js";
|
|
8
|
+
import "./chunk-LTCDQU5Z.js";
|
|
9
9
|
export {
|
|
10
10
|
captureException,
|
|
11
11
|
captureMessage,
|
|
@@ -13,4 +13,4 @@ export {
|
|
|
13
13
|
setSentryUser,
|
|
14
14
|
startSpan
|
|
15
15
|
};
|
|
16
|
-
//# sourceMappingURL=sentrySetup-
|
|
16
|
+
//# sourceMappingURL=sentrySetup-MF6JSVWA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { Socket } from 'socket.io-client';
|
|
2
|
-
|
|
3
|
-
declare function sleep(ms: number): Promise<void>;
|
|
4
|
-
|
|
5
1
|
type DeepPartial<T> = {
|
|
6
2
|
[K in keyof T]?: T[K] extends object | undefined ? DeepPartial<NonNullable<T[K]>> : T[K];
|
|
7
3
|
};
|
|
@@ -734,214 +730,33 @@ declare const getProjectConfig: () => ProjectConfig;
|
|
|
734
730
|
declare const getProjectName: () => string;
|
|
735
731
|
declare const isProjectConfigRegistered: () => boolean;
|
|
736
732
|
|
|
737
|
-
interface NotifyParam {
|
|
738
|
-
key: string;
|
|
739
|
-
value: string | number | boolean;
|
|
740
|
-
}
|
|
741
|
-
interface NotifyInput {
|
|
742
|
-
key: string;
|
|
743
|
-
params?: NotifyParam[];
|
|
744
|
-
}
|
|
745
|
-
interface Notifier {
|
|
746
|
-
success: (input: NotifyInput) => void;
|
|
747
|
-
error: (input: NotifyInput) => void;
|
|
748
|
-
info: (input: NotifyInput) => void;
|
|
749
|
-
warning: (input: NotifyInput) => void;
|
|
750
|
-
}
|
|
751
|
-
declare const registerNotifier: (notifier: Notifier) => void;
|
|
752
|
-
declare const getNotifier: () => Notifier;
|
|
753
|
-
declare const notify: Notifier;
|
|
754
|
-
|
|
755
|
-
declare let socket: Socket | null;
|
|
756
|
-
declare const setSocket: (next: Socket | null) => void;
|
|
757
|
-
declare const incrementResponseIndex: () => number;
|
|
758
|
-
declare const waitForSocket: () => Promise<Socket | null>;
|
|
759
|
-
|
|
760
|
-
/** Why a queued item was dropped (handed to its `onDrop` callback). */
|
|
761
|
-
type QueueDropReason = 'expired' | 'queue-full';
|
|
762
|
-
interface ApiQueueItem {
|
|
763
|
-
id: string;
|
|
764
|
-
key: string;
|
|
765
|
-
run: (socketInstance: Socket) => void;
|
|
766
|
-
createdAt: number;
|
|
767
|
-
/**
|
|
768
|
-
* Per-request override of `projectConfig.offlineQueue.dropPolicy`. See
|
|
769
|
-
* `SyncQueueItem.dropPolicy` for details.
|
|
770
|
-
*/
|
|
771
|
-
dropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';
|
|
772
|
-
/**
|
|
773
|
-
* Optional callback invoked when THIS item is dropped (evicted for age or
|
|
774
|
-
* because the queue was full / rejected) instead of ever running (SYNC-09).
|
|
775
|
-
* The transport wires this to settle the pending `syncRequest`/`apiRequest`
|
|
776
|
-
* promise with an error envelope, so an evicted offline request resolves
|
|
777
|
-
* rather than hanging forever. Invoked at most once; failures are swallowed.
|
|
778
|
-
*/
|
|
779
|
-
onDrop?: (reason: QueueDropReason) => void;
|
|
780
|
-
}
|
|
781
|
-
interface SyncQueueItem {
|
|
782
|
-
id: string;
|
|
783
|
-
key: string;
|
|
784
|
-
run: (socketInstance: Socket) => void;
|
|
785
|
-
createdAt: number;
|
|
786
|
-
/**
|
|
787
|
-
* Per-request override of `projectConfig.offlineQueue.dropPolicy`. Lets a
|
|
788
|
-
* specific sync ("editor cursor move") pick `'drop-oldest'` while the app
|
|
789
|
-
* default stays `'reject'` for safer sends. When omitted, falls back to
|
|
790
|
-
* the global config.
|
|
791
|
-
*/
|
|
792
|
-
dropPolicy?: 'drop-oldest' | 'drop-newest' | 'reject';
|
|
793
|
-
/** See {@link ApiQueueItem.onDrop} (SYNC-09). */
|
|
794
|
-
onDrop?: (reason: QueueDropReason) => void;
|
|
795
|
-
}
|
|
796
|
-
declare const isOnline: () => boolean;
|
|
797
|
-
declare const enqueueApiRequest: (item: ApiQueueItem) => boolean;
|
|
798
|
-
declare const enqueueSyncRequest: (item: SyncQueueItem) => boolean;
|
|
799
|
-
declare const removeApiQueueItem: (id: string) => void;
|
|
800
|
-
declare const removeSyncQueueItem: (id: string) => void;
|
|
801
|
-
declare const removeApiQueueItemsByKey: (key: string) => void;
|
|
802
|
-
declare const flushApiQueue: (canRun: () => boolean, socketInstance: Socket) => void;
|
|
803
|
-
declare const flushSyncQueue: (canRun: () => boolean, socketInstance: Socket) => void;
|
|
804
|
-
declare const getApiQueueSize: () => number;
|
|
805
|
-
declare const getSyncQueueSize: () => number;
|
|
806
|
-
|
|
807
|
-
interface ErrorParam {
|
|
808
|
-
key: string;
|
|
809
|
-
value: string | number | boolean;
|
|
810
|
-
}
|
|
811
|
-
interface ErrorResponseInput {
|
|
812
|
-
status?: unknown;
|
|
813
|
-
errorCode?: unknown;
|
|
814
|
-
errorParams?: unknown;
|
|
815
|
-
httpStatus?: unknown;
|
|
816
|
-
}
|
|
817
|
-
interface NormalizedErrorResponse {
|
|
818
|
-
status: 'error';
|
|
819
|
-
message: string;
|
|
820
|
-
errorCode: string;
|
|
821
|
-
errorParams?: ErrorParam[];
|
|
822
|
-
httpStatus?: number;
|
|
823
|
-
}
|
|
824
|
-
declare const INVALID_ERROR_RESPONSE_CODE = "error.invalidResponse";
|
|
825
|
-
declare const isErrorParamArray: (value: unknown) => value is ErrorParam[];
|
|
826
|
-
declare const normalizeErrorResponseCore: ({ response, fallbackHttpStatus, fallbackErrorCode, resolveMessage, }: {
|
|
827
|
-
response: ErrorResponseInput;
|
|
828
|
-
fallbackHttpStatus?: number;
|
|
829
|
-
fallbackErrorCode?: string;
|
|
830
|
-
resolveMessage?: (args: {
|
|
831
|
-
errorCode: string;
|
|
832
|
-
errorParams?: ErrorParam[];
|
|
833
|
-
}) => string;
|
|
834
|
-
}) => NormalizedErrorResponse;
|
|
835
|
-
declare const defaultHttpStatusForResponse: ({ status, explicitHttpStatus, fallbackErrorStatus, }: {
|
|
836
|
-
status: "success" | "error";
|
|
837
|
-
explicitHttpStatus?: number;
|
|
838
|
-
fallbackErrorStatus?: number;
|
|
839
|
-
}) => number;
|
|
840
|
-
|
|
841
733
|
/**
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
/** Drop the cached token. Call this on logout or when a 403 csrfMismatch is seen. */
|
|
848
|
-
declare const clearCsrfToken: () => void;
|
|
849
|
-
/**
|
|
850
|
-
* Drop-in replacement for `fetch` that automatically attaches the framework's
|
|
851
|
-
* `X-CSRF-Token` and `X-Request-Id` headers to state-changing requests in
|
|
852
|
-
* cookie mode. Use this for any project-side HTTP call that hits a
|
|
853
|
-
* LuckyStack `/api/*`, `/sync/*`, or `/auth/api/*` endpoint.
|
|
734
|
+
* What `T` becomes after `JSON.stringify` → `JSON.parse`, at the type level.
|
|
735
|
+
*
|
|
736
|
+
* The mirror of what the codegen does to route OUTPUTS. Use it wherever you
|
|
737
|
+
* hand-write a type for something that CROSSES THE WIRE — the client never
|
|
738
|
+
* receives your server type, it receives JSON, and JSON has no `Date`:
|
|
854
739
|
*
|
|
855
|
-
*
|
|
856
|
-
* - GET / OPTIONS / HEAD: passes through without CSRF (read-only).
|
|
857
|
-
* - Token mode: passes through without CSRF (immune by design).
|
|
858
|
-
* - Cookie mode + state-changing method: lazily fetches `/auth/csrf` once,
|
|
859
|
-
* caches, attaches as `x-csrf-token`. On a 403 `auth.csrfMismatch`
|
|
860
|
-
* response, clears the cache and retries once.
|
|
861
|
-
* - `credentials: 'include'` is forced unless the caller overrode it.
|
|
740
|
+
* type ClientUser = Jsonify<User>; // createdAt: string, not Date
|
|
862
741
|
*
|
|
863
|
-
*
|
|
864
|
-
*
|
|
865
|
-
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
868
|
-
*
|
|
742
|
+
* Without it a type says `Date`, the client gets `"2026-07-15T15:20:15.553Z"`,
|
|
743
|
+
* and `user.createdAt.getTime()` compiles and then throws at runtime.
|
|
744
|
+
*
|
|
745
|
+
* Deliberately SHALLOW — one level of properties, descending only into arrays.
|
|
746
|
+
* A fully recursive object mapper does not survive contact with Prisma's
|
|
747
|
+
* self-referential `JsonValue`. Already-JSON-stable values are therefore an
|
|
748
|
+
* identity boundary: do not recursively rebuild what JSON already represents.
|
|
749
|
+
* The NON-DISTRIBUTIVE tuple guard is load-bearing — it recognizes the complete
|
|
750
|
+
* `JsonValue` union before the Date branch distributes `Date | null` into
|
|
751
|
+
* `string | null`.
|
|
869
752
|
*/
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
routeName: string;
|
|
877
|
-
}
|
|
878
|
-
interface ServiceRouteParseError {
|
|
879
|
-
status: 'error';
|
|
880
|
-
reason: string;
|
|
881
|
-
}
|
|
882
|
-
type ServiceRouteParseResult = ServiceRouteParseSuccess | ServiceRouteParseError;
|
|
883
|
-
interface TransportRouteParseSuccess {
|
|
884
|
-
status: 'success';
|
|
885
|
-
normalizedFullName: string;
|
|
886
|
-
version: string;
|
|
887
|
-
serviceRoute: ServiceRouteParseSuccess;
|
|
888
|
-
}
|
|
889
|
-
interface TransportRouteParseError {
|
|
890
|
-
status: 'error';
|
|
891
|
-
reason: string;
|
|
892
|
-
}
|
|
893
|
-
type TransportRouteParseResult = TransportRouteParseSuccess | TransportRouteParseError;
|
|
894
|
-
declare const parseServiceRouteName: (value: string) => ServiceRouteParseResult;
|
|
895
|
-
declare const parseTransportRouteName: ({ value, prefix, }: {
|
|
896
|
-
value: string;
|
|
897
|
-
prefix: "api" | "sync";
|
|
898
|
-
}) => TransportRouteParseResult;
|
|
899
|
-
|
|
900
|
-
declare const socketEventNames: {
|
|
901
|
-
readonly apiRequest: "apiRequest";
|
|
902
|
-
readonly apiCancel: "apiCancel";
|
|
903
|
-
readonly sync: "sync";
|
|
904
|
-
readonly syncCancel: "syncCancel";
|
|
905
|
-
readonly joinRoom: "joinRoom";
|
|
906
|
-
readonly leaveRoom: "leaveRoom";
|
|
907
|
-
readonly getJoinedRooms: "getJoinedRooms";
|
|
908
|
-
readonly updateLocation: "updateLocation";
|
|
909
|
-
readonly updateSession: "updateSession";
|
|
910
|
-
readonly sessionReplaced: "sessionReplaced";
|
|
911
|
-
readonly logout: "logout";
|
|
912
|
-
readonly intentionalDisconnect: "intentionalDisconnect";
|
|
913
|
-
readonly intentionalReconnect: "intentionalReconnect";
|
|
914
|
-
readonly activity: "activity";
|
|
915
|
-
readonly userAfk: "userAfk";
|
|
916
|
-
readonly userBack: "userBack";
|
|
917
|
-
readonly userLeft: "userLeft";
|
|
918
|
-
readonly connect: "connect";
|
|
919
|
-
readonly disconnect: "disconnect";
|
|
920
|
-
readonly reconnectAttempt: "reconnect_attempt";
|
|
921
|
-
readonly connectError: "connect_error";
|
|
922
|
-
readonly apiResponsePrefix: "apiResponse-";
|
|
923
|
-
readonly apiStreamPrefix: "apiStream-";
|
|
924
|
-
readonly syncResponsePrefix: "sync-";
|
|
925
|
-
readonly syncProgressPrefix: "sync-progress-";
|
|
926
|
-
readonly joinRoomResponsePrefix: "joinRoom-";
|
|
927
|
-
readonly leaveRoomResponsePrefix: "leaveRoom-";
|
|
928
|
-
readonly getJoinedRoomsResponsePrefix: "getJoinedRooms-";
|
|
753
|
+
type JsonStableValue = string | number | boolean | null | JsonStableValue[] | {
|
|
754
|
+
[key: string]: JsonStableValue | undefined;
|
|
755
|
+
};
|
|
756
|
+
type JsonifyValue<TValue> = [TValue] extends [JsonStableValue] ? TValue : TValue extends Date ? string : TValue extends readonly (infer TElement)[] ? JsonifyValue<TElement>[] : TValue;
|
|
757
|
+
type Jsonify<T> = {
|
|
758
|
+
[TKey in keyof T]: JsonifyValue<T[TKey]>;
|
|
929
759
|
};
|
|
930
|
-
declare const buildApiResponseEventName: (responseIndex: number | string) => string;
|
|
931
|
-
declare const buildApiStreamEventName: (responseIndex: number | string) => string;
|
|
932
|
-
declare const buildSyncResponseEventName: (responseIndex: number | string) => string;
|
|
933
|
-
declare const buildSyncProgressEventName: (responseIndex: number | string) => string;
|
|
934
|
-
declare const buildJoinRoomResponseEventName: (responseIndex: number | string) => string;
|
|
935
|
-
declare const buildLeaveRoomResponseEventName: (responseIndex: number | string) => string;
|
|
936
|
-
declare const buildGetJoinedRoomsResponseEventName: (responseIndex: number | string) => string;
|
|
937
|
-
|
|
938
|
-
type SOCKETSTATUS = "CONNECTED" | "DISCONNECTED" | "AFK" | "RECONNECTING" | "STARTUP";
|
|
939
|
-
interface statusContent {
|
|
940
|
-
status: SOCKETSTATUS;
|
|
941
|
-
reconnectAttempt?: number;
|
|
942
|
-
endTime?: number;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
760
|
interface SessionLocation {
|
|
946
761
|
pathName: string;
|
|
947
762
|
searchParams?: Record<string, string>;
|
|
@@ -994,61 +809,4 @@ interface AuthProps {
|
|
|
994
809
|
}[];
|
|
995
810
|
}
|
|
996
811
|
|
|
997
|
-
type
|
|
998
|
-
interface Logger {
|
|
999
|
-
debug: (message: string, context?: LoggerContext) => void;
|
|
1000
|
-
info: (message: string, context?: LoggerContext) => void;
|
|
1001
|
-
warn: (message: string, context?: LoggerContext) => void;
|
|
1002
|
-
error: (message: string, error?: unknown, context?: LoggerContext) => void;
|
|
1003
|
-
}
|
|
1004
|
-
declare const createDevLogger: () => Logger;
|
|
1005
|
-
declare const registerLogger: (logger: Logger) => void;
|
|
1006
|
-
declare const getLogger: () => Logger;
|
|
1007
|
-
declare const isLoggerRegistered: () => boolean;
|
|
1008
|
-
declare const resetLoggerForTests: () => void;
|
|
1009
|
-
|
|
1010
|
-
type HttpMethodLiteral = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
1011
|
-
type ApiMethodMap = Record<string, Record<string, Record<string, HttpMethodLiteral>>>;
|
|
1012
|
-
declare const registerApiMethodMap: (map: ApiMethodMap) => void;
|
|
1013
|
-
declare const getRegisteredApiMethod: (pagePath: string, apiName: string, version: string) => HttpMethodLiteral | undefined;
|
|
1014
|
-
declare const isApiMethodMapRegistered: () => boolean;
|
|
1015
|
-
|
|
1016
|
-
interface PageRouteRules {
|
|
1017
|
-
/**
|
|
1018
|
-
* Single-character prefix that marks a folder as **invisible-parent** for
|
|
1019
|
-
* page routing. Default: `'_'`.
|
|
1020
|
-
*/
|
|
1021
|
-
privateFolderPrefix: string;
|
|
1022
|
-
/**
|
|
1023
|
-
* Folder names that are reserved for framework-internal use and may NEVER
|
|
1024
|
-
* host a `page.tsx`, nested or otherwise. Scaffold + page-discovery emit
|
|
1025
|
-
* a hard error when a page is placed inside one.
|
|
1026
|
-
*/
|
|
1027
|
-
scaffoldIgnoredFolders: readonly string[];
|
|
1028
|
-
}
|
|
1029
|
-
declare const DEFAULT_PAGE_ROUTE_RULES: PageRouteRules;
|
|
1030
|
-
interface PagePathValidationResult {
|
|
1031
|
-
/** `true` when the page file produces a routeable URL. */
|
|
1032
|
-
valid: boolean;
|
|
1033
|
-
/** Computed URL when `valid`. Always begins with `/`. `/` for root page. */
|
|
1034
|
-
route?: string;
|
|
1035
|
-
/** Human-readable explanation when `!valid`. */
|
|
1036
|
-
reason?: string;
|
|
1037
|
-
}
|
|
1038
|
-
/**
|
|
1039
|
-
* Validate a `page.tsx` file path relative to `src/`. Accepts both
|
|
1040
|
-
* forward and back slashes, with or without a leading `./` or `/`.
|
|
1041
|
-
* The filename **must** be `page.tsx` (or `page.jsx`); other filenames
|
|
1042
|
-
* return `valid: false`.
|
|
1043
|
-
*
|
|
1044
|
-
* Examples (defaults):
|
|
1045
|
-
* admin/page.tsx -> { valid: true, route: '/admin' }
|
|
1046
|
-
* _housing/renting/page.tsx -> { valid: true, route: '/renting' }
|
|
1047
|
-
* _housing/page.tsx -> { valid: false, reason: 'no URL segment left after stripping underscore folders' }
|
|
1048
|
-
* _api/something/page.tsx -> { valid: false, reason: 'page.tsx cannot live inside reserved folder _api' }
|
|
1049
|
-
* page.tsx -> { valid: true, route: '/' }
|
|
1050
|
-
* some/route.tsx -> { valid: false, reason: 'not a page file (expected page.tsx)' }
|
|
1051
|
-
*/
|
|
1052
|
-
declare const validatePagePath: (srcRelativePath: string, rules?: PageRouteRules) => PagePathValidationResult;
|
|
1053
|
-
|
|
1054
|
-
export { setSocket as $, type ApiMethodMap as A, type BaseSessionLayout as B, getProjectConfig as C, DEFAULT_PAGE_ROUTE_RULES as D, getRegisteredApiMethod as E, getSyncQueueSize as F, httpFetch as G, type HttpMethodLiteral as H, incrementResponseIndex as I, isApiMethodMapRegistered as J, isOnline as K, type Logger as L, isProjectConfigRegistered as M, type Notifier as N, normalizeErrorResponseCore as O, type PagePathValidationResult as P, notify as Q, type RateLimitingConfig as R, type SOCKETSTATUS as S, parseServiceRouteName as T, registerApiMethodMap as U, registerLogger as V, registerNotifier as W, registerProjectConfig as X, removeApiQueueItem as Y, removeApiQueueItemsByKey as Z, removeSyncQueueItem as _, type AppConfig as a, sleep as a0, socket as a1, socketEventNames as a2, type statusContent as a3, validatePagePath as a4, waitForSocket as a5, type ErrorParam as a6, type ErrorResponseInput as a7, type NormalizedErrorResponse as a8, type HealthHashConfig as a9, type TransportRouteParseResult as aA, type TransportRouteParseSuccess as aB, type ValidationConfig as aC, createDevLogger as aD, deepMerge as aE, defaultHttpStatusForResponse as aF, getProjectName as aG, isErrorParamArray as aH, isLoggerRegistered as aI, isPlainObject as aJ, parseTransportRouteName as aK, resetLoggerForTests as aL, type DeepPartial as aa, type ApiConfig as ab, type AuthConfig as ac, type AuthRateLimitConfig as ad, type CorsConfig as ae, DEFAULT_PROJECT_CONFIG as af, type DevConfig as ag, type HttpConfig as ah, type HttpStreamConfig as ai, INVALID_ERROR_RESPONSE_CODE as aj, type OfflineQueueConfig as ak, type PasswordPolicyConfig as al, type PathsConfig as am, type ProjectConfigInput as an, type QueueDropReason as ao, type RateLimitIdentity as ap, type RateLimitIdentityParams as aq, type SecurityHeadersConfig as ar, type ServiceRouteParseError as as, type ServiceRouteParseResult as at, type ServiceRouteParseSuccess as au, type SocketConfig as av, type SyncConfig as aw, type SyncFlushPressureConfig as ax, type SyncStreamThrottleConfig as ay, type TransportRouteParseError as az, type AuthProps as b, type LoggerContext as c, type LoggingConfig as d, type NotifyInput as e, type NotifyParam as f, type PageRouteRules as g, type ProjectConfig as h, type SessionConfig as i, type SessionLocation as j, buildApiResponseEventName as k, buildApiStreamEventName as l, buildGetJoinedRoomsResponseEventName as m, buildJoinRoomResponseEventName as n, buildLeaveRoomResponseEventName as o, buildSyncProgressEventName as p, buildSyncResponseEventName as q, clearCsrfToken as r, enqueueApiRequest as s, enqueueSyncRequest as t, flushApiQueue as u, flushSyncQueue as v, getApiQueueSize as w, getCsrfToken as x, getLogger as y, getNotifier as z };
|
|
812
|
+
export { type AppConfig as A, type BaseSessionLayout as B, type CorsConfig as C, type DeepPartial as D, type HealthHashConfig as H, type Jsonify as J, type LoggingConfig as L, type OfflineQueueConfig as O, type ProjectConfig as P, type RateLimitingConfig as R, type SessionConfig as S, type ValidationConfig as V, type AuthProps as a, type SessionLocation as b, type ApiConfig as c, type AuthConfig as d, type AuthRateLimitConfig as e, DEFAULT_PROJECT_CONFIG as f, getProjectConfig as g, type DevConfig as h, isProjectConfigRegistered as i, type HttpConfig as j, type HttpStreamConfig as k, type PasswordPolicyConfig as l, type PathsConfig as m, type ProjectConfigInput as n, type RateLimitIdentity as o, type RateLimitIdentityParams as p, type SecurityHeadersConfig as q, registerProjectConfig as r, type SocketConfig as s, type SyncConfig as t, type SyncFlushPressureConfig as u, type SyncStreamThrottleConfig as v, deepMerge as w, getProjectName as x, isPlainObject as y };
|