@expressive/mvc 1.0.0-beta.43 → 1.0.0-beta.44

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.d.ts CHANGED
@@ -220,7 +220,8 @@ declare namespace Instruction {
220
220
  value?: T;
221
221
  writable?: boolean;
222
222
  get?: Getter<T>;
223
- set?: Setter<T> | false | void;
223
+ set?: Setter<T> | false;
224
+ suspense?: boolean;
224
225
  }
225
226
 
226
227
  interface ExplicitDescriptor extends PropertyDescriptor {
@@ -231,7 +232,8 @@ declare namespace Instruction {
231
232
  recursive: true;
232
233
  enumerable?: boolean;
233
234
  value?: T;
234
- set?: Setter<T> | false | void;
235
+ set?: Setter<T> | false;
236
+ suspense?: boolean;
235
237
  }
236
238
  }
237
239
 
@@ -533,7 +535,7 @@ declare class Global extends MVC {
533
535
  * Returns a thenable; resolves after successful update.
534
536
  * If instance does not already exist, one will be created.
535
537
  */
536
- static set<T extends typeof Global>(this: T, updates: Partial<InstanceOf<T>>): PromiseLike<false | readonly Model.Event<Global, Model>[]>;
538
+ static set<T extends typeof Global>(this: T, updates: Partial<InstanceOf<T>>): Promise<false>;
537
539
  /**
538
540
  * **React Hook** - Fetch most instance of this controller from context, if it exists.
539
541
  *
package/lib/index.esm.js CHANGED
@@ -34,9 +34,6 @@ function defineLazy(object, property, init) {
34
34
  }
35
35
  });
36
36
  }
37
- function setAlias(func, displayName) {
38
- assign(func, { displayName });
39
- }
40
37
  function createEffect(callback) {
41
38
  let unSet;
42
39
  return function (value) {
@@ -144,66 +141,7 @@ class Subscriber {
144
141
 
145
142
  const Oops$b = issues({
146
143
  ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
147
- FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
148
144
  });
149
- /**
150
- * Get value, suspend instead if undefined.
151
- *
152
- * Throws suspense promise also interpretable as an error.
153
- * React could handle it but other contexts probably not.
154
- */
155
- function pendingValue(via, key) {
156
- const value = via.state[key];
157
- if (value === undefined)
158
- throw suspend(via, key);
159
- return value;
160
- }
161
- function pendingFactory(parent, key, fn, immediate) {
162
- const { subject, state } = parent;
163
- let pending;
164
- let error;
165
- const init = () => {
166
- const output = suspenseBoundary(() => {
167
- return fn.call(subject, key, subject);
168
- });
169
- if (output instanceof Promise) {
170
- pending = output
171
- .catch(err => error = err)
172
- .then(val => state[key] = val)
173
- .finally(() => {
174
- pending = undefined;
175
- parent.update(key);
176
- });
177
- const issue = Oops$b.ValueNotReady(subject, key);
178
- Object.assign(pending, {
179
- message: issue.message,
180
- stack: issue.stack
181
- });
182
- }
183
- return state[key] = output;
184
- };
185
- if (immediate)
186
- try {
187
- init();
188
- }
189
- catch (err) {
190
- Oops$b.FactoryFailed(subject, key).warn();
191
- throw err;
192
- }
193
- return () => {
194
- if (pending)
195
- throw pending;
196
- if (error)
197
- throw error;
198
- if (key in state)
199
- return state[key];
200
- let output = init();
201
- if (pending)
202
- throw pending;
203
- else
204
- return output;
205
- };
206
- }
207
145
  function suspend(source, key) {
208
146
  const error = Oops$b.ValueNotReady(source.subject, key);
209
147
  const promise = new Promise(resolve => {
@@ -240,9 +178,7 @@ function from(source, arg1, arg2) {
240
178
  const parent = this;
241
179
  const { subject, state } = this;
242
180
  let getSource;
243
- let getter;
244
- if (arg2 === true || arg1 === true)
245
- getter = pendingValue;
181
+ let required = arg2 === true || arg1 === true;
246
182
  if (typeof arg1 == "boolean")
247
183
  arg1 = undefined;
248
184
  // Easy mistake, using a peer, will always be unresolved.
@@ -326,9 +262,6 @@ function from(source, arg1, arg2) {
326
262
  }
327
263
  }
328
264
  }
329
- setAlias(update, `try ${key}`);
330
- setAlias(create, `new ${key}`);
331
- setAlias(setter, `run ${key}`);
332
265
  INIT.add(create);
333
266
  INFO.set(update, info);
334
267
  defineProperty(state, key, {
@@ -336,8 +269,12 @@ function from(source, arg1, arg2) {
336
269
  configurable: true,
337
270
  enumerable: true
338
271
  });
339
- if (getter)
340
- return () => getter(parent, key);
272
+ return () => {
273
+ const value = parent.state[key];
274
+ if (value === undefined && required)
275
+ throw suspend(this, key);
276
+ return value;
277
+ };
341
278
  });
342
279
  }
343
280
  function ensure(on, keys) {
@@ -364,7 +301,7 @@ function flush(on) {
364
301
  }
365
302
 
366
303
  const Oops$9 = issues({
367
- StrictUpdate: (expected) => `Strict update() did ${expected ? "not " : ""}find pending updates.`,
304
+ StrictUpdate: () => `Strict update() did not find pending updates.`,
368
305
  NoChaining: () => `Then called with undefined; update promise will never catch nor supports chaining.`
369
306
  });
370
307
  const READY = new WeakSet();
@@ -394,7 +331,9 @@ class Controller {
394
331
  return;
395
332
  let onGet;
396
333
  let onSet;
334
+ let set;
397
335
  let enumerable;
336
+ let suspense;
398
337
  const instruction = Pending.get(entry);
399
338
  if (instruction) {
400
339
  Pending.delete(entry);
@@ -407,31 +346,35 @@ class Controller {
407
346
  state[key] = desc.value;
408
347
  onSet = desc.set;
409
348
  onGet = desc.get;
349
+ suspense = desc.suspense;
410
350
  enumerable = desc.enumerable;
411
351
  }
412
352
  }
413
353
  else
414
354
  state[key] = entry;
415
- const set = onSet !== false
416
- ? this.ref(key, onSet)
417
- : undefined;
418
- function get() {
419
- const local = this[LOCAL];
355
+ if (onSet !== false)
356
+ set = this.ref(key, onSet);
357
+ const get = (local) => {
358
+ if (!(key in state) && suspense)
359
+ throw suspend(this, key);
420
360
  const value = state[key];
421
- if (local && !local.watch[key])
422
- local.watch[key] = true;
423
- return onGet ? onGet(value, local) : value;
424
- }
425
- setAlias(get, `tap ${key}`);
361
+ if (onGet)
362
+ return local
363
+ ? onGet(value, local)
364
+ : onGet(value);
365
+ return value;
366
+ };
426
367
  defineProperty(subject, key, {
427
- enumerable,
428
- set,
429
- get: onGet
430
- ? () => onGet(state[key])
431
- : () => state[key]
368
+ enumerable, set, get
432
369
  });
433
370
  defineProperty(proxy, key, {
434
- enumerable, get, set
371
+ enumerable, set,
372
+ get() {
373
+ const local = this[LOCAL];
374
+ if (local && !local.watch[key])
375
+ local.watch[key] = true;
376
+ return get(local);
377
+ }
435
378
  });
436
379
  }
437
380
  ref(key, handler) {
@@ -491,12 +434,12 @@ class Controller {
491
434
  this.waiting.add(arg);
492
435
  return;
493
436
  }
494
- if (typeof arg == "boolean" && arg !== this.frame.size > 0)
495
- return Promise.reject(Oops$9.StrictUpdate(arg));
437
+ if (arg === true && this.frame.size < 1)
438
+ return Promise.reject(Oops$9.StrictUpdate());
496
439
  return {
497
440
  then: (callback) => {
498
441
  if (callback)
499
- if (this.frame.size)
442
+ if (this.frame.size || arg !== false)
500
443
  this.waiting.add(callback);
501
444
  else
502
445
  callback(false);
@@ -679,17 +622,21 @@ function apply(fn, label) {
679
622
  const placeholder = Symbol(`${name} instruction`);
680
623
  function setup(key) {
681
624
  let output = fn.call(this, key, this);
682
- if (typeof output == "function")
683
- return { get: output };
684
- if (typeof output !== "object")
685
- return output || undefined;
686
- if ("explicit" in output && output.explicit) {
687
- defineProperty(this.subject, key, output);
688
- return false;
625
+ switch (typeof output) {
626
+ case "function":
627
+ return { get: output };
628
+ case "object": {
629
+ if ("explicit" in output && output.explicit) {
630
+ defineProperty(this.subject, key, output);
631
+ return false;
632
+ }
633
+ if ("recursive" in output && output.recursive)
634
+ return Object.assign(Object.assign({}, output), { get: recursive(this, key) });
635
+ }
636
+ case "boolean":
637
+ return output;
689
638
  }
690
- if ("recursive" in output)
691
- output = Object.assign(Object.assign({}, output), { get: recursive(this, key) });
692
- return output;
639
+ return;
693
640
  }
694
641
  Pending.set(placeholder, setup);
695
642
  return placeholder;
@@ -745,7 +692,6 @@ function act(task) {
745
692
  });
746
693
  };
747
694
  this.state[key] = undefined;
748
- setAlias(invoke, `run ${key}`);
749
695
  defineProperty(invoke, "active", {
750
696
  get: () => pending
751
697
  });
@@ -780,20 +726,22 @@ function createRef(key, cb) {
780
726
 
781
727
  const Oops$7 = issues({
782
728
  NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
783
- BadFactory: () => `Set instruction can only accept a factory or undefined.`
729
+ BadFactory: () => `Set instruction can only accept a factory or undefined.`,
730
+ ValueNotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
731
+ FactoryFailed: (model, key) => `Generating initial value for ${model}.${key} failed.`
784
732
  });
785
733
  function set(factory, argument) {
786
734
  return apply(function set(key) {
787
735
  let set;
788
736
  let get;
737
+ let suspense;
789
738
  const required = argument === true || argument === undefined;
790
739
  if (factory === undefined)
791
- get = () => pendingValue(this, key);
792
- else if (typeof factory !== "function")
793
- throw Oops$7.BadFactory();
794
- else {
740
+ suspense = true;
741
+ else if (typeof factory === "function")
795
742
  get = pendingFactory(this, key, factory, required);
796
- }
743
+ else
744
+ throw Oops$7.BadFactory();
797
745
  if (typeof argument == "function")
798
746
  set = createValueEffect(argument);
799
747
  else
@@ -801,8 +749,63 @@ function set(factory, argument) {
801
749
  if (value === undefined && required)
802
750
  throw Oops$7.NonOptional(this.subject, key);
803
751
  };
804
- return { set, get };
752
+ return {
753
+ set,
754
+ get,
755
+ suspense
756
+ };
805
757
  });
758
+ }
759
+ function pendingFactory(parent, key, fn, required) {
760
+ const { subject, state } = parent;
761
+ let pending;
762
+ let error;
763
+ const init = () => {
764
+ const output = suspenseBoundary(() => {
765
+ return fn.call(subject, key, subject);
766
+ });
767
+ if (output instanceof Promise) {
768
+ pending = output
769
+ .catch(err => error = err)
770
+ .then(val => state[key] = val)
771
+ .finally(() => {
772
+ pending = undefined;
773
+ parent.update(key);
774
+ });
775
+ return;
776
+ }
777
+ return state[key] = output;
778
+ };
779
+ const suspend = () => {
780
+ if (required === false)
781
+ return undefined;
782
+ const issue = Oops$7.ValueNotReady(subject, key);
783
+ Object.assign(pending, {
784
+ message: issue.message,
785
+ stack: issue.stack
786
+ });
787
+ throw pending;
788
+ };
789
+ if (required)
790
+ try {
791
+ init();
792
+ }
793
+ catch (err) {
794
+ Oops$7.FactoryFailed(subject, key).warn();
795
+ throw err;
796
+ }
797
+ return () => {
798
+ if (pending)
799
+ return suspend();
800
+ if (error)
801
+ throw error;
802
+ if (key in state)
803
+ return state[key];
804
+ let output = init();
805
+ return pending
806
+ ? suspend()
807
+ : output;
808
+ };
806
809
  }
807
810
 
808
811
  const Parent = new WeakMap();
@@ -945,24 +948,26 @@ const ContextWasUsed = new WeakMap();
945
948
  function tap(type, argument) {
946
949
  return apply(function tap(key) {
947
950
  if ("set" in type)
948
- this.state[key] = type.get();
949
- else {
950
- const { subject } = this;
951
- if ("set" in subject.constructor)
952
- throw Oops$3.CantAttachGlobal(subject, type.name);
953
- getPending(subject).push(context => {
954
- let instance = context.get(type);
955
- if (typeof argument == "function") {
956
- if (argument(instance) === false)
957
- instance = undefined;
958
- }
959
- else if (!instance && argument)
960
- throw Oops$3.AmbientRequired(type.name, subject, key);
961
- this.update(key, instance);
962
- });
963
- }
951
+ return {
952
+ recursive: true,
953
+ value: type.get()
954
+ };
955
+ const { subject } = this;
956
+ if ("set" in subject.constructor)
957
+ throw Oops$3.CantAttachGlobal(subject, type.name);
958
+ getPending(subject).push(context => {
959
+ let instance = context.get(type);
960
+ if (typeof argument == "function") {
961
+ if (argument(instance) === false)
962
+ instance = undefined;
963
+ }
964
+ else if (!instance && argument)
965
+ throw Oops$3.AmbientRequired(type.name, subject, key);
966
+ this.update(key, instance);
967
+ });
964
968
  return {
965
- recursive: true
969
+ recursive: true,
970
+ suspense: true
966
971
  };
967
972
  });
968
973
  }
@@ -1260,7 +1265,7 @@ class Global extends MVC {
1260
1265
  static set(updates) {
1261
1266
  const instance = Active.get(this) || this.create();
1262
1267
  instance.import(updates);
1263
- return instance.update();
1268
+ return instance.update(false);
1264
1269
  }
1265
1270
  static get(arg) {
1266
1271
  const instance = Active.get(this);