deepspace 0.3.11 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +258 -120
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +68 -26
- package/dist/index.js +108 -48
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +72 -73
- package/dist/server.js +371 -338
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +72 -73
- package/dist/worker.js +425 -392
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
- package/dist/testing.d.ts +0 -115
package/dist/index.d.ts
CHANGED
|
@@ -396,6 +396,7 @@ declare const MSG: {
|
|
|
396
396
|
readonly USER_LIST: "user.list";
|
|
397
397
|
readonly SET_ROLE: "user.set_role";
|
|
398
398
|
readonly USER_UPDATE: "user.update";
|
|
399
|
+
readonly AUTH: "auth";
|
|
399
400
|
readonly YJS_JOIN: "yjs.join";
|
|
400
401
|
readonly YJS_LEAVE: "yjs.leave";
|
|
401
402
|
readonly ACK: "records.ack";
|
|
@@ -639,6 +640,8 @@ type ServerMessage = BaseMessage<typeof MSG.QUERY_RESULT, {
|
|
|
639
640
|
error: string;
|
|
640
641
|
}> | BaseMessage<typeof MSG.RESUBSCRIBE, EmptyPayload> | BaseMessage<typeof MSG.LIST_SCHEMAS, {
|
|
641
642
|
schemas: unknown;
|
|
643
|
+
}> | BaseMessage<typeof MSG.AUTH, {
|
|
644
|
+
canWrite: boolean;
|
|
642
645
|
}> | BaseMessage<typeof MSG.USER_INFO, unknown> | BaseMessage<typeof MSG.USER_LIST, {
|
|
643
646
|
users: unknown[];
|
|
644
647
|
}> | BaseMessage<typeof MSG.YJS_JOIN, {
|
|
@@ -5972,13 +5975,20 @@ interface UseGameRoomResult {
|
|
|
5972
5975
|
running: boolean;
|
|
5973
5976
|
/** Whether WebSocket is connected */
|
|
5974
5977
|
connected: boolean;
|
|
5975
|
-
/**
|
|
5978
|
+
/**
|
|
5979
|
+
* Whether this connection has write access. False for viewers /
|
|
5980
|
+
* unauthenticated spectators — they receive state broadcasts but the
|
|
5981
|
+
* send callbacks below no-op. UIs should disable input controls when
|
|
5982
|
+
* false so spectators see why their actions don't take effect.
|
|
5983
|
+
*/
|
|
5984
|
+
canWrite: boolean;
|
|
5985
|
+
/** Send a game input (no-op for spectators) */
|
|
5976
5986
|
sendInput: (action: string, data?: Record<string, unknown>) => void;
|
|
5977
|
-
/** Mark self as ready */
|
|
5987
|
+
/** Mark self as ready (no-op for spectators) */
|
|
5978
5988
|
setReady: () => void;
|
|
5979
|
-
/** Request game start */
|
|
5989
|
+
/** Request game start (no-op for spectators) */
|
|
5980
5990
|
startGame: () => void;
|
|
5981
|
-
/** Request game end */
|
|
5991
|
+
/** Request game end (no-op for spectators) */
|
|
5982
5992
|
endGame: () => void;
|
|
5983
5993
|
}
|
|
5984
5994
|
declare function useGameRoom(roomId: string): UseGameRoomResult;
|
|
@@ -6019,21 +6029,28 @@ interface UseCanvasResult {
|
|
|
6019
6029
|
viewports: ViewportClient[];
|
|
6020
6030
|
/** Whether WebSocket is connected */
|
|
6021
6031
|
connected: boolean;
|
|
6022
|
-
/**
|
|
6032
|
+
/**
|
|
6033
|
+
* Whether this connection has write access (member/admin role). The
|
|
6034
|
+
* write callbacks below no-op when this is false; UIs should also use
|
|
6035
|
+
* it to disable controls so viewers see why their clicks do nothing.
|
|
6036
|
+
* Viewport reporting stays available regardless — it's presence info.
|
|
6037
|
+
*/
|
|
6038
|
+
canWrite: boolean;
|
|
6039
|
+
/** Add a shape (no-op when canWrite is false) */
|
|
6023
6040
|
addShape: (shape: Partial<CanvasShapeClient>) => void;
|
|
6024
|
-
/** Move a shape */
|
|
6041
|
+
/** Move a shape (no-op when canWrite is false) */
|
|
6025
6042
|
moveShape: (shapeId: string, x: number, y: number) => void;
|
|
6026
|
-
/** Resize a shape */
|
|
6043
|
+
/** Resize a shape (no-op when canWrite is false) */
|
|
6027
6044
|
resizeShape: (shapeId: string, width: number, height: number, x?: number, y?: number) => void;
|
|
6028
|
-
/** Delete a shape */
|
|
6045
|
+
/** Delete a shape (no-op when canWrite is false) */
|
|
6029
6046
|
deleteShape: (shapeId: string) => void;
|
|
6030
|
-
/** Update shape properties */
|
|
6047
|
+
/** Update shape properties (no-op when canWrite is false) */
|
|
6031
6048
|
updateShape: (shapeId: string, props: Record<string, unknown>) => void;
|
|
6032
|
-
/** Report local viewport */
|
|
6049
|
+
/** Report local viewport (allowed for viewers — presence-like) */
|
|
6033
6050
|
setViewport: (viewport: Omit<ViewportClient, 'userId'>) => void;
|
|
6034
|
-
/** Undo last action */
|
|
6051
|
+
/** Undo last action (no-op when canWrite is false) */
|
|
6035
6052
|
undo: () => void;
|
|
6036
|
-
/** Redo last undone action */
|
|
6053
|
+
/** Redo last undone action (no-op when canWrite is false) */
|
|
6037
6054
|
redo: () => void;
|
|
6038
6055
|
}
|
|
6039
6056
|
declare function useCanvas(roomId: string): UseCanvasResult;
|
|
@@ -6070,11 +6087,18 @@ interface UseCronMonitorResult {
|
|
|
6070
6087
|
history: CronHistoryEntry[];
|
|
6071
6088
|
/** Whether WebSocket is connected */
|
|
6072
6089
|
connected: boolean;
|
|
6073
|
-
/**
|
|
6090
|
+
/**
|
|
6091
|
+
* Whether this connection can mutate cron state (trigger / pause /
|
|
6092
|
+
* resume). False for viewers and unauthenticated connections — the
|
|
6093
|
+
* mutation callbacks below no-op and UIs should disable those
|
|
6094
|
+
* controls. Reading tasks/history stays available either way.
|
|
6095
|
+
*/
|
|
6096
|
+
canWrite: boolean;
|
|
6097
|
+
/** Manually trigger a task (no-op when canWrite is false) */
|
|
6074
6098
|
trigger: (taskName: string) => void;
|
|
6075
|
-
/** Pause a task */
|
|
6099
|
+
/** Pause a task (no-op when canWrite is false) */
|
|
6076
6100
|
pause: (taskName: string) => void;
|
|
6077
|
-
/** Resume a paused task */
|
|
6101
|
+
/** Resume a paused task (no-op when canWrite is false) */
|
|
6078
6102
|
resume: (taskName: string) => void;
|
|
6079
6103
|
}
|
|
6080
6104
|
declare function useCronMonitor(roomId: string): UseCronMonitorResult;
|
|
@@ -6206,17 +6230,23 @@ declare function toConnectionStatus(queryStatus: string): ConnectionStatus;
|
|
|
6206
6230
|
/**
|
|
6207
6231
|
* useR2Files — Scoped file storage hook for DeepSpace apps.
|
|
6208
6232
|
*
|
|
6209
|
-
* Provides upload, download, list, and delete for files in R2
|
|
6210
|
-
*
|
|
6233
|
+
* Provides upload, download, list, and delete for files in R2.
|
|
6234
|
+
*
|
|
6235
|
+
* Two scopes are supported via the `scope` option:
|
|
6236
|
+
* - `'self'` (default): per-user storage. Reads require auth.
|
|
6237
|
+
* - `'app'`: app-wide storage. Reads are public — the upload URL works
|
|
6238
|
+
* directly as an `<img src>`. Use for avatars, logos, etc.
|
|
6211
6239
|
*
|
|
6212
6240
|
* @example
|
|
6213
6241
|
* ```tsx
|
|
6214
|
-
*
|
|
6242
|
+
* // Per-user files (default):
|
|
6243
|
+
* const { upload, downloadFile } = useR2Files()
|
|
6215
6244
|
* await upload(myFile, 'photo.png')
|
|
6216
6245
|
*
|
|
6217
|
-
*
|
|
6218
|
-
*
|
|
6219
|
-
* await
|
|
6246
|
+
* // App-shared files (public reads — usable in <img src>):
|
|
6247
|
+
* const { upload } = useR2Files({ scope: 'app' })
|
|
6248
|
+
* const r = await upload(file, `avatars/${userId}.png`)
|
|
6249
|
+
* // r.url is a plain, anon-readable URL.
|
|
6220
6250
|
* ```
|
|
6221
6251
|
*/
|
|
6222
6252
|
interface R2UploadResult {
|
|
@@ -6290,17 +6320,29 @@ interface UseR2FilesReturn {
|
|
|
6290
6320
|
/**
|
|
6291
6321
|
* Build a plain URL for a file. Accepts an R2FileInfo or a raw key string.
|
|
6292
6322
|
*
|
|
6293
|
-
* **Note**: This URL has no auth token attached. It
|
|
6294
|
-
*
|
|
6295
|
-
*
|
|
6296
|
-
* `readFile` instead.
|
|
6323
|
+
* **Note**: This URL has no auth token attached. It works for `'app'`-scope
|
|
6324
|
+
* files (which are publicly readable) but not for `'self'`-scope files —
|
|
6325
|
+
* those require an Authorization header, so use `readFile` / `downloadFile`.
|
|
6297
6326
|
*/
|
|
6298
6327
|
getUrl: (fileOrKey: R2FileInfo | string) => string;
|
|
6299
6328
|
/** Whether an upload is currently in progress. */
|
|
6300
6329
|
isUploading: boolean;
|
|
6301
6330
|
}
|
|
6331
|
+
/**
|
|
6332
|
+
* Scope controls where files live and who can read them:
|
|
6333
|
+
*
|
|
6334
|
+
* - `'self'` (default) — `apps/<app>/users/<userId>/…`. Per-user storage.
|
|
6335
|
+
* Reads require the caller's auth token; not usable from a plain `<img>`
|
|
6336
|
+
* or unauthenticated browser request.
|
|
6337
|
+
* - `'app'` — `apps/<app>/…`. Shared by everyone in the app. Reads are
|
|
6338
|
+
* public (no auth header needed), so the upload URL works directly as
|
|
6339
|
+
* an `<img src>`. Use this for avatars, logos, attachments, and other
|
|
6340
|
+
* assets meant to be embedded in pages.
|
|
6341
|
+
*
|
|
6342
|
+
* Uploads under `'app'` still require a signed-in user.
|
|
6343
|
+
*/
|
|
6302
6344
|
type R2Scope = {
|
|
6303
|
-
scope?: 'self';
|
|
6345
|
+
scope?: 'self' | 'app';
|
|
6304
6346
|
};
|
|
6305
6347
|
declare function useR2Files(options?: R2Scope): UseR2FilesReturn;
|
|
6306
6348
|
|
package/dist/index.js
CHANGED
|
@@ -407,6 +407,13 @@ var MSG = {
|
|
|
407
407
|
USER_LIST: "user.list",
|
|
408
408
|
SET_ROLE: "user.set_role",
|
|
409
409
|
USER_UPDATE: "user.update",
|
|
410
|
+
// ---- Auth signal -----------------------------------------------------
|
|
411
|
+
// Server → client one-shot on connect from rooms that enforce role-based
|
|
412
|
+
// writes (Canvas, Game, Cron). Tells the client whether this connection
|
|
413
|
+
// can write so its hook can disable local write APIs and the UI can
|
|
414
|
+
// render controls accordingly. Without this, a viewer's click would
|
|
415
|
+
// round-trip to the server and silently come back as ERROR.
|
|
416
|
+
AUTH: "auth",
|
|
410
417
|
// ---- Yjs JSON control frames (binary sync lives in ./yjs.ts) ---------
|
|
411
418
|
YJS_JOIN: "yjs.join",
|
|
412
419
|
YJS_LEAVE: "yjs.leave",
|
|
@@ -717,12 +724,17 @@ function DeepSpaceAuthProvider({ children }) {
|
|
|
717
724
|
}
|
|
718
725
|
|
|
719
726
|
// src/client/auth/hooks.ts
|
|
727
|
+
var sessionHasResolved = false;
|
|
728
|
+
function stickyIsLoaded(isPending) {
|
|
729
|
+
if (!isPending) sessionHasResolved = true;
|
|
730
|
+
return sessionHasResolved;
|
|
731
|
+
}
|
|
720
732
|
function useAuth() {
|
|
721
733
|
const session = useSession();
|
|
722
734
|
const isSignedIn = !!session.data?.user;
|
|
723
735
|
const userId = session.data?.user?.id ?? null;
|
|
724
736
|
return {
|
|
725
|
-
isLoaded:
|
|
737
|
+
isLoaded: stickyIsLoaded(session.isPending),
|
|
726
738
|
isSignedIn,
|
|
727
739
|
userId,
|
|
728
740
|
sessionId: session.data?.session?.id ?? null
|
|
@@ -732,7 +744,7 @@ function useAuthUser() {
|
|
|
732
744
|
const session = useSession();
|
|
733
745
|
const user = session.data?.user ?? null;
|
|
734
746
|
return {
|
|
735
|
-
isLoaded:
|
|
747
|
+
isLoaded: stickyIsLoaded(session.isPending),
|
|
736
748
|
isSignedIn: !!user,
|
|
737
749
|
user: user ? {
|
|
738
750
|
id: user.id,
|
|
@@ -880,7 +892,35 @@ function AuthOverlay({
|
|
|
880
892
|
}
|
|
881
893
|
)
|
|
882
894
|
] }),
|
|
883
|
-
/* @__PURE__ */ jsxs2("
|
|
895
|
+
/* @__PURE__ */ jsxs2("p", { className: "mt-5 text-xs leading-snug text-muted-foreground", children: [
|
|
896
|
+
"By continuing, you agree to the DeepSpace",
|
|
897
|
+
" ",
|
|
898
|
+
/* @__PURE__ */ jsx2(
|
|
899
|
+
"a",
|
|
900
|
+
{
|
|
901
|
+
href: "https://deep.space/terms",
|
|
902
|
+
target: "_blank",
|
|
903
|
+
rel: "noopener noreferrer",
|
|
904
|
+
className: "underline hover:text-foreground",
|
|
905
|
+
children: "Terms of Service"
|
|
906
|
+
}
|
|
907
|
+
),
|
|
908
|
+
" ",
|
|
909
|
+
"and",
|
|
910
|
+
" ",
|
|
911
|
+
/* @__PURE__ */ jsx2(
|
|
912
|
+
"a",
|
|
913
|
+
{
|
|
914
|
+
href: "https://deep.space/privacy",
|
|
915
|
+
target: "_blank",
|
|
916
|
+
rel: "noopener noreferrer",
|
|
917
|
+
className: "underline hover:text-foreground",
|
|
918
|
+
children: "Privacy Policy"
|
|
919
|
+
}
|
|
920
|
+
),
|
|
921
|
+
"."
|
|
922
|
+
] }),
|
|
923
|
+
/* @__PURE__ */ jsxs2("div", { className: "mt-4 flex items-center justify-center gap-1.5 text-[11px] text-muted-foreground", children: [
|
|
884
924
|
/* @__PURE__ */ jsx2("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "currentColor", opacity: "0.5", "aria-hidden": true, children: /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" }) }),
|
|
885
925
|
"Powered by DeepSpace"
|
|
886
926
|
] })
|
|
@@ -2848,6 +2888,7 @@ function useGameRoom(roomId) {
|
|
|
2848
2888
|
const [players, setPlayers] = useState8([]);
|
|
2849
2889
|
const [running, setRunning] = useState8(false);
|
|
2850
2890
|
const [connected, setConnected] = useState8(false);
|
|
2891
|
+
const [canWrite, setCanWrite] = useState8(false);
|
|
2851
2892
|
const wsRef = useRef9(null);
|
|
2852
2893
|
useEffect11(() => {
|
|
2853
2894
|
let ws = null;
|
|
@@ -2869,6 +2910,9 @@ function useGameRoom(roomId) {
|
|
|
2869
2910
|
};
|
|
2870
2911
|
ws.onmessage = (event) => {
|
|
2871
2912
|
dispatch(event.data, {
|
|
2913
|
+
[MSG.AUTH]: (p) => {
|
|
2914
|
+
setCanWrite(p.canWrite);
|
|
2915
|
+
},
|
|
2872
2916
|
[MSG.GAME_STATE]: (p) => {
|
|
2873
2917
|
setGameState(p.state);
|
|
2874
2918
|
setTick(p.tick);
|
|
@@ -2906,6 +2950,7 @@ function useGameRoom(roomId) {
|
|
|
2906
2950
|
wsLog("disconnected", `game:${roomId}`);
|
|
2907
2951
|
wsRef.current = null;
|
|
2908
2952
|
setConnected(false);
|
|
2953
|
+
setCanWrite(false);
|
|
2909
2954
|
if (alive) reconnectTimer = setTimeout(connect, 1e3);
|
|
2910
2955
|
};
|
|
2911
2956
|
ws.onerror = () => ws?.close();
|
|
@@ -2924,27 +2969,23 @@ function useGameRoom(roomId) {
|
|
|
2924
2969
|
wsRef.current = null;
|
|
2925
2970
|
};
|
|
2926
2971
|
}, [roomId]);
|
|
2927
|
-
const
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
const endGame = useCallback10(() =>
|
|
2943
|
-
|
|
2944
|
-
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
2945
|
-
ws.send(encode(clientBuild.gameEnd()));
|
|
2946
|
-
}, []);
|
|
2947
|
-
return { state: gameState, tick, players, running, connected, sendInput, setReady, startGame, endGame };
|
|
2972
|
+
const send = useCallback10(
|
|
2973
|
+
(message) => {
|
|
2974
|
+
if (!canWrite) return;
|
|
2975
|
+
const ws = wsRef.current;
|
|
2976
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
2977
|
+
ws.send(encode(message));
|
|
2978
|
+
},
|
|
2979
|
+
[canWrite]
|
|
2980
|
+
);
|
|
2981
|
+
const sendInput = useCallback10(
|
|
2982
|
+
(action, data = {}) => send(clientBuild.gameInput(action, data)),
|
|
2983
|
+
[send]
|
|
2984
|
+
);
|
|
2985
|
+
const setReady = useCallback10(() => send(clientBuild.gamePlayerReady()), [send]);
|
|
2986
|
+
const startGame = useCallback10(() => send(clientBuild.gameStart()), [send]);
|
|
2987
|
+
const endGame = useCallback10(() => send(clientBuild.gameEnd()), [send]);
|
|
2988
|
+
return { state: gameState, tick, players, running, connected, canWrite, sendInput, setReady, startGame, endGame };
|
|
2948
2989
|
}
|
|
2949
2990
|
|
|
2950
2991
|
// src/client/storage/hooks/useCanvas.ts
|
|
@@ -2953,6 +2994,7 @@ function useCanvas(roomId) {
|
|
|
2953
2994
|
const [shapes, setShapes] = useState9([]);
|
|
2954
2995
|
const [viewports, setViewports] = useState9([]);
|
|
2955
2996
|
const [connected, setConnected] = useState9(false);
|
|
2997
|
+
const [canWrite, setCanWrite] = useState9(false);
|
|
2956
2998
|
const wsRef = useRef10(null);
|
|
2957
2999
|
useEffect12(() => {
|
|
2958
3000
|
let ws = null;
|
|
@@ -2974,6 +3016,9 @@ function useCanvas(roomId) {
|
|
|
2974
3016
|
};
|
|
2975
3017
|
ws.onmessage = (event) => {
|
|
2976
3018
|
dispatch(event.data, {
|
|
3019
|
+
[MSG.AUTH]: (p) => {
|
|
3020
|
+
setCanWrite(p.canWrite);
|
|
3021
|
+
},
|
|
2977
3022
|
[MSG.CANVAS_SHAPES]: (p) => {
|
|
2978
3023
|
setShapes(p.shapes);
|
|
2979
3024
|
setViewports(p.viewports);
|
|
@@ -3026,6 +3071,7 @@ function useCanvas(roomId) {
|
|
|
3026
3071
|
wsLog("disconnected", `canvas:${roomId}`);
|
|
3027
3072
|
wsRef.current = null;
|
|
3028
3073
|
setConnected(false);
|
|
3074
|
+
setCanWrite(false);
|
|
3029
3075
|
if (alive) reconnectTimer = setTimeout(connect, 1e3);
|
|
3030
3076
|
};
|
|
3031
3077
|
ws.onerror = () => ws?.close();
|
|
@@ -3052,33 +3098,40 @@ function useCanvas(roomId) {
|
|
|
3052
3098
|
},
|
|
3053
3099
|
[]
|
|
3054
3100
|
);
|
|
3101
|
+
const sendWrite = useCallback11(
|
|
3102
|
+
(message) => {
|
|
3103
|
+
if (!canWrite) return;
|
|
3104
|
+
sendBuilt(message);
|
|
3105
|
+
},
|
|
3106
|
+
[sendBuilt, canWrite]
|
|
3107
|
+
);
|
|
3055
3108
|
const addShape = useCallback11(
|
|
3056
|
-
(shape) =>
|
|
3057
|
-
[
|
|
3109
|
+
(shape) => sendWrite(clientBuild.canvasAdd(shape)),
|
|
3110
|
+
[sendWrite]
|
|
3058
3111
|
);
|
|
3059
3112
|
const moveShape = useCallback11(
|
|
3060
|
-
(shapeId, x, y) =>
|
|
3061
|
-
[
|
|
3113
|
+
(shapeId, x, y) => sendWrite(clientBuild.canvasMove(shapeId, x, y)),
|
|
3114
|
+
[sendWrite]
|
|
3062
3115
|
);
|
|
3063
3116
|
const resizeShape = useCallback11(
|
|
3064
|
-
(shapeId, width, height, x, y) =>
|
|
3065
|
-
[
|
|
3117
|
+
(shapeId, width, height, x, y) => sendWrite(clientBuild.canvasResize(shapeId, width, height, x, y)),
|
|
3118
|
+
[sendWrite]
|
|
3066
3119
|
);
|
|
3067
3120
|
const deleteShape = useCallback11(
|
|
3068
|
-
(shapeId) =>
|
|
3069
|
-
[
|
|
3121
|
+
(shapeId) => sendWrite(clientBuild.canvasDelete(shapeId)),
|
|
3122
|
+
[sendWrite]
|
|
3070
3123
|
);
|
|
3071
3124
|
const updateShape = useCallback11(
|
|
3072
|
-
(shapeId, props) =>
|
|
3073
|
-
[
|
|
3125
|
+
(shapeId, props) => sendWrite(clientBuild.canvasUpdate(shapeId, props)),
|
|
3126
|
+
[sendWrite]
|
|
3074
3127
|
);
|
|
3075
3128
|
const setViewport = useCallback11(
|
|
3076
3129
|
(viewport) => sendBuilt(clientBuild.canvasViewport(viewport)),
|
|
3077
3130
|
[sendBuilt]
|
|
3078
3131
|
);
|
|
3079
|
-
const undo = useCallback11(() =>
|
|
3080
|
-
const redo = useCallback11(() =>
|
|
3081
|
-
return { shapes, viewports, connected, addShape, moveShape, resizeShape, deleteShape, updateShape, setViewport, undo, redo };
|
|
3132
|
+
const undo = useCallback11(() => sendWrite(clientBuild.canvasUndo()), [sendWrite]);
|
|
3133
|
+
const redo = useCallback11(() => sendWrite(clientBuild.canvasRedo()), [sendWrite]);
|
|
3134
|
+
return { shapes, viewports, connected, canWrite, addShape, moveShape, resizeShape, deleteShape, updateShape, setViewport, undo, redo };
|
|
3082
3135
|
}
|
|
3083
3136
|
|
|
3084
3137
|
// src/client/storage/hooks/useCronMonitor.ts
|
|
@@ -3087,6 +3140,7 @@ function useCronMonitor(roomId) {
|
|
|
3087
3140
|
const [tasks, setTasks] = useState10([]);
|
|
3088
3141
|
const [history, setHistory] = useState10([]);
|
|
3089
3142
|
const [connected, setConnected] = useState10(false);
|
|
3143
|
+
const [canWrite, setCanWrite] = useState10(false);
|
|
3090
3144
|
const wsRef = useRef11(null);
|
|
3091
3145
|
useEffect13(() => {
|
|
3092
3146
|
let ws = null;
|
|
@@ -3104,6 +3158,9 @@ function useCronMonitor(roomId) {
|
|
|
3104
3158
|
ws.onopen = () => setConnected(true);
|
|
3105
3159
|
ws.onmessage = (event) => {
|
|
3106
3160
|
dispatch(event.data, {
|
|
3161
|
+
[MSG.AUTH]: (p) => {
|
|
3162
|
+
setCanWrite(p.canWrite);
|
|
3163
|
+
},
|
|
3107
3164
|
[MSG.CRON_TASKS]: (p) => {
|
|
3108
3165
|
setTasks(p.tasks);
|
|
3109
3166
|
},
|
|
@@ -3119,6 +3176,7 @@ function useCronMonitor(roomId) {
|
|
|
3119
3176
|
ws.onclose = () => {
|
|
3120
3177
|
wsRef.current = null;
|
|
3121
3178
|
setConnected(false);
|
|
3179
|
+
setCanWrite(false);
|
|
3122
3180
|
if (alive) reconnectTimer = setTimeout(connect, 1e3);
|
|
3123
3181
|
};
|
|
3124
3182
|
ws.onerror = () => ws?.close();
|
|
@@ -3131,27 +3189,28 @@ function useCronMonitor(roomId) {
|
|
|
3131
3189
|
wsRef.current = null;
|
|
3132
3190
|
};
|
|
3133
3191
|
}, [roomId]);
|
|
3134
|
-
const
|
|
3192
|
+
const sendWrite = useCallback12(
|
|
3135
3193
|
(message) => {
|
|
3194
|
+
if (!canWrite) return;
|
|
3136
3195
|
const ws = wsRef.current;
|
|
3137
3196
|
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
3138
3197
|
ws.send(encode(message));
|
|
3139
3198
|
},
|
|
3140
|
-
[]
|
|
3199
|
+
[canWrite]
|
|
3141
3200
|
);
|
|
3142
3201
|
const trigger = useCallback12(
|
|
3143
|
-
(taskName) =>
|
|
3144
|
-
[
|
|
3202
|
+
(taskName) => sendWrite(clientBuild.cronTrigger(taskName)),
|
|
3203
|
+
[sendWrite]
|
|
3145
3204
|
);
|
|
3146
3205
|
const pause = useCallback12(
|
|
3147
|
-
(taskName) =>
|
|
3148
|
-
[
|
|
3206
|
+
(taskName) => sendWrite(clientBuild.cronPause(taskName)),
|
|
3207
|
+
[sendWrite]
|
|
3149
3208
|
);
|
|
3150
3209
|
const resume = useCallback12(
|
|
3151
|
-
(taskName) =>
|
|
3152
|
-
[
|
|
3210
|
+
(taskName) => sendWrite(clientBuild.cronResume(taskName)),
|
|
3211
|
+
[sendWrite]
|
|
3153
3212
|
);
|
|
3154
|
-
return { tasks, history, connected, trigger, pause, resume };
|
|
3213
|
+
return { tasks, history, connected, canWrite, trigger, pause, resume };
|
|
3155
3214
|
}
|
|
3156
3215
|
|
|
3157
3216
|
// src/client/storage/hooks/useJobs.ts
|
|
@@ -3376,11 +3435,12 @@ function resolveFile(fileOrKey) {
|
|
|
3376
3435
|
}
|
|
3377
3436
|
function useR2Files(options) {
|
|
3378
3437
|
const [isUploading, setIsUploading] = useState13(false);
|
|
3438
|
+
const scope = options?.scope ?? "self";
|
|
3379
3439
|
const scopeParams = useMemo10(() => {
|
|
3380
3440
|
const params = new URLSearchParams();
|
|
3381
|
-
params.set("scope",
|
|
3441
|
+
params.set("scope", scope);
|
|
3382
3442
|
return params.toString();
|
|
3383
|
-
}, []);
|
|
3443
|
+
}, [scope]);
|
|
3384
3444
|
const authHeaders = useCallback15(async () => {
|
|
3385
3445
|
const token = await getAuthToken();
|
|
3386
3446
|
if (token) {
|