deepspace 0.0.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3173 @@
1
+ import * as Y from 'yjs';
2
+ import * as _ai_sdk_openai_compatible from '@ai-sdk/openai-compatible';
3
+ import * as _ai_sdk_openai from '@ai-sdk/openai';
4
+ import * as _ai_sdk_anthropic from '@ai-sdk/anthropic';
5
+ import * as better_auth from 'better-auth';
6
+ import * as better_auth_plugins from 'better-auth/plugins';
7
+
8
+ /**
9
+ * Wire protocol constants.
10
+ *
11
+ * The JSON WebSocket protocol uses dotted string identifiers (e.g.
12
+ * `"game.input"`) as the `type` discriminator. All message types are
13
+ * grouped under a single `MSG` object so imports stay tidy:
14
+ *
15
+ * import { MSG, dispatch, clientBuild } from 'deepspace'
16
+ *
17
+ * dispatch<ServerMessage>(raw, {
18
+ * [MSG.GAME_STATE]: (p) => { ... },
19
+ * [MSG.GAME_TICK]: (p) => { ... },
20
+ * })
21
+ *
22
+ * Each key's value is the on-wire string — grep-friendly, self-
23
+ * documenting, and infinite per namespace. Adding a new message is one
24
+ * line here plus one arm in the discriminated union in `./messages.ts`.
25
+ *
26
+ * Yjs binary protocol constants (`MSG_YJS_SYNC`, `MSG_YJS_AWARENESS`) are
27
+ * intentionally kept numeric and separate from `MSG` — they ride a
28
+ * binary WebSocket frame format and aren't part of the JSON dispatcher.
29
+ */
30
+ declare const MSG: {
31
+ readonly SUBSCRIBE: "core.subscribe";
32
+ readonly UNSUBSCRIBE: "core.unsubscribe";
33
+ readonly QUERY_RESULT: "core.query_result";
34
+ readonly RECORD_CHANGE: "core.record_change";
35
+ readonly PUT: "core.put";
36
+ readonly DELETE: "core.delete";
37
+ readonly ERROR: "core.error";
38
+ readonly USER_INFO: "user.info";
39
+ readonly USER_LIST: "user.list";
40
+ readonly SET_ROLE: "user.set_role";
41
+ readonly USER_UPDATE: "user.update";
42
+ readonly YJS_JOIN: "yjs.join";
43
+ readonly YJS_LEAVE: "yjs.leave";
44
+ readonly ACK: "records.ack";
45
+ readonly LIST_SCHEMAS: "records.list_schemas";
46
+ readonly RESUBSCRIBE: "records.resubscribe";
47
+ readonly GAME_STATE: "game.state";
48
+ readonly GAME_INPUT: "game.input";
49
+ readonly GAME_PLAYER_JOIN: "game.player_join";
50
+ readonly GAME_PLAYER_LEAVE: "game.player_leave";
51
+ readonly GAME_PLAYER_READY: "game.player_ready";
52
+ readonly GAME_START: "game.start";
53
+ readonly GAME_END: "game.end";
54
+ readonly GAME_TICK: "game.tick";
55
+ readonly CANVAS_SHAPES: "canvas.shapes";
56
+ readonly CANVAS_ADD: "canvas.add";
57
+ readonly CANVAS_MOVE: "canvas.move";
58
+ readonly CANVAS_RESIZE: "canvas.resize";
59
+ readonly CANVAS_DELETE: "canvas.delete";
60
+ readonly CANVAS_UPDATE: "canvas.update";
61
+ readonly CANVAS_VIEWPORT: "canvas.viewport";
62
+ readonly CANVAS_UNDO: "canvas.undo";
63
+ readonly CANVAS_REDO: "canvas.redo";
64
+ readonly CRON_TASKS: "cron.tasks";
65
+ readonly CRON_HISTORY: "cron.history";
66
+ readonly CRON_TRIGGER: "cron.trigger";
67
+ readonly CRON_PAUSE: "cron.pause";
68
+ readonly CRON_RESUME: "cron.resume";
69
+ readonly CRON_STATUS: "cron.status";
70
+ readonly PRESENCE_SYNC: "presence.sync";
71
+ readonly PRESENCE_JOIN: "presence.join";
72
+ readonly PRESENCE_LEAVE: "presence.leave";
73
+ readonly PRESENCE_UPDATE: "presence.update";
74
+ readonly GW_SCOPE_CONNECT: "gateway.scope_connect";
75
+ readonly GW_SCOPE_DISCONNECT: "gateway.scope_disconnect";
76
+ readonly GW_SCOPE_ERROR: "gateway.scope_error";
77
+ readonly GW_TOKEN_REFRESH: "gateway.token_refresh";
78
+ readonly GW_USER_UPDATE: "gateway.user_update";
79
+ };
80
+ /**
81
+ * Type of any message type constant — the union of every string literal
82
+ * stored in `MSG`. Useful when declaring functions that accept "any known
83
+ * message type" without enumerating all 54 strings by hand.
84
+ */
85
+ type MsgType = (typeof MSG)[keyof typeof MSG];
86
+ /** Outer envelope id for binary-framed yjs sync messages. Varuint-encoded. */
87
+ declare const MSG_YJS_SYNC = 22;
88
+ /** Outer envelope id for binary-framed yjs awareness messages. Varuint-encoded. */
89
+ declare const MSG_YJS_AWARENESS = 23;
90
+ /** Role assigned to unauthenticated WebSocket connections */
91
+ declare const ROLE_ANONYMOUS = "viewer";
92
+ /** Default role for newly registered authenticated users */
93
+ declare const ROLE_DEFAULT = "member";
94
+ /** Admin role */
95
+ declare const ROLE_ADMIN = "admin";
96
+
97
+ /**
98
+ * Typed wire-protocol layer — discriminated unions, typed builders, and a
99
+ * type-safe dispatcher for every `MSG.*` the SDK understands.
100
+ *
101
+ * Why this exists
102
+ * ---------------
103
+ *
104
+ * The string `MSG.*` constants in `./constants.ts` are the authoritative
105
+ * wire protocol, but using them directly is error-prone: a typo picks the
106
+ * wrong message with the wrong payload shape and fails silently at
107
+ * runtime. This module pairs every constant with its payload type, so
108
+ * that:
109
+ *
110
+ * 1. Building a message with `clientBuild.gameInput(...)` is payload-
111
+ * checked at the call site — the compiler refuses to ship a wrong
112
+ * shape.
113
+ *
114
+ * 2. Parsing an inbound message via `dispatch(raw, handlers)` narrows the
115
+ * payload type inside each handler automatically, replacing the
116
+ * unsafe `switch (msg.type) { case MSG.X: (payload as any).foo }`
117
+ * pattern.
118
+ *
119
+ * 3. Tightening `BaseRoom.sendTo` / `BaseRoom.broadcast` /
120
+ * `HandlerContext.send` / `SubscriptionContext.send` to accept
121
+ * `ServerMessage` turns the type layer into enforcement: any room
122
+ * that ships a payload inconsistent with its declared arm fails to
123
+ * compile. Without that, the discriminated union is documentation,
124
+ * not contract.
125
+ *
126
+ * 4. Adding a new `MSG.*` is localized: one entry in the discriminated
127
+ * union, one builder function, one handler key in every dispatcher
128
+ * that cares. No grep-and-fix across the codebase.
129
+ *
130
+ * 5. Apps can extend the SDK protocol without forking: `dispatch<M>` is
131
+ * generic over any `M extends ProtocolMessage`, and builders are
132
+ * plain objects so apps compose via spread (`{ ...clientBuild,
133
+ * myMessage: ... }`).
134
+ *
135
+ * Direction split
136
+ * ---------------
137
+ *
138
+ * Some message types carry different payloads depending on who's sending.
139
+ * `MSG.GAME_START`, for example, is `{}` when the client requests a start
140
+ * but `{ state, tick }` when the server broadcasts the start event.
141
+ * `MSG.CANVAS_ADD` is a flat shape dict on the way in and a `{ shape }`
142
+ * wrapper on the way out. Modelling these with one union would force
143
+ * handlers to juggle a union payload — clunky and error-prone. Instead we
144
+ * split by direction:
145
+ *
146
+ * - `ClientMessage` — what the client sends to the server
147
+ * - `ServerMessage` — what the server sends to the client
148
+ * - `ProtocolMessage = ClientMessage | ServerMessage` (for code that
149
+ * really doesn't care — avoid when possible)
150
+ *
151
+ * Each side gets its own builder (`clientBuild` / `serverBuild`) and each
152
+ * side's dispatcher is parameterised with the union it expects.
153
+ *
154
+ * Payload strictness
155
+ * ------------------
156
+ *
157
+ * Where payload shapes are stable + narrow (ids, flags), we type them
158
+ * precisely. Where they're opaque or escape the protocol layer (record
159
+ * data blobs, Yjs binary frames, game-engine state), we use `unknown` and
160
+ * defer narrowing to the caller. This is intentional: over-typing opaque
161
+ * payloads would require the protocol layer to import application types
162
+ * and defeat the "thin wire contract" goal.
163
+ */
164
+
165
+ /**
166
+ * The outer shape of every wire message. `T` is the string discriminator
167
+ * (e.g. `"game.input"`) — keeping it as a generic literal type lets the
168
+ * discriminated-union narrowing in `dispatch()` pick the right payload.
169
+ *
170
+ * Callers extending the protocol should pass a string-literal type for
171
+ * `T`, not the widened `string`. `BaseMessage<string, P>` collapses the
172
+ * discriminated union and handler-map key inference falls back to a
173
+ * single untyped `string` key, losing all narrowing.
174
+ */
175
+ interface BaseMessage<T extends string, P> {
176
+ type: T;
177
+ payload: P;
178
+ }
179
+ /** Matches when a payload is intentionally empty — `{}` on the wire. */
180
+ type EmptyPayload = Record<string, never>;
181
+ /**
182
+ * Every message the client can legitimately send. Extend in app code with
183
+ * a string-literal union arm:
184
+ *
185
+ * type MyClientMessage = ClientMessage | BaseMessage<'myapp.foo', { x: number }>
186
+ */
187
+ type ClientMessage = BaseMessage<typeof MSG.SUBSCRIBE, {
188
+ subscriptionId: string;
189
+ query: unknown;
190
+ }> | BaseMessage<typeof MSG.UNSUBSCRIBE, {
191
+ subscriptionId: string;
192
+ }> | BaseMessage<typeof MSG.PUT, {
193
+ collection: string;
194
+ recordId: string;
195
+ data: Record<string, unknown>;
196
+ requestId?: string;
197
+ }> | BaseMessage<typeof MSG.DELETE, {
198
+ collection: string;
199
+ recordId: string;
200
+ requestId?: string;
201
+ }> | BaseMessage<typeof MSG.USER_LIST, EmptyPayload> | BaseMessage<typeof MSG.SET_ROLE, {
202
+ userId: string;
203
+ role: string;
204
+ }> | BaseMessage<typeof MSG.USER_UPDATE, EmptyPayload> | BaseMessage<typeof MSG.LIST_SCHEMAS, EmptyPayload> | BaseMessage<typeof MSG.YJS_JOIN, {
205
+ collection: string;
206
+ recordId: string;
207
+ fieldName: string;
208
+ }> | BaseMessage<typeof MSG.YJS_LEAVE, {
209
+ collection: string;
210
+ recordId: string;
211
+ fieldName: string;
212
+ }> | BaseMessage<typeof MSG.GAME_INPUT, {
213
+ action: string;
214
+ data: Record<string, unknown>;
215
+ }> | BaseMessage<typeof MSG.GAME_PLAYER_READY, EmptyPayload> | BaseMessage<typeof MSG.GAME_START, EmptyPayload> | BaseMessage<typeof MSG.GAME_END, EmptyPayload> | BaseMessage<typeof MSG.CANVAS_ADD, unknown> | BaseMessage<typeof MSG.CANVAS_MOVE, {
216
+ shapeId: string;
217
+ x: number;
218
+ y: number;
219
+ }> | BaseMessage<typeof MSG.CANVAS_RESIZE, {
220
+ shapeId: string;
221
+ width: number;
222
+ height: number;
223
+ x?: number;
224
+ y?: number;
225
+ }> | BaseMessage<typeof MSG.CANVAS_DELETE, {
226
+ shapeId: string;
227
+ }> | BaseMessage<typeof MSG.CANVAS_UPDATE, {
228
+ shapeId: string;
229
+ props: Record<string, unknown>;
230
+ }> | BaseMessage<typeof MSG.CANVAS_VIEWPORT, unknown> | BaseMessage<typeof MSG.CANVAS_UNDO, EmptyPayload> | BaseMessage<typeof MSG.CANVAS_REDO, EmptyPayload> | BaseMessage<typeof MSG.CRON_TRIGGER, {
231
+ taskName: string;
232
+ }> | BaseMessage<typeof MSG.CRON_PAUSE, {
233
+ taskName: string;
234
+ }> | BaseMessage<typeof MSG.CRON_RESUME, {
235
+ taskName: string;
236
+ }> | BaseMessage<typeof MSG.PRESENCE_UPDATE, Record<string, unknown>> | BaseMessage<typeof MSG.GW_SCOPE_CONNECT, {
237
+ scopeType: string;
238
+ scopeId: string;
239
+ }> | BaseMessage<typeof MSG.GW_SCOPE_DISCONNECT, {
240
+ scopeType: string;
241
+ scopeId: string;
242
+ }> | BaseMessage<typeof MSG.GW_TOKEN_REFRESH, {
243
+ token: string;
244
+ }>;
245
+ /**
246
+ * Every message the server can send. Room and handler `send` / `broadcast`
247
+ * signatures are tightened to this union so outbound payloads are
248
+ * compile-checked against the wire contract. As with `ClientMessage`,
249
+ * extend via a string-literal union arm in app code when adding new
250
+ * server-side broadcasts.
251
+ */
252
+ type ServerMessage = BaseMessage<typeof MSG.QUERY_RESULT, {
253
+ subscriptionId: string;
254
+ records: unknown[];
255
+ }> | BaseMessage<typeof MSG.RECORD_CHANGE, {
256
+ collection: string;
257
+ record: unknown;
258
+ changeType: 'create' | 'update' | 'delete';
259
+ }> | BaseMessage<typeof MSG.ERROR, {
260
+ error: string;
261
+ subscriptionId?: string;
262
+ }> | BaseMessage<typeof MSG.ACK, {
263
+ requestId: string;
264
+ success: true;
265
+ recordId?: string;
266
+ } | {
267
+ requestId: string;
268
+ success: false;
269
+ error: string;
270
+ }> | BaseMessage<typeof MSG.RESUBSCRIBE, EmptyPayload> | BaseMessage<typeof MSG.LIST_SCHEMAS, {
271
+ schemas: unknown;
272
+ }> | BaseMessage<typeof MSG.USER_INFO, unknown> | BaseMessage<typeof MSG.USER_LIST, {
273
+ users: unknown[];
274
+ }> | BaseMessage<typeof MSG.YJS_JOIN, {
275
+ collection: string;
276
+ recordId: string;
277
+ fieldName: string;
278
+ canWrite: boolean;
279
+ }> | BaseMessage<typeof MSG.GAME_STATE, {
280
+ state: unknown;
281
+ tick: number;
282
+ players: unknown[];
283
+ running: boolean;
284
+ }> | BaseMessage<typeof MSG.GAME_TICK, {
285
+ state: unknown;
286
+ tick: number;
287
+ }> | BaseMessage<typeof MSG.GAME_START, {
288
+ state: unknown;
289
+ tick: number;
290
+ }> | BaseMessage<typeof MSG.GAME_END, {
291
+ state: unknown;
292
+ tick: number;
293
+ }> | BaseMessage<typeof MSG.GAME_PLAYER_JOIN, {
294
+ player: unknown;
295
+ }> | BaseMessage<typeof MSG.GAME_PLAYER_LEAVE, {
296
+ userId: string;
297
+ }> | BaseMessage<typeof MSG.GAME_PLAYER_READY, {
298
+ userId: string;
299
+ }> | BaseMessage<typeof MSG.CANVAS_SHAPES, {
300
+ shapes: unknown[];
301
+ viewports: unknown[];
302
+ }> | BaseMessage<typeof MSG.CANVAS_ADD, {
303
+ shape: unknown;
304
+ }> | BaseMessage<typeof MSG.CANVAS_MOVE, {
305
+ shapeId: string;
306
+ x: number;
307
+ y: number;
308
+ }> | BaseMessage<typeof MSG.CANVAS_RESIZE, {
309
+ shapeId: string;
310
+ width: number;
311
+ height: number;
312
+ x?: number;
313
+ y?: number;
314
+ }> | BaseMessage<typeof MSG.CANVAS_DELETE, {
315
+ shapeId: string;
316
+ }> | BaseMessage<typeof MSG.CANVAS_UPDATE, {
317
+ shapeId: string;
318
+ props: Record<string, unknown>;
319
+ }> | BaseMessage<typeof MSG.CANVAS_VIEWPORT, {
320
+ viewport: unknown;
321
+ } | {
322
+ userId: string;
323
+ removed: true;
324
+ }> | BaseMessage<typeof MSG.CRON_TASKS, {
325
+ tasks: unknown;
326
+ }> | BaseMessage<typeof MSG.CRON_HISTORY, {
327
+ history: unknown;
328
+ }> | BaseMessage<typeof MSG.CRON_STATUS, {
329
+ tasks: unknown;
330
+ recentHistory: unknown;
331
+ }> | BaseMessage<typeof MSG.PRESENCE_SYNC, {
332
+ peers: unknown[];
333
+ }> | BaseMessage<typeof MSG.PRESENCE_JOIN, {
334
+ peer: unknown;
335
+ }> | BaseMessage<typeof MSG.PRESENCE_LEAVE, {
336
+ userId: string;
337
+ }> | BaseMessage<typeof MSG.PRESENCE_UPDATE, {
338
+ userId: string;
339
+ state: Record<string, unknown>;
340
+ }> | BaseMessage<typeof MSG.GW_SCOPE_ERROR, {
341
+ scopeType: string;
342
+ scopeId: string;
343
+ error: string;
344
+ }> | BaseMessage<typeof MSG.GW_USER_UPDATE, unknown>;
345
+ /**
346
+ * Any wire message, regardless of direction. Use this only when the code
347
+ * path genuinely doesn't care — otherwise pick `ClientMessage` or
348
+ * `ServerMessage` so the handler's payload type is narrower.
349
+ */
350
+ type ProtocolMessage = ClientMessage | ServerMessage;
351
+ /** Factories for every client → server message. */
352
+ declare const clientBuild: {
353
+ subscribe: (subscriptionId: string, query: unknown) => {
354
+ readonly type: "core.subscribe";
355
+ readonly payload: {
356
+ readonly subscriptionId: string;
357
+ readonly query: unknown;
358
+ };
359
+ };
360
+ unsubscribe: (subscriptionId: string) => {
361
+ readonly type: "core.unsubscribe";
362
+ readonly payload: {
363
+ readonly subscriptionId: string;
364
+ };
365
+ };
366
+ put: (collection: string, recordId: string, data: Record<string, unknown>, requestId?: string) => {
367
+ readonly type: "core.put";
368
+ readonly payload: {
369
+ readonly collection: string;
370
+ readonly recordId: string;
371
+ readonly data: Record<string, unknown>;
372
+ readonly requestId: string | undefined;
373
+ };
374
+ };
375
+ remove: (collection: string, recordId: string, requestId?: string) => {
376
+ readonly type: "core.delete";
377
+ readonly payload: {
378
+ readonly collection: string;
379
+ readonly recordId: string;
380
+ readonly requestId: string | undefined;
381
+ };
382
+ };
383
+ userList: () => {
384
+ readonly type: "user.list";
385
+ readonly payload: EmptyPayload;
386
+ };
387
+ setRole: (userId: string, role: string) => {
388
+ readonly type: "user.set_role";
389
+ readonly payload: {
390
+ readonly userId: string;
391
+ readonly role: string;
392
+ };
393
+ };
394
+ userUpdate: () => {
395
+ readonly type: "user.update";
396
+ readonly payload: EmptyPayload;
397
+ };
398
+ listSchemas: () => {
399
+ readonly type: "records.list_schemas";
400
+ readonly payload: EmptyPayload;
401
+ };
402
+ yjsJoin: (collection: string, recordId: string, fieldName: string) => {
403
+ readonly type: "yjs.join";
404
+ readonly payload: {
405
+ readonly collection: string;
406
+ readonly recordId: string;
407
+ readonly fieldName: string;
408
+ };
409
+ };
410
+ yjsLeave: (collection: string, recordId: string, fieldName: string) => {
411
+ readonly type: "yjs.leave";
412
+ readonly payload: {
413
+ readonly collection: string;
414
+ readonly recordId: string;
415
+ readonly fieldName: string;
416
+ };
417
+ };
418
+ gameInput: (action: string, data?: Record<string, unknown>) => {
419
+ readonly type: "game.input";
420
+ readonly payload: {
421
+ readonly action: string;
422
+ readonly data: Record<string, unknown>;
423
+ };
424
+ };
425
+ gamePlayerReady: () => {
426
+ readonly type: "game.player_ready";
427
+ readonly payload: EmptyPayload;
428
+ };
429
+ gameStart: () => {
430
+ readonly type: "game.start";
431
+ readonly payload: EmptyPayload;
432
+ };
433
+ gameEnd: () => {
434
+ readonly type: "game.end";
435
+ readonly payload: EmptyPayload;
436
+ };
437
+ canvasAdd: (shape: Record<string, unknown>) => {
438
+ readonly type: "canvas.add";
439
+ readonly payload: Record<string, unknown>;
440
+ };
441
+ canvasMove: (shapeId: string, x: number, y: number) => {
442
+ readonly type: "canvas.move";
443
+ readonly payload: {
444
+ readonly shapeId: string;
445
+ readonly x: number;
446
+ readonly y: number;
447
+ };
448
+ };
449
+ canvasResize: (shapeId: string, width: number, height: number, x?: number, y?: number) => {
450
+ readonly type: "canvas.resize";
451
+ readonly payload: {
452
+ readonly shapeId: string;
453
+ readonly width: number;
454
+ readonly height: number;
455
+ readonly x: number | undefined;
456
+ readonly y: number | undefined;
457
+ };
458
+ };
459
+ canvasDelete: (shapeId: string) => {
460
+ readonly type: "canvas.delete";
461
+ readonly payload: {
462
+ readonly shapeId: string;
463
+ };
464
+ };
465
+ canvasUpdate: (shapeId: string, props: Record<string, unknown>) => {
466
+ readonly type: "canvas.update";
467
+ readonly payload: {
468
+ readonly shapeId: string;
469
+ readonly props: Record<string, unknown>;
470
+ };
471
+ };
472
+ canvasViewport: (viewport: Record<string, unknown>) => {
473
+ readonly type: "canvas.viewport";
474
+ readonly payload: Record<string, unknown>;
475
+ };
476
+ canvasUndo: () => {
477
+ readonly type: "canvas.undo";
478
+ readonly payload: EmptyPayload;
479
+ };
480
+ canvasRedo: () => {
481
+ readonly type: "canvas.redo";
482
+ readonly payload: EmptyPayload;
483
+ };
484
+ cronTrigger: (taskName: string) => {
485
+ readonly type: "cron.trigger";
486
+ readonly payload: {
487
+ readonly taskName: string;
488
+ };
489
+ };
490
+ cronPause: (taskName: string) => {
491
+ readonly type: "cron.pause";
492
+ readonly payload: {
493
+ readonly taskName: string;
494
+ };
495
+ };
496
+ cronResume: (taskName: string) => {
497
+ readonly type: "cron.resume";
498
+ readonly payload: {
499
+ readonly taskName: string;
500
+ };
501
+ };
502
+ presenceUpdate: (state: Record<string, unknown>) => {
503
+ readonly type: "presence.update";
504
+ readonly payload: Record<string, unknown>;
505
+ };
506
+ gwScopeConnect: (scopeType: string, scopeId: string) => {
507
+ readonly type: "gateway.scope_connect";
508
+ readonly payload: {
509
+ readonly scopeType: string;
510
+ readonly scopeId: string;
511
+ };
512
+ };
513
+ gwScopeDisconnect: (scopeType: string, scopeId: string) => {
514
+ readonly type: "gateway.scope_disconnect";
515
+ readonly payload: {
516
+ readonly scopeType: string;
517
+ readonly scopeId: string;
518
+ };
519
+ };
520
+ gwTokenRefresh: (token: string) => {
521
+ readonly type: "gateway.token_refresh";
522
+ readonly payload: {
523
+ readonly token: string;
524
+ };
525
+ };
526
+ };
527
+ /** Factories for every server → client message. Used by room DOs and
528
+ * handler modules so server broadcasts are payload-checked too. Opaque
529
+ * slots (`record`, `shape`, `peer`, etc.) take `unknown` so concrete
530
+ * domain types — `RecordResult`, `CanvasShape`, `MediaPeer` — can be
531
+ * passed without casts. */
532
+ declare const serverBuild: {
533
+ queryResult: (subscriptionId: string, records: unknown[]) => {
534
+ readonly type: "core.query_result";
535
+ readonly payload: {
536
+ readonly subscriptionId: string;
537
+ readonly records: unknown[];
538
+ };
539
+ };
540
+ recordChange: (collection: string, record: unknown, changeType: "create" | "update" | "delete") => {
541
+ readonly type: "core.record_change";
542
+ readonly payload: {
543
+ readonly collection: string;
544
+ readonly record: unknown;
545
+ readonly changeType: "create" | "update" | "delete";
546
+ };
547
+ };
548
+ error: (error: string, subscriptionId?: string) => {
549
+ readonly type: "core.error";
550
+ readonly payload: {
551
+ readonly error: string;
552
+ readonly subscriptionId: string | undefined;
553
+ };
554
+ };
555
+ ackSuccess: (requestId: string, recordId?: string) => {
556
+ readonly type: "records.ack";
557
+ readonly payload: {
558
+ readonly requestId: string;
559
+ readonly success: true;
560
+ readonly recordId: string | undefined;
561
+ };
562
+ };
563
+ ackFailure: (requestId: string, error: string) => {
564
+ readonly type: "records.ack";
565
+ readonly payload: {
566
+ readonly requestId: string;
567
+ readonly success: false;
568
+ readonly error: string;
569
+ };
570
+ };
571
+ resubscribe: () => {
572
+ readonly type: "records.resubscribe";
573
+ readonly payload: EmptyPayload;
574
+ };
575
+ schemas: (schemas: unknown) => {
576
+ readonly type: "records.list_schemas";
577
+ readonly payload: {
578
+ readonly schemas: unknown;
579
+ };
580
+ };
581
+ userInfo: (user: unknown) => {
582
+ readonly type: "user.info";
583
+ readonly payload: unknown;
584
+ };
585
+ userList: (users: unknown[]) => {
586
+ readonly type: "user.list";
587
+ readonly payload: {
588
+ readonly users: unknown[];
589
+ };
590
+ };
591
+ yjsJoin: (collection: string, recordId: string, fieldName: string, canWrite: boolean) => {
592
+ readonly type: "yjs.join";
593
+ readonly payload: {
594
+ readonly collection: string;
595
+ readonly recordId: string;
596
+ readonly fieldName: string;
597
+ readonly canWrite: boolean;
598
+ };
599
+ };
600
+ gameState: (state: unknown, tick: number, players: unknown[], running: boolean) => {
601
+ readonly type: "game.state";
602
+ readonly payload: {
603
+ readonly state: unknown;
604
+ readonly tick: number;
605
+ readonly players: unknown[];
606
+ readonly running: boolean;
607
+ };
608
+ };
609
+ gameTick: (state: unknown, tick: number) => {
610
+ readonly type: "game.tick";
611
+ readonly payload: {
612
+ readonly state: unknown;
613
+ readonly tick: number;
614
+ };
615
+ };
616
+ gameStart: (state: unknown, tick: number) => {
617
+ readonly type: "game.start";
618
+ readonly payload: {
619
+ readonly state: unknown;
620
+ readonly tick: number;
621
+ };
622
+ };
623
+ gameEnd: (state: unknown, tick: number) => {
624
+ readonly type: "game.end";
625
+ readonly payload: {
626
+ readonly state: unknown;
627
+ readonly tick: number;
628
+ };
629
+ };
630
+ gamePlayerJoin: (player: unknown) => {
631
+ readonly type: "game.player_join";
632
+ readonly payload: {
633
+ readonly player: unknown;
634
+ };
635
+ };
636
+ gamePlayerLeave: (userId: string) => {
637
+ readonly type: "game.player_leave";
638
+ readonly payload: {
639
+ readonly userId: string;
640
+ };
641
+ };
642
+ gamePlayerReady: (userId: string) => {
643
+ readonly type: "game.player_ready";
644
+ readonly payload: {
645
+ readonly userId: string;
646
+ };
647
+ };
648
+ canvasShapes: (shapes: unknown[], viewports: unknown[]) => {
649
+ readonly type: "canvas.shapes";
650
+ readonly payload: {
651
+ readonly shapes: unknown[];
652
+ readonly viewports: unknown[];
653
+ };
654
+ };
655
+ canvasAdd: (shape: unknown) => {
656
+ readonly type: "canvas.add";
657
+ readonly payload: {
658
+ readonly shape: unknown;
659
+ };
660
+ };
661
+ canvasMove: (shapeId: string, x: number, y: number) => {
662
+ readonly type: "canvas.move";
663
+ readonly payload: {
664
+ readonly shapeId: string;
665
+ readonly x: number;
666
+ readonly y: number;
667
+ };
668
+ };
669
+ canvasResize: (shapeId: string, width: number, height: number, x?: number, y?: number) => {
670
+ readonly type: "canvas.resize";
671
+ readonly payload: {
672
+ readonly shapeId: string;
673
+ readonly width: number;
674
+ readonly height: number;
675
+ readonly x: number | undefined;
676
+ readonly y: number | undefined;
677
+ };
678
+ };
679
+ canvasDelete: (shapeId: string) => {
680
+ readonly type: "canvas.delete";
681
+ readonly payload: {
682
+ readonly shapeId: string;
683
+ };
684
+ };
685
+ canvasUpdate: (shapeId: string, props: Record<string, unknown>) => {
686
+ readonly type: "canvas.update";
687
+ readonly payload: {
688
+ readonly shapeId: string;
689
+ readonly props: Record<string, unknown>;
690
+ };
691
+ };
692
+ canvasViewport: (viewport: unknown) => {
693
+ readonly type: "canvas.viewport";
694
+ readonly payload: {
695
+ readonly viewport: unknown;
696
+ };
697
+ };
698
+ canvasViewportRemoved: (userId: string) => {
699
+ readonly type: "canvas.viewport";
700
+ readonly payload: {
701
+ readonly userId: string;
702
+ readonly removed: true;
703
+ };
704
+ };
705
+ cronTasks: (tasks: unknown) => {
706
+ readonly type: "cron.tasks";
707
+ readonly payload: {
708
+ readonly tasks: unknown;
709
+ };
710
+ };
711
+ cronHistory: (history: unknown) => {
712
+ readonly type: "cron.history";
713
+ readonly payload: {
714
+ readonly history: unknown;
715
+ };
716
+ };
717
+ cronStatus: (tasks: unknown, recentHistory: unknown) => {
718
+ readonly type: "cron.status";
719
+ readonly payload: {
720
+ readonly tasks: unknown;
721
+ readonly recentHistory: unknown;
722
+ };
723
+ };
724
+ presenceSync: (peers: unknown[]) => {
725
+ readonly type: "presence.sync";
726
+ readonly payload: {
727
+ readonly peers: unknown[];
728
+ };
729
+ };
730
+ presenceJoin: (peer: unknown) => {
731
+ readonly type: "presence.join";
732
+ readonly payload: {
733
+ readonly peer: unknown;
734
+ };
735
+ };
736
+ presenceLeave: (userId: string) => {
737
+ readonly type: "presence.leave";
738
+ readonly payload: {
739
+ readonly userId: string;
740
+ };
741
+ };
742
+ presenceUpdate: (userId: string, state: Record<string, unknown>) => {
743
+ readonly type: "presence.update";
744
+ readonly payload: {
745
+ readonly userId: string;
746
+ readonly state: Record<string, unknown>;
747
+ };
748
+ };
749
+ gwScopeError: (scopeType: string, scopeId: string, error: string) => {
750
+ readonly type: "gateway.scope_error";
751
+ readonly payload: {
752
+ readonly scopeType: string;
753
+ readonly scopeId: string;
754
+ readonly error: string;
755
+ };
756
+ };
757
+ gwUserUpdate: (user: unknown) => {
758
+ readonly type: "gateway.user_update";
759
+ readonly payload: unknown;
760
+ };
761
+ };
762
+ /**
763
+ * Handler map keyed by a message's string `type`. Every key maps to the
764
+ * payload type of the matching arm in `M`, so handlers get automatic
765
+ * payload narrowing with no runtime checks.
766
+ *
767
+ * Narrowing requires each `M` arm to use a string-literal `T`; widening
768
+ * `M` to `BaseMessage<string, unknown>` collapses all keys to a single
769
+ * untyped `string` and the map becomes a plain `Record<string, (p: unknown)
770
+ * => void>`.
771
+ */
772
+ type MessageHandlers<M extends BaseMessage<string, unknown> = ProtocolMessage> = {
773
+ [K in M as K['type']]?: (payload: K['payload']) => void;
774
+ };
775
+ /**
776
+ * Parse and route an incoming wire message. Accepts either the raw JSON
777
+ * string (directly from `ws.onmessage`'s `event.data`) or an already-
778
+ * parsed object. Returns `true` if a handler ran, `false` otherwise
779
+ * (unrecognised type, parse failure, or malformed envelope).
780
+ *
781
+ * Defaults `M` to `ProtocolMessage` so callers who don't care about
782
+ * direction can just do `dispatch(raw, handlers)`; narrow with
783
+ * `dispatch<ClientMessage>` / `dispatch<ServerMessage>` to restrict which
784
+ * arms the handler map is allowed to cover.
785
+ */
786
+ declare function dispatch<M extends BaseMessage<string, unknown> = ProtocolMessage>(raw: unknown, handlers: MessageHandlers<M>): boolean;
787
+ /**
788
+ * Serialise a typed-built message for `ws.send`. A tiny wrapper around
789
+ * `JSON.stringify` kept around so call sites read as
790
+ * `ws.send(encode(clientBuild.gameInput(...)))` rather than paying
791
+ * attention to stringify arguments.
792
+ */
793
+ declare function encode<M extends BaseMessage<string, unknown>>(message: M): string;
794
+
795
+ /**
796
+ * BaseRoom — Abstract base class for all DeepSpace Durable Objects.
797
+ *
798
+ * Provides:
799
+ * - WebSocket upgrade with Cloudflare hibernation API
800
+ * - Connection tracking (WebSocket -> UserAttachment)
801
+ * - Auth: parse JWT-verified user info from URL search params
802
+ * - Presence: connected users list, awareness on connect/disconnect
803
+ * - Message routing: JSON parse -> dispatch by `type` field, binary hook
804
+ * - Raw SQLite access via this.sql
805
+ * - Broadcast helpers: broadcast(), sendTo()
806
+ * - HTTP fetch handler with WebSocket upgrade detection
807
+ *
808
+ * Subclasses implement lifecycle hooks:
809
+ * onConnect, onMessage, onBinaryMessage, onDisconnect, onRequest, onAlarm
810
+ */
811
+
812
+ interface UserAttachment {
813
+ userId: string;
814
+ userName: string;
815
+ userEmail: string;
816
+ userImageUrl?: string;
817
+ /** Subclass-specific data serialized alongside user info */
818
+ [key: string]: unknown;
819
+ }
820
+ declare abstract class BaseRoom {
821
+ protected state: DurableObjectState;
822
+ protected env: {
823
+ [key: string]: unknown;
824
+ };
825
+ protected sql: SqlStorage;
826
+ constructor(state: DurableObjectState, env: unknown);
827
+ fetch(request: Request): Promise<Response>;
828
+ private handleWebSocketUpgrade;
829
+ webSocketMessage(ws: WebSocket, message: ArrayBuffer | string): Promise<void>;
830
+ webSocketClose(ws: WebSocket, code: number, reason: string): Promise<void>;
831
+ webSocketError(ws: WebSocket, error: unknown): Promise<void>;
832
+ alarm(): Promise<void>;
833
+ /**
834
+ * Called when a new WebSocket connects (after auth parsing).
835
+ * Return an augmented attachment to serialize on the WebSocket,
836
+ * or void to use the default attachment.
837
+ */
838
+ protected onConnect(ws: WebSocket, user: UserAttachment): UserAttachment | void | Promise<UserAttachment | void>;
839
+ /**
840
+ * Called for each parsed JSON message.
841
+ */
842
+ protected abstract onMessage(ws: WebSocket, user: UserAttachment, message: {
843
+ type: string;
844
+ [key: string]: unknown;
845
+ }): void | Promise<void>;
846
+ /**
847
+ * Called for binary messages (Yjs, custom protocols).
848
+ */
849
+ protected onBinaryMessage?(ws: WebSocket, user: UserAttachment, data: ArrayBuffer): void | Promise<void>;
850
+ /**
851
+ * Called when a WebSocket disconnects.
852
+ */
853
+ protected onDisconnect(ws: WebSocket, user: UserAttachment): void | Promise<void>;
854
+ /**
855
+ * Called for HTTP requests that are NOT WebSocket upgrades.
856
+ */
857
+ protected onRequest?(request: Request): Response | Promise<Response>;
858
+ /**
859
+ * Called on DO alarm.
860
+ */
861
+ protected onAlarm?(): void | Promise<void>;
862
+ /**
863
+ * Get all connected WebSockets.
864
+ */
865
+ protected getWebSockets(): WebSocket[];
866
+ /**
867
+ * Get the user attachment for a WebSocket.
868
+ */
869
+ protected getAttachment(ws: WebSocket): UserAttachment | null;
870
+ /**
871
+ * Get all currently connected users.
872
+ */
873
+ protected getConnectedUsers(): UserAttachment[];
874
+ /**
875
+ * Send a JSON message to a specific WebSocket.
876
+ *
877
+ * Typed as `ServerMessage` so every room's outbound traffic is
878
+ * compile-checked against the wire protocol contract. Passing
879
+ * `{ type: 'whatever', payload: {...} }` with a non-matching arm fails
880
+ * to compile — that's the whole point of the typed layer. Apps that
881
+ * need to send an app-specific message should override `sendTo` in
882
+ * their subclass with a widened union (`ServerMessage | MyAppMessage`).
883
+ */
884
+ protected sendTo(ws: WebSocket, message: ServerMessage): void;
885
+ /**
886
+ * Send binary data to a specific WebSocket.
887
+ */
888
+ protected sendBinaryTo(ws: WebSocket, data: Uint8Array | ArrayBuffer): void;
889
+ /**
890
+ * Broadcast a JSON message to all connected WebSockets.
891
+ * Optionally exclude a specific WebSocket (e.g. the sender).
892
+ *
893
+ * See `sendTo` for the reasoning behind typing as `ServerMessage`.
894
+ */
895
+ protected broadcast(message: ServerMessage, exclude?: WebSocket): void;
896
+ /**
897
+ * Broadcast binary data to all connected WebSockets.
898
+ */
899
+ protected broadcastBinary(data: Uint8Array | ArrayBuffer, exclude?: WebSocket): void;
900
+ }
901
+
902
+ interface Query {
903
+ collection: string;
904
+ where?: Record<string, unknown>;
905
+ orderBy?: string;
906
+ orderDir?: 'asc' | 'desc';
907
+ limit?: number;
908
+ }
909
+ interface Subscription {
910
+ id: string;
911
+ query: Query;
912
+ }
913
+ /** Key for Yjs doc: collection:recordId:fieldName */
914
+ type YjsDocKey = string;
915
+ interface YjsSubscription {
916
+ collection: string;
917
+ recordId: string;
918
+ fieldName: string;
919
+ }
920
+ interface DirectoryConversationData {
921
+ Name: string;
922
+ Description: string;
923
+ Type: string;
924
+ Visibility: string;
925
+ CreatedBy: string;
926
+ ParticipantHash: string;
927
+ ParticipantIds: string;
928
+ Status: string;
929
+ AssigneeId: string;
930
+ LinkedRef: string;
931
+ LastMessageAt: string;
932
+ LastMessagePreview: string;
933
+ LastMessageAuthor: string;
934
+ MessageCount: number;
935
+ }
936
+ interface ConversationStateData {
937
+ ConversationId: string;
938
+ UserId: string;
939
+ LastReadAt: string;
940
+ LastReadMessageCount: number;
941
+ Starred: number;
942
+ Archived: number;
943
+ Trashed: number;
944
+ Labels: string;
945
+ Folder: string;
946
+ }
947
+ interface DirectoryCommunityData {
948
+ Name: string;
949
+ Description: string;
950
+ CreatedBy: string;
951
+ Type: string;
952
+ Visibility: string;
953
+ MemberCount: number;
954
+ Rules: string;
955
+ IconUrl: string;
956
+ CoverUrl: string;
957
+ }
958
+ interface DirectoryMembershipData {
959
+ CommunityId: string;
960
+ UserId: string;
961
+ UserName: string;
962
+ Role: string;
963
+ JoinedAt: string;
964
+ }
965
+ interface DirectoryPostData {
966
+ Title: string;
967
+ Content: string;
968
+ AuthorId: string;
969
+ Type: string;
970
+ CommunityId: string;
971
+ ParentId: string;
972
+ ConversationId: string;
973
+ Status: string;
974
+ Tags: string;
975
+ LinkUrl: string;
976
+ }
977
+ interface ConvMessageData {
978
+ Content: string;
979
+ AuthorId: string;
980
+ ParentId: string;
981
+ Edited: number;
982
+ MessageType: string;
983
+ Metadata: string;
984
+ }
985
+ interface ConvReactionData {
986
+ MessageId: string;
987
+ Emoji: string;
988
+ UserId: string;
989
+ }
990
+ interface ConvMemberData {
991
+ UserId: string;
992
+ UserName: string;
993
+ Role: string;
994
+ }
995
+ interface ConvReadCursorData {
996
+ UserId: string;
997
+ LastReadAt: string;
998
+ }
999
+ interface ConvVoteData {
1000
+ TargetId: string;
1001
+ UserId: string;
1002
+ Direction: number;
1003
+ }
1004
+
1005
+ /**
1006
+ * Server-specific protocol types
1007
+ *
1008
+ * These depend on Cloudflare Workers / Yjs imports and can't live in shared/types.
1009
+ * All other protocol types (Query, payloads, etc.) live in shared/types/index.ts.
1010
+ */
1011
+
1012
+ /** Stored on WebSocket attachment (survives hibernation) */
1013
+ interface ConnectionAttachment extends UserAttachment {
1014
+ role: string;
1015
+ subscriptions: Subscription[];
1016
+ /** Yjs docs this connection is editing */
1017
+ yjsSubscriptions: YjsSubscription[];
1018
+ /** Yjs client ID for awareness */
1019
+ yjsClientId?: number;
1020
+ /** Client-side Yjs awareness clientId (extracted from first awareness message) */
1021
+ awarenessClientId?: number;
1022
+ }
1023
+ /**
1024
+ * Context passed to handlers for accessing shared resources.
1025
+ *
1026
+ * `send` is typed against `ServerMessage` so every outbound message is
1027
+ * compile-checked against the wire-protocol discriminated union. See
1028
+ * `shared/protocol/messages.ts` for why.
1029
+ */
1030
+ interface HandlerContext {
1031
+ sql: SqlStorage;
1032
+ state: DurableObjectState;
1033
+ yjsDocs: Map<YjsDocKey, Y.Doc>;
1034
+ getWebSockets(): Iterable<WebSocket>;
1035
+ send(ws: WebSocket, message: ServerMessage): void;
1036
+ sendBinary(ws: WebSocket, data: Uint8Array): void;
1037
+ }
1038
+
1039
+ /**
1040
+ * Collection Schema Definitions & Validation
1041
+ *
1042
+ * All collections use typed SQL columns. No document-mode / fields-based storage.
1043
+ */
1044
+ type ColumnInterpretation = {
1045
+ kind: 'plain';
1046
+ } | {
1047
+ kind: 'currency';
1048
+ symbol: string;
1049
+ decimals: number;
1050
+ } | {
1051
+ kind: 'date';
1052
+ format?: string;
1053
+ } | {
1054
+ kind: 'datetime';
1055
+ format?: string;
1056
+ } | {
1057
+ kind: 'boolean';
1058
+ trueLabel?: string;
1059
+ falseLabel?: string;
1060
+ } | {
1061
+ kind: 'percent';
1062
+ decimals?: number;
1063
+ } | {
1064
+ kind: 'select';
1065
+ options: string[];
1066
+ } | {
1067
+ kind: 'multiselect';
1068
+ options: string[];
1069
+ } | {
1070
+ kind: 'url';
1071
+ } | {
1072
+ kind: 'email';
1073
+ } | {
1074
+ kind: 'json';
1075
+ } | {
1076
+ kind: 'reference';
1077
+ targetTable: string;
1078
+ displayColumn: string;
1079
+ };
1080
+ interface ColumnDefinition {
1081
+ /** Stable ID override (survives renames). Falls back to `col_{name}`. */
1082
+ id?: string;
1083
+ name: string;
1084
+ storage: 'number' | 'text';
1085
+ interpretation: ColumnInterpretation | string;
1086
+ expression?: string;
1087
+ /** Auto-populate with current user ID on create. */
1088
+ userBound?: boolean;
1089
+ /** Cannot be changed after initial creation. */
1090
+ immutable?: boolean;
1091
+ /** Must be provided on create (non-null). */
1092
+ required?: boolean;
1093
+ /** Default value if not provided on create. */
1094
+ default?: unknown;
1095
+ /** Auto-set ISO timestamp when the named field changes (optionally to a specific value). */
1096
+ timestampTrigger?: {
1097
+ field: string;
1098
+ value?: unknown;
1099
+ };
1100
+ }
1101
+ interface ResolvedColumn {
1102
+ id: string;
1103
+ name: string;
1104
+ storage: 'number' | 'text';
1105
+ interpretation: ColumnInterpretation;
1106
+ expression?: string;
1107
+ readonly: boolean;
1108
+ userBound?: boolean;
1109
+ immutable?: boolean;
1110
+ required?: boolean;
1111
+ default?: unknown;
1112
+ timestampTrigger?: {
1113
+ field: string;
1114
+ value?: unknown;
1115
+ };
1116
+ }
1117
+ declare function collectionTableName(name: string): string;
1118
+ declare function columnId(name: string): string;
1119
+ declare function resolveColumn(col: ColumnDefinition): ResolvedColumn;
1120
+ declare function rowToData(row: Record<string, unknown>, columns: ResolvedColumn[]): Record<string, unknown>;
1121
+ declare function dataToColumnValues(data: Record<string, unknown>, columns: ResolvedColumn[]): Record<string, unknown>;
1122
+ declare function coerceValue(value: unknown, storage: 'number' | 'text', interpretation: ColumnInterpretation): unknown;
1123
+ declare function buildTableSelect(collectionName: string, columns: ResolvedColumn[]): string;
1124
+ type PermissionLevel = boolean | 'own' | 'unclaimed-or-own' | 'collaborator' | 'team' | 'access' | 'published' | 'shared';
1125
+ interface RolePermissions {
1126
+ read: PermissionLevel;
1127
+ create: boolean;
1128
+ update: PermissionLevel;
1129
+ delete: PermissionLevel;
1130
+ /** If set, only these columns can be updated by this role. */
1131
+ writableFields?: string[];
1132
+ }
1133
+ interface CollectionSchema {
1134
+ name: string;
1135
+ /** Column definitions — every collection is stored in a typed SQL table. */
1136
+ columns: ColumnDefinition[];
1137
+ /** Composite uniqueness constraint (e.g., ['userId', 'taskId']). */
1138
+ uniqueOn?: string[];
1139
+ /** Column name used for ownership checks (default: `_created_by`). */
1140
+ ownerField?: string;
1141
+ /** Column containing JSON array of collaborator user IDs. */
1142
+ collaboratorsField?: string;
1143
+ /** Column containing team ID for team-based access. */
1144
+ teamField?: string;
1145
+ /**
1146
+ * Column controlling per-record read visibility.
1147
+ * String: visible when `data[field] === 'public'`.
1148
+ * Object: visible when `data[field] === value`.
1149
+ */
1150
+ visibilityField?: string | {
1151
+ field: string;
1152
+ value: unknown;
1153
+ };
1154
+ /** Permissions per role. Use '*' for a catch-all fallback. */
1155
+ permissions: Record<string, RolePermissions>;
1156
+ /** Default role for new users (only on 'users' collection). */
1157
+ defaultRole?: string;
1158
+ }
1159
+ interface User {
1160
+ id: string;
1161
+ email: string;
1162
+ name: string;
1163
+ imageUrl?: string;
1164
+ role: string;
1165
+ createdAt: string;
1166
+ lastSeenAt: string;
1167
+ }
1168
+ interface StoredRecord {
1169
+ collection: string;
1170
+ recordId: string;
1171
+ data: Record<string, unknown>;
1172
+ createdBy: string;
1173
+ createdAt: string;
1174
+ updatedAt: string;
1175
+ }
1176
+ interface PermissionContext {
1177
+ isTeamMember: (teamId: string, userId: string) => boolean;
1178
+ }
1179
+ declare const noopPermissionContext: PermissionContext;
1180
+ declare function getRolePermissions(schema: CollectionSchema, role: string): RolePermissions;
1181
+ declare function isOwner(schema: CollectionSchema, record: {
1182
+ data: Record<string, unknown>;
1183
+ createdBy: string;
1184
+ }, userId: string): boolean;
1185
+ declare function canRead(schema: CollectionSchema, role: string, record: {
1186
+ data: Record<string, unknown>;
1187
+ createdBy: string;
1188
+ recordId?: string;
1189
+ }, userId: string, ctx?: PermissionContext): boolean;
1190
+ declare function canCreate(schema: CollectionSchema, role: string): boolean;
1191
+ declare function canUpdate(schema: CollectionSchema, role: string, record: {
1192
+ data: Record<string, unknown>;
1193
+ createdBy: string;
1194
+ recordId?: string;
1195
+ }, userId: string, ctx?: PermissionContext): boolean;
1196
+ declare function canDelete(schema: CollectionSchema, role: string, record: {
1197
+ data: Record<string, unknown>;
1198
+ createdBy: string;
1199
+ recordId?: string;
1200
+ }, userId: string, ctx?: PermissionContext): boolean;
1201
+ /** Check if a field update violates writableFields restrictions. */
1202
+ declare function checkFieldPermissions(schema: CollectionSchema, role: string, newData: Record<string, unknown>, existingData?: Record<string, unknown>): string | null;
1203
+ /** Names of columns managed by the system (registerUser), not client mutations. */
1204
+ declare const SYSTEM_MANAGED_COLUMNS: Set<string>;
1205
+ /** Standard user columns. Apps spread these into their users schema. */
1206
+ declare const USERS_COLUMNS: ColumnDefinition[];
1207
+ declare const BASE_USERS_SCHEMA: CollectionSchema;
1208
+ declare class SchemaRegistry {
1209
+ private trusted;
1210
+ constructor(schemas?: CollectionSchema[]);
1211
+ registerTrusted(schema: CollectionSchema): void;
1212
+ get(name: string): CollectionSchema | undefined;
1213
+ has(name: string): boolean;
1214
+ hasTrusted(name: string): boolean;
1215
+ all(): CollectionSchema[];
1216
+ names(): string[];
1217
+ }
1218
+ type PermissionSource = 'explicit' | 'wildcard' | 'default-deny';
1219
+ interface ResolvedPermission {
1220
+ level: PermissionLevel | boolean;
1221
+ source: PermissionSource;
1222
+ }
1223
+ interface CollectionPermissionSummary {
1224
+ collection: string;
1225
+ ownerField?: string;
1226
+ collaboratorsField?: string;
1227
+ teamField?: string;
1228
+ columns: ColumnDefinition[];
1229
+ permissions: Record<string, {
1230
+ read: ResolvedPermission;
1231
+ create: ResolvedPermission;
1232
+ update: ResolvedPermission;
1233
+ delete: ResolvedPermission;
1234
+ writableFields?: string[];
1235
+ }>;
1236
+ }
1237
+ interface PermissionAnalysis {
1238
+ roles: string[];
1239
+ collections: CollectionPermissionSummary[];
1240
+ }
1241
+ declare function analyzePermissions(schemas: CollectionSchema[]): PermissionAnalysis;
1242
+
1243
+ /**
1244
+ * RecordRoom Durable Object
1245
+ *
1246
+ * SQLite-based storage with query-based real-time subscriptions.
1247
+ * Extends BaseRoom for WebSocket/connection infrastructure.
1248
+ *
1249
+ * Architecture:
1250
+ * - Data stored in SQLite (single `records` table)
1251
+ * - Clients subscribe to QUERIES, not collections
1252
+ * - On record change, server evaluates which subscriptions match
1253
+ * - Only matching subscribers receive updates
1254
+ *
1255
+ * Protocol:
1256
+ * - SUBSCRIBE { subscriptionId, query } → QUERY_RESULT { subscriptionId, records }
1257
+ * - UNSUBSCRIBE { subscriptionId }
1258
+ * - PUT { collection, recordId, data } → broadcasts RECORD_CHANGE to matching
1259
+ * - DELETE { collection, recordId } → broadcasts RECORD_CHANGE to matching
1260
+ */
1261
+
1262
+ /**
1263
+ * RecordRoom configuration options
1264
+ */
1265
+ interface RecordRoomConfig {
1266
+ /**
1267
+ * User ID of the app owner.
1268
+ * This user automatically gets 'admin' role on connect.
1269
+ */
1270
+ ownerUserId?: string;
1271
+ }
1272
+ /**
1273
+ * RecordRoom Durable Object
1274
+ */
1275
+ declare class RecordRoom extends BaseRoom {
1276
+ private schemaRegistry;
1277
+ private initPromise;
1278
+ /** Yjs docs loaded in memory (key: collection:recordId:fieldName) */
1279
+ private yjsDocs;
1280
+ /** Next Yjs client ID counter */
1281
+ private nextYjsClientId;
1282
+ /** Owner user ID — gets admin role automatically */
1283
+ private ownerUserId;
1284
+ /** True until the first fetch() completes — detects hibernation wake-up */
1285
+ private freshConstruct;
1286
+ constructor(state: DurableObjectState, env: unknown, schemas?: CollectionSchema[], config?: RecordRoomConfig);
1287
+ private getPermissionContext;
1288
+ fetch(request: Request): Promise<Response>;
1289
+ /** Timing info from the current fetch(), used by onConnect for logging */
1290
+ private _fetchTiming;
1291
+ private ensureInitialized;
1292
+ private initializeDatabase;
1293
+ private migrateUsersTableIfExists;
1294
+ private migrateRecordsTable;
1295
+ private ensureCollectionTable;
1296
+ private ensureAllCollectionTables;
1297
+ protected onConnect(ws: WebSocket, user: UserAttachment): Promise<ConnectionAttachment>;
1298
+ protected onMessage(ws: WebSocket, user: UserAttachment, msg: {
1299
+ type: string;
1300
+ [key: string]: unknown;
1301
+ }): Promise<void>;
1302
+ protected onBinaryMessage(ws: WebSocket, user: UserAttachment, data: ArrayBuffer): Promise<void>;
1303
+ protected onDisconnect(ws: WebSocket, user: UserAttachment): void;
1304
+ webSocketMessage(ws: WebSocket, message: ArrayBuffer | string): Promise<void>;
1305
+ webSocketClose(ws: WebSocket, code: number, reason: string): Promise<void>;
1306
+ webSocketError(ws: WebSocket, error: unknown): Promise<void>;
1307
+ private handleRecordMessage;
1308
+ private handleListSchemas;
1309
+ private createHandlerContext;
1310
+ private createRecordContext;
1311
+ private createUserContext;
1312
+ private createYjsContext;
1313
+ private send;
1314
+ private sendBinaryHelper;
1315
+ }
1316
+
1317
+ /**
1318
+ * YjsRoom — Lightweight Durable Object for collaborative Yjs documents.
1319
+ * Extends BaseRoom for WebSocket/connection infrastructure.
1320
+ *
1321
+ * Unlike RecordRoom (schemas, RBAC, queries, user state), YjsRoom is
1322
+ * purpose-built for Yjs: sync, relay, persist. One DO per document.
1323
+ *
1324
+ * Architecture (SOTA for Yjs + Cloudflare DOs):
1325
+ * - Auth verified at the worker edge, role passed to DO via URL params
1326
+ * - DO is a thin Yjs sync relay: receive → apply → persist → broadcast
1327
+ * - Viewers can observe but not write; members/admins can write
1328
+ * - State persisted as a single binary blob in SQLite
1329
+ *
1330
+ * Uses the shared yjs-protocol.ts encoding utilities — no duplication.
1331
+ */
1332
+
1333
+ interface YjsAttachment extends UserAttachment {
1334
+ role: string;
1335
+ canWrite: boolean;
1336
+ awarenessClientId: number | null;
1337
+ }
1338
+ declare class YjsRoom extends BaseRoom {
1339
+ private doc;
1340
+ private initialized;
1341
+ constructor(state: DurableObjectState, env: unknown);
1342
+ private ensureInitialized;
1343
+ private getDoc;
1344
+ private persistDoc;
1345
+ protected onConnect(ws: WebSocket, user: UserAttachment): YjsAttachment;
1346
+ protected onMessage(ws: WebSocket, user: UserAttachment, message: {
1347
+ type: string;
1348
+ [key: string]: unknown;
1349
+ }): void;
1350
+ protected onBinaryMessage(ws: WebSocket, user: UserAttachment, data: ArrayBuffer): void;
1351
+ protected onDisconnect(ws: WebSocket, user: UserAttachment): void;
1352
+ private handleSync;
1353
+ private handleAwareness;
1354
+ private broadcastRaw;
1355
+ }
1356
+
1357
+ /**
1358
+ * GameRoom — Authoritative game loop Durable Object.
1359
+ *
1360
+ * Extends BaseRoom with:
1361
+ * - Alarm-based tick loop with configurable interval
1362
+ * - Player management (join, leave, ready state)
1363
+ * - Input collection per tick, authoritative state computation
1364
+ * - State broadcast to all connected players
1365
+ *
1366
+ * Subclasses implement game logic via lifecycle hooks:
1367
+ * onTick, onPlayerJoin, onPlayerLeave, onGameStart, onGameEnd
1368
+ *
1369
+ * Message types: game.*
1370
+ */
1371
+
1372
+ interface GameRoomConfig {
1373
+ /** Ticks per second (default: 20) */
1374
+ tickRate?: number;
1375
+ /** Minimum players to start (default: 1) */
1376
+ minPlayers?: number;
1377
+ /** Maximum players (default: unlimited) */
1378
+ maxPlayers?: number;
1379
+ }
1380
+ interface Player {
1381
+ userId: string;
1382
+ userName: string;
1383
+ ready: boolean;
1384
+ connectedAt: string;
1385
+ data: Record<string, unknown>;
1386
+ }
1387
+ interface GameInput {
1388
+ userId: string;
1389
+ action: string;
1390
+ data: Record<string, unknown>;
1391
+ tick: number;
1392
+ }
1393
+ interface GameAttachment extends UserAttachment {
1394
+ joinedAt: string;
1395
+ }
1396
+ declare abstract class GameRoom extends BaseRoom {
1397
+ private config;
1398
+ private players;
1399
+ private inputBuffer;
1400
+ private currentTick;
1401
+ private gameState;
1402
+ private running;
1403
+ private initialized;
1404
+ constructor(state: DurableObjectState, env: unknown, config?: GameRoomConfig);
1405
+ private ensureInitialized;
1406
+ private persistState;
1407
+ protected onConnect(ws: WebSocket, user: UserAttachment): GameAttachment;
1408
+ protected onMessage(ws: WebSocket, user: UserAttachment, message: {
1409
+ type: string;
1410
+ [key: string]: unknown;
1411
+ }): Promise<void>;
1412
+ protected onDisconnect(ws: WebSocket, user: UserAttachment): void;
1413
+ protected onAlarm(): Promise<void>;
1414
+ private checkAutoStart;
1415
+ private startGame;
1416
+ private stopGame;
1417
+ protected getGameState(): Record<string, unknown>;
1418
+ protected setGameState(state: Record<string, unknown>): void;
1419
+ protected getPlayers(): Player[];
1420
+ protected isRunning(): boolean;
1421
+ protected getCurrentTick(): number;
1422
+ /**
1423
+ * Called each tick with current state and collected inputs.
1424
+ * Return the new game state, or undefined to keep current state.
1425
+ */
1426
+ protected abstract onTick(state: Record<string, unknown>, inputs: GameInput[], tick: number): Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>;
1427
+ /** Called when a player connects */
1428
+ protected onPlayerJoin(player: Player): void;
1429
+ /** Called when a player disconnects */
1430
+ protected onPlayerLeave(player: Player): void;
1431
+ /** Called when the game starts */
1432
+ protected onGameStart(): void;
1433
+ /** Called when the game ends */
1434
+ protected onGameEnd(finalState: Record<string, unknown>): void;
1435
+ /**
1436
+ * Called once when the DO first hydrates persisted state from storage.
1437
+ * Receives the parsed state blob as it was written by a previous build.
1438
+ * Return the state object to install as `gameState`.
1439
+ *
1440
+ * Subclasses with evolving schemas should override this hook to:
1441
+ * - merge new fields onto a default template,
1442
+ * - upgrade shapes across versioned states,
1443
+ * - or discard stale blobs entirely by returning a fresh object.
1444
+ *
1445
+ * The default implementation is a pass-through, preserving the legacy
1446
+ * "stored blob is gospel" behavior for subclasses that don't care.
1447
+ *
1448
+ * If JSON parsing of the stored blob fails this hook is NOT called — the
1449
+ * DO starts with an empty state and the subclass's `onGameStart` (or
1450
+ * first `onTick`) is responsible for initializing.
1451
+ */
1452
+ protected onHydrateState(stored: Record<string, unknown>): Record<string, unknown>;
1453
+ }
1454
+
1455
+ /**
1456
+ * CanvasRoom — Spatial canvas Durable Object (tldraw-style).
1457
+ *
1458
+ * Extends BaseRoom with Yjs-backed spatial operations.
1459
+ * Each shape is a Y.Map entry, enabling multi-user concurrent editing.
1460
+ *
1461
+ * Features:
1462
+ * - Shape CRUD (add, move, resize, delete, update properties)
1463
+ * - Viewport awareness (each user's visible region)
1464
+ * - Per-user undo/redo stacks
1465
+ *
1466
+ * Message types: canvas.*
1467
+ */
1468
+
1469
+ interface CanvasShape {
1470
+ id: string;
1471
+ type: string;
1472
+ x: number;
1473
+ y: number;
1474
+ width: number;
1475
+ height: number;
1476
+ rotation?: number;
1477
+ props: Record<string, unknown>;
1478
+ createdBy: string;
1479
+ createdAt: string;
1480
+ updatedAt: string;
1481
+ }
1482
+ interface Viewport {
1483
+ userId: string;
1484
+ x: number;
1485
+ y: number;
1486
+ width: number;
1487
+ height: number;
1488
+ zoom: number;
1489
+ }
1490
+ interface CanvasAttachment extends UserAttachment {
1491
+ viewport: Viewport | null;
1492
+ }
1493
+ declare class CanvasRoom extends BaseRoom {
1494
+ private doc;
1495
+ private initialized;
1496
+ private viewports;
1497
+ private undoStacks;
1498
+ private redoStacks;
1499
+ constructor(state: DurableObjectState, env: unknown);
1500
+ private ensureInitialized;
1501
+ private getDoc;
1502
+ private persistDoc;
1503
+ private getShapesMap;
1504
+ fetch(request: Request): Promise<Response>;
1505
+ protected onConnect(ws: WebSocket, user: UserAttachment): CanvasAttachment;
1506
+ protected onMessage(ws: WebSocket, user: UserAttachment, message: {
1507
+ type: string;
1508
+ [key: string]: unknown;
1509
+ }): Promise<void>;
1510
+ protected onDisconnect(ws: WebSocket, user: UserAttachment): void;
1511
+ private pushUndo;
1512
+ private clearRedo;
1513
+ private handleUndo;
1514
+ private handleRedo;
1515
+ private getAllShapes;
1516
+ }
1517
+
1518
+ /**
1519
+ * PresenceRoom — Ephemeral presence-tracking Durable Object.
1520
+ *
1521
+ * Extends BaseRoom. No SQLite — purely in-memory presence state.
1522
+ * Tracks who is present in a given scope (canvas, doc, thread, etc.)
1523
+ * and broadcasts join/leave/state-update events to all connected peers.
1524
+ *
1525
+ * Each scope ID maps to its own DO instance. Clients connect via
1526
+ * /ws/presence/:scopeId and receive real-time presence for that scope.
1527
+ *
1528
+ * Peers can attach arbitrary state (cursor position, typing indicator,
1529
+ * viewport, selection, etc.) via MSG.PRESENCE_UPDATE.
1530
+ *
1531
+ * Message types: presence.*
1532
+ */
1533
+
1534
+ interface PresencePeer {
1535
+ userId: string;
1536
+ userName: string;
1537
+ userEmail: string;
1538
+ userImageUrl?: string;
1539
+ joinedAt: string;
1540
+ /** Arbitrary per-user state (cursor, typing, viewport, etc.) */
1541
+ state: Record<string, unknown>;
1542
+ }
1543
+ interface PresenceAttachment extends UserAttachment {
1544
+ joinedAt: string;
1545
+ }
1546
+ declare class PresenceRoom extends BaseRoom {
1547
+ private peers;
1548
+ private peerSockets;
1549
+ constructor(state: DurableObjectState, env: unknown);
1550
+ protected onConnect(ws: WebSocket, user: UserAttachment): PresenceAttachment;
1551
+ protected onMessage(ws: WebSocket, user: UserAttachment, message: {
1552
+ type: string;
1553
+ [key: string]: unknown;
1554
+ }): Promise<void>;
1555
+ protected onDisconnect(ws: WebSocket, user: UserAttachment): void;
1556
+ }
1557
+
1558
+ /**
1559
+ * CronRoom — Per-app scheduled task execution Durable Object.
1560
+ *
1561
+ * Extends BaseRoom. One DO per app shards cron work and avoids the
1562
+ * dispatch-worker's global KV-poll bottleneck. The DO alarm triggers
1563
+ * `onTask(name)` on the configured cadence; each execution is recorded
1564
+ * to a per-app `cron_history` table. Subscribers (admin clients via the
1565
+ * `useCronMonitor` hook) get pushes over the WebSocket.
1566
+ *
1567
+ * Tasks declare *either* `intervalMinutes` (run every N minutes) *or*
1568
+ * `schedule` + `timezone` (5-field cron expression evaluated against an
1569
+ * IANA timezone via `Intl.DateTimeFormat`). Cron mode is DST-aware
1570
+ * because the wall-clock comparison happens after the timezone shift,
1571
+ * not before.
1572
+ *
1573
+ * Message types: cron.*
1574
+ */
1575
+
1576
+ interface CronTask {
1577
+ name: string;
1578
+ /** Interval in minutes (interval mode) — mutually exclusive with `schedule`. */
1579
+ intervalMinutes?: number;
1580
+ /** 5-field cron expression (cron mode) — requires `timezone`. */
1581
+ schedule?: string;
1582
+ /** IANA timezone string (e.g. "America/New_York"). Required with `schedule`. */
1583
+ timezone?: string;
1584
+ /** Whether the task starts paused. */
1585
+ paused?: boolean;
1586
+ }
1587
+ interface CronRoomConfig {
1588
+ tasks: CronTask[];
1589
+ }
1590
+ interface CronExecution {
1591
+ taskName: string;
1592
+ startedAt: string;
1593
+ completedAt: string | null;
1594
+ success: boolean;
1595
+ durationMs: number;
1596
+ error?: string;
1597
+ }
1598
+ declare abstract class CronRoom extends BaseRoom {
1599
+ private tasks;
1600
+ private initialized;
1601
+ constructor(state: DurableObjectState, env: unknown, config: CronRoomConfig);
1602
+ private ensureInitialized;
1603
+ fetch(request: Request): Promise<Response>;
1604
+ protected onConnect(ws: WebSocket, user: UserAttachment): UserAttachment;
1605
+ protected onMessage(ws: WebSocket, user: UserAttachment, message: {
1606
+ type: string;
1607
+ [key: string]: unknown;
1608
+ }): Promise<void>;
1609
+ protected onAlarm(): Promise<void>;
1610
+ private executeTask;
1611
+ private scheduleNextAlarm;
1612
+ private getTaskStates;
1613
+ private getRecentHistory;
1614
+ private broadcastStatus;
1615
+ /**
1616
+ * Execute a scheduled task by name.
1617
+ * Called both by the alarm scheduler and manual trigger.
1618
+ */
1619
+ protected abstract onTask(taskName: string): void | Promise<void>;
1620
+ }
1621
+
1622
+ /**
1623
+ * DO Manifest — Dynamic Durable Object binding declarations.
1624
+ *
1625
+ * Apps export a `__DO_MANIFEST__` array in their worker.ts.
1626
+ * The CLI extracts it and sends it to the deploy worker,
1627
+ * which uses it to generate dynamic CF API bindings and migrations.
1628
+ */
1629
+ interface DOManifestEntry {
1630
+ /** CF binding name, e.g. 'RECORD_ROOMS' */
1631
+ binding: string;
1632
+ /** Exported class name, e.g. 'AppRecordRoom' */
1633
+ className: string;
1634
+ /** Whether this DO uses SQLite storage */
1635
+ sqlite: boolean;
1636
+ }
1637
+ type DOManifest = DOManifestEntry[];
1638
+ /**
1639
+ * Utility type: auto-generates Env bindings from a manifest.
1640
+ *
1641
+ * @example
1642
+ * const manifest = [
1643
+ * { binding: 'RECORD_ROOMS', className: 'AppRecordRoom', sqlite: true },
1644
+ * { binding: 'GAME_ROOMS', className: 'AppGameRoom', sqlite: true },
1645
+ * ] as const satisfies DOManifest
1646
+ *
1647
+ * type Env = BaseEnv & DOBindings<typeof manifest>
1648
+ * // => { RECORD_ROOMS: DurableObjectNamespace; GAME_ROOMS: DurableObjectNamespace }
1649
+ */
1650
+ type DOBindings<T extends readonly DOManifestEntry[]> = {
1651
+ [K in T[number]['binding']]: DurableObjectNamespace;
1652
+ };
1653
+ /** Default manifest for apps that don't declare one */
1654
+ declare const DEFAULT_DO_MANIFEST: DOManifest;
1655
+
1656
+ /**
1657
+ * Messaging Schemas
1658
+ *
1659
+ * Pre-built collection schemas for messaging functionality.
1660
+ * Any app can import these to add channels, messages, reactions, etc.
1661
+ *
1662
+ * @example
1663
+ * ```typescript
1664
+ * import { CHANNELS_SCHEMA, MESSAGES_SCHEMA, REACTIONS_SCHEMA } from 'deepspace/worker'
1665
+ * export const schemas = [usersSchema, CHANNELS_SCHEMA, MESSAGES_SCHEMA, REACTIONS_SCHEMA]
1666
+ * ```
1667
+ */
1668
+
1669
+ declare const CHANNELS_SCHEMA: CollectionSchema;
1670
+ declare const MESSAGES_SCHEMA: CollectionSchema;
1671
+ declare const REACTIONS_SCHEMA: CollectionSchema;
1672
+ declare const CHANNEL_MEMBERS_SCHEMA: CollectionSchema;
1673
+ declare const CHANNEL_INVITATIONS_SCHEMA: CollectionSchema;
1674
+ declare const READ_RECEIPTS_SCHEMA: CollectionSchema;
1675
+
1676
+ /**
1677
+ * Shared conversation schemas for RecordRoom-based conversations.
1678
+ *
1679
+ * These define the collections inside a per-conversation RecordRoom DO.
1680
+ * Used by apps that have messaging/conversation features (slack-clone,
1681
+ * helpdesk, mail, reddit-clone, etc.).
1682
+ *
1683
+ * Each conversation gets its own RecordRoom DO keyed by `conv:{id}`.
1684
+ */
1685
+
1686
+ declare const CONVERSATION_SCHEMAS: CollectionSchema[];
1687
+ /**
1688
+ * Voting schemas for Reddit-style apps.
1689
+ * Add these alongside CONVERSATION_SCHEMAS for apps that need voting.
1690
+ */
1691
+ declare const VOTING_SCHEMAS: CollectionSchema[];
1692
+
1693
+ /**
1694
+ * Directory DO Schemas
1695
+ *
1696
+ * Purpose-built collections for the `dir:{appName}` global DO type.
1697
+ * This is the cross-app-visible directory layer — any app can subscribe
1698
+ * to another app's directory in real-time via SHARED_CONNECTIONS.
1699
+ *
1700
+ * Five collections covering all standard communication/social patterns:
1701
+ * - conversations: channels, DMs, email threads, support chats
1702
+ * - conversation_state: per-user metadata (read cursor, stars, labels, folders)
1703
+ * - communities: groups, forums, boards, projects
1704
+ * - memberships: user membership in communities
1705
+ * - posts: feed items, tweets, Q&A questions, announcements
1706
+ *
1707
+ * Domain-specific data (tickets, CRM, procurement) stays in app DOs.
1708
+ * Message content stays in conv:{id} DOs (CONVERSATION_SCHEMAS).
1709
+ */
1710
+
1711
+ /** All directory schemas for the `dir:{appName}` global DO type. */
1712
+ declare const DIRECTORY_SCHEMAS: CollectionSchema[];
1713
+
1714
+ /**
1715
+ * Workspace DO Schemas
1716
+ *
1717
+ * All collections for the workspace:default Durable Object.
1718
+ * This DO consolidates shared, cross-app business data:
1719
+ *
1720
+ * - teams / team_members — workspace-wide team management (replaces built-in teams)
1721
+ * - tasks / projects / tags — team-scoped task management
1722
+ * - people — shared contacts directory
1723
+ * - transactions / accounts — shared financial ledger
1724
+ *
1725
+ * Team-scoped collections use teamField RBAC: members see only
1726
+ * their team's records; admins see everything.
1727
+ */
1728
+
1729
+ declare const workspaceTeamsSchema: CollectionSchema;
1730
+ declare const workspaceTeamMembersSchema: CollectionSchema;
1731
+ declare const workspaceTasksSchema: CollectionSchema;
1732
+ declare const workspaceProjectsSchema: CollectionSchema;
1733
+ declare const workspaceTagsSchema: CollectionSchema;
1734
+ declare const workspacePeopleSchema: CollectionSchema;
1735
+ declare const workspaceTransactionsSchema: CollectionSchema;
1736
+ declare const workspaceAccountsSchema: CollectionSchema;
1737
+ /**
1738
+ * Universal sharing index. Any app can create share records for any content type.
1739
+ *
1740
+ * ContentType: 'document' | 'slide' | 'spreadsheet' | ... (extensible)
1741
+ * ShareType: 'channel' | 'team' | 'direct' | 'link' | 'org' (extensible)
1742
+ * ShareTarget: the ID of the target (channelId, teamId, userId, linkId, orgId)
1743
+ * Permission: 'view' | 'edit' — what the share grants
1744
+ *
1745
+ * Content itself lives in the owner's app DO.
1746
+ * This table is the discovery layer: "what has been shared, with whom, and how".
1747
+ */
1748
+ declare const workspaceContentSharesSchema: CollectionSchema;
1749
+ declare const workspaceFormResponsesSchema: CollectionSchema;
1750
+ /**
1751
+ * Maps DeepSpace users to their claimed @app.space email handles.
1752
+ * Shared across all apps so any app can look up a user's email address.
1753
+ * All handles are under the @app.space domain.
1754
+ */
1755
+ declare const workspaceEmailHandlesSchema: CollectionSchema;
1756
+ declare const WORKSPACE_SCHEMAS: CollectionSchema[];
1757
+
1758
+ /**
1759
+ * Shared Durable Object Schemas
1760
+ *
1761
+ * Central registry of global DO types with fixed schemas.
1762
+ * Apps connect to shared DOs via SHARED_CONNECTIONS in constants.ts.
1763
+ * The ScopeRegistry resolves collection names to the correct scope automatically.
1764
+ *
1765
+ * Scope tiers:
1766
+ * - App DO (app:{appHandle}) — private to each app, app defines tables
1767
+ * - Dir DO (dir:{appHandle}) — cross-app directory (conversations, communities, posts)
1768
+ * - Workspace DO (workspace:default) — shared business data (teams, tasks, people, ledger)
1769
+ * - Conv DO (conv:{id}) — single conversation (messages, reactions, members)
1770
+ */
1771
+
1772
+ interface SharedConnection {
1773
+ type: string;
1774
+ instanceId?: string;
1775
+ }
1776
+ interface GlobalDOType {
1777
+ name: string;
1778
+ schemas: CollectionSchema[];
1779
+ description: string;
1780
+ }
1781
+ declare const GLOBAL_DO_TYPES: GlobalDOType[];
1782
+ /** All valid global DO type names. */
1783
+ declare const GLOBAL_DO_TYPE_NAMES: string[];
1784
+ /** Look up a global DO type by name, returns null if not found. */
1785
+ declare function getGlobalDOType(name: string): GlobalDOType | null;
1786
+ /** Get the fixed schemas for a global DO type. Returns empty array if unknown type. */
1787
+ declare function getGlobalDOSchemas(typeName: string): CollectionSchema[];
1788
+ /** All collection names reserved by global DO types. Apps must not reuse these. */
1789
+ declare const RESERVED_COLLECTION_NAMES: Set<string>;
1790
+
1791
+ /**
1792
+ * Yjs Sync Protocol Implementation
1793
+ *
1794
+ * This implements the y-protocols sync and awareness protocols without
1795
+ * depending on y-websocket internal paths. Makes it easier to bundle
1796
+ * and template.
1797
+ *
1798
+ * Protocol spec: https://github.com/yjs/y-protocols
1799
+ */
1800
+
1801
+ declare const MSG_SYNC = 0;
1802
+ declare const MSG_AWARENESS = 1;
1803
+ declare const MSG_SYNC_STEP1 = 0;
1804
+ declare const MSG_SYNC_STEP2 = 1;
1805
+ declare const MSG_SYNC_UPDATE = 2;
1806
+ declare function createEncoder(): {
1807
+ data: number[];
1808
+ };
1809
+ declare function toUint8Array(encoder: {
1810
+ data: number[];
1811
+ }): Uint8Array;
1812
+ declare function writeVarUint(encoder: {
1813
+ data: number[];
1814
+ }, num: number): void;
1815
+ declare function writeVarUint8Array(encoder: {
1816
+ data: number[];
1817
+ }, arr: Uint8Array): void;
1818
+ declare function createDecoder(data: Uint8Array): {
1819
+ data: Uint8Array;
1820
+ pos: number;
1821
+ };
1822
+ declare function readVarUint(decoder: {
1823
+ data: Uint8Array;
1824
+ pos: number;
1825
+ }): number;
1826
+ declare function readVarUint8Array(decoder: {
1827
+ data: Uint8Array;
1828
+ pos: number;
1829
+ }): Uint8Array;
1830
+ declare function hasContent(decoder: {
1831
+ data: Uint8Array;
1832
+ pos: number;
1833
+ }): boolean;
1834
+ /**
1835
+ * Encode sync step 1: our state vector
1836
+ */
1837
+ declare function encodeSyncStep1(doc: Y.Doc): Uint8Array;
1838
+ /**
1839
+ * Encode sync step 2: diff based on received state vector
1840
+ */
1841
+ declare function encodeSyncStep2(doc: Y.Doc, stateVector: Uint8Array): Uint8Array;
1842
+ /**
1843
+ * Encode an incremental update
1844
+ */
1845
+ declare function encodeUpdate(update: Uint8Array): Uint8Array;
1846
+ interface SyncResult {
1847
+ type: 'step1' | 'step2' | 'update';
1848
+ response?: Uint8Array;
1849
+ stateVector?: Uint8Array;
1850
+ update?: Uint8Array;
1851
+ }
1852
+ /**
1853
+ * Handle an incoming sync message
1854
+ * Returns response to send (if any) and whether this was an update
1855
+ */
1856
+ declare function handleSyncMessage(doc: Y.Doc, data: Uint8Array): SyncResult;
1857
+ interface AwarenessState {
1858
+ [key: string]: unknown;
1859
+ }
1860
+ interface AwarenessStates {
1861
+ [clientId: number]: AwarenessState;
1862
+ }
1863
+ /**
1864
+ * Simple awareness implementation for presence/cursors
1865
+ */
1866
+ declare class Awareness {
1867
+ doc: Y.Doc;
1868
+ clientID: number;
1869
+ states: Map<number, AwarenessState>;
1870
+ private meta;
1871
+ private updateListeners;
1872
+ private changeListeners;
1873
+ constructor(doc: Y.Doc);
1874
+ getStates(): Map<number, AwarenessState>;
1875
+ getLocalState(): AwarenessState | null;
1876
+ setLocalState(state: AwarenessState | null): void;
1877
+ setLocalStateField(field: string, value: unknown): void;
1878
+ on(event: 'update' | 'change', handler: (changes: {
1879
+ added: number[];
1880
+ updated: number[];
1881
+ removed: number[];
1882
+ }) => void): void;
1883
+ off(event: 'update' | 'change', handler: (changes: {
1884
+ added: number[];
1885
+ updated: number[];
1886
+ removed: number[];
1887
+ }) => void): void;
1888
+ private emit;
1889
+ /**
1890
+ * Encode awareness update for specific clients
1891
+ */
1892
+ encodeUpdate(clientIds?: number[]): Uint8Array;
1893
+ /**
1894
+ * Apply awareness update from another client
1895
+ */
1896
+ applyUpdate(data: Uint8Array): {
1897
+ added: number[];
1898
+ updated: number[];
1899
+ removed: number[];
1900
+ };
1901
+ /**
1902
+ * Remove awareness states for disconnected clients
1903
+ */
1904
+ removeStates(clientIds: number[]): void;
1905
+ }
1906
+ /**
1907
+ * Encode a full awareness message
1908
+ */
1909
+ declare function encodeAwarenessMessage(awareness: Awareness, clientIds?: number[]): Uint8Array;
1910
+ /**
1911
+ * Decode and apply awareness message
1912
+ */
1913
+ declare function handleAwarenessMessage(awareness: Awareness, data: Uint8Array): {
1914
+ added: number[];
1915
+ updated: number[];
1916
+ removed: number[];
1917
+ };
1918
+ /**
1919
+ * Get message type from raw data
1920
+ */
1921
+ declare function getMessageType(data: Uint8Array): number;
1922
+
1923
+ /**
1924
+ * Tools API Types and Definitions
1925
+ *
1926
+ * MCP-like interface for agent tool calls.
1927
+ * Built-in tools for storage and backup operations.
1928
+ */
1929
+ interface ToolSchema {
1930
+ name: string;
1931
+ description: string;
1932
+ params: Record<string, {
1933
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array';
1934
+ description: string;
1935
+ required?: boolean;
1936
+ default?: unknown;
1937
+ }>;
1938
+ }
1939
+ /**
1940
+ * Discriminated union so callers don't have to guard on `error` being
1941
+ * defined when `success` is false — TS enforces the invariant.
1942
+ */
1943
+ type ToolResult = {
1944
+ success: true;
1945
+ data?: unknown;
1946
+ } | {
1947
+ success: false;
1948
+ error: string;
1949
+ };
1950
+ /**
1951
+ * Built-in tool definitions for storage, record, schema, user, and backup operations
1952
+ */
1953
+ declare const BUILT_IN_TOOLS: ToolSchema[];
1954
+
1955
+ /**
1956
+ * Shared Scoped R2 Files Handler
1957
+ *
1958
+ * Provides a secure, prefix-scoped R2 files API that enforces:
1959
+ * 1. All R2 keys are validated against the resolved prefix (no bypass)
1960
+ * 2. Path traversal (`..`, `.`) is rejected
1961
+ * 3. Mutations (upload/delete) require authentication by default
1962
+ *
1963
+ * Each worker provides a `resolvePrefix` callback for its scoping rules.
1964
+ * The security invariants are enforced here once — not per-worker.
1965
+ *
1966
+ * Routes:
1967
+ * POST /api/files/upload → upload (prefix + generated key)
1968
+ * GET /api/files → list (prefix + optional user prefix)
1969
+ * GET /api/files/:key → download (validated against prefix)
1970
+ * DELETE /api/files/:key → delete (validated against prefix)
1971
+ */
1972
+ interface ScopeContext {
1973
+ userId: string | null;
1974
+ url: URL;
1975
+ }
1976
+ type PrefixResult = {
1977
+ prefix: string;
1978
+ error?: undefined;
1979
+ } | {
1980
+ prefix?: undefined;
1981
+ error: string;
1982
+ };
1983
+ interface ScopedR2Config {
1984
+ /**
1985
+ * Resolve the R2 key prefix for the given scope.
1986
+ * Called with the `?scope=` query param value (default: 'self').
1987
+ */
1988
+ resolvePrefix: (scope: string, ctx: ScopeContext) => PrefixResult;
1989
+ /**
1990
+ * Require a non-null userId for upload and delete.
1991
+ * @default true
1992
+ */
1993
+ requireAuthForMutations?: boolean;
1994
+ }
1995
+ interface ScopedR2Auth {
1996
+ userId: string | null;
1997
+ }
1998
+ type ScopedR2Handler = (request: Request, url: URL, bucket: R2Bucket, auth: ScopedR2Auth) => Promise<Response>;
1999
+ /**
2000
+ * Create a scoped R2 files handler.
2001
+ *
2002
+ * Security guarantees:
2003
+ * - Download/delete keys are validated to start with the resolved prefix
2004
+ * - Path traversal (`..`) is rejected at the entry point
2005
+ * - Mutations require a non-null userId by default
2006
+ *
2007
+ * @returns A handler function: `(request, url, bucket, auth) => Promise<Response>`
2008
+ */
2009
+ declare function createScopedR2Handler(config: ScopedR2Config): ScopedR2Handler;
2010
+
2011
+ /**
2012
+ * Server Action Types
2013
+ *
2014
+ * Types for app-defined server actions that run in the site worker.
2015
+ * Actions bypass user RBAC via the X-App-Action header — the app's
2016
+ * server-side code IS the trust boundary.
2017
+ */
2018
+ interface ActionResult {
2019
+ success: boolean;
2020
+ data?: unknown;
2021
+ error?: string;
2022
+ }
2023
+ interface ActionTools {
2024
+ create(collection: string, data: Record<string, unknown>): Promise<ActionResult>;
2025
+ update(collection: string, recordId: string, data: Record<string, unknown>): Promise<ActionResult>;
2026
+ remove(collection: string, recordId: string): Promise<ActionResult>;
2027
+ get(collection: string, recordId: string): Promise<ActionResult>;
2028
+ query(collection: string, options?: {
2029
+ where?: Record<string, unknown>;
2030
+ orderBy?: string;
2031
+ orderDir?: 'asc' | 'desc';
2032
+ limit?: number;
2033
+ }): Promise<ActionResult>;
2034
+ /** Call an integration endpoint (e.g. 'openai/completions') via the API worker */
2035
+ integration(endpoint: string, data?: unknown): Promise<ActionResult>;
2036
+ }
2037
+ /**
2038
+ * `TEnv` lets apps type the worker-scoped env object passed to the
2039
+ * action handler. Defaults to a loose `Record<string, unknown>` so
2040
+ * unparameterized handlers still compile; apps that want strict typing
2041
+ * can do `ActionHandler<Env>` where Env is their own worker's bindings
2042
+ * interface.
2043
+ */
2044
+ interface ActionContext<TEnv = Record<string, unknown>> {
2045
+ userId: string;
2046
+ params: Record<string, unknown>;
2047
+ tools: ActionTools;
2048
+ /**
2049
+ * The worker's env bindings. Used by actions that need access to
2050
+ * secrets, bindings, or platform-injected values like `OWNER_USER_ID`
2051
+ * (e.g. for owner-only action gating).
2052
+ */
2053
+ env: TEnv;
2054
+ }
2055
+ type ActionHandler<TEnv = Record<string, unknown>> = (ctx: ActionContext<TEnv>) => Promise<ActionResult>;
2056
+
2057
+ /**
2058
+ * Cron System — Server-Side Scheduled Tasks
2059
+ *
2060
+ * Provides CronContext for miniapp cron handlers and buildCronContext
2061
+ * to construct it from worker environment bindings.
2062
+ *
2063
+ * CronContext gives handlers access to:
2064
+ * - records: Query/create/update/delete via RecordRoom tools API
2065
+ * - integrations: Call platform integration endpoints (billed to owner)
2066
+ * - ownerUserId: The app owner's user ID
2067
+ */
2068
+ /** Context passed to cron handler functions */
2069
+ interface CronContext {
2070
+ /** RecordRoom data access (queries the DO directly via tools API) */
2071
+ records: {
2072
+ query(collection: string, opts?: {
2073
+ where?: Record<string, unknown>;
2074
+ limit?: number;
2075
+ }): Promise<any[]>;
2076
+ create(collection: string, data: Record<string, unknown>): Promise<any>;
2077
+ update(collection: string, recordId: string, data: Record<string, unknown>): Promise<any>;
2078
+ delete(collection: string, recordId: string): Promise<any>;
2079
+ };
2080
+ /** Call platform integration endpoints, billed to owner */
2081
+ integrations: {
2082
+ call(endpoint: string, params: Record<string, unknown>): Promise<any>;
2083
+ };
2084
+ /** App owner's user ID */
2085
+ ownerUserId: string;
2086
+ }
2087
+ /** Environment bindings needed by buildCronContext */
2088
+ interface CronEnv {
2089
+ RECORD_ROOMS: DurableObjectNamespace;
2090
+ INTERNAL_STORAGE_HMAC_SECRET?: string;
2091
+ /** Override API base URL for local dev (defaults to https://api.deep.space) */
2092
+ API_BASE_URL?: string;
2093
+ }
2094
+ /**
2095
+ * Build a CronContext from worker environment bindings.
2096
+ *
2097
+ * @param env - Worker environment with RECORD_ROOMS DO namespace and HMAC secret
2098
+ * @param ownerUserId - App owner's user ID (for RBAC and billing)
2099
+ * @param roomId - RecordRoom ID (defaults to 'default')
2100
+ */
2101
+ declare function buildCronContext(env: CronEnv, ownerUserId: string, roomId?: string): CronContext;
2102
+
2103
+ /**
2104
+ * Upstream worker proxy helpers.
2105
+ *
2106
+ * App workers reach the platform's other workers (api, platform, auth) through
2107
+ * one of two transports:
2108
+ *
2109
+ * 1. **Service binding** (`env.API_WORKER` / `env.PLATFORM_WORKER`) — the
2110
+ * preferred path in production. Configured via wrangler `[[services]]`.
2111
+ * Cross-worker calls over plain `*.workers.dev` URLs return Cloudflare
2112
+ * error 1042 in production, so the binding is the only working path
2113
+ * in deployed apps.
2114
+ *
2115
+ * 2. **HTTPS URL** (`env.API_WORKER_URL` / `env.PLATFORM_WORKER_URL`) — the
2116
+ * fallback used in local development. `deepspace dev` writes these
2117
+ * into `.dev.vars`, which `wrangler dev` exposes as env vars. Service
2118
+ * bindings don't work cross-process under `wrangler dev` for SDK apps,
2119
+ * so the URL is the only working path in dev.
2120
+ *
2121
+ * The auth-worker has no service binding even in production — its responses
2122
+ * carry `Set-Cookie` headers we want preserved verbatim, which we get for
2123
+ * free over plain HTTPS. So `authWorkerFetch` is URL-only; the helper exists
2124
+ * for surface consistency, not to switch transports.
2125
+ *
2126
+ * Each helper:
2127
+ * - Prefers the binding if present, falls back to the URL otherwise.
2128
+ * - Throws an actionable Error if neither is configured. No silent 502s.
2129
+ * - Forwards `init` (method/headers/body) verbatim to the upstream worker.
2130
+ *
2131
+ * History: a previous in-tree helper folded the binding/URL fallback into
2132
+ * the AI module's `resolveTransport`. Inline call sites in the starter
2133
+ * template (integrations, files, debug) used `c.env.X.fetch(...)` directly,
2134
+ * which broke `npx deepspace dev` for any app calling those routes — the
2135
+ * binding is undefined locally, so the fetch threw. These helpers
2136
+ * standardize on the same shape `resolveTransport` had, so every upstream
2137
+ * call works in both dev and prod.
2138
+ */
2139
+ /**
2140
+ * Env shape required by `apiWorkerFetch`. App workers should extend this
2141
+ * (the starter template does) so the helper can be called with `c.env`.
2142
+ */
2143
+ interface ApiWorkerEnv {
2144
+ /** Cloudflare service binding for the api-worker. Preferred. */
2145
+ API_WORKER?: Fetcher;
2146
+ /** HTTPS URL for the api-worker. Used when the binding is absent (dev). */
2147
+ API_WORKER_URL?: string;
2148
+ }
2149
+ /** Env shape required by `platformWorkerFetch`. */
2150
+ interface PlatformWorkerEnv {
2151
+ /** Cloudflare service binding for the platform-worker. Preferred. */
2152
+ PLATFORM_WORKER?: Fetcher;
2153
+ /** HTTPS URL for the platform-worker. Used when the binding is absent. */
2154
+ PLATFORM_WORKER_URL?: string;
2155
+ }
2156
+ /** Env shape required by `authWorkerFetch`. URL-only. */
2157
+ interface AuthWorkerEnv {
2158
+ /** HTTPS URL for the auth-worker. Always required. */
2159
+ AUTH_WORKER_URL?: string;
2160
+ }
2161
+ /**
2162
+ * Fetch the api-worker. Prefers the `API_WORKER` service binding, falls
2163
+ * back to `API_WORKER_URL` over HTTPS.
2164
+ *
2165
+ * `path` is treated as path-only — any host in a passed-in URL is
2166
+ * stripped and replaced. This matches how `c.env.API_WORKER.fetch(...)`
2167
+ * already worked in the starter template (the host was always a
2168
+ * placeholder like `api-worker`).
2169
+ */
2170
+ declare function apiWorkerFetch(env: ApiWorkerEnv, path: string, init?: RequestInit): Promise<Response>;
2171
+ /**
2172
+ * Fetch the platform-worker. Prefers the `PLATFORM_WORKER` service binding,
2173
+ * falls back to `PLATFORM_WORKER_URL` over HTTPS.
2174
+ *
2175
+ * Accepts a `Request` instance directly so callers can hand off
2176
+ * `c.req.raw`-derived requests with their original method/headers/body
2177
+ * intact. (`/api/files/*` does this — it forwards the caller's body
2178
+ * stream verbatim.)
2179
+ */
2180
+ declare function platformWorkerFetch(env: PlatformWorkerEnv, pathOrRequest: string | Request, init?: RequestInit): Promise<Response>;
2181
+ /**
2182
+ * Fetch the auth-worker over HTTPS. URL-only — there is no auth-worker
2183
+ * service binding, by design (we want plain-HTTP cookie semantics).
2184
+ *
2185
+ * Kept as a helper for surface symmetry with `apiWorkerFetch` /
2186
+ * `platformWorkerFetch`. Throws if `AUTH_WORKER_URL` is unset.
2187
+ */
2188
+ declare function authWorkerFetch(env: AuthWorkerEnv, path: string, init?: RequestInit): Promise<Response>;
2189
+
2190
+ type Provider = 'anthropic' | 'openai' | 'cerebras';
2191
+ interface DeepSpaceAIEnv extends ApiWorkerEnv {
2192
+ /**
2193
+ * Long-lived owner-scoped JWT minted at deploy time (or by `deepspace dev`).
2194
+ * Used as the default proxy auth token when `options.authToken` is absent.
2195
+ * Bills the app owner.
2196
+ */
2197
+ APP_OWNER_JWT?: string;
2198
+ }
2199
+ interface DeepSpaceAIOptions {
2200
+ /**
2201
+ * Explicit auth token for this call. Use this for user-initiated flows
2202
+ * where the caller's own JWT should be billed. If omitted, the helper
2203
+ * falls back to `env.APP_OWNER_JWT` (bills the app owner).
2204
+ *
2205
+ * Billing is always against the JWT subject — to bill a different user,
2206
+ * pass a JWT whose subject is that user. The proxy does not accept any
2207
+ * client-supplied billing override.
2208
+ */
2209
+ authToken?: string;
2210
+ }
2211
+ /**
2212
+ * Build an AI SDK provider that routes through the DeepSpace API worker.
2213
+ *
2214
+ * Resolves the transport (service binding or URL) and the auth token
2215
+ * (explicit or `env.APP_OWNER_JWT`) automatically. Throws a clear error if
2216
+ * either is unconfigured.
2217
+ */
2218
+ declare function createDeepSpaceAI(env: DeepSpaceAIEnv, provider: Provider, options?: DeepSpaceAIOptions): _ai_sdk_anthropic.AnthropicProvider | _ai_sdk_openai.OpenAIProvider | _ai_sdk_openai_compatible.OpenAICompatibleProvider<string, string, string, string>;
2219
+
2220
+ /**
2221
+ * Auth types for the DeepSpace SDK.
2222
+ *
2223
+ * Provider-agnostic shapes for JWT verification (issuer, audience, azp
2224
+ * matching, ES256 public key) and the HMAC-signed internal-request
2225
+ * envelope used for worker-to-worker calls.
2226
+ */
2227
+ interface JwtVerifierConfig {
2228
+ /** PEM-encoded public key (ES256) for JWT verification */
2229
+ publicKey: string;
2230
+ /** Expected issuer (e.g. "https://auth.deep.space") */
2231
+ issuer: string;
2232
+ /** Expected audience (e.g. "https://api.deep.space") */
2233
+ audience?: string | string[];
2234
+ /** Allowed origins / authorized parties (supports wildcards like "https://*.app.space") */
2235
+ authorizedParties?: string[];
2236
+ /** Clock skew tolerance in milliseconds (default: 5000) */
2237
+ clockSkewMs?: number;
2238
+ }
2239
+ interface JwtClaims {
2240
+ sub: string;
2241
+ iss?: string;
2242
+ aud?: string | string[];
2243
+ azp?: string;
2244
+ exp?: number;
2245
+ iat?: number;
2246
+ name?: string;
2247
+ email?: string;
2248
+ image?: string;
2249
+ [key: string]: unknown;
2250
+ }
2251
+ interface VerifiedAuth {
2252
+ userId: string;
2253
+ claims: JwtClaims;
2254
+ }
2255
+ interface VerifyResult extends VerifiedAuth {
2256
+ }
2257
+ interface TokenDebugInfo {
2258
+ iss?: string | null;
2259
+ aud?: string | string[] | null;
2260
+ azp?: string | null;
2261
+ exp?: number | null;
2262
+ iat?: number | null;
2263
+ }
2264
+ interface VerifyOutcome {
2265
+ result: VerifyResult | null;
2266
+ debug?: TokenDebugInfo;
2267
+ error?: unknown;
2268
+ }
2269
+ interface InternalSignature {
2270
+ timestamp: string;
2271
+ signature: string;
2272
+ }
2273
+ interface VerifyInternalSignatureInput {
2274
+ secret: string | undefined;
2275
+ timestamp: string | null | undefined;
2276
+ signature: string | null | undefined;
2277
+ payload: string;
2278
+ maxSkewMs?: number;
2279
+ }
2280
+ interface SignInternalPayloadInput {
2281
+ secret: string;
2282
+ payload: string;
2283
+ timestamp?: string;
2284
+ }
2285
+
2286
+ /**
2287
+ * JWT verification for DeepSpace workers.
2288
+ *
2289
+ * jose-based ES256 verification (jose runs on the Cloudflare Workers
2290
+ * edge runtime). Imported public keys are cached per-PEM to avoid
2291
+ * re-importing on every request, and `azp` is matched against an
2292
+ * optional list of authorized-party patterns supporting `*` wildcards.
2293
+ */
2294
+
2295
+ /**
2296
+ * Verify a DeepSpace JWT token.
2297
+ *
2298
+ * @param config - Verification configuration (public key, issuer, audience)
2299
+ * @param token - The JWT string to verify
2300
+ * @returns VerifyOutcome with either the verified result or error details
2301
+ */
2302
+ declare function verifyJwt(config: JwtVerifierConfig, token: string | null | undefined): Promise<VerifyOutcome>;
2303
+
2304
+ /**
2305
+ * HMAC-based internal authentication for service-to-service calls.
2306
+ *
2307
+ * Ported as-is from Miyagi3 — this is auth-provider-agnostic.
2308
+ */
2309
+
2310
+ declare const DEFAULT_MAX_SKEW_MS: number;
2311
+ declare function computeHmacHex(secret: string, payload: string): Promise<string>;
2312
+ declare function timingSafeEqualHex(a: string, b: string): Promise<boolean>;
2313
+ declare function signInternalPayload({ secret, payload, timestamp, }: SignInternalPayloadInput): Promise<InternalSignature>;
2314
+ declare function verifyInternalSignature({ secret, timestamp, signature, payload, maxSkewMs, }: VerifyInternalSignatureInput): Promise<boolean>;
2315
+ declare function buildInternalPayload(body: unknown): string;
2316
+
2317
+ declare function decodeJwtPayload(token: string | null | undefined): TokenDebugInfo | undefined;
2318
+
2319
+ /**
2320
+ * Better Auth configuration factory for DeepSpace
2321
+ *
2322
+ * Provides pre-configured Better Auth instances for Cloudflare Workers + D1.
2323
+ */
2324
+ interface DeepSpaceAuthConfig {
2325
+ /** D1 database binding */
2326
+ database: D1Database;
2327
+ /** Base URL for the auth worker (e.g. "https://auth.deep.space") */
2328
+ baseURL: string;
2329
+ /** Secret for session signing */
2330
+ secret: string;
2331
+ /** Google OAuth credentials (optional) */
2332
+ google?: {
2333
+ clientId: string;
2334
+ clientSecret: string;
2335
+ };
2336
+ /** GitHub OAuth credentials (optional) */
2337
+ github?: {
2338
+ clientId: string;
2339
+ clientSecret: string;
2340
+ };
2341
+ /** Enable email/password authentication */
2342
+ emailAndPassword?: boolean;
2343
+ /** Trusted origins for CORS */
2344
+ trustedOrigins?: string[];
2345
+ }
2346
+ /**
2347
+ * Create a Better Auth instance configured for DeepSpace.
2348
+ *
2349
+ * This is called per-request in the auth worker since D1 bindings
2350
+ * are request-scoped in Cloudflare Workers.
2351
+ */
2352
+ declare function createDeepSpaceAuth(config: DeepSpaceAuthConfig): better_auth.Auth<{
2353
+ database: D1Database;
2354
+ baseURL: string;
2355
+ secret: string;
2356
+ emailAndPassword: {
2357
+ enabled: boolean;
2358
+ };
2359
+ socialProviders: Record<string, {
2360
+ clientId: string;
2361
+ clientSecret: string;
2362
+ }>;
2363
+ trustedOrigins: string[];
2364
+ plugins: [{
2365
+ id: "organization";
2366
+ endpoints: better_auth_plugins.OrganizationEndpoints<better_auth_plugins.OrganizationOptions & {
2367
+ teams: {
2368
+ enabled: true;
2369
+ };
2370
+ dynamicAccessControl?: {
2371
+ enabled?: false | undefined;
2372
+ } | undefined;
2373
+ }> & better_auth_plugins.TeamEndpoints<better_auth_plugins.OrganizationOptions & {
2374
+ teams: {
2375
+ enabled: true;
2376
+ };
2377
+ dynamicAccessControl?: {
2378
+ enabled?: false | undefined;
2379
+ } | undefined;
2380
+ }>;
2381
+ schema: better_auth_plugins.OrganizationSchema<better_auth_plugins.OrganizationOptions & {
2382
+ teams: {
2383
+ enabled: true;
2384
+ };
2385
+ dynamicAccessControl?: {
2386
+ enabled?: false | undefined;
2387
+ } | undefined;
2388
+ }>;
2389
+ $Infer: {
2390
+ Organization: {
2391
+ id: string;
2392
+ name: string;
2393
+ slug: string;
2394
+ createdAt: Date;
2395
+ logo?: string | null | undefined;
2396
+ metadata?: any;
2397
+ };
2398
+ Invitation: {
2399
+ id: string;
2400
+ organizationId: string;
2401
+ email: string;
2402
+ role: "member" | "admin" | "owner";
2403
+ status: better_auth_plugins.InvitationStatus;
2404
+ inviterId: string;
2405
+ expiresAt: Date;
2406
+ createdAt: Date;
2407
+ teamId?: string | undefined | undefined;
2408
+ };
2409
+ Member: {
2410
+ id: string;
2411
+ organizationId: string;
2412
+ role: "member" | "admin" | "owner";
2413
+ createdAt: Date;
2414
+ userId: string;
2415
+ teamId?: string | undefined | undefined;
2416
+ user: {
2417
+ id: string;
2418
+ email: string;
2419
+ name: string;
2420
+ image?: string | undefined;
2421
+ };
2422
+ };
2423
+ Team: {
2424
+ id: string;
2425
+ name: string;
2426
+ organizationId: string;
2427
+ createdAt: Date;
2428
+ updatedAt?: Date | undefined;
2429
+ };
2430
+ TeamMember: {
2431
+ id: string;
2432
+ teamId: string;
2433
+ userId: string;
2434
+ createdAt: Date;
2435
+ };
2436
+ ActiveOrganization: {
2437
+ members: {
2438
+ id: string;
2439
+ organizationId: string;
2440
+ role: "member" | "admin" | "owner";
2441
+ createdAt: Date;
2442
+ userId: string;
2443
+ teamId?: string | undefined | undefined;
2444
+ user: {
2445
+ id: string;
2446
+ email: string;
2447
+ name: string;
2448
+ image?: string | undefined;
2449
+ };
2450
+ }[];
2451
+ invitations: {
2452
+ id: string;
2453
+ organizationId: string;
2454
+ email: string;
2455
+ role: "member" | "admin" | "owner";
2456
+ status: better_auth_plugins.InvitationStatus;
2457
+ inviterId: string;
2458
+ expiresAt: Date;
2459
+ createdAt: Date;
2460
+ teamId?: string | undefined | undefined;
2461
+ }[];
2462
+ teams: {
2463
+ id: string;
2464
+ name: string;
2465
+ organizationId: string;
2466
+ createdAt: Date;
2467
+ updatedAt?: Date | undefined;
2468
+ }[];
2469
+ } & {
2470
+ id: string;
2471
+ name: string;
2472
+ slug: string;
2473
+ createdAt: Date;
2474
+ logo?: string | null | undefined;
2475
+ metadata?: any;
2476
+ };
2477
+ };
2478
+ $ERROR_CODES: {
2479
+ YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_ORGANIZATION">;
2480
+ YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS: better_auth.RawError<"YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_ORGANIZATIONS">;
2481
+ ORGANIZATION_ALREADY_EXISTS: better_auth.RawError<"ORGANIZATION_ALREADY_EXISTS">;
2482
+ ORGANIZATION_SLUG_ALREADY_TAKEN: better_auth.RawError<"ORGANIZATION_SLUG_ALREADY_TAKEN">;
2483
+ ORGANIZATION_NOT_FOUND: better_auth.RawError<"ORGANIZATION_NOT_FOUND">;
2484
+ USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION: better_auth.RawError<"USER_IS_NOT_A_MEMBER_OF_THE_ORGANIZATION">;
2485
+ YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_ORGANIZATION">;
2486
+ YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_ORGANIZATION">;
2487
+ NO_ACTIVE_ORGANIZATION: better_auth.RawError<"NO_ACTIVE_ORGANIZATION">;
2488
+ USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION: better_auth.RawError<"USER_IS_ALREADY_A_MEMBER_OF_THIS_ORGANIZATION">;
2489
+ MEMBER_NOT_FOUND: better_auth.RawError<"MEMBER_NOT_FOUND">;
2490
+ ROLE_NOT_FOUND: better_auth.RawError<"ROLE_NOT_FOUND">;
2491
+ YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM">;
2492
+ TEAM_ALREADY_EXISTS: better_auth.RawError<"TEAM_ALREADY_EXISTS">;
2493
+ TEAM_NOT_FOUND: better_auth.RawError<"TEAM_NOT_FOUND">;
2494
+ YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER: better_auth.RawError<"YOU_CANNOT_LEAVE_THE_ORGANIZATION_AS_THE_ONLY_OWNER">;
2495
+ YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER: better_auth.RawError<"YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER">;
2496
+ YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_MEMBER">;
2497
+ YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_INVITE_USERS_TO_THIS_ORGANIZATION">;
2498
+ USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION: better_auth.RawError<"USER_IS_ALREADY_INVITED_TO_THIS_ORGANIZATION">;
2499
+ INVITATION_NOT_FOUND: better_auth.RawError<"INVITATION_NOT_FOUND">;
2500
+ YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION: better_auth.RawError<"YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION">;
2501
+ EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION: better_auth.RawError<"EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION">;
2502
+ YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_CANCEL_THIS_INVITATION">;
2503
+ INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION: better_auth.RawError<"INVITER_IS_NO_LONGER_A_MEMBER_OF_THE_ORGANIZATION">;
2504
+ YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_INVITE_USER_WITH_THIS_ROLE">;
2505
+ FAILED_TO_RETRIEVE_INVITATION: better_auth.RawError<"FAILED_TO_RETRIEVE_INVITATION">;
2506
+ YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS: better_auth.RawError<"YOU_HAVE_REACHED_THE_MAXIMUM_NUMBER_OF_TEAMS">;
2507
+ UNABLE_TO_REMOVE_LAST_TEAM: better_auth.RawError<"UNABLE_TO_REMOVE_LAST_TEAM">;
2508
+ YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_MEMBER">;
2509
+ ORGANIZATION_MEMBERSHIP_LIMIT_REACHED: better_auth.RawError<"ORGANIZATION_MEMBERSHIP_LIMIT_REACHED">;
2510
+ YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_CREATE_TEAMS_IN_THIS_ORGANIZATION">;
2511
+ YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_DELETE_TEAMS_IN_THIS_ORGANIZATION">;
2512
+ YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_UPDATE_THIS_TEAM">;
2513
+ YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_DELETE_THIS_TEAM">;
2514
+ INVITATION_LIMIT_REACHED: better_auth.RawError<"INVITATION_LIMIT_REACHED">;
2515
+ TEAM_MEMBER_LIMIT_REACHED: better_auth.RawError<"TEAM_MEMBER_LIMIT_REACHED">;
2516
+ USER_IS_NOT_A_MEMBER_OF_THE_TEAM: better_auth.RawError<"USER_IS_NOT_A_MEMBER_OF_THE_TEAM">;
2517
+ YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM: better_auth.RawError<"YOU_CAN_NOT_ACCESS_THE_MEMBERS_OF_THIS_TEAM">;
2518
+ YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM: better_auth.RawError<"YOU_DO_NOT_HAVE_AN_ACTIVE_TEAM">;
2519
+ YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_CREATE_A_NEW_TEAM_MEMBER">;
2520
+ YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_REMOVE_A_TEAM_MEMBER">;
2521
+ YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_ACCESS_THIS_ORGANIZATION">;
2522
+ YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION: better_auth.RawError<"YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION">;
2523
+ MISSING_AC_INSTANCE: better_auth.RawError<"MISSING_AC_INSTANCE">;
2524
+ YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE: better_auth.RawError<"YOU_MUST_BE_IN_AN_ORGANIZATION_TO_CREATE_A_ROLE">;
2525
+ YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_CREATE_A_ROLE">;
2526
+ YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_UPDATE_A_ROLE">;
2527
+ YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_DELETE_A_ROLE">;
2528
+ YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_READ_A_ROLE">;
2529
+ YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_LIST_A_ROLE">;
2530
+ YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE: better_auth.RawError<"YOU_ARE_NOT_ALLOWED_TO_GET_A_ROLE">;
2531
+ TOO_MANY_ROLES: better_auth.RawError<"TOO_MANY_ROLES">;
2532
+ INVALID_RESOURCE: better_auth.RawError<"INVALID_RESOURCE">;
2533
+ ROLE_NAME_IS_ALREADY_TAKEN: better_auth.RawError<"ROLE_NAME_IS_ALREADY_TAKEN">;
2534
+ CANNOT_DELETE_A_PRE_DEFINED_ROLE: better_auth.RawError<"CANNOT_DELETE_A_PRE_DEFINED_ROLE">;
2535
+ ROLE_IS_ASSIGNED_TO_MEMBERS: better_auth.RawError<"ROLE_IS_ASSIGNED_TO_MEMBERS">;
2536
+ };
2537
+ options: NoInfer<better_auth_plugins.OrganizationOptions & {
2538
+ teams: {
2539
+ enabled: true;
2540
+ };
2541
+ dynamicAccessControl?: {
2542
+ enabled?: false | undefined;
2543
+ } | undefined;
2544
+ }>;
2545
+ }, {
2546
+ id: "two-factor";
2547
+ endpoints: {
2548
+ enableTwoFactor: better_auth.StrictEndpoint<"/two-factor/enable", {
2549
+ method: "POST";
2550
+ body: better_auth.ZodObject<{
2551
+ password: better_auth.ZodString;
2552
+ issuer: better_auth.ZodOptional<better_auth.ZodString>;
2553
+ }, better_auth.$strip>;
2554
+ use: ((inputContext: better_auth.MiddlewareInputContext<better_auth.MiddlewareOptions>) => Promise<{
2555
+ session: {
2556
+ session: Record<string, any> & {
2557
+ id: string;
2558
+ createdAt: Date;
2559
+ updatedAt: Date;
2560
+ userId: string;
2561
+ expiresAt: Date;
2562
+ token: string;
2563
+ ipAddress?: string | null | undefined;
2564
+ userAgent?: string | null | undefined;
2565
+ };
2566
+ user: Record<string, any> & {
2567
+ id: string;
2568
+ createdAt: Date;
2569
+ updatedAt: Date;
2570
+ email: string;
2571
+ emailVerified: boolean;
2572
+ name: string;
2573
+ image?: string | null | undefined;
2574
+ };
2575
+ };
2576
+ }>)[];
2577
+ metadata: {
2578
+ openapi: {
2579
+ summary: string;
2580
+ description: string;
2581
+ responses: {
2582
+ 200: {
2583
+ description: string;
2584
+ content: {
2585
+ "application/json": {
2586
+ schema: {
2587
+ type: "object";
2588
+ properties: {
2589
+ totpURI: {
2590
+ type: string;
2591
+ description: string;
2592
+ };
2593
+ backupCodes: {
2594
+ type: string;
2595
+ items: {
2596
+ type: string;
2597
+ };
2598
+ description: string;
2599
+ };
2600
+ };
2601
+ };
2602
+ };
2603
+ };
2604
+ };
2605
+ };
2606
+ };
2607
+ };
2608
+ }, {
2609
+ totpURI: string;
2610
+ backupCodes: string[];
2611
+ }>;
2612
+ disableTwoFactor: better_auth.StrictEndpoint<"/two-factor/disable", {
2613
+ method: "POST";
2614
+ body: better_auth.ZodObject<{
2615
+ password: better_auth.ZodString;
2616
+ }, better_auth.$strip>;
2617
+ use: ((inputContext: better_auth.MiddlewareInputContext<better_auth.MiddlewareOptions>) => Promise<{
2618
+ session: {
2619
+ session: Record<string, any> & {
2620
+ id: string;
2621
+ createdAt: Date;
2622
+ updatedAt: Date;
2623
+ userId: string;
2624
+ expiresAt: Date;
2625
+ token: string;
2626
+ ipAddress?: string | null | undefined;
2627
+ userAgent?: string | null | undefined;
2628
+ };
2629
+ user: Record<string, any> & {
2630
+ id: string;
2631
+ createdAt: Date;
2632
+ updatedAt: Date;
2633
+ email: string;
2634
+ emailVerified: boolean;
2635
+ name: string;
2636
+ image?: string | null | undefined;
2637
+ };
2638
+ };
2639
+ }>)[];
2640
+ metadata: {
2641
+ openapi: {
2642
+ summary: string;
2643
+ description: string;
2644
+ responses: {
2645
+ 200: {
2646
+ description: string;
2647
+ content: {
2648
+ "application/json": {
2649
+ schema: {
2650
+ type: "object";
2651
+ properties: {
2652
+ status: {
2653
+ type: string;
2654
+ };
2655
+ };
2656
+ };
2657
+ };
2658
+ };
2659
+ };
2660
+ };
2661
+ };
2662
+ };
2663
+ }, {
2664
+ status: boolean;
2665
+ }>;
2666
+ verifyBackupCode: better_auth.StrictEndpoint<"/two-factor/verify-backup-code", {
2667
+ method: "POST";
2668
+ body: better_auth.ZodObject<{
2669
+ code: better_auth.ZodString;
2670
+ disableSession: better_auth.ZodOptional<better_auth.ZodBoolean>;
2671
+ trustDevice: better_auth.ZodOptional<better_auth.ZodBoolean>;
2672
+ }, better_auth.$strip>;
2673
+ metadata: {
2674
+ openapi: {
2675
+ description: string;
2676
+ responses: {
2677
+ "200": {
2678
+ description: string;
2679
+ content: {
2680
+ "application/json": {
2681
+ schema: {
2682
+ type: "object";
2683
+ properties: {
2684
+ user: {
2685
+ type: string;
2686
+ properties: {
2687
+ id: {
2688
+ type: string;
2689
+ description: string;
2690
+ };
2691
+ email: {
2692
+ type: string;
2693
+ format: string;
2694
+ nullable: boolean;
2695
+ description: string;
2696
+ };
2697
+ emailVerified: {
2698
+ type: string;
2699
+ nullable: boolean;
2700
+ description: string;
2701
+ };
2702
+ name: {
2703
+ type: string;
2704
+ nullable: boolean;
2705
+ description: string;
2706
+ };
2707
+ image: {
2708
+ type: string;
2709
+ format: string;
2710
+ nullable: boolean;
2711
+ description: string;
2712
+ };
2713
+ twoFactorEnabled: {
2714
+ type: string;
2715
+ description: string;
2716
+ };
2717
+ createdAt: {
2718
+ type: string;
2719
+ format: string;
2720
+ description: string;
2721
+ };
2722
+ updatedAt: {
2723
+ type: string;
2724
+ format: string;
2725
+ description: string;
2726
+ };
2727
+ };
2728
+ required: string[];
2729
+ description: string;
2730
+ };
2731
+ session: {
2732
+ type: string;
2733
+ properties: {
2734
+ token: {
2735
+ type: string;
2736
+ description: string;
2737
+ };
2738
+ userId: {
2739
+ type: string;
2740
+ description: string;
2741
+ };
2742
+ createdAt: {
2743
+ type: string;
2744
+ format: string;
2745
+ description: string;
2746
+ };
2747
+ expiresAt: {
2748
+ type: string;
2749
+ format: string;
2750
+ description: string;
2751
+ };
2752
+ };
2753
+ required: string[];
2754
+ description: string;
2755
+ };
2756
+ };
2757
+ required: string[];
2758
+ };
2759
+ };
2760
+ };
2761
+ };
2762
+ };
2763
+ };
2764
+ };
2765
+ }, {
2766
+ token: string | undefined;
2767
+ user: (Record<string, any> & {
2768
+ id: string;
2769
+ createdAt: Date;
2770
+ updatedAt: Date;
2771
+ email: string;
2772
+ emailVerified: boolean;
2773
+ name: string;
2774
+ image?: string | null | undefined;
2775
+ }) | better_auth_plugins.UserWithTwoFactor;
2776
+ }>;
2777
+ generateBackupCodes: better_auth.StrictEndpoint<"/two-factor/generate-backup-codes", {
2778
+ method: "POST";
2779
+ body: better_auth.ZodObject<{
2780
+ password: better_auth.ZodString;
2781
+ }, better_auth.$strip>;
2782
+ use: ((inputContext: better_auth.MiddlewareInputContext<better_auth.MiddlewareOptions>) => Promise<{
2783
+ session: {
2784
+ session: Record<string, any> & {
2785
+ id: string;
2786
+ createdAt: Date;
2787
+ updatedAt: Date;
2788
+ userId: string;
2789
+ expiresAt: Date;
2790
+ token: string;
2791
+ ipAddress?: string | null | undefined;
2792
+ userAgent?: string | null | undefined;
2793
+ };
2794
+ user: Record<string, any> & {
2795
+ id: string;
2796
+ createdAt: Date;
2797
+ updatedAt: Date;
2798
+ email: string;
2799
+ emailVerified: boolean;
2800
+ name: string;
2801
+ image?: string | null | undefined;
2802
+ };
2803
+ };
2804
+ }>)[];
2805
+ metadata: {
2806
+ openapi: {
2807
+ description: string;
2808
+ responses: {
2809
+ "200": {
2810
+ description: string;
2811
+ content: {
2812
+ "application/json": {
2813
+ schema: {
2814
+ type: "object";
2815
+ properties: {
2816
+ status: {
2817
+ type: string;
2818
+ description: string;
2819
+ enum: boolean[];
2820
+ };
2821
+ backupCodes: {
2822
+ type: string;
2823
+ items: {
2824
+ type: string;
2825
+ };
2826
+ description: string;
2827
+ };
2828
+ };
2829
+ required: string[];
2830
+ };
2831
+ };
2832
+ };
2833
+ };
2834
+ };
2835
+ };
2836
+ };
2837
+ }, {
2838
+ status: boolean;
2839
+ backupCodes: string[];
2840
+ }>;
2841
+ viewBackupCodes: better_auth.StrictEndpoint<string, {
2842
+ method: "POST";
2843
+ body: better_auth.ZodObject<{
2844
+ userId: better_auth.ZodCoercedString<unknown>;
2845
+ }, better_auth.$strip>;
2846
+ }, {
2847
+ status: boolean;
2848
+ backupCodes: string[];
2849
+ }>;
2850
+ sendTwoFactorOTP: better_auth.StrictEndpoint<"/two-factor/send-otp", {
2851
+ method: "POST";
2852
+ body: better_auth.ZodOptional<better_auth.ZodObject<{
2853
+ trustDevice: better_auth.ZodOptional<better_auth.ZodBoolean>;
2854
+ }, better_auth.$strip>>;
2855
+ metadata: {
2856
+ openapi: {
2857
+ summary: string;
2858
+ description: string;
2859
+ responses: {
2860
+ 200: {
2861
+ description: string;
2862
+ content: {
2863
+ "application/json": {
2864
+ schema: {
2865
+ type: "object";
2866
+ properties: {
2867
+ status: {
2868
+ type: string;
2869
+ };
2870
+ };
2871
+ };
2872
+ };
2873
+ };
2874
+ };
2875
+ };
2876
+ };
2877
+ };
2878
+ }, {
2879
+ status: boolean;
2880
+ }>;
2881
+ verifyTwoFactorOTP: better_auth.StrictEndpoint<"/two-factor/verify-otp", {
2882
+ method: "POST";
2883
+ body: better_auth.ZodObject<{
2884
+ code: better_auth.ZodString;
2885
+ trustDevice: better_auth.ZodOptional<better_auth.ZodBoolean>;
2886
+ }, better_auth.$strip>;
2887
+ metadata: {
2888
+ openapi: {
2889
+ summary: string;
2890
+ description: string;
2891
+ responses: {
2892
+ "200": {
2893
+ description: string;
2894
+ content: {
2895
+ "application/json": {
2896
+ schema: {
2897
+ type: "object";
2898
+ properties: {
2899
+ token: {
2900
+ type: string;
2901
+ description: string;
2902
+ };
2903
+ user: {
2904
+ type: string;
2905
+ properties: {
2906
+ id: {
2907
+ type: string;
2908
+ description: string;
2909
+ };
2910
+ email: {
2911
+ type: string;
2912
+ format: string;
2913
+ nullable: boolean;
2914
+ description: string;
2915
+ };
2916
+ emailVerified: {
2917
+ type: string;
2918
+ nullable: boolean;
2919
+ description: string;
2920
+ };
2921
+ name: {
2922
+ type: string;
2923
+ nullable: boolean;
2924
+ description: string;
2925
+ };
2926
+ image: {
2927
+ type: string;
2928
+ format: string;
2929
+ nullable: boolean;
2930
+ description: string;
2931
+ };
2932
+ createdAt: {
2933
+ type: string;
2934
+ format: string;
2935
+ description: string;
2936
+ };
2937
+ updatedAt: {
2938
+ type: string;
2939
+ format: string;
2940
+ description: string;
2941
+ };
2942
+ };
2943
+ required: string[];
2944
+ description: string;
2945
+ };
2946
+ };
2947
+ required: string[];
2948
+ };
2949
+ };
2950
+ };
2951
+ };
2952
+ };
2953
+ };
2954
+ };
2955
+ }, {
2956
+ token: string;
2957
+ user: better_auth_plugins.UserWithTwoFactor;
2958
+ } | {
2959
+ token: string;
2960
+ user: Record<string, any> & {
2961
+ id: string;
2962
+ createdAt: Date;
2963
+ updatedAt: Date;
2964
+ email: string;
2965
+ emailVerified: boolean;
2966
+ name: string;
2967
+ image?: string | null | undefined;
2968
+ };
2969
+ }>;
2970
+ generateTOTP: better_auth.StrictEndpoint<string, {
2971
+ method: "POST";
2972
+ body: better_auth.ZodObject<{
2973
+ secret: better_auth.ZodString;
2974
+ }, better_auth.$strip>;
2975
+ metadata: {
2976
+ openapi: {
2977
+ summary: string;
2978
+ description: string;
2979
+ responses: {
2980
+ 200: {
2981
+ description: string;
2982
+ content: {
2983
+ "application/json": {
2984
+ schema: {
2985
+ type: "object";
2986
+ properties: {
2987
+ code: {
2988
+ type: string;
2989
+ };
2990
+ };
2991
+ };
2992
+ };
2993
+ };
2994
+ };
2995
+ };
2996
+ };
2997
+ };
2998
+ }, {
2999
+ code: string;
3000
+ }>;
3001
+ getTOTPURI: better_auth.StrictEndpoint<"/two-factor/get-totp-uri", {
3002
+ method: "POST";
3003
+ use: ((inputContext: better_auth.MiddlewareInputContext<better_auth.MiddlewareOptions>) => Promise<{
3004
+ session: {
3005
+ session: Record<string, any> & {
3006
+ id: string;
3007
+ createdAt: Date;
3008
+ updatedAt: Date;
3009
+ userId: string;
3010
+ expiresAt: Date;
3011
+ token: string;
3012
+ ipAddress?: string | null | undefined;
3013
+ userAgent?: string | null | undefined;
3014
+ };
3015
+ user: Record<string, any> & {
3016
+ id: string;
3017
+ createdAt: Date;
3018
+ updatedAt: Date;
3019
+ email: string;
3020
+ emailVerified: boolean;
3021
+ name: string;
3022
+ image?: string | null | undefined;
3023
+ };
3024
+ };
3025
+ }>)[];
3026
+ body: better_auth.ZodObject<{
3027
+ password: better_auth.ZodString;
3028
+ }, better_auth.$strip>;
3029
+ metadata: {
3030
+ openapi: {
3031
+ summary: string;
3032
+ description: string;
3033
+ responses: {
3034
+ 200: {
3035
+ description: string;
3036
+ content: {
3037
+ "application/json": {
3038
+ schema: {
3039
+ type: "object";
3040
+ properties: {
3041
+ totpURI: {
3042
+ type: string;
3043
+ };
3044
+ };
3045
+ };
3046
+ };
3047
+ };
3048
+ };
3049
+ };
3050
+ };
3051
+ };
3052
+ }, {
3053
+ totpURI: string;
3054
+ }>;
3055
+ verifyTOTP: better_auth.StrictEndpoint<"/two-factor/verify-totp", {
3056
+ method: "POST";
3057
+ body: better_auth.ZodObject<{
3058
+ code: better_auth.ZodString;
3059
+ trustDevice: better_auth.ZodOptional<better_auth.ZodBoolean>;
3060
+ }, better_auth.$strip>;
3061
+ metadata: {
3062
+ openapi: {
3063
+ summary: string;
3064
+ description: string;
3065
+ responses: {
3066
+ 200: {
3067
+ description: string;
3068
+ content: {
3069
+ "application/json": {
3070
+ schema: {
3071
+ type: "object";
3072
+ properties: {
3073
+ status: {
3074
+ type: string;
3075
+ };
3076
+ };
3077
+ };
3078
+ };
3079
+ };
3080
+ };
3081
+ };
3082
+ };
3083
+ };
3084
+ }, {
3085
+ token: string;
3086
+ user: better_auth_plugins.UserWithTwoFactor;
3087
+ } | {
3088
+ token: string;
3089
+ user: Record<string, any> & {
3090
+ id: string;
3091
+ createdAt: Date;
3092
+ updatedAt: Date;
3093
+ email: string;
3094
+ emailVerified: boolean;
3095
+ name: string;
3096
+ image?: string | null | undefined;
3097
+ };
3098
+ }>;
3099
+ };
3100
+ options: NoInfer<better_auth_plugins.TwoFactorOptions>;
3101
+ hooks: {
3102
+ after: {
3103
+ matcher(context: better_auth.HookEndpointContext): boolean;
3104
+ handler: (inputContext: better_auth.MiddlewareInputContext<better_auth.MiddlewareOptions>) => Promise<{
3105
+ twoFactorRedirect: boolean;
3106
+ } | undefined>;
3107
+ }[];
3108
+ };
3109
+ schema: {
3110
+ user: {
3111
+ fields: {
3112
+ twoFactorEnabled: {
3113
+ type: "boolean";
3114
+ required: false;
3115
+ defaultValue: false;
3116
+ input: false;
3117
+ };
3118
+ };
3119
+ };
3120
+ twoFactor: {
3121
+ fields: {
3122
+ secret: {
3123
+ type: "string";
3124
+ required: true;
3125
+ returned: false;
3126
+ index: true;
3127
+ };
3128
+ backupCodes: {
3129
+ type: "string";
3130
+ required: true;
3131
+ returned: false;
3132
+ };
3133
+ userId: {
3134
+ type: "string";
3135
+ required: true;
3136
+ returned: false;
3137
+ references: {
3138
+ model: string;
3139
+ field: string;
3140
+ };
3141
+ index: true;
3142
+ };
3143
+ };
3144
+ };
3145
+ };
3146
+ rateLimit: {
3147
+ pathMatcher(path: string): boolean;
3148
+ window: number;
3149
+ max: number;
3150
+ }[];
3151
+ $ERROR_CODES: {
3152
+ OTP_NOT_ENABLED: better_auth.RawError<"OTP_NOT_ENABLED">;
3153
+ OTP_HAS_EXPIRED: better_auth.RawError<"OTP_HAS_EXPIRED">;
3154
+ TOTP_NOT_ENABLED: better_auth.RawError<"TOTP_NOT_ENABLED">;
3155
+ TWO_FACTOR_NOT_ENABLED: better_auth.RawError<"TWO_FACTOR_NOT_ENABLED">;
3156
+ BACKUP_CODES_NOT_ENABLED: better_auth.RawError<"BACKUP_CODES_NOT_ENABLED">;
3157
+ INVALID_BACKUP_CODE: better_auth.RawError<"INVALID_BACKUP_CODE">;
3158
+ INVALID_CODE: better_auth.RawError<"INVALID_CODE">;
3159
+ TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: better_auth.RawError<"TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE">;
3160
+ INVALID_TWO_FACTOR_COOKIE: better_auth.RawError<"INVALID_TWO_FACTOR_COOKIE">;
3161
+ };
3162
+ }];
3163
+ }>;
3164
+ type DeepSpaceAuth = ReturnType<typeof createDeepSpaceAuth>;
3165
+
3166
+ /**
3167
+ * Yjs collaborative editing handlers for RecordRoom
3168
+ */
3169
+
3170
+ /** Collections that bypass schema checks (permissive access) */
3171
+ declare const SYSTEM_COLLECTIONS: Set<string>;
3172
+
3173
+ export { type ActionContext, type ActionHandler, type ActionResult, type ActionTools, type ApiWorkerEnv, type AuthWorkerEnv, Awareness, type AwarenessState, type AwarenessStates, BASE_USERS_SCHEMA, BUILT_IN_TOOLS, type BaseMessage, BaseRoom, CHANNELS_SCHEMA, CHANNEL_INVITATIONS_SCHEMA, CHANNEL_MEMBERS_SCHEMA, CONVERSATION_SCHEMAS, CanvasRoom, type CanvasShape, type ClientMessage, type CollectionPermissionSummary, type CollectionSchema, type ColumnDefinition, type ColumnInterpretation, type ConnectionAttachment, type ConvMemberData, type ConvMessageData, type ConvReactionData, type ConvReadCursorData, type ConvVoteData, type ConversationStateData, type CronContext, type CronExecution, CronRoom, type CronRoomConfig, type CronTask, DEFAULT_DO_MANIFEST, DEFAULT_MAX_SKEW_MS, DIRECTORY_SCHEMAS, type DOBindings, type DOManifest, type DOManifestEntry, type DeepSpaceAIEnv, type DeepSpaceAIOptions, type DeepSpaceAuth, type DeepSpaceAuthConfig, type DirectoryCommunityData, type DirectoryConversationData, type DirectoryMembershipData, type DirectoryPostData, GLOBAL_DO_TYPES, GLOBAL_DO_TYPE_NAMES, type GameInput, GameRoom, type GameRoomConfig, type GlobalDOType, type HandlerContext, type InternalSignature, type JwtClaims, type JwtVerifierConfig, MESSAGES_SCHEMA, MSG, MSG_AWARENESS, MSG_SYNC, MSG_SYNC_STEP1, MSG_SYNC_STEP2, MSG_SYNC_UPDATE, MSG_YJS_AWARENESS, MSG_YJS_SYNC, type MessageHandlers, type MsgType, type PermissionAnalysis, type PermissionContext, type PermissionLevel, type PermissionSource, type PlatformWorkerEnv, type Player, type PrefixResult, type PresencePeer, PresenceRoom, type ProtocolMessage, REACTIONS_SCHEMA, READ_RECEIPTS_SCHEMA, RESERVED_COLLECTION_NAMES, ROLE_ADMIN, ROLE_ANONYMOUS, ROLE_DEFAULT, RecordRoom, type RecordRoomConfig, type ResolvedColumn, type ResolvedPermission, type RolePermissions, SYSTEM_COLLECTIONS, SYSTEM_MANAGED_COLUMNS, SchemaRegistry, type ScopeContext, type ScopedR2Auth, type ScopedR2Config, type ScopedR2Handler, type ServerMessage, type SharedConnection, type SignInternalPayloadInput, type StoredRecord, type SyncResult, type TokenDebugInfo, type ToolResult, type ToolSchema, USERS_COLUMNS, type User, type UserAttachment, VOTING_SCHEMAS, type VerifiedAuth, type VerifyInternalSignatureInput, type VerifyOutcome, type VerifyResult, type Viewport, WORKSPACE_SCHEMAS, YjsRoom, analyzePermissions, apiWorkerFetch, authWorkerFetch, buildCronContext, buildInternalPayload, buildTableSelect, canCreate, canDelete, canRead, canUpdate, checkFieldPermissions, clientBuild, coerceValue, collectionTableName, columnId, computeHmacHex, createDecoder, createDeepSpaceAI, createDeepSpaceAuth, createEncoder, createScopedR2Handler, dataToColumnValues, decodeJwtPayload, dispatch, encode, encodeAwarenessMessage, encodeSyncStep1, encodeSyncStep2, encodeUpdate, getGlobalDOSchemas, getGlobalDOType, getMessageType, getRolePermissions, handleAwarenessMessage, handleSyncMessage, hasContent, isOwner, noopPermissionContext, platformWorkerFetch, readVarUint, readVarUint8Array, resolveColumn, rowToData, serverBuild, signInternalPayload, timingSafeEqualHex, toUint8Array, verifyInternalSignature, verifyJwt, workspaceAccountsSchema, workspaceContentSharesSchema, workspaceEmailHandlesSchema, workspaceFormResponsesSchema, workspacePeopleSchema, workspaceProjectsSchema, workspaceTagsSchema, workspaceTasksSchema, workspaceTeamMembersSchema, workspaceTeamsSchema, workspaceTransactionsSchema, writeVarUint, writeVarUint8Array };