effect-cf 0.24.0 → 0.25.1

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,24 @@
1
1
  # effect-cf
2
2
 
3
+ ## 0.25.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#88](https://github.com/danieljvdm/effect-cf/pull/88) [`18941d4`](https://github.com/danieljvdm/effect-cf/commit/18941d418c296c39a36d909516eb22a74778c8e7) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Upgrade to Effect 4 beta.107 and use its explicit environment-record config provider for
8
+ Cloudflare variables.
9
+
10
+ ## 0.25.0
11
+
12
+ ### Minor Changes
13
+
14
+ - [#85](https://github.com/danieljvdm/effect-cf/pull/85) [`2c46f9d`](https://github.com/danieljvdm/effect-cf/commit/2c46f9d64774edb7994e3879313077e2a203777a) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Remove compatibility APIs. Use `WorkerConfig.providerLayer`,
15
+ `AnalyticsEngine.writeDataPoint` or `writeDataPoints`, and
16
+ `DurableObjectAlarm.processDue`; Email sends now require structured builder messages.
17
+
18
+ ### Patch Changes
19
+
20
+ - [#87](https://github.com/danieljvdm/effect-cf/pull/87) [`6d6fdfa`](https://github.com/danieljvdm/effect-cf/commit/6d6fdfaaf4812bb46c49b49aad3fd2ca50af76f6) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Allow compatible Effect releases in package development dependency ranges.
21
+
3
22
  ## 0.24.0
4
23
 
5
24
  ### Minor Changes
package/README.md CHANGED
@@ -4,18 +4,18 @@ Effect-native Cloudflare primitives for Workers, Durable Objects, Containers, bi
4
4
 
5
5
  ## Install
6
6
 
7
- `effect-cf` currently targets Effect 4 beta.
7
+ `effect-cf` targets Effect `^4.0.0-beta.105`.
8
8
 
9
9
  ```bash
10
- bun add effect-cf "effect@^4.0.0-beta.65"
10
+ bun add effect-cf "effect@^4.0.0-beta.105"
11
11
  ```
12
12
 
13
13
  ```bash
14
- pnpm add effect-cf "effect@^4.0.0-beta.65"
14
+ pnpm add effect-cf "effect@^4.0.0-beta.105"
15
15
  ```
16
16
 
17
17
  ```bash
18
- npm install effect-cf "effect@^4.0.0-beta.65"
18
+ npm install effect-cf "effect@^4.0.0-beta.105"
19
19
  ```
20
20
 
21
21
  ## Goal
@@ -491,7 +491,7 @@ Onboard the sending domain under **Compute > Email Service > Email Sending**, th
491
491
  }
492
492
  ```
493
493
 
494
- Messages that violate a documented limit fail with `EmailValidationError` carrying every violation, without calling the binding. Use `layer({ binding, send: { validate: false } })` to skip validation and let Cloudflare reject the message instead. Raw RFC 5322 `EmailMessage` values are always passed through untouched, so the legacy `cloudflare:email` API keeps working.
494
+ Messages that violate a documented limit fail with `EmailValidationError` carrying every violation, without calling the binding. Use `layer({ binding, send: { validate: false } })` to skip validation and let Cloudflare reject the message instead.
495
495
 
496
496
  Cloudflare's total message size limit is exposed as `Email.sendLimits.maxMessageBytes` but is not enforced, because the encoded MIME size is only known once Cloudflare composes the message. Oversized messages fail at the binding with `E_CONTENT_TOO_LARGE`.
497
497
 
@@ -511,7 +511,6 @@ export const RequestAnalyticsLayer = RequestAnalytics.layer({
511
511
  binding: "REQUEST_ANALYTICS",
512
512
  write: {
513
513
  onInvalid: "error",
514
- batchSize: 100,
515
514
  },
516
515
  });
517
516
 
@@ -526,7 +525,7 @@ export const recordPageView = (request: Request) =>
526
525
  doubles: [1],
527
526
  });
528
527
 
529
- yield* analytics.writeBatch(
528
+ yield* analytics.writeDataPoints(
530
529
  [
531
530
  {
532
531
  indexes: [url.hostname],
@@ -29,7 +29,7 @@ type ScalarConfigKey = Extract<{ readonly [Key in keyof Cloudflare.Env]-?: NonNu
29
29
  * const program = Effect.gen(function* () {
30
30
  * const config = yield* AppConfig;
31
31
  * // ...
32
- * }).pipe(Effect.provide(WorkerConfig.layer));
32
+ * }).pipe(Effect.provide(WorkerConfig.providerLayer));
33
33
  * ```
34
34
  *
35
35
  * Keys are constrained to scalar `Cloudflare.Env` properties (`string`,
@@ -59,13 +59,16 @@ declare namespace WorkerConfig {
59
59
  * Keep empty-string env values as explicit `""` config values.
60
60
  *
61
61
  * By default (absent or `false`), empty strings are treated as missing
62
- * config, matching `ConfigProvider.fromUnknown`.
62
+ * config, matching `ConfigProvider.fromEnvRecord`.
63
63
  */
64
64
  readonly preserveEmptyStrings?: boolean;
65
65
  }
66
66
  /**
67
67
  * Build a `ConfigProvider` from a Cloudflare worker `env` object.
68
68
  *
69
+ * Scalar vars and secrets use Effect's environment-record semantics;
70
+ * Cloudflare binding objects are ignored.
71
+ *
69
72
  * Empty-string values are treated as missing config by default; pass
70
73
  * `{ preserveEmptyStrings: true }` to keep them as explicit values.
71
74
  */
@@ -82,8 +85,6 @@ declare namespace WorkerConfig {
82
85
  * Cloudflare worker `env` object.
83
86
  */
84
87
  const providerLayer: Layer.Layer<never, never, WorkerEnvironment>;
85
- /** Alias for `providerLayer` for concise use in worker layers. */
86
- const layer: Layer.Layer<never, never, WorkerEnvironment>;
87
88
  /** Build a `ConfigProvider` layer with a custom `env` conversion function. */
88
89
  const layerWith: (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) => Layer.Layer<never, never, WorkerEnvironment>;
89
90
  }
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { Cause, Config, ConfigProvider, Context, Data, Effect, Exit, Option, Result, Schema } from "effect";
2
+ import { Cause, Config, ConfigProvider, Context, Data, Effect, Exit, Option, Predicate, Result, Schema } from "effect";
3
3
  //#region src/Environment.ts
4
4
  var Environment_exports = /* @__PURE__ */ __exportAll({
5
5
  WorkerConfig: () => WorkerConfig,
@@ -16,10 +16,14 @@ let WorkerConfig;
16
16
  _WorkerConfig.number = (name) => Config.number(name);
17
17
  _WorkerConfig.integer = (name) => Config.int(name);
18
18
  _WorkerConfig.boolean = (name) => Config.boolean(name);
19
- const providerFromEnv = _WorkerConfig.providerFromEnv = (env, options) => ConfigProvider.fromUnknown(env, options);
19
+ const providerFromEnv = _WorkerConfig.providerFromEnv = (env, options) => {
20
+ const record = {};
21
+ for (const [key, value] of Object.entries(env)) if (Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) record[key] = String(value);
22
+ return ConfigProvider.fromEnvRecord(record, options);
23
+ };
20
24
  const providerWith = _WorkerConfig.providerWith = (makeProvider) => Effect.map(WorkerEnvironment, makeProvider);
21
25
  const provider = _WorkerConfig.provider = providerWith(providerFromEnv);
22
- _WorkerConfig.layer = _WorkerConfig.providerLayer = ConfigProvider.layer(provider);
26
+ _WorkerConfig.providerLayer = ConfigProvider.layer(provider);
23
27
  _WorkerConfig.layerWith = (makeProvider) => ConfigProvider.layer(providerWith(makeProvider));
24
28
  })(WorkerConfig || (WorkerConfig = {}));
25
29
  //#endregion
@@ -1,4 +1,4 @@
1
- import { c as WorkerEnvironment, r as BindingValidationError, t as BindingNotFoundError } from "./Binding-Bq_FPzlt.mjs";
1
+ import { c as WorkerEnvironment, r as BindingValidationError, t as BindingNotFoundError } from "./Binding-COCtoJXa.mjs";
2
2
  import { Context, Effect, Layer } from "effect";
3
3
  declare namespace Hyperdrive_d_exports {
4
4
  export { HyperdriveClient, HyperdriveDefinition, HyperdriveService, LayerOptions, Tag, TagClass, isHyperdrive, layer, make, makeClient };
@@ -1,5 +1,5 @@
1
- import { c as WorkerEnvironment, r as BindingValidationError, t as BindingNotFoundError } from "./Binding-Bq_FPzlt.mjs";
2
- import { n as LayerOptions, r as TagClass } from "./Hyperdrive-pEMnh3W6.mjs";
1
+ import { c as WorkerEnvironment, r as BindingValidationError, t as BindingNotFoundError } from "./Binding-COCtoJXa.mjs";
2
+ import { n as LayerOptions, r as TagClass } from "./Hyperdrive-6p59yyVk.mjs";
3
3
  import { Layer } from "effect";
4
4
  import { PgClient } from "@effect/sql-pg";
5
5
  import { SqlClient, SqlError } from "effect/unstable/sql";
package/dist/Vitest.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as WorkerEnvironment, s as WorkerEnv } from "./Binding-Bq_FPzlt.mjs";
2
- import { c as DurableObjectState, l as DurableObjectStateService, o as WorkflowInstanceStatusName } from "./WorkflowBinding-BECGNTzu.mjs";
1
+ import { c as WorkerEnvironment, s as WorkerEnv } from "./Binding-COCtoJXa.mjs";
2
+ import { c as DurableObjectState, l as DurableObjectStateService, o as WorkflowInstanceStatusName } from "./WorkflowBinding-Bzs5kmoE.mjs";
3
3
  import { Effect, Layer } from "effect";
4
4
  import { DurableObject } from "cloudflare:workers";
5
5
  import { adminSecretsStore as adminSecretsStore$1, getQueueResult, introspectWorkflow as introspectWorkflow$1, introspectWorkflowInstance as introspectWorkflowInstance$1 } from "cloudflare:test";
package/dist/Vitest.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { d as WorkerConfig, f as WorkerEnvironment, r as fromDurableObjectState, t as DurableObjectState } from "./DurableObjectState-8sgBJB0s.mjs";
1
+ import { d as WorkerConfig, f as WorkerEnvironment, r as fromDurableObjectState, t as DurableObjectState } from "./DurableObjectState-CEh0DVw7.mjs";
2
2
  import { ConfigProvider, Effect, Exit, Layer } from "effect";
3
3
  import { env } from "cloudflare:workers";
4
4
  import { abortAllDurableObjects as abortAllDurableObjects$1, adminSecretsStore as adminSecretsStore$1, applyD1Migrations as applyD1Migrations$1, createExecutionContext, createMessageBatch, createPagesEventContext, createScheduledController, evictAllDurableObjects as evictAllDurableObjects$1, evictDurableObject as evictDurableObject$1, getQueueResult, introspectWorkflow as introspectWorkflow$1, introspectWorkflowInstance as introspectWorkflowInstance$1, listDurableObjectIds as listDurableObjectIds$1, reset as reset$1, runDurableObjectAlarm as runDurableObjectAlarm$1, runInDurableObject as runInDurableObject$1, waitOnExecutionContext } from "cloudflare:test";
@@ -1,4 +1,4 @@
1
- import { c as WorkerEnvironment, n as BindingService, r as BindingValidationError, s as WorkerEnv, t as BindingNotFoundError } from "./Binding-Bq_FPzlt.mjs";
1
+ import { c as WorkerEnvironment, n as BindingService, r as BindingValidationError, s as WorkerEnv, t as BindingNotFoundError } from "./Binding-COCtoJXa.mjs";
2
2
  import { Cause, Context, Effect, Layer, Option, Schema, Scope } from "effect";
3
3
  import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
4
4
  import { RpcStub, RpcTarget, WorkerEntrypoint } from "cloudflare:workers";
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { a as Environment_d_exports, c as WorkerEnvironment, i as Binding_d_exports, n as BindingService, o as WorkerConfig, r as BindingValidationError, s as WorkerEnv, t as BindingNotFoundError } from "./Binding-Bq_FPzlt.mjs";
2
- import { t as Hyperdrive_d_exports } from "./Hyperdrive-pEMnh3W6.mjs";
3
- import { A as Rpc_d_exports, C as QueueBinding_d_exports, D as RpcDefinition_d_exports, E as ReservedMethodName$2, M as DurableWebSocket, N as DurableObjectStorage_d_exports, O as ServiceFreeSchema$1, P as StorageOperationError, S as QueueDefinition_d_exports, T as Method$2, _ as AsyncMethodArgs, a as WorkflowInstanceCreateOptions, b as AsyncMethodSuccess, c as DurableObjectState, d as ExecutionContext, f as NativeRequest, g as ServiceBinding_d_exports, h as WorkerDefinition_d_exports, i as WorkflowInstanceCreateBatchOptions, j as DurableObjectWebSocket_d_exports, k as Provider, m as Worker_d_exports, n as WorkflowBinding_d_exports, p as WorkerContext, r as WorkflowInstance, s as WorkflowOperationError, t as WorkflowBindingClient, u as DurableObjectState_d_exports, v as AsyncMethodCloudflareReturn, w as Definition$4, x as Queue_d_exports, y as AsyncMethodKey } from "./WorkflowBinding-BECGNTzu.mjs";
1
+ import { a as Environment_d_exports, c as WorkerEnvironment, i as Binding_d_exports, n as BindingService, o as WorkerConfig, r as BindingValidationError, s as WorkerEnv, t as BindingNotFoundError } from "./Binding-COCtoJXa.mjs";
2
+ import { t as Hyperdrive_d_exports } from "./Hyperdrive-6p59yyVk.mjs";
3
+ import { A as Rpc_d_exports, C as QueueBinding_d_exports, D as RpcDefinition_d_exports, E as ReservedMethodName$2, M as DurableWebSocket, N as DurableObjectStorage_d_exports, O as ServiceFreeSchema$1, P as StorageOperationError, S as QueueDefinition_d_exports, T as Method$2, _ as AsyncMethodArgs, a as WorkflowInstanceCreateOptions, b as AsyncMethodSuccess, c as DurableObjectState, d as ExecutionContext, f as NativeRequest, g as ServiceBinding_d_exports, h as WorkerDefinition_d_exports, i as WorkflowInstanceCreateBatchOptions, j as DurableObjectWebSocket_d_exports, k as Provider, m as Worker_d_exports, n as WorkflowBinding_d_exports, p as WorkerContext, r as WorkflowInstance, s as WorkflowOperationError, t as WorkflowBindingClient, u as DurableObjectState_d_exports, v as AsyncMethodCloudflareReturn, w as Definition$4, x as Queue_d_exports, y as AsyncMethodKey } from "./WorkflowBinding-Bzs5kmoE.mjs";
4
4
  import { Config, Context, Data, DateTime, Duration, Effect, Layer, Option, Redacted, Schema, Scope } from "effect";
5
5
  import { Headers as Headers$1, HttpClient, HttpClientResponse } from "effect/unstable/http";
6
6
  import { OtlpExporter } from "effect/unstable/observability";
@@ -173,7 +173,7 @@ declare const layer$13: <Self>(tag: Context.Service<Self, AiGatewayClient>, defi
173
173
  declare const make$12: <Id extends string>(id: Id) => TagClass$11<AiGatewayService<Id>, Id>;
174
174
  declare const Tag$12: <Self>() => <Id extends string>(id: Id) => TagClass$11<Self, Id>;
175
175
  declare namespace AnalyticsEngine_d_exports {
176
- export { AnalyticsEngineBinding, AnalyticsEngineClient, AnalyticsEngineDataPoint, AnalyticsEngineDataPointSchema, AnalyticsEngineDefinition, AnalyticsEngineFieldValue, AnalyticsEngineFieldValueSchema, AnalyticsEngineInvalidWritePolicy, AnalyticsEngineOperationError, AnalyticsEngineQueryClient, AnalyticsEngineQueryColumn, AnalyticsEngineQueryDefinition, AnalyticsEngineQueryError, AnalyticsEngineQueryOptions, AnalyticsEngineQueryResult, AnalyticsEngineQueryRow, AnalyticsEngineQueryService, AnalyticsEngineService, AnalyticsEngineWriteBatchOptions, AnalyticsEngineWriteError, AnalyticsEngineWriteOptions, AnalyticsEngineWritePolicy, AnalyticsEngineWriteValidationError, AnalyticsEngineWriteViolation, LayerOptions$12 as LayerOptions, QueryConfigOptions, QueryTag, QueryTagClass, Tag$11 as Tag, TagClass$10 as TagClass, isAnalyticsEngineDataset, layer$12 as layer, layerFetch, layerFetchConfig, make$11 as make, makeClient$9 as makeClient, makeQuery, makeQueryClient, queryConfig, queryLayer, queryLayerConfig, writeLimits };
176
+ export { AnalyticsEngineBinding, AnalyticsEngineClient, AnalyticsEngineDataPoint, AnalyticsEngineDataPointSchema, AnalyticsEngineDefinition, AnalyticsEngineFieldValue, AnalyticsEngineFieldValueSchema, AnalyticsEngineInvalidWritePolicy, AnalyticsEngineOperationError, AnalyticsEngineQueryClient, AnalyticsEngineQueryColumn, AnalyticsEngineQueryDefinition, AnalyticsEngineQueryError, AnalyticsEngineQueryOptions, AnalyticsEngineQueryResult, AnalyticsEngineQueryRow, AnalyticsEngineQueryService, AnalyticsEngineService, AnalyticsEngineWriteError, AnalyticsEngineWriteOptions, AnalyticsEngineWritePolicy, AnalyticsEngineWriteValidationError, AnalyticsEngineWriteViolation, LayerOptions$12 as LayerOptions, QueryConfigOptions, QueryTag, QueryTagClass, Tag$11 as Tag, TagClass$10 as TagClass, isAnalyticsEngineDataset, layer$12 as layer, layerFetch, layerFetchConfig, make$11 as make, makeClient$9 as makeClient, makeQuery, makeQueryClient, queryConfig, queryLayer, queryLayerConfig, writeLimits };
177
177
  }
178
178
  declare const writeLimits: {
179
179
  readonly maxBlobs: 20;
@@ -254,14 +254,7 @@ interface AnalyticsEngineWriteOptions {
254
254
  */
255
255
  readonly onInvalid?: AnalyticsEngineInvalidWritePolicy;
256
256
  }
257
- interface AnalyticsEngineWriteBatchOptions extends AnalyticsEngineWriteOptions {
258
- /**
259
- * Maximum points per native batch call. Values are clamped to Cloudflare's
260
- * per-invocation limit.
261
- */
262
- readonly batchSize?: number;
263
- }
264
- type AnalyticsEngineWritePolicy = AnalyticsEngineWriteBatchOptions;
257
+ type AnalyticsEngineWritePolicy = AnalyticsEngineWriteOptions;
265
258
  interface AnalyticsEngineQueryResult<Row = AnalyticsEngineQueryRow> {
266
259
  readonly meta: ReadonlyArray<AnalyticsEngineQueryColumn>;
267
260
  readonly data: ReadonlyArray<Row>;
@@ -281,9 +274,7 @@ interface AnalyticsEngineQueryOptions {
281
274
  }
282
275
  interface AnalyticsEngineClient {
283
276
  readonly writeDataPoint: (dataPoint?: AnalyticsEngineDataPoint, options?: AnalyticsEngineWriteOptions) => Effect.Effect<void, AnalyticsEngineWriteError>;
284
- readonly write: (dataPoint?: AnalyticsEngineDataPoint, options?: AnalyticsEngineWriteOptions) => Effect.Effect<void, AnalyticsEngineWriteError>;
285
- readonly writeDataPoints: (dataPoints: ReadonlyArray<AnalyticsEngineDataPoint>, options?: AnalyticsEngineWriteBatchOptions) => Effect.Effect<void, AnalyticsEngineWriteError>;
286
- readonly writeBatch: (dataPoints: ReadonlyArray<AnalyticsEngineDataPoint>, options?: AnalyticsEngineWriteBatchOptions) => Effect.Effect<void, AnalyticsEngineWriteError>;
277
+ readonly writeDataPoints: (dataPoints: ReadonlyArray<AnalyticsEngineDataPoint>, options?: AnalyticsEngineWriteOptions) => Effect.Effect<void, AnalyticsEngineWriteError>;
287
278
  readonly rawUnsafe: Effect.Effect<AnalyticsEngineBinding>;
288
279
  readonly definition: AnalyticsEngineDefinition;
289
280
  }
@@ -1134,7 +1125,7 @@ type DurableObjectClass<Rpc extends DurableObjectRpc<ROut>, ROut> = new (state:
1134
1125
  */
1135
1126
  declare const make$6: <ROut, LayerError, REvent = never, EventLayerError = never, const Rpc extends DurableObjectRpc<ROut | REvent> = Record<never, never>>(layer: Layer.Layer<ROut, LayerError, DurableObjectState | WorkerEnvironment>, options?: DurableObjectOptions<ROut, REvent, EventLayerError, Rpc>) => DurableObjectClass<Rpc, ROut | REvent>;
1136
1127
  declare namespace DurableObjectAlarm_d_exports {
1137
- export { AlarmDefinitionConfig, AlarmDefinitionEntry, AlarmDefinitionPayload, AlarmDefinitions, AlarmFailurePolicy, AlarmPayload, AlarmPayloadSchema, AlarmRef, AlarmRetryPolicy, AlarmScheduler, DefinedAlarmEvent, DefinedAlarmHandlers, DurableObjectAlarm, DurableObjectAlarmError, DurableObjectAlarmEvent, InvalidAlarmPayloadError, InvalidAlarmRefError, InvalidProcessDueAlarmsOptionsError, InvalidRepeatEveryError, ProcessDueAlarmsFailure, ProcessDueAlarmsFailureAction, ProcessDueAlarmsHandler, ProcessDueAlarmsMode, ProcessDueAlarmsOptions, ProcessDueAlarmsResult, ScheduleAlarmInput, StoredAlarmDecodeError, define, process, processDue };
1128
+ export { AlarmDefinitionConfig, AlarmDefinitionEntry, AlarmDefinitionPayload, AlarmDefinitions, AlarmFailurePolicy, AlarmPayload, AlarmPayloadSchema, AlarmRef, AlarmRetryPolicy, AlarmScheduler, DefinedAlarmEvent, DefinedAlarmHandlers, DurableObjectAlarm, DurableObjectAlarmError, DurableObjectAlarmEvent, InvalidAlarmPayloadError, InvalidAlarmRefError, InvalidProcessDueAlarmsOptionsError, InvalidRepeatEveryError, ProcessDueAlarmsFailure, ProcessDueAlarmsFailureAction, ProcessDueAlarmsHandler, ProcessDueAlarmsMode, ProcessDueAlarmsOptions, ProcessDueAlarmsResult, ScheduleAlarmInput, StoredAlarmDecodeError, define, processDue };
1138
1129
  }
1139
1130
  /**
1140
1131
  * JSON-serializable alarm payload stored with each scheduled alarm.
@@ -1396,7 +1387,6 @@ type AlarmScheduler = {
1396
1387
  readonly scheduleAlarm: (input: ScheduleAlarmInput) => Effect.Effect<void, InvalidAlarmPayloadError | InvalidAlarmRefError | InvalidRepeatEveryError | StorageOperationError>;
1397
1388
  };
1398
1389
  declare const processDue: <R = never, E = never, OnFailureR = never, OnFailureE = never>(handle: ProcessDueAlarmsHandler<R, E>, options?: ProcessDueAlarmsOptions<OnFailureR, OnFailureE>) => Effect.Effect<ProcessDueAlarmsResult, E | OnFailureE | DurableObjectAlarmError, OnFailureR | R | DurableObjectAlarm>;
1399
- declare const process: typeof processDue;
1400
1390
  type AlarmPayloadSchema = Schema.Codec<any, any, never, never>;
1401
1391
  type AlarmFailurePolicy = "ordered" | "retry" | "skip-and-advance-repeat";
1402
1392
  interface AlarmRetryPolicy {
@@ -1517,7 +1507,7 @@ type SqliteLayerOptions = Omit<SqliteClient.SqliteClientConfig, "db" | "storage"
1517
1507
  */
1518
1508
  declare const layer$5: (options?: SqliteLayerOptions) => Layer.Layer<SqliteClient.SqliteClient | SqlClient.SqlClient, never, DurableObjectState>;
1519
1509
  declare namespace Email_d_exports {
1520
- export { EmailAddress, EmailAttachment, EmailBinding, EmailClient, EmailDefinition, EmailErrorCode, EmailMessage, EmailMessageBuilder, EmailOperationError, EmailRecipients, EmailSendError, EmailSendInput, EmailSendOptions, EmailSendResult, EmailService, EmailValidationError, EmailViolation, LayerOptions$6 as LayerOptions, Tag$7 as Tag, TagClass$6 as TagClass, emailErrorCodes, isEmailBinding, isEmailMessageBuilder, layer$4 as layer, make$5 as make, makeClient$4 as makeClient, sendLimits, validateMessage };
1510
+ export { EmailAddress, EmailAttachment, EmailBinding, EmailClient, EmailDefinition, EmailErrorCode, EmailMessageBuilder, EmailOperationError, EmailRecipients, EmailSendError, EmailSendOptions, EmailSendResult, EmailService, EmailValidationError, EmailViolation, LayerOptions$6 as LayerOptions, Tag$7 as Tag, TagClass$6 as TagClass, emailErrorCodes, isEmailBinding, layer$4 as layer, make$5 as make, makeClient$4 as makeClient, sendLimits, validateMessage };
1521
1511
  }
1522
1512
  /** Documented Cloudflare Email Sending limits. */
1523
1513
  declare const sendLimits: {
@@ -1587,9 +1577,7 @@ interface EmailDefinition {
1587
1577
  }
1588
1578
  type EmailAddress = CloudflareEmailAddress;
1589
1579
  type EmailAttachment = CloudflareEmailAttachment;
1590
- type EmailMessage = CloudflareEmailMessage;
1591
1580
  type EmailMessageBuilder = Parameters<CloudflareSendEmail["send"]>[0];
1592
- type EmailSendInput = EmailMessage | EmailMessageBuilder;
1593
1581
  type EmailSendResult = CloudflareEmailSendResult;
1594
1582
  type EmailBinding = CloudflareSendEmail;
1595
1583
  type EmailRecipients = string | EmailAddress | ReadonlyArray<string | EmailAddress>;
@@ -1599,10 +1587,7 @@ interface EmailSendOptions {
1599
1587
  readonly validate?: boolean;
1600
1588
  }
1601
1589
  interface EmailClient {
1602
- readonly send: {
1603
- (message: EmailMessage): Effect.Effect<EmailSendResult, EmailSendError>;
1604
- (builder: EmailMessageBuilder): Effect.Effect<EmailSendResult, EmailSendError>;
1605
- };
1590
+ readonly send: (message: EmailMessageBuilder) => Effect.Effect<EmailSendResult, EmailSendError>;
1606
1591
  readonly rawUnsafe: Effect.Effect<EmailBinding>;
1607
1592
  readonly definition: EmailDefinition;
1608
1593
  }
@@ -1621,11 +1606,6 @@ interface TagClass$6<Self, Id extends string> extends Context.ServiceClass<Self,
1621
1606
  readonly id: Id;
1622
1607
  readonly layer: (options: LayerOptions$6) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
1623
1608
  }
1624
- /**
1625
- * Distinguishes structured Email Sending messages from raw MIME `EmailMessage`
1626
- * values, which only carry envelope `from` and `to` addresses.
1627
- */
1628
- declare const isEmailMessageBuilder: (message: EmailSendInput) => message is EmailMessageBuilder;
1629
1609
  /**
1630
1610
  * Validates a structured message against documented Cloudflare Email Sending
1631
1611
  * limits and returns every violation found. Total message size is not checked
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { a as DurableObjectWebSocket_exports, c as causeMessage, d as WorkerConfig, f as WorkerEnvironment, i as fromExecutionContext, l as violationsMessage, n as DurableObjectState_exports, o as fromWebSocket, r as fromDurableObjectState, s as DurableObjectStorage_exports, t as DurableObjectState, u as Environment_exports } from "./DurableObjectState-8sgBJB0s.mjs";
2
+ import { a as DurableObjectWebSocket_exports, c as causeMessage, d as WorkerConfig, f as WorkerEnvironment, i as fromExecutionContext, l as violationsMessage, n as DurableObjectState_exports, o as fromWebSocket, r as fromDurableObjectState, s as DurableObjectStorage_exports, t as DurableObjectState, u as Environment_exports } from "./DurableObjectState-CEh0DVw7.mjs";
3
3
  import { Clock, Config, ConfigProvider, Context, Data, DateTime, Duration, Effect, Exit, Function as Function$1, Layer, ManagedRuntime, Option, Predicate, Queue, Result, Schema } from "effect";
4
4
  import { FetchHttpClient, HttpClient, HttpClientRequest, HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
5
5
  import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/unstable/observability";
@@ -392,14 +392,7 @@ const tryAnalyticsEngineSync = (binding, operation, evaluate) => Effect.try({
392
392
  try: evaluate,
393
393
  catch: (cause) => analyticsEngineError(binding, operation, cause)
394
394
  });
395
- const normalizeBatchSize = (batchSize) => {
396
- if (batchSize === void 0 || !Number.isFinite(batchSize) || batchSize <= 0) return writeLimits.maxDataPointsPerInvocation;
397
- return Math.min(Math.floor(batchSize), writeLimits.maxDataPointsPerInvocation);
398
- };
399
- const resolveWritePolicy = (defaults, options) => ({
400
- onInvalid: options?.onInvalid ?? defaults?.onInvalid ?? "error",
401
- batchSize: normalizeBatchSize(options?.batchSize ?? defaults?.batchSize)
402
- });
395
+ const resolveWritePolicy = (defaults, options) => ({ onInvalid: options?.onInvalid ?? defaults?.onInvalid ?? "error" });
403
396
  const fieldPath = (prefix, field) => prefix === "" ? field : `${prefix}.${field}`;
404
397
  const indexedPath = (prefix, field, index) => `${fieldPath(prefix, field)}[${index}]`;
405
398
  const byteLength$1 = (value) => {
@@ -457,13 +450,8 @@ const validateDataPoints = (binding, operation, dataPoints, policy) => Effect.ge
457
450
  if (violations.length > 0) return yield* Effect.fail(analyticsEngineWriteValidationError(binding, operation, violations));
458
451
  return validPoints;
459
452
  });
460
- const writeChunks = (binding, operation, dataset, dataPoints, batchSize) => Effect.gen(function* () {
461
- const writeDataPoints = Reflect.get(dataset, "writeDataPoints");
462
- for (let index = 0; index < dataPoints.length; index += batchSize) {
463
- const chunk = dataPoints.slice(index, index + batchSize);
464
- if (typeof writeDataPoints === "function") yield* tryAnalyticsEngineSync(binding, operation, () => writeDataPoints.call(dataset, chunk));
465
- else for (const point of chunk) yield* tryAnalyticsEngineSync(binding, operation, () => dataset.writeDataPoint(point));
466
- }
453
+ const writeChunks = (binding, operation, dataset, dataPoints) => Effect.gen(function* () {
454
+ for (const point of dataPoints) yield* tryAnalyticsEngineSync(binding, operation, () => dataset.writeDataPoint(point));
467
455
  });
468
456
  const queryApiUrl = (definition) => {
469
457
  const baseUrl = new URL(definition.apiBaseUrl ?? defaultQueryApiBaseUrl);
@@ -520,24 +508,20 @@ const isAnalyticsEngineDataset = (value) => {
520
508
  return typeof Reflect.get(value, "writeDataPoint") === "function";
521
509
  };
522
510
  const makeClient$13 = (definition, defaults) => (dataset) => {
523
- const writeDataPoint = Effect.fn("AnalyticsEngine.writeDataPoint")((dataPoint, options) => {
524
- const policy = resolveWritePolicy(defaults, options);
525
- return validateOptionalDataPoint(definition.binding, "writeDataPoint", dataPoint).pipe(Effect.matchEffect({
526
- onFailure: (error) => policy.onInvalid === "drop" ? Effect.succeed(void 0) : Effect.fail(error),
527
- onSuccess: (point) => tryAnalyticsEngineSync(definition.binding, "writeDataPoint", () => dataset.writeDataPoint(point))
528
- }));
529
- });
530
- const writeDataPoints = Effect.fn("AnalyticsEngine.writeDataPoints")(function* (dataPoints, options) {
531
- const policy = resolveWritePolicy(defaults, options);
532
- const validPoints = yield* validateDataPoints(definition.binding, "writeDataPoints", dataPoints, policy);
533
- yield* writeChunks(definition.binding, "writeDataPoints", dataset, validPoints, policy.batchSize);
534
- });
535
511
  return {
536
512
  definition,
537
- writeDataPoint,
538
- write: writeDataPoint,
539
- writeDataPoints,
540
- writeBatch: writeDataPoints,
513
+ writeDataPoint: Effect.fn("AnalyticsEngine.writeDataPoint")((dataPoint, options) => {
514
+ const policy = resolveWritePolicy(defaults, options);
515
+ return validateOptionalDataPoint(definition.binding, "writeDataPoint", dataPoint).pipe(Effect.matchEffect({
516
+ onFailure: (error) => policy.onInvalid === "drop" ? Effect.succeed(void 0) : Effect.fail(error),
517
+ onSuccess: (point) => tryAnalyticsEngineSync(definition.binding, "writeDataPoint", () => dataset.writeDataPoint(point))
518
+ }));
519
+ }),
520
+ writeDataPoints: Effect.fn("AnalyticsEngine.writeDataPoints")(function* (dataPoints, options) {
521
+ const policy = resolveWritePolicy(defaults, options);
522
+ const validPoints = yield* validateDataPoints(definition.binding, "writeDataPoints", dataPoints, policy);
523
+ yield* writeChunks(definition.binding, "writeDataPoints", dataset, validPoints);
524
+ }),
541
525
  rawUnsafe: Effect.succeed(dataset)
542
526
  };
543
527
  };
@@ -2150,7 +2134,6 @@ var DurableObjectAlarm_exports = /* @__PURE__ */ __exportAll({
2150
2134
  InvalidRepeatEveryError: () => InvalidRepeatEveryError,
2151
2135
  StoredAlarmDecodeError: () => StoredAlarmDecodeError,
2152
2136
  define: () => define,
2153
- process: () => process,
2154
2137
  processDue: () => processDue
2155
2138
  });
2156
2139
  const INIT_TABLE_SQL = `
@@ -2275,7 +2258,6 @@ const getFailureActionRetryDelay = (action) => typeof action === "string" || act
2275
2258
  const processDue = (handle, options = {}) => Effect.gen(function* () {
2276
2259
  return yield* (yield* DurableObjectAlarm).processDueAlarms(handle, options);
2277
2260
  });
2278
- const process = processDue;
2279
2261
  const isAlarmDefinitionConfig = (definition) => typeof definition === "object" && definition !== null && "payload" in definition;
2280
2262
  const getAlarmDefinitionSchema = (definition) => isAlarmDefinitionConfig(definition) ? definition.payload : definition;
2281
2263
  const getAlarmDefinitionFailureAction = (definition) => {
@@ -2638,7 +2620,6 @@ var Email_exports = /* @__PURE__ */ __exportAll({
2638
2620
  Tag: () => Tag$7,
2639
2621
  emailErrorCodes: () => emailErrorCodes,
2640
2622
  isEmailBinding: () => isEmailBinding,
2641
- isEmailMessageBuilder: () => isEmailMessageBuilder,
2642
2623
  layer: () => layer$6,
2643
2624
  make: () => make$6,
2644
2625
  makeClient: () => makeClient$6,
@@ -2729,24 +2710,6 @@ const tryEmailPromise = (binding, operation, evaluate) => Effect.tryPromise({
2729
2710
  try: evaluate,
2730
2711
  catch: (cause) => emailError(binding, operation, cause)
2731
2712
  });
2732
- const builderFields = [
2733
- "subject",
2734
- "html",
2735
- "text",
2736
- "cc",
2737
- "bcc",
2738
- "replyTo",
2739
- "attachments",
2740
- "headers"
2741
- ];
2742
- /**
2743
- * Distinguishes structured Email Sending messages from raw MIME `EmailMessage`
2744
- * values, which only carry envelope `from` and `to` addresses.
2745
- */
2746
- const isEmailMessageBuilder = (message) => {
2747
- if (typeof message !== "object" || message === null) return false;
2748
- return builderFields.some((field) => Reflect.get(message, field) !== void 0);
2749
- };
2750
2713
  const byteLength = (value) => textEncoder.encode(value).byteLength;
2751
2714
  const limitViolation = (path, label, actual, limit) => ({
2752
2715
  path,
@@ -2881,7 +2844,7 @@ const makeClient$6 = (definition, options) => (email) => {
2881
2844
  return {
2882
2845
  definition,
2883
2846
  send: Effect.fn("Email.send")(function* (message) {
2884
- if (validate && isEmailMessageBuilder(message)) yield* validateSendInput(definition.binding, "send", message);
2847
+ if (validate) yield* validateSendInput(definition.binding, "send", message);
2885
2848
  return yield* tryEmailPromise(definition.binding, "send", () => runtime.send(message));
2886
2849
  }),
2887
2850
  rawUnsafe: Effect.succeed(email)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-cf",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
4
4
  "description": "Effect-native primitives for Cloudflare Workers and bindings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -48,7 +48,7 @@
48
48
  "@cloudflare/containers": "catalog:",
49
49
  "@cloudflare/vitest-pool-workers": "catalog:",
50
50
  "@cloudflare/workers-types": "catalog:",
51
- "@effect/platform-node": "4.0.0-beta.105",
51
+ "@effect/platform-node": "^4.0.0-beta.107 <4.0.0-rc.0",
52
52
  "@effect/sql-d1": "catalog:",
53
53
  "@effect/sql-pg": "catalog:",
54
54
  "@effect/sql-sqlite-do": "catalog:",
@@ -60,10 +60,10 @@
60
60
  "peerDependencies": {
61
61
  "@cloudflare/vitest-pool-workers": "^0.21.3",
62
62
  "@cloudflare/workers-types": "^4.20260611.1",
63
- "@effect/sql-d1": "4.0.0-beta.105",
64
- "@effect/sql-pg": "4.0.0-beta.105",
65
- "@effect/sql-sqlite-do": "4.0.0-beta.105",
66
- "effect": "4.0.0-beta.105"
63
+ "@effect/sql-d1": "^4.0.0-beta.107 <4.0.0-rc.0",
64
+ "@effect/sql-pg": "^4.0.0-beta.107 <4.0.0-rc.0",
65
+ "@effect/sql-sqlite-do": "^4.0.0-beta.107 <4.0.0-rc.0",
66
+ "effect": "^4.0.0-beta.107 <4.0.0-rc.0"
67
67
  },
68
68
  "peerDependenciesMeta": {
69
69
  "@cloudflare/vitest-pool-workers": {
@@ -132,15 +132,7 @@ export interface AnalyticsEngineWriteOptions {
132
132
  readonly onInvalid?: AnalyticsEngineInvalidWritePolicy;
133
133
  }
134
134
 
135
- export interface AnalyticsEngineWriteBatchOptions extends AnalyticsEngineWriteOptions {
136
- /**
137
- * Maximum points per native batch call. Values are clamped to Cloudflare's
138
- * per-invocation limit.
139
- */
140
- readonly batchSize?: number;
141
- }
142
-
143
- export type AnalyticsEngineWritePolicy = AnalyticsEngineWriteBatchOptions;
135
+ export type AnalyticsEngineWritePolicy = AnalyticsEngineWriteOptions;
144
136
 
145
137
  export interface AnalyticsEngineQueryResult<Row = AnalyticsEngineQueryRow> {
146
138
  readonly meta: ReadonlyArray<AnalyticsEngineQueryColumn>;
@@ -167,17 +159,9 @@ export interface AnalyticsEngineClient {
167
159
  dataPoint?: AnalyticsEngineDataPoint,
168
160
  options?: AnalyticsEngineWriteOptions,
169
161
  ) => Effect.Effect<void, AnalyticsEngineWriteError>;
170
- readonly write: (
171
- dataPoint?: AnalyticsEngineDataPoint,
172
- options?: AnalyticsEngineWriteOptions,
173
- ) => Effect.Effect<void, AnalyticsEngineWriteError>;
174
162
  readonly writeDataPoints: (
175
163
  dataPoints: ReadonlyArray<AnalyticsEngineDataPoint>,
176
- options?: AnalyticsEngineWriteBatchOptions,
177
- ) => Effect.Effect<void, AnalyticsEngineWriteError>;
178
- readonly writeBatch: (
179
- dataPoints: ReadonlyArray<AnalyticsEngineDataPoint>,
180
- options?: AnalyticsEngineWriteBatchOptions,
164
+ options?: AnalyticsEngineWriteOptions,
181
165
  ) => Effect.Effect<void, AnalyticsEngineWriteError>;
182
166
  readonly rawUnsafe: Effect.Effect<AnalyticsEngineBinding>;
183
167
  readonly definition: AnalyticsEngineDefinition;
@@ -320,20 +304,11 @@ const tryAnalyticsEngineSync = <A>(
320
304
  catch: (cause) => analyticsEngineError(binding, operation, cause),
321
305
  });
322
306
 
323
- const normalizeBatchSize = (batchSize: number | undefined) => {
324
- if (batchSize === undefined || !Number.isFinite(batchSize) || batchSize <= 0) {
325
- return writeLimits.maxDataPointsPerInvocation;
326
- }
327
-
328
- return Math.min(Math.floor(batchSize), writeLimits.maxDataPointsPerInvocation);
329
- };
330
-
331
307
  const resolveWritePolicy = (
332
308
  defaults: AnalyticsEngineWritePolicy | undefined,
333
- options: AnalyticsEngineWriteBatchOptions | undefined,
309
+ options: AnalyticsEngineWriteOptions | undefined,
334
310
  ) => ({
335
311
  onInvalid: options?.onInvalid ?? defaults?.onInvalid ?? "error",
336
- batchSize: normalizeBatchSize(options?.batchSize ?? defaults?.batchSize),
337
312
  });
338
313
 
339
314
  const fieldPath = (prefix: string, field: string) => (prefix === "" ? field : `${prefix}.${field}`);
@@ -544,23 +519,10 @@ const writeChunks = (
544
519
  operation: string,
545
520
  dataset: AnalyticsEngineBinding,
546
521
  dataPoints: ReadonlyArray<AnalyticsEngineDataPoint>,
547
- batchSize: number,
548
522
  ): Effect.Effect<void, AnalyticsEngineOperationError> =>
549
523
  Effect.gen(function* () {
550
- const writeDataPoints = Reflect.get(dataset, "writeDataPoints");
551
-
552
- for (let index = 0; index < dataPoints.length; index += batchSize) {
553
- const chunk = dataPoints.slice(index, index + batchSize);
554
-
555
- if (typeof writeDataPoints === "function") {
556
- yield* tryAnalyticsEngineSync(binding, operation, () =>
557
- writeDataPoints.call(dataset, chunk),
558
- );
559
- } else {
560
- for (const point of chunk) {
561
- yield* tryAnalyticsEngineSync(binding, operation, () => dataset.writeDataPoint(point));
562
- }
563
- }
524
+ for (const point of dataPoints) {
525
+ yield* tryAnalyticsEngineSync(binding, operation, () => dataset.writeDataPoint(point));
564
526
  }
565
527
  });
566
528
 
@@ -747,7 +709,7 @@ export const makeClient =
747
709
 
748
710
  const writeDataPoints = Effect.fn("AnalyticsEngine.writeDataPoints")(function* (
749
711
  dataPoints: ReadonlyArray<AnalyticsEngineDataPoint>,
750
- options?: AnalyticsEngineWriteBatchOptions,
712
+ options?: AnalyticsEngineWriteOptions,
751
713
  ) {
752
714
  const policy = resolveWritePolicy(defaults, options);
753
715
  const validPoints = yield* validateDataPoints(
@@ -757,21 +719,13 @@ export const makeClient =
757
719
  policy,
758
720
  );
759
721
 
760
- yield* writeChunks(
761
- definition.binding,
762
- "writeDataPoints",
763
- dataset,
764
- validPoints,
765
- policy.batchSize,
766
- );
722
+ yield* writeChunks(definition.binding, "writeDataPoints", dataset, validPoints);
767
723
  });
768
724
 
769
725
  return {
770
726
  definition,
771
727
  writeDataPoint,
772
- write: writeDataPoint,
773
728
  writeDataPoints,
774
- writeBatch: writeDataPoints,
775
729
  rawUnsafe: Effect.succeed(dataset),
776
730
  };
777
731
  };
@@ -457,8 +457,6 @@ export const processDue = <R = never, E = never, OnFailureR = never, OnFailureE
457
457
  return yield* durableObjectAlarm.processDueAlarms(handle, options);
458
458
  });
459
459
 
460
- export const process = processDue;
461
-
462
460
  export type AlarmPayloadSchema = S.Codec<any, any, never, never>;
463
461
 
464
462
  export type AlarmFailurePolicy = "ordered" | "retry" | "skip-and-advance-repeat";
package/src/Email.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import type {
2
2
  EmailAddress as CloudflareEmailAddress,
3
3
  EmailAttachment as CloudflareEmailAttachment,
4
- EmailMessage as CloudflareEmailMessage,
5
4
  EmailSendResult as CloudflareEmailSendResult,
6
5
  SendEmail as CloudflareSendEmail,
7
6
  } from "@cloudflare/workers-types";
@@ -110,9 +109,7 @@ export interface EmailDefinition {
110
109
 
111
110
  export type EmailAddress = CloudflareEmailAddress;
112
111
  export type EmailAttachment = CloudflareEmailAttachment;
113
- export type EmailMessage = CloudflareEmailMessage;
114
112
  export type EmailMessageBuilder = Parameters<CloudflareSendEmail["send"]>[0];
115
- export type EmailSendInput = EmailMessage | EmailMessageBuilder;
116
113
  export type EmailSendResult = CloudflareEmailSendResult;
117
114
  export type EmailBinding = CloudflareSendEmail;
118
115
  export type EmailRecipients = string | EmailAddress | ReadonlyArray<string | EmailAddress>;
@@ -124,14 +121,11 @@ export interface EmailSendOptions {
124
121
  }
125
122
 
126
123
  interface EmailRuntimeBinding {
127
- readonly send: (message: EmailSendInput) => Promise<EmailSendResult>;
124
+ readonly send: (message: EmailMessageBuilder) => Promise<EmailSendResult>;
128
125
  }
129
126
 
130
127
  export interface EmailClient {
131
- readonly send: {
132
- (message: EmailMessage): Effect.Effect<EmailSendResult, EmailSendError>;
133
- (builder: EmailMessageBuilder): Effect.Effect<EmailSendResult, EmailSendError>;
134
- };
128
+ readonly send: (message: EmailMessageBuilder) => Effect.Effect<EmailSendResult, EmailSendError>;
135
129
  readonly rawUnsafe: Effect.Effect<EmailBinding>;
136
130
  readonly definition: EmailDefinition;
137
131
  }
@@ -200,29 +194,6 @@ const tryEmailPromise = <A>(
200
194
  catch: (cause) => emailError(binding, operation, cause),
201
195
  });
202
196
 
203
- const builderFields = [
204
- "subject",
205
- "html",
206
- "text",
207
- "cc",
208
- "bcc",
209
- "replyTo",
210
- "attachments",
211
- "headers",
212
- ] as const;
213
-
214
- /**
215
- * Distinguishes structured Email Sending messages from raw MIME `EmailMessage`
216
- * values, which only carry envelope `from` and `to` addresses.
217
- */
218
- export const isEmailMessageBuilder = (message: EmailSendInput): message is EmailMessageBuilder => {
219
- if (typeof message !== "object" || message === null) {
220
- return false;
221
- }
222
-
223
- return builderFields.some((field) => Reflect.get(message, field) !== undefined);
224
- };
225
-
226
197
  const byteLength = (value: string) => textEncoder.encode(value).byteLength;
227
198
 
228
199
  const limitViolation = (
@@ -469,13 +440,13 @@ export const makeClient =
469
440
  (email: EmailBinding): EmailClient => {
470
441
  const runtime = email as EmailRuntimeBinding;
471
442
  const validate = options?.validate ?? true;
472
- const send = Effect.fn("Email.send")(function* (message: EmailSendInput) {
473
- if (validate && isEmailMessageBuilder(message)) {
443
+ const send = Effect.fn("Email.send")(function* (message: EmailMessageBuilder) {
444
+ if (validate) {
474
445
  yield* validateSendInput(definition.binding, "send", message);
475
446
  }
476
447
 
477
448
  return yield* tryEmailPromise(definition.binding, "send", () => runtime.send(message));
478
- }) as EmailClient["send"];
449
+ });
479
450
 
480
451
  return {
481
452
  definition,
@@ -1,4 +1,4 @@
1
- import { Config, ConfigProvider, Context, Effect, type Layer } from "effect";
1
+ import { Config, ConfigProvider, Context, Effect, type Layer, Predicate } from "effect";
2
2
 
3
3
  /** Cloudflare worker environment object (`env`). */
4
4
  export type WorkerEnv = Cloudflare.Env;
@@ -41,7 +41,7 @@ type ScalarConfigKey = Extract<
41
41
  * const program = Effect.gen(function* () {
42
42
  * const config = yield* AppConfig;
43
43
  * // ...
44
- * }).pipe(Effect.provide(WorkerConfig.layer));
44
+ * }).pipe(Effect.provide(WorkerConfig.providerLayer));
45
45
  * ```
46
46
  *
47
47
  * Keys are constrained to scalar `Cloudflare.Env` properties (`string`,
@@ -78,7 +78,7 @@ export namespace WorkerConfig {
78
78
  * Keep empty-string env values as explicit `""` config values.
79
79
  *
80
80
  * By default (absent or `false`), empty strings are treated as missing
81
- * config, matching `ConfigProvider.fromUnknown`.
81
+ * config, matching `ConfigProvider.fromEnvRecord`.
82
82
  */
83
83
  readonly preserveEmptyStrings?: boolean;
84
84
  }
@@ -86,11 +86,23 @@ export namespace WorkerConfig {
86
86
  /**
87
87
  * Build a `ConfigProvider` from a Cloudflare worker `env` object.
88
88
  *
89
+ * Scalar vars and secrets use Effect's environment-record semantics;
90
+ * Cloudflare binding objects are ignored.
91
+ *
89
92
  * Empty-string values are treated as missing config by default; pass
90
93
  * `{ preserveEmptyStrings: true }` to keep them as explicit values.
91
94
  */
92
- export const providerFromEnv = (env: WorkerEnv, options?: ProviderOptions) =>
93
- ConfigProvider.fromUnknown(env, options);
95
+ export const providerFromEnv = (env: WorkerEnv, options?: ProviderOptions) => {
96
+ const record: Record<string, string> = {};
97
+
98
+ for (const [key, value] of Object.entries(env)) {
99
+ if (Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) {
100
+ record[key] = String(value);
101
+ }
102
+ }
103
+
104
+ return ConfigProvider.fromEnvRecord(record, options);
105
+ };
94
106
 
95
107
  /**
96
108
  * Build a `ConfigProvider` from the current `WorkerEnvironment` with a custom
@@ -109,9 +121,6 @@ export namespace WorkerConfig {
109
121
  export const providerLayer: Layer.Layer<never, never, WorkerEnvironment> =
110
122
  ConfigProvider.layer(provider);
111
123
 
112
- /** Alias for `providerLayer` for concise use in worker layers. */
113
- export const layer: Layer.Layer<never, never, WorkerEnvironment> = providerLayer;
114
-
115
124
  /** Build a `ConfigProvider` layer with a custom `env` conversion function. */
116
125
  export const layerWith = (
117
126
  makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider,