@expressive/mvc 0.33.0 → 0.34.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/lib/index.js CHANGED
@@ -27,619 +27,378 @@ function __awaiter(thisArg, _arguments, P, generator) {
27
27
  });
28
28
  }
29
29
 
30
- class Issue extends Error {
31
- constructor() {
32
- super(...arguments);
33
- // drop first line (error factory) in stack trace.
34
- this.stack = this.stack.replace(/\n.+/, "");
35
- }
36
- warn() {
37
- console.warn(this.message);
38
- }
39
- }
40
- function issues(register) {
41
- const Library = {};
42
- for (const name in register)
43
- Library[name] = (...args) => new Issue(register[name].apply(null, args));
44
- return Library;
45
- }
46
-
47
- const { assign, create, defineProperties, defineProperty, entries, keys, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
48
-
49
- const Oops$8 = issues({
50
- NotFound: (name) => `Could not find ${name} in context.`
51
- });
52
- function get$1(argument) {
53
- return Control.get((dispatch, context) => {
54
- const refresh = () => dispatch(x => x + 1);
55
- let onUpdate;
56
- let value;
57
- if (typeof argument !== "function") {
58
- const got = context.get(this);
59
- if (got)
60
- value = argument === undefined
61
- ? Control.watch(got, k => k ? onUpdate : undefined)
62
- : got;
63
- else if (argument !== false)
64
- throw Oops$8.NotFound(this);
65
- return {
66
- mount() {
67
- onUpdate = refresh;
68
- return () => onUpdate = null;
69
- },
70
- render: () => value
71
- };
72
- }
73
- let compute = argument;
74
- let suspense;
75
- let getValue;
76
- let factory;
77
- let proxy;
78
- const found = context.get(this);
79
- if (!found)
80
- throw Oops$8.NotFound(this);
81
- function forceUpdate(action) {
82
- if (typeof action == "function")
83
- action = action();
84
- if (getValue)
85
- didUpdate(getValue());
86
- else
87
- refresh();
88
- if (action)
89
- return action.finally(refresh);
90
- }
91
- function didUpdate(got) {
92
- value = got;
93
- if (suspense) {
94
- suspense();
95
- suspense = undefined;
96
- }
97
- else
98
- refresh();
99
- }
100
- proxy = Control.watch(found, () => factory ? null : onUpdate);
101
- getValue = () => compute.call(proxy, proxy, forceUpdate);
102
- value = getValue();
103
- if (value === null) {
104
- getValue = undefined;
105
- onUpdate = null;
106
- return;
107
- }
108
- if (typeof value == "function") {
109
- const get = value;
110
- Control.watch(proxy, () => onUpdate);
111
- factory = true;
112
- compute = () => get();
113
- value = get();
114
- }
115
- if (value instanceof Promise) {
116
- onUpdate = null;
117
- value.then(didUpdate);
118
- value = undefined;
119
- }
120
- else
121
- onUpdate = () => {
122
- const next = getValue();
123
- if (value !== next)
124
- didUpdate(next);
125
- };
126
- return {
127
- mount: () => () => {
128
- onUpdate = null;
129
- },
130
- render: () => {
131
- if (value !== undefined)
132
- return value;
133
- if (onUpdate)
134
- return null;
135
- throw new Promise(res => {
136
- suspense = res;
137
- });
138
- }
139
- };
140
- });
141
- }
142
-
143
- function use$1(apply, repeat) {
144
- const render = Control.use(dispatch => {
145
- const instance = this.new();
146
- const local = Control.watch(instance, () => onUpdate);
147
- const refresh = () => dispatch(x => x + 1);
148
- let onUpdate;
149
- let shouldApply = !!apply;
150
- return {
151
- instance,
152
- mount() {
153
- onUpdate = refresh;
154
- return () => {
155
- onUpdate = null;
156
- instance.null();
157
- };
158
- },
159
- render(props) {
160
- if (shouldApply) {
161
- onUpdate = undefined;
162
- if (typeof props == "function")
163
- props(instance);
164
- else if (props)
165
- for (const key in instance)
166
- if (props.hasOwnProperty(key))
167
- instance[key] = props[key];
168
- if (!repeat)
169
- shouldApply = false;
170
- instance.set(0).then(() => onUpdate = refresh);
171
- }
172
- return local;
173
- }
174
- };
175
- });
176
- return render(apply);
177
- }
178
-
179
- const Oops$7 = issues({
180
- NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
181
- Destoryed: () => "Model is destroyed."
182
- });
183
- function suspense(source, key) {
184
- const error = Oops$7.NotReady(source.subject, key);
185
- const watch = source.observers.get(key);
186
- const promise = new Promise((resolve, reject) => {
187
- function onUpdate(key) {
188
- if (key)
189
- return () => {
190
- if (source.state[key] !== undefined) {
191
- watch.delete(onUpdate);
192
- resolve();
193
- }
194
- };
195
- if (key === null)
196
- reject(Oops$7.Destoryed());
197
- }
198
- watch.add(onUpdate);
199
- });
200
- return assign(promise, {
201
- toString: () => String(error),
202
- name: "Suspense",
203
- message: error.message,
204
- stack: error.stack
205
- });
206
- }
207
- function mayRetry(fn) {
208
- const retry = (err) => {
209
- if (err instanceof Promise)
210
- return err.then(compute);
211
- else
212
- throw err;
213
- };
214
- const compute = () => {
215
- try {
216
- const output = fn();
217
- return output instanceof Promise
218
- ? output.catch(retry)
219
- : output;
220
- }
221
- catch (err) {
222
- return retry(err);
223
- }
224
- };
225
- return compute();
226
- }
227
-
228
- const Oops$6 = issues({
229
- BadCallback: () => `Callback for effect-callback may only return a function.`
230
- });
231
- function createEffect(source, callback) {
232
- return control(source, self => {
233
- let { subject } = self;
234
- let unSet;
235
- let busy = false;
236
- function invoke() {
237
- if (busy)
238
- return;
239
- const output = mayRetry(() => {
240
- if (typeof unSet == "function")
241
- unSet();
242
- unSet = callback.call(subject, subject);
243
- if (unSet instanceof Promise)
244
- unSet = undefined;
245
- if (unSet && typeof unSet !== "function")
246
- throw Oops$6.BadCallback();
247
- });
248
- if (output instanceof Promise) {
249
- output.finally(() => busy = false);
250
- busy = true;
251
- }
252
- }
253
- let refresh;
254
- subject = watch(subject, () => refresh);
255
- self.followers.add(key => (key === null || refresh === null ? refresh : undefined));
256
- invoke();
257
- refresh = invoke;
258
- return () => refresh = null;
259
- });
260
- }
261
- function createValueEffect(callback, ignoreNull) {
262
- let unSet;
263
- return function (value) {
264
- if (typeof unSet == "function")
265
- unSet = void unSet(value);
266
- if (value === null && ignoreNull)
267
- return;
268
- const out = callback.call(this, value);
269
- if (typeof out == "boolean")
270
- return out;
271
- if (!out || out instanceof Promise)
272
- return;
273
- if (typeof out == "function")
274
- unSet = out;
275
- else
276
- throw Oops$6.BadCallback();
277
- };
278
- }
279
-
280
- function getMethod(argument, callback, once) {
281
- if (typeof argument == "function")
282
- return createEffect(this, argument);
283
- const self = control(this, true);
284
- function get(key) {
285
- const value = self.state[key];
286
- return value instanceof Model ? value.get() : value;
287
- }
288
- const extract = typeof argument == "string"
289
- ? () => get(argument)
290
- : () => {
291
- const output = {};
292
- for (const key of argument || keys(self.state))
293
- output[key] = get(key);
294
- return output;
295
- };
296
- if (typeof callback != "function")
297
- return extract();
298
- const select = typeof argument == "string" ? [argument] : argument;
299
- const invoke = () => callback(extract(), self.latest || []);
300
- if (select)
301
- for (const key of select)
302
- try {
303
- const value = self.subject[key];
304
- if (!(key in self.state))
305
- self.watch(key, { value });
306
- }
307
- catch (e) {
308
- // TODO: should this be caught?
309
- }
310
- if (once === undefined)
311
- invoke();
312
- const remove = self.addListener(key => {
313
- if (!select || select.includes(key)) {
314
- if (once)
315
- remove();
316
- return invoke;
317
- }
318
- });
319
- return remove;
320
- }
321
- function setMethod(arg1, arg2) {
322
- const self = control(this, true);
323
- const { state } = self;
324
- let timeout = 0;
325
- switch (typeof arg1) {
326
- case "string":
327
- if (1 in arguments)
328
- if (arg1 in state)
329
- state[arg1] = arg2;
330
- else
331
- self.watch(arg1, { value: arg2 });
332
- self.update(arg1);
333
- break;
334
- case "object":
335
- for (const key in state)
336
- if (key in arg1 && (!arg2 || arg2.includes(key))) {
337
- state[key] = arg1[key];
338
- self.update(key);
339
- }
340
- break;
341
- default:
342
- timeout = arg1;
343
- }
344
- return new Promise((resolve) => {
345
- if (!self.frame.size && timeout === 0)
346
- resolve(false);
347
- const remove = self.addListener(() => {
348
- remove();
349
- return () => resolve(self.latest);
350
- });
351
- if (timeout > 0)
352
- setTimeout(() => {
353
- remove();
354
- resolve(false);
355
- }, timeout);
356
- });
357
- }
358
-
359
- class Model {
360
- constructor(id) {
361
- new Control(this, id === undefined ? uid() : id);
362
- }
363
- /** Mark this instance for garbage collection. */
364
- null() {
365
- control(this, true).clear();
366
- }
367
- /**
368
- * Creates a new instance of this controller.
369
- *
370
- * Beyond `new this(...)`, method will activate managed-state.
371
- *
372
- * @param args - arguments sent to constructor
373
- */
374
- static new(...args) {
375
- const instance = new this(...args);
376
- control(instance, true);
377
- return instance;
378
- }
379
- /**
380
- * Static equivalent of `x instanceof this`.
381
- *
382
- * Will determine if provided class is a subtype of this one.
383
- */
384
- static is(maybe) {
385
- return (typeof maybe == "function" &&
386
- maybe.prototype instanceof this);
387
- }
388
- }
389
- Model.get = get$1;
390
- Model.use = use$1;
391
- defineProperty(Model, "toString", {
392
- value() {
393
- return this.name;
394
- }
395
- });
396
- defineProperties(Model.prototype, {
397
- get: { value: getMethod },
398
- set: { value: setMethod },
399
- toString: {
400
- configurable: true,
401
- value() {
402
- return `${this.constructor.name}-${control(this).id}`;
403
- }
404
- }
405
- });
406
- /* TODO: Remove below on 1.0.0 release. */
407
- defineProperty(Model, "isTypeof", {
408
- get() {
409
- throw new Error("Model.isTypeof method was renamed. Use Model.is instead.");
410
- }
411
- });
412
- defineProperties(Model.prototype, {
413
- is: {
414
- configurable: true,
415
- get() {
416
- throw new Error("Model.is property is now is only available from a hook.");
417
- }
418
- },
419
- on: {
420
- configurable: true,
421
- writable: true,
422
- value() {
423
- throw new Error("Model.on() is deprecated. Use Model.get or Model.set instead.");
424
- }
425
- }
426
- });
427
- /** Random alphanumberic of length 6. Will always start with a letter. */
428
- function uid() {
429
- return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
430
- }
30
+ const { assign, create, defineProperty, entries, freeze, isFrozen, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
431
31
 
432
- const Oops$5 = issues({
433
- BadAssignment: (parent, expected, got) => `${parent} expected Model of type ${expected} but got ${got}.`,
434
- });
435
32
  const REGISTER = new WeakMap();
436
33
  const OBSERVER = new WeakMap();
437
34
  const INSTRUCT = new Map();
438
35
  const PENDING$1 = new WeakMap();
439
36
  const PARENTS = new WeakMap();
37
+ const LIFECYCLE = {
38
+ ready: new Set(),
39
+ dispatch: new Set(),
40
+ update: new Set(),
41
+ didUpdate: new Set(),
42
+ };
440
43
  class Control {
44
+ static on(event, callback) {
45
+ LIFECYCLE[event].add(callback);
46
+ return () => LIFECYCLE[event].delete(callback);
47
+ }
441
48
  constructor(subject, id) {
442
49
  this.state = {};
443
- this.frame = new Set();
444
- this.followers = new Set();
445
- this.observers = new Map([["", this.followers]]);
446
- this.id = id;
50
+ this.frame = freeze({});
51
+ this.listeners = new Map();
52
+ this.id = `${subject.constructor}-${id ? String(id) : uid()}`;
447
53
  this.subject = subject;
448
54
  REGISTER.set(subject, this);
449
55
  if (id !== false)
450
- PENDING$1.set(this, new Set(Control.ready));
451
- }
452
- add(key) {
453
- const { value } = getOwnPropertyDescriptor(this.subject, key);
454
- const instruction = INSTRUCT.get(value);
455
- if (typeof instruction == "function") {
456
- INSTRUCT.delete(value);
457
- instruction(this, key);
458
- }
459
- else if (value instanceof Model)
460
- setRecursive(this, key, value);
461
- else
462
- this.watch(key, { value });
56
+ PENDING$1.set(this, new Set(LIFECYCLE.ready));
463
57
  }
464
58
  watch(key, output) {
465
- const { state } = this;
59
+ const { state, subject, listeners } = this;
466
60
  const { set, enumerable = true } = output;
467
- const subs = new Set();
468
- this.observers.set(key, subs);
469
61
  if ("value" in output)
470
62
  state[key] = output.value;
471
- defineProperty(this.subject, key, {
63
+ defineProperty(subject, key, {
472
64
  enumerable,
473
65
  set: set === false
474
66
  ? undefined
475
- : this.ref(key, set),
67
+ : (next) => {
68
+ const previous = state[key];
69
+ if (next !== previous) {
70
+ state[key] = next;
71
+ if (set && set.call(subject, next, previous) === false)
72
+ state[key] = previous;
73
+ else
74
+ this.update(key);
75
+ }
76
+ },
476
77
  get() {
477
- const event = OBSERVER.get(this);
478
- if (event)
479
- subs.add(event);
480
78
  const value = output.get
481
- ? output.get(this) : state[key];
482
- return event && REGISTER.has(value)
483
- ? watch(value, event)
484
- : value;
79
+ ? output.get(this)
80
+ : state[key];
81
+ const observer = OBSERVER.get(this);
82
+ if (observer) {
83
+ const keys = listeners.get(observer);
84
+ listeners.set(observer, new Set(keys).add(key));
85
+ return watch(value, observer);
86
+ }
87
+ return value;
485
88
  }
486
89
  });
487
90
  }
488
- ref(key, callback) {
489
- const { state, subject } = this;
490
- return (value) => {
491
- if (value !== state[key])
492
- switch (callback && callback.call(subject, value)) {
493
- case undefined:
494
- state[key] = value;
495
- case true:
496
- this.update(key);
497
- }
498
- };
499
- }
500
- update(key) {
501
- const { frame } = this;
502
- if (frame.has(key))
91
+ update(key, value) {
92
+ const { state, subject, listeners } = this;
93
+ if (!listeners || 1 in arguments && value === state[key])
503
94
  return;
504
- if (!frame.size) {
505
- this.latest = undefined;
506
- requestUpdateFrame(() => {
507
- this.latest = Array.from(frame);
508
- this.followers.forEach(cb => {
509
- const notify = cb(undefined, this);
510
- if (notify)
511
- requestUpdateFrame(notify);
95
+ if (isFrozen(this.frame)) {
96
+ this.frame = {};
97
+ enqueue(() => {
98
+ freeze(this.frame);
99
+ listeners.forEach((subs, cb) => {
100
+ if (!subs) {
101
+ const notify = cb(undefined, subject);
102
+ if (notify)
103
+ enqueue(notify);
104
+ }
512
105
  });
513
- frame.clear();
514
106
  });
515
107
  }
516
- frame.add(key);
517
- for (const k of ["", key]) {
518
- const subs = this.observers.get(k);
519
- subs && subs.forEach(cb => {
520
- const notify = cb(key, this);
108
+ if (1 in arguments)
109
+ state[key] = value;
110
+ if (key in this.frame)
111
+ return;
112
+ this.frame[key] = state[key];
113
+ for (const [cb, keys] of listeners)
114
+ if (!keys || keys.has(key)) {
115
+ const notify = cb(key, subject);
521
116
  if (notify === null)
522
- subs.delete(cb);
117
+ listeners.delete(cb);
523
118
  else if (notify)
524
- requestUpdateFrame(notify);
119
+ enqueue(notify);
120
+ }
121
+ }
122
+ fetch(key, required) {
123
+ const { state, subject } = this;
124
+ return () => {
125
+ if (key in state || required === false) {
126
+ const value = state[key];
127
+ if (value !== undefined || !required)
128
+ return value;
129
+ }
130
+ const error = new Error(`${subject}.${key} is not yet available.`);
131
+ const promise = new Promise((resolve, reject) => {
132
+ function check() {
133
+ if (state[key] !== undefined) {
134
+ remove();
135
+ resolve();
136
+ }
137
+ }
138
+ const remove = this.addListener((k) => {
139
+ if (k === key)
140
+ return check;
141
+ if (k === null)
142
+ reject(new Error(`${subject} is destroyed.`));
143
+ });
525
144
  });
526
- }
145
+ throw assign(promise, {
146
+ toString: () => String(error),
147
+ name: "Suspense",
148
+ message: error.message,
149
+ stack: error.stack
150
+ });
151
+ };
527
152
  }
528
153
  addListener(fn) {
529
- this.followers.add(fn);
530
- return () => this.followers.delete(fn);
154
+ this.listeners.set(fn, undefined);
155
+ return () => this.listeners.delete(fn);
531
156
  }
532
157
  clear() {
533
- this.observers.forEach(subs => {
534
- subs.forEach(fn => {
535
- const cb = fn(null, this);
536
- cb && cb();
537
- });
538
- });
539
- this.observers.clear();
158
+ this.listeners.forEach((_, fn) => fn(null, this));
159
+ this.listeners.clear();
160
+ freeze(this.state);
540
161
  }
541
162
  }
542
- Control.ready = new Set();
543
- Control.after = new Set();
544
- Control.before = new Set();
545
- Control.pending = new Set();
546
- Control.for = control;
547
- Control.apply = apply;
548
163
  Control.watch = watch;
164
+ Control.for = control;
165
+ Control.add = add;
549
166
  function control(subject, ready) {
550
- const control = REGISTER.get(subject);
551
- const pending = PENDING$1.get(control);
167
+ const self = REGISTER.get(subject.is);
168
+ const pending = PENDING$1.get(self);
552
169
  if (typeof ready == "function") {
553
- if (!pending)
554
- return ready(control);
555
- let done;
556
- pending.add(() => done = ready(control));
557
- return () => done && done();
170
+ if (pending) {
171
+ let done;
172
+ pending.add(() => done = ready(self));
173
+ return () => done && done();
174
+ }
175
+ return ready(self);
558
176
  }
559
177
  if (pending && ready) {
560
- for (const key in control.subject)
561
- control.add(key);
562
- PENDING$1.delete(control);
563
- pending.forEach(cb => cb(control));
178
+ for (const key in subject) {
179
+ const { value } = getOwnPropertyDescriptor(subject, key);
180
+ const instruction = INSTRUCT.get(value);
181
+ let desc = { value };
182
+ if (instruction) {
183
+ INSTRUCT.delete(value);
184
+ delete subject[key];
185
+ const out = instruction.call(self, key, self);
186
+ desc = typeof out == "function" ? { get: out } : out;
187
+ }
188
+ if (desc)
189
+ self.watch(key, desc);
190
+ }
191
+ PENDING$1.delete(self);
192
+ pending.forEach(cb => cb(self));
564
193
  }
565
- return control;
194
+ return self;
566
195
  }
567
- function requestUpdateFrame(event) {
568
- const { after, before, pending } = Control;
569
- if (!pending.size)
196
+ function enqueue(event) {
197
+ const { update, didUpdate, dispatch } = LIFECYCLE;
198
+ if (!dispatch.size)
570
199
  setTimeout(() => {
571
- before.forEach(x => x());
572
- pending.forEach(notify => {
200
+ update.forEach(x => x());
201
+ dispatch.forEach(event => {
573
202
  try {
574
- notify();
203
+ event();
575
204
  }
576
205
  catch (err) {
577
206
  console.error(err);
578
207
  }
579
208
  });
580
- pending.clear();
581
- after.forEach(x => x());
209
+ dispatch.clear();
210
+ didUpdate.forEach(x => x());
582
211
  }, 0);
583
- pending.add(event);
584
- }
585
- function setRecursive(on, key, value) {
586
- const set = (next) => {
587
- if (next instanceof value.constructor) {
588
- on.state[key] = next;
589
- parent(next, on.subject);
590
- control(next, true);
591
- return true;
592
- }
593
- throw Oops$5.BadAssignment(`${on.subject}.${key}`, value.constructor, next);
594
- };
595
- on.watch(key, { set });
596
- set(value);
212
+ dispatch.add(event);
597
213
  }
598
- function parent(child, assign) {
214
+ function parent(from, assign) {
599
215
  if (!assign)
600
- return PARENTS.get(child);
601
- PARENTS.set(child, assign);
216
+ return PARENTS.get(from);
217
+ PARENTS.set(from, assign);
602
218
  }
603
- function watch(value, cb) {
604
- if (!OBSERVER.has(value)) {
605
- const control = REGISTER.get(value);
606
- value = defineProperty(create(value), "is", { value });
607
- REGISTER.set(value, control);
219
+ function watch(value, argument) {
220
+ const control = REGISTER.get(value);
221
+ if (control) {
222
+ if (!OBSERVER.has(value))
223
+ REGISTER.set(value = create(value), control);
224
+ OBSERVER.set(value, argument);
608
225
  }
609
- OBSERVER.set(value, cb);
610
226
  return value;
611
227
  }
612
- function apply(instruction) {
228
+ function add(instruction) {
613
229
  const placeholder = Symbol("instruction");
614
- INSTRUCT.set(placeholder, (onto, key) => {
615
- delete onto.subject[key];
616
- const output = instruction.call(onto, key, onto);
617
- if (output)
618
- onto.watch(key, typeof output == "function" ? { get: output } : output);
619
- });
230
+ INSTRUCT.set(placeholder, instruction);
620
231
  return placeholder;
232
+ }
233
+ /** Random alphanumberic of length 6. Will always start with a letter. */
234
+ function uid() {
235
+ return (Math.random() * 0.722 + 0.278).toString(36).substring(2, 8).toUpperCase();
621
236
  }
622
237
 
623
- const Oops$4 = issues({
624
- DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
238
+ class Model {
239
+ constructor(id) {
240
+ defineProperty(this, "is", { value: this });
241
+ new Control(this, id);
242
+ }
243
+ get(arg1) {
244
+ return typeof arg1 == "function"
245
+ ? effect(this, arg1)
246
+ : extract(this);
247
+ }
248
+ set(arg1, arg2) {
249
+ return typeof arg1 == "function"
250
+ ? control(this, c => c.addListener(k => k && arg1(k)))
251
+ : nextUpdate(this, arg1, arg2);
252
+ }
253
+ /** Mark this instance for garbage collection. */
254
+ null() {
255
+ control(this).clear();
256
+ }
257
+ /**
258
+ * Creates a new instance of this controller.
259
+ *
260
+ * Beyond `new this(...)`, method will activate managed-state.
261
+ *
262
+ * @param args - arguments sent to constructor
263
+ */
264
+ static new(...args) {
265
+ const instance = new this(...args);
266
+ control(instance, true);
267
+ return instance;
268
+ }
269
+ /**
270
+ * Static equivalent of `x instanceof this`.
271
+ * Determines if provided class is a subtype of this one.
272
+ * If so, language server will make available all static
273
+ * methods and properties of this class.
274
+ */
275
+ static is(maybe) {
276
+ return typeof maybe == "function" && maybe.prototype instanceof this;
277
+ }
278
+ }
279
+ defineProperty(Model.prototype, "toString", {
280
+ value() {
281
+ return control(this).id;
282
+ }
625
283
  });
284
+ defineProperty(Model, "toString", {
285
+ value() {
286
+ return this.name;
287
+ }
288
+ });
289
+
290
+ function extract(target) {
291
+ const cache = new Map();
292
+ function get(value) {
293
+ if (value instanceof Model) {
294
+ let flat = cache.get(value);
295
+ if (!flat) {
296
+ cache.set(value, flat = {});
297
+ const { state } = control(value);
298
+ entries(state).forEach(([key, value]) => {
299
+ flat[key] = get(value);
300
+ });
301
+ }
302
+ return flat;
303
+ }
304
+ return value;
305
+ }
306
+ return get(target);
307
+ }
308
+ function nextUpdate(target, arg1, arg2) {
309
+ return new Promise((resolve, reject) => {
310
+ control(target, self => {
311
+ if (isFrozen(self.frame) && typeof arg1 != "number") {
312
+ resolve(false);
313
+ return;
314
+ }
315
+ const callback = () => resolve(self.frame);
316
+ const remove = self.addListener((key) => {
317
+ if (!arg2 || key && arg2(key) === true) {
318
+ remove();
319
+ if (timeout)
320
+ clearTimeout(timeout);
321
+ return callback;
322
+ }
323
+ });
324
+ const timeout = typeof arg1 == "number" && setTimeout(() => {
325
+ remove();
326
+ reject(arg1);
327
+ }, arg1);
328
+ });
329
+ });
330
+ }
331
+ function effect(target, callback) {
332
+ return control(target, self => {
333
+ let refresh;
334
+ let unSet;
335
+ function invoke() {
336
+ if (unSet)
337
+ unSet();
338
+ try {
339
+ const out = callback.call(target, target);
340
+ unSet = typeof out == "function" ? out : undefined;
341
+ }
342
+ catch (err) {
343
+ if (err instanceof Promise) {
344
+ err.then(() => (refresh = invoke)());
345
+ refresh = undefined;
346
+ }
347
+ else
348
+ console.error(err);
349
+ }
350
+ }
351
+ target = watch(target, () => refresh);
352
+ self.addListener(key => {
353
+ if (!refresh)
354
+ return refresh;
355
+ if (key === null && unSet)
356
+ unSet();
357
+ });
358
+ invoke();
359
+ refresh = invoke;
360
+ return () => {
361
+ refresh = null;
362
+ };
363
+ });
364
+ }
365
+ function attempt(fn) {
366
+ function retry(err) {
367
+ if (err instanceof Promise)
368
+ return err.then(compute);
369
+ else
370
+ throw err;
371
+ }
372
+ function compute() {
373
+ try {
374
+ const output = fn();
375
+ return output instanceof Promise
376
+ ? output.catch(retry)
377
+ : output;
378
+ }
379
+ catch (err) {
380
+ return retry(err);
381
+ }
382
+ }
383
+ return compute();
384
+ }
385
+
626
386
  function run(task) {
627
- return apply((key, control) => {
387
+ return add((key, control) => {
628
388
  let pending = false;
629
389
  const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
630
390
  if (pending)
631
- return Promise.reject(Oops$4.DuplicatePending(key));
391
+ return Promise.reject(new Error(`Invoked action ${key} but one is already active.`));
632
392
  pending = true;
633
393
  control.update(key);
634
394
  try {
635
- return yield mayRetry(() => (task.apply(control.subject, args)));
395
+ return yield attempt(() => task.apply(control.subject, args));
636
396
  }
637
397
  finally {
638
398
  pending = false;
639
399
  control.update(key);
640
400
  }
641
401
  });
642
- control.state[key] = undefined;
643
402
  defineProperty(invoke, "active", {
644
403
  get: () => pending
645
404
  });
@@ -650,107 +409,93 @@ function run(task) {
650
409
  });
651
410
  }
652
411
 
653
- const Oops$3 = issues({
654
- PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or get) as computed source for ${model}.${property}. This is not allowed.`,
655
- Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} ${parent}.${property}.`,
656
- Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}.`,
657
- Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
658
- AmbientRequired: (requested, requester) => `Attempted to find an instance of ${requested} in context. It is required by ${requester}, but one could not be found.`
659
- });
660
412
  function get(arg0, arg1) {
661
- return apply((key, control) => {
413
+ return add((key, control) => {
662
414
  let { subject } = control;
663
415
  if (typeof arg0 == "symbol")
664
- throw Oops$3.PeerNotAllowed(subject, key);
665
- let source = cb => cb(subject);
416
+ throw new Error(`Attempted to use an instruction result (probably use or get) as computed source for ${subject}.${key}. This is not allowed.`);
417
+ let source = resolve => resolve(subject);
666
418
  if (arg0 instanceof Model)
667
419
  subject = arg0;
668
420
  else if (Model.is(arg0)) {
669
421
  const hasParent = parent(subject);
670
422
  if (!hasParent) {
671
423
  if (arg1 === true)
672
- throw Oops$3.Required(arg0, subject);
673
- const fetch = Control.has(subject);
674
- source = got => {
424
+ throw new Error(`New ${subject} created standalone but requires parent of type ${arg0}.`);
425
+ if (!get.from)
426
+ throw new Error(`Using context requires an adapter. If you are only testing, define \`get.context\` to simulate one.`);
427
+ const fetch = get.from(subject);
428
+ source = (resolve) => {
675
429
  fetch(context => {
676
430
  const model = context.get(arg0);
677
431
  if (model)
678
- got(model);
432
+ resolve(model);
679
433
  else if (arg1 !== false)
680
- throw Oops$3.AmbientRequired(arg0, subject);
434
+ throw new Error(`Attempted to find an instance of ${arg0} in context. It is required by ${subject}, but one could not be found.`);
681
435
  });
682
436
  };
683
437
  }
684
438
  else if (!arg0 || hasParent instanceof arg0)
685
439
  subject = hasParent;
686
440
  else
687
- throw Oops$3.Unexpected(arg0, subject, hasParent);
441
+ throw new Error(`New ${subject} created as child of ${hasParent}, but must be instanceof ${arg0}.`);
688
442
  }
689
443
  else if (typeof arg0 == "function")
690
444
  arg1 = arg0.call(subject, key, subject);
691
- return typeof arg1 == "function"
692
- ? computed(control, key, source, arg1)
693
- : recursive(control, key, source, arg1);
445
+ if (typeof arg1 == "function")
446
+ return compute(control, key, source, arg1);
447
+ source(got => control.update(key, got));
448
+ return control.fetch(key, arg1 !== false);
694
449
  });
695
450
  }
696
- function recursive(parent, key, source, required) {
697
- let waiting;
698
- if (source)
699
- source((got) => {
700
- parent.state[key] = got;
701
- if (waiting)
702
- parent.update(key);
703
- });
704
- waiting = true;
705
- return () => {
706
- const value = parent.state[key];
707
- if (value)
708
- return value;
709
- if (required !== false)
710
- throw suspense(parent, key);
711
- };
712
- }
713
451
  const ORDER = new WeakMap();
714
452
  const PENDING = new Set();
715
453
  let OFFSET = 0;
716
- function computed(parent, key, source, setter) {
717
- const { state } = parent;
454
+ function compute(parent, key, source, setter) {
455
+ const { state, subject } = parent;
718
456
  let proxy;
719
- let active;
720
457
  let isAsync;
721
458
  let reset;
722
459
  function compute(initial) {
723
- if (parent.frame.has(key))
460
+ if (key in parent.frame)
724
461
  return;
725
462
  let next;
726
463
  try {
727
464
  next = setter.call(proxy, proxy);
728
465
  }
729
466
  catch (err) {
730
- Oops$3.Failed(parent.subject, key, initial).warn();
467
+ // TODO: address possibility of loop
468
+ if (err instanceof Promise)
469
+ throw err.finally(compute);
470
+ console.warn(`An exception was thrown while ${initial ? "initializing" : "refreshing"} ${subject}.${key}.`);
731
471
  if (initial)
732
472
  throw err;
733
473
  console.error(err);
734
474
  }
735
475
  if (next !== state[key]) {
736
- state[key] = next;
737
- if (!initial || isAsync)
476
+ if (key in state || isAsync)
738
477
  parent.update(key);
478
+ state[key] = next;
739
479
  }
740
480
  }
741
481
  function connect(model) {
482
+ let done;
742
483
  if (reset)
743
484
  reset();
744
- let done;
745
485
  reset = () => done = true;
746
- proxy = watch(model, (_, control) => {
486
+ proxy = watch(model, (_, updated) => {
747
487
  if (done)
748
488
  return null;
749
- if (control !== parent)
750
- compute();
751
- else
489
+ if (updated == subject)
752
490
  PENDING.add(compute);
491
+ else
492
+ compute();
753
493
  });
494
+ output.get = () => {
495
+ if (PENDING.delete(compute))
496
+ compute();
497
+ return state[key];
498
+ };
754
499
  try {
755
500
  compute(true);
756
501
  }
@@ -758,20 +503,17 @@ function computed(parent, key, source, setter) {
758
503
  ORDER.set(compute, OFFSET++);
759
504
  }
760
505
  }
761
- return () => {
762
- if (!active) {
763
- active = true;
506
+ const output = {
507
+ get() {
508
+ output.get = parent.fetch(key);
764
509
  source(connect);
765
510
  isAsync = true;
511
+ return output.get();
766
512
  }
767
- if (!proxy)
768
- throw suspense(parent, key);
769
- if (PENDING.delete(compute))
770
- compute();
771
- return state[key];
772
513
  };
514
+ return output;
773
515
  }
774
- function flushComputed() {
516
+ Control.on("update", () => {
775
517
  while (PENDING.size) {
776
518
  let compute;
777
519
  for (const item of PENDING)
@@ -780,21 +522,14 @@ function flushComputed() {
780
522
  PENDING.delete(compute);
781
523
  compute();
782
524
  }
783
- }
784
- Control.before.add(flushComputed);
525
+ });
785
526
 
786
- const Oops$2 = issues({
787
- BadRefObject: () => `ref instruction does not support object which is not 'this'`
788
- });
789
527
  function ref(arg, arg2) {
790
- return apply((key, source) => {
528
+ return add((key, source) => {
529
+ const { subject, state } = source;
791
530
  let value = {};
792
- if (typeof arg != "object")
793
- value = createRef(source, key, arg && createValueEffect(arg, arg2 !== false));
794
- else if (arg !== source.subject)
795
- throw Oops$2.BadRefObject();
796
- else
797
- for (const key in source.state)
531
+ if (arg === subject)
532
+ for (const key in state)
798
533
  defineProperty(value, key, typeof arg2 == "function" ? {
799
534
  configurable: true,
800
535
  get() {
@@ -805,13 +540,32 @@ function ref(arg, arg2) {
805
540
  } : {
806
541
  value: createRef(source, key)
807
542
  });
808
- source.state[key] = undefined;
809
- source.observers.set(key, new Set());
810
- defineProperty(source.subject, key, { value });
543
+ else if (typeof arg == "object")
544
+ throw new Error("ref instruction does not support object which is not 'this'");
545
+ else {
546
+ let unSet;
547
+ const set = (value) => {
548
+ source.update(key, value);
549
+ if (!arg)
550
+ return;
551
+ if (unSet)
552
+ unSet = void unSet(value);
553
+ if (value !== null || arg2 === false) {
554
+ const out = arg.call(subject, value);
555
+ if (typeof out == "function")
556
+ unSet = out;
557
+ }
558
+ };
559
+ value = defineProperty(set, "current", {
560
+ get: () => state[key],
561
+ set
562
+ });
563
+ }
564
+ defineProperty(subject, key, { value });
811
565
  });
812
566
  }
813
- function createRef(src, key, cb) {
814
- const refObjectFunction = src.ref(key, cb);
567
+ function createRef(src, key) {
568
+ const refObjectFunction = src.update.bind(src, key);
815
569
  defineProperty(refObjectFunction, "current", {
816
570
  set: refObjectFunction,
817
571
  get: () => src.state[key]
@@ -819,115 +573,101 @@ function createRef(src, key, cb) {
819
573
  return refObjectFunction;
820
574
  }
821
575
 
822
- const Oops$1 = issues({
823
- ComputeFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`,
824
- NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
825
- });
826
576
  function set(value, argument) {
827
- return apply((key, control) => {
577
+ return add((key, control) => {
828
578
  const { state, subject } = control;
579
+ const output = {};
829
580
  if (typeof value == "function" || value instanceof Promise) {
830
- const output = {};
831
- const init = () => {
581
+ function init() {
832
582
  try {
833
583
  if (typeof value == "function")
834
- value = mayRetry(value.bind(subject, key, subject));
584
+ value = attempt(value.bind(subject, key, subject));
835
585
  if (value instanceof Promise) {
836
586
  const pending = value
837
587
  .then(value => {
838
588
  output.get = undefined;
839
- state[key] = value;
589
+ control.update(key, value);
840
590
  return value;
841
591
  })
842
592
  .catch(err => {
843
593
  output.get = () => { throw err; };
844
- })
845
- .finally(() => {
846
594
  control.update(key);
847
595
  });
596
+ assign(pending, {
597
+ message: `${subject}.${key} is not yet available.`,
598
+ stack: new Error().stack
599
+ });
848
600
  if (argument !== false)
849
- return () => {
850
- const { message, stack } = Oops$1.NotReady(subject, key);
851
- throw assign(pending, { message, stack });
601
+ return output.get = () => {
602
+ throw pending;
852
603
  };
853
604
  }
854
605
  else
855
- state[key] = value;
606
+ control.update(key, value);
856
607
  }
857
608
  catch (err) {
858
- Oops$1.ComputeFailed(subject, key).warn();
609
+ console.warn(`Generating initial value for ${subject}.${key} failed.`);
859
610
  throw err;
860
611
  }
861
- };
612
+ }
862
613
  if (argument)
863
614
  init();
864
615
  else
865
616
  output.get = () => {
866
- const get = output.get =
867
- key in state ? undefined : init();
617
+ const get = key in state ? null : init();
868
618
  return get ? get() : state[key];
869
619
  };
870
- return output;
871
- }
872
- const set = typeof argument == "function"
873
- ? createValueEffect(argument)
874
- : undefined;
875
- if (value !== undefined) {
876
- state[key] = value;
877
- return { set };
878
620
  }
879
- const output = {
880
- get() {
881
- if (key in state)
882
- return state[key];
883
- throw suspense(control, key);
621
+ else {
622
+ if (value !== undefined)
623
+ output.value = value;
624
+ else
625
+ output.get = control.fetch(key);
626
+ if (typeof argument == "function") {
627
+ let unSet;
628
+ output.set = function (value, previous) {
629
+ const out = argument.call(this, value, previous);
630
+ if (out === false)
631
+ return out;
632
+ if (typeof unSet == "function")
633
+ unSet = void unSet(value);
634
+ if (typeof out == "function")
635
+ unSet = out;
636
+ };
884
637
  }
885
- };
886
- if (set)
887
- output.set = function (value) {
888
- output.set = set;
889
- state[key] = state[key];
890
- return set.call(this, value);
891
- };
638
+ }
892
639
  return output;
893
640
  });
894
641
  }
895
642
 
896
643
  function use(input, argument) {
897
- return apply((key, source) => {
898
- const { state, subject } = source;
644
+ return add((key, source) => {
645
+ const { subject, state } = source;
899
646
  if (typeof input === "function")
900
647
  input = new input();
901
648
  function set(next) {
649
+ if (input instanceof Model && !(next instanceof input.constructor))
650
+ throw new Error(`${subject}.${key} expected Model of type ${input.constructor} but got ${next}.`);
902
651
  if (next instanceof Model) {
903
652
  parent(next, subject);
904
653
  control(next, true);
905
654
  }
906
- else if (next)
907
- next = manage(next);
655
+ else if (next) {
656
+ const subject = create(next);
657
+ const control = new Control(subject, false);
658
+ for (const key in control.state = next)
659
+ control.watch(key, {});
660
+ next = subject;
661
+ }
908
662
  state[key] = next;
909
663
  if (typeof argument == "function")
910
664
  argument(next);
911
- return true;
912
665
  }
913
666
  set(input);
914
667
  return { set };
915
668
  });
916
- }
917
- function manage(next) {
918
- const subject = assign(create(next), {
919
- get: getMethod,
920
- set: setMethod
921
- });
922
- const control = new Control(subject, false);
923
- for (const key in control.state = next)
924
- control.watch(key, {});
925
- return subject;
926
669
  }
927
670
 
928
- const Oops = issues({
929
- MultipleExist: (name) => `Did find ${name} in context, but multiple were defined.`
930
- });
931
671
  class Context {
932
672
  constructor() {
933
673
  this.table = new WeakMap();
@@ -944,7 +684,7 @@ class Context {
944
684
  get(Type) {
945
685
  const result = this[this.has(Type)];
946
686
  if (result === null)
947
- throw Oops.MultipleExist(Type);
687
+ throw new Error(`Did find ${Type} in context, but multiple were defined.`);
948
688
  return result;
949
689
  }
950
690
  include(inputs) {
@@ -964,15 +704,13 @@ class Context {
964
704
  return this.include(inputs);
965
705
  }
966
706
  }
967
- for (const [model] of init) {
968
- const { state } = control(model, true);
969
- Object.values(state).forEach(value => {
707
+ for (const [model] of init)
708
+ values(control(model, true).state).forEach(value => {
970
709
  if (parent(value) === model) {
971
710
  this.add(value, true);
972
711
  init.set(value, false);
973
712
  }
974
713
  });
975
- }
976
714
  return init;
977
715
  }
978
716
  add(input, implicit) {
@@ -984,7 +722,7 @@ class Context {
984
722
  I = new input();
985
723
  }
986
724
  else {
987
- I = control(input).subject;
725
+ I = input;
988
726
  T = I.constructor;
989
727
  writable = false;
990
728
  }
@@ -1024,7 +762,7 @@ const STATE = "__state";
1024
762
  const UPDATE = "__update";
1025
763
  const CONTROL = "__control";
1026
764
  const PARENT = "__parent";
1027
- defineProperties(Model.prototype, {
765
+ Object.defineProperties(Model.prototype, {
1028
766
  [CONTROL]: {
1029
767
  get() {
1030
768
  return control(this);
@@ -1037,7 +775,7 @@ defineProperties(Model.prototype, {
1037
775
  },
1038
776
  [UPDATE]: {
1039
777
  get() {
1040
- return control(this).latest;
778
+ return control(this).frame;
1041
779
  }
1042
780
  },
1043
781
  [PARENT]: {