@noya-app/noya-multiplayer-react 0.1.71 → 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 +16 -0
- package/dist/index.bundle.js +42 -26
- package/dist/index.d.mts +69 -49
- package/dist/index.d.ts +69 -49
- package/dist/index.js +475 -253
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +418 -203
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/NoyaStateContext.tsx +148 -97
- package/src/__tests__/noyaApp.test.ts +6 -0
- package/src/__tests__/resourcePayload.test.ts +24 -134
- package/src/components/UserPointersOverlay.tsx +18 -16
- package/src/hooks.ts +19 -12
- package/src/inspector/StateInspector.tsx +84 -36
- package/src/inspector/sections/ServerScriptLogsSection.tsx +128 -0
- package/src/inspector/serialization.ts +4 -2
- package/src/inspector/useStateInspector.tsx +2 -1
- package/src/noyaApp.ts +78 -20
- package/src/singleton.tsx +33 -9
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,
|
|
@@ -30,6 +33,7 @@ export type AppData<State> = {
|
|
|
30
33
|
inspector: boolean | StateInspectorOptions;
|
|
31
34
|
userId?: string;
|
|
32
35
|
isDemo: boolean;
|
|
36
|
+
access?: AccessType;
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
export function createDefaultAppData<State>(
|
|
@@ -41,6 +45,7 @@ export function createDefaultAppData<State>(
|
|
|
41
45
|
initialState,
|
|
42
46
|
inspector: false,
|
|
43
47
|
isDemo: false,
|
|
48
|
+
access: undefined,
|
|
44
49
|
};
|
|
45
50
|
}
|
|
46
51
|
|
|
@@ -97,6 +102,9 @@ export function getAppData<State>(
|
|
|
97
102
|
|
|
98
103
|
appData.initialState = enforceSchema(appData.initialState, schema) as State;
|
|
99
104
|
appData.isDemo = appData.isDemo ?? false;
|
|
105
|
+
const fallbackAccess: AccessType =
|
|
106
|
+
appData.viewType === "editable" ? "write" : "read";
|
|
107
|
+
appData.access = appData.access ?? fallbackAccess;
|
|
100
108
|
|
|
101
109
|
return appData as AppData<State>;
|
|
102
110
|
}
|
|
@@ -106,20 +114,32 @@ export function getSyncAdapter<
|
|
|
106
114
|
M extends object = object,
|
|
107
115
|
E extends object = object,
|
|
108
116
|
MenuT extends string = string,
|
|
117
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
109
118
|
>(params?: {
|
|
110
119
|
multiplayerUrl?: string;
|
|
111
120
|
offlineStorageKey?: string;
|
|
112
121
|
debug?: boolean;
|
|
113
|
-
|
|
114
|
-
|
|
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 ?? {};
|
|
115
132
|
|
|
116
133
|
return isEmbeddedApp()
|
|
117
|
-
? parentFrameSync<S, M, E, MenuT>({ debug })
|
|
134
|
+
? parentFrameSync<S, M, E, MenuT, I>({ debug, policyAuthContext })
|
|
118
135
|
: multiplayerUrl
|
|
119
|
-
? webSocketSync<S, M, E, MenuT>({ url: multiplayerUrl, debug })
|
|
136
|
+
? webSocketSync<S, M, E, MenuT, I>({ url: multiplayerUrl, debug })
|
|
120
137
|
: offlineStorageKey
|
|
121
|
-
? localStorageSync<S, M, E, MenuT>({
|
|
122
|
-
|
|
138
|
+
? localStorageSync<S, M, E, MenuT, I>({
|
|
139
|
+
key: offlineStorageKey,
|
|
140
|
+
...(serverScripts ? { serverScripts } : {}),
|
|
141
|
+
})
|
|
142
|
+
: stubSync<S, M, E, MenuT, I>();
|
|
123
143
|
}
|
|
124
144
|
|
|
125
145
|
export type UseNoyaStateOptions<
|
|
@@ -127,7 +147,8 @@ export type UseNoyaStateOptions<
|
|
|
127
147
|
M extends object = object,
|
|
128
148
|
E extends object = object,
|
|
129
149
|
MenuT extends string = string,
|
|
130
|
-
|
|
150
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
151
|
+
> = UseMultiplayerStateOptions<S, M, E, MenuT, I> & {
|
|
131
152
|
offlineStorageKey?: string;
|
|
132
153
|
};
|
|
133
154
|
|
|
@@ -136,10 +157,11 @@ export type UseNoyaStateResult<
|
|
|
136
157
|
M extends object = object,
|
|
137
158
|
E extends object = object,
|
|
138
159
|
MenuT extends string = string,
|
|
160
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
139
161
|
> = [
|
|
140
|
-
ReturnType<typeof useBindNoyaManager<S, M, E, MenuT>>[0],
|
|
141
|
-
ReturnType<typeof useBindNoyaManager<S, M, E, MenuT>>[1],
|
|
142
|
-
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] & {
|
|
143
165
|
theme: AppTheme;
|
|
144
166
|
viewType: AppViewType;
|
|
145
167
|
isDemo: boolean;
|
|
@@ -160,11 +182,13 @@ export function useNoyaStateInternal<
|
|
|
160
182
|
M extends object = object,
|
|
161
183
|
E extends object = object,
|
|
162
184
|
MenuT extends string = string,
|
|
163
|
-
|
|
185
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
186
|
+
O extends UseNoyaStateOptions<S, M, E, MenuT, I> = UseNoyaStateOptions<
|
|
164
187
|
S,
|
|
165
188
|
M,
|
|
166
189
|
E,
|
|
167
|
-
MenuT
|
|
190
|
+
MenuT,
|
|
191
|
+
I
|
|
168
192
|
>,
|
|
169
193
|
>(
|
|
170
194
|
...args:
|
|
@@ -179,7 +203,8 @@ export function useNoyaStateInternal<
|
|
|
179
203
|
O["schema"] extends TSchema ? Static<O["schema"]> : S,
|
|
180
204
|
M,
|
|
181
205
|
E,
|
|
182
|
-
MenuT
|
|
206
|
+
MenuT,
|
|
207
|
+
I
|
|
183
208
|
> {
|
|
184
209
|
const [initialState, options] =
|
|
185
210
|
typeof args[0] === "object" &&
|
|
@@ -199,6 +224,7 @@ export function useNoyaStateInternal<
|
|
|
199
224
|
viewType,
|
|
200
225
|
userId,
|
|
201
226
|
isDemo,
|
|
227
|
+
access,
|
|
202
228
|
},
|
|
203
229
|
] = useState(() => {
|
|
204
230
|
return getAppData(initialState, options?.schema, {
|
|
@@ -206,26 +232,57 @@ export function useNoyaStateInternal<
|
|
|
206
232
|
});
|
|
207
233
|
});
|
|
208
234
|
|
|
209
|
-
const
|
|
210
|
-
|
|
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>({
|
|
211
253
|
multiplayerUrl,
|
|
212
254
|
offlineStorageKey: options?.offlineStorageKey,
|
|
213
255
|
debug: options?.debug,
|
|
256
|
+
policyAuthContext: basePolicyAuthContext,
|
|
214
257
|
});
|
|
215
|
-
}, [
|
|
258
|
+
}, [
|
|
259
|
+
multiplayerUrl,
|
|
260
|
+
options?.offlineStorageKey,
|
|
261
|
+
options?.debug,
|
|
262
|
+
basePolicyAuthContext,
|
|
263
|
+
]);
|
|
216
264
|
|
|
217
265
|
const [noyaManager] = useState(
|
|
218
|
-
() => new NoyaManager<S, M, E, MenuT>(noyaAppInitialState as S, options)
|
|
266
|
+
() => new NoyaManager<S, M, E, MenuT, I>(noyaAppInitialState as S, options)
|
|
219
267
|
);
|
|
220
268
|
|
|
221
269
|
useEffect(() => {
|
|
222
270
|
if (!userId) return;
|
|
223
271
|
|
|
224
|
-
noyaManager.
|
|
272
|
+
noyaManager.sharedConnectionDataManager.setCurrentConnectionId(userId);
|
|
273
|
+
noyaManager.userManager.setCurrentConnectionId(userId);
|
|
225
274
|
noyaManager.userManager.setCurrentUserId(userId);
|
|
226
275
|
}, [noyaManager, userId]);
|
|
227
276
|
|
|
228
|
-
|
|
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, {
|
|
229
286
|
inspector: options?.inspector ?? inspector,
|
|
230
287
|
sync: options?.sync ?? sync,
|
|
231
288
|
});
|
|
@@ -240,7 +297,8 @@ export function useNoyaStateInternal<
|
|
|
240
297
|
O["schema"] extends TSchema ? Static<O["schema"]> : S,
|
|
241
298
|
M,
|
|
242
299
|
E,
|
|
243
|
-
MenuT
|
|
300
|
+
MenuT,
|
|
301
|
+
I
|
|
244
302
|
>;
|
|
245
303
|
}
|
|
246
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,11 +17,11 @@ 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;
|
|
@@ -33,14 +34,16 @@ export type AnyNoyaSingletonOptions<
|
|
|
33
34
|
M extends object,
|
|
34
35
|
E extends object,
|
|
35
36
|
MenuT extends string,
|
|
37
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
36
38
|
> = NoyaManagerOptions<S, M> & {
|
|
37
39
|
initialState?: S | (() => S);
|
|
38
|
-
sync?: SyncAdapter<S, M, E, MenuT>;
|
|
40
|
+
sync?: SyncAdapter<S, M, E, MenuT, I>;
|
|
39
41
|
inspector?: boolean | StateInspectorOptions;
|
|
40
42
|
theme?: AppTheme;
|
|
41
43
|
viewType?: AppViewType;
|
|
42
44
|
offlineStorageKey?: string;
|
|
43
45
|
multiplayerUrl?: string;
|
|
46
|
+
serverScripts?: LocalStorageSyncOptions["serverScripts"];
|
|
44
47
|
};
|
|
45
48
|
|
|
46
49
|
export function initializeNoyaSingleton<
|
|
@@ -48,7 +51,8 @@ export function initializeNoyaSingleton<
|
|
|
48
51
|
M extends object,
|
|
49
52
|
E extends object,
|
|
50
53
|
MenuT extends string,
|
|
51
|
-
|
|
54
|
+
I extends Record<string, any> = Record<string, unknown>,
|
|
55
|
+
>(options?: AnyNoyaSingletonOptions<S, M, E, MenuT, I>) {
|
|
52
56
|
if (noyaManagerSingleton) {
|
|
53
57
|
throw new Error(
|
|
54
58
|
"Noya has already been initialized. Call `useNoyaState` instead."
|
|
@@ -63,8 +67,9 @@ export function initializeNoyaSingleton<
|
|
|
63
67
|
viewType,
|
|
64
68
|
offlineStorageKey,
|
|
65
69
|
multiplayerUrl,
|
|
70
|
+
serverScripts,
|
|
66
71
|
...managerOptions
|
|
67
|
-
} = (options ?? {}) as AnyNoyaSingletonOptions<S, M, E, MenuT>;
|
|
72
|
+
} = (options ?? {}) as AnyNoyaSingletonOptions<S, M, E, MenuT, I>;
|
|
68
73
|
|
|
69
74
|
const appData = getAppData<S>(
|
|
70
75
|
(initialState ?? null) as S | (() => S),
|
|
@@ -72,24 +77,37 @@ export function initializeNoyaSingleton<
|
|
|
72
77
|
{ overrideExistingState: managerOptions?.overrideExistingState }
|
|
73
78
|
);
|
|
74
79
|
|
|
75
|
-
|
|
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>(
|
|
76
91
|
appData.initialState as S,
|
|
77
92
|
managerOptions
|
|
78
93
|
);
|
|
79
94
|
|
|
80
95
|
if (appData.userId) {
|
|
81
|
-
noyaManagerSingleton.
|
|
96
|
+
noyaManagerSingleton.sharedConnectionDataManager.setCurrentConnectionId(
|
|
82
97
|
appData.userId
|
|
83
98
|
);
|
|
99
|
+
noyaManagerSingleton.userManager.setCurrentConnectionId(appData.userId);
|
|
84
100
|
noyaManagerSingleton.userManager.setCurrentUserId(appData.userId);
|
|
85
101
|
}
|
|
86
102
|
|
|
87
103
|
const resolvedSync =
|
|
88
104
|
sync ??
|
|
89
|
-
getSyncAdapter<S, M, E, MenuT>({
|
|
105
|
+
getSyncAdapter<S, M, E, MenuT, I>({
|
|
90
106
|
multiplayerUrl: multiplayerUrl ?? appData.multiplayerUrl,
|
|
91
107
|
offlineStorageKey,
|
|
92
108
|
debug: managerOptions?.debug,
|
|
109
|
+
serverScripts,
|
|
110
|
+
policyAuthContext,
|
|
93
111
|
});
|
|
94
112
|
|
|
95
113
|
// Activate sync immediately for the singleton
|
|
@@ -134,7 +152,13 @@ export function initializeNoyaSingleton<
|
|
|
134
152
|
};
|
|
135
153
|
}
|
|
136
154
|
|
|
137
|
-
export function getNoyaManagerSingleton()
|
|
155
|
+
export function getNoyaManagerSingleton(): NoyaManager<
|
|
156
|
+
any,
|
|
157
|
+
any,
|
|
158
|
+
any,
|
|
159
|
+
any,
|
|
160
|
+
any
|
|
161
|
+
> {
|
|
138
162
|
if (!noyaManagerSingleton || !noyaSingletonBindings) {
|
|
139
163
|
throw new Error(
|
|
140
164
|
"Noya has not been initialized. Call initializeNoya once before using Noya hooks."
|