@korajs/react 0.0.0-canary-20261008174129
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/LICENSE +21 -0
- package/README.md +78 -0
- package/dist/index.cjs +626 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +322 -0
- package/dist/index.d.ts +322 -0
- package/dist/index.js +595 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
// src/context/kora-context.ts
|
|
2
|
+
import { QueryStoreCache } from "@korajs/store";
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
createElement,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState
|
|
11
|
+
} from "react";
|
|
12
|
+
var KoraContext = createContext(null);
|
|
13
|
+
function KoraProvider({
|
|
14
|
+
app,
|
|
15
|
+
store,
|
|
16
|
+
syncEngine,
|
|
17
|
+
fallback,
|
|
18
|
+
children
|
|
19
|
+
}) {
|
|
20
|
+
const [resolvedStore, setResolvedStore] = useState(store ?? null);
|
|
21
|
+
const [resolvedSync, setResolvedSync] = useState(syncEngine ?? null);
|
|
22
|
+
const [ready, setReady] = useState(!app);
|
|
23
|
+
const [initError, setInitError] = useState(null);
|
|
24
|
+
const fallbackQueryStoreCache = useRef(null);
|
|
25
|
+
if (!fallbackQueryStoreCache.current) {
|
|
26
|
+
fallbackQueryStoreCache.current = new QueryStoreCache();
|
|
27
|
+
}
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!app) return;
|
|
30
|
+
let cancelled = false;
|
|
31
|
+
app.ready.then(() => {
|
|
32
|
+
if (cancelled) return;
|
|
33
|
+
setResolvedStore(app.getStore());
|
|
34
|
+
setResolvedSync(app.getSyncEngine());
|
|
35
|
+
setReady(true);
|
|
36
|
+
}).catch((error) => {
|
|
37
|
+
if (cancelled) return;
|
|
38
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
39
|
+
console.error("[Kora] Initialization failed:", err);
|
|
40
|
+
setInitError(err);
|
|
41
|
+
});
|
|
42
|
+
return () => {
|
|
43
|
+
cancelled = true;
|
|
44
|
+
};
|
|
45
|
+
}, [app]);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
return () => {
|
|
48
|
+
if (!app) {
|
|
49
|
+
fallbackQueryStoreCache.current?.clear();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}, [app]);
|
|
53
|
+
const contextValue = useMemo(() => {
|
|
54
|
+
if (!resolvedStore) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (fallbackQueryStoreCache.current === null) {
|
|
58
|
+
fallbackQueryStoreCache.current = new QueryStoreCache();
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
store: resolvedStore,
|
|
62
|
+
syncEngine: resolvedSync,
|
|
63
|
+
app: app ?? null,
|
|
64
|
+
events: app?.events ?? null,
|
|
65
|
+
subscribeSyncStatus: app?.sync?.subscribeStatus ?? null,
|
|
66
|
+
queryStoreCache: app && typeof app.getQueryStoreCache === "function" ? app.getQueryStoreCache() : fallbackQueryStoreCache.current
|
|
67
|
+
};
|
|
68
|
+
}, [resolvedStore, resolvedSync, app]);
|
|
69
|
+
if (initError) {
|
|
70
|
+
return createElement(
|
|
71
|
+
"div",
|
|
72
|
+
{ style: { color: "red", padding: "1rem", fontFamily: "monospace" } },
|
|
73
|
+
createElement("strong", null, "Kora initialization error: "),
|
|
74
|
+
initError.message
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (!ready) {
|
|
78
|
+
return fallback ?? null;
|
|
79
|
+
}
|
|
80
|
+
if (!contextValue) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'KoraProvider requires either an "app" or "store" prop. Pass a KoraApp from createApp() or a Store instance.'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
return createElement(KoraContext.Provider, { value: contextValue }, children);
|
|
86
|
+
}
|
|
87
|
+
function useKoraContext() {
|
|
88
|
+
const context = useContext(KoraContext);
|
|
89
|
+
if (context === null) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
"useKoraContext must be used within a <KoraProvider>. Wrap your component tree with <KoraProvider store={store}>."
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return context;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/context/auth-bound-kora-provider.ts
|
|
98
|
+
import { createElement as createElement2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
99
|
+
var SENSITIVE_METADATA_KEY = /token|secret|password|authorization|cookie/i;
|
|
100
|
+
function classifyKoraInitializationError(error) {
|
|
101
|
+
const candidate = error;
|
|
102
|
+
const message = typeof candidate?.message === "string" ? candidate.message : String(error);
|
|
103
|
+
const name = typeof candidate?.name === "string" ? candidate.name : "Error";
|
|
104
|
+
const cause = new Error(message);
|
|
105
|
+
cause.name = name;
|
|
106
|
+
if (typeof candidate?.stack === "string") cause.stack = candidate.stack;
|
|
107
|
+
let code = typeof candidate?.code === "string" ? candidate.code : "INITIALIZATION_FAILED";
|
|
108
|
+
if (code === "INITIALIZATION_FAILED" && cause.name === "QuotaExceededError") {
|
|
109
|
+
code = "STORAGE_QUOTA_EXCEEDED";
|
|
110
|
+
}
|
|
111
|
+
const metadata = {};
|
|
112
|
+
if (candidate?.context && typeof candidate.context === "object") {
|
|
113
|
+
for (const [key, value] of Object.entries(candidate.context)) {
|
|
114
|
+
if (SENSITIVE_METADATA_KEY.test(key)) continue;
|
|
115
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
116
|
+
metadata[key] = value;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { code, name: cause.name, message: cause.message, metadata, cause };
|
|
121
|
+
}
|
|
122
|
+
function AuthBoundKoraProvider({
|
|
123
|
+
authClient,
|
|
124
|
+
createApp,
|
|
125
|
+
signedOut = null,
|
|
126
|
+
locked,
|
|
127
|
+
fallback = null,
|
|
128
|
+
error: renderError,
|
|
129
|
+
children
|
|
130
|
+
}) {
|
|
131
|
+
const [app, setApp] = useState2(null);
|
|
132
|
+
const [state, setState] = useState2({ state: "loading" });
|
|
133
|
+
const [initError, setInitError] = useState2(null);
|
|
134
|
+
const active = useRef2(null);
|
|
135
|
+
const createAppRef = useRef2(createApp);
|
|
136
|
+
createAppRef.current = createApp;
|
|
137
|
+
const activeUserId = useRef2(null);
|
|
138
|
+
const generation = useRef2(0);
|
|
139
|
+
const transition = useRef2(Promise.resolve());
|
|
140
|
+
const reconcileRef = useRef2(() => {
|
|
141
|
+
});
|
|
142
|
+
useEffect2(() => {
|
|
143
|
+
let disposed = false;
|
|
144
|
+
const reconcile = () => {
|
|
145
|
+
const currentGeneration = ++generation.current;
|
|
146
|
+
transition.current = transition.current.catch(() => {
|
|
147
|
+
}).then(async () => {
|
|
148
|
+
setInitError(null);
|
|
149
|
+
const next = await authClient.resolveSyncState?.() ?? { state: "signed-out", mayConnectAnonymously: false };
|
|
150
|
+
if (disposed || currentGeneration !== generation.current) return;
|
|
151
|
+
const previous = active.current;
|
|
152
|
+
if (next.state === "authenticated" && next.userId === activeUserId.current && previous) {
|
|
153
|
+
setState(next);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
setApp(null);
|
|
157
|
+
active.current = null;
|
|
158
|
+
activeUserId.current = null;
|
|
159
|
+
if (previous) await previous.close();
|
|
160
|
+
if (disposed || currentGeneration !== generation.current) return;
|
|
161
|
+
setState(next);
|
|
162
|
+
if (next.state !== "authenticated") return;
|
|
163
|
+
const created = createAppRef.current(next);
|
|
164
|
+
try {
|
|
165
|
+
await created.ready;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
await created.close();
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
if (disposed || currentGeneration !== generation.current) {
|
|
171
|
+
await created.close();
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
active.current = created;
|
|
175
|
+
activeUserId.current = next.userId;
|
|
176
|
+
setApp(created);
|
|
177
|
+
}).catch((error) => {
|
|
178
|
+
if (!disposed && currentGeneration === generation.current) {
|
|
179
|
+
void authClient.resolveSyncState?.().then((failedSession) => {
|
|
180
|
+
if (!disposed && currentGeneration === generation.current && failedSession?.state === "authenticated") {
|
|
181
|
+
setInitError({
|
|
182
|
+
error: classifyKoraInitializationError(error),
|
|
183
|
+
session: { userId: failedSession.userId }
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
|
+
reconcileRef.current = reconcile;
|
|
191
|
+
reconcile();
|
|
192
|
+
const unsubscribe = authClient.subscribe?.(reconcile) ?? (() => {
|
|
193
|
+
});
|
|
194
|
+
return () => {
|
|
195
|
+
disposed = true;
|
|
196
|
+
generation.current++;
|
|
197
|
+
unsubscribe();
|
|
198
|
+
const previous = active.current;
|
|
199
|
+
active.current = null;
|
|
200
|
+
activeUserId.current = null;
|
|
201
|
+
if (previous) void previous.close();
|
|
202
|
+
};
|
|
203
|
+
}, [authClient]);
|
|
204
|
+
if (initError) {
|
|
205
|
+
if (renderError) {
|
|
206
|
+
return renderError({
|
|
207
|
+
error: initError.error,
|
|
208
|
+
session: initError.session,
|
|
209
|
+
retry: () => reconcileRef.current()
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return createElement2(
|
|
213
|
+
"div",
|
|
214
|
+
{ style: { color: "red", padding: "1rem", fontFamily: "monospace" } },
|
|
215
|
+
createElement2("strong", null, "Kora initialization error: "),
|
|
216
|
+
initError.error.message
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (state.state === "signed-out" || state.state === "anonymous") return signedOut;
|
|
220
|
+
if (state.state === "authenticated" && state.locked) return locked ?? signedOut;
|
|
221
|
+
if (!app) return fallback;
|
|
222
|
+
return createElement2(KoraProvider, { app, fallback }, children);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// src/hooks/use-app.ts
|
|
226
|
+
function useApp() {
|
|
227
|
+
const { app } = useKoraContext();
|
|
228
|
+
if (!app) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
'useApp() requires KoraProvider to be initialized with an "app" prop. Pass your createApp() result to <KoraProvider app={app}>.'
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
return app;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/hooks/use-query.ts
|
|
237
|
+
import { assertQueryReady, queryKey } from "@korajs/store";
|
|
238
|
+
import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState3, useSyncExternalStore } from "react";
|
|
239
|
+
var EMPTY_ARRAY = Object.freeze([]);
|
|
240
|
+
var noopSubscribe = (_onStoreChange) => {
|
|
241
|
+
return () => {
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
var getServerSnapshot = () => EMPTY_ARRAY;
|
|
245
|
+
var getNoError = () => null;
|
|
246
|
+
function readQueryError(queryStore) {
|
|
247
|
+
if (!queryStore) return null;
|
|
248
|
+
const channel = queryStore;
|
|
249
|
+
return typeof channel.getError === "function" ? channel.getError() : null;
|
|
250
|
+
}
|
|
251
|
+
function useQuerySubscription(query, options) {
|
|
252
|
+
const { queryStoreCache } = useKoraContext();
|
|
253
|
+
const enabled = options?.enabled !== false;
|
|
254
|
+
const descriptorKey = queryKey(query.getDescriptor());
|
|
255
|
+
const queryRef = useRef3(query);
|
|
256
|
+
queryRef.current = query;
|
|
257
|
+
const lastSnapshotRef = useRef3(EMPTY_ARRAY);
|
|
258
|
+
const [queryStore, setQueryStore] = useState3(null);
|
|
259
|
+
useEffect3(() => {
|
|
260
|
+
if (!enabled) {
|
|
261
|
+
setQueryStore(null);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const currentQuery = queryRef.current;
|
|
265
|
+
assertQueryReady(currentQuery);
|
|
266
|
+
const store = queryStoreCache.getOrCreate(currentQuery);
|
|
267
|
+
setQueryStore(store);
|
|
268
|
+
return () => {
|
|
269
|
+
queryStoreCache.release(currentQuery);
|
|
270
|
+
};
|
|
271
|
+
}, [descriptorKey, enabled, queryStoreCache]);
|
|
272
|
+
const subscribe = enabled && queryStore ? queryStore.subscribe : noopSubscribe;
|
|
273
|
+
const getSnapshot = useCallback(() => {
|
|
274
|
+
if (!enabled) {
|
|
275
|
+
lastSnapshotRef.current = EMPTY_ARRAY;
|
|
276
|
+
return lastSnapshotRef.current;
|
|
277
|
+
}
|
|
278
|
+
if (!queryStore || !queryStore.hasSnapshot()) {
|
|
279
|
+
return lastSnapshotRef.current;
|
|
280
|
+
}
|
|
281
|
+
const snapshot = queryStore.getSnapshot();
|
|
282
|
+
lastSnapshotRef.current = snapshot;
|
|
283
|
+
return snapshot;
|
|
284
|
+
}, [enabled, queryStore]);
|
|
285
|
+
const getError = useCallback(
|
|
286
|
+
() => enabled ? readQueryError(queryStore) : null,
|
|
287
|
+
[enabled, queryStore]
|
|
288
|
+
);
|
|
289
|
+
const data = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
290
|
+
const error = useSyncExternalStore(subscribe, getError, getNoError);
|
|
291
|
+
const ready = enabled && queryStore !== null && queryStore.hasSnapshot();
|
|
292
|
+
return { data, error, ready };
|
|
293
|
+
}
|
|
294
|
+
function useQuery(query, options) {
|
|
295
|
+
const { data, error } = useQuerySubscription(query, options);
|
|
296
|
+
if (error && options?.throwOnError !== false) {
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
return data;
|
|
300
|
+
}
|
|
301
|
+
function useQueryState(query, options) {
|
|
302
|
+
const { data, error, ready } = useQuerySubscription(query, options);
|
|
303
|
+
return useMemo2(() => ({ data, error, ready }), [data, error, ready]);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/hooks/create-kora-hooks.ts
|
|
307
|
+
import { useMemo as useMemo4 } from "react";
|
|
308
|
+
|
|
309
|
+
// src/hooks/use-mutation.ts
|
|
310
|
+
import { createMutationController } from "@korajs/core/bindings";
|
|
311
|
+
import { useCallback as useCallback2, useMemo as useMemo3, useRef as useRef5, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
312
|
+
|
|
313
|
+
// src/hooks/use-controller.ts
|
|
314
|
+
import { useEffect as useEffect4, useReducer, useRef as useRef4 } from "react";
|
|
315
|
+
var ORPHAN_REAP_MS = 1e3;
|
|
316
|
+
function useController(create, destroy, deps) {
|
|
317
|
+
const entryRef = useRef4(null);
|
|
318
|
+
const createRef = useRef4(create);
|
|
319
|
+
createRef.current = create;
|
|
320
|
+
const destroyRef = useRef4(destroy);
|
|
321
|
+
destroyRef.current = destroy;
|
|
322
|
+
const [version, bumpVersion] = useReducer((count) => count + 1, 0);
|
|
323
|
+
const handle = useRef4({
|
|
324
|
+
get: () => {
|
|
325
|
+
const current = entryRef.current;
|
|
326
|
+
if (current !== null && !current.reaped) {
|
|
327
|
+
return current.controller;
|
|
328
|
+
}
|
|
329
|
+
const entry = {
|
|
330
|
+
controller: createRef.current(),
|
|
331
|
+
claimed: false,
|
|
332
|
+
reaped: false
|
|
333
|
+
};
|
|
334
|
+
entryRef.current = entry;
|
|
335
|
+
setTimeout(() => {
|
|
336
|
+
if (!entry.claimed && !entry.reaped) {
|
|
337
|
+
entry.reaped = true;
|
|
338
|
+
destroyRef.current(entry.controller);
|
|
339
|
+
}
|
|
340
|
+
}, ORPHAN_REAP_MS);
|
|
341
|
+
return entry.controller;
|
|
342
|
+
},
|
|
343
|
+
peek: () => {
|
|
344
|
+
const current = entryRef.current;
|
|
345
|
+
return current?.claimed && !current.reaped ? current.controller : null;
|
|
346
|
+
}
|
|
347
|
+
}).current;
|
|
348
|
+
useEffect4(() => {
|
|
349
|
+
const current = entryRef.current;
|
|
350
|
+
if (current !== null && !current.reaped && !current.claimed) {
|
|
351
|
+
current.claimed = true;
|
|
352
|
+
} else if (current === null || current.reaped) {
|
|
353
|
+
entryRef.current = { controller: createRef.current(), claimed: true, reaped: false };
|
|
354
|
+
bumpVersion();
|
|
355
|
+
}
|
|
356
|
+
return () => {
|
|
357
|
+
const entry = entryRef.current;
|
|
358
|
+
entryRef.current = null;
|
|
359
|
+
if (entry !== null && !entry.reaped) {
|
|
360
|
+
entry.reaped = true;
|
|
361
|
+
destroyRef.current(entry.controller);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}, deps);
|
|
365
|
+
return { get: handle.get, peek: handle.peek, version };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/hooks/use-mutation.ts
|
|
369
|
+
var IDLE = Object.freeze({ isLoading: false, error: null });
|
|
370
|
+
var getServerSnapshot2 = () => IDLE;
|
|
371
|
+
function useMutation(mutationFn, options) {
|
|
372
|
+
const fnRef = useRef5(mutationFn);
|
|
373
|
+
fnRef.current = mutationFn;
|
|
374
|
+
const optionsRef = useRef5(options);
|
|
375
|
+
optionsRef.current = options;
|
|
376
|
+
const controller = useController(
|
|
377
|
+
() => createMutationController({
|
|
378
|
+
mutationFn: (...args) => fnRef.current(...args),
|
|
379
|
+
resolveOptions: () => optionsRef.current
|
|
380
|
+
}),
|
|
381
|
+
(instance) => instance.destroy(),
|
|
382
|
+
[]
|
|
383
|
+
);
|
|
384
|
+
const getController = controller.get;
|
|
385
|
+
const subscribe = useCallback2(
|
|
386
|
+
(onStoreChange) => getController().subscribe(onStoreChange),
|
|
387
|
+
[getController, controller.version]
|
|
388
|
+
);
|
|
389
|
+
const getSnapshot = useCallback2(() => getController().getSnapshot(), [getController]);
|
|
390
|
+
const state = useSyncExternalStore2(subscribe, getSnapshot, getServerSnapshot2);
|
|
391
|
+
const mutate = useCallback2((...args) => getController().mutate(...args), [getController]);
|
|
392
|
+
const mutateAsync = useCallback2(
|
|
393
|
+
(...args) => getController().mutateAsync(...args),
|
|
394
|
+
[getController]
|
|
395
|
+
);
|
|
396
|
+
const reset = useCallback2(() => getController().reset(), [getController]);
|
|
397
|
+
return useMemo3(
|
|
398
|
+
() => ({ mutate, mutateAsync, isLoading: state.isLoading, error: state.error, reset }),
|
|
399
|
+
[mutate, mutateAsync, reset, state.isLoading, state.error]
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// src/hooks/use-sync-status.ts
|
|
404
|
+
import { OFFLINE_SYNC_STATUS, createSyncStatusController } from "@korajs/sync";
|
|
405
|
+
import { useCallback as useCallback3, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
406
|
+
var getServerSnapshot3 = () => OFFLINE_SYNC_STATUS;
|
|
407
|
+
function useSyncStatus() {
|
|
408
|
+
const { syncEngine, subscribeSyncStatus, events } = useKoraContext();
|
|
409
|
+
const controller = useController(
|
|
410
|
+
() => createSyncStatusController({
|
|
411
|
+
syncEngine,
|
|
412
|
+
subscribeSyncStatus,
|
|
413
|
+
events: subscribeSyncStatus ? null : events
|
|
414
|
+
}),
|
|
415
|
+
(instance) => instance.destroy(),
|
|
416
|
+
[syncEngine, subscribeSyncStatus, events]
|
|
417
|
+
);
|
|
418
|
+
const getController = controller.get;
|
|
419
|
+
const subscribe = useCallback3(
|
|
420
|
+
(onStoreChange) => getController().subscribe(onStoreChange),
|
|
421
|
+
[getController, controller.version]
|
|
422
|
+
);
|
|
423
|
+
const peek = controller.peek;
|
|
424
|
+
const getSnapshot = useCallback3(
|
|
425
|
+
() => peek()?.getSnapshot() ?? OFFLINE_SYNC_STATUS,
|
|
426
|
+
[peek]
|
|
427
|
+
);
|
|
428
|
+
return useSyncExternalStore3(subscribe, getSnapshot, getServerSnapshot3);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/hooks/create-kora-hooks.ts
|
|
432
|
+
function createKoraHooks() {
|
|
433
|
+
function useTypedApp() {
|
|
434
|
+
return useApp();
|
|
435
|
+
}
|
|
436
|
+
function useTypedCollection(name) {
|
|
437
|
+
const { app, store } = useKoraContext();
|
|
438
|
+
return useMemo4(() => {
|
|
439
|
+
const collections = app?.collections;
|
|
440
|
+
const accessor = collections && name in collections ? collections[name] : store.collection(name);
|
|
441
|
+
return accessor;
|
|
442
|
+
}, [app, store, name]);
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
useApp: useTypedApp,
|
|
446
|
+
useCollection: useTypedCollection,
|
|
447
|
+
useQuery,
|
|
448
|
+
useQueryState,
|
|
449
|
+
useMutation,
|
|
450
|
+
useSyncStatus
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// src/hooks/use-collection.ts
|
|
455
|
+
import { useMemo as useMemo5 } from "react";
|
|
456
|
+
function useCollection(name) {
|
|
457
|
+
const { store } = useKoraContext();
|
|
458
|
+
return useMemo5(() => {
|
|
459
|
+
return store.collection(name);
|
|
460
|
+
}, [store, name]);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// src/hooks/use-rich-text.ts
|
|
464
|
+
import { asRichTextSyncEngine, createRichTextController } from "@korajs/store";
|
|
465
|
+
import { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo6, useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
466
|
+
function useRichText(collectionName, recordId, fieldName, options) {
|
|
467
|
+
const { store, syncEngine } = useKoraContext();
|
|
468
|
+
const collection = useMemo6(() => store.collection(collectionName), [store, collectionName]);
|
|
469
|
+
const controller = useController(
|
|
470
|
+
() => createRichTextController({
|
|
471
|
+
collection,
|
|
472
|
+
collectionName,
|
|
473
|
+
recordId,
|
|
474
|
+
fieldName,
|
|
475
|
+
store,
|
|
476
|
+
syncEngine: asRichTextSyncEngine(syncEngine),
|
|
477
|
+
useDocChannel: options?.useDocChannel,
|
|
478
|
+
user: options?.user
|
|
479
|
+
}),
|
|
480
|
+
(instance) => instance.destroy(),
|
|
481
|
+
[collection, collectionName, fieldName, options?.useDocChannel, recordId, store, syncEngine]
|
|
482
|
+
);
|
|
483
|
+
const getController = controller.get;
|
|
484
|
+
useEffect5(() => {
|
|
485
|
+
getController().setUser(options?.user);
|
|
486
|
+
}, [getController, options?.user, controller.version]);
|
|
487
|
+
const subscribe = useCallback4(
|
|
488
|
+
(onStoreChange) => getController().subscribe(onStoreChange),
|
|
489
|
+
[getController, controller.version]
|
|
490
|
+
);
|
|
491
|
+
const getSnapshot = useCallback4(() => getController().getSnapshot(), [getController]);
|
|
492
|
+
const snapshot = useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
|
|
493
|
+
const undo = useCallback4(() => {
|
|
494
|
+
getController().undo();
|
|
495
|
+
}, [getController]);
|
|
496
|
+
const redo = useCallback4(() => {
|
|
497
|
+
getController().redo();
|
|
498
|
+
}, [getController]);
|
|
499
|
+
const setCursor = useCallback4(
|
|
500
|
+
(anchor, head) => getController().setCursor(anchor, head),
|
|
501
|
+
[getController]
|
|
502
|
+
);
|
|
503
|
+
const clearCursor = useCallback4(() => getController().clearCursor(), [getController]);
|
|
504
|
+
const live = getController();
|
|
505
|
+
return useMemo6(
|
|
506
|
+
() => buildResult(live, snapshot, undo, redo, setCursor, clearCursor),
|
|
507
|
+
[live, snapshot, undo, redo, setCursor, clearCursor, controller.version]
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
function buildResult(controller, snapshot, undo, redo, setCursor, clearCursor) {
|
|
511
|
+
return {
|
|
512
|
+
doc: controller.doc,
|
|
513
|
+
text: controller.text,
|
|
514
|
+
undo,
|
|
515
|
+
redo,
|
|
516
|
+
canUndo: snapshot.canUndo,
|
|
517
|
+
canRedo: snapshot.canRedo,
|
|
518
|
+
ready: snapshot.ready,
|
|
519
|
+
error: snapshot.error,
|
|
520
|
+
cursors: [...snapshot.cursors],
|
|
521
|
+
setCursor,
|
|
522
|
+
clearCursor,
|
|
523
|
+
hasUnsavedChanges: snapshot.hasUnsavedChanges,
|
|
524
|
+
retrySave: () => controller.retrySave(),
|
|
525
|
+
getUnsavedState: () => controller.getUnsavedState()
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// src/hooks/use-presence.ts
|
|
530
|
+
import { useEffect as useEffect6 } from "react";
|
|
531
|
+
function usePresence(user) {
|
|
532
|
+
const { syncEngine } = useKoraContext();
|
|
533
|
+
const name = user?.name ?? null;
|
|
534
|
+
const color = user?.color ?? null;
|
|
535
|
+
const avatar = user?.avatar ?? null;
|
|
536
|
+
useEffect6(() => {
|
|
537
|
+
if (!syncEngine || !name || !color) return;
|
|
538
|
+
const awareness = syncEngine.getAwarenessManager();
|
|
539
|
+
awareness.setLocalState({
|
|
540
|
+
user: { name, color, avatar: avatar ?? void 0 }
|
|
541
|
+
});
|
|
542
|
+
return () => {
|
|
543
|
+
awareness.setLocalState(null);
|
|
544
|
+
};
|
|
545
|
+
}, [syncEngine, name, color, avatar]);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/hooks/use-collaborators.ts
|
|
549
|
+
import { subscribeRemoteAwarenessStates } from "@korajs/sync";
|
|
550
|
+
import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef6, useSyncExternalStore as useSyncExternalStore5 } from "react";
|
|
551
|
+
var EMPTY_ARRAY2 = [];
|
|
552
|
+
var getServerSnapshot4 = () => EMPTY_ARRAY2;
|
|
553
|
+
function useCollaborators() {
|
|
554
|
+
const { syncEngine } = useKoraContext();
|
|
555
|
+
const snapshotRef = useRef6(EMPTY_ARRAY2);
|
|
556
|
+
const subscribe = useCallback5(
|
|
557
|
+
(onStoreChange) => {
|
|
558
|
+
if (!syncEngine) {
|
|
559
|
+
snapshotRef.current = EMPTY_ARRAY2;
|
|
560
|
+
return () => {
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
const awareness = syncEngine.getAwarenessManager();
|
|
564
|
+
snapshotRef.current = EMPTY_ARRAY2;
|
|
565
|
+
return subscribeRemoteAwarenessStates(awareness, (states) => {
|
|
566
|
+
snapshotRef.current = states;
|
|
567
|
+
onStoreChange();
|
|
568
|
+
});
|
|
569
|
+
},
|
|
570
|
+
[syncEngine]
|
|
571
|
+
);
|
|
572
|
+
const getSnapshot = useCallback5(() => snapshotRef.current, []);
|
|
573
|
+
useEffect7(() => {
|
|
574
|
+
if (!syncEngine) {
|
|
575
|
+
snapshotRef.current = EMPTY_ARRAY2;
|
|
576
|
+
}
|
|
577
|
+
}, [syncEngine]);
|
|
578
|
+
return useSyncExternalStore5(subscribe, getSnapshot, getServerSnapshot4);
|
|
579
|
+
}
|
|
580
|
+
export {
|
|
581
|
+
AuthBoundKoraProvider,
|
|
582
|
+
KoraProvider,
|
|
583
|
+
classifyKoraInitializationError,
|
|
584
|
+
createKoraHooks,
|
|
585
|
+
useApp,
|
|
586
|
+
useCollaborators,
|
|
587
|
+
useCollection,
|
|
588
|
+
useMutation,
|
|
589
|
+
usePresence,
|
|
590
|
+
useQuery,
|
|
591
|
+
useQueryState,
|
|
592
|
+
useRichText,
|
|
593
|
+
useSyncStatus
|
|
594
|
+
};
|
|
595
|
+
//# sourceMappingURL=index.js.map
|