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/worker.js CHANGED
@@ -288,15 +288,6 @@ var MSG = {
288
288
  ACK: "records.ack",
289
289
  LIST_SCHEMAS: "records.list_schemas",
290
290
  RESUBSCRIBE: "records.resubscribe",
291
- // ---- GameRoom --------------------------------------------------------
292
- GAME_STATE: "game.state",
293
- GAME_INPUT: "game.input",
294
- GAME_PLAYER_JOIN: "game.player_join",
295
- GAME_PLAYER_LEAVE: "game.player_leave",
296
- GAME_PLAYER_READY: "game.player_ready",
297
- GAME_START: "game.start",
298
- GAME_END: "game.end",
299
- GAME_TICK: "game.tick",
300
291
  // ---- CanvasRoom ------------------------------------------------------
301
292
  CANVAS_SHAPES: "canvas.shapes",
302
293
  CANVAS_ADD: "canvas.add",
@@ -1006,11 +997,6 @@ var clientBuild = {
1006
997
  // Yjs
1007
998
  yjsJoin: (collection, recordId, fieldName) => ({ type: MSG.YJS_JOIN, payload: { collection, recordId, fieldName } }),
1008
999
  yjsLeave: (collection, recordId, fieldName) => ({ type: MSG.YJS_LEAVE, payload: { collection, recordId, fieldName } }),
1009
- // Game
1010
- gameInput: (action, data = {}) => ({ type: MSG.GAME_INPUT, payload: { action, data } }),
1011
- gamePlayerReady: () => ({ type: MSG.GAME_PLAYER_READY, payload: EMPTY }),
1012
- gameStart: () => ({ type: MSG.GAME_START, payload: EMPTY }),
1013
- gameEnd: () => ({ type: MSG.GAME_END, payload: EMPTY }),
1014
1000
  // Canvas — `CANVAS_ADD` / `CANVAS_VIEWPORT` payloads are flat (the shape
1015
1001
  // or viewport object is the whole payload).
1016
1002
  canvasAdd: (shape) => ({ type: MSG.CANVAS_ADD, payload: shape }),
@@ -1062,14 +1048,6 @@ var serverBuild = {
1062
1048
  type: MSG.YJS_JOIN,
1063
1049
  payload: { collection, recordId, fieldName, canWrite }
1064
1050
  }),
1065
- // Game
1066
- gameState: (state, tick, players, running) => ({ type: MSG.GAME_STATE, payload: { state, tick, players, running } }),
1067
- gameTick: (state, tick) => ({ type: MSG.GAME_TICK, payload: { state, tick } }),
1068
- gameStart: (state, tick) => ({ type: MSG.GAME_START, payload: { state, tick } }),
1069
- gameEnd: (state, tick) => ({ type: MSG.GAME_END, payload: { state, tick } }),
1070
- gamePlayerJoin: (player) => ({ type: MSG.GAME_PLAYER_JOIN, payload: { player } }),
1071
- gamePlayerLeave: (userId) => ({ type: MSG.GAME_PLAYER_LEAVE, payload: { userId } }),
1072
- gamePlayerReady: (userId) => ({ type: MSG.GAME_PLAYER_READY, payload: { userId } }),
1073
1051
  // Canvas
1074
1052
  canvasShapes: (shapes, viewports) => ({ type: MSG.CANVAS_SHAPES, payload: { shapes, viewports } }),
1075
1053
  canvasAdd: (shape) => ({ type: MSG.CANVAS_ADD, payload: { shape } }),
@@ -2795,136 +2773,6 @@ var RecordRoom = class extends BaseRoom {
2795
2773
  );
2796
2774
  `);
2797
2775
  this.ensureAllCollectionTables();
2798
- await this.migrateUsersTableIfExists();
2799
- await this.migrateRecordsTable();
2800
- }
2801
- async migrateUsersTableIfExists() {
2802
- try {
2803
- const tableCheck = this.sql.exec(
2804
- `SELECT name FROM sqlite_master WHERE type='table' AND name='users'`
2805
- );
2806
- if (tableCheck.toArray().length === 0) {
2807
- return;
2808
- }
2809
- const oldUsers = this.sql.exec(`SELECT * FROM users`).toArray();
2810
- if (oldUsers.length === 0) {
2811
- this.sql.exec(`DROP TABLE IF EXISTS users`);
2812
- return;
2813
- }
2814
- const now = (/* @__PURE__ */ new Date()).toISOString();
2815
- for (const row of oldUsers) {
2816
- const r = row;
2817
- const existing = this.sql.exec(`SELECT 1 FROM c_users WHERE _row_id = ?`, r.user_id).toArray();
2818
- if (existing.length > 0) continue;
2819
- this.sql.exec(
2820
- `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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2821
- r.user_id,
2822
- r.user_id,
2823
- r.created_at,
2824
- now,
2825
- r.email,
2826
- r.name,
2827
- r.image_url || null,
2828
- r.role,
2829
- r.created_at,
2830
- r.last_seen_at
2831
- );
2832
- }
2833
- this.sql.exec(`DROP TABLE IF EXISTS users`);
2834
- } catch (error) {
2835
- console.error(`[RecordRoom] Users table migration error:`, error);
2836
- }
2837
- }
2838
- async migrateRecordsTable() {
2839
- try {
2840
- const tableCheck = this.sql.exec(
2841
- `SELECT name FROM sqlite_master WHERE type='table' AND name='records'`
2842
- );
2843
- if (tableCheck.toArray().length === 0) return;
2844
- const rows = this.sql.exec(`SELECT * FROM records`).toArray();
2845
- if (rows.length === 0) {
2846
- this.sql.exec(`DROP TABLE IF EXISTS records`);
2847
- return;
2848
- }
2849
- const prepared = [];
2850
- const blocked = {
2851
- missingSchema: 0,
2852
- invalidData: 0,
2853
- targetConflict: 0
2854
- };
2855
- for (const row of rows) {
2856
- const r = row;
2857
- const schema = this.schemaRegistry.get(r.collection);
2858
- if (!schema || !schema.columns) {
2859
- blocked.missingSchema++;
2860
- continue;
2861
- }
2862
- const tbl = collectionTableName(r.collection);
2863
- const tblExists = this.sql.exec(`SELECT name FROM sqlite_master WHERE type='table' AND name=?`, tbl).toArray().length > 0;
2864
- if (!tblExists) {
2865
- this.ensureCollectionTable(schema);
2866
- }
2867
- const existing = this.sql.exec(`SELECT 1 FROM "${tbl}" WHERE _row_id = ?`, r.record_id).toArray();
2868
- if (existing.length > 0) {
2869
- blocked.targetConflict++;
2870
- continue;
2871
- }
2872
- let data;
2873
- try {
2874
- data = JSON.parse(r.data);
2875
- } catch {
2876
- blocked.invalidData++;
2877
- continue;
2878
- }
2879
- if (data === null || typeof data !== "object" || Array.isArray(data)) {
2880
- blocked.invalidData++;
2881
- continue;
2882
- }
2883
- const columns = schema.columns.map(resolveColumn);
2884
- const colValues = dataToColumnValues(data, columns);
2885
- const colIds = Object.keys(colValues);
2886
- const allCols = [
2887
- "_row_id",
2888
- "_created_by",
2889
- "_created_at",
2890
- "_updated_at",
2891
- ...colIds.map((c) => `"${c}"`)
2892
- ];
2893
- const placeholders = allCols.map(() => "?").join(", ");
2894
- const params = [
2895
- r.record_id,
2896
- r.created_by,
2897
- r.created_at,
2898
- r.updated_at,
2899
- ...colIds.map((c) => colValues[c])
2900
- ];
2901
- prepared.push({
2902
- sql: `INSERT INTO "${tbl}" (${allCols.join(", ")}) VALUES (${placeholders})`,
2903
- params
2904
- });
2905
- }
2906
- const blockedCount = blocked.missingSchema + blocked.invalidData + blocked.targetConflict;
2907
- if (blockedCount > 0) {
2908
- console.error(
2909
- `[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)`
2910
- );
2911
- return;
2912
- }
2913
- this.state.storage.transactionSync(() => {
2914
- for (const record of prepared) {
2915
- this.sql.exec(record.sql, ...record.params);
2916
- }
2917
- this.sql.exec(`DROP TABLE IF EXISTS records`);
2918
- });
2919
- console.log(
2920
- `[RecordRoom] Migrated ${prepared.length} records from document-mode; legacy records table removed`
2921
- );
2922
- } catch (error) {
2923
- console.error(
2924
- `[RecordRoom] Legacy records migration failed; no records migrated and the records table was retained for retry:`,
2925
- error
2926
- );
2927
- }
2928
2776
  }
2929
2777
  // ============================================================================
2930
2778
  // Table-Mode Schema Management
@@ -3401,6 +3249,9 @@ var YjsRoom = class extends BaseRoom {
3401
3249
  }
3402
3250
  };
3403
3251
 
3252
+ // src/server/rooms/canvas-room.ts
3253
+ import * as Y5 from "yjs";
3254
+
3404
3255
  // src/shared/roles.ts
3405
3256
  var ROLES = {
3406
3257
  VIEWER: "viewer",
@@ -3408,271 +3259,7 @@ var ROLES = {
3408
3259
  ADMIN: "admin"
3409
3260
  };
3410
3261
 
3411
- // src/server/rooms/game-room.ts
3412
- var GAME_WRITE_TYPES = /* @__PURE__ */ new Set([
3413
- MSG.GAME_INPUT,
3414
- MSG.GAME_PLAYER_READY,
3415
- MSG.GAME_START,
3416
- MSG.GAME_END
3417
- ]);
3418
- var GameRoom = class extends BaseRoom {
3419
- config;
3420
- players = /* @__PURE__ */ new Map();
3421
- inputBuffer = [];
3422
- currentTick = 0;
3423
- gameState = {};
3424
- running = false;
3425
- initialized = false;
3426
- constructor(state, env, config = {}) {
3427
- super(state, env);
3428
- this.config = {
3429
- tickRate: config.tickRate ?? 20,
3430
- minPlayers: config.minPlayers ?? 1,
3431
- maxPlayers: config.maxPlayers ?? Infinity
3432
- };
3433
- }
3434
- ensureInitialized() {
3435
- if (this.initialized) return;
3436
- this.initialized = true;
3437
- this.sql.exec(`
3438
- CREATE TABLE IF NOT EXISTS game_state (
3439
- id INTEGER PRIMARY KEY CHECK (id = 1),
3440
- state TEXT NOT NULL,
3441
- tick INTEGER NOT NULL DEFAULT 0,
3442
- updated_at TEXT NOT NULL
3443
- )
3444
- `);
3445
- const rows = this.sql.exec("SELECT state, tick FROM game_state WHERE id = 1").toArray();
3446
- if (rows.length > 0) {
3447
- try {
3448
- const parsed = JSON.parse(rows[0].state);
3449
- this.gameState = this.onHydrateState(parsed);
3450
- this.currentTick = rows[0].tick;
3451
- } catch {
3452
- }
3453
- }
3454
- }
3455
- persistState() {
3456
- const now = (/* @__PURE__ */ new Date()).toISOString();
3457
- this.sql.exec(
3458
- `INSERT INTO game_state (id, state, tick, updated_at) VALUES (1, ?, ?, ?)
3459
- ON CONFLICT(id) DO UPDATE SET state = ?, tick = ?, updated_at = ?`,
3460
- JSON.stringify(this.gameState),
3461
- this.currentTick,
3462
- now,
3463
- JSON.stringify(this.gameState),
3464
- this.currentTick,
3465
- now
3466
- );
3467
- }
3468
- // ==========================================================================
3469
- // BaseRoom Lifecycle
3470
- // ==========================================================================
3471
- onConnect(ws, user) {
3472
- this.ensureInitialized();
3473
- const role = user.role ?? ROLES.VIEWER;
3474
- const canWrite = role === ROLES.MEMBER || role === ROLES.ADMIN;
3475
- const attachment = {
3476
- ...user,
3477
- joinedAt: (/* @__PURE__ */ new Date()).toISOString(),
3478
- canWrite
3479
- };
3480
- this.sendTo(ws, { type: MSG.AUTH, payload: { canWrite } });
3481
- if (!canWrite) {
3482
- this.sendTo(ws, {
3483
- type: MSG.GAME_STATE,
3484
- payload: {
3485
- state: this.gameState,
3486
- tick: this.currentTick,
3487
- players: Array.from(this.players.values()),
3488
- running: this.running
3489
- }
3490
- });
3491
- return attachment;
3492
- }
3493
- const player = {
3494
- userId: user.userId,
3495
- userName: user.userName,
3496
- ready: false,
3497
- connectedAt: attachment.joinedAt,
3498
- data: {}
3499
- };
3500
- if (this.config.maxPlayers !== Infinity && this.players.size >= this.config.maxPlayers) {
3501
- this.sendTo(ws, { type: MSG.ERROR, payload: { error: "Game is full" } });
3502
- return attachment;
3503
- }
3504
- this.players.set(user.userId, player);
3505
- this.sendTo(ws, {
3506
- type: MSG.GAME_STATE,
3507
- payload: {
3508
- state: this.gameState,
3509
- tick: this.currentTick,
3510
- players: Array.from(this.players.values()),
3511
- running: this.running
3512
- }
3513
- });
3514
- this.broadcast({ type: MSG.GAME_PLAYER_JOIN, payload: { player } }, ws);
3515
- this.onPlayerJoin(player);
3516
- return attachment;
3517
- }
3518
- async onMessage(ws, user, message) {
3519
- this.ensureInitialized();
3520
- const { type, payload } = message;
3521
- if (GAME_WRITE_TYPES.has(type) && !user.canWrite) {
3522
- this.sendTo(ws, {
3523
- type: MSG.ERROR,
3524
- payload: { error: "Write access denied: spectators cannot drive the game" }
3525
- });
3526
- return;
3527
- }
3528
- switch (type) {
3529
- case MSG.GAME_INPUT: {
3530
- this.inputBuffer.push({
3531
- userId: user.userId,
3532
- action: payload.action,
3533
- data: payload.data ?? {},
3534
- tick: this.currentTick
3535
- });
3536
- break;
3537
- }
3538
- case MSG.GAME_PLAYER_READY: {
3539
- const player = this.players.get(user.userId);
3540
- if (player) {
3541
- player.ready = true;
3542
- this.broadcast({ type: MSG.GAME_PLAYER_READY, payload: { userId: user.userId } });
3543
- this.checkAutoStart();
3544
- }
3545
- break;
3546
- }
3547
- case MSG.GAME_START: {
3548
- if (!this.running) {
3549
- this.startGame();
3550
- }
3551
- break;
3552
- }
3553
- case MSG.GAME_END: {
3554
- if (this.running) {
3555
- this.stopGame();
3556
- }
3557
- break;
3558
- }
3559
- default:
3560
- this.sendTo(ws, { type: MSG.ERROR, payload: { error: `Unknown game message type: ${type}` } });
3561
- }
3562
- }
3563
- onDisconnect(_ws, user) {
3564
- const player = this.players.get(user.userId);
3565
- if (player) {
3566
- this.players.delete(user.userId);
3567
- this.broadcast({ type: MSG.GAME_PLAYER_LEAVE, payload: { userId: user.userId } });
3568
- this.onPlayerLeave(player);
3569
- if (this.running && this.players.size === 0) {
3570
- this.stopGame();
3571
- }
3572
- }
3573
- }
3574
- async onAlarm() {
3575
- if (!this.running) return;
3576
- const inputs = this.inputBuffer.splice(0);
3577
- this.currentTick++;
3578
- const newState = await this.onTick(this.gameState, inputs, this.currentTick);
3579
- if (newState !== void 0) {
3580
- this.gameState = newState;
3581
- }
3582
- if (this.currentTick % 10 === 0) {
3583
- this.persistState();
3584
- }
3585
- this.broadcast({
3586
- type: MSG.GAME_TICK,
3587
- payload: {
3588
- state: this.gameState,
3589
- tick: this.currentTick
3590
- }
3591
- });
3592
- if (this.running) {
3593
- const intervalMs = 1e3 / this.config.tickRate;
3594
- this.state.storage.setAlarm(Date.now() + intervalMs);
3595
- }
3596
- }
3597
- // ==========================================================================
3598
- // Game Control
3599
- // ==========================================================================
3600
- checkAutoStart() {
3601
- if (this.running) return;
3602
- const readyCount = Array.from(this.players.values()).filter((p) => p.ready).length;
3603
- if (readyCount >= this.config.minPlayers && readyCount === this.players.size) {
3604
- this.startGame();
3605
- }
3606
- }
3607
- startGame() {
3608
- this.running = true;
3609
- this.currentTick = 0;
3610
- this.inputBuffer = [];
3611
- this.onGameStart();
3612
- this.broadcast({ type: MSG.GAME_START, payload: { state: this.gameState, tick: 0 } });
3613
- const intervalMs = 1e3 / this.config.tickRate;
3614
- this.state.storage.setAlarm(Date.now() + intervalMs);
3615
- }
3616
- stopGame() {
3617
- this.running = false;
3618
- this.persistState();
3619
- this.onGameEnd(this.gameState);
3620
- this.broadcast({ type: MSG.GAME_END, payload: { state: this.gameState, tick: this.currentTick } });
3621
- }
3622
- // ==========================================================================
3623
- // Protected Accessors (for subclasses)
3624
- // ==========================================================================
3625
- getGameState() {
3626
- return this.gameState;
3627
- }
3628
- setGameState(state) {
3629
- this.gameState = state;
3630
- }
3631
- getPlayers() {
3632
- return Array.from(this.players.values());
3633
- }
3634
- isRunning() {
3635
- return this.running;
3636
- }
3637
- getCurrentTick() {
3638
- return this.currentTick;
3639
- }
3640
- /** Called when a player connects */
3641
- onPlayerJoin(_player) {
3642
- }
3643
- /** Called when a player disconnects */
3644
- onPlayerLeave(_player) {
3645
- }
3646
- /** Called when the game starts */
3647
- onGameStart() {
3648
- }
3649
- /** Called when the game ends */
3650
- onGameEnd(_finalState) {
3651
- }
3652
- /**
3653
- * Called once when the DO first hydrates persisted state from storage.
3654
- * Receives the parsed state blob as it was written by a previous build.
3655
- * Return the state object to install as `gameState`.
3656
- *
3657
- * Subclasses with evolving schemas should override this hook to:
3658
- * - merge new fields onto a default template,
3659
- * - upgrade shapes across versioned states,
3660
- * - or discard stale blobs entirely by returning a fresh object.
3661
- *
3662
- * The default implementation is a pass-through, preserving the legacy
3663
- * "stored blob is gospel" behavior for subclasses that don't care.
3664
- *
3665
- * If JSON parsing of the stored blob fails this hook is NOT called — the
3666
- * DO starts with an empty state and the subclass's `onGameStart` (or
3667
- * first `onTick`) is responsible for initializing.
3668
- */
3669
- onHydrateState(stored) {
3670
- return stored;
3671
- }
3672
- };
3673
-
3674
3262
  // src/server/rooms/canvas-room.ts
3675
- import * as Y5 from "yjs";
3676
3263
  var CANVAS_WRITE_TYPES = /* @__PURE__ */ new Set([
3677
3264
  MSG.CANVAS_ADD,
3678
3265
  MSG.CANVAS_MOVE,
@@ -6018,7 +5605,6 @@ var RESERVED_COLLECTION_NAMES = new Set(
6018
5605
  // src/shared/protocol/app-migrations.ts
6019
5606
  var APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
6020
5607
  var MAX_APP_MIGRATION_IDS = 256;
6021
- var CANONICAL_APP_IDENTITY_MIGRATION_ID = "2026-08-canonical-app-identity-wire";
6022
5608
  var WORKER_OWNED_NOT_FOUND_MIGRATION_ID = "2026-08-worker-owned-not-found";
6023
5609
  function validateAppMigrationIds(value) {
6024
5610
  if (!Array.isArray(value)) {
@@ -9498,7 +9084,6 @@ export {
9498
9084
  BROWSER_PROXY_ROUTES,
9499
9085
  BUILT_IN_TOOLS,
9500
9086
  BaseRoom,
9501
- CANONICAL_APP_IDENTITY_MIGRATION_ID,
9502
9087
  CHANNELS_SCHEMA,
9503
9088
  CHANNEL_INVITATIONS_SCHEMA,
9504
9089
  CHANNEL_MEMBERS_SCHEMA,
@@ -9520,7 +9105,6 @@ export {
9520
9105
  DeepSpaceAgentProfileError,
9521
9106
  GLOBAL_DO_TYPES,
9522
9107
  GLOBAL_DO_TYPE_NAMES,
9523
- GameRoom,
9524
9108
  JobRoom,
9525
9109
  LOG_LEVELS,
9526
9110
  MAX_APP_FILE_BYTES,