atom.io 0.29.5 → 0.30.1

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 (44) hide show
  1. package/data/dist/index.d.ts +84 -51
  2. package/data/dist/index.js +35 -31
  3. package/data/src/join.ts +240 -134
  4. package/dist/chunk-ADMEAXYU.js +167 -0
  5. package/dist/{chunk-TCINPEYE.js → chunk-SMKF3ZNG.js} +221 -137
  6. package/dist/index.d.ts +75 -10
  7. package/dist/index.js +3 -17
  8. package/internal/dist/index.d.ts +76 -41
  9. package/internal/dist/index.js +2 -1
  10. package/internal/src/atom/dispose-atom.ts +4 -8
  11. package/internal/src/index.ts +1 -1
  12. package/internal/src/ingest-updates/ingest-creation-disposal.ts +71 -27
  13. package/internal/src/junction.ts +152 -84
  14. package/internal/src/molecule/create-molecule-family.ts +2 -2
  15. package/internal/src/molecule/dispose-molecule.ts +4 -2
  16. package/internal/src/molecule/make-molecule-in-store.ts +11 -9
  17. package/internal/src/molecule/molecule-internal.ts +12 -8
  18. package/internal/src/mutable/create-mutable-atom-family.ts +2 -2
  19. package/internal/src/mutable/get-json-family.ts +2 -2
  20. package/internal/src/store/store.ts +10 -2
  21. package/internal/src/timeline/create-timeline.ts +99 -71
  22. package/internal/src/transaction/index.ts +1 -1
  23. package/internal/src/utility-types.ts +9 -2
  24. package/json/dist/index.d.ts +3 -3
  25. package/json/dist/index.js +2 -1
  26. package/json/src/entries.ts +3 -3
  27. package/package.json +15 -15
  28. package/react-devtools/dist/index.js +9 -5
  29. package/react-devtools/src/TimelineIndex.tsx +4 -1
  30. package/react-devtools/src/Updates.tsx +18 -3
  31. package/realtime/dist/index.d.ts +1 -1
  32. package/realtime/dist/index.js +3 -1
  33. package/realtime/src/shared-room-store.ts +2 -0
  34. package/realtime-server/dist/index.d.ts +13 -4
  35. package/realtime-server/dist/index.js +4 -2
  36. package/realtime-server/src/realtime-continuity-synchronizer.ts +2 -2
  37. package/realtime-server/src/realtime-server-stores/server-user-store.ts +17 -1
  38. package/realtime-testing/dist/index.d.ts +3 -0
  39. package/realtime-testing/dist/index.js +11 -4
  40. package/realtime-testing/src/setup-realtime-test.tsx +12 -4
  41. package/src/allocate.ts +277 -0
  42. package/src/index.ts +1 -0
  43. package/src/molecule.ts +9 -5
  44. package/src/transaction.ts +22 -4
@@ -3,6 +3,7 @@ import * as http from "node:http"
3
3
  import type { RenderResult } from "@testing-library/react"
4
4
  import { prettyDOM, render } from "@testing-library/react"
5
5
  import * as AtomIO from "atom.io"
6
+ import { realm } from "atom.io"
6
7
  import { editRelationsInStore, findRelationsInStore } from "atom.io/data"
7
8
  import type { Store } from "atom.io/internal"
8
9
  import {
@@ -44,6 +45,7 @@ function prefixLogger(store: Store, prefix: string) {
44
45
 
45
46
  export type TestSetupOptions = {
46
47
  port: number
48
+ immortal?: { server?: boolean }
47
49
  server: (tools: {
48
50
  socket: SocketIO.Socket
49
51
  silo: AtomIO.Silo
@@ -97,9 +99,13 @@ export const setupRealtimeTestServer = (
97
99
  ): RealtimeTestServer => {
98
100
  ++testNumber
99
101
  const silo = new AtomIO.Silo(
100
- { name: `SERVER-${testNumber}`, lifespan: `ephemeral` },
102
+ {
103
+ name: `SERVER-${testNumber}`,
104
+ lifespan: options.immortal?.server ? `immortal` : `ephemeral`,
105
+ },
101
106
  IMPLICIT.STORE,
102
107
  )
108
+ const socketRealm = realm<RTS.SocketSystemHierarchy>(silo.store)
103
109
 
104
110
  const httpServer = http.createServer((_, res) => res.end(`Hello World!`))
105
111
  const address = httpServer.listen(options.port).address()
@@ -110,12 +116,14 @@ export const setupRealtimeTestServer = (
110
116
  const server = new SocketIO.Server(httpServer).use((socket, next) => {
111
117
  const { token, username } = socket.handshake.auth
112
118
  if (token === `test` && socket.id) {
113
- const socketState = findInStore(silo.store, RTS.socketAtoms, socket.id)
119
+ const userClaim = socketRealm.allocate(`root`, `user::${username}`)
120
+ const socketClaim = socketRealm.allocate(`root`, `socket::${socket.id}`)
121
+ const socketState = findInStore(silo.store, RTS.socketAtoms, socketClaim)
114
122
  setIntoStore(silo.store, socketState, socket)
115
123
  editRelationsInStore(
116
124
  RTS.usersOfSockets,
117
125
  (relations) => {
118
- relations.set(socket.id, username)
126
+ relations.set(userClaim, socketClaim)
119
127
  },
120
128
  silo.store,
121
129
  )
@@ -133,7 +141,7 @@ export const setupRealtimeTestServer = (
133
141
  function enableLogging() {
134
142
  const userKeyState = findRelationsInStore(
135
143
  RTS.usersOfSockets,
136
- socket.id,
144
+ `socket::${socket.id}`,
137
145
  silo.store,
138
146
  ).userKeyOfSocket
139
147
  userKey = getFromStore(silo.store, userKeyState)
@@ -0,0 +1,277 @@
1
+ import type { Each, Store } from "atom.io/internal"
2
+ import {
3
+ disposeFromStore,
4
+ isChildStore,
5
+ Molecule,
6
+ newest,
7
+ } from "atom.io/internal"
8
+ import type { Canonical } from "atom.io/json"
9
+ import { stringifyJson } from "atom.io/json"
10
+
11
+ import { makeRootMoleculeInStore } from "./molecule"
12
+ import type {
13
+ MoleculeCreationModern,
14
+ MoleculeDisposalModern,
15
+ } from "./transaction"
16
+
17
+ export const $provenance = Symbol(`provenance`)
18
+ export type Claim<
19
+ H extends Hierarchy,
20
+ V extends Vassal<H>,
21
+ A extends Above<V, H>,
22
+ > = V & {
23
+ [$provenance]?: A
24
+ }
25
+
26
+ export function allocateIntoStore<
27
+ H extends Hierarchy,
28
+ V extends Vassal<H>,
29
+ A extends Above<V, H>,
30
+ >(store: Store, provenance: A, key: V): Claim<H, V, A> {
31
+ const stringKey = stringifyJson(key)
32
+ try {
33
+ const above: Molecule<any>[] = []
34
+
35
+ let allocationAttachmentStyle: `all` | `any`
36
+ if (provenance === `root`) {
37
+ // biome-ignore lint/style/noNonNullAssertion: let's assume we made the root molecule to get here
38
+ above.push(store.molecules.get(`"root"`)!)
39
+ allocationAttachmentStyle = `all`
40
+ } else if (typeof provenance === `string` && provenance.startsWith(T$)) {
41
+ allocationAttachmentStyle = `any`
42
+ const provenanceKey = stringifyJson(provenance as Canonical)
43
+ const provenanceMolecule = store.molecules.get(provenanceKey)
44
+ if (!provenanceMolecule) {
45
+ throw new Error(
46
+ `Molecule ${provenanceKey} not found in store "${store.config.name}"`,
47
+ )
48
+ }
49
+ above.push(provenanceMolecule)
50
+ } else {
51
+ const allocationIsCompound = key.startsWith(`T$--`)
52
+ if (allocationIsCompound) {
53
+ allocationAttachmentStyle = `all`
54
+ for (const claim of provenance as SingularTypedKey[]) {
55
+ const provenanceKey = stringifyJson(claim)
56
+ const provenanceMolecule = store.molecules.get(provenanceKey)
57
+ if (!provenanceMolecule) {
58
+ throw new Error(
59
+ `Molecule ${provenanceKey} not found in store "${store.config.name}"`,
60
+ )
61
+ }
62
+ above.push(provenanceMolecule)
63
+ }
64
+ } else {
65
+ allocationAttachmentStyle = `any`
66
+ const provenanceKey = stringifyJson(provenance as Canonical)
67
+ const provenanceMolecule = store.molecules.get(provenanceKey)
68
+ if (!provenanceMolecule) {
69
+ throw new Error(
70
+ `Molecule ${provenanceKey} not found in store "${store.config.name}"`,
71
+ )
72
+ }
73
+ above.push(provenanceMolecule)
74
+ }
75
+ }
76
+
77
+ const molecule = new Molecule(above, key)
78
+ molecule._dependsOn = allocationAttachmentStyle
79
+
80
+ store.molecules.set(stringKey, molecule)
81
+
82
+ for (const aboveMolecule of above) {
83
+ aboveMolecule.below.set(molecule.stringKey, molecule)
84
+ }
85
+
86
+ const creationEvent: MoleculeCreationModern = {
87
+ type: `molecule_creation`,
88
+ subType: `modern`,
89
+ key: molecule.key,
90
+ provenance: provenance as Canonical,
91
+ }
92
+ const target = newest(store)
93
+ const isTransaction =
94
+ isChildStore(target) && target.transactionMeta.phase === `building`
95
+ if (isTransaction) {
96
+ target.transactionMeta.update.updates.push(creationEvent)
97
+ } else {
98
+ target.on.moleculeCreationStart.next(creationEvent)
99
+ }
100
+ } catch (thrown) {
101
+ if (thrown instanceof Error) {
102
+ store.logger.error(
103
+ `❌`,
104
+ `molecule`,
105
+ stringKey,
106
+ `allocation failed:`,
107
+ thrown.message,
108
+ )
109
+ }
110
+ }
111
+
112
+ return key as Claim<H, V, A>
113
+ }
114
+
115
+ export function deallocateFromStore<
116
+ H extends Hierarchy,
117
+ V extends Vassal<H>,
118
+ A extends Above<V, H>,
119
+ >(store: Store, claim: Claim<H, V, A>): void {
120
+ const stringKey = stringifyJson(claim)
121
+ const molecule = store.molecules.get(stringKey)
122
+ if (!molecule) {
123
+ throw new Error(
124
+ `Molecule ${stringKey} not found in store "${store.config.name}"`,
125
+ )
126
+ }
127
+
128
+ for (const join of molecule.joins.values()) {
129
+ join.relations.delete(molecule.key)
130
+ join.molecules.delete(molecule.stringKey)
131
+ }
132
+
133
+ let provenance: Canonical
134
+ if (molecule.above.size === 1) {
135
+ const above = molecule.above.values().next().value
136
+ provenance = above.key
137
+ } else {
138
+ provenance = [...molecule.above.values()].map(({ key }) => key)
139
+ }
140
+ const values: [string, any][] = []
141
+ for (const stateToken of molecule.tokens.values()) {
142
+ // biome-ignore lint/style/noNonNullAssertion: tokens of molecules must have a family
143
+ const tokenFamily = stateToken.family!
144
+ values.push([tokenFamily.key, store.valueMap.get(stateToken.key)])
145
+ }
146
+
147
+ for (const state of molecule.tokens.values()) {
148
+ disposeFromStore(store, state)
149
+ }
150
+ for (const child of molecule.below.values()) {
151
+ if (child.dependsOn === `all`) {
152
+ deallocateFromStore<any, any, any>(store, child.key)
153
+ } else {
154
+ child.above.delete(molecule.stringKey)
155
+ if (child.above.size === 0) {
156
+ deallocateFromStore<any, any, any>(store, child.key)
157
+ }
158
+ }
159
+ }
160
+ molecule.below.clear()
161
+
162
+ const disposalEvent: MoleculeDisposalModern = {
163
+ type: `molecule_disposal`,
164
+ subType: `modern`,
165
+ key: molecule.key,
166
+ values,
167
+ provenance,
168
+ }
169
+ const target = newest(store)
170
+ const isTransaction =
171
+ isChildStore(target) && target.transactionMeta.phase === `building`
172
+ if (isTransaction) {
173
+ target.transactionMeta.update.updates.push(disposalEvent)
174
+ } else {
175
+ target.on.moleculeDisposal.next(disposalEvent)
176
+ }
177
+ target.molecules.delete(molecule.stringKey)
178
+
179
+ for (const parent of molecule.above.values()) {
180
+ parent.below.delete(molecule.stringKey)
181
+ }
182
+ }
183
+
184
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
185
+ export function realm<H extends Hierarchy>(store: Store) {
186
+ const root = makeRootMoleculeInStore(`root`, store)
187
+ return {
188
+ root,
189
+ allocate: <V extends Vassal<H>, A extends Above<V, H>>(
190
+ provenance: A,
191
+ key: V,
192
+ ): Claim<H, V, A> => {
193
+ return allocateIntoStore(store, provenance, key)
194
+ },
195
+ deallocate: <V extends Vassal<H>, A extends Above<V, H>>(
196
+ claim: Claim<H, V, A>,
197
+ ): void => {
198
+ deallocateFromStore(store, claim)
199
+ },
200
+ }
201
+ }
202
+
203
+ export const T$ = `T$`
204
+ export type T$ = typeof T$
205
+ export type TypeTag<T extends string> = `${T$}--${T}`
206
+ export type SingularTypedKey<T extends string = string> = `${T}::${string}`
207
+ export type CompoundTypedKey<
208
+ A extends string = string,
209
+ B extends string = string,
210
+ C extends string = string,
211
+ > = `${TypeTag<A>}==${SingularTypedKey<B>}++${SingularTypedKey<C>}`
212
+ export type TypedKey<
213
+ A extends string = string,
214
+ B extends string = string,
215
+ C extends string = string,
216
+ > = CompoundTypedKey<A, B, C> | SingularTypedKey<A>
217
+ type Scope = SingularTypedKey[]
218
+ type MutualFealty = {
219
+ above: Scope
220
+ below: CompoundTypedKey
221
+ style: `all` | `any`
222
+ }
223
+ type ExclusiveFealty = {
224
+ above: TypedKey | `root`
225
+ below: Scope
226
+ }
227
+ type Fealty = ExclusiveFealty | MutualFealty
228
+
229
+ export type Hierarchy<F extends Fealty[] = Fealty[]> = Each<F>
230
+
231
+ export type Vassal<H extends Hierarchy> = {
232
+ [K in keyof H]: H[K] extends MutualFealty
233
+ ? H[K][`below`]
234
+ : H[K] extends { below: Array<infer V> }
235
+ ? V extends TypedKey
236
+ ? V
237
+ : never
238
+ : never
239
+ }[keyof H]
240
+
241
+ export type Above<TK extends TypedKey, H extends Hierarchy> = {
242
+ [K in keyof H]: H[K] extends MutualFealty
243
+ ? TK extends H[K][`below`]
244
+ ? H[K][`above`]
245
+ : never
246
+ : H[K] extends { below: Array<infer V> }
247
+ ? TK extends V
248
+ ? H[K] extends ExclusiveFealty
249
+ ? H[K][`above`]
250
+ : never
251
+ : never
252
+ : never
253
+ }[keyof H]
254
+
255
+ export type Below<TK extends TypedKey | TypedKey[], H extends Hierarchy> = {
256
+ [K in keyof H]: H[K] extends MutualFealty
257
+ ? TK extends H[K][`above`]
258
+ ? H[K][`below`]
259
+ : TK extends H[K][`above`][number]
260
+ ? H[K][`below`]
261
+ : never
262
+ : H[K] extends { above: infer V }
263
+ ? TK extends V
264
+ ? H[K] extends ExclusiveFealty
265
+ ? H[K][`below`][number]
266
+ : never
267
+ : never
268
+ : never
269
+ }[keyof H]
270
+
271
+ export type Mutuals<TK extends TypedKey | TypedKey[], H extends Hierarchy> = {
272
+ [K in keyof H]: H[K] extends MutualFealty
273
+ ? TK extends H[K][`above`][number]
274
+ ? [mutual: Exclude<H[K][`above`][number], TK>, below: H[K][`below`]]
275
+ : never
276
+ : never
277
+ }[keyof H]
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ import type {
7
7
  WritableSelectorFamilyToken,
8
8
  } from "./selector"
9
9
 
10
+ export * from "./allocate"
10
11
  export * from "./atom"
11
12
  export * from "./dispose-state"
12
13
  export * from "./get-state"
package/src/molecule.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type {
2
2
  ActorToolkit,
3
3
  MoleculeCreation,
4
+ MoleculeCreationClassic,
4
5
  MoleculeDisposal,
6
+ MoleculeDisposalClassic,
5
7
  MutableAtomFamilyToken,
6
8
  MutableAtomToken,
7
9
  ReadableFamilyToken,
@@ -36,12 +38,14 @@ export type CtorToolkit<K extends Canonical> = Flat<
36
38
  bond<T>(family: ReadonlySelectorFamilyToken<T, K>): ReadonlySelectorToken<T>
37
39
  bond<T>(family: WritableFamilyToken<T, K>): WritableToken<T>
38
40
  bond<T>(family: ReadableFamilyToken<T, K>): ReadableToken<T>
39
- bond<J extends JoinToken<any, any, any, any>>(
41
+ bond<J extends JoinToken<any, any, any, any, any, any>>(
40
42
  joinToken: J,
41
43
  role: {
42
- as: J extends JoinToken<infer A, infer B, any, any> ? A | B : never
44
+ as: J extends JoinToken<infer A, string, infer B, string, any, any>
45
+ ? A | B
46
+ : never
43
47
  },
44
- ): J extends JoinToken<any, any, any, infer Content>
48
+ ): J extends JoinToken<any, any, any, any, any, infer Content>
45
49
  ? Content extends null
46
50
  ? { relatedKeys: ReadonlySelectorToken<string[]> }
47
51
  : {
@@ -86,7 +90,7 @@ export type MoleculeFamilyToken<M extends MoleculeConstructor> = {
86
90
  }
87
91
  export type MoleculeFamily<M extends MoleculeConstructor> = Flat<
88
92
  MoleculeFamilyToken<M> & {
89
- subject: Subject<MoleculeCreation<M> | MoleculeDisposal>
93
+ subject: Subject<MoleculeCreationClassic<M> | MoleculeDisposalClassic>
90
94
  dependsOn: `all` | `any`
91
95
  new: M
92
96
  }
@@ -121,7 +125,7 @@ export type MoleculeType<T extends MoleculeFamilyToken<any>> =
121
125
  : never
122
126
  export type MoleculeKey<M extends MoleculeConstructor> = InstanceType<M>[`key`]
123
127
 
124
- export function makeRootMolecule(
128
+ export function makeRootMoleculeInStore(
125
129
  key: string,
126
130
  store: Store = IMPLICIT.STORE,
127
131
  ): MoleculeToken<ObjectConstructor> {
@@ -16,7 +16,7 @@ import {
16
16
  createTransaction,
17
17
  IMPLICIT,
18
18
  } from "atom.io/internal"
19
- import type { Json } from "atom.io/json"
19
+ import type { Canonical, Json } from "atom.io/json"
20
20
 
21
21
  import type {
22
22
  disposeState,
@@ -25,7 +25,6 @@ import type {
25
25
  ReadableToken,
26
26
  TokenType,
27
27
  WritableSelectorToken,
28
- WritableToken,
29
28
  } from "."
30
29
 
31
30
  export type TransactionToken<F extends Func> = {
@@ -44,20 +43,39 @@ export type StateDisposal<Token extends ReadableToken<any>> = {
44
43
  value?: TokenType<Token>
45
44
  }
46
45
 
47
- export type MoleculeCreation<M extends MoleculeConstructor> = {
46
+ export type MoleculeCreationClassic<M extends MoleculeConstructor> = {
48
47
  type: `molecule_creation`
48
+ subType: `classic`
49
49
  token: MoleculeToken<M>
50
50
  family: MoleculeFamilyToken<M>
51
51
  context: MoleculeToken<any>[]
52
52
  params: MoleculeParams<M>
53
53
  }
54
- export type MoleculeDisposal = {
54
+ export type MoleculeCreationModern = {
55
+ type: `molecule_creation`
56
+ subType: `modern`
57
+ key: Canonical
58
+ provenance: Canonical
59
+ }
60
+ export type MoleculeCreation<M extends MoleculeConstructor> =
61
+ | MoleculeCreationClassic<M>
62
+ | MoleculeCreationModern
63
+ export type MoleculeDisposalClassic = {
55
64
  type: `molecule_disposal`
65
+ subType: `classic`
56
66
  token: MoleculeToken<any>
57
67
  family: MoleculeFamilyToken<any>
58
68
  context: MoleculeToken<any>[]
59
69
  values: [key: string, value: any][]
60
70
  }
71
+ export type MoleculeDisposalModern = {
72
+ type: `molecule_disposal`
73
+ subType: `modern`
74
+ key: Canonical
75
+ provenance: Canonical
76
+ values: [key: string, value: any][]
77
+ }
78
+ export type MoleculeDisposal = MoleculeDisposalClassic | MoleculeDisposalModern
61
79
 
62
80
  export type TransactionUpdateContent =
63
81
  | KeyedStateUpdate<unknown>