@mblaney/holster 1.0.4 → 1.0.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/build/holster.js +3 -3
- package/build/holster.js.map +3 -3
- package/package.json +1 -1
- package/src/holster.js +78 -70
- package/src/radisk.js +23 -50
- package/src/wire.js +2 -8
- package/test/radisk.test.js +14 -18
- package/test/split.test.js +317 -0
package/package.json
CHANGED
package/src/holster.js
CHANGED
|
@@ -70,7 +70,7 @@ const Holster = opt => {
|
|
|
70
70
|
const data = await new Promise(res => {
|
|
71
71
|
const _ctxid = utils.text.random()
|
|
72
72
|
allctx.set(_ctxid, {chain: [{item: null, soul: id}]})
|
|
73
|
-
api(_ctxid).next(null, res)
|
|
73
|
+
api(_ctxid).next(null, res, _opt)
|
|
74
74
|
})
|
|
75
75
|
msg.put[soul][key] = data
|
|
76
76
|
}
|
|
@@ -145,39 +145,65 @@ const Holster = opt => {
|
|
|
145
145
|
// whole node for verification.
|
|
146
146
|
const lex =
|
|
147
147
|
ctx.user || opt.secure ? {"#": soul} : {"#": soul, ".": item}
|
|
148
|
-
wire.get(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
wire.get(
|
|
149
|
+
lex,
|
|
150
|
+
async msg => {
|
|
151
|
+
if (msg.err) {
|
|
152
|
+
if (ctx.user || opt.secure) {
|
|
153
|
+
console.log(`error getting ${soul}: ${msg.err}`)
|
|
154
|
+
} else {
|
|
155
|
+
console.log(`error getting ${item} on ${soul}: ${msg.err}`)
|
|
156
|
+
}
|
|
157
|
+
if (cb) cb(null)
|
|
158
|
+
return
|
|
154
159
|
}
|
|
155
|
-
if (cb) cb(null)
|
|
156
|
-
return
|
|
157
|
-
}
|
|
158
160
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
161
|
+
let node = msg.put && msg.put[soul]
|
|
162
|
+
if (node && typeof node[item] !== "undefined") {
|
|
163
|
+
let id = utils.rel.is(node[item])
|
|
164
|
+
if (id) {
|
|
165
|
+
ctx.chain[i].soul = id
|
|
166
|
+
allctx.set(ctxid, {...ctx})
|
|
167
|
+
// Call api again using the updated context.
|
|
168
|
+
if (get) {
|
|
169
|
+
api(ctxid).next(null, request.get, cb, request._opt)
|
|
170
|
+
} else if (put) {
|
|
171
|
+
api(ctxid).put(request.put, cb)
|
|
172
|
+
} else if (on) {
|
|
173
|
+
api(ctxid).on(request.on, cb, request._get, request._opt)
|
|
174
|
+
} else if (off) {
|
|
175
|
+
api(ctxid).off(cb)
|
|
176
|
+
}
|
|
177
|
+
} else if (get) {
|
|
178
|
+
// Request was not for a node, return property on current soul.
|
|
179
|
+
cb(node[item])
|
|
168
180
|
} else if (put) {
|
|
169
|
-
|
|
181
|
+
// Request was chained before put, so rel doesn't exist yet.
|
|
182
|
+
id = utils.text.random()
|
|
183
|
+
node[item] = utils.rel.ify(id)
|
|
184
|
+
const g = await graph(soul, node, ctx.user, cb)
|
|
185
|
+
if (g === null) return
|
|
186
|
+
|
|
187
|
+
wire.put(g, err => {
|
|
188
|
+
if (err) {
|
|
189
|
+
cb(`error putting ${item} on ${soul}: ${err}`)
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
ctx.chain[i].soul = id
|
|
194
|
+
api(ctxid).put(request.put, cb)
|
|
195
|
+
})
|
|
170
196
|
} else if (on) {
|
|
171
|
-
|
|
197
|
+
// Allow listening to a node that doesn't exist.
|
|
198
|
+
cb(null)
|
|
172
199
|
} else if (off) {
|
|
173
|
-
|
|
200
|
+
// Allow stop listening to a node that doesn't exist.
|
|
201
|
+
if (cb) cb(null)
|
|
174
202
|
}
|
|
175
|
-
} else if (get) {
|
|
176
|
-
// Request was not for a node, return property on current soul.
|
|
177
|
-
cb(node[item])
|
|
178
203
|
} else if (put) {
|
|
179
204
|
// Request was chained before put, so rel doesn't exist yet.
|
|
180
|
-
id = utils.text.random()
|
|
205
|
+
const id = utils.text.random()
|
|
206
|
+
if (!node) node = {}
|
|
181
207
|
node[item] = utils.rel.ify(id)
|
|
182
208
|
const g = await graph(soul, node, ctx.user, cb)
|
|
183
209
|
if (g === null) return
|
|
@@ -191,35 +217,13 @@ const Holster = opt => {
|
|
|
191
217
|
ctx.chain[i].soul = id
|
|
192
218
|
api(ctxid).put(request.put, cb)
|
|
193
219
|
})
|
|
194
|
-
} else
|
|
195
|
-
// Allow
|
|
196
|
-
cb(null)
|
|
197
|
-
} else if (off) {
|
|
198
|
-
// Allow stop listening to a node that doesn't exist.
|
|
220
|
+
} else {
|
|
221
|
+
// Allow querying a node that doesn't exist.
|
|
199
222
|
if (cb) cb(null)
|
|
200
223
|
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!node) node = {}
|
|
205
|
-
node[item] = utils.rel.ify(id)
|
|
206
|
-
const g = await graph(soul, node, ctx.user, cb)
|
|
207
|
-
if (g === null) return
|
|
208
|
-
|
|
209
|
-
wire.put(g, err => {
|
|
210
|
-
if (err) {
|
|
211
|
-
cb(`error putting ${item} on ${soul}: ${err}`)
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
ctx.chain[i].soul = id
|
|
216
|
-
api(ctxid).put(request.put, cb)
|
|
217
|
-
})
|
|
218
|
-
} else {
|
|
219
|
-
// Allow querying a node that doesn't exist.
|
|
220
|
-
if (cb) cb(null)
|
|
221
|
-
}
|
|
222
|
-
})
|
|
224
|
+
},
|
|
225
|
+
request._opt,
|
|
226
|
+
)
|
|
223
227
|
// Callback has been passed to next soul lookup or called above, so
|
|
224
228
|
// return false as the calling code should not continue.
|
|
225
229
|
return false
|
|
@@ -543,25 +547,29 @@ const Holster = opt => {
|
|
|
543
547
|
allctx.set(ctxid, {chain: [{item: item, soul: soul}], on: true})
|
|
544
548
|
// Map the user's callback because it can also be passed to off,
|
|
545
549
|
// so need a reference to it to compare them.
|
|
546
|
-
map.set(cb, () => api(ctxid).next(null, cb))
|
|
550
|
+
map.set(cb, () => api(ctxid).next(null, cb, _opt))
|
|
547
551
|
// Check if item is a rel and add event listener for the node.
|
|
548
|
-
wire.get(
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
552
|
+
wire.get(
|
|
553
|
+
{"#": soul, ".": item},
|
|
554
|
+
msg => {
|
|
555
|
+
if (msg.err) {
|
|
556
|
+
console.log(`error getting ${soul}.${item}: ${msg.err}`)
|
|
557
|
+
return
|
|
558
|
+
}
|
|
553
559
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
560
|
+
const current = msg.put && msg.put[soul] && msg.put[soul][item]
|
|
561
|
+
const id = utils.rel.is(current)
|
|
562
|
+
if (id) {
|
|
563
|
+
if (lex) lex = utils.obj.put(lex, "#", id)
|
|
564
|
+
else lex = {"#": id, ".": null}
|
|
565
|
+
} else {
|
|
566
|
+
if (lex) lex = utils.obj.put(lex, "#", soul)
|
|
567
|
+
else lex = {"#": soul, ".": item}
|
|
568
|
+
}
|
|
569
|
+
wire.on(lex, map.get(cb), _get, _opt)
|
|
570
|
+
},
|
|
571
|
+
_opt,
|
|
572
|
+
)
|
|
565
573
|
},
|
|
566
574
|
off: cb => {
|
|
567
575
|
if (!ctxid) {
|
package/src/radisk.js
CHANGED
|
@@ -119,7 +119,6 @@ const Radisk = opt => {
|
|
|
119
119
|
// 4. Read the previous file into memory
|
|
120
120
|
// 5. Scan through in memory radix for all values lexically less than limit
|
|
121
121
|
// 6. Merge and write all of those to the in-memory file and back to disk
|
|
122
|
-
// 7. If file is to large then split. More details needed here
|
|
123
122
|
radisk.save = (rad, cb) => {
|
|
124
123
|
const save = {
|
|
125
124
|
find: (tree, key) => {
|
|
@@ -176,70 +175,47 @@ const Radisk = opt => {
|
|
|
176
175
|
cache = null
|
|
177
176
|
const write = {
|
|
178
177
|
text: "",
|
|
178
|
+
limit: "",
|
|
179
|
+
done: false,
|
|
179
180
|
count: 0,
|
|
180
|
-
file: file,
|
|
181
181
|
each: (value, key, k, pre) => {
|
|
182
|
+
// each is called for all keys, but stop adding to write.text when
|
|
183
|
+
// write.slice is called, then the current contents of write.text
|
|
184
|
+
// will be written to file.
|
|
185
|
+
if (write.done) return
|
|
186
|
+
|
|
182
187
|
write.count++
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
"#" +
|
|
186
|
-
Radisk.encode(k) +
|
|
187
|
-
(typeof value === "undefined" ? "" : "=" + Radisk.encode(value)) +
|
|
188
|
-
"\n"
|
|
188
|
+
value = typeof value === "undefined" ? "" : "=" + Radisk.encode(value)
|
|
189
|
+
const enc =
|
|
190
|
+
Radisk.encode(pre.length) + "#" + Radisk.encode(k) + value + "\n"
|
|
189
191
|
// Cannot split the file if only have one entry to write. Also don't
|
|
190
192
|
// start a split if in the middle of writing a node (pre > 0).
|
|
191
193
|
if (
|
|
192
|
-
pre.length === 0 &&
|
|
193
194
|
write.count > 1 &&
|
|
195
|
+
pre.length === 0 &&
|
|
194
196
|
write.text.length + enc.length > opt.size
|
|
195
197
|
) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
write.
|
|
199
|
-
write.count = 0
|
|
198
|
+
const end = k.indexOf(enq)
|
|
199
|
+
write.limit = end === -1 ? k : k.substring(0, end)
|
|
200
|
+
write.done = true
|
|
200
201
|
write.sub = Radix()
|
|
201
202
|
Radix.map(rad, write.slice)
|
|
202
|
-
|
|
203
|
+
radisk.write(write.limit, write.sub, cb)
|
|
204
|
+
return
|
|
203
205
|
}
|
|
204
206
|
|
|
205
207
|
write.text += enc
|
|
206
208
|
},
|
|
207
|
-
put: () => {
|
|
208
|
-
opt.store.put(file, write.text, cb)
|
|
209
|
-
},
|
|
210
209
|
slice: (value, key) => {
|
|
211
|
-
if (key < write.
|
|
212
|
-
|
|
213
|
-
if (++write.count > write.limit) {
|
|
214
|
-
var name = write.file
|
|
215
|
-
// Use only the soul of the key as the filename so that all
|
|
216
|
-
// properties of a soul are written to the same file.
|
|
217
|
-
let end = key.indexOf(enq)
|
|
218
|
-
if (end === -1) {
|
|
219
|
-
write.file = key
|
|
220
|
-
} else {
|
|
221
|
-
write.file = key.substring(0, end)
|
|
222
|
-
}
|
|
223
|
-
write.count = 0
|
|
224
|
-
radisk.write(name, write.sub, write.next)
|
|
225
|
-
return true
|
|
226
|
-
}
|
|
210
|
+
if (key < write.limit) return
|
|
227
211
|
|
|
228
212
|
write.sub(key, value)
|
|
229
213
|
},
|
|
230
|
-
next: err => {
|
|
231
|
-
if (err) return cb(err)
|
|
232
|
-
|
|
233
|
-
write.sub = Radix()
|
|
234
|
-
if (!Radix.map(rad, write.slice)) {
|
|
235
|
-
radisk.write(write.file, write.sub, cb)
|
|
236
|
-
}
|
|
237
|
-
},
|
|
238
214
|
}
|
|
239
|
-
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
|
|
215
|
+
Radix.map(rad, write.each, true)
|
|
216
|
+
// There is always accumulated write.text to store once write.each has
|
|
217
|
+
// finished.
|
|
218
|
+
opt.store.put(file, write.text, cb)
|
|
243
219
|
}
|
|
244
220
|
|
|
245
221
|
radisk.read = (key, cb) => {
|
|
@@ -248,11 +224,8 @@ const Radisk = opt => {
|
|
|
248
224
|
if (typeof value !== "undefined") return cb(u, value)
|
|
249
225
|
}
|
|
250
226
|
// Only the soul of the key is compared to filenames (see radisk.write).
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (end !== -1) {
|
|
254
|
-
soul = key.substring(0, end)
|
|
255
|
-
}
|
|
227
|
+
const end = key.indexOf(enq)
|
|
228
|
+
const soul = end === -1 ? key : key.substring(0, end)
|
|
256
229
|
|
|
257
230
|
const read = {
|
|
258
231
|
lex: file => {
|
package/src/wire.js
CHANGED
|
@@ -231,10 +231,7 @@ const Wire = opt => {
|
|
|
231
231
|
return
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
if (retry++ > 5)
|
|
235
|
-
clearInterval(interval)
|
|
236
|
-
console.log("websocket not available")
|
|
237
|
-
}
|
|
234
|
+
if (retry++ > 5) clearInterval(interval)
|
|
238
235
|
}, 1000)
|
|
239
236
|
}
|
|
240
237
|
})
|
|
@@ -279,10 +276,7 @@ const Wire = opt => {
|
|
|
279
276
|
return
|
|
280
277
|
}
|
|
281
278
|
|
|
282
|
-
if (retry++ > 5)
|
|
283
|
-
clearInterval(interval)
|
|
284
|
-
console.log("websocket not available")
|
|
285
|
-
}
|
|
279
|
+
if (retry++ > 5) clearInterval(interval)
|
|
286
280
|
}, 1000)
|
|
287
281
|
}
|
|
288
282
|
})
|
package/test/radisk.test.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import {describe, test} from "node:test"
|
|
2
2
|
import assert from "node:assert/strict"
|
|
3
3
|
import Radisk from "../src/radisk.js"
|
|
4
|
-
import Names from "./names.js"
|
|
5
|
-
const names = Names()
|
|
6
4
|
|
|
7
5
|
describe("radisk", () => {
|
|
8
6
|
const puts = {}
|
|
@@ -175,8 +173,8 @@ describe("radisk", () => {
|
|
|
175
173
|
"!":
|
|
176
174
|
'\x1F+0\x1F#\x1F"key\x1F=\x1F"value\ncontinued\x0312345\x1F\n' +
|
|
177
175
|
'\x1F+1\x1F#\x1F"A\x1F=\x1F"valueA\x1F\n' +
|
|
178
|
-
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n'
|
|
179
|
-
|
|
176
|
+
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
|
|
177
|
+
'\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
|
|
180
178
|
newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"' + big + "\x1F\n",
|
|
181
179
|
})
|
|
182
180
|
radisk("newFile", (err, value) => {
|
|
@@ -198,8 +196,8 @@ describe("radisk", () => {
|
|
|
198
196
|
"!":
|
|
199
197
|
'\x1F+0\x1F#\x1F"key\x1F=\x1F"value\ncontinued\x0312345\x1F\n' +
|
|
200
198
|
'\x1F+1\x1F#\x1F"A\x1F=\x1F"valueA\x1F\n' +
|
|
201
|
-
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n'
|
|
202
|
-
|
|
199
|
+
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
|
|
200
|
+
'\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
|
|
203
201
|
newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"' + big + "\x1F\n",
|
|
204
202
|
small: '\x1F+0\x1F#\x1F"small\x1F=\x1F"small value\x1F\n',
|
|
205
203
|
})
|
|
@@ -220,8 +218,8 @@ describe("radisk", () => {
|
|
|
220
218
|
"!":
|
|
221
219
|
'\x1F+0\x1F#\x1F"key\x1F=\x1F"value\ncontinued\x0312345\x1F\n' +
|
|
222
220
|
'\x1F+1\x1F#\x1F"A\x1F=\x1F"valueA\x1F\n' +
|
|
223
|
-
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n'
|
|
224
|
-
|
|
221
|
+
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
|
|
222
|
+
'\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
|
|
225
223
|
newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
|
|
226
224
|
small: '\x1F+0\x1F#\x1F"small\x1F=\x1F"small value\x1F\n',
|
|
227
225
|
})
|
|
@@ -244,8 +242,8 @@ describe("radisk", () => {
|
|
|
244
242
|
'\x1F+1\x1F#\x1F"A\x1F=\x1F"' +
|
|
245
243
|
big +
|
|
246
244
|
"\x1F\n" +
|
|
247
|
-
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n'
|
|
248
|
-
|
|
245
|
+
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
|
|
246
|
+
'\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
|
|
249
247
|
newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
|
|
250
248
|
small: '\x1F+0\x1F#\x1F"small\x1F=\x1F"small value\x1F\n',
|
|
251
249
|
})
|
|
@@ -265,8 +263,8 @@ describe("radisk", () => {
|
|
|
265
263
|
'\x1F+1\x1F#\x1F"A\x1F=\x1F"' +
|
|
266
264
|
big +
|
|
267
265
|
"\x1F\n" +
|
|
268
|
-
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n'
|
|
269
|
-
|
|
266
|
+
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
|
|
267
|
+
'\x1F+1\x1F#\x1F"C\x1F=\x1F"valueC\x1F\n',
|
|
270
268
|
newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
|
|
271
269
|
small:
|
|
272
270
|
'\x1F+0\x1F#\x1F"small\x1F=\x1F"small value\x1F\n' +
|
|
@@ -292,9 +290,8 @@ describe("radisk", () => {
|
|
|
292
290
|
'\x1F+1\x1F#\x1F"A\x1F=\x1F"' +
|
|
293
291
|
big +
|
|
294
292
|
"\x1F\n" +
|
|
295
|
-
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n'
|
|
296
|
-
|
|
297
|
-
'\x1F+0\x1F#\x1F"key\x1F\n\x1F+1\x1F#\x1F"C\x1F=\x1F \x1F\n' +
|
|
293
|
+
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
|
|
294
|
+
'\x1F+1\x1F#\x1F"C\x1F=\x1F \x1F\n' +
|
|
298
295
|
'\x1F+1\x1F#\x1F"D\x1F=\x1F"valueD\x1F\n',
|
|
299
296
|
newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
|
|
300
297
|
small:
|
|
@@ -319,9 +316,8 @@ describe("radisk", () => {
|
|
|
319
316
|
"!":
|
|
320
317
|
'\x1F+0\x1F#\x1F"key\x1F=\x1F \x1F\n' +
|
|
321
318
|
'\x1F+1\x1F#\x1F"A\x1F=\x1F \x1F\n' +
|
|
322
|
-
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n'
|
|
323
|
-
|
|
324
|
-
'\x1F+0\x1F#\x1F"key\x1F\n\x1F+1\x1F#\x1F"C\x1F=\x1F \x1F\n' +
|
|
319
|
+
'\x1F+1\x1F#\x1F"B\x1F=\x1F"valueB\x1F\n' +
|
|
320
|
+
'\x1F+1\x1F#\x1F"C\x1F=\x1F \x1F\n' +
|
|
325
321
|
'\x1F+1\x1F#\x1F"D\x1F=\x1F"valueD\x1F\n',
|
|
326
322
|
newFile: '\x1F+0\x1F#\x1F"newFile\x1F=\x1F"removed...\x1F\n',
|
|
327
323
|
small:
|