@e280/stz 0.0.0-3 → 0.0.0-31

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 (172) hide show
  1. package/README.md +285 -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 +5 -6
  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/deadline.d.ts +2 -2
  86. package/x/deadline.js +4 -4
  87. package/x/deadline.js.map +1 -1
  88. package/x/debounce/debounce.test.d.ts +8 -1
  89. package/x/debounce/debounce.test.js +70 -74
  90. package/x/debounce/debounce.test.js.map +1 -1
  91. package/x/deep/deep.test.d.ts +21 -1
  92. package/x/deep/deep.test.js +5 -9
  93. package/x/deep/deep.test.js.map +1 -1
  94. package/x/deep/parts/equal.test.d.ts +19 -1
  95. package/x/deep/parts/equal.test.js +107 -125
  96. package/x/deep/parts/equal.test.js.map +1 -1
  97. package/x/{defer-promise.d.ts → defer.d.ts} +2 -2
  98. package/x/{defer-promise.js → defer.js} +2 -2
  99. package/x/defer.js.map +1 -0
  100. package/x/drill.d.ts +1 -3
  101. package/x/drill.js +2 -4
  102. package/x/drill.js.map +1 -1
  103. package/x/ev.d.ts +6 -0
  104. package/x/ev.js +10 -0
  105. package/x/ev.js.map +1 -0
  106. package/x/id-counter.d.ts +5 -0
  107. package/x/id-counter.js +10 -0
  108. package/x/id-counter.js.map +1 -0
  109. package/x/index.d.ts +13 -2
  110. package/x/index.js +13 -2
  111. package/x/index.js.map +1 -1
  112. package/x/is.d.ts +6 -4
  113. package/x/is.js +6 -4
  114. package/x/is.js.map +1 -1
  115. package/x/loopy.d.ts +2 -0
  116. package/x/loopy.js +10 -0
  117. package/x/loopy.js.map +1 -0
  118. package/x/map-g.d.ts +3 -3
  119. package/x/map-g.js +1 -1
  120. package/x/map-g.js.map +1 -1
  121. package/x/nap.d.ts +1 -0
  122. package/x/nap.js +1 -0
  123. package/x/nap.js.map +1 -1
  124. package/x/once.d.ts +1 -0
  125. package/x/once.js +12 -0
  126. package/x/once.js.map +1 -0
  127. package/x/pubsub.d.ts +12 -1
  128. package/x/pubsub.js +7 -5
  129. package/x/pubsub.js.map +1 -1
  130. package/x/queue/queue.d.ts +4 -0
  131. package/x/queue/queue.js +14 -0
  132. package/x/queue/queue.js.map +1 -0
  133. package/x/queue/queue.test.d.ts +8 -0
  134. package/x/queue/queue.test.js +57 -0
  135. package/x/queue/queue.test.js.map +1 -0
  136. package/x/repeat.d.ts +5 -0
  137. package/x/repeat.js +23 -0
  138. package/x/repeat.js.map +1 -0
  139. package/x/tests.test.js +18 -0
  140. package/x/tests.test.js.map +1 -0
  141. package/x/time.d.ts +18 -0
  142. package/x/time.js +19 -0
  143. package/x/time.js.map +1 -0
  144. package/readme.md +0 -85
  145. package/s/data/anka.ts +0 -66
  146. package/s/data/barname/badge.ts +0 -78
  147. package/s/data/barname/demo.ts +0 -49
  148. package/s/data/barname/index.ts +0 -4
  149. package/x/data/anka.d.ts +0 -5
  150. package/x/data/anka.js +0 -53
  151. package/x/data/anka.js.map +0 -1
  152. package/x/data/barname/badge.d.ts +0 -25
  153. package/x/data/barname/badge.js +0 -64
  154. package/x/data/barname/badge.js.map +0 -1
  155. package/x/data/barname/barname.d.ts +0 -7
  156. package/x/data/barname/barname.js.map +0 -1
  157. package/x/data/barname/demo.js +0 -45
  158. package/x/data/barname/demo.js.map +0 -1
  159. package/x/data/barname/index.d.ts +0 -2
  160. package/x/data/barname/index.js +0 -3
  161. package/x/data/barname/index.js.map +0 -1
  162. package/x/data/barname/utils/prefixes.js.map +0 -1
  163. package/x/data/barname/utils/suffixes.js.map +0 -1
  164. package/x/defer-promise.js.map +0 -1
  165. /package/{license → LICENSE} +0 -0
  166. /package/s/data/{barname → bytename}/utils/prefixes.ts +0 -0
  167. /package/s/data/{barname → bytename}/utils/suffixes.ts +0 -0
  168. /package/x/data/{barname → bytename}/utils/prefixes.d.ts +0 -0
  169. /package/x/data/{barname → bytename}/utils/prefixes.js +0 -0
  170. /package/x/data/{barname → bytename}/utils/suffixes.d.ts +0 -0
  171. /package/x/data/{barname → bytename}/utils/suffixes.js +0 -0
  172. /package/x/{data/barname/demo.d.ts → tests.test.d.ts} +0 -0
package/README.md ADDED
@@ -0,0 +1,285 @@
1
+
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.
9
+
10
+ <br/>
11
+
12
+ ## 🧰 STZ PRIMITIVES
13
+ > cool concepts we use all over the place
14
+
15
+ ### 🍏 MapG
16
+ > extended js map
17
+
18
+ ```ts
19
+ import {MapG} from "@e280/stz"
20
+
21
+ const map = new MapG<number, string>([
22
+ [1, "hello"],
23
+ [2, "world"],
24
+ ])
25
+ ```
26
+
27
+ - `map.require(key)` — throws error if the value is undefined
28
+ ```ts
29
+ const value = map.require(1)
30
+ // "hello"
31
+ ```
32
+ - `map.guarantee(key, make)` — returns the value for `key`, but if undefined, run `make` to set the value
33
+ ```ts
34
+ const value = map.guarantee(3, () => "rofl")
35
+ // "rofl"
36
+ ```
37
+
38
+ ### nap
39
+ > sleep for some milliseconds
40
+
41
+ ```ts
42
+ import {nap} from "@e280/stz"
43
+
44
+ await nap(900)
45
+ // wait for 900 milliseconds
46
+ ```
47
+
48
+ ### defer
49
+ > defer the resolve/reject of a promise to the outside
50
+
51
+ ```ts
52
+ import {defer} from "@e280/stz"
53
+
54
+ const deferred = defer()
55
+ ```
56
+
57
+ - resolve the deferred promise
58
+ ```ts
59
+ deferred.resolve()
60
+ ```
61
+ - reject the deferred promise
62
+ ```ts
63
+ deferred.reject(new Error("fail"))
64
+ ```
65
+ - await the promise
66
+ ```ts
67
+ await deferred.promise
68
+ ```
69
+
70
+ ### 🍏 `pub` and `sub`
71
+ > ergonomic event emitters
72
+
73
+ ```ts
74
+ import {pub, sub} from "@e280/stz"
75
+ ```
76
+
77
+ - make a publisher fn
78
+ ```ts
79
+ // create a pub fn
80
+ const sendMessage = pub<[string]>()
81
+
82
+ // subscribe to it
83
+ sendMessage.sub(m => console.log(m))
84
+
85
+ // publish to it
86
+ sendMessage("hello")
87
+ ```
88
+ - make a subscriber fn *(see how it's just the reverse of pub?)*
89
+ ```ts
90
+ // create a sub fn
91
+ const onMessage = sub<[string]>()
92
+
93
+ // subscribe to it
94
+ onMessage(m => console.log(m))
95
+
96
+ // publish to it
97
+ onMessage.pub("hello")
98
+ ```
99
+ - the pub and sub are the same, but have differing invoke signatures
100
+ - i seem to use `sub` more often
101
+ - both have some extra functionality
102
+ ```ts
103
+ // pub fns return a promise, to wait for all async subscribers
104
+ await sendMessage("hello")
105
+ await onMessage.pub("hello")
106
+
107
+ // sub fns return an unsub fn
108
+ const unsub1 = onMessage(m => console.log(m))
109
+ unsub1() // unsubscribe that listener
110
+
111
+ const unsub2 = sendMessage.sub(m => console.log(m))
112
+ unsub2() // unsubscribe that listener
113
+
114
+ // you can clear all subscribers from a pub or a sub
115
+ sendMessage.clear()
116
+ onMessage.clear()
117
+
118
+ // instead of a 'once' fn we simply await next()
119
+ await onMessage.next()
120
+ await sendMessage.next()
121
+ ```
122
+
123
+ <br/>
124
+
125
+ ## 🧰 FN TOOLS
126
+ > function-oriented tools
127
+
128
+ ### 🍏 `queue(fn)`
129
+ > execute calls in sequence (not concurrent)
130
+
131
+ ```ts
132
+ import {queue, nap} from "@e280/stz"
133
+
134
+ const fn = queue(async() => nap(100))
135
+
136
+ fn()
137
+ fn()
138
+ await fn() // waits for the previous calls (sequentially)
139
+ ```
140
+
141
+ ### 🍏 `once(fn)`
142
+ > ensure a fn is only executed one time
143
+
144
+ ```ts
145
+ import {once} from "@e280/stz"
146
+
147
+ let count = 0
148
+ const fn = once(() => count++)
149
+ console.log(count) // 0
150
+
151
+ fn()
152
+ console.log(count) // 1
153
+
154
+ fn()
155
+ console.log(count) // 1
156
+ ```
157
+
158
+ ### 🍏 `deadline(100, message, fn)`
159
+ > throws an error if the async function takes too long
160
+
161
+ ```ts
162
+ import {deadline} from "@e280/stz"
163
+
164
+ const fn = deadline(100, "deadline exceeded", async() => {
165
+
166
+ // example deliberately takes too long
167
+ await nap(200)
168
+ })
169
+
170
+ await fn()
171
+ // DeadlineError: deadline exceeded, timed out in 0.1 seconds
172
+ ```
173
+
174
+ ### 🍏 `repeat(fn)`
175
+ > execute a function over and over again, back to back
176
+
177
+ ```ts
178
+ import {repeat} from "@e280/stz"
179
+
180
+ let ticks = 0
181
+
182
+ const stop = repeat(async() => {
183
+
184
+ // use a nap to add a delay between each execution
185
+ await nap(200)
186
+
187
+ ticks++
188
+ })
189
+
190
+ // stop repeating whenever you want
191
+ stop()
192
+ ```
193
+
194
+ <br/>
195
+
196
+ ## 🧰 DATA UTILITIES
197
+ > transforming and representing binary data
198
+
199
+ ### 🍏 BaseX
200
+ > represent data in arbitrary encodings
201
+ - make a BaseX instance
202
+ ```ts
203
+ import {BaseX} from "@e280/stz"
204
+
205
+ const hex = new BaseX(BaseX.lexicons.hex)
206
+ ```
207
+ - convert between strings and binary
208
+ ```ts
209
+ hex.toBytes("9960cd633a46acfe8307d8a400e842da0d930a75fb8188e0f5da264e4b6b4e5b")
210
+ // Uint8Array
211
+
212
+ hex.fromBytes(bytes)
213
+ // string
214
+ ```
215
+ - you can also convert between strings and integers
216
+ ```ts
217
+ hex.fromInteger(Date.now())
218
+ // "197140ac804"
219
+
220
+ hex.toInteger(hex)
221
+ // 1748387940356
222
+ ```
223
+ - available lexicons include
224
+ - base2
225
+ - hex
226
+ - base36
227
+ - base58
228
+ - base62
229
+ - base64 (with standard padding)
230
+ - base64url
231
+ - you can make insanely compact timestamps like this:
232
+ ```ts
233
+ import {BaseX} from "@e280/stz"
234
+
235
+ const base62 = new BaseX(BaseX.lexicons.base62)
236
+
237
+ base62.fromInteger(Date.now() / 1000)
238
+ // "1uK3au"
239
+ ```
240
+ - `1748388028` base10 epoch seconds (10 chars)
241
+ - `1uK3au` base62 epoch seconds (6 chars)
242
+ - *nice*
243
+
244
+ <br/>
245
+
246
+ ### 🍏 Bytename
247
+ > friendly string encoding for binary data
248
+
249
+ a bytename looks like `"midsen.picmyn.widrep.baclut dotreg.filtyp.nosnus.siptev"`. that's 16 bytes. each byte maps to a three-letter triplet
250
+
251
+ the bytename parser (`Bytename.toBytes`) ignores all non-alphabetic characters. thus `midsen.picmyn`, `midsenpicmyn`, and `mid@sen$pic@myn` are all equal.
252
+
253
+ ```ts
254
+ import {Bytename} from "@e280/stz"
255
+ ```
256
+ - ```ts
257
+ Bytename.fromBytes(new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]))
258
+ // "ribmug.hilmun"
259
+ ```
260
+ - ```ts
261
+ Bytename.toBytes("ribmug.hilmun")
262
+ // Uint8Array, 4 bytes
263
+ ```
264
+ - ```ts
265
+ const bytes = new Uint8Array([
266
+ 0xDE, 0xAD, 0xBE, 0xEF,
267
+ 0xDE, 0xAD, 0xBE, 0xEF,
268
+ ])
269
+
270
+ Bytename.fromBytes(bytes, {
271
+ groupSize: 2, // default is 4
272
+ groupSeparator: " ",
273
+ wordSeparator: ".",
274
+ })
275
+ // "ribmug.hilmun ribmug.hilmun"
276
+ ```
277
+
278
+ <br/>
279
+
280
+ <br/>
281
+
282
+ ## 💖 stz is made with open source love
283
+ reward us with github stars
284
+ build with us at https://e280.org/ but only if you're cool
285
+
package/package.json CHANGED
@@ -1,28 +1,35 @@
1
1
  {
2
2
  "name": "@e280/stz",
3
- "version": "0.0.0-3",
3
+ "version": "0.0.0-31",
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",
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",
21
+ "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +",
22
+ "watch": "run-p _tscw test-watch",
15
23
  "_clean": "rm -rf x && mkdir x",
16
24
  "_links": "ln -s \"$(realpath node_modules)\" x/node_modules",
17
25
  "_tsc": "tsc",
18
- "start": "exit 0",
19
- "count": "find s -path '*/_archive' -prune -o -name '*.ts' -exec wc -l {} +",
20
- "test": "exit 0"
26
+ "_tscw": "tsc -w"
21
27
  },
22
28
  "devDependencies": {
23
- "@types/node": "^22.14.1",
29
+ "@e280/science": "^0.0.5",
30
+ "@types/node": "^24.2.0",
24
31
  "npm-run-all": "^4.1.5",
25
- "typescript": "^5.8.3"
32
+ "typescript": "^5.9.2"
26
33
  },
27
34
  "keywords": [
28
35
  "standard",
@@ -1,124 +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
- // }
1
+
2
+ import {clone} from "./clone.js"
3
+ import {Science, expect} from "@e280/science"
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).is(originalEntries)
9
+ expect(cloned).not.is(original) // check for deep cloning
10
+ }
11
+
12
+ export default Science.suite({
13
+
14
+ async "clone primitive types"() {
15
+ expect(clone(123)).is(123)
16
+ expect(clone("abc")).is("abc")
17
+ expect(clone(true)).is(true)
18
+ },
19
+
20
+ async "clone arrays"() {
21
+ const array = [1, 2, 3]
22
+ const clonedArray = clone(array)
23
+ expect(JSON.stringify(clonedArray)).is(JSON.stringify(array))
24
+ expect(clonedArray).not.is(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)).is(JSON.stringify(object))
31
+ expect(clonedObject).not.is(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")).is("value")
39
+ expect(clonedMap).not.is(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.is(set)
48
+ },
49
+
50
+ async "clone Date"() {
51
+ const date = new Date()
52
+ const clonedDate = clone(date)
53
+ expect(clonedDate.getTime()).is(date.getTime())
54
+ expect(clonedDate).not.is(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.is(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)).is(JSON.stringify(object))
74
+ expect(clonedObject).not.is(object) // check for deep cloning
75
+ expect(clonedObject.inner).not.is(object.inner) // check for deep cloning
76
+ expect(clonedObject.inner.deeper).not.is(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)).is(JSON.stringify(array))
83
+ expect(clonedArray).not.is(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)).is(JSON.stringify(object))
90
+ expect(clonedObject).not.is(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)).is(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.is(true)
117
+ expect(clonedData.obj1).not.is(clonedData.obj2)
118
+ expect(clonedData.obj1.id).is(1)
119
+ expect(clonedData.obj1.name).is("H4CK3RM4N")
120
+ expect(clonedData.obj2.id).is(1)
121
+ expect(clonedData.obj2.name).is("H4CK3RM4N")
122
+ },
123
+ })
124
124
 
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
  }