circular-history 1.0.2 → 1.0.4
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/package.json +15 -21
- package/.husky/pre-commit +0 -1
- package/.husky/pre-push +0 -1
- package/.nvmrc +0 -1
- package/.prettierignore +0 -3
- package/.prettierrc.json +0 -7
- package/LICENCE +0 -20
- package/dist/README.md +0 -121
- package/dist/package.json +0 -21
- package/scripts/prepare-release.js +0 -13
- package/src/circular-history.js +0 -185
- package/src/circular-history.spec.js +0 -432
- package/src/index.d.ts +0 -31
- /package/{dist/index.d.ts → index.d.ts} +0 -0
- /package/{dist/index.js → index.js} +0 -0
package/package.json
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circular-history",
|
|
3
|
-
"version": "1.0.2",
|
|
4
3
|
"type": "module",
|
|
5
|
-
"
|
|
4
|
+
"version": "1.0.4",
|
|
6
5
|
"description": "Data structure with a fixed number of items of a specific type. Supports adding elements, moving backward and forward with wrapping. Suitable for undo/redo implementations such as history management.",
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"prepare": "husky",
|
|
13
|
-
"prepare-release": "pnpm build && node ./scripts/prepare-release.js"
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"types": "index.d.ts",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/zavvdev/circular-history"
|
|
14
11
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"vite": "npm:rolldown-vite@7.2.5"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
12
|
+
"keywords": [
|
|
13
|
+
"javascript",
|
|
14
|
+
"data-structures",
|
|
15
|
+
"circular-buffer",
|
|
16
|
+
"circular-history",
|
|
17
|
+
"history-management"
|
|
18
|
+
],
|
|
19
|
+
"author": "Ihor ZAVIRIUKHA <ihor.zaviriukha@protonmail.com>",
|
|
20
|
+
"license": "MIT"
|
|
27
21
|
}
|
package/.husky/pre-commit
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pnpm prettier
|
package/.husky/pre-push
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pnpm prettier
|
package/.nvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
v22.20.0
|
package/.prettierignore
DELETED
package/.prettierrc.json
DELETED
package/LICENCE
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
MIT Licence
|
|
2
|
-
|
|
3
|
-
Copyright (c) Ihor ZAVIRIUKHA <ihor.zaviriukha@protonmail.com>
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/README.md
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# Circular History
|
|
2
|
-
|
|
3
|
-
Data structure that holds a fixed amount of items of a specific data type. It's designed more for managing history, such as undo-redo functionality, therefore it allows you to overwrite the items after moving backward in order to discard the "redo" history.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
### 1. Create an instance of `CircularHistory` with a specified capacity and data type.
|
|
8
|
-
|
|
9
|
-
```javascript
|
|
10
|
-
var history = new CircularHistory(5, "string");
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
### 2. Commit new items to the history.
|
|
14
|
-
|
|
15
|
-
```javascript
|
|
16
|
-
history.commit("First");
|
|
17
|
-
history.commit("Second");
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Keep in mind that after moving backward, committing a new item will overwrite the items ahead of the current index.
|
|
21
|
-
It was designed this way to facilitate undo-redo functionality by discarding the "redo" history when a new action is taken.
|
|
22
|
-
|
|
23
|
-
When the capacity is reached, the oldest items will be overwritten in a circular manner.
|
|
24
|
-
|
|
25
|
-
### 3. Get the current item.
|
|
26
|
-
|
|
27
|
-
```javascript
|
|
28
|
-
var currentItem = history.current();
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Returns either the current item or `CircularHistory.FLAGS.empty` if current index is `-1` which means there are no committed items yet or you went backwards beyond the first committed item (basically an empty state).
|
|
32
|
-
|
|
33
|
-
### 4. Move backward and forward in the history.
|
|
34
|
-
|
|
35
|
-
```javascript
|
|
36
|
-
history.moveBackward();
|
|
37
|
-
history.moveForward();
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Moving backward and forward will adjust the current index accordingly and allow you to navigate within specific range which is determined by the number of committed items before navigation. If the number of committed items exceeds the capacity, the range will be limited to the capacity.
|
|
41
|
-
|
|
42
|
-
### 5. Clear the history.
|
|
43
|
-
|
|
44
|
-
```javascript
|
|
45
|
-
history.clear();
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### 6. Get the history array.
|
|
49
|
-
|
|
50
|
-
```javascript
|
|
51
|
-
var historyArray = history.dump();
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
This will return an array of all committed items in the history. If capacity has not been reached, the array will contain items with `undefined` values for uncommitted slots.
|
|
55
|
-
|
|
56
|
-
If you want to get only the committed items, you can pass `true` as an argument.
|
|
57
|
-
|
|
58
|
-
```javascript
|
|
59
|
-
var committedItems = history.dump(true);
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### 7. Get the current index.
|
|
63
|
-
|
|
64
|
-
```javascript
|
|
65
|
-
var currentIndex = history.getCurrentIndex();
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### 8. Determine if start/end has been reached
|
|
69
|
-
|
|
70
|
-
```javascript
|
|
71
|
-
var isAtStart = history.isStartReached();
|
|
72
|
-
var isAtEnd = history.isEndReached();
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Running tests
|
|
76
|
-
|
|
77
|
-
1. `pnpm install`
|
|
78
|
-
|
|
79
|
-
2. `pnpm test`
|
|
80
|
-
|
|
81
|
-
## Usage example
|
|
82
|
-
|
|
83
|
-
Imagine that you have a drawing application. You have a layer where you can draw
|
|
84
|
-
shapes and you can create a snapshot of the layer's state to keep track of changes.
|
|
85
|
-
|
|
86
|
-
```javascript
|
|
87
|
-
var history = new CircularHistory(50, "string");
|
|
88
|
-
|
|
89
|
-
function commitHistorySnapshot() {
|
|
90
|
-
const snapshot = drawLayer.toJSON();
|
|
91
|
-
if (!snapshot) return;
|
|
92
|
-
history.commit(snapshot);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function restoreHistorySnapshot(snapshot) {
|
|
96
|
-
if (!snapshot) return;
|
|
97
|
-
|
|
98
|
-
if (snapshot === CircularHistory.FLAGS.empty) {
|
|
99
|
-
drawLayer.clear();
|
|
100
|
-
drawLayer.redraw();
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
drawLayer.unmount();
|
|
105
|
-
|
|
106
|
-
const restoredLayer = new Layer(snapshot);
|
|
107
|
-
drawLayer = restoredLayer;
|
|
108
|
-
|
|
109
|
-
restoredLayer.redraw();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function undo() {
|
|
113
|
-
history.moveBackward();
|
|
114
|
-
restoreHistorySnapshot(history.current());
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function redo() {
|
|
118
|
-
history.moveForward();
|
|
119
|
-
restoreHistorySnapshot(history.current());
|
|
120
|
-
}
|
|
121
|
-
```
|
package/dist/package.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "circular-history",
|
|
3
|
-
"type": "module",
|
|
4
|
-
"version": "1.0.2",
|
|
5
|
-
"description": "Data structure with a fixed number of items of a specific type. Supports adding elements, moving backward and forward with wrapping. Suitable for undo/redo implementations such as history management.",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"types": "index.d.ts",
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "https://github.com/zavvdev/circular-history"
|
|
11
|
-
},
|
|
12
|
-
"keywords": [
|
|
13
|
-
"javascript",
|
|
14
|
-
"data-structures",
|
|
15
|
-
"circular-buffer",
|
|
16
|
-
"circular-history",
|
|
17
|
-
"history-management"
|
|
18
|
-
],
|
|
19
|
-
"author": "Ihor ZAVIRIUKHA <ihor.zaviriukha@protonmail.com>",
|
|
20
|
-
"license": "MIT"
|
|
21
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
|
|
3
|
-
fs.copyFile("README.md", "./dist/README.md", (err) => {
|
|
4
|
-
if (err) throw err;
|
|
5
|
-
console.log("REAME.md was copied to dist folder");
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
fs.copyFile("./src/index.d.ts", "./dist/index.d.ts", (err) => {
|
|
9
|
-
if (err) throw err;
|
|
10
|
-
console.log("index.d.ts was copied to dist folder");
|
|
11
|
-
console.log("-----------------------------");
|
|
12
|
-
console.log("UPDATE THE VERSION IN package.json");
|
|
13
|
-
});
|
package/src/circular-history.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* #Config
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @description
|
|
7
|
-
* List of data types that are allowed to be stored in the items.
|
|
8
|
-
* All items should be of the same data type.
|
|
9
|
-
*/
|
|
10
|
-
var ALLOWED_DATA_TYPES = ["number", "string", "bigint", "boolean", "symbol", "object"];
|
|
11
|
-
|
|
12
|
-
var NAVIGATION_LOWER_BOUND = 0;
|
|
13
|
-
var EMPTY_POINTER = -1;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @description
|
|
17
|
-
* List of values that can be returned by the CircularHistory methods
|
|
18
|
-
*/
|
|
19
|
-
var FLAGS = {
|
|
20
|
-
empty: Symbol("empty"),
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* #Utils
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
var isTypeAllowed = (type) => ALLOWED_DATA_TYPES.includes(type);
|
|
28
|
-
|
|
29
|
-
var isValueTypeAllowed = (value) => {
|
|
30
|
-
var valueType = typeof value;
|
|
31
|
-
var isObject = valueType === "object" && value !== null;
|
|
32
|
-
return isObject || isTypeAllowed(valueType);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
var canCommit = (value, dataType) => isValueTypeAllowed(value) && typeof value === dataType;
|
|
36
|
-
var isItemEmpty = (slot) => slot === undefined;
|
|
37
|
-
var makeIndex = (pointer, capacity) => pointer % capacity;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* #State
|
|
41
|
-
*
|
|
42
|
-
* @description
|
|
43
|
-
* Using WeakMap to store private state in order to not expose private properties.
|
|
44
|
-
* This will ensure that the reference to the state will be dropped when instances
|
|
45
|
-
* are garbage collected.
|
|
46
|
-
*/
|
|
47
|
-
var STATE = new WeakMap();
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @description
|
|
51
|
-
*
|
|
52
|
-
* Circular History
|
|
53
|
-
*
|
|
54
|
-
* Data structure that holds a fixed amount of items of a specific data type.
|
|
55
|
-
* Supports committing new items, and moving backward and forward through the committed items.
|
|
56
|
-
* When the buffer is full the wrapping occurs and the oldest items are overwritten.
|
|
57
|
-
* After moving backward, new commits will overwrite the items ahead of the current pointer which
|
|
58
|
-
* makes this data structure suitable for undo-redo implementations like history management.
|
|
59
|
-
*
|
|
60
|
-
* @param {number} capacity - Maximum amount of items in the buffer
|
|
61
|
-
* @param {string} dataType - Data type of each slot
|
|
62
|
-
*/
|
|
63
|
-
function CircularHistory(capacity, dataType) {
|
|
64
|
-
if (typeof capacity !== "number" || capacity <= 0 || !Number.isInteger(capacity)) {
|
|
65
|
-
throw new Error(`Capacity must be a positive integer. Got "${capacity}".`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (!isTypeAllowed(dataType)) {
|
|
69
|
-
throw new Error(`"${dataType}" is not allowed`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
STATE.set(this, {
|
|
73
|
-
/**
|
|
74
|
-
* Represents range [0, navigationUpperBound] of how many items can be used
|
|
75
|
-
* for navigation.
|
|
76
|
-
*/
|
|
77
|
-
navigationUpperBound: NAVIGATION_LOWER_BOUND,
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Represents current index within the navigation range [0, navigationUpperBound].
|
|
81
|
-
*/
|
|
82
|
-
navigationIndex: NAVIGATION_LOWER_BOUND,
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Current pointer. It does not point to an index in the buffer directly.
|
|
86
|
-
* It is used to calculate the index in the buffer using modulo operation.
|
|
87
|
-
*/
|
|
88
|
-
pointer: EMPTY_POINTER,
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Total maximum amount of items.
|
|
92
|
-
*/
|
|
93
|
-
capacity: capacity,
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Data type of each slot.
|
|
97
|
-
*/
|
|
98
|
-
dataType: dataType,
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Array of items.
|
|
102
|
-
* Should always be pre-allocated with capacity.
|
|
103
|
-
*/
|
|
104
|
-
buffer: new Array(capacity),
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
CircularHistory.prototype.commit = function (value) {
|
|
109
|
-
var self = STATE.get(this);
|
|
110
|
-
var dataType = self.dataType;
|
|
111
|
-
|
|
112
|
-
if (!canCommit(value, dataType)) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
`Type of ${value} is invalid. Expected "${dataType}", got "${value === null ? "null" : typeof value}".`,
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
var capacity = self.capacity;
|
|
119
|
-
|
|
120
|
-
if (self.pointer === self.capacity - 1) {
|
|
121
|
-
self.navigationUpperBound = capacity - 1;
|
|
122
|
-
self.navigationIndex = capacity - 1;
|
|
123
|
-
} else {
|
|
124
|
-
self.navigationIndex = ++self.navigationUpperBound;
|
|
125
|
-
}
|
|
126
|
-
self.buffer[makeIndex(++self.pointer, capacity)] = value;
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
CircularHistory.prototype.current = function () {
|
|
130
|
-
var self = STATE.get(this);
|
|
131
|
-
var buffer = self.buffer;
|
|
132
|
-
|
|
133
|
-
var pointer = self.pointer;
|
|
134
|
-
if (pointer === EMPTY_POINTER) return FLAGS.empty;
|
|
135
|
-
var nextItem = buffer[makeIndex(pointer, self.capacity)];
|
|
136
|
-
|
|
137
|
-
return isItemEmpty(nextItem) ? FLAGS.empty : nextItem;
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
CircularHistory.prototype.moveBackward = function () {
|
|
141
|
-
var self = STATE.get(this);
|
|
142
|
-
if (self.navigationIndex === NAVIGATION_LOWER_BOUND || self.pointer === EMPTY_POINTER) return;
|
|
143
|
-
self.pointer--;
|
|
144
|
-
self.navigationIndex--;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
CircularHistory.prototype.moveForward = function () {
|
|
148
|
-
var self = STATE.get(this);
|
|
149
|
-
if (self.navigationIndex === self.navigationUpperBound) return;
|
|
150
|
-
self.pointer++;
|
|
151
|
-
self.navigationIndex++;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
CircularHistory.prototype.clear = function () {
|
|
155
|
-
var self = STATE.get(this);
|
|
156
|
-
self.navigationIndex = NAVIGATION_LOWER_BOUND;
|
|
157
|
-
self.navigationUpperBound = NAVIGATION_LOWER_BOUND;
|
|
158
|
-
self.pointer = EMPTY_POINTER;
|
|
159
|
-
self.buffer = new Array(self.capacity);
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
CircularHistory.prototype.dump = function (discardHoles = false) {
|
|
163
|
-
var self = STATE.get(this);
|
|
164
|
-
var result = [...self.buffer];
|
|
165
|
-
return discardHoles ? result.filter((slot) => !isItemEmpty(slot)) : result;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
CircularHistory.prototype.getCurrentIndex = function () {
|
|
169
|
-
var self = STATE.get(this);
|
|
170
|
-
return makeIndex(self.pointer, self.capacity);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
CircularHistory.prototype.isStartReached = function () {
|
|
174
|
-
var self = STATE.get(this);
|
|
175
|
-
return self.navigationIndex === NAVIGATION_LOWER_BOUND;
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
CircularHistory.prototype.isEndReached = function () {
|
|
179
|
-
var self = STATE.get(this);
|
|
180
|
-
return self.navigationIndex === self.navigationUpperBound;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
CircularHistory.FLAGS = FLAGS;
|
|
184
|
-
|
|
185
|
-
export { CircularHistory };
|
|
@@ -1,432 +0,0 @@
|
|
|
1
|
-
import { test, describe, expect } from "vitest";
|
|
2
|
-
import { CircularHistory } from "./circular-history.js";
|
|
3
|
-
|
|
4
|
-
describe("CircularHistory", () => {
|
|
5
|
-
test("should throw if capacity is not a positive integer", () => {
|
|
6
|
-
expect(() => new CircularHistory(0, "number")).toThrow();
|
|
7
|
-
expect(() => new CircularHistory(-5, "number")).toThrow();
|
|
8
|
-
expect(() => new CircularHistory(3.5, "number")).toThrow();
|
|
9
|
-
expect(() => new CircularHistory("10", "number")).toThrow();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test("should throw if data type is not allowed", () => {
|
|
13
|
-
expect(() => new CircularHistory(5, "invalidType")).toThrow();
|
|
14
|
-
expect(() => new CircularHistory(5, 123)).toThrow();
|
|
15
|
-
expect(() => new CircularHistory(5, null)).toThrow();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test("should successfully create a CircularHistory with valid parameters", () => {
|
|
19
|
-
var historyNumber = new CircularHistory(5, "number");
|
|
20
|
-
expect(historyNumber).toBeInstanceOf(CircularHistory);
|
|
21
|
-
|
|
22
|
-
var historyString = new CircularHistory(10, "string");
|
|
23
|
-
expect(historyString).toBeInstanceOf(CircularHistory);
|
|
24
|
-
|
|
25
|
-
var historyBigInt = new CircularHistory(3, "bigint");
|
|
26
|
-
expect(historyBigInt).toBeInstanceOf(CircularHistory);
|
|
27
|
-
|
|
28
|
-
var historyBoolean = new CircularHistory(7, "boolean");
|
|
29
|
-
expect(historyBoolean).toBeInstanceOf(CircularHistory);
|
|
30
|
-
|
|
31
|
-
var historySymbol = new CircularHistory(2, "symbol");
|
|
32
|
-
expect(historySymbol).toBeInstanceOf(CircularHistory);
|
|
33
|
-
|
|
34
|
-
var historyObject = new CircularHistory(4, "object");
|
|
35
|
-
expect(historyObject).toBeInstanceOf(CircularHistory);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("should not commit invalid data types", () => {
|
|
39
|
-
var historyNumber = new CircularHistory(5, "number");
|
|
40
|
-
expect(() => historyNumber.commit("string")).toThrow();
|
|
41
|
-
expect(() => historyNumber.commit(true)).toThrow();
|
|
42
|
-
expect(() => historyNumber.commit({})).toThrow();
|
|
43
|
-
expect(() => historyNumber.commit([])).toThrow();
|
|
44
|
-
expect(() => historyNumber.commit(Symbol("sym"))).toThrow();
|
|
45
|
-
expect(() => historyNumber.commit(10n)).toThrow();
|
|
46
|
-
|
|
47
|
-
var historyString = new CircularHistory(5, "string");
|
|
48
|
-
expect(() => historyString.commit(100)).toThrow();
|
|
49
|
-
expect(() => historyString.commit(false)).toThrow();
|
|
50
|
-
expect(() => historyString.commit({})).toThrow();
|
|
51
|
-
expect(() => historyString.commit([])).toThrow();
|
|
52
|
-
expect(() => historyString.commit(Symbol("sym"))).toThrow();
|
|
53
|
-
expect(() => historyString.commit(10n)).toThrow();
|
|
54
|
-
|
|
55
|
-
var historyBigInt = new CircularHistory(5, "bigint");
|
|
56
|
-
expect(() => historyBigInt.commit(100)).toThrow();
|
|
57
|
-
expect(() => historyBigInt.commit("string")).toThrow();
|
|
58
|
-
expect(() => historyBigInt.commit(true)).toThrow();
|
|
59
|
-
expect(() => historyBigInt.commit({})).toThrow();
|
|
60
|
-
expect(() => historyBigInt.commit(Symbol("sym"))).toThrow();
|
|
61
|
-
expect(() => historyBigInt.commit(10.5)).toThrow();
|
|
62
|
-
|
|
63
|
-
var historyBoolean = new CircularHistory(5, "boolean");
|
|
64
|
-
expect(() => historyBoolean.commit(100)).toThrow();
|
|
65
|
-
expect(() => historyBoolean.commit("string")).toThrow();
|
|
66
|
-
expect(() => historyBoolean.commit({})).toThrow();
|
|
67
|
-
expect(() => historyBoolean.commit([])).toThrow();
|
|
68
|
-
expect(() => historyBoolean.commit(Symbol("sym"))).toThrow();
|
|
69
|
-
expect(() => historyBoolean.commit(10n)).toThrow();
|
|
70
|
-
|
|
71
|
-
var historySymbol = new CircularHistory(5, "symbol");
|
|
72
|
-
expect(() => historySymbol.commit(100)).toThrow();
|
|
73
|
-
expect(() => historySymbol.commit("string")).toThrow();
|
|
74
|
-
expect(() => historySymbol.commit(true)).toThrow();
|
|
75
|
-
expect(() => historySymbol.commit({})).toThrow();
|
|
76
|
-
expect(() => historySymbol.commit(10n)).toThrow();
|
|
77
|
-
|
|
78
|
-
var historyObject = new CircularHistory(5, "object");
|
|
79
|
-
expect(() => historyObject.commit(100)).toThrow();
|
|
80
|
-
expect(() => historyObject.commit("string")).toThrow();
|
|
81
|
-
expect(() => historyObject.commit(true)).toThrow();
|
|
82
|
-
expect(() => historyObject.commit(Symbol("sym"))).toThrow();
|
|
83
|
-
expect(() => historyObject.commit(10n)).toThrow();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("should get an empty flag if history is empty", () => {
|
|
87
|
-
var history = new CircularHistory(5, "number");
|
|
88
|
-
expect(history.current()).toEqual(CircularHistory.FLAGS.empty);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("should wrap around when capacity is exceeded", () => {
|
|
92
|
-
var history = new CircularHistory(3, "number");
|
|
93
|
-
|
|
94
|
-
history.commit(1);
|
|
95
|
-
history.commit(2);
|
|
96
|
-
history.commit(3);
|
|
97
|
-
|
|
98
|
-
expect(history.current()).toBe(3);
|
|
99
|
-
history.commit(4);
|
|
100
|
-
|
|
101
|
-
expect(history.current()).toBe(4);
|
|
102
|
-
expect(history.dump(true)).toEqual([4, 2, 3]);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("should move backward and forward (no wrapping)", () => {
|
|
106
|
-
var history = new CircularHistory(3, "number");
|
|
107
|
-
|
|
108
|
-
history.commit(1);
|
|
109
|
-
history.commit(2);
|
|
110
|
-
history.commit(3);
|
|
111
|
-
expect(history.current()).toBe(3);
|
|
112
|
-
|
|
113
|
-
history.moveBackward();
|
|
114
|
-
expect(history.current()).toBe(2);
|
|
115
|
-
|
|
116
|
-
history.moveBackward();
|
|
117
|
-
expect(history.current()).toBe(1);
|
|
118
|
-
|
|
119
|
-
history.moveBackward();
|
|
120
|
-
expect(history.current()).toBe(CircularHistory.FLAGS.empty);
|
|
121
|
-
|
|
122
|
-
history.moveBackward();
|
|
123
|
-
expect(history.current()).toBe(CircularHistory.FLAGS.empty);
|
|
124
|
-
expect(history.getCurrentIndex()).toBe(-1);
|
|
125
|
-
|
|
126
|
-
history.moveForward();
|
|
127
|
-
expect(history.current()).toBe(1);
|
|
128
|
-
|
|
129
|
-
history.moveForward();
|
|
130
|
-
expect(history.current()).toBe(2);
|
|
131
|
-
|
|
132
|
-
history.moveForward();
|
|
133
|
-
expect(history.current()).toBe(3);
|
|
134
|
-
|
|
135
|
-
history.moveForward();
|
|
136
|
-
expect(history.current()).toBe(3);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("should move backward and forward (with wrapping)", () => {
|
|
140
|
-
var history = new CircularHistory(3, "number");
|
|
141
|
-
history.commit(1);
|
|
142
|
-
history.commit(2);
|
|
143
|
-
history.commit(3);
|
|
144
|
-
expect(history.getCurrentIndex()).toBe(2);
|
|
145
|
-
history.commit(4);
|
|
146
|
-
|
|
147
|
-
expect(history.current()).toBe(4);
|
|
148
|
-
expect(history.getCurrentIndex()).toBe(0);
|
|
149
|
-
|
|
150
|
-
history.moveBackward();
|
|
151
|
-
expect(history.current()).toBe(3);
|
|
152
|
-
|
|
153
|
-
history.moveBackward();
|
|
154
|
-
expect(history.current()).toBe(2);
|
|
155
|
-
|
|
156
|
-
history.moveBackward();
|
|
157
|
-
expect(history.current()).toBe(2);
|
|
158
|
-
|
|
159
|
-
history.moveForward();
|
|
160
|
-
expect(history.current()).toBe(3);
|
|
161
|
-
|
|
162
|
-
history.moveForward();
|
|
163
|
-
expect(history.current()).toBe(4);
|
|
164
|
-
|
|
165
|
-
history.moveForward();
|
|
166
|
-
expect(history.current()).toBe(4);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test("should override items ahead after moving backward", () => {
|
|
170
|
-
var history = new CircularHistory(2, "number");
|
|
171
|
-
|
|
172
|
-
history.commit(1);
|
|
173
|
-
expect(history.current()).toBe(1);
|
|
174
|
-
|
|
175
|
-
history.commit(2);
|
|
176
|
-
expect(history.current()).toBe(2);
|
|
177
|
-
|
|
178
|
-
history.moveBackward();
|
|
179
|
-
expect(history.current()).toBe(1);
|
|
180
|
-
|
|
181
|
-
history.commit(3);
|
|
182
|
-
expect(history.current()).toBe(3);
|
|
183
|
-
|
|
184
|
-
history.moveBackward();
|
|
185
|
-
expect(history.current()).toBe(1);
|
|
186
|
-
|
|
187
|
-
history.moveForward();
|
|
188
|
-
expect(history.current()).toBe(3);
|
|
189
|
-
|
|
190
|
-
history.moveForward();
|
|
191
|
-
expect(history.current()).toBe(3);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test("should clear the history correctly", () => {
|
|
195
|
-
var history = new CircularHistory(3, "number");
|
|
196
|
-
|
|
197
|
-
history.commit(1);
|
|
198
|
-
history.commit(2);
|
|
199
|
-
history.commit(3);
|
|
200
|
-
|
|
201
|
-
expect(history.current()).toBe(3);
|
|
202
|
-
|
|
203
|
-
history.clear();
|
|
204
|
-
expect(history.current()).toEqual(CircularHistory.FLAGS.empty);
|
|
205
|
-
expect(history.getCurrentIndex()).toBe(-1);
|
|
206
|
-
|
|
207
|
-
history.commit(4);
|
|
208
|
-
|
|
209
|
-
expect(history.current()).toBe(4);
|
|
210
|
-
expect(history.getCurrentIndex()).toBe(0);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test("should return back the current history state with empty values", () => {
|
|
214
|
-
var history = new CircularHistory(3, "number");
|
|
215
|
-
history.commit(1);
|
|
216
|
-
history.commit(2);
|
|
217
|
-
expect(history.dump()).toEqual([1, 2, undefined]);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
test("should return back the current history state without empty values", () => {
|
|
221
|
-
var history = new CircularHistory(3, "number");
|
|
222
|
-
history.commit(1);
|
|
223
|
-
history.commit(2);
|
|
224
|
-
expect(history.dump(true)).toEqual([1, 2]);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test("should return back the correct current pointer position", () => {
|
|
228
|
-
var history = new CircularHistory(3, "number");
|
|
229
|
-
expect(history.getCurrentIndex()).toBe(-1);
|
|
230
|
-
|
|
231
|
-
history.commit(1);
|
|
232
|
-
expect(history.getCurrentIndex()).toBe(0);
|
|
233
|
-
|
|
234
|
-
history.commit(2);
|
|
235
|
-
expect(history.getCurrentIndex()).toBe(1);
|
|
236
|
-
|
|
237
|
-
history.commit(3);
|
|
238
|
-
expect(history.getCurrentIndex()).toBe(2);
|
|
239
|
-
|
|
240
|
-
history.commit(4);
|
|
241
|
-
expect(history.getCurrentIndex()).toBe(0);
|
|
242
|
-
|
|
243
|
-
history.commit(5);
|
|
244
|
-
expect(history.getCurrentIndex()).toBe(1);
|
|
245
|
-
|
|
246
|
-
history.moveBackward();
|
|
247
|
-
expect(history.getCurrentIndex()).toBe(0);
|
|
248
|
-
|
|
249
|
-
history.moveBackward();
|
|
250
|
-
expect(history.getCurrentIndex()).toBe(2);
|
|
251
|
-
|
|
252
|
-
history.moveForward();
|
|
253
|
-
expect(history.getCurrentIndex()).toBe(0);
|
|
254
|
-
|
|
255
|
-
history.moveForward();
|
|
256
|
-
expect(history.getCurrentIndex()).toBe(1);
|
|
257
|
-
|
|
258
|
-
history.moveForward();
|
|
259
|
-
expect(history.getCurrentIndex()).toBe(1);
|
|
260
|
-
|
|
261
|
-
history.moveForward();
|
|
262
|
-
expect(history.getCurrentIndex()).toBe(1);
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
test("should correctly identify when start is reached", () => {
|
|
266
|
-
var history = new CircularHistory(3, "number");
|
|
267
|
-
expect(history.isStartReached()).toBe(true);
|
|
268
|
-
|
|
269
|
-
history.commit(1);
|
|
270
|
-
expect(history.isStartReached()).toBe(false);
|
|
271
|
-
|
|
272
|
-
history.commit(2);
|
|
273
|
-
expect(history.isStartReached()).toBe(false);
|
|
274
|
-
|
|
275
|
-
history.moveBackward();
|
|
276
|
-
expect(history.isStartReached()).toBe(false);
|
|
277
|
-
|
|
278
|
-
history.moveBackward();
|
|
279
|
-
expect(history.isStartReached()).toBe(true);
|
|
280
|
-
|
|
281
|
-
history.moveBackward();
|
|
282
|
-
expect(history.isStartReached()).toBe(true);
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
test("should correctly identify when end is reached", () => {
|
|
286
|
-
var history = new CircularHistory(3, "number");
|
|
287
|
-
expect(history.isEndReached()).toBe(true);
|
|
288
|
-
|
|
289
|
-
history.commit(1);
|
|
290
|
-
history.commit(2);
|
|
291
|
-
expect(history.current()).toBe(2);
|
|
292
|
-
|
|
293
|
-
history.moveForward();
|
|
294
|
-
expect(history.isEndReached()).toBe(true);
|
|
295
|
-
|
|
296
|
-
history.moveBackward();
|
|
297
|
-
expect(history.current()).toBe(1);
|
|
298
|
-
expect(history.isEndReached()).toBe(false);
|
|
299
|
-
|
|
300
|
-
history.moveBackward();
|
|
301
|
-
expect(history.current()).toBe(CircularHistory.FLAGS.empty);
|
|
302
|
-
expect(history.isEndReached()).toBe(false);
|
|
303
|
-
|
|
304
|
-
history.moveForward();
|
|
305
|
-
expect(history.current()).toBe(1);
|
|
306
|
-
expect(history.isEndReached()).toBe(false);
|
|
307
|
-
|
|
308
|
-
history.moveForward();
|
|
309
|
-
expect(history.current()).toBe(2);
|
|
310
|
-
expect(history.isEndReached()).toBe(true);
|
|
311
|
-
|
|
312
|
-
history.moveForward();
|
|
313
|
-
expect(history.current()).toBe(2);
|
|
314
|
-
expect(history.isEndReached()).toBe(true);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
test("should return the last available item when capacity is not reached, the next item is empty and moving forward", () => {
|
|
318
|
-
var history = new CircularHistory(3, "number");
|
|
319
|
-
|
|
320
|
-
history.commit(1);
|
|
321
|
-
history.commit(2);
|
|
322
|
-
expect(history.current()).toBe(2);
|
|
323
|
-
|
|
324
|
-
history.moveForward();
|
|
325
|
-
expect(history.current()).toBe(2);
|
|
326
|
-
|
|
327
|
-
history.moveForward();
|
|
328
|
-
expect(history.current()).toBe(2);
|
|
329
|
-
|
|
330
|
-
history.moveBackward();
|
|
331
|
-
expect(history.current()).toBe(1);
|
|
332
|
-
|
|
333
|
-
history.moveBackward();
|
|
334
|
-
expect(history.current()).toBe(CircularHistory.FLAGS.empty);
|
|
335
|
-
|
|
336
|
-
history.moveBackward();
|
|
337
|
-
expect(history.current()).toBe(CircularHistory.FLAGS.empty);
|
|
338
|
-
|
|
339
|
-
history.moveForward();
|
|
340
|
-
expect(history.current()).toBe(1);
|
|
341
|
-
|
|
342
|
-
history.moveForward();
|
|
343
|
-
expect(history.current()).toBe(2);
|
|
344
|
-
|
|
345
|
-
history.moveForward();
|
|
346
|
-
expect(history.current()).toBe(2);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
test("should behave correctly after multiple backward moves against empty history", () => {
|
|
350
|
-
var history = new CircularHistory(3, "number");
|
|
351
|
-
|
|
352
|
-
history.moveBackward();
|
|
353
|
-
history.moveBackward();
|
|
354
|
-
history.moveBackward();
|
|
355
|
-
|
|
356
|
-
history.commit(4);
|
|
357
|
-
expect(history.dump(true)).toEqual([4]);
|
|
358
|
-
expect(history.getCurrentIndex()).toBe(0);
|
|
359
|
-
expect(history.current()).toBe(4);
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
test("should behave correctly after multiple forward moves against empty history", () => {
|
|
363
|
-
var history = new CircularHistory(3, "number");
|
|
364
|
-
|
|
365
|
-
history.moveForward();
|
|
366
|
-
history.moveForward();
|
|
367
|
-
history.moveForward();
|
|
368
|
-
|
|
369
|
-
history.commit(3);
|
|
370
|
-
expect(history.dump(true)).toEqual([3]);
|
|
371
|
-
expect(history.getCurrentIndex()).toBe(0);
|
|
372
|
-
|
|
373
|
-
history.commit(5);
|
|
374
|
-
expect(history.dump(true)).toEqual([3, 5]);
|
|
375
|
-
|
|
376
|
-
history.moveForward();
|
|
377
|
-
history.moveForward();
|
|
378
|
-
|
|
379
|
-
expect(history.current()).toBe(5);
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
test("should not expose the next item after discarding previous after moving backwards", () => {
|
|
383
|
-
var history = new CircularHistory(10, "number");
|
|
384
|
-
|
|
385
|
-
history.commit(1);
|
|
386
|
-
history.commit(2);
|
|
387
|
-
history.commit(3);
|
|
388
|
-
|
|
389
|
-
history.moveBackward();
|
|
390
|
-
history.moveBackward();
|
|
391
|
-
expect(history.current()).toBe(1);
|
|
392
|
-
|
|
393
|
-
history.commit(4);
|
|
394
|
-
expect(history.dump(true)).toEqual([1, 4, 3]);
|
|
395
|
-
|
|
396
|
-
history.moveForward();
|
|
397
|
-
expect(history.getCurrentIndex()).toBe(1);
|
|
398
|
-
expect(history.isEndReached()).toBe(true);
|
|
399
|
-
expect(history.current()).toBe(4);
|
|
400
|
-
|
|
401
|
-
history.moveForward();
|
|
402
|
-
expect(history.current()).toBe(4);
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
test("should behave correctly after multiple moveBackward calls when some item was overriden", () => {
|
|
406
|
-
var history = new CircularHistory(3, "number");
|
|
407
|
-
history.commit(1);
|
|
408
|
-
history.commit(2);
|
|
409
|
-
history.commit(3);
|
|
410
|
-
|
|
411
|
-
history.moveBackward();
|
|
412
|
-
history.moveBackward();
|
|
413
|
-
|
|
414
|
-
history.commit(4);
|
|
415
|
-
|
|
416
|
-
history.moveBackward();
|
|
417
|
-
history.moveBackward();
|
|
418
|
-
history.moveBackward();
|
|
419
|
-
history.moveBackward();
|
|
420
|
-
|
|
421
|
-
expect(history.getCurrentIndex()).toBe(-1);
|
|
422
|
-
|
|
423
|
-
history.moveForward();
|
|
424
|
-
expect(history.current()).toBe(1);
|
|
425
|
-
|
|
426
|
-
history.moveForward();
|
|
427
|
-
expect(history.current()).toBe(4);
|
|
428
|
-
|
|
429
|
-
history.moveForward();
|
|
430
|
-
expect(history.current()).toBe(4);
|
|
431
|
-
});
|
|
432
|
-
});
|
package/src/index.d.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
export declare const FLAGS: {
|
|
2
|
-
readonly empty: unique symbol;
|
|
3
|
-
};
|
|
4
|
-
|
|
5
|
-
export declare type CircularHistoryType = number | string | bigint | boolean | symbol | object;
|
|
6
|
-
|
|
7
|
-
type DataType = "number" | "string" | "bigint" | "boolean" | "symbol" | "object";
|
|
8
|
-
|
|
9
|
-
export declare class CircularHistory<T extends CircularHistoryType = CircularHistoryType> {
|
|
10
|
-
constructor(capacity: number, dataType: DataType);
|
|
11
|
-
|
|
12
|
-
commit(value: T): void;
|
|
13
|
-
|
|
14
|
-
current(): T | typeof FLAGS.empty;
|
|
15
|
-
|
|
16
|
-
moveBackward(): void;
|
|
17
|
-
|
|
18
|
-
moveForward(): void;
|
|
19
|
-
|
|
20
|
-
clear(): void;
|
|
21
|
-
|
|
22
|
-
dump(discardHoles?: boolean): T[];
|
|
23
|
-
|
|
24
|
-
getCurrentIndex(): number;
|
|
25
|
-
|
|
26
|
-
isStartReached(): boolean;
|
|
27
|
-
|
|
28
|
-
isEndReached(): boolean;
|
|
29
|
-
|
|
30
|
-
static readonly FLAGS: typeof FLAGS;
|
|
31
|
-
}
|
|
File without changes
|
|
File without changes
|