yummies 7.19.4 → 7.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/assert.cjs +1 -1
  2. package/assert.cjs.map +1 -1
  3. package/assert.js +1 -1
  4. package/assert.js.map +1 -1
  5. package/async.cjs +147 -137
  6. package/async.cjs.map +1 -1
  7. package/async.d.ts +98 -131
  8. package/async.js +147 -137
  9. package/async.js.map +1 -1
  10. package/common.cjs +3 -1
  11. package/common.cjs.map +1 -1
  12. package/common.d.ts +18 -24
  13. package/common.js +3 -1
  14. package/common.js.map +1 -1
  15. package/complex.cjs +1 -0
  16. package/complex.cjs.map +1 -1
  17. package/complex.js +1 -0
  18. package/complex.js.map +1 -1
  19. package/cookie.cjs +1 -1
  20. package/cookie.cjs.map +1 -1
  21. package/cookie.d.ts +0 -18
  22. package/cookie.js +1 -1
  23. package/cookie.js.map +1 -1
  24. package/css-cx.cjs +78 -0
  25. package/css-cx.cjs.map +1 -0
  26. package/css-cx.d.ts +69 -0
  27. package/css-cx.js +74 -0
  28. package/css-cx.js.map +1 -0
  29. package/css.cjs +32 -111
  30. package/css.cjs.map +1 -1
  31. package/css.d.ts +32 -42
  32. package/css.js +30 -107
  33. package/css.js.map +1 -1
  34. package/data.cjs +60 -50
  35. package/data.cjs.map +1 -1
  36. package/data.d.ts +21 -34
  37. package/data.js +60 -50
  38. package/data.js.map +1 -1
  39. package/date-time.cjs +104 -98
  40. package/date-time.cjs.map +1 -1
  41. package/date-time.d.ts +51 -57
  42. package/date-time.js +104 -97
  43. package/date-time.js.map +1 -1
  44. package/device.cjs +14 -19
  45. package/device.cjs.map +1 -1
  46. package/device.d.ts +15 -26
  47. package/device.js +14 -19
  48. package/device.js.map +1 -1
  49. package/encodings.cjs +1 -1
  50. package/encodings.cjs.map +1 -1
  51. package/encodings.d.ts +0 -17
  52. package/encodings.js +1 -1
  53. package/encodings.js.map +1 -1
  54. package/errors.cjs +1 -18
  55. package/errors.cjs.map +1 -1
  56. package/errors.d.ts +0 -17
  57. package/errors.js +1 -17
  58. package/errors.js.map +1 -1
  59. package/file.cjs +3 -17
  60. package/file.cjs.map +1 -1
  61. package/file.d.ts +1 -16
  62. package/file.js +3 -17
  63. package/file.js.map +1 -1
  64. package/format.cjs +1 -1
  65. package/format.cjs.map +1 -1
  66. package/format.js +1 -1
  67. package/format.js.map +1 -1
  68. package/html.cjs +112 -175
  69. package/html.cjs.map +1 -1
  70. package/html.d.ts +58 -57
  71. package/html.js +111 -172
  72. package/html.js.map +1 -1
  73. package/id-nanoid.cjs +36 -0
  74. package/id-nanoid.cjs.map +1 -0
  75. package/id-nanoid.d.ts +26 -0
  76. package/id-nanoid.js +32 -0
  77. package/id-nanoid.js.map +1 -0
  78. package/id.cjs +10 -45
  79. package/id.cjs.map +1 -1
  80. package/id.d.ts +27 -36
  81. package/id.js +6 -40
  82. package/id.js.map +1 -1
  83. package/imports.cjs +3 -18
  84. package/imports.cjs.map +1 -1
  85. package/imports.d.ts +1 -16
  86. package/imports.js +3 -17
  87. package/imports.js.map +1 -1
  88. package/math.cjs +16 -12
  89. package/math.cjs.map +1 -1
  90. package/math.d.ts +9 -24
  91. package/math.js +16 -12
  92. package/math.js.map +1 -1
  93. package/media.cjs +88 -89
  94. package/media.cjs.map +1 -1
  95. package/media.d.ts +72 -80
  96. package/media.js +88 -88
  97. package/media.js.map +1 -1
  98. package/mobx.cjs +0 -1
  99. package/mobx.cjs.map +1 -1
  100. package/mobx.js.map +1 -1
  101. package/ms.cjs +3 -17
  102. package/ms.cjs.map +1 -1
  103. package/ms.d.ts +1 -16
  104. package/ms.js +3 -17
  105. package/ms.js.map +1 -1
  106. package/number.cjs +1 -17
  107. package/number.cjs.map +1 -1
  108. package/number.d.ts +0 -16
  109. package/number.js +1 -17
  110. package/number.js.map +1 -1
  111. package/package.json +6 -6
  112. package/parser.cjs +1 -1
  113. package/parser.cjs.map +1 -1
  114. package/parser.js +1 -1
  115. package/parser.js.map +1 -1
  116. package/price.cjs +1 -1
  117. package/price.cjs.map +1 -1
  118. package/price.d.ts +0 -16
  119. package/price.js +1 -1
  120. package/price.js.map +1 -1
  121. package/random.cjs +37 -39
  122. package/random.cjs.map +1 -1
  123. package/random.d.ts +31 -40
  124. package/random.js +37 -39
  125. package/random.js.map +1 -1
  126. package/react.cjs +0 -1
  127. package/react.cjs.map +1 -1
  128. package/react.js.map +1 -1
  129. package/sanitize-html.cjs +82 -0
  130. package/sanitize-html.cjs.map +1 -0
  131. package/sanitize-html.d.ts +28 -0
  132. package/sanitize-html.js +79 -0
  133. package/sanitize-html.js.map +1 -0
  134. package/sound.cjs +1 -17
  135. package/sound.cjs.map +1 -1
  136. package/sound.d.ts +0 -16
  137. package/sound.js +1 -17
  138. package/sound.js.map +1 -1
  139. package/storage.cjs +7 -5
  140. package/storage.cjs.map +1 -1
  141. package/storage.d.ts +3 -17
  142. package/storage.js +7 -5
  143. package/storage.js.map +1 -1
  144. package/text.cjs +3 -17
  145. package/text.cjs.map +1 -1
  146. package/text.d.ts +1 -16
  147. package/text.js +3 -17
  148. package/text.js.map +1 -1
  149. package/type-guard.cjs +38 -1
  150. package/type-guard.cjs.map +1 -1
  151. package/type-guard.d.ts +38 -0
  152. package/type-guard.js +38 -1
  153. package/type-guard.js.map +1 -1
  154. package/types.d.ts +10 -1
  155. package/types.global.d.ts +9 -0
  156. package/vibrate.cjs +1 -17
  157. package/vibrate.cjs.map +1 -1
  158. package/vibrate.d.ts +0 -16
  159. package/vibrate.js +1 -17
  160. package/vibrate.js.map +1 -1
  161. /package/{chunk-pbuEa-1d.js → chunk-D7D4PA-g.js} +0 -0
  162. /package/{chunk-CZWwpsFl.cjs → chunk-DakpK96I.cjs} +0 -0
package/media.js CHANGED
@@ -1,21 +1,5 @@
1
1
  import { degToRad } from "yummies/math";
2
- //#region src/media.ts
3
- /**
4
- * ---header-docs-section---
5
- * # yummies/media
6
- *
7
- * ## Description
8
- *
9
- * Binary and media helpers: Base64 data URLs, object URLs, image orientation fixes, and related
10
- * browser APIs wrapped in promises. Centralizes `FileReader`/`URL.createObjectURL` usage so image
11
- * pipelines and uploads stay consistent and easier to test or mock.
12
- *
13
- * ## Usage
14
- *
15
- * ```ts
16
- * import { blobToBase64 } from "yummies/media";
17
- * ```
18
- */
2
+ //#region src/media/blob-to-base64.ts
19
3
  /**
20
4
  * Reads a {@link Blob} as a **data URL** string (`data:<mime>;base64,...`) using {@link FileReader#readAsDataURL}.
21
5
  *
@@ -46,6 +30,8 @@ function blobToBase64(blob) {
46
30
  reader.readAsDataURL(blob);
47
31
  });
48
32
  }
33
+ //#endregion
34
+ //#region src/media/blob-to-url.ts
49
35
  /**
50
36
  * If `urlOrBlob` is already a string, returns it unchanged. If it is a {@link Blob}, returns
51
37
  * `URL.createObjectURL(blob)` — a short-lived `blob:` URL valid in this document until
@@ -71,9 +57,39 @@ function blobToBase64(blob) {
71
57
  * ```
72
58
  */
73
59
  var blobToUrl = (urlOrBlob) => urlOrBlob instanceof Blob ? URL.createObjectURL(urlOrBlob) : urlOrBlob;
60
+ //#endregion
61
+ //#region src/media/decode-data-url.ts
62
+ /**
63
+ * Parses a `data:` URL of the form `data:<mime>;base64,<payload>` into its MIME type and raw
64
+ * Base64 body (without the `data:...;base64,` prefix). Non-matching strings yield `undefined`
65
+ * fields in the result object.
66
+ *
67
+ * @param url - Full data URL string.
68
+ * @returns `{ mimeType, data }` — both optional when the regex does not match.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * const { mimeType, data } = decodeDataUrl('data:image/png;base64,iVBORw0KGgo=');
73
+ * // mimeType === 'image/png', data === 'iVBORw0KGgo='
74
+ * ```
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * decodeDataUrl('not-a-data-url'); // { mimeType: undefined, data: undefined }
79
+ * ```
80
+ */
81
+ function decodeDataUrl(url) {
82
+ const matches = (/* @__PURE__ */ new RegExp(/^data:(.*);base64,\s?(.*)$/)).exec(url);
83
+ return {
84
+ mimeType: matches?.[1],
85
+ data: matches?.[2]
86
+ };
87
+ }
88
+ //#endregion
89
+ //#region src/media/file-to-blob.ts
74
90
  /**
75
91
  * Creates a new {@link Blob} from a {@link File}, copying the bytes and keeping `file.type` as
76
- * the blobs MIME type. Handy when an API accepts `Blob` but you only have `File`, or you want a
92
+ * the blob's MIME type. Handy when an API accepts `Blob` but you only have `File`, or you want a
77
93
  * plain blob without the `File` name/lastModified metadata.
78
94
  *
79
95
  * @param file - Source file from an `<input type="file">` or drag-and-drop.
@@ -94,6 +110,8 @@ var blobToUrl = (urlOrBlob) => urlOrBlob instanceof Blob ? URL.createObjectURL(u
94
110
  var fileToBlob = (file) => {
95
111
  return new Blob([file], { type: file.type });
96
112
  };
113
+ //#endregion
114
+ //#region src/media/image-to-blob.ts
97
115
  /**
98
116
  * Draws an {@link HTMLImageElement} onto an offscreen canvas, then builds a {@link Blob} from the
99
117
  * raster (via `canvas.toDataURL` + binary decode). Dimensions use `naturalWidth` / `naturalHeight`,
@@ -131,6 +149,56 @@ var imageToBlob = (imageElement, mimeType = "image/png") => {
131
149
  for (let index = 0; index < bytes.length; index++) array[index] = bytes.charCodeAt(index);
132
150
  return new Blob([array], { type: base64MimeType });
133
151
  };
152
+ //#endregion
153
+ //#region src/media/is-base64-image.ts
154
+ /**
155
+ * Returns `true` when `str` is a data URL that {@link decodeDataUrl} can parse **and** the MIME
156
+ * type starts with `image/` (e.g. `image/png`, `image/jpeg`). Requires both a non-empty Base64
157
+ * payload and an image MIME — arbitrary `data:text/plain;base64,...` is `false`.
158
+ *
159
+ * @param str - Candidate string (often `img.src` or API payload).
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * isBase64Image('data:image/png;base64,iVBORw0KGgo='); // true
164
+ * ```
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * isBase64Image('https://example.com/x.png'); // false
169
+ * isBase64Image('data:text/plain;base64,SGk='); // false
170
+ * ```
171
+ */
172
+ var isBase64Image = (str) => {
173
+ const { mimeType, data } = decodeDataUrl(str);
174
+ return !!(data && mimeType?.startsWith("image/"));
175
+ };
176
+ //#endregion
177
+ //#region src/media/is-http-url.ts
178
+ /**
179
+ * Returns `true` if `url` starts with `https://` or `http://` (case-sensitive, as in
180
+ * `String#startsWith`). Does not validate that the rest is a well-formed URL — only the scheme
181
+ * prefix. `blob:`, `data:`, and relative paths return `false`.
182
+ *
183
+ * @param url - String to test (often a user-provided or configured href).
184
+ *
185
+ * @example
186
+ * ```ts
187
+ * isHttpUrl('https://example.com/path'); // true
188
+ * isHttpUrl('http://localhost:3000'); // true
189
+ * ```
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * isHttpUrl('//cdn.example.com'); // false — no explicit http(s) prefix
194
+ * isHttpUrl('/assets/logo.png'); // false — relative path
195
+ * ```
196
+ */
197
+ var isHttpUrl = (url) => {
198
+ return url.startsWith("https://") || url.startsWith("http://");
199
+ };
200
+ //#endregion
201
+ //#region src/media/render-image.ts
134
202
  /**
135
203
  * Loads a resource into a new `HTMLImageElement`: `src` is set via {@link blobToUrl} (so blobs get
136
204
  * object URLs, strings are used directly). Resolves on `load` with the same element; rejects on
@@ -160,6 +228,8 @@ var renderImage = (urlOrBlob) => new Promise((resolve, reject) => {
160
228
  image.onload = () => resolve(image);
161
229
  image.onerror = () => reject();
162
230
  });
231
+ //#endregion
232
+ //#region src/media/rotate-image.ts
163
233
  function cropImageFromCanvas(context) {
164
234
  const canvas = context.canvas;
165
235
  let w = canvas.width;
@@ -227,76 +297,6 @@ var rotateImage = (image, angle) => {
227
297
  cropImageFromCanvas(context);
228
298
  return renderImage(canvas.toDataURL("image/png"));
229
299
  };
230
- /**
231
- * Parses a `data:` URL of the form `data:<mime>;base64,<payload>` into its MIME type and raw
232
- * Base64 body (without the `data:...;base64,` prefix). Non-matching strings yield `undefined`
233
- * fields in the result object.
234
- *
235
- * @param url - Full data URL string.
236
- * @returns `{ mimeType, data }` — both optional when the regex does not match.
237
- *
238
- * @example
239
- * ```ts
240
- * const { mimeType, data } = decodeDataUrl('data:image/png;base64,iVBORw0KGgo=');
241
- * // mimeType === 'image/png', data === 'iVBORw0KGgo='
242
- * ```
243
- *
244
- * @example
245
- * ```ts
246
- * decodeDataUrl('not-a-data-url'); // { mimeType: undefined, data: undefined }
247
- * ```
248
- */
249
- function decodeDataUrl(url) {
250
- const matches = (/* @__PURE__ */ new RegExp(/^data:(.*);base64,\s?(.*)$/)).exec(url);
251
- return {
252
- mimeType: matches?.[1],
253
- data: matches?.[2]
254
- };
255
- }
256
- /**
257
- * Returns `true` if `url` starts with `https://` or `http://` (case-sensitive, as in
258
- * `String#startsWith`). Does not validate that the rest is a well-formed URL — only the scheme
259
- * prefix. `blob:`, `data:`, and relative paths return `false`.
260
- *
261
- * @param url - String to test (often a user-provided or configured href).
262
- *
263
- * @example
264
- * ```ts
265
- * isHttpUrl('https://example.com/path'); // true
266
- * isHttpUrl('http://localhost:3000'); // true
267
- * ```
268
- *
269
- * @example
270
- * ```ts
271
- * isHttpUrl('//cdn.example.com'); // false — no explicit http(s) prefix
272
- * isHttpUrl('/assets/logo.png'); // false — relative path
273
- * ```
274
- */
275
- var isHttpUrl = (url) => {
276
- return url.startsWith("https://") || url.startsWith("http://");
277
- };
278
- /**
279
- * Returns `true` when `str` is a data URL that {@link decodeDataUrl} can parse **and** the MIME
280
- * type starts with `image/` (e.g. `image/png`, `image/jpeg`). Requires both a non-empty Base64
281
- * payload and an image MIME — arbitrary `data:text/plain;base64,...` is `false`.
282
- *
283
- * @param str - Candidate string (often `img.src` or API payload).
284
- *
285
- * @example
286
- * ```ts
287
- * isBase64Image('data:image/png;base64,iVBORw0KGgo='); // true
288
- * ```
289
- *
290
- * @example
291
- * ```ts
292
- * isBase64Image('https://example.com/x.png'); // false
293
- * isBase64Image('data:text/plain;base64,SGk='); // false
294
- * ```
295
- */
296
- var isBase64Image = (str) => {
297
- const { mimeType, data } = decodeDataUrl(str);
298
- return !!(data && mimeType?.startsWith("image/"));
299
- };
300
300
  //#endregion
301
301
  export { blobToBase64, blobToUrl, decodeDataUrl, fileToBlob, imageToBlob, isBase64Image, isHttpUrl, renderImage, rotateImage };
302
302
 
package/media.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"media.js","names":[],"sources":["../src/media.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/media\n *\n * ## Description\n *\n * Binary and media helpers: Base64 data URLs, object URLs, image orientation fixes, and related\n * browser APIs wrapped in promises. Centralizes `FileReader`/`URL.createObjectURL` usage so image\n * pipelines and uploads stay consistent and easier to test or mock.\n *\n * ## Usage\n *\n * ```ts\n * import { blobToBase64 } from \"yummies/media\";\n * ```\n */\n\nimport { degToRad } from 'yummies/math';\n\n/**\n * Reads a {@link Blob} as a **data URL** string (`data:<mime>;base64,...`) using {@link FileReader#readAsDataURL}.\n *\n * Useful for previewing uploads, embedding small assets inline, or serializing binary for APIs that\n * expect Base64-in-JSON. The result includes the MIME prefix, not raw Base64 alone — use\n * {@link decodeDataUrl} if you need the payload and type separately.\n *\n * @param blob - Any `Blob` or `File` (files are blobs).\n * @returns Resolves to the data URL string; rejects if reading fails.\n *\n * @example\n * ```ts\n * const dataUrl = await blobToBase64(file);\n * previewImg.src = dataUrl;\n * ```\n *\n * @example\n * ```ts\n * const fromFetch = await fetch('/api/export').then((r) => r.blob());\n * const dataUrl = await blobToBase64(fromFetch);\n * ```\n */\nexport function blobToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n}\n\n/**\n * If `urlOrBlob` is already a string, returns it unchanged. If it is a {@link Blob}, returns\n * `URL.createObjectURL(blob)` — a short-lived `blob:` URL valid in this document until\n * {@link URL.revokeObjectURL} is called.\n *\n * Pair with {@link renderImage} or `<img src>` without re-fetching binary data. **Remember to\n * `revokeObjectURL`** when the URL is no longer needed to avoid retaining blob memory.\n *\n * @param urlOrBlob - Remote/http(s) URL string, data URL, or a `Blob` / `File`.\n * @returns A string suitable for `HTMLImageElement.src` or similar.\n *\n * @example\n * ```ts\n * const src = blobToUrl(uploadedFile);\n * img.src = src;\n * // when done:\n * URL.revokeObjectURL(src);\n * ```\n *\n * @example\n * ```ts\n * blobToUrl('https://cdn.example.com/logo.png'); // passed through as-is\n * ```\n */\nexport const blobToUrl = (urlOrBlob: string | Blob) =>\n urlOrBlob instanceof Blob ? URL.createObjectURL(urlOrBlob) : urlOrBlob;\n\n/**\n * Creates a new {@link Blob} from a {@link File}, copying the bytes and keeping `file.type` as\n * the blob’s MIME type. Handy when an API accepts `Blob` but you only have `File`, or you want a\n * plain blob without the `File` name/lastModified metadata.\n *\n * @param file - Source file from an `<input type=\"file\">` or drag-and-drop.\n * @returns A `Blob` with the same content and `type` as the file.\n *\n * @example\n * ```ts\n * const blob = fileToBlob(fileFromInput);\n * await uploadEndpoint(blob);\n * ```\n *\n * @example\n * ```ts\n * const blob = fileToBlob(imageFile);\n * const url = URL.createObjectURL(blob);\n * ```\n */\nexport const fileToBlob = (file: File) => {\n return new Blob([file], { type: file.type });\n};\n\n/**\n * Draws an {@link HTMLImageElement} onto an offscreen canvas, then builds a {@link Blob} from the\n * raster (via `canvas.toDataURL` + binary decode). Dimensions use `naturalWidth` / `naturalHeight`,\n * falling back to `300×300` if those are zero (e.g. not yet decoded).\n *\n * **CORS:** if the image is cross-origin without proper CORS headers, the canvas may be\n * *tainted* and `toDataURL` can throw — same browser rules as any canvas export.\n *\n * @param imageElement - Loaded image (`complete` / decoded recommended).\n * @param mimeType - Output MIME type, e.g. `'image/png'` (default) or `'image/jpeg'`. Encoder support is browser-dependent.\n * @returns Encoded image as a `Blob` with a matching `type` when possible.\n *\n * @example\n * ```ts\n * const img = await renderImage('/photo.jpg');\n * const jpegBlob = imageToBlob(img, 'image/jpeg');\n * ```\n *\n * @example\n * ```ts\n * const pngBlob = imageToBlob(cachedImg); // default image/png\n * ```\n */\nexport const imageToBlob = (\n imageElement: HTMLImageElement,\n mimeType: string = 'image/png',\n) => {\n const canvas = document.createElement('canvas');\n\n canvas.width = imageElement.naturalWidth || 300;\n canvas.height = imageElement.naturalHeight || 300;\n\n canvas.getContext('2d')!.drawImage(imageElement, 0, 0);\n\n const dataUri = canvas.toDataURL(mimeType, 1);\n const base64data = dataUri.split(',')[1];\n const base64MimeType = dataUri.split(';')[0].slice(5);\n\n const bytes = globalThis.atob(base64data);\n const buf = new ArrayBuffer(bytes.length);\n const array = new Uint8Array(buf);\n\n for (let index = 0; index < bytes.length; index++) {\n array[index] = bytes.charCodeAt(index);\n }\n\n const blob = new Blob([array], { type: base64MimeType });\n\n return blob;\n};\n\n/**\n * Loads a resource into a new `HTMLImageElement`: `src` is set via {@link blobToUrl} (so blobs get\n * object URLs, strings are used directly). Resolves on `load` with the same element; rejects on\n * `error` (e.g. bad URL, network failure, corrupt image) **with no rejection value**.\n *\n * Does not add the node to the DOM — use the returned element for canvas, measuring, or\n * {@link imageToBlob}.\n *\n * @param urlOrBlob - Remote URL, data URL, or `Blob` / `File`.\n * @returns Promise that fulfills with the loaded `HTMLImageElement`.\n *\n * @example\n * ```ts\n * const img = await renderImage('https://example.com/pic.png');\n * document.body.appendChild(img);\n * ```\n *\n * @example\n * ```ts\n * const img = await renderImage(pickedFile);\n * const blob = imageToBlob(img, 'image/webp');\n * ```\n */\nexport const renderImage = (urlOrBlob: Blob | string) =>\n new Promise<HTMLImageElement>((resolve, reject) => {\n const image = new Image();\n image.src = blobToUrl(urlOrBlob);\n image.onload = () => resolve(image);\n image.onerror = () => reject();\n });\n\nfunction cropImageFromCanvas(context: CanvasRenderingContext2D) {\n const canvas = context.canvas;\n let w = canvas.width;\n let h = canvas.height;\n const pix: { x: number[]; y: number[] } = { x: [], y: [] };\n const imageData = context.getImageData(0, 0, canvas.width, canvas.height);\n let x: number;\n let y: number;\n let index: number;\n\n for (y = 0; y < h; y++) {\n for (x = 0; x < w; x++) {\n index = (y * w + x) * 4;\n if (imageData.data[index + 3] > 0) {\n pix.x.push(x);\n pix.y.push(y);\n }\n }\n }\n pix.x.sort((a, b) => a - b);\n pix.y.sort((a, b) => a - b);\n const n = pix.x.length - 1;\n\n w = 1 + pix.x[n] - pix.x[0];\n h = 1 + pix.y[n] - pix.y[0];\n const cut = context.getImageData(pix.x[0], pix.y[0], w, h);\n\n canvas.width = w;\n canvas.height = h;\n context.putImageData(cut, 0, 0);\n return canvas;\n}\n\n// TODO: ломает iphone с огромными изображениями\n/**\n * Rotates `image` around its center on a square canvas (side = max(width, height)), then **crops**\n * transparent margins by trimming to the bounding box of pixels with non-zero alpha.\n * Returns a **new** loaded `HTMLImageElement` (PNG data URL under the hood via {@link renderImage}).\n *\n * `angle` is in **degrees**; converted with {@link degToRad} from `yummies/math`.\n *\n * Very large sources can stress memory on some mobile browsers (known iPhone issues — see TODO in source).\n *\n * @param image - Source image (should be decoded; uses `width` / `height`).\n * @param angle - Rotation in degrees (e.g. `90` for quarter turn).\n * @returns Promise of a new `HTMLImageElement` showing the rotated, cropped result.\n *\n * @example\n * ```ts\n * const upright = await rotateImage(landscapeImg, 90);\n * ```\n *\n * @example\n * ```ts\n * const fixed = await rotateImage(await renderImage(file), -15);\n * ```\n */\nexport const rotateImage = (image: HTMLImageElement, angle: number) => {\n const maxSize = Math.max(image.width, image.height);\n const canvas = document.createElement('canvas');\n canvas.width = maxSize;\n canvas.height = maxSize;\n const context = canvas.getContext('2d')!;\n context.save();\n context.translate(canvas.width / 2, canvas.height / 2);\n context.rotate(degToRad(angle));\n context.drawImage(image, -image.width / 2, -image.height / 2);\n context.restore();\n cropImageFromCanvas(context);\n return renderImage(canvas.toDataURL('image/png'));\n};\n\ninterface DecodedDataUrl {\n mimeType?: string;\n data?: string;\n}\n\n/**\n * Parses a `data:` URL of the form `data:<mime>;base64,<payload>` into its MIME type and raw\n * Base64 body (without the `data:...;base64,` prefix). Non-matching strings yield `undefined`\n * fields in the result object.\n *\n * @param url - Full data URL string.\n * @returns `{ mimeType, data }` — both optional when the regex does not match.\n *\n * @example\n * ```ts\n * const { mimeType, data } = decodeDataUrl('data:image/png;base64,iVBORw0KGgo=');\n * // mimeType === 'image/png', data === 'iVBORw0KGgo='\n * ```\n *\n * @example\n * ```ts\n * decodeDataUrl('not-a-data-url'); // { mimeType: undefined, data: undefined }\n * ```\n */\nexport function decodeDataUrl(url: string): DecodedDataUrl {\n const regex = /^data:(.*);base64,\\s?(.*)$/;\n const matches = new RegExp(regex).exec(url);\n\n return {\n mimeType: matches?.[1],\n data: matches?.[2],\n };\n}\n\n/**\n * Returns `true` if `url` starts with `https://` or `http://` (case-sensitive, as in\n * `String#startsWith`). Does not validate that the rest is a well-formed URL — only the scheme\n * prefix. `blob:`, `data:`, and relative paths return `false`.\n *\n * @param url - String to test (often a user-provided or configured href).\n *\n * @example\n * ```ts\n * isHttpUrl('https://example.com/path'); // true\n * isHttpUrl('http://localhost:3000'); // true\n * ```\n *\n * @example\n * ```ts\n * isHttpUrl('//cdn.example.com'); // false — no explicit http(s) prefix\n * isHttpUrl('/assets/logo.png'); // false — relative path\n * ```\n */\nexport const isHttpUrl = (url: string): boolean => {\n return url.startsWith('https://') || url.startsWith('http://');\n};\n\n/**\n * Returns `true` when `str` is a data URL that {@link decodeDataUrl} can parse **and** the MIME\n * type starts with `image/` (e.g. `image/png`, `image/jpeg`). Requires both a non-empty Base64\n * payload and an image MIME — arbitrary `data:text/plain;base64,...` is `false`.\n *\n * @param str - Candidate string (often `img.src` or API payload).\n *\n * @example\n * ```ts\n * isBase64Image('data:image/png;base64,iVBORw0KGgo='); // true\n * ```\n *\n * @example\n * ```ts\n * isBase64Image('https://example.com/x.png'); // false\n * isBase64Image('data:text/plain;base64,SGk='); // false\n * ```\n */\nexport const isBase64Image = (str: string): boolean => {\n const { mimeType, data } = decodeDataUrl(str);\n return !!(data && mimeType?.startsWith('image/'));\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"media.js","names":[],"sources":["../src/media/blob-to-base64.ts","../src/media/blob-to-url.ts","../src/media/decode-data-url.ts","../src/media/file-to-blob.ts","../src/media/image-to-blob.ts","../src/media/is-base64-image.ts","../src/media/is-http-url.ts","../src/media/render-image.ts","../src/media/rotate-image.ts"],"sourcesContent":["/**\n * Reads a {@link Blob} as a **data URL** string (`data:<mime>;base64,...`) using {@link FileReader#readAsDataURL}.\n *\n * Useful for previewing uploads, embedding small assets inline, or serializing binary for APIs that\n * expect Base64-in-JSON. The result includes the MIME prefix, not raw Base64 alone — use\n * {@link decodeDataUrl} if you need the payload and type separately.\n *\n * @param blob - Any `Blob` or `File` (files are blobs).\n * @returns Resolves to the data URL string; rejects if reading fails.\n *\n * @example\n * ```ts\n * const dataUrl = await blobToBase64(file);\n * previewImg.src = dataUrl;\n * ```\n *\n * @example\n * ```ts\n * const fromFetch = await fetch('/api/export').then((r) => r.blob());\n * const dataUrl = await blobToBase64(fromFetch);\n * ```\n */\nexport function blobToBase64(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolve(reader.result as string);\n reader.onerror = reject;\n reader.readAsDataURL(blob);\n });\n}\n","/**\n * If `urlOrBlob` is already a string, returns it unchanged. If it is a {@link Blob}, returns\n * `URL.createObjectURL(blob)` — a short-lived `blob:` URL valid in this document until\n * {@link URL.revokeObjectURL} is called.\n *\n * Pair with {@link renderImage} or `<img src>` without re-fetching binary data. **Remember to\n * `revokeObjectURL`** when the URL is no longer needed to avoid retaining blob memory.\n *\n * @param urlOrBlob - Remote/http(s) URL string, data URL, or a `Blob` / `File`.\n * @returns A string suitable for `HTMLImageElement.src` or similar.\n *\n * @example\n * ```ts\n * const src = blobToUrl(uploadedFile);\n * img.src = src;\n * // when done:\n * URL.revokeObjectURL(src);\n * ```\n *\n * @example\n * ```ts\n * blobToUrl('https://cdn.example.com/logo.png'); // passed through as-is\n * ```\n */\nexport const blobToUrl = (urlOrBlob: string | Blob) =>\n urlOrBlob instanceof Blob ? URL.createObjectURL(urlOrBlob) : urlOrBlob;\n","interface DecodedDataUrl {\n mimeType?: string;\n data?: string;\n}\n\n/**\n * Parses a `data:` URL of the form `data:<mime>;base64,<payload>` into its MIME type and raw\n * Base64 body (without the `data:...;base64,` prefix). Non-matching strings yield `undefined`\n * fields in the result object.\n *\n * @param url - Full data URL string.\n * @returns `{ mimeType, data }` — both optional when the regex does not match.\n *\n * @example\n * ```ts\n * const { mimeType, data } = decodeDataUrl('data:image/png;base64,iVBORw0KGgo=');\n * // mimeType === 'image/png', data === 'iVBORw0KGgo='\n * ```\n *\n * @example\n * ```ts\n * decodeDataUrl('not-a-data-url'); // { mimeType: undefined, data: undefined }\n * ```\n */\nexport function decodeDataUrl(url: string): DecodedDataUrl {\n const regex = /^data:(.*);base64,\\s?(.*)$/;\n const matches = new RegExp(regex).exec(url);\n\n return {\n mimeType: matches?.[1],\n data: matches?.[2],\n };\n}\n","/**\n * Creates a new {@link Blob} from a {@link File}, copying the bytes and keeping `file.type` as\n * the blob's MIME type. Handy when an API accepts `Blob` but you only have `File`, or you want a\n * plain blob without the `File` name/lastModified metadata.\n *\n * @param file - Source file from an `<input type=\"file\">` or drag-and-drop.\n * @returns A `Blob` with the same content and `type` as the file.\n *\n * @example\n * ```ts\n * const blob = fileToBlob(fileFromInput);\n * await uploadEndpoint(blob);\n * ```\n *\n * @example\n * ```ts\n * const blob = fileToBlob(imageFile);\n * const url = URL.createObjectURL(blob);\n * ```\n */\nexport const fileToBlob = (file: File) => {\n return new Blob([file], { type: file.type });\n};\n","/**\n * Draws an {@link HTMLImageElement} onto an offscreen canvas, then builds a {@link Blob} from the\n * raster (via `canvas.toDataURL` + binary decode). Dimensions use `naturalWidth` / `naturalHeight`,\n * falling back to `300×300` if those are zero (e.g. not yet decoded).\n *\n * **CORS:** if the image is cross-origin without proper CORS headers, the canvas may be\n * *tainted* and `toDataURL` can throw — same browser rules as any canvas export.\n *\n * @param imageElement - Loaded image (`complete` / decoded recommended).\n * @param mimeType - Output MIME type, e.g. `'image/png'` (default) or `'image/jpeg'`. Encoder support is browser-dependent.\n * @returns Encoded image as a `Blob` with a matching `type` when possible.\n *\n * @example\n * ```ts\n * const img = await renderImage('/photo.jpg');\n * const jpegBlob = imageToBlob(img, 'image/jpeg');\n * ```\n *\n * @example\n * ```ts\n * const pngBlob = imageToBlob(cachedImg); // default image/png\n * ```\n */\nexport const imageToBlob = (\n imageElement: HTMLImageElement,\n mimeType: string = 'image/png',\n) => {\n const canvas = document.createElement('canvas');\n\n canvas.width = imageElement.naturalWidth || 300;\n canvas.height = imageElement.naturalHeight || 300;\n\n canvas.getContext('2d')!.drawImage(imageElement, 0, 0);\n\n const dataUri = canvas.toDataURL(mimeType, 1);\n const base64data = dataUri.split(',')[1];\n const base64MimeType = dataUri.split(';')[0].slice(5);\n\n const bytes = globalThis.atob(base64data);\n const buf = new ArrayBuffer(bytes.length);\n const array = new Uint8Array(buf);\n\n for (let index = 0; index < bytes.length; index++) {\n array[index] = bytes.charCodeAt(index);\n }\n\n const blob = new Blob([array], { type: base64MimeType });\n\n return blob;\n};\n","import { decodeDataUrl } from './decode-data-url.js';\n\n/**\n * Returns `true` when `str` is a data URL that {@link decodeDataUrl} can parse **and** the MIME\n * type starts with `image/` (e.g. `image/png`, `image/jpeg`). Requires both a non-empty Base64\n * payload and an image MIME — arbitrary `data:text/plain;base64,...` is `false`.\n *\n * @param str - Candidate string (often `img.src` or API payload).\n *\n * @example\n * ```ts\n * isBase64Image('data:image/png;base64,iVBORw0KGgo='); // true\n * ```\n *\n * @example\n * ```ts\n * isBase64Image('https://example.com/x.png'); // false\n * isBase64Image('data:text/plain;base64,SGk='); // false\n * ```\n */\nexport const isBase64Image = (str: string): boolean => {\n const { mimeType, data } = decodeDataUrl(str);\n return !!(data && mimeType?.startsWith('image/'));\n};\n","/**\n * Returns `true` if `url` starts with `https://` or `http://` (case-sensitive, as in\n * `String#startsWith`). Does not validate that the rest is a well-formed URL — only the scheme\n * prefix. `blob:`, `data:`, and relative paths return `false`.\n *\n * @param url - String to test (often a user-provided or configured href).\n *\n * @example\n * ```ts\n * isHttpUrl('https://example.com/path'); // true\n * isHttpUrl('http://localhost:3000'); // true\n * ```\n *\n * @example\n * ```ts\n * isHttpUrl('//cdn.example.com'); // false — no explicit http(s) prefix\n * isHttpUrl('/assets/logo.png'); // false — relative path\n * ```\n */\nexport const isHttpUrl = (url: string): boolean => {\n return url.startsWith('https://') || url.startsWith('http://');\n};\n","import { blobToUrl } from './blob-to-url.js';\n\n/**\n * Loads a resource into a new `HTMLImageElement`: `src` is set via {@link blobToUrl} (so blobs get\n * object URLs, strings are used directly). Resolves on `load` with the same element; rejects on\n * `error` (e.g. bad URL, network failure, corrupt image) **with no rejection value**.\n *\n * Does not add the node to the DOM — use the returned element for canvas, measuring, or\n * {@link imageToBlob}.\n *\n * @param urlOrBlob - Remote URL, data URL, or `Blob` / `File`.\n * @returns Promise that fulfills with the loaded `HTMLImageElement`.\n *\n * @example\n * ```ts\n * const img = await renderImage('https://example.com/pic.png');\n * document.body.appendChild(img);\n * ```\n *\n * @example\n * ```ts\n * const img = await renderImage(pickedFile);\n * const blob = imageToBlob(img, 'image/webp');\n * ```\n */\nexport const renderImage = (urlOrBlob: Blob | string) =>\n new Promise<HTMLImageElement>((resolve, reject) => {\n const image = new Image();\n image.src = blobToUrl(urlOrBlob);\n image.onload = () => resolve(image);\n image.onerror = () => reject();\n });\n","import { degToRad } from 'yummies/math';\nimport { renderImage } from './render-image.js';\n\nfunction cropImageFromCanvas(context: CanvasRenderingContext2D) {\n const canvas = context.canvas;\n let w = canvas.width;\n let h = canvas.height;\n const pix: { x: number[]; y: number[] } = { x: [], y: [] };\n const imageData = context.getImageData(0, 0, canvas.width, canvas.height);\n let x: number;\n let y: number;\n let index: number;\n\n for (y = 0; y < h; y++) {\n for (x = 0; x < w; x++) {\n index = (y * w + x) * 4;\n if (imageData.data[index + 3] > 0) {\n pix.x.push(x);\n pix.y.push(y);\n }\n }\n }\n pix.x.sort((a, b) => a - b);\n pix.y.sort((a, b) => a - b);\n const n = pix.x.length - 1;\n\n w = 1 + pix.x[n] - pix.x[0];\n h = 1 + pix.y[n] - pix.y[0];\n const cut = context.getImageData(pix.x[0], pix.y[0], w, h);\n\n canvas.width = w;\n canvas.height = h;\n context.putImageData(cut, 0, 0);\n return canvas;\n}\n\n// TODO: ломает iphone с огромными изображениями\n/**\n * Rotates `image` around its center on a square canvas (side = max(width, height)), then **crops**\n * transparent margins by trimming to the bounding box of pixels with non-zero alpha.\n * Returns a **new** loaded `HTMLImageElement` (PNG data URL under the hood via {@link renderImage}).\n *\n * `angle` is in **degrees**; converted with {@link degToRad} from `yummies/math`.\n *\n * Very large sources can stress memory on some mobile browsers (known iPhone issues — see TODO in source).\n *\n * @param image - Source image (should be decoded; uses `width` / `height`).\n * @param angle - Rotation in degrees (e.g. `90` for quarter turn).\n * @returns Promise of a new `HTMLImageElement` showing the rotated, cropped result.\n *\n * @example\n * ```ts\n * const upright = await rotateImage(landscapeImg, 90);\n * ```\n *\n * @example\n * ```ts\n * const fixed = await rotateImage(await renderImage(file), -15);\n * ```\n */\nexport const rotateImage = (image: HTMLImageElement, angle: number) => {\n const maxSize = Math.max(image.width, image.height);\n const canvas = document.createElement('canvas');\n canvas.width = maxSize;\n canvas.height = maxSize;\n const context = canvas.getContext('2d')!;\n context.save();\n context.translate(canvas.width / 2, canvas.height / 2);\n context.rotate(degToRad(angle));\n context.drawImage(image, -image.width / 2, -image.height / 2);\n context.restore();\n cropImageFromCanvas(context);\n return renderImage(canvas.toDataURL('image/png'));\n};\n"],"mappings":"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"}
package/mobx.cjs CHANGED
@@ -1,5 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("./chunk-CZWwpsFl.cjs");
3
2
  let yummies_type_guard = require("yummies/type-guard");
4
3
  let mobx = require("mobx");
5
4
  //#region src/mobx/annotation.ts
package/mobx.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mobx.cjs","names":[],"sources":["../src/mobx/annotation.ts","../src/mobx/apply-observable.ts","../src/mobx/create-enhanced-atom.ts","../src/mobx/create-ref.ts","../src/mobx/deep-observable-struct.ts","../src/mobx/flush-pending-reactions.ts","../src/mobx/get-mobx-administration.ts","../src/mobx/lazy-observe.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **`annotation`** — factories for `makeObservable` maps and {@link applyObservable} tuples:\n * `observable.*` flavours, `computed` with shorthand `equals` (`struct`, `shallow`, reference), custom\n * comparators, and `false` to skip a field.\n *\n * ## Usage\n *\n * ```ts\n * import { annotation } from \"yummies/mobx\";\n * ```\n */\n\nimport {\n comparer,\n computed,\n type IComputedValueOptions,\n type IEqualsComparer,\n observable,\n} from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * How MobX should compare the previous and next computed value before notifying observers.\n *\n * - `'struct'` — structural comparison (`comparer.structural`).\n * - `'shallow'` — shallow comparison (`comparer.shallow`).\n * - `true` — reference equality (`comparer.default`).\n * - `false` — skip the annotation (handled by the `annotation.computed` / `annotation.observable` helpers).\n * - A custom `(a, b) => boolean` is allowed by the type for parity with `IComputedValueOptions.equals`.\n */\nexport type ComputedEqualsValue =\n | 'struct'\n | 'shallow'\n | boolean\n | IEqualsComparer<any>;\n\nconst computedEqualsResolvers: AnyObject = {\n true: comparer.default,\n shallow: comparer.shallow,\n struct: comparer.structural,\n} satisfies Record<\n Exclude<ComputedEqualsValue, Function | boolean> | 'true',\n any\n>;\n\n/**\n * Options forwarded to {@link computed}, except `equals` (that argument is passed separately).\n */\nexport type ComputedOtherOptions = Omit<IComputedValueOptions<any>, 'equals'>;\n\n/**\n * Observable flavour keys returned by {@link annotation.observable}: `ref`, `deep`, `shallow`, `struct`.\n * Also supported: `true` (base `observable`) and `false` (no annotation).\n */\nexport type ObservableTypes =\n | keyof Pick<typeof observable, 'ref' | 'deep' | 'shallow' | 'struct'>\n | boolean;\n\n/**\n * MobX annotation factories for `makeObservable` and tuple-style wiring ({@link applyObservable}).\n *\n * - **`annotation.observable(value?)`** — `observable.ref` / `deep` / `shallow` / `struct`; `true` or\n * omitted → base `observable` (deep by default); `false` → `false` (field omitted from the map).\n * - **`annotation.computed(value?, options?)`** — `computed({ ...options, equals })` with `equals` from\n * {@link ComputedEqualsValue} or a custom `(a, b) => boolean`. Omitted `value`, unknown literals, and\n * `true` resolve to `comparer.default`; `value === false` returns `false`.\n *\n * Other `computed` options (except `equals`) go in the second argument; see {@link ComputedOtherOptions}.\n *\n * @example Observable variants\n * ```ts\n * import { makeObservable } from 'mobx';\n * import { annotation } from 'yummies/mobx';\n *\n * class Store {\n * shallowMap = new Map();\n * deep = { nested: { count: 0 } };\n *\n * constructor() {\n * makeObservable(this, {\n * shallowMap: annotation.observable('shallow'),\n * deep: annotation.observable('deep'),\n * });\n * }\n * }\n * ```\n *\n * @example Skip a field\n * ```ts\n * makeObservable(this, {\n * plain: annotation.observable(false), // not decorated\n * });\n * ```\n *\n * @example Computed with structural equality\n * ```ts\n * makeObservable(this, {\n * fullName: annotation.computed('struct', { name: 'fullName' }),\n * });\n * ```\n *\n * @example Computed with default reference equality\n * ```ts\n * makeObservable(this, {\n * total: annotation.computed(true),\n * });\n * ```\n *\n * @example Omitted first argument or `true` — reference equality (`comparer.default`)\n * ```ts\n * makeObservable(this, {\n * n: annotation.computed(undefined, { name: 'n' }),\n * m: annotation.computed(true, { name: 'm' }), // same `equals` as omitted\n * });\n * ```\n *\n * @example Custom `equals`\n * ```ts\n * makeObservable(this, {\n * row: annotation.computed((a, b) => a.id === b.id),\n * });\n * ```\n *\n * @example With {@link applyObservable}\n * ```ts\n * applyObservable(store, [\n * [annotation.observable('shallow'), 'cache', 'index'],\n * [annotation.computed('struct'), 'viewModel'],\n * ]);\n * ```\n *\n * @example `observable.ref` and skipping `computed`\n * ```ts\n * makeObservable(this, {\n * handle: annotation.observable('ref'),\n * skipMe: annotation.computed(false),\n * });\n * ```\n */\n\ntype AnnotationObject = {\n computed(value: false): false;\n computed(\n value?: Exclude<ComputedEqualsValue, false>,\n options?: ComputedOtherOptions,\n ): typeof computed.struct;\n computed(value?: ComputedEqualsValue): false | typeof computed.struct;\n observable(value: Extract<ObservableTypes, false>): false;\n observable(value?: Extract<ObservableTypes, true>): typeof observable;\n observable<TValue extends Exclude<ObservableTypes, boolean>>(\n value: TValue,\n ): (typeof observable)[TValue];\n observable(\n value?: ObservableTypes,\n ): false | (typeof observable)[Exclude<ObservableTypes, boolean>];\n};\n\nexport const annotation = {\n computed: (value?: ComputedEqualsValue, options?: ComputedOtherOptions) => {\n if (value === false) return false;\n\n return computed({\n ...options,\n equals:\n typeof value === 'function'\n ? value\n : (computedEqualsResolvers[\n value as keyof typeof computedEqualsResolvers\n ] ?? comparer.default),\n });\n },\n observable: (value?: ObservableTypes | boolean) => {\n if (value === false) return false;\n if (value === undefined || value === true) return observable;\n return observable[value];\n },\n} as AnnotationObject;\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * Compact **MobX `makeObservable`** wiring from tuple lists of annotations and keys. Reduces boilerplate\n * when many fields share `observable`, `action`, or `computed` decorators and you want one call site\n * instead of sprawling annotation maps across large stores.\n *\n * ## Usage\n *\n * ```ts\n * import { applyObservable } from \"yummies/mobx\";\n * ```\n */\n\nimport { type AnnotationMapEntry, makeObservable } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\nexport type ObservableAnnotationsArray<T extends AnyObject = AnyObject> = [\n AnnotationMapEntry,\n ...(keyof T | (string & {}))[],\n][];\n\n/**\n * Applies a compact list of MobX annotations to an object using either\n * decorator-style invocation or the annotation map form accepted by `makeObservable`.\n *\n * @template T Target object type.\n * @param context Object that should become observable.\n * @param annotationsArray Tuples of annotation followed by annotated field names.\n * @param useDecorators Enables decorator-style application before calling `makeObservable`.\n *\n * @example\n * ```ts\n * applyObservable(store, [[observable, 'items'], [action, 'setItems']]);\n * ```\n *\n * @example\n * ```ts\n * applyObservable(viewModel, [[computed, 'fullName']], true);\n * ```\n */\nexport const applyObservable = <T extends AnyObject>(\n context: T,\n annotationsArray: ObservableAnnotationsArray<T>,\n useDecorators?: boolean,\n) => {\n if (useDecorators) {\n annotationsArray.forEach(([annotation, ...fields]) => {\n fields.forEach((field) => {\n // @ts-expect-error\n annotation(context, field);\n });\n });\n\n makeObservable(context);\n } else {\n const annotationsObject: AnyObject = {};\n\n annotationsArray.forEach(([annotation, ...fields]) => {\n fields.forEach((field) => {\n annotationsObject[field] = annotation;\n });\n });\n\n makeObservable(context, annotationsObject);\n }\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **`createAtom` wrapper** that attaches arbitrary metadata and keeps MobX’s observed/unobserved\n * hooks in one place. Useful for custom reactive primitives, async resources, or debugging atoms\n * where the stock API is too bare.\n *\n * ## Usage\n *\n * ```ts\n * import { createEnhancedAtom } from \"yummies/mobx\";\n * ```\n */\n\nimport { createAtom, type IAtom } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\nexport interface IEnhancedAtom<TMeta extends AnyObject = AnyObject>\n extends IAtom {\n meta: TMeta;\n}\n\n/**\n * Creates a MobX atom extended with metadata and bound reporting methods.\n *\n * @template TMeta Metadata object stored on the atom.\n * @param name Atom name used by MobX for debugging.\n * @param onBecomeObservedHandler Callback fired when the atom becomes observed.\n * @param onBecomeUnobservedHandler Callback fired when the atom is no longer observed.\n * @param meta Optional metadata attached to the atom.\n * @returns Atom instance with `meta`, `reportChanged` and `reportObserved`.\n *\n * @example\n * ```ts\n * const atom = createEnhancedAtom('user-status');\n * atom.reportChanged();\n * ```\n *\n * @example\n * ```ts\n * const atom = createEnhancedAtom('cache', undefined, undefined, { scope: 'users' });\n * atom.meta.scope;\n * ```\n */\nexport const createEnhancedAtom = <TMeta extends AnyObject>(\n name: string,\n onBecomeObservedHandler?: (atom: IEnhancedAtom<TMeta>) => void,\n onBecomeUnobservedHandler?: (atom: IEnhancedAtom<TMeta>) => void,\n meta?: TMeta,\n): IEnhancedAtom<TMeta> => {\n const atom = createAtom(\n name,\n onBecomeObservedHandler && (() => onBecomeObservedHandler(atom)),\n onBecomeUnobservedHandler && (() => onBecomeUnobservedHandler(atom)),\n ) as IEnhancedAtom<TMeta>;\n atom.meta = meta ?? ({} as TMeta);\n atom.reportChanged = atom.reportChanged.bind(atom);\n atom.reportObserved = atom.reportObserved.bind(atom);\n return atom;\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Observable ref** pattern for MobX: boxed mutable references with change listeners, metadata,\n * and optional custom equality. Bridges React-style ref holders and MobX reactivity when a single\n * mutable cell must notify dependents without replacing the whole parent object graph.\n *\n * ## Usage\n *\n * ```ts\n * import { createRef } from \"yummies/mobx\";\n * ```\n */\n\nimport {\n type IEqualsComparer,\n makeObservable,\n comparer as mobxComparer,\n observable,\n runInAction,\n} from 'mobx';\nimport type { AnyObject, Maybe } from 'yummies/types';\n\n/**\n * You can return `false` if you don't want to change the value in this ref\n */\nexport type RefChangeListener<T> = (\n value: T | null,\n prevValue: T | undefined,\n) => void | false;\n\n/**\n * Alternative to React.createRef but works in MobX world.\n * Typically it the should be the same React.LegacyRef (fn style)\n */\nexport interface Ref<T = any, TMeta = AnyObject> {\n /**\n * Setter function\n */\n (value: Maybe<T>): void;\n\n set(value: Maybe<T>): void;\n listeners: Set<RefChangeListener<NoInfer<T>>>;\n current: NoInfer<T> | null;\n meta: TMeta;\n}\n\nexport interface CreateRefConfig<T = any, TMeta = AnyObject> {\n onSet?: (node: T, prevValue: T | undefined) => void;\n onUnset?: (lastValue: T | undefined) => void;\n onChange?: RefChangeListener<T>;\n meta?: TMeta;\n initial?: Maybe<T>;\n comparer?: IEqualsComparer<T | null>;\n}\n\n/**\n * Creates a MobX-aware ref that behaves like a callback ref and exposes\n * observable `current` and `meta` fields.\n *\n * @template T Referenced value type.\n * @template TMeta Additional observable metadata stored on the ref.\n * @param cfg Optional callbacks, initial value and comparer configuration.\n * @returns Observable ref function object.\n *\n * @example\n * ```ts\n * const inputRef = createRef<HTMLInputElement>();\n * inputRef.set(document.createElement('input'));\n * ```\n *\n * @example\n * ```ts\n * const ref = createRef<number>();\n * ref(3);\n * ref.current; // 3\n * ```\n *\n * @example\n * ```ts\n * const nodeRef = createRef({\n * onUnset: () => console.log('detached'),\n * meta: { mounted: false },\n * });\n * ```\n */\nexport const createRef = <T = any, TMeta = AnyObject>(\n cfg?: CreateRefConfig<T, TMeta>,\n): Ref<T, TMeta> => {\n let lastValue: T | undefined;\n const comparer = cfg?.comparer ?? mobxComparer.default;\n\n const setValue: Ref<T, TMeta>['set'] = (value) => {\n const nextValue = value ?? null;\n\n if (comparer(ref.current, nextValue)) {\n return;\n }\n\n runInAction(() => {\n const prevLastValue = lastValue;\n lastValue = ref.current ?? undefined;\n ref.current = nextValue;\n\n let isNextValueIgnored = false;\n\n ref.listeners.forEach((listener) => {\n const listenerResult = listener(ref.current, lastValue);\n\n if (listenerResult === false) {\n isNextValueIgnored = true;\n }\n });\n\n if (isNextValueIgnored) {\n lastValue = prevLastValue;\n ref.current = lastValue ?? null;\n } else if (ref.current === null && lastValue !== undefined) {\n lastValue = undefined;\n }\n });\n };\n\n const ref = setValue as Ref<T, TMeta>;\n\n ref.set = setValue;\n\n ref.listeners = new Set(cfg?.onChange ? [cfg.onChange] : []);\n\n if (cfg?.onSet || cfg?.onUnset) {\n ref.listeners.add((value, prevValue) => {\n if (value) {\n cfg.onSet?.(value, prevValue);\n } else {\n cfg.onUnset?.(prevValue);\n }\n });\n }\n\n ref.current = cfg?.initial ?? null;\n ref.meta = cfg?.meta ?? ({} as TMeta);\n\n makeObservable(ref, {\n current: observable.ref,\n meta: observable,\n });\n\n return ref;\n};\n\n/**\n * Checks whether the provided value is a ref created by `createRef`.\n *\n * @template T Referenced value type.\n * @template TMeta Ref metadata type.\n * @param value Value to inspect.\n * @returns `true` when the value is a ref-like function with `current`.\n *\n * @example\n * ```ts\n * const ref = createRef<number>();\n * isRef(ref); // true\n * ```\n *\n * @example\n * ```ts\n * isRef({ current: 1 }); // false\n * ```\n */\nexport const isRef = <T, TMeta = any>(\n value: T | Ref<T, TMeta>,\n): value is Ref<T, TMeta> => {\n return typeof value === 'function' && 'current' in value;\n};\n\n/**\n * Normalizes a plain value or an existing ref into a `Ref` instance.\n *\n * @template T Referenced value type.\n * @template TMeta Ref metadata type.\n * @param value Existing ref or initial plain value.\n * @param cfg Optional ref configuration applied when a new ref is created.\n * @returns Existing ref or a newly created ref initialized with `value`.\n *\n * @example\n * ```ts\n * const ref = toRef(document.body);\n * ref.current === document.body;\n * ```\n *\n * @example\n * ```ts\n * const existingRef = createRef<number>();\n * const sameRef = toRef(existingRef);\n * ```\n */\nexport const toRef = <T, TMeta = any>(\n value: T | Ref<T, TMeta>,\n cfg?: Omit<CreateRefConfig<T, TMeta>, 'initial'>,\n): Ref<T, TMeta> => {\n return isRef(value) ? value : createRef({ initial: value, ...cfg });\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Deep observable object** with structural `set` patches that reuse nested observables when keys\n * overlap. Helps store trees (forms, filters, entities) under MobX without wholesale replacement\n * and without manual `observable.map` wiring for every level.\n *\n * ## Usage\n *\n * ```ts\n * import { DeepObservableStruct } from \"yummies/mobx\";\n * ```\n */\n\nimport { action, makeObservable, observable } from 'mobx';\nimport { typeGuard } from 'yummies/type-guard';\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * Wraps a plain object into a deeply observable structure and allows\n * patch-like updates while preserving nested observable references where possible.\n *\n * @template TData Observable object shape.\n *\n * @example\n * ```ts\n * const state = new DeepObservableStruct({ user: { name: 'Ann' } });\n * state.set({ user: { name: 'Bob' } });\n * ```\n *\n * @example\n * ```ts\n * const state = new DeepObservableStruct({ filters: { active: true } });\n * state.set({ filters: { active: false, archived: true } });\n * ```\n */\nexport class DeepObservableStruct<TData extends AnyObject> {\n data: TData;\n\n constructor(data: TData) {\n this.data = data;\n\n makeObservable(this, {\n data: observable.deep,\n set: action,\n });\n }\n\n set(newData: Partial<TData>) {\n type StackItem = [key: string, currObservable: AnyObject, new: AnyObject];\n\n const stack: StackItem[] = Object.keys(this.data).map((key) => [\n key,\n this.data,\n newData,\n ]);\n\n let currentIndex = 0;\n let stackLength = stack.length;\n\n while (currentIndex < stackLength) {\n const [key, currObservableData, newData] = stack[currentIndex];\n const newValue = newData[key];\n const currValue = currObservableData[key];\n\n currentIndex++;\n\n if (key in newData) {\n if (typeGuard.isObject(newValue) && typeGuard.isObject(currValue)) {\n const newValueKeys = Object.keys(newValue);\n\n Object.keys(currValue).forEach((childKey) => {\n if (!(childKey in newValue)) {\n delete currObservableData[key][childKey];\n }\n });\n\n newValueKeys.forEach((childKey) => {\n const length = stack.push([\n childKey,\n currObservableData[key],\n newValue,\n ]);\n stackLength = length;\n });\n } else if (newValue !== currValue) {\n currObservableData[key] = newValue;\n }\n } else {\n delete currObservableData[key];\n }\n }\n\n Object.keys(newData).forEach((newDataKey) => {\n if (!this.data[newDataKey]) {\n // @ts-expect-error\n this.data[newDataKey] = newData[newDataKey];\n }\n });\n }\n}\n","import { _getGlobalState } from 'mobx';\nimport type { MobXGlobals } from 'mobx/dist/internal.js';\n\n/** Same cap as MobX's internal `MAX_REACTION_ITERATIONS` (not exported from the package). */\nconst DEFAULT_MAX_REACTION_ITERATIONS = 100;\n\n/**\n * Synchronously runs MobX reactions from the internal `pendingReactions` queue when they piled up\n * during a batch (`inBatch > 0`, e.g. inside `runInAction`).\n *\n * While a batch is open, MobX only enqueues reactions; this call temporarily resets the batch\n * counter, drains the queue, and restores state—useful in tests and when you need side effects\n * before leaving the action.\n *\n * If there are no pending reactions, a reaction run is already in progress (`isRunningReactions`),\n * or the iteration cap is hit (cycle guard), there is no extra work; when the cap is exceeded the\n * queue is cleared, matching MobX's internal safety behavior.\n *\n * @param maxCount - Maximum iterations of the outer drain loop (default 100, same idea as MobX's internal limit).\n * Pass `Number.POSITIVE_INFINITY` to disable this cap only when you trust the reaction graph to settle;\n * a non-converging cycle will then keep looping until the queue empties (or effectively hang).\n *\n * @example\n * ```ts\n * import { observable, reaction, runInAction } from \"mobx\";\n * import { flushPendingReactions } from \"yummies/mobx\";\n *\n * const state = observable({ count: 0 });\n * const log: number[] = [];\n * reaction(() => state.count, (n) => log.push(n));\n *\n * runInAction(() => {\n * state.count = 1;\n * flushPendingReactions();\n * });\n *\n * // log === [1] — the reaction ran before the action finished\n * ```\n */\nexport function flushPendingReactions(\n maxCount = DEFAULT_MAX_REACTION_ITERATIONS,\n): void {\n const gs: MobXGlobals = _getGlobalState();\n\n if (!maxCount || gs.isRunningReactions || gs.pendingReactions.length === 0) {\n return;\n }\n\n const savedInBatch = gs.inBatch;\n gs.inBatch = 0;\n\n try {\n gs.isRunningReactions = true;\n const queue = gs.pendingReactions;\n let iterations = 0;\n\n while (queue.length > 0) {\n if (++iterations === maxCount) {\n queue.splice(0);\n break;\n }\n\n const batch = queue.splice(0);\n for (let i = 0; i < batch.length; i++) {\n batch[i].runReaction_();\n }\n }\n } finally {\n gs.isRunningReactions = false;\n gs.inBatch = savedInBatch;\n }\n}\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * Typed access to MobX **internal administration** (`$mobx`) for advanced tooling, migration scripts,\n * or introspection. Prefer public MobX APIs in application code; reach for this when you must align\n * with library internals or patch behavior at the administration layer.\n *\n * ## Usage\n *\n * ```ts\n * import { getMobxAdministration } from \"yummies/mobx\";\n * ```\n */\n\nimport { $mobx, type AnnotationMapEntry } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\ntype ObservableObjectAdministration = Parameters<\n Exclude<AnnotationMapEntry, boolean>['make_']\n>[0];\n\n/**\n * Returns the internal MobX administration object associated with an observable target.\n *\n * @param context Observable object instance.\n * @returns MobX administration internals stored under `$mobx`.\n *\n * @example\n * ```ts\n * const admin = getMobxAdministration(store);\n * admin.name_;\n * ```\n *\n * @example\n * ```ts\n * const values = getMobxAdministration(formState).values_;\n * ```\n */\nexport const getMobxAdministration = (\n context: AnyObject,\n): ObservableObjectAdministration => context[$mobx];\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Lazy subscriptions** tied to MobX observation: start work when the first reaction observes\n * tracked keys, stop when nothing listens anymore (optionally after a delay). Ideal for polling,\n * WebSocket feeds, or expensive caches that should idle when the UI is not mounted.\n *\n * ## Usage\n *\n * ```ts\n * import { lazyObserve } from \"yummies/mobx\";\n * ```\n */\n\nimport { onBecomeObserved, onBecomeUnobserved } from 'mobx';\n\n/**\n * Starts side effects only while one or more MobX observables are being observed.\n *\n * When the first property becomes observed, `onStart` is called. When all tracked\n * properties become unobserved, `onEnd` is called with the value returned by\n * `onStart`. Cleanup can be delayed via `endDelay`.\n *\n * It uses MobX `onBecomeObserved` and `onBecomeUnobserved` hooks to perform\n * lazy subscription management.\n *\n * @template TMetaData Data returned from `onStart` and forwarded to `onEnd`.\n * @param config Configuration for tracked properties and lifecycle callbacks.\n * @returns Cleanup function that clears the tracked state and runs `onEnd`.\n *\n * @example\n * ```ts\n * const stop = lazyObserve({\n * context: store,\n * property: 'items',\n * onStart: () => api.subscribe(),\n * onEnd: (subscription) => subscription.unsubscribe(),\n * });\n * ```\n *\n * @example\n * ```ts\n * lazyObserve({\n * property: [boxA, boxB],\n * onStart: () => console.log('observed'),\n * endDelay: 300,\n * });\n * ```\n */\nexport const lazyObserve = <TMetaData = void>({\n context,\n property,\n onStart,\n onEnd,\n endDelay = false,\n}: {\n context?: any;\n property: any | any[];\n onStart?: () => TMetaData;\n onEnd?: (metaData: TMetaData, cleanupFn: VoidFunction) => void;\n endDelay?: number | false;\n}) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let metaData: TMetaData | undefined;\n const observingProps = new Set<string>();\n const properties = Array.isArray(property) ? property : [property];\n\n const cleanup = () => {\n observingProps.clear();\n\n if (endDelay === false) {\n onEnd?.(metaData!, cleanup);\n metaData = undefined;\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n\n timeoutId = setTimeout(() => {\n onEnd?.(metaData!, cleanup);\n timeoutId = undefined;\n metaData = undefined;\n }, endDelay);\n };\n\n const start = (property: string) => {\n const isAlreadyObserving = observingProps.size > 0;\n observingProps.add(property);\n\n if (isAlreadyObserving) {\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n\n metaData = onStart?.();\n };\n\n const stop = (property: string) => {\n const isAlreadyNotObserving = !observingProps.size;\n\n observingProps.delete(property);\n\n const isObserving = observingProps.size > 0;\n\n if (isAlreadyNotObserving || isObserving) {\n return;\n }\n\n cleanup();\n };\n\n properties.forEach((property) => {\n if (context) {\n onBecomeObserved(context, property, () => start(property));\n onBecomeUnobserved(context, property, () => stop(property));\n } else {\n onBecomeObserved(property, () => start(property));\n onBecomeUnobserved(property, () => stop(property));\n }\n });\n\n return cleanup;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"mobx.cjs","names":[],"sources":["../src/mobx/annotation.ts","../src/mobx/apply-observable.ts","../src/mobx/create-enhanced-atom.ts","../src/mobx/create-ref.ts","../src/mobx/deep-observable-struct.ts","../src/mobx/flush-pending-reactions.ts","../src/mobx/get-mobx-administration.ts","../src/mobx/lazy-observe.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **`annotation`** — factories for `makeObservable` maps and {@link applyObservable} tuples:\n * `observable.*` flavours, `computed` with shorthand `equals` (`struct`, `shallow`, reference), custom\n * comparators, and `false` to skip a field.\n *\n * ## Usage\n *\n * ```ts\n * import { annotation } from \"yummies/mobx\";\n * ```\n */\n\nimport {\n comparer,\n computed,\n type IComputedValueOptions,\n type IEqualsComparer,\n observable,\n} from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * How MobX should compare the previous and next computed value before notifying observers.\n *\n * - `'struct'` — structural comparison (`comparer.structural`).\n * - `'shallow'` — shallow comparison (`comparer.shallow`).\n * - `true` — reference equality (`comparer.default`).\n * - `false` — skip the annotation (handled by the `annotation.computed` / `annotation.observable` helpers).\n * - A custom `(a, b) => boolean` is allowed by the type for parity with `IComputedValueOptions.equals`.\n */\nexport type ComputedEqualsValue =\n | 'struct'\n | 'shallow'\n | boolean\n | IEqualsComparer<any>;\n\nconst computedEqualsResolvers: AnyObject = {\n true: comparer.default,\n shallow: comparer.shallow,\n struct: comparer.structural,\n} satisfies Record<\n Exclude<ComputedEqualsValue, Function | boolean> | 'true',\n any\n>;\n\n/**\n * Options forwarded to {@link computed}, except `equals` (that argument is passed separately).\n */\nexport type ComputedOtherOptions = Omit<IComputedValueOptions<any>, 'equals'>;\n\n/**\n * Observable flavour keys returned by {@link annotation.observable}: `ref`, `deep`, `shallow`, `struct`.\n * Also supported: `true` (base `observable`) and `false` (no annotation).\n */\nexport type ObservableTypes =\n | keyof Pick<typeof observable, 'ref' | 'deep' | 'shallow' | 'struct'>\n | boolean;\n\n/**\n * MobX annotation factories for `makeObservable` and tuple-style wiring ({@link applyObservable}).\n *\n * - **`annotation.observable(value?)`** — `observable.ref` / `deep` / `shallow` / `struct`; `true` or\n * omitted → base `observable` (deep by default); `false` → `false` (field omitted from the map).\n * - **`annotation.computed(value?, options?)`** — `computed({ ...options, equals })` with `equals` from\n * {@link ComputedEqualsValue} or a custom `(a, b) => boolean`. Omitted `value`, unknown literals, and\n * `true` resolve to `comparer.default`; `value === false` returns `false`.\n *\n * Other `computed` options (except `equals`) go in the second argument; see {@link ComputedOtherOptions}.\n *\n * @example Observable variants\n * ```ts\n * import { makeObservable } from 'mobx';\n * import { annotation } from 'yummies/mobx';\n *\n * class Store {\n * shallowMap = new Map();\n * deep = { nested: { count: 0 } };\n *\n * constructor() {\n * makeObservable(this, {\n * shallowMap: annotation.observable('shallow'),\n * deep: annotation.observable('deep'),\n * });\n * }\n * }\n * ```\n *\n * @example Skip a field\n * ```ts\n * makeObservable(this, {\n * plain: annotation.observable(false), // not decorated\n * });\n * ```\n *\n * @example Computed with structural equality\n * ```ts\n * makeObservable(this, {\n * fullName: annotation.computed('struct', { name: 'fullName' }),\n * });\n * ```\n *\n * @example Computed with default reference equality\n * ```ts\n * makeObservable(this, {\n * total: annotation.computed(true),\n * });\n * ```\n *\n * @example Omitted first argument or `true` — reference equality (`comparer.default`)\n * ```ts\n * makeObservable(this, {\n * n: annotation.computed(undefined, { name: 'n' }),\n * m: annotation.computed(true, { name: 'm' }), // same `equals` as omitted\n * });\n * ```\n *\n * @example Custom `equals`\n * ```ts\n * makeObservable(this, {\n * row: annotation.computed((a, b) => a.id === b.id),\n * });\n * ```\n *\n * @example With {@link applyObservable}\n * ```ts\n * applyObservable(store, [\n * [annotation.observable('shallow'), 'cache', 'index'],\n * [annotation.computed('struct'), 'viewModel'],\n * ]);\n * ```\n *\n * @example `observable.ref` and skipping `computed`\n * ```ts\n * makeObservable(this, {\n * handle: annotation.observable('ref'),\n * skipMe: annotation.computed(false),\n * });\n * ```\n */\n\ntype AnnotationObject = {\n computed(value: false): false;\n computed(\n value?: Exclude<ComputedEqualsValue, false>,\n options?: ComputedOtherOptions,\n ): typeof computed.struct;\n computed(value?: ComputedEqualsValue): false | typeof computed.struct;\n observable(value: Extract<ObservableTypes, false>): false;\n observable(value?: Extract<ObservableTypes, true>): typeof observable;\n observable<TValue extends Exclude<ObservableTypes, boolean>>(\n value: TValue,\n ): (typeof observable)[TValue];\n observable(\n value?: ObservableTypes,\n ): false | (typeof observable)[Exclude<ObservableTypes, boolean>];\n};\n\nexport const annotation = {\n computed: (value?: ComputedEqualsValue, options?: ComputedOtherOptions) => {\n if (value === false) return false;\n\n return computed({\n ...options,\n equals:\n typeof value === 'function'\n ? value\n : (computedEqualsResolvers[\n value as keyof typeof computedEqualsResolvers\n ] ?? comparer.default),\n });\n },\n observable: (value?: ObservableTypes | boolean) => {\n if (value === false) return false;\n if (value === undefined || value === true) return observable;\n return observable[value];\n },\n} as AnnotationObject;\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * Compact **MobX `makeObservable`** wiring from tuple lists of annotations and keys. Reduces boilerplate\n * when many fields share `observable`, `action`, or `computed` decorators and you want one call site\n * instead of sprawling annotation maps across large stores.\n *\n * ## Usage\n *\n * ```ts\n * import { applyObservable } from \"yummies/mobx\";\n * ```\n */\n\nimport { type AnnotationMapEntry, makeObservable } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\nexport type ObservableAnnotationsArray<T extends AnyObject = AnyObject> = [\n AnnotationMapEntry,\n ...(keyof T | (string & {}))[],\n][];\n\n/**\n * Applies a compact list of MobX annotations to an object using either\n * decorator-style invocation or the annotation map form accepted by `makeObservable`.\n *\n * @template T Target object type.\n * @param context Object that should become observable.\n * @param annotationsArray Tuples of annotation followed by annotated field names.\n * @param useDecorators Enables decorator-style application before calling `makeObservable`.\n *\n * @example\n * ```ts\n * applyObservable(store, [[observable, 'items'], [action, 'setItems']]);\n * ```\n *\n * @example\n * ```ts\n * applyObservable(viewModel, [[computed, 'fullName']], true);\n * ```\n */\nexport const applyObservable = <T extends AnyObject>(\n context: T,\n annotationsArray: ObservableAnnotationsArray<T>,\n useDecorators?: boolean,\n) => {\n if (useDecorators) {\n annotationsArray.forEach(([annotation, ...fields]) => {\n fields.forEach((field) => {\n // @ts-expect-error\n annotation(context, field);\n });\n });\n\n makeObservable(context);\n } else {\n const annotationsObject: AnyObject = {};\n\n annotationsArray.forEach(([annotation, ...fields]) => {\n fields.forEach((field) => {\n annotationsObject[field] = annotation;\n });\n });\n\n makeObservable(context, annotationsObject);\n }\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **`createAtom` wrapper** that attaches arbitrary metadata and keeps MobX’s observed/unobserved\n * hooks in one place. Useful for custom reactive primitives, async resources, or debugging atoms\n * where the stock API is too bare.\n *\n * ## Usage\n *\n * ```ts\n * import { createEnhancedAtom } from \"yummies/mobx\";\n * ```\n */\n\nimport { createAtom, type IAtom } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\nexport interface IEnhancedAtom<TMeta extends AnyObject = AnyObject>\n extends IAtom {\n meta: TMeta;\n}\n\n/**\n * Creates a MobX atom extended with metadata and bound reporting methods.\n *\n * @template TMeta Metadata object stored on the atom.\n * @param name Atom name used by MobX for debugging.\n * @param onBecomeObservedHandler Callback fired when the atom becomes observed.\n * @param onBecomeUnobservedHandler Callback fired when the atom is no longer observed.\n * @param meta Optional metadata attached to the atom.\n * @returns Atom instance with `meta`, `reportChanged` and `reportObserved`.\n *\n * @example\n * ```ts\n * const atom = createEnhancedAtom('user-status');\n * atom.reportChanged();\n * ```\n *\n * @example\n * ```ts\n * const atom = createEnhancedAtom('cache', undefined, undefined, { scope: 'users' });\n * atom.meta.scope;\n * ```\n */\nexport const createEnhancedAtom = <TMeta extends AnyObject>(\n name: string,\n onBecomeObservedHandler?: (atom: IEnhancedAtom<TMeta>) => void,\n onBecomeUnobservedHandler?: (atom: IEnhancedAtom<TMeta>) => void,\n meta?: TMeta,\n): IEnhancedAtom<TMeta> => {\n const atom = createAtom(\n name,\n onBecomeObservedHandler && (() => onBecomeObservedHandler(atom)),\n onBecomeUnobservedHandler && (() => onBecomeUnobservedHandler(atom)),\n ) as IEnhancedAtom<TMeta>;\n atom.meta = meta ?? ({} as TMeta);\n atom.reportChanged = atom.reportChanged.bind(atom);\n atom.reportObserved = atom.reportObserved.bind(atom);\n return atom;\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Observable ref** pattern for MobX: boxed mutable references with change listeners, metadata,\n * and optional custom equality. Bridges React-style ref holders and MobX reactivity when a single\n * mutable cell must notify dependents without replacing the whole parent object graph.\n *\n * ## Usage\n *\n * ```ts\n * import { createRef } from \"yummies/mobx\";\n * ```\n */\n\nimport {\n type IEqualsComparer,\n makeObservable,\n comparer as mobxComparer,\n observable,\n runInAction,\n} from 'mobx';\nimport type { AnyObject, Maybe } from 'yummies/types';\n\n/**\n * You can return `false` if you don't want to change the value in this ref\n */\nexport type RefChangeListener<T> = (\n value: T | null,\n prevValue: T | undefined,\n) => void | false;\n\n/**\n * Alternative to React.createRef but works in MobX world.\n * Typically it the should be the same React.LegacyRef (fn style)\n */\nexport interface Ref<T = any, TMeta = AnyObject> {\n /**\n * Setter function\n */\n (value: Maybe<T>): void;\n\n set(value: Maybe<T>): void;\n listeners: Set<RefChangeListener<NoInfer<T>>>;\n current: NoInfer<T> | null;\n meta: TMeta;\n}\n\nexport interface CreateRefConfig<T = any, TMeta = AnyObject> {\n onSet?: (node: T, prevValue: T | undefined) => void;\n onUnset?: (lastValue: T | undefined) => void;\n onChange?: RefChangeListener<T>;\n meta?: TMeta;\n initial?: Maybe<T>;\n comparer?: IEqualsComparer<T | null>;\n}\n\n/**\n * Creates a MobX-aware ref that behaves like a callback ref and exposes\n * observable `current` and `meta` fields.\n *\n * @template T Referenced value type.\n * @template TMeta Additional observable metadata stored on the ref.\n * @param cfg Optional callbacks, initial value and comparer configuration.\n * @returns Observable ref function object.\n *\n * @example\n * ```ts\n * const inputRef = createRef<HTMLInputElement>();\n * inputRef.set(document.createElement('input'));\n * ```\n *\n * @example\n * ```ts\n * const ref = createRef<number>();\n * ref(3);\n * ref.current; // 3\n * ```\n *\n * @example\n * ```ts\n * const nodeRef = createRef({\n * onUnset: () => console.log('detached'),\n * meta: { mounted: false },\n * });\n * ```\n */\nexport const createRef = <T = any, TMeta = AnyObject>(\n cfg?: CreateRefConfig<T, TMeta>,\n): Ref<T, TMeta> => {\n let lastValue: T | undefined;\n const comparer = cfg?.comparer ?? mobxComparer.default;\n\n const setValue: Ref<T, TMeta>['set'] = (value) => {\n const nextValue = value ?? null;\n\n if (comparer(ref.current, nextValue)) {\n return;\n }\n\n runInAction(() => {\n const prevLastValue = lastValue;\n lastValue = ref.current ?? undefined;\n ref.current = nextValue;\n\n let isNextValueIgnored = false;\n\n ref.listeners.forEach((listener) => {\n const listenerResult = listener(ref.current, lastValue);\n\n if (listenerResult === false) {\n isNextValueIgnored = true;\n }\n });\n\n if (isNextValueIgnored) {\n lastValue = prevLastValue;\n ref.current = lastValue ?? null;\n } else if (ref.current === null && lastValue !== undefined) {\n lastValue = undefined;\n }\n });\n };\n\n const ref = setValue as Ref<T, TMeta>;\n\n ref.set = setValue;\n\n ref.listeners = new Set(cfg?.onChange ? [cfg.onChange] : []);\n\n if (cfg?.onSet || cfg?.onUnset) {\n ref.listeners.add((value, prevValue) => {\n if (value) {\n cfg.onSet?.(value, prevValue);\n } else {\n cfg.onUnset?.(prevValue);\n }\n });\n }\n\n ref.current = cfg?.initial ?? null;\n ref.meta = cfg?.meta ?? ({} as TMeta);\n\n makeObservable(ref, {\n current: observable.ref,\n meta: observable,\n });\n\n return ref;\n};\n\n/**\n * Checks whether the provided value is a ref created by `createRef`.\n *\n * @template T Referenced value type.\n * @template TMeta Ref metadata type.\n * @param value Value to inspect.\n * @returns `true` when the value is a ref-like function with `current`.\n *\n * @example\n * ```ts\n * const ref = createRef<number>();\n * isRef(ref); // true\n * ```\n *\n * @example\n * ```ts\n * isRef({ current: 1 }); // false\n * ```\n */\nexport const isRef = <T, TMeta = any>(\n value: T | Ref<T, TMeta>,\n): value is Ref<T, TMeta> => {\n return typeof value === 'function' && 'current' in value;\n};\n\n/**\n * Normalizes a plain value or an existing ref into a `Ref` instance.\n *\n * @template T Referenced value type.\n * @template TMeta Ref metadata type.\n * @param value Existing ref or initial plain value.\n * @param cfg Optional ref configuration applied when a new ref is created.\n * @returns Existing ref or a newly created ref initialized with `value`.\n *\n * @example\n * ```ts\n * const ref = toRef(document.body);\n * ref.current === document.body;\n * ```\n *\n * @example\n * ```ts\n * const existingRef = createRef<number>();\n * const sameRef = toRef(existingRef);\n * ```\n */\nexport const toRef = <T, TMeta = any>(\n value: T | Ref<T, TMeta>,\n cfg?: Omit<CreateRefConfig<T, TMeta>, 'initial'>,\n): Ref<T, TMeta> => {\n return isRef(value) ? value : createRef({ initial: value, ...cfg });\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Deep observable object** with structural `set` patches that reuse nested observables when keys\n * overlap. Helps store trees (forms, filters, entities) under MobX without wholesale replacement\n * and without manual `observable.map` wiring for every level.\n *\n * ## Usage\n *\n * ```ts\n * import { DeepObservableStruct } from \"yummies/mobx\";\n * ```\n */\n\nimport { action, makeObservable, observable } from 'mobx';\nimport { typeGuard } from 'yummies/type-guard';\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * Wraps a plain object into a deeply observable structure and allows\n * patch-like updates while preserving nested observable references where possible.\n *\n * @template TData Observable object shape.\n *\n * @example\n * ```ts\n * const state = new DeepObservableStruct({ user: { name: 'Ann' } });\n * state.set({ user: { name: 'Bob' } });\n * ```\n *\n * @example\n * ```ts\n * const state = new DeepObservableStruct({ filters: { active: true } });\n * state.set({ filters: { active: false, archived: true } });\n * ```\n */\nexport class DeepObservableStruct<TData extends AnyObject> {\n data: TData;\n\n constructor(data: TData) {\n this.data = data;\n\n makeObservable(this, {\n data: observable.deep,\n set: action,\n });\n }\n\n set(newData: Partial<TData>) {\n type StackItem = [key: string, currObservable: AnyObject, new: AnyObject];\n\n const stack: StackItem[] = Object.keys(this.data).map((key) => [\n key,\n this.data,\n newData,\n ]);\n\n let currentIndex = 0;\n let stackLength = stack.length;\n\n while (currentIndex < stackLength) {\n const [key, currObservableData, newData] = stack[currentIndex];\n const newValue = newData[key];\n const currValue = currObservableData[key];\n\n currentIndex++;\n\n if (key in newData) {\n if (typeGuard.isObject(newValue) && typeGuard.isObject(currValue)) {\n const newValueKeys = Object.keys(newValue);\n\n Object.keys(currValue).forEach((childKey) => {\n if (!(childKey in newValue)) {\n delete currObservableData[key][childKey];\n }\n });\n\n newValueKeys.forEach((childKey) => {\n const length = stack.push([\n childKey,\n currObservableData[key],\n newValue,\n ]);\n stackLength = length;\n });\n } else if (newValue !== currValue) {\n currObservableData[key] = newValue;\n }\n } else {\n delete currObservableData[key];\n }\n }\n\n Object.keys(newData).forEach((newDataKey) => {\n if (!this.data[newDataKey]) {\n // @ts-expect-error\n this.data[newDataKey] = newData[newDataKey];\n }\n });\n }\n}\n","import { _getGlobalState } from 'mobx';\nimport type { MobXGlobals } from 'mobx/dist/internal.js';\n\n/** Same cap as MobX's internal `MAX_REACTION_ITERATIONS` (not exported from the package). */\nconst DEFAULT_MAX_REACTION_ITERATIONS = 100;\n\n/**\n * Synchronously runs MobX reactions from the internal `pendingReactions` queue when they piled up\n * during a batch (`inBatch > 0`, e.g. inside `runInAction`).\n *\n * While a batch is open, MobX only enqueues reactions; this call temporarily resets the batch\n * counter, drains the queue, and restores state—useful in tests and when you need side effects\n * before leaving the action.\n *\n * If there are no pending reactions, a reaction run is already in progress (`isRunningReactions`),\n * or the iteration cap is hit (cycle guard), there is no extra work; when the cap is exceeded the\n * queue is cleared, matching MobX's internal safety behavior.\n *\n * @param maxCount - Maximum iterations of the outer drain loop (default 100, same idea as MobX's internal limit).\n * Pass `Number.POSITIVE_INFINITY` to disable this cap only when you trust the reaction graph to settle;\n * a non-converging cycle will then keep looping until the queue empties (or effectively hang).\n *\n * @example\n * ```ts\n * import { observable, reaction, runInAction } from \"mobx\";\n * import { flushPendingReactions } from \"yummies/mobx\";\n *\n * const state = observable({ count: 0 });\n * const log: number[] = [];\n * reaction(() => state.count, (n) => log.push(n));\n *\n * runInAction(() => {\n * state.count = 1;\n * flushPendingReactions();\n * });\n *\n * // log === [1] — the reaction ran before the action finished\n * ```\n */\nexport function flushPendingReactions(\n maxCount = DEFAULT_MAX_REACTION_ITERATIONS,\n): void {\n const gs: MobXGlobals = _getGlobalState();\n\n if (!maxCount || gs.isRunningReactions || gs.pendingReactions.length === 0) {\n return;\n }\n\n const savedInBatch = gs.inBatch;\n gs.inBatch = 0;\n\n try {\n gs.isRunningReactions = true;\n const queue = gs.pendingReactions;\n let iterations = 0;\n\n while (queue.length > 0) {\n if (++iterations === maxCount) {\n queue.splice(0);\n break;\n }\n\n const batch = queue.splice(0);\n for (let i = 0; i < batch.length; i++) {\n batch[i].runReaction_();\n }\n }\n } finally {\n gs.isRunningReactions = false;\n gs.inBatch = savedInBatch;\n }\n}\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * Typed access to MobX **internal administration** (`$mobx`) for advanced tooling, migration scripts,\n * or introspection. Prefer public MobX APIs in application code; reach for this when you must align\n * with library internals or patch behavior at the administration layer.\n *\n * ## Usage\n *\n * ```ts\n * import { getMobxAdministration } from \"yummies/mobx\";\n * ```\n */\n\nimport { $mobx, type AnnotationMapEntry } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\ntype ObservableObjectAdministration = Parameters<\n Exclude<AnnotationMapEntry, boolean>['make_']\n>[0];\n\n/**\n * Returns the internal MobX administration object associated with an observable target.\n *\n * @param context Observable object instance.\n * @returns MobX administration internals stored under `$mobx`.\n *\n * @example\n * ```ts\n * const admin = getMobxAdministration(store);\n * admin.name_;\n * ```\n *\n * @example\n * ```ts\n * const values = getMobxAdministration(formState).values_;\n * ```\n */\nexport const getMobxAdministration = (\n context: AnyObject,\n): ObservableObjectAdministration => context[$mobx];\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Lazy subscriptions** tied to MobX observation: start work when the first reaction observes\n * tracked keys, stop when nothing listens anymore (optionally after a delay). Ideal for polling,\n * WebSocket feeds, or expensive caches that should idle when the UI is not mounted.\n *\n * ## Usage\n *\n * ```ts\n * import { lazyObserve } from \"yummies/mobx\";\n * ```\n */\n\nimport { onBecomeObserved, onBecomeUnobserved } from 'mobx';\n\n/**\n * Starts side effects only while one or more MobX observables are being observed.\n *\n * When the first property becomes observed, `onStart` is called. When all tracked\n * properties become unobserved, `onEnd` is called with the value returned by\n * `onStart`. Cleanup can be delayed via `endDelay`.\n *\n * It uses MobX `onBecomeObserved` and `onBecomeUnobserved` hooks to perform\n * lazy subscription management.\n *\n * @template TMetaData Data returned from `onStart` and forwarded to `onEnd`.\n * @param config Configuration for tracked properties and lifecycle callbacks.\n * @returns Cleanup function that clears the tracked state and runs `onEnd`.\n *\n * @example\n * ```ts\n * const stop = lazyObserve({\n * context: store,\n * property: 'items',\n * onStart: () => api.subscribe(),\n * onEnd: (subscription) => subscription.unsubscribe(),\n * });\n * ```\n *\n * @example\n * ```ts\n * lazyObserve({\n * property: [boxA, boxB],\n * onStart: () => console.log('observed'),\n * endDelay: 300,\n * });\n * ```\n */\nexport const lazyObserve = <TMetaData = void>({\n context,\n property,\n onStart,\n onEnd,\n endDelay = false,\n}: {\n context?: any;\n property: any | any[];\n onStart?: () => TMetaData;\n onEnd?: (metaData: TMetaData, cleanupFn: VoidFunction) => void;\n endDelay?: number | false;\n}) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let metaData: TMetaData | undefined;\n const observingProps = new Set<string>();\n const properties = Array.isArray(property) ? property : [property];\n\n const cleanup = () => {\n observingProps.clear();\n\n if (endDelay === false) {\n onEnd?.(metaData!, cleanup);\n metaData = undefined;\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n\n timeoutId = setTimeout(() => {\n onEnd?.(metaData!, cleanup);\n timeoutId = undefined;\n metaData = undefined;\n }, endDelay);\n };\n\n const start = (property: string) => {\n const isAlreadyObserving = observingProps.size > 0;\n observingProps.add(property);\n\n if (isAlreadyObserving) {\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n\n metaData = onStart?.();\n };\n\n const stop = (property: string) => {\n const isAlreadyNotObserving = !observingProps.size;\n\n observingProps.delete(property);\n\n const isObserving = observingProps.size > 0;\n\n if (isAlreadyNotObserving || isObserving) {\n return;\n }\n\n cleanup();\n };\n\n properties.forEach((property) => {\n if (context) {\n onBecomeObserved(context, property, () => start(property));\n onBecomeUnobserved(context, property, () => stop(property));\n } else {\n onBecomeObserved(property, () => start(property));\n onBecomeUnobserved(property, () => stop(property));\n }\n });\n\n return cleanup;\n};\n"],"mappings":"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"}
package/mobx.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"mobx.js","names":[],"sources":["../src/mobx/annotation.ts","../src/mobx/apply-observable.ts","../src/mobx/create-enhanced-atom.ts","../src/mobx/create-ref.ts","../src/mobx/deep-observable-struct.ts","../src/mobx/flush-pending-reactions.ts","../src/mobx/get-mobx-administration.ts","../src/mobx/lazy-observe.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **`annotation`** — factories for `makeObservable` maps and {@link applyObservable} tuples:\n * `observable.*` flavours, `computed` with shorthand `equals` (`struct`, `shallow`, reference), custom\n * comparators, and `false` to skip a field.\n *\n * ## Usage\n *\n * ```ts\n * import { annotation } from \"yummies/mobx\";\n * ```\n */\n\nimport {\n comparer,\n computed,\n type IComputedValueOptions,\n type IEqualsComparer,\n observable,\n} from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * How MobX should compare the previous and next computed value before notifying observers.\n *\n * - `'struct'` — structural comparison (`comparer.structural`).\n * - `'shallow'` — shallow comparison (`comparer.shallow`).\n * - `true` — reference equality (`comparer.default`).\n * - `false` — skip the annotation (handled by the `annotation.computed` / `annotation.observable` helpers).\n * - A custom `(a, b) => boolean` is allowed by the type for parity with `IComputedValueOptions.equals`.\n */\nexport type ComputedEqualsValue =\n | 'struct'\n | 'shallow'\n | boolean\n | IEqualsComparer<any>;\n\nconst computedEqualsResolvers: AnyObject = {\n true: comparer.default,\n shallow: comparer.shallow,\n struct: comparer.structural,\n} satisfies Record<\n Exclude<ComputedEqualsValue, Function | boolean> | 'true',\n any\n>;\n\n/**\n * Options forwarded to {@link computed}, except `equals` (that argument is passed separately).\n */\nexport type ComputedOtherOptions = Omit<IComputedValueOptions<any>, 'equals'>;\n\n/**\n * Observable flavour keys returned by {@link annotation.observable}: `ref`, `deep`, `shallow`, `struct`.\n * Also supported: `true` (base `observable`) and `false` (no annotation).\n */\nexport type ObservableTypes =\n | keyof Pick<typeof observable, 'ref' | 'deep' | 'shallow' | 'struct'>\n | boolean;\n\n/**\n * MobX annotation factories for `makeObservable` and tuple-style wiring ({@link applyObservable}).\n *\n * - **`annotation.observable(value?)`** — `observable.ref` / `deep` / `shallow` / `struct`; `true` or\n * omitted → base `observable` (deep by default); `false` → `false` (field omitted from the map).\n * - **`annotation.computed(value?, options?)`** — `computed({ ...options, equals })` with `equals` from\n * {@link ComputedEqualsValue} or a custom `(a, b) => boolean`. Omitted `value`, unknown literals, and\n * `true` resolve to `comparer.default`; `value === false` returns `false`.\n *\n * Other `computed` options (except `equals`) go in the second argument; see {@link ComputedOtherOptions}.\n *\n * @example Observable variants\n * ```ts\n * import { makeObservable } from 'mobx';\n * import { annotation } from 'yummies/mobx';\n *\n * class Store {\n * shallowMap = new Map();\n * deep = { nested: { count: 0 } };\n *\n * constructor() {\n * makeObservable(this, {\n * shallowMap: annotation.observable('shallow'),\n * deep: annotation.observable('deep'),\n * });\n * }\n * }\n * ```\n *\n * @example Skip a field\n * ```ts\n * makeObservable(this, {\n * plain: annotation.observable(false), // not decorated\n * });\n * ```\n *\n * @example Computed with structural equality\n * ```ts\n * makeObservable(this, {\n * fullName: annotation.computed('struct', { name: 'fullName' }),\n * });\n * ```\n *\n * @example Computed with default reference equality\n * ```ts\n * makeObservable(this, {\n * total: annotation.computed(true),\n * });\n * ```\n *\n * @example Omitted first argument or `true` — reference equality (`comparer.default`)\n * ```ts\n * makeObservable(this, {\n * n: annotation.computed(undefined, { name: 'n' }),\n * m: annotation.computed(true, { name: 'm' }), // same `equals` as omitted\n * });\n * ```\n *\n * @example Custom `equals`\n * ```ts\n * makeObservable(this, {\n * row: annotation.computed((a, b) => a.id === b.id),\n * });\n * ```\n *\n * @example With {@link applyObservable}\n * ```ts\n * applyObservable(store, [\n * [annotation.observable('shallow'), 'cache', 'index'],\n * [annotation.computed('struct'), 'viewModel'],\n * ]);\n * ```\n *\n * @example `observable.ref` and skipping `computed`\n * ```ts\n * makeObservable(this, {\n * handle: annotation.observable('ref'),\n * skipMe: annotation.computed(false),\n * });\n * ```\n */\n\ntype AnnotationObject = {\n computed(value: false): false;\n computed(\n value?: Exclude<ComputedEqualsValue, false>,\n options?: ComputedOtherOptions,\n ): typeof computed.struct;\n computed(value?: ComputedEqualsValue): false | typeof computed.struct;\n observable(value: Extract<ObservableTypes, false>): false;\n observable(value?: Extract<ObservableTypes, true>): typeof observable;\n observable<TValue extends Exclude<ObservableTypes, boolean>>(\n value: TValue,\n ): (typeof observable)[TValue];\n observable(\n value?: ObservableTypes,\n ): false | (typeof observable)[Exclude<ObservableTypes, boolean>];\n};\n\nexport const annotation = {\n computed: (value?: ComputedEqualsValue, options?: ComputedOtherOptions) => {\n if (value === false) return false;\n\n return computed({\n ...options,\n equals:\n typeof value === 'function'\n ? value\n : (computedEqualsResolvers[\n value as keyof typeof computedEqualsResolvers\n ] ?? comparer.default),\n });\n },\n observable: (value?: ObservableTypes | boolean) => {\n if (value === false) return false;\n if (value === undefined || value === true) return observable;\n return observable[value];\n },\n} as AnnotationObject;\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * Compact **MobX `makeObservable`** wiring from tuple lists of annotations and keys. Reduces boilerplate\n * when many fields share `observable`, `action`, or `computed` decorators and you want one call site\n * instead of sprawling annotation maps across large stores.\n *\n * ## Usage\n *\n * ```ts\n * import { applyObservable } from \"yummies/mobx\";\n * ```\n */\n\nimport { type AnnotationMapEntry, makeObservable } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\nexport type ObservableAnnotationsArray<T extends AnyObject = AnyObject> = [\n AnnotationMapEntry,\n ...(keyof T | (string & {}))[],\n][];\n\n/**\n * Applies a compact list of MobX annotations to an object using either\n * decorator-style invocation or the annotation map form accepted by `makeObservable`.\n *\n * @template T Target object type.\n * @param context Object that should become observable.\n * @param annotationsArray Tuples of annotation followed by annotated field names.\n * @param useDecorators Enables decorator-style application before calling `makeObservable`.\n *\n * @example\n * ```ts\n * applyObservable(store, [[observable, 'items'], [action, 'setItems']]);\n * ```\n *\n * @example\n * ```ts\n * applyObservable(viewModel, [[computed, 'fullName']], true);\n * ```\n */\nexport const applyObservable = <T extends AnyObject>(\n context: T,\n annotationsArray: ObservableAnnotationsArray<T>,\n useDecorators?: boolean,\n) => {\n if (useDecorators) {\n annotationsArray.forEach(([annotation, ...fields]) => {\n fields.forEach((field) => {\n // @ts-expect-error\n annotation(context, field);\n });\n });\n\n makeObservable(context);\n } else {\n const annotationsObject: AnyObject = {};\n\n annotationsArray.forEach(([annotation, ...fields]) => {\n fields.forEach((field) => {\n annotationsObject[field] = annotation;\n });\n });\n\n makeObservable(context, annotationsObject);\n }\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **`createAtom` wrapper** that attaches arbitrary metadata and keeps MobX’s observed/unobserved\n * hooks in one place. Useful for custom reactive primitives, async resources, or debugging atoms\n * where the stock API is too bare.\n *\n * ## Usage\n *\n * ```ts\n * import { createEnhancedAtom } from \"yummies/mobx\";\n * ```\n */\n\nimport { createAtom, type IAtom } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\nexport interface IEnhancedAtom<TMeta extends AnyObject = AnyObject>\n extends IAtom {\n meta: TMeta;\n}\n\n/**\n * Creates a MobX atom extended with metadata and bound reporting methods.\n *\n * @template TMeta Metadata object stored on the atom.\n * @param name Atom name used by MobX for debugging.\n * @param onBecomeObservedHandler Callback fired when the atom becomes observed.\n * @param onBecomeUnobservedHandler Callback fired when the atom is no longer observed.\n * @param meta Optional metadata attached to the atom.\n * @returns Atom instance with `meta`, `reportChanged` and `reportObserved`.\n *\n * @example\n * ```ts\n * const atom = createEnhancedAtom('user-status');\n * atom.reportChanged();\n * ```\n *\n * @example\n * ```ts\n * const atom = createEnhancedAtom('cache', undefined, undefined, { scope: 'users' });\n * atom.meta.scope;\n * ```\n */\nexport const createEnhancedAtom = <TMeta extends AnyObject>(\n name: string,\n onBecomeObservedHandler?: (atom: IEnhancedAtom<TMeta>) => void,\n onBecomeUnobservedHandler?: (atom: IEnhancedAtom<TMeta>) => void,\n meta?: TMeta,\n): IEnhancedAtom<TMeta> => {\n const atom = createAtom(\n name,\n onBecomeObservedHandler && (() => onBecomeObservedHandler(atom)),\n onBecomeUnobservedHandler && (() => onBecomeUnobservedHandler(atom)),\n ) as IEnhancedAtom<TMeta>;\n atom.meta = meta ?? ({} as TMeta);\n atom.reportChanged = atom.reportChanged.bind(atom);\n atom.reportObserved = atom.reportObserved.bind(atom);\n return atom;\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Observable ref** pattern for MobX: boxed mutable references with change listeners, metadata,\n * and optional custom equality. Bridges React-style ref holders and MobX reactivity when a single\n * mutable cell must notify dependents without replacing the whole parent object graph.\n *\n * ## Usage\n *\n * ```ts\n * import { createRef } from \"yummies/mobx\";\n * ```\n */\n\nimport {\n type IEqualsComparer,\n makeObservable,\n comparer as mobxComparer,\n observable,\n runInAction,\n} from 'mobx';\nimport type { AnyObject, Maybe } from 'yummies/types';\n\n/**\n * You can return `false` if you don't want to change the value in this ref\n */\nexport type RefChangeListener<T> = (\n value: T | null,\n prevValue: T | undefined,\n) => void | false;\n\n/**\n * Alternative to React.createRef but works in MobX world.\n * Typically it the should be the same React.LegacyRef (fn style)\n */\nexport interface Ref<T = any, TMeta = AnyObject> {\n /**\n * Setter function\n */\n (value: Maybe<T>): void;\n\n set(value: Maybe<T>): void;\n listeners: Set<RefChangeListener<NoInfer<T>>>;\n current: NoInfer<T> | null;\n meta: TMeta;\n}\n\nexport interface CreateRefConfig<T = any, TMeta = AnyObject> {\n onSet?: (node: T, prevValue: T | undefined) => void;\n onUnset?: (lastValue: T | undefined) => void;\n onChange?: RefChangeListener<T>;\n meta?: TMeta;\n initial?: Maybe<T>;\n comparer?: IEqualsComparer<T | null>;\n}\n\n/**\n * Creates a MobX-aware ref that behaves like a callback ref and exposes\n * observable `current` and `meta` fields.\n *\n * @template T Referenced value type.\n * @template TMeta Additional observable metadata stored on the ref.\n * @param cfg Optional callbacks, initial value and comparer configuration.\n * @returns Observable ref function object.\n *\n * @example\n * ```ts\n * const inputRef = createRef<HTMLInputElement>();\n * inputRef.set(document.createElement('input'));\n * ```\n *\n * @example\n * ```ts\n * const ref = createRef<number>();\n * ref(3);\n * ref.current; // 3\n * ```\n *\n * @example\n * ```ts\n * const nodeRef = createRef({\n * onUnset: () => console.log('detached'),\n * meta: { mounted: false },\n * });\n * ```\n */\nexport const createRef = <T = any, TMeta = AnyObject>(\n cfg?: CreateRefConfig<T, TMeta>,\n): Ref<T, TMeta> => {\n let lastValue: T | undefined;\n const comparer = cfg?.comparer ?? mobxComparer.default;\n\n const setValue: Ref<T, TMeta>['set'] = (value) => {\n const nextValue = value ?? null;\n\n if (comparer(ref.current, nextValue)) {\n return;\n }\n\n runInAction(() => {\n const prevLastValue = lastValue;\n lastValue = ref.current ?? undefined;\n ref.current = nextValue;\n\n let isNextValueIgnored = false;\n\n ref.listeners.forEach((listener) => {\n const listenerResult = listener(ref.current, lastValue);\n\n if (listenerResult === false) {\n isNextValueIgnored = true;\n }\n });\n\n if (isNextValueIgnored) {\n lastValue = prevLastValue;\n ref.current = lastValue ?? null;\n } else if (ref.current === null && lastValue !== undefined) {\n lastValue = undefined;\n }\n });\n };\n\n const ref = setValue as Ref<T, TMeta>;\n\n ref.set = setValue;\n\n ref.listeners = new Set(cfg?.onChange ? [cfg.onChange] : []);\n\n if (cfg?.onSet || cfg?.onUnset) {\n ref.listeners.add((value, prevValue) => {\n if (value) {\n cfg.onSet?.(value, prevValue);\n } else {\n cfg.onUnset?.(prevValue);\n }\n });\n }\n\n ref.current = cfg?.initial ?? null;\n ref.meta = cfg?.meta ?? ({} as TMeta);\n\n makeObservable(ref, {\n current: observable.ref,\n meta: observable,\n });\n\n return ref;\n};\n\n/**\n * Checks whether the provided value is a ref created by `createRef`.\n *\n * @template T Referenced value type.\n * @template TMeta Ref metadata type.\n * @param value Value to inspect.\n * @returns `true` when the value is a ref-like function with `current`.\n *\n * @example\n * ```ts\n * const ref = createRef<number>();\n * isRef(ref); // true\n * ```\n *\n * @example\n * ```ts\n * isRef({ current: 1 }); // false\n * ```\n */\nexport const isRef = <T, TMeta = any>(\n value: T | Ref<T, TMeta>,\n): value is Ref<T, TMeta> => {\n return typeof value === 'function' && 'current' in value;\n};\n\n/**\n * Normalizes a plain value or an existing ref into a `Ref` instance.\n *\n * @template T Referenced value type.\n * @template TMeta Ref metadata type.\n * @param value Existing ref or initial plain value.\n * @param cfg Optional ref configuration applied when a new ref is created.\n * @returns Existing ref or a newly created ref initialized with `value`.\n *\n * @example\n * ```ts\n * const ref = toRef(document.body);\n * ref.current === document.body;\n * ```\n *\n * @example\n * ```ts\n * const existingRef = createRef<number>();\n * const sameRef = toRef(existingRef);\n * ```\n */\nexport const toRef = <T, TMeta = any>(\n value: T | Ref<T, TMeta>,\n cfg?: Omit<CreateRefConfig<T, TMeta>, 'initial'>,\n): Ref<T, TMeta> => {\n return isRef(value) ? value : createRef({ initial: value, ...cfg });\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Deep observable object** with structural `set` patches that reuse nested observables when keys\n * overlap. Helps store trees (forms, filters, entities) under MobX without wholesale replacement\n * and without manual `observable.map` wiring for every level.\n *\n * ## Usage\n *\n * ```ts\n * import { DeepObservableStruct } from \"yummies/mobx\";\n * ```\n */\n\nimport { action, makeObservable, observable } from 'mobx';\nimport { typeGuard } from 'yummies/type-guard';\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * Wraps a plain object into a deeply observable structure and allows\n * patch-like updates while preserving nested observable references where possible.\n *\n * @template TData Observable object shape.\n *\n * @example\n * ```ts\n * const state = new DeepObservableStruct({ user: { name: 'Ann' } });\n * state.set({ user: { name: 'Bob' } });\n * ```\n *\n * @example\n * ```ts\n * const state = new DeepObservableStruct({ filters: { active: true } });\n * state.set({ filters: { active: false, archived: true } });\n * ```\n */\nexport class DeepObservableStruct<TData extends AnyObject> {\n data: TData;\n\n constructor(data: TData) {\n this.data = data;\n\n makeObservable(this, {\n data: observable.deep,\n set: action,\n });\n }\n\n set(newData: Partial<TData>) {\n type StackItem = [key: string, currObservable: AnyObject, new: AnyObject];\n\n const stack: StackItem[] = Object.keys(this.data).map((key) => [\n key,\n this.data,\n newData,\n ]);\n\n let currentIndex = 0;\n let stackLength = stack.length;\n\n while (currentIndex < stackLength) {\n const [key, currObservableData, newData] = stack[currentIndex];\n const newValue = newData[key];\n const currValue = currObservableData[key];\n\n currentIndex++;\n\n if (key in newData) {\n if (typeGuard.isObject(newValue) && typeGuard.isObject(currValue)) {\n const newValueKeys = Object.keys(newValue);\n\n Object.keys(currValue).forEach((childKey) => {\n if (!(childKey in newValue)) {\n delete currObservableData[key][childKey];\n }\n });\n\n newValueKeys.forEach((childKey) => {\n const length = stack.push([\n childKey,\n currObservableData[key],\n newValue,\n ]);\n stackLength = length;\n });\n } else if (newValue !== currValue) {\n currObservableData[key] = newValue;\n }\n } else {\n delete currObservableData[key];\n }\n }\n\n Object.keys(newData).forEach((newDataKey) => {\n if (!this.data[newDataKey]) {\n // @ts-expect-error\n this.data[newDataKey] = newData[newDataKey];\n }\n });\n }\n}\n","import { _getGlobalState } from 'mobx';\nimport type { MobXGlobals } from 'mobx/dist/internal.js';\n\n/** Same cap as MobX's internal `MAX_REACTION_ITERATIONS` (not exported from the package). */\nconst DEFAULT_MAX_REACTION_ITERATIONS = 100;\n\n/**\n * Synchronously runs MobX reactions from the internal `pendingReactions` queue when they piled up\n * during a batch (`inBatch > 0`, e.g. inside `runInAction`).\n *\n * While a batch is open, MobX only enqueues reactions; this call temporarily resets the batch\n * counter, drains the queue, and restores state—useful in tests and when you need side effects\n * before leaving the action.\n *\n * If there are no pending reactions, a reaction run is already in progress (`isRunningReactions`),\n * or the iteration cap is hit (cycle guard), there is no extra work; when the cap is exceeded the\n * queue is cleared, matching MobX's internal safety behavior.\n *\n * @param maxCount - Maximum iterations of the outer drain loop (default 100, same idea as MobX's internal limit).\n * Pass `Number.POSITIVE_INFINITY` to disable this cap only when you trust the reaction graph to settle;\n * a non-converging cycle will then keep looping until the queue empties (or effectively hang).\n *\n * @example\n * ```ts\n * import { observable, reaction, runInAction } from \"mobx\";\n * import { flushPendingReactions } from \"yummies/mobx\";\n *\n * const state = observable({ count: 0 });\n * const log: number[] = [];\n * reaction(() => state.count, (n) => log.push(n));\n *\n * runInAction(() => {\n * state.count = 1;\n * flushPendingReactions();\n * });\n *\n * // log === [1] — the reaction ran before the action finished\n * ```\n */\nexport function flushPendingReactions(\n maxCount = DEFAULT_MAX_REACTION_ITERATIONS,\n): void {\n const gs: MobXGlobals = _getGlobalState();\n\n if (!maxCount || gs.isRunningReactions || gs.pendingReactions.length === 0) {\n return;\n }\n\n const savedInBatch = gs.inBatch;\n gs.inBatch = 0;\n\n try {\n gs.isRunningReactions = true;\n const queue = gs.pendingReactions;\n let iterations = 0;\n\n while (queue.length > 0) {\n if (++iterations === maxCount) {\n queue.splice(0);\n break;\n }\n\n const batch = queue.splice(0);\n for (let i = 0; i < batch.length; i++) {\n batch[i].runReaction_();\n }\n }\n } finally {\n gs.isRunningReactions = false;\n gs.inBatch = savedInBatch;\n }\n}\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * Typed access to MobX **internal administration** (`$mobx`) for advanced tooling, migration scripts,\n * or introspection. Prefer public MobX APIs in application code; reach for this when you must align\n * with library internals or patch behavior at the administration layer.\n *\n * ## Usage\n *\n * ```ts\n * import { getMobxAdministration } from \"yummies/mobx\";\n * ```\n */\n\nimport { $mobx, type AnnotationMapEntry } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\ntype ObservableObjectAdministration = Parameters<\n Exclude<AnnotationMapEntry, boolean>['make_']\n>[0];\n\n/**\n * Returns the internal MobX administration object associated with an observable target.\n *\n * @param context Observable object instance.\n * @returns MobX administration internals stored under `$mobx`.\n *\n * @example\n * ```ts\n * const admin = getMobxAdministration(store);\n * admin.name_;\n * ```\n *\n * @example\n * ```ts\n * const values = getMobxAdministration(formState).values_;\n * ```\n */\nexport const getMobxAdministration = (\n context: AnyObject,\n): ObservableObjectAdministration => context[$mobx];\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Lazy subscriptions** tied to MobX observation: start work when the first reaction observes\n * tracked keys, stop when nothing listens anymore (optionally after a delay). Ideal for polling,\n * WebSocket feeds, or expensive caches that should idle when the UI is not mounted.\n *\n * ## Usage\n *\n * ```ts\n * import { lazyObserve } from \"yummies/mobx\";\n * ```\n */\n\nimport { onBecomeObserved, onBecomeUnobserved } from 'mobx';\n\n/**\n * Starts side effects only while one or more MobX observables are being observed.\n *\n * When the first property becomes observed, `onStart` is called. When all tracked\n * properties become unobserved, `onEnd` is called with the value returned by\n * `onStart`. Cleanup can be delayed via `endDelay`.\n *\n * It uses MobX `onBecomeObserved` and `onBecomeUnobserved` hooks to perform\n * lazy subscription management.\n *\n * @template TMetaData Data returned from `onStart` and forwarded to `onEnd`.\n * @param config Configuration for tracked properties and lifecycle callbacks.\n * @returns Cleanup function that clears the tracked state and runs `onEnd`.\n *\n * @example\n * ```ts\n * const stop = lazyObserve({\n * context: store,\n * property: 'items',\n * onStart: () => api.subscribe(),\n * onEnd: (subscription) => subscription.unsubscribe(),\n * });\n * ```\n *\n * @example\n * ```ts\n * lazyObserve({\n * property: [boxA, boxB],\n * onStart: () => console.log('observed'),\n * endDelay: 300,\n * });\n * ```\n */\nexport const lazyObserve = <TMetaData = void>({\n context,\n property,\n onStart,\n onEnd,\n endDelay = false,\n}: {\n context?: any;\n property: any | any[];\n onStart?: () => TMetaData;\n onEnd?: (metaData: TMetaData, cleanupFn: VoidFunction) => void;\n endDelay?: number | false;\n}) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let metaData: TMetaData | undefined;\n const observingProps = new Set<string>();\n const properties = Array.isArray(property) ? property : [property];\n\n const cleanup = () => {\n observingProps.clear();\n\n if (endDelay === false) {\n onEnd?.(metaData!, cleanup);\n metaData = undefined;\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n\n timeoutId = setTimeout(() => {\n onEnd?.(metaData!, cleanup);\n timeoutId = undefined;\n metaData = undefined;\n }, endDelay);\n };\n\n const start = (property: string) => {\n const isAlreadyObserving = observingProps.size > 0;\n observingProps.add(property);\n\n if (isAlreadyObserving) {\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n\n metaData = onStart?.();\n };\n\n const stop = (property: string) => {\n const isAlreadyNotObserving = !observingProps.size;\n\n observingProps.delete(property);\n\n const isObserving = observingProps.size > 0;\n\n if (isAlreadyNotObserving || isObserving) {\n return;\n }\n\n cleanup();\n };\n\n properties.forEach((property) => {\n if (context) {\n onBecomeObserved(context, property, () => start(property));\n onBecomeUnobserved(context, property, () => stop(property));\n } else {\n onBecomeObserved(property, () => start(property));\n onBecomeUnobserved(property, () => stop(property));\n }\n });\n\n return cleanup;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"mobx.js","names":[],"sources":["../src/mobx/annotation.ts","../src/mobx/apply-observable.ts","../src/mobx/create-enhanced-atom.ts","../src/mobx/create-ref.ts","../src/mobx/deep-observable-struct.ts","../src/mobx/flush-pending-reactions.ts","../src/mobx/get-mobx-administration.ts","../src/mobx/lazy-observe.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **`annotation`** — factories for `makeObservable` maps and {@link applyObservable} tuples:\n * `observable.*` flavours, `computed` with shorthand `equals` (`struct`, `shallow`, reference), custom\n * comparators, and `false` to skip a field.\n *\n * ## Usage\n *\n * ```ts\n * import { annotation } from \"yummies/mobx\";\n * ```\n */\n\nimport {\n comparer,\n computed,\n type IComputedValueOptions,\n type IEqualsComparer,\n observable,\n} from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * How MobX should compare the previous and next computed value before notifying observers.\n *\n * - `'struct'` — structural comparison (`comparer.structural`).\n * - `'shallow'` — shallow comparison (`comparer.shallow`).\n * - `true` — reference equality (`comparer.default`).\n * - `false` — skip the annotation (handled by the `annotation.computed` / `annotation.observable` helpers).\n * - A custom `(a, b) => boolean` is allowed by the type for parity with `IComputedValueOptions.equals`.\n */\nexport type ComputedEqualsValue =\n | 'struct'\n | 'shallow'\n | boolean\n | IEqualsComparer<any>;\n\nconst computedEqualsResolvers: AnyObject = {\n true: comparer.default,\n shallow: comparer.shallow,\n struct: comparer.structural,\n} satisfies Record<\n Exclude<ComputedEqualsValue, Function | boolean> | 'true',\n any\n>;\n\n/**\n * Options forwarded to {@link computed}, except `equals` (that argument is passed separately).\n */\nexport type ComputedOtherOptions = Omit<IComputedValueOptions<any>, 'equals'>;\n\n/**\n * Observable flavour keys returned by {@link annotation.observable}: `ref`, `deep`, `shallow`, `struct`.\n * Also supported: `true` (base `observable`) and `false` (no annotation).\n */\nexport type ObservableTypes =\n | keyof Pick<typeof observable, 'ref' | 'deep' | 'shallow' | 'struct'>\n | boolean;\n\n/**\n * MobX annotation factories for `makeObservable` and tuple-style wiring ({@link applyObservable}).\n *\n * - **`annotation.observable(value?)`** — `observable.ref` / `deep` / `shallow` / `struct`; `true` or\n * omitted → base `observable` (deep by default); `false` → `false` (field omitted from the map).\n * - **`annotation.computed(value?, options?)`** — `computed({ ...options, equals })` with `equals` from\n * {@link ComputedEqualsValue} or a custom `(a, b) => boolean`. Omitted `value`, unknown literals, and\n * `true` resolve to `comparer.default`; `value === false` returns `false`.\n *\n * Other `computed` options (except `equals`) go in the second argument; see {@link ComputedOtherOptions}.\n *\n * @example Observable variants\n * ```ts\n * import { makeObservable } from 'mobx';\n * import { annotation } from 'yummies/mobx';\n *\n * class Store {\n * shallowMap = new Map();\n * deep = { nested: { count: 0 } };\n *\n * constructor() {\n * makeObservable(this, {\n * shallowMap: annotation.observable('shallow'),\n * deep: annotation.observable('deep'),\n * });\n * }\n * }\n * ```\n *\n * @example Skip a field\n * ```ts\n * makeObservable(this, {\n * plain: annotation.observable(false), // not decorated\n * });\n * ```\n *\n * @example Computed with structural equality\n * ```ts\n * makeObservable(this, {\n * fullName: annotation.computed('struct', { name: 'fullName' }),\n * });\n * ```\n *\n * @example Computed with default reference equality\n * ```ts\n * makeObservable(this, {\n * total: annotation.computed(true),\n * });\n * ```\n *\n * @example Omitted first argument or `true` — reference equality (`comparer.default`)\n * ```ts\n * makeObservable(this, {\n * n: annotation.computed(undefined, { name: 'n' }),\n * m: annotation.computed(true, { name: 'm' }), // same `equals` as omitted\n * });\n * ```\n *\n * @example Custom `equals`\n * ```ts\n * makeObservable(this, {\n * row: annotation.computed((a, b) => a.id === b.id),\n * });\n * ```\n *\n * @example With {@link applyObservable}\n * ```ts\n * applyObservable(store, [\n * [annotation.observable('shallow'), 'cache', 'index'],\n * [annotation.computed('struct'), 'viewModel'],\n * ]);\n * ```\n *\n * @example `observable.ref` and skipping `computed`\n * ```ts\n * makeObservable(this, {\n * handle: annotation.observable('ref'),\n * skipMe: annotation.computed(false),\n * });\n * ```\n */\n\ntype AnnotationObject = {\n computed(value: false): false;\n computed(\n value?: Exclude<ComputedEqualsValue, false>,\n options?: ComputedOtherOptions,\n ): typeof computed.struct;\n computed(value?: ComputedEqualsValue): false | typeof computed.struct;\n observable(value: Extract<ObservableTypes, false>): false;\n observable(value?: Extract<ObservableTypes, true>): typeof observable;\n observable<TValue extends Exclude<ObservableTypes, boolean>>(\n value: TValue,\n ): (typeof observable)[TValue];\n observable(\n value?: ObservableTypes,\n ): false | (typeof observable)[Exclude<ObservableTypes, boolean>];\n};\n\nexport const annotation = {\n computed: (value?: ComputedEqualsValue, options?: ComputedOtherOptions) => {\n if (value === false) return false;\n\n return computed({\n ...options,\n equals:\n typeof value === 'function'\n ? value\n : (computedEqualsResolvers[\n value as keyof typeof computedEqualsResolvers\n ] ?? comparer.default),\n });\n },\n observable: (value?: ObservableTypes | boolean) => {\n if (value === false) return false;\n if (value === undefined || value === true) return observable;\n return observable[value];\n },\n} as AnnotationObject;\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * Compact **MobX `makeObservable`** wiring from tuple lists of annotations and keys. Reduces boilerplate\n * when many fields share `observable`, `action`, or `computed` decorators and you want one call site\n * instead of sprawling annotation maps across large stores.\n *\n * ## Usage\n *\n * ```ts\n * import { applyObservable } from \"yummies/mobx\";\n * ```\n */\n\nimport { type AnnotationMapEntry, makeObservable } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\nexport type ObservableAnnotationsArray<T extends AnyObject = AnyObject> = [\n AnnotationMapEntry,\n ...(keyof T | (string & {}))[],\n][];\n\n/**\n * Applies a compact list of MobX annotations to an object using either\n * decorator-style invocation or the annotation map form accepted by `makeObservable`.\n *\n * @template T Target object type.\n * @param context Object that should become observable.\n * @param annotationsArray Tuples of annotation followed by annotated field names.\n * @param useDecorators Enables decorator-style application before calling `makeObservable`.\n *\n * @example\n * ```ts\n * applyObservable(store, [[observable, 'items'], [action, 'setItems']]);\n * ```\n *\n * @example\n * ```ts\n * applyObservable(viewModel, [[computed, 'fullName']], true);\n * ```\n */\nexport const applyObservable = <T extends AnyObject>(\n context: T,\n annotationsArray: ObservableAnnotationsArray<T>,\n useDecorators?: boolean,\n) => {\n if (useDecorators) {\n annotationsArray.forEach(([annotation, ...fields]) => {\n fields.forEach((field) => {\n // @ts-expect-error\n annotation(context, field);\n });\n });\n\n makeObservable(context);\n } else {\n const annotationsObject: AnyObject = {};\n\n annotationsArray.forEach(([annotation, ...fields]) => {\n fields.forEach((field) => {\n annotationsObject[field] = annotation;\n });\n });\n\n makeObservable(context, annotationsObject);\n }\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **`createAtom` wrapper** that attaches arbitrary metadata and keeps MobX’s observed/unobserved\n * hooks in one place. Useful for custom reactive primitives, async resources, or debugging atoms\n * where the stock API is too bare.\n *\n * ## Usage\n *\n * ```ts\n * import { createEnhancedAtom } from \"yummies/mobx\";\n * ```\n */\n\nimport { createAtom, type IAtom } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\nexport interface IEnhancedAtom<TMeta extends AnyObject = AnyObject>\n extends IAtom {\n meta: TMeta;\n}\n\n/**\n * Creates a MobX atom extended with metadata and bound reporting methods.\n *\n * @template TMeta Metadata object stored on the atom.\n * @param name Atom name used by MobX for debugging.\n * @param onBecomeObservedHandler Callback fired when the atom becomes observed.\n * @param onBecomeUnobservedHandler Callback fired when the atom is no longer observed.\n * @param meta Optional metadata attached to the atom.\n * @returns Atom instance with `meta`, `reportChanged` and `reportObserved`.\n *\n * @example\n * ```ts\n * const atom = createEnhancedAtom('user-status');\n * atom.reportChanged();\n * ```\n *\n * @example\n * ```ts\n * const atom = createEnhancedAtom('cache', undefined, undefined, { scope: 'users' });\n * atom.meta.scope;\n * ```\n */\nexport const createEnhancedAtom = <TMeta extends AnyObject>(\n name: string,\n onBecomeObservedHandler?: (atom: IEnhancedAtom<TMeta>) => void,\n onBecomeUnobservedHandler?: (atom: IEnhancedAtom<TMeta>) => void,\n meta?: TMeta,\n): IEnhancedAtom<TMeta> => {\n const atom = createAtom(\n name,\n onBecomeObservedHandler && (() => onBecomeObservedHandler(atom)),\n onBecomeUnobservedHandler && (() => onBecomeUnobservedHandler(atom)),\n ) as IEnhancedAtom<TMeta>;\n atom.meta = meta ?? ({} as TMeta);\n atom.reportChanged = atom.reportChanged.bind(atom);\n atom.reportObserved = atom.reportObserved.bind(atom);\n return atom;\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Observable ref** pattern for MobX: boxed mutable references with change listeners, metadata,\n * and optional custom equality. Bridges React-style ref holders and MobX reactivity when a single\n * mutable cell must notify dependents without replacing the whole parent object graph.\n *\n * ## Usage\n *\n * ```ts\n * import { createRef } from \"yummies/mobx\";\n * ```\n */\n\nimport {\n type IEqualsComparer,\n makeObservable,\n comparer as mobxComparer,\n observable,\n runInAction,\n} from 'mobx';\nimport type { AnyObject, Maybe } from 'yummies/types';\n\n/**\n * You can return `false` if you don't want to change the value in this ref\n */\nexport type RefChangeListener<T> = (\n value: T | null,\n prevValue: T | undefined,\n) => void | false;\n\n/**\n * Alternative to React.createRef but works in MobX world.\n * Typically it the should be the same React.LegacyRef (fn style)\n */\nexport interface Ref<T = any, TMeta = AnyObject> {\n /**\n * Setter function\n */\n (value: Maybe<T>): void;\n\n set(value: Maybe<T>): void;\n listeners: Set<RefChangeListener<NoInfer<T>>>;\n current: NoInfer<T> | null;\n meta: TMeta;\n}\n\nexport interface CreateRefConfig<T = any, TMeta = AnyObject> {\n onSet?: (node: T, prevValue: T | undefined) => void;\n onUnset?: (lastValue: T | undefined) => void;\n onChange?: RefChangeListener<T>;\n meta?: TMeta;\n initial?: Maybe<T>;\n comparer?: IEqualsComparer<T | null>;\n}\n\n/**\n * Creates a MobX-aware ref that behaves like a callback ref and exposes\n * observable `current` and `meta` fields.\n *\n * @template T Referenced value type.\n * @template TMeta Additional observable metadata stored on the ref.\n * @param cfg Optional callbacks, initial value and comparer configuration.\n * @returns Observable ref function object.\n *\n * @example\n * ```ts\n * const inputRef = createRef<HTMLInputElement>();\n * inputRef.set(document.createElement('input'));\n * ```\n *\n * @example\n * ```ts\n * const ref = createRef<number>();\n * ref(3);\n * ref.current; // 3\n * ```\n *\n * @example\n * ```ts\n * const nodeRef = createRef({\n * onUnset: () => console.log('detached'),\n * meta: { mounted: false },\n * });\n * ```\n */\nexport const createRef = <T = any, TMeta = AnyObject>(\n cfg?: CreateRefConfig<T, TMeta>,\n): Ref<T, TMeta> => {\n let lastValue: T | undefined;\n const comparer = cfg?.comparer ?? mobxComparer.default;\n\n const setValue: Ref<T, TMeta>['set'] = (value) => {\n const nextValue = value ?? null;\n\n if (comparer(ref.current, nextValue)) {\n return;\n }\n\n runInAction(() => {\n const prevLastValue = lastValue;\n lastValue = ref.current ?? undefined;\n ref.current = nextValue;\n\n let isNextValueIgnored = false;\n\n ref.listeners.forEach((listener) => {\n const listenerResult = listener(ref.current, lastValue);\n\n if (listenerResult === false) {\n isNextValueIgnored = true;\n }\n });\n\n if (isNextValueIgnored) {\n lastValue = prevLastValue;\n ref.current = lastValue ?? null;\n } else if (ref.current === null && lastValue !== undefined) {\n lastValue = undefined;\n }\n });\n };\n\n const ref = setValue as Ref<T, TMeta>;\n\n ref.set = setValue;\n\n ref.listeners = new Set(cfg?.onChange ? [cfg.onChange] : []);\n\n if (cfg?.onSet || cfg?.onUnset) {\n ref.listeners.add((value, prevValue) => {\n if (value) {\n cfg.onSet?.(value, prevValue);\n } else {\n cfg.onUnset?.(prevValue);\n }\n });\n }\n\n ref.current = cfg?.initial ?? null;\n ref.meta = cfg?.meta ?? ({} as TMeta);\n\n makeObservable(ref, {\n current: observable.ref,\n meta: observable,\n });\n\n return ref;\n};\n\n/**\n * Checks whether the provided value is a ref created by `createRef`.\n *\n * @template T Referenced value type.\n * @template TMeta Ref metadata type.\n * @param value Value to inspect.\n * @returns `true` when the value is a ref-like function with `current`.\n *\n * @example\n * ```ts\n * const ref = createRef<number>();\n * isRef(ref); // true\n * ```\n *\n * @example\n * ```ts\n * isRef({ current: 1 }); // false\n * ```\n */\nexport const isRef = <T, TMeta = any>(\n value: T | Ref<T, TMeta>,\n): value is Ref<T, TMeta> => {\n return typeof value === 'function' && 'current' in value;\n};\n\n/**\n * Normalizes a plain value or an existing ref into a `Ref` instance.\n *\n * @template T Referenced value type.\n * @template TMeta Ref metadata type.\n * @param value Existing ref or initial plain value.\n * @param cfg Optional ref configuration applied when a new ref is created.\n * @returns Existing ref or a newly created ref initialized with `value`.\n *\n * @example\n * ```ts\n * const ref = toRef(document.body);\n * ref.current === document.body;\n * ```\n *\n * @example\n * ```ts\n * const existingRef = createRef<number>();\n * const sameRef = toRef(existingRef);\n * ```\n */\nexport const toRef = <T, TMeta = any>(\n value: T | Ref<T, TMeta>,\n cfg?: Omit<CreateRefConfig<T, TMeta>, 'initial'>,\n): Ref<T, TMeta> => {\n return isRef(value) ? value : createRef({ initial: value, ...cfg });\n};\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Deep observable object** with structural `set` patches that reuse nested observables when keys\n * overlap. Helps store trees (forms, filters, entities) under MobX without wholesale replacement\n * and without manual `observable.map` wiring for every level.\n *\n * ## Usage\n *\n * ```ts\n * import { DeepObservableStruct } from \"yummies/mobx\";\n * ```\n */\n\nimport { action, makeObservable, observable } from 'mobx';\nimport { typeGuard } from 'yummies/type-guard';\nimport type { AnyObject } from 'yummies/types';\n\n/**\n * Wraps a plain object into a deeply observable structure and allows\n * patch-like updates while preserving nested observable references where possible.\n *\n * @template TData Observable object shape.\n *\n * @example\n * ```ts\n * const state = new DeepObservableStruct({ user: { name: 'Ann' } });\n * state.set({ user: { name: 'Bob' } });\n * ```\n *\n * @example\n * ```ts\n * const state = new DeepObservableStruct({ filters: { active: true } });\n * state.set({ filters: { active: false, archived: true } });\n * ```\n */\nexport class DeepObservableStruct<TData extends AnyObject> {\n data: TData;\n\n constructor(data: TData) {\n this.data = data;\n\n makeObservable(this, {\n data: observable.deep,\n set: action,\n });\n }\n\n set(newData: Partial<TData>) {\n type StackItem = [key: string, currObservable: AnyObject, new: AnyObject];\n\n const stack: StackItem[] = Object.keys(this.data).map((key) => [\n key,\n this.data,\n newData,\n ]);\n\n let currentIndex = 0;\n let stackLength = stack.length;\n\n while (currentIndex < stackLength) {\n const [key, currObservableData, newData] = stack[currentIndex];\n const newValue = newData[key];\n const currValue = currObservableData[key];\n\n currentIndex++;\n\n if (key in newData) {\n if (typeGuard.isObject(newValue) && typeGuard.isObject(currValue)) {\n const newValueKeys = Object.keys(newValue);\n\n Object.keys(currValue).forEach((childKey) => {\n if (!(childKey in newValue)) {\n delete currObservableData[key][childKey];\n }\n });\n\n newValueKeys.forEach((childKey) => {\n const length = stack.push([\n childKey,\n currObservableData[key],\n newValue,\n ]);\n stackLength = length;\n });\n } else if (newValue !== currValue) {\n currObservableData[key] = newValue;\n }\n } else {\n delete currObservableData[key];\n }\n }\n\n Object.keys(newData).forEach((newDataKey) => {\n if (!this.data[newDataKey]) {\n // @ts-expect-error\n this.data[newDataKey] = newData[newDataKey];\n }\n });\n }\n}\n","import { _getGlobalState } from 'mobx';\nimport type { MobXGlobals } from 'mobx/dist/internal.js';\n\n/** Same cap as MobX's internal `MAX_REACTION_ITERATIONS` (not exported from the package). */\nconst DEFAULT_MAX_REACTION_ITERATIONS = 100;\n\n/**\n * Synchronously runs MobX reactions from the internal `pendingReactions` queue when they piled up\n * during a batch (`inBatch > 0`, e.g. inside `runInAction`).\n *\n * While a batch is open, MobX only enqueues reactions; this call temporarily resets the batch\n * counter, drains the queue, and restores state—useful in tests and when you need side effects\n * before leaving the action.\n *\n * If there are no pending reactions, a reaction run is already in progress (`isRunningReactions`),\n * or the iteration cap is hit (cycle guard), there is no extra work; when the cap is exceeded the\n * queue is cleared, matching MobX's internal safety behavior.\n *\n * @param maxCount - Maximum iterations of the outer drain loop (default 100, same idea as MobX's internal limit).\n * Pass `Number.POSITIVE_INFINITY` to disable this cap only when you trust the reaction graph to settle;\n * a non-converging cycle will then keep looping until the queue empties (or effectively hang).\n *\n * @example\n * ```ts\n * import { observable, reaction, runInAction } from \"mobx\";\n * import { flushPendingReactions } from \"yummies/mobx\";\n *\n * const state = observable({ count: 0 });\n * const log: number[] = [];\n * reaction(() => state.count, (n) => log.push(n));\n *\n * runInAction(() => {\n * state.count = 1;\n * flushPendingReactions();\n * });\n *\n * // log === [1] — the reaction ran before the action finished\n * ```\n */\nexport function flushPendingReactions(\n maxCount = DEFAULT_MAX_REACTION_ITERATIONS,\n): void {\n const gs: MobXGlobals = _getGlobalState();\n\n if (!maxCount || gs.isRunningReactions || gs.pendingReactions.length === 0) {\n return;\n }\n\n const savedInBatch = gs.inBatch;\n gs.inBatch = 0;\n\n try {\n gs.isRunningReactions = true;\n const queue = gs.pendingReactions;\n let iterations = 0;\n\n while (queue.length > 0) {\n if (++iterations === maxCount) {\n queue.splice(0);\n break;\n }\n\n const batch = queue.splice(0);\n for (let i = 0; i < batch.length; i++) {\n batch[i].runReaction_();\n }\n }\n } finally {\n gs.isRunningReactions = false;\n gs.inBatch = savedInBatch;\n }\n}\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * Typed access to MobX **internal administration** (`$mobx`) for advanced tooling, migration scripts,\n * or introspection. Prefer public MobX APIs in application code; reach for this when you must align\n * with library internals or patch behavior at the administration layer.\n *\n * ## Usage\n *\n * ```ts\n * import { getMobxAdministration } from \"yummies/mobx\";\n * ```\n */\n\nimport { $mobx, type AnnotationMapEntry } from 'mobx';\nimport type { AnyObject } from 'yummies/types';\n\ntype ObservableObjectAdministration = Parameters<\n Exclude<AnnotationMapEntry, boolean>['make_']\n>[0];\n\n/**\n * Returns the internal MobX administration object associated with an observable target.\n *\n * @param context Observable object instance.\n * @returns MobX administration internals stored under `$mobx`.\n *\n * @example\n * ```ts\n * const admin = getMobxAdministration(store);\n * admin.name_;\n * ```\n *\n * @example\n * ```ts\n * const values = getMobxAdministration(formState).values_;\n * ```\n */\nexport const getMobxAdministration = (\n context: AnyObject,\n): ObservableObjectAdministration => context[$mobx];\n","/**\n * ---header-docs-section---\n * # yummies/mobx\n *\n * ## Description\n *\n * **Lazy subscriptions** tied to MobX observation: start work when the first reaction observes\n * tracked keys, stop when nothing listens anymore (optionally after a delay). Ideal for polling,\n * WebSocket feeds, or expensive caches that should idle when the UI is not mounted.\n *\n * ## Usage\n *\n * ```ts\n * import { lazyObserve } from \"yummies/mobx\";\n * ```\n */\n\nimport { onBecomeObserved, onBecomeUnobserved } from 'mobx';\n\n/**\n * Starts side effects only while one or more MobX observables are being observed.\n *\n * When the first property becomes observed, `onStart` is called. When all tracked\n * properties become unobserved, `onEnd` is called with the value returned by\n * `onStart`. Cleanup can be delayed via `endDelay`.\n *\n * It uses MobX `onBecomeObserved` and `onBecomeUnobserved` hooks to perform\n * lazy subscription management.\n *\n * @template TMetaData Data returned from `onStart` and forwarded to `onEnd`.\n * @param config Configuration for tracked properties and lifecycle callbacks.\n * @returns Cleanup function that clears the tracked state and runs `onEnd`.\n *\n * @example\n * ```ts\n * const stop = lazyObserve({\n * context: store,\n * property: 'items',\n * onStart: () => api.subscribe(),\n * onEnd: (subscription) => subscription.unsubscribe(),\n * });\n * ```\n *\n * @example\n * ```ts\n * lazyObserve({\n * property: [boxA, boxB],\n * onStart: () => console.log('observed'),\n * endDelay: 300,\n * });\n * ```\n */\nexport const lazyObserve = <TMetaData = void>({\n context,\n property,\n onStart,\n onEnd,\n endDelay = false,\n}: {\n context?: any;\n property: any | any[];\n onStart?: () => TMetaData;\n onEnd?: (metaData: TMetaData, cleanupFn: VoidFunction) => void;\n endDelay?: number | false;\n}) => {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let metaData: TMetaData | undefined;\n const observingProps = new Set<string>();\n const properties = Array.isArray(property) ? property : [property];\n\n const cleanup = () => {\n observingProps.clear();\n\n if (endDelay === false) {\n onEnd?.(metaData!, cleanup);\n metaData = undefined;\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n\n timeoutId = setTimeout(() => {\n onEnd?.(metaData!, cleanup);\n timeoutId = undefined;\n metaData = undefined;\n }, endDelay);\n };\n\n const start = (property: string) => {\n const isAlreadyObserving = observingProps.size > 0;\n observingProps.add(property);\n\n if (isAlreadyObserving) {\n return;\n }\n\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = undefined;\n }\n\n metaData = onStart?.();\n };\n\n const stop = (property: string) => {\n const isAlreadyNotObserving = !observingProps.size;\n\n observingProps.delete(property);\n\n const isObserving = observingProps.size > 0;\n\n if (isAlreadyNotObserving || isObserving) {\n return;\n }\n\n cleanup();\n };\n\n properties.forEach((property) => {\n if (context) {\n onBecomeObserved(context, property, () => start(property));\n onBecomeUnobserved(context, property, () => stop(property));\n } else {\n onBecomeObserved(property, () => start(property));\n onBecomeUnobserved(property, () => stop(property));\n }\n });\n\n return cleanup;\n};\n"],"mappings":"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"}
package/ms.cjs CHANGED
@@ -1,21 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region src/ms.ts
3
- /**
4
- * ---header-docs-section---
5
- * # yummies/ms
6
- *
7
- * ## Description
8
- *
9
- * Converts human-friendly time units (seconds, minutes, hours, days, weeks) to **milliseconds** for
10
- * timers, animations, and `dayjs`-style math. The `unitsToMs` map is shared across the library so
11
- * delays and durations stay consistent instead of scattering `* 1000` literals through the code.
12
- *
13
- * ## Usage
14
- *
15
- * ```ts
16
- * import { ms } from "yummies/ms";
17
- * ```
18
- */
2
+ //#region src/ms/units-to-ms.ts
19
3
  var unitsToMs = {
20
4
  ms: 1,
21
5
  sec: 1e3,
@@ -24,6 +8,8 @@ var unitsToMs = {
24
8
  day: 1e3 * 60 * 60 * 24,
25
9
  week: 1e3 * 60 * 60 * 24 * 7
26
10
  };
11
+ //#endregion
12
+ //#region src/ms/ms.ts
27
13
  /**
28
14
  * Converts a value in the specified unit to milliseconds.
29
15
  *