teamplay 0.5.0-alpha.9 → 0.5.0
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/README.md +3 -3
- package/dist/config.d.ts +2 -0
- package/dist/config.js +1 -0
- package/dist/connect/index.d.ts +1 -1
- package/dist/connect/index.js +6 -2
- package/dist/connect/offline/index.d.ts +1 -1
- package/dist/connect/offline/index.js +8 -4
- package/dist/connect/offline/react-native.d.ts +1 -1
- package/dist/connect/offline/web.d.ts +1 -1
- package/dist/connect/test.d.ts +1 -1
- package/dist/connect/test.js +5 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.js +6 -5
- package/dist/orm/$.js +1 -1
- package/dist/orm/Aggregation.d.ts +5 -3
- package/dist/orm/Aggregation.js +39 -15
- package/dist/orm/Doc.js +0 -55
- package/dist/orm/Query.d.ts +1 -0
- package/dist/orm/Query.js +25 -82
- package/dist/orm/Root.d.ts +4 -0
- package/dist/orm/Root.js +16 -0
- package/dist/orm/Signal.d.ts +0 -2
- package/dist/orm/Signal.js +1 -4
- package/dist/orm/SignalBase.d.ts +21 -1
- package/dist/orm/SignalBase.js +259 -56
- package/dist/orm/batchScheduler.d.ts +7 -7
- package/dist/orm/connection.d.ts +0 -4
- package/dist/orm/connection.js +0 -12
- package/dist/orm/dataTree.d.ts +12 -12
- package/dist/orm/dataTree.js +55 -107
- package/dist/orm/disposeRootContext.js +0 -14
- package/dist/orm/events.d.ts +6 -0
- package/dist/orm/events.js +48 -0
- package/dist/orm/getSignal.d.ts +1 -1
- package/dist/orm/getSignal.js +4 -33
- package/dist/orm/idFields.d.ts +10 -1
- package/dist/orm/idFields.js +102 -14
- package/dist/orm/index.d.ts +2 -0
- package/dist/orm/index.js +1 -0
- package/dist/orm/initModels.js +1 -1
- package/dist/orm/privateData.d.ts +7 -22
- package/dist/orm/privateData.js +20 -1
- package/dist/orm/queryReadiness.d.ts +13 -0
- package/dist/orm/{Compat/queryReadiness.js → queryReadiness.js} +10 -10
- package/dist/orm/reaction.d.ts +11 -0
- package/dist/orm/reaction.js +47 -0
- package/dist/orm/rootContext.d.ts +0 -16
- package/dist/orm/rootContext.js +0 -28
- package/dist/orm/signalMetadata.js +3 -3
- package/dist/orm/signalReads.js +3 -9
- package/dist/orm/signalStorageMutations.d.ts +0 -2
- package/dist/orm/signalStorageMutations.js +0 -9
- package/dist/orm/signalSymbols.js +1 -1
- package/dist/orm/signalValueMutations.d.ts +1 -1
- package/dist/orm/signalValueMutations.js +0 -3
- package/dist/orm/sub.d.ts +12 -7
- package/dist/orm/sub.js +87 -30
- package/dist/orm/subscriptionGcDelay.js +2 -6
- package/dist/react/convertToObserver.js +1 -4
- package/dist/react/promiseBatcher.js +1 -1
- package/dist/react/renderAttemptDestroyer.d.ts +0 -8
- package/dist/react/renderAttemptDestroyer.js +2 -28
- package/dist/react/trapRender.js +3 -3
- package/dist/react/useSub.d.ts +86 -5
- package/dist/react/useSub.js +191 -32
- package/dist/react/useSuspendMemo.js +1 -5
- package/dist/server.d.ts +1 -2
- package/dist/server.js +5 -3
- package/package.json +15 -13
- package/dist/orm/Compat/SignalCompat.d.ts +0 -3
- package/dist/orm/Compat/SignalCompat.js +0 -1267
- package/dist/orm/Compat/eventsCompat.d.ts +0 -3
- package/dist/orm/Compat/eventsCompat.js +0 -73
- package/dist/orm/Compat/hooksCompat.d.ts +0 -33
- package/dist/orm/Compat/hooksCompat.js +0 -360
- package/dist/orm/Compat/modelEvents.d.ts +0 -6
- package/dist/orm/Compat/modelEvents.js +0 -228
- package/dist/orm/Compat/queryReadiness.d.ts +0 -5
- package/dist/orm/Compat/refFallback.d.ts +0 -13
- package/dist/orm/Compat/refFallback.js +0 -65
- package/dist/orm/Compat/refRegistry.d.ts +0 -6
- package/dist/orm/Compat/refRegistry.js +0 -54
- package/dist/orm/Compat/silentContext.d.ts +0 -5
- package/dist/orm/Compat/silentContext.js +0 -48
- package/dist/orm/Compat/startStopCompat.d.ts +0 -3
- package/dist/orm/Compat/startStopCompat.js +0 -217
- package/dist/orm/compatEnv.d.ts +0 -1
- package/dist/orm/compatEnv.js +0 -4
- package/dist/react/compatComponentRegistry.d.ts +0 -4
- package/dist/react/compatComponentRegistry.js +0 -19
- /package/dist/orm/{Reaction.d.ts → reactionSubscriptions.d.ts} +0 -0
- /package/dist/orm/{Reaction.js → reactionSubscriptions.js} +0 -0
package/dist/orm/sub.js
CHANGED
|
@@ -3,9 +3,11 @@ import Signal, { SEGMENTS, isPublicCollectionSignal, isPublicDocumentSignal } fr
|
|
|
3
3
|
import { docSubscriptions } from './Doc.js';
|
|
4
4
|
import { querySubscriptions, getQuerySignal } from './Query.js';
|
|
5
5
|
import { aggregationSubscriptions, getAggregationSignal } from './Aggregation.js';
|
|
6
|
-
import { getRoot } from "./Root.js";
|
|
6
|
+
import { getRoot, ROOT_ID } from "./Root.js";
|
|
7
|
+
import { isRootContextClosed } from "./rootContext.js";
|
|
7
8
|
import isServer from "../utils/isServer.js";
|
|
8
|
-
|
|
9
|
+
const SUB_RECORDS = new WeakMap();
|
|
10
|
+
export default function sub($signal, params, options) {
|
|
9
11
|
// TODO: temporarily disable support for multiple subscriptions
|
|
10
12
|
// since this has to be properly cached using useDeferredSignal() in useSub()
|
|
11
13
|
// if (Array.isArray($signal)) {
|
|
@@ -16,26 +18,26 @@ export default function sub($signal, params) {
|
|
|
16
18
|
if (Array.isArray($signal))
|
|
17
19
|
throw Error('sub() does not support multiple subscriptions yet');
|
|
18
20
|
if (isRuntimePublicDocumentSignal($signal)) {
|
|
19
|
-
if (arguments.length >
|
|
21
|
+
if (arguments.length > 2) {
|
|
20
22
|
throw Error(ERRORS.subDocArguments($signal, ...Array.from(arguments).slice(1)));
|
|
21
23
|
}
|
|
22
|
-
return doc$($signal);
|
|
24
|
+
return doc$($signal, parseSubOptions(params));
|
|
23
25
|
}
|
|
24
26
|
else if (isRuntimePublicCollectionSignal($signal)) {
|
|
25
|
-
if (arguments.length
|
|
27
|
+
if (arguments.length < 2 || arguments.length > 3) {
|
|
26
28
|
throw Error(ERRORS.subQueryArguments($signal, params, ...Array.from(arguments).slice(2)));
|
|
27
29
|
}
|
|
28
|
-
return query$($signal, params);
|
|
30
|
+
return query$($signal, params, parseSubOptions(options));
|
|
29
31
|
}
|
|
30
32
|
else if (isClientAggregationFunction($signal)) {
|
|
31
|
-
return getAggregationFromFunction($signal, $signal.collection, params);
|
|
33
|
+
return getAggregationFromFunction($signal, $signal.collection, params, parseSubOptions(options));
|
|
32
34
|
}
|
|
33
35
|
else if (isAggregationHeader($signal)) {
|
|
34
36
|
const aggregationParams = {
|
|
35
37
|
$aggregationName: $signal.name,
|
|
36
38
|
$params: sanitizeAggregationParams(params)
|
|
37
39
|
};
|
|
38
|
-
return aggregation$($signal.collection, aggregationParams);
|
|
40
|
+
return aggregation$($signal.collection, aggregationParams, undefined, parseSubOptions(options));
|
|
39
41
|
}
|
|
40
42
|
else if (isAggregationFunction($signal)) {
|
|
41
43
|
if (isServer) {
|
|
@@ -46,7 +48,7 @@ export default function sub($signal, params) {
|
|
|
46
48
|
}
|
|
47
49
|
const aggregationParams = { ...serverParams };
|
|
48
50
|
delete aggregationParams.$collection;
|
|
49
|
-
return getAggregationFromFunction($signal, collection, aggregationParams);
|
|
51
|
+
return getAggregationFromFunction($signal, collection, aggregationParams, parseSubOptions(options));
|
|
50
52
|
}
|
|
51
53
|
else {
|
|
52
54
|
throw Error(ERRORS.gotAggregationFunction($signal));
|
|
@@ -59,7 +61,25 @@ export default function sub($signal, params) {
|
|
|
59
61
|
throw Error('Invalid args passed for sub()');
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
|
-
function
|
|
64
|
+
export function unsub($signal) {
|
|
65
|
+
if (!($signal instanceof Signal))
|
|
66
|
+
return;
|
|
67
|
+
const record = takeSubRecord($signal);
|
|
68
|
+
if (!record)
|
|
69
|
+
return;
|
|
70
|
+
record.disposed = true;
|
|
71
|
+
const $root = getRoot($signal);
|
|
72
|
+
const rootId = $root?.[ROOT_ID];
|
|
73
|
+
if (isRootContextClosed(rootId))
|
|
74
|
+
return;
|
|
75
|
+
if (record.kind === 'doc')
|
|
76
|
+
return docSubscriptions.unsubscribe($signal, { intent: record.intent });
|
|
77
|
+
if (record.kind === 'query')
|
|
78
|
+
return querySubscriptions.unsubscribe($signal, { intent: record.intent });
|
|
79
|
+
if (record.kind === 'aggregation')
|
|
80
|
+
return aggregationSubscriptions.unsubscribe($signal, { intent: record.intent });
|
|
81
|
+
}
|
|
82
|
+
function getAggregationFromFunction(fn, collection, params, subOptions) {
|
|
63
83
|
const aggregationParams = sanitizeAggregationParams(params);
|
|
64
84
|
let session;
|
|
65
85
|
if (isRecord(aggregationParams) && aggregationParams.$session) {
|
|
@@ -70,15 +90,13 @@ function getAggregationFromFunction(fn, collection, params) {
|
|
|
70
90
|
// should match the context in @teamplay/backend/features/serverAggregate.js
|
|
71
91
|
const context = { collection, session, isServer };
|
|
72
92
|
const result = fn(aggregationParams, context);
|
|
73
|
-
return aggregation$(collection, Array.isArray(result) ? { $aggregate: result } : result);
|
|
93
|
+
return aggregation$(collection, Array.isArray(result) ? { $aggregate: result } : result, undefined, subOptions);
|
|
74
94
|
}
|
|
75
|
-
function doc$($doc) {
|
|
76
|
-
const promise = docSubscriptions.subscribe($doc);
|
|
77
|
-
|
|
78
|
-
return $doc;
|
|
79
|
-
return new Promise(resolve => promise.then(() => resolve($doc)));
|
|
95
|
+
function doc$($doc, subOptions) {
|
|
96
|
+
const promise = docSubscriptions.subscribe($doc, { intent: subOptions.intent });
|
|
97
|
+
return returnSubscribedSignal($doc, 'doc', subOptions.intent, promise);
|
|
80
98
|
}
|
|
81
|
-
function query$($collection, params) {
|
|
99
|
+
function query$($collection, params, subOptions) {
|
|
82
100
|
const collectionName = $collection[SEGMENTS][0];
|
|
83
101
|
if (typeof collectionName !== 'string')
|
|
84
102
|
throw Error(ERRORS.queryCollectionName($collection));
|
|
@@ -86,20 +104,17 @@ function query$($collection, params) {
|
|
|
86
104
|
throw Error(ERRORS.queryParamsObject(collectionName, params));
|
|
87
105
|
const signalOptions = getQuerySignalOptions($collection);
|
|
88
106
|
const queryParams = params;
|
|
89
|
-
if (queryParams?.$aggregate || queryParams?.$aggregationName)
|
|
90
|
-
return aggregation$(collectionName, params, signalOptions);
|
|
107
|
+
if (queryParams?.$aggregate || queryParams?.$aggregationName) {
|
|
108
|
+
return aggregation$(collectionName, params, signalOptions, subOptions);
|
|
109
|
+
}
|
|
91
110
|
const $query = getQuerySignal(collectionName, params, signalOptions);
|
|
92
|
-
const promise = querySubscriptions.subscribe($query);
|
|
93
|
-
|
|
94
|
-
return $query;
|
|
95
|
-
return new Promise(resolve => promise.then(() => resolve($query)));
|
|
111
|
+
const promise = querySubscriptions.subscribe($query, { intent: subOptions.intent });
|
|
112
|
+
return returnSubscribedSignal($query, 'query', subOptions.intent, promise);
|
|
96
113
|
}
|
|
97
|
-
function aggregation$(collectionName, params, signalOptions) {
|
|
114
|
+
function aggregation$(collectionName, params, signalOptions, subOptions = parseSubOptions()) {
|
|
98
115
|
const $aggregationQuery = getAggregationSignal(collectionName, params, signalOptions);
|
|
99
|
-
const promise = aggregationSubscriptions.subscribe($aggregationQuery);
|
|
100
|
-
|
|
101
|
-
return $aggregationQuery;
|
|
102
|
-
return new Promise(resolve => promise.then(() => resolve($aggregationQuery)));
|
|
116
|
+
const promise = aggregationSubscriptions.subscribe($aggregationQuery, { intent: subOptions.intent });
|
|
117
|
+
return returnSubscribedSignal($aggregationQuery, 'aggregation', subOptions.intent, promise);
|
|
103
118
|
}
|
|
104
119
|
function api$(_fn, _args) {
|
|
105
120
|
throw Error('sub() for async functions is not implemented yet');
|
|
@@ -133,14 +148,56 @@ function isRuntimePublicCollectionSignal(value) {
|
|
|
133
148
|
function isApiFunction(value) {
|
|
134
149
|
return typeof value === 'function' && !(value instanceof Signal);
|
|
135
150
|
}
|
|
151
|
+
function parseSubOptions(options) {
|
|
152
|
+
const mode = options?.mode ?? 'auto';
|
|
153
|
+
if (mode !== 'auto' && mode !== 'fetch' && mode !== 'subscribe') {
|
|
154
|
+
throw Error(`sub() option mode must be "auto", "fetch", or "subscribe". Got: ${mode}`);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
intent: mode === 'fetch' ? 'fetch' : 'subscribe'
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function returnSubscribedSignal($signal, kind, intent, promise) {
|
|
161
|
+
if (!promise) {
|
|
162
|
+
addSubRecord($signal, kind, intent);
|
|
163
|
+
return $signal;
|
|
164
|
+
}
|
|
165
|
+
return promise.then(() => {
|
|
166
|
+
addSubRecord($signal, kind, intent);
|
|
167
|
+
return $signal;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function addSubRecord($signal, kind, intent) {
|
|
171
|
+
let records = SUB_RECORDS.get($signal);
|
|
172
|
+
if (!records) {
|
|
173
|
+
records = [];
|
|
174
|
+
SUB_RECORDS.set($signal, records);
|
|
175
|
+
}
|
|
176
|
+
records.push({ kind, intent, disposed: false });
|
|
177
|
+
}
|
|
178
|
+
function takeSubRecord($signal) {
|
|
179
|
+
const records = SUB_RECORDS.get($signal);
|
|
180
|
+
if (!records)
|
|
181
|
+
return;
|
|
182
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
183
|
+
const record = records[i];
|
|
184
|
+
if (record.disposed)
|
|
185
|
+
continue;
|
|
186
|
+
records.splice(i, 1);
|
|
187
|
+
if (records.length === 0)
|
|
188
|
+
SUB_RECORDS.delete($signal);
|
|
189
|
+
return record;
|
|
190
|
+
}
|
|
191
|
+
SUB_RECORDS.delete($signal);
|
|
192
|
+
}
|
|
136
193
|
const ERRORS = {
|
|
137
194
|
subDocArguments: ($signal, ...args) => `
|
|
138
|
-
sub($doc) accepts
|
|
195
|
+
sub($doc) accepts one or two arguments - the document signal and optional options.
|
|
139
196
|
Doc: ${$signal[SEGMENTS]}
|
|
140
197
|
Got args: ${[$signal, ...args]}
|
|
141
198
|
`,
|
|
142
199
|
subQueryArguments: ($signal, params, ...args) => `
|
|
143
|
-
sub($collection, params) accepts
|
|
200
|
+
sub($collection, params) accepts two or three arguments - the collection signal, query params, and optional options.
|
|
144
201
|
If you want to subscribe to all documents in a collection, pass an empty object: sub($collection, {}).
|
|
145
202
|
Collection: ${$signal[SEGMENTS]}
|
|
146
203
|
Params: ${params}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
const DEFAULT_COMPAT_SUBSCRIPTION_GC_DELAY = 3000;
|
|
3
|
-
const DEFAULT_SUBSCRIPTION_GC_DELAY = 0;
|
|
1
|
+
const DEFAULT_SUBSCRIPTION_GC_DELAY = 3000;
|
|
4
2
|
let subscriptionGcDelay = getDefaultSubscriptionGcDelay();
|
|
5
3
|
export function getSubscriptionGcDelay() {
|
|
6
4
|
return subscriptionGcDelay;
|
|
@@ -17,9 +15,7 @@ export function setSubscriptionGcDelay(ms) {
|
|
|
17
15
|
return subscriptionGcDelay;
|
|
18
16
|
}
|
|
19
17
|
export function getDefaultSubscriptionGcDelay() {
|
|
20
|
-
return
|
|
21
|
-
? DEFAULT_COMPAT_SUBSCRIPTION_GC_DELAY
|
|
22
|
-
: DEFAULT_SUBSCRIPTION_GC_DELAY;
|
|
18
|
+
return DEFAULT_SUBSCRIPTION_GC_DELAY;
|
|
23
19
|
}
|
|
24
20
|
export function __resetSubscriptionGcDelayForTests() {
|
|
25
21
|
subscriptionGcDelay = getDefaultSubscriptionGcDelay();
|
|
@@ -7,7 +7,6 @@ import executionContextTracker from "./executionContextTracker.js";
|
|
|
7
7
|
import { pipeComponentMeta, useUnmount, useId, useTriggerUpdate } from "./helpers.js";
|
|
8
8
|
import trapRender from './trapRender.js';
|
|
9
9
|
import { scheduleReaction } from '../orm/batchScheduler.js';
|
|
10
|
-
import { isCompatComponent, unmarkCompatComponent } from "./compatComponentRegistry.js";
|
|
11
10
|
const DEFAULT_THROTTLE_TIMEOUT = 100;
|
|
12
11
|
export default function convertToObserver(BaseComponent, { forwardRef, cache: enableCache = true, throttle, ...options } = {}) {
|
|
13
12
|
throttle = normalizeThrottle(throttle);
|
|
@@ -33,7 +32,7 @@ export default function convertToObserver(BaseComponent, { forwardRef, cache: en
|
|
|
33
32
|
hasDeferredUpdateAfterExecutionContext = false;
|
|
34
33
|
triggerUpdate();
|
|
35
34
|
}
|
|
36
|
-
else
|
|
35
|
+
else {
|
|
37
36
|
if (hasDeferredUpdateAfterExecutionContext)
|
|
38
37
|
return;
|
|
39
38
|
hasDeferredUpdateAfterExecutionContext = true;
|
|
@@ -53,7 +52,6 @@ export default function convertToObserver(BaseComponent, { forwardRef, cache: en
|
|
|
53
52
|
if (!reactionRef.current)
|
|
54
53
|
throw Error(`NO REACTION REF - ${where}`);
|
|
55
54
|
destroyRef.current = undefined;
|
|
56
|
-
unmarkCompatComponent(componentId);
|
|
57
55
|
unobserve(reactionRef.current);
|
|
58
56
|
reactionRef.current = undefined;
|
|
59
57
|
destroyCache(where);
|
|
@@ -71,7 +69,6 @@ export default function convertToObserver(BaseComponent, { forwardRef, cache: en
|
|
|
71
69
|
}
|
|
72
70
|
// clean up observer on unmount
|
|
73
71
|
useUnmount(() => {
|
|
74
|
-
unmarkCompatComponent(componentId);
|
|
75
72
|
destroyRef.current?.('useUnmount()');
|
|
76
73
|
});
|
|
77
74
|
return reactionRef.current(...args);
|
|
@@ -95,7 +95,7 @@ function warnAboutChecksDelay(checks) {
|
|
|
95
95
|
state
|
|
96
96
|
};
|
|
97
97
|
});
|
|
98
|
-
console.warn('[teamplay]
|
|
98
|
+
console.warn('[teamplay] useBatchSub() is waiting for data materialization checks.', details);
|
|
99
99
|
}
|
|
100
100
|
function isDevMode() {
|
|
101
101
|
const processLike = globalThis.process;
|
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
type DestroyAttempt = () => unknown | Promise<unknown>;
|
|
2
1
|
declare class RenderAttemptDestroyer {
|
|
3
|
-
fns: DestroyAttempt[];
|
|
4
|
-
compatAttemptCleanupArmed: boolean;
|
|
5
2
|
suspenseGateArmed: boolean;
|
|
6
3
|
constructor();
|
|
7
|
-
add(fn: DestroyAttempt | undefined, { compat }?: {
|
|
8
|
-
compat?: boolean;
|
|
9
|
-
}): void;
|
|
10
|
-
armCompatAttemptCleanup(): void;
|
|
11
4
|
armSuspenseGate(): void;
|
|
12
5
|
consumeThenableHandling(): {
|
|
13
6
|
shouldKeepShellAlive: boolean;
|
|
14
|
-
destroyAttempt?: () => Promise<void>;
|
|
15
7
|
};
|
|
16
8
|
reset(): void;
|
|
17
9
|
}
|
|
@@ -1,45 +1,19 @@
|
|
|
1
1
|
class RenderAttemptDestroyer {
|
|
2
|
-
fns;
|
|
3
|
-
compatAttemptCleanupArmed;
|
|
4
2
|
suspenseGateArmed;
|
|
5
3
|
constructor() {
|
|
6
|
-
this.fns = [];
|
|
7
|
-
this.compatAttemptCleanupArmed = false;
|
|
8
4
|
this.suspenseGateArmed = false;
|
|
9
5
|
}
|
|
10
|
-
add(fn, { compat = false } = {}) {
|
|
11
|
-
if (typeof fn !== 'function')
|
|
12
|
-
return;
|
|
13
|
-
this.fns.push(fn);
|
|
14
|
-
if (compat)
|
|
15
|
-
this.compatAttemptCleanupArmed = true;
|
|
16
|
-
}
|
|
17
|
-
armCompatAttemptCleanup() {
|
|
18
|
-
this.compatAttemptCleanupArmed = true;
|
|
19
|
-
}
|
|
20
6
|
armSuspenseGate() {
|
|
21
7
|
this.suspenseGateArmed = true;
|
|
22
8
|
}
|
|
23
9
|
consumeThenableHandling() {
|
|
24
|
-
const
|
|
25
|
-
const shouldKeepShellAlive = this.suspenseGateArmed || shouldRunAttemptCleanup;
|
|
26
|
-
let destroyAttempt;
|
|
27
|
-
if (shouldRunAttemptCleanup) {
|
|
28
|
-
const fns = [...this.fns];
|
|
29
|
-
destroyAttempt = async () => {
|
|
30
|
-
await Promise.allSettled(fns.map(fn => fn()));
|
|
31
|
-
fns.length = 0;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
10
|
+
const shouldKeepShellAlive = this.suspenseGateArmed;
|
|
34
11
|
this.reset();
|
|
35
12
|
return {
|
|
36
|
-
shouldKeepShellAlive
|
|
37
|
-
destroyAttempt
|
|
13
|
+
shouldKeepShellAlive
|
|
38
14
|
};
|
|
39
15
|
}
|
|
40
16
|
reset() {
|
|
41
|
-
this.fns.length = 0;
|
|
42
|
-
this.compatAttemptCleanupArmed = false;
|
|
43
17
|
this.suspenseGateArmed = false;
|
|
44
18
|
}
|
|
45
19
|
}
|
package/dist/react/trapRender.js
CHANGED
|
@@ -13,7 +13,7 @@ export default function trapRender({ render, cache, destroy, componentId }) {
|
|
|
13
13
|
promiseBatcher.reset();
|
|
14
14
|
const res = render(...args);
|
|
15
15
|
if (isDevMode() && promiseBatcher.isActive()) {
|
|
16
|
-
throw Error('[teamplay]
|
|
16
|
+
throw Error('[teamplay] batch subscriptions were used without a closing useBatchSub() call or useSub(undefined, undefined, { batch: true }) call.');
|
|
17
17
|
}
|
|
18
18
|
return res;
|
|
19
19
|
}
|
|
@@ -24,9 +24,9 @@ export default function trapRender({ render, cache, destroy, componentId }) {
|
|
|
24
24
|
destroyed = true;
|
|
25
25
|
throw err;
|
|
26
26
|
}
|
|
27
|
-
const { shouldKeepShellAlive
|
|
27
|
+
const { shouldKeepShellAlive } = renderAttemptDestroyer.consumeThenableHandling();
|
|
28
28
|
if (shouldKeepShellAlive) {
|
|
29
|
-
throw Promise.resolve(err)
|
|
29
|
+
throw Promise.resolve(err);
|
|
30
30
|
}
|
|
31
31
|
// TODO: this might only be needed only if promise is thrown
|
|
32
32
|
// (check if useUnmount in convertToObserver is called if a regular error is thrown)
|
package/dist/react/useSub.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AggregationFunction, AggregationParams, ClientAggregationFunction } from '@teamplay/utils/aggregation';
|
|
2
|
-
import type
|
|
2
|
+
import { type CollectionSignal, type ComputedQueryParamsInput, type DocumentSignal, type QueryParams, type RegisteredAggregationInput, type SignalModelConstructor, type SubResult, type TypedAggregationInput, type TypedAggregationSignal, type WildcardSignalPath } from '../orm/Signal.js';
|
|
3
3
|
export interface UseSubOptions {
|
|
4
4
|
/** Return `undefined` while loading instead of throwing a Suspense promise. */
|
|
5
5
|
async?: boolean;
|
|
@@ -7,9 +7,14 @@ 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 compatibility cleanup for legacy observer render attempts. */
|
|
11
|
-
compatAttemptCleanup?: boolean;
|
|
12
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Subscribe to a document signal in React async mode.
|
|
13
|
+
* @param signal Document signal to subscribe to.
|
|
14
|
+
* @param params Must be omitted for document subscriptions.
|
|
15
|
+
* @param options Subscription behavior options.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useAsyncSub<TSignal extends DocumentSignal<any, any, any>>(signal: TSignal, options?: UseSubOptions): SubResult<TSignal>;
|
|
13
18
|
/**
|
|
14
19
|
* Subscribe to a document signal in React async mode.
|
|
15
20
|
* @param signal Document signal to subscribe to.
|
|
@@ -61,6 +66,82 @@ export declare function useAsyncSub<TDocument, TDocumentModel extends SignalMode
|
|
|
61
66
|
* @param options Subscription behavior options.
|
|
62
67
|
*/
|
|
63
68
|
export declare function useAsyncSub<TOutput = unknown, TCollection extends string = string>(signal: AggregationFunction<TOutput, TCollection>, params?: AggregationParams, options?: UseSubOptions): SubResult<AggregationFunction<TOutput, TCollection>, AggregationParams | undefined>;
|
|
69
|
+
/**
|
|
70
|
+
* Close a batch subscription barrier opened by previous `useBatchSub()` calls in this render.
|
|
71
|
+
*/
|
|
72
|
+
export declare function useBatchSub(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Subscribe to a document signal in React batch mode.
|
|
75
|
+
* @param signal Document signal to subscribe to.
|
|
76
|
+
* @param params Must be omitted for document subscriptions.
|
|
77
|
+
* @param options Subscription behavior options.
|
|
78
|
+
*/
|
|
79
|
+
export declare function useBatchSub<TSignal extends DocumentSignal<any, any, any>>(signal: TSignal, options?: UseSubOptions): SubResult<TSignal>;
|
|
80
|
+
/**
|
|
81
|
+
* Subscribe to a document signal in React batch mode.
|
|
82
|
+
* @param signal Document signal to subscribe to.
|
|
83
|
+
* @param params Must be omitted for document subscriptions.
|
|
84
|
+
* @param options Subscription behavior options.
|
|
85
|
+
*/
|
|
86
|
+
export declare function useBatchSub<TSignal extends DocumentSignal<any, any, any>>(signal: TSignal, params?: undefined, options?: UseSubOptions): SubResult<TSignal>;
|
|
87
|
+
/**
|
|
88
|
+
* Subscribe to a collection query in React batch mode.
|
|
89
|
+
* @param signal Collection signal to query.
|
|
90
|
+
* @param params Mongo-style query params, including filters and `$sort`.
|
|
91
|
+
* @param options Subscription behavior options.
|
|
92
|
+
*/
|
|
93
|
+
export declare function useBatchSub<TDocument, TCollectionModel extends SignalModelConstructor<TDocument[]>, TDocumentModel extends SignalModelConstructor<TDocument>, TCollectionPath extends WildcardSignalPath>(signal: CollectionSignal<TDocument, TCollectionModel, TDocumentModel, TCollectionPath>, params: QueryParams<TDocument>, options?: UseSubOptions): SubResult<CollectionSignal<TDocument, TCollectionModel, TDocumentModel, TCollectionPath>, QueryParams<TDocument>>;
|
|
94
|
+
/**
|
|
95
|
+
* Subscribe to a collection query with computed string keys in React batch mode.
|
|
96
|
+
* This fallback preserves Mongo-style computed paths such as `{ [`likes.${id}`]: true }`.
|
|
97
|
+
* Literal query objects should use the stricter overload above.
|
|
98
|
+
* @param signal Collection signal to query.
|
|
99
|
+
* @param params Mongo-style query params with a widened computed key.
|
|
100
|
+
* @param options Subscription behavior options.
|
|
101
|
+
*/
|
|
102
|
+
export declare function useBatchSub<TDocument, TCollectionModel extends SignalModelConstructor<TDocument[]>, TDocumentModel extends SignalModelConstructor<TDocument>, TCollectionPath extends WildcardSignalPath, TParams extends object>(signal: CollectionSignal<TDocument, TCollectionModel, TDocumentModel, TCollectionPath>, params: TParams & ComputedQueryParamsInput<TParams>, options?: UseSubOptions): SubResult<CollectionSignal<TDocument, TCollectionModel, TDocumentModel, TCollectionPath>, TParams>;
|
|
103
|
+
/**
|
|
104
|
+
* Subscribe to a registered collection aggregation in React batch mode.
|
|
105
|
+
* @param signal Aggregation header generated by StartupJS model loading.
|
|
106
|
+
* @param params Parameters passed to the aggregation.
|
|
107
|
+
* @param options Subscription behavior options.
|
|
108
|
+
*/
|
|
109
|
+
export declare function useBatchSub<TCollection extends string, TOutput = unknown>(signal: RegisteredAggregationInput<TCollection, TOutput>, params?: AggregationParams, options?: UseSubOptions): SubResult<RegisteredAggregationInput<TCollection, TOutput>>;
|
|
110
|
+
/**
|
|
111
|
+
* Subscribe to a client aggregation in React batch mode.
|
|
112
|
+
* @param signal Aggregation function created with `aggregation(collection, fn)`.
|
|
113
|
+
* @param params Parameters passed to the aggregation.
|
|
114
|
+
* @param options Subscription behavior options.
|
|
115
|
+
*/
|
|
116
|
+
export declare function useBatchSub<TOutput, TCollection extends string>(signal: ClientAggregationFunction<TOutput, TCollection>, params?: AggregationParams, options?: UseSubOptions): SubResult<ClientAggregationFunction<TOutput, TCollection>>;
|
|
117
|
+
/**
|
|
118
|
+
* Subscribe to an aggregation with explicit output typing in React batch mode.
|
|
119
|
+
* @param signal Typed aggregation input.
|
|
120
|
+
* @param params Parameters passed to the aggregation.
|
|
121
|
+
* @param options Subscription behavior options.
|
|
122
|
+
*/
|
|
123
|
+
export declare function useBatchSub<TDocument, TDocumentModel extends SignalModelConstructor<TDocument>>(signal: TypedAggregationInput<TDocument, TDocumentModel>, params?: AggregationParams, options?: UseSubOptions): TypedAggregationSignal<TDocument, TDocumentModel>;
|
|
124
|
+
/**
|
|
125
|
+
* Subscribe to an unregistered aggregation in React batch mode.
|
|
126
|
+
* @param signal Aggregation function.
|
|
127
|
+
* @param params Parameters passed to the aggregation.
|
|
128
|
+
* @param options Subscription behavior options.
|
|
129
|
+
*/
|
|
130
|
+
export declare function useBatchSub<TOutput = unknown, TCollection extends string = string>(signal: AggregationFunction<TOutput, TCollection>, params?: AggregationParams, options?: UseSubOptions): SubResult<AggregationFunction<TOutput, TCollection>, AggregationParams | undefined>;
|
|
131
|
+
/**
|
|
132
|
+
* Close a batch subscription barrier opened by previous `useSub(..., { batch: true })`
|
|
133
|
+
* calls in this render.
|
|
134
|
+
*/
|
|
135
|
+
export default function useSub(signal: undefined, params: undefined, options: UseSubOptions & {
|
|
136
|
+
batch: true;
|
|
137
|
+
}): void;
|
|
138
|
+
/**
|
|
139
|
+
* Subscribe to a document signal in React.
|
|
140
|
+
* @param signal Document signal to subscribe to.
|
|
141
|
+
* @param params Must be omitted for document subscriptions.
|
|
142
|
+
* @param options Subscription behavior options.
|
|
143
|
+
*/
|
|
144
|
+
export default function useSub<TSignal extends DocumentSignal<any, any, any>>(signal: TSignal, options?: UseSubOptions): SubResult<TSignal>;
|
|
64
145
|
/**
|
|
65
146
|
* Subscribe to a document signal in React.
|
|
66
147
|
* @param signal Document signal to subscribe to.
|
|
@@ -112,8 +193,8 @@ export default function useSub<TDocument, TDocumentModel extends SignalModelCons
|
|
|
112
193
|
* @param options Subscription behavior options.
|
|
113
194
|
*/
|
|
114
195
|
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
|
|
196
|
+
export declare function useSubDeferred(signal: unknown, params?: unknown, { async, defer, batch }?: UseSubOptions): unknown;
|
|
197
|
+
export declare function useSubClassic(signal: unknown, params?: unknown, { async, batch }?: UseSubOptions): unknown;
|
|
117
198
|
export declare function setTestThrottling(ms: number): void;
|
|
118
199
|
export declare function resetTestThrottling(): void;
|
|
119
200
|
export declare function setUseDeferredValue(value: boolean): void;
|