@livestore/sync-cf 0.3.0-dev.4 → 0.3.0-dev.41

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 (51) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/cf-worker/durable-object.d.ts +58 -47
  3. package/dist/cf-worker/durable-object.d.ts.map +1 -1
  4. package/dist/cf-worker/durable-object.js +233 -156
  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 +12 -0
  11. package/dist/cf-worker/worker.d.ts.map +1 -0
  12. package/dist/cf-worker/worker.js +58 -0
  13. package/dist/cf-worker/worker.js.map +1 -0
  14. package/dist/common/mod.d.ts +7 -0
  15. package/dist/common/mod.d.ts.map +1 -0
  16. package/dist/common/mod.js +7 -0
  17. package/dist/common/mod.js.map +1 -0
  18. package/dist/common/ws-message-types.d.ts +163 -266
  19. package/dist/common/ws-message-types.d.ts.map +1 -1
  20. package/dist/common/ws-message-types.js +19 -24
  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 +3 -15
  27. package/dist/sync-impl/ws-impl.d.ts.map +1 -1
  28. package/dist/sync-impl/ws-impl.js +89 -36
  29. package/dist/sync-impl/ws-impl.js.map +1 -1
  30. package/package.json +15 -13
  31. package/src/cf-worker/durable-object.ts +309 -177
  32. package/src/cf-worker/mod.ts +2 -0
  33. package/src/cf-worker/worker.ts +85 -0
  34. package/src/common/mod.ts +8 -0
  35. package/src/common/ws-message-types.ts +22 -36
  36. package/src/sync-impl/ws-impl.ts +146 -100
  37. package/dist/cf-worker/index.d.ts +0 -8
  38. package/dist/cf-worker/index.d.ts.map +0 -1
  39. package/dist/cf-worker/index.js +0 -67
  40. package/dist/cf-worker/index.js.map +0 -1
  41. package/dist/common/index.d.ts +0 -2
  42. package/dist/common/index.d.ts.map +0 -1
  43. package/dist/common/index.js +0 -2
  44. package/dist/common/index.js.map +0 -1
  45. package/dist/sync-impl/index.d.ts +0 -2
  46. package/dist/sync-impl/index.d.ts.map +0 -1
  47. package/dist/sync-impl/index.js +0 -2
  48. package/dist/sync-impl/index.js.map +0 -1
  49. package/src/cf-worker/index.ts +0 -84
  50. package/src/common/index.ts +0 -1
  51. /package/src/sync-impl/{index.ts → mod.ts} +0 -0
@@ -1,89 +1,83 @@
1
- import { MutationEvent } from '@livestore/common/schema'
1
+ import { LiveStoreEvent } from '@livestore/common/schema'
2
2
  import { Schema } from '@livestore/utils/effect'
3
3
 
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
7
  cursor: Schema.optional(Schema.Number),
8
- })
8
+ }).annotations({ title: '@livestore/sync-cf:PullReq' })
9
9
 
10
10
  export type PullReq = typeof PullReq.Type
11
11
 
12
12
  export const SyncMetadata = Schema.Struct({
13
13
  /** ISO date format */
14
14
  createdAt: Schema.String,
15
- })
15
+ }).annotations({ title: '@livestore/sync-cf:SyncMetadata' })
16
16
 
17
17
  export type SyncMetadata = typeof SyncMetadata.Type
18
18
 
19
19
  export const PullRes = Schema.TaggedStruct('WSMessage.PullRes', {
20
- requestId: Schema.String,
21
- events: Schema.Array(
20
+ batch: Schema.Array(
22
21
  Schema.Struct({
23
- mutationEventEncoded: MutationEvent.EncodedAny,
22
+ eventEncoded: LiveStoreEvent.AnyEncodedGlobal,
24
23
  metadata: Schema.Option(SyncMetadata),
25
24
  }),
26
25
  ),
26
+ requestId: Schema.Struct({ context: Schema.Literal('pull', 'push'), requestId: Schema.String }),
27
27
  remaining: Schema.Number,
28
- })
28
+ }).annotations({ title: '@livestore/sync-cf:PullRes' })
29
29
 
30
30
  export type PullRes = typeof PullRes.Type
31
31
 
32
- export const PushBroadcast = Schema.TaggedStruct('WSMessage.PushBroadcast', {
33
- mutationEventEncoded: MutationEvent.EncodedAny,
34
- metadata: Schema.Option(SyncMetadata),
35
- })
36
-
37
- export type PushBroadcast = typeof PushBroadcast.Type
38
-
39
32
  export const PushReq = Schema.TaggedStruct('WSMessage.PushReq', {
40
33
  requestId: Schema.String,
41
- batch: Schema.Array(MutationEvent.EncodedAny),
42
- })
34
+ batch: Schema.Array(LiveStoreEvent.AnyEncodedGlobal),
35
+ }).annotations({ title: '@livestore/sync-cf:PushReq' })
43
36
 
44
37
  export type PushReq = typeof PushReq.Type
45
38
 
46
39
  export const PushAck = Schema.TaggedStruct('WSMessage.PushAck', {
47
40
  requestId: Schema.String,
48
- mutationId: Schema.Number,
49
- })
41
+ }).annotations({ title: '@livestore/sync-cf:PushAck' })
50
42
 
51
43
  export type PushAck = typeof PushAck.Type
52
44
 
53
45
  export const Error = Schema.TaggedStruct('WSMessage.Error', {
54
46
  requestId: Schema.String,
55
47
  message: Schema.String,
56
- })
48
+ }).annotations({ title: '@livestore/sync-cf:Error' })
49
+
50
+ export type Error = typeof Error.Type
57
51
 
58
52
  export const Ping = Schema.TaggedStruct('WSMessage.Ping', {
59
53
  requestId: Schema.Literal('ping'),
60
- })
54
+ }).annotations({ title: '@livestore/sync-cf:Ping' })
61
55
 
62
56
  export type Ping = typeof Ping.Type
63
57
 
64
58
  export const Pong = Schema.TaggedStruct('WSMessage.Pong', {
65
59
  requestId: Schema.Literal('ping'),
66
- })
60
+ }).annotations({ title: '@livestore/sync-cf:Pong' })
67
61
 
68
62
  export type Pong = typeof Pong.Type
69
63
 
70
64
  export const AdminResetRoomReq = Schema.TaggedStruct('WSMessage.AdminResetRoomReq', {
71
65
  requestId: Schema.String,
72
66
  adminSecret: Schema.String,
73
- })
67
+ }).annotations({ title: '@livestore/sync-cf:AdminResetRoomReq' })
74
68
 
75
69
  export type AdminResetRoomReq = typeof AdminResetRoomReq.Type
76
70
 
77
71
  export const AdminResetRoomRes = Schema.TaggedStruct('WSMessage.AdminResetRoomRes', {
78
72
  requestId: Schema.String,
79
- })
73
+ }).annotations({ title: '@livestore/sync-cf:AdminResetRoomRes' })
80
74
 
81
75
  export type AdminResetRoomRes = typeof AdminResetRoomRes.Type
82
76
 
83
77
  export const AdminInfoReq = Schema.TaggedStruct('WSMessage.AdminInfoReq', {
84
78
  requestId: Schema.String,
85
79
  adminSecret: Schema.String,
86
- })
80
+ }).annotations({ title: '@livestore/sync-cf:AdminInfoReq' })
87
81
 
88
82
  export type AdminInfoReq = typeof AdminInfoReq.Type
89
83
 
@@ -92,14 +86,13 @@ export const AdminInfoRes = Schema.TaggedStruct('WSMessage.AdminInfoRes', {
92
86
  info: Schema.Struct({
93
87
  durableObjectId: Schema.String,
94
88
  }),
95
- })
89
+ }).annotations({ title: '@livestore/sync-cf:AdminInfoRes' })
96
90
 
97
91
  export type AdminInfoRes = typeof AdminInfoRes.Type
98
92
 
99
93
  export const Message = Schema.Union(
100
94
  PullReq,
101
95
  PullRes,
102
- PushBroadcast,
103
96
  PushReq,
104
97
  PushAck,
105
98
  Error,
@@ -109,19 +102,12 @@ export const Message = Schema.Union(
109
102
  AdminResetRoomRes,
110
103
  AdminInfoReq,
111
104
  AdminInfoRes,
112
- )
105
+ ).annotations({ title: '@livestore/sync-cf:Message' })
106
+
113
107
  export type Message = typeof Message.Type
114
108
  export type MessageEncoded = typeof Message.Encoded
115
109
 
116
- export const BackendToClientMessage = Schema.Union(
117
- PullRes,
118
- PushBroadcast,
119
- PushAck,
120
- AdminResetRoomRes,
121
- AdminInfoRes,
122
- Error,
123
- Pong,
124
- )
110
+ export const BackendToClientMessage = Schema.Union(PullRes, PushAck, AdminResetRoomRes, AdminInfoRes, Error, Pong)
125
111
  export type BackendToClientMessage = typeof BackendToClientMessage.Type
126
112
 
127
113
  export const ClientToBackendMessage = Schema.Union(PullReq, PushReq, AdminResetRoomReq, AdminInfoReq, Ping)
@@ -1,9 +1,9 @@
1
1
  /// <reference lib="dom" />
2
2
 
3
- import type { SyncBackend, SyncBackendOptionsBase } from '@livestore/common'
4
- import { InvalidPullError, InvalidPushError } from '@livestore/common'
5
- import { pick } from '@livestore/utils'
6
- import type { Scope } from '@livestore/utils/effect'
3
+ import type { SyncBackend, SyncBackendConstructor } from '@livestore/common'
4
+ import { InvalidPullError, InvalidPushError, UnexpectedError } from '@livestore/common'
5
+ import { EventId } from '@livestore/common/schema'
6
+ import { LS_DEV, shouldNeverHappen } from '@livestore/utils'
7
7
  import {
8
8
  Deferred,
9
9
  Effect,
@@ -14,98 +14,139 @@ import {
14
14
  Schema,
15
15
  Stream,
16
16
  SubscriptionRef,
17
+ UrlParams,
17
18
  WebSocket,
18
19
  } from '@livestore/utils/effect'
19
20
  import { nanoid } from '@livestore/utils/nanoid'
20
21
 
21
- import { WSMessage } from '../common/index.js'
22
+ import { SearchParamsSchema, WSMessage } from '../common/mod.js'
22
23
  import type { SyncMetadata } from '../common/ws-message-types.js'
23
24
 
24
- export interface WsSyncOptions extends SyncBackendOptionsBase {
25
- type: 'cf'
25
+ export interface WsSyncOptions {
26
26
  url: string
27
- roomId: string
28
27
  }
29
28
 
30
- interface LiveStoreGlobalCf {
31
- syncBackend: WsSyncOptions
32
- }
33
-
34
- declare global {
35
- interface LiveStoreGlobal extends LiveStoreGlobalCf {}
36
- }
37
-
38
- export const makeWsSync = (options: WsSyncOptions): Effect.Effect<SyncBackend<SyncMetadata>, never, Scope.Scope> =>
39
- Effect.gen(function* () {
40
- const wsUrl = `${options.url}/websocket?room=${options.roomId}`
41
-
42
- const { isConnected, incomingMessages, send } = yield* connect(wsUrl)
43
-
44
- const api = {
45
- isConnected,
46
- pull: (args) =>
47
- Effect.gen(function* () {
48
- const requestId = nanoid()
49
- const cursor = Option.getOrUndefined(args)?.cursor.global
50
-
51
- yield* send(WSMessage.PullReq.make({ cursor, requestId }))
52
-
53
- return Stream.fromPubSub(incomingMessages).pipe(
54
- Stream.filter((_) => (_._tag === 'WSMessage.PullRes' ? _.requestId === requestId : true)),
55
- Stream.tap((_) =>
56
- _._tag === 'WSMessage.Error' && _.requestId === requestId
57
- ? new InvalidPullError({ message: _.message })
58
- : Effect.void,
59
- ),
60
- Stream.filter(Schema.is(Schema.Union(WSMessage.PushBroadcast, WSMessage.PullRes))),
61
- Stream.map((msg) =>
62
- msg._tag === 'WSMessage.PushBroadcast'
63
- ? { batch: [pick(msg, ['mutationEventEncoded', 'metadata'])], remaining: 0 }
64
- : {
65
- batch: msg.events.map(({ mutationEventEncoded, metadata }) => ({
66
- mutationEventEncoded,
67
- metadata,
68
- })),
69
- remaining: msg.remaining,
70
- },
71
- ),
72
- )
73
- }).pipe(Stream.unwrap),
74
-
75
- push: (batch) =>
76
- Effect.gen(function* () {
77
- const ready = yield* Deferred.make<void, InvalidPushError>()
78
- const requestId = nanoid()
79
-
80
- yield* Stream.fromPubSub(incomingMessages).pipe(
81
- Stream.filter((_) => _._tag !== 'WSMessage.PushBroadcast' && _.requestId === requestId),
82
- Stream.tap((_) =>
83
- _._tag === 'WSMessage.Error'
84
- ? Deferred.fail(ready, new InvalidPushError({ reason: { _tag: 'Unexpected', message: _.message } }))
85
- : Effect.void,
86
- ),
87
- Stream.filter(Schema.is(WSMessage.PushAck)),
88
- // TODO bring back filterting of "own events"
89
- // Stream.filter((_) => _.mutationId === mutationEventEncoded.id.global),
90
- Stream.take(1),
91
- Stream.tap(() => Deferred.succeed(ready, void 0)),
92
- Stream.runDrain,
93
- Effect.tapCauseLogPretty,
94
- Effect.fork,
95
- )
96
-
97
- yield* send(WSMessage.PushReq.make({ batch, requestId }))
98
-
99
- yield* ready
100
-
101
- const createdAt = new Date().toISOString()
102
-
103
- return { metadata: Array.from({ length: batch.length }, () => Option.some({ createdAt })) }
104
- }),
105
- } satisfies SyncBackend<SyncMetadata>
29
+ export const makeCfSync =
30
+ (options: WsSyncOptions): SyncBackendConstructor<SyncMetadata> =>
31
+ ({ storeId, payload }) =>
32
+ Effect.gen(function* () {
33
+ const urlParamsData = yield* Schema.encode(SearchParamsSchema)({
34
+ storeId,
35
+ payload,
36
+ }).pipe(UnexpectedError.mapToUnexpectedError)
37
+
38
+ const urlParams = UrlParams.fromInput(urlParamsData)
39
+ const wsUrl = `${options.url}/websocket?${UrlParams.toString(urlParams)}`
40
+
41
+ const { isConnected, incomingMessages, send } = yield* connect(wsUrl)
42
+
43
+ /**
44
+ * We need to account for the scenario where push-caused PullRes message arrive before the pull-caused PullRes message.
45
+ * i.e. a scenario where the WS connection is created but before the server processed the initial pull, a push from
46
+ * another client triggers a PullRes message sent to this client which we need to stash until our pull-caused
47
+ * PullRes message arrives at which point we can combine the stashed events with the pull-caused events and continue.
48
+ */
49
+ const stashedPullBatch: WSMessage.PullRes['batch'][number][] = []
50
+
51
+ // We currently only support one pull stream for a sync backend.
52
+ let pullStarted = false
53
+
54
+ const api = {
55
+ isConnected,
56
+ // Currently we're already eagerly connecting when the sync backend is created but we might want to refactor this later to clean this up
57
+ connect: Effect.void,
58
+ pull: (args) =>
59
+ Effect.gen(function* () {
60
+ if (pullStarted) {
61
+ return shouldNeverHappen(`Pull already started for this sync backend.`)
62
+ }
106
63
 
107
- return api
108
- })
64
+ pullStarted = true
65
+
66
+ let pullResponseReceived = false
67
+
68
+ const requestId = nanoid()
69
+ const cursor = Option.getOrUndefined(args)?.cursor.global
70
+
71
+ yield* send(WSMessage.PullReq.make({ cursor, requestId }))
72
+
73
+ return Stream.fromPubSub(incomingMessages).pipe(
74
+ Stream.tap((_) =>
75
+ _._tag === 'WSMessage.Error' && _.requestId === requestId
76
+ ? new InvalidPullError({ message: _.message })
77
+ : Effect.void,
78
+ ),
79
+ Stream.filterMap((msg) => {
80
+ if (msg._tag === 'WSMessage.PullRes') {
81
+ if (msg.requestId.context === 'pull') {
82
+ if (msg.requestId.requestId === requestId) {
83
+ pullResponseReceived = true
84
+
85
+ if (stashedPullBatch.length > 0 && msg.remaining === 0) {
86
+ const pullResHead = msg.batch.at(-1)?.eventEncoded.id ?? EventId.ROOT.global
87
+ // Index where stashed events are greater than pullResHead
88
+ const newPartialBatchIndex = stashedPullBatch.findIndex(
89
+ (batchItem) => batchItem.eventEncoded.id > pullResHead,
90
+ )
91
+ const batchWithNewStashedEvents =
92
+ newPartialBatchIndex === -1 ? [] : stashedPullBatch.slice(newPartialBatchIndex)
93
+ const combinedBatch = [...msg.batch, ...batchWithNewStashedEvents]
94
+ return Option.some({ ...msg, batch: combinedBatch, remaining: 0 })
95
+ } else {
96
+ return Option.some(msg)
97
+ }
98
+ } else {
99
+ // Ignore
100
+ return Option.none()
101
+ }
102
+ } else {
103
+ if (pullResponseReceived) {
104
+ return Option.some(msg)
105
+ } else {
106
+ stashedPullBatch.push(...msg.batch)
107
+ return Option.none()
108
+ }
109
+ }
110
+ }
111
+
112
+ return Option.none()
113
+ }),
114
+ )
115
+ }).pipe(Stream.unwrap),
116
+
117
+ push: (batch) =>
118
+ Effect.gen(function* () {
119
+ const pushAck = yield* Deferred.make<void, InvalidPushError>()
120
+ const requestId = nanoid()
121
+
122
+ yield* Stream.fromPubSub(incomingMessages).pipe(
123
+ Stream.tap((_) =>
124
+ _._tag === 'WSMessage.Error' && _.requestId === requestId
125
+ ? Deferred.fail(pushAck, new InvalidPushError({ reason: { _tag: 'Unexpected', message: _.message } }))
126
+ : Effect.void,
127
+ ),
128
+ Stream.filter((_) => _._tag === 'WSMessage.PushAck' && _.requestId === requestId),
129
+ Stream.take(1),
130
+ Stream.tap(() => Deferred.succeed(pushAck, void 0)),
131
+ Stream.runDrain,
132
+ Effect.tapCauseLogPretty,
133
+ Effect.fork,
134
+ )
135
+
136
+ yield* send(WSMessage.PushReq.make({ batch, requestId }))
137
+
138
+ yield* pushAck
139
+ }),
140
+ metadata: {
141
+ name: '@livestore/cf-sync',
142
+ description: 'LiveStore sync backend implementation using Cloudflare Workers & Durable Objects',
143
+ protocol: 'ws',
144
+ url: options.url,
145
+ },
146
+ } satisfies SyncBackend<SyncMetadata>
147
+
148
+ return api
149
+ })
109
150
 
110
151
  const connect = (wsUrl: string) =>
111
152
  Effect.gen(function* () {
@@ -123,21 +164,23 @@ const connect = (wsUrl: string) =>
123
164
  // Wait first until we're online
124
165
  yield* waitUntilOnline
125
166
 
126
- yield* Effect.spanEvent(
127
- `Sending message: ${message._tag}`,
128
- message._tag === 'WSMessage.PushReq'
129
- ? {
130
- id: message.batch[0]!.id.global,
131
- parentId: message.batch[0]!.parentId.global,
132
- batchLength: message.batch.length,
133
- }
134
- : message._tag === 'WSMessage.PullReq'
135
- ? { cursor: message.cursor ?? '-' }
136
- : {},
137
- )
138
-
139
167
  // TODO use MsgPack instead of JSON to speed up the serialization / reduce the size of the messages
140
168
  socketRef.current!.send(Schema.encodeSync(Schema.parseJson(WSMessage.Message))(message))
169
+
170
+ if (LS_DEV) {
171
+ yield* Effect.spanEvent(
172
+ `Sent message: ${message._tag}`,
173
+ message._tag === 'WSMessage.PushReq'
174
+ ? {
175
+ id: message.batch[0]!.id,
176
+ parentId: message.batch[0]!.parentId,
177
+ batchLength: message.batch.length,
178
+ }
179
+ : message._tag === 'WSMessage.PullReq'
180
+ ? { cursor: message.cursor ?? '-' }
181
+ : {},
182
+ )
183
+ }
141
184
  })
142
185
 
143
186
  const innerConnect = Effect.gen(function* () {
@@ -146,11 +189,13 @@ const connect = (wsUrl: string) =>
146
189
  while (typeof navigator !== 'undefined' && navigator.onLine === false) {
147
190
  yield* Effect.sleep(1000)
148
191
  }
192
+ // TODO bring this back in a cross-platform way
149
193
  // if (navigator.onLine === false) {
150
194
  // yield* Effect.async((cb) => self.addEventListener('online', () => cb(Effect.void)))
151
195
  // }
152
196
 
153
197
  const socket = yield* WebSocket.makeWebSocket({ url: wsUrl, reconnect: Schedule.exponential(100) })
198
+ // socket.binaryType = 'arraybuffer'
154
199
 
155
200
  yield* SubscriptionRef.set(isConnected, true)
156
201
  socketRef.current = socket
@@ -191,7 +236,8 @@ const connect = (wsUrl: string) =>
191
236
  // NOTE it seems that this callback doesn't work reliably on a worker but only via `window.addEventListener`
192
237
  // We might need to proxy the event from the main thread to the worker if we want this to work reliably.
193
238
  // eslint-disable-next-line unicorn/prefer-global-this
194
- if (typeof self !== 'undefined') {
239
+ if (typeof self !== 'undefined' && typeof self.addEventListener === 'function') {
240
+ // TODO support an Expo equivalent for this
195
241
  // eslint-disable-next-line unicorn/prefer-global-this
196
242
  yield* Effect.eventListener(self, 'offline', () => Deferred.succeed(connectionClosed, void 0))
197
243
  }
@@ -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,2 +0,0 @@
1
- export * as WSMessage from './ws-message-types.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -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,2 +0,0 @@
1
- export * as WSMessage from './ws-message-types.js';
2
- //# sourceMappingURL=index.js.map
@@ -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 { EncodedAny } 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
- }
@@ -1 +0,0 @@
1
- export * as WSMessage from './ws-message-types.js'
File without changes