atom.io 0.44.5 → 0.44.7
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 +5 -6
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js.map +1 -1
- package/dist/introspection/index.d.ts.map +1 -1
- package/dist/realtime/index.d.ts +4 -14
- package/dist/realtime/index.d.ts.map +1 -1
- package/dist/realtime/index.js +10 -8
- package/dist/realtime/index.js.map +1 -1
- package/dist/realtime-client/index.d.ts +6 -7
- package/dist/realtime-client/index.d.ts.map +1 -1
- package/dist/realtime-client/index.js +18 -22
- package/dist/realtime-client/index.js.map +1 -1
- package/dist/realtime-react/index.d.ts +3 -1
- package/dist/realtime-react/index.d.ts.map +1 -1
- package/dist/realtime-react/index.js +10 -6
- package/dist/realtime-react/index.js.map +1 -1
- package/dist/realtime-server/index.d.ts +40 -32
- package/dist/realtime-server/index.d.ts.map +1 -1
- package/dist/realtime-server/index.js +89 -59
- package/dist/realtime-server/index.js.map +1 -1
- package/dist/realtime-testing/index.d.ts +4 -4
- package/dist/realtime-testing/index.d.ts.map +1 -1
- package/dist/realtime-testing/index.js +5 -5
- package/dist/realtime-testing/index.js.map +1 -1
- package/dist/shared-room-store-zzjyXJv7.d.ts +28 -0
- package/dist/shared-room-store-zzjyXJv7.d.ts.map +1 -0
- package/package.json +17 -17
- package/src/internal/get-state/get-from-store.ts +3 -5
- package/src/internal/join/get-internal-relations-from-store.ts +5 -9
- package/src/internal/join/join-internal.ts +5 -2
- package/src/realtime/index.ts +1 -0
- package/src/realtime/realtime-continuity.ts +2 -1
- package/src/realtime/realtime-key-types.ts +11 -0
- package/src/realtime/shared-room-store.ts +20 -13
- package/src/realtime-client/continuity/register-and-attempt-confirmed-update.ts +5 -5
- package/src/realtime-client/realtime-client-stores/client-main-store.ts +8 -12
- package/src/realtime-client/realtime-client-stores/client-sync-store.ts +4 -4
- package/src/realtime-client/sync-continuity.ts +6 -6
- package/src/realtime-react/realtime-context.tsx +14 -5
- package/src/realtime-server/continuity/continuity-store.ts +1 -2
- package/src/realtime-server/continuity/provide-continuity.ts +2 -2
- package/src/realtime-server/continuity/provide-outcomes.ts +1 -2
- package/src/realtime-server/continuity/provide-perspectives.ts +1 -3
- package/src/realtime-server/continuity/provide-startup-payloads.ts +1 -3
- package/src/realtime-server/continuity/track-acknowledgements.ts +1 -2
- package/src/realtime-server/ipc-sockets/parent-socket.ts +1 -1
- package/src/realtime-server/realtime-mutable-family-provider.ts +12 -9
- package/src/realtime-server/realtime-server-stores/server-room-external-store.ts +127 -27
- package/src/realtime-server/realtime-server-stores/server-user-store.ts +11 -18
- package/src/realtime-testing/setup-realtime-test.tsx +7 -6
|
@@ -5,16 +5,12 @@ import type { RootStore } from "../transaction"
|
|
|
5
5
|
import { getJoin } from "./get-join"
|
|
6
6
|
|
|
7
7
|
export function getInternalRelationsFromStore<
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
A extends string,
|
|
9
|
+
B extends string,
|
|
10
10
|
>(
|
|
11
|
-
token: JoinToken<any,
|
|
11
|
+
token: JoinToken<any, A, any, B, any>,
|
|
12
12
|
store: RootStore,
|
|
13
|
-
): MutableAtomFamilyToken<UList<
|
|
13
|
+
): MutableAtomFamilyToken<UList<A> | UList<B>, A | B> {
|
|
14
14
|
const myJoin = getJoin(token, store)
|
|
15
|
-
|
|
16
|
-
UList<AName> | UList<BName>,
|
|
17
|
-
string
|
|
18
|
-
>
|
|
19
|
-
return family
|
|
15
|
+
return myJoin.relatedKeysAtoms
|
|
20
16
|
}
|
|
@@ -72,7 +72,7 @@ export class Join<
|
|
|
72
72
|
public options: JoinOptions<AName, A, BName, B, Cardinality>
|
|
73
73
|
public relations: Junction<AName, A, BName, B>
|
|
74
74
|
public states: JoinStateFamilies<AName, A, BName, B, Cardinality>
|
|
75
|
-
public relatedKeysAtoms: MutableAtomFamilyToken<UList<
|
|
75
|
+
public relatedKeysAtoms: MutableAtomFamilyToken<UList<A> | UList<B>, A | B>
|
|
76
76
|
|
|
77
77
|
public transact(
|
|
78
78
|
toolkit: WriterToolkit,
|
|
@@ -120,7 +120,10 @@ export class Join<
|
|
|
120
120
|
},
|
|
121
121
|
[`join`, `relations`],
|
|
122
122
|
)
|
|
123
|
-
this.relatedKeysAtoms = relatedKeysAtoms
|
|
123
|
+
this.relatedKeysAtoms = relatedKeysAtoms as MutableAtomFamilyToken<
|
|
124
|
+
UList<A> | UList<B>,
|
|
125
|
+
A | B
|
|
126
|
+
>
|
|
124
127
|
|
|
125
128
|
const replaceRelationsSafely: Write<
|
|
126
129
|
(a: string, newRelationsOfA: string[]) => void
|
package/src/realtime/index.ts
CHANGED
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
setEpochNumberOfContinuity,
|
|
13
13
|
} from "atom.io/internal"
|
|
14
14
|
import type { Canonical } from "atom.io/json"
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
import type { UserKey } from "./realtime-key-types"
|
|
16
17
|
|
|
17
18
|
/* eslint-disable no-console */
|
|
18
19
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type SocketKey = `socket::${string}`
|
|
2
|
+
export const isSocketKey = (key: string): key is SocketKey =>
|
|
3
|
+
key.startsWith(`socket::`)
|
|
4
|
+
|
|
5
|
+
export type UserKey = `user::${string}`
|
|
6
|
+
export const isUserKey = (key: string): key is UserKey =>
|
|
7
|
+
key.startsWith(`user::`)
|
|
8
|
+
|
|
9
|
+
export type RoomKey = `room::${string}`
|
|
10
|
+
export const isRoomKey = (key: string): key is RoomKey =>
|
|
11
|
+
key.startsWith(`room::`)
|
|
@@ -6,14 +6,21 @@ import type {
|
|
|
6
6
|
import { getInternalRelations, join, mutableAtom, selectorFamily } from "atom.io"
|
|
7
7
|
import { UList } from "atom.io/transceivers/u-list"
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
9
|
+
import {
|
|
10
|
+
isRoomKey,
|
|
11
|
+
isUserKey,
|
|
12
|
+
type RoomKey,
|
|
13
|
+
type UserKey,
|
|
14
|
+
} from "./realtime-key-types"
|
|
15
|
+
|
|
16
|
+
export type RoomSocketInterface<RoomNames extends string> = {
|
|
17
|
+
createRoom: (roomName: RoomNames) => void
|
|
18
|
+
joinRoom: (roomKey: string) => void
|
|
19
|
+
[leaveRoom: `leaveRoom:${string}`]: () => void
|
|
20
|
+
[deleteRoom: `deleteRoom:${string}`]: () => void
|
|
21
|
+
}
|
|
15
22
|
|
|
16
|
-
export const
|
|
23
|
+
export const roomKeysAtom: MutableAtomToken<UList<string>> = mutableAtom<
|
|
17
24
|
UList<string>
|
|
18
25
|
>({
|
|
19
26
|
key: `roomIndex`,
|
|
@@ -26,19 +33,19 @@ export type UserInRoomMeta = {
|
|
|
26
33
|
export const DEFAULT_USER_IN_ROOM_META: UserInRoomMeta = {
|
|
27
34
|
enteredAtEpoch: 0,
|
|
28
35
|
}
|
|
29
|
-
export const usersInRooms: JoinToken<`room`,
|
|
36
|
+
export const usersInRooms: JoinToken<`room`, RoomKey, `user`, UserKey, `1:n`> =
|
|
30
37
|
join({
|
|
31
38
|
key: `usersInRooms`,
|
|
32
39
|
between: [`room`, `user`],
|
|
33
40
|
cardinality: `1:n`,
|
|
34
|
-
isAType:
|
|
35
|
-
isBType:
|
|
41
|
+
isAType: isRoomKey,
|
|
42
|
+
isBType: isUserKey,
|
|
36
43
|
})
|
|
37
44
|
|
|
38
45
|
export const usersInMyRoomView: ReadonlyPureSelectorFamilyToken<
|
|
39
|
-
MutableAtomToken<UList<
|
|
40
|
-
|
|
41
|
-
> = selectorFamily<MutableAtomToken<UList<
|
|
46
|
+
MutableAtomToken<UList<UserKey>>[],
|
|
47
|
+
UserKey
|
|
48
|
+
> = selectorFamily<MutableAtomToken<UList<UserKey>>[], UserKey>({
|
|
42
49
|
key: `usersInMyRoomView`,
|
|
43
50
|
get:
|
|
44
51
|
(myUsername) =>
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
} from "atom.io/internal"
|
|
10
10
|
import type { Socket } from "atom.io/realtime"
|
|
11
11
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
confirmedUpdateQueueAtom,
|
|
13
|
+
optimisticUpdateQueueAtom,
|
|
14
14
|
} from "atom.io/realtime-client"
|
|
15
15
|
|
|
16
16
|
export const useRegisterAndAttemptConfirmedUpdate =
|
|
@@ -42,7 +42,7 @@ export const useRegisterAndAttemptConfirmedUpdate =
|
|
|
42
42
|
continuityKey,
|
|
43
43
|
`reconciling updates`,
|
|
44
44
|
)
|
|
45
|
-
setIntoStore(store,
|
|
45
|
+
setIntoStore(store, optimisticUpdateQueueAtom, (queue) => {
|
|
46
46
|
queue.shift()
|
|
47
47
|
return queue
|
|
48
48
|
})
|
|
@@ -172,7 +172,7 @@ export const useRegisterAndAttemptConfirmedUpdate =
|
|
|
172
172
|
`pushing confirmed update to queue`,
|
|
173
173
|
confirmed,
|
|
174
174
|
)
|
|
175
|
-
setIntoStore(store,
|
|
175
|
+
setIntoStore(store, confirmedUpdateQueueAtom, (queue) => {
|
|
176
176
|
queue.push(confirmed)
|
|
177
177
|
queue.sort((a, b) => a.epoch - b.epoch)
|
|
178
178
|
return queue
|
|
@@ -229,7 +229,7 @@ export const useRegisterAndAttemptConfirmedUpdate =
|
|
|
229
229
|
continuityKey,
|
|
230
230
|
`pushing confirmed update #${confirmed.epoch} to queue`,
|
|
231
231
|
)
|
|
232
|
-
setIntoStore(store,
|
|
232
|
+
setIntoStore(store, confirmedUpdateQueueAtom, (queue) => {
|
|
233
233
|
queue.push(confirmed)
|
|
234
234
|
queue.sort((a, b) => a.epoch - b.epoch)
|
|
235
235
|
return queue
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
import * as AtomIO from "atom.io"
|
|
2
|
+
import type { SocketKey, UserKey } from "atom.io/realtime"
|
|
2
3
|
import { storageSync } from "atom.io/web"
|
|
3
4
|
|
|
4
|
-
export const
|
|
5
|
-
AtomIO.atom<
|
|
6
|
-
key: `
|
|
5
|
+
export const mySocketKeyAtom: AtomIO.RegularAtomToken<SocketKey | undefined> =
|
|
6
|
+
AtomIO.atom<SocketKey | undefined>({
|
|
7
|
+
key: `mySocketKey`,
|
|
7
8
|
default: undefined,
|
|
8
9
|
})
|
|
9
|
-
export const myIdState: AtomIO.ReadonlyPureSelectorToken<string | undefined> =
|
|
10
|
-
AtomIO.selector<string | undefined>({
|
|
11
|
-
key: `mySocketId`,
|
|
12
|
-
get: ({ get }) => get(myIdState__INTERNAL),
|
|
13
|
-
})
|
|
14
10
|
|
|
15
|
-
export const
|
|
16
|
-
AtomIO.atom<
|
|
17
|
-
key: `
|
|
11
|
+
export const myUserKeyAtom: AtomIO.RegularAtomToken<UserKey | null> =
|
|
12
|
+
AtomIO.atom<UserKey | null>({
|
|
13
|
+
key: `myUserKey`,
|
|
18
14
|
default: null,
|
|
19
|
-
effects: [storageSync(globalThis.localStorage, JSON, `
|
|
15
|
+
effects: [storageSync(globalThis.localStorage, JSON, `myUserKey`)],
|
|
20
16
|
})
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import * as AtomIO from "atom.io"
|
|
2
2
|
|
|
3
|
-
export const
|
|
3
|
+
export const optimisticUpdateQueueAtom: AtomIO.RegularAtomToken<
|
|
4
4
|
AtomIO.TransactionOutcomeEvent<any>[]
|
|
5
5
|
> = AtomIO.atom<AtomIO.TransactionOutcomeEvent<any>[]>({
|
|
6
|
-
key: `
|
|
6
|
+
key: `optimisticUpdateQueue`,
|
|
7
7
|
default: () => [],
|
|
8
8
|
})
|
|
9
9
|
|
|
10
|
-
export const
|
|
10
|
+
export const confirmedUpdateQueueAtom: AtomIO.RegularAtomToken<
|
|
11
11
|
AtomIO.TransactionOutcomeEvent<any>[]
|
|
12
12
|
> = AtomIO.atom<AtomIO.TransactionOutcomeEvent<any>[]>({
|
|
13
|
-
key: `
|
|
13
|
+
key: `confirmedUpdateQueue`,
|
|
14
14
|
default: () => [],
|
|
15
15
|
})
|
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
import type { Json } from "atom.io/json"
|
|
11
11
|
import type { ContinuityToken } from "atom.io/realtime"
|
|
12
12
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
confirmedUpdateQueueAtom,
|
|
14
|
+
optimisticUpdateQueueAtom,
|
|
15
15
|
} from "atom.io/realtime-client"
|
|
16
16
|
import type { Socket } from "socket.io-client"
|
|
17
17
|
|
|
@@ -25,8 +25,8 @@ export function syncContinuity(
|
|
|
25
25
|
continuity: ContinuityToken,
|
|
26
26
|
): () => void {
|
|
27
27
|
const continuityKey = continuity.key
|
|
28
|
-
const optimisticUpdates = getFromStore(store,
|
|
29
|
-
const confirmedUpdates = getFromStore(store,
|
|
28
|
+
const optimisticUpdates = getFromStore(store, optimisticUpdateQueueAtom)
|
|
29
|
+
const confirmedUpdates = getFromStore(store, confirmedUpdateQueueAtom)
|
|
30
30
|
|
|
31
31
|
const initializeContinuity = (epoch: number, payload: Json.Array) => {
|
|
32
32
|
socket.off(`continuity-init:${continuityKey}`, initializeContinuity)
|
|
@@ -83,7 +83,7 @@ export function syncContinuity(
|
|
|
83
83
|
continuityKey,
|
|
84
84
|
`enqueuing new optimistic update`,
|
|
85
85
|
)
|
|
86
|
-
setIntoStore(store,
|
|
86
|
+
setIntoStore(store, optimisticUpdateQueueAtom, (queue) => {
|
|
87
87
|
queue.push(clientUpdate)
|
|
88
88
|
queue.sort((a, b) => a.epoch - b.epoch)
|
|
89
89
|
return queue
|
|
@@ -95,7 +95,7 @@ export function syncContinuity(
|
|
|
95
95
|
continuityKey,
|
|
96
96
|
`replacing existing optimistic update at index ${optimisticUpdateIndex}`,
|
|
97
97
|
)
|
|
98
|
-
setIntoStore(store,
|
|
98
|
+
setIntoStore(store, optimisticUpdateQueueAtom, (queue) => {
|
|
99
99
|
queue[optimisticUpdateIndex] = clientUpdate
|
|
100
100
|
return queue
|
|
101
101
|
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useI } from "atom.io/react"
|
|
2
|
+
import type { RoomSocketInterface } from "atom.io/realtime/shared-room-store"
|
|
2
3
|
import * as RTC from "atom.io/realtime-client"
|
|
3
4
|
import * as React from "react"
|
|
4
5
|
import type { Socket } from "socket.io-client"
|
|
@@ -26,19 +27,27 @@ export const RealtimeProvider: React.FC<{
|
|
|
26
27
|
const services = React.useRef(
|
|
27
28
|
new Map<string, RealtimeServiceCounter>(),
|
|
28
29
|
).current
|
|
29
|
-
const
|
|
30
|
+
const setMySocketKey = useI(RTC.mySocketKeyAtom)
|
|
30
31
|
React.useEffect(() => {
|
|
31
|
-
|
|
32
|
+
setMySocketKey(socket?.id ? `socket::${socket.id}` : undefined)
|
|
32
33
|
socket?.on(`connect`, () => {
|
|
33
|
-
|
|
34
|
+
setMySocketKey(socket?.id ? `socket::${socket.id}` : undefined)
|
|
34
35
|
})
|
|
35
36
|
socket?.on(`disconnect`, () => {
|
|
36
|
-
|
|
37
|
+
setMySocketKey(undefined)
|
|
37
38
|
})
|
|
38
|
-
}, [socket,
|
|
39
|
+
}, [socket, setMySocketKey])
|
|
39
40
|
return (
|
|
40
41
|
<RealtimeContext.Provider value={{ socket, services }}>
|
|
41
42
|
{children}
|
|
42
43
|
</RealtimeContext.Provider>
|
|
43
44
|
)
|
|
44
45
|
}
|
|
46
|
+
|
|
47
|
+
export function useRealtimeRooms<RoomNames extends string>(): Socket<
|
|
48
|
+
{},
|
|
49
|
+
RoomSocketInterface<RoomNames>
|
|
50
|
+
> {
|
|
51
|
+
const { socket } = React.useContext(RealtimeContext)
|
|
52
|
+
return socket as Socket<{}, RoomSocketInterface<RoomNames>>
|
|
53
|
+
}
|
|
@@ -4,8 +4,7 @@ import type {
|
|
|
4
4
|
TransactionSubEvent,
|
|
5
5
|
} from "atom.io"
|
|
6
6
|
import { atomFamily } from "atom.io"
|
|
7
|
-
|
|
8
|
-
import type { UserKey } from "../realtime-server-stores"
|
|
7
|
+
import type { UserKey } from "atom.io/realtime"
|
|
9
8
|
|
|
10
9
|
export function redactTransactionUpdateContent(
|
|
11
10
|
visibleStateKeys: string[],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getFromStore, IMPLICIT } from "atom.io/internal"
|
|
2
2
|
import type { Json } from "atom.io/json"
|
|
3
|
-
import type { ContinuityToken } from "atom.io/realtime"
|
|
3
|
+
import type { ContinuityToken, UserKey } from "atom.io/realtime"
|
|
4
4
|
|
|
5
|
-
import type { ServerConfig
|
|
5
|
+
import type { ServerConfig } from ".."
|
|
6
6
|
import { unacknowledgedUpdatesAtoms } from "./continuity-store"
|
|
7
7
|
import { provideOutcomes } from "./provide-outcomes"
|
|
8
8
|
import { providePerspectives } from "./provide-perspectives"
|
|
@@ -7,9 +7,8 @@ import {
|
|
|
7
7
|
subscribeToTransaction,
|
|
8
8
|
} from "atom.io/internal"
|
|
9
9
|
import type { Json } from "atom.io/json"
|
|
10
|
-
import type { ContinuityToken, Socket } from "atom.io/realtime"
|
|
10
|
+
import type { ContinuityToken, Socket, UserKey } from "atom.io/realtime"
|
|
11
11
|
|
|
12
|
-
import type { UserKey } from ".."
|
|
13
12
|
import {
|
|
14
13
|
redactTransactionUpdateContent,
|
|
15
14
|
unacknowledgedUpdatesAtoms,
|
|
@@ -5,9 +5,7 @@ import {
|
|
|
5
5
|
getJsonToken,
|
|
6
6
|
subscribeToState,
|
|
7
7
|
} from "atom.io/internal"
|
|
8
|
-
import type { ContinuityToken, Socket } from "atom.io/realtime"
|
|
9
|
-
|
|
10
|
-
import type { UserKey } from "../realtime-server-stores"
|
|
8
|
+
import type { ContinuityToken, Socket, UserKey } from "atom.io/realtime"
|
|
11
9
|
|
|
12
10
|
export function providePerspectives(
|
|
13
11
|
store: Store,
|
|
@@ -6,11 +6,9 @@ import {
|
|
|
6
6
|
isRootStore,
|
|
7
7
|
} from "atom.io/internal"
|
|
8
8
|
import type { Json } from "atom.io/json"
|
|
9
|
-
import type { ContinuityToken, Socket } from "atom.io/realtime"
|
|
9
|
+
import type { ContinuityToken, Socket, UserKey } from "atom.io/realtime"
|
|
10
10
|
import { employSocket } from "atom.io/realtime"
|
|
11
11
|
|
|
12
|
-
import type { UserKey } from ".."
|
|
13
|
-
|
|
14
12
|
export function provideStartupPayloads(
|
|
15
13
|
store: Store,
|
|
16
14
|
socket: Socket,
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { Store } from "atom.io/internal"
|
|
2
2
|
import { getFromStore, setIntoStore } from "atom.io/internal"
|
|
3
|
-
import type { ContinuityToken, Socket } from "atom.io/realtime"
|
|
3
|
+
import type { ContinuityToken, Socket, UserKey } from "atom.io/realtime"
|
|
4
4
|
import { employSocket } from "atom.io/realtime"
|
|
5
5
|
|
|
6
|
-
import type { UserKey } from "../realtime-server-stores"
|
|
7
6
|
import { unacknowledgedUpdatesAtoms } from "./continuity-store"
|
|
8
7
|
|
|
9
8
|
export function trackAcknowledgements(
|
|
@@ -3,9 +3,9 @@ import type { Readable, Writable } from "node:stream"
|
|
|
3
3
|
import { Subject } from "atom.io/internal"
|
|
4
4
|
import type { Json } from "atom.io/json"
|
|
5
5
|
import { parseJson, stringifyJson } from "atom.io/json"
|
|
6
|
+
import type { UserKey } from "atom.io/realtime"
|
|
6
7
|
import { UList } from "atom.io/transceivers/u-list"
|
|
7
8
|
|
|
8
|
-
import type { UserKey } from "../realtime-server-stores"
|
|
9
9
|
import type { StderrLog } from "./child-socket"
|
|
10
10
|
import type { EventBuffer, EventPayload, Events } from "./custom-socket"
|
|
11
11
|
import { CustomSocket } from "./custom-socket"
|
|
@@ -14,6 +14,18 @@ import { employSocket } from "atom.io/realtime"
|
|
|
14
14
|
|
|
15
15
|
import type { ServerConfig } from "."
|
|
16
16
|
|
|
17
|
+
const isAvailable = <K extends Canonical>(
|
|
18
|
+
exposedSubKeys: Iterable<K>,
|
|
19
|
+
subKey: K,
|
|
20
|
+
): boolean => {
|
|
21
|
+
for (const exposedSubKey of exposedSubKeys) {
|
|
22
|
+
if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) {
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
|
|
17
29
|
export type MutableFamilyProvider = ReturnType<
|
|
18
30
|
typeof realtimeMutableFamilyProvider
|
|
19
31
|
>
|
|
@@ -78,15 +90,6 @@ export function realtimeMutableFamilyProvider({
|
|
|
78
90
|
)
|
|
79
91
|
}
|
|
80
92
|
|
|
81
|
-
const isAvailable = (exposedSubKeys: Iterable<K>, subKey: K): boolean => {
|
|
82
|
-
for (const exposedSubKey of exposedSubKeys) {
|
|
83
|
-
if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) {
|
|
84
|
-
return true
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return false
|
|
88
|
-
}
|
|
89
|
-
|
|
90
93
|
const start = () => {
|
|
91
94
|
coreSubscriptions.add(
|
|
92
95
|
employSocket(socket, `sub:${family.key}`, (subKey: K) => {
|
|
@@ -2,23 +2,50 @@ import type { ChildProcessWithoutNullStreams } from "node:child_process"
|
|
|
2
2
|
import { spawn } from "node:child_process"
|
|
3
3
|
|
|
4
4
|
import type { Store } from "atom.io/internal"
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
editRelationsInStore,
|
|
7
|
+
findInStore,
|
|
8
|
+
findRelationsInStore,
|
|
9
|
+
getFromStore,
|
|
10
|
+
getInternalRelationsFromStore,
|
|
11
|
+
IMPLICIT,
|
|
12
|
+
setIntoStore,
|
|
13
|
+
} from "atom.io/internal"
|
|
6
14
|
import type { Json } from "atom.io/json"
|
|
7
|
-
import type { Socket } from "atom.io/realtime"
|
|
8
|
-
import {
|
|
15
|
+
import type { RoomKey, Socket, SocketKey, UserKey } from "atom.io/realtime"
|
|
16
|
+
import { roomKeysAtom, usersInRooms } from "atom.io/realtime"
|
|
9
17
|
|
|
10
18
|
import { ChildSocket } from "../ipc-sockets"
|
|
19
|
+
import { realtimeMutableFamilyProvider } from "../realtime-mutable-family-provider"
|
|
20
|
+
import { realtimeMutableProvider } from "../realtime-mutable-provider"
|
|
21
|
+
import type { ServerConfig } from "../server-config"
|
|
22
|
+
import {
|
|
23
|
+
selfListSelectors,
|
|
24
|
+
socketKeysAtom,
|
|
25
|
+
userKeysAtom,
|
|
26
|
+
usersOfSockets,
|
|
27
|
+
} from "./server-user-store"
|
|
11
28
|
|
|
12
|
-
export
|
|
29
|
+
export type RoomMap = Map<
|
|
13
30
|
string,
|
|
14
31
|
ChildSocket<any, any, ChildProcessWithoutNullStreams>
|
|
15
|
-
>
|
|
32
|
+
>
|
|
33
|
+
|
|
34
|
+
declare global {
|
|
35
|
+
var ATOM_IO_REALTIME_SERVER_ROOMS: RoomMap
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const ROOMS: RoomMap =
|
|
39
|
+
globalThis.ATOM_IO_REALTIME_SERVER_ROOMS ??
|
|
40
|
+
(globalThis.ATOM_IO_REALTIME_SERVER_ROOMS = new Map())
|
|
41
|
+
|
|
42
|
+
export const roomMeta: { count: number } = { count: 0 }
|
|
16
43
|
|
|
17
44
|
export async function spawnRoom(
|
|
18
|
-
|
|
45
|
+
store: Store,
|
|
46
|
+
roomKey: RoomKey,
|
|
19
47
|
command: string,
|
|
20
48
|
args: string[],
|
|
21
|
-
store: Store,
|
|
22
49
|
): Promise<ChildSocket<any, any>> {
|
|
23
50
|
const child = await new Promise<ChildProcessWithoutNullStreams>((resolve) => {
|
|
24
51
|
const room = spawn(command, args, { env: process.env })
|
|
@@ -30,22 +57,22 @@ export async function spawnRoom(
|
|
|
30
57
|
}
|
|
31
58
|
room.stdout.on(`data`, resolver)
|
|
32
59
|
})
|
|
33
|
-
const roomSocket = new ChildSocket(child,
|
|
34
|
-
ROOMS.set(
|
|
35
|
-
setIntoStore(store,
|
|
60
|
+
const roomSocket = new ChildSocket(child, roomKey)
|
|
61
|
+
ROOMS.set(roomKey, roomSocket)
|
|
62
|
+
setIntoStore(store, roomKeysAtom, (index) => (index.add(roomKey), index))
|
|
36
63
|
|
|
37
64
|
roomSocket.on(`close`, () => {
|
|
38
|
-
destroyRoom(
|
|
65
|
+
destroyRoom(store, roomKey)
|
|
39
66
|
})
|
|
40
67
|
|
|
41
68
|
return roomSocket
|
|
42
69
|
}
|
|
43
70
|
|
|
44
71
|
export function joinRoom(
|
|
45
|
-
roomId: string,
|
|
46
|
-
userId: string,
|
|
47
|
-
socket: Socket,
|
|
48
72
|
store: Store,
|
|
73
|
+
roomKey: RoomKey,
|
|
74
|
+
userKey: UserKey,
|
|
75
|
+
socket: Socket,
|
|
49
76
|
): {
|
|
50
77
|
leave: () => void
|
|
51
78
|
roomSocket: ChildSocket<any, any, ChildProcessWithoutNullStreams>
|
|
@@ -63,22 +90,22 @@ export function joinRoom(
|
|
|
63
90
|
editRelationsInStore(
|
|
64
91
|
usersInRooms,
|
|
65
92
|
(relations) => {
|
|
66
|
-
relations.set({ room:
|
|
93
|
+
relations.set({ room: roomKey, user: userKey })
|
|
67
94
|
},
|
|
68
95
|
store,
|
|
69
96
|
)
|
|
70
|
-
const roomSocket = ROOMS.get(
|
|
97
|
+
const roomSocket = ROOMS.get(roomKey)
|
|
71
98
|
if (!roomSocket) {
|
|
72
|
-
store.logger.error(`❌`, `unknown`,
|
|
99
|
+
store.logger.error(`❌`, `unknown`, roomKey, `no room found with this id`)
|
|
73
100
|
return null
|
|
74
101
|
}
|
|
75
102
|
roomSocket.onAny((...payload) => {
|
|
76
103
|
socket.emit(...payload)
|
|
77
104
|
})
|
|
78
|
-
roomSocket.emit(`user-joins`,
|
|
105
|
+
roomSocket.emit(`user-joins`, userKey)
|
|
79
106
|
|
|
80
107
|
toRoom = (payload) => {
|
|
81
|
-
roomSocket.emit(`user::${
|
|
108
|
+
roomSocket.emit(`user::${userKey}`, ...payload)
|
|
82
109
|
}
|
|
83
110
|
while (roomQueue.length > 0) {
|
|
84
111
|
const payload = roomQueue.shift()
|
|
@@ -88,34 +115,107 @@ export function joinRoom(
|
|
|
88
115
|
const leave = () => {
|
|
89
116
|
socket.offAny(forward)
|
|
90
117
|
toRoom([`user-leaves`])
|
|
91
|
-
leaveRoom(
|
|
118
|
+
leaveRoom(store, roomKey, userKey)
|
|
92
119
|
}
|
|
93
120
|
|
|
94
121
|
return { leave, roomSocket }
|
|
95
122
|
}
|
|
96
123
|
|
|
97
|
-
export function leaveRoom(
|
|
124
|
+
export function leaveRoom(
|
|
125
|
+
store: Store,
|
|
126
|
+
roomKey: RoomKey,
|
|
127
|
+
userKey: UserKey,
|
|
128
|
+
): void {
|
|
98
129
|
editRelationsInStore(
|
|
99
130
|
usersInRooms,
|
|
100
131
|
(relations) => {
|
|
101
|
-
relations.delete({ room:
|
|
132
|
+
relations.delete({ room: roomKey, user: userKey })
|
|
102
133
|
},
|
|
103
134
|
store,
|
|
104
135
|
)
|
|
105
136
|
}
|
|
106
137
|
|
|
107
|
-
export function destroyRoom(
|
|
138
|
+
export function destroyRoom(store: Store, roomKey: RoomKey): void {
|
|
139
|
+
setIntoStore(store, roomKeysAtom, (s) => (s.delete(roomKey), s))
|
|
108
140
|
editRelationsInStore(
|
|
109
141
|
usersInRooms,
|
|
110
142
|
(relations) => {
|
|
111
|
-
relations.delete({ room:
|
|
143
|
+
relations.delete({ room: roomKey })
|
|
112
144
|
},
|
|
113
145
|
store,
|
|
114
146
|
)
|
|
115
|
-
|
|
116
|
-
const room = ROOMS.get(roomId)
|
|
147
|
+
const room = ROOMS.get(roomKey)
|
|
117
148
|
if (room) {
|
|
118
149
|
room.emit(`exit`)
|
|
119
|
-
ROOMS.delete(
|
|
150
|
+
ROOMS.delete(roomKey)
|
|
120
151
|
}
|
|
121
152
|
}
|
|
153
|
+
|
|
154
|
+
export function provideRooms<RoomNames extends string>(
|
|
155
|
+
{ store = IMPLICIT.STORE, socket }: ServerConfig,
|
|
156
|
+
resolveRoomScript: (path: string) => [string, string[]],
|
|
157
|
+
): void {
|
|
158
|
+
const socketKey = `socket::${socket.id}` satisfies SocketKey
|
|
159
|
+
const userKey = getFromStore(
|
|
160
|
+
store,
|
|
161
|
+
findRelationsInStore(usersOfSockets, socketKey, store).userKeyOfSocket,
|
|
162
|
+
)!
|
|
163
|
+
|
|
164
|
+
const exposeMutable = realtimeMutableProvider({ socket, store })
|
|
165
|
+
const exposeMutableFamily = realtimeMutableFamilyProvider({
|
|
166
|
+
socket,
|
|
167
|
+
store,
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
exposeMutable(roomKeysAtom)
|
|
171
|
+
|
|
172
|
+
const usersInRoomsAtoms = getInternalRelationsFromStore(usersInRooms, store)
|
|
173
|
+
const usersWhoseRoomsCanBeSeenSelector = findInStore(
|
|
174
|
+
store,
|
|
175
|
+
selfListSelectors,
|
|
176
|
+
userKey,
|
|
177
|
+
)
|
|
178
|
+
exposeMutableFamily(usersInRoomsAtoms, usersWhoseRoomsCanBeSeenSelector)
|
|
179
|
+
const usersOfSocketsAtoms = getInternalRelationsFromStore(
|
|
180
|
+
usersOfSockets,
|
|
181
|
+
store,
|
|
182
|
+
)
|
|
183
|
+
exposeMutableFamily(usersOfSocketsAtoms, socketKeysAtom)
|
|
184
|
+
|
|
185
|
+
socket.on(`createRoom`, async (roomName: RoomNames) => {
|
|
186
|
+
// logger.info(`[${shortId}]:${username}`, `creating room "${roomId}"`)
|
|
187
|
+
const roomId = `room::${roomMeta.count++}` satisfies RoomKey
|
|
188
|
+
await spawnRoom(store, roomId, ...resolveRoomScript(roomName))
|
|
189
|
+
socket.on(`deleteRoom:${roomId}`, () => {
|
|
190
|
+
// logger.info(`[${shortId}]:${username}`, `deleting room "${roomId}"`)
|
|
191
|
+
destroyRoom(store, roomId)
|
|
192
|
+
})
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
socket.on(`joinRoom`, (roomKey: RoomKey) => {
|
|
196
|
+
// logger.info(`[${shortId}]:${username}`, `joining room "${roomId}"`)
|
|
197
|
+
const { leave } = joinRoom(store, roomKey, userKey, socket)!
|
|
198
|
+
socket.on(`leaveRoom:${roomKey}`, leave)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
socket.on(`disconnect`, () => {
|
|
202
|
+
editRelationsInStore(
|
|
203
|
+
usersOfSockets,
|
|
204
|
+
(relations) => relations.delete(socketKey),
|
|
205
|
+
store,
|
|
206
|
+
)
|
|
207
|
+
if (userKey) {
|
|
208
|
+
setIntoStore(
|
|
209
|
+
store,
|
|
210
|
+
userKeysAtom,
|
|
211
|
+
(index) => (index.delete(userKey), index),
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
setIntoStore(
|
|
215
|
+
store,
|
|
216
|
+
socketKeysAtom,
|
|
217
|
+
(index) => (index.delete(socketKey), index),
|
|
218
|
+
)
|
|
219
|
+
// logger.info(`${socket.id} disconnected`)
|
|
220
|
+
})
|
|
221
|
+
}
|