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