teamplay 0.4.0-alpha.80 → 0.4.0-alpha.81
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/orm/Aggregation.js +48 -13
- package/orm/Compat/SignalCompat.js +97 -46
- package/orm/Compat/eventsCompat.js +2 -2
- package/orm/Compat/modelEvents.js +58 -31
- package/orm/Compat/queryReadiness.js +15 -15
- package/orm/Compat/refFallback.js +5 -4
- package/orm/Compat/refRegistry.js +34 -8
- package/orm/Doc.js +53 -2
- package/orm/Query.js +290 -135
- package/orm/Reaction.js +11 -9
- package/orm/Root.js +28 -0
- package/orm/SignalBase.js +79 -39
- package/orm/Value.js +7 -5
- package/orm/dataTree.js +66 -40
- package/orm/disposeRootContext.js +68 -0
- package/orm/getSignal.js +20 -20
- package/orm/privateData.js +156 -0
- package/orm/rootContext.js +303 -0
- package/orm/rootScope.js +52 -0
- package/orm/sub.js +15 -6
- package/package.json +2 -2
package/orm/Aggregation.js
CHANGED
|
@@ -1,34 +1,54 @@
|
|
|
1
1
|
import { raw } from '@nx-js/observer-util'
|
|
2
|
-
import {
|
|
2
|
+
import { getRaw } from './dataTree.js'
|
|
3
3
|
import getSignal from './getSignal.js'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
QuerySubscriptions,
|
|
6
|
+
hashQuery,
|
|
7
|
+
Query,
|
|
8
|
+
HASH,
|
|
9
|
+
PARAMS,
|
|
10
|
+
COLLECTION_NAME,
|
|
11
|
+
parseQueryHash
|
|
12
|
+
} from './Query.js'
|
|
5
13
|
import Signal, { SEGMENTS } from './Signal.js'
|
|
6
14
|
import { getIdFieldsForSegments, isPlainObject } from './idFields.js'
|
|
15
|
+
import { delPrivateData, getPrivateData, setPrivateData } from './privateData.js'
|
|
7
16
|
|
|
8
17
|
export const IS_AGGREGATION = Symbol('is aggregation signal')
|
|
9
18
|
export const AGGREGATIONS = '$aggregations'
|
|
10
19
|
|
|
11
20
|
class Aggregation extends Query {
|
|
12
21
|
_initData () {
|
|
13
|
-
|
|
14
|
-
const extra = raw(this.shareQuery.extra)
|
|
15
|
-
injectAggregationIds(extra, this.collectionName)
|
|
16
|
-
_set([AGGREGATIONS, this.hash], extra)
|
|
17
|
-
}
|
|
22
|
+
this._syncAllRootsData()
|
|
18
23
|
|
|
19
24
|
this.shareQuery.on('extra', extra => {
|
|
20
25
|
extra = raw(extra)
|
|
21
26
|
injectAggregationIds(extra, this.collectionName)
|
|
22
|
-
|
|
27
|
+
this._forEachRoot(rootId => {
|
|
28
|
+
setPrivateData(rootId, [AGGREGATIONS, this.hash], extra)
|
|
29
|
+
})
|
|
23
30
|
})
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
_syncRootData (rootId) {
|
|
34
|
+
if (!this.shareQuery) return
|
|
35
|
+
const extra = raw(this.shareQuery.extra)
|
|
36
|
+
injectAggregationIds(extra, this.collectionName)
|
|
37
|
+
setPrivateData(rootId, [AGGREGATIONS, this.hash], extra)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_removeRootData (rootId) {
|
|
41
|
+
delPrivateData(rootId, [AGGREGATIONS, this.hash])
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
_removeData () {
|
|
27
|
-
|
|
45
|
+
this._forEachRoot(rootId => this._removeRootData(rootId))
|
|
46
|
+
this.rootIds.clear()
|
|
28
47
|
}
|
|
29
48
|
}
|
|
30
49
|
|
|
31
50
|
export const aggregationSubscriptions = new QuerySubscriptions(Aggregation)
|
|
51
|
+
aggregationSubscriptions.runtimeKind = 'aggregation'
|
|
32
52
|
|
|
33
53
|
function injectAggregationIds (extra, collectionName) {
|
|
34
54
|
if (!Array.isArray(extra)) return
|
|
@@ -44,13 +64,14 @@ function injectAggregationIds (extra, collectionName) {
|
|
|
44
64
|
|
|
45
65
|
export function getAggregationSignal (collectionName, params, options) {
|
|
46
66
|
params = JSON.parse(JSON.stringify(params))
|
|
47
|
-
const
|
|
67
|
+
const transportHash = hashQuery(collectionName, params)
|
|
68
|
+
const { root, signalOptions } = parseAggregationSignalOptions(options)
|
|
48
69
|
|
|
49
|
-
const $aggregation = getSignal(
|
|
70
|
+
const $aggregation = getSignal(root, [AGGREGATIONS, transportHash], signalOptions)
|
|
50
71
|
$aggregation[IS_AGGREGATION] ??= true
|
|
51
72
|
$aggregation[COLLECTION_NAME] ??= collectionName
|
|
52
73
|
$aggregation[PARAMS] ??= params
|
|
53
|
-
$aggregation[HASH] ??=
|
|
74
|
+
$aggregation[HASH] ??= transportHash
|
|
54
75
|
return $aggregation
|
|
55
76
|
}
|
|
56
77
|
|
|
@@ -65,10 +86,13 @@ export function isAggregationSignal ($signal) {
|
|
|
65
86
|
|
|
66
87
|
// example: ['$aggregations', '{"active":true}', 42]
|
|
67
88
|
// AND only if it also has either '_id' or 'id' field inside
|
|
68
|
-
export function getAggregationDocId (segments, method
|
|
89
|
+
export function getAggregationDocId (segments, rootId, method) {
|
|
69
90
|
if (!(segments.length >= 3)) return
|
|
70
91
|
if (!(segments[0] === AGGREGATIONS)) return
|
|
71
92
|
if (!(typeof segments[2] === 'number')) return
|
|
93
|
+
if (typeof method !== 'function') {
|
|
94
|
+
method = path => rootId == null ? getRaw(path) : getPrivateData(rootId, path)
|
|
95
|
+
}
|
|
72
96
|
const docId = method([...segments.slice(0, 3), '_id']) || method([...segments.slice(0, 3), 'id'])
|
|
73
97
|
return docId
|
|
74
98
|
}
|
|
@@ -80,3 +104,14 @@ export function getAggregationCollectionName (segments) {
|
|
|
80
104
|
const { collectionName } = parseQueryHash(hash)
|
|
81
105
|
return collectionName
|
|
82
106
|
}
|
|
107
|
+
|
|
108
|
+
function parseAggregationSignalOptions (options) {
|
|
109
|
+
if (!options || typeof options !== 'object') {
|
|
110
|
+
return {
|
|
111
|
+
root: undefined,
|
|
112
|
+
signalOptions: {}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const { root, ...signalOptions } = options
|
|
116
|
+
return { root, signalOptions }
|
|
117
|
+
}
|
|
@@ -8,23 +8,14 @@ import {
|
|
|
8
8
|
isPublicCollectionSignal,
|
|
9
9
|
isPublicDocumentSignal
|
|
10
10
|
} from '../SignalBase.js'
|
|
11
|
-
import { getRoot, ROOT, getRootSignal, GLOBAL_ROOT_ID } from '../Root.js'
|
|
11
|
+
import { getRoot, ROOT, ROOT_ID, getRootSignal, GLOBAL_ROOT_ID, unregisterRootFinalizer } from '../Root.js'
|
|
12
12
|
import { publicOnly, fetchOnly, setFetchOnly } from '../connection.js'
|
|
13
13
|
import { docSubscriptions } from '../Doc.js'
|
|
14
14
|
import { IS_QUERY, getQuerySignal, querySubscriptions } from '../Query.js'
|
|
15
15
|
import { IS_AGGREGATION, aggregationSubscriptions, getAggregationSignal } from '../Aggregation.js'
|
|
16
16
|
import { getIdFieldsForSegments, isIdFieldPath, isPublicDocPath, normalizeIdFields, isPlainObject } from '../idFields.js'
|
|
17
17
|
import {
|
|
18
|
-
del as _del,
|
|
19
|
-
setReplace as _setReplace,
|
|
20
18
|
incrementPublic as _incrementPublic,
|
|
21
|
-
arrayPush as _arrayPush,
|
|
22
|
-
arrayUnshift as _arrayUnshift,
|
|
23
|
-
arrayInsert as _arrayInsert,
|
|
24
|
-
arrayPop as _arrayPop,
|
|
25
|
-
arrayShift as _arrayShift,
|
|
26
|
-
arrayRemove as _arrayRemove,
|
|
27
|
-
arrayMove as _arrayMove,
|
|
28
19
|
arrayPushPublic as _arrayPushPublic,
|
|
29
20
|
arrayUnshiftPublic as _arrayUnshiftPublic,
|
|
30
21
|
arrayInsertPublic as _arrayInsertPublic,
|
|
@@ -32,19 +23,32 @@ import {
|
|
|
32
23
|
arrayShiftPublic as _arrayShiftPublic,
|
|
33
24
|
arrayRemovePublic as _arrayRemovePublic,
|
|
34
25
|
arrayMovePublic as _arrayMovePublic,
|
|
35
|
-
stringInsertLocal as _stringInsertLocal,
|
|
36
|
-
stringRemoveLocal as _stringRemoveLocal,
|
|
37
26
|
stringInsertPublic as _stringInsertPublic,
|
|
38
27
|
stringRemovePublic as _stringRemovePublic
|
|
39
28
|
} from '../dataTree.js'
|
|
40
29
|
import { on as onCustomEvent, removeListener as removeCustomEventListener } from './eventsCompat.js'
|
|
41
30
|
import { waitForImperativeQueryReady } from './queryReadiness.js'
|
|
42
31
|
import { normalizePattern, onModelEvent, removeModelListener } from './modelEvents.js'
|
|
43
|
-
import { setRefLink, removeRefLink,
|
|
32
|
+
import { setRefLink, removeRefLink, getAllRefLinks } from './refRegistry.js'
|
|
44
33
|
import { REF_TARGET, resolveRefSignalSafe, resolveRefSegmentsSafe } from './refFallback.js'
|
|
45
34
|
import { runInBatch } from '../batchScheduler.js'
|
|
46
35
|
import { runInSilentContext, runInModelEventsSilentContext } from './silentContext.js'
|
|
47
36
|
import universal$ from '../../react/universal$.js'
|
|
37
|
+
import { getRootContext } from '../rootContext.js'
|
|
38
|
+
import disposeRootContext from '../disposeRootContext.js'
|
|
39
|
+
import {
|
|
40
|
+
arrayInsertPrivateData,
|
|
41
|
+
arrayMovePrivateData,
|
|
42
|
+
arrayPopPrivateData,
|
|
43
|
+
arrayPushPrivateData,
|
|
44
|
+
arrayRemovePrivateData,
|
|
45
|
+
arrayShiftPrivateData,
|
|
46
|
+
arrayUnshiftPrivateData,
|
|
47
|
+
delPrivateData,
|
|
48
|
+
setReplacePrivateData,
|
|
49
|
+
stringInsertPrivateData,
|
|
50
|
+
stringRemovePrivateData
|
|
51
|
+
} from '../privateData.js'
|
|
48
52
|
|
|
49
53
|
class SignalCompat extends Signal {
|
|
50
54
|
static ID_FIELDS = ['_id', 'id']
|
|
@@ -100,10 +104,12 @@ class SignalCompat extends Signal {
|
|
|
100
104
|
if (arguments.length < 1 || arguments.length > 3) throw Error('Signal.query() expects one to three arguments')
|
|
101
105
|
if (typeof collection !== 'string') throw Error('Signal.query() expects collection to be a string')
|
|
102
106
|
const normalized = normalizeQueryParams(collection, params)
|
|
107
|
+
const root = getRoot(this) || (this[ROOT_ID] ? this : undefined)
|
|
108
|
+
const scopedOptions = withQueryScopeOptions(options, root)
|
|
103
109
|
if (isAggregationParams(normalized)) {
|
|
104
|
-
return getAggregationSignal(collection, normalized,
|
|
110
|
+
return getAggregationSignal(collection, normalized, scopedOptions)
|
|
105
111
|
}
|
|
106
|
-
return getQuerySignal(collection, normalized,
|
|
112
|
+
return getQuerySignal(collection, normalized, scopedOptions)
|
|
107
113
|
}
|
|
108
114
|
|
|
109
115
|
subscribe (...items) {
|
|
@@ -140,9 +146,15 @@ class SignalCompat extends Signal {
|
|
|
140
146
|
if (callback != null && typeof callback !== 'function') {
|
|
141
147
|
throw Error('Signal.close() expects callback to be a function')
|
|
142
148
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
const $root = getRoot(this) || this
|
|
150
|
+
const rootId = $root?.[ROOT_ID]
|
|
151
|
+
unregisterRootFinalizer($root)
|
|
152
|
+
disposeRootContext(rootId)
|
|
153
|
+
.then(() => callback?.())
|
|
154
|
+
.catch(err => {
|
|
155
|
+
if (callback) callback(err)
|
|
156
|
+
else console.error(err)
|
|
157
|
+
})
|
|
146
158
|
}
|
|
147
159
|
|
|
148
160
|
silent (value) {
|
|
@@ -542,7 +554,8 @@ class SignalCompat extends Signal {
|
|
|
542
554
|
}
|
|
543
555
|
if (typeof handler !== 'function') throw Error('Signal.on() expects a handler function')
|
|
544
556
|
const normalized = normalizePattern(pattern, 'Signal.on()')
|
|
545
|
-
|
|
557
|
+
const rootId = (getRoot(this) || this)?.[ROOT_ID]
|
|
558
|
+
return onModelEvent(rootId, eventName, normalized, handler)
|
|
546
559
|
}
|
|
547
560
|
if (typeof pattern !== 'function') throw Error('Signal.on() expects a handler function')
|
|
548
561
|
return onCustomEvent(eventName, pattern)
|
|
@@ -572,7 +585,8 @@ class SignalCompat extends Signal {
|
|
|
572
585
|
removeListener (eventName, handler) {
|
|
573
586
|
if (arguments.length !== 2) throw Error('Signal.removeListener() expects two arguments')
|
|
574
587
|
if (eventName === 'change' || eventName === 'all') {
|
|
575
|
-
|
|
588
|
+
const rootId = (getRoot(this) || this)?.[ROOT_ID]
|
|
589
|
+
return removeModelListener(rootId, eventName, handler)
|
|
576
590
|
}
|
|
577
591
|
return removeCustomEventListener(eventName, handler)
|
|
578
592
|
}
|
|
@@ -606,13 +620,21 @@ class SignalCompat extends Signal {
|
|
|
606
620
|
const mirrorOnly = !!($to?.[IS_QUERY] || $to?.[IS_AGGREGATION])
|
|
607
621
|
const { stop, onChange } = createRefLink($from, $to, { mirrorOnly, options })
|
|
608
622
|
store.set(fromPath, { stop })
|
|
623
|
+
const fromRootId = (getRoot($from) || $from)?.[ROOT_ID]
|
|
624
|
+
const toRootId = (getRoot($to) || $to)?.[ROOT_ID]
|
|
609
625
|
if (!mirrorOnly) {
|
|
610
626
|
$from[REF_TARGET] = $to
|
|
611
|
-
setRefLink(fromPath, $to.path(), $from[SEGMENTS], $to[SEGMENTS], {
|
|
627
|
+
setRefLink(fromRootId, fromPath, $to.path(), $from[SEGMENTS], $to[SEGMENTS], {
|
|
628
|
+
mirrorOnly: false,
|
|
629
|
+
fromRootId,
|
|
630
|
+
toRootId
|
|
631
|
+
})
|
|
612
632
|
} else {
|
|
613
|
-
setRefLink(fromPath, $to.path(), $from[SEGMENTS], $to[SEGMENTS], {
|
|
633
|
+
setRefLink(fromRootId, fromPath, $to.path(), $from[SEGMENTS], $to[SEGMENTS], {
|
|
614
634
|
mirrorOnly: true,
|
|
615
|
-
onChange
|
|
635
|
+
onChange,
|
|
636
|
+
fromRootId,
|
|
637
|
+
toRootId
|
|
616
638
|
})
|
|
617
639
|
if ($from[REF_TARGET]) delete $from[REF_TARGET]
|
|
618
640
|
}
|
|
@@ -658,7 +680,8 @@ class SignalCompat extends Signal {
|
|
|
658
680
|
existing.stop()
|
|
659
681
|
store.delete(fromPath)
|
|
660
682
|
}
|
|
661
|
-
|
|
683
|
+
const fromRootId = (getRoot($from) || $from)?.[ROOT_ID]
|
|
684
|
+
removeRefLink(fromRootId, fromPath)
|
|
662
685
|
const $target = resolveRefSignal($from)
|
|
663
686
|
if ($target !== $from) {
|
|
664
687
|
setDiffDeepBypassRef($from, deepCopy($target.get()))
|
|
@@ -725,11 +748,10 @@ function createSilentSignalWrapper ($signal, enabled = true) {
|
|
|
725
748
|
return new Proxy($signal, handler)
|
|
726
749
|
}
|
|
727
750
|
|
|
728
|
-
const REFS = Symbol('compat refs')
|
|
729
751
|
function getRefStore ($signal) {
|
|
730
752
|
const $root = getRoot($signal) || $signal
|
|
731
|
-
$root[
|
|
732
|
-
return
|
|
753
|
+
const rootId = $root?.[ROOT_ID]
|
|
754
|
+
return getRootContext(rootId, true).activeRefs
|
|
733
755
|
}
|
|
734
756
|
|
|
735
757
|
function createRefLink ($from, $to, { mirrorOnly = false } = {}) {
|
|
@@ -790,7 +812,10 @@ function readRefValue ($signal) {
|
|
|
790
812
|
function resolveRefSignal ($signal) {
|
|
791
813
|
const directTarget = resolveRefSignalSafe($signal)
|
|
792
814
|
if (directTarget && directTarget !== $signal) return directTarget
|
|
793
|
-
const resolvedSegments = resolveRefSegmentsSafe(
|
|
815
|
+
const resolvedSegments = resolveRefSegmentsSafe(
|
|
816
|
+
$signal[SEGMENTS],
|
|
817
|
+
(getRoot($signal) || $signal)?.[ROOT_ID]
|
|
818
|
+
)
|
|
794
819
|
if (!resolvedSegments) return $signal
|
|
795
820
|
const $root = getRoot($signal) || $signal
|
|
796
821
|
return resolveSignal($root, resolvedSegments)
|
|
@@ -805,21 +830,28 @@ function forwardRef ($signal, methodName, args) {
|
|
|
805
830
|
function setDiffDeepBypassRef ($signal, value) {
|
|
806
831
|
const segments = $signal[SEGMENTS]
|
|
807
832
|
if (isPublicCollection(segments[0])) return Signal.prototype.set.call($signal, value)
|
|
808
|
-
return
|
|
833
|
+
return setReplacePrivateData(getOwningRootId($signal), segments, value)
|
|
809
834
|
}
|
|
810
835
|
|
|
811
836
|
function mirrorRefMutationFromTarget (targetSegments, value) {
|
|
812
837
|
if (!Array.isArray(targetSegments) || targetSegments.length === 0) return
|
|
813
838
|
const updates = []
|
|
814
|
-
for (const link of
|
|
839
|
+
for (const link of getAllRefLinks()) {
|
|
815
840
|
if (!isPathPrefix(link.toSegments, targetSegments)) continue
|
|
816
841
|
const suffix = targetSegments.slice(link.toSegments.length)
|
|
817
|
-
updates.push({
|
|
842
|
+
updates.push({
|
|
843
|
+
fromRootId: link.fromRootId,
|
|
844
|
+
segments: link.fromSegments.concat(suffix),
|
|
845
|
+
value: deepCopy(value)
|
|
846
|
+
})
|
|
818
847
|
}
|
|
819
848
|
if (!updates.length) return
|
|
820
|
-
const $root = getRootSignal({ rootId: GLOBAL_ROOT_ID, rootFunction: universal$ })
|
|
821
849
|
runInModelEventsSilentContext(() => {
|
|
822
850
|
for (const update of updates) {
|
|
851
|
+
const $root = getRootSignal({
|
|
852
|
+
rootId: update.fromRootId || GLOBAL_ROOT_ID,
|
|
853
|
+
rootFunction: universal$
|
|
854
|
+
})
|
|
823
855
|
const $target = resolveSignal($root, update.segments)
|
|
824
856
|
setDiffDeepBypassRef($target, update.value)
|
|
825
857
|
}
|
|
@@ -891,7 +923,10 @@ function resolveSignal ($signal, segments) {
|
|
|
891
923
|
function resolveSignalWithRefs ($signal, relativeSegments) {
|
|
892
924
|
const baseSegments = Array.isArray($signal?.[SEGMENTS]) ? $signal[SEGMENTS] : []
|
|
893
925
|
const absoluteSegments = baseSegments.concat(relativeSegments)
|
|
894
|
-
const resolvedSegments = resolveRefSegmentsSafe(
|
|
926
|
+
const resolvedSegments = resolveRefSegmentsSafe(
|
|
927
|
+
absoluteSegments,
|
|
928
|
+
(getRoot($signal) || $signal)?.[ROOT_ID]
|
|
929
|
+
)
|
|
895
930
|
if (!resolvedSegments) return resolveSignal($signal, relativeSegments)
|
|
896
931
|
|
|
897
932
|
// Signals created through root functions can carry a raw root in [ROOT].
|
|
@@ -1018,7 +1053,7 @@ function setReplacePrivateCompatSync ($signal, value) {
|
|
|
1018
1053
|
if (isPublicDocPath(segments)) {
|
|
1019
1054
|
value = normalizeIdFields(value, idFields, segments[1])
|
|
1020
1055
|
}
|
|
1021
|
-
|
|
1056
|
+
setReplacePrivateData(getOwningRootId($signal), segments, value)
|
|
1022
1057
|
mirrorRefMutationFromTarget(segments, value)
|
|
1023
1058
|
}
|
|
1024
1059
|
|
|
@@ -1027,7 +1062,7 @@ function delPrivateCompatSync ($signal) {
|
|
|
1027
1062
|
if (segments.length === 0) throw Error('Can\'t delete the root signal data')
|
|
1028
1063
|
const idFields = getIdFieldsForSegments(segments)
|
|
1029
1064
|
if (isIdFieldPath(segments, idFields)) return
|
|
1030
|
-
|
|
1065
|
+
delPrivateData(getOwningRootId($signal), segments)
|
|
1031
1066
|
}
|
|
1032
1067
|
|
|
1033
1068
|
function deepEqualCompat (left, right) {
|
|
@@ -1074,7 +1109,7 @@ async function setReplaceOnSignal ($signal, value) {
|
|
|
1074
1109
|
return result
|
|
1075
1110
|
}
|
|
1076
1111
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1077
|
-
const result =
|
|
1112
|
+
const result = setReplacePrivateData(getOwningRootId($signal), segments, value)
|
|
1078
1113
|
mirrorRefMutationFromTarget(segments, value)
|
|
1079
1114
|
return result
|
|
1080
1115
|
}
|
|
@@ -1094,7 +1129,7 @@ async function incrementOnSignal ($signal, byNumber) {
|
|
|
1094
1129
|
return currentValue + byNumber
|
|
1095
1130
|
}
|
|
1096
1131
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1097
|
-
|
|
1132
|
+
setReplacePrivateData(getOwningRootId($signal), segments, currentValue + byNumber)
|
|
1098
1133
|
return currentValue + byNumber
|
|
1099
1134
|
}
|
|
1100
1135
|
|
|
@@ -1127,7 +1162,7 @@ async function arrayPushOnSignal ($signal, value) {
|
|
|
1127
1162
|
if (isIdFieldPath(segments, idFields)) return
|
|
1128
1163
|
if (isPublicCollection(segments[0])) return _arrayPushPublic(segments, value)
|
|
1129
1164
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1130
|
-
return
|
|
1165
|
+
return arrayPushPrivateData(getOwningRootId($signal), segments, value)
|
|
1131
1166
|
}
|
|
1132
1167
|
|
|
1133
1168
|
async function arrayUnshiftOnSignal ($signal, value) {
|
|
@@ -1136,7 +1171,7 @@ async function arrayUnshiftOnSignal ($signal, value) {
|
|
|
1136
1171
|
if (isIdFieldPath(segments, idFields)) return
|
|
1137
1172
|
if (isPublicCollection(segments[0])) return _arrayUnshiftPublic(segments, value)
|
|
1138
1173
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1139
|
-
return
|
|
1174
|
+
return arrayUnshiftPrivateData(getOwningRootId($signal), segments, value)
|
|
1140
1175
|
}
|
|
1141
1176
|
|
|
1142
1177
|
async function arrayInsertOnSignal ($signal, index, values) {
|
|
@@ -1145,7 +1180,7 @@ async function arrayInsertOnSignal ($signal, index, values) {
|
|
|
1145
1180
|
if (isIdFieldPath(segments, idFields)) return
|
|
1146
1181
|
if (isPublicCollection(segments[0])) return _arrayInsertPublic(segments, index, values)
|
|
1147
1182
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1148
|
-
return
|
|
1183
|
+
return arrayInsertPrivateData(getOwningRootId($signal), segments, index, values)
|
|
1149
1184
|
}
|
|
1150
1185
|
|
|
1151
1186
|
async function arrayPopOnSignal ($signal) {
|
|
@@ -1154,7 +1189,7 @@ async function arrayPopOnSignal ($signal) {
|
|
|
1154
1189
|
if (isIdFieldPath(segments, idFields)) return
|
|
1155
1190
|
if (isPublicCollection(segments[0])) return _arrayPopPublic(segments)
|
|
1156
1191
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1157
|
-
return
|
|
1192
|
+
return arrayPopPrivateData(getOwningRootId($signal), segments)
|
|
1158
1193
|
}
|
|
1159
1194
|
|
|
1160
1195
|
async function arrayShiftOnSignal ($signal) {
|
|
@@ -1163,7 +1198,7 @@ async function arrayShiftOnSignal ($signal) {
|
|
|
1163
1198
|
if (isIdFieldPath(segments, idFields)) return
|
|
1164
1199
|
if (isPublicCollection(segments[0])) return _arrayShiftPublic(segments)
|
|
1165
1200
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1166
|
-
return
|
|
1201
|
+
return arrayShiftPrivateData(getOwningRootId($signal), segments)
|
|
1167
1202
|
}
|
|
1168
1203
|
|
|
1169
1204
|
async function arrayRemoveOnSignal ($signal, index, howMany) {
|
|
@@ -1172,7 +1207,7 @@ async function arrayRemoveOnSignal ($signal, index, howMany) {
|
|
|
1172
1207
|
if (isIdFieldPath(segments, idFields)) return
|
|
1173
1208
|
if (isPublicCollection(segments[0])) return _arrayRemovePublic(segments, index, howMany)
|
|
1174
1209
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1175
|
-
return
|
|
1210
|
+
return arrayRemovePrivateData(getOwningRootId($signal), segments, index, howMany)
|
|
1176
1211
|
}
|
|
1177
1212
|
|
|
1178
1213
|
async function arrayMoveOnSignal ($signal, from, to, howMany) {
|
|
@@ -1181,7 +1216,7 @@ async function arrayMoveOnSignal ($signal, from, to, howMany) {
|
|
|
1181
1216
|
if (isIdFieldPath(segments, idFields)) return
|
|
1182
1217
|
if (isPublicCollection(segments[0])) return _arrayMovePublic(segments, from, to, howMany)
|
|
1183
1218
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1184
|
-
return
|
|
1219
|
+
return arrayMovePrivateData(getOwningRootId($signal), segments, from, to, howMany)
|
|
1185
1220
|
}
|
|
1186
1221
|
|
|
1187
1222
|
async function stringInsertOnSignal ($signal, index, text) {
|
|
@@ -1190,7 +1225,7 @@ async function stringInsertOnSignal ($signal, index, text) {
|
|
|
1190
1225
|
if (isIdFieldPath(segments, idFields)) return
|
|
1191
1226
|
if (isPublicCollection(segments[0])) return _stringInsertPublic(segments, index, text)
|
|
1192
1227
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1193
|
-
return
|
|
1228
|
+
return stringInsertPrivateData(getOwningRootId($signal), segments, index, text)
|
|
1194
1229
|
}
|
|
1195
1230
|
|
|
1196
1231
|
async function stringRemoveOnSignal ($signal, index, howMany) {
|
|
@@ -1199,7 +1234,12 @@ async function stringRemoveOnSignal ($signal, index, howMany) {
|
|
|
1199
1234
|
if (isIdFieldPath(segments, idFields)) return
|
|
1200
1235
|
if (isPublicCollection(segments[0])) return _stringRemovePublic(segments, index, howMany)
|
|
1201
1236
|
if (publicOnly) throw Error(ERRORS.publicOnly)
|
|
1202
|
-
return
|
|
1237
|
+
return stringRemovePrivateData(getOwningRootId($signal), segments, index, howMany)
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
function getOwningRootId ($signal) {
|
|
1241
|
+
const $root = getRoot($signal) || $signal
|
|
1242
|
+
return $root?.[ROOT_ID]
|
|
1203
1243
|
}
|
|
1204
1244
|
|
|
1205
1245
|
function shallowCopy (value) {
|
|
@@ -1246,6 +1286,17 @@ function isAggregationParams (params) {
|
|
|
1246
1286
|
return Boolean(params?.$aggregate || params?.$aggregationName)
|
|
1247
1287
|
}
|
|
1248
1288
|
|
|
1289
|
+
function withQueryScopeOptions (options, $root) {
|
|
1290
|
+
if (!options || typeof options !== 'object') {
|
|
1291
|
+
if (!$root) return options
|
|
1292
|
+
return { root: $root }
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
const nextOptions = { ...options }
|
|
1296
|
+
if (nextOptions.root == null && $root) nextOptions.root = $root
|
|
1297
|
+
return nextOptions
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1249
1300
|
function withFetchOnly (fn) {
|
|
1250
1301
|
const prevFetchOnly = fetchOnly
|
|
1251
1302
|
setFetchOnly(true)
|
|
@@ -54,9 +54,9 @@ export function useOn (eventName, patternOrHandler, handler, deps) {
|
|
|
54
54
|
return
|
|
55
55
|
}
|
|
56
56
|
if (!isModelEventsEnabled()) return
|
|
57
|
-
const listener = onModelEvent(eventName, normalizedPattern, handler)
|
|
57
|
+
const listener = onModelEvent(undefined, eventName, normalizedPattern, handler)
|
|
58
58
|
return () => {
|
|
59
|
-
removeModelListener(eventName, listener)
|
|
59
|
+
removeModelListener(undefined, eventName, listener)
|
|
60
60
|
}
|
|
61
61
|
}, [eventName, patternOrHandler, handler, deps, normalizedPattern, isCustom])
|
|
62
62
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { getRefLinks } from './refRegistry.js'
|
|
1
|
+
import { getRefLinks, getRefRootIds } from './refRegistry.js'
|
|
2
2
|
import { isCompatEnv } from '../compatEnv.js'
|
|
3
3
|
import { isSilentContextActive, isModelEventsSilentContextActive } from './silentContext.js'
|
|
4
|
+
import { normalizeRootId } from '../rootScope.js'
|
|
5
|
+
import { getRootContext, getRootContexts } from '../rootContext.js'
|
|
4
6
|
|
|
5
|
-
const
|
|
6
|
-
change: new Map(),
|
|
7
|
-
all: new Map()
|
|
8
|
-
}
|
|
7
|
+
const MODEL_EVENT_NAMES = ['change', 'all']
|
|
9
8
|
|
|
10
9
|
export function isModelEventsEnabled () {
|
|
11
10
|
return isCompatEnv()
|
|
@@ -20,10 +19,10 @@ export function normalizePattern (pattern, methodName) {
|
|
|
20
19
|
return pattern.split('.').filter(Boolean).join('.')
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
export function onModelEvent (eventName, pattern, handler) {
|
|
22
|
+
export function onModelEvent (rootId, eventName, pattern, handler) {
|
|
24
23
|
if (typeof handler !== 'function') throw Error('Model event handler must be a function')
|
|
25
|
-
if (!
|
|
26
|
-
const store =
|
|
24
|
+
if (!MODEL_EVENT_NAMES.includes(eventName)) throw Error(`Unsupported model event: ${eventName}`)
|
|
25
|
+
const store = getModelEventRootStore(eventName, rootId, true)
|
|
27
26
|
const normalized = normalizePattern(pattern)
|
|
28
27
|
let entry = store.get(normalized)
|
|
29
28
|
if (!entry) {
|
|
@@ -38,8 +37,8 @@ export function onModelEvent (eventName, pattern, handler) {
|
|
|
38
37
|
return handler
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
export function removeModelListener (eventName, handler) {
|
|
42
|
-
const store =
|
|
40
|
+
export function removeModelListener (rootId, eventName, handler) {
|
|
41
|
+
const store = getModelEventRootStore(eventName, rootId)
|
|
43
42
|
if (!store) return
|
|
44
43
|
for (const [pattern, entry] of store) {
|
|
45
44
|
entry.handlers.delete(handler)
|
|
@@ -51,39 +50,44 @@ export function emitModelChange (path, value, prevValue, meta) {
|
|
|
51
50
|
if (!isModelEventsEnabled()) return
|
|
52
51
|
if (isSilentContextActive() || isModelEventsSilentContextActive()) return
|
|
53
52
|
const initialSegments = splitPath(path)
|
|
54
|
-
const visited = new Set()
|
|
55
|
-
const queue = [initialSegments]
|
|
56
53
|
const eventName = meta?.eventName || 'change'
|
|
54
|
+
const rootIds = getTargetRootIds(meta?.rootId)
|
|
55
|
+
|
|
56
|
+
for (const rootId of rootIds) {
|
|
57
|
+
const visited = new Set()
|
|
58
|
+
const queue = [initialSegments]
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
while (queue.length) {
|
|
61
|
+
const segments = queue.shift()
|
|
62
|
+
const key = segments.join('.')
|
|
63
|
+
if (visited.has(key)) continue
|
|
64
|
+
visited.add(key)
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
emitForEvent(rootId, 'change', segments, value, prevValue, meta)
|
|
67
|
+
emitForEvent(rootId, 'all', segments, value, prevValue, meta, eventName)
|
|
66
68
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
for (const link of getRefLinks(rootId).values()) {
|
|
70
|
+
if (!isPathPrefix(link.toSegments, segments)) continue
|
|
71
|
+
if (link.mirrorOnly && typeof link.onChange === 'function') {
|
|
72
|
+
link.onChange()
|
|
73
|
+
}
|
|
74
|
+
const suffix = segments.slice(link.toSegments.length)
|
|
75
|
+
const nextSegments = link.fromSegments.concat(suffix)
|
|
76
|
+
const nextKey = nextSegments.join('.')
|
|
77
|
+
if (!visited.has(nextKey)) queue.push(nextSegments)
|
|
71
78
|
}
|
|
72
|
-
const suffix = segments.slice(link.toSegments.length)
|
|
73
|
-
const nextSegments = link.fromSegments.concat(suffix)
|
|
74
|
-
const nextKey = nextSegments.join('.')
|
|
75
|
-
if (!visited.has(nextKey)) queue.push(nextSegments)
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
export function __resetModelEventsForTests () {
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
for (const context of getRootContexts()) {
|
|
85
|
+
context.resetModelListeners()
|
|
86
|
+
}
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
function emitForEvent (eventName, pathSegments, value, prevValue, meta, resolvedEventName = eventName) {
|
|
86
|
-
const store =
|
|
89
|
+
function emitForEvent (rootId, eventName, pathSegments, value, prevValue, meta, resolvedEventName = eventName) {
|
|
90
|
+
const store = getModelEventRootStore(eventName, rootId)
|
|
87
91
|
if (!store || store.size === 0) return
|
|
88
92
|
for (const entry of store.values()) {
|
|
89
93
|
const captures = matchPattern(entry.segments, pathSegments)
|
|
@@ -103,6 +107,29 @@ function splitPattern (pattern) {
|
|
|
103
107
|
return pattern.split('.').filter(Boolean)
|
|
104
108
|
}
|
|
105
109
|
|
|
110
|
+
function getModelEventRootStore (eventName, rootId, create = false) {
|
|
111
|
+
return getRootContext(normalizeRootId(rootId), create)?.getModelEventStore(eventName, create)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getModelEventRootIds () {
|
|
115
|
+
const rootIds = new Set()
|
|
116
|
+
for (const context of getRootContexts()) {
|
|
117
|
+
for (const store of Object.values(context.modelListeners)) {
|
|
118
|
+
if (store.size) rootIds.add(context.rootId)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return rootIds
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function getTargetRootIds (rootId) {
|
|
125
|
+
if (rootId != null) return [normalizeRootId(rootId)]
|
|
126
|
+
const rootIds = new Set([
|
|
127
|
+
...getModelEventRootIds(),
|
|
128
|
+
...getRefRootIds()
|
|
129
|
+
])
|
|
130
|
+
return rootIds
|
|
131
|
+
}
|
|
132
|
+
|
|
106
133
|
function splitPath (path) {
|
|
107
134
|
if (Array.isArray(path)) return path.map(segment => String(segment))
|
|
108
135
|
if (!path) return []
|