@expressive/mvc 1.0.0-beta.36 → 1.0.0-beta.37

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
@@ -8,33 +8,6 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
8
8
 
9
9
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
10
10
 
11
- /*! *****************************************************************************
12
- Copyright (c) Microsoft Corporation.
13
-
14
- Permission to use, copy, modify, and/or distribute this software for any
15
- purpose with or without fee is hereby granted.
16
-
17
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
18
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
19
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
20
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
21
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
22
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23
- PERFORMANCE OF THIS SOFTWARE.
24
- ***************************************************************************** */
25
-
26
- function __rest(s, e) {
27
- var t = {};
28
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
29
- t[p] = s[p];
30
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
31
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
32
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
33
- t[p[i]] = s[p[i]];
34
- }
35
- return t;
36
- }
37
-
38
11
  class Issue extends Error {
39
12
  constructor() {
40
13
  super(...arguments);
@@ -52,10 +25,10 @@ function issues(register) {
52
25
  return Library;
53
26
  }
54
27
 
55
- const Oops$9 = issues({
28
+ const Oops$d = issues({
56
29
  BadEffectCallback: () => `Callback for effect-callback may only return a function.`
57
30
  });
58
- const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertyDescriptors, getOwnPropertySymbols, keys, values } = Object;
31
+ const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
59
32
  function define(target, key, value) {
60
33
  defineProperty(target, key, { value });
61
34
  }
@@ -81,7 +54,7 @@ function createEffect(callback) {
81
54
  if (unSet instanceof Promise)
82
55
  unSet = undefined;
83
56
  if (unSet && typeof unSet !== "function")
84
- throw Oops$9.BadEffectCallback();
57
+ throw Oops$d.BadEffectCallback();
85
58
  };
86
59
  }
87
60
  function createValueEffect(callback) {
@@ -97,52 +70,92 @@ function createValueEffect(callback) {
97
70
  if (typeof out == "function")
98
71
  unSet = out;
99
72
  else
100
- throw Oops$9.BadEffectCallback();
73
+ throw Oops$d.BadEffectCallback();
101
74
  };
102
75
  }
103
76
 
104
- const Oops$8 = issues({
77
+ const UPDATE = new WeakMap();
78
+ function applyUpdate(subject, keys) {
79
+ UPDATE.set(subject, keys);
80
+ return () => {
81
+ setTimeout(() => {
82
+ UPDATE.delete(subject);
83
+ }, 0);
84
+ };
85
+ }
86
+
87
+ const Oops$c = issues({
105
88
  StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
106
89
  NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
107
90
  });
91
+ const READY = new WeakSet();
108
92
  class Controller {
109
93
  constructor(subject) {
110
94
  this.subject = subject;
95
+ this.state = {};
111
96
  this.frame = new Set();
112
97
  this.waiting = new Set();
98
+ this.onDestroy = new Set();
113
99
  this.followers = new Set();
100
+ this.proxy = Object.create(subject);
114
101
  }
115
102
  start() {
116
- this.state = {};
117
103
  for (const key in this.subject)
118
104
  this.manage(key);
119
- const handle = new Set(this.waiting);
120
- this.waiting.clear();
121
- handle.forEach(cb => cb([]));
122
- return this;
105
+ this.emit([]);
123
106
  }
124
- manage(key, handler) {
125
- const { state, subject } = this;
126
- const desc = getOwnPropertyDescriptor(subject, key);
127
- if (desc && "value" in desc) {
128
- const { value } = desc;
129
- const instruction = Pending.get(value);
130
- if (instruction) {
131
- Pending.delete(value);
132
- delete subject[key];
133
- instruction.call(this, key, this);
134
- }
135
- else if (typeof value !== "function" || /^[A-Z]/.test(key)) {
136
- state[key] = value;
137
- defineProperty(subject, key, {
138
- enumerable: true,
139
- get: () => state[key],
140
- set: this.setter(key, handler)
141
- });
142
- }
107
+ stop() {
108
+ this.followers.clear();
109
+ this.onDestroy.forEach(x => x());
110
+ }
111
+ manage(key) {
112
+ const { proxy, state, subject } = this;
113
+ const { value: entry } = getOwnPropertyDescriptor(subject, key);
114
+ if (typeof entry == "function")
115
+ return;
116
+ let onGet;
117
+ let onSet;
118
+ let enumerable;
119
+ const instruction = Pending.get(entry);
120
+ if (instruction) {
121
+ Pending.delete(entry);
122
+ delete subject[key];
123
+ const desc = instruction.call(this, key, this);
124
+ if (!desc)
125
+ return;
126
+ if ("value" in desc)
127
+ state[key] = desc.value;
128
+ onSet = desc.set;
129
+ onGet = desc.get;
130
+ enumerable = desc.enumerable;
143
131
  }
132
+ else
133
+ state[key] = entry;
134
+ const set = onSet !== false
135
+ ? this.ref(key, onSet)
136
+ : undefined;
137
+ function get() {
138
+ const local = this[LOCAL];
139
+ const value = state[key];
140
+ if (local && !local.watch[key])
141
+ local.watch[key] = true;
142
+ return onGet ? onGet(value, local) : value;
143
+ }
144
+ setAlias(get, `tap ${key}`);
145
+ defineProperty(subject, key, {
146
+ enumerable,
147
+ set,
148
+ get: onGet
149
+ ? () => onGet(state[key])
150
+ : () => state[key]
151
+ });
152
+ defineProperty(proxy, key, {
153
+ enumerable,
154
+ set,
155
+ get
156
+ });
144
157
  }
145
- setter(key, handler) {
158
+ ref(key, handler) {
146
159
  const { state, subject } = this;
147
160
  return (value) => {
148
161
  if (state[key] == value)
@@ -163,6 +176,18 @@ class Controller {
163
176
  this.followers.delete(listener);
164
177
  };
165
178
  }
179
+ emit(keys) {
180
+ const waiting = [...this.waiting];
181
+ this.waiting.clear();
182
+ applyUpdate(this.subject, keys)();
183
+ for (const callback of waiting)
184
+ try {
185
+ callback(keys);
186
+ }
187
+ catch (err) {
188
+ console.error(err);
189
+ }
190
+ }
166
191
  update(key, value) {
167
192
  if (1 in arguments)
168
193
  this.state[key] = value;
@@ -172,19 +197,8 @@ class Controller {
172
197
  setTimeout(() => {
173
198
  flush(this);
174
199
  const keys = Object.freeze([...this.frame]);
175
- const handle = new Set(this.waiting);
176
- this.waiting.clear();
177
200
  this.frame.clear();
178
- UPDATE.set(this.subject, keys);
179
- setTimeout(() => {
180
- UPDATE.delete(this.subject);
181
- }, 0);
182
- handle.forEach(callback => {
183
- try {
184
- callback(keys);
185
- }
186
- catch (e) { }
187
- });
201
+ this.emit(keys);
188
202
  }, 0);
189
203
  this.frame.add(key);
190
204
  for (const callback of this.followers) {
@@ -199,7 +213,7 @@ class Controller {
199
213
  return;
200
214
  }
201
215
  if (typeof arg == "boolean" && arg !== this.frame.size > 0)
202
- return Promise.reject(Oops$8.StrictUpdate(arg));
216
+ return Promise.reject(Oops$c.StrictUpdate(arg));
203
217
  return {
204
218
  then: (callback) => {
205
219
  if (callback)
@@ -208,76 +222,83 @@ class Controller {
208
222
  else
209
223
  callback(false);
210
224
  else
211
- throw Oops$8.NoChaining();
225
+ throw Oops$c.NoChaining();
212
226
  }
213
227
  };
214
228
  }
229
+ }
230
+ function control(subject, cb) {
231
+ let control = subject[CONTROL];
232
+ const ready = READY.has(control);
233
+ if (!control) {
234
+ control = new Controller(subject);
235
+ define(subject, CONTROL, control);
236
+ }
237
+ if (!cb) {
238
+ if (!ready) {
239
+ READY.add(control);
240
+ control.start();
241
+ }
242
+ return control;
243
+ }
244
+ if (!ready) {
245
+ let done;
246
+ control.requestUpdate(() => {
247
+ done = cb(control);
248
+ });
249
+ return () => done && done();
250
+ }
251
+ return cb(control);
215
252
  }
216
253
 
217
254
  class Subscriber {
218
255
  constructor(parent, onUpdate) {
219
256
  this.onUpdate = onUpdate;
220
257
  this.active = false;
221
- this.watch = {};
222
258
  this.dependant = new Set();
259
+ this.watch = {};
223
260
  if (!(parent instanceof Controller))
224
- parent = getController(parent);
225
- this.parent = parent;
226
- this.source = parent.subject;
227
- const proxy = this.proxy = create(parent.subject);
228
- const DEBUG = (keys) => {
229
- UPDATE.set(proxy, keys.filter(k => k in this.watch));
230
- };
261
+ parent = control(parent);
262
+ let reset;
263
+ const proxy = create(parent.proxy);
264
+ const controller = parent;
231
265
  define(proxy, LOCAL, this);
232
266
  defineProperty(this, "proxy", {
233
- get: () => {
234
- if (UPDATE.has(proxy))
235
- setTimeout(() => {
236
- UPDATE.delete(proxy);
237
- }, 0);
267
+ configurable: true,
268
+ get() {
269
+ if (reset)
270
+ setTimeout(reset, 0);
238
271
  return proxy;
239
272
  }
240
273
  });
241
- for (const key in parent.state) {
242
- const existing = getOwnPropertyDescriptor(parent.subject, key);
243
- const isUsing = () => {
244
- this.watch[key] = true;
245
- delete proxy[key];
246
- return proxy[key];
274
+ const onEvent = (key) => {
275
+ const handler = this.watch[key];
276
+ if (!handler)
277
+ return;
278
+ if (typeof handler == "function")
279
+ handler();
280
+ const notify = this.onUpdate(key, controller);
281
+ const getWhy = (keys) => {
282
+ reset = applyUpdate(proxy, keys.filter(k => k in this.watch));
247
283
  };
248
- setAlias(isUsing, `tap ${key}`);
249
- defineProperty(proxy, key, {
250
- set: existing.set,
251
- get: isUsing,
252
- configurable: true,
253
- enumerable: true
254
- });
255
- }
284
+ if (notify) {
285
+ controller.requestUpdate(getWhy);
286
+ controller.requestUpdate(notify);
287
+ }
288
+ };
256
289
  this.commit = () => {
257
- const { parent } = this;
258
- const onDone = parent.addListener(key => {
259
- const handler = this.watch[key];
260
- if (!handler)
261
- return;
262
- if (typeof handler == "function")
263
- handler();
264
- const notify = this.onUpdate(key, parent);
265
- if (notify) {
266
- parent.requestUpdate(DEBUG);
267
- parent.requestUpdate(notify);
268
- }
269
- });
290
+ const release = controller.addListener(onEvent);
270
291
  this.active = true;
271
292
  this.dependant.forEach(x => x.commit());
272
293
  return this.release = () => {
273
294
  this.dependant.forEach(x => x.release());
274
- onDone();
295
+ release();
275
296
  };
276
297
  };
277
298
  }
278
299
  }
279
300
 
280
- const Oops$7 = issues({
301
+ const Oops$b = issues({
281
302
  ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
282
303
  ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
283
304
  });
@@ -295,15 +316,12 @@ function prepare(parent, key, source, setter, getter) {
295
316
  USED.set(parent, register);
296
317
  }
297
318
  register.set(key, info);
298
- const current = getter
299
- ? () => getter(parent, key)
300
- : () => state[key];
301
319
  function compute(initial) {
302
320
  try {
303
321
  return setter.call(sub.proxy, sub.proxy);
304
322
  }
305
323
  catch (err) {
306
- Oops$7.ComputeFailed(subject, key, !!initial).warn();
324
+ Oops$b.ComputeFailed(subject, key, !!initial).warn();
307
325
  throw err;
308
326
  }
309
327
  }
@@ -339,17 +357,12 @@ function prepare(parent, key, source, setter, getter) {
339
357
  value: undefined,
340
358
  writable: true
341
359
  });
342
- defineProperty(subject, key, {
343
- enumerable: true,
344
- configurable: true,
345
- get: current
346
- });
347
360
  try {
348
- state[key] = compute(true);
361
+ return state[key] = compute(true);
349
362
  }
350
363
  catch (e) {
351
364
  if (early)
352
- Oops$7.ComputedEarly(key).warn();
365
+ Oops$b.ComputedEarly(key).warn();
353
366
  throw e;
354
367
  }
355
368
  finally {
@@ -360,23 +373,24 @@ function prepare(parent, key, source, setter, getter) {
360
373
  info.priority = peer.priority + 1;
361
374
  }
362
375
  }
363
- return current();
364
376
  }
365
377
  setAlias(update, `try ${key}`);
366
378
  setAlias(create, `new ${key}`);
367
379
  setAlias(setter, `run ${key}`);
368
380
  INIT.add(create);
369
381
  INFO.set(update, info);
370
- for (const on of [state, subject])
371
- defineProperty(on, key, {
372
- get: create,
373
- configurable: true,
374
- enumerable: true
375
- });
382
+ defineProperty(state, key, {
383
+ get: create,
384
+ configurable: true,
385
+ enumerable: true
386
+ });
387
+ return getter
388
+ ? () => getter(parent, key)
389
+ : {};
376
390
  }
377
- function ensure$1(on, keys) {
391
+ function ensure(on, keys) {
378
392
  for (const key of keys) {
379
- const desc = getOwnPropertyDescriptor(on.subject, key);
393
+ const desc = getOwnPropertyDescriptor(on.state, key);
380
394
  const getter = desc && desc.get;
381
395
  if (INIT.has(getter))
382
396
  getter(true);
@@ -401,26 +415,6 @@ const CONTROL = Symbol("CONTROL");
401
415
  const WHY = Symbol("UPDATE");
402
416
  const LOCAL = Symbol("LOCAL");
403
417
  const STATE = Symbol("STATE");
404
- const UPDATE = new WeakMap();
405
- function ensure(subject, callback) {
406
- const control = subject[CONTROL];
407
- if (!control.state) {
408
- let done;
409
- control.requestUpdate(() => {
410
- done = callback(control);
411
- });
412
- return () => done();
413
- }
414
- return callback(control);
415
- }
416
- function getController(subject) {
417
- const control = subject[CONTROL];
418
- if (!control.state)
419
- control.start();
420
- if (subject.didCreate)
421
- subject.didCreate();
422
- return control;
423
- }
424
418
  class Model {
425
419
  constructor() {
426
420
  define(this, CONTROL, new Controller(this));
@@ -434,11 +428,13 @@ class Model {
434
428
  return UPDATE.get(this);
435
429
  }
436
430
  on(select, handler, squash, once) {
437
- return ensure(this, control => {
431
+ return control(this, control => {
432
+ if (typeof select == "function")
433
+ return control.addListener(select);
438
434
  const keys = typeof select == "string" ? [select] :
439
435
  !select.length ? getOwnPropertyNames(control.state) :
440
436
  select;
441
- ensure$1(control, keys);
437
+ ensure(control, keys);
442
438
  const callback = squash
443
439
  ? handler.bind(this)
444
440
  : frame => frame
@@ -463,7 +459,7 @@ class Model {
463
459
  }
464
460
  effect(callback, select) {
465
461
  const effect = createEffect(callback);
466
- return ensure(this, control => {
462
+ return control(this, control => {
467
463
  if (!select) {
468
464
  const sub = new Subscriber(control, () => invoke);
469
465
  const invoke = () => {
@@ -477,30 +473,40 @@ class Model {
477
473
  effect.call(this.get, this.get);
478
474
  };
479
475
  invoke();
480
- if (!select.length)
481
- select.push("willDestroy");
476
+ if (!select.length) {
477
+ control.onDestroy.add(invoke);
478
+ return () => {
479
+ control.onDestroy.delete(invoke);
480
+ };
481
+ }
482
482
  return control.addListener(key => {
483
483
  if (select.includes(key))
484
484
  return invoke;
485
485
  });
486
486
  });
487
487
  }
488
- import(from, subset) {
489
- for (const key of subset || getOwnPropertyNames(this))
490
- if (key in from)
491
- this[key] = from[key];
488
+ // TODO: account for exotic properties
489
+ import(source, select) {
490
+ const { subject } = control(this);
491
+ if (!select)
492
+ select = getOwnPropertyNames(subject);
493
+ for (const key of select)
494
+ if (key in source)
495
+ this[key] = source[key];
492
496
  }
493
497
  export(subset) {
494
- const { state } = getController(this);
498
+ const { state } = control(this);
495
499
  const output = {};
496
- for (const key of subset || getOwnPropertyNames(state))
500
+ if (!subset)
501
+ subset = getOwnPropertyNames(state);
502
+ for (const key of subset)
497
503
  output[key] = state[key];
498
504
  return output;
499
505
  }
500
506
  update(arg, tag) {
501
- const control = getController(this);
507
+ const target = control(this);
502
508
  if (typeof arg == "string") {
503
- control.update(arg);
509
+ target.update(arg);
504
510
  if (1 in arguments && arg in this) {
505
511
  const method = this[arg];
506
512
  if (typeof method == "function")
@@ -511,28 +517,39 @@ class Model {
511
517
  }
512
518
  arg = undefined;
513
519
  }
514
- return control.requestUpdate(arg);
520
+ return target.requestUpdate(arg);
515
521
  }
522
+ /**
523
+ * Mark this instance for garbage-collection and send `willDestroy` event to all listeners.
524
+ */
516
525
  destroy() {
517
- this.update("willDestroy", true);
526
+ control(this).stop();
518
527
  }
519
528
  toString() {
520
529
  return this.constructor.name;
521
530
  }
531
+ /**
532
+ * Creates a new instance of this controller.
533
+ *
534
+ * Beyond `new this(...)`, method will activate managed-state.
535
+ *
536
+ * @param args - arguments sent to constructor
537
+ */
522
538
  static create(...args) {
523
539
  const instance = new this(...args);
524
- getController(instance);
540
+ control(instance);
525
541
  return instance;
526
542
  }
543
+ /**
544
+ * Static equivalent of `x instanceof this`.
545
+ *
546
+ * Will determine if provided class is a subtype of this one.
547
+ */
527
548
  static isTypeof(maybe) {
528
549
  return (typeof maybe == "function" &&
529
550
  maybe.prototype instanceof this);
530
551
  }
531
552
  }
532
- Model.CONTROL = CONTROL;
533
- Model.STATE = STATE;
534
- Model.LOCAL = LOCAL;
535
- Model.WHY = WHY;
536
553
  defineLazy(Model, CONTROL, function () {
537
554
  return new Controller(this);
538
555
  });
@@ -542,306 +559,302 @@ function apply(fn, label) {
542
559
  const name = label || fn.name || "pending";
543
560
  const placeholder = Symbol(`${name} instruction`);
544
561
  function setup(key) {
545
- const { subject, state } = this;
546
562
  let output = fn.call(this, key, this);
547
- let desc = {};
548
- let get;
549
- let set;
550
563
  if (!output)
551
564
  return;
552
- if (typeof output == "function")
553
- get = output;
554
- else {
555
- (({ get, set } = output), desc = __rest(output, ["get", "set"]));
556
- if ("value" in desc) {
557
- state[key] = desc.value;
558
- if (get || set)
559
- delete desc.value;
560
- else {
561
- defineProperty(subject, key, desc);
562
- return;
563
- }
564
- }
565
+ if ("explicit" in output) {
566
+ defineProperty(this.subject, key, output);
567
+ return;
565
568
  }
566
- defineProperty(subject, key, Object.assign(Object.assign({}, desc), { set: this.setter(key, set), get: get
567
- ? function () {
568
- return get(state[key], this[LOCAL]);
569
- }
570
- : () => state[key] }));
569
+ if ("recursive" in output)
570
+ output = Object.assign(Object.assign({}, output), { get: recursive(this, key) });
571
+ if (typeof output == "function")
572
+ return { get: output };
573
+ else
574
+ return output;
571
575
  }
572
576
  Pending.set(placeholder, setup);
573
577
  return placeholder;
574
- }
575
-
576
- //TODO: sync instruction
577
- function child(source, name) {
578
- return apply(function child(key) {
579
- const context = new WeakMap();
580
- const peer = source.call(this, key);
581
- function subscribe(sub) {
582
- let child;
583
- function init() {
584
- if (child) {
585
- child.release();
586
- sub.dependant.delete(child);
587
- context.set(sub, undefined);
588
- child = undefined;
589
- }
590
- const instance = peer.get();
591
- if (instance) {
592
- child = new Subscriber(instance, sub.onUpdate);
593
- if (sub.active)
594
- child.commit();
595
- sub.dependant.add(child);
596
- context.set(sub, child.proxy);
597
- }
578
+ }
579
+ function recursive(source, key) {
580
+ const context = new WeakMap();
581
+ const subscribe = (parent) => {
582
+ let child;
583
+ const init = () => {
584
+ if (child) {
585
+ child.release();
586
+ parent.dependant.delete(child);
587
+ context.set(parent, undefined);
588
+ child = undefined;
598
589
  }
599
- init();
600
- sub.watch[key] = init;
601
- }
602
- return {
603
- value: peer.get(),
604
- set: peer.set,
605
- get(_value, local) {
606
- if (!local)
607
- return peer.get();
608
- if (!context.has(local))
609
- subscribe(local);
610
- return context.get(local);
590
+ const value = source.state[key];
591
+ if (value && CONTROL in value) {
592
+ child = new Subscriber(value, parent.onUpdate);
593
+ if (parent.active)
594
+ child.commit();
595
+ parent.dependant.add(child);
596
+ context.set(parent, child.proxy);
611
597
  }
612
598
  };
613
- }, name || source.name);
599
+ init();
600
+ parent.watch[key] = init;
601
+ };
602
+ return (value, local) => {
603
+ if (!local)
604
+ return value;
605
+ if (!context.has(local))
606
+ subscribe(local);
607
+ return context.get(local);
608
+ };
614
609
  }
615
610
 
616
- const Parent = new WeakMap();
617
- const Oops$6 = issues({
618
- ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
619
- UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
620
- BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
611
+ const Oops$a = issues({
612
+ DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
621
613
  });
622
- function bootstrap(object) {
623
- const breakdown = getOwnPropertyDescriptors(object);
624
- const control = new Model();
625
- for (const key in breakdown)
626
- defineProperty(control, key, breakdown[key]);
627
- return control;
628
- }
629
- function use$1(input, argument) {
630
- return child(function use(key) {
631
- const { subject } = this;
632
- let current;
633
- const onUpdate = (next) => {
634
- if (next) {
635
- current = next instanceof Model
636
- ? next : bootstrap(next);
637
- Parent.set(current, subject);
638
- getController(current);
639
- }
640
- else
641
- current = undefined;
642
- if (typeof argument == "function")
643
- return argument(current);
614
+ function act(task) {
615
+ return apply(function act(key) {
616
+ let pending = false;
617
+ const invoke = (...args) => {
618
+ if (pending)
619
+ return Promise.reject(Oops$a.DuplicateAction(key));
620
+ pending = true;
621
+ this.update(key);
622
+ return new Promise(res => {
623
+ res(task.apply(this.subject, args));
624
+ }).finally(() => {
625
+ pending = false;
626
+ this.update(key);
627
+ });
644
628
  };
645
- if (input) {
646
- const initial = Model.isTypeof(input)
647
- ? new input() :
648
- input instanceof Model
649
- ? input :
650
- typeof input === "function"
651
- ? input() :
652
- typeof input === "object"
653
- ? bootstrap(input)
654
- : null;
655
- if (initial)
656
- onUpdate(initial);
657
- else if (initial === null)
658
- throw Oops$6.BadArgument(typeof input);
659
- }
629
+ this.state[key] = undefined;
630
+ setAlias(invoke, `run ${key}`);
631
+ defineProperty(invoke, "active", {
632
+ get: () => pending
633
+ });
660
634
  return {
661
- get: () => current,
662
- set: onUpdate
635
+ value: invoke,
636
+ set: false
663
637
  };
664
638
  });
665
- }
666
- function parent(Expects, required) {
667
- return apply(function parent() {
668
- const child = this.subject;
669
- const expected = Expects.name;
670
- const value = Parent.get(this.subject);
671
- if (!value) {
672
- if (required)
673
- throw Oops$6.ParentRequired(expected, child);
674
- }
675
- else if (!(value instanceof Expects))
676
- throw Oops$6.UnexpectedParent(expected, child, value);
677
- return { value };
678
- });
679
- }
680
-
681
- const Lifecycle = {
682
- WILL_RENDER: "willRender",
683
- WILL_UPDATE: "willUpdate",
684
- WILL_MOUNT: "willMount",
685
- WILL_UNMOUNT: "willUnmount",
686
- DID_MOUNT: "didMount"
687
- };
688
- [
689
- "didCreate",
690
- "willDestroy",
691
- ...values(Lifecycle)
692
- ];
693
- function lifecycle(prefix) {
694
- const type = eventsFor(prefix);
695
- return function useLifecycle(sub, tag) {
696
- const emit = React__default["default"].useMemo(() => {
697
- const subject = sub.source;
698
- const id = typeof tag == "function" ? tag(subject) : tag;
699
- return (name) => {
700
- for (const key of [name, type(name)])
701
- subject.update(key, id);
702
- };
703
- }, [sub, tag]);
704
- emit(Lifecycle.WILL_RENDER);
705
- emit(sub.active
706
- ? Lifecycle.WILL_UPDATE
707
- : Lifecycle.WILL_MOUNT);
708
- React__default["default"].useLayoutEffect(() => {
709
- sub.commit();
710
- emit(Lifecycle.DID_MOUNT);
711
- return () => {
712
- emit(Lifecycle.WILL_UNMOUNT);
713
- sub.release();
714
- };
715
- }, [sub]);
716
- };
717
- }
718
- function eventsFor(prefix) {
719
- const map = new Map();
720
- for (const name of values(Lifecycle)) {
721
- const alias = prefix + name[0].toUpperCase() + name.slice(1);
722
- map.set(name, alias);
723
- }
724
- return (e) => map.get(e);
725
639
  }
726
640
 
727
- const useElementLifecycle = lifecycle("element");
728
- const useComponentLifecycle = lifecycle("component");
729
- function use(init) {
730
- const [state, update] = React__default["default"].useState(() => [
731
- init(() => update(state.concat()))
732
- ]);
733
- return state[0];
734
- }
735
- function useTap(model, path, expect) {
736
- if (typeof path == "function")
737
- return useComputed(model, path, expect);
738
- const proxy = useActive(model, path, expect);
739
- model.update("willRender", true);
740
- return proxy;
741
- }
742
- function useTag(model, id) {
743
- const hook = use(refresh => (new Subscriber(model, () => refresh)));
744
- useElementLifecycle(hook, id || 0);
745
- return hook.proxy;
746
- }
747
- function useNew(model, callback) {
748
- const hook = use(refresh => {
749
- if (callback)
750
- callback(model);
751
- return new Subscriber(model, () => refresh);
752
- });
753
- useComponentLifecycle(hook);
754
- return hook.proxy;
755
- }
756
- function usePassive(Type, callback) {
757
- const instance = React__default["default"].useMemo(() => {
758
- const instance = Type.create();
759
- if (callback)
760
- callback(instance);
761
- return instance;
762
- }, []);
763
- React__default["default"].useLayoutEffect(() => () => instance.destroy(), []);
764
- instance.update("willRender", true);
765
- return instance;
641
+ const Oops$9 = issues({
642
+ ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
643
+ });
644
+ /**
645
+ * Get value, suspend instead if undefined.
646
+ *
647
+ * Throws suspense promise also interpretable as an error.
648
+ * React could handle it but other contexts probably not.
649
+ */
650
+ function pendingValue(via, key) {
651
+ const value = via.state[key];
652
+ if (value === undefined)
653
+ throw suspend(via, key);
654
+ return value;
766
655
  }
767
- function useActive(target, focus, expected) {
768
- const hook = use(refresh => {
769
- const sub = new Subscriber(target, () => refresh);
770
- if (focus) {
771
- const source = sub.proxy;
772
- defineProperty(sub, "proxy", {
773
- get() {
774
- const value = source[focus];
775
- if (value === undefined && expected)
776
- throw suspend(sub.parent, focus);
777
- return value;
778
- }
779
- });
656
+ function pendingFactory(via, key, fn) {
657
+ const { subject, state } = via;
658
+ let waiting;
659
+ let error;
660
+ return () => {
661
+ if (waiting)
662
+ throw waiting;
663
+ if (error)
664
+ throw error;
665
+ if (key in state)
666
+ return state[key];
667
+ let output;
668
+ try {
669
+ output = fn.call(subject, key, subject);
780
670
  }
781
- return sub;
782
- });
783
- React__default["default"].useLayoutEffect(() => hook.commit(), []);
784
- return hook.proxy;
785
- }
786
- function useComputed(target, compute, expected) {
787
- const hook = use(refresh => {
788
- const sub = new Subscriber(target, () => update);
789
- const spy = sub.proxy;
790
- let value = compute.call(spy, spy);
791
- let update;
792
- let retry;
793
- const set = (next) => {
794
- value = next;
795
- if (retry) {
796
- retry();
797
- retry = undefined;
798
- }
799
- else
800
- refresh();
801
- };
802
- if (value instanceof Promise) {
803
- value.then(set);
804
- value = undefined;
671
+ catch (err) {
672
+ throw error = err;
673
+ }
674
+ if (output instanceof Promise) {
675
+ const issue = Oops$9.ValueNotReady(subject, key);
676
+ output
677
+ .catch(err => error = err)
678
+ .then(out => state[key] = out)
679
+ .finally(() => waiting = undefined);
680
+ throw waiting = Object.assign(output, {
681
+ message: issue.message,
682
+ stack: issue.stack
683
+ });
805
684
  }
806
685
  else {
807
- sub.commit();
808
- update = () => {
809
- const next = compute.call(spy, spy);
810
- if (value != next)
811
- set(next);
812
- };
686
+ return state[key] = output;
813
687
  }
814
- const get = expected
815
- ? () => {
816
- if (value === undefined)
817
- throw new Promise(res => retry = res);
818
- return value;
819
- }
820
- : () => value;
821
- defineProperty(sub, "proxy", { get });
822
- return sub;
823
- });
824
- React__default["default"].useLayoutEffect(() => hook.release, []);
825
- return hook.proxy;
688
+ };
826
689
  }
827
- function useModel(Type, callback) {
828
- const hook = use(refresh => {
829
- const instance = new Type();
830
- const sub = new Subscriber(instance, () => refresh);
831
- const release = sub.parent.addListener((key) => {
832
- if (key == Lifecycle.WILL_UNMOUNT)
690
+ function suspend(source, key) {
691
+ const error = Oops$9.ValueNotReady(source.subject, key);
692
+ const promise = new Promise(resolve => {
693
+ const release = source.addListener(forKey => {
694
+ if (forKey == key)
833
695
  return () => {
834
- instance.destroy();
835
- release();
836
- };
696
+ if (source.state[key] !== undefined) {
697
+ release();
698
+ resolve();
699
+ }
700
+ };
837
701
  });
838
- if (callback)
839
- callback(instance);
840
- return sub;
841
702
  });
842
- useComponentLifecycle(hook);
843
- usePeerContext(hook.source);
844
- return hook.proxy;
703
+ return Object.assign(promise, {
704
+ toString: () => String(error),
705
+ name: "Suspense",
706
+ message: error.message,
707
+ stack: error.stack
708
+ });
709
+ }
710
+
711
+ const Oops$8 = issues({
712
+ BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
713
+ PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
714
+ });
715
+ function from(source, setter, arg) {
716
+ return apply(function from(key) {
717
+ const { subject } = this;
718
+ let getSource;
719
+ let getter;
720
+ if (arg === true || setter === true)
721
+ getter = pendingValue;
722
+ if (typeof setter == "boolean")
723
+ setter = undefined;
724
+ // Easy mistake, using a peer, will always be unresolved.
725
+ if (typeof source == "symbol")
726
+ throw Oops$8.PeerNotAllowed(subject, key);
727
+ // replace source controller in-case it is different
728
+ if (typeof source == "object")
729
+ getSource = () => control(source);
730
+ // specifically an arrow function (getter factory)
731
+ else if (!source.prototype) {
732
+ setter = source.call(subject, key);
733
+ getSource = () => this;
734
+ }
735
+ // Regular function is too ambiguous so not allowed.
736
+ else
737
+ throw Oops$8.BadComputedSource(subject, key, source);
738
+ return prepare(this, key, getSource, setter, getter);
739
+ });
740
+ }
741
+
742
+ function ref(arg) {
743
+ return apply(function ref(key) {
744
+ let value = {};
745
+ if (typeof arg == "object") {
746
+ const source = control(arg);
747
+ for (const key in source.state)
748
+ defineLazy(value, key, createRef.bind(source, key));
749
+ }
750
+ else
751
+ value = createRef.call(this, key, arg);
752
+ return { value, explicit: true };
753
+ });
754
+ }
755
+ function createRef(key, cb) {
756
+ const refObjectFunction = this.ref(key, cb && createValueEffect(cb));
757
+ defineProperty(refObjectFunction, "current", {
758
+ set: refObjectFunction,
759
+ get: () => this.state[key]
760
+ });
761
+ return refObjectFunction;
762
+ }
763
+
764
+ const Oops$7 = issues({
765
+ NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
766
+ BadFactory: () => `Set instruction can only accept a factory or undefined.`,
767
+ FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
768
+ });
769
+ function set(factory, argument) {
770
+ return apply(function set(key) {
771
+ let set;
772
+ let get;
773
+ const required = argument === true || argument === undefined;
774
+ if (factory === undefined)
775
+ get = () => pendingValue(this, key);
776
+ else if (typeof factory !== "function")
777
+ throw Oops$7.BadFactory();
778
+ else {
779
+ get = pendingFactory(this, key, factory);
780
+ if (required)
781
+ try {
782
+ get();
783
+ }
784
+ catch (err) {
785
+ if (err instanceof Promise)
786
+ ;
787
+ else {
788
+ Oops$7.FactoryFailed(this.subject, key).warn();
789
+ throw err;
790
+ }
791
+ }
792
+ }
793
+ if (typeof argument == "function")
794
+ set = createValueEffect(argument);
795
+ else
796
+ set = (value) => {
797
+ if (value === undefined && required)
798
+ throw Oops$7.NonOptional(this.subject, key);
799
+ };
800
+ return { set, get };
801
+ });
802
+ }
803
+
804
+ const Parent = new WeakMap();
805
+ const Oops$6 = issues({
806
+ ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
807
+ UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
808
+ });
809
+ function parent(Expects, required) {
810
+ return apply(function parent() {
811
+ const child = this.subject;
812
+ const value = Parent.get(child);
813
+ const expected = Expects.name;
814
+ if (!value) {
815
+ if (required !== false)
816
+ throw Oops$6.ParentRequired(expected, child);
817
+ }
818
+ else if (!(value instanceof Expects))
819
+ throw Oops$6.UnexpectedParent(expected, child, value);
820
+ return { value };
821
+ });
822
+ }
823
+
824
+ const Oops$5 = issues({
825
+ BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
826
+ });
827
+ function use$1(input, argument) {
828
+ return apply(function use(key) {
829
+ const { subject } = this;
830
+ const onUpdate = (next) => {
831
+ this.state[key] = next;
832
+ if (next) {
833
+ Parent.set(next, subject);
834
+ control(next);
835
+ }
836
+ if (typeof argument == "function")
837
+ argument(next);
838
+ return true;
839
+ };
840
+ if (input) {
841
+ const value = Model.isTypeof(input)
842
+ ? new input() :
843
+ typeof input === "object"
844
+ ? input :
845
+ typeof input === "function"
846
+ ? input()
847
+ : null;
848
+ if (value === null)
849
+ throw Oops$5.BadArgument(typeof input);
850
+ if (value)
851
+ onUpdate(value);
852
+ }
853
+ return {
854
+ set: onUpdate,
855
+ recursive: true
856
+ };
857
+ });
845
858
  }
846
859
 
847
860
  class Lookup {
@@ -899,113 +912,56 @@ class Lookup {
899
912
  pop() {
900
913
  for (const key of getOwnPropertySymbols(this)) {
901
914
  const entry = getOwnPropertyDescriptor(this, key);
902
- if (entry.writable)
915
+ if (entry.writable && entry.value)
903
916
  entry.value.destroy();
904
917
  }
905
918
  }
906
919
  }
907
920
 
908
- const Oops$5 = issues({
909
- NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`,
910
- NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`,
911
- BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
912
- });
913
921
  const LookupContext = React__default["default"].createContext(new Lookup());
914
922
  const useLookup = () => React__default["default"].useContext(LookupContext);
915
- function useContext(Type, strict) {
923
+ const Oops$4 = issues({
924
+ NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
925
+ });
926
+ function useInContext(Type, arg) {
916
927
  const instance = useLookup().get(Type);
917
- if (!instance && strict)
918
- throw Oops$5.NothingInContext(Type.name);
919
- return instance;
920
- }
921
- function Consumer(props) {
922
- const { get, has, children, of: Control } = props;
923
- if (typeof children == "function")
924
- return children(useTap(useContext(Control)));
925
- const callback = has || get;
926
- if (typeof callback == "function")
927
- callback(useContext(Control, !!has));
928
- else
929
- throw Oops$5.BadConsumerProps();
930
- return null;
931
- }
932
- function useNewContext(inject) {
933
- const from = useLookup();
934
- return React__default["default"].useMemo(() => {
935
- if (!inject)
936
- throw Oops$5.NoProviderType();
937
- const context = from.push(inject);
938
- for (const instance of context.local)
939
- if (instance)
940
- for (const apply of getPending(instance))
941
- apply(context);
942
- return context;
943
- }, []);
944
- }
945
- function useAppliedProps(within, props) {
946
- const update = React__default["default"].useMemo(() => {
947
- const targets = within.local;
948
- return function integrate(props) {
949
- for (const [key, value] of entries(props))
950
- if (key != "of" && key != "children")
951
- for (const into of targets)
952
- if (into && key in into)
953
- into[key] = value;
954
- };
955
- }, []);
956
- update(props);
957
- }
958
- function RenderFunction(props) {
959
- const hook = use(refresh => {
960
- const targets = props.context.local;
961
- if (targets.length == 1)
962
- return new Subscriber(targets[0], () => refresh);
963
- return {};
964
- });
965
- if (hook.commit)
966
- React__default["default"].useLayoutEffect(() => hook.commit(), []);
967
- return props.render(hook.proxy);
968
- }
969
- function Provider(props) {
970
- const context = useNewContext(props.of);
971
- const render = props.children;
972
- useAppliedProps(context, props);
973
- React__default["default"].useLayoutEffect(() => () => context.pop(), []);
974
- return React__default["default"].createElement(LookupContext.Provider, { value: context }, typeof render == "function"
975
- ? React__default["default"].createElement(RenderFunction, { context, render })
976
- : render);
928
+ if (!instance && arg !== false)
929
+ throw Oops$4.NothingInContext(Type.name);
930
+ return typeof arg == "string" ?
931
+ instance[arg] :
932
+ instance;
977
933
  }
978
934
 
979
- const Oops$4 = issues({
980
- CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
935
+ const Oops$3 = issues({
936
+ CantAttachGlobal: (parent, child) => `Global '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
981
937
  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.`
982
938
  });
983
939
  const PendingContext = new WeakMap();
984
940
  const ContextWasUsed = new WeakMap();
985
941
  function tap(type, argument) {
986
- return child(function tap(key) {
942
+ return apply(function tap(key) {
943
+ if ("set" in type)
944
+ this.state[key] = type.get();
945
+ else {
946
+ const { subject } = this;
947
+ if ("set" in subject.constructor)
948
+ throw Oops$3.CantAttachGlobal(subject, type.name);
949
+ getPending(subject).push(context => {
950
+ let instance = context.get(type);
951
+ if (typeof argument == "function") {
952
+ if (argument(instance) === false)
953
+ instance = undefined;
954
+ }
955
+ else if (!instance && argument)
956
+ throw Oops$3.AmbientRequired(type.name, subject, key);
957
+ this.update(key, instance);
958
+ });
959
+ }
987
960
  return {
988
- get: pendingAccess(this.subject, type, key, argument)
961
+ recursive: true
989
962
  };
990
963
  });
991
964
  }
992
- function pendingAccess(from, type, key, argument) {
993
- if ("set" in type)
994
- return () => type.get();
995
- if ("set" in from.constructor)
996
- throw Oops$4.CantAttachGlobal(from, type.name);
997
- let instance;
998
- getPending(from).push(context => {
999
- instance = context.get(type);
1000
- if (typeof argument == "function") {
1001
- if (argument(instance) === false)
1002
- instance = undefined;
1003
- }
1004
- else if (!instance && argument)
1005
- throw Oops$4.AmbientRequired(type.name, from, key);
1006
- });
1007
- return () => instance;
1008
- }
1009
965
  function usePeerContext(subject) {
1010
966
  if (ContextWasUsed.has(subject)) {
1011
967
  if (ContextWasUsed.get(subject))
@@ -1028,268 +984,242 @@ function getPending(subject) {
1028
984
  return pending;
1029
985
  }
1030
986
 
1031
- const Oops$3 = issues({
1032
- DuplicateAction: (key) => `Invoked action ${key} but one is already active.`,
1033
- BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
1034
- PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
1035
- });
1036
- function createRef(key, cb) {
1037
- const refObjectFunction = this.setter(key, cb && createValueEffect(cb));
1038
- defineProperty(refObjectFunction, "current", {
1039
- set: refObjectFunction,
1040
- get: () => this.state[key]
1041
- });
1042
- return refObjectFunction;
1043
- }
1044
- function ref(arg) {
1045
- return apply(function ref(key) {
1046
- let value = {};
1047
- if (typeof arg == "object") {
1048
- const source = getController(arg);
1049
- for (const key in source.state)
1050
- defineLazy(value, key, createRef.bind(source, key));
1051
- }
1052
- else
1053
- value = createRef.call(this, key, arg);
1054
- return { value };
1055
- });
1056
- }
1057
- function act(task) {
1058
- return apply(function act(key) {
1059
- let pending = false;
1060
- const invoke = (...args) => {
1061
- if (pending)
1062
- return Promise.reject(Oops$3.DuplicateAction(key));
1063
- pending = true;
1064
- this.update(key);
1065
- return new Promise(res => {
1066
- res(task.apply(this.subject, args));
1067
- }).finally(() => {
1068
- pending = false;
1069
- this.update(key);
1070
- });
1071
- };
1072
- this.state[key] = undefined;
1073
- setAlias(invoke, `run ${key}`);
1074
- defineProperty(invoke, "active", {
1075
- get: () => pending
987
+ function use(init) {
988
+ const [state, update] = React__default["default"].useState(() => [
989
+ init(() => update(state.concat()))
990
+ ]);
991
+ return state[0];
992
+ }
993
+
994
+ function useModel(source, arg, callback) {
995
+ const instance = React__default["default"].useMemo(() => {
996
+ const cb = callback || arg;
997
+ const instance = typeof source == "function" ?
998
+ source.prototype instanceof Model ?
999
+ new source() :
1000
+ source() :
1001
+ source;
1002
+ control(instance);
1003
+ if (typeof cb == "function")
1004
+ cb(instance);
1005
+ return instance;
1006
+ }, []);
1007
+ if (Array.isArray(arg)) {
1008
+ const update = React.useState(0)[1];
1009
+ React__default["default"].useLayoutEffect(() => {
1010
+ if (arg.length && instance instanceof Model)
1011
+ instance.on(arg, () => update(x => x + 1), true);
1012
+ return () => {
1013
+ if (Model.isTypeof(source))
1014
+ instance.destroy();
1015
+ };
1016
+ }, []);
1017
+ return instance;
1018
+ }
1019
+ else {
1020
+ const local = use(refresh => {
1021
+ return new Subscriber(instance, () => refresh);
1076
1022
  });
1077
- return {
1078
- value: invoke,
1079
- writable: false
1080
- };
1081
- });
1082
- }
1083
- function from(source, setter, argument) {
1084
- return apply(function from(key) {
1085
- let getSource;
1086
- const { subject } = this;
1087
- const getter = argument === true
1088
- ? pendingValue
1089
- : argument || undefined;
1090
- // Easy mistake, using a peer, will always be unresolved.
1091
- if (typeof source == "symbol")
1092
- throw Oops$3.PeerNotAllowed(subject, key);
1093
- // replace source controller in-case different
1094
- if (typeof source == "object")
1095
- getSource = () => getController(source);
1096
- // specifically an arrow function (getter factory)
1097
- else if (!source.prototype) {
1098
- setter = source.call(subject, key);
1099
- getSource = () => this;
1100
- }
1101
- // is a peer Model (constructor)
1102
- else if (Model.isTypeof(source))
1103
- getSource = pendingAccess(subject, source, key, true);
1104
- // Regular function is too ambiguous so not allowed.
1105
- else
1106
- throw Oops$3.BadComputedSource(subject, key, source);
1107
- prepare(this, key, getSource, setter, getter);
1108
- });
1023
+ React__default["default"].useLayoutEffect(() => {
1024
+ local.commit();
1025
+ return () => {
1026
+ local.release();
1027
+ if (Model.isTypeof(source))
1028
+ instance.destroy();
1029
+ };
1030
+ }, []);
1031
+ return local.proxy;
1032
+ }
1109
1033
  }
1110
1034
 
1111
- const Oops$2 = issues({
1112
- ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
1113
- });
1114
- /* deprecated - ignore coverage */
1115
- /* istanbul ignore next */
1116
- function pending(source, compute) {
1117
- if (source === undefined)
1118
- return set();
1119
- if (typeof source == "function" && !source.prototype)
1120
- return set(source);
1121
- return from(source, compute, true);
1122
- }
1123
- /**
1124
- * Get value, suspend instead if undefined.
1125
- *
1126
- * Throws suspense promise also interpretable as an error.
1127
- * React could handle it but other contexts probably not.
1128
- */
1129
- function pendingValue(via, key) {
1130
- const value = via.state[key];
1131
- if (value === undefined)
1132
- throw suspend(via, key);
1133
- return value;
1134
- }
1135
- function pendingFactory(via, key, fn) {
1136
- const { subject, state } = via;
1137
- let waiting;
1138
- let error;
1139
- return () => {
1140
- if (waiting)
1141
- throw waiting;
1142
- if (error)
1143
- throw error;
1144
- if (key in state)
1145
- return state[key];
1146
- let output;
1147
- try {
1148
- output = fn.call(subject, key, subject);
1149
- }
1150
- catch (err) {
1151
- throw error = err;
1152
- }
1153
- if (output instanceof Promise) {
1154
- const issue = Oops$2.ValueNotReady(subject, key);
1155
- output
1156
- .catch(err => error = err)
1157
- .then(out => state[key] = out)
1158
- .finally(() => waiting = undefined);
1159
- throw waiting = Object.assign(output, {
1160
- message: issue.message,
1161
- stack: issue.stack
1162
- });
1035
+ function useFrom(target, compute, suspend) {
1036
+ const local = use(refresh => {
1037
+ const sub = new Subscriber(target, () => update);
1038
+ const spy = sub.proxy;
1039
+ let value = compute.call(spy, spy);
1040
+ let update;
1041
+ let retry;
1042
+ const set = (next) => {
1043
+ value = next;
1044
+ if (retry) {
1045
+ retry();
1046
+ retry = undefined;
1047
+ }
1048
+ else
1049
+ refresh();
1050
+ };
1051
+ if (value instanceof Promise) {
1052
+ value.then(set);
1053
+ value = undefined;
1163
1054
  }
1164
1055
  else {
1165
- return state[key] = output;
1056
+ sub.commit();
1057
+ update = () => {
1058
+ const next = compute.call(spy, spy);
1059
+ if (value != next)
1060
+ set(next);
1061
+ };
1166
1062
  }
1167
- };
1168
- }
1169
- function suspend(source, key) {
1170
- const error = Oops$2.ValueNotReady(source.subject, key);
1171
- const promise = new Promise(resolve => {
1172
- const release = source.addListener(forKey => {
1173
- if (forKey == key)
1174
- return () => {
1175
- if (source.state[key] !== undefined) {
1176
- release();
1177
- resolve();
1178
- }
1179
- };
1063
+ defineProperty(sub, "proxy", {
1064
+ get() {
1065
+ if (value === undefined && suspend)
1066
+ throw new Promise(res => retry = res);
1067
+ return value;
1068
+ }
1180
1069
  });
1070
+ return sub;
1181
1071
  });
1182
- return Object.assign(promise, {
1183
- toString: () => String(error),
1184
- name: "Suspense",
1185
- message: error.message,
1186
- stack: error.stack
1187
- });
1072
+ React__default["default"].useLayoutEffect(() => local.release, []);
1073
+ return local.proxy;
1188
1074
  }
1189
1075
 
1190
- const Oops$1 = issues({
1191
- NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
1192
- BadFactory: () => `Set instruction can only accept a factory or undefined.`,
1193
- FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
1194
- });
1195
- function lazy(value) {
1196
- return apply(function lazy(key) {
1197
- const source = this.subject;
1198
- source[key] = value;
1199
- defineProperty(this.state, key, {
1200
- get: () => source[key]
1201
- });
1202
- });
1203
- }
1204
- function set(factory, argument) {
1205
- return apply(function set(key) {
1206
- let set;
1207
- let get;
1208
- const required = argument === true || argument === undefined;
1209
- if (factory === undefined)
1210
- get = () => pendingValue(this, key);
1211
- else if (typeof factory !== "function")
1212
- throw Oops$1.BadFactory();
1213
- else {
1214
- get = pendingFactory(this, key, factory);
1215
- if (required)
1216
- try {
1217
- get();
1218
- }
1219
- catch (err) {
1220
- if (err instanceof Promise)
1221
- ;
1222
- else {
1223
- Oops$1.FactoryFailed(this.subject, key).warn();
1224
- throw err;
1225
- }
1076
+ function useTap(source, path, expect) {
1077
+ const instance = React.useMemo(() => {
1078
+ if (typeof source == "object")
1079
+ return () => source;
1080
+ if ("get" in source)
1081
+ return () => source.get();
1082
+ if ("prototype" in source)
1083
+ return () => useInContext(source);
1084
+ else
1085
+ return source;
1086
+ }, [])();
1087
+ if (typeof path == "function")
1088
+ return useFrom(instance, path, expect);
1089
+ const local = use(refresh => {
1090
+ const parent = control(instance);
1091
+ const sub = new Subscriber(parent, () => refresh);
1092
+ if (typeof path == "string") {
1093
+ const source = sub.proxy;
1094
+ defineProperty(sub, "proxy", {
1095
+ get() {
1096
+ const value = source[path];
1097
+ if (value === undefined && expect)
1098
+ throw suspend(parent, path);
1099
+ return value;
1226
1100
  }
1101
+ });
1227
1102
  }
1228
- if (typeof argument == "function")
1229
- set = createValueEffect(argument);
1230
- else
1231
- set = (value) => {
1232
- if (value === undefined && required)
1233
- throw Oops$1.NonOptional(this.subject, key);
1234
- };
1235
- return { set, get };
1103
+ return sub;
1236
1104
  });
1237
- }
1238
- function on(initial, onUpdate) {
1239
- return set(initial && (() => initial), onUpdate);
1105
+ React__default["default"].useLayoutEffect(() => local.commit(), []);
1106
+ return local.proxy;
1240
1107
  }
1241
1108
 
1242
1109
  class MVC extends Model {
1243
1110
  tap(path, expect) {
1244
1111
  return useTap(this, path, expect);
1245
1112
  }
1246
- tag(id) {
1247
- return useTag(this, id);
1113
+ use(arg, callback) {
1114
+ return useModel(this, arg, callback);
1248
1115
  }
1249
- use(callback) {
1250
- return useNew(this, callback);
1251
- }
1252
- static new(callback) {
1253
- return usePassive(this, callback);
1254
- }
1255
- static get(key) {
1256
- const instance = useContext(this, key !== false);
1257
- return (typeof key == "string" ?
1258
- instance[key] :
1259
- instance);
1116
+ static get(required) {
1117
+ return useInContext(this, required);
1260
1118
  }
1261
1119
  static tap(key, expect) {
1262
- return this.get().tap(key, expect);
1120
+ return useTap(this, key, expect);
1263
1121
  }
1264
- static tag(id) {
1265
- return this.get().tag(id);
1122
+ /**
1123
+ * **React Hook** - Spawn and maintain a controller from within a component.
1124
+ *
1125
+ * More efficient than `use()` if you don't need hook-based features.
1126
+ *
1127
+ * @param callback - Run after creation of instance.
1128
+ */
1129
+ static new(callback) {
1130
+ return useModel(this, [], callback);
1266
1131
  }
1267
- static use(callback) {
1268
- return useModel(this, callback);
1132
+ static use(arg, callback) {
1133
+ const instance = useModel(this, arg, callback);
1134
+ usePeerContext(instance.get);
1135
+ return instance;
1269
1136
  }
1270
- static uses(props, only) {
1137
+ static uses(data, only) {
1271
1138
  return this.use(instance => {
1272
- instance.import(props, only);
1139
+ instance.import(data, only);
1273
1140
  });
1274
1141
  }
1275
- static using(props, only) {
1142
+ static using(data, only) {
1276
1143
  const instance = this.use();
1277
- instance.import(props, only);
1144
+ instance.import(data, only);
1278
1145
  return instance;
1279
1146
  }
1280
1147
  static meta(path, expect) {
1281
- return typeof path == "function"
1282
- ? useComputed(this, path, expect)
1283
- : useActive(this, path, expect);
1148
+ return useTap(() => this, path, expect);
1284
1149
  }
1285
1150
  }
1286
1151
 
1152
+ const Oops$2 = issues({
1153
+ BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
1154
+ });
1155
+ function Consumer(props) {
1156
+ const { get, has, children, of: Control } = props;
1157
+ if (typeof children == "function")
1158
+ return children(useTap(Control));
1159
+ const callback = has || get;
1160
+ if (typeof callback == "function")
1161
+ callback(useInContext(Control, !!has));
1162
+ else
1163
+ throw Oops$2.BadConsumerProps();
1164
+ return null;
1165
+ }
1166
+
1167
+ const Oops$1 = issues({
1168
+ NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`
1169
+ });
1170
+ function Provider(props) {
1171
+ const context = useNewContext(props.of);
1172
+ const render = props.children;
1173
+ useAppliedProps(context, props);
1174
+ React__default["default"].useLayoutEffect(() => () => context.pop(), []);
1175
+ return React__default["default"].createElement(LookupContext.Provider, { value: context }, typeof render == "function"
1176
+ ? React__default["default"].createElement(RenderFunction, { context, render })
1177
+ : render);
1178
+ }
1179
+ function useNewContext(inject) {
1180
+ const from = useLookup();
1181
+ return React__default["default"].useMemo(() => {
1182
+ if (!inject)
1183
+ throw Oops$1.NoProviderType();
1184
+ const context = from.push(inject);
1185
+ for (const instance of context.local)
1186
+ if (instance)
1187
+ for (const apply of getPending(instance))
1188
+ apply(context);
1189
+ return context;
1190
+ }, []);
1191
+ }
1192
+ function useAppliedProps(within, props) {
1193
+ const update = React__default["default"].useMemo(() => {
1194
+ const targets = within.local;
1195
+ return function integrate(props) {
1196
+ for (const [key, value] of entries(props))
1197
+ if (key != "of" && key != "children")
1198
+ for (const into of targets)
1199
+ if (into && key in into)
1200
+ into[key] = value;
1201
+ };
1202
+ }, []);
1203
+ update(props);
1204
+ }
1205
+ function RenderFunction(props) {
1206
+ const hook = use(refresh => {
1207
+ const targets = props.context.local;
1208
+ if (targets.length == 1)
1209
+ return new Subscriber(targets[0], () => refresh);
1210
+ return {};
1211
+ });
1212
+ if (hook.commit)
1213
+ React__default["default"].useLayoutEffect(() => hook.commit(), []);
1214
+ return props.render(hook.proxy);
1215
+ }
1216
+
1287
1217
  const Oops = issues({
1288
1218
  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.`,
1289
1219
  GlobalExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
1290
1220
  });
1291
1221
  const Active = new WeakMap();
1292
- class Singleton extends MVC {
1222
+ class Global extends MVC {
1293
1223
  static create(...args) {
1294
1224
  const Type = this;
1295
1225
  if (Active.has(Type))
@@ -1298,6 +1228,11 @@ class Singleton extends MVC {
1298
1228
  Active.set(Type, instance);
1299
1229
  return instance;
1300
1230
  }
1231
+ /**
1232
+ * Update the active instance of this class.
1233
+ * Returns a thenable; resolves after successful update.
1234
+ * If instance does not already exist, one will be created.
1235
+ */
1301
1236
  static set(updates) {
1302
1237
  const instance = Active.get(this) || this.create();
1303
1238
  instance.import(updates);
@@ -1311,6 +1246,7 @@ class Singleton extends MVC {
1311
1246
  instance[arg] :
1312
1247
  instance);
1313
1248
  }
1249
+ /** Destroy current instance of Global, if it exists. */
1314
1250
  static reset() {
1315
1251
  const current = Active.get(this);
1316
1252
  if (current)
@@ -1322,22 +1258,25 @@ class Singleton extends MVC {
1322
1258
  }
1323
1259
  }
1324
1260
 
1261
+ exports.CONTROL = CONTROL;
1325
1262
  exports.Consumer = Consumer;
1263
+ exports.Global = Global;
1264
+ exports.LOCAL = LOCAL;
1326
1265
  exports.Model = MVC;
1327
1266
  exports.Provider = Provider;
1328
- exports.Singleton = Singleton;
1267
+ exports.STATE = STATE;
1268
+ exports.WHY = WHY;
1329
1269
  exports.act = act;
1330
1270
  exports.apply = apply;
1331
- exports.child = child;
1332
1271
  exports["default"] = MVC;
1333
1272
  exports.from = from;
1334
- exports.lazy = lazy;
1335
- exports.memo = set;
1336
- exports.on = on;
1337
1273
  exports.parent = parent;
1338
- exports.pending = pending;
1339
1274
  exports.ref = ref;
1340
1275
  exports.set = set;
1341
1276
  exports.tap = tap;
1342
1277
  exports.use = use$1;
1278
+ exports.useFrom = useFrom;
1279
+ exports.useInContext = useInContext;
1280
+ exports.useModel = useModel;
1281
+ exports.useTap = useTap;
1343
1282
  //# sourceMappingURL=index.js.map