atom.io 0.40.9 → 0.41.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.
Files changed (32) hide show
  1. package/dist/eslint-plugin/index.js +1 -1
  2. package/dist/internal/index.d.ts +11 -2
  3. package/dist/internal/index.d.ts.map +1 -1
  4. package/dist/internal/index.js +34 -1
  5. package/dist/internal/index.js.map +1 -1
  6. package/dist/realtime/index.d.ts +2 -1
  7. package/dist/realtime/index.d.ts.map +1 -1
  8. package/dist/realtime/index.js.map +1 -1
  9. package/dist/realtime-client/index.js +2 -1
  10. package/dist/realtime-client/index.js.map +1 -1
  11. package/dist/realtime-server/index.js +1 -1
  12. package/dist/realtime-server/index.js.map +1 -1
  13. package/dist/transceivers/o-list/index.d.ts +87 -0
  14. package/dist/transceivers/o-list/index.d.ts.map +1 -0
  15. package/dist/transceivers/o-list/index.js +513 -0
  16. package/dist/transceivers/o-list/index.js.map +1 -0
  17. package/dist/transceivers/set-rtx/index.d.ts +16 -16
  18. package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
  19. package/dist/transceivers/set-rtx/index.js.map +1 -1
  20. package/dist/transceivers/u-list/index.d.ts +27 -18
  21. package/dist/transceivers/u-list/index.d.ts.map +1 -1
  22. package/dist/transceivers/u-list/index.js +54 -34
  23. package/dist/transceivers/u-list/index.js.map +1 -1
  24. package/package.json +17 -13
  25. package/src/internal/index.ts +1 -0
  26. package/src/internal/micro.ts +69 -0
  27. package/src/realtime/shared-room-store.ts +3 -2
  28. package/src/realtime-server/ipc-sockets/parent-socket.ts +1 -1
  29. package/src/transceivers/o-list/index.ts +1 -0
  30. package/src/transceivers/o-list/o-list.ts +613 -0
  31. package/src/transceivers/set-rtx/set-rtx.ts +22 -21
  32. package/src/transceivers/u-list/u-list.ts +85 -55
@@ -0,0 +1,613 @@
1
+ import type {
2
+ Enumeration,
3
+ Fn,
4
+ Transceiver,
5
+ TransceiverMode,
6
+ } from "atom.io/internal"
7
+ import { enumeration, packValue, Subject, unpackValue } from "atom.io/internal"
8
+ import type { primitive } from "atom.io/json"
9
+
10
+ export type ArrayMutations = Exclude<keyof Array<any>, keyof ReadonlyArray<any>>
11
+ export type ArrayUpdate<P extends primitive> =
12
+ | {
13
+ type: `copyWithin`
14
+ target: number
15
+ start: number
16
+ end?: number
17
+ prev: readonly P[]
18
+ }
19
+ | {
20
+ type: `extend`
21
+ next: number
22
+ prev: number
23
+ }
24
+ | {
25
+ type: `fill`
26
+ value: P
27
+ start?: number
28
+ end?: number
29
+ prev: readonly P[]
30
+ }
31
+ | {
32
+ type: `pop` | `shift`
33
+ value?: P
34
+ }
35
+ | {
36
+ type: `push` | `unshift`
37
+ items: readonly P[]
38
+ }
39
+ | {
40
+ type: `reverse`
41
+ }
42
+ | {
43
+ type: `set`
44
+ next: P
45
+ prev?: P
46
+ index: number
47
+ }
48
+ | {
49
+ type: `sort`
50
+ next: readonly P[]
51
+ prev: readonly P[]
52
+ }
53
+ | {
54
+ type: `splice`
55
+ start: number
56
+ deleteCount: number
57
+ items: readonly P[]
58
+ deleted: readonly P[]
59
+ }
60
+ | {
61
+ type: `truncate`
62
+ length: number
63
+ items: readonly P[]
64
+ }
65
+ export type OListUpdateType = ArrayUpdate<any>[`type`]
66
+ true satisfies ArrayMutations extends OListUpdateType
67
+ ? true
68
+ : Exclude<ArrayMutations, OListUpdateType>
69
+
70
+ export type PackedArrayUpdate<P extends primitive> = string & {
71
+ update?: ArrayUpdate<P>
72
+ type?: P
73
+ }
74
+
75
+ const ARRAY_UPDATES = [
76
+ // virtual methods
77
+ `set`,
78
+ `truncate`,
79
+ `extend`,
80
+ // actual methods
81
+ `pop`,
82
+ `push`,
83
+ `shift`,
84
+ `unshift`,
85
+ `copyWithin`,
86
+ `fill`,
87
+ `splice`,
88
+ `reverse`,
89
+ `sort`,
90
+ ] as const
91
+ true satisfies ArrayUpdate<any>[`type`] extends (typeof ARRAY_UPDATES)[number]
92
+ ? true
93
+ : Exclude<ArrayUpdate<any>[`type`], (typeof ARRAY_UPDATES)[number]>
94
+
95
+ export const ARRAY_UPDATE_ENUM: Enumeration<typeof ARRAY_UPDATES> =
96
+ enumeration(ARRAY_UPDATES)
97
+
98
+ export function packArrayUpdate<P extends primitive>(
99
+ update: ArrayUpdate<P>,
100
+ ): PackedArrayUpdate<P> {
101
+ let packed = ARRAY_UPDATE_ENUM[update.type] + `\u001F`
102
+ switch (update.type) {
103
+ case `set`:
104
+ packed += update.index + `\u001E` + packValue(update.next)
105
+ if (update.prev !== undefined) {
106
+ packed += `\u001E` + packValue(update.prev)
107
+ }
108
+ return packed
109
+ case `truncate`:
110
+ return (
111
+ packed +
112
+ update.length +
113
+ `\u001E` +
114
+ update.items.map(packValue).join(`\u001E`)
115
+ )
116
+ case `extend`:
117
+ return packed + update.next + `\u001E` + update.prev
118
+ case `pop`:
119
+ case `shift`:
120
+ if (update.value !== undefined) {
121
+ packed += packValue(update.value)
122
+ }
123
+ return packed
124
+ case `push`:
125
+ case `unshift`:
126
+ return packed + update.items.map(packValue).join(`\u001E`)
127
+ case `copyWithin`:
128
+ packed += update.target + `\u001E` + update.start
129
+ if (update.end !== undefined) {
130
+ packed += `\u001E` + update.end
131
+ }
132
+ packed += `\u001E\u001E` + update.prev.map(packValue).join(`\u001E`)
133
+ return packed
134
+ case `fill`:
135
+ packed += packValue(update.value)
136
+ if (update.start !== undefined) {
137
+ packed += `\u001E` + update.start
138
+ }
139
+ if (update.end !== undefined) {
140
+ packed += `\u001E` + update.end
141
+ }
142
+ packed += `\u001E\u001E` + update.prev.map(packValue).join(`\u001E`)
143
+ return packed
144
+ case `splice`:
145
+ return (
146
+ packed +
147
+ update.start +
148
+ `\u001E\u001E` +
149
+ update.deleteCount +
150
+ `\u001E\u001E` +
151
+ update.items.map(packValue).join(`\u001E`) +
152
+ `\u001E\u001E` +
153
+ update.deleted.map(packValue).join(`\u001E`)
154
+ )
155
+ case `reverse`:
156
+ return packed
157
+ case `sort`:
158
+ return (
159
+ packed +
160
+ update.next.map(packValue).join(`\u001E`) +
161
+ `\u001E\u001E` +
162
+ update.prev.map(packValue).join(`\u001E`)
163
+ )
164
+ }
165
+ }
166
+
167
+ export function unpackArrayUpdate<P extends primitive>(
168
+ packed: PackedArrayUpdate<P>,
169
+ ): ArrayUpdate<P> {
170
+ const [head, tail] = packed.split(`\u001F`) as [
171
+ Extract<keyof typeof ARRAY_UPDATE_ENUM, number>,
172
+ string,
173
+ ]
174
+ const type = ARRAY_UPDATE_ENUM[head]
175
+ switch (type) {
176
+ case `set`: {
177
+ const [i, n, p] = tail.split(`\u001E`)
178
+ const index = +i
179
+ const next = unpackValue(n) as P
180
+ if (p === undefined) {
181
+ return { type, index, next }
182
+ }
183
+ const prev = unpackValue(p) as P
184
+ return { type, index, next, prev }
185
+ }
186
+ case `truncate`: {
187
+ const [l, ...i] = tail.split(`\u001E`)
188
+ const length = +l
189
+ const items = i.map(unpackValue) as P[]
190
+ return { type, length, items }
191
+ }
192
+ case `extend`: {
193
+ const [n, p] = tail.split(`\u001E`)
194
+ const next = +n
195
+ const prev = +p
196
+ return { type, next, prev }
197
+ }
198
+ case `pop`:
199
+ case `shift`:
200
+ if (tail !== ``) {
201
+ const value = unpackValue(tail) as P
202
+ return { type, value }
203
+ }
204
+ return { type }
205
+ case `push`:
206
+ case `unshift`: {
207
+ const items = tail.split(`\u001E`).map(unpackValue) as P[]
208
+ return { type, items }
209
+ }
210
+ case `copyWithin`: {
211
+ const [numbers, data] = tail.split(`\u001E\u001E`)
212
+ const prev = data ? (data.split(`\u001E`).map(unpackValue) as P[]) : []
213
+ const [t, s, e] = numbers.split(`\u001E`)
214
+ const target = +t
215
+ const start = +s
216
+ if (e === undefined) {
217
+ return { type, target, start, prev }
218
+ }
219
+ const end = +e
220
+ return { type, target, start, prev, end }
221
+ }
222
+ case `fill`: {
223
+ const [numbers, data] = tail.split(`\u001E\u001E`)
224
+ const prev = data ? (data.split(`\u001E`).map(unpackValue) as P[]) : []
225
+ const [v, s, e] = numbers.split(`\u001E`)
226
+ const value = unpackValue(v) as P
227
+ if (s === undefined && e === undefined) {
228
+ return { type, value, prev }
229
+ }
230
+ const start = +s
231
+ if (e === undefined) {
232
+ return { type, value, prev, start }
233
+ }
234
+ const end = +e
235
+ return { type, value, prev, start, end }
236
+ }
237
+ case `splice`: {
238
+ const [s, c, i, d] = tail.split(`\u001E\u001E`)
239
+
240
+ const start = +s
241
+ const deleteCount = +c
242
+ const items = i ? (i.split(`\u001E`).map(unpackValue) as P[]) : []
243
+ const deleted = d ? (d.split(`\u001E`).map(unpackValue) as P[]) : []
244
+ return { type, start, deleteCount, items, deleted }
245
+ }
246
+ case `reverse`:
247
+ return { type }
248
+ case `sort`: {
249
+ const [n, p] = tail.split(`\u001E\u001E`)
250
+ const next = n ? (n.split(`\u001E`).map(unpackValue) as P[]) : []
251
+ const prev = p ? (p.split(`\u001E`).map(unpackValue) as P[]) : []
252
+ return { type, next, prev }
253
+ }
254
+ }
255
+ }
256
+
257
+ export type ArrayMutationHandler = {
258
+ [K in Exclude<OListUpdateType, `extend` | `set` | `truncate`>]: Fn
259
+ }
260
+
261
+ export class OList<P extends primitive>
262
+ extends Array<P>
263
+ implements
264
+ Transceiver<ReadonlyArray<P>, PackedArrayUpdate<P>, ReadonlyArray<P>>,
265
+ ArrayMutationHandler
266
+ {
267
+ public mode: TransceiverMode = `record`
268
+ public readonly subject: Subject<PackedArrayUpdate<P>> = new Subject()
269
+
270
+ public readonly READONLY_VIEW: ReadonlyArray<P> = this
271
+
272
+ public constructor(arrayLength?: number)
273
+ public constructor(...items: P[])
274
+ public constructor(...items: P[]) {
275
+ super(...items)
276
+ // biome-ignore lint/correctness/noConstructorReturn: this is chill
277
+ return new Proxy(this, {
278
+ set: (target, prop, value, receiver) => {
279
+ if (
280
+ typeof prop === `string` &&
281
+ !Number.isNaN(Number.parseInt(prop, 10))
282
+ ) {
283
+ const index = Number(prop)
284
+ let prev: P | undefined
285
+ if (this.mode === `record`) {
286
+ prev = target[index]
287
+ }
288
+ target[index] = value
289
+ if (prev) {
290
+ this.emit({ type: `set`, index, next: value, prev })
291
+ } else if (this.mode === `record`) {
292
+ this.emit({ type: `set`, index, next: value })
293
+ }
294
+ return true
295
+ }
296
+ if (prop === `length`) {
297
+ if (this.mode === `record`) {
298
+ const prevLength = target.length
299
+ if (prevLength === value) return true
300
+ if (prevLength > value) {
301
+ const dropped = target.slice(value)
302
+ target.length = value
303
+ this.emit({ type: `truncate`, length: value, items: dropped })
304
+ } else {
305
+ target.length = value
306
+ this.emit({ type: `extend`, next: value, prev: prevLength })
307
+ }
308
+ } else {
309
+ target.length = value
310
+ }
311
+ return true
312
+ }
313
+ return Reflect.set(target, prop, value, receiver)
314
+ },
315
+ })
316
+ }
317
+
318
+ public toJSON(): ReadonlyArray<P> {
319
+ return [...this]
320
+ }
321
+
322
+ public static fromJSON<P extends primitive>(json: ReadonlyArray<P>): OList<P> {
323
+ return new OList<P>(...json)
324
+ }
325
+
326
+ public push(...items: P[]): number {
327
+ let result: number
328
+ if (this.mode === `record`) {
329
+ this.mode = `playback`
330
+ result = super.push(...items)
331
+ this.mode = `record`
332
+ this.emit({ type: `push`, items })
333
+ } else {
334
+ result = super.push(...items)
335
+ }
336
+ return result
337
+ }
338
+ public pop(): P | undefined {
339
+ let value: P | undefined
340
+ if (this.mode === `record`) {
341
+ this.mode = `playback`
342
+ value = super.pop()
343
+ if (value === undefined) {
344
+ this.emit({ type: `pop` })
345
+ } else {
346
+ this.emit({ type: `pop`, value })
347
+ }
348
+ this.mode = `record`
349
+ } else {
350
+ value = super.pop()
351
+ }
352
+ return value
353
+ }
354
+ public shift(): P | undefined {
355
+ let value: P | undefined
356
+ if (this.mode === `record`) {
357
+ this.mode = `playback`
358
+ value = super.shift()
359
+ if (value === undefined) {
360
+ this.emit({ type: `shift` })
361
+ } else {
362
+ this.emit({ type: `shift`, value })
363
+ }
364
+ this.mode = `record`
365
+ } else {
366
+ value = super.shift()
367
+ }
368
+ return value
369
+ }
370
+ public unshift(...items: P[]): number {
371
+ let result: number
372
+ if (this.mode === `record`) {
373
+ this.mode = `playback`
374
+ result = super.unshift(...items)
375
+ this.emit({ type: `unshift`, items })
376
+ this.mode = `record`
377
+ } else {
378
+ result = super.unshift(...items)
379
+ }
380
+ return result
381
+ }
382
+
383
+ public reverse(): this {
384
+ super.reverse()
385
+ if (this.mode === `record`) {
386
+ this.emit({ type: `reverse` })
387
+ }
388
+ return this
389
+ }
390
+
391
+ public fill(value: P, start?: number, end?: number): this {
392
+ if (this.mode === `record`) {
393
+ this.mode = `playback`
394
+ const prev = this.slice(start, end)
395
+ super.fill(value, start, end)
396
+ if (start === undefined) {
397
+ this.emit({ type: `fill`, value, prev })
398
+ } else {
399
+ if (end === undefined) {
400
+ this.emit({ type: `fill`, value, start, prev })
401
+ } else {
402
+ this.emit({ type: `fill`, value, start, end, prev })
403
+ }
404
+ }
405
+ this.mode = `record`
406
+ } else {
407
+ super.fill(value, start, end)
408
+ }
409
+ return this
410
+ }
411
+
412
+ public sort(compareFn?: (a: P, b: P) => number): this {
413
+ if (this.mode === `record`) {
414
+ this.mode = `playback`
415
+ const prev = [...this]
416
+ super.sort(compareFn)
417
+ const next = [...this]
418
+ this.emit({ type: `sort`, next, prev })
419
+ this.mode = `record`
420
+ }
421
+ return this
422
+ }
423
+
424
+ public splice(start: number, deleteCount?: number): P[]
425
+ public splice(start: number, deleteCount: number, ...items: P[]): P[]
426
+ public splice(
427
+ ...params: [start: number, deleteCount?: number, ...items: P[]]
428
+ ): P[] {
429
+ const [start, deleteCount, ...items] = params
430
+ const originalMode = this.mode
431
+ if (originalMode === `record`) this.mode = `playback`
432
+ const deleted = super.splice(...(params as [number, number, ...P[]]))
433
+ if (originalMode === `record`) this.mode = `record`
434
+ if (deleteCount === undefined) {
435
+ this.emit({
436
+ type: `splice`,
437
+ start,
438
+ items,
439
+ deleted,
440
+ deleteCount: deleted.length,
441
+ })
442
+ } else {
443
+ this.emit({ type: `splice`, start, items, deleted, deleteCount })
444
+ }
445
+
446
+ return deleted
447
+ }
448
+
449
+ public copyWithin(target: number, start: number, end?: number): this {
450
+ const originalMode = this.mode
451
+ let prev: P[] | undefined
452
+ if (originalMode === `record`) {
453
+ prev = this.slice(target)
454
+ this.mode = `playback`
455
+ }
456
+ super.copyWithin(target, start, end)
457
+ if (originalMode === `record`) this.mode = `record`
458
+ if (prev) {
459
+ if (end === undefined) {
460
+ this.emit({ type: `copyWithin`, prev, target, start })
461
+ } else {
462
+ this.emit({ type: `copyWithin`, prev, target, start, end })
463
+ }
464
+ }
465
+ return this
466
+ }
467
+
468
+ public subscribe(
469
+ key: string,
470
+ fn: (update: PackedArrayUpdate<P>) => void,
471
+ ): () => void {
472
+ return this.subject.subscribe(key, fn)
473
+ }
474
+
475
+ public emit(update: ArrayUpdate<P>): void {
476
+ this.subject.next(packArrayUpdate(update))
477
+ }
478
+
479
+ private doStep(update: ArrayUpdate<P>): void {
480
+ const type = update.type
481
+ switch (type) {
482
+ case `copyWithin`:
483
+ this.copyWithin(update.target, update.start, update.end)
484
+ break
485
+ case `extend`:
486
+ this.length = update.next
487
+ break
488
+ case `fill`:
489
+ this.fill(update.value, update.start, update.end)
490
+ break
491
+ case `pop`:
492
+ this.pop()
493
+ break
494
+ case `push`:
495
+ this.push(...update.items)
496
+ break
497
+ case `reverse`:
498
+ this.reverse()
499
+ break
500
+ case `shift`:
501
+ this.shift()
502
+ break
503
+ case `sort`:
504
+ for (let i = 0; i < update.next.length; i++) {
505
+ this[i] = update.next[i]
506
+ }
507
+ this.length = update.next.length
508
+ break
509
+ case `splice`:
510
+ this.splice(update.start, update.deleteCount, ...update.items)
511
+ break
512
+ case `truncate`:
513
+ this.length = update.length
514
+ break
515
+ case `set`:
516
+ this[update.index] = update.next
517
+ break
518
+ case `unshift`:
519
+ this.unshift(...update.items)
520
+ break
521
+ }
522
+ }
523
+
524
+ public do(update: PackedArrayUpdate<P>): null {
525
+ this.mode = `playback`
526
+ const unpacked = unpackArrayUpdate(update)
527
+ this.doStep(unpacked)
528
+ this.mode = `record`
529
+ return null
530
+ }
531
+
532
+ public undoStep(update: ArrayUpdate<P>): void {
533
+ switch (update.type) {
534
+ case `copyWithin`:
535
+ for (let i = 0; i < update.prev.length; i++) {
536
+ this[i + update.target] = update.prev[i]
537
+ }
538
+ break
539
+ case `extend`:
540
+ this.length = update.prev
541
+ break
542
+ case `fill`:
543
+ {
544
+ const start = update.start ?? 0
545
+ for (let i = 0; i < update.prev.length; i++) {
546
+ this[i + start] = update.prev[i]
547
+ }
548
+ }
549
+ break
550
+ case `pop`:
551
+ if (update.value) this.push(update.value)
552
+ break
553
+ case `push`:
554
+ {
555
+ let i = update.items.length - 1
556
+ while (i >= 0) {
557
+ this.pop()
558
+ --i
559
+ }
560
+ }
561
+ break
562
+ case `reverse`:
563
+ this.reverse()
564
+ break
565
+ case `shift`:
566
+ if (update.value) this.unshift(update.value)
567
+ break
568
+ case `sort`:
569
+ for (let i = 0; i < update.prev.length; i++) {
570
+ this[i] = update.prev[i]
571
+ }
572
+ this.length = update.prev.length
573
+ break
574
+ case `set`:
575
+ if (update.prev) {
576
+ this[update.index] = update.prev
577
+ } else {
578
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
579
+ delete this[update.index]
580
+ const firstEmptyIndex = this.findIndex(
581
+ (_, i) => !Object.hasOwn(this, i),
582
+ )
583
+ if (firstEmptyIndex !== -1) {
584
+ this.length = firstEmptyIndex
585
+ }
586
+ }
587
+ break
588
+ case `splice`:
589
+ this.splice(update.start, update.items.length, ...update.deleted)
590
+ break
591
+ case `truncate`:
592
+ this.push(...update.items)
593
+ break
594
+ case `unshift`:
595
+ {
596
+ let i = update.items.length - 1
597
+ while (i >= 0) {
598
+ this.shift()
599
+ --i
600
+ }
601
+ }
602
+ break
603
+ }
604
+ }
605
+
606
+ public undo(update: PackedArrayUpdate<P>): number | null {
607
+ this.mode = `playback`
608
+ const unpacked = unpackArrayUpdate(update)
609
+ this.undoStep(unpacked)
610
+ this.mode = `record`
611
+ return null
612
+ }
613
+ }