atom.io 0.17.0 → 0.18.0
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/data/dist/index.cjs +62 -40
- package/data/dist/index.cjs.map +1 -1
- package/data/dist/index.d.ts +8 -2
- package/data/dist/index.js +64 -42
- package/data/dist/index.js.map +1 -1
- package/data/src/dict.ts +8 -4
- package/data/src/join.ts +74 -33
- package/data/src/struct-family.ts +18 -17
- package/dist/chunk-OEVFAUPE.js +289 -0
- package/dist/chunk-OEVFAUPE.js.map +1 -0
- package/dist/index.cjs +4 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +62 -51
- package/dist/index.js +5 -11
- package/dist/index.js.map +1 -1
- package/internal/dist/index.cjs +163 -64
- package/internal/dist/index.cjs.map +1 -1
- package/internal/dist/index.d.ts +94 -70
- package/internal/dist/index.js +155 -59
- package/internal/dist/index.js.map +1 -1
- package/internal/src/arbitrary.ts +3 -0
- package/internal/src/caching.ts +8 -6
- package/internal/src/families/find-in-store.ts +16 -0
- package/internal/src/get-environment-data.ts +4 -7
- package/internal/src/index.ts +6 -5
- package/internal/src/ingest-updates/ingest-transaction-update.ts +0 -1
- package/internal/src/selector/create-standalone-selector.ts +0 -2
- package/internal/src/set-state/set-atom.ts +14 -18
- package/internal/src/store/store.ts +14 -2
- package/internal/src/store/withdraw.ts +72 -2
- package/internal/src/subscribe/subscribe-to-timeline.ts +2 -2
- package/internal/src/subscribe/subscribe-to-transaction.ts +2 -2
- package/internal/src/timeline/create-timeline.ts +12 -1
- package/internal/src/transaction/act-upon-store.ts +19 -0
- package/internal/src/transaction/apply-transaction.ts +7 -1
- package/internal/src/transaction/assign-transaction-to-continuity.ts +18 -0
- package/internal/src/transaction/build-transaction.ts +7 -6
- package/internal/src/transaction/create-transaction.ts +1 -1
- package/internal/src/transaction/get-epoch-number.ts +40 -0
- package/internal/src/transaction/index.ts +10 -1
- package/internal/src/transaction/set-epoch-number.ts +31 -0
- package/introspection/dist/index.cjs.map +1 -1
- package/introspection/dist/index.d.ts +3 -3
- package/introspection/dist/index.js.map +1 -1
- package/introspection/src/attach-introspection-states.ts +6 -2
- package/introspection/src/attach-timeline-family.ts +5 -2
- package/introspection/src/attach-transaction-logs.ts +2 -2
- package/json/dist/index.d.ts +3 -1
- package/json/src/index.ts +6 -2
- package/package.json +24 -13
- package/react/dist/index.cjs.map +1 -1
- package/react/dist/index.d.ts +1 -1
- package/react/dist/index.js.map +1 -1
- package/react/src/use-json.ts +1 -1
- package/react-devtools/dist/index.cjs +131 -134
- package/react-devtools/dist/index.cjs.map +1 -1
- package/react-devtools/dist/index.css +2 -2
- package/react-devtools/dist/index.css.map +1 -1
- package/react-devtools/dist/index.d.ts +3 -3
- package/react-devtools/dist/index.js +103 -106
- package/react-devtools/dist/index.js.map +1 -1
- package/react-devtools/src/StateEditor.tsx +6 -6
- package/react-devtools/src/StateIndex.tsx +2 -5
- package/react-devtools/src/TimelineIndex.tsx +3 -3
- package/react-devtools/src/TransactionIndex.tsx +9 -8
- package/react-devtools/src/Updates.tsx +1 -1
- package/react-devtools/src/index.ts +4 -4
- package/realtime/dist/index.cjs +72 -0
- package/realtime/dist/index.cjs.map +1 -0
- package/realtime/dist/index.d.ts +39 -0
- package/realtime/dist/index.js +68 -0
- package/realtime/dist/index.js.map +1 -0
- package/realtime/package.json +16 -0
- package/realtime/src/index.ts +1 -0
- package/realtime/src/realtime-continuity.ts +152 -0
- package/realtime-client/dist/index.cjs +389 -48
- package/realtime-client/dist/index.cjs.map +1 -1
- package/realtime-client/dist/index.d.ts +16 -9
- package/realtime-client/dist/index.js +100 -37
- package/realtime-client/dist/index.js.map +1 -1
- package/realtime-client/src/index.ts +8 -5
- package/realtime-client/src/{pull-family-member.ts → pull-atom-family-member.ts} +2 -2
- package/realtime-client/src/{pull-state.ts → pull-atom.ts} +2 -2
- package/realtime-client/src/{pull-mutable-family-member.ts → pull-mutable-atom-family-member.ts} +1 -1
- package/realtime-client/src/{pull-mutable.ts → pull-mutable-atom.ts} +1 -1
- package/realtime-client/src/pull-selector-family-member.ts +42 -0
- package/realtime-client/src/pull-selector.ts +38 -0
- package/realtime-client/src/realtime-client-stores/client-main-store.ts +2 -2
- package/realtime-client/src/realtime-client-stores/client-sync-store.ts +7 -7
- package/realtime-client/src/sync-continuity.ts +321 -0
- package/realtime-client/src/sync-server-action.ts +18 -20
- package/realtime-react/dist/index.cjs +330 -15
- package/realtime-react/dist/index.cjs.map +1 -1
- package/realtime-react/dist/index.d.ts +26 -6
- package/realtime-react/dist/index.js +43 -12
- package/realtime-react/dist/index.js.map +1 -1
- package/realtime-react/src/index.ts +6 -3
- package/realtime-react/src/use-pull-atom-family-member.ts +21 -0
- package/realtime-react/src/{use-pull-family-member.ts → use-pull-atom.ts} +6 -5
- package/realtime-react/src/{use-pull-mutable.ts → use-pull-mutable-atom.ts} +4 -3
- package/realtime-react/src/use-pull-mutable-family-member.ts +9 -4
- package/realtime-react/src/use-pull-selector-family-member.ts +21 -0
- package/realtime-react/src/{use-pull.ts → use-pull-selector.ts} +7 -5
- package/realtime-react/src/use-push.ts +3 -2
- package/realtime-react/src/use-server-action.ts +3 -2
- package/realtime-react/src/use-sync-continuity.ts +12 -0
- package/realtime-react/src/use-sync-server-action.ts +3 -2
- package/realtime-server/dist/index.cjs +568 -242
- package/realtime-server/dist/index.cjs.map +1 -1
- package/realtime-server/dist/index.d.ts +124 -49
- package/realtime-server/dist/index.js +555 -238
- package/realtime-server/dist/index.js.map +1 -1
- package/realtime-server/src/index.ts +18 -2
- package/realtime-server/src/ipc-socket.ts +230 -0
- package/realtime-server/src/realtime-action-receiver.ts +8 -5
- package/realtime-server/src/realtime-action-synchronizer.ts +40 -28
- package/realtime-server/src/realtime-continuity-synchronizer.ts +247 -0
- package/realtime-server/src/realtime-family-provider.ts +30 -71
- package/realtime-server/src/realtime-mutable-family-provider.ts +24 -86
- package/realtime-server/src/realtime-server-stores/index.ts +3 -1
- package/realtime-server/src/realtime-server-stores/realtime-continuity-store.ts +90 -0
- package/realtime-server/src/realtime-server-stores/server-room-store.ts +97 -0
- package/realtime-server/src/realtime-server-stores/server-sync-store.ts +2 -72
- package/realtime-server/src/realtime-server-stores/server-user-store.ts +14 -29
- package/realtime-server/src/realtime-state-receiver.ts +0 -1
- package/realtime-testing/dist/index.cjs +28 -28
- package/realtime-testing/dist/index.cjs.map +1 -1
- package/realtime-testing/dist/index.js +28 -27
- package/realtime-testing/dist/index.js.map +1 -1
- package/realtime-testing/src/setup-realtime-test.tsx +38 -28
- package/src/atom.ts +49 -31
- package/src/logger.ts +10 -5
- package/src/selector.ts +44 -25
- package/src/subscribe.ts +2 -1
- package/src/timeline.ts +4 -4
- package/src/transaction.ts +13 -17
- package/src/validators.ts +15 -9
- package/dist/chunk-H4Q5FTPZ.js +0 -11
- package/dist/chunk-H4Q5FTPZ.js.map +0 -1
- package/internal/src/set-state/copy-mutable-in-transaction.ts +0 -19
|
@@ -1,58 +1,74 @@
|
|
|
1
|
-
import * as AtomIO from "atom.io"
|
|
1
|
+
import type * as AtomIO from "atom.io"
|
|
2
2
|
import {
|
|
3
3
|
IMPLICIT,
|
|
4
|
+
actUponStore,
|
|
5
|
+
assignTransactionToContinuity,
|
|
4
6
|
findInStore,
|
|
5
7
|
getFromStore,
|
|
6
8
|
setIntoStore,
|
|
7
9
|
subscribeToTransaction,
|
|
8
10
|
} from "atom.io/internal"
|
|
11
|
+
import type { Json, JsonIO } from "atom.io/json"
|
|
9
12
|
|
|
10
13
|
import type { ServerConfig } from "."
|
|
11
|
-
import { usersOfSockets } from "./realtime-server-stores"
|
|
12
14
|
import {
|
|
13
15
|
completeUpdateAtoms,
|
|
14
16
|
redactedUpdateSelectors,
|
|
15
|
-
socketEpochSelectors,
|
|
16
|
-
socketUnacknowledgedUpdatesSelectors,
|
|
17
17
|
transactionRedactorAtoms,
|
|
18
|
-
|
|
18
|
+
userUnacknowledgedQueues,
|
|
19
|
+
usersOfSockets,
|
|
20
|
+
} from "./realtime-server-stores"
|
|
19
21
|
|
|
20
22
|
export type ActionSynchronizer = ReturnType<typeof realtimeActionSynchronizer>
|
|
21
23
|
export function realtimeActionSynchronizer({
|
|
22
24
|
socket,
|
|
23
25
|
store = IMPLICIT.STORE,
|
|
24
26
|
}: ServerConfig) {
|
|
25
|
-
return function actionSynchronizer<ƒ extends
|
|
27
|
+
return function actionSynchronizer<ƒ extends JsonIO>(
|
|
26
28
|
tx: AtomIO.TransactionToken<ƒ>,
|
|
27
29
|
filter?: (
|
|
28
30
|
update: AtomIO.TransactionUpdateContent[],
|
|
29
31
|
) => AtomIO.TransactionUpdateContent[],
|
|
30
32
|
): () => void {
|
|
33
|
+
assignTransactionToContinuity(`default`, tx.key, store)
|
|
34
|
+
|
|
31
35
|
const userKeyState = findInStore(
|
|
32
36
|
usersOfSockets.states.userKeyOfSocket,
|
|
33
37
|
socket.id,
|
|
34
38
|
store,
|
|
35
39
|
)
|
|
36
40
|
const userKey = getFromStore(userKeyState, store)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
if (!userKey) {
|
|
42
|
+
store.logger.error(
|
|
43
|
+
`❌`,
|
|
44
|
+
`transaction`,
|
|
45
|
+
tx.key,
|
|
46
|
+
`Tried to create a synchronizer for a socket (${socket.id}) that is not connected to a user.`,
|
|
47
|
+
)
|
|
48
|
+
return () => {}
|
|
49
|
+
}
|
|
50
|
+
const userUnacknowledgedQueue = findInStore(
|
|
51
|
+
userUnacknowledgedQueues,
|
|
52
|
+
userKey,
|
|
40
53
|
store,
|
|
41
54
|
)
|
|
42
|
-
const
|
|
43
|
-
|
|
55
|
+
const userUnacknowledgedUpdates = getFromStore(
|
|
56
|
+
userUnacknowledgedQueue,
|
|
44
57
|
store,
|
|
45
58
|
)
|
|
46
59
|
if (filter) {
|
|
47
60
|
const redactorState = findInStore(transactionRedactorAtoms, tx.key, store)
|
|
48
61
|
setIntoStore(redactorState, { filter }, store)
|
|
49
62
|
}
|
|
50
|
-
|
|
63
|
+
|
|
64
|
+
const fillTransactionRequest = (
|
|
65
|
+
update: Pick<AtomIO.TransactionUpdate<ƒ>, `id` | `params`>,
|
|
66
|
+
) => {
|
|
51
67
|
const performanceKey = `tx-run:${tx.key}:${update.id}`
|
|
52
68
|
const performanceKeyStart = `${performanceKey}:start`
|
|
53
69
|
const performanceKeyEnd = `${performanceKey}:end`
|
|
54
70
|
performance.mark(performanceKeyStart)
|
|
55
|
-
|
|
71
|
+
actUponStore<ƒ>(tx, update.id, store)(...update.params)
|
|
56
72
|
performance.mark(performanceKeyEnd)
|
|
57
73
|
const metric = performance.measure(
|
|
58
74
|
performanceKey,
|
|
@@ -71,7 +87,10 @@ export function realtimeActionSynchronizer({
|
|
|
71
87
|
(update) => {
|
|
72
88
|
const updateState = findInStore(completeUpdateAtoms, update.id, store)
|
|
73
89
|
setIntoStore(updateState, update, store)
|
|
74
|
-
const toEmit
|
|
90
|
+
const toEmit: Pick<
|
|
91
|
+
AtomIO.TransactionUpdate<ƒ>,
|
|
92
|
+
`epoch` | `id` | `key` | `output` | `updates`
|
|
93
|
+
> | null = filter
|
|
75
94
|
? getFromStore(
|
|
76
95
|
findInStore(redactedUpdateSelectors, [tx.key, update.id], store),
|
|
77
96
|
store,
|
|
@@ -83,7 +102,7 @@ export function realtimeActionSynchronizer({
|
|
|
83
102
|
//
|
|
84
103
|
// we need a client session that can persist between disconnects
|
|
85
104
|
setIntoStore(
|
|
86
|
-
|
|
105
|
+
userUnacknowledgedQueue,
|
|
87
106
|
(updates) => {
|
|
88
107
|
if (toEmit) {
|
|
89
108
|
updates.push(toEmit)
|
|
@@ -94,7 +113,7 @@ export function realtimeActionSynchronizer({
|
|
|
94
113
|
store,
|
|
95
114
|
)
|
|
96
115
|
|
|
97
|
-
socket.emit(`tx-new:${tx.key}`, toEmit)
|
|
116
|
+
socket.emit(`tx-new:${tx.key}`, toEmit as Json.Serializable)
|
|
98
117
|
},
|
|
99
118
|
`tx-sub:${tx.key}:${socket.id}`,
|
|
100
119
|
store,
|
|
@@ -106,10 +125,9 @@ export function realtimeActionSynchronizer({
|
|
|
106
125
|
let i = 1
|
|
107
126
|
let next = 1
|
|
108
127
|
const retry = setInterval(() => {
|
|
109
|
-
const toEmit =
|
|
110
|
-
console.log(userKey, socketUnacknowledgedUpdates)
|
|
128
|
+
const toEmit = userUnacknowledgedUpdates[0]
|
|
111
129
|
if (toEmit && i === next) {
|
|
112
|
-
socket.emit(`tx-new:${tx.key}`, toEmit)
|
|
130
|
+
socket.emit(`tx-new:${tx.key}`, toEmit as Json.Serializable)
|
|
113
131
|
next *= 2
|
|
114
132
|
}
|
|
115
133
|
|
|
@@ -119,16 +137,10 @@ export function realtimeActionSynchronizer({
|
|
|
119
137
|
const trackClientAcknowledgement = (epoch: number) => {
|
|
120
138
|
i = 1
|
|
121
139
|
next = 1
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
socket.id,
|
|
125
|
-
store,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
setIntoStore(socketEpochState, epoch, store)
|
|
129
|
-
if (socketUnacknowledgedUpdates[0]?.epoch === epoch) {
|
|
140
|
+
8
|
|
141
|
+
if (userUnacknowledgedUpdates[0]?.epoch === epoch) {
|
|
130
142
|
setIntoStore(
|
|
131
|
-
|
|
143
|
+
userUnacknowledgedQueue,
|
|
132
144
|
(updates) => {
|
|
133
145
|
updates.shift()
|
|
134
146
|
return updates
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import type * as AtomIO from "atom.io"
|
|
2
|
+
import {
|
|
3
|
+
IMPLICIT,
|
|
4
|
+
actUponStore,
|
|
5
|
+
findInStore,
|
|
6
|
+
getFromStore,
|
|
7
|
+
isRootStore,
|
|
8
|
+
setIntoStore,
|
|
9
|
+
subscribeToState,
|
|
10
|
+
subscribeToTransaction,
|
|
11
|
+
} from "atom.io/internal"
|
|
12
|
+
import type { Json, JsonIO } from "atom.io/json"
|
|
13
|
+
import type { ContinuityToken } from "atom.io/realtime"
|
|
14
|
+
|
|
15
|
+
import type { ServerConfig, Socket } from "."
|
|
16
|
+
import { socketAtoms, usersOfSockets } from "."
|
|
17
|
+
import { redactedPerspectiveUpdateSelectors } from "./realtime-server-stores"
|
|
18
|
+
import {
|
|
19
|
+
completeUpdateAtoms,
|
|
20
|
+
userUnacknowledgedQueues,
|
|
21
|
+
} from "./realtime-server-stores"
|
|
22
|
+
|
|
23
|
+
export type RealtimeContinuitySynchronizer = ReturnType<
|
|
24
|
+
typeof realtimeContinuitySynchronizer
|
|
25
|
+
>
|
|
26
|
+
export function realtimeContinuitySynchronizer({
|
|
27
|
+
socket: initialSocket,
|
|
28
|
+
store = IMPLICIT.STORE,
|
|
29
|
+
}: ServerConfig) {
|
|
30
|
+
return function synchronizer(continuity: ContinuityToken): () => void {
|
|
31
|
+
let socket: Socket | null = initialSocket
|
|
32
|
+
|
|
33
|
+
const continuityKey = continuity.key
|
|
34
|
+
const userKeyState = findInStore(
|
|
35
|
+
usersOfSockets.states.userKeyOfSocket,
|
|
36
|
+
socket.id,
|
|
37
|
+
store,
|
|
38
|
+
)
|
|
39
|
+
const userKey = getFromStore(userKeyState, store)
|
|
40
|
+
if (!userKey) {
|
|
41
|
+
store.logger.error(
|
|
42
|
+
`❌`,
|
|
43
|
+
`continuity`,
|
|
44
|
+
continuityKey,
|
|
45
|
+
`Tried to create a synchronizer for a socket (${socket.id}) that is not connected to a user.`,
|
|
46
|
+
)
|
|
47
|
+
return () => {}
|
|
48
|
+
}
|
|
49
|
+
const socketKeyState = findInStore(
|
|
50
|
+
usersOfSockets.states.socketKeyOfUser,
|
|
51
|
+
userKey,
|
|
52
|
+
store,
|
|
53
|
+
)
|
|
54
|
+
subscribeToState(
|
|
55
|
+
socketKeyState,
|
|
56
|
+
({ newValue: newSocketKey }) => {
|
|
57
|
+
store.logger.info(
|
|
58
|
+
`👋`,
|
|
59
|
+
`continuity`,
|
|
60
|
+
continuityKey,
|
|
61
|
+
`seeing ${userKey} on new socket ${newSocketKey}`,
|
|
62
|
+
)
|
|
63
|
+
if (newSocketKey === null) {
|
|
64
|
+
store.logger.error(
|
|
65
|
+
`❌`,
|
|
66
|
+
`continuity`,
|
|
67
|
+
continuityKey,
|
|
68
|
+
`Tried to create a synchronizer for a user (${userKey}) that is not connected to a socket.`,
|
|
69
|
+
)
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
const newSocketState = findInStore(socketAtoms, newSocketKey, store)
|
|
73
|
+
const newSocket = getFromStore(newSocketState, store)
|
|
74
|
+
socket = newSocket
|
|
75
|
+
},
|
|
76
|
+
`sync-continuity:${continuityKey}:${userKey}`,
|
|
77
|
+
store,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
const userUnacknowledgedQueue = findInStore(
|
|
81
|
+
userUnacknowledgedQueues,
|
|
82
|
+
userKey,
|
|
83
|
+
store,
|
|
84
|
+
)
|
|
85
|
+
const userUnacknowledgedUpdates = getFromStore(
|
|
86
|
+
userUnacknowledgedQueue,
|
|
87
|
+
store,
|
|
88
|
+
)
|
|
89
|
+
const unsubscribeFunctions: (() => void)[] = []
|
|
90
|
+
|
|
91
|
+
const sendInitialPayload = () => {
|
|
92
|
+
const initialPayload: Json.Serializable[] = []
|
|
93
|
+
for (const atom of continuity.globals) {
|
|
94
|
+
initialPayload.push(atom, getFromStore(atom, store))
|
|
95
|
+
}
|
|
96
|
+
for (const { perspectiveAtoms } of continuity.perspectives) {
|
|
97
|
+
const perspectiveTokensState = findInStore(
|
|
98
|
+
perspectiveAtoms,
|
|
99
|
+
userKey,
|
|
100
|
+
store,
|
|
101
|
+
)
|
|
102
|
+
const perspectiveTokens = getFromStore(perspectiveTokensState, store)
|
|
103
|
+
for (const perspectiveToken of perspectiveTokens) {
|
|
104
|
+
const resource = getFromStore(perspectiveToken, store)
|
|
105
|
+
initialPayload.push(perspectiveToken, resource)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const epoch = isRootStore(store)
|
|
110
|
+
? store.transactionMeta.epoch.get(continuityKey) ?? null
|
|
111
|
+
: null
|
|
112
|
+
socket?.emit(`continuity-init:${continuityKey}`, epoch, initialPayload)
|
|
113
|
+
|
|
114
|
+
for (const transaction of continuity.actions) {
|
|
115
|
+
const unsubscribeFromTransaction = subscribeToTransaction(
|
|
116
|
+
transaction,
|
|
117
|
+
(update) => {
|
|
118
|
+
// store.logger.info(`userId`, userKey)
|
|
119
|
+
const updateState = findInStore(
|
|
120
|
+
completeUpdateAtoms,
|
|
121
|
+
update.id,
|
|
122
|
+
store,
|
|
123
|
+
)
|
|
124
|
+
setIntoStore(updateState, update, store)
|
|
125
|
+
const redactedUpdateKey = {
|
|
126
|
+
userId: userKey,
|
|
127
|
+
syncGroupKey: continuityKey,
|
|
128
|
+
updateId: update.id,
|
|
129
|
+
}
|
|
130
|
+
const redactedUpdateState = findInStore(
|
|
131
|
+
redactedPerspectiveUpdateSelectors,
|
|
132
|
+
redactedUpdateKey,
|
|
133
|
+
store,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
const redactedUpdate = getFromStore(redactedUpdateState, store)
|
|
137
|
+
|
|
138
|
+
setIntoStore(
|
|
139
|
+
userUnacknowledgedQueue,
|
|
140
|
+
(updates) => {
|
|
141
|
+
if (redactedUpdate) {
|
|
142
|
+
updates.push(redactedUpdate)
|
|
143
|
+
updates.sort((a, b) => a.epoch - b.epoch)
|
|
144
|
+
}
|
|
145
|
+
return updates
|
|
146
|
+
},
|
|
147
|
+
store,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
socket?.emit(
|
|
151
|
+
`tx-new:${continuityKey}`,
|
|
152
|
+
redactedUpdate as Json.Serializable,
|
|
153
|
+
)
|
|
154
|
+
},
|
|
155
|
+
`sync-continuity:${continuityKey}:${userKey}`,
|
|
156
|
+
store,
|
|
157
|
+
)
|
|
158
|
+
unsubscribeFunctions.push(unsubscribeFromTransaction)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
socket.off(`get:${continuityKey}`, sendInitialPayload)
|
|
162
|
+
socket.on(`get:${continuityKey}`, sendInitialPayload)
|
|
163
|
+
|
|
164
|
+
const fillTransactionRequest = (
|
|
165
|
+
update: Pick<AtomIO.TransactionUpdate<JsonIO>, `id` | `key` | `params`>,
|
|
166
|
+
) => {
|
|
167
|
+
const transactionKey = update.key
|
|
168
|
+
const updateId = update.id
|
|
169
|
+
const performanceKey = `tx-run:${transactionKey}:${updateId}`
|
|
170
|
+
const performanceKeyStart = `${performanceKey}:start`
|
|
171
|
+
const performanceKeyEnd = `${performanceKey}:end`
|
|
172
|
+
performance.mark(performanceKeyStart)
|
|
173
|
+
actUponStore(
|
|
174
|
+
{ type: `transaction`, key: transactionKey },
|
|
175
|
+
updateId,
|
|
176
|
+
store,
|
|
177
|
+
)(...update.params)
|
|
178
|
+
performance.mark(performanceKeyEnd)
|
|
179
|
+
const metric = performance.measure(
|
|
180
|
+
performanceKey,
|
|
181
|
+
performanceKeyStart,
|
|
182
|
+
performanceKeyEnd,
|
|
183
|
+
)
|
|
184
|
+
store?.logger.info(
|
|
185
|
+
`🚀`,
|
|
186
|
+
`transaction`,
|
|
187
|
+
transactionKey,
|
|
188
|
+
updateId,
|
|
189
|
+
metric.duration,
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
socket.off(`tx-run:${continuityKey}`, fillTransactionRequest)
|
|
193
|
+
socket.on(`tx-run:${continuityKey}`, fillTransactionRequest)
|
|
194
|
+
|
|
195
|
+
let i = 1
|
|
196
|
+
let next = 1
|
|
197
|
+
const retry = setInterval(() => {
|
|
198
|
+
const toEmit = userUnacknowledgedUpdates[0]
|
|
199
|
+
store.logger.info(
|
|
200
|
+
`🔄`,
|
|
201
|
+
`continuity`,
|
|
202
|
+
continuityKey,
|
|
203
|
+
`${store.config.name} retrying ${userKey} (${i}/${next})`,
|
|
204
|
+
socket?.id,
|
|
205
|
+
userUnacknowledgedUpdates,
|
|
206
|
+
)
|
|
207
|
+
if (toEmit && i === next) {
|
|
208
|
+
socket?.emit(`tx-new:${continuityKey}`, toEmit as Json.Serializable)
|
|
209
|
+
next *= 2
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
i++
|
|
213
|
+
}, 250)
|
|
214
|
+
const trackClientAcknowledgement = (epoch: number) => {
|
|
215
|
+
store.logger.info(
|
|
216
|
+
`👍`,
|
|
217
|
+
`continuity`,
|
|
218
|
+
continuityKey,
|
|
219
|
+
`${userKey} acknowledged epoch ${epoch}`,
|
|
220
|
+
)
|
|
221
|
+
i = 1
|
|
222
|
+
next = 1
|
|
223
|
+
const isUnacknowledged = userUnacknowledgedUpdates[0]?.epoch === epoch
|
|
224
|
+
|
|
225
|
+
if (isUnacknowledged) {
|
|
226
|
+
setIntoStore(
|
|
227
|
+
userUnacknowledgedQueue,
|
|
228
|
+
(updates) => {
|
|
229
|
+
updates.shift()
|
|
230
|
+
return updates
|
|
231
|
+
},
|
|
232
|
+
store,
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
socket.off(`ack:${continuityKey}`, trackClientAcknowledgement)
|
|
237
|
+
socket.on(`ack:${continuityKey}`, trackClientAcknowledgement)
|
|
238
|
+
|
|
239
|
+
return () => {
|
|
240
|
+
clearInterval(retry)
|
|
241
|
+
for (const unsubscribe of unsubscribeFunctions) unsubscribe()
|
|
242
|
+
socket?.off(`ack:${continuityKey}`, trackClientAcknowledgement)
|
|
243
|
+
socket?.off(`get:${continuityKey}`, sendInitialPayload)
|
|
244
|
+
socket?.off(`tx-run:${continuityKey}`, fillTransactionRequest)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -5,13 +5,12 @@ import {
|
|
|
5
5
|
getFromStore,
|
|
6
6
|
subscribeToState,
|
|
7
7
|
} from "atom.io/internal"
|
|
8
|
-
import type
|
|
9
|
-
import { parseJson } from "atom.io/json"
|
|
8
|
+
import { type Json, stringifyJson } from "atom.io/json"
|
|
10
9
|
|
|
11
10
|
import type { ServerConfig } from "."
|
|
12
11
|
|
|
13
|
-
export type FamilyProvider = ReturnType<typeof
|
|
14
|
-
export function
|
|
12
|
+
export type FamilyProvider = ReturnType<typeof realtimeAtomFamilyProvider>
|
|
13
|
+
export function realtimeAtomFamilyProvider({
|
|
15
14
|
socket,
|
|
16
15
|
store = IMPLICIT.STORE,
|
|
17
16
|
}: ServerConfig) {
|
|
@@ -19,77 +18,40 @@ export function realtimeFamilyProvider({
|
|
|
19
18
|
J extends Json.Serializable,
|
|
20
19
|
K extends Json.Serializable,
|
|
21
20
|
>(
|
|
22
|
-
family: AtomIO.
|
|
21
|
+
family: AtomIO.RegularAtomFamilyToken<J, K>,
|
|
23
22
|
index: AtomIO.ReadableToken<Iterable<K>>,
|
|
24
23
|
): () => void {
|
|
25
|
-
const
|
|
26
|
-
const unsubFamilyCallbacksByKey = new Map<string, () => void>()
|
|
24
|
+
const unsubCallbacksByKey = new Map<string, () => void>()
|
|
27
25
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
unsubFamilyCallbacksByKey.clear()
|
|
33
|
-
socket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const fillSingleUnsubRequest = (key: string) => {
|
|
37
|
-
socket.off(`unsub:${key}`, fillSingleUnsubRequest)
|
|
38
|
-
const unsub = unsubSingleCallbacksByKey.get(key)
|
|
26
|
+
const fillUnsubRequest = (key: string) => {
|
|
27
|
+
socket.off(`unsub:${key}`, fillUnsubRequest)
|
|
28
|
+
const unsub = unsubCallbacksByKey.get(key)
|
|
39
29
|
if (unsub) {
|
|
40
30
|
unsub()
|
|
41
|
-
|
|
31
|
+
unsubCallbacksByKey.delete(key)
|
|
42
32
|
}
|
|
43
33
|
}
|
|
44
34
|
|
|
45
|
-
const fillSubRequest = (subKey
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const token = findInStore(family,
|
|
50
|
-
socket.emit(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
35
|
+
const fillSubRequest = (subKey: K) => {
|
|
36
|
+
const exposedSubKeys = getFromStore(index, store)
|
|
37
|
+
for (const exposedSubKey of exposedSubKeys) {
|
|
38
|
+
if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) {
|
|
39
|
+
const token = findInStore(family, subKey, store)
|
|
40
|
+
socket.emit(`serve:${token.key}`, getFromStore(token, store))
|
|
41
|
+
const unsubscribe = subscribeToState(
|
|
42
|
+
token,
|
|
43
|
+
({ newValue }) => {
|
|
44
|
+
socket.emit(`serve:${token.key}`, newValue)
|
|
45
|
+
},
|
|
46
|
+
`expose-family:${family.key}:${socket.id}`,
|
|
47
|
+
store,
|
|
54
48
|
)
|
|
49
|
+
unsubCallbacksByKey.set(token.key, unsubscribe)
|
|
50
|
+
socket.on(`unsub:${token.key}`, () => {
|
|
51
|
+
fillUnsubRequest(token.key)
|
|
52
|
+
})
|
|
53
|
+
break
|
|
55
54
|
}
|
|
56
|
-
|
|
57
|
-
const unsubscribeFromTokenCreation = family.subject.subscribe(
|
|
58
|
-
`expose-family:${socket.id}`,
|
|
59
|
-
(token: AtomIO.WritableToken<J>) => {
|
|
60
|
-
const unsub = subscribeToState(
|
|
61
|
-
token,
|
|
62
|
-
({ newValue }) => {
|
|
63
|
-
socket.emit(
|
|
64
|
-
`serve:${family.key}`,
|
|
65
|
-
parseJson(token.family?.subKey || `null`),
|
|
66
|
-
newValue,
|
|
67
|
-
)
|
|
68
|
-
},
|
|
69
|
-
`expose-family:${family.key}:${socket.id}`,
|
|
70
|
-
store,
|
|
71
|
-
)
|
|
72
|
-
unsubFamilyCallbacksByKey.set(token.key, unsub)
|
|
73
|
-
},
|
|
74
|
-
)
|
|
75
|
-
unsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)
|
|
76
|
-
|
|
77
|
-
socket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)
|
|
78
|
-
} else {
|
|
79
|
-
const token = family(subKey)
|
|
80
|
-
socket.emit(`serve:${token.key}`, getFromStore(token, store))
|
|
81
|
-
const unsubscribe = subscribeToState(
|
|
82
|
-
token,
|
|
83
|
-
({ newValue }) => {
|
|
84
|
-
socket.emit(`serve:${token.key}`, newValue)
|
|
85
|
-
},
|
|
86
|
-
`expose-family:${family.key}:${socket.id}`,
|
|
87
|
-
store,
|
|
88
|
-
)
|
|
89
|
-
unsubSingleCallbacksByKey.set(token.key, unsubscribe)
|
|
90
|
-
socket.on(`unsub:${token.key}`, () => {
|
|
91
|
-
fillSingleUnsubRequest(token.key)
|
|
92
|
-
})
|
|
93
55
|
}
|
|
94
56
|
}
|
|
95
57
|
|
|
@@ -97,14 +59,11 @@ export function realtimeFamilyProvider({
|
|
|
97
59
|
|
|
98
60
|
return () => {
|
|
99
61
|
socket.off(`sub:${family.key}`, fillSubRequest)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
for (const [, unsub] of unsubSingleCallbacksByKey) {
|
|
62
|
+
|
|
63
|
+
for (const [, unsub] of unsubCallbacksByKey) {
|
|
104
64
|
unsub()
|
|
105
65
|
}
|
|
106
|
-
|
|
107
|
-
unsubSingleCallbacksByKey.clear()
|
|
66
|
+
unsubCallbacksByKey.clear()
|
|
108
67
|
}
|
|
109
68
|
}
|
|
110
69
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
subscribeToState,
|
|
10
10
|
} from "atom.io/internal"
|
|
11
11
|
import type { Json } from "atom.io/json"
|
|
12
|
-
import {
|
|
12
|
+
import { stringifyJson } from "atom.io/json"
|
|
13
13
|
|
|
14
14
|
import type { ServerConfig } from "."
|
|
15
15
|
|
|
@@ -25,100 +25,42 @@ export function realtimeMutableFamilyProvider({
|
|
|
25
25
|
J extends Json.Serializable,
|
|
26
26
|
K extends Json.Serializable,
|
|
27
27
|
>(
|
|
28
|
-
family: AtomIO.
|
|
28
|
+
family: AtomIO.MutableAtomFamilyToken<T, J, K>,
|
|
29
29
|
index: AtomIO.ReadableToken<Iterable<K>>,
|
|
30
30
|
): () => void {
|
|
31
|
-
const
|
|
32
|
-
const unsubFamilyCallbacksByKey = new Map<string, () => void>()
|
|
31
|
+
const unsubCallbacksByKey = new Map<string, () => void>()
|
|
33
32
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
unsubFamilyCallbacksByKey.clear()
|
|
39
|
-
socket.off(`unsub:${family.key}`, fillFamilyUnsubRequest)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const fillSingleUnsubRequest = (key: string) => {
|
|
43
|
-
socket.off(`unsub:${key}`, fillSingleUnsubRequest)
|
|
44
|
-
const unsub = unsubSingleCallbacksByKey.get(key)
|
|
33
|
+
const fillUnsubRequest = (key: string) => {
|
|
34
|
+
socket.off(`unsub:${key}`, fillUnsubRequest)
|
|
35
|
+
const unsub = unsubCallbacksByKey.get(key)
|
|
45
36
|
if (unsub) {
|
|
46
37
|
unsub()
|
|
47
|
-
|
|
38
|
+
unsubCallbacksByKey.delete(key)
|
|
48
39
|
}
|
|
49
40
|
}
|
|
50
41
|
|
|
51
|
-
const fillSubRequest = (subKey
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const token = findInStore(family,
|
|
42
|
+
const fillSubRequest = (subKey: K) => {
|
|
43
|
+
const exposedSubKeys = getFromStore(index, store)
|
|
44
|
+
for (const exposedSubKey of exposedSubKeys) {
|
|
45
|
+
if (stringifyJson(exposedSubKey) === stringifyJson(subKey)) {
|
|
46
|
+
const token = findInStore(family, subKey, store)
|
|
56
47
|
const jsonToken = getJsonToken(token)
|
|
57
|
-
const
|
|
58
|
-
socket.emit(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
getFromStore(jsonToken, store),
|
|
62
|
-
)
|
|
63
|
-
const unsubFromUpdates = subscribeToState(
|
|
64
|
-
trackerToken,
|
|
48
|
+
const updateToken = getUpdateToken(token)
|
|
49
|
+
socket.emit(`init:${token.key}`, getFromStore(jsonToken, store))
|
|
50
|
+
const unsubscribe = subscribeToState(
|
|
51
|
+
updateToken,
|
|
65
52
|
({ newValue }) => {
|
|
66
|
-
socket.emit(
|
|
67
|
-
`next:${token.key}`,
|
|
68
|
-
parseJson(jsonToken.family?.subKey || `null`),
|
|
69
|
-
newValue,
|
|
70
|
-
)
|
|
53
|
+
socket.emit(`next:${token.key}`, newValue)
|
|
71
54
|
},
|
|
72
55
|
`expose-family:${family.key}:${socket.id}`,
|
|
73
56
|
store,
|
|
74
57
|
)
|
|
75
|
-
|
|
58
|
+
unsubCallbacksByKey.set(token.key, unsubscribe)
|
|
59
|
+
socket.on(`unsub:${token.key}`, () => {
|
|
60
|
+
fillUnsubRequest(token.key)
|
|
61
|
+
})
|
|
62
|
+
break
|
|
76
63
|
}
|
|
77
|
-
const unsubscribeFromTokenCreation = family.subject.subscribe(
|
|
78
|
-
`expose-family:${socket.id}`,
|
|
79
|
-
(token) => {
|
|
80
|
-
const jsonToken = getJsonToken(token)
|
|
81
|
-
const trackerToken = getUpdateToken(token)
|
|
82
|
-
socket.emit(
|
|
83
|
-
`init:${family.key}`,
|
|
84
|
-
parseJson(jsonToken.family?.subKey || `null`),
|
|
85
|
-
getFromStore(jsonToken, store),
|
|
86
|
-
)
|
|
87
|
-
const unsubFromUpdates = subscribeToState(
|
|
88
|
-
trackerToken,
|
|
89
|
-
({ newValue }) => {
|
|
90
|
-
socket.emit(
|
|
91
|
-
`next:${token.key}`,
|
|
92
|
-
parseJson(jsonToken.family?.subKey || `null`),
|
|
93
|
-
newValue,
|
|
94
|
-
)
|
|
95
|
-
},
|
|
96
|
-
`expose-family:${family.key}:${socket.id}`,
|
|
97
|
-
store,
|
|
98
|
-
)
|
|
99
|
-
unsubFamilyCallbacksByKey.set(token.key, unsubFromUpdates)
|
|
100
|
-
},
|
|
101
|
-
)
|
|
102
|
-
unsubFamilyCallbacksByKey.set(family.key, unsubscribeFromTokenCreation)
|
|
103
|
-
|
|
104
|
-
socket.on(`unsub:${family.key}`, fillFamilyUnsubRequest)
|
|
105
|
-
} else {
|
|
106
|
-
const token = family(subKey)
|
|
107
|
-
const jsonToken = getJsonToken(token)
|
|
108
|
-
const updateToken = getUpdateToken(token)
|
|
109
|
-
socket.emit(`init:${token.key}`, getFromStore(jsonToken, store))
|
|
110
|
-
const unsubscribe = subscribeToState(
|
|
111
|
-
updateToken,
|
|
112
|
-
({ newValue }) => {
|
|
113
|
-
socket.emit(`next:${token.key}`, newValue)
|
|
114
|
-
},
|
|
115
|
-
`expose-family:${family.key}:${socket.id}`,
|
|
116
|
-
store,
|
|
117
|
-
)
|
|
118
|
-
unsubSingleCallbacksByKey.set(token.key, unsubscribe)
|
|
119
|
-
socket.on(`unsub:${token.key}`, () => {
|
|
120
|
-
fillSingleUnsubRequest(token.key)
|
|
121
|
-
})
|
|
122
64
|
}
|
|
123
65
|
}
|
|
124
66
|
|
|
@@ -126,14 +68,10 @@ export function realtimeMutableFamilyProvider({
|
|
|
126
68
|
|
|
127
69
|
return () => {
|
|
128
70
|
socket.off(`sub:${family.key}`, fillSubRequest)
|
|
129
|
-
for (const [, unsub] of
|
|
130
|
-
unsub()
|
|
131
|
-
}
|
|
132
|
-
for (const [, unsub] of unsubSingleCallbacksByKey) {
|
|
71
|
+
for (const [, unsub] of unsubCallbacksByKey) {
|
|
133
72
|
unsub()
|
|
134
73
|
}
|
|
135
|
-
|
|
136
|
-
unsubSingleCallbacksByKey.clear()
|
|
74
|
+
unsubCallbacksByKey.clear()
|
|
137
75
|
}
|
|
138
76
|
}
|
|
139
77
|
}
|