@mblaney/holster 2.0.1 → 2.1.0
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/README.md +37 -6
- package/build/holster.js +3 -3
- package/build/holster.js.map +3 -3
- package/dist/holster.js +3 -3
- package/dist/holster.js.map +3 -3
- package/package.json +1 -1
- package/src/ham.js +10 -3
- package/src/holster.js +36 -25
- package/src/radisk.js +26 -28
- package/src/store.js +30 -17
- package/src/user.js +92 -101
- package/src/wire.js +325 -125
- package/ts/src/array.ts +10 -13
- package/ts/src/buffer.ts +40 -20
- package/ts/src/get.ts +5 -6
- package/ts/src/ham.ts +47 -28
- package/ts/src/holster.ts +277 -132
- package/ts/src/index.ts +14 -15
- package/ts/src/radisk.ts +122 -51
- package/ts/src/radix.ts +50 -34
- package/ts/src/schemas.ts +44 -24
- package/ts/src/sea-utils.ts +7 -11
- package/ts/src/sea.ts +51 -54
- package/ts/src/store.ts +76 -58
- package/ts/src/user.ts +136 -113
- package/ts/src/utils.ts +36 -11
- package/ts/src/wire.ts +477 -248
package/ts/src/holster.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as utils from "./utils.ts"
|
|
7
|
-
import Wire, {
|
|
8
|
-
import User, {
|
|
7
|
+
import Wire, {type WireAPI} from "./wire.ts"
|
|
8
|
+
import User, {type UserInterface} from "./user.ts"
|
|
9
9
|
import SEA from "./sea.ts"
|
|
10
10
|
import type {
|
|
11
11
|
HolsterOptions,
|
|
@@ -25,31 +25,41 @@ import type {
|
|
|
25
25
|
*/
|
|
26
26
|
export interface HolsterAPI {
|
|
27
27
|
get: {
|
|
28
|
-
(key: string | null, lex?: LexWithDot, cb?: (data: unknown) => void, _opt?: WireOptions): HolsterAPI
|
|
29
28
|
(
|
|
30
29
|
key: string | null,
|
|
30
|
+
lex?: LexWithDot,
|
|
31
31
|
cb?: (data: unknown) => void,
|
|
32
|
-
_opt?: WireOptions
|
|
32
|
+
_opt?: WireOptions,
|
|
33
|
+
): HolsterAPI
|
|
34
|
+
(
|
|
35
|
+
key: string | null,
|
|
36
|
+
cb?: (data: unknown) => void,
|
|
37
|
+
_opt?: WireOptions,
|
|
33
38
|
): HolsterAPI
|
|
34
39
|
}
|
|
35
40
|
next: {
|
|
36
|
-
(key: string | null, lex?: LexWithDot, cb?: (data: unknown) => void, _opt?: WireOptions): HolsterAPI
|
|
37
41
|
(
|
|
38
42
|
key: string | null,
|
|
43
|
+
lex?: LexWithDot,
|
|
39
44
|
cb?: (data: unknown) => void,
|
|
40
|
-
_opt?: WireOptions
|
|
45
|
+
_opt?: WireOptions,
|
|
46
|
+
): HolsterAPI
|
|
47
|
+
(
|
|
48
|
+
key: string | null,
|
|
49
|
+
cb?: (data: unknown) => void,
|
|
50
|
+
_opt?: WireOptions,
|
|
41
51
|
): HolsterAPI
|
|
42
52
|
}
|
|
43
53
|
put: (
|
|
44
54
|
data: GraphValue | Record<string, GraphValue>,
|
|
45
55
|
set?: boolean | ((data?: string | null) => void),
|
|
46
|
-
cb?: (data?: string | null) => void
|
|
56
|
+
cb?: (data?: string | null) => void,
|
|
47
57
|
) => HolsterAPI | void
|
|
48
58
|
on: (
|
|
49
59
|
lex: LexWithDot | ((data: unknown) => void),
|
|
50
60
|
cb?: ((data: unknown) => void) | boolean,
|
|
51
61
|
_get?: boolean,
|
|
52
|
-
_opt?: WireOptions
|
|
62
|
+
_opt?: WireOptions,
|
|
53
63
|
) => void
|
|
54
64
|
off: (cb?: (data: unknown) => void) => HolsterAPI | void
|
|
55
65
|
user: () => UserInterface & HolsterAPI
|
|
@@ -59,8 +69,8 @@ export interface HolsterAPI {
|
|
|
59
69
|
|
|
60
70
|
const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
61
71
|
let options: HolsterOptions
|
|
62
|
-
if (typeof opt === "string") options = {
|
|
63
|
-
else if (opt instanceof Array) options = {
|
|
72
|
+
if (typeof opt === "string") options = {peers: [opt]}
|
|
73
|
+
else if (opt instanceof Array) options = {peers: opt}
|
|
64
74
|
else if (!utils.obj.is(opt)) options = {}
|
|
65
75
|
else options = opt
|
|
66
76
|
|
|
@@ -81,7 +91,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
const check = (
|
|
84
|
-
data: GraphValue | Record<string, GraphValue
|
|
94
|
+
data: GraphValue | Record<string, GraphValue>,
|
|
85
95
|
): true | string | string[] => {
|
|
86
96
|
if (ok(data as GraphValue)) return true
|
|
87
97
|
|
|
@@ -98,7 +108,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
98
108
|
if (typeof value === "undefined") {
|
|
99
109
|
return `error undefined ${key} cannot be converted to a graph`
|
|
100
110
|
}
|
|
101
|
-
const error = JSON.stringify({
|
|
111
|
+
const error = JSON.stringify({[key]: value})
|
|
102
112
|
return `error ${error} cannot be converted to a graph`
|
|
103
113
|
}
|
|
104
114
|
if (keys.length !== 0) return keys
|
|
@@ -113,44 +123,50 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
113
123
|
lex: LexFilter,
|
|
114
124
|
soul: string,
|
|
115
125
|
ack: (data: unknown) => void,
|
|
116
|
-
_opt?: WireOptions
|
|
126
|
+
_opt?: WireOptions,
|
|
117
127
|
): void => {
|
|
118
128
|
wire.get(
|
|
119
129
|
utils.obj.put(lex as never, "#", soul) as Lex,
|
|
120
130
|
async msg => {
|
|
121
131
|
if (msg.err) console.log(msg.err)
|
|
122
132
|
if (msg.put && msg.put[soul]) {
|
|
123
|
-
delete (msg.put[soul] as {
|
|
124
|
-
delete (msg.put[soul] as Record<string, unknown>)[
|
|
125
|
-
|
|
133
|
+
delete (msg.put[soul] as {_?: unknown})._
|
|
134
|
+
delete (msg.put[soul] as Record<string, unknown>)[
|
|
135
|
+
utils.userPublicKey
|
|
136
|
+
]
|
|
137
|
+
delete (msg.put[soul] as Record<string, unknown>)[
|
|
138
|
+
utils.userSignature
|
|
139
|
+
]
|
|
126
140
|
const node = msg.put[soul]!
|
|
127
|
-
await Promise.all(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
141
|
+
await Promise.all(
|
|
142
|
+
Object.keys(node).map(async key => {
|
|
143
|
+
const id = utils.rel.is(node[key] as GraphValue)
|
|
144
|
+
if (!id) return
|
|
145
|
+
const attemptRead = async (retries = 0): Promise<unknown> => {
|
|
146
|
+
const data = await new Promise<unknown>(res => {
|
|
147
|
+
const _ctxid = utils.text.random()
|
|
148
|
+
const ctx = allctx.get(ctxid!)
|
|
149
|
+
allctx.set(_ctxid, {
|
|
150
|
+
chain: [{item: null, soul: id}],
|
|
151
|
+
user: ctx ? ctx.user : null,
|
|
152
|
+
})
|
|
153
|
+
api(_ctxid).next(null as never, res as never, _opt)
|
|
137
154
|
})
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
155
|
+
if (data !== null || retries >= 5) {
|
|
156
|
+
return data
|
|
157
|
+
}
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, 50))
|
|
159
|
+
return attemptRead(retries + 1)
|
|
142
160
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
node[key] = (await attemptRead()) as GraphValue
|
|
147
|
-
}))
|
|
161
|
+
node[key] = (await attemptRead()) as GraphValue
|
|
162
|
+
}),
|
|
163
|
+
)
|
|
148
164
|
ack(msg.put[soul])
|
|
149
165
|
} else {
|
|
150
166
|
ack(null)
|
|
151
167
|
}
|
|
152
168
|
},
|
|
153
|
-
_opt
|
|
169
|
+
_opt,
|
|
154
170
|
)
|
|
155
171
|
}
|
|
156
172
|
|
|
@@ -158,7 +174,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
158
174
|
soul: string,
|
|
159
175
|
data: Record<string, GraphValue>,
|
|
160
176
|
userctx?: UserIdentity | null,
|
|
161
|
-
cb?: (err?: string) => void
|
|
177
|
+
cb?: (err?: string) => void,
|
|
162
178
|
): Promise<Graph | null> => {
|
|
163
179
|
if (userctx) {
|
|
164
180
|
const timestamp = Date.now()
|
|
@@ -192,53 +208,68 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
192
208
|
item: string,
|
|
193
209
|
ctx: ApiContext,
|
|
194
210
|
i: number,
|
|
195
|
-
request: {
|
|
211
|
+
request: {
|
|
212
|
+
on?: LexFilter
|
|
213
|
+
get?: LexFilter
|
|
214
|
+
_get?: boolean
|
|
215
|
+
_opt?: WireOptions
|
|
216
|
+
},
|
|
196
217
|
cb: (data: unknown) => void,
|
|
197
|
-
on = true
|
|
218
|
+
on = true,
|
|
198
219
|
): void => {
|
|
199
220
|
let timer: ReturnType<typeof setTimeout> | null = null
|
|
200
221
|
const handler = (): void => {
|
|
201
222
|
// Bail out if the context was removed by off().
|
|
202
223
|
if (!allctx.has(ctxid!)) {
|
|
203
|
-
wire.off({
|
|
224
|
+
wire.off({"#": soul, ".": item}, handler)
|
|
204
225
|
if (timer) clearTimeout(timer)
|
|
205
226
|
return
|
|
206
227
|
}
|
|
207
228
|
wire.get(
|
|
208
|
-
{
|
|
229
|
+
{"#": soul, ".": item},
|
|
209
230
|
msg => {
|
|
210
231
|
const node = msg.put && msg.put[soul]
|
|
211
|
-
const id = utils.rel.is(node && node[item] as GraphValue)
|
|
232
|
+
const id = utils.rel.is(node && (node[item] as GraphValue))
|
|
212
233
|
if (!id) {
|
|
213
234
|
// If it's a plain value (not a rel) and this is a get, deliver it.
|
|
214
235
|
// node._ guards against timeout null-acks which lack metadata.
|
|
215
236
|
if (!on && node && node._ && typeof node[item] !== "undefined") {
|
|
216
237
|
if (timer) clearTimeout(timer)
|
|
217
|
-
wire.off({
|
|
238
|
+
wire.off({"#": soul, ".": item}, handler)
|
|
218
239
|
cb(node[item])
|
|
219
240
|
}
|
|
220
241
|
return
|
|
221
242
|
}
|
|
222
243
|
if (timer) clearTimeout(timer)
|
|
223
|
-
wire.off({
|
|
244
|
+
wire.off({"#": soul, ".": item}, handler)
|
|
224
245
|
ctx.chain[i]!.soul = id
|
|
225
|
-
allctx.set(ctxid!, {
|
|
246
|
+
allctx.set(ctxid!, {...ctx})
|
|
226
247
|
if (on) {
|
|
227
248
|
api(ctxid).on(request.on!, cb, request._get, request._opt)
|
|
228
249
|
} else {
|
|
229
|
-
api(ctxid).next(
|
|
250
|
+
api(ctxid).next(
|
|
251
|
+
null as never,
|
|
252
|
+
request.get as never,
|
|
253
|
+
cb as never,
|
|
254
|
+
request._opt,
|
|
255
|
+
)
|
|
230
256
|
}
|
|
231
257
|
},
|
|
232
|
-
{
|
|
258
|
+
{
|
|
259
|
+
...request._opt,
|
|
260
|
+
secure:
|
|
261
|
+
(typeof ctx.user === "boolean" ? ctx.user : !!ctx.user) ||
|
|
262
|
+
options.secure,
|
|
263
|
+
},
|
|
233
264
|
)
|
|
234
265
|
}
|
|
235
|
-
wire.on({
|
|
266
|
+
wire.on({"#": soul, ".": item}, handler, false, request._opt)
|
|
236
267
|
// Time out after the same total duration as the on() retry loop
|
|
237
268
|
// (1+2+4+8+16 = 31s) so callers are not blocked forever if the node
|
|
238
269
|
// genuinely doesn't exist.
|
|
239
270
|
if (!on) {
|
|
240
271
|
timer = setTimeout(() => {
|
|
241
|
-
wire.off({
|
|
272
|
+
wire.off({"#": soul, ".": item}, handler)
|
|
242
273
|
if (cb) cb(null)
|
|
243
274
|
}, 31000)
|
|
244
275
|
}
|
|
@@ -253,7 +284,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
253
284
|
_opt?: WireOptions
|
|
254
285
|
_get?: boolean
|
|
255
286
|
},
|
|
256
|
-
cb?: (data: unknown) => void
|
|
287
|
+
cb?: (data: unknown) => void,
|
|
257
288
|
): ChainItem | false => {
|
|
258
289
|
if (!request) {
|
|
259
290
|
console.log("error resolve request parameter required")
|
|
@@ -277,9 +308,9 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
277
308
|
if (found) {
|
|
278
309
|
let i = 1
|
|
279
310
|
while (i < ctx.chain.length && ctx.chain[i]!.soul !== null) i++
|
|
280
|
-
const {
|
|
311
|
+
const {item, soul} = ctx.chain[i - 1]!
|
|
281
312
|
wire.get(
|
|
282
|
-
{
|
|
313
|
+
{"#": soul!, ".": item!},
|
|
283
314
|
async msg => {
|
|
284
315
|
if (msg.err) {
|
|
285
316
|
console.log(`error getting ${item} on ${soul}: ${msg.err}`)
|
|
@@ -292,9 +323,14 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
292
323
|
let id = utils.rel.is(node[item!] as GraphValue)
|
|
293
324
|
if (id) {
|
|
294
325
|
ctx.chain[i]!.soul = id
|
|
295
|
-
allctx.set(ctxid!, {
|
|
326
|
+
allctx.set(ctxid!, {...ctx})
|
|
296
327
|
if (get) {
|
|
297
|
-
api(ctxid).next(
|
|
328
|
+
api(ctxid).next(
|
|
329
|
+
null as never,
|
|
330
|
+
request.get as never,
|
|
331
|
+
cb as never,
|
|
332
|
+
request._opt,
|
|
333
|
+
)
|
|
298
334
|
} else if (put) {
|
|
299
335
|
api(ctxid).put(request.put!, cb as never)
|
|
300
336
|
} else if (on) {
|
|
@@ -307,12 +343,19 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
307
343
|
} else if (put) {
|
|
308
344
|
id = utils.text.random()
|
|
309
345
|
node[item!] = utils.rel.ify(id)
|
|
310
|
-
const g = await graph(
|
|
346
|
+
const g = await graph(
|
|
347
|
+
soul!,
|
|
348
|
+
node as never,
|
|
349
|
+
ctx.user,
|
|
350
|
+
cb as never,
|
|
351
|
+
)
|
|
311
352
|
if (g === null) return
|
|
312
353
|
|
|
313
354
|
wire.put(g, err => {
|
|
314
355
|
if (err) {
|
|
315
|
-
(cb as (err: string) => void)(
|
|
356
|
+
;(cb as (err: string) => void)(
|
|
357
|
+
`error putting ${item} on ${soul}: ${err}`,
|
|
358
|
+
)
|
|
316
359
|
return
|
|
317
360
|
}
|
|
318
361
|
if (id) {
|
|
@@ -337,7 +380,9 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
337
380
|
|
|
338
381
|
wire.put(g, err => {
|
|
339
382
|
if (err) {
|
|
340
|
-
(cb as (err: string) => void)(
|
|
383
|
+
;(cb as (err: string) => void)(
|
|
384
|
+
`error putting ${item} on ${soul}: ${err}`,
|
|
385
|
+
)
|
|
341
386
|
return
|
|
342
387
|
}
|
|
343
388
|
ctx.chain[i]!.soul = id
|
|
@@ -349,8 +394,16 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
349
394
|
watchForRel(soul!, item!, ctx, i, request, cb!)
|
|
350
395
|
if (request._get) cb!(null)
|
|
351
396
|
} else if (get && node) {
|
|
352
|
-
const sv = (
|
|
353
|
-
|
|
397
|
+
const sv = (
|
|
398
|
+
node as Record<string, unknown> & {
|
|
399
|
+
_?: {">": Record<string, unknown>}
|
|
400
|
+
}
|
|
401
|
+
)._?.[">"]
|
|
402
|
+
if (
|
|
403
|
+
sv &&
|
|
404
|
+
Object.keys(sv).length > 0 &&
|
|
405
|
+
typeof sv[item!] === "undefined"
|
|
406
|
+
) {
|
|
354
407
|
// State vector has other properties but not this one — never written.
|
|
355
408
|
if (cb) cb(null)
|
|
356
409
|
} else {
|
|
@@ -363,14 +416,24 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
363
416
|
}
|
|
364
417
|
}
|
|
365
418
|
},
|
|
366
|
-
{
|
|
419
|
+
{
|
|
420
|
+
...request._opt,
|
|
421
|
+
secure:
|
|
422
|
+
(typeof ctx.user === "boolean" ? ctx.user : !!ctx.user) ||
|
|
423
|
+
options.secure,
|
|
424
|
+
},
|
|
367
425
|
)
|
|
368
426
|
return false
|
|
369
427
|
}
|
|
370
428
|
|
|
371
429
|
if (get && ctx.chain[ctx.chain.length - 1]!.item !== null) {
|
|
372
|
-
ctx.chain.push({
|
|
373
|
-
api(ctxid).next(
|
|
430
|
+
ctx.chain.push({item: null, soul: null})
|
|
431
|
+
api(ctxid).next(
|
|
432
|
+
null as never,
|
|
433
|
+
request.get as never,
|
|
434
|
+
cb as never,
|
|
435
|
+
request._opt,
|
|
436
|
+
)
|
|
374
437
|
return false
|
|
375
438
|
}
|
|
376
439
|
|
|
@@ -383,7 +446,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
383
446
|
key: string | null,
|
|
384
447
|
lex?: LexFilter | ((data: unknown) => void),
|
|
385
448
|
cb?: ((data: unknown) => void) | WireOptions,
|
|
386
|
-
_opt?: WireOptions
|
|
449
|
+
_opt?: WireOptions,
|
|
387
450
|
): HolsterAPI {
|
|
388
451
|
let lexFilter: LexFilter | null | undefined
|
|
389
452
|
let callback: ((data: unknown) => void) | undefined
|
|
@@ -412,13 +475,13 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
412
475
|
|
|
413
476
|
ctxid = utils.text.random()
|
|
414
477
|
allctx.set(ctxid, {
|
|
415
|
-
chain: [{
|
|
478
|
+
chain: [{item: String(key), soul: "root"}],
|
|
416
479
|
cb: callback,
|
|
417
480
|
})
|
|
418
481
|
if (!callback) return api(ctxid)
|
|
419
482
|
|
|
420
483
|
const _done = done(ctxid)
|
|
421
|
-
const result = resolve({
|
|
484
|
+
const result = resolve({get: lexFilter, _opt: opts}, _done)
|
|
422
485
|
if (result) get(lexFilter as never, result.soul!, _done, opts)
|
|
423
486
|
return this
|
|
424
487
|
} as HolsterAPI["get"],
|
|
@@ -428,7 +491,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
428
491
|
key: string | null,
|
|
429
492
|
lex?: LexFilter | ((data: unknown) => void),
|
|
430
493
|
cb?: ((data: unknown) => void) | WireOptions,
|
|
431
|
-
_opt?: WireOptions
|
|
494
|
+
_opt?: WireOptions,
|
|
432
495
|
): HolsterAPI {
|
|
433
496
|
let lexFilter: LexFilter | null | undefined
|
|
434
497
|
let callback: ((data: unknown) => void) | undefined
|
|
@@ -479,10 +542,10 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
479
542
|
callback = undefined
|
|
480
543
|
}
|
|
481
544
|
|
|
482
|
-
if (key !== null) ctx.chain.push({
|
|
545
|
+
if (key !== null) ctx.chain.push({item: String(key), soul: null})
|
|
483
546
|
if (!ctx.cb) return api(ctxid)
|
|
484
547
|
|
|
485
|
-
const result = resolve({
|
|
548
|
+
const result = resolve({get: lexFilter, _opt: opts}, _ack)
|
|
486
549
|
if (result) get(lexFilter as never, result.soul!, _ack, opts)
|
|
487
550
|
return this
|
|
488
551
|
} as HolsterAPI["next"],
|
|
@@ -490,7 +553,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
490
553
|
put: function (
|
|
491
554
|
data: GraphValue | Record<string, GraphValue>,
|
|
492
555
|
set?: boolean | ((data?: string | null) => void),
|
|
493
|
-
cb?: (data?: string | null) => void
|
|
556
|
+
cb?: (data?: string | null) => void,
|
|
494
557
|
): HolsterAPI | void {
|
|
495
558
|
let isSet = false
|
|
496
559
|
let callback: ((data?: string | null) => void) | undefined
|
|
@@ -526,7 +589,8 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
526
589
|
callback = undefined
|
|
527
590
|
}
|
|
528
591
|
|
|
529
|
-
if (isSet)
|
|
592
|
+
if (isSet)
|
|
593
|
+
data = {[utils.text.random()]: data} as Record<string, GraphValue>
|
|
530
594
|
|
|
531
595
|
const _ack = ack(ctxid)
|
|
532
596
|
const result = check(data)
|
|
@@ -535,15 +599,15 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
535
599
|
return
|
|
536
600
|
}
|
|
537
601
|
|
|
538
|
-
const resolved = resolve({
|
|
602
|
+
const resolved = resolve({put: data, _opt: {put: true}}, _ack as never)
|
|
539
603
|
if (!resolved) return
|
|
540
604
|
|
|
541
|
-
const {
|
|
605
|
+
const {item, soul} = resolved
|
|
542
606
|
if (!soul) return
|
|
543
607
|
|
|
544
608
|
if (result === true) {
|
|
545
609
|
wire.get(
|
|
546
|
-
{
|
|
610
|
+
{"#": soul, ".": item!},
|
|
547
611
|
async msg => {
|
|
548
612
|
if (msg.err) {
|
|
549
613
|
_ack(`error getting ${soul}: ${msg.err}`)
|
|
@@ -556,14 +620,19 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
556
620
|
if (!id) {
|
|
557
621
|
if (!node) node = {} as never
|
|
558
622
|
node[item!] = (isSet ? data : data) as GraphValue
|
|
559
|
-
const g = await graph(
|
|
623
|
+
const g = await graph(
|
|
624
|
+
soul,
|
|
625
|
+
node as never,
|
|
626
|
+
ctx.user,
|
|
627
|
+
_ack as never,
|
|
628
|
+
)
|
|
560
629
|
if (g === null) return
|
|
561
630
|
|
|
562
631
|
wire.put(g, _ack as never)
|
|
563
632
|
return
|
|
564
633
|
}
|
|
565
634
|
|
|
566
|
-
wire.get({
|
|
635
|
+
wire.get({"#": id}, async msg => {
|
|
567
636
|
if (msg.err) {
|
|
568
637
|
_ack(`error getting ${id}: ${msg.err}`)
|
|
569
638
|
return
|
|
@@ -583,12 +652,14 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
583
652
|
continue
|
|
584
653
|
}
|
|
585
654
|
|
|
586
|
-
const err = await new Promise<string | null | undefined>(
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
655
|
+
const err = await new Promise<string | null | undefined>(
|
|
656
|
+
res => {
|
|
657
|
+
const _ctxid = utils.text.random()
|
|
658
|
+
const chain: ChainItem[] = [{item: key, soul: id}]
|
|
659
|
+
allctx.set(_ctxid, {chain: chain, user: ctx.user})
|
|
660
|
+
api(_ctxid).put(null!, res as never)
|
|
661
|
+
},
|
|
662
|
+
)
|
|
592
663
|
if (err) {
|
|
593
664
|
_ack(err)
|
|
594
665
|
return
|
|
@@ -596,20 +667,30 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
596
667
|
}
|
|
597
668
|
if (!node) node = {} as never
|
|
598
669
|
node[item!] = data as GraphValue
|
|
599
|
-
const g = await graph(
|
|
670
|
+
const g = await graph(
|
|
671
|
+
soul,
|
|
672
|
+
node as never,
|
|
673
|
+
ctx.user,
|
|
674
|
+
_ack as never,
|
|
675
|
+
)
|
|
600
676
|
if (g === null) return
|
|
601
677
|
|
|
602
678
|
wire.put(g, _ack as never)
|
|
603
679
|
})
|
|
604
680
|
},
|
|
605
|
-
{
|
|
681
|
+
{
|
|
682
|
+
secure:
|
|
683
|
+
(typeof ctx.user === "boolean" ? ctx.user : !!ctx.user) ||
|
|
684
|
+
options.secure,
|
|
685
|
+
put: true,
|
|
686
|
+
},
|
|
606
687
|
)
|
|
607
688
|
return
|
|
608
689
|
}
|
|
609
690
|
|
|
610
691
|
const attemptRead = (retries = 0): void => {
|
|
611
692
|
wire.get(
|
|
612
|
-
{
|
|
693
|
+
{"#": soul, ".": item!},
|
|
613
694
|
async msg => {
|
|
614
695
|
if (msg.err) {
|
|
615
696
|
_ack(`error getting ${soul}.${item}: ${msg.err}`)
|
|
@@ -628,7 +709,12 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
628
709
|
if (!id) {
|
|
629
710
|
if (!node) node = {} as never
|
|
630
711
|
node[item!] = utils.rel.ify(utils.text.random())
|
|
631
|
-
const g = await graph(
|
|
712
|
+
const g = await graph(
|
|
713
|
+
soul,
|
|
714
|
+
node as never,
|
|
715
|
+
ctx.user,
|
|
716
|
+
_ack as never,
|
|
717
|
+
)
|
|
632
718
|
if (g === null) return
|
|
633
719
|
|
|
634
720
|
wire.put(g, err => {
|
|
@@ -636,7 +722,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
636
722
|
_ack(`error putting ${item} on ${soul}: ${err}`)
|
|
637
723
|
} else {
|
|
638
724
|
const _ctxid = utils.text.random()
|
|
639
|
-
const chain: ChainItem[] = [{
|
|
725
|
+
const chain: ChainItem[] = [{item: item!, soul: soul}]
|
|
640
726
|
allctx.set(_ctxid, {
|
|
641
727
|
chain: chain,
|
|
642
728
|
user: ctx.user,
|
|
@@ -650,20 +736,25 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
650
736
|
|
|
651
737
|
const update: string[] = []
|
|
652
738
|
for (const key of result as string[]) {
|
|
653
|
-
const err = await new Promise<string | null | undefined>(
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
739
|
+
const err = await new Promise<string | null | undefined>(
|
|
740
|
+
res => {
|
|
741
|
+
if (
|
|
742
|
+
utils.obj.is((data as Record<string, unknown>)[key]) &&
|
|
743
|
+
!utils.rel.is((data as Record<string, GraphValue>)[key])
|
|
744
|
+
) {
|
|
745
|
+
const _ctxid = utils.text.random()
|
|
746
|
+
const chain: ChainItem[] = [{item: key, soul: id}]
|
|
747
|
+
allctx.set(_ctxid, {chain: chain, user: ctx.user})
|
|
748
|
+
api(_ctxid).put(
|
|
749
|
+
(data as Record<string, GraphValue>)[key]!,
|
|
750
|
+
res as never,
|
|
751
|
+
)
|
|
752
|
+
} else {
|
|
753
|
+
update.push(key)
|
|
754
|
+
res(null)
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
)
|
|
667
758
|
if (err) {
|
|
668
759
|
_ack(err)
|
|
669
760
|
return
|
|
@@ -681,12 +772,22 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
681
772
|
update.forEach(key => {
|
|
682
773
|
updateNode[key] = (data as Record<string, GraphValue>)[key]!
|
|
683
774
|
})
|
|
684
|
-
const g = await graph(
|
|
775
|
+
const g = await graph(
|
|
776
|
+
id,
|
|
777
|
+
updateNode as never,
|
|
778
|
+
ctx.user,
|
|
779
|
+
_ack as never,
|
|
780
|
+
)
|
|
685
781
|
if (g === null) return
|
|
686
782
|
|
|
687
783
|
wire.put(g, _ack as never)
|
|
688
784
|
},
|
|
689
|
-
{
|
|
785
|
+
{
|
|
786
|
+
secure:
|
|
787
|
+
(typeof ctx.user === "boolean" ? ctx.user : !!ctx.user) ||
|
|
788
|
+
options.secure,
|
|
789
|
+
put: true,
|
|
790
|
+
},
|
|
690
791
|
)
|
|
691
792
|
}
|
|
692
793
|
attemptRead()
|
|
@@ -696,7 +797,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
696
797
|
lex: LexWithDot | ((data: unknown) => void),
|
|
697
798
|
cb?: ((data: unknown) => void) | boolean,
|
|
698
799
|
_get?: boolean,
|
|
699
|
-
_opt?: WireOptions
|
|
800
|
+
_opt?: WireOptions,
|
|
700
801
|
): void {
|
|
701
802
|
let lexFilter: LexFilter | undefined
|
|
702
803
|
let callback: (data: unknown) => void
|
|
@@ -724,16 +825,19 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
724
825
|
return
|
|
725
826
|
}
|
|
726
827
|
|
|
727
|
-
const resolved = resolve(
|
|
828
|
+
const resolved = resolve(
|
|
829
|
+
{on: lexFilter, _get: _get, _opt: opts},
|
|
830
|
+
callback,
|
|
831
|
+
)
|
|
728
832
|
if (!resolved) return
|
|
729
833
|
|
|
730
|
-
const {
|
|
834
|
+
const {item, soul} = resolved
|
|
731
835
|
if (!soul) return
|
|
732
836
|
|
|
733
837
|
const ctx = allctx.get(ctxid)
|
|
734
838
|
|
|
735
839
|
allctx.set(ctxid, {
|
|
736
|
-
chain: [{
|
|
840
|
+
chain: [{item: item, soul: soul}],
|
|
737
841
|
on: true,
|
|
738
842
|
user: ctx ? ctx.user : null,
|
|
739
843
|
})
|
|
@@ -755,7 +859,7 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
755
859
|
clearTimeout(retryTimer!)
|
|
756
860
|
retryTimer = null
|
|
757
861
|
wire.get(
|
|
758
|
-
{
|
|
862
|
+
{"#": soul, ".": item!},
|
|
759
863
|
msg => {
|
|
760
864
|
const node = msg.put && msg.put[soul]
|
|
761
865
|
const current = node && node[item!]
|
|
@@ -765,10 +869,16 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
765
869
|
const data = await new Promise<unknown>(res => {
|
|
766
870
|
const _ctxid = utils.text.random()
|
|
767
871
|
allctx.set(_ctxid, {
|
|
768
|
-
chain: [{
|
|
872
|
+
chain: [{item: null, soul: id}],
|
|
769
873
|
user: ctx ? ctx.user : null,
|
|
770
874
|
})
|
|
771
|
-
api(_ctxid).next(
|
|
875
|
+
api(_ctxid).next(
|
|
876
|
+
null as never,
|
|
877
|
+
res as never,
|
|
878
|
+
(retries === 0
|
|
879
|
+
? utils.obj.put(opts, "fast", true)
|
|
880
|
+
: opts) as never,
|
|
881
|
+
)
|
|
772
882
|
})
|
|
773
883
|
if (data !== null || retries >= 5) {
|
|
774
884
|
retryCount = 0
|
|
@@ -789,20 +899,32 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
789
899
|
// exhaust retries. on() with _get can return null on a cache
|
|
790
900
|
// miss and never fire again if the data doesn't change while
|
|
791
901
|
// the listener is attached, so we must poll for initial load.
|
|
792
|
-
const secureOpt = {
|
|
902
|
+
const secureOpt = {
|
|
903
|
+
...opts,
|
|
904
|
+
secure: ctx
|
|
905
|
+
? (typeof ctx.user === "boolean"
|
|
906
|
+
? ctx.user
|
|
907
|
+
: !!ctx.user) || options.secure
|
|
908
|
+
: options.secure,
|
|
909
|
+
}
|
|
793
910
|
const retry = (): void => {
|
|
794
|
-
const delay = Math.min(
|
|
911
|
+
const delay = Math.min(
|
|
912
|
+
retryDelay * Math.pow(2, retryCount),
|
|
913
|
+
30000,
|
|
914
|
+
)
|
|
795
915
|
retryCount++
|
|
796
916
|
retryTimer = setTimeout(() => {
|
|
797
917
|
wire.get(
|
|
798
|
-
{
|
|
918
|
+
{"#": soul, ".": item!},
|
|
799
919
|
retryMsg => {
|
|
800
920
|
const retryNode = retryMsg.put && retryMsg.put[soul]
|
|
801
921
|
if (retryNode) {
|
|
802
922
|
retryCount = 0
|
|
803
923
|
retryTimer = null
|
|
804
924
|
const retryValue = retryNode[item!]
|
|
805
|
-
callback(
|
|
925
|
+
callback(
|
|
926
|
+
retryValue !== undefined ? retryValue : null,
|
|
927
|
+
)
|
|
806
928
|
} else if (retryCount < maxRetries) {
|
|
807
929
|
retry()
|
|
808
930
|
} else {
|
|
@@ -820,20 +942,29 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
820
942
|
}
|
|
821
943
|
}
|
|
822
944
|
},
|
|
823
|
-
{
|
|
945
|
+
{
|
|
946
|
+
...opts,
|
|
947
|
+
secure: ctx
|
|
948
|
+
? (typeof ctx.user === "boolean" ? ctx.user : !!ctx.user) ||
|
|
949
|
+
options.secure
|
|
950
|
+
: options.secure,
|
|
951
|
+
},
|
|
824
952
|
)
|
|
825
953
|
})
|
|
826
954
|
|
|
827
|
-
let initialLex: {
|
|
828
|
-
? (utils.obj.put(lexFilter as never, "#", soul) as {
|
|
829
|
-
|
|
955
|
+
let initialLex: {"#": string; "."?: string | number} = lexFilter
|
|
956
|
+
? (utils.obj.put(lexFilter as never, "#", soul) as {
|
|
957
|
+
"#": string
|
|
958
|
+
"."?: string | number
|
|
959
|
+
})
|
|
960
|
+
: {"#": soul, ".": item!}
|
|
830
961
|
if (initialLex["."] != null && typeof initialLex["."] === "number") {
|
|
831
|
-
initialLex = {
|
|
962
|
+
initialLex = {...initialLex, ".": String(initialLex["."])}
|
|
832
963
|
}
|
|
833
964
|
wire.on(initialLex as never, map.get(callback)!, false, opts)
|
|
834
965
|
|
|
835
966
|
wire.get(
|
|
836
|
-
{
|
|
967
|
+
{"#": soul, ".": item!},
|
|
837
968
|
msg => {
|
|
838
969
|
if (msg.err) {
|
|
839
970
|
console.log(`error getting ${soul}.${item}: ${msg.err}`)
|
|
@@ -844,14 +975,25 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
844
975
|
const id = utils.rel.is(current as GraphValue)
|
|
845
976
|
if (id) {
|
|
846
977
|
wire.off(initialLex as never, map.get(callback)!)
|
|
847
|
-
wire.on(
|
|
978
|
+
wire.on(
|
|
979
|
+
{"#": id, ".": null} as never,
|
|
980
|
+
map.get(callback)!,
|
|
981
|
+
false,
|
|
982
|
+
opts,
|
|
983
|
+
)
|
|
848
984
|
if (_get) map.get(callback)!()
|
|
849
985
|
} else if (_get) {
|
|
850
986
|
// Not a rel, but _get was requested, so trigger callback.
|
|
851
987
|
map.get(callback)!()
|
|
852
988
|
}
|
|
853
989
|
},
|
|
854
|
-
{
|
|
990
|
+
{
|
|
991
|
+
...opts,
|
|
992
|
+
secure: ctx
|
|
993
|
+
? (typeof ctx.user === "boolean" ? ctx.user : !!ctx.user) ||
|
|
994
|
+
options.secure
|
|
995
|
+
: options.secure,
|
|
996
|
+
},
|
|
855
997
|
)
|
|
856
998
|
},
|
|
857
999
|
|
|
@@ -862,15 +1004,15 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
862
1004
|
return
|
|
863
1005
|
}
|
|
864
1006
|
|
|
865
|
-
const resolved = resolve({
|
|
1007
|
+
const resolved = resolve({off: true}, cb)
|
|
866
1008
|
if (!resolved) return
|
|
867
1009
|
|
|
868
|
-
const {
|
|
1010
|
+
const {item, soul} = resolved
|
|
869
1011
|
if (!soul) return
|
|
870
1012
|
|
|
871
|
-
wire.off({
|
|
1013
|
+
wire.off({"#": soul} as never, map.get(cb!)!)
|
|
872
1014
|
|
|
873
|
-
wire.get({
|
|
1015
|
+
wire.get({"#": soul, ".": item!}, msg => {
|
|
874
1016
|
if (msg.err) {
|
|
875
1017
|
map.delete(cb!)
|
|
876
1018
|
allctx.delete(ctxid!)
|
|
@@ -879,21 +1021,21 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
879
1021
|
|
|
880
1022
|
const current = msg.put && msg.put[soul] && msg.put[soul]![item!]
|
|
881
1023
|
const id = utils.rel.is(current as GraphValue)
|
|
882
|
-
if (id) wire.off({
|
|
1024
|
+
if (id) wire.off({"#": id} as never, map.get(cb!)!)
|
|
883
1025
|
map.delete(cb!)
|
|
884
1026
|
allctx.delete(ctxid!)
|
|
885
1027
|
})
|
|
886
1028
|
},
|
|
887
1029
|
|
|
888
1030
|
user: () => {
|
|
889
|
-
if (!(user as {
|
|
1031
|
+
if (!(user as {get?: unknown}).get) {
|
|
890
1032
|
Object.assign(user, api())
|
|
891
1033
|
;(user as UserInterface & HolsterAPI).get = function (
|
|
892
1034
|
this: HolsterAPI,
|
|
893
1035
|
keys: string | string[],
|
|
894
1036
|
lex?: LexFilter | ((data: unknown) => void),
|
|
895
1037
|
cb?: ((data: unknown) => void) | WireOptions,
|
|
896
|
-
_opt?: WireOptions
|
|
1038
|
+
_opt?: WireOptions,
|
|
897
1039
|
): HolsterAPI {
|
|
898
1040
|
let lexFilter: LexFilter | null | undefined
|
|
899
1041
|
let callback: ((data: unknown) => void) | undefined
|
|
@@ -940,12 +1082,16 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
940
1082
|
}
|
|
941
1083
|
|
|
942
1084
|
ctxid = utils.text.random()
|
|
943
|
-
const chain: ChainItem[] = [{
|
|
944
|
-
allctx.set(ctxid, {
|
|
1085
|
+
const chain: ChainItem[] = [{item: String(key), soul: "~" + pub}]
|
|
1086
|
+
allctx.set(ctxid, {
|
|
1087
|
+
chain: chain,
|
|
1088
|
+
user: user.is,
|
|
1089
|
+
cb: callback as never,
|
|
1090
|
+
})
|
|
945
1091
|
if (!callback) return api(ctxid)
|
|
946
1092
|
|
|
947
1093
|
const _done = done(ctxid)
|
|
948
|
-
const resolved = resolve({
|
|
1094
|
+
const resolved = resolve({get: lexFilter, _opt: opts}, _done)
|
|
949
1095
|
if (resolved) get(lexFilter as never, resolved.soul!, _done, opts)
|
|
950
1096
|
return this
|
|
951
1097
|
} as never
|
|
@@ -961,4 +1107,3 @@ const Holster = (opt?: HolsterOptions | string | string[]): HolsterAPI => {
|
|
|
961
1107
|
}
|
|
962
1108
|
|
|
963
1109
|
export default Holster
|
|
964
|
-
|