@expressive/mvc 1.0.0-beta.4 → 1.0.0-beta.40

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