teamplay 0.5.0-alpha.12 → 0.5.0-alpha.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/orm/Compat/hooksCompat.d.ts +0 -11
- package/dist/orm/Compat/hooksCompat.js +2 -90
- package/dist/react/renderAttemptDestroyer.d.ts +4 -4
- package/dist/react/renderAttemptDestroyer.js +9 -9
- package/dist/react/useSub.d.ts +4 -4
- package/dist/react/useSub.js +15 -15
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export { default as sub } from './orm/sub.js';
|
|
|
57
57
|
export { default as useSub, useAsyncSub, setUseDeferredValue as __setUseDeferredValue, setDefaultDefer as __setDefaultDefer } from './react/useSub.js';
|
|
58
58
|
export { default as useSuspendMemo, useSuspendMemoByKey } from './react/useSuspendMemo.js';
|
|
59
59
|
export declare const observer: ObserverFunction;
|
|
60
|
-
export {
|
|
60
|
+
export { useBatch, useDoc, useDoc$, useBatchDoc, useBatchDoc$, useAsyncDoc, useAsyncDoc$, useQuery, useQuery$, useAsyncQuery, useAsyncQuery$, useBatchQuery, useBatchQuery$, useQueryIds, useBatchQueryIds, useAsyncQueryIds, useQueryDoc, useQueryDoc$, useBatchQueryDoc, useBatchQueryDoc$, useAsyncQueryDoc, useAsyncQueryDoc$ } from './orm/Compat/hooksCompat.js';
|
|
61
61
|
export { emit, useOn, useEmit } from './orm/Compat/eventsCompat.js';
|
|
62
62
|
export { useDidUpdate, useOnce, useSyncEffect } from './react/helpers.js';
|
|
63
63
|
export { connection, setConnection, getConnection, getDefaultFetchOnly, setDefaultFetchOnly, publicOnly, setPublicOnly } from './orm/connection.js';
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export { default as sub } from "./orm/sub.js";
|
|
|
18
18
|
export { default as useSub, useAsyncSub, setUseDeferredValue as __setUseDeferredValue, setDefaultDefer as __setDefaultDefer } from "./react/useSub.js";
|
|
19
19
|
export { default as useSuspendMemo, useSuspendMemoByKey } from "./react/useSuspendMemo.js";
|
|
20
20
|
export const observer = runtimeObserver;
|
|
21
|
-
export {
|
|
21
|
+
export { useBatch, useDoc, useDoc$, useBatchDoc, useBatchDoc$, useAsyncDoc, useAsyncDoc$, useQuery, useQuery$, useAsyncQuery, useAsyncQuery$, useBatchQuery, useBatchQuery$, useQueryIds, useBatchQueryIds, useAsyncQueryIds, useQueryDoc, useQueryDoc$, useBatchQueryDoc, useBatchQueryDoc$, useAsyncQueryDoc, useAsyncQueryDoc$ } from './orm/Compat/hooksCompat.js';
|
|
22
22
|
export { emit, useOn, useEmit } from './orm/Compat/eventsCompat.js';
|
|
23
23
|
export { useDidUpdate, useOnce, useSyncEffect } from "./react/helpers.js";
|
|
24
24
|
export { connection, setConnection, getConnection, getDefaultFetchOnly, setDefaultFetchOnly, publicOnly, setPublicOnly } from "./orm/connection.js";
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
export const useValue: any
|
|
2
|
-
export const useValue$: any
|
|
3
|
-
export const useModel: any
|
|
4
|
-
export const useLocal: any
|
|
5
|
-
export const useLocal$: any
|
|
6
|
-
export const useLocalDoc: any
|
|
7
|
-
export const useLocalDoc$: any
|
|
8
|
-
export const useSession: any
|
|
9
|
-
export const useSession$: any
|
|
10
|
-
export const usePage: any
|
|
11
|
-
export const usePage$: any
|
|
12
1
|
export const useBatch: any
|
|
13
2
|
export const useDoc: any
|
|
14
3
|
export const useDoc$: any
|
|
@@ -6,71 +6,6 @@ import { isCompatEnv } from '../compatEnv.js';
|
|
|
6
6
|
import { isQueryReady } from './queryReadiness.js';
|
|
7
7
|
const $root = getRootSignal({ rootId: GLOBAL_ROOT_ID, rootFunction: universal$ });
|
|
8
8
|
const emittedCompatWarnings = new Set();
|
|
9
|
-
// Hook-compatible wrapper around $() for compatibility mode.
|
|
10
|
-
export function useValue$(defaultValue) {
|
|
11
|
-
return $root(defaultValue);
|
|
12
|
-
}
|
|
13
|
-
// Returns [value, $signal] similar to useState, but backed by $().
|
|
14
|
-
export function useValue(defaultValue) {
|
|
15
|
-
const $sig = useValue$(defaultValue);
|
|
16
|
-
return [$sig.get(), $sig];
|
|
17
|
-
}
|
|
18
|
-
export function useModel(path) {
|
|
19
|
-
if (arguments.length === 0 || path == null)
|
|
20
|
-
return $root;
|
|
21
|
-
if (path && typeof path.path === 'function')
|
|
22
|
-
return path;
|
|
23
|
-
if (typeof path !== 'string')
|
|
24
|
-
throw Error('useModel() expects a string path or a signal');
|
|
25
|
-
const segments = path.split('.').filter(Boolean);
|
|
26
|
-
if (segments.length === 0)
|
|
27
|
-
return $root;
|
|
28
|
-
let $cursor = $root;
|
|
29
|
-
for (const segment of segments) {
|
|
30
|
-
$cursor = $cursor[segment];
|
|
31
|
-
}
|
|
32
|
-
return $cursor;
|
|
33
|
-
}
|
|
34
|
-
export function useLocal$(path) {
|
|
35
|
-
const resolvedPath = resolveLocalPath(path);
|
|
36
|
-
if (!resolvedPath)
|
|
37
|
-
return $root;
|
|
38
|
-
const segments = resolvedPath.split('.').filter(Boolean);
|
|
39
|
-
let $cursor = $root;
|
|
40
|
-
for (const segment of segments) {
|
|
41
|
-
$cursor = $cursor[segment];
|
|
42
|
-
}
|
|
43
|
-
return $cursor;
|
|
44
|
-
}
|
|
45
|
-
export function useLocal(path) {
|
|
46
|
-
const $sig = useLocal$(path);
|
|
47
|
-
return [$sig.get(), $sig];
|
|
48
|
-
}
|
|
49
|
-
export function useLocalDoc$(collection, id) {
|
|
50
|
-
if (collection == null)
|
|
51
|
-
throw Error('useLocalDoc() expects a collection name');
|
|
52
|
-
if (id == null)
|
|
53
|
-
return undefined;
|
|
54
|
-
return $root[collection][id];
|
|
55
|
-
}
|
|
56
|
-
export function useLocalDoc(collection, id) {
|
|
57
|
-
const $doc = useLocalDoc$(collection, id);
|
|
58
|
-
if (!$doc)
|
|
59
|
-
return [undefined, undefined];
|
|
60
|
-
return [$doc.get(), $doc];
|
|
61
|
-
}
|
|
62
|
-
export function useSession$(path) {
|
|
63
|
-
return useLocal$(prefixLocalPath('_session', path));
|
|
64
|
-
}
|
|
65
|
-
export function useSession(path) {
|
|
66
|
-
return useLocal(prefixLocalPath('_session', path));
|
|
67
|
-
}
|
|
68
|
-
export function usePage$(path) {
|
|
69
|
-
return useLocal$(prefixLocalPath('_page', path));
|
|
70
|
-
}
|
|
71
|
-
export function usePage(path) {
|
|
72
|
-
return useLocal(prefixLocalPath('_page', path));
|
|
73
|
-
}
|
|
74
9
|
export function useBatch() {
|
|
75
10
|
const promise = promiseBatcher.getPromiseAll();
|
|
76
11
|
if (promise)
|
|
@@ -256,29 +191,6 @@ export function useAsyncQueryDoc$(collection, query, options) {
|
|
|
256
191
|
const [, $doc] = useAsyncQueryDoc(collection, query, options);
|
|
257
192
|
return $doc;
|
|
258
193
|
}
|
|
259
|
-
function resolveLocalPath(path) {
|
|
260
|
-
if (path && typeof path.path === 'function')
|
|
261
|
-
return path.path();
|
|
262
|
-
if (typeof path === 'string')
|
|
263
|
-
return path;
|
|
264
|
-
if (path == null)
|
|
265
|
-
return '';
|
|
266
|
-
throw Error('useLocal() expects a string path or a signal');
|
|
267
|
-
}
|
|
268
|
-
function prefixLocalPath(prefix, path) {
|
|
269
|
-
if (path == null || path === '')
|
|
270
|
-
return prefix;
|
|
271
|
-
let resolved = path;
|
|
272
|
-
if (path && typeof path.path === 'function')
|
|
273
|
-
resolved = path.path();
|
|
274
|
-
if (typeof resolved !== 'string')
|
|
275
|
-
throw Error(`${prefix} hook expects a string path or a signal`);
|
|
276
|
-
if (resolved.startsWith(prefix + '.'))
|
|
277
|
-
return resolved;
|
|
278
|
-
if (resolved === prefix)
|
|
279
|
-
return resolved;
|
|
280
|
-
return `${prefix}.${resolved}`;
|
|
281
|
-
}
|
|
282
194
|
function getDocSignal(collection, id, hookName) {
|
|
283
195
|
if (typeof collection !== 'string') {
|
|
284
196
|
throw Error(`[${hookName}] collection must be a string. Got: ${collection}`);
|
|
@@ -335,7 +247,7 @@ function isExtraQuery(query) {
|
|
|
335
247
|
const BATCH_SUB_OPTIONS = Object.freeze({
|
|
336
248
|
async: false,
|
|
337
249
|
batch: true,
|
|
338
|
-
|
|
250
|
+
renderAttemptCleanup: true,
|
|
339
251
|
// Batch hooks are a hard suspense barrier. Deferred params can skip the barrier
|
|
340
252
|
// on route transitions and cause immediate reads from stale/empty local nodes.
|
|
341
253
|
defer: false
|
|
@@ -347,7 +259,7 @@ function normalizeSyncSubOptions(options) {
|
|
|
347
259
|
return {
|
|
348
260
|
...(options || {}),
|
|
349
261
|
async: false,
|
|
350
|
-
|
|
262
|
+
renderAttemptCleanup: true,
|
|
351
263
|
// Compat sync hooks are strict by design: no deferred snapshots between route/tab switches.
|
|
352
264
|
defer: false
|
|
353
265
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
type DestroyAttempt = () => unknown | Promise<unknown>;
|
|
2
2
|
declare class RenderAttemptDestroyer {
|
|
3
3
|
fns: DestroyAttempt[];
|
|
4
|
-
|
|
4
|
+
renderAttemptCleanupArmed: boolean;
|
|
5
5
|
suspenseGateArmed: boolean;
|
|
6
6
|
constructor();
|
|
7
|
-
add(fn: DestroyAttempt | undefined, {
|
|
8
|
-
|
|
7
|
+
add(fn: DestroyAttempt | undefined, { renderAttemptCleanup }?: {
|
|
8
|
+
renderAttemptCleanup?: boolean;
|
|
9
9
|
}): void;
|
|
10
|
-
|
|
10
|
+
armRenderAttemptCleanup(): void;
|
|
11
11
|
armSuspenseGate(): void;
|
|
12
12
|
consumeThenableHandling(): {
|
|
13
13
|
shouldKeepShellAlive: boolean;
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
class RenderAttemptDestroyer {
|
|
2
2
|
fns;
|
|
3
|
-
|
|
3
|
+
renderAttemptCleanupArmed;
|
|
4
4
|
suspenseGateArmed;
|
|
5
5
|
constructor() {
|
|
6
6
|
this.fns = [];
|
|
7
|
-
this.
|
|
7
|
+
this.renderAttemptCleanupArmed = false;
|
|
8
8
|
this.suspenseGateArmed = false;
|
|
9
9
|
}
|
|
10
|
-
add(fn, {
|
|
10
|
+
add(fn, { renderAttemptCleanup = false } = {}) {
|
|
11
11
|
if (typeof fn !== 'function')
|
|
12
12
|
return;
|
|
13
13
|
this.fns.push(fn);
|
|
14
|
-
if (
|
|
15
|
-
this.
|
|
14
|
+
if (renderAttemptCleanup)
|
|
15
|
+
this.renderAttemptCleanupArmed = true;
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
this.
|
|
17
|
+
armRenderAttemptCleanup() {
|
|
18
|
+
this.renderAttemptCleanupArmed = true;
|
|
19
19
|
}
|
|
20
20
|
armSuspenseGate() {
|
|
21
21
|
this.suspenseGateArmed = true;
|
|
22
22
|
}
|
|
23
23
|
consumeThenableHandling() {
|
|
24
|
-
const shouldRunAttemptCleanup = this.
|
|
24
|
+
const shouldRunAttemptCleanup = this.renderAttemptCleanupArmed && this.fns.length > 0;
|
|
25
25
|
const shouldKeepShellAlive = this.suspenseGateArmed || shouldRunAttemptCleanup;
|
|
26
26
|
let destroyAttempt;
|
|
27
27
|
if (shouldRunAttemptCleanup) {
|
|
@@ -39,7 +39,7 @@ class RenderAttemptDestroyer {
|
|
|
39
39
|
}
|
|
40
40
|
reset() {
|
|
41
41
|
this.fns.length = 0;
|
|
42
|
-
this.
|
|
42
|
+
this.renderAttemptCleanupArmed = false;
|
|
43
43
|
this.suspenseGateArmed = false;
|
|
44
44
|
}
|
|
45
45
|
}
|
package/dist/react/useSub.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export interface UseSubOptions {
|
|
|
7
7
|
defer?: boolean | number;
|
|
8
8
|
/** Batch Suspense promises across multiple subscriptions in one render attempt. */
|
|
9
9
|
batch?: boolean;
|
|
10
|
-
/** Enable
|
|
11
|
-
|
|
10
|
+
/** Enable cleanup for observer render attempts when a subscription suspends. */
|
|
11
|
+
renderAttemptCleanup?: boolean;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Subscribe to a document signal in React async mode.
|
|
@@ -112,8 +112,8 @@ export default function useSub<TDocument, TDocumentModel extends SignalModelCons
|
|
|
112
112
|
* @param options Subscription behavior options.
|
|
113
113
|
*/
|
|
114
114
|
export default function useSub<TOutput = unknown, TCollection extends string = string>(signal: AggregationFunction<TOutput, TCollection>, params?: AggregationParams, options?: UseSubOptions): SubResult<AggregationFunction<TOutput, TCollection>, AggregationParams | undefined>;
|
|
115
|
-
export declare function useSubDeferred(signal: unknown, params?: unknown, { async, defer, batch,
|
|
116
|
-
export declare function useSubClassic(signal: unknown, params?: unknown, { async, batch,
|
|
115
|
+
export declare function useSubDeferred(signal: unknown, params?: unknown, { async, defer, batch, renderAttemptCleanup }?: UseSubOptions): unknown;
|
|
116
|
+
export declare function useSubClassic(signal: unknown, params?: unknown, { async, batch, renderAttemptCleanup }?: UseSubOptions): unknown;
|
|
117
117
|
export declare function setTestThrottling(ms: number): void;
|
|
118
118
|
export declare function resetTestThrottling(): void;
|
|
119
119
|
export declare function setUseDeferredValue(value: boolean): void;
|
package/dist/react/useSub.js
CHANGED
|
@@ -27,12 +27,12 @@ function runUseSub(signal, params, options) {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
// version of sub() which works as a react hook and throws promise for Suspense
|
|
30
|
-
export function useSubDeferred(signal, params, { async = false, defer, batch = false,
|
|
30
|
+
export function useSubDeferred(signal, params, { async = false, defer, batch = false, renderAttemptCleanup = false } = {}) {
|
|
31
31
|
const $signalRef = useRef();
|
|
32
32
|
const componentId = useId();
|
|
33
33
|
const scheduleUpdate = useScheduleUpdate();
|
|
34
34
|
const observerDefer = useDefer();
|
|
35
|
-
if (
|
|
35
|
+
if (renderAttemptCleanup)
|
|
36
36
|
markCompatComponent(componentId);
|
|
37
37
|
if (batch)
|
|
38
38
|
promiseBatcher.activate();
|
|
@@ -52,8 +52,8 @@ export function useSubDeferred(signal, params, { async = false, defer, batch = f
|
|
|
52
52
|
// On resubscribe we keep rendering previous signal and refresh in background.
|
|
53
53
|
if (!hasPreviousSignal) {
|
|
54
54
|
promiseBatcher.add(promise);
|
|
55
|
-
if (
|
|
56
|
-
|
|
55
|
+
if (renderAttemptCleanup)
|
|
56
|
+
registerRenderAttemptCleanup(signal, params);
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
59
|
scheduleUpdate(promise);
|
|
@@ -71,8 +71,8 @@ export function useSubDeferred(signal, params, { async = false, defer, batch = f
|
|
|
71
71
|
scheduleUpdate(promise);
|
|
72
72
|
return $signalRef.current;
|
|
73
73
|
}
|
|
74
|
-
if (
|
|
75
|
-
|
|
74
|
+
if (renderAttemptCleanup)
|
|
75
|
+
registerRenderAttemptCleanup(signal, params);
|
|
76
76
|
throw promise;
|
|
77
77
|
// 2. if it's a signal, we save it into ref to make sure it's not garbage collected while component exists
|
|
78
78
|
}
|
|
@@ -85,12 +85,12 @@ export function useSubDeferred(signal, params, { async = false, defer, batch = f
|
|
|
85
85
|
}
|
|
86
86
|
// classic version which initially throws promise for Suspense
|
|
87
87
|
// but if we get a promise second time, we return the last signal and wait for promise to resolve
|
|
88
|
-
export function useSubClassic(signal, params, { async = false, batch = false,
|
|
88
|
+
export function useSubClassic(signal, params, { async = false, batch = false, renderAttemptCleanup = false } = {}) {
|
|
89
89
|
const id = executionContextTracker.newHookId();
|
|
90
90
|
const componentId = useId();
|
|
91
91
|
const cache = useCache(undefined);
|
|
92
92
|
const scheduleUpdate = useScheduleUpdate();
|
|
93
|
-
if (
|
|
93
|
+
if (renderAttemptCleanup)
|
|
94
94
|
markCompatComponent(componentId);
|
|
95
95
|
if (batch)
|
|
96
96
|
promiseBatcher.activate();
|
|
@@ -104,8 +104,8 @@ export function useSubClassic(signal, params, { async = false, batch = false, co
|
|
|
104
104
|
// On resubscribe we keep rendering previous signal and refresh in background.
|
|
105
105
|
if (!hasPreviousSignal) {
|
|
106
106
|
promiseBatcher.add(promise);
|
|
107
|
-
if (
|
|
108
|
-
|
|
107
|
+
if (renderAttemptCleanup)
|
|
108
|
+
registerRenderAttemptCleanup(signal, params);
|
|
109
109
|
}
|
|
110
110
|
else {
|
|
111
111
|
scheduleUpdate(promise);
|
|
@@ -126,8 +126,8 @@ export function useSubClassic(signal, params, { async = false, batch = false, co
|
|
|
126
126
|
scheduleUpdate(promise);
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
|
-
if (
|
|
130
|
-
|
|
129
|
+
if (renderAttemptCleanup)
|
|
130
|
+
registerRenderAttemptCleanup(signal, params);
|
|
131
131
|
// in regular mode we throw the promise to be caught by Suspense
|
|
132
132
|
// this way we guarantee that the signal with all the data
|
|
133
133
|
// will always be there when component is rendered
|
|
@@ -180,10 +180,10 @@ function isThenable(value) {
|
|
|
180
180
|
(typeof value === 'object' || typeof value === 'function') &&
|
|
181
181
|
typeof value.then === 'function';
|
|
182
182
|
}
|
|
183
|
-
function
|
|
184
|
-
//
|
|
183
|
+
function registerRenderAttemptCleanup(_signal, _params) {
|
|
184
|
+
// Legacy hooks don't build per-hook init objects like Racer.
|
|
185
185
|
// We still need a marker so trapRender can defer observer-shell cleanup
|
|
186
186
|
// only when a real attempt cleanup exists.
|
|
187
187
|
// This path must not arm suspense-gate keep-alive by itself.
|
|
188
|
-
renderAttemptDestroyer.
|
|
188
|
+
renderAttemptDestroyer.armRenderAttemptCleanup();
|
|
189
189
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "teamplay",
|
|
3
|
-
"version": "0.5.0-alpha.
|
|
3
|
+
"version": "0.5.0-alpha.14",
|
|
4
4
|
"description": "Full-stack signals ORM with multiplayer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -69,13 +69,13 @@
|
|
|
69
69
|
"dependencies": {
|
|
70
70
|
"@nx-js/observer-util": "^4.1.3",
|
|
71
71
|
"@startupjs/sharedb-mingo-memory": "^4.0.0-2",
|
|
72
|
-
"@teamplay/backend": "^0.5.0-alpha.
|
|
72
|
+
"@teamplay/backend": "^0.5.0-alpha.13",
|
|
73
73
|
"@teamplay/cache": "^0.5.0-alpha.7",
|
|
74
74
|
"@teamplay/channel": "^0.5.0-alpha.10",
|
|
75
75
|
"@teamplay/debug": "^0.5.0-alpha.7",
|
|
76
|
-
"@teamplay/schema": "^0.5.0-alpha.
|
|
76
|
+
"@teamplay/schema": "^0.5.0-alpha.13",
|
|
77
77
|
"@teamplay/utils": "^0.5.0-alpha.7",
|
|
78
|
-
"babel-plugin-teamplay": "^0.5.0-alpha.
|
|
78
|
+
"babel-plugin-teamplay": "^0.5.0-alpha.13",
|
|
79
79
|
"diff-match-patch": "^1.0.5",
|
|
80
80
|
"events": "^3.3.0",
|
|
81
81
|
"json0-ot-diff": "^1.1.2",
|
|
@@ -134,5 +134,5 @@
|
|
|
134
134
|
]
|
|
135
135
|
},
|
|
136
136
|
"license": "MIT",
|
|
137
|
-
"gitHead": "
|
|
137
|
+
"gitHead": "84185ddad8cdbac3d2dfcc48cff3588e5ab99630"
|
|
138
138
|
}
|