@live-change/db 0.9.61 → 0.9.63
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/lib/ChangeStream.js +100 -6
- package/lib/Index.js +40 -0
- package/lib/queryGet.js +32 -7
- package/lib/queryObservable.js +38 -9
- package/lib/utils.js +41 -0
- package/package.json +5 -5
package/lib/ChangeStream.js
CHANGED
|
@@ -4,21 +4,47 @@ class ChangeStream {
|
|
|
4
4
|
onChange() {
|
|
5
5
|
throw new Error("abstract method - not implemented")
|
|
6
6
|
}
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
async rangeGet(range) {
|
|
8
|
+
throw new Error("abstract method - not implemented")
|
|
9
|
+
}
|
|
10
|
+
range(range) {
|
|
11
|
+
throw new Error("abstract method - not implemented")
|
|
12
|
+
}
|
|
13
|
+
async objectGet(id) {
|
|
14
|
+
throw new Error("abstract method - not implemented")
|
|
15
|
+
}
|
|
16
|
+
object(id) {
|
|
17
|
+
throw new Error("abstract method - not implemented")
|
|
9
18
|
}
|
|
19
|
+
async count(range = {}) {
|
|
20
|
+
throw new Error("abstract method - not implemented")
|
|
21
|
+
}
|
|
22
|
+
async to(output) {
|
|
23
|
+
return this.onChange(async (obj, oldObj, id, timestamp) => {
|
|
24
|
+
if(obj || oldObj) await output.change(obj, oldObj, id, timestamp)
|
|
25
|
+
})
|
|
26
|
+
await this.observerPromise
|
|
27
|
+
}
|
|
10
28
|
filter(func) {
|
|
11
29
|
const pipe = new ChangeStreamPipe()
|
|
12
|
-
const observerPromise = this.onChange((obj, oldObj, id, timestamp) =>
|
|
13
|
-
|
|
30
|
+
const observerPromise = this.onChange(async (obj, oldObj, id, timestamp) => {
|
|
31
|
+
const newObj = obj && await func(obj)
|
|
32
|
+
const newOldObj = oldObj && await func(oldObj)
|
|
33
|
+
if(!(newObj || newOldObj)) return
|
|
34
|
+
pipe.change(newObj, newOldObj, id, timestamp)
|
|
35
|
+
})
|
|
14
36
|
pipe.master = this
|
|
15
37
|
pipe.observerPromise = observerPromise
|
|
16
38
|
return pipe
|
|
17
39
|
}
|
|
18
40
|
map(func) {
|
|
19
41
|
const pipe = new ChangeStreamPipe()
|
|
20
|
-
const observerPromise = this.onChange((obj, oldObj, id, timestamp) =>
|
|
21
|
-
|
|
42
|
+
const observerPromise = this.onChange(async (obj, oldObj, id, timestamp) => {
|
|
43
|
+
const newObj = obj && await func(obj)
|
|
44
|
+
const newOldObj = oldObj && await func(oldObj)
|
|
45
|
+
if(!(newObj || newOldObj)) return
|
|
46
|
+
pipe.change(newObj, newOldObj, id, timestamp)
|
|
47
|
+
})
|
|
22
48
|
pipe.master = this
|
|
23
49
|
pipe.observerPromise = observerPromise
|
|
24
50
|
return pipe
|
|
@@ -42,6 +68,74 @@ class ChangeStream {
|
|
|
42
68
|
pipe.observerPromise = observerPromise
|
|
43
69
|
return pipe
|
|
44
70
|
}
|
|
71
|
+
async readInBuckets(bucketCallback, bucketSize = 128) {
|
|
72
|
+
let position = ''
|
|
73
|
+
let readed = 0
|
|
74
|
+
do {
|
|
75
|
+
const bucket = await this.rangeGet({ gt: position, limit: bucketSize })
|
|
76
|
+
readed = bucket.length
|
|
77
|
+
if(!bucket.length) break
|
|
78
|
+
position = bucket[bucket.length - 1].id
|
|
79
|
+
await bucketCallback(bucket)
|
|
80
|
+
} while(readed === bucketSize)
|
|
81
|
+
}
|
|
82
|
+
cross(other, selfToRange, otherToRange, bucketSize = 128) {
|
|
83
|
+
const pipe = new ChangeStreamPipe()
|
|
84
|
+
const observerPromise = this.onChange(async (obj, oldObj, id, timestamp) => {
|
|
85
|
+
const otherRange = await selfToRange(obj || oldObj)
|
|
86
|
+
if(!otherRange) return // ignore
|
|
87
|
+
if(typeof otherRange === 'string') { // single id
|
|
88
|
+
const otherObj = await other.objectGet(otherRange)
|
|
89
|
+
await pipe.change([obj, otherObj], [oldObj, otherObj], [id, otherRange], timestamp)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
await other.range(otherRange).readInBuckets(async bucket => {
|
|
93
|
+
for(const otherObj of bucket) {
|
|
94
|
+
const otherId = otherObj.id
|
|
95
|
+
await pipe.change([obj, otherObj], [oldObj, otherObj], [id, otherId], timestamp)
|
|
96
|
+
}
|
|
97
|
+
}, bucketSize)
|
|
98
|
+
})
|
|
99
|
+
const otherObserverPromise = other.onChange(async (otherObj, oldOtherObj, id, timestamp) => {
|
|
100
|
+
const selfRange = await otherToRange(otherObj || oldOtherObj)
|
|
101
|
+
if(!selfRange) return // ignore
|
|
102
|
+
const otherId = id
|
|
103
|
+
if(typeof selfRange === 'string') { // single id
|
|
104
|
+
const obj = await this.objectGet(selfRange)
|
|
105
|
+
await pipe.change([obj, otherObj], [obj, oldOtherObj], [selfRange, otherId], timestamp)
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
await this.range(selfRange).readInBuckets(async bucket => {
|
|
109
|
+
for(const obj of bucket) {
|
|
110
|
+
const id = obj.id
|
|
111
|
+
await pipe.change([obj, otherObj], [obj, oldOtherObj], [id, otherId], timestamp)
|
|
112
|
+
}
|
|
113
|
+
}, bucketSize)
|
|
114
|
+
})
|
|
115
|
+
pipe.master = this
|
|
116
|
+
pipe.observerPromise = Promise.all([observerPromise, otherObserverPromise])
|
|
117
|
+
return pipe
|
|
118
|
+
}
|
|
119
|
+
groupExisting(objectToRange) {
|
|
120
|
+
const pipe = new ChangeStreamPipe()
|
|
121
|
+
const observerPromise = this.onChange(async (obj, oldObj, id, timestamp) => {
|
|
122
|
+
const existingObj = obj || oldObj
|
|
123
|
+
let range = await objectToRange(existingObj)
|
|
124
|
+
if(!range) return
|
|
125
|
+
if(typeof range === 'string') {
|
|
126
|
+
range = { gte: range, lte: range + '\xFF\xFF\xFF\xFF' }
|
|
127
|
+
}
|
|
128
|
+
const count = await this.count(range)
|
|
129
|
+
if(count) {
|
|
130
|
+
await pipe.change(existingObj, null, id, timestamp)
|
|
131
|
+
} else {
|
|
132
|
+
await pipe.change(null, existingObj, id, timestamp)
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
pipe.master = this
|
|
136
|
+
pipe.observerPromise = observerPromise
|
|
137
|
+
return pipe
|
|
138
|
+
}
|
|
45
139
|
}
|
|
46
140
|
|
|
47
141
|
class ChangeStreamPipe extends ChangeStream {
|
package/lib/Index.js
CHANGED
|
@@ -6,6 +6,7 @@ import queryGet from './queryGet.js'
|
|
|
6
6
|
import profileLog from './profileLog.js'
|
|
7
7
|
import nextTick from 'next-tick'
|
|
8
8
|
import { ChangeStream } from './ChangeStream.js'
|
|
9
|
+
import { rangeIntersection } from './utils.js'
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
import Debug from 'debug'
|
|
@@ -37,6 +38,21 @@ class ObjectReader extends ChangeStream {
|
|
|
37
38
|
async get() {
|
|
38
39
|
return await (await this.tableReader.table).objectGet(this.id)
|
|
39
40
|
}
|
|
41
|
+
async rangeGet(range) {
|
|
42
|
+
return await (await this.tableReader.table).rangeGet(rangeIntersection(unitRange(this.id), range))
|
|
43
|
+
}
|
|
44
|
+
range(range) {
|
|
45
|
+
return new RangeReader(this.tableReader, unitRange(this.id))
|
|
46
|
+
}
|
|
47
|
+
object(id) {
|
|
48
|
+
return new ObjectReader(this.tableReader, id)
|
|
49
|
+
}
|
|
50
|
+
async objectGet(id) {
|
|
51
|
+
return await (await this.tableReader.table).objectGet(id)
|
|
52
|
+
}
|
|
53
|
+
async count(range = {}) {
|
|
54
|
+
return await (await this.tableReader.table).countGet(rangeIntersection(unitRange(this.id), range))
|
|
55
|
+
}
|
|
40
56
|
dispose() {}
|
|
41
57
|
}
|
|
42
58
|
|
|
@@ -63,9 +79,24 @@ class RangeReader extends ChangeStream {
|
|
|
63
79
|
async change(obj, oldObj, id, timestamp) {
|
|
64
80
|
for(const callback of this.callbacks) await callback(obj, oldObj, id, timestamp)
|
|
65
81
|
}
|
|
82
|
+
async rangeGet(range) {
|
|
83
|
+
return await (await this.tableReader.table).rangeGet(rangeIntersection(this.range, range))
|
|
84
|
+
}
|
|
85
|
+
range(range) {
|
|
86
|
+
return new RangeReader(this.tableReader, rangeIntersection(this.range, range))
|
|
87
|
+
}
|
|
66
88
|
async get() {
|
|
67
89
|
return await (await this.tableReader.table).rangeGet(this.range)
|
|
68
90
|
}
|
|
91
|
+
async objectGet(id) {
|
|
92
|
+
return await (await this.tableReader.table).objectGet(id)
|
|
93
|
+
}
|
|
94
|
+
object(id) {
|
|
95
|
+
return new ObjectReader(this.tableReader, id)
|
|
96
|
+
}
|
|
97
|
+
async count(range = {}) {
|
|
98
|
+
return await (await this.tableReader.table).countGet(rangeIntersection(this.range, range))
|
|
99
|
+
}
|
|
69
100
|
}
|
|
70
101
|
|
|
71
102
|
class TableReader extends ChangeStream {
|
|
@@ -138,6 +169,9 @@ class TableReader extends ChangeStream {
|
|
|
138
169
|
for(const callback of this.callbacks) await callback(obj, oldObj, id, timestamp)
|
|
139
170
|
if(profileOp) await profileLog.end(profileOp)
|
|
140
171
|
}
|
|
172
|
+
async rangeGet(range) {
|
|
173
|
+
return await this.table.rangeGet(range)
|
|
174
|
+
}
|
|
141
175
|
range(range) {
|
|
142
176
|
const key = JSON.stringify(range)
|
|
143
177
|
let reader = this.rangeReaders.get(key)
|
|
@@ -149,6 +183,12 @@ class TableReader extends ChangeStream {
|
|
|
149
183
|
return reader
|
|
150
184
|
return new RangeReader(this, range)
|
|
151
185
|
}
|
|
186
|
+
async objectGet(id) {
|
|
187
|
+
return await (await this.table).objectGet(id)
|
|
188
|
+
}
|
|
189
|
+
async count(range = {}) {
|
|
190
|
+
return await (await this.table).count(rangeIntersection(this.range, range))
|
|
191
|
+
}
|
|
152
192
|
object(id) {
|
|
153
193
|
let reader = this.objectReaders.get(id)
|
|
154
194
|
if(!reader) {
|
package/lib/queryGet.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TableWriter, LogWriter } from './queryUpdate.js'
|
|
2
2
|
import { ChangeStream } from './ChangeStream.js'
|
|
3
|
-
|
|
3
|
+
import { unitRange, rangeIntersection } from './utils.js'
|
|
4
4
|
const maxGetLimit = 256
|
|
5
5
|
|
|
6
6
|
class ObjectReader extends ChangeStream {
|
|
@@ -22,6 +22,15 @@ class ObjectReader extends ChangeStream {
|
|
|
22
22
|
async get() {
|
|
23
23
|
return await (await this.#table).objectGet(this.#id)
|
|
24
24
|
}
|
|
25
|
+
async rangeGet(range) {
|
|
26
|
+
return await (await this.#table).rangeGet(rangeIntersection(unitRange(this.#id), range))
|
|
27
|
+
}
|
|
28
|
+
range(range) {
|
|
29
|
+
return new RangeReader(this.#table, rangeIntersection(unitRange(this.#id), range))
|
|
30
|
+
}
|
|
31
|
+
async count(range = {}) {
|
|
32
|
+
return await this.#table.countGet(rangeIntersection(unitRange(this.#id), range))
|
|
33
|
+
}
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
class RangeReader extends ChangeStream {
|
|
@@ -45,6 +54,22 @@ class RangeReader extends ChangeStream {
|
|
|
45
54
|
async get() {
|
|
46
55
|
return await (await this.#table).rangeGet(this.#range)
|
|
47
56
|
}
|
|
57
|
+
|
|
58
|
+
async rangeGet(range) {
|
|
59
|
+
return await (await this.#table).rangeGet(rangeIntersection(this.#range, range))
|
|
60
|
+
}
|
|
61
|
+
range(range) {
|
|
62
|
+
return new RangeReader(this.#table, rangeIntersection(this.#range, range), this.#time)
|
|
63
|
+
}
|
|
64
|
+
object(id) {
|
|
65
|
+
return new ObjectReader(this.#table, id, this.#time)
|
|
66
|
+
}
|
|
67
|
+
async objectGet(id) {
|
|
68
|
+
return await (await this.#table).objectGet(id)
|
|
69
|
+
}
|
|
70
|
+
async count(range = {}) {
|
|
71
|
+
return await this.#table.countGet(rangeIntersection(this.#range, range))
|
|
72
|
+
}
|
|
48
73
|
}
|
|
49
74
|
|
|
50
75
|
class TableReader extends ChangeStream {
|
|
@@ -73,20 +98,20 @@ class TableReader extends ChangeStream {
|
|
|
73
98
|
return results
|
|
74
99
|
}
|
|
75
100
|
unobserve(obs) {}
|
|
101
|
+
async rangeGet(range) {
|
|
102
|
+
return new RangeReader(this.#table, range, this.#time)
|
|
103
|
+
}
|
|
76
104
|
range(range) {
|
|
77
105
|
return new RangeReader(this.#table, range, this.#time)
|
|
78
106
|
}
|
|
79
107
|
object(id) {
|
|
80
108
|
return new ObjectReader(this.#table, id, this.#time)
|
|
81
109
|
}
|
|
82
|
-
objectGet(id) {
|
|
83
|
-
return this.#table.objectGet(id)
|
|
84
|
-
}
|
|
85
|
-
async get(range = {}) {
|
|
86
|
-
return (await this.#table).rangeGet(range)
|
|
110
|
+
async objectGet(id) {
|
|
111
|
+
return await (await this.#table).objectGet(id)
|
|
87
112
|
}
|
|
88
113
|
async count(range = {}) {
|
|
89
|
-
return (await this.#table).countGet(range)
|
|
114
|
+
return await (await this.#table).countGet(range)
|
|
90
115
|
}
|
|
91
116
|
}
|
|
92
117
|
|
package/lib/queryObservable.js
CHANGED
|
@@ -31,7 +31,7 @@ class ObjectObserver {
|
|
|
31
31
|
}
|
|
32
32
|
readPromise() {
|
|
33
33
|
return this.#valuePromise
|
|
34
|
-
}
|
|
34
|
+
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
class RangeObserver {
|
|
@@ -119,11 +119,12 @@ class Reader extends ChangeStream {
|
|
|
119
119
|
class ObjectReader extends Reader {
|
|
120
120
|
#table = null
|
|
121
121
|
#id = null
|
|
122
|
-
|
|
123
|
-
constructor(queryReader, table, id) {
|
|
122
|
+
#tableReader = null
|
|
123
|
+
constructor(queryReader, table, id, tableReader) {
|
|
124
124
|
super(queryReader)
|
|
125
125
|
this.#table = table
|
|
126
126
|
this.#id = id
|
|
127
|
+
this.#tableReader = tableReader
|
|
127
128
|
}
|
|
128
129
|
observableFactory() {
|
|
129
130
|
return this.#table.objectObservable(this.#id)
|
|
@@ -132,19 +133,32 @@ class ObjectReader extends Reader {
|
|
|
132
133
|
return this.startObserver((r) => new ObjectObserver(r, cb))
|
|
133
134
|
}
|
|
134
135
|
get() {
|
|
135
|
-
//console.log("OBJ GET", this.#table.name, this.#id)
|
|
136
136
|
return this.#table.objectGet(this.#id)
|
|
137
137
|
}
|
|
138
|
+
async rangeGet(range) {
|
|
139
|
+
return this.#tableReader.rangeGet(rangeIntersection(unitRange(this.#id), range))
|
|
140
|
+
}
|
|
141
|
+
async objectGet(id) {
|
|
142
|
+
return this.#table.objectGet(id)
|
|
143
|
+
}
|
|
144
|
+
object(id) {
|
|
145
|
+
return this.#tableReader.object(id)
|
|
146
|
+
}
|
|
147
|
+
async count(range = {}) {
|
|
148
|
+
return await this.#table.count(rangeIntersection(unitRange(this.#id), range))
|
|
149
|
+
}
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
class RangeReader extends Reader {
|
|
141
153
|
#table = null
|
|
142
154
|
#range = null
|
|
155
|
+
#tableReader = null
|
|
143
156
|
|
|
144
|
-
constructor(queryReader, table, range) {
|
|
157
|
+
constructor(queryReader, table, range, tableReader) {
|
|
145
158
|
super(queryReader)
|
|
146
159
|
this.#table = table
|
|
147
160
|
this.#range = range
|
|
161
|
+
this.#tableReader = tableReader
|
|
148
162
|
}
|
|
149
163
|
async observableFactory() {
|
|
150
164
|
return await (await this.#table).rangeObservable(this.#range)
|
|
@@ -155,6 +169,18 @@ class RangeReader extends Reader {
|
|
|
155
169
|
async get() {
|
|
156
170
|
return await (await this.#table).rangeGet(this.#range)
|
|
157
171
|
}
|
|
172
|
+
async rangeGet(range) {
|
|
173
|
+
return await this.#tableReader.rangeGet(rangeIntersection(this.#range, range))
|
|
174
|
+
}
|
|
175
|
+
async objectGet(id) {
|
|
176
|
+
return await this.#table.objectGet(id)
|
|
177
|
+
}
|
|
178
|
+
object(id) {
|
|
179
|
+
return this.#tableReader.object(id)
|
|
180
|
+
}
|
|
181
|
+
async count(range = {}) {
|
|
182
|
+
return await this.#table.count(rangeIntersection(this.#range, range))
|
|
183
|
+
}
|
|
158
184
|
}
|
|
159
185
|
|
|
160
186
|
class TableReader extends Reader {
|
|
@@ -172,16 +198,19 @@ class TableReader extends Reader {
|
|
|
172
198
|
onChange(cb) {
|
|
173
199
|
return this.startObserver((r) => new RangeObserver(r, cb))
|
|
174
200
|
}
|
|
201
|
+
async rangeGet(range) {
|
|
202
|
+
return await this.#table.rangeGet(range)
|
|
203
|
+
}
|
|
175
204
|
range(range) {
|
|
176
205
|
return this._queryReader.getExistingReaderOrCreate(this.#prefix+':'+JSON.stringify(range),
|
|
177
|
-
() => new RangeReader(this._queryReader, this.#table, range))
|
|
206
|
+
() => new RangeReader(this._queryReader, this.#table, range, this))
|
|
178
207
|
}
|
|
179
208
|
object(id) {
|
|
180
209
|
return this._queryReader.getExistingReaderOrCreate(this.#prefix+'#'+id,
|
|
181
|
-
() => new ObjectReader(this._queryReader, this.#table, id))
|
|
210
|
+
() => new ObjectReader(this._queryReader, this.#table, id, this))
|
|
182
211
|
}
|
|
183
|
-
objectGet(id) {
|
|
184
|
-
return this.#table.objectGet(id)
|
|
212
|
+
async objectGet(id) {
|
|
213
|
+
return await this.#table.objectGet(id)
|
|
185
214
|
}
|
|
186
215
|
async get(range = {}) {
|
|
187
216
|
return await (await this.#table).rangeGet(range)
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function min(a, b) {
|
|
2
|
+
if(a === undefined) return b
|
|
3
|
+
if(b === undefined) return a
|
|
4
|
+
return a < b ? a : b
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function max(a, b) {
|
|
8
|
+
if(a === undefined) return b
|
|
9
|
+
if(b === undefined) return a
|
|
10
|
+
return a > b ? a : b
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function rangeIntersection(range1, range2) {
|
|
14
|
+
return {
|
|
15
|
+
reverse: range2.reverse,
|
|
16
|
+
gt: max(range1.gt, range2.gt),
|
|
17
|
+
lt: min(range1.lt, range2.lt),
|
|
18
|
+
gte: max(range1.gte, range2.gte),
|
|
19
|
+
lte: min(range1.lte, range2.lte),
|
|
20
|
+
limit: range2.limit
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function rangeUnion(range1, range2) {
|
|
25
|
+
return {
|
|
26
|
+
reverse: range1.reverse,
|
|
27
|
+
gt: min(range1.gt, range2.gt),
|
|
28
|
+
lt: max(range1.lt, range2.lt),
|
|
29
|
+
gte: min(range1.gte, range2.gte),
|
|
30
|
+
lte: max(range1.lte, range2.lte),
|
|
31
|
+
limit: range1.limit + range2.limit
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function unitRange(id) {
|
|
36
|
+
return {
|
|
37
|
+
gte: id,
|
|
38
|
+
lt: id,
|
|
39
|
+
limit: 1
|
|
40
|
+
}
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/db",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.63",
|
|
4
4
|
"description": "Database with observable data for live queries",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"type": "module",
|
|
23
23
|
"homepage": "https://github.com/live-change/live-change-stack",
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@live-change/db-store-level": "^0.9.
|
|
26
|
-
"@live-change/db-store-lmdb": "^0.9.
|
|
25
|
+
"@live-change/db-store-level": "^0.9.63",
|
|
26
|
+
"@live-change/db-store-lmdb": "^0.9.63",
|
|
27
27
|
"minimist": ">=1.2.3",
|
|
28
28
|
"next-tick": "^1.1.0",
|
|
29
29
|
"rimraf": "^5.0.5",
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
"websocket-extensions": ">=0.1.4"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@live-change/dao": "^0.9.
|
|
35
|
+
"@live-change/dao": "^0.9.63",
|
|
36
36
|
"get-random-values": "^1.2.2",
|
|
37
37
|
"node-interval-tree": "^1.3.3"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "89b9647aeaff9fc66add1f07d225fe4f44d91a39"
|
|
40
40
|
}
|