atom.io 0.46.0 → 0.46.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 (28) hide show
  1. package/dist/introspection/index.d.ts.map +1 -1
  2. package/dist/realtime/index.d.ts +6 -3
  3. package/dist/realtime/index.d.ts.map +1 -1
  4. package/dist/realtime/index.js +9 -1
  5. package/dist/realtime/index.js.map +1 -1
  6. package/dist/realtime-server/index.d.ts +73 -50
  7. package/dist/realtime-server/index.d.ts.map +1 -1
  8. package/dist/realtime-server/index.js +222 -182
  9. package/dist/realtime-server/index.js.map +1 -1
  10. package/dist/realtime-testing/index.d.ts +1 -2
  11. package/dist/realtime-testing/index.d.ts.map +1 -1
  12. package/dist/realtime-testing/index.js +31 -53
  13. package/dist/realtime-testing/index.js.map +1 -1
  14. package/package.json +1 -1
  15. package/src/realtime/cast-socket.ts +1 -0
  16. package/src/realtime/shared-room-store.ts +15 -0
  17. package/src/realtime/socket-interface.ts +5 -1
  18. package/src/realtime-server/index.ts +3 -2
  19. package/src/realtime-server/ipc-sockets/custom-socket.ts +18 -2
  20. package/src/realtime-server/ipc-sockets/parent-socket.ts +1 -1
  21. package/src/realtime-server/{realtime-server-stores/provide-rooms.ts → provide-rooms.ts} +32 -57
  22. package/src/realtime-server/realtime-state-provider.ts +5 -3
  23. package/src/realtime-server/realtime-state-receiver.ts +19 -3
  24. package/src/realtime-server/server-config.ts +112 -1
  25. package/src/realtime-server/{realtime-server-stores/server-user-store.ts → server-socket-state.ts} +1 -8
  26. package/src/realtime-testing/setup-realtime-test.tsx +38 -83
  27. package/src/realtime-server/realtime-server-stores/index.ts +0 -3
  28. package/src/realtime-server/realtime-server-stores/provide-identity.ts +0 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atom.io",
3
- "version": "0.46.0",
3
+ "version": "0.46.1",
4
4
  "description": "Composable and testable reactive data library.",
5
5
  "homepage": "https://atom.io.fyi",
6
6
  "sideEffects": false,
@@ -61,6 +61,7 @@ export function castSocket<T extends TypedSocket>(
61
61
  })
62
62
  })
63
63
  },
64
+ onAnyOutgoing: socket.onAnyOutgoing.bind(socket),
64
65
  off: socket.off.bind(socket),
65
66
  offAny: socket.offAny.bind(socket),
66
67
  emit: socket.emit.bind(socket),
@@ -1,6 +1,7 @@
1
1
  import type {
2
2
  JoinToken,
3
3
  MutableAtomToken,
4
+ PureSelectorFamilyToken,
4
5
  ReadonlyPureSelectorFamilyToken,
5
6
  } from "atom.io"
6
7
  import { getInternalRelations, join, mutableAtom, selectorFamily } from "atom.io"
@@ -41,6 +42,20 @@ export const usersInRooms: JoinToken<`room`, RoomKey, `user`, UserKey, `1:n`> =
41
42
  isBType: isUserKey,
42
43
  })
43
44
 
45
+ export const visibleUsersInRoomsSelector: PureSelectorFamilyToken<
46
+ (RoomKey | UserKey)[],
47
+ UserKey
48
+ > = selectorFamily({
49
+ key: `selfList`,
50
+ get:
51
+ (userKey) =>
52
+ ({ get }) => {
53
+ const [, roomsOfUsersAtoms] = getInternalRelations(usersInRooms, `split`)
54
+ const rooms = get(roomsOfUsersAtoms, userKey)
55
+ return [userKey, ...rooms]
56
+ },
57
+ })
58
+
44
59
  export const ownersOfRooms: JoinToken<`user`, UserKey, `room`, RoomKey, `1:n`> =
45
60
  join({
46
61
  key: `ownersOfRooms`,
@@ -28,11 +28,12 @@ export type EventEmitter<EmitEvents extends EventsMap = EventsMap> = <
28
28
 
29
29
  export type TypedSocket<
30
30
  ListenEvents extends EventsMap = EventsMap,
31
- EmitEvents extends EventsMap = EventsMap,
31
+ EmitEvents extends EventsMap = never,
32
32
  > = {
33
33
  id: string | undefined
34
34
  on: ParticularEventListener<ListenEvents>
35
35
  onAny: (listener: AllEventsListener<ListenEvents>) => void
36
+ onAnyOutgoing: (listener: AllEventsListener<EmitEvents>) => void
36
37
  off: ParticularEventListener<ListenEvents>
37
38
  offAny: (listener: AllEventsListener<ListenEvents>) => void
38
39
  emit: EventEmitter<EmitEvents>
@@ -44,6 +45,9 @@ export type Socket = {
44
45
  onAny: (
45
46
  listener: (event: string, ...args: Json.Serializable[]) => void,
46
47
  ) => void
48
+ onAnyOutgoing: (
49
+ listener: (event: string, ...args: Json.Serializable[]) => void,
50
+ ) => void
47
51
  off: (event: string, listener: (...args: Json.Serializable[]) => void) => void
48
52
  offAny: (
49
53
  listener: (event: string, ...args: Json.Serializable[]) => void,
@@ -1,9 +1,10 @@
1
1
  export * from "./continuity/provide-continuity"
2
2
  export * from "./ipc-sockets"
3
+ export * from "./provide-rooms"
3
4
  export * from "./realtime-family-provider"
4
5
  export * from "./realtime-mutable-family-provider"
5
6
  export * from "./realtime-mutable-provider"
6
- export * from "./realtime-server-stores"
7
7
  export * from "./realtime-state-provider"
8
8
  export * from "./realtime-state-receiver"
9
- export type * from "./server-config"
9
+ export * from "./server-config"
10
+ export * from "./server-socket-state"
@@ -20,6 +20,9 @@ export abstract class CustomSocket<I extends Events, O extends Events>
20
20
  {
21
21
  protected listeners: Map<keyof O, Set<(...args: Json.Array) => void>>
22
22
  protected globalListeners: Set<(event: string, ...args: Json.Array) => void>
23
+ protected globalListenersOutgoing: Set<
24
+ (event: string, ...args: Json.Array) => void
25
+ >
23
26
  protected handleEvent<K extends string & keyof I>(
24
27
  ...args: EventPayload<I, K>
25
28
  ): void {
@@ -36,7 +39,7 @@ export abstract class CustomSocket<I extends Events, O extends Events>
36
39
  }
37
40
 
38
41
  public id = `no_id_retrieved`
39
- public emit: <Event extends keyof I>(
42
+ public emit: <Event extends string & keyof I>(
40
43
  event: Event,
41
44
  ...args: I[Event]
42
45
  ) => CustomSocket<I, O>
@@ -47,9 +50,15 @@ export abstract class CustomSocket<I extends Events, O extends Events>
47
50
  ...args: I[Event]
48
51
  ) => CustomSocket<I, O>,
49
52
  ) {
50
- this.emit = emit
53
+ this.emit = (...args) => {
54
+ for (const listener of this.globalListenersOutgoing) {
55
+ listener(...args)
56
+ }
57
+ return emit(...args)
58
+ }
51
59
  this.listeners = new Map()
52
60
  this.globalListeners = new Set()
61
+ this.globalListenersOutgoing = new Set()
53
62
  }
54
63
 
55
64
  public on<Event extends keyof O>(
@@ -70,6 +79,13 @@ export abstract class CustomSocket<I extends Events, O extends Events>
70
79
  return this
71
80
  }
72
81
 
82
+ public onAnyOutgoing(
83
+ listener: (event: string, ...args: Json.Array) => void,
84
+ ): this {
85
+ this.globalListenersOutgoing.add(listener)
86
+ return this
87
+ }
88
+
73
89
  public off<Event extends keyof O>(
74
90
  event: Event,
75
91
  listener?: (...args: O[Event]) => void,
@@ -190,7 +190,7 @@ export class ParentSocket<
190
190
  relay.in.next(data)
191
191
  })
192
192
  relay.out.subscribe(`socket`, (data) => {
193
- this.emit(...(data as [string, ...I[keyof I]]))
193
+ this.emit(...(data as [string, ...I[string & keyof I]]))
194
194
  })
195
195
  })
196
196
 
@@ -19,7 +19,6 @@ import type {
19
19
  RoomSocketInterface,
20
20
  Socket,
21
21
  SocketGuard,
22
- SocketKey,
23
22
  StandardSchemaV1,
24
23
  TypedSocket,
25
24
  UserKey,
@@ -30,18 +29,13 @@ import {
30
29
  ownersOfRooms,
31
30
  roomKeysAtom,
32
31
  usersInRooms,
32
+ visibleUsersInRoomsSelector,
33
33
  } from "atom.io/realtime"
34
34
 
35
- import { ChildSocket } from "../ipc-sockets"
36
- import { realtimeMutableFamilyProvider } from "../realtime-mutable-family-provider"
37
- import { realtimeMutableProvider } from "../realtime-mutable-provider"
38
- import type { ServerConfig } from "../server-config"
39
- import {
40
- selfListSelectors,
41
- socketKeysAtom,
42
- userKeysAtom,
43
- usersOfSockets,
44
- } from "./server-user-store"
35
+ import { ChildSocket } from "./ipc-sockets"
36
+ import { realtimeMutableFamilyProvider } from "./realtime-mutable-family-provider"
37
+ import { realtimeMutableProvider } from "./realtime-mutable-provider"
38
+ import type { ServerConfig } from "./server-config"
45
39
 
46
40
  export type RoomMap = Map<
47
41
  string,
@@ -240,74 +234,55 @@ export function provideRooms<RoomNames extends string>({
240
234
  socket,
241
235
  resolveRoomScript,
242
236
  roomNames,
243
- }: ProvideRoomsConfig<RoomNames> & ServerConfig): void {
244
- const socketKey = `socket::${socket.id}` satisfies SocketKey
245
- const userKey = getFromStore(
246
- store,
247
- findRelationsInStore(store, usersOfSockets, socketKey).userKeyOfSocket,
248
- )!
249
- // const roomSocket = socket as TypedSocket<RoomSocketInterface<RoomNames>, {}>
250
- const roomSocket = castSocket<TypedSocket<RoomSocketInterface<RoomNames>, {}>>(
251
- socket,
252
- createRoomSocketGuard(roomNames),
253
- )
254
-
237
+ userKey,
238
+ }: ProvideRoomsConfig<RoomNames> & ServerConfig): () => void {
239
+ const roomSocket = castSocket<
240
+ TypedSocket<RoomSocketInterface<RoomNames>, never>
241
+ >(socket, createRoomSocketGuard(roomNames))
255
242
  const exposeMutable = realtimeMutableProvider({ socket, store, userKey })
256
- const exposeMutableFamily = realtimeMutableFamilyProvider({
257
- socket,
258
- store,
259
- userKey,
260
- })
261
-
262
- exposeMutable(roomKeysAtom)
263
-
264
- const [, usersInRoomsAtoms] = getInternalRelationsFromStore(
243
+ const unsubFromRoomKeys = exposeMutable(roomKeysAtom)
244
+ const usersInRoomsAtoms = getInternalRelationsFromStore(store, usersInRooms)
245
+ const [, usersInRoomsAtomsUsersOnly] = getInternalRelationsFromStore(
265
246
  store,
266
247
  usersInRooms,
267
248
  `split`,
268
249
  )
269
250
  const usersWhoseRoomsCanBeSeenSelector = findInStore(
270
251
  store,
271
- selfListSelectors,
252
+ visibleUsersInRoomsSelector,
272
253
  userKey,
273
254
  )
274
- exposeMutableFamily(usersInRoomsAtoms, usersWhoseRoomsCanBeSeenSelector)
275
- // const usersOfSocketsAtoms = getInternalRelationsFromStore(
276
- // store,
277
- // usersOfSockets,
278
- // )
279
- // exposeMutableFamily(usersOfSocketsAtoms, socketKeysAtom)
280
- const [ownersOfRoomsAtoms] = getInternalRelationsFromStore(
255
+ const ownersOfRoomsAtoms = getInternalRelationsFromStore(store, ownersOfRooms)
256
+ const exposeMutableFamily = realtimeMutableFamilyProvider({
257
+ socket,
281
258
  store,
282
- ownersOfRooms,
283
- `split`,
259
+ userKey,
260
+ })
261
+ const unsubFromUsersInRooms = exposeMutableFamily(
262
+ usersInRoomsAtoms,
263
+ usersWhoseRoomsCanBeSeenSelector,
264
+ )
265
+ const unsubFromOwnersOfRooms = exposeMutableFamily(
266
+ ownersOfRoomsAtoms,
267
+ usersWhoseRoomsCanBeSeenSelector,
284
268
  )
285
- exposeMutableFamily(ownersOfRoomsAtoms, usersWhoseRoomsCanBeSeenSelector)
286
-
287
269
  const enterRoom = provideEnterAndExit({ store, socket, roomSocket, userKey })
288
270
 
289
- const userRoomSet = getFromStore(store, usersInRoomsAtoms, userKey)
271
+ const userRoomSet = getFromStore(store, usersInRoomsAtomsUsersOnly, userKey)
290
272
  for (const userRoomKey of userRoomSet) {
291
273
  enterRoom(userRoomKey)
292
274
  break
293
275
  }
294
-
295
276
  roomSocket.on(
296
277
  `createRoom`,
297
278
  spawnRoom({ store, socket, userKey, resolveRoomScript }),
298
279
  )
299
280
  roomSocket.on(`deleteRoom`, destroyRoom({ store, socket, userKey }))
300
- socket.on(`disconnect`, () => {
301
- store.logger.info(
302
- `📡`,
303
- `socket`,
304
- socket.id ?? `[ID MISSING?!]`,
305
- `👤 ${userKey} disconnects`,
306
- )
307
- editRelationsInStore(store, usersOfSockets, (rel) => rel.delete(socketKey))
308
- setIntoStore(store, userKeysAtom, (keys) => (keys.delete(userKey), keys))
309
- setIntoStore(store, socketKeysAtom, (keys) => (keys.delete(socketKey), keys))
310
- })
281
+ return () => {
282
+ unsubFromRoomKeys()
283
+ unsubFromUsersInRooms()
284
+ unsubFromOwnersOfRooms()
285
+ }
311
286
  }
312
287
 
313
288
  const roomKeySchema: StandardSchemaV1<Json.Array, [RoomKey]> = {
@@ -21,9 +21,11 @@ export function realtimeStateProvider({
21
21
  store = IMPLICIT.STORE,
22
22
  }: ServerConfig) {
23
23
  store.logger.info(`🔌`, `user`, userKey, `initialized state provider`)
24
- return function stateProvider<J extends Json.Serializable>(
25
- clientToken: AtomIO.WritableToken<J>,
26
- serverData: AtomIO.ReadableToken<NoInfer<J>> | NoInfer<J> = clientToken,
24
+ return function stateProvider<C extends Json.Serializable, S extends C>(
25
+ clientToken: AtomIO.WritableToken<C>,
26
+ serverData:
27
+ | AtomIO.ReadableToken<S>
28
+ | S = clientToken as AtomIO.ReadableToken<S>,
27
29
  ): () => void {
28
30
  const isStatic = !isReadableToken(serverData)
29
31
 
@@ -9,6 +9,7 @@ import {
9
9
  subscribeToState,
10
10
  } from "atom.io/internal"
11
11
  import type { Json } from "atom.io/json"
12
+ import type { SocketKey, StandardSchemaV1 } from "atom.io/realtime"
12
13
  import { employSocket, mutexAtoms } from "atom.io/realtime"
13
14
 
14
15
  import type { ServerConfig } from "."
@@ -16,12 +17,15 @@ import type { ServerConfig } from "."
16
17
  export type StateReceiver = ReturnType<typeof realtimeStateReceiver>
17
18
  export function realtimeStateReceiver({
18
19
  socket,
20
+ userKey,
19
21
  store = IMPLICIT.STORE,
20
22
  }: ServerConfig) {
21
23
  return function stateReceiver<S extends Json.Serializable, C extends S>(
24
+ schema: StandardSchemaV1<unknown, C>,
22
25
  clientToken: WritableToken<C>,
23
26
  serverToken: WritableToken<S> = clientToken,
24
27
  ): () => void {
28
+ const socketKey = `socket::${socket.id}` satisfies SocketKey
25
29
  const mutexAtom = findInStore(store, mutexAtoms, serverToken.key)
26
30
 
27
31
  const subscriptions = new Set<() => void>()
@@ -33,8 +37,20 @@ export function realtimeStateReceiver({
33
37
  const permitPublish = () => {
34
38
  clearSubscriptions()
35
39
  subscriptions.add(
36
- employSocket(socket, `pub:${clientToken.key}`, (newValue) => {
37
- setIntoStore(store, serverToken, newValue as C)
40
+ employSocket(socket, `pub:${clientToken.key}`, async (newValue) => {
41
+ const parsed = await schema[`~standard`].validate(newValue)
42
+ if (parsed.issues) {
43
+ store.logger.error(
44
+ `❌`,
45
+ `user`,
46
+ userKey,
47
+ `attempted to publish invalid value`,
48
+ newValue,
49
+ `to state "${serverToken.key}"`,
50
+ )
51
+ return
52
+ }
53
+ setIntoStore(store, serverToken, parsed.value)
38
54
  }),
39
55
  )
40
56
  subscriptions.add(
@@ -52,7 +68,7 @@ export function realtimeStateReceiver({
52
68
  if (getFromStore(store, mutexAtom)) {
53
69
  clearSubscriptions()
54
70
  subscriptions.add(
55
- subscribeToState(store, mutexAtom, socket.id!, () => {
71
+ subscribeToState(store, mutexAtom, socketKey, () => {
56
72
  const currentValue = getFromStore(store, mutexAtom)
57
73
  if (currentValue === false) {
58
74
  operateOnStore(OWN_OP, store, mutexAtom, true)
@@ -1,8 +1,119 @@
1
+ import type { IncomingHttpHeaders } from "node:http"
2
+ import type { ParsedUrlQuery } from "node:querystring"
3
+
4
+ import { Realm } from "atom.io"
1
5
  import type { RootStore } from "atom.io/internal"
2
- import type { Socket, UserKey } from "atom.io/realtime"
6
+ import {
7
+ editRelationsInStore,
8
+ findInStore,
9
+ findRelationsInStore,
10
+ getFromStore,
11
+ IMPLICIT,
12
+ setIntoStore,
13
+ } from "atom.io/internal"
14
+ import type { Socket, SocketKey, UserKey } from "atom.io/realtime"
15
+ import type { Server } from "socket.io"
16
+
17
+ import { realtimeStateProvider } from "./realtime-state-provider"
18
+ import type { SocketSystemHierarchy } from "./server-socket-state"
19
+ import {
20
+ socketAtoms,
21
+ socketKeysAtom,
22
+ userKeysAtom,
23
+ usersOfSockets,
24
+ } from "./server-socket-state"
3
25
 
4
26
  export type ServerConfig = {
5
27
  socket: Socket
6
28
  userKey: UserKey
7
29
  store?: RootStore
8
30
  }
31
+
32
+ /** Socket Handshake details--taken from socket.io */
33
+ export type Handshake = {
34
+ /** The headers sent as part of the handshake */
35
+ headers: IncomingHttpHeaders
36
+ /** The date of creation (as string) */
37
+ time: string
38
+ /** The ip of the client */
39
+ address: string
40
+ /** Whether the connection is cross-domain */
41
+ xdomain: boolean
42
+ /** Whether the connection is secure */
43
+ secure: boolean
44
+ /** The date of creation (as unix timestamp) */
45
+ issued: number
46
+ /** The request URL string */
47
+ url: string
48
+ /** The query object */
49
+ query: ParsedUrlQuery
50
+ /** The auth object */
51
+ auth: {
52
+ [key: string]: any
53
+ }
54
+ }
55
+
56
+ export function realtime(
57
+ server: Server,
58
+ auth: (handshake: Handshake) => Error | UserKey,
59
+ onConnect: (config: ServerConfig) => () => void,
60
+ store: RootStore = IMPLICIT.STORE,
61
+ ): () => Promise<void> {
62
+ const socketRealm = new Realm<SocketSystemHierarchy>(store)
63
+
64
+ server
65
+ .use((socket, next) => {
66
+ const result = auth(socket.handshake)
67
+ if (result instanceof Error) {
68
+ next(result)
69
+ return
70
+ }
71
+ const userClaim = socketRealm.allocate(`root`, result)
72
+ const socketClaim = socketRealm.allocate(`root`, `socket::${socket.id}`)
73
+ const socketState = findInStore(store, socketAtoms, socketClaim)
74
+ setIntoStore(store, socketState, socket)
75
+ editRelationsInStore(store, usersOfSockets, (relations) => {
76
+ relations.set(userClaim, socketClaim)
77
+ })
78
+ setIntoStore(store, userKeysAtom, (index) => index.add(userClaim))
79
+ setIntoStore(store, socketKeysAtom, (index) => index.add(socketClaim))
80
+ next()
81
+ })
82
+ .on(`connection`, (socket) => {
83
+ const socketKey = `socket::${socket.id}` satisfies SocketKey
84
+ const userKeyState = findRelationsInStore(
85
+ store,
86
+ usersOfSockets,
87
+ socketKey,
88
+ ).userKeyOfSocket
89
+ const userKey = getFromStore(store, userKeyState)!
90
+ const serverConfig: ServerConfig = { store, socket, userKey }
91
+ const provideState = realtimeStateProvider(serverConfig)
92
+ const unsubFromMyUserKey = provideState(
93
+ { key: `myUserKey`, type: `atom` },
94
+ userKey,
95
+ )
96
+
97
+ const disposeServices = onConnect(serverConfig)
98
+
99
+ socket.on(`disconnect`, () => {
100
+ store.logger.info(`📡`, `socket`, socketKey, `👤 ${userKey} disconnects`)
101
+ disposeServices()
102
+ unsubFromMyUserKey()
103
+ editRelationsInStore(store, usersOfSockets, (rel) =>
104
+ rel.delete(socketKey),
105
+ )
106
+ setIntoStore(store, userKeysAtom, (keys) => (keys.delete(userKey), keys))
107
+ setIntoStore(
108
+ store,
109
+ socketKeysAtom,
110
+ (keys) => (keys.delete(socketKey), keys),
111
+ )
112
+ })
113
+ })
114
+
115
+ const disposeAll = async () => {
116
+ await server.close()
117
+ }
118
+ return disposeAll
119
+ }
@@ -2,10 +2,9 @@ import type {
2
2
  Hierarchy,
3
3
  JoinToken,
4
4
  MutableAtomToken,
5
- PureSelectorFamilyToken,
6
5
  RegularAtomFamilyToken,
7
6
  } from "atom.io"
8
- import { atomFamily, join, mutableAtom, selectorFamily } from "atom.io"
7
+ import { atomFamily, join, mutableAtom } from "atom.io"
9
8
  import type { RoomKey, Socket, SocketKey, UserKey } from "atom.io/realtime"
10
9
  import { isSocketKey, isUserKey } from "atom.io/realtime"
11
10
  import { UList } from "atom.io/transceivers/u-list"
@@ -46,9 +45,3 @@ export const usersOfSockets: JoinToken<
46
45
  isAType: isUserKey,
47
46
  isBType: isSocketKey,
48
47
  })
49
-
50
- export const selfListSelectors: PureSelectorFamilyToken<UserKey[], UserKey> =
51
- selectorFamily({
52
- key: `selfList`,
53
- get: (userKey) => () => [userKey],
54
- })
@@ -5,19 +5,10 @@ import type { RenderResult } from "@testing-library/react"
5
5
  import { prettyDOM, render } from "@testing-library/react"
6
6
  import * as AtomIO from "atom.io"
7
7
  import type { Store } from "atom.io/internal"
8
- import {
9
- clearStore,
10
- editRelationsInStore,
11
- findInStore,
12
- findRelationsInStore,
13
- getFromStore,
14
- IMPLICIT,
15
- setIntoStore,
16
- } from "atom.io/internal"
8
+ import { clearStore, IMPLICIT } from "atom.io/internal"
17
9
  import { toEntries } from "atom.io/json"
18
10
  import * as AR from "atom.io/react"
19
- import type * as RT from "atom.io/realtime"
20
- import * as RTC from "atom.io/realtime-client"
11
+ import * as RT from "atom.io/realtime"
21
12
  import * as RTR from "atom.io/realtime-react"
22
13
  import * as RTS from "atom.io/realtime-server"
23
14
  import { UList } from "atom.io/transceivers/u-list"
@@ -64,7 +55,7 @@ function prefixLogger(store: Store, prefix: string) {
64
55
  export type TestSetupOptions = {
65
56
  immortal?: { server?: boolean }
66
57
  server: (tools: {
67
- socket: SocketIO.Socket
58
+ socket: RT.Socket
68
59
  silo: AtomIO.Silo
69
60
  userKey: RT.UserKey
70
61
  enableLogging: () => void
@@ -124,8 +115,6 @@ export const setupRealtimeTestServer = (
124
115
  },
125
116
  IMPLICIT.STORE,
126
117
  )
127
- // prefixLogger(silo.store, `server`)
128
- const socketRealm = new AtomIO.Realm<RTS.SocketSystemHierarchy>(silo.store)
129
118
 
130
119
  const httpServer = http.createServer((_, res) => res.end(`Hello World!`))
131
120
  const address = httpServer.listen().address()
@@ -133,76 +122,42 @@ export const setupRealtimeTestServer = (
133
122
  typeof address === `string` ? null : address === null ? null : address.port
134
123
  if (port === null) throw new Error(`Could not determine port for test server`)
135
124
 
136
- const server = new SocketIO.Server(httpServer).use((socket, next) => {
137
- const { token, username } = socket.handshake.auth
138
- if (token === `test` && socket.id) {
139
- const userClaim = socketRealm.allocate(`root`, username as RT.UserKey)
140
- const socketClaim = socketRealm.allocate(`root`, `socket::${socket.id}`)
141
- const socketState = findInStore(silo.store, RTS.socketAtoms, socketClaim)
142
- setIntoStore(silo.store, socketState, socket)
143
- editRelationsInStore(silo.store, RTS.usersOfSockets, (relations) => {
144
- relations.set(userClaim, socketClaim)
145
- })
146
- setIntoStore(silo.store, RTS.userKeysAtom, (index) => index.add(userClaim))
147
- setIntoStore(silo.store, RTS.socketKeysAtom, (index) =>
148
- index.add(socketClaim),
149
- )
150
- next()
151
- } else {
152
- next(new Error(`Authentication error`))
153
- }
154
- })
155
-
156
- const socketServices = new Set<() => void>()
157
- const disposeAllSocketServices = () => {
158
- for (const disposeService of socketServices) disposeService()
159
- }
160
-
161
- server.on(`connection`, (socket: SocketIO.Socket) => {
162
- const userKeyState = findRelationsInStore(
163
- silo.store,
164
- RTS.usersOfSockets,
165
- `socket::${socket.id}`,
166
- ).userKeyOfSocket
167
- const userKey = getFromStore(silo.store, userKeyState)!
168
- function enableLogging() {
169
- prefixLogger(silo.store, `server`)
170
- socket.onAny((event, ...args) => {
171
- console.log(`🛰 `, userKey, event, ...args)
172
- })
173
- socket.onAnyOutgoing((event, ...args) => {
174
- console.log(`🛰 >>`, userKey, event, ...args)
175
- })
176
- socket.on(`disconnect`, () => {
177
- console.log(`${userKey} disconnected`)
125
+ const server = new SocketIO.Server(httpServer)
126
+ const dispose = RTS.realtime(
127
+ server,
128
+ (handshake) => {
129
+ const { token, username } = handshake.auth
130
+ if (RT.isUserKey(username) && token === `test`) {
131
+ return username
132
+ }
133
+ return new Error(`Authentication error`)
134
+ },
135
+ (config) => {
136
+ const { socket, userKey } = config
137
+ function enableLogging() {
138
+ prefixLogger(silo.store, `server`)
139
+ socket.onAny((event, ...args) => {
140
+ console.log(`🛰 `, userKey, event, ...args)
141
+ })
142
+ socket.onAnyOutgoing((event, ...args) => {
143
+ console.log(`🛰 >>`, userKey, event, ...args)
144
+ })
145
+ socket.on(`disconnect`, () => {
146
+ console.log(`${userKey} disconnected`)
147
+ })
148
+ }
149
+ const disposeServices = options.server({
150
+ socket: config.socket,
151
+ userKey: config.userKey,
152
+ enableLogging,
153
+ silo,
178
154
  })
179
- }
180
- const disposeServices = options.server({
181
- socket,
182
- enableLogging,
183
- silo,
184
- userKey,
185
- })
186
- if (disposeServices) {
187
- socketServices.add(disposeServices)
188
- socket.on(`disconnect`, disposeServices)
189
- }
190
- })
191
-
192
- const dispose = async () => {
193
- disposeAllSocketServices()
194
- await server.close()
195
-
196
- // const roomKeys = getFromStore(silo.store, RT.roomIndex)
197
- // for (const roomKey of roomKeys) {
198
- // const roomState = findInStore(silo.store, RTS.roomSelectors, roomKey)
199
- // const room = getFromStore(silo.store, roomState)
200
- // if (room && !(room instanceof Promise)) {
201
- // room.process.kill()
202
- // }
203
- // } // ❗ POSSIBLY STILL NEEDED
204
- silo.store.valueMap.clear()
205
- }
155
+ return () => {
156
+ disposeServices?.()
157
+ }
158
+ },
159
+ silo.store,
160
+ )
206
161
 
207
162
  return {
208
163
  name: `SERVER`,
@@ -1,3 +0,0 @@
1
- export * from "./provide-identity"
2
- export * from "./provide-rooms"
3
- export * from "./server-user-store"
@@ -1,18 +0,0 @@
1
- import { IMPLICIT } from "atom.io/internal"
2
-
3
- import { realtimeStateProvider } from "../realtime-state-provider"
4
- import type { ServerConfig } from "../server-config"
5
-
6
- export function provideIdentity({
7
- store = IMPLICIT.STORE,
8
- socket,
9
- userKey,
10
- }: ServerConfig): void {
11
- const provideState = realtimeStateProvider({ socket, store, userKey })
12
-
13
- const unsub = provideState({ key: `myUserKey`, type: `atom` }, userKey)
14
-
15
- socket.on(`disconnect`, () => {
16
- unsub()
17
- })
18
- }