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