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

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,94 @@ 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
- });
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 === false)
125
+ return;
126
+ if (typeof desc == "object") {
127
+ if ("value" in desc)
128
+ state[key] = desc.value;
129
+ onSet = desc.set;
130
+ onGet = desc.get;
131
+ enumerable = desc.enumerable;
142
132
  }
143
133
  }
134
+ else
135
+ state[key] = entry;
136
+ const set = onSet !== false
137
+ ? this.ref(key, onSet)
138
+ : undefined;
139
+ function get() {
140
+ const local = this[LOCAL];
141
+ const value = state[key];
142
+ if (local && !local.watch[key])
143
+ local.watch[key] = true;
144
+ return onGet ? onGet(value, local) : value;
145
+ }
146
+ setAlias(get, `tap ${key}`);
147
+ defineProperty(subject, key, {
148
+ enumerable,
149
+ set,
150
+ get: onGet
151
+ ? () => onGet(state[key])
152
+ : () => state[key]
153
+ });
154
+ defineProperty(proxy, key, {
155
+ enumerable,
156
+ set,
157
+ get
158
+ });
144
159
  }
145
- setter(key, handler) {
160
+ ref(key, handler) {
146
161
  const { state, subject } = this;
147
162
  return (value) => {
148
163
  if (state[key] == value)
@@ -163,6 +178,18 @@ class Controller {
163
178
  this.followers.delete(listener);
164
179
  };
165
180
  }
181
+ emit(keys) {
182
+ const waiting = [...this.waiting];
183
+ this.waiting.clear();
184
+ applyUpdate(this.subject, keys)();
185
+ for (const callback of waiting)
186
+ try {
187
+ callback(keys);
188
+ }
189
+ catch (err) {
190
+ console.error(err);
191
+ }
192
+ }
166
193
  update(key, value) {
167
194
  if (1 in arguments)
168
195
  this.state[key] = value;
@@ -172,19 +199,8 @@ class Controller {
172
199
  setTimeout(() => {
173
200
  flush(this);
174
201
  const keys = Object.freeze([...this.frame]);
175
- const handle = new Set(this.waiting);
176
- this.waiting.clear();
177
202
  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
- });
203
+ this.emit(keys);
188
204
  }, 0);
189
205
  this.frame.add(key);
190
206
  for (const callback of this.followers) {
@@ -199,7 +215,7 @@ class Controller {
199
215
  return;
200
216
  }
201
217
  if (typeof arg == "boolean" && arg !== this.frame.size > 0)
202
- return Promise.reject(Oops$8.StrictUpdate(arg));
218
+ return Promise.reject(Oops$c.StrictUpdate(arg));
203
219
  return {
204
220
  then: (callback) => {
205
221
  if (callback)
@@ -208,76 +224,82 @@ class Controller {
208
224
  else
209
225
  callback(false);
210
226
  else
211
- throw Oops$8.NoChaining();
227
+ throw Oops$c.NoChaining();
212
228
  }
213
229
  };
214
230
  }
231
+ }
232
+ function control(subject, cb) {
233
+ let control = subject[CONTROL];
234
+ const ready = READY.has(control);
235
+ if (!control) {
236
+ control = new Controller(subject);
237
+ define(subject, CONTROL, control);
238
+ }
239
+ if (!cb) {
240
+ if (!ready) {
241
+ READY.add(control);
242
+ control.start();
243
+ }
244
+ return control;
245
+ }
246
+ if (!ready) {
247
+ let done;
248
+ control.requestUpdate(() => {
249
+ done = cb(control);
250
+ });
251
+ return () => done && done();
252
+ }
253
+ return cb(control);
215
254
  }
216
255
 
217
256
  class Subscriber {
218
- constructor(parent, onUpdate) {
257
+ constructor(target, onUpdate) {
219
258
  this.onUpdate = onUpdate;
220
259
  this.active = false;
221
- this.watch = {};
222
260
  this.dependant = new Set();
223
- 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
+ this.watch = {};
262
+ const parent = target instanceof Controller
263
+ ? target : control(target);
264
+ const proxy = create(parent.proxy);
265
+ let reset;
231
266
  define(proxy, LOCAL, this);
232
267
  defineProperty(this, "proxy", {
233
- get: () => {
234
- if (UPDATE.has(proxy))
235
- setTimeout(() => {
236
- UPDATE.delete(proxy);
237
- }, 0);
268
+ configurable: true,
269
+ get() {
270
+ if (reset)
271
+ setTimeout(reset, 0);
238
272
  return proxy;
239
273
  }
240
274
  });
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];
275
+ const release = parent.addListener(key => {
276
+ const handler = this.watch[key];
277
+ if (!handler || !this.active)
278
+ return;
279
+ if (typeof handler == "function")
280
+ handler();
281
+ const notify = this.onUpdate(key, parent);
282
+ const getWhy = (keys) => {
283
+ reset = applyUpdate(proxy, keys.filter(k => k in this.watch));
247
284
  };
248
- setAlias(isUsing, `tap ${key}`);
249
- defineProperty(proxy, key, {
250
- set: existing.set,
251
- get: isUsing,
252
- configurable: true,
253
- enumerable: true
254
- });
255
- }
285
+ if (notify) {
286
+ parent.requestUpdate(getWhy);
287
+ parent.requestUpdate(notify);
288
+ }
289
+ });
256
290
  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
- });
270
291
  this.active = true;
271
292
  this.dependant.forEach(x => x.commit());
272
- return this.release = () => {
273
- this.dependant.forEach(x => x.release());
274
- onDone();
275
- };
293
+ return this.release;
294
+ };
295
+ this.release = () => {
296
+ this.dependant.forEach(x => x.release());
297
+ release();
276
298
  };
277
299
  }
278
300
  }
279
301
 
280
- const Oops$7 = issues({
302
+ const Oops$b = issues({
281
303
  ComputeFailed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
282
304
  ComputedEarly: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
283
305
  });
@@ -295,15 +317,12 @@ function prepare(parent, key, source, setter, getter) {
295
317
  USED.set(parent, register);
296
318
  }
297
319
  register.set(key, info);
298
- const current = getter
299
- ? () => getter(parent, key)
300
- : () => state[key];
301
320
  function compute(initial) {
302
321
  try {
303
322
  return setter.call(sub.proxy, sub.proxy);
304
323
  }
305
324
  catch (err) {
306
- Oops$7.ComputeFailed(subject, key, !!initial).warn();
325
+ Oops$b.ComputeFailed(subject, key, !!initial).warn();
307
326
  throw err;
308
327
  }
309
328
  }
@@ -339,17 +358,12 @@ function prepare(parent, key, source, setter, getter) {
339
358
  value: undefined,
340
359
  writable: true
341
360
  });
342
- defineProperty(subject, key, {
343
- enumerable: true,
344
- configurable: true,
345
- get: current
346
- });
347
361
  try {
348
- state[key] = compute(true);
362
+ return state[key] = compute(true);
349
363
  }
350
364
  catch (e) {
351
365
  if (early)
352
- Oops$7.ComputedEarly(key).warn();
366
+ Oops$b.ComputedEarly(key).warn();
353
367
  throw e;
354
368
  }
355
369
  finally {
@@ -360,23 +374,23 @@ function prepare(parent, key, source, setter, getter) {
360
374
  info.priority = peer.priority + 1;
361
375
  }
362
376
  }
363
- return current();
364
377
  }
365
378
  setAlias(update, `try ${key}`);
366
379
  setAlias(create, `new ${key}`);
367
380
  setAlias(setter, `run ${key}`);
368
381
  INIT.add(create);
369
382
  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
- });
383
+ defineProperty(state, key, {
384
+ get: create,
385
+ configurable: true,
386
+ enumerable: true
387
+ });
388
+ if (getter)
389
+ return () => getter(parent, key);
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,301 @@ 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
- if (!output)
551
- return;
552
563
  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
- }
564
+ return { get: output };
565
+ if (typeof output !== "object")
566
+ return output || undefined;
567
+ if ("explicit" in output && output.explicit) {
568
+ defineProperty(this.subject, key, output);
569
+ return false;
565
570
  }
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] }));
571
+ if ("recursive" in output)
572
+ output = Object.assign(Object.assign({}, output), { get: recursive(this, key) });
573
+ return output;
571
574
  }
572
575
  Pending.set(placeholder, setup);
573
576
  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
- }
577
+ }
578
+ function recursive(source, key) {
579
+ const context = new WeakMap();
580
+ const subscribe = (parent) => {
581
+ let child;
582
+ const init = () => {
583
+ if (child) {
584
+ child.release();
585
+ parent.dependant.delete(child);
586
+ context.set(parent, undefined);
587
+ child = undefined;
598
588
  }
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);
589
+ const value = source.state[key];
590
+ if (value && CONTROL in value) {
591
+ child = new Subscriber(value, parent.onUpdate);
592
+ if (parent.active)
593
+ child.commit();
594
+ parent.dependant.add(child);
595
+ context.set(parent, child.proxy);
611
596
  }
612
597
  };
613
- }, name || source.name);
598
+ init();
599
+ parent.watch[key] = init;
600
+ };
601
+ return (value, local) => {
602
+ if (!local)
603
+ return value;
604
+ if (!context.has(local))
605
+ subscribe(local);
606
+ return context.get(local);
607
+ };
614
608
  }
615
609
 
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}.`,
610
+ const Oops$a = issues({
611
+ DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
621
612
  });
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);
613
+ function act(task) {
614
+ return apply(function act(key) {
615
+ let pending = false;
616
+ const invoke = (...args) => {
617
+ if (pending)
618
+ return Promise.reject(Oops$a.DuplicateAction(key));
619
+ pending = true;
620
+ this.update(key);
621
+ return new Promise(res => {
622
+ res(task.apply(this.subject, args));
623
+ }).finally(() => {
624
+ pending = false;
625
+ this.update(key);
626
+ });
644
627
  };
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
- }
628
+ this.state[key] = undefined;
629
+ setAlias(invoke, `run ${key}`);
630
+ defineProperty(invoke, "active", {
631
+ get: () => pending
632
+ });
660
633
  return {
661
- get: () => current,
662
- set: onUpdate
634
+ value: invoke,
635
+ set: false
663
636
  };
664
637
  });
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
638
  }
726
639
 
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;
640
+ const Oops$9 = issues({
641
+ ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`
642
+ });
643
+ /**
644
+ * Get value, suspend instead if undefined.
645
+ *
646
+ * Throws suspense promise also interpretable as an error.
647
+ * React could handle it but other contexts probably not.
648
+ */
649
+ function pendingValue(via, key) {
650
+ const value = via.state[key];
651
+ if (value === undefined)
652
+ throw suspend(via, key);
653
+ return value;
766
654
  }
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
- });
655
+ function pendingFactory(via, key, fn) {
656
+ const { subject, state } = via;
657
+ let waiting;
658
+ let error;
659
+ return () => {
660
+ if (waiting)
661
+ throw waiting;
662
+ if (error)
663
+ throw error;
664
+ if (key in state)
665
+ return state[key];
666
+ let output;
667
+ try {
668
+ output = fn.call(subject, key, subject);
780
669
  }
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;
670
+ catch (err) {
671
+ throw error = err;
672
+ }
673
+ if (output instanceof Promise) {
674
+ const issue = Oops$9.ValueNotReady(subject, key);
675
+ output
676
+ .catch(err => error = err)
677
+ .then(out => state[key] = out)
678
+ .finally(() => waiting = undefined);
679
+ throw waiting = Object.assign(output, {
680
+ message: issue.message,
681
+ stack: issue.stack
682
+ });
805
683
  }
806
684
  else {
807
- sub.commit();
808
- update = () => {
809
- const next = compute.call(spy, spy);
810
- if (value != next)
811
- set(next);
812
- };
685
+ return state[key] = output;
813
686
  }
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;
687
+ };
826
688
  }
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)
689
+ function suspend(source, key) {
690
+ const error = Oops$9.ValueNotReady(source.subject, key);
691
+ const promise = new Promise(resolve => {
692
+ const release = source.addListener(forKey => {
693
+ if (forKey == key)
833
694
  return () => {
834
- instance.destroy();
835
- release();
695
+ if (source.state[key] !== undefined) {
696
+ release();
697
+ resolve();
698
+ }
836
699
  };
837
700
  });
838
- if (callback)
839
- callback(instance);
840
- return sub;
841
701
  });
842
- useComponentLifecycle(hook);
843
- usePeerContext(hook.source);
844
- return hook.proxy;
702
+ return Object.assign(promise, {
703
+ toString: () => String(error),
704
+ name: "Suspense",
705
+ message: error.message,
706
+ stack: error.stack
707
+ });
708
+ }
709
+
710
+ const Oops$8 = issues({
711
+ BadComputedSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
712
+ PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`
713
+ });
714
+ function from(source, setter, arg) {
715
+ return apply(function from(key) {
716
+ const { subject } = this;
717
+ let getSource;
718
+ let getter;
719
+ if (arg === true || setter === true)
720
+ getter = pendingValue;
721
+ if (typeof setter == "boolean")
722
+ setter = undefined;
723
+ // Easy mistake, using a peer, will always be unresolved.
724
+ if (typeof source == "symbol")
725
+ throw Oops$8.PeerNotAllowed(subject, key);
726
+ // replace source controller in-case it is different
727
+ if (typeof source == "object")
728
+ getSource = () => control(source);
729
+ // specifically an arrow function (getter factory)
730
+ else if (!source.prototype) {
731
+ setter = source.call(subject, key);
732
+ getSource = () => this;
733
+ }
734
+ // Regular function is too ambiguous so not allowed.
735
+ else
736
+ throw Oops$8.BadComputedSource(subject, key, source);
737
+ return prepare(this, key, getSource, setter, getter);
738
+ });
739
+ }
740
+
741
+ function ref(arg) {
742
+ return apply(function ref(key) {
743
+ let value = {};
744
+ if (typeof arg == "object") {
745
+ const source = control(arg);
746
+ for (const key in source.state)
747
+ defineLazy(value, key, createRef.bind(source, key));
748
+ }
749
+ else
750
+ value = createRef.call(this, key, arg);
751
+ return { value, explicit: true };
752
+ });
753
+ }
754
+ function createRef(key, cb) {
755
+ const refObjectFunction = this.ref(key, cb && createValueEffect(cb));
756
+ defineProperty(refObjectFunction, "current", {
757
+ set: refObjectFunction,
758
+ get: () => this.state[key]
759
+ });
760
+ return refObjectFunction;
761
+ }
762
+
763
+ const Oops$7 = issues({
764
+ NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
765
+ BadFactory: () => `Set instruction can only accept a factory or undefined.`,
766
+ FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
767
+ });
768
+ function set(factory, argument) {
769
+ return apply(function set(key) {
770
+ let set;
771
+ let get;
772
+ const required = argument === true || argument === undefined;
773
+ if (factory === undefined)
774
+ get = () => pendingValue(this, key);
775
+ else if (typeof factory !== "function")
776
+ throw Oops$7.BadFactory();
777
+ else {
778
+ get = pendingFactory(this, key, factory);
779
+ if (required)
780
+ try {
781
+ get();
782
+ }
783
+ catch (err) {
784
+ if (err instanceof Promise)
785
+ ;
786
+ else {
787
+ Oops$7.FactoryFailed(this.subject, key).warn();
788
+ throw err;
789
+ }
790
+ }
791
+ }
792
+ if (typeof argument == "function")
793
+ set = createValueEffect(argument);
794
+ else
795
+ set = (value) => {
796
+ if (value === undefined && required)
797
+ throw Oops$7.NonOptional(this.subject, key);
798
+ };
799
+ return { set, get };
800
+ });
801
+ }
802
+
803
+ const Parent = new WeakMap();
804
+ const Oops$6 = issues({
805
+ ParentRequired: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
806
+ UnexpectedParent: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
807
+ });
808
+ function parent(Expects, required) {
809
+ return apply(function parent() {
810
+ const child = this.subject;
811
+ const value = Parent.get(child);
812
+ const expected = Expects.name;
813
+ if (!value) {
814
+ if (required !== false)
815
+ throw Oops$6.ParentRequired(expected, child);
816
+ }
817
+ else if (!(value instanceof Expects))
818
+ throw Oops$6.UnexpectedParent(expected, child, value);
819
+ return { value };
820
+ });
821
+ }
822
+
823
+ const Oops$5 = issues({
824
+ BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`,
825
+ });
826
+ function use$1(input, argument) {
827
+ return apply(function use(key) {
828
+ const { subject } = this;
829
+ const onUpdate = (next) => {
830
+ this.state[key] = next;
831
+ if (next) {
832
+ Parent.set(next, subject);
833
+ control(next);
834
+ }
835
+ if (typeof argument == "function")
836
+ argument(next);
837
+ return true;
838
+ };
839
+ if (input) {
840
+ const value = Model.isTypeof(input)
841
+ ? new input() :
842
+ typeof input === "object"
843
+ ? input :
844
+ typeof input === "function"
845
+ ? input()
846
+ : null;
847
+ if (value === null)
848
+ throw Oops$5.BadArgument(typeof input);
849
+ if (value)
850
+ onUpdate(value);
851
+ }
852
+ return {
853
+ set: onUpdate,
854
+ recursive: true
855
+ };
856
+ });
845
857
  }
846
858
 
847
859
  class Lookup {
@@ -899,113 +911,56 @@ class Lookup {
899
911
  pop() {
900
912
  for (const key of getOwnPropertySymbols(this)) {
901
913
  const entry = getOwnPropertyDescriptor(this, key);
902
- if (entry.writable)
914
+ if (entry.writable && entry.value)
903
915
  entry.value.destroy();
904
916
  }
905
917
  }
906
918
  }
907
919
 
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
920
  const LookupContext = React__default["default"].createContext(new Lookup());
914
921
  const useLookup = () => React__default["default"].useContext(LookupContext);
915
- function useContext(Type, strict) {
922
+ const Oops$4 = issues({
923
+ NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
924
+ });
925
+ function useInContext(Type, arg) {
916
926
  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);
927
+ if (!instance && arg !== false)
928
+ throw Oops$4.NothingInContext(Type.name);
929
+ return typeof arg == "string" ?
930
+ instance[arg] :
931
+ instance;
977
932
  }
978
933
 
979
- const Oops$4 = issues({
980
- CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
934
+ const Oops$3 = issues({
935
+ CantAttachGlobal: (parent, child) => `Global '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
981
936
  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
937
  });
983
938
  const PendingContext = new WeakMap();
984
939
  const ContextWasUsed = new WeakMap();
985
940
  function tap(type, argument) {
986
- return child(function tap(key) {
941
+ return apply(function tap(key) {
942
+ if ("set" in type)
943
+ this.state[key] = type.get();
944
+ else {
945
+ const { subject } = this;
946
+ if ("set" in subject.constructor)
947
+ throw Oops$3.CantAttachGlobal(subject, type.name);
948
+ getPending(subject).push(context => {
949
+ let instance = context.get(type);
950
+ if (typeof argument == "function") {
951
+ if (argument(instance) === false)
952
+ instance = undefined;
953
+ }
954
+ else if (!instance && argument)
955
+ throw Oops$3.AmbientRequired(type.name, subject, key);
956
+ this.update(key, instance);
957
+ });
958
+ }
987
959
  return {
988
- get: pendingAccess(this.subject, type, key, argument)
960
+ recursive: true
989
961
  };
990
962
  });
991
963
  }
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
964
  function usePeerContext(subject) {
1010
965
  if (ContextWasUsed.has(subject)) {
1011
966
  if (ContextWasUsed.get(subject))
@@ -1028,268 +983,252 @@ function getPending(subject) {
1028
983
  return pending;
1029
984
  }
1030
985
 
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 = {};
986
+ function use(init) {
987
+ const [state, update] = React__default["default"].useState(() => [
988
+ init(() => update(state.concat()))
989
+ ]);
990
+ return state[0];
991
+ }
992
+
993
+ function useModel(source, arg, arg2) {
994
+ const instance = React__default["default"].useMemo(() => {
995
+ const cb = arg2 || arg;
996
+ const instance = typeof source == "function" ?
997
+ source.prototype instanceof Model ?
998
+ new source() :
999
+ source() :
1000
+ source;
1001
+ control(instance);
1002
+ if (typeof cb == "function")
1003
+ cb(instance);
1004
+ return instance;
1005
+ }, []);
1006
+ if (Array.isArray(arg)) {
1007
+ const update = React.useState(0)[1];
1008
+ React__default["default"].useLayoutEffect(() => {
1009
+ if (arg.length && instance instanceof Model)
1010
+ instance.on(arg, () => update(x => x + 1), true);
1011
+ return () => {
1012
+ if (Model.isTypeof(source))
1013
+ instance.destroy();
1014
+ };
1015
+ }, []);
1016
+ return instance;
1017
+ }
1018
+ else {
1019
+ const local = use(refresh => (new Subscriber(instance, () => refresh)));
1047
1020
  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);
1021
+ local.active = false;
1022
+ if (typeof arg2 !== "object")
1023
+ arg2 = getOwnPropertyNames(instance);
1024
+ for (const key of arg2)
1025
+ if (key in arg)
1026
+ instance[key] = arg[key];
1027
+ React__default["default"].useLayoutEffect(() => {
1028
+ local.active = true;
1070
1029
  });
1071
- };
1072
- this.state[key] = undefined;
1073
- setAlias(invoke, `run ${key}`);
1074
- defineProperty(invoke, "active", {
1075
- get: () => pending
1076
- });
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
1030
  }
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
- });
1031
+ React__default["default"].useLayoutEffect(() => {
1032
+ local.commit();
1033
+ return () => {
1034
+ local.release();
1035
+ if (Model.isTypeof(source))
1036
+ instance.destroy();
1037
+ };
1038
+ }, []);
1039
+ return local.proxy;
1040
+ }
1109
1041
  }
1110
1042
 
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
- });
1043
+ function useFrom(target, compute, suspend) {
1044
+ const local = use(refresh => {
1045
+ const sub = new Subscriber(target, () => update);
1046
+ const spy = sub.proxy;
1047
+ let value = compute.call(spy, spy);
1048
+ let update;
1049
+ let retry;
1050
+ const set = (next) => {
1051
+ value = next;
1052
+ if (retry) {
1053
+ retry();
1054
+ retry = undefined;
1055
+ }
1056
+ else
1057
+ refresh();
1058
+ };
1059
+ if (value instanceof Promise) {
1060
+ value.then(set);
1061
+ value = undefined;
1163
1062
  }
1164
1063
  else {
1165
- return state[key] = output;
1064
+ sub.commit();
1065
+ update = () => {
1066
+ const next = compute.call(spy, spy);
1067
+ if (value != next)
1068
+ set(next);
1069
+ };
1166
1070
  }
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
- };
1071
+ defineProperty(sub, "proxy", {
1072
+ get() {
1073
+ if (value === undefined && suspend)
1074
+ throw new Promise(res => retry = res);
1075
+ return value;
1076
+ }
1180
1077
  });
1078
+ return sub;
1181
1079
  });
1182
- return Object.assign(promise, {
1183
- toString: () => String(error),
1184
- name: "Suspense",
1185
- message: error.message,
1186
- stack: error.stack
1187
- });
1080
+ React__default["default"].useLayoutEffect(() => local.release, []);
1081
+ return local.proxy;
1188
1082
  }
1189
1083
 
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
- }
1084
+ function useTap(source, path, expect) {
1085
+ const instance = React.useMemo(() => {
1086
+ if (typeof source == "object")
1087
+ return () => source;
1088
+ if ("get" in source)
1089
+ return () => source.get();
1090
+ if ("prototype" in source)
1091
+ return () => useInContext(source);
1092
+ else
1093
+ return source;
1094
+ }, [])();
1095
+ if (typeof path == "function")
1096
+ return useFrom(instance, path, expect);
1097
+ const local = use(refresh => {
1098
+ const parent = control(instance);
1099
+ const sub = new Subscriber(parent, () => refresh);
1100
+ if (typeof path == "string") {
1101
+ const source = sub.proxy;
1102
+ defineProperty(sub, "proxy", {
1103
+ get() {
1104
+ const value = source[path];
1105
+ if (value === undefined && expect)
1106
+ throw suspend(parent, path);
1107
+ return value;
1226
1108
  }
1109
+ });
1227
1110
  }
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 };
1111
+ return sub;
1236
1112
  });
1237
- }
1238
- function on(initial, onUpdate) {
1239
- return set(initial && (() => initial), onUpdate);
1113
+ React__default["default"].useLayoutEffect(local.commit, []);
1114
+ return local.proxy;
1240
1115
  }
1241
1116
 
1117
+ /* istanbul ignore file */
1242
1118
  class MVC extends Model {
1243
1119
  tap(path, expect) {
1244
1120
  return useTap(this, path, expect);
1245
1121
  }
1246
- tag(id) {
1247
- return useTag(this, id);
1122
+ use(arg, callback) {
1123
+ return useModel(this, arg, callback);
1248
1124
  }
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);
1125
+ static get(required) {
1126
+ return useInContext(this, required);
1260
1127
  }
1261
1128
  static tap(key, expect) {
1262
- return this.get().tap(key, expect);
1129
+ return useTap(this, key, expect);
1263
1130
  }
1264
- static tag(id) {
1265
- return this.get().tag(id);
1131
+ /**
1132
+ * **React Hook** - Spawn and maintain a controller from within a component.
1133
+ *
1134
+ * More efficient than `use()` if you don't need hook-based features.
1135
+ *
1136
+ * @param callback - Run after creation of instance.
1137
+ */
1138
+ static new(callback) {
1139
+ return useModel(this, [], callback);
1266
1140
  }
1267
- static use(callback) {
1268
- return useModel(this, callback);
1141
+ static use(arg, callback) {
1142
+ const instance = useModel(this, arg, callback);
1143
+ usePeerContext(instance.get);
1144
+ return instance;
1269
1145
  }
1270
- static uses(props, only) {
1146
+ static uses(data, only) {
1271
1147
  return this.use(instance => {
1272
- instance.import(props, only);
1148
+ instance.import(data, only);
1273
1149
  });
1274
1150
  }
1275
- static using(props, only) {
1151
+ static using(data, only) {
1276
1152
  const instance = this.use();
1277
- instance.import(props, only);
1153
+ instance.import(data, only);
1278
1154
  return instance;
1279
1155
  }
1280
1156
  static meta(path, expect) {
1281
- return typeof path == "function"
1282
- ? useComputed(this, path, expect)
1283
- : useActive(this, path, expect);
1157
+ return useTap(() => this, path, expect);
1284
1158
  }
1285
1159
  }
1286
1160
 
1161
+ const Oops$2 = issues({
1162
+ BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
1163
+ });
1164
+ function Consumer(props) {
1165
+ const { get, has, children, of: Control } = props;
1166
+ if (typeof children == "function")
1167
+ return children(useTap(Control));
1168
+ const callback = has || get;
1169
+ if (typeof callback == "function")
1170
+ callback(useInContext(Control, !!has));
1171
+ else
1172
+ throw Oops$2.BadConsumerProps();
1173
+ return null;
1174
+ }
1175
+
1176
+ const Oops$1 = issues({
1177
+ NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`
1178
+ });
1179
+ function Provider(props) {
1180
+ const context = useNewContext(props.of);
1181
+ const render = props.children;
1182
+ useAppliedProps(context, props);
1183
+ React__default["default"].useLayoutEffect(() => () => context.pop(), []);
1184
+ return React__default["default"].createElement(LookupContext.Provider, { value: context }, typeof render == "function"
1185
+ ? React__default["default"].createElement(RenderFunction, { context, render })
1186
+ : render);
1187
+ }
1188
+ function useNewContext(inject) {
1189
+ const from = useLookup();
1190
+ return React__default["default"].useMemo(() => {
1191
+ if (!inject)
1192
+ throw Oops$1.NoProviderType();
1193
+ const context = from.push(inject);
1194
+ for (const instance of context.local)
1195
+ if (instance)
1196
+ for (const apply of getPending(instance))
1197
+ apply(context);
1198
+ return context;
1199
+ }, []);
1200
+ }
1201
+ function useAppliedProps(within, props) {
1202
+ const update = React__default["default"].useMemo(() => {
1203
+ const targets = within.local;
1204
+ return function integrate(props) {
1205
+ for (const [key, value] of entries(props))
1206
+ if (key != "of" && key != "children")
1207
+ for (const into of targets)
1208
+ if (into && key in into)
1209
+ into[key] = value;
1210
+ };
1211
+ }, []);
1212
+ update(props);
1213
+ }
1214
+ function RenderFunction(props) {
1215
+ const hook = use(refresh => {
1216
+ const targets = props.context.local;
1217
+ if (targets.length == 1)
1218
+ return new Subscriber(targets[0], () => refresh);
1219
+ return {};
1220
+ });
1221
+ if (hook.commit)
1222
+ React__default["default"].useLayoutEffect(hook.commit, []);
1223
+ return props.render(hook.proxy);
1224
+ }
1225
+
1287
1226
  const Oops = issues({
1288
1227
  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
1228
  GlobalExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
1290
1229
  });
1291
1230
  const Active = new WeakMap();
1292
- class Singleton extends MVC {
1231
+ class Global extends MVC {
1293
1232
  static create(...args) {
1294
1233
  const Type = this;
1295
1234
  if (Active.has(Type))
@@ -1298,6 +1237,11 @@ class Singleton extends MVC {
1298
1237
  Active.set(Type, instance);
1299
1238
  return instance;
1300
1239
  }
1240
+ /**
1241
+ * Update the active instance of this class.
1242
+ * Returns a thenable; resolves after successful update.
1243
+ * If instance does not already exist, one will be created.
1244
+ */
1301
1245
  static set(updates) {
1302
1246
  const instance = Active.get(this) || this.create();
1303
1247
  instance.import(updates);
@@ -1311,6 +1255,7 @@ class Singleton extends MVC {
1311
1255
  instance[arg] :
1312
1256
  instance);
1313
1257
  }
1258
+ /** Destroy current instance of Global, if it exists. */
1314
1259
  static reset() {
1315
1260
  const current = Active.get(this);
1316
1261
  if (current)
@@ -1322,22 +1267,25 @@ class Singleton extends MVC {
1322
1267
  }
1323
1268
  }
1324
1269
 
1270
+ exports.CONTROL = CONTROL;
1325
1271
  exports.Consumer = Consumer;
1272
+ exports.Global = Global;
1273
+ exports.LOCAL = LOCAL;
1326
1274
  exports.Model = MVC;
1327
1275
  exports.Provider = Provider;
1328
- exports.Singleton = Singleton;
1276
+ exports.STATE = STATE;
1277
+ exports.WHY = WHY;
1329
1278
  exports.act = act;
1330
1279
  exports.apply = apply;
1331
- exports.child = child;
1332
1280
  exports["default"] = MVC;
1333
1281
  exports.from = from;
1334
- exports.lazy = lazy;
1335
- exports.memo = set;
1336
- exports.on = on;
1337
1282
  exports.parent = parent;
1338
- exports.pending = pending;
1339
1283
  exports.ref = ref;
1340
1284
  exports.set = set;
1341
1285
  exports.tap = tap;
1342
1286
  exports.use = use$1;
1287
+ exports.useFrom = useFrom;
1288
+ exports.useInContext = useInContext;
1289
+ exports.useModel = useModel;
1290
+ exports.useTap = useTap;
1343
1291
  //# sourceMappingURL=index.js.map