effect-cf 0.2.0 → 0.4.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/dist/index.mjs CHANGED
@@ -120,11 +120,11 @@ const Service$4 = () => (id, definition) => {
120
120
  //#region src/QueueDefinition.ts
121
121
  var QueueDefinition_exports = /* @__PURE__ */ __exportAll({
122
122
  Queue: () => Queue$1,
123
- Tag: () => Tag$7,
123
+ Tag: () => Tag$8,
124
124
  implement: () => implement$7,
125
125
  make: () => make$9
126
126
  });
127
- const makeDefinition$3 = (id, definition) => {
127
+ const makeDefinition$4 = (id, definition) => {
128
128
  const queueDefinition = {
129
129
  id,
130
130
  message: definition.message
@@ -144,9 +144,9 @@ const makeDefinition$3 = (id, definition) => {
144
144
  })
145
145
  });
146
146
  };
147
- const make$9 = makeDefinition$3;
148
- const Tag$7 = () => (id, definition) => {
149
- const queueDefinition = makeDefinition$3(id, definition);
147
+ const make$9 = makeDefinition$4;
148
+ const Tag$8 = () => (id, definition) => {
149
+ const queueDefinition = makeDefinition$4(id, definition);
150
150
  class QueueDefinitionClass {
151
151
  static id = queueDefinition.id;
152
152
  static message = queueDefinition.message;
@@ -156,7 +156,7 @@ const Tag$7 = () => (id, definition) => {
156
156
  }
157
157
  return QueueDefinitionClass;
158
158
  };
159
- const Queue$1 = Tag$7;
159
+ const Queue$1 = Tag$8;
160
160
  const wrapHandler$1 = (definition, handler) => {
161
161
  const decodeBody = Schema.decodeUnknownEffect(definition.message);
162
162
  return (batch) => Effect.gen(function* () {
@@ -168,7 +168,7 @@ const implement$7 = (_definition, handler) => handler;
168
168
  //#region src/Queue.ts
169
169
  var Queue_exports = /* @__PURE__ */ __exportAll({
170
170
  QueueMessageDecodeError: () => QueueMessageDecodeError,
171
- Tag: () => Tag$6,
171
+ Tag: () => Tag$7,
172
172
  decodeBatch: () => decodeBatch,
173
173
  fromMessage: () => fromMessage,
174
174
  fromMessageBatch: () => fromMessageBatch,
@@ -206,7 +206,7 @@ const decodeBatch = (batch, decodeBody) => Effect.gen(function* () {
206
206
  }
207
207
  return fromMessageBatch(batch, messages);
208
208
  });
209
- const Tag$6 = Tag$7;
209
+ const Tag$7 = Tag$8;
210
210
  const implement$6 = implement$7;
211
211
  //#endregion
212
212
  //#region src/RpcDefinition.ts
@@ -480,7 +480,7 @@ const makeDirectMethods$1 = (rpcDefinition, call) => {
480
480
  //#endregion
481
481
  //#region src/WorkerDefinition.ts
482
482
  var WorkerDefinition_exports = /* @__PURE__ */ __exportAll({
483
- Tag: () => Tag$5,
483
+ Tag: () => Tag$6,
484
484
  Worker: () => Worker,
485
485
  implement: () => implement$5,
486
486
  make: () => make$7,
@@ -519,7 +519,7 @@ const method$3 = method$4;
519
519
  * });
520
520
  * ```
521
521
  */
522
- const makeDefinition$2 = (id, methods) => {
522
+ const makeDefinition$3 = (id, methods) => {
523
523
  assertNoReservedMethods("Worker", methods, reservedMethodNames$3);
524
524
  const definition = make$8(id, methods);
525
525
  return Object.assign(definition, {
@@ -537,9 +537,9 @@ const makeDefinition$2 = (id, methods) => {
537
537
  })
538
538
  });
539
539
  };
540
- const make$7 = makeDefinition$2;
541
- const Tag$5 = () => (id, methods) => {
542
- const definition = makeDefinition$2(id, methods);
540
+ const make$7 = makeDefinition$3;
541
+ const Tag$6 = () => (id, methods) => {
542
+ const definition = makeDefinition$3(id, methods);
543
543
  class WorkerDefinitionClass {
544
544
  static id = definition.id;
545
545
  static methods = definition.methods;
@@ -549,7 +549,7 @@ const Tag$5 = () => (id, methods) => {
549
549
  }
550
550
  return WorkerDefinitionClass;
551
551
  };
552
- const Worker = Tag$5;
552
+ const Worker = Tag$6;
553
553
  const wrapHandlers$1 = (definition, handlers) => {
554
554
  const wrapped = {};
555
555
  for (const key of Object.keys(definition.methods)) {
@@ -605,7 +605,7 @@ const fromExecutionContext = (ctx, runPromiseExit) => {
605
605
  var Worker_exports = /* @__PURE__ */ __exportAll({
606
606
  ExecutionContext: () => ExecutionContext,
607
607
  NativeRequest: () => NativeRequest,
608
- Tag: () => Tag$4,
608
+ Tag: () => Tag$5,
609
609
  WorkerContext: () => WorkerContext,
610
610
  implement: () => implement$4,
611
611
  isWebSocketUpgrade: () => isWebSocketUpgrade,
@@ -672,7 +672,7 @@ const makeFetchHandler = (layer, options) => {
672
672
  const WorkerClass = make$6(layer, options);
673
673
  return { fetch: (request, env, ctx) => Promise.resolve(new WorkerClass(ctx, env).fetch(request)) };
674
674
  };
675
- const Tag$4 = Tag$5;
675
+ const Tag$5 = Tag$6;
676
676
  const method$2 = method$3;
677
677
  const implement$4 = implement$5;
678
678
  //#endregion
@@ -923,11 +923,11 @@ const attachment = (schema) => {
923
923
  schema
924
924
  };
925
925
  };
926
- /** Adapts wrapped websocket lifecycle handlers to `DurableObject.make` raw callbacks. */
926
+ /** Maps compact websocket lifecycle handler names to `DurableObject.make` options. */
927
927
  const handlers = (options) => ({
928
- webSocketMessage: options.message === void 0 ? void 0 : (socket, message) => options.message?.(fromWebSocket(socket), message) ?? Effect.void,
929
- webSocketClose: options.close === void 0 ? void 0 : (socket, code, reason, wasClean) => options.close?.(fromWebSocket(socket), code, reason, wasClean) ?? Effect.void,
930
- webSocketError: options.error === void 0 ? void 0 : (socket, error) => options.error?.(fromWebSocket(socket), error) ?? Effect.void
928
+ webSocketMessage: options.message,
929
+ webSocketClose: options.close,
930
+ webSocketError: options.error
931
931
  });
932
932
  //#endregion
933
933
  //#region src/DurableObjectState.ts
@@ -1106,7 +1106,7 @@ const makeDirectMethods = (rpcDefinition, helpers) => {
1106
1106
  //#region src/DurableObjectDefinition.ts
1107
1107
  var DurableObjectDefinition_exports = /* @__PURE__ */ __exportAll({
1108
1108
  DurableObject: () => DurableObject$1,
1109
- Tag: () => Tag$3,
1109
+ Tag: () => Tag$4,
1110
1110
  implement: () => implement$3,
1111
1111
  make: () => make$5,
1112
1112
  method: () => method$1
@@ -1137,7 +1137,7 @@ const method$1 = method$4;
1137
1137
  * });
1138
1138
  * ```
1139
1139
  */
1140
- const makeDefinition$1 = (id, methods) => {
1140
+ const makeDefinition$2 = (id, methods) => {
1141
1141
  assertNoReservedMethods("Durable Object", methods, reservedMethodNames$1);
1142
1142
  const definition = make$8(id, methods);
1143
1143
  return Object.assign(definition, {
@@ -1155,9 +1155,9 @@ const makeDefinition$1 = (id, methods) => {
1155
1155
  })
1156
1156
  });
1157
1157
  };
1158
- const make$5 = makeDefinition$1;
1159
- const Tag$3 = () => (id, methods) => {
1160
- const definition = makeDefinition$1(id, methods);
1158
+ const make$5 = makeDefinition$2;
1159
+ const Tag$4 = () => (id, methods) => {
1160
+ const definition = makeDefinition$2(id, methods);
1161
1161
  class DurableObjectDefinitionClass {
1162
1162
  static id = definition.id;
1163
1163
  static methods = definition.methods;
@@ -1167,7 +1167,7 @@ const Tag$3 = () => (id, methods) => {
1167
1167
  }
1168
1168
  return DurableObjectDefinitionClass;
1169
1169
  };
1170
- const DurableObject$1 = Tag$3;
1170
+ const DurableObject$1 = Tag$4;
1171
1171
  const wrapHandlers = (definition, handlers) => {
1172
1172
  const wrapped = {};
1173
1173
  for (const key of Object.keys(definition.methods)) {
@@ -1185,7 +1185,7 @@ const implement$3 = (_definition, handlers) => handlers;
1185
1185
  //#endregion
1186
1186
  //#region src/DurableObject.ts
1187
1187
  var DurableObject_exports = /* @__PURE__ */ __exportAll({
1188
- Tag: () => Tag$2,
1188
+ Tag: () => Tag$3,
1189
1189
  implement: () => implement$2,
1190
1190
  make: () => make$4,
1191
1191
  method: () => method
@@ -1230,19 +1230,19 @@ const make$4 = (layer, options = {}) => {
1230
1230
  if (rawAlarm !== void 0) return this[RunSymbol](rawAlarm);
1231
1231
  }
1232
1232
  webSocketMessage(socket, message) {
1233
- if (options.webSocketMessage !== void 0) return this[RunSymbol](options.webSocketMessage(socket, message));
1233
+ if (options.webSocketMessage !== void 0) return this[RunSymbol](options.webSocketMessage(fromWebSocket(socket), message));
1234
1234
  }
1235
1235
  webSocketClose(socket, code, reason, wasClean) {
1236
- if (options.webSocketClose !== void 0) return this[RunSymbol](options.webSocketClose(socket, code, reason, wasClean));
1236
+ if (options.webSocketClose !== void 0) return this[RunSymbol](options.webSocketClose(fromWebSocket(socket), code, reason, wasClean));
1237
1237
  }
1238
1238
  webSocketError(socket, error) {
1239
- if (options.webSocketError !== void 0) return this[RunSymbol](options.webSocketError(socket, error));
1239
+ if (options.webSocketError !== void 0) return this[RunSymbol](options.webSocketError(fromWebSocket(socket), error));
1240
1240
  }
1241
1241
  }
1242
1242
  defineEntrypointRpcMethods("Durable Object", EffectDurableObject.prototype, options.rpc, reservedMethodNames, (self, effect) => self[RunSymbol](effect));
1243
1243
  return assumeEntrypointClass(EffectDurableObject);
1244
1244
  };
1245
- const Tag$2 = Tag$3;
1245
+ const Tag$3 = Tag$4;
1246
1246
  const method = method$1;
1247
1247
  const implement$2 = implement$3;
1248
1248
  //#endregion
@@ -1698,6 +1698,7 @@ const writeAttachment = (socket, key, clientId) => {
1698
1698
  var Kv_exports = /* @__PURE__ */ __exportAll({
1699
1699
  KvOperationError: () => KvOperationError,
1700
1700
  Service: () => Service$1,
1701
+ Tag: () => Tag$2,
1701
1702
  make: () => make$3
1702
1703
  });
1703
1704
  const TypeId$1 = "effect-cf/Kv";
@@ -1718,6 +1719,36 @@ const tryKvPromise = (binding, operation, evaluate) => Effect.tryPromise({
1718
1719
  * Creates a typed KV service tag plus Effect helpers.
1719
1720
  */
1720
1721
  const make$3 = (id, definition) => Service$1()(id, definition);
1722
+ const makeDefinition$1 = (id, definition) => {
1723
+ const kvDefinition = {
1724
+ id,
1725
+ key: definition.key,
1726
+ value: definition.value
1727
+ };
1728
+ return Object.assign(kvDefinition, {
1729
+ Binding: () => (bindingId, binding) => Service$1()(bindingId, {
1730
+ ...binding,
1731
+ key: definition.key,
1732
+ value: definition.value
1733
+ }),
1734
+ binding: (bindingId, binding) => Service$1()(bindingId, {
1735
+ ...binding,
1736
+ key: definition.key,
1737
+ value: definition.value
1738
+ })
1739
+ });
1740
+ };
1741
+ const Tag$2 = () => (id, definition) => {
1742
+ const kvDefinition = makeDefinition$1(id, definition);
1743
+ class KvDefinitionClass {
1744
+ static id = kvDefinition.id;
1745
+ static key = kvDefinition.key;
1746
+ static value = kvDefinition.value;
1747
+ static Binding = kvDefinition.Binding;
1748
+ static binding = kvDefinition.binding;
1749
+ }
1750
+ return KvDefinitionClass;
1751
+ };
1721
1752
  /**
1722
1753
  * Builds a KV service around a Cloudflare KV namespace binding.
1723
1754
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-cf",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Effect-native primitives for Cloudflare Workers and bindings.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -4,6 +4,7 @@ import { Effect, Layer, ManagedRuntime, type Scope } from "effect";
4
4
  import { NativeRequest } from "./Worker";
5
5
  import { WorkerEnvironment, type WorkerEnv } from "./Environment";
6
6
  import { DurableObjectState, fromDurableObjectState } from "./DurableObjectState";
7
+ import { fromWebSocket, type DurableWebSocket } from "./DurableObjectWebSocket";
7
8
  import * as DurableObjectDefinition from "./DurableObjectDefinition";
8
9
  import * as Entrypoint from "./internal/Entrypoint";
9
10
 
@@ -84,17 +85,17 @@ export interface DurableObjectOptions<ROut, Rpc extends DurableObjectRpc<ROut>>
84
85
  alarmInfo?: globalThis.AlarmInvocationInfo,
85
86
  ) => Effect.Effect<void, unknown, HandlerContext<ROut>>;
86
87
  readonly webSocketMessage?: (
87
- socket: WebSocket,
88
+ socket: DurableWebSocket,
88
89
  message: string | ArrayBuffer,
89
90
  ) => Effect.Effect<void, unknown, HandlerContext<ROut>>;
90
91
  readonly webSocketClose?: (
91
- socket: WebSocket,
92
+ socket: DurableWebSocket,
92
93
  code: number,
93
94
  reason: string,
94
95
  wasClean: boolean,
95
96
  ) => Effect.Effect<void, unknown, HandlerContext<ROut>>;
96
97
  readonly webSocketError?: (
97
- socket: WebSocket,
98
+ socket: DurableWebSocket,
98
99
  error: unknown,
99
100
  ) => Effect.Effect<void, unknown, HandlerContext<ROut>>;
100
101
  }
@@ -172,7 +173,7 @@ export const make = <
172
173
 
173
174
  webSocketMessage(socket: WebSocket, message: string | ArrayBuffer): Promise<void> | void {
174
175
  if (options.webSocketMessage !== undefined) {
175
- return this[RunSymbol](options.webSocketMessage(socket, message));
176
+ return this[RunSymbol](options.webSocketMessage(fromWebSocket(socket), message));
176
177
  }
177
178
  }
178
179
 
@@ -183,13 +184,15 @@ export const make = <
183
184
  wasClean: boolean,
184
185
  ): Promise<void> | void {
185
186
  if (options.webSocketClose !== undefined) {
186
- return this[RunSymbol](options.webSocketClose(socket, code, reason, wasClean));
187
+ return this[RunSymbol](
188
+ options.webSocketClose(fromWebSocket(socket), code, reason, wasClean),
189
+ );
187
190
  }
188
191
  }
189
192
 
190
193
  webSocketError(socket: WebSocket, error: unknown): Promise<void> | void {
191
194
  if (options.webSocketError !== undefined) {
192
- return this[RunSymbol](options.webSocketError(socket, error));
195
+ return this[RunSymbol](options.webSocketError(fromWebSocket(socket), error));
193
196
  }
194
197
  }
195
198
  }
@@ -251,21 +251,9 @@ export interface DurableWebSocketHandlers<R = never, E = unknown> {
251
251
  readonly error?: (socket: DurableWebSocket, error: unknown) => Effect.Effect<void, E, R>;
252
252
  }
253
253
 
254
- /** Adapts wrapped websocket lifecycle handlers to `DurableObject.make` raw callbacks. */
254
+ /** Maps compact websocket lifecycle handler names to `DurableObject.make` options. */
255
255
  export const handlers = <R = never, E = unknown>(options: DurableWebSocketHandlers<R, E>) => ({
256
- webSocketMessage:
257
- options.message === undefined
258
- ? undefined
259
- : (socket: WebSocket, message: string | ArrayBuffer) =>
260
- options.message?.(fromWebSocket(socket), message) ?? Effect.void,
261
- webSocketClose:
262
- options.close === undefined
263
- ? undefined
264
- : (socket: WebSocket, code: number, reason: string, wasClean: boolean) =>
265
- options.close?.(fromWebSocket(socket), code, reason, wasClean) ?? Effect.void,
266
- webSocketError:
267
- options.error === undefined
268
- ? undefined
269
- : (socket: WebSocket, error: unknown) =>
270
- options.error?.(fromWebSocket(socket), error) ?? Effect.void,
256
+ webSocketMessage: options.message,
257
+ webSocketClose: options.close,
258
+ webSocketError: options.error,
271
259
  });
package/src/Kv.ts CHANGED
@@ -52,6 +52,26 @@ export interface KvDefinition<Key, Value, EncodedValue> {
52
52
  readonly value: S.Codec<Value, EncodedValue>;
53
53
  }
54
54
 
55
+ /**
56
+ * Reusable typed KV resource definition without a concrete Cloudflare binding name.
57
+ */
58
+ export interface Definition<
59
+ Id extends string = string,
60
+ Key = unknown,
61
+ Value = unknown,
62
+ EncodedValue = unknown,
63
+ > {
64
+ readonly id: Id;
65
+ /** Codec used to encode/decode keys. */
66
+ readonly key: S.Codec<Key, string>;
67
+ /** Codec used to encode/decode values. */
68
+ readonly value: S.Codec<Value, EncodedValue>;
69
+ }
70
+
71
+ export namespace Definition {
72
+ export type Any = Definition<string, any, any, any>;
73
+ }
74
+
55
75
  declare const KvServiceTypeId: unique symbol;
56
76
 
57
77
  /** Nominal service marker for KV services created with {@link make}. */
@@ -64,6 +84,16 @@ export interface KvService<Id extends string, Key, Value, EncodedValue> {
64
84
  };
65
85
  }
66
86
 
87
+ declare const BindingServiceTypeId: unique symbol;
88
+
89
+ /** Nominal service marker for KV bindings created from a reusable {@link Definition}. */
90
+ export interface BindingService<Id extends string, Self extends Definition.Any> {
91
+ readonly [BindingServiceTypeId]: {
92
+ readonly id: Id;
93
+ readonly definition: Self;
94
+ };
95
+ }
96
+
67
97
  const maybeString = (value: string | null | undefined): Option.Option<string> =>
68
98
  value == null ? Option.none() : Option.some(value);
69
99
 
@@ -92,6 +122,66 @@ export const make = <Id extends string, Key, Value, EncodedValue>(
92
122
  ) =>
93
123
  Service<KvService<Id, Key, Value, EncodedValue>>()<Id, Key, Value, EncodedValue>(id, definition);
94
124
 
125
+ const makeDefinition = <Id extends string, Key, Value, EncodedValue>(
126
+ id: Id,
127
+ definition: { readonly key: S.Codec<Key, string>; readonly value: S.Codec<Value, EncodedValue> },
128
+ ) => {
129
+ type SelfDefinition = Definition<Id, Key, Value, EncodedValue>;
130
+ const kvDefinition: SelfDefinition = {
131
+ id,
132
+ key: definition.key,
133
+ value: definition.value,
134
+ };
135
+
136
+ return Object.assign(kvDefinition, {
137
+ Binding:
138
+ <Self>() =>
139
+ <BindingId extends string>(
140
+ bindingId: BindingId,
141
+ binding: Omit<KvDefinition<Key, Value, EncodedValue>, "key" | "value">,
142
+ ) =>
143
+ Service<Self>()<BindingId, Key, Value, EncodedValue>(bindingId, {
144
+ ...binding,
145
+ key: definition.key,
146
+ value: definition.value,
147
+ }),
148
+ binding: <BindingId extends string>(
149
+ bindingId: BindingId,
150
+ binding: Omit<KvDefinition<Key, Value, EncodedValue>, "key" | "value">,
151
+ ) =>
152
+ Service<BindingService<BindingId, SelfDefinition>>()<BindingId, Key, Value, EncodedValue>(
153
+ bindingId,
154
+ {
155
+ ...binding,
156
+ key: definition.key,
157
+ value: definition.value,
158
+ },
159
+ ),
160
+ });
161
+ };
162
+
163
+ export const Tag =
164
+ <_Self>() =>
165
+ <Id extends string, Key, Value, EncodedValue>(
166
+ id: Id,
167
+ definition: {
168
+ readonly key: S.Codec<Key, string>;
169
+ readonly value: S.Codec<Value, EncodedValue>;
170
+ },
171
+ ) => {
172
+ const kvDefinition = makeDefinition(id, definition);
173
+
174
+ abstract class KvDefinitionClass {
175
+ static readonly id = kvDefinition.id;
176
+ static readonly key = kvDefinition.key;
177
+ static readonly value = kvDefinition.value;
178
+ static readonly Binding = kvDefinition.Binding;
179
+ static readonly binding = kvDefinition.binding;
180
+ }
181
+
182
+ return KvDefinitionClass as (abstract new () => object) & typeof kvDefinition;
183
+ };
184
+
95
185
  /**
96
186
  * Builds a KV service around a Cloudflare KV namespace binding.
97
187
  *