@expressive/mvc 0.1.1 → 0.2.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.d.ts +23 -5
- package/lib/index.esm.js +47 -47
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +47 -47
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -112,8 +112,16 @@ interface Model extends Stateful {
|
|
|
112
112
|
* Circular reference to `this` controller.
|
|
113
113
|
*
|
|
114
114
|
* Useful to obtain full reference where one has already destructured.
|
|
115
|
+
*
|
|
116
|
+
* @deprecated will be removed in favor of `is`
|
|
115
117
|
*/
|
|
116
118
|
get: this;
|
|
119
|
+
/**
|
|
120
|
+
* Circular reference to `this` controller.
|
|
121
|
+
*
|
|
122
|
+
* Useful to obtain full reference where one has already destructured.
|
|
123
|
+
*/
|
|
124
|
+
is: this;
|
|
117
125
|
/**
|
|
118
126
|
* Setter utility
|
|
119
127
|
*
|
|
@@ -464,8 +472,10 @@ declare class MVC extends Model {
|
|
|
464
472
|
|
|
465
473
|
declare namespace Consumer {
|
|
466
474
|
type HasProps<E extends Class> = {
|
|
475
|
+
/** @deprecated - use for instead */
|
|
476
|
+
of?: never;
|
|
467
477
|
/** Type of controller to fetch from context. */
|
|
468
|
-
|
|
478
|
+
for: E;
|
|
469
479
|
/**
|
|
470
480
|
* Getter function. Is called on every natural render of this component.
|
|
471
481
|
* Will throw if usable instance cannot be found in context.
|
|
@@ -473,14 +483,18 @@ declare namespace Consumer {
|
|
|
473
483
|
has: (value: InstanceType<E>) => void;
|
|
474
484
|
};
|
|
475
485
|
type GetProps<E extends Class> = {
|
|
486
|
+
/** @deprecated - use for instead */
|
|
487
|
+
of?: never;
|
|
476
488
|
/** Type of controller to fetch from context. */
|
|
477
|
-
|
|
489
|
+
for: E;
|
|
478
490
|
/** Getter function. Is called on every natural render of this component. */
|
|
479
491
|
get: (value: InstanceType<E> | undefined) => void;
|
|
480
492
|
};
|
|
481
493
|
type RenderProps<E extends Class> = {
|
|
494
|
+
/** @deprecated - use for instead */
|
|
495
|
+
of?: never;
|
|
482
496
|
/** Type of controller to fetch from context. */
|
|
483
|
-
|
|
497
|
+
for: E;
|
|
484
498
|
/**
|
|
485
499
|
* Render function, will receive instance of desired controller.
|
|
486
500
|
*
|
|
@@ -513,11 +527,15 @@ declare namespace Provider {
|
|
|
513
527
|
};
|
|
514
528
|
type Existent<E> = E extends Class ? InstanceType<E> : E extends Model ? E : never;
|
|
515
529
|
type NormalProps<E, I = Existent<E>> = {
|
|
516
|
-
|
|
530
|
+
/** @deprecated use for instead. */
|
|
531
|
+
of?: never;
|
|
532
|
+
for: E;
|
|
517
533
|
children: React.ReactNode | ((instance: I) => React.ReactNode);
|
|
518
534
|
} & Model.Compat<I>;
|
|
519
535
|
type MultipleProps<T extends Item> = {
|
|
520
|
-
|
|
536
|
+
/** @deprecated use for instead. */
|
|
537
|
+
of?: never;
|
|
538
|
+
for: Collection<T>;
|
|
521
539
|
children?: React.ReactNode | (() => React.ReactNode);
|
|
522
540
|
} & Model.Compat<Existent<T>>;
|
|
523
541
|
type Props<T extends Item> = MultipleProps<T> | NormalProps<T>;
|
package/lib/index.esm.js
CHANGED
|
@@ -43,10 +43,10 @@ function issues(register) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
const Oops$c = issues({
|
|
46
|
-
|
|
46
|
+
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
47
47
|
});
|
|
48
48
|
function suspend(source, key) {
|
|
49
|
-
const error = Oops$c.
|
|
49
|
+
const error = Oops$c.NotReady(source.subject, key);
|
|
50
50
|
const promise = new Promise(resolve => {
|
|
51
51
|
const release = source.addListener(forKey => {
|
|
52
52
|
if (forKey == key)
|
|
@@ -87,7 +87,7 @@ function mayRetry(fn) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
const Oops$b = issues({
|
|
90
|
-
|
|
90
|
+
BadCallback: () => `Callback for effect-callback may only return a function.`
|
|
91
91
|
});
|
|
92
92
|
const { assign, create, defineProperty, entries, getPrototypeOf, getOwnPropertyNames, getOwnPropertyDescriptor, getOwnPropertySymbols, values } = Object;
|
|
93
93
|
function define(target, key, value) {
|
|
@@ -112,7 +112,7 @@ function createEffect(callback) {
|
|
|
112
112
|
if (unSet instanceof Promise)
|
|
113
113
|
unSet = undefined;
|
|
114
114
|
if (unSet && typeof unSet !== "function")
|
|
115
|
-
throw Oops$b.
|
|
115
|
+
throw Oops$b.BadCallback();
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
function createValueEffect(callback) {
|
|
@@ -128,7 +128,7 @@ function createValueEffect(callback) {
|
|
|
128
128
|
if (typeof out == "function")
|
|
129
129
|
unSet = out;
|
|
130
130
|
else
|
|
131
|
-
throw Oops$b.
|
|
131
|
+
throw Oops$b.BadCallback();
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -148,16 +148,16 @@ const LOCAL = Symbol("LOCAL");
|
|
|
148
148
|
const STATE = Symbol("STATE");
|
|
149
149
|
class Model {
|
|
150
150
|
constructor() {
|
|
151
|
-
|
|
152
|
-
define(this, CONTROL, control);
|
|
151
|
+
define(this, CONTROL, new Controller(this));
|
|
153
152
|
define(this, "get", this);
|
|
153
|
+
define(this, "is", this);
|
|
154
154
|
defineLazy(this, "set", () => {
|
|
155
|
+
const controller = control(this);
|
|
155
156
|
const assign = (key, value) => {
|
|
156
|
-
|
|
157
|
+
controller.update(key, value);
|
|
157
158
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
define(assign, key, control.ref(key));
|
|
159
|
+
for (const key of getOwnPropertyNames(controller.proxy))
|
|
160
|
+
define(assign, key, controller.ref(key));
|
|
161
161
|
return assign;
|
|
162
162
|
});
|
|
163
163
|
}
|
|
@@ -345,10 +345,10 @@ class Subscriber {
|
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
const Oops$a = issues({
|
|
348
|
-
|
|
348
|
+
BadSource: (model, property, got) => `Bad from-instruction provided to ${model}.${property}. Expects an arrow-function or a Model as source. Got ${got}.`,
|
|
349
349
|
PeerNotAllowed: (model, property) => `Attempted to use an instruction result (probably use or tap) as computed source for ${model}.${property}. This is not possible.`,
|
|
350
|
-
|
|
351
|
-
|
|
350
|
+
Failed: (parent, property, initial) => `An exception was thrown while ${initial ? "initializing" : "refreshing"} [${parent}.${property}].`,
|
|
351
|
+
Early: (property) => `Note: Computed values don't run until accessed, except when subscribed to. '${property}' getter may have run earlier than intended.`
|
|
352
352
|
});
|
|
353
353
|
const INIT = new WeakSet();
|
|
354
354
|
const INFO = new WeakMap();
|
|
@@ -375,7 +375,7 @@ function from(source, arg1, arg2) {
|
|
|
375
375
|
}
|
|
376
376
|
// Regular function is too ambiguous so not allowed.
|
|
377
377
|
else
|
|
378
|
-
throw Oops$a.
|
|
378
|
+
throw Oops$a.BadSource(subject, key, source);
|
|
379
379
|
let sub;
|
|
380
380
|
const setter = arg1;
|
|
381
381
|
const info = { key, parent, priority: 1 };
|
|
@@ -390,7 +390,7 @@ function from(source, arg1, arg2) {
|
|
|
390
390
|
return setter.call(sub.proxy, sub.proxy);
|
|
391
391
|
}
|
|
392
392
|
catch (err) {
|
|
393
|
-
Oops$a.
|
|
393
|
+
Oops$a.Failed(subject, key, !!initial).warn();
|
|
394
394
|
throw err;
|
|
395
395
|
}
|
|
396
396
|
}
|
|
@@ -431,7 +431,7 @@ function from(source, arg1, arg2) {
|
|
|
431
431
|
}
|
|
432
432
|
catch (e) {
|
|
433
433
|
if (early)
|
|
434
|
-
Oops$a.
|
|
434
|
+
Oops$a.Early(key).warn();
|
|
435
435
|
throw e;
|
|
436
436
|
}
|
|
437
437
|
finally {
|
|
@@ -615,9 +615,6 @@ function control(subject, cb) {
|
|
|
615
615
|
control = new Controller(subject);
|
|
616
616
|
define(subject, CONTROL, control);
|
|
617
617
|
}
|
|
618
|
-
return ready(control, cb);
|
|
619
|
-
}
|
|
620
|
-
function ready(control, cb) {
|
|
621
618
|
const ready = READY.has(control);
|
|
622
619
|
if (!ready) {
|
|
623
620
|
if (cb) {
|
|
@@ -715,14 +712,14 @@ function forSubscriber(fn) {
|
|
|
715
712
|
}
|
|
716
713
|
|
|
717
714
|
const Oops$8 = issues({
|
|
718
|
-
|
|
715
|
+
DuplicatePending: (key) => `Invoked action ${key} but one is already active.`
|
|
719
716
|
});
|
|
720
717
|
function act(task) {
|
|
721
718
|
return apply(function act(key) {
|
|
722
719
|
let pending = false;
|
|
723
720
|
const invoke = (...args) => __awaiter(this, void 0, void 0, function* () {
|
|
724
721
|
if (pending)
|
|
725
|
-
return Promise.reject(Oops$8.
|
|
722
|
+
return Promise.reject(Oops$8.DuplicatePending(key));
|
|
726
723
|
pending = true;
|
|
727
724
|
this.update(key);
|
|
728
725
|
try {
|
|
@@ -828,8 +825,8 @@ function createRef(key, cb) {
|
|
|
828
825
|
const Oops$7 = issues({
|
|
829
826
|
NonOptional: (Parent, key) => `Property ${Parent}.${key} is marked as required.`,
|
|
830
827
|
BadFactory: () => `Set instruction can only accept a factory or undefined.`,
|
|
831
|
-
|
|
832
|
-
|
|
828
|
+
NotReady: (model, key) => `Value ${model}.${key} value is not yet available.`,
|
|
829
|
+
Failed: (model, key) => `Generating initial value for ${model}.${key} failed.`
|
|
833
830
|
});
|
|
834
831
|
function set(factory, argument) {
|
|
835
832
|
return apply(function set(key) {
|
|
@@ -880,7 +877,7 @@ function pendingFactory(parent, key, fn, required) {
|
|
|
880
877
|
const suspend = () => {
|
|
881
878
|
if (required === false)
|
|
882
879
|
return undefined;
|
|
883
|
-
const issue = Oops$7.
|
|
880
|
+
const issue = Oops$7.NotReady(subject, key);
|
|
884
881
|
Object.assign(pending, {
|
|
885
882
|
message: issue.message,
|
|
886
883
|
stack: issue.stack
|
|
@@ -892,7 +889,7 @@ function pendingFactory(parent, key, fn, required) {
|
|
|
892
889
|
init();
|
|
893
890
|
}
|
|
894
891
|
catch (err) {
|
|
895
|
-
Oops$7.
|
|
892
|
+
Oops$7.Failed(subject, key).warn();
|
|
896
893
|
throw err;
|
|
897
894
|
}
|
|
898
895
|
return () => {
|
|
@@ -911,8 +908,8 @@ function pendingFactory(parent, key, fn, required) {
|
|
|
911
908
|
|
|
912
909
|
const Parent = new WeakMap();
|
|
913
910
|
const Oops$6 = issues({
|
|
914
|
-
|
|
915
|
-
|
|
911
|
+
Required: (expects, child) => `New ${child} created standalone but requires parent of type ${expects}. Did you remember to create via use(${child})?`,
|
|
912
|
+
Unexpected: (expects, child, got) => `New ${child} created as child of ${got}, but must be instanceof ${expects}.`,
|
|
916
913
|
});
|
|
917
914
|
function parent(Expects, required) {
|
|
918
915
|
return apply(function parent() {
|
|
@@ -921,10 +918,10 @@ function parent(Expects, required) {
|
|
|
921
918
|
const expected = Expects.name;
|
|
922
919
|
if (!value) {
|
|
923
920
|
if (required !== false)
|
|
924
|
-
throw Oops$6.
|
|
921
|
+
throw Oops$6.Required(expected, child);
|
|
925
922
|
}
|
|
926
923
|
else if (!(value instanceof Expects))
|
|
927
|
-
throw Oops$6.
|
|
924
|
+
throw Oops$6.Unexpected(expected, child, value);
|
|
928
925
|
return { value };
|
|
929
926
|
});
|
|
930
927
|
}
|
|
@@ -1029,19 +1026,19 @@ class Lookup {
|
|
|
1029
1026
|
const LookupContext = React.createContext(new Lookup());
|
|
1030
1027
|
const useLookup = () => React.useContext(LookupContext);
|
|
1031
1028
|
const Oops$4 = issues({
|
|
1032
|
-
|
|
1029
|
+
NotFound: (name) => `Couldn't find ${name} in context; did you forget to use a Provider?`
|
|
1033
1030
|
});
|
|
1034
1031
|
function useLocal(Type, arg) {
|
|
1035
1032
|
const instance = useLookup().get(Type);
|
|
1036
1033
|
if (!instance && arg !== false)
|
|
1037
|
-
throw Oops$4.
|
|
1034
|
+
throw Oops$4.NotFound(Type.name);
|
|
1038
1035
|
return typeof arg == "string" ?
|
|
1039
1036
|
instance[arg] :
|
|
1040
1037
|
instance;
|
|
1041
1038
|
}
|
|
1042
1039
|
|
|
1043
1040
|
const Oops$3 = issues({
|
|
1044
|
-
|
|
1041
|
+
NotAllowed: (parent, child) => `Global '${parent}' attempted to attach '${child}' but it is not also a singleton.`,
|
|
1045
1042
|
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.`
|
|
1046
1043
|
});
|
|
1047
1044
|
const PendingContext = new WeakMap();
|
|
@@ -1055,7 +1052,7 @@ function tap(type, argument) {
|
|
|
1055
1052
|
};
|
|
1056
1053
|
const { subject } = this;
|
|
1057
1054
|
if ("set" in subject.constructor)
|
|
1058
|
-
throw Oops$3.
|
|
1055
|
+
throw Oops$3.NotAllowed(subject, type.name);
|
|
1059
1056
|
getPending(subject).push(context => {
|
|
1060
1057
|
let instance = context.get(type);
|
|
1061
1058
|
if (typeof argument == "function") {
|
|
@@ -1280,25 +1277,28 @@ class MVC extends Model {
|
|
|
1280
1277
|
}
|
|
1281
1278
|
|
|
1282
1279
|
const Oops$2 = issues({
|
|
1283
|
-
|
|
1280
|
+
BadProps: () => `Provider expects either a render function, 'get' or 'has' props.`
|
|
1284
1281
|
});
|
|
1285
1282
|
function Consumer(props) {
|
|
1286
|
-
const { get, has, children,
|
|
1283
|
+
const { get, has, children, for: type } = props;
|
|
1287
1284
|
if (typeof children == "function")
|
|
1288
|
-
return children(useTap(
|
|
1285
|
+
return children(useTap(type));
|
|
1289
1286
|
const callback = has || get;
|
|
1290
1287
|
if (typeof callback == "function")
|
|
1291
|
-
callback(useLocal(
|
|
1288
|
+
callback(useLocal(type, !!has));
|
|
1292
1289
|
else
|
|
1293
|
-
throw Oops$2.
|
|
1290
|
+
throw Oops$2.BadProps();
|
|
1294
1291
|
return null;
|
|
1295
1292
|
}
|
|
1296
1293
|
|
|
1297
1294
|
const Oops$1 = issues({
|
|
1298
|
-
|
|
1295
|
+
NoType: () => "Provider 'of' prop must be Model, typeof Model or a collection of them.",
|
|
1296
|
+
PropDeprecated: () => "Provider `of` prop is deprecated, use `for` instead."
|
|
1299
1297
|
});
|
|
1300
1298
|
function Provider(props) {
|
|
1301
|
-
|
|
1299
|
+
if ("of" in props)
|
|
1300
|
+
throw Oops$1.PropDeprecated();
|
|
1301
|
+
const context = useNewContext(props.for);
|
|
1302
1302
|
const render = props.children;
|
|
1303
1303
|
useAppliedProps(context, props);
|
|
1304
1304
|
React.useLayoutEffect(() => () => context.pop(), []);
|
|
@@ -1310,7 +1310,7 @@ function useNewContext(inject) {
|
|
|
1310
1310
|
const from = useLookup();
|
|
1311
1311
|
return React.useMemo(() => {
|
|
1312
1312
|
if (!inject)
|
|
1313
|
-
throw Oops$1.
|
|
1313
|
+
throw Oops$1.NoType();
|
|
1314
1314
|
const context = from.push(inject);
|
|
1315
1315
|
for (const instance of context.local)
|
|
1316
1316
|
if (instance)
|
|
@@ -1324,7 +1324,7 @@ function useAppliedProps(within, props) {
|
|
|
1324
1324
|
const targets = within.local;
|
|
1325
1325
|
return function integrate(props) {
|
|
1326
1326
|
for (const [key, value] of entries(props))
|
|
1327
|
-
if (
|
|
1327
|
+
if (["of", "for", "children"].indexOf(key) < 0)
|
|
1328
1328
|
for (const into of targets)
|
|
1329
1329
|
if (into && key in into)
|
|
1330
1330
|
into[key] = value;
|
|
@@ -1345,15 +1345,15 @@ function RenderFunction(props) {
|
|
|
1345
1345
|
}
|
|
1346
1346
|
|
|
1347
1347
|
const Oops = issues({
|
|
1348
|
-
|
|
1349
|
-
|
|
1348
|
+
DoesNotExist: (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.`,
|
|
1349
|
+
AlreadyExists: (name) => `Shared instance of ${name} already exists! Consider unmounting existing, or use ${name}.reset() to force-delete it.`
|
|
1350
1350
|
});
|
|
1351
1351
|
const Active = new WeakMap();
|
|
1352
1352
|
class Global extends MVC {
|
|
1353
1353
|
static create(...args) {
|
|
1354
1354
|
const Type = this;
|
|
1355
1355
|
if (Active.has(Type))
|
|
1356
|
-
throw Oops.
|
|
1356
|
+
throw Oops.AlreadyExists(this.name);
|
|
1357
1357
|
const instance = super.create(...args);
|
|
1358
1358
|
Active.set(Type, instance);
|
|
1359
1359
|
return instance;
|
|
@@ -1371,7 +1371,7 @@ class Global extends MVC {
|
|
|
1371
1371
|
static get(arg) {
|
|
1372
1372
|
const instance = Active.get(this);
|
|
1373
1373
|
if (!instance && arg !== false)
|
|
1374
|
-
throw Oops.
|
|
1374
|
+
throw Oops.DoesNotExist(this.name);
|
|
1375
1375
|
return (typeof arg == "string" ?
|
|
1376
1376
|
instance[arg] :
|
|
1377
1377
|
instance);
|