@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.
- 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 +151 -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} +83 -67
- package/dist/cf-worker/do/sqlite.d.ts.map +1 -0
- package/dist/cf-worker/do/sqlite.js +36 -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 +191 -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 +8 -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 +30 -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 +175 -0
- package/dist/cf-worker/shared.d.ts.map +1 -0
- package/dist/cf-worker/shared.js +43 -0
- package/dist/cf-worker/shared.js.map +1 -0
- package/dist/cf-worker/worker.d.ts +59 -51
- package/dist/cf-worker/worker.d.ts.map +1 -1
- package/dist/cf-worker/worker.js +75 -43
- 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 +115 -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 +46 -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 +238 -0
- package/src/cf-worker/do/layer.ts +128 -0
- package/src/cf-worker/do/pull.ts +75 -0
- package/src/cf-worker/do/push.ts +205 -0
- package/src/cf-worker/do/sqlite.ts +37 -0
- package/src/cf-worker/do/sync-storage.ts +323 -0
- package/src/cf-worker/do/transport/do-rpc-server.ts +84 -0
- package/src/cf-worker/do/transport/http-rpc-server.ts +51 -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 +141 -0
- package/src/cf-worker/worker.ts +138 -116
- package/src/client/mod.ts +3 -0
- package/src/client/transport/do-rpc-client.ts +189 -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,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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"}
|