@noya-app/noya-multiplayer-react 0.1.45 → 0.1.46
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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +10 -0
- package/dist/index.bundle.js +10 -10
- package/dist/index.d.mts +81 -86
- package/dist/index.d.ts +81 -86
- package/dist/index.js +371 -418
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +268 -312
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/NoyaStateContext.tsx +96 -95
- package/src/WebSocketConnection.ts +153 -0
- package/src/components/ErrorOverlay.tsx +3 -3
- package/src/hooks.ts +74 -191
- package/src/index.ts +2 -0
- package/src/inspector/StateInspector.tsx +83 -45
- package/src/inspector/useStateInspector.tsx +11 -55
- package/src/noyaApp.ts +36 -95
- package/src/globals.ts +0 -4
package/dist/index.mjs
CHANGED
|
@@ -89,18 +89,12 @@ var UserPointersOverlay = memoGeneric(function UserPointers({
|
|
|
89
89
|
|
|
90
90
|
// src/hooks.ts
|
|
91
91
|
import {
|
|
92
|
-
|
|
93
|
-
AssetManager,
|
|
94
|
-
ConnectedUsersManager,
|
|
95
|
-
EphemeralUserDataManager,
|
|
96
|
-
MultiplayerStateManager,
|
|
97
|
-
RPCManager,
|
|
92
|
+
NoyaManager,
|
|
98
93
|
StateManager,
|
|
99
|
-
stubSync
|
|
100
|
-
WorkflowManager
|
|
94
|
+
stubSync
|
|
101
95
|
} from "@noya-app/state-manager";
|
|
102
96
|
import {
|
|
103
|
-
createElement,
|
|
97
|
+
createElement as createElement2,
|
|
104
98
|
useCallback,
|
|
105
99
|
useEffect as useEffect3,
|
|
106
100
|
useMemo as useMemo2,
|
|
@@ -110,25 +104,9 @@ import {
|
|
|
110
104
|
} from "react";
|
|
111
105
|
import { createRoot as createRoot2 } from "react-dom/client";
|
|
112
106
|
|
|
113
|
-
// ../noya-state-manager/src/SecretManager.ts
|
|
114
|
-
import { Observable } from "@noya-app/observable";
|
|
115
|
-
var SecretManager = class {
|
|
116
|
-
constructor(rpcManager) {
|
|
117
|
-
this.rpcManager = rpcManager;
|
|
118
|
-
}
|
|
119
|
-
secrets$ = new Observable([]);
|
|
120
|
-
isInitialized$ = new Observable(false);
|
|
121
|
-
async createSecret(name, value) {
|
|
122
|
-
return await this.rpcManager.request({ type: "createSecret", name, value });
|
|
123
|
-
}
|
|
124
|
-
async deleteSecret(name) {
|
|
125
|
-
return await this.rpcManager.request({ type: "deleteSecret", name });
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
107
|
// src/components/ErrorOverlay.tsx
|
|
130
|
-
import React2 from "react";
|
|
131
|
-
|
|
108
|
+
import * as React2 from "react";
|
|
109
|
+
var ErrorOverlay = React2.memo(function ErrorOverlay2({
|
|
132
110
|
error
|
|
133
111
|
}) {
|
|
134
112
|
return /* @__PURE__ */ React2.createElement(
|
|
@@ -198,12 +176,7 @@ function ErrorOverlay({
|
|
|
198
176
|
"Reload"
|
|
199
177
|
)
|
|
200
178
|
);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// src/globals.ts
|
|
204
|
-
import { Observable as Observable2 } from "@noya-app/observable";
|
|
205
|
-
var shouldTrackEvents$ = new Observable2(false);
|
|
206
|
-
var shouldTrackTasks$ = new Observable2(false);
|
|
179
|
+
});
|
|
207
180
|
|
|
208
181
|
// src/inspector/useStateInspector.tsx
|
|
209
182
|
import React5, { useLayoutEffect as useLayoutEffect2 } from "react";
|
|
@@ -428,21 +401,23 @@ function InspectorRow({
|
|
|
428
401
|
}
|
|
429
402
|
var HISTORY_ELEMENT_PREFIX = "noya-multiplayer-history-";
|
|
430
403
|
var StateInspector = memoGeneric2(function StateInspector2({
|
|
431
|
-
|
|
432
|
-
connectionEvents,
|
|
433
|
-
tasks,
|
|
434
|
-
unstyled,
|
|
404
|
+
noyaManager,
|
|
435
405
|
colorScheme = "light",
|
|
436
|
-
multiplayerStateManager,
|
|
437
|
-
assetManager,
|
|
438
|
-
secretManager,
|
|
439
|
-
connectedUsersManager,
|
|
440
|
-
ephemeralUserDataManager,
|
|
441
406
|
anchor = "right",
|
|
442
|
-
|
|
407
|
+
unstyled = false,
|
|
443
408
|
...props
|
|
444
409
|
}) {
|
|
410
|
+
const {
|
|
411
|
+
multiplayerStateManager,
|
|
412
|
+
assetManager,
|
|
413
|
+
connectedUsersManager,
|
|
414
|
+
secretManager,
|
|
415
|
+
ephemeralUserDataManager,
|
|
416
|
+
connectionEventManager,
|
|
417
|
+
taskManager
|
|
418
|
+
} = noyaManager;
|
|
445
419
|
const [didMount, setDidMount] = React4.useState(false);
|
|
420
|
+
const tasks = useObservable(taskManager.tasks$);
|
|
446
421
|
useLayoutEffect(() => {
|
|
447
422
|
setDidMount(true);
|
|
448
423
|
}, []);
|
|
@@ -484,23 +459,24 @@ var StateInspector = memoGeneric2(function StateInspector2({
|
|
|
484
459
|
"noya-multiplayer-react-show-secrets",
|
|
485
460
|
false
|
|
486
461
|
);
|
|
487
|
-
|
|
488
|
-
shouldTrackEvents$.set(showEvents);
|
|
489
|
-
}, [showEvents]);
|
|
490
|
-
useEffect2(() => {
|
|
491
|
-
shouldTrackTasks$.set(showTasks);
|
|
492
|
-
}, [showTasks]);
|
|
462
|
+
const connectionEvents = useObservable(connectionEventManager.events$);
|
|
493
463
|
useEffect2(() => {
|
|
494
464
|
if (eventsContainerRef.current) {
|
|
495
465
|
eventsContainerRef.current.scrollTop = eventsContainerRef.current.scrollHeight;
|
|
496
466
|
}
|
|
497
467
|
}, [connectionEvents]);
|
|
468
|
+
const multipeerStateInitialized = useObservable(
|
|
469
|
+
multiplayerStateManager.isInitialized$
|
|
470
|
+
);
|
|
498
471
|
const ephemeral = useObservable(ephemeralUserDataManager.data$);
|
|
499
472
|
const historySnapshot = useManagedHistory(multiplayerStateManager.sm);
|
|
500
473
|
const assets = useObservable(assetManager.assets$);
|
|
474
|
+
const assetsInitialized = useObservable(assetManager.isInitialized$);
|
|
501
475
|
const secrets = useObservable(secretManager.secrets$);
|
|
476
|
+
const secretsInitialized = useObservable(secretManager.isInitialized$);
|
|
502
477
|
const connectedUsers = useObservable(connectedUsersManager.connectedUsers$);
|
|
503
478
|
const userId = useObservable(connectedUsersManager.currentUserId$);
|
|
479
|
+
const state = useObservable(multiplayerStateManager.optimisticState$);
|
|
504
480
|
useEffect2(() => {
|
|
505
481
|
if (historyContainerRef.current) {
|
|
506
482
|
historyContainerRef.current.scrollTop = historyContainerRef.current.scrollHeight;
|
|
@@ -640,7 +616,12 @@ var StateInspector = memoGeneric2(function StateInspector2({
|
|
|
640
616
|
/* @__PURE__ */ React4.createElement(
|
|
641
617
|
DisclosureSection,
|
|
642
618
|
{
|
|
643
|
-
title: "Data",
|
|
619
|
+
title: /* @__PURE__ */ React4.createElement(TitleLabel, null, "Data", /* @__PURE__ */ React4.createElement(
|
|
620
|
+
ColoredDot,
|
|
621
|
+
{
|
|
622
|
+
type: multipeerStateInitialized ? "success" : "error"
|
|
623
|
+
}
|
|
624
|
+
)),
|
|
644
625
|
colorScheme,
|
|
645
626
|
setOpen: setShowData,
|
|
646
627
|
open: showData,
|
|
@@ -657,7 +638,7 @@ var StateInspector = memoGeneric2(function StateInspector2({
|
|
|
657
638
|
{
|
|
658
639
|
theme,
|
|
659
640
|
onClick: () => {
|
|
660
|
-
forceInit();
|
|
641
|
+
noyaManager.forceInit();
|
|
661
642
|
}
|
|
662
643
|
},
|
|
663
644
|
"Reset"
|
|
@@ -821,9 +802,22 @@ var StateInspector = memoGeneric2(function StateInspector2({
|
|
|
821
802
|
{
|
|
822
803
|
open: showAssets,
|
|
823
804
|
setOpen: setShowAssets,
|
|
824
|
-
title: "Assets",
|
|
805
|
+
title: /* @__PURE__ */ React4.createElement(TitleLabel, null, "Assets", /* @__PURE__ */ React4.createElement(ColoredDot, { type: assetsInitialized ? "success" : "error" })),
|
|
825
806
|
colorScheme,
|
|
826
|
-
right: /* @__PURE__ */ React4.createElement("span", { style: { display: "flex", gap: "
|
|
807
|
+
right: /* @__PURE__ */ React4.createElement("span", { style: { display: "flex", gap: "10px" } }, /* @__PURE__ */ React4.createElement(
|
|
808
|
+
SmallButton,
|
|
809
|
+
{
|
|
810
|
+
theme,
|
|
811
|
+
onClick: () => {
|
|
812
|
+
console.info(
|
|
813
|
+
"[StateInspector] Reloading assets",
|
|
814
|
+
noyaManager.id
|
|
815
|
+
);
|
|
816
|
+
assetManager.fetch();
|
|
817
|
+
}
|
|
818
|
+
},
|
|
819
|
+
"Reload"
|
|
820
|
+
), /* @__PURE__ */ React4.createElement(
|
|
827
821
|
SmallButton,
|
|
828
822
|
{
|
|
829
823
|
theme,
|
|
@@ -886,7 +880,7 @@ var StateInspector = memoGeneric2(function StateInspector2({
|
|
|
886
880
|
/* @__PURE__ */ React4.createElement(
|
|
887
881
|
DisclosureSection,
|
|
888
882
|
{
|
|
889
|
-
title: "Secrets",
|
|
883
|
+
title: /* @__PURE__ */ React4.createElement(TitleLabel, null, "Secrets", /* @__PURE__ */ React4.createElement(ColoredDot, { type: secretsInitialized ? "success" : "error" })),
|
|
890
884
|
colorScheme,
|
|
891
885
|
open: showSecrets,
|
|
892
886
|
setOpen: setShowSecrets,
|
|
@@ -960,8 +954,7 @@ var StateInspector = memoGeneric2(function StateInspector2({
|
|
|
960
954
|
depth,
|
|
961
955
|
name,
|
|
962
956
|
data,
|
|
963
|
-
isNonenumerable
|
|
964
|
-
expanded
|
|
957
|
+
isNonenumerable
|
|
965
958
|
}) => {
|
|
966
959
|
const direction = event.type === "send" ? "up" : "down";
|
|
967
960
|
return depth === 0 ? /* @__PURE__ */ React4.createElement(ObjectRootLabel, { direction, data }) : /* @__PURE__ */ React4.createElement(
|
|
@@ -1097,6 +1090,24 @@ function SmallButton({
|
|
|
1097
1090
|
children
|
|
1098
1091
|
);
|
|
1099
1092
|
}
|
|
1093
|
+
function ColoredDot({ type }) {
|
|
1094
|
+
return /* @__PURE__ */ React4.createElement(
|
|
1095
|
+
"span",
|
|
1096
|
+
{
|
|
1097
|
+
style: {
|
|
1098
|
+
display: "inline-block",
|
|
1099
|
+
width: 6,
|
|
1100
|
+
height: 6,
|
|
1101
|
+
background: type === "success" ? "rgba(0,200,0,0.8)" : "rgba(200,0,0,0.8)",
|
|
1102
|
+
borderRadius: "50%",
|
|
1103
|
+
verticalAlign: "middle"
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
function TitleLabel({ children }) {
|
|
1109
|
+
return /* @__PURE__ */ React4.createElement("span", { style: { display: "flex", alignItems: "center", gap: "4px" } }, children);
|
|
1110
|
+
}
|
|
1100
1111
|
|
|
1101
1112
|
// src/inspector/useStateInspector.tsx
|
|
1102
1113
|
function createShadowRootElement() {
|
|
@@ -1108,18 +1119,10 @@ function createShadowRootElement() {
|
|
|
1108
1119
|
return { host, container };
|
|
1109
1120
|
}
|
|
1110
1121
|
function useStateInspector({
|
|
1111
|
-
|
|
1112
|
-
connectionEvents,
|
|
1113
|
-
tasks,
|
|
1122
|
+
noyaManager,
|
|
1114
1123
|
disabled = false,
|
|
1115
1124
|
colorScheme,
|
|
1116
|
-
anchor
|
|
1117
|
-
multiplayerStateManager,
|
|
1118
|
-
assetManager,
|
|
1119
|
-
secretManager,
|
|
1120
|
-
connectedUsersManager,
|
|
1121
|
-
ephemeralUserDataManager,
|
|
1122
|
-
forceInit
|
|
1125
|
+
anchor
|
|
1123
1126
|
}) {
|
|
1124
1127
|
const [root, setRoot] = React5.useState(null);
|
|
1125
1128
|
useLayoutEffect2(() => {
|
|
@@ -1138,34 +1141,13 @@ function useStateInspector({
|
|
|
1138
1141
|
/* @__PURE__ */ React5.createElement(
|
|
1139
1142
|
StateInspector,
|
|
1140
1143
|
{
|
|
1141
|
-
state,
|
|
1142
|
-
connectionEvents,
|
|
1143
|
-
tasks,
|
|
1144
1144
|
colorScheme,
|
|
1145
1145
|
anchor,
|
|
1146
|
-
|
|
1147
|
-
multiplayerStateManager,
|
|
1148
|
-
assetManager,
|
|
1149
|
-
secretManager,
|
|
1150
|
-
ephemeralUserDataManager,
|
|
1151
|
-
connectedUsersManager
|
|
1146
|
+
noyaManager
|
|
1152
1147
|
}
|
|
1153
1148
|
)
|
|
1154
1149
|
);
|
|
1155
|
-
}, [
|
|
1156
|
-
anchor,
|
|
1157
|
-
colorScheme,
|
|
1158
|
-
connectionEvents,
|
|
1159
|
-
root,
|
|
1160
|
-
state,
|
|
1161
|
-
multiplayerStateManager,
|
|
1162
|
-
tasks,
|
|
1163
|
-
forceInit,
|
|
1164
|
-
ephemeralUserDataManager,
|
|
1165
|
-
assetManager,
|
|
1166
|
-
secretManager,
|
|
1167
|
-
connectedUsersManager
|
|
1168
|
-
]);
|
|
1150
|
+
}, [noyaManager, anchor, colorScheme, root]);
|
|
1169
1151
|
}
|
|
1170
1152
|
|
|
1171
1153
|
// src/hooks.ts
|
|
@@ -1227,170 +1209,81 @@ function useManagedHistory(stateManager) {
|
|
|
1227
1209
|
stateManager.getHistorySnapshot
|
|
1228
1210
|
);
|
|
1229
1211
|
}
|
|
1230
|
-
function useSyncMultiplayerStateManager(multiplayerStateManager) {
|
|
1231
|
-
return useSyncExternalStore2(
|
|
1232
|
-
multiplayerStateManager.addListener,
|
|
1233
|
-
multiplayerStateManager.getOptimisticState,
|
|
1234
|
-
multiplayerStateManager.getOptimisticState
|
|
1235
|
-
);
|
|
1236
|
-
}
|
|
1237
1212
|
var defaultStubSync = stubSync();
|
|
1238
1213
|
function useMultiplayerState(initialState, options) {
|
|
1239
|
-
const {
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1214
|
+
const {
|
|
1215
|
+
sync = defaultStubSync,
|
|
1216
|
+
inspector,
|
|
1217
|
+
...rest
|
|
1218
|
+
} = options ?? {};
|
|
1219
|
+
const [noyaManager] = useState2(
|
|
1220
|
+
() => new NoyaManager(initialState, rest)
|
|
1245
1221
|
);
|
|
1246
|
-
const
|
|
1247
|
-
|
|
1248
|
-
const [workflowManager] = useState2(() => new WorkflowManager(rpcManager));
|
|
1249
|
-
const [secretManager] = useState2(() => new SecretManager(rpcManager));
|
|
1250
|
-
const [connectedUsersManager] = useState2(() => new ConnectedUsersManager());
|
|
1251
|
-
const syncRef = useRef(sync);
|
|
1252
|
-
const [connectionEvents, setConnectionEvents] = useState2();
|
|
1253
|
-
const [ephemeralUserDataManager] = useState2(
|
|
1254
|
-
() => new EphemeralUserDataManager()
|
|
1222
|
+
const state = useObservable(
|
|
1223
|
+
noyaManager.multiplayerStateManager.optimisticState$
|
|
1255
1224
|
);
|
|
1256
|
-
const
|
|
1257
|
-
const assets = useObservable(assetManager.assets$);
|
|
1225
|
+
const assets = useObservable(noyaManager.assetManager.assets$);
|
|
1258
1226
|
const extras = useMemo2(() => {
|
|
1259
1227
|
return {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
assetManager,
|
|
1267
|
-
aiManager,
|
|
1268
|
-
rpcManager,
|
|
1269
|
-
workflowManager,
|
|
1270
|
-
secretManager,
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1228
|
+
noyaManager,
|
|
1229
|
+
taskManager: noyaManager.taskManager,
|
|
1230
|
+
connectionEventManager: noyaManager.connectionEventManager,
|
|
1231
|
+
connectedUsersManager: noyaManager.connectedUsersManager,
|
|
1232
|
+
ephemeralUserDataManager: noyaManager.ephemeralUserDataManager,
|
|
1233
|
+
multiplayerStateManager: noyaManager.multiplayerStateManager,
|
|
1234
|
+
assetManager: noyaManager.assetManager,
|
|
1235
|
+
aiManager: noyaManager.aiManager,
|
|
1236
|
+
rpcManager: noyaManager.rpcManager,
|
|
1237
|
+
workflowManager: noyaManager.workflowManager,
|
|
1238
|
+
secretManager: noyaManager.secretManager,
|
|
1239
|
+
menuItemsManager: noyaManager.menuManager,
|
|
1240
|
+
createAsset: noyaManager.assetManager.create,
|
|
1241
|
+
deleteAsset: noyaManager.assetManager.delete,
|
|
1242
|
+
assets
|
|
1275
1243
|
};
|
|
1276
|
-
}, [
|
|
1277
|
-
|
|
1278
|
-
connectionEvents,
|
|
1279
|
-
connectedUsersManager,
|
|
1280
|
-
ephemeralUserDataManager,
|
|
1281
|
-
multiplayerStateManager,
|
|
1282
|
-
assets,
|
|
1283
|
-
assetManager,
|
|
1284
|
-
aiManager,
|
|
1285
|
-
rpcManager,
|
|
1286
|
-
workflowManager,
|
|
1287
|
-
secretManager
|
|
1288
|
-
]);
|
|
1289
|
-
const globalTrackEvents = useObservable(shouldTrackEvents$);
|
|
1290
|
-
const globalTrackTasks = useObservable(shouldTrackTasks$);
|
|
1291
|
-
const shouldTrackEvents = options?.trackConnectionEvents ?? globalTrackEvents;
|
|
1292
|
-
const shouldTrackTasks = options?.trackTasks ?? globalTrackTasks;
|
|
1293
|
-
const trackEventsCallbackRef = useRef();
|
|
1294
|
-
const trackTasksCallbackRef = useRef();
|
|
1295
|
-
useEffect3(() => {
|
|
1296
|
-
trackEventsCallbackRef.current = shouldTrackEvents ? (e) => setConnectionEvents((events) => {
|
|
1297
|
-
const updated = events ? [...events, e] : [e];
|
|
1298
|
-
return updated.length > 50 ? updated.slice(-50) : updated;
|
|
1299
|
-
}) : void 0;
|
|
1300
|
-
}, [shouldTrackEvents]);
|
|
1301
|
-
useEffect3(() => {
|
|
1302
|
-
trackTasksCallbackRef.current = shouldTrackTasks ? (tasks2) => {
|
|
1303
|
-
setTasks((old) => {
|
|
1304
|
-
const newIds = new Set(tasks2.map((t) => t.id));
|
|
1305
|
-
const unchanged = old.filter((t) => !newIds.has(t.id));
|
|
1306
|
-
return [...unchanged, ...tasks2];
|
|
1307
|
-
});
|
|
1308
|
-
} : void 0;
|
|
1309
|
-
}, [shouldTrackTasks]);
|
|
1244
|
+
}, [noyaManager, assets]);
|
|
1245
|
+
const syncRef = useRef(sync);
|
|
1310
1246
|
useEffect3(() => {
|
|
1311
1247
|
if (syncRef.current) {
|
|
1312
|
-
return syncRef.current({
|
|
1313
|
-
ms: multiplayerStateManager,
|
|
1314
|
-
assetManager,
|
|
1315
|
-
workflowManager,
|
|
1316
|
-
secretManager,
|
|
1317
|
-
rpcManager,
|
|
1318
|
-
aiManager,
|
|
1319
|
-
ephemeralUserDataManager,
|
|
1320
|
-
connectedUsersManager,
|
|
1321
|
-
onConnectionEvent: (e) => trackEventsCallbackRef.current?.(e),
|
|
1322
|
-
onChangeTasks: (tasks2) => trackTasksCallbackRef.current?.(tasks2)
|
|
1323
|
-
});
|
|
1248
|
+
return syncRef.current({ noyaManager });
|
|
1324
1249
|
}
|
|
1325
|
-
}, [
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
const
|
|
1336
|
-
const forceInit = useCallback(() => {
|
|
1337
|
-
const state2 = initialState instanceof Function ? initialState() : initialState;
|
|
1338
|
-
const schema = options?.schema;
|
|
1339
|
-
multiplayerStateManager.sm.clearHistory();
|
|
1340
|
-
multiplayerStateManager.sendInit({ force: true, state: state2, schema });
|
|
1341
|
-
}, [initialState, multiplayerStateManager, options?.schema]);
|
|
1342
|
-
const [unrecoverableError, setUnrecoverableError] = useState2();
|
|
1343
|
-
useEffect3(() => {
|
|
1344
|
-
return multiplayerStateManager.errorEmitter.addListener((error) => {
|
|
1345
|
-
switch (error.reason) {
|
|
1346
|
-
case "invalidDataType":
|
|
1347
|
-
case "schemaMismatch":
|
|
1348
|
-
case "outdatedSchema":
|
|
1349
|
-
case "dataMigrationFailure":
|
|
1350
|
-
case "schemaMigration":
|
|
1351
|
-
setUnrecoverableError(error);
|
|
1352
|
-
break;
|
|
1353
|
-
}
|
|
1354
|
-
});
|
|
1355
|
-
}, [multiplayerStateManager]);
|
|
1250
|
+
}, [noyaManager]);
|
|
1251
|
+
useErrorOverlay(noyaManager);
|
|
1252
|
+
useStateInspector({
|
|
1253
|
+
noyaManager,
|
|
1254
|
+
disabled: !inspector,
|
|
1255
|
+
...typeof inspector === "object" && inspector
|
|
1256
|
+
});
|
|
1257
|
+
return [state, noyaManager.multiplayerStateManager.setState, extras];
|
|
1258
|
+
}
|
|
1259
|
+
function useErrorOverlay(noyaManager) {
|
|
1260
|
+
const unrecoverableError = useObservable(noyaManager.unrecoverableError);
|
|
1356
1261
|
useEffect3(() => {
|
|
1357
1262
|
if (!unrecoverableError) return;
|
|
1358
1263
|
const el = document.createElement("div");
|
|
1359
1264
|
el.id = "noya-multiplayer-react-error-overlay";
|
|
1360
1265
|
document.body.appendChild(el);
|
|
1361
1266
|
const root = createRoot2(el);
|
|
1362
|
-
root.render(
|
|
1267
|
+
root.render(createElement2(ErrorOverlay, { error: unrecoverableError }));
|
|
1363
1268
|
return () => {
|
|
1364
1269
|
root.unmount();
|
|
1365
1270
|
el.remove();
|
|
1366
1271
|
};
|
|
1367
1272
|
});
|
|
1368
|
-
useStateInspector({
|
|
1369
|
-
ephemeralUserDataManager,
|
|
1370
|
-
multiplayerStateManager,
|
|
1371
|
-
assetManager,
|
|
1372
|
-
secretManager,
|
|
1373
|
-
state,
|
|
1374
|
-
connectionEvents,
|
|
1375
|
-
connectedUsersManager,
|
|
1376
|
-
tasks,
|
|
1377
|
-
forceInit,
|
|
1378
|
-
disabled: !options?.inspector,
|
|
1379
|
-
...typeof options?.inspector === "object" && options.inspector
|
|
1380
|
-
});
|
|
1381
|
-
return [state, multiplayerStateManager.setState, extras];
|
|
1382
1273
|
}
|
|
1383
1274
|
|
|
1384
1275
|
// src/noyaApp.ts
|
|
1385
1276
|
import {
|
|
1386
1277
|
createOrCastValue,
|
|
1387
1278
|
findAllDefs,
|
|
1279
|
+
isEmbeddedApp,
|
|
1388
1280
|
localStorageSync,
|
|
1281
|
+
parentFrameSync,
|
|
1389
1282
|
stubSync as stubSync2,
|
|
1390
1283
|
TypeGuard,
|
|
1391
1284
|
webSocketSync
|
|
1392
1285
|
} from "@noya-app/state-manager";
|
|
1393
|
-
import {
|
|
1286
|
+
import { useMemo as useMemo3, useState as useState3 } from "react";
|
|
1394
1287
|
function createDefaultAppData(initialState) {
|
|
1395
1288
|
return {
|
|
1396
1289
|
theme: "light",
|
|
@@ -1429,43 +1322,6 @@ function getAppData(initialState, schema, options) {
|
|
|
1429
1322
|
appData.initialState = enforceSchema(appData.initialState, schema);
|
|
1430
1323
|
return appData;
|
|
1431
1324
|
}
|
|
1432
|
-
function useBroadcastConnectedUsers(connectedUsersManager) {
|
|
1433
|
-
useEffect4(() => {
|
|
1434
|
-
if (!isEmbeddedApp()) return;
|
|
1435
|
-
const unsubscribe = connectedUsersManager.connectedUsers$.subscribe(
|
|
1436
|
-
(users) => {
|
|
1437
|
-
const message = {
|
|
1438
|
-
type: "multiplayer.setUsers",
|
|
1439
|
-
payload: { users }
|
|
1440
|
-
};
|
|
1441
|
-
parent?.postMessage(message, "*");
|
|
1442
|
-
}
|
|
1443
|
-
);
|
|
1444
|
-
return unsubscribe;
|
|
1445
|
-
}, [connectedUsersManager]);
|
|
1446
|
-
}
|
|
1447
|
-
function useBroadcastMenuItems(menuItems, handleSelectMenuItem) {
|
|
1448
|
-
useEffect4(() => {
|
|
1449
|
-
if (!isEmbeddedApp()) return;
|
|
1450
|
-
const message = {
|
|
1451
|
-
type: "application.setMenuItems",
|
|
1452
|
-
payload: { menuItems }
|
|
1453
|
-
};
|
|
1454
|
-
parent?.postMessage(message, "*");
|
|
1455
|
-
const listener = (event) => {
|
|
1456
|
-
if (event.data.type === "application.selectMenuItem") {
|
|
1457
|
-
handleSelectMenuItem(event.data.payload.value);
|
|
1458
|
-
}
|
|
1459
|
-
};
|
|
1460
|
-
window.addEventListener("message", listener);
|
|
1461
|
-
return () => {
|
|
1462
|
-
window.removeEventListener("message", listener);
|
|
1463
|
-
};
|
|
1464
|
-
}, [handleSelectMenuItem, menuItems]);
|
|
1465
|
-
}
|
|
1466
|
-
function isEmbeddedApp() {
|
|
1467
|
-
return window.self !== window.top;
|
|
1468
|
-
}
|
|
1469
1325
|
function useNoyaState(...args) {
|
|
1470
1326
|
const [initialState, options] = typeof args[0] === "object" && args[0] !== null && "schema" in args[0] && TypeGuard.IsSchema(args[0].schema) ? [null, args[0]] : [args[0], args[1]];
|
|
1471
1327
|
const [
|
|
@@ -1482,15 +1338,14 @@ function useNoyaState(...args) {
|
|
|
1482
1338
|
});
|
|
1483
1339
|
});
|
|
1484
1340
|
const sync = useMemo3(() => {
|
|
1485
|
-
return multiplayerUrl ? webSocketSync(multiplayerUrl) : options?.offlineStorageKey ? localStorageSync({ key: options.offlineStorageKey }) : stubSync2();
|
|
1486
|
-
}, [multiplayerUrl, options?.offlineStorageKey]);
|
|
1341
|
+
return isEmbeddedApp() ? parentFrameSync({ debug: options?.debug }) : multiplayerUrl ? webSocketSync(multiplayerUrl) : options?.offlineStorageKey ? localStorageSync({ key: options.offlineStorageKey }) : stubSync2();
|
|
1342
|
+
}, [multiplayerUrl, options?.offlineStorageKey, options?.debug]);
|
|
1487
1343
|
const result = useMultiplayerState(noyaAppInitialState, {
|
|
1488
1344
|
...options,
|
|
1489
1345
|
inspector: options?.inspector ?? inspector,
|
|
1490
1346
|
sync: options?.sync ?? sync
|
|
1491
1347
|
});
|
|
1492
1348
|
const [, , extras] = result;
|
|
1493
|
-
useBroadcastConnectedUsers(extras.connectedUsersManager);
|
|
1494
1349
|
const mergedExtras = useMemo3(() => {
|
|
1495
1350
|
return { ...extras, theme, viewType };
|
|
1496
1351
|
}, [extras, theme, viewType]);
|
|
@@ -1499,12 +1354,13 @@ function useNoyaState(...args) {
|
|
|
1499
1354
|
|
|
1500
1355
|
// src/NoyaStateContext.tsx
|
|
1501
1356
|
import {
|
|
1502
|
-
Observable
|
|
1357
|
+
Observable
|
|
1503
1358
|
} from "@noya-app/observable";
|
|
1504
1359
|
import React6, {
|
|
1505
1360
|
createContext,
|
|
1506
1361
|
useCallback as useCallback2,
|
|
1507
1362
|
useContext,
|
|
1363
|
+
useEffect as useEffect4,
|
|
1508
1364
|
useMemo as useMemo4
|
|
1509
1365
|
} from "react";
|
|
1510
1366
|
var AnyNoyaStateContext = createContext(void 0);
|
|
@@ -1559,9 +1415,13 @@ function useSecret(name) {
|
|
|
1559
1415
|
);
|
|
1560
1416
|
}
|
|
1561
1417
|
function useIsInitialized() {
|
|
1562
|
-
const {
|
|
1418
|
+
const {
|
|
1419
|
+
multiplayerStateManager: ms,
|
|
1420
|
+
secretManager,
|
|
1421
|
+
assetManager
|
|
1422
|
+
} = useAnyNoyaStateContext();
|
|
1563
1423
|
const isInitializedObservable = useMemo4(
|
|
1564
|
-
() =>
|
|
1424
|
+
() => Observable.combine(
|
|
1565
1425
|
[
|
|
1566
1426
|
ms.isInitialized$,
|
|
1567
1427
|
secretManager.isInitialized$,
|
|
@@ -1607,49 +1467,27 @@ function createNoyaContext({
|
|
|
1607
1467
|
initialState,
|
|
1608
1468
|
...options
|
|
1609
1469
|
}) {
|
|
1610
|
-
const [
|
|
1611
|
-
,
|
|
1612
|
-
,
|
|
1613
|
-
{
|
|
1614
|
-
multiplayerStateManager,
|
|
1615
|
-
assetManager,
|
|
1616
|
-
workflowManager,
|
|
1617
|
-
secretManager,
|
|
1618
|
-
aiManager,
|
|
1619
|
-
ephemeralUserDataManager,
|
|
1620
|
-
connectedUsersManager
|
|
1621
|
-
}
|
|
1622
|
-
] = useNoyaState(initialState, {
|
|
1470
|
+
const [, , { noyaManager }] = useNoyaState(initialState, {
|
|
1623
1471
|
...options,
|
|
1624
1472
|
schema,
|
|
1625
1473
|
mergeHistoryEntries
|
|
1626
1474
|
});
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
ms: multiplayerStateManager,
|
|
1630
|
-
assetManager,
|
|
1631
|
-
workflowManager,
|
|
1632
|
-
secretManager,
|
|
1633
|
-
aiManager
|
|
1634
|
-
}),
|
|
1635
|
-
[
|
|
1636
|
-
multiplayerStateManager,
|
|
1637
|
-
assetManager,
|
|
1638
|
-
workflowManager,
|
|
1639
|
-
secretManager,
|
|
1640
|
-
aiManager
|
|
1641
|
-
]
|
|
1642
|
-
);
|
|
1643
|
-
return /* @__PURE__ */ React6.createElement(AnyNoyaStateContext.Provider, { value }, /* @__PURE__ */ React6.createElement(ConnectedUsersContext.Provider, { value: connectedUsersManager }, /* @__PURE__ */ React6.createElement(
|
|
1644
|
-
EphemeralUserDataManagerContext.Provider,
|
|
1475
|
+
return /* @__PURE__ */ React6.createElement(AnyNoyaStateContext.Provider, { value: noyaManager }, /* @__PURE__ */ React6.createElement(
|
|
1476
|
+
ConnectedUsersContext.Provider,
|
|
1645
1477
|
{
|
|
1646
|
-
value:
|
|
1478
|
+
value: noyaManager.connectedUsersManager
|
|
1647
1479
|
},
|
|
1648
|
-
|
|
1649
|
-
|
|
1480
|
+
/* @__PURE__ */ React6.createElement(
|
|
1481
|
+
EphemeralUserDataManagerContext.Provider,
|
|
1482
|
+
{
|
|
1483
|
+
value: noyaManager.ephemeralUserDataManager
|
|
1484
|
+
},
|
|
1485
|
+
options.fallback !== void 0 ? /* @__PURE__ */ React6.createElement(FallbackUntilInitialized, { fallback: options.fallback }, children) : children
|
|
1486
|
+
)
|
|
1487
|
+
));
|
|
1650
1488
|
}
|
|
1651
1489
|
function useValue(path, options) {
|
|
1652
|
-
const { ms } = useAnyNoyaStateContext();
|
|
1490
|
+
const { multiplayerStateManager: ms } = useAnyNoyaStateContext();
|
|
1653
1491
|
let actualPath = typeof path === "function" || !path ? "" : path;
|
|
1654
1492
|
const mappedObservable = useMemo4(() => {
|
|
1655
1493
|
if (typeof path === "function") {
|
|
@@ -1661,12 +1499,11 @@ function createNoyaContext({
|
|
|
1661
1499
|
return value;
|
|
1662
1500
|
}
|
|
1663
1501
|
function useSetValue(path) {
|
|
1664
|
-
const { ms } = useAnyNoyaStateContext();
|
|
1502
|
+
const { multiplayerStateManager: ms } = useAnyNoyaStateContext();
|
|
1665
1503
|
const setValue = useCallback2(
|
|
1666
1504
|
(...args) => {
|
|
1667
|
-
const [metadata, value] = args.length === 2 ? args : [
|
|
1668
|
-
|
|
1669
|
-
setStateAtPath(metadata, path ?? "", value);
|
|
1505
|
+
const [metadata, value] = args.length === 2 ? args : [{}, args[0]];
|
|
1506
|
+
ms.setStateAtPath(metadata, path ?? "", value);
|
|
1670
1507
|
},
|
|
1671
1508
|
[ms, path]
|
|
1672
1509
|
);
|
|
@@ -1679,7 +1516,7 @@ function createNoyaContext({
|
|
|
1679
1516
|
return [value, setValue];
|
|
1680
1517
|
}
|
|
1681
1518
|
function useStateManager() {
|
|
1682
|
-
const { ms } = useAnyNoyaStateContext();
|
|
1519
|
+
const { multiplayerStateManager: ms } = useAnyNoyaStateContext();
|
|
1683
1520
|
return ms;
|
|
1684
1521
|
}
|
|
1685
1522
|
function useEphemeralUserDataManager() {
|
|
@@ -1691,6 +1528,27 @@ function createNoyaContext({
|
|
|
1691
1528
|
}
|
|
1692
1529
|
return ephemeralUserData;
|
|
1693
1530
|
}
|
|
1531
|
+
function useNoyaManager() {
|
|
1532
|
+
return useAnyNoyaStateContext();
|
|
1533
|
+
}
|
|
1534
|
+
function useLeftMenuItems() {
|
|
1535
|
+
const { menuManager } = useNoyaManager();
|
|
1536
|
+
return useObservable(menuManager.leftMenuItems$);
|
|
1537
|
+
}
|
|
1538
|
+
function useRightMenuItems() {
|
|
1539
|
+
const { menuManager } = useNoyaManager();
|
|
1540
|
+
return useObservable(menuManager.rightMenuItems$);
|
|
1541
|
+
}
|
|
1542
|
+
function useHandleMenuItem(callback) {
|
|
1543
|
+
const { menuManager } = useNoyaManager();
|
|
1544
|
+
useEffect4(() => {
|
|
1545
|
+
return menuManager.addListener(callback);
|
|
1546
|
+
}, [menuManager, callback]);
|
|
1547
|
+
}
|
|
1548
|
+
function useOnSelectMenuItemCallback() {
|
|
1549
|
+
const { menuManager } = useNoyaManager();
|
|
1550
|
+
return useCallback2((type) => menuManager.emit(type), [menuManager]);
|
|
1551
|
+
}
|
|
1694
1552
|
return {
|
|
1695
1553
|
Context: NoyaStateContext,
|
|
1696
1554
|
Provider: NoyaStateProvider,
|
|
@@ -1698,31 +1556,130 @@ function createNoyaContext({
|
|
|
1698
1556
|
useSetValue,
|
|
1699
1557
|
useValueState,
|
|
1700
1558
|
useStateManager,
|
|
1701
|
-
useEphemeralUserDataManager
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1559
|
+
useEphemeralUserDataManager,
|
|
1560
|
+
useLeftMenuItems,
|
|
1561
|
+
useRightMenuItems,
|
|
1562
|
+
useHandleMenuItem,
|
|
1563
|
+
useOnSelectMenuItemCallback,
|
|
1564
|
+
useNoyaManager
|
|
1705
1565
|
};
|
|
1706
1566
|
}
|
|
1567
|
+
|
|
1568
|
+
// src/WebSocketConnection.ts
|
|
1569
|
+
import {
|
|
1570
|
+
ReconnectingWebSocket,
|
|
1571
|
+
UserActivityDetector
|
|
1572
|
+
} from "@noya-app/state-manager";
|
|
1573
|
+
var WebSocketConnection = class {
|
|
1574
|
+
constructor(url, options = {}) {
|
|
1575
|
+
this.url = url;
|
|
1576
|
+
this.options = options;
|
|
1577
|
+
this.ws = new ReconnectingWebSocket({
|
|
1578
|
+
debug: options.debug,
|
|
1579
|
+
onopen: this.handleOpen,
|
|
1580
|
+
onmessage: this.handleMessage,
|
|
1581
|
+
onclose: this.handleClose,
|
|
1582
|
+
activityDetector: new UserActivityDetector()
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
ws;
|
|
1586
|
+
pingCount = 0;
|
|
1587
|
+
pendingPingId;
|
|
1588
|
+
intervalId;
|
|
1589
|
+
async connect() {
|
|
1590
|
+
this.ws.connect(this.url.toString());
|
|
1591
|
+
this.intervalId = setInterval(() => {
|
|
1592
|
+
if (this.ws.state !== "OPEN") return;
|
|
1593
|
+
if (this.pendingPingId) {
|
|
1594
|
+
this.ws.close();
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
const pingId = (this.pingCount++).toString();
|
|
1598
|
+
const message = {
|
|
1599
|
+
type: "ping",
|
|
1600
|
+
id: pingId
|
|
1601
|
+
};
|
|
1602
|
+
this.pendingPingId = message.id;
|
|
1603
|
+
this.options.onConnectionEvent?.({
|
|
1604
|
+
type: "send",
|
|
1605
|
+
message
|
|
1606
|
+
});
|
|
1607
|
+
this.ws.send(JSON.stringify(message));
|
|
1608
|
+
}, 1e4);
|
|
1609
|
+
}
|
|
1610
|
+
handleOpen = () => {
|
|
1611
|
+
this.pendingPingId = void 0;
|
|
1612
|
+
if (this.options.debug) {
|
|
1613
|
+
console.info("ws connected");
|
|
1614
|
+
}
|
|
1615
|
+
this.options.onConnectionEvent?.({
|
|
1616
|
+
type: "stateChange",
|
|
1617
|
+
state: "OPEN"
|
|
1618
|
+
});
|
|
1619
|
+
};
|
|
1620
|
+
handleMessage = (event) => {
|
|
1621
|
+
if (this.options.debug) {
|
|
1622
|
+
console.info("ws receiving message ", event.data);
|
|
1623
|
+
}
|
|
1624
|
+
const parsed = JSON.parse(event.data);
|
|
1625
|
+
this.options.onConnectionEvent?.({
|
|
1626
|
+
type: "receive",
|
|
1627
|
+
message: parsed
|
|
1628
|
+
});
|
|
1629
|
+
if (parsed.type === "pong" && parsed.id === this.pendingPingId) {
|
|
1630
|
+
this.pendingPingId = void 0;
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
handleClose = () => {
|
|
1634
|
+
this.pendingPingId = void 0;
|
|
1635
|
+
if (this.options.debug) {
|
|
1636
|
+
console.info("ws disconnected");
|
|
1637
|
+
}
|
|
1638
|
+
this.options.onConnectionEvent?.({
|
|
1639
|
+
type: "stateChange",
|
|
1640
|
+
state: "CLOSED"
|
|
1641
|
+
});
|
|
1642
|
+
};
|
|
1643
|
+
send(message) {
|
|
1644
|
+
if (this.options.debug) {
|
|
1645
|
+
console.info("ws sending message", message);
|
|
1646
|
+
}
|
|
1647
|
+
this.options.onConnectionEvent?.({
|
|
1648
|
+
type: "send",
|
|
1649
|
+
message
|
|
1650
|
+
});
|
|
1651
|
+
this.ws.send(JSON.stringify(message));
|
|
1652
|
+
}
|
|
1653
|
+
close() {
|
|
1654
|
+
if (this.intervalId) {
|
|
1655
|
+
clearInterval(this.intervalId);
|
|
1656
|
+
}
|
|
1657
|
+
this.ws.shutdown();
|
|
1658
|
+
}
|
|
1659
|
+
addListener(listener) {
|
|
1660
|
+
return this.ws.addListener(listener);
|
|
1661
|
+
}
|
|
1662
|
+
removeListener(listener) {
|
|
1663
|
+
return this.ws.removeListener(listener);
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1707
1666
|
export {
|
|
1708
1667
|
AnyEphemeralUserDataManagerContext,
|
|
1709
1668
|
ConnectedUsersContext,
|
|
1710
1669
|
FallbackUntilInitialized,
|
|
1711
1670
|
StateInspector,
|
|
1712
1671
|
UserPointersOverlay,
|
|
1672
|
+
WebSocketConnection,
|
|
1713
1673
|
createDefaultAppData,
|
|
1714
1674
|
createNoyaContext,
|
|
1715
1675
|
enforceSchema,
|
|
1716
1676
|
getAppData,
|
|
1717
|
-
isEmbeddedApp,
|
|
1718
1677
|
parseAppDataParameter,
|
|
1719
1678
|
useAIManager,
|
|
1720
1679
|
useAnyEphemeralUserData,
|
|
1721
1680
|
useAsset,
|
|
1722
1681
|
useAssetManager,
|
|
1723
1682
|
useAssets,
|
|
1724
|
-
useBroadcastConnectedUsers,
|
|
1725
|
-
useBroadcastMenuItems,
|
|
1726
1683
|
useConnectedUsersManager,
|
|
1727
1684
|
useCurrentUserId,
|
|
1728
1685
|
useIsInitialized,
|
|
@@ -1734,7 +1691,6 @@ export {
|
|
|
1734
1691
|
useSecret,
|
|
1735
1692
|
useSecretManager,
|
|
1736
1693
|
useSecrets,
|
|
1737
|
-
useSyncMultiplayerStateManager,
|
|
1738
1694
|
useSyncStateManager,
|
|
1739
1695
|
useWorkflowManager
|
|
1740
1696
|
};
|