@expressive/mvc 0.88.0 → 0.89.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 +18 -18
- package/dist/component.d.ts +11 -19
- package/dist/component.js +5 -51
- package/dist/component.js.map +1 -1
- package/dist/context.d.ts +27 -27
- package/dist/context.js +18 -18
- package/dist/context.js.map +1 -1
- package/dist/field/def.d.ts +3 -3
- package/dist/field/def.js +6 -6
- package/dist/field/def.js.map +1 -1
- package/dist/field/get.d.ts +16 -16
- package/dist/field/get.js +3 -3
- package/dist/field/get.js.map +1 -1
- package/dist/field/has.d.ts +8 -8
- package/dist/field/has.js +3 -3
- package/dist/field/has.js.map +1 -1
- package/dist/field/map.d.ts +2 -2
- package/dist/field/map.js +2 -2
- package/dist/field/map.js.map +1 -1
- package/dist/field/ref.d.ts +8 -8
- package/dist/field/ref.js +3 -3
- package/dist/field/ref.js.map +1 -1
- package/dist/field/set.js.map +1 -1
- package/dist/hot.d.ts +4 -4
- package/dist/hot.js +7 -11
- package/dist/hot.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +2 -2
- package/dist/jsx-runtime.js +1 -3
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/observable.d.ts +7 -4
- package/dist/observable.js +94 -27
- package/dist/observable.js.map +1 -1
- package/dist/state.d.ts +110 -96
- package/dist/state.js +53 -39
- package/dist/state.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
The core of [Expressive MVC](https://github.com/gabeklein/expressive-mvc): reactive primitives built around plain classes, with no framework dependency. Provides the `
|
|
14
|
+
The core of [Expressive MVC](https://github.com/gabeklein/expressive-mvc): reactive primitives built around plain classes, with no framework dependency. Provides the `Model` class, 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) alongside this package - the adapter declares `@expressive/mvc` as a peer dependency, so both belong in your `package.json`. Import `
|
|
16
|
+
> **Building a React app?** Install [`@expressive/react`](https://www.npmjs.com/package/@expressive/react) alongside this package - the adapter declares `@expressive/mvc` as a peer dependency, so both belong in your `package.json`. Import `Model`, `Component`, and every instruction from this package, and import the adapter once from your entry so the host registers. The same holds for [`@expressive/dom`](https://www.npmjs.com/package/@expressive/dom), the renderer without React.
|
|
17
17
|
|
|
18
18
|
Installed alone, it serves host-agnostic code - a shared domain package, a Node service, or a new adapter:
|
|
19
19
|
|
|
@@ -21,14 +21,14 @@ Installed alone, it serves host-agnostic code - a shared domain package, a Node
|
|
|
21
21
|
npm install @expressive/mvc
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## Model
|
|
25
25
|
|
|
26
|
-
A `
|
|
26
|
+
A `Model` is a class whose fields are reactive - read to subscribe, assign to update. Getters are cached computed values.
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
|
-
import {
|
|
29
|
+
import { Model } from '@expressive/mvc';
|
|
30
30
|
|
|
31
|
-
class Counter extends
|
|
31
|
+
class Counter extends Model {
|
|
32
32
|
count = 0;
|
|
33
33
|
increment = () => this.count++;
|
|
34
34
|
|
|
@@ -47,7 +47,7 @@ counter.get(({ count }) => {
|
|
|
47
47
|
counter.increment(); // -> "count is 1"
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
Instances run and test anywhere - no renderer required.
|
|
51
51
|
|
|
52
52
|
## Instructions
|
|
53
53
|
|
|
@@ -57,14 +57,14 @@ Field initializers that change how a property behaves:
|
|
|
57
57
|
|---|---|
|
|
58
58
|
| `ref()` | a mutable reference (e.g. a DOM node) that's still reactive |
|
|
59
59
|
| `set()` | computed values, smart setters, side-effects on assignment |
|
|
60
|
-
| `get()` | dependency injection - pull another `
|
|
60
|
+
| `get()` | dependency injection - pull another `Model` from context |
|
|
61
61
|
| `map()` | a shallow-reactive Map |
|
|
62
62
|
| `has()` | an owned reactive collection - an ordered list or a spawned pool |
|
|
63
63
|
|
|
64
64
|
```ts
|
|
65
|
-
import {
|
|
65
|
+
import { Model, set, has, map } from '@expressive/mvc';
|
|
66
66
|
|
|
67
|
-
class Cart extends
|
|
67
|
+
class Cart extends Model {
|
|
68
68
|
items = has<Item>(); // reactive collection
|
|
69
69
|
products = map<string, Product>(); // reactive keyed collection
|
|
70
70
|
coupon = set('', code => apply(code)); // side-effect on assignment
|
|
@@ -74,7 +74,7 @@ class Cart extends State {
|
|
|
74
74
|
## Lifecycle
|
|
75
75
|
|
|
76
76
|
```ts
|
|
77
|
-
class Timer extends
|
|
77
|
+
class Timer extends Model {
|
|
78
78
|
seconds = 0;
|
|
79
79
|
|
|
80
80
|
new() { // runs once on activation
|
|
@@ -89,22 +89,22 @@ timer.set(null); // destroy - runs cleanup, freezes state
|
|
|
89
89
|
|
|
90
90
|
## Context & composition
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
Instances find each other through context, and compose by holding one another:
|
|
93
93
|
|
|
94
94
|
```ts
|
|
95
|
-
import {
|
|
95
|
+
import { Model, get } from '@expressive/mvc';
|
|
96
96
|
|
|
97
|
-
class Session extends
|
|
97
|
+
class Session extends Model {
|
|
98
98
|
user = 'guest';
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
class Profile extends
|
|
101
|
+
class Profile extends Model {
|
|
102
102
|
session = get(Session); // injected from the nearest provider
|
|
103
|
-
address = new Address(); // nested
|
|
103
|
+
address = new Address(); // nested instance - its changes bubble up
|
|
104
104
|
}
|
|
105
105
|
```
|
|
106
106
|
|
|
107
|
-
An adapter (e.g. [`@expressive/react`](https://www.npmjs.com/package/@expressive/react)) supplies the provider that places a `
|
|
107
|
+
An adapter (e.g. [`@expressive/react`](https://www.npmjs.com/package/@expressive/react)) supplies the provider that places a `Model` in a tree; `get()` pulls it back out.
|
|
108
108
|
|
|
109
109
|
## Events
|
|
110
110
|
|
|
@@ -130,7 +130,7 @@ off(); // stop listening
|
|
|
130
130
|
}
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
`Component` - a `
|
|
133
|
+
`Component` - a `Model` usable in JSX - lives here as the agnostic base. For using it in an app (rendering, props, subcomponents, suspense), see **[`@expressive/react`](https://www.npmjs.com/package/@expressive/react)**.
|
|
134
134
|
|
|
135
135
|
---
|
|
136
136
|
|
package/dist/component.d.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Model } from "./state.js";
|
|
2
2
|
import { Host } from "./jsx-runtime.js";
|
|
3
3
|
|
|
4
4
|
//#region src/component.d.ts
|
|
5
5
|
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? A : B;
|
|
6
6
|
/** Keys of T which are settable (excludes get-only accessors and `readonly`). */
|
|
7
7
|
type Acceptable<T> = { [P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P, never> }[keyof T];
|
|
8
|
-
type ForProps<T extends
|
|
8
|
+
type ForProps<T extends Model> = {
|
|
9
9
|
children?: Component.Node;
|
|
10
10
|
fallback?: Component.Node | false;
|
|
11
11
|
catch?: (error: Error, instance: Component) => Promise<void> | void;
|
|
12
12
|
} & ({
|
|
13
|
-
for:
|
|
13
|
+
for: Model.Extends<T>;
|
|
14
14
|
is?: (instance: T) => void;
|
|
15
15
|
} | {
|
|
16
16
|
for: T | undefined;
|
|
17
17
|
is?: never;
|
|
18
|
-
}) & { [K in Exclude<keyof T, keyof
|
|
18
|
+
}) & { [K in Exclude<keyof T, keyof Model> & Acceptable<T>]?: T[K] };
|
|
19
19
|
declare namespace Component {
|
|
20
20
|
/**
|
|
21
21
|
* Host element type produced by `Component.render`. Delegates to the
|
|
@@ -49,22 +49,24 @@ declare namespace Component {
|
|
|
49
49
|
*/
|
|
50
50
|
catch?(error: Error, instance: T): Promise<void> | void;
|
|
51
51
|
}
|
|
52
|
-
type
|
|
52
|
+
type ModelProps<T extends Model> = { [K in Exclude<keyof T, keyof Component> & Acceptable<T>]?: T[K] };
|
|
53
|
+
/** @deprecated Renamed to `ModelProps`. */
|
|
54
|
+
type StateProps<T extends Model> = ModelProps<T>;
|
|
53
55
|
type RenderProps<T> = [T] extends [(props: infer P) => any] ? [keyof NonNullable<P>] extends [never] ? {
|
|
54
56
|
children?: Component.Node;
|
|
55
57
|
} : NonNullable<P> : {
|
|
56
58
|
children?: Component.Node;
|
|
57
59
|
};
|
|
58
|
-
type Props<T extends Component<any>> =
|
|
60
|
+
type Props<T extends Component<any>> = ModelProps<T> & BaseProps<T> & RenderProps<T['render']>;
|
|
59
61
|
}
|
|
60
|
-
declare class Component<P = unknown> extends
|
|
62
|
+
declare class Component<P = unknown> extends Model {
|
|
61
63
|
/**
|
|
62
64
|
* All JSX attributes passed to this component.
|
|
63
65
|
* Includes state-derived props, render props, and built-in props like `is` and `fallback`.
|
|
64
66
|
*
|
|
65
67
|
* Will incorperate extra props you declare as props parameter in `render` method.
|
|
66
68
|
*/
|
|
67
|
-
readonly props: [P] extends [
|
|
69
|
+
readonly props: [P] extends [Model] ? ForProps<P> : Component.Props<this>;
|
|
68
70
|
/** Stable identity used when this instance is rendered in a collection. */
|
|
69
71
|
readonly key: string;
|
|
70
72
|
/**
|
|
@@ -86,8 +88,6 @@ declare class Component<P = unknown> extends State {
|
|
|
86
88
|
* props from JSX, beyond those merged to state properties; declare its shape
|
|
87
89
|
* via `props = {} as { ... }`. Without a parameter (the default below),
|
|
88
90
|
* children pass through.
|
|
89
|
-
*
|
|
90
|
-
* The constructor installs a composed `render` per instance.
|
|
91
91
|
*/
|
|
92
92
|
render(props?: {}): Component.Node;
|
|
93
93
|
/**
|
|
@@ -101,14 +101,6 @@ declare class Component<P = unknown> extends State {
|
|
|
101
101
|
*/
|
|
102
102
|
catch?(error: Error, instance: this): Promise<void> | void;
|
|
103
103
|
}
|
|
104
|
-
/**
|
|
105
|
-
* Render `this` through its class's composed content render: content renders
|
|
106
|
-
* up the prototype chain, each base layer receiving the subclass's as a lazy
|
|
107
|
-
* `children` getter. Falls back to passing `children` through.
|
|
108
|
-
*/
|
|
109
|
-
declare function compose(this: State, props?: {}): any;
|
|
110
|
-
/** Drop composed renders, so each is rebuilt from current prototypes. */
|
|
111
|
-
declare function rechain(): void;
|
|
112
104
|
//#endregion
|
|
113
|
-
export { Component
|
|
105
|
+
export { Component };
|
|
114
106
|
//# sourceMappingURL=component.d.ts.map
|
package/dist/component.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Model, adopt, trailing } from "./state.js";
|
|
2
2
|
import { Context } from "./context.js";
|
|
3
3
|
import { set } from "./field/set.js";
|
|
4
4
|
|
|
5
5
|
//#region src/component.ts
|
|
6
6
|
const PENDING = /* @__PURE__ */ new WeakMap();
|
|
7
|
-
|
|
8
|
-
let CHAIN = /* @__PURE__ */ new WeakMap();
|
|
9
|
-
var Component = class Component extends State {
|
|
7
|
+
var Component = class Component extends Model {
|
|
10
8
|
/**
|
|
11
9
|
* Content to display while this component or its children are suspended.
|
|
12
10
|
* Will cover suspense by pending properties in `this.render` as well.
|
|
@@ -50,11 +48,6 @@ var Component = class Component extends State {
|
|
|
50
48
|
this.set("props", () => {
|
|
51
49
|
this.set(merge(this.props));
|
|
52
50
|
});
|
|
53
|
-
Object.defineProperty(this, "render", {
|
|
54
|
-
writable: true,
|
|
55
|
-
configurable: true,
|
|
56
|
-
value: compose
|
|
57
|
-
});
|
|
58
51
|
}
|
|
59
52
|
/**
|
|
60
53
|
* Output for this component. Override to define custom JSX.
|
|
@@ -64,8 +57,6 @@ var Component = class Component extends State {
|
|
|
64
57
|
* props from JSX, beyond those merged to state properties; declare its shape
|
|
65
58
|
* via `props = {} as { ... }`. Without a parameter (the default below),
|
|
66
59
|
* children pass through.
|
|
67
|
-
*
|
|
68
|
-
* The constructor installs a composed `render` per instance.
|
|
69
60
|
*/
|
|
70
61
|
render(props) {
|
|
71
62
|
const { children } = props || this.props;
|
|
@@ -120,7 +111,7 @@ function provide(self) {
|
|
|
120
111
|
if (next !== input) {
|
|
121
112
|
input = next;
|
|
122
113
|
unmount();
|
|
123
|
-
if (
|
|
114
|
+
if (Model.is(next)) {
|
|
124
115
|
adopt(self, "for", target = new next(rest));
|
|
125
116
|
owned = target;
|
|
126
117
|
is?.(target);
|
|
@@ -128,51 +119,14 @@ function provide(self) {
|
|
|
128
119
|
return;
|
|
129
120
|
}
|
|
130
121
|
owned = void 0;
|
|
131
|
-
adopt(self, "for", target = next instanceof
|
|
122
|
+
adopt(self, "for", target = next instanceof Model ? next : void 0);
|
|
132
123
|
}
|
|
133
124
|
target?.set(rest);
|
|
134
125
|
}
|
|
135
126
|
sync();
|
|
136
127
|
self.set("props", sync);
|
|
137
128
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Render `this` through its class's composed content render: content renders
|
|
140
|
-
* up the prototype chain, each base layer receiving the subclass's as a lazy
|
|
141
|
-
* `children` getter. Falls back to passing `children` through.
|
|
142
|
-
*/
|
|
143
|
-
function compose(props) {
|
|
144
|
-
const type = this.constructor;
|
|
145
|
-
let render = CHAIN.get(type);
|
|
146
|
-
if (!render) {
|
|
147
|
-
for (let T = type; T !== Component && T !== State; T = Object.getPrototypeOf(T)) {
|
|
148
|
-
const next = method(T);
|
|
149
|
-
if (next) render = render ? wrap(next, render) : next;
|
|
150
|
-
}
|
|
151
|
-
if (!render) render = method(Component);
|
|
152
|
-
CHAIN.set(type, render);
|
|
153
|
-
}
|
|
154
|
-
return render.call(this, props);
|
|
155
|
-
}
|
|
156
|
-
function method(from) {
|
|
157
|
-
const desc = Object.getOwnPropertyDescriptor(from.prototype, "render");
|
|
158
|
-
if (desc) return unbind(desc.get || desc.value);
|
|
159
|
-
}
|
|
160
|
-
function wrap(outer, inner) {
|
|
161
|
-
return function(props) {
|
|
162
|
-
const self = this;
|
|
163
|
-
return outer.call(self, {
|
|
164
|
-
...props,
|
|
165
|
-
get children() {
|
|
166
|
-
return inner.call(self, props);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
/** Drop composed renders, so each is rebuilt from current prototypes. */
|
|
172
|
-
function rechain() {
|
|
173
|
-
CHAIN = /* @__PURE__ */ new WeakMap();
|
|
174
|
-
}
|
|
175
129
|
|
|
176
130
|
//#endregion
|
|
177
|
-
export { Component
|
|
131
|
+
export { Component };
|
|
178
132
|
//# sourceMappingURL=component.js.map
|
package/dist/component.js.map
CHANGED
|
@@ -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, adopt, trailing, unbind } from './state';\n\nimport type { Host } from './jsx-runtime';\n\nconst PENDING = new WeakMap<object, Component<any>>();\n\n/** Per-class composed content render. */\nlet 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\ntype ForProps<T extends State> = {\n children?: Component.Node;\n fallback?: Component.Node | false;\n catch?: (error: Error, instance: Component) => Promise<void> | void;\n} & (\n | { for: State.Extends<T>; is?: (instance: T) => void }\n | { for: T | undefined; is?: never }\n) & { [K in Exclude<keyof T, keyof State> & Acceptable<T>]?: T[K] };\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<any>> {\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 * Called when this element's content throws, in place of the class's own\n * `catch`. A returned promise retries once settled.\n */\n catch?(error: Error, instance: T): Promise<void> | void;\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<any>> =\n & StateProps<T>\n & BaseProps<T>\n & RenderProps<T['render']>;\n}\n\nclass Component<P = unknown> 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: [P] extends [State] ? ForProps<P> : 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 twin = PENDING.get(props);\n const provider = new.target === Component && 'for' in 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([\n props,\n rest.filter((x) => !(x instanceof Context)),\n () => {\n const other = twin || PENDING.get(props)!;\n\n if (other !== this) {\n trailing(other).forEach((orphan) => orphan.set(null));\n other.set(null);\n }\n\n if (provider) provide(this);\n else props.is?.(this);\n\n Object.defineProperty(this, 'props', { enumerable: false });\n PENDING.delete(props);\n }\n ]);\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: compose\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.\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, instance: this): 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\nComponent.on({\n setup(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});\n\nfunction provide(self: Component<any>) {\n let input: unknown;\n let target: State | undefined;\n let owned: { mount?(): unknown } | undefined;\n let mounted = false;\n let release: unknown;\n\n function unmount() {\n if (typeof release == 'function') release();\n release = undefined;\n }\n\n const inherited = (self as { mount?(): unknown }).mount;\n\n Object.defineProperty(self, 'mount', {\n configurable: true,\n value() {\n const done = inherited?.call(self);\n\n mounted = true;\n release = owned?.mount?.();\n\n return () => {\n mounted = false;\n unmount();\n if (typeof done == 'function') done();\n };\n }\n });\n\n function sync() {\n const { for: next, is, children, fallback, catch: _catch, ...rest } = self.props as Record<string, any>;\n\n if (fallback === undefined) self.fallback = false;\n\n if (next !== input) {\n input = next;\n unmount();\n\n if (State.is(next)) {\n adopt(self, 'for', (target = new (next as State.Type)(rest)));\n owned = target as typeof owned;\n is?.(target);\n if (mounted) release = owned!.mount?.();\n return;\n }\n\n owned = undefined;\n adopt(self, 'for', (target = next instanceof State ? next : undefined));\n }\n\n target?.set(rest);\n }\n\n sync();\n self.set('props', sync);\n}\n\n/**\n * Render `this` through its class's composed content render: content renders\n * up the prototype chain, each base layer receiving the subclass's as a lazy\n * `children` getter. Falls back to passing `children` through.\n */\nfunction compose(this: State, props?: {}) {\n const type = this.constructor;\n let render = CHAIN.get(type);\n\n if (!render) {\n for (let T = type; T !== Component && T !== State; T = Object.getPrototypeOf(T)) {\n const next = method(T);\n\n if (next) render = render ? wrap(next, render) : next;\n }\n\n if (!render) render = method(Component)!;\n\n CHAIN.set(type, render);\n }\n\n return render.call(this, props);\n}\n\nfunction method(from: Function) {\n const desc = Object.getOwnPropertyDescriptor(from.prototype, 'render');\n if (desc) return unbind(desc.get || desc.value) as Function;\n}\n\nfunction wrap(outer: Function, inner: Function): Function {\n return function (this: State, 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\n/** Drop composed renders, so each is rebuilt from current prototypes. */\nfunction rechain() {\n CHAIN = new WeakMap();\n}\n\nexport { Component, compose, rechain };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"component.js","names":[],"sources":["../src/component.ts"],"sourcesContent":["import { Context } from './context';\nimport { set } from './field/set';\nimport { Model, adopt, trailing } from './state';\n\nimport type { Host } from './jsx-runtime';\n\nconst PENDING = new WeakMap<object, Component<any>>();\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\ntype ForProps<T extends Model> = {\n children?: Component.Node;\n fallback?: Component.Node | false;\n catch?: (error: Error, instance: Component) => Promise<void> | void;\n} & (\n | { for: Model.Extends<T>; is?: (instance: T) => void }\n | { for: T | undefined; is?: never }\n) & { [K in Exclude<keyof T, keyof Model> & Acceptable<T>]?: T[K] };\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<any>> {\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 * Called when this element's content throws, in place of the class's own\n * `catch`. A returned promise retries once settled.\n */\n catch?(error: Error, instance: T): Promise<void> | void;\n }\n\n type ModelProps<T extends Model> = {\n [K in Exclude<keyof T, keyof Component> & Acceptable<T>]?: T[K];\n };\n\n /** @deprecated Renamed to `ModelProps`. */\n type StateProps<T extends Model> = ModelProps<T>;\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<any>> =\n & ModelProps<T>\n & BaseProps<T>\n & RenderProps<T['render']>;\n}\n\nclass Component<P = unknown> extends Model {\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: [P] extends [Model] ? ForProps<P> : 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 twin = PENDING.get(props);\n const provider = new.target === Component && 'for' in 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([\n props,\n rest.filter((x) => !(x instanceof Context)),\n () => {\n const other = twin || PENDING.get(props)!;\n\n if (other !== this) {\n trailing(other).forEach((orphan) => orphan.set(null));\n other.set(null);\n }\n\n if (provider) provide(this);\n else props.is?.(this);\n\n Object.defineProperty(this, 'props', { enumerable: false });\n PENDING.delete(props);\n }\n ]);\n\n PENDING.set(props, this);\n\n this.props = props;\n this.set('props', () => {\n this.set(merge(this.props));\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 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, instance: this): 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\nComponent.on({\n setup(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});\n\nfunction provide(self: Component<any>) {\n let input: unknown;\n let target: Model | undefined;\n let owned: { mount?(): unknown } | undefined;\n let mounted = false;\n let release: unknown;\n\n function unmount() {\n if (typeof release == 'function') release();\n release = undefined;\n }\n\n const inherited = (self as { mount?(): unknown }).mount;\n\n Object.defineProperty(self, 'mount', {\n configurable: true,\n value() {\n const done = inherited?.call(self);\n\n mounted = true;\n release = owned?.mount?.();\n\n return () => {\n mounted = false;\n unmount();\n if (typeof done == 'function') done();\n };\n }\n });\n\n function sync() {\n const { for: next, is, children, fallback, catch: _catch, ...rest } = self.props as Record<string, any>;\n\n if (fallback === undefined) self.fallback = false;\n\n if (next !== input) {\n input = next;\n unmount();\n\n if (Model.is(next)) {\n adopt(self, 'for', (target = new (next as Model.Type)(rest)));\n owned = target as typeof owned;\n is?.(target);\n if (mounted) release = owned!.mount?.();\n return;\n }\n\n owned = undefined;\n adopt(self, 'for', (target = next instanceof Model ? next : undefined));\n }\n\n target?.set(rest);\n }\n\n sync();\n self.set('props', sync);\n}\n\nexport { Component };\n"],"mappings":";;;;;AAMA,MAAM,0BAAU,IAAI,SAAiC;AA8ErD,IAAM,YAAN,MAAM,kBAA+B,MAAM;;;;;;;;;;CAqBzC,WAA2B,IAAI,KAAK;CAEpC,YAAY,OAAa,GAAG,MAAa;AACvC,MAAI,SAAS,KAAM,SAAQ,EAAE;EAE7B,MAAM,OAAO,EAAE;EACf,MAAM,OAAO,QAAQ,IAAI,MAAM;EAC/B,MAAM,WAAW,IAAI,WAAW,aAAa,SAAS;AAEtD,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;GACJ;GACA,KAAK,QAAQ,MAAM,EAAE,aAAa,SAAS;SACrC;IACJ,MAAM,QAAQ,QAAQ,QAAQ,IAAI,MAAM;AAExC,QAAI,UAAU,MAAM;AAClB,cAAS,MAAM,CAAC,SAAS,WAAW,OAAO,IAAI,KAAK,CAAC;AACrD,WAAM,IAAI,KAAK;;AAGjB,QAAI,SAAU,SAAQ,KAAK;QACtB,OAAM,KAAK,KAAK;AAErB,WAAO,eAAe,MAAM,SAAS,EAAE,YAAY,OAAO,CAAC;AAC3D,YAAQ,OAAO,MAAM;;GAExB,CAAC;AAEF,UAAQ,IAAI,OAAO,KAAK;AAExB,OAAK,QAAQ;AACb,OAAK,IAAI,eAAe;AACtB,QAAK,IAAI,MAAM,KAAK,MAAM,CAAC;IAC3B;;;;;;;;;;;CAYJ,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;AAEF,UAAU,GAAG,EACX,MAAM,MAAM;AAGV,KAFY,OAAO,yBAAyB,MAAM,MAAM,EAE/C,aACP,QAAO,eAAe,MAAM,OAAO;EACjC,YAAY;EACZ,UAAU;EACX,CAAC;GAEP,CAAC;AAEF,SAAS,QAAQ,MAAsB;CACrC,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU;CACd,IAAI;CAEJ,SAAS,UAAU;AACjB,MAAI,OAAO,WAAW,WAAY,UAAS;AAC3C,YAAU;;CAGZ,MAAM,YAAa,KAA+B;AAElD,QAAO,eAAe,MAAM,SAAS;EACnC,cAAc;EACd,QAAQ;GACN,MAAM,OAAO,WAAW,KAAK,KAAK;AAElC,aAAU;AACV,aAAU,OAAO,SAAS;AAE1B,gBAAa;AACX,cAAU;AACV,aAAS;AACT,QAAI,OAAO,QAAQ,WAAY,OAAM;;;EAG1C,CAAC;CAEF,SAAS,OAAO;EACd,MAAM,EAAE,KAAK,MAAM,IAAI,UAAU,UAAU,OAAO,QAAQ,GAAG,SAAS,KAAK;AAE3E,MAAI,aAAa,OAAW,MAAK,WAAW;AAE5C,MAAI,SAAS,OAAO;AAClB,WAAQ;AACR,YAAS;AAET,OAAI,MAAM,GAAG,KAAK,EAAE;AAClB,UAAM,MAAM,OAAQ,SAAS,IAAK,KAAoB,KAAK,CAAE;AAC7D,YAAQ;AACR,SAAK,OAAO;AACZ,QAAI,QAAS,WAAU,MAAO,SAAS;AACvC;;AAGF,WAAQ;AACR,SAAM,MAAM,OAAQ,SAAS,gBAAgB,QAAQ,OAAO,OAAW;;AAGzE,UAAQ,IAAI,KAAK;;AAGnB,OAAM;AACN,MAAK,IAAI,SAAS,KAAK"}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Model } from "./state.js";
|
|
2
2
|
|
|
3
3
|
//#region src/context.d.ts
|
|
4
|
-
type Accept<T extends
|
|
5
|
-
type Expect<T extends
|
|
4
|
+
type Accept<T extends Model = Model> = T | Model.Type<T> | Record<string | number, T | Model.Type<T>>;
|
|
5
|
+
type Expect<T extends Model = Model> = (state: T, downstream: boolean) => (() => void) | void;
|
|
6
6
|
declare namespace Context {
|
|
7
7
|
export { Accept, Expect };
|
|
8
8
|
}
|
|
9
9
|
declare class Context {
|
|
10
10
|
static get root(): Context;
|
|
11
|
-
/** Get the context for a
|
|
12
|
-
static get(state?:
|
|
11
|
+
/** Get the context for a Model. Adapters may override to provide framework context. */
|
|
12
|
+
static get(state?: Model): Context;
|
|
13
13
|
id: string;
|
|
14
14
|
parent?: Context;
|
|
15
15
|
scope: Set<Context>;
|
|
16
|
-
consume: Map<
|
|
17
|
-
provide: Map<
|
|
18
|
-
protected inputs: Record<string | number,
|
|
16
|
+
consume: Map<Model.Extends<Model>, Set<[Expect<Model>, boolean | undefined]> | null>;
|
|
17
|
+
provide: Map<Model.Extends<Model>, Set<[Model, boolean]> | null>;
|
|
18
|
+
protected inputs: Record<string | number, Model | Model.Extends>;
|
|
19
19
|
private cleanup;
|
|
20
20
|
constructor(arg?: Context | Accept);
|
|
21
21
|
private traverse;
|
|
22
22
|
private register;
|
|
23
23
|
/** Find specified type upstream. Throws if not found. */
|
|
24
|
-
get<T extends
|
|
24
|
+
get<T extends Model>(Type: Model.Extends<T>, required?: true): T;
|
|
25
25
|
/** Find specified type upstream. Returns undefined if not found. */
|
|
26
|
-
get<T extends
|
|
26
|
+
get<T extends Model>(Type: Model.Extends<T>, required?: boolean): T | undefined;
|
|
27
27
|
/** Subscribe to a type becoming available in context. */
|
|
28
|
-
get<T extends
|
|
29
|
-
/** Lookup excluding a specific instance (used by
|
|
30
|
-
get<T extends
|
|
28
|
+
get<T extends Model>(Type: Model.Extends<T>, callback: Expect<T>, downstream?: boolean): () => void;
|
|
29
|
+
/** Lookup excluding a specific instance (used by Model.prototype.get for upstream-of-self semantics). */
|
|
30
|
+
get<T extends Model>(Type: Model.Extends<T>, arg: boolean | Expect<T> | undefined, downstream: boolean | undefined, skip: Model): T | undefined | (() => void);
|
|
31
31
|
/**
|
|
32
|
-
* Register one or more
|
|
32
|
+
* Register one or more Models to this context.
|
|
33
33
|
*
|
|
34
|
-
* Context will add or remove
|
|
34
|
+
* Context will add or remove Models as needed to keep with provided input.
|
|
35
35
|
*
|
|
36
|
-
* @param inputs
|
|
37
|
-
* @param forEach Optional callback to run for each
|
|
38
|
-
* whether this context created the
|
|
36
|
+
* @param inputs Model, Model class, or map of Models / Model classes to register.
|
|
37
|
+
* @param forEach Optional callback to run for each Model registered. Receives
|
|
38
|
+
* whether this context created the Model - and so will destroy it - as
|
|
39
39
|
* opposed to it having been provided already active. May return a callback
|
|
40
40
|
* to run before that entry is dropped.
|
|
41
41
|
*/
|
|
42
|
-
set<T extends
|
|
43
|
-
add(I:
|
|
42
|
+
set<T extends Model>(inputs: Accept<T>, forEach?: (state: T, owned: boolean) => (() => void) | void): this;
|
|
43
|
+
add(I: Model, explicit?: boolean): () => void;
|
|
44
44
|
/**
|
|
45
|
-
* Create a child context, optionally registering one or more
|
|
45
|
+
* Create a child context, optionally registering one or more Models to it.
|
|
46
46
|
*
|
|
47
|
-
* @param inputs
|
|
47
|
+
* @param inputs Model, Model class, or map of Models / Model classes to register.
|
|
48
48
|
*/
|
|
49
49
|
push(inputs?: Accept): Context;
|
|
50
50
|
/**
|
|
51
|
-
* Remove all
|
|
51
|
+
* Remove all Models from this context.
|
|
52
52
|
*
|
|
53
|
-
* Will also run any cleanup callbacks registered when
|
|
53
|
+
* Will also run any cleanup callbacks registered when Models were added.
|
|
54
54
|
*/
|
|
55
55
|
pop(): void;
|
|
56
56
|
}
|
|
@@ -58,9 +58,9 @@ declare class Context {
|
|
|
58
58
|
* Add `value` to the context `state` was added to. A context-less `state` holds
|
|
59
59
|
* it instead - every context adding `state` later adds its held children too.
|
|
60
60
|
*/
|
|
61
|
-
declare function join(state:
|
|
62
|
-
/** The
|
|
63
|
-
declare function host(state:
|
|
61
|
+
declare function join(state: Model, value: Model): () => void;
|
|
62
|
+
/** The Model a host mounted `state` under - the one its nearest enclosing context was set up for. */
|
|
63
|
+
declare function host(state: Model): Model | undefined;
|
|
64
64
|
//#endregion
|
|
65
65
|
export { Context, host, join };
|
|
66
66
|
//# sourceMappingURL=context.d.ts.map
|
package/dist/context.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { event, listener } from "./observable.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Model, uid } from "./state.js";
|
|
3
3
|
|
|
4
4
|
//#region src/context.ts
|
|
5
5
|
const LOOKUP = /* @__PURE__ */ new WeakMap();
|
|
@@ -10,7 +10,7 @@ var Context = class Context {
|
|
|
10
10
|
static get root() {
|
|
11
11
|
return ROOT ??= new Context();
|
|
12
12
|
}
|
|
13
|
-
/** Get the context for a
|
|
13
|
+
/** Get the context for a Model. Adapters may override to provide framework context. */
|
|
14
14
|
static get(state) {
|
|
15
15
|
return state && LOOKUP.get(state.is) || Context.root;
|
|
16
16
|
}
|
|
@@ -79,21 +79,21 @@ var Context = class Context {
|
|
|
79
79
|
if (arg !== false) throw new Error(`Could not find ${Type} in context.`);
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* Register one or more
|
|
82
|
+
* Register one or more Models to this context.
|
|
83
83
|
*
|
|
84
|
-
* Context will add or remove
|
|
84
|
+
* Context will add or remove Models as needed to keep with provided input.
|
|
85
85
|
*
|
|
86
|
-
* @param inputs
|
|
87
|
-
* @param forEach Optional callback to run for each
|
|
88
|
-
* whether this context created the
|
|
86
|
+
* @param inputs Model, Model class, or map of Models / Model classes to register.
|
|
87
|
+
* @param forEach Optional callback to run for each Model registered. Receives
|
|
88
|
+
* whether this context created the Model - and so will destroy it - as
|
|
89
89
|
* opposed to it having been provided already active. May return a callback
|
|
90
90
|
* to run before that entry is dropped.
|
|
91
91
|
*/
|
|
92
92
|
set(inputs, forEach) {
|
|
93
93
|
const init = /* @__PURE__ */ new Set();
|
|
94
94
|
const { cleanup } = this;
|
|
95
|
-
const single = typeof inputs == "function" || inputs instanceof
|
|
96
|
-
if (typeof inputs == "function" || inputs instanceof
|
|
95
|
+
const single = typeof inputs == "function" || inputs instanceof Model;
|
|
96
|
+
if (typeof inputs == "function" || inputs instanceof Model) inputs = { [0]: inputs };
|
|
97
97
|
for (const K of Object.keys({
|
|
98
98
|
...this.inputs,
|
|
99
99
|
...inputs
|
|
@@ -106,10 +106,10 @@ var Context = class Context {
|
|
|
106
106
|
cleanup.delete(K);
|
|
107
107
|
}
|
|
108
108
|
if (!V) continue;
|
|
109
|
-
const owned =
|
|
110
|
-
if (!(owned || V instanceof
|
|
109
|
+
const owned = Model.is(V);
|
|
110
|
+
if (!(owned || V instanceof Model)) {
|
|
111
111
|
const as = K == "0" || K == String(V) ? V : `${V} (as '${K}')`;
|
|
112
|
-
throw new Error(`Context can only include an instance or class of
|
|
112
|
+
throw new Error(`Context can only include an instance or class of Model but got ${as}.`);
|
|
113
113
|
}
|
|
114
114
|
const state = owned ? new V() : V.is;
|
|
115
115
|
const remove = this.add(state, true);
|
|
@@ -138,7 +138,7 @@ var Context = class Context {
|
|
|
138
138
|
for (const entry of entries) if (!entry[1] && entry[0] !== I) return entries.delete(entry);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
-
for (let T = I.constructor; T !==
|
|
141
|
+
for (let T = I.constructor; T !== Model; T = Object.getPrototypeOf(T)) if (explicit || !root || !conflict(T)) TT.push(T);
|
|
142
142
|
const expects = /* @__PURE__ */ new Map();
|
|
143
143
|
const onDone = /* @__PURE__ */ new Set();
|
|
144
144
|
for (const T of TT) onDone.add(this.register(T, [I, explicit]));
|
|
@@ -185,9 +185,9 @@ var Context = class Context {
|
|
|
185
185
|
return remove;
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
188
|
-
* Create a child context, optionally registering one or more
|
|
188
|
+
* Create a child context, optionally registering one or more Models to it.
|
|
189
189
|
*
|
|
190
|
-
* @param inputs
|
|
190
|
+
* @param inputs Model, Model class, or map of Models / Model classes to register.
|
|
191
191
|
*/
|
|
192
192
|
push(inputs) {
|
|
193
193
|
const next = new Context(this);
|
|
@@ -195,9 +195,9 @@ var Context = class Context {
|
|
|
195
195
|
return next;
|
|
196
196
|
}
|
|
197
197
|
/**
|
|
198
|
-
* Remove all
|
|
198
|
+
* Remove all Models from this context.
|
|
199
199
|
*
|
|
200
|
-
* Will also run any cleanup callbacks registered when
|
|
200
|
+
* Will also run any cleanup callbacks registered when Models were added.
|
|
201
201
|
*/
|
|
202
202
|
pop() {
|
|
203
203
|
this.inputs = {};
|
|
@@ -231,7 +231,7 @@ function join(state, value) {
|
|
|
231
231
|
});
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
|
-
/** The
|
|
234
|
+
/** The Model a host mounted `state` under - the one its nearest enclosing context was set up for. */
|
|
235
235
|
function host(state) {
|
|
236
236
|
for (let ctx = LOOKUP.get(state); ctx; ctx = ctx.parent) {
|
|
237
237
|
const found = HOST.get(ctx);
|
package/dist/context.js.map
CHANGED
|
@@ -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>();\nconst HELD = new WeakMap<State, Map<State, Set<() => void>>>();\nconst HOST = new WeakMap<Context, State>();\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 const single = typeof inputs == \"function\" || inputs instanceof State;\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 if (single && this !== ROOT) HOST.set(this, state);\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) 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, same?: 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 (same || filter == null || filter === downstream)\n expects.set(cb, () => {\n const r = cb(I, filter ?? downstream);\n if (r) onDone.add(r);\n });\n }\n return found;\n }\n\n queue(this, true, true);\n for (let ctx = this.parent; 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 const held = Array.from(HELD.get(I) || [], ([child, added]) => {\n const done = this.add(child);\n added.add(done);\n return () => {\n if (added.delete(done)) done();\n };\n });\n\n function remove() {\n cleanup.delete(remove);\n held.forEach((done) => done());\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\n/**\n * Add `value` to the context `state` was added to. A context-less `state` holds\n * it instead - every context adding `state` later adds its held children too.\n */\nfunction join(state: State, value: State): () => void {\n const ctx = LOOKUP.get(state.is);\n\n if (ctx) return ctx.add(value);\n\n const held = HELD.get(state) || new Map();\n const added = new Set<() => void>();\n\n HELD.set(state, held.set(value, added));\n\n return () => {\n held.delete(value);\n added.forEach((done) => {\n added.delete(done);\n done();\n });\n };\n}\n\n/** The State a host mounted `state` under - the one its nearest enclosing context was set up for. */\nfunction host(state: State) {\n for (let ctx = LOOKUP.get(state); ctx; ctx = ctx.parent) {\n const found = HOST.get(ctx);\n if (found && found !== state) return found;\n }\n}\n\nexport { Context, host, join };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"context.js","names":[],"sources":["../src/context.ts"],"sourcesContent":["import { listener } from \"./observable\";\nimport { event, Model, uid } from \"./state\";\n\nconst LOOKUP = new WeakMap<Model, Context>();\nconst HELD = new WeakMap<Model, Map<Model, Set<() => void>>>();\nconst HOST = new WeakMap<Context, Model>();\nlet ROOT: Context;\n\ntype Accept<T extends Model = Model> =\n | T\n | Model.Type<T>\n | Record<string | number, T | Model.Type<T>>;\n\ntype Expect<T extends Model = Model> = (\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 Model. Adapters may override to provide framework context. */\n static get(state?: Model): 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 Model.Extends,\n Set<[Expect, boolean | undefined]> | null\n >();\n public provide = new Map<Model.Extends, Set<[Model, boolean]> | null>();\n\n protected inputs: Record<string | number, Model | Model.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 Model>(\n type: Model.Extends<T>,\n value: [Expect<T>, boolean | undefined],\n asConsumer: true,\n ): () => void;\n\n private register<T extends Model>(\n type: Model.Extends<T>,\n value: [T, boolean],\n asConsumer?: false,\n ): () => void;\n\n private register(type: Model.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 Model>(Type: Model.Extends<T>, required?: true): T;\n\n /** Find specified type upstream. Returns undefined if not found. */\n public get<T extends Model>(\n Type: Model.Extends<T>,\n required?: boolean,\n ): T | undefined;\n\n /** Subscribe to a type becoming available in context. */\n public get<T extends Model>(\n Type: Model.Extends<T>,\n callback: Expect<T>,\n downstream?: boolean,\n ): () => void;\n\n /** Lookup excluding a specific instance (used by Model.prototype.get for upstream-of-self semantics). */\n public get<T extends Model>(\n Type: Model.Extends<T>,\n arg: boolean | Expect<T> | undefined,\n downstream: boolean | undefined,\n skip: Model,\n ): T | undefined | (() => void);\n\n public get<T extends Model>(\n Type: Model.Extends<T>,\n arg?: boolean | Expect<T>,\n downstream?: boolean,\n skip?: Model,\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 Models to this context.\n *\n * Context will add or remove Models as needed to keep with provided input.\n *\n * @param inputs Model, Model class, or map of Models / Model classes to register.\n * @param forEach Optional callback to run for each Model registered. Receives\n * whether this context created the Model - 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 Model>(\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 const single = typeof inputs == \"function\" || inputs instanceof Model;\n\n if (typeof inputs == \"function\" || inputs instanceof Model)\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 = Model.is(V);\n\n if (!(owned || V instanceof Model)) {\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 Model but got ${as}.`,\n );\n }\n\n const state = owned ? new (V as Model.Type)() : V.is;\n const remove = this.add(state, true);\n\n if (single && this !== ROOT) HOST.set(this, state);\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: Model, explicit = false) {\n const { cleanup, provide } = this;\n const root = this === Context.root;\n const TT: Model.Extends[] = [];\n\n function conflict(T: Model.Extends) {\n const entries = provide.get(T);\n if (entries)\n for (const entry of entries)\n if (!entry[1] && entry[0] !== I) return entries.delete(entry);\n }\n\n for (\n let T = I.constructor as Model.Extends;\n T !== Model;\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, same?: 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 (same || filter == null || filter === downstream)\n expects.set(cb, () => {\n const r = cb(I, filter ?? downstream);\n if (r) onDone.add(r);\n });\n }\n return found;\n }\n\n queue(this, true, true);\n for (let ctx = this.parent; 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 const held = Array.from(HELD.get(I) || [], ([child, added]) => {\n const done = this.add(child);\n added.add(done);\n return () => {\n if (added.delete(done)) done();\n };\n });\n\n function remove() {\n cleanup.delete(remove);\n held.forEach((done) => done());\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 Models to it.\n *\n * @param inputs Model, Model class, or map of Models / Model 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 Models from this context.\n *\n * Will also run any cleanup callbacks registered when Models 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\n/**\n * Add `value` to the context `state` was added to. A context-less `state` holds\n * it instead - every context adding `state` later adds its held children too.\n */\nfunction join(state: Model, value: Model): () => void {\n const ctx = LOOKUP.get(state.is);\n\n if (ctx) return ctx.add(value);\n\n const held = HELD.get(state) || new Map();\n const added = new Set<() => void>();\n\n HELD.set(state, held.set(value, added));\n\n return () => {\n held.delete(value);\n added.forEach((done) => {\n added.delete(done);\n done();\n });\n };\n}\n\n/** The Model a host mounted `state` under - the one its nearest enclosing context was set up for. */\nfunction host(state: Model) {\n for (let ctx = LOOKUP.get(state); ctx; ctx = ctx.parent) {\n const found = HOST.get(ctx);\n if (found && found !== state) return found;\n }\n}\n\nexport { Context, host, join };\n"],"mappings":"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"}
|
package/dist/field/def.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Model } from "../state.js";
|
|
2
2
|
|
|
3
3
|
//#region src/field/def.d.ts
|
|
4
4
|
declare namespace def {
|
|
@@ -6,8 +6,8 @@ declare namespace def {
|
|
|
6
6
|
* Property initializer, will run upon instance creation.
|
|
7
7
|
* Optional returned callback will run when once upon first access.
|
|
8
8
|
*/
|
|
9
|
-
type Factory<T = any, M extends
|
|
10
|
-
interface Config<T = any> extends
|
|
9
|
+
type Factory<T = any, M extends Model = any> = (this: M, key: Extract<Model.Field<M>, string>, thisArg: M, state: Model.Values<M>) => def.Config<T> | (() => void) | void;
|
|
10
|
+
interface Config<T = any> extends Model.Apply<T> {
|
|
11
11
|
destroy?: () => void;
|
|
12
12
|
}
|
|
13
13
|
}
|