effect-cf 0.46.1 → 0.48.0
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/CHANGELOG.md +16 -0
- package/README.md +2 -0
- package/dist/Artifacts.d.mts +1 -1
- package/dist/Artifacts.mjs +11 -1
- package/dist/DurableObjectRpcWebSocket.mjs +20 -7
- package/dist/RpcWebSocketClient.d.mts +30 -0
- package/dist/RpcWebSocketClient.mjs +19 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/package.json +6 -1
- package/src/Artifacts.ts +15 -2
- package/src/DurableObjectRpcWebSocket.ts +25 -2
- package/src/RpcWebSocketClient.ts +45 -0
- package/src/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# effect-cf
|
|
2
2
|
|
|
3
|
+
## 0.48.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#192](https://github.com/danieljvdm/effect-cf/pull/192) [`761c4f4`](https://github.com/danieljvdm/effect-cf/commit/761c4f411e11ae0491f61c6fe7c7697faf68e7f0) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Report absent Artifacts commits as `ArtifactsOperationError` with `NOT_FOUND` instead of succeeding with `null`. Correct the raw `ArtifactsRepoBinding.readCommit` return type to include `null`; the Effect client continues to return a commit or a typed failure.
|
|
8
|
+
|
|
9
|
+
## 0.47.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#190](https://github.com/danieljvdm/effect-cf/pull/190) [`a3ae008`](https://github.com/danieljvdm/effect-cf/commit/a3ae008af959d08eec649042edc9309982354a0d) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Add `RpcWebSocketClient.layer` and the browser-safe `effect-cf/rpc-websocket-client` entrypoint. Provide a typed client service and its WebSocket connection for an application or session without requiring callers to wrap client construction in `Effect.scoped`. The layer closes the connection when its lifetime ends.
|
|
14
|
+
|
|
15
|
+
Persist the RPC serializer's content type in Durable Object WebSocket attachments and reset restored connections whose saved content type differs from the server's, preventing incompatible frames from reaching RPC handlers after a deployment. Existing attachments without serializer metadata remain supported.
|
|
16
|
+
|
|
17
|
+
Acknowledge WebSocket close events and ignore late messages on closed connections so client scope cleanup completes the close handshake.
|
|
18
|
+
|
|
3
19
|
## 0.46.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ The repository tests against workerd `1.20260825.1` and `@cloudflare/workers-typ
|
|
|
10
10
|
|
|
11
11
|
See the [runtime performance guide](https://github.com/danieljvdm/effect-cf/blob/main/docs/runtime-performance.md) for measured alarm batching and telemetry configuration guidance.
|
|
12
12
|
|
|
13
|
+
For typed WebSocket RPC clients whose connection lifetime is owned by an Effect layer, see the [WebSocket RPC guide](https://github.com/danieljvdm/effect-cf/blob/main/docs/websocket-rpc.md).
|
|
14
|
+
|
|
13
15
|
## Worker
|
|
14
16
|
|
|
15
17
|
```ts
|
package/dist/Artifacts.d.mts
CHANGED
|
@@ -145,7 +145,7 @@ interface ArtifactsRepoBinding extends Partial<ArtifactsRepoInfo> {
|
|
|
145
145
|
readonly revokeToken: (tokenOrId: string) => Promise<boolean>;
|
|
146
146
|
readonly fork: (name: RepoName, options?: ArtifactsForkOptions) => Promise<ArtifactsCreateRepoResult>;
|
|
147
147
|
readonly log: (options?: ArtifactsLogOptions) => Promise<ArtifactsLogResult>;
|
|
148
|
-
readonly readCommit: (hash: string) => Promise<ArtifactsCommit>;
|
|
148
|
+
readonly readCommit: (hash: string) => Promise<ArtifactsCommit | null>;
|
|
149
149
|
readonly readTree: (hash: string) => Promise<ArtifactsTree>;
|
|
150
150
|
}
|
|
151
151
|
interface ArtifactsBinding {
|
package/dist/Artifacts.mjs
CHANGED
|
@@ -105,7 +105,17 @@ const wrapRepo = (binding, repo, info) => ({
|
|
|
105
105
|
revokeToken: Effect.fn("Artifacts.revokeToken", spanOptions(binding, "revokeToken"))((tokenOrId) => tryArtifactsPromise(binding, "revokeToken", () => repo.revokeToken(tokenOrId))),
|
|
106
106
|
fork: Effect.fn("Artifacts.fork", spanOptions(binding, "fork"))((name, options) => tryArtifactsPromise(binding, "fork", () => repo.fork(name, options))),
|
|
107
107
|
log: Effect.fn("Artifacts.log", spanOptions(binding, "log"))((options) => tryArtifactsPromise(binding, "log", () => repo.log(options))),
|
|
108
|
-
readCommit: Effect.fn("Artifacts.readCommit", spanOptions(binding, "readCommit"))((hash)
|
|
108
|
+
readCommit: Effect.fn("Artifacts.readCommit", spanOptions(binding, "readCommit"))(function* (hash) {
|
|
109
|
+
const commit = yield* tryArtifactsPromise(binding, "readCommit", () => repo.readCommit(hash));
|
|
110
|
+
if (commit === null) return yield* new ArtifactsOperationError({
|
|
111
|
+
binding,
|
|
112
|
+
operation: "readCommit",
|
|
113
|
+
code: "NOT_FOUND",
|
|
114
|
+
numericCode: artifactsErrorNumericCodes.NOT_FOUND,
|
|
115
|
+
cause: /* @__PURE__ */ new Error("Commit not found")
|
|
116
|
+
});
|
|
117
|
+
return commit;
|
|
118
|
+
}),
|
|
109
119
|
readTree: Effect.fn("Artifacts.readTree", spanOptions(binding, "readTree"))((hash) => tryArtifactsPromise(binding, "readTree", () => repo.readTree(hash))),
|
|
110
120
|
raw: repo
|
|
111
121
|
});
|
|
@@ -40,7 +40,8 @@ const ResumableAttachmentMetadataSchema = Schema.Struct({
|
|
|
40
40
|
clientId: Schema.Finite,
|
|
41
41
|
hasPendingRequests: Schema.Boolean,
|
|
42
42
|
hasNonResumableRequests: Schema.Boolean,
|
|
43
|
-
subscriptions: Schema.Array(PersistedSubscriptionSchema)
|
|
43
|
+
subscriptions: Schema.Array(PersistedSubscriptionSchema),
|
|
44
|
+
serialization: Schema.optionalKey(Schema.String)
|
|
44
45
|
});
|
|
45
46
|
const decodeResumableAttachment = Schema.decodeUnknownOption(ResumableAttachmentMetadataSchema);
|
|
46
47
|
var DurableObjectRpcWebSocket = class extends Context.Service()("effect-cf/DurableObjectRpcWebSocket") {};
|
|
@@ -83,7 +84,8 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
83
84
|
const created = {
|
|
84
85
|
version: legacyAttachmentVersion,
|
|
85
86
|
clientId: id,
|
|
86
|
-
hasPendingRequests: false
|
|
87
|
+
hasPendingRequests: false,
|
|
88
|
+
serialization: serialization.contentType
|
|
87
89
|
};
|
|
88
90
|
writeAttachment(socket.raw, attachmentKey, created);
|
|
89
91
|
return created;
|
|
@@ -143,7 +145,8 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
143
145
|
writeAttachment(connection.socket.raw, attachmentKey, {
|
|
144
146
|
version: legacyAttachmentVersion,
|
|
145
147
|
clientId: connection.id,
|
|
146
|
-
hasPendingRequests: connection.requestIds.size > 0
|
|
148
|
+
hasPendingRequests: connection.requestIds.size > 0,
|
|
149
|
+
serialization: serialization.contentType
|
|
147
150
|
});
|
|
148
151
|
return;
|
|
149
152
|
}
|
|
@@ -153,7 +156,8 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
153
156
|
clientId: connection.id,
|
|
154
157
|
hasPendingRequests: connection.requestIds.size > 0,
|
|
155
158
|
hasNonResumableRequests: connection.nonResumableRequestIds.size > 0,
|
|
156
|
-
subscriptions
|
|
159
|
+
subscriptions,
|
|
160
|
+
serialization: serialization.contentType
|
|
157
161
|
});
|
|
158
162
|
},
|
|
159
163
|
catch: (cause) => new DurableWebSocketAttachmentError({
|
|
@@ -249,6 +253,10 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
249
253
|
continue;
|
|
250
254
|
}
|
|
251
255
|
const metadata = attachment.metadata;
|
|
256
|
+
if (metadata.serialization !== void 0 && metadata.serialization !== serialization.contentType) {
|
|
257
|
+
yield* resetSocket(socket);
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
252
260
|
if (metadata.version === legacyAttachmentVersion) {
|
|
253
261
|
if (metadata.hasPendingRequests) yield* resetSocket(socket);
|
|
254
262
|
else if (connectionsById.has(metadata.clientId)) yield* resetSocket(socket);
|
|
@@ -476,7 +484,10 @@ const layer = (options = {}) => Layer.effectContext(Effect.gen(function* () {
|
|
|
476
484
|
const connection = register(socket);
|
|
477
485
|
return Predicate.isTagged(cause, "MaxBufferSizeExceeded") ? Effect.ignore(connection.socket.close(1009, String(cause))) : send(connection, RpcMessage.ResponseDefectEncoded(encodeDefect(cause)));
|
|
478
486
|
})),
|
|
479
|
-
close:
|
|
487
|
+
close: (socket) => Effect.gen(function* () {
|
|
488
|
+
resetSockets.add(socket.raw);
|
|
489
|
+
yield* socket.close(1e3).pipe(Effect.ignore);
|
|
490
|
+
}).pipe(Effect.ensuring(unregister(socket)), Effect.uninterruptible),
|
|
480
491
|
error: resetSocket,
|
|
481
492
|
checkpoint
|
|
482
493
|
});
|
|
@@ -506,12 +517,14 @@ const readAttachment = (socket, key) => {
|
|
|
506
517
|
} : { _tag: "Invalid" };
|
|
507
518
|
}
|
|
508
519
|
const hasPendingRequests = Predicate.hasProperty(metadata, "hasPendingRequests") ? metadata.hasPendingRequests : false;
|
|
509
|
-
|
|
520
|
+
const serialization = Predicate.hasProperty(metadata, "serialization") ? metadata.serialization : void 0;
|
|
521
|
+
if ((!Predicate.hasProperty(metadata, "version") || metadata.version === legacyAttachmentVersion) && Predicate.hasProperty(metadata, "clientId") && Predicate.isNumber(metadata.clientId) && isClientId(metadata.clientId) && Predicate.isBoolean(hasPendingRequests) && (serialization === void 0 || Predicate.isString(serialization))) return {
|
|
510
522
|
_tag: "Valid",
|
|
511
523
|
metadata: {
|
|
512
524
|
version: legacyAttachmentVersion,
|
|
513
525
|
clientId: metadata.clientId,
|
|
514
|
-
hasPendingRequests
|
|
526
|
+
hasPendingRequests,
|
|
527
|
+
serialization
|
|
515
528
|
}
|
|
516
529
|
};
|
|
517
530
|
return { _tag: "Invalid" };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
4
|
+
import * as RpcClient from "effect/unstable/rpc/RpcClient";
|
|
5
|
+
import * as Socket from "effect/unstable/socket/Socket";
|
|
6
|
+
import * as Rpc from "effect/unstable/rpc/Rpc";
|
|
7
|
+
import { RpcClientError } from "effect/unstable/rpc/RpcClientError";
|
|
8
|
+
import * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
|
9
|
+
declare namespace RpcWebSocketClient_d_exports {
|
|
10
|
+
export { LayerOptions, layer };
|
|
11
|
+
}
|
|
12
|
+
interface LayerOptions {
|
|
13
|
+
readonly socket?: Parameters<typeof Socket.layerWebSocket>[1];
|
|
14
|
+
readonly protocol?: Parameters<typeof RpcClient.layerProtocolSocket>[0];
|
|
15
|
+
readonly client?: Omit<NonNullable<Parameters<typeof RpcClient.make>[1]>, "flatten">;
|
|
16
|
+
/** Must match the server's serialization. Defaults to Effect RPC JSON. */
|
|
17
|
+
readonly serialization?: RpcSerialization.RpcSerialization["Service"] | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Provides a typed RPC client and its WebSocket transport for the layer's lifetime.
|
|
21
|
+
* Callers yield the service without supplying a Scope. Provide this layer once
|
|
22
|
+
* around the application or session to share the connection across calls.
|
|
23
|
+
*
|
|
24
|
+
* Provide Socket.layerWebSocketConstructorGlobal in browsers, or a platform's
|
|
25
|
+
* WebSocketConstructor layer elsewhere. Streaming RPCs returned as queues still
|
|
26
|
+
* require a consumer scope; ordinary Stream operations manage their own scopes.
|
|
27
|
+
*/
|
|
28
|
+
declare const layer: <I, Rpcs extends Rpc.Any>(service: Context.Key<I, RpcClient.RpcClient<Rpcs, RpcClientError>>, group: RpcGroup.RpcGroup<Rpcs>, url: Parameters<typeof Socket.layerWebSocket>[0], options?: LayerOptions) => Layer.Layer<I, never, Socket.WebSocketConstructor | Rpc.MiddlewareClient<Rpcs>>;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { LayerOptions, RpcWebSocketClient_d_exports, layer };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { __exportAll } from "./_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
4
|
+
import * as RpcClient from "effect/unstable/rpc/RpcClient";
|
|
5
|
+
import * as Socket from "effect/unstable/socket/Socket";
|
|
6
|
+
//#region src/RpcWebSocketClient.ts
|
|
7
|
+
var RpcWebSocketClient_exports = /* @__PURE__ */ __exportAll({ layer: () => layer });
|
|
8
|
+
/**
|
|
9
|
+
* Provides a typed RPC client and its WebSocket transport for the layer's lifetime.
|
|
10
|
+
* Callers yield the service without supplying a Scope. Provide this layer once
|
|
11
|
+
* around the application or session to share the connection across calls.
|
|
12
|
+
*
|
|
13
|
+
* Provide Socket.layerWebSocketConstructorGlobal in browsers, or a platform's
|
|
14
|
+
* WebSocketConstructor layer elsewhere. Streaming RPCs returned as queues still
|
|
15
|
+
* require a consumer scope; ordinary Stream operations manage their own scopes.
|
|
16
|
+
*/
|
|
17
|
+
const layer = (service, group, url, options = {}) => Layer.effect(service, RpcClient.make(group, options.client)).pipe(Layer.provide(RpcClient.layerProtocolSocket(options.protocol).pipe(Layer.provide(Socket.layerWebSocket(url, options.socket)), Layer.provide(Layer.succeed(RpcSerialization.RpcSerialization, options.serialization ?? RpcSerialization.json)))));
|
|
18
|
+
//#endregion
|
|
19
|
+
export { RpcWebSocketClient_exports, layer };
|
package/dist/index.d.mts
CHANGED
|
@@ -14,6 +14,7 @@ import { ServiceBinding_d_exports } from "./ServiceBinding.mjs";
|
|
|
14
14
|
import { Worker_d_exports } from "./Worker.mjs";
|
|
15
15
|
import { DurableObjectState_d_exports } from "./DurableObjectState.mjs";
|
|
16
16
|
import { Hyperdrive_d_exports } from "./Hyperdrive.mjs";
|
|
17
|
+
import { RpcWebSocketClient_d_exports } from "./RpcWebSocketClient.mjs";
|
|
17
18
|
import { ContainerNamespace_d_exports } from "./ContainerNamespace.mjs";
|
|
18
19
|
import { WorkflowBinding_d_exports } from "./WorkflowBinding.mjs";
|
|
19
20
|
import { WorkersAi_d_exports } from "./WorkersAi.mjs";
|
|
@@ -39,4 +40,4 @@ import { Vectorize_d_exports } from "./Vectorize.mjs";
|
|
|
39
40
|
import { WebTransport_d_exports } from "./WebTransport.mjs";
|
|
40
41
|
import { WorkflowDefinition_d_exports } from "./WorkflowDefinition.mjs";
|
|
41
42
|
import { Workflow_d_exports } from "./Workflow.mjs";
|
|
42
|
-
export { AiGateway_d_exports as AiGateway, AnalyticsEngine_d_exports as AnalyticsEngine, Artifacts_d_exports as Artifacts, Binding_d_exports as Binding, BrowserRendering_d_exports as BrowserRendering, Cache_d_exports as Cache, CloudflareOtlp_d_exports as CloudflareOtlp, CloudflareTracer_d_exports as CloudflareTracer, ContainerNamespace_d_exports as ContainerNamespace, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_d_exports as DurableObjectSqlite, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Email_d_exports as Email, Environment_d_exports as Environment, Hyperdrive_d_exports as Hyperdrive, Images_d_exports as Images, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, R2_d_exports as R2, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, RpcTargets_d_exports as RpcTargets, RpcTracing_d_exports as RpcTracing, ServiceBinding_d_exports as ServiceBinding, Vectorize_d_exports as Vectorize, WebTransport_d_exports as WebTransport, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, WorkersAi_d_exports as WorkersAi, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
|
|
43
|
+
export { AiGateway_d_exports as AiGateway, AnalyticsEngine_d_exports as AnalyticsEngine, Artifacts_d_exports as Artifacts, Binding_d_exports as Binding, BrowserRendering_d_exports as BrowserRendering, Cache_d_exports as Cache, CloudflareOtlp_d_exports as CloudflareOtlp, CloudflareTracer_d_exports as CloudflareTracer, ContainerNamespace_d_exports as ContainerNamespace, D1_d_exports as D1, DurableObject_d_exports as DurableObject, DurableObjectAlarm_d_exports as DurableObjectAlarm, DurableObjectDefinition_d_exports as DurableObjectDefinition, DurableObjectNamespace_d_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_d_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_d_exports as DurableObjectSqlite, DurableObjectState_d_exports as DurableObjectState, DurableObjectStorage_d_exports as DurableObjectStorage, DurableObjectWebSocket_d_exports as DurableObjectWebSocket, Email_d_exports as Email, Environment_d_exports as Environment, Hyperdrive_d_exports as Hyperdrive, Images_d_exports as Images, Kv_d_exports as Kv, Queue_d_exports as Queue, QueueBinding_d_exports as QueueBinding, QueueDefinition_d_exports as QueueDefinition, R2_d_exports as R2, Rpc_d_exports as Rpc, RpcDefinition_d_exports as RpcDefinition, RpcTargets_d_exports as RpcTargets, RpcTracing_d_exports as RpcTracing, RpcWebSocketClient_d_exports as RpcWebSocketClient, ServiceBinding_d_exports as ServiceBinding, Vectorize_d_exports as Vectorize, WebTransport_d_exports as WebTransport, Worker_d_exports as Worker, WorkerConfig, WorkerDefinition_d_exports as WorkerDefinition, type WorkerEnv, WorkerEnvironment, WorkersAi_d_exports as WorkersAi, Workflow_d_exports as Workflow, WorkflowBinding_d_exports as WorkflowBinding, WorkflowDefinition_d_exports as WorkflowDefinition };
|
package/dist/index.mjs
CHANGED
|
@@ -34,9 +34,10 @@ import { Hyperdrive_exports } from "./Hyperdrive.mjs";
|
|
|
34
34
|
import { Images_exports } from "./Images.mjs";
|
|
35
35
|
import { Kv_exports } from "./Kv.mjs";
|
|
36
36
|
import { R2_exports } from "./R2.mjs";
|
|
37
|
+
import { RpcWebSocketClient_exports } from "./RpcWebSocketClient.mjs";
|
|
37
38
|
import { Vectorize_exports } from "./Vectorize.mjs";
|
|
38
39
|
import { WebTransport_exports } from "./WebTransport.mjs";
|
|
39
40
|
import { WorkflowBinding_exports } from "./WorkflowBinding.mjs";
|
|
40
41
|
import { WorkflowDefinition_exports } from "./WorkflowDefinition.mjs";
|
|
41
42
|
import { Workflow_exports } from "./Workflow.mjs";
|
|
42
|
-
export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTargets_exports as RpcTargets, RpcTracing_exports as RpcTracing, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
|
|
43
|
+
export { AiGateway_exports as AiGateway, AnalyticsEngine_exports as AnalyticsEngine, Artifacts_exports as Artifacts, Binding_exports as Binding, BrowserRendering_exports as BrowserRendering, Cache_exports as Cache, CloudflareOtlp_exports as CloudflareOtlp, CloudflareTracer_exports as CloudflareTracer, ContainerNamespace_exports as ContainerNamespace, D1_exports as D1, DurableObject_exports as DurableObject, DurableObjectAlarm_exports as DurableObjectAlarm, DurableObjectDefinition_exports as DurableObjectDefinition, DurableObjectNamespace_exports as DurableObjectNamespace, DurableObjectRpcWebSocket_exports as DurableObjectRpcWebSocket, DurableObjectSqlite_exports as DurableObjectSqlite, DurableObjectState_exports as DurableObjectState, DurableObjectStorage_exports as DurableObjectStorage, DurableObjectWebSocket_exports as DurableObjectWebSocket, Email_exports as Email, Environment_exports as Environment, Hyperdrive_exports as Hyperdrive, Images_exports as Images, Kv_exports as Kv, Queue_exports as Queue, QueueBinding_exports as QueueBinding, QueueDefinition_exports as QueueDefinition, R2_exports as R2, Rpc_exports as Rpc, RpcDefinition_exports as RpcDefinition, RpcTargets_exports as RpcTargets, RpcTracing_exports as RpcTracing, RpcWebSocketClient_exports as RpcWebSocketClient, ServiceBinding_exports as ServiceBinding, Vectorize_exports as Vectorize, WebTransport_exports as WebTransport, Worker_exports as Worker, WorkerConfig, WorkerDefinition_exports as WorkerDefinition, WorkerEnvironment, WorkersAi_exports as WorkersAi, Workflow_exports as Workflow, WorkflowBinding_exports as WorkflowBinding, WorkflowDefinition_exports as WorkflowDefinition };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-cf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "Effect-native primitives for Cloudflare Workers and bindings.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -57,6 +57,11 @@
|
|
|
57
57
|
"import": "./dist/Vitest.mjs",
|
|
58
58
|
"default": "./dist/Vitest.mjs"
|
|
59
59
|
},
|
|
60
|
+
"./rpc-websocket-client": {
|
|
61
|
+
"types": "./dist/RpcWebSocketClient.d.mts",
|
|
62
|
+
"import": "./dist/RpcWebSocketClient.mjs",
|
|
63
|
+
"default": "./dist/RpcWebSocketClient.mjs"
|
|
64
|
+
},
|
|
60
65
|
"./package.json": "./package.json"
|
|
61
66
|
},
|
|
62
67
|
"publishConfig": {
|
package/src/Artifacts.ts
CHANGED
|
@@ -222,7 +222,7 @@ export interface ArtifactsRepoBinding extends Partial<ArtifactsRepoInfo> {
|
|
|
222
222
|
options?: ArtifactsForkOptions,
|
|
223
223
|
) => Promise<ArtifactsCreateRepoResult>;
|
|
224
224
|
readonly log: (options?: ArtifactsLogOptions) => Promise<ArtifactsLogResult>;
|
|
225
|
-
readonly readCommit: (hash: string) => Promise<ArtifactsCommit>;
|
|
225
|
+
readonly readCommit: (hash: string) => Promise<ArtifactsCommit | null>;
|
|
226
226
|
readonly readTree: (hash: string) => Promise<ArtifactsTree>;
|
|
227
227
|
}
|
|
228
228
|
|
|
@@ -451,7 +451,20 @@ const wrapRepo = (
|
|
|
451
451
|
readCommit: Effect.fn(
|
|
452
452
|
"Artifacts.readCommit",
|
|
453
453
|
spanOptions(binding, "readCommit"),
|
|
454
|
-
)((hash: string)
|
|
454
|
+
)(function* (hash: string) {
|
|
455
|
+
const commit = yield* tryArtifactsPromise(binding, "readCommit", () => repo.readCommit(hash));
|
|
456
|
+
|
|
457
|
+
if (commit === null)
|
|
458
|
+
return yield* new ArtifactsOperationError({
|
|
459
|
+
binding,
|
|
460
|
+
operation: "readCommit",
|
|
461
|
+
code: "NOT_FOUND",
|
|
462
|
+
numericCode: artifactsErrorNumericCodes.NOT_FOUND,
|
|
463
|
+
cause: new Error("Commit not found"),
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
return commit;
|
|
467
|
+
}),
|
|
455
468
|
readTree: Effect.fn(
|
|
456
469
|
"Artifacts.readTree",
|
|
457
470
|
spanOptions(binding, "readTree"),
|
|
@@ -148,6 +148,7 @@ interface LegacyAttachmentMetadata {
|
|
|
148
148
|
readonly version: 1;
|
|
149
149
|
readonly clientId: number;
|
|
150
150
|
readonly hasPendingRequests: boolean;
|
|
151
|
+
readonly serialization?: string | undefined;
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
interface PersistedPendingBatch {
|
|
@@ -170,6 +171,7 @@ interface ResumableAttachmentMetadata {
|
|
|
170
171
|
readonly hasPendingRequests: boolean;
|
|
171
172
|
readonly hasNonResumableRequests: boolean;
|
|
172
173
|
readonly subscriptions: ReadonlyArray<PersistedSubscription>;
|
|
174
|
+
readonly serialization?: string | undefined;
|
|
173
175
|
}
|
|
174
176
|
|
|
175
177
|
type AttachmentMetadata = LegacyAttachmentMetadata | ResumableAttachmentMetadata;
|
|
@@ -200,6 +202,7 @@ const ResumableAttachmentMetadataSchema = Schema.Struct({
|
|
|
200
202
|
hasPendingRequests: Schema.Boolean,
|
|
201
203
|
hasNonResumableRequests: Schema.Boolean,
|
|
202
204
|
subscriptions: Schema.Array(PersistedSubscriptionSchema),
|
|
205
|
+
serialization: Schema.optionalKey(Schema.String),
|
|
203
206
|
});
|
|
204
207
|
|
|
205
208
|
const decodeResumableAttachment = Schema.decodeUnknownOption(ResumableAttachmentMetadataSchema);
|
|
@@ -279,6 +282,7 @@ export const layer = (
|
|
|
279
282
|
version: legacyAttachmentVersion,
|
|
280
283
|
clientId: id,
|
|
281
284
|
hasPendingRequests: false,
|
|
285
|
+
serialization: serialization.contentType,
|
|
282
286
|
};
|
|
283
287
|
|
|
284
288
|
writeAttachment(socket.raw, attachmentKey, created);
|
|
@@ -392,6 +396,7 @@ export const layer = (
|
|
|
392
396
|
version: legacyAttachmentVersion,
|
|
393
397
|
clientId: connection.id,
|
|
394
398
|
hasPendingRequests: connection.requestIds.size > 0,
|
|
399
|
+
serialization: serialization.contentType,
|
|
395
400
|
});
|
|
396
401
|
|
|
397
402
|
return;
|
|
@@ -407,6 +412,7 @@ export const layer = (
|
|
|
407
412
|
hasPendingRequests: connection.requestIds.size > 0,
|
|
408
413
|
hasNonResumableRequests: connection.nonResumableRequestIds.size > 0,
|
|
409
414
|
subscriptions,
|
|
415
|
+
serialization: serialization.contentType,
|
|
410
416
|
});
|
|
411
417
|
},
|
|
412
418
|
catch: (cause) => new DurableWebSocketAttachmentError({ operation: "serialize", cause }),
|
|
@@ -589,6 +595,14 @@ export const layer = (
|
|
|
589
595
|
|
|
590
596
|
const metadata = attachment.metadata;
|
|
591
597
|
|
|
598
|
+
if (
|
|
599
|
+
metadata.serialization !== undefined &&
|
|
600
|
+
metadata.serialization !== serialization.contentType
|
|
601
|
+
) {
|
|
602
|
+
yield* resetSocket(socket);
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
|
|
592
606
|
if (metadata.version === legacyAttachmentVersion) {
|
|
593
607
|
if (metadata.hasPendingRequests) {
|
|
594
608
|
yield* resetSocket(socket);
|
|
@@ -1036,7 +1050,11 @@ export const layer = (
|
|
|
1036
1050
|
: send(connection, RpcMessage.ResponseDefectEncoded(encodeDefect(cause)));
|
|
1037
1051
|
}),
|
|
1038
1052
|
),
|
|
1039
|
-
close:
|
|
1053
|
+
close: (socket) =>
|
|
1054
|
+
Effect.gen(function* () {
|
|
1055
|
+
resetSockets.add(socket.raw);
|
|
1056
|
+
yield* socket.close(1000).pipe(Effect.ignore);
|
|
1057
|
+
}).pipe(Effect.ensuring(unregister(socket)), Effect.uninterruptible),
|
|
1040
1058
|
error: resetSocket,
|
|
1041
1059
|
checkpoint,
|
|
1042
1060
|
});
|
|
@@ -1102,13 +1120,17 @@ const readAttachment = (socket: WebSocket, key: string): AttachmentRead => {
|
|
|
1102
1120
|
const hasPendingRequests = Predicate.hasProperty(metadata, "hasPendingRequests")
|
|
1103
1121
|
? metadata.hasPendingRequests
|
|
1104
1122
|
: false;
|
|
1123
|
+
const serialization = Predicate.hasProperty(metadata, "serialization")
|
|
1124
|
+
? metadata.serialization
|
|
1125
|
+
: undefined;
|
|
1105
1126
|
|
|
1106
1127
|
if (
|
|
1107
1128
|
(!Predicate.hasProperty(metadata, "version") || metadata.version === legacyAttachmentVersion) &&
|
|
1108
1129
|
Predicate.hasProperty(metadata, "clientId") &&
|
|
1109
1130
|
Predicate.isNumber(metadata.clientId) &&
|
|
1110
1131
|
isClientId(metadata.clientId) &&
|
|
1111
|
-
Predicate.isBoolean(hasPendingRequests)
|
|
1132
|
+
Predicate.isBoolean(hasPendingRequests) &&
|
|
1133
|
+
(serialization === undefined || Predicate.isString(serialization))
|
|
1112
1134
|
) {
|
|
1113
1135
|
return {
|
|
1114
1136
|
_tag: "Valid",
|
|
@@ -1116,6 +1138,7 @@ const readAttachment = (socket: WebSocket, key: string): AttachmentRead => {
|
|
|
1116
1138
|
version: legacyAttachmentVersion,
|
|
1117
1139
|
clientId: metadata.clientId,
|
|
1118
1140
|
hasPendingRequests,
|
|
1141
|
+
serialization,
|
|
1119
1142
|
},
|
|
1120
1143
|
};
|
|
1121
1144
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type * as Context from "effect/Context";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import type * as Rpc from "effect/unstable/rpc/Rpc";
|
|
4
|
+
import * as RpcClient from "effect/unstable/rpc/RpcClient";
|
|
5
|
+
import type { RpcClientError } from "effect/unstable/rpc/RpcClientError";
|
|
6
|
+
import type * as RpcGroup from "effect/unstable/rpc/RpcGroup";
|
|
7
|
+
import * as RpcSerialization from "effect/unstable/rpc/RpcSerialization";
|
|
8
|
+
import * as Socket from "effect/unstable/socket/Socket";
|
|
9
|
+
|
|
10
|
+
export interface LayerOptions {
|
|
11
|
+
readonly socket?: Parameters<typeof Socket.layerWebSocket>[1];
|
|
12
|
+
readonly protocol?: Parameters<typeof RpcClient.layerProtocolSocket>[0];
|
|
13
|
+
readonly client?: Omit<NonNullable<Parameters<typeof RpcClient.make>[1]>, "flatten">;
|
|
14
|
+
/** Must match the server's serialization. Defaults to Effect RPC JSON. */
|
|
15
|
+
readonly serialization?: RpcSerialization.RpcSerialization["Service"] | undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Provides a typed RPC client and its WebSocket transport for the layer's lifetime.
|
|
20
|
+
* Callers yield the service without supplying a Scope. Provide this layer once
|
|
21
|
+
* around the application or session to share the connection across calls.
|
|
22
|
+
*
|
|
23
|
+
* Provide Socket.layerWebSocketConstructorGlobal in browsers, or a platform's
|
|
24
|
+
* WebSocketConstructor layer elsewhere. Streaming RPCs returned as queues still
|
|
25
|
+
* require a consumer scope; ordinary Stream operations manage their own scopes.
|
|
26
|
+
*/
|
|
27
|
+
export const layer = <I, Rpcs extends Rpc.Any>(
|
|
28
|
+
service: Context.Key<I, RpcClient.RpcClient<Rpcs, RpcClientError>>,
|
|
29
|
+
group: RpcGroup.RpcGroup<Rpcs>,
|
|
30
|
+
url: Parameters<typeof Socket.layerWebSocket>[0],
|
|
31
|
+
options: LayerOptions = {},
|
|
32
|
+
): Layer.Layer<I, never, Socket.WebSocketConstructor | Rpc.MiddlewareClient<Rpcs>> =>
|
|
33
|
+
Layer.effect(service, RpcClient.make(group, options.client)).pipe(
|
|
34
|
+
Layer.provide(
|
|
35
|
+
RpcClient.layerProtocolSocket(options.protocol).pipe(
|
|
36
|
+
Layer.provide(Socket.layerWebSocket(url, options.socket)),
|
|
37
|
+
Layer.provide(
|
|
38
|
+
Layer.succeed(
|
|
39
|
+
RpcSerialization.RpcSerialization,
|
|
40
|
+
options.serialization ?? RpcSerialization.json,
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,7 @@ export * as Rpc from "./Rpc";
|
|
|
30
30
|
export * as RpcDefinition from "./RpcDefinition";
|
|
31
31
|
export * as RpcTracing from "./RpcTracing";
|
|
32
32
|
export * as RpcTargets from "./RpcTargets";
|
|
33
|
+
export * as RpcWebSocketClient from "./RpcWebSocketClient";
|
|
33
34
|
export * as ServiceBinding from "./ServiceBinding";
|
|
34
35
|
export * as Vectorize from "./Vectorize";
|
|
35
36
|
export * as WebTransport from "./WebTransport";
|