@e280/stz 0.0.0-8 → 0.0.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 +275 -24
- package/package.json +13 -9
- package/s/coalesce.ts +1 -0
- package/s/constructor.ts +15 -0
- package/s/data/base-x.ts +170 -0
- package/s/data/base58.ts +15 -5
- package/s/data/base64.ts +15 -6
- package/s/data/base64url.ts +15 -6
- package/s/data/bytename/bytename.test.ts +12 -12
- package/s/data/bytename/bytename.ts +13 -6
- package/s/data/bytename/index.ts +1 -1
- package/s/data/bytename/thumbprint.test.ts +53 -0
- package/s/data/bytename/thumbprint.ts +112 -0
- package/s/data/bytes.ts +2 -2
- package/s/data/data.test.ts +98 -0
- package/s/data/hex.ts +15 -5
- package/s/data/txt.ts +14 -4
- package/s/deadline.ts +5 -6
- package/s/{defer-promise.ts → defer.ts} +2 -2
- package/s/drill.ts +2 -4
- package/s/ev.ts +20 -0
- package/s/id-counter.ts +9 -0
- package/s/index.ts +12 -1
- package/s/is.ts +10 -4
- package/s/map-g.ts +9 -10
- package/s/nap.ts +1 -0
- package/s/once.ts +14 -0
- package/s/pipe.ts +7 -8
- package/s/pubsub.ts +24 -6
- package/s/queue/queue.test.ts +67 -0
- package/s/queue/queue.ts +21 -0
- package/s/repeat.ts +25 -0
- package/s/scope.ts +108 -0
- package/s/templating.ts +22 -0
- package/s/tests.test.ts +14 -10
- package/s/time.ts +22 -0
- package/x/coalesce.d.ts +1 -0
- package/x/coalesce.js +1 -0
- package/x/coalesce.js.map +1 -1
- package/x/constructor.d.ts +3 -0
- package/x/constructor.js +4 -0
- package/x/constructor.js.map +1 -0
- package/x/data/base-x.d.ts +47 -0
- package/x/data/base-x.js +147 -0
- package/x/data/base-x.js.map +1 -0
- package/x/data/base58.d.ts +5 -1
- package/x/data/base58.js +13 -5
- package/x/data/base58.js.map +1 -1
- package/x/data/base64.d.ts +7 -3
- package/x/data/base64.js +13 -5
- package/x/data/base64.js.map +1 -1
- package/x/data/base64url.d.ts +5 -1
- package/x/data/base64url.js +13 -5
- package/x/data/base64url.js.map +1 -1
- package/x/data/bytename/bytename.d.ts +9 -3
- package/x/data/bytename/bytename.js +13 -6
- package/x/data/bytename/bytename.js.map +1 -1
- package/x/data/bytename/bytename.test.js +12 -12
- package/x/data/bytename/bytename.test.js.map +1 -1
- package/x/data/bytename/index.d.ts +1 -1
- package/x/data/bytename/index.js +1 -1
- package/x/data/bytename/index.js.map +1 -1
- package/x/data/bytename/thumbprint.d.ts +36 -0
- package/x/data/bytename/thumbprint.js +79 -0
- package/x/data/bytename/thumbprint.js.map +1 -0
- package/x/data/bytename/{badge.test.d.ts → thumbprint.test.d.ts} +2 -2
- package/x/data/bytename/thumbprint.test.js +45 -0
- package/x/data/bytename/thumbprint.test.js.map +1 -0
- package/x/data/bytes.d.ts +2 -2
- package/x/data/bytes.js +2 -2
- package/x/data/bytes.js.map +1 -1
- package/x/data/data.test.d.ts +69 -0
- package/x/data/data.test.js +77 -0
- package/x/data/data.test.js.map +1 -0
- package/x/data/hex.d.ts +8 -4
- package/x/data/hex.js +13 -5
- package/x/data/hex.js.map +1 -1
- package/x/data/txt.d.ts +6 -2
- package/x/data/txt.js +12 -4
- package/x/data/txt.js.map +1 -1
- package/x/deadline.d.ts +2 -2
- package/x/deadline.js +4 -4
- package/x/deadline.js.map +1 -1
- package/x/{defer-promise.d.ts → defer.d.ts} +2 -2
- package/x/{defer-promise.js → defer.js} +2 -2
- package/x/defer.js.map +1 -0
- package/x/drill.d.ts +1 -3
- package/x/drill.js +2 -4
- package/x/drill.js.map +1 -1
- package/x/ev.d.ts +6 -0
- package/x/ev.js +10 -0
- package/x/ev.js.map +1 -0
- package/x/id-counter.d.ts +5 -0
- package/x/id-counter.js +10 -0
- package/x/id-counter.js.map +1 -0
- package/x/index.d.ts +11 -1
- package/x/index.js +11 -1
- package/x/index.js.map +1 -1
- package/x/is.d.ts +6 -4
- package/x/is.js +6 -4
- package/x/is.js.map +1 -1
- package/x/map-g.d.ts +3 -3
- package/x/map-g.js +9 -8
- package/x/map-g.js.map +1 -1
- package/x/nap.d.ts +1 -0
- package/x/nap.js +1 -0
- package/x/nap.js.map +1 -1
- package/x/once.d.ts +1 -0
- package/x/once.js +12 -0
- package/x/once.js.map +1 -0
- package/x/pipe.d.ts +3 -3
- package/x/pipe.js +5 -5
- package/x/pipe.js.map +1 -1
- package/x/pubsub.d.ts +12 -1
- package/x/pubsub.js +7 -5
- package/x/pubsub.js.map +1 -1
- package/x/queue/queue.d.ts +4 -0
- package/x/queue/queue.js +14 -0
- package/x/queue/queue.js.map +1 -0
- package/x/queue/queue.test.d.ts +8 -0
- package/x/queue/queue.test.js +57 -0
- package/x/queue/queue.test.js.map +1 -0
- package/x/repeat.d.ts +5 -0
- package/x/repeat.js +23 -0
- package/x/repeat.js.map +1 -0
- package/x/scope.d.ts +37 -0
- package/x/scope.js +82 -0
- package/x/scope.js.map +1 -0
- package/x/templating.d.ts +6 -0
- package/x/templating.js +11 -0
- package/x/templating.js.map +1 -0
- package/x/tests.test.js +14 -10
- package/x/tests.test.js.map +1 -1
- package/x/time.d.ts +18 -0
- package/x/time.js +19 -0
- package/x/time.js.map +1 -0
- package/s/data/anka.ts +0 -66
- package/s/data/bytename/badge.test.ts +0 -53
- package/s/data/bytename/badge.ts +0 -81
- package/x/data/anka.d.ts +0 -5
- package/x/data/anka.js +0 -53
- package/x/data/anka.js.map +0 -1
- package/x/data/bytename/badge.d.ts +0 -27
- package/x/data/bytename/badge.js +0 -57
- package/x/data/bytename/badge.js.map +0 -1
- package/x/data/bytename/badge.test.js +0 -45
- package/x/data/bytename/badge.test.js.map +0 -1
- package/x/defer-promise.js.map +0 -1
|
@@ -11,24 +11,24 @@ function good(bytes: Uint8Array) {
|
|
|
11
11
|
|
|
12
12
|
export default Science.suite({
|
|
13
13
|
"bytes->string->bytes": test(async() => {
|
|
14
|
-
const text = Bytename.
|
|
14
|
+
const text = Bytename.fromBytes(deadbeef)
|
|
15
15
|
expect(text).is("ribmug.hilmun")
|
|
16
|
-
good(Bytename.
|
|
16
|
+
good(Bytename.toBytes(text))
|
|
17
17
|
}),
|
|
18
18
|
|
|
19
19
|
"zero bytes": test(async() => {
|
|
20
|
-
const text = Bytename.
|
|
20
|
+
const text = Bytename.fromBytes(new Uint8Array([]))
|
|
21
21
|
expect(text).is("")
|
|
22
22
|
}),
|
|
23
23
|
|
|
24
24
|
"one byte": test(async() => {
|
|
25
|
-
const text = Bytename.
|
|
25
|
+
const text = Bytename.fromBytes(new Uint8Array([0x00]))
|
|
26
26
|
expect(text).is("doz")
|
|
27
27
|
}),
|
|
28
28
|
|
|
29
29
|
"groupings": test(async() => {
|
|
30
30
|
const bytes = new Uint8Array([...deadbeef, ...deadbeef])
|
|
31
|
-
const text = Bytename.
|
|
31
|
+
const text = Bytename.fromBytes(bytes, {
|
|
32
32
|
groupSize: 2,
|
|
33
33
|
groupSeparator: " ",
|
|
34
34
|
wordSeparator: ".",
|
|
@@ -37,13 +37,13 @@ export default Science.suite({
|
|
|
37
37
|
}),
|
|
38
38
|
|
|
39
39
|
"wordsep": Science.suite({
|
|
40
|
-
"underscore": test(async() => good(Bytename.
|
|
41
|
-
"uppercase": test(async() => good(Bytename.
|
|
42
|
-
"dots": test(async() => good(Bytename.
|
|
43
|
-
"crushed": test(async() => good(Bytename.
|
|
44
|
-
"space": test(async() => good(Bytename.
|
|
45
|
-
"spaces": test(async() => good(Bytename.
|
|
46
|
-
"whitespace": test(async() => good(Bytename.
|
|
40
|
+
"underscore": test(async() => good(Bytename.toBytes("ribmug_hilmun"))),
|
|
41
|
+
"uppercase": test(async() => good(Bytename.toBytes("RIBMUG_HILMUN"))),
|
|
42
|
+
"dots": test(async() => good(Bytename.toBytes("ribmug.hilmun"))),
|
|
43
|
+
"crushed": test(async() => good(Bytename.toBytes("ribmughilmun"))),
|
|
44
|
+
"space": test(async() => good(Bytename.toBytes("ribmug hilmun"))),
|
|
45
|
+
"spaces": test(async() => good(Bytename.toBytes("ribmug hilmun"))),
|
|
46
|
+
"whitespace": test(async() => good(Bytename.toBytes("\n ribmug \n \t \n hilmun \n"))),
|
|
47
47
|
}),
|
|
48
48
|
})
|
|
49
49
|
|
|
@@ -10,6 +10,12 @@ export type BytenameOptions = {
|
|
|
10
10
|
groupSeparator: string
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Bytename is a friendly presentation format for arbitrary binary data.
|
|
15
|
+
* - looks like "midsen.picmyn.widrep.baclut dotreg.filtyp.nosnus.siptev"
|
|
16
|
+
* - each byte maps to a three-letter triplet
|
|
17
|
+
* - all delimiters are just sugar, parser doesn't care
|
|
18
|
+
*/
|
|
13
19
|
export const Bytename = {
|
|
14
20
|
defaults: (<BytenameOptions>{
|
|
15
21
|
groupSize: 4,
|
|
@@ -18,10 +24,11 @@ export const Bytename = {
|
|
|
18
24
|
}),
|
|
19
25
|
|
|
20
26
|
random(byteCount: number, options?: Partial<BytenameOptions>) {
|
|
21
|
-
|
|
27
|
+
const bytes = Bytes.random(byteCount)
|
|
28
|
+
return this.fromBytes(bytes, options)
|
|
22
29
|
},
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
fromBytes(bytes: Uint8Array, options: Partial<BytenameOptions> = {}) {
|
|
25
32
|
const {
|
|
26
33
|
groupSize = Bytename.defaults.groupSize,
|
|
27
34
|
wordSeparator = Bytename.defaults.wordSeparator,
|
|
@@ -50,7 +57,7 @@ export const Bytename = {
|
|
|
50
57
|
return grouped.join(groupSeparator)
|
|
51
58
|
},
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
toBytes(bytename: string) {
|
|
54
61
|
const letters = bytename
|
|
55
62
|
.toLowerCase()
|
|
56
63
|
.replace(/[^a-z]/g, "") // strip everything except letters
|
|
@@ -72,12 +79,12 @@ export const Bytename = {
|
|
|
72
79
|
}))
|
|
73
80
|
},
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
return Hex.
|
|
82
|
+
toHex(bytename: string) {
|
|
83
|
+
return Hex.fromBytes(Bytename.toBytes(bytename))
|
|
77
84
|
},
|
|
78
85
|
|
|
79
86
|
fromHex(hex: string, options?: Partial<BytenameOptions>) {
|
|
80
|
-
return Bytename.
|
|
87
|
+
return Bytename.fromBytes(Hex.toBytes(hex), options)
|
|
81
88
|
},
|
|
82
89
|
}
|
|
83
90
|
|
package/s/data/bytename/index.ts
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
|
|
2
|
+
import {Science, test, expect} from "@e280/science"
|
|
3
|
+
import {Hex} from "../hex.js"
|
|
4
|
+
import {Bytes} from "../bytes.js"
|
|
5
|
+
import {Thumbprint} from "./thumbprint.js"
|
|
6
|
+
|
|
7
|
+
const sampleThumbprint = "nodlyn.fasrep.habbud.ralwel.Avo7gFmdWMRHkwsD149mcaBoZdS69iXuJ"
|
|
8
|
+
const sampleHex = "88e8c3fad1028fcf6ce5ac491578850f4d833336feca03b608265501c3019d59"
|
|
9
|
+
const sampleBytes = Hex.toBytes(sampleHex)
|
|
10
|
+
|
|
11
|
+
function good(bytes: Uint8Array) {
|
|
12
|
+
expect(Bytes.eq(bytes, sampleBytes)).ok()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default Science.suite({
|
|
16
|
+
"bytes->string->bytes": test(async() => {
|
|
17
|
+
const text = Thumbprint.fromBytes(sampleBytes)
|
|
18
|
+
expect(text).is(sampleThumbprint)
|
|
19
|
+
good(Thumbprint.toBytes(text))
|
|
20
|
+
}),
|
|
21
|
+
|
|
22
|
+
"zero bytes": test(async() => {
|
|
23
|
+
const text = Thumbprint.fromBytes(new Uint8Array([]))
|
|
24
|
+
expect(text).is("")
|
|
25
|
+
expect(Bytes.eq(Thumbprint.toBytes(text), new Uint8Array([]))).ok()
|
|
26
|
+
}),
|
|
27
|
+
|
|
28
|
+
"one byte": test(async() => {
|
|
29
|
+
const text = Thumbprint.fromBytes(new Uint8Array([0x00]))
|
|
30
|
+
expect(text).is("doz")
|
|
31
|
+
expect(Bytes.eq(Thumbprint.toBytes(text), new Uint8Array([0x00]))).ok()
|
|
32
|
+
}),
|
|
33
|
+
|
|
34
|
+
"partially": Science.suite({
|
|
35
|
+
"normal": test(async() => expect(Thumbprint.toBytes(sampleThumbprint).length).ok()),
|
|
36
|
+
"nothing": test(async() => expect(Thumbprint.toBytes("").length).is(0)),
|
|
37
|
+
"no-bulk": test(async() => expect(Thumbprint.toBytes("nodlyn.fasrep").length).ok()),
|
|
38
|
+
"one-byte": test(async() => expect(Thumbprint.toBytes("nod").length).is(1)),
|
|
39
|
+
"three-byte": test(async() => expect(Thumbprint.toBytes("nodlynfas").length).is(3)),
|
|
40
|
+
"one-byte-sigil": test(async() => expect(Thumbprint.toBytes("nod::39gfeGFAAnBzH5pkT7EdoETMUMAekG9h1iymk6k").length).ok()),
|
|
41
|
+
}),
|
|
42
|
+
|
|
43
|
+
"tolerance": Science.suite({
|
|
44
|
+
"normal": test(async() => good(Thumbprint.toBytes("nodlyn.fasrep::39gfeGFAAnBzH5pkT7EdoETMUMAekG9h1iymk6k"))),
|
|
45
|
+
"coolio": test(async() => good(Thumbprint.toBytes("nodlyn.fasrep..39gfeGFAAnBzH5pkT7EdoETMUMAekG9h1iymk6k"))),
|
|
46
|
+
"spaces": test(async() => good(Thumbprint.toBytes("nodlyn fasrep 39gfeGFAAnBzH5pkT7EdoETMUMAekG9h1iymk6k"))),
|
|
47
|
+
"dots": test(async() => good(Thumbprint.toBytes("nodlyn.fasrep.39gfeGFAAnBzH5pkT7EdoETMUMAekG9h1iymk6k"))),
|
|
48
|
+
"one-space": test(async() => good(Thumbprint.toBytes("nodlynfasrep 39gfeGFAAnBzH5pkT7EdoETMUMAekG9h1iymk6k"))),
|
|
49
|
+
"one-newline": test(async() => good(Thumbprint.toBytes("nodlynfasrep\n39gfeGFAAnBzH5pkT7EdoETMUMAekG9h1iymk6k"))),
|
|
50
|
+
"whitespace": test(async() => good(Thumbprint.toBytes("\t\n nodlyn\n fasrep \n\n\t\n 39gfeGFAAnBzH5pkT7EdoETMUMAekG9h1iymk6k\n\t"))),
|
|
51
|
+
}),
|
|
52
|
+
})
|
|
53
|
+
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
|
|
2
|
+
import {Hex} from "../hex.js"
|
|
3
|
+
import {Base58} from "../base58.js"
|
|
4
|
+
import {Bytename} from "./bytename.js"
|
|
5
|
+
|
|
6
|
+
export type ThumbprintOptions = {
|
|
7
|
+
delimiter: string
|
|
8
|
+
sigilBytes: number
|
|
9
|
+
previewBytes: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ThumbprintData = {
|
|
13
|
+
bytes: Uint8Array
|
|
14
|
+
thumbprint: string
|
|
15
|
+
preview: string
|
|
16
|
+
bulk: string
|
|
17
|
+
sigil: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Thumbprint is a friendly presentation format for arbitrary binary data.
|
|
22
|
+
* - looks like "nodlyn.fasrep.habbud.ralwel.Avo7gFmdWMRHkwsD149mcaBoZdS69iXuJ"
|
|
23
|
+
* - the preview: "nodlyn.fasrep.habbud.ralwel" (bytename format)
|
|
24
|
+
* - the bulk: "Avo7gFmdWMRHkwsD149mcaBoZdS69iXuJ" (base58 format)
|
|
25
|
+
* - the sigil: "nodlyn.fasrep" (shorter part of the bytename)
|
|
26
|
+
* - originally designed to be a nice way to present 256-bit ids, but can actually represent any number of bytes
|
|
27
|
+
*/
|
|
28
|
+
export const Thumbprint = {
|
|
29
|
+
defaults: (<ThumbprintOptions>{
|
|
30
|
+
delimiter: ".",
|
|
31
|
+
sigilBytes: 4,
|
|
32
|
+
previewBytes: 8,
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
toBytes(thumbprint: string) {
|
|
36
|
+
thumbprint = thumbprint.trim()
|
|
37
|
+
const beans = thumbprint.split(/[^a-zA-Z0-9]+/m)
|
|
38
|
+
.filter(Boolean)
|
|
39
|
+
.map(s => s.trim())
|
|
40
|
+
|
|
41
|
+
if (beans.length < 2)
|
|
42
|
+
return Bytename.toBytes(beans.join(""))
|
|
43
|
+
|
|
44
|
+
const bulk = beans.pop()!
|
|
45
|
+
const preview = beans.join("")
|
|
46
|
+
|
|
47
|
+
return new Uint8Array([
|
|
48
|
+
...Bytename.toBytes(preview),
|
|
49
|
+
...Base58.toBytes(bulk),
|
|
50
|
+
])
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
parse(thumbprint: string, options?: Partial<ThumbprintOptions>): ThumbprintData {
|
|
54
|
+
const bytes = Thumbprint.toBytes(thumbprint)
|
|
55
|
+
return Thumbprint.build.fromBytes(bytes, options)
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
build: {
|
|
59
|
+
fromBytes(bytes: Uint8Array, options: Partial<ThumbprintOptions> = {}): ThumbprintData {
|
|
60
|
+
const {delimiter, previewBytes, sigilBytes}
|
|
61
|
+
= {...Thumbprint.defaults, ...options}
|
|
62
|
+
|
|
63
|
+
const yoink = (b: number) => (bytes.length > 0)
|
|
64
|
+
? Bytename.fromBytes(bytes.slice(0, b), {
|
|
65
|
+
wordSeparator: delimiter,
|
|
66
|
+
groupSeparator: delimiter,
|
|
67
|
+
})
|
|
68
|
+
: ""
|
|
69
|
+
|
|
70
|
+
const sigil = yoink(sigilBytes)
|
|
71
|
+
const preview = yoink(previewBytes)
|
|
72
|
+
|
|
73
|
+
const bulk = (bytes.length > previewBytes)
|
|
74
|
+
? Base58.fromBytes(bytes.slice(previewBytes))
|
|
75
|
+
: ""
|
|
76
|
+
|
|
77
|
+
const thumbprint = [preview, bulk]
|
|
78
|
+
.filter(s => s.length > 0)
|
|
79
|
+
.join(delimiter)
|
|
80
|
+
|
|
81
|
+
return {bytes, thumbprint, preview, bulk, sigil}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
fromHex(hex: string, options?: Partial<ThumbprintOptions>) {
|
|
85
|
+
const bytes = Hex.toBytes(hex)
|
|
86
|
+
return Thumbprint.build.fromBytes(bytes, options)
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
toHex(thumbprint: string) {
|
|
91
|
+
const bytes = Thumbprint.toBytes(thumbprint)
|
|
92
|
+
return Hex.fromBytes(bytes)
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
fromBytes(bytes: Uint8Array, options?: Partial<ThumbprintOptions>) {
|
|
96
|
+
return Thumbprint.build.fromBytes(bytes, options).thumbprint
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
fromHex(hex: string, options?: Partial<ThumbprintOptions>) {
|
|
100
|
+
return Thumbprint.fromBytes(Hex.toBytes(hex), options)
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
sigil: {
|
|
104
|
+
fromHex(hex: string, options?: Partial<ThumbprintOptions>) {
|
|
105
|
+
return Thumbprint.build.fromHex(hex, options).sigil
|
|
106
|
+
},
|
|
107
|
+
fromBytes(bytes: Uint8Array, options?: Partial<ThumbprintOptions>) {
|
|
108
|
+
return Thumbprint.build.fromBytes(bytes, options).sigil
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
|
package/s/data/bytes.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
export const Bytes = {
|
|
2
|
+
export const Bytes = Object.freeze({
|
|
3
3
|
eq(a: Uint8Array, b: Uint8Array) {
|
|
4
4
|
if (a.length !== b.length)
|
|
5
5
|
return false
|
|
@@ -13,5 +13,5 @@ export const Bytes = {
|
|
|
13
13
|
random(count: number) {
|
|
14
14
|
return crypto.getRandomValues(new Uint8Array(count))
|
|
15
15
|
},
|
|
16
|
-
}
|
|
16
|
+
})
|
|
17
17
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
|
|
2
|
+
import {Science, test, expect} from "@e280/science"
|
|
3
|
+
import {ob} from "../ob.js"
|
|
4
|
+
import {Txt} from "./txt.js"
|
|
5
|
+
import {Hex} from "./hex.js"
|
|
6
|
+
import {Bytes} from "./bytes.js"
|
|
7
|
+
import {BaseX} from "./base-x.js"
|
|
8
|
+
import {Base58} from "./base58.js"
|
|
9
|
+
import {Base64} from "./base64.js"
|
|
10
|
+
import {Base64url} from "./base64url.js"
|
|
11
|
+
|
|
12
|
+
const sampleBytes = Hex.toBytes("9960cd633a46acfe8307d8a400e842da0d930a75fb8188e0f5da264e4b6b4e5b")
|
|
13
|
+
|
|
14
|
+
type ByteUtil = {
|
|
15
|
+
toBytes: (string: string) => Uint8Array
|
|
16
|
+
fromBytes: (bytes: Uint8Array) => string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type NumberUtil = {
|
|
20
|
+
toInteger: (s: string) => number
|
|
21
|
+
fromInteger: (n: number) => string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function testBytes(util: ByteUtil) {
|
|
25
|
+
return async() => {
|
|
26
|
+
const string = util.fromBytes(sampleBytes)
|
|
27
|
+
const recreated = util.toBytes(string)
|
|
28
|
+
expect(Bytes.eq(sampleBytes, recreated)).ok()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function testNumbers(util: NumberUtil) {
|
|
33
|
+
return {
|
|
34
|
+
positive: async() => {
|
|
35
|
+
const original = 123456789
|
|
36
|
+
const string = util.fromInteger(original)
|
|
37
|
+
const recreated = util.toInteger(string)
|
|
38
|
+
expect(original).is(recreated)
|
|
39
|
+
expect(util.toInteger("")).is(0)
|
|
40
|
+
},
|
|
41
|
+
negative: async() => {
|
|
42
|
+
const original = -123456789
|
|
43
|
+
const string = util.fromInteger(original)
|
|
44
|
+
const recreated = util.toInteger(string)
|
|
45
|
+
expect(original).is(recreated)
|
|
46
|
+
expect(util.toInteger("")).is(0)
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function testBoth(util: ByteUtil & NumberUtil) {
|
|
52
|
+
return {
|
|
53
|
+
bytes: testBytes(util),
|
|
54
|
+
numbers: testNumbers(util),
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function testCompat(alpha: ByteUtil, bravo: ByteUtil) {
|
|
59
|
+
return async() => {
|
|
60
|
+
expect(alpha.fromBytes(sampleBytes))
|
|
61
|
+
.is(bravo.fromBytes(sampleBytes))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default Science.suite({
|
|
66
|
+
"Base58": testBytes(Base58),
|
|
67
|
+
"Base64": testBytes(Base64),
|
|
68
|
+
"Base64url": testBytes(Base64url),
|
|
69
|
+
"Hex": testBytes(Hex),
|
|
70
|
+
|
|
71
|
+
"Txt": test(async() => {
|
|
72
|
+
const original = `build or die. 💻>☠️ 命火工`
|
|
73
|
+
const bytes = Txt.toBytes(original)
|
|
74
|
+
const recreated = Txt.fromBytes(bytes)
|
|
75
|
+
expect(original).is(recreated)
|
|
76
|
+
}),
|
|
77
|
+
|
|
78
|
+
"BaseX": Science.suite({
|
|
79
|
+
lexicons: Science.suite(
|
|
80
|
+
ob(BaseX.lexicons).map(lex => testBoth(new BaseX(lex)))
|
|
81
|
+
),
|
|
82
|
+
compat: Science.suite({
|
|
83
|
+
"Hex": testCompat(Hex, new BaseX(BaseX.lexicons.hex)),
|
|
84
|
+
"Base64": testCompat(Base64, new BaseX(BaseX.lexicons.base64)),
|
|
85
|
+
"Base64url": testCompat(Base64url, new BaseX(BaseX.lexicons.base64url)),
|
|
86
|
+
}),
|
|
87
|
+
"base64 has padding": test(async() => {
|
|
88
|
+
const s = new BaseX(BaseX.lexicons.base64).fromBytes(sampleBytes)
|
|
89
|
+
expect(s.endsWith("=")).ok()
|
|
90
|
+
}),
|
|
91
|
+
"antagonistic base64 positive integer": test(async() => {
|
|
92
|
+
const base64url = new BaseX(BaseX.lexicons.base64url)
|
|
93
|
+
expect(base64url.toInteger(base64url.fromInteger(62))).is(62)
|
|
94
|
+
expect(base64url.toInteger(base64url.fromInteger(4030))).is(4030)
|
|
95
|
+
}),
|
|
96
|
+
}),
|
|
97
|
+
})
|
|
98
|
+
|
package/s/data/hex.ts
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
|
|
12
12
|
import {Bytes} from "./bytes.js"
|
|
13
13
|
|
|
14
|
-
export const Hex = {
|
|
15
|
-
|
|
14
|
+
export const Hex = Object.freeze({
|
|
15
|
+
fromBytes(bytes: Uint8Array) {
|
|
16
16
|
return [...bytes]
|
|
17
17
|
.map(byte => byte.toString(16).padStart(2, "0"))
|
|
18
18
|
.join("")
|
|
19
19
|
},
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
toBytes(string: string) {
|
|
22
22
|
if (string.length % 2 !== 0)
|
|
23
23
|
throw new Error("must have even number of hex characters")
|
|
24
24
|
const bytes = new Uint8Array(string.length / 2)
|
|
@@ -29,7 +29,17 @@ export const Hex = {
|
|
|
29
29
|
|
|
30
30
|
/** generate a random hex string. byteCount defaults to 32. */
|
|
31
31
|
random(byteCount = 32) {
|
|
32
|
-
return this.
|
|
32
|
+
return this.fromBytes(Bytes.random(byteCount))
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/** @deprecated renamed to `fromBytes` */
|
|
36
|
+
string(bytes: Uint8Array) {
|
|
37
|
+
return Hex.fromBytes(bytes)
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
/** @deprecated renamed to `toBytes` */
|
|
41
|
+
bytes(string: string) {
|
|
42
|
+
return Hex.toBytes(string)
|
|
33
43
|
},
|
|
34
|
-
}
|
|
44
|
+
})
|
|
35
45
|
|
package/s/data/txt.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
|
|
2
|
-
export const Txt = {
|
|
3
|
-
|
|
2
|
+
export const Txt = Object.freeze({
|
|
3
|
+
fromBytes(bytes: Uint8Array) {
|
|
4
4
|
return new TextDecoder().decode(bytes)
|
|
5
5
|
},
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
toBytes(string: string) {
|
|
8
8
|
return new TextEncoder().encode(string)
|
|
9
9
|
},
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
/** @deprecated renamed to `fromBytes` */
|
|
12
|
+
string(bytes: Uint8Array) {
|
|
13
|
+
return Txt.fromBytes(bytes)
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
/** @deprecated renamed to `toBytes` */
|
|
17
|
+
bytes(string: string) {
|
|
18
|
+
return Txt.toBytes(string)
|
|
19
|
+
},
|
|
20
|
+
})
|
|
11
21
|
|
package/s/deadline.ts
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
|
|
2
2
|
export class DeadlineError extends Error {
|
|
3
3
|
name = this.constructor.name
|
|
4
|
-
constructor(public milliseconds: number
|
|
5
|
-
super(
|
|
4
|
+
constructor(public milliseconds: number) {
|
|
5
|
+
super(`deadline exceeded (${(milliseconds / 1000).toFixed(1)} seconds)`)
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/** set a deadline for a fn to do something, will reject with a `DeadlineError` if it takes too long */
|
|
10
|
-
export function deadline<R>(milliseconds: number,
|
|
10
|
+
export function deadline<R>(milliseconds: number, fn: () => Promise<R>) {
|
|
11
11
|
if (milliseconds <= 0 || milliseconds === Infinity)
|
|
12
12
|
return fn()
|
|
13
13
|
|
|
14
14
|
return new Promise<R>((resolve, reject) => {
|
|
15
|
-
|
|
16
15
|
const id = setTimeout(
|
|
17
|
-
() => reject(new DeadlineError(milliseconds
|
|
16
|
+
() => reject(new DeadlineError(milliseconds)),
|
|
18
17
|
milliseconds,
|
|
19
18
|
)
|
|
20
|
-
|
|
21
19
|
fn()
|
|
22
20
|
.then(resolve)
|
|
23
21
|
.catch(reject)
|
|
24
22
|
.finally(() => clearTimeout(id))
|
|
25
23
|
})
|
|
26
24
|
}
|
|
25
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
/** a promise which can be resolved from the outside */
|
|
3
|
-
export type
|
|
3
|
+
export type Deferred<R = void> = {
|
|
4
4
|
promise: Promise<R>
|
|
5
5
|
resolve: (result: R) => void
|
|
6
6
|
reject: (reason: any) => void
|
|
@@ -10,7 +10,7 @@ export type DeferredPromise<R> = {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/** returns a deferred promise with exposed resolve and reject fns */
|
|
13
|
-
export function
|
|
13
|
+
export function defer<R = void>(): Deferred<R> {
|
|
14
14
|
let resolve!: (result: R) => void
|
|
15
15
|
let reject!: (reason: any) => void
|
|
16
16
|
|
package/s/drill.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import {is} from "./is.js"
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* return a value within an object tree, found at the given path.
|
|
6
|
-
*/
|
|
4
|
+
/** return a value within an object tree, found at the given path. */
|
|
7
5
|
export function drill<xResult>(
|
|
8
6
|
object: {[key: string]: any},
|
|
9
7
|
path: string[],
|
|
@@ -13,7 +11,7 @@ export function drill<xResult>(
|
|
|
13
11
|
|
|
14
12
|
for (const key of path) {
|
|
15
13
|
current = current[key]
|
|
16
|
-
if (is.
|
|
14
|
+
if (is.unset(current))
|
|
17
15
|
break
|
|
18
16
|
}
|
|
19
17
|
|
package/s/ev.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
export type EvFn = (...a: any[]) => void
|
|
3
|
+
|
|
4
|
+
export type EvTarget<N extends string> = {
|
|
5
|
+
addEventListener(name: N, fn: EvFn): void
|
|
6
|
+
removeEventListener(name: N, fn: EvFn): void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function ev<N extends string>(target: EvTarget<N>, fns: Record<N, EvFn>) {
|
|
10
|
+
const entries = Object.entries(fns) as [N, EvFn][]
|
|
11
|
+
|
|
12
|
+
for (const [name, fn] of entries)
|
|
13
|
+
target.addEventListener(name, fn)
|
|
14
|
+
|
|
15
|
+
return () => {
|
|
16
|
+
for (const [name, fn] of entries)
|
|
17
|
+
target.removeEventListener(name, fn)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
package/s/id-counter.ts
ADDED
package/s/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
export * from "./data/bytename/index.js"
|
|
3
|
+
export * from "./data/base-x.js"
|
|
3
4
|
export * from "./data/base58.js"
|
|
4
5
|
export * from "./data/base64.js"
|
|
5
6
|
export * from "./data/base64url.js"
|
|
@@ -12,20 +13,30 @@ export * from "./debounce/types.js"
|
|
|
12
13
|
|
|
13
14
|
export * from "./deep/deep.js"
|
|
14
15
|
|
|
16
|
+
export * from "./queue/queue.js"
|
|
17
|
+
|
|
15
18
|
export * from "./coalesce.js"
|
|
16
19
|
export * from "./concurrent.js"
|
|
20
|
+
export * from "./constructor.js"
|
|
17
21
|
export * from "./deadline.js"
|
|
18
22
|
export * from "./dedupe.js"
|
|
19
|
-
export * from "./defer
|
|
23
|
+
export * from "./defer.js"
|
|
20
24
|
export * from "./drill.js"
|
|
21
25
|
export * from "./escape-regex.js"
|
|
26
|
+
export * from "./ev.js"
|
|
22
27
|
export * from "./hat.js"
|
|
28
|
+
export * from "./id-counter.js"
|
|
23
29
|
export * from "./is.js"
|
|
24
30
|
export * from "./loopy.js"
|
|
25
31
|
export * from "./map-g.js"
|
|
26
32
|
export * from "./nap.js"
|
|
27
33
|
export * from "./ob.js"
|
|
34
|
+
export * from "./once.js"
|
|
28
35
|
export * from "./pipe.js"
|
|
29
36
|
export * from "./pubsub.js"
|
|
37
|
+
export * from "./repeat.js"
|
|
38
|
+
export * from "./scope.js"
|
|
39
|
+
export * from "./templating.js"
|
|
40
|
+
export * from "./time.js"
|
|
30
41
|
export * from "./trash.js"
|
|
31
42
|
|
package/s/is.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
export const is = {
|
|
3
|
-
|
|
2
|
+
export const is = Object.freeze({
|
|
3
|
+
set: <X>(x: X): x is NonNullable<X> =>
|
|
4
4
|
x !== undefined && x !== null,
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
unset: (x: any): x is (undefined | null) =>
|
|
7
7
|
x === undefined || x === null,
|
|
8
8
|
|
|
9
9
|
boolean: (x: any): x is boolean =>
|
|
@@ -23,5 +23,11 @@ export const is = {
|
|
|
23
23
|
|
|
24
24
|
array: (x: any | any[]): x is any[] =>
|
|
25
25
|
Array.isArray(x),
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
fn: (x: any): x is (...a: any[]) => any =>
|
|
28
|
+
typeof x === "function",
|
|
29
|
+
|
|
30
|
+
symbol: (x: any): x is symbol =>
|
|
31
|
+
typeof x === "symbol",
|
|
32
|
+
})
|
|
27
33
|
|
package/s/map-g.ts
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
|
|
2
|
-
/** js map
|
|
2
|
+
/** extended js map with handy methods like `require` and `guarantee` */
|
|
3
3
|
export class MapG<K, V> extends Map<K, V> {
|
|
4
4
|
static require<K, V>(map: Map<K, V>, key: K) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
if (map.has(key))
|
|
6
|
+
return map.get(key)!
|
|
7
|
+
else
|
|
7
8
|
throw new Error(`required key not found: "${key}"`)
|
|
8
|
-
return value
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
static guarantee<K, V>(map: Map<K, V>, key: K, make: () => V) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
value = make()
|
|
12
|
+
if (map.has(key))
|
|
13
|
+
return map.get(key)!
|
|
14
|
+
else {
|
|
15
|
+
const value = make()
|
|
16
16
|
map.set(key, value)
|
|
17
|
+
return value
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
return value
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
array() {
|
package/s/nap.ts
CHANGED