reactronic 0.93.25025 → 0.93.25026
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/README.md +19 -18
- package/build/dist/source/Options.d.ts +2 -2
- package/build/dist/source/Options.js +2 -2
- package/build/dist/source/ReactiveSystem.d.ts +6 -6
- package/build/dist/source/ReactiveSystem.js +8 -8
- package/build/dist/source/Ref.d.ts +1 -1
- package/build/dist/source/Ref.js +4 -4
- package/build/dist/source/api.d.ts +2 -2
- package/build/dist/source/api.js +2 -2
- package/build/dist/source/core/Operation.js +8 -8
- package/build/dist/source/core/ReactiveNode.js +9 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,14 +28,15 @@ concepts:
|
|
|
28
28
|
- **Triggering Objects** - a set of objects that store
|
|
29
29
|
data of an application (state) and cause reactions
|
|
30
30
|
upon their changes;
|
|
31
|
-
- **Atomic
|
|
32
|
-
triggering objects in atomic way ("all or
|
|
33
|
-
|
|
31
|
+
- **Atomic Block** - a code block that makes changes
|
|
32
|
+
in triggering objects in atomic way ("all or
|
|
33
|
+
nothing");
|
|
34
|
+
- **Reaction** - a function that is
|
|
34
35
|
(re-)executed in response to changes made in
|
|
35
36
|
triggering objects by atomic actions;
|
|
36
|
-
- **
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
- **Cache** - a function which result is remembered
|
|
38
|
+
and, if becomes obsolete, causes function to
|
|
39
|
+
re-execute on-demand.
|
|
39
40
|
|
|
40
41
|
Demo application built with Reactronic: https://nevod.io/#/playground.
|
|
41
42
|
Source code of the demo: https://gitlab.com/nezaboodka/nevod.web.public/-/blob/master/README.md.
|
|
@@ -57,7 +58,7 @@ class Demo extends TriggeringObject {
|
|
|
57
58
|
this.email = email
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
@
|
|
61
|
+
@reaction
|
|
61
62
|
printContact(): void {
|
|
62
63
|
// depends on `name` and `email` and reacts to their changes
|
|
63
64
|
if (this.email.indexOf('@') >= 0)
|
|
@@ -83,12 +84,12 @@ class Demo extends TriggeringObject {
|
|
|
83
84
|
name: string = 'Nezaboodka Software'
|
|
84
85
|
email: string = 'contact@nezaboodka.com'
|
|
85
86
|
|
|
86
|
-
@
|
|
87
|
+
@cache
|
|
87
88
|
get contact(): string {
|
|
88
89
|
return this.name + ' <' + this.email + '>'
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
@
|
|
92
|
+
@reaction
|
|
92
93
|
printContact(): void {
|
|
93
94
|
if (this.contact !== '')
|
|
94
95
|
Console.log(this.contact)
|
|
@@ -192,7 +193,7 @@ execution.
|
|
|
192
193
|
|
|
193
194
|
``` tsx
|
|
194
195
|
class MyView extends Component<{model: MyModel}> {
|
|
195
|
-
@
|
|
196
|
+
@cache
|
|
196
197
|
render(): React.JSX.Element {
|
|
197
198
|
return (
|
|
198
199
|
<div>
|
|
@@ -206,12 +207,12 @@ class MyView extends Component<{model: MyModel}> {
|
|
|
206
207
|
|
|
207
208
|
``` tsx
|
|
208
209
|
class Component<P> extends React.Component<P> {
|
|
209
|
-
@
|
|
210
|
+
@cache
|
|
210
211
|
render(): React.JSX.Element {
|
|
211
212
|
throw new Error('render method is undefined')
|
|
212
213
|
}
|
|
213
214
|
|
|
214
|
-
@
|
|
215
|
+
@reaction // called in response to changes
|
|
215
216
|
ensureUpToDate(): void {
|
|
216
217
|
if (this.shouldComponentUpdate()) {
|
|
217
218
|
// Ask React to re-render
|
|
@@ -330,12 +331,12 @@ class TriggeringObject { }
|
|
|
330
331
|
function trigger(boolean) // field only
|
|
331
332
|
function trigger(proto, prop) // field only
|
|
332
333
|
function atomic(proto, prop, pd) // method only
|
|
333
|
-
function
|
|
334
|
-
function
|
|
334
|
+
function reaction(proto, prop, pd) // method only
|
|
335
|
+
function cache(proto, prop, pd) // method only
|
|
335
336
|
function options(value: Partial<MemberOptions>): F<any>
|
|
336
337
|
|
|
337
|
-
function
|
|
338
|
-
function
|
|
338
|
+
function runNonReactively<T>(func: F<T>, ...args: any[]): T
|
|
339
|
+
function runSensitively<T>(sensitivity: Sensitivity, func: F<T>, ...args: any[]): T
|
|
339
340
|
|
|
340
341
|
// SnapshotOptions, MemberOptions, Kind, Reentrance, Indicator, LoggingOptions, ProfilingOptions
|
|
341
342
|
|
|
@@ -363,8 +364,8 @@ type MemberOptions = {
|
|
|
363
364
|
enum Kind {
|
|
364
365
|
plain = 0,
|
|
365
366
|
atomic = 1,
|
|
366
|
-
|
|
367
|
-
|
|
367
|
+
reaction = 2,
|
|
368
|
+
cache = 3
|
|
368
369
|
}
|
|
369
370
|
|
|
370
371
|
enum Reentrance {
|
|
@@ -3,8 +3,8 @@ export var Kind;
|
|
|
3
3
|
(function (Kind) {
|
|
4
4
|
Kind[Kind["plain"] = 0] = "plain";
|
|
5
5
|
Kind[Kind["atomic"] = 1] = "atomic";
|
|
6
|
-
Kind[Kind["
|
|
7
|
-
Kind[Kind["
|
|
6
|
+
Kind[Kind["reaction"] = 2] = "reaction";
|
|
7
|
+
Kind[Kind["cache"] = 3] = "cache";
|
|
8
8
|
})(Kind || (Kind = {}));
|
|
9
9
|
export var Reentrance;
|
|
10
10
|
(function (Reentrance) {
|
|
@@ -17,14 +17,14 @@ export declare class ReactiveSystem {
|
|
|
17
17
|
static getLoggingHint<T extends object>(obj: T, full?: boolean): string | undefined;
|
|
18
18
|
static setProfilingMode(isOn: boolean, options?: Partial<ProfilingOptions>): void;
|
|
19
19
|
}
|
|
20
|
-
export declare function
|
|
21
|
-
export declare function
|
|
22
|
-
export declare function
|
|
23
|
-
export declare function
|
|
24
|
-
export declare function
|
|
20
|
+
export declare function runAtomically<T>(func: F<T>, ...args: any[]): T;
|
|
21
|
+
export declare function runAtomically<T>(options: SnapshotOptions, func: F<T>, ...args: any[]): T;
|
|
22
|
+
export declare function runNonReactively<T>(func: F<T>, ...args: any[]): T;
|
|
23
|
+
export declare function runSensitively<T>(sensitivity: boolean, func: F<T>, ...args: any[]): T;
|
|
24
|
+
export declare function runContextually<T>(p: Promise<T>): Promise<T>;
|
|
25
25
|
export declare function trigger(enabled: boolean): (proto: object, prop: PropertyKey) => any;
|
|
26
26
|
export declare function trigger<T>(proto: object, prop: PropertyKey): any;
|
|
27
|
-
export declare function
|
|
27
|
+
export declare function atomic(proto: object, prop: PropertyKey, pd: PropertyDescriptor): any;
|
|
28
28
|
export declare function reaction(proto: object, prop: PropertyKey, pd: PropertyDescriptor): any;
|
|
29
29
|
export declare function cache(proto: object, prop: PropertyKey, pd: PropertyDescriptor): any;
|
|
30
30
|
export declare function options(value: Partial<MemberOptions>): F<any>;
|
|
@@ -22,7 +22,7 @@ export class ReactiveSystem {
|
|
|
22
22
|
static getLoggingHint(obj, full = false) { return ObjectHandle.getHint(obj, full); }
|
|
23
23
|
static setProfilingMode(isOn, options) { Mvcc.setProfilingMode(isOn, options); }
|
|
24
24
|
}
|
|
25
|
-
export function
|
|
25
|
+
export function runAtomically(p1, p2, p3) {
|
|
26
26
|
if (p1 instanceof Function) {
|
|
27
27
|
if (p2 !== undefined)
|
|
28
28
|
return Transaction.run(null, p1, ...p2);
|
|
@@ -36,13 +36,13 @@ export function atomicRun(p1, p2, p3) {
|
|
|
36
36
|
return Transaction.run(p1, p2);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
export function
|
|
39
|
+
export function runNonReactively(func, ...args) {
|
|
40
40
|
return OperationImpl.proceedWithinGivenLaunch(undefined, func, ...args);
|
|
41
41
|
}
|
|
42
|
-
export function
|
|
42
|
+
export function runSensitively(sensitivity, func, ...args) {
|
|
43
43
|
return Mvcc.sensitive(sensitivity, func, ...args);
|
|
44
44
|
}
|
|
45
|
-
export function
|
|
45
|
+
export function runContextually(p) {
|
|
46
46
|
throw new Error("not implemented yet");
|
|
47
47
|
}
|
|
48
48
|
export function trigger(protoOrEnabled, prop) {
|
|
@@ -54,16 +54,16 @@ export function trigger(protoOrEnabled, prop) {
|
|
|
54
54
|
else
|
|
55
55
|
return Mvcc.decorateData(true, protoOrEnabled, prop);
|
|
56
56
|
}
|
|
57
|
-
export function
|
|
57
|
+
export function atomic(proto, prop, pd) {
|
|
58
58
|
const opts = {
|
|
59
59
|
kind: Kind.atomic,
|
|
60
60
|
isolation: Isolation.joinToCurrentTransaction,
|
|
61
61
|
};
|
|
62
|
-
return Mvcc.decorateOperation(true,
|
|
62
|
+
return Mvcc.decorateOperation(true, atomic, opts, proto, prop, pd);
|
|
63
63
|
}
|
|
64
64
|
export function reaction(proto, prop, pd) {
|
|
65
65
|
const opts = {
|
|
66
|
-
kind: Kind.
|
|
66
|
+
kind: Kind.reaction,
|
|
67
67
|
isolation: Isolation.joinAsNestedTransaction,
|
|
68
68
|
throttling: -1,
|
|
69
69
|
};
|
|
@@ -71,7 +71,7 @@ export function reaction(proto, prop, pd) {
|
|
|
71
71
|
}
|
|
72
72
|
export function cache(proto, prop, pd) {
|
|
73
73
|
const opts = {
|
|
74
|
-
kind: Kind.
|
|
74
|
+
kind: Kind.cache,
|
|
75
75
|
isolation: Isolation.joinToCurrentTransaction,
|
|
76
76
|
noSideEffects: true,
|
|
77
77
|
};
|
|
@@ -27,7 +27,7 @@ export declare class Ref<T = any> {
|
|
|
27
27
|
constructor(owner: any, name: string, index?: number);
|
|
28
28
|
get variable(): T;
|
|
29
29
|
set variable(value: T);
|
|
30
|
-
|
|
30
|
+
nonReactively(): T;
|
|
31
31
|
observe(): T;
|
|
32
32
|
unobserve(): T;
|
|
33
33
|
static sameRefs(v1: Ref, v2: Ref): boolean;
|
package/build/dist/source/Ref.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { runAtomically, runNonReactively } from "./ReactiveSystem.js";
|
|
2
2
|
export function refs(owner) {
|
|
3
3
|
return new Proxy(owner, RefGettingProxy);
|
|
4
4
|
}
|
|
@@ -27,8 +27,8 @@ export class Ref {
|
|
|
27
27
|
else
|
|
28
28
|
this.owner[this.name][this.index] = value;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
return
|
|
30
|
+
nonReactively() {
|
|
31
|
+
return runNonReactively(() => this.variable);
|
|
32
32
|
}
|
|
33
33
|
observe() {
|
|
34
34
|
return this.variable;
|
|
@@ -52,7 +52,7 @@ export class ToggleRef extends Ref {
|
|
|
52
52
|
toggle() {
|
|
53
53
|
const o = this.owner;
|
|
54
54
|
const p = this.name;
|
|
55
|
-
|
|
55
|
+
runAtomically({ hint: `toggle ${o.constructor.name}.${p}` }, () => {
|
|
56
56
|
const v = o[p];
|
|
57
57
|
const isOn = v === this.valueOn || (v instanceof Ref && this.valueOn instanceof Ref &&
|
|
58
58
|
Ref.sameRefs(v, this.valueOn));
|
|
@@ -16,8 +16,8 @@ export { Changeset } from "./core/Changeset.js";
|
|
|
16
16
|
export { Transaction } from "./core/Transaction.js";
|
|
17
17
|
export { Indicator } from "./core/Indicator.js";
|
|
18
18
|
export { Journal } from "./core/Journal.js";
|
|
19
|
-
export {
|
|
20
|
-
export { ReactiveSystem, trigger,
|
|
19
|
+
export { runAtomically, runNonReactively, runSensitively, runContextually } from "./ReactiveSystem.js";
|
|
20
|
+
export { ReactiveSystem, trigger, atomic, reaction, cache, options } from "./ReactiveSystem.js";
|
|
21
21
|
export { ReactiveLoop } from "./ReactiveLoop.js";
|
|
22
22
|
export { ReactiveNode, Mode, Priority, BaseDriver, ReactiveNodeVariable } from "./core/ReactiveNode.js";
|
|
23
23
|
export type { Script, ScriptAsync, Handler, ReactiveNodeDecl, ReactiveNodeDriver, ReactiveNodeContext } from "./core/ReactiveNode.js";
|
package/build/dist/source/api.js
CHANGED
|
@@ -12,7 +12,7 @@ export { Changeset } from "./core/Changeset.js";
|
|
|
12
12
|
export { Transaction } from "./core/Transaction.js";
|
|
13
13
|
export { Indicator } from "./core/Indicator.js";
|
|
14
14
|
export { Journal } from "./core/Journal.js";
|
|
15
|
-
export {
|
|
16
|
-
export { ReactiveSystem, trigger,
|
|
15
|
+
export { runAtomically, runNonReactively, runSensitively, runContextually } from "./ReactiveSystem.js";
|
|
16
|
+
export { ReactiveSystem, trigger, atomic, reaction, cache, options } from "./ReactiveSystem.js";
|
|
17
17
|
export { ReactiveLoop } from "./ReactiveLoop.js";
|
|
18
18
|
export { ReactiveNode, Mode, Priority, BaseDriver, ReactiveNodeVariable } from "./core/ReactiveNode.js";
|
|
@@ -294,15 +294,15 @@ class Launch extends FieldVersion {
|
|
|
294
294
|
changeset.id === this.lastEditorChangesetId;
|
|
295
295
|
if (!skip) {
|
|
296
296
|
const why = `${Dump.snapshot2(h, changeset, fk, trigger)} ◀◀ ${outer}`;
|
|
297
|
-
const
|
|
297
|
+
const isReaction = this.options.kind === Kind.reaction;
|
|
298
298
|
this.obsoleteDueTo = why;
|
|
299
299
|
this.obsoleteSince = since;
|
|
300
300
|
if (Log.isOn && (Log.opt.obsolete || ((_a = this.options.logging) === null || _a === void 0 ? void 0 : _a.obsolete)))
|
|
301
|
-
Log.write(Log.opt.transaction && !Changeset.current().sealed ? "║" : " ",
|
|
301
|
+
Log.write(Log.opt.transaction && !Changeset.current().sealed ? "║" : " ", isReaction ? "█" : "▒", isReaction && changeset === EMPTY_OBJECT_VERSION.changeset
|
|
302
302
|
? `${this.hint()} is reactive and will run automatically (order ${this.options.order})`
|
|
303
|
-
: `${this.hint()} is obsolete due to ${Dump.snapshot2(h, changeset, fk)} since s${since}${
|
|
303
|
+
: `${this.hint()} is obsolete due to ${Dump.snapshot2(h, changeset, fk)} since s${since}${isReaction ? ` and will run automatically (order ${this.options.order})` : ""}`);
|
|
304
304
|
this.unsubscribeFromAllTriggers();
|
|
305
|
-
if (
|
|
305
|
+
if (isReaction)
|
|
306
306
|
collector.push(this);
|
|
307
307
|
else
|
|
308
308
|
(_b = this.reactions) === null || _b === void 0 ? void 0 : _b.forEach(s => s.markObsoleteDueTo(this, this.operation.fieldKey, this.changeset, this.operation.ownerHandle, why, since, collector));
|
|
@@ -326,14 +326,14 @@ class Launch extends FieldVersion {
|
|
|
326
326
|
const launch = this.operation.reuseOrRelaunch(false, undefined);
|
|
327
327
|
if (launch.result instanceof Promise)
|
|
328
328
|
launch.result.catch(error => {
|
|
329
|
-
if (launch.options.kind === Kind.
|
|
329
|
+
if (launch.options.kind === Kind.reaction)
|
|
330
330
|
misuse(`reactive function ${launch.hint()} failed and will not run anymore: ${error}`, error);
|
|
331
331
|
});
|
|
332
332
|
}
|
|
333
333
|
catch (e) {
|
|
334
334
|
if (!nothrow)
|
|
335
335
|
throw e;
|
|
336
|
-
else if (this.options.kind === Kind.
|
|
336
|
+
else if (this.options.kind === Kind.reaction)
|
|
337
337
|
misuse(`reactive ${this.hint()} failed and will not run anymore: ${e}`, e);
|
|
338
338
|
}
|
|
339
339
|
}
|
|
@@ -652,11 +652,11 @@ class Launch extends FieldVersion {
|
|
|
652
652
|
const rx = launch ? launch.operation : new OperationImpl(EMPTY_HANDLE, fk);
|
|
653
653
|
const opts = launch ? launch.options : OptionsImpl.INITIAL;
|
|
654
654
|
initial[fk] = launch = new Launch(Transaction.current, rx, EMPTY_OBJECT_VERSION.changeset, new OptionsImpl(getter, setter, opts, options, implicit), false);
|
|
655
|
-
if (launch.options.kind === Kind.
|
|
655
|
+
if (launch.options.kind === Kind.reaction && launch.options.throttling < Number.MAX_SAFE_INTEGER) {
|
|
656
656
|
const reactive = Meta.acquire(proto, Meta.Reactive);
|
|
657
657
|
reactive[fk] = launch;
|
|
658
658
|
}
|
|
659
|
-
else if (launch.options.kind === Kind.
|
|
659
|
+
else if (launch.options.kind === Kind.reaction && launch.options.throttling >= Number.MAX_SAFE_INTEGER) {
|
|
660
660
|
const reactive = Meta.getFrom(proto, Meta.Reactive);
|
|
661
661
|
delete reactive[fk];
|
|
662
662
|
}
|
|
@@ -22,7 +22,7 @@ import { emitLetters, getCallerInfo, proceedSyncOrAsync } from "../util/Utils.js
|
|
|
22
22
|
import { Isolation, Reentrance } from "../Options.js";
|
|
23
23
|
import { TriggeringObject } from "../core/Mvcc.js";
|
|
24
24
|
import { Transaction } from "../core/Transaction.js";
|
|
25
|
-
import { ReactiveSystem, options, trigger, reaction,
|
|
25
|
+
import { ReactiveSystem, options, trigger, reaction, runAtomically, runNonReactively } from "../ReactiveSystem.js";
|
|
26
26
|
export var Mode;
|
|
27
27
|
(function (Mode) {
|
|
28
28
|
Mode[Mode["default"] = 0] = "default";
|
|
@@ -333,13 +333,13 @@ class ReactiveNodeImpl extends ReactiveNode {
|
|
|
333
333
|
static setNodeVariableValue(variable, value) {
|
|
334
334
|
const node = ReactiveNodeImpl.nodeSlot.instance;
|
|
335
335
|
const owner = node.owner;
|
|
336
|
-
const hostCtx =
|
|
336
|
+
const hostCtx = runNonReactively(() => { var _a; return (_a = owner.context) === null || _a === void 0 ? void 0 : _a.value; });
|
|
337
337
|
if (value && value !== hostCtx) {
|
|
338
338
|
if (hostCtx)
|
|
339
339
|
node.outer = owner;
|
|
340
340
|
else
|
|
341
341
|
node.outer = owner.outer;
|
|
342
|
-
|
|
342
|
+
runAtomically({ isolation: Isolation.joinAsNestedTransaction }, () => {
|
|
343
343
|
const ctx = node.context;
|
|
344
344
|
if (ctx) {
|
|
345
345
|
ctx.variable = variable;
|
|
@@ -484,18 +484,18 @@ function triggerScriptRunViaSlot(nodeSlot) {
|
|
|
484
484
|
});
|
|
485
485
|
});
|
|
486
486
|
}
|
|
487
|
-
|
|
487
|
+
runNonReactively(node.script, node.declaration.triggers);
|
|
488
488
|
}
|
|
489
489
|
else if (node.owner !== node)
|
|
490
490
|
runScriptNow(nodeSlot);
|
|
491
491
|
else
|
|
492
|
-
|
|
492
|
+
runAtomically(() => runScriptNow(nodeSlot));
|
|
493
493
|
}
|
|
494
494
|
}
|
|
495
495
|
function mountOrRemountIfNecessary(node) {
|
|
496
496
|
const driver = node.driver;
|
|
497
497
|
if (node.stamp === Number.MAX_SAFE_INTEGER) {
|
|
498
|
-
|
|
498
|
+
runNonReactively(() => {
|
|
499
499
|
node.stamp = Number.MAX_SAFE_INTEGER - 1;
|
|
500
500
|
driver.runPreparation(node);
|
|
501
501
|
if (!node.has(Mode.manualMount)) {
|
|
@@ -506,7 +506,7 @@ function mountOrRemountIfNecessary(node) {
|
|
|
506
506
|
});
|
|
507
507
|
}
|
|
508
508
|
else if (node.isMoved && !node.has(Mode.manualMount) && node.host !== node)
|
|
509
|
-
|
|
509
|
+
runNonReactively(() => driver.runMount(node));
|
|
510
510
|
}
|
|
511
511
|
function runScriptNow(nodeSlot) {
|
|
512
512
|
const node = nodeSlot.instance;
|
|
@@ -539,7 +539,7 @@ function triggerFinalization(nodeSlot, isLeader, individual) {
|
|
|
539
539
|
if (individual && node.key !== node.declaration.key && !driver.isPartition)
|
|
540
540
|
console.log(`WARNING: it is recommended to assign explicit key for conditional element in order to avoid unexpected side effects: ${node.key}`);
|
|
541
541
|
node.stamp = ~node.stamp;
|
|
542
|
-
const childrenAreLeaders =
|
|
542
|
+
const childrenAreLeaders = runNonReactively(() => driver.runFinalization(node, isLeader));
|
|
543
543
|
if (node.has(Mode.autonomous)) {
|
|
544
544
|
nodeSlot.aux = undefined;
|
|
545
545
|
const last = gLastToDispose;
|
|
@@ -548,7 +548,7 @@ function triggerFinalization(nodeSlot, isLeader, individual) {
|
|
|
548
548
|
else
|
|
549
549
|
gFirstToDispose = gLastToDispose = nodeSlot;
|
|
550
550
|
if (gFirstToDispose === nodeSlot)
|
|
551
|
-
|
|
551
|
+
runAtomically({ isolation: Isolation.disjoinForInternalDisposal, hint: `runDisposalLoop(initiator=${nodeSlot.instance.key})` }, () => {
|
|
552
552
|
void runDisposalLoop().then(NOP, error => console.log(error));
|
|
553
553
|
});
|
|
554
554
|
}
|