atom.io 0.30.0 → 0.30.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.
@@ -2,7 +2,7 @@ import { Subject, Transceiver, Store } from 'atom.io/internal';
2
2
  import { Json, stringified, JsonIO, Canonical } from 'atom.io/json';
3
3
  import { ChildProcessWithoutNullStreams } from 'node:child_process';
4
4
  import * as AtomIO from 'atom.io';
5
- import { TransactionUpdateContent, TransactionUpdate, WritableToken } from 'atom.io';
5
+ import { TransactionUpdateContent, TransactionUpdate, Hierarchy, WritableToken } from 'atom.io';
6
6
  import { ContinuityToken, UserInRoomMeta } from 'atom.io/realtime';
7
7
  import * as atom_io_data from 'atom.io/data';
8
8
  import { Loadable } from 'atom.io/data';
@@ -112,10 +112,19 @@ declare const actionOcclusionAtoms: AtomIO.RegularAtomFamilyToken<{
112
112
  }, string>;
113
113
  declare const userUnacknowledgedQueues: AtomIO.RegularAtomFamilyToken<Pick<TransactionUpdate<any>, "key" | "epoch" | "id" | "updates" | "output">[], string>;
114
114
 
115
- declare const socketAtoms: AtomIO.RegularAtomFamilyToken<Socket | null, string>;
116
- declare const socketIndex: AtomIO.MutableAtomToken<SetRTX<string>, SetRTXJson<string>>;
117
- declare const userIndex: AtomIO.MutableAtomToken<SetRTX<string>, SetRTXJson<string>>;
118
- declare const usersOfSockets: atom_io_data.JoinToken<"user", "socket", "1:1", null>;
115
+ type SocketKey = `socket::${string}`;
116
+ type UserKey = `user::${string}`;
117
+ type RoomKey = `room::${string}`;
118
+ type SocketSystemHierarchy = Hierarchy<[
119
+ {
120
+ above: `root`;
121
+ below: [UserKey, SocketKey, RoomKey];
122
+ }
123
+ ]>;
124
+ declare const socketAtoms: AtomIO.RegularAtomFamilyToken<Socket | null, `socket::${string}`>;
125
+ declare const socketIndex: AtomIO.MutableAtomToken<SetRTX<`socket::${string}`>, SetRTXJson<`socket::${string}`>>;
126
+ declare const userIndex: AtomIO.MutableAtomToken<SetRTX<`user::${string}`>, SetRTXJson<`user::${string}`>>;
127
+ declare const usersOfSockets: atom_io_data.JoinToken<"user", `user::${string}`, "socket", `socket::${string}`, "1:1", null>;
119
128
 
120
129
  type StateProvider = ReturnType<typeof realtimeStateProvider>;
121
130
  declare function realtimeStateProvider({ socket, store, }: ServerConfig): <J extends Json.Serializable>(token: AtomIO.WritableToken<J>) => () => void;
@@ -136,4 +145,4 @@ type ServerConfig = {
136
145
  store?: Store;
137
146
  };
138
147
 
139
- export { type ActionReceiver, ChildSocket, type CreateRoomIO, CustomSocket, type EventBuffer, type Events, type FamilyProvider, type JoinRoomIO, type LeaveRoomIO, type MutableFamilyProvider, type MutableProvider, ParentSocket, type RealtimeContinuitySynchronizer, type RoomArguments, type ServerConfig, type Socket, type StateProvider, type StateReceiver, type StringifiedEvent, SubjectSocket, actionOcclusionAtoms, createRoomTX, destroyRoomTX, joinRoomTX, leaveRoomTX, realtimeActionReceiver, realtimeAtomFamilyProvider, realtimeContinuitySynchronizer, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, redactTransactionUpdateContent, roomArgumentsAtoms, roomSelectors, socketAtoms, socketIndex, userIndex, userUnacknowledgedQueues, usersOfSockets };
148
+ export { type ActionReceiver, ChildSocket, type CreateRoomIO, CustomSocket, type EventBuffer, type Events, type FamilyProvider, type JoinRoomIO, type LeaveRoomIO, type MutableFamilyProvider, type MutableProvider, ParentSocket, type RealtimeContinuitySynchronizer, type RoomArguments, type RoomKey, type ServerConfig, type Socket, type SocketKey, type SocketSystemHierarchy, type StateProvider, type StateReceiver, type StringifiedEvent, SubjectSocket, type UserKey, actionOcclusionAtoms, createRoomTX, destroyRoomTX, joinRoomTX, leaveRoomTX, realtimeActionReceiver, realtimeAtomFamilyProvider, realtimeContinuitySynchronizer, realtimeMutableFamilyProvider, realtimeMutableProvider, realtimeStateProvider, realtimeStateReceiver, redactTransactionUpdateContent, roomArgumentsAtoms, roomSelectors, socketAtoms, socketIndex, userIndex, userUnacknowledgedQueues, usersOfSockets };
@@ -468,7 +468,9 @@ var userIndex = atom({
468
468
  var usersOfSockets = join({
469
469
  key: `usersOfSockets`,
470
470
  between: [`user`, `socket`],
471
- cardinality: `1:1`
471
+ cardinality: `1:1`,
472
+ isAType: (s) => s.startsWith(`user::`),
473
+ isBType: (s) => s.startsWith(`socket::`)
472
474
  });
473
475
 
474
476
  // realtime-server/src/realtime-continuity-synchronizer.ts
@@ -481,7 +483,7 @@ function realtimeContinuitySynchronizer({
481
483
  const continuityKey = continuity.key;
482
484
  const userKeyState = findRelationsInStore(
483
485
  usersOfSockets,
484
- socket.id,
486
+ `socket::${socket.id}`,
485
487
  store
486
488
  ).userKeyOfSocket;
487
489
  const userKey = getFromStore(store, userKeyState);
@@ -15,7 +15,7 @@ import {
15
15
  import type { Json, JsonIO } from "atom.io/json"
16
16
  import type { ContinuityToken } from "atom.io/realtime"
17
17
 
18
- import type { ServerConfig, Socket } from "."
18
+ import type { ServerConfig, Socket, SocketKey } from "."
19
19
  import { socketAtoms, usersOfSockets } from "."
20
20
  import {
21
21
  redactTransactionUpdateContent,
@@ -35,7 +35,7 @@ export function realtimeContinuitySynchronizer({
35
35
  const continuityKey = continuity.key
36
36
  const userKeyState = findRelationsInStore(
37
37
  usersOfSockets,
38
- socket.id,
38
+ `socket::${socket.id}`,
39
39
  store,
40
40
  ).userKeyOfSocket
41
41
  const userKey = getFromStore(store, userKeyState)
@@ -1,3 +1,4 @@
1
+ import type { Hierarchy } from "atom.io"
1
2
  import { atom, atomFamily } from "atom.io"
2
3
  import { join } from "atom.io/data"
3
4
  import type { SetRTXJson } from "atom.io/transceivers/set-rtx"
@@ -5,22 +6,35 @@ import { SetRTX } from "atom.io/transceivers/set-rtx"
5
6
 
6
7
  import type { Socket } from ".."
7
8
 
8
- export const socketAtoms = atomFamily<Socket | null, string>({
9
+ export type SocketKey = `socket::${string}`
10
+ export type UserKey = `user::${string}`
11
+ export type RoomKey = `room::${string}`
12
+
13
+ export type SocketSystemHierarchy = Hierarchy<
14
+ [
15
+ {
16
+ above: `root`
17
+ below: [UserKey, SocketKey, RoomKey]
18
+ },
19
+ ]
20
+ >
21
+
22
+ export const socketAtoms = atomFamily<Socket | null, SocketKey>({
9
23
  key: `sockets`,
10
24
  default: null,
11
25
  })
12
26
 
13
- export const socketIndex = atom<SetRTX<string>, SetRTXJson<string>>({
27
+ export const socketIndex = atom<SetRTX<SocketKey>, SetRTXJson<SocketKey>>({
14
28
  key: `socketsIndex`,
15
29
  mutable: true,
16
- default: () => new SetRTX<string>(),
30
+ default: () => new SetRTX(),
17
31
  toJson: (set) => set.toJSON(),
18
32
  fromJson: (json) => SetRTX.fromJSON(json),
19
33
  })
20
- export const userIndex = atom<SetRTX<string>, SetRTXJson<string>>({
34
+ export const userIndex = atom<SetRTX<UserKey>, SetRTXJson<UserKey>>({
21
35
  key: `usersIndex`,
22
36
  mutable: true,
23
- default: () => new SetRTX<string>(),
37
+ default: () => new SetRTX(),
24
38
  toJson: (set) => set.toJSON(),
25
39
  fromJson: (json) => SetRTX.fromJSON(json),
26
40
  })
@@ -28,4 +42,6 @@ export const usersOfSockets = join({
28
42
  key: `usersOfSockets`,
29
43
  between: [`user`, `socket`],
30
44
  cardinality: `1:1`,
45
+ isAType: (s): s is UserKey => s.startsWith(`user::`),
46
+ isBType: (s): s is SocketKey => s.startsWith(`socket::`),
31
47
  })
@@ -6,6 +6,9 @@ import { Socket } from 'socket.io-client';
6
6
 
7
7
  type TestSetupOptions = {
8
8
  port: number;
9
+ immortal?: {
10
+ server?: boolean;
11
+ };
9
12
  server: (tools: {
10
13
  socket: SocketIO.Socket;
11
14
  silo: AtomIO.Silo;
@@ -2,6 +2,7 @@ import '../../dist/chunk-XWL6SNVU.js';
2
2
  import * as http from 'node:http';
3
3
  import { render, prettyDOM } from '@testing-library/react';
4
4
  import * as AtomIO from 'atom.io';
5
+ import { realm } from 'atom.io';
5
6
  import { editRelationsInStore, findRelationsInStore } from 'atom.io/data';
6
7
  import { IMPLICIT, findInStore, setIntoStore, getFromStore, clearStore } from 'atom.io/internal';
7
8
  import { toEntries } from 'atom.io/json';
@@ -32,9 +33,13 @@ function prefixLogger(store, prefix) {
32
33
  var setupRealtimeTestServer = (options) => {
33
34
  ++testNumber;
34
35
  const silo = new AtomIO.Silo(
35
- { name: `SERVER-${testNumber}`, lifespan: `ephemeral` },
36
+ {
37
+ name: `SERVER-${testNumber}`,
38
+ lifespan: options.immortal?.server ? `immortal` : `ephemeral`
39
+ },
36
40
  IMPLICIT.STORE
37
41
  );
42
+ const socketRealm = realm(silo.store);
38
43
  const httpServer = http.createServer((_, res) => res.end(`Hello World!`));
39
44
  const address = httpServer.listen(options.port).address();
40
45
  const port = typeof address === `string` ? null : address === null ? null : address.port;
@@ -42,17 +47,23 @@ var setupRealtimeTestServer = (options) => {
42
47
  const server = new SocketIO.Server(httpServer).use((socket, next) => {
43
48
  const { token, username } = socket.handshake.auth;
44
49
  if (token === `test` && socket.id) {
45
- const socketState = findInStore(silo.store, RTS.socketAtoms, socket.id);
50
+ const userClaim = socketRealm.allocate(`root`, `user::${username}`);
51
+ const socketClaim = socketRealm.allocate(`root`, `socket::${socket.id}`);
52
+ const socketState = findInStore(silo.store, RTS.socketAtoms, socketClaim);
46
53
  setIntoStore(silo.store, socketState, socket);
47
54
  editRelationsInStore(
48
55
  RTS.usersOfSockets,
49
56
  (relations) => {
50
- relations.set(socket.id, username);
57
+ relations.set(userClaim, socketClaim);
51
58
  },
52
59
  silo.store
53
60
  );
54
- setIntoStore(silo.store, RTS.userIndex, (index) => index.add(username));
55
- setIntoStore(silo.store, RTS.socketIndex, (index) => index.add(socket.id));
61
+ setIntoStore(silo.store, RTS.userIndex, (index) => index.add(userClaim));
62
+ setIntoStore(
63
+ silo.store,
64
+ RTS.socketIndex,
65
+ (index) => index.add(socketClaim)
66
+ );
56
67
  console.log(`${username} connected on ${socket.id}`);
57
68
  next();
58
69
  } else {
@@ -64,7 +75,7 @@ var setupRealtimeTestServer = (options) => {
64
75
  function enableLogging() {
65
76
  const userKeyState = findRelationsInStore(
66
77
  RTS.usersOfSockets,
67
- socket.id,
78
+ `socket::${socket.id}`,
68
79
  silo.store
69
80
  ).userKeyOfSocket;
70
81
  userKey = getFromStore(silo.store, userKeyState);
@@ -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,17 +116,21 @@ 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
  )
122
- setIntoStore(silo.store, RTS.userIndex, (index) => index.add(username))
123
- setIntoStore(silo.store, RTS.socketIndex, (index) => index.add(socket.id))
130
+ setIntoStore(silo.store, RTS.userIndex, (index) => index.add(userClaim))
131
+ setIntoStore(silo.store, RTS.socketIndex, (index) =>
132
+ index.add(socketClaim),
133
+ )
124
134
  console.log(`${username} connected on ${socket.id}`)
125
135
  next()
126
136
  } else {
@@ -133,7 +143,7 @@ export const setupRealtimeTestServer = (
133
143
  function enableLogging() {
134
144
  const userKeyState = findRelationsInStore(
135
145
  RTS.usersOfSockets,
136
- socket.id,
146
+ `socket::${socket.id}`,
137
147
  silo.store,
138
148
  ).userKeyOfSocket
139
149
  userKey = getFromStore(silo.store, userKeyState)
package/src/allocate.ts CHANGED
@@ -29,7 +29,6 @@ export function allocateIntoStore<
29
29
  A extends Above<V, H>,
30
30
  >(store: Store, provenance: A, key: V): Claim<H, V, A> {
31
31
  const stringKey = stringifyJson(key)
32
-
33
32
  try {
34
33
  const above: Molecule<any>[] = []
35
34
 
@@ -38,7 +37,7 @@ export function allocateIntoStore<
38
37
  // biome-ignore lint/style/noNonNullAssertion: let's assume we made the root molecule to get here
39
38
  above.push(store.molecules.get(`"root"`)!)
40
39
  allocationAttachmentStyle = `all`
41
- } else if (provenance[0][0] === T$) {
40
+ } else if (typeof provenance === `string` && provenance.startsWith(T$)) {
42
41
  allocationAttachmentStyle = `any`
43
42
  const provenanceKey = stringifyJson(provenance as Canonical)
44
43
  const provenanceMolecule = store.molecules.get(provenanceKey)
@@ -49,7 +48,7 @@ export function allocateIntoStore<
49
48
  }
50
49
  above.push(provenanceMolecule)
51
50
  } else {
52
- const allocationIsCompound = key[0][0] === T$
51
+ const allocationIsCompound = key.startsWith(`T$--`)
53
52
  if (allocationIsCompound) {
54
53
  allocationAttachmentStyle = `all`
55
54
  for (const claim of provenance as SingularTypedKey[]) {
@@ -150,11 +149,11 @@ export function deallocateFromStore<
150
149
  }
151
150
  for (const child of molecule.below.values()) {
152
151
  if (child.dependsOn === `all`) {
153
- deallocateFromStore(store, child.key)
152
+ deallocateFromStore<any, any, any>(store, child.key)
154
153
  } else {
155
154
  child.above.delete(molecule.stringKey)
156
155
  if (child.above.size === 0) {
157
- deallocateFromStore(store, child.key)
156
+ deallocateFromStore<any, any, any>(store, child.key)
158
157
  }
159
158
  }
160
159
  }
@@ -203,13 +202,13 @@ export function realm<H extends Hierarchy>(store: Store) {
203
202
 
204
203
  export const T$ = `T$`
205
204
  export type T$ = typeof T$
206
- export type TypeTag<T extends string> = [T$, T]
207
- export type SingularTypedKey<T extends string = string> = [T, string]
205
+ export type TypeTag<T extends string> = `${T$}--${T}`
206
+ export type SingularTypedKey<T extends string = string> = `${T}::${string}`
208
207
  export type CompoundTypedKey<
209
208
  A extends string = string,
210
209
  B extends string = string,
211
210
  C extends string = string,
212
- > = [TypeTag<A>, TypedKey<B>, TypedKey<C>]
211
+ > = `${TypeTag<A>}==${SingularTypedKey<B>}++${SingularTypedKey<C>}`
213
212
  export type TypedKey<
214
213
  A extends string = string,
215
214
  B extends string = string,
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
@@ -38,12 +38,14 @@ export type CtorToolkit<K extends Canonical> = Flat<
38
38
  bond<T>(family: ReadonlySelectorFamilyToken<T, K>): ReadonlySelectorToken<T>
39
39
  bond<T>(family: WritableFamilyToken<T, K>): WritableToken<T>
40
40
  bond<T>(family: ReadableFamilyToken<T, K>): ReadableToken<T>
41
- bond<J extends JoinToken<any, any, any, any>>(
41
+ bond<J extends JoinToken<any, any, any, any, any, any>>(
42
42
  joinToken: J,
43
43
  role: {
44
- 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
45
47
  },
46
- ): J extends JoinToken<any, any, any, infer Content>
48
+ ): J extends JoinToken<any, any, any, any, any, infer Content>
47
49
  ? Content extends null
48
50
  ? { relatedKeys: ReadonlySelectorToken<string[]> }
49
51
  : {
@@ -1,20 +0,0 @@
1
- import { createMoleculeFamily, IMPLICIT, makeMoleculeInStore, Molecule } from 'atom.io/internal';
2
- import { stringifyJson } from 'atom.io/json';
3
-
4
- // src/molecule.ts
5
- function moleculeFamily(options) {
6
- return createMoleculeFamily(IMPLICIT.STORE, options);
7
- }
8
- function makeMolecule(context, family, key, ...params) {
9
- return makeMoleculeInStore(IMPLICIT.STORE, context, family, key, ...params);
10
- }
11
- function makeRootMoleculeInStore(key, store = IMPLICIT.STORE) {
12
- const molecule = new Molecule(void 0, key);
13
- store.molecules.set(stringifyJson(key), molecule);
14
- return {
15
- key,
16
- type: `molecule`
17
- };
18
- }
19
-
20
- export { makeMolecule, makeRootMoleculeInStore, moleculeFamily };