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.
- package/dist/introspection/index.d.ts.map +1 -1
- package/dist/realtime/index.d.ts +6 -3
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js +9 -1
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime-server/index.d.ts +73 -50
- package/dist/realtime-server/index.d.ts.map +1 -1
- package/dist/realtime-server/index.js +222 -182
- package/dist/realtime-server/index.js.map +1 -1
- package/dist/realtime-testing/index.d.ts +1 -2
- package/dist/realtime-testing/index.d.ts.map +1 -1
- package/dist/realtime-testing/index.js +31 -53
- package/dist/realtime-testing/index.js.map +1 -1
- package/package.json +1 -1
- package/src/realtime/cast-socket.ts +1 -0
- package/src/realtime/shared-room-store.ts +15 -0
- package/src/realtime/socket-interface.ts +5 -1
- package/src/realtime-server/index.ts +3 -2
- package/src/realtime-server/ipc-sockets/custom-socket.ts +18 -2
- package/src/realtime-server/ipc-sockets/parent-socket.ts +1 -1
- package/src/realtime-server/{realtime-server-stores/provide-rooms.ts → provide-rooms.ts} +32 -57
- package/src/realtime-server/realtime-state-provider.ts +5 -3
- package/src/realtime-server/realtime-state-receiver.ts +19 -3
- package/src/realtime-server/server-config.ts +112 -1
- package/src/realtime-server/{realtime-server-stores/server-user-store.ts → server-socket-state.ts} +1 -8
- package/src/realtime-testing/setup-realtime-test.tsx +38 -83
- package/src/realtime-server/realtime-server-stores/index.ts +0 -3
- package/src/realtime-server/realtime-server-stores/provide-identity.ts +0 -18
package/package.json
CHANGED
|
@@ -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 =
|
|
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
|
|
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 =
|
|
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,
|
|
@@ -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 "
|
|
36
|
-
import { realtimeMutableFamilyProvider } from "
|
|
37
|
-
import { realtimeMutableProvider } from "
|
|
38
|
-
import type { ServerConfig } from "
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
252
|
+
visibleUsersInRoomsSelector,
|
|
272
253
|
userKey,
|
|
273
254
|
)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
283
|
-
|
|
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,
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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<
|
|
25
|
-
clientToken: AtomIO.WritableToken<
|
|
26
|
-
serverData:
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
+
}
|
package/src/realtime-server/{realtime-server-stores/server-user-store.ts → server-socket-state.ts}
RENAMED
|
@@ -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
|
|
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
|
|
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:
|
|
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)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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,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
|
-
}
|