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