@nxtedition/deepstream.io-client-js 23.4.3 → 23.4.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/deepstream.io-client-js",
3
- "version": "23.4.3",
3
+ "version": "23.4.5",
4
4
  "description": "the javascript client for deepstream.io",
5
5
  "homepage": "http://deepstream.io",
6
6
  "bugs": {
@@ -19,8 +19,7 @@
19
19
  "scripts": {
20
20
  "_postinstall": "husky install",
21
21
  "prepublishOnly": "pinst --disable",
22
- "postpublish": "pinst --enable",
23
- "test": "jest --bail --watchAll"
22
+ "postpublish": "pinst --enable"
24
23
  },
25
24
  "lint-staged": {
26
25
  "*.{js,jsx,md,ts}": [
@@ -59,27 +58,27 @@
59
58
  "/__tests__"
60
59
  ],
61
60
  "dependencies": {
61
+ "@nxtedition/json-path": "^1.0.3",
62
62
  "bufferutil": "^4.0.7",
63
63
  "component-emitter2": "^1.3.5",
64
64
  "invariant": "^2.2.4",
65
- "utf-8-validate": "^5.0.10",
66
- "ws": "^8.11.0",
67
- "xuid": "^4.1.0",
65
+ "utf-8-validate": "^6.0.3",
66
+ "ws": "^8.13.0",
67
+ "xuid": "^4.1.2",
68
68
  "xxhash-wasm": "^1.0.2"
69
69
  },
70
70
  "devDependencies": {
71
- "eslint": "^8.27.0",
72
- "eslint-config-prettier": "^8.5.0",
71
+ "eslint": "^8.38.0",
72
+ "eslint-config-prettier": "^8.8.0",
73
73
  "eslint-config-standard": "^17.0.0",
74
- "eslint-plugin-import": "^2.26.0",
75
- "eslint-plugin-n": "^15.5.0",
74
+ "eslint-plugin-import": "^2.27.5",
75
+ "eslint-plugin-n": "^15.7.0",
76
76
  "eslint-plugin-node": "^11.1.0",
77
77
  "eslint-plugin-promise": "^6.1.1",
78
- "husky": "^8.0.2",
79
- "jest": "^29.3.0",
80
- "lint-staged": "^13.0.3",
78
+ "husky": "^8.0.3",
79
+ "lint-staged": "^13.2.1",
81
80
  "pinst": "^3.0.0",
82
- "prettier": "^2.7.1"
81
+ "prettier": "^2.8.7"
83
82
  },
84
83
  "peerDependencies": {
85
84
  "rxjs": ">=6.x"
@@ -5,10 +5,11 @@ const C = require('../constants/constants')
5
5
  const rxjs = require('rxjs')
6
6
  const invariant = require('invariant')
7
7
  const EventEmitter = require('component-emitter2')
8
- const jsonPath = require('./json-path')
8
+ const jsonPath = require('@nxtedition/json-path')
9
9
  const utils = require('../utils/utils')
10
10
  const rx = require('rxjs/operators')
11
11
  const xuid = require('xuid')
12
+ const timers = require('../utils/timers')
12
13
 
13
14
  const kEmpty = Symbol('kEmpty')
14
15
 
@@ -180,7 +181,7 @@ class RecordHandler {
180
181
  signal?.removeEventListener('abort', onAbort)
181
182
 
182
183
  if (timeoutHandle) {
183
- clearTimeout(timeoutHandle)
184
+ timers.clearTimeout(timeoutHandle)
184
185
  timeoutHandle = null
185
186
  }
186
187
 
@@ -207,8 +208,7 @@ class RecordHandler {
207
208
  const onTimeout = () => {
208
209
  const elapsed = Date.now() - this._connected
209
210
  if (elapsed < timeoutValue) {
210
- timeoutHandle = setTimeout(onTimeout, timeoutValue - elapsed)
211
- timeoutHandle.unref?.()
211
+ timeoutHandle = timers.setTimeout(onTimeout, timeoutValue - elapsed)
212
212
  } else {
213
213
  for (const rec of records.filter((rec) => !rec.isReady)) {
214
214
  this._client._$onError(C.TOPIC.RECORD, C.EVENT.TIMEOUT, 'record timeout', [
@@ -230,8 +230,7 @@ class RecordHandler {
230
230
  }
231
231
 
232
232
  if (timeoutValue) {
233
- timeoutHandle = setTimeout(onTimeout, timeoutValue)
234
- timeoutHandle.unref?.()
233
+ timeoutHandle = timers.setTimeout(onTimeout, timeoutValue)
235
234
  }
236
235
 
237
236
  signal?.addEventListener('abort', onAbort)
@@ -373,7 +372,7 @@ class RecordHandler {
373
372
  }
374
373
 
375
374
  if (timeoutHandle) {
376
- clearTimeout(timeoutHandle)
375
+ timers.clearTimeout(timeoutHandle)
377
376
  timeoutHandle = null
378
377
  }
379
378
 
@@ -397,7 +396,7 @@ class RecordHandler {
397
396
  const record = this.getRecord(name).subscribe(onUpdate)
398
397
 
399
398
  if (timeoutValue && state && record.state < state) {
400
- timeoutHandle = setTimeout(() => {
399
+ timeoutHandle = timers.setTimeout(() => {
401
400
  const expected = C.RECORD_STATE_NAME[state]
402
401
  const current = C.RECORD_STATE_NAME[record.state]
403
402
  o.error(
@@ -407,7 +406,6 @@ class RecordHandler {
407
406
  )
408
407
  )
409
408
  }, timeoutValue)
410
- timeoutHandle.unref?.()
411
409
  }
412
410
 
413
411
  if (record.version) {
@@ -1,4 +1,4 @@
1
- const jsonPath = require('./json-path')
1
+ const jsonPath = require('@nxtedition/json-path')
2
2
  const utils = require('../utils/utils')
3
3
  const C = require('../constants/constants')
4
4
  const messageParser = require('../message/message-parser')
@@ -0,0 +1,97 @@
1
+ // undici timers
2
+
3
+ 'use strict'
4
+
5
+ let fastNow = Date.now()
6
+ let fastNowTimeout
7
+
8
+ const fastTimers = []
9
+
10
+ function onTimeout() {
11
+ fastNow = Date.now()
12
+
13
+ let len = fastTimers.length
14
+ let idx = 0
15
+ while (idx < len) {
16
+ const timer = fastTimers[idx]
17
+
18
+ if (timer.state === 0) {
19
+ timer.state = fastNow + timer.delay
20
+ } else if (timer.state > 0 && fastNow >= timer.state) {
21
+ timer.state = -1
22
+ timer.callback(timer.opaque)
23
+ }
24
+
25
+ if (timer.state === -1) {
26
+ timer.state = -2
27
+ if (idx !== len - 1) {
28
+ fastTimers[idx] = fastTimers.pop()
29
+ } else {
30
+ fastTimers.pop()
31
+ }
32
+ len -= 1
33
+ } else {
34
+ idx += 1
35
+ }
36
+ }
37
+
38
+ if (fastTimers.length > 0) {
39
+ refreshTimeout()
40
+ }
41
+ }
42
+
43
+ function refreshTimeout() {
44
+ if (fastNowTimeout && fastNowTimeout.refresh) {
45
+ fastNowTimeout.refresh()
46
+ } else {
47
+ clearTimeout(fastNowTimeout)
48
+ fastNowTimeout = setTimeout(onTimeout, 1e3)
49
+ if (fastNowTimeout.unref) {
50
+ fastNowTimeout.unref()
51
+ }
52
+ }
53
+ }
54
+
55
+ class Timeout {
56
+ constructor(callback, delay, opaque) {
57
+ this.callback = callback
58
+ this.delay = delay
59
+ this.opaque = opaque
60
+
61
+ // -2 not in timer list
62
+ // -1 in timer list but inactive
63
+ // 0 in timer list waiting for time
64
+ // > 0 in timer list waiting for time to expire
65
+ this.state = -2
66
+
67
+ this.refresh()
68
+ }
69
+
70
+ refresh() {
71
+ if (this.state === -2) {
72
+ fastTimers.push(this)
73
+ if (!fastNowTimeout || fastTimers.length === 1) {
74
+ refreshTimeout()
75
+ }
76
+ }
77
+
78
+ this.state = 0
79
+ }
80
+
81
+ clear() {
82
+ this.state = -1
83
+ }
84
+ }
85
+
86
+ module.exports = {
87
+ setTimeout(callback, delay, opaque) {
88
+ return delay < 1e3 ? setTimeout(callback, delay, opaque) : new Timeout(callback, delay, opaque)
89
+ },
90
+ clearTimeout(timeout) {
91
+ if (timeout instanceof Timeout) {
92
+ timeout.clear()
93
+ } else {
94
+ clearTimeout(timeout)
95
+ }
96
+ },
97
+ }
@@ -1,353 +0,0 @@
1
- const jsonPath = require('../src/record/json-path')
2
-
3
- describe('equality', () => {
4
- it('deep equal', () => {
5
- const obj = { asd: 123 }
6
- const res = jsonPath.set(
7
- {
8
- obj,
9
- foo: 'true',
10
- },
11
- null,
12
- {
13
- obj,
14
- }
15
- )
16
-
17
- expect(res.obj).toBe(obj)
18
- expect(res).toEqual({ obj })
19
- })
20
-
21
- it('returns old value', () => {
22
- const obj = {
23
- asd: true,
24
- }
25
- const res = jsonPath.set(obj, undefined, {
26
- asd: true,
27
- })
28
-
29
- expect(res).toBe(obj)
30
- })
31
- })
32
-
33
- describe('json clone', () => {
34
- it('types', () => {
35
- expect(jsonPath.jsonClone('ASD')).toEqual('ASD')
36
- expect(jsonPath.jsonClone(null)).toEqual(null)
37
- expect(jsonPath.jsonClone(undefined)).toEqual(undefined)
38
- expect(jsonPath.jsonClone(1)).toEqual(1)
39
- expect(jsonPath.jsonClone(NaN)).toEqual(null)
40
- expect(jsonPath.jsonClone(1.1)).toEqual(1.1)
41
-
42
- const date = new Date()
43
- expect(jsonPath.jsonClone(date)).toEqual(date.toISOString())
44
- })
45
- })
46
-
47
- describe('set', () => {
48
- it('set through null', () => {
49
- const res = jsonPath.set(
50
- {
51
- asd: null,
52
- },
53
- 'asd.foo.bar',
54
- true
55
- )
56
- expect(res).toEqual({
57
- asd: { foo: { bar: true } },
58
- })
59
- })
60
-
61
- it('set empty', () => {
62
- const obj = {
63
- asd: null,
64
- }
65
- const res = jsonPath.set(obj, null, {})
66
- expect(res).toEqual({})
67
- })
68
-
69
- it('set on null', () => {
70
- const res = jsonPath.set(null, null, { sad: true })
71
- expect(res).toEqual({ sad: true })
72
- })
73
-
74
- it('set string', () => {
75
- const res = jsonPath.set({ foo: 'ASD' }, null, { sad: 'ASD' })
76
- expect(res).toEqual({ sad: 'ASD' })
77
- })
78
-
79
- it('set array on object', () => {
80
- const res = jsonPath.set({}, null, [])
81
- expect(res).toEqual([])
82
- })
83
-
84
- it('set array on object plainJSON', () => {
85
- const res = jsonPath.set({}, null, [], true)
86
- expect(res).toEqual([])
87
- })
88
- })
89
-
90
- describe('order', () => {
91
- it('updates order', () => {
92
- const obj = {
93
- foo: 1,
94
- bar: 1,
95
- }
96
- const res = jsonPath.set(obj, undefined, {
97
- bar: 1,
98
- foo: 2,
99
- })
100
-
101
- expect(res).not.toBe(obj)
102
- expect(res).toEqual({ bar: 1, foo: 2 })
103
- })
104
-
105
- it('keeps order', () => {
106
- const obj = {
107
- foo: 1,
108
- bar: 1,
109
- }
110
- const res = jsonPath.set(obj, undefined, {
111
- foo: 1,
112
- bar: 1,
113
- })
114
-
115
- expect(res).toBe(obj)
116
- })
117
- })
118
-
119
- describe('paths are tokenized and retrieved correctly', () => {
120
- const testRecord = {
121
- _$data: {
122
- firstname: 'Wolfram',
123
- lastname: 'Hempel',
124
- address: {
125
- street: 'currentStreet',
126
- },
127
- pastAddresses: [
128
- { street: 'firststreet', postCode: 1001 },
129
- { street: 'secondstreet', postCode: 2002 },
130
- ],
131
- 1234: 'integer index',
132
- },
133
- }
134
-
135
- it('retrieves simple paths', () => {
136
- expect(jsonPath.get(testRecord._$data, 'firstname')).toBe('Wolfram')
137
- })
138
-
139
- it('retrieves nested paths', () => {
140
- expect(jsonPath.get(testRecord._$data, 'address.street')).toBe('currentStreet')
141
- })
142
-
143
- it('retrieves array entries', () => {
144
- expect(jsonPath.get(testRecord._$data, 'pastAddresses[1]')).toEqual({
145
- street: 'secondstreet',
146
- postCode: 2002,
147
- })
148
- })
149
-
150
- it('retrieves other array entries', () => {
151
- expect(jsonPath.get(testRecord._$data, 'pastAddresses[0]')).toEqual({
152
- street: 'firststreet',
153
- postCode: 1001,
154
- })
155
- })
156
-
157
- it('retrieves values from objects within arrays', () => {
158
- expect(jsonPath.get(testRecord._$data, 'pastAddresses[0].postCode')).toBe(1001)
159
- })
160
-
161
- it('handles whitespace', () => {
162
- expect(jsonPath.get(testRecord._$data, ' pastAddresses[ 1 ].postCode ')).toBe(2002)
163
- })
164
-
165
- it('handles integers', () => {
166
- expect(jsonPath.get(testRecord._$data, 1234)).toBe('integer index')
167
- })
168
-
169
- it('returns undefined for non existing keys', () => {
170
- expect(jsonPath.get(testRecord._$data, 'doesNotExist')).toBe(undefined)
171
- })
172
-
173
- it('returns undefined for non existing nested keys', () => {
174
- expect(jsonPath.get(testRecord._$data, 'address.number')).toBe(undefined)
175
- })
176
-
177
- it('returns undefined for existing array indices', () => {
178
- expect(jsonPath.get(testRecord._$data, 'pastAddresses[3]')).toBe(undefined)
179
- })
180
-
181
- it('returns undefined for negative array indices', () => {
182
- expect(jsonPath.get(testRecord._$data, 'pastAddresses[-1]')).toBe(undefined)
183
- })
184
-
185
- it('detects changes', () => {
186
- expect(jsonPath.get(testRecord._$data, 'firstname')).toBe('Wolfram')
187
- testRecord._$data.firstname = 'Egon'
188
- expect(jsonPath.get(testRecord._$data, 'firstname')).toBe('Egon')
189
- })
190
-
191
- it('detects changes to arrays', () => {
192
- expect(jsonPath.get(testRecord._$data, 'pastAddresses[1].street')).toBe('secondstreet')
193
- testRecord._$data.pastAddresses.pop()
194
- expect(jsonPath.get(testRecord._$data, 'pastAddresses[1].street')).toBe(undefined)
195
- })
196
- })
197
-
198
- describe('objects are created from paths and their value is set correctly', () => {
199
- it('sets simple values', () => {
200
- const record = { _$data: {} }
201
- record._$data = jsonPath.set(record._$data, 'firstname', 'Wolfram')
202
- expect(jsonPath.get(record._$data, 'firstname')).toBe('Wolfram')
203
- expect(record._$data).toEqual({ firstname: 'Wolfram' })
204
- })
205
-
206
- it('sets values for nested objects', () => {
207
- const record = { _$data: {} }
208
- record._$data = jsonPath.set(record._$data, 'adress.street', 'someStreet')
209
- expect(jsonPath.get(record._$data, 'adress.street')).toBe('someStreet')
210
- expect(record._$data).toEqual({
211
- adress: {
212
- street: 'someStreet',
213
- },
214
- })
215
- })
216
-
217
- it('sets values for arrays', () => {
218
- const record = { _$data: {} }
219
- record._$data = jsonPath.set(record._$data, 'pastAddresses[1].street', 'someStreet')
220
- expect(jsonPath.get(record._$data, 'pastAddresses[1].street')).toBe('someStreet')
221
- expect(record._$data).toEqual({
222
- pastAddresses: [
223
- undefined,
224
- {
225
- street: 'someStreet',
226
- },
227
- ],
228
- })
229
- })
230
-
231
- it('sets values for null values', () => {
232
- const record = {
233
- _$data: {
234
- job: null,
235
- joinedAt: 1480020987915,
236
- },
237
- }
238
- const jobId = { id: 88 }
239
- record._$data = jsonPath.set(record._$data, 'job', jobId, true)
240
- expect(jsonPath.get(record._$data, 'job.id')).toBe(88)
241
- expect(record._$data).toEqual({
242
- job: { id: 88 },
243
- joinedAt: 1480020987915,
244
- })
245
- })
246
-
247
- it('extends existing objects', () => {
248
- const record = { _$data: { firstname: 'Wolfram' } }
249
- record._$data = jsonPath.set(record._$data, 'lastname', 'Hempel')
250
- expect(jsonPath.get(record._$data, 'lastname')).toBe('Hempel')
251
- expect(record._$data).toEqual({
252
- firstname: 'Wolfram',
253
- lastname: 'Hempel',
254
- })
255
- })
256
-
257
- it('even when the path is not NaNish and could be interpreted as a base 16 number', () => {
258
- let record = {}
259
- const pathName = '0x02335'
260
- record = jsonPath.set(record, pathName, 'value')
261
- expect(record[0]).toBe(undefined)
262
- expect(record[pathName]).toBe('value')
263
- expect(jsonPath.get(record, pathName)).toBe('value')
264
- expect(record[pathName]).toBe('value')
265
- })
266
-
267
- it('extends existing arrays', () => {
268
- const record = {
269
- _$data: {
270
- firstname: 'Wolfram',
271
- animals: ['Bear', 'Cow', 'Ostrich'],
272
- },
273
- }
274
- record._$data = jsonPath.set(record._$data, 'animals[ 1 ]', 'Emu')
275
- expect(jsonPath.get(record._$data, 'animals[ 1 ]')).toBe('Emu')
276
- expect(record._$data).toEqual({
277
- firstname: 'Wolfram',
278
- animals: ['Bear', 'Emu', 'Ostrich'],
279
- })
280
- })
281
- })
282
-
283
- describe('plain JSON', () => {
284
- it('converts into plain JSON', () => {
285
- const time = new Date()
286
- const x = {
287
- a: time,
288
- b: undefined,
289
- c: [undefined],
290
- d: NaN,
291
- e: Infinity,
292
- g: {
293
- a: time,
294
- },
295
- }
296
- const res = jsonPath.set({ a: 1 }, null, x)
297
- expect(res).toEqual({
298
- a: time.toISOString(),
299
- c: [null],
300
- d: null,
301
- e: null,
302
- g: {
303
- a: time.toISOString(),
304
- },
305
- })
306
- expect(res.hasOwnProperty('b')).toEqual(false)
307
- })
308
-
309
- it('undefined is removed from src', () => {
310
- const y = jsonPath.EMPTY
311
- const res = jsonPath.set(y, null, {
312
- b: undefined,
313
- })
314
- expect(Object.keys(res).length).toEqual(0)
315
- expect(res).toBe(y)
316
- })
317
-
318
- it('empty is EMPTY', () => {
319
- const y = {
320
- b: 1,
321
- }
322
- const res = jsonPath.set(y, null, {
323
- b: undefined,
324
- })
325
- expect(Object.keys(res).length).toEqual(0)
326
- expect(res).toBe(jsonPath.EMPTY)
327
- expect(res).toEqual({})
328
- })
329
-
330
- it('stringify date', () => {
331
- const y = {
332
- time: {},
333
- }
334
- const date = new Date()
335
- const res = jsonPath.set(
336
- y,
337
- null,
338
- {
339
- date,
340
- },
341
- false
342
- )
343
- expect(res.date).toEqual(date.toISOString())
344
- })
345
-
346
- it('remove property on undefined', () => {
347
- const y = {
348
- time: {},
349
- }
350
- const res = jsonPath.set(y, 'time', undefined)
351
- expect(Object.keys(res)).toEqual([])
352
- })
353
- })
@@ -1,199 +0,0 @@
1
- const utils = require('../utils/utils')
2
- const PARTS_REG_EXP = /([^.[\]\s]+)/g
3
-
4
- const cache = new Map()
5
- const EMPTY = utils.deepFreeze({})
6
-
7
- const EMPTY_OBJ = EMPTY
8
- const EMPTY_ARR = utils.deepFreeze([])
9
-
10
- function get(data, path) {
11
- data = data || EMPTY
12
-
13
- if (!path) {
14
- return data
15
- }
16
-
17
- const tokens = tokenize(path)
18
-
19
- for (let i = 0; i < tokens.length; i++) {
20
- if (data == null || typeof data !== 'object') {
21
- return undefined
22
- }
23
- data = data[tokens[i]]
24
- }
25
-
26
- return data
27
- }
28
-
29
- function parse(value) {
30
- if (value === '{}') {
31
- return EMPTY_OBJ
32
- } else if (value === '[]') {
33
- return EMPTY_ARR
34
- } else {
35
- return JSON.parse(value)
36
- }
37
- }
38
-
39
- function stringify(value) {
40
- if (value === EMPTY_OBJ) {
41
- return '{}'
42
- } else if (value === EMPTY_ARR) {
43
- return '[]'
44
- } else {
45
- return JSON.stringify(value)
46
- }
47
- }
48
-
49
- function set(data, path, value, isPlainJSON = false) {
50
- data = data || EMPTY
51
-
52
- if (!path) {
53
- return patch(data, value, isPlainJSON)
54
- }
55
-
56
- const oldValue = get(data, path)
57
- const newValue = patch(oldValue, value, isPlainJSON)
58
-
59
- if (newValue === oldValue) {
60
- return data
61
- }
62
-
63
- const result = data ? utils.shallowCopy(data) : {}
64
-
65
- const tokens = tokenize(path)
66
-
67
- let node = result
68
- for (let i = 0; i < tokens.length; i++) {
69
- const token = tokens[i]
70
- if (i === tokens.length - 1) {
71
- if (newValue !== undefined) {
72
- node[token] = newValue
73
- } else {
74
- delete node[token]
75
- }
76
- } else if (node[token] != null && typeof node[token] === 'object') {
77
- node = node[token] = utils.shallowCopy(node[token])
78
- } else if (tokens[i + 1] && !isNaN(tokens[i + 1])) {
79
- node = node[token] = []
80
- } else {
81
- node = node[token] = {}
82
- }
83
- }
84
-
85
- return result
86
- }
87
-
88
- function jsonClone(o) {
89
- if (o == null || typeof o === 'string') {
90
- return o
91
- }
92
-
93
- if (Array.isArray(o) && o.length === 0) {
94
- return EMPTY_ARR
95
- } else if (utils.isPlainObject(o) && Object.keys(o).length === 0) {
96
- return EMPTY_OBJ
97
- }
98
-
99
- return JSON.parse(JSON.stringify(o))
100
- }
101
-
102
- function patch(oldValue, newValue, isPlainJSON) {
103
- if (oldValue === newValue) {
104
- return oldValue
105
- } else if (oldValue === null || newValue === null) {
106
- return isPlainJSON ? newValue : jsonClone(newValue)
107
- } else if (Array.isArray(oldValue) && Array.isArray(newValue)) {
108
- if (newValue.length === 0) {
109
- return EMPTY_ARR
110
- }
111
-
112
- let arr = newValue.length === oldValue.length ? null : []
113
- for (let i = 0; i < newValue.length; i++) {
114
- const value = patch(oldValue[i], newValue[i], isPlainJSON)
115
-
116
- if (!arr) {
117
- if (value === oldValue[i]) {
118
- continue
119
- }
120
- arr = []
121
- for (let j = 0; j < i; ++j) {
122
- arr[j] = oldValue[j]
123
- }
124
- }
125
- // JSON: compat, undefined in array is null
126
- arr[i] = value === undefined ? null : value
127
- }
128
-
129
- return arr || oldValue
130
- } else if (utils.isPlainObject(oldValue, true) && utils.isPlainObject(newValue, isPlainJSON)) {
131
- const newKeys = Object.keys(newValue).filter((key) => newValue[key] !== undefined)
132
- const oldKeys = Object.keys(oldValue)
133
-
134
- if (newKeys.length === 0) {
135
- return oldKeys.length === 0 ? oldValue : EMPTY
136
- }
137
-
138
- let obj = newKeys.length === oldKeys.length ? null : {}
139
- for (let i = 0; i < newKeys.length; ++i) {
140
- const key = newKeys[i]
141
- const val = patch(oldValue[key], newValue[key], isPlainJSON)
142
-
143
- if (!obj) {
144
- if (val === oldValue[key] && key === oldKeys[i]) {
145
- continue
146
- }
147
- obj = {}
148
- for (let j = 0; j < i; j++) {
149
- obj[newKeys[j]] = oldValue[newKeys[j]]
150
- }
151
- }
152
-
153
- if (val !== undefined) {
154
- obj[key] = val
155
- }
156
- }
157
-
158
- return obj || oldValue
159
- } else {
160
- return isPlainJSON ? newValue : jsonClone(newValue)
161
- }
162
- }
163
-
164
- function tokenize(path) {
165
- if (!path) {
166
- return []
167
- }
168
-
169
- if (Array.isArray(path)) {
170
- return path
171
- }
172
-
173
- let parts = cache.get(path)
174
-
175
- if (parts) {
176
- return parts
177
- }
178
-
179
- parts = path && String(path) !== 'undefined' ? String(path).match(PARTS_REG_EXP) : []
180
-
181
- if (!parts) {
182
- throw new Error('invalid path ' + path)
183
- }
184
-
185
- cache.set(path, parts)
186
-
187
- return parts
188
- }
189
-
190
- module.exports = {
191
- EMPTY,
192
- EMPTY_OBJ,
193
- EMPTY_ARR,
194
- parse,
195
- stringify,
196
- get,
197
- set,
198
- jsonClone,
199
- }