informa 1.0.0
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/.vscode/settings.json +3 -0
- package/README.md +238 -0
- package/dist/EnumerableWeakMap.d.ts +13 -0
- package/dist/EnumerableWeakMap.d.ts.map +1 -0
- package/dist/EnumerableWeakMap.js +56 -0
- package/dist/EnumerableWeakMap.js.map +1 -0
- package/dist/clone.d.ts +2 -0
- package/dist/clone.d.ts.map +1 -0
- package/dist/clone.js +30 -0
- package/dist/clone.js.map +1 -0
- package/dist/high.d.ts +60 -0
- package/dist/high.d.ts.map +1 -0
- package/dist/high.js +107 -0
- package/dist/high.js.map +1 -0
- package/dist/internals.d.ts +14 -0
- package/dist/internals.d.ts.map +1 -0
- package/dist/internals.js +24 -0
- package/dist/internals.js.map +1 -0
- package/dist/low.d.ts +57 -0
- package/dist/low.d.ts.map +1 -0
- package/dist/low.js +260 -0
- package/dist/low.js.map +1 -0
- package/dist/quirks/array.d.ts +21 -0
- package/dist/quirks/array.d.ts.map +1 -0
- package/dist/quirks/array.js +176 -0
- package/dist/quirks/array.js.map +1 -0
- package/dist/quirks/basestatified.d.ts +5 -0
- package/dist/quirks/basestatified.d.ts.map +1 -0
- package/dist/quirks/basestatified.js +117 -0
- package/dist/quirks/basestatified.js.map +1 -0
- package/dist/quirks/set.d.ts +12 -0
- package/dist/quirks/set.d.ts.map +1 -0
- package/dist/quirks/set.js +37 -0
- package/dist/quirks/set.js.map +1 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +37 -0
- package/dist/test.js.map +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +23 -0
- package/dist/utils.js.map +1 -0
- package/package.json +28 -0
- package/src/EnumerableWeakMap.ts +63 -0
- package/src/high.ts +163 -0
- package/src/internals.ts +32 -0
- package/src/low.ts +411 -0
- package/src/quirks/array.ts +211 -0
- package/src/quirks/basestatified.ts +194 -0
- package/src/quirks/set.ts +48 -0
- package/src/test.ts +49 -0
- package/src/utils.ts +31 -0
- package/tsconfig.json +44 -0
package/src/low.ts
ADDED
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { exitProxySymbol, getMetadataOf, globalStateMode, metadataMap, proxyMemoized, setGlobalStateMode, setMetadataOf, statifySealKey, type Statify } from "./internals.js";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
import { StatifiedSet } from "./quirks/set.js";
|
|
4
|
+
import { isListInGrid } from "./utils.js";
|
|
5
|
+
import { EnumerableWeakMap } from "./EnumerableWeakMap.js";
|
|
6
|
+
|
|
7
|
+
type ProxyEventEmitterEvents = {
|
|
8
|
+
"set": [any],
|
|
9
|
+
"replace": [any],
|
|
10
|
+
|
|
11
|
+
// "changeDeep": [unknown], // TODO
|
|
12
|
+
"deleteProp": [any, string | symbol],
|
|
13
|
+
"setProp": [any, string | symbol],
|
|
14
|
+
"replaceProp": [any, string | symbol],
|
|
15
|
+
|
|
16
|
+
"lengthChanged": [],
|
|
17
|
+
"spliceInElement": [any, number],
|
|
18
|
+
"spliceOutElement": [any, number],
|
|
19
|
+
"replaceElement": [any, number],
|
|
20
|
+
|
|
21
|
+
"sizeChanged": [],
|
|
22
|
+
"addItem": [any],
|
|
23
|
+
"deleteItem": [any],
|
|
24
|
+
} & Record<string, never>;
|
|
25
|
+
|
|
26
|
+
class Tree<T, U> {
|
|
27
|
+
value: U;
|
|
28
|
+
children = new Map<T, Tree<T, U>>();
|
|
29
|
+
|
|
30
|
+
constructor(value: U) {
|
|
31
|
+
this.value = value;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get(key: T) {
|
|
35
|
+
return this.children.get(key);
|
|
36
|
+
}
|
|
37
|
+
getOrCreate(key: T, creation: (k: T) => U) {
|
|
38
|
+
if (!this.children.has(key)) {
|
|
39
|
+
const result = new Tree<T, U>(creation(key));
|
|
40
|
+
this.children.set(key, result);
|
|
41
|
+
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return this.children.get(key)!;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class StateMetadata extends EventEmitter<ProxyEventEmitterEvents> {
|
|
50
|
+
eventEmitterTree = new Tree<string | symbol, StateMetadata>(this);
|
|
51
|
+
|
|
52
|
+
parents = new EnumerableWeakMap<StateMetadata, Set<string | symbol>>();
|
|
53
|
+
|
|
54
|
+
eventEmitterAtPath(path: (string | symbol)[]) {
|
|
55
|
+
if (path.length === 0) return this;
|
|
56
|
+
|
|
57
|
+
let node = this.eventEmitterTree;
|
|
58
|
+
for (let i = 0; i < path.length; i += 1) {
|
|
59
|
+
const nextAccess = path[i]!;
|
|
60
|
+
node = node.getOrCreate(nextAccess, () => new StateMetadata());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return node.value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
eventEmitterAtPathMaybe(path: (string | symbol)[]) {
|
|
67
|
+
if (path.length === 0) return this;
|
|
68
|
+
|
|
69
|
+
let node = this.eventEmitterTree;
|
|
70
|
+
for (let i = 0; i < path.length; i += 1) {
|
|
71
|
+
const nextAccess = path[i]!;
|
|
72
|
+
|
|
73
|
+
const maybeNode = node.get(nextAccess);
|
|
74
|
+
if (!maybeNode) return undefined;
|
|
75
|
+
|
|
76
|
+
node = maybeNode;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return node.value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#pushParents(
|
|
83
|
+
queue: { ancestor: StateMetadata; path: (string | symbol)[] }[],
|
|
84
|
+
suffixPath: (string | symbol)[],
|
|
85
|
+
) {
|
|
86
|
+
for (const [parent, keys] of this.parents) {
|
|
87
|
+
for (const key of keys) {
|
|
88
|
+
queue.push({
|
|
89
|
+
ancestor: parent,
|
|
90
|
+
path: [key, ...suffixPath],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
emit<E extends string | symbol>(
|
|
97
|
+
eventName: string | E,
|
|
98
|
+
...args: E extends string
|
|
99
|
+
? ProxyEventEmitterEvents[E]
|
|
100
|
+
: E extends keyof EventEmitter.EventEmitterEventMap
|
|
101
|
+
? EventEmitter.EventEmitterEventMap[E]
|
|
102
|
+
: any[]
|
|
103
|
+
): boolean {
|
|
104
|
+
let result = super.emit(eventName, ...args);
|
|
105
|
+
|
|
106
|
+
const queue: { ancestor: StateMetadata; path: (string | symbol)[] }[] = [];
|
|
107
|
+
this.#pushParents(queue, []);
|
|
108
|
+
|
|
109
|
+
const seen = new Map<StateMetadata, (string | symbol)[][]>();
|
|
110
|
+
|
|
111
|
+
while (queue.length > 0) {
|
|
112
|
+
const { ancestor, path } = queue.pop()!;
|
|
113
|
+
|
|
114
|
+
const paths = seen.get(ancestor);
|
|
115
|
+
|
|
116
|
+
if (isListInGrid(paths ?? [], path)) continue;
|
|
117
|
+
|
|
118
|
+
if (paths) paths.push(path);
|
|
119
|
+
else seen.set(ancestor, [path]);
|
|
120
|
+
|
|
121
|
+
const target = ancestor.eventEmitterAtPathMaybe(path);
|
|
122
|
+
if (target) result ||= target.emit(eventName, ...args);
|
|
123
|
+
|
|
124
|
+
ancestor.#pushParents(queue, path);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export interface ExitProxyValue {
|
|
132
|
+
path: (string | symbol)[],
|
|
133
|
+
stateRoot: Statify<StatifiableObj>,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface Exitable {
|
|
137
|
+
[exitProxySymbol]: ExitProxyValue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export type StatifiableProp = boolean | string | number | bigint | symbol | null | undefined | StatifiableObj | Statify<StatifiableObj>;
|
|
141
|
+
export type StatifiableObj = { [k: string | symbol]: StatifiableProp } | {};
|
|
142
|
+
|
|
143
|
+
export type Statified<T extends StatifiableProp> = T extends object
|
|
144
|
+
? T extends Set<infer U>
|
|
145
|
+
? StatifiedSet<Statified<U>>
|
|
146
|
+
: T extends Set<any>
|
|
147
|
+
? never
|
|
148
|
+
: Statify<{ [K in keyof T]: Statified<T[K]> }>
|
|
149
|
+
: T;
|
|
150
|
+
|
|
151
|
+
export function statifyObject<T extends { [k: string | symbol]: Statified<StatifiableProp> }>(target: T): Statified<T> {
|
|
152
|
+
if ((target as Statified<T>)[statifySealKey]) {
|
|
153
|
+
return target as Statified<T>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (proxyMemoized.has(target)) {
|
|
157
|
+
return proxyMemoized.get(target)! as Statified<T>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let stateMetadata: StateMetadata;
|
|
161
|
+
|
|
162
|
+
const proxyObj = new Proxy(target, {
|
|
163
|
+
get(target, prop, recv) {
|
|
164
|
+
if (prop === statifySealKey) return true;
|
|
165
|
+
|
|
166
|
+
const result = Reflect.get(target, prop, recv);
|
|
167
|
+
|
|
168
|
+
if (globalStateMode === "extract-proxy-path") {
|
|
169
|
+
if (prop === exitProxySymbol) {
|
|
170
|
+
return { path: [], stateRoot: proxyObj };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return extract(result, proxyObj, [prop])
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return result;
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
set(target, prop, newVal, recv) {
|
|
180
|
+
const had = Reflect.has(target, prop);
|
|
181
|
+
const oldVal = Reflect.get(target, prop) as unknown;
|
|
182
|
+
|
|
183
|
+
const result = Reflect.set(target, prop, newVal, recv);
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
if (result) {
|
|
187
|
+
if (
|
|
188
|
+
oldVal !== newVal
|
|
189
|
+
&& typeof newVal === "object"
|
|
190
|
+
&& newVal != null
|
|
191
|
+
&& newVal[statifySealKey]
|
|
192
|
+
) {
|
|
193
|
+
const newValMetadata = getMetadataOf(newVal as Statify<StatifiableObj>);
|
|
194
|
+
|
|
195
|
+
hook(stateMetadata, prop, newValMetadata);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (had) {
|
|
199
|
+
if (
|
|
200
|
+
oldVal !== newVal
|
|
201
|
+
&& typeof oldVal === "object"
|
|
202
|
+
&& oldVal != null
|
|
203
|
+
&& (oldVal as any)[statifySealKey]
|
|
204
|
+
) {
|
|
205
|
+
const oldValMetadata = getMetadataOf(oldVal as Statify<StatifiableObj>);
|
|
206
|
+
|
|
207
|
+
unhook(stateMetadata, prop, oldValMetadata);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (
|
|
211
|
+
typeof prop !== "symbol" &&
|
|
212
|
+
Number.isInteger(Number(prop)) &&
|
|
213
|
+
Array.isArray(target)
|
|
214
|
+
) {
|
|
215
|
+
stateMetadata.emit('replaceElement', newVal, Number(prop));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
stateMetadata.emit('replaceProp', newVal, prop);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
stateMetadata.emit('setProp', newVal, prop);
|
|
222
|
+
|
|
223
|
+
const maybeEventEmitterAtVal = stateMetadata.eventEmitterAtPathMaybe([prop]);
|
|
224
|
+
if (maybeEventEmitterAtVal) {
|
|
225
|
+
if (had) {
|
|
226
|
+
maybeEventEmitterAtVal.emit('replace', newVal);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
maybeEventEmitterAtVal.emit('set', newVal);
|
|
230
|
+
|
|
231
|
+
emitDescendantPathEvents(
|
|
232
|
+
stateMetadata,
|
|
233
|
+
[prop],
|
|
234
|
+
newVal,
|
|
235
|
+
had,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
} finally {
|
|
240
|
+
return result;
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
deleteProperty(target, prop) {
|
|
245
|
+
const oldVal = target[prop as keyof typeof target] as unknown;
|
|
246
|
+
|
|
247
|
+
const result = Reflect.deleteProperty(target, prop);
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
if (result) {
|
|
251
|
+
if (
|
|
252
|
+
typeof oldVal === "object"
|
|
253
|
+
&& oldVal != null
|
|
254
|
+
&& (oldVal as Statify<StatifiableObj>)[statifySealKey]
|
|
255
|
+
) {
|
|
256
|
+
const oldValMetadata = getMetadataOf(oldVal as Statify<StatifiableObj>);
|
|
257
|
+
|
|
258
|
+
unhook(stateMetadata, prop, oldValMetadata);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
stateMetadata.emit('deleteProp', oldVal, prop);
|
|
262
|
+
}
|
|
263
|
+
} finally {
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
has(target, prop) {
|
|
269
|
+
if (globalStateMode === "extract-proxy-path") {
|
|
270
|
+
if (prop === exitProxySymbol) {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return Reflect.has(target, prop);
|
|
276
|
+
},
|
|
277
|
+
}) as Statified<typeof target>;
|
|
278
|
+
|
|
279
|
+
stateMetadata = setMetadataOf(proxyObj, new StateMetadata());
|
|
280
|
+
|
|
281
|
+
proxyMemoized.set(target, proxyObj);
|
|
282
|
+
|
|
283
|
+
return proxyObj;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function extract(
|
|
287
|
+
targetMaybePrimitive: unknown,
|
|
288
|
+
stateRoot: Statify<StatifiableObj>,
|
|
289
|
+
path: (string | symbol)[],
|
|
290
|
+
):
|
|
291
|
+
Record<string | symbol, unknown> & Exitable
|
|
292
|
+
{
|
|
293
|
+
const target = Object(targetMaybePrimitive);
|
|
294
|
+
|
|
295
|
+
return new Proxy(target, {
|
|
296
|
+
get(target, prop, recv) {
|
|
297
|
+
if (prop === exitProxySymbol) {
|
|
298
|
+
return { path, stateRoot };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (Array.isArray(target)) {
|
|
302
|
+
// TODO
|
|
303
|
+
throw new Error("Array index check is not yet supported.");
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return extract(Reflect.get(target, prop, recv), stateRoot, [...path, prop]);
|
|
307
|
+
},
|
|
308
|
+
has(target, prop) {
|
|
309
|
+
if (globalStateMode === "extract-proxy-path") {
|
|
310
|
+
if (prop === exitProxySymbol) return true;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return Reflect.has(target, prop);
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function selectorToRootAndPath(selector: () => Statify<StatifiableObj>) {
|
|
319
|
+
setGlobalStateMode("extract-proxy-path");
|
|
320
|
+
|
|
321
|
+
let result;
|
|
322
|
+
try {
|
|
323
|
+
result = (selector() as Exitable)[exitProxySymbol];
|
|
324
|
+
} finally {
|
|
325
|
+
setGlobalStateMode("normal");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export function hook(
|
|
332
|
+
fromMetadata: StateMetadata,
|
|
333
|
+
prop: string | symbol,
|
|
334
|
+
toMetadata: StateMetadata,
|
|
335
|
+
) {
|
|
336
|
+
let resultingSet = toMetadata.parents.get(fromMetadata);
|
|
337
|
+
|
|
338
|
+
if (!resultingSet) {
|
|
339
|
+
resultingSet = new Set();
|
|
340
|
+
|
|
341
|
+
toMetadata.parents.set(fromMetadata, resultingSet);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
resultingSet.add(prop);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function unhook(
|
|
348
|
+
fromMetadata: StateMetadata,
|
|
349
|
+
prop: string | symbol,
|
|
350
|
+
toMetadata: StateMetadata,
|
|
351
|
+
) {
|
|
352
|
+
const set = toMetadata.parents.get(fromMetadata);
|
|
353
|
+
if (set) {
|
|
354
|
+
set.delete(prop);
|
|
355
|
+
|
|
356
|
+
if (set.size === 0) {
|
|
357
|
+
toMetadata.parents.delete(fromMetadata);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function getAtPathMaybe(
|
|
363
|
+
value: unknown,
|
|
364
|
+
path: (string | symbol)[],
|
|
365
|
+
) {
|
|
366
|
+
let current = value;
|
|
367
|
+
|
|
368
|
+
for (const key of path) {
|
|
369
|
+
if (current == null) return undefined;
|
|
370
|
+
current = Reflect.get(Object(current), key);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return current;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function emitDescendantPathEvents(
|
|
377
|
+
rootMetadata: StateMetadata,
|
|
378
|
+
prefixPath: (string | symbol)[],
|
|
379
|
+
nextRootValue: unknown,
|
|
380
|
+
emitReplace: boolean,
|
|
381
|
+
) {
|
|
382
|
+
let node = rootMetadata.eventEmitterTree;
|
|
383
|
+
|
|
384
|
+
for (const key of prefixPath) {
|
|
385
|
+
const next = node.get(key);
|
|
386
|
+
if (!next) return;
|
|
387
|
+
node = next;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const walk = (
|
|
391
|
+
tree: Tree<string | symbol, StateMetadata>,
|
|
392
|
+
suffixPath: (string | symbol)[],
|
|
393
|
+
) => {
|
|
394
|
+
if (suffixPath.length > 0) {
|
|
395
|
+
const value = getAtPathMaybe(nextRootValue, suffixPath);
|
|
396
|
+
|
|
397
|
+
if (emitReplace) {
|
|
398
|
+
tree.value.emit("replace", value);
|
|
399
|
+
}
|
|
400
|
+
tree.value.emit("set", value);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
for (const [key, child] of tree.children) {
|
|
404
|
+
walk(child, [...suffixPath, key]);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
for (const [key, child] of node.children) {
|
|
409
|
+
walk(child, [key]);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { exitProxySymbol, getMetadataOf, setMetadataOf, statifySealKey, type Statify } from "../internals.js";
|
|
2
|
+
import { StateMetadata, type ExitProxyValue, type StatifiableProp } from "../low.js";
|
|
3
|
+
import { MakeStatified } from "./basestatified.js";
|
|
4
|
+
|
|
5
|
+
const StatifiableOrgArray = MakeStatified(Array);
|
|
6
|
+
|
|
7
|
+
export class StatifiedArray<T extends StatifiableProp>
|
|
8
|
+
extends StatifiableOrgArray<T> implements Array<T>, Statify<T[]>
|
|
9
|
+
{
|
|
10
|
+
[statifySealKey]: true = true;
|
|
11
|
+
declare [exitProxySymbol]: ExitProxyValue;
|
|
12
|
+
|
|
13
|
+
#metadata: StateMetadata;
|
|
14
|
+
|
|
15
|
+
constructor(arrayLength: number);
|
|
16
|
+
constructor(...items: T[]);
|
|
17
|
+
constructor(arrayLengthOrFirstItem?: number | T, ...rest: T[]) {
|
|
18
|
+
if (arrayLengthOrFirstItem == undefined) {
|
|
19
|
+
super();
|
|
20
|
+
} else if (typeof arrayLengthOrFirstItem === "number") {
|
|
21
|
+
super(arrayLengthOrFirstItem);
|
|
22
|
+
} else {
|
|
23
|
+
super(arrayLengthOrFirstItem, ...rest);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.#metadata = getMetadataOf(this);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
push(...items: T[]): number {
|
|
30
|
+
const result = super.push(...items);
|
|
31
|
+
|
|
32
|
+
for (let i = result - items.length; i < result; i += 1) {
|
|
33
|
+
this.#metadata.emit("spliceInElement", this[i]!, i);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (items.length > 0) {
|
|
37
|
+
this.#metadata.emit("lengthChanged");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
unshift(...items: T[]): number {
|
|
43
|
+
const result = super.unshift(...items);
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
46
|
+
this.#metadata.emit("spliceInElement", this[i]!, i);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (items.length > 0) {
|
|
50
|
+
this.#metadata.emit("lengthChanged");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pop(): T | undefined {
|
|
57
|
+
if (this.length > 0) {
|
|
58
|
+
const result = super.pop();
|
|
59
|
+
|
|
60
|
+
this.#metadata.emit("spliceOutElement", result, super.length);
|
|
61
|
+
this.#metadata.emit("lengthChanged");
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
shift(): T | undefined {
|
|
68
|
+
if (this.length > 0) {
|
|
69
|
+
const result = super.shift();
|
|
70
|
+
|
|
71
|
+
this.#metadata.emit("spliceOutElement", result, 0);
|
|
72
|
+
this.#metadata.emit("lengthChanged");
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#normalizeSplice(start: number, deleteCount?: number, ...items: T[]): [number, number, T[]] {
|
|
79
|
+
if (start == undefined) {
|
|
80
|
+
start = 0;
|
|
81
|
+
} else if (-this.length <= start && start < 0) {
|
|
82
|
+
start += this.length;
|
|
83
|
+
} else if (start < -this.length) {
|
|
84
|
+
start = 0;
|
|
85
|
+
} else if (start >= this.length) {
|
|
86
|
+
[start, deleteCount] = [this.length - 1, 0];
|
|
87
|
+
} else if (arguments.length === 0) {
|
|
88
|
+
return [0, 0, []];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (arguments.length === 1) {
|
|
92
|
+
return [start, this.length - start, []];
|
|
93
|
+
} else if (deleteCount == undefined) {
|
|
94
|
+
deleteCount = 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
deleteCount = Math.min(this.length - start, deleteCount);
|
|
98
|
+
|
|
99
|
+
return [start, deleteCount, items];
|
|
100
|
+
}
|
|
101
|
+
splice(start: number, deleteCount?: number, ...items: T[]): T[] {
|
|
102
|
+
const [startNormed, deleteCountNormed, itemsNormed] = arguments.length === 1
|
|
103
|
+
? this.#normalizeSplice(start)
|
|
104
|
+
: this.#normalizeSplice(start, deleteCount, ...items);
|
|
105
|
+
|
|
106
|
+
const result = super.splice(startNormed, deleteCountNormed, ...itemsNormed);
|
|
107
|
+
|
|
108
|
+
for (let i = startNormed + deleteCountNormed; i > startNormed + itemsNormed.length; i -= 1) {
|
|
109
|
+
this.#metadata.emit("spliceOutElement", result[i - startNormed], i);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (let i = startNormed; i < startNormed + Math.min(itemsNormed.length, deleteCountNormed); i += 1) {
|
|
113
|
+
this.#metadata.emit("replaceElement", this[i], i);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
for (let i = startNormed + deleteCountNormed; i < startNormed + itemsNormed.length; i += 1) {
|
|
117
|
+
this.#metadata.emit("spliceInElement", this[i], i);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (itemsNormed.length !== deleteCountNormed) {
|
|
121
|
+
this.#metadata.emit("lengthChanged");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#normalizeStartEnd(start?: number, end?: number): [number, number] {
|
|
128
|
+
if (start == undefined) {
|
|
129
|
+
start = 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (-this.length <= start && start < 0) {
|
|
133
|
+
start = start + this.length;
|
|
134
|
+
} else if (start < -this.length) {
|
|
135
|
+
start = 0;
|
|
136
|
+
} else if (start >= this.length) {
|
|
137
|
+
return [0, 0];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (end == undefined) {
|
|
141
|
+
end = this.length;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (-this.length <= end && end < 0) {
|
|
145
|
+
end += this.length;
|
|
146
|
+
} else if (end < -this.length) {
|
|
147
|
+
end = 0;
|
|
148
|
+
} else if (end >= this.length) {
|
|
149
|
+
end = this.length;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (end < start) {
|
|
153
|
+
end = start;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return [start, end];
|
|
157
|
+
}
|
|
158
|
+
fill(value: T, start?: number, end?: number): this {
|
|
159
|
+
const [startNormed, endNormed] = arguments.length === 1
|
|
160
|
+
? this.#normalizeStartEnd()
|
|
161
|
+
: arguments.length === 2
|
|
162
|
+
? this.#normalizeStartEnd(start)
|
|
163
|
+
: this.#normalizeStartEnd(start, end);
|
|
164
|
+
|
|
165
|
+
const result = super.fill(value, startNormed, endNormed);
|
|
166
|
+
|
|
167
|
+
for (let i = startNormed; i < endNormed; i += 1) {
|
|
168
|
+
this.#metadata.emit("replaceElement", this[i], i);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
copyWithin(target: number, start: number, end?: number): this {
|
|
174
|
+
const [startNormed, endNormed] = arguments.length === 2
|
|
175
|
+
? this.#normalizeStartEnd(start)
|
|
176
|
+
: this.#normalizeStartEnd(start, end);
|
|
177
|
+
|
|
178
|
+
const result = super.copyWithin(target, startNormed, endNormed);
|
|
179
|
+
|
|
180
|
+
for (let i = target; i < target + endNormed - startNormed; i += 1) {
|
|
181
|
+
this.#metadata.emit("replaceElement", this[i], i);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
reverse(): T[] {
|
|
188
|
+
const result = super.reverse();
|
|
189
|
+
|
|
190
|
+
const parity = this.length % 2;
|
|
191
|
+
const half = (this.length / 2) | 0;
|
|
192
|
+
for (let i = 0; i < this.length - parity; i += 1) {
|
|
193
|
+
if (i > half) {
|
|
194
|
+
this.#metadata.emit("replaceElement", this[i], i + parity);
|
|
195
|
+
} else {
|
|
196
|
+
this.#metadata.emit("replaceElement", this[i], i);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
sort(compareFn?: ((a: T, b: T) => number) | undefined): this {
|
|
203
|
+
const result = super.sort(compareFn);
|
|
204
|
+
|
|
205
|
+
for (let i = 0; i < this.length; i += 1) {
|
|
206
|
+
this.#metadata.emit("replaceElement", this[i], i);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
}
|