@nxtedition/deepstream.io-client-js 23.4.48 → 23.4.50
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/package.json +1 -1
- package/src/constants/constants.js +3 -5
- package/src/record/record-handler.js +19 -41
- package/src/record/record.js +102 -85
package/package.json
CHANGED
|
@@ -10,13 +10,11 @@ module.exports.CONNECTION_STATE.ERROR = 'ERROR'
|
|
|
10
10
|
module.exports.CONNECTION_STATE.RECONNECTING = 'RECONNECTING'
|
|
11
11
|
|
|
12
12
|
module.exports.RECORD_STATE = {}
|
|
13
|
-
module.exports.RECORD_STATE.INIT = -1
|
|
14
13
|
module.exports.RECORD_STATE.VOID = 0
|
|
15
14
|
module.exports.RECORD_STATE.CLIENT = 1
|
|
16
|
-
module.exports.RECORD_STATE.
|
|
17
|
-
module.exports.RECORD_STATE.
|
|
18
|
-
module.exports.RECORD_STATE.
|
|
19
|
-
module.exports.RECORD_STATE.PROVIDER = 5
|
|
15
|
+
module.exports.RECORD_STATE.SERVER = 2
|
|
16
|
+
module.exports.RECORD_STATE.STALE = 3
|
|
17
|
+
module.exports.RECORD_STATE.PROVIDER = 4
|
|
20
18
|
|
|
21
19
|
module.exports.RECORD_STATE_NAME = []
|
|
22
20
|
for (const [key, val] of Object.entries(module.exports.RECORD_STATE)) {
|
|
@@ -35,6 +35,7 @@ class RecordHandler {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
this._syncEmitter = new EventEmitter()
|
|
38
|
+
this._pendingEmitter = new EventEmitter()
|
|
38
39
|
|
|
39
40
|
this.set = this.set.bind(this)
|
|
40
41
|
this.get = this.get.bind(this)
|
|
@@ -49,52 +50,41 @@ class RecordHandler {
|
|
|
49
50
|
|
|
50
51
|
this._pruningTimeout = null
|
|
51
52
|
|
|
52
|
-
// TODO (perf): schedule & yield to avoid blocking event loop?
|
|
53
53
|
const _prune = () => {
|
|
54
54
|
const pruning = this._pruning
|
|
55
|
-
|
|
56
55
|
this._pruning = new Set()
|
|
57
|
-
for (const rec of pruning) {
|
|
58
|
-
invariant(rec.refs === 0, 'cannot prune referenced record')
|
|
59
|
-
invariant(!this._pending.has(rec), 'cannot prune pending record')
|
|
60
56
|
|
|
57
|
+
for (const rec of pruning) {
|
|
61
58
|
rec._$dispose()
|
|
62
59
|
this._records.delete(rec.name)
|
|
63
60
|
this._stats.destroyed++
|
|
64
61
|
}
|
|
65
62
|
|
|
66
|
-
if (this._pruningTimeout
|
|
63
|
+
if (this._pruningTimeout) {
|
|
67
64
|
this._pruningTimeout.refresh()
|
|
68
65
|
} else {
|
|
69
|
-
this._pruningTimeout = setTimeout(_prune, 1e3)
|
|
70
|
-
if (this._pruningTimeout.unref) {
|
|
71
|
-
this._pruningTimeout.unref()
|
|
72
|
-
}
|
|
66
|
+
this._pruningTimeout = timers.setTimeout(_prune, 1e3)
|
|
73
67
|
}
|
|
74
68
|
}
|
|
75
69
|
|
|
76
70
|
_prune()
|
|
77
71
|
}
|
|
78
72
|
|
|
79
|
-
|
|
80
|
-
if (
|
|
73
|
+
_onPruning(rec, isPruning) {
|
|
74
|
+
if (isPruning) {
|
|
81
75
|
this._pruning.add(rec)
|
|
82
|
-
} else
|
|
76
|
+
} else {
|
|
83
77
|
this._pruning.delete(rec)
|
|
84
78
|
}
|
|
85
79
|
}
|
|
86
80
|
|
|
87
|
-
|
|
88
|
-
if (
|
|
89
|
-
rec.state < Record.STATE.SERVER &&
|
|
90
|
-
(prevState === Record.STATE.INIT || prevState >= Record.STATE.SERVER)
|
|
91
|
-
) {
|
|
92
|
-
rec.ref()
|
|
81
|
+
_onPending(rec, isPending) {
|
|
82
|
+
if (isPending) {
|
|
93
83
|
this._pending.add(rec)
|
|
94
|
-
} else
|
|
95
|
-
rec.unref()
|
|
84
|
+
} else {
|
|
96
85
|
this._pending.delete(rec)
|
|
97
86
|
}
|
|
87
|
+
this._pendingEmitter.emit(rec.name, isPending)
|
|
98
88
|
}
|
|
99
89
|
|
|
100
90
|
get connected() {
|
|
@@ -120,7 +110,7 @@ class RecordHandler {
|
|
|
120
110
|
let record = this._records.get(name)
|
|
121
111
|
|
|
122
112
|
if (!record) {
|
|
123
|
-
record = new Record(name, this)
|
|
113
|
+
record = new Record(name, this).ref()
|
|
124
114
|
this._records.set(name, record)
|
|
125
115
|
this._stats.created++
|
|
126
116
|
} else {
|
|
@@ -163,7 +153,7 @@ class RecordHandler {
|
|
|
163
153
|
|
|
164
154
|
sync() {
|
|
165
155
|
return new Promise((resolve) => {
|
|
166
|
-
let counter =
|
|
156
|
+
let counter = this._pending.size
|
|
167
157
|
|
|
168
158
|
const maybeSync = () => {
|
|
169
159
|
if (counter > 0) {
|
|
@@ -178,24 +168,12 @@ class RecordHandler {
|
|
|
178
168
|
}
|
|
179
169
|
}
|
|
180
170
|
|
|
181
|
-
const onUpdate = (rec) => {
|
|
182
|
-
if (rec.state < C.RECORD_STATE.SERVER) {
|
|
183
|
-
return
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
rec.unsubscribe(onUpdate)
|
|
187
|
-
rec.unref()
|
|
188
|
-
counter -= 1
|
|
189
|
-
|
|
190
|
-
maybeSync()
|
|
191
|
-
}
|
|
192
|
-
|
|
193
171
|
for (const rec of this._pending) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
172
|
+
this._pendingEmitter.once(rec.name, (isPending) => {
|
|
173
|
+
invariant(!isPending, 'unexpected pending state')
|
|
174
|
+
counter -= 1
|
|
175
|
+
maybeSync()
|
|
176
|
+
})
|
|
199
177
|
}
|
|
200
178
|
|
|
201
179
|
maybeSync()
|
|
@@ -347,7 +325,7 @@ class RecordHandler {
|
|
|
347
325
|
const record = this.getRecord(name).subscribe(onUpdate)
|
|
348
326
|
|
|
349
327
|
if (timeoutValue && state && record.state < state) {
|
|
350
|
-
timeoutHandle = timers.setTimeout(() => {
|
|
328
|
+
timeoutHandle = timers.timers.setTimeout(() => {
|
|
351
329
|
const expected = C.RECORD_STATE_NAME[state]
|
|
352
330
|
const current = C.RECORD_STATE_NAME[record.state]
|
|
353
331
|
o.error(
|
package/src/record/record.js
CHANGED
|
@@ -16,16 +16,12 @@ class Record {
|
|
|
16
16
|
this._version = ''
|
|
17
17
|
this._data = jsonPath.EMPTY
|
|
18
18
|
this._state = Record.STATE.VOID
|
|
19
|
-
this._refs =
|
|
20
|
-
this._subscribed = false
|
|
19
|
+
this._refs = 0
|
|
21
20
|
this._subscriptions = []
|
|
22
21
|
this._subscriptionsEmitting = false
|
|
23
22
|
this._updating = null
|
|
24
23
|
this._patches = null
|
|
25
|
-
|
|
26
|
-
this._handler._onState(this, Record.STATE.INIT)
|
|
27
|
-
|
|
28
|
-
this._subscribe()
|
|
24
|
+
this._subscribed = false
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
get name() {
|
|
@@ -49,26 +45,27 @@ class Record {
|
|
|
49
45
|
}
|
|
50
46
|
|
|
51
47
|
ref() {
|
|
52
|
-
const prevRefs = this._refs
|
|
53
|
-
|
|
54
48
|
this._refs += 1
|
|
55
|
-
if (this._refs === 1
|
|
56
|
-
this.
|
|
49
|
+
if (this._refs === 1) {
|
|
50
|
+
this._handler._onPruning(this, false)
|
|
57
51
|
}
|
|
58
52
|
|
|
59
|
-
this._handler.
|
|
53
|
+
if (this._refs === 1 && this._handler._connection.connected && !this._subscribed) {
|
|
54
|
+
this._subscribe()
|
|
55
|
+
}
|
|
60
56
|
|
|
61
57
|
return this
|
|
62
58
|
}
|
|
63
59
|
|
|
64
60
|
unref() {
|
|
65
61
|
invariant(this._refs > 0, 'missing refs')
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
invariant(this._refs > 1 || !this._patches, 'must not have patches')
|
|
63
|
+
invariant(this._refs > 1 || this._state >= Record.STATE.SERVER, 'must be ready')
|
|
68
64
|
|
|
69
65
|
this._refs -= 1
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
if (this._refs === 0) {
|
|
67
|
+
this._handler._onPruning(this, true)
|
|
68
|
+
}
|
|
72
69
|
|
|
73
70
|
return this
|
|
74
71
|
}
|
|
@@ -103,7 +100,6 @@ class Record {
|
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
set(pathOrData, dataOrNil) {
|
|
106
|
-
const prevState = this._state
|
|
107
103
|
const prevData = this._data
|
|
108
104
|
const prevVersion = this._version
|
|
109
105
|
|
|
@@ -131,15 +127,20 @@ class Record {
|
|
|
131
127
|
throw new Error('invalid argument: path')
|
|
132
128
|
}
|
|
133
129
|
|
|
134
|
-
this._update(jsonPath.set(this._data, path, data, false))
|
|
135
|
-
|
|
136
130
|
if (this._state < Record.STATE.SERVER) {
|
|
137
|
-
|
|
131
|
+
if (!this._patches) {
|
|
132
|
+
this.ref()
|
|
133
|
+
this._patches = []
|
|
134
|
+
} else if (path) {
|
|
135
|
+
this._patches.splice(0)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
138
|
this._patches.push(path, cloneDeep(data))
|
|
139
|
-
|
|
140
|
-
this.
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
} else {
|
|
140
|
+
this._update(jsonPath.set(this._data, path, data, false))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (this._data !== prevData || this._version !== prevVersion) {
|
|
143
144
|
this._emitUpdate()
|
|
144
145
|
}
|
|
145
146
|
}
|
|
@@ -228,93 +229,97 @@ class Record {
|
|
|
228
229
|
}
|
|
229
230
|
|
|
230
231
|
_$onConnectionStateChange() {
|
|
231
|
-
|
|
232
|
-
if (connection.connected) {
|
|
233
|
-
invariant(!this._subscribed, 'must not be subscribed')
|
|
234
|
-
|
|
232
|
+
if (this._handler._connection.connected) {
|
|
235
233
|
if (this._refs > 0) {
|
|
236
234
|
this._subscribe()
|
|
237
235
|
}
|
|
238
236
|
|
|
239
237
|
if (this._updating) {
|
|
240
238
|
for (const update of this._updating.values()) {
|
|
241
|
-
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, update)
|
|
239
|
+
this._handler._connection.connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, update)
|
|
242
240
|
}
|
|
243
241
|
}
|
|
244
242
|
} else {
|
|
245
|
-
this.
|
|
243
|
+
this._subscribed = false
|
|
246
244
|
}
|
|
247
|
-
}
|
|
248
245
|
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
if (this._state > Record.STATE.CLIENT) {
|
|
247
|
+
this._state = Record.STATE.CLIENT
|
|
248
|
+
this._emitUpdate()
|
|
249
|
+
}
|
|
251
250
|
}
|
|
252
251
|
|
|
253
252
|
_subscribe() {
|
|
254
|
-
|
|
253
|
+
invariant(this._handler._connection.connected, 'must be connected')
|
|
255
254
|
|
|
256
|
-
|
|
257
|
-
|
|
255
|
+
this._handler._connection.sendMsg1(C.TOPIC.RECORD, C.ACTIONS.SUBSCRIBE, this._name)
|
|
256
|
+
this._subscribed = true
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
this._subscribed = true
|
|
262
|
-
}
|
|
258
|
+
this.ref()
|
|
259
|
+
this._handler._onPending(this, true)
|
|
263
260
|
}
|
|
264
261
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
invariant(
|
|
270
|
-
invariant(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
262
|
+
_$dispose() {
|
|
263
|
+
invariant(!this._refs, 'must not have refs')
|
|
264
|
+
invariant(!this._patches, 'must not have patches')
|
|
265
|
+
invariant(!this._updating, 'must not have updates')
|
|
266
|
+
invariant(this.state >= C.RECORD_STATE.SERVER, 'must not be pending')
|
|
267
|
+
invariant(
|
|
268
|
+
!this._subscribed || this._handler._connection.connected,
|
|
269
|
+
'must be unsubscribed or connected'
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if (this._subscribed) {
|
|
273
|
+
this._handler._connection.sendMsg1(C.TOPIC.RECORD, C.ACTIONS.UNSUBSCRIBE, this._name)
|
|
275
274
|
this._subscribed = false
|
|
276
275
|
}
|
|
277
276
|
|
|
278
|
-
if (this._state >
|
|
279
|
-
this._state =
|
|
280
|
-
this._handler._onState(this, prevState)
|
|
277
|
+
if (this._state > C.RECORD_STATE.CLIENT) {
|
|
278
|
+
this._state = C.RECORD_STATE.CLIENT
|
|
281
279
|
this._emitUpdate()
|
|
282
280
|
}
|
|
283
281
|
}
|
|
284
282
|
|
|
285
283
|
_update(nextData) {
|
|
284
|
+
invariant(this._version, 'must have version')
|
|
285
|
+
|
|
286
286
|
if (nextData === this._data) {
|
|
287
|
-
return
|
|
287
|
+
return
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
|
|
290
|
+
const prevVersion = this._version
|
|
291
|
+
const nextVersion = this._makeVersion(parseInt(prevVersion) + 1)
|
|
291
292
|
|
|
292
|
-
|
|
293
|
-
const prevVersion = this._version
|
|
294
|
-
const nextVersion = this._makeVersion(parseInt(prevVersion) + 1)
|
|
295
|
-
this._version = nextVersion
|
|
293
|
+
const update = [this._name, nextVersion, jsonPath.stringify(nextData), prevVersion]
|
|
296
294
|
|
|
297
|
-
|
|
298
|
-
this._updating
|
|
299
|
-
this.
|
|
300
|
-
|
|
295
|
+
if (!this._updating) {
|
|
296
|
+
this._updating = new Map()
|
|
297
|
+
this.ref()
|
|
298
|
+
}
|
|
301
299
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
300
|
+
this._updating.set(nextVersion, update)
|
|
301
|
+
this._handler._stats.updating += 1
|
|
302
|
+
|
|
303
|
+
const connection = this._handler._connection
|
|
304
|
+
if (connection.connected) {
|
|
305
|
+
connection.sendMsg(C.TOPIC.RECORD, C.ACTIONS.UPDATE, update)
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
|
|
308
|
+
this._data = nextData
|
|
309
|
+
this._version = nextVersion
|
|
309
310
|
}
|
|
310
311
|
|
|
311
312
|
_onUpdate([, version, data]) {
|
|
312
|
-
const prevState = this._state
|
|
313
313
|
const prevData = this._data
|
|
314
314
|
const prevVersion = this._version
|
|
315
|
+
const prevState = this._state
|
|
315
316
|
|
|
316
317
|
if (this._updating?.delete(version)) {
|
|
317
318
|
this._handler._stats.updating -= 1
|
|
319
|
+
if (this._updating.size === 0) {
|
|
320
|
+
this._updating = null
|
|
321
|
+
this.unref()
|
|
322
|
+
}
|
|
318
323
|
}
|
|
319
324
|
|
|
320
325
|
if (
|
|
@@ -329,20 +334,26 @@ class Record {
|
|
|
329
334
|
invariant(this._version, 'must have version')
|
|
330
335
|
invariant(this._data, 'must have data')
|
|
331
336
|
|
|
332
|
-
if (this._patches
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
337
|
+
if (this._patches) {
|
|
338
|
+
if (this._version.charAt(0) !== 'I') {
|
|
339
|
+
let patchData = this._data
|
|
340
|
+
for (let n = 0; n < this._patches.length; n += 2) {
|
|
341
|
+
patchData = jsonPath.set(patchData, this._patches[n + 0], this._patches[n + 1], false)
|
|
342
|
+
}
|
|
343
|
+
this._update(patchData)
|
|
336
344
|
}
|
|
337
|
-
|
|
345
|
+
|
|
346
|
+
this._patches = null
|
|
347
|
+
this.unref()
|
|
338
348
|
}
|
|
339
|
-
this._patches = null
|
|
340
349
|
|
|
341
|
-
if (this._state <
|
|
342
|
-
this._state = this._version.charAt(0) === 'I' ?
|
|
343
|
-
this._handler.
|
|
344
|
-
this.
|
|
345
|
-
}
|
|
350
|
+
if (this._state < C.RECORD_STATE.SERVER) {
|
|
351
|
+
this._state = this._version.charAt(0) === 'I' ? C.RECORD_STATE.STALE : C.RECORD_STATE.SERVER
|
|
352
|
+
this._handler._onPending(this, false)
|
|
353
|
+
this.unref()
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (this._state !== prevState || this._data !== prevData || this._version !== prevVersion) {
|
|
346
357
|
this._emitUpdate()
|
|
347
358
|
}
|
|
348
359
|
}
|
|
@@ -353,14 +364,13 @@ class Record {
|
|
|
353
364
|
this._state =
|
|
354
365
|
hasProvider &&
|
|
355
366
|
messageParser.convertTyped(hasProvider, this._handler._client) &&
|
|
356
|
-
this._state >=
|
|
357
|
-
?
|
|
367
|
+
this._state >= C.RECORD_STATE.SERVER
|
|
368
|
+
? C.RECORD_STATE.PROVIDER
|
|
358
369
|
: this._version.charAt(0) === 'I'
|
|
359
|
-
?
|
|
360
|
-
:
|
|
370
|
+
? C.RECORD_STATE.STALE
|
|
371
|
+
: C.RECORD_STATE.SERVER
|
|
361
372
|
|
|
362
373
|
if (this._state !== prevState) {
|
|
363
|
-
this._handler._onState(this, prevState)
|
|
364
374
|
this._emitUpdate()
|
|
365
375
|
}
|
|
366
376
|
}
|
|
@@ -387,7 +397,14 @@ class Record {
|
|
|
387
397
|
this._subscriptionsEmitting = true
|
|
388
398
|
try {
|
|
389
399
|
for (const fn of this._subscriptions) {
|
|
390
|
-
|
|
400
|
+
try {
|
|
401
|
+
fn(this)
|
|
402
|
+
} catch (err) {
|
|
403
|
+
this._error(
|
|
404
|
+
C.EVENT.USER_ERROR,
|
|
405
|
+
Object.assign(new Error('user callback failed'), { cause: err })
|
|
406
|
+
)
|
|
407
|
+
}
|
|
391
408
|
}
|
|
392
409
|
} finally {
|
|
393
410
|
this._subscriptionsEmitting = false
|