@expressive/mvc 1.0.0-beta.12 → 1.0.0-beta.17

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
@@ -44,13 +44,11 @@ function defineLazy(object, property, init) {
44
44
  function setAlias(func, displayName) {
45
45
  assign(func, { displayName });
46
46
  }
47
- function entriesIn(object) {
48
- return entries(getOwnPropertyDescriptors(object));
49
- }
50
47
  function createEffect(callback) {
51
48
  let unSet;
52
49
  return function (value) {
53
- typeof unSet == "function" && unSet();
50
+ if (typeof unSet == "function")
51
+ unSet();
54
52
  unSet = callback.call(this, value);
55
53
  if (unSet instanceof Promise)
56
54
  unSet = undefined;
@@ -58,6 +56,28 @@ function createEffect(callback) {
58
56
  throw Oops$8.BadEffectCallback();
59
57
  };
60
58
  }
59
+ function createValueEffect(callback) {
60
+ let unSet;
61
+ return function (value) {
62
+ if (typeof unSet == "function")
63
+ unSet(value);
64
+ const out = callback.call(this, value);
65
+ if (typeof out == "boolean")
66
+ return out;
67
+ if (!out || out instanceof Promise)
68
+ return;
69
+ if (typeof out == "function")
70
+ unSet = out;
71
+ else
72
+ throw Oops$8.BadEffectCallback();
73
+ };
74
+ }
75
+ function select(source, select) {
76
+ const proxy = {};
77
+ for (const k in source)
78
+ proxy[k] = k;
79
+ return select(proxy);
80
+ }
61
81
  function selectRecursive(using, keys) {
62
82
  const found = new Set();
63
83
  const spy = {};
@@ -72,6 +92,187 @@ function selectRecursive(using, keys) {
72
92
  return Array.from(found);
73
93
  }
74
94
 
95
+ const Lifecycle = {
96
+ WILL_RENDER: "willRender",
97
+ WILL_UPDATE: "willUpdate",
98
+ WILL_MOUNT: "willMount",
99
+ WILL_UNMOUNT: "willUnmount",
100
+ DID_MOUNT: "didMount"
101
+ };
102
+ const lifecycleEvents = [
103
+ "didCreate",
104
+ "willDestroy",
105
+ ...values(Lifecycle)
106
+ ];
107
+ function lifecycle(prefix) {
108
+ const type = eventsFor(prefix);
109
+ return function useLifecycle(sub, tag) {
110
+ const emit = react.useMemo(() => {
111
+ const subject = sub.source;
112
+ const id = typeof tag == "function" ? tag(subject) : tag;
113
+ return (name) => {
114
+ for (const key of [name, type(name)])
115
+ subject.update(key, id);
116
+ };
117
+ }, [sub, tag]);
118
+ emit(Lifecycle.WILL_RENDER);
119
+ emit(sub.active
120
+ ? Lifecycle.WILL_UPDATE
121
+ : Lifecycle.WILL_MOUNT);
122
+ react.useLayoutEffect(() => {
123
+ sub.commit();
124
+ emit(Lifecycle.DID_MOUNT);
125
+ return () => {
126
+ emit(Lifecycle.WILL_UNMOUNT);
127
+ sub.release();
128
+ };
129
+ }, [sub]);
130
+ };
131
+ }
132
+ function eventsFor(prefix) {
133
+ const map = new Map();
134
+ for (const name of values(Lifecycle)) {
135
+ const alias = prefix + name[0].toUpperCase() + name.slice(1);
136
+ lifecycleEvents.push(alias);
137
+ map.set(name, alias);
138
+ }
139
+ return (e) => map.get(e);
140
+ }
141
+
142
+ const CONTROL = Symbol("control");
143
+ const CREATE = Symbol("start");
144
+ const LOCAL = Symbol("local");
145
+ const STATE = Symbol("state");
146
+ const Pending = new Map();
147
+ function manage(src) {
148
+ return src[CONTROL];
149
+ }
150
+ function does(instruction, label) {
151
+ const placeholder = Symbol(`${label} instruction`);
152
+ Pending.set(placeholder, instruction);
153
+ return placeholder;
154
+ }
155
+ class Controller {
156
+ constructor(subject) {
157
+ this.subject = subject;
158
+ this.state = {};
159
+ this.frame = new Set();
160
+ this.handles = new Set();
161
+ this.waiting = [];
162
+ }
163
+ static setup(onto) {
164
+ const create = onto[CREATE];
165
+ const control = new this(onto);
166
+ const after = create && create.call(onto, control);
167
+ defineLazy(onto, CONTROL, () => {
168
+ onto[STATE] = control.state;
169
+ control.start();
170
+ if (after)
171
+ after();
172
+ if (onto.didCreate)
173
+ onto.didCreate();
174
+ return control;
175
+ });
176
+ return control;
177
+ }
178
+ get pending() {
179
+ return this.frame.size > 0;
180
+ }
181
+ start() {
182
+ const { subject } = this;
183
+ for (const key in subject) {
184
+ const desc = getOwnPropertyDescriptor(subject, key);
185
+ if (desc && "value" in desc) {
186
+ const { value } = desc;
187
+ const instruction = Pending.get(value);
188
+ if (instruction) {
189
+ Pending.delete(value);
190
+ delete subject[key];
191
+ instruction.call(this, key, this);
192
+ }
193
+ else if (typeof value !== "function" || /^[A-Z]/.test(key))
194
+ this.manage(key, value);
195
+ }
196
+ }
197
+ this.emit();
198
+ return this;
199
+ }
200
+ select(using) {
201
+ const keys = getOwnPropertyNames(this.state);
202
+ return using
203
+ ? selectRecursive(using, keys.concat(lifecycleEvents))
204
+ : keys;
205
+ }
206
+ manage(key, initial, effect) {
207
+ const { state, subject } = this;
208
+ state[key] = initial;
209
+ defineProperty(subject, key, {
210
+ enumerable: true,
211
+ configurable: true,
212
+ get: () => state[key],
213
+ set: this.setter(key, effect)
214
+ });
215
+ }
216
+ setter(key, handler) {
217
+ const { state, subject } = this;
218
+ return (value) => {
219
+ if (state[key] == value)
220
+ return;
221
+ if (handler)
222
+ switch (handler.call(subject, value)) {
223
+ case true:
224
+ this.update(key);
225
+ case false:
226
+ return;
227
+ }
228
+ this.update(key, value);
229
+ };
230
+ }
231
+ addListener(batch) {
232
+ this.handles.add(batch);
233
+ return () => {
234
+ this.handles.delete(batch);
235
+ };
236
+ }
237
+ include(to) {
238
+ if (capture(this, to))
239
+ return;
240
+ else
241
+ this.waiting.push(to);
242
+ }
243
+ update(key, value) {
244
+ if (1 in arguments)
245
+ this.state[key] = value;
246
+ if (this.frame.has(key))
247
+ return;
248
+ if (!this.frame.size)
249
+ setTimeout(() => this.emit(), 0);
250
+ this.frame.add(key);
251
+ for (const subscription of this.handles)
252
+ if (key in subscription)
253
+ this.include(subscription[key]);
254
+ }
255
+ emit() {
256
+ flush(this);
257
+ const keys = Array.from(this.frame);
258
+ const handle = new Set(this.waiting.splice(0));
259
+ this.frame.clear();
260
+ handle.forEach(callback => {
261
+ try {
262
+ callback(keys);
263
+ }
264
+ catch (e) { }
265
+ });
266
+ }
267
+ }
268
+ function keys(from, using) {
269
+ if (typeof using == "string")
270
+ return [using];
271
+ if (typeof using == "object")
272
+ return Array.from(using);
273
+ return from.select(using);
274
+ }
275
+
75
276
  class Subscriber {
76
277
  constructor(parent, onUpdate) {
77
278
  this.onUpdate = onUpdate;
@@ -132,6 +333,15 @@ function getRegister(on) {
132
333
  ComputedUsed.set(on, register = new Map());
133
334
  return register;
134
335
  }
336
+ function prepareGetters(on) {
337
+ const defined = getRegister(on);
338
+ for (let scan = on.subject; scan.constructor !== Object; scan = getPrototypeOf(scan))
339
+ for (const key of getOwnPropertyNames(scan)) {
340
+ const { get, set } = getOwnPropertyDescriptor(scan, key);
341
+ if (get && !defined.has(key))
342
+ prepare(on, key, get, set);
343
+ }
344
+ }
135
345
  function prepare(on, key, getter, setter) {
136
346
  const { state, subject } = on;
137
347
  const defined = getRegister(on);
@@ -194,6 +404,14 @@ function prepare(on, key, getter, setter) {
194
404
  }
195
405
  }
196
406
  }
407
+ function revert(value) {
408
+ delete state[key];
409
+ defineProperty(subject, key, {
410
+ enumerable: true,
411
+ configurable: true,
412
+ value
413
+ });
414
+ }
197
415
  setAlias(update, `try ${key}`);
198
416
  setAlias(create, `new ${key}`);
199
417
  setAlias(getter, `run ${key}`);
@@ -201,6 +419,7 @@ function prepare(on, key, getter, setter) {
201
419
  for (const on of [state, subject])
202
420
  defineProperty(on, key, {
203
421
  get: create,
422
+ set: setter || revert,
204
423
  configurable: true,
205
424
  enumerable: true
206
425
  });
@@ -216,16 +435,17 @@ function ensure(on, keys) {
216
435
  }
217
436
  function capture(on, request) {
218
437
  const compute = ComputedInfo.get(request);
438
+ const callback = request;
219
439
  if (!compute)
220
440
  return;
221
441
  let pending = ComputedKeys.get(on);
222
442
  if (!pending)
223
443
  ComputedKeys.set(on, pending = []);
224
444
  if (compute.parent !== on)
225
- request();
445
+ callback();
226
446
  else {
227
- const byPriorty = (sib) => compute.priority > ComputedInfo.get(sib).priority;
228
- pending.splice(pending.findIndex(byPriorty) + 1, 0, request);
447
+ const queue = pending.findIndex(peer => compute.priority > ComputedInfo.get(peer).priority);
448
+ pending.splice(queue + 1, 0, callback);
229
449
  }
230
450
  return true;
231
451
  }
@@ -244,36 +464,22 @@ function flush(on) {
244
464
  }
245
465
 
246
466
  const Oops$6 = issues({
247
- DuplicateAction: (key) => `Invoked action ${key} but one is already active.`
467
+ DuplicateAction: (key) => `Invoked action ${key} but one is already active.`,
468
+ SourceNotSupported: () => `Computed from-instruction does not support source other than 'this'.`
248
469
  });
249
- const Pending = new Map();
250
- function apply(on, key, value) {
251
- const instruction = Pending.get(value);
252
- if (instruction) {
253
- Pending.delete(value);
254
- delete on.subject[key];
255
- instruction.call(on, key);
256
- return true;
257
- }
258
- }
259
- function set(instruction, name = instruction.name || "pending") {
260
- const placeholder = Symbol(`${name} instruction`);
261
- function apply(key) {
262
- let output = instruction.call(this, key);
263
- if (typeof output == "function") {
264
- const getter = output;
265
- output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
266
- return getter(this[LOCAL]);
267
- } });
268
- }
269
- if (output)
270
- defineProperty(this.subject, key, output);
271
- }
272
- Pending.set(placeholder, apply);
273
- return placeholder;
274
- }
470
+ const set = (fn, name) => does(function set(key) {
471
+ let output = fn.call(this, key, this);
472
+ if (typeof output == "function") {
473
+ const getter = output;
474
+ output = Object.assign(Object.assign({}, getOwnPropertyDescriptor(this.subject, key)), { get() {
475
+ return getter(this[LOCAL]);
476
+ } });
477
+ }
478
+ if (output)
479
+ defineProperty(this.subject, key, output);
480
+ }, name || fn.name || "pending");
275
481
  const ref = (cb) => set(function ref(key) {
276
- const refObjectFunction = this.sets(key, cb && createEffect(cb));
482
+ const refObjectFunction = this.setter(key, cb && createValueEffect(cb));
277
483
  defineProperty(refObjectFunction, "current", {
278
484
  set: refObjectFunction,
279
485
  get: () => this.state[key]
@@ -282,8 +488,8 @@ const ref = (cb) => set(function ref(key) {
282
488
  value: refObjectFunction
283
489
  };
284
490
  });
285
- const on = (value, cb) => set(function on(key) {
286
- this.manage(key, value, cb && createEffect(cb));
491
+ const on = (initial, cb) => set(function on(key) {
492
+ this.manage(key, initial, cb && createValueEffect(cb));
287
493
  });
288
494
  const memo = (factory, defer) => set(function memo(key) {
289
495
  const source = this.subject;
@@ -314,279 +520,83 @@ const act = (task) => set(function act(key) {
314
520
  this.update(key);
315
521
  });
316
522
  };
317
- setAlias(invoke, `run ${key}`);
318
- defineProperty(invoke, "active", {
319
- get: () => pending
320
- });
321
- return {
322
- value: invoke,
323
- writable: false
324
- };
325
- });
326
- const from = (getter) => set(function from(key) {
327
- prepare(this, key, getter);
328
- });
329
-
330
- const Lifecycle = {
331
- WILL_RENDER: "willRender",
332
- WILL_UPDATE: "willUpdate",
333
- WILL_MOUNT: "willMount",
334
- WILL_UNMOUNT: "willUnmount",
335
- DID_MOUNT: "didMount"
336
- };
337
- const lifecycleEvents = [
338
- "didCreate",
339
- "willDestroy",
340
- ...values(Lifecycle)
341
- ];
342
- function lifecycle(prefix) {
343
- const type = eventsFor(prefix);
344
- return function useLifecycle(sub, tag) {
345
- const emit = react.useMemo(() => {
346
- const { parent } = sub;
347
- const { subject } = parent;
348
- const id = typeof tag == "function" ? tag(subject) : tag;
349
- return (name) => {
350
- for (const key of [name, type(name)]) {
351
- const handler = subject[key];
352
- if (handler)
353
- handler.call(subject, id);
354
- parent.update(key);
355
- }
356
- };
357
- }, [sub, tag]);
358
- emit(Lifecycle.WILL_RENDER);
359
- emit(sub.active
360
- ? Lifecycle.WILL_UPDATE
361
- : Lifecycle.WILL_MOUNT);
362
- react.useLayoutEffect(() => {
363
- sub.commit();
364
- emit(Lifecycle.DID_MOUNT);
365
- return () => {
366
- emit(Lifecycle.WILL_UNMOUNT);
367
- sub.release();
368
- };
369
- }, [sub]);
370
- };
371
- }
372
- function eventsFor(prefix) {
373
- const map = new Map();
374
- for (const name of values(Lifecycle)) {
375
- const alias = prefix + name[0].toUpperCase() + name.slice(1);
376
- lifecycleEvents.push(alias);
377
- map.set(name, alias);
378
- }
379
- return (e) => map.get(e);
380
- }
381
-
382
- const CONTROL = Symbol("control");
383
- const LOCAL = Symbol("local");
384
- const STATE = Symbol("state");
385
- function manage(src) {
386
- return src[CONTROL];
387
- }
388
- class Controller {
389
- constructor(subject) {
390
- this.subject = subject;
391
- this.state = {};
392
- this.frame = new Set();
393
- this.handles = new Set();
394
- this.waiting = [];
395
- }
396
- static init(onto, after) {
397
- const control = new this(onto);
398
- defer(control, "on");
399
- defer(control, "effect");
400
- defineLazy(onto, CONTROL, () => {
401
- delete onto.on;
402
- delete onto.effect;
403
- onto[STATE] = control.state;
404
- control.start();
405
- after && after();
406
- return control;
407
- });
408
- }
409
- get pending() {
410
- return this.frame.size > 0;
411
- }
412
- start() {
413
- entriesIn(this.subject).forEach(([key, desc]) => {
414
- if (desc && desc.enumerable && "value" in desc) {
415
- const { value } = desc;
416
- if (apply(this, key, value))
417
- return;
418
- if (typeof value !== "function" || /^[A-Z]/.test(key))
419
- this.manage(key, value);
420
- }
421
- });
422
- this.emit();
423
- return this;
424
- }
425
- select(using) {
426
- const keys = getOwnPropertyNames(this.state);
427
- return using
428
- ? selectRecursive(using, keys.concat(lifecycleEvents))
429
- : keys;
430
- }
431
- manage(key, initial, effect) {
432
- const { state, subject } = this;
433
- state[key] = initial;
434
- defineProperty(subject, key, {
435
- enumerable: true,
436
- configurable: true,
437
- get: () => state[key],
438
- set: this.sets(key, effect)
439
- });
440
- }
441
- sets(key, handler) {
442
- const { state, subject } = this;
443
- return (value) => {
444
- if (state[key] == value)
445
- return;
446
- if (handler)
447
- switch (handler.call(subject, value)) {
448
- case true:
449
- this.update(key);
450
- case false:
451
- return;
452
- }
453
- this.update(key, value);
454
- };
455
- }
456
- watch(subset, handler, squash, once) {
457
- const set = keys(this, subset);
458
- const batch = {};
459
- const remove = this.addListener(batch);
460
- const callback = squash
461
- ? handler.bind(this.subject)
462
- : (frame) => {
463
- for (const key of frame)
464
- if (set.includes(key))
465
- handler.call(this.subject, this.state[key], key);
466
- };
467
- const handle = once
468
- ? (k) => { remove(); callback(k); }
469
- : callback;
470
- for (const key of set)
471
- batch[key] = handle;
472
- ensure(this, set);
473
- return remove;
474
- }
475
- addListener(batch) {
476
- this.handles.add(batch);
477
- return () => {
478
- this.handles.delete(batch);
479
- };
480
- }
481
- include(to) {
482
- if (capture(this, to))
483
- return;
484
- else
485
- this.waiting.push(to);
486
- }
487
- update(key, value) {
488
- if (1 in arguments)
489
- this.state[key] = value;
490
- if (this.frame.has(key))
491
- return;
492
- if (!this.frame.size)
493
- setTimeout(() => this.emit(), 0);
494
- this.frame.add(key);
495
- for (const subscription of this.handles)
496
- if (key in subscription)
497
- this.include(subscription[key]);
498
- }
499
- emit() {
500
- flush(this);
501
- const keys = Array.from(this.frame);
502
- const handle = new Set(this.waiting.splice(0));
503
- this.frame.clear();
504
- handle.forEach(cb => {
505
- try {
506
- cb(keys);
507
- }
508
- catch (e) { }
509
- });
510
- }
511
- }
512
- function keys(from, using) {
513
- if (typeof using == "string")
514
- return [using];
515
- if (typeof using == "object")
516
- return Array.from(using);
517
- return from.select(using);
518
- }
519
- function defer(on, method) {
520
- const { subject, waiting } = on;
521
- const real = subject[method];
522
- subject[method] = (...args) => {
523
- let done;
524
- waiting.push(() => {
525
- done = real.apply(subject, args);
526
- });
527
- return () => done();
528
- };
529
- }
530
-
531
- const Oops$5 = issues({
532
- CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
533
- 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.`
523
+ setAlias(invoke, `run ${key}`);
524
+ defineProperty(invoke, "active", {
525
+ get: () => pending
526
+ });
527
+ return {
528
+ value: invoke,
529
+ writable: false
530
+ };
534
531
  });
535
- const PendingContext = new WeakMap();
536
- const ContextWasUsed = new WeakMap();
537
- const tap = (type, argument) => child(function tap(key) {
538
- if ("current" in type)
539
- return () => type.current;
540
- const to = this.subject;
541
- if ("current" in to.constructor)
542
- throw Oops$5.CantAttachGlobal(to, type.name);
543
- let instance;
544
- getPending(to).push(context => {
545
- instance = context.get(type);
546
- if (typeof argument == "function") {
547
- if (argument(instance) === false)
548
- instance = undefined;
532
+ function from(source, fn) {
533
+ return set(function from(key) {
534
+ let getter;
535
+ if (typeof source == "function")
536
+ getter = source.call(this.subject, key);
537
+ else {
538
+ if (source !== this.subject)
539
+ throw Oops$6.SourceNotSupported();
540
+ getter = fn;
549
541
  }
550
- else if (!instance && argument)
551
- throw Oops$5.AmbientRequired(type.name, to, key);
542
+ prepare(this, key, getter);
552
543
  });
553
- return () => instance;
554
- });
555
- function usePeerContext(subject) {
556
- if (ContextWasUsed.has(subject)) {
557
- if (ContextWasUsed.get(subject))
558
- useLookup();
559
- return;
560
- }
561
- const pending = PendingContext.get(subject);
562
- if (pending) {
563
- const local = useLookup();
564
- for (const init of pending)
565
- init(local);
566
- PendingContext.delete(subject);
567
- }
568
- ContextWasUsed.set(subject, !!pending);
569
- }
570
- function getPending(subject) {
571
- let pending = PendingContext.get(subject);
572
- if (!pending)
573
- PendingContext.set(subject, pending = []);
574
- return pending;
575
544
  }
576
545
 
577
- const Oops$4 = issues({
546
+ const attach = (from, name) => set(function attach(key) {
547
+ const proxyCache = new WeakMap();
548
+ const getCurrent = from.call(this, key);
549
+ function subscribe(sub) {
550
+ let child;
551
+ function start() {
552
+ const instance = getCurrent();
553
+ if (instance) {
554
+ child = new Subscriber(instance, sub.onUpdate);
555
+ if (sub.active)
556
+ child.commit();
557
+ sub.dependant.add(child);
558
+ proxyCache.set(sub, child.proxy);
559
+ }
560
+ }
561
+ function refresh() {
562
+ if (child) {
563
+ child.release();
564
+ sub.dependant.delete(child);
565
+ proxyCache.set(sub, undefined);
566
+ child = undefined;
567
+ }
568
+ start();
569
+ sub.onUpdate();
570
+ }
571
+ start();
572
+ sub.follow(key, refresh);
573
+ }
574
+ return (local) => {
575
+ if (!local)
576
+ return getCurrent();
577
+ if (!proxyCache.has(local))
578
+ subscribe(local);
579
+ return proxyCache.get(local);
580
+ };
581
+ }, name || from.name);
582
+
583
+ const Oops$5 = issues({
578
584
  HasPropertyUndefined: (control, property) => `${control}.${property} is marked as required for this render.`
579
585
  });
580
586
  const useElementLifecycle = lifecycle("element");
581
- const useComponentLifecycle = lifecycle("component");
582
587
  function use$1(init) {
583
588
  const [state, update] = react.useState(() => [
584
589
  init(() => update(state.concat()))
585
590
  ]);
586
591
  return state[0];
587
592
  }
588
- function useLazy(Type, args) {
589
- const instance = react.useMemo(() => Type.create(...args), []);
593
+ function useLazy(Type, args, callback) {
594
+ const instance = react.useMemo(() => {
595
+ const instance = Type.create(...args || []);
596
+ if (callback)
597
+ callback(instance);
598
+ return instance;
599
+ }, []);
590
600
  react.useLayoutEffect(() => () => instance.destroy(), []);
591
601
  return instance;
592
602
  }
@@ -599,7 +609,7 @@ function useWatcher(target, focus, expected) {
599
609
  defineProperty(sub, "proxy", {
600
610
  get() {
601
611
  if (src[key] === undefined && expected)
602
- throw Oops$4.HasPropertyUndefined(target, key);
612
+ throw Oops$5.HasPropertyUndefined(target, key);
603
613
  return src[key];
604
614
  }
605
615
  });
@@ -616,17 +626,19 @@ function useSubscriber(target, tag) {
616
626
  }
617
627
  function useModel(Type, args, callback) {
618
628
  const hook = use$1(refresh => {
619
- const instance = new Type(...args);
620
- const sub = new Subscriber(instance, refresh);
621
- instance.on(Lifecycle.WILL_UNMOUNT, () => {
622
- instance.destroy();
629
+ const instance = new Type(...args || []);
630
+ const control = manage(instance);
631
+ const sub = new Subscriber(control, refresh);
632
+ const release = control.addListener({
633
+ [Lifecycle.WILL_UNMOUNT]() {
634
+ instance.destroy();
635
+ release();
636
+ }
623
637
  });
624
638
  if (callback)
625
639
  callback(instance);
626
640
  return sub;
627
641
  });
628
- usePeerContext(hook.parent.subject);
629
- useComponentLifecycle(hook);
630
642
  return hook.proxy;
631
643
  }
632
644
 
@@ -670,10 +682,10 @@ class Lookup {
670
682
  }
671
683
  do {
672
684
  defineProperty(this, this.key(T), {
673
- value: I,
674
- writable
685
+ value: I, writable
675
686
  });
676
- } while (T = T.inherits);
687
+ T = getPrototypeOf(T);
688
+ } while (T !== Model);
677
689
  return I;
678
690
  }
679
691
  pop() {
@@ -685,7 +697,7 @@ class Lookup {
685
697
  }
686
698
  }
687
699
 
688
- const Oops$3 = issues({
700
+ const Oops$4 = issues({
689
701
  NoProviderType: () => `Provider 'of' prop must be Model, typeof Model or a collection of them.`,
690
702
  NothingInContext: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`,
691
703
  BadConsumerProps: () => `Provider expects either a render function, 'get' or 'has' props.`
@@ -695,7 +707,7 @@ const useLookup = () => react.useContext(LookupContext);
695
707
  function useFromContext(Type, strict) {
696
708
  const instance = useLookup().get(Type);
697
709
  if (!instance && strict)
698
- throw Oops$3.NothingInContext(Type.name);
710
+ throw Oops$4.NothingInContext(Type.name);
699
711
  return instance;
700
712
  }
701
713
  function Consumer(props) {
@@ -706,13 +718,13 @@ function Consumer(props) {
706
718
  if (typeof callback == "function")
707
719
  callback(Control.get(!!has));
708
720
  else
709
- throw Oops$3.BadConsumerProps();
721
+ throw Oops$4.BadConsumerProps();
710
722
  return null;
711
723
  }
712
724
  function useNewContext(from, inject) {
713
725
  return react.useMemo(() => {
714
726
  if (!inject)
715
- throw Oops$3.NoProviderType();
727
+ throw Oops$4.NoProviderType();
716
728
  const context = from.push(inject);
717
729
  for (const instance of context.local)
718
730
  for (const apply of getPending(instance))
@@ -755,50 +767,63 @@ function Provider(props) {
755
767
  : render);
756
768
  }
757
769
 
770
+ const Oops$3 = issues({
771
+ CantAttachGlobal: (parent, child) => `Singleton '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
772
+ 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.`
773
+ });
774
+ const PendingContext = new WeakMap();
775
+ const ContextWasUsed = new WeakMap();
776
+ const tap = (type, argument) => attach(function tap(key) {
777
+ if ("current" in type)
778
+ return () => type.current;
779
+ const to = this.subject;
780
+ if ("current" in to.constructor)
781
+ throw Oops$3.CantAttachGlobal(to, type.name);
782
+ let instance;
783
+ getPending(to).push(context => {
784
+ instance = context.get(type);
785
+ if (typeof argument == "function") {
786
+ if (argument(instance) === false)
787
+ instance = undefined;
788
+ }
789
+ else if (!instance && argument)
790
+ throw Oops$3.AmbientRequired(type.name, to, key);
791
+ });
792
+ return () => instance;
793
+ });
794
+ function usePeerContext(subject) {
795
+ if (ContextWasUsed.has(subject)) {
796
+ if (ContextWasUsed.get(subject))
797
+ useLookup();
798
+ return;
799
+ }
800
+ const pending = PendingContext.get(subject);
801
+ if (pending) {
802
+ const local = useLookup();
803
+ for (const init of pending)
804
+ init(local);
805
+ PendingContext.delete(subject);
806
+ }
807
+ ContextWasUsed.set(subject, !!pending);
808
+ }
809
+ function getPending(subject) {
810
+ let pending = PendingContext.get(subject);
811
+ if (!pending)
812
+ PendingContext.set(subject, pending = []);
813
+ return pending;
814
+ }
815
+
758
816
  const Oops$2 = issues({
759
- StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`
817
+ StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
818
+ NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
760
819
  });
761
- class Model {
762
- constructor() {
820
+ class State {
821
+ constructor(computeGetters) {
822
+ const control = Controller.setup(this);
763
823
  define(this, "get", this);
764
824
  define(this, "set", this);
765
- Controller.init(this, () => {
766
- if (this.didCreate)
767
- this.didCreate();
768
- });
769
- }
770
- tap(path, expect) {
771
- return useWatcher(this, path, expect);
772
- }
773
- tag(id) {
774
- return useSubscriber(this, id);
775
- }
776
- on(select, callback, squash, once) {
777
- return manage(this).watch(select, callback, squash, once);
778
- }
779
- once(select, callback, squash) {
780
- if (callback)
781
- return this.on(select, callback, squash, true);
782
- else
783
- return new Promise(resolve => {
784
- this.on(select, resolve, true, true);
785
- });
786
- }
787
- effect(callback, select) {
788
- const control = manage(this);
789
- let target = this;
790
- const effect = createEffect(callback);
791
- const invoke = () => effect.call(target, target);
792
- if (select) {
793
- invoke();
794
- return this.on(select, invoke, true);
795
- }
796
- else {
797
- const sub = new Subscriber(control, invoke);
798
- target = sub.proxy;
799
- invoke();
800
- return sub.commit();
801
- }
825
+ if (computeGetters !== false)
826
+ prepareGetters(control);
802
827
  }
803
828
  import(from, subset) {
804
829
  for (const key of keys(manage(this), subset))
@@ -812,46 +837,66 @@ class Model {
812
837
  output[key] = control.state[key];
813
838
  return output;
814
839
  }
815
- update(arg) {
840
+ update(arg, tag) {
816
841
  const control = manage(this);
842
+ if (typeof arg == "function")
843
+ arg = select(this, arg);
817
844
  if (typeof arg == "boolean") {
818
845
  if (!control.pending === arg)
819
846
  return Promise.reject(Oops$2.StrictUpdate(arg));
820
847
  }
821
- else if (arg)
822
- for (const key of keys(control, arg))
823
- control.update(key);
824
- if (control.pending)
825
- return new Promise(cb => control.include(cb));
826
- return Promise.resolve(false);
848
+ else if (arg) {
849
+ control.update(arg);
850
+ if (1 in arguments && arg in this)
851
+ if (typeof tag != "boolean")
852
+ this[arg](tag);
853
+ else if (tag)
854
+ this[arg]();
855
+ }
856
+ return {
857
+ then(callback) {
858
+ if (callback)
859
+ if (control.pending)
860
+ control.include(callback);
861
+ else
862
+ callback(false);
863
+ else
864
+ throw Oops$2.NoChaining();
865
+ }
866
+ };
827
867
  }
828
868
  destroy() {
829
- if (this.willDestroy)
830
- this.willDestroy();
869
+ this.update("willDestroy", true);
831
870
  }
832
871
  toString() {
833
872
  return this.constructor.name;
834
873
  }
874
+ tap(path, expect) {
875
+ return useWatcher(this, path, expect);
876
+ }
835
877
  static create(...args) {
836
878
  const instance = new this(...args);
837
879
  manage(instance);
838
880
  return instance;
839
881
  }
840
- static use(...args) {
841
- return useModel(this, args);
882
+ static new(args, callback) {
883
+ return useLazy(this, args, callback);
884
+ }
885
+ static use(args, callback) {
886
+ return useModel(this, args, callback);
842
887
  }
843
888
  static uses(props, only) {
844
- return useModel(this, [], instance => {
889
+ return this.use([], instance => {
845
890
  instance.import(props, only);
846
891
  });
847
892
  }
848
893
  static using(props, only) {
849
- const instance = useModel(this, []);
894
+ const instance = this.use([]);
850
895
  instance.import(props, only);
851
896
  return instance;
852
897
  }
853
- static new(...args) {
854
- return useLazy(this, args);
898
+ static find(strict) {
899
+ return useFromContext(this, strict);
855
900
  }
856
901
  static get(key) {
857
902
  const instance = this.find(!!key);
@@ -864,31 +909,103 @@ class Model {
864
909
  static tap(key, expect) {
865
910
  return useWatcher(this.find(true), key, expect);
866
911
  }
912
+ static isTypeof(maybe) {
913
+ return (typeof maybe == "function" &&
914
+ maybe.prototype instanceof this);
915
+ }
916
+ }
917
+ State.CONTROL = CONTROL;
918
+ State.STATE = STATE;
919
+ State.INIT = CREATE;
920
+ State.LOCAL = LOCAL;
921
+
922
+ const useComponentLifecycle = lifecycle("component");
923
+ class Model extends State {
924
+ constructor() {
925
+ super(false);
926
+ }
927
+ [CREATE](using) {
928
+ defer(using, "on");
929
+ defer(using, "effect");
930
+ return () => {
931
+ delete this.on;
932
+ delete this.effect;
933
+ };
934
+ }
935
+ on(subset, handler, squash, once) {
936
+ const control = manage(this);
937
+ const set = keys(control, subset);
938
+ const batch = {};
939
+ const remove = control.addListener(batch);
940
+ const callback = squash
941
+ ? handler.bind(this)
942
+ : (frame) => {
943
+ for (const key of frame)
944
+ if (set.includes(key))
945
+ handler.call(this, control.state[key], key);
946
+ };
947
+ const handle = once
948
+ ? (k) => { remove(); callback(k); }
949
+ : callback;
950
+ for (const key of set)
951
+ batch[key] = handle;
952
+ ensure(control, set);
953
+ return remove;
954
+ }
955
+ once(select, callback, squash) {
956
+ if (callback)
957
+ return this.on(select, callback, squash, true);
958
+ else
959
+ return new Promise(resolve => {
960
+ this.on(select, resolve, true, true);
961
+ });
962
+ }
963
+ effect(callback, select) {
964
+ const control = manage(this);
965
+ let target = this;
966
+ const effect = createEffect(callback);
967
+ const invoke = () => effect.call(target, target);
968
+ if (select) {
969
+ invoke();
970
+ return this.on(select, invoke, true);
971
+ }
972
+ else {
973
+ const sub = new Subscriber(control, invoke);
974
+ target = sub.proxy;
975
+ invoke();
976
+ return sub.commit();
977
+ }
978
+ }
979
+ tag(id) {
980
+ return useSubscriber(this, id);
981
+ }
982
+ static use(args, callback) {
983
+ const instance = useModel(this, args, callback);
984
+ useComponentLifecycle(instance[LOCAL]);
985
+ usePeerContext(instance.get);
986
+ return instance;
987
+ }
867
988
  static tag(id) {
868
989
  return useSubscriber(this.find(true), id);
869
990
  }
870
991
  static meta(path) {
871
992
  return useWatcher(this, path);
872
993
  }
873
- static find(strict) {
874
- return useFromContext(this, strict);
875
- }
876
- static isTypeof(maybe) {
877
- return (typeof maybe == "function" &&
878
- maybe.prototype instanceof this);
879
- }
880
- static get inherits() {
881
- const I = getPrototypeOf(this);
882
- if (I !== Model)
883
- return I;
884
- }
885
994
  }
886
- Model.STATE = STATE;
887
- Model.CONTROL = CONTROL;
888
- Model.LOCAL = LOCAL;
889
995
  defineLazy(Model, CONTROL, function () {
890
996
  return new Controller(this).start();
891
- });
997
+ });
998
+ function defer(on, method) {
999
+ const { subject, waiting } = on;
1000
+ const real = subject[method];
1001
+ subject[method] = (...args) => {
1002
+ let done;
1003
+ waiting.push(() => {
1004
+ done = real.apply(subject, args);
1005
+ });
1006
+ return () => done();
1007
+ };
1008
+ }
892
1009
 
893
1010
  const Parent = new WeakMap();
894
1011
  const Oops$1 = issues({
@@ -897,7 +1014,14 @@ const Oops$1 = issues({
897
1014
  UndefinedNotAllowed: (key) => `Child property ${key} may not be undefined.`,
898
1015
  BadArgument: (type) => `Instruction \`use\` cannot accept argument type of ${type}.`
899
1016
  });
900
- const use = (input, argument) => child(function use(key) {
1017
+ function bootstrap(object) {
1018
+ const breakdown = getOwnPropertyDescriptors(object);
1019
+ const control = new Model();
1020
+ for (const key in breakdown)
1021
+ defineProperty(control, key, breakdown[key]);
1022
+ return control;
1023
+ }
1024
+ const use = (input, argument) => attach(function use(key) {
901
1025
  let instance;
902
1026
  const update = (next) => {
903
1027
  instance =
@@ -927,52 +1051,6 @@ const use = (input, argument) => child(function use(key) {
927
1051
  this.manage(key, instance, update);
928
1052
  return () => instance;
929
1053
  });
930
- function bootstrap(object) {
931
- const breakdown = getOwnPropertyDescriptors(object);
932
- const control = new Model();
933
- for (const key in breakdown)
934
- defineProperty(control, key, breakdown[key]);
935
- return control;
936
- }
937
- const child = (from, name) => {
938
- function child(key) {
939
- const proxyCache = new WeakMap();
940
- const getCurrent = from.call(this, key);
941
- function subscribe(sub) {
942
- let child;
943
- function start() {
944
- const instance = getCurrent();
945
- if (instance) {
946
- child = new Subscriber(instance, sub.onUpdate);
947
- if (sub.active)
948
- child.commit();
949
- sub.dependant.add(child);
950
- proxyCache.set(sub, child.proxy);
951
- }
952
- }
953
- function refresh() {
954
- if (child) {
955
- child.release();
956
- sub.dependant.delete(child);
957
- proxyCache.set(sub, undefined);
958
- child = undefined;
959
- }
960
- start();
961
- sub.onUpdate();
962
- }
963
- start();
964
- sub.follow(key, refresh);
965
- }
966
- return (local) => {
967
- if (!local)
968
- return getCurrent();
969
- if (!proxyCache.has(local))
970
- subscribe(local);
971
- return proxyCache.get(local);
972
- };
973
- }
974
- return set(child, name || from.name);
975
- };
976
1054
  const parent = (Expects, required) => set(function parent() {
977
1055
  const child = this.subject;
978
1056
  const expected = Expects.name;
@@ -1003,10 +1081,19 @@ class Singleton extends Model {
1003
1081
  Active.set(Type, instance);
1004
1082
  return instance;
1005
1083
  }
1006
- static find() {
1007
- if (this.current)
1008
- return this.current;
1009
- throw Oops.GlobalDoesNotExist(this.name);
1084
+ static update(updates) {
1085
+ const instance = this.current || this.create();
1086
+ instance.import(updates);
1087
+ return instance.update();
1088
+ }
1089
+ static is() {
1090
+ return this.find(true);
1091
+ }
1092
+ static find(strict) {
1093
+ const instance = this.current;
1094
+ if (!instance && strict)
1095
+ throw Oops.GlobalDoesNotExist(this.name);
1096
+ return instance;
1010
1097
  }
1011
1098
  static reset() {
1012
1099
  if (this.current)
@@ -1022,8 +1109,9 @@ exports.Consumer = Consumer;
1022
1109
  exports.Model = Model;
1023
1110
  exports.Provider = Provider;
1024
1111
  exports.Singleton = Singleton;
1112
+ exports.State = State;
1025
1113
  exports.act = act;
1026
- exports.child = child;
1114
+ exports.attach = attach;
1027
1115
  exports.default = Model;
1028
1116
  exports.from = from;
1029
1117
  exports.lazy = lazy;