@expressive/mvc 1.0.0-beta.19 → 1.0.0-beta.22

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
@@ -24,7 +24,7 @@ function issues(register) {
24
24
  return Library;
25
25
  }
26
26
 
27
- const Oops$8 = issues({
27
+ const Oops$9 = issues({
28
28
  BadEffectCallback: () => `Callback for effect-callback may only return a function.`
29
29
  });
30
30
  const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertyDescriptors, getOwnPropertySymbols, keys: keys$1, values } = Object;
@@ -53,7 +53,7 @@ function createEffect(callback) {
53
53
  if (unSet instanceof Promise)
54
54
  unSet = undefined;
55
55
  if (unSet && typeof unSet !== "function")
56
- throw Oops$8.BadEffectCallback();
56
+ throw Oops$9.BadEffectCallback();
57
57
  };
58
58
  }
59
59
  function createValueEffect(callback) {
@@ -69,7 +69,7 @@ function createValueEffect(callback) {
69
69
  if (typeof out == "function")
70
70
  unSet = out;
71
71
  else
72
- throw Oops$8.BadEffectCallback();
72
+ throw Oops$9.BadEffectCallback();
73
73
  };
74
74
  }
75
75
  function select(source, select) {
@@ -92,6 +92,179 @@ function selectRecursive(using, keys) {
92
92
  return Array.from(found);
93
93
  }
94
94
 
95
+ class Subscriber {
96
+ constructor(parent, onUpdate) {
97
+ this.onUpdate = onUpdate;
98
+ this.active = false;
99
+ this.follows = {};
100
+ this.dependant = new Set();
101
+ if (!(parent instanceof Controller))
102
+ parent = manage(parent);
103
+ this.parent = parent;
104
+ this.source = parent.subject;
105
+ this.proxy = create(parent.subject);
106
+ define(this.proxy, LOCAL, this);
107
+ for (const key in parent.state)
108
+ this.spy(key);
109
+ }
110
+ spy(key) {
111
+ const { proxy, source } = this;
112
+ const { set } = getOwnPropertyDescriptor(source, key);
113
+ const intercept = () => {
114
+ this.follow(key);
115
+ delete proxy[key];
116
+ return proxy[key];
117
+ };
118
+ setAlias(intercept, `tap ${key}`);
119
+ defineProperty(proxy, key, {
120
+ get: intercept,
121
+ set,
122
+ configurable: true,
123
+ enumerable: true
124
+ });
125
+ }
126
+ follow(key, cb) {
127
+ this.follows[key] = cb || this.onUpdate;
128
+ }
129
+ commit() {
130
+ const { dependant, follows, parent } = this;
131
+ const onDone = parent.addListener(follows);
132
+ this.active = true;
133
+ dependant.forEach(x => x.commit());
134
+ return this.release = () => {
135
+ dependant.forEach(x => x.release());
136
+ onDone();
137
+ };
138
+ }
139
+ }
140
+
141
+ const Oops$8 = issues({
142
+ ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
143
+ ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
144
+ });
145
+ const ComputedInit = new WeakSet();
146
+ const ComputedInfo = new WeakMap();
147
+ const ComputedUsed = new WeakMap();
148
+ const ComputedKeys = new WeakMap();
149
+ function prepare(parent, key, source, setter, getter) {
150
+ let sub;
151
+ const { state, subject } = parent;
152
+ const info = { key, parent, priority: 1 };
153
+ const current = getter
154
+ ? () => getter.call(subject, state[key], key)
155
+ : () => state[key];
156
+ const prioritize = register(parent, key, info);
157
+ function compute(initial) {
158
+ try {
159
+ return setter.call(sub.proxy, sub.proxy);
160
+ }
161
+ catch (err) {
162
+ Oops$8.ComputeFailed(subject, key, !!initial).warn();
163
+ throw err;
164
+ }
165
+ }
166
+ function update() {
167
+ let value;
168
+ try {
169
+ value = compute(false);
170
+ }
171
+ catch (e) {
172
+ console.error(e);
173
+ }
174
+ finally {
175
+ if (state[key] !== value) {
176
+ parent.update(key, value);
177
+ return value;
178
+ }
179
+ }
180
+ }
181
+ function create(early) {
182
+ sub = new Subscriber(source(), update);
183
+ defineProperty(state, key, {
184
+ value: undefined,
185
+ writable: true
186
+ });
187
+ defineProperty(subject, key, {
188
+ enumerable: true,
189
+ configurable: true,
190
+ get: current
191
+ });
192
+ try {
193
+ state[key] = compute(true);
194
+ }
195
+ catch (e) {
196
+ if (early)
197
+ Oops$8.ComputedEarly(key).warn();
198
+ throw e;
199
+ }
200
+ finally {
201
+ sub.commit();
202
+ for (const key in sub.follows)
203
+ prioritize(key);
204
+ }
205
+ return current();
206
+ }
207
+ setAlias(update, `try ${key}`);
208
+ setAlias(create, `new ${key}`);
209
+ setAlias(setter, `run ${key}`);
210
+ ComputedInit.add(create);
211
+ ComputedInfo.set(update, info);
212
+ for (const on of [state, subject])
213
+ defineProperty(on, key, {
214
+ get: create,
215
+ configurable: true,
216
+ enumerable: true
217
+ });
218
+ }
219
+ function register(on, key, info) {
220
+ let register = ComputedUsed.get(on);
221
+ if (!register)
222
+ ComputedUsed.set(on, register = new Map());
223
+ register.set(key, info);
224
+ return (key) => {
225
+ const peer = register.get(key);
226
+ if (peer && peer.priority >= info.priority)
227
+ info.priority = peer.priority + 1;
228
+ };
229
+ }
230
+ function ensure(on, keys) {
231
+ for (const key of keys) {
232
+ const desc = getOwnPropertyDescriptor(on.subject, key);
233
+ const getter = desc && desc.get;
234
+ if (ComputedInit.has(getter))
235
+ getter(true);
236
+ }
237
+ }
238
+ function capture(on, request) {
239
+ const compute = ComputedInfo.get(request);
240
+ const callback = request;
241
+ if (!compute)
242
+ return;
243
+ let pending = ComputedKeys.get(on);
244
+ if (!pending)
245
+ ComputedKeys.set(on, pending = []);
246
+ if (compute.parent !== on)
247
+ callback();
248
+ else {
249
+ const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
250
+ pending.splice(queue + 1, 0, callback);
251
+ }
252
+ return true;
253
+ }
254
+ function flush(on) {
255
+ const handled = on.frame;
256
+ let pending = ComputedKeys.get(on);
257
+ if (pending) {
258
+ while (pending.length) {
259
+ const compute = pending.shift();
260
+ const { key } = ComputedInfo.get(compute);
261
+ if (!handled.has(key))
262
+ compute();
263
+ }
264
+ ComputedKeys.delete(on);
265
+ }
266
+ }
267
+
95
268
  const Lifecycle = {
96
269
  WILL_RENDER: "willRender",
97
270
  WILL_UPDATE: "willUpdate",
@@ -140,16 +313,27 @@ function eventsFor(prefix) {
140
313
  }
141
314
 
142
315
  const CONTROL = Symbol("control");
143
- const CREATE = Symbol("start");
144
316
  const LOCAL = Symbol("local");
145
317
  const STATE = Symbol("state");
146
- const Pending = new Map();
147
318
  function manage(src) {
148
319
  return src[CONTROL];
149
320
  }
150
- function does(instruction, label) {
151
- const placeholder = Symbol(`${label} instruction`);
152
- Pending.set(placeholder, instruction);
321
+ const Pending = new Map();
322
+ function apply(fn, label) {
323
+ const name = label || fn.name || "pending";
324
+ const placeholder = Symbol(`${name} instruction`);
325
+ function setup(key) {
326
+ let output = fn.call(this, key, this);
327
+ if (typeof output == "function") {
328
+ const getter = output;
329
+ output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
330
+ return getter(this[LOCAL]);
331
+ } });
332
+ }
333
+ if (output)
334
+ defineProperty(this.subject, key, output);
335
+ }
336
+ Pending.set(placeholder, setup);
153
337
  return placeholder;
154
338
  }
155
339
  class Controller {
@@ -161,14 +345,10 @@ class Controller {
161
345
  this.waiting = [];
162
346
  }
163
347
  static setup(onto) {
164
- const create = onto[CREATE];
165
348
  const control = new this(onto);
166
- const after = create && create.call(onto, control);
167
349
  defineLazy(onto, CONTROL, () => {
168
350
  onto[STATE] = control.state;
169
351
  control.start();
170
- if (after)
171
- after();
172
352
  if (onto.didCreate)
173
353
  onto.didCreate();
174
354
  return control;
@@ -273,210 +453,78 @@ function keys(from, using) {
273
453
  return from.select(using);
274
454
  }
275
455
 
276
- class Subscriber {
277
- constructor(parent, onUpdate) {
278
- this.onUpdate = onUpdate;
279
- this.active = false;
280
- this.follows = {};
281
- this.dependant = new Set();
282
- if (!(parent instanceof Controller))
283
- parent = manage(parent);
284
- this.parent = parent;
285
- this.source = parent.subject;
286
- this.proxy = create(parent.subject);
287
- define(this.proxy, LOCAL, this);
288
- for (const key in parent.state)
289
- this.spy(key);
290
- }
291
- spy(key) {
292
- const { proxy, source } = this;
293
- const { set } = getOwnPropertyDescriptor(source, key);
294
- const intercept = () => {
295
- this.follow(key);
296
- delete proxy[key];
297
- return proxy[key];
298
- };
299
- setAlias(intercept, `tap ${key}`);
300
- defineProperty(proxy, key, {
301
- get: intercept,
302
- set,
303
- configurable: true,
304
- enumerable: true
305
- });
306
- }
307
- follow(key, cb) {
308
- this.follows[key] = cb || this.onUpdate;
309
- }
310
- commit() {
311
- const { dependant, follows, parent } = this;
312
- const onDone = parent.addListener(follows);
313
- this.active = true;
314
- dependant.forEach(x => x.commit());
315
- return this.release = () => {
316
- dependant.forEach(x => x.release());
317
- onDone();
456
+ function child(from, name) {
457
+ return apply(function child(key) {
458
+ const proxyCache = new WeakMap();
459
+ const getCurrent = from.call(this, key);
460
+ function subscribe(sub) {
461
+ let child;
462
+ function start() {
463
+ const instance = getCurrent();
464
+ if (instance) {
465
+ child = new Subscriber(instance, sub.onUpdate);
466
+ if (sub.active)
467
+ child.commit();
468
+ sub.dependant.add(child);
469
+ proxyCache.set(sub, child.proxy);
470
+ }
471
+ }
472
+ function refresh() {
473
+ if (child) {
474
+ child.release();
475
+ sub.dependant.delete(child);
476
+ proxyCache.set(sub, undefined);
477
+ child = undefined;
478
+ }
479
+ start();
480
+ sub.onUpdate();
481
+ }
482
+ start();
483
+ sub.follow(key, refresh);
484
+ }
485
+ return (local) => {
486
+ if (!local)
487
+ return getCurrent();
488
+ if (!proxyCache.has(local))
489
+ subscribe(local);
490
+ return proxyCache.get(local);
318
491
  };
319
- }
492
+ }, name || from.name);
320
493
  }
321
494
 
322
495
  const Oops$7 = issues({
323
- ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
324
- ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
496
+ HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
325
497
  });
326
- const ComputedInit = new WeakSet();
327
- const ComputedInfo = new WeakMap();
328
- const ComputedUsed = new WeakMap();
329
- const ComputedKeys = new WeakMap();
330
- function getRegister(on) {
331
- let register = ComputedUsed.get(on);
332
- if (!register)
333
- ComputedUsed.set(on, register = new Map());
334
- return register;
498
+ const useElementLifecycle = lifecycle("element");
499
+ function use$1(init) {
500
+ const [state, update] = react.useState(() => [
501
+ init(() => update(state.concat()))
502
+ ]);
503
+ return state[0];
335
504
  }
336
- function prepare(control, key, getter, getSource) {
337
- const { state, subject } = control;
338
- const defined = getRegister(control);
339
- const info = {
340
- key, parent: control, priority: 1
341
- };
342
- let sub;
343
- defined.set(key, info);
344
- function compute(initial) {
345
- try {
346
- return getter.call(sub.proxy, sub.proxy);
347
- }
348
- catch (err) {
349
- Oops$7.ComputeFailed(subject, key, !!initial).warn();
350
- throw err;
351
- }
352
- }
353
- function update() {
354
- let value;
355
- try {
356
- value = compute(false);
357
- }
358
- catch (e) {
359
- console.error(e);
360
- }
361
- finally {
362
- if (state[key] !== value) {
363
- control.update(key, value);
364
- return value;
365
- }
366
- }
367
- }
368
- function create(early) {
369
- sub = new Subscriber(getSource(), update);
370
- ComputedInfo.set(update, info);
371
- defineProperty(state, key, {
372
- value: undefined,
373
- writable: true
374
- });
375
- defineProperty(subject, key, {
376
- enumerable: true,
377
- configurable: true,
378
- get: () => state[key]
379
- });
380
- try {
381
- return state[key] = compute(true);
382
- }
383
- catch (e) {
384
- if (early)
385
- Oops$7.ComputedEarly(key).warn();
386
- throw e;
387
- }
388
- finally {
389
- sub.commit();
390
- for (const key in sub.follows) {
391
- const peer = defined.get(key);
392
- if (peer && peer.priority >= info.priority)
393
- info.priority = peer.priority + 1;
394
- }
395
- }
396
- }
397
- setAlias(update, `try ${key}`);
398
- setAlias(create, `new ${key}`);
399
- setAlias(getter, `run ${key}`);
400
- ComputedInit.add(create);
401
- for (const on of [state, subject])
402
- defineProperty(on, key, {
403
- get: create,
404
- configurable: true,
405
- enumerable: true
406
- });
407
- return info;
408
- }
409
- function ensure(on, keys) {
410
- for (const key of keys) {
411
- const desc = getOwnPropertyDescriptor(on.subject, key);
412
- const getter = desc && desc.get;
413
- if (ComputedInit.has(getter))
414
- getter(true);
415
- }
416
- }
417
- function capture(on, request) {
418
- const compute = ComputedInfo.get(request);
419
- const callback = request;
420
- if (!compute)
421
- return;
422
- let pending = ComputedKeys.get(on);
423
- if (!pending)
424
- ComputedKeys.set(on, pending = []);
425
- if (compute.parent !== on)
426
- callback();
427
- else {
428
- const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
429
- pending.splice(queue + 1, 0, callback);
430
- }
431
- return true;
432
- }
433
- function flush(on) {
434
- const handled = on.frame;
435
- let pending = ComputedKeys.get(on);
436
- if (pending) {
437
- while (pending.length) {
438
- const compute = pending.shift();
439
- const { key } = ComputedInfo.get(compute);
440
- if (!handled.has(key))
441
- compute();
442
- }
443
- ComputedKeys.delete(on);
444
- }
445
- }
446
-
447
- const Oops$6 = issues({
448
- HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
449
- });
450
- const useElementLifecycle = lifecycle("element");
451
- function use$1(init) {
452
- const [state, update] = react.useState(() => [
453
- init(() => update(state.concat()))
454
- ]);
455
- return state[0];
456
- }
457
- function useLazy(Type, args, callback) {
458
- const instance = react.useMemo(() => {
459
- const instance = Type.create(...args || []);
460
- if (callback)
461
- callback(instance);
462
- return instance;
463
- }, []);
464
- react.useLayoutEffect(() => () => instance.destroy(), []);
465
- return instance;
466
- }
467
- function useWatcher(target, focus, expected) {
468
- const hook = use$1(refresh => {
469
- const sub = new Subscriber(target, refresh);
470
- if (focus) {
471
- const src = sub.proxy;
472
- const key = keys(sub.parent, focus)[0];
473
- defineProperty(sub, "proxy", {
474
- get() {
475
- if (src[key] === undefined && expected)
476
- throw Oops$6.HasPropertyUndefined(target, key);
477
- return src[key];
478
- }
479
- });
505
+ function useLazy(Type, args, callback) {
506
+ const instance = react.useMemo(() => {
507
+ const instance = Type.create(...args || []);
508
+ if (callback)
509
+ callback(instance);
510
+ return instance;
511
+ }, []);
512
+ react.useLayoutEffect(() => () => instance.destroy(), []);
513
+ return instance;
514
+ }
515
+ function useWatcher(target, focus, expected) {
516
+ const hook = use$1(refresh => {
517
+ const sub = new Subscriber(target, refresh);
518
+ if (focus) {
519
+ const src = sub.proxy;
520
+ const key = keys(sub.parent, focus)[0];
521
+ defineProperty(sub, "proxy", {
522
+ get() {
523
+ if (src[key] === undefined && expected)
524
+ throw Oops$7.HasPropertyUndefined(target, key);
525
+ return src[key];
526
+ }
527
+ });
480
528
  }
481
529
  return sub;
482
530
  });
@@ -506,6 +554,56 @@ function useModel(Type, args, callback) {
506
554
  return hook.proxy;
507
555
  }
508
556
 
557
+ const Oops$6 = issues({
558
+ CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
559
+ AmbientRequired: (requested, requester, key) => `Attempted to find an instance of ${requested} in context. It is required for [${requester}.${key}], but one could not be found.`
560
+ });
561
+ const PendingContext = new WeakMap();
562
+ const ContextWasUsed = new WeakMap();
563
+ function tap(type, argument) {
564
+ return child(function tap(key) {
565
+ return pendingAccess(this.subject, type, key, argument);
566
+ });
567
+ }
568
+ function pendingAccess(from, type, key, argument) {
569
+ if ("current" in type)
570
+ return () => type.current;
571
+ if ("current" in from.constructor)
572
+ throw Oops$6.CantAttachGlobal(from, type.name);
573
+ let instance;
574
+ getPending(from).push(context => {
575
+ instance = context.get(type);
576
+ if (typeof argument == "function") {
577
+ if (argument(instance) === false)
578
+ instance = undefined;
579
+ }
580
+ else if (!instance && argument)
581
+ throw Oops$6.AmbientRequired(type.name, from, key);
582
+ });
583
+ return () => instance;
584
+ }
585
+ function usePeerContext(subject) {
586
+ if (ContextWasUsed.has(subject)) {
587
+ if (ContextWasUsed.get(subject))
588
+ useLookup();
589
+ return;
590
+ }
591
+ const pending = PendingContext.get(subject);
592
+ if (pending) {
593
+ const local = useLookup();
594
+ for (const init of pending)
595
+ init(local);
596
+ PendingContext.delete(subject);
597
+ }
598
+ ContextWasUsed.set(subject, !!pending);
599
+ }
600
+ function getPending(subject) {
601
+ let pending = PendingContext.get(subject);
602
+ if (!pending)
603
+ PendingContext.set(subject, pending = []);
604
+ return pending;
605
+ }
606
+
509
607
  class Lookup {
510
608
  constructor() {
511
609
  this.table = new Map();
@@ -632,62 +730,73 @@ function Provider(props) {
632
730
  }
633
731
 
634
732
  const Oops$4 = issues({
635
- CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
636
- AmbientRequired: (requested, requester, key) => `Attempted to find an instance of ${requested} in context. It is required for [${requester}.${key}], but one could not be found.`
637
- });
638
- const PendingContext = new WeakMap();
639
- const ContextWasUsed = new WeakMap();
640
- const tap = (type, argument) => attach(function tap(key) {
641
- return pendingAccess(this.subject, type, key, argument);
642
- });
643
- function pendingAccess(from, type, key, argument) {
644
- if ("current" in type)
645
- return () => type.current;
646
- if ("current" in from.constructor)
647
- throw Oops$4.CantAttachGlobal(from, type.name);
648
- let instance;
649
- getPending(from).push(context => {
650
- instance = context.get(type);
651
- if (typeof argument == "function") {
652
- if (argument(instance) === false)
653
- instance = undefined;
654
- }
655
- else if (!instance && argument)
656
- throw Oops$4.AmbientRequired(type.name, from, key);
657
- });
658
- return () => instance;
659
- }
660
- function usePeerContext(subject) {
661
- if (ContextWasUsed.has(subject)) {
662
- if (ContextWasUsed.get(subject))
663
- useLookup();
664
- return;
665
- }
666
- const pending = PendingContext.get(subject);
667
- if (pending) {
668
- const local = useLookup();
669
- for (const init of pending)
670
- init(local);
671
- PendingContext.delete(subject);
672
- }
673
- ContextWasUsed.set(subject, !!pending);
674
- }
675
- function getPending(subject) {
676
- let pending = PendingContext.get(subject);
677
- if (!pending)
678
- PendingContext.set(subject, pending = []);
679
- return pending;
680
- }
681
-
682
- const Oops$3 = issues({
683
733
  StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
684
734
  NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
685
735
  });
686
- class State {
736
+ const useComponentLifecycle = lifecycle("component");
737
+ class Model {
687
738
  constructor() {
688
- Controller.setup(this);
739
+ const control = Controller.setup(this);
689
740
  define(this, "get", this);
690
741
  define(this, "set", this);
742
+ defer(control, "on");
743
+ defer(control, "effect");
744
+ control.include(() => {
745
+ delete this.on;
746
+ delete this.effect;
747
+ });
748
+ }
749
+ tap(path, expect) {
750
+ const proxy = useWatcher(this, path, expect);
751
+ this.update("willRender", true);
752
+ return proxy;
753
+ }
754
+ tag(id) {
755
+ return useSubscriber(this, id);
756
+ }
757
+ on(subset, handler, squash, once) {
758
+ const control = manage(this);
759
+ const set = keys(control, subset);
760
+ const batch = {};
761
+ const remove = control.addListener(batch);
762
+ const callback = squash
763
+ ? handler.bind(this)
764
+ : (frame) => {
765
+ for (const key of frame)
766
+ if (set.includes(key))
767
+ handler.call(this, control.state[key], key);
768
+ };
769
+ const handle = once
770
+ ? (k) => { remove(); callback(k); }
771
+ : callback;
772
+ for (const key of set)
773
+ batch[key] = handle;
774
+ ensure(control, set);
775
+ return remove;
776
+ }
777
+ once(select, callback, squash) {
778
+ if (callback)
779
+ return this.on(select, callback, squash, true);
780
+ else
781
+ return new Promise(resolve => {
782
+ this.on(select, resolve, true, true);
783
+ });
784
+ }
785
+ effect(callback, select) {
786
+ const control = manage(this);
787
+ let target = this;
788
+ const effect = createEffect(callback);
789
+ const invoke = () => effect.call(target, target);
790
+ if (select) {
791
+ invoke();
792
+ return this.on(select, invoke, true);
793
+ }
794
+ else {
795
+ const sub = new Subscriber(control, invoke);
796
+ target = sub.proxy;
797
+ invoke();
798
+ return sub.commit();
799
+ }
691
800
  }
692
801
  import(from, subset) {
693
802
  for (const key of keys(manage(this), subset))
@@ -707,15 +816,18 @@ class State {
707
816
  arg = select(this, arg);
708
817
  if (typeof arg == "boolean") {
709
818
  if (!control.pending === arg)
710
- return Promise.reject(Oops$3.StrictUpdate(arg));
819
+ return Promise.reject(Oops$4.StrictUpdate(arg));
711
820
  }
712
821
  else if (arg) {
713
822
  control.update(arg);
714
- if (1 in arguments && arg in this)
715
- if (typeof tag != "boolean")
716
- this[arg](tag);
717
- else if (tag)
718
- this[arg]();
823
+ if (1 in arguments && arg in this) {
824
+ const method = this[arg];
825
+ if (typeof method == "function")
826
+ if (typeof tag != "boolean")
827
+ method.call(this, tag);
828
+ else if (tag)
829
+ method.call(this);
830
+ }
719
831
  }
720
832
  return {
721
833
  then(callback) {
@@ -725,7 +837,7 @@ class State {
725
837
  else
726
838
  callback(false);
727
839
  else
728
- throw Oops$3.NoChaining();
840
+ throw Oops$4.NoChaining();
729
841
  }
730
842
  };
731
843
  }
@@ -735,28 +847,14 @@ class State {
735
847
  toString() {
736
848
  return this.constructor.name;
737
849
  }
738
- tap(path, expect) {
739
- return useWatcher(this, path, expect);
740
- }
741
850
  static create(...args) {
742
851
  const instance = new this(...args);
743
852
  manage(instance);
744
853
  return instance;
745
854
  }
746
855
  static new(args, callback) {
747
- return useLazy(this, args, callback);
748
- }
749
- static use(args, callback) {
750
- return useModel(this, args, callback);
751
- }
752
- static uses(props, only) {
753
- return this.use([], instance => {
754
- instance.import(props, only);
755
- });
756
- }
757
- static using(props, only) {
758
- const instance = this.use([]);
759
- instance.import(props, only);
856
+ const instance = useLazy(this, args, callback);
857
+ instance.update("willRender", true);
760
858
  return instance;
761
859
  }
762
860
  static find(strict) {
@@ -771,74 +869,10 @@ class State {
771
869
  instance);
772
870
  }
773
871
  static tap(key, expect) {
774
- return useWatcher(this.find(true), key, expect);
775
- }
776
- static isTypeof(maybe) {
777
- return (typeof maybe == "function" &&
778
- maybe.prototype instanceof this);
779
- }
780
- }
781
- State.CONTROL = CONTROL;
782
- State.STATE = STATE;
783
- State.INIT = CREATE;
784
- State.LOCAL = LOCAL;
785
-
786
- const useComponentLifecycle = lifecycle("component");
787
- class Model extends State {
788
- [CREATE](using) {
789
- defer(using, "on");
790
- defer(using, "effect");
791
- return () => {
792
- delete this.on;
793
- delete this.effect;
794
- };
872
+ return this.find(true).tap(key, expect);
795
873
  }
796
- on(subset, handler, squash, once) {
797
- const control = manage(this);
798
- const set = keys(control, subset);
799
- const batch = {};
800
- const remove = control.addListener(batch);
801
- const callback = squash
802
- ? handler.bind(this)
803
- : (frame) => {
804
- for (const key of frame)
805
- if (set.includes(key))
806
- handler.call(this, control.state[key], key);
807
- };
808
- const handle = once
809
- ? (k) => { remove(); callback(k); }
810
- : callback;
811
- for (const key of set)
812
- batch[key] = handle;
813
- ensure(control, set);
814
- return remove;
815
- }
816
- once(select, callback, squash) {
817
- if (callback)
818
- return this.on(select, callback, squash, true);
819
- else
820
- return new Promise(resolve => {
821
- this.on(select, resolve, true, true);
822
- });
823
- }
824
- effect(callback, select) {
825
- const control = manage(this);
826
- let target = this;
827
- const effect = createEffect(callback);
828
- const invoke = () => effect.call(target, target);
829
- if (select) {
830
- invoke();
831
- return this.on(select, invoke, true);
832
- }
833
- else {
834
- const sub = new Subscriber(control, invoke);
835
- target = sub.proxy;
836
- invoke();
837
- return sub.commit();
838
- }
839
- }
840
- tag(id) {
841
- return useSubscriber(this, id);
874
+ static tag(id) {
875
+ return this.find(true).tag(id);
842
876
  }
843
877
  static use(args, callback) {
844
878
  const instance = useModel(this, args, callback);
@@ -846,13 +880,27 @@ class Model extends State {
846
880
  usePeerContext(instance.get);
847
881
  return instance;
848
882
  }
849
- static tag(id) {
850
- return useSubscriber(this.find(true), id);
883
+ static uses(props, only) {
884
+ return this.use([], instance => {
885
+ instance.import(props, only);
886
+ });
887
+ }
888
+ static using(props, only) {
889
+ const instance = this.use([]);
890
+ instance.import(props, only);
891
+ return instance;
851
892
  }
852
893
  static meta(path) {
853
894
  return useWatcher(this, path);
854
895
  }
896
+ static isTypeof(maybe) {
897
+ return (typeof maybe == "function" &&
898
+ maybe.prototype instanceof this);
899
+ }
855
900
  }
901
+ Model.CONTROL = CONTROL;
902
+ Model.STATE = STATE;
903
+ Model.LOCAL = LOCAL;
856
904
  defineLazy(Model, CONTROL, function () {
857
905
  return new Controller(this).start();
858
906
  });
@@ -868,194 +916,218 @@ function defer(on, method) {
868
916
  };
869
917
  }
870
918
 
919
+ const Parent = new WeakMap();
920
+ const Oops$3 = issues({
921
+ ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
922
+ UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
923
+ UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
924
+ BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`
925
+ });
926
+ function bootstrap(object) {
927
+ const breakdown = getOwnPropertyDescriptors(object);
928
+ const control = new Model();
929
+ for (const key in breakdown)
930
+ defineProperty(control, key, breakdown[key]);
931
+ return control;
932
+ }
933
+ function use(input, argument) {
934
+ return child(function use(key) {
935
+ let instance;
936
+ const update = (next) => {
937
+ instance =
938
+ next instanceof Model ? next :
939
+ next && bootstrap(next);
940
+ if (instance) {
941
+ Parent.set(instance, this.subject);
942
+ manage(instance);
943
+ }
944
+ if (typeof argument == "function")
945
+ argument(instance);
946
+ else if (!instance && argument !== false)
947
+ throw Oops$3.UndefinedNotAllowed(key);
948
+ };
949
+ if (input) {
950
+ instance =
951
+ Model.isTypeof(input) ? new input() :
952
+ input instanceof Model ? input :
953
+ typeof input === "function" ? input() :
954
+ typeof input === "object" ? bootstrap(input) :
955
+ Oops$3.BadArgument(typeof input).throw();
956
+ if (instance)
957
+ update(instance);
958
+ }
959
+ else
960
+ argument = false;
961
+ this.manage(key, instance, update);
962
+ return () => instance;
963
+ });
964
+ }
965
+ function parent(Expects, required) {
966
+ return apply(function parent() {
967
+ const child = this.subject;
968
+ const expected = Expects.name;
969
+ const value = Parent.get(this.subject);
970
+ if (!value) {
971
+ if (required)
972
+ throw Oops$3.ParentRequired(expected, child);
973
+ }
974
+ else if (!(value instanceof Expects))
975
+ throw Oops$3.UnexpectedParent(expected, child, value);
976
+ return { value };
977
+ });
978
+ }
979
+
871
980
  const Oops$2 = issues({
872
981
  DuplicateAction: (key) => `Invoked action ${key} but one is already active.`,
873
982
  BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
874
- PeerNotAllowed: (model, property) => 'Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.'
983
+ PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
875
984
  });
876
- const set = (fn, name) => does(function set(key) {
877
- let output = fn.call(this, key, this);
878
- if (typeof output == "function") {
879
- const getter = output;
880
- output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
881
- return getter(this[LOCAL]);
882
- } });
883
- }
884
- if (output)
885
- defineProperty(this.subject, key, output);
886
- }, name || fn.name || "pending");
887
- const ref = (cb) => set(function ref(key) {
985
+ function createRef(key, cb) {
888
986
  const refObjectFunction = this.setter(key, cb && createValueEffect(cb));
889
987
  defineProperty(refObjectFunction, "current", {
890
988
  set: refObjectFunction,
891
989
  get: () => this.state[key]
892
990
  });
893
- return {
894
- value: refObjectFunction
895
- };
896
- });
897
- const on = (initial, cb) => set(function on(key) {
898
- this.manage(key, initial, cb && createValueEffect(cb));
899
- });
900
- const memo = (factory, defer) => set(function memo(key) {
901
- const source = this.subject;
902
- const get = () => factory.call(source);
903
- if (defer)
904
- defineLazy(source, key, get);
905
- else
906
- define(source, key, get());
907
- });
908
- const lazy = (value) => set(function lazy(key) {
909
- const source = this.subject;
910
- source[key] = value;
911
- defineProperty(this.state, key, {
912
- get: () => source[key]
991
+ return refObjectFunction;
992
+ }
993
+ function ref(arg) {
994
+ return apply(function ref(key) {
995
+ let value = {};
996
+ if (typeof arg == "object") {
997
+ const source = manage(arg);
998
+ for (const key in source.state)
999
+ defineLazy(value, key, createRef.bind(source, key));
1000
+ }
1001
+ else
1002
+ value = createRef.call(this, key, arg);
1003
+ return { value };
913
1004
  });
914
- });
915
- const act = (task) => set(function act(key) {
916
- let pending = false;
917
- const invoke = (...args) => {
918
- if (pending)
919
- return Promise.reject(Oops$2.DuplicateAction(key));
920
- pending = true;
921
- this.update(key);
922
- return new Promise(res => {
923
- res(task.apply(this.subject, args));
924
- }).finally(() => {
925
- pending = false;
1005
+ }
1006
+ function on(initial, cb) {
1007
+ return apply(function on(key) {
1008
+ this.manage(key, initial, cb && createValueEffect(cb));
1009
+ });
1010
+ }
1011
+ function memo(factory, defer) {
1012
+ return apply(function memo(key) {
1013
+ const source = this.subject;
1014
+ const get = () => factory.call(source);
1015
+ if (defer)
1016
+ defineLazy(source, key, get);
1017
+ else
1018
+ define(source, key, get());
1019
+ });
1020
+ }
1021
+ function lazy(value) {
1022
+ return apply(function lazy(key) {
1023
+ const source = this.subject;
1024
+ source[key] = value;
1025
+ defineProperty(this.state, key, {
1026
+ get: () => source[key]
1027
+ });
1028
+ });
1029
+ }
1030
+ function act(task) {
1031
+ return apply(function act(key) {
1032
+ let pending = false;
1033
+ const invoke = (...args) => {
1034
+ if (pending)
1035
+ return Promise.reject(Oops$2.DuplicateAction(key));
1036
+ pending = true;
926
1037
  this.update(key);
1038
+ return new Promise(res => {
1039
+ res(task.apply(this.subject, args));
1040
+ }).finally(() => {
1041
+ pending = false;
1042
+ this.update(key);
1043
+ });
1044
+ };
1045
+ setAlias(invoke, `run ${key}`);
1046
+ defineProperty(invoke, "active", {
1047
+ get: () => pending
927
1048
  });
928
- };
929
- setAlias(invoke, `run ${key}`);
930
- defineProperty(invoke, "active", {
931
- get: () => pending
1049
+ return {
1050
+ value: invoke,
1051
+ writable: false
1052
+ };
932
1053
  });
933
- return {
934
- value: invoke,
935
- writable: false
936
- };
937
- });
938
- function from(source, fn) {
939
- return set(function from(key) {
1054
+ }
1055
+ function from(source, setter, getter) {
1056
+ return apply(function from(key) {
940
1057
  const { subject } = this;
941
- let getter;
942
- let getSource = () => this;
943
- // Easy mistake, using a peer - will always be unresolved.
1058
+ let getSource;
1059
+ // Easy mistake, using a peer, will always be unresolved.
944
1060
  if (typeof source == "symbol")
945
1061
  throw Oops$2.PeerNotAllowed(subject, key);
946
- if (typeof source == "object") {
947
- getter = fn;
948
- // replace source controller incase different
949
- getSource = () => source[CONTROL];
1062
+ // replace source controller in-case different
1063
+ if (typeof source == "object")
1064
+ getSource = () => manage(source);
1065
+ // specifically an arrow function (getter factory)
1066
+ else if (!source.prototype) {
1067
+ setter = source.call(subject, key);
1068
+ getSource = () => this;
950
1069
  }
951
- // is an arrow function (getter factory)
952
- else if (!source.prototype)
953
- getter = source(key);
954
1070
  // is a peer Model (constructor)
955
- else if (Model.isTypeof(source)) {
956
- getter = fn;
1071
+ else if (Model.isTypeof(source))
957
1072
  getSource = pendingAccess(subject, source, key, true);
958
- }
959
- // Vanilla function is to ambiguous so not allowed.
1073
+ // Regular function is to ambiguous so not allowed.
960
1074
  else
961
1075
  throw Oops$2.BadComputedSource(subject, key, source);
962
- prepare(this, key, getter, getSource);
1076
+ prepare(this, key, getSource, setter, getter);
963
1077
  });
964
1078
  }
965
1079
 
966
- const attach = (from, name) => set(function attach(key) {
967
- const proxyCache = new WeakMap();
968
- const getCurrent = from.call(this, key);
969
- function subscribe(sub) {
970
- let child;
971
- function start() {
972
- const instance = getCurrent();
973
- if (instance) {
974
- child = new Subscriber(instance, sub.onUpdate);
975
- if (sub.active)
976
- child.commit();
977
- sub.dependant.add(child);
978
- proxyCache.set(sub, child.proxy);
979
- }
980
- }
981
- function refresh() {
982
- if (child) {
983
- child.release();
984
- sub.dependant.delete(child);
985
- proxyCache.set(sub, undefined);
986
- child = undefined;
987
- }
988
- start();
989
- sub.onUpdate();
990
- }
991
- start();
992
- sub.follow(key, refresh);
993
- }
994
- return (local) => {
995
- if (!local)
996
- return getCurrent();
997
- if (!proxyCache.has(local))
998
- subscribe(local);
999
- return proxyCache.get(local);
1000
- };
1001
- }, name || from.name);
1002
-
1003
- const Parent = new WeakMap();
1004
1080
  const Oops$1 = issues({
1005
- ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
1006
- UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
1007
- UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
1008
- BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`
1081
+ ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
1009
1082
  });
1010
- function bootstrap(object) {
1011
- const breakdown = getOwnPropertyDescriptors(object);
1012
- const control = new Model();
1013
- for (const key in breakdown)
1014
- defineProperty(control, key, breakdown[key]);
1015
- return control;
1083
+ /**
1084
+ * Create suspense promise also interpretable as an error.
1085
+ * React will handle it but other contexts probably won't.
1086
+ */
1087
+ function suspense(model, key) {
1088
+ const { message, stack } = Oops$1.ValueNotReady(model, key);
1089
+ const promise = new Promise(resolve => {
1090
+ const release = model.on(key, (value) => {
1091
+ if (value !== undefined)
1092
+ release(), resolve();
1093
+ });
1094
+ });
1095
+ return Object.assign(promise, { message, stack });
1016
1096
  }
1017
- const use = (input, argument) => attach(function use(key) {
1018
- let instance;
1019
- const update = (next) => {
1020
- instance =
1021
- next instanceof Model ? next :
1022
- next && bootstrap(next);
1023
- if (instance) {
1024
- Parent.set(instance, this.subject);
1025
- manage(instance);
1026
- }
1027
- if (typeof argument == "function")
1028
- argument(instance);
1029
- else if (!instance && argument !== false)
1030
- throw Oops$1.UndefinedNotAllowed(key);
1031
- };
1032
- if (input) {
1033
- instance =
1034
- Model.isTypeof(input) ? new input() :
1035
- input instanceof Model ? input :
1036
- typeof input === "function" ? input() :
1037
- typeof input === "object" ? bootstrap(input) :
1038
- Oops$1.BadArgument(typeof input).throw();
1039
- if (instance)
1040
- update(instance);
1041
- }
1097
+ function pending(source, compute) {
1098
+ if (typeof source == "function" && !source.prototype)
1099
+ return suspendForAsync(source);
1042
1100
  else
1043
- argument = false;
1044
- this.manage(key, instance, update);
1045
- return () => instance;
1046
- });
1047
- const parent = (Expects, required) => set(function parent() {
1048
- const child = this.subject;
1049
- const expected = Expects.name;
1050
- const value = Parent.get(this.subject);
1051
- if (!value) {
1052
- if (required)
1053
- throw Oops$1.ParentRequired(expected, child);
1054
- }
1055
- else if (!(value instanceof Expects))
1056
- throw Oops$1.UnexpectedParent(expected, child, value);
1057
- return { value };
1058
- });
1101
+ return from(source, compute, function getter(value, key) {
1102
+ if (value === undefined)
1103
+ throw suspense(this, key);
1104
+ return value;
1105
+ });
1106
+ }
1107
+ function suspendForAsync(source) {
1108
+ return apply(function suspense(key) {
1109
+ let waiting = true;
1110
+ let output;
1111
+ let error;
1112
+ return () => {
1113
+ if (waiting) {
1114
+ const issue = Oops$1.ValueNotReady(this.subject, key);
1115
+ const promise = source
1116
+ .call(this.subject, key)
1117
+ .catch(err => error = err)
1118
+ .then((value) => output = value)
1119
+ .finally(() => waiting = false);
1120
+ throw Object.assign(promise, {
1121
+ message: issue.message,
1122
+ stack: issue.stack
1123
+ });
1124
+ }
1125
+ if (error)
1126
+ throw error;
1127
+ return output;
1128
+ };
1129
+ });
1130
+ }
1059
1131
 
1060
1132
  const Oops = issues({
1061
1133
  GlobalDoesNotExist: (name) => `Tried to access singleton ${name}, but none exist! Did you forget to initialize?\nCall ${name}.create() before attempting to access, or consider using ${name}.use() instead.`,
@@ -1102,16 +1174,16 @@ exports.Consumer = Consumer;
1102
1174
  exports.Model = Model;
1103
1175
  exports.Provider = Provider;
1104
1176
  exports.Singleton = Singleton;
1105
- exports.State = State;
1106
1177
  exports.act = act;
1107
- exports.attach = attach;
1178
+ exports.apply = apply;
1179
+ exports.child = child;
1108
1180
  exports["default"] = Model;
1109
1181
  exports.from = from;
1110
1182
  exports.lazy = lazy;
1111
1183
  exports.memo = memo;
1112
1184
  exports.on = on;
1113
1185
  exports.parent = parent;
1186
+ exports.pending = pending;
1114
1187
  exports.ref = ref;
1115
- exports.set = set;
1116
1188
  exports.tap = tap;
1117
1189
  exports.use = use;