@livestore/sync-cf 0.0.0-snapshot-4c2ee347c9a2ee93b7d67a7652d1db36f5b7ee30 → 0.0.0-snapshot-1d99fea7d2ce2c7a5d9ed0a3752f8a7bda6bc3db

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 (50) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/cf-worker/durable-object.d.ts +40 -34
  3. package/dist/cf-worker/durable-object.d.ts.map +1 -1
  4. package/dist/cf-worker/durable-object.js +159 -124
  5. package/dist/cf-worker/durable-object.js.map +1 -1
  6. package/dist/cf-worker/mod.d.ts +3 -0
  7. package/dist/cf-worker/mod.d.ts.map +1 -0
  8. package/dist/cf-worker/mod.js +3 -0
  9. package/dist/cf-worker/mod.js.map +1 -0
  10. package/dist/cf-worker/worker.d.ts +6 -0
  11. package/dist/cf-worker/worker.d.ts.map +1 -0
  12. package/dist/cf-worker/worker.js +29 -0
  13. package/dist/cf-worker/worker.js.map +1 -0
  14. package/dist/common/{index.js → mod.d.ts} +1 -1
  15. package/dist/common/mod.d.ts.map +1 -0
  16. package/dist/common/{index.d.ts → mod.js} +1 -1
  17. package/dist/common/mod.js.map +1 -0
  18. package/dist/common/ws-message-types.d.ts +92 -216
  19. package/dist/common/ws-message-types.d.ts.map +1 -1
  20. package/dist/common/ws-message-types.js +13 -9
  21. package/dist/common/ws-message-types.js.map +1 -1
  22. package/dist/sync-impl/mod.d.ts +2 -0
  23. package/dist/sync-impl/mod.d.ts.map +1 -0
  24. package/dist/sync-impl/mod.js +2 -0
  25. package/dist/sync-impl/mod.js.map +1 -0
  26. package/dist/sync-impl/ws-impl.d.ts +5 -13
  27. package/dist/sync-impl/ws-impl.d.ts.map +1 -1
  28. package/dist/sync-impl/ws-impl.js +69 -69
  29. package/dist/sync-impl/ws-impl.js.map +1 -1
  30. package/package.json +17 -8
  31. package/src/cf-worker/durable-object.ts +236 -153
  32. package/src/cf-worker/mod.ts +2 -0
  33. package/src/cf-worker/worker.ts +39 -0
  34. package/src/common/ws-message-types.ts +18 -9
  35. package/src/sync-impl/ws-impl.ts +120 -121
  36. package/dist/cf-worker/index.d.ts +0 -8
  37. package/dist/cf-worker/index.d.ts.map +0 -1
  38. package/dist/cf-worker/index.js +0 -67
  39. package/dist/cf-worker/index.js.map +0 -1
  40. package/dist/common/index.d.ts.map +0 -1
  41. package/dist/common/index.js.map +0 -1
  42. package/dist/sync-impl/index.d.ts +0 -2
  43. package/dist/sync-impl/index.d.ts.map +0 -1
  44. package/dist/sync-impl/index.js +0 -2
  45. package/dist/sync-impl/index.js.map +0 -1
  46. package/src/cf-worker/index.ts +0 -84
  47. package/tsconfig.json +0 -12
  48. package/wrangler.toml +0 -21
  49. /package/src/common/{index.ts → mod.ts} +0 -0
  50. /package/src/sync-impl/{index.ts → mod.ts} +0 -0
@@ -1,82 +1,70 @@
1
1
  /// <reference lib="dom" />
2
2
 
3
- import type { SyncBackend, SyncBackendOptionsBase } from '@livestore/common'
3
+ import type { SyncBackend } from '@livestore/common'
4
4
  import { InvalidPullError, InvalidPushError } from '@livestore/common'
5
+ import { pick } from '@livestore/utils'
5
6
  import type { Scope } from '@livestore/utils/effect'
6
- import { Deferred, Effect, Option, PubSub, Queue, Schema, Stream, SubscriptionRef } from '@livestore/utils/effect'
7
+ import {
8
+ Deferred,
9
+ Effect,
10
+ Option,
11
+ PubSub,
12
+ Queue,
13
+ Schedule,
14
+ Schema,
15
+ Stream,
16
+ SubscriptionRef,
17
+ WebSocket,
18
+ } from '@livestore/utils/effect'
7
19
  import { nanoid } from '@livestore/utils/nanoid'
8
20
 
9
- import { WSMessage } from '../common/index.js'
21
+ import { WSMessage } from '../common/mod.js'
22
+ import type { SyncMetadata } from '../common/ws-message-types.js'
10
23
 
11
- export interface WsSyncOptions extends SyncBackendOptionsBase {
12
- type: 'cf'
24
+ export interface WsSyncOptions {
13
25
  url: string
14
- roomId: string
26
+ storeId: string
15
27
  }
16
28
 
17
- interface LiveStoreGlobalCf {
18
- syncBackend: WsSyncOptions
19
- }
20
-
21
- declare global {
22
- interface LiveStoreGlobal extends LiveStoreGlobalCf {}
23
- }
24
-
25
- export const makeWsSync = (options: WsSyncOptions): Effect.Effect<SyncBackend<null>, never, Scope.Scope> =>
29
+ export const makeWsSync = (options: WsSyncOptions): Effect.Effect<SyncBackend<SyncMetadata>, never, Scope.Scope> =>
26
30
  Effect.gen(function* () {
27
- const wsUrl = `${options.url}/websocket?room=${options.roomId}`
31
+ // TODO also allow for auth scenarios
32
+ const wsUrl = `${options.url}/websocket?storeId=${options.storeId}`
28
33
 
29
34
  const { isConnected, incomingMessages, send } = yield* connect(wsUrl)
30
35
 
31
- const metadata = Option.none()
32
-
33
36
  const api = {
34
37
  isConnected,
35
- pull: (args, { listenForNew }) =>
36
- listenForNew
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)
57
- : Effect.gen(function* () {
58
- const requestId = nanoid()
59
- const cursor = Option.getOrUndefined(args)?.cursor.global
60
-
61
- yield* send(WSMessage.PullReq.make({ cursor, requestId }))
62
-
63
- return Stream.fromPubSub(incomingMessages).pipe(
64
- Stream.filter((_) => _._tag !== 'WSMessage.PushBroadcast' && _.requestId === requestId),
65
- Stream.tap((_) =>
66
- _._tag === 'WSMessage.Error' ? new InvalidPullError({ message: _.message }) : Effect.void,
67
- ),
68
- Stream.filter(Schema.is(WSMessage.PullRes)),
69
- Stream.takeUntil((_) => _.hasMore === false),
70
- Stream.map((_) => _.events),
71
- Stream.flattenIterables,
72
- Stream.map((mutationEventEncoded) => ({
73
- mutationEventEncoded,
74
- metadata,
75
- persisted: true,
76
- })),
77
- )
78
- }).pipe(Stream.unwrap),
79
- push: (mutationEventEncoded, persisted) =>
38
+ pull: (args) =>
39
+ Effect.gen(function* () {
40
+ const requestId = nanoid()
41
+ const cursor = Option.getOrUndefined(args)?.cursor.global
42
+
43
+ yield* send(WSMessage.PullReq.make({ cursor, requestId }))
44
+
45
+ return Stream.fromPubSub(incomingMessages).pipe(
46
+ Stream.filter((_) => (_._tag === 'WSMessage.PullRes' ? _.requestId === requestId : true)),
47
+ Stream.tap((_) =>
48
+ _._tag === 'WSMessage.Error' && _.requestId === requestId
49
+ ? new InvalidPullError({ message: _.message })
50
+ : Effect.void,
51
+ ),
52
+ Stream.filter(Schema.is(Schema.Union(WSMessage.PushBroadcast, WSMessage.PullRes))),
53
+ Stream.map((msg) =>
54
+ msg._tag === 'WSMessage.PushBroadcast'
55
+ ? { batch: [pick(msg, ['mutationEventEncoded', 'metadata'])], remaining: 0 }
56
+ : {
57
+ batch: msg.events.map(({ mutationEventEncoded, metadata }) => ({
58
+ mutationEventEncoded,
59
+ metadata,
60
+ })),
61
+ remaining: msg.remaining,
62
+ },
63
+ ),
64
+ )
65
+ }).pipe(Stream.unwrap),
66
+
67
+ push: (batch) =>
80
68
  Effect.gen(function* () {
81
69
  const ready = yield* Deferred.make<void, InvalidPushError>()
82
70
  const requestId = nanoid()
@@ -85,11 +73,12 @@ export const makeWsSync = (options: WsSyncOptions): Effect.Effect<SyncBackend<nu
85
73
  Stream.filter((_) => _._tag !== 'WSMessage.PushBroadcast' && _.requestId === requestId),
86
74
  Stream.tap((_) =>
87
75
  _._tag === 'WSMessage.Error'
88
- ? Deferred.fail(ready, new InvalidPushError({ message: _.message }))
76
+ ? Deferred.fail(ready, new InvalidPushError({ reason: { _tag: 'Unexpected', message: _.message } }))
89
77
  : Effect.void,
90
78
  ),
91
79
  Stream.filter(Schema.is(WSMessage.PushAck)),
92
- Stream.filter((_) => _.mutationId === mutationEventEncoded.id.global),
80
+ // TODO bring back filterting of "own events"
81
+ // Stream.filter((_) => _.mutationId === mutationEventEncoded.id.global),
93
82
  Stream.take(1),
94
83
  Stream.tap(() => Deferred.succeed(ready, void 0)),
95
84
  Stream.runDrain,
@@ -97,13 +86,15 @@ export const makeWsSync = (options: WsSyncOptions): Effect.Effect<SyncBackend<nu
97
86
  Effect.fork,
98
87
  )
99
88
 
100
- yield* send(WSMessage.PushReq.make({ mutationEventEncoded, requestId, persisted }))
89
+ yield* send(WSMessage.PushReq.make({ batch, requestId }))
101
90
 
102
- yield* Deferred.await(ready)
91
+ yield* ready
103
92
 
104
- return { metadata }
93
+ const createdAt = new Date().toISOString()
94
+
95
+ return { metadata: Array.from({ length: batch.length }, () => Option.some({ createdAt })) }
105
96
  }),
106
- } satisfies SyncBackend<null>
97
+ } satisfies SyncBackend<SyncMetadata>
107
98
 
108
99
  return api
109
100
  })
@@ -111,9 +102,11 @@ export const makeWsSync = (options: WsSyncOptions): Effect.Effect<SyncBackend<nu
111
102
  const connect = (wsUrl: string) =>
112
103
  Effect.gen(function* () {
113
104
  const isConnected = yield* SubscriptionRef.make(false)
114
- const wsRef: { current: WebSocket | undefined } = { current: undefined }
105
+ const socketRef: { current: globalThis.WebSocket | undefined } = { current: undefined }
115
106
 
116
- const incomingMessages = yield* PubSub.unbounded<Exclude<WSMessage.BackendToClientMessage, WSMessage.Pong>>()
107
+ const incomingMessages = yield* PubSub.unbounded<Exclude<WSMessage.BackendToClientMessage, WSMessage.Pong>>().pipe(
108
+ Effect.acquireRelease(PubSub.shutdown),
109
+ )
117
110
 
118
111
  const waitUntilOnline = isConnected.changes.pipe(Stream.filter(Boolean), Stream.take(1), Stream.runDrain)
119
112
 
@@ -122,80 +115,86 @@ const connect = (wsUrl: string) =>
122
115
  // Wait first until we're online
123
116
  yield* waitUntilOnline
124
117
 
125
- wsRef.current!.send(Schema.encodeSync(Schema.parseJson(WSMessage.Message))(message))
118
+ yield* Effect.spanEvent(
119
+ `Sending message: ${message._tag}`,
120
+ message._tag === 'WSMessage.PushReq'
121
+ ? {
122
+ id: message.batch[0]!.id,
123
+ parentId: message.batch[0]!.parentId,
124
+ batchLength: message.batch.length,
125
+ }
126
+ : message._tag === 'WSMessage.PullReq'
127
+ ? { cursor: message.cursor ?? '-' }
128
+ : {},
129
+ )
130
+
131
+ // TODO use MsgPack instead of JSON to speed up the serialization / reduce the size of the messages
132
+ socketRef.current!.send(Schema.encodeSync(Schema.parseJson(WSMessage.Message))(message))
126
133
  })
127
134
 
128
135
  const innerConnect = Effect.gen(function* () {
129
136
  // If the browser already tells us we're offline, then we'll at least wait until the browser
130
137
  // thinks we're online again. (We'll only know for sure once the WS conneciton is established.)
131
- while (navigator.onLine === false) {
138
+ while (typeof navigator !== 'undefined' && navigator.onLine === false) {
132
139
  yield* Effect.sleep(1000)
133
140
  }
134
141
  // if (navigator.onLine === false) {
135
142
  // yield* Effect.async((cb) => self.addEventListener('online', () => cb(Effect.void)))
136
143
  // }
137
144
 
138
- const ws = new WebSocket(wsUrl)
145
+ const socket = yield* WebSocket.makeWebSocket({ url: wsUrl, reconnect: Schedule.exponential(100) })
146
+
147
+ yield* SubscriptionRef.set(isConnected, true)
148
+ socketRef.current = socket
149
+
139
150
  const connectionClosed = yield* Deferred.make<void>()
140
151
 
141
- const pongMessages = yield* Queue.unbounded<WSMessage.Pong>()
152
+ const pongMessages = yield* Queue.unbounded<WSMessage.Pong>().pipe(Effect.acquireRelease(Queue.shutdown))
142
153
 
143
- const messageHandler = (event: MessageEvent<any>): void => {
144
- const decodedEventRes = Schema.decodeUnknownEither(Schema.parseJson(WSMessage.BackendToClientMessage))(
145
- event.data,
146
- )
154
+ yield* Effect.eventListener(socket, 'message', (event: MessageEvent) =>
155
+ Effect.gen(function* () {
156
+ const decodedEventRes = Schema.decodeUnknownEither(Schema.parseJson(WSMessage.BackendToClientMessage))(
157
+ event.data,
158
+ )
147
159
 
148
- if (decodedEventRes._tag === 'Left') {
149
- console.error('Sync: Invalid message received', decodedEventRes.left)
150
- return
151
- } else {
152
- if (decodedEventRes.right._tag === 'WSMessage.Pong') {
153
- Queue.offer(pongMessages, decodedEventRes.right).pipe(Effect.runSync)
160
+ if (decodedEventRes._tag === 'Left') {
161
+ console.error('Sync: Invalid message received', decodedEventRes.left)
162
+ return
154
163
  } else {
155
- PubSub.publish(incomingMessages, decodedEventRes.right).pipe(Effect.runSync)
164
+ if (decodedEventRes.right._tag === 'WSMessage.Pong') {
165
+ yield* Queue.offer(pongMessages, decodedEventRes.right)
166
+ } else {
167
+ // yield* Effect.logDebug(`decodedEventRes: ${decodedEventRes.right._tag}`)
168
+ yield* PubSub.publish(incomingMessages, decodedEventRes.right)
169
+ }
156
170
  }
157
- }
158
- }
171
+ }),
172
+ )
159
173
 
160
- const offlineHandler = () => {
161
- Deferred.succeed(connectionClosed, void 0).pipe(Effect.runSync)
162
- }
174
+ yield* Effect.eventListener(socket, 'close', () => Deferred.succeed(connectionClosed, void 0))
175
+
176
+ yield* Effect.eventListener(socket, 'error', () =>
177
+ Effect.gen(function* () {
178
+ socket.close(3000, 'Sync: WebSocket error')
179
+ yield* Deferred.succeed(connectionClosed, void 0)
180
+ }),
181
+ )
163
182
 
164
183
  // NOTE it seems that this callback doesn't work reliably on a worker but only via `window.addEventListener`
165
184
  // We might need to proxy the event from the main thread to the worker if we want this to work reliably.
166
- self.addEventListener('offline', offlineHandler)
185
+ // eslint-disable-next-line unicorn/prefer-global-this
186
+ if (typeof self !== 'undefined') {
187
+ // eslint-disable-next-line unicorn/prefer-global-this
188
+ yield* Effect.eventListener(self, 'offline', () => Deferred.succeed(connectionClosed, void 0))
189
+ }
167
190
 
168
191
  yield* Effect.addFinalizer(() =>
169
192
  Effect.gen(function* () {
170
- ws.removeEventListener('message', messageHandler)
171
- self.removeEventListener('offline', offlineHandler)
172
- wsRef.current?.close()
173
- wsRef.current = undefined
193
+ socketRef.current = undefined
174
194
  yield* SubscriptionRef.set(isConnected, false)
175
195
  }),
176
196
  )
177
197
 
178
- ws.addEventListener('message', messageHandler)
179
-
180
- if (ws.readyState === WebSocket.OPEN) {
181
- wsRef.current = ws
182
- SubscriptionRef.set(isConnected, true).pipe(Effect.runSync)
183
- } else {
184
- ws.addEventListener('open', () => {
185
- wsRef.current = ws
186
- SubscriptionRef.set(isConnected, true).pipe(Effect.runSync)
187
- })
188
- }
189
-
190
- ws.addEventListener('close', () => {
191
- Deferred.succeed(connectionClosed, void 0).pipe(Effect.runSync)
192
- })
193
-
194
- ws.addEventListener('error', () => {
195
- ws.close()
196
- Deferred.succeed(connectionClosed, void 0).pipe(Effect.runSync)
197
- })
198
-
199
198
  const checkPingPong = Effect.gen(function* () {
200
199
  // TODO include pong latency infomation in network status
201
200
  yield* send({ _tag: 'WSMessage.Ping', requestId: 'ping' })
@@ -204,7 +203,7 @@ const connect = (wsUrl: string) =>
204
203
  yield* Queue.take(pongMessages).pipe(Effect.timeout(5000))
205
204
 
206
205
  yield* Effect.sleep(25_000)
207
- }).pipe(Effect.withSpan('@livestore/sync-cf:connect:checkPingPong'))
206
+ }).pipe(Effect.withSpan('@livestore/sync-cf:connect:checkPingPong'), Effect.ignore)
208
207
 
209
208
  yield* waitUntilOnline.pipe(
210
209
  Effect.andThen(checkPingPong.pipe(Effect.forever)),
@@ -212,10 +211,10 @@ const connect = (wsUrl: string) =>
212
211
  Effect.forkScoped,
213
212
  )
214
213
 
215
- yield* Deferred.await(connectionClosed)
216
- }).pipe(Effect.scoped)
214
+ yield* connectionClosed
215
+ }).pipe(Effect.scoped, Effect.withSpan('@livestore/sync-cf:connect'))
217
216
 
218
- yield* innerConnect.pipe(Effect.forever, Effect.tapCauseLogPretty, Effect.forkScoped)
217
+ yield* innerConnect.pipe(Effect.forever, Effect.interruptible, Effect.tapCauseLogPretty, Effect.forkScoped)
219
218
 
220
219
  return { isConnected, incomingMessages, send }
221
220
  })
@@ -1,8 +0,0 @@
1
- /// <reference no-default-lib="true"/>
2
- import type { Env } from './durable-object.js';
3
- export * from './durable-object.js';
4
- declare const _default: {
5
- fetch: (request: Request, env: Env, _ctx: ExecutionContext) => Promise<Response>;
6
- };
7
- export default _default;
8
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cf-worker/index.ts"],"names":[],"mappings":";AAMA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AAE9C,cAAc,qBAAqB,CAAA;;qBA4CV,OAAO,OAAO,GAAG,QAAQ,gBAAgB,KAAG,OAAO,CAAC,QAAQ,CAAC;;AADtF,wBAgCC"}
@@ -1,67 +0,0 @@
1
- /// <reference no-default-lib="true"/>
2
- /// <reference lib="esnext" />
3
- export * from './durable-object.js';
4
- // const handleRequest = (request: Request, env: Env) =>
5
- // HttpServer.router.empty.pipe(
6
- // HttpServer.router.get(
7
- // '/websocket',
8
- // Effect.gen(function* () {
9
- // // This example will refer to the same Durable Object instance,
10
- // // since the name "foo" is hardcoded.
11
- // const id = env.WEBSOCKET_SERVER.idFromName('foo')
12
- // const durableObject = env.WEBSOCKET_SERVER.get(id)
13
- // HttpServer.
14
- // // Expect to receive a WebSocket Upgrade request.
15
- // // If there is one, accept the request and return a WebSocket Response.
16
- // const headerRes = yield* HttpServer.request
17
- // .schemaHeaders(
18
- // Schema.Struct({
19
- // Upgrade: Schema.Literal('websocket'),
20
- // }),
21
- // )
22
- // .pipe(Effect.either)
23
- // if (headerRes._tag === 'Left') {
24
- // // return new Response('Durable Object expected Upgrade: websocket', { status: 426 })
25
- // return yield* HttpServer.response.text('Durable Object expected Upgrade: websocket', { status: 426 })
26
- // }
27
- // HttpServer.response.empty
28
- // return yield* Effect.promise(() => durableObject.fetch(request))
29
- // }),
30
- // ),
31
- // HttpServer.router.catchAll((e) => {
32
- // console.log(e)
33
- // return HttpServer.response.empty({ status: 400 })
34
- // }),
35
- // (_) => HttpServer.app.toWebHandler(_)(request),
36
- // // request
37
- // )
38
- // Worker
39
- export default {
40
- fetch: async (request, env, _ctx) => {
41
- const url = new URL(request.url);
42
- const searchParams = url.searchParams;
43
- const roomId = searchParams.get('room');
44
- if (roomId === null) {
45
- return new Response('Room ID is required', { status: 400 });
46
- }
47
- // This example will refer to the same Durable Object instance,
48
- // since the name "foo" is hardcoded.
49
- const id = env.WEBSOCKET_SERVER.idFromName(roomId);
50
- const durableObject = env.WEBSOCKET_SERVER.get(id);
51
- if (url.pathname.endsWith('/websocket')) {
52
- const upgradeHeader = request.headers.get('Upgrade');
53
- if (!upgradeHeader || upgradeHeader !== 'websocket') {
54
- return new Response('Durable Object expected Upgrade: websocket', { status: 426 });
55
- }
56
- return durableObject.fetch(request);
57
- }
58
- return new Response(null, {
59
- status: 400,
60
- statusText: 'Bad Request',
61
- headers: {
62
- 'Content-Type': 'text/plain',
63
- },
64
- });
65
- },
66
- };
67
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cf-worker/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,8BAA8B;AAO9B,cAAc,qBAAqB,CAAA;AAEnC,wDAAwD;AACxD,kCAAkC;AAClC,6BAA6B;AAC7B,sBAAsB;AACtB,kCAAkC;AAClC,0EAA0E;AAC1E,gDAAgD;AAChD,4DAA4D;AAC5D,6DAA6D;AAE7D,sBAAsB;AAEtB,4DAA4D;AAC5D,kFAAkF;AAClF,sDAAsD;AACtD,4BAA4B;AAC5B,8BAA8B;AAC9B,sDAAsD;AACtD,kBAAkB;AAClB,cAAc;AACd,iCAAiC;AAEjC,2CAA2C;AAC3C,kGAAkG;AAClG,kHAAkH;AAClH,YAAY;AAEZ,oCAAoC;AAEpC,2EAA2E;AAC3E,YAAY;AACZ,SAAS;AACT,0CAA0C;AAC1C,uBAAuB;AACvB,0DAA0D;AAC1D,UAAU;AACV,sDAAsD;AACtD,iBAAiB;AACjB,MAAM;AAEN,SAAS;AACT,eAAe;IACb,KAAK,EAAE,KAAK,EAAE,OAAgB,EAAE,GAAQ,EAAE,IAAsB,EAAqB,EAAE;QACrF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAChC,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,CAAA;QACrC,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;QAEvC,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,OAAO,IAAI,QAAQ,CAAC,qBAAqB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;QAC7D,CAAC;QAED,+DAA+D;QAC/D,qCAAqC;QACrC,MAAM,EAAE,GAAG,GAAG,CAAC,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;QAClD,MAAM,aAAa,GAAG,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAElD,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACxC,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YACpD,IAAI,CAAC,aAAa,IAAI,aAAa,KAAK,WAAW,EAAE,CAAC;gBACpD,OAAO,IAAI,QAAQ,CAAC,4CAA4C,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;YACpF,CAAC;YAED,OAAO,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QACrC,CAAC;QAED,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;YACxB,MAAM,EAAE,GAAG;YACX,UAAU,EAAE,aAAa;YACzB,OAAO,EAAE;gBACP,cAAc,EAAE,YAAY;aAC7B;SACF,CAAC,CAAA;IACJ,CAAC;CACF,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,uBAAuB,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,uBAAuB,CAAA"}
@@ -1,2 +0,0 @@
1
- export * from './ws-impl.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync-impl/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA"}
@@ -1,2 +0,0 @@
1
- export * from './ws-impl.js';
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync-impl/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA"}
@@ -1,84 +0,0 @@
1
- /// <reference no-default-lib="true"/>
2
- /// <reference lib="esnext" />
3
-
4
- // import { mutationEventSchemaEncodedAny } from '@livestore/common/schema'
5
- // import { Effect, HttpServer, Schema } from '@livestore/utils/effect'
6
-
7
- import type { Env } from './durable-object.js'
8
-
9
- export * from './durable-object.js'
10
-
11
- // const handleRequest = (request: Request, env: Env) =>
12
- // HttpServer.router.empty.pipe(
13
- // HttpServer.router.get(
14
- // '/websocket',
15
- // Effect.gen(function* () {
16
- // // This example will refer to the same Durable Object instance,
17
- // // since the name "foo" is hardcoded.
18
- // const id = env.WEBSOCKET_SERVER.idFromName('foo')
19
- // const durableObject = env.WEBSOCKET_SERVER.get(id)
20
-
21
- // HttpServer.
22
-
23
- // // Expect to receive a WebSocket Upgrade request.
24
- // // If there is one, accept the request and return a WebSocket Response.
25
- // const headerRes = yield* HttpServer.request
26
- // .schemaHeaders(
27
- // Schema.Struct({
28
- // Upgrade: Schema.Literal('websocket'),
29
- // }),
30
- // )
31
- // .pipe(Effect.either)
32
-
33
- // if (headerRes._tag === 'Left') {
34
- // // return new Response('Durable Object expected Upgrade: websocket', { status: 426 })
35
- // return yield* HttpServer.response.text('Durable Object expected Upgrade: websocket', { status: 426 })
36
- // }
37
-
38
- // HttpServer.response.empty
39
-
40
- // return yield* Effect.promise(() => durableObject.fetch(request))
41
- // }),
42
- // ),
43
- // HttpServer.router.catchAll((e) => {
44
- // console.log(e)
45
- // return HttpServer.response.empty({ status: 400 })
46
- // }),
47
- // (_) => HttpServer.app.toWebHandler(_)(request),
48
- // // request
49
- // )
50
-
51
- // Worker
52
- export default {
53
- fetch: async (request: Request, env: Env, _ctx: ExecutionContext): Promise<Response> => {
54
- const url = new URL(request.url)
55
- const searchParams = url.searchParams
56
- const roomId = searchParams.get('room')
57
-
58
- if (roomId === null) {
59
- return new Response('Room ID is required', { status: 400 })
60
- }
61
-
62
- // This example will refer to the same Durable Object instance,
63
- // since the name "foo" is hardcoded.
64
- const id = env.WEBSOCKET_SERVER.idFromName(roomId)
65
- const durableObject = env.WEBSOCKET_SERVER.get(id)
66
-
67
- if (url.pathname.endsWith('/websocket')) {
68
- const upgradeHeader = request.headers.get('Upgrade')
69
- if (!upgradeHeader || upgradeHeader !== 'websocket') {
70
- return new Response('Durable Object expected Upgrade: websocket', { status: 426 })
71
- }
72
-
73
- return durableObject.fetch(request)
74
- }
75
-
76
- return new Response(null, {
77
- status: 400,
78
- statusText: 'Bad Request',
79
- headers: {
80
- 'Content-Type': 'text/plain',
81
- },
82
- })
83
- },
84
- }
package/tsconfig.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src",
6
- "target": "es2022",
7
- "tsBuildInfoFile": "./dist/.tsbuildinfo",
8
- "types": ["@cloudflare/workers-types"]
9
- },
10
- "include": ["./src"],
11
- "references": [{ "path": "../common" }, { "path": "../utils" }]
12
- }
package/wrangler.toml DELETED
@@ -1,21 +0,0 @@
1
- name = "websocket-server"
2
- main = "./src/cf-worker/index.ts"
3
- compatibility_date = "2024-05-12"
4
-
5
- [[durable_objects.bindings]]
6
- name = "WEBSOCKET_SERVER"
7
- class_name = "WebSocketServer"
8
-
9
- [[migrations]]
10
- tag = "v1"
11
- new_classes = ["WebSocketServer"]
12
-
13
- [[d1_databases]]
14
- binding = "DB"
15
- database_name = "livestore-sync-cf-demo"
16
- database_id = "1c9b5dae-f1fa-49d8-83fa-7bd5b39c4121"
17
- # database_id = "${LIVESTORE_CF_SYNC_DATABASE_ID}"
18
-
19
- [vars]
20
- # should be set via CF dashboard (as secret)
21
- # ADMIN_SECRET = "..."
File without changes
File without changes