reactronic 0.92.25004 → 0.92.25006
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 +139 -119
- package/build/dist/source/Clock.js +3 -3
- package/build/dist/source/Options.d.ts +3 -3
- package/build/dist/source/Options.js +3 -3
- package/build/dist/source/Reaction.d.ts +1 -1
- package/build/dist/source/Reaction.js +4 -4
- package/build/dist/source/ReactiveSystem.d.ts +7 -5
- package/build/dist/source/ReactiveSystem.js +27 -9
- package/build/dist/source/Ref.js +2 -2
- package/build/dist/source/api.d.ts +2 -2
- package/build/dist/source/api.js +2 -2
- package/build/dist/source/core/Operation.js +13 -13
- package/build/dist/source/core/ReactiveNode.js +4 -4
- package/build/dist/source/core/Transaction.d.ts +5 -5
- package/build/dist/source/core/Transaction.js +21 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,12 +24,12 @@ Transactional reactivity is based on four fundamental concepts:
|
|
|
24
24
|
|
|
25
25
|
- **Observable Objects** - a set of objects that store data of an
|
|
26
26
|
application (state);
|
|
27
|
-
- **
|
|
27
|
+
- **Atomic Action** - a function that makes changes in observable
|
|
28
28
|
objects in atomic way ("all or nothing");
|
|
29
|
-
- **
|
|
29
|
+
- **Reactive Process** - a function that is executed automatically in
|
|
30
30
|
response to changes made by a transaction;
|
|
31
|
-
- **
|
|
32
|
-
obsolete,
|
|
31
|
+
- **Cached Result** - function result that is remembered and, if the becomes
|
|
32
|
+
obsolete, causes its function to re-execute on-demand.
|
|
33
33
|
|
|
34
34
|
Demo application built with Reactronic: https://nevod.io/#/playground.
|
|
35
35
|
Source code of the demo: https://gitlab.com/nezaboodka/nevod.web.public/-/blob/master/README.md.
|
|
@@ -45,13 +45,13 @@ class Demo extends ObservableObject {
|
|
|
45
45
|
name: string = 'Nezaboodka Software'
|
|
46
46
|
email: string = 'contact@nezaboodka.com'
|
|
47
47
|
|
|
48
|
-
@
|
|
48
|
+
@atomicAction
|
|
49
49
|
saveContact(name: string, email: string): void {
|
|
50
50
|
this.name = name
|
|
51
51
|
this.email = email
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
@
|
|
54
|
+
@reactiveProcess
|
|
55
55
|
printContact(): void {
|
|
56
56
|
// depends on `name` and `email` and reacts to their changes
|
|
57
57
|
if (this.email.indexOf('@') >= 0)
|
|
@@ -63,25 +63,26 @@ class Demo extends ObservableObject {
|
|
|
63
63
|
|
|
64
64
|
In the example above, `Demo` is an observable object,
|
|
65
65
|
meaning that access to its fields are seamlessly tracked
|
|
66
|
-
to determine dependent
|
|
67
|
-
|
|
68
|
-
thus depends on them. It is executed
|
|
69
|
-
response to changes of these fields
|
|
70
|
-
|
|
66
|
+
to determine dependent reactive processes and cached
|
|
67
|
+
results. Reactive process `printContact` reads `name`
|
|
68
|
+
and `email` fields, thus depends on them. It is executed
|
|
69
|
+
automatically in response to changes of these fields
|
|
70
|
+
made by the atomic action `saveContact`.
|
|
71
71
|
|
|
72
|
-
Here is an example of a cached
|
|
72
|
+
Here is an example of a cached result that is
|
|
73
|
+
(re-)computed on-demand:
|
|
73
74
|
|
|
74
75
|
``` typescript
|
|
75
76
|
class Demo extends ObservableObject {
|
|
76
77
|
name: string = 'Nezaboodka Software'
|
|
77
78
|
email: string = 'contact@nezaboodka.com'
|
|
78
79
|
|
|
79
|
-
@
|
|
80
|
+
@cachedResult
|
|
80
81
|
get contact(): string {
|
|
81
82
|
return this.name + ' <' + this.email + '>'
|
|
82
83
|
}
|
|
83
84
|
|
|
84
|
-
@
|
|
85
|
+
@reactiveProcess
|
|
85
86
|
printContact(): void {
|
|
86
87
|
if (this.contact !== '')
|
|
87
88
|
Console.log(this.contact)
|
|
@@ -89,19 +90,22 @@ class Demo extends ObservableObject {
|
|
|
89
90
|
}
|
|
90
91
|
```
|
|
91
92
|
|
|
92
|
-
In the example above, the
|
|
93
|
-
source fields `name` and `email
|
|
94
|
-
the result is cached and is reused until
|
|
95
|
-
and `email` are changed. Once
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
In the example above, the result of `contact` getter is
|
|
94
|
+
computed from source fields `name` and `email`. Once
|
|
95
|
+
computed, the result is cached and is reused until
|
|
96
|
+
source fields `name` and `email` are changed. Once
|
|
97
|
+
source fields changed, `contact` result becomes obsolete,
|
|
98
|
+
thus causing execution of depending reactive process
|
|
99
|
+
`printContact`. When function of reactive process
|
|
100
|
+
`printContact` runs it reads `contact` and causes its
|
|
101
|
+
re-computation.
|
|
99
102
|
|
|
100
103
|
## Observable Objects
|
|
101
104
|
|
|
102
|
-
Observable objects store data of an
|
|
103
|
-
|
|
104
|
-
both on reads and
|
|
105
|
+
Observable objects are aimed to store data of an
|
|
106
|
+
application. All such objects are transparently hooked
|
|
107
|
+
to track access to their properties, both on reads and
|
|
108
|
+
writes.
|
|
105
109
|
|
|
106
110
|
``` typescript
|
|
107
111
|
class MyModel extends ObservableObject {
|
|
@@ -111,23 +115,23 @@ class MyModel extends ObservableObject {
|
|
|
111
115
|
}
|
|
112
116
|
```
|
|
113
117
|
|
|
114
|
-
In the example above, the class `MyModel` is based on
|
|
115
|
-
`ObservableObject` class and all its
|
|
116
|
-
and `timestamp` are hooked.
|
|
118
|
+
In the example above, the class `MyModel` is based on
|
|
119
|
+
Reactronic's `ObservableObject` class and all its
|
|
120
|
+
properties `url`, `content`, and `timestamp` are hooked.
|
|
117
121
|
|
|
118
|
-
##
|
|
122
|
+
## Atomic Action
|
|
119
123
|
|
|
120
|
-
|
|
121
|
-
in
|
|
122
|
-
of dependent
|
|
123
|
-
|
|
124
|
-
provide transparent atomicity
|
|
125
|
-
switching and isolation).
|
|
124
|
+
Atomic action makes changes in observable objects
|
|
125
|
+
in atomic (transactional) way, thus provoking execution
|
|
126
|
+
of dependent reactive processes and recalculation of
|
|
127
|
+
dependent cached results. Atomic action function is
|
|
128
|
+
instrumented with hooks to provide transparent atomicity
|
|
129
|
+
(by implicit context switching and isolation).
|
|
126
130
|
|
|
127
131
|
``` typescript
|
|
128
132
|
class MyModel extends ObservableObject {
|
|
129
133
|
// ...
|
|
130
|
-
@
|
|
134
|
+
@atomicAction
|
|
131
135
|
async load(url: string): Promise<void> {
|
|
132
136
|
this.url = url
|
|
133
137
|
this.content = await fetch(url)
|
|
@@ -136,49 +140,54 @@ class MyModel extends ObservableObject {
|
|
|
136
140
|
}
|
|
137
141
|
```
|
|
138
142
|
|
|
139
|
-
In the example above, the
|
|
140
|
-
changes to `url`, `content` and `timestamp` properties.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
of the
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
of
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
143
|
+
In the example above, the atomic action `load` makes
|
|
144
|
+
changes to `url`, `content` and `timestamp` properties.
|
|
145
|
+
While atomic action is running, the changes are visible
|
|
146
|
+
only inside the action itself. The new values become
|
|
147
|
+
atomically visible outside of the action only upon its
|
|
148
|
+
completion.
|
|
149
|
+
|
|
150
|
+
Atomicity is achieved by making changes in an isolated
|
|
151
|
+
data snapshot that is not visible outside of the running
|
|
152
|
+
action until it is fully finished and applied. Multiple
|
|
153
|
+
objects and their properties can be changed with full
|
|
154
|
+
respect to the all-or-nothing principle. To do so,
|
|
155
|
+
separate data snapshot is automatically maintained for
|
|
156
|
+
each atomic action. That is a logical snapshot that does
|
|
157
|
+
not create a full copy of all the data.
|
|
158
|
+
|
|
159
|
+
Compensating rollback operations are not needed in case
|
|
160
|
+
of the atomic action failure, because all the changes
|
|
161
|
+
made by the atomic action in its logical snapshot are
|
|
162
|
+
simply discarded. In case the atomic action is
|
|
163
|
+
successfully applied, affected cached results are marked
|
|
164
|
+
as obsolete and corresponding caching functions are
|
|
165
|
+
re-executed in a proper order (but only when all the
|
|
166
|
+
data changes are fully applied).
|
|
167
|
+
|
|
168
|
+
Asynchronous operations (promises) are supported out of
|
|
169
|
+
the box during atomic action execution. Atomic action
|
|
170
|
+
may consist of a set of asynchronous calls prolonging
|
|
171
|
+
the action until completion of all of them. An
|
|
172
|
+
asynchronous call may spawn other asynchronous calls,
|
|
173
|
+
which prolong atomic atomic execution until the whole
|
|
174
|
+
chain of asynchronous operations is fully completed.
|
|
175
|
+
|
|
176
|
+
## Reactive Process & Cached Result
|
|
177
|
+
|
|
178
|
+
Reactive process function is automatically and
|
|
179
|
+
immediately called in response to changes in observable
|
|
180
|
+
objects made by atomic actions. Function of cached result
|
|
181
|
+
is called on-demand to renew the result if it was marked
|
|
182
|
+
as obsolete due to changes made by an atomic action.
|
|
183
|
+
Functions of reactive processes and cached results are
|
|
184
|
+
instrumented with hooks to seamlessly subscribe to those
|
|
185
|
+
observable objects and other cached results
|
|
186
|
+
(dependencies), which are used during their execution.
|
|
178
187
|
|
|
179
188
|
``` tsx
|
|
180
189
|
class MyView extends Component<{model: MyModel}> {
|
|
181
|
-
@
|
|
190
|
+
@cachedResult
|
|
182
191
|
render(): React.JSX.Element {
|
|
183
192
|
return (
|
|
184
193
|
<div>
|
|
@@ -192,72 +201,83 @@ class MyView extends Component<{model: MyModel}> {
|
|
|
192
201
|
|
|
193
202
|
``` tsx
|
|
194
203
|
class Component<P> extends React.Component<P> {
|
|
195
|
-
@
|
|
204
|
+
@cachedResult
|
|
196
205
|
render(): React.JSX.Element {
|
|
197
206
|
throw new Error('render method is undefined')
|
|
198
207
|
}
|
|
199
208
|
|
|
200
|
-
@
|
|
209
|
+
@reactiveProcess // called in response to changes
|
|
201
210
|
ensureUpToDate(): void {
|
|
202
|
-
if (this.shouldComponentUpdate())
|
|
203
|
-
|
|
204
|
-
|
|
211
|
+
if (this.shouldComponentUpdate()) {
|
|
212
|
+
// Ask React to re-render
|
|
213
|
+
Transaction.outside(() => this.setState({}))
|
|
214
|
+
}
|
|
215
|
+
} // EnsureUpToDate is subscribed to render
|
|
205
216
|
|
|
206
217
|
shouldComponentUpdate(): boolean {
|
|
207
218
|
return !RxSystem.getController(this.render).isUpToDate
|
|
208
219
|
}
|
|
209
220
|
|
|
210
221
|
componentDidMount(): void {
|
|
211
|
-
|
|
222
|
+
// Run to subscribe for the first time
|
|
223
|
+
this.ensureUpToDate()
|
|
212
224
|
}
|
|
213
225
|
|
|
214
226
|
componentWillUnmount(): void {
|
|
215
|
-
|
|
227
|
+
atomicAction(RxSystem.dispose, this)
|
|
216
228
|
}
|
|
217
229
|
}
|
|
218
230
|
```
|
|
219
231
|
|
|
220
|
-
In the example above,
|
|
221
|
-
to the
|
|
222
|
-
|
|
223
|
-
`
|
|
224
|
-
`
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
`
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
232
|
+
In the example above, reactive process `refresh` is
|
|
233
|
+
transparently subscribed to the cached result of
|
|
234
|
+
function `render`. In turn, cached result of function
|
|
235
|
+
`render` is subscribed to the properties `url` and
|
|
236
|
+
`content` of a corresponding `MyModel` object. Once
|
|
237
|
+
`url` or `content` values are changed, the `render`
|
|
238
|
+
cached result becomes obsolete and causes the reactive
|
|
239
|
+
process `refresh` to become obsolete and re-executed.
|
|
240
|
+
While being executed, the reactive process function
|
|
241
|
+
`refresh` enqueues re-rendering request to React, which
|
|
242
|
+
calls `render` function causing it to renew its cached
|
|
243
|
+
value.
|
|
244
|
+
|
|
245
|
+
In general case, all reactive processes and cached
|
|
246
|
+
results are automatically and immediately marked as
|
|
247
|
+
obsolete when changes are made in those observable
|
|
248
|
+
objects and other cached results that were used during
|
|
249
|
+
their execution. And once marked, the functions are
|
|
250
|
+
automatically executed again, either immediately (for
|
|
251
|
+
reactive processes) or on-demand (for cached results).
|
|
252
|
+
|
|
253
|
+
Reactronic takes full care of tracking dependencies
|
|
254
|
+
between all the observable objects and reactive processes
|
|
255
|
+
or cached results. With Reactronic, you no longer need
|
|
256
|
+
to create data change events in one set of objects,
|
|
257
|
+
subscribe to these events in other objects, and manually
|
|
258
|
+
maintain switching from the previous object version to a
|
|
259
|
+
new one.
|
|
242
260
|
|
|
243
261
|
## Behavior Options
|
|
244
262
|
|
|
245
|
-
There are multiple options to configure behavior of
|
|
263
|
+
There are multiple options to configure behavior of
|
|
264
|
+
transactional reactivity.
|
|
246
265
|
|
|
247
|
-
**Order** options defines order of execution for
|
|
266
|
+
**Order** options defines order of execution for
|
|
267
|
+
reactive processes:
|
|
248
268
|
|
|
249
269
|
- (TBD)
|
|
250
270
|
|
|
251
|
-
**Throttling** option defines how often
|
|
252
|
-
of recurring changes:
|
|
271
|
+
**Throttling** option defines how often reactive process
|
|
272
|
+
is executed in case of recurring changes:
|
|
253
273
|
|
|
254
274
|
- `(ms)` - minimal delay in milliseconds between executions;
|
|
255
|
-
- `-1` - execute immediately once
|
|
275
|
+
- `-1` - execute immediately once atomic action is applied (synchronously);
|
|
256
276
|
- `0` - execute immediately via event loop (asynchronously with zero timeout);
|
|
257
277
|
- `>= Number.MAX_SAFE_INTEGER` - never execute (suspended reaction).
|
|
258
278
|
|
|
259
|
-
**Reentrance** option defines how to handle reentrant calls of
|
|
260
|
-
and
|
|
279
|
+
**Reentrance** option defines how to handle reentrant calls of atomic
|
|
280
|
+
actions and reactive processes:
|
|
261
281
|
|
|
262
282
|
- `preventWithError` - fail with error if there is an existing call in progress;
|
|
263
283
|
- `waitAndRestart` - wait for previous call to finish and then restart current one;
|
|
@@ -267,8 +287,8 @@ and reaction functions:
|
|
|
267
287
|
|
|
268
288
|
**Indicator** is an object that maintains status of running functions,
|
|
269
289
|
which it is attached to. A single indicator object can be shared between
|
|
270
|
-
multiple
|
|
271
|
-
consolidated status for all of them (busy, workers, etc).
|
|
290
|
+
multiple atomic actions, reactive processes, and cached results, thus
|
|
291
|
+
maintaining consolidated status for all of them (busy, workers, etc).
|
|
272
292
|
|
|
273
293
|
## Notes
|
|
274
294
|
|
|
@@ -305,9 +325,9 @@ class ObservableObject { }
|
|
|
305
325
|
|
|
306
326
|
function observable(proto, prop) // field only
|
|
307
327
|
function unobservable(proto, prop) // field only
|
|
308
|
-
function
|
|
309
|
-
function
|
|
310
|
-
function
|
|
328
|
+
function atomicAction(proto, prop, pd) // method only
|
|
329
|
+
function reactiveProcess(proto, prop, pd) // method only
|
|
330
|
+
function cachedResult(proto, prop, pd) // method only
|
|
311
331
|
function options(value: Partial<MemberOptions>): F<any>
|
|
312
332
|
|
|
313
333
|
function nonreactive<T>(func: F<T>, ...args: any[]): T
|
|
@@ -338,9 +358,9 @@ type MemberOptions = {
|
|
|
338
358
|
|
|
339
359
|
enum Kind {
|
|
340
360
|
plain = 0,
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
361
|
+
atomicAction = 1,
|
|
362
|
+
reactiveProcess = 2,
|
|
363
|
+
cachedResult = 3
|
|
344
364
|
}
|
|
345
365
|
|
|
346
366
|
enum Reentrance {
|
|
@@ -8,7 +8,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import { ObservableObject } from "./core/Mvcc.js";
|
|
11
|
-
import {
|
|
11
|
+
import { atomicAction } from "./ReactiveSystem.js";
|
|
12
12
|
export class Clock extends ObservableObject {
|
|
13
13
|
constructor(interval = 1000) {
|
|
14
14
|
super();
|
|
@@ -40,13 +40,13 @@ export class Clock extends ObservableObject {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
__decorate([
|
|
43
|
-
|
|
43
|
+
atomicAction,
|
|
44
44
|
__metadata("design:type", Function),
|
|
45
45
|
__metadata("design:paramtypes", [Boolean]),
|
|
46
46
|
__metadata("design:returntype", void 0)
|
|
47
47
|
], Clock.prototype, "pause", null);
|
|
48
48
|
__decorate([
|
|
49
|
-
|
|
49
|
+
atomicAction,
|
|
50
50
|
__metadata("design:type", Function),
|
|
51
51
|
__metadata("design:paramtypes", []),
|
|
52
52
|
__metadata("design:returntype", void 0)
|
|
@@ -25,9 +25,9 @@ export type MemberOptions = {
|
|
|
25
25
|
};
|
|
26
26
|
export declare enum Kind {
|
|
27
27
|
plain = 0,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
atomicAction = 1,
|
|
29
|
+
reactiveProcess = 2,
|
|
30
|
+
cachedResult = 3
|
|
31
31
|
}
|
|
32
32
|
export declare enum Reentrance {
|
|
33
33
|
preventWithError = 1,
|
|
@@ -2,9 +2,9 @@ export { LoggingLevel } from "./Logging.js";
|
|
|
2
2
|
export var Kind;
|
|
3
3
|
(function (Kind) {
|
|
4
4
|
Kind[Kind["plain"] = 0] = "plain";
|
|
5
|
-
Kind[Kind["
|
|
6
|
-
Kind[Kind["
|
|
7
|
-
Kind[Kind["
|
|
5
|
+
Kind[Kind["atomicAction"] = 1] = "atomicAction";
|
|
6
|
+
Kind[Kind["reactiveProcess"] = 2] = "reactiveProcess";
|
|
7
|
+
Kind[Kind["cachedResult"] = 3] = "cachedResult";
|
|
8
8
|
})(Kind || (Kind = {}));
|
|
9
9
|
export var Reentrance;
|
|
10
10
|
(function (Reentrance) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { F } from "./util/Utils.js";
|
|
2
2
|
import { ObservableObject } from "./core/Mvcc.js";
|
|
3
|
-
export declare class
|
|
3
|
+
export declare class ReactiveProcess<T> extends ObservableObject {
|
|
4
4
|
protected action: F<T>;
|
|
5
5
|
constructor(action: F<T>);
|
|
6
6
|
protected launch(): T;
|
|
@@ -8,8 +8,8 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import { ObservableObject } from "./core/Mvcc.js";
|
|
11
|
-
import {
|
|
12
|
-
export class
|
|
11
|
+
import { reactiveProcess } from "./ReactiveSystem.js";
|
|
12
|
+
export class ReactiveProcess extends ObservableObject {
|
|
13
13
|
constructor(action) {
|
|
14
14
|
super();
|
|
15
15
|
this.action = action;
|
|
@@ -19,8 +19,8 @@ export class Reaction extends ObservableObject {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
__decorate([
|
|
22
|
-
|
|
22
|
+
reactiveProcess,
|
|
23
23
|
__metadata("design:type", Function),
|
|
24
24
|
__metadata("design:paramtypes", []),
|
|
25
25
|
__metadata("design:returntype", Object)
|
|
26
|
-
],
|
|
26
|
+
], ReactiveProcess.prototype, "launch", null);
|
|
@@ -18,13 +18,15 @@ export declare class ReactiveSystem {
|
|
|
18
18
|
static setProfilingMode(isOn: boolean, options?: Partial<ProfilingOptions>): void;
|
|
19
19
|
}
|
|
20
20
|
export declare function nonreactive<T>(func: F<T>, ...args: any[]): T;
|
|
21
|
+
export declare function nonreactive2<T>(options: SnapshotOptions, func: F<T>, ...args: any[]): T;
|
|
22
|
+
export declare function nonreactive2<T>(func: F<T>, ...args: any[]): T;
|
|
21
23
|
export declare function sensitive<T>(sensitivity: boolean, func: F<T>, ...args: any[]): T;
|
|
22
24
|
export declare function contextually<T>(p: Promise<T>): Promise<T>;
|
|
23
|
-
export declare function
|
|
24
|
-
export declare function
|
|
25
|
-
export declare function
|
|
25
|
+
export declare function atomicAction<T>(func: F<T>, ...args: any[]): T;
|
|
26
|
+
export declare function atomicAction<T>(options: SnapshotOptions, func: F<T>, ...args: any[]): T;
|
|
27
|
+
export declare function atomicAction(proto: object, prop: PropertyKey, pd: PropertyDescriptor): any;
|
|
26
28
|
export declare function unobservable(proto: object, prop: PropertyKey): any;
|
|
27
29
|
export declare function observable(proto: object, prop: PropertyKey): any;
|
|
28
|
-
export declare function
|
|
29
|
-
export declare function
|
|
30
|
+
export declare function reactiveProcess(proto: object, prop: PropertyKey, pd: PropertyDescriptor): any;
|
|
31
|
+
export declare function cachedResult(proto: object, prop: PropertyKey, pd: PropertyDescriptor): any;
|
|
30
32
|
export declare function options(value: Partial<MemberOptions>): F<any>;
|
|
@@ -25,13 +25,31 @@ export class ReactiveSystem {
|
|
|
25
25
|
export function nonreactive(func, ...args) {
|
|
26
26
|
return OperationImpl.proceedWithinGivenLaunch(undefined, func, ...args);
|
|
27
27
|
}
|
|
28
|
+
export function nonreactive2(p1, p2, p3) {
|
|
29
|
+
if (p1 instanceof Function) {
|
|
30
|
+
return OperationImpl.proceedWithinGivenLaunch(undefined, () => {
|
|
31
|
+
if (p2 !== undefined)
|
|
32
|
+
return Transaction.run(null, p1, ...p2);
|
|
33
|
+
else
|
|
34
|
+
return Transaction.run(null, p1);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
return OperationImpl.proceedWithinGivenLaunch(undefined, () => {
|
|
39
|
+
if (p3 !== undefined)
|
|
40
|
+
return Transaction.run(p1, p2, ...p3);
|
|
41
|
+
else
|
|
42
|
+
return Transaction.run(p1, p2);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
28
46
|
export function sensitive(sensitivity, func, ...args) {
|
|
29
47
|
return Mvcc.sensitive(sensitivity, func, ...args);
|
|
30
48
|
}
|
|
31
49
|
export function contextually(p) {
|
|
32
50
|
throw new Error("not implemented yet");
|
|
33
51
|
}
|
|
34
|
-
export function
|
|
52
|
+
export function atomicAction(p1, p2, p3) {
|
|
35
53
|
if (p1 instanceof Function) {
|
|
36
54
|
if (p2 !== undefined)
|
|
37
55
|
return Transaction.run(null, p1, ...p2);
|
|
@@ -46,10 +64,10 @@ export function apply(p1, p2, p3) {
|
|
|
46
64
|
}
|
|
47
65
|
else {
|
|
48
66
|
const opts = {
|
|
49
|
-
kind: Kind.
|
|
67
|
+
kind: Kind.atomicAction,
|
|
50
68
|
isolation: Isolation.joinToCurrentTransaction,
|
|
51
69
|
};
|
|
52
|
-
return Mvcc.decorateOperation(true,
|
|
70
|
+
return Mvcc.decorateOperation(true, atomicAction, opts, p1, p2, p3);
|
|
53
71
|
}
|
|
54
72
|
}
|
|
55
73
|
export function unobservable(proto, prop) {
|
|
@@ -58,21 +76,21 @@ export function unobservable(proto, prop) {
|
|
|
58
76
|
export function observable(proto, prop) {
|
|
59
77
|
return Mvcc.decorateData(true, proto, prop);
|
|
60
78
|
}
|
|
61
|
-
export function
|
|
79
|
+
export function reactiveProcess(proto, prop, pd) {
|
|
62
80
|
const opts = {
|
|
63
|
-
kind: Kind.
|
|
81
|
+
kind: Kind.reactiveProcess,
|
|
64
82
|
isolation: Isolation.joinAsNestedTransaction,
|
|
65
83
|
throttling: -1,
|
|
66
84
|
};
|
|
67
|
-
return Mvcc.decorateOperation(true,
|
|
85
|
+
return Mvcc.decorateOperation(true, reactiveProcess, opts, proto, prop, pd);
|
|
68
86
|
}
|
|
69
|
-
export function
|
|
87
|
+
export function cachedResult(proto, prop, pd) {
|
|
70
88
|
const opts = {
|
|
71
|
-
kind: Kind.
|
|
89
|
+
kind: Kind.cachedResult,
|
|
72
90
|
isolation: Isolation.joinToCurrentTransaction,
|
|
73
91
|
noSideEffects: true,
|
|
74
92
|
};
|
|
75
|
-
return Mvcc.decorateOperation(true,
|
|
93
|
+
return Mvcc.decorateOperation(true, cachedResult, opts, proto, prop, pd);
|
|
76
94
|
}
|
|
77
95
|
export function options(value) {
|
|
78
96
|
return Mvcc.decorateOperationParametrized(options, value);
|
package/build/dist/source/Ref.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { atomicAction, nonreactive } from "./ReactiveSystem.js";
|
|
2
2
|
export function refs(owner) {
|
|
3
3
|
return new Proxy(owner, RefGettingProxy);
|
|
4
4
|
}
|
|
@@ -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
|
+
atomicAction({ 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 { ReactiveSystem, observable, unobservable,
|
|
20
|
-
export {
|
|
19
|
+
export { ReactiveSystem, observable, unobservable, atomicAction, reactiveProcess, cachedResult, nonreactive, sensitive, contextually, options } from "./ReactiveSystem.js";
|
|
20
|
+
export { ReactiveProcess } from "./Reaction.js";
|
|
21
21
|
export { ReactiveNode, Mode, Priority, BaseDriver, ReactiveNodeVariable } from "./core/ReactiveNode.js";
|
|
22
22
|
export type { Script, ScriptAsync, Handler, ReactiveNodeDecl, ReactiveNodeDriver, ReactiveNodeContext } from "./core/ReactiveNode.js";
|
|
23
23
|
export { Clock } from "./Clock.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 { ReactiveSystem, observable, unobservable,
|
|
16
|
-
export {
|
|
15
|
+
export { ReactiveSystem, observable, unobservable, atomicAction, reactiveProcess, cachedResult, nonreactive, sensitive, contextually, options } from "./ReactiveSystem.js";
|
|
16
|
+
export { ReactiveProcess } from "./Reaction.js";
|
|
17
17
|
export { ReactiveNode, Mode, Priority, BaseDriver, ReactiveNodeVariable } from "./core/ReactiveNode.js";
|
|
18
18
|
export { Clock } from "./Clock.js";
|
|
@@ -99,7 +99,7 @@ export class OperationImpl {
|
|
|
99
99
|
const ov = ctx.lookupObjectVersion(this.ownerHandle, this.fieldKey, false);
|
|
100
100
|
const launch = this.acquireFromObjectVersion(ov, args);
|
|
101
101
|
const applied = this.ownerHandle.applied.data[this.fieldKey];
|
|
102
|
-
const isReusable = launch.options.kind !== Kind.
|
|
102
|
+
const isReusable = launch.options.kind !== Kind.atomicAction && launch.cause !== BOOT_CAUSE &&
|
|
103
103
|
(ctx === launch.changeset || ctx.timestamp < launch.obsoleteSince || applied.obsoleteDueTo === undefined) &&
|
|
104
104
|
(!launch.options.triggeringArgs || args === undefined ||
|
|
105
105
|
launch.args.length === args.length && launch.args.every((t, i) => t === args[i])) || ov.disposed;
|
|
@@ -175,7 +175,7 @@ export class OperationImpl {
|
|
|
175
175
|
}
|
|
176
176
|
else {
|
|
177
177
|
ror = this.peek(argsx);
|
|
178
|
-
if (ror.launch.options.kind === Kind.
|
|
178
|
+
if (ror.launch.options.kind === Kind.atomicAction || !ror.isReusable) {
|
|
179
179
|
ror = this.edit();
|
|
180
180
|
if (Log.isOn && Log.opt.operation)
|
|
181
181
|
Log.write("║", " o", `${ror.launch.why()}`);
|
|
@@ -251,7 +251,7 @@ class Launch extends FieldVersion {
|
|
|
251
251
|
let cause;
|
|
252
252
|
if (this.cause)
|
|
253
253
|
cause = ` ◀◀ ${this.cause}`;
|
|
254
|
-
else if (this.operation.options.kind === Kind.
|
|
254
|
+
else if (this.operation.options.kind === Kind.atomicAction)
|
|
255
255
|
cause = " ◀◀ operation";
|
|
256
256
|
else
|
|
257
257
|
cause = ` ◀◀ T${this.changeset.id}[${this.changeset.hint}]`;
|
|
@@ -294,7 +294,7 @@ class Launch extends FieldVersion {
|
|
|
294
294
|
changeset.id === this.lastEditorChangesetId;
|
|
295
295
|
if (!skip) {
|
|
296
296
|
const why = `${Dump.snapshot2(h, changeset, fk, observable)} ◀◀ ${outer}`;
|
|
297
|
-
const isReactive = this.options.kind === Kind.
|
|
297
|
+
const isReactive = this.options.kind === Kind.reactiveProcess;
|
|
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)))
|
|
@@ -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.reactiveProcess)
|
|
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.reactiveProcess)
|
|
337
337
|
misuse(`reactive ${this.hint()} failed and will not run anymore: ${e}`, e);
|
|
338
338
|
}
|
|
339
339
|
}
|
|
@@ -346,7 +346,7 @@ class Launch extends FieldVersion {
|
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
isNotUpToDate() {
|
|
349
|
-
return !this.error && (this.options.kind === Kind.
|
|
349
|
+
return !this.error && (this.options.kind === Kind.atomicAction ||
|
|
350
350
|
!this.successor || this.successor.transaction.isCanceled);
|
|
351
351
|
}
|
|
352
352
|
reenterOver(head) {
|
|
@@ -469,9 +469,9 @@ class Launch extends FieldVersion {
|
|
|
469
469
|
x.relaunchIfNotUpToDate(true, true);
|
|
470
470
|
}
|
|
471
471
|
static markUsed(observable, ov, fk, h, kind, weak) {
|
|
472
|
-
if (kind !== Kind.
|
|
472
|
+
if (kind !== Kind.atomicAction) {
|
|
473
473
|
const launch = Launch.current;
|
|
474
|
-
if (launch && launch.options.kind !== Kind.
|
|
474
|
+
if (launch && launch.options.kind !== Kind.atomicAction &&
|
|
475
475
|
launch.transaction === Transaction.current && fk !== Meta.Handle) {
|
|
476
476
|
const ctx = Changeset.current();
|
|
477
477
|
if (ctx !== ov.changeset)
|
|
@@ -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.reactiveProcess && 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.reactiveProcess && launch.options.throttling >= Number.MAX_SAFE_INTEGER) {
|
|
660
660
|
const reactive = Meta.getFrom(proto, Meta.Reactive);
|
|
661
661
|
delete reactive[fk];
|
|
662
662
|
}
|
|
@@ -744,8 +744,8 @@ function reactronicHookedThen(resolve, reject) {
|
|
|
744
744
|
resolve = launch.wrap(resolve);
|
|
745
745
|
reject = launch.wrap(reject);
|
|
746
746
|
}
|
|
747
|
-
resolve = tran.
|
|
748
|
-
reject = tran.
|
|
747
|
+
resolve = tran.wrapAsPending(resolve, false);
|
|
748
|
+
reject = tran.wrapAsPending(reject, true);
|
|
749
749
|
}
|
|
750
750
|
return ORIGINAL_PROMISE_THEN.call(this, resolve, reject);
|
|
751
751
|
}
|
|
@@ -22,7 +22,7 @@ import { emitLetters, getCallerInfo, proceedSyncOrAsync } from "../util/Utils.js
|
|
|
22
22
|
import { Isolation, Reentrance } from "../Options.js";
|
|
23
23
|
import { ObservableObject } from "../core/Mvcc.js";
|
|
24
24
|
import { Transaction } from "../core/Transaction.js";
|
|
25
|
-
import { ReactiveSystem, options, unobservable,
|
|
25
|
+
import { ReactiveSystem, options, unobservable, reactiveProcess, nonreactive, atomicAction } from "../ReactiveSystem.js";
|
|
26
26
|
export var Mode;
|
|
27
27
|
(function (Mode) {
|
|
28
28
|
Mode[Mode["default"] = 0] = "default";
|
|
@@ -339,7 +339,7 @@ class ReactiveNodeImpl extends ReactiveNode {
|
|
|
339
339
|
node.outer = owner;
|
|
340
340
|
else
|
|
341
341
|
node.outer = owner.outer;
|
|
342
|
-
|
|
342
|
+
atomicAction({ isolation: Isolation.joinAsNestedTransaction }, () => {
|
|
343
343
|
const ctx = node.context;
|
|
344
344
|
if (ctx) {
|
|
345
345
|
ctx.variable = variable;
|
|
@@ -359,7 +359,7 @@ ReactiveNodeImpl.logging = undefined;
|
|
|
359
359
|
ReactiveNodeImpl.grandNodeCount = 0;
|
|
360
360
|
ReactiveNodeImpl.disposableNodeCount = 0;
|
|
361
361
|
__decorate([
|
|
362
|
-
|
|
362
|
+
reactiveProcess,
|
|
363
363
|
options({
|
|
364
364
|
reentrance: Reentrance.cancelAndWaitPrevious,
|
|
365
365
|
allowObsoleteToFinish: true,
|
|
@@ -546,7 +546,7 @@ function triggerFinalization(slot, isLeader, individual) {
|
|
|
546
546
|
else
|
|
547
547
|
gFirstToDispose = gLastToDispose = slot;
|
|
548
548
|
if (gFirstToDispose === slot)
|
|
549
|
-
|
|
549
|
+
atomicAction({ isolation: Isolation.disjoinForInternalDisposal, hint: `runDisposalLoop(initiator=${slot.instance.key})` }, () => {
|
|
550
550
|
void runDisposalLoop().then(NOP, error => console.log(error));
|
|
551
551
|
});
|
|
552
552
|
}
|
|
@@ -17,7 +17,7 @@ export declare abstract class Transaction implements Worker {
|
|
|
17
17
|
abstract inspect<T>(func: F<T>, ...args: any[]): T;
|
|
18
18
|
abstract apply(): void;
|
|
19
19
|
abstract seal(): this;
|
|
20
|
-
abstract
|
|
20
|
+
abstract wrapAsPending<T>(func: F<T>, secondary: boolean): F<T>;
|
|
21
21
|
abstract cancel(error: Error, retryAfterOrIgnore?: Worker | null): this;
|
|
22
22
|
abstract readonly isCanceled: boolean;
|
|
23
23
|
abstract readonly isFinished: boolean;
|
|
@@ -33,7 +33,7 @@ export declare abstract class Transaction implements Worker {
|
|
|
33
33
|
export declare class TransactionImpl extends Transaction {
|
|
34
34
|
private static readonly none;
|
|
35
35
|
private static curr;
|
|
36
|
-
private static
|
|
36
|
+
private static isInspectionMode;
|
|
37
37
|
private static frameStartTime;
|
|
38
38
|
private static frameOverCounter;
|
|
39
39
|
readonly margin: number;
|
|
@@ -57,9 +57,9 @@ export declare class TransactionImpl extends Transaction {
|
|
|
57
57
|
inspect<T>(func: F<T>, ...args: any[]): T;
|
|
58
58
|
apply(): void;
|
|
59
59
|
seal(): this;
|
|
60
|
-
|
|
61
|
-
private static
|
|
62
|
-
private static
|
|
60
|
+
wrapAsPending<T>(func: F<T>, secondary: boolean): F<T>;
|
|
61
|
+
private static preparePendingFunction;
|
|
62
|
+
private static runPendingFunction;
|
|
63
63
|
cancel(error: Error, restartAfter?: Worker | null): this;
|
|
64
64
|
get isCanceled(): boolean;
|
|
65
65
|
get isFinished(): boolean;
|
|
@@ -50,15 +50,15 @@ export class TransactionImpl extends Transaction {
|
|
|
50
50
|
return this.runImpl(undefined, func, ...args);
|
|
51
51
|
}
|
|
52
52
|
inspect(func, ...args) {
|
|
53
|
-
const
|
|
53
|
+
const outer = TransactionImpl.isInspectionMode;
|
|
54
54
|
try {
|
|
55
|
-
TransactionImpl.
|
|
55
|
+
TransactionImpl.isInspectionMode = true;
|
|
56
56
|
if (Log.isOn && Log.opt.transaction)
|
|
57
57
|
Log.write(" ", " ", `T${this.id}[${this.hint}] is being inspected by T${TransactionImpl.curr.id}[${TransactionImpl.curr.hint}]`);
|
|
58
58
|
return this.runImpl(undefined, func, ...args);
|
|
59
59
|
}
|
|
60
60
|
finally {
|
|
61
|
-
TransactionImpl.
|
|
61
|
+
TransactionImpl.isInspectionMode = outer;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
apply() {
|
|
@@ -73,27 +73,27 @@ export class TransactionImpl extends Transaction {
|
|
|
73
73
|
this.run(TransactionImpl.seal, this, undefined, undefined);
|
|
74
74
|
return this;
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
wrapAsPending(func, secondary) {
|
|
77
77
|
this.guard();
|
|
78
78
|
const self = this;
|
|
79
|
-
const
|
|
80
|
-
if (!
|
|
81
|
-
self.run(TransactionImpl.
|
|
79
|
+
const inspection = TransactionImpl.isInspectionMode;
|
|
80
|
+
if (!inspection)
|
|
81
|
+
self.run(TransactionImpl.preparePendingFunction, self, secondary);
|
|
82
82
|
else
|
|
83
|
-
self.inspect(TransactionImpl.
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
86
|
-
return self.runImpl(undefined, TransactionImpl.
|
|
83
|
+
self.inspect(TransactionImpl.preparePendingFunction, self, secondary);
|
|
84
|
+
const wrappedAsPendingForTransaction = (...args) => {
|
|
85
|
+
if (!inspection)
|
|
86
|
+
return self.runImpl(undefined, TransactionImpl.runPendingFunction, self, secondary, func, ...args);
|
|
87
87
|
else
|
|
88
|
-
return self.inspect(TransactionImpl.
|
|
88
|
+
return self.inspect(TransactionImpl.runPendingFunction, self, secondary, func, ...args);
|
|
89
89
|
};
|
|
90
|
-
return
|
|
90
|
+
return wrappedAsPendingForTransaction;
|
|
91
91
|
}
|
|
92
|
-
static
|
|
93
|
-
if (!
|
|
92
|
+
static preparePendingFunction(t, secondary) {
|
|
93
|
+
if (!secondary)
|
|
94
94
|
t.pending++;
|
|
95
95
|
}
|
|
96
|
-
static
|
|
96
|
+
static runPendingFunction(t, secondary, func, ...args) {
|
|
97
97
|
t.pending--;
|
|
98
98
|
const result = func(...args);
|
|
99
99
|
return result;
|
|
@@ -118,10 +118,10 @@ export class TransactionImpl extends Transaction {
|
|
|
118
118
|
}
|
|
119
119
|
static run(options, func, ...args) {
|
|
120
120
|
const t = TransactionImpl.acquire(options);
|
|
121
|
-
const
|
|
121
|
+
const isRoot = t !== TransactionImpl.curr;
|
|
122
122
|
t.guard();
|
|
123
123
|
let result = t.runImpl(options === null || options === void 0 ? void 0 : options.logging, func, ...args);
|
|
124
|
-
if (
|
|
124
|
+
if (isRoot) {
|
|
125
125
|
if (result instanceof Promise) {
|
|
126
126
|
result = TransactionImpl.outside(() => {
|
|
127
127
|
return t.wrapToRetry(t.wrapToWaitUntilFinish(result), func, ...args);
|
|
@@ -231,7 +231,7 @@ export class TransactionImpl extends Transaction {
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
catch (e) {
|
|
234
|
-
if (!TransactionImpl.
|
|
234
|
+
if (!TransactionImpl.isInspectionMode)
|
|
235
235
|
this.cancel(e);
|
|
236
236
|
throw e;
|
|
237
237
|
}
|
|
@@ -460,7 +460,7 @@ export class TransactionImpl extends Transaction {
|
|
|
460
460
|
return TransactionImpl.curr.changeset;
|
|
461
461
|
}
|
|
462
462
|
static getEditableChangeset() {
|
|
463
|
-
if (TransactionImpl.
|
|
463
|
+
if (TransactionImpl.isInspectionMode)
|
|
464
464
|
throw misuse("cannot make changes during transaction inspection");
|
|
465
465
|
return TransactionImpl.curr.changeset;
|
|
466
466
|
}
|
|
@@ -474,7 +474,7 @@ export class TransactionImpl extends Transaction {
|
|
|
474
474
|
}
|
|
475
475
|
TransactionImpl.none = new TransactionImpl({ hint: "<none>" });
|
|
476
476
|
TransactionImpl.curr = TransactionImpl.none;
|
|
477
|
-
TransactionImpl.
|
|
477
|
+
TransactionImpl.isInspectionMode = false;
|
|
478
478
|
TransactionImpl.frameStartTime = 0;
|
|
479
479
|
TransactionImpl.frameOverCounter = 0;
|
|
480
480
|
TransactionImpl.migrateFieldVersion = function (fv, target) {
|