@livestore/sync-cf 0.4.0-dev.0 → 0.4.0-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/cf-worker/do/durable-object.d.ts +45 -0
- package/dist/cf-worker/do/durable-object.d.ts.map +1 -0
- package/dist/cf-worker/do/durable-object.js +150 -0
- package/dist/cf-worker/do/durable-object.js.map +1 -0
- package/dist/cf-worker/do/layer.d.ts +34 -0
- package/dist/cf-worker/do/layer.d.ts.map +1 -0
- package/dist/cf-worker/do/layer.js +91 -0
- package/dist/cf-worker/do/layer.js.map +1 -0
- package/dist/cf-worker/do/pull.d.ts +6 -0
- package/dist/cf-worker/do/pull.d.ts.map +1 -0
- package/dist/cf-worker/do/pull.js +47 -0
- package/dist/cf-worker/do/pull.js.map +1 -0
- package/dist/cf-worker/do/push.d.ts +14 -0
- package/dist/cf-worker/do/push.d.ts.map +1 -0
- package/dist/cf-worker/do/push.js +131 -0
- package/dist/cf-worker/do/push.js.map +1 -0
- package/dist/cf-worker/{durable-object.d.ts → do/sqlite.d.ts} +77 -70
- package/dist/cf-worker/do/sqlite.d.ts.map +1 -0
- package/dist/cf-worker/do/sqlite.js +27 -0
- package/dist/cf-worker/do/sqlite.js.map +1 -0
- package/dist/cf-worker/do/sync-storage.d.ts +25 -0
- package/dist/cf-worker/do/sync-storage.d.ts.map +1 -0
- package/dist/cf-worker/do/sync-storage.js +190 -0
- package/dist/cf-worker/do/sync-storage.js.map +1 -0
- package/dist/cf-worker/do/transport/do-rpc-server.d.ts +9 -0
- package/dist/cf-worker/do/transport/do-rpc-server.d.ts.map +1 -0
- package/dist/cf-worker/do/transport/do-rpc-server.js +45 -0
- package/dist/cf-worker/do/transport/do-rpc-server.js.map +1 -0
- package/dist/cf-worker/do/transport/http-rpc-server.d.ts +7 -0
- package/dist/cf-worker/do/transport/http-rpc-server.d.ts.map +1 -0
- package/dist/cf-worker/do/transport/http-rpc-server.js +24 -0
- package/dist/cf-worker/do/transport/http-rpc-server.js.map +1 -0
- package/dist/cf-worker/do/transport/ws-rpc-server.d.ts +4 -0
- package/dist/cf-worker/do/transport/ws-rpc-server.d.ts.map +1 -0
- package/dist/cf-worker/do/transport/ws-rpc-server.js +21 -0
- package/dist/cf-worker/do/transport/ws-rpc-server.js.map +1 -0
- package/dist/cf-worker/mod.d.ts +4 -2
- package/dist/cf-worker/mod.d.ts.map +1 -1
- package/dist/cf-worker/mod.js +3 -2
- package/dist/cf-worker/mod.js.map +1 -1
- package/dist/cf-worker/shared.d.ts +147 -0
- package/dist/cf-worker/shared.d.ts.map +1 -0
- package/dist/cf-worker/shared.js +32 -0
- package/dist/cf-worker/shared.js.map +1 -0
- package/dist/cf-worker/worker.d.ts +45 -45
- package/dist/cf-worker/worker.d.ts.map +1 -1
- package/dist/cf-worker/worker.js +51 -39
- package/dist/cf-worker/worker.js.map +1 -1
- package/dist/client/mod.d.ts +4 -0
- package/dist/client/mod.d.ts.map +1 -0
- package/dist/client/mod.js +4 -0
- package/dist/client/mod.js.map +1 -0
- package/dist/client/transport/do-rpc-client.d.ts +40 -0
- package/dist/client/transport/do-rpc-client.d.ts.map +1 -0
- package/dist/client/transport/do-rpc-client.js +117 -0
- package/dist/client/transport/do-rpc-client.js.map +1 -0
- package/dist/client/transport/http-rpc-client.d.ts +43 -0
- package/dist/client/transport/http-rpc-client.d.ts.map +1 -0
- package/dist/client/transport/http-rpc-client.js +103 -0
- package/dist/client/transport/http-rpc-client.js.map +1 -0
- package/dist/client/transport/ws-rpc-client.d.ts +45 -0
- package/dist/client/transport/ws-rpc-client.d.ts.map +1 -0
- package/dist/client/transport/ws-rpc-client.js +108 -0
- package/dist/client/transport/ws-rpc-client.js.map +1 -0
- package/dist/common/constants.d.ts +7 -0
- package/dist/common/constants.d.ts.map +1 -0
- package/dist/common/constants.js +17 -0
- package/dist/common/constants.js.map +1 -0
- package/dist/common/do-rpc-schema.d.ts +76 -0
- package/dist/common/do-rpc-schema.d.ts.map +1 -0
- package/dist/common/do-rpc-schema.js +48 -0
- package/dist/common/do-rpc-schema.js.map +1 -0
- package/dist/common/http-rpc-schema.d.ts +58 -0
- package/dist/common/http-rpc-schema.d.ts.map +1 -0
- package/dist/common/http-rpc-schema.js +37 -0
- package/dist/common/http-rpc-schema.js.map +1 -0
- package/dist/common/mod.d.ts +8 -1
- package/dist/common/mod.d.ts.map +1 -1
- package/dist/common/mod.js +7 -1
- package/dist/common/mod.js.map +1 -1
- package/dist/common/{ws-message-types.d.ts → sync-message-types.d.ts} +119 -153
- package/dist/common/sync-message-types.d.ts.map +1 -0
- package/dist/common/sync-message-types.js +60 -0
- package/dist/common/sync-message-types.js.map +1 -0
- package/dist/common/ws-rpc-schema.d.ts +55 -0
- package/dist/common/ws-rpc-schema.d.ts.map +1 -0
- package/dist/common/ws-rpc-schema.js +32 -0
- package/dist/common/ws-rpc-schema.js.map +1 -0
- package/package.json +7 -8
- package/src/cf-worker/do/durable-object.ts +237 -0
- package/src/cf-worker/do/layer.ts +128 -0
- package/src/cf-worker/do/pull.ts +77 -0
- package/src/cf-worker/do/push.ts +205 -0
- package/src/cf-worker/do/sqlite.ts +28 -0
- package/src/cf-worker/do/sync-storage.ts +321 -0
- package/src/cf-worker/do/transport/do-rpc-server.ts +84 -0
- package/src/cf-worker/do/transport/http-rpc-server.ts +37 -0
- package/src/cf-worker/do/transport/ws-rpc-server.ts +34 -0
- package/src/cf-worker/mod.ts +4 -2
- package/src/cf-worker/shared.ts +112 -0
- package/src/cf-worker/worker.ts +91 -105
- package/src/client/mod.ts +3 -0
- package/src/client/transport/do-rpc-client.ts +191 -0
- package/src/client/transport/http-rpc-client.ts +225 -0
- package/src/client/transport/ws-rpc-client.ts +202 -0
- package/src/common/constants.ts +18 -0
- package/src/common/do-rpc-schema.ts +54 -0
- package/src/common/http-rpc-schema.ts +40 -0
- package/src/common/mod.ts +10 -1
- package/src/common/sync-message-types.ts +117 -0
- package/src/common/ws-rpc-schema.ts +36 -0
- package/dist/cf-worker/cf-types.d.ts +0 -2
- package/dist/cf-worker/cf-types.d.ts.map +0 -1
- package/dist/cf-worker/cf-types.js +0 -2
- package/dist/cf-worker/cf-types.js.map +0 -1
- package/dist/cf-worker/durable-object.d.ts.map +0 -1
- package/dist/cf-worker/durable-object.js +0 -317
- package/dist/cf-worker/durable-object.js.map +0 -1
- package/dist/common/ws-message-types.d.ts.map +0 -1
- package/dist/common/ws-message-types.js +0 -57
- package/dist/common/ws-message-types.js.map +0 -1
- package/dist/sync-impl/mod.d.ts +0 -2
- package/dist/sync-impl/mod.d.ts.map +0 -1
- package/dist/sync-impl/mod.js +0 -2
- package/dist/sync-impl/mod.js.map +0 -1
- package/dist/sync-impl/ws-impl.d.ts +0 -7
- package/dist/sync-impl/ws-impl.d.ts.map +0 -1
- package/dist/sync-impl/ws-impl.js +0 -175
- package/dist/sync-impl/ws-impl.js.map +0 -1
- package/src/cf-worker/cf-types.ts +0 -12
- package/src/cf-worker/durable-object.ts +0 -478
- package/src/common/ws-message-types.ts +0 -114
- package/src/sync-impl/mod.ts +0 -1
- 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,sBAwJ/B,CAAA"}
|
|
@@ -0,0 +1,150 @@
|
|
|
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
|
+
}).pipe(Effect.withSpan('@livestore/sync-cf:durable-object:handleHttp'));
|
|
147
|
+
runEffectAsPromise = (effect) => effect.pipe(Effect.tapCauseLogPretty, Logger.withMinimumLogLevel(LogLevel.Debug), Effect.provide(Layer.mergeAll(Observability, Logging)), Effect.scoped, Effect.runPromise);
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
//# 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":"AAAA,mDAAmD;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,OAAO,EAAgB,8BAA8B,EAAE,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AACxD,OAAO,EACL,MAAM,EACN,eAAe,EACf,KAAK,EACL,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,UAAU,EACV,MAAM,GAEP,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAGL,gBAAgB,EAEhB,yBAAyB,GAC1B,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAA;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAA;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAS5D,MAAM,iBAAiB,GAAG,CAAA,aAGgD,CAAA,CAAA;AAU1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAA2B,CAAC,OAAO,EAAE,EAAE;IACnE,MAAM,iBAAiB,GAAG,OAAO,EAAE,iBAAiB,IAAI,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAA;IAEzF,MAAM,OAAO,GAAG,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAA;IAElD,MAAM,aAAa,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO;QAC1C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YACT,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,OAAO;YAC7B,oBAAoB,EAAE,EAAE;YACxB,QAAQ,EAAE;gBACR,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW,IAAI,YAAY;aACtD;SACF,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC,CAAC,KAAK,CAAC,KAAK,CAAA;IAEf,OAAO,MAAM,iBAAkB,SAAQ,iBAAiB;QACtD,sBAAsB,GAAG,mBAA4B,CAAA;QAErD,YAAY,GAA+B,EAAE,GAAQ;YACnD,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;YAEf,MAAM,sBAAsB,GAAG,aAAa,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;YAElF,sEAAsE;YACtE,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,8BAA8B,CAAC;oBAC7B,MAAM,EAAE,IAAI;oBACZ,QAAQ,EAAE,sBAAsB;oBAChC,aAAa,EAAE,WAAW;oBAC1B,oFAAoF;oBACpF,qCAAqC;oBACrC,SAAS,EAAE,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE;wBACzB,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,KAAK,gBAAgB,EAAE,CAAC;4BACnE,mDAAmD;4BACnD,MAAM,UAAU,GAAG,EAAE,CAAC,qBAAqB,EAAE,CAAA;4BAC7C,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,UAAU,CAAC,CAAA;4BAC5F,EAAE,CAAC,mBAAmB,CACpB,MAAM,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC;gCAC3C,GAAG,IAAI;gCACP,cAAc,EAAE,CAAC,GAAG,cAAc,EAAE,OAAO,CAAC,EAAE,CAAC;6BAChD,CAAC,CACH,CAAA;wBACH,CAAC;6BAAM,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;4BACxC,6DAA6D;4BAC7D,MAAM,UAAU,GAAG,EAAE,CAAC,qBAAqB,EAAE,CAAA;4BAC7C,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,UAAU,CAAC,CAAA;4BAC5F,EAAE,CAAC,mBAAmB,CACpB,MAAM,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC;gCAC3C,GAAG,IAAI;gCACP,cAAc,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,OAAO,CAAC,SAAS,CAAC;6BACxE,CAAC,CACH,CAAA;4BACD,2CAA2C;wBAC7C,CAAC;oBACH,CAAC;oBACD,SAAS,EAAE,aAAa;iBACzB,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,KAAK,GAAG,KAAK,EAAE,OAAgB,EAAqB,EAAE,CACpD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC;YACxB,MAAM,YAAY,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;YAC9C,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAA;YAC1D,CAAC;YAED,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,YAAY,CAAA;YAEpD,IAAI,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,KAAK,EAAE,CAAC;gBAC/C,MAAM,IAAI,KAAK,CAAC,aAAa,SAAS,0DAA0D,CAAC,CAAA;YACnG,CAAC;YAED,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;gBACzB,OAAO,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;YACxC,CAAC;YAED,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;gBACvB,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,aAAa,EAAE,CAAA;gBAEpD,mHAAmH;gBACnH,MAAM,CAAC,mBAAmB,CACxB,MAAM,CAAC,UAAU,CAAC,yBAAyB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CACvF,CAAA;gBAED,8FAA8F;gBAE9F,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAA;gBAEhC,kDAAkD;gBAClD,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAC/B,IAAI,4BAA4B,CAC9B,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,EACpC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CACrC,CACF,CAAA;gBAED,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE;oBACxB,MAAM,EAAE,GAAG;oBACX,SAAS,EAAE,MAAM;iBAClB,CAAC,CAAA;YACJ,CAAC;YAED,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,CAAA;YAE1C,OAAO,IAAI,QAAQ,CAAC,cAAc,EAAE;gBAClC,MAAM,EAAE,GAAG;gBACX,UAAU,EAAE,aAAa;aAC1B,CAAC,CAAA;QACJ,CAAC,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,iBAAiB,EAAE,kDAAkD;QAC5E,MAAM,CAAC,aAAa,CAAC,CAAC,KAAK,EAAE,EAAE,CAC7B,MAAM,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CACrF,EACD,MAAM,CAAC,QAAQ,CAAC,yCAAyC,CAAC,EAC1D,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,EAClF,IAAI,CAAC,kBAAkB,CACxB,CAAA;QAEH;;WAEG;QACH,KAAK,CAAC,GAAG,CAAC,OAAgC;YACxC,IAAI,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,EAAE,CAAC;gBAC9C,MAAM,IAAI,KAAK,CAAC,wEAAwE,CAAC,CAAA;YAC3F,CAAC;YAED,OAAO,kBAAkB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,CACtF,MAAM,CAAC,QAAQ,CAAC,uCAAuC,CAAC,EACxD,IAAI,CAAC,kBAAkB,CACxB,CAAA;QACH,CAAC;QAED;;;;WAIG;QACK,UAAU,GAAG,CAAC,OAAwB,EAAE,EAAE,CAChD,oBAAoB,CAAC;YACnB,OAAO;SACR,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,8CAA8C,CAAC,CAAC,CAAA;QAElE,kBAAkB,GAAG,CAAe,MAAwC,EAAc,EAAE,CAClG,MAAM,CAAC,IAAI,CACT,MAAM,CAAC,iBAAiB,EACxB,MAAM,CAAC,mBAAmB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAC1C,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,EACtD,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,UAAU,CAClB,CAAA;KACJ,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { UnexpectedError } 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.GlobalEventSequenceNumber) => 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
|
+
}, UnexpectedError, 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,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,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,yBAAyB;;;;;;;;AAjE3F,qBAAa,KAAM,SAAQ,UA0GzB;CAAG"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { UnexpectedError } 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* UnexpectedError.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.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.ROOT.global);
|
|
86
|
+
}
|
|
87
|
+
return storageCache;
|
|
88
|
+
}, UnexpectedError.mapToUnexpectedError, 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":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAErE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AAEhD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;AAWnC,MAAM,OAAO,KAAM,SAAQ,MAAM,CAAC,OAAO,EAAS,CAAC,OAAO,EAAE;IAC1D,MAAM,EAAE,MAAM,CAAC,EAAE,CACf,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAc;QAChD,IAAK,MAAc,CAAC,WAAW,CAAC,KAAK,SAAS,EAAE,CAAC;YAC/C,OAAQ,MAAc,CAAC,WAAW,CAAU,CAAA;QAC9C,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,IAA2C,EAAE,EAAE;YACjE,IAAI,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;gBACvC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;gBAC7B,OAAO,CACL,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,iBAAiB,CAAC,kDAAkD,CAAC,CACzG,CAAA;YACH,CAAC;YACD,OAAO,IAAI,CAAC,OAAO,CAAA;QACrB,CAAC,CAAA;QAED,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QAChC,yBAAyB;QACzB,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACrC,MAAM,GAAG,GAAG,SAAS,EAAE,OAAO,CAAA;YAC9B,IAAI,GAAG,EAAE,IAAI,KAAK,IAAI,EAAE,CAAC;gBACvB,MAAM,EAAE,GAAI,MAAM,CAAC,GAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;gBAC3C,IAAI,CAAC,EAAE,EAAE,CAAC;oBACR,OAAO,KAAK,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,eAAe,GAAG,CAAC,OAAO,oBAAoB,CAAC,EAAE,CAAC,CAAA;gBAC1G,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,IAAa,EAAE,EAAE,EAAE,CAAA;YACpC,CAAC;iBAAM,IAAI,GAAG,EAAE,IAAI,KAAK,WAAW,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBAC1D,OAAO,EAAE,IAAI,EAAE,WAAoB,EAAE,CAAA;YACvC,CAAC;;gBAAM,OAAO,iBAAiB,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAA;QAChE,CAAC,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAA;QAEhC,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,CAAA;QAExD,6BAA6B;QAC7B,CAAC;YACC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;YACxE,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBACzB,oDAAoD;gBACpD,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CACzB,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,+BAA+B,OAAO,CAAC,MAAM,MAAM,OAAO,UAAU,CAAC,CACrF,CAAA;YACH,CAAC;iBAAM,CAAC;gBACN,kDAAkD;gBAClD,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,+BAA+B,OAAO,CAAC,MAAM,MAAM,OAAO,UAAU,CAAC,CAAA;YACnG,CAAC;QACH,CAAC;QACD,CAAC;YACC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;YACvE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,+BAA+B,YAAY,CAAC,SAAS,CAAC,IAAI,MAAM,OAAO,UAAU,CAAC,CAAA;QAChH,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG;aACtC,IAAI,CAAC,kBAAkB,YAAY,CAAC,SAAS,CAAC,IAAI,qBAAqB,EAAE,OAAO,CAAC;aACjF,OAAO,EAAE,CAAC,CAAC,CAAmD,CAAA;QAEjE,MAAM,cAAc,GAAG,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,IAAI,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,CAAA;QAE9F,oFAAoF;QAEpF,kEAAkE;QAClE,MAAM,SAAS,GAAG,UAAU,EAAE,SAAS,IAAI,MAAM,EAAE,CAAA;QAEnD,MAAM,iBAAiB,GAAG,CAAC,WAA0D,EAAE,EAAE;YACvF,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CACzB,2BAA2B,YAAY,CAAC,SAAS,CAAC,IAAI,sDAAsD,EAC5G,OAAO,EACP,WAAW,EACX,SAAS,CACV,CAAA;YAED,cAAc,CAAC,OAAO,GAAG,WAAW,CAAA;YAEpC,wHAAwH;YACxH,mBAAmB;YACnB,MAAM,CAAC,WAAW,CAAC,CAAC,cAAc,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,CAAA;QAC/D,CAAC,CAAA;QAED,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA2B,CAAA;QAE3D,MAAM,YAAY,GAAG;YACnB,OAAO;YACP,SAAS;YACT,cAAc;YACd,iBAAiB;YACjB,OAAO;YACP,SAAS;YACT,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,gBAAgB;SACjB,CAEA;QAAC,MAAc,CAAC,WAAW,CAAC,GAAG,YAAY,CAAA;QAE5C,mCAAmC;QACnC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,iBAAiB,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACpD,CAAC;QAED,OAAO,YAAY,CAAA;IACrB,CAAC,EACD,eAAe,CAAC,oBAAoB,EACpC,MAAM,CAAC,QAAQ,CAAC,6CAA6C,CAAC,CAC/D;CACF,CAAC;CAAG"}
|
|
@@ -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,EAAgC,MAAM,mBAAmB,CAAA;AAE1G,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,CAuD/D,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BackendIdMismatchError, InvalidPullError, SyncBackend, UnexpectedError } 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(UnexpectedError.mapToUnexpectedError);
|
|
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(UnexpectedError.mapToUnexpectedError);
|
|
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, UnexpectedError, } 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(UnexpectedError.mapToUnexpectedError);
|
|
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(UnexpectedError.mapToUnexpectedError);
|
|
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(UnexpectedError.mapToUnexpectedError);
|
|
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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"}
|