@expressive/mvc 0.27.0 → 0.29.0

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.esm.js CHANGED
@@ -40,162 +40,9 @@ function issues(register) {
40
40
  return Library;
41
41
  }
42
42
 
43
- const { assign, create, defineProperties, defineProperty, entries, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
44
-
45
- const Oops$8 = issues({
46
- NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
47
- Destoryed: () => "Model is destroyed."
48
- });
49
- function suspense(source, key) {
50
- const error = Oops$8.NotReady(source.subject, key);
51
- const watch = source.observers.get(key);
52
- const promise = new Promise((resolve, reject) => {
53
- function onUpdate(key) {
54
- if (key)
55
- return () => {
56
- if (source.state[key] !== undefined) {
57
- watch.delete(onUpdate);
58
- resolve();
59
- }
60
- };
61
- if (key === null)
62
- reject(Oops$8.Destoryed());
63
- }
64
- watch.add(onUpdate);
65
- });
66
- return assign(promise, {
67
- toString: () => String(error),
68
- name: "Suspense",
69
- message: error.message,
70
- stack: error.stack
71
- });
72
- }
73
- function mayRetry(fn) {
74
- const retry = (err) => {
75
- if (err instanceof Promise)
76
- return err.then(compute);
77
- else
78
- throw err;
79
- };
80
- const compute = () => {
81
- try {
82
- const output = fn();
83
- return output instanceof Promise
84
- ? output.catch(retry)
85
- : output;
86
- }
87
- catch (err) {
88
- return retry(err);
89
- }
90
- };
91
- return compute();
92
- }
43
+ const { assign, create, defineProperties, defineProperty, entries, keys, getPrototypeOf, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
93
44
 
94
45
  const Oops$7 = issues({
95
- BadCallback: () => `Callback for effect-callback may only return a function.`
96
- });
97
- function createEffect(source, callback) {
98
- return control(source, self => {
99
- let { subject } = self;
100
- let unSet;
101
- let busy = false;
102
- function invoke() {
103
- if (busy)
104
- return;
105
- const output = mayRetry(() => {
106
- if (typeof unSet == "function")
107
- unSet();
108
- unSet = callback.call(subject, subject);
109
- if (unSet instanceof Promise)
110
- unSet = undefined;
111
- if (unSet && typeof unSet !== "function")
112
- throw Oops$7.BadCallback();
113
- });
114
- if (output instanceof Promise) {
115
- output.finally(() => busy = false);
116
- busy = true;
117
- }
118
- }
119
- let refresh;
120
- subject = watch(subject, () => refresh);
121
- self.followers.add(key => (key === null || refresh === null ? refresh : undefined));
122
- invoke();
123
- refresh = invoke;
124
- return () => refresh = null;
125
- });
126
- }
127
- function createValueEffect(callback) {
128
- let unSet;
129
- return function (value) {
130
- if (typeof unSet == "function")
131
- unSet(value);
132
- const out = callback.call(this, value, this);
133
- if (typeof out == "boolean")
134
- return out;
135
- if (!out || out instanceof Promise)
136
- return;
137
- if (typeof out == "function")
138
- unSet = out;
139
- else
140
- throw Oops$7.BadCallback();
141
- };
142
- }
143
-
144
- const Oops$6 = issues({
145
- KeysExpected: (key) => `Key "${key}" was expected in current update.`
146
- });
147
- function addEventListener(source, select, callback, once) {
148
- return control(source, self => {
149
- const { subject } = self;
150
- if (typeof select == "string")
151
- select = [select];
152
- if (select)
153
- for (const key of select)
154
- try {
155
- void subject[key];
156
- }
157
- catch (e) {
158
- // TODO: should this be caught?
159
- }
160
- const removeListener = self.addListener(key => {
161
- if (key === null)
162
- callback.call(subject, null);
163
- else if (!select || select.includes(key)) {
164
- if (once)
165
- removeListener();
166
- return () => callback.call(subject, self.latest);
167
- }
168
- });
169
- return removeListener;
170
- });
171
- }
172
- function awaitUpdate(source, select, timeout) {
173
- return new Promise((resolve, reject) => {
174
- if (timeout === 0) {
175
- const self = control(source, true);
176
- if (!self.frame.size)
177
- resolve(false);
178
- else if (typeof select == "string" && !self.frame.has(select))
179
- reject(Oops$6.KeysExpected(select));
180
- else {
181
- const remove = self.addListener(() => {
182
- remove();
183
- return () => resolve(self.latest);
184
- });
185
- }
186
- }
187
- else {
188
- const remove = addEventListener(source, select, resolve, true);
189
- if (timeout > 0)
190
- setTimeout(() => {
191
- remove();
192
- resolve(false);
193
- }, timeout);
194
- }
195
- });
196
- }
197
-
198
- const Oops$5 = issues({
199
46
  NotFound: (name) => `Could not find ${name} in context.`
200
47
  });
201
48
  function get$1(argument) {
@@ -210,7 +57,7 @@ function get$1(argument) {
210
57
  ? Control.watch(got, k => k ? onUpdate : undefined)
211
58
  : got;
212
59
  else if (argument !== false)
213
- throw Oops$5.NotFound(this);
60
+ throw Oops$7.NotFound(this);
214
61
  return {
215
62
  mount() {
216
63
  onUpdate = refresh;
@@ -226,7 +73,7 @@ function get$1(argument) {
226
73
  let proxy;
227
74
  const found = context.get(this);
228
75
  if (!found)
229
- throw Oops$5.NotFound(this);
76
+ throw Oops$7.NotFound(this);
230
77
  function forceUpdate(action) {
231
78
  if (typeof action == "function")
232
79
  action = action();
@@ -316,7 +163,7 @@ function use$1(apply, repeat) {
316
163
  instance[key] = props[key];
317
164
  if (!repeat)
318
165
  shouldApply = false;
319
- instance.on(0).then(() => onUpdate = refresh);
166
+ instance.set(0).then(() => onUpdate = refresh);
320
167
  }
321
168
  return local;
322
169
  }
@@ -325,59 +172,184 @@ function use$1(apply, repeat) {
325
172
  return render(apply);
326
173
  }
327
174
 
328
- class Model {
329
- constructor(id) {
330
- new Control(this, id);
331
- }
332
- /**
333
- * Reference to `this` without a subscription.
334
- * Use to obtain full reference from a destructure.
335
- */
336
- get is() {
337
- return this;
338
- }
339
- on(arg1, arg2, arg3) {
340
- if (typeof arg1 == "function")
341
- return createEffect(this, arg1);
342
- if (typeof arg1 == "number") {
343
- arg2 = arg1;
344
- arg1 = undefined;
175
+ const Oops$6 = issues({
176
+ NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
177
+ Destoryed: () => "Model is destroyed."
178
+ });
179
+ function suspense(source, key) {
180
+ const error = Oops$6.NotReady(source.subject, key);
181
+ const watch = source.observers.get(key);
182
+ const promise = new Promise((resolve, reject) => {
183
+ function onUpdate(key) {
184
+ if (key)
185
+ return () => {
186
+ if (source.state[key] !== undefined) {
187
+ watch.delete(onUpdate);
188
+ resolve();
189
+ }
190
+ };
191
+ if (key === null)
192
+ reject(Oops$6.Destoryed());
193
+ }
194
+ watch.add(onUpdate);
195
+ });
196
+ return assign(promise, {
197
+ toString: () => String(error),
198
+ name: "Suspense",
199
+ message: error.message,
200
+ stack: error.stack
201
+ });
202
+ }
203
+ function mayRetry(fn) {
204
+ const retry = (err) => {
205
+ if (err instanceof Promise)
206
+ return err.then(compute);
207
+ else
208
+ throw err;
209
+ };
210
+ const compute = () => {
211
+ try {
212
+ const output = fn();
213
+ return output instanceof Promise
214
+ ? output.catch(retry)
215
+ : output;
345
216
  }
346
- return typeof arg2 == "function"
347
- ? addEventListener(this, arg1, arg2, arg3)
348
- : awaitUpdate(this, arg1, arg2);
217
+ catch (err) {
218
+ return retry(err);
219
+ }
220
+ };
221
+ return compute();
222
+ }
223
+
224
+ const Oops$5 = issues({
225
+ BadCallback: () => `Callback for effect-callback may only return a function.`
226
+ });
227
+ function createEffect(source, callback) {
228
+ return control(source, self => {
229
+ let { subject } = self;
230
+ let unSet;
231
+ let busy = false;
232
+ function invoke() {
233
+ if (busy)
234
+ return;
235
+ const output = mayRetry(() => {
236
+ if (typeof unSet == "function")
237
+ unSet();
238
+ unSet = callback.call(subject, subject);
239
+ if (unSet instanceof Promise)
240
+ unSet = undefined;
241
+ if (unSet && typeof unSet !== "function")
242
+ throw Oops$5.BadCallback();
243
+ });
244
+ if (output instanceof Promise) {
245
+ output.finally(() => busy = false);
246
+ busy = true;
247
+ }
248
+ }
249
+ let refresh;
250
+ subject = watch(subject, () => refresh);
251
+ self.followers.add(key => (key === null || refresh === null ? refresh : undefined));
252
+ invoke();
253
+ refresh = invoke;
254
+ return () => refresh = null;
255
+ });
256
+ }
257
+ function createValueEffect(callback) {
258
+ let unSet;
259
+ return function (value) {
260
+ if (typeof unSet == "function")
261
+ unSet(value);
262
+ const out = callback.call(this, value, this);
263
+ if (typeof out == "boolean")
264
+ return out;
265
+ if (!out || out instanceof Promise)
266
+ return;
267
+ if (typeof out == "function")
268
+ unSet = out;
269
+ else
270
+ throw Oops$5.BadCallback();
271
+ };
272
+ }
273
+
274
+ function getMethod(argument, callback, once) {
275
+ if (typeof argument == "function")
276
+ return createEffect(this, argument);
277
+ const self = control(this, true);
278
+ function get(key) {
279
+ const value = self.state[key];
280
+ return value instanceof Model ? value.get() : value;
349
281
  }
350
- get(arg1, arg2) {
351
- const { state } = control(this, true);
352
- function values() {
353
- if (typeof arg1 == "string")
354
- return state[arg1];
355
- if (!arg1)
356
- return Object.assign({}, state);
282
+ const extract = typeof argument == "string"
283
+ ? () => get(argument)
284
+ : () => {
357
285
  const output = {};
358
- for (const key of arg1)
359
- output[key] = state[key];
286
+ for (const key of argument || keys(self.state))
287
+ output[key] = get(key);
360
288
  return output;
289
+ };
290
+ if (typeof callback != "function")
291
+ return extract();
292
+ const select = typeof argument == "string" ? [argument] : argument;
293
+ if (select)
294
+ for (const key of select)
295
+ try {
296
+ const value = self.subject[key];
297
+ if (!(key in self.state))
298
+ self.watch(key, { value });
299
+ }
300
+ catch (e) {
301
+ // TODO: should this be caught?
302
+ }
303
+ const remove = self.addListener(key => {
304
+ if (!select || select.includes(key)) {
305
+ if (once)
306
+ remove();
307
+ return () => callback(extract(), self.latest);
361
308
  }
362
- return typeof arg2 == "function"
363
- ? this.on(arg1, () => arg2(values()))
364
- : values();
365
- }
366
- set(arg1, arg2) {
367
- const controller = control(this, true);
368
- const { state } = controller;
369
- if (typeof arg1 == "string") {
370
- controller.update(arg1);
371
- if (1 in arguments && arg1 in state)
372
- state[arg1] = arg2;
373
- }
374
- else if (typeof arg1 == "object")
309
+ });
310
+ return remove;
311
+ }
312
+ function setMethod(arg1, arg2) {
313
+ const self = control(this, true);
314
+ const { state } = self;
315
+ let timeout = 0;
316
+ switch (typeof arg1) {
317
+ case "string":
318
+ if (1 in arguments)
319
+ if (arg1 in state)
320
+ state[arg1] = arg2;
321
+ else
322
+ self.watch(arg1, { value: arg2 });
323
+ self.update(arg1);
324
+ break;
325
+ case "object":
375
326
  for (const key in state)
376
327
  if (key in arg1 && (!arg2 || arg2.includes(key))) {
377
328
  state[key] = arg1[key];
378
- controller.update(key);
329
+ self.update(key);
379
330
  }
380
- return this.on(0);
331
+ break;
332
+ default:
333
+ timeout = arg1;
334
+ }
335
+ return new Promise((resolve) => {
336
+ if (!self.frame.size && timeout === 0)
337
+ resolve(false);
338
+ const remove = self.addListener(() => {
339
+ remove();
340
+ return () => resolve(self.latest);
341
+ });
342
+ if (timeout > 0)
343
+ setTimeout(() => {
344
+ remove();
345
+ resolve(false);
346
+ }, timeout);
347
+ });
348
+ }
349
+
350
+ class Model {
351
+ constructor(id) {
352
+ new Control(this, id);
381
353
  }
382
354
  /** Mark this instance for garbage collection. */
383
355
  null() {
@@ -400,23 +372,48 @@ class Model {
400
372
  *
401
373
  * Will determine if provided class is a subtype of this one.
402
374
  */
403
- static isTypeof(maybe) {
375
+ static is(maybe) {
404
376
  return (typeof maybe == "function" &&
405
377
  maybe.prototype instanceof this);
406
378
  }
407
379
  }
408
380
  Model.get = get$1;
409
381
  Model.use = use$1;
410
- defineProperty(Model.prototype, "toString", {
411
- configurable: true,
412
- value() {
413
- return `${this.constructor.name}-${control(this).id}`;
414
- }
415
- });
416
382
  defineProperty(Model, "toString", {
417
383
  value() {
418
384
  return this.name;
419
385
  }
386
+ });
387
+ defineProperties(Model.prototype, {
388
+ get: { value: getMethod },
389
+ set: { value: setMethod },
390
+ toString: {
391
+ configurable: true,
392
+ value() {
393
+ return `${this.constructor.name}-${control(this).id}`;
394
+ }
395
+ }
396
+ });
397
+ /* TODO: Remove below on 1.0.0 release. */
398
+ defineProperty(Model, "isTypeof", {
399
+ get() {
400
+ throw new Error("Model.isTypeof method was renamed. Use Model.is instead.");
401
+ }
402
+ });
403
+ defineProperties(Model.prototype, {
404
+ is: {
405
+ configurable: true,
406
+ get() {
407
+ throw new Error("Model.is property is now is only available from a hook.");
408
+ }
409
+ },
410
+ on: {
411
+ configurable: true,
412
+ writable: true,
413
+ value() {
414
+ throw new Error("Model.on() is deprecated. Use Model.get or Model.set instead.");
415
+ }
416
+ }
420
417
  });
421
418
 
422
419
  const Oops$4 = issues({
@@ -433,10 +430,12 @@ class Control {
433
430
  this.frame = new Set();
434
431
  this.followers = new Set();
435
432
  this.observers = new Map([["", this.followers]]);
436
- this.id = typeof id == "undefined" ? uid() : id;
437
433
  this.subject = subject;
438
434
  REGISTER.set(subject, this);
439
- PENDING$1.set(this, new Set(Control.ready));
435
+ if (id !== false) {
436
+ this.id = id === undefined ? uid() : id;
437
+ PENDING$1.set(this, new Set(Control.ready));
438
+ }
440
439
  }
441
440
  add(key) {
442
441
  const { value } = getOwnPropertyDescriptor(this.subject, key);
@@ -536,7 +535,7 @@ Control.for = control;
536
535
  Control.apply = apply;
537
536
  Control.watch = watch;
538
537
  function control(subject, ready) {
539
- const control = REGISTER.get(subject.is);
538
+ const control = REGISTER.get(subject);
540
539
  const pending = PENDING$1.get(control);
541
540
  if (typeof ready == "function") {
542
541
  if (!pending)
@@ -589,11 +588,14 @@ function parent(child, assign) {
589
588
  return PARENTS.get(child);
590
589
  PARENTS.set(child, assign);
591
590
  }
592
- function watch(on, cb) {
593
- if (!on.hasOwnProperty("is"))
594
- on = defineProperty(create(on), "is", { value: on });
595
- OBSERVER.set(on, cb);
596
- return on;
591
+ function watch(value, cb) {
592
+ if (!OBSERVER.has(value)) {
593
+ const control = REGISTER.get(value);
594
+ value = defineProperty(create(value), "is", { value });
595
+ REGISTER.set(value, control);
596
+ }
597
+ OBSERVER.set(value, cb);
598
+ return value;
597
599
  }
598
600
  function apply(instruction) {
599
601
  const placeholder = Symbol("instruction");
@@ -655,7 +657,7 @@ function get(arg0, arg1) {
655
657
  let source = cb => cb(subject);
656
658
  if (arg0 instanceof Model)
657
659
  subject = arg0;
658
- else if (Model.isTypeof(arg0)) {
660
+ else if (Model.is(arg0)) {
659
661
  const hasParent = parent(subject);
660
662
  if (!hasParent) {
661
663
  if (arg1 === true)
@@ -901,28 +903,11 @@ function use(input, argument) {
901
903
  });
902
904
  }
903
905
  function manage(next) {
904
- const methods = {
905
- on(key, event) {
906
- const callback = () => event(next[key], key);
907
- if (!(key in next))
908
- control.watch(key, { value: subject[key] });
909
- callback();
910
- return control.addListener(k => {
911
- if (k === key)
912
- return callback;
913
- if (typeof k !== "string")
914
- return k;
915
- });
916
- },
917
- set(key, value) {
918
- if (key in next)
919
- subject[key] = value;
920
- else
921
- control.watch(key, { value });
922
- }
923
- };
924
- const subject = assign(create(next), methods);
925
- const control = new Control(subject);
906
+ const subject = assign(create(next), {
907
+ get: getMethod,
908
+ set: setMethod
909
+ });
910
+ const control = new Control(subject, false);
926
911
  for (const key in control.state = next)
927
912
  control.watch(key, {});
928
913
  return subject;
@@ -987,7 +972,7 @@ class Context {
987
972
  I = new input();
988
973
  }
989
974
  else {
990
- I = input.is;
975
+ I = control(input).subject;
991
976
  T = I.constructor;
992
977
  writable = false;
993
978
  }