teamplay 0.4.0 → 0.5.0-alpha.1
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/babel-loader.cjs +1 -0
- package/babel.cjs +1 -0
- package/dist/connect/index.d.ts +1 -0
- package/dist/connect/index.js +9 -0
- package/dist/connect/lib/sharedb-crosstab-pubsub.d.cts +10 -0
- package/dist/connect/offline/index.d.ts +1 -0
- package/dist/connect/offline/index.js +126 -0
- package/dist/connect/offline/react-native.d.ts +10 -0
- package/dist/connect/offline/react-native.js +25 -0
- package/dist/connect/offline/web.d.ts +9 -0
- package/dist/connect/offline/web.js +12 -0
- package/dist/connect/sharedbConnection.d.cts +2 -0
- package/dist/connect/test.d.ts +1 -0
- package/dist/connect/test.js +12 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +74 -0
- package/dist/orm/$.d.ts +9 -0
- package/dist/orm/$.js +34 -0
- package/dist/orm/Aggregation.d.ts +17 -0
- package/dist/orm/Aggregation.js +115 -0
- package/dist/orm/Cache.d.ts +9 -0
- package/dist/orm/Cache.js +32 -0
- package/dist/orm/Compat/SignalCompat.d.ts +3 -0
- package/dist/orm/Compat/SignalCompat.js +1542 -0
- package/dist/orm/Compat/eventsCompat.d.ts +3 -0
- package/dist/orm/Compat/eventsCompat.js +73 -0
- package/dist/orm/Compat/hooksCompat.d.ts +33 -0
- package/dist/orm/Compat/hooksCompat.js +360 -0
- package/dist/orm/Compat/modelEvents.d.ts +6 -0
- package/dist/orm/Compat/modelEvents.js +228 -0
- package/dist/orm/Compat/queryReadiness.d.ts +5 -0
- package/dist/orm/Compat/queryReadiness.js +185 -0
- package/dist/orm/Compat/refFallback.d.ts +13 -0
- package/dist/orm/Compat/refFallback.js +65 -0
- package/dist/orm/Compat/refRegistry.d.ts +6 -0
- package/dist/orm/Compat/refRegistry.js +54 -0
- package/dist/orm/Compat/silentContext.d.ts +5 -0
- package/dist/orm/Compat/silentContext.js +48 -0
- package/dist/orm/Compat/startStopCompat.d.ts +3 -0
- package/dist/orm/Compat/startStopCompat.js +217 -0
- package/dist/orm/Doc.d.ts +96 -0
- package/dist/orm/Doc.js +966 -0
- package/dist/orm/Query.d.ts +117 -0
- package/dist/orm/Query.js +1111 -0
- package/dist/orm/Reaction.d.ts +10 -0
- package/dist/orm/Reaction.js +41 -0
- package/dist/orm/Root.d.ts +22 -0
- package/dist/orm/Root.js +85 -0
- package/dist/orm/Signal.d.ts +12 -0
- package/dist/orm/Signal.js +7 -0
- package/dist/orm/SignalBase.d.ts +168 -0
- package/dist/orm/SignalBase.js +625 -0
- package/dist/orm/SubscriptionState.d.ts +17 -0
- package/dist/orm/SubscriptionState.js +123 -0
- package/dist/orm/Value.d.ts +9 -0
- package/dist/orm/Value.js +25 -0
- package/dist/orm/addModel.d.ts +6 -0
- package/dist/orm/addModel.js +39 -0
- package/dist/orm/associations.d.ts +18 -0
- package/dist/orm/associations.js +70 -0
- package/dist/orm/batchScheduler.d.ts +7 -0
- package/dist/orm/batchScheduler.js +62 -0
- package/dist/orm/compatEnv.d.ts +1 -0
- package/dist/orm/compatEnv.js +4 -0
- package/dist/orm/connection.d.ts +26 -0
- package/dist/orm/connection.js +38 -0
- package/dist/orm/dataTree.d.ts +34 -0
- package/dist/orm/dataTree.js +880 -0
- package/dist/orm/disposeRootContext.d.ts +4 -0
- package/dist/orm/disposeRootContext.js +59 -0
- package/dist/orm/getSignal.d.ts +16 -0
- package/dist/orm/getSignal.js +133 -0
- package/dist/orm/idFields.d.ts +14 -0
- package/dist/orm/idFields.js +95 -0
- package/dist/orm/index.d.ts +7 -0
- package/dist/orm/index.js +6 -0
- package/dist/orm/initModels.d.ts +5 -0
- package/dist/orm/initModels.js +74 -0
- package/dist/orm/missingDoc.d.ts +1 -0
- package/dist/orm/missingDoc.js +3 -0
- package/dist/orm/pluralize.d.ts +12 -0
- package/dist/orm/privateData.d.ts +22 -0
- package/dist/orm/privateData.js +170 -0
- package/dist/orm/rootContext.d.ts +78 -0
- package/dist/orm/rootContext.js +297 -0
- package/dist/orm/rootScope.d.ts +12 -0
- package/dist/orm/rootScope.js +46 -0
- package/dist/orm/signalArrayReaders.d.ts +22 -0
- package/dist/orm/signalArrayReaders.js +42 -0
- package/dist/orm/signalMetadata.d.ts +17 -0
- package/dist/orm/signalMetadata.js +56 -0
- package/dist/orm/signalMutationGuards.d.ts +4 -0
- package/dist/orm/signalMutationGuards.js +14 -0
- package/dist/orm/signalPathKind.d.ts +2 -0
- package/dist/orm/signalPathKind.js +10 -0
- package/dist/orm/signalPathRules.d.ts +6 -0
- package/dist/orm/signalPathRules.js +28 -0
- package/dist/orm/signalReads.d.ts +26 -0
- package/dist/orm/signalReads.js +64 -0
- package/dist/orm/signalRuntimeAccess.d.ts +16 -0
- package/dist/orm/signalRuntimeAccess.js +24 -0
- package/dist/orm/signalRuntimeDescriptor.d.ts +19 -0
- package/dist/orm/signalRuntimeDescriptor.js +97 -0
- package/dist/orm/signalStorageMutations.d.ts +18 -0
- package/dist/orm/signalStorageMutations.js +26 -0
- package/dist/orm/signalSymbols.d.ts +5 -0
- package/dist/orm/signalSymbols.js +5 -0
- package/dist/orm/signalValueMutations.d.ts +14 -0
- package/dist/orm/signalValueMutations.js +36 -0
- package/dist/orm/sub.d.ts +37 -0
- package/dist/orm/sub.js +187 -0
- package/dist/orm/subscriptionGcDelay.d.ts +4 -0
- package/dist/orm/subscriptionGcDelay.js +26 -0
- package/dist/orm/types/baseMethods.d.ts +43 -0
- package/dist/orm/types/baseMethods.js +1 -0
- package/dist/orm/types/jsonSchema.d.ts +75 -0
- package/dist/orm/types/jsonSchema.js +1 -0
- package/dist/orm/types/modelManifest.d.ts +37 -0
- package/dist/orm/types/modelManifest.js +1 -0
- package/dist/orm/types/path.d.ts +8 -0
- package/dist/orm/types/path.js +1 -0
- package/dist/orm/types/query.d.ts +29 -0
- package/dist/orm/types/query.js +1 -0
- package/dist/orm/types/signal.d.ts +132 -0
- package/dist/orm/types/signal.js +1 -0
- package/dist/react/compatComponentRegistry.d.ts +4 -0
- package/dist/react/compatComponentRegistry.js +19 -0
- package/dist/react/convertToObserver.d.ts +26 -0
- package/dist/react/convertToObserver.js +114 -0
- package/dist/react/executionContextTracker.d.ts +11 -0
- package/dist/react/executionContextTracker.js +28 -0
- package/dist/react/helpers.d.ts +34 -0
- package/dist/react/helpers.js +134 -0
- package/dist/react/observer.d.ts +22 -0
- package/dist/react/observer.js +8 -0
- package/dist/react/promiseBatcher.d.ts +13 -0
- package/dist/react/promiseBatcher.js +118 -0
- package/dist/react/renderAttemptDestroyer.d.ts +19 -0
- package/dist/react/renderAttemptDestroyer.js +46 -0
- package/dist/react/trapRender.d.ts +6 -0
- package/dist/react/trapRender.js +48 -0
- package/dist/react/universal$.d.ts +1 -0
- package/dist/react/universal$.js +18 -0
- package/dist/react/universalSub.d.ts +1 -0
- package/dist/react/universalSub.js +22 -0
- package/dist/react/useApi.d.ts +13 -0
- package/dist/react/useApi.js +67 -0
- package/dist/react/useSub.d.ts +102 -0
- package/dist/react/useSub.js +189 -0
- package/dist/react/useSuspendMemo.d.ts +3 -0
- package/dist/react/useSuspendMemo.js +102 -0
- package/dist/react/wrapIntoSuspense.d.ts +9 -0
- package/dist/react/wrapIntoSuspense.js +102 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +28 -0
- package/dist/utils/MockFinalizationRegistry.d.ts +37 -0
- package/dist/utils/MockFinalizationRegistry.js +85 -0
- package/dist/utils/MockWeakRef.d.ts +12 -0
- package/dist/utils/MockWeakRef.js +23 -0
- package/dist/utils/isServer.d.ts +3 -0
- package/dist/utils/isServer.js +18 -0
- package/dist/utils/setDiffDeep.d.ts +1 -0
- package/dist/utils/setDiffDeep.js +61 -0
- package/dist/utils/useIsomorphicLayoutEffect.d.ts +3 -0
- package/dist/utils/useIsomorphicLayoutEffect.js +3 -0
- package/file-based-models.js +3 -0
- package/file-based-models.node.js +6 -0
- package/package.json +77 -32
- package/teamplay.models.auto-init.virtual.js +1 -0
- package/teamplay.models.virtual.js +1 -0
- package/connect/index.js +0 -9
- package/connect/offline/index.js +0 -124
- package/connect/offline/react-native.js +0 -28
- package/connect/offline/web.js +0 -15
- package/connect/test.js +0 -12
- package/index.d.ts +0 -106
- package/index.js +0 -125
- package/orm/$.js +0 -38
- package/orm/Aggregation.js +0 -117
- package/orm/Cache.js +0 -46
- package/orm/Compat/README.md +0 -1064
- package/orm/Compat/SignalCompat.js +0 -1479
- package/orm/Compat/eventsCompat.js +0 -79
- package/orm/Compat/hooksCompat.js +0 -374
- package/orm/Compat/modelEvents.js +0 -225
- package/orm/Compat/queryReadiness.js +0 -191
- package/orm/Compat/refFallback.js +0 -62
- package/orm/Compat/refRegistry.js +0 -61
- package/orm/Compat/silentContext.js +0 -51
- package/orm/Compat/startStopCompat.js +0 -207
- package/orm/Doc.js +0 -969
- package/orm/Query.js +0 -1127
- package/orm/Reaction.js +0 -48
- package/orm/Root.js +0 -88
- package/orm/Signal.js +0 -22
- package/orm/SignalBase.js +0 -696
- package/orm/SubscriptionState.js +0 -138
- package/orm/Value.js +0 -30
- package/orm/addModel.js +0 -31
- package/orm/associations.js +0 -97
- package/orm/batchScheduler.js +0 -62
- package/orm/compatEnv.js +0 -4
- package/orm/connection.js +0 -46
- package/orm/dataTree.js +0 -869
- package/orm/disposeRootContext.js +0 -68
- package/orm/getSignal.js +0 -130
- package/orm/idFields.js +0 -88
- package/orm/index.d.ts +0 -6
- package/orm/index.js +0 -5
- package/orm/missingDoc.js +0 -3
- package/orm/privateData.js +0 -181
- package/orm/rootContext.js +0 -313
- package/orm/rootScope.js +0 -51
- package/orm/sub.js +0 -151
- package/orm/subscriptionGcDelay.js +0 -32
- package/react/compatComponentRegistry.js +0 -20
- package/react/convertToObserver.js +0 -117
- package/react/executionContextTracker.js +0 -32
- package/react/helpers.js +0 -141
- package/react/observer.js +0 -9
- package/react/promiseBatcher.js +0 -115
- package/react/renderAttemptDestroyer.js +0 -47
- package/react/trapRender.js +0 -50
- package/react/universal$.js +0 -18
- package/react/universalSub.js +0 -21
- package/react/useApi.js +0 -63
- package/react/useSub.js +0 -169
- package/react/useSuspendMemo.js +0 -96
- package/react/wrapIntoSuspense.js +0 -119
- package/server.js +0 -31
- package/utils/MockFinalizationRegistry.js +0 -94
- package/utils/MockWeakRef.js +0 -25
- package/utils/isServer.js +0 -17
- package/utils/setDiffDeep.js +0 -58
- package/utils/useIsomorphicLayoutEffect.js +0 -4
- /package/{connect → dist/connect}/lib/sharedb-crosstab-pubsub.cjs +0 -0
- /package/{connect → dist/connect}/sharedbConnection.cjs +0 -0
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export default new class ExecutionContextTracker {
|
|
2
|
-
#contextId
|
|
3
|
-
#hooksCounter
|
|
4
|
-
|
|
5
|
-
isActive () {
|
|
6
|
-
return this.#contextId !== undefined
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
getComponentId () {
|
|
10
|
-
return this.#contextId
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
newHookId () {
|
|
14
|
-
this.incrementHooksCounter()
|
|
15
|
-
const id = `_${this.#contextId}_${this.#hooksCounter}`
|
|
16
|
-
return id
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
incrementHooksCounter () {
|
|
20
|
-
if (!this.#contextId) return
|
|
21
|
-
this.#hooksCounter++
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
_start (contextId) {
|
|
25
|
-
this.#contextId = contextId
|
|
26
|
-
this.#hooksCounter = -1
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
_clear () {
|
|
30
|
-
this.#contextId = undefined
|
|
31
|
-
}
|
|
32
|
-
}()
|
package/react/helpers.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { useContext, createContext, useRef, useEffect, useLayoutEffect } from 'react'
|
|
2
|
-
|
|
3
|
-
export const ComponentMetaContext = createContext({})
|
|
4
|
-
|
|
5
|
-
export function pipeComponentDisplayName (SourceComponent, TargetComponent, suffix = '', defaultName = 'StartupjsWrapper') {
|
|
6
|
-
const displayName = SourceComponent.displayName || SourceComponent.name
|
|
7
|
-
|
|
8
|
-
if (!TargetComponent.displayName) {
|
|
9
|
-
TargetComponent.displayName = displayName ? (displayName + suffix) : defaultName
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function pipeComponentMeta (SourceComponent, TargetComponent, suffix = '', defaultName = 'StartupjsWrapper') {
|
|
14
|
-
pipeComponentDisplayName(SourceComponent, TargetComponent, suffix, defaultName)
|
|
15
|
-
|
|
16
|
-
if (!TargetComponent.propTypes && SourceComponent.propTypes) {
|
|
17
|
-
TargetComponent.propTypes = SourceComponent.propTypes
|
|
18
|
-
}
|
|
19
|
-
if (!TargetComponent.defaultProps && SourceComponent.defaultProps) {
|
|
20
|
-
TargetComponent.defaultProps = SourceComponent.defaultProps
|
|
21
|
-
}
|
|
22
|
-
return TargetComponent
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function useNow () {
|
|
26
|
-
const context = useContext(ComponentMetaContext)
|
|
27
|
-
if (!context) throw Error(ERRORS.useNow)
|
|
28
|
-
return context.createdAt
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function useId () {
|
|
32
|
-
const context = useContext(ComponentMetaContext)
|
|
33
|
-
if (!context) throw Error(ERRORS.useId)
|
|
34
|
-
return context.componentId
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function useTriggerUpdate () {
|
|
38
|
-
const context = useContext(ComponentMetaContext)
|
|
39
|
-
if (!context) throw Error(ERRORS.useTriggerUpdate)
|
|
40
|
-
return context.triggerUpdate
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function useScheduleUpdate () {
|
|
44
|
-
const context = useContext(ComponentMetaContext)
|
|
45
|
-
if (!context) throw Error(ERRORS.useScheduleUpdate)
|
|
46
|
-
return context.scheduleUpdate
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function useDefer () {
|
|
50
|
-
const context = useContext(ComponentMetaContext)
|
|
51
|
-
if (!context) throw Error(ERRORS.useScheduleUpdate)
|
|
52
|
-
return context.defer
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function useCache (key) {
|
|
56
|
-
const context = useContext(ComponentMetaContext)
|
|
57
|
-
if (!context) throw Error(ERRORS.useCache)
|
|
58
|
-
return context.cache
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function useUnmount (fn) {
|
|
62
|
-
const fnRef = useRef()
|
|
63
|
-
if (fnRef.current !== fn) fnRef.current = fn
|
|
64
|
-
useEffect(
|
|
65
|
-
() => () => {
|
|
66
|
-
fnRef.current()
|
|
67
|
-
},
|
|
68
|
-
[]
|
|
69
|
-
)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export function useDidUpdate (fn, deps) {
|
|
73
|
-
const isFirst = useRef(true)
|
|
74
|
-
const fnRef = useRef(fn)
|
|
75
|
-
if (fnRef.current !== fn) fnRef.current = fn
|
|
76
|
-
const stableDeps = useStableDeps(deps)
|
|
77
|
-
useEffect(() => {
|
|
78
|
-
if (isFirst.current) {
|
|
79
|
-
isFirst.current = false
|
|
80
|
-
return
|
|
81
|
-
}
|
|
82
|
-
return fnRef.current()
|
|
83
|
-
}, stableDeps)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function useOnce (condition, fn) {
|
|
87
|
-
const fired = useRef(false)
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
if (fired.current) return
|
|
90
|
-
if (!condition) return
|
|
91
|
-
fired.current = true
|
|
92
|
-
return fn()
|
|
93
|
-
}, [condition, fn])
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function useSyncEffect (fn, deps) {
|
|
97
|
-
const stableDeps = useStableDeps(deps)
|
|
98
|
-
useLayoutEffect(fn, [fn, stableDeps])
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function useStableDeps (deps) {
|
|
102
|
-
const depsRef = useRef([])
|
|
103
|
-
const nextDeps = Array.isArray(deps) ? deps : []
|
|
104
|
-
if (!shallowEqualArrays(depsRef.current, nextDeps)) {
|
|
105
|
-
depsRef.current = nextDeps
|
|
106
|
-
}
|
|
107
|
-
return depsRef.current
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function shallowEqualArrays (a, b) {
|
|
111
|
-
if (a === b) return true
|
|
112
|
-
if (!Array.isArray(a) || !Array.isArray(b)) return false
|
|
113
|
-
if (a.length !== b.length) return false
|
|
114
|
-
for (let i = 0; i < a.length; i++) {
|
|
115
|
-
if (!Object.is(a[i], b[i])) return false
|
|
116
|
-
}
|
|
117
|
-
return true
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const ERRORS = {
|
|
121
|
-
useTriggerUpdate: `
|
|
122
|
-
useTriggerUpdate() can only be used inside a component wrapped with observer().
|
|
123
|
-
You have probably forgot to wrap your component with observer().
|
|
124
|
-
`,
|
|
125
|
-
useScheduleUpdate: `
|
|
126
|
-
useScheduleUpdate() can only be used inside a component wrapped with observer().
|
|
127
|
-
You have probably forgot to wrap your component with observer().
|
|
128
|
-
`,
|
|
129
|
-
useId: `
|
|
130
|
-
useId() can only be used inside a component wrapped with observer().
|
|
131
|
-
You have probably forgot to wrap your component with observer().
|
|
132
|
-
`,
|
|
133
|
-
useNow: `
|
|
134
|
-
useNow() can only be used inside a component wrapped with observer().
|
|
135
|
-
You have probably forgot to wrap your component with observer().
|
|
136
|
-
`,
|
|
137
|
-
useCache: `
|
|
138
|
-
useCache() can only be used inside a component wrapped with observer().
|
|
139
|
-
You have probably forgot to wrap your component with observer().
|
|
140
|
-
`
|
|
141
|
-
}
|
package/react/observer.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import convertToObserver from './convertToObserver.js'
|
|
2
|
-
import wrapIntoSuspense from './wrapIntoSuspense.js'
|
|
3
|
-
|
|
4
|
-
function observer (Component, options) {
|
|
5
|
-
return wrapIntoSuspense(convertToObserver(Component, options))
|
|
6
|
-
}
|
|
7
|
-
observer.__wrapObserverMeta = wrapIntoSuspense
|
|
8
|
-
observer.__makeObserver = convertToObserver
|
|
9
|
-
export default observer
|
package/react/promiseBatcher.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
let active = false
|
|
2
|
-
let promises = []
|
|
3
|
-
let checks = new Map()
|
|
4
|
-
|
|
5
|
-
const READINESS_POLL_INTERVAL_MS = 16
|
|
6
|
-
const READINESS_WARN_AFTER_MS = 1000
|
|
7
|
-
|
|
8
|
-
export function activate () {
|
|
9
|
-
active = true
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function add (promise) {
|
|
13
|
-
if (!promise || typeof promise.then !== 'function') return
|
|
14
|
-
promises.push(promise)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function addCheck (check) {
|
|
18
|
-
if (!check || typeof check.isReady !== 'function') return
|
|
19
|
-
const key = check.key ?? Symbol('batch-check')
|
|
20
|
-
checks.set(key, { ...check, key })
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function getPromiseAll () {
|
|
24
|
-
const pendingPromises = promises
|
|
25
|
-
const pendingChecks = Array.from(checks.values())
|
|
26
|
-
const hasPromises = pendingPromises.length > 0
|
|
27
|
-
// Checks are a materialization barrier for initial batch subscriptions.
|
|
28
|
-
// If there were no subscription promises in this render, we are in update mode
|
|
29
|
-
// and should not suspend the whole subtree.
|
|
30
|
-
const result = hasPromises
|
|
31
|
-
? waitForBatchReady(pendingPromises, pendingChecks)
|
|
32
|
-
: null
|
|
33
|
-
reset()
|
|
34
|
-
return result
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function isActive () {
|
|
38
|
-
return active
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function reset () {
|
|
42
|
-
active = false
|
|
43
|
-
promises = []
|
|
44
|
-
checks = new Map()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function waitForBatchReady (pendingPromises, pendingChecks) {
|
|
48
|
-
if (pendingPromises.length > 0) await Promise.all(pendingPromises)
|
|
49
|
-
// Let microtasks flush after subscription promises resolve so tree writes become visible.
|
|
50
|
-
await Promise.resolve()
|
|
51
|
-
await waitForChecksReady(pendingChecks)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function waitForChecksReady (pendingChecks) {
|
|
55
|
-
if (pendingChecks.length === 0) return
|
|
56
|
-
let warned = false
|
|
57
|
-
const startedAt = Date.now()
|
|
58
|
-
while (true) {
|
|
59
|
-
const notReadyChecks = getNotReadyChecks(pendingChecks)
|
|
60
|
-
if (notReadyChecks.length === 0) return
|
|
61
|
-
if (!warned && isDevMode() && Date.now() - startedAt >= READINESS_WARN_AFTER_MS) {
|
|
62
|
-
warned = true
|
|
63
|
-
warnAboutChecksDelay(notReadyChecks)
|
|
64
|
-
}
|
|
65
|
-
await delay(READINESS_POLL_INTERVAL_MS)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function getNotReadyChecks (pendingChecks) {
|
|
70
|
-
const notReady = []
|
|
71
|
-
for (const check of pendingChecks) {
|
|
72
|
-
if (!isCheckReady(check)) notReady.push(check)
|
|
73
|
-
}
|
|
74
|
-
return notReady
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function isCheckReady (check) {
|
|
78
|
-
try {
|
|
79
|
-
return !!check.isReady()
|
|
80
|
-
} catch (err) {
|
|
81
|
-
if (isThenable(err)) return false
|
|
82
|
-
throw err
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function warnAboutChecksDelay (checks) {
|
|
87
|
-
const details = checks.map(check => {
|
|
88
|
-
let state
|
|
89
|
-
try {
|
|
90
|
-
state = typeof check.getState === 'function' ? check.getState() : undefined
|
|
91
|
-
} catch (err) {
|
|
92
|
-
state = isThenable(err) ? 'suspended' : `state-error: ${err?.message || err}`
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
type: check.type || 'unknown',
|
|
96
|
-
key: String(check.key),
|
|
97
|
-
details: check.details,
|
|
98
|
-
state
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
console.warn('[teamplay] useBatch() is waiting for data materialization checks.', details)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function isDevMode () {
|
|
105
|
-
if (typeof process === 'undefined' || !process?.env) return true
|
|
106
|
-
return process.env.NODE_ENV !== 'production'
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function isThenable (value) {
|
|
110
|
-
return !!value && typeof value.then === 'function'
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function delay (ms) {
|
|
114
|
-
return new Promise(resolve => setTimeout(resolve, ms))
|
|
115
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
class RenderAttemptDestroyer {
|
|
2
|
-
constructor () {
|
|
3
|
-
this.fns = []
|
|
4
|
-
this.compatAttemptCleanupArmed = false
|
|
5
|
-
this.suspenseGateArmed = false
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
add (fn, { compat = false } = {}) {
|
|
9
|
-
if (typeof fn !== 'function') return
|
|
10
|
-
this.fns.push(fn)
|
|
11
|
-
if (compat) this.compatAttemptCleanupArmed = true
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
armCompatAttemptCleanup () {
|
|
15
|
-
this.compatAttemptCleanupArmed = true
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
armSuspenseGate () {
|
|
19
|
-
this.suspenseGateArmed = true
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
consumeThenableHandling () {
|
|
23
|
-
const shouldRunAttemptCleanup = this.compatAttemptCleanupArmed && this.fns.length > 0
|
|
24
|
-
const shouldKeepShellAlive = this.suspenseGateArmed || shouldRunAttemptCleanup
|
|
25
|
-
let destroyAttempt
|
|
26
|
-
if (shouldRunAttemptCleanup) {
|
|
27
|
-
const fns = [...this.fns]
|
|
28
|
-
destroyAttempt = async () => {
|
|
29
|
-
await Promise.allSettled(fns.map(fn => fn()))
|
|
30
|
-
fns.length = 0
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
this.reset()
|
|
34
|
-
return {
|
|
35
|
-
shouldKeepShellAlive,
|
|
36
|
-
destroyAttempt
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
reset () {
|
|
41
|
-
this.fns.length = 0
|
|
42
|
-
this.compatAttemptCleanupArmed = false
|
|
43
|
-
this.suspenseGateArmed = false
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export default new RenderAttemptDestroyer()
|
package/react/trapRender.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// trap render function (functional component) to block observer updates and activate cache
|
|
2
|
-
// during synchronous rendering
|
|
3
|
-
import executionContextTracker from './executionContextTracker.js'
|
|
4
|
-
import * as promiseBatcher from './promiseBatcher.js'
|
|
5
|
-
import renderAttemptDestroyer from './renderAttemptDestroyer.js'
|
|
6
|
-
|
|
7
|
-
export default function trapRender ({ render, cache, destroy, componentId }) {
|
|
8
|
-
return (...args) => {
|
|
9
|
-
executionContextTracker._start(componentId)
|
|
10
|
-
cache.activate()
|
|
11
|
-
let destroyed
|
|
12
|
-
try {
|
|
13
|
-
renderAttemptDestroyer.reset()
|
|
14
|
-
promiseBatcher.reset()
|
|
15
|
-
const res = render(...args)
|
|
16
|
-
if (isDevMode() && promiseBatcher.isActive()) {
|
|
17
|
-
throw Error('[teamplay] useBatch* hooks were used without a closing useBatch() call.')
|
|
18
|
-
}
|
|
19
|
-
return res
|
|
20
|
-
} catch (err) {
|
|
21
|
-
promiseBatcher.reset()
|
|
22
|
-
if (!err.then) {
|
|
23
|
-
destroy('trapRender.js')
|
|
24
|
-
destroyed = true
|
|
25
|
-
throw err
|
|
26
|
-
}
|
|
27
|
-
const {
|
|
28
|
-
shouldKeepShellAlive,
|
|
29
|
-
destroyAttempt
|
|
30
|
-
} = renderAttemptDestroyer.consumeThenableHandling()
|
|
31
|
-
if (shouldKeepShellAlive) {
|
|
32
|
-
throw Promise.resolve(err).then(() => destroyAttempt?.())
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// TODO: this might only be needed only if promise is thrown
|
|
36
|
-
// (check if useUnmount in convertToObserver is called if a regular error is thrown)
|
|
37
|
-
destroy('trapRender.js')
|
|
38
|
-
destroyed = true
|
|
39
|
-
throw err
|
|
40
|
-
} finally {
|
|
41
|
-
if (!destroyed) cache.deactivate()
|
|
42
|
-
executionContextTracker._clear()
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function isDevMode () {
|
|
48
|
-
if (typeof process === 'undefined' || !process?.env) return true
|
|
49
|
-
return process.env.NODE_ENV !== 'production'
|
|
50
|
-
}
|
package/react/universal$.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import $ from '../orm/$.js'
|
|
2
|
-
import { useCache } from './helpers.js'
|
|
3
|
-
import executionContextTracker from './executionContextTracker.js'
|
|
4
|
-
|
|
5
|
-
// universal versions of $() which work as a plain function or as a react hook
|
|
6
|
-
export default function universal$ ($root, value) {
|
|
7
|
-
if (executionContextTracker.isActive()) {
|
|
8
|
-
// within react component
|
|
9
|
-
const id = executionContextTracker.newHookId()
|
|
10
|
-
const cache = useCache() // eslint-disable-line react-hooks/rules-of-hooks
|
|
11
|
-
const $signal = $($root, value, id)
|
|
12
|
-
cache.set(id, $signal)
|
|
13
|
-
// save signal into ref to make sure it's not garbage collected while component exists
|
|
14
|
-
return $signal
|
|
15
|
-
} else {
|
|
16
|
-
return $($root, value)
|
|
17
|
-
}
|
|
18
|
-
}
|
package/react/universalSub.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// NOTE: this is not used currently since using an explicit useSub()
|
|
2
|
-
// hook is easier to understand in a React context.
|
|
3
|
-
// Having the same sub() function working with either await or without it
|
|
4
|
-
// is confusing. It's better to have a separate function for the hook.
|
|
5
|
-
import { useRef } from 'react'
|
|
6
|
-
import sub from '../orm/sub.js'
|
|
7
|
-
import executionContextTracker from './executionContextTracker.js'
|
|
8
|
-
|
|
9
|
-
// universal versions of sub() which work as a plain function or as a react hook
|
|
10
|
-
export default function universalSub (...args) {
|
|
11
|
-
const promiseOrSignal = sub(...args)
|
|
12
|
-
if (executionContextTracker.isActive()) {
|
|
13
|
-
// within react component
|
|
14
|
-
// 1. if it's a promise, throw it so that Suspense can catch it and wait for subscription to finish
|
|
15
|
-
if (promiseOrSignal.then) throw promiseOrSignal
|
|
16
|
-
// 2. if it's a signal, we save it into ref to make sure it's not garbage collected while component exists
|
|
17
|
-
const $signalRef = useRef() // eslint-disable-line react-hooks/rules-of-hooks
|
|
18
|
-
if ($signalRef.current !== promiseOrSignal) $signalRef.current = promiseOrSignal
|
|
19
|
-
}
|
|
20
|
-
return promiseOrSignal
|
|
21
|
-
}
|
package/react/useApi.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
export default function useApi (api, args = [], options = {}) {
|
|
4
|
-
const { debounce = 0 } = options || {}
|
|
5
|
-
const [data, setData] = useState()
|
|
6
|
-
const [error, setError] = useState()
|
|
7
|
-
const [loading, setLoading] = useState(false)
|
|
8
|
-
const requestIdRef = useRef(0)
|
|
9
|
-
const stableArgs = useStableDeps(Array.isArray(args) ? args : [args])
|
|
10
|
-
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
if (typeof api !== 'function') return
|
|
13
|
-
let cancelled = false
|
|
14
|
-
const requestId = ++requestIdRef.current
|
|
15
|
-
let timer
|
|
16
|
-
|
|
17
|
-
const run = async () => {
|
|
18
|
-
try {
|
|
19
|
-
setLoading(true)
|
|
20
|
-
const result = await api(...stableArgs)
|
|
21
|
-
if (cancelled || requestId !== requestIdRef.current) return
|
|
22
|
-
setData(result)
|
|
23
|
-
setError(undefined)
|
|
24
|
-
} catch (err) {
|
|
25
|
-
if (cancelled || requestId !== requestIdRef.current) return
|
|
26
|
-
setError(err)
|
|
27
|
-
} finally {
|
|
28
|
-
if (!cancelled && requestId === requestIdRef.current) setLoading(false)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (debounce > 0) {
|
|
33
|
-
timer = setTimeout(run, debounce)
|
|
34
|
-
} else {
|
|
35
|
-
run()
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return () => {
|
|
39
|
-
cancelled = true
|
|
40
|
-
if (timer) clearTimeout(timer)
|
|
41
|
-
}
|
|
42
|
-
}, [api, debounce, stableArgs])
|
|
43
|
-
|
|
44
|
-
return [data, loading, error]
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function useStableDeps (deps) {
|
|
48
|
-
const depsRef = useRef([])
|
|
49
|
-
if (!shallowEqualArrays(depsRef.current, deps)) {
|
|
50
|
-
depsRef.current = deps
|
|
51
|
-
}
|
|
52
|
-
return depsRef.current
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function shallowEqualArrays (a, b) {
|
|
56
|
-
if (a === b) return true
|
|
57
|
-
if (!Array.isArray(a) || !Array.isArray(b)) return false
|
|
58
|
-
if (a.length !== b.length) return false
|
|
59
|
-
for (let i = 0; i < a.length; i++) {
|
|
60
|
-
if (!Object.is(a[i], b[i])) return false
|
|
61
|
-
}
|
|
62
|
-
return true
|
|
63
|
-
}
|
package/react/useSub.js
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { useRef, useDeferredValue } from 'react'
|
|
2
|
-
import sub from '../orm/sub.js'
|
|
3
|
-
import { useScheduleUpdate, useCache, useDefer, useId } from './helpers.js'
|
|
4
|
-
import executionContextTracker from './executionContextTracker.js'
|
|
5
|
-
import * as promiseBatcher from './promiseBatcher.js'
|
|
6
|
-
import renderAttemptDestroyer from './renderAttemptDestroyer.js'
|
|
7
|
-
import { markCompatComponent } from './compatComponentRegistry.js'
|
|
8
|
-
|
|
9
|
-
let TEST_THROTTLING = false
|
|
10
|
-
|
|
11
|
-
// experimental feature to leverage useDeferredValue() to handle re-subscriptions.
|
|
12
|
-
// Currently it does lead to issues with extra rerenders and requires further investigation
|
|
13
|
-
let USE_DEFERRED_VALUE = true
|
|
14
|
-
// by default we want to defer stuff if possible instead of throwing promises
|
|
15
|
-
let DEFAULT_DEFER = true
|
|
16
|
-
|
|
17
|
-
export function useAsyncSub (signal, params, options) {
|
|
18
|
-
return useSub(signal, params, { ...options, async: true })
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export default function useSub (signal, params, options) {
|
|
22
|
-
if (USE_DEFERRED_VALUE) {
|
|
23
|
-
return useSubDeferred(signal, params, options) // eslint-disable-line react-hooks/rules-of-hooks
|
|
24
|
-
} else {
|
|
25
|
-
return useSubClassic(signal, params, options) // eslint-disable-line react-hooks/rules-of-hooks
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
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, compatAttemptCleanup = false } = {}) {
|
|
31
|
-
const $signalRef = useRef() // eslint-disable-line react-hooks/rules-of-hooks
|
|
32
|
-
const componentId = useId()
|
|
33
|
-
const scheduleUpdate = useScheduleUpdate()
|
|
34
|
-
const observerDefer = useDefer()
|
|
35
|
-
if (compatAttemptCleanup) markCompatComponent(componentId)
|
|
36
|
-
if (batch) promiseBatcher.activate()
|
|
37
|
-
defer ??= observerDefer ?? DEFAULT_DEFER
|
|
38
|
-
if (defer) {
|
|
39
|
-
signal = useDeferredValue(signal) // eslint-disable-line react-hooks/rules-of-hooks
|
|
40
|
-
params = useDeferredValue(params ? JSON.stringify(params) : undefined) // eslint-disable-line react-hooks/rules-of-hooks
|
|
41
|
-
params = params != null ? JSON.parse(params) : undefined
|
|
42
|
-
}
|
|
43
|
-
const promiseOrSignal = params != null ? sub(signal, params) : sub(signal)
|
|
44
|
-
// 1. if it's a promise, throw it so that Suspense can catch it and wait for subscription to finish
|
|
45
|
-
if (promiseOrSignal.then) {
|
|
46
|
-
const promise = maybeThrottle(promiseOrSignal)
|
|
47
|
-
const hasPreviousSignal = !!$signalRef.current
|
|
48
|
-
if (batch) {
|
|
49
|
-
// Batch suspense must block only on initial load.
|
|
50
|
-
// On resubscribe we keep rendering previous signal and refresh in background.
|
|
51
|
-
if (!hasPreviousSignal) {
|
|
52
|
-
promiseBatcher.add(promise)
|
|
53
|
-
if (compatAttemptCleanup) registerCompatAttemptCleanup(signal, params)
|
|
54
|
-
} else {
|
|
55
|
-
scheduleUpdate(promise)
|
|
56
|
-
}
|
|
57
|
-
if (async) scheduleUpdate(promise)
|
|
58
|
-
return $signalRef.current
|
|
59
|
-
}
|
|
60
|
-
if (async) {
|
|
61
|
-
scheduleUpdate(promise)
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
// Keep previous snapshot during update re-subscribe and refresh in background.
|
|
65
|
-
if (hasPreviousSignal) {
|
|
66
|
-
scheduleUpdate(promise)
|
|
67
|
-
return $signalRef.current
|
|
68
|
-
}
|
|
69
|
-
if (compatAttemptCleanup) registerCompatAttemptCleanup(signal, params)
|
|
70
|
-
throw promise
|
|
71
|
-
// 2. if it's a signal, we save it into ref to make sure it's not garbage collected while component exists
|
|
72
|
-
} else {
|
|
73
|
-
const $signal = promiseOrSignal
|
|
74
|
-
if ($signalRef.current !== $signal) $signalRef.current = $signal
|
|
75
|
-
return $signal
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// classic version which initially throws promise for Suspense
|
|
80
|
-
// but if we get a promise second time, we return the last signal and wait for promise to resolve
|
|
81
|
-
export function useSubClassic (signal, params, { async = false, batch = false, compatAttemptCleanup = false } = {}) {
|
|
82
|
-
const id = executionContextTracker.newHookId()
|
|
83
|
-
const componentId = useId()
|
|
84
|
-
const cache = useCache()
|
|
85
|
-
const activePromiseRef = useRef()
|
|
86
|
-
const scheduleUpdate = useScheduleUpdate()
|
|
87
|
-
if (compatAttemptCleanup) markCompatComponent(componentId)
|
|
88
|
-
if (batch) promiseBatcher.activate()
|
|
89
|
-
const promiseOrSignal = params != null ? sub(signal, params) : sub(signal)
|
|
90
|
-
// 1. if it's a promise, throw it so that Suspense can catch it and wait for subscription to finish
|
|
91
|
-
if (promiseOrSignal.then) {
|
|
92
|
-
const promise = maybeThrottle(promiseOrSignal)
|
|
93
|
-
if (batch) {
|
|
94
|
-
const hasPreviousSignal = cache.has(id)
|
|
95
|
-
// Batch suspense must block only on initial load.
|
|
96
|
-
// On resubscribe we keep rendering previous signal and refresh in background.
|
|
97
|
-
if (!hasPreviousSignal) {
|
|
98
|
-
promiseBatcher.add(promise)
|
|
99
|
-
if (compatAttemptCleanup) registerCompatAttemptCleanup(signal, params)
|
|
100
|
-
} else {
|
|
101
|
-
scheduleUpdate(promise)
|
|
102
|
-
}
|
|
103
|
-
if (async) scheduleUpdate(promise)
|
|
104
|
-
if (hasPreviousSignal) return cache.get(id)
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
// first time we just throw the promise to be caught by Suspense
|
|
108
|
-
if (!cache.has(id)) {
|
|
109
|
-
// if we are in async mode, we just return nothing and let the user
|
|
110
|
-
// handle appearance of signal on their own.
|
|
111
|
-
// We manually schedule an update when promise resolves since we can't
|
|
112
|
-
// rely on Suspense in this case to automatically trigger component's re-render
|
|
113
|
-
if (async) {
|
|
114
|
-
scheduleUpdate(promise)
|
|
115
|
-
return
|
|
116
|
-
}
|
|
117
|
-
if (compatAttemptCleanup) registerCompatAttemptCleanup(signal, params)
|
|
118
|
-
// in regular mode we throw the promise to be caught by Suspense
|
|
119
|
-
// this way we guarantee that the signal with all the data
|
|
120
|
-
// will always be there when component is rendered
|
|
121
|
-
throw promise
|
|
122
|
-
}
|
|
123
|
-
// if we already have a previous signal, we return it and wait for new promise to resolve
|
|
124
|
-
scheduleUpdate(promise)
|
|
125
|
-
return cache.get(id)
|
|
126
|
-
// 2. if it's a signal, we save it into ref to make sure it's not garbage collected while component exists
|
|
127
|
-
} else {
|
|
128
|
-
const $signal = promiseOrSignal
|
|
129
|
-
if (cache.get(id) !== $signal) {
|
|
130
|
-
activePromiseRef.current = undefined
|
|
131
|
-
cache.set(id, $signal)
|
|
132
|
-
}
|
|
133
|
-
return $signal
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function setTestThrottling (ms) {
|
|
138
|
-
if (typeof ms !== 'number') throw Error('setTestThrottling() accepts only a number in ms')
|
|
139
|
-
if (ms === 0) throw Error('setTestThrottling(0) is not allowed, use resetTestThrottling() instead')
|
|
140
|
-
if (ms < 0) throw Error('setTestThrottling() accepts only a positive number in ms')
|
|
141
|
-
TEST_THROTTLING = ms
|
|
142
|
-
}
|
|
143
|
-
export function resetTestThrottling () {
|
|
144
|
-
TEST_THROTTLING = false
|
|
145
|
-
}
|
|
146
|
-
export function setUseDeferredValue (value) {
|
|
147
|
-
USE_DEFERRED_VALUE = value
|
|
148
|
-
}
|
|
149
|
-
export function setDefaultDefer (value) {
|
|
150
|
-
DEFAULT_DEFER = value
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// throttle to simulate slow network
|
|
154
|
-
function maybeThrottle (promise) {
|
|
155
|
-
if (!TEST_THROTTLING) return promise
|
|
156
|
-
return new Promise((resolve, reject) => {
|
|
157
|
-
setTimeout(() => {
|
|
158
|
-
promise.then(resolve, reject)
|
|
159
|
-
}, TEST_THROTTLING)
|
|
160
|
-
})
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function registerCompatAttemptCleanup (signal, params) {
|
|
164
|
-
// Compat hooks don't build per-hook init objects like Racer.
|
|
165
|
-
// We still need a marker so trapRender can defer observer-shell cleanup
|
|
166
|
-
// only when a real attempt cleanup exists.
|
|
167
|
-
// This path must not arm suspense-gate keep-alive by itself.
|
|
168
|
-
renderAttemptDestroyer.armCompatAttemptCleanup()
|
|
169
|
-
}
|