@live-change/dao 0.3.5 → 0.3.9
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/browser.js +4 -0
- package/index.js +3 -0
- package/lib/DaoPrerenderCache.js +47 -10
- package/lib/ExtendedObservableList.js +225 -0
- package/lib/Observable.js +15 -14
- package/lib/ObservableProxy.js +3 -2
- package/lib/Path.js +8 -3
- package/lib/collectPointers.js +33 -8
- package/package.json +1 -1
- package/tests/pointers-collector.js +24 -19
package/browser.js
CHANGED
|
@@ -14,6 +14,10 @@ import ObservableList from "./lib/ObservableList.js"
|
|
|
14
14
|
rd.ObservableList = ObservableList
|
|
15
15
|
export { ObservableList }
|
|
16
16
|
|
|
17
|
+
import ExtendedObservableList from "./lib/ExtendedObservableList.js"
|
|
18
|
+
rd.ExtendedObservableList = ExtendedObservableList
|
|
19
|
+
export { ExtendedObservableList }
|
|
20
|
+
|
|
17
21
|
import DaoPrerenderCache from "./lib/DaoPrerenderCache.js"
|
|
18
22
|
const ReactiveCache = DaoPrerenderCache // backward compatibility
|
|
19
23
|
rd.ReactiveCache = DaoPrerenderCache // backward compatibility
|
package/index.js
CHANGED
|
@@ -11,6 +11,9 @@ Dao.ObservableValue = ObservableValue
|
|
|
11
11
|
const ObservableList = require("./lib/ObservableList.js")
|
|
12
12
|
Dao.ObservableList = ObservableList
|
|
13
13
|
|
|
14
|
+
const ExtendedObservableList = require("./lib/ExtendedObservableList.js")
|
|
15
|
+
Dao.ExtendedObservableList = ExtendedObservableList
|
|
16
|
+
|
|
14
17
|
const ReactiveServer = require("./lib/ReactiveServer.js")
|
|
15
18
|
Dao.ReactiveServer = ReactiveServer
|
|
16
19
|
|
package/lib/DaoPrerenderCache.js
CHANGED
|
@@ -6,12 +6,21 @@ class DaoPrerenderCache {
|
|
|
6
6
|
constructor(dao, mode) {
|
|
7
7
|
this.dao = dao
|
|
8
8
|
this.cache = new Map()
|
|
9
|
+
this.extendedCache = new Map()
|
|
9
10
|
this.mode = mode
|
|
10
11
|
this.observables = new Map()
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
+
|
|
13
14
|
setCache(data) {
|
|
14
15
|
this.cache = new Map(data)
|
|
16
|
+
for(const [keyJson, value] of data) {
|
|
17
|
+
const key = JSON.parse(keyJson)
|
|
18
|
+
if(key.paths) {
|
|
19
|
+
for(const { what, data } of value) {
|
|
20
|
+
this.extendedCache.set(JSON.stringify(what), data)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
15
24
|
for(const [what, observable] of this.observables.entries()) {
|
|
16
25
|
if(observable.isDisposed()) {
|
|
17
26
|
observable.set(this.cache.get(what))
|
|
@@ -35,7 +44,12 @@ class DaoPrerenderCache {
|
|
|
35
44
|
observable = this.dao.observable(what)
|
|
36
45
|
}
|
|
37
46
|
this.observables.set(cacheKey, observable)
|
|
38
|
-
if
|
|
47
|
+
if(this.cache.has(cacheKey)) observable.restore(this.cache.get(cacheKey))
|
|
48
|
+
if(this.extendedCache.has(cacheKey)) {
|
|
49
|
+
observable.restore(this.extendedCache.get(cacheKey))
|
|
50
|
+
return observable
|
|
51
|
+
// do not save extended values
|
|
52
|
+
}
|
|
39
53
|
if(observable.isInitialized && observable.isInitialized()) {
|
|
40
54
|
if(this.mode == 'save') {
|
|
41
55
|
this.cache.set(cacheKey, observable.save())
|
|
@@ -43,34 +57,57 @@ class DaoPrerenderCache {
|
|
|
43
57
|
return observable
|
|
44
58
|
}
|
|
45
59
|
if(this.mode == 'load') {
|
|
46
|
-
//if (this.cache.has(cacheKey)) observable.restore(this.cache.get(cacheKey))
|
|
60
|
+
// if (this.cache.has(cacheKey)) observable.restore(this.cache.get(cacheKey))
|
|
61
|
+
// it was loaded earlier
|
|
47
62
|
}
|
|
48
63
|
return observable
|
|
49
64
|
}
|
|
50
|
-
|
|
65
|
+
|
|
51
66
|
get(what) {
|
|
52
67
|
const cacheKey = JSON.stringify(what)
|
|
53
68
|
debug("GET", cacheKey)
|
|
54
|
-
if
|
|
69
|
+
if(this.cache.has(cacheKey)) {
|
|
55
70
|
const value = this.cache.get(cacheKey)
|
|
56
71
|
debug("GET FROM CACHE", cacheKey, " => ", value)
|
|
57
72
|
return Promise.resolve(value)
|
|
58
73
|
}
|
|
74
|
+
if(this.extendedCache.has(cacheKey)) {
|
|
75
|
+
const value = this.extendedCache.get(cacheKey)
|
|
76
|
+
debug("GET FROM EXTENDED CACHE", cacheKey, " => ", value)
|
|
77
|
+
return Promise.resolve(value)
|
|
78
|
+
}
|
|
59
79
|
if(this.mode == 'load') {
|
|
60
80
|
}
|
|
61
81
|
const promise = this.dao.get(what)
|
|
62
82
|
if(this.mode == 'save') {
|
|
63
83
|
if(!promise) throw new Error("GET NOT FOUND: "+what)
|
|
64
|
-
promise.then(result => {
|
|
84
|
+
promise.then(result => {
|
|
65
85
|
let observable = this.observables.get(cacheKey)
|
|
66
86
|
if(observable) {
|
|
67
|
-
if(typeof observable == 'function')
|
|
68
|
-
|
|
69
|
-
|
|
87
|
+
if(typeof observable == 'function') {
|
|
88
|
+
observable('set', result)
|
|
89
|
+
} else if(observable.notify) {
|
|
90
|
+
observable.notify('set', result)
|
|
91
|
+
} else {
|
|
92
|
+
observable.set(result)
|
|
70
93
|
}
|
|
71
|
-
observable.set(result)
|
|
72
94
|
}
|
|
73
95
|
this.cache.set(cacheKey, result)
|
|
96
|
+
if(what.paths) {
|
|
97
|
+
for(const { what, data } of result) {
|
|
98
|
+
let observable = this.observables.get(cacheKey)
|
|
99
|
+
if(observable) {
|
|
100
|
+
if(typeof observable == 'function') {
|
|
101
|
+
observable('set', data)
|
|
102
|
+
} else if(observable.notify) {
|
|
103
|
+
observable.notify('set', data)
|
|
104
|
+
} else {
|
|
105
|
+
observable.set(data)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.extendedCache.set(JSON.stringify(what), data)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
74
111
|
})
|
|
75
112
|
}
|
|
76
113
|
return promise
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
const ObservableList = require("./ObservableList.js")
|
|
2
|
+
|
|
3
|
+
class ExtendedObservableList extends ObservableList {
|
|
4
|
+
constructor(observableList, elementActivator, elementDispose, valueActivator = observableList.valueActivator) {
|
|
5
|
+
let list = observableList.list
|
|
6
|
+
if(elementActivator) {
|
|
7
|
+
list = Array.isArray(list) ? list.map(elementActivator) : elementActivator(list)
|
|
8
|
+
}
|
|
9
|
+
super(list, undefined, undefined, valueActivator)
|
|
10
|
+
|
|
11
|
+
this.observableList = observableList
|
|
12
|
+
this.elementActivator = elementActivator
|
|
13
|
+
this.elementDispose = elementDispose
|
|
14
|
+
this.valueActivator = valueActivator
|
|
15
|
+
|
|
16
|
+
this.savedError = null
|
|
17
|
+
this.properties = []
|
|
18
|
+
this.errorProperties = []
|
|
19
|
+
|
|
20
|
+
this.observableList.observe(this)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
dispose() {
|
|
24
|
+
this.observableList.unobserve(this)
|
|
25
|
+
if(this.elementDispose) {
|
|
26
|
+
if(Array.isArray(this.list)) {
|
|
27
|
+
for(const disposed of this.list) this.elementDispose(disposed)
|
|
28
|
+
} else {
|
|
29
|
+
this.elementDispose(this.list)
|
|
30
|
+
}
|
|
31
|
+
this.list = undefined
|
|
32
|
+
}
|
|
33
|
+
ObservableList.dispose.apply(this)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
respawn() {
|
|
37
|
+
ObservableList.respawn.apply(this)
|
|
38
|
+
this.observableList.observe(this)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
extend(elementFunc, elementDispose) {
|
|
42
|
+
const extendedList = new ExtendedObservableList(
|
|
43
|
+
this.value, null, null, this.valueActivator, elementFunc, elementDispose)
|
|
44
|
+
const oldDispose = extendedList.dispose
|
|
45
|
+
const oldRespawn = extendedList.respawn
|
|
46
|
+
|
|
47
|
+
this.observe(extendedList)
|
|
48
|
+
extendedList.dispose = () => {
|
|
49
|
+
this.unobserve(extendedList)
|
|
50
|
+
oldDispose.apply(extendedList)
|
|
51
|
+
}
|
|
52
|
+
extendedList.respawn = () => {
|
|
53
|
+
oldRespawn.apply(extendedList)
|
|
54
|
+
this.observe(extendedList)
|
|
55
|
+
}
|
|
56
|
+
return extendedList
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
set(list) {
|
|
60
|
+
if(list === this.list) return;
|
|
61
|
+
try {
|
|
62
|
+
if (JSON.stringify(list) == JSON.stringify(this.list)) return;
|
|
63
|
+
} catch(e) {}
|
|
64
|
+
if(this.elementDispose) {
|
|
65
|
+
if(Array.isArray(this.list)) {
|
|
66
|
+
for(const disposed of this.list) this.elementDispose(disposed)
|
|
67
|
+
} else {
|
|
68
|
+
this.elementDispose(this.list)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if(this.elementActivator) {
|
|
72
|
+
list = Array.isArray(list) ? list.map(this.elementActivator) : this.elementActivator(list)
|
|
73
|
+
}
|
|
74
|
+
this.list = this.valueActivator ? this.valueActivator(list) : list
|
|
75
|
+
this.fireObservers('set', list)
|
|
76
|
+
for(const [object, property] of this.properties) {
|
|
77
|
+
object[property] = this.list
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
push(value) {
|
|
82
|
+
if(this.elementActivator) value = this.elementActivator(value)
|
|
83
|
+
this.list.push(value)
|
|
84
|
+
this.fireObservers('push', value)
|
|
85
|
+
}
|
|
86
|
+
unshift(value) {
|
|
87
|
+
if(this.elementActivator) value = this.elementActivator(value)
|
|
88
|
+
this.list.unshift(value)
|
|
89
|
+
this.fireObservers('unshift', value)
|
|
90
|
+
}
|
|
91
|
+
pop() {
|
|
92
|
+
if(this.elementDispose) this.elementDispose(this.list[this.list.length - 1])
|
|
93
|
+
this.list.pop()
|
|
94
|
+
this.fireObservers('pop')
|
|
95
|
+
}
|
|
96
|
+
shift() {
|
|
97
|
+
if(this.elementDispose) this.elementDispose(this.list[0])
|
|
98
|
+
this.list.shift()
|
|
99
|
+
this.fireObservers('shift')
|
|
100
|
+
}
|
|
101
|
+
splice(at, del, ...values) {
|
|
102
|
+
const removed = this.list.splice(at, del, ...values)
|
|
103
|
+
if(this.elementDispose) for(const disposed of removed) this.elementDispose(dispose)
|
|
104
|
+
this.fireObservers('splice', at, del, ...values)
|
|
105
|
+
}
|
|
106
|
+
putByField(field, value, element, reverse = false, oldElement) {
|
|
107
|
+
if(this.elementActivator) element = this.elementActivator(element)
|
|
108
|
+
if(!reverse) {
|
|
109
|
+
let i, l
|
|
110
|
+
for(i = 0, l = this.list.length; i < l; i++) {
|
|
111
|
+
if(this.list[i][field] == value) {
|
|
112
|
+
oldElement = this.list[i]
|
|
113
|
+
if(this.elementDispose) this.elementDispose(oldElement)
|
|
114
|
+
this.list.splice(i, 1, element)
|
|
115
|
+
break
|
|
116
|
+
} else if(this.list[i][field] > value) {
|
|
117
|
+
this.list.splice(i, 0, element)
|
|
118
|
+
break
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if(i == l) this.list.push(element)
|
|
122
|
+
} else {
|
|
123
|
+
let i
|
|
124
|
+
for(i = this.list.length-1; i >= 0; i--) {
|
|
125
|
+
if(this.list[i][field] == value) {
|
|
126
|
+
oldElement = this.list[i]
|
|
127
|
+
if(this.elementDispose) this.elementDispose(oldElement)
|
|
128
|
+
this.list.splice(i, 1, element)
|
|
129
|
+
break
|
|
130
|
+
} else if(this.list[i][field] > value) {
|
|
131
|
+
this.list.splice(i + 1, 0, element)
|
|
132
|
+
break
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if(i < 0) this.list.splice(0, 0, element)
|
|
136
|
+
}
|
|
137
|
+
this.fireObservers('putByField', field, value, element, reverse, oldElement)
|
|
138
|
+
}
|
|
139
|
+
remove(exact) {
|
|
140
|
+
let json = JSON.stringify(exact)
|
|
141
|
+
for(let i = 0, l = this.list.length; i < l; i++) {
|
|
142
|
+
if(JSON.stringify(this.list[i]) == json) {
|
|
143
|
+
if(this.elementDispose) this.elementDispose(this.list[i])
|
|
144
|
+
this.list.splice(i, 1)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
this.fireObservers('remove', exact)
|
|
148
|
+
}
|
|
149
|
+
removeByField(field, value, oldElement) {
|
|
150
|
+
let json = JSON.stringify(value)
|
|
151
|
+
for(let i = 0, l = this.list.length; i < l; i++) {
|
|
152
|
+
if(JSON.stringify(this.list[i][field]) == json) {
|
|
153
|
+
oldElement = this.list[i]
|
|
154
|
+
if(this.elementDispose) this.elementDispose(oldElement)
|
|
155
|
+
this.list.splice(i, 1)
|
|
156
|
+
i--
|
|
157
|
+
l--
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
this.fireObservers('removeByField', field, value, oldElement)
|
|
161
|
+
}
|
|
162
|
+
removeBy(fields) {
|
|
163
|
+
let jsonf = []
|
|
164
|
+
for(var k in fields) {
|
|
165
|
+
jsonf.push([k, JSON.stringify(fields[k])])
|
|
166
|
+
}
|
|
167
|
+
for(let i = 0, l = this.list.length; i < l; i++) {
|
|
168
|
+
let found = true
|
|
169
|
+
for(let [key, json] of jsonf) {
|
|
170
|
+
found = found && (JSON.stringify(this.list[i][key]) == json)
|
|
171
|
+
}
|
|
172
|
+
if(found) {
|
|
173
|
+
if(this.elementDispose) this.elementDispose(this.list[i])
|
|
174
|
+
this.list.splice(i, 1)
|
|
175
|
+
i--
|
|
176
|
+
l--
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
this.fireObservers('removeBy', fields)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
update(exact, element) {
|
|
183
|
+
let json = JSON.stringify(exact)
|
|
184
|
+
for(let i = 0, l = this.list.length; i < l; i++) {
|
|
185
|
+
if(JSON.stringify(this.list[i]) == json) {
|
|
186
|
+
if(this.elementDispose) this.elementDispose(this.list[i])
|
|
187
|
+
if(this.elementActivator) element = this.elementActivator(element)
|
|
188
|
+
this.list.splice(i, 1, element)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
this.fireObservers('update', exact, element)
|
|
192
|
+
}
|
|
193
|
+
updateByField(field, value, element) {
|
|
194
|
+
let json = JSON.stringify(value)
|
|
195
|
+
for(let i = 0, l = this.list.length; i < l; i++) {
|
|
196
|
+
if(JSON.stringify(this.list[i][field]) == json) {
|
|
197
|
+
if(this.elementDispose) this.elementDispose(this.list[i])
|
|
198
|
+
if(this.elementActivator) element = this.elementActivator(element)
|
|
199
|
+
this.list.splice(i, 1, element)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
this.fireObservers('updateByField', field, value, element)
|
|
203
|
+
}
|
|
204
|
+
updateBy(fields, element) {
|
|
205
|
+
let jsonf = []
|
|
206
|
+
for(const k in fields) {
|
|
207
|
+
jsonf.push([k, JSON.stringify(fields[k])])
|
|
208
|
+
}
|
|
209
|
+
for(let i = 0, l = this.list.length; i < l; i++) {
|
|
210
|
+
let found = true
|
|
211
|
+
for(let [key, json] of jsonf) {
|
|
212
|
+
found = found && (JSON.stringify(this.list[i][key]) == json)
|
|
213
|
+
}
|
|
214
|
+
if(found) {
|
|
215
|
+
if(this.elementDispose) this.elementDispose(this.list[i])
|
|
216
|
+
if(this.elementActivator) element = this.elementActivator(element)
|
|
217
|
+
this.list.splice(i, 1, element)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
this.fireObservers('updateBy', fields, element)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
module.exports = ExtendedObservableList
|
package/lib/Observable.js
CHANGED
|
@@ -90,21 +90,22 @@ class Observable {
|
|
|
90
90
|
|
|
91
91
|
wait() {
|
|
92
92
|
let finished = false
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
93
|
+
|
|
94
|
+
const waitPromise = new Promise((resolve, reject) => {
|
|
95
|
+
const resultObserver = (signal) => {
|
|
96
|
+
finished = true
|
|
97
|
+
this.unobserve(resultObserver)
|
|
98
|
+
this.uncatch(errorObserver)
|
|
99
|
+
resolve(signal)
|
|
100
|
+
}
|
|
101
|
+
const errorObserver = (error) => {
|
|
102
|
+
finished = true
|
|
103
|
+
this.unobserve(resultObserver)
|
|
104
|
+
this.uncatch(errorObserver)
|
|
105
|
+
reject(error)
|
|
106
|
+
}
|
|
107
107
|
if(!finished) this.catch(errorObserver)
|
|
108
|
+
if(!finished) this.observe(resultObserver)
|
|
108
109
|
})
|
|
109
110
|
waitPromise.cancel = () => {
|
|
110
111
|
finished = true
|
package/lib/ObservableProxy.js
CHANGED
|
@@ -15,6 +15,7 @@ class ObservableProxy extends Observable {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
setTarget(observable) {
|
|
18
|
+
if(this === observable) throw new Error('infinite loop')
|
|
18
19
|
if(!this.disposed && this.observable) {
|
|
19
20
|
this.observable.unobserve(this.observer)
|
|
20
21
|
for(let [object, property] of this.properties) {
|
|
@@ -80,8 +81,8 @@ class ObservableProxy extends Observable {
|
|
|
80
81
|
unbindProperty(object, property) {
|
|
81
82
|
for(var i = 0; i < this.properties.length; i++) {
|
|
82
83
|
var prop = this.properties[i]
|
|
83
|
-
if(prop[0]
|
|
84
|
-
this.properties.splice(i,1)
|
|
84
|
+
if(prop[0] === object && prop[1] === property) {
|
|
85
|
+
this.properties.splice(i, 1)
|
|
85
86
|
if(this.observable) this.observable.unbindProperty(object, property)
|
|
86
87
|
if(this.isUseless()) this.dispose()
|
|
87
88
|
return
|
package/lib/Path.js
CHANGED
|
@@ -35,16 +35,16 @@ function resolve(schema) {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
class Path {
|
|
38
|
-
constructor(what, more = undefined) {
|
|
38
|
+
constructor(what, more = undefined, to = undefined) {
|
|
39
39
|
this.what = what
|
|
40
40
|
this.more = more
|
|
41
|
+
this.to = to
|
|
41
42
|
}
|
|
42
43
|
with(...funcs) {
|
|
43
44
|
let newMore = this.more ? this.more.slice() : []
|
|
44
45
|
for(const func of funcs) {
|
|
45
46
|
const source = sourceProxy()
|
|
46
47
|
const fetchObject = func(source)
|
|
47
|
-
const what = fetchObject.what
|
|
48
48
|
const path = fetchObject.what.slice(0, -1)
|
|
49
49
|
const params = fetchObject.what[fetchObject.what.length - 1]
|
|
50
50
|
let processedParams = {}
|
|
@@ -57,7 +57,8 @@ class Path {
|
|
|
57
57
|
}
|
|
58
58
|
const more = {
|
|
59
59
|
schema: [[...path, { object: processedParams }]],
|
|
60
|
-
more: fetchObject.more
|
|
60
|
+
more: fetchObject.more,
|
|
61
|
+
to: fetchObject.to
|
|
61
62
|
}
|
|
62
63
|
newMore.push(more)
|
|
63
64
|
}
|
|
@@ -71,6 +72,10 @@ class Path {
|
|
|
71
72
|
schema: resolve(outputObject)
|
|
72
73
|
}
|
|
73
74
|
}
|
|
75
|
+
|
|
76
|
+
bind(to) {
|
|
77
|
+
return new Path(this.what, this.more, to)
|
|
78
|
+
}
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
module.exports = Path
|
package/lib/collectPointers.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
function flatMap(source, fun) {
|
|
2
2
|
let results = source.map(fun)
|
|
3
3
|
let count = 0
|
|
4
|
-
|
|
4
|
+
let many = source.many
|
|
5
|
+
for(let result of results) {
|
|
6
|
+
count += result.length
|
|
7
|
+
many = many || result.many
|
|
8
|
+
}
|
|
9
|
+
if(count > 1 && !many) throw new Error("too many results from not many")
|
|
5
10
|
let out = new Array(count)
|
|
11
|
+
out.many = many
|
|
6
12
|
let p = 0
|
|
7
13
|
for(let result of results) {
|
|
8
14
|
for(let element of result) out[p++] = element
|
|
@@ -12,12 +18,12 @@ function flatMap(source, fun) {
|
|
|
12
18
|
|
|
13
19
|
function getPropertyValues(source, property) {
|
|
14
20
|
if(Array.isArray(source)) {
|
|
15
|
-
return flatMap(source, v => getPropertyValues(v, property))
|
|
21
|
+
return flatMap(markMany(source), v => getPropertyValues(v, property))
|
|
16
22
|
} else {
|
|
17
23
|
if(source === undefined) return []
|
|
18
24
|
if(source === null) return []
|
|
19
25
|
let v = source[property]
|
|
20
|
-
if(Array.isArray(v)) return v
|
|
26
|
+
if(Array.isArray(v)) return markMany(v)
|
|
21
27
|
if(v === undefined) return []
|
|
22
28
|
return [v]
|
|
23
29
|
}
|
|
@@ -31,10 +37,22 @@ function getNestedPropertyValues(source, property) {
|
|
|
31
37
|
return accumulator
|
|
32
38
|
}
|
|
33
39
|
|
|
40
|
+
function markMany(arr) {
|
|
41
|
+
arr = arr.slice()
|
|
42
|
+
arr.many = true
|
|
43
|
+
return arr
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
function cross(lists) {
|
|
35
47
|
let count = 1
|
|
36
|
-
|
|
48
|
+
let many = false
|
|
49
|
+
for(let list of lists) {
|
|
50
|
+
count *= list.length
|
|
51
|
+
many = many || list.many
|
|
52
|
+
}
|
|
37
53
|
let out = new Array(count)
|
|
54
|
+
out.many = many
|
|
55
|
+
if(count > 1 && !many) throw new Error("more than one result for non array fields")
|
|
38
56
|
for(let i = 0; i < count; i++) {
|
|
39
57
|
let res = new Array(lists.length)
|
|
40
58
|
let a = i
|
|
@@ -68,10 +86,13 @@ function collect(source, schema, getSource) {
|
|
|
68
86
|
return results
|
|
69
87
|
})
|
|
70
88
|
} else if(schema.nonEmpty) {
|
|
71
|
-
|
|
89
|
+
const collected = collect(source, schema.nonEmpty, getSource)
|
|
90
|
+
const result = collected.filter(x=>!!x)
|
|
91
|
+
result.many = collected.many
|
|
92
|
+
return result
|
|
72
93
|
} else if(schema.identity) {
|
|
73
94
|
if(typeof source == 'undefined' || source === null) return []
|
|
74
|
-
return Array.isArray(source) ? source : [source]
|
|
95
|
+
return Array.isArray(source) ? markMany(source) : [source]
|
|
75
96
|
} else if(schema.array) {
|
|
76
97
|
return [ collect(source, schema.array, getSource) ]
|
|
77
98
|
} else if(schema.property) {
|
|
@@ -112,6 +133,7 @@ function collect(source, schema, getSource) {
|
|
|
112
133
|
}
|
|
113
134
|
results[i] = result
|
|
114
135
|
}
|
|
136
|
+
results.many = crossed.many
|
|
115
137
|
return results
|
|
116
138
|
}
|
|
117
139
|
}
|
|
@@ -120,9 +142,12 @@ function collect(source, schema, getSource) {
|
|
|
120
142
|
function collectPointers(source, schemas, getSource) {
|
|
121
143
|
let results = []
|
|
122
144
|
for(let schema of schemas) {
|
|
123
|
-
|
|
145
|
+
const collected = collect(source, schema, getSource)
|
|
146
|
+
const many = results.many || collected.many
|
|
147
|
+
results = results.concat(collected)
|
|
148
|
+
results.many = many
|
|
124
149
|
}
|
|
125
150
|
return results
|
|
126
151
|
}
|
|
127
152
|
|
|
128
|
-
module.exports = collectPointers
|
|
153
|
+
module.exports = collectPointers
|
package/package.json
CHANGED
|
@@ -5,43 +5,47 @@ test("pointers collector", (t) => {
|
|
|
5
5
|
t.plan(14)
|
|
6
6
|
|
|
7
7
|
t.test("simple property", (t) => {
|
|
8
|
-
t.plan(
|
|
8
|
+
t.plan(2)
|
|
9
9
|
let pointers = ReactiveDao.collectPointers({
|
|
10
10
|
user: "123"
|
|
11
11
|
},[
|
|
12
12
|
["users", "User", { property: "user" }]
|
|
13
13
|
])
|
|
14
|
-
t.
|
|
14
|
+
t.equal(!!pointers.many, false)
|
|
15
|
+
t.deepEqual(pointers.slice(), [["users","User","123"]], "found one user")
|
|
15
16
|
})
|
|
16
17
|
|
|
17
18
|
t.test("simple property from array", (t) => {
|
|
18
|
-
t.plan(
|
|
19
|
+
t.plan(2)
|
|
19
20
|
let pointers = ReactiveDao.collectPointers([
|
|
20
21
|
{ user: "123" },
|
|
21
22
|
{ user: "233" }
|
|
22
23
|
],[
|
|
23
24
|
["users", "User", { property: "user" }]
|
|
24
25
|
])
|
|
25
|
-
t.
|
|
26
|
+
t.equal(!!pointers.many, true)
|
|
27
|
+
t.deepEqual(pointers.slice(), [["users","User","123"], ["users","User","233"]], "found two users")
|
|
26
28
|
})
|
|
27
29
|
|
|
28
30
|
t.test("identity pointers", (t) => {
|
|
29
|
-
t.plan(
|
|
31
|
+
t.plan(2)
|
|
30
32
|
let pointers = ReactiveDao.collectPointers([ 0, 1, 2, 3 ], [
|
|
31
33
|
[ 'test', 'user', { identity: true } ]
|
|
32
34
|
])
|
|
33
|
-
t.
|
|
35
|
+
t.equal(!!pointers.many, true)
|
|
36
|
+
t.deepEqual(pointers.slice(), [ [ 'test', 'user', 0 ], [ 'test', 'user', 1 ], [ 'test', 'user', 2 ],
|
|
34
37
|
[ 'test', 'user', 3 ] ])
|
|
35
38
|
})
|
|
36
39
|
|
|
37
40
|
t.test("array property tags", (t) => {
|
|
38
|
-
t.plan(
|
|
41
|
+
t.plan(2)
|
|
39
42
|
let pointers = ReactiveDao.collectPointers({
|
|
40
43
|
tags: ["1", "2", "3"]
|
|
41
44
|
},[
|
|
42
45
|
["tags", "Tag", { property: "tags" }]
|
|
43
46
|
])
|
|
44
|
-
t.
|
|
47
|
+
t.equal(!!pointers.many, true)
|
|
48
|
+
t.deepEqual(pointers.slice(), [
|
|
45
49
|
["tags","Tag","1"],
|
|
46
50
|
["tags","Tag","2"],
|
|
47
51
|
["tags","Tag","3"]
|
|
@@ -49,7 +53,7 @@ test("pointers collector", (t) => {
|
|
|
49
53
|
})
|
|
50
54
|
|
|
51
55
|
t.test("nested property", (t) => {
|
|
52
|
-
t.plan(
|
|
56
|
+
t.plan(2)
|
|
53
57
|
let pointers = ReactiveDao.collectPointers({
|
|
54
58
|
userData: {
|
|
55
59
|
country: "PL"
|
|
@@ -57,7 +61,8 @@ test("pointers collector", (t) => {
|
|
|
57
61
|
},[
|
|
58
62
|
["country", { property: ["userData", "country"] }]
|
|
59
63
|
])
|
|
60
|
-
t.
|
|
64
|
+
t.equal(!!pointers.many, false)
|
|
65
|
+
t.deepEqual(pointers.slice(), [["country","PL"]], "found nested property value")
|
|
61
66
|
})
|
|
62
67
|
|
|
63
68
|
t.test("object result", (t) => {
|
|
@@ -72,7 +77,7 @@ test("pointers collector", (t) => {
|
|
|
72
77
|
} },
|
|
73
78
|
{ object: { path: ["tags", { property: "tags" }] } }
|
|
74
79
|
])
|
|
75
|
-
t.deepEqual(pointers, [
|
|
80
|
+
t.deepEqual(pointers.slice(), [
|
|
76
81
|
{ path: ["user", { user: "123" }], next: [[ "picture", { property: "picture" } ]] },
|
|
77
82
|
{ path: ["tags","1"] },
|
|
78
83
|
{ path: ["tags","2"] },
|
|
@@ -91,7 +96,7 @@ test("pointers collector", (t) => {
|
|
|
91
96
|
{ source: 'interests', schema: { array: { identity: true } }},
|
|
92
97
|
{ source: { static: 'city' }, schema: { property: "name" } }]
|
|
93
98
|
], (src) => sources[src])
|
|
94
|
-
t.deepEqual(pointers, [ [ 'findProjects', [ 'cats', 'dogs', 'birds' ], 'NY' ] ])
|
|
99
|
+
t.deepEqual(JSON.parse(JSON.stringify(pointers)), [ [ 'findProjects', [ 'cats', 'dogs', 'birds' ], 'NY' ] ])
|
|
95
100
|
})
|
|
96
101
|
|
|
97
102
|
t.test("undefined argument", (t) => {
|
|
@@ -99,7 +104,7 @@ test("pointers collector", (t) => {
|
|
|
99
104
|
let pointers = ReactiveDao.collectPointers({ sessionId: 1 },[
|
|
100
105
|
["users", "User", { property: "user" }]
|
|
101
106
|
])
|
|
102
|
-
t.deepEqual(pointers, [])
|
|
107
|
+
t.deepEqual(pointers.slice(), [])
|
|
103
108
|
})
|
|
104
109
|
|
|
105
110
|
t.test("undefined argument in object", (t) => {
|
|
@@ -107,7 +112,7 @@ test("pointers collector", (t) => {
|
|
|
107
112
|
let pointers = ReactiveDao.collectPointers({ sessionId: 1 },[
|
|
108
113
|
["users", "User", { object: { user: { property: "user" }}}]
|
|
109
114
|
])
|
|
110
|
-
t.deepEqual(pointers, [])
|
|
115
|
+
t.deepEqual(pointers.slice(), [])
|
|
111
116
|
})
|
|
112
117
|
|
|
113
118
|
t.test("switch match", (t) => {
|
|
@@ -120,7 +125,7 @@ test("pointers collector", (t) => {
|
|
|
120
125
|
US: "New York"
|
|
121
126
|
}}]
|
|
122
127
|
])
|
|
123
|
-
t.deepEqual(pointers, [["Warsaw"]], "switch working")
|
|
128
|
+
t.deepEqual(pointers.slice(), [["Warsaw"]], "switch working")
|
|
124
129
|
})
|
|
125
130
|
|
|
126
131
|
t.test("switch default", (t) => {
|
|
@@ -134,7 +139,7 @@ test("pointers collector", (t) => {
|
|
|
134
139
|
},
|
|
135
140
|
default: "London"}]
|
|
136
141
|
])
|
|
137
|
-
t.deepEqual(pointers, [["London"]], "switch working")
|
|
142
|
+
t.deepEqual(pointers.slice(), [["London"]], "switch working")
|
|
138
143
|
})
|
|
139
144
|
|
|
140
145
|
t.test("switch not match", (t) => {
|
|
@@ -147,7 +152,7 @@ test("pointers collector", (t) => {
|
|
|
147
152
|
US: "New York"
|
|
148
153
|
}}]
|
|
149
154
|
])
|
|
150
|
-
t.deepEqual(pointers, [], "switch working")
|
|
155
|
+
t.deepEqual(pointers.slice(), [], "switch working")
|
|
151
156
|
})
|
|
152
157
|
|
|
153
158
|
t.test("test nonEmpty", (t) => {
|
|
@@ -157,7 +162,7 @@ test("pointers collector", (t) => {
|
|
|
157
162
|
},[
|
|
158
163
|
["users", "User", { nonEmpty: { property: "user" }}]
|
|
159
164
|
])
|
|
160
|
-
t.deepEqual(pointers, [], "nulls are filtered")
|
|
165
|
+
t.deepEqual(pointers.slice(), [], "nulls are filtered")
|
|
161
166
|
})
|
|
162
167
|
|
|
163
168
|
t.test("test complex property fetch", (t) => {
|
|
@@ -187,7 +192,7 @@ test("pointers collector", (t) => {
|
|
|
187
192
|
category: { nonEmpty: { property: ['data', 'sidebarItems','category'] } }
|
|
188
193
|
} }]
|
|
189
194
|
])
|
|
190
|
-
t.deepEqual(pointers, [
|
|
195
|
+
t.deepEqual(pointers.slice(), [
|
|
191
196
|
[
|
|
192
197
|
"categories",
|
|
193
198
|
"CategoryOne",
|