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