effect-cf 0.42.1 → 0.43.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 CHANGED
@@ -1,5 +1,45 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.43.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#174](https://github.com/danieljvdm/effect-cf/pull/174) [`10cd4a9`](https://github.com/danieljvdm/effect-cf/commit/10cd4a9cd162ef38599a53c772cdb19f8e241605) Thanks [@danieljvdm](https://github.com/danieljvdm)! - ## effect-cf
8
+
9
+ Require Effect `^4.0.0-rc.115` and matching `@effect/sql-d1`, `@effect/sql-pg`, and
10
+ `@effect/sql-sqlite-do` peers. Upgrade the Effect family together.
11
+
12
+ Preserve Cloudflare span context with Effect's new fiber representation and close
13
+ request scopes when HEAD or status 204, 205, or 304 omits an Effect streaming body.
14
+ Omitted streams are not started, and request resources and telemetry are finalized.
15
+
16
+ HyperdrivePg now uses Effect's native PostgreSQL driver. Consumers must account for
17
+ its new result codecs (`int8` becomes `bigint`, dates become strings, and timestamps
18
+ become epoch milliseconds), wrap JSON parameters with `sql.json`, and submit one
19
+ statement per query. The driver enables named prepared statements by default;
20
+ pass `prepare: false` for poolers that cannot preserve them.
21
+
22
+ ## effect-webtransport
23
+
24
+ Require Effect `^4.0.0-rc.115` and migrate the Socket adapter to scoped readers and
25
+ writers. Replace `socket.run(handler)` with a scoped acquisition of `socket.reader`
26
+ and a loop over `reader.pull`. Use `writer.write` and `writer.writeAll` after
27
+ acquiring `socket.writer`.
28
+
29
+ Reads now follow consumer demand without dispatching a fiber per chunk. Reader
30
+ scope cleanup still releases stream locks, sends FIN on success, and aborts writes
31
+ on failure. Releasing a writer scope half-closes the write side. Interrupting a
32
+ batch stops unsent frames while leaving the socket available for later writes.
33
+
34
+ Every close, including peer FIN, fails with a `SocketError` containing
35
+ `SocketCloseError`. Remove `closeCodeIsError`, `FromBidirectionalStreamOptions`,
36
+ and `defaultCloseCodeIsError`; handle close reasons in the read loop and use
37
+ `Effect.retry` around the scoped loop to reconnect. WebSocket fallback options now
38
+ accept `highWaterMark`.
39
+
40
+ MessagePack serialization was removed upstream. Use `RpcSerialization.layerNdjson`
41
+ or `RpcSerialization.layerSchemaBinary()` for the byte-stream transport.
42
+
3
43
  ## 0.42.1
4
44
 
5
45
  ### Patch Changes
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Cloudflare entrypoints and bindings as Effect services.
4
4
 
5
5
  ```sh
6
- npm install effect-cf "effect@^4.0.0-rc.112"
6
+ npm install effect-cf "effect@^4.0.0-rc.115"
7
7
  ```
8
8
 
9
9
  The repository tests against workerd `1.20260825.1` and `@cloudflare/workers-types@5.20260825.1`. Use `compatibility_date: "2026-08-25"` in Wrangler.
@@ -235,9 +235,9 @@ const makeClient = (definition, defaults) => (dataset) => {
235
235
  };
236
236
  };
237
237
  const queryConfig = (options) => Config.all({
238
- accountId: options?.accountId ?? Config.string("CLOUDFLARE_ACCOUNT_ID"),
239
- apiToken: options?.apiToken ?? Config.redacted("CLOUDFLARE_API_TOKEN"),
240
- apiBaseUrl: options?.apiBaseUrl ?? Config.string("CLOUDFLARE_API_BASE_URL").pipe(Config.withDefault(defaultQueryApiBaseUrl))
238
+ accountId: options?.accountId ?? Config.String("CLOUDFLARE_ACCOUNT_ID"),
239
+ apiToken: options?.apiToken ?? Config.Redacted("CLOUDFLARE_API_TOKEN"),
240
+ apiBaseUrl: options?.apiBaseUrl ?? Config.String("CLOUDFLARE_API_BASE_URL").pipe(Config.withDefault(defaultQueryApiBaseUrl))
241
241
  });
242
242
  const queryLayer = (tag, definition) => Layer.effect(tag, makeQueryClient(definition));
243
243
  const layerFetch = (tag, definition) => queryLayer(tag, definition).pipe(Layer.provide(FetchHttpClient.layer));
@@ -61,7 +61,7 @@ const layer = Layer.effect(Tracer.Tracer, Effect.sync(() => {
61
61
  return parentContext(() => tracing.startActiveSpan(options.name, (span) => new Span(options, AsyncLocalStorage.snapshot(), span)));
62
62
  },
63
63
  context(primitive, fiber) {
64
- return contextFor(fiber.currentSpan)(() => primitive["~effect/Effect/evaluate"](fiber));
64
+ return contextFor(fiber.cache.span)(() => primitive["~effect/Effect/evaluate"](fiber));
65
65
  }
66
66
  });
67
67
  }));
@@ -15,11 +15,11 @@ var Environment_exports = /* @__PURE__ */ __exportAll({
15
15
  var WorkerEnvironment = class extends Context.Service()("effect-cf/WorkerEnvironment") {};
16
16
  let WorkerConfig;
17
17
  (function(_WorkerConfig) {
18
- _WorkerConfig.string = (name) => Config.string(name);
19
- _WorkerConfig.redacted = (name) => Config.redacted(name);
20
- _WorkerConfig.number = (name) => Config.number(name);
21
- _WorkerConfig.integer = (name) => Config.int(name);
22
- _WorkerConfig.boolean = (name) => Config.boolean(name);
18
+ _WorkerConfig.string = (name) => Config.String(name);
19
+ _WorkerConfig.redacted = (name) => Config.Redacted(name);
20
+ _WorkerConfig.number = (name) => Config.Number(name);
21
+ _WorkerConfig.integer = (name) => Config.Int(name);
22
+ _WorkerConfig.boolean = (name) => Config.Boolean(name);
23
23
  const providerFromEnv = _WorkerConfig.providerFromEnv = (env, options) => {
24
24
  const record = {};
25
25
  for (const [key, value] of Object.entries(env)) if (Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) record[key] = String(value);
package/dist/Worker.mjs CHANGED
@@ -58,8 +58,9 @@ const renderFetchSuccess = (effect, eventLayer) => Effect.suspend(() => {
58
58
  return Effect.succeed(response);
59
59
  });
60
60
  const handleResponse = (request, response) => nativeResponse !== void 0 ? Effect.void : Effect.map(Effect.context(), (context) => {
61
- webResponse = HttpServerResponse.toWeb(HttpEffect.scopeTransferToStream(response), {
62
- withoutBody: request.method === "HEAD",
61
+ const withoutBody = request.method === "HEAD";
62
+ webResponse = HttpServerResponse.toWeb(HttpServerResponse.omitsBody(response, withoutBody) ? response : HttpEffect.scopeTransferToStream(response), {
63
+ withoutBody,
63
64
  context
64
65
  });
65
66
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-cf",
3
- "version": "0.42.1",
3
+ "version": "0.43.0",
4
4
  "description": "Effect-native primitives for Cloudflare Workers and bindings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -94,11 +94,11 @@
94
94
  "@cloudflare/sandbox": "^0.13.0-next.738.2",
95
95
  "@cloudflare/vitest-plugin": "^1.0.0",
96
96
  "@cloudflare/workers-types": "^5.20260825.1",
97
- "@effect/sql-d1": "^4.0.0-rc.112",
98
- "@effect/sql-pg": "^4.0.0-rc.112",
99
- "@effect/sql-sqlite-do": "^4.0.0-rc.112",
97
+ "@effect/sql-d1": "^4.0.0-rc.115",
98
+ "@effect/sql-pg": "^4.0.0-rc.115",
99
+ "@effect/sql-sqlite-do": "^4.0.0-rc.115",
100
100
  "@modelcontextprotocol/server": "^2.0.0",
101
- "effect": "^4.0.0-rc.112"
101
+ "effect": "^4.0.0-rc.115"
102
102
  },
103
103
  "peerDependenciesMeta": {
104
104
  "@cloudflare/computer": {
@@ -729,11 +729,11 @@ export const makeClient =
729
729
 
730
730
  export const queryConfig = (options?: QueryConfigOptions) =>
731
731
  Config.all({
732
- accountId: options?.accountId ?? Config.string("CLOUDFLARE_ACCOUNT_ID"),
733
- apiToken: options?.apiToken ?? Config.redacted("CLOUDFLARE_API_TOKEN"),
732
+ accountId: options?.accountId ?? Config.String("CLOUDFLARE_ACCOUNT_ID"),
733
+ apiToken: options?.apiToken ?? Config.Redacted("CLOUDFLARE_API_TOKEN"),
734
734
  apiBaseUrl:
735
735
  options?.apiBaseUrl ??
736
- Config.string("CLOUDFLARE_API_BASE_URL").pipe(Config.withDefault(defaultQueryApiBaseUrl)),
736
+ Config.String("CLOUDFLARE_API_BASE_URL").pipe(Config.withDefault(defaultQueryApiBaseUrl)),
737
737
  });
738
738
 
739
739
  export const queryLayer = <Self>(
@@ -91,7 +91,7 @@ export const layer: Layer.Layer<never> = Layer.effect(
91
91
  );
92
92
  },
93
93
  context(primitive, fiber) {
94
- return contextFor(fiber.currentSpan)(() => primitive["~effect/Effect/evaluate"](fiber));
94
+ return contextFor(fiber.cache.span)(() => primitive["~effect/Effect/evaluate"](fiber));
95
95
  },
96
96
  });
97
97
  }),
@@ -33,19 +33,19 @@ export namespace WorkerConfig {
33
33
  export type Key = ScalarConfigKey;
34
34
 
35
35
  /** Read a scalar Cloudflare var or secret as a string. */
36
- export const string = <const Name extends Key>(name: Name) => Config.string(name);
36
+ export const string = <const Name extends Key>(name: Name) => Config.String(name);
37
37
 
38
38
  /** Read a scalar Cloudflare secret as a redacted string. */
39
- export const redacted = <const Name extends Key>(name: Name) => Config.redacted(name);
39
+ export const redacted = <const Name extends Key>(name: Name) => Config.Redacted(name);
40
40
 
41
41
  /** Read a scalar Cloudflare var or secret as a number. */
42
- export const number = <const Name extends Key>(name: Name) => Config.number(name);
42
+ export const number = <const Name extends Key>(name: Name) => Config.Number(name);
43
43
 
44
44
  /** Read a scalar Cloudflare var or secret as an integer. */
45
- export const integer = <const Name extends Key>(name: Name) => Config.int(name);
45
+ export const integer = <const Name extends Key>(name: Name) => Config.Int(name);
46
46
 
47
47
  /** Read a scalar Cloudflare var or secret as a boolean. */
48
- export const boolean = <const Name extends Key>(name: Name) => Config.boolean(name);
48
+ export const boolean = <const Name extends Key>(name: Name) => Config.Boolean(name);
49
49
 
50
50
  export interface ProviderOptions {
51
51
  /**
package/src/Worker.ts CHANGED
@@ -277,10 +277,17 @@ const renderFetchSuccess = <E, R, REvent, EventLayerError, EventLayerRequirement
277
277
  nativeResponse !== undefined
278
278
  ? Effect.void
279
279
  : Effect.map(Effect.context<never>(), (context) => {
280
- webResponse = HttpServerResponse.toWeb(HttpEffect.scopeTransferToStream(response), {
281
- withoutBody: request.method === "HEAD",
282
- context,
283
- });
280
+ const withoutBody = request.method === "HEAD";
281
+
282
+ webResponse = HttpServerResponse.toWeb(
283
+ HttpServerResponse.omitsBody(response, withoutBody)
284
+ ? response
285
+ : HttpEffect.scopeTransferToStream(response),
286
+ {
287
+ withoutBody,
288
+ context,
289
+ },
290
+ );
284
291
  });
285
292
  const telemetryMiddleware = HttpMiddleware.make((self) =>
286
293
  RpcTargets.withScope(