@e280/stz 0.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.
Files changed (175) hide show
  1. package/license +23 -0
  2. package/package.json +40 -0
  3. package/readme.md +85 -0
  4. package/s/clone/clone.test.ts +124 -0
  5. package/s/clone/clone.ts +41 -0
  6. package/s/concurrent.ts +12 -0
  7. package/s/data/anka.ts +66 -0
  8. package/s/data/barname/badge.ts +78 -0
  9. package/s/data/barname/barname.ts +62 -0
  10. package/s/data/barname/demo.ts +49 -0
  11. package/s/data/barname/index.ts +4 -0
  12. package/s/data/barname/utils/prefixes.ts +20 -0
  13. package/s/data/barname/utils/suffixes.ts +20 -0
  14. package/s/data/base58.ts +65 -0
  15. package/s/data/base64.ts +22 -0
  16. package/s/data/base64url.ts +27 -0
  17. package/s/data/bytes.ts +7 -0
  18. package/s/data/hex.ts +35 -0
  19. package/s/data/nomen/demo.ts +8 -0
  20. package/s/data/nomen/grammar.ts +41 -0
  21. package/s/data/nomen/index.ts +5 -0
  22. package/s/data/nomen/nomen.ts +34 -0
  23. package/s/data/nomen/presets/falrysk.ts +112 -0
  24. package/s/data/nomen/types.ts +9 -0
  25. package/s/data/txt.ts +11 -0
  26. package/s/deadline.ts +24 -0
  27. package/s/debounce/debounce.test.ts +73 -0
  28. package/s/debounce/debounce.ts +54 -0
  29. package/s/debounce/types.ts +8 -0
  30. package/s/dedupe.ts +5 -0
  31. package/s/deep/deep.test.ts +8 -0
  32. package/s/deep/deep.ts +11 -0
  33. package/s/deep/parts/equal.test.ts +124 -0
  34. package/s/deep/parts/equal.ts +49 -0
  35. package/s/deep/parts/freeze.ts +34 -0
  36. package/s/defer-promise.ts +29 -0
  37. package/s/escape-regex.ts +5 -0
  38. package/s/hat.ts +17 -0
  39. package/s/index.ts +23 -0
  40. package/s/is.ts +27 -0
  41. package/s/map-g.ts +52 -0
  42. package/s/nap.ts +5 -0
  43. package/s/ob.ts +66 -0
  44. package/s/pipe.ts +24 -0
  45. package/s/pubsub.ts +68 -0
  46. package/s/trash.ts +30 -0
  47. package/x/clone/clone.d.ts +1 -0
  48. package/x/clone/clone.js +26 -0
  49. package/x/clone/clone.js.map +1 -0
  50. package/x/clone/clone.test.d.ts +1 -0
  51. package/x/clone/clone.test.js +125 -0
  52. package/x/clone/clone.test.js.map +1 -0
  53. package/x/concurrent.d.ts +1 -0
  54. package/x/concurrent.js +7 -0
  55. package/x/concurrent.js.map +1 -0
  56. package/x/data/anka.d.ts +5 -0
  57. package/x/data/anka.js +53 -0
  58. package/x/data/anka.js.map +1 -0
  59. package/x/data/barname/badge.d.ts +25 -0
  60. package/x/data/barname/badge.js +64 -0
  61. package/x/data/barname/badge.js.map +1 -0
  62. package/x/data/barname/barname.d.ts +7 -0
  63. package/x/data/barname/barname.js +49 -0
  64. package/x/data/barname/barname.js.map +1 -0
  65. package/x/data/barname/demo.d.ts +1 -0
  66. package/x/data/barname/demo.js +45 -0
  67. package/x/data/barname/demo.js.map +1 -0
  68. package/x/data/barname/index.d.ts +2 -0
  69. package/x/data/barname/index.js +3 -0
  70. package/x/data/barname/index.js.map +1 -0
  71. package/x/data/barname/utils/prefixes.d.ts +1 -0
  72. package/x/data/barname/utils/prefixes.js +19 -0
  73. package/x/data/barname/utils/prefixes.js.map +1 -0
  74. package/x/data/barname/utils/suffixes.d.ts +1 -0
  75. package/x/data/barname/utils/suffixes.js +19 -0
  76. package/x/data/barname/utils/suffixes.js.map +1 -0
  77. package/x/data/base58.d.ts +5 -0
  78. package/x/data/base58.js +58 -0
  79. package/x/data/base58.js.map +1 -0
  80. package/x/data/base64.d.ts +5 -0
  81. package/x/data/base64.js +17 -0
  82. package/x/data/base64.js.map +1 -0
  83. package/x/data/base64url.d.ts +5 -0
  84. package/x/data/base64url.js +22 -0
  85. package/x/data/base64url.js.map +1 -0
  86. package/x/data/bytes.d.ts +3 -0
  87. package/x/data/bytes.js +6 -0
  88. package/x/data/bytes.js.map +1 -0
  89. package/x/data/hex.d.ts +6 -0
  90. package/x/data/hex.js +30 -0
  91. package/x/data/hex.js.map +1 -0
  92. package/x/data/nomen/demo.d.ts +1 -0
  93. package/x/data/nomen/demo.js +5 -0
  94. package/x/data/nomen/demo.js.map +1 -0
  95. package/x/data/nomen/grammar.d.ts +11 -0
  96. package/x/data/nomen/grammar.js +32 -0
  97. package/x/data/nomen/grammar.js.map +1 -0
  98. package/x/data/nomen/index.d.ts +3 -0
  99. package/x/data/nomen/index.js +4 -0
  100. package/x/data/nomen/index.js.map +1 -0
  101. package/x/data/nomen/nomen.d.ts +11 -0
  102. package/x/data/nomen/nomen.js +29 -0
  103. package/x/data/nomen/nomen.js.map +1 -0
  104. package/x/data/nomen/presets/falrysk.d.ts +5 -0
  105. package/x/data/nomen/presets/falrysk.js +99 -0
  106. package/x/data/nomen/presets/falrysk.js.map +1 -0
  107. package/x/data/nomen/types.d.ts +6 -0
  108. package/x/data/nomen/types.js +2 -0
  109. package/x/data/nomen/types.js.map +1 -0
  110. package/x/data/txt.d.ts +4 -0
  111. package/x/data/txt.js +9 -0
  112. package/x/data/txt.js.map +1 -0
  113. package/x/deadline.d.ts +6 -0
  114. package/x/deadline.js +17 -0
  115. package/x/deadline.js.map +1 -0
  116. package/x/debounce/debounce.d.ts +2 -0
  117. package/x/debounce/debounce.js +37 -0
  118. package/x/debounce/debounce.js.map +1 -0
  119. package/x/debounce/debounce.test.d.ts +1 -0
  120. package/x/debounce/debounce.test.js +75 -0
  121. package/x/debounce/debounce.test.js.map +1 -0
  122. package/x/debounce/types.d.ts +2 -0
  123. package/x/debounce/types.js +2 -0
  124. package/x/debounce/types.js.map +1 -0
  125. package/x/dedupe.d.ts +1 -0
  126. package/x/dedupe.js +4 -0
  127. package/x/dedupe.js.map +1 -0
  128. package/x/deep/deep.d.ts +7 -0
  129. package/x/deep/deep.js +9 -0
  130. package/x/deep/deep.js.map +1 -0
  131. package/x/deep/deep.test.d.ts +1 -0
  132. package/x/deep/deep.test.js +10 -0
  133. package/x/deep/deep.test.js.map +1 -0
  134. package/x/deep/parts/equal.d.ts +1 -0
  135. package/x/deep/parts/equal.js +39 -0
  136. package/x/deep/parts/equal.js.map +1 -0
  137. package/x/deep/parts/equal.test.d.ts +1 -0
  138. package/x/deep/parts/equal.test.js +126 -0
  139. package/x/deep/parts/equal.test.js.map +1 -0
  140. package/x/deep/parts/freeze.d.ts +1 -0
  141. package/x/deep/parts/freeze.js +24 -0
  142. package/x/deep/parts/freeze.js.map +1 -0
  143. package/x/defer-promise.d.ts +10 -0
  144. package/x/defer-promise.js +15 -0
  145. package/x/defer-promise.js.map +1 -0
  146. package/x/escape-regex.d.ts +1 -0
  147. package/x/escape-regex.js +4 -0
  148. package/x/escape-regex.js.map +1 -0
  149. package/x/hat.d.ts +6 -0
  150. package/x/hat.js +16 -0
  151. package/x/hat.js.map +1 -0
  152. package/x/index.d.ts +20 -0
  153. package/x/index.js +21 -0
  154. package/x/index.js.map +1 -0
  155. package/x/is.d.ts +10 -0
  156. package/x/is.js +11 -0
  157. package/x/is.js.map +1 -0
  158. package/x/map-g.d.ts +17 -0
  159. package/x/map-g.js +40 -0
  160. package/x/map-g.js.map +1 -0
  161. package/x/nap.d.ts +1 -0
  162. package/x/nap.js +2 -0
  163. package/x/nap.js.map +1 -0
  164. package/x/ob.d.ts +14 -0
  165. package/x/ob.js +21 -0
  166. package/x/ob.js.map +1 -0
  167. package/x/pipe.d.ts +8 -0
  168. package/x/pipe.js +16 -0
  169. package/x/pipe.js.map +1 -0
  170. package/x/pubsub.d.ts +18 -0
  171. package/x/pubsub.js +40 -0
  172. package/x/pubsub.js.map +1 -0
  173. package/x/trash.d.ts +14 -0
  174. package/x/trash.js +25 -0
  175. package/x/trash.js.map +1 -0
package/license ADDED
@@ -0,0 +1,23 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2025 Chase Moskal
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@e280/stz",
3
+ "version": "0.0.0-0",
4
+ "description": "everyday ts fns for everything",
5
+ "license": "MIT",
6
+ "author": "Chase Moskal <chasemoskal@gmail.com>",
7
+ "type": "module",
8
+ "main": "x/index.js",
9
+ "files": [
10
+ "x",
11
+ "s"
12
+ ],
13
+ "scripts": {
14
+ "build": "run-s _clean _links _tsc",
15
+ "_clean": "rm -rf x && mkdir x",
16
+ "_links": "ln -s \"$(realpath node_modules)\" x/node_modules",
17
+ "_tsc": "tsc",
18
+ "start": "exit 0",
19
+ "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +",
20
+ "test": "exit 0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.14.1",
24
+ "npm-run-all": "^4.1.5",
25
+ "typescript": "^5.8.3"
26
+ },
27
+ "keywords": [
28
+ "standard",
29
+ "std",
30
+ "steeze"
31
+ ],
32
+ "homepage": "https://github.com/e280/stz#readme",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/e280/stz.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/e280/stz/issues"
39
+ }
40
+ }
package/readme.md ADDED
@@ -0,0 +1,85 @@
1
+
2
+ # `@e280/stz`
3
+ standard library of environment-agnostic typescript functions we use basically everywhere
4
+
5
+ <br/>
6
+
7
+ ## the tools
8
+ stz has many more tools than documented below, see their [sourcecode here in s/](./s/)
9
+
10
+ ### MapG — an extension of js Map with handy methods
11
+ - `map.require`
12
+ ```ts
13
+ import {MapG} from "@e280/stz"
14
+
15
+ const map = new MapG<number, string>([
16
+ [1, "hello"],
17
+ [2, "world"],
18
+ ])
19
+
20
+ // throws error if the value is undefined
21
+ const value = map.require(1)
22
+ ```
23
+ - `map.guarantee`
24
+ ```ts
25
+ // if the value is undefined, the new value "rofl" is set and returned
26
+ const value = map.guarantee(3, () => "rofl")
27
+ ```
28
+
29
+ ### pub and sub — minimal ergonomic event system
30
+ - make a publisher fn
31
+ ```ts
32
+ import {pub} from "@e280/stz"
33
+
34
+ // create a pub fn
35
+ const sendMessage = pub<[string]>()
36
+
37
+ // subscribe to it
38
+ sendMessage.sub(m => console.log(m))
39
+
40
+ // publish to it
41
+ sendMessage("hello")
42
+ ```
43
+ - make a subscriber fn *(see how it's just the reverse of pub?)*
44
+ ```ts
45
+ import {sub} from "@e280/stz"
46
+
47
+ // create a sub fn
48
+ const onMessage = sub<[string]>()
49
+
50
+ // subscribe to it
51
+ onMessage(m => console.log(m))
52
+
53
+ // publish to it
54
+ onMessage.pub("hello")
55
+ ```
56
+ - the pub and sub are the same, but have differing invoke signatures
57
+ - i seem to use `sub` more often
58
+ - both have some extra functionality
59
+ ```ts
60
+ // pub fns return a promise, to wait for all async subscribers
61
+ await sendMessage("hello")
62
+ await onMessage.pub("hello")
63
+
64
+ // sub fns return an unsub fn
65
+ const unsub1 = onMessage(m => console.log(m))
66
+ unsub1() // unsubscribe that listener
67
+
68
+ const unsub2 = sendMessage.sub(m => console.log(m))
69
+ unsub2() // unsubscribe that listener
70
+
71
+ // you can clear all subscribers from a pub or a sub
72
+ sendMessage.clear()
73
+ onMessage.clear()
74
+
75
+ // you can subscribe to only one next call
76
+ onMessage.once(m => console.log(m))
77
+ sendMessage.once(m => console.log(m))
78
+ ```
79
+
80
+ <br/>
81
+
82
+ ## 💖 made with open source love
83
+ reward us with github stars
84
+ build with us at https://e280.org/ but only if you're cool
85
+
@@ -0,0 +1,124 @@
1
+ //
2
+ // import {Suite, expect} from "cynic"
3
+ // import {clone} from "./clone.js"
4
+ //
5
+ // function checkMapOrSetEquality(original: any, cloned: any) {
6
+ // const originalEntries = JSON.stringify(Array.from(original))
7
+ // const clonedEntries = JSON.stringify(Array.from(cloned))
8
+ // expect(clonedEntries).equals(originalEntries)
9
+ // expect(cloned).not.equals(original) // check for deep cloning
10
+ // }
11
+ //
12
+ // export default <Suite>{
13
+ //
14
+ // async "clone primitive types"() {
15
+ // expect(clone(123)).equals(123)
16
+ // expect(clone("abc")).equals("abc")
17
+ // expect(clone(true)).equals(true)
18
+ // },
19
+ //
20
+ // async "clone arrays"() {
21
+ // const array = [1, 2, 3]
22
+ // const clonedArray = clone(array)
23
+ // expect(JSON.stringify(clonedArray)).equals(JSON.stringify(array))
24
+ // expect(clonedArray).not.equals(array) // check for deep cloning
25
+ // },
26
+ //
27
+ // async "clone plain objects"() {
28
+ // const object = {foo: "bar", baz: 123}
29
+ // const clonedObject = clone(object)
30
+ // expect(JSON.stringify(clonedObject)).equals(JSON.stringify(object))
31
+ // expect(clonedObject).not.equals(object) // check for deep cloning
32
+ // },
33
+ //
34
+ // async "clone Map"() {
35
+ // const map = new Map()
36
+ // map.set("key", "value")
37
+ // const clonedMap = clone(map)
38
+ // expect(map.get("key")).equals("value")
39
+ // expect(clonedMap).not.equals(map)
40
+ // },
41
+ //
42
+ // async "clone Set"() {
43
+ // const set = new Set()
44
+ // set.add("value")
45
+ // const clonedSet = clone(set)
46
+ // expect(clonedSet.has("value")).ok()
47
+ // expect(clonedSet).not.equals(set)
48
+ // },
49
+ //
50
+ // async "clone Date"() {
51
+ // const date = new Date()
52
+ // const clonedDate = clone(date)
53
+ // expect(clonedDate.getTime()).equals(date.getTime())
54
+ // expect(clonedDate).not.equals(date) // check for deep cloning
55
+ // },
56
+ //
57
+ // async "clone objects with circular reference"() {
58
+ // const object: any = {foo: "bar"}
59
+ // object.self = object // create circular reference
60
+ // let error: any
61
+ // try {
62
+ // clone(object)
63
+ // } catch (e) {
64
+ // error = e
65
+ // }
66
+ // expect(error).not.equals(undefined)
67
+ // expect(error instanceof Error).ok()
68
+ // },
69
+ //
70
+ // async "clone nested objects"() {
71
+ // const object = {foo: "bar", inner: {baz: 123, deeper: {qux: true}}}
72
+ // const clonedObject = clone(object)
73
+ // expect(JSON.stringify(clonedObject)).equals(JSON.stringify(object))
74
+ // expect(clonedObject).not.equals(object) // check for deep cloning
75
+ // expect(clonedObject.inner).not.equals(object.inner) // check for deep cloning
76
+ // expect(clonedObject.inner.deeper).not.equals(object.inner.deeper) // check for deep cloning
77
+ // },
78
+ //
79
+ // async "clone array of objects"() {
80
+ // const array = [{foo: "bar"}, {baz: 123}, {qux: true}]
81
+ // const clonedArray = clone(array)
82
+ // expect(JSON.stringify(clonedArray)).equals(JSON.stringify(array))
83
+ // expect(clonedArray).not.equals(array) // check for deep cloning
84
+ // },
85
+ //
86
+ // async "clone object with array"() {
87
+ // const object = {foo: "bar", array: [1, 2, 3]}
88
+ // const clonedObject = clone(object)
89
+ // expect(JSON.stringify(clonedObject)).equals(JSON.stringify(object))
90
+ // expect(clonedObject).not.equals(object) // check for deep cloning
91
+ // },
92
+ //
93
+ // async "clone object with Map"() {
94
+ // const map = new Map()
95
+ // map.set("key", "value")
96
+ // const object = {foo: "bar", map}
97
+ // const clonedObject = clone(object)
98
+ // expect(JSON.stringify(clonedObject.foo)).equals(JSON.stringify(object.foo))
99
+ // checkMapOrSetEquality(object.map, clonedObject.map)
100
+ // },
101
+ //
102
+ // async "clone object with duplicate sibling references"() {
103
+ // const dupeObject = { id: 1, name: "H4CK3RM4N" }
104
+ // const data = { obj1: dupeObject, obj2: dupeObject }
105
+ //
106
+ // let didThrow = false
107
+ // let clonedData: typeof data = {} as typeof data
108
+ //
109
+ // try {
110
+ // clonedData = clone(data)
111
+ // }
112
+ // catch (error) {
113
+ // didThrow = true
114
+ // }
115
+ //
116
+ // expect(didThrow).not.equals(true)
117
+ // expect(clonedData.obj1).not.equals(clonedData.obj2)
118
+ // expect(clonedData.obj1.id).equals(1)
119
+ // expect(clonedData.obj1.name).equals("H4CK3RM4N")
120
+ // expect(clonedData.obj2.id).equals(1)
121
+ // expect(clonedData.obj2.name).equals("H4CK3RM4N")
122
+ // },
123
+ // }
124
+
@@ -0,0 +1,41 @@
1
+
2
+ export function clone<T>(data: T, refs = new Set<any>()): T {
3
+
4
+ if (refs.has(data))
5
+ throw new Error("cannot clone circular reference")
6
+
7
+ let copy = undefined as T
8
+
9
+ if (typeof data === "function" || (data !== null && typeof data === "object")) {
10
+ refs.add(data)
11
+
12
+ if (Array.isArray(data))
13
+ copy = data.map(d => clone(d, new Set(refs))) as T
14
+
15
+ else if (data.constructor === Object)
16
+ copy = Object.fromEntries(
17
+ Object.entries(data)
18
+ .map(([key, d]) => [key, clone(d, new Set(refs))])
19
+ ) as T
20
+
21
+ else if (data instanceof Map)
22
+ copy = new Map(Array.from(data, ([key, val]) => [key, clone(val, new Set(refs))])) as T
23
+
24
+ else if (data instanceof Set)
25
+ copy = new Set(Array.from(data, val => clone(val, new Set(refs)))) as T
26
+
27
+ else if (data instanceof Date)
28
+ copy = new Date(data.getTime()) as T
29
+
30
+ else
31
+ copy = data
32
+
33
+ refs.delete(data) // Remove current data from refs once we're done with this branch of the object tree
34
+ }
35
+
36
+ else
37
+ copy = data
38
+
39
+ return copy
40
+ }
41
+
@@ -0,0 +1,12 @@
1
+
2
+ export async function concurrent<O extends Record<string, Promise<any>>>(
3
+ ob: O,
4
+ ) {
5
+ const entries = Object.entries(ob)
6
+ const promises = entries.map(([,promise]) => promise)
7
+ const values = await Promise.all(promises)
8
+ return (
9
+ Object.fromEntries(entries.map(([key], index) => [key, values[index]]))
10
+ ) as {[K in keyof O]: Awaited<O[K]>}
11
+ }
12
+
package/s/data/anka.ts ADDED
@@ -0,0 +1,66 @@
1
+
2
+ /*
3
+
4
+ "anka" is an alternative to base64 that i totally made up.
5
+ - it looks cool because it features characters from the indian subcontinent.
6
+ - each byte is represented by one character, which makes a 256 bit id incomparably short at only 32 characters.
7
+ - however, it's probably a bad idea, for these negative reasons:
8
+ 1. many of these characters are actually 2-or-3-byte sequences (so even though anka is visually more compact than base64 to the user, it's actually more data in terms of bytes).
9
+ 2. it's horrible for url-encoding.
10
+ 3. it's horrible for trying to manually copy by typing.
11
+
12
+ 256-bit anka ids look like this:
13
+
14
+ ஒഐওଆઊসਅവஎଉखਬശயഘसചവਭଏரঊझഠடଓઐഴঠണവઞ
15
+ ठକநइஎਦਏஒഊটਹஜഴयભকயঙਪഉजकঝഐवઠসഏਓਡਟऋ
16
+ થঞठખஉધഫரਜऐફരਪਙਗഭਣഎफઇমએরଔડਉதਤஅदઘച
17
+
18
+ */
19
+
20
+ import {Bytes} from "./bytes.js"
21
+
22
+ const characters = [...new Set([
23
+ // devanagari
24
+ ..."अआइईउऊऋएऐओऔकखगघङचछजझञटठडढणतथदधनपफबभमयरलवशषसह",
25
+
26
+ // tamil
27
+ ..."அஆஇஈஉஊஎஏஐஒஓகஙசஜஞடணதநபமயரலவழளறன",
28
+
29
+ // bengali
30
+ ..."অআইঈউঊএঐওকখগঘঙচছজঝঞটঠডঢণতথদধনপফবভমযরলশষসহ",
31
+
32
+ // gurmukhi
33
+ ..."ਅਆਇਈਉਊਏਐਓਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨਪਫਬਭਮਯਰਲਵਸਹ",
34
+
35
+ // malayalam
36
+ ..."അആഇഈഉഊഎഏഐഒഓകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനപഫബഭമയരലവശസഹളഴറ",
37
+
38
+ // gujarati
39
+ ..."અઆઇઈઉઊએઐઓકખગઘઙચછજઝઞટઠડઢણતથદધનપફબભમયરલવશષસહ",
40
+
41
+ // odia
42
+ ..."ଅଆଇଈଉଊଏଐଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନପଫବଭମଯରଲଶଷସହ",
43
+ ])]
44
+
45
+ export const Anka = Object.freeze({
46
+
47
+ string(bytes: Uint8Array) {
48
+ return [...bytes]
49
+ .map(int => characters.at(int))
50
+ .join("")
51
+ },
52
+
53
+ bytes(code: string) {
54
+ return new Uint8Array([...code].map(char => {
55
+ const index = characters.indexOf(char)
56
+ if (index === -1)
57
+ throw new Error(`invalid anka character "${char}"`)
58
+ return index
59
+ }))
60
+ },
61
+
62
+ random(count = 32) {
63
+ return this.string(Bytes.random(count))
64
+ },
65
+ })
66
+
@@ -0,0 +1,78 @@
1
+
2
+ import {Hex} from "../hex.js"
3
+ import {Base58} from "../base58.js"
4
+ import {Barname} from "./barname.js"
5
+
6
+ /**
7
+ * Badge is a human-friendly presentation format for arbitrary binary data.
8
+ * - looks like "nomluc_rigpem.tg2bjNkjMh1H6M2b2EhD5V4x6XAqx9wyWddsBt"
9
+ * - the first bytes are shown in barname format
10
+ * - the rest of the data is in base58
11
+ * - designed to be a nice way to present 256-bit passport thumbprints
12
+ * - can be used for data of different lengths
13
+ * - preview size can be customized as 'leadCount', which defaults to 4
14
+ */
15
+ export class Badge {
16
+ static readonly separator = "."
17
+ static readonly defaultLeadCount = 4
18
+
19
+ readonly hex: string
20
+ readonly string: string
21
+ readonly preview: string
22
+
23
+ constructor(
24
+ public readonly bytes: Uint8Array,
25
+ public readonly leadCount = Badge.defaultLeadCount,
26
+ ) {
27
+
28
+ if (leadCount < 1)
29
+ throw new Error(`badge leadCount must be greater than 0 (was ${leadCount})`)
30
+
31
+ this.hex = Hex.string(this.bytes)
32
+ this.preview = Barname.string(this.bytes.slice(0, leadCount))
33
+
34
+ this.string = (bytes.length > leadCount)
35
+ ? `${this.preview}.${Base58.string(this.bytes.slice(leadCount))}`
36
+ : this.preview
37
+ }
38
+
39
+ static parse(badge: string) {
40
+ const [barname, b58] = badge.split(Badge.separator)
41
+
42
+ // badge has a base58 component
43
+ if (b58) {
44
+ const appetizer = Barname.bytes(barname)
45
+ const entree = Base58.bytes(b58)
46
+ const bytes = new Uint8Array([...appetizer, ...entree])
47
+ return new this(bytes, appetizer.length)
48
+ }
49
+
50
+ // badge is just a barname (no base58 part)
51
+ else {
52
+ const bytes = Barname.bytes(barname)
53
+ return new this(bytes, bytes.length)
54
+ }
55
+ }
56
+
57
+ static fromHex(hex: string, leadCount = this.defaultLeadCount) {
58
+ const bytes = Hex.bytes(hex)
59
+ return new this(bytes, leadCount)
60
+ }
61
+
62
+ static string(bytes: Uint8Array, leadCount = this.defaultLeadCount) {
63
+ return new this(bytes, leadCount).string
64
+ }
65
+
66
+ static bytes(badge: string) {
67
+ return this.parse(badge)
68
+ }
69
+
70
+ static hex(badge: string) {
71
+ return this.parse(badge).hex
72
+ }
73
+
74
+ toString() {
75
+ return this.string
76
+ }
77
+ }
78
+
@@ -0,0 +1,62 @@
1
+
2
+ import {Bytes} from "../bytes.js"
3
+ import {prefixes} from "./utils/prefixes.js"
4
+ import {suffixes} from "./utils/suffixes.js"
5
+
6
+ const wordSeparator = "_"
7
+ const groupSeparator = " "
8
+ const defaultWordsPerGroup = 4
9
+
10
+ export const Barname = {
11
+ random(byteCount: number) {
12
+ return this.string(Bytes.random(byteCount))
13
+ },
14
+
15
+ string(bytes: Uint8Array, options?: {wordsPerGroup?: number}) {
16
+ const groupSize = options?.wordsPerGroup ?? defaultWordsPerGroup
17
+
18
+ const words: string[] = []
19
+ let currentWord: string[] = []
20
+
21
+ bytes.forEach((byte, index) => {
22
+ const source = ((index % 2) === 0) ? prefixes : suffixes
23
+ currentWord.push(source[byte]!)
24
+ if (currentWord.length === 2) {
25
+ words.push(currentWord.join(""))
26
+ currentWord = []
27
+ }
28
+ })
29
+
30
+ if (currentWord.length)
31
+ words.push(currentWord.join(""))
32
+
33
+ const grouped = []
34
+ for (let i = 0; i < words.length; i += groupSize)
35
+ grouped.push(words.slice(i, i + groupSize).join(wordSeparator))
36
+
37
+ return grouped.join(groupSeparator)
38
+ },
39
+
40
+ bytes(string: string) {
41
+ const letters = string
42
+ .toLowerCase()
43
+ .replace(/[^a-z]/g, "") // strip everything except letters
44
+
45
+ const count = letters.length / 3
46
+ if ((count % 1) !== 0)
47
+ throw new Error(`invalid triplet count, ${letters.length} does not divide into triplets`)
48
+
49
+ const triplets: string[] = []
50
+ for (let i = 0; i < letters.length; i += 3)
51
+ triplets.push(letters.slice(i, i + 3))
52
+
53
+ return new Uint8Array(triplets.map((triplet, index) => {
54
+ const source = ((index % 2) === 0) ? prefixes : suffixes
55
+ const number = source.findIndex(t => t === triplet)
56
+ if (number === -1)
57
+ throw new Error(`unknown triplet ${triplet}`)
58
+ return number
59
+ }))
60
+ },
61
+ }
62
+
@@ -0,0 +1,49 @@
1
+
2
+ import {Hex} from "../hex.js"
3
+ import {Badge} from "./badge.js"
4
+ import {Bytes} from "../bytes.js"
5
+ import {Barname} from "./barname.js"
6
+
7
+ for (const _ of Array(2)) {
8
+ const string = Barname.random(34)
9
+ const bytes = Barname.bytes(string)
10
+ const string2 = Barname.string(bytes)
11
+ console.log(string.replaceAll(" ", "\n"))
12
+ if (string !== string2)
13
+ throw new Error("barname fails equality check")
14
+ console.log("")
15
+ }
16
+
17
+ for (const _ of Array(10)) {
18
+ const bytes = Bytes.random(32)
19
+ const badge = new Badge(bytes)
20
+ const bytes2 = badge.bytes
21
+ console.log(badge.string)
22
+ if (Hex.string(bytes) !== Hex.string(bytes2))
23
+ throw new Error("badge fails equality check")
24
+ }
25
+
26
+ {
27
+ console.log("")
28
+ const alpha = `
29
+ dacsyd_labdun_fipsub_bonmel
30
+ fitlys_rovbud_sipnel_sonryx
31
+ sitrem_lasdyr_dovpun_nibwep
32
+ tippet_mapnub_ticnet_famset
33
+ wanteg
34
+ `
35
+ const bravo = `
36
+ dacsyd-labdun-fipsub-bonmel;
37
+ [fitlys rovbud sipnel sonryx]
38
+ sitrem/lasdyr/dovpun/nibwep
39
+ tippet+mapnub+ticnet+famset
40
+ wanteg++
41
+ `
42
+ const alphaHex = Hex.string(Barname.bytes(alpha))
43
+ const bravoHex = Hex.string(Barname.bytes(bravo))
44
+ if (alphaHex.length === 68 && alphaHex === bravoHex)
45
+ console.log("*barname parsing appears resilient to whitespace and garbage*")
46
+ else
47
+ throw new Error("barname resiliency failure")
48
+ }
49
+
@@ -0,0 +1,4 @@
1
+
2
+ export * from "./barname.js"
3
+ export * from "./badge.js"
4
+
@@ -0,0 +1,20 @@
1
+
2
+ export const prefixes = [
3
+ "doz","mar","bin","wan","sam","lit","sig","hid","fid","lis","sog","dir","wac","sab","wis","sib",
4
+ "rig","sol","dop","mod","fog","lid","hop","dar","dor","lor","hod","fol","rin","tog","sil","mir",
5
+ "hol","pas","lac","rov","liv","dal","sat","lib","tab","han","tic","pid","tor","bol","fos","dot",
6
+ "los","dil","for","pil","ram","tir","win","tad","bic","dif","roc","wid","bis","das","mid","lop",
7
+ "ril","nar","dap","mol","san","loc","nov","sit","nid","tip","sic","rop","wit","nat","pan","min",
8
+ "rit","pod","mot","tam","tol","sav","pos","nap","nop","som","fin","fon","ban","mor","wor","sip",
9
+ "ron","nor","bot","wic","soc","wat","dol","mag","pic","dav","bid","bal","tim","tas","mal","lig",
10
+ "siv","tag","pad","sal","div","dac","tan","sid","fab","tar","mon","ran","nis","wol","mis","pal",
11
+ "las","dis","map","rab","tob","rol","lat","lon","nod","nav","fig","nom","nib","pag","sop","ral",
12
+ "bil","had","doc","rid","moc","pac","rav","rip","fal","tod","til","tin","hap","mic","fan","pat",
13
+ "tac","lab","mog","sim","son","pin","lom","ric","tap","fir","has","bos","bat","poc","hac","tid",
14
+ "hav","sap","lin","dib","hos","dab","bit","bar","rac","par","lod","dos","bor","toc","hil","mac",
15
+ "tom","dig","fil","fas","mit","hob","har","mig","hin","rad","mas","hal","rag","lag","fad","top",
16
+ "mop","hab","nil","nos","mil","fop","fam","dat","nol","din","hat","nac","ris","fot","rib","hoc",
17
+ "nim","lar","fit","wal","rap","sar","nal","mos","lan","don","dan","lad","dov","riv","bac","pol",
18
+ "lap","tal","pit","nam","bon","ros","ton","fod","pon","sov","noc","sor","lav","mat","mip","fip",
19
+ ]
20
+
@@ -0,0 +1,20 @@
1
+
2
+ export const suffixes = [
3
+ "zod","nec","bud","wes","sev","per","sut","let","ful","pen","syt","dur","wep","ser","wyl","sun",
4
+ "ryp","syx","dyr","nup","heb","peg","lup","dep","dys","put","lug","hec","ryt","tyv","syd","nex",
5
+ "lun","mep","lut","sep","pes","del","sul","ped","tem","led","tul","met","wen","byn","hex","feb",
6
+ "pyl","dul","het","mev","rut","tyl","wyd","tep","bes","dex","sef","wyc","bur","der","nep","pur",
7
+ "rys","reb","den","nut","sub","pet","rul","syn","reg","tyd","sup","sem","wyn","rec","meg","net",
8
+ "sec","mul","nym","tev","web","sum","mut","nyx","rex","teb","fus","hep","ben","mus","wyx","sym",
9
+ "sel","ruc","dec","wex","syr","wet","dyl","myn","mes","det","bet","bel","tux","tug","myr","pel",
10
+ "syp","ter","meb","set","dut","deg","tex","sur","fel","tud","nux","rux","ren","wyt","nub","med",
11
+ "lyt","dus","neb","rum","tyn","seg","lyx","pun","res","red","fun","rev","ref","mec","ted","rus",
12
+ "bex","leb","dux","ryn","num","pyx","ryg","ryx","fep","tyr","tus","tyc","leg","nem","fer","mer",
13
+ "ten","lus","nus","syl","tec","mex","pub","rym","tuc","fyl","lep","deb","ber","mug","hut","tun",
14
+ "byl","sud","pem","dev","lur","def","bus","bep","run","mel","pex","dyt","byt","typ","lev","myl",
15
+ "wed","duc","fur","fex","nul","luc","len","ner","lex","rup","ned","lec","ryd","lyd","fen","wel",
16
+ "nyd","hus","rel","rud","nes","hes","fet","des","ret","dun","ler","nyr","seb","hul","ryl","lud",
17
+ "rem","lys","fyn","wer","ryc","sug","nys","nyl","lyn","dyn","dem","lux","fed","sed","bec","mun",
18
+ "lyr","tes","mud","nyt","byr","sen","weg","fyr","mur","tel","rep","teg","pec","nel","nev","fes",
19
+ ]
20
+