teamplay 0.4.0-alpha.57 → 0.4.0-alpha.59

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.
@@ -119,10 +119,11 @@ export function useAsyncDoc (collection, id, options) {
119
119
  }
120
120
 
121
121
  export function useQuery$ (collection, query, options) {
122
+ const normalizedQuery = normalizeQuery(query, 'useQuery')
122
123
  const $collection = getCollectionSignal(collection, query, 'useQuery')
123
124
  const normalizedOptions = normalizeSyncSubOptions(options)
124
- const $query = useSub($collection, normalizeQuery(query, 'useQuery'), normalizedOptions)
125
- return $query
125
+ const $query = useSub($collection, normalizedQuery, normalizedOptions)
126
+ return isExtraQuery(normalizedQuery) ? $query.extra : $query
126
127
  }
127
128
 
128
129
  export function useQuery (collection, query, options) {
@@ -133,9 +134,11 @@ export function useQuery (collection, query, options) {
133
134
  }
134
135
 
135
136
  export function useAsyncQuery$ (collection, query, options) {
137
+ const normalizedQuery = normalizeQuery(query, 'useAsyncQuery')
136
138
  const $collection = getCollectionSignal(collection, query, 'useAsyncQuery')
137
- const $query = useAsyncSub($collection, normalizeQuery(query, 'useAsyncQuery'), options)
138
- return $query
139
+ const $query = useAsyncSub($collection, normalizedQuery, options)
140
+ if (!$query) return $query
141
+ return isExtraQuery(normalizedQuery) ? $query.extra : $query
139
142
  }
140
143
 
141
144
  export function useAsyncQuery (collection, query, options) {
@@ -150,7 +153,9 @@ export function useBatchQuery$ (collection, query, _options) {
150
153
  const $collection = getCollectionSignal(collection, query, 'useBatchQuery')
151
154
  registerBatchQueryReadinessCheck(collection, normalizedQuery)
152
155
  const options = _options ? { ..._options, ...BATCH_SUB_OPTIONS } : BATCH_SUB_OPTIONS
153
- return useSub($collection, normalizedQuery, options)
156
+ const $query = useSub($collection, normalizedQuery, options)
157
+ if (!$query) return $query
158
+ return isExtraQuery(normalizedQuery) ? $query.extra : $query
154
159
  }
155
160
 
156
161
  export function useBatchQuery (collection, query, options) {
@@ -317,6 +322,15 @@ function normalizeQuery (query, hookName) {
317
322
  return query
318
323
  }
319
324
 
325
+ function isExtraQuery (query) {
326
+ if (!query || typeof query !== 'object') return false
327
+ return !!(
328
+ query.$count ||
329
+ query.$queryName ||
330
+ query.$aggregationName
331
+ )
332
+ }
333
+
320
334
  const BATCH_SUB_OPTIONS = Object.freeze({
321
335
  async: false,
322
336
  batch: true,
@@ -378,11 +392,20 @@ function registerBatchQueryReadinessCheck (collection, query) {
378
392
  const extraSegments = [QUERIES, hash, 'extra']
379
393
  const aggregationSegments = [AGGREGATIONS, hash]
380
394
  const isAggregate = Array.isArray(query.$aggregate)
395
+ const hasExtraResult = isExtraQuery(query)
381
396
  promiseBatcher.addCheck({
382
397
  key: `query:${hash}`,
383
- type: 'query',
384
- details: { collection, hash, query, isAggregate },
385
- isReady: () => isQueryReady(collection, idsSegments, docsSegments, extraSegments, aggregationSegments, isAggregate),
398
+ type: hasExtraResult ? 'queryExtra' : 'query',
399
+ details: { collection, hash, query, isAggregate, hasExtraResult },
400
+ isReady: () => isQueryReady(
401
+ collection,
402
+ idsSegments,
403
+ docsSegments,
404
+ extraSegments,
405
+ aggregationSegments,
406
+ isAggregate,
407
+ hasExtraResult
408
+ ),
386
409
  getState: () => {
387
410
  const ids = getRaw(idsSegments)
388
411
  const docs = getRaw(docsSegments)
@@ -404,7 +427,18 @@ function registerBatchQueryReadinessCheck (collection, query) {
404
427
  })
405
428
  }
406
429
 
407
- function isQueryReady (collection, idsSegments, docsSegments, extraSegments, aggregationSegments, isAggregate) {
430
+ function isQueryReady (
431
+ collection,
432
+ idsSegments,
433
+ docsSegments,
434
+ extraSegments,
435
+ aggregationSegments,
436
+ isAggregate,
437
+ hasExtraResult
438
+ ) {
439
+ if (hasExtraResult) {
440
+ return getRaw(extraSegments) !== undefined
441
+ }
408
442
  if (isAggregate) {
409
443
  const docs = getRaw(docsSegments)
410
444
  if (Array.isArray(docs)) return true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamplay",
3
- "version": "0.4.0-alpha.57",
3
+ "version": "0.4.0-alpha.59",
4
4
  "description": "Full-stack signals ORM with multiplayer",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -83,5 +83,5 @@
83
83
  ]
84
84
  },
85
85
  "license": "MIT",
86
- "gitHead": "701b0938bed619d75fefd080814987de92451638"
86
+ "gitHead": "c981ac82c249772524a9ccd5ccb4f6d437af4e4c"
87
87
  }
@@ -0,0 +1,20 @@
1
+ const compatComponentIds = new Set()
2
+
3
+ export function markCompatComponent (componentId) {
4
+ if (!componentId) return
5
+ compatComponentIds.add(componentId)
6
+ }
7
+
8
+ export function unmarkCompatComponent (componentId) {
9
+ if (!componentId) return
10
+ compatComponentIds.delete(componentId)
11
+ }
12
+
13
+ export function isCompatComponent (componentId) {
14
+ if (!componentId) return false
15
+ return compatComponentIds.has(componentId)
16
+ }
17
+
18
+ export function __resetCompatComponentRegistryForTests () {
19
+ compatComponentIds.clear()
20
+ }
@@ -7,6 +7,7 @@ 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'
10
11
 
11
12
  const DEFAULT_THROTTLE_TIMEOUT = 100
12
13
 
@@ -33,15 +34,29 @@ export default function convertToObserver (BaseComponent, {
33
34
  const reactionRef = useRef()
34
35
  const destroyRef = useRef()
35
36
  if (!reactionRef.current) {
37
+ let hasDeferredUpdateAfterExecutionContext = false
36
38
  let update = () => {
37
39
  // It's important to block updates caused by rendering itself
38
40
  // (when the sync rendering is in progress).
39
- if (!executionContextTracker.isActive()) triggerUpdate()
41
+ if (!executionContextTracker.isActive()) {
42
+ hasDeferredUpdateAfterExecutionContext = false
43
+ triggerUpdate()
44
+ } else if (isCompatComponent(componentId)) {
45
+ if (hasDeferredUpdateAfterExecutionContext) return
46
+ hasDeferredUpdateAfterExecutionContext = true
47
+ queueMicrotask(() => {
48
+ if (!hasDeferredUpdateAfterExecutionContext) return
49
+ if (executionContextTracker.isActive()) return
50
+ hasDeferredUpdateAfterExecutionContext = false
51
+ update()
52
+ })
53
+ }
40
54
  }
41
55
  if (throttle) update = _throttle(update, throttle)
42
56
  destroyRef.current = (where) => {
43
57
  if (!reactionRef.current) throw Error(`NO REACTION REF - ${where}`)
44
58
  destroyRef.current = undefined
59
+ unmarkCompatComponent(componentId)
45
60
  unobserve(reactionRef.current)
46
61
  reactionRef.current = undefined
47
62
  destroyCache(where)
@@ -60,6 +75,7 @@ export default function convertToObserver (BaseComponent, {
60
75
 
61
76
  // clean up observer on unmount
62
77
  useUnmount(() => {
78
+ unmarkCompatComponent(componentId)
63
79
  destroyRef.current?.('useUnmount()')
64
80
  })
65
81
 
package/react/useSub.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { useRef, useDeferredValue } from 'react'
2
2
  import sub from '../orm/sub.js'
3
- import { useScheduleUpdate, useCache, useDefer } from './helpers.js'
3
+ import { useScheduleUpdate, useCache, useDefer, useId } from './helpers.js'
4
4
  import executionContextTracker from './executionContextTracker.js'
5
5
  import * as promiseBatcher from './promiseBatcher.js'
6
6
  import renderAttemptDestroyer from './renderAttemptDestroyer.js'
7
+ import { markCompatComponent } from './compatComponentRegistry.js'
7
8
 
8
9
  let TEST_THROTTLING = false
9
10
 
@@ -28,8 +29,10 @@ export default function useSub (signal, params, options) {
28
29
  // version of sub() which works as a react hook and throws promise for Suspense
29
30
  export function useSubDeferred (signal, params, { async = false, defer, batch = false, compatAttemptCleanup = false } = {}) {
30
31
  const $signalRef = useRef() // eslint-disable-line react-hooks/rules-of-hooks
32
+ const componentId = useId()
31
33
  const scheduleUpdate = useScheduleUpdate()
32
34
  const observerDefer = useDefer()
35
+ if (compatAttemptCleanup) markCompatComponent(componentId)
33
36
  if (batch) promiseBatcher.activate()
34
37
  defer ??= observerDefer ?? DEFAULT_DEFER
35
38
  if (defer) {
@@ -72,9 +75,11 @@ export function useSubDeferred (signal, params, { async = false, defer, batch =
72
75
  // but if we get a promise second time, we return the last signal and wait for promise to resolve
73
76
  export function useSubClassic (signal, params, { async = false, batch = false, compatAttemptCleanup = false } = {}) {
74
77
  const id = executionContextTracker.newHookId()
78
+ const componentId = useId()
75
79
  const cache = useCache()
76
80
  const activePromiseRef = useRef()
77
81
  const scheduleUpdate = useScheduleUpdate()
82
+ if (compatAttemptCleanup) markCompatComponent(componentId)
78
83
  if (batch) promiseBatcher.activate()
79
84
  const promiseOrSignal = params != null ? sub(signal, params) : sub(signal)
80
85
  // 1. if it's a promise, throw it so that Suspense can catch it and wait for subscription to finish