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

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