@livestore/sync-cf 0.4.0 → 0.5.0-dev.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/dist/.tsbuildinfo +1 -1
- package/dist/cf-worker/do/durable-object.d.ts.map +1 -1
- package/dist/cf-worker/do/durable-object.js +24 -11
- package/dist/cf-worker/do/durable-object.js.map +1 -1
- package/dist/cf-worker/do/layer.d.ts +22 -18
- package/dist/cf-worker/do/layer.d.ts.map +1 -1
- package/dist/cf-worker/do/layer.js +43 -33
- package/dist/cf-worker/do/layer.js.map +1 -1
- package/dist/cf-worker/do/pull.d.ts +3 -3
- package/dist/cf-worker/do/pull.d.ts.map +1 -1
- package/dist/cf-worker/do/pull.js +17 -18
- package/dist/cf-worker/do/pull.js.map +1 -1
- package/dist/cf-worker/do/push.d.ts +16 -3
- package/dist/cf-worker/do/push.d.ts.map +1 -1
- package/dist/cf-worker/do/push.js +69 -49
- package/dist/cf-worker/do/push.js.map +1 -1
- package/dist/cf-worker/do/push.test.d.ts +2 -0
- package/dist/cf-worker/do/push.test.d.ts.map +1 -0
- package/dist/cf-worker/do/push.test.js +30 -0
- package/dist/cf-worker/do/push.test.js.map +1 -0
- package/dist/cf-worker/do/sqlite.d.ts +34 -46
- package/dist/cf-worker/do/sqlite.d.ts.map +1 -1
- package/dist/cf-worker/do/sqlite.js +1 -1
- package/dist/cf-worker/do/sqlite.js.map +1 -1
- package/dist/cf-worker/do/sync-storage.d.ts +7 -6
- package/dist/cf-worker/do/sync-storage.d.ts.map +1 -1
- package/dist/cf-worker/do/sync-storage.js +26 -14
- package/dist/cf-worker/do/sync-storage.js.map +1 -1
- package/dist/cf-worker/do/transport/do-rpc-server.d.ts +2 -2
- package/dist/cf-worker/do/transport/do-rpc-server.d.ts.map +1 -1
- package/dist/cf-worker/do/transport/do-rpc-server.js +21 -19
- package/dist/cf-worker/do/transport/do-rpc-server.js.map +1 -1
- package/dist/cf-worker/do/transport/http-rpc-server.d.ts +2 -2
- package/dist/cf-worker/do/transport/http-rpc-server.d.ts.map +1 -1
- package/dist/cf-worker/do/transport/http-rpc-server.js +7 -7
- package/dist/cf-worker/do/transport/http-rpc-server.js.map +1 -1
- package/dist/cf-worker/do/transport/ws-rpc-server.d.ts +2 -2
- package/dist/cf-worker/do/transport/ws-rpc-server.d.ts.map +1 -1
- package/dist/cf-worker/do/transport/ws-rpc-server.js +9 -9
- package/dist/cf-worker/do/transport/ws-rpc-server.js.map +1 -1
- package/dist/cf-worker/shared.d.ts +22 -19
- package/dist/cf-worker/shared.d.ts.map +1 -1
- package/dist/cf-worker/shared.js +13 -11
- package/dist/cf-worker/shared.js.map +1 -1
- package/dist/cf-worker/worker.d.ts +6 -6
- package/dist/cf-worker/worker.d.ts.map +1 -1
- package/dist/cf-worker/worker.js +16 -14
- package/dist/cf-worker/worker.js.map +1 -1
- package/dist/client/transport/do-rpc-client.d.ts +13 -4
- package/dist/client/transport/do-rpc-client.d.ts.map +1 -1
- package/dist/client/transport/do-rpc-client.js +55 -26
- package/dist/client/transport/do-rpc-client.js.map +1 -1
- package/dist/client/transport/http-rpc-client.d.ts +3 -3
- package/dist/client/transport/http-rpc-client.d.ts.map +1 -1
- package/dist/client/transport/http-rpc-client.js +30 -21
- package/dist/client/transport/http-rpc-client.js.map +1 -1
- package/dist/client/transport/ws-rpc-client.d.ts +3 -3
- package/dist/client/transport/ws-rpc-client.d.ts.map +1 -1
- package/dist/client/transport/ws-rpc-client.js +20 -16
- package/dist/client/transport/ws-rpc-client.js.map +1 -1
- package/dist/common/do-rpc-schema.d.ts +38 -39
- package/dist/common/do-rpc-schema.d.ts.map +1 -1
- package/dist/common/do-rpc-schema.js +3 -3
- package/dist/common/do-rpc-schema.js.map +1 -1
- package/dist/common/http-rpc-schema.d.ts +36 -37
- package/dist/common/http-rpc-schema.d.ts.map +1 -1
- package/dist/common/http-rpc-schema.js +5 -5
- package/dist/common/http-rpc-schema.js.map +1 -1
- package/dist/common/mod.d.ts +4 -5
- package/dist/common/mod.d.ts.map +1 -1
- package/dist/common/mod.js +3 -4
- package/dist/common/mod.js.map +1 -1
- package/dist/common/sync-message-types.d.ts +116 -133
- package/dist/common/sync-message-types.d.ts.map +1 -1
- package/dist/common/sync-message-types.js +26 -14
- package/dist/common/sync-message-types.js.map +1 -1
- package/dist/common/ws-rpc-schema.d.ts +33 -34
- package/dist/common/ws-rpc-schema.d.ts.map +1 -1
- package/dist/common/ws-rpc-schema.js +4 -4
- package/dist/common/ws-rpc-schema.js.map +1 -1
- package/package.json +21 -37
- package/src/cf-worker/do/durable-object.ts +45 -36
- package/src/cf-worker/do/layer.ts +142 -99
- package/src/cf-worker/do/pull.ts +28 -28
- package/src/cf-worker/do/push.test.ts +40 -0
- package/src/cf-worker/do/push.ts +138 -115
- package/src/cf-worker/do/sqlite.ts +1 -1
- package/src/cf-worker/do/sync-storage.ts +34 -21
- package/src/cf-worker/do/transport/do-rpc-server.ts +23 -34
- package/src/cf-worker/do/transport/http-rpc-server.ts +7 -10
- package/src/cf-worker/do/transport/ws-rpc-server.ts +10 -10
- package/src/cf-worker/shared.ts +18 -13
- package/src/cf-worker/worker.ts +21 -22
- package/src/client/transport/do-rpc-client.ts +82 -40
- package/src/client/transport/http-rpc-client.ts +47 -37
- package/src/client/transport/ws-rpc-client.ts +29 -26
- package/src/common/do-rpc-schema.ts +3 -3
- package/src/common/http-rpc-schema.ts +5 -5
- package/src/common/mod.ts +3 -4
- package/src/common/sync-message-types.ts +16 -16
- package/src/common/ws-rpc-schema.ts +4 -4
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { SyncBackend, UnknownError } from '@livestore/common'
|
|
2
|
-
import type { EventSequenceNumber } from '@livestore/common/schema'
|
|
3
|
-
import {
|
|
4
|
-
import { omit } from '@livestore/utils'
|
|
2
|
+
import type { EventSequenceNumber, LiveStoreEvent } from '@livestore/common/schema'
|
|
3
|
+
import { splitArrayBySize } from '@livestore/common/sync'
|
|
5
4
|
import {
|
|
6
|
-
Chunk,
|
|
7
5
|
type Duration,
|
|
8
6
|
Effect,
|
|
9
7
|
HttpClient,
|
|
@@ -11,11 +9,14 @@ import {
|
|
|
11
9
|
identity,
|
|
12
10
|
Layer,
|
|
13
11
|
Option,
|
|
12
|
+
ReadonlyArray as EffectArray,
|
|
14
13
|
RpcClient,
|
|
15
14
|
RpcSerialization,
|
|
16
15
|
Schedule,
|
|
17
16
|
Schema,
|
|
17
|
+
Semaphore,
|
|
18
18
|
Stream,
|
|
19
|
+
Struct,
|
|
19
20
|
SubscriptionRef,
|
|
20
21
|
UrlParams,
|
|
21
22
|
} from '@livestore/utils/effect'
|
|
@@ -25,6 +26,8 @@ import { SyncHttpRpc } from '../../common/http-rpc-schema.ts'
|
|
|
25
26
|
import { SearchParamsSchema } from '../../common/mod.ts'
|
|
26
27
|
import type { SyncMetadata } from '../../common/sync-message-types.ts'
|
|
27
28
|
|
|
29
|
+
type PushBatchItem = LiveStoreEvent.Global.Encoded
|
|
30
|
+
|
|
28
31
|
export interface HttpSyncOptions {
|
|
29
32
|
/**
|
|
30
33
|
* URL of the sync backend
|
|
@@ -41,7 +44,7 @@ export interface HttpSyncOptions {
|
|
|
41
44
|
* How often to poll for new events
|
|
42
45
|
* @default 5 seconds
|
|
43
46
|
*/
|
|
44
|
-
pollInterval?: Duration.
|
|
47
|
+
pollInterval?: Duration.Input
|
|
45
48
|
}
|
|
46
49
|
ping?: {
|
|
47
50
|
/**
|
|
@@ -52,12 +55,12 @@ export interface HttpSyncOptions {
|
|
|
52
55
|
* How long to wait for a ping response before timing out
|
|
53
56
|
* @default 10 seconds
|
|
54
57
|
*/
|
|
55
|
-
requestTimeout?: Duration.
|
|
58
|
+
requestTimeout?: Duration.Input
|
|
56
59
|
/**
|
|
57
60
|
* How often to send ping requests
|
|
58
61
|
* @default 10 seconds
|
|
59
62
|
*/
|
|
60
|
-
requestInterval?: Duration.
|
|
63
|
+
requestInterval?: Duration.Input
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
|
|
@@ -73,7 +76,7 @@ export const makeHttpSync =
|
|
|
73
76
|
|
|
74
77
|
const livePullInterval = options.livePull?.pollInterval ?? 5_000
|
|
75
78
|
|
|
76
|
-
const urlParamsData = yield* Schema.
|
|
79
|
+
const urlParamsData = yield* Schema.encodeEffect(SearchParamsSchema)({
|
|
77
80
|
storeId,
|
|
78
81
|
payload,
|
|
79
82
|
transport: 'http',
|
|
@@ -83,9 +86,10 @@ export const makeHttpSync =
|
|
|
83
86
|
|
|
84
87
|
// Setup HTTP RPC Protocol
|
|
85
88
|
const HttpProtocolLive = RpcClient.layerProtocolHttp({
|
|
86
|
-
url:
|
|
89
|
+
url: options.url,
|
|
87
90
|
transformClient: HttpClient.mapRequest((request) =>
|
|
88
91
|
request.pipe(
|
|
92
|
+
HttpClientRequest.appendUrlParams(urlParams),
|
|
89
93
|
HttpClientRequest.setHeaders({
|
|
90
94
|
...options.headers,
|
|
91
95
|
// Used in CF Worker to identify the store (additionally to storeId embedded in the RPC requests)
|
|
@@ -100,13 +104,13 @@ export const makeHttpSync =
|
|
|
100
104
|
const pingTimeout = options.ping?.requestTimeout ?? 10_000
|
|
101
105
|
|
|
102
106
|
const ping: SyncBackend.SyncBackend<SyncMetadata>['ping'] = Effect.gen(function* () {
|
|
103
|
-
yield* rpcClient
|
|
107
|
+
yield* rpcClient['SyncHttpRpc.Ping']({ storeId, payload })
|
|
104
108
|
|
|
105
109
|
yield* SubscriptionRef.set(isConnected, true)
|
|
106
110
|
}).pipe(
|
|
107
111
|
UnknownError.mapToUnknownError,
|
|
108
112
|
Effect.timeout(pingTimeout),
|
|
109
|
-
Effect.catchTag('
|
|
113
|
+
Effect.catchTag('TimeoutError', () => SubscriptionRef.set(isConnected, false)),
|
|
110
114
|
)
|
|
111
115
|
|
|
112
116
|
const pingInterval = options.ping?.requestInterval ?? 10_000
|
|
@@ -130,7 +134,7 @@ export const makeHttpSync =
|
|
|
130
134
|
)
|
|
131
135
|
|
|
132
136
|
const pull: SyncBackend.SyncBackend<SyncMetadata>['pull'] = (cursor, options) =>
|
|
133
|
-
rpcClient
|
|
137
|
+
rpcClient['SyncHttpRpc.Pull']({
|
|
134
138
|
storeId,
|
|
135
139
|
payload,
|
|
136
140
|
cursor: mapCursor(cursor),
|
|
@@ -139,34 +143,40 @@ export const makeHttpSync =
|
|
|
139
143
|
? // Phase 2: Simulate `live` pull by polling for new events
|
|
140
144
|
Stream.concatWithLastElement((lastElement) => {
|
|
141
145
|
const initialPhase2Cursor = lastElement.pipe(
|
|
142
|
-
Option.flatMap((_) => Option.
|
|
146
|
+
Option.flatMap((_) => Option.fromNullishOr(_.batch.at(-1)?.eventEncoded.seqNum)),
|
|
143
147
|
Option.map((eventSequenceNumber) => ({ eventSequenceNumber })),
|
|
144
148
|
Option.orElse(() => cursor),
|
|
145
149
|
mapCursor,
|
|
146
150
|
)
|
|
147
151
|
|
|
148
|
-
return Stream.
|
|
152
|
+
return Stream.paginate(initialPhase2Cursor, (currentCursor) =>
|
|
149
153
|
Effect.gen(function* () {
|
|
150
154
|
yield* Effect.sleep(livePullInterval)
|
|
151
155
|
|
|
152
|
-
const items = yield* rpcClient
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
const items = yield* rpcClient['SyncHttpRpc.Pull']({
|
|
157
|
+
storeId,
|
|
158
|
+
payload,
|
|
159
|
+
cursor: currentCursor,
|
|
160
|
+
}).pipe(Stream.runCollect)
|
|
155
161
|
|
|
156
|
-
const nextCursor =
|
|
157
|
-
Option.flatMap((item) => Option.
|
|
162
|
+
const nextCursor = EffectArray.last(items).pipe(
|
|
163
|
+
Option.flatMap((item) => Option.fromNullishOr(item.batch.at(-1)?.eventEncoded.seqNum)),
|
|
158
164
|
Option.map((eventSequenceNumber) => ({ eventSequenceNumber })),
|
|
159
165
|
Option.orElse(() => currentCursor),
|
|
160
166
|
mapCursor,
|
|
161
167
|
)
|
|
162
168
|
|
|
163
|
-
|
|
169
|
+
const page: readonly [typeof items, Option.Option<typeof currentCursor>] = [
|
|
170
|
+
items,
|
|
171
|
+
Option.some(nextCursor),
|
|
172
|
+
]
|
|
173
|
+
return page
|
|
164
174
|
}),
|
|
165
175
|
)
|
|
166
176
|
})
|
|
167
177
|
: identity,
|
|
168
178
|
Stream.tap((res) => backendIdHelper.lazySet(res.backendId)),
|
|
169
|
-
Stream.map((res) => omit(res, ['backendId'])),
|
|
179
|
+
Stream.map((res) => Struct.omit(res, ['backendId'])),
|
|
170
180
|
Stream.mapError((cause) =>
|
|
171
181
|
cause._tag === 'UnknownError' || cause._tag === 'BackendIdMismatchError'
|
|
172
182
|
? cause
|
|
@@ -175,7 +185,7 @@ export const makeHttpSync =
|
|
|
175
185
|
Stream.withSpan('http-sync-client:pull'),
|
|
176
186
|
)
|
|
177
187
|
|
|
178
|
-
const pushSemaphore = yield*
|
|
188
|
+
const pushSemaphore = yield* Semaphore.make(1)
|
|
179
189
|
|
|
180
190
|
const push: SyncBackend.SyncBackend<SyncMetadata>['push'] = Effect.fn('http-sync-client:push')(
|
|
181
191
|
function* (batch) {
|
|
@@ -184,23 +194,23 @@ export const makeHttpSync =
|
|
|
184
194
|
}
|
|
185
195
|
|
|
186
196
|
const backendId = backendIdHelper.get()
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
+
if (EffectArray.isReadonlyArrayNonEmpty(batch) === false) {
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const batchChunks = yield* splitArrayBySize({
|
|
202
|
+
maxItems: MAX_PUSH_EVENTS_PER_REQUEST,
|
|
203
|
+
maxBytes: MAX_HTTP_REQUEST_BYTES,
|
|
204
|
+
encode: (items: ReadonlyArray<PushBatchItem>) => ({
|
|
205
|
+
batch: items,
|
|
206
|
+
storeId,
|
|
207
|
+
payload,
|
|
208
|
+
backendId,
|
|
197
209
|
}),
|
|
198
|
-
|
|
199
|
-
)
|
|
210
|
+
})(batch).pipe(Effect.mapError((cause) => new UnknownError({ cause })))
|
|
200
211
|
|
|
201
|
-
for (const
|
|
202
|
-
|
|
203
|
-
yield* rpcClient.SyncHttpRpc.Push({ storeId, payload, batch: chunkArray, backendId })
|
|
212
|
+
for (const batchChunk of batchChunks) {
|
|
213
|
+
yield* rpcClient['SyncHttpRpc.Push']({ storeId, payload, batch: batchChunk, backendId })
|
|
204
214
|
}
|
|
205
215
|
},
|
|
206
216
|
pushSemaphore.withPermits(1),
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { IsOfflineError, SyncBackend, UnknownError } from '@livestore/common'
|
|
2
2
|
import type { LiveStoreEvent } from '@livestore/common/schema'
|
|
3
|
-
import {
|
|
4
|
-
import { omit } from '@livestore/utils'
|
|
3
|
+
import { splitArrayBySize } from '@livestore/common/sync'
|
|
5
4
|
import {
|
|
6
|
-
|
|
7
|
-
type Duration,
|
|
5
|
+
Duration,
|
|
8
6
|
Effect,
|
|
9
7
|
Layer,
|
|
10
8
|
Option,
|
|
9
|
+
ReadonlyArray as EffectArray,
|
|
11
10
|
RpcClient,
|
|
12
11
|
RpcSerialization,
|
|
13
12
|
Schedule,
|
|
@@ -15,6 +14,7 @@ import {
|
|
|
15
14
|
type Scope,
|
|
16
15
|
Socket,
|
|
17
16
|
Stream,
|
|
17
|
+
Struct,
|
|
18
18
|
SubscriptionRef,
|
|
19
19
|
UrlParams,
|
|
20
20
|
} from '@livestore/utils/effect'
|
|
@@ -48,12 +48,12 @@ export interface WsSyncOptions {
|
|
|
48
48
|
* How long to wait for a ping response before timing out
|
|
49
49
|
* @default 10 seconds
|
|
50
50
|
*/
|
|
51
|
-
requestTimeout?: Duration.
|
|
51
|
+
requestTimeout?: Duration.Input
|
|
52
52
|
/**
|
|
53
53
|
* How often to send ping requests
|
|
54
54
|
* @default 10 seconds
|
|
55
55
|
*/
|
|
56
|
-
requestInterval?: Duration.
|
|
56
|
+
requestInterval?: Duration.Input
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -70,7 +70,7 @@ export const makeWsSync =
|
|
|
70
70
|
(options: WsSyncOptions): SyncBackend.SyncBackendConstructor<SyncMetadata> =>
|
|
71
71
|
({ storeId, payload }) =>
|
|
72
72
|
Effect.gen(function* () {
|
|
73
|
-
const urlParamsData = yield* Schema.
|
|
73
|
+
const urlParamsData = yield* Schema.encodeEffect(SearchParamsSchema)({
|
|
74
74
|
storeId,
|
|
75
75
|
payload,
|
|
76
76
|
transport: 'ws',
|
|
@@ -89,7 +89,7 @@ export const makeWsSync =
|
|
|
89
89
|
// }
|
|
90
90
|
// TODO bring this back in a cross-platform way
|
|
91
91
|
// if (navigator.onLine === false) {
|
|
92
|
-
// yield* Effect.
|
|
92
|
+
// yield* Effect.callback((cb) => self.addEventListener('online', () => cb(Effect.void)))
|
|
93
93
|
// }
|
|
94
94
|
|
|
95
95
|
const pingInterval = options.ping?.requestInterval ?? 10_000
|
|
@@ -97,10 +97,10 @@ export const makeWsSync =
|
|
|
97
97
|
const ProtocolLive = RpcClient.layerProtocolSocketWithIsConnected({
|
|
98
98
|
isConnected,
|
|
99
99
|
retryTransientErrors: Schedule.exponential('1 seconds').pipe(
|
|
100
|
-
Schedule.
|
|
100
|
+
Schedule.modifyDelay(({ duration }) => Effect.succeed(Duration.min(duration, Duration.seconds(30)))),
|
|
101
101
|
Schedule.jittered,
|
|
102
102
|
),
|
|
103
|
-
pingSchedule: Schedule.
|
|
103
|
+
pingSchedule: Schedule.recurs(1).pipe(Schedule.concat(Schedule.fixed(pingInterval))),
|
|
104
104
|
url: wsUrl,
|
|
105
105
|
}).pipe(
|
|
106
106
|
Layer.provide(Socket.layerWebSocket(wsUrl)),
|
|
@@ -122,7 +122,7 @@ export const makeWsSync =
|
|
|
122
122
|
yield* SubscriptionRef.set(isConnected, true)
|
|
123
123
|
}).pipe(
|
|
124
124
|
Effect.timeout(pingTimeout),
|
|
125
|
-
Effect.catchTag('
|
|
125
|
+
Effect.catchTag('TimeoutError', () => SubscriptionRef.set(isConnected, false)),
|
|
126
126
|
UnknownError.mapToUnknownError,
|
|
127
127
|
Effect.withSpan('ping'),
|
|
128
128
|
)
|
|
@@ -133,7 +133,7 @@ export const makeWsSync =
|
|
|
133
133
|
isConnected,
|
|
134
134
|
connect: ping,
|
|
135
135
|
pull: (cursor, options) =>
|
|
136
|
-
rpcClient
|
|
136
|
+
rpcClient['SyncWsRpc.Pull']({
|
|
137
137
|
storeId,
|
|
138
138
|
payload,
|
|
139
139
|
cursor: cursor.pipe(
|
|
@@ -145,7 +145,7 @@ export const makeWsSync =
|
|
|
145
145
|
live: options?.live === true,
|
|
146
146
|
}).pipe(
|
|
147
147
|
Stream.tap((res) => backendIdHelper.lazySet(res.backendId)),
|
|
148
|
-
Stream.map((res) => omit(res, ['backendId'])),
|
|
148
|
+
Stream.map((res) => Struct.omit(res, ['backendId'])),
|
|
149
149
|
Stream.mapError((cause) =>
|
|
150
150
|
cause._tag === 'RpcClientError' && Socket.isSocketError(cause.cause) === true
|
|
151
151
|
? new IsOfflineError({ cause: cause.cause })
|
|
@@ -166,24 +166,27 @@ export const makeWsSync =
|
|
|
166
166
|
backendId: backendIdHelper.get(),
|
|
167
167
|
})
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
169
|
+
if (EffectArray.isReadonlyArrayNonEmpty(batch) === false) {
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const batchChunks = yield* splitArrayBySize({
|
|
174
|
+
maxItems: MAX_PUSH_EVENTS_PER_REQUEST,
|
|
175
|
+
maxBytes: MAX_WS_MESSAGE_BYTES,
|
|
176
|
+
encode: encodePayload,
|
|
177
|
+
})(batch).pipe(Effect.mapError((cause) => new UnknownError({ cause })))
|
|
178
|
+
|
|
179
|
+
for (const batchChunk of batchChunks) {
|
|
180
|
+
yield* rpcClient['SyncWsRpc.Push']({
|
|
180
181
|
storeId,
|
|
181
182
|
payload,
|
|
182
|
-
batch:
|
|
183
|
+
batch: batchChunk,
|
|
183
184
|
backendId: backendIdHelper.get(),
|
|
184
185
|
}).pipe(
|
|
185
186
|
Effect.mapError((cause) =>
|
|
186
|
-
cause._tag === 'UnknownError' ||
|
|
187
|
+
cause._tag === 'UnknownError' ||
|
|
188
|
+
cause._tag === 'ServerAheadError' ||
|
|
189
|
+
cause._tag === 'BackendIdMismatchError'
|
|
187
190
|
? cause
|
|
188
191
|
: new UnknownError({ cause }),
|
|
189
192
|
),
|
|
@@ -11,7 +11,7 @@ const commonPayloadFields = {
|
|
|
11
11
|
*/
|
|
12
12
|
storeId: Schema.String,
|
|
13
13
|
/** Needed for various reasons (e.g. auth) */
|
|
14
|
-
payload: Schema.optional(Schema.
|
|
14
|
+
payload: Schema.optional(Schema.Json),
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export class SyncDoRpc extends RpcGroup.make(
|
|
@@ -35,7 +35,7 @@ export class SyncDoRpc extends RpcGroup.make(
|
|
|
35
35
|
rpcRequestId: Schema.String,
|
|
36
36
|
...SyncMessage.PullResponse.fields,
|
|
37
37
|
}),
|
|
38
|
-
error: Schema.Union(UnknownError, BackendIdMismatchError),
|
|
38
|
+
error: Schema.Union([UnknownError, BackendIdMismatchError]),
|
|
39
39
|
stream: true,
|
|
40
40
|
}),
|
|
41
41
|
Rpc.make('SyncDoRpc.Push', {
|
|
@@ -44,7 +44,7 @@ export class SyncDoRpc extends RpcGroup.make(
|
|
|
44
44
|
...commonPayloadFields,
|
|
45
45
|
},
|
|
46
46
|
success: SyncMessage.PushAck,
|
|
47
|
-
error: Schema.Union(UnknownError, ServerAheadError, BackendIdMismatchError),
|
|
47
|
+
error: Schema.Union([UnknownError, ServerAheadError, BackendIdMismatchError]),
|
|
48
48
|
}),
|
|
49
49
|
Rpc.make('SyncDoRpc.Ping', {
|
|
50
50
|
payload: {
|
|
@@ -14,26 +14,26 @@ export class SyncHttpRpc extends RpcGroup.make(
|
|
|
14
14
|
Rpc.make('SyncHttpRpc.Pull', {
|
|
15
15
|
payload: Schema.Struct({
|
|
16
16
|
storeId: Schema.String,
|
|
17
|
-
payload: Schema.optional(Schema.
|
|
17
|
+
payload: Schema.optional(Schema.Json),
|
|
18
18
|
...SyncMessage.PullRequest.fields,
|
|
19
19
|
}),
|
|
20
20
|
success: SyncMessage.PullResponse,
|
|
21
|
-
error: Schema.Union(UnknownError, BackendIdMismatchError),
|
|
21
|
+
error: Schema.Union([UnknownError, BackendIdMismatchError]),
|
|
22
22
|
stream: true,
|
|
23
23
|
}),
|
|
24
24
|
Rpc.make('SyncHttpRpc.Push', {
|
|
25
25
|
payload: Schema.Struct({
|
|
26
26
|
storeId: Schema.String,
|
|
27
|
-
payload: Schema.optional(Schema.
|
|
27
|
+
payload: Schema.optional(Schema.Json),
|
|
28
28
|
...SyncMessage.PushRequest.fields,
|
|
29
29
|
}),
|
|
30
30
|
success: SyncMessage.PushAck,
|
|
31
|
-
error: Schema.Union(UnknownError, ServerAheadError, BackendIdMismatchError),
|
|
31
|
+
error: Schema.Union([UnknownError, ServerAheadError, BackendIdMismatchError]),
|
|
32
32
|
}),
|
|
33
33
|
Rpc.make('SyncHttpRpc.Ping', {
|
|
34
34
|
payload: Schema.Struct({
|
|
35
35
|
storeId: Schema.String,
|
|
36
|
-
payload: Schema.optional(Schema.
|
|
36
|
+
payload: Schema.optional(Schema.Json),
|
|
37
37
|
}),
|
|
38
38
|
success: SyncMessage.Pong,
|
|
39
39
|
error: UnknownError,
|
package/src/common/mod.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
export { OversizeChunkItemError, splitArrayBySize } from '@livestore/common/sync'
|
|
2
2
|
import { Schema } from '@livestore/utils/effect'
|
|
3
3
|
|
|
4
4
|
export type { CfTypes } from '@livestore/common-cf'
|
|
5
5
|
export * from './constants.ts'
|
|
6
6
|
export { SyncHttpRpc } from './http-rpc-schema.ts'
|
|
7
7
|
export * as SyncMessage from './sync-message-types.ts'
|
|
8
|
-
export { OversizeChunkItemError, splitChunkBySize }
|
|
9
8
|
|
|
10
9
|
export const SearchParamsSchema = Schema.Struct({
|
|
11
10
|
storeId: Schema.String,
|
|
12
|
-
payload: Schema.
|
|
11
|
+
payload: Schema.optional(Schema.StringFromUriComponent.pipe(Schema.decodeTo(Schema.fromJsonString(Schema.Json)))),
|
|
13
12
|
// NOTE `do-rpc` is handled differently
|
|
14
|
-
transport: Schema.
|
|
13
|
+
transport: Schema.Literals(['http', 'ws']),
|
|
15
14
|
})
|
|
16
15
|
|
|
17
16
|
export type SearchParams = typeof SearchParamsSchema.Type
|
|
@@ -11,7 +11,7 @@ import { Schema } from '@livestore/utils/effect'
|
|
|
11
11
|
export const SyncMetadata = Schema.TaggedStruct('SyncMessage.SyncMetadata', {
|
|
12
12
|
/** ISO date format */
|
|
13
13
|
createdAt: Schema.String,
|
|
14
|
-
}).
|
|
14
|
+
}).annotate({ title: '@livestore/sync-cf:SyncMetadata' })
|
|
15
15
|
|
|
16
16
|
export type SyncMetadata = typeof SyncMetadata.Type
|
|
17
17
|
|
|
@@ -23,7 +23,7 @@ export const PullRequest = Schema.Struct({
|
|
|
23
23
|
eventSequenceNumber: EventSequenceNumber.Global.Schema,
|
|
24
24
|
}),
|
|
25
25
|
),
|
|
26
|
-
}).
|
|
26
|
+
}).annotate({ title: '@livestore/sync-cf:PullRequest' })
|
|
27
27
|
|
|
28
28
|
export type PullRequest = typeof PullRequest.Type
|
|
29
29
|
|
|
@@ -36,7 +36,7 @@ export const PullResponse = Schema.Struct({
|
|
|
36
36
|
),
|
|
37
37
|
pageInfo: SyncBackend.PullResPageInfo,
|
|
38
38
|
backendId: BackendId,
|
|
39
|
-
}).
|
|
39
|
+
}).annotate({ title: '@livestore/sync-cf:PullResponse' })
|
|
40
40
|
|
|
41
41
|
export const emptyPullResponse = (backendId: string) =>
|
|
42
42
|
PullResponse.make({
|
|
@@ -50,32 +50,32 @@ export type PullResponse = typeof PullResponse.Type
|
|
|
50
50
|
export const PushRequest = Schema.Struct({
|
|
51
51
|
batch: Schema.Array(LiveStoreEvent.Global.Encoded),
|
|
52
52
|
backendId: Schema.Option(BackendId),
|
|
53
|
-
}).
|
|
53
|
+
}).annotate({ title: '@livestore/sync-cf:PushRequest' })
|
|
54
54
|
|
|
55
55
|
export type PushRequest = typeof PushRequest.Type
|
|
56
56
|
|
|
57
|
-
export const PushAck = Schema.Struct({}).
|
|
57
|
+
export const PushAck = Schema.Struct({}).annotate({
|
|
58
58
|
title: '@livestore/sync-cf:PushAck',
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
export type PushAck = typeof PushAck.Type
|
|
62
62
|
|
|
63
|
-
export const Ping = Schema.TaggedStruct('SyncMessage.Ping', {}).
|
|
63
|
+
export const Ping = Schema.TaggedStruct('SyncMessage.Ping', {}).annotate({ title: '@livestore/sync-cf:Ping' })
|
|
64
64
|
|
|
65
65
|
export type Ping = typeof Ping.Type
|
|
66
66
|
|
|
67
|
-
export const Pong = Schema.TaggedStruct('SyncMessage.Pong', {}).
|
|
67
|
+
export const Pong = Schema.TaggedStruct('SyncMessage.Pong', {}).annotate({ title: '@livestore/sync-cf:Pong' })
|
|
68
68
|
|
|
69
69
|
export type Pong = typeof Pong.Type
|
|
70
70
|
|
|
71
71
|
// Admin operations
|
|
72
72
|
export const AdminResetRoomRequest = Schema.TaggedStruct('SyncMessage.AdminResetRoomRequest', {
|
|
73
73
|
adminSecret: Schema.String,
|
|
74
|
-
}).
|
|
74
|
+
}).annotate({ title: '@livestore/sync-cf:AdminResetRoomRequest' })
|
|
75
75
|
|
|
76
76
|
export type AdminResetRoomRequest = typeof AdminResetRoomRequest.Type
|
|
77
77
|
|
|
78
|
-
export const AdminResetRoomResponse = Schema.TaggedStruct('SyncMessage.AdminResetRoomResponse', {}).
|
|
78
|
+
export const AdminResetRoomResponse = Schema.TaggedStruct('SyncMessage.AdminResetRoomResponse', {}).annotate({
|
|
79
79
|
title: '@livestore/sync-cf:AdminResetRoomResponse',
|
|
80
80
|
})
|
|
81
81
|
|
|
@@ -83,7 +83,7 @@ export type AdminResetRoomResponse = typeof AdminResetRoomResponse.Type
|
|
|
83
83
|
|
|
84
84
|
export const AdminInfoRequest = Schema.TaggedStruct('SyncMessage.AdminInfoRequest', {
|
|
85
85
|
adminSecret: Schema.String,
|
|
86
|
-
}).
|
|
86
|
+
}).annotate({ title: '@livestore/sync-cf:AdminInfoRequest' })
|
|
87
87
|
|
|
88
88
|
export type AdminInfoRequest = typeof AdminInfoRequest.Type
|
|
89
89
|
|
|
@@ -91,27 +91,27 @@ export const AdminInfoResponse = Schema.TaggedStruct('SyncMessage.AdminInfoRespo
|
|
|
91
91
|
info: Schema.Struct({
|
|
92
92
|
durableObjectId: Schema.String,
|
|
93
93
|
}),
|
|
94
|
-
}).
|
|
94
|
+
}).annotate({ title: '@livestore/sync-cf:AdminInfoResponse' })
|
|
95
95
|
|
|
96
96
|
export type AdminInfoResponse = typeof AdminInfoResponse.Type
|
|
97
97
|
|
|
98
|
-
export const BackendToClientMessage = Schema.Union(
|
|
98
|
+
export const BackendToClientMessage = Schema.Union([
|
|
99
99
|
PullResponse,
|
|
100
100
|
PushAck,
|
|
101
101
|
Pong,
|
|
102
102
|
AdminResetRoomResponse,
|
|
103
103
|
AdminInfoResponse,
|
|
104
|
-
)
|
|
104
|
+
])
|
|
105
105
|
export type BackendToClientMessage = typeof BackendToClientMessage.Type
|
|
106
106
|
|
|
107
|
-
export const ClientToBackendMessage = Schema.Union(
|
|
107
|
+
export const ClientToBackendMessage = Schema.Union([
|
|
108
108
|
PullRequest,
|
|
109
109
|
PushRequest,
|
|
110
110
|
Ping,
|
|
111
111
|
AdminResetRoomRequest,
|
|
112
112
|
AdminInfoRequest,
|
|
113
|
-
)
|
|
113
|
+
])
|
|
114
114
|
export type ClientToBackendMessage = typeof ClientToBackendMessage.Type
|
|
115
115
|
|
|
116
|
-
export const Message = Schema.Union(BackendToClientMessage, ClientToBackendMessage)
|
|
116
|
+
export const Message = Schema.Union([BackendToClientMessage, ClientToBackendMessage])
|
|
117
117
|
export type Message = typeof Message.Type
|
|
@@ -14,23 +14,23 @@ export class SyncWsRpc extends RpcGroup.make(
|
|
|
14
14
|
Rpc.make('SyncWsRpc.Pull', {
|
|
15
15
|
payload: Schema.Struct({
|
|
16
16
|
storeId: Schema.String,
|
|
17
|
-
payload: Schema.optional(Schema.
|
|
17
|
+
payload: Schema.optional(Schema.Json),
|
|
18
18
|
/** Whether to keep the pull stream alive and wait for more events */
|
|
19
19
|
live: Schema.Boolean,
|
|
20
20
|
...SyncMessage.PullRequest.fields,
|
|
21
21
|
}),
|
|
22
22
|
success: SyncMessage.PullResponse,
|
|
23
|
-
error: Schema.Union(UnknownError, BackendIdMismatchError),
|
|
23
|
+
error: Schema.Union([UnknownError, BackendIdMismatchError]),
|
|
24
24
|
stream: true,
|
|
25
25
|
}),
|
|
26
26
|
Rpc.make('SyncWsRpc.Push', {
|
|
27
27
|
payload: Schema.Struct({
|
|
28
28
|
storeId: Schema.String,
|
|
29
|
-
payload: Schema.optional(Schema.
|
|
29
|
+
payload: Schema.optional(Schema.Json),
|
|
30
30
|
...SyncMessage.PushRequest.fields,
|
|
31
31
|
}),
|
|
32
32
|
success: SyncMessage.PushAck,
|
|
33
|
-
error: Schema.Union(UnknownError, ServerAheadError, BackendIdMismatchError),
|
|
33
|
+
error: Schema.Union([UnknownError, ServerAheadError, BackendIdMismatchError]),
|
|
34
34
|
}),
|
|
35
35
|
// Ping <> Pong is handled by DO WS auto-response
|
|
36
36
|
// TODO add admin RPCs
|