yummies 7.12.0 → 7.13.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 +5 -87
- package/async.cjs +17 -0
- package/async.cjs.map +1 -1
- package/async.d.ts +17 -0
- package/async.js +17 -0
- package/async.js.map +1 -1
- package/common.cjs.map +1 -1
- package/common.d.ts +18 -0
- package/common.js.map +1 -1
- package/complex.cjs.map +1 -1
- package/complex.d.ts +66 -0
- package/complex.js.map +1 -1
- package/cookie.cjs.map +1 -1
- package/cookie.d.ts +18 -0
- package/cookie.js.map +1 -1
- package/css.cjs +16 -0
- package/css.cjs.map +1 -1
- package/css.d.ts +17 -0
- package/css.js +16 -0
- package/css.js.map +1 -1
- package/data.cjs.map +1 -1
- package/data.d.ts +18 -0
- package/data.js.map +1 -1
- package/date-time.cjs +16 -0
- package/date-time.cjs.map +1 -1
- package/date-time.d.ts +17 -0
- package/date-time.js +16 -0
- package/date-time.js.map +1 -1
- package/device.cjs +17 -0
- package/device.cjs.map +1 -1
- package/device.d.ts +17 -0
- package/device.js +17 -0
- package/device.js.map +1 -1
- package/encodings.cjs.map +1 -1
- package/encodings.d.ts +17 -0
- package/encodings.js.map +1 -1
- package/errors.cjs +16 -0
- package/errors.cjs.map +1 -1
- package/errors.d.ts +17 -0
- package/errors.js +16 -0
- package/errors.js.map +1 -1
- package/file.cjs +16 -0
- package/file.cjs.map +1 -1
- package/file.d.ts +16 -0
- package/file.js +16 -0
- package/file.js.map +1 -1
- package/format.cjs.map +1 -1
- package/format.d.ts +18 -0
- package/format.js.map +1 -1
- package/html.cjs +16 -0
- package/html.cjs.map +1 -1
- package/html.d.ts +17 -0
- package/html.js +16 -0
- package/html.js.map +1 -1
- package/id.cjs +16 -0
- package/id.cjs.map +1 -1
- package/id.d.ts +16 -0
- package/id.js +16 -0
- package/id.js.map +1 -1
- package/imports.cjs +16 -0
- package/imports.cjs.map +1 -1
- package/imports.d.ts +16 -0
- package/imports.js +16 -0
- package/imports.js.map +1 -1
- package/math.cjs.map +1 -1
- package/math.d.ts +17 -0
- package/math.js.map +1 -1
- package/media.cjs +16 -0
- package/media.cjs.map +1 -1
- package/media.d.ts +16 -0
- package/media.js +16 -0
- package/media.js.map +1 -1
- package/mobx.cjs +96 -0
- package/mobx.cjs.map +1 -1
- package/mobx.d.ts +101 -0
- package/mobx.js +96 -0
- package/mobx.js.map +1 -1
- package/ms.cjs +16 -0
- package/ms.cjs.map +1 -1
- package/ms.d.ts +16 -0
- package/ms.js +16 -0
- package/ms.js.map +1 -1
- package/number.cjs +16 -0
- package/number.cjs.map +1 -1
- package/number.d.ts +16 -0
- package/number.js +16 -0
- package/number.js.map +1 -1
- package/package.json +2 -2
- package/parser.cjs.map +1 -1
- package/parser.d.ts +17 -0
- package/parser.js.map +1 -1
- package/price.cjs.map +1 -1
- package/price.d.ts +16 -0
- package/price.js.map +1 -1
- package/random.cjs +16 -0
- package/random.cjs.map +1 -1
- package/random.d.ts +16 -0
- package/random.js +16 -0
- package/random.js.map +1 -1
- package/sound.cjs +16 -0
- package/sound.cjs.map +1 -1
- package/sound.d.ts +16 -0
- package/sound.js +16 -0
- package/sound.js.map +1 -1
- package/storage.cjs.map +1 -1
- package/storage.d.ts +16 -0
- package/storage.js.map +1 -1
- package/text.cjs +16 -0
- package/text.cjs.map +1 -1
- package/text.d.ts +16 -0
- package/text.js +16 -0
- package/text.js.map +1 -1
- package/type-guard.cjs.map +1 -1
- package/type-guard.d.ts +18 -0
- package/type-guard.js.map +1 -1
- package/types.d.ts +41 -0
- package/types.global.d.ts +41 -0
- package/vibrate.cjs +16 -0
- package/vibrate.cjs.map +1 -1
- package/vibrate.d.ts +16 -0
- package/vibrate.js +16 -0
- package/vibrate.js.map +1 -1
package/README.md
CHANGED
|
@@ -15,94 +15,12 @@
|
|
|
15
15
|
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/yummies
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## [yummies/common](src/common.ts)
|
|
24
|
-
All other utilities without groupping
|
|
25
|
-
|
|
26
|
-
## [yummies/cookie](src/cookie.ts)
|
|
27
|
-
Utilities for working with cookies
|
|
28
|
-
|
|
29
|
-
## [yummies/css](src/css.ts)
|
|
30
|
-
Utilities for working with CSS
|
|
31
|
-
|
|
32
|
-
## [yummies/date-time](src/date-time.ts)
|
|
33
|
-
Utilities for working with dates and times (based on dayjs)
|
|
34
|
-
|
|
35
|
-
## [yummies/device](src/device.ts)
|
|
36
|
-
Utilities for working with devices
|
|
37
|
-
|
|
38
|
-
## [yummies/html](src/html.ts)
|
|
39
|
-
Utilities for working with HTML
|
|
40
|
-
|
|
41
|
-
## [yummies/id](src/id.ts)
|
|
42
|
-
Utilities for working with identifiers
|
|
43
|
-
|
|
44
|
-
## [yummies/imports](src/imports.ts)
|
|
45
|
-
Utilities for working with module imports
|
|
46
|
-
|
|
47
|
-
## [yummies/math](src/math.ts)
|
|
48
|
-
Utilities for working with devices
|
|
49
|
-
|
|
50
|
-
## [yummies/media](src/media.ts)
|
|
51
|
-
Utilities for working with media (image, canvas and blob)
|
|
52
|
-
|
|
53
|
-
## [yummies/ms](src/ms.ts)
|
|
54
|
-
Utilities for working with milliseconds
|
|
55
|
-
|
|
56
|
-
## [yummies/price](src/price.ts)
|
|
57
|
-
Utilities for working with monetary values (formatting)
|
|
58
|
-
|
|
59
|
-
## [yummies/sound](src/sound.ts)
|
|
60
|
-
Utilities for working with sound
|
|
61
|
-
|
|
62
|
-
## [yummies/storage](src/storage.ts)
|
|
63
|
-
Utilities for working with storage (localStorage, sessionStorage)
|
|
64
|
-
|
|
65
|
-
## [yummies/text](src/text.ts)
|
|
66
|
-
Utilities for working with text
|
|
67
|
-
|
|
68
|
-
## [yummies/type-guard](src/type-guard.ts)
|
|
69
|
-
Utility for type checks
|
|
70
|
-
|
|
71
|
-
## [yummies/vibrate](src/vibrate.ts)
|
|
72
|
-
Utilities for working with vibrate api
|
|
73
|
-
|
|
74
|
-
## [yummies/types.global](src/types.ts)
|
|
75
|
-
## [yummies/types](src/types.ts)
|
|
76
|
-
TypeScript utility types that simplify writing TypeScript code.
|
|
77
|
-
They can be imported globally using the `d.ts` file, embedding it in the environment
|
|
78
|
-
```ts
|
|
79
|
-
import 'yummies/types.global';
|
|
80
|
-
```
|
|
81
|
-
Or specified in `tsconfig.json` in the `"types"` field
|
|
82
|
-
```json
|
|
83
|
-
{
|
|
84
|
-
"compilerOptions": {
|
|
85
|
-
"types": [
|
|
86
|
-
"yummies/types.global"
|
|
87
|
-
],
|
|
88
|
-
"target": "...blabla",
|
|
89
|
-
...
|
|
90
|
-
}
|
|
91
|
-
...
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
Alternatively, you can use the "library" approach, where you need exported types.
|
|
95
|
-
For this, you can use the `yummies` or `yummies/types` import.
|
|
96
|
-
|
|
97
|
-
```ts
|
|
98
|
-
import { AnyObject } from 'yummies';
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
## [yummies/complex](src/complex/index.ts)
|
|
103
|
-
|
|
104
|
-
Additional set of complex utilities
|
|
18
|
+
`yummies` - a set of various utilities for JavaScript projects with open source code,
|
|
19
|
+
designed to simplify the execution of common tasks and increase performance.
|
|
20
|
+
This project provides developers with powerful and easy-to-use functions
|
|
21
|
+
that can be easily integrated into any JavaScript code.
|
|
105
22
|
|
|
23
|
+
## [Read the docs →](https://js2me.github.io/yummies/)
|
|
106
24
|
|
|
107
25
|
## Migration from 5.x to 6.x
|
|
108
26
|
|
package/async.cjs
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
//#region src/async.ts
|
|
3
3
|
/**
|
|
4
|
+
* ---header-docs-section---
|
|
5
|
+
* # yummies/async
|
|
6
|
+
*
|
|
7
|
+
* ## Description
|
|
8
|
+
*
|
|
9
|
+
* Helpers for asynchronous control flow: delays, cancellable waits, scheduling on the next frame,
|
|
10
|
+
* and small utilities around `requestAnimationFrame` and `queueMicrotask`. They complement native
|
|
11
|
+
* `Promise`/`AbortSignal` patterns and keep timing logic easy to test and tree-shake per call site.
|
|
12
|
+
* Import only what you need from `yummies/async` so bundlers can drop unused helpers.
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { sleep } from "yummies/async";
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
4
21
|
* Returns a promise that resolves after `time` milliseconds.
|
|
5
22
|
*
|
|
6
23
|
* When `signal` is passed and becomes aborted before the delay elapses, the promise
|
package/async.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.cjs","names":[],"sources":["../src/async.ts"],"sourcesContent":["/**\n * Returns a promise that resolves after `time` milliseconds.\n *\n * When `signal` is passed and becomes aborted before the delay elapses, the promise\n * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).\n * The timeout is cleared on abort so no resolve happens after cancellation.\n *\n * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).\n * @param signal - Optional `AbortSignal` to cancel the wait early.\n * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.\n *\n * @example\n * Basic pause in an async function:\n * ```ts\n * await sleep(250);\n * console.log('after 250ms');\n * ```\n *\n * @example\n * Cancellable delay tied to component unmount or user action:\n * ```ts\n * const ac = new AbortController();\n * try {\n * await sleep(5000, ac.signal);\n * } catch (e) {\n * // aborted — e is signal.reason\n * }\n * ac.abort('user cancelled');\n * ```\n */\nexport const sleep = (time: number = 0, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n if (signal) {\n const abortListener = () => {\n clearTimeout(timerId);\n reject(signal?.reason);\n };\n const timerId = setTimeout(() => {\n signal.removeEventListener('abort', abortListener);\n resolve();\n }, time);\n signal.addEventListener('abort', abortListener, { once: true });\n } else {\n setTimeout(resolve, time);\n }\n });\n\n/**\n * Creates a promise that resolves after the specified number of milliseconds.\n *\n * @deprecated Use `sleep` instead.\n * @param ms Delay in milliseconds.\n * @returns Promise\n */\nexport const waitAsync = async (ms = 1000) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Runs a loop driven by `requestAnimationFrame`: on each frame, `quitFunction` is called first.\n * If it returns a truthy value, the loop stops and no further frames are scheduled.\n * If it returns falsy or nothing, the next frame is scheduled recursively.\n *\n * Use this for per-frame work (animations, layout reads after paint) without managing\n * `cancelAnimationFrame` manually — returning `true` from `quitFunction` is the exit condition.\n *\n * When `asMicrotask` is `true`, scheduling the next RAF is deferred with `queueMicrotask`\n * so other microtasks can run before the frame is requested (useful when you need to\n * batch DOM updates or let reactive frameworks flush first).\n *\n * @param quitFunction - Invoked each animation frame. Return `true` to stop the loop.\n * @param asMicrotask - If `true`, wrap the `requestAnimationFrame` call in `queueMicrotask`.\n *\n * @example\n * Stop after 60 frames (~1s at 60Hz):\n * ```ts\n * let frames = 0;\n * endlessRAF(() => {\n * frames++;\n * updateSomething(frames);\n * return frames >= 60;\n * });\n * ```\n *\n * @example\n * Run until an element is removed or a flag is set:\n * ```ts\n * let running = true;\n * endlessRAF(() => {\n * if (!running || !document.body.contains(el)) return true;\n * draw(el);\n * }, true);\n * ```\n */\nexport const endlessRAF = (\n quitFunction: () => boolean | void,\n asMicrotask?: boolean,\n) => {\n if (quitFunction()) return;\n\n const raf = () =>\n requestAnimationFrame(() => endlessRAF(quitFunction, asMicrotask));\n\n if (asMicrotask) {\n queueMicrotask(raf);\n } else {\n raf();\n }\n};\n\n/**\n * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared\n * and `callback` is never run. If the callback runs normally, the abort listener is removed.\n *\n * Does nothing special if `signal` is omitted — behaves like a plain timeout.\n *\n * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).\n * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).\n * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableTimeout(() => console.log('done'), 500, controller.signal);\n * // later: controller.abort(); // timeout cancelled, log never runs\n * ```\n *\n * @example\n * Zero-delay scheduling that can still be cancelled before the macrotask runs:\n * ```ts\n * const ac = new AbortController();\n * setAbortableTimeout(() => startIntro(), 0, ac.signal);\n * // e.g. on teardown: ac.abort();\n * ```\n */\nexport function setAbortableTimeout(\n callback: VoidFunction,\n delayInMs?: number,\n signal?: AbortSignal,\n) {\n let internalTimer: number | null = null;\n\n const handleAbort = () => {\n if (internalTimer == null) {\n return;\n }\n clearTimeout(internalTimer);\n internalTimer = null;\n };\n\n signal?.addEventListener('abort', handleAbort, { once: true });\n\n internalTimer = setTimeout(() => {\n signal?.removeEventListener('abort', handleAbort);\n callback();\n }, delayInMs);\n}\n\n/**\n * Like `setInterval`, but when `signal` aborts, the interval is cleared with `clearInterval`\n * and `callback` stops being called. If `signal` is omitted, behaves like a normal interval\n * (you must clear it yourself).\n *\n * @param callback - Invoked every `delayInMs` milliseconds until aborted or cleared.\n * @param delayInMs - Interval period in milliseconds (same as `setInterval`).\n * @param signal - Aborting stops the interval and removes the abort listener path from keeping work alive.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableInterval(() => console.log('tick'), 1000, controller.signal);\n * // stop: controller.abort();\n * ```\n *\n * @example\n * ```ts\n * const ac = new AbortController();\n * setAbortableInterval(syncStatus, 30_000, ac.signal);\n * window.addEventListener('beforeunload', () => ac.abort());\n * ```\n */\nexport function setAbortableInterval(\n callback: VoidFunction,\n delayInMs?: number,\n signal?: AbortSignal,\n) {\n let timer: number | null = null;\n\n const handleAbort = () => {\n if (timer == null) {\n return;\n }\n clearInterval(timer);\n timer = null;\n };\n\n signal?.addEventListener('abort', handleAbort, { once: true });\n\n timer = setInterval(callback, delayInMs);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"async.cjs","names":[],"sources":["../src/async.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/async\n *\n * ## Description\n *\n * Helpers for asynchronous control flow: delays, cancellable waits, scheduling on the next frame,\n * and small utilities around `requestAnimationFrame` and `queueMicrotask`. They complement native\n * `Promise`/`AbortSignal` patterns and keep timing logic easy to test and tree-shake per call site.\n * Import only what you need from `yummies/async` so bundlers can drop unused helpers.\n *\n * ## Usage\n *\n * ```ts\n * import { sleep } from \"yummies/async\";\n * ```\n */\n\n/**\n * Returns a promise that resolves after `time` milliseconds.\n *\n * When `signal` is passed and becomes aborted before the delay elapses, the promise\n * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).\n * The timeout is cleared on abort so no resolve happens after cancellation.\n *\n * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).\n * @param signal - Optional `AbortSignal` to cancel the wait early.\n * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.\n *\n * @example\n * Basic pause in an async function:\n * ```ts\n * await sleep(250);\n * console.log('after 250ms');\n * ```\n *\n * @example\n * Cancellable delay tied to component unmount or user action:\n * ```ts\n * const ac = new AbortController();\n * try {\n * await sleep(5000, ac.signal);\n * } catch (e) {\n * // aborted — e is signal.reason\n * }\n * ac.abort('user cancelled');\n * ```\n */\nexport const sleep = (time: number = 0, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n if (signal) {\n const abortListener = () => {\n clearTimeout(timerId);\n reject(signal?.reason);\n };\n const timerId = setTimeout(() => {\n signal.removeEventListener('abort', abortListener);\n resolve();\n }, time);\n signal.addEventListener('abort', abortListener, { once: true });\n } else {\n setTimeout(resolve, time);\n }\n });\n\n/**\n * Creates a promise that resolves after the specified number of milliseconds.\n *\n * @deprecated Use `sleep` instead.\n * @param ms Delay in milliseconds.\n * @returns Promise\n */\nexport const waitAsync = async (ms = 1000) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Runs a loop driven by `requestAnimationFrame`: on each frame, `quitFunction` is called first.\n * If it returns a truthy value, the loop stops and no further frames are scheduled.\n * If it returns falsy or nothing, the next frame is scheduled recursively.\n *\n * Use this for per-frame work (animations, layout reads after paint) without managing\n * `cancelAnimationFrame` manually — returning `true` from `quitFunction` is the exit condition.\n *\n * When `asMicrotask` is `true`, scheduling the next RAF is deferred with `queueMicrotask`\n * so other microtasks can run before the frame is requested (useful when you need to\n * batch DOM updates or let reactive frameworks flush first).\n *\n * @param quitFunction - Invoked each animation frame. Return `true` to stop the loop.\n * @param asMicrotask - If `true`, wrap the `requestAnimationFrame` call in `queueMicrotask`.\n *\n * @example\n * Stop after 60 frames (~1s at 60Hz):\n * ```ts\n * let frames = 0;\n * endlessRAF(() => {\n * frames++;\n * updateSomething(frames);\n * return frames >= 60;\n * });\n * ```\n *\n * @example\n * Run until an element is removed or a flag is set:\n * ```ts\n * let running = true;\n * endlessRAF(() => {\n * if (!running || !document.body.contains(el)) return true;\n * draw(el);\n * }, true);\n * ```\n */\nexport const endlessRAF = (\n quitFunction: () => boolean | void,\n asMicrotask?: boolean,\n) => {\n if (quitFunction()) return;\n\n const raf = () =>\n requestAnimationFrame(() => endlessRAF(quitFunction, asMicrotask));\n\n if (asMicrotask) {\n queueMicrotask(raf);\n } else {\n raf();\n }\n};\n\n/**\n * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared\n * and `callback` is never run. If the callback runs normally, the abort listener is removed.\n *\n * Does nothing special if `signal` is omitted — behaves like a plain timeout.\n *\n * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).\n * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).\n * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableTimeout(() => console.log('done'), 500, controller.signal);\n * // later: controller.abort(); // timeout cancelled, log never runs\n * ```\n *\n * @example\n * Zero-delay scheduling that can still be cancelled before the macrotask runs:\n * ```ts\n * const ac = new AbortController();\n * setAbortableTimeout(() => startIntro(), 0, ac.signal);\n * // e.g. on teardown: ac.abort();\n * ```\n */\nexport function setAbortableTimeout(\n callback: VoidFunction,\n delayInMs?: number,\n signal?: AbortSignal,\n) {\n let internalTimer: number | null = null;\n\n const handleAbort = () => {\n if (internalTimer == null) {\n return;\n }\n clearTimeout(internalTimer);\n internalTimer = null;\n };\n\n signal?.addEventListener('abort', handleAbort, { once: true });\n\n internalTimer = setTimeout(() => {\n signal?.removeEventListener('abort', handleAbort);\n callback();\n }, delayInMs);\n}\n\n/**\n * Like `setInterval`, but when `signal` aborts, the interval is cleared with `clearInterval`\n * and `callback` stops being called. If `signal` is omitted, behaves like a normal interval\n * (you must clear it yourself).\n *\n * @param callback - Invoked every `delayInMs` milliseconds until aborted or cleared.\n * @param delayInMs - Interval period in milliseconds (same as `setInterval`).\n * @param signal - Aborting stops the interval and removes the abort listener path from keeping work alive.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableInterval(() => console.log('tick'), 1000, controller.signal);\n * // stop: controller.abort();\n * ```\n *\n * @example\n * ```ts\n * const ac = new AbortController();\n * setAbortableInterval(syncStatus, 30_000, ac.signal);\n * window.addEventListener('beforeunload', () => ac.abort());\n * ```\n */\nexport function setAbortableInterval(\n callback: VoidFunction,\n delayInMs?: number,\n signal?: AbortSignal,\n) {\n let timer: number | null = null;\n\n const handleAbort = () => {\n if (timer == null) {\n return;\n }\n clearInterval(timer);\n timer = null;\n };\n\n signal?.addEventListener('abort', handleAbort, { once: true });\n\n timer = setInterval(callback, delayInMs);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,IAAa,SAAS,OAAe,GAAG,WACtC,IAAI,SAAe,SAAS,WAAW;AACrC,KAAI,QAAQ;EACV,MAAM,sBAAsB;AAC1B,gBAAa,QAAQ;AACrB,UAAO,QAAQ,OAAO;;EAExB,MAAM,UAAU,iBAAiB;AAC/B,UAAO,oBAAoB,SAAS,cAAc;AAClD,YAAS;KACR,KAAK;AACR,SAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,MAAM,CAAC;OAE/D,YAAW,SAAS,KAAK;EAE3B;;;;;;;;AASJ,IAAa,YAAY,OAAO,KAAK,QACnC,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCnD,IAAa,cACX,cACA,gBACG;AACH,KAAI,cAAc,CAAE;CAEpB,MAAM,YACJ,4BAA4B,WAAW,cAAc,YAAY,CAAC;AAEpE,KAAI,YACF,gBAAe,IAAI;KAEnB,MAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BT,SAAgB,oBACd,UACA,WACA,QACA;CACA,IAAI,gBAA+B;CAEnC,MAAM,oBAAoB;AACxB,MAAI,iBAAiB,KACnB;AAEF,eAAa,cAAc;AAC3B,kBAAgB;;AAGlB,SAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,MAAM,CAAC;AAE9D,iBAAgB,iBAAiB;AAC/B,UAAQ,oBAAoB,SAAS,YAAY;AACjD,YAAU;IACT,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;AA0Bf,SAAgB,qBACd,UACA,WACA,QACA;CACA,IAAI,QAAuB;CAE3B,MAAM,oBAAoB;AACxB,MAAI,SAAS,KACX;AAEF,gBAAc,MAAM;AACpB,UAAQ;;AAGV,SAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,MAAM,CAAC;AAE9D,SAAQ,YAAY,UAAU,UAAU"}
|
package/async.d.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ---header-docs-section---
|
|
3
|
+
* # yummies/async
|
|
4
|
+
*
|
|
5
|
+
* ## Description
|
|
6
|
+
*
|
|
7
|
+
* Helpers for asynchronous control flow: delays, cancellable waits, scheduling on the next frame,
|
|
8
|
+
* and small utilities around `requestAnimationFrame` and `queueMicrotask`. They complement native
|
|
9
|
+
* `Promise`/`AbortSignal` patterns and keep timing logic easy to test and tree-shake per call site.
|
|
10
|
+
* Import only what you need from `yummies/async` so bundlers can drop unused helpers.
|
|
11
|
+
*
|
|
12
|
+
* ## Usage
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { sleep } from "yummies/async";
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
1
18
|
/**
|
|
2
19
|
* Returns a promise that resolves after `time` milliseconds.
|
|
3
20
|
*
|
package/async.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
//#region src/async.ts
|
|
2
2
|
/**
|
|
3
|
+
* ---header-docs-section---
|
|
4
|
+
* # yummies/async
|
|
5
|
+
*
|
|
6
|
+
* ## Description
|
|
7
|
+
*
|
|
8
|
+
* Helpers for asynchronous control flow: delays, cancellable waits, scheduling on the next frame,
|
|
9
|
+
* and small utilities around `requestAnimationFrame` and `queueMicrotask`. They complement native
|
|
10
|
+
* `Promise`/`AbortSignal` patterns and keep timing logic easy to test and tree-shake per call site.
|
|
11
|
+
* Import only what you need from `yummies/async` so bundlers can drop unused helpers.
|
|
12
|
+
*
|
|
13
|
+
* ## Usage
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { sleep } from "yummies/async";
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
3
20
|
* Returns a promise that resolves after `time` milliseconds.
|
|
4
21
|
*
|
|
5
22
|
* When `signal` is passed and becomes aborted before the delay elapses, the promise
|
package/async.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async.js","names":[],"sources":["../src/async.ts"],"sourcesContent":["/**\n * Returns a promise that resolves after `time` milliseconds.\n *\n * When `signal` is passed and becomes aborted before the delay elapses, the promise\n * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).\n * The timeout is cleared on abort so no resolve happens after cancellation.\n *\n * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).\n * @param signal - Optional `AbortSignal` to cancel the wait early.\n * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.\n *\n * @example\n * Basic pause in an async function:\n * ```ts\n * await sleep(250);\n * console.log('after 250ms');\n * ```\n *\n * @example\n * Cancellable delay tied to component unmount or user action:\n * ```ts\n * const ac = new AbortController();\n * try {\n * await sleep(5000, ac.signal);\n * } catch (e) {\n * // aborted — e is signal.reason\n * }\n * ac.abort('user cancelled');\n * ```\n */\nexport const sleep = (time: number = 0, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n if (signal) {\n const abortListener = () => {\n clearTimeout(timerId);\n reject(signal?.reason);\n };\n const timerId = setTimeout(() => {\n signal.removeEventListener('abort', abortListener);\n resolve();\n }, time);\n signal.addEventListener('abort', abortListener, { once: true });\n } else {\n setTimeout(resolve, time);\n }\n });\n\n/**\n * Creates a promise that resolves after the specified number of milliseconds.\n *\n * @deprecated Use `sleep` instead.\n * @param ms Delay in milliseconds.\n * @returns Promise\n */\nexport const waitAsync = async (ms = 1000) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Runs a loop driven by `requestAnimationFrame`: on each frame, `quitFunction` is called first.\n * If it returns a truthy value, the loop stops and no further frames are scheduled.\n * If it returns falsy or nothing, the next frame is scheduled recursively.\n *\n * Use this for per-frame work (animations, layout reads after paint) without managing\n * `cancelAnimationFrame` manually — returning `true` from `quitFunction` is the exit condition.\n *\n * When `asMicrotask` is `true`, scheduling the next RAF is deferred with `queueMicrotask`\n * so other microtasks can run before the frame is requested (useful when you need to\n * batch DOM updates or let reactive frameworks flush first).\n *\n * @param quitFunction - Invoked each animation frame. Return `true` to stop the loop.\n * @param asMicrotask - If `true`, wrap the `requestAnimationFrame` call in `queueMicrotask`.\n *\n * @example\n * Stop after 60 frames (~1s at 60Hz):\n * ```ts\n * let frames = 0;\n * endlessRAF(() => {\n * frames++;\n * updateSomething(frames);\n * return frames >= 60;\n * });\n * ```\n *\n * @example\n * Run until an element is removed or a flag is set:\n * ```ts\n * let running = true;\n * endlessRAF(() => {\n * if (!running || !document.body.contains(el)) return true;\n * draw(el);\n * }, true);\n * ```\n */\nexport const endlessRAF = (\n quitFunction: () => boolean | void,\n asMicrotask?: boolean,\n) => {\n if (quitFunction()) return;\n\n const raf = () =>\n requestAnimationFrame(() => endlessRAF(quitFunction, asMicrotask));\n\n if (asMicrotask) {\n queueMicrotask(raf);\n } else {\n raf();\n }\n};\n\n/**\n * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared\n * and `callback` is never run. If the callback runs normally, the abort listener is removed.\n *\n * Does nothing special if `signal` is omitted — behaves like a plain timeout.\n *\n * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).\n * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).\n * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableTimeout(() => console.log('done'), 500, controller.signal);\n * // later: controller.abort(); // timeout cancelled, log never runs\n * ```\n *\n * @example\n * Zero-delay scheduling that can still be cancelled before the macrotask runs:\n * ```ts\n * const ac = new AbortController();\n * setAbortableTimeout(() => startIntro(), 0, ac.signal);\n * // e.g. on teardown: ac.abort();\n * ```\n */\nexport function setAbortableTimeout(\n callback: VoidFunction,\n delayInMs?: number,\n signal?: AbortSignal,\n) {\n let internalTimer: number | null = null;\n\n const handleAbort = () => {\n if (internalTimer == null) {\n return;\n }\n clearTimeout(internalTimer);\n internalTimer = null;\n };\n\n signal?.addEventListener('abort', handleAbort, { once: true });\n\n internalTimer = setTimeout(() => {\n signal?.removeEventListener('abort', handleAbort);\n callback();\n }, delayInMs);\n}\n\n/**\n * Like `setInterval`, but when `signal` aborts, the interval is cleared with `clearInterval`\n * and `callback` stops being called. If `signal` is omitted, behaves like a normal interval\n * (you must clear it yourself).\n *\n * @param callback - Invoked every `delayInMs` milliseconds until aborted or cleared.\n * @param delayInMs - Interval period in milliseconds (same as `setInterval`).\n * @param signal - Aborting stops the interval and removes the abort listener path from keeping work alive.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableInterval(() => console.log('tick'), 1000, controller.signal);\n * // stop: controller.abort();\n * ```\n *\n * @example\n * ```ts\n * const ac = new AbortController();\n * setAbortableInterval(syncStatus, 30_000, ac.signal);\n * window.addEventListener('beforeunload', () => ac.abort());\n * ```\n */\nexport function setAbortableInterval(\n callback: VoidFunction,\n delayInMs?: number,\n signal?: AbortSignal,\n) {\n let timer: number | null = null;\n\n const handleAbort = () => {\n if (timer == null) {\n return;\n }\n clearInterval(timer);\n timer = null;\n };\n\n signal?.addEventListener('abort', handleAbort, { once: true });\n\n timer = setInterval(callback, delayInMs);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"async.js","names":[],"sources":["../src/async.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/async\n *\n * ## Description\n *\n * Helpers for asynchronous control flow: delays, cancellable waits, scheduling on the next frame,\n * and small utilities around `requestAnimationFrame` and `queueMicrotask`. They complement native\n * `Promise`/`AbortSignal` patterns and keep timing logic easy to test and tree-shake per call site.\n * Import only what you need from `yummies/async` so bundlers can drop unused helpers.\n *\n * ## Usage\n *\n * ```ts\n * import { sleep } from \"yummies/async\";\n * ```\n */\n\n/**\n * Returns a promise that resolves after `time` milliseconds.\n *\n * When `signal` is passed and becomes aborted before the delay elapses, the promise\n * rejects with `signal.reason` (same as native `fetch` / `AbortController` usage).\n * The timeout is cleared on abort so no resolve happens after cancellation.\n *\n * @param time - Delay in milliseconds. Defaults to `0` (next macrotask tick, same idea as `setTimeout(0)`).\n * @param signal - Optional `AbortSignal` to cancel the wait early.\n * @returns A promise that resolves with `void` when the delay completes, or rejects if aborted.\n *\n * @example\n * Basic pause in an async function:\n * ```ts\n * await sleep(250);\n * console.log('after 250ms');\n * ```\n *\n * @example\n * Cancellable delay tied to component unmount or user action:\n * ```ts\n * const ac = new AbortController();\n * try {\n * await sleep(5000, ac.signal);\n * } catch (e) {\n * // aborted — e is signal.reason\n * }\n * ac.abort('user cancelled');\n * ```\n */\nexport const sleep = (time: number = 0, signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n if (signal) {\n const abortListener = () => {\n clearTimeout(timerId);\n reject(signal?.reason);\n };\n const timerId = setTimeout(() => {\n signal.removeEventListener('abort', abortListener);\n resolve();\n }, time);\n signal.addEventListener('abort', abortListener, { once: true });\n } else {\n setTimeout(resolve, time);\n }\n });\n\n/**\n * Creates a promise that resolves after the specified number of milliseconds.\n *\n * @deprecated Use `sleep` instead.\n * @param ms Delay in milliseconds.\n * @returns Promise\n */\nexport const waitAsync = async (ms = 1000) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Runs a loop driven by `requestAnimationFrame`: on each frame, `quitFunction` is called first.\n * If it returns a truthy value, the loop stops and no further frames are scheduled.\n * If it returns falsy or nothing, the next frame is scheduled recursively.\n *\n * Use this for per-frame work (animations, layout reads after paint) without managing\n * `cancelAnimationFrame` manually — returning `true` from `quitFunction` is the exit condition.\n *\n * When `asMicrotask` is `true`, scheduling the next RAF is deferred with `queueMicrotask`\n * so other microtasks can run before the frame is requested (useful when you need to\n * batch DOM updates or let reactive frameworks flush first).\n *\n * @param quitFunction - Invoked each animation frame. Return `true` to stop the loop.\n * @param asMicrotask - If `true`, wrap the `requestAnimationFrame` call in `queueMicrotask`.\n *\n * @example\n * Stop after 60 frames (~1s at 60Hz):\n * ```ts\n * let frames = 0;\n * endlessRAF(() => {\n * frames++;\n * updateSomething(frames);\n * return frames >= 60;\n * });\n * ```\n *\n * @example\n * Run until an element is removed or a flag is set:\n * ```ts\n * let running = true;\n * endlessRAF(() => {\n * if (!running || !document.body.contains(el)) return true;\n * draw(el);\n * }, true);\n * ```\n */\nexport const endlessRAF = (\n quitFunction: () => boolean | void,\n asMicrotask?: boolean,\n) => {\n if (quitFunction()) return;\n\n const raf = () =>\n requestAnimationFrame(() => endlessRAF(quitFunction, asMicrotask));\n\n if (asMicrotask) {\n queueMicrotask(raf);\n } else {\n raf();\n }\n};\n\n/**\n * Like `setTimeout`, but if `signal` aborts before the delay fires, the timer is cleared\n * and `callback` is never run. If the callback runs normally, the abort listener is removed.\n *\n * Does nothing special if `signal` is omitted — behaves like a plain timeout.\n *\n * @param callback - Function to run once after `delayInMs` (same as `setTimeout` callback).\n * @param delayInMs - Milliseconds to wait. Passed through to `setTimeout` (browser/Node semantics apply).\n * @param signal - When aborted, clears the pending timeout so `callback` is not invoked.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableTimeout(() => console.log('done'), 500, controller.signal);\n * // later: controller.abort(); // timeout cancelled, log never runs\n * ```\n *\n * @example\n * Zero-delay scheduling that can still be cancelled before the macrotask runs:\n * ```ts\n * const ac = new AbortController();\n * setAbortableTimeout(() => startIntro(), 0, ac.signal);\n * // e.g. on teardown: ac.abort();\n * ```\n */\nexport function setAbortableTimeout(\n callback: VoidFunction,\n delayInMs?: number,\n signal?: AbortSignal,\n) {\n let internalTimer: number | null = null;\n\n const handleAbort = () => {\n if (internalTimer == null) {\n return;\n }\n clearTimeout(internalTimer);\n internalTimer = null;\n };\n\n signal?.addEventListener('abort', handleAbort, { once: true });\n\n internalTimer = setTimeout(() => {\n signal?.removeEventListener('abort', handleAbort);\n callback();\n }, delayInMs);\n}\n\n/**\n * Like `setInterval`, but when `signal` aborts, the interval is cleared with `clearInterval`\n * and `callback` stops being called. If `signal` is omitted, behaves like a normal interval\n * (you must clear it yourself).\n *\n * @param callback - Invoked every `delayInMs` milliseconds until aborted or cleared.\n * @param delayInMs - Interval period in milliseconds (same as `setInterval`).\n * @param signal - Aborting stops the interval and removes the abort listener path from keeping work alive.\n *\n * @example\n * ```ts\n * const controller = new AbortController();\n * setAbortableInterval(() => console.log('tick'), 1000, controller.signal);\n * // stop: controller.abort();\n * ```\n *\n * @example\n * ```ts\n * const ac = new AbortController();\n * setAbortableInterval(syncStatus, 30_000, ac.signal);\n * window.addEventListener('beforeunload', () => ac.abort());\n * ```\n */\nexport function setAbortableInterval(\n callback: VoidFunction,\n delayInMs?: number,\n signal?: AbortSignal,\n) {\n let timer: number | null = null;\n\n const handleAbort = () => {\n if (timer == null) {\n return;\n }\n clearInterval(timer);\n timer = null;\n };\n\n signal?.addEventListener('abort', handleAbort, { once: true });\n\n timer = setInterval(callback, delayInMs);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,IAAa,SAAS,OAAe,GAAG,WACtC,IAAI,SAAe,SAAS,WAAW;AACrC,KAAI,QAAQ;EACV,MAAM,sBAAsB;AAC1B,gBAAa,QAAQ;AACrB,UAAO,QAAQ,OAAO;;EAExB,MAAM,UAAU,iBAAiB;AAC/B,UAAO,oBAAoB,SAAS,cAAc;AAClD,YAAS;KACR,KAAK;AACR,SAAO,iBAAiB,SAAS,eAAe,EAAE,MAAM,MAAM,CAAC;OAE/D,YAAW,SAAS,KAAK;EAE3B;;;;;;;;AASJ,IAAa,YAAY,OAAO,KAAK,QACnC,IAAI,SAAS,YAAY,WAAW,SAAS,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCnD,IAAa,cACX,cACA,gBACG;AACH,KAAI,cAAc,CAAE;CAEpB,MAAM,YACJ,4BAA4B,WAAW,cAAc,YAAY,CAAC;AAEpE,KAAI,YACF,gBAAe,IAAI;KAEnB,MAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BT,SAAgB,oBACd,UACA,WACA,QACA;CACA,IAAI,gBAA+B;CAEnC,MAAM,oBAAoB;AACxB,MAAI,iBAAiB,KACnB;AAEF,eAAa,cAAc;AAC3B,kBAAgB;;AAGlB,SAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,MAAM,CAAC;AAE9D,iBAAgB,iBAAiB;AAC/B,UAAQ,oBAAoB,SAAS,YAAY;AACjD,YAAU;IACT,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;AA0Bf,SAAgB,qBACd,UACA,WACA,QACA;CACA,IAAI,QAAuB;CAE3B,MAAM,oBAAoB;AACxB,MAAI,SAAS,KACX;AAEF,gBAAc,MAAM;AACpB,UAAQ;;AAGV,SAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,MAAM,CAAC;AAE9D,SAAQ,YAAY,UAAU,UAAU"}
|
package/common.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.cjs","names":[],"sources":["../src/common.ts"],"sourcesContent":["import type { MaybeFn } from 'yummies/types';\n\n/**\n * @deprecated use {MaybeFn} type\n */\nexport type FnValue<TValue, TArgs extends any[] = []> = MaybeFn<TValue, TArgs>;\n\n/**\n * Normalizes a {@link MaybeFn} — a value that may be either a plain `TValue` or a function\n * `(...args: TArgs) => TValue`. If `fn` is callable, it is invoked with `args` and the return\n * value is returned; otherwise `fn` is returned as-is (treated as the resolved value).\n *\n * Typical uses: config fields that accept a static value or a lazy/computed factory, theme\n * tokens, labels, or callbacks where the caller should not branch on `typeof fn` themselves.\n *\n * @template TValue - Result type when `fn` is not a function, or return type when it is.\n * @template TArgs - Tuple of argument types passed through when `fn` is invoked.\n *\n * @param fn - Either a `TValue` or a function producing `TValue` from `args`.\n * @param args - Arguments forwarded to `fn` only when `fn` is a function.\n * @returns The resolved `TValue`.\n *\n * @example\n * Plain value — returned unchanged (no call):\n * ```ts\n * const n = callFunction(42); // 42\n * const label = callFunction('Hello'); // 'Hello'\n * ```\n *\n * @example\n * Function — called with the given arguments:\n * ```ts\n * const sum = callFunction((a: number, b: number) => a + b, 2, 3); // 5\n * ```\n *\n * @example\n * Same API for “static or factory” props:\n * ```ts\n * type Title = MaybeFn<string, [locale: string]>;\n * const title: Title = (loc) => (loc === 'ru' ? 'Привет' : 'Hi');\n * const text = callFunction(title, 'ru'); // 'Привет'\n * const fixed = callFunction('Hi', 'ru'); // 'Hi' — args ignored\n * ```\n */\nexport const callFunction = <TValue, TArgs extends any[] = []>(\n fn: MaybeFn<TValue, TArgs>,\n ...args: TArgs\n) => {\n if (typeof fn === 'function') {\n return (fn as any)(...args) as TValue;\n }\n\n return fn;\n};\n\n/**\n * @deprecated use {callFunction}\n */\nexport const resolveFnValue = callFunction;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"common.cjs","names":[],"sources":["../src/common.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/common\n *\n * ## Description\n *\n * Small helpers for values that may be either plain data or callables (`MaybeFn` pattern). Use them\n * when a prop or config can be a static value or a factory, without forcing callers to branch on\n * `typeof` everywhere. The module also keeps a thin compatibility layer for older `FnValue` naming.\n * Everything is typed to preserve argument tuples and return types through `callFunction`.\n *\n * ## Usage\n *\n * ```ts\n * import { callFunction } from \"yummies/common\";\n * ```\n */\n\nimport type { MaybeFn } from 'yummies/types';\n\n/**\n * @deprecated use {MaybeFn} type\n */\nexport type FnValue<TValue, TArgs extends any[] = []> = MaybeFn<TValue, TArgs>;\n\n/**\n * Normalizes a {@link MaybeFn} — a value that may be either a plain `TValue` or a function\n * `(...args: TArgs) => TValue`. If `fn` is callable, it is invoked with `args` and the return\n * value is returned; otherwise `fn` is returned as-is (treated as the resolved value).\n *\n * Typical uses: config fields that accept a static value or a lazy/computed factory, theme\n * tokens, labels, or callbacks where the caller should not branch on `typeof fn` themselves.\n *\n * @template TValue - Result type when `fn` is not a function, or return type when it is.\n * @template TArgs - Tuple of argument types passed through when `fn` is invoked.\n *\n * @param fn - Either a `TValue` or a function producing `TValue` from `args`.\n * @param args - Arguments forwarded to `fn` only when `fn` is a function.\n * @returns The resolved `TValue`.\n *\n * @example\n * Plain value — returned unchanged (no call):\n * ```ts\n * const n = callFunction(42); // 42\n * const label = callFunction('Hello'); // 'Hello'\n * ```\n *\n * @example\n * Function — called with the given arguments:\n * ```ts\n * const sum = callFunction((a: number, b: number) => a + b, 2, 3); // 5\n * ```\n *\n * @example\n * Same API for “static or factory” props:\n * ```ts\n * type Title = MaybeFn<string, [locale: string]>;\n * const title: Title = (loc) => (loc === 'ru' ? 'Привет' : 'Hi');\n * const text = callFunction(title, 'ru'); // 'Привет'\n * const fixed = callFunction('Hi', 'ru'); // 'Hi' — args ignored\n * ```\n */\nexport const callFunction = <TValue, TArgs extends any[] = []>(\n fn: MaybeFn<TValue, TArgs>,\n ...args: TArgs\n) => {\n if (typeof fn === 'function') {\n return (fn as any)(...args) as TValue;\n }\n\n return fn;\n};\n\n/**\n * @deprecated use {callFunction}\n */\nexport const resolveFnValue = callFunction;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAa,gBACX,IACA,GAAG,SACA;AACH,KAAI,OAAO,OAAO,WAChB,QAAQ,GAAW,GAAG,KAAK;AAG7B,QAAO;;;;;AAMT,IAAa,iBAAiB"}
|
package/common.d.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { MaybeFn } from 'yummies/types';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* ---header-docs-section---
|
|
5
|
+
* # yummies/common
|
|
6
|
+
*
|
|
7
|
+
* ## Description
|
|
8
|
+
*
|
|
9
|
+
* Small helpers for values that may be either plain data or callables (`MaybeFn` pattern). Use them
|
|
10
|
+
* when a prop or config can be a static value or a factory, without forcing callers to branch on
|
|
11
|
+
* `typeof` everywhere. The module also keeps a thin compatibility layer for older `FnValue` naming.
|
|
12
|
+
* Everything is typed to preserve argument tuples and return types through `callFunction`.
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { callFunction } from "yummies/common";
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
3
21
|
/**
|
|
4
22
|
* @deprecated use {MaybeFn} type
|
|
5
23
|
*/
|
package/common.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.js","names":[],"sources":["../src/common.ts"],"sourcesContent":["import type { MaybeFn } from 'yummies/types';\n\n/**\n * @deprecated use {MaybeFn} type\n */\nexport type FnValue<TValue, TArgs extends any[] = []> = MaybeFn<TValue, TArgs>;\n\n/**\n * Normalizes a {@link MaybeFn} — a value that may be either a plain `TValue` or a function\n * `(...args: TArgs) => TValue`. If `fn` is callable, it is invoked with `args` and the return\n * value is returned; otherwise `fn` is returned as-is (treated as the resolved value).\n *\n * Typical uses: config fields that accept a static value or a lazy/computed factory, theme\n * tokens, labels, or callbacks where the caller should not branch on `typeof fn` themselves.\n *\n * @template TValue - Result type when `fn` is not a function, or return type when it is.\n * @template TArgs - Tuple of argument types passed through when `fn` is invoked.\n *\n * @param fn - Either a `TValue` or a function producing `TValue` from `args`.\n * @param args - Arguments forwarded to `fn` only when `fn` is a function.\n * @returns The resolved `TValue`.\n *\n * @example\n * Plain value — returned unchanged (no call):\n * ```ts\n * const n = callFunction(42); // 42\n * const label = callFunction('Hello'); // 'Hello'\n * ```\n *\n * @example\n * Function — called with the given arguments:\n * ```ts\n * const sum = callFunction((a: number, b: number) => a + b, 2, 3); // 5\n * ```\n *\n * @example\n * Same API for “static or factory” props:\n * ```ts\n * type Title = MaybeFn<string, [locale: string]>;\n * const title: Title = (loc) => (loc === 'ru' ? 'Привет' : 'Hi');\n * const text = callFunction(title, 'ru'); // 'Привет'\n * const fixed = callFunction('Hi', 'ru'); // 'Hi' — args ignored\n * ```\n */\nexport const callFunction = <TValue, TArgs extends any[] = []>(\n fn: MaybeFn<TValue, TArgs>,\n ...args: TArgs\n) => {\n if (typeof fn === 'function') {\n return (fn as any)(...args) as TValue;\n }\n\n return fn;\n};\n\n/**\n * @deprecated use {callFunction}\n */\nexport const resolveFnValue = callFunction;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"common.js","names":[],"sources":["../src/common.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/common\n *\n * ## Description\n *\n * Small helpers for values that may be either plain data or callables (`MaybeFn` pattern). Use them\n * when a prop or config can be a static value or a factory, without forcing callers to branch on\n * `typeof` everywhere. The module also keeps a thin compatibility layer for older `FnValue` naming.\n * Everything is typed to preserve argument tuples and return types through `callFunction`.\n *\n * ## Usage\n *\n * ```ts\n * import { callFunction } from \"yummies/common\";\n * ```\n */\n\nimport type { MaybeFn } from 'yummies/types';\n\n/**\n * @deprecated use {MaybeFn} type\n */\nexport type FnValue<TValue, TArgs extends any[] = []> = MaybeFn<TValue, TArgs>;\n\n/**\n * Normalizes a {@link MaybeFn} — a value that may be either a plain `TValue` or a function\n * `(...args: TArgs) => TValue`. If `fn` is callable, it is invoked with `args` and the return\n * value is returned; otherwise `fn` is returned as-is (treated as the resolved value).\n *\n * Typical uses: config fields that accept a static value or a lazy/computed factory, theme\n * tokens, labels, or callbacks where the caller should not branch on `typeof fn` themselves.\n *\n * @template TValue - Result type when `fn` is not a function, or return type when it is.\n * @template TArgs - Tuple of argument types passed through when `fn` is invoked.\n *\n * @param fn - Either a `TValue` or a function producing `TValue` from `args`.\n * @param args - Arguments forwarded to `fn` only when `fn` is a function.\n * @returns The resolved `TValue`.\n *\n * @example\n * Plain value — returned unchanged (no call):\n * ```ts\n * const n = callFunction(42); // 42\n * const label = callFunction('Hello'); // 'Hello'\n * ```\n *\n * @example\n * Function — called with the given arguments:\n * ```ts\n * const sum = callFunction((a: number, b: number) => a + b, 2, 3); // 5\n * ```\n *\n * @example\n * Same API for “static or factory” props:\n * ```ts\n * type Title = MaybeFn<string, [locale: string]>;\n * const title: Title = (loc) => (loc === 'ru' ? 'Привет' : 'Hi');\n * const text = callFunction(title, 'ru'); // 'Привет'\n * const fixed = callFunction('Hi', 'ru'); // 'Hi' — args ignored\n * ```\n */\nexport const callFunction = <TValue, TArgs extends any[] = []>(\n fn: MaybeFn<TValue, TArgs>,\n ...args: TArgs\n) => {\n if (typeof fn === 'function') {\n return (fn as any)(...args) as TValue;\n }\n\n return fn;\n};\n\n/**\n * @deprecated use {callFunction}\n */\nexport const resolveFnValue = callFunction;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,IAAa,gBACX,IACA,GAAG,SACA;AACH,KAAI,OAAO,OAAO,WAChB,QAAQ,GAAW,GAAG,KAAK;AAG7B,QAAO;;;;;AAMT,IAAa,iBAAiB"}
|
package/complex.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"complex.cjs","names":[],"sources":["../src/complex/counter.ts","../src/complex/global-point.ts","../src/complex/global-config.ts","../src/complex/modules-factory.ts","../src/complex/pub-sub.ts"],"sourcesContent":["export interface Counter<TValue = number> {\n (): TValue;\n counter: number;\n value: TValue;\n increment(): TValue;\n decrement(): TValue;\n reset(): void;\n}\n\n/**\n * @deprecated use {`Counter`}. Will be removed in next major release\n */\nexport interface CounterFn<TValue = number> extends Counter<TValue> {}\n\n/**\n * Creates a callable counter object with increment, decrement and reset helpers.\n *\n * The returned function increments the internal numeric counter when called and\n * exposes both the raw counter value and an optionally transformed `value`.\n *\n * @template TValue Public value type returned by the counter.\n * @param processValue Optional mapper that transforms the numeric counter value.\n * @param initial Initial numeric counter value.\n * @returns Callable counter with state and control methods.\n *\n * @example\n * ```ts\n * const counter = createCounter();\n * counter.increment(); // 1\n * ```\n *\n * @example\n * ```ts\n * const idCounter = createCounter((value) => `id-${value}`, 10);\n * idCounter(); // 'id-11'\n * ```\n */\nexport const createCounter = <TValue = number>(\n processValue?: (value: number) => TValue,\n initial: number = 0,\n): Counter<TValue> => {\n const update = (counter: number) => {\n fn.value = processValue?.(counter) ?? (counter as TValue);\n return fn.value;\n };\n\n const increment = () => update(++fn.counter);\n const decrement = () => update(--fn.counter);\n\n const fn: Counter<TValue> = increment as any;\n\n fn.increment = increment;\n fn.decrement = decrement;\n\n fn.reset = () => {\n fn.counter = initial;\n fn.value = processValue?.(initial) ?? (initial as TValue);\n };\n\n fn.reset();\n\n return fn as Counter<TValue>;\n};\n","import type { AnyObject } from 'yummies/types';\n\nexport interface GlobalPoint<TValue> {\n get(): TValue;\n set(value: TValue): TValue;\n unset(): void;\n}\n\n/**\n * Creates a simple storage point that can live either in `globalThis` under a\n * provided key or in a local closure when no key is given.\n *\n * @template TValue Stored value type.\n * @param accessSymbol Optional global property name used for storage.\n * @returns Getter/setter API for the stored value.\n *\n * @example\n * ```ts\n * const point = createGlobalPoint<number>();\n * point.set(10);\n * ```\n *\n * @example\n * ```ts\n * const point = createGlobalPoint<string>('__token__');\n * point.get();\n * ```\n */\nexport const createGlobalPoint = <TValue>(\n accessSymbol?: keyof any,\n): GlobalPoint<TValue> => {\n if (accessSymbol == null) {\n let storedValue: TValue | undefined;\n\n return {\n get: (): TValue => storedValue!,\n unset: () => {\n storedValue = undefined;\n },\n set: (value: TValue): TValue => {\n storedValue = value;\n return value;\n },\n };\n }\n\n const _globalThis = globalThis as AnyObject;\n\n return {\n get: (): TValue => _globalThis[accessSymbol],\n unset: () => {\n delete _globalThis[accessSymbol];\n },\n set: (value: TValue): TValue => {\n _globalThis[accessSymbol] = value;\n return value;\n },\n };\n};\n","import type { AnyObject, Maybe } from 'yummies/types';\nimport { createGlobalPoint } from './global-point.js';\n\n/**\n * Creates or reuses a globally accessible config object.\n *\n * The config is stored in a global point identified by `accessSymbol`, or in a\n * local closure when no symbol is provided.\n *\n * @template T Config object type.\n * @param defaultValue Default value used when no config has been created yet.\n * @param accessSymbol Optional global key used to store the config.\n * @returns Existing or newly initialized global config object.\n *\n * @example\n * ```ts\n * const config = createGlobalConfig({ locale: 'en' }, '__app_config__');\n * ```\n *\n * @example\n * ```ts\n * const config = createGlobalConfig({ debug: false });\n * config.debug;\n * ```\n */\nexport const createGlobalConfig = <T extends AnyObject>(\n defaultValue: T,\n accessSymbol?: keyof any,\n): T => {\n const globalPoint = createGlobalPoint<T>(accessSymbol);\n return globalPoint.get() || globalPoint.set(defaultValue);\n};\n\nexport interface GlobalDynamicConfig<TValue extends AnyObject> {\n get(): TValue;\n set(value: TValue): TValue;\n unset(): void;\n update(value: Partial<TValue>): void;\n}\n\n/**\n * Creates a mutable global config manager whose value is produced and updated\n * through a custom processor function.\n *\n * @template T Config object type.\n * @param processFn Function that builds the next config state from a partial change and current value.\n * @param accessSymbol Optional global key used to store the config.\n * @returns API for reading, replacing, resetting and partially updating the config.\n *\n * @example\n * ```ts\n * const config = createGlobalDynamicConfig(\n * (change, current) => ({ theme: 'light', ...current, ...change }),\n * '__theme__',\n * );\n * ```\n *\n * @example\n * ```ts\n * const config = createGlobalDynamicConfig((change, current) => ({ ...current, ...change }));\n * config.update({ locale: 'ru' });\n * ```\n */\nexport const createGlobalDynamicConfig = <T extends AnyObject>(\n processFn: (change: Maybe<Partial<T>>, current: Maybe<T>) => T,\n accessSymbol?: keyof any,\n): GlobalDynamicConfig<T> => {\n const globalPoint = createGlobalPoint<T>(accessSymbol);\n\n const getValue = () => {\n return globalPoint.get() ?? globalPoint.set(processFn(null, null))!;\n };\n\n return {\n get: getValue,\n set: globalPoint.set,\n unset: globalPoint.unset,\n update: (value: Partial<T>) => {\n const currentValue = getValue();\n Object.assign(currentValue, processFn(value, currentValue));\n },\n };\n};\n","import type { AnyObject, Class, EmptyObject, IsPartial } from 'yummies/types';\n\ntype ModuleLoaderConfig<TPredefinedDeps extends AnyObject = EmptyObject> = {\n factory<TInstance, TDeps extends TPredefinedDeps>(\n moduleClass: Class<TInstance, [TDeps]>,\n deps: TDeps,\n ): TInstance;\n} & (TPredefinedDeps extends EmptyObject\n ? { deps?: TPredefinedDeps }\n : { deps: TPredefinedDeps });\n\n/**\n * Universal factory for creating class instances with predefined and per-call\n * dependencies.\n *\n * Works with classes whose constructor accepts a single dependency object.\n *\n * @template TPredefinedDeps Dependency shape that is always injected by the factory.\n *\n * @example\n * ```\n * const factory = new ModulesFactory({\n * factory: (MyClass, deps) => new MyClass(deps),\n * deps: { someDependency: new Dependency() }\n * });\n *\n * const instance = factory.create(MyClass, { extraDependency: new ExtraDependency() });\n * ```\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * });\n * ```\n *\n * @example\n * ```ts\n * const service = factory.create(UserService, { api });\n * ```\n */\nexport class ModulesFactory<TPredefinedDeps extends AnyObject = EmptyObject> {\n /**\n * Creates a new module factory.\n *\n * @param config Factory strategy and predefined dependencies.\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * });\n * ```\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * deps: { api },\n * });\n * ```\n */\n constructor(private config: ModuleLoaderConfig<TPredefinedDeps>) {}\n\n /**\n * Creates an instance of the provided class by merging predefined and\n * per-call dependencies.\n *\n * @template TInstance Instance type produced by the constructor.\n * @template TDeps Full dependency object expected by the constructor.\n * @param Constructor Class constructor receiving a single dependency object.\n * @param args Additional dependencies merged over predefined ones.\n * @returns Created class instance.\n *\n * @example\n * ```ts\n * const service = factory.create(UserService, { logger });\n * ```\n *\n * @example\n * ```ts\n * const store = factory.create(UserStore);\n * ```\n */\n create<TInstance, TDeps extends TPredefinedDeps = TPredefinedDeps>(\n Constructor: Class<TInstance, [TDeps]>,\n ...args: IsPartial<Omit<TDeps, keyof TPredefinedDeps>> extends true\n ? [extraDeps?: Omit<TDeps, keyof TPredefinedDeps>]\n : [extraDeps: Omit<TDeps, keyof TPredefinedDeps>]\n ) {\n return this.config.factory(Constructor, {\n ...this.config.deps!,\n ...args[0],\n } as any);\n }\n}\n","export type SubFn<PubArgs extends any[] = any[]> = (...args: PubArgs) => void;\n\n/**\n * The Publish-Subscribe pattern, which allows objects to interact with each other\n * through an event system. Subscribers can subscribe to events and receive notifications\n * when these events occur. The last published data can be accessed through the `data` property.\n */\nexport interface PubSub<PubArgs extends any[] = any[]> {\n (...args: PubArgs): void;\n\n /**\n * Last published arguments\n */\n lastPub: PubArgs | undefined;\n\n /**\n * An array of subscriber functions (sub) that will be called\n * when an event is published. Each subscriber must match the type SubFn,\n * taking the arguments that will be passed to it when the publisher calls pub.\n */\n subs: SubFn<PubArgs>[];\n\n /**\n * A function to unsubscribe from events. When a subscriber function (sub) is passed,\n * it will be removed from the `subs` array, and will no longer receive notifications.\n */\n unsub(sub: SubFn<PubArgs>): void;\n /**\n * A function to subscribe to events. When a subscriber function (sub) is passed,\n * it will be added to the `subs` array, and will receive notifications when the publisher calls pub.\n * Returns a function that can be used to unsubscribe from events.\n */\n sub(sub: SubFn<PubArgs>): VoidFunction;\n}\n\n/**\n * Creates a simple publish-subscribe dispatcher that stores the last published\n * arguments and allows subscription management.\n *\n * @template PubArgs Argument tuple delivered to subscribers.\n * @returns Callable publisher with subscribe and unsubscribe helpers.\n *\n * @example\n * ```ts\n * const pub = createPubSub<[string]>();\n * pub('ready');\n * ```\n *\n * @example\n * ```ts\n * const pub = createPubSub<[number]>();\n * const unsub = pub.sub((value) => console.log(value));\n * ```\n */\nexport const createPubSub = <PubArgs extends any[] = any[]>() => {\n const pubSub = ((...args: PubArgs) => {\n pubSub.lastPub = args;\n pubSub.subs.forEach((sub) => {\n sub(...args);\n });\n }) as PubSub<PubArgs>;\n pubSub.lastPub = undefined;\n\n pubSub.subs = [];\n\n pubSub.unsub = (sub: SubFn<PubArgs>) => {\n pubSub.subs = pubSub.subs.filter((it) => it !== sub);\n };\n pubSub.sub = (sub: SubFn<PubArgs>) => {\n pubSub.subs.push(sub);\n return () => pubSub.unsub(sub);\n };\n\n return pubSub;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,IAAa,iBACX,cACA,UAAkB,MACE;CACpB,MAAM,UAAU,YAAoB;AAClC,KAAG,QAAQ,eAAe,QAAQ,IAAK;AACvC,SAAO,GAAG;;CAGZ,MAAM,kBAAkB,OAAO,EAAE,GAAG,QAAQ;CAC5C,MAAM,kBAAkB,OAAO,EAAE,GAAG,QAAQ;CAE5C,MAAM,KAAsB;AAE5B,IAAG,YAAY;AACf,IAAG,YAAY;AAEf,IAAG,cAAc;AACf,KAAG,UAAU;AACb,KAAG,QAAQ,eAAe,QAAQ,IAAK;;AAGzC,IAAG,OAAO;AAEV,QAAO;;;;;;;;;;;;;;;;;;;;;;;;ACjCT,IAAa,qBACX,iBACwB;AACxB,KAAI,gBAAgB,MAAM;EACxB,IAAI;AAEJ,SAAO;GACL,WAAmB;GACnB,aAAa;AACX,kBAAc,KAAA;;GAEhB,MAAM,UAA0B;AAC9B,kBAAc;AACd,WAAO;;GAEV;;CAGH,MAAM,cAAc;AAEpB,QAAO;EACL,WAAmB,YAAY;EAC/B,aAAa;AACX,UAAO,YAAY;;EAErB,MAAM,UAA0B;AAC9B,eAAY,gBAAgB;AAC5B,UAAO;;EAEV;;;;;;;;;;;;;;;;;;;;;;;;;;AChCH,IAAa,sBACX,cACA,iBACM;CACN,MAAM,cAAc,kBAAqB,aAAa;AACtD,QAAO,YAAY,KAAK,IAAI,YAAY,IAAI,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;AAiC3D,IAAa,6BACX,WACA,iBAC2B;CAC3B,MAAM,cAAc,kBAAqB,aAAa;CAEtD,MAAM,iBAAiB;AACrB,SAAO,YAAY,KAAK,IAAI,YAAY,IAAI,UAAU,MAAM,KAAK,CAAC;;AAGpE,QAAO;EACL,KAAK;EACL,KAAK,YAAY;EACjB,OAAO,YAAY;EACnB,SAAS,UAAsB;GAC7B,MAAM,eAAe,UAAU;AAC/B,UAAO,OAAO,cAAc,UAAU,OAAO,aAAa,CAAC;;EAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxCH,IAAa,iBAAb,MAA6E;;;;;;;;;;;;;;;;;;;;;CAqB3E,YAAY,QAAqD;AAA7C,OAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;CAsBpB,OACE,aACA,GAAG,MAGH;AACA,SAAO,KAAK,OAAO,QAAQ,aAAa;GACtC,GAAG,KAAK,OAAO;GACf,GAAG,KAAK;GACT,CAAQ;;;;;;;;;;;;;;;;;;;;;;;;ACvCb,IAAa,qBAAoD;CAC/D,MAAM,WAAW,GAAG,SAAkB;AACpC,SAAO,UAAU;AACjB,SAAO,KAAK,SAAS,QAAQ;AAC3B,OAAI,GAAG,KAAK;IACZ;;AAEJ,QAAO,UAAU,KAAA;AAEjB,QAAO,OAAO,EAAE;AAEhB,QAAO,SAAS,QAAwB;AACtC,SAAO,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,IAAI;;AAEtD,QAAO,OAAO,QAAwB;AACpC,SAAO,KAAK,KAAK,IAAI;AACrB,eAAa,OAAO,MAAM,IAAI;;AAGhC,QAAO"}
|
|
1
|
+
{"version":3,"file":"complex.cjs","names":[],"sources":["../src/complex/counter.ts","../src/complex/global-point.ts","../src/complex/global-config.ts","../src/complex/modules-factory.ts","../src/complex/pub-sub.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * Callable **counter** objects with increment, decrement, and reset for UI steppers, pagination,\n * and id generators. Values can be mapped on read so the public API stays typed while the internal\n * tick stays numeric. This file is the `counter` slice of the broader `yummies/complex` entry point.\n *\n * ## Usage\n *\n * ```ts\n * import { createCounter } from \"yummies/complex\";\n * ```\n */\n\nexport interface Counter<TValue = number> {\n (): TValue;\n counter: number;\n value: TValue;\n increment(): TValue;\n decrement(): TValue;\n reset(): void;\n}\n\n/**\n * @deprecated use {`Counter`}. Will be removed in next major release\n */\nexport interface CounterFn<TValue = number> extends Counter<TValue> {}\n\n/**\n * Creates a callable counter object with increment, decrement and reset helpers.\n *\n * The returned function increments the internal numeric counter when called and\n * exposes both the raw counter value and an optionally transformed `value`.\n *\n * @template TValue Public value type returned by the counter.\n * @param processValue Optional mapper that transforms the numeric counter value.\n * @param initial Initial numeric counter value.\n * @returns Callable counter with state and control methods.\n *\n * @example\n * ```ts\n * const counter = createCounter();\n * counter.increment(); // 1\n * ```\n *\n * @example\n * ```ts\n * const idCounter = createCounter((value) => `id-${value}`, 10);\n * idCounter(); // 'id-11'\n * ```\n */\nexport const createCounter = <TValue = number>(\n processValue?: (value: number) => TValue,\n initial: number = 0,\n): Counter<TValue> => {\n const update = (counter: number) => {\n fn.value = processValue?.(counter) ?? (counter as TValue);\n return fn.value;\n };\n\n const increment = () => update(++fn.counter);\n const decrement = () => update(--fn.counter);\n\n const fn: Counter<TValue> = increment as any;\n\n fn.increment = increment;\n fn.decrement = decrement;\n\n fn.reset = () => {\n fn.counter = initial;\n fn.value = processValue?.(initial) ?? (initial as TValue);\n };\n\n fn.reset();\n\n return fn as Counter<TValue>;\n};\n","/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * Minimal **global or scoped storage** with `get` / `set` / `unset` semantics. When an access symbol\n * is provided the value lives on `globalThis`; otherwise it is isolated in a closure. Handy for\n * bridging non-React singletons, test doubles, or legacy globals without `window.foo =` sprawl.\n *\n * ## Usage\n *\n * ```ts\n * import { createGlobalPoint } from \"yummies/complex\";\n * ```\n */\n\nimport type { AnyObject } from 'yummies/types';\n\nexport interface GlobalPoint<TValue> {\n get(): TValue;\n set(value: TValue): TValue;\n unset(): void;\n}\n\n/**\n * Creates a simple storage point that can live either in `globalThis` under a\n * provided key or in a local closure when no key is given.\n *\n * @template TValue Stored value type.\n * @param accessSymbol Optional global property name used for storage.\n * @returns Getter/setter API for the stored value.\n *\n * @example\n * ```ts\n * const point = createGlobalPoint<number>();\n * point.set(10);\n * ```\n *\n * @example\n * ```ts\n * const point = createGlobalPoint<string>('__token__');\n * point.get();\n * ```\n */\nexport const createGlobalPoint = <TValue>(\n accessSymbol?: keyof any,\n): GlobalPoint<TValue> => {\n if (accessSymbol == null) {\n let storedValue: TValue | undefined;\n\n return {\n get: (): TValue => storedValue!,\n unset: () => {\n storedValue = undefined;\n },\n set: (value: TValue): TValue => {\n storedValue = value;\n return value;\n },\n };\n }\n\n const _globalThis = globalThis as AnyObject;\n\n return {\n get: (): TValue => _globalThis[accessSymbol],\n unset: () => {\n delete _globalThis[accessSymbol];\n },\n set: (value: TValue): TValue => {\n _globalThis[accessSymbol] = value;\n return value;\n },\n };\n};\n","/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * **Singleton-style config** backed by either `globalThis` (keyed by symbol) or a module closure.\n * Use for app-wide feature flags, bootstrapped locale, or SSR-safe defaults without prop-drilling.\n * Pairs with `createGlobalPoint` for other global handles in the same `yummies/complex` package.\n *\n * ## Usage\n *\n * ```ts\n * import { createGlobalConfig } from \"yummies/complex\";\n * ```\n */\n\nimport type { AnyObject, Maybe } from 'yummies/types';\nimport { createGlobalPoint } from './global-point.js';\n\n/**\n * Creates or reuses a globally accessible config object.\n *\n * The config is stored in a global point identified by `accessSymbol`, or in a\n * local closure when no symbol is provided.\n *\n * @template T Config object type.\n * @param defaultValue Default value used when no config has been created yet.\n * @param accessSymbol Optional global key used to store the config.\n * @returns Existing or newly initialized global config object.\n *\n * @example\n * ```ts\n * const config = createGlobalConfig({ locale: 'en' }, '__app_config__');\n * ```\n *\n * @example\n * ```ts\n * const config = createGlobalConfig({ debug: false });\n * config.debug;\n * ```\n */\nexport const createGlobalConfig = <T extends AnyObject>(\n defaultValue: T,\n accessSymbol?: keyof any,\n): T => {\n const globalPoint = createGlobalPoint<T>(accessSymbol);\n return globalPoint.get() || globalPoint.set(defaultValue);\n};\n\nexport interface GlobalDynamicConfig<TValue extends AnyObject> {\n get(): TValue;\n set(value: TValue): TValue;\n unset(): void;\n update(value: Partial<TValue>): void;\n}\n\n/**\n * Creates a mutable global config manager whose value is produced and updated\n * through a custom processor function.\n *\n * @template T Config object type.\n * @param processFn Function that builds the next config state from a partial change and current value.\n * @param accessSymbol Optional global key used to store the config.\n * @returns API for reading, replacing, resetting and partially updating the config.\n *\n * @example\n * ```ts\n * const config = createGlobalDynamicConfig(\n * (change, current) => ({ theme: 'light', ...current, ...change }),\n * '__theme__',\n * );\n * ```\n *\n * @example\n * ```ts\n * const config = createGlobalDynamicConfig((change, current) => ({ ...current, ...change }));\n * config.update({ locale: 'ru' });\n * ```\n */\nexport const createGlobalDynamicConfig = <T extends AnyObject>(\n processFn: (change: Maybe<Partial<T>>, current: Maybe<T>) => T,\n accessSymbol?: keyof any,\n): GlobalDynamicConfig<T> => {\n const globalPoint = createGlobalPoint<T>(accessSymbol);\n\n const getValue = () => {\n return globalPoint.get() ?? globalPoint.set(processFn(null, null))!;\n };\n\n return {\n get: getValue,\n set: globalPoint.set,\n unset: globalPoint.unset,\n update: (value: Partial<T>) => {\n const currentValue = getValue();\n Object.assign(currentValue, processFn(value, currentValue));\n },\n };\n};\n","/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * **Dependency-injection style factory** for classes that take a single deps object in their\n * constructor. Merges predefined singletons with per-call overrides so feature modules can be\n * constructed consistently in apps that avoid heavyweight DI containers.\n *\n * ## Usage\n *\n * ```ts\n * import { ModulesFactory } from \"yummies/complex\";\n * ```\n */\n\nimport type { AnyObject, Class, EmptyObject, IsPartial } from 'yummies/types';\n\ntype ModuleLoaderConfig<TPredefinedDeps extends AnyObject = EmptyObject> = {\n factory<TInstance, TDeps extends TPredefinedDeps>(\n moduleClass: Class<TInstance, [TDeps]>,\n deps: TDeps,\n ): TInstance;\n} & (TPredefinedDeps extends EmptyObject\n ? { deps?: TPredefinedDeps }\n : { deps: TPredefinedDeps });\n\n/**\n * Universal factory for creating class instances with predefined and per-call\n * dependencies.\n *\n * Works with classes whose constructor accepts a single dependency object.\n *\n * @template TPredefinedDeps Dependency shape that is always injected by the factory.\n *\n * @example\n * ```\n * const factory = new ModulesFactory({\n * factory: (MyClass, deps) => new MyClass(deps),\n * deps: { someDependency: new Dependency() }\n * });\n *\n * const instance = factory.create(MyClass, { extraDependency: new ExtraDependency() });\n * ```\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * });\n * ```\n *\n * @example\n * ```ts\n * const service = factory.create(UserService, { api });\n * ```\n */\nexport class ModulesFactory<TPredefinedDeps extends AnyObject = EmptyObject> {\n /**\n * Creates a new module factory.\n *\n * @param config Factory strategy and predefined dependencies.\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * });\n * ```\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * deps: { api },\n * });\n * ```\n */\n constructor(private config: ModuleLoaderConfig<TPredefinedDeps>) {}\n\n /**\n * Creates an instance of the provided class by merging predefined and\n * per-call dependencies.\n *\n * @template TInstance Instance type produced by the constructor.\n * @template TDeps Full dependency object expected by the constructor.\n * @param Constructor Class constructor receiving a single dependency object.\n * @param args Additional dependencies merged over predefined ones.\n * @returns Created class instance.\n *\n * @example\n * ```ts\n * const service = factory.create(UserService, { logger });\n * ```\n *\n * @example\n * ```ts\n * const store = factory.create(UserStore);\n * ```\n */\n create<TInstance, TDeps extends TPredefinedDeps = TPredefinedDeps>(\n Constructor: Class<TInstance, [TDeps]>,\n ...args: IsPartial<Omit<TDeps, keyof TPredefinedDeps>> extends true\n ? [extraDeps?: Omit<TDeps, keyof TPredefinedDeps>]\n : [extraDeps: Omit<TDeps, keyof TPredefinedDeps>]\n ) {\n return this.config.factory(Constructor, {\n ...this.config.deps!,\n ...args[0],\n } as any);\n }\n}\n","/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * Tiny **publish–subscribe** bus: subscribers register callbacks, publishers broadcast arguments, and\n * the last payload remains readable for late subscribers. Simpler than full event emitters when you\n * only need in-memory fan-out inside a feature or view-model layer.\n *\n * ## Usage\n *\n * ```ts\n * import { createPubSub } from \"yummies/complex\";\n * ```\n */\n\nexport type SubFn<PubArgs extends any[] = any[]> = (...args: PubArgs) => void;\n\n/**\n * The Publish-Subscribe pattern, which allows objects to interact with each other\n * through an event system. Subscribers can subscribe to events and receive notifications\n * when these events occur. The last published data can be accessed through the `data` property.\n */\nexport interface PubSub<PubArgs extends any[] = any[]> {\n (...args: PubArgs): void;\n\n /**\n * Last published arguments\n */\n lastPub: PubArgs | undefined;\n\n /**\n * An array of subscriber functions (sub) that will be called\n * when an event is published. Each subscriber must match the type SubFn,\n * taking the arguments that will be passed to it when the publisher calls pub.\n */\n subs: SubFn<PubArgs>[];\n\n /**\n * A function to unsubscribe from events. When a subscriber function (sub) is passed,\n * it will be removed from the `subs` array, and will no longer receive notifications.\n */\n unsub(sub: SubFn<PubArgs>): void;\n /**\n * A function to subscribe to events. When a subscriber function (sub) is passed,\n * it will be added to the `subs` array, and will receive notifications when the publisher calls pub.\n * Returns a function that can be used to unsubscribe from events.\n */\n sub(sub: SubFn<PubArgs>): VoidFunction;\n}\n\n/**\n * Creates a simple publish-subscribe dispatcher that stores the last published\n * arguments and allows subscription management.\n *\n * @template PubArgs Argument tuple delivered to subscribers.\n * @returns Callable publisher with subscribe and unsubscribe helpers.\n *\n * @example\n * ```ts\n * const pub = createPubSub<[string]>();\n * pub('ready');\n * ```\n *\n * @example\n * ```ts\n * const pub = createPubSub<[number]>();\n * const unsub = pub.sub((value) => console.log(value));\n * ```\n */\nexport const createPubSub = <PubArgs extends any[] = any[]>() => {\n const pubSub = ((...args: PubArgs) => {\n pubSub.lastPub = args;\n pubSub.subs.forEach((sub) => {\n sub(...args);\n });\n }) as PubSub<PubArgs>;\n pubSub.lastPub = undefined;\n\n pubSub.subs = [];\n\n pubSub.unsub = (sub: SubFn<PubArgs>) => {\n pubSub.subs = pubSub.subs.filter((it) => it !== sub);\n };\n pubSub.sub = (sub: SubFn<PubArgs>) => {\n pubSub.subs.push(sub);\n return () => pubSub.unsub(sub);\n };\n\n return pubSub;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,IAAa,iBACX,cACA,UAAkB,MACE;CACpB,MAAM,UAAU,YAAoB;AAClC,KAAG,QAAQ,eAAe,QAAQ,IAAK;AACvC,SAAO,GAAG;;CAGZ,MAAM,kBAAkB,OAAO,EAAE,GAAG,QAAQ;CAC5C,MAAM,kBAAkB,OAAO,EAAE,GAAG,QAAQ;CAE5C,MAAM,KAAsB;AAE5B,IAAG,YAAY;AACf,IAAG,YAAY;AAEf,IAAG,cAAc;AACf,KAAG,UAAU;AACb,KAAG,QAAQ,eAAe,QAAQ,IAAK;;AAGzC,IAAG,OAAO;AAEV,QAAO;;;;;;;;;;;;;;;;;;;;;;;;ACjCT,IAAa,qBACX,iBACwB;AACxB,KAAI,gBAAgB,MAAM;EACxB,IAAI;AAEJ,SAAO;GACL,WAAmB;GACnB,aAAa;AACX,kBAAc,KAAA;;GAEhB,MAAM,UAA0B;AAC9B,kBAAc;AACd,WAAO;;GAEV;;CAGH,MAAM,cAAc;AAEpB,QAAO;EACL,WAAmB,YAAY;EAC/B,aAAa;AACX,UAAO,YAAY;;EAErB,MAAM,UAA0B;AAC9B,eAAY,gBAAgB;AAC5B,UAAO;;EAEV;;;;;;;;;;;;;;;;;;;;;;;;;;AChCH,IAAa,sBACX,cACA,iBACM;CACN,MAAM,cAAc,kBAAqB,aAAa;AACtD,QAAO,YAAY,KAAK,IAAI,YAAY,IAAI,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;AAiC3D,IAAa,6BACX,WACA,iBAC2B;CAC3B,MAAM,cAAc,kBAAqB,aAAa;CAEtD,MAAM,iBAAiB;AACrB,SAAO,YAAY,KAAK,IAAI,YAAY,IAAI,UAAU,MAAM,KAAK,CAAC;;AAGpE,QAAO;EACL,KAAK;EACL,KAAK,YAAY;EACjB,OAAO,YAAY;EACnB,SAAS,UAAsB;GAC7B,MAAM,eAAe,UAAU;AAC/B,UAAO,OAAO,cAAc,UAAU,OAAO,aAAa,CAAC;;EAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxCH,IAAa,iBAAb,MAA6E;;;;;;;;;;;;;;;;;;;;;CAqB3E,YAAY,QAAqD;AAA7C,OAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;CAsBpB,OACE,aACA,GAAG,MAGH;AACA,SAAO,KAAK,OAAO,QAAQ,aAAa;GACtC,GAAG,KAAK,OAAO;GACf,GAAG,KAAK;GACT,CAAQ;;;;;;;;;;;;;;;;;;;;;;;;ACvCb,IAAa,qBAAoD;CAC/D,MAAM,WAAW,GAAG,SAAkB;AACpC,SAAO,UAAU;AACjB,SAAO,KAAK,SAAS,QAAQ;AAC3B,OAAI,GAAG,KAAK;IACZ;;AAEJ,QAAO,UAAU,KAAA;AAEjB,QAAO,OAAO,EAAE;AAEhB,QAAO,SAAS,QAAwB;AACtC,SAAO,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,IAAI;;AAEtD,QAAO,OAAO,QAAwB;AACpC,SAAO,KAAK,KAAK,IAAI;AACrB,eAAa,OAAO,MAAM,IAAI;;AAGhC,QAAO"}
|
package/complex.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { AnyObject, Maybe, EmptyObject, Class, IsPartial } from 'yummies/types';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* ---header-docs-section---
|
|
5
|
+
* # yummies/complex
|
|
6
|
+
*
|
|
7
|
+
* ## Description
|
|
8
|
+
*
|
|
9
|
+
* Callable **counter** objects with increment, decrement, and reset for UI steppers, pagination,
|
|
10
|
+
* and id generators. Values can be mapped on read so the public API stays typed while the internal
|
|
11
|
+
* tick stays numeric. This file is the `counter` slice of the broader `yummies/complex` entry point.
|
|
12
|
+
*
|
|
13
|
+
* ## Usage
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { createCounter } from "yummies/complex";
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
3
19
|
interface Counter<TValue = number> {
|
|
4
20
|
(): TValue;
|
|
5
21
|
counter: number;
|
|
@@ -38,6 +54,23 @@ interface CounterFn<TValue = number> extends Counter<TValue> {
|
|
|
38
54
|
*/
|
|
39
55
|
declare const createCounter: <TValue = number>(processValue?: (value: number) => TValue, initial?: number) => Counter<TValue>;
|
|
40
56
|
|
|
57
|
+
/**
|
|
58
|
+
* ---header-docs-section---
|
|
59
|
+
* # yummies/complex
|
|
60
|
+
*
|
|
61
|
+
* ## Description
|
|
62
|
+
*
|
|
63
|
+
* **Singleton-style config** backed by either `globalThis` (keyed by symbol) or a module closure.
|
|
64
|
+
* Use for app-wide feature flags, bootstrapped locale, or SSR-safe defaults without prop-drilling.
|
|
65
|
+
* Pairs with `createGlobalPoint` for other global handles in the same `yummies/complex` package.
|
|
66
|
+
*
|
|
67
|
+
* ## Usage
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { createGlobalConfig } from "yummies/complex";
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
|
|
41
74
|
/**
|
|
42
75
|
* Creates or reuses a globally accessible config object.
|
|
43
76
|
*
|
|
@@ -92,6 +125,23 @@ interface GlobalDynamicConfig<TValue extends AnyObject> {
|
|
|
92
125
|
*/
|
|
93
126
|
declare const createGlobalDynamicConfig: <T extends AnyObject>(processFn: (change: Maybe<Partial<T>>, current: Maybe<T>) => T, accessSymbol?: keyof any) => GlobalDynamicConfig<T>;
|
|
94
127
|
|
|
128
|
+
/**
|
|
129
|
+
* ---header-docs-section---
|
|
130
|
+
* # yummies/complex
|
|
131
|
+
*
|
|
132
|
+
* ## Description
|
|
133
|
+
*
|
|
134
|
+
* **Dependency-injection style factory** for classes that take a single deps object in their
|
|
135
|
+
* constructor. Merges predefined singletons with per-call overrides so feature modules can be
|
|
136
|
+
* constructed consistently in apps that avoid heavyweight DI containers.
|
|
137
|
+
*
|
|
138
|
+
* ## Usage
|
|
139
|
+
*
|
|
140
|
+
* ```ts
|
|
141
|
+
* import { ModulesFactory } from "yummies/complex";
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
|
|
95
145
|
type ModuleLoaderConfig<TPredefinedDeps extends AnyObject = EmptyObject> = {
|
|
96
146
|
factory<TInstance, TDeps extends TPredefinedDeps>(moduleClass: Class<TInstance, [TDeps]>, deps: TDeps): TInstance;
|
|
97
147
|
} & (TPredefinedDeps extends EmptyObject ? {
|
|
@@ -175,6 +225,22 @@ declare class ModulesFactory<TPredefinedDeps extends AnyObject = EmptyObject> {
|
|
|
175
225
|
create<TInstance, TDeps extends TPredefinedDeps = TPredefinedDeps>(Constructor: Class<TInstance, [TDeps]>, ...args: IsPartial<Omit<TDeps, keyof TPredefinedDeps>> extends true ? [extraDeps?: Omit<TDeps, keyof TPredefinedDeps>] : [extraDeps: Omit<TDeps, keyof TPredefinedDeps>]): TInstance;
|
|
176
226
|
}
|
|
177
227
|
|
|
228
|
+
/**
|
|
229
|
+
* ---header-docs-section---
|
|
230
|
+
* # yummies/complex
|
|
231
|
+
*
|
|
232
|
+
* ## Description
|
|
233
|
+
*
|
|
234
|
+
* Tiny **publish–subscribe** bus: subscribers register callbacks, publishers broadcast arguments, and
|
|
235
|
+
* the last payload remains readable for late subscribers. Simpler than full event emitters when you
|
|
236
|
+
* only need in-memory fan-out inside a feature or view-model layer.
|
|
237
|
+
*
|
|
238
|
+
* ## Usage
|
|
239
|
+
*
|
|
240
|
+
* ```ts
|
|
241
|
+
* import { createPubSub } from "yummies/complex";
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
178
244
|
type SubFn<PubArgs extends any[] = any[]> = (...args: PubArgs) => void;
|
|
179
245
|
/**
|
|
180
246
|
* The Publish-Subscribe pattern, which allows objects to interact with each other
|
package/complex.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"complex.js","names":[],"sources":["../src/complex/counter.ts","../src/complex/global-point.ts","../src/complex/global-config.ts","../src/complex/modules-factory.ts","../src/complex/pub-sub.ts"],"sourcesContent":["export interface Counter<TValue = number> {\n (): TValue;\n counter: number;\n value: TValue;\n increment(): TValue;\n decrement(): TValue;\n reset(): void;\n}\n\n/**\n * @deprecated use {`Counter`}. Will be removed in next major release\n */\nexport interface CounterFn<TValue = number> extends Counter<TValue> {}\n\n/**\n * Creates a callable counter object with increment, decrement and reset helpers.\n *\n * The returned function increments the internal numeric counter when called and\n * exposes both the raw counter value and an optionally transformed `value`.\n *\n * @template TValue Public value type returned by the counter.\n * @param processValue Optional mapper that transforms the numeric counter value.\n * @param initial Initial numeric counter value.\n * @returns Callable counter with state and control methods.\n *\n * @example\n * ```ts\n * const counter = createCounter();\n * counter.increment(); // 1\n * ```\n *\n * @example\n * ```ts\n * const idCounter = createCounter((value) => `id-${value}`, 10);\n * idCounter(); // 'id-11'\n * ```\n */\nexport const createCounter = <TValue = number>(\n processValue?: (value: number) => TValue,\n initial: number = 0,\n): Counter<TValue> => {\n const update = (counter: number) => {\n fn.value = processValue?.(counter) ?? (counter as TValue);\n return fn.value;\n };\n\n const increment = () => update(++fn.counter);\n const decrement = () => update(--fn.counter);\n\n const fn: Counter<TValue> = increment as any;\n\n fn.increment = increment;\n fn.decrement = decrement;\n\n fn.reset = () => {\n fn.counter = initial;\n fn.value = processValue?.(initial) ?? (initial as TValue);\n };\n\n fn.reset();\n\n return fn as Counter<TValue>;\n};\n","import type { AnyObject } from 'yummies/types';\n\nexport interface GlobalPoint<TValue> {\n get(): TValue;\n set(value: TValue): TValue;\n unset(): void;\n}\n\n/**\n * Creates a simple storage point that can live either in `globalThis` under a\n * provided key or in a local closure when no key is given.\n *\n * @template TValue Stored value type.\n * @param accessSymbol Optional global property name used for storage.\n * @returns Getter/setter API for the stored value.\n *\n * @example\n * ```ts\n * const point = createGlobalPoint<number>();\n * point.set(10);\n * ```\n *\n * @example\n * ```ts\n * const point = createGlobalPoint<string>('__token__');\n * point.get();\n * ```\n */\nexport const createGlobalPoint = <TValue>(\n accessSymbol?: keyof any,\n): GlobalPoint<TValue> => {\n if (accessSymbol == null) {\n let storedValue: TValue | undefined;\n\n return {\n get: (): TValue => storedValue!,\n unset: () => {\n storedValue = undefined;\n },\n set: (value: TValue): TValue => {\n storedValue = value;\n return value;\n },\n };\n }\n\n const _globalThis = globalThis as AnyObject;\n\n return {\n get: (): TValue => _globalThis[accessSymbol],\n unset: () => {\n delete _globalThis[accessSymbol];\n },\n set: (value: TValue): TValue => {\n _globalThis[accessSymbol] = value;\n return value;\n },\n };\n};\n","import type { AnyObject, Maybe } from 'yummies/types';\nimport { createGlobalPoint } from './global-point.js';\n\n/**\n * Creates or reuses a globally accessible config object.\n *\n * The config is stored in a global point identified by `accessSymbol`, or in a\n * local closure when no symbol is provided.\n *\n * @template T Config object type.\n * @param defaultValue Default value used when no config has been created yet.\n * @param accessSymbol Optional global key used to store the config.\n * @returns Existing or newly initialized global config object.\n *\n * @example\n * ```ts\n * const config = createGlobalConfig({ locale: 'en' }, '__app_config__');\n * ```\n *\n * @example\n * ```ts\n * const config = createGlobalConfig({ debug: false });\n * config.debug;\n * ```\n */\nexport const createGlobalConfig = <T extends AnyObject>(\n defaultValue: T,\n accessSymbol?: keyof any,\n): T => {\n const globalPoint = createGlobalPoint<T>(accessSymbol);\n return globalPoint.get() || globalPoint.set(defaultValue);\n};\n\nexport interface GlobalDynamicConfig<TValue extends AnyObject> {\n get(): TValue;\n set(value: TValue): TValue;\n unset(): void;\n update(value: Partial<TValue>): void;\n}\n\n/**\n * Creates a mutable global config manager whose value is produced and updated\n * through a custom processor function.\n *\n * @template T Config object type.\n * @param processFn Function that builds the next config state from a partial change and current value.\n * @param accessSymbol Optional global key used to store the config.\n * @returns API for reading, replacing, resetting and partially updating the config.\n *\n * @example\n * ```ts\n * const config = createGlobalDynamicConfig(\n * (change, current) => ({ theme: 'light', ...current, ...change }),\n * '__theme__',\n * );\n * ```\n *\n * @example\n * ```ts\n * const config = createGlobalDynamicConfig((change, current) => ({ ...current, ...change }));\n * config.update({ locale: 'ru' });\n * ```\n */\nexport const createGlobalDynamicConfig = <T extends AnyObject>(\n processFn: (change: Maybe<Partial<T>>, current: Maybe<T>) => T,\n accessSymbol?: keyof any,\n): GlobalDynamicConfig<T> => {\n const globalPoint = createGlobalPoint<T>(accessSymbol);\n\n const getValue = () => {\n return globalPoint.get() ?? globalPoint.set(processFn(null, null))!;\n };\n\n return {\n get: getValue,\n set: globalPoint.set,\n unset: globalPoint.unset,\n update: (value: Partial<T>) => {\n const currentValue = getValue();\n Object.assign(currentValue, processFn(value, currentValue));\n },\n };\n};\n","import type { AnyObject, Class, EmptyObject, IsPartial } from 'yummies/types';\n\ntype ModuleLoaderConfig<TPredefinedDeps extends AnyObject = EmptyObject> = {\n factory<TInstance, TDeps extends TPredefinedDeps>(\n moduleClass: Class<TInstance, [TDeps]>,\n deps: TDeps,\n ): TInstance;\n} & (TPredefinedDeps extends EmptyObject\n ? { deps?: TPredefinedDeps }\n : { deps: TPredefinedDeps });\n\n/**\n * Universal factory for creating class instances with predefined and per-call\n * dependencies.\n *\n * Works with classes whose constructor accepts a single dependency object.\n *\n * @template TPredefinedDeps Dependency shape that is always injected by the factory.\n *\n * @example\n * ```\n * const factory = new ModulesFactory({\n * factory: (MyClass, deps) => new MyClass(deps),\n * deps: { someDependency: new Dependency() }\n * });\n *\n * const instance = factory.create(MyClass, { extraDependency: new ExtraDependency() });\n * ```\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * });\n * ```\n *\n * @example\n * ```ts\n * const service = factory.create(UserService, { api });\n * ```\n */\nexport class ModulesFactory<TPredefinedDeps extends AnyObject = EmptyObject> {\n /**\n * Creates a new module factory.\n *\n * @param config Factory strategy and predefined dependencies.\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * });\n * ```\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * deps: { api },\n * });\n * ```\n */\n constructor(private config: ModuleLoaderConfig<TPredefinedDeps>) {}\n\n /**\n * Creates an instance of the provided class by merging predefined and\n * per-call dependencies.\n *\n * @template TInstance Instance type produced by the constructor.\n * @template TDeps Full dependency object expected by the constructor.\n * @param Constructor Class constructor receiving a single dependency object.\n * @param args Additional dependencies merged over predefined ones.\n * @returns Created class instance.\n *\n * @example\n * ```ts\n * const service = factory.create(UserService, { logger });\n * ```\n *\n * @example\n * ```ts\n * const store = factory.create(UserStore);\n * ```\n */\n create<TInstance, TDeps extends TPredefinedDeps = TPredefinedDeps>(\n Constructor: Class<TInstance, [TDeps]>,\n ...args: IsPartial<Omit<TDeps, keyof TPredefinedDeps>> extends true\n ? [extraDeps?: Omit<TDeps, keyof TPredefinedDeps>]\n : [extraDeps: Omit<TDeps, keyof TPredefinedDeps>]\n ) {\n return this.config.factory(Constructor, {\n ...this.config.deps!,\n ...args[0],\n } as any);\n }\n}\n","export type SubFn<PubArgs extends any[] = any[]> = (...args: PubArgs) => void;\n\n/**\n * The Publish-Subscribe pattern, which allows objects to interact with each other\n * through an event system. Subscribers can subscribe to events and receive notifications\n * when these events occur. The last published data can be accessed through the `data` property.\n */\nexport interface PubSub<PubArgs extends any[] = any[]> {\n (...args: PubArgs): void;\n\n /**\n * Last published arguments\n */\n lastPub: PubArgs | undefined;\n\n /**\n * An array of subscriber functions (sub) that will be called\n * when an event is published. Each subscriber must match the type SubFn,\n * taking the arguments that will be passed to it when the publisher calls pub.\n */\n subs: SubFn<PubArgs>[];\n\n /**\n * A function to unsubscribe from events. When a subscriber function (sub) is passed,\n * it will be removed from the `subs` array, and will no longer receive notifications.\n */\n unsub(sub: SubFn<PubArgs>): void;\n /**\n * A function to subscribe to events. When a subscriber function (sub) is passed,\n * it will be added to the `subs` array, and will receive notifications when the publisher calls pub.\n * Returns a function that can be used to unsubscribe from events.\n */\n sub(sub: SubFn<PubArgs>): VoidFunction;\n}\n\n/**\n * Creates a simple publish-subscribe dispatcher that stores the last published\n * arguments and allows subscription management.\n *\n * @template PubArgs Argument tuple delivered to subscribers.\n * @returns Callable publisher with subscribe and unsubscribe helpers.\n *\n * @example\n * ```ts\n * const pub = createPubSub<[string]>();\n * pub('ready');\n * ```\n *\n * @example\n * ```ts\n * const pub = createPubSub<[number]>();\n * const unsub = pub.sub((value) => console.log(value));\n * ```\n */\nexport const createPubSub = <PubArgs extends any[] = any[]>() => {\n const pubSub = ((...args: PubArgs) => {\n pubSub.lastPub = args;\n pubSub.subs.forEach((sub) => {\n sub(...args);\n });\n }) as PubSub<PubArgs>;\n pubSub.lastPub = undefined;\n\n pubSub.subs = [];\n\n pubSub.unsub = (sub: SubFn<PubArgs>) => {\n pubSub.subs = pubSub.subs.filter((it) => it !== sub);\n };\n pubSub.sub = (sub: SubFn<PubArgs>) => {\n pubSub.subs.push(sub);\n return () => pubSub.unsub(sub);\n };\n\n return pubSub;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAqCA,IAAa,iBACX,cACA,UAAkB,MACE;CACpB,MAAM,UAAU,YAAoB;AAClC,KAAG,QAAQ,eAAe,QAAQ,IAAK;AACvC,SAAO,GAAG;;CAGZ,MAAM,kBAAkB,OAAO,EAAE,GAAG,QAAQ;CAC5C,MAAM,kBAAkB,OAAO,EAAE,GAAG,QAAQ;CAE5C,MAAM,KAAsB;AAE5B,IAAG,YAAY;AACf,IAAG,YAAY;AAEf,IAAG,cAAc;AACf,KAAG,UAAU;AACb,KAAG,QAAQ,eAAe,QAAQ,IAAK;;AAGzC,IAAG,OAAO;AAEV,QAAO;;;;;;;;;;;;;;;;;;;;;;;;ACjCT,IAAa,qBACX,iBACwB;AACxB,KAAI,gBAAgB,MAAM;EACxB,IAAI;AAEJ,SAAO;GACL,WAAmB;GACnB,aAAa;AACX,kBAAc,KAAA;;GAEhB,MAAM,UAA0B;AAC9B,kBAAc;AACd,WAAO;;GAEV;;CAGH,MAAM,cAAc;AAEpB,QAAO;EACL,WAAmB,YAAY;EAC/B,aAAa;AACX,UAAO,YAAY;;EAErB,MAAM,UAA0B;AAC9B,eAAY,gBAAgB;AAC5B,UAAO;;EAEV;;;;;;;;;;;;;;;;;;;;;;;;;;AChCH,IAAa,sBACX,cACA,iBACM;CACN,MAAM,cAAc,kBAAqB,aAAa;AACtD,QAAO,YAAY,KAAK,IAAI,YAAY,IAAI,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;AAiC3D,IAAa,6BACX,WACA,iBAC2B;CAC3B,MAAM,cAAc,kBAAqB,aAAa;CAEtD,MAAM,iBAAiB;AACrB,SAAO,YAAY,KAAK,IAAI,YAAY,IAAI,UAAU,MAAM,KAAK,CAAC;;AAGpE,QAAO;EACL,KAAK;EACL,KAAK,YAAY;EACjB,OAAO,YAAY;EACnB,SAAS,UAAsB;GAC7B,MAAM,eAAe,UAAU;AAC/B,UAAO,OAAO,cAAc,UAAU,OAAO,aAAa,CAAC;;EAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxCH,IAAa,iBAAb,MAA6E;;;;;;;;;;;;;;;;;;;;;CAqB3E,YAAY,QAAqD;AAA7C,OAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;CAsBpB,OACE,aACA,GAAG,MAGH;AACA,SAAO,KAAK,OAAO,QAAQ,aAAa;GACtC,GAAG,KAAK,OAAO;GACf,GAAG,KAAK;GACT,CAAQ;;;;;;;;;;;;;;;;;;;;;;;;ACvCb,IAAa,qBAAoD;CAC/D,MAAM,WAAW,GAAG,SAAkB;AACpC,SAAO,UAAU;AACjB,SAAO,KAAK,SAAS,QAAQ;AAC3B,OAAI,GAAG,KAAK;IACZ;;AAEJ,QAAO,UAAU,KAAA;AAEjB,QAAO,OAAO,EAAE;AAEhB,QAAO,SAAS,QAAwB;AACtC,SAAO,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,IAAI;;AAEtD,QAAO,OAAO,QAAwB;AACpC,SAAO,KAAK,KAAK,IAAI;AACrB,eAAa,OAAO,MAAM,IAAI;;AAGhC,QAAO"}
|
|
1
|
+
{"version":3,"file":"complex.js","names":[],"sources":["../src/complex/counter.ts","../src/complex/global-point.ts","../src/complex/global-config.ts","../src/complex/modules-factory.ts","../src/complex/pub-sub.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * Callable **counter** objects with increment, decrement, and reset for UI steppers, pagination,\n * and id generators. Values can be mapped on read so the public API stays typed while the internal\n * tick stays numeric. This file is the `counter` slice of the broader `yummies/complex` entry point.\n *\n * ## Usage\n *\n * ```ts\n * import { createCounter } from \"yummies/complex\";\n * ```\n */\n\nexport interface Counter<TValue = number> {\n (): TValue;\n counter: number;\n value: TValue;\n increment(): TValue;\n decrement(): TValue;\n reset(): void;\n}\n\n/**\n * @deprecated use {`Counter`}. Will be removed in next major release\n */\nexport interface CounterFn<TValue = number> extends Counter<TValue> {}\n\n/**\n * Creates a callable counter object with increment, decrement and reset helpers.\n *\n * The returned function increments the internal numeric counter when called and\n * exposes both the raw counter value and an optionally transformed `value`.\n *\n * @template TValue Public value type returned by the counter.\n * @param processValue Optional mapper that transforms the numeric counter value.\n * @param initial Initial numeric counter value.\n * @returns Callable counter with state and control methods.\n *\n * @example\n * ```ts\n * const counter = createCounter();\n * counter.increment(); // 1\n * ```\n *\n * @example\n * ```ts\n * const idCounter = createCounter((value) => `id-${value}`, 10);\n * idCounter(); // 'id-11'\n * ```\n */\nexport const createCounter = <TValue = number>(\n processValue?: (value: number) => TValue,\n initial: number = 0,\n): Counter<TValue> => {\n const update = (counter: number) => {\n fn.value = processValue?.(counter) ?? (counter as TValue);\n return fn.value;\n };\n\n const increment = () => update(++fn.counter);\n const decrement = () => update(--fn.counter);\n\n const fn: Counter<TValue> = increment as any;\n\n fn.increment = increment;\n fn.decrement = decrement;\n\n fn.reset = () => {\n fn.counter = initial;\n fn.value = processValue?.(initial) ?? (initial as TValue);\n };\n\n fn.reset();\n\n return fn as Counter<TValue>;\n};\n","/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * Minimal **global or scoped storage** with `get` / `set` / `unset` semantics. When an access symbol\n * is provided the value lives on `globalThis`; otherwise it is isolated in a closure. Handy for\n * bridging non-React singletons, test doubles, or legacy globals without `window.foo =` sprawl.\n *\n * ## Usage\n *\n * ```ts\n * import { createGlobalPoint } from \"yummies/complex\";\n * ```\n */\n\nimport type { AnyObject } from 'yummies/types';\n\nexport interface GlobalPoint<TValue> {\n get(): TValue;\n set(value: TValue): TValue;\n unset(): void;\n}\n\n/**\n * Creates a simple storage point that can live either in `globalThis` under a\n * provided key or in a local closure when no key is given.\n *\n * @template TValue Stored value type.\n * @param accessSymbol Optional global property name used for storage.\n * @returns Getter/setter API for the stored value.\n *\n * @example\n * ```ts\n * const point = createGlobalPoint<number>();\n * point.set(10);\n * ```\n *\n * @example\n * ```ts\n * const point = createGlobalPoint<string>('__token__');\n * point.get();\n * ```\n */\nexport const createGlobalPoint = <TValue>(\n accessSymbol?: keyof any,\n): GlobalPoint<TValue> => {\n if (accessSymbol == null) {\n let storedValue: TValue | undefined;\n\n return {\n get: (): TValue => storedValue!,\n unset: () => {\n storedValue = undefined;\n },\n set: (value: TValue): TValue => {\n storedValue = value;\n return value;\n },\n };\n }\n\n const _globalThis = globalThis as AnyObject;\n\n return {\n get: (): TValue => _globalThis[accessSymbol],\n unset: () => {\n delete _globalThis[accessSymbol];\n },\n set: (value: TValue): TValue => {\n _globalThis[accessSymbol] = value;\n return value;\n },\n };\n};\n","/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * **Singleton-style config** backed by either `globalThis` (keyed by symbol) or a module closure.\n * Use for app-wide feature flags, bootstrapped locale, or SSR-safe defaults without prop-drilling.\n * Pairs with `createGlobalPoint` for other global handles in the same `yummies/complex` package.\n *\n * ## Usage\n *\n * ```ts\n * import { createGlobalConfig } from \"yummies/complex\";\n * ```\n */\n\nimport type { AnyObject, Maybe } from 'yummies/types';\nimport { createGlobalPoint } from './global-point.js';\n\n/**\n * Creates or reuses a globally accessible config object.\n *\n * The config is stored in a global point identified by `accessSymbol`, or in a\n * local closure when no symbol is provided.\n *\n * @template T Config object type.\n * @param defaultValue Default value used when no config has been created yet.\n * @param accessSymbol Optional global key used to store the config.\n * @returns Existing or newly initialized global config object.\n *\n * @example\n * ```ts\n * const config = createGlobalConfig({ locale: 'en' }, '__app_config__');\n * ```\n *\n * @example\n * ```ts\n * const config = createGlobalConfig({ debug: false });\n * config.debug;\n * ```\n */\nexport const createGlobalConfig = <T extends AnyObject>(\n defaultValue: T,\n accessSymbol?: keyof any,\n): T => {\n const globalPoint = createGlobalPoint<T>(accessSymbol);\n return globalPoint.get() || globalPoint.set(defaultValue);\n};\n\nexport interface GlobalDynamicConfig<TValue extends AnyObject> {\n get(): TValue;\n set(value: TValue): TValue;\n unset(): void;\n update(value: Partial<TValue>): void;\n}\n\n/**\n * Creates a mutable global config manager whose value is produced and updated\n * through a custom processor function.\n *\n * @template T Config object type.\n * @param processFn Function that builds the next config state from a partial change and current value.\n * @param accessSymbol Optional global key used to store the config.\n * @returns API for reading, replacing, resetting and partially updating the config.\n *\n * @example\n * ```ts\n * const config = createGlobalDynamicConfig(\n * (change, current) => ({ theme: 'light', ...current, ...change }),\n * '__theme__',\n * );\n * ```\n *\n * @example\n * ```ts\n * const config = createGlobalDynamicConfig((change, current) => ({ ...current, ...change }));\n * config.update({ locale: 'ru' });\n * ```\n */\nexport const createGlobalDynamicConfig = <T extends AnyObject>(\n processFn: (change: Maybe<Partial<T>>, current: Maybe<T>) => T,\n accessSymbol?: keyof any,\n): GlobalDynamicConfig<T> => {\n const globalPoint = createGlobalPoint<T>(accessSymbol);\n\n const getValue = () => {\n return globalPoint.get() ?? globalPoint.set(processFn(null, null))!;\n };\n\n return {\n get: getValue,\n set: globalPoint.set,\n unset: globalPoint.unset,\n update: (value: Partial<T>) => {\n const currentValue = getValue();\n Object.assign(currentValue, processFn(value, currentValue));\n },\n };\n};\n","/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * **Dependency-injection style factory** for classes that take a single deps object in their\n * constructor. Merges predefined singletons with per-call overrides so feature modules can be\n * constructed consistently in apps that avoid heavyweight DI containers.\n *\n * ## Usage\n *\n * ```ts\n * import { ModulesFactory } from \"yummies/complex\";\n * ```\n */\n\nimport type { AnyObject, Class, EmptyObject, IsPartial } from 'yummies/types';\n\ntype ModuleLoaderConfig<TPredefinedDeps extends AnyObject = EmptyObject> = {\n factory<TInstance, TDeps extends TPredefinedDeps>(\n moduleClass: Class<TInstance, [TDeps]>,\n deps: TDeps,\n ): TInstance;\n} & (TPredefinedDeps extends EmptyObject\n ? { deps?: TPredefinedDeps }\n : { deps: TPredefinedDeps });\n\n/**\n * Universal factory for creating class instances with predefined and per-call\n * dependencies.\n *\n * Works with classes whose constructor accepts a single dependency object.\n *\n * @template TPredefinedDeps Dependency shape that is always injected by the factory.\n *\n * @example\n * ```\n * const factory = new ModulesFactory({\n * factory: (MyClass, deps) => new MyClass(deps),\n * deps: { someDependency: new Dependency() }\n * });\n *\n * const instance = factory.create(MyClass, { extraDependency: new ExtraDependency() });\n * ```\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * });\n * ```\n *\n * @example\n * ```ts\n * const service = factory.create(UserService, { api });\n * ```\n */\nexport class ModulesFactory<TPredefinedDeps extends AnyObject = EmptyObject> {\n /**\n * Creates a new module factory.\n *\n * @param config Factory strategy and predefined dependencies.\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * });\n * ```\n *\n * @example\n * ```ts\n * const factory = new ModulesFactory({\n * factory: (Module, deps) => new Module(deps),\n * deps: { api },\n * });\n * ```\n */\n constructor(private config: ModuleLoaderConfig<TPredefinedDeps>) {}\n\n /**\n * Creates an instance of the provided class by merging predefined and\n * per-call dependencies.\n *\n * @template TInstance Instance type produced by the constructor.\n * @template TDeps Full dependency object expected by the constructor.\n * @param Constructor Class constructor receiving a single dependency object.\n * @param args Additional dependencies merged over predefined ones.\n * @returns Created class instance.\n *\n * @example\n * ```ts\n * const service = factory.create(UserService, { logger });\n * ```\n *\n * @example\n * ```ts\n * const store = factory.create(UserStore);\n * ```\n */\n create<TInstance, TDeps extends TPredefinedDeps = TPredefinedDeps>(\n Constructor: Class<TInstance, [TDeps]>,\n ...args: IsPartial<Omit<TDeps, keyof TPredefinedDeps>> extends true\n ? [extraDeps?: Omit<TDeps, keyof TPredefinedDeps>]\n : [extraDeps: Omit<TDeps, keyof TPredefinedDeps>]\n ) {\n return this.config.factory(Constructor, {\n ...this.config.deps!,\n ...args[0],\n } as any);\n }\n}\n","/**\n * ---header-docs-section---\n * # yummies/complex\n *\n * ## Description\n *\n * Tiny **publish–subscribe** bus: subscribers register callbacks, publishers broadcast arguments, and\n * the last payload remains readable for late subscribers. Simpler than full event emitters when you\n * only need in-memory fan-out inside a feature or view-model layer.\n *\n * ## Usage\n *\n * ```ts\n * import { createPubSub } from \"yummies/complex\";\n * ```\n */\n\nexport type SubFn<PubArgs extends any[] = any[]> = (...args: PubArgs) => void;\n\n/**\n * The Publish-Subscribe pattern, which allows objects to interact with each other\n * through an event system. Subscribers can subscribe to events and receive notifications\n * when these events occur. The last published data can be accessed through the `data` property.\n */\nexport interface PubSub<PubArgs extends any[] = any[]> {\n (...args: PubArgs): void;\n\n /**\n * Last published arguments\n */\n lastPub: PubArgs | undefined;\n\n /**\n * An array of subscriber functions (sub) that will be called\n * when an event is published. Each subscriber must match the type SubFn,\n * taking the arguments that will be passed to it when the publisher calls pub.\n */\n subs: SubFn<PubArgs>[];\n\n /**\n * A function to unsubscribe from events. When a subscriber function (sub) is passed,\n * it will be removed from the `subs` array, and will no longer receive notifications.\n */\n unsub(sub: SubFn<PubArgs>): void;\n /**\n * A function to subscribe to events. When a subscriber function (sub) is passed,\n * it will be added to the `subs` array, and will receive notifications when the publisher calls pub.\n * Returns a function that can be used to unsubscribe from events.\n */\n sub(sub: SubFn<PubArgs>): VoidFunction;\n}\n\n/**\n * Creates a simple publish-subscribe dispatcher that stores the last published\n * arguments and allows subscription management.\n *\n * @template PubArgs Argument tuple delivered to subscribers.\n * @returns Callable publisher with subscribe and unsubscribe helpers.\n *\n * @example\n * ```ts\n * const pub = createPubSub<[string]>();\n * pub('ready');\n * ```\n *\n * @example\n * ```ts\n * const pub = createPubSub<[number]>();\n * const unsub = pub.sub((value) => console.log(value));\n * ```\n */\nexport const createPubSub = <PubArgs extends any[] = any[]>() => {\n const pubSub = ((...args: PubArgs) => {\n pubSub.lastPub = args;\n pubSub.subs.forEach((sub) => {\n sub(...args);\n });\n }) as PubSub<PubArgs>;\n pubSub.lastPub = undefined;\n\n pubSub.subs = [];\n\n pubSub.unsub = (sub: SubFn<PubArgs>) => {\n pubSub.subs = pubSub.subs.filter((it) => it !== sub);\n };\n pubSub.sub = (sub: SubFn<PubArgs>) => {\n pubSub.subs.push(sub);\n return () => pubSub.unsub(sub);\n };\n\n return pubSub;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAsDA,IAAa,iBACX,cACA,UAAkB,MACE;CACpB,MAAM,UAAU,YAAoB;AAClC,KAAG,QAAQ,eAAe,QAAQ,IAAK;AACvC,SAAO,GAAG;;CAGZ,MAAM,kBAAkB,OAAO,EAAE,GAAG,QAAQ;CAC5C,MAAM,kBAAkB,OAAO,EAAE,GAAG,QAAQ;CAE5C,MAAM,KAAsB;AAE5B,IAAG,YAAY;AACf,IAAG,YAAY;AAEf,IAAG,cAAc;AACf,KAAG,UAAU;AACb,KAAG,QAAQ,eAAe,QAAQ,IAAK;;AAGzC,IAAG,OAAO;AAEV,QAAO;;;;;;;;;;;;;;;;;;;;;;;;ACjCT,IAAa,qBACX,iBACwB;AACxB,KAAI,gBAAgB,MAAM;EACxB,IAAI;AAEJ,SAAO;GACL,WAAmB;GACnB,aAAa;AACX,kBAAc,KAAA;;GAEhB,MAAM,UAA0B;AAC9B,kBAAc;AACd,WAAO;;GAEV;;CAGH,MAAM,cAAc;AAEpB,QAAO;EACL,WAAmB,YAAY;EAC/B,aAAa;AACX,UAAO,YAAY;;EAErB,MAAM,UAA0B;AAC9B,eAAY,gBAAgB;AAC5B,UAAO;;EAEV;;;;;;;;;;;;;;;;;;;;;;;;;;AChCH,IAAa,sBACX,cACA,iBACM;CACN,MAAM,cAAc,kBAAqB,aAAa;AACtD,QAAO,YAAY,KAAK,IAAI,YAAY,IAAI,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;AAiC3D,IAAa,6BACX,WACA,iBAC2B;CAC3B,MAAM,cAAc,kBAAqB,aAAa;CAEtD,MAAM,iBAAiB;AACrB,SAAO,YAAY,KAAK,IAAI,YAAY,IAAI,UAAU,MAAM,KAAK,CAAC;;AAGpE,QAAO;EACL,KAAK;EACL,KAAK,YAAY;EACjB,OAAO,YAAY;EACnB,SAAS,UAAsB;GAC7B,MAAM,eAAe,UAAU;AAC/B,UAAO,OAAO,cAAc,UAAU,OAAO,aAAa,CAAC;;EAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxCH,IAAa,iBAAb,MAA6E;;;;;;;;;;;;;;;;;;;;;CAqB3E,YAAY,QAAqD;AAA7C,OAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;CAsBpB,OACE,aACA,GAAG,MAGH;AACA,SAAO,KAAK,OAAO,QAAQ,aAAa;GACtC,GAAG,KAAK,OAAO;GACf,GAAG,KAAK;GACT,CAAQ;;;;;;;;;;;;;;;;;;;;;;;;ACvCb,IAAa,qBAAoD;CAC/D,MAAM,WAAW,GAAG,SAAkB;AACpC,SAAO,UAAU;AACjB,SAAO,KAAK,SAAS,QAAQ;AAC3B,OAAI,GAAG,KAAK;IACZ;;AAEJ,QAAO,UAAU,KAAA;AAEjB,QAAO,OAAO,EAAE;AAEhB,QAAO,SAAS,QAAwB;AACtC,SAAO,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,IAAI;;AAEtD,QAAO,OAAO,QAAwB;AACpC,SAAO,KAAK,KAAK,IAAI;AACrB,eAAa,OAAO,MAAM,IAAI;;AAGhC,QAAO"}
|
package/cookie.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie.cjs","names":[],"sources":["../src/cookie.ts"],"sourcesContent":["import type { AnyObject } from 'yummies/types';\n\n/**\n * Parses a cookie string into an object with cookie names as keys.\n *\n * @example\n * ```ts\n * parseCookie('theme=dark; token=abc');\n * ```\n */\nexport const parseCookie = (cookiesString = document.cookie) => {\n return cookiesString\n .split(';')\n .map((cookieString) => cookieString.trim().split('='))\n .reduce<AnyObject>((acc, current) => {\n acc[current[0]] = current[1];\n return acc;\n }, {});\n};\n"],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"cookie.cjs","names":[],"sources":["../src/cookie.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/cookie\n *\n * ## Description\n *\n * Minimal helpers for reading the browser `document.cookie` string as a plain key/value map. This\n * avoids hand-rolled splitting and trimming at every call site and keeps parsing consistent across\n * the app. It is intentionally small: set paths, `Secure`, `SameSite`, and expiry are still handled\n * by your server or `document.cookie` writes elsewhere.\n *\n * ## Usage\n *\n * ```ts\n * import { parseCookie } from \"yummies/cookie\";\n * ```\n */\n\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * Parses a cookie string into an object with cookie names as keys.\n *\n * @example\n * ```ts\n * parseCookie('theme=dark; token=abc');\n * ```\n */\nexport const parseCookie = (cookiesString = document.cookie) => {\n return cookiesString\n .split(';')\n .map((cookieString) => cookieString.trim().split('='))\n .reduce<AnyObject>((acc, current) => {\n acc[current[0]] = current[1];\n return acc;\n }, {});\n};\n"],"mappings":";;;;;;;;;;AA4BA,IAAa,eAAe,gBAAgB,SAAS,WAAW;AAC9D,QAAO,cACJ,MAAM,IAAI,CACV,KAAK,iBAAiB,aAAa,MAAM,CAAC,MAAM,IAAI,CAAC,CACrD,QAAmB,KAAK,YAAY;AACnC,MAAI,QAAQ,MAAM,QAAQ;AAC1B,SAAO;IACN,EAAE,CAAC"}
|
package/cookie.d.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { AnyObject } from 'yummies/types';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* ---header-docs-section---
|
|
5
|
+
* # yummies/cookie
|
|
6
|
+
*
|
|
7
|
+
* ## Description
|
|
8
|
+
*
|
|
9
|
+
* Minimal helpers for reading the browser `document.cookie` string as a plain key/value map. This
|
|
10
|
+
* avoids hand-rolled splitting and trimming at every call site and keeps parsing consistent across
|
|
11
|
+
* the app. It is intentionally small: set paths, `Secure`, `SameSite`, and expiry are still handled
|
|
12
|
+
* by your server or `document.cookie` writes elsewhere.
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { parseCookie } from "yummies/cookie";
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
|
|
3
21
|
/**
|
|
4
22
|
* Parses a cookie string into an object with cookie names as keys.
|
|
5
23
|
*
|
package/cookie.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie.js","names":[],"sources":["../src/cookie.ts"],"sourcesContent":["import type { AnyObject } from 'yummies/types';\n\n/**\n * Parses a cookie string into an object with cookie names as keys.\n *\n * @example\n * ```ts\n * parseCookie('theme=dark; token=abc');\n * ```\n */\nexport const parseCookie = (cookiesString = document.cookie) => {\n return cookiesString\n .split(';')\n .map((cookieString) => cookieString.trim().split('='))\n .reduce<AnyObject>((acc, current) => {\n acc[current[0]] = current[1];\n return acc;\n }, {});\n};\n"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"cookie.js","names":[],"sources":["../src/cookie.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/cookie\n *\n * ## Description\n *\n * Minimal helpers for reading the browser `document.cookie` string as a plain key/value map. This\n * avoids hand-rolled splitting and trimming at every call site and keeps parsing consistent across\n * the app. It is intentionally small: set paths, `Secure`, `SameSite`, and expiry are still handled\n * by your server or `document.cookie` writes elsewhere.\n *\n * ## Usage\n *\n * ```ts\n * import { parseCookie } from \"yummies/cookie\";\n * ```\n */\n\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * Parses a cookie string into an object with cookie names as keys.\n *\n * @example\n * ```ts\n * parseCookie('theme=dark; token=abc');\n * ```\n */\nexport const parseCookie = (cookiesString = document.cookie) => {\n return cookiesString\n .split(';')\n .map((cookieString) => cookieString.trim().split('='))\n .reduce<AnyObject>((acc, current) => {\n acc[current[0]] = current[1];\n return acc;\n }, {});\n};\n"],"mappings":";;;;;;;;;AA4BA,IAAa,eAAe,gBAAgB,SAAS,WAAW;AAC9D,QAAO,cACJ,MAAM,IAAI,CACV,KAAK,iBAAiB,aAAa,MAAM,CAAC,MAAM,IAAI,CAAC,CACrD,QAAmB,KAAK,YAAY;AACnC,MAAI,QAAQ,MAAM,QAAQ;AAC1B,SAAO;IACN,EAAE,CAAC"}
|
package/css.cjs
CHANGED
|
@@ -6,6 +6,22 @@ clsx = require_chunk.__toESM(clsx);
|
|
|
6
6
|
let tailwind_merge = require("tailwind-merge");
|
|
7
7
|
//#region src/css.ts
|
|
8
8
|
/**
|
|
9
|
+
* ---header-docs-section---
|
|
10
|
+
* # yummies/css
|
|
11
|
+
*
|
|
12
|
+
* ## Description
|
|
13
|
+
*
|
|
14
|
+
* Styling utilities for React and plain DOM: `rem` conversion, `clsx` + `tailwind-merge` via `cx`,
|
|
15
|
+
* and a `cva` bridge for variant-driven class names. The goal is predictable class strings without
|
|
16
|
+
* Tailwind conflicts and with less boilerplate than concatenating strings by hand across components.
|
|
17
|
+
*
|
|
18
|
+
* ## Usage
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { cx, toRem } from "yummies/css";
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
9
25
|
* Converts a length in **pixels** to a CSS **`rem`** string (`"<number>rem"`).
|
|
10
26
|
*
|
|
11
27
|
* Use when authoring component styles in JS/TS where design tokens are in px but the stylesheet
|