@mblaney/holster 1.0.29 → 1.0.30
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/build/holster.js +3 -3
- package/build/holster.js.map +3 -3
- package/package.json +1 -1
- package/src/ham.js +11 -18
- package/src/holster.js +9 -3
- package/src/user.js +1 -1
- package/src/utils.js +3 -0
- package/src/wire.js +61 -1
- package/test/holster.on.test.js +47 -23
- package/test/holster.user.on.test.js +40 -17
- package/test/system/concurrent-listeners.test.js +5 -4
- package/test/system/listener-before-data.test.js +2 -1
- package/test/system/user-concurrent-listeners.test.js +4 -3
- package/test/system/user-listener-before-data.test.js +2 -1
package/package.json
CHANGED
package/src/ham.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as utils from "./utils.js"
|
|
2
2
|
import SEA from "./sea.js"
|
|
3
3
|
|
|
4
|
-
const LISTENER_DELAY = 100
|
|
5
4
|
// Maximum number of souls to keep in memory
|
|
6
5
|
const MAX_GRAPH_SIZE = 10000
|
|
7
6
|
|
|
@@ -51,6 +50,7 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
51
50
|
const machine = Date.now()
|
|
52
51
|
const now = {}
|
|
53
52
|
const defer = {}
|
|
53
|
+
const listeners = [] // Collect listeners to fire after store.put
|
|
54
54
|
const validProperties = new Map() // Track valid properties per soul
|
|
55
55
|
const validTimestamps = new Map() // Track valid timestamps per soul
|
|
56
56
|
let wait = 0
|
|
@@ -187,30 +187,23 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
187
187
|
graph[soul]._["s"][key] = now[soul]._["s"][key] = node._["s"][key]
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
// updates on a node.
|
|
190
|
+
// Collect event listeners to fire after store.put completes.
|
|
191
|
+
// This ensures listeners always fire with data that's been stored.
|
|
193
192
|
if (listen[soul]) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
.filter(l => utils.match(l["."], key))
|
|
198
|
-
.forEach(l => l.cb())
|
|
199
|
-
}
|
|
200
|
-
}, LISTENER_DELAY)
|
|
193
|
+
listen[soul]
|
|
194
|
+
.filter(l => utils.match(l["."], key))
|
|
195
|
+
.forEach(l => listeners.push(l.cb))
|
|
201
196
|
}
|
|
202
197
|
updated = true
|
|
203
198
|
}
|
|
204
199
|
}
|
|
205
200
|
}
|
|
206
201
|
|
|
207
|
-
//
|
|
202
|
+
// Collect event listeners for update on soul.
|
|
208
203
|
if (updated && listen[soul]) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
}, LISTENER_DELAY)
|
|
204
|
+
listen[soul]
|
|
205
|
+
.filter(l => utils.match(l["."]))
|
|
206
|
+
.forEach(l => listeners.push(l.cb))
|
|
214
207
|
}
|
|
215
208
|
}
|
|
216
209
|
|
|
@@ -228,7 +221,7 @@ Ham.mix = async (change, graph, secure, listen) => {
|
|
|
228
221
|
remove.forEach(({soul}) => delete graph[soul])
|
|
229
222
|
}
|
|
230
223
|
|
|
231
|
-
return {now: now, defer: defer, wait: wait}
|
|
224
|
+
return {now: now, defer: defer, wait: wait, listeners: listeners}
|
|
232
225
|
}
|
|
233
226
|
|
|
234
227
|
export default Ham
|
package/src/holster.js
CHANGED
|
@@ -673,17 +673,23 @@ const Holster = opt => {
|
|
|
673
673
|
const {item, soul} = resolve({off: true}, cb)
|
|
674
674
|
if (!soul) return
|
|
675
675
|
|
|
676
|
-
//
|
|
676
|
+
// Remove listener immediately from the original soul to avoid race condition.
|
|
677
|
+
// The listener may be on a related soul if item is a rel, but we remove
|
|
678
|
+
// from the original soul first since that's where it's initially registered.
|
|
679
|
+
wire.off({"#": soul}, map.get(cb))
|
|
680
|
+
|
|
681
|
+
// Check if item is a rel and remove from the related node as well.
|
|
677
682
|
wire.get({"#": soul, ".": item}, msg => {
|
|
678
683
|
if (msg.err) {
|
|
679
|
-
|
|
684
|
+
// Even if get fails, we've already removed from original soul
|
|
685
|
+
map.delete(cb)
|
|
686
|
+
allctx.delete(ctxid)
|
|
680
687
|
return
|
|
681
688
|
}
|
|
682
689
|
|
|
683
690
|
const current = msg.put && msg.put[soul] && msg.put[soul][item]
|
|
684
691
|
const id = utils.rel.is(current)
|
|
685
692
|
if (id) wire.off({"#": id}, map.get(cb))
|
|
686
|
-
else wire.off({"#": soul}, map.get(cb))
|
|
687
693
|
map.delete(cb)
|
|
688
694
|
allctx.delete(ctxid)
|
|
689
695
|
})
|
package/src/user.js
CHANGED
package/src/utils.js
CHANGED
|
@@ -118,6 +118,9 @@ export const match = (lex, key) => {
|
|
|
118
118
|
|
|
119
119
|
if (typeof lex === "string") return lex === key
|
|
120
120
|
|
|
121
|
+
// Array support: check if key is in the array
|
|
122
|
+
if (Array.isArray(lex)) return lex.includes(key)
|
|
123
|
+
|
|
121
124
|
if (!obj.is(lex) || !key) return false
|
|
122
125
|
|
|
123
126
|
const prefix = lex["*"]
|
package/src/wire.js
CHANGED
|
@@ -197,6 +197,54 @@ const Wire = opt => {
|
|
|
197
197
|
const rateLimiter = createRateLimiter(isTestEnv)
|
|
198
198
|
const connectionManager = createConnectionManager(opt.maxConnections || 1000)
|
|
199
199
|
|
|
200
|
+
// The check function is required because user data must provide a public key
|
|
201
|
+
// so that it can be verified. The public key might verify the provided
|
|
202
|
+
// signature but not actually match the user under which the data is being
|
|
203
|
+
// stored. To avoid this, the current data on a soul needs to be checked to
|
|
204
|
+
// make sure the stored public key matches the one provided with the update.
|
|
205
|
+
const check = async (data, send, cb) => {
|
|
206
|
+
const key = utils.userPublicKey
|
|
207
|
+
|
|
208
|
+
for (const soul of Object.keys(data)) {
|
|
209
|
+
const msg = await new Promise(res => {
|
|
210
|
+
getWithCallback({"#": soul, ".": key}, res, send)
|
|
211
|
+
})
|
|
212
|
+
if (msg.err) {
|
|
213
|
+
if (cb) cb(msg.err)
|
|
214
|
+
return false
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const node = data[soul]
|
|
218
|
+
// If there is no current node then the data is ok to write without
|
|
219
|
+
// matching public keys, as the provided soul also needs a rel on the
|
|
220
|
+
// parent node which then also requires checking. Otherwise public keys
|
|
221
|
+
// need to match for existing data. If the incoming update doesn't
|
|
222
|
+
// include a userPublicKey property at all, allow it - the update is
|
|
223
|
+
// not trying to change the public key.
|
|
224
|
+
if (
|
|
225
|
+
!msg.put ||
|
|
226
|
+
!msg.put[soul] ||
|
|
227
|
+
node[key] === undefined ||
|
|
228
|
+
msg.put[soul][key] === node[key]
|
|
229
|
+
) {
|
|
230
|
+
continue
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// If a soul exists but does not have a public key, then one should not be
|
|
234
|
+
// added because the node is not user data. The above check fails in this
|
|
235
|
+
// case if a public key is provided. Note that this is only an error case
|
|
236
|
+
// if called via the API, which is when a callback is provided here.
|
|
237
|
+
// (The wire spec can fetch and put data on the wire without a signature
|
|
238
|
+
// or public key and this can be ignored.)
|
|
239
|
+
if (cb) {
|
|
240
|
+
cb(`error in wire check public key does not match for soul: ${soul}`)
|
|
241
|
+
}
|
|
242
|
+
return false
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return true
|
|
246
|
+
}
|
|
247
|
+
|
|
200
248
|
const get = (msg, send) => {
|
|
201
249
|
const ack = Get(msg.get, graph)
|
|
202
250
|
if (ack) {
|
|
@@ -241,6 +289,8 @@ const Wire = opt => {
|
|
|
241
289
|
return
|
|
242
290
|
}
|
|
243
291
|
|
|
292
|
+
if (!(await check(update.now, send))) return
|
|
293
|
+
|
|
244
294
|
store.put(update.now, err => {
|
|
245
295
|
send(
|
|
246
296
|
JSON.stringify({
|
|
@@ -249,6 +299,8 @@ const Wire = opt => {
|
|
|
249
299
|
err: err,
|
|
250
300
|
}),
|
|
251
301
|
)
|
|
302
|
+
// Fire listeners after data is stored
|
|
303
|
+
update.listeners.forEach(cb => cb())
|
|
252
304
|
})
|
|
253
305
|
|
|
254
306
|
if (Object.keys(update.defer).length !== 0) {
|
|
@@ -342,6 +394,10 @@ const Wire = opt => {
|
|
|
342
394
|
return
|
|
343
395
|
}
|
|
344
396
|
|
|
397
|
+
if (!(await check(update.now, send, cb))) {
|
|
398
|
+
return
|
|
399
|
+
}
|
|
400
|
+
|
|
345
401
|
// Seed pendingReferences with any new references from API calls
|
|
346
402
|
for (const [soul, node] of Object.entries(update.now)) {
|
|
347
403
|
if (node && typeof node === "object") {
|
|
@@ -355,7 +411,11 @@ const Wire = opt => {
|
|
|
355
411
|
}
|
|
356
412
|
}
|
|
357
413
|
|
|
358
|
-
store.put(update.now,
|
|
414
|
+
store.put(update.now, err => {
|
|
415
|
+
if (cb) cb(err)
|
|
416
|
+
// Fire listeners after data is stored
|
|
417
|
+
update.listeners.forEach(l => l())
|
|
418
|
+
})
|
|
359
419
|
|
|
360
420
|
// Always put data on the wire spec even if no local update needed
|
|
361
421
|
const sendResult = send(
|
package/test/holster.on.test.js
CHANGED
|
@@ -16,13 +16,17 @@ describe("holster.on", () => {
|
|
|
16
16
|
})
|
|
17
17
|
|
|
18
18
|
test("on for property on root then update - event", (t, done) => {
|
|
19
|
-
// Listener
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
// Listener is now called for initial put and updates
|
|
20
|
+
let callCount = 0
|
|
21
|
+
holster.get("key").on(data => {
|
|
22
|
+
callCount++
|
|
23
|
+
if (callCount === 1) {
|
|
24
|
+
assert.equal(data, "value")
|
|
25
|
+
} else if (callCount === 2) {
|
|
26
|
+
assert.equal(data, "update")
|
|
27
|
+
done()
|
|
28
|
+
}
|
|
29
|
+
})
|
|
26
30
|
|
|
27
31
|
holster.get("key").put("value", err => {
|
|
28
32
|
assert.equal(err, null)
|
|
@@ -37,19 +41,27 @@ describe("holster.on", () => {
|
|
|
37
41
|
let done1 = false
|
|
38
42
|
let done2 = false
|
|
39
43
|
|
|
44
|
+
let callback1Count = 0
|
|
40
45
|
const callback1 = data => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
callback1Count++
|
|
47
|
+
if (callback1Count === 2) {
|
|
48
|
+
assert.equal(data, "update")
|
|
49
|
+
holster.get("two").off(callback1)
|
|
50
|
+
if (done2) done()
|
|
51
|
+
else done1 = true
|
|
52
|
+
}
|
|
45
53
|
}
|
|
46
54
|
holster.get("two").on(callback1)
|
|
47
55
|
|
|
56
|
+
let callback2Count = 0
|
|
48
57
|
const callback2 = data => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
callback2Count++
|
|
59
|
+
if (callback2Count === 2) {
|
|
60
|
+
assert.equal(data, "update")
|
|
61
|
+
holster.get("two").off(callback2)
|
|
62
|
+
if (done1) done()
|
|
63
|
+
else done2 = true
|
|
64
|
+
}
|
|
53
65
|
}
|
|
54
66
|
holster.get("two").on(callback2)
|
|
55
67
|
|
|
@@ -80,11 +92,12 @@ describe("holster.on", () => {
|
|
|
80
92
|
holster.get("key3").put("value3", err => {
|
|
81
93
|
assert.equal(err, null)
|
|
82
94
|
|
|
83
|
-
let
|
|
95
|
+
let callCount = 0
|
|
84
96
|
holster.get("key3").on(data => {
|
|
85
|
-
|
|
97
|
+
callCount++
|
|
98
|
+
if (callCount === 1) {
|
|
86
99
|
assert.equal(data, "update1")
|
|
87
|
-
} else {
|
|
100
|
+
} else if (callCount === 2) {
|
|
88
101
|
assert.equal(data, "update2")
|
|
89
102
|
done()
|
|
90
103
|
}
|
|
@@ -95,7 +108,6 @@ describe("holster.on", () => {
|
|
|
95
108
|
})
|
|
96
109
|
|
|
97
110
|
setTimeout(() => {
|
|
98
|
-
first = false
|
|
99
111
|
holster.get("key3").put("update2", err => {
|
|
100
112
|
assert.equal(err, null)
|
|
101
113
|
})
|
|
@@ -112,10 +124,14 @@ describe("holster.on", () => {
|
|
|
112
124
|
|
|
113
125
|
// Need to wait until after all initial puts are done to set on listeners.
|
|
114
126
|
setTimeout(() => {
|
|
127
|
+
let completed = 0
|
|
115
128
|
for (let i = 0; i < 5; i++) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
129
|
+
const idx = i // Capture value
|
|
130
|
+
holster.get("for" + idx).on(data => {
|
|
131
|
+
// Listener fires for updates (initial puts already completed before listener setup)
|
|
132
|
+
assert.equal(data, "update" + idx)
|
|
133
|
+
completed++
|
|
134
|
+
if (completed === 5) done()
|
|
119
135
|
})
|
|
120
136
|
}
|
|
121
137
|
|
|
@@ -154,6 +170,7 @@ describe("holster.on", () => {
|
|
|
154
170
|
.get("hello")
|
|
155
171
|
.next("world!")
|
|
156
172
|
.on(data => {
|
|
173
|
+
// Listener fires for updates (initial put already completed before listener setup)
|
|
157
174
|
assert.equal(data, "update")
|
|
158
175
|
holster.get("hello").next("world!").off()
|
|
159
176
|
done()
|
|
@@ -183,6 +200,7 @@ describe("holster.on", () => {
|
|
|
183
200
|
number: 42,
|
|
184
201
|
}
|
|
185
202
|
holster.get("plain").on(data => {
|
|
203
|
+
// Listener fires for updates (initial put already completed before listener setup)
|
|
186
204
|
assert.deepEqual(data, update)
|
|
187
205
|
done()
|
|
188
206
|
})
|
|
@@ -211,14 +229,20 @@ describe("holster.on", () => {
|
|
|
211
229
|
has: "child update",
|
|
212
230
|
},
|
|
213
231
|
}
|
|
232
|
+
let parentFired = false
|
|
214
233
|
holster.get("nested").on(data => {
|
|
215
|
-
|
|
234
|
+
// Listener fires for updates (initial put already completed before listener setup)
|
|
235
|
+
if (!parentFired) {
|
|
236
|
+
parentFired = true
|
|
237
|
+
assert.deepEqual(data, {key: nested.key, ...update})
|
|
238
|
+
}
|
|
216
239
|
})
|
|
217
240
|
|
|
218
241
|
holster
|
|
219
242
|
.get("nested")
|
|
220
243
|
.next("child")
|
|
221
244
|
.on(data => {
|
|
245
|
+
// Listener fires for updates (initial put already completed before listener setup)
|
|
222
246
|
assert.deepEqual(data, update.child)
|
|
223
247
|
done()
|
|
224
248
|
})
|
|
@@ -31,11 +31,17 @@ describe("holster.user.on", () => {
|
|
|
31
31
|
})
|
|
32
32
|
|
|
33
33
|
test("on for property then update - event", (t, done) => {
|
|
34
|
-
// Listener
|
|
34
|
+
// Listener is now called for initial put and updates
|
|
35
|
+
let callCount = 0
|
|
35
36
|
const callback = data => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
callCount++
|
|
38
|
+
if (callCount === 1) {
|
|
39
|
+
assert.equal(data, "value")
|
|
40
|
+
} else if (callCount === 2) {
|
|
41
|
+
assert.equal(data, "update")
|
|
42
|
+
user.get("key").off(callback)
|
|
43
|
+
done()
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
user.get("key").on(callback)
|
|
41
47
|
|
|
@@ -52,19 +58,27 @@ describe("holster.user.on", () => {
|
|
|
52
58
|
let done1 = false
|
|
53
59
|
let done2 = false
|
|
54
60
|
|
|
61
|
+
let callback1Count = 0
|
|
55
62
|
const callback1 = data => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
callback1Count++
|
|
64
|
+
if (callback1Count === 2) {
|
|
65
|
+
assert.equal(data, "update")
|
|
66
|
+
user.get("two").off(callback1)
|
|
67
|
+
if (done2) done()
|
|
68
|
+
else done1 = true
|
|
69
|
+
}
|
|
60
70
|
}
|
|
61
71
|
user.get("two").on(callback1)
|
|
62
72
|
|
|
73
|
+
let callback2Count = 0
|
|
63
74
|
const callback2 = data => {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
callback2Count++
|
|
76
|
+
if (callback2Count === 2) {
|
|
77
|
+
assert.equal(data, "update")
|
|
78
|
+
user.get("two").off(callback2)
|
|
79
|
+
if (done1) done()
|
|
80
|
+
else done2 = true
|
|
81
|
+
}
|
|
68
82
|
}
|
|
69
83
|
user.get("two").on(callback2)
|
|
70
84
|
|
|
@@ -92,11 +106,12 @@ describe("holster.user.on", () => {
|
|
|
92
106
|
user.get("key3").put("value3", err => {
|
|
93
107
|
assert.equal(err, null)
|
|
94
108
|
|
|
95
|
-
let
|
|
109
|
+
let callCount = 0
|
|
96
110
|
const callback = data => {
|
|
97
|
-
|
|
111
|
+
callCount++
|
|
112
|
+
if (callCount === 1) {
|
|
98
113
|
assert.equal(data, "update1")
|
|
99
|
-
} else {
|
|
114
|
+
} else if (callCount === 2) {
|
|
100
115
|
assert.equal(data, "update2")
|
|
101
116
|
user.get("key3").off(callback)
|
|
102
117
|
done()
|
|
@@ -109,7 +124,6 @@ describe("holster.user.on", () => {
|
|
|
109
124
|
})
|
|
110
125
|
|
|
111
126
|
setTimeout(() => {
|
|
112
|
-
first = false
|
|
113
127
|
user.get("key3").put("update2", err => {
|
|
114
128
|
assert.equal(err, null)
|
|
115
129
|
})
|
|
@@ -132,6 +146,7 @@ describe("holster.user.on", () => {
|
|
|
132
146
|
for (let i = 0; i < 5; i++) {
|
|
133
147
|
const idx = i // Capture value
|
|
134
148
|
const callback = data => {
|
|
149
|
+
// Listener fires for updates (initial puts already completed before listener setup)
|
|
135
150
|
assert.equal(data, "update" + idx)
|
|
136
151
|
user.get("for" + idx).off(callback)
|
|
137
152
|
count++
|
|
@@ -176,6 +191,7 @@ describe("holster.user.on", () => {
|
|
|
176
191
|
assert.equal(err, null)
|
|
177
192
|
|
|
178
193
|
const callback = data => {
|
|
194
|
+
// Listener fires for updates (initial put already completed before listener setup)
|
|
179
195
|
assert.equal(data, "update")
|
|
180
196
|
user.get("hello").next("world!").off(callback)
|
|
181
197
|
done()
|
|
@@ -208,6 +224,7 @@ describe("holster.user.on", () => {
|
|
|
208
224
|
}
|
|
209
225
|
|
|
210
226
|
const callback = data => {
|
|
227
|
+
// Listener fires for updates (initial put already completed before listener setup)
|
|
211
228
|
assert.deepEqual(data, update)
|
|
212
229
|
user.get("plain").off(callback)
|
|
213
230
|
done()
|
|
@@ -240,12 +257,18 @@ describe("holster.user.on", () => {
|
|
|
240
257
|
},
|
|
241
258
|
}
|
|
242
259
|
|
|
260
|
+
let parentFired = false
|
|
243
261
|
const parentCallback = data => {
|
|
244
|
-
|
|
262
|
+
// Listener fires for updates (initial put already completed before listener setup)
|
|
263
|
+
if (!parentFired) {
|
|
264
|
+
parentFired = true
|
|
265
|
+
assert.deepEqual(data, update)
|
|
266
|
+
}
|
|
245
267
|
}
|
|
246
268
|
user.get("nested").on(parentCallback)
|
|
247
269
|
|
|
248
270
|
const childCallback = data => {
|
|
271
|
+
// Listener fires for updates (initial put already completed before listener setup)
|
|
249
272
|
assert.deepEqual(data, update.child)
|
|
250
273
|
user.get("nested").off(parentCallback)
|
|
251
274
|
user.get("nested").next("child").off(childCallback)
|
|
@@ -86,22 +86,23 @@ describe("system - concurrent listeners", () => {
|
|
|
86
86
|
holster.get("race").put({value: "first"}, err => {
|
|
87
87
|
assert.equal(err, null)
|
|
88
88
|
|
|
89
|
-
// Set up listener with
|
|
89
|
+
// Set up listener with _get=true to read existing data
|
|
90
|
+
// The listener will be called with the existing data via the get request
|
|
90
91
|
holster.get("race").on(data => {
|
|
91
92
|
results.push(data?.value)
|
|
92
93
|
}, true)
|
|
93
94
|
|
|
94
|
-
// Immediately put new data
|
|
95
|
+
// Immediately put new data - listener will fire for this put as well
|
|
95
96
|
setTimeout(() => {
|
|
96
97
|
holster.get("race").put({value: "second"}, err => {
|
|
97
98
|
assert.equal(err, null)
|
|
98
99
|
|
|
99
100
|
setTimeout(() => {
|
|
100
|
-
// Should have received both values
|
|
101
|
+
// Should have received both values - one from get, one from put
|
|
101
102
|
assert.equal(
|
|
102
103
|
results.length >= 1,
|
|
103
104
|
true,
|
|
104
|
-
"Should receive at least initial value",
|
|
105
|
+
"Should receive at least initial value from get",
|
|
105
106
|
)
|
|
106
107
|
assert.equal(results[0], "first", "First value should be 'first'")
|
|
107
108
|
done()
|
|
@@ -23,7 +23,8 @@ describe("system - listener before data", () => {
|
|
|
23
23
|
|
|
24
24
|
// Give listener time to fire
|
|
25
25
|
setTimeout(() => {
|
|
26
|
-
//
|
|
26
|
+
// With the new behavior, listener fires after data is stored
|
|
27
|
+
// Should have received the test value from the put
|
|
27
28
|
const nonNull = results.filter(r => r !== null)
|
|
28
29
|
assert.equal(
|
|
29
30
|
nonNull.length >= 1,
|
|
@@ -103,21 +103,22 @@ describe("system - user concurrent listeners", () => {
|
|
|
103
103
|
assert.equal(err, null)
|
|
104
104
|
|
|
105
105
|
// Set up listener with _get=true to read existing data
|
|
106
|
+
// The listener will be called with the existing data via the get request
|
|
106
107
|
user.get("race").on(data => {
|
|
107
108
|
results.push(data?.value)
|
|
108
109
|
}, true)
|
|
109
110
|
|
|
110
|
-
// Immediately put new data
|
|
111
|
+
// Immediately put new data - listener will fire for this put as well
|
|
111
112
|
setTimeout(() => {
|
|
112
113
|
user.get("race").put({value: "second"}, err => {
|
|
113
114
|
assert.equal(err, null)
|
|
114
115
|
|
|
115
116
|
setTimeout(() => {
|
|
116
|
-
// Should have received both values
|
|
117
|
+
// Should have received both values - one from get, one from put
|
|
117
118
|
assert.equal(
|
|
118
119
|
results.length >= 1,
|
|
119
120
|
true,
|
|
120
|
-
"Should receive at least initial value",
|
|
121
|
+
"Should receive at least initial value from get",
|
|
121
122
|
)
|
|
122
123
|
assert.equal(results[0], "first", "First value should be 'first'")
|
|
123
124
|
done()
|
|
@@ -37,7 +37,8 @@ describe("system - user listener before data", () => {
|
|
|
37
37
|
|
|
38
38
|
// Give listener time to fire
|
|
39
39
|
setTimeout(() => {
|
|
40
|
-
//
|
|
40
|
+
// With the new behavior, listener fires after data is stored
|
|
41
|
+
// Should have received the test value from the put
|
|
41
42
|
const nonNull = results.filter(r => r !== null)
|
|
42
43
|
assert.equal(
|
|
43
44
|
nonNull.length >= 1,
|