@expressive/mvc 0.87.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 +19 -19
- package/dist/component.d.ts +28 -16
- package/dist/component.js +53 -72
- package/dist/component.js.map +1 -1
- package/dist/context.d.ts +28 -26
- package/dist/context.js +45 -27
- package/dist/context.js.map +1 -1
- package/dist/dispatch.d.ts +6 -1
- package/dist/dispatch.js +14 -6
- package/dist/dispatch.js.map +1 -1
- package/dist/field/def.d.ts +3 -3
- package/dist/field/def.js +7 -7
- package/dist/field/def.js.map +1 -1
- package/dist/field/get.d.ts +16 -16
- package/dist/field/get.js +21 -5
- 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 +1 -1
- package/dist/field/set.js.map +1 -1
- package/dist/hot.d.ts +5 -8
- package/dist/hot.js +126 -18
- package/dist/hot.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/jsx-dev-runtime.d.ts +1 -2
- package/dist/jsx-dev-runtime.js +1 -1
- package/dist/jsx-runtime.d.ts +77 -4
- package/dist/jsx-runtime.js +67 -2
- package/dist/jsx-runtime.js.map +1 -0
- 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 +154 -112
- package/dist/state.js +115 -192
- package/dist/state.js.map +1 -1
- package/package.json +7 -5
- package/dist/_virtual/_rolldown/runtime.js +0 -18
- package/dist/caught.d.ts +0 -5237
- package/dist/caught.js +0 -64
- package/dist/caught.js.map +0 -1
- package/dist/runtime.d.ts +0 -72
- package/dist/runtime.js +0 -70
- package/dist/runtime.js.map +0 -1
package/dist/context.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { State, uid } from "./state.js";
|
|
2
1
|
import { event, listener } from "./observable.js";
|
|
2
|
+
import { Model, uid } from "./state.js";
|
|
3
3
|
|
|
4
4
|
//#region src/context.ts
|
|
5
5
|
const LOOKUP = /* @__PURE__ */ new WeakMap();
|
|
6
6
|
const HELD = /* @__PURE__ */ new WeakMap();
|
|
7
|
+
const HOST = /* @__PURE__ */ new WeakMap();
|
|
7
8
|
let ROOT;
|
|
8
9
|
var Context = class Context {
|
|
9
10
|
static get root() {
|
|
10
11
|
return ROOT ??= new Context();
|
|
11
12
|
}
|
|
12
|
-
/** Get the context for a
|
|
13
|
+
/** Get the context for a Model. Adapters may override to provide framework context. */
|
|
13
14
|
static get(state) {
|
|
14
15
|
return state && LOOKUP.get(state.is) || Context.root;
|
|
15
16
|
}
|
|
@@ -78,20 +79,21 @@ var Context = class Context {
|
|
|
78
79
|
if (arg !== false) throw new Error(`Could not find ${Type} in context.`);
|
|
79
80
|
}
|
|
80
81
|
/**
|
|
81
|
-
* Register one or more
|
|
82
|
+
* Register one or more Models to this context.
|
|
82
83
|
*
|
|
83
|
-
* Context will add or remove
|
|
84
|
+
* Context will add or remove Models as needed to keep with provided input.
|
|
84
85
|
*
|
|
85
|
-
* @param inputs
|
|
86
|
-
* @param forEach Optional callback to run for each
|
|
87
|
-
* 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
|
|
88
89
|
* opposed to it having been provided already active. May return a callback
|
|
89
90
|
* to run before that entry is dropped.
|
|
90
91
|
*/
|
|
91
92
|
set(inputs, forEach) {
|
|
92
93
|
const init = /* @__PURE__ */ new Set();
|
|
93
94
|
const { cleanup } = this;
|
|
94
|
-
|
|
95
|
+
const single = typeof inputs == "function" || inputs instanceof Model;
|
|
96
|
+
if (typeof inputs == "function" || inputs instanceof Model) inputs = { [0]: inputs };
|
|
95
97
|
for (const K of Object.keys({
|
|
96
98
|
...this.inputs,
|
|
97
99
|
...inputs
|
|
@@ -104,13 +106,14 @@ var Context = class Context {
|
|
|
104
106
|
cleanup.delete(K);
|
|
105
107
|
}
|
|
106
108
|
if (!V) continue;
|
|
107
|
-
const owned =
|
|
108
|
-
if (!(owned || V instanceof
|
|
109
|
+
const owned = Model.is(V);
|
|
110
|
+
if (!(owned || V instanceof Model)) {
|
|
109
111
|
const as = K == "0" || K == String(V) ? V : `${V} (as '${K}')`;
|
|
110
|
-
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}.`);
|
|
111
113
|
}
|
|
112
114
|
const state = owned ? new V() : V.is;
|
|
113
115
|
const remove = this.add(state, true);
|
|
116
|
+
if (single && this !== ROOT) HOST.set(this, state);
|
|
114
117
|
init.add(() => {
|
|
115
118
|
event(state);
|
|
116
119
|
const dispose = forEach?.(state, owned);
|
|
@@ -132,15 +135,10 @@ var Context = class Context {
|
|
|
132
135
|
function conflict(T) {
|
|
133
136
|
const entries = provide.get(T);
|
|
134
137
|
if (entries) {
|
|
135
|
-
for (const entry of entries) if (!entry[1] && entry[0] !== I)
|
|
136
|
-
const type = I.constructor;
|
|
137
|
-
const g = type.global;
|
|
138
|
-
if (entry[0].constructor === type && (typeof g == "function" ? g(I) : g)) throw new Error(`Cannot register ${I} as a global - ${entry[0]} already exists in root. Destroy the existing instance first, or provide additional ones via explicit context.`);
|
|
139
|
-
return entries.delete(entry);
|
|
140
|
-
}
|
|
138
|
+
for (const entry of entries) if (!entry[1] && entry[0] !== I) return entries.delete(entry);
|
|
141
139
|
}
|
|
142
140
|
}
|
|
143
|
-
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);
|
|
144
142
|
const expects = /* @__PURE__ */ new Map();
|
|
145
143
|
const onDone = /* @__PURE__ */ new Set();
|
|
146
144
|
for (const T of TT) onDone.add(this.register(T, [I, explicit]));
|
|
@@ -171,7 +169,13 @@ var Context = class Context {
|
|
|
171
169
|
onDone.forEach((r) => r());
|
|
172
170
|
onDone.clear();
|
|
173
171
|
}
|
|
174
|
-
const held = Array.from(HELD.get(I) || [], (child) =>
|
|
172
|
+
const held = Array.from(HELD.get(I) || [], ([child, added]) => {
|
|
173
|
+
const done = this.add(child);
|
|
174
|
+
added.add(done);
|
|
175
|
+
return () => {
|
|
176
|
+
if (added.delete(done)) done();
|
|
177
|
+
};
|
|
178
|
+
});
|
|
175
179
|
function remove() {
|
|
176
180
|
cleanup.delete(remove);
|
|
177
181
|
held.forEach((done) => done());
|
|
@@ -181,9 +185,9 @@ var Context = class Context {
|
|
|
181
185
|
return remove;
|
|
182
186
|
}
|
|
183
187
|
/**
|
|
184
|
-
* Create a child context, optionally registering one or more
|
|
188
|
+
* Create a child context, optionally registering one or more Models to it.
|
|
185
189
|
*
|
|
186
|
-
* @param inputs
|
|
190
|
+
* @param inputs Model, Model class, or map of Models / Model classes to register.
|
|
187
191
|
*/
|
|
188
192
|
push(inputs) {
|
|
189
193
|
const next = new Context(this);
|
|
@@ -191,9 +195,9 @@ var Context = class Context {
|
|
|
191
195
|
return next;
|
|
192
196
|
}
|
|
193
197
|
/**
|
|
194
|
-
* Remove all
|
|
198
|
+
* Remove all Models from this context.
|
|
195
199
|
*
|
|
196
|
-
* Will also run any cleanup callbacks registered when
|
|
200
|
+
* Will also run any cleanup callbacks registered when Models were added.
|
|
197
201
|
*/
|
|
198
202
|
pop() {
|
|
199
203
|
this.inputs = {};
|
|
@@ -216,11 +220,25 @@ Object.defineProperty(Context.prototype, "toString", { value() {
|
|
|
216
220
|
function join(state, value) {
|
|
217
221
|
const ctx = LOOKUP.get(state.is);
|
|
218
222
|
if (ctx) return ctx.add(value);
|
|
219
|
-
const held = HELD.get(state) || /* @__PURE__ */ new
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
const held = HELD.get(state) || /* @__PURE__ */ new Map();
|
|
224
|
+
const added = /* @__PURE__ */ new Set();
|
|
225
|
+
HELD.set(state, held.set(value, added));
|
|
226
|
+
return () => {
|
|
227
|
+
held.delete(value);
|
|
228
|
+
added.forEach((done) => {
|
|
229
|
+
added.delete(done);
|
|
230
|
+
done();
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/** The Model a host mounted `state` under - the one its nearest enclosing context was set up for. */
|
|
235
|
+
function host(state) {
|
|
236
|
+
for (let ctx = LOOKUP.get(state); ctx; ctx = ctx.parent) {
|
|
237
|
+
const found = HOST.get(ctx);
|
|
238
|
+
if (found && found !== state) return found;
|
|
239
|
+
}
|
|
222
240
|
}
|
|
223
241
|
|
|
224
242
|
//#endregion
|
|
225
|
-
export { Context, join };
|
|
243
|
+
export { Context, host, join };
|
|
226
244
|
//# sourceMappingURL=context.js.map
|
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, Set<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 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 const type = I.constructor as State.Extends;\n const g = type.global;\n\n if (entry[0].constructor === type && (typeof g == 'function' ? g(I) : g))\n throw new Error(\n `Cannot register ${I} as a global - ${entry[0]} already exists in root. Destroy the existing instance first, or provide additional ones via explicit context.`\n );\n\n return entries.delete(entry);\n }\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) => this.add(child));\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 Set();\n HELD.set(state, held.add(value));\n\n return () => held.delete(value);\n}\n\nexport { Context, 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/dispatch.d.ts
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
type Handler = () => void;
|
|
3
3
|
type Transition = (work: Handler) => void;
|
|
4
4
|
type Hold = (retry?: Handler | false) => void;
|
|
5
|
+
/** Where errors caught in a flush go - `as` takes a report, `error` throws what is left uncaught. */
|
|
6
|
+
declare const REPORT: {
|
|
7
|
+
as(kind: string, owner?: object, cause?: unknown, key?: string): void;
|
|
8
|
+
error(err: unknown): void;
|
|
9
|
+
};
|
|
5
10
|
/**
|
|
6
11
|
* Hold the work being replayed until the returned callback runs - a subscriber
|
|
7
12
|
* which has not yet absorbed the update takes one, and settlement waits on it
|
|
@@ -33,5 +38,5 @@ declare function pending(work: Handler): Promise<void>;
|
|
|
33
38
|
*/
|
|
34
39
|
declare function pending(): (() => void) | undefined;
|
|
35
40
|
//#endregion
|
|
36
|
-
export { enqueue, hold, pending };
|
|
41
|
+
export { REPORT, enqueue, hold, pending };
|
|
37
42
|
//# sourceMappingURL=dispatch.d.ts.map
|
package/dist/dispatch.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import { fault } from "./state.js";
|
|
2
|
-
|
|
3
1
|
//#region src/dispatch.ts
|
|
4
2
|
const DISPATCH = /* @__PURE__ */ new Map();
|
|
3
|
+
/** Where errors caught in a flush go - `as` takes a report, `error` throws what is left uncaught. */
|
|
4
|
+
const REPORT = {
|
|
5
|
+
as(kind, owner, cause, key) {
|
|
6
|
+
REPORT.error(cause);
|
|
7
|
+
},
|
|
8
|
+
error(err) {
|
|
9
|
+
queueMicrotask(() => {
|
|
10
|
+
throw err;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
};
|
|
5
14
|
let current;
|
|
6
15
|
let replaying;
|
|
7
16
|
/**
|
|
@@ -59,10 +68,9 @@ function flush() {
|
|
|
59
68
|
if (transition) transition(handler);
|
|
60
69
|
else handler();
|
|
61
70
|
} catch (err) {
|
|
62
|
-
|
|
63
|
-
} finally {
|
|
64
|
-
current = replaying = void 0;
|
|
71
|
+
REPORT.as("effect", scheduled.owner, err);
|
|
65
72
|
}
|
|
73
|
+
current = replaying = void 0;
|
|
66
74
|
if (!scheduled.holds) drop(scheduled);
|
|
67
75
|
}
|
|
68
76
|
}
|
|
@@ -102,5 +110,5 @@ function pending(work) {
|
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
//#endregion
|
|
105
|
-
export { enqueue, hold, pending };
|
|
113
|
+
export { REPORT, enqueue, hold, pending };
|
|
106
114
|
//# sourceMappingURL=dispatch.js.map
|
package/dist/dispatch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.js","names":[],"sources":["../src/dispatch.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"dispatch.js","names":[],"sources":["../src/dispatch.ts"],"sourcesContent":["type Handler = () => void;\ntype Transition = (work: Handler) => void;\n\ninterface Pending {\n count: number;\n done(): void;\n}\n\ntype Hold = (retry?: Handler | false) => void;\n\ninterface Scheduled {\n /** How this subscriber defers, if it can - supplied where it subscribed. */\n transition?: Transition;\n awaiting?: Set<Pending>;\n holds?: number;\n owner?: object;\n}\n\nconst DISPATCH = new Map<Handler, Scheduled>();\n\n/** Where errors caught in a flush go - `as` takes a report, `error` throws what is left uncaught. */\nconst REPORT = {\n as(kind: string, owner?: object, cause?: unknown, key?: string): void {\n REPORT.error(cause);\n },\n error(err: unknown): void {\n queueMicrotask(() => {\n throw err;\n });\n }\n};\nlet current: Iterable<Pending> | undefined;\nlet replaying: Scheduled | undefined;\n\n/**\n * Await this handler's replay for the work being scheduled. Overlapping calls\n * may each be waiting on the same handler, which replays once.\n */\nfunction claim(scheduled: Scheduled) {\n if (!current) return;\n\n const awaiting = scheduled.awaiting || (scheduled.awaiting = new Set());\n\n for (const record of current)\n if (!awaiting.has(record)) {\n awaiting.add(record);\n record.count++;\n }\n}\n\nfunction drop(scheduled: Scheduled) {\n const { awaiting } = scheduled;\n\n if (!awaiting) return;\n\n scheduled.awaiting = undefined;\n\n for (const record of awaiting)\n if (!--record.count) record.done();\n}\n\n/**\n * Hold the work being replayed until the returned callback runs - a subscriber\n * which has not yet absorbed the update takes one, and settlement waits on it\n * rather than on the replay. Returns nothing unless the replay carries pending\n * work, so an unrelated subscriber pays for none of it.\n */\nfunction hold(detached?: true, transition?: Transition) {\n const scheduled = replaying || {};\n\n if (detached) {\n scheduled.transition = transition;\n claim(scheduled);\n }\n else if (!scheduled.awaiting) return;\n\n let released = false;\n\n scheduled.holds = (scheduled.holds || 0) + 1;\n\n const release: Hold = (retry) => {\n if (released) return;\n if (retry === false) return claim(scheduled);\n\n if (retry) {\n current = scheduled.awaiting;\n enqueue(retry, scheduled.transition);\n current = undefined;\n }\n\n released = true;\n if (!--scheduled.holds!) drop(scheduled);\n };\n\n return release;\n}\n\nfunction flush() {\n for (const [handler, scheduled] of DISPATCH) {\n DISPATCH.delete(handler);\n\n const { awaiting, transition } = scheduled;\n\n current = awaiting;\n replaying = scheduled;\n\n try {\n if (transition) transition(handler);\n else handler();\n } catch (err) {\n REPORT.as('effect', scheduled.owner, err);\n }\n\n current = replaying = undefined;\n\n if (!scheduled.holds) drop(scheduled);\n }\n}\n\n/**\n * Queue `handler` to replay after this tick. `transition` is how this\n * subscriber defers - it brackets the replay, but only where the scheduled\n * work asked for one.\n */\nfunction enqueue(handler: Handler, transition?: Transition, owner?: object) {\n if (!DISPATCH.size) queueMicrotask(flush);\n\n const scheduled = DISPATCH.get(handler);\n\n if (!scheduled) {\n const next: Scheduled = {\n transition: current ? transition : undefined,\n owner\n };\n\n DISPATCH.set(handler, next);\n claim(next);\n } else if (current) {\n claim(scheduled);\n } else {\n scheduled.transition = undefined;\n }\n}\n\n/**\n * Run `work` now, marking the subscriber updates it queues non-urgent - each\n * replays through whatever scheduler it subscribed with. The callback itself is\n * synchronous; only what it notifies is pending.\n *\n * Resolves once every one of those updates has replayed and been absorbed. A\n * subscriber which cannot report absorption resolves on replay; one which can\n * defer brackets its own replay.\n */\nfunction pending(work: Handler): Promise<void>;\n\n/**\n * Declare the subscriber currently replaying has not absorbed its update yet -\n * settlement waits on the returned callback rather than on the replay. Call\n * only from inside a replay carrying pending work; elsewhere this returns\n * nothing and the subscriber pays for none of it.\n */\nfunction pending(): (() => void) | undefined;\n\nfunction pending(work?: Handler): Promise<void> | (() => void) | undefined {\n if (!work) return hold();\n\n const parent = current;\n const record = { count: 1 } as Pending;\n const promise = new Promise<void>((resolve) => {\n record.done = resolve;\n });\n\n current = parent ? [...parent, record] : [record];\n\n try {\n work();\n } finally {\n current = parent;\n if (!--record.count) record.done();\n }\n\n return promise;\n}\n\nexport { enqueue, hold, pending, REPORT };\n"],"mappings":";AAkBA,MAAM,2BAAW,IAAI,KAAyB;;AAG9C,MAAM,SAAS;CACb,GAAG,MAAc,OAAgB,OAAiB,KAAoB;AACpE,SAAO,MAAM,MAAM;;CAErB,MAAM,KAAoB;AACxB,uBAAqB;AACnB,SAAM;IACN;;CAEL;AACD,IAAI;AACJ,IAAI;;;;;AAMJ,SAAS,MAAM,WAAsB;AACnC,KAAI,CAAC,QAAS;CAEd,MAAM,WAAW,UAAU,aAAa,UAAU,2BAAW,IAAI,KAAK;AAEtE,MAAK,MAAM,UAAU,QACnB,KAAI,CAAC,SAAS,IAAI,OAAO,EAAE;AACzB,WAAS,IAAI,OAAO;AACpB,SAAO;;;AAIb,SAAS,KAAK,WAAsB;CAClC,MAAM,EAAE,aAAa;AAErB,KAAI,CAAC,SAAU;AAEf,WAAU,WAAW;AAErB,MAAK,MAAM,UAAU,SACnB,KAAI,CAAC,EAAE,OAAO,MAAO,QAAO,MAAM;;;;;;;;AAStC,SAAS,KAAK,UAAiB,YAAyB;CACtD,MAAM,YAAY,aAAa,EAAE;AAEjC,KAAI,UAAU;AACZ,YAAU,aAAa;AACvB,QAAM,UAAU;YAET,CAAC,UAAU,SAAU;CAE9B,IAAI,WAAW;AAEf,WAAU,SAAS,UAAU,SAAS,KAAK;CAE3C,MAAM,WAAiB,UAAU;AAC/B,MAAI,SAAU;AACd,MAAI,UAAU,MAAO,QAAO,MAAM,UAAU;AAE5C,MAAI,OAAO;AACT,aAAU,UAAU;AACpB,WAAQ,OAAO,UAAU,WAAW;AACpC,aAAU;;AAGZ,aAAW;AACX,MAAI,CAAC,EAAE,UAAU,MAAQ,MAAK,UAAU;;AAG1C,QAAO;;AAGT,SAAS,QAAQ;AACf,MAAK,MAAM,CAAC,SAAS,cAAc,UAAU;AAC3C,WAAS,OAAO,QAAQ;EAExB,MAAM,EAAE,UAAU,eAAe;AAEjC,YAAU;AACV,cAAY;AAEZ,MAAI;AACF,OAAI,WAAY,YAAW,QAAQ;OAC9B,UAAS;WACP,KAAK;AACZ,UAAO,GAAG,UAAU,UAAU,OAAO,IAAI;;AAG3C,YAAU,YAAY;AAEtB,MAAI,CAAC,UAAU,MAAO,MAAK,UAAU;;;;;;;;AASzC,SAAS,QAAQ,SAAkB,YAAyB,OAAgB;AAC1E,KAAI,CAAC,SAAS,KAAM,gBAAe,MAAM;CAEzC,MAAM,YAAY,SAAS,IAAI,QAAQ;AAEvC,KAAI,CAAC,WAAW;EACd,MAAM,OAAkB;GACtB,YAAY,UAAU,aAAa;GACnC;GACD;AAED,WAAS,IAAI,SAAS,KAAK;AAC3B,QAAM,KAAK;YACF,QACT,OAAM,UAAU;KAEhB,WAAU,aAAa;;AAuB3B,SAAS,QAAQ,MAA0D;AACzE,KAAI,CAAC,KAAM,QAAO,MAAM;CAExB,MAAM,SAAS;CACf,MAAM,SAAS,EAAE,OAAO,GAAG;CAC3B,MAAM,UAAU,IAAI,SAAe,YAAY;AAC7C,SAAO,OAAO;GACd;AAEF,WAAU,SAAS,CAAC,GAAG,QAAQ,OAAO,GAAG,CAAC,OAAO;AAEjD,KAAI;AACF,QAAM;WACE;AACR,YAAU;AACV,MAAI,CAAC,EAAE,OAAO,MAAO,QAAO,MAAM;;AAGpC,QAAO"}
|
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
|
}
|
package/dist/field/def.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { PENDING, STORE, State, apply, uid } from "../state.js";
|
|
2
1
|
import { listener } from "../observable.js";
|
|
2
|
+
import { Model, PENDING, STORE, apply, uid } from "../state.js";
|
|
3
3
|
|
|
4
4
|
//#region src/field/def.ts
|
|
5
5
|
const APPLY = /* @__PURE__ */ new Map();
|
|
6
6
|
const RESET = () => APPLY.clear();
|
|
7
7
|
function def(arg1) {
|
|
8
|
-
if (!PENDING.size || PENDING.size == 1 && PENDING.has(RESET)) throw new Error("Instruction created with no
|
|
8
|
+
if (!PENDING.size || PENDING.size == 1 && PENDING.has(RESET)) throw new Error("Instruction created with no Model under construction.");
|
|
9
9
|
PENDING.add(RESET);
|
|
10
10
|
const token = Symbol("field-" + uid());
|
|
11
|
-
APPLY.set(token, [arg1, [...PENDING].filter((x) => x instanceof
|
|
11
|
+
APPLY.set(token, [arg1, [...PENDING].filter((x) => x instanceof Model)]);
|
|
12
12
|
return token;
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
Model.on({ setup(self) {
|
|
15
15
|
const store = STORE.get(self);
|
|
16
16
|
for (const key in self) {
|
|
17
17
|
const property = Object.getOwnPropertyDescriptor(self, key) || {};
|
|
18
18
|
const entry = APPLY.get(property.value);
|
|
19
|
-
if (entry === null) throw new Error(`${self}.${key} has an instruction applied to another
|
|
19
|
+
if (entry === null) throw new Error(`${self}.${key} has an instruction applied to another Model.`);
|
|
20
20
|
if (!entry) continue;
|
|
21
21
|
if (key[0] == "_") throw new Error(`${self}.${key} is unmanaged - instructions cannot apply to _ keys.`);
|
|
22
22
|
const [instruction, pending] = entry;
|
|
@@ -29,9 +29,9 @@ State.on((self) => {
|
|
|
29
29
|
if (desc.destroy) listener(self, desc.destroy, null);
|
|
30
30
|
apply(self, key, desc, true);
|
|
31
31
|
}
|
|
32
|
-
});
|
|
32
|
+
} });
|
|
33
33
|
/**
|
|
34
|
-
* Drop pending
|
|
34
|
+
* Drop pending Models constructed after `self` and before the instruction -
|
|
35
35
|
* products of its own initializers, discarded when this token replaced them.
|
|
36
36
|
*/
|
|
37
37
|
function discard(self, pending) {
|
package/dist/field/def.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"def.js","names":[],"sources":["../../src/field/def.ts"],"sourcesContent":["import { listener } from '../observable';\nimport { PENDING,
|
|
1
|
+
{"version":3,"file":"def.js","names":[],"sources":["../../src/field/def.ts"],"sourcesContent":["import { listener } from '../observable';\nimport { PENDING, Model, STORE, uid, apply } from '../state';\n\ndeclare namespace def {\n /**\n * Property initializer, will run upon instance creation.\n * Optional returned callback will run when once upon first access.\n */\n type Factory<T = any, M extends Model = any> = (\n this: M,\n key: Extract<Model.Field<M>, string>,\n thisArg: M,\n state: Model.Values<M>\n ) => def.Config<T> | (() => void) | void;\n\n interface Config<T = any> extends Model.Apply<T> {\n destroy?: () => void;\n }\n}\n\nconst APPLY = new Map<symbol, [def.Factory, Model[]] | null>();\nconst RESET = () => APPLY.clear();\n\nfunction def<T>(arg1: def.Factory<T>) {\n if (!PENDING.size || (PENDING.size == 1 && PENDING.has(RESET)))\n throw new Error(\n 'Instruction created with no Model under construction.'\n );\n\n PENDING.add(RESET);\n\n const token = Symbol('field-' + uid());\n\n APPLY.set(token, [arg1, [...PENDING].filter((x) => x instanceof Model) as Model[]]);\n\n return token as T extends void ? unknown : T;\n}\n\nModel.on({\n setup(self) {\n const store = STORE.get(self)!;\n\n for (const key in self) {\n const property: PropertyDescriptor = Object.getOwnPropertyDescriptor(self, key) || {};\n const entry = APPLY.get(property.value);\n\n if (entry === null)\n throw new Error(\n `${self}.${key} has an instruction applied to another Model.`\n );\n\n if (!entry) continue;\n\n if (key[0] == '_')\n throw new Error(`${self}.${key} is unmanaged - instructions cannot apply to _ keys.`);\n\n const [instruction, pending] = entry;\n\n APPLY.set(property.value, null);\n discard(self, pending);\n delete (self as any)[key];\n\n const output = instruction.call(self, key, self, store);\n\n if (!output) continue;\n\n const desc = typeof output == 'function' ? { destroy: output } : output;\n\n if (desc.destroy) listener(self, desc.destroy, null);\n\n apply(self, key, desc, true);\n }\n }\n});\n\n/**\n * Drop pending Models constructed after `self` and before the instruction -\n * products of its own initializers, discarded when this token replaced them.\n */\nfunction discard(self: Model, pending: Model[]) {\n let seen = false;\n\n for (const item of pending)\n if (!seen) seen = item === self;\n else PENDING.delete(item);\n}\n\nexport { def };\n"],"mappings":";;;;AAoBA,MAAM,wBAAQ,IAAI,KAA4C;AAC9D,MAAM,cAAc,MAAM,OAAO;AAEjC,SAAS,IAAO,MAAsB;AACpC,KAAI,CAAC,QAAQ,QAAS,QAAQ,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAC3D,OAAM,IAAI,MACR,wDACD;AAEH,SAAQ,IAAI,MAAM;CAElB,MAAM,QAAQ,OAAO,WAAW,KAAK,CAAC;AAEtC,OAAM,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,QAAQ,MAAM,aAAa,MAAM,CAAY,CAAC;AAEnF,QAAO;;AAGT,MAAM,GAAG,EACP,MAAM,MAAM;CACV,MAAM,QAAQ,MAAM,IAAI,KAAK;AAE7B,MAAK,MAAM,OAAO,MAAM;EACtB,MAAM,WAA+B,OAAO,yBAAyB,MAAM,IAAI,IAAI,EAAE;EACrF,MAAM,QAAQ,MAAM,IAAI,SAAS,MAAM;AAEvC,MAAI,UAAU,KACZ,OAAM,IAAI,MACR,GAAG,KAAK,GAAG,IAAI,+CAChB;AAEH,MAAI,CAAC,MAAO;AAEZ,MAAI,IAAI,MAAM,IACZ,OAAM,IAAI,MAAM,GAAG,KAAK,GAAG,IAAI,sDAAsD;EAEvF,MAAM,CAAC,aAAa,WAAW;AAE/B,QAAM,IAAI,SAAS,OAAO,KAAK;AAC/B,UAAQ,MAAM,QAAQ;AACtB,SAAQ,KAAa;EAErB,MAAM,SAAS,YAAY,KAAK,MAAM,KAAK,MAAM,MAAM;AAEvD,MAAI,CAAC,OAAQ;EAEb,MAAM,OAAO,OAAO,UAAU,aAAa,EAAE,SAAS,QAAQ,GAAG;AAEjE,MAAI,KAAK,QAAS,UAAS,MAAM,KAAK,SAAS,KAAK;AAEpD,QAAM,MAAM,KAAK,MAAM,KAAK;;GAGjC,CAAC;;;;;AAMF,SAAS,QAAQ,MAAa,SAAkB;CAC9C,IAAI,OAAO;AAEX,MAAK,MAAM,QAAQ,QACjB,KAAI,CAAC,KAAM,QAAO,SAAS;KACtB,SAAQ,OAAO,KAAK"}
|
package/dist/field/get.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Model } from "../state.js";
|
|
2
2
|
|
|
3
3
|
//#region src/field/get.d.ts
|
|
4
4
|
declare namespace get {
|
|
5
|
-
type Callback<T = any> = (state: T, subject:
|
|
5
|
+
type Callback<T = any> = (state: T, subject: Model) => void | boolean | (() => void);
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* Fetches upstream
|
|
8
|
+
* Fetches upstream Model from context with optional lifecycle callback.
|
|
9
9
|
* Callback runs on mount and can return cleanup function for unmount.
|
|
10
10
|
*
|
|
11
|
-
* @param Type - Type of
|
|
11
|
+
* @param Type - Type of Model to fetch.
|
|
12
12
|
* @param callback - Optional lifecycle callback, receives (state, subject). Can return cleanup function.
|
|
13
13
|
*/
|
|
14
|
-
declare function get<T extends
|
|
14
|
+
declare function get<T extends Model>(Type: Model.Extends<T>, callback?: get.Callback<T>): T;
|
|
15
15
|
/**
|
|
16
16
|
* Fetches and assigns the controller which spawned this host.
|
|
17
17
|
* When parent assigns an instance via `use()` directive, it
|
|
@@ -20,38 +20,38 @@ declare function get<T extends State>(Type: State.Extends<T>, callback?: get.Cal
|
|
|
20
20
|
* @param Type - Type of controller compatible with this class.
|
|
21
21
|
* @param required - If false, property may be undefined. Otherwise will throw.
|
|
22
22
|
*/
|
|
23
|
-
declare function get<T extends
|
|
23
|
+
declare function get<T extends Model>(Type: Model.Extends<T>, required: false): T | undefined;
|
|
24
24
|
/**
|
|
25
|
-
* Collects downstream
|
|
26
|
-
* for which this is an ancestor. Returns an array that updates as
|
|
25
|
+
* Collects downstream Models, accumulating all instances of specified type
|
|
26
|
+
* for which this is an ancestor. Returns an array that updates as Models
|
|
27
27
|
* are added or removed. Callback runs on each registration and can return
|
|
28
28
|
* cleanup or false to prevent registration.
|
|
29
29
|
*
|
|
30
|
-
* @param Type - Type of
|
|
30
|
+
* @param Type - Type of Model to collect.
|
|
31
31
|
* @param downstream - Must be true to enable downstream mode.
|
|
32
32
|
* @param callback - Optional lifecycle callback, receives (state, subject). May return false to prevent registration.
|
|
33
33
|
*/
|
|
34
|
-
declare function get<T extends
|
|
34
|
+
declare function get<T extends Model>(Type: Model.Extends<T>, downstream: true, callback?: get.Callback<T>): T[];
|
|
35
35
|
/**
|
|
36
|
-
* Fetches a single downstream
|
|
36
|
+
* Fetches a single downstream Model of specified type.
|
|
37
37
|
* Updates when a matching child appears or is removed.
|
|
38
38
|
* Throws if not found.
|
|
39
39
|
*
|
|
40
|
-
* @param Type - Type of
|
|
40
|
+
* @param Type - Type of Model to fetch.
|
|
41
41
|
* @param downstream - Must be true to enable downstream mode.
|
|
42
42
|
* @param required - Throw if no match found.
|
|
43
43
|
*/
|
|
44
|
-
declare function get<T extends
|
|
44
|
+
declare function get<T extends Model>(Type: Model.Extends<T>, downstream: true, single: true): T;
|
|
45
45
|
/**
|
|
46
|
-
* Fetches a single downstream
|
|
46
|
+
* Fetches a single downstream Model of specified type.
|
|
47
47
|
* Updates when a matching child appears or is removed.
|
|
48
48
|
* Returns undefined if not found.
|
|
49
49
|
*
|
|
50
|
-
* @param Type - Type of
|
|
50
|
+
* @param Type - Type of Model to fetch.
|
|
51
51
|
* @param downstream - Must be true to enable downstream mode.
|
|
52
52
|
* @param required - If false, returns undefined if no match found.
|
|
53
53
|
*/
|
|
54
|
-
declare function get<T extends
|
|
54
|
+
declare function get<T extends Model>(Type: Model.Extends<T>, downstream: true, required: false): T | undefined;
|
|
55
55
|
//#endregion
|
|
56
56
|
export { get };
|
|
57
57
|
//# sourceMappingURL=get.d.ts.map
|
package/dist/field/get.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { STORE, children, parent, update } from "../state.js";
|
|
2
1
|
import { capture, listener, observer } from "../observable.js";
|
|
2
|
+
import { Model, STORE, children, owned, parent, update } from "../state.js";
|
|
3
3
|
import { Context } from "../context.js";
|
|
4
4
|
import { def } from "./def.js";
|
|
5
5
|
|
|
@@ -23,11 +23,26 @@ function above(Type, argument) {
|
|
|
23
23
|
assign(hasParent);
|
|
24
24
|
return {};
|
|
25
25
|
}
|
|
26
|
+
if (Type === Model) {
|
|
27
|
+
if (argument === false) return {
|
|
28
|
+
get: false,
|
|
29
|
+
enumerable: false
|
|
30
|
+
};
|
|
31
|
+
throw new Error(`${subject} has no owner.`);
|
|
32
|
+
}
|
|
26
33
|
let found = false;
|
|
27
34
|
let depth = Infinity;
|
|
28
35
|
let level = 0;
|
|
29
36
|
for (let p = hasParent; p; p = parent(p), level++) {
|
|
30
37
|
const at = level;
|
|
38
|
+
if (p instanceof Type) {
|
|
39
|
+
if (!found) {
|
|
40
|
+
found = true;
|
|
41
|
+
depth = at;
|
|
42
|
+
assign(p);
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
31
46
|
const remove = children(p, (child) => {
|
|
32
47
|
if (child !== subject && child instanceof Type && at <= depth) {
|
|
33
48
|
found = true;
|
|
@@ -68,13 +83,14 @@ function above(Type, argument) {
|
|
|
68
83
|
function below(Type, arg) {
|
|
69
84
|
return def((key, subject) => {
|
|
70
85
|
const context = Context.get(subject);
|
|
86
|
+
const watch = (callback) => Type === Model ? owned(subject, callback) : context.get(Type, callback, true);
|
|
71
87
|
if (typeof arg == "boolean") {
|
|
72
|
-
|
|
88
|
+
watch((state) => {
|
|
73
89
|
update(subject, key, state);
|
|
74
90
|
return state.set(null, () => {
|
|
75
91
|
update(subject, key, void 0);
|
|
76
92
|
});
|
|
77
|
-
}
|
|
93
|
+
});
|
|
78
94
|
return {
|
|
79
95
|
get: arg,
|
|
80
96
|
value: void 0,
|
|
@@ -82,7 +98,7 @@ function below(Type, arg) {
|
|
|
82
98
|
};
|
|
83
99
|
}
|
|
84
100
|
const applied = /* @__PURE__ */ new Set();
|
|
85
|
-
|
|
101
|
+
watch((state) => {
|
|
86
102
|
let remove;
|
|
87
103
|
let release;
|
|
88
104
|
if (applied.has(state)) return;
|
|
@@ -107,7 +123,7 @@ function below(Type, arg) {
|
|
|
107
123
|
}
|
|
108
124
|
const ignore = state.set(null, done);
|
|
109
125
|
return done;
|
|
110
|
-
}
|
|
126
|
+
});
|
|
111
127
|
return {
|
|
112
128
|
value: [],
|
|
113
129
|
enumerable: false
|
package/dist/field/get.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get.js","names":[],"sources":["../../src/field/get.ts"],"sourcesContent":["import { capture, listener, observer } from '../observable';\nimport { Context } from '../context';\nimport {
|
|
1
|
+
{"version":3,"file":"get.js","names":[],"sources":["../../src/field/get.ts"],"sourcesContent":["import { capture, listener, observer } from '../observable';\nimport { Context } from '../context';\nimport { Model, children, owned, parent, STORE, update } from '../state';\nimport { def } from './def';\n\ndeclare namespace get {\n type Callback<T = any> = (\n state: T,\n subject: Model\n ) => void | boolean | (() => void);\n}\n\n/**\n * Fetches upstream Model from context with optional lifecycle callback.\n * Callback runs on mount and can return cleanup function for unmount.\n *\n * @param Type - Type of Model to fetch.\n * @param callback - Optional lifecycle callback, receives (state, subject). Can return cleanup function.\n */\nfunction get<T extends Model>(\n Type: Model.Extends<T>,\n callback?: get.Callback<T>\n): T;\n\n/**\n * Fetches and assigns the controller which spawned this host.\n * When parent assigns an instance via `use()` directive, it\n * will be made available to child upon this request.\n *\n * @param Type - Type of controller compatible with this class.\n * @param required - If false, property may be undefined. Otherwise will throw.\n */\nfunction get<T extends Model>(\n Type: Model.Extends<T>,\n required: false\n): T | undefined;\n\n/**\n * Collects downstream Models, accumulating all instances of specified type\n * for which this is an ancestor. Returns an array that updates as Models\n * are added or removed. Callback runs on each registration and can return\n * cleanup or false to prevent registration.\n *\n * @param Type - Type of Model to collect.\n * @param downstream - Must be true to enable downstream mode.\n * @param callback - Optional lifecycle callback, receives (state, subject). May return false to prevent registration.\n */\nfunction get<T extends Model>(\n Type: Model.Extends<T>,\n downstream: true,\n callback?: get.Callback<T>\n): T[];\n\n/**\n * Fetches a single downstream Model of specified type.\n * Updates when a matching child appears or is removed.\n * Throws if not found.\n *\n * @param Type - Type of Model to fetch.\n * @param downstream - Must be true to enable downstream mode.\n * @param required - Throw if no match found.\n */\nfunction get<T extends Model>(\n Type: Model.Extends<T>,\n downstream: true,\n single: true\n): T;\n\n/**\n * Fetches a single downstream Model of specified type.\n * Updates when a matching child appears or is removed.\n * Returns undefined if not found.\n *\n * @param Type - Type of Model to fetch.\n * @param downstream - Must be true to enable downstream mode.\n * @param required - If false, returns undefined if no match found.\n */\nfunction get<T extends Model>(\n Type: Model.Extends<T>,\n downstream: true,\n required: false\n): T | undefined;\n\nfunction get<T extends Model>(\n Type: Model.Extends<T>,\n arg1?: get.Callback<T> | boolean,\n arg2?: get.Callback<T> | boolean\n) {\n return arg1 === true ? below(Type, arg2) : above(Type, arg1);\n}\n\nfunction above<T extends Model>(\n Type: Model.Extends<T>,\n argument: get.Callback<T> | boolean | undefined\n) {\n return def<T>((key, subject) => {\n const hasParent = parent(subject);\n const callback =\n typeof argument === 'function'\n ? (argument as get.Callback<T>)\n : undefined;\n\n function assign(value: T) {\n if (STORE.get(subject)![key] === value) return;\n\n if (callback) {\n const result = callback(value, subject);\n if (typeof result === 'function') subject.set(null, result);\n }\n update(subject, key, value);\n }\n\n if (hasParent && hasParent instanceof Type) {\n assign(hasParent as T);\n return {};\n }\n\n if (Type === Model) {\n if (argument === false) return { get: false, enumerable: false };\n throw new Error(`${subject} has no owner.`);\n }\n\n let found = false;\n let depth = Infinity;\n let level = 0;\n\n for (let p: Model | null | undefined = hasParent; p; p = parent(p), level++) {\n const at = level;\n\n if (p instanceof Type) {\n if (!found) {\n found = true;\n depth = at;\n assign(p as T);\n }\n break;\n }\n\n const remove = children(p, (child) => {\n if (child !== subject && child instanceof Type && at <= depth) {\n found = true;\n depth = at;\n assign(child as T);\n }\n });\n\n subject.set(null, () => { remove(); });\n }\n\n if (!found) {\n const remove = Context.get(subject).get(Type, (state) => {\n if (depth !== Infinity) return;\n found = true;\n assign(state);\n }, false, subject) as () => void;\n\n subject.set(null, () => { remove(); });\n }\n\n if (!found && argument !== false) {\n const missing = () =>\n new Error(`Required ${Type} not found in context for ${subject}.`);\n\n let activating: Model | undefined;\n\n for (\n let p: Model | null | undefined = hasParent;\n p && !observer(p)!.ready;\n p = parent(p)\n )\n activating = p;\n\n if (!activating) throw missing();\n\n listener(activating, () => {\n if (!found) throw missing();\n return null;\n }, true);\n }\n\n return {\n get: argument !== false,\n enumerable: false\n };\n });\n}\n\nfunction below<T extends Model>(\n Type: Model.Extends<T>,\n arg: get.Callback<T> | boolean | undefined\n) {\n return def<T[]>((key, subject) => {\n const context = Context.get(subject);\n const watch = (callback: (state: T) => (() => void) | void) =>\n Type === Model\n ? owned(subject, callback as (state: Model) => (() => void) | void)\n : context.get(Type, callback, true);\n\n if (typeof arg == 'boolean') {\n watch(\n (state) => {\n update(subject, key, state);\n return state.set(null, () => {\n update(subject, key, undefined);\n });\n }\n );\n\n return {\n get: arg,\n value: undefined,\n enumerable: false\n };\n }\n\n const applied = new Set<Model>();\n\n watch(\n (state) => {\n let remove: (() => void) | undefined;\n let release: (() => void) | undefined;\n\n if (applied.has(state)) return;\n\n if (arg) {\n let rejected = false;\n\n capture((fn) => {\n const done = arg(state, subject);\n\n if (done === false) rejected = true;\n else if (typeof done == 'function') remove = done;\n\n release = fn;\n });\n\n if (rejected) return;\n }\n\n applied.add(state);\n update(subject, key, [...applied]);\n\n function done() {\n if (release) release();\n ignore();\n\n applied.delete(state);\n update(subject, key, [...applied]);\n\n if (typeof remove == 'function') remove();\n }\n\n const ignore = state.set(null, done);\n\n return done;\n }\n );\n\n return {\n value: [],\n enumerable: false\n };\n });\n}\n\nexport { get };\n"],"mappings":"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"}
|
package/dist/field/has.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Model } from "../state.js";
|
|
2
2
|
|
|
3
3
|
//#region src/field/has.d.ts
|
|
4
4
|
type Predicate<T> = (value: T, index: number, self: unknown) => boolean;
|
|
@@ -7,13 +7,13 @@ declare namespace has {
|
|
|
7
7
|
export { Predicate, Mapper, List, Pool, Create, From };
|
|
8
8
|
}
|
|
9
9
|
declare function has<T>(initial?: Iterable<T> | false | null): List<T>;
|
|
10
|
-
declare function has<T extends
|
|
11
|
-
declare function has<T extends
|
|
10
|
+
declare function has<T extends Model>(Type: new (...args: Model.Args<T>) => T): Create<T>;
|
|
11
|
+
declare function has<T extends Model, K extends Model.Field<T>>(Type: new (...args: Model.Args<T>) => T, fromKey: K): From<T, K>;
|
|
12
12
|
declare function has<R, A extends unknown[]>(make: (...args: A) => R): Pool<Exclude<R, null | undefined>, A, R>;
|
|
13
13
|
declare class List<T> {
|
|
14
14
|
constructor(initial?: Iterable<T> | false | null);
|
|
15
15
|
get size(): number;
|
|
16
|
-
get():
|
|
16
|
+
get(): Model.Export<T>[];
|
|
17
17
|
get(index: number): T | undefined;
|
|
18
18
|
get(start: number, end: number): T[];
|
|
19
19
|
get(predicate: Predicate<T>): T | undefined;
|
|
@@ -31,11 +31,11 @@ declare class List<T> {
|
|
|
31
31
|
any(fn: Predicate<T>): boolean;
|
|
32
32
|
all(fn: Predicate<T>): boolean;
|
|
33
33
|
}
|
|
34
|
-
interface Create<T extends
|
|
35
|
-
add(...args:
|
|
34
|
+
interface Create<T extends Model> extends Pool<T, Model.Args<T>> {
|
|
35
|
+
add(...args: Model.Args<T>): T;
|
|
36
36
|
add(instance: T): T;
|
|
37
37
|
}
|
|
38
|
-
interface From<T extends
|
|
38
|
+
interface From<T extends Model, K extends Model.Field<T>> extends Pool<T, [from: T[K]]> {
|
|
39
39
|
add(from: T[K]): T;
|
|
40
40
|
add(instance: T): T;
|
|
41
41
|
}
|
|
@@ -43,7 +43,7 @@ declare class Pool<T, A extends unknown[] = unknown[], R = T> {
|
|
|
43
43
|
constructor(make: Function, fromKey?: string);
|
|
44
44
|
get size(): number;
|
|
45
45
|
add(...args: A): R;
|
|
46
|
-
get():
|
|
46
|
+
get(): Model.Export<T>[];
|
|
47
47
|
get(predicate: Predicate<T>): T | undefined;
|
|
48
48
|
has(value: T): boolean;
|
|
49
49
|
delete(value: T): boolean;
|