@expressive/mvc 0.52.0 → 0.52.2-alpha.74
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/dist/index.d.ts +70 -65
- package/dist/index.js +154 -137
- package/dist/index.mjs +151 -136
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,16 +5,16 @@ declare namespace Model {
|
|
|
5
5
|
type Init<T extends Model = Model> = (new (...args: Model.Args<T>) => T) & Omit<typeof Model, never>;
|
|
6
6
|
/**
|
|
7
7
|
* Model constructor callback - is called when Model finishes intializing.
|
|
8
|
-
* Returned function will
|
|
8
|
+
* Returned function will run when model is destroyed.
|
|
9
9
|
*/
|
|
10
10
|
type Callback<T extends Model = Model> = (this: T, thisArg: T) => Promise<void> | (() => void) | Assign<T> | void;
|
|
11
11
|
/** Model constructor argument */
|
|
12
12
|
type Argument<T extends Model = Model> = Assign<T> | Callback<T> | string | void;
|
|
13
13
|
/** Model constructor arguments */
|
|
14
|
-
type Args<T extends Model = any> = Argument<T>[];
|
|
14
|
+
type Args<T extends Model = any> = (Argument<T> | Args<T>)[];
|
|
15
15
|
/** Subset of `keyof T` which are not methods or defined by base Model U. **/
|
|
16
16
|
type Field<T> = Exclude<keyof T, keyof Model>;
|
|
17
|
-
/** Any valid key for
|
|
17
|
+
/** Any valid key for model, including but not limited to Field<T>. */
|
|
18
18
|
type Event<T> = Field<T> | (string & {}) | number | symbol;
|
|
19
19
|
/** Object overlay to override values and methods on a model. */
|
|
20
20
|
type Assign<T> = {
|
|
@@ -24,12 +24,12 @@ declare namespace Model {
|
|
|
24
24
|
type Export<R> = R extends {
|
|
25
25
|
get(): infer T;
|
|
26
26
|
} ? T : R;
|
|
27
|
-
/** Value for a property managed by a
|
|
27
|
+
/** Value for a property managed by a model. */
|
|
28
28
|
type Value<T extends Model, K extends Event<T>> = K extends keyof T ? Export<T[K]> : unknown;
|
|
29
29
|
type OnEvent<T extends Model> = (this: T, key: unknown, source: T) => (() => void) | void | null;
|
|
30
30
|
type OnUpdate<T extends Model, K extends Event<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
|
|
31
31
|
/**
|
|
32
|
-
* Values from current state of given
|
|
32
|
+
* Values from current state of given model.
|
|
33
33
|
* Differs from `Values` as values here will drill
|
|
34
34
|
* into "real" values held by exotics like ref.Object.
|
|
35
35
|
*/
|
|
@@ -48,7 +48,7 @@ declare namespace Model {
|
|
|
48
48
|
/**
|
|
49
49
|
* A callback function which is subscribed to parent and updates when accessed properties change.
|
|
50
50
|
*
|
|
51
|
-
* @param current - Current state of this
|
|
51
|
+
* @param current - Current state of this model. This is a proxy which detects properties which
|
|
52
52
|
* where accessed, and thus depended upon to stay current.
|
|
53
53
|
*
|
|
54
54
|
* @param update - Set of properties which have changed, and events fired, since last update.
|
|
@@ -59,7 +59,7 @@ declare namespace Model {
|
|
|
59
59
|
/**
|
|
60
60
|
* A callback function returned by effect. Will be called when effect is stale.
|
|
61
61
|
*
|
|
62
|
-
* @param update - `true` if update is pending, `false` effect has been cancelled, `null` if
|
|
62
|
+
* @param update - `true` if update is pending, `false` effect has been cancelled, `null` if model is destroyed.
|
|
63
63
|
*/
|
|
64
64
|
type EffectCallback = ((update: boolean | null) => void);
|
|
65
65
|
/**
|
|
@@ -88,7 +88,7 @@ declare abstract class Model {
|
|
|
88
88
|
/**
|
|
89
89
|
* Pull current values from state. Flattens all models and exotic values recursively.
|
|
90
90
|
*
|
|
91
|
-
* @returns Object with all values from this
|
|
91
|
+
* @returns Object with all values from this model.
|
|
92
92
|
**/
|
|
93
93
|
get(): Model.State<this>;
|
|
94
94
|
/**
|
|
@@ -96,7 +96,7 @@ declare abstract class Model {
|
|
|
96
96
|
*
|
|
97
97
|
* @param effect Function to run, and again whenever accessed values change.
|
|
98
98
|
* If effect returns a function, it will be called when a change occurs (syncronously),
|
|
99
|
-
* effect is cancelled, or parent
|
|
99
|
+
* effect is cancelled, or parent model is destroyed.
|
|
100
100
|
* @returns Function to cancel listener.
|
|
101
101
|
*/
|
|
102
102
|
get(effect: Model.Effect<this>): () => void;
|
|
@@ -133,29 +133,17 @@ declare abstract class Model {
|
|
|
133
133
|
/**
|
|
134
134
|
* Get update in progress.
|
|
135
135
|
*
|
|
136
|
-
* @returns Promise which resolves object with updated values
|
|
136
|
+
* @returns Promise which resolves object with updated values, `undefined` if there no update is pending.
|
|
137
137
|
**/
|
|
138
138
|
set(): PromiseLike<Model.Event<this>[]> | undefined;
|
|
139
139
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* Given function is called for every assignment (which changes value) or explicit `set`.
|
|
143
|
-
*
|
|
144
|
-
* To run logic on final value only, callback may return a function. Using the same
|
|
145
|
-
* function for one or more events will ensure it is called only when events are settled.
|
|
146
|
-
*
|
|
147
|
-
* @param callback - Function to call when update occurs.
|
|
148
|
-
* @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.
|
|
149
|
-
*
|
|
150
|
-
*/
|
|
151
|
-
set(callback: Model.OnEvent<this>): () => boolean;
|
|
152
|
-
/**
|
|
153
|
-
* Declare an end to updates. This event is final and will freeze state.
|
|
154
|
-
* This event can be watched for as well, to run cleanup logic and internally will remove all listeners.
|
|
140
|
+
* Update mulitple properties at once. Merges argument with current state.
|
|
141
|
+
* Properties which are not managed by this model will be ignored.
|
|
155
142
|
*
|
|
156
|
-
* @param
|
|
143
|
+
* @param assign - Object with properties to update.
|
|
144
|
+
* @returns Promise resolving an array of keys updated, `undefined` (immediately) if a noop.
|
|
157
145
|
*/
|
|
158
|
-
set(
|
|
146
|
+
set(assign: Model.Assign<this>): PromiseLike<Model.Event<this>[]> | undefined;
|
|
159
147
|
/**
|
|
160
148
|
* Push an update. This will not change the value of associated property.
|
|
161
149
|
*
|
|
@@ -168,32 +156,45 @@ declare abstract class Model {
|
|
|
168
156
|
* to prefix an event with "!" and/or use dash-case. e.g. `set("!event")` or `set("my-event")`.
|
|
169
157
|
*
|
|
170
158
|
* @param key - Property or event to dispatch.
|
|
171
|
-
* @returns Promise
|
|
159
|
+
* @returns Promise resolves an array of keys updated.
|
|
172
160
|
*/
|
|
173
161
|
set(key: Model.Event<this>): PromiseLike<Model.Event<this>[]>;
|
|
174
|
-
/**
|
|
175
|
-
* Update mulitple properties at once. Merges argument with current state.
|
|
176
|
-
* Properties which are not managed by this controller will be ignored.
|
|
177
|
-
*
|
|
178
|
-
* @param assign - Object with properties to update.
|
|
179
|
-
* @returns Promise which resolves an array of keys which were updated.
|
|
180
|
-
*/
|
|
181
|
-
set(assign: Model.Assign<this>): PromiseLike<Model.Event<this>[]> | undefined;
|
|
182
162
|
/**
|
|
183
163
|
* Update a property with value.
|
|
184
164
|
*
|
|
185
165
|
* @param key - property to update
|
|
186
166
|
* @param value - value to update property with (if the same as current, no update will occur)
|
|
187
167
|
* @param silent - if true, will not notify listeners of an update
|
|
168
|
+
* @returns Promise resolving an array of keys updated, `undefined` (immediately) if a noop.
|
|
169
|
+
*/
|
|
170
|
+
set<K extends string>(key: K, value: Model.Value<this, K>, silent?: boolean): PromiseLike<Model.Event<this>[]> | undefined;
|
|
171
|
+
/**
|
|
172
|
+
* Call a function when update occurs.
|
|
173
|
+
*
|
|
174
|
+
* Given function is called for every assignment (which changes value) or explicit `set`.
|
|
175
|
+
*
|
|
176
|
+
* To run logic on final value only, callback may return a function. Using the same
|
|
177
|
+
* function for one or more events will ensure it is called only when events are settled.
|
|
178
|
+
*
|
|
179
|
+
* @param callback - Function to call when update occurs.
|
|
180
|
+
* @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.
|
|
181
|
+
*
|
|
182
|
+
*/
|
|
183
|
+
set(callback: Model.OnEvent<this>): () => boolean;
|
|
184
|
+
/**
|
|
185
|
+
* Declare an end to updates. This event is final and will freeze state.
|
|
186
|
+
* This event can be watched for as well, to run cleanup logic and internally will remove all listeners.
|
|
187
|
+
*
|
|
188
|
+
* @param status - `null` to end updates.
|
|
188
189
|
*/
|
|
189
|
-
set
|
|
190
|
+
set(status: null): void;
|
|
190
191
|
/**
|
|
191
192
|
* Iterate over managed properties in this instance of Model.
|
|
192
193
|
* Yeilds the key and underlying value for each property.
|
|
193
194
|
*/
|
|
194
195
|
[Symbol.iterator](): Iterator<[string, unknown]>;
|
|
195
196
|
/**
|
|
196
|
-
* Create and activate a new instance of this
|
|
197
|
+
* Create and activate a new instance of this model.
|
|
197
198
|
*
|
|
198
199
|
* **Important** - Unlike `new this(...)` - this method also activates state.
|
|
199
200
|
*
|
|
@@ -325,10 +326,10 @@ declare namespace set {
|
|
|
325
326
|
type Factory<T, S = any> = (this: S, key: string) => Promise<T> | T;
|
|
326
327
|
}
|
|
327
328
|
/**
|
|
328
|
-
* Set property
|
|
329
|
+
* Set property as `undefined` but required.
|
|
329
330
|
*
|
|
330
331
|
* Property cannot be accessed until it is defined. If accessed while undefined, a hybrid
|
|
331
|
-
* `Promise`/`Error` (
|
|
332
|
+
* `Promise`/`Error` (aka: [Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html)) will be thrown.
|
|
332
333
|
*/
|
|
333
334
|
declare function set<T = any>(): T;
|
|
334
335
|
/**
|
|
@@ -366,6 +367,21 @@ declare namespace has {
|
|
|
366
367
|
declare function has<T extends Model>(type: Model.Type<T>, callback?: has.Callback<T>): readonly T[];
|
|
367
368
|
declare function has(callback?: has.Callback): readonly Model[];
|
|
368
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Update callback function.
|
|
372
|
+
*
|
|
373
|
+
* @param key -
|
|
374
|
+
* - `string` - property which has updated.
|
|
375
|
+
* - `false` - a normal update has completed.
|
|
376
|
+
* - `true` - initial event; instance is ready.
|
|
377
|
+
* - `null` - terminal event; instance is expired.
|
|
378
|
+
* @param source - Instance of Model for which update has occured.
|
|
379
|
+
*/
|
|
380
|
+
type OnUpdate<T = any> = (this: T, key: unknown, source: T) => (() => void) | null | void;
|
|
381
|
+
type OnAccess<T, R = unknown> = (from: T, key: string | number, value: R) => unknown;
|
|
382
|
+
type Event = number | string | null | boolean | symbol;
|
|
383
|
+
declare function addListener(subject: {}, callback: OnUpdate, select?: Event): () => boolean;
|
|
384
|
+
declare function createProxy<T extends {}>(from: T, observer: OnAccess<T>): any;
|
|
369
385
|
type Effect<T extends {}> = (this: T, argument: T) => ((update: boolean | null) => void) | Promise<void> | null | void;
|
|
370
386
|
/**
|
|
371
387
|
* Create a side-effect which will update whenever values accessed change.
|
|
@@ -375,11 +391,15 @@ type Effect<T extends {}> = (this: T, argument: T) => ((update: boolean | null)
|
|
|
375
391
|
* @param callback - Function to invoke when values change.
|
|
376
392
|
* @param requireValues - If `true` will throw if accessing a value which is `undefined`.
|
|
377
393
|
*/
|
|
378
|
-
declare function
|
|
379
|
-
declare function
|
|
394
|
+
declare function createEffect<T extends {}>(target: T, callback: Effect<Required<T>>, requireValues: true): () => void;
|
|
395
|
+
declare function createEffect<T extends {}>(target: T, callback: Effect<T>, requireValues?: boolean): () => void;
|
|
380
396
|
|
|
381
397
|
declare namespace Context {
|
|
382
|
-
type
|
|
398
|
+
type Multiple<T extends Model> = {
|
|
399
|
+
[key: string | number]: Model.Init<T> | T;
|
|
400
|
+
};
|
|
401
|
+
type Accept<T extends Model = Model> = T | Model.Init<T> | Multiple<T>;
|
|
402
|
+
type ForEach = (added: Model, explicit: boolean) => void;
|
|
383
403
|
type Expect = (model: Model) => (() => void) | void;
|
|
384
404
|
}
|
|
385
405
|
interface Context {
|
|
@@ -391,30 +411,15 @@ declare class Context {
|
|
|
391
411
|
id: string;
|
|
392
412
|
protected layer: Map<string | number, Model | Model.Type<Model>>;
|
|
393
413
|
protected cleanup: Set<() => void>;
|
|
394
|
-
constructor(inputs?: Context.
|
|
414
|
+
constructor(inputs?: Context.Accept);
|
|
415
|
+
/** Run callback when a specified type is registered to a **child** context. */
|
|
416
|
+
has<T extends Model>(Type: Model.Type<T>, callback: (model: T) => void): void;
|
|
417
|
+
/** Find specified type registered to a parent context. Returns undefined if none are found. */
|
|
395
418
|
get<T extends Model>(Type: Model.Type<T>): T | undefined;
|
|
396
|
-
|
|
397
|
-
push(inputs?: Context.Input): this;
|
|
419
|
+
push(inputs?: Context.Accept): this;
|
|
398
420
|
pop(): void;
|
|
399
|
-
include(inputs: Context.
|
|
421
|
+
include<T extends Model>(inputs: Context.Accept<T>, forEach?: Context.ForEach | Model.Assign<T>): void;
|
|
400
422
|
protected add<T extends Model>(input: T | Model.Init<T>, implicit?: boolean): T;
|
|
401
423
|
}
|
|
402
424
|
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Defines MVC specific matchers for [Jest](https://jestjs.io/) test suites.
|
|
406
|
-
*
|
|
407
|
-
* For these to work you will need to add `@expressive/mvc/jest`
|
|
408
|
-
* under `setupFilesAfterEnv` in your Jest configuration.
|
|
409
|
-
*/
|
|
410
|
-
namespace jest {
|
|
411
|
-
interface Matchers<R> {
|
|
412
|
-
/** Assert model does have one or more updates pending. */
|
|
413
|
-
toUpdate(timeout?: number): Promise<R>;
|
|
414
|
-
/** Assert model did update with keys specified. */
|
|
415
|
-
toHaveUpdated<R>(...keys: (string | symbol | number)[]): Promise<R>;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
export { Context, Model, Model as default, effect, get, has, ref, set, use };
|
|
425
|
+
export { Context, Model, addListener, createEffect, createProxy, Model as default, get, has, ref, set, use };
|
package/dist/index.js
CHANGED
|
@@ -22,8 +22,10 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
Context: () => Context,
|
|
24
24
|
Model: () => Model,
|
|
25
|
+
addListener: () => addListener,
|
|
26
|
+
createEffect: () => createEffect,
|
|
27
|
+
createProxy: () => createProxy,
|
|
25
28
|
default: () => Model,
|
|
26
|
-
effect: () => effect,
|
|
27
29
|
get: () => get,
|
|
28
30
|
has: () => has,
|
|
29
31
|
ref: () => ref,
|
|
@@ -35,25 +37,30 @@ module.exports = __toCommonJS(src_exports);
|
|
|
35
37
|
// src/control.ts
|
|
36
38
|
var DISPATCH = /* @__PURE__ */ new Set();
|
|
37
39
|
var OBSERVER = /* @__PURE__ */ new WeakMap();
|
|
38
|
-
var
|
|
40
|
+
var LISTENERS = /* @__PURE__ */ new WeakMap();
|
|
41
|
+
var PENDING = /* @__PURE__ */ new WeakMap();
|
|
39
42
|
var onReady = () => null;
|
|
40
43
|
function addListener(subject, callback, select) {
|
|
41
|
-
let subs =
|
|
44
|
+
let subs = LISTENERS.get(subject);
|
|
42
45
|
if (!subs)
|
|
43
|
-
|
|
44
|
-
const filter = select
|
|
45
|
-
if (!
|
|
46
|
+
LISTENERS.set(subject, subs = /* @__PURE__ */ new Map([[onReady, void 0]]));
|
|
47
|
+
const filter = select === void 0 ? void 0 : /* @__PURE__ */ new Set([select]);
|
|
48
|
+
if (!subs.has(onReady) && !filter)
|
|
46
49
|
callback(true, subject);
|
|
47
50
|
subs.set(callback, filter);
|
|
48
51
|
return () => subs.delete(callback);
|
|
49
52
|
}
|
|
53
|
+
function createProxy(from, observer) {
|
|
54
|
+
const proxy = Object.create(from);
|
|
55
|
+
OBSERVER.set(proxy, observer);
|
|
56
|
+
return proxy;
|
|
57
|
+
}
|
|
50
58
|
function watch(from, key2, value) {
|
|
51
59
|
const access = OBSERVER.get(from);
|
|
52
60
|
return access ? access(from, key2, value) : value;
|
|
53
61
|
}
|
|
54
|
-
var PENDING = /* @__PURE__ */ new WeakMap();
|
|
55
62
|
function emit(source, key2) {
|
|
56
|
-
const subs =
|
|
63
|
+
const subs = LISTENERS.get(source);
|
|
57
64
|
const ready = !subs.has(onReady);
|
|
58
65
|
if (key2 === true && ready)
|
|
59
66
|
return;
|
|
@@ -79,27 +86,28 @@ function emit(source, key2) {
|
|
|
79
86
|
});
|
|
80
87
|
PENDING.delete(source);
|
|
81
88
|
}
|
|
82
|
-
function queue(
|
|
89
|
+
function queue(eventHandler) {
|
|
83
90
|
if (!DISPATCH.size)
|
|
84
91
|
setTimeout(() => {
|
|
85
|
-
DISPATCH.forEach((
|
|
92
|
+
DISPATCH.forEach((event2) => {
|
|
86
93
|
try {
|
|
87
|
-
|
|
94
|
+
event2();
|
|
88
95
|
} catch (err) {
|
|
89
96
|
console.error(err);
|
|
90
97
|
}
|
|
91
98
|
});
|
|
92
99
|
DISPATCH.clear();
|
|
93
100
|
}, 0);
|
|
94
|
-
DISPATCH.add(
|
|
101
|
+
DISPATCH.add(eventHandler);
|
|
95
102
|
}
|
|
96
|
-
function
|
|
97
|
-
const listeners =
|
|
103
|
+
function createEffect(target, callback, requireValues) {
|
|
104
|
+
const listeners = LISTENERS.get(target);
|
|
98
105
|
let unset;
|
|
99
106
|
let reset;
|
|
100
107
|
function invoke() {
|
|
101
108
|
let stale;
|
|
102
|
-
const subscriber =
|
|
109
|
+
const subscriber = createProxy(target, access);
|
|
110
|
+
LISTENERS.set(subscriber, listeners);
|
|
103
111
|
function onUpdate() {
|
|
104
112
|
if (stale)
|
|
105
113
|
return null;
|
|
@@ -113,20 +121,19 @@ function effect(target, callback, requireValues) {
|
|
|
113
121
|
function access(from, key2, value) {
|
|
114
122
|
if (value === void 0 && requireValues)
|
|
115
123
|
throw new Error(`${from}.${key2} is required in this context.`);
|
|
116
|
-
const listeners2 =
|
|
124
|
+
const listeners2 = LISTENERS.get(from);
|
|
117
125
|
let listener = listeners2.get(onUpdate);
|
|
118
126
|
if (!listener)
|
|
119
127
|
listeners2.set(onUpdate, listener = /* @__PURE__ */ new Set());
|
|
120
128
|
listener.add(key2);
|
|
121
|
-
const nested =
|
|
122
|
-
if (nested)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
const nested = LISTENERS.get(value);
|
|
130
|
+
if (nested)
|
|
131
|
+
LISTENERS.set(
|
|
132
|
+
value = createProxy(value, access),
|
|
133
|
+
nested
|
|
134
|
+
);
|
|
126
135
|
return value;
|
|
127
136
|
}
|
|
128
|
-
LISTENER.set(subscriber, listeners);
|
|
129
|
-
OBSERVER.set(subscriber, access);
|
|
130
137
|
try {
|
|
131
138
|
const out = callback.call(subscriber, subscriber);
|
|
132
139
|
unset = typeof out == "function" ? out : void 0;
|
|
@@ -163,27 +170,24 @@ var PENDING2 = /* @__PURE__ */ new WeakMap();
|
|
|
163
170
|
var PARENT = /* @__PURE__ */ new WeakMap();
|
|
164
171
|
var METHODS = /* @__PURE__ */ new WeakMap();
|
|
165
172
|
var METHOD = /* @__PURE__ */ new WeakMap();
|
|
166
|
-
var
|
|
167
|
-
var Model = class _Model {
|
|
173
|
+
var Model = class {
|
|
168
174
|
constructor(...args) {
|
|
169
|
-
ID.set(this, `${this.constructor}-${uid()}`);
|
|
170
|
-
STATE.set(this, {});
|
|
171
175
|
prepare(this);
|
|
172
176
|
define(this, "is", { value: this });
|
|
173
|
-
|
|
177
|
+
init(this, args);
|
|
174
178
|
}
|
|
175
179
|
get(arg1, arg2) {
|
|
176
180
|
const self = this.is;
|
|
177
181
|
if (arg1 === void 0)
|
|
178
|
-
return
|
|
182
|
+
return snapshot(self);
|
|
179
183
|
if (typeof arg1 == "function") {
|
|
180
184
|
let pending = /* @__PURE__ */ new Set();
|
|
181
|
-
return
|
|
185
|
+
return createEffect(self, (state) => {
|
|
182
186
|
const cb = arg1.call(state, state, pending);
|
|
183
187
|
return cb === null ? cb : (update2) => {
|
|
184
188
|
pending = PENDING2.get(self);
|
|
185
189
|
if (typeof cb == "function")
|
|
186
|
-
|
|
190
|
+
cb(update2);
|
|
187
191
|
};
|
|
188
192
|
});
|
|
189
193
|
}
|
|
@@ -238,14 +242,13 @@ var Model = class _Model {
|
|
|
238
242
|
return Object.entries(STATE.get(this.is))[Symbol.iterator]();
|
|
239
243
|
}
|
|
240
244
|
/**
|
|
241
|
-
* Create and activate a new instance of this
|
|
245
|
+
* Create and activate a new instance of this model.
|
|
242
246
|
*
|
|
243
247
|
* **Important** - Unlike `new this(...)` - this method also activates state.
|
|
244
248
|
*
|
|
245
249
|
* @param args - arguments sent to constructor
|
|
246
250
|
*/
|
|
247
251
|
static new(...args) {
|
|
248
|
-
_Model.is(this);
|
|
249
252
|
const instance = new this(...args);
|
|
250
253
|
emit(instance, true);
|
|
251
254
|
return instance;
|
|
@@ -257,9 +260,7 @@ var Model = class _Model {
|
|
|
257
260
|
* methods and properties of this class.
|
|
258
261
|
*/
|
|
259
262
|
static is(maybe) {
|
|
260
|
-
|
|
261
|
-
throw new Error("Model is abstract and not a valid type.");
|
|
262
|
-
return typeof maybe == "function" && maybe.prototype instanceof this;
|
|
263
|
+
return maybe === this || typeof maybe == "function" && maybe.prototype instanceof this;
|
|
263
264
|
}
|
|
264
265
|
/**
|
|
265
266
|
* Register a callback to run when any instance of this Model is updated.
|
|
@@ -283,50 +284,59 @@ define(Model, "toString", {
|
|
|
283
284
|
}
|
|
284
285
|
});
|
|
285
286
|
function prepare(model) {
|
|
287
|
+
let T = model.constructor;
|
|
288
|
+
if (T === Model)
|
|
289
|
+
throw new Error("Cannot create base Model.");
|
|
286
290
|
const chain = [];
|
|
287
|
-
let type = model.constructor;
|
|
288
291
|
let keys2 = /* @__PURE__ */ new Set();
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
for (const type2 of chain) {
|
|
294
|
-
for (const cb of NOTIFY.get(type2) || [])
|
|
292
|
+
ID.set(model, `${T}-${uid()}`);
|
|
293
|
+
while (T.name) {
|
|
294
|
+
for (const cb of NOTIFY.get(T) || [])
|
|
295
295
|
addListener(model, cb);
|
|
296
|
-
if (
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
296
|
+
if (T === Model)
|
|
297
|
+
break;
|
|
298
|
+
else
|
|
299
|
+
chain.unshift(T);
|
|
300
|
+
T = Object.getPrototypeOf(T);
|
|
301
|
+
}
|
|
302
|
+
for (const type of chain) {
|
|
303
|
+
if (METHODS.has(type)) {
|
|
304
|
+
keys2 = METHODS.get(type);
|
|
300
305
|
continue;
|
|
301
306
|
}
|
|
302
|
-
METHODS.set(
|
|
303
|
-
for (const
|
|
304
|
-
|
|
307
|
+
METHODS.set(type, keys2 = new Set(keys2));
|
|
308
|
+
for (const [key2, { value }] of Object.entries(
|
|
309
|
+
Object.getOwnPropertyDescriptors(type.prototype)
|
|
310
|
+
)) {
|
|
311
|
+
let get3 = function() {
|
|
312
|
+
return this.hasOwnProperty(key2) ? value : set3.call(this, value);
|
|
313
|
+
}, set3 = function(method) {
|
|
305
314
|
const value2 = method.bind(this.is);
|
|
306
315
|
METHOD.set(value2, method);
|
|
307
316
|
define(this.is, key2, { value: value2, writable: true });
|
|
308
317
|
return value2;
|
|
309
318
|
};
|
|
310
|
-
var set2 = set3;
|
|
319
|
+
var get2 = get3, set2 = set3;
|
|
311
320
|
if (!value || key2 == "constructor")
|
|
312
321
|
continue;
|
|
313
322
|
keys2.add(key2);
|
|
314
|
-
|
|
315
|
-
set: set3,
|
|
316
|
-
get() {
|
|
317
|
-
return this.hasOwnProperty(key2) ? value : set3.call(this, value);
|
|
318
|
-
}
|
|
319
|
-
});
|
|
323
|
+
define(type.prototype, key2, { get: get3, set: set3 });
|
|
320
324
|
}
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
|
-
function
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
+
function init(model, args) {
|
|
328
|
+
const state = {};
|
|
329
|
+
const clean = /* @__PURE__ */ new Set();
|
|
330
|
+
STATE.set(model, state);
|
|
331
|
+
args = args.flat().filter((arg) => {
|
|
332
|
+
if (typeof arg != "string")
|
|
333
|
+
return true;
|
|
334
|
+
ID.set(model, arg);
|
|
335
|
+
});
|
|
327
336
|
addListener(model, () => {
|
|
328
|
-
|
|
329
|
-
|
|
337
|
+
if (!PARENT.has(model))
|
|
338
|
+
PARENT.set(model, null);
|
|
339
|
+
args.forEach((arg) => {
|
|
330
340
|
const use2 = typeof arg == "function" ? arg.call(model, model) : arg;
|
|
331
341
|
if (use2 instanceof Promise)
|
|
332
342
|
use2.catch((err) => {
|
|
@@ -336,37 +346,30 @@ function apply(model, args) {
|
|
|
336
346
|
else if (typeof use2 == "object")
|
|
337
347
|
assign(model, use2);
|
|
338
348
|
else if (typeof use2 == "function")
|
|
339
|
-
|
|
349
|
+
clean.add(use2);
|
|
350
|
+
});
|
|
351
|
+
for (const key2 in model) {
|
|
352
|
+
const desc = Object.getOwnPropertyDescriptor(model, key2);
|
|
353
|
+
if ("value" in desc) {
|
|
354
|
+
update(model, key2, desc.value, true);
|
|
355
|
+
define(model, key2, {
|
|
356
|
+
configurable: false,
|
|
357
|
+
set: (x) => update(model, key2, x),
|
|
358
|
+
get() {
|
|
359
|
+
return watch(this, key2, state[key2]);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
340
363
|
}
|
|
341
|
-
addListener(model, () => {
|
|
342
|
-
done.forEach((x) => x());
|
|
343
|
-
}, null);
|
|
344
364
|
return null;
|
|
345
365
|
});
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
const state = STATE.get(model);
|
|
349
|
-
if (!PARENT.has(model))
|
|
350
|
-
PARENT.set(model, null);
|
|
351
|
-
for (const key2 in model) {
|
|
352
|
-
const desc = Object.getOwnPropertyDescriptor(model, key2);
|
|
353
|
-
if ("value" in desc) {
|
|
354
|
-
update(model, key2, desc.value, true);
|
|
355
|
-
define(model, key2, {
|
|
356
|
-
configurable: false,
|
|
357
|
-
set: (x) => update(model, key2, x),
|
|
358
|
-
get() {
|
|
359
|
-
return watch(this, key2, state[key2]);
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
return () => {
|
|
366
|
+
addListener(model, () => {
|
|
367
|
+
clean.forEach((x) => x());
|
|
365
368
|
for (const [_, value] of model)
|
|
366
369
|
if (value instanceof Model && PARENT.get(value) === model)
|
|
367
370
|
value.set(null);
|
|
368
371
|
Object.freeze(state);
|
|
369
|
-
};
|
|
372
|
+
}, null);
|
|
370
373
|
}
|
|
371
374
|
function fetch(subject, property, required) {
|
|
372
375
|
const state = STATE.get(subject);
|
|
@@ -393,6 +396,25 @@ function fetch(subject, property, required) {
|
|
|
393
396
|
stack: error.stack
|
|
394
397
|
});
|
|
395
398
|
}
|
|
399
|
+
var EXPORT;
|
|
400
|
+
function snapshot(model) {
|
|
401
|
+
const values = {};
|
|
402
|
+
let isNotRecursive;
|
|
403
|
+
if (!EXPORT) {
|
|
404
|
+
isNotRecursive = true;
|
|
405
|
+
EXPORT = /* @__PURE__ */ new Map([[model, values]]);
|
|
406
|
+
}
|
|
407
|
+
for (let [key2, value] of model) {
|
|
408
|
+
if (EXPORT.has(value))
|
|
409
|
+
value = EXPORT.get(value);
|
|
410
|
+
else if (value && typeof value == "object" && "get" in value && typeof value.get === "function")
|
|
411
|
+
EXPORT.set(value, value = value.get());
|
|
412
|
+
values[key2] = value;
|
|
413
|
+
}
|
|
414
|
+
if (isNotRecursive)
|
|
415
|
+
EXPORT = void 0;
|
|
416
|
+
return Object.freeze(values);
|
|
417
|
+
}
|
|
396
418
|
function update(subject, key2, value, arg) {
|
|
397
419
|
const state = STATE.get(subject);
|
|
398
420
|
if (Object.isFrozen(state))
|
|
@@ -429,42 +451,23 @@ function event(subject, key2, silent) {
|
|
|
429
451
|
if (!silent)
|
|
430
452
|
emit(subject, key2);
|
|
431
453
|
}
|
|
432
|
-
function assign(to,
|
|
454
|
+
function assign(to, values) {
|
|
433
455
|
const methods = METHODS.get(to.constructor);
|
|
434
|
-
for (const key2 in
|
|
456
|
+
for (const key2 in values)
|
|
435
457
|
if (key2 in to || methods.has(key2))
|
|
436
|
-
to[key2] =
|
|
437
|
-
}
|
|
438
|
-
function values(model) {
|
|
439
|
-
const values2 = {};
|
|
440
|
-
const current = !EXPORT && (EXPORT = /* @__PURE__ */ new Map([[model, values2]]));
|
|
441
|
-
for (let [key2, value] of model) {
|
|
442
|
-
if (EXPORT.has(value))
|
|
443
|
-
value = EXPORT.get(value);
|
|
444
|
-
else if (value && typeof value.get === "function")
|
|
445
|
-
EXPORT.set(value, value = value.get());
|
|
446
|
-
values2[key2] = value;
|
|
447
|
-
}
|
|
448
|
-
if (current)
|
|
449
|
-
EXPORT = void 0;
|
|
450
|
-
return Object.freeze(values2);
|
|
458
|
+
to[key2] = values[key2];
|
|
451
459
|
}
|
|
452
460
|
function uid() {
|
|
453
461
|
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
454
462
|
}
|
|
455
|
-
function entries(object) {
|
|
456
|
-
return Object.entries(
|
|
457
|
-
Object.getOwnPropertyDescriptors(object)
|
|
458
|
-
).map(([key2, desc]) => ({ key: key2, ...desc }));
|
|
459
|
-
}
|
|
460
463
|
|
|
461
464
|
// src/context.ts
|
|
462
|
-
var
|
|
463
|
-
var
|
|
465
|
+
var LOOKUP = /* @__PURE__ */ new WeakMap();
|
|
466
|
+
var KEYS = /* @__PURE__ */ new Map();
|
|
464
467
|
function key(T, upstream) {
|
|
465
|
-
let K =
|
|
468
|
+
let K = KEYS.get(T);
|
|
466
469
|
if (!K)
|
|
467
|
-
|
|
470
|
+
KEYS.set(T, K = Symbol(
|
|
468
471
|
typeof T == "symbol" ? "get " + T.description : String(T)
|
|
469
472
|
));
|
|
470
473
|
return upstream ? key(K) : K;
|
|
@@ -484,7 +487,7 @@ var Context = class _Context {
|
|
|
484
487
|
this.include(inputs);
|
|
485
488
|
}
|
|
486
489
|
static get(from, callback) {
|
|
487
|
-
const waiting =
|
|
490
|
+
const waiting = LOOKUP.get(from);
|
|
488
491
|
if (waiting instanceof _Context) {
|
|
489
492
|
if (callback)
|
|
490
493
|
callback(waiting);
|
|
@@ -494,11 +497,14 @@ var Context = class _Context {
|
|
|
494
497
|
if (waiting)
|
|
495
498
|
waiting.push(callback);
|
|
496
499
|
else
|
|
497
|
-
|
|
500
|
+
LOOKUP.set(from, [callback]);
|
|
498
501
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
+
/** Run callback when a specified type is registered to a **child** context. */
|
|
503
|
+
has(Type, callback) {
|
|
504
|
+
define(this, key(Type, true), { value: callback });
|
|
505
|
+
}
|
|
506
|
+
/** Find specified type registered to a parent context. Returns undefined if none are found. */
|
|
507
|
+
get(Type) {
|
|
502
508
|
const result = this[key(Type)];
|
|
503
509
|
if (result === null)
|
|
504
510
|
throw new Error(`Did find ${Type} in context, but multiple were defined.`);
|
|
@@ -520,9 +526,16 @@ var Context = class _Context {
|
|
|
520
526
|
}
|
|
521
527
|
include(inputs, forEach) {
|
|
522
528
|
const init2 = /* @__PURE__ */ new Map();
|
|
529
|
+
if (typeof inputs == "function" || inputs instanceof Model)
|
|
530
|
+
inputs = { 0: inputs };
|
|
523
531
|
Object.entries(inputs).forEach(([K, V]) => {
|
|
524
|
-
if (
|
|
525
|
-
|
|
532
|
+
if (Model.is(V) || V instanceof Model)
|
|
533
|
+
return;
|
|
534
|
+
if (K && K != V)
|
|
535
|
+
V = `${V} (as '${K}')`;
|
|
536
|
+
throw new Error(
|
|
537
|
+
`Context may only include instance or class \`extends Model\` but got ${V}.`
|
|
538
|
+
);
|
|
526
539
|
});
|
|
527
540
|
for (const key2 in inputs) {
|
|
528
541
|
const input = inputs[key2];
|
|
@@ -538,9 +551,11 @@ var Context = class _Context {
|
|
|
538
551
|
}
|
|
539
552
|
}
|
|
540
553
|
for (const [model, explicit] of init2) {
|
|
541
|
-
model
|
|
542
|
-
if (forEach)
|
|
554
|
+
emit(model, true);
|
|
555
|
+
if (typeof forEach == "function")
|
|
543
556
|
forEach(model, explicit);
|
|
557
|
+
else if (forEach && explicit)
|
|
558
|
+
assign(model, forEach);
|
|
544
559
|
for (const [_key, value] of model)
|
|
545
560
|
if (PARENT.get(value) === model) {
|
|
546
561
|
this.add(value, true);
|
|
@@ -571,15 +586,15 @@ var Context = class _Context {
|
|
|
571
586
|
keys(T).forEach((K) => {
|
|
572
587
|
const value = this.hasOwnProperty(K) ? null : I;
|
|
573
588
|
if (value || this[K] !== I && !implicit)
|
|
574
|
-
|
|
589
|
+
define(this, K, {
|
|
575
590
|
configurable: true,
|
|
576
591
|
value
|
|
577
592
|
});
|
|
578
593
|
});
|
|
579
|
-
const waiting =
|
|
594
|
+
const waiting = LOOKUP.get(I);
|
|
580
595
|
if (waiting instanceof Array)
|
|
581
596
|
waiting.forEach((cb) => cb(this));
|
|
582
|
-
|
|
597
|
+
LOOKUP.set(I, this);
|
|
583
598
|
return I;
|
|
584
599
|
}
|
|
585
600
|
};
|
|
@@ -611,7 +626,8 @@ function use(arg1, arg2) {
|
|
|
611
626
|
}
|
|
612
627
|
Model.on((_, model) => {
|
|
613
628
|
const state = STATE.get(model);
|
|
614
|
-
for (const
|
|
629
|
+
for (const key2 in model) {
|
|
630
|
+
const { value } = Object.getOwnPropertyDescriptor(model, key2);
|
|
615
631
|
const instruction = INSTRUCT.get(value);
|
|
616
632
|
if (!instruction) {
|
|
617
633
|
if (value instanceof Model && key2 !== "is")
|
|
@@ -626,7 +642,7 @@ Model.on((_, model) => {
|
|
|
626
642
|
const desc = typeof output == "object" ? output : { get: output };
|
|
627
643
|
if ("value" in desc)
|
|
628
644
|
state[key2] = desc.value;
|
|
629
|
-
|
|
645
|
+
define(model, key2, {
|
|
630
646
|
enumerable: desc.enumerable !== false,
|
|
631
647
|
get() {
|
|
632
648
|
return watch(
|
|
@@ -679,7 +695,7 @@ function get(arg0, arg1) {
|
|
|
679
695
|
}
|
|
680
696
|
if (typeof arg1 == "function") {
|
|
681
697
|
let connect2 = function(model) {
|
|
682
|
-
reset =
|
|
698
|
+
reset = createEffect(model, (current) => {
|
|
683
699
|
proxy = current;
|
|
684
700
|
if (!(key2 in state) || STALE.delete(compute2))
|
|
685
701
|
compute2(!reset);
|
|
@@ -723,7 +739,6 @@ function get(arg0, arg1) {
|
|
|
723
739
|
}
|
|
724
740
|
|
|
725
741
|
// src/instruction/ref.ts
|
|
726
|
-
var define2 = Object.defineProperty;
|
|
727
742
|
function ref(arg, arg2) {
|
|
728
743
|
return use((key2, subject, state) => {
|
|
729
744
|
let value = {};
|
|
@@ -731,20 +746,20 @@ function ref(arg, arg2) {
|
|
|
731
746
|
subject.get(() => {
|
|
732
747
|
for (const key3 in state)
|
|
733
748
|
if (typeof arg2 == "function")
|
|
734
|
-
|
|
749
|
+
define(value, key3, {
|
|
735
750
|
configurable: true,
|
|
736
751
|
get() {
|
|
737
752
|
const out = arg2(key3);
|
|
738
|
-
|
|
753
|
+
define(value, key3, { value: out });
|
|
739
754
|
return out;
|
|
740
755
|
}
|
|
741
756
|
});
|
|
742
757
|
else {
|
|
743
758
|
const get2 = () => state[key3];
|
|
744
759
|
const set2 = (value2) => subject.set(key3, value2);
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
760
|
+
define(set2, "current", { get: get2, set: set2 });
|
|
761
|
+
define(set2, "get", { value: get2 });
|
|
762
|
+
define(value, key3, { value: set2 });
|
|
748
763
|
}
|
|
749
764
|
});
|
|
750
765
|
else if (typeof arg == "object")
|
|
@@ -769,7 +784,7 @@ function ref(arg, arg2) {
|
|
|
769
784
|
get: { value: get2 }
|
|
770
785
|
});
|
|
771
786
|
}
|
|
772
|
-
|
|
787
|
+
define(subject, key2, { value });
|
|
773
788
|
});
|
|
774
789
|
}
|
|
775
790
|
|
|
@@ -856,7 +871,7 @@ function has(arg1, arg2) {
|
|
|
856
871
|
};
|
|
857
872
|
if (Model.is(arg1))
|
|
858
873
|
Context.get(subject, (context) => {
|
|
859
|
-
context.
|
|
874
|
+
context.has(arg1, (got) => {
|
|
860
875
|
let remove;
|
|
861
876
|
let disconnect;
|
|
862
877
|
if (applied.has(got))
|
|
@@ -920,7 +935,9 @@ function has(arg1, arg2) {
|
|
|
920
935
|
0 && (module.exports = {
|
|
921
936
|
Context,
|
|
922
937
|
Model,
|
|
923
|
-
|
|
938
|
+
addListener,
|
|
939
|
+
createEffect,
|
|
940
|
+
createProxy,
|
|
924
941
|
get,
|
|
925
942
|
has,
|
|
926
943
|
ref,
|
package/dist/index.mjs
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
// src/control.ts
|
|
2
2
|
var DISPATCH = /* @__PURE__ */ new Set();
|
|
3
3
|
var OBSERVER = /* @__PURE__ */ new WeakMap();
|
|
4
|
-
var
|
|
4
|
+
var LISTENERS = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
var PENDING = /* @__PURE__ */ new WeakMap();
|
|
5
6
|
var onReady = () => null;
|
|
6
7
|
function addListener(subject, callback, select) {
|
|
7
|
-
let subs =
|
|
8
|
+
let subs = LISTENERS.get(subject);
|
|
8
9
|
if (!subs)
|
|
9
|
-
|
|
10
|
-
const filter = select
|
|
11
|
-
if (!
|
|
10
|
+
LISTENERS.set(subject, subs = /* @__PURE__ */ new Map([[onReady, void 0]]));
|
|
11
|
+
const filter = select === void 0 ? void 0 : /* @__PURE__ */ new Set([select]);
|
|
12
|
+
if (!subs.has(onReady) && !filter)
|
|
12
13
|
callback(true, subject);
|
|
13
14
|
subs.set(callback, filter);
|
|
14
15
|
return () => subs.delete(callback);
|
|
15
16
|
}
|
|
17
|
+
function createProxy(from, observer) {
|
|
18
|
+
const proxy = Object.create(from);
|
|
19
|
+
OBSERVER.set(proxy, observer);
|
|
20
|
+
return proxy;
|
|
21
|
+
}
|
|
16
22
|
function watch(from, key2, value) {
|
|
17
23
|
const access = OBSERVER.get(from);
|
|
18
24
|
return access ? access(from, key2, value) : value;
|
|
19
25
|
}
|
|
20
|
-
var PENDING = /* @__PURE__ */ new WeakMap();
|
|
21
26
|
function emit(source, key2) {
|
|
22
|
-
const subs =
|
|
27
|
+
const subs = LISTENERS.get(source);
|
|
23
28
|
const ready = !subs.has(onReady);
|
|
24
29
|
if (key2 === true && ready)
|
|
25
30
|
return;
|
|
@@ -45,27 +50,28 @@ function emit(source, key2) {
|
|
|
45
50
|
});
|
|
46
51
|
PENDING.delete(source);
|
|
47
52
|
}
|
|
48
|
-
function queue(
|
|
53
|
+
function queue(eventHandler) {
|
|
49
54
|
if (!DISPATCH.size)
|
|
50
55
|
setTimeout(() => {
|
|
51
|
-
DISPATCH.forEach((
|
|
56
|
+
DISPATCH.forEach((event2) => {
|
|
52
57
|
try {
|
|
53
|
-
|
|
58
|
+
event2();
|
|
54
59
|
} catch (err) {
|
|
55
60
|
console.error(err);
|
|
56
61
|
}
|
|
57
62
|
});
|
|
58
63
|
DISPATCH.clear();
|
|
59
64
|
}, 0);
|
|
60
|
-
DISPATCH.add(
|
|
65
|
+
DISPATCH.add(eventHandler);
|
|
61
66
|
}
|
|
62
|
-
function
|
|
63
|
-
const listeners =
|
|
67
|
+
function createEffect(target, callback, requireValues) {
|
|
68
|
+
const listeners = LISTENERS.get(target);
|
|
64
69
|
let unset;
|
|
65
70
|
let reset;
|
|
66
71
|
function invoke() {
|
|
67
72
|
let stale;
|
|
68
|
-
const subscriber =
|
|
73
|
+
const subscriber = createProxy(target, access);
|
|
74
|
+
LISTENERS.set(subscriber, listeners);
|
|
69
75
|
function onUpdate() {
|
|
70
76
|
if (stale)
|
|
71
77
|
return null;
|
|
@@ -79,20 +85,19 @@ function effect(target, callback, requireValues) {
|
|
|
79
85
|
function access(from, key2, value) {
|
|
80
86
|
if (value === void 0 && requireValues)
|
|
81
87
|
throw new Error(`${from}.${key2} is required in this context.`);
|
|
82
|
-
const listeners2 =
|
|
88
|
+
const listeners2 = LISTENERS.get(from);
|
|
83
89
|
let listener = listeners2.get(onUpdate);
|
|
84
90
|
if (!listener)
|
|
85
91
|
listeners2.set(onUpdate, listener = /* @__PURE__ */ new Set());
|
|
86
92
|
listener.add(key2);
|
|
87
|
-
const nested =
|
|
88
|
-
if (nested)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
const nested = LISTENERS.get(value);
|
|
94
|
+
if (nested)
|
|
95
|
+
LISTENERS.set(
|
|
96
|
+
value = createProxy(value, access),
|
|
97
|
+
nested
|
|
98
|
+
);
|
|
92
99
|
return value;
|
|
93
100
|
}
|
|
94
|
-
LISTENER.set(subscriber, listeners);
|
|
95
|
-
OBSERVER.set(subscriber, access);
|
|
96
101
|
try {
|
|
97
102
|
const out = callback.call(subscriber, subscriber);
|
|
98
103
|
unset = typeof out == "function" ? out : void 0;
|
|
@@ -129,27 +134,24 @@ var PENDING2 = /* @__PURE__ */ new WeakMap();
|
|
|
129
134
|
var PARENT = /* @__PURE__ */ new WeakMap();
|
|
130
135
|
var METHODS = /* @__PURE__ */ new WeakMap();
|
|
131
136
|
var METHOD = /* @__PURE__ */ new WeakMap();
|
|
132
|
-
var
|
|
133
|
-
var Model = class _Model {
|
|
137
|
+
var Model = class {
|
|
134
138
|
constructor(...args) {
|
|
135
|
-
ID.set(this, `${this.constructor}-${uid()}`);
|
|
136
|
-
STATE.set(this, {});
|
|
137
139
|
prepare(this);
|
|
138
140
|
define(this, "is", { value: this });
|
|
139
|
-
|
|
141
|
+
init(this, args);
|
|
140
142
|
}
|
|
141
143
|
get(arg1, arg2) {
|
|
142
144
|
const self = this.is;
|
|
143
145
|
if (arg1 === void 0)
|
|
144
|
-
return
|
|
146
|
+
return snapshot(self);
|
|
145
147
|
if (typeof arg1 == "function") {
|
|
146
148
|
let pending = /* @__PURE__ */ new Set();
|
|
147
|
-
return
|
|
149
|
+
return createEffect(self, (state) => {
|
|
148
150
|
const cb = arg1.call(state, state, pending);
|
|
149
151
|
return cb === null ? cb : (update2) => {
|
|
150
152
|
pending = PENDING2.get(self);
|
|
151
153
|
if (typeof cb == "function")
|
|
152
|
-
|
|
154
|
+
cb(update2);
|
|
153
155
|
};
|
|
154
156
|
});
|
|
155
157
|
}
|
|
@@ -204,14 +206,13 @@ var Model = class _Model {
|
|
|
204
206
|
return Object.entries(STATE.get(this.is))[Symbol.iterator]();
|
|
205
207
|
}
|
|
206
208
|
/**
|
|
207
|
-
* Create and activate a new instance of this
|
|
209
|
+
* Create and activate a new instance of this model.
|
|
208
210
|
*
|
|
209
211
|
* **Important** - Unlike `new this(...)` - this method also activates state.
|
|
210
212
|
*
|
|
211
213
|
* @param args - arguments sent to constructor
|
|
212
214
|
*/
|
|
213
215
|
static new(...args) {
|
|
214
|
-
_Model.is(this);
|
|
215
216
|
const instance = new this(...args);
|
|
216
217
|
emit(instance, true);
|
|
217
218
|
return instance;
|
|
@@ -223,9 +224,7 @@ var Model = class _Model {
|
|
|
223
224
|
* methods and properties of this class.
|
|
224
225
|
*/
|
|
225
226
|
static is(maybe) {
|
|
226
|
-
|
|
227
|
-
throw new Error("Model is abstract and not a valid type.");
|
|
228
|
-
return typeof maybe == "function" && maybe.prototype instanceof this;
|
|
227
|
+
return maybe === this || typeof maybe == "function" && maybe.prototype instanceof this;
|
|
229
228
|
}
|
|
230
229
|
/**
|
|
231
230
|
* Register a callback to run when any instance of this Model is updated.
|
|
@@ -249,50 +248,59 @@ define(Model, "toString", {
|
|
|
249
248
|
}
|
|
250
249
|
});
|
|
251
250
|
function prepare(model) {
|
|
251
|
+
let T = model.constructor;
|
|
252
|
+
if (T === Model)
|
|
253
|
+
throw new Error("Cannot create base Model.");
|
|
252
254
|
const chain = [];
|
|
253
|
-
let type = model.constructor;
|
|
254
255
|
let keys2 = /* @__PURE__ */ new Set();
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
for (const type2 of chain) {
|
|
260
|
-
for (const cb of NOTIFY.get(type2) || [])
|
|
256
|
+
ID.set(model, `${T}-${uid()}`);
|
|
257
|
+
while (T.name) {
|
|
258
|
+
for (const cb of NOTIFY.get(T) || [])
|
|
261
259
|
addListener(model, cb);
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
260
|
+
if (T === Model)
|
|
261
|
+
break;
|
|
262
|
+
else
|
|
263
|
+
chain.unshift(T);
|
|
264
|
+
T = Object.getPrototypeOf(T);
|
|
265
|
+
}
|
|
266
|
+
for (const type of chain) {
|
|
267
|
+
if (METHODS.has(type)) {
|
|
268
|
+
keys2 = METHODS.get(type);
|
|
266
269
|
continue;
|
|
267
270
|
}
|
|
268
|
-
METHODS.set(
|
|
269
|
-
for (const
|
|
270
|
-
|
|
271
|
+
METHODS.set(type, keys2 = new Set(keys2));
|
|
272
|
+
for (const [key2, { value }] of Object.entries(
|
|
273
|
+
Object.getOwnPropertyDescriptors(type.prototype)
|
|
274
|
+
)) {
|
|
275
|
+
let get3 = function() {
|
|
276
|
+
return this.hasOwnProperty(key2) ? value : set3.call(this, value);
|
|
277
|
+
}, set3 = function(method) {
|
|
271
278
|
const value2 = method.bind(this.is);
|
|
272
279
|
METHOD.set(value2, method);
|
|
273
280
|
define(this.is, key2, { value: value2, writable: true });
|
|
274
281
|
return value2;
|
|
275
282
|
};
|
|
276
|
-
var set2 = set3;
|
|
283
|
+
var get2 = get3, set2 = set3;
|
|
277
284
|
if (!value || key2 == "constructor")
|
|
278
285
|
continue;
|
|
279
286
|
keys2.add(key2);
|
|
280
|
-
|
|
281
|
-
set: set3,
|
|
282
|
-
get() {
|
|
283
|
-
return this.hasOwnProperty(key2) ? value : set3.call(this, value);
|
|
284
|
-
}
|
|
285
|
-
});
|
|
287
|
+
define(type.prototype, key2, { get: get3, set: set3 });
|
|
286
288
|
}
|
|
287
289
|
}
|
|
288
290
|
}
|
|
289
|
-
function
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
function init(model, args) {
|
|
292
|
+
const state = {};
|
|
293
|
+
const clean = /* @__PURE__ */ new Set();
|
|
294
|
+
STATE.set(model, state);
|
|
295
|
+
args = args.flat().filter((arg) => {
|
|
296
|
+
if (typeof arg != "string")
|
|
297
|
+
return true;
|
|
298
|
+
ID.set(model, arg);
|
|
299
|
+
});
|
|
293
300
|
addListener(model, () => {
|
|
294
|
-
|
|
295
|
-
|
|
301
|
+
if (!PARENT.has(model))
|
|
302
|
+
PARENT.set(model, null);
|
|
303
|
+
args.forEach((arg) => {
|
|
296
304
|
const use2 = typeof arg == "function" ? arg.call(model, model) : arg;
|
|
297
305
|
if (use2 instanceof Promise)
|
|
298
306
|
use2.catch((err) => {
|
|
@@ -302,37 +310,30 @@ function apply(model, args) {
|
|
|
302
310
|
else if (typeof use2 == "object")
|
|
303
311
|
assign(model, use2);
|
|
304
312
|
else if (typeof use2 == "function")
|
|
305
|
-
|
|
313
|
+
clean.add(use2);
|
|
314
|
+
});
|
|
315
|
+
for (const key2 in model) {
|
|
316
|
+
const desc = Object.getOwnPropertyDescriptor(model, key2);
|
|
317
|
+
if ("value" in desc) {
|
|
318
|
+
update(model, key2, desc.value, true);
|
|
319
|
+
define(model, key2, {
|
|
320
|
+
configurable: false,
|
|
321
|
+
set: (x) => update(model, key2, x),
|
|
322
|
+
get() {
|
|
323
|
+
return watch(this, key2, state[key2]);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
306
327
|
}
|
|
307
|
-
addListener(model, () => {
|
|
308
|
-
done.forEach((x) => x());
|
|
309
|
-
}, null);
|
|
310
328
|
return null;
|
|
311
329
|
});
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const state = STATE.get(model);
|
|
315
|
-
if (!PARENT.has(model))
|
|
316
|
-
PARENT.set(model, null);
|
|
317
|
-
for (const key2 in model) {
|
|
318
|
-
const desc = Object.getOwnPropertyDescriptor(model, key2);
|
|
319
|
-
if ("value" in desc) {
|
|
320
|
-
update(model, key2, desc.value, true);
|
|
321
|
-
define(model, key2, {
|
|
322
|
-
configurable: false,
|
|
323
|
-
set: (x) => update(model, key2, x),
|
|
324
|
-
get() {
|
|
325
|
-
return watch(this, key2, state[key2]);
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return () => {
|
|
330
|
+
addListener(model, () => {
|
|
331
|
+
clean.forEach((x) => x());
|
|
331
332
|
for (const [_, value] of model)
|
|
332
333
|
if (value instanceof Model && PARENT.get(value) === model)
|
|
333
334
|
value.set(null);
|
|
334
335
|
Object.freeze(state);
|
|
335
|
-
};
|
|
336
|
+
}, null);
|
|
336
337
|
}
|
|
337
338
|
function fetch(subject, property, required) {
|
|
338
339
|
const state = STATE.get(subject);
|
|
@@ -359,6 +360,25 @@ function fetch(subject, property, required) {
|
|
|
359
360
|
stack: error.stack
|
|
360
361
|
});
|
|
361
362
|
}
|
|
363
|
+
var EXPORT;
|
|
364
|
+
function snapshot(model) {
|
|
365
|
+
const values = {};
|
|
366
|
+
let isNotRecursive;
|
|
367
|
+
if (!EXPORT) {
|
|
368
|
+
isNotRecursive = true;
|
|
369
|
+
EXPORT = /* @__PURE__ */ new Map([[model, values]]);
|
|
370
|
+
}
|
|
371
|
+
for (let [key2, value] of model) {
|
|
372
|
+
if (EXPORT.has(value))
|
|
373
|
+
value = EXPORT.get(value);
|
|
374
|
+
else if (value && typeof value == "object" && "get" in value && typeof value.get === "function")
|
|
375
|
+
EXPORT.set(value, value = value.get());
|
|
376
|
+
values[key2] = value;
|
|
377
|
+
}
|
|
378
|
+
if (isNotRecursive)
|
|
379
|
+
EXPORT = void 0;
|
|
380
|
+
return Object.freeze(values);
|
|
381
|
+
}
|
|
362
382
|
function update(subject, key2, value, arg) {
|
|
363
383
|
const state = STATE.get(subject);
|
|
364
384
|
if (Object.isFrozen(state))
|
|
@@ -395,42 +415,23 @@ function event(subject, key2, silent) {
|
|
|
395
415
|
if (!silent)
|
|
396
416
|
emit(subject, key2);
|
|
397
417
|
}
|
|
398
|
-
function assign(to,
|
|
418
|
+
function assign(to, values) {
|
|
399
419
|
const methods = METHODS.get(to.constructor);
|
|
400
|
-
for (const key2 in
|
|
420
|
+
for (const key2 in values)
|
|
401
421
|
if (key2 in to || methods.has(key2))
|
|
402
|
-
to[key2] =
|
|
403
|
-
}
|
|
404
|
-
function values(model) {
|
|
405
|
-
const values2 = {};
|
|
406
|
-
const current = !EXPORT && (EXPORT = /* @__PURE__ */ new Map([[model, values2]]));
|
|
407
|
-
for (let [key2, value] of model) {
|
|
408
|
-
if (EXPORT.has(value))
|
|
409
|
-
value = EXPORT.get(value);
|
|
410
|
-
else if (value && typeof value.get === "function")
|
|
411
|
-
EXPORT.set(value, value = value.get());
|
|
412
|
-
values2[key2] = value;
|
|
413
|
-
}
|
|
414
|
-
if (current)
|
|
415
|
-
EXPORT = void 0;
|
|
416
|
-
return Object.freeze(values2);
|
|
422
|
+
to[key2] = values[key2];
|
|
417
423
|
}
|
|
418
424
|
function uid() {
|
|
419
425
|
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
420
426
|
}
|
|
421
|
-
function entries(object) {
|
|
422
|
-
return Object.entries(
|
|
423
|
-
Object.getOwnPropertyDescriptors(object)
|
|
424
|
-
).map(([key2, desc]) => ({ key: key2, ...desc }));
|
|
425
|
-
}
|
|
426
427
|
|
|
427
428
|
// src/context.ts
|
|
428
|
-
var
|
|
429
|
-
var
|
|
429
|
+
var LOOKUP = /* @__PURE__ */ new WeakMap();
|
|
430
|
+
var KEYS = /* @__PURE__ */ new Map();
|
|
430
431
|
function key(T, upstream) {
|
|
431
|
-
let K =
|
|
432
|
+
let K = KEYS.get(T);
|
|
432
433
|
if (!K)
|
|
433
|
-
|
|
434
|
+
KEYS.set(T, K = Symbol(
|
|
434
435
|
typeof T == "symbol" ? "get " + T.description : String(T)
|
|
435
436
|
));
|
|
436
437
|
return upstream ? key(K) : K;
|
|
@@ -450,7 +451,7 @@ var Context = class _Context {
|
|
|
450
451
|
this.include(inputs);
|
|
451
452
|
}
|
|
452
453
|
static get(from, callback) {
|
|
453
|
-
const waiting =
|
|
454
|
+
const waiting = LOOKUP.get(from);
|
|
454
455
|
if (waiting instanceof _Context) {
|
|
455
456
|
if (callback)
|
|
456
457
|
callback(waiting);
|
|
@@ -460,11 +461,14 @@ var Context = class _Context {
|
|
|
460
461
|
if (waiting)
|
|
461
462
|
waiting.push(callback);
|
|
462
463
|
else
|
|
463
|
-
|
|
464
|
+
LOOKUP.set(from, [callback]);
|
|
464
465
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
466
|
+
/** Run callback when a specified type is registered to a **child** context. */
|
|
467
|
+
has(Type, callback) {
|
|
468
|
+
define(this, key(Type, true), { value: callback });
|
|
469
|
+
}
|
|
470
|
+
/** Find specified type registered to a parent context. Returns undefined if none are found. */
|
|
471
|
+
get(Type) {
|
|
468
472
|
const result = this[key(Type)];
|
|
469
473
|
if (result === null)
|
|
470
474
|
throw new Error(`Did find ${Type} in context, but multiple were defined.`);
|
|
@@ -486,9 +490,16 @@ var Context = class _Context {
|
|
|
486
490
|
}
|
|
487
491
|
include(inputs, forEach) {
|
|
488
492
|
const init2 = /* @__PURE__ */ new Map();
|
|
493
|
+
if (typeof inputs == "function" || inputs instanceof Model)
|
|
494
|
+
inputs = { 0: inputs };
|
|
489
495
|
Object.entries(inputs).forEach(([K, V]) => {
|
|
490
|
-
if (
|
|
491
|
-
|
|
496
|
+
if (Model.is(V) || V instanceof Model)
|
|
497
|
+
return;
|
|
498
|
+
if (K && K != V)
|
|
499
|
+
V = `${V} (as '${K}')`;
|
|
500
|
+
throw new Error(
|
|
501
|
+
`Context may only include instance or class \`extends Model\` but got ${V}.`
|
|
502
|
+
);
|
|
492
503
|
});
|
|
493
504
|
for (const key2 in inputs) {
|
|
494
505
|
const input = inputs[key2];
|
|
@@ -504,9 +515,11 @@ var Context = class _Context {
|
|
|
504
515
|
}
|
|
505
516
|
}
|
|
506
517
|
for (const [model, explicit] of init2) {
|
|
507
|
-
model
|
|
508
|
-
if (forEach)
|
|
518
|
+
emit(model, true);
|
|
519
|
+
if (typeof forEach == "function")
|
|
509
520
|
forEach(model, explicit);
|
|
521
|
+
else if (forEach && explicit)
|
|
522
|
+
assign(model, forEach);
|
|
510
523
|
for (const [_key, value] of model)
|
|
511
524
|
if (PARENT.get(value) === model) {
|
|
512
525
|
this.add(value, true);
|
|
@@ -537,15 +550,15 @@ var Context = class _Context {
|
|
|
537
550
|
keys(T).forEach((K) => {
|
|
538
551
|
const value = this.hasOwnProperty(K) ? null : I;
|
|
539
552
|
if (value || this[K] !== I && !implicit)
|
|
540
|
-
|
|
553
|
+
define(this, K, {
|
|
541
554
|
configurable: true,
|
|
542
555
|
value
|
|
543
556
|
});
|
|
544
557
|
});
|
|
545
|
-
const waiting =
|
|
558
|
+
const waiting = LOOKUP.get(I);
|
|
546
559
|
if (waiting instanceof Array)
|
|
547
560
|
waiting.forEach((cb) => cb(this));
|
|
548
|
-
|
|
561
|
+
LOOKUP.set(I, this);
|
|
549
562
|
return I;
|
|
550
563
|
}
|
|
551
564
|
};
|
|
@@ -577,7 +590,8 @@ function use(arg1, arg2) {
|
|
|
577
590
|
}
|
|
578
591
|
Model.on((_, model) => {
|
|
579
592
|
const state = STATE.get(model);
|
|
580
|
-
for (const
|
|
593
|
+
for (const key2 in model) {
|
|
594
|
+
const { value } = Object.getOwnPropertyDescriptor(model, key2);
|
|
581
595
|
const instruction = INSTRUCT.get(value);
|
|
582
596
|
if (!instruction) {
|
|
583
597
|
if (value instanceof Model && key2 !== "is")
|
|
@@ -592,7 +606,7 @@ Model.on((_, model) => {
|
|
|
592
606
|
const desc = typeof output == "object" ? output : { get: output };
|
|
593
607
|
if ("value" in desc)
|
|
594
608
|
state[key2] = desc.value;
|
|
595
|
-
|
|
609
|
+
define(model, key2, {
|
|
596
610
|
enumerable: desc.enumerable !== false,
|
|
597
611
|
get() {
|
|
598
612
|
return watch(
|
|
@@ -645,7 +659,7 @@ function get(arg0, arg1) {
|
|
|
645
659
|
}
|
|
646
660
|
if (typeof arg1 == "function") {
|
|
647
661
|
let connect2 = function(model) {
|
|
648
|
-
reset =
|
|
662
|
+
reset = createEffect(model, (current) => {
|
|
649
663
|
proxy = current;
|
|
650
664
|
if (!(key2 in state) || STALE.delete(compute2))
|
|
651
665
|
compute2(!reset);
|
|
@@ -689,7 +703,6 @@ function get(arg0, arg1) {
|
|
|
689
703
|
}
|
|
690
704
|
|
|
691
705
|
// src/instruction/ref.ts
|
|
692
|
-
var define2 = Object.defineProperty;
|
|
693
706
|
function ref(arg, arg2) {
|
|
694
707
|
return use((key2, subject, state) => {
|
|
695
708
|
let value = {};
|
|
@@ -697,20 +710,20 @@ function ref(arg, arg2) {
|
|
|
697
710
|
subject.get(() => {
|
|
698
711
|
for (const key3 in state)
|
|
699
712
|
if (typeof arg2 == "function")
|
|
700
|
-
|
|
713
|
+
define(value, key3, {
|
|
701
714
|
configurable: true,
|
|
702
715
|
get() {
|
|
703
716
|
const out = arg2(key3);
|
|
704
|
-
|
|
717
|
+
define(value, key3, { value: out });
|
|
705
718
|
return out;
|
|
706
719
|
}
|
|
707
720
|
});
|
|
708
721
|
else {
|
|
709
722
|
const get2 = () => state[key3];
|
|
710
723
|
const set2 = (value2) => subject.set(key3, value2);
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
724
|
+
define(set2, "current", { get: get2, set: set2 });
|
|
725
|
+
define(set2, "get", { value: get2 });
|
|
726
|
+
define(value, key3, { value: set2 });
|
|
714
727
|
}
|
|
715
728
|
});
|
|
716
729
|
else if (typeof arg == "object")
|
|
@@ -735,7 +748,7 @@ function ref(arg, arg2) {
|
|
|
735
748
|
get: { value: get2 }
|
|
736
749
|
});
|
|
737
750
|
}
|
|
738
|
-
|
|
751
|
+
define(subject, key2, { value });
|
|
739
752
|
});
|
|
740
753
|
}
|
|
741
754
|
|
|
@@ -822,7 +835,7 @@ function has(arg1, arg2) {
|
|
|
822
835
|
};
|
|
823
836
|
if (Model.is(arg1))
|
|
824
837
|
Context.get(subject, (context) => {
|
|
825
|
-
context.
|
|
838
|
+
context.has(arg1, (got) => {
|
|
826
839
|
let remove;
|
|
827
840
|
let disconnect;
|
|
828
841
|
if (applied.has(got))
|
|
@@ -885,8 +898,10 @@ function has(arg1, arg2) {
|
|
|
885
898
|
export {
|
|
886
899
|
Context,
|
|
887
900
|
Model,
|
|
901
|
+
addListener,
|
|
902
|
+
createEffect,
|
|
903
|
+
createProxy,
|
|
888
904
|
Model as default,
|
|
889
|
-
effect,
|
|
890
905
|
get,
|
|
891
906
|
has,
|
|
892
907
|
ref,
|
package/package.json
CHANGED