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/index.js CHANGED
@@ -570,15 +570,6 @@ var MSG = {
570
570
  ACK: "records.ack",
571
571
  LIST_SCHEMAS: "records.list_schemas",
572
572
  RESUBSCRIBE: "records.resubscribe",
573
- // ---- GameRoom --------------------------------------------------------
574
- GAME_STATE: "game.state",
575
- GAME_INPUT: "game.input",
576
- GAME_PLAYER_JOIN: "game.player_join",
577
- GAME_PLAYER_LEAVE: "game.player_leave",
578
- GAME_PLAYER_READY: "game.player_ready",
579
- GAME_START: "game.start",
580
- GAME_END: "game.end",
581
- GAME_TICK: "game.tick",
582
573
  // ---- CanvasRoom ------------------------------------------------------
583
574
  CANVAS_SHAPES: "canvas.shapes",
584
575
  CANVAS_ADD: "canvas.add",
@@ -641,11 +632,6 @@ var clientBuild = {
641
632
  // Yjs
642
633
  yjsJoin: (collection, recordId, fieldName) => ({ type: MSG.YJS_JOIN, payload: { collection, recordId, fieldName } }),
643
634
  yjsLeave: (collection, recordId, fieldName) => ({ type: MSG.YJS_LEAVE, payload: { collection, recordId, fieldName } }),
644
- // Game
645
- gameInput: (action, data = {}) => ({ type: MSG.GAME_INPUT, payload: { action, data } }),
646
- gamePlayerReady: () => ({ type: MSG.GAME_PLAYER_READY, payload: EMPTY }),
647
- gameStart: () => ({ type: MSG.GAME_START, payload: EMPTY }),
648
- gameEnd: () => ({ type: MSG.GAME_END, payload: EMPTY }),
649
635
  // Canvas — `CANVAS_ADD` / `CANVAS_VIEWPORT` payloads are flat (the shape
650
636
  // or viewport object is the whole payload).
651
637
  canvasAdd: (shape) => ({ type: MSG.CANVAS_ADD, payload: shape }),
@@ -697,14 +683,6 @@ var serverBuild = {
697
683
  type: MSG.YJS_JOIN,
698
684
  payload: { collection, recordId, fieldName, canWrite }
699
685
  }),
700
- // Game
701
- gameState: (state, tick, players, running) => ({ type: MSG.GAME_STATE, payload: { state, tick, players, running } }),
702
- gameTick: (state, tick) => ({ type: MSG.GAME_TICK, payload: { state, tick } }),
703
- gameStart: (state, tick) => ({ type: MSG.GAME_START, payload: { state, tick } }),
704
- gameEnd: (state, tick) => ({ type: MSG.GAME_END, payload: { state, tick } }),
705
- gamePlayerJoin: (player) => ({ type: MSG.GAME_PLAYER_JOIN, payload: { player } }),
706
- gamePlayerLeave: (userId) => ({ type: MSG.GAME_PLAYER_LEAVE, payload: { userId } }),
707
- gamePlayerReady: (userId) => ({ type: MSG.GAME_PLAYER_READY, payload: { userId } }),
708
686
  // Canvas
709
687
  canvasShapes: (shapes, viewports) => ({ type: MSG.CANVAS_SHAPES, payload: { shapes, viewports } }),
710
688
  canvasAdd: (shape) => ({ type: MSG.CANVAS_ADD, payload: { shape } }),
@@ -770,7 +748,6 @@ function encode(message) {
770
748
  // src/shared/protocol/app-migrations.ts
771
749
  var APP_MIGRATION_ID_RE = /^[a-z0-9][a-z0-9._-]{0,127}$/;
772
750
  var MAX_APP_MIGRATION_IDS = 256;
773
- var CANONICAL_APP_IDENTITY_MIGRATION_ID = "2026-08-canonical-app-identity-wire";
774
751
  var WORKER_OWNED_NOT_FOUND_MIGRATION_ID = "2026-08-worker-owned-not-found";
775
752
  function validateAppMigrationIds(value) {
776
753
  if (!Array.isArray(value)) {
@@ -1809,6 +1786,8 @@ function parseServerError(error) {
1809
1786
  // src/client/storage/record-socket.ts
1810
1787
  var WS_CONNECTING = 0;
1811
1788
  var WS_OPEN = 1;
1789
+ var PING_EVERY_MS = 15e3;
1790
+ var PONG_TIMEOUT_MS = 3e4;
1812
1791
  var RecordSocket = class {
1813
1792
  constructor(config) {
1814
1793
  this.config = config;
@@ -1824,6 +1803,14 @@ var RecordSocket = class {
1824
1803
  pendingRequests = /* @__PURE__ */ new Map();
1825
1804
  reconnectAttempt = 0;
1826
1805
  reconnectTimeout = null;
1806
+ heartbeat = null;
1807
+ /** Any inbound frame proves the peer is answering. */
1808
+ lastInboundAt = 0;
1809
+ /** Monotonic time of the one probe awaiting an inbound answer. */
1810
+ outstandingPingAt = null;
1811
+ /** Monotonic time of the prior heartbeat callback. A long gap means the
1812
+ * scheduler was suspended, so a pre-suspension probe cannot be judged. */
1813
+ lastHeartbeatCheckAt = 0;
1827
1814
  // Bumped by connect() (on entry) and disconnect()/destroy(). connect()
1828
1815
  // captures it before awaiting the auth token and re-checks after; a
1829
1816
  // mismatch means a teardown (or a newer connect) raced in during the
@@ -1872,10 +1859,12 @@ var RecordSocket = class {
1872
1859
  ws.onopen = () => {
1873
1860
  config.log?.("connected", config.roomId);
1874
1861
  config.listeners.onStatus("connected");
1862
+ this.startHeartbeat(ws);
1875
1863
  this.resubscribeAll(ws);
1876
1864
  };
1877
1865
  ws.onmessage = (event) => this.handleMessage(event);
1878
1866
  ws.onclose = () => {
1867
+ this.stopHeartbeat();
1879
1868
  config.log?.("disconnected", config.roomId);
1880
1869
  config.listeners.onStatus("disconnected");
1881
1870
  config.listeners.onReady(false);
@@ -1930,8 +1919,48 @@ var RecordSocket = class {
1930
1919
  this.reconnectTimeout = null;
1931
1920
  }
1932
1921
  }
1922
+ /**
1923
+ * Turn a peer that stopped answering into a `close` event. That is the whole
1924
+ * job — `onclose` already reports 'disconnected', marks every query stale,
1925
+ * and reconnects with backoff. Without this it is never called, because
1926
+ * nothing about the connection is actually broken.
1927
+ */
1928
+ startHeartbeat(ws) {
1929
+ this.stopHeartbeat();
1930
+ this.lastInboundAt = performance.now();
1931
+ this.lastHeartbeatCheckAt = this.lastInboundAt;
1932
+ this.outstandingPingAt = null;
1933
+ this.heartbeat = setInterval(() => {
1934
+ if (ws.readyState !== WS_OPEN) return;
1935
+ const now = performance.now();
1936
+ const callbackGap = now - this.lastHeartbeatCheckAt;
1937
+ this.lastHeartbeatCheckAt = now;
1938
+ if (this.outstandingPingAt !== null) {
1939
+ if (now - this.outstandingPingAt >= PONG_TIMEOUT_MS) {
1940
+ if (callbackGap >= PONG_TIMEOUT_MS) {
1941
+ ws.send("ping");
1942
+ this.outstandingPingAt = now;
1943
+ } else {
1944
+ ws.close();
1945
+ }
1946
+ }
1947
+ return;
1948
+ }
1949
+ if (now - this.lastInboundAt >= PING_EVERY_MS) {
1950
+ ws.send("ping");
1951
+ this.outstandingPingAt = now;
1952
+ }
1953
+ }, PING_EVERY_MS);
1954
+ }
1955
+ stopHeartbeat() {
1956
+ if (this.heartbeat) clearInterval(this.heartbeat);
1957
+ this.heartbeat = null;
1958
+ this.outstandingPingAt = null;
1959
+ this.lastHeartbeatCheckAt = 0;
1960
+ }
1933
1961
  /** Close without firing onclose — prevents the zombie-reconnect. */
1934
1962
  teardownSocket() {
1963
+ this.stopHeartbeat();
1935
1964
  const ws = this.ws;
1936
1965
  if (!ws) return;
1937
1966
  this.config.log?.("closing", this.config.roomId);
@@ -2000,6 +2029,9 @@ var RecordSocket = class {
2000
2029
  }
2001
2030
  }
2002
2031
  handleMessage(event) {
2032
+ this.lastInboundAt = performance.now();
2033
+ this.outstandingPingAt = null;
2034
+ if (event.data === "pong") return;
2003
2035
  if (event.data instanceof ArrayBuffer) {
2004
2036
  this.binaryHandlers.forEach((h) => h(event.data));
2005
2037
  return;
@@ -2973,123 +3005,15 @@ function useYjsRoom(docId, fieldName) {
2973
3005
  return { doc, awareness, text, setText, synced, canWrite, writeAuthResolved };
2974
3006
  }
2975
3007
 
2976
- // src/client/storage/hooks/useGameRoom.ts
3008
+ // src/client/storage/hooks/useCanvas.ts
2977
3009
  import { useState as useState8, useEffect as useEffect11, useCallback as useCallback10, useRef as useRef9 } from "react";
2978
- function useGameRoom(roomId) {
2979
- const [gameState, setGameState] = useState8({});
2980
- const [tick, setTick] = useState8(0);
2981
- const [players, setPlayers] = useState8([]);
2982
- const [running, setRunning] = useState8(false);
3010
+ function useCanvas(roomId) {
3011
+ const [shapes, setShapes] = useState8([]);
3012
+ const [viewports, setViewports] = useState8([]);
2983
3013
  const [connected, setConnected] = useState8(false);
2984
3014
  const [canWrite, setCanWrite] = useState8(false);
2985
3015
  const wsRef = useRef9(null);
2986
3016
  useEffect11(() => {
2987
- let ws = null;
2988
- let reconnectTimer = null;
2989
- let alive = true;
2990
- const connect = async () => {
2991
- if (!alive) return;
2992
- const token = await getAuthToken();
2993
- const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
2994
- const baseUrl = `${protocol}//${window.location.host}`;
2995
- const url = new URL(`/ws/game/${encodeURIComponent(roomId)}`, baseUrl);
2996
- if (token) url.searchParams.set("token", token);
2997
- wsLog("connecting", `game:${roomId}`);
2998
- ws = new WebSocket(url.toString());
2999
- wsRef.current = ws;
3000
- ws.onopen = () => {
3001
- wsLog("connected", `game:${roomId}`);
3002
- setConnected(true);
3003
- };
3004
- ws.onmessage = (event) => {
3005
- dispatch(event.data, {
3006
- [MSG.AUTH]: (p) => {
3007
- setCanWrite(p.canWrite);
3008
- },
3009
- [MSG.GAME_STATE]: (p) => {
3010
- setGameState(p.state);
3011
- setTick(p.tick);
3012
- setPlayers(p.players);
3013
- setRunning(p.running);
3014
- },
3015
- [MSG.GAME_TICK]: (p) => {
3016
- setGameState(p.state);
3017
- setTick(p.tick);
3018
- },
3019
- [MSG.GAME_PLAYER_JOIN]: (p) => {
3020
- const player = p.player;
3021
- setPlayers((prev) => [...prev.filter((x) => x.userId !== player.userId), player]);
3022
- },
3023
- [MSG.GAME_PLAYER_LEAVE]: (p) => {
3024
- setPlayers((prev) => prev.filter((x) => x.userId !== p.userId));
3025
- },
3026
- [MSG.GAME_PLAYER_READY]: (p) => {
3027
- setPlayers(
3028
- (prev) => prev.map((x) => x.userId === p.userId ? { ...x, ready: true } : x)
3029
- );
3030
- },
3031
- [MSG.GAME_START]: (p) => {
3032
- setRunning(true);
3033
- setGameState(p.state);
3034
- setTick(p.tick);
3035
- },
3036
- [MSG.GAME_END]: (p) => {
3037
- setRunning(false);
3038
- setGameState(p.state);
3039
- }
3040
- });
3041
- };
3042
- ws.onclose = () => {
3043
- wsLog("disconnected", `game:${roomId}`);
3044
- wsRef.current = null;
3045
- setConnected(false);
3046
- setCanWrite(false);
3047
- if (alive) reconnectTimer = setTimeout(connect, 1e3);
3048
- };
3049
- ws.onerror = () => ws?.close();
3050
- };
3051
- connect();
3052
- return () => {
3053
- wsLog("closing", `game:${roomId}`);
3054
- alive = false;
3055
- if (reconnectTimer) clearTimeout(reconnectTimer);
3056
- if (ws) {
3057
- ws.onclose = null;
3058
- ws.onmessage = null;
3059
- ws.onerror = null;
3060
- ws.close();
3061
- }
3062
- wsRef.current = null;
3063
- };
3064
- }, [roomId]);
3065
- const send = useCallback10(
3066
- (message) => {
3067
- if (!canWrite) return;
3068
- const ws = wsRef.current;
3069
- if (!ws || ws.readyState !== WebSocket.OPEN) return;
3070
- ws.send(encode(message));
3071
- },
3072
- [canWrite]
3073
- );
3074
- const sendInput = useCallback10(
3075
- (action, data = {}) => send(clientBuild.gameInput(action, data)),
3076
- [send]
3077
- );
3078
- const setReady = useCallback10(() => send(clientBuild.gamePlayerReady()), [send]);
3079
- const startGame = useCallback10(() => send(clientBuild.gameStart()), [send]);
3080
- const endGame = useCallback10(() => send(clientBuild.gameEnd()), [send]);
3081
- return { state: gameState, tick, players, running, connected, canWrite, sendInput, setReady, startGame, endGame };
3082
- }
3083
-
3084
- // src/client/storage/hooks/useCanvas.ts
3085
- import { useState as useState9, useEffect as useEffect12, useCallback as useCallback11, useRef as useRef10 } from "react";
3086
- function useCanvas(roomId) {
3087
- const [shapes, setShapes] = useState9([]);
3088
- const [viewports, setViewports] = useState9([]);
3089
- const [connected, setConnected] = useState9(false);
3090
- const [canWrite, setCanWrite] = useState9(false);
3091
- const wsRef = useRef10(null);
3092
- useEffect12(() => {
3093
3017
  let ws = null;
3094
3018
  let reconnectTimer = null;
3095
3019
  let alive = true;
@@ -3183,7 +3107,7 @@ function useCanvas(roomId) {
3183
3107
  wsRef.current = null;
3184
3108
  };
3185
3109
  }, [roomId]);
3186
- const sendBuilt = useCallback11(
3110
+ const sendBuilt = useCallback10(
3187
3111
  (message) => {
3188
3112
  const ws = wsRef.current;
3189
3113
  if (!ws || ws.readyState !== WebSocket.OPEN) return;
@@ -3191,51 +3115,51 @@ function useCanvas(roomId) {
3191
3115
  },
3192
3116
  []
3193
3117
  );
3194
- const sendWrite = useCallback11(
3118
+ const sendWrite = useCallback10(
3195
3119
  (message) => {
3196
3120
  if (!canWrite) return;
3197
3121
  sendBuilt(message);
3198
3122
  },
3199
3123
  [sendBuilt, canWrite]
3200
3124
  );
3201
- const addShape = useCallback11(
3125
+ const addShape = useCallback10(
3202
3126
  (shape) => sendWrite(clientBuild.canvasAdd(shape)),
3203
3127
  [sendWrite]
3204
3128
  );
3205
- const moveShape = useCallback11(
3129
+ const moveShape = useCallback10(
3206
3130
  (shapeId, x, y) => sendWrite(clientBuild.canvasMove(shapeId, x, y)),
3207
3131
  [sendWrite]
3208
3132
  );
3209
- const resizeShape = useCallback11(
3133
+ const resizeShape = useCallback10(
3210
3134
  (shapeId, width, height, x, y) => sendWrite(clientBuild.canvasResize(shapeId, width, height, x, y)),
3211
3135
  [sendWrite]
3212
3136
  );
3213
- const deleteShape = useCallback11(
3137
+ const deleteShape = useCallback10(
3214
3138
  (shapeId) => sendWrite(clientBuild.canvasDelete(shapeId)),
3215
3139
  [sendWrite]
3216
3140
  );
3217
- const updateShape = useCallback11(
3141
+ const updateShape = useCallback10(
3218
3142
  (shapeId, props) => sendWrite(clientBuild.canvasUpdate(shapeId, props)),
3219
3143
  [sendWrite]
3220
3144
  );
3221
- const setViewport = useCallback11(
3145
+ const setViewport = useCallback10(
3222
3146
  (viewport) => sendBuilt(clientBuild.canvasViewport(viewport)),
3223
3147
  [sendBuilt]
3224
3148
  );
3225
- const undo = useCallback11(() => sendWrite(clientBuild.canvasUndo()), [sendWrite]);
3226
- const redo = useCallback11(() => sendWrite(clientBuild.canvasRedo()), [sendWrite]);
3149
+ const undo = useCallback10(() => sendWrite(clientBuild.canvasUndo()), [sendWrite]);
3150
+ const redo = useCallback10(() => sendWrite(clientBuild.canvasRedo()), [sendWrite]);
3227
3151
  return { shapes, viewports, connected, canWrite, addShape, moveShape, resizeShape, deleteShape, updateShape, setViewport, undo, redo };
3228
3152
  }
3229
3153
 
3230
3154
  // src/client/storage/hooks/useCronMonitor.ts
3231
- import { useState as useState10, useEffect as useEffect13, useCallback as useCallback12, useRef as useRef11 } from "react";
3155
+ import { useState as useState9, useEffect as useEffect12, useCallback as useCallback11, useRef as useRef10 } from "react";
3232
3156
  function useCronMonitor(roomId) {
3233
- const [tasks, setTasks] = useState10([]);
3234
- const [history, setHistory] = useState10([]);
3235
- const [connected, setConnected] = useState10(false);
3236
- const [canWrite, setCanWrite] = useState10(false);
3237
- const wsRef = useRef11(null);
3238
- useEffect13(() => {
3157
+ const [tasks, setTasks] = useState9([]);
3158
+ const [history, setHistory] = useState9([]);
3159
+ const [connected, setConnected] = useState9(false);
3160
+ const [canWrite, setCanWrite] = useState9(false);
3161
+ const wsRef = useRef10(null);
3162
+ useEffect12(() => {
3239
3163
  let ws = null;
3240
3164
  let reconnectTimer = null;
3241
3165
  let alive = true;
@@ -3282,7 +3206,7 @@ function useCronMonitor(roomId) {
3282
3206
  wsRef.current = null;
3283
3207
  };
3284
3208
  }, [roomId]);
3285
- const sendWrite = useCallback12(
3209
+ const sendWrite = useCallback11(
3286
3210
  (message) => {
3287
3211
  if (!canWrite) return;
3288
3212
  const ws = wsRef.current;
@@ -3291,15 +3215,15 @@ function useCronMonitor(roomId) {
3291
3215
  },
3292
3216
  [canWrite]
3293
3217
  );
3294
- const trigger = useCallback12(
3218
+ const trigger = useCallback11(
3295
3219
  (taskName) => sendWrite(clientBuild.cronTrigger(taskName)),
3296
3220
  [sendWrite]
3297
3221
  );
3298
- const pause = useCallback12(
3222
+ const pause = useCallback11(
3299
3223
  (taskName) => sendWrite(clientBuild.cronPause(taskName)),
3300
3224
  [sendWrite]
3301
3225
  );
3302
- const resume = useCallback12(
3226
+ const resume = useCallback11(
3303
3227
  (taskName) => sendWrite(clientBuild.cronResume(taskName)),
3304
3228
  [sendWrite]
3305
3229
  );
@@ -3307,14 +3231,14 @@ function useCronMonitor(roomId) {
3307
3231
  }
3308
3232
 
3309
3233
  // src/client/storage/hooks/useJobs.ts
3310
- import { useState as useState11, useEffect as useEffect14, useCallback as useCallback13, useRef as useRef12 } from "react";
3234
+ import { useState as useState10, useEffect as useEffect13, useCallback as useCallback12, useRef as useRef11 } from "react";
3311
3235
  var ENQUEUE_TIMEOUT_MS = 1e4;
3312
3236
  function useJobs(roomId) {
3313
- const [jobs, setJobs] = useState11([]);
3314
- const [connected, setConnected] = useState11(false);
3315
- const wsRef = useRef12(null);
3316
- const pendingEnqueueRef = useRef12(/* @__PURE__ */ new Map());
3317
- useEffect14(() => {
3237
+ const [jobs, setJobs] = useState10([]);
3238
+ const [connected, setConnected] = useState10(false);
3239
+ const wsRef = useRef11(null);
3240
+ const pendingEnqueueRef = useRef11(/* @__PURE__ */ new Map());
3241
+ useEffect13(() => {
3318
3242
  let ws = null;
3319
3243
  let reconnectTimer = null;
3320
3244
  let alive = true;
@@ -3382,7 +3306,7 @@ function useJobs(roomId) {
3382
3306
  wsRef.current = null;
3383
3307
  };
3384
3308
  }, [roomId]);
3385
- const enqueue = useCallback13(
3309
+ const enqueue = useCallback12(
3386
3310
  (type, payload, options) => {
3387
3311
  return new Promise((resolve, reject) => {
3388
3312
  const ws = wsRef.current;
@@ -3413,7 +3337,7 @@ function useJobs(roomId) {
3413
3337
  },
3414
3338
  []
3415
3339
  );
3416
- const sendBuilt = useCallback13(
3340
+ const sendBuilt = useCallback12(
3417
3341
  (message) => {
3418
3342
  const ws = wsRef.current;
3419
3343
  if (!ws || ws.readyState !== WebSocket.OPEN) return;
@@ -3421,15 +3345,15 @@ function useJobs(roomId) {
3421
3345
  },
3422
3346
  []
3423
3347
  );
3424
- const getJob = useCallback13(
3348
+ const getJob = useCallback12(
3425
3349
  (jobId) => jobs.find((j) => j.id === jobId),
3426
3350
  [jobs]
3427
3351
  );
3428
- const cancel = useCallback13(
3352
+ const cancel = useCallback12(
3429
3353
  (jobId) => sendBuilt(clientBuild.jobCancel(jobId)),
3430
3354
  [sendBuilt]
3431
3355
  );
3432
- const retry = useCallback13(
3356
+ const retry = useCallback12(
3433
3357
  (jobId) => sendBuilt(clientBuild.jobRetry(jobId)),
3434
3358
  [sendBuilt]
3435
3359
  );
@@ -3437,12 +3361,12 @@ function useJobs(roomId) {
3437
3361
  }
3438
3362
 
3439
3363
  // src/client/storage/hooks/usePresenceRoom.ts
3440
- import { useState as useState12, useEffect as useEffect15, useCallback as useCallback14, useRef as useRef13 } from "react";
3364
+ import { useState as useState11, useEffect as useEffect14, useCallback as useCallback13, useRef as useRef12 } from "react";
3441
3365
  function usePresenceRoom(scopeId) {
3442
- const [peers, setPeers] = useState12([]);
3443
- const [connected, setConnected] = useState12(false);
3444
- const wsRef = useRef13(null);
3445
- useEffect15(() => {
3366
+ const [peers, setPeers] = useState11([]);
3367
+ const [connected, setConnected] = useState11(false);
3368
+ const wsRef = useRef12(null);
3369
+ useEffect14(() => {
3446
3370
  let ws = null;
3447
3371
  let reconnectTimer = null;
3448
3372
  let alive = true;
@@ -3503,7 +3427,7 @@ function usePresenceRoom(scopeId) {
3503
3427
  wsRef.current = null;
3504
3428
  };
3505
3429
  }, [scopeId]);
3506
- const updateState = useCallback14((state) => {
3430
+ const updateState = useCallback13((state) => {
3507
3431
  const ws = wsRef.current;
3508
3432
  if (!ws || ws.readyState !== WebSocket.OPEN) return;
3509
3433
  ws.send(encode(clientBuild.presenceUpdate(state)));
@@ -3519,7 +3443,7 @@ function toConnectionStatus(queryStatus) {
3519
3443
  }
3520
3444
 
3521
3445
  // src/client/storage/useR2Files.ts
3522
- import { useCallback as useCallback15, useState as useState13, useMemo as useMemo11 } from "react";
3446
+ import { useCallback as useCallback14, useState as useState12, useMemo as useMemo11 } from "react";
3523
3447
 
3524
3448
  // src/shared/app-files.ts
3525
3449
  var MAX_APP_FILE_BYTES = 1024 * 1024 * 1024;
@@ -3659,21 +3583,21 @@ async function uploadInParts(file, name, scopeParams, authHeaders, onProgress) {
3659
3583
  }
3660
3584
  }
3661
3585
  function useR2Files(options) {
3662
- const [isUploading, setIsUploading] = useState13(false);
3586
+ const [isUploading, setIsUploading] = useState12(false);
3663
3587
  const scope = options?.scope ?? "self";
3664
3588
  const scopeParams = useMemo11(() => {
3665
3589
  const params = new URLSearchParams();
3666
3590
  params.set("scope", scope);
3667
3591
  return params.toString();
3668
3592
  }, [scope]);
3669
- const authHeaders = useCallback15(async () => {
3593
+ const authHeaders = useCallback14(async () => {
3670
3594
  const token = await getAuthToken();
3671
3595
  if (token) {
3672
3596
  return { Authorization: `Bearer ${token}` };
3673
3597
  }
3674
3598
  return {};
3675
3599
  }, []);
3676
- const upload = useCallback15(
3600
+ const upload = useCallback14(
3677
3601
  async (file, name, options2) => {
3678
3602
  setIsUploading(true);
3679
3603
  try {
@@ -3702,7 +3626,7 @@ function useR2Files(options) {
3702
3626
  },
3703
3627
  [scopeParams, authHeaders]
3704
3628
  );
3705
- const uploadBase64 = useCallback15(
3629
+ const uploadBase64 = useCallback14(
3706
3630
  async (base64Data, name, mimeType) => {
3707
3631
  setIsUploading(true);
3708
3632
  try {
@@ -3726,7 +3650,7 @@ function useR2Files(options) {
3726
3650
  },
3727
3651
  [scopeParams, authHeaders]
3728
3652
  );
3729
- const deleteFile = useCallback15(
3653
+ const deleteFile = useCallback14(
3730
3654
  async (fileOrKey) => {
3731
3655
  const { key } = resolveFile(fileOrKey);
3732
3656
  try {
@@ -3742,7 +3666,7 @@ function useR2Files(options) {
3742
3666
  },
3743
3667
  [scopeParams, authHeaders]
3744
3668
  );
3745
- const list = useCallback15(
3669
+ const list = useCallback14(
3746
3670
  async (prefix) => {
3747
3671
  try {
3748
3672
  const headers = await authHeaders();
@@ -3757,7 +3681,7 @@ function useR2Files(options) {
3757
3681
  },
3758
3682
  [scopeParams, authHeaders]
3759
3683
  );
3760
- const downloadFile = useCallback15(
3684
+ const downloadFile = useCallback14(
3761
3685
  async (fileOrKey, fileName) => {
3762
3686
  const { key, name: infoName } = resolveFile(fileOrKey);
3763
3687
  try {
@@ -3787,7 +3711,7 @@ function useR2Files(options) {
3787
3711
  },
3788
3712
  [scopeParams, authHeaders]
3789
3713
  );
3790
- const readFile = useCallback15(
3714
+ const readFile = useCallback14(
3791
3715
  async (fileOrKey) => {
3792
3716
  const { key } = resolveFile(fileOrKey);
3793
3717
  const headers = await authHeaders();
@@ -3800,7 +3724,7 @@ function useR2Files(options) {
3800
3724
  },
3801
3725
  [scopeParams, authHeaders]
3802
3726
  );
3803
- const getUrl = useCallback15(
3727
+ const getUrl = useCallback14(
3804
3728
  (fileOrKey) => {
3805
3729
  const { key } = resolveFile(fileOrKey);
3806
3730
  return `/api/files/${encodeKeyPath(key)}?${scopeParams}`;
@@ -3846,7 +3770,7 @@ function getUserColor(userId, palette = DEFAULT_USER_COLORS) {
3846
3770
  }
3847
3771
 
3848
3772
  // src/client/messaging/useMessages.ts
3849
- import { useCallback as useCallback16, useMemo as useMemo12 } from "react";
3773
+ import { useCallback as useCallback15, useMemo as useMemo12 } from "react";
3850
3774
  function useMessages(channelId, options) {
3851
3775
  const where = useMemo12(() => {
3852
3776
  if (!channelId) return { channelId: "__none__" };
@@ -3863,7 +3787,7 @@ function useMessages(channelId, options) {
3863
3787
  orderDir: "asc"
3864
3788
  });
3865
3789
  const { create, put, remove: removeMutation } = useMutations("messages");
3866
- const send = useCallback16(
3790
+ const send = useCallback15(
3867
3791
  (content, parentMessageId) => {
3868
3792
  if (!channelId || !user) return;
3869
3793
  const data = { channelId, content, authorId: user.id, edited: false };
@@ -3872,19 +3796,19 @@ function useMessages(channelId, options) {
3872
3796
  },
3873
3797
  [channelId, user, create]
3874
3798
  );
3875
- const edit = useCallback16(
3799
+ const edit = useCallback15(
3876
3800
  (messageId, newContent) => {
3877
3801
  put(messageId, { content: newContent, edited: true });
3878
3802
  },
3879
3803
  [put]
3880
3804
  );
3881
- const remove = useCallback16(
3805
+ const remove = useCallback15(
3882
3806
  (messageId) => {
3883
3807
  removeMutation(messageId);
3884
3808
  },
3885
3809
  [removeMutation]
3886
3810
  );
3887
- const softDelete = useCallback16(
3811
+ const softDelete = useCallback15(
3888
3812
  (messageId) => {
3889
3813
  put(messageId, { deleted: true });
3890
3814
  },
@@ -3902,7 +3826,7 @@ function useMessages(channelId, options) {
3902
3826
  }
3903
3827
 
3904
3828
  // src/client/messaging/useChannels.ts
3905
- import { useCallback as useCallback17 } from "react";
3829
+ import { useCallback as useCallback16 } from "react";
3906
3830
  function useChannels() {
3907
3831
  const { user } = useUser();
3908
3832
  const { records, status, error } = useQuery("channels", {
@@ -3910,7 +3834,7 @@ function useChannels() {
3910
3834
  orderDir: "asc"
3911
3835
  });
3912
3836
  const { create: createMutation, put, remove } = useMutations("channels");
3913
- const create = useCallback17(
3837
+ const create = useCallback16(
3914
3838
  (data) => {
3915
3839
  return createMutation({
3916
3840
  name: data.name,
@@ -3922,13 +3846,13 @@ function useChannels() {
3922
3846
  },
3923
3847
  [user, createMutation]
3924
3848
  );
3925
- const archive = useCallback17(
3849
+ const archive = useCallback16(
3926
3850
  (channelId) => {
3927
3851
  put(channelId, { archived: true });
3928
3852
  },
3929
3853
  [put]
3930
3854
  );
3931
- const update = useCallback17(
3855
+ const update = useCallback16(
3932
3856
  (channelId, data) => {
3933
3857
  put(channelId, data);
3934
3858
  },
@@ -3946,14 +3870,14 @@ function useChannels() {
3946
3870
  }
3947
3871
 
3948
3872
  // src/client/messaging/useReactions.ts
3949
- import { useCallback as useCallback18 } from "react";
3873
+ import { useCallback as useCallback17 } from "react";
3950
3874
  function useReactions(channelId) {
3951
3875
  const { user } = useUser();
3952
3876
  const { records, status, error } = useQuery("reactions", {
3953
3877
  where: channelId ? { channelId } : { channelId: "__none__" }
3954
3878
  });
3955
3879
  const { create, remove: removeMutation } = useMutations("reactions");
3956
- const getReactionsForMessage = useCallback18(
3880
+ const getReactionsForMessage = useCallback17(
3957
3881
  (messageId) => {
3958
3882
  const messageReactions = records.filter((r) => r.data.messageId === messageId);
3959
3883
  const grouped = /* @__PURE__ */ new Map();
@@ -3970,7 +3894,7 @@ function useReactions(channelId) {
3970
3894
  },
3971
3895
  [records, user]
3972
3896
  );
3973
- const toggle = useCallback18(
3897
+ const toggle = useCallback17(
3974
3898
  (messageId, emoji) => {
3975
3899
  if (!channelId || !user) return;
3976
3900
  const existing = records.find(
@@ -3988,20 +3912,20 @@ function useReactions(channelId) {
3988
3912
  }
3989
3913
 
3990
3914
  // src/client/messaging/useChannelMembers.ts
3991
- import { useCallback as useCallback19 } from "react";
3915
+ import { useCallback as useCallback18 } from "react";
3992
3916
  function useChannelMembers(channelId) {
3993
3917
  const { user } = useUser();
3994
3918
  const { records, status, error } = useQuery("channel-members", {
3995
3919
  where: channelId ? { channelId } : { channelId: "__none__" }
3996
3920
  });
3997
3921
  const { create, remove: removeMutation } = useMutations("channel-members");
3998
- const join = useCallback19(() => {
3922
+ const join = useCallback18(() => {
3999
3923
  if (!channelId || !user) return;
4000
3924
  const existing = records.find((r) => r.data.userId === user.id);
4001
3925
  if (existing) return;
4002
3926
  create({ channelId, userId: user.id, joinedAt: (/* @__PURE__ */ new Date()).toISOString() });
4003
3927
  }, [channelId, user, records, create]);
4004
- const leave = useCallback19(() => {
3928
+ const leave = useCallback18(() => {
4005
3929
  if (!channelId || !user) return;
4006
3930
  const membership = records.find((r) => r.data.userId === user.id);
4007
3931
  if (membership) removeMutation(membership.recordId);
@@ -4011,14 +3935,14 @@ function useChannelMembers(channelId) {
4011
3935
  }
4012
3936
 
4013
3937
  // src/client/messaging/useReadReceipts.ts
4014
- import { useCallback as useCallback20 } from "react";
3938
+ import { useCallback as useCallback19 } from "react";
4015
3939
  function useReadReceipts() {
4016
3940
  const { user } = useUser();
4017
3941
  const { records, status, error } = useQuery("read-receipts", {
4018
3942
  where: user ? { userId: user.id } : { userId: "__none__" }
4019
3943
  });
4020
3944
  const { create, put } = useMutations("read-receipts");
4021
- const markAsRead = useCallback20(
3945
+ const markAsRead = useCallback19(
4022
3946
  (channelId) => {
4023
3947
  if (!user) return;
4024
3948
  const existing = records.find((r) => r.data.channelId === channelId);
@@ -4031,7 +3955,7 @@ function useReadReceipts() {
4031
3955
  },
4032
3956
  [user, records, create, put]
4033
3957
  );
4034
- const getUnreadCount = useCallback20(
3958
+ const getUnreadCount = useCallback19(
4035
3959
  (channelId, messages) => {
4036
3960
  const receipt = records.find((r) => r.data.channelId === channelId);
4037
3961
  if (!receipt) return messages.length;
@@ -4044,7 +3968,7 @@ function useReadReceipts() {
4044
3968
  }
4045
3969
 
4046
3970
  // src/client/messaging/useConversation.ts
4047
- import { useCallback as useCallback21, useMemo as useMemo13 } from "react";
3971
+ import { useCallback as useCallback20, useMemo as useMemo13 } from "react";
4048
3972
  function useConversation(opts) {
4049
3973
  const { user } = useUser();
4050
3974
  const { ready } = useRecordContext();
@@ -4053,7 +3977,7 @@ function useConversation(opts) {
4053
3977
  const { records: members } = useQuery("conv_members");
4054
3978
  const messageMut = useMutations("conv_messages");
4055
3979
  const reactionMut = useMutations("conv_reactions");
4056
- const send = useCallback21(
3980
+ const send = useCallback20(
4057
3981
  (content, parentMessageId, messageType, metadata) => {
4058
3982
  messageMut.create({
4059
3983
  Content: content,
@@ -4068,7 +3992,7 @@ function useConversation(opts) {
4068
3992
  // eslint-disable-next-line react-hooks/exhaustive-deps -- depend on the precise opts?.onMessageSent callback, not the whole opts object, to avoid recreating send when unrelated opts fields change
4069
3993
  [messageMut, user?.id, opts?.onMessageSent]
4070
3994
  );
4071
- const edit = useCallback21(
3995
+ const edit = useCallback20(
4072
3996
  (recordId, content) => {
4073
3997
  const existing = allMessages.find((m) => m.recordId === recordId);
4074
3998
  if (!existing) return;
@@ -4076,7 +4000,7 @@ function useConversation(opts) {
4076
4000
  },
4077
4001
  [messageMut, allMessages]
4078
4002
  );
4079
- const remove = useCallback21(
4003
+ const remove = useCallback20(
4080
4004
  (recordId) => {
4081
4005
  const existing = allMessages.find((m) => m.recordId === recordId);
4082
4006
  if (!existing) return;
@@ -4089,7 +4013,7 @@ function useConversation(opts) {
4089
4013
  },
4090
4014
  [messageMut, allMessages]
4091
4015
  );
4092
- const toggleReaction = useCallback21(
4016
+ const toggleReaction = useCallback20(
4093
4017
  (messageId, emoji) => {
4094
4018
  const existing = allReactions.find(
4095
4019
  (r) => r.data.MessageId === messageId && r.data.Emoji === emoji && r.data.UserId === user?.id
@@ -4222,7 +4146,7 @@ function parseMessageMetadata(raw) {
4222
4146
  }
4223
4147
 
4224
4148
  // src/client/directory/useConversations.ts
4225
- import { useCallback as useCallback22, useMemo as useMemo14, useRef as useRef14 } from "react";
4149
+ import { useCallback as useCallback21, useMemo as useMemo14, useRef as useRef13 } from "react";
4226
4150
  var defaultConvFields = {
4227
4151
  Visibility: "public",
4228
4152
  Status: "active",
@@ -4237,7 +4161,7 @@ function useConversations() {
4237
4161
  const stateMut = useMutations("conversation_state");
4238
4162
  const { user } = useUser();
4239
4163
  const ready = status === "ready";
4240
- const recentCreationsRef = useRef14(/* @__PURE__ */ new Map());
4164
+ const recentCreationsRef = useRef13(/* @__PURE__ */ new Map());
4241
4165
  for (const [id] of recentCreationsRef.current) {
4242
4166
  if (conversations.find((c) => c.recordId === id)) {
4243
4167
  recentCreationsRef.current.delete(id);
@@ -4259,13 +4183,13 @@ function useConversations() {
4259
4183
  () => new Set(stateRecords.filter((r) => r.data.Archived).map((r) => r.data.ConversationId)),
4260
4184
  [stateRecords]
4261
4185
  );
4262
- const getConversationState = useCallback22(
4186
+ const getConversationState = useCallback21(
4263
4187
  (conversationId) => {
4264
4188
  return stateRecords.find((r) => r.data.ConversationId === conversationId)?.data;
4265
4189
  },
4266
4190
  [stateRecords]
4267
4191
  );
4268
- const createChannel = useCallback22(
4192
+ const createChannel = useCallback21(
4269
4193
  async (name, description) => {
4270
4194
  const existing = conversations.find((r) => r.data.Name === name && r.data.Type !== "dm");
4271
4195
  if (existing) return existing.recordId;
@@ -4285,7 +4209,7 @@ function useConversations() {
4285
4209
  },
4286
4210
  [create, user?.id, conversations]
4287
4211
  );
4288
- const createDM = useCallback22(
4212
+ const createDM = useCallback21(
4289
4213
  async (otherUserId) => {
4290
4214
  if (!user?.id) return null;
4291
4215
  const hash = [user.id, otherUserId].sort().join(":");
@@ -4313,7 +4237,7 @@ function useConversations() {
4313
4237
  },
4314
4238
  [create, user?.id, conversations]
4315
4239
  );
4316
- const createGroupDM = useCallback22(
4240
+ const createGroupDM = useCallback21(
4317
4241
  async (participantIds) => {
4318
4242
  if (!user?.id) return null;
4319
4243
  const allIds = [.../* @__PURE__ */ new Set([user.id, ...participantIds])];
@@ -4343,14 +4267,14 @@ function useConversations() {
4343
4267
  },
4344
4268
  [create, user?.id, conversations]
4345
4269
  );
4346
- const lookupByName = useCallback22(
4270
+ const lookupByName = useCallback21(
4347
4271
  (name) => {
4348
4272
  const found = conversations.find((r) => r.data.Name === name);
4349
4273
  return found?.recordId ?? null;
4350
4274
  },
4351
4275
  [conversations]
4352
4276
  );
4353
- const updateLastMessage = useCallback22(
4277
+ const updateLastMessage = useCallback21(
4354
4278
  (conversationId, preview, authorId) => {
4355
4279
  const conv = conversations.find((c) => c.recordId === conversationId);
4356
4280
  const baseData = conv?.data ?? recentCreationsRef.current.get(conversationId);
@@ -4369,7 +4293,7 @@ function useConversations() {
4369
4293
  },
4370
4294
  [put, conversations]
4371
4295
  );
4372
- const upsertState = useCallback22(
4296
+ const upsertState = useCallback21(
4373
4297
  (conversationId, partial) => {
4374
4298
  if (!user?.id) return;
4375
4299
  const existing = stateRecords.find(
@@ -4391,7 +4315,7 @@ function useConversations() {
4391
4315
  },
4392
4316
  [stateMut, stateRecords, user?.id]
4393
4317
  );
4394
- const markRead = useCallback22(
4318
+ const markRead = useCallback21(
4395
4319
  (conversationId) => {
4396
4320
  const conv = conversations.find((c) => c.recordId === conversationId);
4397
4321
  const msgCount = conv?.data.MessageCount ?? recentCreationsRef.current.get(conversationId)?.MessageCount ?? 0;
@@ -4402,32 +4326,32 @@ function useConversations() {
4402
4326
  },
4403
4327
  [upsertState, conversations]
4404
4328
  );
4405
- const toggleStar = useCallback22(
4329
+ const toggleStar = useCallback21(
4406
4330
  (conversationId) => {
4407
4331
  const isStarred = starredSet.has(conversationId);
4408
4332
  upsertState(conversationId, { Starred: isStarred ? 0 : 1 });
4409
4333
  },
4410
4334
  [upsertState, starredSet]
4411
4335
  );
4412
- const setArchived = useCallback22(
4336
+ const setArchived = useCallback21(
4413
4337
  (conversationId, archived) => {
4414
4338
  upsertState(conversationId, { Archived: archived ? 1 : 0 });
4415
4339
  },
4416
4340
  [upsertState]
4417
4341
  );
4418
- const setTrashed = useCallback22(
4342
+ const setTrashed = useCallback21(
4419
4343
  (conversationId, trashed) => {
4420
4344
  upsertState(conversationId, { Trashed: trashed ? 1 : 0 });
4421
4345
  },
4422
4346
  [upsertState]
4423
4347
  );
4424
- const setLabels = useCallback22(
4348
+ const setLabels = useCallback21(
4425
4349
  (conversationId, labels) => {
4426
4350
  upsertState(conversationId, { Labels: JSON.stringify(labels) });
4427
4351
  },
4428
4352
  [upsertState]
4429
4353
  );
4430
- const setFolder = useCallback22(
4354
+ const setFolder = useCallback21(
4431
4355
  (conversationId, folder) => {
4432
4356
  upsertState(conversationId, { Folder: folder });
4433
4357
  },
@@ -4457,7 +4381,7 @@ function useConversations() {
4457
4381
  }
4458
4382
 
4459
4383
  // src/client/directory/useCommunities.ts
4460
- import { useCallback as useCallback23, useMemo as useMemo15 } from "react";
4384
+ import { useCallback as useCallback22, useMemo as useMemo15 } from "react";
4461
4385
  function useCommunities() {
4462
4386
  const { records: communities, status } = useQuery("communities");
4463
4387
  const communityMut = useMutations("communities");
@@ -4480,7 +4404,7 @@ function useCommunities() {
4480
4404
  ),
4481
4405
  [memberships, user?.id]
4482
4406
  );
4483
- const createCommunity = useCallback23(
4407
+ const createCommunity = useCallback22(
4484
4408
  async (name, opts) => {
4485
4409
  const existing = communities.find((c) => c.data.Name === name);
4486
4410
  if (existing) return existing.recordId;
@@ -4499,7 +4423,7 @@ function useCommunities() {
4499
4423
  },
4500
4424
  [communityMut, user?.id, communities]
4501
4425
  );
4502
- const updateCommunity = useCallback23(
4426
+ const updateCommunity = useCallback22(
4503
4427
  (communityId, updates) => {
4504
4428
  const existing = communities.find((c) => c.recordId === communityId);
4505
4429
  if (!existing) return;
@@ -4507,7 +4431,7 @@ function useCommunities() {
4507
4431
  },
4508
4432
  [communityMut, communities]
4509
4433
  );
4510
- const joinCommunity = useCallback23(
4434
+ const joinCommunity = useCallback22(
4511
4435
  async (communityId, userName) => {
4512
4436
  if (!user?.id) return null;
4513
4437
  const existing = memberships.find(
@@ -4525,7 +4449,7 @@ function useCommunities() {
4525
4449
  },
4526
4450
  [membershipMut, memberships, user?.id]
4527
4451
  );
4528
- const leaveCommunity = useCallback23(
4452
+ const leaveCommunity = useCallback22(
4529
4453
  (communityId) => {
4530
4454
  if (!user?.id) return;
4531
4455
  const membership = memberships.find(
@@ -4537,13 +4461,13 @@ function useCommunities() {
4537
4461
  },
4538
4462
  [membershipMut, memberships, user?.id]
4539
4463
  );
4540
- const getMembersOf = useCallback23(
4464
+ const getMembersOf = useCallback22(
4541
4465
  (communityId) => {
4542
4466
  return membershipMap.get(communityId) ?? [];
4543
4467
  },
4544
4468
  [membershipMap]
4545
4469
  );
4546
- const lookupByName = useCallback23(
4470
+ const lookupByName = useCallback22(
4547
4471
  (name) => {
4548
4472
  const found = communities.find((c) => c.data.Name === name);
4549
4473
  return found?.recordId ?? null;
@@ -4565,14 +4489,14 @@ function useCommunities() {
4565
4489
  }
4566
4490
 
4567
4491
  // src/client/directory/usePosts.ts
4568
- import { useCallback as useCallback24 } from "react";
4492
+ import { useCallback as useCallback23 } from "react";
4569
4493
  function usePosts(opts) {
4570
4494
  const { records: allPosts, status } = useQuery("posts");
4571
4495
  const postMut = useMutations("posts");
4572
4496
  const { user } = useUser();
4573
4497
  const ready = status === "ready";
4574
4498
  const posts = opts?.communityId ? allPosts.filter((p) => p.data.CommunityId === opts.communityId) : allPosts;
4575
- const createPost = useCallback24(
4499
+ const createPost = useCallback23(
4576
4500
  async (data) => {
4577
4501
  const recordId = await postMut.create({
4578
4502
  Title: data.title,
@@ -4590,7 +4514,7 @@ function usePosts(opts) {
4590
4514
  },
4591
4515
  [postMut, user?.id]
4592
4516
  );
4593
- const updatePost = useCallback24(
4517
+ const updatePost = useCallback23(
4594
4518
  (postId, updates) => {
4595
4519
  const existing = allPosts.find((p) => p.recordId === postId);
4596
4520
  if (!existing) return;
@@ -4598,13 +4522,13 @@ function usePosts(opts) {
4598
4522
  },
4599
4523
  [postMut, allPosts]
4600
4524
  );
4601
- const deletePost = useCallback24(
4525
+ const deletePost = useCallback23(
4602
4526
  (postId) => {
4603
4527
  postMut.remove(postId);
4604
4528
  },
4605
4529
  [postMut]
4606
4530
  );
4607
- const setConversationId = useCallback24(
4531
+ const setConversationId = useCallback23(
4608
4532
  (postId, conversationId) => {
4609
4533
  updatePost(postId, { ConversationId: conversationId });
4610
4534
  },
@@ -4621,7 +4545,7 @@ function usePosts(opts) {
4621
4545
  }
4622
4546
 
4623
4547
  // src/client/theme/DeepSpaceThemeProvider.tsx
4624
- import { useEffect as useEffect16 } from "react";
4548
+ import { useEffect as useEffect15 } from "react";
4625
4549
 
4626
4550
  // src/client/theme/applyTheme.ts
4627
4551
  function mix(color, pct, base = "transparent") {
@@ -4934,7 +4858,7 @@ function DeepSpaceThemeProvider({
4934
4858
  children,
4935
4859
  theme
4936
4860
  }) {
4937
- useEffect16(() => {
4861
+ useEffect15(() => {
4938
4862
  const root = document.documentElement;
4939
4863
  const config = theme ?? readThemeFromDOM(root);
4940
4864
  applyDeepSpaceTheme(config, root);
@@ -4943,12 +4867,12 @@ function DeepSpaceThemeProvider({
4943
4867
  }
4944
4868
 
4945
4869
  // src/client/theme/useIsDarkTheme.ts
4946
- import { useState as useState14, useEffect as useEffect17 } from "react";
4870
+ import { useState as useState13, useEffect as useEffect16 } from "react";
4947
4871
  function useIsDarkTheme() {
4948
- const [isDark, setIsDark] = useState14(
4872
+ const [isDark, setIsDark] = useState13(
4949
4873
  () => typeof document === "undefined" || document.documentElement.dataset.uiTheme !== "light"
4950
4874
  );
4951
- useEffect17(() => {
4875
+ useEffect16(() => {
4952
4876
  const update = () => {
4953
4877
  setIsDark(document.documentElement.dataset.uiTheme !== "light");
4954
4878
  };
@@ -4960,7 +4884,7 @@ function useIsDarkTheme() {
4960
4884
  }
4961
4885
 
4962
4886
  // src/client/platform/PlatformProvider.tsx
4963
- import { createContext as createContext3, useContext as useContext5, useState as useState15, useEffect as useEffect18, useRef as useRef15, useCallback as useCallback25 } from "react";
4887
+ import { createContext as createContext3, useContext as useContext5, useState as useState14, useEffect as useEffect17, useRef as useRef14, useCallback as useCallback24 } from "react";
4964
4888
  import { jsx as jsx9 } from "react/jsx-runtime";
4965
4889
  var PlatformContext = createContext3(null);
4966
4890
  function usePlatform() {
@@ -4970,14 +4894,14 @@ function usePlatform() {
4970
4894
  }
4971
4895
  function useInbox() {
4972
4896
  const { inbox, subscribeInbox } = usePlatform();
4973
- useEffect18(() => subscribeInbox(), [subscribeInbox]);
4897
+ useEffect17(() => subscribeInbox(), [subscribeInbox]);
4974
4898
  return inbox;
4975
4899
  }
4976
4900
  function PlatformProvider({ children }) {
4977
- const [inbox, setInbox] = useState15([]);
4978
- const inboxSubscribers = useRef15(0);
4979
- const [inboxActive, setInboxActive] = useState15(false);
4980
- const subscribeInbox = useCallback25(() => {
4901
+ const [inbox, setInbox] = useState14([]);
4902
+ const inboxSubscribers = useRef14(0);
4903
+ const [inboxActive, setInboxActive] = useState14(false);
4904
+ const subscribeInbox = useCallback24(() => {
4981
4905
  inboxSubscribers.current++;
4982
4906
  if (inboxSubscribers.current === 1) setInboxActive(true);
4983
4907
  return () => {
@@ -4985,7 +4909,7 @@ function PlatformProvider({ children }) {
4985
4909
  if (inboxSubscribers.current === 0) setInboxActive(false);
4986
4910
  };
4987
4911
  }, []);
4988
- useEffect18(() => {
4912
+ useEffect17(() => {
4989
4913
  if (!inboxActive) return;
4990
4914
  let mounted = true;
4991
4915
  let ws = null;
@@ -5046,7 +4970,7 @@ function PlatformProvider({ children }) {
5046
4970
  setInbox([]);
5047
4971
  };
5048
4972
  }, [inboxActive]);
5049
- const platformFetch = useCallback25(async (path, init) => {
4973
+ const platformFetch = useCallback24(async (path, init) => {
5050
4974
  const token = await getAuthToken();
5051
4975
  const headers = new Headers(init?.headers);
5052
4976
  if (token) headers.set("Authorization", `Bearer ${token}`);
@@ -5057,28 +4981,28 @@ function PlatformProvider({ children }) {
5057
4981
  }
5058
4982
 
5059
4983
  // src/client/platform/usePlatformWS.ts
5060
- import { useState as useState16, useEffect as useEffect19, useRef as useRef16 } from "react";
4984
+ import { useState as useState15, useEffect as useEffect18, useRef as useRef15 } from "react";
5061
4985
  var MAX_RECONNECT_DELAY = 1e4;
5062
4986
  var BASE_RECONNECT_DELAY = 500;
5063
4987
  function usePlatformWS(options) {
5064
4988
  const { path, scopeId, initialState, onMessage } = options;
5065
- const wsRef = useRef16(null);
5066
- const pingIntervalRef = useRef16(null);
5067
- const [state, setState] = useState16(initialState);
5068
- const reconnectAttemptRef = useRef16(0);
5069
- const reconnectTimerRef = useRef16(null);
5070
- const mountedRef = useRef16(true);
5071
- const onMessageRef = useRef16(onMessage);
4989
+ const wsRef = useRef15(null);
4990
+ const pingIntervalRef = useRef15(null);
4991
+ const [state, setState] = useState15(initialState);
4992
+ const reconnectAttemptRef = useRef15(0);
4993
+ const reconnectTimerRef = useRef15(null);
4994
+ const mountedRef = useRef15(true);
4995
+ const onMessageRef = useRef15(onMessage);
5072
4996
  onMessageRef.current = onMessage;
5073
- const initialStateRef = useRef16(initialState);
4997
+ const initialStateRef = useRef15(initialState);
5074
4998
  initialStateRef.current = initialState;
5075
- useEffect19(() => {
4999
+ useEffect18(() => {
5076
5000
  mountedRef.current = true;
5077
5001
  return () => {
5078
5002
  mountedRef.current = false;
5079
5003
  };
5080
5004
  }, []);
5081
- useEffect19(() => {
5005
+ useEffect18(() => {
5082
5006
  if (!scopeId) return;
5083
5007
  setState(initialStateRef.current);
5084
5008
  reconnectAttemptRef.current = 0;
@@ -5137,7 +5061,7 @@ function usePlatformWS(options) {
5137
5061
  wsRef.current = null;
5138
5062
  };
5139
5063
  }, [scopeId, path]);
5140
- const send = useRef16((msg) => {
5064
+ const send = useRef15((msg) => {
5141
5065
  wsRef.current?.send(JSON.stringify(msg));
5142
5066
  }).current;
5143
5067
  return { state, send };
@@ -5248,27 +5172,27 @@ var integration = {
5248
5172
  };
5249
5173
 
5250
5174
  // src/client/voice/useVoiceAgent.ts
5251
- import { useCallback as useCallback26, useEffect as useEffect20, useRef as useRef17, useState as useState17 } from "react";
5175
+ import { useCallback as useCallback25, useEffect as useEffect19, useRef as useRef16, useState as useState16 } from "react";
5252
5176
  var OPENAI_REALTIME_BASE = "https://api.openai.com/v1";
5253
5177
  var ZERO_USAGE = { inputAudioTokens: 0, outputAudioTokens: 0, inputTextTokens: 0, outputTextTokens: 0 };
5254
5178
  function useVoiceAgent(options = {}) {
5255
- const [status, setStatus] = useState17("idle");
5256
- const [isMuted, setIsMuted] = useState17(false);
5257
- const [isAgentSpeaking, setIsAgentSpeaking] = useState17(false);
5258
- const [transcript, setTranscript] = useState17([]);
5259
- const [error, setError] = useState17(null);
5260
- const pcRef = useRef17(null);
5261
- const dcRef = useRef17(null);
5262
- const streamRef = useRef17(null);
5263
- const audioRef = useRef17(null);
5264
- const timerRef = useRef17(null);
5265
- const sessionIdRef = useRef17(null);
5266
- const liveStartRef = useRef17(null);
5267
- const settledRef = useRef17(false);
5268
- const usageRef = useRef17({ ...ZERO_USAGE });
5269
- const optsRef = useRef17(options);
5179
+ const [status, setStatus] = useState16("idle");
5180
+ const [isMuted, setIsMuted] = useState16(false);
5181
+ const [isAgentSpeaking, setIsAgentSpeaking] = useState16(false);
5182
+ const [transcript, setTranscript] = useState16([]);
5183
+ const [error, setError] = useState16(null);
5184
+ const pcRef = useRef16(null);
5185
+ const dcRef = useRef16(null);
5186
+ const streamRef = useRef16(null);
5187
+ const audioRef = useRef16(null);
5188
+ const timerRef = useRef16(null);
5189
+ const sessionIdRef = useRef16(null);
5190
+ const liveStartRef = useRef16(null);
5191
+ const settledRef = useRef16(false);
5192
+ const usageRef = useRef16({ ...ZERO_USAGE });
5193
+ const optsRef = useRef16(options);
5270
5194
  optsRef.current = options;
5271
- const settle = useCallback26(async () => {
5195
+ const settle = useCallback25(async () => {
5272
5196
  const sessionId = sessionIdRef.current;
5273
5197
  if (!sessionId || settledRef.current) return;
5274
5198
  settledRef.current = true;
@@ -5282,7 +5206,7 @@ function useVoiceAgent(options = {}) {
5282
5206
  } catch {
5283
5207
  }
5284
5208
  }, []);
5285
- const cleanup = useCallback26(() => {
5209
+ const cleanup = useCallback25(() => {
5286
5210
  if (timerRef.current) {
5287
5211
  clearTimeout(timerRef.current);
5288
5212
  timerRef.current = null;
@@ -5306,13 +5230,13 @@ function useVoiceAgent(options = {}) {
5306
5230
  streamRef.current = null;
5307
5231
  audioRef.current = null;
5308
5232
  }, []);
5309
- const stop = useCallback26(async () => {
5233
+ const stop = useCallback25(async () => {
5310
5234
  cleanup();
5311
5235
  await settle();
5312
5236
  setIsAgentSpeaking(false);
5313
5237
  setStatus((s) => s === "error" ? s : "ended");
5314
5238
  }, [cleanup, settle]);
5315
- const appendTranscript = useCallback26((role, delta) => {
5239
+ const appendTranscript = useCallback25((role, delta) => {
5316
5240
  if (!delta) return;
5317
5241
  setTranscript((prev) => {
5318
5242
  const last = prev[prev.length - 1];
@@ -5320,7 +5244,7 @@ function useVoiceAgent(options = {}) {
5320
5244
  return [...prev, { role, content: delta }];
5321
5245
  });
5322
5246
  }, []);
5323
- const handleToolCall = useCallback26(async (item) => {
5247
+ const handleToolCall = useCallback25(async (item) => {
5324
5248
  const onToolCall = optsRef.current.onToolCall;
5325
5249
  let output = null;
5326
5250
  if (onToolCall && item.name) {
@@ -5346,7 +5270,7 @@ function useVoiceAgent(options = {}) {
5346
5270
  );
5347
5271
  dc.send(JSON.stringify({ type: "response.create" }));
5348
5272
  }, []);
5349
- const handleEvent = useCallback26(
5273
+ const handleEvent = useCallback25(
5350
5274
  (raw) => {
5351
5275
  if (typeof raw !== "string") return;
5352
5276
  let ev;
@@ -5386,7 +5310,7 @@ function useVoiceAgent(options = {}) {
5386
5310
  },
5387
5311
  [appendTranscript, handleToolCall]
5388
5312
  );
5389
- const start = useCallback26(
5313
+ const start = useCallback25(
5390
5314
  async (overrides = {}) => {
5391
5315
  if (sessionIdRef.current) {
5392
5316
  cleanup();
@@ -5448,7 +5372,7 @@ function useVoiceAgent(options = {}) {
5448
5372
  },
5449
5373
  [cleanup, settle, stop, handleEvent]
5450
5374
  );
5451
- const toggleMute = useCallback26(() => {
5375
+ const toggleMute = useCallback25(() => {
5452
5376
  const stream = streamRef.current;
5453
5377
  if (!stream) return;
5454
5378
  setIsMuted((muted) => {
@@ -5457,7 +5381,7 @@ function useVoiceAgent(options = {}) {
5457
5381
  return next;
5458
5382
  });
5459
5383
  }, []);
5460
- useEffect20(() => {
5384
+ useEffect19(() => {
5461
5385
  return () => {
5462
5386
  cleanup();
5463
5387
  void settle();
@@ -5545,7 +5469,7 @@ function decodeAiStreamChunk(chunk) {
5545
5469
  }
5546
5470
 
5547
5471
  // src/client/status.ts
5548
- import { useCallback as useCallback27, useEffect as useEffect21, useMemo as useMemo16, useRef as useRef18, useState as useState18 } from "react";
5472
+ import { useCallback as useCallback26, useEffect as useEffect20, useMemo as useMemo16, useRef as useRef17, useState as useState17 } from "react";
5549
5473
 
5550
5474
  // src/client/status-core.ts
5551
5475
  var ASYNC_RESOURCE_DEFAULTS = {
@@ -5629,7 +5553,7 @@ function useAsyncResource(fetcher, deps, options = {}) {
5629
5553
  const retry = options.retry ?? ASYNC_RESOURCE_DEFAULTS.retry;
5630
5554
  const retryDelayMs = options.retryDelayMs ?? ASYNC_RESOURCE_DEFAULTS.retryDelayMs;
5631
5555
  const slowAfterMs = options.slowAfterMs ?? ASYNC_RESOURCE_DEFAULTS.slowAfterMs;
5632
- const [state, setState] = useState18(() => ({
5556
+ const [state, setState] = useState17(() => ({
5633
5557
  status: enabled ? "loading" : "idle",
5634
5558
  data: options.initialData ?? null,
5635
5559
  error: null,
@@ -5637,12 +5561,12 @@ function useAsyncResource(fetcher, deps, options = {}) {
5637
5561
  isSlow: false,
5638
5562
  retryCount: 0
5639
5563
  }));
5640
- const requestIdRef = useRef18(0);
5641
- const [reloadTick, setReloadTick] = useState18(0);
5642
- const reload = useCallback27(() => {
5564
+ const requestIdRef = useRef17(0);
5565
+ const [reloadTick, setReloadTick] = useState17(0);
5566
+ const reload = useCallback26(() => {
5643
5567
  setReloadTick((tick) => tick + 1);
5644
5568
  }, []);
5645
- useEffect21(() => {
5569
+ useEffect20(() => {
5646
5570
  if (!enabled) {
5647
5571
  setState((prev) => ({ ...prev, status: "idle", isRefreshing: false, isSlow: false, retryCount: 0 }));
5648
5572
  return;
@@ -5731,7 +5655,7 @@ function usePagedResource(fetchPage, deps, options = {}) {
5731
5655
  const retryDelayMs = options.retryDelayMs ?? ASYNC_RESOURCE_DEFAULTS.retryDelayMs;
5732
5656
  const maxRetryDelayMs = options.maxRetryDelayMs ?? 3e4;
5733
5657
  const hasInitialItems = initialItems.length > 0;
5734
- const [state, setState] = useState18(() => ({
5658
+ const [state, setState] = useState17(() => ({
5735
5659
  status: enabled ? hasInitialItems ? "ready" : "loading" : "idle",
5736
5660
  items: initialItems,
5737
5661
  error: null,
@@ -5741,25 +5665,25 @@ function usePagedResource(fetchPage, deps, options = {}) {
5741
5665
  isLoadingMore: false,
5742
5666
  isRefreshing: false
5743
5667
  }));
5744
- const requestIdRef = useRef18(0);
5745
- const controllerRef = useRef18(null);
5746
- const loadingRef = useRef18(false);
5747
- const nextPageRef = useRef18(hasInitialItems ? 2 : 1);
5748
- const failedPageRef = useRef18(null);
5749
- const failureCountRef = useRef18(0);
5750
- const cooldownUntilRef = useRef18(0);
5751
- const autoRetryTimerRef = useRef18(null);
5752
- const clearAutoRetryTimer = useCallback27(() => {
5668
+ const requestIdRef = useRef17(0);
5669
+ const controllerRef = useRef17(null);
5670
+ const loadingRef = useRef17(false);
5671
+ const nextPageRef = useRef17(hasInitialItems ? 2 : 1);
5672
+ const failedPageRef = useRef17(null);
5673
+ const failureCountRef = useRef17(0);
5674
+ const cooldownUntilRef = useRef17(0);
5675
+ const autoRetryTimerRef = useRef17(null);
5676
+ const clearAutoRetryTimer = useCallback26(() => {
5753
5677
  if (autoRetryTimerRef.current) {
5754
5678
  clearTimeout(autoRetryTimerRef.current);
5755
5679
  autoRetryTimerRef.current = null;
5756
5680
  }
5757
5681
  }, []);
5758
- const resetCooldown = useCallback27(() => {
5682
+ const resetCooldown = useCallback26(() => {
5759
5683
  failureCountRef.current = 0;
5760
5684
  cooldownUntilRef.current = 0;
5761
5685
  }, []);
5762
- const setFailureCooldown = useCallback27(() => {
5686
+ const setFailureCooldown = useCallback26(() => {
5763
5687
  failureCountRef.current += 1;
5764
5688
  const delay = getPagedResourceCooldownMs({
5765
5689
  failureCount: failureCountRef.current,
@@ -5769,7 +5693,7 @@ function usePagedResource(fetchPage, deps, options = {}) {
5769
5693
  cooldownUntilRef.current = Date.now() + delay;
5770
5694
  return delay;
5771
5695
  }, [maxRetryDelayMs, retryDelayMs]);
5772
- const startLoad = useCallback27(async (page, mode) => {
5696
+ const startLoad = useCallback26(async (page, mode) => {
5773
5697
  if (!enabled || loadingRef.current) return;
5774
5698
  if (mode === "auto" && !canAutoLoadPagedResource({
5775
5699
  hasMore: true,
@@ -5849,21 +5773,21 @@ function usePagedResource(fetchPage, deps, options = {}) {
5849
5773
  }
5850
5774
  }
5851
5775
  }, [autoRetryOnError, clearAutoRetryTimer, enabled, fetchPage, keepPreviousData, maxItemsPerPage, pageSize, resetCooldown, setFailureCooldown]);
5852
- const loadMore = useCallback27(() => {
5776
+ const loadMore = useCallback26(() => {
5853
5777
  if (!state.hasMore || state.isLoadingInitial || state.isLoadingMore || state.isRefreshing) return;
5854
5778
  void startLoad(nextPageRef.current, "auto");
5855
5779
  }, [startLoad, state.hasMore, state.isLoadingInitial, state.isLoadingMore, state.isRefreshing]);
5856
- const retry = useCallback27(() => {
5780
+ const retry = useCallback26(() => {
5857
5781
  const page = failedPageRef.current ?? nextPageRef.current;
5858
5782
  void startLoad(page, "manual");
5859
5783
  }, [startLoad]);
5860
- const refresh = useCallback27(() => {
5784
+ const refresh = useCallback26(() => {
5861
5785
  failedPageRef.current = null;
5862
5786
  resetCooldown();
5863
5787
  nextPageRef.current = 1;
5864
5788
  void startLoad(1, "refresh");
5865
5789
  }, [resetCooldown, startLoad]);
5866
- useEffect21(() => {
5790
+ useEffect20(() => {
5867
5791
  requestIdRef.current += 1;
5868
5792
  controllerRef.current?.abort();
5869
5793
  clearAutoRetryTimer();
@@ -5900,7 +5824,7 @@ function usePagedResource(fetchPage, deps, options = {}) {
5900
5824
  }
5901
5825
 
5902
5826
  // src/client/subscriptions/useSubscription.ts
5903
- import { useCallback as useCallback28, useEffect as useEffect22, useState as useState19 } from "react";
5827
+ import { useCallback as useCallback27, useEffect as useEffect21, useState as useState18 } from "react";
5904
5828
 
5905
5829
  // src/shared/subscription.ts
5906
5830
  var ENTITLED_STATUSES = /* @__PURE__ */ new Set([
@@ -5931,7 +5855,7 @@ async function authedJson(path, init) {
5931
5855
  return data;
5932
5856
  }
5933
5857
  function useSubscription() {
5934
- const [state, setState] = useState19({
5858
+ const [state, setState] = useState18({
5935
5859
  tier: "free",
5936
5860
  status: "none",
5937
5861
  interval: null,
@@ -5940,9 +5864,9 @@ function useSubscription() {
5940
5864
  trialEndsAt: null,
5941
5865
  plans: []
5942
5866
  });
5943
- const [isLoading, setLoading] = useState19(true);
5944
- const [error, setError] = useState19(null);
5945
- const refresh = useCallback28(async () => {
5867
+ const [isLoading, setLoading] = useState18(true);
5868
+ const [error, setError] = useState18(null);
5869
+ const refresh = useCallback27(async () => {
5946
5870
  setLoading(true);
5947
5871
  try {
5948
5872
  const r = await authedJson("/_deepspace/subscriptions/me");
@@ -5962,15 +5886,15 @@ function useSubscription() {
5962
5886
  setLoading(false);
5963
5887
  }
5964
5888
  }, []);
5965
- useEffect22(() => {
5889
+ useEffect21(() => {
5966
5890
  refresh();
5967
5891
  }, [refresh]);
5968
5892
  const entitled = isEntitled(state.tier, state.status);
5969
- const hasTier = useCallback28(
5893
+ const hasTier = useCallback27(
5970
5894
  (slug) => state.tier === slug && isEntitled(state.tier, state.status),
5971
5895
  [state.tier, state.status]
5972
5896
  );
5973
- const isAtLeast = useCallback28(
5897
+ const isAtLeast = useCallback27(
5974
5898
  (slug) => {
5975
5899
  if (!isEntitled(state.tier, state.status)) return false;
5976
5900
  const targetRank = state.plans.find((p) => p.slug === slug)?.rank;
@@ -5980,7 +5904,7 @@ function useSubscription() {
5980
5904
  },
5981
5905
  [state.plans, state.tier, state.status]
5982
5906
  );
5983
- const subscribe = useCallback28(
5907
+ const subscribe = useCallback27(
5984
5908
  async (planSlug, opts) => {
5985
5909
  const result = await authedJson("/_deepspace/subscriptions/checkout", {
5986
5910
  method: "POST",
@@ -6000,7 +5924,7 @@ function useSubscription() {
6000
5924
  },
6001
5925
  [refresh]
6002
5926
  );
6003
- const openPortal = useCallback28(
5927
+ const openPortal = useCallback27(
6004
5928
  async (returnUrl) => {
6005
5929
  return authedJson("/_deepspace/subscriptions/portal", {
6006
5930
  method: "POST",
@@ -6069,7 +5993,7 @@ function PricingTable(props) {
6069
5993
  }
6070
5994
 
6071
5995
  // src/client/charges/useCheckout.ts
6072
- import { useCallback as useCallback29, useEffect as useEffect23, useMemo as useMemo17, useState as useState20 } from "react";
5996
+ import { useCallback as useCallback28, useEffect as useEffect22, useMemo as useMemo17, useState as useState19 } from "react";
6073
5997
  async function authedJson2(path, init) {
6074
5998
  const token = await getAuthToken();
6075
5999
  const res = await fetch(path, {
@@ -6088,10 +6012,10 @@ async function authedJson2(path, init) {
6088
6012
  return data;
6089
6013
  }
6090
6014
  function useCheckout(opts) {
6091
- const [purchases, setPurchases] = useState20([]);
6092
- const [isLoading, setLoading] = useState20(true);
6093
- const [error, setError] = useState20(null);
6094
- const refresh = useCallback29(async () => {
6015
+ const [purchases, setPurchases] = useState19([]);
6016
+ const [isLoading, setLoading] = useState19(true);
6017
+ const [error, setError] = useState19(null);
6018
+ const refresh = useCallback28(async () => {
6095
6019
  setLoading(true);
6096
6020
  try {
6097
6021
  const r = await authedJson2(
@@ -6105,10 +6029,10 @@ function useCheckout(opts) {
6105
6029
  setLoading(false);
6106
6030
  }
6107
6031
  }, []);
6108
- useEffect23(() => {
6032
+ useEffect22(() => {
6109
6033
  refresh();
6110
6034
  }, [refresh]);
6111
- const chargeOnce = useCallback29(
6035
+ const chargeOnce = useCallback28(
6112
6036
  async (chargeOpts) => {
6113
6037
  setError(null);
6114
6038
  const common = {
@@ -6142,7 +6066,7 @@ function useCheckout(opts) {
6142
6066
  },
6143
6067
  []
6144
6068
  );
6145
- const ownsProduct = useCallback29(
6069
+ const ownsProduct = useCallback28(
6146
6070
  (productId) => purchases.some((p) => p.productId === productId),
6147
6071
  [purchases]
6148
6072
  );
@@ -6311,7 +6235,6 @@ export {
6311
6235
  AuthGate,
6312
6236
  AuthOverlay,
6313
6237
  Awareness,
6314
- CANONICAL_APP_IDENTITY_MIGRATION_ID,
6315
6238
  DEEPSPACE_AGENT_PROFILES,
6316
6239
  DEEPSPACE_AI_DEFAULTS,
6317
6240
  DEEPSPACE_AI_MODELS,
@@ -6422,7 +6345,6 @@ export {
6422
6345
  useConversations,
6423
6346
  useCronMonitor,
6424
6347
  useDisplayName,
6425
- useGameRoom,
6426
6348
  useInbox,
6427
6349
  useIsDarkTheme,
6428
6350
  useJobs,