@noya-app/noya-multiplayer-react 0.1.70 → 0.1.72
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 +24 -0
- package/dist/index.bundle.js +46 -20
- package/dist/index.d.mts +84 -55
- package/dist/index.d.ts +84 -55
- package/dist/index.js +518 -273
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +471 -233
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/NoyaStateContext.tsx +156 -105
- package/src/__tests__/noyaApp.test.ts +34 -0
- package/src/__tests__/resourcePayload.test.ts +24 -134
- package/src/components/UserPointersOverlay.tsx +22 -20
- package/src/hooks.ts +22 -14
- package/src/inspector/StateInspector.tsx +318 -255
- package/src/inspector/StateInspectorRow.tsx +3 -1
- package/src/inspector/sections/ServerScriptLogsSection.tsx +128 -0
- package/src/inspector/serialization.ts +4 -2
- package/src/inspector/useStateInspector.tsx +10 -4
- package/src/noyaApp.ts +94 -22
- package/src/singleton.tsx +46 -8
|
@@ -8,12 +8,14 @@ export function StateInspectorRow({
|
|
|
8
8
|
selected,
|
|
9
9
|
style,
|
|
10
10
|
variant,
|
|
11
|
+
bordered = true,
|
|
11
12
|
}: {
|
|
12
13
|
children: React.ReactNode;
|
|
13
14
|
colorScheme: "light" | "dark";
|
|
14
15
|
selected?: boolean;
|
|
15
16
|
style?: CSSProperties;
|
|
16
17
|
variant?: "up" | "down";
|
|
18
|
+
bordered?: boolean;
|
|
17
19
|
}) {
|
|
18
20
|
const solidBorderColor =
|
|
19
21
|
colorScheme === "light" ? "rgb(223 223 223)" : "rgb(29 29 29)";
|
|
@@ -21,7 +23,7 @@ export function StateInspectorRow({
|
|
|
21
23
|
return (
|
|
22
24
|
<div
|
|
23
25
|
style={{
|
|
24
|
-
borderBottom: `1px solid ${solidBorderColor}
|
|
26
|
+
borderBottom: bordered ? `1px solid ${solidBorderColor}` : undefined,
|
|
25
27
|
fontSize: "12px",
|
|
26
28
|
fontFamily: "Menlo, monospace",
|
|
27
29
|
padding: "2px 12px 1px",
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { ServerScriptLogEntry } from "@noya-app/state-manager";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { ObjectInspector } from "react-inspector";
|
|
4
|
+
import { getStateInspectorTheme } from "../inspectorTheme";
|
|
5
|
+
import { StateInspectorButton } from "../StateInspectorButton";
|
|
6
|
+
import {
|
|
7
|
+
StateInspectorDisclosureRowInner,
|
|
8
|
+
StateInspectorDisclosureSection,
|
|
9
|
+
} from "../StateInspectorDisclosureSection";
|
|
10
|
+
import { StateInspectorRow } from "../StateInspectorRow";
|
|
11
|
+
|
|
12
|
+
const levelColors: Record<ServerScriptLogEntry["level"], string> = {
|
|
13
|
+
error: "#f87171",
|
|
14
|
+
warn: "#facc15",
|
|
15
|
+
log: "#9ca3af",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function formatTimestamp(timestamp: number) {
|
|
19
|
+
return new Date(timestamp).toLocaleTimeString();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function ServerScriptLogsSection({
|
|
23
|
+
showLogs,
|
|
24
|
+
setShowLogs,
|
|
25
|
+
colorScheme,
|
|
26
|
+
logs,
|
|
27
|
+
containerRef,
|
|
28
|
+
paused,
|
|
29
|
+
onTogglePaused,
|
|
30
|
+
}: {
|
|
31
|
+
showLogs: boolean;
|
|
32
|
+
setShowLogs: (value: boolean) => void;
|
|
33
|
+
colorScheme: "light" | "dark";
|
|
34
|
+
logs: ServerScriptLogEntry[];
|
|
35
|
+
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
36
|
+
paused: boolean;
|
|
37
|
+
onTogglePaused: () => void;
|
|
38
|
+
}) {
|
|
39
|
+
const theme = getStateInspectorTheme(colorScheme);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<StateInspectorDisclosureSection
|
|
43
|
+
open={showLogs}
|
|
44
|
+
setOpen={setShowLogs}
|
|
45
|
+
title="Server Script Logs"
|
|
46
|
+
colorScheme={colorScheme}
|
|
47
|
+
right={
|
|
48
|
+
<StateInspectorButton theme={theme} onClick={onTogglePaused}>
|
|
49
|
+
{paused ? "Resume" : "Pause"}
|
|
50
|
+
</StateInspectorButton>
|
|
51
|
+
}
|
|
52
|
+
>
|
|
53
|
+
<StateInspectorDisclosureRowInner ref={containerRef}>
|
|
54
|
+
{paused && (
|
|
55
|
+
<div
|
|
56
|
+
style={{
|
|
57
|
+
padding: "4px 12px",
|
|
58
|
+
fontSize: "11px",
|
|
59
|
+
opacity: 0.7,
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
Log streaming paused
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
{logs.map((log) => (
|
|
66
|
+
<StateInspectorRow key={log.id} colorScheme={colorScheme}>
|
|
67
|
+
<div
|
|
68
|
+
style={{
|
|
69
|
+
display: "flex",
|
|
70
|
+
gap: "6px",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
fontSize: "10px",
|
|
73
|
+
color: "inherit",
|
|
74
|
+
marginBottom: log.values.length ? "4px" : 0,
|
|
75
|
+
textTransform: "uppercase",
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<span>{formatTimestamp(log.timestamp)}</span>
|
|
79
|
+
<span style={{ color: levelColors[log.level] }}>{log.level}</span>
|
|
80
|
+
<span
|
|
81
|
+
style={{
|
|
82
|
+
fontFamily: "monospace",
|
|
83
|
+
textTransform: "none",
|
|
84
|
+
fontSize: "10px",
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{log.scriptId}
|
|
88
|
+
</span>
|
|
89
|
+
</div>
|
|
90
|
+
<div
|
|
91
|
+
style={{
|
|
92
|
+
display: "flex",
|
|
93
|
+
flexDirection: "column",
|
|
94
|
+
gap: "4px",
|
|
95
|
+
}}
|
|
96
|
+
>
|
|
97
|
+
{log.values.length > 0 ? (
|
|
98
|
+
log.values.map((value, index) => (
|
|
99
|
+
<ObjectInspector
|
|
100
|
+
key={index}
|
|
101
|
+
data={value}
|
|
102
|
+
theme={theme}
|
|
103
|
+
expandLevel={3}
|
|
104
|
+
/>
|
|
105
|
+
))
|
|
106
|
+
) : (
|
|
107
|
+
<span style={{ fontSize: "11px", opacity: 0.7 }}>No data</span>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
</StateInspectorRow>
|
|
111
|
+
))}
|
|
112
|
+
{!logs?.length && (
|
|
113
|
+
<div
|
|
114
|
+
style={{
|
|
115
|
+
padding: "12px",
|
|
116
|
+
fontSize: "12px",
|
|
117
|
+
display: "flex",
|
|
118
|
+
flexDirection: "column",
|
|
119
|
+
gap: "4px",
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
<span>No logs received</span>
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
</StateInspectorDisclosureRowInner>
|
|
126
|
+
</StateInspectorDisclosureSection>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
@@ -17,7 +17,9 @@ export async function fetchAssetMap(
|
|
|
17
17
|
return Object.fromEntries(res);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export async function exportAll(
|
|
20
|
+
export async function exportAll(
|
|
21
|
+
noyaManager: NoyaManager<any, any, any, any, any>
|
|
22
|
+
) {
|
|
21
23
|
const { multiplayerStateManager, assetManager } = noyaManager;
|
|
22
24
|
|
|
23
25
|
const assetMap = await fetchAssetMap(assetManager.assets$.get());
|
|
@@ -64,7 +66,7 @@ export function encodeAll({
|
|
|
64
66
|
|
|
65
67
|
export async function importAll(
|
|
66
68
|
buffer: Uint8Array | ArrayBuffer,
|
|
67
|
-
noyaManager: NoyaManager<any, any, any, any>,
|
|
69
|
+
noyaManager: NoyaManager<any, any, any, any, any>,
|
|
68
70
|
{
|
|
69
71
|
shouldAllowSchemaChange,
|
|
70
72
|
}: {
|
|
@@ -35,15 +35,20 @@ export function useStateInspector<
|
|
|
35
35
|
M extends object,
|
|
36
36
|
E extends object,
|
|
37
37
|
MenuT extends string = string,
|
|
38
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
38
39
|
>({
|
|
39
40
|
noyaManager,
|
|
40
41
|
disabled = false,
|
|
42
|
+
advanced = false,
|
|
41
43
|
colorScheme,
|
|
42
44
|
anchor,
|
|
43
45
|
}: {
|
|
44
|
-
noyaManager: NoyaManager<S, M, E, MenuT>;
|
|
45
|
-
disabled
|
|
46
|
-
|
|
46
|
+
noyaManager: NoyaManager<S, M, E, MenuT, I>;
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
advanced?: boolean;
|
|
49
|
+
colorScheme?: ComponentProps<typeof StateInspector>["colorScheme"];
|
|
50
|
+
anchor?: ComponentProps<typeof StateInspector>["anchor"];
|
|
51
|
+
}) {
|
|
47
52
|
const [root, setRoot] = React.useState<Root | null>(null);
|
|
48
53
|
|
|
49
54
|
useIsomorphicLayoutEffect(() => {
|
|
@@ -64,7 +69,8 @@ export function useStateInspector<
|
|
|
64
69
|
colorScheme={colorScheme}
|
|
65
70
|
anchor={anchor}
|
|
66
71
|
noyaManager={noyaManager}
|
|
72
|
+
advanced={advanced}
|
|
67
73
|
/>
|
|
68
74
|
);
|
|
69
|
-
}, [noyaManager, anchor, colorScheme, root]);
|
|
75
|
+
}, [noyaManager, anchor, colorScheme, root, advanced]);
|
|
70
76
|
}
|
package/src/noyaApp.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { findAllDefs } from "@noya-app/noya-schemas";
|
|
4
|
+
import type { LocalStorageSyncOptions } from "@noya-app/state-manager";
|
|
4
5
|
import {
|
|
6
|
+
AccessType,
|
|
5
7
|
createOrCastValue,
|
|
6
8
|
isEmbeddedApp,
|
|
7
9
|
localStorageSync,
|
|
8
10
|
NoyaManager,
|
|
9
11
|
parentFrameSync,
|
|
12
|
+
PolicyAuthContext,
|
|
10
13
|
Static,
|
|
11
14
|
stubSync,
|
|
12
15
|
SyncAdapter,
|
|
@@ -14,7 +17,7 @@ import {
|
|
|
14
17
|
TypeGuard,
|
|
15
18
|
webSocketSync,
|
|
16
19
|
} from "@noya-app/state-manager";
|
|
17
|
-
import { useMemo, useState } from "react";
|
|
20
|
+
import { useEffect, useMemo, useState } from "react";
|
|
18
21
|
import { useBindNoyaManager, UseMultiplayerStateOptions } from "./hooks";
|
|
19
22
|
import { StateInspectorOptions } from "./inspector/StateInspector";
|
|
20
23
|
|
|
@@ -28,6 +31,9 @@ export type AppData<State> = {
|
|
|
28
31
|
viewType: AppViewType;
|
|
29
32
|
initialState: State;
|
|
30
33
|
inspector: boolean | StateInspectorOptions;
|
|
34
|
+
userId?: string;
|
|
35
|
+
isDemo: boolean;
|
|
36
|
+
access?: AccessType;
|
|
31
37
|
};
|
|
32
38
|
|
|
33
39
|
export function createDefaultAppData<State>(
|
|
@@ -38,6 +44,8 @@ export function createDefaultAppData<State>(
|
|
|
38
44
|
viewType: "editable",
|
|
39
45
|
initialState,
|
|
40
46
|
inspector: false,
|
|
47
|
+
isDemo: false,
|
|
48
|
+
access: undefined,
|
|
41
49
|
};
|
|
42
50
|
}
|
|
43
51
|
|
|
@@ -93,6 +101,10 @@ export function getAppData<State>(
|
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
appData.initialState = enforceSchema(appData.initialState, schema) as State;
|
|
104
|
+
appData.isDemo = appData.isDemo ?? false;
|
|
105
|
+
const fallbackAccess: AccessType =
|
|
106
|
+
appData.viewType === "editable" ? "write" : "read";
|
|
107
|
+
appData.access = appData.access ?? fallbackAccess;
|
|
96
108
|
|
|
97
109
|
return appData as AppData<State>;
|
|
98
110
|
}
|
|
@@ -102,20 +114,32 @@ export function getSyncAdapter<
|
|
|
102
114
|
M extends object = object,
|
|
103
115
|
E extends object = object,
|
|
104
116
|
MenuT extends string = string,
|
|
117
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
105
118
|
>(params?: {
|
|
106
119
|
multiplayerUrl?: string;
|
|
107
120
|
offlineStorageKey?: string;
|
|
108
121
|
debug?: boolean;
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
policyAuthContext?: PolicyAuthContext;
|
|
123
|
+
serverScripts?: LocalStorageSyncOptions["serverScripts"];
|
|
124
|
+
}): SyncAdapter<S, M, E, MenuT, I> {
|
|
125
|
+
const {
|
|
126
|
+
multiplayerUrl,
|
|
127
|
+
offlineStorageKey,
|
|
128
|
+
debug,
|
|
129
|
+
policyAuthContext,
|
|
130
|
+
serverScripts,
|
|
131
|
+
} = params ?? {};
|
|
111
132
|
|
|
112
133
|
return isEmbeddedApp()
|
|
113
|
-
? parentFrameSync<S, M, E, MenuT>({ debug })
|
|
134
|
+
? parentFrameSync<S, M, E, MenuT, I>({ debug, policyAuthContext })
|
|
114
135
|
: multiplayerUrl
|
|
115
|
-
? webSocketSync<S, M, E, MenuT>({ url: multiplayerUrl, debug })
|
|
136
|
+
? webSocketSync<S, M, E, MenuT, I>({ url: multiplayerUrl, debug })
|
|
116
137
|
: offlineStorageKey
|
|
117
|
-
? localStorageSync<S, M, E, MenuT>({
|
|
118
|
-
|
|
138
|
+
? localStorageSync<S, M, E, MenuT, I>({
|
|
139
|
+
key: offlineStorageKey,
|
|
140
|
+
...(serverScripts ? { serverScripts } : {}),
|
|
141
|
+
})
|
|
142
|
+
: stubSync<S, M, E, MenuT, I>();
|
|
119
143
|
}
|
|
120
144
|
|
|
121
145
|
export type UseNoyaStateOptions<
|
|
@@ -123,7 +147,8 @@ export type UseNoyaStateOptions<
|
|
|
123
147
|
M extends object = object,
|
|
124
148
|
E extends object = object,
|
|
125
149
|
MenuT extends string = string,
|
|
126
|
-
|
|
150
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
151
|
+
> = UseMultiplayerStateOptions<S, M, E, MenuT, I> & {
|
|
127
152
|
offlineStorageKey?: string;
|
|
128
153
|
};
|
|
129
154
|
|
|
@@ -132,12 +157,14 @@ export type UseNoyaStateResult<
|
|
|
132
157
|
M extends object = object,
|
|
133
158
|
E extends object = object,
|
|
134
159
|
MenuT extends string = string,
|
|
160
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
135
161
|
> = [
|
|
136
|
-
ReturnType<typeof useBindNoyaManager<S, M, E, MenuT>>[0],
|
|
137
|
-
ReturnType<typeof useBindNoyaManager<S, M, E, MenuT>>[1],
|
|
138
|
-
ReturnType<typeof useBindNoyaManager<S, M, E, MenuT>>[2] & {
|
|
162
|
+
ReturnType<typeof useBindNoyaManager<S, M, E, MenuT, I>>[0],
|
|
163
|
+
ReturnType<typeof useBindNoyaManager<S, M, E, MenuT, I>>[1],
|
|
164
|
+
ReturnType<typeof useBindNoyaManager<S, M, E, MenuT, I>>[2] & {
|
|
139
165
|
theme: AppTheme;
|
|
140
166
|
viewType: AppViewType;
|
|
167
|
+
isDemo: boolean;
|
|
141
168
|
},
|
|
142
169
|
];
|
|
143
170
|
|
|
@@ -155,11 +182,13 @@ export function useNoyaStateInternal<
|
|
|
155
182
|
M extends object = object,
|
|
156
183
|
E extends object = object,
|
|
157
184
|
MenuT extends string = string,
|
|
158
|
-
|
|
185
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
186
|
+
O extends UseNoyaStateOptions<S, M, E, MenuT, I> = UseNoyaStateOptions<
|
|
159
187
|
S,
|
|
160
188
|
M,
|
|
161
189
|
E,
|
|
162
|
-
MenuT
|
|
190
|
+
MenuT,
|
|
191
|
+
I
|
|
163
192
|
>,
|
|
164
193
|
>(
|
|
165
194
|
...args:
|
|
@@ -174,7 +203,8 @@ export function useNoyaStateInternal<
|
|
|
174
203
|
O["schema"] extends TSchema ? Static<O["schema"]> : S,
|
|
175
204
|
M,
|
|
176
205
|
E,
|
|
177
|
-
MenuT
|
|
206
|
+
MenuT,
|
|
207
|
+
I
|
|
178
208
|
> {
|
|
179
209
|
const [initialState, options] =
|
|
180
210
|
typeof args[0] === "object" &&
|
|
@@ -192,6 +222,9 @@ export function useNoyaStateInternal<
|
|
|
192
222
|
initialState: noyaAppInitialState,
|
|
193
223
|
theme,
|
|
194
224
|
viewType,
|
|
225
|
+
userId,
|
|
226
|
+
isDemo,
|
|
227
|
+
access,
|
|
195
228
|
},
|
|
196
229
|
] = useState(() => {
|
|
197
230
|
return getAppData(initialState, options?.schema, {
|
|
@@ -199,19 +232,57 @@ export function useNoyaStateInternal<
|
|
|
199
232
|
});
|
|
200
233
|
});
|
|
201
234
|
|
|
202
|
-
const
|
|
203
|
-
|
|
235
|
+
const basePolicyAuthContext = useMemo((): PolicyAuthContext | undefined => {
|
|
236
|
+
if (!access && userId === undefined) return undefined;
|
|
237
|
+
|
|
238
|
+
const context: PolicyAuthContext = {};
|
|
239
|
+
|
|
240
|
+
if (access) {
|
|
241
|
+
context.access = access;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (userId !== undefined) {
|
|
245
|
+
context.id = userId ?? null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return context;
|
|
249
|
+
}, [access, userId]);
|
|
250
|
+
|
|
251
|
+
const sync = useMemo((): SyncAdapter<S, M, E, MenuT, I> => {
|
|
252
|
+
return getSyncAdapter<S, M, E, MenuT, I>({
|
|
204
253
|
multiplayerUrl,
|
|
205
254
|
offlineStorageKey: options?.offlineStorageKey,
|
|
206
255
|
debug: options?.debug,
|
|
256
|
+
policyAuthContext: basePolicyAuthContext,
|
|
207
257
|
});
|
|
208
|
-
}, [
|
|
258
|
+
}, [
|
|
259
|
+
multiplayerUrl,
|
|
260
|
+
options?.offlineStorageKey,
|
|
261
|
+
options?.debug,
|
|
262
|
+
basePolicyAuthContext,
|
|
263
|
+
]);
|
|
209
264
|
|
|
210
265
|
const [noyaManager] = useState(
|
|
211
|
-
() => new NoyaManager<S, M, E, MenuT>(noyaAppInitialState as S, options)
|
|
266
|
+
() => new NoyaManager<S, M, E, MenuT, I>(noyaAppInitialState as S, options)
|
|
212
267
|
);
|
|
213
268
|
|
|
214
|
-
|
|
269
|
+
useEffect(() => {
|
|
270
|
+
if (!userId) return;
|
|
271
|
+
|
|
272
|
+
noyaManager.sharedConnectionDataManager.setCurrentConnectionId(userId);
|
|
273
|
+
noyaManager.userManager.setCurrentConnectionId(userId);
|
|
274
|
+
noyaManager.userManager.setCurrentUserId(userId);
|
|
275
|
+
}, [noyaManager, userId]);
|
|
276
|
+
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
if (!basePolicyAuthContext) return;
|
|
279
|
+
|
|
280
|
+
noyaManager.multiplayerStateManager.setPolicyAuthContext(
|
|
281
|
+
basePolicyAuthContext
|
|
282
|
+
);
|
|
283
|
+
}, [noyaManager, basePolicyAuthContext]);
|
|
284
|
+
|
|
285
|
+
const result = useBindNoyaManager<S, M, E, MenuT, I>(noyaManager, {
|
|
215
286
|
inspector: options?.inspector ?? inspector,
|
|
216
287
|
sync: options?.sync ?? sync,
|
|
217
288
|
});
|
|
@@ -219,14 +290,15 @@ export function useNoyaStateInternal<
|
|
|
219
290
|
const [, , extras] = result;
|
|
220
291
|
|
|
221
292
|
const mergedExtras = useMemo(() => {
|
|
222
|
-
return { ...extras, theme, viewType } as const;
|
|
223
|
-
}, [extras, theme, viewType]);
|
|
293
|
+
return { ...extras, theme, viewType, isDemo } as const;
|
|
294
|
+
}, [extras, theme, viewType, isDemo]);
|
|
224
295
|
|
|
225
296
|
return [result[0], result[1], mergedExtras] as UseNoyaStateResult<
|
|
226
297
|
O["schema"] extends TSchema ? Static<O["schema"]> : S,
|
|
227
298
|
M,
|
|
228
299
|
E,
|
|
229
|
-
MenuT
|
|
300
|
+
MenuT,
|
|
301
|
+
I
|
|
230
302
|
>;
|
|
231
303
|
}
|
|
232
304
|
|
package/src/singleton.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import type { LocalStorageSyncOptions } from "@noya-app/state-manager";
|
|
3
4
|
import {
|
|
4
5
|
NoyaManager,
|
|
5
6
|
NoyaManagerOptions,
|
|
@@ -16,14 +17,16 @@ import {
|
|
|
16
17
|
getSyncAdapter,
|
|
17
18
|
} from "./noyaApp";
|
|
18
19
|
|
|
19
|
-
let noyaManagerSingleton: NoyaManager<any, any, any, any> | undefined;
|
|
20
|
+
let noyaManagerSingleton: NoyaManager<any, any, any, any, any> | undefined;
|
|
20
21
|
let noyaSingletonBindings: AnyNoyaSingletonBindings | undefined;
|
|
21
22
|
|
|
22
23
|
export type AnyNoyaSingletonBindings = {
|
|
23
|
-
sync?: SyncAdapter<any, any, any, any>;
|
|
24
|
+
sync?: SyncAdapter<any, any, any, any, any>;
|
|
24
25
|
inspector?: boolean | StateInspectorOptions;
|
|
25
26
|
theme: AppTheme;
|
|
26
27
|
viewType: AppViewType;
|
|
28
|
+
userId?: string;
|
|
29
|
+
isDemo: boolean;
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
export type AnyNoyaSingletonOptions<
|
|
@@ -31,14 +34,16 @@ export type AnyNoyaSingletonOptions<
|
|
|
31
34
|
M extends object,
|
|
32
35
|
E extends object,
|
|
33
36
|
MenuT extends string,
|
|
37
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
34
38
|
> = NoyaManagerOptions<S, M> & {
|
|
35
39
|
initialState?: S | (() => S);
|
|
36
|
-
sync?: SyncAdapter<S, M, E, MenuT>;
|
|
40
|
+
sync?: SyncAdapter<S, M, E, MenuT, I>;
|
|
37
41
|
inspector?: boolean | StateInspectorOptions;
|
|
38
42
|
theme?: AppTheme;
|
|
39
43
|
viewType?: AppViewType;
|
|
40
44
|
offlineStorageKey?: string;
|
|
41
45
|
multiplayerUrl?: string;
|
|
46
|
+
serverScripts?: LocalStorageSyncOptions["serverScripts"];
|
|
42
47
|
};
|
|
43
48
|
|
|
44
49
|
export function initializeNoyaSingleton<
|
|
@@ -46,7 +51,8 @@ export function initializeNoyaSingleton<
|
|
|
46
51
|
M extends object,
|
|
47
52
|
E extends object,
|
|
48
53
|
MenuT extends string,
|
|
49
|
-
|
|
54
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
55
|
+
>(options?: AnyNoyaSingletonOptions<S, M, E, MenuT, I>) {
|
|
50
56
|
if (noyaManagerSingleton) {
|
|
51
57
|
throw new Error(
|
|
52
58
|
"Noya has already been initialized. Call `useNoyaState` instead."
|
|
@@ -61,8 +67,9 @@ export function initializeNoyaSingleton<
|
|
|
61
67
|
viewType,
|
|
62
68
|
offlineStorageKey,
|
|
63
69
|
multiplayerUrl,
|
|
70
|
+
serverScripts,
|
|
64
71
|
...managerOptions
|
|
65
|
-
} = (options ?? {}) as AnyNoyaSingletonOptions<S, M, E, MenuT>;
|
|
72
|
+
} = (options ?? {}) as AnyNoyaSingletonOptions<S, M, E, MenuT, I>;
|
|
66
73
|
|
|
67
74
|
const appData = getAppData<S>(
|
|
68
75
|
(initialState ?? null) as S | (() => S),
|
|
@@ -70,17 +77,37 @@ export function initializeNoyaSingleton<
|
|
|
70
77
|
{ overrideExistingState: managerOptions?.overrideExistingState }
|
|
71
78
|
);
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
const policyAuthContext =
|
|
81
|
+
appData.access || appData.userId !== undefined
|
|
82
|
+
? {
|
|
83
|
+
...(appData.access ? { access: appData.access } : {}),
|
|
84
|
+
...(appData.userId !== undefined
|
|
85
|
+
? { id: appData.userId ?? null }
|
|
86
|
+
: {}),
|
|
87
|
+
}
|
|
88
|
+
: undefined;
|
|
89
|
+
|
|
90
|
+
noyaManagerSingleton = new NoyaManager<S, M, E, MenuT, I>(
|
|
74
91
|
appData.initialState as S,
|
|
75
92
|
managerOptions
|
|
76
93
|
);
|
|
77
94
|
|
|
95
|
+
if (appData.userId) {
|
|
96
|
+
noyaManagerSingleton.sharedConnectionDataManager.setCurrentConnectionId(
|
|
97
|
+
appData.userId
|
|
98
|
+
);
|
|
99
|
+
noyaManagerSingleton.userManager.setCurrentConnectionId(appData.userId);
|
|
100
|
+
noyaManagerSingleton.userManager.setCurrentUserId(appData.userId);
|
|
101
|
+
}
|
|
102
|
+
|
|
78
103
|
const resolvedSync =
|
|
79
104
|
sync ??
|
|
80
|
-
getSyncAdapter<S, M, E, MenuT>({
|
|
105
|
+
getSyncAdapter<S, M, E, MenuT, I>({
|
|
81
106
|
multiplayerUrl: multiplayerUrl ?? appData.multiplayerUrl,
|
|
82
107
|
offlineStorageKey,
|
|
83
108
|
debug: managerOptions?.debug,
|
|
109
|
+
serverScripts,
|
|
110
|
+
policyAuthContext,
|
|
84
111
|
});
|
|
85
112
|
|
|
86
113
|
// Activate sync immediately for the singleton
|
|
@@ -108,6 +135,9 @@ export function initializeNoyaSingleton<
|
|
|
108
135
|
anchor={
|
|
109
136
|
resolvedInspector === true ? "bottom right" : resolvedInspector.anchor
|
|
110
137
|
}
|
|
138
|
+
advanced={
|
|
139
|
+
resolvedInspector === true ? false : resolvedInspector.advanced
|
|
140
|
+
}
|
|
111
141
|
/>
|
|
112
142
|
);
|
|
113
143
|
}
|
|
@@ -117,10 +147,18 @@ export function initializeNoyaSingleton<
|
|
|
117
147
|
inspector: resolvedInspector,
|
|
118
148
|
theme: resolvedTheme,
|
|
119
149
|
viewType: resolvedViewType,
|
|
150
|
+
userId: appData.userId,
|
|
151
|
+
isDemo: appData.isDemo,
|
|
120
152
|
};
|
|
121
153
|
}
|
|
122
154
|
|
|
123
|
-
export function getNoyaManagerSingleton()
|
|
155
|
+
export function getNoyaManagerSingleton(): NoyaManager<
|
|
156
|
+
any,
|
|
157
|
+
any,
|
|
158
|
+
any,
|
|
159
|
+
any,
|
|
160
|
+
any
|
|
161
|
+
> {
|
|
124
162
|
if (!noyaManagerSingleton || !noyaSingletonBindings) {
|
|
125
163
|
throw new Error(
|
|
126
164
|
"Noya has not been initialized. Call initializeNoya once before using Noya hooks."
|