reactronic 0.22.313 → 0.22.316
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 +6 -6
- package/build/dist/source/Buffer.d.ts +1 -1
- package/build/dist/source/Buffer.js +1 -1
- package/build/dist/source/Options.d.ts +3 -3
- package/build/dist/source/Rx.d.ts +2 -2
- package/build/dist/source/Rx.js +14 -14
- package/build/dist/source/api.d.ts +5 -2
- package/build/dist/source/api.js +5 -2
- package/build/dist/source/impl/Changeset.js +5 -5
- package/build/dist/source/impl/Data.d.ts +1 -1
- package/build/dist/source/impl/Journal.d.ts +1 -1
- package/build/dist/source/impl/Journal.js +4 -4
- package/build/dist/source/impl/Monitor.d.ts +1 -1
- package/build/dist/source/impl/Monitor.js +4 -4
- package/build/dist/source/impl/{Hooks.d.ts → Mvcc.d.ts} +14 -51
- package/build/dist/source/impl/{Hooks.js → Mvcc.js} +53 -100
- package/build/dist/source/impl/MvccArray.d.ts +60 -0
- package/build/dist/source/impl/MvccArray.js +58 -0
- package/build/dist/source/impl/MvccCollection.d.ts +25 -0
- package/build/dist/source/impl/MvccCollection.js +23 -0
- package/build/dist/source/impl/MvccMap.d.ts +25 -0
- package/build/dist/source/impl/MvccMap.js +35 -0
- package/build/dist/source/impl/Operation.d.ts +3 -3
- package/build/dist/source/impl/Operation.js +20 -20
- package/build/dist/source/impl/Transaction.d.ts +2 -2
- package/build/dist/source/impl/Transaction.js +9 -9
- package/build/dist/source/util/Collection.d.ts +63 -0
- package/build/dist/source/util/Collection.js +283 -0
- package/package.json +8 -8
|
@@ -5,11 +5,11 @@ import { ObjectHandle, Subscription, Meta } from './Data';
|
|
|
5
5
|
import { Changeset, Dump, EMPTY_SNAPSHOT, MAX_REVISION } from './Changeset';
|
|
6
6
|
import { Transaction } from './Transaction';
|
|
7
7
|
import { MonitorImpl } from './Monitor';
|
|
8
|
-
import {
|
|
8
|
+
import { Mvcc, OptionsImpl } from './Mvcc';
|
|
9
9
|
import { JournalImpl } from './Journal';
|
|
10
10
|
const BOOT_ARGS = [];
|
|
11
11
|
const BOOT_CAUSE = '<boot>';
|
|
12
|
-
const EMPTY_HANDLE = new ObjectHandle(undefined, undefined,
|
|
12
|
+
const EMPTY_HANDLE = new ObjectHandle(undefined, undefined, Mvcc.reactive, EMPTY_SNAPSHOT, '<empty>');
|
|
13
13
|
export class OperationController extends Controller {
|
|
14
14
|
constructor(h, m) {
|
|
15
15
|
super();
|
|
@@ -36,12 +36,12 @@ export class OperationController extends Controller {
|
|
|
36
36
|
&& (!weak || op.cause === BOOT_CAUSE || !op.successor ||
|
|
37
37
|
op.successor.transaction.isFinished)) {
|
|
38
38
|
const outerOpts = (_a = Operation.current) === null || _a === void 0 ? void 0 : _a.options;
|
|
39
|
-
const
|
|
39
|
+
const separation = weak || opts.separation || opts.kind === Kind.Reaction ||
|
|
40
40
|
(opts.kind === Kind.Transaction && outerOpts && (outerOpts.noSideEffects || outerOpts.kind === Kind.Cache)) ||
|
|
41
41
|
(opts.kind === Kind.Cache && (oc.snapshot.changeset.sealed ||
|
|
42
42
|
oc.snapshot.former.snapshot !== EMPTY_SNAPSHOT));
|
|
43
43
|
const token = opts.noSideEffects ? this : undefined;
|
|
44
|
-
const oc2 = this.run(oc,
|
|
44
|
+
const oc2 = this.run(oc, separation, opts, token, args);
|
|
45
45
|
const ctx2 = oc2.operation.changeset;
|
|
46
46
|
if (!weak || ctx === ctx2 || (ctx2.sealed && ctx.timestamp >= ctx2.timestamp))
|
|
47
47
|
oc = oc2;
|
|
@@ -53,7 +53,7 @@ export class OperationController extends Controller {
|
|
|
53
53
|
Changeset.markUsed(t, oc.snapshot, this.memberName, this.objectHandle, t.options.kind, weak);
|
|
54
54
|
return t;
|
|
55
55
|
}
|
|
56
|
-
static
|
|
56
|
+
static getControllerOf(method) {
|
|
57
57
|
const ctl = Meta.get(method, Meta.Controller);
|
|
58
58
|
if (!ctl)
|
|
59
59
|
throw misuse(`given method is not decorated as reactronic one: ${method.name}`);
|
|
@@ -137,8 +137,8 @@ export class OperationController extends Controller {
|
|
|
137
137
|
if (op.controller !== this) {
|
|
138
138
|
if (os.changeset !== EMPTY_SNAPSHOT.changeset) {
|
|
139
139
|
const hint = Log.isOn ? `${Dump.obj(this.objectHandle, m)}/boot` : 'MethodController/init';
|
|
140
|
-
const
|
|
141
|
-
op = Transaction.run({ hint,
|
|
140
|
+
const separation = os.changeset.sealed || os.former.snapshot !== EMPTY_SNAPSHOT;
|
|
141
|
+
op = Transaction.run({ hint, separation, token: this }, () => {
|
|
142
142
|
const h = this.objectHandle;
|
|
143
143
|
let r2 = Changeset.current().getObjectSnapshot(h, m);
|
|
144
144
|
let op2 = r2.data[m];
|
|
@@ -168,10 +168,10 @@ export class OperationController extends Controller {
|
|
|
168
168
|
}
|
|
169
169
|
return op;
|
|
170
170
|
}
|
|
171
|
-
run(existing,
|
|
171
|
+
run(existing, separation, options, token, args) {
|
|
172
172
|
const hint = Log.isOn ? `${Dump.obj(this.objectHandle, this.memberName)}${args && args.length > 0 && (typeof args[0] === 'number' || typeof args[0] === 'string') ? ` - ${args[0]}` : ''}` : `${Dump.obj(this.objectHandle, this.memberName)}`;
|
|
173
173
|
let oc = existing;
|
|
174
|
-
const opts = { hint,
|
|
174
|
+
const opts = { hint, separation, journal: options.journal, logging: options.logging, token };
|
|
175
175
|
const result = Transaction.run(opts, (argsx) => {
|
|
176
176
|
if (!oc.operation.transaction.isCanceled) {
|
|
177
177
|
oc = this.edit();
|
|
@@ -226,7 +226,7 @@ class Operation extends Subscription {
|
|
|
226
226
|
get originSnapshotId() { return this.changeset.id; }
|
|
227
227
|
hint() { return `${Dump.snapshot2(this.controller.objectHandle, this.changeset, this.controller.memberName)}`; }
|
|
228
228
|
get order() { return this.options.order; }
|
|
229
|
-
get ['#this']() {
|
|
229
|
+
get ['#this#']() {
|
|
230
230
|
return `Operation: ${this.why()}`;
|
|
231
231
|
}
|
|
232
232
|
why() {
|
|
@@ -254,7 +254,7 @@ class Operation extends Subscription {
|
|
|
254
254
|
const ms = Date.now() - started;
|
|
255
255
|
if (Log.isOn && Log.opt.step && this.result)
|
|
256
256
|
Log.writeAs({ margin2: this.margin }, '║', '_/', `${this.hint()} - step out `, 0, this.started > 0 ? ' │' : '');
|
|
257
|
-
if (ms >
|
|
257
|
+
if (ms > Mvcc.mainThreadBlockingWarningThreshold)
|
|
258
258
|
Log.write('', '[!]', this.why(), ms, ' *** main thread is too busy ***');
|
|
259
259
|
return result;
|
|
260
260
|
};
|
|
@@ -410,7 +410,7 @@ class Operation extends Subscription {
|
|
|
410
410
|
this.started = -this.started;
|
|
411
411
|
if (Log.isOn && Log.opt.operation)
|
|
412
412
|
Log.write('║', `${op}`, `${this.hint()} ${message}`, ms, highlight);
|
|
413
|
-
if (ms > (main ?
|
|
413
|
+
if (ms > (main ? Mvcc.mainThreadBlockingWarningThreshold : Mvcc.asyncActionDurationWarningThreshold))
|
|
414
414
|
Log.write('', '[!]', this.why(), ms, main ? ' *** main thread is too busy ***' : ' *** async is too long ***');
|
|
415
415
|
this.cause = undefined;
|
|
416
416
|
if (this.options.monitor)
|
|
@@ -419,17 +419,17 @@ class Operation extends Subscription {
|
|
|
419
419
|
monitorEnter(mon) {
|
|
420
420
|
const options = {
|
|
421
421
|
hint: 'Monitor.enter',
|
|
422
|
-
|
|
422
|
+
separation: 'isolated',
|
|
423
423
|
logging: Log.isOn && Log.opt.monitor ? undefined : Log.global
|
|
424
424
|
};
|
|
425
425
|
OperationController.runWithin(undefined, Transaction.run, options, MonitorImpl.enter, mon, this.transaction);
|
|
426
426
|
}
|
|
427
427
|
monitorLeave(mon) {
|
|
428
|
-
Transaction.
|
|
428
|
+
Transaction.outside(() => {
|
|
429
429
|
const leave = () => {
|
|
430
430
|
const options = {
|
|
431
431
|
hint: 'Monitor.leave',
|
|
432
|
-
|
|
432
|
+
separation: 'isolated',
|
|
433
433
|
logging: Log.isOn && Log.opt.monitor ? undefined : Log.DefaultLevel
|
|
434
434
|
};
|
|
435
435
|
OperationController.runWithin(undefined, Transaction.run, options, MonitorImpl.leave, mon, this.transaction);
|
|
@@ -520,9 +520,9 @@ class Operation extends Subscription {
|
|
|
520
520
|
}
|
|
521
521
|
if (curr instanceof Operation) {
|
|
522
522
|
if (curr.changeset === os.changeset && curr.subscriptions !== undefined) {
|
|
523
|
-
if (
|
|
523
|
+
if (Mvcc.repetitiveUsageWarningThreshold < Number.MAX_SAFE_INTEGER) {
|
|
524
524
|
curr.subscriptions.forEach((info, v) => {
|
|
525
|
-
if (info.usageCount >
|
|
525
|
+
if (info.usageCount > Mvcc.repetitiveUsageWarningThreshold)
|
|
526
526
|
Log.write('', '[!]', `${curr.hint()} uses ${info.memberHint} ${info.usageCount} times (consider remembering it in a local variable)`, 0, ' *** WARNING ***');
|
|
527
527
|
});
|
|
528
528
|
}
|
|
@@ -566,7 +566,7 @@ class Operation extends Subscription {
|
|
|
566
566
|
const ok = Operation.canSubscribe(subscription, os, m, h, timestamp);
|
|
567
567
|
if (ok) {
|
|
568
568
|
let times = 0;
|
|
569
|
-
if (
|
|
569
|
+
if (Mvcc.repetitiveUsageWarningThreshold < Number.MAX_SAFE_INTEGER) {
|
|
570
570
|
const existing = this.subscriptions.get(subscription);
|
|
571
571
|
times = existing ? existing.usageCount + 1 : 1;
|
|
572
572
|
}
|
|
@@ -628,8 +628,8 @@ class Operation extends Subscription {
|
|
|
628
628
|
Changeset.propagateAllChangesThroughSubscriptions = Operation.propagateAllChangesThroughSubscriptions;
|
|
629
629
|
Changeset.revokeAllSubscriptions = Operation.revokeAllSubscriptions;
|
|
630
630
|
Changeset.enqueueReactionsToRun = Operation.enqueueReactionsToRun;
|
|
631
|
-
|
|
632
|
-
|
|
631
|
+
Mvcc.createOperation = Operation.createOperation;
|
|
632
|
+
Mvcc.rememberOperationOptions = Operation.rememberOperationOptions;
|
|
633
633
|
Promise.prototype.then = reactronicHookedThen;
|
|
634
634
|
try {
|
|
635
635
|
Object.defineProperty(globalThis, 'rWhy', {
|
|
@@ -22,8 +22,8 @@ export declare abstract class Transaction implements Worker {
|
|
|
22
22
|
whenFinished(): Promise<void>;
|
|
23
23
|
static create(options: SnapshotOptions | null): Transaction;
|
|
24
24
|
static run<T>(options: SnapshotOptions | null, func: F<T>, ...args: any[]): T;
|
|
25
|
-
static
|
|
26
|
-
static
|
|
25
|
+
static separate<T>(func: F<T>, ...args: any[]): T;
|
|
26
|
+
static outside<T>(func: F<T>, ...args: any[]): T;
|
|
27
27
|
static isFrameOver(everyN?: number, timeLimit?: number): boolean;
|
|
28
28
|
static requestNextFrame(sleepTime?: number): Promise<void>;
|
|
29
29
|
static get isCanceled(): boolean;
|
|
@@ -17,8 +17,8 @@ export class Transaction {
|
|
|
17
17
|
}
|
|
18
18
|
static create(options) { return new TransactionImpl(options); }
|
|
19
19
|
static run(options, func, ...args) { return TransactionImpl.run(options, func, ...args); }
|
|
20
|
-
static
|
|
21
|
-
static
|
|
20
|
+
static separate(func, ...args) { return TransactionImpl.separate(func, ...args); }
|
|
21
|
+
static outside(func, ...args) { return TransactionImpl.outside(func, ...args); }
|
|
22
22
|
static isFrameOver(everyN = 1, timeLimit = 10) { return TransactionImpl.isFrameOver(everyN, timeLimit); }
|
|
23
23
|
static requestNextFrame(sleepTime = 0) { return TransactionImpl.requestNextFrame(sleepTime); }
|
|
24
24
|
static get isCanceled() { return TransactionImpl.current.isCanceled; }
|
|
@@ -118,7 +118,7 @@ class TransactionImpl extends Transaction {
|
|
|
118
118
|
let result = t.runImpl(options === null || options === void 0 ? void 0 : options.logging, func, ...args);
|
|
119
119
|
if (root) {
|
|
120
120
|
if (result instanceof Promise) {
|
|
121
|
-
result = TransactionImpl.
|
|
121
|
+
result = TransactionImpl.outside(() => {
|
|
122
122
|
return t.wrapToRetry(t.wrapToWaitUntilFinish(result), func, ...args);
|
|
123
123
|
});
|
|
124
124
|
}
|
|
@@ -126,10 +126,10 @@ class TransactionImpl extends Transaction {
|
|
|
126
126
|
}
|
|
127
127
|
return result;
|
|
128
128
|
}
|
|
129
|
-
static
|
|
130
|
-
return TransactionImpl.run({
|
|
129
|
+
static separate(func, ...args) {
|
|
130
|
+
return TransactionImpl.run({ separation: true }, func, ...args);
|
|
131
131
|
}
|
|
132
|
-
static
|
|
132
|
+
static outside(func, ...args) {
|
|
133
133
|
const outer = TransactionImpl.curr;
|
|
134
134
|
try {
|
|
135
135
|
TransactionImpl.curr = TransactionImpl.none;
|
|
@@ -153,7 +153,7 @@ class TransactionImpl extends Transaction {
|
|
|
153
153
|
}
|
|
154
154
|
static acquire(options) {
|
|
155
155
|
const curr = TransactionImpl.curr;
|
|
156
|
-
if ((options === null || options === void 0 ? void 0 : options.
|
|
156
|
+
if ((options === null || options === void 0 ? void 0 : options.separation) || curr.isFinished || curr.options.separation === 'isolated')
|
|
157
157
|
return new TransactionImpl(options);
|
|
158
158
|
else
|
|
159
159
|
return TransactionImpl.curr;
|
|
@@ -176,7 +176,7 @@ class TransactionImpl extends Transaction {
|
|
|
176
176
|
yield this.after.whenFinished();
|
|
177
177
|
const options = {
|
|
178
178
|
hint: `${this.hint} - restart after T${this.after.id}`,
|
|
179
|
-
|
|
179
|
+
separation: this.options.separation === 'isolated' ? 'isolated' : true,
|
|
180
180
|
logging: this.changeset.options.logging,
|
|
181
181
|
token: this.changeset.options.token,
|
|
182
182
|
};
|
|
@@ -226,7 +226,7 @@ class TransactionImpl extends Transaction {
|
|
|
226
226
|
if (this.sealed && this.pending === 0) {
|
|
227
227
|
const reactions = this.applyOrDiscard();
|
|
228
228
|
TransactionImpl.curr = outer;
|
|
229
|
-
TransactionImpl.
|
|
229
|
+
TransactionImpl.outside(Changeset.enqueueReactionsToRun, reactions);
|
|
230
230
|
}
|
|
231
231
|
else
|
|
232
232
|
TransactionImpl.curr = outer;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export declare type GetItemKey<T = unknown> = (item: T) => string | undefined;
|
|
2
|
+
export interface CollectionReader<T> {
|
|
3
|
+
readonly strict: boolean;
|
|
4
|
+
readonly count: number;
|
|
5
|
+
readonly addedCount: number;
|
|
6
|
+
readonly removedCount: number;
|
|
7
|
+
readonly isMergeInProgress: boolean;
|
|
8
|
+
lookup(key: string): Item<T> | undefined;
|
|
9
|
+
claim(key: string): Item<T> | undefined;
|
|
10
|
+
add(self: T): Item<T>;
|
|
11
|
+
remove(item: Item<T>): void;
|
|
12
|
+
move(item: Item<T>, after: Item<T>): void;
|
|
13
|
+
beginMerge(): void;
|
|
14
|
+
endMerge(error?: unknown): void;
|
|
15
|
+
resetAddedAndRemovedLists(): void;
|
|
16
|
+
items(): Generator<Item<T>>;
|
|
17
|
+
addedItems(reset?: boolean): Generator<Item<T>>;
|
|
18
|
+
removedItems(reset?: boolean): Generator<Item<T>>;
|
|
19
|
+
isAdded(item: Item<T>): boolean;
|
|
20
|
+
isMoved(item: Item<T>): boolean;
|
|
21
|
+
isRemoved(item: Item<T>): boolean;
|
|
22
|
+
isCurrent(item: Item<T>): boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface Item<T> {
|
|
25
|
+
readonly self: T;
|
|
26
|
+
readonly prev?: Item<T>;
|
|
27
|
+
aux?: Item<T>;
|
|
28
|
+
}
|
|
29
|
+
export declare class Collection<T> implements CollectionReader<T> {
|
|
30
|
+
readonly strict: boolean;
|
|
31
|
+
readonly getKey: GetItemKey<T>;
|
|
32
|
+
private map;
|
|
33
|
+
private tag;
|
|
34
|
+
private current;
|
|
35
|
+
private added;
|
|
36
|
+
private removed;
|
|
37
|
+
private lastNotFoundKey;
|
|
38
|
+
private strictNextItem?;
|
|
39
|
+
constructor(strict: boolean, getKey: GetItemKey<T>);
|
|
40
|
+
get count(): number;
|
|
41
|
+
get addedCount(): number;
|
|
42
|
+
get removedCount(): number;
|
|
43
|
+
get isMergeInProgress(): boolean;
|
|
44
|
+
lookup(key: string | undefined): Item<T> | undefined;
|
|
45
|
+
claim(key: string, resolution?: {
|
|
46
|
+
isDuplicate: boolean;
|
|
47
|
+
}): Item<T> | undefined;
|
|
48
|
+
add(self: T): Item<T>;
|
|
49
|
+
remove(item: Item<T>): void;
|
|
50
|
+
move(item: Item<T>, after: Item<T>): void;
|
|
51
|
+
beginMerge(): void;
|
|
52
|
+
endMerge(error?: unknown): void;
|
|
53
|
+
resetAddedAndRemovedLists(): void;
|
|
54
|
+
items(): Generator<Item<T>>;
|
|
55
|
+
addedItems(reset?: boolean): Generator<Item<T>>;
|
|
56
|
+
removedItems(reset?: boolean): Generator<Item<T>>;
|
|
57
|
+
isAdded(item: Item<T>): boolean;
|
|
58
|
+
isMoved(item: Item<T>): boolean;
|
|
59
|
+
isRemoved(item: Item<T>): boolean;
|
|
60
|
+
isCurrent(item: Item<T>): boolean;
|
|
61
|
+
markAsMoved(item: Item<T>): void;
|
|
62
|
+
static createItem<T>(self: T): Item<T>;
|
|
63
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
export class Collection {
|
|
2
|
+
constructor(strict, getKey) {
|
|
3
|
+
this.strict = strict;
|
|
4
|
+
this.getKey = getKey;
|
|
5
|
+
this.map = new Map();
|
|
6
|
+
this.tag = ~0;
|
|
7
|
+
this.current = new ItemChain();
|
|
8
|
+
this.added = new ItemChain();
|
|
9
|
+
this.removed = new ItemChain();
|
|
10
|
+
this.lastNotFoundKey = undefined;
|
|
11
|
+
this.strictNextItem = undefined;
|
|
12
|
+
}
|
|
13
|
+
get count() {
|
|
14
|
+
return this.current.count;
|
|
15
|
+
}
|
|
16
|
+
get addedCount() {
|
|
17
|
+
return this.added.count;
|
|
18
|
+
}
|
|
19
|
+
get removedCount() {
|
|
20
|
+
return this.removed.count;
|
|
21
|
+
}
|
|
22
|
+
get isMergeInProgress() {
|
|
23
|
+
return this.tag > 0;
|
|
24
|
+
}
|
|
25
|
+
lookup(key) {
|
|
26
|
+
let result = undefined;
|
|
27
|
+
if (key !== undefined && key !== this.lastNotFoundKey) {
|
|
28
|
+
result = this.map.get(key);
|
|
29
|
+
if (result) {
|
|
30
|
+
if (this.getKey(result.self) !== key) {
|
|
31
|
+
this.lastNotFoundKey = key;
|
|
32
|
+
result = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else
|
|
36
|
+
this.lastNotFoundKey = key;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
claim(key, resolution) {
|
|
41
|
+
const tag = this.tag;
|
|
42
|
+
if (tag < 0)
|
|
43
|
+
throw new Error('merge is not in progress');
|
|
44
|
+
let item = this.strictNextItem;
|
|
45
|
+
if (key !== (item ? this.getKey(item.self) : undefined))
|
|
46
|
+
item = this.lookup(key);
|
|
47
|
+
if (item) {
|
|
48
|
+
if (item.tag !== tag) {
|
|
49
|
+
item.tag = tag;
|
|
50
|
+
if (this.strict && item !== this.strictNextItem)
|
|
51
|
+
item.status = tag;
|
|
52
|
+
this.strictNextItem = item.next;
|
|
53
|
+
this.removed.exclude(item);
|
|
54
|
+
this.current.include(item);
|
|
55
|
+
if (resolution)
|
|
56
|
+
resolution.isDuplicate = false;
|
|
57
|
+
}
|
|
58
|
+
else if (resolution)
|
|
59
|
+
resolution.isDuplicate = true;
|
|
60
|
+
else
|
|
61
|
+
throw new Error(`duplicate item: ${key}`);
|
|
62
|
+
}
|
|
63
|
+
else if (resolution)
|
|
64
|
+
resolution.isDuplicate = false;
|
|
65
|
+
return item;
|
|
66
|
+
}
|
|
67
|
+
add(self) {
|
|
68
|
+
const key = this.getKey(self);
|
|
69
|
+
if (this.lookup(key) !== undefined)
|
|
70
|
+
throw new Error(`key is already in use: ${key}`);
|
|
71
|
+
let tag = this.tag;
|
|
72
|
+
if (tag < 0) {
|
|
73
|
+
tag = ~this.tag + 1;
|
|
74
|
+
this.tag = ~tag;
|
|
75
|
+
}
|
|
76
|
+
const item = new ItemImpl(self, tag);
|
|
77
|
+
this.map.set(key, item);
|
|
78
|
+
this.lastNotFoundKey = undefined;
|
|
79
|
+
this.strictNextItem = undefined;
|
|
80
|
+
this.current.include(item);
|
|
81
|
+
this.added.aux(item);
|
|
82
|
+
return item;
|
|
83
|
+
}
|
|
84
|
+
remove(item) {
|
|
85
|
+
const t = item;
|
|
86
|
+
if (!this.isRemoved(t)) {
|
|
87
|
+
this.current.exclude(t);
|
|
88
|
+
this.removed.include(t);
|
|
89
|
+
t.tag--;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
move(item, after) {
|
|
93
|
+
throw new Error('not implemented');
|
|
94
|
+
}
|
|
95
|
+
beginMerge() {
|
|
96
|
+
if (this.isMergeInProgress)
|
|
97
|
+
throw new Error('merge is in progress already');
|
|
98
|
+
this.tag = ~this.tag + 1;
|
|
99
|
+
this.strictNextItem = this.current.first;
|
|
100
|
+
this.removed.grab(this.current, false);
|
|
101
|
+
this.added.reset();
|
|
102
|
+
}
|
|
103
|
+
endMerge(error) {
|
|
104
|
+
if (!this.isMergeInProgress)
|
|
105
|
+
throw new Error('merge is ended already');
|
|
106
|
+
this.tag = ~this.tag;
|
|
107
|
+
if (error === undefined) {
|
|
108
|
+
const currentCount = this.current.count;
|
|
109
|
+
if (currentCount > 0) {
|
|
110
|
+
const getKey = this.getKey;
|
|
111
|
+
if (currentCount > this.removed.count) {
|
|
112
|
+
const map = this.map;
|
|
113
|
+
for (const x of this.removed.items())
|
|
114
|
+
map.delete(getKey(x.self));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
const map = this.map = new Map();
|
|
118
|
+
for (const x of this.current.items())
|
|
119
|
+
map.set(getKey(x.self), x);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else
|
|
123
|
+
this.map = new Map();
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.current.grab(this.removed, true);
|
|
127
|
+
const getKey = this.getKey;
|
|
128
|
+
for (const x of this.added.itemsViaAux()) {
|
|
129
|
+
this.map.delete(getKey(x.self));
|
|
130
|
+
this.current.exclude(x);
|
|
131
|
+
}
|
|
132
|
+
this.added.reset();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
resetAddedAndRemovedLists() {
|
|
136
|
+
this.removed.reset();
|
|
137
|
+
this.added.reset();
|
|
138
|
+
}
|
|
139
|
+
*items() {
|
|
140
|
+
let x = this.current.first;
|
|
141
|
+
while (x !== undefined) {
|
|
142
|
+
const next = x.next;
|
|
143
|
+
yield x;
|
|
144
|
+
x = next;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
*addedItems(reset) {
|
|
148
|
+
let x = this.added.first;
|
|
149
|
+
while (x !== undefined) {
|
|
150
|
+
const next = x.aux;
|
|
151
|
+
if (!this.isRemoved(x))
|
|
152
|
+
yield x;
|
|
153
|
+
x = next;
|
|
154
|
+
}
|
|
155
|
+
if (reset)
|
|
156
|
+
this.added.reset();
|
|
157
|
+
}
|
|
158
|
+
*removedItems(reset) {
|
|
159
|
+
let x = this.removed.first;
|
|
160
|
+
while (x !== undefined) {
|
|
161
|
+
const next = x.next;
|
|
162
|
+
yield x;
|
|
163
|
+
x = next;
|
|
164
|
+
}
|
|
165
|
+
if (reset)
|
|
166
|
+
this.removed.reset();
|
|
167
|
+
}
|
|
168
|
+
isAdded(item) {
|
|
169
|
+
const t = item;
|
|
170
|
+
let tag = this.tag;
|
|
171
|
+
if (tag < 0)
|
|
172
|
+
tag = ~tag;
|
|
173
|
+
return t.status === ~tag && t.tag > 0;
|
|
174
|
+
}
|
|
175
|
+
isMoved(item) {
|
|
176
|
+
const t = item;
|
|
177
|
+
let tag = this.tag;
|
|
178
|
+
if (tag < 0)
|
|
179
|
+
tag = ~tag;
|
|
180
|
+
return t.status === tag && t.tag > 0;
|
|
181
|
+
}
|
|
182
|
+
isRemoved(item) {
|
|
183
|
+
const t = item;
|
|
184
|
+
const tag = this.tag;
|
|
185
|
+
return tag > 0 ? t.tag < tag : t.tag < tag - 1;
|
|
186
|
+
}
|
|
187
|
+
isCurrent(item) {
|
|
188
|
+
const t = item;
|
|
189
|
+
return t.tag === this.tag;
|
|
190
|
+
}
|
|
191
|
+
markAsMoved(item) {
|
|
192
|
+
const t = item;
|
|
193
|
+
if (t.tag > 0)
|
|
194
|
+
t.status = t.tag;
|
|
195
|
+
}
|
|
196
|
+
static createItem(self) {
|
|
197
|
+
return new ItemImpl(self, 0);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
class ItemImpl {
|
|
201
|
+
constructor(self, tag) {
|
|
202
|
+
this.self = self;
|
|
203
|
+
this.tag = tag;
|
|
204
|
+
this.status = ~tag;
|
|
205
|
+
this.next = undefined;
|
|
206
|
+
this.prev = undefined;
|
|
207
|
+
this.aux = undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
class ItemChain {
|
|
211
|
+
constructor() {
|
|
212
|
+
this.count = 0;
|
|
213
|
+
this.first = undefined;
|
|
214
|
+
this.last = undefined;
|
|
215
|
+
}
|
|
216
|
+
*items() {
|
|
217
|
+
let x = this.first;
|
|
218
|
+
while (x !== undefined) {
|
|
219
|
+
const next = x.next;
|
|
220
|
+
yield x;
|
|
221
|
+
x = next;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
*itemsViaAux() {
|
|
225
|
+
let x = this.first;
|
|
226
|
+
while (x !== undefined) {
|
|
227
|
+
const next = x.aux;
|
|
228
|
+
yield x;
|
|
229
|
+
x = next;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
reset() {
|
|
233
|
+
this.count = 0;
|
|
234
|
+
this.first = undefined;
|
|
235
|
+
this.last = undefined;
|
|
236
|
+
}
|
|
237
|
+
grab(from, join) {
|
|
238
|
+
const head = from.first;
|
|
239
|
+
if (join && head) {
|
|
240
|
+
const last = this.last;
|
|
241
|
+
head.prev = last;
|
|
242
|
+
if (last)
|
|
243
|
+
this.last = last.next = head;
|
|
244
|
+
else
|
|
245
|
+
this.first = this.last = head;
|
|
246
|
+
this.count += from.count;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
this.count = from.count;
|
|
250
|
+
this.first = head;
|
|
251
|
+
this.last = from.last;
|
|
252
|
+
}
|
|
253
|
+
from.reset();
|
|
254
|
+
}
|
|
255
|
+
include(item) {
|
|
256
|
+
const last = this.last;
|
|
257
|
+
item.prev = last;
|
|
258
|
+
item.next = undefined;
|
|
259
|
+
if (last)
|
|
260
|
+
this.last = last.next = item;
|
|
261
|
+
else
|
|
262
|
+
this.first = this.last = item;
|
|
263
|
+
this.count++;
|
|
264
|
+
}
|
|
265
|
+
exclude(item) {
|
|
266
|
+
if (item.prev !== undefined)
|
|
267
|
+
item.prev.next = item.next;
|
|
268
|
+
if (item.next !== undefined)
|
|
269
|
+
item.next.prev = item.prev;
|
|
270
|
+
if (item === this.first)
|
|
271
|
+
this.first = item.next;
|
|
272
|
+
this.count--;
|
|
273
|
+
}
|
|
274
|
+
aux(item) {
|
|
275
|
+
item.aux = undefined;
|
|
276
|
+
const last = this.last;
|
|
277
|
+
if (last)
|
|
278
|
+
this.last = last.aux = item;
|
|
279
|
+
else
|
|
280
|
+
this.first = this.last = item;
|
|
281
|
+
this.count++;
|
|
282
|
+
}
|
|
283
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reactronic",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.316",
|
|
4
4
|
"description": "Reactronic - Transactional Reactive State Management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/dist/source/api.js",
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/nezaboodka/reactronic/blob/master/README.md#readme",
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@types/node": "18.0.
|
|
34
|
-
"@types/react": "18.0.
|
|
35
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
36
|
-
"@typescript-eslint/parser": "5.
|
|
37
|
-
"ava": "4.3.
|
|
33
|
+
"@types/node": "18.0.5",
|
|
34
|
+
"@types/react": "18.0.15",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "5.30.6",
|
|
36
|
+
"@typescript-eslint/parser": "5.30.6",
|
|
37
|
+
"ava": "4.3.1",
|
|
38
38
|
"c8": "7.11.3",
|
|
39
|
-
"eslint": "8.
|
|
39
|
+
"eslint": "8.19.0",
|
|
40
40
|
"react": "18.2.0",
|
|
41
|
-
"ts-node": "10.
|
|
41
|
+
"ts-node": "10.9.1",
|
|
42
42
|
"typescript": "4.7.3"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|