@e280/stz 0.0.0-8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +8 -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 +4 -3
- package/x/pipe.js +6 -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
package/README.md
CHANGED
|
@@ -1,36 +1,80 @@
|
|
|
1
1
|
|
|
2
|
-
# `@e280/stz`
|
|
3
|
-
|
|
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
|
-
##
|
|
8
|
-
stz has many more tools than documented below, see their [sourcecode here in s/](./s/)
|
|
12
|
+
## 🧰 STZ PRIMITIVES
|
|
9
13
|
|
|
10
|
-
### MapG
|
|
11
|
-
|
|
12
|
-
```ts
|
|
13
|
-
import {MapG} from "@e280/stz"
|
|
14
|
+
### 🍏 MapG
|
|
15
|
+
> extended js map
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
//
|
|
76
|
-
onMessage.
|
|
77
|
-
sendMessage.
|
|
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.
|
|
3
|
+
"version": "0.1.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
|
|
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
|
-
"
|
|
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
|
|
26
|
-
"@types/node": "^
|
|
29
|
+
"@e280/science": "^0.1.0",
|
|
30
|
+
"@types/node": "^24.3.0",
|
|
27
31
|
"npm-run-all": "^4.1.5",
|
|
28
|
-
"typescript": "^5.
|
|
32
|
+
"typescript": "^5.9.2"
|
|
29
33
|
},
|
|
30
34
|
"keywords": [
|
|
31
35
|
"standard",
|
package/s/coalesce.ts
CHANGED
package/s/constructor.ts
ADDED
|
@@ -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
|
+
|
package/s/data/base-x.ts
ADDED
|
@@ -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
|
-
|
|
20
|
-
let intVal = BigInt("0x" + Hex.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
package/s/data/base64url.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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.
|
|
19
|
+
return Base64.toBytes(b64)
|
|
21
20
|
},
|
|
22
21
|
|
|
23
22
|
random(count = 32) {
|
|
24
|
-
return this.
|
|
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
|
|