@expressive/mvc 0.35.1 → 0.35.5-alpha.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 +43 -46
- package/dist/index.js +117 -88
- package/dist/index.mjs +117 -88
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
type Predicate = (key: string) => boolean | void;
|
|
2
1
|
declare namespace Model {
|
|
3
2
|
/** Any type of Model, using own class constructor as its identifier. */
|
|
4
3
|
type Type<T extends Model = Model> = abstract new (...args: any[]) => T;
|
|
5
4
|
/** A type of Model which may be created without constructor arguments. */
|
|
6
5
|
type New<T extends Model = Model> = (new () => T) & typeof Model;
|
|
7
6
|
/** Subset of `keyof T` which are not methods or defined by base Model U. **/
|
|
8
|
-
type
|
|
9
|
-
type
|
|
7
|
+
type Field<T> = Exclude<keyof T, keyof Model>;
|
|
8
|
+
type Key<T> = Field<T> | (string & {}) | number | symbol;
|
|
10
9
|
/** Actual value stored in state. */
|
|
11
|
-
type
|
|
12
|
-
type
|
|
10
|
+
type Actual<R> = R extends Ref<infer T> ? T : R extends Model ? State<R> : R;
|
|
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 OnUpdate<T extends Model, K extends Key<T>> = (this: T, value: Value<T, K>, key: K, thisArg: K) => void;
|
|
13
13
|
/**
|
|
14
14
|
* Values from current state of given controller.
|
|
15
15
|
* Differs from `Values` as values here will drill
|
|
16
16
|
* into "real" values held by exotics like ref.
|
|
17
17
|
*/
|
|
18
|
-
type
|
|
19
|
-
[P in
|
|
18
|
+
type State<T> = {
|
|
19
|
+
[P in Field<T>]: Actual<T[P]>;
|
|
20
20
|
};
|
|
21
21
|
/** Object comperable to data found in T. */
|
|
22
22
|
type Values<T> = {
|
|
23
|
-
[P in
|
|
23
|
+
[P in Field<T>]?: Actual<T[P]>;
|
|
24
24
|
};
|
|
25
25
|
/** Exotic value, where actual value is contained within. */
|
|
26
26
|
type Ref<T = any> = {
|
|
@@ -29,7 +29,7 @@ declare namespace Model {
|
|
|
29
29
|
};
|
|
30
30
|
/** A callback function which is subscribed to parent and updates when values change. */
|
|
31
31
|
type Effect<T> = (this: T, argument: T) => (() => void) | Promise<void> | null | void;
|
|
32
|
-
type Event = (this:
|
|
32
|
+
type Event<T extends Model> = (this: T, key: unknown, source: T) => (() => void) | void | null;
|
|
33
33
|
namespace Instruction {
|
|
34
34
|
type Getter<T> = (source: Model) => T;
|
|
35
35
|
type Setter<T> = (value: T, previous: T) => boolean | void | (() => T);
|
|
@@ -44,7 +44,7 @@ declare namespace Model {
|
|
|
44
44
|
* Property initializer, will run upon instance creation.
|
|
45
45
|
* Optional returned callback will run when once upon first access.
|
|
46
46
|
*/
|
|
47
|
-
type Instruction<T = any, M extends Model = any> = (this: M, key: Model.
|
|
47
|
+
type Instruction<T = any, M extends Model = any> = (this: M, key: Model.Field<M> & string, thisArg: M, state: Model.State<M>) => Instruction.Descriptor<T> | Instruction.Getter<T> | void;
|
|
48
48
|
}
|
|
49
49
|
interface Model {
|
|
50
50
|
/**
|
|
@@ -55,48 +55,44 @@ interface Model {
|
|
|
55
55
|
}
|
|
56
56
|
declare class Model {
|
|
57
57
|
constructor(id?: string | number);
|
|
58
|
-
/** Pull current values from state. Flattens all models and exotic values
|
|
59
|
-
get(): Model.
|
|
58
|
+
/** Pull current values from state. Flattens all models and exotic values recursively. */
|
|
59
|
+
get(): Model.State<this>;
|
|
60
60
|
/** Run a function which will run automatically when accessed values change. */
|
|
61
61
|
get(effect: Model.Effect<this>): () => void;
|
|
62
|
-
|
|
63
|
-
get
|
|
62
|
+
/** Check if model is expired. */
|
|
63
|
+
get(event: null): boolean;
|
|
64
|
+
/** Callback when model is to be destroyed. */
|
|
65
|
+
get(event: null, callback: () => void): () => void;
|
|
66
|
+
get<T extends Model.Key<this>>(key: T, required?: boolean): Model.Value<this, T>;
|
|
67
|
+
get<T extends Model.Key<this>>(key: T, callback: Model.OnUpdate<this, T>): () => void;
|
|
64
68
|
/**
|
|
65
|
-
*
|
|
66
|
-
* Returns a promise which resolves updated keys. If no update, will resolve to `false`.
|
|
67
|
-
**/
|
|
68
|
-
set(): Promise<Model.Values<this> | false>;
|
|
69
|
-
/**
|
|
70
|
-
* Expect an update to state within a set time.
|
|
69
|
+
* Get update in progress.
|
|
71
70
|
*
|
|
72
|
-
* @
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* @returns a promise to resolve when next accepted update has occured.
|
|
76
|
-
*/
|
|
77
|
-
set(timeout: number, predicate?: Predicate): Promise<Model.Values<this>>;
|
|
71
|
+
* @returns Promise which resolves object with updated values. Is `undefined` if there is no update.
|
|
72
|
+
**/
|
|
73
|
+
set(): Promise<Model.Values<this>> | undefined;
|
|
78
74
|
/**
|
|
79
|
-
* Call a function
|
|
75
|
+
* Call a function when update occurs.
|
|
80
76
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* Returning the same function for a key will ensure it is only called once.
|
|
77
|
+
* Given function is called for every assignment (which changes value) or explicit `set`.
|
|
78
|
+
* To run logic on final value only, callback itself may return a function.
|
|
79
|
+
* Returning the same function for one or more events will ensure a side-effect is called only when settled.
|
|
85
80
|
*
|
|
86
|
-
* @
|
|
81
|
+
* @param callback - Function to call when update occurs.
|
|
82
|
+
* @returns Function to remove listener. Will return `true` if removed, `false` if inactive already.
|
|
87
83
|
*/
|
|
88
|
-
set(callback: Model.Event): () => boolean;
|
|
84
|
+
set(callback: Model.Event<this>): () => boolean;
|
|
89
85
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* This is useful where a property value internally has changed, but the object remains the same.
|
|
93
|
-
* For example: An array which has pushed a new value, or a change to nested property.
|
|
86
|
+
* Declare an end to updates. This event is final and will freeze state.
|
|
94
87
|
*/
|
|
95
|
-
set(
|
|
88
|
+
set(event: null): void;
|
|
96
89
|
/**
|
|
97
|
-
*
|
|
90
|
+
* Push an update. This will not change the value of associated property.
|
|
91
|
+
*
|
|
92
|
+
* Useful where a property value internally has changed, but the object is the same.
|
|
93
|
+
* For example: An array has pushed a new value, or a nested property is updated.
|
|
98
94
|
*/
|
|
99
|
-
set(key:
|
|
95
|
+
set(key: Model.Key<this>): void;
|
|
100
96
|
/**
|
|
101
97
|
* Update a property with value.
|
|
102
98
|
*
|
|
@@ -104,13 +100,13 @@ declare class Model {
|
|
|
104
100
|
* @param value - value to update property with (if the same as current, no update will occur)
|
|
105
101
|
* @param silent - if true, will not notify listeners of an update
|
|
106
102
|
*/
|
|
107
|
-
set<K extends string>(key: K, value?: Model.
|
|
103
|
+
set<K extends string>(key: K, value?: Model.Value<this, K>, silent?: boolean): void;
|
|
108
104
|
/** Iterate over managed properties in this instance of Model. */
|
|
109
105
|
[Symbol.iterator](): Iterator<[string, unknown]>;
|
|
110
106
|
/**
|
|
111
107
|
* Creates a new instance of this controller.
|
|
112
108
|
*
|
|
113
|
-
*
|
|
109
|
+
* **Important** - Use instead of `new this(...)` as this method also activates state.
|
|
114
110
|
*
|
|
115
111
|
* @param args - arguments sent to constructor
|
|
116
112
|
*/
|
|
@@ -122,9 +118,10 @@ declare class Model {
|
|
|
122
118
|
* methods and properties of this class.
|
|
123
119
|
*/
|
|
124
120
|
static is<T extends Model.Type>(this: T, maybe: any): maybe is T;
|
|
121
|
+
static on<T extends Model>(this: Model.Type<T>, event: Model.Event<T>): () => boolean;
|
|
125
122
|
}
|
|
126
123
|
|
|
127
|
-
declare function add<T = any
|
|
124
|
+
declare function add<T = any>(instruction: Model.Instruction): T;
|
|
128
125
|
|
|
129
126
|
declare namespace get {
|
|
130
127
|
type Function<T, S = any> = (this: S, on: S) => T;
|
|
@@ -179,10 +176,10 @@ declare namespace ref {
|
|
|
179
176
|
}
|
|
180
177
|
/** Object with references to all managed values of `T`. */
|
|
181
178
|
type Proxy<T extends Model> = {
|
|
182
|
-
[P in Model.
|
|
179
|
+
[P in Model.Field<T>]-?: Model.Ref<T[P]>;
|
|
183
180
|
};
|
|
184
181
|
type CustomProxy<T extends Model, R> = {
|
|
185
|
-
[P in Model.
|
|
182
|
+
[P in Model.Field<T>]-?: R;
|
|
186
183
|
};
|
|
187
184
|
}
|
|
188
185
|
/**
|
|
@@ -202,7 +199,7 @@ declare function ref<T extends Model>(target: T): ref.Proxy<T>;
|
|
|
202
199
|
* @param target - Source model from which to reference values.
|
|
203
200
|
* @param mapper - Function producing the placeholder value for any given property.
|
|
204
201
|
*/
|
|
205
|
-
declare function ref<O extends Model, R>(target: O, mapper: (key: Model.
|
|
202
|
+
declare function ref<O extends Model, R>(target: O, mapper: (key: Model.Field<O>) => R): ref.CustomProxy<O, R>;
|
|
206
203
|
/**
|
|
207
204
|
* Creates a ref-compatible property.
|
|
208
205
|
* Will persist value, and updates to this are made part of controller event-stream.
|
package/dist/index.js
CHANGED
|
@@ -38,13 +38,14 @@ var LISTENER = /* @__PURE__ */ new WeakMap();
|
|
|
38
38
|
function onReady() {
|
|
39
39
|
return null;
|
|
40
40
|
}
|
|
41
|
-
function addListener(to, fn) {
|
|
41
|
+
function addListener(to, fn, select) {
|
|
42
42
|
let subs = LISTENER.get(to);
|
|
43
43
|
if (!subs)
|
|
44
|
-
LISTENER.set(to, subs = (/* @__PURE__ */ new Map()).set(onReady,
|
|
45
|
-
|
|
44
|
+
LISTENER.set(to, subs = (/* @__PURE__ */ new Map()).set(onReady, false));
|
|
45
|
+
const filter = 2 in arguments && /* @__PURE__ */ new Set([select]);
|
|
46
|
+
if (!filter && !subs.has(onReady))
|
|
46
47
|
fn(true, to);
|
|
47
|
-
subs.set(fn,
|
|
48
|
+
subs.set(fn, filter);
|
|
48
49
|
return () => subs.delete(fn);
|
|
49
50
|
}
|
|
50
51
|
function watch(from, key, value) {
|
|
@@ -138,28 +139,28 @@ function effect(target, callback) {
|
|
|
138
139
|
// src/model.ts
|
|
139
140
|
var ID = /* @__PURE__ */ new WeakMap();
|
|
140
141
|
var PARENT = /* @__PURE__ */ new WeakMap();
|
|
141
|
-
var INSTRUCT = /* @__PURE__ */ new Map();
|
|
142
|
-
var PENDING = /* @__PURE__ */ new WeakMap();
|
|
143
142
|
var STATE = /* @__PURE__ */ new WeakMap();
|
|
143
|
+
var NOTIFY = /* @__PURE__ */ new WeakMap();
|
|
144
|
+
var PENDING = /* @__PURE__ */ new WeakMap();
|
|
144
145
|
var define = Object.defineProperty;
|
|
145
146
|
var Model = class _Model {
|
|
146
147
|
constructor(id) {
|
|
148
|
+
let Type = this.constructor;
|
|
147
149
|
const state = {};
|
|
148
150
|
define(this, "is", { value: this });
|
|
149
|
-
ID.set(this, `${this.constructor}-${id ? String(id) : uid()}`);
|
|
150
151
|
STATE.set(this, state);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
ID.set(this, id ? String(id) : `${Type}-${uid()}`);
|
|
153
|
+
while (true) {
|
|
154
|
+
new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
|
|
155
|
+
if (Type === _Model)
|
|
156
|
+
break;
|
|
157
|
+
Type = Object.getPrototypeOf(Type);
|
|
154
158
|
}
|
|
155
159
|
addListener(this, () => {
|
|
156
160
|
for (const key in this) {
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
instruction(this, key, state);
|
|
161
|
-
else {
|
|
162
|
-
state[key] = property.value;
|
|
161
|
+
const desc = Object.getOwnPropertyDescriptor(this, key);
|
|
162
|
+
if ("value" in desc) {
|
|
163
|
+
state[key] = desc.value;
|
|
163
164
|
define(this, key, {
|
|
164
165
|
enumerable: true,
|
|
165
166
|
set: (x) => update(this, key, x),
|
|
@@ -169,56 +170,68 @@ var Model = class _Model {
|
|
|
169
170
|
});
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
|
-
addListener(this,
|
|
173
|
+
addListener(this, () => {
|
|
174
|
+
for (const [_, value] of this)
|
|
175
|
+
if (value instanceof _Model && PARENT.get(value) === this)
|
|
176
|
+
value.set(null);
|
|
177
|
+
Object.freeze(state);
|
|
178
|
+
}, null);
|
|
173
179
|
return null;
|
|
174
180
|
});
|
|
175
181
|
}
|
|
176
182
|
get(arg1, arg2) {
|
|
177
183
|
const self = this.is;
|
|
178
|
-
if (typeof arg1 == "
|
|
179
|
-
return fetch(self, arg1, arg2);
|
|
180
|
-
if (arg1)
|
|
184
|
+
if (typeof arg1 == "function")
|
|
181
185
|
return effect(self, arg1);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
186
|
+
if (arg1 === void 0) {
|
|
187
|
+
let get3 = function(value) {
|
|
188
|
+
if (value instanceof _Model) {
|
|
189
|
+
if (cache.has(value))
|
|
190
|
+
return cache.get(value);
|
|
191
|
+
const model = value;
|
|
192
|
+
cache.set(value, value = {});
|
|
193
|
+
for (const [key, val] of model)
|
|
194
|
+
value[key] = get3(val);
|
|
195
|
+
}
|
|
196
|
+
return value;
|
|
197
|
+
};
|
|
198
|
+
var get2 = get3;
|
|
199
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
200
|
+
return get3(self);
|
|
193
201
|
}
|
|
194
|
-
|
|
202
|
+
if (arg1 === null)
|
|
203
|
+
if (typeof arg2 == "function")
|
|
204
|
+
return addListener(self, arg2.bind(this, this), null);
|
|
205
|
+
else
|
|
206
|
+
return Object.isFrozen(STATE.get(self));
|
|
207
|
+
if (typeof arg2 == "function") {
|
|
208
|
+
const state = STATE.get(self);
|
|
209
|
+
if (arg1 in state)
|
|
210
|
+
arg2.call(this, state[arg1], arg1, this);
|
|
211
|
+
return addListener(self, () => {
|
|
212
|
+
arg2.call(this, arg1 in state ? state[arg1] : arg1, arg1, this);
|
|
213
|
+
}, arg1);
|
|
214
|
+
} else
|
|
215
|
+
return fetch(self, arg1, arg2);
|
|
195
216
|
}
|
|
196
217
|
set(arg1, arg2, arg3) {
|
|
197
218
|
const self = this.is;
|
|
198
219
|
if (typeof arg1 == "function")
|
|
199
220
|
return addListener(self, (key) => {
|
|
200
221
|
if (typeof key == "string")
|
|
201
|
-
return arg1.call(
|
|
222
|
+
return arg1.call(self, key, self);
|
|
202
223
|
});
|
|
203
|
-
if (
|
|
224
|
+
if (arg1 !== void 0)
|
|
204
225
|
return 1 in arguments ? update(self, arg1, arg2, arg3) : update(self, arg1);
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
226
|
+
if (!PENDING.has(self))
|
|
227
|
+
return;
|
|
228
|
+
return new Promise((resolve) => {
|
|
210
229
|
const remove = addListener(this, (key) => {
|
|
211
|
-
if (key
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
remove();
|
|
216
|
-
return resolve.bind(null, PENDING.get(self));
|
|
230
|
+
if (key !== true) {
|
|
231
|
+
remove();
|
|
232
|
+
return resolve.bind(null, PENDING.get(self));
|
|
233
|
+
}
|
|
217
234
|
});
|
|
218
|
-
const timeout = typeof arg1 == "number" && setTimeout(() => {
|
|
219
|
-
remove();
|
|
220
|
-
reject(arg1);
|
|
221
|
-
}, arg1);
|
|
222
235
|
});
|
|
223
236
|
}
|
|
224
237
|
/** Iterate over managed properties in this instance of Model. */
|
|
@@ -228,7 +241,7 @@ var Model = class _Model {
|
|
|
228
241
|
/**
|
|
229
242
|
* Creates a new instance of this controller.
|
|
230
243
|
*
|
|
231
|
-
*
|
|
244
|
+
* **Important** - Use instead of `new this(...)` as this method also activates state.
|
|
232
245
|
*
|
|
233
246
|
* @param args - arguments sent to constructor
|
|
234
247
|
*/
|
|
@@ -246,6 +259,13 @@ var Model = class _Model {
|
|
|
246
259
|
static is(maybe) {
|
|
247
260
|
return typeof maybe == "function" && maybe.prototype instanceof this;
|
|
248
261
|
}
|
|
262
|
+
static on(event2) {
|
|
263
|
+
let notify = NOTIFY.get(this);
|
|
264
|
+
if (!notify)
|
|
265
|
+
NOTIFY.set(this, notify = /* @__PURE__ */ new Set());
|
|
266
|
+
notify.add(event2);
|
|
267
|
+
return () => notify.delete(event2);
|
|
268
|
+
}
|
|
249
269
|
};
|
|
250
270
|
define(Model.prototype, "toString", {
|
|
251
271
|
value() {
|
|
@@ -285,15 +305,15 @@ function fetch(subject, property, required) {
|
|
|
285
305
|
function update(subject, key, value, silent) {
|
|
286
306
|
const state = STATE.get(subject);
|
|
287
307
|
let pending = PENDING.get(subject);
|
|
288
|
-
if (typeof key == "string") {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
308
|
+
if (typeof key == "string" && 2 in arguments) {
|
|
309
|
+
const previous = state[key];
|
|
310
|
+
if (value === previous)
|
|
311
|
+
return true;
|
|
312
|
+
state[key] = value;
|
|
313
|
+
if (silent === true)
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (key !== true && key) {
|
|
297
317
|
if (!pending) {
|
|
298
318
|
PENDING.set(subject, pending = {});
|
|
299
319
|
queue(() => {
|
|
@@ -301,7 +321,7 @@ function update(subject, key, value, silent) {
|
|
|
301
321
|
PENDING.delete(subject);
|
|
302
322
|
});
|
|
303
323
|
}
|
|
304
|
-
pending[key] = value;
|
|
324
|
+
pending[key] = key in state ? value : true;
|
|
305
325
|
}
|
|
306
326
|
event(subject, key);
|
|
307
327
|
}
|
|
@@ -310,10 +330,20 @@ function uid() {
|
|
|
310
330
|
}
|
|
311
331
|
|
|
312
332
|
// src/instruction/add.ts
|
|
333
|
+
var INSTRUCT = /* @__PURE__ */ new Map();
|
|
313
334
|
function add(instruction) {
|
|
314
|
-
const
|
|
315
|
-
INSTRUCT.set(
|
|
316
|
-
|
|
335
|
+
const token = Symbol("instruction");
|
|
336
|
+
INSTRUCT.set(token, instruction);
|
|
337
|
+
return token;
|
|
338
|
+
}
|
|
339
|
+
Model.on((_, subject) => {
|
|
340
|
+
const state = STATE.get(subject);
|
|
341
|
+
for (const key in subject) {
|
|
342
|
+
const { value } = Object.getOwnPropertyDescriptor(subject, key);
|
|
343
|
+
const instruction = INSTRUCT.get(value);
|
|
344
|
+
if (!instruction)
|
|
345
|
+
continue;
|
|
346
|
+
INSTRUCT.delete(value);
|
|
317
347
|
delete subject[key];
|
|
318
348
|
const output = instruction.call(subject, key, subject, state);
|
|
319
349
|
if (!output)
|
|
@@ -346,9 +376,9 @@ function add(instruction) {
|
|
|
346
376
|
);
|
|
347
377
|
}
|
|
348
378
|
});
|
|
349
|
-
}
|
|
350
|
-
return
|
|
351
|
-
}
|
|
379
|
+
}
|
|
380
|
+
return null;
|
|
381
|
+
});
|
|
352
382
|
|
|
353
383
|
// src/context.ts
|
|
354
384
|
var Context = class {
|
|
@@ -416,8 +446,8 @@ var Context = class {
|
|
|
416
446
|
if (value || I !== this[key] && !implicit)
|
|
417
447
|
Object.defineProperty(this, key, {
|
|
418
448
|
configurable: true,
|
|
419
|
-
|
|
420
|
-
|
|
449
|
+
writable,
|
|
450
|
+
value
|
|
421
451
|
});
|
|
422
452
|
T = Object.getPrototypeOf(T);
|
|
423
453
|
} while (T !== Model);
|
|
@@ -524,24 +554,23 @@ function ref(arg, arg2) {
|
|
|
524
554
|
return add((key, subject, state) => {
|
|
525
555
|
let value = {};
|
|
526
556
|
if (arg === subject)
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
get: () => state[key2],
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
}
|
|
557
|
+
subject.get(() => {
|
|
558
|
+
for (const key2 in state)
|
|
559
|
+
if (typeof arg2 == "function")
|
|
560
|
+
define2(value, key2, {
|
|
561
|
+
configurable: true,
|
|
562
|
+
get() {
|
|
563
|
+
const out = arg2(key2);
|
|
564
|
+
define2(value, key2, { value: out });
|
|
565
|
+
return out;
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
else {
|
|
569
|
+
const set2 = (value2) => subject.set(key2, value2);
|
|
570
|
+
define2(set2, "current", { get: () => state[key2], set: set2 });
|
|
571
|
+
define2(value, key2, { value: set2 });
|
|
572
|
+
}
|
|
573
|
+
});
|
|
545
574
|
else if (typeof arg == "object")
|
|
546
575
|
throw new Error("ref instruction does not support object which is not 'this'");
|
|
547
576
|
else {
|
|
@@ -641,6 +670,8 @@ function attempt(fn) {
|
|
|
641
670
|
|
|
642
671
|
// src/instruction/use.ts
|
|
643
672
|
function use(value, argument) {
|
|
673
|
+
if (typeof value === "function")
|
|
674
|
+
value = new value();
|
|
644
675
|
return add((property, subject) => {
|
|
645
676
|
function set2(next) {
|
|
646
677
|
if (value instanceof Model && !(next instanceof value.constructor))
|
|
@@ -666,8 +697,6 @@ function use(value, argument) {
|
|
|
666
697
|
if (typeof argument == "function")
|
|
667
698
|
argument(next);
|
|
668
699
|
}
|
|
669
|
-
if (typeof value === "function")
|
|
670
|
-
value = new value();
|
|
671
700
|
const output = { set: set2, value };
|
|
672
701
|
set2(value);
|
|
673
702
|
return output;
|
package/dist/index.mjs
CHANGED
|
@@ -5,13 +5,14 @@ var LISTENER = /* @__PURE__ */ new WeakMap();
|
|
|
5
5
|
function onReady() {
|
|
6
6
|
return null;
|
|
7
7
|
}
|
|
8
|
-
function addListener(to, fn) {
|
|
8
|
+
function addListener(to, fn, select) {
|
|
9
9
|
let subs = LISTENER.get(to);
|
|
10
10
|
if (!subs)
|
|
11
|
-
LISTENER.set(to, subs = (/* @__PURE__ */ new Map()).set(onReady,
|
|
12
|
-
|
|
11
|
+
LISTENER.set(to, subs = (/* @__PURE__ */ new Map()).set(onReady, false));
|
|
12
|
+
const filter = 2 in arguments && /* @__PURE__ */ new Set([select]);
|
|
13
|
+
if (!filter && !subs.has(onReady))
|
|
13
14
|
fn(true, to);
|
|
14
|
-
subs.set(fn,
|
|
15
|
+
subs.set(fn, filter);
|
|
15
16
|
return () => subs.delete(fn);
|
|
16
17
|
}
|
|
17
18
|
function watch(from, key, value) {
|
|
@@ -105,28 +106,28 @@ function effect(target, callback) {
|
|
|
105
106
|
// src/model.ts
|
|
106
107
|
var ID = /* @__PURE__ */ new WeakMap();
|
|
107
108
|
var PARENT = /* @__PURE__ */ new WeakMap();
|
|
108
|
-
var INSTRUCT = /* @__PURE__ */ new Map();
|
|
109
|
-
var PENDING = /* @__PURE__ */ new WeakMap();
|
|
110
109
|
var STATE = /* @__PURE__ */ new WeakMap();
|
|
110
|
+
var NOTIFY = /* @__PURE__ */ new WeakMap();
|
|
111
|
+
var PENDING = /* @__PURE__ */ new WeakMap();
|
|
111
112
|
var define = Object.defineProperty;
|
|
112
113
|
var Model = class _Model {
|
|
113
114
|
constructor(id) {
|
|
115
|
+
let Type = this.constructor;
|
|
114
116
|
const state = {};
|
|
115
117
|
define(this, "is", { value: this });
|
|
116
|
-
ID.set(this, `${this.constructor}-${id ? String(id) : uid()}`);
|
|
117
118
|
STATE.set(this, state);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
ID.set(this, id ? String(id) : `${Type}-${uid()}`);
|
|
120
|
+
while (true) {
|
|
121
|
+
new Set(NOTIFY.get(Type)).forEach((x) => addListener(this, x));
|
|
122
|
+
if (Type === _Model)
|
|
123
|
+
break;
|
|
124
|
+
Type = Object.getPrototypeOf(Type);
|
|
121
125
|
}
|
|
122
126
|
addListener(this, () => {
|
|
123
127
|
for (const key in this) {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
instruction(this, key, state);
|
|
128
|
-
else {
|
|
129
|
-
state[key] = property.value;
|
|
128
|
+
const desc = Object.getOwnPropertyDescriptor(this, key);
|
|
129
|
+
if ("value" in desc) {
|
|
130
|
+
state[key] = desc.value;
|
|
130
131
|
define(this, key, {
|
|
131
132
|
enumerable: true,
|
|
132
133
|
set: (x) => update(this, key, x),
|
|
@@ -136,56 +137,68 @@ var Model = class _Model {
|
|
|
136
137
|
});
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
|
-
addListener(this,
|
|
140
|
+
addListener(this, () => {
|
|
141
|
+
for (const [_, value] of this)
|
|
142
|
+
if (value instanceof _Model && PARENT.get(value) === this)
|
|
143
|
+
value.set(null);
|
|
144
|
+
Object.freeze(state);
|
|
145
|
+
}, null);
|
|
140
146
|
return null;
|
|
141
147
|
});
|
|
142
148
|
}
|
|
143
149
|
get(arg1, arg2) {
|
|
144
150
|
const self = this.is;
|
|
145
|
-
if (typeof arg1 == "
|
|
146
|
-
return fetch(self, arg1, arg2);
|
|
147
|
-
if (arg1)
|
|
151
|
+
if (typeof arg1 == "function")
|
|
148
152
|
return effect(self, arg1);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
153
|
+
if (arg1 === void 0) {
|
|
154
|
+
let get3 = function(value) {
|
|
155
|
+
if (value instanceof _Model) {
|
|
156
|
+
if (cache.has(value))
|
|
157
|
+
return cache.get(value);
|
|
158
|
+
const model = value;
|
|
159
|
+
cache.set(value, value = {});
|
|
160
|
+
for (const [key, val] of model)
|
|
161
|
+
value[key] = get3(val);
|
|
162
|
+
}
|
|
163
|
+
return value;
|
|
164
|
+
};
|
|
165
|
+
var get2 = get3;
|
|
166
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
167
|
+
return get3(self);
|
|
160
168
|
}
|
|
161
|
-
|
|
169
|
+
if (arg1 === null)
|
|
170
|
+
if (typeof arg2 == "function")
|
|
171
|
+
return addListener(self, arg2.bind(this, this), null);
|
|
172
|
+
else
|
|
173
|
+
return Object.isFrozen(STATE.get(self));
|
|
174
|
+
if (typeof arg2 == "function") {
|
|
175
|
+
const state = STATE.get(self);
|
|
176
|
+
if (arg1 in state)
|
|
177
|
+
arg2.call(this, state[arg1], arg1, this);
|
|
178
|
+
return addListener(self, () => {
|
|
179
|
+
arg2.call(this, arg1 in state ? state[arg1] : arg1, arg1, this);
|
|
180
|
+
}, arg1);
|
|
181
|
+
} else
|
|
182
|
+
return fetch(self, arg1, arg2);
|
|
162
183
|
}
|
|
163
184
|
set(arg1, arg2, arg3) {
|
|
164
185
|
const self = this.is;
|
|
165
186
|
if (typeof arg1 == "function")
|
|
166
187
|
return addListener(self, (key) => {
|
|
167
188
|
if (typeof key == "string")
|
|
168
|
-
return arg1.call(
|
|
189
|
+
return arg1.call(self, key, self);
|
|
169
190
|
});
|
|
170
|
-
if (
|
|
191
|
+
if (arg1 !== void 0)
|
|
171
192
|
return 1 in arguments ? update(self, arg1, arg2, arg3) : update(self, arg1);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
193
|
+
if (!PENDING.has(self))
|
|
194
|
+
return;
|
|
195
|
+
return new Promise((resolve) => {
|
|
177
196
|
const remove = addListener(this, (key) => {
|
|
178
|
-
if (key
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
remove();
|
|
183
|
-
return resolve.bind(null, PENDING.get(self));
|
|
197
|
+
if (key !== true) {
|
|
198
|
+
remove();
|
|
199
|
+
return resolve.bind(null, PENDING.get(self));
|
|
200
|
+
}
|
|
184
201
|
});
|
|
185
|
-
const timeout = typeof arg1 == "number" && setTimeout(() => {
|
|
186
|
-
remove();
|
|
187
|
-
reject(arg1);
|
|
188
|
-
}, arg1);
|
|
189
202
|
});
|
|
190
203
|
}
|
|
191
204
|
/** Iterate over managed properties in this instance of Model. */
|
|
@@ -195,7 +208,7 @@ var Model = class _Model {
|
|
|
195
208
|
/**
|
|
196
209
|
* Creates a new instance of this controller.
|
|
197
210
|
*
|
|
198
|
-
*
|
|
211
|
+
* **Important** - Use instead of `new this(...)` as this method also activates state.
|
|
199
212
|
*
|
|
200
213
|
* @param args - arguments sent to constructor
|
|
201
214
|
*/
|
|
@@ -213,6 +226,13 @@ var Model = class _Model {
|
|
|
213
226
|
static is(maybe) {
|
|
214
227
|
return typeof maybe == "function" && maybe.prototype instanceof this;
|
|
215
228
|
}
|
|
229
|
+
static on(event2) {
|
|
230
|
+
let notify = NOTIFY.get(this);
|
|
231
|
+
if (!notify)
|
|
232
|
+
NOTIFY.set(this, notify = /* @__PURE__ */ new Set());
|
|
233
|
+
notify.add(event2);
|
|
234
|
+
return () => notify.delete(event2);
|
|
235
|
+
}
|
|
216
236
|
};
|
|
217
237
|
define(Model.prototype, "toString", {
|
|
218
238
|
value() {
|
|
@@ -252,15 +272,15 @@ function fetch(subject, property, required) {
|
|
|
252
272
|
function update(subject, key, value, silent) {
|
|
253
273
|
const state = STATE.get(subject);
|
|
254
274
|
let pending = PENDING.get(subject);
|
|
255
|
-
if (typeof key == "string") {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
275
|
+
if (typeof key == "string" && 2 in arguments) {
|
|
276
|
+
const previous = state[key];
|
|
277
|
+
if (value === previous)
|
|
278
|
+
return true;
|
|
279
|
+
state[key] = value;
|
|
280
|
+
if (silent === true)
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (key !== true && key) {
|
|
264
284
|
if (!pending) {
|
|
265
285
|
PENDING.set(subject, pending = {});
|
|
266
286
|
queue(() => {
|
|
@@ -268,7 +288,7 @@ function update(subject, key, value, silent) {
|
|
|
268
288
|
PENDING.delete(subject);
|
|
269
289
|
});
|
|
270
290
|
}
|
|
271
|
-
pending[key] = value;
|
|
291
|
+
pending[key] = key in state ? value : true;
|
|
272
292
|
}
|
|
273
293
|
event(subject, key);
|
|
274
294
|
}
|
|
@@ -277,10 +297,20 @@ function uid() {
|
|
|
277
297
|
}
|
|
278
298
|
|
|
279
299
|
// src/instruction/add.ts
|
|
300
|
+
var INSTRUCT = /* @__PURE__ */ new Map();
|
|
280
301
|
function add(instruction) {
|
|
281
|
-
const
|
|
282
|
-
INSTRUCT.set(
|
|
283
|
-
|
|
302
|
+
const token = Symbol("instruction");
|
|
303
|
+
INSTRUCT.set(token, instruction);
|
|
304
|
+
return token;
|
|
305
|
+
}
|
|
306
|
+
Model.on((_, subject) => {
|
|
307
|
+
const state = STATE.get(subject);
|
|
308
|
+
for (const key in subject) {
|
|
309
|
+
const { value } = Object.getOwnPropertyDescriptor(subject, key);
|
|
310
|
+
const instruction = INSTRUCT.get(value);
|
|
311
|
+
if (!instruction)
|
|
312
|
+
continue;
|
|
313
|
+
INSTRUCT.delete(value);
|
|
284
314
|
delete subject[key];
|
|
285
315
|
const output = instruction.call(subject, key, subject, state);
|
|
286
316
|
if (!output)
|
|
@@ -313,9 +343,9 @@ function add(instruction) {
|
|
|
313
343
|
);
|
|
314
344
|
}
|
|
315
345
|
});
|
|
316
|
-
}
|
|
317
|
-
return
|
|
318
|
-
}
|
|
346
|
+
}
|
|
347
|
+
return null;
|
|
348
|
+
});
|
|
319
349
|
|
|
320
350
|
// src/context.ts
|
|
321
351
|
var Context = class {
|
|
@@ -383,8 +413,8 @@ var Context = class {
|
|
|
383
413
|
if (value || I !== this[key] && !implicit)
|
|
384
414
|
Object.defineProperty(this, key, {
|
|
385
415
|
configurable: true,
|
|
386
|
-
|
|
387
|
-
|
|
416
|
+
writable,
|
|
417
|
+
value
|
|
388
418
|
});
|
|
389
419
|
T = Object.getPrototypeOf(T);
|
|
390
420
|
} while (T !== Model);
|
|
@@ -491,24 +521,23 @@ function ref(arg, arg2) {
|
|
|
491
521
|
return add((key, subject, state) => {
|
|
492
522
|
let value = {};
|
|
493
523
|
if (arg === subject)
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
get: () => state[key2],
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
}
|
|
524
|
+
subject.get(() => {
|
|
525
|
+
for (const key2 in state)
|
|
526
|
+
if (typeof arg2 == "function")
|
|
527
|
+
define2(value, key2, {
|
|
528
|
+
configurable: true,
|
|
529
|
+
get() {
|
|
530
|
+
const out = arg2(key2);
|
|
531
|
+
define2(value, key2, { value: out });
|
|
532
|
+
return out;
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
else {
|
|
536
|
+
const set2 = (value2) => subject.set(key2, value2);
|
|
537
|
+
define2(set2, "current", { get: () => state[key2], set: set2 });
|
|
538
|
+
define2(value, key2, { value: set2 });
|
|
539
|
+
}
|
|
540
|
+
});
|
|
512
541
|
else if (typeof arg == "object")
|
|
513
542
|
throw new Error("ref instruction does not support object which is not 'this'");
|
|
514
543
|
else {
|
|
@@ -608,6 +637,8 @@ function attempt(fn) {
|
|
|
608
637
|
|
|
609
638
|
// src/instruction/use.ts
|
|
610
639
|
function use(value, argument) {
|
|
640
|
+
if (typeof value === "function")
|
|
641
|
+
value = new value();
|
|
611
642
|
return add((property, subject) => {
|
|
612
643
|
function set2(next) {
|
|
613
644
|
if (value instanceof Model && !(next instanceof value.constructor))
|
|
@@ -633,8 +664,6 @@ function use(value, argument) {
|
|
|
633
664
|
if (typeof argument == "function")
|
|
634
665
|
argument(next);
|
|
635
666
|
}
|
|
636
|
-
if (typeof value === "function")
|
|
637
|
-
value = new value();
|
|
638
667
|
const output = { set: set2, value };
|
|
639
668
|
set2(value);
|
|
640
669
|
return output;
|
package/package.json
CHANGED