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.
- package/dist/cli.js +1158 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +7148 -0
- package/dist/index.js +6382 -0
- package/dist/index.js.map +1 -0
- package/dist/worker.d.ts +3173 -0
- package/dist/worker.js +5374 -0
- package/dist/worker.js.map +1 -0
- package/package.json +121 -3
- package/index.js +0 -1
package/dist/worker.d.ts
ADDED
|
@@ -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 };
|