@expressive/mvc 1.0.0-beta.10 → 1.0.0-beta.14

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