@bepalo/router 1.11.32 → 1.12.34
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/dist/cjs/framework.d.ts +2 -4
- package/dist/cjs/framework.d.ts.map +1 -1
- package/dist/cjs/framework.js +4 -6
- package/dist/cjs/framework.js.map +1 -1
- package/dist/cjs/helpers.d.ts +2 -2
- package/dist/cjs/helpers.d.ts.map +1 -1
- package/dist/cjs/helpers.js +1 -1
- package/dist/cjs/helpers.js.map +1 -1
- package/dist/cjs/index.d.ts +5 -5
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -5
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/middlewares.d.ts +2 -2
- package/dist/cjs/middlewares.d.ts.map +1 -1
- package/dist/cjs/middlewares.js +24 -24
- package/dist/cjs/middlewares.js.map +1 -1
- package/dist/cjs/router.d.ts +2 -2
- package/dist/cjs/router.d.ts.map +1 -1
- package/dist/cjs/router.js +8 -8
- package/dist/cjs/router.js.map +1 -1
- package/dist/cjs/types.d.ts +1 -1
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/upload-stream.d.ts +1 -1
- package/dist/cjs/upload-stream.d.ts.map +1 -1
- package/dist/cjs/upload-stream.js +7 -7
- package/dist/cjs/upload-stream.js.map +1 -1
- package/dist/framework.d.ts +2 -4
- package/dist/framework.d.ts.map +1 -1
- package/dist/framework.js +4 -6
- package/dist/framework.js.map +1 -1
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +1 -1
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/middlewares.d.ts +2 -2
- package/dist/middlewares.d.ts.map +1 -1
- package/dist/middlewares.js +24 -24
- package/dist/middlewares.js.map +1 -1
- package/dist/router.d.ts +2 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +8 -8
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/upload-stream.d.ts +1 -1
- package/dist/upload-stream.d.ts.map +1 -1
- package/dist/upload-stream.js +7 -7
- package/dist/upload-stream.js.map +1 -1
- package/package.json +8 -1
- package/src/framework.deno.ts +194 -0
- package/src/framework.ts +197 -0
- package/src/helpers.ts +829 -0
- package/src/index.ts +5 -0
- package/src/list.ts +462 -0
- package/src/middlewares.deno.ts +855 -0
- package/src/middlewares.ts +851 -0
- package/src/router.ts +993 -0
- package/src/tree.ts +139 -0
- package/src/types.ts +197 -0
- package/src/upload-stream.ts +661 -0
- package/dist/cjs/framework.deno.d.ts +0 -31
- package/dist/cjs/framework.deno.d.ts.map +0 -1
- package/dist/cjs/framework.deno.js +0 -245
- package/dist/cjs/framework.deno.js.map +0 -1
- package/dist/framework.deno.d.ts +0 -31
- package/dist/framework.deno.d.ts.map +0 -1
- package/dist/framework.deno.js +0 -245
- package/dist/framework.deno.js.map +0 -1
package/src/index.ts
ADDED
package/src/list.ts
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file A minimal and efficient implementation of a doubly-linked list with node-level access.
|
|
3
|
+
* @author Natnael Eshetu
|
|
4
|
+
* @exports List
|
|
5
|
+
* @exports ListNode
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A node in a doubly linked list.
|
|
10
|
+
* @template T
|
|
11
|
+
*/
|
|
12
|
+
export class ListNode<T> {
|
|
13
|
+
/**
|
|
14
|
+
* Creates a new ListNode.
|
|
15
|
+
* @param {T} value - Value held by this node.
|
|
16
|
+
* @param {ListNode<T>} [prev] - Previous node in the list.
|
|
17
|
+
* @param {ListNode<T>} [next] - Next node in the list.
|
|
18
|
+
*/
|
|
19
|
+
constructor(
|
|
20
|
+
public value: T,
|
|
21
|
+
public prev?: ListNode<T>,
|
|
22
|
+
public next?: ListNode<T>,
|
|
23
|
+
) {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class List<T> {
|
|
27
|
+
// Private fields
|
|
28
|
+
#first?: ListNode<T> = undefined;
|
|
29
|
+
#last?: ListNode<T> = undefined;
|
|
30
|
+
#size: number = 0;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new List instance. If an iterable is provided, pushes all values into the list.
|
|
34
|
+
* @param {Iterable<T>} [iterable] - Optional iterable of values to initialize the list.
|
|
35
|
+
*/
|
|
36
|
+
constructor(iterable?: Iterable<T>) {
|
|
37
|
+
if (iterable) {
|
|
38
|
+
for (const value of iterable) {
|
|
39
|
+
this.push(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Iterates over the values in the list from first to last.
|
|
46
|
+
* @returns {IterableIterator<T>}
|
|
47
|
+
*/
|
|
48
|
+
*[Symbol.iterator](): IterableIterator<T> {
|
|
49
|
+
for (let it = this.#first; it != null; it = it.next) {
|
|
50
|
+
yield it.value;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Alias for [Symbol.iterator].
|
|
56
|
+
* @returns {IterableIterator<T>}
|
|
57
|
+
*/
|
|
58
|
+
*entries(): IterableIterator<T> {
|
|
59
|
+
for (let it = this.#first; it != null; it = it.next) {
|
|
60
|
+
yield it.value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Iterates over the list nodes (including value, prev, and next).
|
|
66
|
+
* @returns {IterableIterator<ListNode<T>>}
|
|
67
|
+
*/
|
|
68
|
+
*iterator(): IterableIterator<ListNode<T>> {
|
|
69
|
+
for (let it = this.#first; it != null; it = it.next) {
|
|
70
|
+
yield it;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Iterates over the list nodes in reverse (last to first).
|
|
76
|
+
* @returns {IterableIterator<ListNode<T>>}
|
|
77
|
+
*/
|
|
78
|
+
*reverseIterator(): IterableIterator<ListNode<T>> {
|
|
79
|
+
for (let it = this.#last; it != null; it = it.prev) {
|
|
80
|
+
yield it;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Converts the list to an array of values.
|
|
86
|
+
* @returns {T[]}
|
|
87
|
+
*/
|
|
88
|
+
toArray(): T[] {
|
|
89
|
+
const values: T[] = new Array(this.#size);
|
|
90
|
+
for (let it = this.#first, i = 0; it != null; it = it.next, i++) {
|
|
91
|
+
values[i] = it.value;
|
|
92
|
+
}
|
|
93
|
+
return values;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Serializes the list to a JSON-compatible array.
|
|
98
|
+
* @returns {T[]}
|
|
99
|
+
*/
|
|
100
|
+
toJSON(): T[] {
|
|
101
|
+
return this.toArray();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Returns a stringified JSON representation of the list.
|
|
106
|
+
* @returns {string}
|
|
107
|
+
*/
|
|
108
|
+
toString(): string {
|
|
109
|
+
return JSON.stringify(this.toArray());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** @returns {T[]} The list in array form */
|
|
113
|
+
[Symbol.for("nodejs.util.inspect.custom")](): T[] {
|
|
114
|
+
return this.toArray();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** @returns {T[]} The list in array form */
|
|
118
|
+
[Symbol.toPrimitive](_hint?: string): T[] {
|
|
119
|
+
return this.toArray();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** @returns {number} Current size of the list */
|
|
123
|
+
get size(): number {
|
|
124
|
+
return this.#size;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** @returns {ListNode<T>|undefined} First node in the list */
|
|
128
|
+
get first(): ListNode<T> | undefined {
|
|
129
|
+
return this.#first;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** @returns {ListNode<T>|undefined} Last node in the list */
|
|
133
|
+
get last(): ListNode<T> | undefined {
|
|
134
|
+
return this.#last;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Removes and returns the first value in the list.
|
|
139
|
+
* @returns {T | undefined}
|
|
140
|
+
*/
|
|
141
|
+
popFirst(): T | undefined {
|
|
142
|
+
const value = this.#first?.value;
|
|
143
|
+
if (this.#first == null || this.#last == null) { // empty
|
|
144
|
+
return undefined;
|
|
145
|
+
} else if (this.#first == this.#last) { // one element
|
|
146
|
+
this.#first = undefined;
|
|
147
|
+
this.#last = undefined;
|
|
148
|
+
this.#size--;
|
|
149
|
+
} else { // many elements
|
|
150
|
+
this.#first = this.#first.next;
|
|
151
|
+
if (this.#first != null) {
|
|
152
|
+
this.#first.prev = undefined;
|
|
153
|
+
}
|
|
154
|
+
this.#size--;
|
|
155
|
+
}
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Removes and returns the last value in the list.
|
|
161
|
+
* @returns {T | undefined}
|
|
162
|
+
*/
|
|
163
|
+
pop(): T | undefined {
|
|
164
|
+
const value = this.#last?.value;
|
|
165
|
+
if (this.#first == null || this.#last == null) { // empty
|
|
166
|
+
return undefined;
|
|
167
|
+
} else if (this.#first == this.#last) { // one element
|
|
168
|
+
this.#first = undefined;
|
|
169
|
+
this.#last = undefined;
|
|
170
|
+
this.#size--;
|
|
171
|
+
} else { // many elements
|
|
172
|
+
this.#last = this.#last.prev;
|
|
173
|
+
if (this.#last != null) {
|
|
174
|
+
this.#last.next = undefined;
|
|
175
|
+
}
|
|
176
|
+
this.#size--;
|
|
177
|
+
}
|
|
178
|
+
return value;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Appends a new value to the end of the list.
|
|
183
|
+
* @param {T} value - Value to append.
|
|
184
|
+
* @returns {ListNode<T>} The inserted node.
|
|
185
|
+
*/
|
|
186
|
+
push(value: T): ListNode<T> {
|
|
187
|
+
const node = new ListNode<T>(value);
|
|
188
|
+
if (this.#first == null || this.#last == null) { // empty
|
|
189
|
+
this.#first = this.#last = node;
|
|
190
|
+
} else { // many elements
|
|
191
|
+
node.prev = this.#last;
|
|
192
|
+
this.#last.next = node;
|
|
193
|
+
this.#last = node;
|
|
194
|
+
}
|
|
195
|
+
this.#size++;
|
|
196
|
+
return node;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Prepends a new value to the beginning of the list.
|
|
201
|
+
* @param {T} value - Value to prepend.
|
|
202
|
+
* @returns {ListNode<T>} The inserted node.
|
|
203
|
+
*/
|
|
204
|
+
pushStart(value: T): ListNode<T> {
|
|
205
|
+
const node = new ListNode<T>(value);
|
|
206
|
+
if (this.#first == null || this.#last == null) { // empty
|
|
207
|
+
this.#first = this.#last = node;
|
|
208
|
+
} else { // many elements
|
|
209
|
+
node.next = this.#first;
|
|
210
|
+
this.#first.prev = node;
|
|
211
|
+
this.#first = node;
|
|
212
|
+
}
|
|
213
|
+
this.#size++;
|
|
214
|
+
return node;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Inserts an existing node after the target node.
|
|
219
|
+
* @param {ListNode<T>} node - Node to insert.
|
|
220
|
+
* @param {ListNode<T>} targetNode - Node to insert after.
|
|
221
|
+
* @returns {ListNode<T>} The inserted node.
|
|
222
|
+
*/
|
|
223
|
+
insertNodeAfter(node: ListNode<T>, targetNode: ListNode<T>): ListNode<T> {
|
|
224
|
+
node.prev = targetNode;
|
|
225
|
+
node.next = targetNode.next;
|
|
226
|
+
if (targetNode.next != null) {
|
|
227
|
+
targetNode.next.prev = node;
|
|
228
|
+
}
|
|
229
|
+
targetNode.next = node;
|
|
230
|
+
if (targetNode === this.#last) {
|
|
231
|
+
this.#last = node;
|
|
232
|
+
}
|
|
233
|
+
this.#size++;
|
|
234
|
+
return node;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Inserts a new value after the target node.
|
|
239
|
+
* @param {T} value - Value to insert.
|
|
240
|
+
* @param {ListNode<T>} targetNode - Node to insert after.
|
|
241
|
+
* @returns {ListNode<T>} The new node.
|
|
242
|
+
*/
|
|
243
|
+
insertAfter(value: T, targetNode: ListNode<T>): ListNode<T> {
|
|
244
|
+
const node = new ListNode<T>(value, targetNode, targetNode.next);
|
|
245
|
+
if (targetNode.next != null) {
|
|
246
|
+
targetNode.next.prev = node;
|
|
247
|
+
}
|
|
248
|
+
targetNode.next = node;
|
|
249
|
+
if (targetNode === this.#last) {
|
|
250
|
+
this.#last = node;
|
|
251
|
+
}
|
|
252
|
+
this.#size++;
|
|
253
|
+
return node;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Inserts an existing node before the target node.
|
|
258
|
+
* @param {ListNode<T>} node - Node to insert.
|
|
259
|
+
* @param {ListNode<T>} targetNode - Node to insert before.
|
|
260
|
+
* @returns {ListNode<T>} The inserted node.
|
|
261
|
+
*/
|
|
262
|
+
insertNodeBefore(node: ListNode<T>, targetNode: ListNode<T>): ListNode<T> {
|
|
263
|
+
node.prev = targetNode.prev;
|
|
264
|
+
node.next = targetNode;
|
|
265
|
+
if (targetNode.prev != null) {
|
|
266
|
+
targetNode.prev.next = node;
|
|
267
|
+
}
|
|
268
|
+
targetNode.prev = node;
|
|
269
|
+
if (targetNode === this.#first) {
|
|
270
|
+
this.#first = node;
|
|
271
|
+
}
|
|
272
|
+
this.#size++;
|
|
273
|
+
return node;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Inserts a new value before the target node.
|
|
278
|
+
* @param {T} value - Value to insert.
|
|
279
|
+
* @param {ListNode<T>} targetNode - Node to insert before.
|
|
280
|
+
* @returns {ListNode<T>} The new node.
|
|
281
|
+
*/
|
|
282
|
+
insertBefore(value: T, targetNode: ListNode<T>): ListNode<T> {
|
|
283
|
+
const node = new ListNode<T>(value, targetNode.prev, targetNode);
|
|
284
|
+
if (targetNode.prev != null) {
|
|
285
|
+
targetNode.prev.next = node;
|
|
286
|
+
}
|
|
287
|
+
targetNode.prev = node;
|
|
288
|
+
if (targetNode === this.#first) {
|
|
289
|
+
this.#first = node;
|
|
290
|
+
}
|
|
291
|
+
this.#size++;
|
|
292
|
+
return node;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Removes the given node from the list.
|
|
297
|
+
* @param {ListNode<T>} node - Node to remove.
|
|
298
|
+
* @returns {ListNode<T>} The removed node.
|
|
299
|
+
*/
|
|
300
|
+
remove(node: ListNode<T>): ListNode<T> {
|
|
301
|
+
if (node.next != null) {
|
|
302
|
+
node.next.prev = node.prev;
|
|
303
|
+
}
|
|
304
|
+
if (node.prev != null) {
|
|
305
|
+
node.prev.next = node.next;
|
|
306
|
+
}
|
|
307
|
+
if (this.#first === node) {
|
|
308
|
+
this.#first = node.next;
|
|
309
|
+
}
|
|
310
|
+
if (this.#last === node) {
|
|
311
|
+
this.#last = node.prev;
|
|
312
|
+
}
|
|
313
|
+
this.#size--;
|
|
314
|
+
return node;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Clears the list.
|
|
319
|
+
* @returns {void}
|
|
320
|
+
*/
|
|
321
|
+
clear(): void {
|
|
322
|
+
this.#first = this.#last = undefined;
|
|
323
|
+
this.#size = 0;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Rotates the list by the given amount.
|
|
328
|
+
* Positive values move nodes from first to last.
|
|
329
|
+
* Negative values move nodes from last to first.
|
|
330
|
+
* @param {number} amount - Number of positions to rotate.
|
|
331
|
+
* @returns {number} The absolute number of rotations performed.
|
|
332
|
+
*/
|
|
333
|
+
rotate(amount: number): number {
|
|
334
|
+
if (this.#first == null || this.#last == null || this.#size === 0) { // empty or invalid
|
|
335
|
+
return 0;
|
|
336
|
+
} else if (this.#size === 1) {
|
|
337
|
+
return 1;
|
|
338
|
+
}
|
|
339
|
+
amount = amount % this.#size;
|
|
340
|
+
if (amount === 0) {
|
|
341
|
+
return 0;
|
|
342
|
+
}
|
|
343
|
+
// rotate via the shortest path
|
|
344
|
+
const absAmount = Math.abs(amount);
|
|
345
|
+
if (absAmount > this.#size / 2) {
|
|
346
|
+
amount = amount > 0 ? amount - this.#size : this.#size + amount;
|
|
347
|
+
}
|
|
348
|
+
// make circular
|
|
349
|
+
this.#first.prev = this.#last;
|
|
350
|
+
this.#last.next = this.#first;
|
|
351
|
+
// find the new start node
|
|
352
|
+
let it: ListNode<T> = this.#first;
|
|
353
|
+
if (amount > 0) {
|
|
354
|
+
while (amount-- > 0) it = it.next!;
|
|
355
|
+
} else {
|
|
356
|
+
while (amount++ < 0) it = it.prev!;
|
|
357
|
+
}
|
|
358
|
+
// reset the first and last nodes
|
|
359
|
+
this.#first = it;
|
|
360
|
+
this.#last = it.prev!;
|
|
361
|
+
// detach first and last nodes
|
|
362
|
+
this.#first.prev = undefined;
|
|
363
|
+
this.#last.next = undefined;
|
|
364
|
+
return absAmount;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Removes `amount` nodes from the start of the list and returns the last node
|
|
369
|
+
* of the detached segment (i.e., the node that was at the boundary of the detachment).
|
|
370
|
+
*
|
|
371
|
+
* If `amount` is greater than or equal to the list size, the entire list is cleared,
|
|
372
|
+
* and the original first node is returned.
|
|
373
|
+
*
|
|
374
|
+
* @param {number} amount - Number of nodes to remove from the start.
|
|
375
|
+
* @returns {ListNode<T> | undefined} The first node of the detached segment, or `undefined` if the list was empty or `amount <= 0`.
|
|
376
|
+
*/
|
|
377
|
+
trimStart(amount: number): ListNode<T> | undefined {
|
|
378
|
+
if (this.#first == null || this.#last == null || amount <= 0) { // empty
|
|
379
|
+
return undefined;
|
|
380
|
+
}
|
|
381
|
+
let node = this.#first;
|
|
382
|
+
if (this.#first == this.#last) { // one element
|
|
383
|
+
this.#first = undefined;
|
|
384
|
+
this.#last = undefined;
|
|
385
|
+
this.#size = 0;
|
|
386
|
+
} else if (amount >= this.#size) { // trimming all elements
|
|
387
|
+
// clear
|
|
388
|
+
this.#first = this.#last = undefined;
|
|
389
|
+
this.#size = 0;
|
|
390
|
+
} else { // many elements
|
|
391
|
+
// iterate via the shortest path
|
|
392
|
+
const absAmount = Math.abs(amount);
|
|
393
|
+
if (absAmount > this.#size / 2) {
|
|
394
|
+
amount = amount > 0 ? amount - this.#size : this.#size + amount;
|
|
395
|
+
}
|
|
396
|
+
let it: ListNode<T>;
|
|
397
|
+
if (amount > 0) {
|
|
398
|
+
it = this.#first;
|
|
399
|
+
while (amount-- > 0) it = it.next!;
|
|
400
|
+
} else {
|
|
401
|
+
it = this.#last;
|
|
402
|
+
while (++amount < 0) it = it.prev!;
|
|
403
|
+
}
|
|
404
|
+
// detach end
|
|
405
|
+
node = it.prev!;
|
|
406
|
+
it.prev!.next = undefined;
|
|
407
|
+
it.prev = undefined;
|
|
408
|
+
this.#first = it;
|
|
409
|
+
this.#size -= absAmount;
|
|
410
|
+
}
|
|
411
|
+
return node;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Removes `amount` nodes from the end of the list and returns the first node
|
|
416
|
+
* of the detached segment (i.e., the node that was at the boundary of the detachment).
|
|
417
|
+
*
|
|
418
|
+
* If `amount` is greater than or equal to the list size, the entire list is cleared,
|
|
419
|
+
* and the original last node is returned.
|
|
420
|
+
*
|
|
421
|
+
* @param {number} amount - Number of nodes to remove from the end.
|
|
422
|
+
* @returns {ListNode<T> | undefined} The first node of the detached segment, or `undefined` if the list was empty or `amount <= 0`.
|
|
423
|
+
*/
|
|
424
|
+
trimEnd(amount: number): ListNode<T> | undefined {
|
|
425
|
+
if (this.#first == null || this.#last == null || amount <= 0) { // empty
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
let node = this.#last;
|
|
429
|
+
if (this.#first == this.#last) { // one element
|
|
430
|
+
this.#first = undefined;
|
|
431
|
+
this.#last = undefined;
|
|
432
|
+
this.#size--;
|
|
433
|
+
} else if (amount >= this.#size) { // trimming all elements
|
|
434
|
+
// clear
|
|
435
|
+
this.#first = this.#last = undefined;
|
|
436
|
+
this.#size = 0;
|
|
437
|
+
} else { // many elements
|
|
438
|
+
// iterate via the shortest path
|
|
439
|
+
const absAmount = Math.abs(amount);
|
|
440
|
+
if (absAmount > this.#size / 2) {
|
|
441
|
+
amount = amount > 0 ? amount - this.#size : this.#size + amount;
|
|
442
|
+
}
|
|
443
|
+
let it: ListNode<T>;
|
|
444
|
+
if (amount > 0) {
|
|
445
|
+
it = this.#last;
|
|
446
|
+
while (amount-- > 0) it = it.prev!;
|
|
447
|
+
} else {
|
|
448
|
+
it = this.#first;
|
|
449
|
+
while (++amount < 0) it = it.next!;
|
|
450
|
+
}
|
|
451
|
+
// detach end
|
|
452
|
+
node = it.next!;
|
|
453
|
+
it.next!.prev = undefined;
|
|
454
|
+
it.next = undefined;
|
|
455
|
+
this.#last = it;
|
|
456
|
+
this.#size -= absAmount;
|
|
457
|
+
}
|
|
458
|
+
return node;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export default List;
|