yummies 7.19.3 → 7.20.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 (162) hide show
  1. package/assert.cjs +1 -1
  2. package/assert.cjs.map +1 -1
  3. package/assert.js +1 -1
  4. package/assert.js.map +1 -1
  5. package/async.cjs +147 -137
  6. package/async.cjs.map +1 -1
  7. package/async.d.ts +98 -131
  8. package/async.js +147 -137
  9. package/async.js.map +1 -1
  10. package/common.cjs +3 -1
  11. package/common.cjs.map +1 -1
  12. package/common.d.ts +18 -24
  13. package/common.js +3 -1
  14. package/common.js.map +1 -1
  15. package/complex.cjs +1 -0
  16. package/complex.cjs.map +1 -1
  17. package/complex.js +1 -0
  18. package/complex.js.map +1 -1
  19. package/cookie.cjs +1 -1
  20. package/cookie.cjs.map +1 -1
  21. package/cookie.d.ts +0 -18
  22. package/cookie.js +1 -1
  23. package/cookie.js.map +1 -1
  24. package/css-cx.cjs +78 -0
  25. package/css-cx.cjs.map +1 -0
  26. package/css-cx.d.ts +69 -0
  27. package/css-cx.js +74 -0
  28. package/css-cx.js.map +1 -0
  29. package/css.cjs +32 -111
  30. package/css.cjs.map +1 -1
  31. package/css.d.ts +32 -42
  32. package/css.js +30 -107
  33. package/css.js.map +1 -1
  34. package/data.cjs +60 -50
  35. package/data.cjs.map +1 -1
  36. package/data.d.ts +21 -34
  37. package/data.js +60 -50
  38. package/data.js.map +1 -1
  39. package/date-time.cjs +104 -98
  40. package/date-time.cjs.map +1 -1
  41. package/date-time.d.ts +51 -57
  42. package/date-time.js +104 -97
  43. package/date-time.js.map +1 -1
  44. package/device.cjs +14 -19
  45. package/device.cjs.map +1 -1
  46. package/device.d.ts +15 -26
  47. package/device.js +14 -19
  48. package/device.js.map +1 -1
  49. package/encodings.cjs +1 -1
  50. package/encodings.cjs.map +1 -1
  51. package/encodings.d.ts +0 -17
  52. package/encodings.js +1 -1
  53. package/encodings.js.map +1 -1
  54. package/errors.cjs +1 -18
  55. package/errors.cjs.map +1 -1
  56. package/errors.d.ts +0 -17
  57. package/errors.js +1 -17
  58. package/errors.js.map +1 -1
  59. package/file.cjs +3 -17
  60. package/file.cjs.map +1 -1
  61. package/file.d.ts +1 -16
  62. package/file.js +3 -17
  63. package/file.js.map +1 -1
  64. package/format.cjs +1 -1
  65. package/format.cjs.map +1 -1
  66. package/format.js +1 -1
  67. package/format.js.map +1 -1
  68. package/html.cjs +113 -173
  69. package/html.cjs.map +1 -1
  70. package/html.d.ts +68 -58
  71. package/html.js +112 -170
  72. package/html.js.map +1 -1
  73. package/id-nanoid.cjs +36 -0
  74. package/id-nanoid.cjs.map +1 -0
  75. package/id-nanoid.d.ts +26 -0
  76. package/id-nanoid.js +32 -0
  77. package/id-nanoid.js.map +1 -0
  78. package/id.cjs +10 -45
  79. package/id.cjs.map +1 -1
  80. package/id.d.ts +27 -36
  81. package/id.js +6 -40
  82. package/id.js.map +1 -1
  83. package/imports.cjs +3 -18
  84. package/imports.cjs.map +1 -1
  85. package/imports.d.ts +1 -16
  86. package/imports.js +3 -17
  87. package/imports.js.map +1 -1
  88. package/math.cjs +16 -12
  89. package/math.cjs.map +1 -1
  90. package/math.d.ts +9 -24
  91. package/math.js +16 -12
  92. package/math.js.map +1 -1
  93. package/media.cjs +88 -89
  94. package/media.cjs.map +1 -1
  95. package/media.d.ts +72 -80
  96. package/media.js +88 -88
  97. package/media.js.map +1 -1
  98. package/mobx.cjs +0 -1
  99. package/mobx.cjs.map +1 -1
  100. package/mobx.js.map +1 -1
  101. package/ms.cjs +3 -17
  102. package/ms.cjs.map +1 -1
  103. package/ms.d.ts +1 -16
  104. package/ms.js +3 -17
  105. package/ms.js.map +1 -1
  106. package/number.cjs +1 -17
  107. package/number.cjs.map +1 -1
  108. package/number.d.ts +0 -16
  109. package/number.js +1 -17
  110. package/number.js.map +1 -1
  111. package/package.json +6 -6
  112. package/parser.cjs +1 -1
  113. package/parser.cjs.map +1 -1
  114. package/parser.js +1 -1
  115. package/parser.js.map +1 -1
  116. package/price.cjs +1 -1
  117. package/price.cjs.map +1 -1
  118. package/price.d.ts +0 -16
  119. package/price.js +1 -1
  120. package/price.js.map +1 -1
  121. package/random.cjs +37 -39
  122. package/random.cjs.map +1 -1
  123. package/random.d.ts +31 -40
  124. package/random.js +37 -39
  125. package/random.js.map +1 -1
  126. package/react.cjs +0 -1
  127. package/react.cjs.map +1 -1
  128. package/react.js.map +1 -1
  129. package/sanitize-html.cjs +82 -0
  130. package/sanitize-html.cjs.map +1 -0
  131. package/sanitize-html.d.ts +28 -0
  132. package/sanitize-html.js +79 -0
  133. package/sanitize-html.js.map +1 -0
  134. package/sound.cjs +1 -17
  135. package/sound.cjs.map +1 -1
  136. package/sound.d.ts +0 -16
  137. package/sound.js +1 -17
  138. package/sound.js.map +1 -1
  139. package/storage.cjs +7 -5
  140. package/storage.cjs.map +1 -1
  141. package/storage.d.ts +3 -17
  142. package/storage.js +7 -5
  143. package/storage.js.map +1 -1
  144. package/text.cjs +3 -17
  145. package/text.cjs.map +1 -1
  146. package/text.d.ts +1 -16
  147. package/text.js +3 -17
  148. package/text.js.map +1 -1
  149. package/type-guard.cjs +38 -1
  150. package/type-guard.cjs.map +1 -1
  151. package/type-guard.d.ts +38 -0
  152. package/type-guard.js +38 -1
  153. package/type-guard.js.map +1 -1
  154. package/types.d.ts +10 -1
  155. package/types.global.d.ts +9 -0
  156. package/vibrate.cjs +1 -17
  157. package/vibrate.cjs.map +1 -1
  158. package/vibrate.d.ts +0 -16
  159. package/vibrate.js +1 -17
  160. package/vibrate.js.map +1 -1
  161. /package/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
  162. /package/{chunk-CZWwpsFl.cjs → chunk-DakpK96I.cjs} +0 -0
package/async.d.ts CHANGED
@@ -1,83 +1,74 @@
1
+ import { MaybeFn, Primitive, MaybePromise } from 'yummies/types';
2
+
1
3
  /**
2
- * Represents all primitive types in TypeScript.
3
- *
4
- * @returns Union of all primitive types
5
- */
6
- type Primitive = null | undefined | string | number | boolean | symbol | bigint;
7
- type Fn<T, TArgs extends any[] = any[]> = (...args: TArgs) => T;
8
- /**
9
- * Represents a type that can be either the specified type or a function returning that type.
10
- *
11
- * @template T - The type to make possibly a function
12
- * @template TArgs - Arguments type for the function
13
- * @returns T or a function that returns T
14
- */
15
- type MaybeFn<T, TArgs extends any[] = any[]> = T | Fn<T, TArgs>;
16
- /**
17
- * Represents a type that can be either the specified type or a Promise resolving to that type.
4
+ * A single interpolated segment in {@link asyncTemplate}.
18
5
  *
19
- * @template T - The type to make possibly a promise
20
- * @returns T or Promise<T>
6
+ * - **Primitives** use `String(value)` for output, except that values matching
7
+ * `value === null || value === undefined || value === false` yield no text (early return in `processTemplatePiece`).
8
+ * Other falsy values such as `0` or `''` are still stringified.
9
+ * - **Arrays** are flattened recursively in order.
10
+ * - **Promises** are awaited; the resolved value is processed the same way.
11
+ * - **Async iterables of primitives** are streamed in order; each yielded value uses the same rules
12
+ * as a standalone primitive (`String(...)`, with `null` / `undefined` / `false` omitted).
13
+ * - **Functions** (thunks): if the piece is a function, it is invoked with **no arguments** (`piece()`), and the return
14
+ * value is processed recursively—so you can defer work or return any other {@link AsyncTemplatePiece} shape
15
+ * (see {@link MaybeFn}). At runtime a thunk may also return a `Promise` of a piece; that is covered by
16
+ * {@link MaybePromise} on each template interpolation.
21
17
  */
22
- type MaybePromise<T> = T | Promise<T>;
18
+ type AsyncTemplatePiece = MaybeFn<Primitive | void | AsyncIterable<Primitive> | Promise<Primitive> | AsyncTemplatePiece[]>;
23
19
 
24
20
  /**
25
- * ---header-docs-section---
26
- * # yummies/async
21
+ * Tagged template that builds an async iterable of string chunks (like a streaming template engine).
27
22
  *
28
- * ## Description
23
+ * Static template parts are yielded as-is. Each interpolated “piece” can be a primitive, a nested array of pieces,
24
+ * a `Promise` of a piece (including a resolved primitive), an async iterable of primitives, or a
25
+ * **zero-arg function** whose result is processed the same way (see {@link AsyncTemplatePiece} / `MaybeFn`).
29
26
  *
30
- * Helpers for asynchronous control flow: delays, cancellable waits, scheduling on the next frame,
31
- * and small utilities around `requestAnimationFrame` and `queueMicrotask`. They complement native
32
- * `Promise`/`AbortSignal` patterns and keep timing logic easy to test and tree-shake per call site.
33
- * Import only what you need from `yummies/async` so bundlers can drop unused helpers.
27
+ * Interpolation handling matches `processTemplatePiece`: `null`, `undefined`, and `false` add nothing; any other value
28
+ * becomes text via `String(...)`; functions are called once with no arguments before further processing. Collect chunks
29
+ * with `for await...of` or utilities like `Array.fromAsync` where available.
34
30
  *
35
- * ## Usage
31
+ * @param strings - Cooked template segments from the tag (`strings[0]`, `strings[1]`, …).
32
+ * @param pieces - Values between segments (`pieces[i]` sits between `strings[i]` and `strings[i + 1]`).
33
+ * @returns An async generator yielding string fragments in document order.
36
34
  *
35
+ * @example
36
+ * Plain values and promises:
37
37
  * ```ts
38
- * import { sleep } from "yummies/async";
38
+ * const gen = asyncTemplate`Hello, ${'world'}! Status: ${Promise.resolve(200)}`;
39
+ * let out = '';
40
+ * for await (const chunk of gen) out += chunk;
41
+ * // out === 'Hello, world! Status: 200'
39
42
  * ```
40
- */
41
-
42
- /**
43
- * Returns a promise that resolves after `time` milliseconds.
44
- *
45
- * When `signal` is passed and becomes aborted before the delay elapses, the promise
46
- * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).
47
- * The timeout is cleared on abort so no resolve happens after cancellation.
48
- *
49
- * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).
50
- * @param signal - Optional `AbortSignal` to cancel the wait early.
51
- * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.
52
43
  *
53
44
  * @example
54
- * Basic pause in an async function:
45
+ * Falsy pieces are omitted (`null`, `undefined`, `false`); arrays flatten recursively:
55
46
  * ```ts
56
- * await sleep(250);
57
- * console.log('after 250ms');
47
+ * const gen = asyncTemplate`${null}${false}A${['B', ['C']]}`;
48
+ * const out = await Array.fromAsync(gen).then((parts) => parts.join(''));
49
+ * // out === 'ABC'
58
50
  * ```
59
51
  *
60
52
  * @example
61
- * Cancellable delay tied to component unmount or user action:
53
+ * Stream from an async iterable (e.g. chunked upstream text):
62
54
  * ```ts
63
- * const ac = new AbortController();
64
- * try {
65
- * await sleep(5000, ac.signal);
66
- * } catch (e) {
67
- * // aborted — e is signal.reason
55
+ * async function* lines() {
56
+ * yield 'line1\n';
57
+ * yield 'line2\n';
68
58
  * }
69
- * ac.abort('user cancelled');
59
+ * const gen = asyncTemplate`Header\n${lines()}Footer`;
70
60
  * ```
71
- */
72
- declare const sleep: (time?: number, signal?: AbortSignal) => Promise<void>;
73
- /**
74
- * Creates a promise that resolves after the specified number of milliseconds.
75
61
  *
76
- * @deprecated Use `sleep` instead.
77
- * @param ms Delay in milliseconds.
78
- * @returns Promise
62
+ * @example
63
+ * Lazy piece via a thunk (invoked as `piece()`):
64
+ * ```ts
65
+ * let n = 0;
66
+ * const gen = asyncTemplate`count=${() => ++n}`;
67
+ * // first consumption yields "count=1", etc.
68
+ * ```
79
69
  */
80
- declare const waitAsync: (ms?: number) => Promise<unknown>;
70
+ declare function asyncTemplate(strings: TemplateStringsArray, ...pieces: MaybePromise<AsyncTemplatePiece>[]): AsyncGenerator<string, void, unknown>;
71
+
81
72
  /**
82
73
  * Runs a loop driven by `requestAnimationFrame`: on each frame, `quitFunction` is called first.
83
74
  * If it returns a truthy value, the loop stops and no further frames are scheduled.
@@ -115,32 +106,7 @@ declare const waitAsync: (ms?: number) => Promise<unknown>;
115
106
  * ```
116
107
  */
117
108
  declare const endlessRAF: (quitFunction: () => boolean | void, asMicrotask?: boolean) => void;
118
- /**
119
- * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared
120
- * and `callback` is never run. If the callback runs normally, the abort listener is removed.
121
- *
122
- * Does nothing special if `signal` is omitted — behaves like a plain timeout.
123
- *
124
- * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).
125
- * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).
126
- * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.
127
- *
128
- * @example
129
- * ```ts
130
- * const controller = new AbortController();
131
- * setAbortableTimeout(() => console.log('done'), 500, controller.signal);
132
- * // later: controller.abort(); // timeout cancelled, log never runs
133
- * ```
134
- *
135
- * @example
136
- * Zero-delay scheduling that can still be cancelled before the macrotask runs:
137
- * ```ts
138
- * const ac = new AbortController();
139
- * setAbortableTimeout(() => startIntro(), 0, ac.signal);
140
- * // e.g. on teardown: ac.abort();
141
- * ```
142
- */
143
- declare function setAbortableTimeout(callback: VoidFunction, delayInMs?: number, signal?: AbortSignal): void;
109
+
144
110
  /**
145
111
  * Like `setInterval`, but when `signal` aborts, the interval is cleared with `clearInterval`
146
112
  * and `callback` stops being called. If `signal` is omitted, behaves like a normal interval
@@ -165,73 +131,74 @@ declare function setAbortableTimeout(callback: VoidFunction, delayInMs?: number,
165
131
  * ```
166
132
  */
167
133
  declare function setAbortableInterval(callback: VoidFunction, delayInMs?: number, signal?: AbortSignal): void;
134
+
168
135
  /**
169
- * A single interpolated segment in {@link asyncTemplate}.
170
- *
171
- * - **Primitives** use `String(value)` for output, except that values matching
172
- * `value === null || value === undefined || value === false` yield no text (early return in `processTemplatePiece`).
173
- * Other falsy values such as `0` or `''` are still stringified.
174
- * - **Arrays** are flattened recursively in order.
175
- * - **Promises** are awaited; the resolved value is processed the same way.
176
- * - **Async iterables of primitives** are streamed in order; each yielded value uses the same rules
177
- * as a standalone primitive (`String(...)`, with `null` / `undefined` / `false` omitted).
178
- * - **Functions** (thunks): if the piece is a function, it is invoked with **no arguments** (`piece()`), and the return
179
- * value is processed recursively—so you can defer work or return any other {@link AsyncTemplatePiece} shape
180
- * (see {@link MaybeFn}). At runtime a thunk may also return a `Promise` of a piece; that is covered by
181
- * {@link MaybePromise} on each template interpolation.
182
- */
183
- type AsyncTemplatePiece = MaybeFn<Primitive | void | AsyncIterable<Primitive> | Promise<Primitive> | AsyncTemplatePiece[]>;
184
- /**
185
- * Tagged template that builds an async iterable of string chunks (like a streaming template engine).
186
- *
187
- * Static template parts are yielded as-is. Each interpolated “piece” can be a primitive, a nested array of pieces,
188
- * a `Promise` of a piece (including a resolved primitive), an async iterable of primitives, or a
189
- * **zero-arg function** whose result is processed the same way (see {@link AsyncTemplatePiece} / `MaybeFn`).
136
+ * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared
137
+ * and `callback` is never run. If the callback runs normally, the abort listener is removed.
190
138
  *
191
- * Interpolation handling matches `processTemplatePiece`: `null`, `undefined`, and `false` add nothing; any other value
192
- * becomes text via `String(...)`; functions are called once with no arguments before further processing. Collect chunks
193
- * with `for await...of` or utilities like `Array.fromAsync` where available.
139
+ * Does nothing special if `signal` is omitted behaves like a plain timeout.
194
140
  *
195
- * @param strings - Cooked template segments from the tag (`strings[0]`, `strings[1]`, ).
196
- * @param pieces - Values between segments (`pieces[i]` sits between `strings[i]` and `strings[i + 1]`).
197
- * @returns An async generator yielding string fragments in document order.
141
+ * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).
142
+ * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).
143
+ * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.
198
144
  *
199
145
  * @example
200
- * Plain values and promises:
201
146
  * ```ts
202
- * const gen = asyncTemplate`Hello, ${'world'}! Status: ${Promise.resolve(200)}`;
203
- * let out = '';
204
- * for await (const chunk of gen) out += chunk;
205
- * // out === 'Hello, world! Status: 200'
147
+ * const controller = new AbortController();
148
+ * setAbortableTimeout(() => console.log('done'), 500, controller.signal);
149
+ * // later: controller.abort(); // timeout cancelled, log never runs
206
150
  * ```
207
151
  *
208
152
  * @example
209
- * Falsy pieces are omitted (`null`, `undefined`, `false`); arrays flatten recursively:
153
+ * Zero-delay scheduling that can still be cancelled before the macrotask runs:
210
154
  * ```ts
211
- * const gen = asyncTemplate`${null}${false}A${['B', ['C']]}`;
212
- * const out = await Array.fromAsync(gen).then((parts) => parts.join(''));
213
- * // out === 'ABC'
155
+ * const ac = new AbortController();
156
+ * setAbortableTimeout(() => startIntro(), 0, ac.signal);
157
+ * // e.g. on teardown: ac.abort();
214
158
  * ```
159
+ */
160
+ declare function setAbortableTimeout(callback: VoidFunction, delayInMs?: number, signal?: AbortSignal): void;
161
+
162
+ /**
163
+ * Returns a promise that resolves after `time` milliseconds.
164
+ *
165
+ * When `signal` is passed and becomes aborted before the delay elapses, the promise
166
+ * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).
167
+ * The timeout is cleared on abort so no resolve happens after cancellation.
168
+ *
169
+ * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).
170
+ * @param signal - Optional `AbortSignal` to cancel the wait early.
171
+ * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.
215
172
  *
216
173
  * @example
217
- * Stream from an async iterable (e.g. chunked upstream text):
174
+ * Basic pause in an async function:
218
175
  * ```ts
219
- * async function* lines() {
220
- * yield 'line1\n';
221
- * yield 'line2\n';
222
- * }
223
- * const gen = asyncTemplate`Header\n${lines()}Footer`;
176
+ * await sleep(250);
177
+ * console.log('after 250ms');
224
178
  * ```
225
179
  *
226
180
  * @example
227
- * Lazy piece via a thunk (invoked as `piece()`):
181
+ * Cancellable delay tied to component unmount or user action:
228
182
  * ```ts
229
- * let n = 0;
230
- * const gen = asyncTemplate`count=${() => ++n}`;
231
- * // first consumption yields "count=1", etc.
183
+ * const ac = new AbortController();
184
+ * try {
185
+ * await sleep(5000, ac.signal);
186
+ * } catch (e) {
187
+ * // aborted — e is signal.reason
188
+ * }
189
+ * ac.abort('user cancelled');
232
190
  * ```
233
191
  */
234
- declare function asyncTemplate(strings: TemplateStringsArray, ...pieces: MaybePromise<AsyncTemplatePiece>[]): AsyncGenerator<string, void, unknown>;
192
+ declare const sleep: (time?: number, signal?: AbortSignal) => Promise<void>;
193
+
194
+ /**
195
+ * Creates a promise that resolves after the specified number of milliseconds.
196
+ *
197
+ * @deprecated Use `sleep` instead.
198
+ * @param ms Delay in milliseconds.
199
+ * @returns Promise
200
+ */
201
+ declare const waitAsync: (ms?: number) => Promise<void>;
235
202
 
236
203
  export { asyncTemplate, endlessRAF, setAbortableInterval, setAbortableTimeout, sleep, waitAsync };
237
204
  export type { AsyncTemplatePiece };
package/async.js CHANGED
@@ -1,55 +1,92 @@
1
- //#region src/async.ts
1
+ //#region src/async/async-template.ts
2
2
  /**
3
- * Returns a promise that resolves after `time` milliseconds.
3
+ * Tagged template that builds an async iterable of string chunks (like a streaming template engine).
4
4
  *
5
- * When `signal` is passed and becomes aborted before the delay elapses, the promise
6
- * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).
7
- * The timeout is cleared on abort so no resolve happens after cancellation.
5
+ * Static template parts are yielded as-is. Each interpolated “piece” can be a primitive, a nested array of pieces,
6
+ * a `Promise` of a piece (including a resolved primitive), an async iterable of primitives, or a
7
+ * **zero-arg function** whose result is processed the same way (see {@link AsyncTemplatePiece} / `MaybeFn`).
8
8
  *
9
- * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).
10
- * @param signal - Optional `AbortSignal` to cancel the wait early.
11
- * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.
9
+ * Interpolation handling matches `processTemplatePiece`: `null`, `undefined`, and `false` add nothing; any other value
10
+ * becomes text via `String(...)`; functions are called once with no arguments before further processing. Collect chunks
11
+ * with `for await...of` or utilities like `Array.fromAsync` where available.
12
+ *
13
+ * @param strings - Cooked template segments from the tag (`strings[0]`, `strings[1]`, …).
14
+ * @param pieces - Values between segments (`pieces[i]` sits between `strings[i]` and `strings[i + 1]`).
15
+ * @returns An async generator yielding string fragments in document order.
12
16
  *
13
17
  * @example
14
- * Basic pause in an async function:
18
+ * Plain values and promises:
15
19
  * ```ts
16
- * await sleep(250);
17
- * console.log('after 250ms');
20
+ * const gen = asyncTemplate`Hello, ${'world'}! Status: ${Promise.resolve(200)}`;
21
+ * let out = '';
22
+ * for await (const chunk of gen) out += chunk;
23
+ * // out === 'Hello, world! Status: 200'
18
24
  * ```
19
25
  *
20
26
  * @example
21
- * Cancellable delay tied to component unmount or user action:
27
+ * Falsy pieces are omitted (`null`, `undefined`, `false`); arrays flatten recursively:
22
28
  * ```ts
23
- * const ac = new AbortController();
24
- * try {
25
- * await sleep(5000, ac.signal);
26
- * } catch (e) {
27
- * // aborted — e is signal.reason
29
+ * const gen = asyncTemplate`${null}${false}A${['B', ['C']]}`;
30
+ * const out = await Array.fromAsync(gen).then((parts) => parts.join(''));
31
+ * // out === 'ABC'
32
+ * ```
33
+ *
34
+ * @example
35
+ * Stream from an async iterable (e.g. chunked upstream text):
36
+ * ```ts
37
+ * async function* lines() {
38
+ * yield 'line1\n';
39
+ * yield 'line2\n';
28
40
  * }
29
- * ac.abort('user cancelled');
41
+ * const gen = asyncTemplate`Header\n${lines()}Footer`;
42
+ * ```
43
+ *
44
+ * @example
45
+ * Lazy piece via a thunk (invoked as `piece()`):
46
+ * ```ts
47
+ * let n = 0;
48
+ * const gen = asyncTemplate`count=${() => ++n}`;
49
+ * // first consumption yields "count=1", etc.
30
50
  * ```
31
51
  */
32
- var sleep = (time = 0, signal) => new Promise((resolve, reject) => {
33
- if (signal) {
34
- const abortListener = () => {
35
- clearTimeout(timerId);
36
- reject(signal?.reason);
37
- };
38
- const timerId = setTimeout(() => {
39
- signal.removeEventListener("abort", abortListener);
40
- resolve();
41
- }, time);
42
- signal.addEventListener("abort", abortListener, { once: true });
43
- } else setTimeout(resolve, time);
44
- });
52
+ async function* asyncTemplate(strings, ...pieces) {
53
+ for (let i = 0; i < strings.length; i++) {
54
+ yield strings[i];
55
+ if (i < pieces.length) {
56
+ const piece = pieces[i];
57
+ yield* processTemplatePiece(piece);
58
+ }
59
+ }
60
+ }
45
61
  /**
46
- * Creates a promise that resolves after the specified number of milliseconds.
62
+ * Resolves a template piece into yielded string chunks.
47
63
  *
48
- * @deprecated Use `sleep` instead.
49
- * @param ms Delay in milliseconds.
50
- * @returns Promise
64
+ * Skips output when `value === null || value === undefined || value === false`.
65
+ * Otherwise awaits promises, flattens arrays, streams async iterables, invokes **functions** with no arguments and
66
+ * recurses on the return value, and uses `String(value)` for primitives.
51
67
  */
52
- var waitAsync = async (ms = 1e3) => new Promise((resolve) => setTimeout(resolve, ms));
68
+ async function* processTemplatePiece(piece) {
69
+ if (piece === null || piece === void 0 || piece === false) return;
70
+ if (piece instanceof Promise) {
71
+ yield* processTemplatePiece(await piece);
72
+ return;
73
+ }
74
+ if (Array.isArray(piece)) {
75
+ for (const item of piece) yield* processTemplatePiece(item);
76
+ return;
77
+ }
78
+ if (typeof piece === "object" && piece !== null && typeof piece[Symbol.asyncIterator] === "function") {
79
+ for await (const item of piece) yield* processTemplatePiece(item);
80
+ return;
81
+ }
82
+ if (typeof piece === "function") {
83
+ yield* processTemplatePiece(piece());
84
+ return;
85
+ }
86
+ yield String(piece);
87
+ }
88
+ //#endregion
89
+ //#region src/async/endless-raf.ts
53
90
  /**
54
91
  * Runs a loop driven by `requestAnimationFrame`: on each frame, `quitFunction` is called first.
55
92
  * If it returns a truthy value, the loop stops and no further frames are scheduled.
@@ -92,44 +129,8 @@ var endlessRAF = (quitFunction, asMicrotask) => {
92
129
  if (asMicrotask) queueMicrotask(raf);
93
130
  else raf();
94
131
  };
95
- /**
96
- * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared
97
- * and `callback` is never run. If the callback runs normally, the abort listener is removed.
98
- *
99
- * Does nothing special if `signal` is omitted — behaves like a plain timeout.
100
- *
101
- * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).
102
- * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).
103
- * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.
104
- *
105
- * @example
106
- * ```ts
107
- * const controller = new AbortController();
108
- * setAbortableTimeout(() => console.log('done'), 500, controller.signal);
109
- * // later: controller.abort(); // timeout cancelled, log never runs
110
- * ```
111
- *
112
- * @example
113
- * Zero-delay scheduling that can still be cancelled before the macrotask runs:
114
- * ```ts
115
- * const ac = new AbortController();
116
- * setAbortableTimeout(() => startIntro(), 0, ac.signal);
117
- * // e.g. on teardown: ac.abort();
118
- * ```
119
- */
120
- function setAbortableTimeout(callback, delayInMs, signal) {
121
- let internalTimer = null;
122
- const handleAbort = () => {
123
- if (internalTimer == null) return;
124
- clearTimeout(internalTimer);
125
- internalTimer = null;
126
- };
127
- signal?.addEventListener("abort", handleAbort, { once: true });
128
- internalTimer = setTimeout(() => {
129
- signal?.removeEventListener("abort", handleAbort);
130
- callback();
131
- }, delayInMs);
132
- }
132
+ //#endregion
133
+ //#region src/async/set-abortable-interval.ts
133
134
  /**
134
135
  * Like `setInterval`, but when `signal` aborts, the interval is cleared with `clearInterval`
135
136
  * and `callback` stops being called. If `signal` is omitted, behaves like a normal interval
@@ -163,92 +164,101 @@ function setAbortableInterval(callback, delayInMs, signal) {
163
164
  signal?.addEventListener("abort", handleAbort, { once: true });
164
165
  timer = setInterval(callback, delayInMs);
165
166
  }
167
+ //#endregion
168
+ //#region src/async/set-abortable-timeout.ts
166
169
  /**
167
- * Tagged template that builds an async iterable of string chunks (like a streaming template engine).
168
- *
169
- * Static template parts are yielded as-is. Each interpolated “piece” can be a primitive, a nested array of pieces,
170
- * a `Promise` of a piece (including a resolved primitive), an async iterable of primitives, or a
171
- * **zero-arg function** whose result is processed the same way (see {@link AsyncTemplatePiece} / `MaybeFn`).
170
+ * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared
171
+ * and `callback` is never run. If the callback runs normally, the abort listener is removed.
172
172
  *
173
- * Interpolation handling matches `processTemplatePiece`: `null`, `undefined`, and `false` add nothing; any other value
174
- * becomes text via `String(...)`; functions are called once with no arguments before further processing. Collect chunks
175
- * with `for await...of` or utilities like `Array.fromAsync` where available.
173
+ * Does nothing special if `signal` is omitted behaves like a plain timeout.
176
174
  *
177
- * @param strings - Cooked template segments from the tag (`strings[0]`, `strings[1]`, ).
178
- * @param pieces - Values between segments (`pieces[i]` sits between `strings[i]` and `strings[i + 1]`).
179
- * @returns An async generator yielding string fragments in document order.
175
+ * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).
176
+ * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).
177
+ * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.
180
178
  *
181
179
  * @example
182
- * Plain values and promises:
183
180
  * ```ts
184
- * const gen = asyncTemplate`Hello, ${'world'}! Status: ${Promise.resolve(200)}`;
185
- * let out = '';
186
- * for await (const chunk of gen) out += chunk;
187
- * // out === 'Hello, world! Status: 200'
181
+ * const controller = new AbortController();
182
+ * setAbortableTimeout(() => console.log('done'), 500, controller.signal);
183
+ * // later: controller.abort(); // timeout cancelled, log never runs
188
184
  * ```
189
185
  *
190
186
  * @example
191
- * Falsy pieces are omitted (`null`, `undefined`, `false`); arrays flatten recursively:
187
+ * Zero-delay scheduling that can still be cancelled before the macrotask runs:
192
188
  * ```ts
193
- * const gen = asyncTemplate`${null}${false}A${['B', ['C']]}`;
194
- * const out = await Array.fromAsync(gen).then((parts) => parts.join(''));
195
- * // out === 'ABC'
189
+ * const ac = new AbortController();
190
+ * setAbortableTimeout(() => startIntro(), 0, ac.signal);
191
+ * // e.g. on teardown: ac.abort();
196
192
  * ```
193
+ */
194
+ function setAbortableTimeout(callback, delayInMs, signal) {
195
+ let internalTimer = null;
196
+ const handleAbort = () => {
197
+ if (internalTimer == null) return;
198
+ clearTimeout(internalTimer);
199
+ internalTimer = null;
200
+ };
201
+ signal?.addEventListener("abort", handleAbort, { once: true });
202
+ internalTimer = setTimeout(() => {
203
+ signal?.removeEventListener("abort", handleAbort);
204
+ callback();
205
+ }, delayInMs);
206
+ }
207
+ //#endregion
208
+ //#region src/async/sleep.ts
209
+ /**
210
+ * Returns a promise that resolves after `time` milliseconds.
211
+ *
212
+ * When `signal` is passed and becomes aborted before the delay elapses, the promise
213
+ * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).
214
+ * The timeout is cleared on abort so no resolve happens after cancellation.
215
+ *
216
+ * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).
217
+ * @param signal - Optional `AbortSignal` to cancel the wait early.
218
+ * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.
197
219
  *
198
220
  * @example
199
- * Stream from an async iterable (e.g. chunked upstream text):
221
+ * Basic pause in an async function:
200
222
  * ```ts
201
- * async function* lines() {
202
- * yield 'line1\n';
203
- * yield 'line2\n';
204
- * }
205
- * const gen = asyncTemplate`Header\n${lines()}Footer`;
223
+ * await sleep(250);
224
+ * console.log('after 250ms');
206
225
  * ```
207
226
  *
208
227
  * @example
209
- * Lazy piece via a thunk (invoked as `piece()`):
228
+ * Cancellable delay tied to component unmount or user action:
210
229
  * ```ts
211
- * let n = 0;
212
- * const gen = asyncTemplate`count=${() => ++n}`;
213
- * // first consumption yields "count=1", etc.
230
+ * const ac = new AbortController();
231
+ * try {
232
+ * await sleep(5000, ac.signal);
233
+ * } catch (e) {
234
+ * // aborted — e is signal.reason
235
+ * }
236
+ * ac.abort('user cancelled');
214
237
  * ```
215
238
  */
216
- async function* asyncTemplate(strings, ...pieces) {
217
- for (let i = 0; i < strings.length; i++) {
218
- yield strings[i];
219
- if (i < pieces.length) {
220
- const piece = pieces[i];
221
- yield* processTemplatePiece(piece);
222
- }
223
- }
224
- }
239
+ var sleep = (time = 0, signal) => new Promise((resolve, reject) => {
240
+ if (signal) {
241
+ const abortListener = () => {
242
+ clearTimeout(timerId);
243
+ reject(signal?.reason);
244
+ };
245
+ const timerId = setTimeout(() => {
246
+ signal.removeEventListener("abort", abortListener);
247
+ resolve();
248
+ }, time);
249
+ signal.addEventListener("abort", abortListener, { once: true });
250
+ } else setTimeout(resolve, time);
251
+ });
252
+ //#endregion
253
+ //#region src/async/wait-async.ts
225
254
  /**
226
- * Resolves a template piece into yielded string chunks.
255
+ * Creates a promise that resolves after the specified number of milliseconds.
227
256
  *
228
- * Skips output when `value === null || value === undefined || value === false`.
229
- * Otherwise awaits promises, flattens arrays, streams async iterables, invokes **functions** with no arguments and
230
- * recurses on the return value, and uses `String(value)` for primitives.
257
+ * @deprecated Use `sleep` instead.
258
+ * @param ms Delay in milliseconds.
259
+ * @returns Promise
231
260
  */
232
- async function* processTemplatePiece(piece) {
233
- if (piece === null || piece === void 0 || piece === false) return;
234
- if (piece instanceof Promise) {
235
- yield* processTemplatePiece(await piece);
236
- return;
237
- }
238
- if (Array.isArray(piece)) {
239
- for (const item of piece) yield* processTemplatePiece(item);
240
- return;
241
- }
242
- if (typeof piece === "object" && piece !== null && typeof piece[Symbol.asyncIterator] === "function") {
243
- for await (const item of piece) yield* processTemplatePiece(item);
244
- return;
245
- }
246
- if (typeof piece === "function") {
247
- yield* processTemplatePiece(piece());
248
- return;
249
- }
250
- yield String(piece);
251
- }
261
+ var waitAsync = (ms = 1e3) => sleep(ms);
252
262
  //#endregion
253
263
  export { asyncTemplate, endlessRAF, setAbortableInterval, setAbortableTimeout, sleep, waitAsync };
254
264