@e280/stz 0.0.0-3 → 0.0.0-30

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.
Files changed (169) hide show
  1. package/README.md +170 -0
  2. package/package.json +14 -7
  3. package/s/clone/clone.test.ts +123 -123
  4. package/s/coalesce.ts +1 -0
  5. package/s/data/base-x.ts +170 -0
  6. package/s/data/base58.ts +15 -5
  7. package/s/data/base64.ts +15 -6
  8. package/s/data/base64url.ts +15 -6
  9. package/s/data/bytename/bytename.test.ts +49 -0
  10. package/s/data/{barname/barname.ts → bytename/bytename.ts} +38 -10
  11. package/s/data/bytename/index.ts +4 -0
  12. package/s/data/bytename/thumbprint.test.ts +53 -0
  13. package/s/data/bytename/thumbprint.ts +112 -0
  14. package/s/data/bytes.ts +12 -2
  15. package/s/data/data.test.ts +98 -0
  16. package/s/data/hex.ts +15 -5
  17. package/s/data/txt.ts +14 -4
  18. package/s/deadline.ts +1 -0
  19. package/s/debounce/debounce.test.ts +73 -73
  20. package/s/deep/deep.test.ts +8 -8
  21. package/s/deep/parts/equal.test.ts +123 -124
  22. package/s/{defer-promise.ts → defer.ts} +2 -2
  23. package/s/drill.ts +2 -4
  24. package/s/ev.ts +20 -0
  25. package/s/id-counter.ts +9 -0
  26. package/s/index.ts +16 -2
  27. package/s/is.ts +10 -4
  28. package/s/loopy.ts +12 -0
  29. package/s/map-g.ts +2 -2
  30. package/s/nap.ts +1 -0
  31. package/s/once.ts +14 -0
  32. package/s/pubsub.ts +24 -6
  33. package/s/queue/queue.test.ts +67 -0
  34. package/s/queue/queue.ts +21 -0
  35. package/s/repeat.ts +25 -0
  36. package/s/tests.test.ts +21 -0
  37. package/s/time.ts +22 -0
  38. package/x/clone/clone.test.d.ts +15 -1
  39. package/x/clone/clone.test.js +106 -124
  40. package/x/clone/clone.test.js.map +1 -1
  41. package/x/coalesce.d.ts +1 -0
  42. package/x/coalesce.js +1 -0
  43. package/x/coalesce.js.map +1 -1
  44. package/x/data/base-x.d.ts +47 -0
  45. package/x/data/base-x.js +147 -0
  46. package/x/data/base-x.js.map +1 -0
  47. package/x/data/base58.d.ts +5 -1
  48. package/x/data/base58.js +13 -5
  49. package/x/data/base58.js.map +1 -1
  50. package/x/data/base64.d.ts +7 -3
  51. package/x/data/base64.js +13 -5
  52. package/x/data/base64.js.map +1 -1
  53. package/x/data/base64url.d.ts +5 -1
  54. package/x/data/base64url.js +13 -5
  55. package/x/data/base64url.js.map +1 -1
  56. package/x/data/bytename/bytename.d.ts +19 -0
  57. package/x/data/{barname/barname.js → bytename/bytename.js} +27 -11
  58. package/x/data/bytename/bytename.js.map +1 -0
  59. package/x/data/bytename/bytename.test.d.ts +17 -0
  60. package/x/data/bytename/bytename.test.js +41 -0
  61. package/x/data/bytename/bytename.test.js.map +1 -0
  62. package/x/data/bytename/index.d.ts +2 -0
  63. package/x/data/bytename/index.js +3 -0
  64. package/x/data/bytename/index.js.map +1 -0
  65. package/x/data/bytename/thumbprint.d.ts +36 -0
  66. package/x/data/bytename/thumbprint.js +79 -0
  67. package/x/data/bytename/thumbprint.js.map +1 -0
  68. package/x/data/bytename/thumbprint.test.d.ts +24 -0
  69. package/x/data/bytename/thumbprint.test.js +45 -0
  70. package/x/data/bytename/thumbprint.test.js.map +1 -0
  71. package/x/data/bytename/utils/prefixes.js.map +1 -0
  72. package/x/data/bytename/utils/suffixes.js.map +1 -0
  73. package/x/data/bytes.d.ts +3 -2
  74. package/x/data/bytes.js +11 -2
  75. package/x/data/bytes.js.map +1 -1
  76. package/x/data/data.test.d.ts +69 -0
  77. package/x/data/data.test.js +77 -0
  78. package/x/data/data.test.js.map +1 -0
  79. package/x/data/hex.d.ts +8 -4
  80. package/x/data/hex.js +13 -5
  81. package/x/data/hex.js.map +1 -1
  82. package/x/data/txt.d.ts +6 -2
  83. package/x/data/txt.js +12 -4
  84. package/x/data/txt.js.map +1 -1
  85. package/x/debounce/debounce.test.d.ts +8 -1
  86. package/x/debounce/debounce.test.js +70 -74
  87. package/x/debounce/debounce.test.js.map +1 -1
  88. package/x/deep/deep.test.d.ts +21 -1
  89. package/x/deep/deep.test.js +5 -9
  90. package/x/deep/deep.test.js.map +1 -1
  91. package/x/deep/parts/equal.test.d.ts +19 -1
  92. package/x/deep/parts/equal.test.js +107 -125
  93. package/x/deep/parts/equal.test.js.map +1 -1
  94. package/x/{defer-promise.d.ts → defer.d.ts} +2 -2
  95. package/x/{defer-promise.js → defer.js} +2 -2
  96. package/x/defer.js.map +1 -0
  97. package/x/drill.d.ts +1 -3
  98. package/x/drill.js +2 -4
  99. package/x/drill.js.map +1 -1
  100. package/x/ev.d.ts +6 -0
  101. package/x/ev.js +10 -0
  102. package/x/ev.js.map +1 -0
  103. package/x/id-counter.d.ts +5 -0
  104. package/x/id-counter.js +10 -0
  105. package/x/id-counter.js.map +1 -0
  106. package/x/index.d.ts +13 -2
  107. package/x/index.js +13 -2
  108. package/x/index.js.map +1 -1
  109. package/x/is.d.ts +6 -4
  110. package/x/is.js +6 -4
  111. package/x/is.js.map +1 -1
  112. package/x/loopy.d.ts +2 -0
  113. package/x/loopy.js +10 -0
  114. package/x/loopy.js.map +1 -0
  115. package/x/map-g.d.ts +3 -3
  116. package/x/map-g.js +1 -1
  117. package/x/map-g.js.map +1 -1
  118. package/x/nap.d.ts +1 -0
  119. package/x/nap.js +1 -0
  120. package/x/nap.js.map +1 -1
  121. package/x/once.d.ts +1 -0
  122. package/x/once.js +12 -0
  123. package/x/once.js.map +1 -0
  124. package/x/pubsub.d.ts +12 -1
  125. package/x/pubsub.js +7 -5
  126. package/x/pubsub.js.map +1 -1
  127. package/x/queue/queue.d.ts +4 -0
  128. package/x/queue/queue.js +14 -0
  129. package/x/queue/queue.js.map +1 -0
  130. package/x/queue/queue.test.d.ts +8 -0
  131. package/x/queue/queue.test.js +57 -0
  132. package/x/queue/queue.test.js.map +1 -0
  133. package/x/repeat.d.ts +5 -0
  134. package/x/repeat.js +23 -0
  135. package/x/repeat.js.map +1 -0
  136. package/x/tests.test.js +18 -0
  137. package/x/tests.test.js.map +1 -0
  138. package/x/time.d.ts +18 -0
  139. package/x/time.js +19 -0
  140. package/x/time.js.map +1 -0
  141. package/readme.md +0 -85
  142. package/s/data/anka.ts +0 -66
  143. package/s/data/barname/badge.ts +0 -78
  144. package/s/data/barname/demo.ts +0 -49
  145. package/s/data/barname/index.ts +0 -4
  146. package/x/data/anka.d.ts +0 -5
  147. package/x/data/anka.js +0 -53
  148. package/x/data/anka.js.map +0 -1
  149. package/x/data/barname/badge.d.ts +0 -25
  150. package/x/data/barname/badge.js +0 -64
  151. package/x/data/barname/badge.js.map +0 -1
  152. package/x/data/barname/barname.d.ts +0 -7
  153. package/x/data/barname/barname.js.map +0 -1
  154. package/x/data/barname/demo.js +0 -45
  155. package/x/data/barname/demo.js.map +0 -1
  156. package/x/data/barname/index.d.ts +0 -2
  157. package/x/data/barname/index.js +0 -3
  158. package/x/data/barname/index.js.map +0 -1
  159. package/x/data/barname/utils/prefixes.js.map +0 -1
  160. package/x/data/barname/utils/suffixes.js.map +0 -1
  161. package/x/defer-promise.js.map +0 -1
  162. /package/{license → LICENSE} +0 -0
  163. /package/s/data/{barname → bytename}/utils/prefixes.ts +0 -0
  164. /package/s/data/{barname → bytename}/utils/suffixes.ts +0 -0
  165. /package/x/data/{barname → bytename}/utils/prefixes.d.ts +0 -0
  166. /package/x/data/{barname → bytename}/utils/prefixes.js +0 -0
  167. /package/x/data/{barname → bytename}/utils/suffixes.d.ts +0 -0
  168. /package/x/data/{barname → bytename}/utils/suffixes.js +0 -0
  169. /package/x/{data/barname/demo.d.ts → tests.test.d.ts} +0 -0
package/s/data/base64.ts CHANGED
@@ -1,22 +1,31 @@
1
1
 
2
2
  import {Bytes} from "./bytes.js"
3
3
 
4
- export const Base64 = {
5
-
6
- string(bytes: Uint8Array) {
4
+ export const Base64 = Object.freeze({
5
+ fromBytes(bytes: Uint8Array) {
7
6
  return (typeof btoa === "function")
8
7
  ? btoa(String.fromCharCode(...bytes))
9
8
  : Buffer.from(bytes).toString("base64")
10
9
  },
11
10
 
12
- bytes(string: string) {
11
+ toBytes(string: string) {
13
12
  return (typeof atob === "function")
14
13
  ? Uint8Array.from(atob(string), char => char.charCodeAt(0))
15
14
  : Uint8Array.from(Buffer.from(string, "base64"))
16
15
  },
17
16
 
18
17
  random(count = 32) {
19
- return this.string(Bytes.random(count))
18
+ return this.fromBytes(Bytes.random(count))
19
+ },
20
+
21
+ /** @deprecated rename to "fromBytes" */
22
+ string(bytes: Uint8Array) {
23
+ return Base64.fromBytes(bytes)
24
+ },
25
+
26
+ /** @deprecated rename to "toBytes" */
27
+ bytes(string: string) {
28
+ return Base64.toBytes(string)
20
29
  },
21
- }
30
+ })
22
31
 
@@ -3,25 +3,34 @@ import {Bytes} from "./bytes.js"
3
3
  import {Base64} from "./base64.js"
4
4
 
5
5
  export const Base64url = {
6
-
7
- string(bytes: Uint8Array) {
8
- return Base64.string(bytes)
6
+ fromBytes(bytes: Uint8Array) {
7
+ return Base64.fromBytes(bytes)
9
8
  .replace(/\+/g, "-")
10
9
  .replace(/\//g, "_")
11
10
  .replace(/=+$/g, "")
12
11
  },
13
12
 
14
- bytes(string: string) {
13
+ toBytes(string: string) {
15
14
  let b64 = string
16
15
  .replace(/-/g, "+")
17
16
  .replace(/_/g, "/")
18
17
  if (b64.length % 4 !== 0)
19
18
  b64 = b64.padEnd(b64.length + (4 - b64.length % 4) % 4, "=")
20
- return Base64.bytes(b64)
19
+ return Base64.toBytes(b64)
21
20
  },
22
21
 
23
22
  random(count = 32) {
24
- return this.string(Bytes.random(count))
23
+ return this.fromBytes(Bytes.random(count))
24
+ },
25
+
26
+ /** @deprecated renamed to "fromBytes" */
27
+ string(bytes: Uint8Array) {
28
+ return Base64url.fromBytes(bytes)
29
+ },
30
+
31
+ /** @deprecated renamed to "toBytes" */
32
+ bytes(string: string) {
33
+ return Base64url.toBytes(string)
25
34
  },
26
35
  }
27
36
 
@@ -0,0 +1,49 @@
1
+
2
+ import {Bytes} from "../bytes.js"
3
+ import {Bytename} from "./bytename.js"
4
+ import {Science, test, expect} from "@e280/science"
5
+
6
+ const deadbeef = new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF])
7
+
8
+ function good(bytes: Uint8Array) {
9
+ expect(Bytes.eq(bytes, deadbeef)).ok()
10
+ }
11
+
12
+ export default Science.suite({
13
+ "bytes->string->bytes": test(async() => {
14
+ const text = Bytename.fromBytes(deadbeef)
15
+ expect(text).is("ribmug.hilmun")
16
+ good(Bytename.toBytes(text))
17
+ }),
18
+
19
+ "zero bytes": test(async() => {
20
+ const text = Bytename.fromBytes(new Uint8Array([]))
21
+ expect(text).is("")
22
+ }),
23
+
24
+ "one byte": test(async() => {
25
+ const text = Bytename.fromBytes(new Uint8Array([0x00]))
26
+ expect(text).is("doz")
27
+ }),
28
+
29
+ "groupings": test(async() => {
30
+ const bytes = new Uint8Array([...deadbeef, ...deadbeef])
31
+ const text = Bytename.fromBytes(bytes, {
32
+ groupSize: 2,
33
+ groupSeparator: " ",
34
+ wordSeparator: ".",
35
+ })
36
+ expect(text).is("ribmug.hilmun ribmug.hilmun")
37
+ }),
38
+
39
+ "wordsep": Science.suite({
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
+ }),
48
+ })
49
+
@@ -1,19 +1,39 @@
1
1
 
2
+ import {Hex} from "../hex.js"
2
3
  import {Bytes} from "../bytes.js"
3
4
  import {prefixes} from "./utils/prefixes.js"
4
5
  import {suffixes} from "./utils/suffixes.js"
5
6
 
6
- const wordSeparator = "_"
7
- const groupSeparator = " "
8
- const defaultWordsPerGroup = 4
7
+ export type BytenameOptions = {
8
+ groupSize: number
9
+ wordSeparator: string
10
+ groupSeparator: string
11
+ }
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
+ */
19
+ export const Bytename = {
20
+ defaults: (<BytenameOptions>{
21
+ groupSize: 4,
22
+ wordSeparator: ".",
23
+ groupSeparator: " ",
24
+ }),
9
25
 
10
- export const Barname = {
11
- random(byteCount: number) {
12
- return this.string(Bytes.random(byteCount))
26
+ random(byteCount: number, options?: Partial<BytenameOptions>) {
27
+ const bytes = Bytes.random(byteCount)
28
+ return this.fromBytes(bytes, options)
13
29
  },
14
30
 
15
- string(bytes: Uint8Array, options?: {wordsPerGroup?: number}) {
16
- const groupSize = options?.wordsPerGroup ?? defaultWordsPerGroup
31
+ fromBytes(bytes: Uint8Array, options: Partial<BytenameOptions> = {}) {
32
+ const {
33
+ groupSize = Bytename.defaults.groupSize,
34
+ wordSeparator = Bytename.defaults.wordSeparator,
35
+ groupSeparator = Bytename.defaults.groupSeparator,
36
+ } = options
17
37
 
18
38
  const words: string[] = []
19
39
  let currentWord: string[] = []
@@ -37,8 +57,8 @@ export const Barname = {
37
57
  return grouped.join(groupSeparator)
38
58
  },
39
59
 
40
- bytes(string: string) {
41
- const letters = string
60
+ toBytes(bytename: string) {
61
+ const letters = bytename
42
62
  .toLowerCase()
43
63
  .replace(/[^a-z]/g, "") // strip everything except letters
44
64
 
@@ -58,5 +78,13 @@ export const Barname = {
58
78
  return number
59
79
  }))
60
80
  },
81
+
82
+ toHex(bytename: string) {
83
+ return Hex.fromBytes(Bytename.toBytes(bytename))
84
+ },
85
+
86
+ fromHex(hex: string, options?: Partial<BytenameOptions>) {
87
+ return Bytename.fromBytes(Hex.toBytes(hex), options)
88
+ },
61
89
  }
62
90
 
@@ -0,0 +1,4 @@
1
+
2
+ export * from "./bytename.js"
3
+ export * from "./thumbprint.js"
4
+
@@ -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,7 +1,17 @@
1
1
 
2
- export const Bytes = {
2
+ export const Bytes = Object.freeze({
3
+ eq(a: Uint8Array, b: Uint8Array) {
4
+ if (a.length !== b.length)
5
+ return false
6
+ for (let i = 0; i <= a.length; i++) {
7
+ if (a.at(i) !== b.at(i))
8
+ return false
9
+ }
10
+ return true
11
+ },
12
+
3
13
  random(count: number) {
4
14
  return crypto.getRandomValues(new Uint8Array(count))
5
15
  },
6
- }
16
+ })
7
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
- string(bytes: Uint8Array) {
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
- bytes(string: string) {
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.string(Bytes.random(byteCount))
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
- string(bytes: Uint8Array) {
2
+ export const Txt = Object.freeze({
3
+ fromBytes(bytes: Uint8Array) {
4
4
  return new TextDecoder().decode(bytes)
5
5
  },
6
6
 
7
- bytes(string: string) {
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
@@ -24,3 +24,4 @@ export function deadline<R>(milliseconds: number, message: string, fn: () => Pro
24
24
  .finally(() => clearTimeout(id))
25
25
  })
26
26
  }
27
+