deepspace 0.15.0 → 0.17.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/CHANGELOG.md +118 -0
- package/dist/cli.js +562 -237
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +26 -157
- package/dist/index.js +209 -331
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +28 -154
- package/dist/server.js +38 -426
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +44 -227
- package/dist/worker.js +39 -433
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
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)) {
|
|
@@ -6317,19 +5903,21 @@ var MAX_UPLOAD_REQUEST_BYTES = 25 * 1024 * 1024;
|
|
|
6317
5903
|
var UPLOAD_PART_BYTES = 20 * 1024 * 1024;
|
|
6318
5904
|
var MAX_UPLOAD_PARTS = Math.ceil(MAX_APP_FILE_BYTES / UPLOAD_PART_BYTES);
|
|
6319
5905
|
var MAX_BASE64_UPLOAD_BYTES = Math.floor(MAX_UPLOAD_REQUEST_BYTES * 3 / 4);
|
|
6320
|
-
var
|
|
5906
|
+
var GIB = 1024 * 1024 * 1024;
|
|
5907
|
+
var ACCOUNT_STORAGE_LIMIT_BYTES = {
|
|
6321
5908
|
test: 0,
|
|
6322
|
-
free:
|
|
6323
|
-
starter:
|
|
6324
|
-
premium:
|
|
6325
|
-
admin:
|
|
5909
|
+
free: 1 * GIB,
|
|
5910
|
+
starter: 10 * GIB,
|
|
5911
|
+
premium: 30 * GIB,
|
|
5912
|
+
admin: 100 * GIB
|
|
6326
5913
|
};
|
|
6327
5914
|
function storageLimitForTier(tier) {
|
|
6328
|
-
if (tier && Object.hasOwn(
|
|
6329
|
-
return
|
|
5915
|
+
if (tier && Object.hasOwn(ACCOUNT_STORAGE_LIMIT_BYTES, tier)) {
|
|
5916
|
+
return ACCOUNT_STORAGE_LIMIT_BYTES[tier];
|
|
6330
5917
|
}
|
|
6331
|
-
return
|
|
5918
|
+
return ACCOUNT_STORAGE_LIMIT_BYTES.free;
|
|
6332
5919
|
}
|
|
5920
|
+
var REPO_STORE_LIMIT_BYTES = 256 * 1024 * 1024;
|
|
6333
5921
|
function storageQuotaMessage(incomingBytes, usedBytes, limitBytes) {
|
|
6334
5922
|
const free = Math.max(0, limitBytes - usedBytes);
|
|
6335
5923
|
return `This upload needs ${formatBytes(incomingBytes)} but the app's file storage has ${formatBytes(free)} free (${formatBytes(usedBytes)} of ${formatBytes(limitBytes)} used). Delete files with \`deepspace app files rm <key>\`, or upgrade the owner's plan.`;
|
|
@@ -6420,7 +6008,7 @@ function declaredLength(request, what) {
|
|
|
6420
6008
|
function isKeyWithinPrefix(key, prefix) {
|
|
6421
6009
|
return key.startsWith(prefix);
|
|
6422
6010
|
}
|
|
6423
|
-
async function
|
|
6011
|
+
async function prefixUsage(bucket, prefix) {
|
|
6424
6012
|
let total = 0;
|
|
6425
6013
|
let cursor;
|
|
6426
6014
|
do {
|
|
@@ -6430,6 +6018,13 @@ async function allocationUsage(bucket, prefix) {
|
|
|
6430
6018
|
} while (cursor !== void 0);
|
|
6431
6019
|
return total;
|
|
6432
6020
|
}
|
|
6021
|
+
async function allocationUsage(bucket, storage) {
|
|
6022
|
+
const siblings = storage.siblingPrefixes ? await storage.siblingPrefixes() : [];
|
|
6023
|
+
if (siblings === null) return null;
|
|
6024
|
+
const prefixes = [storage.prefix, ...siblings.filter((p) => p !== storage.prefix)];
|
|
6025
|
+
const totals = await Promise.all(prefixes.map((prefix) => prefixUsage(bucket, prefix)));
|
|
6026
|
+
return totals.reduce((sum, bytes) => sum + bytes, 0);
|
|
6027
|
+
}
|
|
6433
6028
|
async function admitStorage(bucket, storage, incomingBytes, replacesKey) {
|
|
6434
6029
|
if (!storage) return null;
|
|
6435
6030
|
const limit = await storage.limitBytes();
|
|
@@ -6440,7 +6035,14 @@ async function admitStorage(bucket, storage, incomingBytes, replacesKey) {
|
|
|
6440
6035
|
"storage_limit_unavailable"
|
|
6441
6036
|
);
|
|
6442
6037
|
}
|
|
6443
|
-
const stored = await allocationUsage(bucket, storage
|
|
6038
|
+
const stored = await allocationUsage(bucket, storage);
|
|
6039
|
+
if (stored === null) {
|
|
6040
|
+
return fail(
|
|
6041
|
+
503,
|
|
6042
|
+
"The storage already used by this account could not be measured; nothing was uploaded. Retry shortly.",
|
|
6043
|
+
"storage_limit_unavailable"
|
|
6044
|
+
);
|
|
6045
|
+
}
|
|
6444
6046
|
const replaced = replacesKey === null ? 0 : (await bucket.head(replacesKey))?.size ?? 0;
|
|
6445
6047
|
const used = stored - replaced;
|
|
6446
6048
|
if (used + incomingBytes <= limit) return null;
|
|
@@ -6456,7 +6058,11 @@ async function admitAssembledObject(bucket, storage, key, objectBytes) {
|
|
|
6456
6058
|
console.error("[files:quota] limit unavailable at complete; keeping", key);
|
|
6457
6059
|
return null;
|
|
6458
6060
|
}
|
|
6459
|
-
const stored = await allocationUsage(bucket, storage
|
|
6061
|
+
const stored = await allocationUsage(bucket, storage);
|
|
6062
|
+
if (stored === null) {
|
|
6063
|
+
console.error("[files:quota] account usage unmeasurable at complete; keeping", key);
|
|
6064
|
+
return null;
|
|
6065
|
+
}
|
|
6460
6066
|
if (stored <= limit) return null;
|
|
6461
6067
|
await bucket.delete(key).catch(() => void 0);
|
|
6462
6068
|
return fail(
|
|
@@ -6853,9 +6459,10 @@ async function handleList(bucket, prefix, url, scope, auth) {
|
|
|
6853
6459
|
});
|
|
6854
6460
|
let storage;
|
|
6855
6461
|
if (auth.storage) {
|
|
6856
|
-
|
|
6462
|
+
const usedBytes = await allocationUsage(bucket, auth.storage);
|
|
6463
|
+
if (usedBytes !== null) storage = { usedBytes };
|
|
6857
6464
|
const limitBytes = await auth.storage.limitBytes();
|
|
6858
|
-
if (limitBytes !== null) storage.limitBytes = limitBytes;
|
|
6465
|
+
if (storage && limitBytes !== null) storage.limitBytes = limitBytes;
|
|
6859
6466
|
}
|
|
6860
6467
|
return Response.json(
|
|
6861
6468
|
{ files, truncated: listed.truncated, ...storage ? { storage } : {} },
|
|
@@ -9462,13 +9069,13 @@ var APP_LOG_EVENT_KEYS = [
|
|
|
9462
9069
|
"exception"
|
|
9463
9070
|
];
|
|
9464
9071
|
export {
|
|
9072
|
+
ACCOUNT_STORAGE_LIMIT_BYTES,
|
|
9465
9073
|
AI_CHATS_SCHEMA,
|
|
9466
9074
|
AI_MESSAGES_SCHEMA,
|
|
9467
9075
|
ALLOWED_BINDING_TYPES,
|
|
9468
9076
|
APP_ID_RE,
|
|
9469
9077
|
APP_LOG_EVENT_KEYS,
|
|
9470
9078
|
APP_NAME_RULES,
|
|
9471
|
-
APP_STORAGE_LIMIT_BYTES,
|
|
9472
9079
|
ASSET_NOT_FOUND_HANDLING_VALUES,
|
|
9473
9080
|
AUTO_PROVISIONABLE_TYPES,
|
|
9474
9081
|
AUTO_PROVISION_SENTINEL,
|
|
@@ -9477,7 +9084,6 @@ export {
|
|
|
9477
9084
|
BROWSER_PROXY_ROUTES,
|
|
9478
9085
|
BUILT_IN_TOOLS,
|
|
9479
9086
|
BaseRoom,
|
|
9480
|
-
CANONICAL_APP_IDENTITY_MIGRATION_ID,
|
|
9481
9087
|
CHANNELS_SCHEMA,
|
|
9482
9088
|
CHANNEL_INVITATIONS_SCHEMA,
|
|
9483
9089
|
CHANNEL_MEMBERS_SCHEMA,
|
|
@@ -9499,7 +9105,6 @@ export {
|
|
|
9499
9105
|
DeepSpaceAgentProfileError,
|
|
9500
9106
|
GLOBAL_DO_TYPES,
|
|
9501
9107
|
GLOBAL_DO_TYPE_NAMES,
|
|
9502
|
-
GameRoom,
|
|
9503
9108
|
JobRoom,
|
|
9504
9109
|
LOG_LEVELS,
|
|
9505
9110
|
MAX_APP_FILE_BYTES,
|
|
@@ -9520,6 +9125,7 @@ export {
|
|
|
9520
9125
|
READ_RECEIPTS_SCHEMA,
|
|
9521
9126
|
REGISTRY_INSTANCE,
|
|
9522
9127
|
RELEASE_STAMP_PATH,
|
|
9128
|
+
REPO_STORE_LIMIT_BYTES,
|
|
9523
9129
|
REQUIRED_COMPATIBILITY_FLAGS,
|
|
9524
9130
|
RESERVED_BINDING_NAMES,
|
|
9525
9131
|
RESERVED_COLLECTION_NAMES,
|