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.
@@ -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.9",
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.21",
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.1",
90
+ "preact": "10.27.2",
91
91
  "react": "19.1.1",
92
92
  "react-dom": "19.1.1",
93
- "react-router-dom": "7.9.0",
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
+ }