deepspace 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.d.ts CHANGED
@@ -1466,14 +1466,12 @@ declare function runMigrations(db: D1Database, migrations: readonly string[]): P
1466
1466
  * Wire protocol constants.
1467
1467
  *
1468
1468
  * The JSON WebSocket protocol uses dotted string identifiers (e.g.
1469
- * `"game.input"`) as the `type` discriminator. All message types are
1469
+ * `"records.query"`) as the `type` discriminator. All message types are
1470
1470
  * grouped under a single `MSG` object so imports stay tidy:
1471
1471
  *
1472
1472
  * import { MSG, dispatch, clientBuild } from 'deepspace'
1473
1473
  *
1474
1474
  * dispatch<ServerMessage>(raw, {
1475
- * [MSG.GAME_STATE]: (p) => { ... },
1476
- * [MSG.GAME_TICK]: (p) => { ... },
1477
1475
  * })
1478
1476
  *
1479
1477
  * Each key's value is the on-wire string — grep-friendly, self-
@@ -1502,14 +1500,6 @@ declare const MSG: {
1502
1500
  readonly ACK: "records.ack";
1503
1501
  readonly LIST_SCHEMAS: "records.list_schemas";
1504
1502
  readonly RESUBSCRIBE: "records.resubscribe";
1505
- readonly GAME_STATE: "game.state";
1506
- readonly GAME_INPUT: "game.input";
1507
- readonly GAME_PLAYER_JOIN: "game.player_join";
1508
- readonly GAME_PLAYER_LEAVE: "game.player_leave";
1509
- readonly GAME_PLAYER_READY: "game.player_ready";
1510
- readonly GAME_START: "game.start";
1511
- readonly GAME_END: "game.end";
1512
- readonly GAME_TICK: "game.tick";
1513
1503
  readonly CANVAS_SHAPES: "canvas.shapes";
1514
1504
  readonly CANVAS_ADD: "canvas.add";
1515
1505
  readonly CANVAS_MOVE: "canvas.move";
@@ -1582,7 +1572,7 @@ declare const MSG: {
1582
1572
  * ---------------
1583
1573
  *
1584
1574
  * Some message types carry different payloads depending on who's sending.
1585
- * `MSG.GAME_START`, for example, is `{}` when the client requests a start
1575
+ * `MSG.RECORDS_RESUBSCRIBE`, for example, is `{}` when the client asks to
1586
1576
  * but `{ state, tick }` when the server broadcasts the start event.
1587
1577
  * `MSG.CANVAS_ADD` is a flat shape dict on the way in and a `{ shape }`
1588
1578
  * wrapper on the way out. Modelling these with one union would force
@@ -1660,26 +1650,6 @@ type ServerMessage = BaseMessage<typeof MSG.QUERY_RESULT, {
1660
1650
  recordId: string;
1661
1651
  fieldName: string;
1662
1652
  canWrite: boolean;
1663
- }> | BaseMessage<typeof MSG.GAME_STATE, {
1664
- state: unknown;
1665
- tick: number;
1666
- players: unknown[];
1667
- running: boolean;
1668
- }> | BaseMessage<typeof MSG.GAME_TICK, {
1669
- state: unknown;
1670
- tick: number;
1671
- }> | BaseMessage<typeof MSG.GAME_START, {
1672
- state: unknown;
1673
- tick: number;
1674
- }> | BaseMessage<typeof MSG.GAME_END, {
1675
- state: unknown;
1676
- tick: number;
1677
- }> | BaseMessage<typeof MSG.GAME_PLAYER_JOIN, {
1678
- player: unknown;
1679
- }> | BaseMessage<typeof MSG.GAME_PLAYER_LEAVE, {
1680
- userId: string;
1681
- }> | BaseMessage<typeof MSG.GAME_PLAYER_READY, {
1682
- userId: string;
1683
1653
  }> | BaseMessage<typeof MSG.CANVAS_SHAPES, {
1684
1654
  shapes: unknown[];
1685
1655
  viewports: unknown[];
@@ -2087,8 +2057,6 @@ declare class RecordRoom<E = Record<string, unknown>> extends BaseRoom<E> {
2087
2057
  private _fetchTiming;
2088
2058
  private ensureInitialized;
2089
2059
  private initializeDatabase;
2090
- private migrateUsersTableIfExists;
2091
- private migrateRecordsTable;
2092
2060
  private ensureCollectionTable;
2093
2061
  private ensureAllCollectionTables;
2094
2062
  protected onConnect(ws: WebSocket, user: UserAttachment): Promise<ConnectionAttachment>;
@@ -2156,106 +2124,6 @@ declare class YjsRoom<E = Record<string, unknown>> extends BaseRoom<E> {
2156
2124
  private broadcastRaw;
2157
2125
  }
2158
2126
 
2159
- /**
2160
- * GameRoom — Authoritative game loop Durable Object.
2161
- *
2162
- * Extends BaseRoom with:
2163
- * - Alarm-based tick loop with configurable interval
2164
- * - Player management (join, leave, ready state)
2165
- * - Input collection per tick, authoritative state computation
2166
- * - State broadcast to all connected players
2167
- *
2168
- * Subclasses implement game logic via lifecycle hooks:
2169
- * onTick, onPlayerJoin, onPlayerLeave, onGameStart, onGameEnd
2170
- *
2171
- * Message types: game.*
2172
- */
2173
-
2174
- interface GameRoomConfig {
2175
- /** Ticks per second (default: 20) */
2176
- tickRate?: number;
2177
- /** Minimum players to start (default: 1) */
2178
- minPlayers?: number;
2179
- /** Maximum players (default: unlimited) */
2180
- maxPlayers?: number;
2181
- }
2182
- interface Player {
2183
- userId: string;
2184
- userName: string;
2185
- ready: boolean;
2186
- connectedAt: string;
2187
- data: Record<string, unknown>;
2188
- }
2189
- interface GameInput {
2190
- userId: string;
2191
- action: string;
2192
- data: Record<string, unknown>;
2193
- tick: number;
2194
- }
2195
- interface GameAttachment extends UserAttachment {
2196
- joinedAt: string;
2197
- /** True for member/admin roles; false for viewers, unauthenticated anon, spectators. */
2198
- canWrite: boolean;
2199
- }
2200
- declare abstract class GameRoom<E = Record<string, unknown>> extends BaseRoom<E> {
2201
- private config;
2202
- private players;
2203
- private inputBuffer;
2204
- private currentTick;
2205
- private gameState;
2206
- private running;
2207
- private initialized;
2208
- constructor(state: DurableObjectState, env: unknown, config?: GameRoomConfig);
2209
- private ensureInitialized;
2210
- private persistState;
2211
- protected onConnect(ws: WebSocket, user: UserAttachment): GameAttachment;
2212
- protected onMessage(ws: WebSocket, user: UserAttachment, message: {
2213
- type: string;
2214
- [key: string]: unknown;
2215
- }): Promise<void>;
2216
- protected onDisconnect(_ws: WebSocket, user: UserAttachment): void;
2217
- protected onAlarm(): Promise<void>;
2218
- private checkAutoStart;
2219
- private startGame;
2220
- private stopGame;
2221
- protected getGameState(): Record<string, unknown>;
2222
- protected setGameState(state: Record<string, unknown>): void;
2223
- protected getPlayers(): Player[];
2224
- protected isRunning(): boolean;
2225
- protected getCurrentTick(): number;
2226
- /**
2227
- * Called each tick with current state and collected inputs.
2228
- * Return the new game state, or undefined to keep current state.
2229
- */
2230
- protected abstract onTick(state: Record<string, unknown>, inputs: GameInput[], tick: number): Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>;
2231
- /** Called when a player connects */
2232
- protected onPlayerJoin(_player: Player): void;
2233
- /** Called when a player disconnects */
2234
- protected onPlayerLeave(_player: Player): void;
2235
- /** Called when the game starts */
2236
- protected onGameStart(): void;
2237
- /** Called when the game ends */
2238
- protected onGameEnd(_finalState: Record<string, unknown>): void;
2239
- /**
2240
- * Called once when the DO first hydrates persisted state from storage.
2241
- * Receives the parsed state blob as it was written by a previous build.
2242
- * Return the state object to install as `gameState`.
2243
- *
2244
- * Subclasses with evolving schemas should override this hook to:
2245
- * - merge new fields onto a default template,
2246
- * - upgrade shapes across versioned states,
2247
- * - or discard stale blobs entirely by returning a fresh object.
2248
- *
2249
- * The default implementation is a pass-through, preserving the legacy
2250
- * "stored blob is gospel" behavior for subclasses that don't care.
2251
- *
2252
- * If JSON parsing of the stored blob fails this hook is NOT called — the
2253
- * DO starts with an empty state and the subclass's `onGameStart` (or
2254
- * first `onTick`) is responsible for initializing.
2255
- */
2256
- protected onHydrateState(stored: Record<string, unknown>): Record<string, unknown>;
2257
- }
2258
-
2259
2127
  /**
2260
2128
  * CanvasRoom — Spatial canvas Durable Object (tldraw-style).
2261
2129
  *
@@ -2619,11 +2487,10 @@ type DOManifest = DOManifestEntry[];
2619
2487
  * @example
2620
2488
  * const manifest = [
2621
2489
  * { binding: 'RECORD_ROOMS', className: 'AppRecordRoom', sqlite: true },
2622
- * { binding: 'GAME_ROOMS', className: 'AppGameRoom', sqlite: true },
2623
2490
  * ] as const satisfies DOManifest
2624
2491
  *
2625
2492
  * type Env = BaseEnv & DOBindings<typeof manifest>
2626
- * // => { RECORD_ROOMS: DurableObjectNamespace; GAME_ROOMS: DurableObjectNamespace }
2493
+ * // => { RECORD_ROOMS: DurableObjectNamespace }
2627
2494
  */
2628
2495
  type DOBindings<T extends readonly DOManifestEntry[]> = {
2629
2496
  [K in T[number]['binding']]: DurableObjectNamespace;
@@ -4666,4 +4533,4 @@ declare class RefundError extends Error {
4666
4533
  constructor(message: string, status: number);
4667
4534
  }
4668
4535
 
4669
- export { ACCOUNT_STORAGE_LIMIT_BYTES, AI_CHATS_SCHEMA, AI_MESSAGES_SCHEMA, ALLOWED_BINDING_TYPES, APP_ID_RE, APP_NAME_RULES, AUTO_PROVISIONABLE_TYPES, AUTO_PROVISION_SENTINEL, type ActionContext, type ActionHandler, type ActionResult, type ActionTools, type ApiWorkerEnv, type AppNameResolution, type AppNameValidation, type AuthWorkerEnv, BASE_USERS_SCHEMA, BUILT_IN_TOOLS, BaseRoom, CHANNELS_SCHEMA, CHANNEL_INVITATIONS_SCHEMA, CHANNEL_MEMBERS_SCHEMA, CONVERSATION_SCHEMAS, COST_RATES, CancelSubscriptionError, type CancelSubscriptionOpts, type CancelSubscriptionResult, CanvasRoom, type CanvasShape, type ChatContextConfig, type ChatMessageRow, type ChatRow, type ChatTurn, type CollectionPermissionSummary, type CollectionSchema, type ColumnDefinition, type ColumnInterpretation, type ComposioToolsOptions, type ConvMemberData, type ConvMessageData, type ConvReactionData, type ConvReadCursorData, type ConvVoteData, type ConversationStateData, type CronContext, type CronExecution, CronRoom, type CronRoomConfig, type CronTask, type CustomBinding, type CustomBindingManifest, DEFAULT_CONTEXT_CONFIG, DEFAULT_DO_MANIFEST, DEFAULT_QUERY_LIMIT, DEPLOYED_APPS_DEFAULTS, DIRECTORY_SCHEMAS, type DOBindings, type DOManifest, type DOManifestEntry, type DebugApiContext, type DeepSpaceAIEnv, type DeepSpaceAIOptions, type DeepSpaceAgentDiagnosticContext, DeepSpaceAgentModelError, DeepSpaceAgentProfileError, type DeepSpaceAgentStream, type DeepSpaceAgentStreamOptions, type DeepSpaceAuth, type DeepSpaceAuthConfig, type DirectoryCommunityData, type DirectoryConversationData, type DirectoryMembershipData, type DirectoryPostData, type DoMigrationDirective, type DoMigrationPlan, type ExistingDOBinding, GLOBAL_DO_TYPES, GLOBAL_DO_TYPE_NAMES, type GameInput, GameRoom, type GameRoomConfig, type GetActionData, type GlobalDOType, type Job, type JobContext, JobRoom, type JobRoomConfig, type JobStatus, type JwtClaims, type JwtVerifierConfig, MAX_APP_FILE_BYTES, MAX_DEPLOY_ASSET_FILE_BYTES, MAX_UPLOAD_PARTS, MAX_UPLOAD_REQUEST_BYTES, MESSAGES_SCHEMA, type MutateActionData, type PermissionAnalysis, type PermissionContext, type PermissionLevel, type PermissionSource, type PlanInfo, type PlatformWorkerEnv, type Player, type PrefixResult, type PresencePeer, PresenceRoom, type QueryActionData, REACTIONS_SCHEMA, READ_RECEIPTS_SCHEMA, REGISTRY_INSTANCE, RESERVED_BINDING_NAMES, RESERVED_COLLECTION_NAMES, RESOURCE_ID_RE, type RecordContext, RecordRoom, type RecordRoomConfig, RefundError, type RefundOpts, type RefundResult, RegistryClientError, type ResolvedColumn, type ResolvedPermission, type RolePermissions, type RunMigrationsResult, STRICT_APP_ID_RE, SYSTEM_COLLECTION_SCHEMAS, SYSTEM_MANAGED_COLUMNS, SchemaRegistry, type ScopeContext, type ScopedR2Auth, type ScopedR2Config, type ScopedR2Handler, type ScreenshotEnv, type ScreenshotResult, type SharedConnection, type StorageAdmission, type StoredRecord, SubscriptionAuthError, type SubscriptionContext, type SubscriptionRead, SubscriptionRequiredError, type SubscriptionStatus, type Summarizer, TEST_ACCOUNT_EMAIL_SUFFIX, type TokenDebugInfo, type ToolResult, type ToolSchema, type ToolsApiContext, UPLOAD_PART_BYTES, USERS_COLUMNS, type User, type UserAttachment, type UserContext, VOTING_SCHEMAS, type ValidationError, type VerifiedAuth, type VerifyOutcome, type VerifyResult, type Viewport, WORKSPACE_SCHEMAS, type YjsContext, YjsRoom, analyzePermissions, apiWorkerFetch, appendMessage, applyAiToolDefaults, applySlidingWindow, authWorkerFetch, bindingManifestFromOutputConfig, broadcastChange, broadcastYjsUpdate, buildCronContext, buildTableSelect, buildUiParts, callRegistry, canCreate, canDelete, canRead, canUpdate, cancelSubscription, capToolResultSize, captureScreenshot, checkFieldPermissions, coerceValue, collectionTableName, columnId, composioTools, computeDoMigration, computeHmacHex, createChat, createDeepSpaceAI, createDeepSpaceAuth, createScopedR2Handler, dataToColumnValues, decodeJwtPayload, deepSpaceAgentErrorSummary, deleteChatCascade, deleteRecord, enqueueJob, executeQuery, formatBytes, getAllUsers, getChat, getGlobalDOSchemas, getGlobalDOType, getOrCreateYjsDoc, getRecord, getRolePermissions, getSubscription, getUser, getYjsDocKey, handleApiRequest, handleDelete, handlePut, handleSetRole, handleSubscribe, handleToolsRequest, handleUnsubscribe, handleUserList, handleUserUpdate, handleYjsBinaryMessage, handleYjsJoin, handleYjsLeave, isAutoProvision, isFilesVerbPath, isOwner, isTestAccountClaims, isTestAccountEmail, isTestAccountTier, lintSchema, loadMessages, makeDefaultSummarizer, matchOrigin, meterAi, meterUsage, meterVectorize, noopPermissionContext, parseQuotaLimit, platformCorsPatterns, platformWorkerFetch, prepareMessagesWithCompaction, priceBindingUsageEvent, putRecord, readRecord, recordMatchesSubscription, refundInvoice, registerUser, registryErrorJson, registryStub, requireSubscription, resolveAppName, resolveColumn, rowToData, runMigrations, saveYjsDoc, storageLimitForTier, storageQuotaMessage, streamDeepSpaceAgent, timingSafeEqualHex, timingSafeEqualStrings, totalChars, truncateOldToolResults, turnsToCoreMessages, unwrapToolOutput, updateChat, validateAppName, validateBindingManifest, validateDoManifest, verifyJwt, workspaceAccountsSchema, workspaceContentSharesSchema, workspaceEmailHandlesSchema, workspaceFormResponsesSchema, workspacePeopleSchema, workspaceProjectsSchema, workspaceTagsSchema, workspaceTasksSchema, workspaceTeamMembersSchema, workspaceTeamsSchema, workspaceTransactionsSchema };
4536
+ export { ACCOUNT_STORAGE_LIMIT_BYTES, AI_CHATS_SCHEMA, AI_MESSAGES_SCHEMA, ALLOWED_BINDING_TYPES, APP_ID_RE, APP_NAME_RULES, AUTO_PROVISIONABLE_TYPES, AUTO_PROVISION_SENTINEL, type ActionContext, type ActionHandler, type ActionResult, type ActionTools, type ApiWorkerEnv, type AppNameResolution, type AppNameValidation, type AuthWorkerEnv, BASE_USERS_SCHEMA, BUILT_IN_TOOLS, BaseRoom, CHANNELS_SCHEMA, CHANNEL_INVITATIONS_SCHEMA, CHANNEL_MEMBERS_SCHEMA, CONVERSATION_SCHEMAS, COST_RATES, CancelSubscriptionError, type CancelSubscriptionOpts, type CancelSubscriptionResult, CanvasRoom, type CanvasShape, type ChatContextConfig, type ChatMessageRow, type ChatRow, type ChatTurn, type CollectionPermissionSummary, type CollectionSchema, type ColumnDefinition, type ColumnInterpretation, type ComposioToolsOptions, type ConvMemberData, type ConvMessageData, type ConvReactionData, type ConvReadCursorData, type ConvVoteData, type ConversationStateData, type CronContext, type CronExecution, CronRoom, type CronRoomConfig, type CronTask, type CustomBinding, type CustomBindingManifest, DEFAULT_CONTEXT_CONFIG, DEFAULT_DO_MANIFEST, DEFAULT_QUERY_LIMIT, DEPLOYED_APPS_DEFAULTS, DIRECTORY_SCHEMAS, type DOBindings, type DOManifest, type DOManifestEntry, type DebugApiContext, type DeepSpaceAIEnv, type DeepSpaceAIOptions, type DeepSpaceAgentDiagnosticContext, DeepSpaceAgentModelError, DeepSpaceAgentProfileError, type DeepSpaceAgentStream, type DeepSpaceAgentStreamOptions, type DeepSpaceAuth, type DeepSpaceAuthConfig, type DirectoryCommunityData, type DirectoryConversationData, type DirectoryMembershipData, type DirectoryPostData, type DoMigrationDirective, type DoMigrationPlan, type ExistingDOBinding, GLOBAL_DO_TYPES, GLOBAL_DO_TYPE_NAMES, type GetActionData, type GlobalDOType, type Job, type JobContext, JobRoom, type JobRoomConfig, type JobStatus, type JwtClaims, type JwtVerifierConfig, MAX_APP_FILE_BYTES, MAX_DEPLOY_ASSET_FILE_BYTES, MAX_UPLOAD_PARTS, MAX_UPLOAD_REQUEST_BYTES, MESSAGES_SCHEMA, type MutateActionData, type PermissionAnalysis, type PermissionContext, type PermissionLevel, type PermissionSource, type PlanInfo, type PlatformWorkerEnv, type PrefixResult, type PresencePeer, PresenceRoom, type QueryActionData, REACTIONS_SCHEMA, READ_RECEIPTS_SCHEMA, REGISTRY_INSTANCE, RESERVED_BINDING_NAMES, RESERVED_COLLECTION_NAMES, RESOURCE_ID_RE, type RecordContext, RecordRoom, type RecordRoomConfig, RefundError, type RefundOpts, type RefundResult, RegistryClientError, type ResolvedColumn, type ResolvedPermission, type RolePermissions, type RunMigrationsResult, STRICT_APP_ID_RE, SYSTEM_COLLECTION_SCHEMAS, SYSTEM_MANAGED_COLUMNS, SchemaRegistry, type ScopeContext, type ScopedR2Auth, type ScopedR2Config, type ScopedR2Handler, type ScreenshotEnv, type ScreenshotResult, type SharedConnection, type StorageAdmission, type StoredRecord, SubscriptionAuthError, type SubscriptionContext, type SubscriptionRead, SubscriptionRequiredError, type SubscriptionStatus, type Summarizer, TEST_ACCOUNT_EMAIL_SUFFIX, type TokenDebugInfo, type ToolResult, type ToolSchema, type ToolsApiContext, UPLOAD_PART_BYTES, USERS_COLUMNS, type User, type UserAttachment, type UserContext, VOTING_SCHEMAS, type ValidationError, type VerifiedAuth, type VerifyOutcome, type VerifyResult, type Viewport, WORKSPACE_SCHEMAS, type YjsContext, YjsRoom, analyzePermissions, apiWorkerFetch, appendMessage, applyAiToolDefaults, applySlidingWindow, authWorkerFetch, bindingManifestFromOutputConfig, broadcastChange, broadcastYjsUpdate, buildCronContext, buildTableSelect, buildUiParts, callRegistry, canCreate, canDelete, canRead, canUpdate, cancelSubscription, capToolResultSize, captureScreenshot, checkFieldPermissions, coerceValue, collectionTableName, columnId, composioTools, computeDoMigration, computeHmacHex, createChat, createDeepSpaceAI, createDeepSpaceAuth, createScopedR2Handler, dataToColumnValues, decodeJwtPayload, deepSpaceAgentErrorSummary, deleteChatCascade, deleteRecord, enqueueJob, executeQuery, formatBytes, getAllUsers, getChat, getGlobalDOSchemas, getGlobalDOType, getOrCreateYjsDoc, getRecord, getRolePermissions, getSubscription, getUser, getYjsDocKey, handleApiRequest, handleDelete, handlePut, handleSetRole, handleSubscribe, handleToolsRequest, handleUnsubscribe, handleUserList, handleUserUpdate, handleYjsBinaryMessage, handleYjsJoin, handleYjsLeave, isAutoProvision, isFilesVerbPath, isOwner, isTestAccountClaims, isTestAccountEmail, isTestAccountTier, lintSchema, loadMessages, makeDefaultSummarizer, matchOrigin, meterAi, meterUsage, meterVectorize, noopPermissionContext, parseQuotaLimit, platformCorsPatterns, platformWorkerFetch, prepareMessagesWithCompaction, priceBindingUsageEvent, putRecord, readRecord, recordMatchesSubscription, refundInvoice, registerUser, registryErrorJson, registryStub, requireSubscription, resolveAppName, resolveColumn, rowToData, runMigrations, saveYjsDoc, storageLimitForTier, storageQuotaMessage, streamDeepSpaceAgent, timingSafeEqualHex, timingSafeEqualStrings, totalChars, truncateOldToolResults, turnsToCoreMessages, unwrapToolOutput, updateChat, validateAppName, validateBindingManifest, validateDoManifest, verifyJwt, workspaceAccountsSchema, workspaceContentSharesSchema, workspaceEmailHandlesSchema, workspaceFormResponsesSchema, workspacePeopleSchema, workspaceProjectsSchema, workspaceTagsSchema, workspaceTasksSchema, workspaceTeamMembersSchema, workspaceTeamsSchema, workspaceTransactionsSchema };
package/dist/server.js CHANGED
@@ -2220,15 +2220,6 @@ var MSG = {
2220
2220
  ACK: "records.ack",
2221
2221
  LIST_SCHEMAS: "records.list_schemas",
2222
2222
  RESUBSCRIBE: "records.resubscribe",
2223
- // ---- GameRoom --------------------------------------------------------
2224
- GAME_STATE: "game.state",
2225
- GAME_INPUT: "game.input",
2226
- GAME_PLAYER_JOIN: "game.player_join",
2227
- GAME_PLAYER_LEAVE: "game.player_leave",
2228
- GAME_PLAYER_READY: "game.player_ready",
2229
- GAME_START: "game.start",
2230
- GAME_END: "game.end",
2231
- GAME_TICK: "game.tick",
2232
2223
  // ---- CanvasRoom ------------------------------------------------------
2233
2224
  CANVAS_SHAPES: "canvas.shapes",
2234
2225
  CANVAS_ADD: "canvas.add",
@@ -2722,14 +2713,6 @@ var serverBuild = {
2722
2713
  type: MSG.YJS_JOIN,
2723
2714
  payload: { collection, recordId, fieldName, canWrite }
2724
2715
  }),
2725
- // Game
2726
- gameState: (state, tick, players, running) => ({ type: MSG.GAME_STATE, payload: { state, tick, players, running } }),
2727
- gameTick: (state, tick) => ({ type: MSG.GAME_TICK, payload: { state, tick } }),
2728
- gameStart: (state, tick) => ({ type: MSG.GAME_START, payload: { state, tick } }),
2729
- gameEnd: (state, tick) => ({ type: MSG.GAME_END, payload: { state, tick } }),
2730
- gamePlayerJoin: (player) => ({ type: MSG.GAME_PLAYER_JOIN, payload: { player } }),
2731
- gamePlayerLeave: (userId) => ({ type: MSG.GAME_PLAYER_LEAVE, payload: { userId } }),
2732
- gamePlayerReady: (userId) => ({ type: MSG.GAME_PLAYER_READY, payload: { userId } }),
2733
2716
  // Canvas
2734
2717
  canvasShapes: (shapes, viewports) => ({ type: MSG.CANVAS_SHAPES, payload: { shapes, viewports } }),
2735
2718
  canvasAdd: (shape) => ({ type: MSG.CANVAS_ADD, payload: { shape } }),
@@ -4325,136 +4308,6 @@ var RecordRoom = class extends BaseRoom {
4325
4308
  );
4326
4309
  `);
4327
4310
  this.ensureAllCollectionTables();
4328
- await this.migrateUsersTableIfExists();
4329
- await this.migrateRecordsTable();
4330
- }
4331
- async migrateUsersTableIfExists() {
4332
- try {
4333
- const tableCheck = this.sql.exec(
4334
- `SELECT name FROM sqlite_master WHERE type='table' AND name='users'`
4335
- );
4336
- if (tableCheck.toArray().length === 0) {
4337
- return;
4338
- }
4339
- const oldUsers = this.sql.exec(`SELECT * FROM users`).toArray();
4340
- if (oldUsers.length === 0) {
4341
- this.sql.exec(`DROP TABLE IF EXISTS users`);
4342
- return;
4343
- }
4344
- const now = (/* @__PURE__ */ new Date()).toISOString();
4345
- for (const row of oldUsers) {
4346
- const r = row;
4347
- const existing = this.sql.exec(`SELECT 1 FROM c_users WHERE _row_id = ?`, r.user_id).toArray();
4348
- if (existing.length > 0) continue;
4349
- this.sql.exec(
4350
- `INSERT INTO c_users (_row_id, _created_by, _created_at, _updated_at, col_email, col_name, col_imageurl, col_role, col_createdat, col_lastseenat) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
4351
- r.user_id,
4352
- r.user_id,
4353
- r.created_at,
4354
- now,
4355
- r.email,
4356
- r.name,
4357
- r.image_url || null,
4358
- r.role,
4359
- r.created_at,
4360
- r.last_seen_at
4361
- );
4362
- }
4363
- this.sql.exec(`DROP TABLE IF EXISTS users`);
4364
- } catch (error) {
4365
- console.error(`[RecordRoom] Users table migration error:`, error);
4366
- }
4367
- }
4368
- async migrateRecordsTable() {
4369
- try {
4370
- const tableCheck = this.sql.exec(
4371
- `SELECT name FROM sqlite_master WHERE type='table' AND name='records'`
4372
- );
4373
- if (tableCheck.toArray().length === 0) return;
4374
- const rows = this.sql.exec(`SELECT * FROM records`).toArray();
4375
- if (rows.length === 0) {
4376
- this.sql.exec(`DROP TABLE IF EXISTS records`);
4377
- return;
4378
- }
4379
- const prepared = [];
4380
- const blocked = {
4381
- missingSchema: 0,
4382
- invalidData: 0,
4383
- targetConflict: 0
4384
- };
4385
- for (const row of rows) {
4386
- const r = row;
4387
- const schema = this.schemaRegistry.get(r.collection);
4388
- if (!schema || !schema.columns) {
4389
- blocked.missingSchema++;
4390
- continue;
4391
- }
4392
- const tbl = collectionTableName(r.collection);
4393
- const tblExists = this.sql.exec(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, tbl).toArray().length > 0;
4394
- if (!tblExists) {
4395
- this.ensureCollectionTable(schema);
4396
- }
4397
- const existing = this.sql.exec(`SELECT 1 FROM "${tbl}" WHERE _row_id = ?`, r.record_id).toArray();
4398
- if (existing.length > 0) {
4399
- blocked.targetConflict++;
4400
- continue;
4401
- }
4402
- let data;
4403
- try {
4404
- data = JSON.parse(r.data);
4405
- } catch {
4406
- blocked.invalidData++;
4407
- continue;
4408
- }
4409
- if (data === null || typeof data !== "object" || Array.isArray(data)) {
4410
- blocked.invalidData++;
4411
- continue;
4412
- }
4413
- const columns = schema.columns.map(resolveColumn);
4414
- const colValues = dataToColumnValues(data, columns);
4415
- const colIds = Object.keys(colValues);
4416
- const allCols = [
4417
- "_row_id",
4418
- "_created_by",
4419
- "_created_at",
4420
- "_updated_at",
4421
- ...colIds.map((c) => `"${c}"`)
4422
- ];
4423
- const placeholders = allCols.map(() => "?").join(", ");
4424
- const params = [
4425
- r.record_id,
4426
- r.created_by,
4427
- r.created_at,
4428
- r.updated_at,
4429
- ...colIds.map((c) => colValues[c])
4430
- ];
4431
- prepared.push({
4432
- sql: `INSERT INTO "${tbl}" (${allCols.join(", ")}) VALUES (${placeholders})`,
4433
- params
4434
- });
4435
- }
4436
- const blockedCount = blocked.missingSchema + blocked.invalidData + blocked.targetConflict;
4437
- if (blockedCount > 0) {
4438
- console.error(
4439
- `[RecordRoom] Legacy records migration blocked; no records migrated and the records table was retained for retry (${blockedCount} blocked: ${blocked.missingSchema} missing schema, ${blocked.invalidData} invalid data, ${blocked.targetConflict} target conflict)`
4440
- );
4441
- return;
4442
- }
4443
- this.state.storage.transactionSync(() => {
4444
- for (const record of prepared) {
4445
- this.sql.exec(record.sql, ...record.params);
4446
- }
4447
- this.sql.exec(`DROP TABLE IF EXISTS records`);
4448
- });
4449
- console.log(
4450
- `[RecordRoom] Migrated ${prepared.length} records from document-mode; legacy records table removed`
4451
- );
4452
- } catch (error) {
4453
- console.error(
4454
- `[RecordRoom] Legacy records migration failed; no records migrated and the records table was retained for retry:`,
4455
- error
4456
- );
4457
- }
4458
4311
  }
4459
4312
  // ============================================================================
4460
4313
  // Table-Mode Schema Management
@@ -4931,6 +4784,9 @@ var YjsRoom = class extends BaseRoom {
4931
4784
  }
4932
4785
  };
4933
4786
 
4787
+ // src/server/rooms/canvas-room.ts
4788
+ import * as Y5 from "yjs";
4789
+
4934
4790
  // src/shared/roles.ts
4935
4791
  var ROLES = {
4936
4792
  VIEWER: "viewer",
@@ -4938,271 +4794,7 @@ var ROLES = {
4938
4794
  ADMIN: "admin"
4939
4795
  };
4940
4796
 
4941
- // src/server/rooms/game-room.ts
4942
- var GAME_WRITE_TYPES = /* @__PURE__ */ new Set([
4943
- MSG.GAME_INPUT,
4944
- MSG.GAME_PLAYER_READY,
4945
- MSG.GAME_START,
4946
- MSG.GAME_END
4947
- ]);
4948
- var GameRoom = class extends BaseRoom {
4949
- config;
4950
- players = /* @__PURE__ */ new Map();
4951
- inputBuffer = [];
4952
- currentTick = 0;
4953
- gameState = {};
4954
- running = false;
4955
- initialized = false;
4956
- constructor(state, env, config = {}) {
4957
- super(state, env);
4958
- this.config = {
4959
- tickRate: config.tickRate ?? 20,
4960
- minPlayers: config.minPlayers ?? 1,
4961
- maxPlayers: config.maxPlayers ?? Infinity
4962
- };
4963
- }
4964
- ensureInitialized() {
4965
- if (this.initialized) return;
4966
- this.initialized = true;
4967
- this.sql.exec(`
4968
- CREATE TABLE IF NOT EXISTS game_state (
4969
- id INTEGER PRIMARY KEY CHECK (id = 1),
4970
- state TEXT NOT NULL,
4971
- tick INTEGER NOT NULL DEFAULT 0,
4972
- updated_at TEXT NOT NULL
4973
- )
4974
- `);
4975
- const rows = this.sql.exec("SELECT state, tick FROM game_state WHERE id = 1").toArray();
4976
- if (rows.length > 0) {
4977
- try {
4978
- const parsed = JSON.parse(rows[0].state);
4979
- this.gameState = this.onHydrateState(parsed);
4980
- this.currentTick = rows[0].tick;
4981
- } catch {
4982
- }
4983
- }
4984
- }
4985
- persistState() {
4986
- const now = (/* @__PURE__ */ new Date()).toISOString();
4987
- this.sql.exec(
4988
- `INSERT INTO game_state (id, state, tick, updated_at) VALUES (1, ?, ?, ?)
4989
- ON CONFLICT(id) DO UPDATE SET state = ?, tick = ?, updated_at = ?`,
4990
- JSON.stringify(this.gameState),
4991
- this.currentTick,
4992
- now,
4993
- JSON.stringify(this.gameState),
4994
- this.currentTick,
4995
- now
4996
- );
4997
- }
4998
- // ==========================================================================
4999
- // BaseRoom Lifecycle
5000
- // ==========================================================================
5001
- onConnect(ws, user) {
5002
- this.ensureInitialized();
5003
- const role = user.role ?? ROLES.VIEWER;
5004
- const canWrite = role === ROLES.MEMBER || role === ROLES.ADMIN;
5005
- const attachment = {
5006
- ...user,
5007
- joinedAt: (/* @__PURE__ */ new Date()).toISOString(),
5008
- canWrite
5009
- };
5010
- this.sendTo(ws, { type: MSG.AUTH, payload: { canWrite } });
5011
- if (!canWrite) {
5012
- this.sendTo(ws, {
5013
- type: MSG.GAME_STATE,
5014
- payload: {
5015
- state: this.gameState,
5016
- tick: this.currentTick,
5017
- players: Array.from(this.players.values()),
5018
- running: this.running
5019
- }
5020
- });
5021
- return attachment;
5022
- }
5023
- const player = {
5024
- userId: user.userId,
5025
- userName: user.userName,
5026
- ready: false,
5027
- connectedAt: attachment.joinedAt,
5028
- data: {}
5029
- };
5030
- if (this.config.maxPlayers !== Infinity && this.players.size >= this.config.maxPlayers) {
5031
- this.sendTo(ws, { type: MSG.ERROR, payload: { error: "Game is full" } });
5032
- return attachment;
5033
- }
5034
- this.players.set(user.userId, player);
5035
- this.sendTo(ws, {
5036
- type: MSG.GAME_STATE,
5037
- payload: {
5038
- state: this.gameState,
5039
- tick: this.currentTick,
5040
- players: Array.from(this.players.values()),
5041
- running: this.running
5042
- }
5043
- });
5044
- this.broadcast({ type: MSG.GAME_PLAYER_JOIN, payload: { player } }, ws);
5045
- this.onPlayerJoin(player);
5046
- return attachment;
5047
- }
5048
- async onMessage(ws, user, message) {
5049
- this.ensureInitialized();
5050
- const { type, payload } = message;
5051
- if (GAME_WRITE_TYPES.has(type) && !user.canWrite) {
5052
- this.sendTo(ws, {
5053
- type: MSG.ERROR,
5054
- payload: { error: "Write access denied: spectators cannot drive the game" }
5055
- });
5056
- return;
5057
- }
5058
- switch (type) {
5059
- case MSG.GAME_INPUT: {
5060
- this.inputBuffer.push({
5061
- userId: user.userId,
5062
- action: payload.action,
5063
- data: payload.data ?? {},
5064
- tick: this.currentTick
5065
- });
5066
- break;
5067
- }
5068
- case MSG.GAME_PLAYER_READY: {
5069
- const player = this.players.get(user.userId);
5070
- if (player) {
5071
- player.ready = true;
5072
- this.broadcast({ type: MSG.GAME_PLAYER_READY, payload: { userId: user.userId } });
5073
- this.checkAutoStart();
5074
- }
5075
- break;
5076
- }
5077
- case MSG.GAME_START: {
5078
- if (!this.running) {
5079
- this.startGame();
5080
- }
5081
- break;
5082
- }
5083
- case MSG.GAME_END: {
5084
- if (this.running) {
5085
- this.stopGame();
5086
- }
5087
- break;
5088
- }
5089
- default:
5090
- this.sendTo(ws, { type: MSG.ERROR, payload: { error: `Unknown game message type: ${type}` } });
5091
- }
5092
- }
5093
- onDisconnect(_ws, user) {
5094
- const player = this.players.get(user.userId);
5095
- if (player) {
5096
- this.players.delete(user.userId);
5097
- this.broadcast({ type: MSG.GAME_PLAYER_LEAVE, payload: { userId: user.userId } });
5098
- this.onPlayerLeave(player);
5099
- if (this.running && this.players.size === 0) {
5100
- this.stopGame();
5101
- }
5102
- }
5103
- }
5104
- async onAlarm() {
5105
- if (!this.running) return;
5106
- const inputs = this.inputBuffer.splice(0);
5107
- this.currentTick++;
5108
- const newState = await this.onTick(this.gameState, inputs, this.currentTick);
5109
- if (newState !== void 0) {
5110
- this.gameState = newState;
5111
- }
5112
- if (this.currentTick % 10 === 0) {
5113
- this.persistState();
5114
- }
5115
- this.broadcast({
5116
- type: MSG.GAME_TICK,
5117
- payload: {
5118
- state: this.gameState,
5119
- tick: this.currentTick
5120
- }
5121
- });
5122
- if (this.running) {
5123
- const intervalMs = 1e3 / this.config.tickRate;
5124
- this.state.storage.setAlarm(Date.now() + intervalMs);
5125
- }
5126
- }
5127
- // ==========================================================================
5128
- // Game Control
5129
- // ==========================================================================
5130
- checkAutoStart() {
5131
- if (this.running) return;
5132
- const readyCount = Array.from(this.players.values()).filter((p) => p.ready).length;
5133
- if (readyCount >= this.config.minPlayers && readyCount === this.players.size) {
5134
- this.startGame();
5135
- }
5136
- }
5137
- startGame() {
5138
- this.running = true;
5139
- this.currentTick = 0;
5140
- this.inputBuffer = [];
5141
- this.onGameStart();
5142
- this.broadcast({ type: MSG.GAME_START, payload: { state: this.gameState, tick: 0 } });
5143
- const intervalMs = 1e3 / this.config.tickRate;
5144
- this.state.storage.setAlarm(Date.now() + intervalMs);
5145
- }
5146
- stopGame() {
5147
- this.running = false;
5148
- this.persistState();
5149
- this.onGameEnd(this.gameState);
5150
- this.broadcast({ type: MSG.GAME_END, payload: { state: this.gameState, tick: this.currentTick } });
5151
- }
5152
- // ==========================================================================
5153
- // Protected Accessors (for subclasses)
5154
- // ==========================================================================
5155
- getGameState() {
5156
- return this.gameState;
5157
- }
5158
- setGameState(state) {
5159
- this.gameState = state;
5160
- }
5161
- getPlayers() {
5162
- return Array.from(this.players.values());
5163
- }
5164
- isRunning() {
5165
- return this.running;
5166
- }
5167
- getCurrentTick() {
5168
- return this.currentTick;
5169
- }
5170
- /** Called when a player connects */
5171
- onPlayerJoin(_player) {
5172
- }
5173
- /** Called when a player disconnects */
5174
- onPlayerLeave(_player) {
5175
- }
5176
- /** Called when the game starts */
5177
- onGameStart() {
5178
- }
5179
- /** Called when the game ends */
5180
- onGameEnd(_finalState) {
5181
- }
5182
- /**
5183
- * Called once when the DO first hydrates persisted state from storage.
5184
- * Receives the parsed state blob as it was written by a previous build.
5185
- * Return the state object to install as `gameState`.
5186
- *
5187
- * Subclasses with evolving schemas should override this hook to:
5188
- * - merge new fields onto a default template,
5189
- * - upgrade shapes across versioned states,
5190
- * - or discard stale blobs entirely by returning a fresh object.
5191
- *
5192
- * The default implementation is a pass-through, preserving the legacy
5193
- * "stored blob is gospel" behavior for subclasses that don't care.
5194
- *
5195
- * If JSON parsing of the stored blob fails this hook is NOT called — the
5196
- * DO starts with an empty state and the subclass's `onGameStart` (or
5197
- * first `onTick`) is responsible for initializing.
5198
- */
5199
- onHydrateState(stored) {
5200
- return stored;
5201
- }
5202
- };
5203
-
5204
4797
  // src/server/rooms/canvas-room.ts
5205
- import * as Y5 from "yjs";
5206
4798
  var CANVAS_WRITE_TYPES = /* @__PURE__ */ new Set([
5207
4799
  MSG.CANVAS_ADD,
5208
4800
  MSG.CANVAS_MOVE,
@@ -8033,7 +7625,6 @@ export {
8033
7625
  DeepSpaceAgentProfileError,
8034
7626
  GLOBAL_DO_TYPES,
8035
7627
  GLOBAL_DO_TYPE_NAMES,
8036
- GameRoom,
8037
7628
  JobRoom,
8038
7629
  MAX_APP_FILE_BYTES,
8039
7630
  MAX_DEPLOY_ASSET_FILE_BYTES,