@expressive/mvc 1.0.0-beta.2 → 1.0.0-beta.23

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