reactronic 0.94.25037 → 0.95.25043
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 +67 -66
- package/build/dist/source/Enums.d.ts +3 -3
- package/build/dist/source/Enums.js +3 -3
- package/build/dist/source/OperationEx.d.ts +2 -2
- package/build/dist/source/OperationEx.js +5 -5
- package/build/dist/source/Options.d.ts +2 -2
- package/build/dist/source/Pipe.d.ts +2 -2
- package/build/dist/source/Pipe.js +2 -2
- package/build/dist/source/Ref.d.ts +2 -2
- package/build/dist/source/Ref.js +5 -5
- package/build/dist/source/System.d.ts +14 -14
- package/build/dist/source/System.js +22 -22
- package/build/dist/source/api.d.ts +7 -7
- package/build/dist/source/api.js +6 -6
- package/build/dist/source/core/Changeset.js +1 -1
- package/build/dist/source/core/Data.d.ts +1 -1
- package/build/dist/source/core/Indicator.d.ts +2 -2
- package/build/dist/source/core/Indicator.js +2 -2
- package/build/dist/source/core/Journal.d.ts +2 -2
- package/build/dist/source/core/Journal.js +2 -2
- package/build/dist/source/core/Mvcc.d.ts +10 -10
- package/build/dist/source/core/Mvcc.js +19 -19
- package/build/dist/source/core/MvccArray.d.ts +3 -3
- package/build/dist/source/core/MvccArray.js +4 -4
- package/build/dist/source/core/MvccMap.d.ts +3 -3
- package/build/dist/source/core/MvccMap.js +4 -4
- package/build/dist/source/core/MvccReconciliationList.d.ts +12 -11
- package/build/dist/source/core/MvccReconciliationList.js +12 -11
- package/build/dist/source/core/Operation.d.ts +8 -8
- package/build/dist/source/core/Operation.js +41 -41
- package/build/dist/source/core/Transaction.js +28 -28
- package/build/dist/source/core/TreeNode.d.ts +17 -17
- package/build/dist/source/core/TreeNode.js +52 -52
- package/build/dist/source/util/LinkedList.d.ts +52 -0
- package/build/dist/source/util/LinkedList.js +177 -0
- package/build/dist/source/util/LinkedListRenovation.d.ts +20 -0
- package/build/dist/source/util/LinkedListRenovation.js +134 -0
- package/build/dist/source/util/ReconciliationList.d.ts +10 -8
- package/build/dist/source/util/ReconciliationList.js +59 -58
- package/package.json +10 -9
|
@@ -21,9 +21,9 @@ import { Uri } from "../util/Uri.js";
|
|
|
21
21
|
import { ReconciliationList } from "../util/ReconciliationList.js";
|
|
22
22
|
import { emitLetters, flags, getCallerInfo, proceedSyncOrAsync } from "../util/Utils.js";
|
|
23
23
|
import { Priority, Mode, Isolation, Reentrance } from "../Enums.js";
|
|
24
|
-
import {
|
|
24
|
+
import { SxObject } from "../core/Mvcc.js";
|
|
25
25
|
import { Transaction } from "../core/Transaction.js";
|
|
26
|
-
import { ReactiveSystem, options,
|
|
26
|
+
import { ReactiveSystem, options, signal, reaction, runTransactional, runNonReactive, manageReaction, disposeSignallingObject } from "../System.js";
|
|
27
27
|
export function declare(driver, scriptOrDeclaration, scriptAsync, key, mode, preparation, preparationAsync, finalization, triggers, basis) {
|
|
28
28
|
let result;
|
|
29
29
|
let declaration;
|
|
@@ -46,17 +46,17 @@ export function declare(driver, scriptOrDeclaration, scriptAsync, key, mode, pre
|
|
|
46
46
|
if (result.driver !== driver && driver !== undefined)
|
|
47
47
|
throw misuse(`changing element driver is not yet supported: "${result.driver.name}" -> "${driver === null || driver === void 0 ? void 0 : driver.name}"`);
|
|
48
48
|
const exTriggers = result.declaration.triggers;
|
|
49
|
-
if (
|
|
49
|
+
if (signalsAreEqual(declaration.triggers, exTriggers))
|
|
50
50
|
declaration.triggers = exTriggers;
|
|
51
51
|
result.declaration = declaration;
|
|
52
52
|
}
|
|
53
53
|
else {
|
|
54
|
-
result = new
|
|
54
|
+
result = new ReactiveTreeNode$(effectiveKey || generateKey(owner), driver, declaration, owner);
|
|
55
55
|
result.slot = children.add(result);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
|
-
result = new
|
|
59
|
+
result = new ReactiveTreeNode$(effectiveKey || generateKey(owner), driver, declaration, owner);
|
|
60
60
|
result.slot = ReconciliationList.createItem(result);
|
|
61
61
|
}
|
|
62
62
|
return result;
|
|
@@ -74,7 +74,7 @@ export function launch(node, triggers) {
|
|
|
74
74
|
}
|
|
75
75
|
export class ReactiveTreeNode {
|
|
76
76
|
static get current() {
|
|
77
|
-
return
|
|
77
|
+
return ReactiveTreeNode$.nodeSlot.instance;
|
|
78
78
|
}
|
|
79
79
|
static get isFirstScriptRun() {
|
|
80
80
|
return ReactiveTreeNode.current.stamp === 1;
|
|
@@ -82,7 +82,7 @@ export class ReactiveTreeNode {
|
|
|
82
82
|
static launchScript(node, triggers) {
|
|
83
83
|
const impl = node;
|
|
84
84
|
const declaration = impl.declaration;
|
|
85
|
-
if (node.stamp >= Number.MAX_SAFE_INTEGER || !
|
|
85
|
+
if (node.stamp >= Number.MAX_SAFE_INTEGER || !signalsAreEqual(triggers, declaration.triggers)) {
|
|
86
86
|
declaration.triggers = triggers;
|
|
87
87
|
launchScriptViaSlot(impl.slot);
|
|
88
88
|
}
|
|
@@ -92,7 +92,7 @@ export class ReactiveTreeNode {
|
|
|
92
92
|
launchFinalizationViaSlot(impl.slot, true, true);
|
|
93
93
|
}
|
|
94
94
|
static launchNestedNodesThenDo(action) {
|
|
95
|
-
launchNestedNodesThenDoImpl(
|
|
95
|
+
launchNestedNodesThenDoImpl(ReactiveTreeNode$.nodeSlot, undefined, action);
|
|
96
96
|
}
|
|
97
97
|
static markAsMounted(node, yes) {
|
|
98
98
|
const n = node;
|
|
@@ -131,10 +131,10 @@ export class ReactiveTreeNode {
|
|
|
131
131
|
ReactiveTreeNode.forEachChildRecursively(child.instance, action);
|
|
132
132
|
}
|
|
133
133
|
static getDefaultLoggingOptions() {
|
|
134
|
-
return
|
|
134
|
+
return ReactiveTreeNode$.logging;
|
|
135
135
|
}
|
|
136
136
|
static setDefaultLoggingOptions(logging) {
|
|
137
|
-
|
|
137
|
+
ReactiveTreeNode$.logging = logging;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
ReactiveTreeNode.shortFrameDuration = 16;
|
|
@@ -173,13 +173,13 @@ export class ReactiveTreeVariable {
|
|
|
173
173
|
this.defaultValue = defaultValue;
|
|
174
174
|
}
|
|
175
175
|
set value(value) {
|
|
176
|
-
|
|
176
|
+
ReactiveTreeNode$.setTreeVariableValue(this, value);
|
|
177
177
|
}
|
|
178
178
|
get value() {
|
|
179
|
-
return
|
|
179
|
+
return ReactiveTreeNode$.useTreeVariableValue(this);
|
|
180
180
|
}
|
|
181
181
|
get valueOrUndefined() {
|
|
182
|
-
return
|
|
182
|
+
return ReactiveTreeNode$.tryUseTreeVariableValue(this);
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
export function generateKey(owner) {
|
|
@@ -234,7 +234,7 @@ function invokeFinalizationUsingBasisChain(element, declaration) {
|
|
|
234
234
|
else if (basis)
|
|
235
235
|
invokeFinalizationUsingBasisChain(element, basis);
|
|
236
236
|
}
|
|
237
|
-
class
|
|
237
|
+
class ReactiveTreeNodeContext$ extends SxObject {
|
|
238
238
|
constructor(variable, value) {
|
|
239
239
|
super();
|
|
240
240
|
this.next = undefined;
|
|
@@ -243,14 +243,14 @@ class ReactiveTreeNodeContextImpl extends ObservableObject {
|
|
|
243
243
|
}
|
|
244
244
|
}
|
|
245
245
|
__decorate([
|
|
246
|
-
|
|
246
|
+
signal(false),
|
|
247
247
|
__metadata("design:type", Object)
|
|
248
|
-
],
|
|
248
|
+
], ReactiveTreeNodeContext$.prototype, "next", void 0);
|
|
249
249
|
__decorate([
|
|
250
|
-
|
|
250
|
+
signal(false),
|
|
251
251
|
__metadata("design:type", ReactiveTreeVariable)
|
|
252
|
-
],
|
|
253
|
-
class
|
|
252
|
+
], ReactiveTreeNodeContext$.prototype, "variable", void 0);
|
|
253
|
+
class ReactiveTreeNode$ extends ReactiveTreeNode {
|
|
254
254
|
constructor(key, driver, declaration, owner) {
|
|
255
255
|
super();
|
|
256
256
|
const thisAsUnknown = this;
|
|
@@ -277,9 +277,9 @@ class ReactiveTreeNodeImpl extends ReactiveTreeNode {
|
|
|
277
277
|
this.numerator = 0;
|
|
278
278
|
this.priority = Priority.realtime;
|
|
279
279
|
this.childrenShuffling = false;
|
|
280
|
-
|
|
280
|
+
ReactiveTreeNode$.grandNodeCount++;
|
|
281
281
|
if (this.has(Mode.autonomous))
|
|
282
|
-
|
|
282
|
+
ReactiveTreeNode$.disposableNodeCount++;
|
|
283
283
|
}
|
|
284
284
|
getUri(relativeTo) {
|
|
285
285
|
const path = [];
|
|
@@ -309,7 +309,7 @@ class ReactiveTreeNodeImpl extends ReactiveTreeNode {
|
|
|
309
309
|
configureReactivity(options) {
|
|
310
310
|
if (this.stamp < Number.MAX_SAFE_INTEGER - 1 || !this.has(Mode.autonomous))
|
|
311
311
|
throw misuse("reactronic can be configured only for elements with autonomous mode and only during preparation");
|
|
312
|
-
return
|
|
312
|
+
return manageReaction(this.script).configure(options);
|
|
313
313
|
}
|
|
314
314
|
static get nodeSlot() {
|
|
315
315
|
if (!gNodeSlot)
|
|
@@ -318,35 +318,35 @@ class ReactiveTreeNodeImpl extends ReactiveTreeNode {
|
|
|
318
318
|
}
|
|
319
319
|
static tryUseTreeVariableValue(variable) {
|
|
320
320
|
var _a, _b;
|
|
321
|
-
let node =
|
|
321
|
+
let node = ReactiveTreeNode$.nodeSlot.instance;
|
|
322
322
|
while (((_a = node.context) === null || _a === void 0 ? void 0 : _a.variable) !== variable && node.owner !== node)
|
|
323
323
|
node = node.outer.slot.instance;
|
|
324
324
|
return (_b = node.context) === null || _b === void 0 ? void 0 : _b.value;
|
|
325
325
|
}
|
|
326
326
|
static useTreeVariableValue(variable) {
|
|
327
327
|
var _a;
|
|
328
|
-
const result = (_a =
|
|
328
|
+
const result = (_a = ReactiveTreeNode$.tryUseTreeVariableValue(variable)) !== null && _a !== void 0 ? _a : variable.defaultValue;
|
|
329
329
|
if (!result)
|
|
330
330
|
throw misuse("unknown node variable");
|
|
331
331
|
return result;
|
|
332
332
|
}
|
|
333
333
|
static setTreeVariableValue(variable, value) {
|
|
334
|
-
const node =
|
|
334
|
+
const node = ReactiveTreeNode$.nodeSlot.instance;
|
|
335
335
|
const owner = node.owner;
|
|
336
|
-
const hostCtx =
|
|
336
|
+
const hostCtx = runNonReactive(() => { 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
|
+
runTransactional({ isolation: Isolation.joinAsNestedTransaction }, () => {
|
|
343
343
|
const ctx = node.context;
|
|
344
344
|
if (ctx) {
|
|
345
345
|
ctx.variable = variable;
|
|
346
346
|
ctx.value = value;
|
|
347
347
|
}
|
|
348
348
|
else
|
|
349
|
-
node.context = new
|
|
349
|
+
node.context = new ReactiveTreeNodeContext$(variable, value);
|
|
350
350
|
});
|
|
351
351
|
}
|
|
352
352
|
else if (hostCtx)
|
|
@@ -355,21 +355,21 @@ class ReactiveTreeNodeImpl extends ReactiveTreeNode {
|
|
|
355
355
|
node.outer = owner.outer;
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
358
|
+
ReactiveTreeNode$.logging = undefined;
|
|
359
|
+
ReactiveTreeNode$.grandNodeCount = 0;
|
|
360
|
+
ReactiveTreeNode$.disposableNodeCount = 0;
|
|
361
361
|
__decorate([
|
|
362
|
-
|
|
362
|
+
reaction,
|
|
363
363
|
options({
|
|
364
364
|
reentrance: Reentrance.cancelAndWaitPrevious,
|
|
365
365
|
allowObsoleteToFinish: true,
|
|
366
|
-
|
|
366
|
+
signalArgs: true,
|
|
367
367
|
noSideEffects: false,
|
|
368
368
|
}),
|
|
369
369
|
__metadata("design:type", Function),
|
|
370
370
|
__metadata("design:paramtypes", [Object]),
|
|
371
371
|
__metadata("design:returntype", void 0)
|
|
372
|
-
],
|
|
372
|
+
], ReactiveTreeNode$.prototype, "script", null);
|
|
373
373
|
function gatherAuthorityAndPath(node, path, relativeTo) {
|
|
374
374
|
let authority;
|
|
375
375
|
if (node.owner !== node && node.owner !== relativeTo) {
|
|
@@ -454,9 +454,9 @@ function runNestedScriptsIncrementally(owner, stamp, allChildren, items, priorit
|
|
|
454
454
|
return __awaiter(this, void 0, void 0, function* () {
|
|
455
455
|
yield Transaction.requestNextFrame();
|
|
456
456
|
const node = owner.instance;
|
|
457
|
-
if (!Transaction.isCanceled || !Transaction.isFrameOver(1,
|
|
458
|
-
let outerPriority =
|
|
459
|
-
|
|
457
|
+
if (!Transaction.isCanceled || !Transaction.isFrameOver(1, ReactiveTreeNode$.shortFrameDuration / 3)) {
|
|
458
|
+
let outerPriority = ReactiveTreeNode$.currentScriptPriority;
|
|
459
|
+
ReactiveTreeNode$.currentScriptPriority = priority;
|
|
460
460
|
try {
|
|
461
461
|
if (node.childrenShuffling)
|
|
462
462
|
shuffle(items);
|
|
@@ -465,10 +465,10 @@ function runNestedScriptsIncrementally(owner, stamp, allChildren, items, priorit
|
|
|
465
465
|
for (const child of items) {
|
|
466
466
|
launchScriptViaSlot(child);
|
|
467
467
|
if (Transaction.isFrameOver(1, frameDuration)) {
|
|
468
|
-
|
|
468
|
+
ReactiveTreeNode$.currentScriptPriority = outerPriority;
|
|
469
469
|
yield Transaction.requestNextFrame(0);
|
|
470
|
-
outerPriority =
|
|
471
|
-
|
|
470
|
+
outerPriority = ReactiveTreeNode$.currentScriptPriority;
|
|
471
|
+
ReactiveTreeNode$.currentScriptPriority = priority;
|
|
472
472
|
frameDuration = Math.min(4 * frameDuration, Math.min(frameDurationLimit, ReactiveTreeNode.frameDuration));
|
|
473
473
|
}
|
|
474
474
|
if (Transaction.isCanceled && Transaction.isFrameOver(1, ReactiveTreeNode.shortFrameDuration / 3))
|
|
@@ -476,7 +476,7 @@ function runNestedScriptsIncrementally(owner, stamp, allChildren, items, priorit
|
|
|
476
476
|
}
|
|
477
477
|
}
|
|
478
478
|
finally {
|
|
479
|
-
|
|
479
|
+
ReactiveTreeNode$.currentScriptPriority = outerPriority;
|
|
480
480
|
}
|
|
481
481
|
}
|
|
482
482
|
});
|
|
@@ -489,23 +489,23 @@ function launchScriptViaSlot(nodeSlot) {
|
|
|
489
489
|
Transaction.outside(() => {
|
|
490
490
|
if (ReactiveSystem.isLogging)
|
|
491
491
|
ReactiveSystem.setLoggingHint(node.element, node.key);
|
|
492
|
-
|
|
492
|
+
manageReaction(node.script).configure({
|
|
493
493
|
order: node.level,
|
|
494
494
|
});
|
|
495
495
|
});
|
|
496
496
|
}
|
|
497
|
-
|
|
497
|
+
runNonReactive(node.script, node.declaration.triggers);
|
|
498
498
|
}
|
|
499
499
|
else if (node.owner !== node)
|
|
500
500
|
runScriptNow(nodeSlot);
|
|
501
501
|
else
|
|
502
|
-
|
|
502
|
+
runTransactional(() => runScriptNow(nodeSlot));
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
function mountOrRemountIfNecessary(node) {
|
|
506
506
|
const driver = node.driver;
|
|
507
507
|
if (node.stamp === Number.MAX_SAFE_INTEGER) {
|
|
508
|
-
|
|
508
|
+
runNonReactive(() => {
|
|
509
509
|
node.stamp = Number.MAX_SAFE_INTEGER - 1;
|
|
510
510
|
driver.runPreparation(node);
|
|
511
511
|
if (!node.has(Mode.external)) {
|
|
@@ -516,7 +516,7 @@ function mountOrRemountIfNecessary(node) {
|
|
|
516
516
|
});
|
|
517
517
|
}
|
|
518
518
|
else if (node.isMoved && !node.has(Mode.external) && node.host !== node)
|
|
519
|
-
|
|
519
|
+
runNonReactive(() => driver.runMount(node));
|
|
520
520
|
}
|
|
521
521
|
function runScriptNow(nodeSlot) {
|
|
522
522
|
const node = nodeSlot.instance;
|
|
@@ -549,7 +549,7 @@ function launchFinalizationViaSlot(nodeSlot, isLeader, individual) {
|
|
|
549
549
|
if (individual && node.key !== node.declaration.key && !driver.isPartition)
|
|
550
550
|
console.log(`WARNING: it is recommended to assign explicit key for conditional element in order to avoid unexpected side effects: ${node.key}`);
|
|
551
551
|
node.stamp = ~node.stamp;
|
|
552
|
-
const childrenAreLeaders =
|
|
552
|
+
const childrenAreLeaders = runNonReactive(() => driver.runFinalization(node, isLeader));
|
|
553
553
|
if (node.has(Mode.autonomous)) {
|
|
554
554
|
nodeSlot.aux = undefined;
|
|
555
555
|
const last = gLastToDispose;
|
|
@@ -558,13 +558,13 @@ function launchFinalizationViaSlot(nodeSlot, isLeader, individual) {
|
|
|
558
558
|
else
|
|
559
559
|
gFirstToDispose = gLastToDispose = nodeSlot;
|
|
560
560
|
if (gFirstToDispose === nodeSlot)
|
|
561
|
-
|
|
561
|
+
runTransactional({ isolation: Isolation.disjoinForInternalDisposal, hint: `runDisposalLoop(initiator=${nodeSlot.instance.key})` }, () => {
|
|
562
562
|
void runDisposalLoop().then(NOP, error => console.log(error));
|
|
563
563
|
});
|
|
564
564
|
}
|
|
565
565
|
for (const child of node.children.items())
|
|
566
566
|
launchFinalizationViaSlot(child, childrenAreLeaders, false);
|
|
567
|
-
|
|
567
|
+
ReactiveTreeNode$.grandNodeCount--;
|
|
568
568
|
}
|
|
569
569
|
}
|
|
570
570
|
function runDisposalLoop() {
|
|
@@ -574,9 +574,9 @@ function runDisposalLoop() {
|
|
|
574
574
|
while (slot !== undefined) {
|
|
575
575
|
if (Transaction.isFrameOver(500, 5))
|
|
576
576
|
yield Transaction.requestNextFrame();
|
|
577
|
-
|
|
577
|
+
disposeSignallingObject(slot.instance);
|
|
578
578
|
slot = slot.aux;
|
|
579
|
-
|
|
579
|
+
ReactiveTreeNode$.disposableNodeCount--;
|
|
580
580
|
}
|
|
581
581
|
gFirstToDispose = gLastToDispose = undefined;
|
|
582
582
|
});
|
|
@@ -602,7 +602,7 @@ function runInsideContextOfNode(nodeSlot, func, ...args) {
|
|
|
602
602
|
gNodeSlot = outer;
|
|
603
603
|
}
|
|
604
604
|
}
|
|
605
|
-
export function
|
|
605
|
+
export function signalsAreEqual(a1, a2) {
|
|
606
606
|
let result = a1 === a2;
|
|
607
607
|
if (!result) {
|
|
608
608
|
if (Array.isArray(a1)) {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type Extractor<T, Result> = (item: T) => Result;
|
|
2
|
+
export type KeyExtractor<T> = Extractor<T, string | undefined>;
|
|
3
|
+
export declare class LinkedList<T extends LinkedItem<T>> {
|
|
4
|
+
readonly keyOf: KeyExtractor<T>;
|
|
5
|
+
private isStrictOrder$;
|
|
6
|
+
private map;
|
|
7
|
+
items$: LinkedSubList<T>;
|
|
8
|
+
former$: LinkedSubList<T> | undefined;
|
|
9
|
+
constructor(keyExtractor: KeyExtractor<T>, isStrictOrder?: boolean);
|
|
10
|
+
get isStrictOrder(): boolean;
|
|
11
|
+
set isStrictOrder(value: boolean);
|
|
12
|
+
get isRenovationInProgress(): boolean;
|
|
13
|
+
get count(): number;
|
|
14
|
+
items(): Generator<T>;
|
|
15
|
+
lookup(key: string | undefined): T | undefined;
|
|
16
|
+
add(item: T, before?: T): void;
|
|
17
|
+
move(item: T, before: T | undefined): void;
|
|
18
|
+
remove(item: T): void;
|
|
19
|
+
static move$<T extends LinkedItem<T>>(list: LinkedList<T>, item: T, before: T | undefined): void;
|
|
20
|
+
static remove$<T extends LinkedItem<T>>(list: LinkedList<T>, item: T): void;
|
|
21
|
+
static removeKey$<T extends LinkedItem<T>>(list: LinkedList<T>, key: string | undefined): void;
|
|
22
|
+
}
|
|
23
|
+
export declare enum Mark {
|
|
24
|
+
prolonged = 0,
|
|
25
|
+
added = 1,
|
|
26
|
+
modified = 2,
|
|
27
|
+
removed = 3
|
|
28
|
+
}
|
|
29
|
+
export declare class LinkedItem<T extends LinkedItem<T>> {
|
|
30
|
+
private list$;
|
|
31
|
+
private next$;
|
|
32
|
+
private prev$;
|
|
33
|
+
private status;
|
|
34
|
+
constructor();
|
|
35
|
+
get list(): LinkedSubList<T> | undefined;
|
|
36
|
+
get next(): T | undefined;
|
|
37
|
+
get prev(): T | undefined;
|
|
38
|
+
get mark(): Mark;
|
|
39
|
+
get rank(): number;
|
|
40
|
+
get isManagedExternally(): boolean;
|
|
41
|
+
static setStatus$<T extends LinkedItem<T>>(item: T, mark: Mark, rank: number): void;
|
|
42
|
+
static link$<T extends LinkedItem<T>>(list: LinkedSubList<T> | undefined, item: T, before: T | undefined): void;
|
|
43
|
+
private static unlink;
|
|
44
|
+
}
|
|
45
|
+
export declare class LinkedSubList<T extends LinkedItem<T>> {
|
|
46
|
+
count: number;
|
|
47
|
+
first?: T;
|
|
48
|
+
last?: T;
|
|
49
|
+
items(): Generator<T>;
|
|
50
|
+
clear(): void;
|
|
51
|
+
grab(from: LinkedSubList<T>, join: boolean): void;
|
|
52
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { misuse } from "./Dbg.js";
|
|
2
|
+
export class LinkedList {
|
|
3
|
+
constructor(keyExtractor, isStrictOrder = false) {
|
|
4
|
+
this.keyOf = keyExtractor;
|
|
5
|
+
this.isStrictOrder$ = isStrictOrder;
|
|
6
|
+
this.map = new Map();
|
|
7
|
+
this.items$ = new LinkedSubList();
|
|
8
|
+
this.former$ = undefined;
|
|
9
|
+
}
|
|
10
|
+
get isStrictOrder() { return this.isStrictOrder$; }
|
|
11
|
+
set isStrictOrder(value) {
|
|
12
|
+
if (this.former$ !== undefined)
|
|
13
|
+
throw misuse("cannot change strict mode in the middle of renovation");
|
|
14
|
+
this.isStrictOrder$ = value;
|
|
15
|
+
}
|
|
16
|
+
get isRenovationInProgress() {
|
|
17
|
+
return this.former$ !== undefined;
|
|
18
|
+
}
|
|
19
|
+
get count() {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
return this.items$.count + ((_b = (_a = this.former$) === null || _a === void 0 ? void 0 : _a.count) !== null && _b !== void 0 ? _b : 0);
|
|
22
|
+
}
|
|
23
|
+
items() {
|
|
24
|
+
return this.items$.items();
|
|
25
|
+
}
|
|
26
|
+
lookup(key) {
|
|
27
|
+
return this.map.get(key);
|
|
28
|
+
}
|
|
29
|
+
add(item, before) {
|
|
30
|
+
const key = this.keyOf(item);
|
|
31
|
+
if (this.map.get(key) !== undefined)
|
|
32
|
+
throw misuse(`item with given key already exists: ${key}`);
|
|
33
|
+
this.map.set(key, item);
|
|
34
|
+
LinkedItem.link$(this.items$, item, before);
|
|
35
|
+
}
|
|
36
|
+
move(item, before) {
|
|
37
|
+
if (item.list !== this.items$)
|
|
38
|
+
throw misuse("cannot move item that belongs to another list");
|
|
39
|
+
if (!item.isManagedExternally)
|
|
40
|
+
throw misuse("cannot move given item outside of renovation cycle");
|
|
41
|
+
LinkedList.move$(this, item, before);
|
|
42
|
+
}
|
|
43
|
+
remove(item) {
|
|
44
|
+
if (item.list !== this.items$)
|
|
45
|
+
throw misuse("cannot remove item that belongs to another list");
|
|
46
|
+
if (!item.isManagedExternally)
|
|
47
|
+
throw misuse("cannot remove given item outside of renovation cycle");
|
|
48
|
+
LinkedList.remove$(this, item);
|
|
49
|
+
}
|
|
50
|
+
static move$(list, item, before) {
|
|
51
|
+
LinkedItem.link$(list.items$, item, before);
|
|
52
|
+
}
|
|
53
|
+
static remove$(list, item) {
|
|
54
|
+
LinkedList.removeKey$(list, list.keyOf(item));
|
|
55
|
+
LinkedItem.link$(undefined, item, undefined);
|
|
56
|
+
}
|
|
57
|
+
static removeKey$(list, key) {
|
|
58
|
+
list.map.delete(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export var Mark;
|
|
62
|
+
(function (Mark) {
|
|
63
|
+
Mark[Mark["prolonged"] = 0] = "prolonged";
|
|
64
|
+
Mark[Mark["added"] = 1] = "added";
|
|
65
|
+
Mark[Mark["modified"] = 2] = "modified";
|
|
66
|
+
Mark[Mark["removed"] = 3] = "removed";
|
|
67
|
+
})(Mark || (Mark = {}));
|
|
68
|
+
const MARK_MOD = 4;
|
|
69
|
+
export class LinkedItem {
|
|
70
|
+
constructor() {
|
|
71
|
+
this.list$ = undefined;
|
|
72
|
+
this.next$ = undefined;
|
|
73
|
+
this.prev$ = undefined;
|
|
74
|
+
this.status = 0;
|
|
75
|
+
}
|
|
76
|
+
get list() { return this.list$; }
|
|
77
|
+
get next() { return this.next$; }
|
|
78
|
+
get prev() { return this.prev$; }
|
|
79
|
+
get mark() { return this.status % MARK_MOD; }
|
|
80
|
+
get rank() { return Math.trunc(this.status / MARK_MOD); }
|
|
81
|
+
get isManagedExternally() { return this.status === 0; }
|
|
82
|
+
static setStatus$(item, mark, rank) {
|
|
83
|
+
item.status = rank * MARK_MOD + mark;
|
|
84
|
+
}
|
|
85
|
+
static link$(list, item, before) {
|
|
86
|
+
if (before === undefined) {
|
|
87
|
+
LinkedItem.unlink(item);
|
|
88
|
+
if (list !== undefined) {
|
|
89
|
+
item.list$ = list;
|
|
90
|
+
const last = list.last;
|
|
91
|
+
item.prev$ = last;
|
|
92
|
+
item.next$ = undefined;
|
|
93
|
+
if (last !== undefined)
|
|
94
|
+
list.last = last.next$ = item;
|
|
95
|
+
else
|
|
96
|
+
list.first = list.last = item;
|
|
97
|
+
list.count++;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
item.list$ = undefined;
|
|
101
|
+
item.next$ = undefined;
|
|
102
|
+
item.prev$ = undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
if (list === before.list && list !== undefined) {
|
|
107
|
+
LinkedItem.unlink(item);
|
|
108
|
+
const after = before.prev$;
|
|
109
|
+
item.prev$ = after;
|
|
110
|
+
item.next$ = before;
|
|
111
|
+
before.prev$ = item;
|
|
112
|
+
if (after !== undefined)
|
|
113
|
+
after.next$ = item;
|
|
114
|
+
if (before == list.first)
|
|
115
|
+
list.first = item;
|
|
116
|
+
item.list$ = list;
|
|
117
|
+
list.count++;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
if (list !== before.list)
|
|
121
|
+
throw misuse("sibling is not in the given list");
|
|
122
|
+
else if (before.list === undefined)
|
|
123
|
+
throw misuse("cannot link to sibling that is not in a list");
|
|
124
|
+
else
|
|
125
|
+
throw misuse("linked list invariant is broken");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
static unlink(item) {
|
|
130
|
+
const list = item.list;
|
|
131
|
+
if (list) {
|
|
132
|
+
const prev = item.prev$;
|
|
133
|
+
if (prev !== undefined)
|
|
134
|
+
prev.next$ = item.next$;
|
|
135
|
+
const next = item.next$;
|
|
136
|
+
if (next !== undefined)
|
|
137
|
+
next.prev$ = item.prev$;
|
|
138
|
+
if (item === list.first)
|
|
139
|
+
list.first = item.next$;
|
|
140
|
+
if (item === list.last)
|
|
141
|
+
list.last = undefined;
|
|
142
|
+
list.count--;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export class LinkedSubList {
|
|
147
|
+
constructor() {
|
|
148
|
+
this.count = 0;
|
|
149
|
+
this.first = undefined;
|
|
150
|
+
this.last = undefined;
|
|
151
|
+
}
|
|
152
|
+
*items() {
|
|
153
|
+
let x = this.first;
|
|
154
|
+
while (x !== undefined) {
|
|
155
|
+
const next = x.next;
|
|
156
|
+
yield x;
|
|
157
|
+
x = next;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
clear() {
|
|
161
|
+
this.count = 0;
|
|
162
|
+
this.first = undefined;
|
|
163
|
+
this.last = undefined;
|
|
164
|
+
}
|
|
165
|
+
grab(from, join) {
|
|
166
|
+
const head = from.first;
|
|
167
|
+
if (join !== undefined && head !== undefined) {
|
|
168
|
+
this.count += from.count;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
this.count = from.count;
|
|
172
|
+
this.first = head;
|
|
173
|
+
this.last = from.last;
|
|
174
|
+
}
|
|
175
|
+
from.clear();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { LinkedList, LinkedItem } from "./LinkedList.js";
|
|
2
|
+
export declare class LinkedListRenovation<T extends LinkedItem<T>> {
|
|
3
|
+
readonly list: LinkedList<T>;
|
|
4
|
+
readonly diff: Array<T> | undefined;
|
|
5
|
+
private lost$;
|
|
6
|
+
private expected;
|
|
7
|
+
private absent;
|
|
8
|
+
constructor(list: LinkedList<T>, diff?: Array<T>);
|
|
9
|
+
lookup(key: string | undefined): T | undefined;
|
|
10
|
+
tryToProlonge(key: string, resolution?: {
|
|
11
|
+
isDuplicate: boolean;
|
|
12
|
+
}, error?: string): T | undefined;
|
|
13
|
+
thisIsAdded(item: T, before?: T): T;
|
|
14
|
+
thisIsModified(item: T): void;
|
|
15
|
+
thisIsMoved(item: T, before: T | undefined): void;
|
|
16
|
+
thisIsRemoved(item: T): void;
|
|
17
|
+
get lostItemCount(): number;
|
|
18
|
+
lostItems(): Generator<T>;
|
|
19
|
+
done(error?: unknown): void;
|
|
20
|
+
}
|