atom.io 0.40.9 → 0.40.10
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/transceivers/o-list/index.d.ts +77 -0
- package/dist/transceivers/o-list/index.d.ts.map +1 -0
- package/dist/transceivers/o-list/index.js +308 -0
- package/dist/transceivers/o-list/index.js.map +1 -0
- package/package.json +8 -4
- package/src/transceivers/o-list/index.ts +1 -0
- package/src/transceivers/o-list/o-list.ts +412 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Fn, Subject, Transceiver, TransceiverMode } from "atom.io/internal";
|
|
2
|
+
import { primitive } from "atom.io/json";
|
|
3
|
+
|
|
4
|
+
//#region src/transceivers/o-list/o-list.d.ts
|
|
5
|
+
type ArrayUpdate<P extends primitive> = {
|
|
6
|
+
type: `copyWithin`;
|
|
7
|
+
target: number;
|
|
8
|
+
start: number;
|
|
9
|
+
end?: number;
|
|
10
|
+
prev: readonly P[];
|
|
11
|
+
} | {
|
|
12
|
+
type: `extend`;
|
|
13
|
+
next: number;
|
|
14
|
+
prev: number;
|
|
15
|
+
} | {
|
|
16
|
+
type: `fill`;
|
|
17
|
+
value: P;
|
|
18
|
+
start?: number;
|
|
19
|
+
end?: number;
|
|
20
|
+
prev: readonly P[];
|
|
21
|
+
} | {
|
|
22
|
+
type: `pop` | `shift`;
|
|
23
|
+
value?: P;
|
|
24
|
+
} | {
|
|
25
|
+
type: `push` | `unshift`;
|
|
26
|
+
items: readonly P[];
|
|
27
|
+
} | {
|
|
28
|
+
type: `reverse`;
|
|
29
|
+
} | {
|
|
30
|
+
type: `set`;
|
|
31
|
+
next: P;
|
|
32
|
+
prev?: P;
|
|
33
|
+
index: number;
|
|
34
|
+
} | {
|
|
35
|
+
type: `sort`;
|
|
36
|
+
next: readonly P[];
|
|
37
|
+
prev: readonly P[];
|
|
38
|
+
} | {
|
|
39
|
+
type: `splice`;
|
|
40
|
+
start: number;
|
|
41
|
+
deleteCount?: number;
|
|
42
|
+
items?: readonly P[];
|
|
43
|
+
deleted?: readonly P[];
|
|
44
|
+
} | {
|
|
45
|
+
type: `truncate`;
|
|
46
|
+
length: number;
|
|
47
|
+
items: readonly P[];
|
|
48
|
+
};
|
|
49
|
+
type OListUpdateType = ArrayUpdate<any>[`type`];
|
|
50
|
+
type ArrayMutationHandler = { [K in Exclude<OListUpdateType, `extend` | `set` | `truncate`>]: Fn };
|
|
51
|
+
declare class OList<P extends primitive> extends Array<P> implements Transceiver<ReadonlyArray<P>, ArrayUpdate<P>, ReadonlyArray<P>>, ArrayMutationHandler {
|
|
52
|
+
mode: TransceiverMode;
|
|
53
|
+
readonly subject: Subject<ArrayUpdate<P>>;
|
|
54
|
+
readonly READONLY_VIEW: ReadonlyArray<P>;
|
|
55
|
+
constructor(arrayLength?: number);
|
|
56
|
+
constructor(...items: P[]);
|
|
57
|
+
toJSON(): ReadonlyArray<P>;
|
|
58
|
+
static fromJSON<P extends primitive>(json: ReadonlyArray<P>): OList<P>;
|
|
59
|
+
push(...items: P[]): number;
|
|
60
|
+
pop(): P | undefined;
|
|
61
|
+
shift(): P | undefined;
|
|
62
|
+
unshift(...items: P[]): number;
|
|
63
|
+
reverse(): this;
|
|
64
|
+
sort(compareFn?: (a: P, b: P) => number): this;
|
|
65
|
+
splice(start: number, deleteCount?: number): P[];
|
|
66
|
+
splice(start: number, deleteCount: number, ...items: P[]): P[];
|
|
67
|
+
copyWithin(target: number, start: number, end?: number): this;
|
|
68
|
+
subscribe(key: string, fn: (update: ArrayUpdate<P>) => void): () => void;
|
|
69
|
+
emit(update: ArrayUpdate<P>): void;
|
|
70
|
+
private doStep;
|
|
71
|
+
do(update: ArrayUpdate<P>): null;
|
|
72
|
+
undoStep(update: ArrayUpdate<P>): void;
|
|
73
|
+
undo(update: ArrayUpdate<P>): number | null;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { ArrayMutationHandler, ArrayUpdate, OList, OListUpdateType };
|
|
77
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/transceivers/o-list/o-list.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { Subject } from "atom.io/internal";
|
|
2
|
+
|
|
3
|
+
//#region src/transceivers/o-list/o-list.ts
|
|
4
|
+
var OList = class OList extends Array {
|
|
5
|
+
mode = `record`;
|
|
6
|
+
subject = new Subject();
|
|
7
|
+
READONLY_VIEW = this;
|
|
8
|
+
constructor(...items) {
|
|
9
|
+
super(...items);
|
|
10
|
+
return new Proxy(this, { set: (target, prop, value, receiver) => {
|
|
11
|
+
if (typeof prop === `string` && !Number.isNaN(Number.parseInt(prop, 10))) {
|
|
12
|
+
const index = Number(prop);
|
|
13
|
+
let prev;
|
|
14
|
+
if (this.mode === `record`) prev = target[index];
|
|
15
|
+
target[index] = value;
|
|
16
|
+
if (prev) this.emit({
|
|
17
|
+
type: `set`,
|
|
18
|
+
index,
|
|
19
|
+
next: value,
|
|
20
|
+
prev
|
|
21
|
+
});
|
|
22
|
+
else if (this.mode === `record`) this.emit({
|
|
23
|
+
type: `set`,
|
|
24
|
+
index,
|
|
25
|
+
next: value
|
|
26
|
+
});
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (prop === `length`) {
|
|
30
|
+
if (this.mode === `record`) {
|
|
31
|
+
const prevLength = target.length;
|
|
32
|
+
if (prevLength === value) return true;
|
|
33
|
+
if (prevLength > value) {
|
|
34
|
+
const dropped = target.slice(value);
|
|
35
|
+
target.length = value;
|
|
36
|
+
this.emit({
|
|
37
|
+
type: `truncate`,
|
|
38
|
+
length: value,
|
|
39
|
+
items: dropped
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
target.length = value;
|
|
43
|
+
this.emit({
|
|
44
|
+
type: `extend`,
|
|
45
|
+
next: value,
|
|
46
|
+
prev: prevLength
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
} else target.length = value;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return Reflect.set(target, prop, value, receiver);
|
|
53
|
+
} });
|
|
54
|
+
}
|
|
55
|
+
toJSON() {
|
|
56
|
+
return [...this];
|
|
57
|
+
}
|
|
58
|
+
static fromJSON(json) {
|
|
59
|
+
return new OList(...json);
|
|
60
|
+
}
|
|
61
|
+
push(...items) {
|
|
62
|
+
let result;
|
|
63
|
+
if (this.mode === `record`) {
|
|
64
|
+
this.mode = `playback`;
|
|
65
|
+
result = super.push(...items);
|
|
66
|
+
this.mode = `record`;
|
|
67
|
+
this.emit({
|
|
68
|
+
type: `push`,
|
|
69
|
+
items
|
|
70
|
+
});
|
|
71
|
+
} else result = super.push(...items);
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
pop() {
|
|
75
|
+
let value;
|
|
76
|
+
if (this.mode === `record`) {
|
|
77
|
+
this.mode = `playback`;
|
|
78
|
+
value = super.pop();
|
|
79
|
+
if (value === void 0) this.emit({ type: `pop` });
|
|
80
|
+
else this.emit({
|
|
81
|
+
type: `pop`,
|
|
82
|
+
value
|
|
83
|
+
});
|
|
84
|
+
this.mode = `record`;
|
|
85
|
+
} else value = super.pop();
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
shift() {
|
|
89
|
+
let value;
|
|
90
|
+
if (this.mode === `record`) {
|
|
91
|
+
this.mode = `playback`;
|
|
92
|
+
value = super.shift();
|
|
93
|
+
if (value === void 0) this.emit({ type: `shift` });
|
|
94
|
+
else this.emit({
|
|
95
|
+
type: `shift`,
|
|
96
|
+
value
|
|
97
|
+
});
|
|
98
|
+
this.mode = `record`;
|
|
99
|
+
} else value = super.shift();
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
unshift(...items) {
|
|
103
|
+
let result;
|
|
104
|
+
if (this.mode === `record`) {
|
|
105
|
+
this.mode = `playback`;
|
|
106
|
+
result = super.unshift(...items);
|
|
107
|
+
this.emit({
|
|
108
|
+
type: `unshift`,
|
|
109
|
+
items
|
|
110
|
+
});
|
|
111
|
+
this.mode = `record`;
|
|
112
|
+
} else result = super.unshift(...items);
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
reverse() {
|
|
116
|
+
super.reverse();
|
|
117
|
+
if (this.mode === `record`) this.emit({ type: `reverse` });
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
sort(compareFn) {
|
|
121
|
+
if (this.mode === `record`) {
|
|
122
|
+
this.mode = `playback`;
|
|
123
|
+
const prev = [...this];
|
|
124
|
+
super.sort(compareFn);
|
|
125
|
+
const next = [...this];
|
|
126
|
+
this.emit({
|
|
127
|
+
type: `sort`,
|
|
128
|
+
next,
|
|
129
|
+
prev
|
|
130
|
+
});
|
|
131
|
+
this.mode = `record`;
|
|
132
|
+
}
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
splice(...params) {
|
|
136
|
+
const [start, deleteCount, ...items] = params;
|
|
137
|
+
const originalMode = this.mode;
|
|
138
|
+
if (originalMode === `record`) this.mode = `playback`;
|
|
139
|
+
const deleted = super.splice(...params);
|
|
140
|
+
if (originalMode === `record`) this.mode = `record`;
|
|
141
|
+
if (deleteCount === void 0) this.emit({
|
|
142
|
+
type: `splice`,
|
|
143
|
+
start,
|
|
144
|
+
items,
|
|
145
|
+
deleted,
|
|
146
|
+
deleteCount: deleted.length
|
|
147
|
+
});
|
|
148
|
+
else this.emit({
|
|
149
|
+
type: `splice`,
|
|
150
|
+
start,
|
|
151
|
+
items,
|
|
152
|
+
deleted,
|
|
153
|
+
deleteCount
|
|
154
|
+
});
|
|
155
|
+
return deleted;
|
|
156
|
+
}
|
|
157
|
+
copyWithin(target, start, end) {
|
|
158
|
+
const originalMode = this.mode;
|
|
159
|
+
let prev;
|
|
160
|
+
if (originalMode === `record`) {
|
|
161
|
+
prev = this.slice(target);
|
|
162
|
+
this.mode = `playback`;
|
|
163
|
+
}
|
|
164
|
+
super.copyWithin(target, start, end);
|
|
165
|
+
if (originalMode === `record`) this.mode = `record`;
|
|
166
|
+
if (prev) if (end === void 0) this.emit({
|
|
167
|
+
type: `copyWithin`,
|
|
168
|
+
prev,
|
|
169
|
+
target,
|
|
170
|
+
start
|
|
171
|
+
});
|
|
172
|
+
else this.emit({
|
|
173
|
+
type: `copyWithin`,
|
|
174
|
+
prev,
|
|
175
|
+
target,
|
|
176
|
+
start,
|
|
177
|
+
end
|
|
178
|
+
});
|
|
179
|
+
return this;
|
|
180
|
+
}
|
|
181
|
+
subscribe(key, fn) {
|
|
182
|
+
return this.subject.subscribe(key, fn);
|
|
183
|
+
}
|
|
184
|
+
emit(update) {
|
|
185
|
+
this.subject.next(update);
|
|
186
|
+
}
|
|
187
|
+
doStep(update) {
|
|
188
|
+
switch (update.type) {
|
|
189
|
+
case `copyWithin`:
|
|
190
|
+
this.copyWithin(update.target, update.start, update.end);
|
|
191
|
+
break;
|
|
192
|
+
case `extend`:
|
|
193
|
+
this.length = update.next;
|
|
194
|
+
break;
|
|
195
|
+
case `fill`:
|
|
196
|
+
this.fill(update.value, update.start, update.end);
|
|
197
|
+
break;
|
|
198
|
+
case `pop`:
|
|
199
|
+
this.pop();
|
|
200
|
+
break;
|
|
201
|
+
case `push`:
|
|
202
|
+
this.push(...update.items);
|
|
203
|
+
break;
|
|
204
|
+
case `reverse`:
|
|
205
|
+
this.reverse();
|
|
206
|
+
break;
|
|
207
|
+
case `shift`:
|
|
208
|
+
this.shift();
|
|
209
|
+
break;
|
|
210
|
+
case `sort`:
|
|
211
|
+
for (let i = 0; i < update.next.length; i++) this[i] = update.next[i];
|
|
212
|
+
this.length = update.next.length;
|
|
213
|
+
break;
|
|
214
|
+
case `splice`:
|
|
215
|
+
if (update.deleteCount !== void 0 && update.items) this.splice(update.start, update.deleteCount, ...update.items);
|
|
216
|
+
else this.splice(update.start);
|
|
217
|
+
break;
|
|
218
|
+
case `truncate`:
|
|
219
|
+
this.length = update.length;
|
|
220
|
+
break;
|
|
221
|
+
case `set`:
|
|
222
|
+
this[update.index] = update.next;
|
|
223
|
+
break;
|
|
224
|
+
case `unshift`:
|
|
225
|
+
this.unshift(...update.items);
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
do(update) {
|
|
230
|
+
this.mode = `playback`;
|
|
231
|
+
this.doStep(update);
|
|
232
|
+
this.mode = `record`;
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
undoStep(update) {
|
|
236
|
+
switch (update.type) {
|
|
237
|
+
case `copyWithin`:
|
|
238
|
+
for (let i = 0; i < update.prev.length; i++) this[i + update.target] = update.prev[i];
|
|
239
|
+
break;
|
|
240
|
+
case `extend`:
|
|
241
|
+
this.length = update.prev;
|
|
242
|
+
break;
|
|
243
|
+
case `fill`:
|
|
244
|
+
{
|
|
245
|
+
const start = update.start ?? 0;
|
|
246
|
+
for (let i = 0; i < update.prev.length; i++) this[i + start] = update.prev[i];
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
249
|
+
case `pop`:
|
|
250
|
+
if (update.value) this.push(update.value);
|
|
251
|
+
break;
|
|
252
|
+
case `push`:
|
|
253
|
+
{
|
|
254
|
+
let i = update.items.length - 1;
|
|
255
|
+
while (i >= 0) {
|
|
256
|
+
this.pop();
|
|
257
|
+
--i;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
case `reverse`:
|
|
262
|
+
this.reverse();
|
|
263
|
+
break;
|
|
264
|
+
case `shift`:
|
|
265
|
+
if (update.value) this.unshift(update.value);
|
|
266
|
+
break;
|
|
267
|
+
case `sort`:
|
|
268
|
+
for (let i = 0; i < update.prev.length; i++) this[i] = update.prev[i];
|
|
269
|
+
this.length = update.prev.length;
|
|
270
|
+
break;
|
|
271
|
+
case `set`:
|
|
272
|
+
if (update.prev) this[update.index] = update.prev;
|
|
273
|
+
else {
|
|
274
|
+
delete this[update.index];
|
|
275
|
+
const firstEmptyIndex = this.findIndex((_, i) => !Object.hasOwn(this, i));
|
|
276
|
+
if (firstEmptyIndex !== -1) this.length = firstEmptyIndex;
|
|
277
|
+
}
|
|
278
|
+
break;
|
|
279
|
+
case `splice`:
|
|
280
|
+
if (update.deleted) if (update.items) this.splice(update.start, update.items.length, ...update.deleted);
|
|
281
|
+
else this.splice(update.start, 0, ...update.deleted);
|
|
282
|
+
else if (update.items) this.splice(update.start, update.items.length);
|
|
283
|
+
break;
|
|
284
|
+
case `truncate`:
|
|
285
|
+
this.push(...update.items);
|
|
286
|
+
break;
|
|
287
|
+
case `unshift`:
|
|
288
|
+
{
|
|
289
|
+
let i = update.items.length - 1;
|
|
290
|
+
while (i >= 0) {
|
|
291
|
+
this.shift();
|
|
292
|
+
--i;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
undo(update) {
|
|
299
|
+
this.mode = `playback`;
|
|
300
|
+
this.undoStep(update);
|
|
301
|
+
this.mode = `record`;
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
//#endregion
|
|
307
|
+
export { OList };
|
|
308
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["prev: P | undefined","result: number","value: P | undefined","prev: P[] | undefined"],"sources":["../../../src/transceivers/o-list/o-list.ts"],"sourcesContent":["import type { Fn, Transceiver, TransceiverMode } from \"atom.io/internal\"\nimport { Subject } from \"atom.io/internal\"\nimport type { primitive } from \"atom.io/json\"\n\ntype ArrayMutations = Exclude<keyof Array<any>, keyof ReadonlyArray<any>>\nexport type ArrayUpdate<P extends primitive> =\n\t| {\n\t\t\ttype: `copyWithin`\n\t\t\ttarget: number\n\t\t\tstart: number\n\t\t\tend?: number\n\t\t\tprev: readonly P[]\n\t }\n\t| {\n\t\t\ttype: `extend`\n\t\t\tnext: number\n\t\t\tprev: number\n\t }\n\t| {\n\t\t\ttype: `fill`\n\t\t\tvalue: P\n\t\t\tstart?: number\n\t\t\tend?: number\n\t\t\tprev: readonly P[]\n\t }\n\t| {\n\t\t\ttype: `pop` | `shift`\n\t\t\tvalue?: P\n\t }\n\t| {\n\t\t\ttype: `push` | `unshift`\n\t\t\titems: readonly P[]\n\t }\n\t| {\n\t\t\ttype: `reverse`\n\t }\n\t| {\n\t\t\ttype: `set`\n\t\t\tnext: P\n\t\t\tprev?: P\n\t\t\tindex: number\n\t }\n\t| {\n\t\t\ttype: `sort`\n\t\t\tnext: readonly P[]\n\t\t\tprev: readonly P[]\n\t }\n\t| {\n\t\t\ttype: `splice`\n\t\t\tstart: number\n\t\t\tdeleteCount?: number\n\t\t\titems?: readonly P[]\n\t\t\tdeleted?: readonly P[]\n\t }\n\t| {\n\t\t\ttype: `truncate`\n\t\t\tlength: number\n\t\t\titems: readonly P[]\n\t }\nexport type OListUpdateType = ArrayUpdate<any>[`type`]\ntrue satisfies ArrayMutations extends OListUpdateType\n\t? true\n\t: Exclude<ArrayMutations, OListUpdateType>\n\nexport type ArrayMutationHandler = {\n\t[K in Exclude<OListUpdateType, `extend` | `set` | `truncate`>]: Fn\n}\n\nexport class OList<P extends primitive>\n\textends Array<P>\n\timplements\n\t\tTransceiver<ReadonlyArray<P>, ArrayUpdate<P>, ReadonlyArray<P>>,\n\t\tArrayMutationHandler\n{\n\tpublic mode: TransceiverMode = `record`\n\tpublic readonly subject: Subject<ArrayUpdate<P>> = new Subject<\n\t\tArrayUpdate<P>\n\t>()\n\n\tpublic readonly READONLY_VIEW: ReadonlyArray<P> = this\n\n\tpublic constructor(arrayLength?: number)\n\tpublic constructor(...items: P[])\n\tpublic constructor(...items: P[]) {\n\t\tsuper(...items)\n\t\t// biome-ignore lint/correctness/noConstructorReturn: this is chill\n\t\treturn new Proxy(this, {\n\t\t\tset: (target, prop, value, receiver) => {\n\t\t\t\tif (\n\t\t\t\t\ttypeof prop === `string` &&\n\t\t\t\t\t!Number.isNaN(Number.parseInt(prop, 10))\n\t\t\t\t) {\n\t\t\t\t\tconst index = Number(prop)\n\t\t\t\t\tlet prev: P | undefined\n\t\t\t\t\tif (this.mode === `record`) {\n\t\t\t\t\t\tprev = target[index]\n\t\t\t\t\t}\n\t\t\t\t\ttarget[index] = value\n\t\t\t\t\tif (prev) {\n\t\t\t\t\t\tthis.emit({ type: `set`, index, next: value, prev })\n\t\t\t\t\t} else if (this.mode === `record`) {\n\t\t\t\t\t\tthis.emit({ type: `set`, index, next: value })\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (prop === `length`) {\n\t\t\t\t\tif (this.mode === `record`) {\n\t\t\t\t\t\tconst prevLength = target.length\n\t\t\t\t\t\tif (prevLength === value) return true\n\t\t\t\t\t\tif (prevLength > value) {\n\t\t\t\t\t\t\tconst dropped = target.slice(value)\n\t\t\t\t\t\t\ttarget.length = value\n\t\t\t\t\t\t\tthis.emit({ type: `truncate`, length: value, items: dropped })\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\ttarget.length = value\n\t\t\t\t\t\t\tthis.emit({ type: `extend`, next: value, prev: prevLength })\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttarget.length = value\n\t\t\t\t\t}\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\treturn Reflect.set(target, prop, value, receiver)\n\t\t\t},\n\t\t})\n\t}\n\n\tpublic toJSON(): ReadonlyArray<P> {\n\t\treturn [...this]\n\t}\n\n\tpublic static fromJSON<P extends primitive>(json: ReadonlyArray<P>): OList<P> {\n\t\treturn new OList<P>(...json)\n\t}\n\n\tpublic push(...items: P[]): number {\n\t\tlet result: number\n\t\tif (this.mode === `record`) {\n\t\t\tthis.mode = `playback`\n\t\t\tresult = super.push(...items)\n\t\t\tthis.mode = `record`\n\t\t\tthis.emit({ type: `push`, items })\n\t\t} else {\n\t\t\tresult = super.push(...items)\n\t\t}\n\t\treturn result\n\t}\n\tpublic pop(): P | undefined {\n\t\tlet value: P | undefined\n\t\tif (this.mode === `record`) {\n\t\t\tthis.mode = `playback`\n\t\t\tvalue = super.pop()\n\t\t\tif (value === undefined) {\n\t\t\t\tthis.emit({ type: `pop` })\n\t\t\t} else {\n\t\t\t\tthis.emit({ type: `pop`, value })\n\t\t\t}\n\t\t\tthis.mode = `record`\n\t\t} else {\n\t\t\tvalue = super.pop()\n\t\t}\n\t\treturn value\n\t}\n\tpublic shift(): P | undefined {\n\t\tlet value: P | undefined\n\t\tif (this.mode === `record`) {\n\t\t\tthis.mode = `playback`\n\t\t\tvalue = super.shift()\n\t\t\tif (value === undefined) {\n\t\t\t\tthis.emit({ type: `shift` })\n\t\t\t} else {\n\t\t\t\tthis.emit({ type: `shift`, value })\n\t\t\t}\n\t\t\tthis.mode = `record`\n\t\t} else {\n\t\t\tvalue = super.shift()\n\t\t}\n\t\treturn value\n\t}\n\tpublic unshift(...items: P[]): number {\n\t\tlet result: number\n\t\tif (this.mode === `record`) {\n\t\t\tthis.mode = `playback`\n\t\t\tresult = super.unshift(...items)\n\t\t\tthis.emit({ type: `unshift`, items })\n\t\t\tthis.mode = `record`\n\t\t} else {\n\t\t\tresult = super.unshift(...items)\n\t\t}\n\t\treturn result\n\t}\n\n\tpublic reverse(): this {\n\t\tsuper.reverse()\n\t\tif (this.mode === `record`) {\n\t\t\tthis.emit({ type: `reverse` })\n\t\t}\n\t\treturn this\n\t}\n\n\tpublic sort(compareFn?: (a: P, b: P) => number): this {\n\t\tif (this.mode === `record`) {\n\t\t\tthis.mode = `playback`\n\t\t\tconst prev = [...this]\n\t\t\tsuper.sort(compareFn)\n\t\t\tconst next = [...this]\n\t\t\tthis.emit({ type: `sort`, next, prev })\n\t\t\tthis.mode = `record`\n\t\t}\n\t\treturn this\n\t}\n\n\tpublic splice(start: number, deleteCount?: number): P[]\n\tpublic splice(start: number, deleteCount: number, ...items: P[]): P[]\n\tpublic splice(\n\t\t...params: [start: number, deleteCount?: number, ...items: P[]]\n\t): P[] {\n\t\tconst [start, deleteCount, ...items] = params\n\t\tconst originalMode = this.mode\n\t\tif (originalMode === `record`) this.mode = `playback`\n\t\tconst deleted = super.splice(...(params as [number, number, ...P[]]))\n\t\tif (originalMode === `record`) this.mode = `record`\n\t\tif (deleteCount === undefined) {\n\t\t\tthis.emit({\n\t\t\t\ttype: `splice`,\n\t\t\t\tstart,\n\t\t\t\titems,\n\t\t\t\tdeleted,\n\t\t\t\tdeleteCount: deleted.length,\n\t\t\t})\n\t\t} else {\n\t\t\tthis.emit({ type: `splice`, start, items, deleted, deleteCount })\n\t\t}\n\n\t\treturn deleted\n\t}\n\n\tpublic copyWithin(target: number, start: number, end?: number): this {\n\t\tconst originalMode = this.mode\n\t\tlet prev: P[] | undefined\n\t\tif (originalMode === `record`) {\n\t\t\tprev = this.slice(target)\n\t\t\tthis.mode = `playback`\n\t\t}\n\t\tsuper.copyWithin(target, start, end)\n\t\tif (originalMode === `record`) this.mode = `record`\n\t\tif (prev) {\n\t\t\tif (end === undefined) {\n\t\t\t\tthis.emit({ type: `copyWithin`, prev, target, start })\n\t\t\t} else {\n\t\t\t\tthis.emit({ type: `copyWithin`, prev, target, start, end })\n\t\t\t}\n\t\t}\n\t\treturn this\n\t}\n\n\tpublic subscribe(\n\t\tkey: string,\n\t\tfn: (update: ArrayUpdate<P>) => void,\n\t): () => void {\n\t\treturn this.subject.subscribe(key, fn)\n\t}\n\n\tpublic emit(update: ArrayUpdate<P>): void {\n\t\tthis.subject.next(update)\n\t}\n\n\tprivate doStep(update: ArrayUpdate<P>): void {\n\t\tconst type = update.type\n\t\tswitch (type) {\n\t\t\tcase `copyWithin`:\n\t\t\t\tthis.copyWithin(update.target, update.start, update.end)\n\t\t\t\tbreak\n\t\t\tcase `extend`:\n\t\t\t\tthis.length = update.next\n\t\t\t\tbreak\n\t\t\tcase `fill`:\n\t\t\t\tthis.fill(update.value, update.start, update.end)\n\t\t\t\tbreak\n\t\t\tcase `pop`:\n\t\t\t\tthis.pop()\n\t\t\t\tbreak\n\t\t\tcase `push`:\n\t\t\t\tthis.push(...update.items)\n\t\t\t\tbreak\n\t\t\tcase `reverse`:\n\t\t\t\tthis.reverse()\n\t\t\t\tbreak\n\t\t\tcase `shift`:\n\t\t\t\tthis.shift()\n\t\t\t\tbreak\n\t\t\tcase `sort`:\n\t\t\t\tfor (let i = 0; i < update.next.length; i++) {\n\t\t\t\t\tthis[i] = update.next[i]\n\t\t\t\t}\n\t\t\t\tthis.length = update.next.length\n\t\t\t\tbreak\n\t\t\tcase `splice`:\n\t\t\t\tif (update.deleteCount !== undefined && update.items) {\n\t\t\t\t\tthis.splice(update.start, update.deleteCount, ...update.items)\n\t\t\t\t} else {\n\t\t\t\t\tthis.splice(update.start)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\tcase `truncate`:\n\t\t\t\tthis.length = update.length\n\t\t\t\tbreak\n\t\t\tcase `set`:\n\t\t\t\tthis[update.index] = update.next\n\t\t\t\tbreak\n\t\t\tcase `unshift`:\n\t\t\t\tthis.unshift(...update.items)\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tpublic do(update: ArrayUpdate<P>): null {\n\t\tthis.mode = `playback`\n\t\tthis.doStep(update)\n\t\tthis.mode = `record`\n\t\treturn null\n\t}\n\n\tpublic undoStep(update: ArrayUpdate<P>): void {\n\t\tswitch (update.type) {\n\t\t\tcase `copyWithin`:\n\t\t\t\tfor (let i = 0; i < update.prev.length; i++) {\n\t\t\t\t\tthis[i + update.target] = update.prev[i]\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\tcase `extend`:\n\t\t\t\tthis.length = update.prev\n\t\t\t\tbreak\n\t\t\tcase `fill`:\n\t\t\t\t{\n\t\t\t\t\tconst start = update.start ?? 0\n\t\t\t\t\tfor (let i = 0; i < update.prev.length; i++) {\n\t\t\t\t\t\tthis[i + start] = update.prev[i]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\tcase `pop`:\n\t\t\t\tif (update.value) this.push(update.value)\n\t\t\t\tbreak\n\t\t\tcase `push`:\n\t\t\t\t{\n\t\t\t\t\tlet i = update.items.length - 1\n\t\t\t\t\twhile (i >= 0) {\n\t\t\t\t\t\tthis.pop()\n\t\t\t\t\t\t--i\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\tcase `reverse`:\n\t\t\t\tthis.reverse()\n\t\t\t\tbreak\n\t\t\tcase `shift`:\n\t\t\t\tif (update.value) this.unshift(update.value)\n\t\t\t\tbreak\n\t\t\tcase `sort`:\n\t\t\t\tfor (let i = 0; i < update.prev.length; i++) {\n\t\t\t\t\tthis[i] = update.prev[i]\n\t\t\t\t}\n\t\t\t\tthis.length = update.prev.length\n\t\t\t\tbreak\n\t\t\tcase `set`:\n\t\t\t\tif (update.prev) {\n\t\t\t\t\tthis[update.index] = update.prev\n\t\t\t\t} else {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-dynamic-delete\n\t\t\t\t\tdelete this[update.index]\n\t\t\t\t\tconst firstEmptyIndex = this.findIndex(\n\t\t\t\t\t\t(_, i) => !Object.hasOwn(this, i),\n\t\t\t\t\t)\n\t\t\t\t\tif (firstEmptyIndex !== -1) {\n\t\t\t\t\t\tthis.length = firstEmptyIndex\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\tcase `splice`:\n\t\t\t\tif (update.deleted) {\n\t\t\t\t\tif (update.items) {\n\t\t\t\t\t\tthis.splice(update.start, update.items.length, ...update.deleted)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.splice(update.start, 0, ...update.deleted)\n\t\t\t\t\t}\n\t\t\t\t} else if (update.items) {\n\t\t\t\t\tthis.splice(update.start, update.items.length)\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\tcase `truncate`:\n\t\t\t\tthis.push(...update.items)\n\t\t\t\tbreak\n\t\t\tcase `unshift`:\n\t\t\t\t{\n\t\t\t\t\tlet i = update.items.length - 1\n\t\t\t\t\twhile (i >= 0) {\n\t\t\t\t\t\tthis.shift()\n\t\t\t\t\t\t--i\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\tpublic undo(update: ArrayUpdate<P>): number | null {\n\t\tthis.mode = `playback`\n\t\tthis.undoStep(update)\n\t\tthis.mode = `record`\n\t\treturn null\n\t}\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "atom.io",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.10",
|
|
4
4
|
"description": "Composable and testable reactive data library.",
|
|
5
5
|
"homepage": "https://atom.io.fyi",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@storybook/addon-onboarding": "9.1.5",
|
|
66
66
|
"@storybook/react-vite": "9.1.5",
|
|
67
67
|
"@testing-library/react": "16.3.0",
|
|
68
|
-
"@types/bun": "npm:bun-types@1.2.
|
|
68
|
+
"@types/bun": "npm:bun-types@1.2.22",
|
|
69
69
|
"@types/eslint": "9.6.1",
|
|
70
70
|
"@types/estree": "1.0.8",
|
|
71
71
|
"@types/http-proxy": "1.17.16",
|
|
@@ -87,10 +87,10 @@
|
|
|
87
87
|
"npmlog": "7.0.1",
|
|
88
88
|
"nyc": "17.1.0",
|
|
89
89
|
"postgres": "3.4.7",
|
|
90
|
-
"preact": "10.27.
|
|
90
|
+
"preact": "10.27.2",
|
|
91
91
|
"react": "19.1.1",
|
|
92
92
|
"react-dom": "19.1.1",
|
|
93
|
-
"react-router-dom": "7.9.
|
|
93
|
+
"react-router-dom": "7.9.1",
|
|
94
94
|
"recoverage": "0.1.11",
|
|
95
95
|
"socket.io": "4.8.1",
|
|
96
96
|
"socket.io-client": "4.8.1",
|
|
@@ -166,6 +166,10 @@
|
|
|
166
166
|
"import": "./dist/realtime-testing/index.js",
|
|
167
167
|
"types": "./dist/realtime-testing/index.d.ts"
|
|
168
168
|
},
|
|
169
|
+
"./transceivers/o-list": {
|
|
170
|
+
"import": "./dist/transceivers/o-list/index.js",
|
|
171
|
+
"types": "./dist/transceivers/o-list/index.d.ts"
|
|
172
|
+
},
|
|
169
173
|
"./transceivers/set-rtx": {
|
|
170
174
|
"import": "./dist/transceivers/set-rtx/index.js",
|
|
171
175
|
"types": "./dist/transceivers/set-rtx/index.d.ts"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./o-list"
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import type { Fn, Transceiver, TransceiverMode } from "atom.io/internal"
|
|
2
|
+
import { Subject } from "atom.io/internal"
|
|
3
|
+
import type { primitive } from "atom.io/json"
|
|
4
|
+
|
|
5
|
+
type ArrayMutations = Exclude<keyof Array<any>, keyof ReadonlyArray<any>>
|
|
6
|
+
export type ArrayUpdate<P extends primitive> =
|
|
7
|
+
| {
|
|
8
|
+
type: `copyWithin`
|
|
9
|
+
target: number
|
|
10
|
+
start: number
|
|
11
|
+
end?: number
|
|
12
|
+
prev: readonly P[]
|
|
13
|
+
}
|
|
14
|
+
| {
|
|
15
|
+
type: `extend`
|
|
16
|
+
next: number
|
|
17
|
+
prev: number
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
type: `fill`
|
|
21
|
+
value: P
|
|
22
|
+
start?: number
|
|
23
|
+
end?: number
|
|
24
|
+
prev: readonly P[]
|
|
25
|
+
}
|
|
26
|
+
| {
|
|
27
|
+
type: `pop` | `shift`
|
|
28
|
+
value?: P
|
|
29
|
+
}
|
|
30
|
+
| {
|
|
31
|
+
type: `push` | `unshift`
|
|
32
|
+
items: readonly P[]
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
type: `reverse`
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
type: `set`
|
|
39
|
+
next: P
|
|
40
|
+
prev?: P
|
|
41
|
+
index: number
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
type: `sort`
|
|
45
|
+
next: readonly P[]
|
|
46
|
+
prev: readonly P[]
|
|
47
|
+
}
|
|
48
|
+
| {
|
|
49
|
+
type: `splice`
|
|
50
|
+
start: number
|
|
51
|
+
deleteCount?: number
|
|
52
|
+
items?: readonly P[]
|
|
53
|
+
deleted?: readonly P[]
|
|
54
|
+
}
|
|
55
|
+
| {
|
|
56
|
+
type: `truncate`
|
|
57
|
+
length: number
|
|
58
|
+
items: readonly P[]
|
|
59
|
+
}
|
|
60
|
+
export type OListUpdateType = ArrayUpdate<any>[`type`]
|
|
61
|
+
true satisfies ArrayMutations extends OListUpdateType
|
|
62
|
+
? true
|
|
63
|
+
: Exclude<ArrayMutations, OListUpdateType>
|
|
64
|
+
|
|
65
|
+
export type ArrayMutationHandler = {
|
|
66
|
+
[K in Exclude<OListUpdateType, `extend` | `set` | `truncate`>]: Fn
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class OList<P extends primitive>
|
|
70
|
+
extends Array<P>
|
|
71
|
+
implements
|
|
72
|
+
Transceiver<ReadonlyArray<P>, ArrayUpdate<P>, ReadonlyArray<P>>,
|
|
73
|
+
ArrayMutationHandler
|
|
74
|
+
{
|
|
75
|
+
public mode: TransceiverMode = `record`
|
|
76
|
+
public readonly subject: Subject<ArrayUpdate<P>> = new Subject<
|
|
77
|
+
ArrayUpdate<P>
|
|
78
|
+
>()
|
|
79
|
+
|
|
80
|
+
public readonly READONLY_VIEW: ReadonlyArray<P> = this
|
|
81
|
+
|
|
82
|
+
public constructor(arrayLength?: number)
|
|
83
|
+
public constructor(...items: P[])
|
|
84
|
+
public constructor(...items: P[]) {
|
|
85
|
+
super(...items)
|
|
86
|
+
// biome-ignore lint/correctness/noConstructorReturn: this is chill
|
|
87
|
+
return new Proxy(this, {
|
|
88
|
+
set: (target, prop, value, receiver) => {
|
|
89
|
+
if (
|
|
90
|
+
typeof prop === `string` &&
|
|
91
|
+
!Number.isNaN(Number.parseInt(prop, 10))
|
|
92
|
+
) {
|
|
93
|
+
const index = Number(prop)
|
|
94
|
+
let prev: P | undefined
|
|
95
|
+
if (this.mode === `record`) {
|
|
96
|
+
prev = target[index]
|
|
97
|
+
}
|
|
98
|
+
target[index] = value
|
|
99
|
+
if (prev) {
|
|
100
|
+
this.emit({ type: `set`, index, next: value, prev })
|
|
101
|
+
} else if (this.mode === `record`) {
|
|
102
|
+
this.emit({ type: `set`, index, next: value })
|
|
103
|
+
}
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
if (prop === `length`) {
|
|
107
|
+
if (this.mode === `record`) {
|
|
108
|
+
const prevLength = target.length
|
|
109
|
+
if (prevLength === value) return true
|
|
110
|
+
if (prevLength > value) {
|
|
111
|
+
const dropped = target.slice(value)
|
|
112
|
+
target.length = value
|
|
113
|
+
this.emit({ type: `truncate`, length: value, items: dropped })
|
|
114
|
+
} else {
|
|
115
|
+
target.length = value
|
|
116
|
+
this.emit({ type: `extend`, next: value, prev: prevLength })
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
target.length = value
|
|
120
|
+
}
|
|
121
|
+
return true
|
|
122
|
+
}
|
|
123
|
+
return Reflect.set(target, prop, value, receiver)
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public toJSON(): ReadonlyArray<P> {
|
|
129
|
+
return [...this]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public static fromJSON<P extends primitive>(json: ReadonlyArray<P>): OList<P> {
|
|
133
|
+
return new OList<P>(...json)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public push(...items: P[]): number {
|
|
137
|
+
let result: number
|
|
138
|
+
if (this.mode === `record`) {
|
|
139
|
+
this.mode = `playback`
|
|
140
|
+
result = super.push(...items)
|
|
141
|
+
this.mode = `record`
|
|
142
|
+
this.emit({ type: `push`, items })
|
|
143
|
+
} else {
|
|
144
|
+
result = super.push(...items)
|
|
145
|
+
}
|
|
146
|
+
return result
|
|
147
|
+
}
|
|
148
|
+
public pop(): P | undefined {
|
|
149
|
+
let value: P | undefined
|
|
150
|
+
if (this.mode === `record`) {
|
|
151
|
+
this.mode = `playback`
|
|
152
|
+
value = super.pop()
|
|
153
|
+
if (value === undefined) {
|
|
154
|
+
this.emit({ type: `pop` })
|
|
155
|
+
} else {
|
|
156
|
+
this.emit({ type: `pop`, value })
|
|
157
|
+
}
|
|
158
|
+
this.mode = `record`
|
|
159
|
+
} else {
|
|
160
|
+
value = super.pop()
|
|
161
|
+
}
|
|
162
|
+
return value
|
|
163
|
+
}
|
|
164
|
+
public shift(): P | undefined {
|
|
165
|
+
let value: P | undefined
|
|
166
|
+
if (this.mode === `record`) {
|
|
167
|
+
this.mode = `playback`
|
|
168
|
+
value = super.shift()
|
|
169
|
+
if (value === undefined) {
|
|
170
|
+
this.emit({ type: `shift` })
|
|
171
|
+
} else {
|
|
172
|
+
this.emit({ type: `shift`, value })
|
|
173
|
+
}
|
|
174
|
+
this.mode = `record`
|
|
175
|
+
} else {
|
|
176
|
+
value = super.shift()
|
|
177
|
+
}
|
|
178
|
+
return value
|
|
179
|
+
}
|
|
180
|
+
public unshift(...items: P[]): number {
|
|
181
|
+
let result: number
|
|
182
|
+
if (this.mode === `record`) {
|
|
183
|
+
this.mode = `playback`
|
|
184
|
+
result = super.unshift(...items)
|
|
185
|
+
this.emit({ type: `unshift`, items })
|
|
186
|
+
this.mode = `record`
|
|
187
|
+
} else {
|
|
188
|
+
result = super.unshift(...items)
|
|
189
|
+
}
|
|
190
|
+
return result
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public reverse(): this {
|
|
194
|
+
super.reverse()
|
|
195
|
+
if (this.mode === `record`) {
|
|
196
|
+
this.emit({ type: `reverse` })
|
|
197
|
+
}
|
|
198
|
+
return this
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public sort(compareFn?: (a: P, b: P) => number): this {
|
|
202
|
+
if (this.mode === `record`) {
|
|
203
|
+
this.mode = `playback`
|
|
204
|
+
const prev = [...this]
|
|
205
|
+
super.sort(compareFn)
|
|
206
|
+
const next = [...this]
|
|
207
|
+
this.emit({ type: `sort`, next, prev })
|
|
208
|
+
this.mode = `record`
|
|
209
|
+
}
|
|
210
|
+
return this
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public splice(start: number, deleteCount?: number): P[]
|
|
214
|
+
public splice(start: number, deleteCount: number, ...items: P[]): P[]
|
|
215
|
+
public splice(
|
|
216
|
+
...params: [start: number, deleteCount?: number, ...items: P[]]
|
|
217
|
+
): P[] {
|
|
218
|
+
const [start, deleteCount, ...items] = params
|
|
219
|
+
const originalMode = this.mode
|
|
220
|
+
if (originalMode === `record`) this.mode = `playback`
|
|
221
|
+
const deleted = super.splice(...(params as [number, number, ...P[]]))
|
|
222
|
+
if (originalMode === `record`) this.mode = `record`
|
|
223
|
+
if (deleteCount === undefined) {
|
|
224
|
+
this.emit({
|
|
225
|
+
type: `splice`,
|
|
226
|
+
start,
|
|
227
|
+
items,
|
|
228
|
+
deleted,
|
|
229
|
+
deleteCount: deleted.length,
|
|
230
|
+
})
|
|
231
|
+
} else {
|
|
232
|
+
this.emit({ type: `splice`, start, items, deleted, deleteCount })
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return deleted
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
public copyWithin(target: number, start: number, end?: number): this {
|
|
239
|
+
const originalMode = this.mode
|
|
240
|
+
let prev: P[] | undefined
|
|
241
|
+
if (originalMode === `record`) {
|
|
242
|
+
prev = this.slice(target)
|
|
243
|
+
this.mode = `playback`
|
|
244
|
+
}
|
|
245
|
+
super.copyWithin(target, start, end)
|
|
246
|
+
if (originalMode === `record`) this.mode = `record`
|
|
247
|
+
if (prev) {
|
|
248
|
+
if (end === undefined) {
|
|
249
|
+
this.emit({ type: `copyWithin`, prev, target, start })
|
|
250
|
+
} else {
|
|
251
|
+
this.emit({ type: `copyWithin`, prev, target, start, end })
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return this
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public subscribe(
|
|
258
|
+
key: string,
|
|
259
|
+
fn: (update: ArrayUpdate<P>) => void,
|
|
260
|
+
): () => void {
|
|
261
|
+
return this.subject.subscribe(key, fn)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
public emit(update: ArrayUpdate<P>): void {
|
|
265
|
+
this.subject.next(update)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private doStep(update: ArrayUpdate<P>): void {
|
|
269
|
+
const type = update.type
|
|
270
|
+
switch (type) {
|
|
271
|
+
case `copyWithin`:
|
|
272
|
+
this.copyWithin(update.target, update.start, update.end)
|
|
273
|
+
break
|
|
274
|
+
case `extend`:
|
|
275
|
+
this.length = update.next
|
|
276
|
+
break
|
|
277
|
+
case `fill`:
|
|
278
|
+
this.fill(update.value, update.start, update.end)
|
|
279
|
+
break
|
|
280
|
+
case `pop`:
|
|
281
|
+
this.pop()
|
|
282
|
+
break
|
|
283
|
+
case `push`:
|
|
284
|
+
this.push(...update.items)
|
|
285
|
+
break
|
|
286
|
+
case `reverse`:
|
|
287
|
+
this.reverse()
|
|
288
|
+
break
|
|
289
|
+
case `shift`:
|
|
290
|
+
this.shift()
|
|
291
|
+
break
|
|
292
|
+
case `sort`:
|
|
293
|
+
for (let i = 0; i < update.next.length; i++) {
|
|
294
|
+
this[i] = update.next[i]
|
|
295
|
+
}
|
|
296
|
+
this.length = update.next.length
|
|
297
|
+
break
|
|
298
|
+
case `splice`:
|
|
299
|
+
if (update.deleteCount !== undefined && update.items) {
|
|
300
|
+
this.splice(update.start, update.deleteCount, ...update.items)
|
|
301
|
+
} else {
|
|
302
|
+
this.splice(update.start)
|
|
303
|
+
}
|
|
304
|
+
break
|
|
305
|
+
case `truncate`:
|
|
306
|
+
this.length = update.length
|
|
307
|
+
break
|
|
308
|
+
case `set`:
|
|
309
|
+
this[update.index] = update.next
|
|
310
|
+
break
|
|
311
|
+
case `unshift`:
|
|
312
|
+
this.unshift(...update.items)
|
|
313
|
+
break
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
public do(update: ArrayUpdate<P>): null {
|
|
318
|
+
this.mode = `playback`
|
|
319
|
+
this.doStep(update)
|
|
320
|
+
this.mode = `record`
|
|
321
|
+
return null
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
public undoStep(update: ArrayUpdate<P>): void {
|
|
325
|
+
switch (update.type) {
|
|
326
|
+
case `copyWithin`:
|
|
327
|
+
for (let i = 0; i < update.prev.length; i++) {
|
|
328
|
+
this[i + update.target] = update.prev[i]
|
|
329
|
+
}
|
|
330
|
+
break
|
|
331
|
+
case `extend`:
|
|
332
|
+
this.length = update.prev
|
|
333
|
+
break
|
|
334
|
+
case `fill`:
|
|
335
|
+
{
|
|
336
|
+
const start = update.start ?? 0
|
|
337
|
+
for (let i = 0; i < update.prev.length; i++) {
|
|
338
|
+
this[i + start] = update.prev[i]
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
break
|
|
342
|
+
case `pop`:
|
|
343
|
+
if (update.value) this.push(update.value)
|
|
344
|
+
break
|
|
345
|
+
case `push`:
|
|
346
|
+
{
|
|
347
|
+
let i = update.items.length - 1
|
|
348
|
+
while (i >= 0) {
|
|
349
|
+
this.pop()
|
|
350
|
+
--i
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
break
|
|
354
|
+
case `reverse`:
|
|
355
|
+
this.reverse()
|
|
356
|
+
break
|
|
357
|
+
case `shift`:
|
|
358
|
+
if (update.value) this.unshift(update.value)
|
|
359
|
+
break
|
|
360
|
+
case `sort`:
|
|
361
|
+
for (let i = 0; i < update.prev.length; i++) {
|
|
362
|
+
this[i] = update.prev[i]
|
|
363
|
+
}
|
|
364
|
+
this.length = update.prev.length
|
|
365
|
+
break
|
|
366
|
+
case `set`:
|
|
367
|
+
if (update.prev) {
|
|
368
|
+
this[update.index] = update.prev
|
|
369
|
+
} else {
|
|
370
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
371
|
+
delete this[update.index]
|
|
372
|
+
const firstEmptyIndex = this.findIndex(
|
|
373
|
+
(_, i) => !Object.hasOwn(this, i),
|
|
374
|
+
)
|
|
375
|
+
if (firstEmptyIndex !== -1) {
|
|
376
|
+
this.length = firstEmptyIndex
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
break
|
|
380
|
+
case `splice`:
|
|
381
|
+
if (update.deleted) {
|
|
382
|
+
if (update.items) {
|
|
383
|
+
this.splice(update.start, update.items.length, ...update.deleted)
|
|
384
|
+
} else {
|
|
385
|
+
this.splice(update.start, 0, ...update.deleted)
|
|
386
|
+
}
|
|
387
|
+
} else if (update.items) {
|
|
388
|
+
this.splice(update.start, update.items.length)
|
|
389
|
+
}
|
|
390
|
+
break
|
|
391
|
+
case `truncate`:
|
|
392
|
+
this.push(...update.items)
|
|
393
|
+
break
|
|
394
|
+
case `unshift`:
|
|
395
|
+
{
|
|
396
|
+
let i = update.items.length - 1
|
|
397
|
+
while (i >= 0) {
|
|
398
|
+
this.shift()
|
|
399
|
+
--i
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
break
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
public undo(update: ArrayUpdate<P>): number | null {
|
|
407
|
+
this.mode = `playback`
|
|
408
|
+
this.undoStep(update)
|
|
409
|
+
this.mode = `record`
|
|
410
|
+
return null
|
|
411
|
+
}
|
|
412
|
+
}
|