@expressive/mvc 0.36.2 → 0.37.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/dist/index.d.ts +83 -19
- package/dist/index.js +105 -107
- package/dist/index.mjs +105 -107
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,15 +5,20 @@ declare namespace Model {
|
|
|
5
5
|
type New<T extends Model = Model> = (new () => T) & typeof Model;
|
|
6
6
|
/** Subset of `keyof T` which are not methods or defined by base Model U. **/
|
|
7
7
|
type Field<T> = Exclude<keyof T, keyof Model>;
|
|
8
|
+
/** Any valid key for controller, including but not limited to Field<T>. */
|
|
8
9
|
type Key<T> = Field<T> | (string & {}) | number | symbol;
|
|
9
|
-
type Value<T extends Model, K extends Key<T>> = K extends keyof T ? T[K] extends Ref<infer V> ? V : T[K] : unknown;
|
|
10
|
-
type OnUpdate<T extends Model, K extends Key<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
|
|
11
10
|
/** Export/Import compatible value for a given property in a Model. */
|
|
12
|
-
type Export<R> = R extends
|
|
11
|
+
type Export<R> = R extends {
|
|
12
|
+
get(): infer T;
|
|
13
|
+
} ? T : R;
|
|
14
|
+
/** Value for a property managed by a controller. */
|
|
15
|
+
type Value<T extends Model, K extends Key<T>> = K extends keyof T ? Export<T[K]> : unknown;
|
|
16
|
+
type OnEvent<T extends Model> = (this: T, key: unknown, source: T) => (() => void) | void | null;
|
|
17
|
+
type OnUpdate<T extends Model, K extends Key<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
|
|
13
18
|
/**
|
|
14
19
|
* Values from current state of given controller.
|
|
15
20
|
* Differs from `Values` as values here will drill
|
|
16
|
-
* into "real" values held by exotics like ref.
|
|
21
|
+
* into "real" values held by exotics like ref.Object.
|
|
17
22
|
*/
|
|
18
23
|
type State<T> = {
|
|
19
24
|
[P in Field<T>]: Export<T[P]>;
|
|
@@ -27,9 +32,23 @@ declare namespace Model {
|
|
|
27
32
|
(next: T): void;
|
|
28
33
|
current: T | null;
|
|
29
34
|
};
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
/**
|
|
36
|
+
* A callback function which is subscribed to parent and updates when accessed properties change.
|
|
37
|
+
*
|
|
38
|
+
* @param current - Current state of this controller. This is a proxy which detects properties which
|
|
39
|
+
* where accessed, and thus depended upon to stay current.
|
|
40
|
+
*
|
|
41
|
+
* @param update - Set of properties which have changed, and events fired, since last update.
|
|
42
|
+
*
|
|
43
|
+
* @returns A callback function which will be called when this effect is stale.
|
|
44
|
+
*/
|
|
45
|
+
type Effect<T> = (this: T, current: T, update: Set<Model.Key<T>>) => EffectCallback | Promise<void> | null | void;
|
|
46
|
+
/**
|
|
47
|
+
* A callback function returned by effect. Will be called when effect is stale.
|
|
48
|
+
*
|
|
49
|
+
* @param update - `true` if update is pending, `false` effect has been cancelled, `null` if controller is destroyed.
|
|
50
|
+
*/
|
|
51
|
+
type EffectCallback = ((update: boolean | null) => void);
|
|
33
52
|
namespace Instruction {
|
|
34
53
|
type Getter<T> = (source: Model) => T;
|
|
35
54
|
type Setter<T> = (value: T, previous: T) => boolean | void | (() => T);
|
|
@@ -57,13 +76,42 @@ declare class Model {
|
|
|
57
76
|
constructor(arg?: string | (() => void | (() => void)));
|
|
58
77
|
/** Pull current values from state. Flattens all models and exotic values recursively. */
|
|
59
78
|
get(): Model.State<this>;
|
|
60
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* Run a function which will run automatically when accessed values change.
|
|
81
|
+
*
|
|
82
|
+
* @param effect - Function to run, and again whenever accessed values change.
|
|
83
|
+
* If effect returns a function, it will be called when a change occurs (syncronously),
|
|
84
|
+
* effect is cancelled, or parent controller is destroyed.
|
|
85
|
+
*
|
|
86
|
+
* @returns Function to cancel listener.
|
|
87
|
+
*/
|
|
61
88
|
get(effect: Model.Effect<this>): () => void;
|
|
89
|
+
/**
|
|
90
|
+
* Get value of a property.
|
|
91
|
+
*
|
|
92
|
+
* @param key - Property to get value of.
|
|
93
|
+
* @param required - If true, will throw an error if property is not available.
|
|
94
|
+
*/
|
|
62
95
|
get<T extends Model.Key<this>>(key: T, required?: boolean): Model.Value<this, T>;
|
|
96
|
+
/**
|
|
97
|
+
* Run a function when a property is updated.
|
|
98
|
+
*
|
|
99
|
+
* @param key - Property to watch for updates.
|
|
100
|
+
* @param callback - Function to call when property is updated.
|
|
101
|
+
*/
|
|
63
102
|
get<T extends Model.Key<this>>(key: T, callback: Model.OnUpdate<this, T>): () => void;
|
|
64
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Check if model is expired.
|
|
105
|
+
*
|
|
106
|
+
* @returns `true` if model is expired, `false` otherwise.
|
|
107
|
+
*/
|
|
65
108
|
get(status: null): boolean;
|
|
66
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Callback when model is to be destroyed.
|
|
111
|
+
*
|
|
112
|
+
* @param callback - Function to call when model is destroyed.
|
|
113
|
+
* @returns Function to cancel listener.
|
|
114
|
+
*/
|
|
67
115
|
get(status: null, callback: () => void): () => void;
|
|
68
116
|
/**
|
|
69
117
|
* Get update in progress.
|
|
@@ -75,13 +123,15 @@ declare class Model {
|
|
|
75
123
|
* Call a function when update occurs.
|
|
76
124
|
*
|
|
77
125
|
* Given function is called for every assignment (which changes value) or explicit `set`.
|
|
78
|
-
*
|
|
79
|
-
*
|
|
126
|
+
*
|
|
127
|
+
* To run logic on final value only, callback may return a function. Using the same
|
|
128
|
+
* function for one or more events will ensure it is called only when events are settled.
|
|
80
129
|
*
|
|
81
130
|
* @param callback - Function to call when update occurs.
|
|
82
131
|
* @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.
|
|
132
|
+
*
|
|
83
133
|
*/
|
|
84
|
-
set(callback: Model.
|
|
134
|
+
set(callback: Model.OnEvent<this>): () => boolean;
|
|
85
135
|
/**
|
|
86
136
|
* Declare an end to updates. This event is final and will freeze state.
|
|
87
137
|
* This event can be watched for as well, to run cleanup logic and internally will remove all listeners.
|
|
@@ -93,10 +143,13 @@ declare class Model {
|
|
|
93
143
|
* Useful where a property value internally has changed, but the object is the same.
|
|
94
144
|
* For example: An array has pushed a new value, or a nested property is updated.
|
|
95
145
|
*
|
|
96
|
-
* You can also use this to dispatch arbitrary events
|
|
97
|
-
* recommended as non-property events, however you can use any string.
|
|
98
|
-
* be sure to avoid collisions with property names
|
|
99
|
-
* prefix
|
|
146
|
+
* You can also use this to dispatch arbitrary events.
|
|
147
|
+
* Symbols are recommended as non-property events, however you can use any string.
|
|
148
|
+
* If doing so, be sure to avoid collisions with property names! An easy way to do this is
|
|
149
|
+
* to prefix an event with "!" and/or use dash-case. e.g. `set("!event")` or `set("my-event")`.
|
|
150
|
+
*
|
|
151
|
+
* @param key - Property or event to dispatch.
|
|
152
|
+
* @returns Promise which resolves an array of keys which were updated.
|
|
100
153
|
*/
|
|
101
154
|
set(key: Model.Key<this>): PromiseLike<Model.Key<this>[]>;
|
|
102
155
|
/**
|
|
@@ -124,7 +177,10 @@ declare class Model {
|
|
|
124
177
|
* methods and properties of this class.
|
|
125
178
|
*/
|
|
126
179
|
static is<T extends Model.Type>(this: T, maybe: any): maybe is T;
|
|
127
|
-
|
|
180
|
+
/**
|
|
181
|
+
* Register a callback to run when any instance of this Model is updated.
|
|
182
|
+
*/
|
|
183
|
+
static on<T extends Model>(this: Model.Type<T>, listener: Model.OnEvent<T>): () => boolean;
|
|
128
184
|
}
|
|
129
185
|
|
|
130
186
|
declare function add<T = any>(instruction: Model.Instruction): T;
|
|
@@ -178,14 +234,22 @@ declare namespace ref {
|
|
|
178
234
|
type Callback<T> = (argument: T) => ((next: T | null) => void) | Promise<void> | void | boolean;
|
|
179
235
|
interface Object<T = any> {
|
|
180
236
|
(next: T | null): void;
|
|
237
|
+
/** Current value held by this reference. */
|
|
181
238
|
current: T | null;
|
|
239
|
+
/** Retrieve current value. */
|
|
240
|
+
get(): T | null;
|
|
182
241
|
}
|
|
183
242
|
/** Object with references to all managed values of `T`. */
|
|
243
|
+
/** Object with references to all managed values of `T`. */
|
|
184
244
|
type Proxy<T extends Model> = {
|
|
185
|
-
[P in Model.Field<T>]-?:
|
|
245
|
+
[P in Model.Field<T>]-?: Object<T[P]>;
|
|
246
|
+
} & {
|
|
247
|
+
get(): T;
|
|
186
248
|
};
|
|
187
249
|
type CustomProxy<T extends Model, R> = {
|
|
188
250
|
[P in Model.Field<T>]-?: R;
|
|
251
|
+
} & {
|
|
252
|
+
get(): T;
|
|
189
253
|
};
|
|
190
254
|
}
|
|
191
255
|
/**
|
package/dist/index.js
CHANGED
|
@@ -35,13 +35,11 @@ module.exports = __toCommonJS(src_exports);
|
|
|
35
35
|
var DISPATCH = /* @__PURE__ */ new Set();
|
|
36
36
|
var OBSERVER = /* @__PURE__ */ new WeakMap();
|
|
37
37
|
var LISTENER = /* @__PURE__ */ new WeakMap();
|
|
38
|
-
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
38
|
+
var onReady = () => null;
|
|
41
39
|
function addListener(subject, callback, select) {
|
|
42
40
|
let subs = LISTENER.get(subject);
|
|
43
41
|
if (!subs)
|
|
44
|
-
LISTENER.set(subject, subs =
|
|
42
|
+
LISTENER.set(subject, subs = /* @__PURE__ */ new Map([[onReady, false]]));
|
|
45
43
|
const filter = 2 in arguments && /* @__PURE__ */ new Set([select]);
|
|
46
44
|
if (!filter && !subs.has(onReady))
|
|
47
45
|
callback(true, subject);
|
|
@@ -49,9 +47,9 @@ function addListener(subject, callback, select) {
|
|
|
49
47
|
return () => subs.delete(callback);
|
|
50
48
|
}
|
|
51
49
|
function watch(from, key, value) {
|
|
52
|
-
const listeners = LISTENER.get(from);
|
|
53
50
|
const observer = OBSERVER.get(from);
|
|
54
51
|
if (observer) {
|
|
52
|
+
const listeners = LISTENER.get(from);
|
|
55
53
|
listeners.set(
|
|
56
54
|
observer,
|
|
57
55
|
(listeners.get(observer) || /* @__PURE__ */ new Set()).add(key)
|
|
@@ -105,79 +103,97 @@ function queue(event2) {
|
|
|
105
103
|
}
|
|
106
104
|
function effect(target, callback) {
|
|
107
105
|
const listeners = LISTENER.get(target);
|
|
108
|
-
let
|
|
109
|
-
let
|
|
106
|
+
let unset;
|
|
107
|
+
let reset;
|
|
110
108
|
function invoke() {
|
|
111
|
-
let
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
let stale;
|
|
110
|
+
const subscriber = Object.create(target);
|
|
111
|
+
LISTENER.set(subscriber, listeners);
|
|
112
|
+
OBSERVER.set(subscriber, () => {
|
|
113
|
+
if (stale)
|
|
115
114
|
return null;
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
stale = true;
|
|
116
|
+
if (reset && unset) {
|
|
117
|
+
unset(true);
|
|
118
|
+
unset = void 0;
|
|
120
119
|
}
|
|
121
|
-
return
|
|
122
|
-
};
|
|
123
|
-
LISTENER.set(local, listeners);
|
|
124
|
-
OBSERVER.set(local, update2);
|
|
120
|
+
return reset;
|
|
121
|
+
});
|
|
125
122
|
try {
|
|
126
|
-
const out = callback.call(
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
const out = callback.call(subscriber, subscriber);
|
|
124
|
+
unset = typeof out == "function" ? out : void 0;
|
|
125
|
+
reset = out === null ? out : invoke;
|
|
129
126
|
} catch (err) {
|
|
130
127
|
if (err instanceof Promise) {
|
|
131
|
-
|
|
132
|
-
err.then(invoke)
|
|
133
|
-
} else
|
|
134
|
-
console.error(err);
|
|
135
|
-
else
|
|
128
|
+
reset = void 0;
|
|
129
|
+
err.then(invoke);
|
|
130
|
+
} else
|
|
136
131
|
throw err;
|
|
137
132
|
}
|
|
138
133
|
}
|
|
139
134
|
addListener(target, (key) => {
|
|
140
135
|
if (key === true)
|
|
141
136
|
invoke();
|
|
142
|
-
else if (!
|
|
143
|
-
return
|
|
144
|
-
if (key === null &&
|
|
145
|
-
|
|
137
|
+
else if (!reset)
|
|
138
|
+
return reset;
|
|
139
|
+
if (key === null && unset)
|
|
140
|
+
unset(null);
|
|
146
141
|
});
|
|
147
142
|
return () => {
|
|
148
|
-
if (
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
if (unset)
|
|
144
|
+
unset(false);
|
|
145
|
+
reset = null;
|
|
151
146
|
};
|
|
152
147
|
}
|
|
153
148
|
|
|
154
149
|
// src/model.ts
|
|
155
|
-
var
|
|
156
|
-
var
|
|
150
|
+
var FLATTEN;
|
|
151
|
+
var REGISTER = /* @__PURE__ */ new WeakMap();
|
|
157
152
|
var STATE = /* @__PURE__ */ new WeakMap();
|
|
158
153
|
var NOTIFY = /* @__PURE__ */ new WeakMap();
|
|
159
154
|
var PENDING2 = /* @__PURE__ */ new WeakMap();
|
|
155
|
+
var PARENT = /* @__PURE__ */ new WeakMap();
|
|
160
156
|
var define = Object.defineProperty;
|
|
161
157
|
var Model = class _Model {
|
|
162
158
|
constructor(arg) {
|
|
159
|
+
const state = {};
|
|
163
160
|
let Type = this.constructor;
|
|
164
161
|
define(this, "is", { value: this });
|
|
165
|
-
STATE.set(this,
|
|
166
|
-
|
|
162
|
+
STATE.set(this, state);
|
|
163
|
+
REGISTER.set(this, typeof arg == "string" ? arg : `${Type}-${uid()}`);
|
|
167
164
|
while (true) {
|
|
168
165
|
new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
|
|
169
166
|
if (Type === _Model)
|
|
170
167
|
break;
|
|
171
168
|
Type = Object.getPrototypeOf(Type);
|
|
172
169
|
}
|
|
173
|
-
addListener(this,
|
|
174
|
-
|
|
170
|
+
addListener(this, () => {
|
|
171
|
+
let onDone;
|
|
172
|
+
for (const key in this) {
|
|
173
|
+
const desc = Object.getOwnPropertyDescriptor(this, key);
|
|
174
|
+
if ("value" in desc) {
|
|
175
|
+
state[key] = desc.value;
|
|
176
|
+
define(this, key, {
|
|
177
|
+
configurable: false,
|
|
178
|
+
set: (x) => update(this, key, x),
|
|
179
|
+
get() {
|
|
180
|
+
return watch(this, key, state[key]);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (typeof arg == "function")
|
|
186
|
+
onDone = arg();
|
|
175
187
|
addListener(this, () => {
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
188
|
+
for (const [_, value] of this)
|
|
189
|
+
if (value instanceof _Model && PARENT.get(value) === this)
|
|
190
|
+
value.set(null);
|
|
191
|
+
if (typeof onDone == "function")
|
|
192
|
+
onDone();
|
|
193
|
+
Object.freeze(state);
|
|
194
|
+
}, null);
|
|
195
|
+
return null;
|
|
196
|
+
});
|
|
181
197
|
}
|
|
182
198
|
get(arg1, arg2) {
|
|
183
199
|
const self = this.is;
|
|
@@ -185,15 +201,27 @@ var Model = class _Model {
|
|
|
185
201
|
let pending = /* @__PURE__ */ new Set();
|
|
186
202
|
return effect(self, (state) => {
|
|
187
203
|
const cb = arg1.call(state, state, pending);
|
|
188
|
-
return cb === null ?
|
|
204
|
+
return cb === null ? cb : (update2) => {
|
|
189
205
|
pending = PENDING2.get(self);
|
|
190
206
|
if (typeof cb == "function")
|
|
191
207
|
return cb(update2);
|
|
192
208
|
};
|
|
193
209
|
});
|
|
194
210
|
}
|
|
195
|
-
if (arg1 === void 0)
|
|
196
|
-
|
|
211
|
+
if (arg1 === void 0) {
|
|
212
|
+
const values = {};
|
|
213
|
+
const current = !FLATTEN && (FLATTEN = /* @__PURE__ */ new Map([[self, values]]));
|
|
214
|
+
for (let [key, value] of self) {
|
|
215
|
+
if (FLATTEN.has(value))
|
|
216
|
+
value = FLATTEN.get(value);
|
|
217
|
+
else if (value && typeof value.get === "function")
|
|
218
|
+
FLATTEN.set(value, value = value.get());
|
|
219
|
+
values[key] = value;
|
|
220
|
+
}
|
|
221
|
+
if (current)
|
|
222
|
+
FLATTEN = void 0;
|
|
223
|
+
return Object.freeze(values);
|
|
224
|
+
}
|
|
197
225
|
if (typeof arg2 == "function") {
|
|
198
226
|
if (arg1 === null)
|
|
199
227
|
return addListener(self, arg2.bind(this, this), null);
|
|
@@ -257,17 +285,20 @@ var Model = class _Model {
|
|
|
257
285
|
static is(maybe) {
|
|
258
286
|
return typeof maybe == "function" && maybe.prototype instanceof this;
|
|
259
287
|
}
|
|
260
|
-
|
|
288
|
+
/**
|
|
289
|
+
* Register a callback to run when any instance of this Model is updated.
|
|
290
|
+
*/
|
|
291
|
+
static on(listener) {
|
|
261
292
|
let notify = NOTIFY.get(this);
|
|
262
293
|
if (!notify)
|
|
263
294
|
NOTIFY.set(this, notify = /* @__PURE__ */ new Set());
|
|
264
|
-
notify.add(
|
|
265
|
-
return () => notify.delete(
|
|
295
|
+
notify.add(listener);
|
|
296
|
+
return () => notify.delete(listener);
|
|
266
297
|
}
|
|
267
298
|
};
|
|
268
299
|
define(Model.prototype, "toString", {
|
|
269
300
|
value() {
|
|
270
|
-
return
|
|
301
|
+
return REGISTER.get(this.is);
|
|
271
302
|
}
|
|
272
303
|
});
|
|
273
304
|
define(Model, "toString", {
|
|
@@ -275,29 +306,6 @@ define(Model, "toString", {
|
|
|
275
306
|
return this.name;
|
|
276
307
|
}
|
|
277
308
|
});
|
|
278
|
-
function bootstrap() {
|
|
279
|
-
const state = STATE.get(this);
|
|
280
|
-
for (const key in this) {
|
|
281
|
-
const desc = Object.getOwnPropertyDescriptor(this, key);
|
|
282
|
-
if ("value" in desc) {
|
|
283
|
-
state[key] = desc.value;
|
|
284
|
-
define(this, key, {
|
|
285
|
-
configurable: false,
|
|
286
|
-
set: (x) => update(this, key, x),
|
|
287
|
-
get() {
|
|
288
|
-
return watch(this, key, state[key]);
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
addListener(this, () => {
|
|
294
|
-
for (const [_, value] of this)
|
|
295
|
-
if (value instanceof Model && PARENT.get(value) === this)
|
|
296
|
-
value.set(null);
|
|
297
|
-
Object.freeze(state);
|
|
298
|
-
}, null);
|
|
299
|
-
return null;
|
|
300
|
-
}
|
|
301
309
|
function fetch(subject, property, required) {
|
|
302
310
|
const state = STATE.get(subject);
|
|
303
311
|
if (property in state || required === false) {
|
|
@@ -331,36 +339,22 @@ function update(subject, key, value, silent) {
|
|
|
331
339
|
if (value === previous)
|
|
332
340
|
return true;
|
|
333
341
|
state[key] = value;
|
|
334
|
-
if (silent
|
|
335
|
-
|
|
336
|
-
push(subject, key);
|
|
342
|
+
if (silent !== true)
|
|
343
|
+
push(subject, key);
|
|
337
344
|
}
|
|
338
|
-
function push(subject, key) {
|
|
345
|
+
function push(subject, key, silent) {
|
|
339
346
|
let pending = PENDING2.get(subject);
|
|
340
347
|
if (!pending) {
|
|
341
348
|
PENDING2.set(subject, pending = /* @__PURE__ */ new Set());
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
349
|
+
if (!silent)
|
|
350
|
+
queue(() => {
|
|
351
|
+
event(subject, false);
|
|
352
|
+
PENDING2.delete(subject);
|
|
353
|
+
});
|
|
346
354
|
}
|
|
347
355
|
pending.add(key);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
function extract(from) {
|
|
351
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
352
|
-
function get2(value) {
|
|
353
|
-
if (value instanceof Model) {
|
|
354
|
-
if (cache.has(value))
|
|
355
|
-
return cache.get(value);
|
|
356
|
-
const model = value;
|
|
357
|
-
cache.set(value, value = {});
|
|
358
|
-
for (const [key, val] of model)
|
|
359
|
-
value[key] = get2(val);
|
|
360
|
-
}
|
|
361
|
-
return value;
|
|
362
|
-
}
|
|
363
|
-
return get2(from);
|
|
356
|
+
if (!silent)
|
|
357
|
+
event(subject, key);
|
|
364
358
|
}
|
|
365
359
|
function uid() {
|
|
366
360
|
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
@@ -570,10 +564,10 @@ function compute(subject, key, source, setter) {
|
|
|
570
564
|
compute2(true);
|
|
571
565
|
else if (STALE.delete(compute2))
|
|
572
566
|
compute2();
|
|
573
|
-
return (
|
|
574
|
-
if (
|
|
567
|
+
return (didUpdate) => {
|
|
568
|
+
if (didUpdate) {
|
|
575
569
|
STALE.add(compute2);
|
|
576
|
-
subject
|
|
570
|
+
push(subject, key, true);
|
|
577
571
|
}
|
|
578
572
|
};
|
|
579
573
|
});
|
|
@@ -588,7 +582,7 @@ function compute(subject, key, source, setter) {
|
|
|
588
582
|
throw err;
|
|
589
583
|
console.error(err);
|
|
590
584
|
}
|
|
591
|
-
subject
|
|
585
|
+
update(subject, key, next, !isAsync);
|
|
592
586
|
}
|
|
593
587
|
return () => {
|
|
594
588
|
if (!proxy) {
|
|
@@ -619,8 +613,10 @@ function ref(arg, arg2) {
|
|
|
619
613
|
}
|
|
620
614
|
});
|
|
621
615
|
else {
|
|
616
|
+
const get2 = () => state[key2];
|
|
622
617
|
const set2 = (value2) => subject.set(key2, value2);
|
|
623
|
-
define2(set2, "current", { get:
|
|
618
|
+
define2(set2, "current", { get: get2, set: set2 });
|
|
619
|
+
define2(set2, "get", { value: get2 });
|
|
624
620
|
define2(value, key2, { value: set2 });
|
|
625
621
|
}
|
|
626
622
|
});
|
|
@@ -628,6 +624,7 @@ function ref(arg, arg2) {
|
|
|
628
624
|
throw new Error("ref instruction does not support object which is not 'this'");
|
|
629
625
|
else {
|
|
630
626
|
let unSet;
|
|
627
|
+
const get2 = () => state[key];
|
|
631
628
|
const set2 = (value2) => {
|
|
632
629
|
if (!subject.set(key, value2) || !arg)
|
|
633
630
|
return;
|
|
@@ -639,9 +636,10 @@ function ref(arg, arg2) {
|
|
|
639
636
|
unSet = out;
|
|
640
637
|
}
|
|
641
638
|
};
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
set: set2
|
|
639
|
+
state[key] = null;
|
|
640
|
+
value = Object.defineProperties(set2, {
|
|
641
|
+
current: { get: get2, set: set2 },
|
|
642
|
+
get: { value: get2 }
|
|
645
643
|
});
|
|
646
644
|
}
|
|
647
645
|
define2(subject, key, { value });
|
package/dist/index.mjs
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
var DISPATCH = /* @__PURE__ */ new Set();
|
|
3
3
|
var OBSERVER = /* @__PURE__ */ new WeakMap();
|
|
4
4
|
var LISTENER = /* @__PURE__ */ new WeakMap();
|
|
5
|
-
|
|
6
|
-
return null;
|
|
7
|
-
}
|
|
5
|
+
var onReady = () => null;
|
|
8
6
|
function addListener(subject, callback, select) {
|
|
9
7
|
let subs = LISTENER.get(subject);
|
|
10
8
|
if (!subs)
|
|
11
|
-
LISTENER.set(subject, subs =
|
|
9
|
+
LISTENER.set(subject, subs = /* @__PURE__ */ new Map([[onReady, false]]));
|
|
12
10
|
const filter = 2 in arguments && /* @__PURE__ */ new Set([select]);
|
|
13
11
|
if (!filter && !subs.has(onReady))
|
|
14
12
|
callback(true, subject);
|
|
@@ -16,9 +14,9 @@ function addListener(subject, callback, select) {
|
|
|
16
14
|
return () => subs.delete(callback);
|
|
17
15
|
}
|
|
18
16
|
function watch(from, key, value) {
|
|
19
|
-
const listeners = LISTENER.get(from);
|
|
20
17
|
const observer = OBSERVER.get(from);
|
|
21
18
|
if (observer) {
|
|
19
|
+
const listeners = LISTENER.get(from);
|
|
22
20
|
listeners.set(
|
|
23
21
|
observer,
|
|
24
22
|
(listeners.get(observer) || /* @__PURE__ */ new Set()).add(key)
|
|
@@ -72,79 +70,97 @@ function queue(event2) {
|
|
|
72
70
|
}
|
|
73
71
|
function effect(target, callback) {
|
|
74
72
|
const listeners = LISTENER.get(target);
|
|
75
|
-
let
|
|
76
|
-
let
|
|
73
|
+
let unset;
|
|
74
|
+
let reset;
|
|
77
75
|
function invoke() {
|
|
78
|
-
let
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
let stale;
|
|
77
|
+
const subscriber = Object.create(target);
|
|
78
|
+
LISTENER.set(subscriber, listeners);
|
|
79
|
+
OBSERVER.set(subscriber, () => {
|
|
80
|
+
if (stale)
|
|
82
81
|
return null;
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
stale = true;
|
|
83
|
+
if (reset && unset) {
|
|
84
|
+
unset(true);
|
|
85
|
+
unset = void 0;
|
|
87
86
|
}
|
|
88
|
-
return
|
|
89
|
-
};
|
|
90
|
-
LISTENER.set(local, listeners);
|
|
91
|
-
OBSERVER.set(local, update2);
|
|
87
|
+
return reset;
|
|
88
|
+
});
|
|
92
89
|
try {
|
|
93
|
-
const out = callback.call(
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
const out = callback.call(subscriber, subscriber);
|
|
91
|
+
unset = typeof out == "function" ? out : void 0;
|
|
92
|
+
reset = out === null ? out : invoke;
|
|
96
93
|
} catch (err) {
|
|
97
94
|
if (err instanceof Promise) {
|
|
98
|
-
|
|
99
|
-
err.then(invoke)
|
|
100
|
-
} else
|
|
101
|
-
console.error(err);
|
|
102
|
-
else
|
|
95
|
+
reset = void 0;
|
|
96
|
+
err.then(invoke);
|
|
97
|
+
} else
|
|
103
98
|
throw err;
|
|
104
99
|
}
|
|
105
100
|
}
|
|
106
101
|
addListener(target, (key) => {
|
|
107
102
|
if (key === true)
|
|
108
103
|
invoke();
|
|
109
|
-
else if (!
|
|
110
|
-
return
|
|
111
|
-
if (key === null &&
|
|
112
|
-
|
|
104
|
+
else if (!reset)
|
|
105
|
+
return reset;
|
|
106
|
+
if (key === null && unset)
|
|
107
|
+
unset(null);
|
|
113
108
|
});
|
|
114
109
|
return () => {
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
if (unset)
|
|
111
|
+
unset(false);
|
|
112
|
+
reset = null;
|
|
118
113
|
};
|
|
119
114
|
}
|
|
120
115
|
|
|
121
116
|
// src/model.ts
|
|
122
|
-
var
|
|
123
|
-
var
|
|
117
|
+
var FLATTEN;
|
|
118
|
+
var REGISTER = /* @__PURE__ */ new WeakMap();
|
|
124
119
|
var STATE = /* @__PURE__ */ new WeakMap();
|
|
125
120
|
var NOTIFY = /* @__PURE__ */ new WeakMap();
|
|
126
121
|
var PENDING2 = /* @__PURE__ */ new WeakMap();
|
|
122
|
+
var PARENT = /* @__PURE__ */ new WeakMap();
|
|
127
123
|
var define = Object.defineProperty;
|
|
128
124
|
var Model = class _Model {
|
|
129
125
|
constructor(arg) {
|
|
126
|
+
const state = {};
|
|
130
127
|
let Type = this.constructor;
|
|
131
128
|
define(this, "is", { value: this });
|
|
132
|
-
STATE.set(this,
|
|
133
|
-
|
|
129
|
+
STATE.set(this, state);
|
|
130
|
+
REGISTER.set(this, typeof arg == "string" ? arg : `${Type}-${uid()}`);
|
|
134
131
|
while (true) {
|
|
135
132
|
new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
|
|
136
133
|
if (Type === _Model)
|
|
137
134
|
break;
|
|
138
135
|
Type = Object.getPrototypeOf(Type);
|
|
139
136
|
}
|
|
140
|
-
addListener(this,
|
|
141
|
-
|
|
137
|
+
addListener(this, () => {
|
|
138
|
+
let onDone;
|
|
139
|
+
for (const key in this) {
|
|
140
|
+
const desc = Object.getOwnPropertyDescriptor(this, key);
|
|
141
|
+
if ("value" in desc) {
|
|
142
|
+
state[key] = desc.value;
|
|
143
|
+
define(this, key, {
|
|
144
|
+
configurable: false,
|
|
145
|
+
set: (x) => update(this, key, x),
|
|
146
|
+
get() {
|
|
147
|
+
return watch(this, key, state[key]);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (typeof arg == "function")
|
|
153
|
+
onDone = arg();
|
|
142
154
|
addListener(this, () => {
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
for (const [_, value] of this)
|
|
156
|
+
if (value instanceof _Model && PARENT.get(value) === this)
|
|
157
|
+
value.set(null);
|
|
158
|
+
if (typeof onDone == "function")
|
|
159
|
+
onDone();
|
|
160
|
+
Object.freeze(state);
|
|
161
|
+
}, null);
|
|
162
|
+
return null;
|
|
163
|
+
});
|
|
148
164
|
}
|
|
149
165
|
get(arg1, arg2) {
|
|
150
166
|
const self = this.is;
|
|
@@ -152,15 +168,27 @@ var Model = class _Model {
|
|
|
152
168
|
let pending = /* @__PURE__ */ new Set();
|
|
153
169
|
return effect(self, (state) => {
|
|
154
170
|
const cb = arg1.call(state, state, pending);
|
|
155
|
-
return cb === null ?
|
|
171
|
+
return cb === null ? cb : (update2) => {
|
|
156
172
|
pending = PENDING2.get(self);
|
|
157
173
|
if (typeof cb == "function")
|
|
158
174
|
return cb(update2);
|
|
159
175
|
};
|
|
160
176
|
});
|
|
161
177
|
}
|
|
162
|
-
if (arg1 === void 0)
|
|
163
|
-
|
|
178
|
+
if (arg1 === void 0) {
|
|
179
|
+
const values = {};
|
|
180
|
+
const current = !FLATTEN && (FLATTEN = /* @__PURE__ */ new Map([[self, values]]));
|
|
181
|
+
for (let [key, value] of self) {
|
|
182
|
+
if (FLATTEN.has(value))
|
|
183
|
+
value = FLATTEN.get(value);
|
|
184
|
+
else if (value && typeof value.get === "function")
|
|
185
|
+
FLATTEN.set(value, value = value.get());
|
|
186
|
+
values[key] = value;
|
|
187
|
+
}
|
|
188
|
+
if (current)
|
|
189
|
+
FLATTEN = void 0;
|
|
190
|
+
return Object.freeze(values);
|
|
191
|
+
}
|
|
164
192
|
if (typeof arg2 == "function") {
|
|
165
193
|
if (arg1 === null)
|
|
166
194
|
return addListener(self, arg2.bind(this, this), null);
|
|
@@ -224,17 +252,20 @@ var Model = class _Model {
|
|
|
224
252
|
static is(maybe) {
|
|
225
253
|
return typeof maybe == "function" && maybe.prototype instanceof this;
|
|
226
254
|
}
|
|
227
|
-
|
|
255
|
+
/**
|
|
256
|
+
* Register a callback to run when any instance of this Model is updated.
|
|
257
|
+
*/
|
|
258
|
+
static on(listener) {
|
|
228
259
|
let notify = NOTIFY.get(this);
|
|
229
260
|
if (!notify)
|
|
230
261
|
NOTIFY.set(this, notify = /* @__PURE__ */ new Set());
|
|
231
|
-
notify.add(
|
|
232
|
-
return () => notify.delete(
|
|
262
|
+
notify.add(listener);
|
|
263
|
+
return () => notify.delete(listener);
|
|
233
264
|
}
|
|
234
265
|
};
|
|
235
266
|
define(Model.prototype, "toString", {
|
|
236
267
|
value() {
|
|
237
|
-
return
|
|
268
|
+
return REGISTER.get(this.is);
|
|
238
269
|
}
|
|
239
270
|
});
|
|
240
271
|
define(Model, "toString", {
|
|
@@ -242,29 +273,6 @@ define(Model, "toString", {
|
|
|
242
273
|
return this.name;
|
|
243
274
|
}
|
|
244
275
|
});
|
|
245
|
-
function bootstrap() {
|
|
246
|
-
const state = STATE.get(this);
|
|
247
|
-
for (const key in this) {
|
|
248
|
-
const desc = Object.getOwnPropertyDescriptor(this, key);
|
|
249
|
-
if ("value" in desc) {
|
|
250
|
-
state[key] = desc.value;
|
|
251
|
-
define(this, key, {
|
|
252
|
-
configurable: false,
|
|
253
|
-
set: (x) => update(this, key, x),
|
|
254
|
-
get() {
|
|
255
|
-
return watch(this, key, state[key]);
|
|
256
|
-
}
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
addListener(this, () => {
|
|
261
|
-
for (const [_, value] of this)
|
|
262
|
-
if (value instanceof Model && PARENT.get(value) === this)
|
|
263
|
-
value.set(null);
|
|
264
|
-
Object.freeze(state);
|
|
265
|
-
}, null);
|
|
266
|
-
return null;
|
|
267
|
-
}
|
|
268
276
|
function fetch(subject, property, required) {
|
|
269
277
|
const state = STATE.get(subject);
|
|
270
278
|
if (property in state || required === false) {
|
|
@@ -298,36 +306,22 @@ function update(subject, key, value, silent) {
|
|
|
298
306
|
if (value === previous)
|
|
299
307
|
return true;
|
|
300
308
|
state[key] = value;
|
|
301
|
-
if (silent
|
|
302
|
-
|
|
303
|
-
push(subject, key);
|
|
309
|
+
if (silent !== true)
|
|
310
|
+
push(subject, key);
|
|
304
311
|
}
|
|
305
|
-
function push(subject, key) {
|
|
312
|
+
function push(subject, key, silent) {
|
|
306
313
|
let pending = PENDING2.get(subject);
|
|
307
314
|
if (!pending) {
|
|
308
315
|
PENDING2.set(subject, pending = /* @__PURE__ */ new Set());
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
316
|
+
if (!silent)
|
|
317
|
+
queue(() => {
|
|
318
|
+
event(subject, false);
|
|
319
|
+
PENDING2.delete(subject);
|
|
320
|
+
});
|
|
313
321
|
}
|
|
314
322
|
pending.add(key);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
function extract(from) {
|
|
318
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
319
|
-
function get2(value) {
|
|
320
|
-
if (value instanceof Model) {
|
|
321
|
-
if (cache.has(value))
|
|
322
|
-
return cache.get(value);
|
|
323
|
-
const model = value;
|
|
324
|
-
cache.set(value, value = {});
|
|
325
|
-
for (const [key, val] of model)
|
|
326
|
-
value[key] = get2(val);
|
|
327
|
-
}
|
|
328
|
-
return value;
|
|
329
|
-
}
|
|
330
|
-
return get2(from);
|
|
323
|
+
if (!silent)
|
|
324
|
+
event(subject, key);
|
|
331
325
|
}
|
|
332
326
|
function uid() {
|
|
333
327
|
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
@@ -537,10 +531,10 @@ function compute(subject, key, source, setter) {
|
|
|
537
531
|
compute2(true);
|
|
538
532
|
else if (STALE.delete(compute2))
|
|
539
533
|
compute2();
|
|
540
|
-
return (
|
|
541
|
-
if (
|
|
534
|
+
return (didUpdate) => {
|
|
535
|
+
if (didUpdate) {
|
|
542
536
|
STALE.add(compute2);
|
|
543
|
-
subject
|
|
537
|
+
push(subject, key, true);
|
|
544
538
|
}
|
|
545
539
|
};
|
|
546
540
|
});
|
|
@@ -555,7 +549,7 @@ function compute(subject, key, source, setter) {
|
|
|
555
549
|
throw err;
|
|
556
550
|
console.error(err);
|
|
557
551
|
}
|
|
558
|
-
subject
|
|
552
|
+
update(subject, key, next, !isAsync);
|
|
559
553
|
}
|
|
560
554
|
return () => {
|
|
561
555
|
if (!proxy) {
|
|
@@ -586,8 +580,10 @@ function ref(arg, arg2) {
|
|
|
586
580
|
}
|
|
587
581
|
});
|
|
588
582
|
else {
|
|
583
|
+
const get2 = () => state[key2];
|
|
589
584
|
const set2 = (value2) => subject.set(key2, value2);
|
|
590
|
-
define2(set2, "current", { get:
|
|
585
|
+
define2(set2, "current", { get: get2, set: set2 });
|
|
586
|
+
define2(set2, "get", { value: get2 });
|
|
591
587
|
define2(value, key2, { value: set2 });
|
|
592
588
|
}
|
|
593
589
|
});
|
|
@@ -595,6 +591,7 @@ function ref(arg, arg2) {
|
|
|
595
591
|
throw new Error("ref instruction does not support object which is not 'this'");
|
|
596
592
|
else {
|
|
597
593
|
let unSet;
|
|
594
|
+
const get2 = () => state[key];
|
|
598
595
|
const set2 = (value2) => {
|
|
599
596
|
if (!subject.set(key, value2) || !arg)
|
|
600
597
|
return;
|
|
@@ -606,9 +603,10 @@ function ref(arg, arg2) {
|
|
|
606
603
|
unSet = out;
|
|
607
604
|
}
|
|
608
605
|
};
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
set: set2
|
|
606
|
+
state[key] = null;
|
|
607
|
+
value = Object.defineProperties(set2, {
|
|
608
|
+
current: { get: get2, set: set2 },
|
|
609
|
+
get: { value: get2 }
|
|
612
610
|
});
|
|
613
611
|
}
|
|
614
612
|
define2(subject, key, { value });
|
package/package.json
CHANGED