@livestore/sync-cf 0.0.58-dev.6 → 0.0.58-dev.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 (44) hide show
  1. package/.wrangler/state/v3/d1/miniflare-D1DatabaseObject/8ee300993f9a2c909aede59646369fa0cc28d25304b9b9af94f54d4543ad60f4.sqlite +0 -0
  2. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/037646d8e1e6ad005a1f4ff1c53fd716bec6a458a8b35ebffd9d5b78ec4ea8a3.sqlite +0 -0
  3. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/1471dd2fd02c2d9eb229fcf3cad5fd330cd295bfdee44481714d91b883756b11.sqlite +0 -0
  4. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/23a5a193673cd4aa1f412953ff8ee7cf6169b146127a42e9b84ce79993672b0a.sqlite +0 -0
  5. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/31569057f8ec82f1d479956671fe58b9f0f4e32a64a68c3ba9c3cb6744f74b3d.sqlite +0 -0
  6. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/354c1d4fbf3d44a1e359b6595b4e7f2e0c320e607cad72290b0f842246dea4c3.sqlite +0 -0
  7. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/472d06f3610154b855a10d98212ec0ae8aa4022def5c932134cafe07ddca6cc4.sqlite +0 -0
  8. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/472d06f3610154b855a10d98212ec0ae8aa4022def5c932134cafe07ddca6cc4.sqlite-shm +0 -0
  9. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/472d06f3610154b855a10d98212ec0ae8aa4022def5c932134cafe07ddca6cc4.sqlite-wal +0 -0
  10. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/56fc052bba96d75e557bb09c4826387d0d3500ab0eb8de9e9b7379b795277338.sqlite +0 -0
  11. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/70a851401fda75ce3a534dacc3f979fceaff8234430fc6333f22112519388493.sqlite +0 -0
  12. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/82afe32c9f3dfe706e088859636f9137e1824b12206e1c0870eef31abcb77d2e.sqlite +0 -0
  13. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/83b7880fabbc105109963ee7d1b9b8560b31a560b6b06cf2c326f4f1eca6e80f.sqlite +0 -0
  14. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/985f28c526c3fe0133b72007b09233c1a827ae9a588ca5cff0e740f2f5da7dd3.sqlite +0 -0
  15. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/a0f452412c79c524292ab9f6895e09605492f5980d8ad6cfc34c0fb88ee6d1cd.sqlite +0 -0
  16. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/a58eeee7c3798f238d9446895cd2d0f140b06589dcdf9a777ed79131eb83ea5b.sqlite +0 -0
  17. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/baf2225931d572afb47e60d5e189ae58b9764ea0a8a501e0ac202a35c2b35084.sqlite +0 -0
  18. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/c27c5b00d9c92102951df7d882fb0b9c9f1a9466e0752265b255041c6c3dc739.sqlite +0 -0
  19. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/c4aa0e61dcd784604bee52282348e0538ab69ec76e1652322bbccd539d9ff3ee.sqlite +0 -0
  20. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/d2245ee36dd16e1f76180d89bd2503f0d4808a5986a58b8677f9a365a793acc6.sqlite +0 -0
  21. package/.wrangler/state/v3/do/websocket-server-WebSocketServer/d87433156774c933da83e962eba16107cf02ade2f133d7205963558fe47db3ff.sqlite +0 -0
  22. package/.wrangler/tmp/dev-33iU4b/index.js +42167 -0
  23. package/.wrangler/tmp/dev-33iU4b/index.js.map +8 -0
  24. package/.wrangler/tmp/dev-rI63Kk/index.js +42165 -0
  25. package/.wrangler/tmp/dev-rI63Kk/index.js.map +8 -0
  26. package/.wrangler/tmp/dev-txPodK/index.js +42118 -0
  27. package/.wrangler/tmp/dev-txPodK/index.js.map +8 -0
  28. package/dist/.tsbuildinfo +1 -1
  29. package/dist/cf-worker/durable-object.d.ts +15 -5
  30. package/dist/cf-worker/durable-object.d.ts.map +1 -1
  31. package/dist/cf-worker/durable-object.js +100 -68
  32. package/dist/cf-worker/durable-object.js.map +1 -1
  33. package/dist/common/ws-message-types.d.ts +238 -50
  34. package/dist/common/ws-message-types.d.ts.map +1 -1
  35. package/dist/common/ws-message-types.js +4 -4
  36. package/dist/common/ws-message-types.js.map +1 -1
  37. package/dist/sync-impl/ws-impl.d.ts.map +1 -1
  38. package/dist/sync-impl/ws-impl.js +16 -13
  39. package/dist/sync-impl/ws-impl.js.map +1 -1
  40. package/package.json +4 -4
  41. package/src/cf-worker/durable-object.ts +123 -80
  42. package/src/common/ws-message-types.ts +15 -5
  43. package/src/sync-impl/ws-impl.ts +31 -20
  44. package/tsconfig.json +1 -0
@@ -1,5 +1,5 @@
1
1
  import { makeColumnSpec } from '@livestore/common'
2
- import { DbSchema, type MutationEvent } from '@livestore/common/schema'
2
+ import { DbSchema, type MutationEvent, mutationEventSchemaAny } from '@livestore/common/schema'
3
3
  import { shouldNeverHappen } from '@livestore/utils'
4
4
  import { Effect, Schema } from '@livestore/utils/effect'
5
5
  import { DurableObject } from 'cloudflare:workers'
@@ -14,14 +14,15 @@ export interface Env {
14
14
 
15
15
  type WebSocketClient = WebSocket
16
16
 
17
- const encodeMessage = Schema.encodeSync(Schema.parseJson(WSMessage.Message))
18
- const decodeMessage = Schema.decodeUnknownEither(Schema.parseJson(WSMessage.Message))
17
+ const encodeOutgoingMessage = Schema.encodeSync(Schema.parseJson(WSMessage.BackendToClientMessage))
18
+ const encodeIncomingMessage = Schema.encodeSync(Schema.parseJson(WSMessage.ClientToBackendMessage))
19
+ const decodeIncomingMessage = Schema.decodeUnknownEither(Schema.parseJson(WSMessage.ClientToBackendMessage))
19
20
 
20
21
  export const mutationLogTable = DbSchema.table('__unused', {
21
- // TODO add parent ids (see https://vlcn.io/blog/crdt-substrate)
22
- id: DbSchema.text({ primaryKey: true }),
23
- mutation: DbSchema.text({ nullable: false }),
24
- args: DbSchema.text({ nullable: false, schema: Schema.parseJson(Schema.Any) }),
22
+ id: DbSchema.integer({ primaryKey: true }),
23
+ parentId: DbSchema.integer({}),
24
+ mutation: DbSchema.text({}),
25
+ args: DbSchema.text({ schema: Schema.parseJson(Schema.Any) }),
25
26
  })
26
27
 
27
28
  // Durable Object
@@ -43,8 +44,8 @@ export class WebSocketServer extends DurableObject<Env> {
43
44
 
44
45
  this.ctx.setWebSocketAutoResponse(
45
46
  new WebSocketRequestResponsePair(
46
- encodeMessage(WSMessage.Ping.make({ requestId: 'ping' })),
47
- encodeMessage(WSMessage.Pong.make({ requestId: 'ping' })),
47
+ encodeIncomingMessage(WSMessage.Ping.make({ requestId: 'ping' })),
48
+ encodeOutgoingMessage(WSMessage.Pong.make({ requestId: 'ping' })),
48
49
  ),
49
50
  )
50
51
 
@@ -58,7 +59,7 @@ export class WebSocketServer extends DurableObject<Env> {
58
59
  }).pipe(Effect.tapCauseLogPretty, Effect.runPromise)
59
60
 
60
61
  webSocketMessage = async (ws: WebSocketClient, message: ArrayBuffer | string) => {
61
- const decodedMessageRes = decodeMessage(message)
62
+ const decodedMessageRes = decodeIncomingMessage(message)
62
63
 
63
64
  if (decodedMessageRes._tag === 'Left') {
64
65
  console.error('Invalid message received', decodedMessageRes.left)
@@ -68,91 +69,116 @@ export class WebSocketServer extends DurableObject<Env> {
68
69
  const decodedMessage = decodedMessageRes.right
69
70
  const requestId = decodedMessage.requestId
70
71
 
71
- switch (decodedMessage._tag) {
72
- case 'WSMessage.PullReq': {
73
- const cursor = decodedMessage.cursor
74
- const CHUNK_SIZE = 100
72
+ try {
73
+ switch (decodedMessage._tag) {
74
+ case 'WSMessage.PullReq': {
75
+ const cursor = decodedMessage.cursor
76
+ const CHUNK_SIZE = 100
75
77
 
76
- // TODO use streaming
77
- const remainingEvents = [...(await this.storage.getEvents(cursor))]
78
+ // TODO use streaming
79
+ const remainingEvents = [...(await this.storage.getEvents(cursor))]
78
80
 
79
- // NOTE we want to make sure the WS server responds at least once with `InitRes` even if `events` is empty
80
- while (true) {
81
- const events = remainingEvents.splice(0, CHUNK_SIZE)
82
- const hasMore = remainingEvents.length > 0
81
+ // NOTE we want to make sure the WS server responds at least once with `InitRes` even if `events` is empty
82
+ while (true) {
83
+ const events = remainingEvents.splice(0, CHUNK_SIZE)
84
+ const encodedEvents = Schema.encodeSync(Schema.Array(mutationEventSchemaAny))(events)
85
+ const hasMore = remainingEvents.length > 0
83
86
 
84
- ws.send(encodeMessage(WSMessage.PullRes.make({ events, hasMore, requestId })))
87
+ ws.send(encodeOutgoingMessage(WSMessage.PullRes.make({ events: encodedEvents, hasMore, requestId })))
85
88
 
86
- if (hasMore === false) {
87
- break
89
+ if (hasMore === false) {
90
+ break
91
+ }
88
92
  }
93
+
94
+ break
89
95
  }
96
+ case 'WSMessage.PushReq': {
97
+ // TODO check whether we could use the Durable Object storage for this to speed up the lookup
98
+ const latestEvent = await this.storage.getLatestEvent()
99
+ const expectedParentId = latestEvent?.id ?? { global: 0, local: 0 }
100
+
101
+ if (decodedMessage.mutationEventEncoded.parentId !== expectedParentId) {
102
+ ws.send(
103
+ encodeOutgoingMessage(
104
+ WSMessage.Error.make({
105
+ message: `Invalid parent id. Received ${decodedMessage.mutationEventEncoded.parentId} but expected ${expectedParentId}`,
106
+ requestId,
107
+ }),
108
+ ),
109
+ )
110
+ return
111
+ }
90
112
 
91
- break
92
- }
93
- case 'WSMessage.PushReq': {
94
- // NOTE we're currently not blocking on this to allow broadcasting right away
95
- // however we should do some mutation validation first (e.g. checking parent event id)
96
- const storePromise = decodedMessage.persisted
97
- ? this.storage.appendEvent(decodedMessage.mutationEventEncoded)
98
- : Promise.resolve()
99
-
100
- ws.send(
101
- encodeMessage(WSMessage.PushAck.make({ mutationId: decodedMessage.mutationEventEncoded.id, requestId })),
102
- )
103
-
104
- // console.debug(`Broadcasting mutation event to ${this.subscribedWebSockets.size} clients`)
105
-
106
- const connectedClients = this.ctx.getWebSockets()
107
-
108
- if (connectedClients.length > 0) {
109
- const broadcastMessage = encodeMessage(
110
- WSMessage.PushBroadcast.make({
111
- mutationEventEncoded: decodedMessage.mutationEventEncoded,
112
- requestId,
113
- persisted: decodedMessage.persisted,
114
- }),
113
+ // TODO handle clientId unique conflict
114
+
115
+ // NOTE we're currently not blocking on this to allow broadcasting right away
116
+ const storePromise = decodedMessage.persisted
117
+ ? this.storage.appendEvent(decodedMessage.mutationEventEncoded)
118
+ : Promise.resolve()
119
+
120
+ ws.send(
121
+ encodeOutgoingMessage(
122
+ WSMessage.PushAck.make({ mutationId: decodedMessage.mutationEventEncoded.id.global, requestId }),
123
+ ),
115
124
  )
116
125
 
117
- for (const conn of connectedClients) {
118
- console.log('Broadcasting to client', conn === ws ? 'self' : 'other')
119
- if (conn !== ws) {
126
+ // console.debug(`Broadcasting mutation event to ${this.subscribedWebSockets.size} clients`)
127
+
128
+ const connectedClients = this.ctx.getWebSockets()
129
+
130
+ if (connectedClients.length > 0) {
131
+ const broadcastMessage = encodeOutgoingMessage(
132
+ WSMessage.PushBroadcast.make({
133
+ mutationEventEncoded: decodedMessage.mutationEventEncoded,
134
+ persisted: decodedMessage.persisted,
135
+ }),
136
+ )
137
+
138
+ for (const conn of connectedClients) {
139
+ console.log('Broadcasting to client', conn === ws ? 'self' : 'other')
140
+ // if (conn !== ws) {
120
141
  conn.send(broadcastMessage)
142
+ // }
121
143
  }
122
144
  }
123
- }
124
145
 
125
- await storePromise
146
+ await storePromise
126
147
 
127
- break
128
- }
129
- case 'WSMessage.AdminResetRoomReq': {
130
- if (decodedMessage.adminSecret !== this.env.ADMIN_SECRET) {
131
- ws.send(encodeMessage(WSMessage.Error.make({ message: 'Invalid admin secret', requestId })))
132
- return
148
+ break
133
149
  }
150
+ case 'WSMessage.AdminResetRoomReq': {
151
+ if (decodedMessage.adminSecret !== this.env.ADMIN_SECRET) {
152
+ ws.send(encodeOutgoingMessage(WSMessage.Error.make({ message: 'Invalid admin secret', requestId })))
153
+ return
154
+ }
134
155
 
135
- await this.storage.resetRoom()
136
- ws.send(encodeMessage(WSMessage.AdminResetRoomRes.make({ requestId })))
156
+ await this.storage.resetRoom()
157
+ ws.send(encodeOutgoingMessage(WSMessage.AdminResetRoomRes.make({ requestId })))
137
158
 
138
- break
139
- }
140
- case 'WSMessage.AdminInfoReq': {
141
- if (decodedMessage.adminSecret !== this.env.ADMIN_SECRET) {
142
- ws.send(encodeMessage(WSMessage.Error.make({ message: 'Invalid admin secret', requestId })))
143
- return
159
+ break
144
160
  }
161
+ case 'WSMessage.AdminInfoReq': {
162
+ if (decodedMessage.adminSecret !== this.env.ADMIN_SECRET) {
163
+ ws.send(encodeOutgoingMessage(WSMessage.Error.make({ message: 'Invalid admin secret', requestId })))
164
+ return
165
+ }
145
166
 
146
- ws.send(
147
- encodeMessage(WSMessage.AdminInfoRes.make({ requestId, info: { durableObjectId: this.ctx.id.toString() } })),
148
- )
167
+ ws.send(
168
+ encodeOutgoingMessage(
169
+ WSMessage.AdminInfoRes.make({ requestId, info: { durableObjectId: this.ctx.id.toString() } }),
170
+ ),
171
+ )
149
172
 
150
- break
151
- }
152
- default: {
153
- console.error('unsupported message', decodedMessage)
154
- return shouldNeverHappen()
173
+ break
174
+ }
175
+ default: {
176
+ console.error('unsupported message', decodedMessage)
177
+ return shouldNeverHappen()
178
+ }
155
179
  }
180
+ } catch (error: any) {
181
+ ws.send(encodeOutgoingMessage(WSMessage.Error.make({ message: error.message, requestId })))
156
182
  }
157
183
  }
158
184
 
@@ -163,25 +189,42 @@ export class WebSocketServer extends DurableObject<Env> {
163
189
  }
164
190
 
165
191
  const makeStorage = (ctx: DurableObjectState, env: Env, dbName: string) => {
166
- const getEvents = async (cursor: string | undefined): Promise<ReadonlyArray<MutationEvent.Any>> => {
167
- const whereClause = cursor ? `WHERE id > '${cursor}'` : ''
192
+ const getLatestEvent = async (): Promise<MutationEvent.Any | undefined> => {
193
+ const rawEvents = await env.DB.prepare(`SELECT * FROM ${dbName} ORDER BY id DESC LIMIT 1`).all()
194
+ if (rawEvents.error) {
195
+ throw new Error(rawEvents.error)
196
+ }
197
+ const events = Schema.decodeUnknownSync(Schema.Array(mutationLogTable.schema))(rawEvents.results).map((e) => ({
198
+ ...e,
199
+ id: { global: e.id, local: 0 },
200
+ parentId: { global: e.parentId, local: 0 },
201
+ }))
202
+ return events[0]
203
+ }
204
+
205
+ const getEvents = async (cursor: number | undefined): Promise<ReadonlyArray<MutationEvent.Any>> => {
206
+ const whereClause = cursor ? `WHERE id > ${cursor}` : ''
168
207
  // TODO handle case where `cursor` was not found
169
208
  const rawEvents = await env.DB.prepare(`SELECT * FROM ${dbName} ${whereClause} ORDER BY id ASC`).all()
170
209
  if (rawEvents.error) {
171
210
  throw new Error(rawEvents.error)
172
211
  }
173
- const events = Schema.decodeUnknownSync(Schema.Array(mutationLogTable.schema))(rawEvents.results)
212
+ const events = Schema.decodeUnknownSync(Schema.Array(mutationLogTable.schema))(rawEvents.results).map((e) => ({
213
+ ...e,
214
+ id: { global: e.id, local: 0 },
215
+ parentId: { global: e.parentId, local: 0 },
216
+ }))
174
217
  return events
175
218
  }
176
219
 
177
220
  const appendEvent = async (event: MutationEvent.Any) => {
178
- const sql = `INSERT INTO ${dbName} (id, args, mutation) VALUES (?, ?, ?)`
179
- await env.DB.prepare(sql).bind(event.id, JSON.stringify(event.args), event.mutation).run()
221
+ const sql = `INSERT INTO ${dbName} (id, parentId, args, mutation) VALUES (?, ?, ?, ?)`
222
+ await env.DB.prepare(sql).bind(event.id, event.parentId, JSON.stringify(event.args), event.mutation).run()
180
223
  }
181
224
 
182
225
  const resetRoom = async () => {
183
226
  await ctx.storage.deleteAll()
184
227
  }
185
228
 
186
- return { getEvents, appendEvent, resetRoom }
229
+ return { getLatestEvent, getEvents, appendEvent, resetRoom }
187
230
  }
@@ -4,7 +4,7 @@ import { Schema } from '@livestore/utils/effect'
4
4
  export const PullReq = Schema.TaggedStruct('WSMessage.PullReq', {
5
5
  requestId: Schema.String,
6
6
  /** Omitting the cursor will start from the beginning */
7
- cursor: Schema.optional(Schema.String),
7
+ cursor: Schema.optional(Schema.Number),
8
8
  })
9
9
 
10
10
  export type PullReq = typeof PullReq.Type
@@ -20,7 +20,6 @@ export const PullRes = Schema.TaggedStruct('WSMessage.PullRes', {
20
20
  export type PullRes = typeof PullRes.Type
21
21
 
22
22
  export const PushBroadcast = Schema.TaggedStruct('WSMessage.PushBroadcast', {
23
- requestId: Schema.String,
24
23
  mutationEventEncoded: mutationEventSchemaEncodedAny,
25
24
  persisted: Schema.Boolean,
26
25
  })
@@ -37,7 +36,7 @@ export type PushReq = typeof PushReq.Type
37
36
 
38
37
  export const PushAck = Schema.TaggedStruct('WSMessage.PushAck', {
39
38
  requestId: Schema.String,
40
- mutationId: Schema.String,
39
+ mutationId: Schema.Number,
41
40
  })
42
41
 
43
42
  export type PushAck = typeof PushAck.Type
@@ -105,5 +104,16 @@ export const Message = Schema.Union(
105
104
  export type Message = typeof Message.Type
106
105
  export type MessageEncoded = typeof Message.Encoded
107
106
 
108
- export const IncomingMessage = Schema.Union(PullRes, PushBroadcast, PushAck, Error, Pong)
109
- export type IncomingMessage = typeof IncomingMessage.Type
107
+ export const BackendToClientMessage = Schema.Union(
108
+ PullRes,
109
+ PushBroadcast,
110
+ PushAck,
111
+ AdminResetRoomRes,
112
+ AdminInfoRes,
113
+ Error,
114
+ Pong,
115
+ )
116
+ export type BackendToClientMessage = typeof BackendToClientMessage.Type
117
+
118
+ export const ClientToBackendMessage = Schema.Union(PullReq, PushReq, AdminResetRoomReq, AdminInfoReq, Ping)
119
+ export type ClientToBackendMessage = typeof ClientToBackendMessage.Type
@@ -2,9 +2,9 @@
2
2
 
3
3
  import type { SyncBackend, SyncBackendOptionsBase } from '@livestore/common'
4
4
  import { InvalidPullError, InvalidPushError } from '@livestore/common'
5
- import { cuid } from '@livestore/utils/cuid'
6
5
  import type { Scope } from '@livestore/utils/effect'
7
6
  import { Deferred, Effect, Option, PubSub, Queue, Schema, Stream, SubscriptionRef } from '@livestore/utils/effect'
7
+ import { nanoid } from '@livestore/utils/nanoid'
8
8
 
9
9
  import { WSMessage } from '../common/index.js'
10
10
 
@@ -34,25 +34,34 @@ export const makeWsSync = (options: WsSyncOptions): Effect.Effect<SyncBackend<nu
34
34
  isConnected,
35
35
  pull: (args, { listenForNew }) =>
36
36
  listenForNew
37
- ? Stream.fromPubSub(incomingMessages).pipe(
38
- Stream.tap((_) =>
39
- _._tag === 'WSMessage.Error' ? new InvalidPullError({ message: _.message }) : Effect.void,
40
- ),
41
- Stream.filter(Schema.is(WSMessage.PushBroadcast)),
42
- Stream.map((_) => ({
43
- mutationEventEncoded: _.mutationEventEncoded,
44
- persisted: _.persisted,
45
- metadata,
46
- })),
47
- )
37
+ ? Effect.gen(function* () {
38
+ const requestId = nanoid()
39
+ const cursor = Option.getOrUndefined(args)?.cursor.global
40
+
41
+ yield* send(WSMessage.PullReq.make({ cursor, requestId }))
42
+
43
+ return Stream.fromPubSub(incomingMessages).pipe(
44
+ Stream.filter((_) => (_._tag === 'WSMessage.PullRes' ? _.requestId === requestId : true)),
45
+ Stream.tap((_) =>
46
+ _._tag === 'WSMessage.Error' ? new InvalidPullError({ message: _.message }) : Effect.void,
47
+ ),
48
+ Stream.filter(Schema.is(Schema.Union(WSMessage.PushBroadcast, WSMessage.PullRes))),
49
+ Stream.map((msg) =>
50
+ msg._tag === 'WSMessage.PushBroadcast'
51
+ ? [{ mutationEventEncoded: msg.mutationEventEncoded, persisted: msg.persisted, metadata }]
52
+ : msg.events.map((_) => ({ mutationEventEncoded: _, metadata, persisted: true })),
53
+ ),
54
+ Stream.flattenIterables,
55
+ )
56
+ }).pipe(Stream.unwrap)
48
57
  : Effect.gen(function* () {
49
- const requestId = cuid()
50
- const cursor = Option.getOrUndefined(args)?.cursor
58
+ const requestId = nanoid()
59
+ const cursor = Option.getOrUndefined(args)?.cursor.global
51
60
 
52
61
  yield* send(WSMessage.PullReq.make({ cursor, requestId }))
53
62
 
54
63
  return Stream.fromPubSub(incomingMessages).pipe(
55
- Stream.filter((_) => _.requestId === requestId),
64
+ Stream.filter((_) => _._tag !== 'WSMessage.PushBroadcast' && _.requestId === requestId),
56
65
  Stream.tap((_) =>
57
66
  _._tag === 'WSMessage.Error' ? new InvalidPullError({ message: _.message }) : Effect.void,
58
67
  ),
@@ -70,17 +79,17 @@ export const makeWsSync = (options: WsSyncOptions): Effect.Effect<SyncBackend<nu
70
79
  push: (mutationEventEncoded, persisted) =>
71
80
  Effect.gen(function* () {
72
81
  const ready = yield* Deferred.make<void, InvalidPushError>()
73
- const requestId = cuid()
82
+ const requestId = nanoid()
74
83
 
75
84
  yield* Stream.fromPubSub(incomingMessages).pipe(
76
- Stream.filter((_) => _.requestId === requestId),
85
+ Stream.filter((_) => _._tag !== 'WSMessage.PushBroadcast' && _.requestId === requestId),
77
86
  Stream.tap((_) =>
78
87
  _._tag === 'WSMessage.Error'
79
88
  ? Deferred.fail(ready, new InvalidPushError({ message: _.message }))
80
89
  : Effect.void,
81
90
  ),
82
91
  Stream.filter(Schema.is(WSMessage.PushAck)),
83
- Stream.filter((_) => _.mutationId === mutationEventEncoded.id),
92
+ Stream.filter((_) => _.mutationId === mutationEventEncoded.id.global),
84
93
  Stream.take(1),
85
94
  Stream.tap(() => Deferred.succeed(ready, void 0)),
86
95
  Stream.runDrain,
@@ -104,7 +113,7 @@ const connect = (wsUrl: string) =>
104
113
  const isConnected = yield* SubscriptionRef.make(false)
105
114
  const wsRef: { current: WebSocket | undefined } = { current: undefined }
106
115
 
107
- const incomingMessages = yield* PubSub.unbounded<Exclude<WSMessage.IncomingMessage, WSMessage.Pong>>()
116
+ const incomingMessages = yield* PubSub.unbounded<Exclude<WSMessage.BackendToClientMessage, WSMessage.Pong>>()
108
117
 
109
118
  const waitUntilOnline = isConnected.changes.pipe(Stream.filter(Boolean), Stream.take(1), Stream.runDrain)
110
119
 
@@ -132,7 +141,9 @@ const connect = (wsUrl: string) =>
132
141
  const pongMessages = yield* Queue.unbounded<WSMessage.Pong>()
133
142
 
134
143
  const messageHandler = (event: MessageEvent<any>): void => {
135
- const decodedEventRes = Schema.decodeUnknownEither(Schema.parseJson(WSMessage.IncomingMessage))(event.data)
144
+ const decodedEventRes = Schema.decodeUnknownEither(Schema.parseJson(WSMessage.BackendToClientMessage))(
145
+ event.data,
146
+ )
136
147
 
137
148
  if (decodedEventRes._tag === 'Left') {
138
149
  console.error('Sync: Invalid message received', decodedEventRes.left)
package/tsconfig.json CHANGED
@@ -3,6 +3,7 @@
3
3
  "compilerOptions": {
4
4
  "outDir": "./dist",
5
5
  "rootDir": "./src",
6
+ "target": "es2022",
6
7
  "tsBuildInfoFile": "./dist/.tsbuildinfo",
7
8
  "types": ["@cloudflare/workers-types"]
8
9
  },