atom.io 0.42.0 → 0.42.2
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/internal/index.d.ts +9 -9
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +46 -36
- package/dist/internal/index.js.map +1 -1
- package/dist/main/index.d.ts +34 -20
- package/dist/main/index.d.ts.map +1 -1
- package/dist/main/index.js +32 -6
- package/dist/main/index.js.map +1 -1
- package/dist/transceivers/o-list/index.d.ts +6 -1
- package/dist/transceivers/o-list/index.d.ts.map +1 -1
- package/dist/transceivers/o-list/index.js +88 -2
- package/dist/transceivers/o-list/index.js.map +1 -1
- package/dist/transceivers/u-list/index.d.ts +5 -1
- package/dist/transceivers/u-list/index.d.ts.map +1 -1
- package/dist/transceivers/u-list/index.js +33 -2
- package/dist/transceivers/u-list/index.js.map +1 -1
- package/package.json +17 -17
- package/src/internal/atom/create-regular-atom.ts +3 -1
- package/src/internal/join/join-internal.ts +18 -47
- package/src/internal/molecule.ts +44 -25
- package/src/internal/mutable/create-mutable-atom.ts +15 -11
- package/src/internal/store/store.ts +15 -14
- package/src/internal/transaction/build-transaction.ts +1 -1
- package/src/main/atom.ts +15 -6
- package/src/main/realm.ts +54 -13
- package/src/transceivers/o-list/index.ts +1 -0
- package/src/transceivers/o-list/o-list-disposed-key-cleanup-effect.ts +153 -0
- package/src/transceivers/u-list/index.ts +1 -0
- package/src/transceivers/u-list/u-list-disposed-key-cleanup-effect.ts +47 -0
|
@@ -5,7 +5,7 @@ import type {
|
|
|
5
5
|
UpdateHandler,
|
|
6
6
|
} from "atom.io"
|
|
7
7
|
|
|
8
|
-
import { newest } from "../lineage"
|
|
8
|
+
import { eldest, newest } from "../lineage"
|
|
9
9
|
import { createStandaloneSelector } from "../selector"
|
|
10
10
|
import { resetInStore, setIntoStore } from "../set-state"
|
|
11
11
|
import type { MutableAtom } from "../state-types"
|
|
@@ -58,6 +58,18 @@ export function createMutableAtom<T extends Transceiver<any, any, any>>(
|
|
|
58
58
|
}
|
|
59
59
|
target.atoms.set(newAtom.key, newAtom)
|
|
60
60
|
const token = deposit(newAtom)
|
|
61
|
+
|
|
62
|
+
new Tracker(token, store)
|
|
63
|
+
if (!family) {
|
|
64
|
+
createStandaloneSelector(store, {
|
|
65
|
+
key: `${key}:JSON`,
|
|
66
|
+
get: ({ get }) => get(token).toJSON(),
|
|
67
|
+
set: ({ set }, newValue) => {
|
|
68
|
+
set(token, options.class.fromJSON(newValue))
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
if (options.effects) {
|
|
62
74
|
let effectIndex = 0
|
|
63
75
|
const cleanupFunctions: (() => void)[] = []
|
|
@@ -71,6 +83,8 @@ export function createMutableAtom<T extends Transceiver<any, any, any>>(
|
|
|
71
83
|
},
|
|
72
84
|
onSet: (handle: UpdateHandler<T>) =>
|
|
73
85
|
subscribeToState(store, token, `effect[${effectIndex}]`, handle),
|
|
86
|
+
token: token as any,
|
|
87
|
+
store: eldest(store),
|
|
74
88
|
})
|
|
75
89
|
if (cleanup) {
|
|
76
90
|
cleanupFunctions.push(cleanup)
|
|
@@ -84,16 +98,6 @@ export function createMutableAtom<T extends Transceiver<any, any, any>>(
|
|
|
84
98
|
}
|
|
85
99
|
}
|
|
86
100
|
|
|
87
|
-
new Tracker(token, store)
|
|
88
|
-
if (!family) {
|
|
89
|
-
createStandaloneSelector(store, {
|
|
90
|
-
key: `${key}:JSON`,
|
|
91
|
-
get: ({ get }) => get(token).toJSON(),
|
|
92
|
-
set: ({ set }, newValue) => {
|
|
93
|
-
set(token, options.class.fromJSON(newValue))
|
|
94
|
-
},
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
101
|
store.on.atomCreation.next(token)
|
|
98
102
|
return token
|
|
99
103
|
}
|
|
@@ -107,20 +107,7 @@ export class Store implements Lineage {
|
|
|
107
107
|
new CircularBuffer(100)
|
|
108
108
|
|
|
109
109
|
public molecules: Map<string, Molecule<Canonical>> = new Map()
|
|
110
|
-
|
|
111
|
-
`moleculeKey`,
|
|
112
|
-
stringified<Canonical>,
|
|
113
|
-
`joinKey`,
|
|
114
|
-
string
|
|
115
|
-
> = new Junction(
|
|
116
|
-
{
|
|
117
|
-
between: [`moleculeKey`, `joinKey`],
|
|
118
|
-
cardinality: `n:n`,
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
makeContentKey: (...keys) => keys.sort().join(`:`),
|
|
122
|
-
},
|
|
123
|
-
)
|
|
110
|
+
|
|
124
111
|
public moleculeGraph: Junction<
|
|
125
112
|
`upstreamMoleculeKey`,
|
|
126
113
|
stringified<Canonical> | `"root"`,
|
|
@@ -150,6 +137,20 @@ export class Store implements Lineage {
|
|
|
150
137
|
makeContentKey: (...keys) => keys.sort().join(`:`),
|
|
151
138
|
},
|
|
152
139
|
)
|
|
140
|
+
public keyRefsInJoins: Junction<
|
|
141
|
+
`moleculeKey`,
|
|
142
|
+
stringified<Canonical>,
|
|
143
|
+
`joinKey`,
|
|
144
|
+
string
|
|
145
|
+
> = new Junction(
|
|
146
|
+
{
|
|
147
|
+
between: [`moleculeKey`, `joinKey`],
|
|
148
|
+
cardinality: `n:n`,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
makeContentKey: (...keys) => keys.sort().join(`:`),
|
|
152
|
+
},
|
|
153
|
+
)
|
|
153
154
|
public miscResources: Map<string, Disposable> = new Map()
|
|
154
155
|
|
|
155
156
|
public on: StoreEventCarrier = {
|
|
@@ -54,7 +54,7 @@ export const buildTransaction = (
|
|
|
54
54
|
molecules: new MapOverlay(parent.molecules),
|
|
55
55
|
moleculeGraph: parent.moleculeGraph.overlay(),
|
|
56
56
|
moleculeData: parent.moleculeData.overlay(),
|
|
57
|
-
|
|
57
|
+
keyRefsInJoins: parent.keyRefsInJoins.overlay(),
|
|
58
58
|
miscResources: new MapOverlay(parent.miscResources),
|
|
59
59
|
}
|
|
60
60
|
const epoch = getEpochNumberOfAction(store, token.key)
|
package/src/main/atom.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConstructorOf, Ctor, Transceiver } from "atom.io/internal"
|
|
1
|
+
import type { ConstructorOf, Ctor, Store, Transceiver } from "atom.io/internal"
|
|
2
2
|
import {
|
|
3
3
|
createMutableAtom,
|
|
4
4
|
createMutableAtomFamily,
|
|
@@ -10,6 +10,7 @@ import type { Canonical } from "atom.io/json"
|
|
|
10
10
|
|
|
11
11
|
import type { StateUpdate } from "./events"
|
|
12
12
|
import type {
|
|
13
|
+
AtomToken,
|
|
13
14
|
MutableAtomFamilyToken,
|
|
14
15
|
MutableAtomToken,
|
|
15
16
|
RegularAtomFamilyToken,
|
|
@@ -22,7 +23,7 @@ export type RegularAtomOptions<T, E = never> = {
|
|
|
22
23
|
/** The starting value of the atom */
|
|
23
24
|
default: T | (() => T)
|
|
24
25
|
/** Hooks used to run side effects when the atom is set */
|
|
25
|
-
effects?: readonly AtomEffect<T>[]
|
|
26
|
+
effects?: readonly AtomEffect<T, E>[]
|
|
26
27
|
/** The classes of errors that might be thrown when deriving the atom's default value */
|
|
27
28
|
catch?: readonly Ctor<E>[]
|
|
28
29
|
}
|
|
@@ -67,8 +68,10 @@ export function mutableAtom<T extends Transceiver<any, any, any>>(
|
|
|
67
68
|
* @returns
|
|
68
69
|
* Optionally, a cleanup function that will be called when the atom is disposed
|
|
69
70
|
*/
|
|
70
|
-
export type AtomEffect<T
|
|
71
|
-
|
|
71
|
+
export type AtomEffect<T, E = never> = (
|
|
72
|
+
tools: Effectors<T, E>,
|
|
73
|
+
) => (() => void) | void
|
|
74
|
+
export type Effectors<T, E = never> = {
|
|
72
75
|
/**
|
|
73
76
|
* Reset the value of the atom to its default
|
|
74
77
|
*/
|
|
@@ -79,7 +82,13 @@ export type Effectors<T> = {
|
|
|
79
82
|
*/
|
|
80
83
|
setSelf: <New extends T>(next: New | ((old: T) => New)) => void
|
|
81
84
|
/** Subscribe to changes to the atom */
|
|
82
|
-
onSet: (callback: (options: StateUpdate<T>) => void) => void
|
|
85
|
+
onSet: (callback: (options: StateUpdate<E | T>) => void) => void
|
|
86
|
+
/** The token of the atom */
|
|
87
|
+
token: T extends Transceiver<any, any, any>
|
|
88
|
+
? MutableAtomToken<T>
|
|
89
|
+
: AtomToken<T, any, E>
|
|
90
|
+
/** The store in which the atom exists */
|
|
91
|
+
store: Store
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
export type RegularAtomFamilyOptions<T, K extends Canonical, E = never> = {
|
|
@@ -88,7 +97,7 @@ export type RegularAtomFamilyOptions<T, K extends Canonical, E = never> = {
|
|
|
88
97
|
/** The starting value of the atom family */
|
|
89
98
|
default: T | ((key: K) => T)
|
|
90
99
|
/** Hooks used to run side effects when an atom in the family is set */
|
|
91
|
-
effects?: (key: K) => AtomEffect<T>[]
|
|
100
|
+
effects?: (key: K) => AtomEffect<T, E>[]
|
|
92
101
|
/** The classes of errors that might be thrown when deriving the atom's default value */
|
|
93
102
|
catch?: readonly Ctor<E>[]
|
|
94
103
|
}
|
package/src/main/realm.ts
CHANGED
|
@@ -14,16 +14,20 @@ import type { Canonical } from "atom.io/json"
|
|
|
14
14
|
|
|
15
15
|
import type { TransactionToken } from "./tokens"
|
|
16
16
|
|
|
17
|
-
export const $
|
|
18
|
-
export type
|
|
17
|
+
export const $validatedKey: unique symbol = Symbol.for(`claim`)
|
|
18
|
+
export type ValidKey<K extends Canonical> = K & { [$validatedKey]?: true }
|
|
19
|
+
|
|
20
|
+
export function simpleCompound(a: string, b: string): string {
|
|
21
|
+
return [a, b].sort().join(`\u001F`)
|
|
22
|
+
}
|
|
19
23
|
|
|
20
24
|
export class Realm<H extends Hierarchy> {
|
|
21
25
|
public store: RootStore
|
|
22
|
-
public deallocateTX: TransactionToken<(claim:
|
|
26
|
+
public deallocateTX: TransactionToken<(claim: ValidKey<Vassal<H>>) => void>
|
|
23
27
|
public claimTX: TransactionToken<
|
|
24
28
|
<V extends Exclude<Vassal<H>, CompoundTypedKey>, A extends Above<V, H>>(
|
|
25
29
|
newProvenance: A,
|
|
26
|
-
claim:
|
|
30
|
+
claim: ValidKey<V>,
|
|
27
31
|
exclusive?: `exclusive`,
|
|
28
32
|
) => void
|
|
29
33
|
>
|
|
@@ -42,13 +46,13 @@ export class Realm<H extends Hierarchy> {
|
|
|
42
46
|
* @param key - A unique identifier for the new subject
|
|
43
47
|
* @param attachmentStyle - The attachment style of new subject to its owner(s). `any` means that if any owners remain, the subject will be retained. `all` means that the subject be retained only if all owners remain .
|
|
44
48
|
* @returns
|
|
45
|
-
* The subject's key, given status as a true {@link
|
|
49
|
+
* The subject's key, given status as a true {@link ValidKey}
|
|
46
50
|
*/
|
|
47
51
|
public allocate<V extends Vassal<H>, A extends Above<V, H>>(
|
|
48
52
|
provenance: A,
|
|
49
53
|
key: V,
|
|
50
54
|
attachmentStyle?: `all` | `any`,
|
|
51
|
-
):
|
|
55
|
+
): ValidKey<V> {
|
|
52
56
|
return allocateIntoStore<H, V, A>(
|
|
53
57
|
this.store,
|
|
54
58
|
provenance,
|
|
@@ -62,7 +66,7 @@ export class Realm<H extends Hierarchy> {
|
|
|
62
66
|
* @param reagentA - the left reagent of the compound
|
|
63
67
|
* @param reagentB - the right reagent of the compound
|
|
64
68
|
* @returns
|
|
65
|
-
* The compound's key, given status as a
|
|
69
|
+
* The compound's key, given status as a {@link ValidKey}
|
|
66
70
|
*/
|
|
67
71
|
public fuse<
|
|
68
72
|
C extends CompoundFrom<H>,
|
|
@@ -73,14 +77,14 @@ export class Realm<H extends Hierarchy> {
|
|
|
73
77
|
type: T,
|
|
74
78
|
reagentA: SingularTypedKey<A>,
|
|
75
79
|
reagentB: SingularTypedKey<B>,
|
|
76
|
-
):
|
|
80
|
+
): ValidKey<CompoundTypedKey<T, A, B>> {
|
|
77
81
|
return fuseWithinStore<H, C, T, A, B>(this.store, type, reagentA, reagentB)
|
|
78
82
|
}
|
|
79
83
|
/**
|
|
80
84
|
* Remove a subject from the realm
|
|
81
85
|
* @param claim - The subject to be deallocated
|
|
82
86
|
*/
|
|
83
|
-
public deallocate<V extends Vassal<H>>(claim:
|
|
87
|
+
public deallocate<V extends Vassal<H>>(claim: ValidKey<V>): void {
|
|
84
88
|
actUponStore(this.store, this.deallocateTX, arbitrary())(claim)
|
|
85
89
|
}
|
|
86
90
|
/**
|
|
@@ -89,12 +93,12 @@ export class Realm<H extends Hierarchy> {
|
|
|
89
93
|
* @param claim - The subject to be claimed
|
|
90
94
|
* @param exclusive - Whether the subjects previous owners should be detached from it
|
|
91
95
|
* @returns
|
|
92
|
-
* The subject's key, given status as a true {@link
|
|
96
|
+
* The subject's key, given status as a true {@link ValidKey}
|
|
93
97
|
*/
|
|
94
98
|
public claim<
|
|
95
99
|
V extends Exclude<Vassal<H>, CompoundTypedKey>,
|
|
96
100
|
A extends Above<V, H>,
|
|
97
|
-
>(newProvenance: A, claim:
|
|
101
|
+
>(newProvenance: A, claim: ValidKey<V>, exclusive?: `exclusive`): void {
|
|
98
102
|
actUponStore(this.store, this.claimTX, arbitrary())(
|
|
99
103
|
newProvenance,
|
|
100
104
|
claim,
|
|
@@ -157,10 +161,47 @@ export class Anarchy {
|
|
|
157
161
|
): void {
|
|
158
162
|
claimWithinStore<any, any, any>(this.store, newProvenance, key, exclusive)
|
|
159
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Fuse two reagents into a compound
|
|
166
|
+
* @param type - the name of the compound that is being fused
|
|
167
|
+
* @param reagentA - the left reagent of the compound
|
|
168
|
+
* @param reagentB - the right reagent of the compound
|
|
169
|
+
* @returns
|
|
170
|
+
* The compound's key, given status as a {@link ValidKey}
|
|
171
|
+
*/
|
|
172
|
+
public fuse<T extends string, A extends string, B extends string>(
|
|
173
|
+
type: T,
|
|
174
|
+
reagentA: SingularTypedKey<A>,
|
|
175
|
+
reagentB: SingularTypedKey<B>,
|
|
176
|
+
): ValidKey<CompoundTypedKey<T, A, B>> {
|
|
177
|
+
return fuseWithinStore<any, any, T, A, B>(
|
|
178
|
+
this.store,
|
|
179
|
+
type,
|
|
180
|
+
reagentA,
|
|
181
|
+
reagentB,
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function decomposeCompound(
|
|
187
|
+
compound: Canonical,
|
|
188
|
+
): [type: string, a: string, b: string] | null {
|
|
189
|
+
if ((typeof compound === `string`) === false) {
|
|
190
|
+
return null
|
|
191
|
+
}
|
|
192
|
+
const [typeTag, components] = compound.split(`==`)
|
|
193
|
+
if (!components) {
|
|
194
|
+
return null
|
|
195
|
+
}
|
|
196
|
+
const type = typeTag.slice(4)
|
|
197
|
+
const [a, b] = components.split(`++`)
|
|
198
|
+
if (type && a && b) {
|
|
199
|
+
return [type, a, b]
|
|
200
|
+
}
|
|
201
|
+
return null
|
|
160
202
|
}
|
|
161
203
|
|
|
162
|
-
export
|
|
163
|
-
export type T$ = typeof T$
|
|
204
|
+
export type T$ = `T$`
|
|
164
205
|
export type TypeTag<T extends string> = `${T$}--${T}`
|
|
165
206
|
export type SingularTypedKey<T extends string = string> = `${T}::${string}`
|
|
166
207
|
export type CompoundTypedKey<
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { AtomEffect } from "atom.io"
|
|
2
|
+
import { getFromStore, getUpdateToken, subscribeInStore } from "atom.io/internal"
|
|
3
|
+
import { type primitive, stringifyJson } from "atom.io/json"
|
|
4
|
+
|
|
5
|
+
import { OList } from "./o-list"
|
|
6
|
+
|
|
7
|
+
export function filterOutInPlace<T>(arr: T[], toRemove: T): T[] {
|
|
8
|
+
let writeIndex = 0
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/prefer-for-of
|
|
11
|
+
for (let readIndex = 0; readIndex < arr.length; readIndex++) {
|
|
12
|
+
if (toRemove !== arr[readIndex]) {
|
|
13
|
+
arr[writeIndex] = arr[readIndex]
|
|
14
|
+
writeIndex++
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
arr.length = writeIndex
|
|
19
|
+
return arr
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const oListDisposedKeyCleanupEffect: AtomEffect<OList<primitive>> = ({
|
|
23
|
+
token,
|
|
24
|
+
setSelf,
|
|
25
|
+
store,
|
|
26
|
+
}) => {
|
|
27
|
+
const disposalSubscriptions = new Map<primitive, () => void>()
|
|
28
|
+
const updateToken = getUpdateToken(token)
|
|
29
|
+
|
|
30
|
+
const addedValues = new Set<primitive>()
|
|
31
|
+
const removedValues = new Set<primitive>()
|
|
32
|
+
function updateSubscriptions() {
|
|
33
|
+
for (const addedValue of addedValues) {
|
|
34
|
+
const molecule = store.molecules.get(stringifyJson(addedValue))
|
|
35
|
+
if (molecule) {
|
|
36
|
+
disposalSubscriptions.set(
|
|
37
|
+
addedValue,
|
|
38
|
+
molecule.subject.subscribe(token.key, () => {
|
|
39
|
+
disposalSubscriptions.get(addedValue)?.()
|
|
40
|
+
disposalSubscriptions.delete(addedValue)
|
|
41
|
+
setSelf((self) => {
|
|
42
|
+
filterOutInPlace(self, addedValue)
|
|
43
|
+
return self
|
|
44
|
+
})
|
|
45
|
+
}),
|
|
46
|
+
)
|
|
47
|
+
} else {
|
|
48
|
+
store.logger.warn(
|
|
49
|
+
`❌`,
|
|
50
|
+
token.type,
|
|
51
|
+
token.key,
|
|
52
|
+
`Added "${addedValue}" to ${token.key} but it has not been allocated.`,
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const removedValue of removedValues) {
|
|
57
|
+
if (disposalSubscriptions.has(removedValue)) {
|
|
58
|
+
disposalSubscriptions.get(removedValue)?.()
|
|
59
|
+
disposalSubscriptions.delete(removedValue)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
subscribeInStore(
|
|
64
|
+
store,
|
|
65
|
+
updateToken,
|
|
66
|
+
function manageAutoDeletionTriggers({ newValue }) {
|
|
67
|
+
const currentList = getFromStore(store, token)
|
|
68
|
+
const unpacked = OList.unpackUpdate(newValue)
|
|
69
|
+
switch (unpacked.type) {
|
|
70
|
+
case `extend`:
|
|
71
|
+
case `reverse`:
|
|
72
|
+
case `sort`:
|
|
73
|
+
break // these don't change what values are present in the list
|
|
74
|
+
|
|
75
|
+
case `set`:
|
|
76
|
+
{
|
|
77
|
+
const { next } = unpacked
|
|
78
|
+
if (`prev` in unpacked && !currentList.includes(unpacked.prev)) {
|
|
79
|
+
removedValues.add(unpacked.prev)
|
|
80
|
+
}
|
|
81
|
+
if (!disposalSubscriptions.has(next)) {
|
|
82
|
+
addedValues.add(next)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
break
|
|
86
|
+
case `truncate`:
|
|
87
|
+
{
|
|
88
|
+
for (const item of unpacked.items) {
|
|
89
|
+
if (!currentList.includes(item)) {
|
|
90
|
+
removedValues.add(item)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
break
|
|
95
|
+
case `shift`:
|
|
96
|
+
case `pop`:
|
|
97
|
+
{
|
|
98
|
+
const { value } = unpacked
|
|
99
|
+
if (value !== undefined && !currentList.includes(value)) {
|
|
100
|
+
removedValues.add(value)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
break
|
|
104
|
+
case `push`:
|
|
105
|
+
case `unshift`:
|
|
106
|
+
for (const item of unpacked.items) {
|
|
107
|
+
if (!disposalSubscriptions.has(item)) {
|
|
108
|
+
addedValues.add(item)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
break
|
|
112
|
+
case `copyWithin`:
|
|
113
|
+
for (const item of unpacked.prev) {
|
|
114
|
+
if (!currentList.includes(item)) {
|
|
115
|
+
removedValues.add(item)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
case `fill`:
|
|
120
|
+
{
|
|
121
|
+
const { value } = unpacked
|
|
122
|
+
if (value !== undefined) {
|
|
123
|
+
if (!disposalSubscriptions.has(value)) {
|
|
124
|
+
addedValues.add(value)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const item of unpacked.prev) {
|
|
128
|
+
if (!currentList.includes(item)) {
|
|
129
|
+
removedValues.add(item)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
break
|
|
134
|
+
case `splice`:
|
|
135
|
+
for (const item of unpacked.deleted) {
|
|
136
|
+
if (!currentList.includes(item)) {
|
|
137
|
+
removedValues.add(item)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (const addedItem of unpacked.items) {
|
|
141
|
+
if (!disposalSubscriptions.has(addedItem)) {
|
|
142
|
+
addedValues.add(addedItem)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
break
|
|
146
|
+
}
|
|
147
|
+
updateSubscriptions()
|
|
148
|
+
addedValues.clear()
|
|
149
|
+
removedValues.clear()
|
|
150
|
+
},
|
|
151
|
+
`set-auto-deletion-triggers`,
|
|
152
|
+
)
|
|
153
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { AtomEffect } from "atom.io"
|
|
2
|
+
import { getUpdateToken, subscribeInStore } from "atom.io/internal"
|
|
3
|
+
import { type primitive, stringifyJson } from "atom.io/json"
|
|
4
|
+
|
|
5
|
+
import { UList } from "./u-list"
|
|
6
|
+
|
|
7
|
+
export const uListDisposedKeyCleanupEffect: AtomEffect<UList<primitive>> = ({
|
|
8
|
+
token,
|
|
9
|
+
setSelf,
|
|
10
|
+
store,
|
|
11
|
+
}) => {
|
|
12
|
+
const disposalSubscriptions = new Map<primitive, () => void>()
|
|
13
|
+
const updateToken = getUpdateToken(token)
|
|
14
|
+
subscribeInStore(
|
|
15
|
+
store,
|
|
16
|
+
updateToken,
|
|
17
|
+
function setAutoDeletionTriggers({ newValue }) {
|
|
18
|
+
const unpacked = UList.unpackUpdate(newValue)
|
|
19
|
+
switch (unpacked.type) {
|
|
20
|
+
case `add`:
|
|
21
|
+
{
|
|
22
|
+
const molecule = store.molecules.get(stringifyJson(unpacked.value))
|
|
23
|
+
if (molecule) {
|
|
24
|
+
disposalSubscriptions.set(
|
|
25
|
+
unpacked.value,
|
|
26
|
+
molecule.subject.subscribe(token.key, () => {
|
|
27
|
+
setSelf((self) => {
|
|
28
|
+
self.delete(unpacked.value)
|
|
29
|
+
return self
|
|
30
|
+
})
|
|
31
|
+
}),
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
break
|
|
36
|
+
case `delete`:
|
|
37
|
+
disposalSubscriptions.get(unpacked.value)?.()
|
|
38
|
+
disposalSubscriptions.delete(unpacked.value)
|
|
39
|
+
break
|
|
40
|
+
case `clear`:
|
|
41
|
+
for (const unsub of disposalSubscriptions.values()) unsub()
|
|
42
|
+
disposalSubscriptions.clear()
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
`set-auto-deletion-triggers`,
|
|
46
|
+
)
|
|
47
|
+
}
|