@monstermann/dsp 0.2.3 → 0.3.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 CHANGED
@@ -2,8 +2,532 @@
2
2
 
3
3
  <h1>dsp</h1>
4
4
 
5
+ ![Minified](https://img.shields.io/badge/Minified-1.43_KB-blue?style=flat-square&labelColor=%2315161D&color=%2369a1ff) ![Minzipped](https://img.shields.io/badge/Minzipped-593_B-blue?style=flat-square&labelColor=%2315161D&color=%2369a1ff)
6
+
5
7
  **Small & fast disposables.**
6
8
 
7
9
  [Documentation](https://MichaelOstermann.github.io/dsp)
8
10
 
9
11
  </div>
12
+
13
+ ## Example
14
+
15
+ ```ts
16
+ import { Dsp } from "@monstermann/dsp";
17
+
18
+ const dspA = Dsp.create();
19
+ const dspB = Dsp.create();
20
+
21
+ Dsp.add(dspA, () => console.log(1));
22
+ Dsp.add(dspA, () => console.log(2));
23
+ Dsp.add(dspB, () => console.log(3));
24
+ Dsp.add(dspB, () => console.log(4));
25
+ Dsp.add(dspA, dspB);
26
+
27
+ Dsp.dispose(dspA); // Prints: 4, 3, 2, 1
28
+ ```
29
+
30
+ ## Installation
31
+
32
+ ```sh [npm]
33
+ npm install @monstermann/dsp
34
+ ```
35
+
36
+ ```sh [pnpm]
37
+ pnpm add @monstermann/dsp
38
+ ```
39
+
40
+ ```sh [yarn]
41
+ yarn add @monstermann/dsp
42
+ ```
43
+
44
+ ```sh [bun]
45
+ bun add @monstermann/dsp
46
+ ```
47
+
48
+ ## Benchmarks
49
+
50
+ Apple M1 Max, Node v24.0.1
51
+
52
+ ### bench-dispose-cbs
53
+
54
+ - Setup: Create a disposable with N callbacks attached
55
+ - Bench: Dispose above
56
+
57
+ | name | ops/sec | time/op | margin | samples |
58
+ | ------------------------------- | ------: | ------: | :----: | ------: |
59
+ | Dsp.dispose(dsp) x 1 | 33M | 23ns | ±0.07% | 43M |
60
+ | Dsp.dispose(dsp) x 10 | 24M | 40ns | ±0.11% | 25M |
61
+ | Dsp.dispose(dsp) x 100 | 4M | 237ns | ±0.07% | 4M |
62
+ | DisposableStack.dispose() x 1 | 22M | 51ns | ±0.07% | 20M |
63
+ | DisposableStack.dispose() x 10 | 4M | 245ns | ±0.07% | 4M |
64
+ | DisposableStack.dispose() x 100 | 468K | 2µs | ±0.10% | 463K |
65
+
66
+ ### bench-dispose-dsps-wide
67
+
68
+ - Setup: Create a disposable with N other disposables attached
69
+ - Bench: Dispose above
70
+
71
+ | name | ops/sec | time/op | margin | samples |
72
+ | ------------------------------- | ------: | ------: | :----: | ------: |
73
+ | Dsp.dispose(dsp) x 1 | 27M | 30ns | ±0.07% | 34M |
74
+ | Dsp.dispose(dsp) x 10 | 9M | 118ns | ±0.11% | 8M |
75
+ | Dsp.dispose(dsp) x 100 | 986K | 1µs | ±4.23% | 877K |
76
+ | DisposableStack.dispose() x 1 | 12M | 83ns | ±0.07% | 12M |
77
+ | DisposableStack.dispose() x 10 | 2M | 569ns | ±0.08% | 2M |
78
+ | DisposableStack.dispose() x 100 | 188K | 5µs | ±0.09% | 186K |
79
+
80
+ ### bench-dispose-dsps-deep
81
+
82
+ - Setup: Create a disposable with N other disposables attached, arranged as a chain
83
+ - Bench: Dispose above
84
+
85
+ | name | ops/sec | time/op | margin | samples |
86
+ | ------------------------------- | ------: | ------: | :----: | ------: |
87
+ | Dsp.dispose(dsp) x 1 | 29M | 26ns | ±0.08% | 38M |
88
+ | Dsp.dispose(dsp) x 10 | 7M | 156ns | ±0.24% | 6M |
89
+ | Dsp.dispose(dsp) x 100 | 682K | 2µs | ±3.65% | 634K |
90
+ | DisposableStack.dispose() x 1 | 13M | 83ns | ±0.07% | 12M |
91
+ | DisposableStack.dispose() x 10 | 1M | 783ns | ±0.07% | 1M |
92
+ | DisposableStack.dispose() x 100 | 110K | 9µs | ±0.09% | 109K |
93
+
94
+ ### bench-dispose-dsps-wide-reverse
95
+
96
+ - Setup: Create a disposable with N other disposables attached
97
+ - Bench: Dispose above in reverse order, one by one
98
+
99
+ | name | ops/sec | time/op | margin | samples |
100
+ | ------------------------------- | ------: | ------: | :----: | ------: |
101
+ | Dsp.dispose(dsp) x 1 | 29M | 26ns | ±0.08% | 38M |
102
+ | Dsp.dispose(dsp) x 10 | 7M | 155ns | ±1.19% | 6M |
103
+ | Dsp.dispose(dsp) x 100 | 767K | 1µs | ±0.10% | 756K |
104
+ | DisposableStack.dispose() x 1 | 20M | 55ns | ±0.08% | 18M |
105
+ | DisposableStack.dispose() x 10 | 3M | 370ns | ±0.05% | 3M |
106
+ | DisposableStack.dispose() x 100 | 294K | 3µs | ±0.05% | 293K |
107
+
108
+ ### bench-dispose-dsps-deep-reverse
109
+
110
+ - Setup: Create a disposable with N other disposables attached, arranged as a chain
111
+ - Bench: Dispose above in reverse order, one by one
112
+
113
+ | name | ops/sec | time/op | margin | samples |
114
+ | ------------------------------- | ------: | ------: | :----: | ------: |
115
+ | Dsp.dispose(dsp) x 1 | 24M | 40ns | ±0.04% | 25M |
116
+ | Dsp.dispose(dsp) x 10 | 5M | 223ns | ±0.06% | 4M |
117
+ | Dsp.dispose(dsp) x 100 | 471K | 2µs | ±2.48% | 462K |
118
+ | DisposableStack.dispose() x 1 | 11M | 93ns | ±0.31% | 11M |
119
+ | DisposableStack.dispose() x 10 | 1M | 841ns | ±0.05% | 1M |
120
+ | DisposableStack.dispose() x 100 | 103K | 10µs | ±0.06% | 103K |
121
+
122
+ ### bench-link-cbs
123
+
124
+ - Setup: Create a disposable
125
+ - Bench: Attach N callbacks to it
126
+
127
+ | name | ops/sec | time/op | margin | samples |
128
+ | ------------------------------------------------- | ------: | ------: | :----: | ------: |
129
+ | Dsp.add(dsp, callback) x 1 | 34M | 22ns | ±0.06% | 45M |
130
+ | Dsp.add(dsp, callback) x 100 | 2M | 768ns | ±6.35% | 1M |
131
+ | Dsp.add(dsp, callback) x 1000 | 158K | 7µs | ±1.56% | 137K |
132
+ | DisposableStack.adopt(undefined, callback) x 1 | 24M | 43ns | ±0.28% | 23M |
133
+ | DisposableStack.adopt(undefined, callback) x 100 | 496K | 2µs | ±6.78% | 451K |
134
+ | DisposableStack.adopt(undefined, callback) x 1000 | 46K | 23µs | ±0.75% | 44K |
135
+
136
+ ### bench-link-dsps
137
+
138
+ - Setup: Create a disposable
139
+ - Bench: Attach N other disposables to it
140
+
141
+ | name | ops/sec | time/op | margin | samples |
142
+ | ------------------------------------------------- | ------: | ------: | :----: | ------: |
143
+ | Dsp.add(dsp, Dsp.create()) x 1 | 34M | 22ns | ±0.07% | 45M |
144
+ | Dsp.add(dsp, Dsp.create()) x 100 | 2M | 762ns | ±7.31% | 1M |
145
+ | Dsp.add(dsp, Dsp.create()) x 1000 | 162K | 7µs | ±2.75% | 137K |
146
+ | DisposableStack.use(new DisposableStack()) x 1 | 11M | 91ns | ±0.06% | 11M |
147
+ | DisposableStack.use(new DisposableStack()) x 100 | 151K | 7µs | ±6.90% | 142K |
148
+ | DisposableStack.use(new DisposableStack()) x 1000 | 15K | 68µs | ±0.61% | 15K |
149
+
150
+ ### bench-create
151
+
152
+ - Bench: Creates N empty disposables
153
+
154
+ | name | ops/sec | time/op | margin | samples |
155
+ | ---------------------------- | ------: | ------: | :----: | ------: |
156
+ | Dsp.create() x 1 | 36M | 21ns | ±0.08% | 48M |
157
+ | Dsp.create() x 100 | 24M | 41ns | ±0.85% | 24M |
158
+ | Dsp.create() x 1000 | 3M | 340ns | ±0.07% | 3M |
159
+ | new DisposableStack() x 1 | 22M | 51ns | ±5.95% | 20M |
160
+ | new DisposableStack() x 100 | 359K | 3µs | ±8.03% | 332K |
161
+ | new DisposableStack() x 1000 | 36K | 28µs | ±0.40% | 36K |
162
+
163
+ ### memory
164
+
165
+ Heapsize for 1M instances:
166
+
167
+ - `Dsp.create()`: 247.57 MB
168
+ - `new DisposableStack()`: 299.39 MB
169
+
170
+ ## Tree-shaking
171
+
172
+ ### Installation
173
+
174
+ ```sh [npm]
175
+ npm install -D @monstermann/unplugin-dsp
176
+ ```
177
+
178
+ ```sh [pnpm]
179
+ pnpm -D add @monstermann/unplugin-dsp
180
+ ```
181
+
182
+ ```sh [yarn]
183
+ yarn -D add @monstermann/unplugin-dsp
184
+ ```
185
+
186
+ ```sh [bun]
187
+ bun -D add @monstermann/unplugin-dsp
188
+ ```
189
+
190
+ ### Usage
191
+
192
+ ```ts [Vite]
193
+ // vite.config.ts
194
+ import dsp from "@monstermann/unplugin-dsp/vite";
195
+
196
+ export default defineConfig({
197
+ plugins: [dsp()],
198
+ });
199
+ ```
200
+
201
+ ```ts [Rollup]
202
+ // rollup.config.js
203
+ import dsp from "@monstermann/unplugin-dsp/rollup";
204
+
205
+ export default {
206
+ plugins: [dsp()],
207
+ };
208
+ ```
209
+
210
+ ```ts [Rolldown]
211
+ // rolldown.config.js
212
+ import dsp from "@monstermann/unplugin-dsp/rolldown";
213
+
214
+ export default {
215
+ plugins: [dsp()],
216
+ };
217
+ ```
218
+
219
+ ```ts [Webpack]
220
+ // webpack.config.js
221
+ const dsp = require("@monstermann/unplugin-dsp/webpack");
222
+
223
+ module.exports = {
224
+ plugins: [dsp()],
225
+ };
226
+ ```
227
+
228
+ ```ts [Rspack]
229
+ // rspack.config.js
230
+ const dsp = require("@monstermann/unplugin-dsp/rspack");
231
+
232
+ module.exports = {
233
+ plugins: [dsp()],
234
+ };
235
+ ```
236
+
237
+ ```ts [ESBuild]
238
+ // esbuild.config.js
239
+ import { build } from "esbuild";
240
+ import dsp from "@monstermann/unplugin-dsp/esbuild";
241
+
242
+ build({
243
+ plugins: [dsp()],
244
+ });
245
+ ```
246
+
247
+ ## Dsp
248
+
249
+ ### add
250
+
251
+ ```ts
252
+ function Dsp.add(disposer: Dsp, value: (() => void) | Dsp): DspLink | undefined;
253
+ ```
254
+
255
+ Takes a Dsp instance and attaches a callback or another Dsp.
256
+
257
+ Returns a data-structure that can be passed to `unlink` for fast `O(1)` removals.
258
+
259
+ Returns undefined if:
260
+
261
+ - The target Dsp has already been disposed
262
+ - The provided Dsp has already been disposed
263
+ - The target Dsp is equal to the provided Dsp
264
+
265
+ If the target Dsp is already disposed, the provided value will be immediately disposed if possible.
266
+
267
+ #### Example
268
+
269
+ ```ts
270
+ import { Dsp } from "@monstermann/dsp";
271
+
272
+ const dspA = Dsp.create();
273
+ const dspB = Dsp.create();
274
+
275
+ Dsp.add(dspA, () => console.log(1));
276
+ Dsp.add(dspB, () => console.log(2));
277
+ Dsp.add(dspA, dspB);
278
+
279
+ Dsp.dispose(dspA); // Prints: 2, 1
280
+
281
+ Dsp.add(dspA, () => console.log(3)); // Prints: 3
282
+ Dsp.add(dspB, () => console.log(4)); // Prints: 4
283
+ ```
284
+
285
+ ### create
286
+
287
+ ```ts
288
+ function Dsp.create(): Dsp;
289
+ ```
290
+
291
+ Creates a new `Dsp` instance.
292
+
293
+ #### Example
294
+
295
+ ```ts
296
+ import { Dsp } from "@monstermann/dsp";
297
+
298
+ const dsp = Dsp.create();
299
+ ```
300
+
301
+ ### Dispose
302
+
303
+ ```ts
304
+ function Dsp.dispose(disposer: Dsp): void;
305
+ ```
306
+
307
+ Takes a `Dsp` instance and disposes it, walking through all added values in reverse order (LIFO).
308
+
309
+ Disposed `Dsp`s will dereference themselves from other `Dsp`s.
310
+
311
+ Every added value is wrapped with a `try/catch` and errors are accumulated in an `AggregateError`.
312
+
313
+ #### Example
314
+
315
+ ```ts
316
+ import { Dsp } from "@monstermann/dsp";
317
+
318
+ const dspA = Dsp.create();
319
+ Dsp.add(dspA, () => console.log(1));
320
+ Dsp.add(dspA, () => console.log(2));
321
+
322
+ Dsp.dispose(dspA); // Prints: 2, 1
323
+ ```
324
+
325
+ ```ts
326
+ import { Dsp } from "@monstermann/dsp";
327
+
328
+ const dspA = Dsp.create();
329
+ const dspB = Dsp.create();
330
+
331
+ Dsp.add(dspA, () => console.log(1));
332
+ Dsp.add(dspA, () => console.log(2));
333
+ Dsp.add(dspB, () => console.log(3));
334
+ Dsp.add(dspB, () => console.log(4));
335
+ Dsp.add(dspA, dspB);
336
+
337
+ Dsp.dispose(dspA); // Prints: 4, 3, 2, 1
338
+ ```
339
+
340
+ ```ts
341
+ import { Dsp } from "@monstermann/dsp";
342
+
343
+ const dspA = Dsp.create();
344
+ const dspB = Dsp.create();
345
+
346
+ Dsp.add(dspA, () => console.log(1));
347
+ Dsp.add(dspA, () => console.log(2));
348
+ Dsp.add(dspB, () => console.log(3));
349
+ Dsp.add(dspB, () => console.log(4));
350
+ Dsp.add(dspA, dspB);
351
+
352
+ Dsp.dispose(dspB); // Prints: 4, 3
353
+ Dsp.dispose(dspA); // Prints: 2, 1
354
+ ```
355
+
356
+ ```ts
357
+ import { Dsp } from "@monstermann/dsp";
358
+
359
+ const dspA = Dsp.create();
360
+
361
+ Dsp.add(dspA, () => console.log(1));
362
+ Dsp.add(dspA, () => console.log(2));
363
+ Dsp.add(dspA, () => {
364
+ throw new Error();
365
+ });
366
+
367
+ Dsp.dispose(dspA); // Prints: 2, 1, then rethrows above error
368
+ ```
369
+
370
+ ```ts
371
+ import { Dsp } from "@monstermann/dsp";
372
+
373
+ const dspA = Dsp.create();
374
+
375
+ Dsp.add(dspA, () => console.log(1));
376
+ Dsp.add(dspA, () => console.log(2));
377
+ Dsp.add(dspA, () => {
378
+ throw new Error();
379
+ });
380
+ Dsp.add(dspA, () => {
381
+ throw new Error();
382
+ });
383
+
384
+ Dsp.dispose(dspA); // Prints: 2, 1, then throws AggregateError
385
+ ```
386
+
387
+ ### find
388
+
389
+ ```ts
390
+ function Dsp.find(disposer: Dsp, value: (() => void) | Dsp): DspLink | undefined;
391
+ ```
392
+
393
+ `O(n)`
394
+
395
+ Takes a `Dsp` instance and finds the last occurrence of `value`, the result can be passed to `unlink` to remove it.
396
+
397
+ #### Example
398
+
399
+ ```ts
400
+ import { Dsp } from "@monstermann/dsp";
401
+
402
+ const dsp = Dsp.create();
403
+
404
+ const cb1 = () => {};
405
+ const cb2 = () => {};
406
+
407
+ Dsp.add(dsp, cb1);
408
+
409
+ Dsp.includes(dsp, cb1); // true
410
+ Dsp.includes(dsp, cb2); // false
411
+
412
+ const l1 = Dsp.find(dsp, cb1); // link
413
+ const l2 = Dsp.find(dsp, cb2); // undefined
414
+
415
+ Dsp.unlink(l1);
416
+ Dsp.unlink(l2);
417
+
418
+ Dsp.includes(dsp, cb1); // false
419
+ Dsp.includes(dsp, cb2); // false
420
+ ```
421
+
422
+ ### dspIncludes
423
+
424
+ ```ts
425
+ function Dsp.includes(disposer: Dsp, value: (() => void) | Dsp): boolean;
426
+ ```
427
+
428
+ `O(n)`
429
+
430
+ Returns a boolean indicating whether the provided `Dsp` instance includes `value`.
431
+
432
+ By default `add` does not check for duplicates, you can use this or `find` if you need to make sure there are none.
433
+
434
+ #### Example
435
+
436
+ ```ts
437
+ import { Dsp } from "@monstermann/dsp";
438
+
439
+ const dsp = Dsp.create();
440
+ const cb = () => {};
441
+
442
+ Dsp.includes(dsp, cb); // false
443
+ const link = Dsp.add(dsp, cb);
444
+ Dsp.includes(dsp, cb); // true
445
+ Dsp.unlink(dsp, link);
446
+ Dsp.includes(dsp, cb); // false
447
+ ```
448
+
449
+ ### isDisposed
450
+
451
+ ```ts
452
+ function Dsp.isDisposed(disposer: Dsp): boolean;
453
+ ```
454
+
455
+ Returns a boolean indicating whether the provided `Dsp` instance has been disposed.
456
+
457
+ #### Example
458
+
459
+ ```ts
460
+ import { Dsp } from "@monstermann/dsp";
461
+
462
+ const dsp = Dsp.create();
463
+ Dsp.isDisposed(dsp); // false
464
+ Dsp.dispose(dsp);
465
+ Dsp.isDisposed(dsp); // true
466
+ ```
467
+
468
+ ### isDsp
469
+
470
+ ```ts
471
+ function Dsp.isDsp(value: unknown): value is Dsp;
472
+ ```
473
+
474
+ Checks whether the provided `value` is a `Dsp` instance.
475
+
476
+ #### Example
477
+
478
+ ```ts
479
+ import { Dsp } from "@monstermann/dsp";
480
+
481
+ const dsp = Dsp.create();
482
+ Dsp.isDsp(dsp); // true
483
+ ```
484
+
485
+ ### remove
486
+
487
+ ```ts
488
+ function Dsp.remove(disposer: Dsp, value: (() => void) | Dsp): void;
489
+ ```
490
+
491
+ `O(n)`
492
+
493
+ Takes a `Dsp` instance and removes the last occurrence of `value` from it.
494
+
495
+ If you can, use `unlink` as it is `O(1)`.
496
+
497
+ #### Example
498
+
499
+ ```ts
500
+ import { Dsp } from "@monstermann/dsp";
501
+
502
+ const dsp = Dsp.create();
503
+ const cb = () => {};
504
+ const link = Dsp.add(dsp, cb);
505
+ Dsp.includes(dsp, cb); // true
506
+ Dsp.remove(dsp, cb);
507
+ Dsp.includes(dsp, cb); // false
508
+ ```
509
+
510
+ ### unlink
511
+
512
+ ```ts
513
+ function Dsp.unlink(link: DspLink | undefined): void;
514
+ ```
515
+
516
+ `O(1)`
517
+
518
+ Takes a value constructed by `add` and destroys all references.
519
+
520
+ #### Example
521
+
522
+ ```ts
523
+ import { Dsp } from "@monstermann/dsp";
524
+
525
+ const cb = () => {};
526
+
527
+ const dsp = Dsp.create();
528
+ const link = Dsp.add(dsp, cb);
529
+
530
+ Dsp.includes(dsp, cb); // true
531
+ Dsp.unlink(link);
532
+ Dsp.includes(dsp, cb); // false
533
+ ```
@@ -1,6 +1,45 @@
1
1
  import { Dsp, DspLink } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/add.d.ts
4
+
5
+ /**
6
+ * # add
7
+ *
8
+ * ```ts
9
+ * function Dsp.add(disposer: Dsp, value: (() => void) | Dsp): DspLink | undefined;
10
+ * ```
11
+ *
12
+ * Takes a Dsp instance and attaches a callback or another Dsp.
13
+ *
14
+ * Returns a data-structure that can be passed to `unlink` for fast `O(1)` removals.
15
+ *
16
+ * Returns undefined if:
17
+ *
18
+ * - The target Dsp has already been disposed
19
+ * - The provided Dsp has already been disposed
20
+ * - The target Dsp is equal to the provided Dsp
21
+ *
22
+ * If the target Dsp is already disposed, the provided value will be immediately disposed if possible.
23
+ *
24
+ * ## Example
25
+ *
26
+ * ```ts
27
+ * import { Dsp } from "@monstermann/dsp";
28
+ *
29
+ * const dspA = Dsp.create();
30
+ * const dspB = Dsp.create();
31
+ *
32
+ * Dsp.add(dspA, () => console.log(1));
33
+ * Dsp.add(dspB, () => console.log(2));
34
+ * Dsp.add(dspA, dspB);
35
+ *
36
+ * Dsp.dispose(dspA); // Prints: 2, 1
37
+ *
38
+ * Dsp.add(dspA, () => console.log(3)); // Prints: 3
39
+ * Dsp.add(dspB, () => console.log(4)); // Prints: 4
40
+ * ```
41
+ *
42
+ */
4
43
  declare function add(target: Dsp, value: (() => void) | Dsp): DspLink | undefined;
5
44
  //#endregion
6
45
  export { add };
@@ -1,6 +1,25 @@
1
1
  import { Dsp } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/create.d.ts
4
+
5
+ /**
6
+ * # create
7
+ *
8
+ * ```ts
9
+ * function Dsp.create(): Dsp;
10
+ * ```
11
+ *
12
+ * Creates a new `Dsp` instance.
13
+ *
14
+ * ## Example
15
+ *
16
+ * ```ts
17
+ * import { Dsp } from "@monstermann/dsp";
18
+ *
19
+ * const dsp = Dsp.create();
20
+ * ```
21
+ *
22
+ */
4
23
  declare function create(): Dsp;
5
24
  //#endregion
6
25
  export { create };
@@ -1,6 +1,24 @@
1
1
  import { symbol } from "./symbol.mjs";
2
2
 
3
3
  //#region src/Dsp/create.ts
4
+ /**
5
+ * # create
6
+ *
7
+ * ```ts
8
+ * function Dsp.create(): Dsp;
9
+ * ```
10
+ *
11
+ * Creates a new `Dsp` instance.
12
+ *
13
+ * ## Example
14
+ *
15
+ * ```ts
16
+ * import { Dsp } from "@monstermann/dsp";
17
+ *
18
+ * const dsp = Dsp.create();
19
+ * ```
20
+ *
21
+ */
4
22
  function create() {
5
23
  return {
6
24
  dsps: void 0,
@@ -1,6 +1,95 @@
1
1
  import { Dsp } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/dispose.d.ts
4
+
5
+ /**
6
+ * # Dispose
7
+ *
8
+ * ```ts
9
+ * function Dsp.dispose(disposer: Dsp): void;
10
+ * ```
11
+ *
12
+ * Takes a `Dsp` instance and disposes it, walking through all added values in reverse order (LIFO).
13
+ *
14
+ * Disposed `Dsp`s will dereference themselves from other `Dsp`s.
15
+ *
16
+ * Every added value is wrapped with a `try/catch` and errors are accumulated in an `AggregateError`.
17
+ *
18
+ * ## Example
19
+ *
20
+ * ```ts
21
+ * import { Dsp } from "@monstermann/dsp";
22
+ *
23
+ * const dspA = Dsp.create();
24
+ * Dsp.add(dspA, () => console.log(1));
25
+ * Dsp.add(dspA, () => console.log(2));
26
+ *
27
+ * Dsp.dispose(dspA); // Prints: 2, 1
28
+ * ```
29
+ *
30
+ * ```ts
31
+ * import { Dsp } from "@monstermann/dsp";
32
+ *
33
+ * const dspA = Dsp.create();
34
+ * const dspB = Dsp.create();
35
+ *
36
+ * Dsp.add(dspA, () => console.log(1));
37
+ * Dsp.add(dspA, () => console.log(2));
38
+ * Dsp.add(dspB, () => console.log(3));
39
+ * Dsp.add(dspB, () => console.log(4));
40
+ * Dsp.add(dspA, dspB);
41
+ *
42
+ * Dsp.dispose(dspA); // Prints: 4, 3, 2, 1
43
+ * ```
44
+ *
45
+ * ```ts
46
+ * import { Dsp } from "@monstermann/dsp";
47
+ *
48
+ * const dspA = Dsp.create();
49
+ * const dspB = Dsp.create();
50
+ *
51
+ * Dsp.add(dspA, () => console.log(1));
52
+ * Dsp.add(dspA, () => console.log(2));
53
+ * Dsp.add(dspB, () => console.log(3));
54
+ * Dsp.add(dspB, () => console.log(4));
55
+ * Dsp.add(dspA, dspB);
56
+ *
57
+ * Dsp.dispose(dspB); // Prints: 4, 3
58
+ * Dsp.dispose(dspA); // Prints: 2, 1
59
+ * ```
60
+ *
61
+ * ```ts
62
+ * import { Dsp } from "@monstermann/dsp";
63
+ *
64
+ * const dspA = Dsp.create();
65
+ *
66
+ * Dsp.add(dspA, () => console.log(1));
67
+ * Dsp.add(dspA, () => console.log(2));
68
+ * Dsp.add(dspA, () => {
69
+ * throw new Error();
70
+ * });
71
+ *
72
+ * Dsp.dispose(dspA); // Prints: 2, 1, then rethrows above error
73
+ * ```
74
+ *
75
+ * ```ts
76
+ * import { Dsp } from "@monstermann/dsp";
77
+ *
78
+ * const dspA = Dsp.create();
79
+ *
80
+ * Dsp.add(dspA, () => console.log(1));
81
+ * Dsp.add(dspA, () => console.log(2));
82
+ * Dsp.add(dspA, () => {
83
+ * throw new Error();
84
+ * });
85
+ * Dsp.add(dspA, () => {
86
+ * throw new Error();
87
+ * });
88
+ *
89
+ * Dsp.dispose(dspA); // Prints: 2, 1, then throws AggregateError
90
+ * ```
91
+ *
92
+ */
4
93
  declare function dispose(target: Dsp): void;
5
94
  //#endregion
6
95
  export { dispose };
@@ -2,6 +2,94 @@ import { symbol } from "./symbol.mjs";
2
2
  import { unlinkVal } from "./internals.mjs";
3
3
 
4
4
  //#region src/Dsp/dispose.ts
5
+ /**
6
+ * # Dispose
7
+ *
8
+ * ```ts
9
+ * function Dsp.dispose(disposer: Dsp): void;
10
+ * ```
11
+ *
12
+ * Takes a `Dsp` instance and disposes it, walking through all added values in reverse order (LIFO).
13
+ *
14
+ * Disposed `Dsp`s will dereference themselves from other `Dsp`s.
15
+ *
16
+ * Every added value is wrapped with a `try/catch` and errors are accumulated in an `AggregateError`.
17
+ *
18
+ * ## Example
19
+ *
20
+ * ```ts
21
+ * import { Dsp } from "@monstermann/dsp";
22
+ *
23
+ * const dspA = Dsp.create();
24
+ * Dsp.add(dspA, () => console.log(1));
25
+ * Dsp.add(dspA, () => console.log(2));
26
+ *
27
+ * Dsp.dispose(dspA); // Prints: 2, 1
28
+ * ```
29
+ *
30
+ * ```ts
31
+ * import { Dsp } from "@monstermann/dsp";
32
+ *
33
+ * const dspA = Dsp.create();
34
+ * const dspB = Dsp.create();
35
+ *
36
+ * Dsp.add(dspA, () => console.log(1));
37
+ * Dsp.add(dspA, () => console.log(2));
38
+ * Dsp.add(dspB, () => console.log(3));
39
+ * Dsp.add(dspB, () => console.log(4));
40
+ * Dsp.add(dspA, dspB);
41
+ *
42
+ * Dsp.dispose(dspA); // Prints: 4, 3, 2, 1
43
+ * ```
44
+ *
45
+ * ```ts
46
+ * import { Dsp } from "@monstermann/dsp";
47
+ *
48
+ * const dspA = Dsp.create();
49
+ * const dspB = Dsp.create();
50
+ *
51
+ * Dsp.add(dspA, () => console.log(1));
52
+ * Dsp.add(dspA, () => console.log(2));
53
+ * Dsp.add(dspB, () => console.log(3));
54
+ * Dsp.add(dspB, () => console.log(4));
55
+ * Dsp.add(dspA, dspB);
56
+ *
57
+ * Dsp.dispose(dspB); // Prints: 4, 3
58
+ * Dsp.dispose(dspA); // Prints: 2, 1
59
+ * ```
60
+ *
61
+ * ```ts
62
+ * import { Dsp } from "@monstermann/dsp";
63
+ *
64
+ * const dspA = Dsp.create();
65
+ *
66
+ * Dsp.add(dspA, () => console.log(1));
67
+ * Dsp.add(dspA, () => console.log(2));
68
+ * Dsp.add(dspA, () => {
69
+ * throw new Error();
70
+ * });
71
+ *
72
+ * Dsp.dispose(dspA); // Prints: 2, 1, then rethrows above error
73
+ * ```
74
+ *
75
+ * ```ts
76
+ * import { Dsp } from "@monstermann/dsp";
77
+ *
78
+ * const dspA = Dsp.create();
79
+ *
80
+ * Dsp.add(dspA, () => console.log(1));
81
+ * Dsp.add(dspA, () => console.log(2));
82
+ * Dsp.add(dspA, () => {
83
+ * throw new Error();
84
+ * });
85
+ * Dsp.add(dspA, () => {
86
+ * throw new Error();
87
+ * });
88
+ *
89
+ * Dsp.dispose(dspA); // Prints: 2, 1, then throws AggregateError
90
+ * ```
91
+ *
92
+ */
5
93
  function dispose(target) {
6
94
  let errors;
7
95
  let stack = {
@@ -1,6 +1,44 @@
1
1
  import { Dsp, DspLink } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/find.d.ts
4
+
5
+ /**
6
+ * # find
7
+ *
8
+ * ```ts
9
+ * function Dsp.find(disposer: Dsp, value: (() => void) | Dsp): DspLink | undefined;
10
+ * ```
11
+ *
12
+ * `O(n)`
13
+ *
14
+ * Takes a `Dsp` instance and finds the last occurrence of `value`, the result can be passed to `unlink` to remove it.
15
+ *
16
+ * ## Example
17
+ *
18
+ * ```ts
19
+ * import { Dsp } from "@monstermann/dsp";
20
+ *
21
+ * const dsp = Dsp.create();
22
+ *
23
+ * const cb1 = () => {};
24
+ * const cb2 = () => {};
25
+ *
26
+ * Dsp.add(dsp, cb1);
27
+ *
28
+ * Dsp.includes(dsp, cb1); // true
29
+ * Dsp.includes(dsp, cb2); // false
30
+ *
31
+ * const l1 = Dsp.find(dsp, cb1); // link
32
+ * const l2 = Dsp.find(dsp, cb2); // undefined
33
+ *
34
+ * Dsp.unlink(l1);
35
+ * Dsp.unlink(l2);
36
+ *
37
+ * Dsp.includes(dsp, cb1); // false
38
+ * Dsp.includes(dsp, cb2); // false
39
+ * ```
40
+ *
41
+ */
4
42
  declare function find(target: Dsp, value: (() => void) | Dsp): DspLink | undefined;
5
43
  //#endregion
6
44
  export { find };
package/dist/Dsp/find.mjs CHANGED
@@ -1,4 +1,41 @@
1
1
  //#region src/Dsp/find.ts
2
+ /**
3
+ * # find
4
+ *
5
+ * ```ts
6
+ * function Dsp.find(disposer: Dsp, value: (() => void) | Dsp): DspLink | undefined;
7
+ * ```
8
+ *
9
+ * `O(n)`
10
+ *
11
+ * Takes a `Dsp` instance and finds the last occurrence of `value`, the result can be passed to `unlink` to remove it.
12
+ *
13
+ * ## Example
14
+ *
15
+ * ```ts
16
+ * import { Dsp } from "@monstermann/dsp";
17
+ *
18
+ * const dsp = Dsp.create();
19
+ *
20
+ * const cb1 = () => {};
21
+ * const cb2 = () => {};
22
+ *
23
+ * Dsp.add(dsp, cb1);
24
+ *
25
+ * Dsp.includes(dsp, cb1); // true
26
+ * Dsp.includes(dsp, cb2); // false
27
+ *
28
+ * const l1 = Dsp.find(dsp, cb1); // link
29
+ * const l2 = Dsp.find(dsp, cb2); // undefined
30
+ *
31
+ * Dsp.unlink(l1);
32
+ * Dsp.unlink(l2);
33
+ *
34
+ * Dsp.includes(dsp, cb1); // false
35
+ * Dsp.includes(dsp, cb2); // false
36
+ * ```
37
+ *
38
+ */
2
39
  function find(target, value) {
3
40
  let link = target.vals;
4
41
  while (link) {
@@ -1,6 +1,36 @@
1
1
  import { Dsp } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/includes.d.ts
4
+
5
+ /**
6
+ * # dspIncludes
7
+ *
8
+ * ```ts
9
+ * function Dsp.includes(disposer: Dsp, value: (() => void) | Dsp): boolean;
10
+ * ```
11
+ *
12
+ * `O(n)`
13
+ *
14
+ * Returns a boolean indicating whether the provided `Dsp` instance includes `value`.
15
+ *
16
+ * By default `add` does not check for duplicates, you can use this or `find` if you need to make sure there are none.
17
+ *
18
+ * ## Example
19
+ *
20
+ * ```ts
21
+ * import { Dsp } from "@monstermann/dsp";
22
+ *
23
+ * const dsp = Dsp.create();
24
+ * const cb = () => {};
25
+ *
26
+ * Dsp.includes(dsp, cb); // false
27
+ * const link = Dsp.add(dsp, cb);
28
+ * Dsp.includes(dsp, cb); // true
29
+ * Dsp.unlink(dsp, link);
30
+ * Dsp.includes(dsp, cb); // false
31
+ * ```
32
+ *
33
+ */
4
34
  declare function includes(target: Dsp, value: (() => void) | Dsp): boolean;
5
35
  //#endregion
6
36
  export { includes };
@@ -1,6 +1,35 @@
1
1
  import { find } from "./find.mjs";
2
2
 
3
3
  //#region src/Dsp/includes.ts
4
+ /**
5
+ * # dspIncludes
6
+ *
7
+ * ```ts
8
+ * function Dsp.includes(disposer: Dsp, value: (() => void) | Dsp): boolean;
9
+ * ```
10
+ *
11
+ * `O(n)`
12
+ *
13
+ * Returns a boolean indicating whether the provided `Dsp` instance includes `value`.
14
+ *
15
+ * By default `add` does not check for duplicates, you can use this or `find` if you need to make sure there are none.
16
+ *
17
+ * ## Example
18
+ *
19
+ * ```ts
20
+ * import { Dsp } from "@monstermann/dsp";
21
+ *
22
+ * const dsp = Dsp.create();
23
+ * const cb = () => {};
24
+ *
25
+ * Dsp.includes(dsp, cb); // false
26
+ * const link = Dsp.add(dsp, cb);
27
+ * Dsp.includes(dsp, cb); // true
28
+ * Dsp.unlink(dsp, link);
29
+ * Dsp.includes(dsp, cb); // false
30
+ * ```
31
+ *
32
+ */
4
33
  function includes(target, value) {
5
34
  return !!find(target, value);
6
35
  }
@@ -1,6 +1,28 @@
1
1
  import { Dsp } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/isDisposed.d.ts
4
+
5
+ /**
6
+ * # isDisposed
7
+ *
8
+ * ```ts
9
+ * function Dsp.isDisposed(disposer: Dsp): boolean;
10
+ * ```
11
+ *
12
+ * Returns a boolean indicating whether the provided `Dsp` instance has been disposed.
13
+ *
14
+ * ## Example
15
+ *
16
+ * ```ts
17
+ * import { Dsp } from "@monstermann/dsp";
18
+ *
19
+ * const dsp = Dsp.create();
20
+ * Dsp.isDisposed(dsp); // false
21
+ * Dsp.dispose(dsp);
22
+ * Dsp.isDisposed(dsp); // true
23
+ * ```
24
+ *
25
+ */
4
26
  declare function isDisposed(target: Dsp): boolean;
5
27
  //#endregion
6
28
  export { isDisposed };
@@ -1,6 +1,27 @@
1
1
  import { symbol } from "./symbol.mjs";
2
2
 
3
3
  //#region src/Dsp/isDisposed.ts
4
+ /**
5
+ * # isDisposed
6
+ *
7
+ * ```ts
8
+ * function Dsp.isDisposed(disposer: Dsp): boolean;
9
+ * ```
10
+ *
11
+ * Returns a boolean indicating whether the provided `Dsp` instance has been disposed.
12
+ *
13
+ * ## Example
14
+ *
15
+ * ```ts
16
+ * import { Dsp } from "@monstermann/dsp";
17
+ *
18
+ * const dsp = Dsp.create();
19
+ * Dsp.isDisposed(dsp); // false
20
+ * Dsp.dispose(dsp);
21
+ * Dsp.isDisposed(dsp); // true
22
+ * ```
23
+ *
24
+ */
4
25
  function isDisposed(target) {
5
26
  return target[symbol];
6
27
  }
@@ -1,6 +1,26 @@
1
1
  import { Dsp } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/isDsp.d.ts
4
+
5
+ /**
6
+ * # isDsp
7
+ *
8
+ * ```ts
9
+ * function Dsp.isDsp(value: unknown): value is Dsp;
10
+ * ```
11
+ *
12
+ * Checks whether the provided `value` is a `Dsp` instance.
13
+ *
14
+ * ## Example
15
+ *
16
+ * ```ts
17
+ * import { Dsp } from "@monstermann/dsp";
18
+ *
19
+ * const dsp = Dsp.create();
20
+ * Dsp.isDsp(dsp); // true
21
+ * ```
22
+ *
23
+ */
4
24
  declare function isDsp(value: unknown): value is Dsp;
5
25
  //#endregion
6
26
  export { isDsp };
@@ -1,6 +1,25 @@
1
1
  import { symbol } from "./symbol.mjs";
2
2
 
3
3
  //#region src/Dsp/isDsp.ts
4
+ /**
5
+ * # isDsp
6
+ *
7
+ * ```ts
8
+ * function Dsp.isDsp(value: unknown): value is Dsp;
9
+ * ```
10
+ *
11
+ * Checks whether the provided `value` is a `Dsp` instance.
12
+ *
13
+ * ## Example
14
+ *
15
+ * ```ts
16
+ * import { Dsp } from "@monstermann/dsp";
17
+ *
18
+ * const dsp = Dsp.create();
19
+ * Dsp.isDsp(dsp); // true
20
+ * ```
21
+ *
22
+ */
4
23
  function isDsp(value) {
5
24
  return value != null && typeof value === "object" && symbol in value;
6
25
  }
@@ -1,6 +1,34 @@
1
1
  import { Dsp } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/remove.d.ts
4
+
5
+ /**
6
+ * # remove
7
+ *
8
+ * ```ts
9
+ * function Dsp.remove(disposer: Dsp, value: (() => void) | Dsp): void;
10
+ * ```
11
+ *
12
+ * `O(n)`
13
+ *
14
+ * Takes a `Dsp` instance and removes the last occurrence of `value` from it.
15
+ *
16
+ * If you can, use `unlink` as it is `O(1)`.
17
+ *
18
+ * ## Example
19
+ *
20
+ * ```ts
21
+ * import { Dsp } from "@monstermann/dsp";
22
+ *
23
+ * const dsp = Dsp.create();
24
+ * const cb = () => {};
25
+ * const link = Dsp.add(dsp, cb);
26
+ * Dsp.includes(dsp, cb); // true
27
+ * Dsp.remove(dsp, cb);
28
+ * Dsp.includes(dsp, cb); // false
29
+ * ```
30
+ *
31
+ */
4
32
  declare function remove(disposer: Dsp, value: (() => void) | Dsp): void;
5
33
  //#endregion
6
34
  export { remove };
@@ -2,6 +2,33 @@ import { find } from "./find.mjs";
2
2
  import { unlink } from "./unlink.mjs";
3
3
 
4
4
  //#region src/Dsp/remove.ts
5
+ /**
6
+ * # remove
7
+ *
8
+ * ```ts
9
+ * function Dsp.remove(disposer: Dsp, value: (() => void) | Dsp): void;
10
+ * ```
11
+ *
12
+ * `O(n)`
13
+ *
14
+ * Takes a `Dsp` instance and removes the last occurrence of `value` from it.
15
+ *
16
+ * If you can, use `unlink` as it is `O(1)`.
17
+ *
18
+ * ## Example
19
+ *
20
+ * ```ts
21
+ * import { Dsp } from "@monstermann/dsp";
22
+ *
23
+ * const dsp = Dsp.create();
24
+ * const cb = () => {};
25
+ * const link = Dsp.add(dsp, cb);
26
+ * Dsp.includes(dsp, cb); // true
27
+ * Dsp.remove(dsp, cb);
28
+ * Dsp.includes(dsp, cb); // false
29
+ * ```
30
+ *
31
+ */
5
32
  function remove(disposer, value) {
6
33
  const link = find(disposer, value);
7
34
  if (link) unlink(link);
@@ -1,6 +1,34 @@
1
1
  import { DspLink } from "./index.mjs";
2
2
 
3
3
  //#region src/Dsp/unlink.d.ts
4
+
5
+ /**
6
+ * # unlink
7
+ *
8
+ * ```ts
9
+ * function Dsp.unlink(link: DspLink | undefined): void;
10
+ * ```
11
+ *
12
+ * `O(1)`
13
+ *
14
+ * Takes a value constructed by `add` and destroys all references.
15
+ *
16
+ * ## Example
17
+ *
18
+ * ```ts
19
+ * import { Dsp } from "@monstermann/dsp";
20
+ *
21
+ * const cb = () => {};
22
+ *
23
+ * const dsp = Dsp.create();
24
+ * const link = Dsp.add(dsp, cb);
25
+ *
26
+ * Dsp.includes(dsp, cb); // true
27
+ * Dsp.unlink(link);
28
+ * Dsp.includes(dsp, cb); // false
29
+ * ```
30
+ *
31
+ */
4
32
  declare function unlink(link: DspLink | undefined): void;
5
33
  //#endregion
6
34
  export { unlink };
@@ -1,6 +1,33 @@
1
1
  import { unlinkDsp, unlinkVal } from "./internals.mjs";
2
2
 
3
3
  //#region src/Dsp/unlink.ts
4
+ /**
5
+ * # unlink
6
+ *
7
+ * ```ts
8
+ * function Dsp.unlink(link: DspLink | undefined): void;
9
+ * ```
10
+ *
11
+ * `O(1)`
12
+ *
13
+ * Takes a value constructed by `add` and destroys all references.
14
+ *
15
+ * ## Example
16
+ *
17
+ * ```ts
18
+ * import { Dsp } from "@monstermann/dsp";
19
+ *
20
+ * const cb = () => {};
21
+ *
22
+ * const dsp = Dsp.create();
23
+ * const link = Dsp.add(dsp, cb);
24
+ *
25
+ * Dsp.includes(dsp, cb); // true
26
+ * Dsp.unlink(link);
27
+ * Dsp.includes(dsp, cb); // false
28
+ * ```
29
+ *
30
+ */
4
31
  function unlink(link) {
5
32
  if (!link) return;
6
33
  unlinkVal(link);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@monstermann/dsp",
3
3
  "type": "module",
4
- "version": "0.2.3",
4
+ "version": "0.3.0",
5
5
  "description": "Small & fast disposables.",
6
6
  "author": "Michael Ostermann <michaelostermann@me.com>",
7
7
  "license": "MIT",