atom.io 0.46.5 → 0.46.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.
Files changed (42) hide show
  1. package/dist/realtime/index.d.ts +7 -6
  2. package/dist/realtime/index.d.ts.map +1 -1
  3. package/dist/realtime/index.js +19 -10
  4. package/dist/realtime/index.js.map +1 -1
  5. package/dist/realtime-client/index.d.ts +13 -13
  6. package/dist/realtime-client/index.d.ts.map +1 -1
  7. package/dist/realtime-client/index.js +10 -3
  8. package/dist/realtime-client/index.js.map +1 -1
  9. package/dist/realtime-server/index.d.ts +49 -41
  10. package/dist/realtime-server/index.d.ts.map +1 -1
  11. package/dist/realtime-server/index.js +126 -97
  12. package/dist/realtime-server/index.js.map +1 -1
  13. package/dist/realtime-testing/index.js +2 -2
  14. package/dist/realtime-testing/index.js.map +1 -1
  15. package/package.json +18 -17
  16. package/src/realtime/realtime-continuity.ts +2 -2
  17. package/src/realtime/shared-room-store.ts +38 -17
  18. package/src/realtime/socket-interface.ts +1 -1
  19. package/src/realtime-client/continuity/register-and-attempt-confirmed-update.ts +3 -1
  20. package/src/realtime-client/continuity/use-conceal-state.ts +2 -1
  21. package/src/realtime-client/pull-atom-family-member.ts +1 -1
  22. package/src/realtime-client/pull-atom.ts +1 -1
  23. package/src/realtime-client/pull-mutable-atom-family-member.ts +1 -1
  24. package/src/realtime-client/pull-mutable-atom.ts +1 -1
  25. package/src/realtime-client/pull-selector-family-member.ts +1 -1
  26. package/src/realtime-client/pull-selector-roots.ts +1 -1
  27. package/src/realtime-client/pull-selector.ts +1 -1
  28. package/src/realtime-client/push-state.ts +1 -1
  29. package/src/realtime-client/realtime-client-stores/client-main-store.ts +16 -3
  30. package/src/realtime-client/sync-continuity.ts +1 -2
  31. package/src/realtime-server/continuity/provide-outcomes.ts +1 -1
  32. package/src/realtime-server/ipc-sockets/child-socket.ts +6 -9
  33. package/src/realtime-server/ipc-sockets/parent-socket.ts +1 -8
  34. package/src/realtime-server/provide-rooms.ts +64 -16
  35. package/src/realtime-server/realtime-family-provider.ts +51 -35
  36. package/src/realtime-server/realtime-mutable-family-provider.ts +50 -34
  37. package/src/realtime-server/realtime-mutable-provider.ts +4 -4
  38. package/src/realtime-server/realtime-state-provider.ts +7 -7
  39. package/src/realtime-server/realtime-state-receiver.ts +2 -2
  40. package/src/realtime-server/server-config.ts +20 -13
  41. package/src/realtime-server/server-socket-state.ts +3 -3
  42. package/src/realtime-testing/setup-realtime-test.tsx +2 -2
@@ -23,7 +23,7 @@ export type RoomSocketInterface<RoomNames extends string> = {
23
23
  }
24
24
 
25
25
  export const roomKeysAtom: MutableAtomToken<UList<RoomKey>> = mutableAtom({
26
- key: `roomIndex`,
26
+ key: `roomKeys`,
27
27
  class: UList,
28
28
  })
29
29
 
@@ -43,10 +43,10 @@ export const usersInRooms: JoinToken<`room`, RoomKey, `user`, UserKey, `1:n`> =
43
43
  })
44
44
 
45
45
  export const visibleUsersInRoomsSelector: PureSelectorFamilyToken<
46
- (RoomKey | UserKey)[],
46
+ [self: UserKey, ...RoomKey[]],
47
47
  UserKey
48
48
  > = selectorFamily({
49
- key: `selfList`,
49
+ key: `visibleUsersInRooms`,
50
50
  get:
51
51
  (userKey) =>
52
52
  ({ get }) => {
@@ -56,6 +56,41 @@ export const visibleUsersInRoomsSelector: PureSelectorFamilyToken<
56
56
  },
57
57
  })
58
58
 
59
+ export const visibilityFromRoomSelector: PureSelectorFamilyToken<
60
+ [self: RoomKey, ...UserKey[]],
61
+ RoomKey
62
+ > = selectorFamily({
63
+ key: `visibilityFromRoom`,
64
+ get:
65
+ (roomKey) =>
66
+ ({ get }) => {
67
+ const [usersOfRoomsAtoms] = getInternalRelations(usersInRooms, `split`)
68
+ const users = get(usersOfRoomsAtoms, roomKey)
69
+ return [roomKey, ...users]
70
+ },
71
+ })
72
+
73
+ export const mutualUsersSelector: ReadonlyPureSelectorFamilyToken<
74
+ UserKey[],
75
+ UserKey
76
+ > = selectorFamily({
77
+ key: `mutualUsers`,
78
+ get:
79
+ (userKey) =>
80
+ ({ get }) => {
81
+ const [usersOfRoomsAtoms, roomsOfUsersAtoms] = getInternalRelations(
82
+ usersInRooms,
83
+ `split`,
84
+ )
85
+ const rooms = get(roomsOfUsersAtoms, userKey)
86
+ for (const room of rooms) {
87
+ const users = get(usersOfRoomsAtoms, room)
88
+ return [...users]
89
+ }
90
+ return []
91
+ },
92
+ })
93
+
59
94
  export const ownersOfRooms: JoinToken<`user`, UserKey, `room`, RoomKey, `1:n`> =
60
95
  join({
61
96
  key: `ownersOfRooms`,
@@ -64,17 +99,3 @@ export const ownersOfRooms: JoinToken<`user`, UserKey, `room`, RoomKey, `1:n`> =
64
99
  isAType: isUserKey,
65
100
  isBType: isRoomKey,
66
101
  })
67
-
68
- export const usersInMyRoomView: ReadonlyPureSelectorFamilyToken<
69
- MutableAtomToken<UList<RoomKey>>[],
70
- UserKey
71
- > = selectorFamily({
72
- key: `usersInMyRoomView`,
73
- get:
74
- (myUsername) =>
75
- ({ find }) => {
76
- const [, roomsOfUsersAtoms] = getInternalRelations(usersInRooms, `split`)
77
- const myRoomIndex = find(roomsOfUsersAtoms, myUsername)
78
- return [myRoomIndex]
79
- },
80
- })
@@ -48,7 +48,7 @@ export type Socket = {
48
48
  onAnyOutgoing: (
49
49
  listener: (event: string, ...args: Json.Serializable[]) => void,
50
50
  ) => void
51
- off: (event: string, listener: (...args: Json.Serializable[]) => void) => void
51
+ off: (event: string, listener?: (...args: Json.Serializable[]) => void) => void
52
52
  offAny: (
53
53
  listener: (event: string, ...args: Json.Serializable[]) => void,
54
54
  ) => void
@@ -7,6 +7,7 @@ import {
7
7
  setEpochNumberOfContinuity,
8
8
  setIntoStore,
9
9
  } from "atom.io/internal"
10
+ import type { Json } from "atom.io/json"
10
11
  import type { Socket } from "atom.io/realtime"
11
12
 
12
13
  import {
@@ -27,7 +28,8 @@ export const useRegisterAndAttemptConfirmedUpdate =
27
28
  >[],
28
29
  ) =>
29
30
  (
30
- confirmed: AtomIO.TransactionOutcomeEvent<AtomIO.TransactionToken<Fn>>,
31
+ confirmed: AtomIO.TransactionOutcomeEvent<AtomIO.TransactionToken<Fn>> &
32
+ Json.Serializable,
31
33
  ): void => {
32
34
  function reconcileEpoch(
33
35
  optimisticUpdate: AtomIO.TransactionOutcomeEvent<
@@ -1,9 +1,10 @@
1
1
  import type { AtomToken } from "atom.io"
2
2
  import type { Store } from "atom.io/internal"
3
3
  import { disposeAtom } from "atom.io/internal"
4
+ import type { Json } from "atom.io/json"
4
5
 
5
6
  export function useConcealState(store: Store) {
6
- return (concealed: AtomToken<unknown>[]): void => {
7
+ return (concealed: AtomToken<Json.Serializable>[]): void => {
7
8
  for (const token of concealed) {
8
9
  disposeAtom(store, token)
9
10
  }
@@ -1,7 +1,7 @@
1
1
  import type * as AtomIO from "atom.io"
2
2
  import { findInStore, setIntoStore, type Store } from "atom.io/internal"
3
3
  import type { Canonical, Json } from "atom.io/json"
4
- import type { Socket } from "socket.io-client"
4
+ import type { Socket } from "atom.io/realtime"
5
5
 
6
6
  export function pullAtomFamilyMember<
7
7
  J extends Json.Serializable,
@@ -1,7 +1,7 @@
1
1
  import type * as AtomIO from "atom.io"
2
2
  import { setIntoStore, type Store } from "atom.io/internal"
3
3
  import type { Json } from "atom.io/json"
4
- import type { Socket } from "socket.io-client"
4
+ import type { Socket } from "atom.io/realtime"
5
5
 
6
6
  export function pullAtom<J extends Json.Serializable>(
7
7
  store: Store,
@@ -7,7 +7,7 @@ import {
7
7
  setIntoStore,
8
8
  } from "atom.io/internal"
9
9
  import type { Canonical } from "atom.io/json"
10
- import type { Socket } from "socket.io-client"
10
+ import type { Socket } from "atom.io/realtime"
11
11
 
12
12
  export function pullMutableAtomFamilyMember<
13
13
  T extends Transceiver<any, any, any>,
@@ -1,7 +1,7 @@
1
1
  import type * as AtomIO from "atom.io"
2
2
  import type { AsJSON, SignalFrom, Store, Transceiver } from "atom.io/internal"
3
3
  import { getJsonToken, getUpdateToken, setIntoStore } from "atom.io/internal"
4
- import type { Socket } from "socket.io-client"
4
+ import type { Socket } from "atom.io/realtime"
5
5
 
6
6
  export function pullMutableAtom<T extends Transceiver<any, any, any>>(
7
7
  store: Store,
@@ -2,7 +2,7 @@ import type * as AtomIO from "atom.io"
2
2
  import type { Store } from "atom.io/internal"
3
3
  import { findInStore } from "atom.io/internal"
4
4
  import type { Canonical } from "atom.io/json"
5
- import type { Socket } from "socket.io-client"
5
+ import type { Socket } from "atom.io/realtime"
6
6
 
7
7
  import { pullSelectorRoots } from "./pull-selector-roots"
8
8
 
@@ -2,7 +2,7 @@ import type { AtomToken, SelectorToken } from "atom.io"
2
2
  import type { Store } from "atom.io/internal"
3
3
  import { getFamilyOfToken, subscribeToState } from "atom.io/internal"
4
4
  import { parseJson } from "atom.io/json"
5
- import type { Socket } from "socket.io-client"
5
+ import type { Socket } from "atom.io/realtime"
6
6
 
7
7
  import { pullAtom } from "./pull-atom"
8
8
  import { pullAtomFamilyMember } from "./pull-atom-family-member"
@@ -1,6 +1,6 @@
1
1
  import type * as AtomIO from "atom.io"
2
2
  import type { Store } from "atom.io/internal"
3
- import type { Socket } from "socket.io-client"
3
+ import type { Socket } from "atom.io/realtime"
4
4
 
5
5
  import { pullSelectorRoots } from "./pull-selector-roots"
6
6
 
@@ -2,8 +2,8 @@ import type { WritableToken } from "atom.io"
2
2
  import type { Store } from "atom.io/internal"
3
3
  import { setIntoStore, subscribeToState } from "atom.io/internal"
4
4
  import type { Json } from "atom.io/json"
5
+ import type { Socket } from "atom.io/realtime"
5
6
  import { employSocket, mutexAtoms } from "atom.io/realtime"
6
- import type { Socket } from "socket.io-client"
7
7
 
8
8
  export function pushState<J extends Json.Serializable>(
9
9
  store: Store,
@@ -1,6 +1,5 @@
1
1
  import * as AtomIO from "atom.io"
2
- import type { SocketKey, UserKey } from "atom.io/realtime"
3
- import { storageSync } from "atom.io/web"
2
+ import type { RoomKey, SocketKey, UserKey } from "atom.io/realtime"
4
3
 
5
4
  export const mySocketKeyAtom: AtomIO.RegularAtomToken<SocketKey | undefined> =
6
5
  AtomIO.atom<SocketKey | undefined>({
@@ -12,5 +11,19 @@ export const myUserKeyAtom: AtomIO.RegularAtomToken<UserKey | null> =
12
11
  AtomIO.atom<UserKey | null>({
13
12
  key: `myUserKey`,
14
13
  default: null,
15
- effects: [storageSync(globalThis.localStorage, JSON, `myUserKey`)],
14
+ effects: [
15
+ (userKey) => {
16
+ if (typeof window !== `undefined`) {
17
+ void import(`atom.io/web`).then(({ storageSync }) => {
18
+ storageSync(globalThis.localStorage, JSON, `myUserKey`)(userKey)
19
+ })
20
+ }
21
+ },
22
+ ],
23
+ })
24
+
25
+ export const myRoomKeyAtom: AtomIO.RegularAtomToken<RoomKey | null> =
26
+ AtomIO.atom<RoomKey | null>({
27
+ key: `myRoomKey`,
28
+ default: null,
16
29
  })
@@ -8,8 +8,7 @@ import {
8
8
  subscribeToTransaction,
9
9
  } from "atom.io/internal"
10
10
  import type { Json } from "atom.io/json"
11
- import type { ContinuityToken } from "atom.io/realtime"
12
- import type { Socket } from "socket.io-client"
11
+ import type { ContinuityToken, Socket } from "atom.io/realtime"
13
12
 
14
13
  import { useRegisterAndAttemptConfirmedUpdate } from "./continuity/register-and-attempt-confirmed-update"
15
14
  import { useConcealState } from "./continuity/use-conceal-state"
@@ -82,7 +82,7 @@ export function provideOutcomes(
82
82
 
83
83
  socket.emit(
84
84
  `tx-new:${continuityKey}`,
85
- redactedUpdate as Json.Serializable,
85
+ redactedUpdate as unknown as Json.Serializable,
86
86
  )
87
87
  } catch (thrown) {
88
88
  if (thrown instanceof Error) {
@@ -58,18 +58,10 @@ export class ChildSocket<
58
58
  ) {
59
59
  super((event, ...args) => {
60
60
  const stringifiedEvent = JSON.stringify([event, ...args]) + `\x03`
61
- const errorHandler = (err: { code: string }) => {
62
- if (err.code === `EPIPE`) {
63
- console.error(`EPIPE error during write`, this.proc.stdin)
64
- }
65
- this.proc.stdin.removeListener(`error`, errorHandler)
66
- }
67
-
68
- this.proc.stdin.once(`error`, errorHandler)
69
61
  this.proc.stdin.write(stringifiedEvent)
70
-
71
62
  return this
72
63
  })
64
+
73
65
  this.proc = proc
74
66
  this.key = key
75
67
  this.logger = logger ?? {
@@ -208,6 +200,11 @@ export class ChildSocket<
208
200
  ++idx
209
201
  }
210
202
  })
203
+ this.proc.stdin.once(`error`, (err: { code: string }) => {
204
+ if (err.code === `EPIPE`) {
205
+ console.error(`EPIPE error during write`, this.proc.stdin)
206
+ }
207
+ })
211
208
  if (proc.pid) {
212
209
  this.id = proc.pid.toString()
213
210
  }
@@ -179,18 +179,11 @@ export class ParentSocket<
179
179
  this.logger.info(`👤`, userKey, `joined`)
180
180
  const relay = new SubjectSocket(userKey)
181
181
  this.relays.set(userKey, relay)
182
- this.logger.info(
183
- `🔗`,
184
- `attaching services for user`,
185
- userKey,
186
- // `[${[...this.initRelay.keys()].join(`, `)}]`,
187
- )
188
- // for (const attachRelay of this.initRelay) {
182
+ this.logger.info(`🔗`, `attaching services for user`, userKey)
189
183
  const cleanupRelay = this.initRelay(relay, userKey)
190
184
  if (cleanupRelay) {
191
185
  relay.disposalFunctions.push(cleanupRelay)
192
186
  }
193
- // }
194
187
  this.on(userKey, (...data) => {
195
188
  relay.in.next(data)
196
189
  })
@@ -29,13 +29,15 @@ import {
29
29
  ownersOfRooms,
30
30
  roomKeysAtom,
31
31
  usersInRooms,
32
+ visibilityFromRoomSelector,
32
33
  visibleUsersInRoomsSelector,
33
34
  } from "atom.io/realtime"
35
+ import { myRoomKeyAtom } from "atom.io/realtime-client"
34
36
 
35
37
  import { ChildSocket, PROOF_OF_LIFE_SIGNAL } from "./ipc-sockets"
36
38
  import { realtimeMutableFamilyProvider } from "./realtime-mutable-family-provider"
37
39
  import { realtimeMutableProvider } from "./realtime-mutable-provider"
38
- import type { ServerConfig } from "./server-config"
40
+ import { realtimeStateProvider } from "./realtime-state-provider"
39
41
 
40
42
  export type RoomMap = Map<
41
43
  string,
@@ -87,19 +89,47 @@ export function spawnRoom<RoomNames extends string>({
87
89
  room.stdout.on(`data`, resolver)
88
90
  },
89
91
  )
90
- const roomSocket = new ChildSocket(child, roomKey)
91
- ROOMS.set(roomKey, roomSocket)
92
- setIntoStore(store, roomKeysAtom, (index) => (index.add(roomKey), index))
93
92
 
93
+ const room = new ChildSocket(child, roomKey)
94
+ ROOMS.set(roomKey, room)
95
+ setIntoStore(store, roomKeysAtom, (index) => (index.add(roomKey), index))
94
96
  editRelationsInStore(store, ownersOfRooms, (relations) => {
95
97
  relations.set({ room: roomKey, user: userKey })
96
98
  })
97
99
 
98
- roomSocket.on(`close`, () => {
100
+ const provideMutableFamily = realtimeMutableFamilyProvider({
101
+ socket: room,
102
+ consumer: roomKey,
103
+ store,
104
+ })
105
+ const provideState = realtimeStateProvider({
106
+ socket: room,
107
+ consumer: roomKey,
108
+ store,
109
+ })
110
+ const unsubFromRoomKey = provideState(myRoomKeyAtom, roomKey)
111
+
112
+ const ownersOfRoomsAtoms = getInternalRelationsFromStore(
113
+ store,
114
+ ownersOfRooms,
115
+ )
116
+ const unsubFromOwnerKeys = provideMutableFamily(ownersOfRoomsAtoms, [
117
+ roomKey,
118
+ ])
119
+ const usersInRoomsAtoms = getInternalRelationsFromStore(store, usersInRooms)
120
+ const unsubFromUsersInRooms = provideMutableFamily(
121
+ usersInRoomsAtoms,
122
+ findInStore(store, visibilityFromRoomSelector, roomKey),
123
+ )
124
+
125
+ room.on(`close`, () => {
126
+ unsubFromRoomKey()
127
+ unsubFromOwnerKeys()
128
+ unsubFromUsersInRooms()
99
129
  destroyRoom({ store, socket, userKey })(roomKey)
100
130
  })
101
131
 
102
- return roomSocket
132
+ return room
103
133
  }
104
134
  }
105
135
 
@@ -115,7 +145,7 @@ export function provideEnterAndExit({
115
145
  roomSocket,
116
146
  userKey,
117
147
  }: ProvideEnterAndExitConfig): (roomKey: RoomKey) => void {
118
- const enterRoom = (roomKey: RoomKey) => {
148
+ const enterRoom = (roomKey: RoomKey): void => {
119
149
  store.logger.info(
120
150
  `📡`,
121
151
  `socket`,
@@ -123,6 +153,10 @@ export function provideEnterAndExit({
123
153
  `👤 ${userKey} enters room ${roomKey}`,
124
154
  )
125
155
 
156
+ const dcUserFromRoom = () => {
157
+ toRoom([`user-leaves`])
158
+ }
159
+
126
160
  const exitRoom = () => {
127
161
  store.logger.info(
128
162
  `📡`,
@@ -131,17 +165,15 @@ export function provideEnterAndExit({
131
165
  `👤 ${userKey} leaves room ${roomKey}`,
132
166
  )
133
167
  socket.offAny(forward)
134
- toRoom([`user-leaves`])
168
+ dcUserFromRoom()
135
169
  editRelationsInStore(store, usersInRooms, (relations) => {
136
170
  relations.delete({ room: roomKey, user: userKey })
137
171
  })
172
+
138
173
  roomSocket.off(`leaveRoom`, exitRoom)
139
174
  roomSocket.on(`joinRoom`, enterRoom)
140
175
  }
141
176
 
142
- roomSocket.on(`leaveRoom`, exitRoom)
143
- roomSocket.off(`joinRoom`, enterRoom)
144
-
145
177
  const roomQueue: [string, ...Json.Array][] = []
146
178
  const pushToRoomQueue = (payload: [string, ...Json.Array]): void => {
147
179
  roomQueue.push(payload)
@@ -158,7 +190,7 @@ export function provideEnterAndExit({
158
190
  const childSocket = ROOMS.get(roomKey)
159
191
  if (!childSocket) {
160
192
  store.logger.error(`❌`, `unknown`, roomKey, `no room found with this id`)
161
- return null
193
+ return
162
194
  }
163
195
  childSocket.onAny((...payload) => {
164
196
  socket.emit(...payload)
@@ -172,6 +204,10 @@ export function provideEnterAndExit({
172
204
  const payload = roomQueue.shift()
173
205
  if (payload) toRoom(payload)
174
206
  }
207
+
208
+ socket.on(`disconnect`, dcUserFromRoom)
209
+ roomSocket.on(`leaveRoom`, exitRoom)
210
+ roomSocket.off(`joinRoom`, enterRoom)
175
211
  }
176
212
  roomSocket.on(`joinRoom`, enterRoom)
177
213
  return enterRoom
@@ -229,6 +265,9 @@ export type ProvideRoomsConfig<RoomNames extends string> = {
229
265
  resolveRoomScript: (path: RoomNames) => [string, string[]]
230
266
  roomNames: RoomNames[]
231
267
  roomTimeLimit?: number
268
+ userKey: UserKey
269
+ store: RootStore
270
+ socket: Socket
232
271
  }
233
272
  export function provideRooms<RoomNames extends string>({
234
273
  store = IMPLICIT.STORE,
@@ -236,11 +275,15 @@ export function provideRooms<RoomNames extends string>({
236
275
  resolveRoomScript,
237
276
  roomNames,
238
277
  userKey,
239
- }: ProvideRoomsConfig<RoomNames> & ServerConfig): () => void {
278
+ }: ProvideRoomsConfig<RoomNames>): () => void {
240
279
  const roomSocket = castSocket<
241
280
  TypedSocket<RoomSocketInterface<RoomNames>, never>
242
281
  >(socket, createRoomSocketGuard(roomNames))
243
- const exposeMutable = realtimeMutableProvider({ socket, store, userKey })
282
+ const exposeMutable = realtimeMutableProvider({
283
+ socket,
284
+ store,
285
+ consumer: userKey,
286
+ })
244
287
  const unsubFromRoomKeys = exposeMutable(roomKeysAtom)
245
288
  const usersInRoomsAtoms = getInternalRelationsFromStore(store, usersInRooms)
246
289
  const [, usersInRoomsAtomsUsersOnly] = getInternalRelationsFromStore(
@@ -257,7 +300,7 @@ export function provideRooms<RoomNames extends string>({
257
300
  const exposeMutableFamily = realtimeMutableFamilyProvider({
258
301
  socket,
259
302
  store,
260
- userKey,
303
+ consumer: userKey,
261
304
  })
262
305
  const unsubFromUsersInRooms = exposeMutableFamily(
263
306
  usersInRoomsAtoms,
@@ -267,7 +310,12 @@ export function provideRooms<RoomNames extends string>({
267
310
  ownersOfRoomsAtoms,
268
311
  usersWhoseRoomsCanBeSeenSelector,
269
312
  )
270
- const enterRoom = provideEnterAndExit({ store, socket, roomSocket, userKey })
313
+ const enterRoom = provideEnterAndExit({
314
+ store,
315
+ socket,
316
+ roomSocket,
317
+ userKey,
318
+ })
271
319
 
272
320
  const userRoomSet = getFromStore(store, usersInRoomsAtomsUsersOnly, userKey)
273
321
  for (const userRoomKey of userRoomSet) {
@@ -14,7 +14,7 @@ import type { ServerConfig } from "."
14
14
  export type FamilyProvider = ReturnType<typeof realtimeAtomFamilyProvider>
15
15
  export function realtimeAtomFamilyProvider({
16
16
  socket,
17
- userKey,
17
+ consumer,
18
18
  store = IMPLICIT.STORE,
19
19
  }: ServerConfig) {
20
20
  return function familyProvider<
@@ -22,8 +22,17 @@ export function realtimeAtomFamilyProvider({
22
22
  K extends Canonical,
23
23
  >(
24
24
  family: AtomIO.RegularAtomFamilyToken<J, K>,
25
- index: AtomIO.ReadableToken<Iterable<NoInfer<K>>>,
25
+ index: AtomIO.ReadableToken<Iterable<NoInfer<K>>> | Iterable<NoInfer<K>>,
26
26
  ): () => void {
27
+ const [dynamicIndex, staticIndex]:
28
+ | [AtomIO.ReadableToken<Iterable<NoInfer<K>>>, undefined]
29
+ | [undefined, Iterable<NoInfer<K>>] = (() => {
30
+ if (typeof index === `object` && `key` in index && `type` in index) {
31
+ return [index, undefined] as const
32
+ }
33
+ return [undefined, index] as const
34
+ })()
35
+
27
36
  const coreSubscriptions = new Set<() => void>()
28
37
  const clearCoreSubscriptions = () => {
29
38
  for (const unsub of coreSubscriptions) unsub()
@@ -70,7 +79,7 @@ export function realtimeAtomFamilyProvider({
70
79
  store.logger.info(
71
80
  `🙈`,
72
81
  `user`,
73
- userKey,
82
+ consumer,
74
83
  `unsubscribed from state "${token.key}"`,
75
84
  )
76
85
  fillUnsubRequest(token.key)
@@ -91,18 +100,23 @@ export function realtimeAtomFamilyProvider({
91
100
  store.logger.info(
92
101
  `👀`,
93
102
  `user`,
94
- userKey,
103
+ consumer,
95
104
  `can subscribe to family "${family.key}"`,
96
105
  )
97
106
  coreSubscriptions.add(
98
107
  employSocket(socket, `sub:${family.key}`, (subKey: K) => {
99
- const exposedSubKeys = getFromStore(store, index)
108
+ let exposedSubKeys: Iterable<K>
109
+ if (dynamicIndex) {
110
+ exposedSubKeys = getFromStore(store, dynamicIndex)
111
+ } else {
112
+ exposedSubKeys = staticIndex
113
+ }
100
114
  const shouldExpose = isAvailable(exposedSubKeys, subKey)
101
115
  if (shouldExpose) {
102
116
  store.logger.info(
103
117
  `👀`,
104
118
  `user`,
105
- userKey,
119
+ consumer,
106
120
  `is approved for a subscription to`,
107
121
  subKey,
108
122
  `in family "${family.key}"`,
@@ -112,7 +126,7 @@ export function realtimeAtomFamilyProvider({
112
126
  store.logger.info(
113
127
  `❌`,
114
128
  `user`,
115
- userKey,
129
+ consumer,
116
130
  `is denied for a subscription to`,
117
131
  subKey,
118
132
  `in family "${family.key}"`,
@@ -122,35 +136,37 @@ export function realtimeAtomFamilyProvider({
122
136
  }
123
137
  }),
124
138
  )
125
- coreSubscriptions.add(
126
- subscribeToState(
127
- store,
128
- index,
129
- `expose-family:${family.key}:${socket.id}`,
130
- ({ newValue: newExposedSubKeys }) => {
131
- store.logger.info(
132
- `👀`,
133
- `user`,
134
- userKey,
135
- `has the following keys available for family "${family.key}"`,
136
- newExposedSubKeys,
137
- )
138
- for (const subKey of newExposedSubKeys) {
139
- if (familyMemberSubscriptionsWanted.has(stringifyJson(subKey))) {
140
- store.logger.info(
141
- `👀`,
142
- `user`,
143
- userKey,
144
- `is retroactively approved for a subscription to`,
145
- subKey,
146
- `in family "${family.key}"`,
147
- )
148
- exposeFamilyMembers(subKey)
139
+ if (dynamicIndex) {
140
+ coreSubscriptions.add(
141
+ subscribeToState(
142
+ store,
143
+ dynamicIndex,
144
+ `expose-family:${family.key}:${socket.id}`,
145
+ ({ newValue: newExposedSubKeys }) => {
146
+ store.logger.info(
147
+ `👀`,
148
+ `user`,
149
+ consumer,
150
+ `has the following keys available for family "${family.key}"`,
151
+ newExposedSubKeys,
152
+ )
153
+ for (const subKey of newExposedSubKeys) {
154
+ if (familyMemberSubscriptionsWanted.has(stringifyJson(subKey))) {
155
+ store.logger.info(
156
+ `👀`,
157
+ `user`,
158
+ consumer,
159
+ `is retroactively approved for a subscription to`,
160
+ subKey,
161
+ `in family "${family.key}"`,
162
+ )
163
+ exposeFamilyMembers(subKey)
164
+ }
149
165
  }
150
- }
151
- },
152
- ),
153
- )
166
+ },
167
+ ),
168
+ )
169
+ }
154
170
  }
155
171
 
156
172
  start()