@expressive/mvc 0.0.1

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