@expressive/mvc 1.0.0-beta.3 → 1.0.0-beta.30

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