@nxtedition/deepstream.io-client-js 23.4.4 → 23.4.6
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 +13 -14
- package/src/record/record-handler.js +1 -1
- package/src/record/record.js +1 -1
- package/src/utils/unicast-listener.js +25 -24
- package/__tests__/json-path.test.js +0 -353
- package/src/record/json-path.js +0 -199
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/deepstream.io-client-js",
|
|
3
|
-
"version": "23.4.
|
|
3
|
+
"version": "23.4.6",
|
|
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": "^
|
|
66
|
-
"ws": "^8.
|
|
67
|
-
"xuid": "^4.1.
|
|
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.
|
|
72
|
-
"eslint-config-prettier": "^8.
|
|
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.
|
|
75
|
-
"eslint-plugin-n": "^15.
|
|
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.
|
|
79
|
-
"
|
|
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
|
|
81
|
+
"prettier": "^2.8.7"
|
|
83
82
|
},
|
|
84
83
|
"peerDependencies": {
|
|
85
84
|
"rxjs": ">=6.x"
|
|
@@ -5,7 +5,7 @@ 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('
|
|
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')
|
package/src/record/record.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const C = require('../constants/constants')
|
|
2
|
-
const rx = require('rxjs/operators')
|
|
3
2
|
const rxjs = require('rxjs')
|
|
4
3
|
|
|
5
4
|
class Listener {
|
|
@@ -12,26 +11,7 @@ class Listener {
|
|
|
12
11
|
this._connection = this._handler._connection
|
|
13
12
|
this._subscriptions = new Map()
|
|
14
13
|
this._stringify = stringify || JSON.stringify
|
|
15
|
-
this.
|
|
16
|
-
rx.map((value) => {
|
|
17
|
-
let data
|
|
18
|
-
if (value && typeof value === 'string') {
|
|
19
|
-
if (value.charAt(0) !== '{' && value.charAt(0) !== '[') {
|
|
20
|
-
throw new Error(`invalid value: ${value}`)
|
|
21
|
-
}
|
|
22
|
-
data = value
|
|
23
|
-
} else if (value && typeof value === 'object') {
|
|
24
|
-
data = this._stringify(value)
|
|
25
|
-
} else {
|
|
26
|
-
throw new Error(`invalid value: ${value}`)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const hash = this._connection.hasher.h64ToString(data)
|
|
30
|
-
|
|
31
|
-
return { data, hash }
|
|
32
|
-
}),
|
|
33
|
-
rx.distinctUntilKeyChanged('hash')
|
|
34
|
-
)
|
|
14
|
+
this._value = null
|
|
35
15
|
|
|
36
16
|
this._$onConnectionStateChange()
|
|
37
17
|
|
|
@@ -79,9 +59,30 @@ class Listener {
|
|
|
79
59
|
}
|
|
80
60
|
|
|
81
61
|
if (value$) {
|
|
82
|
-
const subscription = value$.
|
|
83
|
-
next: (
|
|
84
|
-
|
|
62
|
+
const subscription = value$.subscribe({
|
|
63
|
+
next: (value) => {
|
|
64
|
+
let data
|
|
65
|
+
if (value && typeof value === 'string') {
|
|
66
|
+
if (value.charAt(0) !== '{' && value.charAt(0) !== '[') {
|
|
67
|
+
throw new Error(`invalid value: ${value}`)
|
|
68
|
+
}
|
|
69
|
+
data = value
|
|
70
|
+
} else if (value && typeof value === 'object') {
|
|
71
|
+
data = this._stringify(value)
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error(`invalid value: ${value}`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (value === this._value) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this._value = value
|
|
81
|
+
this._connection.sendMsg(this._topic, C.ACTIONS.UPDATE, [
|
|
82
|
+
name,
|
|
83
|
+
`INF-${this._connection.hasher.h64ToString(data)}`,
|
|
84
|
+
data,
|
|
85
|
+
])
|
|
85
86
|
},
|
|
86
87
|
error: (err) => {
|
|
87
88
|
this._error(name, err)
|
|
@@ -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
|
-
})
|
package/src/record/json-path.js
DELETED
|
@@ -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
|
-
}
|