@livestore/sync-cf 0.4.0-dev.2 → 0.4.0-dev.20

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 (136) hide show
  1. package/README.md +60 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/cf-worker/do/durable-object.d.ts +45 -0
  4. package/dist/cf-worker/do/durable-object.d.ts.map +1 -0
  5. package/dist/cf-worker/do/durable-object.js +151 -0
  6. package/dist/cf-worker/do/durable-object.js.map +1 -0
  7. package/dist/cf-worker/do/layer.d.ts +34 -0
  8. package/dist/cf-worker/do/layer.d.ts.map +1 -0
  9. package/dist/cf-worker/do/layer.js +91 -0
  10. package/dist/cf-worker/do/layer.js.map +1 -0
  11. package/dist/cf-worker/do/pull.d.ts +6 -0
  12. package/dist/cf-worker/do/pull.d.ts.map +1 -0
  13. package/dist/cf-worker/do/pull.js +47 -0
  14. package/dist/cf-worker/do/pull.js.map +1 -0
  15. package/dist/cf-worker/do/push.d.ts +14 -0
  16. package/dist/cf-worker/do/push.d.ts.map +1 -0
  17. package/dist/cf-worker/do/push.js +131 -0
  18. package/dist/cf-worker/do/push.js.map +1 -0
  19. package/dist/cf-worker/{durable-object.d.ts → do/sqlite.d.ts} +83 -67
  20. package/dist/cf-worker/do/sqlite.d.ts.map +1 -0
  21. package/dist/cf-worker/do/sqlite.js +36 -0
  22. package/dist/cf-worker/do/sqlite.js.map +1 -0
  23. package/dist/cf-worker/do/sync-storage.d.ts +25 -0
  24. package/dist/cf-worker/do/sync-storage.d.ts.map +1 -0
  25. package/dist/cf-worker/do/sync-storage.js +191 -0
  26. package/dist/cf-worker/do/sync-storage.js.map +1 -0
  27. package/dist/cf-worker/do/transport/do-rpc-server.d.ts +9 -0
  28. package/dist/cf-worker/do/transport/do-rpc-server.d.ts.map +1 -0
  29. package/dist/cf-worker/do/transport/do-rpc-server.js +45 -0
  30. package/dist/cf-worker/do/transport/do-rpc-server.js.map +1 -0
  31. package/dist/cf-worker/do/transport/http-rpc-server.d.ts +8 -0
  32. package/dist/cf-worker/do/transport/http-rpc-server.d.ts.map +1 -0
  33. package/dist/cf-worker/do/transport/http-rpc-server.js +30 -0
  34. package/dist/cf-worker/do/transport/http-rpc-server.js.map +1 -0
  35. package/dist/cf-worker/do/transport/ws-rpc-server.d.ts +4 -0
  36. package/dist/cf-worker/do/transport/ws-rpc-server.d.ts.map +1 -0
  37. package/dist/cf-worker/do/transport/ws-rpc-server.js +21 -0
  38. package/dist/cf-worker/do/transport/ws-rpc-server.js.map +1 -0
  39. package/dist/cf-worker/mod.d.ts +4 -2
  40. package/dist/cf-worker/mod.d.ts.map +1 -1
  41. package/dist/cf-worker/mod.js +3 -2
  42. package/dist/cf-worker/mod.js.map +1 -1
  43. package/dist/cf-worker/shared.d.ts +175 -0
  44. package/dist/cf-worker/shared.d.ts.map +1 -0
  45. package/dist/cf-worker/shared.js +43 -0
  46. package/dist/cf-worker/shared.js.map +1 -0
  47. package/dist/cf-worker/worker.d.ts +59 -51
  48. package/dist/cf-worker/worker.d.ts.map +1 -1
  49. package/dist/cf-worker/worker.js +75 -43
  50. package/dist/cf-worker/worker.js.map +1 -1
  51. package/dist/client/mod.d.ts +4 -0
  52. package/dist/client/mod.d.ts.map +1 -0
  53. package/dist/client/mod.js +4 -0
  54. package/dist/client/mod.js.map +1 -0
  55. package/dist/client/transport/do-rpc-client.d.ts +40 -0
  56. package/dist/client/transport/do-rpc-client.d.ts.map +1 -0
  57. package/dist/client/transport/do-rpc-client.js +115 -0
  58. package/dist/client/transport/do-rpc-client.js.map +1 -0
  59. package/dist/client/transport/http-rpc-client.d.ts +43 -0
  60. package/dist/client/transport/http-rpc-client.d.ts.map +1 -0
  61. package/dist/client/transport/http-rpc-client.js +103 -0
  62. package/dist/client/transport/http-rpc-client.js.map +1 -0
  63. package/dist/client/transport/ws-rpc-client.d.ts +46 -0
  64. package/dist/client/transport/ws-rpc-client.d.ts.map +1 -0
  65. package/dist/client/transport/ws-rpc-client.js +108 -0
  66. package/dist/client/transport/ws-rpc-client.js.map +1 -0
  67. package/dist/common/constants.d.ts +7 -0
  68. package/dist/common/constants.d.ts.map +1 -0
  69. package/dist/common/constants.js +17 -0
  70. package/dist/common/constants.js.map +1 -0
  71. package/dist/common/do-rpc-schema.d.ts +76 -0
  72. package/dist/common/do-rpc-schema.d.ts.map +1 -0
  73. package/dist/common/do-rpc-schema.js +48 -0
  74. package/dist/common/do-rpc-schema.js.map +1 -0
  75. package/dist/common/http-rpc-schema.d.ts +58 -0
  76. package/dist/common/http-rpc-schema.d.ts.map +1 -0
  77. package/dist/common/http-rpc-schema.js +37 -0
  78. package/dist/common/http-rpc-schema.js.map +1 -0
  79. package/dist/common/mod.d.ts +8 -1
  80. package/dist/common/mod.d.ts.map +1 -1
  81. package/dist/common/mod.js +7 -1
  82. package/dist/common/mod.js.map +1 -1
  83. package/dist/common/{ws-message-types.d.ts → sync-message-types.d.ts} +119 -153
  84. package/dist/common/sync-message-types.d.ts.map +1 -0
  85. package/dist/common/sync-message-types.js +60 -0
  86. package/dist/common/sync-message-types.js.map +1 -0
  87. package/dist/common/ws-rpc-schema.d.ts +55 -0
  88. package/dist/common/ws-rpc-schema.d.ts.map +1 -0
  89. package/dist/common/ws-rpc-schema.js +32 -0
  90. package/dist/common/ws-rpc-schema.js.map +1 -0
  91. package/package.json +7 -8
  92. package/src/cf-worker/do/durable-object.ts +238 -0
  93. package/src/cf-worker/do/layer.ts +128 -0
  94. package/src/cf-worker/do/pull.ts +75 -0
  95. package/src/cf-worker/do/push.ts +205 -0
  96. package/src/cf-worker/do/sqlite.ts +37 -0
  97. package/src/cf-worker/do/sync-storage.ts +323 -0
  98. package/src/cf-worker/do/transport/do-rpc-server.ts +84 -0
  99. package/src/cf-worker/do/transport/http-rpc-server.ts +51 -0
  100. package/src/cf-worker/do/transport/ws-rpc-server.ts +34 -0
  101. package/src/cf-worker/mod.ts +4 -2
  102. package/src/cf-worker/shared.ts +141 -0
  103. package/src/cf-worker/worker.ts +138 -116
  104. package/src/client/mod.ts +3 -0
  105. package/src/client/transport/do-rpc-client.ts +189 -0
  106. package/src/client/transport/http-rpc-client.ts +225 -0
  107. package/src/client/transport/ws-rpc-client.ts +202 -0
  108. package/src/common/constants.ts +18 -0
  109. package/src/common/do-rpc-schema.ts +54 -0
  110. package/src/common/http-rpc-schema.ts +40 -0
  111. package/src/common/mod.ts +10 -1
  112. package/src/common/sync-message-types.ts +117 -0
  113. package/src/common/ws-rpc-schema.ts +36 -0
  114. package/dist/cf-worker/cf-types.d.ts +0 -2
  115. package/dist/cf-worker/cf-types.d.ts.map +0 -1
  116. package/dist/cf-worker/cf-types.js +0 -2
  117. package/dist/cf-worker/cf-types.js.map +0 -1
  118. package/dist/cf-worker/durable-object.d.ts.map +0 -1
  119. package/dist/cf-worker/durable-object.js +0 -317
  120. package/dist/cf-worker/durable-object.js.map +0 -1
  121. package/dist/common/ws-message-types.d.ts.map +0 -1
  122. package/dist/common/ws-message-types.js +0 -57
  123. package/dist/common/ws-message-types.js.map +0 -1
  124. package/dist/sync-impl/mod.d.ts +0 -2
  125. package/dist/sync-impl/mod.d.ts.map +0 -1
  126. package/dist/sync-impl/mod.js +0 -2
  127. package/dist/sync-impl/mod.js.map +0 -1
  128. package/dist/sync-impl/ws-impl.d.ts +0 -7
  129. package/dist/sync-impl/ws-impl.d.ts.map +0 -1
  130. package/dist/sync-impl/ws-impl.js +0 -175
  131. package/dist/sync-impl/ws-impl.js.map +0 -1
  132. package/src/cf-worker/cf-types.ts +0 -12
  133. package/src/cf-worker/durable-object.ts +0 -478
  134. package/src/common/ws-message-types.ts +0 -114
  135. package/src/sync-impl/mod.ts +0 -1
  136. package/src/sync-impl/ws-impl.ts +0 -274
@@ -0,0 +1,45 @@
1
+ import { type CfTypes } from '@livestore/common-cf';
2
+ import { type Env, type MakeDurableObjectClassOptions, type SyncBackendRpcInterface } from '../shared.ts';
3
+ export type DoState = CfTypes.DurableObjectState;
4
+ export type DoObject<T> = CfTypes.DurableObject & T;
5
+ export type MakeDurableObjectClass = (options?: MakeDurableObjectClassOptions) => {
6
+ new (ctx: DoState, env: Env): DoObject<SyncBackendRpcInterface>;
7
+ };
8
+ /**
9
+ * Creates a Durable Object class for handling WebSocket-based sync.
10
+ * A sync durable object is uniquely scoped to a specific `storeId`.
11
+ *
12
+ * The sync DO supports 3 transport modes:
13
+ * - HTTP JSON-RPC
14
+ * - WebSocket
15
+ * - Durable Object RPC calls (only works in combination with `@livestore/adapter-cf`)
16
+ *
17
+ * Example:
18
+ *
19
+ * ```ts
20
+ * // In your Cloudflare Worker file
21
+ * import { makeDurableObject } from '@livestore/sync-cf/cf-worker'
22
+ *
23
+ * export class SyncBackendDO extends makeDurableObject({
24
+ * onPush: async (message) => {
25
+ * console.log('onPush', message.batch)
26
+ * },
27
+ * onPull: async (message) => {
28
+ * console.log('onPull', message)
29
+ * },
30
+ * }) {}
31
+ * ```
32
+ *
33
+ * `wrangler.toml`
34
+ * ```toml
35
+ * [[durable_objects.bindings]]
36
+ * name = "SYNC_BACKEND_DO"
37
+ * class_name = "SyncBackendDO"
38
+
39
+ * [[migrations]]
40
+ * tag = "v1"
41
+ * new_sqlite_classes = ["SyncBackendDO"]
42
+ * ```
43
+ */
44
+ export declare const makeDurableObject: MakeDurableObjectClass;
45
+ //# sourceMappingURL=durable-object.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-object.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/durable-object.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAkC,MAAM,sBAAsB,CAAA;AAanF,OAAO,EACL,KAAK,GAAG,EACR,KAAK,6BAA6B,EAElC,KAAK,uBAAuB,EAE7B,MAAM,cAAc,CAAA;AAmBrB,MAAM,MAAM,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAA;AAChD,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,OAAO,CAAC,aAAa,GAAG,CAAC,CAAA;AAEnD,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,CAAC,EAAE,6BAA6B,KAAK;IAChF,KAAK,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAAA;CAChE,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,iBAAiB,EAAE,sBAyJ/B,CAAA"}
@@ -0,0 +1,151 @@
1
+ /// <reference types="@cloudflare/workers-types" />
2
+ import { DurableObject } from 'cloudflare:workers';
3
+ import { setupDurableObjectWebSocketRpc } from '@livestore/common-cf';
4
+ import { CfDeclare } from '@livestore/common-cf/declare';
5
+ import { Effect, FetchHttpClient, Layer, Logger, LogLevel, Otlp, RpcMessage, Schema, } from '@livestore/utils/effect';
6
+ import { matchSyncRequest, WebSocketAttachmentSchema, } from "../shared.js";
7
+ import { DoCtx } from "./layer.js";
8
+ import { createDoRpcHandler } from "./transport/do-rpc-server.js";
9
+ import { createHttpRpcHandler } from "./transport/http-rpc-server.js";
10
+ import { makeRpcServer } from "./transport/ws-rpc-server.js";
11
+ const DurableObjectBase = (DurableObject);
12
+ /**
13
+ * Creates a Durable Object class for handling WebSocket-based sync.
14
+ * A sync durable object is uniquely scoped to a specific `storeId`.
15
+ *
16
+ * The sync DO supports 3 transport modes:
17
+ * - HTTP JSON-RPC
18
+ * - WebSocket
19
+ * - Durable Object RPC calls (only works in combination with `@livestore/adapter-cf`)
20
+ *
21
+ * Example:
22
+ *
23
+ * ```ts
24
+ * // In your Cloudflare Worker file
25
+ * import { makeDurableObject } from '@livestore/sync-cf/cf-worker'
26
+ *
27
+ * export class SyncBackendDO extends makeDurableObject({
28
+ * onPush: async (message) => {
29
+ * console.log('onPush', message.batch)
30
+ * },
31
+ * onPull: async (message) => {
32
+ * console.log('onPull', message)
33
+ * },
34
+ * }) {}
35
+ * ```
36
+ *
37
+ * `wrangler.toml`
38
+ * ```toml
39
+ * [[durable_objects.bindings]]
40
+ * name = "SYNC_BACKEND_DO"
41
+ * class_name = "SyncBackendDO"
42
+
43
+ * [[migrations]]
44
+ * tag = "v1"
45
+ * new_sqlite_classes = ["SyncBackendDO"]
46
+ * ```
47
+ */
48
+ export const makeDurableObject = (options) => {
49
+ const enabledTransports = options?.enabledTransports ?? new Set(['http', 'ws', 'do-rpc']);
50
+ const Logging = Logger.consoleWithThread('SyncDo');
51
+ const Observability = options?.otel?.baseUrl
52
+ ? Otlp.layer({
53
+ baseUrl: options.otel.baseUrl,
54
+ tracerExportInterval: 50,
55
+ resource: {
56
+ serviceName: options.otel.serviceName ?? 'sync-cf-do',
57
+ },
58
+ }).pipe(Layer.provide(FetchHttpClient.layer))
59
+ : Layer.empty;
60
+ return class SyncBackendDOBase extends DurableObjectBase {
61
+ __DURABLE_OBJECT_BRAND = 'SyncBackendDOBase';
62
+ constructor(ctx, env) {
63
+ super(ctx, env);
64
+ const WebSocketRpcServerLive = makeRpcServer({ doSelf: this, doOptions: options });
65
+ // This registers the `webSocketMessage` and `webSocketClose` handlers
66
+ if (enabledTransports.has('ws')) {
67
+ setupDurableObjectWebSocketRpc({
68
+ doSelf: this,
69
+ rpcLayer: WebSocketRpcServerLive,
70
+ webSocketMode: 'hibernate',
71
+ // See `pull.ts` for more details how `pull` Effect RPC requests streams are handled
72
+ // in combination with DO hibernation
73
+ onMessage: (request, ws) => {
74
+ if (request._tag === 'Request' && request.tag === 'SyncWsRpc.Pull') {
75
+ // Is Pull request: add requestId to pullRequestIds
76
+ const attachment = ws.deserializeAttachment();
77
+ const { pullRequestIds, ...rest } = Schema.decodeSync(WebSocketAttachmentSchema)(attachment);
78
+ ws.serializeAttachment(Schema.encodeSync(WebSocketAttachmentSchema)({
79
+ ...rest,
80
+ pullRequestIds: [...pullRequestIds, request.id],
81
+ }));
82
+ }
83
+ else if (request._tag === 'Interrupt') {
84
+ // Is Interrupt request: remove requestId from pullRequestIds
85
+ const attachment = ws.deserializeAttachment();
86
+ const { pullRequestIds, ...rest } = Schema.decodeSync(WebSocketAttachmentSchema)(attachment);
87
+ ws.serializeAttachment(Schema.encodeSync(WebSocketAttachmentSchema)({
88
+ ...rest,
89
+ pullRequestIds: pullRequestIds.filter((id) => id !== request.requestId),
90
+ }));
91
+ // TODO also emit `Exit` stream RPC message
92
+ }
93
+ },
94
+ mainLayer: Observability,
95
+ });
96
+ }
97
+ }
98
+ fetch = async (request) => Effect.gen(this, function* () {
99
+ const searchParams = matchSyncRequest(request);
100
+ if (searchParams === undefined) {
101
+ throw new Error('No search params found in request URL');
102
+ }
103
+ const { storeId, payload, transport } = searchParams;
104
+ if (enabledTransports.has(transport) === false) {
105
+ throw new Error(`Transport ${transport} is not enabled (based on \`options.enabledTransports\`)`);
106
+ }
107
+ if (transport === 'http') {
108
+ return yield* this.handleHttp(request);
109
+ }
110
+ if (transport === 'ws') {
111
+ const { 0: client, 1: server } = new WebSocketPair();
112
+ // Since we're using websocket hibernation, we need to remember the storeId for subsequent `webSocketMessage` calls
113
+ server.serializeAttachment(Schema.encodeSync(WebSocketAttachmentSchema)({ storeId, payload, pullRequestIds: [] }));
114
+ // See https://developers.cloudflare.com/durable-objects/examples/websocket-hibernation-server
115
+ this.ctx.acceptWebSocket(server);
116
+ // Ping requests are sent by Effect RPC internally
117
+ this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair(JSON.stringify(RpcMessage.constPing), JSON.stringify(RpcMessage.constPong)));
118
+ return new Response(null, {
119
+ status: 101,
120
+ webSocket: client,
121
+ });
122
+ }
123
+ console.error('Invalid path', request.url);
124
+ return new Response('Invalid path', {
125
+ status: 400,
126
+ statusText: 'Bad Request',
127
+ });
128
+ }).pipe(Effect.tapCauseLogPretty, // Also log errors to console before catching them
129
+ Effect.catchAllCause((cause) => Effect.succeed(new Response('Error', { status: 500, statusText: cause.toString() }))), Effect.withSpan('@livestore/sync-cf:durable-object:fetch'), Effect.provide(DoCtx.Default({ doSelf: this, doOptions: options, from: request })), this.runEffectAsPromise);
130
+ /**
131
+ * Handles DO <-> DO RPC calls
132
+ */
133
+ async rpc(payload) {
134
+ if (enabledTransports.has('do-rpc') === false) {
135
+ throw new Error('Do RPC transport is not enabled (based on `options.enabledTransports`)');
136
+ }
137
+ return createDoRpcHandler({ payload, input: { doSelf: this, doOptions: options } }).pipe(Effect.withSpan('@livestore/sync-cf:durable-object:rpc'), this.runEffectAsPromise);
138
+ }
139
+ /**
140
+ * Handles HTTP RPC calls
141
+ *
142
+ * Requires the `enable_request_signal` compatibility flag to properly support `pull` streaming responses
143
+ */
144
+ handleHttp = (request) => createHttpRpcHandler({
145
+ request,
146
+ responseHeaders: options?.http?.responseHeaders,
147
+ }).pipe(Effect.withSpan('@livestore/sync-cf:durable-object:handleHttp'));
148
+ runEffectAsPromise = (effect) => effect.pipe(Effect.tapCauseLogPretty, Logger.withMinimumLogLevel(LogLevel.Debug), Effect.provide(Layer.mergeAll(Observability, Logging)), Effect.scoped, Effect.runPromise);
149
+ };
150
+ };
151
+ //# sourceMappingURL=durable-object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"durable-object.js","sourceRoot":"","sources":["../../../src/cf-worker/do/durable-object.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { UnknownError } from '@livestore/common';
2
+ import { EventSequenceNumber } from '@livestore/common/schema';
3
+ import type { CfTypes } from '@livestore/common-cf';
4
+ import { Effect } from '@livestore/utils/effect';
5
+ import type { Env, MakeDurableObjectClassOptions, RpcSubscription } from '../shared.ts';
6
+ export interface DoCtxInput {
7
+ doSelf: CfTypes.DurableObject & {
8
+ ctx: CfTypes.DurableObjectState;
9
+ env: Env;
10
+ };
11
+ doOptions: MakeDurableObjectClassOptions | undefined;
12
+ from: CfTypes.Request | {
13
+ storeId: string;
14
+ };
15
+ }
16
+ declare const DoCtx_base: Effect.Service.Class<DoCtx, "DoCtx", {
17
+ readonly effect: (args_0: DoCtxInput) => Effect.Effect.AsEffect<Effect.Effect<{
18
+ storeId: string;
19
+ backendId: string;
20
+ currentHeadRef: {
21
+ current: number & import("effect/Brand").Brand<"GlobalEventSequenceNumber">;
22
+ };
23
+ updateCurrentHead: (currentHead: EventSequenceNumber.Global.Type) => void;
24
+ storage: import("./sync-storage.ts").SyncStorage;
25
+ doOptions: MakeDurableObjectClassOptions | undefined;
26
+ env: Env;
27
+ ctx: CfTypes.DurableObjectState<unknown>;
28
+ rpcSubscriptions: Map<string, RpcSubscription>;
29
+ }, UnknownError, never>>;
30
+ }>;
31
+ export declare class DoCtx extends DoCtx_base {
32
+ }
33
+ export {};
34
+ //# sourceMappingURL=layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/layer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAS,MAAM,0BAA0B,CAAA;AACrE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEnD,OAAO,EAAE,MAAM,EAAa,MAAM,yBAAyB,CAAA;AAE3D,OAAO,KAAK,EAAE,GAAG,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAMvF,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC,aAAa,GAAG;QAC9B,GAAG,EAAE,OAAO,CAAC,kBAAkB,CAAA;QAC/B,GAAG,EAAE,GAAG,CAAA;KACT,CAAA;IACD,SAAS,EAAE,6BAA6B,GAAG,SAAS,CAAA;IACpD,IAAI,EAAE,OAAO,CAAC,OAAO,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5C;;;;;;;;yCAmE6C,mBAAmB,CAAC,MAAM,CAAC,IAAI;;;;;;;;AAjE7E,qBAAa,KAAM,SAAQ,UA0GzB;CAAG"}
@@ -0,0 +1,91 @@
1
+ import { UnknownError } from '@livestore/common';
2
+ import { EventSequenceNumber, State } from '@livestore/common/schema';
3
+ import { shouldNeverHappen } from '@livestore/utils';
4
+ import { Effect, Predicate } from '@livestore/utils/effect';
5
+ import { nanoid } from '@livestore/utils/nanoid';
6
+ import { contextTable, eventlogTable } from "./sqlite.js";
7
+ import { makeStorage } from "./sync-storage.js";
8
+ const CacheSymbol = Symbol('Cache');
9
+ export class DoCtx extends Effect.Service()('DoCtx', {
10
+ effect: Effect.fn(function* ({ doSelf, doOptions, from }) {
11
+ if (doSelf[CacheSymbol] !== undefined) {
12
+ return doSelf[CacheSymbol];
13
+ }
14
+ const getStoreId = (from) => {
15
+ if (Predicate.hasProperty(from, 'url')) {
16
+ const url = new URL(from.url);
17
+ return (url.searchParams.get('storeId') ?? shouldNeverHappen(`No storeId provided in request URL search params`));
18
+ }
19
+ return from.storeId;
20
+ };
21
+ const storeId = getStoreId(from);
22
+ // Resolve storage engine
23
+ const makeEngine = Effect.gen(function* () {
24
+ const opt = doOptions?.storage;
25
+ if (opt?._tag === 'd1') {
26
+ const db = doSelf.env[opt.binding];
27
+ if (!db) {
28
+ return yield* UnknownError.make({ cause: new Error(`D1 binding '${opt.binding}' not found on env`) });
29
+ }
30
+ return { _tag: 'd1', db };
31
+ }
32
+ else if (opt?._tag === 'do-sqlite' || opt === undefined) {
33
+ return { _tag: 'do-sqlite' };
34
+ }
35
+ else
36
+ return shouldNeverHappen(`Invalid storage engine`, opt);
37
+ });
38
+ const engine = yield* makeEngine;
39
+ const storage = makeStorage(doSelf.ctx, storeId, engine);
40
+ // Initialize database tables
41
+ {
42
+ const colSpec = State.SQLite.makeColumnSpec(eventlogTable.sqliteDef.ast);
43
+ if (engine._tag === 'd1') {
44
+ // D1 database is async, so we need to use a promise
45
+ yield* Effect.promise(() => engine.db.exec(`CREATE TABLE IF NOT EXISTS "${storage.dbName}" (${colSpec}) strict`));
46
+ }
47
+ else {
48
+ // DO SQLite table lives in Durable Object storage
49
+ doSelf.ctx.storage.sql.exec(`CREATE TABLE IF NOT EXISTS "${storage.dbName}" (${colSpec}) strict`);
50
+ }
51
+ }
52
+ {
53
+ const colSpec = State.SQLite.makeColumnSpec(contextTable.sqliteDef.ast);
54
+ doSelf.ctx.storage.sql.exec(`CREATE TABLE IF NOT EXISTS "${contextTable.sqliteDef.name}" (${colSpec}) strict`);
55
+ }
56
+ const storageRow = doSelf.ctx.storage.sql
57
+ .exec(`SELECT * FROM "${contextTable.sqliteDef.name}" WHERE storeId = ?`, storeId)
58
+ .toArray()[0];
59
+ const currentHeadRef = { current: storageRow?.currentHead ?? EventSequenceNumber.Client.ROOT.global };
60
+ // TODO do concistency check with eventlog table to make sure the head is consistent
61
+ // Should be the same backendId for lifetime of the durable object
62
+ const backendId = storageRow?.backendId ?? nanoid();
63
+ const updateCurrentHead = (currentHead) => {
64
+ doSelf.ctx.storage.sql.exec(`INSERT OR REPLACE INTO "${contextTable.sqliteDef.name}" (storeId, currentHead, backendId) VALUES (?, ?, ?)`, storeId, currentHead, backendId);
65
+ currentHeadRef.current = currentHead;
66
+ // I still don't know why we need to re-assign this ref to the `doSelf` object but somehow this seems to be needed 😵‍💫
67
+ // @ts-expect-error
68
+ doSelf[CacheSymbol].currentHeadRef = { current: currentHead };
69
+ };
70
+ const rpcSubscriptions = new Map();
71
+ const storageCache = {
72
+ storeId,
73
+ backendId,
74
+ currentHeadRef,
75
+ updateCurrentHead,
76
+ storage,
77
+ doOptions,
78
+ env: doSelf.env,
79
+ ctx: doSelf.ctx,
80
+ rpcSubscriptions,
81
+ };
82
+ doSelf[CacheSymbol] = storageCache;
83
+ // Set initial current head to root
84
+ if (storageRow === undefined) {
85
+ updateCurrentHead(EventSequenceNumber.Client.ROOT.global);
86
+ }
87
+ return storageCache;
88
+ }, UnknownError.mapToUnknownError, Effect.withSpan('@livestore/sync-cf:durable-object:makeDoCtx')),
89
+ }) {
90
+ }
91
+ //# sourceMappingURL=layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer.js","sourceRoot":"","sources":["../../../src/cf-worker/do/layer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { InvalidPullError } from '@livestore/common';
2
+ import { Schema, Stream } from '@livestore/utils/effect';
3
+ import { SyncMessage } from '../../common/mod.ts';
4
+ import { DoCtx } from './layer.ts';
5
+ export declare const makeEndingPullStream: (req: SyncMessage.PullRequest, payload: Schema.JsonValue | undefined) => Stream.Stream<SyncMessage.PullResponse, InvalidPullError, DoCtx>;
6
+ //# sourceMappingURL=pull.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/pull.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,gBAAgB,EAA6B,MAAM,mBAAmB,CAAA;AAEvG,OAAO,EAAyB,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAE/E,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAalC,eAAO,MAAM,oBAAoB,GAC/B,KAAK,WAAW,CAAC,WAAW,EAC5B,SAAS,MAAM,CAAC,SAAS,GAAG,SAAS,KACpC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,YAAY,EAAE,gBAAgB,EAAE,KAAK,CAqD/D,CAAA"}
@@ -0,0 +1,47 @@
1
+ import { BackendIdMismatchError, InvalidPullError, SyncBackend, UnknownError } from '@livestore/common';
2
+ import { splitChunkBySize } from '@livestore/common/sync';
3
+ import { Chunk, Effect, Option, Schema, Stream } from '@livestore/utils/effect';
4
+ import { MAX_PULL_EVENTS_PER_MESSAGE, MAX_WS_MESSAGE_BYTES } from "../../common/constants.js";
5
+ import { SyncMessage } from "../../common/mod.js";
6
+ import { DoCtx } from "./layer.js";
7
+ const encodePullResponse = Schema.encodeSync(SyncMessage.PullResponse);
8
+ // Notes on stream handling:
9
+ // We're intentionally closing the stream once we've read all existing events
10
+ //
11
+ // WebSocket:
12
+ // - Further chunks will be emitted manually in `push.ts`
13
+ // - If the client sends a `Interrupt` RPC message, it will be handled in the `durable-object.ts` constructor
14
+ // DO RPC:
15
+ // - Further chunks will be emitted manually in `push.ts`
16
+ // - If the client sends a `Interrupt` RPC message, TODO
17
+ export const makeEndingPullStream = (req, payload) => Effect.gen(function* () {
18
+ const { doOptions, backendId, storeId, storage } = yield* DoCtx;
19
+ if (doOptions?.onPull) {
20
+ yield* Effect.tryAll(() => doOptions.onPull(req, { storeId, payload })).pipe(UnknownError.mapToUnknownError);
21
+ }
22
+ if (req.cursor._tag === 'Some' && req.cursor.value.backendId !== backendId) {
23
+ return yield* new BackendIdMismatchError({ expected: backendId, received: req.cursor.value.backendId });
24
+ }
25
+ const { stream: storedEvents, total } = yield* storage.getEvents(Option.getOrUndefined(req.cursor)?.eventSequenceNumber);
26
+ return storedEvents.pipe(Stream.mapChunksEffect(splitChunkBySize({
27
+ maxItems: MAX_PULL_EVENTS_PER_MESSAGE,
28
+ maxBytes: MAX_WS_MESSAGE_BYTES,
29
+ encode: (batch) => encodePullResponse(SyncMessage.PullResponse.make({ batch, pageInfo: SyncBackend.pageInfoNoMore, backendId })),
30
+ })), Stream.mapAccum(total, (remaining, chunk) => {
31
+ const asArray = Chunk.toReadonlyArray(chunk);
32
+ const nextRemaining = Math.max(0, remaining - asArray.length);
33
+ return [
34
+ nextRemaining,
35
+ SyncMessage.PullResponse.make({
36
+ batch: asArray,
37
+ pageInfo: nextRemaining > 0 ? SyncBackend.pageInfoMoreKnown(nextRemaining) : SyncBackend.pageInfoNoMore,
38
+ backendId,
39
+ }),
40
+ ];
41
+ }), Stream.tap(Effect.fn(function* (res) {
42
+ if (doOptions?.onPullRes) {
43
+ yield* Effect.tryAll(() => doOptions.onPullRes(res)).pipe(UnknownError.mapToUnknownError);
44
+ }
45
+ })), Stream.emitIfEmpty(SyncMessage.emptyPullResponse(backendId)));
46
+ }).pipe(Stream.unwrap, Stream.mapError((cause) => InvalidPullError.make({ cause })), Stream.withSpan('cloudflare-provider:pull'));
47
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/cf-worker/do/pull.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { InvalidPushError } from '@livestore/common';
2
+ import { type CfTypes } from '@livestore/common-cf';
3
+ import { Effect, Schema } from '@livestore/utils/effect';
4
+ import { SyncMessage } from '../../common/mod.ts';
5
+ import { type Env, type MakeDurableObjectClassOptions, type StoreId } from '../shared.ts';
6
+ import { DoCtx } from './layer.ts';
7
+ export declare const makePush: ({ payload, options, storeId, ctx, env, }: {
8
+ payload: Schema.JsonValue | undefined;
9
+ options: MakeDurableObjectClassOptions | undefined;
10
+ storeId: StoreId;
11
+ ctx: CfTypes.DurableObjectState;
12
+ env: Env;
13
+ }) => (pushRequest: Omit<SyncMessage.PushRequest, "_tag">) => Effect.Effect<{}, InvalidPushError, DoCtx>;
14
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/cf-worker/do/push.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,gBAAgB,EAIjB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,KAAK,OAAO,EAAsB,MAAM,sBAAsB,CAAA;AACvE,OAAO,EAAS,MAAM,EAA2B,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAExF,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACjD,OAAO,EAAE,KAAK,GAAG,EAAE,KAAK,6BAA6B,EAAE,KAAK,OAAO,EAA6B,MAAM,cAAc,CAAA;AACpH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAKlC,eAAO,MAAM,QAAQ,GAClB,0CAME;IACD,OAAO,EAAE,MAAM,CAAC,SAAS,GAAG,SAAS,CAAA;IACrC,OAAO,EAAE,6BAA6B,GAAG,SAAS,CAAA;IAClD,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,OAAO,CAAC,kBAAkB,CAAA;IAC/B,GAAG,EAAE,GAAG,CAAA;CACT,MACA,aAAa,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,MAAM,CAAC,+CA6JhD,CAAA"}
@@ -0,0 +1,131 @@
1
+ import { BackendIdMismatchError, InvalidPushError, ServerAheadError, SyncBackend, UnknownError, } from '@livestore/common';
2
+ import { splitChunkBySize } from '@livestore/common/sync';
3
+ import { emitStreamResponse } from '@livestore/common-cf';
4
+ import { Chunk, Effect, Option, Schema } from '@livestore/utils/effect';
5
+ import { MAX_PUSH_EVENTS_PER_REQUEST, MAX_WS_MESSAGE_BYTES } from "../../common/constants.js";
6
+ import { SyncMessage } from "../../common/mod.js";
7
+ import { WebSocketAttachmentSchema } from "../shared.js";
8
+ import { DoCtx } from "./layer.js";
9
+ const encodePullResponse = Schema.encodeSync(SyncMessage.PullResponse);
10
+ export const makePush = ({ payload, options, storeId, ctx, env, }) => (pushRequest) => Effect.gen(function* () {
11
+ // yield* Effect.log(`Pushing ${decodedMessage.batch.length} events`, decodedMessage.batch)
12
+ const { backendId, storage, currentHeadRef, updateCurrentHead, rpcSubscriptions } = yield* DoCtx;
13
+ if (pushRequest.batch.length === 0) {
14
+ return SyncMessage.PushAck.make({});
15
+ }
16
+ if (options?.onPush) {
17
+ yield* Effect.tryAll(() => options.onPush(pushRequest, { storeId, payload })).pipe(UnknownError.mapToUnknownError);
18
+ }
19
+ if (pushRequest.backendId._tag === 'Some' && pushRequest.backendId.value !== backendId) {
20
+ return yield* new BackendIdMismatchError({ expected: backendId, received: pushRequest.backendId.value });
21
+ }
22
+ // This part of the code needs to run sequentially to avoid race conditions
23
+ const { createdAt } = yield* Effect.gen(function* () {
24
+ const currentHead = currentHeadRef.current;
25
+ // TODO handle clientId unique conflict
26
+ // Validate the batch
27
+ const firstEventParent = pushRequest.batch[0].parentSeqNum;
28
+ if (firstEventParent !== currentHead) {
29
+ // yield* Effect.logDebug('ServerAheadError: backend head mismatch', {
30
+ // expectedHead: currentHead,
31
+ // providedHead: firstEventParent,
32
+ // batchSize: pushRequest.batch.length,
33
+ // backendId,
34
+ // })
35
+ return yield* new ServerAheadError({ minimumExpectedNum: currentHead, providedNum: firstEventParent });
36
+ }
37
+ const createdAt = new Date().toISOString();
38
+ // TODO possibly model this as a queue in order to speed up subsequent pushes
39
+ yield* storage.appendEvents(pushRequest.batch, createdAt);
40
+ updateCurrentHead(pushRequest.batch.at(-1).seqNum);
41
+ return { createdAt };
42
+ }).pipe(blockConcurrencyWhile(ctx));
43
+ // Run in background but already return the push ack to the client
44
+ yield* Effect.gen(function* () {
45
+ const connectedClients = ctx.getWebSockets();
46
+ // Preparing chunks of responses to make sure we don't exceed the WS message size limit.
47
+ const responses = yield* Chunk.fromIterable(pushRequest.batch).pipe(splitChunkBySize({
48
+ maxItems: MAX_PUSH_EVENTS_PER_REQUEST,
49
+ maxBytes: MAX_WS_MESSAGE_BYTES,
50
+ encode: (items) => encodePullResponse(SyncMessage.PullResponse.make({
51
+ batch: items.map((eventEncoded) => ({
52
+ eventEncoded,
53
+ metadata: Option.some(SyncMessage.SyncMetadata.make({ createdAt })),
54
+ })),
55
+ pageInfo: SyncBackend.pageInfoNoMore,
56
+ backendId,
57
+ })),
58
+ }), Effect.map(Chunk.map((eventsChunk) => {
59
+ const batchWithMetadata = Chunk.toReadonlyArray(eventsChunk).map((eventEncoded) => ({
60
+ eventEncoded,
61
+ metadata: Option.some(SyncMessage.SyncMetadata.make({ createdAt })),
62
+ }));
63
+ const response = SyncMessage.PullResponse.make({
64
+ batch: batchWithMetadata,
65
+ pageInfo: SyncBackend.pageInfoNoMore,
66
+ backendId,
67
+ });
68
+ return {
69
+ response,
70
+ encoded: Schema.encodeSync(SyncMessage.PullResponse)(response),
71
+ };
72
+ })));
73
+ // Dual broadcasting: WebSocket + RPC clients
74
+ // Broadcast to WebSocket clients
75
+ if (connectedClients.length > 0) {
76
+ for (const { response, encoded } of responses) {
77
+ // Only calling once for now.
78
+ if (options?.onPullRes) {
79
+ yield* Effect.tryAll(() => options.onPullRes(response)).pipe(UnknownError.mapToUnknownError);
80
+ }
81
+ // NOTE we're also sending the pullRes chunk to the pushing ws client as confirmation
82
+ for (const conn of connectedClients) {
83
+ const attachment = Schema.decodeSync(WebSocketAttachmentSchema)(conn.deserializeAttachment());
84
+ // We're doing something a bit "advanced" here as we're directly emitting Effect RPC-compatible
85
+ // response messsages on the Effect RPC-managed websocket connection to the WS client.
86
+ // For this we need to get the RPC `requestId` from the WebSocket attachment.
87
+ for (const requestId of attachment.pullRequestIds) {
88
+ const res = {
89
+ _tag: 'Chunk',
90
+ requestId,
91
+ values: [encoded],
92
+ };
93
+ conn.send(JSON.stringify(res));
94
+ }
95
+ }
96
+ }
97
+ yield* Effect.logDebug(`Broadcasted to ${connectedClients.length} WebSocket clients`);
98
+ }
99
+ // RPC broadcasting would require reconstructing client stubs from clientIds
100
+ if (rpcSubscriptions.size > 0) {
101
+ for (const subscription of rpcSubscriptions.values()) {
102
+ for (const { encoded } of responses) {
103
+ yield* emitStreamResponse({
104
+ callerContext: subscription.callerContext,
105
+ env,
106
+ requestId: subscription.requestId,
107
+ values: [encoded],
108
+ }).pipe(Effect.tapCauseLogPretty, Effect.exit);
109
+ }
110
+ }
111
+ yield* Effect.logDebug(`Broadcasted to ${rpcSubscriptions.size} RPC clients`);
112
+ }
113
+ }).pipe(Effect.tapCauseLogPretty, Effect.withSpan('push-rpc-broadcast'), Effect.uninterruptible, // We need to make sure Effect RPC doesn't interrupt this fiber
114
+ Effect.fork);
115
+ // We need to yield here to make sure the fork above is kicked off before we let Effect RPC finish the request
116
+ yield* Effect.yieldNow();
117
+ return SyncMessage.PushAck.make({});
118
+ }).pipe(Effect.tap(Effect.fn(function* (message) {
119
+ if (options?.onPushRes) {
120
+ yield* Effect.tryAll(() => options.onPushRes(message)).pipe(UnknownError.mapToUnknownError);
121
+ }
122
+ })), Effect.mapError((cause) => InvalidPushError.make({ cause })), Effect.withSpan('sync-cf:do:push', { attributes: { storeId, batchSize: pushRequest.batch.length } }));
123
+ /**
124
+ * @see https://developers.cloudflare.com/durable-objects/api/state/#blockconcurrencywhile
125
+ */
126
+ const blockConcurrencyWhile = (ctx) => (eff) => Effect.gen(function* () {
127
+ const runtime = yield* Effect.runtime();
128
+ const exit = yield* Effect.promise(() => ctx.blockConcurrencyWhile(() => eff.pipe(Effect.provide(runtime), Effect.runPromiseExit)));
129
+ return yield* exit;
130
+ });
131
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/cf-worker/do/push.ts"],"names":[],"mappings":"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"}