@expressive/mvc 0.36.3 → 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 -21
- package/dist/index.js +93 -97
- package/dist/index.mjs +93 -97
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,18 +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
|
|
8
|
+
/** Any valid key for controller, including but not limited to Field<T>. */
|
|
9
9
|
type Key<T> = Field<T> | (string & {}) | number | symbol;
|
|
10
|
-
/** Value for a given property managed by a controller. */
|
|
11
|
-
type Value<T extends Model, K extends Key<T>> = K extends keyof T ? T[K] extends Ref<infer V> ? V : T[K] : unknown;
|
|
12
|
-
type Event<T extends Model> = (this: T, key: unknown, source: T) => (() => void) | void | null;
|
|
13
|
-
type OnUpdate<T extends Model, K extends Key<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
|
|
14
10
|
/** Export/Import compatible value for a given property in a Model. */
|
|
15
|
-
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;
|
|
16
18
|
/**
|
|
17
19
|
* Values from current state of given controller.
|
|
18
20
|
* Differs from `Values` as values here will drill
|
|
19
|
-
* into "real" values held by exotics like ref.
|
|
21
|
+
* into "real" values held by exotics like ref.Object.
|
|
20
22
|
*/
|
|
21
23
|
type State<T> = {
|
|
22
24
|
[P in Field<T>]: Export<T[P]>;
|
|
@@ -30,8 +32,23 @@ declare namespace Model {
|
|
|
30
32
|
(next: T): void;
|
|
31
33
|
current: T | null;
|
|
32
34
|
};
|
|
33
|
-
/**
|
|
34
|
-
|
|
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);
|
|
35
52
|
namespace Instruction {
|
|
36
53
|
type Getter<T> = (source: Model) => T;
|
|
37
54
|
type Setter<T> = (value: T, previous: T) => boolean | void | (() => T);
|
|
@@ -59,13 +76,42 @@ declare class Model {
|
|
|
59
76
|
constructor(arg?: string | (() => void | (() => void)));
|
|
60
77
|
/** Pull current values from state. Flattens all models and exotic values recursively. */
|
|
61
78
|
get(): Model.State<this>;
|
|
62
|
-
/**
|
|
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
|
+
*/
|
|
63
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
|
+
*/
|
|
64
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
|
+
*/
|
|
65
102
|
get<T extends Model.Key<this>>(key: T, callback: Model.OnUpdate<this, T>): () => void;
|
|
66
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Check if model is expired.
|
|
105
|
+
*
|
|
106
|
+
* @returns `true` if model is expired, `false` otherwise.
|
|
107
|
+
*/
|
|
67
108
|
get(status: null): boolean;
|
|
68
|
-
/**
|
|
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
|
+
*/
|
|
69
115
|
get(status: null, callback: () => void): () => void;
|
|
70
116
|
/**
|
|
71
117
|
* Get update in progress.
|
|
@@ -77,13 +123,15 @@ declare class Model {
|
|
|
77
123
|
* Call a function when update occurs.
|
|
78
124
|
*
|
|
79
125
|
* Given function is called for every assignment (which changes value) or explicit `set`.
|
|
80
|
-
*
|
|
81
|
-
*
|
|
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.
|
|
82
129
|
*
|
|
83
130
|
* @param callback - Function to call when update occurs.
|
|
84
131
|
* @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.
|
|
132
|
+
*
|
|
85
133
|
*/
|
|
86
|
-
set(callback: Model.
|
|
134
|
+
set(callback: Model.OnEvent<this>): () => boolean;
|
|
87
135
|
/**
|
|
88
136
|
* Declare an end to updates. This event is final and will freeze state.
|
|
89
137
|
* This event can be watched for as well, to run cleanup logic and internally will remove all listeners.
|
|
@@ -95,10 +143,13 @@ declare class Model {
|
|
|
95
143
|
* Useful where a property value internally has changed, but the object is the same.
|
|
96
144
|
* For example: An array has pushed a new value, or a nested property is updated.
|
|
97
145
|
*
|
|
98
|
-
* You can also use this to dispatch arbitrary events
|
|
99
|
-
* recommended as non-property events, however you can use any string.
|
|
100
|
-
* be sure to avoid collisions with property names
|
|
101
|
-
* 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.
|
|
102
153
|
*/
|
|
103
154
|
set(key: Model.Key<this>): PromiseLike<Model.Key<this>[]>;
|
|
104
155
|
/**
|
|
@@ -126,7 +177,10 @@ declare class Model {
|
|
|
126
177
|
* methods and properties of this class.
|
|
127
178
|
*/
|
|
128
179
|
static is<T extends Model.Type>(this: T, maybe: any): maybe is T;
|
|
129
|
-
|
|
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;
|
|
130
184
|
}
|
|
131
185
|
|
|
132
186
|
declare function add<T = any>(instruction: Model.Instruction): T;
|
|
@@ -180,14 +234,22 @@ declare namespace ref {
|
|
|
180
234
|
type Callback<T> = (argument: T) => ((next: T | null) => void) | Promise<void> | void | boolean;
|
|
181
235
|
interface Object<T = any> {
|
|
182
236
|
(next: T | null): void;
|
|
237
|
+
/** Current value held by this reference. */
|
|
183
238
|
current: T | null;
|
|
239
|
+
/** Retrieve current value. */
|
|
240
|
+
get(): T | null;
|
|
184
241
|
}
|
|
185
242
|
/** Object with references to all managed values of `T`. */
|
|
243
|
+
/** Object with references to all managed values of `T`. */
|
|
186
244
|
type Proxy<T extends Model> = {
|
|
187
|
-
[P in Model.Field<T>]-?:
|
|
245
|
+
[P in Model.Field<T>]-?: Object<T[P]>;
|
|
246
|
+
} & {
|
|
247
|
+
get(): T;
|
|
188
248
|
};
|
|
189
249
|
type CustomProxy<T extends Model, R> = {
|
|
190
250
|
[P in Model.Field<T>]-?: R;
|
|
251
|
+
} & {
|
|
252
|
+
get(): T;
|
|
191
253
|
};
|
|
192
254
|
}
|
|
193
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,9 +339,8 @@ 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
345
|
function push(subject, key, silent) {
|
|
339
346
|
let pending = PENDING2.get(subject);
|
|
@@ -349,21 +356,6 @@ function push(subject, key, silent) {
|
|
|
349
356
|
if (!silent)
|
|
350
357
|
event(subject, key);
|
|
351
358
|
}
|
|
352
|
-
function extract(from) {
|
|
353
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
354
|
-
function get2(value) {
|
|
355
|
-
if (value instanceof Model) {
|
|
356
|
-
if (cache.has(value))
|
|
357
|
-
return cache.get(value);
|
|
358
|
-
const model = value;
|
|
359
|
-
cache.set(value, value = {});
|
|
360
|
-
for (const [key, val] of model)
|
|
361
|
-
value[key] = get2(val);
|
|
362
|
-
}
|
|
363
|
-
return value;
|
|
364
|
-
}
|
|
365
|
-
return get2(from);
|
|
366
|
-
}
|
|
367
359
|
function uid() {
|
|
368
360
|
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
369
361
|
}
|
|
@@ -621,8 +613,10 @@ function ref(arg, arg2) {
|
|
|
621
613
|
}
|
|
622
614
|
});
|
|
623
615
|
else {
|
|
616
|
+
const get2 = () => state[key2];
|
|
624
617
|
const set2 = (value2) => subject.set(key2, value2);
|
|
625
|
-
define2(set2, "current", { get:
|
|
618
|
+
define2(set2, "current", { get: get2, set: set2 });
|
|
619
|
+
define2(set2, "get", { value: get2 });
|
|
626
620
|
define2(value, key2, { value: set2 });
|
|
627
621
|
}
|
|
628
622
|
});
|
|
@@ -630,6 +624,7 @@ function ref(arg, arg2) {
|
|
|
630
624
|
throw new Error("ref instruction does not support object which is not 'this'");
|
|
631
625
|
else {
|
|
632
626
|
let unSet;
|
|
627
|
+
const get2 = () => state[key];
|
|
633
628
|
const set2 = (value2) => {
|
|
634
629
|
if (!subject.set(key, value2) || !arg)
|
|
635
630
|
return;
|
|
@@ -641,9 +636,10 @@ function ref(arg, arg2) {
|
|
|
641
636
|
unSet = out;
|
|
642
637
|
}
|
|
643
638
|
};
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
set: set2
|
|
639
|
+
state[key] = null;
|
|
640
|
+
value = Object.defineProperties(set2, {
|
|
641
|
+
current: { get: get2, set: set2 },
|
|
642
|
+
get: { value: get2 }
|
|
647
643
|
});
|
|
648
644
|
}
|
|
649
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,9 +306,8 @@ 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
312
|
function push(subject, key, silent) {
|
|
306
313
|
let pending = PENDING2.get(subject);
|
|
@@ -316,21 +323,6 @@ function push(subject, key, silent) {
|
|
|
316
323
|
if (!silent)
|
|
317
324
|
event(subject, key);
|
|
318
325
|
}
|
|
319
|
-
function extract(from) {
|
|
320
|
-
const cache = /* @__PURE__ */ new WeakMap();
|
|
321
|
-
function get2(value) {
|
|
322
|
-
if (value instanceof Model) {
|
|
323
|
-
if (cache.has(value))
|
|
324
|
-
return cache.get(value);
|
|
325
|
-
const model = value;
|
|
326
|
-
cache.set(value, value = {});
|
|
327
|
-
for (const [key, val] of model)
|
|
328
|
-
value[key] = get2(val);
|
|
329
|
-
}
|
|
330
|
-
return value;
|
|
331
|
-
}
|
|
332
|
-
return get2(from);
|
|
333
|
-
}
|
|
334
326
|
function uid() {
|
|
335
327
|
return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
|
|
336
328
|
}
|
|
@@ -588,8 +580,10 @@ function ref(arg, arg2) {
|
|
|
588
580
|
}
|
|
589
581
|
});
|
|
590
582
|
else {
|
|
583
|
+
const get2 = () => state[key2];
|
|
591
584
|
const set2 = (value2) => subject.set(key2, value2);
|
|
592
|
-
define2(set2, "current", { get:
|
|
585
|
+
define2(set2, "current", { get: get2, set: set2 });
|
|
586
|
+
define2(set2, "get", { value: get2 });
|
|
593
587
|
define2(value, key2, { value: set2 });
|
|
594
588
|
}
|
|
595
589
|
});
|
|
@@ -597,6 +591,7 @@ function ref(arg, arg2) {
|
|
|
597
591
|
throw new Error("ref instruction does not support object which is not 'this'");
|
|
598
592
|
else {
|
|
599
593
|
let unSet;
|
|
594
|
+
const get2 = () => state[key];
|
|
600
595
|
const set2 = (value2) => {
|
|
601
596
|
if (!subject.set(key, value2) || !arg)
|
|
602
597
|
return;
|
|
@@ -608,9 +603,10 @@ function ref(arg, arg2) {
|
|
|
608
603
|
unSet = out;
|
|
609
604
|
}
|
|
610
605
|
};
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
set: set2
|
|
606
|
+
state[key] = null;
|
|
607
|
+
value = Object.defineProperties(set2, {
|
|
608
|
+
current: { get: get2, set: set2 },
|
|
609
|
+
get: { value: get2 }
|
|
614
610
|
});
|
|
615
611
|
}
|
|
616
612
|
define2(subject, key, { value });
|
package/package.json
CHANGED