@expressive/mvc 0.80.1 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,13 +6,17 @@
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://www.npmjs.com/package/@expressive/mvc"><img alt="NPM" src="https://badge.fury.io/js/%40expressive%2Fmvc.svg"></a>
9
- <img src="https://img.shields.io/badge/Coverage-100%25-brightgreen.svg">
9
+ <a href="https://github.com/gabeklein/expressive-mvc/actions/workflows/coverage.yml"><img alt="Coverage" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fgabeklein%2Fexpressive-mvc%2Fbadges%2Fcoverage.json"></a>
10
10
  </p>
11
11
 
12
12
  ---
13
13
 
14
14
  The core of [Expressive MVC](https://github.com/gabeklein/expressive-mvc): reactive primitives built around plain classes, with no framework dependency. Provides the `State` model, instructions, context, and the renderer-agnostic `Component` that adapters like [`@expressive/react`](https://www.npmjs.com/package/@expressive/react) build on.
15
15
 
16
+ > **Building a React app?** Install [`@expressive/react`](https://www.npmjs.com/package/@expressive/react) instead - it depends on this package, so you get the whole core through it and should **not** list `@expressive/mvc` in your `package.json` as well. Import `State`, `Component`, and every instruction from the adapter.
17
+
18
+ Install this package directly only for host-agnostic code that must not depend on a UI adapter - a shared domain package, a Node service, or a new adapter:
19
+
16
20
  ```bash
17
21
  npm install @expressive/mvc
18
22
  ```
@@ -54,13 +58,15 @@ Field initializers that change how a property behaves:
54
58
  | `ref()` | a mutable reference (e.g. a DOM node) that's still reactive |
55
59
  | `set()` | computed values, smart setters, side-effects on assignment |
56
60
  | `get()` | dependency injection - pull another `State` from context |
57
- | `hot()` | a shallow-reactive array or object |
61
+ | `map()` | a shallow-reactive Map |
62
+ | `has()` | an owned reactive collection - an ordered list or a spawned pool |
58
63
 
59
64
  ```ts
60
- import { State, set, hot } from '@expressive/mvc';
65
+ import { State, set, has, map } from '@expressive/mvc';
61
66
 
62
67
  class Cart extends State {
63
- items = hot<Item[]>([]); // reactive collection
68
+ items = has<Item>(); // reactive collection
69
+ products = map<string, Product>(); // reactive keyed collection
64
70
  coupon = set('', code => apply(code)); // side-effect on assignment
65
71
  }
66
72
  ```
@@ -49,6 +49,8 @@ declare class Component extends State {
49
49
  * Will incorperate extra props you declare as props parameter in `render` method.
50
50
  */
51
51
  readonly props: Component.Props<this>;
52
+ /** Stable identity used when this instance is rendered in a collection. */
53
+ readonly key: string;
52
54
  /**
53
55
  * Content to display while this component or its children are suspended.
54
56
  * Will cover suspense by pending properties in `this.render` as well.
package/dist/component.js CHANGED
@@ -60,17 +60,37 @@ var Component = class extends State {
60
60
  }
61
61
  };
62
62
  /**
63
+ * Default `key` accessor. The get/set pair keeps it off observed state.
64
+ */
65
+ Object.defineProperty(Component.prototype, "key", {
66
+ configurable: true,
67
+ get() {
68
+ return String(this);
69
+ },
70
+ set(value) {
71
+ Object.defineProperty(this, "key", { value });
72
+ }
73
+ });
74
+ /**
63
75
  * Seal each class's own `render` non-configurable at bootstrap so member
64
76
  * classification leaves it unbound - it stays the content-render seam the chain
65
77
  * reads (and which adapters like preact detect as the class-component marker).
66
78
  */
67
- Component.on({ type(type) {
68
- const desc = Object.getOwnPropertyDescriptor(type.prototype, "render");
69
- if (desc && typeof desc.value == "function") Object.defineProperty(type.prototype, "render", {
70
- ...desc,
71
- configurable: false
72
- });
73
- } });
79
+ Component.on({
80
+ before(self) {
81
+ if (Object.getOwnPropertyDescriptor(self, "key")?.configurable) Object.defineProperty(self, "key", {
82
+ enumerable: false,
83
+ writable: false
84
+ });
85
+ },
86
+ type(type) {
87
+ const desc = Object.getOwnPropertyDescriptor(type.prototype, "render");
88
+ if (desc && typeof desc.value == "function") Object.defineProperty(type.prototype, "render", {
89
+ ...desc,
90
+ configurable: false
91
+ });
92
+ }
93
+ });
74
94
  /**
75
95
  * Build (or fetch the cached) composed content render for an instance's class.
76
96
  * Walks the prototype chain for content renders strictly below Component - the
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","names":[],"sources":["../src/component.ts"],"sourcesContent":["import { Context } from './context';\nimport { set } from './field/set';\nimport { State, unbind } from './state';\n\nimport type { Host } from './jsx-runtime';\n\nconst PENDING = new WeakMap<object, Component>();\n\n/** Per-class composed content render. */\nconst CHAIN = new WeakMap<Function, Function>();\n\ntype IfEquals<X, Y, A, B> =\n (<T>() => T extends X ? 1 : 2) extends\n (<T>() => T extends Y ? 1 : 2) ? A : B;\n\n/** Keys of T which are settable (excludes get-only accessors and `readonly`). */\ntype Acceptable<T> = {\n [P in keyof T]-?: IfEquals<\n { [Q in P]: T[P] },\n { -readonly [Q in P]: T[P] },\n P, never\n >;\n}[keyof T];\n\ndeclare namespace Component {\n /**\n * Host element type produced by `Component.render`. Delegates to the\n * {@link Host} manifest on the jsx-runtime entry; falls back to `any`\n * until an adapter augments it with `node`. `any` (not `unknown`) so an\n * un-annotated `render` override in a host-agnostic package still emits a\n * JSX-valid return - `any` is assignable to every host's node type, where\n * `unknown` is assignable to none.\n */\n type Node = Host extends { node: infer T } ? T : any;\n\n interface BaseProps<T extends Component> {\n /**\n * Callback for newly created instance. Only called once.\n *\n * Runs after props apply but **before** the `new()` lifecycle hook - so it\n * may configure state that `new()` then observes. To react to a fully\n * initialized instance instead, use `watch` or an effect.\n */\n is?: (instance: T) => void;\n\n /**\n * Fallback to show when suspended or in error recovery.\n * Pass `false` to opt out of the component's own suspense boundary,\n * letting suspension bubble to an ancestor.\n */\n fallback?: Component.Node;\n }\n\n type StateProps<T extends State> = {\n [K in Exclude<keyof T, keyof Component> & Acceptable<T>]?: T[K];\n };\n\n type RenderProps<T> = [T] extends [(props: infer P) => any]\n ? [keyof NonNullable<P>] extends [never]\n ? { children?: Component.Node }\n : NonNullable<P>\n : { children?: Component.Node };\n\n type Props<T extends Component> =\n & StateProps<T>\n & BaseProps<T>\n & RenderProps<T['render']>;\n}\n\nclass Component extends State {\n /**\n * All JSX attributes passed to this component.\n * Includes state-derived props, render props, and built-in props like `is` and `fallback`.\n *\n * Will incorperate extra props you declare as props parameter in `render` method.\n */\n declare readonly props: Component.Props<this>;\n\n /**\n * Content to display while this component or its children are suspended.\n * Will cover suspense by pending properties in `this.render` as well.\n * Can be set as a class property or overridden via the `fallback` JSX prop.\n *\n * Defaults to null - nothing will be rendered.\n * Set `false` to opt out of having a boundary at all - suspension then\n * bubbles to the nearest ancestor boundary.\n */\n fallback: Component.Node = set(null);\n\n constructor(props?: any, ...rest: any[]) {\n if (props == null) props = {};\n\n const seen = {} as Record<string, undefined>;\n const copy = PENDING.get(props);\n\n if (typeof props == 'object') merge(props);\n\n function merge(props: {}) {\n for (const k in props) seen[k] = undefined;\n return { ...seen, ...props };\n }\n\n super(copy ? [] : [\n props,\n rest.filter((x) => !(x instanceof Context)),\n () => {\n props.is?.(this);\n Object.defineProperty(this, 'props', { enumerable: false });\n PENDING.delete(props);\n }\n ]);\n\n if (copy) return copy;\n\n PENDING.set(props, this);\n\n this.props = props;\n this.set('props', () => {\n this.set(merge(this.props));\n });\n\n Object.defineProperty(this, 'render', {\n writable: true,\n configurable: true,\n value: render(this)\n });\n }\n\n /**\n * Output for this component. Override to define custom JSX.\n *\n * Properties accessed via `this` are reactive and trigger a render when they\n * change, in addition to props. Accepts an optional parameter to receive extra\n * props from JSX, beyond those merged to state properties; declare its shape\n * via `props = {} as { ... }`. Without a parameter (the default below),\n * children pass through.\n *\n * The constructor installs a composed `render` per instance; the bootstrap\n * `type` pass seals this and any override non-configurable, keeping it the\n * content-render seam the chain reads.\n */\n render(props?: {}): Component.Node {\n const { children } = (props || this.props) as { children?: Component.Node };\n return children || null;\n }\n\n /**\n * Called when a child component throws during render.\n * While this is pending, `fallback` is displayed.\n * When resolved, the error boundary resets and `render` is called again.\n *\n * Override to handle errors - set `this.fallback` for error-specific UI,\n * await async recovery, or await user interaction before retrying. If you\n * assign a fallback within catch, it will be reverted after resolved.\n */\n catch?(error: Error): Promise<void> | void;\n}\n\n/**\n * Seal each class's own `render` non-configurable at bootstrap so member\n * classification leaves it unbound - it stays the content-render seam the chain\n * reads (and which adapters like preact detect as the class-component marker).\n */\nComponent.on({\n type(type) {\n const desc = Object.getOwnPropertyDescriptor(type.prototype, 'render');\n\n if (desc && typeof desc.value == 'function')\n Object.defineProperty(type.prototype, 'render', {\n ...desc,\n configurable: false\n });\n }\n});\n\n/**\n * Build (or fetch the cached) composed content render for an instance's class.\n * Walks the prototype chain for content renders strictly below Component - the\n * default render on Component.prototype is excluded and serves as the fallback\n * when a class authors none.\n */\nfunction render(target: Component) {\n const cached = CHAIN.get(target.constructor);\n\n if (cached) return cached;\n\n let render: Function | undefined;\n let proto = target;\n\n while ((proto = Object.getPrototypeOf(proto)) !== Component.prototype) {\n const desc = Object.getOwnPropertyDescriptor(proto, 'render');\n\n if (desc) {\n const layer = unbind(desc.get || desc.value);\n render = render ? compose(layer, render) : layer;\n }\n }\n\n if (!render)\n render = Component.prototype.render;\n\n CHAIN.set(target.constructor, render);\n\n return render;\n}\n\n/** Wrap an outer render so it receives `inner` as a lazy `children` getter. */\nfunction compose(outer: Function, inner: Function): Function {\n return function (this: Component, props?: {}) {\n const self = this;\n return outer.call(self, {\n ...props,\n get children() {\n return inner.call(self, props);\n }\n });\n };\n}\n\nexport { Component };\n"],"mappings":";;;;;AAMA,MAAM,0BAAU,IAAI,SAA4B;;AAGhD,MAAM,wBAAQ,IAAI,SAA6B;AA4D/C,IAAM,YAAN,cAAwB,MAAM;CAoB5B,YAAY,OAAa,GAAG,MAAa;AACvC,MAAI,SAAS,KAAM,SAAQ,EAAE;EAE7B,MAAM,OAAO,EAAE;EACf,MAAM,OAAO,QAAQ,IAAI,MAAM;AAE/B,MAAI,OAAO,SAAS,SAAU,OAAM,MAAM;EAE1C,SAAS,MAAM,OAAW;AACxB,QAAK,MAAM,KAAK,MAAO,MAAK,KAAK;AACjC,UAAO;IAAE,GAAG;IAAM,GAAG;IAAO;;AAG9B,QAAM,OAAO,EAAE,GAAG;GAChB;GACA,KAAK,QAAQ,MAAM,EAAE,aAAa,SAAS;SACrC;AACJ,UAAM,KAAK,KAAK;AAChB,WAAO,eAAe,MAAM,SAAS,EAAE,YAAY,OAAO,CAAC;AAC3D,YAAQ,OAAO,MAAM;;GAExB,CAAC;kBAvBuB,IAAI,KAAK;AAyBlC,MAAI,KAAM,QAAO;AAEjB,UAAQ,IAAI,OAAO,KAAK;AAExB,OAAK,QAAQ;AACb,OAAK,IAAI,eAAe;AACtB,QAAK,IAAI,MAAM,KAAK,MAAM,CAAC;IAC3B;AAEF,SAAO,eAAe,MAAM,UAAU;GACpC,UAAU;GACV,cAAc;GACd,OAAO,OAAO,KAAK;GACpB,CAAC;;;;;;;;;;;;;;;CAgBJ,OAAO,OAA4B;EACjC,MAAM,EAAE,aAAc,SAAS,KAAK;AACpC,SAAO,YAAY;;;;;;;;AAoBvB,UAAU,GAAG,EACX,KAAK,MAAM;CACT,MAAM,OAAO,OAAO,yBAAyB,KAAK,WAAW,SAAS;AAEtE,KAAI,QAAQ,OAAO,KAAK,SAAS,WAC/B,QAAO,eAAe,KAAK,WAAW,UAAU;EAC9C,GAAG;EACH,cAAc;EACf,CAAC;GAEP,CAAC;;;;;;;AAQF,SAAS,OAAO,QAAmB;CACjC,MAAM,SAAS,MAAM,IAAI,OAAO,YAAY;AAE5C,KAAI,OAAQ,QAAO;CAEnB,IAAI;CACJ,IAAI,QAAQ;AAEZ,SAAQ,QAAQ,OAAO,eAAe,MAAM,MAAM,UAAU,WAAW;EACrE,MAAM,OAAO,OAAO,yBAAyB,OAAO,SAAS;AAE7D,MAAI,MAAM;GACR,MAAM,QAAQ,OAAO,KAAK,OAAO,KAAK,MAAM;AAC5C,YAAS,SAAS,QAAQ,OAAO,OAAO,GAAG;;;AAI/C,KAAI,CAAC,OACH,UAAS,UAAU,UAAU;AAE/B,OAAM,IAAI,OAAO,aAAa,OAAO;AAErC,QAAO;;;AAIT,SAAS,QAAQ,OAAiB,OAA2B;AAC3D,QAAO,SAA2B,OAAY;EAC5C,MAAM,OAAO;AACb,SAAO,MAAM,KAAK,MAAM;GACtB,GAAG;GACH,IAAI,WAAW;AACb,WAAO,MAAM,KAAK,MAAM,MAAM;;GAEjC,CAAC"}
1
+ {"version":3,"file":"component.js","names":[],"sources":["../src/component.ts"],"sourcesContent":["import { Context } from './context';\nimport { set } from './field/set';\nimport { State, unbind } from './state';\n\nimport type { Host } from './jsx-runtime';\n\nconst PENDING = new WeakMap<object, Component>();\n\n/** Per-class composed content render. */\nconst CHAIN = new WeakMap<Function, Function>();\n\ntype IfEquals<X, Y, A, B> =\n (<T>() => T extends X ? 1 : 2) extends\n (<T>() => T extends Y ? 1 : 2) ? A : B;\n\n/** Keys of T which are settable (excludes get-only accessors and `readonly`). */\ntype Acceptable<T> = {\n [P in keyof T]-?: IfEquals<\n { [Q in P]: T[P] },\n { -readonly [Q in P]: T[P] },\n P, never\n >;\n}[keyof T];\n\ndeclare namespace Component {\n /**\n * Host element type produced by `Component.render`. Delegates to the\n * {@link Host} manifest on the jsx-runtime entry; falls back to `any`\n * until an adapter augments it with `node`. `any` (not `unknown`) so an\n * un-annotated `render` override in a host-agnostic package still emits a\n * JSX-valid return - `any` is assignable to every host's node type, where\n * `unknown` is assignable to none.\n */\n type Node = Host extends { node: infer T } ? T : any;\n\n interface BaseProps<T extends Component> {\n /**\n * Callback for newly created instance. Only called once.\n *\n * Runs after props apply but **before** the `new()` lifecycle hook - so it\n * may configure state that `new()` then observes. To react to a fully\n * initialized instance instead, use `watch` or an effect.\n */\n is?: (instance: T) => void;\n\n /**\n * Fallback to show when suspended or in error recovery.\n * Pass `false` to opt out of the component's own suspense boundary,\n * letting suspension bubble to an ancestor.\n */\n fallback?: Component.Node;\n }\n\n type StateProps<T extends State> = {\n [K in Exclude<keyof T, keyof Component> & Acceptable<T>]?: T[K];\n };\n\n type RenderProps<T> = [T] extends [(props: infer P) => any]\n ? [keyof NonNullable<P>] extends [never]\n ? { children?: Component.Node }\n : NonNullable<P>\n : { children?: Component.Node };\n\n type Props<T extends Component> =\n & StateProps<T>\n & BaseProps<T>\n & RenderProps<T['render']>;\n}\n\nclass Component extends State {\n /**\n * All JSX attributes passed to this component.\n * Includes state-derived props, render props, and built-in props like `is` and `fallback`.\n *\n * Will incorperate extra props you declare as props parameter in `render` method.\n */\n declare readonly props: Component.Props<this>;\n\n /** Stable identity used when this instance is rendered in a collection. */\n declare readonly key: string;\n\n /**\n * Content to display while this component or its children are suspended.\n * Will cover suspense by pending properties in `this.render` as well.\n * Can be set as a class property or overridden via the `fallback` JSX prop.\n *\n * Defaults to null - nothing will be rendered.\n * Set `false` to opt out of having a boundary at all - suspension then\n * bubbles to the nearest ancestor boundary.\n */\n fallback: Component.Node = set(null);\n\n constructor(props?: any, ...rest: any[]) {\n if (props == null) props = {};\n\n const seen = {} as Record<string, undefined>;\n const copy = PENDING.get(props);\n\n if (typeof props == 'object') merge(props);\n\n function merge(props: {}) {\n for (const k in props) seen[k] = undefined;\n return { ...seen, ...props };\n }\n\n super(copy ? [] : [\n props,\n rest.filter((x) => !(x instanceof Context)),\n () => {\n props.is?.(this);\n Object.defineProperty(this, 'props', { enumerable: false });\n PENDING.delete(props);\n }\n ]);\n\n if (copy) return copy;\n\n PENDING.set(props, this);\n\n this.props = props;\n this.set('props', () => {\n this.set(merge(this.props));\n });\n\n Object.defineProperty(this, 'render', {\n writable: true,\n configurable: true,\n value: render(this)\n });\n }\n\n /**\n * Output for this component. Override to define custom JSX.\n *\n * Properties accessed via `this` are reactive and trigger a render when they\n * change, in addition to props. Accepts an optional parameter to receive extra\n * props from JSX, beyond those merged to state properties; declare its shape\n * via `props = {} as { ... }`. Without a parameter (the default below),\n * children pass through.\n *\n * The constructor installs a composed `render` per instance; the bootstrap\n * `type` pass seals this and any override non-configurable, keeping it the\n * content-render seam the chain reads.\n */\n render(props?: {}): Component.Node {\n const { children } = (props || this.props) as { children?: Component.Node };\n return children || null;\n }\n\n /**\n * Called when a child component throws during render.\n * While this is pending, `fallback` is displayed.\n * When resolved, the error boundary resets and `render` is called again.\n *\n * Override to handle errors - set `this.fallback` for error-specific UI,\n * await async recovery, or await user interaction before retrying. If you\n * assign a fallback within catch, it will be reverted after resolved.\n */\n catch?(error: Error): Promise<void> | void;\n}\n\n/**\n * Default `key` accessor. The get/set pair keeps it off observed state.\n */\nObject.defineProperty(Component.prototype, 'key', {\n configurable: true,\n get() {\n return String(this);\n },\n set(value) {\n Object.defineProperty(this, 'key', { value });\n }\n});\n\n/**\n * Seal each class's own `render` non-configurable at bootstrap so member\n * classification leaves it unbound - it stays the content-render seam the chain\n * reads (and which adapters like preact detect as the class-component marker).\n */\nComponent.on({\n before(self) {\n const key = Object.getOwnPropertyDescriptor(self, 'key');\n\n if (key?.configurable)\n Object.defineProperty(self, 'key', {\n enumerable: false,\n writable: false\n });\n },\n type(type) {\n const desc = Object.getOwnPropertyDescriptor(type.prototype, 'render');\n\n if (desc && typeof desc.value == 'function')\n Object.defineProperty(type.prototype, 'render', {\n ...desc,\n configurable: false\n });\n }\n});\n\n/**\n * Build (or fetch the cached) composed content render for an instance's class.\n * Walks the prototype chain for content renders strictly below Component - the\n * default render on Component.prototype is excluded and serves as the fallback\n * when a class authors none.\n */\nfunction render(target: Component) {\n const cached = CHAIN.get(target.constructor);\n\n if (cached) return cached;\n\n let render: Function | undefined;\n let proto = target;\n\n while ((proto = Object.getPrototypeOf(proto)) !== Component.prototype) {\n const desc = Object.getOwnPropertyDescriptor(proto, 'render');\n\n if (desc) {\n const layer = unbind(desc.get || desc.value);\n render = render ? compose(layer, render) : layer;\n }\n }\n\n if (!render)\n render = Component.prototype.render;\n\n CHAIN.set(target.constructor, render);\n\n return render;\n}\n\n/** Wrap an outer render so it receives `inner` as a lazy `children` getter. */\nfunction compose(outer: Function, inner: Function): Function {\n return function (this: Component, props?: {}) {\n const self = this;\n return outer.call(self, {\n ...props,\n get children() {\n return inner.call(self, props);\n }\n });\n };\n}\n\nexport { Component };\n"],"mappings":";;;;;AAMA,MAAM,0BAAU,IAAI,SAA4B;;AAGhD,MAAM,wBAAQ,IAAI,SAA6B;AA4D/C,IAAM,YAAN,cAAwB,MAAM;CAuB5B,YAAY,OAAa,GAAG,MAAa;AACvC,MAAI,SAAS,KAAM,SAAQ,EAAE;EAE7B,MAAM,OAAO,EAAE;EACf,MAAM,OAAO,QAAQ,IAAI,MAAM;AAE/B,MAAI,OAAO,SAAS,SAAU,OAAM,MAAM;EAE1C,SAAS,MAAM,OAAW;AACxB,QAAK,MAAM,KAAK,MAAO,MAAK,KAAK;AACjC,UAAO;IAAE,GAAG;IAAM,GAAG;IAAO;;AAG9B,QAAM,OAAO,EAAE,GAAG;GAChB;GACA,KAAK,QAAQ,MAAM,EAAE,aAAa,SAAS;SACrC;AACJ,UAAM,KAAK,KAAK;AAChB,WAAO,eAAe,MAAM,SAAS,EAAE,YAAY,OAAO,CAAC;AAC3D,YAAQ,OAAO,MAAM;;GAExB,CAAC;kBAvBuB,IAAI,KAAK;AAyBlC,MAAI,KAAM,QAAO;AAEjB,UAAQ,IAAI,OAAO,KAAK;AAExB,OAAK,QAAQ;AACb,OAAK,IAAI,eAAe;AACtB,QAAK,IAAI,MAAM,KAAK,MAAM,CAAC;IAC3B;AAEF,SAAO,eAAe,MAAM,UAAU;GACpC,UAAU;GACV,cAAc;GACd,OAAO,OAAO,KAAK;GACpB,CAAC;;;;;;;;;;;;;;;CAgBJ,OAAO,OAA4B;EACjC,MAAM,EAAE,aAAc,SAAS,KAAK;AACpC,SAAO,YAAY;;;;;;AAkBvB,OAAO,eAAe,UAAU,WAAW,OAAO;CAChD,cAAc;CACd,MAAM;AACJ,SAAO,OAAO,KAAK;;CAErB,IAAI,OAAO;AACT,SAAO,eAAe,MAAM,OAAO,EAAE,OAAO,CAAC;;CAEhD,CAAC;;;;;;AAOF,UAAU,GAAG;CACX,OAAO,MAAM;AAGX,MAFY,OAAO,yBAAyB,MAAM,MAAM,EAE/C,aACP,QAAO,eAAe,MAAM,OAAO;GACjC,YAAY;GACZ,UAAU;GACX,CAAC;;CAEN,KAAK,MAAM;EACT,MAAM,OAAO,OAAO,yBAAyB,KAAK,WAAW,SAAS;AAEtE,MAAI,QAAQ,OAAO,KAAK,SAAS,WAC/B,QAAO,eAAe,KAAK,WAAW,UAAU;GAC9C,GAAG;GACH,cAAc;GACf,CAAC;;CAEP,CAAC;;;;;;;AAQF,SAAS,OAAO,QAAmB;CACjC,MAAM,SAAS,MAAM,IAAI,OAAO,YAAY;AAE5C,KAAI,OAAQ,QAAO;CAEnB,IAAI;CACJ,IAAI,QAAQ;AAEZ,SAAQ,QAAQ,OAAO,eAAe,MAAM,MAAM,UAAU,WAAW;EACrE,MAAM,OAAO,OAAO,yBAAyB,OAAO,SAAS;AAE7D,MAAI,MAAM;GACR,MAAM,QAAQ,OAAO,KAAK,OAAO,KAAK,MAAM;AAC5C,YAAS,SAAS,QAAQ,OAAO,OAAO,GAAG;;;AAI/C,KAAI,CAAC,OACH,UAAS,UAAU,UAAU;AAE/B,OAAM,IAAI,OAAO,aAAa,OAAO;AAErC,QAAO;;;AAIT,SAAS,QAAQ,OAAiB,OAA2B;AAC3D,QAAO,SAA2B,OAAY;EAC5C,MAAM,OAAO;AACb,SAAO,MAAM,KAAK,MAAM;GACtB,GAAG;GACH,IAAI,WAAW;AACb,WAAO,MAAM,KAAK,MAAM,MAAM;;GAEjC,CAAC"}
package/dist/context.d.ts CHANGED
@@ -34,9 +34,12 @@ declare class Context {
34
34
  * Context will add or remove States as needed to keep with provided input.
35
35
  *
36
36
  * @param inputs State, State class, or map of States / State classes to register.
37
- * @param forEach Optional callback to run for each State registered.
37
+ * @param forEach Optional callback to run for each State registered. Receives
38
+ * whether this context created the State - and so will destroy it - as
39
+ * opposed to it having been provided already active. May return a callback
40
+ * to run before that entry is dropped.
38
41
  */
39
- set<T extends State>(inputs: Accept<T>, forEach?: (state: T) => (() => void) | void): this;
42
+ set<T extends State>(inputs: Accept<T>, forEach?: (state: T, owned: boolean) => (() => void) | void): this;
40
43
  add(I: State, explicit?: boolean): () => void;
41
44
  /**
42
45
  * Create a child context, optionally registering one or more States to it.
package/dist/context.js CHANGED
@@ -81,7 +81,10 @@ var Context = class Context {
81
81
  * Context will add or remove States as needed to keep with provided input.
82
82
  *
83
83
  * @param inputs State, State class, or map of States / State classes to register.
84
- * @param forEach Optional callback to run for each State registered.
84
+ * @param forEach Optional callback to run for each State registered. Receives
85
+ * whether this context created the State - and so will destroy it - as
86
+ * opposed to it having been provided already active. May return a callback
87
+ * to run before that entry is dropped.
85
88
  */
86
89
  set(inputs, forEach) {
87
90
  const init = /* @__PURE__ */ new Set();
@@ -99,19 +102,20 @@ var Context = class Context {
99
102
  cleanup.delete(K);
100
103
  }
101
104
  if (!V) continue;
102
- if (!(State.is(V) || V instanceof State)) {
105
+ const owned = State.is(V);
106
+ if (!(owned || V instanceof State)) {
103
107
  const as = K == "0" || K == String(V) ? V : `${V} (as '${K}')`;
104
108
  throw new Error(`Context can only include an instance or class of State but got ${as}.`);
105
109
  }
106
- const state = State.is(V) ? new V() : V.is;
110
+ const state = owned ? new V() : V.is;
107
111
  const remove = this.add(state, true);
108
112
  init.add(() => {
109
113
  event(state);
110
- const dispose = forEach && forEach(state);
114
+ const dispose = forEach?.(state, owned);
111
115
  cleanup.set(K, () => {
112
- if (dispose) dispose();
116
+ if (typeof dispose == "function") dispose();
113
117
  remove();
114
- if (state !== V) event(state, null);
118
+ if (owned) event(state, null);
115
119
  });
116
120
  });
117
121
  }
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["import { listener } from \"./observable\";\nimport { event, State, uid } from \"./state\";\n\nconst LOOKUP = new WeakMap<State, Context>();\nlet ROOT: Context;\n\ntype Accept<T extends State = State> =\n | T\n | State.Type<T>\n | Record<string | number, T | State.Type<T>>;\n\ntype Expect<T extends State = State> = (\n state: T,\n downstream: boolean,\n) => (() => void) | void;\n\ndeclare namespace Context {\n export { Accept, Expect };\n}\n\nclass Context {\n static get root(): Context {\n return ROOT ??= new Context();\n }\n\n /** Get the context for a State. Adapters may override to provide framework context. */\n static get(state?: State): Context {\n return state && LOOKUP.get(state.is) || Context.root;\n }\n\n public id = uid();\n public parent?: Context;\n public scope = new Set<Context>();\n public consume = new Map<\n State.Extends,\n Set<[Expect, boolean | undefined]> | null\n >();\n public provide = new Map<State.Extends, Set<[State, boolean]> | null>();\n\n protected inputs: Record<string | number, State | State.Extends> = {};\n\n private cleanup = new Map<string | number | Function, () => void>();\n\n constructor(arg?: Context | Accept) {\n if (arg instanceof Context) {\n this.parent = arg;\n arg.scope.add(this);\n } else if (arg) {\n this.set(arg);\n }\n }\n\n private traverse(accept: (ctx: Context) => boolean | void) {\n const queue = [...this.scope];\n for (const ctx of queue)\n if (accept(ctx) !== false) for (const c of ctx.scope) queue.push(c);\n }\n\n private register<T extends State>(\n type: State.Extends<T>,\n value: [Expect<T>, boolean | undefined],\n asConsumer: true,\n ): () => void;\n\n private register<T extends State>(\n type: State.Extends<T>,\n value: [T, boolean],\n asConsumer?: false,\n ): () => void;\n\n private register(type: State.Extends, value: any, asConsumer?: boolean) {\n const mode = asConsumer ? \"consume\" : \"provide\";\n let set = this[mode].get(type) as Set<any>;\n if (!set) this[mode].set(type, (set = new Set()));\n set.add(value);\n\n for (let ctx = this.parent; ctx; ctx = ctx.parent) {\n if (ctx[mode].has(type)) break;\n ctx[mode].set(type, null);\n }\n\n return () => set.delete(value);\n }\n\n /** Find specified type upstream. Throws if not found. */\n public get<T extends State>(Type: State.Extends<T>, required?: true): T;\n\n /** Find specified type upstream. Returns undefined if not found. */\n public get<T extends State>(\n Type: State.Extends<T>,\n required?: boolean,\n ): T | undefined;\n\n /** Subscribe to a type becoming available in context. */\n public get<T extends State>(\n Type: State.Extends<T>,\n callback: Expect<T>,\n downstream?: boolean,\n ): () => void;\n\n /** Lookup excluding a specific instance (used by State.prototype.get for upstream-of-self semantics). */\n public get<T extends State>(\n Type: State.Extends<T>,\n arg: boolean | Expect<T> | undefined,\n downstream: boolean | undefined,\n skip: State,\n ): T | undefined | (() => void);\n\n public get<T extends State>(\n Type: State.Extends<T>,\n arg?: boolean | Expect<T>,\n downstream?: boolean,\n skip?: State,\n ) {\n let found: T | null | undefined;\n let priority = false;\n\n for (let ctx: Context | undefined = this; ctx; ctx = ctx.parent) {\n const entries = ctx.provide.get(Type);\n if (!entries) continue;\n\n for (const [state, explicit] of entries) {\n if (state === skip || found === state) continue;\n if (!found || (explicit && !priority)) {\n found = state as T;\n priority = explicit;\n continue;\n }\n if (!priority) {\n found = null;\n break;\n }\n if (explicit)\n throw new Error(\n `Did find ${Type} in context, but multiple were defined.`,\n );\n }\n if (found !== undefined) break;\n }\n\n if (typeof arg === \"function\") {\n if (found && !downstream) arg(found, false);\n\n if (downstream !== false) {\n this.traverse((ctx) => {\n const has = ctx.provide.get(Type);\n if (has) for (const x of has) if (x[0] !== skip) arg(x[0] as T, true);\n return has !== undefined;\n });\n }\n\n return this.register(Type, [arg as Expect, downstream], true);\n }\n\n if (found) return found;\n if (found === null) return null;\n if (arg !== false) throw new Error(`Could not find ${Type} in context.`);\n }\n\n /**\n * Register one or more States to this context.\n *\n * Context will add or remove States as needed to keep with provided input.\n *\n * @param inputs State, State class, or map of States / State classes to register.\n * @param forEach Optional callback to run for each State registered.\n */\n public set<T extends State>(\n inputs: Accept<T>,\n forEach?: (state: T) => (() => void) | void,\n ) {\n const init = new Set<() => void>();\n const { cleanup } = this;\n\n if (typeof inputs == \"function\" || inputs instanceof State)\n inputs = { [0]: inputs };\n\n for (const K of Object.keys({ ...this.inputs, ...inputs })) {\n const V = inputs[K];\n const E = this.inputs[K];\n\n if (E === V) continue;\n\n if (E) {\n cleanup.get(K)?.();\n cleanup.delete(K);\n }\n\n if (!V) continue;\n\n if (!(State.is(V) || V instanceof State)) {\n const as = K == \"0\" || K == String(V) ? V : `${V} (as '${K}')`;\n throw new Error(\n `Context can only include an instance or class of State but got ${as}.`,\n );\n }\n\n const state = State.is(V) ? new (V as State.Type)() : V.is;\n const remove = this.add(state, true);\n\n init.add(() => {\n event(state);\n const dispose = forEach && forEach(state as T);\n cleanup.set(K, () => {\n if (dispose) dispose();\n remove();\n if (state !== V) event(state, null);\n });\n });\n }\n\n for (const i of init) i();\n\n this.inputs = inputs;\n\n return this;\n }\n\n add(I: State, explicit = false) {\n const { cleanup, provide } = this;\n const root = this === Context.root;\n const TT: State.Extends[] = [];\n\n function conflict(T: State.Extends) {\n const entries = provide.get(T);\n if (entries)\n for (const entry of entries)\n if (!entry[1] && entry[0] !== I)\n return entries.delete(entry);\n }\n\n for (\n let T = I.constructor as State.Extends;\n T !== State;\n T = Object.getPrototypeOf(T)\n )\n if (explicit || !root || !conflict(T)) TT.push(T);\n\n const expects = new Map<Expect, () => void>();\n const onDone = new Set<() => void>();\n\n for (const T of TT) onDone.add(this.register(T, [I, explicit]));\n\n function queue(ctx: Context, downstream: boolean) {\n let found = false;\n for (const T of TT) {\n const list = ctx.consume.get(T);\n if (list !== undefined) found = true;\n if (list)\n for (const [cb, filter] of list)\n if (filter === downstream || filter == null)\n expects.set(cb, () => {\n const r = cb(I, downstream);\n if (r) onDone.add(r);\n });\n }\n return found;\n }\n\n for (let ctx: Context | undefined = this; ctx; ctx = ctx.parent) queue(ctx, true);\n this.traverse((ctx) => queue(ctx, false));\n\n if (!LOOKUP.has(I)) LOOKUP.set(I, this);\n\n listener(I, () => {\n expects.forEach((f) => f());\n expects.clear();\n return null;\n });\n\n function flush() {\n onDone.forEach((r) => r());\n onDone.clear();\n }\n\n function remove() {\n cleanup.delete(remove);\n flush();\n }\n\n cleanup.set(remove, remove);\n\n return remove;\n }\n\n /**\n * Create a child context, optionally registering one or more States to it.\n *\n * @param inputs State, State class, or map of States / State classes to register.\n */\n public push(inputs?: Accept) {\n const next = new Context(this);\n if (inputs) next.set(inputs);\n return next;\n }\n\n /**\n * Remove all States from this context.\n *\n * Will also run any cleanup callbacks registered when States were added.\n */\n public pop() {\n this.inputs = {};\n this.scope.forEach((x) => x.pop());\n this.scope.clear();\n this.cleanup.forEach((cb) => cb());\n this.cleanup.clear();\n this.consume.clear();\n this.provide.clear();\n if (this.parent) {\n this.parent.scope.delete(this);\n }\n }\n}\n\nObject.defineProperty(Context.prototype, \"toString\", {\n value() {\n return `Context-${this.id}`;\n },\n});\n\nexport { Context };\n"],"mappings":"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"}
1
+ {"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["import { listener } from \"./observable\";\nimport { event, State, uid } from \"./state\";\n\nconst LOOKUP = new WeakMap<State, Context>();\nlet ROOT: Context;\n\ntype Accept<T extends State = State> =\n | T\n | State.Type<T>\n | Record<string | number, T | State.Type<T>>;\n\ntype Expect<T extends State = State> = (\n state: T,\n downstream: boolean,\n) => (() => void) | void;\n\ndeclare namespace Context {\n export { Accept, Expect };\n}\n\nclass Context {\n static get root(): Context {\n return ROOT ??= new Context();\n }\n\n /** Get the context for a State. Adapters may override to provide framework context. */\n static get(state?: State): Context {\n return state && LOOKUP.get(state.is) || Context.root;\n }\n\n public id = uid();\n public parent?: Context;\n public scope = new Set<Context>();\n public consume = new Map<\n State.Extends,\n Set<[Expect, boolean | undefined]> | null\n >();\n public provide = new Map<State.Extends, Set<[State, boolean]> | null>();\n\n protected inputs: Record<string | number, State | State.Extends> = {};\n\n private cleanup = new Map<string | number | Function, () => void>();\n\n constructor(arg?: Context | Accept) {\n if (arg instanceof Context) {\n this.parent = arg;\n arg.scope.add(this);\n } else if (arg) {\n this.set(arg);\n }\n }\n\n private traverse(accept: (ctx: Context) => boolean | void) {\n const queue = [...this.scope];\n for (const ctx of queue)\n if (accept(ctx) !== false) for (const c of ctx.scope) queue.push(c);\n }\n\n private register<T extends State>(\n type: State.Extends<T>,\n value: [Expect<T>, boolean | undefined],\n asConsumer: true,\n ): () => void;\n\n private register<T extends State>(\n type: State.Extends<T>,\n value: [T, boolean],\n asConsumer?: false,\n ): () => void;\n\n private register(type: State.Extends, value: any, asConsumer?: boolean) {\n const mode = asConsumer ? \"consume\" : \"provide\";\n let set = this[mode].get(type) as Set<any>;\n if (!set) this[mode].set(type, (set = new Set()));\n set.add(value);\n\n for (let ctx = this.parent; ctx; ctx = ctx.parent) {\n if (ctx[mode].has(type)) break;\n ctx[mode].set(type, null);\n }\n\n return () => set.delete(value);\n }\n\n /** Find specified type upstream. Throws if not found. */\n public get<T extends State>(Type: State.Extends<T>, required?: true): T;\n\n /** Find specified type upstream. Returns undefined if not found. */\n public get<T extends State>(\n Type: State.Extends<T>,\n required?: boolean,\n ): T | undefined;\n\n /** Subscribe to a type becoming available in context. */\n public get<T extends State>(\n Type: State.Extends<T>,\n callback: Expect<T>,\n downstream?: boolean,\n ): () => void;\n\n /** Lookup excluding a specific instance (used by State.prototype.get for upstream-of-self semantics). */\n public get<T extends State>(\n Type: State.Extends<T>,\n arg: boolean | Expect<T> | undefined,\n downstream: boolean | undefined,\n skip: State,\n ): T | undefined | (() => void);\n\n public get<T extends State>(\n Type: State.Extends<T>,\n arg?: boolean | Expect<T>,\n downstream?: boolean,\n skip?: State,\n ) {\n let found: T | null | undefined;\n let priority = false;\n\n for (let ctx: Context | undefined = this; ctx; ctx = ctx.parent) {\n const entries = ctx.provide.get(Type);\n if (!entries) continue;\n\n for (const [state, explicit] of entries) {\n if (state === skip || found === state) continue;\n if (!found || (explicit && !priority)) {\n found = state as T;\n priority = explicit;\n continue;\n }\n if (!priority) {\n found = null;\n break;\n }\n if (explicit)\n throw new Error(\n `Did find ${Type} in context, but multiple were defined.`,\n );\n }\n if (found !== undefined) break;\n }\n\n if (typeof arg === \"function\") {\n if (found && !downstream) arg(found, false);\n\n if (downstream !== false) {\n this.traverse((ctx) => {\n const has = ctx.provide.get(Type);\n if (has) for (const x of has) if (x[0] !== skip) arg(x[0] as T, true);\n return has !== undefined;\n });\n }\n\n return this.register(Type, [arg as Expect, downstream], true);\n }\n\n if (found) return found;\n if (found === null) return null;\n if (arg !== false) throw new Error(`Could not find ${Type} in context.`);\n }\n\n /**\n * Register one or more States to this context.\n *\n * Context will add or remove States as needed to keep with provided input.\n *\n * @param inputs State, State class, or map of States / State classes to register.\n * @param forEach Optional callback to run for each State registered. Receives\n * whether this context created the State - and so will destroy it - as\n * opposed to it having been provided already active. May return a callback\n * to run before that entry is dropped.\n */\n public set<T extends State>(\n inputs: Accept<T>,\n forEach?: (state: T, owned: boolean) => (() => void) | void,\n ) {\n const init = new Set<() => void>();\n const { cleanup } = this;\n\n if (typeof inputs == \"function\" || inputs instanceof State)\n inputs = { [0]: inputs };\n\n for (const K of Object.keys({ ...this.inputs, ...inputs })) {\n const V = inputs[K];\n const E = this.inputs[K];\n\n if (E === V) continue;\n\n if (E) {\n cleanup.get(K)?.();\n cleanup.delete(K);\n }\n\n if (!V) continue;\n\n // a class is constructed here - and so destroyed here; an instance is\n // adopted as-is and outlives this context\n const owned = State.is(V);\n\n if (!(owned || V instanceof State)) {\n const as = K == \"0\" || K == String(V) ? V : `${V} (as '${K}')`;\n throw new Error(\n `Context can only include an instance or class of State but got ${as}.`,\n );\n }\n\n const state = owned ? new (V as State.Type)() : V.is;\n const remove = this.add(state, true);\n\n init.add(() => {\n event(state);\n const dispose = forEach?.(state as T, owned);\n cleanup.set(K, () => {\n if (typeof dispose == \"function\") dispose();\n remove();\n if (owned) event(state, null);\n });\n });\n }\n\n for (const i of init) i();\n\n this.inputs = inputs;\n\n return this;\n }\n\n add(I: State, explicit = false) {\n const { cleanup, provide } = this;\n const root = this === Context.root;\n const TT: State.Extends[] = [];\n\n function conflict(T: State.Extends) {\n const entries = provide.get(T);\n if (entries)\n for (const entry of entries)\n if (!entry[1] && entry[0] !== I)\n return entries.delete(entry);\n }\n\n for (\n let T = I.constructor as State.Extends;\n T !== State;\n T = Object.getPrototypeOf(T)\n )\n if (explicit || !root || !conflict(T)) TT.push(T);\n\n const expects = new Map<Expect, () => void>();\n const onDone = new Set<() => void>();\n\n for (const T of TT) onDone.add(this.register(T, [I, explicit]));\n\n function queue(ctx: Context, downstream: boolean) {\n let found = false;\n for (const T of TT) {\n const list = ctx.consume.get(T);\n if (list !== undefined) found = true;\n if (list)\n for (const [cb, filter] of list)\n if (filter === downstream || filter == null)\n expects.set(cb, () => {\n const r = cb(I, downstream);\n if (r) onDone.add(r);\n });\n }\n return found;\n }\n\n for (let ctx: Context | undefined = this; ctx; ctx = ctx.parent) queue(ctx, true);\n this.traverse((ctx) => queue(ctx, false));\n\n if (!LOOKUP.has(I)) LOOKUP.set(I, this);\n\n listener(I, () => {\n expects.forEach((f) => f());\n expects.clear();\n return null;\n });\n\n function flush() {\n onDone.forEach((r) => r());\n onDone.clear();\n }\n\n function remove() {\n cleanup.delete(remove);\n flush();\n }\n\n cleanup.set(remove, remove);\n\n return remove;\n }\n\n /**\n * Create a child context, optionally registering one or more States to it.\n *\n * @param inputs State, State class, or map of States / State classes to register.\n */\n public push(inputs?: Accept) {\n const next = new Context(this);\n if (inputs) next.set(inputs);\n return next;\n }\n\n /**\n * Remove all States from this context.\n *\n * Will also run any cleanup callbacks registered when States were added.\n */\n public pop() {\n this.inputs = {};\n this.scope.forEach((x) => x.pop());\n this.scope.clear();\n this.cleanup.forEach((cb) => cb());\n this.cleanup.clear();\n this.consume.clear();\n this.provide.clear();\n if (this.parent) {\n this.parent.scope.delete(this);\n }\n }\n}\n\nObject.defineProperty(Context.prototype, \"toString\", {\n value() {\n return `Context-${this.id}`;\n },\n});\n\nexport { Context };\n"],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { State } from "../state.js";
2
+
3
+ //#region src/field/has.d.ts
4
+ type Predicate<T> = (value: T, index: number, self: unknown) => boolean;
5
+ type Mapper<T, U> = (value: T, index: number, self: unknown) => U;
6
+ declare namespace has {
7
+ export { Predicate, Mapper, List, Pool };
8
+ }
9
+ declare function has<T>(initial?: Iterable<T> | false | null): List<T>;
10
+ declare function has<T extends State>(Type: new (...args: State.Args<T>) => T): Pool<T, State.Args<T>>;
11
+ declare function has<T, A extends unknown[]>(make: (...args: A) => T): Pool<T, A>;
12
+ declare class List<T> {
13
+ constructor(initial?: Iterable<T> | false | null);
14
+ get size(): number;
15
+ get(): State.Export<T>[];
16
+ get(index: number): T | undefined;
17
+ get(start: number, end: number): T[];
18
+ get(predicate: Predicate<T>): T | undefined;
19
+ set(index: number, value: T): void;
20
+ put(index: number, ...values: T[]): void;
21
+ push(...values: T[]): number;
22
+ pop(): T | undefined;
23
+ pop(index: number): T | undefined;
24
+ pop(index: number, count: number): T[];
25
+ clear(): void;
26
+ [Symbol.iterator](): IterableIterator<T>;
27
+ map<U>(fn: Mapper<T, U>): U[];
28
+ map<U, X extends U>(fn: Mapper<T, U>, ignore: X): Exclude<U, X>[];
29
+ filter(fn: Predicate<T>): T[];
30
+ any(fn: Predicate<T>): boolean;
31
+ all(fn: Predicate<T>): boolean;
32
+ }
33
+ declare class Pool<T, A extends unknown[] = unknown[]> {
34
+ constructor(make: Function);
35
+ get size(): number;
36
+ add(...args: A): T;
37
+ get(): State.Export<T>[];
38
+ get(predicate: Predicate<T>): T | undefined;
39
+ has(value: T): boolean;
40
+ delete(value: T): boolean;
41
+ clear(): void;
42
+ [Symbol.iterator](): IterableIterator<T>;
43
+ map<U>(fn: Mapper<T, U>): U[];
44
+ map<U, X extends U>(fn: Mapper<T, U>, ignore: X): Exclude<U, X>[];
45
+ filter(fn: Predicate<T>): T[];
46
+ any(fn: Predicate<T>): boolean;
47
+ all(fn: Predicate<T>): boolean;
48
+ }
49
+ //#endregion
50
+ export { has };
51
+ //# sourceMappingURL=has.d.ts.map
@@ -0,0 +1,233 @@
1
+ import { event, listener, touch } from "../observable.js";
2
+ import { State, parent } from "../state.js";
3
+ import { Context } from "../context.js";
4
+ import { def } from "./def.js";
5
+
6
+ //#region src/field/has.ts
7
+ const LENGTH = Symbol("length");
8
+ const SHAPE = Symbol("shape");
9
+ const ITEMS = /* @__PURE__ */ new WeakMap();
10
+ const MEMBERS = /* @__PURE__ */ new WeakMap();
11
+ const MAKE = /* @__PURE__ */ new WeakMap();
12
+ const OWNED = /* @__PURE__ */ new WeakMap();
13
+ function has(arg) {
14
+ return def((_key, subject) => {
15
+ const value = typeof arg == "function" ? new Pool(arg) : new List(arg);
16
+ parent(value, subject);
17
+ listener(subject, () => value.clear(), null);
18
+ return {
19
+ set: false,
20
+ value
21
+ };
22
+ });
23
+ }
24
+ var List = class {
25
+ constructor(initial) {
26
+ ITEMS.set(this, initial ? Array.from(initial) : []);
27
+ OWNED.set(this, /* @__PURE__ */ new Map());
28
+ event(this);
29
+ }
30
+ get size() {
31
+ return touch(this, LENGTH, items(this).length);
32
+ }
33
+ get(arg, second) {
34
+ const values = items(this);
35
+ if (arg === void 0) return values.map(exportValue);
36
+ if (typeof arg === "function") return findOf(this, arg);
37
+ if (typeof second !== "number") {
38
+ const at = arg < 0 ? values.length + arg : arg;
39
+ return at < 0 || at >= values.length ? touch(this, LENGTH) : touch(this, at, values[at]);
40
+ }
41
+ touch(this, LENGTH);
42
+ const start = arg < 0 ? Math.max(values.length + arg, 0) : arg;
43
+ return values.slice(start, Math.min(second, values.length)).map((v, i) => touch(this, start + i, v));
44
+ }
45
+ set(index, value) {
46
+ const target = source(this);
47
+ const values = ITEMS.get(target);
48
+ const at = index < 0 ? values.length + index : index;
49
+ if (at < 0 || at >= values.length || values[at] === value) return;
50
+ values[at] = value;
51
+ event(target, at);
52
+ }
53
+ put(index, ...values) {
54
+ if (!values.length) return;
55
+ const target = source(this);
56
+ const arr = ITEMS.get(target);
57
+ const at = index < 0 ? Math.max(arr.length + index, 0) : Math.min(index, arr.length);
58
+ arr.splice(at, 0, ...values);
59
+ for (let i = at; i < arr.length; i++) event(target, i);
60
+ event(target, LENGTH);
61
+ }
62
+ push(...values) {
63
+ this.put(items(this).length, ...values);
64
+ return items(this).length;
65
+ }
66
+ pop(index = -1, count) {
67
+ const target = source(this);
68
+ const values = ITEMS.get(target);
69
+ const before = values.length;
70
+ const start = index < 0 ? Math.max(before + index, 0) : Math.min(index, before);
71
+ const removed = values.splice(start, count ?? 1);
72
+ if (removed.length) {
73
+ const end = Math.max(before, values.length);
74
+ for (let i = start; i < end; i++) event(target, i);
75
+ event(target, LENGTH);
76
+ }
77
+ return count === void 0 ? removed[0] : removed;
78
+ }
79
+ clear() {
80
+ this.pop(0, items(this).length);
81
+ }
82
+ *[Symbol.iterator]() {
83
+ const values = items(this);
84
+ touch(this, LENGTH);
85
+ for (let i = 0; i < values.length; i++) yield touch(this, i, values[i]);
86
+ }
87
+ map(fn, ignore) {
88
+ return mapOf(this, fn, arguments.length > 1, ignore);
89
+ }
90
+ filter(fn) {
91
+ return filterOf(this, fn);
92
+ }
93
+ any(fn) {
94
+ return anyOf(this, fn);
95
+ }
96
+ all(fn) {
97
+ return !anyOf(this, (v, i, self) => !fn(v, i, self));
98
+ }
99
+ };
100
+ var Pool = class {
101
+ constructor(make) {
102
+ MEMBERS.set(this, /* @__PURE__ */ new Set());
103
+ MAKE.set(this, make);
104
+ OWNED.set(this, /* @__PURE__ */ new Map());
105
+ event(this);
106
+ }
107
+ get size() {
108
+ return touch(this, SHAPE, members(this).size);
109
+ }
110
+ add(...args) {
111
+ const target = source(this);
112
+ const values = MEMBERS.get(target);
113
+ const make = MAKE.get(target);
114
+ const value = State.is(make) ? new make(...args) : make(...args);
115
+ if (!values.has(value)) {
116
+ values.add(value);
117
+ adopt(target, value, value);
118
+ event(target, value);
119
+ event(target, SHAPE);
120
+ }
121
+ return value;
122
+ }
123
+ get(predicate) {
124
+ return predicate ? findOf(this, predicate) : Array.from(members(this), exportValue);
125
+ }
126
+ has(value) {
127
+ return touch(this, value, members(this).has(value));
128
+ }
129
+ delete(value) {
130
+ const target = source(this);
131
+ const values = MEMBERS.get(target);
132
+ if (!values.has(value)) return false;
133
+ release(target, value);
134
+ values.delete(value);
135
+ event(target, value);
136
+ event(target, SHAPE);
137
+ return true;
138
+ }
139
+ clear() {
140
+ const target = source(this);
141
+ const values = MEMBERS.get(target);
142
+ if (!values.size) return;
143
+ const removed = Array.from(values);
144
+ for (const value of removed) release(target, value);
145
+ values.clear();
146
+ for (const value of removed) event(target, value);
147
+ event(target, SHAPE);
148
+ }
149
+ *[Symbol.iterator]() {
150
+ const values = members(this);
151
+ touch(this, SHAPE);
152
+ for (const value of values) yield touch(this, value, value);
153
+ }
154
+ map(fn, ignore) {
155
+ return mapOf(this, fn, arguments.length > 1, ignore);
156
+ }
157
+ filter(fn) {
158
+ return filterOf(this, fn);
159
+ }
160
+ any(fn) {
161
+ return anyOf(this, fn);
162
+ }
163
+ all(fn) {
164
+ return !anyOf(this, (v, i, self) => !fn(v, i, self));
165
+ }
166
+ };
167
+ function source(from) {
168
+ return OWNED.has(from) ? from : Object.getPrototypeOf(from);
169
+ }
170
+ function adopt(target, key, value) {
171
+ if (!(value instanceof State)) return;
172
+ const owner = parent(target);
173
+ const fresh = parent(value, owner ?? null);
174
+ const evict = listener(value, () => void target.delete(key), null);
175
+ let detach;
176
+ if (fresh && owner) detach = Context.get(owner).add(value);
177
+ OWNED.get(target).set(key, () => {
178
+ evict();
179
+ if (detach) detach();
180
+ if (fresh) value.set(null);
181
+ });
182
+ if (fresh) event(value);
183
+ }
184
+ function release(target, key) {
185
+ const owned = OWNED.get(target);
186
+ const free = owned.get(key);
187
+ if (free) {
188
+ owned.delete(key);
189
+ free();
190
+ }
191
+ }
192
+ function exportValue(value) {
193
+ return value && typeof value === "object" && typeof value.get === "function" ? value.get() : value;
194
+ }
195
+ function items(from) {
196
+ return ITEMS.get(source(from));
197
+ }
198
+ function members(from) {
199
+ return MEMBERS.get(source(from));
200
+ }
201
+ function findOf(self, fn) {
202
+ let i = 0;
203
+ for (const value of self) if (fn(value, i++, self)) return value;
204
+ }
205
+ function anyOf(self, fn) {
206
+ let i = 0;
207
+ for (const value of self) if (fn(value, i++, self)) return true;
208
+ return false;
209
+ }
210
+ function mapOf(self, fn, skip, ignore) {
211
+ const out = [];
212
+ let i = 0;
213
+ for (const value of self) {
214
+ const result = fn(value, i++, self);
215
+ if (skip && result === ignore) continue;
216
+ out.push(result);
217
+ }
218
+ return out;
219
+ }
220
+ function filterOf(self, fn) {
221
+ const out = [];
222
+ let i = 0;
223
+ for (const value of self) if (fn(value, i++, self)) out.push(value);
224
+ return out;
225
+ }
226
+ Object.assign(has, {
227
+ List,
228
+ Pool
229
+ });
230
+
231
+ //#endregion
232
+ export { has };
233
+ //# sourceMappingURL=has.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has.js","names":[],"sources":["../../src/field/has.ts"],"sourcesContent":["import { Context } from '../context';\nimport { event, listener, touch } from '../observable';\nimport { State, parent } from '../state';\nimport { def } from './def';\n\nconst LENGTH = Symbol('length');\nconst SHAPE = Symbol('shape');\n\nconst ITEMS = new WeakMap<object, unknown[]>();\nconst MEMBERS = new WeakMap<object, Set<unknown>>();\nconst MAKE = new WeakMap<object, Function>();\nconst OWNED = new WeakMap<object, Map<unknown, () => void>>();\n\ntype Predicate<T> = (value: T, index: number, self: unknown) => boolean;\ntype Mapper<T, U> = (value: T, index: number, self: unknown) => U;\n\ndeclare namespace has {\n export { Predicate, Mapper, List, Pool }\n}\n\nfunction has<T>(initial?: Iterable<T> | false | null): List<T>;\n\nfunction has<T extends State>(\n Type: new (...args: State.Args<T>) => T\n): Pool<T, State.Args<T>>;\n\nfunction has<T, A extends unknown[]>(\n make: (...args: A) => T\n): Pool<T, A>;\n\nfunction has(arg?: Iterable<unknown> | Function | false | null): unknown {\n return def((_key, subject) => {\n const value = typeof arg == 'function' ? new Pool(arg) : new List(arg);\n\n parent(value, subject);\n listener(subject, () => value.clear(), null);\n\n return { set: false, value };\n });\n}\n\nclass List<T> {\n constructor(initial?: Iterable<T> | false | null) {\n ITEMS.set(this, initial ? Array.from(initial) : []);\n OWNED.set(this, new Map());\n\n event(this);\n }\n\n get size(): number {\n return touch(this, LENGTH, items<T>(this).length);\n }\n\n get(): State.Export<T>[];\n get(index: number): T | undefined;\n get(start: number, end: number): T[];\n get(predicate: Predicate<T>): T | undefined;\n get(arg?: number | Predicate<T>, second?: number): any {\n const values = items<T>(this);\n\n if (arg === undefined) return values.map(exportValue);\n\n if (typeof arg === 'function') return findOf(this, arg);\n\n if (typeof second !== 'number') {\n const at = arg < 0 ? values.length + arg : arg;\n\n return at < 0 || at >= values.length\n ? touch(this, LENGTH)\n : touch(this, at, values[at]);\n }\n\n touch(this, LENGTH);\n\n const start = arg < 0 ? Math.max(values.length + arg, 0) : arg;\n\n return values\n .slice(start, Math.min(second, values.length))\n .map((v, i) => touch(this, start + i, v));\n }\n\n set(index: number, value: T) {\n const target = source(this);\n const values = ITEMS.get(target)! as T[];\n const at = index < 0 ? values.length + index : index;\n\n if (at < 0 || at >= values.length || values[at] === value) return;\n\n values[at] = value;\n event(target, at);\n }\n\n put(index: number, ...values: T[]) {\n if (!values.length) return;\n\n const target = source(this);\n const arr = ITEMS.get(target)! as T[];\n const at =\n index < 0\n ? Math.max(arr.length + index, 0)\n : Math.min(index, arr.length);\n\n arr.splice(at, 0, ...values);\n\n for (let i = at; i < arr.length; i++) event(target, i);\n event(target, LENGTH);\n }\n\n push(...values: T[]): number {\n this.put(items(this).length, ...values);\n return items(this).length;\n }\n\n pop(): T | undefined;\n pop(index: number): T | undefined;\n pop(index: number, count: number): T[];\n pop(index = -1, count?: number): T | T[] | undefined {\n const target = source(this);\n const values = ITEMS.get(target)! as T[];\n const before = values.length;\n const start =\n index < 0 ? Math.max(before + index, 0) : Math.min(index, before);\n const removed = values.splice(start, count ?? 1);\n\n if (removed.length) {\n const end = Math.max(before, values.length);\n for (let i = start; i < end; i++) event(target, i);\n event(target, LENGTH);\n }\n\n return count === undefined ? removed[0] : removed;\n }\n\n clear() {\n this.pop(0, items(this).length);\n }\n\n *[Symbol.iterator](): IterableIterator<T> {\n const values = items<T>(this);\n\n touch(this, LENGTH);\n\n for (let i = 0; i < values.length; i++) yield touch(this, i, values[i]);\n }\n\n map<U>(fn: Mapper<T, U>): U[];\n map<U, X extends U>(fn: Mapper<T, U>, ignore: X): Exclude<U, X>[];\n map<U>(fn: Mapper<T, U>, ignore?: U): U[] {\n return mapOf(this, fn, arguments.length > 1, ignore);\n }\n\n filter(fn: Predicate<T>): T[] {\n return filterOf(this, fn);\n }\n\n any(fn: Predicate<T>): boolean {\n return anyOf(this, fn);\n }\n\n all(fn: Predicate<T>): boolean {\n return !anyOf(this, (v, i, self) => !fn(v, i, self));\n }\n}\n\nclass Pool<T, A extends unknown[] = unknown[]> {\n constructor(make: Function) {\n MEMBERS.set(this, new Set());\n MAKE.set(this, make);\n OWNED.set(this, new Map());\n\n event(this);\n }\n\n get size(): number {\n return touch(this, SHAPE, members<T>(this).size);\n }\n\n add(...args: A): T {\n const target = source(this);\n const values = MEMBERS.get(target)! as Set<T>;\n const make = MAKE.get(target)!;\n\n const value = (\n State.is(make)\n ? new (make as State.Type)(...(args as State.Args))\n : make(...args)\n ) as T;\n\n if (!values.has(value)) {\n values.add(value);\n adopt(target, value, value);\n\n event(target, value as never);\n event(target, SHAPE);\n }\n\n return value;\n }\n\n get(): State.Export<T>[];\n get(predicate: Predicate<T>): T | undefined;\n get(predicate?: Predicate<T>): any {\n return predicate\n ? findOf(this, predicate)\n : Array.from(members<T>(this), exportValue);\n }\n\n has(value: T): boolean {\n return touch(this, value, members<T>(this).has(value));\n }\n\n delete(value: T): boolean {\n const target = source(this);\n const values = MEMBERS.get(target)! as Set<T>;\n\n if (!values.has(value)) return false;\n\n release(target, value);\n values.delete(value);\n\n event(target, value as never);\n event(target, SHAPE);\n\n return true;\n }\n\n clear() {\n const target = source(this);\n const values = MEMBERS.get(target)! as Set<T>;\n\n if (!values.size) return;\n\n const removed = Array.from(values);\n\n for (const value of removed) release(target, value);\n\n values.clear();\n\n for (const value of removed) event(target, value as never);\n event(target, SHAPE);\n }\n\n *[Symbol.iterator](): IterableIterator<T> {\n const values = members<T>(this);\n\n touch(this, SHAPE);\n\n for (const value of values) yield touch(this, value, value);\n }\n\n map<U>(fn: Mapper<T, U>): U[];\n map<U, X extends U>(fn: Mapper<T, U>, ignore: X): Exclude<U, X>[];\n map<U>(fn: Mapper<T, U>, ignore?: U): U[] {\n return mapOf(this, fn, arguments.length > 1, ignore);\n }\n\n filter(fn: Predicate<T>): T[] {\n return filterOf(this, fn);\n }\n\n any(fn: Predicate<T>): boolean {\n return anyOf(this, fn);\n }\n\n all(fn: Predicate<T>): boolean {\n return !anyOf(this, (v, i, self) => !fn(v, i, self));\n }\n}\n\nfunction source<T extends object>(from: T): T {\n return OWNED.has(from) ? from : Object.getPrototypeOf(from);\n}\n\nfunction adopt(\n target: { delete(key: never): boolean },\n key: unknown,\n value: unknown\n) {\n if (!(value instanceof State)) return;\n\n const owner = parent(target as object);\n const fresh = parent(value, owner ?? null);\n const evict = listener(value, () => void target.delete(key as never), null);\n\n let detach: (() => void) | undefined;\n\n if (fresh && owner)\n detach = Context.get(owner).add(value);\n\n OWNED.get(target)!.set(key, () => {\n evict();\n if (detach) detach();\n if (fresh) value.set(null);\n });\n\n if (fresh) event(value);\n}\n\nfunction release(target: object, key: unknown) {\n const owned = OWNED.get(target)!;\n const free = owned.get(key);\n\n if (free) {\n owned.delete(key);\n free();\n }\n}\n\nfunction exportValue(value: any) {\n return value && typeof value === 'object' && typeof value.get === 'function'\n ? value.get()\n : value;\n}\n\nfunction items<T>(from: object): T[] {\n return ITEMS.get(source(from)) as T[];\n}\n\nfunction members<T>(from: object): Set<T> {\n return MEMBERS.get(source(from)) as Set<T>;\n}\n\nfunction findOf<T>(\n self: Iterable<T>,\n fn: Predicate<T>\n): T | undefined {\n let i = 0;\n\n for (const value of self)\n if (fn(value, i++, self)) return value;\n}\n\nfunction anyOf<T>(self: Iterable<T>, fn: Predicate<T>): boolean {\n let i = 0;\n\n for (const value of self)\n if (fn(value, i++, self)) return true;\n\n return false;\n}\n\nfunction mapOf<T, U>(\n self: Iterable<T>,\n fn: Mapper<T, U>,\n skip: boolean,\n ignore?: U\n): U[] {\n const out: U[] = [];\n let i = 0;\n\n for (const value of self) {\n const result = fn(value, i++, self);\n if (skip && result === ignore) continue;\n out.push(result);\n }\n\n return out;\n}\n\nfunction filterOf<T>(self: Iterable<T>, fn: Predicate<T>): T[] {\n const out: T[] = [];\n let i = 0;\n\n for (const value of self) if (fn(value, i++, self)) out.push(value);\n\n return out;\n}\n\nObject.assign(has, { List, Pool });\n\nexport { has };\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import { State } from "../state.js";
2
+
3
+ //#region src/field/map.d.ts
4
+ declare namespace map {
5
+ export interface Create<A extends [unknown, ...unknown[]], V> extends Managed<A[0], V> {
6
+ set(...args: A): this;
7
+ }
8
+ export interface Insert<K, V> extends Managed<K, V> {
9
+ set(key: K, value: V): this;
10
+ }
11
+ export { Managed };
12
+ }
13
+ declare function map<K, V>(entries?: Iterable<readonly [K, V]> | false | null): map.Insert<K, V>;
14
+ declare function map<A extends [unknown, ...unknown[]], V>(make: (...args: A) => V): map.Create<A, V>;
15
+ declare class Managed<K, V> extends Map<K, V> {
16
+ constructor(arg?: Iterable<readonly [K, V]> | Function | false | null);
17
+ get size(): number;
18
+ get(): ReadonlyMap<K, State.Export<V>>;
19
+ get(key: K): V | undefined;
20
+ has(key: K): boolean;
21
+ set(key: K, ...rest: unknown[]): this;
22
+ delete(key: K): boolean;
23
+ clear(): void;
24
+ entries(): MapIterator<[K, V]>;
25
+ entries<R>(fn: (entry: [K, V]) => R): Iterable<R>;
26
+ keys(): MapIterator<K>;
27
+ keys<R>(fn: (key: K) => R): Iterable<R>;
28
+ values(): MapIterator<V>;
29
+ values<R>(fn: (value: V, key: K) => R): Iterable<R>;
30
+ forEach(callbackfn: (value: V, key: K, map: this) => void, thisArg?: unknown): void;
31
+ [Symbol.iterator](): MapIterator<[K, V]>;
32
+ }
33
+ //#endregion
34
+ export { map };
35
+ //# sourceMappingURL=map.d.ts.map
@@ -0,0 +1,144 @@
1
+ import { event, listener, touch } from "../observable.js";
2
+ import { State, parent } from "../state.js";
3
+ import { Context } from "../context.js";
4
+ import { def } from "./def.js";
5
+
6
+ //#region src/field/map.ts
7
+ const SHAPE = Symbol("shape");
8
+ const KEYED = (_key, value) => value;
9
+ const MAKE = /* @__PURE__ */ new WeakMap();
10
+ const OWNED = /* @__PURE__ */ new WeakMap();
11
+ const MAP = Map.prototype;
12
+ const MAP_SIZE = Object.getOwnPropertyDescriptor(MAP, "size").get;
13
+ function map(arg) {
14
+ return def((_key, subject) => {
15
+ const value = new Managed(typeof arg == "function" && arg);
16
+ parent(value, subject);
17
+ listener(subject, () => value.clear(), null);
18
+ if (arg && typeof arg == "object") for (const [key, entry] of arg) value.set(key, entry);
19
+ return {
20
+ set: false,
21
+ value
22
+ };
23
+ });
24
+ }
25
+ var Managed = class extends Map {
26
+ constructor(arg) {
27
+ super();
28
+ MAKE.set(this, typeof arg == "function" ? arg : KEYED);
29
+ OWNED.set(this, /* @__PURE__ */ new Map());
30
+ event(this);
31
+ if (arg && typeof arg != "function") for (const [key, value] of arg) store(this, key, value);
32
+ }
33
+ get size() {
34
+ return touch(this, SHAPE, MAP_SIZE.call(source(this)));
35
+ }
36
+ get(key) {
37
+ const target = source(this);
38
+ return arguments.length ? touch(this, key, super.get.call(target, key)) : new Map(Array.from(MAP.entries.call(target), ([key, value]) => [key, value && typeof value === "object" && typeof value.get === "function" ? value.get() : value]));
39
+ }
40
+ has(key) {
41
+ return touch(this, key, MAP.has.call(source(this), key));
42
+ }
43
+ set(key, ...rest) {
44
+ const target = source(this);
45
+ store(target, key, MAKE.get(target)(key, ...rest));
46
+ return this;
47
+ }
48
+ delete(key) {
49
+ const target = source(this);
50
+ if (!MAP.has.call(target, key)) return false;
51
+ release(target, key);
52
+ MAP.delete.call(target, key);
53
+ event(target, key);
54
+ event(target, SHAPE);
55
+ return true;
56
+ }
57
+ clear() {
58
+ const target = source(this);
59
+ const keys = Array.from(MAP.keys.call(target));
60
+ if (!keys.length) return;
61
+ for (const key of keys) release(target, key);
62
+ MAP.clear.call(target);
63
+ for (const key of keys) event(target, key);
64
+ event(target, SHAPE);
65
+ }
66
+ entries(fn) {
67
+ const self = this;
68
+ function* iterate() {
69
+ const target = source(self);
70
+ touch(self, SHAPE);
71
+ for (const [key, value] of MAP.entries.call(target)) yield [key, touch(self, key, value)];
72
+ }
73
+ return fn ? transform(iterate, fn) : iterate();
74
+ }
75
+ keys(fn) {
76
+ const self = this;
77
+ function* iterate() {
78
+ touch(self, SHAPE);
79
+ yield* MAP.keys.call(source(self));
80
+ }
81
+ return fn ? transform(iterate, fn) : iterate();
82
+ }
83
+ values(fn) {
84
+ if (fn) return this.entries(([key, value]) => fn(value, key));
85
+ const self = this;
86
+ return (function* () {
87
+ for (const [, value] of self.entries()) yield value;
88
+ })();
89
+ }
90
+ forEach(callbackfn, thisArg) {
91
+ for (const [key, value] of this.entries()) callbackfn.call(thisArg, value, key, this);
92
+ }
93
+ [Symbol.iterator]() {
94
+ return this.entries();
95
+ }
96
+ };
97
+ Object.assign(map, { Managed });
98
+ function transform(iterate, fn) {
99
+ return { *[Symbol.iterator]() {
100
+ for (const item of iterate()) try {
101
+ yield fn(item);
102
+ } catch (err) {
103
+ if (err !== false) throw err;
104
+ }
105
+ } };
106
+ }
107
+ function store(target, key, value) {
108
+ const exists = MAP.has.call(target, key);
109
+ if (exists) {
110
+ if (MAP.get.call(target, key) === value) return;
111
+ release(target, key);
112
+ }
113
+ MAP.set.call(target, key, value);
114
+ if (value instanceof State) {
115
+ const owner = parent(target);
116
+ const fresh = parent(value, owner ?? null);
117
+ const evict = listener(value, () => void target.delete(key), null);
118
+ let detach;
119
+ if (fresh && owner) detach = Context.get(owner).add(value);
120
+ OWNED.get(target).set(key, () => {
121
+ evict();
122
+ if (detach) detach();
123
+ if (fresh) value.set(null);
124
+ });
125
+ if (fresh) event(value);
126
+ }
127
+ event(target, key);
128
+ if (!exists) event(target, SHAPE);
129
+ }
130
+ function release(target, key) {
131
+ const owned = OWNED.get(target);
132
+ const free = owned.get(key);
133
+ if (free) {
134
+ owned.delete(key);
135
+ free();
136
+ }
137
+ }
138
+ function source(from) {
139
+ return OWNED.has(from) ? from : Object.getPrototypeOf(from);
140
+ }
141
+
142
+ //#endregion
143
+ export { map };
144
+ //# sourceMappingURL=map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.js","names":[],"sources":["../../src/field/map.ts"],"sourcesContent":["import { Context } from '../context';\nimport { event, listener, touch } from '../observable';\nimport { State, parent } from '../state';\nimport { def } from './def';\n\nconst SHAPE = Symbol('shape');\nconst KEYED = (_key: unknown, value: unknown) => value;\n\nconst MAKE = new WeakMap<object, Function>();\nconst OWNED = new WeakMap<object, Map<unknown, () => void>>();\n\nconst MAP = Map.prototype;\nconst MAP_SIZE = Object.getOwnPropertyDescriptor(MAP, 'size')!.get!;\n\ndeclare namespace map {\n export interface Create<A extends [unknown, ...unknown[]], V> extends Managed<A[0], V> {\n set(...args: A): this;\n }\n\n export interface Insert<K, V> extends Managed<K, V> {\n set(key: K, value: V): this;\n }\n\n export { Managed };\n}\n\nfunction map<K, V>(\n entries?: Iterable<readonly [K, V]> | false | null\n): map.Insert<K, V>;\n\nfunction map<A extends [unknown, ...unknown[]], V>(\n make: (...args: A) => V\n): map.Create<A, V>;\n\nfunction map(\n arg?: Iterable<readonly [unknown, unknown]> | Function | false | null\n): unknown {\n return def((_key, subject) => {\n const value = new Managed(typeof arg == 'function' && arg);\n\n parent(value, subject);\n listener(subject, () => value.clear(), null);\n\n if (arg && typeof arg == 'object')\n for (const [key, entry] of arg) value.set(key, entry);\n\n return { set: false, value };\n });\n}\n\nclass Managed<K, V> extends Map<K, V> {\n constructor(arg?: Iterable<readonly [K, V]> | Function | false | null) {\n super();\n\n MAKE.set(this, typeof arg == 'function' ? arg : KEYED);\n OWNED.set(this, new Map());\n\n event(this);\n\n if (arg && typeof arg != 'function')\n for (const [key, value] of arg) store(this, key, value);\n }\n\n get size(): number {\n return touch(this, SHAPE, MAP_SIZE.call(source(this)));\n }\n\n get(): ReadonlyMap<K, State.Export<V>>;\n get(key: K): V | undefined;\n get(key?: K): unknown {\n const target = source(this);\n\n return arguments.length\n ? touch(this, key, super.get.call(target, key as K)) \n : new Map(\n Array.from(MAP.entries.call(target), ([key, value]) => [\n key,\n value && typeof value === 'object' && typeof value.get === 'function'\n ? value.get()\n : value\n ])\n );\n }\n\n has(key: K): boolean {\n return touch(this, key, MAP.has.call(source(this), key));\n }\n\n set(key: K, ...rest: unknown[]): this {\n const target = source(this);\n const value = MAKE.get(target)!(key, ...rest) as V;\n\n store(target, key, value);\n return this;\n }\n\n delete(key: K) {\n const target = source(this);\n\n if (!MAP.has.call(target, key)) return false;\n\n release(target, key);\n MAP.delete.call(target, key);\n\n event(target, key as never);\n event(target, SHAPE);\n\n return true;\n }\n\n clear() {\n const target = source(this);\n const keys = Array.from(MAP.keys.call(target));\n\n if (!keys.length) return;\n\n for (const key of keys) release(target, key);\n\n MAP.clear.call(target);\n\n for (const key of keys) event(target, key as never);\n event(target, SHAPE);\n }\n\n entries(): MapIterator<[K, V]>;\n entries<R>(fn: (entry: [K, V]) => R): Iterable<R>;\n entries(fn?: (entry: [K, V]) => unknown): any {\n const self = this;\n function* iterate() {\n const target = source(self);\n\n touch(self, SHAPE);\n\n for (const [key, value] of MAP.entries.call(target))\n yield [key, touch(self, key, value)] as [K, V];\n }\n\n return fn ? transform(iterate, fn) : iterate();\n }\n\n keys(): MapIterator<K>;\n keys<R>(fn: (key: K) => R): Iterable<R>;\n keys(fn?: (key: K) => unknown): any {\n const self = this;\n function* iterate() {\n touch(self, SHAPE);\n yield* MAP.keys.call(source(self)) as IterableIterator<K>;\n }\n\n return fn ? transform(iterate, fn) : iterate();\n }\n\n values(): MapIterator<V>;\n values<R>(fn: (value: V, key: K) => R): Iterable<R>;\n values(fn?: (value: V, key: K) => unknown): any {\n if (fn)\n return this.entries(([key, value]) => fn(value, key));\n\n const self = this;\n return (function* () {\n for (const [, value] of self.entries())\n yield value;\n })();\n }\n\n forEach(\n callbackfn: (value: V, key: K, map: this) => void,\n thisArg?: unknown\n ) {\n for (const [key, value] of this.entries())\n callbackfn.call(thisArg, value, key, this);\n }\n\n [Symbol.iterator]() {\n return this.entries();\n }\n}\n\nObject.assign(map, { Managed });\n\nfunction transform<T, R>(\n iterate: () => Generator<T>,\n fn: (item: T) => R\n): Iterable<R> {\n return {\n *[Symbol.iterator]() {\n for (const item of iterate())\n try {\n yield fn(item);\n } catch (err) {\n if (err !== false) throw err;\n }\n }\n };\n}\n\nfunction store<K, V>(target: Managed<K, V>, key: K, value: V) {\n const exists = MAP.has.call(target, key);\n\n if (exists) {\n if (MAP.get.call(target, key) === value) return;\n\n release(target, key);\n }\n\n MAP.set.call(target, key, value);\n\n if (value instanceof State) {\n const owner = parent(target);\n const fresh = parent(value, owner ?? null);\n const evict = listener(value, () => void target.delete(key), null);\n\n let detach: (() => void) | undefined;\n\n if (fresh && owner)\n detach = Context.get(owner).add(value);\n\n OWNED.get(target)!.set(key, () => {\n evict();\n if (detach) detach();\n if (fresh) value.set(null);\n });\n\n if (fresh) event(value);\n }\n\n event(target, key as never);\n\n if (!exists) event(target, SHAPE);\n}\n\nfunction release(target: object, key: unknown) {\n const owned = OWNED.get(target)!;\n const free = owned.get(key);\n\n if (free) {\n owned.delete(key);\n free();\n }\n}\n\nfunction source<T extends object>(from: T): T {\n return OWNED.has(from) ? from : Object.getPrototypeOf(from);\n}\n\nexport { map };\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,7 +3,8 @@ import { State, unbind } from "./state.js";
3
3
  import { Component } from "./component.js";
4
4
  import { def } from "./field/def.js";
5
5
  import { get } from "./field/get.js";
6
- import { hot } from "./field/hot.js";
6
+ import { has } from "./field/has.js";
7
+ import { map } from "./field/map.js";
7
8
  import { ref } from "./field/ref.js";
8
9
  import { set } from "./field/set.js";
9
- export { Component, Context, State, def, State as default, get, hot, ref, set, unbind };
10
+ export { Component, Context, State, def, State as default, get, has, map, ref, set, unbind };
package/dist/index.js CHANGED
@@ -4,7 +4,8 @@ import { def } from "./field/def.js";
4
4
  import { set } from "./field/set.js";
5
5
  import { Component } from "./component.js";
6
6
  import { get } from "./field/get.js";
7
- import { hot } from "./field/hot.js";
7
+ import { has } from "./field/has.js";
8
+ import { map } from "./field/map.js";
8
9
  import { ref } from "./field/ref.js";
9
10
 
10
- export { Component, Context, State, def, State as default, get, hot, ref, set, unbind };
11
+ export { Component, Context, State, def, State as default, get, has, map, ref, set, unbind };
package/dist/state.d.ts CHANGED
@@ -11,6 +11,15 @@ declare namespace State {
11
11
  type Type<T extends State = State> = (new (...args: State.Args<T>) => T) & Omit<typeof State, never>;
12
12
  /** State constructor arguments */
13
13
  type Args<T extends State = any> = (Args<T> | Init<T> | Assign<T> | void)[];
14
+ /**
15
+ * Value of `static global`. A boolean opts a State in or out of the
16
+ * process-global root; a resolver decides at activation, receiving the
17
+ * instance and returning whether it registers - use it to make the choice
18
+ * conditional (e.g. per environment). A bare literal (`= true` / `= false`)
19
+ * seals the choice for subclasses; widen a subclass's `static global` type to
20
+ * `State.Global` to permit a resolver or a later override.
21
+ */
22
+ type Global<T extends State = any> = boolean | ((self: T) => boolean);
14
23
  /**
15
24
  * State constructor callback - runs during activation, in argument order,
16
25
  * before the `new()` lifecycle hook (so it may configure state `new()`
@@ -103,6 +112,18 @@ declare namespace State {
103
112
  type Updated<T extends State> = readonly Event<T>[] & PromiseLike<readonly Event<T>[]>;
104
113
  }
105
114
  declare abstract class State {
115
+ /**
116
+ * Whether an instance activated with no enclosing context registers itself
117
+ * to the process-global root, where `get()` can resolve it from anywhere.
118
+ * `false` (the default) keeps a context-less instance fully functional but
119
+ * private - not injectable, though it can still read declared globals. Opt in
120
+ * with `static readonly global = true`; a resolver (see {@link State.Global})
121
+ * makes the choice conditional. It must be declared per class - a subclass
122
+ * that would inherit a global without its own declaration throws on
123
+ * activation, so an accidental global (a forgotten `Provider`, an extended
124
+ * global) cannot leak into the shared root.
125
+ */
126
+ static readonly global: State.Global;
106
127
  /**
107
128
  * Loopback to instance of this state. This is useful when in a subscribed context,
108
129
  * to keep write access to `this` after a destructure. You can use it to read variables silently as well.
@@ -311,10 +332,13 @@ declare function uid(): string;
311
332
  declare function unbind<T extends Function>(fn: T): T;
312
333
  declare function unbind<T extends Function>(fn?: T): T | undefined;
313
334
  /**
314
- * Get or set a parent on given state. May only be assigned once; ignores new value.
315
- * Returns parent of child, or `undefined` if not assigned yet.
335
+ * Read or claim a parent for a given child. In read mode (one argument) returns
336
+ * the current parent, or `undefined` if never assigned. In write mode assigns
337
+ * only if unclaimed and returns whether the assignment applied - `true` means
338
+ * this call freshly claimed it, `false` means it was already parented.
316
339
  */
317
- declare function parent(child: State, value?: State | null): State | null | undefined;
340
+ declare function parent(child: object): State | null | undefined;
341
+ declare function parent(child: object, value: State | null): boolean;
318
342
  //#endregion
319
343
  export { STORE, State, access, apply, compute, event, parent, uid, unbind, update };
320
344
  //# sourceMappingURL=state.d.ts.map
package/dist/state.js CHANGED
@@ -20,6 +20,9 @@ const GETTERS = /* @__PURE__ */ new WeakMap();
20
20
  /** Stale flags for compute closures awaiting refresh on next access. */
21
21
  const STALE = /* @__PURE__ */ new WeakSet();
22
22
  var State = class State {
23
+ static {
24
+ this.global = false;
25
+ }
23
26
  constructor(...args) {
24
27
  define(this, "is", { value: this });
25
28
  init(this, args, this.new);
@@ -113,11 +116,16 @@ function init(state, ...args) {
113
116
  function observe() {
114
117
  for (const key in state) {
115
118
  const desc = Object.getOwnPropertyDescriptor(state, key);
116
- if ("value" in desc) apply(state, key, desc, true);
119
+ if ("value" in desc && desc.configurable) apply(state, key, desc, true);
117
120
  }
118
121
  }
119
122
  function register() {
120
- if (Context.get(state) === Context.root) return Context.root.add(state);
123
+ if (Context.get(state) !== Context.root) return;
124
+ const type = state.constructor;
125
+ const g = type.global;
126
+ if (!(typeof g == "function" ? g(state) : g)) return;
127
+ if (!Object.prototype.hasOwnProperty.call(type, "global")) throw new Error(`${state} would register as a global by inheritance alone - re-declare \`static global\` on ${type.name} (\`true\` to keep it, \`false\` to opt out).`);
128
+ return Context.root.add(state);
121
129
  }
122
130
  listener(state, () => {
123
131
  parent(state, null);
@@ -319,8 +327,7 @@ function child(state) {
319
327
  reset();
320
328
  if (!(value instanceof State)) return;
321
329
  const remove = ctx.add(value);
322
- if (parent(value) === void 0) {
323
- parent(value, state);
330
+ if (parent(value, state)) {
324
331
  cleanup = () => {
325
332
  cancel();
326
333
  remove();
@@ -415,13 +422,11 @@ function uid() {
415
422
  function unbind(fn) {
416
423
  return UNBIND.get(fn) || fn;
417
424
  }
418
- /**
419
- * Get or set a parent on given state. May only be assigned once; ignores new value.
420
- * Returns parent of child, or `undefined` if not assigned yet.
421
- */
422
425
  function parent(child, value) {
423
- if (arguments.length > 1 && !PARENT.has(child)) PARENT.set(child, value);
424
- return PARENT.get(child);
426
+ if (arguments.length < 2) return PARENT.get(child);
427
+ if (PARENT.has(child)) return false;
428
+ PARENT.set(child, value ?? null);
429
+ return true;
425
430
  }
426
431
 
427
432
  //#endregion
package/dist/state.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"state.js","names":[],"sources":["../src/state.ts"],"sourcesContent":["import { Context } from './context';\nimport {\n event,\n listener,\n Observer,\n observer,\n pending,\n touch,\n watch\n} from './observable';\n\nconst define = Object.defineProperty;\n\n/** Register for all active states via string identifiers (usually unique). */\nconst ID = new WeakMap<State, string>();\n\n/** Internal state assigned to states. */\nconst STORE = new WeakMap<State, Record<string | number | symbol, unknown>>();\n\n/** External lifecycle listeners for any given State class. */\nconst SETUP = new WeakMap<State.Extends, Set<State.Init<any> | State.On<any>>>();\n\n/** Parent-child relationships. */\nconst PARENT = new WeakMap<State, State | null>();\n\n/** Reference bound instance methods, or their getter, to real one. */\nconst UNBIND = new WeakMap<any, any>();\n\n/** List of methods defined by a given type. */\nconst METHODS = new WeakMap<Function, Map<string, (value: any) => void>>();\n\n/** List of reactive getters defined by a given type. */\nconst GETTERS = new WeakMap<Function, Map<string, () => unknown>>();\n\n/** Stale flags for compute closures awaiting refresh on next access. */\nconst STALE = new WeakSet<() => void>();\n\ndeclare namespace State {\n /** Any type of State, using own class constructor as its identifier. */\n type Extends<T extends State = State> = (abstract new (...args: any[]) => T) &\n typeof State;\n\n /** A State constructor which may be instanciated. */\n type Type<T extends State = State> = (new (...args: State.Args<T>) => T) &\n Omit<typeof State, never>;\n\n /** State constructor arguments */\n type Args<T extends State = any> = (Args<T> | Init<T> | Assign<T> | void)[];\n\n /**\n * State constructor callback - runs during activation, in argument order,\n * before the `new()` lifecycle hook (so it may configure state `new()`\n * will observe). Returned function will run when state is destroyed.\n */\n type Init<T extends State = State> = (\n this: T,\n thisArg: T\n ) => Promise<void> | (() => void) | Args<T> | Assign<T> | void;\n\n /**\n * Lifecycle handlers for `State.on`, keyed by when they run. A bare `Init`\n * function passed to `on` is sugar for `{ before }`.\n */\n interface On<T extends State = State> {\n /**\n * Per-class setup, run once when the class is first bootstrapped - before\n * its members are classified and bound. Receives the class, so a handler\n * may inspect or reshape the prototype first. A handler registered on a\n * base class runs for each subclass too.\n */\n type?(type: State.Extends<T>): void;\n\n /**\n * Per-instance setup, run in the `prepare` phase before `observe` and the\n * `new()` hook. Equivalent to passing a bare function to `on`. May return a\n * cleanup, constructor args, or an assign overlay.\n */\n before?(this: T, thisArg: T): void | (() => void) | Promise<void> | Args<T> | Assign<T>;\n\n /**\n * Per-instance setup, run at the `new()` slot - after own values are\n * observed and constructor args applied. May return a cleanup function.\n */\n after?(this: T, self: T): void | (() => void);\n }\n\n /** Object overlay to override values and methods on a state. */\n type Assign<T> = Record<string, unknown> & {\n [K in Field<T>]?: T[K] extends (...args: infer A) => infer R\n ? (this: T, ...args: A) => R\n : T[K];\n };\n\n /** Subset of `keyof T` not defined by base State. **/\n type Field<T> = Exclude<keyof T, keyof State>;\n\n /** Any valid key for state, including but not limited to Field<T>. */\n type Event<T = State> = Field<T> | number | symbol | (string & {});\n\n /** Any event signal dispatched to listeners, including lifecycle meta events. */\n type Signal<T = State> = Event<T> | true | false | null;\n\n /** Export/Import compatible value for a given property in a State. */\n type Export<R> = R extends State\n ? Values<R>\n : R extends { get(): infer T }\n ? T\n : R;\n\n /**\n * Values from current state of given state.\n * Differs from `Values` as values here will drill\n * into \"real\" values held by exotics like ref.Object.\n */\n type Values<T> = { [P in Field<T>]: Export<T[P]> };\n\n /** Object comperable to data found in T. */\n type Partial<T> = { [P in Field<T>]?: Export<T[P]> };\n\n /** Value for a property applied by a state. */\n type Value<T extends State, K extends State.Event<T>> = K extends keyof T\n ? Export<T[K]>\n : unknown;\n\n type Setter<T> = (value: T, previous: T) => T | void;\n\n /** Descriptor config for a managed property. */\n type Apply<T = any> = {\n value?: T;\n get?: ((source: State) => T) | boolean;\n set?: Setter<T> | boolean;\n enumerable?: boolean;\n // destroy?: () => void;\n };\n\n /** Descriptor config for set() overload - enforces type when key is a known field. */\n type Define<T, K> = K extends Field<T> ? Apply<T[K] | Export<T[K]>> : Apply;\n\n type OnEvent<T extends State> = (\n this: T,\n key: Signal<T>,\n source: T\n ) => void | (() => void) | null;\n\n /** Exotic value, where actual value is contained within. */\n type Ref<T = any> = {\n (next: T): void;\n current: T | null;\n };\n\n /**\n * A callback function which is subscribed to parent and updates when accessed properties change.\n *\n * @param current - Current state of this state. This is a proxy which detects properties which\n * where accessed, and thus depended upon to stay current.\n *\n * @param update - Set of properties which have changed, and events fired, since last update.\n *\n * @returns A callback function which will be called when this effect is stale.\n */\n type Effect<T> = (\n this: T,\n current: T,\n update: readonly State.Event<T>[] | undefined\n ) => EffectCallback | Promise<void> | null | void;\n\n /**\n * A callback function returned by effect. Will be called when effect is stale.\n *\n * @param update - `true` if update is pending, `false` effect has been cancelled, `null` if state is destroyed.\n */\n type EffectCallback = (update: boolean | null) => void;\n\n /**\n * A list of keys updated, or events fired, since last update.\n * This may be awaited to get full list (same array) when update is settled.\n */\n type Updated<T extends State> = readonly Event<T>[] &\n PromiseLike<readonly Event<T>[]>;\n}\n\nabstract class State {\n /**\n * Loopback to instance of this state. This is useful when in a subscribed context,\n * to keep write access to `this` after a destructure. You can use it to read variables silently as well.\n **/\n declare is: this;\n\n constructor(...args: State.Args) {\n define(this, 'is', { value: this });\n init(this, args, this.new);\n }\n\n /**\n * Optional lifecycle hook called during State initialization.\n * Can return a cleanup function to run when state is destroyed.\n *\n * It is recommended you protect this method.\n */\n protected new?(): void | (() => void);\n\n /**\n * Pull current values from state. Flattens all states and exotic values recursively.\n *\n * @returns Object with all values from this state.\n **/\n get(): State.Values<this>;\n\n /**\n * Run a function to run automatically when accessed values change.\n *\n * @param effect Function to run, and whenever accessed values change.\n * If effect returns a function, it will be called when a change occurs (syncronously),\n * effect is cancelled, or parent state is destroyed.\n * @returns Function to cancel listener.\n */\n get(effect: State.Effect<this>): () => void;\n\n /**\n * Get value of a property. Will fetch underlying value from exotic values like ref.Object.\n *\n * Any value which implements `get()` (with no arguments) will be treated as such.\n *\n * @param key - Property to get value of.\n * @param required - If true, will throw an error if property is not available.\n * @returns Value of property.\n */\n get<T extends State.Event<this>>(\n key: T,\n required?: boolean\n ): State.Value<this, T>;\n\n /**\n * Check if state is destroyed.\n *\n * @param status - `null` to check if state is destroyed.\n * @returns `true` if state is destroyed, `false` otherwise.\n */\n get(status: null): boolean;\n\n /**\n * Callback when state is to be destroyed.\n *\n * @param callback - Function to call when state is destroyed.\n * @returns Function to cancel listener.\n */\n get(status: null, callback: () => void): () => void;\n\n /** Fetch State of type from context. Throws if not found. */\n get<T extends State>(type: State.Type<T>, required?: true): T;\n\n /** Fetch a State from context. Undefined if not found. */\n get<T extends State>(type: State.Type<T>, required: boolean): T | undefined;\n\n /**\n * Subscribe to State becoming available in context.\n *\n * Will search both up and downstream by default.\n * Normally you can ignore this because State you expect is rarely both.\n *\n * Specify downstream if you only want to watch for State in children, which can be useful if you expect multiple of the same State in different branches of the tree.\n *\n * @param type - Type of State to watch for.\n * @param callback - Function to call when State is found. Will be called immediately if State is already available.\n * @param downstream - If true, will only watch for State in children, if false will only for parents.\n * @returns Function to cancel listener.\n */\n get<T extends State>(\n type: State.Type<T>,\n callback: Context.Expect<T>,\n downstream?: boolean\n ): () => void;\n\n get(\n arg1?: State.Effect<this> | State.Type | string | null,\n arg2?: boolean | Context.Expect | (() => void),\n arg3?: boolean\n ) {\n const self = this.is;\n\n if (arg1 === undefined) return values(self);\n if (State.is(arg1)) return Context.get(self).get(arg1, arg2, arg3, self);\n if (typeof arg1 == 'function') return watch(self, unbind(arg1));\n if (typeof arg2 == 'function') return callback(self, arg2, arg1);\n if (arg1 === null) return observer(self) === null;\n return access(self, arg1, arg2);\n }\n\n /**\n * Get update in progress.\n *\n * @returns Promise which resolves object with updated values, `undefined` if there no update is pending.\n **/\n set(): State.Updated<this>;\n\n /**\n * Merge argument with current state, updating one or more properties at once.\n * Properties which are not managed by this state will be ignored.\n *\n * @param assign - Object with properties to update.\n * @param silent - If true, listeners will not be notified. If state is destroyed, will squash update without throwing.\n * @returns Array of keys updated, syncronously contains keys updated immediately and may be resolved (to itself) when all updates are settled.\n */\n set(assign?: State.Assign<this>, silent?: boolean): State.Updated<this>;\n\n /**\n * Call a function when update occurs.\n *\n * Given function is called for every assignment (which changes value) or explicit `set`.\n *\n * To run logic on final value only, callback may return a function. The same\n * function for one or more events will be called only once when update is settled.\n *\n * Return `null` from callback to stop listening.\n *\n * @param callback - Function to call when update occurs.\n * @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.\n */\n set(callback: State.OnEvent<this>): () => boolean;\n\n /**\n * Push an update. This will not change the value of associated property.\n *\n * Useful where a property value internally has changed, but the object is the same.\n * For example: An array has pushed a new value, or a nested property is updated.\n *\n * You can also use this to dispatch arbitrary events.\n * Symbols are recommended as non-property events, however you can use any string.\n * If doing so, be sure to avoid collisions using property names. An easy way to do this is\n * to prefix an event with \"!\" and/or use dash-case. e.g. `set(\"!event\")` or `set(\"my-event\")`.\n *\n * @param key - Property or event to dispatch.\n * @returns Promise resolves an array of keys updated.\n */\n set(key: State.Event<this>): State.Updated<this>;\n\n /**\n * Declare an end to updates. This event is final and will freeze state.\n * This event can be watched for as well, to run cleanup logic and internally will remove all listeners.\n *\n * @param status - `null` to end updates.\n */\n set(status: null): void;\n\n /**\n * Register a callback for a specific property or event.\n *\n * Callback receives the key, current value, and source instance.\n *\n * @param event - Property or event to watch. If `null`, will callback on destroy.\n * @param callback - Function to call when event occurs.\n * @returns Function to remove listener.\n */\n set<K extends State.Event<this>>(\n event: K | null,\n callback: State.OnEvent<this>\n ): () => boolean;\n\n /**\n * Define or update a managed property using a descriptor config.\n * If the property already is managed, config will only accept value.\n * If the property does not exist, it will be created and made reactive.\n *\n * @param key - Property to define or update.\n * @param config - Descriptor config with value, get, set, enumerable, and/or destroy.\n * @returns Promise resolves an array of keys updated.\n */\n set<K extends State.Event<this>>(\n key: K,\n config: State.Define<this, K>\n ): State.Updated<this>;\n\n set(\n arg1?: State.OnEvent<this> | State.Assign<this> | State.Event<this> | null,\n arg2?: unknown\n ) {\n const self = this.is;\n\n if (typeof arg1 == 'function')\n return listener(self, (key) => {\n if (\n typeof key == 'string' ||\n typeof key == 'number' ||\n typeof key == 'symbol'\n )\n return arg1.call(self, key, self);\n });\n\n if (typeof arg2 == 'function')\n return callback(self, arg2, arg1 as State.Signal);\n\n if (arg1 && typeof arg1 == 'object') assign(self, arg1, arg2 === true);\n else if (arg2) apply(self, arg1 as string, arg2);\n else event(self, arg1);\n\n return pending(self) as State.Updated<this>;\n }\n\n /**\n * Iterate over managed properties in this instance of State.\n * Yeilds the key and current value for each property.\n */\n [Symbol.iterator](): Iterator<[string, unknown]> {\n return Object.entries(STORE.get(this.is)!)[Symbol.iterator]();\n }\n\n /**\n * Create and activate a new instance of this state.\n *\n * **Important** - Unlike `new this(...)` - this method also activates state.\n *\n * @param args - arguments sent to constructor\n */\n static new<T extends State>(this: State.Type<T>, ...args: State.Args<T>): T {\n const instance = new this(...args);\n event(instance);\n return instance;\n }\n\n /**\n * Static equivalent of `x instanceof this`.\n * Determines if provided class is a subtype of this one.\n * If so, language server will make available all static\n * methods and properties of this class.\n */\n static is<T extends State.Extends>(this: T, maybe: unknown): maybe is T {\n return (\n maybe === this ||\n (typeof maybe == 'function' && maybe.prototype instanceof this)\n );\n }\n\n /**\n * Register a lifecycle handler for this State and its subclasses.\n *\n * A bare function is per-instance setup run in the `prepare` phase (sugar for\n * `{ before }`); if it returns a function, that runs when the instance is\n * destroyed. Pass a {@link State.On} object to hook by cadence - `type`\n * (per-class, at bootstrap), `before` (per-instance, before `new()`), and\n * `after` (per-instance, at the `new()` slot).\n *\n * @returns Function to remove the handler.\n */\n static on<T extends State>(\n this: State.Extends<T>,\n handler: State.Init<T> | State.On<T>\n ) {\n let setup = SETUP.get(this);\n\n if (!setup) SETUP.set(this, (setup = new Set()));\n\n setup.add(handler);\n\n return () => setup.delete(handler);\n }\n}\n\ndefine(State.prototype, 'toString', {\n value() {\n return ID.get(this.is);\n }\n});\n\ndefine(State, 'toString', {\n value() {\n return this.name;\n }\n});\n\n/** Register a user OnEvent callback, preserving `this` and `source`. */\nfunction callback<T extends State>(\n self: T,\n cb: Function,\n select?: Observer.Signal | Set<Observer.Signal>\n) {\n return listener(self, (key) => cb.call(self, key, self), select);\n}\n\n/**\n * Apply state arguemnts, run callbacks and observe properties.\n * Accumulate and handle cleanup events.\n **/\nfunction init(state: State, ...args: State.Args) {\n const T = state.constructor as State.Extends;\n\n if (T === State) throw new Error('Cannot create base State.');\n\n const { before, after } = bootstrap(T);\n\n ID.set(state, `${T}-${uid()}`);\n STORE.set(state, {});\n\n function observe() {\n for (const key in state) {\n const desc = Object.getOwnPropertyDescriptor(state, key)!;\n if ('value' in desc) apply(state, key, desc, true);\n }\n }\n\n function register() {\n if (Context.get(state) === Context.root) return Context.root.add(state);\n }\n\n listener(state, () => {\n parent(state, null);\n\n const queue = [...before, observe, ...args, ...after, register];\n\n for (let i = 0; i < queue.length; i++) {\n const arg = queue[i];\n const out = typeof arg == 'function' ? arg.call(state, state) : arg;\n\n if (out instanceof Promise)\n out.catch((err) => {\n console.error(`Async error in constructor for ${state}:`);\n console.error(err);\n });\n else if (Array.isArray(out)) queue.splice(i + 1, 0, ...out);\n else if (typeof out == 'function') listener(state, out, null);\n else if (typeof out == 'object') assign(state, out, true);\n }\n\n return null;\n });\n}\n\n/**\n * Apply instructions and inherited event listeners. Ensure class metadata is ready.\n *\n * @param T - State class to bootstrap.\n * @return Array of init functions to run on instance, in order of class hierarchy.\n */\nfunction bootstrap(T: State.Extends) {\n const chain = [] as State.Extends[];\n const before = new Set<State.Init>();\n const after = new Set<State.Init>();\n const onType = new Set<NonNullable<State.On['type']>>();\n let keys = new Map<string, (value: any) => void>();\n let getters = new Map<string, () => unknown>();\n\n do {\n chain.unshift(T);\n T = Object.getPrototypeOf(T);\n } while (T.name);\n\n for (const type of chain) {\n for (const handler of SETUP.get(type) || [])\n if (typeof handler == 'function') before.add(handler);\n else {\n if (handler.before) before.add(handler.before);\n if (handler.after) after.add(handler.after);\n if (handler.type) onType.add(handler.type);\n }\n\n if (type === State) continue;\n\n if (METHODS.has(type)) {\n keys = METHODS.get(type)!;\n getters = GETTERS.get(type)!;\n continue;\n }\n\n for (const setupType of onType) setupType(type);\n\n METHODS.set(type, (keys = new Map(keys)));\n GETTERS.set(type, (getters = new Map(getters)));\n\n for (const [key, desc] of Object.entries(\n Object.getOwnPropertyDescriptors(type.prototype)\n )) {\n if (key == 'constructor' || !desc.configurable) continue;\n\n if (typeof desc.get == 'function') {\n if (desc.configurable && typeof desc.set != 'function')\n getters.set(key, desc.get);\n\n continue;\n }\n\n const { value } = desc;\n\n if (typeof value !== 'function') continue;\n\n function bind(this: State, original?: Function) {\n const { is } = this;\n\n if (is.hasOwnProperty(key) && !original) return value as Function;\n\n const fn = original || value;\n const bound = fn.bind(is);\n\n UNBIND.set(bound, fn);\n define(is, key, { value: bound, writable: true, configurable: true });\n\n return bound;\n }\n\n UNBIND.set(bind, value);\n\n keys.set(key, bind);\n define(type.prototype, key, { get: bind, set: bind });\n }\n }\n\n if (getters.size)\n before.add((self) => getters.forEach(compute, self));\n\n return { before, after };\n}\n\n/**\n * Install a reactive computed property on a state instance, derived from a prototype\n * getter or an arity-bearing `set` factory.\n *\n * The getter is invoked with the tracking proxy as both `this` and its first argument;\n * reads of managed properties through it create subscriptions. Result is cached and\n * emits a keyed event when stale.\n */\nfunction compute(this: State, getter: (self: any) => unknown, key: string) {\n const store = STORE.get(this)!;\n let reset: (() => void) | undefined;\n let isAsync: boolean;\n let proxy: any;\n\n const connect = () => {\n reset = watch(\n this,\n (current) => {\n proxy = current;\n\n if (!(key in store) || STALE.delete(run)) run(!reset);\n\n return (didUpdate) => {\n if (didUpdate) {\n STALE.add(run);\n event(this, key, true);\n }\n };\n },\n false\n );\n };\n\n const run = (initial?: boolean) => {\n let next: unknown;\n\n try {\n next = getter.call(proxy, proxy);\n } catch (err) {\n console.warn(\n `An exception was thrown while ${initial ? 'initializing' : 'refreshing'\n } ${this}.${key}.`\n );\n\n if (initial) throw err;\n\n console.error(err);\n }\n\n update(this, key, next, !isAsync);\n };\n\n apply(\n this,\n key,\n {\n enumerable: true,\n set: false,\n get: () => {\n if (!proxy) {\n if (observer(this) === null) return store[key];\n\n connect();\n isAsync = true;\n }\n\n if (STALE.delete(run)) run();\n\n return access(this, key, !proxy);\n }\n },\n true\n );\n}\n\n/**\n * Define or update a managed property using a descriptor config.\n * If the property already is managed, config will only accept value.\n * If the property does not exist, it will be created and made reactive.\n *\n * @param state - State to apply property to.\n * @param key - Property to define or update.\n * @param config - Descriptor config with value, get, set, enumerable, and/or destroy.\n * @param silent - If an update does occur, listeners will not be refreshed automatically.\n */\nfunction apply(\n state: State,\n key: string,\n config: State.Apply,\n silent?: boolean\n) {\n const desc = Object.getOwnPropertyDescriptor(state, key);\n\n if (desc && 'get' in desc) {\n if (Object.keys(config).some((k) => k != 'value'))\n throw new Error(`Property ${key} on ${state} is already defined.`);\n if ('value' in config) update(state, key, config.value, silent);\n return;\n }\n\n const adopt = config.value instanceof State && child(state);\n\n function set(value: unknown, silent?: boolean) {\n if (!update(state, key, value, silent)) return;\n if (adopt) adopt(value);\n }\n\n define(state, key, {\n configurable: true,\n enumerable: config.enumerable !== false,\n get(this: State) {\n const value =\n typeof config.get == 'function'\n ? config.get(this)\n : access(state, key, config.get);\n\n return touch(this, key, value);\n },\n set(next: any, silent?: boolean) {\n if (config.set === false)\n throw new Error(`${state}.${String(key)} is read-only.`);\n\n if (typeof config.set == 'function')\n try {\n const output = config.set(next, STORE.get(state)![key]);\n if (output !== undefined) next = output;\n } catch (err: unknown) {\n if (err === false) return;\n if (err === true) {\n set(next, true);\n return;\n }\n throw err;\n }\n\n set(next, silent);\n }\n });\n\n if ('value' in config) set(config.value, silent);\n}\n\nfunction child(state: State) {\n let cleanup: (() => void) | undefined;\n const ctx = Context.get(state);\n\n function reset() {\n if (cleanup) cleanup();\n cleanup = undefined;\n }\n\n listener(state, reset, null);\n\n return (value: unknown) => {\n reset();\n\n if (!(value instanceof State)) return;\n\n const remove = ctx.add(value);\n\n if (parent(value) === undefined) {\n parent(value, state);\n cleanup = () => {\n cancel();\n remove();\n event(value, null);\n };\n const cancel = listener(state, cleanup, null);\n } else {\n cleanup = remove;\n }\n\n event(value);\n };\n}\n\n/** Currently accumulating export. Stores real values of placeholder properties such as ref() or child states. */\nlet EXPORT: Map<any, any> | undefined;\n\nfunction values<T extends State>(state: T): State.Values<T> {\n const values = {} as any;\n let notRecursive;\n\n if (!EXPORT) {\n notRecursive = true;\n EXPORT = new Map();\n }\n\n EXPORT.set(state, values);\n\n for (let [key, value] of state) {\n if (EXPORT.has(value)) value = EXPORT.get(value);\n else if (\n value &&\n typeof value == 'object' &&\n 'get' in value &&\n typeof value.get === 'function'\n )\n value = value.get();\n\n values[key] = value;\n }\n\n if (notRecursive) EXPORT = undefined;\n\n return Object.freeze(values);\n}\n\nfunction access(state: State, property: string, required?: boolean) {\n const store = STORE.get(state)!;\n\n if (property in store || required === false) {\n const value = store[property];\n\n if (value !== undefined || !required) return value;\n }\n\n if (METHODS.get(state.constructor)!.has(property))\n return unbind((state as any)[property]);\n\n if (observer(state) === null) return undefined;\n\n const error = new Error(`${state}.${property} is not yet available.`);\n const promise = new Promise<any>((resolve, reject) => {\n listener(state, (key) => {\n if (key === property) {\n resolve(store[key]);\n return null;\n }\n\n if (key === null) {\n reject(new Error(`${state} is destroyed.`));\n }\n });\n });\n\n throw Object.assign(promise, {\n toString: () => String(error),\n name: 'Suspense',\n message: error.message,\n stack: error.stack\n });\n}\n\nfunction assign(state: State, data: State.Assign<State>, silent?: boolean) {\n if (!silent) event(state);\n\n const methods = METHODS.get(state.constructor)!;\n const getters = GETTERS.get(state.constructor);\n\n for (const key in data) {\n const bind = methods.get(key);\n\n if (bind) bind.call(state, data[key]);\n else if (getters?.has(key)) continue;\n else if (key in state && key !== 'is') {\n const desc = Object.getOwnPropertyDescriptor(state, key)!;\n const set = desc && (desc.set as (value: any, silent?: boolean) => void);\n\n if (set) {\n set.call(state, data[key], silent);\n } else {\n (state as any)[key] = data[key];\n }\n }\n }\n}\n\n/**\n * Update a property on a state instance and notify listeners.\n *\n * This is used internally to update properties, but can also be used to update properties which are not managed by state, or to update values without triggering setters.\n *\n * If `silent` is true, the update will not dispatch events and will return `false` instead of throwing if state is destroyed.\n */\nfunction update<T>(\n state: State,\n key: State.Event<T>,\n value: T,\n silent?: boolean\n) {\n if (observer(state) === null) {\n if (silent) return false;\n throw new Error(\n `Tried to update ${state}.${String(key)} but state is destroyed.`\n );\n }\n\n const store = STORE.get(state)!;\n\n if (key in store && value === store[key]) return false;\n\n store[key] = value;\n\n if (!silent) event(state, key);\n\n return true;\n}\n\n/** Random alphanumberic of length 6; always starts with a letter. */\nfunction uid() {\n return (0.278 + Math.random() * 0.722)\n .toString(36)\n .substring(2, 8)\n .toUpperCase();\n}\n\nfunction unbind<T extends Function>(fn: T): T;\nfunction unbind<T extends Function>(fn?: T): T | undefined;\nfunction unbind(fn?: Function) {\n return UNBIND.get(fn) || fn;\n}\n\n/**\n * Get or set a parent on given state. May only be assigned once; ignores new value.\n * Returns parent of child, or `undefined` if not assigned yet.\n */\nfunction parent(child: State, value?: State | null) {\n if (arguments.length > 1 && !PARENT.has(child)) PARENT.set(child, value!);\n return PARENT.get(child);\n}\n\nexport { event, unbind, State, parent, STORE, uid, access, update, apply, compute };\n"],"mappings":"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"}
1
+ {"version":3,"file":"state.js","names":[],"sources":["../src/state.ts"],"sourcesContent":["import { Context } from './context';\nimport {\n event,\n listener,\n Observer,\n observer,\n pending,\n touch,\n watch\n} from './observable';\n\nconst define = Object.defineProperty;\n\n/** Register for all active states via string identifiers (usually unique). */\nconst ID = new WeakMap<State, string>();\n\n/** Internal state assigned to states. */\nconst STORE = new WeakMap<State, Record<string | number | symbol, unknown>>();\n\n/** External lifecycle listeners for any given State class. */\nconst SETUP = new WeakMap<State.Extends, Set<State.Init<any> | State.On<any>>>();\n\n/** Parent-child relationships. */\nconst PARENT = new WeakMap<object, State | null>();\n\n/** Reference bound instance methods, or their getter, to real one. */\nconst UNBIND = new WeakMap<any, any>();\n\n/** List of methods defined by a given type. */\nconst METHODS = new WeakMap<Function, Map<string, (value: any) => void>>();\n\n/** List of reactive getters defined by a given type. */\nconst GETTERS = new WeakMap<Function, Map<string, () => unknown>>();\n\n/** Stale flags for compute closures awaiting refresh on next access. */\nconst STALE = new WeakSet<() => void>();\n\ndeclare namespace State {\n /** Any type of State, using own class constructor as its identifier. */\n type Extends<T extends State = State> = (abstract new (...args: any[]) => T) &\n typeof State;\n\n /** A State constructor which may be instanciated. */\n type Type<T extends State = State> = (new (...args: State.Args<T>) => T) &\n Omit<typeof State, never>;\n\n /** State constructor arguments */\n type Args<T extends State = any> = (Args<T> | Init<T> | Assign<T> | void)[];\n\n /**\n * Value of `static global`. A boolean opts a State in or out of the\n * process-global root; a resolver decides at activation, receiving the\n * instance and returning whether it registers - use it to make the choice\n * conditional (e.g. per environment). A bare literal (`= true` / `= false`)\n * seals the choice for subclasses; widen a subclass's `static global` type to\n * `State.Global` to permit a resolver or a later override.\n */\n type Global<T extends State = any> = boolean | ((self: T) => boolean);\n\n /**\n * State constructor callback - runs during activation, in argument order,\n * before the `new()` lifecycle hook (so it may configure state `new()`\n * will observe). Returned function will run when state is destroyed.\n */\n type Init<T extends State = State> = (\n this: T,\n thisArg: T\n ) => Promise<void> | (() => void) | Args<T> | Assign<T> | void;\n\n /**\n * Lifecycle handlers for `State.on`, keyed by when they run. A bare `Init`\n * function passed to `on` is sugar for `{ before }`.\n */\n interface On<T extends State = State> {\n /**\n * Per-class setup, run once when the class is first bootstrapped - before\n * its members are classified and bound. Receives the class, so a handler\n * may inspect or reshape the prototype first. A handler registered on a\n * base class runs for each subclass too.\n */\n type?(type: State.Extends<T>): void;\n\n /**\n * Per-instance setup, run in the `prepare` phase before `observe` and the\n * `new()` hook. Equivalent to passing a bare function to `on`. May return a\n * cleanup, constructor args, or an assign overlay.\n */\n before?(this: T, thisArg: T): void | (() => void) | Promise<void> | Args<T> | Assign<T>;\n\n /**\n * Per-instance setup, run at the `new()` slot - after own values are\n * observed and constructor args applied. May return a cleanup function.\n */\n after?(this: T, self: T): void | (() => void);\n }\n\n /** Object overlay to override values and methods on a state. */\n type Assign<T> = Record<string, unknown> & {\n [K in Field<T>]?: T[K] extends (...args: infer A) => infer R\n ? (this: T, ...args: A) => R\n : T[K];\n };\n\n /** Subset of `keyof T` not defined by base State. **/\n type Field<T> = Exclude<keyof T, keyof State>;\n\n /** Any valid key for state, including but not limited to Field<T>. */\n type Event<T = State> = Field<T> | number | symbol | (string & {});\n\n /** Any event signal dispatched to listeners, including lifecycle meta events. */\n type Signal<T = State> = Event<T> | true | false | null;\n\n /** Export/Import compatible value for a given property in a State. */\n type Export<R> = R extends State\n ? Values<R>\n : R extends { get(): infer T }\n ? T\n : R;\n\n /**\n * Values from current state of given state.\n * Differs from `Values` as values here will drill\n * into \"real\" values held by exotics like ref.Object.\n */\n type Values<T> = { [P in Field<T>]: Export<T[P]> };\n\n /** Object comperable to data found in T. */\n type Partial<T> = { [P in Field<T>]?: Export<T[P]> };\n\n /** Value for a property applied by a state. */\n type Value<T extends State, K extends State.Event<T>> = K extends keyof T\n ? Export<T[K]>\n : unknown;\n\n type Setter<T> = (value: T, previous: T) => T | void;\n\n /** Descriptor config for a managed property. */\n type Apply<T = any> = {\n value?: T;\n get?: ((source: State) => T) | boolean;\n set?: Setter<T> | boolean;\n enumerable?: boolean;\n // destroy?: () => void;\n };\n\n /** Descriptor config for set() overload - enforces type when key is a known field. */\n type Define<T, K> = K extends Field<T> ? Apply<T[K] | Export<T[K]>> : Apply;\n\n type OnEvent<T extends State> = (\n this: T,\n key: Signal<T>,\n source: T\n ) => void | (() => void) | null;\n\n /** Exotic value, where actual value is contained within. */\n type Ref<T = any> = {\n (next: T): void;\n current: T | null;\n };\n\n /**\n * A callback function which is subscribed to parent and updates when accessed properties change.\n *\n * @param current - Current state of this state. This is a proxy which detects properties which\n * where accessed, and thus depended upon to stay current.\n *\n * @param update - Set of properties which have changed, and events fired, since last update.\n *\n * @returns A callback function which will be called when this effect is stale.\n */\n type Effect<T> = (\n this: T,\n current: T,\n update: readonly State.Event<T>[] | undefined\n ) => EffectCallback | Promise<void> | null | void;\n\n /**\n * A callback function returned by effect. Will be called when effect is stale.\n *\n * @param update - `true` if update is pending, `false` effect has been cancelled, `null` if state is destroyed.\n */\n type EffectCallback = (update: boolean | null) => void;\n\n /**\n * A list of keys updated, or events fired, since last update.\n * This may be awaited to get full list (same array) when update is settled.\n */\n type Updated<T extends State> = readonly Event<T>[] &\n PromiseLike<readonly Event<T>[]>;\n}\n\nabstract class State {\n /**\n * Whether an instance activated with no enclosing context registers itself\n * to the process-global root, where `get()` can resolve it from anywhere.\n * `false` (the default) keeps a context-less instance fully functional but\n * private - not injectable, though it can still read declared globals. Opt in\n * with `static readonly global = true`; a resolver (see {@link State.Global})\n * makes the choice conditional. It must be declared per class - a subclass\n * that would inherit a global without its own declaration throws on\n * activation, so an accidental global (a forgotten `Provider`, an extended\n * global) cannot leak into the shared root.\n */\n static readonly global: State.Global = false;\n\n /**\n * Loopback to instance of this state. This is useful when in a subscribed context,\n * to keep write access to `this` after a destructure. You can use it to read variables silently as well.\n **/\n declare is: this;\n\n constructor(...args: State.Args) {\n define(this, 'is', { value: this });\n init(this, args, this.new);\n }\n\n /**\n * Optional lifecycle hook called during State initialization.\n * Can return a cleanup function to run when state is destroyed.\n *\n * It is recommended you protect this method.\n */\n protected new?(): void | (() => void);\n\n /**\n * Pull current values from state. Flattens all states and exotic values recursively.\n *\n * @returns Object with all values from this state.\n **/\n get(): State.Values<this>;\n\n /**\n * Run a function to run automatically when accessed values change.\n *\n * @param effect Function to run, and whenever accessed values change.\n * If effect returns a function, it will be called when a change occurs (syncronously),\n * effect is cancelled, or parent state is destroyed.\n * @returns Function to cancel listener.\n */\n get(effect: State.Effect<this>): () => void;\n\n /**\n * Get value of a property. Will fetch underlying value from exotic values like ref.Object.\n *\n * Any value which implements `get()` (with no arguments) will be treated as such.\n *\n * @param key - Property to get value of.\n * @param required - If true, will throw an error if property is not available.\n * @returns Value of property.\n */\n get<T extends State.Event<this>>(\n key: T,\n required?: boolean\n ): State.Value<this, T>;\n\n /**\n * Check if state is destroyed.\n *\n * @param status - `null` to check if state is destroyed.\n * @returns `true` if state is destroyed, `false` otherwise.\n */\n get(status: null): boolean;\n\n /**\n * Callback when state is to be destroyed.\n *\n * @param callback - Function to call when state is destroyed.\n * @returns Function to cancel listener.\n */\n get(status: null, callback: () => void): () => void;\n\n /** Fetch State of type from context. Throws if not found. */\n get<T extends State>(type: State.Type<T>, required?: true): T;\n\n /** Fetch a State from context. Undefined if not found. */\n get<T extends State>(type: State.Type<T>, required: boolean): T | undefined;\n\n /**\n * Subscribe to State becoming available in context.\n *\n * Will search both up and downstream by default.\n * Normally you can ignore this because State you expect is rarely both.\n *\n * Specify downstream if you only want to watch for State in children, which can be useful if you expect multiple of the same State in different branches of the tree.\n *\n * @param type - Type of State to watch for.\n * @param callback - Function to call when State is found. Will be called immediately if State is already available.\n * @param downstream - If true, will only watch for State in children, if false will only for parents.\n * @returns Function to cancel listener.\n */\n get<T extends State>(\n type: State.Type<T>,\n callback: Context.Expect<T>,\n downstream?: boolean\n ): () => void;\n\n get(\n arg1?: State.Effect<this> | State.Type | string | null,\n arg2?: boolean | Context.Expect | (() => void),\n arg3?: boolean\n ) {\n const self = this.is;\n\n if (arg1 === undefined) return values(self);\n if (State.is(arg1)) return Context.get(self).get(arg1, arg2, arg3, self);\n if (typeof arg1 == 'function') return watch(self, unbind(arg1));\n if (typeof arg2 == 'function') return callback(self, arg2, arg1);\n if (arg1 === null) return observer(self) === null;\n return access(self, arg1, arg2);\n }\n\n /**\n * Get update in progress.\n *\n * @returns Promise which resolves object with updated values, `undefined` if there no update is pending.\n **/\n set(): State.Updated<this>;\n\n /**\n * Merge argument with current state, updating one or more properties at once.\n * Properties which are not managed by this state will be ignored.\n *\n * @param assign - Object with properties to update.\n * @param silent - If true, listeners will not be notified. If state is destroyed, will squash update without throwing.\n * @returns Array of keys updated, syncronously contains keys updated immediately and may be resolved (to itself) when all updates are settled.\n */\n set(assign?: State.Assign<this>, silent?: boolean): State.Updated<this>;\n\n /**\n * Call a function when update occurs.\n *\n * Given function is called for every assignment (which changes value) or explicit `set`.\n *\n * To run logic on final value only, callback may return a function. The same\n * function for one or more events will be called only once when update is settled.\n *\n * Return `null` from callback to stop listening.\n *\n * @param callback - Function to call when update occurs.\n * @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.\n */\n set(callback: State.OnEvent<this>): () => boolean;\n\n /**\n * Push an update. This will not change the value of associated property.\n *\n * Useful where a property value internally has changed, but the object is the same.\n * For example: An array has pushed a new value, or a nested property is updated.\n *\n * You can also use this to dispatch arbitrary events.\n * Symbols are recommended as non-property events, however you can use any string.\n * If doing so, be sure to avoid collisions using property names. An easy way to do this is\n * to prefix an event with \"!\" and/or use dash-case. e.g. `set(\"!event\")` or `set(\"my-event\")`.\n *\n * @param key - Property or event to dispatch.\n * @returns Promise resolves an array of keys updated.\n */\n set(key: State.Event<this>): State.Updated<this>;\n\n /**\n * Declare an end to updates. This event is final and will freeze state.\n * This event can be watched for as well, to run cleanup logic and internally will remove all listeners.\n *\n * @param status - `null` to end updates.\n */\n set(status: null): void;\n\n /**\n * Register a callback for a specific property or event.\n *\n * Callback receives the key, current value, and source instance.\n *\n * @param event - Property or event to watch. If `null`, will callback on destroy.\n * @param callback - Function to call when event occurs.\n * @returns Function to remove listener.\n */\n set<K extends State.Event<this>>(\n event: K | null,\n callback: State.OnEvent<this>\n ): () => boolean;\n\n /**\n * Define or update a managed property using a descriptor config.\n * If the property already is managed, config will only accept value.\n * If the property does not exist, it will be created and made reactive.\n *\n * @param key - Property to define or update.\n * @param config - Descriptor config with value, get, set, enumerable, and/or destroy.\n * @returns Promise resolves an array of keys updated.\n */\n set<K extends State.Event<this>>(\n key: K,\n config: State.Define<this, K>\n ): State.Updated<this>;\n\n set(\n arg1?: State.OnEvent<this> | State.Assign<this> | State.Event<this> | null,\n arg2?: unknown\n ) {\n const self = this.is;\n\n if (typeof arg1 == 'function')\n return listener(self, (key) => {\n if (\n typeof key == 'string' ||\n typeof key == 'number' ||\n typeof key == 'symbol'\n )\n return arg1.call(self, key, self);\n });\n\n if (typeof arg2 == 'function')\n return callback(self, arg2, arg1 as State.Signal);\n\n if (arg1 && typeof arg1 == 'object') assign(self, arg1, arg2 === true);\n else if (arg2) apply(self, arg1 as string, arg2);\n else event(self, arg1);\n\n return pending(self) as State.Updated<this>;\n }\n\n /**\n * Iterate over managed properties in this instance of State.\n * Yeilds the key and current value for each property.\n */\n [Symbol.iterator](): Iterator<[string, unknown]> {\n return Object.entries(STORE.get(this.is)!)[Symbol.iterator]();\n }\n\n /**\n * Create and activate a new instance of this state.\n *\n * **Important** - Unlike `new this(...)` - this method also activates state.\n *\n * @param args - arguments sent to constructor\n */\n static new<T extends State>(this: State.Type<T>, ...args: State.Args<T>): T {\n const instance = new this(...args);\n event(instance);\n return instance;\n }\n\n /**\n * Static equivalent of `x instanceof this`.\n * Determines if provided class is a subtype of this one.\n * If so, language server will make available all static\n * methods and properties of this class.\n */\n static is<T extends State.Extends>(this: T, maybe: unknown): maybe is T {\n return (\n maybe === this ||\n (typeof maybe == 'function' && maybe.prototype instanceof this)\n );\n }\n\n /**\n * Register a lifecycle handler for this State and its subclasses.\n *\n * A bare function is per-instance setup run in the `prepare` phase (sugar for\n * `{ before }`); if it returns a function, that runs when the instance is\n * destroyed. Pass a {@link State.On} object to hook by cadence - `type`\n * (per-class, at bootstrap), `before` (per-instance, before `new()`), and\n * `after` (per-instance, at the `new()` slot).\n *\n * @returns Function to remove the handler.\n */\n static on<T extends State>(\n this: State.Extends<T>,\n handler: State.Init<T> | State.On<T>\n ) {\n let setup = SETUP.get(this);\n\n if (!setup) SETUP.set(this, (setup = new Set()));\n\n setup.add(handler);\n\n return () => setup.delete(handler);\n }\n}\n\ndefine(State.prototype, 'toString', {\n value() {\n return ID.get(this.is);\n }\n});\n\ndefine(State, 'toString', {\n value() {\n return this.name;\n }\n});\n\n/** Register a user OnEvent callback, preserving `this` and `source`. */\nfunction callback<T extends State>(\n self: T,\n cb: Function,\n select?: Observer.Signal | Set<Observer.Signal>\n) {\n return listener(self, (key) => cb.call(self, key, self), select);\n}\n\n/**\n * Apply state arguemnts, run callbacks and observe properties.\n * Accumulate and handle cleanup events.\n **/\nfunction init(state: State, ...args: State.Args) {\n const T = state.constructor as State.Extends;\n\n if (T === State) throw new Error('Cannot create base State.');\n\n const { before, after } = bootstrap(T);\n\n ID.set(state, `${T}-${uid()}`);\n STORE.set(state, {});\n\n function observe() {\n for (const key in state) {\n const desc = Object.getOwnPropertyDescriptor(state, key)!;\n if ('value' in desc && desc.configurable) apply(state, key, desc, true);\n }\n }\n\n function register() {\n if (Context.get(state) !== Context.root) return;\n\n const type = state.constructor as typeof State;\n const g = type.global;\n\n if (!(typeof g == 'function' ? g(state) : g)) return;\n\n if (!Object.prototype.hasOwnProperty.call(type, 'global'))\n throw new Error(\n `${state} would register as a global by inheritance alone - re-declare \\`static global\\` on ${type.name} (\\`true\\` to keep it, \\`false\\` to opt out).`\n );\n\n return Context.root.add(state);\n }\n\n listener(state, () => {\n parent(state, null);\n\n const queue = [...before, observe, ...args, ...after, register];\n\n for (let i = 0; i < queue.length; i++) {\n const arg = queue[i];\n const out = typeof arg == 'function' ? arg.call(state, state) : arg;\n\n if (out instanceof Promise)\n out.catch((err) => {\n console.error(`Async error in constructor for ${state}:`);\n console.error(err);\n });\n else if (Array.isArray(out)) queue.splice(i + 1, 0, ...out);\n else if (typeof out == 'function') listener(state, out, null);\n else if (typeof out == 'object') assign(state, out, true);\n }\n\n return null;\n });\n}\n\n/**\n * Apply instructions and inherited event listeners. Ensure class metadata is ready.\n *\n * @param T - State class to bootstrap.\n * @return Array of init functions to run on instance, in order of class hierarchy.\n */\nfunction bootstrap(T: State.Extends) {\n const chain = [] as State.Extends[];\n const before = new Set<State.Init>();\n const after = new Set<State.Init>();\n const onType = new Set<NonNullable<State.On['type']>>();\n let keys = new Map<string, (value: any) => void>();\n let getters = new Map<string, () => unknown>();\n\n do {\n chain.unshift(T);\n T = Object.getPrototypeOf(T);\n } while (T.name);\n\n for (const type of chain) {\n for (const handler of SETUP.get(type) || [])\n if (typeof handler == 'function') before.add(handler);\n else {\n if (handler.before) before.add(handler.before);\n if (handler.after) after.add(handler.after);\n if (handler.type) onType.add(handler.type);\n }\n\n if (type === State) continue;\n\n if (METHODS.has(type)) {\n keys = METHODS.get(type)!;\n getters = GETTERS.get(type)!;\n continue;\n }\n\n for (const setupType of onType) setupType(type);\n\n METHODS.set(type, (keys = new Map(keys)));\n GETTERS.set(type, (getters = new Map(getters)));\n\n for (const [key, desc] of Object.entries(\n Object.getOwnPropertyDescriptors(type.prototype)\n )) {\n if (key == 'constructor' || !desc.configurable) continue;\n\n if (typeof desc.get == 'function') {\n if (desc.configurable && typeof desc.set != 'function')\n getters.set(key, desc.get);\n\n continue;\n }\n\n const { value } = desc;\n\n if (typeof value !== 'function') continue;\n\n function bind(this: State, original?: Function) {\n const { is } = this;\n\n if (is.hasOwnProperty(key) && !original) return value as Function;\n\n const fn = original || value;\n const bound = fn.bind(is);\n\n UNBIND.set(bound, fn);\n define(is, key, { value: bound, writable: true, configurable: true });\n\n return bound;\n }\n\n UNBIND.set(bind, value);\n\n keys.set(key, bind);\n define(type.prototype, key, { get: bind, set: bind });\n }\n }\n\n if (getters.size)\n before.add((self) => getters.forEach(compute, self));\n\n return { before, after };\n}\n\n/**\n * Install a reactive computed property on a state instance, derived from a prototype\n * getter or an arity-bearing `set` factory.\n *\n * The getter is invoked with the tracking proxy as both `this` and its first argument;\n * reads of managed properties through it create subscriptions. Result is cached and\n * emits a keyed event when stale.\n */\nfunction compute(this: State, getter: (self: any) => unknown, key: string) {\n const store = STORE.get(this)!;\n let reset: (() => void) | undefined;\n let isAsync: boolean;\n let proxy: any;\n\n const connect = () => {\n reset = watch(\n this,\n (current) => {\n proxy = current;\n\n if (!(key in store) || STALE.delete(run)) run(!reset);\n\n return (didUpdate) => {\n if (didUpdate) {\n STALE.add(run);\n event(this, key, true);\n }\n };\n },\n false\n );\n };\n\n const run = (initial?: boolean) => {\n let next: unknown;\n\n try {\n next = getter.call(proxy, proxy);\n } catch (err) {\n console.warn(\n `An exception was thrown while ${initial ? 'initializing' : 'refreshing'\n } ${this}.${key}.`\n );\n\n if (initial) throw err;\n\n console.error(err);\n }\n\n update(this, key, next, !isAsync);\n };\n\n apply(\n this,\n key,\n {\n enumerable: true,\n set: false,\n get: () => {\n if (!proxy) {\n if (observer(this) === null) return store[key];\n\n connect();\n isAsync = true;\n }\n\n if (STALE.delete(run)) run();\n\n return access(this, key, !proxy);\n }\n },\n true\n );\n}\n\n/**\n * Define or update a managed property using a descriptor config.\n * If the property already is managed, config will only accept value.\n * If the property does not exist, it will be created and made reactive.\n *\n * @param state - State to apply property to.\n * @param key - Property to define or update.\n * @param config - Descriptor config with value, get, set, enumerable, and/or destroy.\n * @param silent - If an update does occur, listeners will not be refreshed automatically.\n */\nfunction apply(\n state: State,\n key: string,\n config: State.Apply,\n silent?: boolean\n) {\n const desc = Object.getOwnPropertyDescriptor(state, key);\n\n if (desc && 'get' in desc) {\n if (Object.keys(config).some((k) => k != 'value'))\n throw new Error(`Property ${key} on ${state} is already defined.`);\n if ('value' in config) update(state, key, config.value, silent);\n return;\n }\n\n const adopt = config.value instanceof State && child(state);\n\n function set(value: unknown, silent?: boolean) {\n if (!update(state, key, value, silent)) return;\n if (adopt) adopt(value);\n }\n\n define(state, key, {\n configurable: true,\n enumerable: config.enumerable !== false,\n get(this: State) {\n const value =\n typeof config.get == 'function'\n ? config.get(this)\n : access(state, key, config.get);\n\n return touch(this, key, value);\n },\n set(next: any, silent?: boolean) {\n if (config.set === false)\n throw new Error(`${state}.${String(key)} is read-only.`);\n\n if (typeof config.set == 'function')\n try {\n const output = config.set(next, STORE.get(state)![key]);\n if (output !== undefined) next = output;\n } catch (err: unknown) {\n if (err === false) return;\n if (err === true) {\n set(next, true);\n return;\n }\n throw err;\n }\n\n set(next, silent);\n }\n });\n\n if ('value' in config) set(config.value, silent);\n}\n\nfunction child(state: State) {\n let cleanup: (() => void) | undefined;\n const ctx = Context.get(state);\n\n function reset() {\n if (cleanup) cleanup();\n cleanup = undefined;\n }\n\n listener(state, reset, null);\n\n return (value: unknown) => {\n reset();\n\n if (!(value instanceof State)) return;\n\n const remove = ctx.add(value);\n\n if (parent(value, state)) {\n cleanup = () => {\n cancel();\n remove();\n event(value, null);\n };\n const cancel = listener(state, cleanup, null);\n } else {\n cleanup = remove;\n }\n\n event(value);\n };\n}\n\n/** Currently accumulating export. Stores real values of placeholder properties such as ref() or child states. */\nlet EXPORT: Map<any, any> | undefined;\n\nfunction values<T extends State>(state: T): State.Values<T> {\n const values = {} as any;\n let notRecursive;\n\n if (!EXPORT) {\n notRecursive = true;\n EXPORT = new Map();\n }\n\n EXPORT.set(state, values);\n\n for (let [key, value] of state) {\n if (EXPORT.has(value)) value = EXPORT.get(value);\n else if (\n value &&\n typeof value == 'object' &&\n 'get' in value &&\n typeof value.get === 'function'\n )\n value = value.get();\n\n values[key] = value;\n }\n\n if (notRecursive) EXPORT = undefined;\n\n return Object.freeze(values);\n}\n\nfunction access(state: State, property: string, required?: boolean) {\n const store = STORE.get(state)!;\n\n if (property in store || required === false) {\n const value = store[property];\n\n if (value !== undefined || !required) return value;\n }\n\n if (METHODS.get(state.constructor)!.has(property))\n return unbind((state as any)[property]);\n\n if (observer(state) === null) return undefined;\n\n const error = new Error(`${state}.${property} is not yet available.`);\n const promise = new Promise<any>((resolve, reject) => {\n listener(state, (key) => {\n if (key === property) {\n resolve(store[key]);\n return null;\n }\n\n if (key === null) {\n reject(new Error(`${state} is destroyed.`));\n }\n });\n });\n\n throw Object.assign(promise, {\n toString: () => String(error),\n name: 'Suspense',\n message: error.message,\n stack: error.stack\n });\n}\n\nfunction assign(state: State, data: State.Assign<State>, silent?: boolean) {\n if (!silent) event(state);\n\n const methods = METHODS.get(state.constructor)!;\n const getters = GETTERS.get(state.constructor);\n\n for (const key in data) {\n const bind = methods.get(key);\n\n if (bind) bind.call(state, data[key]);\n else if (getters?.has(key)) continue;\n else if (key in state && key !== 'is') {\n const desc = Object.getOwnPropertyDescriptor(state, key)!;\n const set = desc && (desc.set as (value: any, silent?: boolean) => void);\n\n if (set) {\n set.call(state, data[key], silent);\n } else {\n (state as any)[key] = data[key];\n }\n }\n }\n}\n\n/**\n * Update a property on a state instance and notify listeners.\n *\n * This is used internally to update properties, but can also be used to update properties which are not managed by state, or to update values without triggering setters.\n *\n * If `silent` is true, the update will not dispatch events and will return `false` instead of throwing if state is destroyed.\n */\nfunction update<T>(\n state: State,\n key: State.Event<T>,\n value: T,\n silent?: boolean\n) {\n if (observer(state) === null) {\n if (silent) return false;\n throw new Error(\n `Tried to update ${state}.${String(key)} but state is destroyed.`\n );\n }\n\n const store = STORE.get(state)!;\n\n if (key in store && value === store[key]) return false;\n\n store[key] = value;\n\n if (!silent) event(state, key);\n\n return true;\n}\n\n/** Random alphanumberic of length 6; always starts with a letter. */\nfunction uid() {\n return (0.278 + Math.random() * 0.722)\n .toString(36)\n .substring(2, 8)\n .toUpperCase();\n}\n\nfunction unbind<T extends Function>(fn: T): T;\nfunction unbind<T extends Function>(fn?: T): T | undefined;\nfunction unbind(fn?: Function) {\n return UNBIND.get(fn) || fn;\n}\n\n/**\n * Read or claim a parent for a given child. In read mode (one argument) returns\n * the current parent, or `undefined` if never assigned. In write mode assigns\n * only if unclaimed and returns whether the assignment applied - `true` means\n * this call freshly claimed it, `false` means it was already parented.\n */\nfunction parent(child: object): State | null | undefined;\nfunction parent(child: object, value: State | null): boolean;\nfunction parent(child: object, value?: State | null) {\n if (arguments.length < 2) return PARENT.get(child as State);\n if (PARENT.has(child as State)) return false;\n PARENT.set(child as State, value ?? null);\n return true;\n}\n\nexport { event, unbind, State, parent, STORE, uid, access, update, apply, compute };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,16 @@
1
1
  {
2
2
  "name": "@expressive/mvc",
3
- "version": "0.80.1",
3
+ "version": "0.82.0",
4
+ "description": "Class-based reactive state management - framework-agnostic core of Expressive MVC.",
4
5
  "sideEffects": false,
5
6
  "keywords": [
6
7
  "expressive",
7
8
  "mvc",
8
- "state"
9
+ "state",
10
+ "state-management",
11
+ "reactive",
12
+ "observable",
13
+ "model"
9
14
  ],
10
15
  "license": "MIT",
11
16
  "author": {
@@ -1,16 +0,0 @@
1
- //#region src/field/hot.d.ts
2
- /**
3
- * Wrap an array or object as a reactive Proxy.
4
- *
5
- * Reads register subscriptions in active watch contexts; writes fire keyed events.
6
- * Single-level only - nested objects are not wrapped recursively. Use a child State
7
- * (or a separate `hot()` call) for nested reactivity.
8
- *
9
- * Storage is shared with the input value: external mutation of the original
10
- * collection bypasses dispatch. Pass a fresh value when this matters.
11
- */
12
- declare function hot<T>(value: T[]): T[];
13
- declare function hot<T extends object>(value: T): T;
14
- //#endregion
15
- export { hot };
16
- //# sourceMappingURL=hot.d.ts.map
package/dist/field/hot.js DELETED
@@ -1,98 +0,0 @@
1
- import { event, touch } from "../observable.js";
2
-
3
- //#region src/field/hot.ts
4
- function hot(value) {
5
- if (typeof value !== "object" || !value) throw new Error("hot() requires an array or object");
6
- return Array.isArray(value) ? array(value) : object(value);
7
- }
8
- const MAX_INDEX = 2 ** 32 - 2;
9
- const DELETES = new Set([
10
- "pop",
11
- "shift",
12
- "splice"
13
- ]);
14
- function array(value) {
15
- function index(key) {
16
- const value = typeof key == "symbol" ? -1 : +key;
17
- return String(value) === key && value >= 0 && value <= MAX_INDEX ? value : -1;
18
- }
19
- function deny() {
20
- throw new Error("hot() arrays must be dense. Use undefined/null placeholders or splice().");
21
- }
22
- for (let i = 0; i < value.length; i++) if (!(i in value)) deny();
23
- const get = () => Object.freeze(value.slice());
24
- let internal = false;
25
- const proxy = new Proxy(value, {
26
- has,
27
- get(target, key, receiver) {
28
- if (key === "get") return get;
29
- const result = Reflect.get(target, key, receiver);
30
- if (typeof key === "symbol") return result;
31
- if (typeof result === "function") return DELETES.has(key) ? (...args) => {
32
- internal = true;
33
- try {
34
- return result.apply(receiver, args);
35
- } finally {
36
- internal = false;
37
- }
38
- } : result;
39
- return touch(receiver, key, result);
40
- },
41
- set(target, key, value) {
42
- const oldLength = target.length;
43
- if (key === "length" && value > oldLength) deny();
44
- const grows = index(key) >= target.length;
45
- if (grows && index(key) > target.length) deny();
46
- const ok = assign(proxy, target, key, value);
47
- if (ok && grows && typeof key !== "symbol") event(proxy, "length");
48
- if (ok && key === "length") for (let i = target.length; i < oldLength; i++) event(proxy, String(i));
49
- return ok;
50
- },
51
- deleteProperty(target, key) {
52
- if (!(key in target)) return true;
53
- if (index(key) >= 0 && !internal) deny();
54
- return remove(proxy, target, key);
55
- }
56
- });
57
- event(proxy);
58
- return proxy;
59
- }
60
- function object(value) {
61
- const get = () => Object.freeze({ ...value });
62
- const proxy = new Proxy(value, {
63
- has,
64
- get(target, key, receiver) {
65
- if (key === "get" && !(key in target)) return get;
66
- const result = Reflect.get(target, key, receiver);
67
- if (typeof key === "symbol" || typeof result === "function") return result;
68
- return touch(receiver, key, result);
69
- },
70
- set(target, key, value) {
71
- return assign(proxy, target, key, value);
72
- },
73
- deleteProperty(target, key) {
74
- return remove(proxy, target, key);
75
- }
76
- });
77
- event(proxy);
78
- return proxy;
79
- }
80
- function has(target, key) {
81
- return key === "get" || key in target;
82
- }
83
- function assign(proxy, target, key, value) {
84
- const old = target[key];
85
- const ok = Reflect.set(target, key, value);
86
- if (ok && typeof key !== "symbol" && old !== value) event(proxy, key);
87
- return ok;
88
- }
89
- function remove(proxy, target, key) {
90
- if (!(key in target)) return true;
91
- const ok = Reflect.deleteProperty(target, key);
92
- if (ok && typeof key !== "symbol") event(proxy, key);
93
- return ok;
94
- }
95
-
96
- //#endregion
97
- export { hot };
98
- //# sourceMappingURL=hot.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hot.js","names":[],"sources":["../../src/field/hot.ts"],"sourcesContent":["import { event, touch } from '../observable';\n\n/**\n * Wrap an array or object as a reactive Proxy.\n *\n * Reads register subscriptions in active watch contexts; writes fire keyed events.\n * Single-level only - nested objects are not wrapped recursively. Use a child State\n * (or a separate `hot()` call) for nested reactivity.\n *\n * Storage is shared with the input value: external mutation of the original\n * collection bypasses dispatch. Pass a fresh value when this matters.\n */\nfunction hot<T>(value: T[]): T[];\nfunction hot<T extends object>(value: T): T;\nfunction hot(value: any) {\n if (typeof value !== 'object' || !value)\n throw new Error('hot() requires an array or object');\n\n return Array.isArray(value) ? array(value) : object(value);\n}\n\nconst MAX_INDEX = 2 ** 32 - 2;\nconst DELETES = new Set(['pop', 'shift', 'splice']);\n\nfunction array<T>(value: T[]) {\n function index(key: string | symbol) {\n const value = typeof key == 'symbol' ? -1 : +key;\n return String(value) === key && value >= 0 && value <= MAX_INDEX ? value : -1;\n }\n\n function deny() {\n throw new Error(\n 'hot() arrays must be dense. Use undefined/null placeholders or splice().'\n );\n }\n\n for (let i = 0; i < value.length; i++)\n if (!(i in value)) deny();\n\n const get = () => Object.freeze(value.slice());\n let internal = false;\n\n const proxy: any = new Proxy(value, {\n has,\n get(target: T[], key, receiver) {\n if (key === 'get') return get;\n\n const result = Reflect.get(target, key, receiver);\n\n if (typeof key === 'symbol') return result;\n if (typeof result === 'function')\n return DELETES.has(key) ? (...args: unknown[]) => {\n internal = true;\n\n try {\n return result.apply(receiver, args);\n } finally {\n internal = false;\n }\n } : result;\n\n return touch(receiver, key, result);\n },\n set(target: T[], key, value) {\n const oldLength = target.length;\n if (key === 'length' && value > oldLength) deny();\n\n const grows = index(key) >= target.length;\n if (grows && index(key) > target.length) deny();\n\n const ok = assign(proxy, target, key, value);\n if (ok && grows && typeof key !== 'symbol') event(proxy, 'length');\n if (ok && key === 'length')\n for (let i = target.length; i < oldLength; i++) event(proxy, String(i));\n\n return ok;\n },\n deleteProperty(target, key) {\n if (!(key in target)) return true;\n if (index(key) >= 0 && !internal) deny();\n\n return remove(proxy, target, key);\n }\n });\n\n event(proxy);\n return proxy;\n}\n\nfunction object<T extends object>(value: T) {\n const get = () => Object.freeze({ ...value });\n\n const proxy: any = new Proxy(value, {\n has,\n get(target, key, receiver) {\n if (key === 'get' && !(key in target)) return get;\n\n const result = Reflect.get(target, key, receiver);\n\n if (typeof key === 'symbol' || typeof result === 'function')\n return result;\n\n return touch(receiver, key, result);\n },\n set(target, key, value) {\n return assign(proxy, target, key, value);\n },\n deleteProperty(target, key) {\n return remove(proxy, target, key);\n }\n });\n\n event(proxy);\n return proxy;\n}\n\nfunction has(target: object, key: string | symbol) {\n return key === 'get' || key in target;\n}\n\nfunction assign(proxy: object, target: object, key: string | symbol, value: any) {\n const old = (target as any)[key];\n const ok = Reflect.set(target, key, value);\n\n if (ok && typeof key !== 'symbol' && old !== value) event(proxy, key);\n\n return ok;\n}\n\nfunction remove(proxy: object, target: object, key: string | symbol) {\n if (!(key in target)) return true;\n\n const ok = Reflect.deleteProperty(target, key);\n if (ok && typeof key !== 'symbol') event(proxy, key);\n\n return ok;\n}\n\nexport { hot };\n"],"mappings":"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"}