@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.
Files changed (148) hide show
  1. package/README.md +275 -24
  2. package/package.json +13 -9
  3. package/s/coalesce.ts +1 -0
  4. package/s/constructor.ts +15 -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 +12 -12
  10. package/s/data/bytename/bytename.ts +13 -6
  11. package/s/data/bytename/index.ts +1 -1
  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 +2 -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 +5 -6
  19. package/s/{defer-promise.ts → defer.ts} +2 -2
  20. package/s/drill.ts +2 -4
  21. package/s/ev.ts +20 -0
  22. package/s/id-counter.ts +9 -0
  23. package/s/index.ts +12 -1
  24. package/s/is.ts +10 -4
  25. package/s/map-g.ts +9 -10
  26. package/s/nap.ts +1 -0
  27. package/s/once.ts +14 -0
  28. package/s/pipe.ts +7 -8
  29. package/s/pubsub.ts +24 -6
  30. package/s/queue/queue.test.ts +67 -0
  31. package/s/queue/queue.ts +21 -0
  32. package/s/repeat.ts +25 -0
  33. package/s/scope.ts +108 -0
  34. package/s/templating.ts +22 -0
  35. package/s/tests.test.ts +14 -10
  36. package/s/time.ts +22 -0
  37. package/x/coalesce.d.ts +1 -0
  38. package/x/coalesce.js +1 -0
  39. package/x/coalesce.js.map +1 -1
  40. package/x/constructor.d.ts +3 -0
  41. package/x/constructor.js +4 -0
  42. package/x/constructor.js.map +1 -0
  43. package/x/data/base-x.d.ts +47 -0
  44. package/x/data/base-x.js +147 -0
  45. package/x/data/base-x.js.map +1 -0
  46. package/x/data/base58.d.ts +5 -1
  47. package/x/data/base58.js +13 -5
  48. package/x/data/base58.js.map +1 -1
  49. package/x/data/base64.d.ts +7 -3
  50. package/x/data/base64.js +13 -5
  51. package/x/data/base64.js.map +1 -1
  52. package/x/data/base64url.d.ts +5 -1
  53. package/x/data/base64url.js +13 -5
  54. package/x/data/base64url.js.map +1 -1
  55. package/x/data/bytename/bytename.d.ts +9 -3
  56. package/x/data/bytename/bytename.js +13 -6
  57. package/x/data/bytename/bytename.js.map +1 -1
  58. package/x/data/bytename/bytename.test.js +12 -12
  59. package/x/data/bytename/bytename.test.js.map +1 -1
  60. package/x/data/bytename/index.d.ts +1 -1
  61. package/x/data/bytename/index.js +1 -1
  62. package/x/data/bytename/index.js.map +1 -1
  63. package/x/data/bytename/thumbprint.d.ts +36 -0
  64. package/x/data/bytename/thumbprint.js +79 -0
  65. package/x/data/bytename/thumbprint.js.map +1 -0
  66. package/x/data/bytename/{badge.test.d.ts → thumbprint.test.d.ts} +2 -2
  67. package/x/data/bytename/thumbprint.test.js +45 -0
  68. package/x/data/bytename/thumbprint.test.js.map +1 -0
  69. package/x/data/bytes.d.ts +2 -2
  70. package/x/data/bytes.js +2 -2
  71. package/x/data/bytes.js.map +1 -1
  72. package/x/data/data.test.d.ts +69 -0
  73. package/x/data/data.test.js +77 -0
  74. package/x/data/data.test.js.map +1 -0
  75. package/x/data/hex.d.ts +8 -4
  76. package/x/data/hex.js +13 -5
  77. package/x/data/hex.js.map +1 -1
  78. package/x/data/txt.d.ts +6 -2
  79. package/x/data/txt.js +12 -4
  80. package/x/data/txt.js.map +1 -1
  81. package/x/deadline.d.ts +2 -2
  82. package/x/deadline.js +4 -4
  83. package/x/deadline.js.map +1 -1
  84. package/x/{defer-promise.d.ts → defer.d.ts} +2 -2
  85. package/x/{defer-promise.js → defer.js} +2 -2
  86. package/x/defer.js.map +1 -0
  87. package/x/drill.d.ts +1 -3
  88. package/x/drill.js +2 -4
  89. package/x/drill.js.map +1 -1
  90. package/x/ev.d.ts +6 -0
  91. package/x/ev.js +10 -0
  92. package/x/ev.js.map +1 -0
  93. package/x/id-counter.d.ts +5 -0
  94. package/x/id-counter.js +10 -0
  95. package/x/id-counter.js.map +1 -0
  96. package/x/index.d.ts +11 -1
  97. package/x/index.js +11 -1
  98. package/x/index.js.map +1 -1
  99. package/x/is.d.ts +6 -4
  100. package/x/is.js +6 -4
  101. package/x/is.js.map +1 -1
  102. package/x/map-g.d.ts +3 -3
  103. package/x/map-g.js +9 -8
  104. package/x/map-g.js.map +1 -1
  105. package/x/nap.d.ts +1 -0
  106. package/x/nap.js +1 -0
  107. package/x/nap.js.map +1 -1
  108. package/x/once.d.ts +1 -0
  109. package/x/once.js +12 -0
  110. package/x/once.js.map +1 -0
  111. package/x/pipe.d.ts +3 -3
  112. package/x/pipe.js +5 -5
  113. package/x/pipe.js.map +1 -1
  114. package/x/pubsub.d.ts +12 -1
  115. package/x/pubsub.js +7 -5
  116. package/x/pubsub.js.map +1 -1
  117. package/x/queue/queue.d.ts +4 -0
  118. package/x/queue/queue.js +14 -0
  119. package/x/queue/queue.js.map +1 -0
  120. package/x/queue/queue.test.d.ts +8 -0
  121. package/x/queue/queue.test.js +57 -0
  122. package/x/queue/queue.test.js.map +1 -0
  123. package/x/repeat.d.ts +5 -0
  124. package/x/repeat.js +23 -0
  125. package/x/repeat.js.map +1 -0
  126. package/x/scope.d.ts +37 -0
  127. package/x/scope.js +82 -0
  128. package/x/scope.js.map +1 -0
  129. package/x/templating.d.ts +6 -0
  130. package/x/templating.js +11 -0
  131. package/x/templating.js.map +1 -0
  132. package/x/tests.test.js +14 -10
  133. package/x/tests.test.js.map +1 -1
  134. package/x/time.d.ts +18 -0
  135. package/x/time.js +19 -0
  136. package/x/time.js.map +1 -0
  137. package/s/data/anka.ts +0 -66
  138. package/s/data/bytename/badge.test.ts +0 -53
  139. package/s/data/bytename/badge.ts +0 -81
  140. package/x/data/anka.d.ts +0 -5
  141. package/x/data/anka.js +0 -53
  142. package/x/data/anka.js.map +0 -1
  143. package/x/data/bytename/badge.d.ts +0 -27
  144. package/x/data/bytename/badge.js +0 -57
  145. package/x/data/bytename/badge.js.map +0 -1
  146. package/x/data/bytename/badge.test.js +0 -45
  147. package/x/data/bytename/badge.test.js.map +0 -1
  148. package/x/defer-promise.js.map +0 -1
package/README.md CHANGED
@@ -1,36 +1,80 @@
1
1
 
2
- # `@e280/stz`
3
- standard library of environment-agnostic typescript functions we use basically everywhere
2
+ # 🏂 `@e280/stz`
3
+
4
+ **stz** is e280's standard library of environment-agnostic typescript tools.
5
+
6
+ it's our javascript toolkit.
7
+
8
+ zero dependencies.
4
9
 
5
10
  <br/>
6
11
 
7
- ## the tools
8
- stz has many more tools than documented below, see their [sourcecode here in s/](./s/)
12
+ ## 🧰 STZ PRIMITIVES
9
13
 
10
- ### MapG — an extension of js Map with handy methods
11
- - `map.require`
12
- ```ts
13
- import {MapG} from "@e280/stz"
14
+ ### 🍏 MapG
15
+ > extended js map
14
16
 
15
- const map = new MapG<number, string>([
16
- [1, "hello"],
17
- [2, "world"],
18
- ])
17
+ ```ts
18
+ import {MapG} from "@e280/stz"
19
+
20
+ const map = new MapG<number, string>([
21
+ [1, "hello"],
22
+ [2, "world"],
23
+ ])
24
+ ```
19
25
 
20
- // throws error if the value is undefined
26
+ - `map.require(key)` returns the value for key — if missing, throw an error
27
+ ```ts
21
28
  const value = map.require(1)
29
+ // "hello"
22
30
  ```
23
- - `map.guarantee`
31
+ - `map.guarantee(key, make)` — returns the value for `key` — if missing, run `make` to set the value and return it
24
32
  ```ts
25
- // if the value is undefined, the new value "rofl" is set and returned
26
33
  const value = map.guarantee(3, () => "rofl")
34
+ // "rofl"
27
35
  ```
28
36
 
29
- ### pub and sub — minimal ergonomic event system
37
+ ### 🍏 nap
38
+ > sleep for some milliseconds
39
+
40
+ ```ts
41
+ import {nap} from "@e280/stz"
42
+
43
+ await nap(900)
44
+ // wait for 900 milliseconds
45
+ ```
46
+
47
+ ### 🍏 defer
48
+ > defer the resolve/reject of a promise to the outside
49
+
50
+ ```ts
51
+ import {defer} from "@e280/stz"
52
+
53
+ const deferred = defer()
54
+ ```
55
+
56
+ - resolve the deferred promise
57
+ ```ts
58
+ deferred.resolve()
59
+ ```
60
+ - reject the deferred promise
61
+ ```ts
62
+ deferred.reject(new Error("fail"))
63
+ ```
64
+ - await the promise
65
+ ```ts
66
+ await deferred.promise
67
+ ```
68
+
69
+ ### 🍏 `pub` and `sub`
70
+ > ergonomic event emitters
71
+
72
+ ```ts
73
+ import {pub, sub} from "@e280/stz"
74
+ ```
75
+
30
76
  - make a publisher fn
31
77
  ```ts
32
- import {pub} from "@e280/stz"
33
-
34
78
  // create a pub fn
35
79
  const sendMessage = pub<[string]>()
36
80
 
@@ -42,8 +86,6 @@ stz has many more tools than documented below, see their [sourcecode here in s/]
42
86
  ```
43
87
  - make a subscriber fn *(see how it's just the reverse of pub?)*
44
88
  ```ts
45
- import {sub} from "@e280/stz"
46
-
47
89
  // create a sub fn
48
90
  const onMessage = sub<[string]>()
49
91
 
@@ -72,14 +114,223 @@ stz has many more tools than documented below, see their [sourcecode here in s/]
72
114
  sendMessage.clear()
73
115
  onMessage.clear()
74
116
 
75
- // you can subscribe to only one next call
76
- onMessage.once(m => console.log(m))
77
- sendMessage.once(m => console.log(m))
117
+ // instead of a 'once' fn we simply await next()
118
+ await onMessage.next()
119
+ await sendMessage.next()
120
+ ```
121
+
122
+ <br/>
123
+
124
+ ## 🧰 STZ FN TOOLS
125
+
126
+ ### 🍏 `queue(fn)`
127
+ > execute calls in sequence (not concurrent)
128
+
129
+ ```ts
130
+ import {queue, nap} from "@e280/stz"
131
+
132
+ const fn = queue(async() => nap(100))
133
+
134
+ fn()
135
+ fn()
136
+ await fn() // waits for the previous calls (sequentially)
137
+ ```
138
+
139
+ ### 🍏 `once(fn)`
140
+ > ensure a fn is only executed one time
141
+
142
+ ```ts
143
+ import {once} from "@e280/stz"
144
+
145
+ let count = 0
146
+ const fn = once(() => count++)
147
+ console.log(count) // 0
148
+
149
+ fn()
150
+ console.log(count) // 1
151
+
152
+ fn()
153
+ console.log(count) // 1
154
+ ```
155
+
156
+ ### 🍏 `deadline(100, fn)`
157
+ > throws an error if the async function takes too long
158
+
159
+ ```ts
160
+ import {deadline} from "@e280/stz"
161
+
162
+ const fn = deadline(100, async() => {
163
+
164
+ // example deliberately takes too long
165
+ await nap(200)
166
+ })
167
+
168
+ await fn()
169
+ // DeadlineError: deadline exceeded (0.1 seconds)
170
+ ```
171
+
172
+ ### 🍏 `debounce(100, fn)`
173
+ > wait some time before actually executing the fn (absorbing redundant calls)
174
+
175
+ we use `debounce` a lot in ui code, like on a user's keyboard input in a form field, but rendering the form input can actually be slow enough that it causes problems when they type fast — to eliminate the jank, we `debounce` with like 400 ms, so we wait for the user to finish typing for a moment before actually running the validation.
176
+
177
+ ```ts
178
+ import {debounce} from "@e280/stz"
179
+
180
+ const fn = debounce(100, async() => {
181
+ await coolAction()
182
+ })
183
+
184
+ // each fn() call resets the timer
185
+ fn()
186
+ fn()
187
+ fn()
188
+
189
+ // coolAction is only called once here, other calls are redundant
190
+ ```
191
+
192
+ ### 🍏 `repeat(fn)`
193
+ > execute a function over and over again, back to back
194
+
195
+ ```ts
196
+ import {repeat} from "@e280/stz"
197
+
198
+ let ticks = 0
199
+
200
+ const stop = repeat(async() => {
201
+
202
+ // use a nap to add a delay between each execution
203
+ await nap(200)
204
+
205
+ ticks++
206
+ })
207
+
208
+ // stop repeating whenever you want
209
+ stop()
210
+ ```
211
+
212
+ <br/>
213
+
214
+ ## 🧰 STZ DATA UTILITIES
215
+
216
+ ### 🍏 Hex
217
+ > convert to/from hexadecimal string format
218
+ - `Hex.fromBytes(bytes)` — bytes to hex string
219
+ - `Hex.toBytes(string)` — hex string to bytes
220
+ - `Hex.random(32)` — generate random hex string (32 bytes)
221
+
222
+ ### 🍏 Base64
223
+ > convert to/from base64 string format
224
+ - `Base64.fromBytes(bytes)` — bytes to string
225
+ - `Base64.toBytes(string)` — string to bytes
226
+ - `Base64.random(32)` — generate random string (32 bytes)
227
+
228
+ ### 🍏 Base64url
229
+ > convert to/from base64 string format
230
+ - `Base64url.fromBytes(bytes)` — bytes to string
231
+ - `Base64url.toBytes(string)` — string to bytes
232
+ - `Base64url.random(32)` — generate random string (32 bytes)
233
+
234
+ ### 🍏 Base58
235
+ > convert to/from base64 string format
236
+ - `Base58.fromBytes(bytes)` — bytes to string
237
+ - `Base58.toBytes(string)` — string to bytes
238
+ - `Base58.random(32)` — generate random string (32 bytes)
239
+
240
+ ### 🍏 Txt
241
+ > convert to/from utf8 string format
242
+ - `Txt.fromBytes(bytes)` — bytes to string
243
+ - `Txt.toBytes(string)` — string to bytes
244
+
245
+ ### 🍏 Bytes
246
+ > utilities for dealing with Uint8Array
247
+ - `Bytes.eq(bytesA, bytesB)` — check if two byte arrays are equal
248
+ - `Bytes.random(32)` — generate crypto-random bytes
249
+
250
+ ### 🍏 BaseX
251
+ > convert data into arbitrary data encodings
252
+ - make a BaseX instance
253
+ ```ts
254
+ import {BaseX} from "@e280/stz"
255
+
256
+ const hex = new BaseX(BaseX.lexicons.hex)
257
+ ```
258
+ - convert between strings and binary
259
+ ```ts
260
+ hex.toBytes("9960cd633a46acfe8307d8a400e842da0d930a75fb8188e0f5da264e4b6b4e5b")
261
+ // Uint8Array
262
+
263
+ hex.fromBytes(bytes)
264
+ // string
265
+ ```
266
+ - you can also convert between strings and integers
267
+ ```ts
268
+ hex.fromInteger(Date.now())
269
+ // "197140ac804"
270
+
271
+ hex.toInteger(hex)
272
+ // 1748387940356
78
273
  ```
274
+ - available lexicons include
275
+ - base2
276
+ - hex
277
+ - base36
278
+ - base58
279
+ - base62
280
+ - base64 (with standard padding)
281
+ - base64url
282
+ - you can make insanely compact timestamps like this:
283
+ ```ts
284
+ import {BaseX} from "@e280/stz"
285
+
286
+ const base62 = new BaseX(BaseX.lexicons.base62)
287
+
288
+ base62.fromInteger(Date.now() / 1000)
289
+ // "1uK3au"
290
+ ```
291
+ - `1748388028` base10 epoch seconds (10 chars)
292
+ - `1uK3au` base62 epoch seconds (6 chars)
293
+ - *nice*
294
+
295
+ <br/>
296
+
297
+ ### 🍏 Bytename
298
+ > friendly string encoding for binary data
299
+
300
+ a bytename looks like `"midsen.picmyn.widrep.baclut dotreg.filtyp.nosnus.siptev"`. that's 16 bytes. each byte maps to a three-letter triplet
301
+
302
+ the bytename parser (`Bytename.toBytes`) ignores all non-alphabetic characters. thus `midsen.picmyn`, `midsenpicmyn`, and `mid@sen$pic@myn` are all equal.
303
+
304
+ ```ts
305
+ import {Bytename} from "@e280/stz"
306
+ ```
307
+ - ```ts
308
+ Bytename.fromBytes(new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]))
309
+ // "ribmug.hilmun"
310
+ ```
311
+ - ```ts
312
+ Bytename.toBytes("ribmug.hilmun")
313
+ // Uint8Array, 4 bytes
314
+ ```
315
+ - ```ts
316
+ const bytes = new Uint8Array([
317
+ 0xDE, 0xAD, 0xBE, 0xEF,
318
+ 0xDE, 0xAD, 0xBE, 0xEF,
319
+ ])
320
+
321
+ Bytename.fromBytes(bytes, {
322
+ groupSize: 2, // default is 4
323
+ groupSeparator: " ",
324
+ wordSeparator: ".",
325
+ })
326
+ // "ribmug.hilmun ribmug.hilmun"
327
+ ```
328
+
329
+ <br/>
79
330
 
80
331
  <br/>
81
332
 
82
- ## 💖 made with open source love
333
+ ## 💖 stz is made with open source love
83
334
  reward us with github stars
84
335
  build with us at https://e280.org/ but only if you're cool
85
336
 
package/package.json CHANGED
@@ -1,31 +1,35 @@
1
1
  {
2
2
  "name": "@e280/stz",
3
- "version": "0.0.0-8",
3
+ "version": "0.0.0",
4
4
  "description": "everyday ts fns for everything",
5
5
  "license": "MIT",
6
6
  "author": "Chase Moskal <chasemoskal@gmail.com>",
7
7
  "type": "module",
8
- "main": "x/index.js",
8
+ "main": "./x/index.js",
9
+ "exports": {
10
+ ".": "./x/index.js"
11
+ },
9
12
  "files": [
10
13
  "x",
11
14
  "s"
12
15
  ],
13
16
  "scripts": {
14
17
  "build": "run-s _clean _links _tsc",
15
- "test": "node x/tests.test.js --verbose",
18
+ "test": "node x/tests.test.js",
19
+ "test-watch": "node --watch x/tests.test.js",
20
+ "test-inspect": "node inspect x/tests.test.js",
16
21
  "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +",
17
- "start": "run-p _tscw _testw",
22
+ "watch": "run-p _tscw test-watch",
18
23
  "_clean": "rm -rf x && mkdir x",
19
24
  "_links": "ln -s \"$(realpath node_modules)\" x/node_modules",
20
25
  "_tsc": "tsc",
21
- "_tscw": "tsc -w",
22
- "_testw": "node --watch x/tests.test.js"
26
+ "_tscw": "tsc -w"
23
27
  },
24
28
  "devDependencies": {
25
- "@e280/science": "^0.0.4",
26
- "@types/node": "^22.15.3",
29
+ "@e280/science": "^0.0.6",
30
+ "@types/node": "^24.3.0",
27
31
  "npm-run-all": "^4.1.5",
28
- "typescript": "^5.8.3"
32
+ "typescript": "^5.9.2"
29
33
  },
30
34
  "keywords": [
31
35
  "standard",
package/s/coalesce.ts CHANGED
@@ -1,4 +1,5 @@
1
1
 
2
+ /** return a single function that calls all the provided functions */
2
3
  export function coalesce<A extends any[] = []>(...fns: ((...a: A) => void)[]) {
3
4
  return (...a: A) => fns.forEach(fn => fn(...a))
4
5
  }
@@ -0,0 +1,15 @@
1
+
2
+ export type Constructor<T extends {} = {}> = new(...args: any[]) => T
3
+
4
+ export type Ctor<
5
+ Params extends any[] = any[],
6
+ Instance extends {} = {},
7
+ > = new(...params: Params) => Instance
8
+
9
+ export function denew<
10
+ Params extends any[] = any[],
11
+ Instance extends {} = {},
12
+ >(C: Ctor<Params, Instance>) {
13
+ return (...p: Params) => new C(...p)
14
+ }
15
+
@@ -0,0 +1,170 @@
1
+
2
+ import {Bytes} from "./bytes.js"
3
+
4
+ export type Lexicon = {
5
+ characters: string
6
+ negativePrefix?: string
7
+ padding?: {
8
+ character: string
9
+ size: number
10
+ }
11
+ }
12
+
13
+ export class BaseX {
14
+ static lexicons = Object.freeze({
15
+ base2: {characters: "01"},
16
+ hex: {characters: "0123456789abcdef"},
17
+ base36: {characters: "0123456789abcdefghijklmnopqrstuvwxyz"},
18
+ base58: {characters: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"},
19
+ base62: {characters: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"},
20
+ base64url: {
21
+ negativePrefix: "~",
22
+ characters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
23
+ },
24
+ base64: {
25
+ characters: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
26
+ padding: {character: "=", size: 4},
27
+ },
28
+ })
29
+
30
+ private lookup: Record<string, number>
31
+ private negativePrefix: string
32
+
33
+ constructor(public lexicon: Lexicon) {
34
+ this.lookup = Object.fromEntries(
35
+ [...lexicon.characters].map((char, i) => [char, i])
36
+ )
37
+ this.negativePrefix = lexicon.negativePrefix ?? "-"
38
+ }
39
+
40
+ toBytes(s: string): Uint8Array {
41
+ const bitsPerChar = Math.log2(this.lexicon.characters.length)
42
+ if (Number.isInteger(bitsPerChar)) {
43
+ // Bitstream mode (for power-of-2 lexicons)
44
+ let bitBuffer = 0
45
+ let bitCount = 0
46
+ const output: number[] = []
47
+
48
+ for (const char of s) {
49
+ if (char === this.lexicon.padding?.character) continue
50
+ const val = this.lookup[char]
51
+ if (val === undefined) throw new Error(`Invalid character: ${char}`)
52
+ bitBuffer = (bitBuffer << bitsPerChar) | val
53
+ bitCount += bitsPerChar
54
+
55
+ while (bitCount >= 8) {
56
+ bitCount -= 8
57
+ output.push((bitBuffer >> bitCount) & 0xFF)
58
+ }
59
+ }
60
+
61
+ return new Uint8Array(output)
62
+ }
63
+
64
+ // Radix mode fallback
65
+ let num = 0n
66
+ const base = BigInt(this.lexicon.characters.length)
67
+ let negative = false
68
+ if (s.startsWith(this.negativePrefix)) {
69
+ s = s.slice(this.negativePrefix.length)
70
+ negative = true
71
+ }
72
+ for (const char of s) {
73
+ const val = this.lookup[char]
74
+ if (val === undefined) throw new Error(`Invalid character: ${char}`)
75
+ num = num * base + BigInt(val)
76
+ }
77
+ const out: number[] = []
78
+ while (num > 0n) {
79
+ out.unshift(Number(num % 256n))
80
+ num = num / 256n
81
+ }
82
+ return new Uint8Array(out)
83
+ }
84
+
85
+ fromBytes(bytes: Uint8Array): string {
86
+ const bitsPerChar = Math.log2(this.lexicon.characters.length)
87
+ if (Number.isInteger(bitsPerChar)) {
88
+ // Bitstream mode (for power-of-2 lexicons)
89
+ let bitBuffer = 0
90
+ let bitCount = 0
91
+ let out = ""
92
+
93
+ for (const byte of bytes) {
94
+ bitBuffer = (bitBuffer << 8) | byte
95
+ bitCount += 8
96
+
97
+ while (bitCount >= bitsPerChar) {
98
+ bitCount -= bitsPerChar
99
+ const index = (bitBuffer >> bitCount) & ((1 << bitsPerChar) - 1)
100
+ out += this.lexicon.characters[index]
101
+ }
102
+ }
103
+
104
+ // 🩹 flush remaining bits
105
+ if (bitCount > 0) {
106
+ const index = (bitBuffer << (bitsPerChar - bitCount)) & ((1 << bitsPerChar) - 1)
107
+ out += this.lexicon.characters[index]
108
+ }
109
+
110
+ // Add padding if applicable
111
+ if (this.lexicon.padding) {
112
+ while (out.length % this.lexicon.padding.size !== 0)
113
+ out += this.lexicon.padding.character
114
+ }
115
+
116
+ return out
117
+ }
118
+
119
+ // Radix mode fallback
120
+ let num = 0n
121
+ for (const byte of bytes)
122
+ num = (num << 8n) + BigInt(byte)
123
+
124
+ if (num === 0n) return this.lexicon.characters[0]
125
+
126
+ const base = BigInt(this.lexicon.characters.length)
127
+ let out = ""
128
+ while (num > 0n) {
129
+ out = this.lexicon.characters[Number(num % base)] + out
130
+ num = num / base
131
+ }
132
+ return out
133
+ }
134
+
135
+ toInteger(s: string) {
136
+ if (!s) return 0
137
+ let n = 0n
138
+ let negative = false
139
+ const base = BigInt(this.lexicon.characters.length)
140
+ if (s.startsWith(this.negativePrefix)) {
141
+ s = s.slice(this.negativePrefix.length)
142
+ negative = true
143
+ }
144
+ for (const char of s) {
145
+ const value = this.lookup[char]
146
+ if (value === undefined) throw new Error(`Invalid character: ${char}`)
147
+ n = n * base + BigInt(value)
148
+ }
149
+ return Number(negative ? -n : n)
150
+ }
151
+
152
+ fromInteger(n: number) {
153
+ n = Math.floor(n)
154
+ const negative = n < 0
155
+ let num = BigInt(negative ? -n : n)
156
+ if (num === 0n) return this.lexicon.characters[0]
157
+ const base = BigInt(this.lexicon.characters.length)
158
+ let out = ""
159
+ while (num > 0n) {
160
+ out = this.lexicon.characters[Number(num % base)] + out
161
+ num = num / base
162
+ }
163
+ return negative ? `${this.negativePrefix}${out}` : out
164
+ }
165
+
166
+ random(count = 32) {
167
+ return this.fromBytes(Bytes.random(count))
168
+ }
169
+ }
170
+
package/s/data/base58.ts CHANGED
@@ -16,8 +16,8 @@ const base = 58
16
16
  const characters = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
17
17
 
18
18
  export const Base58 = Object.freeze({
19
- string(bytes: Uint8Array) {
20
- let intVal = BigInt("0x" + Hex.string(bytes))
19
+ fromBytes(bytes: Uint8Array) {
20
+ let intVal = BigInt("0x" + Hex.fromBytes(bytes))
21
21
  let encoded = ""
22
22
 
23
23
  while (intVal > 0) {
@@ -34,7 +34,7 @@ export const Base58 = Object.freeze({
34
34
  return encoded
35
35
  },
36
36
 
37
- bytes(string: string) {
37
+ toBytes(string: string) {
38
38
  let intVal = BigInt(0)
39
39
 
40
40
  for (const char of string) {
@@ -45,7 +45,7 @@ export const Base58 = Object.freeze({
45
45
 
46
46
  let hex = intVal.toString(16)
47
47
  if (hex.length % 2 !== 0) hex = "0" + hex
48
- const bytes = Hex.bytes(hex)
48
+ const bytes = Hex.toBytes(hex)
49
49
 
50
50
  let leadingZeroes = 0
51
51
  for (const char of string) {
@@ -59,7 +59,17 @@ export const Base58 = Object.freeze({
59
59
  },
60
60
 
61
61
  random(count = 32) {
62
- return this.string(Bytes.random(count))
62
+ return this.fromBytes(Bytes.random(count))
63
+ },
64
+
65
+ /** @deprecated renamed to `fromBytes` */
66
+ string(bytes: Uint8Array) {
67
+ return Base58.fromBytes(bytes)
68
+ },
69
+
70
+ /** @deprecated renamed to `toBytes` */
71
+ bytes(string: string) {
72
+ return Base58.toBytes(string)
63
73
  },
64
74
  })
65
75
 
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