@supalive/core 1.20.2 → 1.21.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.
Files changed (68) hide show
  1. package/dist/ephemeral-BVeIVuYw.js +276 -0
  2. package/dist/ephemeral-BVeIVuYw.js.map +1 -0
  3. package/dist/ephemeral-CkidSG9y.js +295 -0
  4. package/dist/ephemeral-CkidSG9y.js.map +1 -0
  5. package/dist/index-CzW0TkpK.d.ts +2729 -0
  6. package/dist/index-CzW0TkpK.d.ts.map +1 -0
  7. package/dist/index-DB7_pv9P.d.ts +2746 -0
  8. package/dist/index-DB7_pv9P.d.ts.map +1 -0
  9. package/dist/logger-BJNCJkFh.js +463 -0
  10. package/dist/logger-BJNCJkFh.js.map +1 -0
  11. package/dist/logger-By9Pw-X2.js +454 -0
  12. package/dist/logger-By9Pw-X2.js.map +1 -0
  13. package/dist/mysql-Bvc_iWUy.d.ts +114 -0
  14. package/dist/mysql-Bvc_iWUy.d.ts.map +1 -0
  15. package/dist/mysql-CcIuIAt0.js +622 -0
  16. package/dist/mysql-CcIuIAt0.js.map +1 -0
  17. package/dist/mysql-DTK2f5oU.d.ts +114 -0
  18. package/dist/mysql-DTK2f5oU.d.ts.map +1 -0
  19. package/dist/one-shot-query-BNYLU19W.js +573 -0
  20. package/dist/one-shot-query-BNYLU19W.js.map +1 -0
  21. package/dist/one-shot-query-BSK7adwG.js +573 -0
  22. package/dist/one-shot-query-BSK7adwG.js.map +1 -0
  23. package/dist/one-shot-query-DAAHGTcI.js +573 -0
  24. package/dist/one-shot-query-DAAHGTcI.js.map +1 -0
  25. package/dist/overlap-checker-DseE1Lvh.js +300 -0
  26. package/dist/overlap-checker-DseE1Lvh.js.map +1 -0
  27. package/dist/postgres-Bp7SMb9D.d.ts +118 -0
  28. package/dist/postgres-Bp7SMb9D.d.ts.map +1 -0
  29. package/dist/postgres-C5CbPBcp.d.ts +118 -0
  30. package/dist/postgres-C5CbPBcp.d.ts.map +1 -0
  31. package/dist/postgres-CLhoKn_R.js +867 -0
  32. package/dist/postgres-CLhoKn_R.js.map +1 -0
  33. package/dist/realtime_db-CTGOrFuV.js +463 -0
  34. package/dist/realtime_db-CTGOrFuV.js.map +1 -0
  35. package/dist/src/client/index.d.ts +2 -2
  36. package/dist/src/client/index.js +6 -5
  37. package/dist/src/client/index.js.map +1 -1
  38. package/dist/src/exports/mysql.d.ts +1 -1
  39. package/dist/src/exports/mysql.js +1 -1
  40. package/dist/src/exports/postgres.d.ts +1 -1
  41. package/dist/src/exports/postgres.js +1 -1
  42. package/dist/src/exports/procedure.d.ts +2 -2
  43. package/dist/src/exports/procedure.d.ts.map +1 -1
  44. package/dist/src/exports/procedure.js +2 -1
  45. package/dist/src/exports/procedure.js.map +1 -1
  46. package/dist/src/exports/schema-sql.d.ts +1 -1
  47. package/dist/src/exports/server.d.ts +56 -5
  48. package/dist/src/exports/server.d.ts.map +1 -1
  49. package/dist/src/exports/server.js +165 -7
  50. package/dist/src/exports/server.js.map +1 -1
  51. package/dist/src/exports/sub-manager-worker-entry.js +1 -1
  52. package/dist/src/exports/types.d.ts +2 -2
  53. package/dist/src/exports/types.js +2 -2
  54. package/dist/sub-worker-dispatch-C20KTMfg.js +925 -0
  55. package/dist/sub-worker-dispatch-C20KTMfg.js.map +1 -0
  56. package/dist/sub-worker-dispatch-CQCuwZPm.js +925 -0
  57. package/dist/sub-worker-dispatch-CQCuwZPm.js.map +1 -0
  58. package/dist/sub-worker-dispatch-Dwrp3c40.js +925 -0
  59. package/dist/sub-worker-dispatch-Dwrp3c40.js.map +1 -0
  60. package/dist/types_client_rpc-CqUVHTwT.js +163 -0
  61. package/dist/types_client_rpc-CqUVHTwT.js.map +1 -0
  62. package/dist/types_db-DPMeNETt.js +173 -0
  63. package/dist/types_db-DPMeNETt.js.map +1 -0
  64. package/dist/types_server-hiwqDm4G.d.ts +737 -0
  65. package/dist/types_server-hiwqDm4G.d.ts.map +1 -0
  66. package/dist/types_server-xHmZ6baD.d.ts +737 -0
  67. package/dist/types_server-xHmZ6baD.d.ts.map +1 -0
  68. package/package.json +1 -1
@@ -0,0 +1,2746 @@
1
+ import { i as ObjectStorage } from "./object-storage-DTVmZq3l.js";
2
+ import z, { ZodRawShape, ZodType, z as z$1 } from "zod";
3
+ import { Span } from "@opentelemetry/api";
4
+ import { RetryPolicy } from "cockatiel";
5
+ import pino from "pino";
6
+ import Redis from "ioredis";
7
+
8
+ //#region src/db/types_db.d.ts
9
+ /** Monotonically increasing integer from global_commit_ts sequence in Postgres */
10
+ type CommitTs = bigint;
11
+ declare const BigIntSchema: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
12
+ declare const CompareOperatorSchema: z.ZodEnum<{
13
+ "=": "=";
14
+ "!=": "!=";
15
+ ">": ">";
16
+ ">=": ">=";
17
+ "<": "<";
18
+ "<=": "<=";
19
+ like: "like";
20
+ ilike: "ilike";
21
+ contains: "contains";
22
+ icontains: "icontains";
23
+ between: "between";
24
+ in: "in";
25
+ jsonContains: "jsonContains";
26
+ jsonContainedBy: "jsonContainedBy";
27
+ jsonHasKey: "jsonHasKey";
28
+ jsonArrayContains: "jsonArrayContains";
29
+ isNull: "isNull";
30
+ isNotNull: "isNotNull";
31
+ }>;
32
+ type CompareOperator = z.infer<typeof CompareOperatorSchema>;
33
+ type Predicate = LeafPredicate | {
34
+ kind: "and";
35
+ children: Predicate[];
36
+ } | {
37
+ kind: "or";
38
+ children: Predicate[];
39
+ };
40
+ declare const PredicateSchema: z.ZodType<Predicate>;
41
+ declare const LeafPredicateSchema: z.ZodObject<{
42
+ kind: z.ZodLiteral<"leaf">;
43
+ column: z.ZodString;
44
+ operator: z.ZodEnum<{
45
+ "=": "=";
46
+ "!=": "!=";
47
+ ">": ">";
48
+ ">=": ">=";
49
+ "<": "<";
50
+ "<=": "<=";
51
+ like: "like";
52
+ ilike: "ilike";
53
+ contains: "contains";
54
+ icontains: "icontains";
55
+ between: "between";
56
+ in: "in";
57
+ jsonContains: "jsonContains";
58
+ jsonContainedBy: "jsonContainedBy";
59
+ jsonHasKey: "jsonHasKey";
60
+ jsonArrayContains: "jsonArrayContains";
61
+ isNull: "isNull";
62
+ isNotNull: "isNotNull";
63
+ }>;
64
+ value: z.ZodUnknown;
65
+ value2: z.ZodOptional<z.ZodUnknown>;
66
+ raw: z.ZodOptional<z.ZodBoolean>;
67
+ path: z.ZodOptional<z.ZodString>;
68
+ mode: z.ZodOptional<z.ZodEnum<{
69
+ one: "one";
70
+ all: "all";
71
+ }>>;
72
+ }, z.core.$strip>;
73
+ type LeafPredicate = z.infer<typeof LeafPredicateSchema>;
74
+ declare const AndPredicateSchema: z.ZodObject<{
75
+ kind: z.ZodLiteral<"and">;
76
+ children: z.ZodArray<z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>>;
77
+ }, z.core.$strip>;
78
+ type AndPredicate = z.infer<typeof AndPredicateSchema>;
79
+ declare const OrPredicateSchema: z.ZodObject<{
80
+ kind: z.ZodLiteral<"or">;
81
+ children: z.ZodArray<z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>>;
82
+ }, z.core.$strip>;
83
+ type OrPredicate = z.infer<typeof OrPredicateSchema>;
84
+ /** Query specification - what was queried (used for cache key generation) */
85
+ type QuerySpec = ReadEntry;
86
+ declare function normalizeIdToBytes(id: string | number, fromHex?: boolean): Uint8Array;
87
+ declare function normalizeToBytes(value: Uint8Array | unknown): Uint8Array;
88
+ declare function bytesFromJson(value: Uint8Array | unknown): Uint8Array;
89
+ declare const PointReadSchema: z.ZodObject<{
90
+ kind: z.ZodLiteral<"point">;
91
+ table: z.ZodString;
92
+ id: z.ZodString;
93
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
94
+ }, z.core.$strip>;
95
+ type PointRead = z.infer<typeof PointReadSchema>;
96
+ declare const RawPointReadSchema: z.ZodObject<{
97
+ kind: z.ZodLiteral<"point">;
98
+ tableId: z.ZodPipe<z.ZodAny, z.ZodTransform<Uint8Array<ArrayBufferLike>, any>>;
99
+ id: z.ZodPipe<z.ZodAny, z.ZodTransform<Uint8Array<ArrayBufferLike>, any>>;
100
+ table: z.ZodString;
101
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
102
+ }, z.core.$strip>;
103
+ type RawPointRead = z.infer<typeof RawPointReadSchema>;
104
+ /**
105
+ * Query specification for a range read (get with filters).
106
+ * This is the query definition, not the result.
107
+ */
108
+ declare const RangeReadSchema: z.ZodObject<{
109
+ kind: z.ZodLiteral<"range">;
110
+ table: z.ZodString;
111
+ predicate: z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>;
112
+ columns: z.ZodArray<z.ZodString>;
113
+ orderBy: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
114
+ limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
115
+ offset: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
116
+ }, z.core.$strip>;
117
+ type RangeRead = z.infer<typeof RangeReadSchema>;
118
+ declare const RawRangeReadSchema: z.ZodObject<{
119
+ kind: z.ZodLiteral<"range">;
120
+ table: z.ZodString;
121
+ tableId: z.ZodPipe<z.ZodAny, z.ZodTransform<Uint8Array<ArrayBufferLike>, any>>;
122
+ predicate: z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>;
123
+ columns: z.ZodArray<z.ZodString>;
124
+ orderBy: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
125
+ limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
126
+ offset: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
127
+ }, z.core.$strip>;
128
+ type RawRangeRead = z.infer<typeof RawRangeReadSchema>;
129
+ declare const ReadEntrySchema: z.ZodUnion<readonly [z.ZodObject<{
130
+ kind: z.ZodLiteral<"point">;
131
+ table: z.ZodString;
132
+ id: z.ZodString;
133
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
134
+ }, z.core.$strip>, z.ZodObject<{
135
+ kind: z.ZodLiteral<"range">;
136
+ table: z.ZodString;
137
+ predicate: z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>;
138
+ columns: z.ZodArray<z.ZodString>;
139
+ orderBy: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
140
+ limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
141
+ offset: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
142
+ }, z.core.$strip>]>;
143
+ type ReadEntry = z.infer<typeof ReadEntrySchema>;
144
+ declare const RawReadEntrySchema: z.ZodUnion<readonly [z.ZodObject<{
145
+ kind: z.ZodLiteral<"point">;
146
+ tableId: z.ZodPipe<z.ZodAny, z.ZodTransform<Uint8Array<ArrayBufferLike>, any>>;
147
+ id: z.ZodPipe<z.ZodAny, z.ZodTransform<Uint8Array<ArrayBufferLike>, any>>;
148
+ table: z.ZodString;
149
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
150
+ }, z.core.$strip>, z.ZodObject<{
151
+ kind: z.ZodLiteral<"range">;
152
+ table: z.ZodString;
153
+ tableId: z.ZodPipe<z.ZodAny, z.ZodTransform<Uint8Array<ArrayBufferLike>, any>>;
154
+ predicate: z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>;
155
+ columns: z.ZodArray<z.ZodString>;
156
+ orderBy: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
157
+ limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
158
+ offset: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
159
+ }, z.core.$strip>]>;
160
+ type RawReadEntry = z.infer<typeof RawReadEntrySchema>;
161
+ declare const WriteOpSchema: z.ZodEnum<{
162
+ insert: "insert";
163
+ update: "update";
164
+ delete: "delete";
165
+ }>;
166
+ type WriteOp = z.infer<typeof WriteOpSchema>;
167
+ declare const WriteEntrySchema: z.ZodObject<{
168
+ table: z.ZodString;
169
+ tableId: z.ZodPipe<z.ZodAny, z.ZodTransform<Uint8Array<ArrayBufferLike>, any>>;
170
+ id: z.ZodString;
171
+ op: z.ZodEnum<{
172
+ insert: "insert";
173
+ update: "update";
174
+ delete: "delete";
175
+ }>;
176
+ data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
177
+ prevData: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
178
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
179
+ }, z.core.$strip>;
180
+ type WriteEntry = z.infer<typeof WriteEntrySchema>;
181
+ interface CommitLogEntry {
182
+ id: Uint8Array;
183
+ ts: bigint;
184
+ tableId: Uint8Array;
185
+ data: Uint8Array;
186
+ _parsedData?: Record<string, unknown> | null;
187
+ }
188
+ interface MutationResult<T> {
189
+ result: T;
190
+ writeSet: WriteEntry[];
191
+ commitTs: CommitTs;
192
+ }
193
+ interface LiveResult<T> {
194
+ ts: bigint;
195
+ data: T;
196
+ readSet: ReadEntry[];
197
+ /**
198
+ * Ephemeral keys (see db/ephemeral.ts) this computation read. A parallel
199
+ * dependency set to `readSet`: these carry no snapshot ts and no commit log,
200
+ * so they are invalidated by exact key match rather than predicate overlap,
201
+ * and are deliberately kept OUT of `readSet` so the OCC validator and the
202
+ * commit-log freshness scan never see them.
203
+ */
204
+ ephemeralKeys: string[];
205
+ }
206
+ declare const QueryCacheMetadataSchema: z.ZodObject<{
207
+ lastSnapshotTs: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
208
+ version: z.ZodString;
209
+ readSet: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
210
+ kind: z.ZodLiteral<"point">;
211
+ table: z.ZodString;
212
+ id: z.ZodString;
213
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
214
+ }, z.core.$strip>, z.ZodObject<{
215
+ kind: z.ZodLiteral<"range">;
216
+ table: z.ZodString;
217
+ predicate: z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>;
218
+ columns: z.ZodArray<z.ZodString>;
219
+ orderBy: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
220
+ limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
221
+ offset: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
222
+ }, z.core.$strip>]>>;
223
+ ephemeralKeys: z.ZodDefault<z.ZodArray<z.ZodString>>;
224
+ }, z.core.$strip>;
225
+ type QueryCacheMetadata = z.infer<typeof QueryCacheMetadataSchema>;
226
+ declare const QueryCacheEntrySchema: z.ZodObject<{
227
+ lastSnapshotTs: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
228
+ data: z.ZodUnknown;
229
+ version: z.ZodString;
230
+ }, z.core.$strip>;
231
+ type QueryCacheEntry = z.infer<typeof QueryCacheEntrySchema>;
232
+ declare const CachedPgMetadataSchema: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>;
233
+ type CachedPgMetadata = z.infer<typeof CachedPgMetadataSchema>;
234
+ interface RetryConfig {
235
+ maxAttempts: number;
236
+ backoff: "none" | "linear" | "exponential";
237
+ delayMs?: number;
238
+ onConflict?: (attempt: number, error: OccConflictError) => void;
239
+ }
240
+ declare const NO_RETRY: RetryConfig;
241
+ declare const DEFAULT_RETRY: RetryConfig;
242
+ declare class OccConflictError extends Error {
243
+ readonly reason: string;
244
+ readonly conflictDetail?: unknown | undefined;
245
+ constructor(reason: string, conflictDetail?: unknown | undefined);
246
+ static isOccConflictError(err: unknown): boolean;
247
+ }
248
+ declare class OccAbortError extends Error {
249
+ constructor(message: string);
250
+ }
251
+ //#endregion
252
+ //#region src/server/sub-manager-types.d.ts
253
+ declare const RegisterSubscriptionParamsSchema: z.ZodObject<{
254
+ subId: z.ZodString;
255
+ cacheKey: z.ZodString;
256
+ queryName: z.ZodString;
257
+ args: z.ZodUnknown;
258
+ queryIdentity: z.ZodString;
259
+ instanceName: z.ZodString;
260
+ lastSnapshotTs: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
261
+ }, z.core.$strip>;
262
+ type RegisterSubscriptionParams = z.infer<typeof RegisterSubscriptionParamsSchema>;
263
+ declare const RegisterSubscriptionResultSchema: z.ZodObject<{
264
+ cacheKey: z.ZodString;
265
+ recompute: z.ZodBoolean;
266
+ readSet: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
267
+ kind: z.ZodLiteral<"point">;
268
+ table: z.ZodString;
269
+ id: z.ZodString;
270
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
271
+ }, z.core.$strip>, z.ZodObject<{
272
+ kind: z.ZodLiteral<"range">;
273
+ table: z.ZodString;
274
+ predicate: z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>;
275
+ columns: z.ZodArray<z.ZodString>;
276
+ orderBy: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
277
+ limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
278
+ offset: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
279
+ }, z.core.$strip>]>>;
280
+ ephemeralKeys: z.ZodDefault<z.ZodArray<z.ZodString>>;
281
+ version: z.ZodOptional<z.ZodString>;
282
+ }, z.core.$strip>;
283
+ type RegisterSubscriptionResult = z.infer<typeof RegisterSubscriptionResultSchema>;
284
+ declare const UpdateSubscriptionReadSetParamsSchema: z.ZodObject<{
285
+ cacheKey: z.ZodString;
286
+ subId: z.ZodString;
287
+ instanceName: z.ZodString;
288
+ lastSnapshotTs: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
289
+ readSet: z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
290
+ kind: z.ZodLiteral<"point">;
291
+ table: z.ZodString;
292
+ id: z.ZodString;
293
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
294
+ }, z.core.$strip>, z.ZodObject<{
295
+ kind: z.ZodLiteral<"range">;
296
+ table: z.ZodString;
297
+ predicate: z.ZodType<Predicate, unknown, z.core.$ZodTypeInternals<Predicate, unknown>>;
298
+ columns: z.ZodArray<z.ZodString>;
299
+ orderBy: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
300
+ limit: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
301
+ offset: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
302
+ }, z.core.$strip>]>>;
303
+ ephemeralKeys: z.ZodDefault<z.ZodArray<z.ZodString>>;
304
+ }, z.core.$strip>;
305
+ type UpdateSubscriptionReadSetParams = z.infer<typeof UpdateSubscriptionReadSetParamsSchema>;
306
+ /**
307
+ * `tracked: false` means the sub-manager has no record of this subId (e.g.
308
+ * a restart wiped state and the caller hasn't re-registered yet). The app
309
+ * server should treat this as a signal to register the sub before retrying.
310
+ */
311
+ declare const UpdateSubscriptionReadSetResultSchema: z.ZodObject<{
312
+ tracked: z.ZodBoolean;
313
+ }, z.core.$strip>;
314
+ type UpdateSubscriptionReadSetResult = z.infer<typeof UpdateSubscriptionReadSetResultSchema>;
315
+ declare const UnregisterSubscriptionParamsSchema: z.ZodObject<{
316
+ subId: z.ZodString;
317
+ instanceName: z.ZodString;
318
+ }, z.core.$strip>;
319
+ type UnregisterSubscriptionParams = z.infer<typeof UnregisterSubscriptionParamsSchema>;
320
+ declare const UnregisterSubscriptionsParamsSchema: z.ZodArray<z.ZodObject<{
321
+ subId: z.ZodString;
322
+ instanceName: z.ZodString;
323
+ }, z.core.$strip>>;
324
+ type UnregisterSubscriptionsParams = z.infer<typeof UnregisterSubscriptionsParamsSchema>;
325
+ declare const UnregisterSubscriptionResultSchema: z.ZodObject<{
326
+ ok: z.ZodBoolean;
327
+ }, z.core.$strip>;
328
+ type UnregisterSubscriptionResult = z.infer<typeof UnregisterSubscriptionResultSchema>;
329
+ declare const UnregisterSubscriptionsResultSchema: z.ZodArray<z.ZodObject<{
330
+ ok: z.ZodBoolean;
331
+ }, z.core.$strip>>;
332
+ type UnregisterSubscriptionsResult = z.infer<typeof UnregisterSubscriptionsResultSchema>;
333
+ declare const InvalidateWritesetParamsSchema: z.ZodObject<{
334
+ writeSet: z.ZodArray<z.ZodObject<{
335
+ table: z.ZodString;
336
+ tableId: z.ZodPipe<z.ZodAny, z.ZodTransform<Uint8Array<ArrayBufferLike>, any>>;
337
+ id: z.ZodString;
338
+ op: z.ZodEnum<{
339
+ insert: "insert";
340
+ update: "update";
341
+ delete: "delete";
342
+ }>;
343
+ data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
344
+ prevData: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
345
+ expectedCommitTs: z.ZodOptional<z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>>>;
346
+ }, z.core.$strip>>;
347
+ }, z.core.$strip>;
348
+ type InvalidateWritesetParams = z.infer<typeof InvalidateWritesetParamsSchema>;
349
+ /**
350
+ * Ephemeral counterpart of {@link InvalidateWritesetParamsSchema}. Carries just
351
+ * the written keys — matching is an exact key lookup in the worker's index, with
352
+ * no predicate to evaluate and no row images to compare, so there is nothing
353
+ * else to send. The recompute re-reads the current value from Redis, so the
354
+ * VALUE never travels on this path either.
355
+ */
356
+ declare const InvalidateEphemeralParamsSchema: z.ZodObject<{
357
+ keys: z.ZodArray<z.ZodString>;
358
+ version: z.ZodString;
359
+ }, z.core.$strip>;
360
+ type InvalidateEphemeralParams = z.infer<typeof InvalidateEphemeralParamsSchema>;
361
+ declare const AffectedSubscriptionSchema: z.ZodObject<{
362
+ subId: z.ZodString;
363
+ queryName: z.ZodString;
364
+ args: z.ZodUnknown;
365
+ cacheKey: z.ZodString;
366
+ notifyInstances: z.ZodArray<z.ZodString>;
367
+ }, z.core.$strip>;
368
+ type AffectedSubscription = z.infer<typeof AffectedSubscriptionSchema>;
369
+ declare const InvalidateWritesetResultSchema: z.ZodObject<{
370
+ affected: z.ZodArray<z.ZodObject<{
371
+ subId: z.ZodString;
372
+ queryName: z.ZodString;
373
+ args: z.ZodUnknown;
374
+ cacheKey: z.ZodString;
375
+ notifyInstances: z.ZodArray<z.ZodString>;
376
+ }, z.core.$strip>>;
377
+ }, z.core.$strip>;
378
+ type InvalidateWritesetResult = z.infer<typeof InvalidateWritesetResultSchema>;
379
+ declare const InvalidateEphemeralResultSchema: z.ZodObject<{
380
+ affected: z.ZodArray<z.ZodObject<{
381
+ subId: z.ZodString;
382
+ queryName: z.ZodString;
383
+ args: z.ZodUnknown;
384
+ cacheKey: z.ZodString;
385
+ notifyInstances: z.ZodArray<z.ZodString>;
386
+ }, z.core.$strip>>;
387
+ }, z.core.$strip>;
388
+ type InvalidateEphemeralResult = z.infer<typeof InvalidateEphemeralResultSchema>;
389
+ declare const RegistrationRecordSchema: z.ZodObject<{
390
+ queryName: z.ZodString;
391
+ args: z.ZodUnknown;
392
+ queryIdentity: z.ZodString;
393
+ cacheKey: z.ZodString;
394
+ }, z.core.$strip>;
395
+ type RegistrationRecord = z.infer<typeof RegistrationRecordSchema>;
396
+ //#endregion
397
+ //#region src/db/cache.d.ts
398
+ declare class CacheLayer {
399
+ redis: Redis;
400
+ private readonly ttlSeconds;
401
+ /** Cached EVALSHA digests keyed by Lua source (see evalScript). */
402
+ private readonly scriptShaCache;
403
+ constructor(redis: Redis, ttlSeconds?: number);
404
+ /**
405
+ * Run a Lua script via EVALSHA using the script's SHA1 (computed locally — the
406
+ * same digest Redis keys scripts by, so no SCRIPT LOAD round-trip is needed on
407
+ * the hot path). On NOSCRIPT (the script isn't in Redis's cache yet, e.g. first
408
+ * use or after a Redis restart/failover) load it explicitly via SCRIPT LOAD and
409
+ * re-run via EVALSHA — so the script ends up cached server-side and subsequent
410
+ * EVALSHA calls hit. The runtime client is always ioredis (see initCacheLayer).
411
+ */
412
+ evalScript(src: string, keys: string[], args: (string | number)[]): Promise<unknown>;
413
+ /** One-shot recompute: atomically write data + metadata (CAS on version). */
414
+ setQueryCacheAndMetadataFor(cacheKey: string, data: unknown, metaData: QueryCacheMetadata, ttlSeconds?: number): Promise<void>;
415
+ /** Subscribe path: write the data blob only (CAS against the metadata's
416
+ * version). The metadata is written separately by the sub-worker; the
417
+ * `version` links the two so a divergent pair is detected on read. Takes the
418
+ * snapshot ts directly (rather than a pre-stringified version) so the entry
419
+ * can carry `lastSnapshotTs` as its own field; `version` is derived from it,
420
+ * preserving the `version === String(ts)` convention the CAS relies on. */
421
+ setQueryCacheFor(cacheKey: string, data: unknown, lastSnapshotTs: bigint, ttlSeconds?: number): Promise<void>;
422
+ getQueryCacheFor(cacheKey: string): Promise<QueryCacheEntry | null>;
423
+ /** Subscription recompute: replace the whole metadata (CAS on version). */
424
+ replaceQueryCacheMetadata(cacheKey: string, metaData: QueryCacheMetadata, ttlSeconds?: number): Promise<void>;
425
+ /** Still-fresh subscription: advance `lastSnapshotTs` only, preserving the
426
+ * data's `version` + `readSet` (no recompute). Monotonic and non-clobbering
427
+ * — see ADVANCE_META. `metaData` carries the new horizon plus the version +
428
+ * readSet the caller read, which the script keeps unless a newer computation
429
+ * has since landed. */
430
+ advanceQueryCacheMetadata(cacheKey: string, metaData: QueryCacheMetadata, ttlSeconds?: number): Promise<void>;
431
+ getQueryCacheMetaData(cacheKey: string): Promise<QueryCacheMetadata | null>;
432
+ persistRegistrationRecord(subId: string, record: RegistrationRecord): Promise<void>;
433
+ deleteRegistrationRecord(subId: string): Promise<void>;
434
+ deleteRegistrationsRecord(subIds: string[]): Promise<void>;
435
+ fetchRegistrationRecordsBatch(subIds: string[]): Promise<Map<string, RegistrationRecord | null>>;
436
+ getCachedTableColumnTypes(key: string): Promise<CachedPgMetadata | null>;
437
+ setCachedTableColumnsTypes(key: string, value: CachedPgMetadata): Promise<void>;
438
+ /**
439
+ * Race participant: try to claim `lock:recompute:<subId>:<commitTs>`
440
+ * with `SET NX EX`. The single Redis round-trip both arbitrates and
441
+ * sets a TTL fallback in case the winner crashes mid-recompute.
442
+ */
443
+ tryAcquireRecomputeLock(instanceId: string, subId: string, commitTs: string, ttl: number): Promise<boolean>;
444
+ /** Raw value at an ephemeral key, or `null` when absent/expired. */
445
+ ephemeralGet(key: string): Promise<string | null>;
446
+ /** Raw values for `keys`, positionally aligned; `null` for each absent key. */
447
+ ephemeralGetMany(keys: string[]): Promise<(string | null)[]>;
448
+ private getRegistrationKey;
449
+ private getQueryCacheDataKey;
450
+ private getQeruyCacheMetaKey;
451
+ }
452
+ type IORedisClient = Redis;
453
+ //#endregion
454
+ //#region src/db/db.d.ts
455
+ interface DbQueryResult<T = any> {
456
+ rows: T[];
457
+ rowCount?: number | null;
458
+ /**
459
+ * Auto-increment / LAST_INSERT_ID value from the driver, when applicable
460
+ * (MySQL `ResultSetHeader.insertId`). Used to read the advanced value back
461
+ * from `UPDATE ... SET id = LAST_INSERT_ID(id + 1)` without a second
462
+ * round-trip. Undefined for Postgres / SELECTs.
463
+ */
464
+ insertId?: number | bigint;
465
+ }
466
+ interface RawClient {
467
+ query<T = any>(sql: string, params?: unknown[]): Promise<DbQueryResult<T>>;
468
+ }
469
+ interface PooledClient extends RawClient {
470
+ release(): void;
471
+ }
472
+ type DbType = "postgres" | "mysql";
473
+ interface PendingPointEntry {
474
+ entry: PointRead;
475
+ key: string;
476
+ }
477
+ interface PreparedQuery {
478
+ sql: string;
479
+ params: unknown[] | unknown[][];
480
+ operation: "insertSingle" | "insertBatch" | "updateSingle" | "updateBatch" | "deleteSingle" | "deleteBatch" | "pointReadValidation" | "commit_logs";
481
+ table: string;
482
+ entryId?: unknown;
483
+ expectedTs?: bigint;
484
+ entries?: WriteEntry[];
485
+ pendingsEntry?: PendingPointEntry[];
486
+ }
487
+ interface PreparedQueries {
488
+ commits: PreparedQuery[];
489
+ validateReadPoints?: PreparedQuery[];
490
+ hasRangeRead: boolean;
491
+ tableIds?: Uint8Array[];
492
+ rawReadSet?: RawReadEntry[];
493
+ }
494
+ /**
495
+ * Bound to a single dedicated connection that holds the core-init lock. Passed
496
+ * to the callback of {@link Database.withCoreInitLock} so version reads/writes
497
+ * and migration statements all run under the same lock.
498
+ */
499
+ interface CoreInitLockCtx {
500
+ exec(sql: string, params?: unknown[]): Promise<DbQueryResult>;
501
+ getVersion(): Promise<bigint>;
502
+ setVersion(version: bigint): Promise<void>;
503
+ }
504
+ interface Database {
505
+ readonly dbType: DbType;
506
+ readonly sqlBuilder: SqlBuilder;
507
+ columnTypes: Map<string, Map<string, string>>;
508
+ /**
509
+ * Current core schema version stored in `metadata.core_version`, or `0n`
510
+ * when unset. Read on a pooled connection for the lock-free fast path of
511
+ * {@link initCore}.
512
+ */
513
+ getCoreVersion(): Promise<bigint>;
514
+ /**
515
+ * The app's current schema-migration marker, stored in
516
+ * `metadata.app_migration`, or `0n` when unset. This is the 14-digit
517
+ * timestamp of the last applied migration file (e.g. `20260726112333` for
518
+ * `0001_20260726112333.sql`), written by the deploy pipeline's tracked
519
+ * migrate step. Used to key the bootstrap column-type cache
520
+ * (`supalive:schema:types:<app_migration>`) so a schema change automatically
521
+ * invalidates cold instances' cached types — see `bootstrapDbTypes`.
522
+ */
523
+ getAppMigration(): Promise<bigint>;
524
+ /**
525
+ * Runtime bootstrap statements to apply when migrating core TO `toVersion`
526
+ * (i.e. the `toVersion - 1` → `toVersion` step): data seeds, backfills, or a
527
+ * schema change that must be applied directly at runtime. Dialect-specific;
528
+ * the core schema (tables, indexes, the `global_commit_ts` sequence/counter)
529
+ * lives in the generated schema instead — see `coreTablesSql`.
530
+ */
531
+ coreMigrationStatements(toVersion: bigint): string[];
532
+ /**
533
+ * Run `fn` while holding the cross-process core-init lock (PG
534
+ * `pg_advisory_xact_lock`, MySQL `GET_LOCK`) on a single dedicated
535
+ * connection, so concurrent servers can't race the core migration. All of
536
+ * `fn`'s work runs on that locked connection via {@link CoreInitLockCtx}.
537
+ */
538
+ withCoreInitLock(fn: (ctx: CoreInitLockCtx) => Promise<void>): Promise<void>;
539
+ bootstrapColumnTypes(opts: {
540
+ redis?: CacheLayer;
541
+ cacheKey: string;
542
+ tables?: string[];
543
+ }): Promise<void>;
544
+ query<T = any>(sql: string, params?: unknown[]): Promise<DbQueryResult<T>>;
545
+ getClient(): Promise<PooledClient>;
546
+ getTransactionClient(): Promise<TxDatabase>;
547
+ getLatestSnapshotTimestamp(): Promise<bigint>;
548
+ updateLatestSnapshotTimestamp(commitTs: bigint): Promise<void>;
549
+ /**
550
+ * The retention watermark: the boundary `ts` below which commit_logs have
551
+ * been pruned, so any cache entry whose `lastSnapshotTs` is below it can no
552
+ * longer be freshness-checked (its scan window would miss deleted rows) and
553
+ * must be recomputed. Persisted in the `metadata` table (survives restarts)
554
+ * as a cheap PK read — callers load it into an in-memory variable at startup
555
+ * and keep it fresh via pub/sub rather than reading it per query. Returns the
556
+ * -1n genesis when nothing has been pruned yet (matching the empty-DB snapshot
557
+ * ts), so every entry is scannable.
558
+ */
559
+ getMinSnapshotTimestamp(): Promise<bigint>;
560
+ /**
561
+ * Advance the retention watermark. Monotonic: only ever moves forward (a
562
+ * stale writer can never lower it). Called by the sub-manager right before it
563
+ * prunes commit_logs below `ts`.
564
+ */
565
+ updateMinSnapshotTimestamp(ts: bigint): Promise<void>;
566
+ getNextTimestamp(): Promise<bigint>;
567
+ prepareCommitWrites(readSet: ReadEntry[], writeSet: WriteEntry[], commitTs: LazyCommitTsParam): Promise<PreparedQueries>;
568
+ commitWrites(beginTs: bigint, queries: PreparedQueries, commitTs: bigint): Promise<{
569
+ success: boolean;
570
+ }>;
571
+ close(): Promise<void>;
572
+ /**
573
+ * Delete commit_logs older than `ts` (exclusive). Used by the periodic
574
+ * retention sweep in SubscriptionManager so the table doesn't grow
575
+ * unbounded.
576
+ */
577
+ pruneCommitLogsBefore(ts: bigint): Promise<bigint>;
578
+ /**
579
+ * Oldest `ts` still present in commit_logs (i.e. the retention watermark
580
+ * after the last prune), or `null` when the table is empty. A cache entry
581
+ * whose `lastSnapshotTs` is below it can no longer be freshness-checked via
582
+ * {@link getCommitLogsBetweenTs} (the logs it would need were pruned), so the
583
+ * one-shot/register cache paths recompute instead of trusting it.
584
+ */
585
+ getMinCommitLogTs(): Promise<bigint | null>;
586
+ /**
587
+ * Half-open range scan: ts >= beginTs AND ts < endTs. Used by the
588
+ * subscription-recovery path to replay logs in a specific time window.
589
+ */
590
+ getCommitLogsBetweenTs(beginTs: bigint, endTs: bigint, tableIds: Uint8Array[]): Promise<CommitLogEntry[]>;
591
+ /**
592
+ * Existence-only freshness probe for point reads: does any commit in the
593
+ * half-open window `[beginTs, endTs)` touch one of the exact `(tableId, id)`
594
+ * pairs in `points`? Returns as soon as one match is found.
595
+ *
596
+ * Point-read overlap is a pure key match (`checkPointReadOverlap`), so it can
597
+ * be answered entirely in SQL with a `(table_id, id)` tuple `IN` against the
598
+ * `commit_logs_by_table_and_id (table_id, id, ts)` index — no row `data`
599
+ * blobs fetched, no JSON parsed. This is the precise-filtering path used by
600
+ * {@link CacheFreshness} for tables whose read set is point-reads-only; tables
601
+ * that also carry a range read still go through {@link getCommitLogsBetweenTs}
602
+ * because their predicates must be evaluated in-app. `points` empty → `false`.
603
+ */
604
+ hasCommitForPoints(beginTs: bigint, endTs: bigint, points: {
605
+ tableId: Uint8Array;
606
+ id: Uint8Array;
607
+ }[]): Promise<boolean>;
608
+ /**
609
+ * Conflict-check scan for OCC: ts >= sinceTs AND ts <> excludeTs. The
610
+ * exclude is our own commitTs so we don't conflict with ourselves; the
611
+ * lack of an upper bound is intentional, so a concurrent peer that
612
+ * happens to have a higher commitTs but committed before us is still
613
+ * visible. (MVCC keeps in-flight peers invisible until they commit.)
614
+ */
615
+ getCommitLogsSinceTs(sinceTs: bigint, excludeTs: bigint, tableIds: Uint8Array[]): Promise<CommitLogEntry[]>;
616
+ /**
617
+ * For each `update`/`delete` entry, attach `prevData` (consumed by the
618
+ * invalidator) and `expectedCommitTs` (the CAS fence).
619
+ *
620
+ * `expectedCommitTs` is preferentially copied from a prior point-read of
621
+ * the same row in `readSet` — that's the value the handler's logic
622
+ * actually depended on, so the CAS check enforces beginTs-level isolation
623
+ * for read-then-update patterns. If no prior read exists ("blind"
624
+ * write), we fall back to the value observed in this batch fetch (weak
625
+ * OCC, sufficient for writes that don't depend on prior state).
626
+ *
627
+ * `prevData` always comes from this batch fetch (SELECT * per table) so
628
+ * the invalidator has the full row regardless of which columns the
629
+ * handler's read selected.
630
+ *
631
+ * If the target row no longer exists at fetch time, `expectedCommitTs`
632
+ * is left undefined and the per-row CAS will match nothing — surfaced
633
+ * as OccConflictError.
634
+ */
635
+ attachPrevDataToWriteSet(writeSet: WriteEntry[], readSet: ReadEntry[]): Promise<void>;
636
+ }
637
+ interface TxDatabase extends Database {
638
+ begin(): Promise<void>;
639
+ commit(): Promise<void>;
640
+ rollback(): Promise<void>;
641
+ release(): void;
642
+ }
643
+ interface JsonOpOptions$1 {
644
+ /**
645
+ * When true, the bound `value` parameter is treated as already prepared
646
+ * for the underlying SQL (e.g. a literal JSON path string for
647
+ * jsonHasKey, a JSON-encoded literal for the contains family). When
648
+ * false/omitted, the value was prepared client-side from a JS value.
649
+ */
650
+ raw?: boolean;
651
+ /**
652
+ * 1-based positional index of an extra bound parameter holding a
653
+ * jsonpath that scopes the contains-family check to a sub-document of
654
+ * the target column. When omitted, the check runs against the whole
655
+ * column value.
656
+ */
657
+ pathIndex?: number;
658
+ /**
659
+ * jsonHasKey only: when set, multiple path parameters are bound starting
660
+ * at `paramIndex`, and the predicate evaluates whether any (`one`) or
661
+ * every (`all`) path exists in the column.
662
+ */
663
+ mode?: "one" | "all";
664
+ /** Number of bound path parameters in multi-path mode. */
665
+ pathCount?: number;
666
+ }
667
+ interface SqlBuilder {
668
+ placeholder(index: number): string;
669
+ like(col: string, paramIndex: number): string;
670
+ ilike(col: string, paramIndex: number): string;
671
+ icontains(col: string, paramIndex: number): string;
672
+ /** PG @> ; MySQL JSON_CONTAINS */
673
+ jsonContains(col: string, paramIndex: number, opts?: JsonOpOptions$1): string;
674
+ /** PG <@ ; MySQL JSON_CONTAINS(candidate, target) */
675
+ jsonContainedBy(col: string, paramIndex: number, opts?: JsonOpOptions$1): string;
676
+ /**
677
+ * Key/path existence. With `opts.raw === true`, the bound param is a JSON
678
+ * path expression (PG `jsonb_path_exists`, MySQL `JSON_CONTAINS_PATH`).
679
+ * Otherwise the bound param is a literal top-level key (PG `?`, MySQL
680
+ * `JSON_CONTAINS_PATH($."<key>")` with server-side quoting).
681
+ * With `opts.pathCount` set, emits the multi-path form keyed by `mode`.
682
+ */
683
+ jsonHasKey(col: string, paramIndex: number, opts?: JsonOpOptions$1): string;
684
+ /** Array membership: target array contains the given element. */
685
+ jsonArrayContains(col: string, paramIndex: number, opts?: JsonOpOptions$1): string;
686
+ }
687
+ declare class LazyCommitTsParam {
688
+ value: unknown;
689
+ constructor(value: unknown);
690
+ toPostgres(prepareValue: (value: any) => any): any;
691
+ toString(): any;
692
+ validate(): void;
693
+ }
694
+ //#endregion
695
+ //#region src/db/table-id.d.ts
696
+ /**
697
+ * Canonical table identifier: a fixed **16-byte** digest of the table name
698
+ * (UUID v3, which is MD5-based). A `tableId` is fully derived from the table
699
+ * name, so it is never stored redundantly — the query-cache `readSet` persists
700
+ * only `table` (the name) and reconstructs the bytes on read via
701
+ * {@link tableIdOf}. The physical `commit_logs.table_id` column (MySQL
702
+ * `BINARY(16)`, Postgres `BYTEA`) and its indexes hold these same bytes.
703
+ *
704
+ * A fixed 16 bytes (rather than the raw UTF-8 of the name) is required for
705
+ * MySQL: `BINARY(16)` would overflow on a table name longer than 16 bytes and
706
+ * zero-pad shorter ones (breaking byte-exact id matching). MD5 sidesteps both.
707
+ * Only used for equality (never decoded back to a name), so a non-cryptographic
708
+ * digest is fine. Browser-safe and synchronous (via the `uuid` package), which
709
+ * matters because this module is reachable from the client bundle.
710
+ */
711
+ declare function tableNameToId(tableName: string): Uint8Array;
712
+ //#endregion
713
+ //#region src/db/schema.d.ts
714
+ interface ColumnCodec<TData = unknown, TDriver = unknown> {
715
+ encode?: (value: TData) => TDriver;
716
+ decode?: (value: TDriver) => TData;
717
+ }
718
+ type SchemaCodecField<TSchema extends ZodRawShape, K extends PropertyKey> = K extends keyof TSchema ? TSchema[K] extends z$1.ZodTypeAny ? z$1.infer<TSchema[K]> : unknown : K extends keyof BaseSchema ? BaseSchema[K] extends z$1.ZodTypeAny ? z$1.infer<BaseSchema[K]> : unknown : unknown;
719
+ type SchemaCodecs<TSchema extends ZodRawShape> = Partial<{ [K in keyof (TSchema & BaseSchema)]: ColumnCodec<SchemaCodecField<TSchema, K>, any> }>;
720
+ interface SchemaColumnsOptions<TSchema extends ZodRawShape = ZodRawShape> {
721
+ codecs?: SchemaCodecs<TSchema>;
722
+ }
723
+ interface SchemaColumnMapping {
724
+ [key: string]: string;
725
+ }
726
+ interface DeclarativeIndex {
727
+ name: string;
728
+ columns: string[];
729
+ unique?: boolean;
730
+ type?: "btree" | "hash" | "gin" | "gist" | "spgist" | "brin";
731
+ where?: string;
732
+ using?: string;
733
+ options?: string;
734
+ tablespace?: string;
735
+ }
736
+ type IndexDefinition = string | DeclarativeIndex;
737
+ interface DefineSchemaConfig<TSchema extends ZodRawShape> {
738
+ name: string;
739
+ schema: TSchema;
740
+ options?: SchemaColumnsOptions<TSchema>;
741
+ columns?: SchemaColumnMapping;
742
+ indexes?: IndexDefinition[];
743
+ }
744
+ type CodecDataType<TCodec, TFallback> = [TCodec] extends [undefined] ? TFallback : TCodec extends {
745
+ decode: (...args: any[]) => infer TDecoded;
746
+ } ? TDecoded : TCodec extends ColumnCodec<infer TData, unknown> ? TData : TFallback;
747
+ type ZodOutput<TZod> = [TZod] extends [undefined] ? unknown : TZod extends z$1.ZodTypeAny ? z$1.infer<TZod> : unknown;
748
+ type ComputedDataType<TCodec, TZod> = CodecDataType<TCodec, ZodOutput<TZod>>;
749
+ type ComputedCodec<TData, TDriver> = ColumnCodec<TData, TDriver>;
750
+ type OverrideField<TObj, TKey extends PropertyKey, TValue> = Omit<TObj, TKey> & { [K in TKey]: TValue };
751
+ interface ComputedFieldConfig<TField extends string, TDecoded = unknown, TDriver = unknown, TZodType extends z$1.ZodTypeAny | undefined = undefined> {
752
+ field: TField;
753
+ sql: string;
754
+ alias?: string;
755
+ type?: TZodType;
756
+ codec?: {
757
+ decode: (value: TDriver) => TDecoded;
758
+ encode?: (value: TDecoded) => TDriver;
759
+ };
760
+ }
761
+ type ComputedField = {
762
+ field: string;
763
+ sql: string;
764
+ alias: string;
765
+ codec?: ColumnCodec<any, any>;
766
+ };
767
+ declare function defineComputedField<TField extends string, TDecoded = unknown, TDriver = unknown, TZodType extends z$1.ZodTypeAny | undefined = undefined>(config: ComputedFieldConfig<TField, TDecoded, TDriver, TZodType>): ComputedFieldConfig<TField, TDecoded, TDriver, TZodType>;
768
+ type Prettify<T> = { [K in keyof T]: T[K] } & {};
769
+ type ApplySchemaCodecs<TBase, TCodecs> = { [K in keyof TBase]: K extends keyof TCodecs ? CodecDataType<TCodecs[K], TBase[K]> : TBase[K] };
770
+ type ResolveSchemaType<TSchema extends ZodRawShape, TOptions extends SchemaColumnsOptions<TSchema>> = TOptions extends {
771
+ codecs: infer TCodecs;
772
+ } ? Prettify<ApplySchemaCodecs<z$1.infer<z$1.ZodObject<TSchema & BaseSchema>>, TCodecs>> : Prettify<z$1.infer<z$1.ZodObject<TSchema & BaseSchema>>>;
773
+ type IsOptionalField<T> = T extends z$1.ZodOptional<any> ? true : T extends z$1.ZodNullable<any> ? true : T extends z$1.ZodDefault<any> ? true : false;
774
+ type OptionalKeys<TZod extends ZodRawShape> = { [K in keyof TZod]: IsOptionalField<TZod[K]> extends true ? K : never }[keyof TZod];
775
+ type RequiredKeys<TZod extends ZodRawShape> = { [K in keyof TZod]: IsOptionalField<TZod[K]> extends true ? never : K }[keyof TZod];
776
+ type BaseSchema = {
777
+ id: z$1.ZodString;
778
+ commitTs: z$1.ZodCoercedBigInt;
779
+ };
780
+ declare const schemaRegistry: Map<string, SchemaDefinition<string, any, any>>;
781
+ declare const shouldTrackSchema: boolean;
782
+ declare function defineSchema<TSchema extends ZodRawShape, TName extends string, TOptions extends SchemaColumnsOptions<TSchema> = SchemaColumnsOptions<TSchema>>(config: DefineSchemaConfig<TSchema> & {
783
+ name: TName;
784
+ options?: TOptions;
785
+ }): SchemaDefinition<TName, Prettify<TSchema & BaseSchema>, ResolveSchemaType<TSchema, TOptions>>;
786
+ interface StoredSchemaColumnsOptions {
787
+ codecs?: Record<string, ColumnCodec<any, any>>;
788
+ }
789
+ interface SchemaDefinition<TTable extends string, TZod extends ZodRawShape, TData = z$1.infer<z$1.ZodObject<TZod>>> {
790
+ tableId: Uint8Array;
791
+ table: TTable;
792
+ schema: z$1.ZodObject<TZod>;
793
+ columnsOptions: StoredSchemaColumnsOptions;
794
+ columns: Record<string, string>;
795
+ indexes: IndexDefinition[];
796
+ _dataType?: TData;
797
+ }
798
+ type InferSchema<T extends SchemaDefinition<string, ZodRawShape, any>> = T extends SchemaDefinition<string, ZodRawShape, infer TData> ? TData : never;
799
+ type InsertData<T extends SchemaDefinition<string, ZodRawShape, any>> = T extends SchemaDefinition<string, infer TZod, infer TData> ? { [K in RequiredKeys<TZod> as K extends "id" | "commitTs" ? never : K]: Extract<K, string> extends keyof TData ? TData[Extract<K, string>] : never } & { [K in OptionalKeys<TZod> as K extends "id" | "commitTs" ? never : K]?: Extract<K, string> extends keyof TData ? TData[Extract<K, string>] : never } : never;
800
+ interface Model {
801
+ table: string;
802
+ tableId: Uint8Array;
803
+ columns: Record<string, string>;
804
+ reverseColumns: Record<string, string>;
805
+ types: Record<string, string>;
806
+ codecs: Record<string, ColumnCodec<any, any>>;
807
+ }
808
+ type MaybePromise<T> = T | Promise<T>;
809
+ type ReturnLiveQuery<TLiveQueryFn extends (...args: any[]) => MaybePromise<LiveQueryHandle<any>>> = Awaited<ReturnType<TLiveQueryFn>> extends LiveQueryHandle<infer T> ? T : never;
810
+ type ReturnQuery<TQueryFn extends (...args: any[]) => any> = Awaited<ReturnType<TQueryFn>>;
811
+ type QueryArgs<TQueryFn extends (...args: any[]) => any> = Parameters<TQueryFn>[0];
812
+ type MutationArgs<TMutationFn extends (...args: any[]) => any> = Parameters<TMutationFn>[0];
813
+ type ActionArgs<TActionFn extends (...args: any[]) => any> = Parameters<TActionFn>[0];
814
+ //#endregion
815
+ //#region src/db/query.d.ts
816
+ type FieldValue<S extends SchemaDefinition<string, ZodRawShape>, K> = K extends keyof InferSchema<S> ? InferSchema<S>[K] : never;
817
+ type OrderableValue = number | bigint | string | Date;
818
+ type StringFieldKeys<S extends SchemaDefinition<string, ZodRawShape>> = { [K in keyof InferSchema<S>]: NonNullable<InferSchema<S>[K]> extends string ? K : never }[keyof InferSchema<S>];
819
+ type OrderableFieldKeys<S extends SchemaDefinition<string, ZodRawShape>> = { [K in keyof InferSchema<S>]: NonNullable<InferSchema<S>[K]> extends OrderableValue ? K : never }[keyof InferSchema<S>];
820
+ type JsonFieldKeys<S extends SchemaDefinition<string, ZodRawShape>> = { [K in keyof InferSchema<S>]: NonNullable<InferSchema<S>[K]> extends OrderableValue ? never : NonNullable<InferSchema<S>[K]> extends boolean ? never : NonNullable<InferSchema<S>[K]> extends (object | unknown[]) ? K : unknown extends NonNullable<InferSchema<S>[K]> ? K : never }[keyof InferSchema<S>];
821
+ interface JsonOpOptions {
822
+ /**
823
+ * Skip client-side preparation of the value (no JSON-stringify, no key
824
+ * escaping). The caller is responsible for the exact form the dialect
825
+ * expects: a JSON path string for jsonHasKey, a JSON-encoded string for
826
+ * the contains family.
827
+ */
828
+ raw?: true;
829
+ }
830
+ interface JsonContainsOptions extends JsonOpOptions {
831
+ /**
832
+ * Sub-path into the target JSON to scope the check to. `string` is a
833
+ * single literal top-level key; `string[]` is a nested literal-key path
834
+ * compiled to `$."a"."b"`. Mutually exclusive with `pathRaw`.
835
+ */
836
+ path?: string | string[];
837
+ /**
838
+ * Raw jsonpath expression used verbatim by the dialect (PG `jsonpath`,
839
+ * MySQL JSON path). Mutually exclusive with `path`.
840
+ */
841
+ pathRaw?: string;
842
+ }
843
+ interface JsonHasKeyMultiOptions extends JsonOpOptions {
844
+ /** Required for the multi-path overload — chooses any vs every. */
845
+ mode: "one" | "all";
846
+ }
847
+ declare class QueryBuilder<S extends SchemaDefinition<string, ZodRawShape>> {
848
+ protected ctx: TxContext;
849
+ protected model: Model;
850
+ constructor(ctx: TxContext, model: Model);
851
+ protected getColumnName(key: string): string;
852
+ select(): SelectQueryBuilder<S, keyof InferSchema<S>>;
853
+ select<K extends keyof InferSchema<S>>(...columns: K[]): SelectQueryBuilder<S, K>;
854
+ }
855
+ type IdAndCommitTs = {
856
+ id: string;
857
+ commitTs: bigint;
858
+ };
859
+ type SelectRow<S extends SchemaDefinition<string, ZodRawShape>, K extends keyof InferSchema<S>, C extends Record<string, unknown>> = Prettify<Pick<InferSchema<S>, Extract<K | "id" | "commitTs", keyof InferSchema<S>>> & C>;
860
+ type OrderDirection = "ASC" | "DESC";
861
+ interface OrderByOptions {
862
+ direction?: OrderDirection;
863
+ nulls?: "first" | "last";
864
+ collate?: string;
865
+ }
866
+ /**
867
+ * Cursor-based pagination using a timestamp column and a unique ID column.
868
+ *
869
+ * Generates a stable cursor predicate:
870
+ *
871
+ * DESC (default):
872
+ * ```sql
873
+ * WHERE (timeCol < ? OR (timeCol = ? AND idCol < ?))
874
+ * ORDER BY timeCol DESC, idCol DESC
875
+ * ```
876
+ *
877
+ * ASC:
878
+ * ```sql
879
+ * WHERE (timeCol > ? OR (timeCol = ? AND idCol > ?))
880
+ * ORDER BY timeCol ASC, idCol ASC
881
+ * ```
882
+ */
883
+ interface PaginationClause<S extends SchemaDefinition<string, ZodRawShape>, Time extends OrderableFieldKeys<S>, Id extends OrderableFieldKeys<S>> {
884
+ /** Timestamp column used for cursor pagination. */
885
+ timeCol: Time;
886
+ /**
887
+ * Unique tie-breaker column.
888
+ *
889
+ * @default "id"
890
+ */
891
+ idCol?: Id;
892
+ /**
893
+ * Pagination direction.
894
+ *
895
+ * - `"desc"` (default): Newest → oldest.
896
+ * - `"asc"`: Oldest → newest.
897
+ *
898
+ * @default "desc"
899
+ */
900
+ direction?: OrderDirection;
901
+ /**
902
+ * Maximum number of rows to return.
903
+ */
904
+ limit?: number;
905
+ /**
906
+ * Cursor returned from the previous page.
907
+ *
908
+ * Omit or set to `null` to fetch the first page.
909
+ */
910
+ cursor?: {
911
+ time: FieldValue<S, Time>;
912
+ id: FieldValue<S, Id>;
913
+ } | null;
914
+ }
915
+ declare class SelectQueryBuilder<S extends SchemaDefinition<string, ZodRawShape>, K extends keyof InferSchema<S> = never, C extends Record<string, unknown> = {}> {
916
+ private ctx;
917
+ private model;
918
+ private filters;
919
+ private paginationClause;
920
+ private orderByClause;
921
+ private limitVal;
922
+ private offsetVal;
923
+ private selectedProps;
924
+ private computedFields;
925
+ private commentStr;
926
+ constructor(ctx: TxContext, model: Model, filters?: ((fb: FilterBuilder<S>) => void)[], orderByClause?: string[] | null, limitVal?: number | null, offsetVal?: number | null, selectedProps?: (keyof InferSchema<S>)[], computedFields?: ComputedField[], commentStr?: string);
927
+ private getColumnName;
928
+ private getPropertyName;
929
+ private mapRowToSchema;
930
+ where(filterFn: (fb: FilterBuilder<S>) => void): SelectQueryBuilder<S, K, C>;
931
+ paginate<Time extends OrderableFieldKeys<S>, Id extends OrderableFieldKeys<S>>(clause: PaginationClause<S, Time, Id>): SelectQueryBuilder<S, K, C>;
932
+ orderBy(column: keyof InferSchema<S> | keyof C, direction?: OrderDirection): SelectQueryBuilder<S, K, C>;
933
+ orderBy(column: keyof InferSchema<S> | keyof C, options?: OrderByOptions): SelectQueryBuilder<S, K, C>;
934
+ private buildOrderBySql;
935
+ orderByRaw(sql: string): SelectQueryBuilder<S, K, C>;
936
+ limit(n: number): SelectQueryBuilder<S, K, C>;
937
+ offset(n: number): SelectQueryBuilder<S, K, C>;
938
+ computed<TField extends string, TZodType extends z$1.ZodTypeAny | undefined = undefined, TDecode extends (value: any) => any = (value: unknown) => unknown>(config: ComputedFieldConfig<TField, ReturnType<TDecode>, Parameters<TDecode>[0], TZodType> & {
939
+ codec: {
940
+ decode: TDecode;
941
+ encode?: (value: ReturnType<TDecode>) => Parameters<TDecode>[0];
942
+ };
943
+ }): SelectQueryBuilder<S, K | TField, OverrideField<C, TField, ComputedDataType<ComputedCodec<ReturnType<TDecode>, Parameters<TDecode>[0]>, TZodType>>>;
944
+ computed<TField extends string, TZodType extends z$1.ZodTypeAny | undefined = undefined>(config: ComputedFieldConfig<TField, unknown, unknown, TZodType> & {
945
+ codec?: undefined;
946
+ }): SelectQueryBuilder<S, K | TField, OverrideField<C, TField, ComputedDataType<undefined, TZodType>>>;
947
+ comment(input: string | Record<string, string>): SelectQueryBuilder<S, K, C>;
948
+ first(): Promise<SelectRow<S, K, C> | null>;
949
+ exists(): Promise<boolean>;
950
+ private static defaultCountType;
951
+ count(): Promise<number>;
952
+ count<T extends z$1.ZodType>(parser: T): Promise<z$1.output<T>>;
953
+ find(id: string): Promise<SelectRow<S, K, C> | null>;
954
+ get(): Promise<SelectRow<S, K, C>[]>;
955
+ private buildPredicate;
956
+ }
957
+ declare class FilterBuilder<S extends SchemaDefinition<string, ZodRawShape>> {
958
+ private columns;
959
+ private codecs;
960
+ predicates: Predicate[];
961
+ constructor(columns: Record<string, string>, codecs: Record<string, ColumnCodec<unknown, unknown>>);
962
+ private add;
963
+ private addJsonContains;
964
+ private getColumnName;
965
+ private encode;
966
+ private encodeJson;
967
+ eq<K extends keyof InferSchema<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S>;
968
+ neq<K extends keyof InferSchema<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S>;
969
+ gt<K extends OrderableFieldKeys<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S>;
970
+ gte<K extends OrderableFieldKeys<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S>;
971
+ lt<K extends OrderableFieldKeys<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S>;
972
+ lte<K extends OrderableFieldKeys<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S>;
973
+ like<K extends StringFieldKeys<S>>(key: K, pattern: string): FilterBuilder<S>;
974
+ ilike<K extends StringFieldKeys<S>>(key: K, pattern: string): FilterBuilder<S>;
975
+ contains<K extends StringFieldKeys<S>>(key: K, value: string): FilterBuilder<S>;
976
+ icontains<K extends StringFieldKeys<S>>(key: K, value: string): FilterBuilder<S>;
977
+ /**
978
+ * JSON containment. Targets a JSON/JSONB column and asserts the document
979
+ * contains the given candidate (object-key subset for objects,
980
+ * element-subset for arrays — see dialect-specific docs for edge cases).
981
+ * Pass `{ raw: true }` if `value` is already a JSON-encoded string.
982
+ * Pass `{ path: ... }` to scope the check to a sub-document of the
983
+ * target (jsonb_path_query_first on PG, JSON_CONTAINS path arg on MySQL).
984
+ */
985
+ jsonContains<K extends JsonFieldKeys<S>>(key: K, value: unknown, opts?: JsonContainsOptions): FilterBuilder<S>;
986
+ /** Inverse of {@link jsonContains}: column is contained in the given value. */
987
+ jsonContainedBy<K extends JsonFieldKeys<S>>(key: K, value: unknown, opts?: JsonContainsOptions): FilterBuilder<S>;
988
+ /**
989
+ * Key/path existence check on a JSON column.
990
+ *
991
+ * - String form (top-level key): `jsonHasKey("meta", "color")`. On PG
992
+ * also matches when the column is a string array and `value` is one of
993
+ * its elements (PG `?` semantics); MySQL stays strict to object keys.
994
+ * - Array form (nested path): `jsonHasKey("meta", ["a", "b"])` compiles
995
+ * to a path expression `$."a"."b"` on both dialects.
996
+ * - Raw form: `jsonHasKey("meta", "$.foo[*]", { raw: true })` passes the
997
+ * string through as a dialect path expression (PG `jsonb_path_exists`
998
+ * jsonpath, MySQL `JSON_CONTAINS_PATH`). Caller owns the syntax.
999
+ * - Multi-path form: `jsonHasKey("meta", [["a","b"], "color"], { mode: "all" })`
1000
+ * compiles each entry to a jsonpath and emits PG OR/AND of
1001
+ * `jsonb_path_exists` or MySQL `JSON_CONTAINS_PATH('all', ...)`.
1002
+ */
1003
+ jsonHasKey<K extends JsonFieldKeys<S>>(key: K, value: string): FilterBuilder<S>;
1004
+ jsonHasKey<K extends JsonFieldKeys<S>>(key: K, value: string[]): FilterBuilder<S>;
1005
+ jsonHasKey<K extends JsonFieldKeys<S>>(key: K, value: string, opts: {
1006
+ raw: true;
1007
+ }): FilterBuilder<S>;
1008
+ jsonHasKey<K extends JsonFieldKeys<S>>(key: K, paths: (string | string[])[], opts: JsonHasKeyMultiOptions): FilterBuilder<S>;
1009
+ jsonHasKey<K extends JsonFieldKeys<S>>(key: K, paths: string[], opts: JsonHasKeyMultiOptions & {
1010
+ raw: true;
1011
+ }): FilterBuilder<S>;
1012
+ /**
1013
+ * Array element containment: target column is a JSON array; the given
1014
+ * `value` (any JSON-encodable type) must appear as one of its elements.
1015
+ * Pass `{ raw: true }` if `value` is already a JSON-encoded string.
1016
+ * Pass `{ path: ... }` to scope the check to a sub-array of the column.
1017
+ */
1018
+ jsonArrayContains<K extends JsonFieldKeys<S>>(key: K, value: unknown, opts?: JsonContainsOptions): FilterBuilder<S>;
1019
+ between<K extends OrderableFieldKeys<S>>(key: K, lo: FieldValue<S, K>, hi: FieldValue<S, K>): FilterBuilder<S>;
1020
+ /**
1021
+ * Set membership: `key` equals any of `values` (SQL `IN`). Each value is
1022
+ * codec-encoded like `eq`. An empty `values` matches no rows. Bound as one
1023
+ * parameter per element, so keep the list within the driver's parameter
1024
+ * limit (chunk very large sets at the call site).
1025
+ */
1026
+ in<K extends keyof InferSchema<S>>(key: K, values: readonly FieldValue<S, K>[]): FilterBuilder<S>;
1027
+ isNull<K extends keyof InferSchema<S>>(key: K): FilterBuilder<S>;
1028
+ notNull<K extends keyof InferSchema<S>>(key: K): FilterBuilder<S>;
1029
+ /**
1030
+ * Compose an OR clause. Each callback fills a fresh sub-FilterBuilder
1031
+ * with predicates that are AND-ed together within that branch; the
1032
+ * branches themselves are OR-ed. Example:
1033
+ * f.or([
1034
+ * b => b.eq("tag", "a"),
1035
+ * b => b.eq("tag", "b").gt("qty", 3), // (tag=b AND qty>3)
1036
+ * ])
1037
+ * produces (tag=a) OR (tag=b AND qty>3).
1038
+ */
1039
+ or(branches: ((fb: FilterBuilder<S>) => void)[]): FilterBuilder<S>;
1040
+ }
1041
+ declare function buildPredicateSql(predicate: Predicate, params: unknown[], sql: SqlBuilder): string;
1042
+ declare function matchesPredicate(row: Record<string, unknown>, predicate: Predicate): boolean;
1043
+ declare function jsonContains(target: unknown, candidate: unknown): boolean;
1044
+ declare function jsonPathExtract(val: unknown, path: string): unknown;
1045
+ declare function jsonPathExists(val: unknown, path: string): boolean;
1046
+ //#endregion
1047
+ //#region src/db/ephemeral.d.ts
1048
+ /**
1049
+ * Default lifetime of an ephemeral entry: 7 days. Long enough that a value set
1050
+ * once (a device's last known position, a presence flag) survives normal
1051
+ * operation, short enough that abandoned keys eventually drain out of Redis on
1052
+ * their own. Override per call via {@link EphemeralSetOptions.ttlSeconds}.
1053
+ */
1054
+ declare const EPHEMERAL_DEFAULT_TTL_SECONDS: number;
1055
+ /**
1056
+ * Shared stand-in for "this computation read no ephemeral state" — the
1057
+ * overwhelmingly common case. Frozen and reused so neither a `TxContext` nor a
1058
+ * `LiveResult` allocates an array for a query that never touches the store.
1059
+ */
1060
+ declare const EMPTY_EPHEMERAL_KEYS: string[];
1061
+ /** Thrown by {@link EphemeralReader.get} when the key holds no value. */
1062
+ declare class EphemeralKeyNotFoundError extends Error {
1063
+ readonly key: string;
1064
+ constructor(key: string);
1065
+ }
1066
+ /**
1067
+ * Thrown when a stored value does not match the schema the reader asked for —
1068
+ * corrupt bytes, or (far more likely) a value written by an older deploy under
1069
+ * a shape that has since changed.
1070
+ *
1071
+ * This is a hard error rather than a silent `null` because the two are not the
1072
+ * same thing: `null` means "nothing is here", and quietly conflating a schema
1073
+ * mismatch with absence turns a deploy bug into a blank screen. A caller that
1074
+ * genuinely wants tolerance can express it in the schema itself — `.catch()`,
1075
+ * `.optional()`, or a union — which is strictly more expressive than anything
1076
+ * this layer could decide on its behalf.
1077
+ */
1078
+ declare class EphemeralDecodeError extends Error {
1079
+ readonly key: string;
1080
+ constructor(key: string, options?: {
1081
+ cause?: unknown;
1082
+ });
1083
+ }
1084
+ /**
1085
+ * Thrown when a mutation's buffered ephemeral writes could not be applied after
1086
+ * its transaction committed.
1087
+ *
1088
+ * The distinction this type exists to make: **the mutation succeeded**. Its rows
1089
+ * are written and durable, and their subscription invalidation has already gone
1090
+ * out. Only the ephemeral half — which lives in Redis, outside the transaction —
1091
+ * failed to land. A caller must NOT respond by retrying the mutation; that would
1092
+ * apply the row writes a second time. Retry the ephemeral write alone, or let
1093
+ * the next write to those keys carry the state forward.
1094
+ */
1095
+ declare class EphemeralFlushError extends Error {
1096
+ readonly keys: string[];
1097
+ constructor(keys: string[], options?: {
1098
+ cause?: unknown;
1099
+ });
1100
+ }
1101
+ interface EphemeralSetOptions {
1102
+ /**
1103
+ * Lifetime in seconds. Defaults to {@link EPHEMERAL_DEFAULT_TTL_SECONDS}
1104
+ * (7 days), or to the server-configured default.
1105
+ *
1106
+ * Expiry is silent: when the TTL elapses the value simply disappears and the
1107
+ * next recompute of a subscribed query observes `null`. It does NOT by itself
1108
+ * push an update — call {@link EphemeralWriter.delete} when subscribers need
1109
+ * to see the value go away at a specific moment.
1110
+ */
1111
+ ttlSeconds?: number;
1112
+ }
1113
+ /**
1114
+ * Read side of the ephemeral store.
1115
+ *
1116
+ * Reached from a query/mutation handler as `ctx.db.ephemeral`, where every read
1117
+ * also records a live-query dependency on the key, and from an action/job as
1118
+ * `ctx.db.ephemeral` on the {@link SupaliveDb} itself (untracked — an action has
1119
+ * no read set).
1120
+ *
1121
+ * Every read takes the Zod schema its value should satisfy. Ephemeral state is
1122
+ * schemaless in Redis — no table, no column types, and its writer may be a
1123
+ * different deploy of your own code — so a `as T` cast here would be a type
1124
+ * that is checked nowhere and true only by convention. Passing the schema makes
1125
+ * the boundary an actual parse.
1126
+ *
1127
+ * The naming mirrors the SQL query builder: {@link find} returns `null` for a
1128
+ * missing key exactly like `query(Schema).find(id)` does, {@link get} is the
1129
+ * "I know it's there" variant that throws, and {@link getOr} supplies a default.
1130
+ */
1131
+ interface EphemeralReader {
1132
+ /** Value for `key`, or `null` when it is absent or expired. */
1133
+ find<T>(key: string, schema: ZodType<T>): Promise<T | null>;
1134
+ /** Value for `key`. Throws {@link EphemeralKeyNotFoundError} when absent. */
1135
+ get<T>(key: string, schema: ZodType<T>): Promise<T>;
1136
+ /** Value for `key`, or `fallback` when it is absent or expired. */
1137
+ getOr<T>(key: string, schema: ZodType<T>, fallback: T): Promise<T>;
1138
+ /** Values for `keys`, positionally aligned; `null` for each absent key. */
1139
+ findMany<T>(keys: string[], schema: ZodType<T>): Promise<(T | null)[]>;
1140
+ /** Whether `key` currently holds a value. Does not decode it, so it needs no
1141
+ * schema — and a value that fails to parse still counts as present. */
1142
+ has(key: string): Promise<boolean>;
1143
+ }
1144
+ /** Read side plus writes. See {@link EphemeralReader} for the read semantics. */
1145
+ interface EphemeralWriter extends EphemeralReader {
1146
+ /** Store `value` under `key`, replacing any existing entry. */
1147
+ set<T>(key: string, value: T, opts?: EphemeralSetOptions): Promise<void>;
1148
+ /** Remove `key`. Invalidates subscribers just like a `set` does. */
1149
+ delete(key: string): Promise<void>;
1150
+ }
1151
+ /** A single pending write in a `DbWriter`'s buffer. `ttlSeconds: 0` = delete. */
1152
+ interface PendingEphemeralWrite {
1153
+ key: string;
1154
+ /** Pre-serialized payload; empty string for a delete. */
1155
+ value: string;
1156
+ ttlSeconds: number;
1157
+ }
1158
+ /**
1159
+ * Redis-backed store for state that drives live queries but is never persisted
1160
+ * to the database — a device's latest GPS fix, a "typing…" flag, a transient
1161
+ * job progress percentage.
1162
+ *
1163
+ * Unlike a table row, an ephemeral key has no commit log and no snapshot ts, so
1164
+ * it cannot participate in the OCC read set or the commit-log freshness replay.
1165
+ * Instead a query records the plain key strings it read
1166
+ * (`LiveResult.ephemeralKeys`), the sub-manager indexes subscriptions by those
1167
+ * keys, and a write looks subscribers up by exact key match — cheaper than the
1168
+ * write-set scan, because there is no predicate to evaluate.
1169
+ */
1170
+ declare class EphemeralStore implements EphemeralWriter {
1171
+ private readonly cache;
1172
+ private readonly defaultTtlSeconds;
1173
+ /**
1174
+ * Fired after a batch of writes lands in Redis, with the affected keys and the
1175
+ * newly allocated global version. The server wires this to the sub-manager's
1176
+ * ephemeral invalidation, mirroring how `SupaliveDb.setOnAfterCommit` drives
1177
+ * write-set invalidation. Left unset outside a server (tests, tooling), where
1178
+ * writes are then purely local.
1179
+ */
1180
+ onAfterWrite?: (keys: string[], version: string) => void;
1181
+ constructor(cache: CacheLayer, opts?: {
1182
+ defaultTtlSeconds?: number;
1183
+ });
1184
+ /** The TTL a `set` without an explicit `ttlSeconds` uses. */
1185
+ get defaultTtl(): number;
1186
+ find<T>(key: string, schema: ZodType<T>): Promise<T | null>;
1187
+ get<T>(key: string, schema: ZodType<T>): Promise<T>;
1188
+ getOr<T>(key: string, schema: ZodType<T>, fallback: T): Promise<T>;
1189
+ findMany<T>(keys: string[], schema: ZodType<T>): Promise<(T | null)[]>;
1190
+ has(key: string): Promise<boolean>;
1191
+ set<T>(key: string, value: T, opts?: EphemeralSetOptions): Promise<void>;
1192
+ delete(key: string): Promise<void>;
1193
+ /**
1194
+ * Serialize a `set` into a buffered write WITHOUT touching Redis. Used by
1195
+ * `DbWriter`, which defers ephemeral writes until its transaction commits.
1196
+ */
1197
+ prepareSet<T>(key: string, value: T, opts?: EphemeralSetOptions): PendingEphemeralWrite;
1198
+ /** The `delete` counterpart of {@link prepareSet}. */
1199
+ prepareDelete(key: string): PendingEphemeralWrite;
1200
+ /**
1201
+ * Apply buffered writes atomically, then notify the invalidation hook. The
1202
+ * hook runs only after Redis holds the new values, so any recompute it
1203
+ * triggers is guaranteed to read them.
1204
+ */
1205
+ apply(writes: PendingEphemeralWrite[]): Promise<void>;
1206
+ }
1207
+ /**
1208
+ * An {@link EphemeralStore} backed by a plain Map instead of Redis — for unit
1209
+ * tests and tooling that construct a `SupaliveDb` without a cache layer.
1210
+ *
1211
+ * It is process-local, so it is NOT a substitute for the real store in any
1212
+ * multi-instance deployment: two servers would each see their own values, and
1213
+ * cross-instance invalidation would push stale data. TTLs are honoured on read
1214
+ * (there is no background expiry), which is enough for the semantics tests care
1215
+ * about.
1216
+ */
1217
+ declare function createMemoryEphemeralStore(opts?: {
1218
+ defaultTtlSeconds?: number;
1219
+ }): EphemeralStore;
1220
+ //#endregion
1221
+ //#region src/db/context.d.ts
1222
+ declare class TxContext {
1223
+ beginTs: bigint;
1224
+ db: Database;
1225
+ protected readSet: ReadEntry[];
1226
+ protected writeSet: WriteEntry[];
1227
+ /**
1228
+ * Ephemeral keys read by this context, in first-read order and de-duplicated.
1229
+ * Kept separate from {@link readSet} on purpose: an ephemeral key has no
1230
+ * snapshot ts and no commit log, so letting it into the read set would feed
1231
+ * the OCC validator and the commit-log freshness scan a dependency neither
1232
+ * can reason about.
1233
+ *
1234
+ * `null` until the first ephemeral read. The vast majority of queries never
1235
+ * touch the store, and this context is constructed once per query execution —
1236
+ * so the array is allocated on demand rather than on every read path.
1237
+ */
1238
+ protected ephemeralKeys: string[] | null;
1239
+ /** `set`/`delete` calls awaiting a commit — see {@link DbWriter.ephemeral}.
1240
+ * Lazily allocated, like {@link ephemeralKeys}. */
1241
+ protected ephemeralWrites: PendingEphemeralWrite[] | null;
1242
+ /** Backing store for `ctx.db.ephemeral`. */
1243
+ protected readonly ephemeralStore: EphemeralStore;
1244
+ private models;
1245
+ protected getOrCreateModel(schema: SchemaDefinition<string, ZodRawShape>): Model;
1246
+ constructor(db: Database, beginTs: bigint, ephemeralStore: EphemeralStore);
1247
+ /**
1248
+ * Record a live-query dependency on `key` (idempotent within a context).
1249
+ * @internal — called by the wrappers in db/ephemeral.ts.
1250
+ */
1251
+ internalTrackEphemeralRead(key: string): void;
1252
+ /**
1253
+ * Queue an ephemeral write for the post-commit flush, replacing any earlier
1254
+ * write to the same key so the last write in an attempt wins.
1255
+ * @internal — called by the wrapper in db/ephemeral.ts.
1256
+ */
1257
+ internalBufferEphemeralWrite(write: PendingEphemeralWrite): void;
1258
+ /**
1259
+ * Rebind the read connection (and its snapshot ts). Used by the replica
1260
+ * router when a query handler calls `ctx.usePrimaryConn()`/`useReplicaConn()`.
1261
+ * Must be called BEFORE any read — the readSet/beginTs would otherwise mix
1262
+ * snapshots — so this throws if a read or write has already happened.
1263
+ */
1264
+ useConnection(db: Database, beginTs: bigint): void;
1265
+ internalGetReadSet(): ReadEntry[];
1266
+ internalGetWriteSet(): WriteEntry[];
1267
+ /** Null when this context read no ephemeral state — see {@link ephemeralKeys}. */
1268
+ internalGetEphemeralKeys(): string[] | null;
1269
+ /** Null when this context buffered no ephemeral writes. */
1270
+ internalGetEphemeralWrites(): PendingEphemeralWrite[] | null;
1271
+ /**
1272
+ * Merge a parent procedure's reads into this context's readSet. Used when a
1273
+ * `runMutation` sub-transaction inherits the reads of the procedure that
1274
+ * invoked it, so the sub-tx's OCC validation also fences on those rows.
1275
+ * Entries are shallow-cloned so the two readSets don't alias.
1276
+ */
1277
+ appendInternalReadSet(parentEntries: ReadEntry[]): void;
1278
+ /**
1279
+ * Merge ephemeral dependencies recorded elsewhere into this context. The
1280
+ * counterpart of {@link appendInternalReadSet}: used when a nested query is
1281
+ * served from cache, so the parent still depends on the keys the cached
1282
+ * computation read and re-runs when any of them change.
1283
+ */
1284
+ appendInternalEphemeralKeys(keys: string[]): void;
1285
+ protected findPendingWrite(table: string, id: string): WriteEntry | undefined;
1286
+ protected findPendingInsert(table: string, id: string): WriteEntry | undefined;
1287
+ protected encodeWithCodec(model: Model, property: string, value: unknown): unknown;
1288
+ }
1289
+ declare class DbReader extends TxContext {
1290
+ private ephemeralApi?;
1291
+ query<S extends SchemaDefinition<string, ZodRawShape>>(schema: S): QueryBuilder<S>;
1292
+ /**
1293
+ * Read ephemeral state — values that drive live queries but are never stored
1294
+ * in the database (a device's latest GPS fix, a presence flag).
1295
+ *
1296
+ * Every read here records a dependency on the key, so a subscribed query
1297
+ * re-runs and pushes automatically when an action or mutation writes it. The
1298
+ * dependency is on the KEY, not the value: reading a key that is currently
1299
+ * absent still subscribes you to it being set later.
1300
+ *
1301
+ * Built on first access — a query that never reaches for ephemeral state
1302
+ * allocates neither the wrapper nor its key list.
1303
+ */
1304
+ get ephemeral(): EphemeralReader;
1305
+ }
1306
+ declare class DbWriter extends DbReader {
1307
+ private ephemeralWriteApi?;
1308
+ /**
1309
+ * Read AND write ephemeral state. Reads behave exactly as on a
1310
+ * {@link DbReader}; writes are BUFFERED and applied only once the surrounding
1311
+ * mutation commits (see `SupaliveDb.mutation` and
1312
+ * {@link createBufferedEphemeralWriter} for why).
1313
+ */
1314
+ get ephemeral(): EphemeralWriter;
1315
+ insert<S extends SchemaDefinition<string, ZodRawShape>, ID extends string>(schema: S, id: ID, data: InsertData<S>): Promise<void>;
1316
+ update<S extends SchemaDefinition<string, ZodRawShape>, ID extends string>(schema: S, id: ID, data: Partial<Omit<InferSchema<S>, "id" | "commitTs">>): Promise<void>;
1317
+ delete<S extends SchemaDefinition<string, ZodRawShape>, ID extends string>(schema: S, id: ID): Promise<void>;
1318
+ }
1319
+ //#endregion
1320
+ //#region src/db/query_definition.d.ts
1321
+ /**
1322
+ * A named, typed query definition produced by defineQuery().
1323
+ * Carries the query function at runtime and the name/params/result types
1324
+ * at compile time — making it the single source of truth for both the
1325
+ * server-side registry and the client-side RPC.
1326
+ */
1327
+ interface QueryDefinition<TName extends string, TParams, TResult> {
1328
+ readonly _type: "QueryDefinition";
1329
+ readonly name: TName;
1330
+ readonly fn: (ctx: DbReader, params: TParams) => Promise<TResult>;
1331
+ }
1332
+ type AnyQueryDef = QueryDefinition<string, any, any>;
1333
+ /** Extract the params type from a QueryDefinition. */
1334
+ type ParamsOf<D extends AnyQueryDef> = D extends QueryDefinition<any, infer P, any> ? P : never;
1335
+ /** Extract the result type from a QueryDefinition. */
1336
+ type ResultOf<D extends AnyQueryDef> = D extends QueryDefinition<any, any, infer R> ? R : never;
1337
+ /**
1338
+ * Convert a readonly tuple of QueryDefinitions into a Record keyed by their
1339
+ * name literals. This is what makes ClientRpc fully type-safe: each key in
1340
+ * the map carries its own TParams and TResult rather than collapsing to `any`.
1341
+ *
1342
+ * Example:
1343
+ * const defs = [projectBoardQuery, teamMembersQuery] as const;
1344
+ * type Map = DefsToMap<typeof defs>;
1345
+ * // → { projectBoard: typeof projectBoardQuery; teamMembers: typeof teamMembersQuery }
1346
+ */
1347
+ type DefsToMap<T extends readonly AnyQueryDef[]> = { [D in T[number] as D["name"]]: D };
1348
+ /** For test teardown only — clears the global map between suites. */
1349
+ declare function _resetGlobalDefs(): void;
1350
+ /**
1351
+ * Create a named, typed query definition.
1352
+ *
1353
+ * The definition is automatically registered in a module-level global so
1354
+ * the server can call registry.defineAll() instead of listing every query
1355
+ * explicitly. On the client, pass the definition to createClientRpc() to
1356
+ * get a fully typed subscribe() call.
1357
+ *
1358
+ * Usage:
1359
+ * export const projectBoardQuery = defineQuery(
1360
+ * "projectBoard",
1361
+ * async (ctx, params: { projectId: string }) => {
1362
+ * return ctx.query(Tasks)
1363
+ * .select()
1364
+ * .where(f => f.eq("projectId", params.projectId))
1365
+ * .get();
1366
+ * }
1367
+ * );
1368
+ */
1369
+ declare function defineQuery<TName extends string, TParams, TResult>(name: TName, fn: (ctx: DbReader, params: TParams) => Promise<TResult>): QueryDefinition<TName, TParams, TResult>;
1370
+ //#endregion
1371
+ //#region src/db/realtime_db.d.ts
1372
+ declare class SupaliveDb {
1373
+ readonly impl: Database;
1374
+ readonly ephemeral: EphemeralStore;
1375
+ readonly replica?: SupaliveDb | undefined;
1376
+ /**
1377
+ * @param impl the PRIMARY (writer) connection — all mutations, the OCC
1378
+ * commit-ts source, and cache-freshness commit-log scans run here.
1379
+ * @param ephemeral Redis-backed store behind `ctx.db.ephemeral` (db/ephemeral.ts).
1380
+ * Required: it is reachable from every handler context, so a
1381
+ * db that could be missing one would push the failure out to
1382
+ * request time. A replica SHARES the primary's store —
1383
+ * ephemeral state lives in Redis rather than in either
1384
+ * connection, so read routing must not change what it sees.
1385
+ * @param replica optional read replica for query/action reads (see the
1386
+ * routing in one-shot-query.ts). Undefined → reads use primary.
1387
+ */
1388
+ constructor(impl: Database, ephemeral: EphemeralStore, replica?: SupaliveDb | undefined);
1389
+ static create(config: {
1390
+ db: Database; /** See the constructor: shared with the replica, never per-connection. */
1391
+ ephemeral: EphemeralStore;
1392
+ replica?: Database;
1393
+ }): SupaliveDb;
1394
+ /** True when a distinct read replica is configured. */
1395
+ get hasReplica(): boolean;
1396
+ /**
1397
+ * Register a hook fired after every batch of ephemeral writes lands in Redis.
1398
+ * The ephemeral twin of {@link setOnAfterCommit}: the server uses it to drive
1399
+ * subscription invalidation for writes from any source — an action, a job, or
1400
+ * a mutation's post-commit flush.
1401
+ */
1402
+ setOnAfterEphemeralWrite(cb: (keys: string[], version: string) => void): void;
1403
+ protected onAfterCommit?: (writeSet: WriteEntry[], commitTs: CommitTs) => void;
1404
+ /**
1405
+ * Register a hook fired once after every successful `mutation()` commit
1406
+ * (never on an OCC conflict/rollback), with the committed writeSet and its
1407
+ * commitTs. The server uses this to drive subscription invalidation for ALL
1408
+ * mutation sources — client WS calls, actions, jobs, and the internal caller —
1409
+ * so a server-side write pushes live updates just like a client write does.
1410
+ * Non-server consumers (migration tooling, tests) leave it unset (no-op).
1411
+ */
1412
+ setOnAfterCommit(cb: (writeSet: WriteEntry[], commitTs: CommitTs) => void): void;
1413
+ /**
1414
+ * One-shot read against an EXPLICIT connection + snapshot ts (rather than this
1415
+ * db's own latest). Used by the replica router: the caller pre-resolves the
1416
+ * default read connection (replica for queries) and hands the handler a
1417
+ * `DbReader` it can rebind via `useConnection` (ctx.usePrimaryConn/…). The
1418
+ * returned `ts` is the FINAL bound connection's snapshot, so the cache entry is
1419
+ * stamped with the ts actually read at.
1420
+ */
1421
+ queryInternalOn<T>(read: {
1422
+ impl: Database;
1423
+ beginTs: bigint;
1424
+ }, fn: (ctx: DbReader) => Promise<T>): Promise<LiveResult<T>>;
1425
+ query<T>(fn: (ctx: DbReader) => Promise<T>): Promise<T>;
1426
+ /**
1427
+ * Compute a query once at the latest snapshot, returning the result together
1428
+ * with the snapshot `ts` and the tracked `readSet`. This is a plain one-shot
1429
+ * evaluation — it registers NO live subscription; the `readSet`/`ts` are
1430
+ * returned purely so the caller (e.g. the one-shot query cache) can store
1431
+ * freshness metadata. Use this for cached one-shot query calls (WS `call`,
1432
+ * HTTP RPC). `liveQuery` is the same computation under the subscription path's
1433
+ * name and simply delegates here.
1434
+ */
1435
+ queryInternal<T>(fn: (ctx: DbReader) => Promise<T>): Promise<LiveResult<T>>;
1436
+ liveQuery<T>(fn: (ctx: DbReader) => Promise<T>): Promise<LiveResult<T>>;
1437
+ mutation<T>(fn: (ctx: DbWriter) => Promise<T>, retryPolicy?: RetryPolicy): Promise<MutationResult<T>>;
1438
+ }
1439
+ declare const sleep: (ms: number) => Promise<void>;
1440
+ //#endregion
1441
+ //#region src/jobs/scheduler.d.ts
1442
+ /** A recurring job to keep synced on the scheduler side. */
1443
+ interface ScheduledJobDef {
1444
+ /** Job procedure name (also the last path segment of its endpoint). */
1445
+ name: string;
1446
+ /** Cron expression, e.g. `"0 3 * * *"`. */
1447
+ cron: string;
1448
+ }
1449
+ /** Options for a precise, one-shot job run. */
1450
+ interface EnqueueOptions {
1451
+ /** Run after this many seconds from now. */
1452
+ delaySeconds?: number;
1453
+ /** Run at this absolute time. Overrides `delaySeconds`. */
1454
+ runAt?: Date;
1455
+ /**
1456
+ * Idempotency key. If two enqueues share the same id within the provider's
1457
+ * dedup window, only the first is delivered. Use it to make "schedule the
1458
+ * expiry of X" safe to call more than once.
1459
+ */
1460
+ dedupeId?: string;
1461
+ }
1462
+ /** Handle returned by {@link JobScheduler.enqueue}; pass its `id` to `cancel`. */
1463
+ interface ScheduleHandle {
1464
+ id: string;
1465
+ }
1466
+ /** The pieces of an inbound webhook the scheduler needs to authenticate it. */
1467
+ interface IncomingJobRequest {
1468
+ /** Job name parsed from the request path. */
1469
+ name: string;
1470
+ /** Raw (unparsed) request body — signatures are computed over these bytes. */
1471
+ rawBody: string;
1472
+ /** Request headers, keys lowercased. */
1473
+ headers: Record<string, string | undefined>;
1474
+ /** Full request URL the webhook was delivered to. */
1475
+ url: string;
1476
+ }
1477
+ /**
1478
+ * Pluggable scheduler. The server owns exactly one instance and uses it for
1479
+ * both directions:
1480
+ * • inbound — `verify` authenticates a webhook before the job runs.
1481
+ * • outbound — `syncSchedules` (recurring, at startup) and `enqueue`/`cancel`
1482
+ * (precise one-shots, at runtime) drive the provider.
1483
+ *
1484
+ * Both dev and production implementations POST to the same HTTP job endpoint,
1485
+ * so the server's dispatch path is exercised identically in every environment.
1486
+ */
1487
+ interface JobScheduler {
1488
+ /** Authenticate an inbound webhook. Return false (or throw) to reject. */
1489
+ verify(req: IncomingJobRequest): Promise<boolean>;
1490
+ /**
1491
+ * Make the provider's recurring schedules match `jobs` exactly (create,
1492
+ * update, and remove stale ones). Called once at server startup.
1493
+ * `endpointFor(name)` yields the absolute URL the provider should POST to.
1494
+ */
1495
+ syncSchedules(jobs: ScheduledJobDef[], endpointFor: (name: string) => string): Promise<void>;
1496
+ /** Enqueue a precise one-shot run of `name` with `body` at `endpoint`. */
1497
+ enqueue(name: string, body: unknown, endpoint: string, opts?: EnqueueOptions): Promise<ScheduleHandle>;
1498
+ /** Cancel a previously enqueued one-shot by its handle id. */
1499
+ cancel(handleId: string): Promise<void>;
1500
+ }
1501
+ interface DevSchedulerOptions {
1502
+ /**
1503
+ * How often, in ms, to fire each recurring job in dev. Dev intentionally
1504
+ * ignores exact cron timing (that is the production scheduler's job); it
1505
+ * just fires often enough to exercise the handler. Default 60_000.
1506
+ */
1507
+ recurringIntervalMs?: number;
1508
+ /**
1509
+ * Override the HTTP client (for tests). Defaults to the global `fetch`.
1510
+ */
1511
+ fetchImpl?: typeof fetch;
1512
+ }
1513
+ /**
1514
+ * In-process scheduler for local development and tests. Recurring jobs fire on
1515
+ * a fixed interval; one-shots fire on an exact `setTimeout` (so precise timing
1516
+ * is faithfully testable). Every fire POSTs to the real HTTP job endpoint, and
1517
+ * `verify` accepts everything — there is no signing in dev.
1518
+ */
1519
+ declare class DevScheduler implements JobScheduler {
1520
+ private readonly intervalMs;
1521
+ private readonly fetchImpl;
1522
+ private intervals;
1523
+ private timeouts;
1524
+ constructor(opts?: DevSchedulerOptions);
1525
+ verify(_req: IncomingJobRequest): Promise<boolean>;
1526
+ syncSchedules(jobs: ScheduledJobDef[], endpointFor: (name: string) => string): Promise<void>;
1527
+ enqueue(name: string, body: unknown, endpoint: string, opts?: EnqueueOptions): Promise<ScheduleHandle>;
1528
+ cancel(handleId: string): Promise<void>;
1529
+ /** Stop every dev timer (call on server shutdown / in test teardown). */
1530
+ stop(): void;
1531
+ private post;
1532
+ }
1533
+ interface QStashSchedulerOptions {
1534
+ /** QStash Base url (`QSTASH_BASE_URL`). */
1535
+ baseUrl: string;
1536
+ /** QStash REST token (`QSTASH_TOKEN`). */
1537
+ token: string;
1538
+ /** Current signing key for webhook verification (`QSTASH_CURRENT_SIGNING_KEY`). */
1539
+ currentSigningKey: string;
1540
+ /** Next signing key, for zero-downtime key rotation (`QSTASH_NEXT_SIGNING_KEY`). */
1541
+ nextSigningKey: string;
1542
+ devMode: boolean;
1543
+ }
1544
+ /**
1545
+ * Production scheduler backed by Upstash QStash. Externalizes the timer so a
1546
+ * horizontally-scaled deployment fires each cron tick exactly once (QStash
1547
+ * calls one HTTP endpoint), with at-least-once retries and signed webhooks.
1548
+ */
1549
+ declare class QStashScheduler implements JobScheduler {
1550
+ private readonly client;
1551
+ private readonly receiver;
1552
+ constructor(opts: QStashSchedulerOptions);
1553
+ verify(req: IncomingJobRequest): Promise<boolean>;
1554
+ syncSchedules(jobs: ScheduledJobDef[], endpointFor: (name: string) => string): Promise<void>;
1555
+ enqueue(name: string, body: unknown, endpoint: string, opts?: EnqueueOptions): Promise<ScheduleHandle>;
1556
+ cancel(handleId: string): Promise<void>;
1557
+ }
1558
+ /**
1559
+ * Thin app-facing handle for scheduling precise one-shots from inside
1560
+ * procedures. The server builds one (via `getJobClient()`) that already knows
1561
+ * how to turn a job name into its endpoint, so callers just say
1562
+ * `jobs.enqueue("expireSpendAuth", { id }, { runAt })`.
1563
+ */
1564
+ declare class JobClient {
1565
+ private readonly scheduler;
1566
+ private readonly endpointFor;
1567
+ constructor(scheduler: JobScheduler, endpointFor: (name: string) => string);
1568
+ enqueue(name: string, body: unknown, opts?: EnqueueOptions): Promise<ScheduleHandle>;
1569
+ cancel(handleId: string): Promise<void>;
1570
+ }
1571
+ //#endregion
1572
+ //#region src/router/procedure.d.ts
1573
+ /**
1574
+ * Minimal request-scoped logger exposed as `ctx.log`. Structurally satisfied by
1575
+ * a pino logger, so the server hands its own child logger straight through; the
1576
+ * interface keeps handler code decoupled from pino. Bound with `reqId`, the
1577
+ * procedure name, and (when tracing is enabled) the `traceId`, so every line a
1578
+ * handler writes correlates to its request and trace.
1579
+ */
1580
+ interface HandlerLogger {
1581
+ debug: pino.LogFn;
1582
+ info: pino.LogFn;
1583
+ warn: pino.LogFn;
1584
+ error: pino.LogFn;
1585
+ }
1586
+ /**
1587
+ * Observability handles present on a handler context. `log` is ALWAYS present:
1588
+ * the server binds a request-correlated logger, and direct internal-caller
1589
+ * invocations build a per-procedure child logger from the caller's observability —
1590
+ * handlers never null-check it.
1591
+ */
1592
+ interface HandlerObservability {
1593
+ /** Logger correlated to this operation — request-scoped (reqId + trace id) on
1594
+ * the server path, a per-procedure child logger on internal-caller paths. */
1595
+ log: HandlerLogger;
1596
+ /** The active OpenTelemetry span for this operation. Add attributes/events or
1597
+ * parent child spans off it. A non-recording no-op span when tracing is off. */
1598
+ span?: Span;
1599
+ }
1600
+ /**
1601
+ * Query context passed to query handlers.
1602
+ * Contains database reader and user-defined server context.
1603
+ */
1604
+ /** Options for the per-handler connection-routing methods (see {@link QueryCtx}). */
1605
+ interface ConnOpts {
1606
+ /**
1607
+ * Force the PRIMARY (writer) connection even inside a query/action —
1608
+ * read-your-writes. On `useReplicaConn` this overrides the replica choice.
1609
+ */
1610
+ readOwnWrite?: boolean;
1611
+ }
1612
+ interface QueryCtx<TContext = unknown> extends HandlerObservability {
1613
+ /** Database reader for queries */
1614
+ db: DbReader;
1615
+ /** User-defined server context (auth, requestId, etc.) */
1616
+ serverCtx?: TContext;
1617
+ /**
1618
+ * Route this query's reads to the PRIMARY (writer) connection. Must be called
1619
+ * BEFORE any read (throws otherwise). A no-op when reads already run on primary
1620
+ * (no replica configured, or a primary-only path like a live query).
1621
+ */
1622
+ usePrimaryConn(opts?: ConnOpts): void;
1623
+ /**
1624
+ * Route this query's reads to the read REPLICA, if one is configured. Must be
1625
+ * called BEFORE any read. `{ readOwnWrite: true }` forces the PRIMARY instead.
1626
+ */
1627
+ useReplicaConn(opts?: ConnOpts): void;
1628
+ }
1629
+ /**
1630
+ * Mutation context passed to mutation handlers.
1631
+ * Contains database writer and user-defined server context.
1632
+ */
1633
+ interface MutationCtx<TContext = unknown> extends HandlerObservability {
1634
+ /** Database writer for mutations (includes insert/update/delete) */
1635
+ db: DbWriter;
1636
+ /** User-defined server context (auth, requestId, etc.) */
1637
+ serverCtx?: TContext;
1638
+ /**
1639
+ * No-op on a mutation — writes (and their reads) always run on the primary.
1640
+ * Present so a `MutationCtx` stays structurally assignable to `QueryCtx`, i.e.
1641
+ * a mutation can reuse a `(ctx: QueryCtx) => …` read helper.
1642
+ */
1643
+ usePrimaryConn(opts?: ConnOpts): void;
1644
+ /** No-op on a mutation (always primary). See {@link usePrimaryConn}. */
1645
+ useReplicaConn(opts?: ConnOpts): void;
1646
+ }
1647
+ /**
1648
+ * Action context passed to action handlers.
1649
+ *
1650
+ * Actions are the "external I/O" tier: they hold the full {@link SupaliveDb}
1651
+ * (queries + mutations) and are the ONLY context that also carries side-effect
1652
+ * services — {@link ObjectStorage} for uploads/downloads and a {@link JobClient}
1653
+ * for scheduling precise one-shots. Queries/mutations stay pure (db only) so
1654
+ * they remain cacheable and transaction-scoped.
1655
+ */
1656
+ interface ActionCtx<TContext = unknown> extends HandlerObservability {
1657
+ /** Full database interface for queries and mutations */
1658
+ db: SupaliveDb;
1659
+ /** Object storage, when the server was configured with one (see
1660
+ * `SupaliveServerConfig.storage`); `undefined` otherwise. The usual home
1661
+ * for upload/download presigning. */
1662
+ storage: ObjectStorage;
1663
+ /** Job scheduler client for enqueueing precise one-shots, when the server
1664
+ * was configured with a `scheduler`; `undefined` otherwise. Scheduling is
1665
+ * network I/O, so it belongs in actions, never inside a DB transaction. */
1666
+ scheduler: JobClient;
1667
+ /** User-defined server context (auth, requestId, etc.) */
1668
+ serverCtx?: TContext;
1669
+ }
1670
+ /**
1671
+ * Job context passed to job handlers. Structurally identical to
1672
+ * {@link ActionCtx}: a job is a server-only procedure that runs
1673
+ * non-transactionally against the full {@link SupaliveDb} and may perform
1674
+ * external work, but it is triggered by the scheduler over HTTP (a cron tick
1675
+ * or a precise one-shot) rather than by a connected client. `serverCtx` is
1676
+ * the system context built by the server's `jobContext` factory.
1677
+ */
1678
+ interface JobCtx<TContext = unknown> extends HandlerObservability {
1679
+ /** Full database interface for queries and mutations */
1680
+ db: SupaliveDb;
1681
+ /** Object storage (R2 / S3) for server-side reads/writes from a job */
1682
+ storage: ObjectStorage;
1683
+ /** Job client for scheduling precise one-shots from inside a job */
1684
+ scheduler: JobClient;
1685
+ /** System server context built by the server for scheduler-triggered runs */
1686
+ serverCtx?: TContext;
1687
+ }
1688
+ /**
1689
+ * Context handed to `caller.<proc>.runQuery` / `.runMutation` when invoking one
1690
+ * procedure from inside another. Pass the caller handler's own `ctx` — its `db`
1691
+ * carries the parent's live transaction (a {@link DbReader}/{@link DbWriter}) or,
1692
+ * inside an action, the full {@link SupaliveDb}. The caller uses this to decide
1693
+ * whether the nested call joins the parent's snapshot (queries) or runs as an
1694
+ * independent sub-transaction (mutations).
1695
+ */
1696
+ interface ParentCtx<TContext = unknown> {
1697
+ db: DbReader | DbWriter | SupaliveDb;
1698
+ serverCtx?: TContext | undefined;
1699
+ /**
1700
+ * The parent handler's own logger, carried through to a nested
1701
+ * `runQuery`/`runMutation` so the nested call logs under the SAME logger as
1702
+ * its parent (reqId/trace correlation preserved).
1703
+ */
1704
+ log: HandlerLogger;
1705
+ /**
1706
+ * The parent handler's own span, carried through to a nested
1707
+ * `runQuery`/`runMutation`. Used ONLY as an explicit fallback parent: the
1708
+ * nested call normally links implicitly through the ambient OTEL context
1709
+ * (the parent handler already runs inside its operation span's context).
1710
+ * This span is consulted only when the ambient context is root — e.g. a
1711
+ * detached/fire-and-forget internal call.
1712
+ */
1713
+ span?: Span;
1714
+ }
1715
+ /**
1716
+ * The slice of the server's {@link Observability} an internal caller needs to
1717
+ * build per-call loggers (see `createCaller().init({ obs })`). Structural, so an
1718
+ * `Observability` instance satisfies it without the caller depending on it.
1719
+ */
1720
+ interface CallerObservability {
1721
+ lazyChildLogger(fields: Record<string, unknown>): HandlerLogger;
1722
+ }
1723
+ type QueryFn<TInput, TResult, TContext = unknown> = (ctx: QueryCtx<TContext>, input: TInput) => Promise<TResult>;
1724
+ type MutationFn<TInput, TResult, TContext = unknown> = (ctx: MutationCtx<TContext>, input: TInput) => Promise<TResult>;
1725
+ type ActionFn<TInput, TResult, TContext = unknown> = (ctx: ActionCtx<TContext>, input: TInput) => Promise<TResult>;
1726
+ type JobFn<TInput, TResult, TContext = unknown> = (ctx: JobCtx<TContext>, input: TInput) => Promise<TResult>;
1727
+ /**
1728
+ * Per-procedure override for the cache/subscription segmentation key.
1729
+ *
1730
+ * omitted | undefined → fall back to `config.getUserId(serverCtx)` (default)
1731
+ * false → no identity in hash; cache/sub shared across all users
1732
+ * string → a static identity literal (e.g. "public" or a tenant id)
1733
+ * function → compute from serverCtx + input (sync)
1734
+ *
1735
+ * When the result of a query is identical regardless of who calls it, set
1736
+ * this to a literal (or `false`) so a single cache entry serves everyone.
1737
+ *
1738
+ * The function form intentionally takes `serverCtx` (not the full `QueryCtx`)
1739
+ * because identity is resolved before any DB read is issued.
1740
+ */
1741
+ type QueryIdentitySpec<TInput, TContext> = false | string | ((serverCtx: TContext, input: TInput) => string | null | undefined);
1742
+ interface BaseProcedure<TInput, TResult, TType extends "query" | "mutation" | "action" | "job", TContext = unknown, TInternal extends boolean = boolean> {
1743
+ readonly _type: "procedure";
1744
+ readonly procedureType: TType;
1745
+ readonly inputSchema: ZodType<TInput>;
1746
+ readonly outputSchema?: ZodType<any>;
1747
+ readonly fn: QueryFn<TInput, TResult, TContext> | MutationFn<TInput, TResult, TContext> | ActionFn<TInput, TResult, TContext> | JobFn<TInput, TResult, TContext>;
1748
+ readonly internal: TInternal;
1749
+ }
1750
+ interface QueryProcedure<TInput, TResult, TContext = unknown, TInternal extends boolean = boolean> extends BaseProcedure<TInput, TResult, "query", TContext, TInternal> {
1751
+ readonly procedureType: "query";
1752
+ readonly fn: QueryFn<TInput, TResult, TContext>;
1753
+ readonly queryIdentity?: QueryIdentitySpec<TInput, TContext>;
1754
+ }
1755
+ interface MutationProcedure<TInput, TResult, TContext = unknown, TInternal extends boolean = boolean> extends BaseProcedure<TInput, TResult, "mutation", TContext, TInternal> {
1756
+ readonly procedureType: "mutation";
1757
+ readonly fn: MutationFn<TInput, TResult, TContext>;
1758
+ }
1759
+ interface ActionProcedure<TInput, TResult, TContext = unknown, TInternal extends boolean = boolean> extends BaseProcedure<TInput, TResult, "action", TContext, TInternal> {
1760
+ readonly procedureType: "action";
1761
+ readonly fn: ActionFn<TInput, TResult, TContext>;
1762
+ }
1763
+ /**
1764
+ * A scheduler-triggered, server-only procedure. Runs like an action (full
1765
+ * db, non-transactional, may do external work) but is dispatched by the
1766
+ * server's HTTP job endpoint on a cron tick or a precise one-shot rather
1767
+ * than over the client WebSocket. Always {@link internal}: true, so it is
1768
+ * never reachable via `call`/`subscribe`.
1769
+ */
1770
+ interface JobProcedure<TInput, TResult, TContext = unknown> extends BaseProcedure<TInput, TResult, "job", TContext, true> {
1771
+ readonly procedureType: "job";
1772
+ readonly fn: JobFn<TInput, TResult, TContext>;
1773
+ /**
1774
+ * Cron expression for a recurring job (e.g. `"0 3 * * *"`). Declared crons
1775
+ * are synced to the scheduler at server startup. Omit for a job that is
1776
+ * only ever invoked as a precise one-shot via the scheduler API.
1777
+ */
1778
+ readonly cron?: string;
1779
+ }
1780
+ type AnyProcedure<TContext = unknown> = QueryProcedure<any, any, TContext> | MutationProcedure<any, any, TContext> | ActionProcedure<any, any, TContext> | JobProcedure<any, any, TContext>;
1781
+ interface QueryConfig<TInput, TResult, TContext = unknown> {
1782
+ /** Zod schema for input validation */
1783
+ args: ZodType<TInput>;
1784
+ /** The query handler function */
1785
+ handler: QueryFn<TInput, TResult, TContext>;
1786
+ /** Mark as internal (server-only). Default: false */
1787
+ internal?: boolean;
1788
+ /**
1789
+ * Optional Zod schema describing return-type overrides for codegen.
1790
+ * Only the fields you specify are overridden; everything else is inferred
1791
+ * from the handler's TypeScript return type. Use `.modelName("Name")`
1792
+ * on a Zod object to rename the generated model class.
1793
+ */
1794
+ returns?: ZodType<any>;
1795
+ /**
1796
+ * Override the cache/subscription segmentation key for this procedure.
1797
+ * See {@link QueryIdentitySpec}. Omit to keep the default (per-user) behavior.
1798
+ */
1799
+ queryIdentity?: QueryIdentitySpec<TInput, TContext>;
1800
+ }
1801
+ interface MutationConfig<TInput, TResult, TContext = unknown> {
1802
+ /** Zod schema for input validation */
1803
+ args: ZodType<TInput>;
1804
+ /** The mutation handler function */
1805
+ handler: MutationFn<TInput, TResult, TContext>;
1806
+ /** Mark as internal (server-only). Default: false */
1807
+ internal?: boolean;
1808
+ /**
1809
+ * Optional Zod schema describing return-type overrides for codegen.
1810
+ * Only the fields you specify are overridden; everything else is inferred
1811
+ * from the handler's TypeScript return type.
1812
+ */
1813
+ returns?: ZodType<any>;
1814
+ }
1815
+ interface ActionConfig<TInput, TResult, TContext = unknown> {
1816
+ /** Zod schema for input validation */
1817
+ args: ZodType<TInput>;
1818
+ /** The action handler function */
1819
+ handler: ActionFn<TInput, TResult, TContext>;
1820
+ /** Mark as internal (server-only). Default: false */
1821
+ internal?: boolean;
1822
+ /**
1823
+ * Optional Zod schema describing return-type overrides for codegen.
1824
+ * Only the fields you specify are overridden; everything else is inferred
1825
+ * from the handler's TypeScript return type.
1826
+ */
1827
+ returns?: ZodType<any>;
1828
+ }
1829
+ interface JobConfig<TInput, TResult, TContext = unknown> {
1830
+ /** Zod schema for the job payload */
1831
+ args: ZodType<TInput>;
1832
+ /** The job handler function */
1833
+ handler: JobFn<TInput, TResult, TContext>;
1834
+ /**
1835
+ * Cron expression for a recurring schedule (e.g. `"0 3 * * *"`). Omit for a
1836
+ * job that is only invoked as a precise one-shot.
1837
+ */
1838
+ cron?: string;
1839
+ }
1840
+ /** Extract input type from a procedure */
1841
+ type InputOf<T> = T extends BaseProcedure<infer I, any, any, any> ? I : never;
1842
+ /** Extract output type from a procedure */
1843
+ type OutputOf<T> = T extends BaseProcedure<any, infer O, any, any> ? O : never;
1844
+ /** Extract procedure type (query/mutation) */
1845
+ type TypeOf<T> = T extends BaseProcedure<any, any, infer Type, any> ? Type : never;
1846
+ /** Extract server context type from a procedure */
1847
+ type ContextOf<T> = T extends BaseProcedure<any, any, any, infer C> ? C : never;
1848
+ /**
1849
+ * Create a query builder with a pre-defined context type.
1850
+ * This allows you to define the context type once and have it inferred
1851
+ * automatically in all your query handlers.
1852
+ *
1853
+ * @example
1854
+ * // Define your server context
1855
+ * interface ServerContext {
1856
+ * auth: { userId: string };
1857
+ * requestId: string;
1858
+ * }
1859
+ *
1860
+ * // Create a typed query builder
1861
+ * const query = createQueryBuilder<ServerContext>();
1862
+ *
1863
+ * // Use it - context type is automatically inferred!
1864
+ * const getUser = query({
1865
+ * args: z.object({ id: z.string() }),
1866
+ * handler: async (ctx, { id }) => {
1867
+ * // ctx.db for database queries
1868
+ * const user = await ctx.db.query(UsersSchema).find(id);
1869
+ * // ctx.context for server context
1870
+ * console.log(ctx.context.requestId);
1871
+ * return user;
1872
+ * }
1873
+ * });
1874
+ */
1875
+ declare function createQueryBuilder<TContext = unknown>(): <TInput, TResult, const TInternal extends boolean = false>(config: QueryConfig<TInput, TResult, TContext> & {
1876
+ internal?: TInternal;
1877
+ }) => QueryProcedure<TInput, TResult, TContext, TInternal>;
1878
+ /**
1879
+ * Create a mutation builder with a pre-defined context type.
1880
+ * This allows you to define the context type once and have it inferred
1881
+ * automatically in all your mutation handlers.
1882
+ *
1883
+ * @example
1884
+ * // Define your server context
1885
+ * interface ServerContext {
1886
+ * auth: { userId: string };
1887
+ * requestId: string;
1888
+ * }
1889
+ *
1890
+ * // Create a typed mutation builder
1891
+ * const mutation = createMutationBuilder<ServerContext>();
1892
+ *
1893
+ * // Use it - context type is automatically inferred!
1894
+ * const createUser = mutation({
1895
+ * args: z.object({ name: z.string() }),
1896
+ * handler: async (ctx, { name }) => {
1897
+ * // ctx.db for mutations
1898
+ * await ctx.db.insert(UsersSchema, id, { name });
1899
+ * // ctx.context for server context
1900
+ * console.log(ctx.context.requestId);
1901
+ * return { success: true };
1902
+ * }
1903
+ * });
1904
+ */
1905
+ declare function createMutationBuilder<TContext = unknown>(): <TInput, TResult, const TInternal extends boolean = false>(config: MutationConfig<TInput, TResult, TContext> & {
1906
+ internal?: TInternal;
1907
+ }) => MutationProcedure<TInput, TResult, TContext, TInternal>;
1908
+ /**
1909
+ * Create an action builder with a pre-defined context type.
1910
+ * Actions have access to the full SupaliveDb for both queries and mutations.
1911
+ *
1912
+ * @example
1913
+ * // Define your server context
1914
+ * interface ServerContext {
1915
+ * auth: { userId: string };
1916
+ * requestId: string;
1917
+ * }
1918
+ *
1919
+ * // Create a typed action builder
1920
+ * const action = createActionBuilder<ServerContext>();
1921
+ *
1922
+ * // Use it - context type is automatically inferred!
1923
+ * const processOrder = action({
1924
+ * args: z.object({ orderId: z.string() }),
1925
+ * handler: async (ctx, { orderId }) => {
1926
+ * // ctx.db for full database access
1927
+ * const order = await ctx.db.query(async (db) => {
1928
+ * return db.query(OrdersSchema).find(orderId);
1929
+ * });
1930
+ * await ctx.db.mutation(async (db) => {
1931
+ * await db.update(OrdersSchema, orderId, { status: "processed" });
1932
+ * });
1933
+ * // ctx.context for server context
1934
+ * console.log(ctx.context.requestId);
1935
+ * return { success: true };
1936
+ * }
1937
+ * });
1938
+ */
1939
+ declare function createActionBuilder<TContext = unknown>(): <TInput, TResult, const TInternal extends boolean = false>(config: ActionConfig<TInput, TResult, TContext> & {
1940
+ internal?: TInternal;
1941
+ }) => ActionProcedure<TInput, TResult, TContext, TInternal>;
1942
+ /**
1943
+ * Create a job builder with a pre-defined context type. A job is a
1944
+ * server-only procedure invoked by the scheduler over HTTP — either on its
1945
+ * declared `cron` schedule or as a precise one-shot enqueued via the
1946
+ * server's job API. Jobs run like actions (full db, non-transactional) and
1947
+ * are always internal, so they are never reachable from a client.
1948
+ *
1949
+ * @example
1950
+ * const job = createJobBuilder<ServerContext>();
1951
+ *
1952
+ * export const cleanupOtps = job({
1953
+ * cron: "0 * * * *", // hourly
1954
+ * args: z.object({}),
1955
+ * handler: async (ctx) => {
1956
+ * await ctx.db.mutation(async (db) => { ... });
1957
+ * return { ok: true };
1958
+ * },
1959
+ * });
1960
+ */
1961
+ declare function createJobBuilder<TContext = unknown>(): <TInput, TResult>(config: JobConfig<TInput, TResult, TContext>) => JobProcedure<TInput, TResult, TContext>;
1962
+ //#endregion
1963
+ //#region src/router/router.d.ts
1964
+ /**
1965
+ * A router maps procedure names (keys) to their definitions.
1966
+ * This is the type that's exported from your app and used by the client.
1967
+ */
1968
+ type Router<TProcedures extends Record<string, AnyProcedure<TContext>>, TContext = unknown> = {
1969
+ _type: "router";
1970
+ procedures: TProcedures;
1971
+ contextName: string;
1972
+ };
1973
+ /**
1974
+ * Inferred AppRouter type from router() call.
1975
+ * Captures the full procedure map for client type inference.
1976
+ */
1977
+ type AppRouter<TProcedures extends Record<string, AnyProcedure<TContext>> = Record<string, AnyProcedure>, TContext = unknown> = Router<TProcedures, TContext>;
1978
+ /** Get query procedures only from a router */
1979
+ type QueryProcedures<T extends Record<string, AnyProcedure>> = { [K in keyof T as T[K] extends {
1980
+ procedureType: "query";
1981
+ } ? K : never]: T[K] };
1982
+ /** Get mutation procedures only from a router */
1983
+ type MutationProcedures<T extends Record<string, AnyProcedure>> = { [K in keyof T as T[K] extends {
1984
+ procedureType: "mutation";
1985
+ } ? K : never]: T[K] };
1986
+ /** Get action procedures only from a router */
1987
+ type ActionProcedures<T extends Record<string, AnyProcedure>> = { [K in keyof T as T[K] extends {
1988
+ procedureType: "action";
1989
+ } ? K : never]: T[K] };
1990
+ /** Get job procedures only from a router */
1991
+ type JobProcedures<T extends Record<string, AnyProcedure>> = { [K in keyof T as T[K] extends {
1992
+ procedureType: "job";
1993
+ } ? K : never]: T[K] };
1994
+ /** Get public (non-internal) procedures - for client type safety */
1995
+ type PublicProcedures<T extends Record<string, AnyProcedure>> = Pick<T, { [K in keyof T]: T[K] extends {
1996
+ internal: true;
1997
+ } ? never : K }[keyof T]>;
1998
+ /** Extract procedure names from a router */
1999
+ type ProcedureNames<T extends Router<any>> = T extends Router<infer P> ? keyof P : never;
2000
+ type RouterConfig<T> = {
2001
+ procedures: T;
2002
+ contextName?: string;
2003
+ };
2004
+ /**
2005
+ * Create a router from procedure definitions.
2006
+ * Procedure names are inferred from the object keys.
2007
+ *
2008
+ * @example
2009
+ * const appRouter = router({
2010
+ * procedures: {
2011
+ * getUser,
2012
+ * createUser,
2013
+ * internalGetAll,
2014
+ * },
2015
+ * });
2016
+ *
2017
+ * export type AppRouter = typeof appRouter;
2018
+ */
2019
+ declare function router<TProcedures extends Record<string, AnyProcedure<any>>, TContext = unknown>(config: RouterConfig<TProcedures>): Router<TProcedures, TContext>;
2020
+ interface RegisteredProcedure<TContext = unknown> {
2021
+ name: string;
2022
+ type: "query" | "mutation" | "action" | "job";
2023
+ internal: boolean;
2024
+ inputSchema: AnyProcedure<TContext>["inputSchema"];
2025
+ fn: AnyProcedure<TContext>["fn"];
2026
+ /** Only present for `type === "query"`. See `QueryIdentitySpec`. */
2027
+ queryIdentity?: false | string | ((serverCtx: TContext | undefined, input: unknown) => string | null | undefined);
2028
+ /** Only present for `type === "job"`. Cron expression for recurring jobs. */
2029
+ cron?: string;
2030
+ }
2031
+ declare function getContextRegistry(inContext: string): ContextRegistry;
2032
+ declare class ContextRegistry {
2033
+ procedureRegistry: Map<string, RegisteredProcedure<any>>;
2034
+ internalProcedureNames: Set<string>;
2035
+ registerProcedure<TContext>(name: string, proc: AnyProcedure<TContext>): void;
2036
+ /** Get a registered procedure by name (runtime lookup) */
2037
+ getProcedure<TContext = unknown>(name: string): RegisteredProcedure<TContext> | undefined;
2038
+ /** Get all registered procedure names */
2039
+ getProcedureNames(): string[];
2040
+ /** Get all registered procedures */
2041
+ getAllProcedures(): Map<string, RegisteredProcedure>;
2042
+ /** Get all registered job procedures (server-only, scheduler-triggered). */
2043
+ getJobProcedures(): RegisteredProcedure[];
2044
+ /** Clear the registry (for testing only) */
2045
+ clearRegistry(): void;
2046
+ markInternalProcedure(name: string): void;
2047
+ isInternalProcedure(name: string): boolean;
2048
+ clearInternalProcedures(): void;
2049
+ }
2050
+ //#endregion
2051
+ //#region src/client/ws_client_manager.d.ts
2052
+ declare class RPCError extends Error {
2053
+ code?: string;
2054
+ constructor(message: string, code?: string);
2055
+ }
2056
+ type ClientLogLevel = "debug" | "info" | "warn" | "error" | "silent";
2057
+ /** Minimal logger sink the client writes lifecycle/diagnostic lines to. Pass a
2058
+ * custom implementation via {@link WSClientOptions.logger} to route logs into
2059
+ * your app's logger; otherwise a console sink gated by
2060
+ * {@link WSClientOptions.logLevel} is used. */
2061
+ interface ClientLogger {
2062
+ debug(message: string, ...args: unknown[]): void;
2063
+ info(message: string, ...args: unknown[]): void;
2064
+ warn(message: string, ...args: unknown[]): void;
2065
+ error(message: string, ...args: unknown[]): void;
2066
+ }
2067
+ /** A structured lifecycle event, for feeding client metrics without parsing logs. */
2068
+ interface ClientEvent {
2069
+ type: "connecting" | "connected" | "disconnected" | "reconnecting" | "error";
2070
+ /** Reason/detail (e.g. reconnect trigger, close reason). */
2071
+ detail?: string;
2072
+ /** Reconnect attempt number, when `type === "reconnecting"`. */
2073
+ attempt?: number;
2074
+ }
2075
+ /** Console-backed logger that drops anything below `level`. `"silent"` mutes all. */
2076
+ declare function createConsoleLogger(level?: ClientLogLevel): ClientLogger;
2077
+ interface HeartbeatOptions {
2078
+ /**
2079
+ * Close + reconnect when no server message arrives for this long. The
2080
+ * server emits `{type:"ping"}` every ~15s of channel idle, so anything
2081
+ * larger than ~30s leaves comfortable headroom. Default 60_000.
2082
+ */
2083
+ serverInactivityMs?: number;
2084
+ }
2085
+ interface SendThrottleOptions {
2086
+ /** Yield the event loop every N `call()` invocations. 0 disables. Default 100. */
2087
+ everyN?: number;
2088
+ /** Length of the yield in ms. Default 10. */
2089
+ delayMs?: number;
2090
+ }
2091
+ interface WSClientOptions {
2092
+ /** WebSocket URL (e.g. ws://localhost:3000/ws) */
2093
+ url: string;
2094
+ /** Auth token to send after connection */
2095
+ token?: () => Promise<string | undefined>;
2096
+ /** Optional: reconnection timeout in ms (default: 180000 = 3 minutes) */
2097
+ reconnectionTimeout?: number;
2098
+ /** Reconnection options */
2099
+ reconnect?: {
2100
+ /** Enable automatic reconnection (default: true) */enabled?: boolean; /** Max reconnection attempts (default: 5) */
2101
+ maxAttempts?: number; /** Delay between attempts in ms (default: 1000) */
2102
+ delay?: number; /** Exponential backoff multiplier (default: 1.5) */
2103
+ backoff?: number;
2104
+ };
2105
+ /** Server-inactivity watchdog. See {@link HeartbeatOptions.serverInactivityMs}. */
2106
+ heartbeat?: HeartbeatOptions;
2107
+ /**
2108
+ * Yields the event loop for `delayMs` ms after every `everyN`
2109
+ * `call()` invocations. Useful for keeping React renders / GC tasks
2110
+ * scheduled during long synchronous bursts of mutations/actions; the
2111
+ * heartbeat does not depend on this (the server drives it).
2112
+ * Default `{ everyN: 100, delayMs: 10 }`. Set `everyN: 0` to disable.
2113
+ */
2114
+ throttle?: SendThrottleOptions;
2115
+ /** Called when connection is established */
2116
+ onConnect?: () => void;
2117
+ /** Called when connection is lost */
2118
+ onDisconnect?: () => void;
2119
+ /** Called on connection error */
2120
+ onError?: (error: Error) => void;
2121
+ /**
2122
+ * Custom log sink. When set, it receives all lifecycle/diagnostic lines and
2123
+ * `logLevel` is ignored (filtering is the sink's responsibility). When unset,
2124
+ * a console sink gated by {@link logLevel} is used.
2125
+ */
2126
+ logger?: ClientLogger;
2127
+ /** Level for the default console logger. Default `"warn"` (quiet). */
2128
+ logLevel?: ClientLogLevel;
2129
+ /** Optional structured lifecycle events, for client-side metrics. */
2130
+ onEvent?: (event: ClientEvent) => void;
2131
+ /**
2132
+ * OpenTelemetry-agnostic trace propagation. Return the current W3C
2133
+ * `traceparent` for the active client span (e.g. from `@opentelemetry/api`'s
2134
+ * propagation) and it is sent on every `call`/`subscribe`; the server
2135
+ * continues the trace. Return `undefined` (the default — no hook) to send
2136
+ * nothing. Called per outbound request.
2137
+ */
2138
+ getTraceparent?: () => string | undefined | null;
2139
+ }
2140
+ /**
2141
+ * Public state of the client, surfaced to consumers (React provider, etc.).
2142
+ * - "connecting" — initial connect or reconnect handshake in flight
2143
+ * - "authenticating" — connected, waiting for auth result
2144
+ * - "ready" — connected and auth resolved (authenticated, anonymous, or
2145
+ * explicitly failed — i.e. ready to send calls)
2146
+ * - "disconnected" — currently offline (may auto-retry)
2147
+ */
2148
+ type ClientPublicState = "disconnected" | "connecting" | "authenticating" | "ready";
2149
+ declare class WsClientManager {
2150
+ private ws;
2151
+ private options;
2152
+ private lastSuccessfulConnectionTime;
2153
+ private reconnectionTimeout;
2154
+ private requestId;
2155
+ private pendingRequests;
2156
+ /**
2157
+ * Single source of truth for live subscriptions.
2158
+ *
2159
+ * Keying:
2160
+ * - In-flight subscribe (no subId yet): `_pending:<requestId>`.
2161
+ * - Established: the server-returned canonical `subId`.
2162
+ *
2163
+ * Routing `sub:update` / `sub:gone` is O(1) (`get(subId)`). Subscribe-time
2164
+ * dedup scans `values()` for a matching `localKey` (O(N)), which is cheap
2165
+ * since subscribes happen on component mounts, not in the hot path.
2166
+ */
2167
+ private subscriptions;
2168
+ private queuedCalls;
2169
+ private reconnectAttempts;
2170
+ private reconnectTimer;
2171
+ private connectionState;
2172
+ private authState;
2173
+ /**
2174
+ * Authenticated user id from `auth:success`. Distinct from `queryIdentity`
2175
+ * (server-side cache/sub segmentation key); this is just "who am I logged
2176
+ * in as" for app display purposes. Null while unauthenticated / anonymous.
2177
+ */
2178
+ private authUserId;
2179
+ private explicitlyClosed;
2180
+ private stateListeners;
2181
+ private lastPublicState;
2182
+ private highWaterTs;
2183
+ private serverInactivityMs;
2184
+ private lastServerMessageAt;
2185
+ private inactivityTimer;
2186
+ private pendingConnectResolve;
2187
+ private pendingConnectReject;
2188
+ private throttleEveryN;
2189
+ private throttleDelayMs;
2190
+ private throttleCounter;
2191
+ private throttling;
2192
+ private visibilityListener;
2193
+ private onlineListener;
2194
+ private readonly log;
2195
+ constructor(options: WSClientOptions);
2196
+ /**
2197
+ * Subscribe to client state transitions. Returns an unsubscribe fn.
2198
+ * The listener is called immediately with the current state.
2199
+ */
2200
+ onState(listener: (s: ClientPublicState) => void): () => void;
2201
+ /** Current public state. */
2202
+ getPublicState(): ClientPublicState;
2203
+ /**
2204
+ * Connect to the WebSocket server.
2205
+ * Returns a promise that resolves when connected and authenticated.
2206
+ */
2207
+ connect(): Promise<void>;
2208
+ /**
2209
+ * Disconnect from the server.
2210
+ */
2211
+ disconnect(): void;
2212
+ /**
2213
+ * Check if connected and authenticated.
2214
+ */
2215
+ isReady(): boolean;
2216
+ /**
2217
+ * Check if authenticated.
2218
+ */
2219
+ isAuthenticated(): boolean;
2220
+ /**
2221
+ * Check if anonymouse.
2222
+ */
2223
+ isAnonymouse(): boolean;
2224
+ /**
2225
+ * The authenticated user's id (from `auth:success`), or null when
2226
+ * unauthenticated / anonymous. Useful for "Logged in as …" display.
2227
+ *
2228
+ * Not to be confused with server-side `queryIdentity`, which is the
2229
+ * cache/subscription segmentation key (resolved entirely on the server).
2230
+ */
2231
+ getAuthUserId(): string | null;
2232
+ /**
2233
+ * Call a procedure (query, mutation, or action).
2234
+ *
2235
+ * If the client is not ready (disconnected or still authenticating), the
2236
+ * call is FIFO-queued and dispatched on the next ready state. The promise
2237
+ * still resolves with the eventual server response.
2238
+ *
2239
+ * Pass `onSend` to be notified when the call actually leaves the queue
2240
+ * (used by `useMutation` to flip `state` from "queued" → "loading").
2241
+ */
2242
+ call(procedure: string, input: unknown, options?: {
2243
+ onSend?: () => void;
2244
+ }): Promise<unknown>;
2245
+ /**
2246
+ * Throttle drain loop. Each iteration: sleep `throttleDelayMs`, then
2247
+ * dispatch up to `throttleEveryN` queued items. Exits as soon as the
2248
+ * queue drains inside a single batch (burst over, back to direct
2249
+ * dispatch on the next `call()`) or the connection goes away
2250
+ * (reconnect path will restart us via `flushQueuedCalls`).
2251
+ */
2252
+ private runThrottleLoop;
2253
+ /**
2254
+ * Issue the call against an established connection. Caller must ensure
2255
+ * `isReady()` before invoking.
2256
+ */
2257
+ private dispatchCall;
2258
+ /** Advance the read high-water from a server-returned `ts` (monotonic). */
2259
+ private advanceHighWater;
2260
+ /**
2261
+ * Flush queued calls in FIFO order. Called once `isReady()` becomes
2262
+ * true. With the throttle enabled, dispatch the first batch
2263
+ * synchronously then hand off to the drain loop so a large
2264
+ * post-reconnect flush respects backpressure instead of dumping 10k
2265
+ * frames in one tick.
2266
+ */
2267
+ private flushQueuedCalls;
2268
+ /**
2269
+ * Re-send `subscribe` for every existing subscription. The subId for each
2270
+ * may legitimately change (e.g. `queryIdentity` was a function of user
2271
+ * identity and the user changed during the disconnect), so we re-key the
2272
+ * unified `subscriptions` Map: snapshot every record, drop them from the
2273
+ * Map, then reinsert under a fresh `_pending:<reqId>` key. Each pending
2274
+ * request's `resolve` rekeys the parked record under the new server-issued
2275
+ * subId so subsequent `sub:update` frames route to the existing
2276
+ * `onUpdate` callback (which the React handle is still holding).
2277
+ */
2278
+ private resubscribeAll;
2279
+ /**
2280
+ * Subscribe to a live query.
2281
+ *
2282
+ * The client does not compute the subId locally — it sends the
2283
+ * (procedure, input) pair and receives the canonical subId on the
2284
+ * subscribe response. This lets the server's per-procedure
2285
+ * `queryIdentity` override be authoritative without forcing the client
2286
+ * to mirror any of the resolution logic.
2287
+ *
2288
+ * Returns the server-assigned subId and an unsubscribe function.
2289
+ */
2290
+ subscribe(procedure: string, input: unknown, serializedInput: string, onUpdate: (data: unknown) => void, onGone?: (reason: string | undefined) => void): Promise<{
2291
+ subId: string;
2292
+ unsubscribe: () => void;
2293
+ }>;
2294
+ private findSubByLocalKey;
2295
+ /** Resolve every joiner waiting on this Subscription with the canonical subId. */
2296
+ private resolveJoiners;
2297
+ /** Reject every joiner waiting on this Subscription with the given error. */
2298
+ private rejectJoiners;
2299
+ private unsubscribeByLocalKey;
2300
+ private handleMessage;
2301
+ /**
2302
+ * Called once the client transitions to a "ready" state on a fresh
2303
+ * connection. Replays live-query subscriptions and flushes any queued
2304
+ * calls in FIFO order.
2305
+ */
2306
+ private afterReady;
2307
+ private handleResponse;
2308
+ private handleSubscriptionUpdate;
2309
+ /**
2310
+ * Handles a `sub:gone` message: the server no longer tracks this
2311
+ * subscription (e.g. it was fully unregistered while we were disconnected
2312
+ * from the sub-manager). Notify the handle and drop the local entry so a
2313
+ * re-subscribe is required to receive further updates.
2314
+ */
2315
+ private handleSubscriptionGone;
2316
+ private handleClose;
2317
+ private handleError;
2318
+ private startInactivityWatchdog;
2319
+ private stopInactivityWatchdog;
2320
+ /**
2321
+ * Close the current WebSocket and schedule a reconnect via `handleClose`.
2322
+ *
2323
+ * Atomically swaps the old socket's event handlers to no-ops BEFORE
2324
+ * `close()` so a frame in flight at the moment of close cannot fire
2325
+ * `handleMessage` into a half-cleaned state — that was the source of
2326
+ * the "browser says socket is alive but client says disconnected" race.
2327
+ */
2328
+ private closeAndReconnect;
2329
+ private installEnvironmentListeners;
2330
+ private removeEnvironmentListeners;
2331
+ private scheduleReconnect;
2332
+ private computePublicState;
2333
+ private emitState;
2334
+ /** Fire the optional structured lifecycle event; never lets a throwing hook
2335
+ * break the client. */
2336
+ private emitEvent;
2337
+ private ensureConnected;
2338
+ private send;
2339
+ /** Current W3C traceparent from the optional hook, or undefined. */
2340
+ private traceparent;
2341
+ private cleanup;
2342
+ private generateRequestId;
2343
+ }
2344
+ //#endregion
2345
+ //#region src/db/types_client_rpc.d.ts
2346
+ interface ErrorMessage {
2347
+ type: "error";
2348
+ id?: string;
2349
+ error: string;
2350
+ code?: string;
2351
+ }
2352
+ declare const AuthMessageSchema: z.ZodObject<{
2353
+ type: z.ZodLiteral<"auth">;
2354
+ data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
2355
+ }, z.core.$strip>;
2356
+ declare const CallMessageSchema: z.ZodObject<{
2357
+ type: z.ZodLiteral<"call">;
2358
+ id: z.ZodString;
2359
+ procedure: z.ZodString;
2360
+ input: z.ZodUnknown;
2361
+ minTs: z.ZodOptional<z.ZodString>;
2362
+ trace: z.ZodOptional<z.ZodString>;
2363
+ }, z.core.$strip>;
2364
+ declare const SubscribeMessageSchema: z.ZodObject<{
2365
+ type: z.ZodLiteral<"subscribe">;
2366
+ id: z.ZodString;
2367
+ procedure: z.ZodString;
2368
+ input: z.ZodUnknown;
2369
+ trace: z.ZodOptional<z.ZodString>;
2370
+ }, z.core.$strip>;
2371
+ declare const UnsubscribeMessageSchema: z.ZodObject<{
2372
+ type: z.ZodLiteral<"unsubscribe">;
2373
+ subId: z.ZodString;
2374
+ }, z.core.$strip>;
2375
+ declare const ClientMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
2376
+ type: z.ZodLiteral<"auth">;
2377
+ data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
2378
+ }, z.core.$strip>, z.ZodObject<{
2379
+ type: z.ZodLiteral<"call">;
2380
+ id: z.ZodString;
2381
+ procedure: z.ZodString;
2382
+ input: z.ZodUnknown;
2383
+ minTs: z.ZodOptional<z.ZodString>;
2384
+ trace: z.ZodOptional<z.ZodString>;
2385
+ }, z.core.$strip>, z.ZodObject<{
2386
+ type: z.ZodLiteral<"subscribe">;
2387
+ id: z.ZodString;
2388
+ procedure: z.ZodString;
2389
+ input: z.ZodUnknown;
2390
+ trace: z.ZodOptional<z.ZodString>;
2391
+ }, z.core.$strip>, z.ZodObject<{
2392
+ type: z.ZodLiteral<"unsubscribe">;
2393
+ subId: z.ZodString;
2394
+ }, z.core.$strip>]>;
2395
+ type AuthMessage = z.infer<typeof AuthMessageSchema>;
2396
+ type CallMessage = z.infer<typeof CallMessageSchema>;
2397
+ type SubscribeMessage = z.infer<typeof SubscribeMessageSchema>;
2398
+ type UnsubscribeMessage = z.infer<typeof UnsubscribeMessageSchema>;
2399
+ type ClientMessage = z.infer<typeof ClientMessageSchema>;
2400
+ declare const ConnectedMessageSchema: z.ZodObject<{
2401
+ type: z.ZodLiteral<"connected">;
2402
+ sessionId: z.ZodString;
2403
+ build: z.ZodOptional<z.ZodNumber>;
2404
+ }, z.core.$strip>;
2405
+ declare const AuthSuccessMessageSchema: z.ZodObject<{
2406
+ type: z.ZodLiteral<"auth:success">;
2407
+ userId: z.ZodString;
2408
+ extra: z.ZodOptional<z.ZodObject<{}, z.core.$strip>>;
2409
+ build: z.ZodOptional<z.ZodNumber>;
2410
+ }, z.core.$strip>;
2411
+ declare const AuthFailedMessageSchema: z.ZodObject<{
2412
+ type: z.ZodLiteral<"auth:failed">;
2413
+ message: z.ZodString;
2414
+ extra: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
2415
+ build: z.ZodOptional<z.ZodNumber>;
2416
+ }, z.core.$strip>;
2417
+ declare const ResponseMessageSchema: z.ZodObject<{
2418
+ type: z.ZodLiteral<"response">;
2419
+ id: z.ZodString;
2420
+ success: z.ZodBoolean;
2421
+ data: z.ZodOptional<z.ZodUnknown>;
2422
+ error: z.ZodOptional<z.ZodString>;
2423
+ code: z.ZodOptional<z.ZodString>;
2424
+ subId: z.ZodOptional<z.ZodString>;
2425
+ ts: z.ZodOptional<z.ZodString>;
2426
+ build: z.ZodOptional<z.ZodNumber>;
2427
+ }, z.core.$strip>;
2428
+ declare const SubscriptionUpdateMessageSchema: z.ZodObject<{
2429
+ type: z.ZodLiteral<"sub:update">;
2430
+ subId: z.ZodString;
2431
+ data: z.ZodOptional<z.ZodUnknown>;
2432
+ dataHash: z.ZodOptional<z.ZodString>;
2433
+ ts: z.ZodOptional<z.ZodString>;
2434
+ build: z.ZodOptional<z.ZodNumber>;
2435
+ }, z.core.$strip>;
2436
+ /**
2437
+ * Notifies a client that a subscription is no longer tracked on the server
2438
+ * and will not receive further updates. Sent when, during a sub-manager
2439
+ * recovery cycle, the registration record could not be found (e.g. another
2440
+ * app-server instance fully unregistered the sub while we were disconnected).
2441
+ * The client may choose to re-subscribe or surface the state to the user.
2442
+ */
2443
+ declare const SubscriptionGoneMessageSchema: z.ZodObject<{
2444
+ type: z.ZodLiteral<"sub:gone">;
2445
+ subId: z.ZodString;
2446
+ reason: z.ZodOptional<z.ZodString>;
2447
+ build: z.ZodOptional<z.ZodNumber>;
2448
+ }, z.core.$strip>;
2449
+ /**
2450
+ * Server-driven liveness signal. Sent when the channel has been idle for
2451
+ * the configured app-ping interval (default 15s) and again after every
2452
+ * outbound message. Pure no-op for the client: receiving any message
2453
+ * (including this one) resets the client's inactivity watchdog. The client
2454
+ * does NOT reply.
2455
+ */
2456
+ declare const ServerPingMessageSchema: z.ZodObject<{
2457
+ type: z.ZodLiteral<"ping">;
2458
+ build: z.ZodOptional<z.ZodNumber>;
2459
+ }, z.core.$strip>;
2460
+ declare const ServerMessageSchema: z.ZodUnion<readonly [z.ZodObject<{
2461
+ type: z.ZodLiteral<"connected">;
2462
+ sessionId: z.ZodString;
2463
+ build: z.ZodOptional<z.ZodNumber>;
2464
+ }, z.core.$strip>, z.ZodObject<{
2465
+ type: z.ZodLiteral<"auth:success">;
2466
+ userId: z.ZodString;
2467
+ extra: z.ZodOptional<z.ZodObject<{}, z.core.$strip>>;
2468
+ build: z.ZodOptional<z.ZodNumber>;
2469
+ }, z.core.$strip>, z.ZodObject<{
2470
+ type: z.ZodLiteral<"auth:failed">;
2471
+ message: z.ZodString;
2472
+ extra: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
2473
+ build: z.ZodOptional<z.ZodNumber>;
2474
+ }, z.core.$strip>, z.ZodObject<{
2475
+ type: z.ZodLiteral<"response">;
2476
+ id: z.ZodString;
2477
+ success: z.ZodBoolean;
2478
+ data: z.ZodOptional<z.ZodUnknown>;
2479
+ error: z.ZodOptional<z.ZodString>;
2480
+ code: z.ZodOptional<z.ZodString>;
2481
+ subId: z.ZodOptional<z.ZodString>;
2482
+ ts: z.ZodOptional<z.ZodString>;
2483
+ build: z.ZodOptional<z.ZodNumber>;
2484
+ }, z.core.$strip>, z.ZodObject<{
2485
+ type: z.ZodLiteral<"sub:update">;
2486
+ subId: z.ZodString;
2487
+ data: z.ZodOptional<z.ZodUnknown>;
2488
+ dataHash: z.ZodOptional<z.ZodString>;
2489
+ ts: z.ZodOptional<z.ZodString>;
2490
+ build: z.ZodOptional<z.ZodNumber>;
2491
+ }, z.core.$strip>, z.ZodObject<{
2492
+ type: z.ZodLiteral<"sub:gone">;
2493
+ subId: z.ZodString;
2494
+ reason: z.ZodOptional<z.ZodString>;
2495
+ build: z.ZodOptional<z.ZodNumber>;
2496
+ }, z.core.$strip>, z.ZodObject<{
2497
+ type: z.ZodLiteral<"ping">;
2498
+ build: z.ZodOptional<z.ZodNumber>;
2499
+ }, z.core.$strip>]>;
2500
+ type ConnectedMessage = z.infer<typeof ConnectedMessageSchema>;
2501
+ type AuthSuccessMessage = z.infer<typeof AuthSuccessMessageSchema>;
2502
+ type AuthFailedMessage = z.infer<typeof AuthFailedMessageSchema>;
2503
+ type ResponseMessage = z.infer<typeof ResponseMessageSchema>;
2504
+ type SubscriptionUpdateMessage = z.infer<typeof SubscriptionUpdateMessageSchema>;
2505
+ type SubscriptionGoneMessage = z.infer<typeof SubscriptionGoneMessageSchema>;
2506
+ type ServerPingMessage = z.infer<typeof ServerPingMessageSchema>;
2507
+ type ServerMessage = z.infer<typeof ServerMessageSchema>;
2508
+ declare class AuthenticationError extends Error {
2509
+ extra: Record<string, unknown>;
2510
+ constructor(message: string, extra?: Record<string, unknown>);
2511
+ }
2512
+ /**
2513
+ * Helper type to check if a procedure is internal.
2514
+ */
2515
+ type IsInternal<T> = T extends {
2516
+ internal: infer I;
2517
+ } ? (I extends true ? true : false) : false;
2518
+ interface Context$1 {
2519
+ requestId: string;
2520
+ /** Authenticated user info */
2521
+ user: {
2522
+ userId: string;
2523
+ };
2524
+ }
2525
+ //#endregion
2526
+ //#region src/client/client.d.ts
2527
+ interface ClientOptions {
2528
+ /** WebSocket URL (e.g. ws://localhost:3000/ws) */
2529
+ url: string;
2530
+ /** Auth Info callback */
2531
+ token?: () => Promise<string>;
2532
+ /** Optional: reconnection timeout in ms (default: 180000 = 3 minutes) */
2533
+ reconnectionTimeout?: number;
2534
+ /** Reconnection options */
2535
+ reconnect?: WSClientOptions["reconnect"];
2536
+ /**
2537
+ * Inactivity watchdog. The client never sends a ping itself; the server
2538
+ * emits `{type:"ping"}` while idle and we close+reconnect if no inbound
2539
+ * message arrives within `serverInactivityMs` (default 60s).
2540
+ */
2541
+ heartbeat?: WSClientOptions["heartbeat"];
2542
+ /**
2543
+ * Yields the event loop for `delayMs` after every `everyN` `call()`
2544
+ * invocations. Useful for keeping React renders / GC tasks scheduled
2545
+ * during long synchronous bursts; not load-bearing for the heartbeat.
2546
+ * Default `{ everyN: 100, delayMs: 10 }`. Set `everyN: 0` to disable.
2547
+ */
2548
+ throttle?: WSClientOptions["throttle"];
2549
+ /** Custom log sink; when set, `logLevel` is ignored. See {@link WSClientOptions.logger}. */
2550
+ logger?: WSClientOptions["logger"];
2551
+ /** Level for the default console logger. Default `"warn"` (quiet). */
2552
+ logLevel?: WSClientOptions["logLevel"];
2553
+ /** Optional structured lifecycle events, for client-side metrics. */
2554
+ onEvent?: WSClientOptions["onEvent"];
2555
+ /** OpenTelemetry-agnostic W3C `traceparent` provider; see {@link WSClientOptions.getTraceparent}. */
2556
+ getTraceparent?: WSClientOptions["getTraceparent"];
2557
+ onConnect?: () => void;
2558
+ onDisConnect?: () => void;
2559
+ }
2560
+ /**
2561
+ * Public state of a live query subscription, exposed via `getState()` and
2562
+ * `onState()` on the handle so React (or any UI) can render a consistent
2563
+ * snapshot via `useSyncExternalStore`.
2564
+ *
2565
+ * - "connecting" — initial subscribe in flight
2566
+ * - "success" — subscribed, last server data is in `data`
2567
+ * - "stale" — was successful, transport dropped; keeping last `data`
2568
+ * until the resubscribe completes
2569
+ * - "loading" — `refetch()` triggered an explicit re-subscribe
2570
+ * - "error" — subscribe failed or server sent `sub:gone`
2571
+ */
2572
+ type LiveQueryStatus = "connecting" | "success" | "stale" | "loading" | "error";
2573
+ interface LiveQueryState<T> {
2574
+ data: T | undefined;
2575
+ status: LiveQueryStatus;
2576
+ error: Error | null;
2577
+ }
2578
+ interface LiveQueryHandle<T> {
2579
+ /** Get current cached data (legacy convenience getter). */
2580
+ get(): T | undefined;
2581
+ /** Snapshot of `{ data, status, error }`. Stable between updates. */
2582
+ getState(): LiveQueryState<T>;
2583
+ /**
2584
+ * Listen for state changes (data, status or error). Returns an
2585
+ * unsubscribe function that removes only this listener — the underlying
2586
+ * server subscription stays alive as long as any listener is attached.
2587
+ *
2588
+ * Suitable for `useSyncExternalStore`.
2589
+ */
2590
+ onState(listener: () => void): () => void;
2591
+ /** Listen for data-only changes. Returns listener-removal fn. */
2592
+ onData(callback: (data: T) => void): () => void;
2593
+ /** Force a re-subscribe round-trip. */
2594
+ refetch(): void;
2595
+ /** Unsubscribe from the server (drops listeners too). */
2596
+ unsubscribe(): void;
2597
+ }
2598
+ /**
2599
+ * Create a fully typed RPC client with WebSocket transport.
2600
+ */
2601
+ declare function createClient<AppRouter extends Router<Record<string, AnyProcedure<TContext>>, TContext>, TContext = unknown, TProcedures extends Record<string, AnyProcedure<TContext>> = AppRouter["procedures"]>(options: ClientOptions): ClientFromProcedures<TProcedures, TContext> & WSClientMethods;
2602
+ /**
2603
+ * WebSocket client methods.
2604
+ */
2605
+ interface WSClientMethods {
2606
+ /** Connect to the WebSocket server */
2607
+ connect(): Promise<void>;
2608
+ /** Disconnect from the server */
2609
+ disconnect(): void;
2610
+ /** Check if connected and authenticated */
2611
+ isReady(): boolean;
2612
+ /** Check if authenticated */
2613
+ isAuthenticated(): boolean;
2614
+ /** Check if anonymouse */
2615
+ isAnonymouse(): boolean;
2616
+ /**
2617
+ * The authenticated user id reported by the server on `auth:success`, or
2618
+ * null when anonymous/unauthenticated. This is the auth-tier identity,
2619
+ * NOT the server-side `queryIdentity` (which is the cache/subscription
2620
+ * segmentation key and is not exposed to the client).
2621
+ */
2622
+ getAuthUserId(): string | null;
2623
+ /** Subscribe to client connection state transitions. Returns unsubscribe fn. */
2624
+ onState(listener: (s: ClientPublicState) => void): () => void;
2625
+ /** Snapshot of the current public state. */
2626
+ getPublicState(): ClientPublicState;
2627
+ }
2628
+ /**
2629
+ * Client type from procedures.
2630
+ * Filters internal procedures and creates appropriate methods.
2631
+ * Query procedures get an additional liveQuery() method for real-time subscriptions.
2632
+ */
2633
+ type ClientFromProcedures<TProcedures extends Record<string, AnyProcedure<TContext>>, TContext = unknown> = { [K in keyof TProcedures as IsInternal<TProcedures[K]> extends true ? never : K]: TProcedures[K] extends {
2634
+ procedureType: "query";
2635
+ } ? {
2636
+ query: (input: InputOf<TProcedures[K]>) => Promise<OutputOf<TProcedures[K]>>;
2637
+ liveQuery: (input: InputOf<TProcedures[K]>) => LiveQueryHandle<OutputOf<TProcedures[K]>>;
2638
+ } : TProcedures[K] extends {
2639
+ procedureType: "mutation";
2640
+ } ? {
2641
+ mutate: (input: InputOf<TProcedures[K]>, opts?: CallOptions) => Promise<OutputOf<TProcedures[K]>>;
2642
+ } : TProcedures[K] extends {
2643
+ procedureType: "action";
2644
+ } ? {
2645
+ action: (input: InputOf<TProcedures[K]>, opts?: CallOptions) => Promise<OutputOf<TProcedures[K]>>;
2646
+ } : never };
2647
+ /**
2648
+ * Optional call options forwarded to the transport layer.
2649
+ */
2650
+ interface CallOptions {
2651
+ /**
2652
+ * Fired the first time the request leaves the queue and is actually
2653
+ * sent on the wire. If the call was already sent immediately (client
2654
+ * was ready at call time), this is invoked synchronously.
2655
+ *
2656
+ * Used by `useMutation` to flip `state` from "queued" → "loading".
2657
+ */
2658
+ onSend?: () => void;
2659
+ }
2660
+ //#endregion
2661
+ //#region src/client/caller_client.d.ts
2662
+ interface CallerOptions<TContext = unknown> {
2663
+ isInternal?: boolean;
2664
+ inContext: string;
2665
+ }
2666
+ /**
2667
+ * Create a server-side caller that bypasses HTTP/WebSocket.
2668
+ *
2669
+ * For in-memory calls, input validation is skipped (data is already a JS object).
2670
+ * Query procedures also get a liveQuery() method for server-side use.
2671
+ *
2672
+ * `init` requires the shared Redis-backed query cache, so every read path —
2673
+ * `query`, `runQuery`, and `liveQuery` — behaves like a normal client procedure
2674
+ * call: it checks cache freshness first and only falls back to executing the
2675
+ * query on a miss/stale entry (see runCachedQuery and runCachedQueryInParent).
2676
+ * `runQuery` keeps joining the parent snapshot/readSet (or opening a fresh
2677
+ * routed read for action/job parents), so nested reads stay consistent with the
2678
+ * parent and mutations keep reads-your-writes.
2679
+ */
2680
+ declare function createCaller<AppRouter extends Router<Record<string, AnyProcedure<TContext>>, TContext>, TContext = unknown, TProcedures extends Record<string, AnyProcedure<TContext>> = AppRouter["procedures"]>(options: CallerOptions<any>): CallerFromProcedures<TProcedures, TContext>;
2681
+ /**
2682
+ * Caller type from procedures (includes internal procedures).
2683
+ * Query procedures also have liveQuery() for server-side use.
2684
+ */
2685
+ type CallerFromProcedures<TProcedures extends Record<string, AnyProcedure<TContext>>, TContext = unknown> = { [K in keyof TProcedures]: TProcedures[K] extends {
2686
+ procedureType: "query";
2687
+ } ? {
2688
+ query: (input: InputOf<TProcedures[K]>, serverCtx: TContext) => Promise<OutputOf<TProcedures[K]>>;
2689
+ liveQuery: (input: InputOf<TProcedures[K]>, serverCtx: TContext) => Promise<LiveResult<OutputOf<TProcedures[K]>>>;
2690
+ runQuery: (input: InputOf<TProcedures[K]>, ctx: ParentCtx<TContext>) => Promise<OutputOf<TProcedures[K]>>;
2691
+ } : TProcedures[K] extends {
2692
+ procedureType: "mutation";
2693
+ } ? {
2694
+ mutate: (input: InputOf<TProcedures[K]>, serverCtx: TContext) => Promise<OutputOf<TProcedures[K]>>;
2695
+ runMutation: (input: InputOf<TProcedures[K]>, ctx: ParentCtx<TContext>) => Promise<OutputOf<TProcedures[K]>>;
2696
+ } : TProcedures[K] extends {
2697
+ procedureType: "action";
2698
+ } ? {
2699
+ action: (input: InputOf<TProcedures[K]>, serverCtx: TContext) => Promise<OutputOf<TProcedures[K]>>;
2700
+ } : TProcedures[K] extends {
2701
+ procedureType: "job";
2702
+ } ? {
2703
+ run: (input: InputOf<TProcedures[K]>, serverCtx: TContext) => Promise<OutputOf<TProcedures[K]>>;
2704
+ } : never } & {
2705
+ init: (config: CallerClientInitConfig<TContext>) => Promise<void>;
2706
+ };
2707
+ type CallerClientInitConfig<TContext = unknown> = {
2708
+ db: SupaliveDb;
2709
+ storage: ObjectStorage;
2710
+ scheduler: JobClient;
2711
+ /**
2712
+ * Observability so standalone internal calls log under the configured logger
2713
+ * (a per-procedure child). Required — a caller with no running server (e.g. a
2714
+ * CLI seed) passes a console-backed shim. Nested runQuery/runMutation always
2715
+ * inherit the parent handler's log instead.
2716
+ */
2717
+ obs: CallerObservability;
2718
+ /**
2719
+ * Shared query cache (Redis-backed) — REQUIRED. Every caller read path
2720
+ * (`query`, `runQuery`, `liveQuery`) goes cache-first with the SAME
2721
+ * identity/freshness model as a client one-shot (see runCachedQuery /
2722
+ * runCachedQueryInParent) and writes recomputed results back.
2723
+ */
2724
+ cache: CacheLayer;
2725
+ /**
2726
+ * Oldest retained commit-log ts (retention watermark) — the guard for the
2727
+ * cache-freshness scan. The running server passes the live sub-manager
2728
+ * watermark; a CLI seed (no server, nothing pruned) passes `() => 0n`.
2729
+ */
2730
+ getMinRetainedTs: () => bigint;
2731
+ /**
2732
+ * Same identity resolver as the server (`SupaliveServerConfig.getUserId`), so
2733
+ * internal cache keys segment identically to client WS/HTTP queries — a
2734
+ * nested/standalone call shares cache entries with the equivalent client
2735
+ * query. Return `null` for anonymous (the cache's anonymous identity
2736
+ * segment).
2737
+ */
2738
+ getUserId: (ctx: TContext | undefined) => string | null;
2739
+ };
2740
+ /**
2741
+ * Caller type from router.
2742
+ */
2743
+ type CallerFromRouter<TRouter extends Router<any, any>> = CallerFromProcedures<TRouter["procedures"]>;
2744
+ //#endregion
2745
+ export { ActionProcedures as $, schemaRegistry as $n, Predicate as $r, ResultOf as $t, ResponseMessage as A, ComputedCodec as An, UpdateSubscriptionReadSetParamsSchema as Ar, QueryFn as At, SubscriptionUpdateMessage as B, MutationArgs as Bn, CompareOperatorSchema as Br, IncomingJobRequest as Bt, ClientMessage as C, normalizeToBytes as Ci, buildPredicateSql as Cn, RegisterSubscriptionResult as Cr, MutationConfig as Ct, Context$1 as D, matchesPredicate as Dn, UnregisterSubscriptionsParams as Dr, OutputOf as Dt, ConnectedMessageSchema as E, jsonPathExtract as En, UnregisterSubscriptionResult as Er, MutationProcedure as Et, ServerPingMessageSchema as F, DefineSchemaConfig as Fn, CachedPgMetadata as Fr, createMutationBuilder as Ft, ClientLogLevel as G, ReturnQuery as Gn, MutationResult as Gr, ScheduleHandle as Gt, UnsubscribeMessage as H, Prettify as Hn, LeafPredicate as Hr, JobScheduler as Ht, SubscribeMessage as I, IndexDefinition as In, CachedPgMetadataSchema as Ir, createQueryBuilder as It, HeartbeatOptions as J, SchemaColumnsOptions as Jn, OccConflictError as Jr, sleep as Jt, ClientLogger as K, SchemaCodecs as Kn, NO_RETRY as Kr, ScheduledJobDef as Kt, SubscribeMessageSchema as L, InferSchema as Ln, CommitLogEntry as Lr, DevScheduler as Lt, ServerMessage as M, ComputedField as Mn, AndPredicate as Mr, TypeOf as Mt, ServerMessageSchema as N, ComputedFieldConfig as Nn, AndPredicateSchema as Nr, createActionBuilder as Nt, ErrorMessage as O, ActionArgs as On, UnregisterSubscriptionsResult as Or, QueryConfig as Ot, ServerPingMessage as P, DeclarativeIndex as Pn, BigIntSchema as Pr, createJobBuilder as Pt, createConsoleLogger as Q, defineSchema as Qn, PointReadSchema as Qr, QueryDefinition as Qt, SubscriptionGoneMessage as R, InsertData as Rn, CommitTs as Rr, DevSchedulerOptions as Rt, CallMessageSchema as S, normalizeIdToBytes as Si, QueryBuilder as Sn, RegisterSubscriptionParamsSchema as Sr, JobProcedure as St, ConnectedMessage as T, jsonPathExists as Tn, UnregisterSubscriptionParamsSchema as Tr, MutationFn as Tt, UnsubscribeMessageSchema as U, QueryArgs as Un, LeafPredicateSchema as Ur, QStashScheduler as Ut, SubscriptionUpdateMessageSchema as V, OverrideField as Vn, DEFAULT_RETRY as Vr, JobClient as Vt, ClientEvent as W, ReturnLiveQuery as Wn, LiveResult as Wr, QStashSchedulerOptions as Wt, WSClientOptions as X, StoredSchemaColumnsOptions as Xn, OrPredicateSchema as Xr, DefsToMap as Xt, RPCError as Y, SchemaDefinition as Yn, OrPredicate as Yr, AnyQueryDef as Yt, WsClientManager as Z, defineComputedField as Zn, PointRead as Zr, ParamsOf as Zt, AuthMessageSchema as _, WriteEntry as _i, JsonHasKeyMultiOptions as _n, InvalidateEphemeralResult as _r, HandlerLogger as _t, createCaller as a, QuerySpec as ai, EMPTY_EPHEMERAL_KEYS as an, DbType as ar, PublicProcedures as at, AuthenticationError as b, WriteOpSchema as bi, OrderDirection as bn, InvalidateWritesetResult as br, JobCtx as bt, ClientOptions as c, RawPointRead as ci, EphemeralFlushError as cn, PreparedQueries as cr, Router as ct, LiveQueryStatus as d, RawRangeReadSchema as di, EphemeralSetOptions as dn, TxDatabase as dr, ActionConfig as dt, PredicateSchema as ei, _resetGlobalDefs as en, shouldTrackSchema as er, AppRouter as et, WSClientMethods as f, RawReadEntry as fi, EphemeralStore as fn, CacheLayer as fr, ActionCtx as ft, AuthMessage as g, RetryConfig as gi, JsonContainsOptions as gn, InvalidateEphemeralParamsSchema as gr, ContextOf as gt, AuthFailedMessageSchema as h, ReadEntrySchema as hi, IdAndCommitTs as hn, InvalidateEphemeralParams as hr, AnyProcedure as ht, CallerOptions as i, QueryCacheMetadataSchema as ii, TxContext as in, DbQueryResult as ir, ProcedureNames as it, ResponseMessageSchema as j, ComputedDataType as jn, UpdateSubscriptionReadSetResult as jr, QueryProcedure as jt, IsInternal as k, ColumnCodec as kn, UpdateSubscriptionReadSetParams as kr, QueryCtx as kt, LiveQueryHandle as l, RawPointReadSchema as li, EphemeralKeyNotFoundError as ln, RawClient as lr, getContextRegistry as lt, AuthFailedMessage as m, ReadEntry as mi, createMemoryEphemeralStore as mn, AffectedSubscription as mr, ActionProcedure as mt, CallerFromProcedures as n, QueryCacheEntrySchema as ni, DbReader as nn, CoreInitLockCtx as nr, JobProcedures as nt, CallOptions as o, RangeRead as oi, EPHEMERAL_DEFAULT_TTL_SECONDS as on, LazyCommitTsParam as or, QueryProcedures as ot, createClient as p, RawReadEntrySchema as pi, EphemeralWriter as pn, IORedisClient as pr, ActionFn as pt, ClientPublicState as q, SchemaColumnMapping as qn, OccAbortError as qr, SupaliveDb as qt, CallerFromRouter as r, QueryCacheMetadata as ri, DbWriter as rn, Database as rr, MutationProcedures as rt, ClientFromProcedures as s, RangeReadSchema as si, EphemeralDecodeError as sn, PooledClient as sr, RegisteredProcedure as st, CallerClientInitConfig as t, QueryCacheEntry as ti, defineQuery as tn, tableNameToId as tr, ContextRegistry as tt, LiveQueryState as u, RawRangeRead as ui, EphemeralReader as un, SqlBuilder as ur, router as ut, AuthSuccessMessage as v, WriteEntrySchema as vi, JsonOpOptions as vn, InvalidateWritesetParams as vr, HandlerObservability as vt, ClientMessageSchema as w, jsonContains as wn, UnregisterSubscriptionParams as wr, MutationCtx as wt, CallMessage as x, bytesFromJson as xi, PaginationClause as xn, RegisterSubscriptionParams as xr, JobFn as xt, AuthSuccessMessageSchema as y, WriteOp as yi, OrderByOptions as yn, InvalidateWritesetParamsSchema as yr, JobConfig as yt, SubscriptionGoneMessageSchema as z, Model as zn, CompareOperator as zr, EnqueueOptions as zt };
2746
+ //# sourceMappingURL=index-DB7_pv9P.d.ts.map