yummies 7.16.1 → 7.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/assert.cjs CHANGED
@@ -26,6 +26,7 @@ let yummies_common = require("yummies/common");
26
26
  */
27
27
  var _exports_exports = /* @__PURE__ */ require_chunk.__exportAll({
28
28
  AssertionError: () => AssertionError,
29
+ array: () => array,
29
30
  boolean: () => boolean,
30
31
  defined: () => defined,
31
32
  fail: () => fail,
@@ -35,6 +36,7 @@ var _exports_exports = /* @__PURE__ */ require_chunk.__exportAll({
35
36
  notNull: () => notNull,
36
37
  notUndefined: () => notUndefined,
37
38
  number: () => number,
39
+ object: () => object,
38
40
  ok: () => ok,
39
41
  string: () => string,
40
42
  symbol: () => symbol,
@@ -110,6 +112,24 @@ function string(value, message) {
110
112
  if (!yummies_type_guard.typeGuard.isString(value)) throw new AssertionError(resolveAssertMessage(message, "Expected a string"));
111
113
  }
112
114
  /**
115
+ * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isObject`.
116
+ *
117
+ * @param value Value to narrow.
118
+ * @param message Optional message or lazy message factory.
119
+ */
120
+ function object(value, message) {
121
+ if (!yummies_type_guard.typeGuard.isObject(value)) throw new AssertionError(resolveAssertMessage(message, "Expected an object"));
122
+ }
123
+ /**
124
+ * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isArray`.
125
+ *
126
+ * @param value Value to narrow.
127
+ * @param message Optional message or lazy message factory.
128
+ */
129
+ function array(value, message) {
130
+ if (!yummies_type_guard.typeGuard.isArray(value)) throw new AssertionError(resolveAssertMessage(message, "Expected an array"));
131
+ }
132
+ /**
113
133
  * Throws when `value` is not a finite non-NaN number; uses `typeGuard.isNumber`.
114
134
  *
115
135
  * @param value Value to narrow.
package/assert.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"assert.cjs","names":[],"sources":["../src/assert/_exports.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/assert\n *\n * ## Description\n *\n * Runtime **assertions** for invariants, narrowing, and exhaustiveness. Helpers throw\n * {@link AssertionError} with an optional message (`string` or {@link MaybeFn}) so failing fast stays\n * explicit without a heavy assertion library. Use `assert.ok`, `assert.string`, `assert.defined`,\n * `assert.fail`, and the rest on the namespace export from the package entry.\n *\n * ## Usage\n *\n * ```ts\n * import { assert } from \"yummies/assert\";\n *\n * assert.ok(user.id, \"user id is required\");\n * assert.defined(maybeName);\n * assert.string(raw);\n * ```\n */\n\nimport { callFunction } from 'yummies/common';\nimport { typeGuard } from 'yummies/type-guard';\nimport type { Maybe, MaybeFn } from 'yummies/types';\n\n/**\n * Error thrown by assertion helpers in this module.\n */\nexport class AssertionError extends Error {\n override name = 'AssertionError';\n}\n\nfunction resolveAssertMessage(\n message: MaybeFn<string> | undefined,\n fallback: string,\n): string {\n if (message === undefined) {\n return fallback;\n }\n return callFunction(message);\n}\n\n/**\n * Throws when `condition` is falsy; narrows the type when it is truthy.\n *\n * @param condition Value treated as a boolean guard.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.ok(user.id, \"user id is required\");\n * ```\n */\nexport function ok(\n condition: unknown,\n message?: MaybeFn<string>,\n): asserts condition {\n if (typeGuard.isFalsy(condition)) {\n throw new AssertionError(resolveAssertMessage(message, 'Assertion failed'));\n }\n}\n\n/**\n * Same as {@link ok}; common alias for internal invariants (e.g. after optional checks).\n */\nexport const invariant: typeof ok = ok;\n\n/**\n * Throws when the value is `null` or `undefined`; otherwise narrows away nullish.\n *\n * @param value Possibly nullish value.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.defined(maybeName, \"name must be present\");\n * ```\n */\nexport function defined<T>(\n value: Maybe<T>,\n message?: MaybeFn<string>,\n): asserts value is NonNullable<T> {\n if (!typeGuard.isDefined(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a defined value'),\n );\n }\n}\n\n/**\n * Throws when the value is `null`; allows `undefined` unless combined with other checks.\n *\n * @param value Value that may be `null`.\n * @param message Optional message or lazy message factory.\n */\nexport function notNull<T>(\n value: T | null,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (typeGuard.isNull(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a non-null value'),\n );\n }\n}\n\n/**\n * Throws when the value is `undefined`.\n *\n * @param value Value that may be `undefined`.\n * @param message Optional message or lazy message factory.\n */\nexport function notUndefined<T>(\n value: T | undefined,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (typeGuard.isUndefined(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a defined value'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a string (primitive or `String` object); uses `typeGuard.isString`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function string(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is string {\n if (!typeGuard.isString(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a string'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a finite non-NaN number; uses `typeGuard.isNumber`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function number(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is number {\n if (!typeGuard.isNumber(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a finite number'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a boolean; uses `typeGuard.isBoolean`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function boolean(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is boolean {\n if (!typeGuard.isBoolean(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a boolean'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a symbol; uses `typeGuard.isSymbol`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function symbol(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is symbol {\n if (!typeGuard.isSymbol(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a symbol'),\n );\n }\n}\n\n/**\n * Always throws; use for branches that must be impossible or as a typed “abort”.\n *\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * function parse(kind: \"a\" | \"b\") {\n * if (kind === \"a\") return 1;\n * if (kind === \"b\") return 2;\n * assert.fail(\"unreachable\");\n * }\n * ```\n */\nexport function fail(message?: MaybeFn<string>): never {\n throw new AssertionError(resolveAssertMessage(message, 'Unreachable'));\n}\n\n/**\n * Exhaustiveness helper: call with a `never` value when all union cases are handled.\n *\n * @param value Should be `never` when the type checker is satisfied.\n * @param message Optional message (this path always throws, so a lazy factory is unnecessary).\n */\nexport function unreachable(value: never, message?: string): never {\n throw new AssertionError(\n resolveAssertMessage(message, `Unexpected value: ${String(value)}`),\n );\n}\n\n/**\n * Alias for {@link unreachable} (common name in switch exhaustiveness snippets).\n */\nexport { unreachable as never };\n\n/**\n * Throws when `value` is not an instance of `ctor`.\n *\n * @param value Value to check.\n * @param ctor Constructor used with `instanceof`.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.instanceOf(el, HTMLElement, \"expected a DOM element\");\n * ```\n */\nexport function instanceOf<T>(\n value: unknown,\n ctor: abstract new (...args: never[]) => T,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (!(value instanceof ctor)) {\n throw new AssertionError(\n resolveAssertMessage(message, `Expected instance of ${ctor.name}`),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,iBAAb,cAAoC,MAAM;CACxC,OAAgB;;AAGlB,SAAS,qBACP,SACA,UACQ;AACR,KAAI,YAAY,KAAA,EACd,QAAO;AAET,SAAA,GAAA,eAAA,cAAoB,QAAQ;;;;;;;;;;;;;AAc9B,SAAgB,GACd,WACA,SACmB;AACnB,KAAI,mBAAA,UAAU,QAAQ,UAAU,CAC9B,OAAM,IAAI,eAAe,qBAAqB,SAAS,mBAAmB,CAAC;;;;;AAO/E,IAAa,YAAuB;;;;;;;;;;;;AAapC,SAAgB,QACd,OACA,SACiC;AACjC,KAAI,CAAC,mBAAA,UAAU,UAAU,MAAM,CAC7B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,QACd,OACA,SACoB;AACpB,KAAI,mBAAA,UAAU,OAAO,MAAM,CACzB,OAAM,IAAI,eACR,qBAAqB,SAAS,4BAA4B,CAC3D;;;;;;;;AAUL,SAAgB,aACd,OACA,SACoB;AACpB,KAAI,mBAAA,UAAU,YAAY,MAAM,CAC9B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,mBAAA,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,mBAAA,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,QACd,OACA,SAC0B;AAC1B,KAAI,CAAC,mBAAA,UAAU,UAAU,MAAM,CAC7B,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,CACpD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,mBAAA,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;;;;;;;;;AAkBL,SAAgB,KAAK,SAAkC;AACrD,OAAM,IAAI,eAAe,qBAAqB,SAAS,cAAc,CAAC;;;;;;;;AASxE,SAAgB,YAAY,OAAc,SAAyB;AACjE,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,OAAO,MAAM,GAAG,CACpE;;;;;;;;;;;;;;AAoBH,SAAgB,WACd,OACA,MACA,SACoB;AACpB,KAAI,EAAE,iBAAiB,MACrB,OAAM,IAAI,eACR,qBAAqB,SAAS,wBAAwB,KAAK,OAAO,CACnE"}
1
+ {"version":3,"file":"assert.cjs","names":[],"sources":["../src/assert/_exports.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/assert\n *\n * ## Description\n *\n * Runtime **assertions** for invariants, narrowing, and exhaustiveness. Helpers throw\n * {@link AssertionError} with an optional message (`string` or {@link MaybeFn}) so failing fast stays\n * explicit without a heavy assertion library. Use `assert.ok`, `assert.string`, `assert.defined`,\n * `assert.fail`, and the rest on the namespace export from the package entry.\n *\n * ## Usage\n *\n * ```ts\n * import { assert } from \"yummies/assert\";\n *\n * assert.ok(user.id, \"user id is required\");\n * assert.defined(maybeName);\n * assert.string(raw);\n * ```\n */\n\nimport { callFunction } from 'yummies/common';\nimport { typeGuard } from 'yummies/type-guard';\nimport type { AnyObject, Maybe, MaybeFn } from 'yummies/types';\n\n/**\n * Error thrown by assertion helpers in this module.\n */\nexport class AssertionError extends Error {\n override name = 'AssertionError';\n}\n\nfunction resolveAssertMessage(\n message: MaybeFn<string> | undefined,\n fallback: string,\n): string {\n if (message === undefined) {\n return fallback;\n }\n return callFunction(message);\n}\n\n/**\n * Throws when `condition` is falsy; narrows the type when it is truthy.\n *\n * @param condition Value treated as a boolean guard.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.ok(user.id, \"user id is required\");\n * ```\n */\nexport function ok(\n condition: unknown,\n message?: MaybeFn<string>,\n): asserts condition {\n if (typeGuard.isFalsy(condition)) {\n throw new AssertionError(resolveAssertMessage(message, 'Assertion failed'));\n }\n}\n\n/**\n * Same as {@link ok}; common alias for internal invariants (e.g. after optional checks).\n */\nexport const invariant: typeof ok = ok;\n\n/**\n * Throws when the value is `null` or `undefined`; otherwise narrows away nullish.\n *\n * @param value Possibly nullish value.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.defined(maybeName, \"name must be present\");\n * ```\n */\nexport function defined<T>(\n value: Maybe<T>,\n message?: MaybeFn<string>,\n): asserts value is NonNullable<T> {\n if (!typeGuard.isDefined(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a defined value'),\n );\n }\n}\n\n/**\n * Throws when the value is `null`; allows `undefined` unless combined with other checks.\n *\n * @param value Value that may be `null`.\n * @param message Optional message or lazy message factory.\n */\nexport function notNull<T>(\n value: T | null,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (typeGuard.isNull(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a non-null value'),\n );\n }\n}\n\n/**\n * Throws when the value is `undefined`.\n *\n * @param value Value that may be `undefined`.\n * @param message Optional message or lazy message factory.\n */\nexport function notUndefined<T>(\n value: T | undefined,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (typeGuard.isUndefined(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a defined value'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a string (primitive or `String` object); uses `typeGuard.isString`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function string(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is string {\n if (!typeGuard.isString(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a string'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isObject`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function object<T extends AnyObject = AnyObject>(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (!typeGuard.isObject(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected an object'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isArray`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function array<T extends any[] = any[]>(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (!typeGuard.isArray(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected an array'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a finite non-NaN number; uses `typeGuard.isNumber`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function number(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is number {\n if (!typeGuard.isNumber(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a finite number'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a boolean; uses `typeGuard.isBoolean`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function boolean(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is boolean {\n if (!typeGuard.isBoolean(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a boolean'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a symbol; uses `typeGuard.isSymbol`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function symbol(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is symbol {\n if (!typeGuard.isSymbol(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a symbol'),\n );\n }\n}\n\n/**\n * Always throws; use for branches that must be impossible or as a typed “abort”.\n *\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * function parse(kind: \"a\" | \"b\") {\n * if (kind === \"a\") return 1;\n * if (kind === \"b\") return 2;\n * assert.fail(\"unreachable\");\n * }\n * ```\n */\nexport function fail(message?: MaybeFn<string>): never {\n throw new AssertionError(resolveAssertMessage(message, 'Unreachable'));\n}\n\n/**\n * Exhaustiveness helper: call with a `never` value when all union cases are handled.\n *\n * @param value Should be `never` when the type checker is satisfied.\n * @param message Optional message (this path always throws, so a lazy factory is unnecessary).\n */\nexport function unreachable(value: never, message?: string): never {\n throw new AssertionError(\n resolveAssertMessage(message, `Unexpected value: ${String(value)}`),\n );\n}\n\n/**\n * Alias for {@link unreachable} (common name in switch exhaustiveness snippets).\n */\nexport { unreachable as never };\n\n/**\n * Throws when `value` is not an instance of `ctor`.\n *\n * @param value Value to check.\n * @param ctor Constructor used with `instanceof`.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.instanceOf(el, HTMLElement, \"expected a DOM element\");\n * ```\n */\nexport function instanceOf<T>(\n value: unknown,\n ctor: abstract new (...args: never[]) => T,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (!(value instanceof ctor)) {\n throw new AssertionError(\n resolveAssertMessage(message, `Expected instance of ${ctor.name}`),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,iBAAb,cAAoC,MAAM;CACxC,OAAgB;;AAGlB,SAAS,qBACP,SACA,UACQ;AACR,KAAI,YAAY,KAAA,EACd,QAAO;AAET,SAAA,GAAA,eAAA,cAAoB,QAAQ;;;;;;;;;;;;;AAc9B,SAAgB,GACd,WACA,SACmB;AACnB,KAAI,mBAAA,UAAU,QAAQ,UAAU,CAC9B,OAAM,IAAI,eAAe,qBAAqB,SAAS,mBAAmB,CAAC;;;;;AAO/E,IAAa,YAAuB;;;;;;;;;;;;AAapC,SAAgB,QACd,OACA,SACiC;AACjC,KAAI,CAAC,mBAAA,UAAU,UAAU,MAAM,CAC7B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,QACd,OACA,SACoB;AACpB,KAAI,mBAAA,UAAU,OAAO,MAAM,CACzB,OAAM,IAAI,eACR,qBAAqB,SAAS,4BAA4B,CAC3D;;;;;;;;AAUL,SAAgB,aACd,OACA,SACoB;AACpB,KAAI,mBAAA,UAAU,YAAY,MAAM,CAC9B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,mBAAA,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACoB;AACpB,KAAI,CAAC,mBAAA,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,CACpD;;;;;;;;AAUL,SAAgB,MACd,OACA,SACoB;AACpB,KAAI,CAAC,mBAAA,UAAU,QAAQ,MAAM,CAC3B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,mBAAA,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,QACd,OACA,SAC0B;AAC1B,KAAI,CAAC,mBAAA,UAAU,UAAU,MAAM,CAC7B,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,CACpD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,mBAAA,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;;;;;;;;;AAkBL,SAAgB,KAAK,SAAkC;AACrD,OAAM,IAAI,eAAe,qBAAqB,SAAS,cAAc,CAAC;;;;;;;;AASxE,SAAgB,YAAY,OAAc,SAAyB;AACjE,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,OAAO,MAAM,GAAG,CACpE;;;;;;;;;;;;;;AAoBH,SAAgB,WACd,OACA,MACA,SACoB;AACpB,KAAI,EAAE,iBAAiB,MACrB,OAAM,IAAI,eACR,qBAAqB,SAAS,wBAAwB,KAAK,OAAO,CACnE"}
package/assert.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MaybeFn, Maybe } from 'yummies/types';
1
+ import { MaybeFn, Maybe, AnyObject } from 'yummies/types';
2
2
 
3
3
  /**
4
4
  * ---header-docs-section---
@@ -77,6 +77,20 @@ declare function notUndefined<T>(value: T | undefined, message?: MaybeFn<string>
77
77
  * @param message Optional message or lazy message factory.
78
78
  */
79
79
  declare function string(value: unknown, message?: MaybeFn<string>): asserts value is string;
80
+ /**
81
+ * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isObject`.
82
+ *
83
+ * @param value Value to narrow.
84
+ * @param message Optional message or lazy message factory.
85
+ */
86
+ declare function object<T extends AnyObject = AnyObject>(value: unknown, message?: MaybeFn<string>): asserts value is T;
87
+ /**
88
+ * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isArray`.
89
+ *
90
+ * @param value Value to narrow.
91
+ * @param message Optional message or lazy message factory.
92
+ */
93
+ declare function array<T extends any[] = any[]>(value: unknown, message?: MaybeFn<string>): asserts value is T;
80
94
  /**
81
95
  * Throws when `value` is not a finite non-NaN number; uses `typeGuard.isNumber`.
82
96
  *
@@ -137,6 +151,7 @@ declare function instanceOf<T>(value: unknown, ctor: abstract new (...args: neve
137
151
 
138
152
  type _exports_AssertionError = AssertionError;
139
153
  declare const _exports_AssertionError: typeof AssertionError;
154
+ declare const _exports_array: typeof array;
140
155
  declare const _exports_boolean: typeof boolean;
141
156
  declare const _exports_defined: typeof defined;
142
157
  declare const _exports_fail: typeof fail;
@@ -145,6 +160,7 @@ declare const _exports_invariant: typeof invariant;
145
160
  declare const _exports_notNull: typeof notNull;
146
161
  declare const _exports_notUndefined: typeof notUndefined;
147
162
  declare const _exports_number: typeof number;
163
+ declare const _exports_object: typeof object;
148
164
  declare const _exports_ok: typeof ok;
149
165
  declare const _exports_string: typeof string;
150
166
  declare const _exports_symbol: typeof symbol;
@@ -152,6 +168,7 @@ declare const _exports_unreachable: typeof unreachable;
152
168
  declare namespace _exports {
153
169
  export {
154
170
  _exports_AssertionError as AssertionError,
171
+ _exports_array as array,
155
172
  _exports_boolean as boolean,
156
173
  _exports_defined as defined,
157
174
  _exports_fail as fail,
@@ -161,6 +178,7 @@ declare namespace _exports {
161
178
  _exports_notNull as notNull,
162
179
  _exports_notUndefined as notUndefined,
163
180
  _exports_number as number,
181
+ _exports_object as object,
164
182
  _exports_ok as ok,
165
183
  _exports_string as string,
166
184
  _exports_symbol as symbol,
package/assert.js CHANGED
@@ -25,6 +25,7 @@ import { callFunction } from "yummies/common";
25
25
  */
26
26
  var _exports_exports = /* @__PURE__ */ __exportAll({
27
27
  AssertionError: () => AssertionError,
28
+ array: () => array,
28
29
  boolean: () => boolean,
29
30
  defined: () => defined,
30
31
  fail: () => fail,
@@ -34,6 +35,7 @@ var _exports_exports = /* @__PURE__ */ __exportAll({
34
35
  notNull: () => notNull,
35
36
  notUndefined: () => notUndefined,
36
37
  number: () => number,
38
+ object: () => object,
37
39
  ok: () => ok,
38
40
  string: () => string,
39
41
  symbol: () => symbol,
@@ -109,6 +111,24 @@ function string(value, message) {
109
111
  if (!typeGuard.isString(value)) throw new AssertionError(resolveAssertMessage(message, "Expected a string"));
110
112
  }
111
113
  /**
114
+ * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isObject`.
115
+ *
116
+ * @param value Value to narrow.
117
+ * @param message Optional message or lazy message factory.
118
+ */
119
+ function object(value, message) {
120
+ if (!typeGuard.isObject(value)) throw new AssertionError(resolveAssertMessage(message, "Expected an object"));
121
+ }
122
+ /**
123
+ * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isArray`.
124
+ *
125
+ * @param value Value to narrow.
126
+ * @param message Optional message or lazy message factory.
127
+ */
128
+ function array(value, message) {
129
+ if (!typeGuard.isArray(value)) throw new AssertionError(resolveAssertMessage(message, "Expected an array"));
130
+ }
131
+ /**
112
132
  * Throws when `value` is not a finite non-NaN number; uses `typeGuard.isNumber`.
113
133
  *
114
134
  * @param value Value to narrow.
package/assert.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"assert.js","names":[],"sources":["../src/assert/_exports.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/assert\n *\n * ## Description\n *\n * Runtime **assertions** for invariants, narrowing, and exhaustiveness. Helpers throw\n * {@link AssertionError} with an optional message (`string` or {@link MaybeFn}) so failing fast stays\n * explicit without a heavy assertion library. Use `assert.ok`, `assert.string`, `assert.defined`,\n * `assert.fail`, and the rest on the namespace export from the package entry.\n *\n * ## Usage\n *\n * ```ts\n * import { assert } from \"yummies/assert\";\n *\n * assert.ok(user.id, \"user id is required\");\n * assert.defined(maybeName);\n * assert.string(raw);\n * ```\n */\n\nimport { callFunction } from 'yummies/common';\nimport { typeGuard } from 'yummies/type-guard';\nimport type { Maybe, MaybeFn } from 'yummies/types';\n\n/**\n * Error thrown by assertion helpers in this module.\n */\nexport class AssertionError extends Error {\n override name = 'AssertionError';\n}\n\nfunction resolveAssertMessage(\n message: MaybeFn<string> | undefined,\n fallback: string,\n): string {\n if (message === undefined) {\n return fallback;\n }\n return callFunction(message);\n}\n\n/**\n * Throws when `condition` is falsy; narrows the type when it is truthy.\n *\n * @param condition Value treated as a boolean guard.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.ok(user.id, \"user id is required\");\n * ```\n */\nexport function ok(\n condition: unknown,\n message?: MaybeFn<string>,\n): asserts condition {\n if (typeGuard.isFalsy(condition)) {\n throw new AssertionError(resolveAssertMessage(message, 'Assertion failed'));\n }\n}\n\n/**\n * Same as {@link ok}; common alias for internal invariants (e.g. after optional checks).\n */\nexport const invariant: typeof ok = ok;\n\n/**\n * Throws when the value is `null` or `undefined`; otherwise narrows away nullish.\n *\n * @param value Possibly nullish value.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.defined(maybeName, \"name must be present\");\n * ```\n */\nexport function defined<T>(\n value: Maybe<T>,\n message?: MaybeFn<string>,\n): asserts value is NonNullable<T> {\n if (!typeGuard.isDefined(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a defined value'),\n );\n }\n}\n\n/**\n * Throws when the value is `null`; allows `undefined` unless combined with other checks.\n *\n * @param value Value that may be `null`.\n * @param message Optional message or lazy message factory.\n */\nexport function notNull<T>(\n value: T | null,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (typeGuard.isNull(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a non-null value'),\n );\n }\n}\n\n/**\n * Throws when the value is `undefined`.\n *\n * @param value Value that may be `undefined`.\n * @param message Optional message or lazy message factory.\n */\nexport function notUndefined<T>(\n value: T | undefined,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (typeGuard.isUndefined(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a defined value'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a string (primitive or `String` object); uses `typeGuard.isString`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function string(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is string {\n if (!typeGuard.isString(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a string'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a finite non-NaN number; uses `typeGuard.isNumber`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function number(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is number {\n if (!typeGuard.isNumber(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a finite number'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a boolean; uses `typeGuard.isBoolean`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function boolean(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is boolean {\n if (!typeGuard.isBoolean(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a boolean'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a symbol; uses `typeGuard.isSymbol`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function symbol(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is symbol {\n if (!typeGuard.isSymbol(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a symbol'),\n );\n }\n}\n\n/**\n * Always throws; use for branches that must be impossible or as a typed “abort”.\n *\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * function parse(kind: \"a\" | \"b\") {\n * if (kind === \"a\") return 1;\n * if (kind === \"b\") return 2;\n * assert.fail(\"unreachable\");\n * }\n * ```\n */\nexport function fail(message?: MaybeFn<string>): never {\n throw new AssertionError(resolveAssertMessage(message, 'Unreachable'));\n}\n\n/**\n * Exhaustiveness helper: call with a `never` value when all union cases are handled.\n *\n * @param value Should be `never` when the type checker is satisfied.\n * @param message Optional message (this path always throws, so a lazy factory is unnecessary).\n */\nexport function unreachable(value: never, message?: string): never {\n throw new AssertionError(\n resolveAssertMessage(message, `Unexpected value: ${String(value)}`),\n );\n}\n\n/**\n * Alias for {@link unreachable} (common name in switch exhaustiveness snippets).\n */\nexport { unreachable as never };\n\n/**\n * Throws when `value` is not an instance of `ctor`.\n *\n * @param value Value to check.\n * @param ctor Constructor used with `instanceof`.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.instanceOf(el, HTMLElement, \"expected a DOM element\");\n * ```\n */\nexport function instanceOf<T>(\n value: unknown,\n ctor: abstract new (...args: never[]) => T,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (!(value instanceof ctor)) {\n throw new AssertionError(\n resolveAssertMessage(message, `Expected instance of ${ctor.name}`),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,iBAAb,cAAoC,MAAM;CACxC,OAAgB;;AAGlB,SAAS,qBACP,SACA,UACQ;AACR,KAAI,YAAY,KAAA,EACd,QAAO;AAET,QAAO,aAAa,QAAQ;;;;;;;;;;;;;AAc9B,SAAgB,GACd,WACA,SACmB;AACnB,KAAI,UAAU,QAAQ,UAAU,CAC9B,OAAM,IAAI,eAAe,qBAAqB,SAAS,mBAAmB,CAAC;;;;;AAO/E,IAAa,YAAuB;;;;;;;;;;;;AAapC,SAAgB,QACd,OACA,SACiC;AACjC,KAAI,CAAC,UAAU,UAAU,MAAM,CAC7B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,QACd,OACA,SACoB;AACpB,KAAI,UAAU,OAAO,MAAM,CACzB,OAAM,IAAI,eACR,qBAAqB,SAAS,4BAA4B,CAC3D;;;;;;;;AAUL,SAAgB,aACd,OACA,SACoB;AACpB,KAAI,UAAU,YAAY,MAAM,CAC9B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,QACd,OACA,SAC0B;AAC1B,KAAI,CAAC,UAAU,UAAU,MAAM,CAC7B,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,CACpD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;;;;;;;;;AAkBL,SAAgB,KAAK,SAAkC;AACrD,OAAM,IAAI,eAAe,qBAAqB,SAAS,cAAc,CAAC;;;;;;;;AASxE,SAAgB,YAAY,OAAc,SAAyB;AACjE,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,OAAO,MAAM,GAAG,CACpE;;;;;;;;;;;;;;AAoBH,SAAgB,WACd,OACA,MACA,SACoB;AACpB,KAAI,EAAE,iBAAiB,MACrB,OAAM,IAAI,eACR,qBAAqB,SAAS,wBAAwB,KAAK,OAAO,CACnE"}
1
+ {"version":3,"file":"assert.js","names":[],"sources":["../src/assert/_exports.ts"],"sourcesContent":["/**\n * ---header-docs-section---\n * # yummies/assert\n *\n * ## Description\n *\n * Runtime **assertions** for invariants, narrowing, and exhaustiveness. Helpers throw\n * {@link AssertionError} with an optional message (`string` or {@link MaybeFn}) so failing fast stays\n * explicit without a heavy assertion library. Use `assert.ok`, `assert.string`, `assert.defined`,\n * `assert.fail`, and the rest on the namespace export from the package entry.\n *\n * ## Usage\n *\n * ```ts\n * import { assert } from \"yummies/assert\";\n *\n * assert.ok(user.id, \"user id is required\");\n * assert.defined(maybeName);\n * assert.string(raw);\n * ```\n */\n\nimport { callFunction } from 'yummies/common';\nimport { typeGuard } from 'yummies/type-guard';\nimport type { AnyObject, Maybe, MaybeFn } from 'yummies/types';\n\n/**\n * Error thrown by assertion helpers in this module.\n */\nexport class AssertionError extends Error {\n override name = 'AssertionError';\n}\n\nfunction resolveAssertMessage(\n message: MaybeFn<string> | undefined,\n fallback: string,\n): string {\n if (message === undefined) {\n return fallback;\n }\n return callFunction(message);\n}\n\n/**\n * Throws when `condition` is falsy; narrows the type when it is truthy.\n *\n * @param condition Value treated as a boolean guard.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.ok(user.id, \"user id is required\");\n * ```\n */\nexport function ok(\n condition: unknown,\n message?: MaybeFn<string>,\n): asserts condition {\n if (typeGuard.isFalsy(condition)) {\n throw new AssertionError(resolveAssertMessage(message, 'Assertion failed'));\n }\n}\n\n/**\n * Same as {@link ok}; common alias for internal invariants (e.g. after optional checks).\n */\nexport const invariant: typeof ok = ok;\n\n/**\n * Throws when the value is `null` or `undefined`; otherwise narrows away nullish.\n *\n * @param value Possibly nullish value.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.defined(maybeName, \"name must be present\");\n * ```\n */\nexport function defined<T>(\n value: Maybe<T>,\n message?: MaybeFn<string>,\n): asserts value is NonNullable<T> {\n if (!typeGuard.isDefined(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a defined value'),\n );\n }\n}\n\n/**\n * Throws when the value is `null`; allows `undefined` unless combined with other checks.\n *\n * @param value Value that may be `null`.\n * @param message Optional message or lazy message factory.\n */\nexport function notNull<T>(\n value: T | null,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (typeGuard.isNull(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a non-null value'),\n );\n }\n}\n\n/**\n * Throws when the value is `undefined`.\n *\n * @param value Value that may be `undefined`.\n * @param message Optional message or lazy message factory.\n */\nexport function notUndefined<T>(\n value: T | undefined,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (typeGuard.isUndefined(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a defined value'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a string (primitive or `String` object); uses `typeGuard.isString`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function string(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is string {\n if (!typeGuard.isString(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a string'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isObject`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function object<T extends AnyObject = AnyObject>(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (!typeGuard.isObject(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected an object'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a object (primitive or `AnyObject` object); uses `typeGuard.isArray`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function array<T extends any[] = any[]>(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (!typeGuard.isArray(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected an array'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a finite non-NaN number; uses `typeGuard.isNumber`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function number(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is number {\n if (!typeGuard.isNumber(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a finite number'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a boolean; uses `typeGuard.isBoolean`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function boolean(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is boolean {\n if (!typeGuard.isBoolean(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a boolean'),\n );\n }\n}\n\n/**\n * Throws when `value` is not a symbol; uses `typeGuard.isSymbol`.\n *\n * @param value Value to narrow.\n * @param message Optional message or lazy message factory.\n */\nexport function symbol(\n value: unknown,\n message?: MaybeFn<string>,\n): asserts value is symbol {\n if (!typeGuard.isSymbol(value)) {\n throw new AssertionError(\n resolveAssertMessage(message, 'Expected a symbol'),\n );\n }\n}\n\n/**\n * Always throws; use for branches that must be impossible or as a typed “abort”.\n *\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * function parse(kind: \"a\" | \"b\") {\n * if (kind === \"a\") return 1;\n * if (kind === \"b\") return 2;\n * assert.fail(\"unreachable\");\n * }\n * ```\n */\nexport function fail(message?: MaybeFn<string>): never {\n throw new AssertionError(resolveAssertMessage(message, 'Unreachable'));\n}\n\n/**\n * Exhaustiveness helper: call with a `never` value when all union cases are handled.\n *\n * @param value Should be `never` when the type checker is satisfied.\n * @param message Optional message (this path always throws, so a lazy factory is unnecessary).\n */\nexport function unreachable(value: never, message?: string): never {\n throw new AssertionError(\n resolveAssertMessage(message, `Unexpected value: ${String(value)}`),\n );\n}\n\n/**\n * Alias for {@link unreachable} (common name in switch exhaustiveness snippets).\n */\nexport { unreachable as never };\n\n/**\n * Throws when `value` is not an instance of `ctor`.\n *\n * @param value Value to check.\n * @param ctor Constructor used with `instanceof`.\n * @param message Optional message or lazy message factory.\n *\n * @example\n * ```ts\n * assert.instanceOf(el, HTMLElement, \"expected a DOM element\");\n * ```\n */\nexport function instanceOf<T>(\n value: unknown,\n ctor: abstract new (...args: never[]) => T,\n message?: MaybeFn<string>,\n): asserts value is T {\n if (!(value instanceof ctor)) {\n throw new AssertionError(\n resolveAssertMessage(message, `Expected instance of ${ctor.name}`),\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,iBAAb,cAAoC,MAAM;CACxC,OAAgB;;AAGlB,SAAS,qBACP,SACA,UACQ;AACR,KAAI,YAAY,KAAA,EACd,QAAO;AAET,QAAO,aAAa,QAAQ;;;;;;;;;;;;;AAc9B,SAAgB,GACd,WACA,SACmB;AACnB,KAAI,UAAU,QAAQ,UAAU,CAC9B,OAAM,IAAI,eAAe,qBAAqB,SAAS,mBAAmB,CAAC;;;;;AAO/E,IAAa,YAAuB;;;;;;;;;;;;AAapC,SAAgB,QACd,OACA,SACiC;AACjC,KAAI,CAAC,UAAU,UAAU,MAAM,CAC7B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,QACd,OACA,SACoB;AACpB,KAAI,UAAU,OAAO,MAAM,CACzB,OAAM,IAAI,eACR,qBAAqB,SAAS,4BAA4B,CAC3D;;;;;;;;AAUL,SAAgB,aACd,OACA,SACoB;AACpB,KAAI,UAAU,YAAY,MAAM,CAC9B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACoB;AACpB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,CACpD;;;;;;;;AAUL,SAAgB,MACd,OACA,SACoB;AACpB,KAAI,CAAC,UAAU,QAAQ,MAAM,CAC3B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,2BAA2B,CAC1D;;;;;;;;AAUL,SAAgB,QACd,OACA,SAC0B;AAC1B,KAAI,CAAC,UAAU,UAAU,MAAM,CAC7B,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,CACpD;;;;;;;;AAUL,SAAgB,OACd,OACA,SACyB;AACzB,KAAI,CAAC,UAAU,SAAS,MAAM,CAC5B,OAAM,IAAI,eACR,qBAAqB,SAAS,oBAAoB,CACnD;;;;;;;;;;;;;;;;AAkBL,SAAgB,KAAK,SAAkC;AACrD,OAAM,IAAI,eAAe,qBAAqB,SAAS,cAAc,CAAC;;;;;;;;AASxE,SAAgB,YAAY,OAAc,SAAyB;AACjE,OAAM,IAAI,eACR,qBAAqB,SAAS,qBAAqB,OAAO,MAAM,GAAG,CACpE;;;;;;;;;;;;;;AAoBH,SAAgB,WACd,OACA,MACA,SACoB;AACpB,KAAI,EAAE,iBAAiB,MACrB,OAAM,IAAI,eACR,qBAAqB,SAAS,wBAAwB,KAAK,OAAO,CACnE"}
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 = keyof Pick<\n typeof observable,\n 'ref' | 'deep' | 'shallow' | 'struct'\n>;\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 observable(value: false): false;\n observable(value?: undefined | true): typeof observable;\n observable<TValue extends ObservableTypes>(\n value: TValue,\n ): (typeof observable)[TValue];\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":";;;;;;;;;;;;;;;;;;;;;AAyCA,IAAM,0BAAqC;CACzC,MAAM,KAAA,SAAS;CACf,SAAS,KAAA,SAAS;CAClB,QAAQ,KAAA,SAAS;CAClB;AAqHD,IAAa,aAAa;CACxB,WAAW,OAA6B,YAAmC;AACzE,MAAI,UAAU,MAAO,QAAO;AAE5B,UAAA,GAAA,KAAA,UAAgB;GACd,GAAG;GACH,QACE,OAAO,UAAU,aACb,QACC,wBACC,UACG,KAAA,SAAS;GACrB,CAAC;;CAEJ,aAAa,UAAsC;AACjD,MAAI,UAAU,MAAO,QAAO;AAC5B,MAAI,UAAU,KAAA,KAAa,UAAU,KAAM,QAAO,KAAA;AAClD,SAAO,KAAA,WAAW;;CAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACzID,IAAa,mBACX,SACA,kBACA,kBACG;AACH,KAAI,eAAe;AACjB,mBAAiB,SAAS,CAAC,YAAY,GAAG,YAAY;AACpD,UAAO,SAAS,UAAU;AAExB,eAAW,SAAS,MAAM;KAC1B;IACF;AAEF,GAAA,GAAA,KAAA,gBAAe,QAAQ;QAClB;EACL,MAAM,oBAA+B,EAAE;AAEvC,mBAAiB,SAAS,CAAC,YAAY,GAAG,YAAY;AACpD,UAAO,SAAS,UAAU;AACxB,sBAAkB,SAAS;KAC3B;IACF;AAEF,GAAA,GAAA,KAAA,gBAAe,SAAS,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpB9C,IAAa,sBACX,MACA,yBACA,2BACA,SACyB;CACzB,MAAM,QAAA,GAAA,KAAA,YACJ,MACA,kCAAkC,wBAAwB,KAAK,GAC/D,oCAAoC,0BAA0B,KAAK,EACpE;AACD,MAAK,OAAO,QAAS,EAAE;AACvB,MAAK,gBAAgB,KAAK,cAAc,KAAK,KAAK;AAClD,MAAK,iBAAiB,KAAK,eAAe,KAAK,KAAK;AACpD,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC4BT,IAAa,aACX,QACkB;CAClB,IAAI;CACJ,MAAM,WAAW,KAAK,YAAY,KAAA,SAAa;CAE/C,MAAM,YAAkC,UAAU;EAChD,MAAM,YAAY,SAAS;AAE3B,MAAI,SAAS,IAAI,SAAS,UAAU,CAClC;AAGF,GAAA,GAAA,KAAA,mBAAkB;GAChB,MAAM,gBAAgB;AACtB,eAAY,IAAI,WAAW,KAAA;AAC3B,OAAI,UAAU;GAEd,IAAI,qBAAqB;AAEzB,OAAI,UAAU,SAAS,aAAa;AAGlC,QAFuB,SAAS,IAAI,SAAS,UAAU,KAEhC,MACrB,sBAAqB;KAEvB;AAEF,OAAI,oBAAoB;AACtB,gBAAY;AACZ,QAAI,UAAU,aAAa;cAClB,IAAI,YAAY,QAAQ,cAAc,KAAA,EAC/C,aAAY,KAAA;IAEd;;CAGJ,MAAM,MAAM;AAEZ,KAAI,MAAM;AAEV,KAAI,YAAY,IAAI,IAAI,KAAK,WAAW,CAAC,IAAI,SAAS,GAAG,EAAE,CAAC;AAE5D,KAAI,KAAK,SAAS,KAAK,QACrB,KAAI,UAAU,KAAK,OAAO,cAAc;AACtC,MAAI,MACF,KAAI,QAAQ,OAAO,UAAU;MAE7B,KAAI,UAAU,UAAU;GAE1B;AAGJ,KAAI,UAAU,KAAK,WAAW;AAC9B,KAAI,OAAO,KAAK,QAAS,EAAE;AAE3B,EAAA,GAAA,KAAA,gBAAe,KAAK;EAClB,SAAS,KAAA,WAAW;EACpB,MAAM,KAAA;EACP,CAAC;AAEF,QAAO;;;;;;;;;;;;;;;;;;;;;AAsBT,IAAa,SACX,UAC2B;AAC3B,QAAO,OAAO,UAAU,cAAc,aAAa;;;;;;;;;;;;;;;;;;;;;;;AAwBrD,IAAa,SACX,OACA,QACkB;AAClB,QAAO,MAAM,MAAM,GAAG,QAAQ,UAAU;EAAE,SAAS;EAAO,GAAG;EAAK,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpKrE,IAAa,uBAAb,MAA2D;CACzD;CAEA,YAAY,MAAa;AACvB,OAAK,OAAO;AAEZ,GAAA,GAAA,KAAA,gBAAe,MAAM;GACnB,MAAM,KAAA,WAAW;GACjB,KAAK,KAAA;GACN,CAAC;;CAGJ,IAAI,SAAyB;EAG3B,MAAM,QAAqB,OAAO,KAAK,KAAK,KAAK,CAAC,KAAK,QAAQ;GAC7D;GACA,KAAK;GACL;GACD,CAAC;EAEF,IAAI,eAAe;EACnB,IAAI,cAAc,MAAM;AAExB,SAAO,eAAe,aAAa;GACjC,MAAM,CAAC,KAAK,oBAAoB,WAAW,MAAM;GACjD,MAAM,WAAW,QAAQ;GACzB,MAAM,YAAY,mBAAmB;AAErC;AAEA,OAAI,OAAO;QACL,mBAAA,UAAU,SAAS,SAAS,IAAI,mBAAA,UAAU,SAAS,UAAU,EAAE;KACjE,MAAM,eAAe,OAAO,KAAK,SAAS;AAE1C,YAAO,KAAK,UAAU,CAAC,SAAS,aAAa;AAC3C,UAAI,EAAE,YAAY,UAChB,QAAO,mBAAmB,KAAK;OAEjC;AAEF,kBAAa,SAAS,aAAa;AAMjC,oBALe,MAAM,KAAK;OACxB;OACA,mBAAmB;OACnB;OACD,CAAC;OAEF;eACO,aAAa,UACtB,oBAAmB,OAAO;SAG5B,QAAO,mBAAmB;;AAI9B,SAAO,KAAK,QAAQ,CAAC,SAAS,eAAe;AAC3C,OAAI,CAAC,KAAK,KAAK,YAEb,MAAK,KAAK,cAAc,QAAQ;IAElC;;;;;;ACjGN,IAAM,kCAAkC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCxC,SAAgB,sBACd,WAAW,iCACL;CACN,MAAM,MAAA,GAAA,KAAA,kBAAmC;AAEzC,KAAI,CAAC,YAAY,GAAG,sBAAsB,GAAG,iBAAiB,WAAW,EACvE;CAGF,MAAM,eAAe,GAAG;AACxB,IAAG,UAAU;AAEb,KAAI;AACF,KAAG,qBAAqB;EACxB,MAAM,QAAQ,GAAG;EACjB,IAAI,aAAa;AAEjB,SAAO,MAAM,SAAS,GAAG;AACvB,OAAI,EAAE,eAAe,UAAU;AAC7B,UAAM,OAAO,EAAE;AACf;;GAGF,MAAM,QAAQ,MAAM,OAAO,EAAE;AAC7B,QAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,IAChC,OAAM,GAAG,cAAc;;WAGnB;AACR,KAAG,qBAAqB;AACxB,KAAG,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5BjB,IAAa,yBACX,YACmC,QAAQ,KAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACS7C,IAAa,eAAiC,EAC5C,SACA,UACA,SACA,OACA,WAAW,YAOP;CACJ,IAAI;CACJ,IAAI;CACJ,MAAM,iCAAiB,IAAI,KAAa;CACxC,MAAM,aAAa,MAAM,QAAQ,SAAS,GAAG,WAAW,CAAC,SAAS;CAElE,MAAM,gBAAgB;AACpB,iBAAe,OAAO;AAEtB,MAAI,aAAa,OAAO;AACtB,WAAQ,UAAW,QAAQ;AAC3B,cAAW,KAAA;AACX;;AAGF,MAAI,WAAW;AACb,gBAAa,UAAU;AACvB,eAAY,KAAA;;AAGd,cAAY,iBAAiB;AAC3B,WAAQ,UAAW,QAAQ;AAC3B,eAAY,KAAA;AACZ,cAAW,KAAA;KACV,SAAS;;CAGd,MAAM,SAAS,aAAqB;EAClC,MAAM,qBAAqB,eAAe,OAAO;AACjD,iBAAe,IAAI,SAAS;AAE5B,MAAI,mBACF;AAGF,MAAI,WAAW;AACb,gBAAa,UAAU;AACvB,eAAY,KAAA;;AAGd,aAAW,WAAW;;CAGxB,MAAM,QAAQ,aAAqB;EACjC,MAAM,wBAAwB,CAAC,eAAe;AAE9C,iBAAe,OAAO,SAAS;EAE/B,MAAM,cAAc,eAAe,OAAO;AAE1C,MAAI,yBAAyB,YAC3B;AAGF,WAAS;;AAGX,YAAW,SAAS,aAAa;AAC/B,MAAI,SAAS;AACX,IAAA,GAAA,KAAA,kBAAiB,SAAS,gBAAgB,MAAM,SAAS,CAAC;AAC1D,IAAA,GAAA,KAAA,oBAAmB,SAAS,gBAAgB,KAAK,SAAS,CAAC;SACtD;AACL,IAAA,GAAA,KAAA,kBAAiB,gBAAgB,MAAM,SAAS,CAAC;AACjD,IAAA,GAAA,KAAA,oBAAmB,gBAAgB,KAAK,SAAS,CAAC;;GAEpD;AAEF,QAAO"}
package/mobx.d.ts CHANGED
@@ -36,7 +36,7 @@ type ComputedOtherOptions = Omit<IComputedValueOptions<any>, 'equals'>;
36
36
  * Observable flavour keys returned by {@link annotation.observable}: `ref`, `deep`, `shallow`, `struct`.
37
37
  * Also supported: `true` (base `observable`) and `false` (no annotation).
38
38
  */
39
- type ObservableTypes = keyof Pick<typeof observable, 'ref' | 'deep' | 'shallow' | 'struct'>;
39
+ type ObservableTypes = keyof Pick<typeof observable, 'ref' | 'deep' | 'shallow' | 'struct'> | boolean;
40
40
  /**
41
41
  * MobX annotation factories for `makeObservable` and tuple-style wiring ({@link applyObservable}).
42
42
  *
@@ -121,9 +121,11 @@ type ObservableTypes = keyof Pick<typeof observable, 'ref' | 'deep' | 'shallow'
121
121
  type AnnotationObject = {
122
122
  computed(value: false): false;
123
123
  computed(value?: Exclude<ComputedEqualsValue, false>, options?: ComputedOtherOptions): typeof computed.struct;
124
- observable(value: false): false;
125
- observable(value?: undefined | true): typeof observable;
126
- observable<TValue extends ObservableTypes>(value: TValue): (typeof observable)[TValue];
124
+ computed(value?: ComputedEqualsValue): false | typeof computed.struct;
125
+ observable(value: Extract<ObservableTypes, false>): false;
126
+ observable(value?: Extract<ObservableTypes, true>): typeof observable;
127
+ observable<TValue extends Exclude<ObservableTypes, boolean>>(value: TValue): (typeof observable)[TValue];
128
+ observable(value?: ObservableTypes): false | (typeof observable)[Exclude<ObservableTypes, boolean>];
127
129
  };
128
130
  declare const annotation: AnnotationObject;
129
131
 
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 = keyof Pick<\n typeof observable,\n 'ref' | 'deep' | 'shallow' | 'struct'\n>;\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 observable(value: false): false;\n observable(value?: undefined | true): typeof observable;\n observable<TValue extends ObservableTypes>(\n value: TValue,\n ): (typeof observable)[TValue];\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yummies",
3
- "version": "7.16.1",
3
+ "version": "7.17.0",
4
4
  "keywords": [
5
5
  "javascript",
6
6
  "typescript",