effect-cf 0.9.0 → 0.9.2
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 +12 -0
- package/dist/{Hyperdrive-LuQaVhXU.mjs → Hyperdrive-CT6UD37x.mjs} +79 -9
- package/dist/{Hyperdrive-By6DrSS_.d.mts → Hyperdrive-L6ldMlRF.d.mts} +10 -3
- package/dist/HyperdrivePg.d.mts +1 -1
- package/dist/HyperdrivePg.mjs +1 -1
- package/dist/index.d.mts +17 -7
- package/dist/index.mjs +96 -37
- package/package.json +1 -1
- package/src/Binding.ts +123 -5
- package/src/D1.ts +4 -1
- package/src/DurableObjectNamespace.ts +4 -0
- package/src/Hyperdrive.ts +6 -1
- package/src/Images.ts +5 -1
- package/src/Kv.ts +4 -0
- package/src/QueueBinding.ts +52 -17
- package/src/R2.ts +107 -42
- package/src/ServiceBinding.ts +2 -0
- package/src/WorkflowBinding.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# effect-cf
|
|
2
2
|
|
|
3
|
+
## 0.9.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#30](https://github.com/danieljvdm/effect-cf/pull/30) [`29c3fd4`](https://github.com/danieljvdm/effect-cf/commit/29c3fd491bad0c8d6994e9a666f6e501c1a337a2) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Accept local queue producer bindings that only expose `send`, including Wrangler local dev bindings. Binding validation errors now include the binding name, expected shape, and actual resource shape in pretty output across Cloudflare bindings.
|
|
8
|
+
|
|
9
|
+
## 0.9.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#27](https://github.com/danieljvdm/effect-cf/pull/27) [`ac2fb0f`](https://github.com/danieljvdm/effect-cf/commit/ac2fb0f9b557c1f73d779ed024c03245c1850b02) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Wrap R2 object body reader methods in Effect so `json`, `text`, `bytes`, `arrayBuffer`, and `blob` report read failures as `R2OperationError`.
|
|
14
|
+
|
|
3
15
|
## 0.9.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -33,21 +33,90 @@ const TypeId = "effect-cf/Binding";
|
|
|
33
33
|
var BindingNotFoundError = class extends Data.TaggedError("BindingNotFoundError") {};
|
|
34
34
|
/** Error raised when a binding exists but does not match the expected shape. */
|
|
35
35
|
var BindingValidationError = class extends Data.TaggedError("BindingValidationError") {};
|
|
36
|
+
const defaultExpected = "Cloudflare binding resource";
|
|
36
37
|
const isPropertyTarget = (value) => (typeof value === "object" || typeof value === "function") && value !== null;
|
|
37
|
-
const
|
|
38
|
-
|
|
38
|
+
const getObjectName = (value) => {
|
|
39
|
+
const tag = (() => {
|
|
40
|
+
try {
|
|
41
|
+
return Object.prototype.toString.call(value).slice(8, -1);
|
|
42
|
+
} catch {
|
|
43
|
+
return typeof value;
|
|
44
|
+
}
|
|
45
|
+
})();
|
|
46
|
+
const constructorName = (() => {
|
|
47
|
+
try {
|
|
48
|
+
return "constructor" in value && typeof value.constructor === "function" && typeof value.constructor.name === "string" ? value.constructor.name : void 0;
|
|
49
|
+
} catch {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
if (tag !== "Object") return tag;
|
|
54
|
+
if (constructorName !== void 0 && constructorName !== "" && constructorName !== "Object") return constructorName;
|
|
55
|
+
return tag;
|
|
56
|
+
};
|
|
57
|
+
const propertyNames = (value) => {
|
|
58
|
+
const names = /* @__PURE__ */ new Set();
|
|
59
|
+
for (const target of [value, Object.getPrototypeOf(value)]) {
|
|
60
|
+
if (target === null || target === Object.prototype || target === Function.prototype) continue;
|
|
61
|
+
try {
|
|
62
|
+
for (const name of Object.getOwnPropertyNames(target)) names.add(name);
|
|
63
|
+
} catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return [...names].filter((name) => name !== "constructor").sort();
|
|
68
|
+
};
|
|
69
|
+
const isMethod = (value, name) => {
|
|
70
|
+
try {
|
|
71
|
+
return typeof Reflect.get(value, name) === "function";
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const describeActual = (value) => {
|
|
77
|
+
if (value === null) return "null";
|
|
78
|
+
if (typeof value !== "object" && typeof value !== "function") return typeof value;
|
|
79
|
+
const names = propertyNames(value);
|
|
80
|
+
const methods = names.filter((name) => isMethod(value, name));
|
|
81
|
+
const properties = names.filter((name) => !methods.includes(name));
|
|
82
|
+
const details = [methods.length > 0 ? `methods ${methods.join(", ")}` : void 0, properties.length > 0 ? `properties ${properties.join(", ")}` : void 0].filter((detail) => detail !== void 0);
|
|
83
|
+
if (details.length === 0) return getObjectName(value);
|
|
84
|
+
return `${getObjectName(value)} with ${details.join("; ")}`;
|
|
85
|
+
};
|
|
86
|
+
const getBinding = (env, binding, isResource, options) => Effect.gen(function* () {
|
|
87
|
+
if (!isPropertyTarget(env)) {
|
|
88
|
+
const actual = describeActual(env);
|
|
89
|
+
return yield* Effect.fail(new BindingValidationError({
|
|
90
|
+
binding,
|
|
91
|
+
expected: "WorkerEnvironment object",
|
|
92
|
+
actual,
|
|
93
|
+
message: `Cloudflare binding "${binding}" failed validation. Expected WorkerEnvironment object; got ${actual}`
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
39
96
|
const resource = Reflect.get(env, binding);
|
|
40
|
-
if (resource === void 0) return yield* Effect.fail(new BindingNotFoundError({
|
|
41
|
-
|
|
97
|
+
if (resource === void 0) return yield* Effect.fail(new BindingNotFoundError({
|
|
98
|
+
binding,
|
|
99
|
+
message: `Cloudflare binding "${binding}" was not found in WorkerEnvironment`
|
|
100
|
+
}));
|
|
101
|
+
if (!isResource(resource)) {
|
|
102
|
+
const expected = options?.expected ?? defaultExpected;
|
|
103
|
+
const actual = describeActual(resource);
|
|
104
|
+
return yield* Effect.fail(new BindingValidationError({
|
|
105
|
+
binding,
|
|
106
|
+
expected,
|
|
107
|
+
actual,
|
|
108
|
+
message: `Cloudflare binding "${binding}" failed validation. Expected ${expected}; got ${actual}`
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
42
111
|
return resource;
|
|
43
112
|
});
|
|
44
|
-
const layer$1 = (tag, binding, isResource, wrap) => Layer.effect(tag, Effect.gen(function* () {
|
|
45
|
-
const resource = yield* getBinding(yield* WorkerEnvironment, binding, isResource);
|
|
113
|
+
const layer$1 = (tag, binding, isResource, wrap, options) => Layer.effect(tag, Effect.gen(function* () {
|
|
114
|
+
const resource = yield* getBinding(yield* WorkerEnvironment, binding, isResource, options);
|
|
46
115
|
return wrap === void 0 ? resource : wrap(resource);
|
|
47
116
|
}));
|
|
48
|
-
const Service = () => (id, binding, isResource, wrap) => {
|
|
117
|
+
const Service = () => (id, binding, isResource, wrap, options) => {
|
|
49
118
|
const tag = Context.Service()(id);
|
|
50
|
-
const serviceLayer = layer$1(tag, binding, isResource, wrap);
|
|
119
|
+
const serviceLayer = layer$1(tag, binding, isResource, wrap, options);
|
|
51
120
|
return Object.assign(tag, {
|
|
52
121
|
[TypeId]: TypeId,
|
|
53
122
|
binding,
|
|
@@ -63,6 +132,7 @@ var Hyperdrive_exports = /* @__PURE__ */ __exportAll({
|
|
|
63
132
|
make: () => make,
|
|
64
133
|
makeClient: () => makeClient
|
|
65
134
|
});
|
|
135
|
+
const expectedHyperdrive = "Hyperdrive binding with connectionString";
|
|
66
136
|
const isHyperdrive = (value) => {
|
|
67
137
|
if (typeof value !== "object" || value === null) return false;
|
|
68
138
|
return typeof value.connectionString === "string";
|
|
@@ -72,7 +142,7 @@ const makeClient = (definition) => (hyperdrive) => ({
|
|
|
72
142
|
connectionString: hyperdrive.connectionString,
|
|
73
143
|
unsafeRaw: Effect.succeed(hyperdrive)
|
|
74
144
|
});
|
|
75
|
-
const layer = (tag, definition) => layer$1(tag, definition.binding, isHyperdrive, makeClient(definition));
|
|
145
|
+
const layer = (tag, definition) => layer$1(tag, definition.binding, isHyperdrive, makeClient(definition), { expected: expectedHyperdrive });
|
|
76
146
|
const make = (id) => Tag()(id);
|
|
77
147
|
const Tag = () => (id) => {
|
|
78
148
|
const tag = Context.Service()(id);
|
|
@@ -76,7 +76,7 @@ declare namespace WorkerConfig {
|
|
|
76
76
|
const layerWith: (makeProvider: (env: WorkerEnv) => ConfigProvider.ConfigProvider) => _$effect_Layer0.Layer<never, never, WorkerEnvironment>;
|
|
77
77
|
}
|
|
78
78
|
declare namespace Binding_d_exports {
|
|
79
|
-
export { BindingNotFoundError, BindingService, BindingValidationError, Service, TypeId, layer$1 as layer };
|
|
79
|
+
export { BindingNotFoundError, BindingService, BindingValidationError, Service, TypeId, ValidationOptions, layer$1 as layer };
|
|
80
80
|
}
|
|
81
81
|
/** Internal type id marker used by binding helper services. */
|
|
82
82
|
declare const TypeId: "effect-cf/Binding";
|
|
@@ -86,6 +86,7 @@ declare const BindingNotFoundError_base: new <A extends Record<string, any> = {}
|
|
|
86
86
|
/** Error raised when a configured binding does not exist on `env`. */
|
|
87
87
|
declare class BindingNotFoundError extends BindingNotFoundError_base<{
|
|
88
88
|
readonly binding: string;
|
|
89
|
+
readonly message: string;
|
|
89
90
|
}> {}
|
|
90
91
|
declare const BindingValidationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
91
92
|
readonly _tag: "BindingValidationError";
|
|
@@ -93,7 +94,13 @@ declare const BindingValidationError_base: new <A extends Record<string, any> =
|
|
|
93
94
|
/** Error raised when a binding exists but does not match the expected shape. */
|
|
94
95
|
declare class BindingValidationError extends BindingValidationError_base<{
|
|
95
96
|
readonly binding: string;
|
|
97
|
+
readonly expected: string;
|
|
98
|
+
readonly actual: string;
|
|
99
|
+
readonly message: string;
|
|
96
100
|
}> {}
|
|
101
|
+
interface ValidationOptions {
|
|
102
|
+
readonly expected?: string;
|
|
103
|
+
}
|
|
97
104
|
/**
|
|
98
105
|
* Creates a Context tag + layer for reading and validating a Cloudflare binding.
|
|
99
106
|
*/
|
|
@@ -102,8 +109,8 @@ interface BindingService<Self, Id extends string, Service> extends Context.Servi
|
|
|
102
109
|
readonly binding: string;
|
|
103
110
|
readonly layer: Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
|
|
104
111
|
}
|
|
105
|
-
declare const layer$1: <Self, Resource, Service = Resource>(tag: Context.Service<Self, Service>, binding: string, isResource: (value: unknown) => value is Resource, wrap?: (resource: Resource) => Service) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
|
|
106
|
-
declare const Service: <Self>() => <Id extends string, Resource, Service = Resource>(id: Id, binding: string, isResource: (value: unknown) => value is Resource, wrap?: (resource: Resource) => Service) => BindingService<Self, Id, Service>;
|
|
112
|
+
declare const layer$1: <Self, Resource, Service = Resource>(tag: Context.Service<Self, Service>, binding: string, isResource: (value: unknown) => value is Resource, wrap?: (resource: Resource) => Service, options?: ValidationOptions) => Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
|
|
113
|
+
declare const Service: <Self>() => <Id extends string, Resource, Service = Resource>(id: Id, binding: string, isResource: (value: unknown) => value is Resource, wrap?: (resource: Resource) => Service, options?: ValidationOptions) => BindingService<Self, Id, Service>;
|
|
107
114
|
declare namespace Hyperdrive_d_exports {
|
|
108
115
|
export { HyperdriveClient, HyperdriveDefinition, HyperdriveService, LayerOptions, Tag, TagClass, isHyperdrive, layer, make, makeClient };
|
|
109
116
|
}
|
package/dist/HyperdrivePg.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as BindingNotFoundError, n as LayerOptions, o as BindingValidationError, r as TagClass, u as WorkerEnvironment } from "./Hyperdrive-
|
|
1
|
+
import { i as BindingNotFoundError, n as LayerOptions, o as BindingValidationError, r as TagClass, u as WorkerEnvironment } from "./Hyperdrive-L6ldMlRF.mjs";
|
|
2
2
|
import { Layer } from "effect";
|
|
3
3
|
import { PgClient } from "@effect/sql-pg";
|
|
4
4
|
import * as _$effect_unstable_sql_SqlClient0 from "effect/unstable/sql/SqlClient";
|
package/dist/HyperdrivePg.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as BindingService, c as WorkerConfig, i as BindingNotFoundError, l as WorkerEnv, o as BindingValidationError, s as Binding_d_exports, t as Hyperdrive_d_exports, u as WorkerEnvironment } from "./Hyperdrive-
|
|
1
|
+
import { a as BindingService, c as WorkerConfig, i as BindingNotFoundError, l as WorkerEnv, o as BindingValidationError, s as Binding_d_exports, t as Hyperdrive_d_exports, u as WorkerEnvironment } from "./Hyperdrive-L6ldMlRF.mjs";
|
|
2
2
|
import { Cause, Context, Data, DateTime, Duration, Effect, Layer, Option, Schema, Scope } from "effect";
|
|
3
3
|
import { D1Client } from "@effect/sql-d1";
|
|
4
4
|
import { DurableObject, RpcStub, RpcTarget, WorkerEntrypoint, WorkflowEntrypoint, WorkflowEvent, WorkflowSleepDuration, WorkflowStep, WorkflowStepConfig, WorkflowStepContext, WorkflowStepEvent, WorkflowTimeoutDuration } from "cloudflare:workers";
|
|
@@ -184,7 +184,7 @@ declare const encodeSuccess: <const Self extends Definition$9.Any, MethodName ex
|
|
|
184
184
|
declare const decodeSuccess: <const Self extends Definition$9.Any, MethodName extends Definition$9.MethodNames<Self>>(definition: Self, methodName: MethodName, value: unknown) => Effect.Effect<Method$5.Success<Self["methods"][MethodName]>, RpcSuccessDecodeError>;
|
|
185
185
|
declare const make$9: <Id extends string, const MethodsShape extends Methods$4>(id: Id, methods: MethodsShape) => Definition$9<Id, MethodsShape>;
|
|
186
186
|
declare namespace QueueBinding_d_exports {
|
|
187
|
-
export { MessageSendRequest, QueueBindingClient, QueueBindingDefinition, QueueMetrics, QueueOperationError, QueueSendBatchOptions, QueueSendBatchResponse, QueueSendOptions, QueueSendResponse, isQueue, layer$7 as layer, makeClient$6 as makeClient };
|
|
187
|
+
export { MessageSendRequest, QueueBindingClient, QueueBindingDefinition, QueueMetrics, QueueOperationError, QueueProducer, QueueSendBatchOptions, QueueSendBatchResponse, QueueSendOptions, QueueSendResponse, isQueue, layer$7 as layer, makeClient$6 as makeClient };
|
|
188
188
|
}
|
|
189
189
|
type QueueSendOptions = globalThis.QueueSendOptions;
|
|
190
190
|
type QueueSendResponse = globalThis.QueueSendResponse;
|
|
@@ -192,6 +192,7 @@ type QueueSendBatchOptions = globalThis.QueueSendBatchOptions;
|
|
|
192
192
|
type QueueSendBatchResponse = globalThis.QueueSendBatchResponse;
|
|
193
193
|
type QueueMetrics = globalThis.QueueMetrics;
|
|
194
194
|
type MessageSendRequest<Body> = globalThis.MessageSendRequest<Body>;
|
|
195
|
+
type QueueProducer<Body> = Pick<globalThis.Queue<Body>, "send"> & Partial<Pick<globalThis.Queue<Body>, "sendBatch" | "metrics">>;
|
|
195
196
|
interface QueueBindingDefinition<Message extends ServiceFreeSchema$4> {
|
|
196
197
|
/** Binding name as configured in `wrangler.jsonc`. */
|
|
197
198
|
readonly binding: string;
|
|
@@ -211,9 +212,10 @@ declare class QueueOperationError extends QueueOperationError_base<{
|
|
|
211
212
|
readonly binding: string;
|
|
212
213
|
readonly operation: string;
|
|
213
214
|
readonly cause: unknown;
|
|
215
|
+
readonly message: string;
|
|
214
216
|
}> {}
|
|
215
|
-
declare const isQueue: <Body>(value: unknown) => value is
|
|
216
|
-
declare const makeClient$6: <Message extends ServiceFreeSchema$4>(definition: QueueBindingDefinition<Message>) => ((queue:
|
|
217
|
+
declare const isQueue: <Body>(value: unknown) => value is QueueProducer<Body>;
|
|
218
|
+
declare const makeClient$6: <Message extends ServiceFreeSchema$4>(definition: QueueBindingDefinition<Message>) => ((queue: QueueProducer<Schema.Codec.Encoded<Message>>) => QueueBindingClient<Message>);
|
|
217
219
|
declare const layer$7: <Self, Message extends ServiceFreeSchema$4>(tag: Context.Service<Self, QueueBindingClient<Message>>, definition: QueueBindingDefinition<Message>) => _$effect_Layer0.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment>;
|
|
218
220
|
declare namespace QueueDefinition_d_exports {
|
|
219
221
|
export { Definition$8 as Definition, Handler$3 as Handler, LayerOptions$11 as LayerOptions, Options$7 as Options, Queue$1 as Queue, Tag$11 as Tag, TagClass$10 as TagClass, implement$7 as implement, make$8 as make };
|
|
@@ -1921,7 +1923,7 @@ declare const Tag$4: <Self>() => <Id extends string, Key, Value, EncodedValue>(i
|
|
|
1921
1923
|
readonly value: Schema.Codec<Value, EncodedValue>;
|
|
1922
1924
|
}) => TagClass$3<Self, Id, Key, Value, EncodedValue>;
|
|
1923
1925
|
declare namespace R2_d_exports {
|
|
1924
|
-
export { LayerOptions$2 as LayerOptions, R2Client, R2Definition, R2GetOptions, R2ListOptions, R2MultipartOptions, R2MultipartUploadClient, R2OperationError, R2PutOptions, R2PutValue, R2Service, R2UploadPartOptions, R2UploadPartValue, Tag$3 as Tag, TagClass$2 as TagClass, isR2Bucket, layer$1 as layer, make$2 as make, makeClient$1 as makeClient };
|
|
1926
|
+
export { LayerOptions$2 as LayerOptions, R2Client, R2Definition, R2GetOptions, R2ListOptions, R2MultipartOptions, R2MultipartUploadClient, R2ObjectBodyClient, R2OperationError, R2PutOptions, R2PutValue, R2Service, R2UploadPartOptions, R2UploadPartValue, Tag$3 as Tag, TagClass$2 as TagClass, isR2Bucket, layer$1 as layer, make$2 as make, makeClient$1 as makeClient };
|
|
1925
1927
|
}
|
|
1926
1928
|
declare const R2OperationError_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
1927
1929
|
readonly _tag: "R2OperationError";
|
|
@@ -1944,6 +1946,14 @@ type R2MultipartOptions = globalThis.R2MultipartOptions;
|
|
|
1944
1946
|
type R2UploadPartOptions = globalThis.R2UploadPartOptions;
|
|
1945
1947
|
type R2PutValue = ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob;
|
|
1946
1948
|
type R2UploadPartValue = ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob;
|
|
1949
|
+
interface R2ObjectBodyClient extends Omit<globalThis.R2ObjectBody, "arrayBuffer" | "blob" | "bytes" | "json" | "text"> {
|
|
1950
|
+
readonly raw: globalThis.R2ObjectBody;
|
|
1951
|
+
readonly arrayBuffer: Effect.Effect<ArrayBuffer, R2OperationError>;
|
|
1952
|
+
readonly bytes: Effect.Effect<Uint8Array, R2OperationError>;
|
|
1953
|
+
readonly text: Effect.Effect<string, R2OperationError>;
|
|
1954
|
+
readonly json: <T = unknown>() => Effect.Effect<T, R2OperationError>;
|
|
1955
|
+
readonly blob: Effect.Effect<Blob, R2OperationError>;
|
|
1956
|
+
}
|
|
1947
1957
|
interface R2MultipartUploadClient {
|
|
1948
1958
|
readonly raw: globalThis.R2MultipartUpload;
|
|
1949
1959
|
readonly key: string;
|
|
@@ -1957,8 +1967,8 @@ interface R2Client {
|
|
|
1957
1967
|
readonly get: {
|
|
1958
1968
|
(key: string, options: R2GetOptions & {
|
|
1959
1969
|
readonly onlyIf: globalThis.R2Conditional | Headers;
|
|
1960
|
-
}): Effect.Effect<Option.Option<
|
|
1961
|
-
(key: string, options?: R2GetOptions): Effect.Effect<Option.Option<
|
|
1970
|
+
}): Effect.Effect<Option.Option<R2ObjectBodyClient | globalThis.R2Object>, R2OperationError>;
|
|
1971
|
+
(key: string, options?: R2GetOptions): Effect.Effect<Option.Option<R2ObjectBodyClient>, R2OperationError>;
|
|
1962
1972
|
};
|
|
1963
1973
|
readonly put: {
|
|
1964
1974
|
(key: string, value: R2PutValue, options: R2PutOptions & {
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
-
import { a as WorkerConfig, i as layer$8, n as Binding_exports, o as WorkerEnvironment, r as Service$2, t as Hyperdrive_exports } from "./Hyperdrive-
|
|
2
|
+
import { a as WorkerConfig, i as layer$8, n as Binding_exports, o as WorkerEnvironment, r as Service$2, t as Hyperdrive_exports } from "./Hyperdrive-CT6UD37x.mjs";
|
|
3
3
|
import { Cause, Clock, ConfigProvider, Context, Data, DateTime, Duration, Effect, Exit, Layer, ManagedRuntime, Option, Queue, Schema } from "effect";
|
|
4
4
|
import { D1Client } from "@effect/sql-d1";
|
|
5
5
|
import { DurableObject, RpcStub, RpcTarget, WorkerEntrypoint, WorkflowEntrypoint } from "cloudflare:workers";
|
|
@@ -13,6 +13,7 @@ var D1_exports = /* @__PURE__ */ __exportAll({
|
|
|
13
13
|
make: () => make$10
|
|
14
14
|
});
|
|
15
15
|
const TypeId$2 = "effect-cf/D1";
|
|
16
|
+
const expectedD1Database = "D1 database binding with prepare(), batch(), and exec()";
|
|
16
17
|
const isD1Database = (value) => {
|
|
17
18
|
if (typeof value !== "object" || value === null) return false;
|
|
18
19
|
const resource = value;
|
|
@@ -38,7 +39,7 @@ const make$10 = (id, definition) => Service$1()(id, definition);
|
|
|
38
39
|
* ```
|
|
39
40
|
*/
|
|
40
41
|
const Service$1 = () => (id, definition) => {
|
|
41
|
-
const tag = Service$2()(id, definition.binding, isD1Database);
|
|
42
|
+
const tag = Service$2()(id, definition.binding, isD1Database, void 0, { expected: expectedD1Database });
|
|
42
43
|
const sqlLayer = (options) => Layer.unwrap(Effect.gen(function* () {
|
|
43
44
|
const db = yield* tag;
|
|
44
45
|
return D1Client.layer({
|
|
@@ -60,20 +61,20 @@ var QueueBinding_exports = /* @__PURE__ */ __exportAll({
|
|
|
60
61
|
layer: () => layer$7,
|
|
61
62
|
makeClient: () => makeClient$6
|
|
62
63
|
});
|
|
64
|
+
const expectedQueueProducer = "Queue producer binding with send(); optional sendBatch()/metrics()";
|
|
63
65
|
var QueueOperationError = class extends Data.TaggedError("QueueOperationError") {};
|
|
64
|
-
const queueError = (binding, operation, cause) => new QueueOperationError({
|
|
65
|
-
binding,
|
|
66
|
-
operation,
|
|
67
|
-
cause
|
|
68
|
-
});
|
|
69
66
|
const tryQueuePromise = (binding, operation, evaluate) => Effect.tryPromise({
|
|
70
67
|
try: evaluate,
|
|
71
|
-
catch: (cause) =>
|
|
68
|
+
catch: (cause) => new QueueOperationError({
|
|
69
|
+
binding,
|
|
70
|
+
operation,
|
|
71
|
+
cause,
|
|
72
|
+
message: `Cloudflare queue binding "${binding}" operation "${operation}" failed`
|
|
73
|
+
})
|
|
72
74
|
});
|
|
73
75
|
const isQueue = (value) => {
|
|
74
76
|
if (typeof value !== "object" || value === null) return false;
|
|
75
|
-
|
|
76
|
-
return typeof resource.send === "function" && typeof resource.sendBatch === "function" && typeof resource.metrics === "function";
|
|
77
|
+
return typeof value.send === "function";
|
|
77
78
|
};
|
|
78
79
|
const makeClient$6 = (definition) => {
|
|
79
80
|
const encodeMessage = Schema.encodeEffect(definition.message);
|
|
@@ -88,13 +89,32 @@ const makeClient$6 = (definition) => {
|
|
|
88
89
|
...message,
|
|
89
90
|
body: yield* encodeMessage(message.body)
|
|
90
91
|
});
|
|
91
|
-
|
|
92
|
+
const sendBatch = queue.sendBatch;
|
|
93
|
+
if (sendBatch !== void 0) {
|
|
94
|
+
yield* tryQueuePromise(definition.binding, "sendBatch", () => sendBatch.call(queue, encodedMessages, options));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
yield* tryQueuePromise(definition.binding, "sendBatch", async () => {
|
|
98
|
+
for (const message of encodedMessages) await queue.send(message.body, {
|
|
99
|
+
contentType: message.contentType,
|
|
100
|
+
delaySeconds: message.delaySeconds ?? options?.delaySeconds
|
|
101
|
+
});
|
|
102
|
+
});
|
|
92
103
|
}),
|
|
93
|
-
metrics: () =>
|
|
104
|
+
metrics: () => {
|
|
105
|
+
const metrics = queue.metrics;
|
|
106
|
+
if (metrics === void 0) return Effect.fail(new QueueOperationError({
|
|
107
|
+
binding: definition.binding,
|
|
108
|
+
operation: "metrics",
|
|
109
|
+
cause: /* @__PURE__ */ new Error(`Queue binding "${definition.binding}" does not provide metrics()`),
|
|
110
|
+
message: `Cloudflare queue binding "${definition.binding}" does not provide metrics()`
|
|
111
|
+
}));
|
|
112
|
+
return tryQueuePromise(definition.binding, "metrics", () => metrics.call(queue));
|
|
113
|
+
},
|
|
94
114
|
unsafeRaw: Effect.succeed(queue)
|
|
95
115
|
});
|
|
96
116
|
};
|
|
97
|
-
const layer$7 = (tag, definition) => layer$8(tag, definition.binding, (value) => isQueue(value), makeClient$6(definition));
|
|
117
|
+
const layer$7 = (tag, definition) => layer$8(tag, definition.binding, (value) => isQueue(value), makeClient$6(definition), { expected: expectedQueueProducer });
|
|
98
118
|
//#endregion
|
|
99
119
|
//#region src/QueueDefinition.ts
|
|
100
120
|
var QueueDefinition_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -378,6 +398,7 @@ var ServiceBinding_exports = /* @__PURE__ */ __exportAll({
|
|
|
378
398
|
makeDirectMethods: () => makeDirectMethods$1
|
|
379
399
|
});
|
|
380
400
|
const TypeId$1 = "effect-cf/ServiceBinding";
|
|
401
|
+
const expectedServiceBinding = "Worker service binding with fetch()";
|
|
381
402
|
/**
|
|
382
403
|
* Failure raised when calling `fetch` on a service binding.
|
|
383
404
|
*/
|
|
@@ -436,7 +457,7 @@ const makeClient$5 = (definition) => {
|
|
|
436
457
|
});
|
|
437
458
|
};
|
|
438
459
|
};
|
|
439
|
-
const layer$6 = (tag, definition) => layer$8(tag, definition.binding, (value) => isServiceBindingClient(value), makeClient$5(definition));
|
|
460
|
+
const layer$6 = (tag, definition) => layer$8(tag, definition.binding, (value) => isServiceBindingClient(value), makeClient$5(definition), { expected: expectedServiceBinding });
|
|
440
461
|
/**
|
|
441
462
|
* Creates a typed Effect service for a Worker service binding.
|
|
442
463
|
*
|
|
@@ -1003,6 +1024,7 @@ var DurableObjectNamespace_exports = /* @__PURE__ */ __exportAll({
|
|
|
1003
1024
|
makeClient: () => makeClient$4,
|
|
1004
1025
|
makeDirectMethods: () => makeDirectMethods
|
|
1005
1026
|
});
|
|
1027
|
+
const expectedDurableObjectNamespace = "Durable Object namespace binding with getByName(), get(), idFromName(), idFromString(), newUniqueId(), and jurisdiction()";
|
|
1006
1028
|
/**
|
|
1007
1029
|
* Failure raised when invoking an RPC method on a Durable Object stub.
|
|
1008
1030
|
*/
|
|
@@ -1080,7 +1102,7 @@ const makeClient$4 = (definition) => {
|
|
|
1080
1102
|
});
|
|
1081
1103
|
};
|
|
1082
1104
|
};
|
|
1083
|
-
const layer$5 = (tag, definition) => layer$8(tag, definition.binding, (value) => isDurableObjectNamespaceClient(value), makeClient$4(definition));
|
|
1105
|
+
const layer$5 = (tag, definition) => layer$8(tag, definition.binding, (value) => isDurableObjectNamespaceClient(value), makeClient$4(definition), { expected: expectedDurableObjectNamespace });
|
|
1084
1106
|
const makeDirectMethods = (rpcDefinition, helpers) => {
|
|
1085
1107
|
const methods = {};
|
|
1086
1108
|
if (rpcDefinition !== void 0) {
|
|
@@ -1752,6 +1774,7 @@ var Images_exports = /* @__PURE__ */ __exportAll({
|
|
|
1752
1774
|
transform: () => transform
|
|
1753
1775
|
});
|
|
1754
1776
|
const TypeId = "effect-cf/Images/Steps";
|
|
1777
|
+
const expectedImagesBinding = "Images binding with info() and input()";
|
|
1755
1778
|
/** Error raised when a Cloudflare Images operation fails. */
|
|
1756
1779
|
var ImagesOperationError = class extends Data.TaggedError("ImagesOperationError") {};
|
|
1757
1780
|
const makeSteps = (steps) => ({
|
|
@@ -1834,7 +1857,7 @@ const makeClient$3 = (definition) => (images) => {
|
|
|
1834
1857
|
unsafeRaw: Effect.succeed(images)
|
|
1835
1858
|
};
|
|
1836
1859
|
};
|
|
1837
|
-
const layer$3 = (tag, definition) => layer$8(tag, definition.binding, isImagesBinding, makeClient$3(definition));
|
|
1860
|
+
const layer$3 = (tag, definition) => layer$8(tag, definition.binding, isImagesBinding, makeClient$3(definition), { expected: expectedImagesBinding });
|
|
1838
1861
|
const make$3 = (id) => Tag$4()(id);
|
|
1839
1862
|
const Tag$4 = () => (id) => {
|
|
1840
1863
|
const tag = Context.Service()(id);
|
|
@@ -1853,6 +1876,7 @@ var Kv_exports = /* @__PURE__ */ __exportAll({
|
|
|
1853
1876
|
layer: () => layer$2,
|
|
1854
1877
|
makeClient: () => makeClient$2
|
|
1855
1878
|
});
|
|
1879
|
+
const expectedKvNamespace = "KV namespace binding with get(), put(), delete(), getWithMetadata(), and list()";
|
|
1856
1880
|
/** Error raised when a KV operation fails. */
|
|
1857
1881
|
var KvOperationError = class extends Data.TaggedError("KvOperationError") {};
|
|
1858
1882
|
const maybeString = (value) => value == null ? Option.none() : Option.some(value);
|
|
@@ -1928,7 +1952,7 @@ const makeClient$2 = (definition) => {
|
|
|
1928
1952
|
unsafeRaw: Effect.succeed(kv)
|
|
1929
1953
|
});
|
|
1930
1954
|
};
|
|
1931
|
-
const layer$2 = (tag, definition) => layer$8(tag, definition.binding, isKvNamespace, makeClient$2(definition));
|
|
1955
|
+
const layer$2 = (tag, definition) => layer$8(tag, definition.binding, isKvNamespace, makeClient$2(definition), { expected: expectedKvNamespace });
|
|
1932
1956
|
const makeDefinition$1 = (id, definition) => {
|
|
1933
1957
|
const kvDefinition = {
|
|
1934
1958
|
id,
|
|
@@ -1962,6 +1986,7 @@ var R2_exports = /* @__PURE__ */ __exportAll({
|
|
|
1962
1986
|
make: () => make$2,
|
|
1963
1987
|
makeClient: () => makeClient$1
|
|
1964
1988
|
});
|
|
1989
|
+
const expectedR2Bucket = "R2 bucket binding with head(), get(), put(), createMultipartUpload(), resumeMultipartUpload(), delete(), and list()";
|
|
1965
1990
|
/** Error raised when an R2 operation fails. */
|
|
1966
1991
|
var R2OperationError = class extends Data.TaggedError("R2OperationError") {};
|
|
1967
1992
|
const r2Error = (binding, operation, cause) => new R2OperationError({
|
|
@@ -1978,6 +2003,36 @@ const tryR2Sync = (binding, operation, evaluate) => Effect.try({
|
|
|
1978
2003
|
catch: (cause) => r2Error(binding, operation, cause)
|
|
1979
2004
|
});
|
|
1980
2005
|
const maybe = (value) => value === null ? Option.none() : Option.some(value);
|
|
2006
|
+
const isR2ObjectBody = (value) => "body" in value && typeof value.arrayBuffer === "function" && typeof value.bytes === "function" && typeof value.text === "function" && typeof value.json === "function" && typeof value.blob === "function";
|
|
2007
|
+
const wrapObjectBody = (binding, object) => ({
|
|
2008
|
+
key: object.key,
|
|
2009
|
+
version: object.version,
|
|
2010
|
+
size: object.size,
|
|
2011
|
+
etag: object.etag,
|
|
2012
|
+
httpEtag: object.httpEtag,
|
|
2013
|
+
checksums: object.checksums,
|
|
2014
|
+
uploaded: object.uploaded,
|
|
2015
|
+
httpMetadata: object.httpMetadata,
|
|
2016
|
+
customMetadata: object.customMetadata,
|
|
2017
|
+
range: object.range,
|
|
2018
|
+
storageClass: object.storageClass,
|
|
2019
|
+
ssecKeyMd5: object.ssecKeyMd5,
|
|
2020
|
+
writeHttpMetadata: (headers) => object.writeHttpMetadata(headers),
|
|
2021
|
+
raw: object,
|
|
2022
|
+
body: object.body,
|
|
2023
|
+
get bodyUsed() {
|
|
2024
|
+
return object.bodyUsed;
|
|
2025
|
+
},
|
|
2026
|
+
arrayBuffer: tryR2Promise(binding, "arrayBuffer", () => object.arrayBuffer()),
|
|
2027
|
+
bytes: tryR2Promise(binding, "bytes", () => object.bytes()),
|
|
2028
|
+
text: tryR2Promise(binding, "text", () => object.text()),
|
|
2029
|
+
json: () => tryR2Promise(binding, "json", () => object.json()),
|
|
2030
|
+
blob: tryR2Promise(binding, "blob", () => object.blob())
|
|
2031
|
+
});
|
|
2032
|
+
const wrapGetResult = (binding, object) => {
|
|
2033
|
+
if (object === null || !isR2ObjectBody(object)) return object;
|
|
2034
|
+
return wrapObjectBody(binding, object);
|
|
2035
|
+
};
|
|
1981
2036
|
const isR2Bucket = (value) => {
|
|
1982
2037
|
if (typeof value !== "object" || value === null) return false;
|
|
1983
2038
|
const resource = value;
|
|
@@ -1992,26 +2047,29 @@ const makeClient$1 = (definition) => {
|
|
|
1992
2047
|
abort: tryR2Promise(definition.binding, "abortMultipartUpload", () => upload.abort()),
|
|
1993
2048
|
complete: (uploadedParts) => tryR2Promise(definition.binding, "completeMultipartUpload", () => upload.complete([...uploadedParts]))
|
|
1994
2049
|
});
|
|
1995
|
-
return (bucket) =>
|
|
1996
|
-
definition,
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2050
|
+
return (bucket) => {
|
|
2051
|
+
const get = ((key, options) => tryR2Promise(definition.binding, "get", () => bucket.get(key, options)).pipe(Effect.map((object) => maybe(wrapGetResult(definition.binding, object)))));
|
|
2052
|
+
return {
|
|
2053
|
+
definition,
|
|
2054
|
+
head: (key) => tryR2Promise(definition.binding, "head", () => bucket.head(key)).pipe(Effect.map(maybe)),
|
|
2055
|
+
get,
|
|
2056
|
+
put: ((key, value, options) => tryR2Promise(definition.binding, "put", () => bucket.put(key, value, options)).pipe(Effect.map((object) => {
|
|
2057
|
+
if (object === null) return Option.none();
|
|
2058
|
+
if (options !== void 0 && "onlyIf" in options) return Option.some(object);
|
|
2059
|
+
return object;
|
|
2060
|
+
}))),
|
|
2061
|
+
createMultipartUpload: (key, options) => tryR2Promise(definition.binding, "createMultipartUpload", () => bucket.createMultipartUpload(key, options)).pipe(Effect.map(wrapUpload)),
|
|
2062
|
+
resumeMultipartUpload: (key, uploadId) => tryR2Sync(definition.binding, "resumeMultipartUpload", () => wrapUpload(bucket.resumeMultipartUpload(key, uploadId))),
|
|
2063
|
+
delete: (keys) => {
|
|
2064
|
+
const nativeKeys = typeof keys === "string" ? keys : [...keys];
|
|
2065
|
+
return tryR2Promise(definition.binding, "delete", () => bucket.delete(nativeKeys));
|
|
2066
|
+
},
|
|
2067
|
+
list: (options) => tryR2Promise(definition.binding, "list", () => bucket.list(options)),
|
|
2068
|
+
unsafeRaw: Effect.succeed(bucket)
|
|
2069
|
+
};
|
|
2070
|
+
};
|
|
2013
2071
|
};
|
|
2014
|
-
const layer$1 = (tag, definition) => layer$8(tag, definition.binding, isR2Bucket, makeClient$1(definition));
|
|
2072
|
+
const layer$1 = (tag, definition) => layer$8(tag, definition.binding, isR2Bucket, makeClient$1(definition), { expected: expectedR2Bucket });
|
|
2015
2073
|
const make$2 = (id) => Tag$2()(id);
|
|
2016
2074
|
const Tag$2 = () => (id) => {
|
|
2017
2075
|
const tag = Context.Service()(id);
|
|
@@ -2030,6 +2088,7 @@ var WorkflowBinding_exports = /* @__PURE__ */ __exportAll({
|
|
|
2030
2088
|
layer: () => layer,
|
|
2031
2089
|
makeClient: () => makeClient
|
|
2032
2090
|
});
|
|
2091
|
+
const expectedWorkflow = "Workflow binding with create(), createBatch(), and get()";
|
|
2033
2092
|
var WorkflowOperationError = class extends Data.TaggedError("WorkflowOperationError") {};
|
|
2034
2093
|
var WorkflowResultDecodeError = class extends Data.TaggedError("WorkflowResultDecodeError") {};
|
|
2035
2094
|
const workflowError = (binding, operation, cause) => new WorkflowOperationError({
|
|
@@ -2096,7 +2155,7 @@ const makeClient = (definition) => {
|
|
|
2096
2155
|
unsafeRaw: Effect.succeed(workflow)
|
|
2097
2156
|
});
|
|
2098
2157
|
};
|
|
2099
|
-
const layer = (tag, definition) => layer$8(tag, definition.binding, (value) => isWorkflow(value), makeClient(definition));
|
|
2158
|
+
const layer = (tag, definition) => layer$8(tag, definition.binding, (value) => isWorkflow(value), makeClient(definition), { expected: expectedWorkflow });
|
|
2100
2159
|
//#endregion
|
|
2101
2160
|
//#region src/WorkflowDefinition.ts
|
|
2102
2161
|
var WorkflowDefinition_exports = /* @__PURE__ */ __exportAll({
|
package/package.json
CHANGED
package/src/Binding.ts
CHANGED
|
@@ -9,34 +9,150 @@ export const TypeId = "effect-cf/Binding" as const;
|
|
|
9
9
|
/** Error raised when a configured binding does not exist on `env`. */
|
|
10
10
|
export class BindingNotFoundError extends Data.TaggedError("BindingNotFoundError")<{
|
|
11
11
|
readonly binding: string;
|
|
12
|
+
readonly message: string;
|
|
12
13
|
}> {}
|
|
13
14
|
|
|
14
15
|
/** Error raised when a binding exists but does not match the expected shape. */
|
|
15
16
|
export class BindingValidationError extends Data.TaggedError("BindingValidationError")<{
|
|
16
17
|
readonly binding: string;
|
|
18
|
+
readonly expected: string;
|
|
19
|
+
readonly actual: string;
|
|
20
|
+
readonly message: string;
|
|
17
21
|
}> {}
|
|
18
22
|
|
|
23
|
+
export interface ValidationOptions {
|
|
24
|
+
readonly expected?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const defaultExpected = "Cloudflare binding resource";
|
|
28
|
+
|
|
19
29
|
const isPropertyTarget = (value: unknown): value is object =>
|
|
20
30
|
(typeof value === "object" || typeof value === "function") && value !== null;
|
|
21
31
|
|
|
32
|
+
const getObjectName = (value: object | Function): string => {
|
|
33
|
+
const tag = (() => {
|
|
34
|
+
try {
|
|
35
|
+
return Object.prototype.toString.call(value).slice("[object ".length, -1);
|
|
36
|
+
} catch {
|
|
37
|
+
return typeof value;
|
|
38
|
+
}
|
|
39
|
+
})();
|
|
40
|
+
const constructorName = (() => {
|
|
41
|
+
try {
|
|
42
|
+
return "constructor" in value &&
|
|
43
|
+
typeof value.constructor === "function" &&
|
|
44
|
+
typeof value.constructor.name === "string"
|
|
45
|
+
? value.constructor.name
|
|
46
|
+
: undefined;
|
|
47
|
+
} catch {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
|
|
52
|
+
if (tag !== "Object") {
|
|
53
|
+
return tag;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (constructorName !== undefined && constructorName !== "" && constructorName !== "Object") {
|
|
57
|
+
return constructorName;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return tag;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const propertyNames = (value: object | Function): ReadonlyArray<string> => {
|
|
64
|
+
const names = new Set<string>();
|
|
65
|
+
|
|
66
|
+
for (const target of [value, Object.getPrototypeOf(value)] as const) {
|
|
67
|
+
if (target === null || target === Object.prototype || target === Function.prototype) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
for (const name of Object.getOwnPropertyNames(target)) {
|
|
73
|
+
names.add(name);
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return [...names].filter((name) => name !== "constructor").sort();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const isMethod = (value: object | Function, name: string): boolean => {
|
|
84
|
+
try {
|
|
85
|
+
return typeof Reflect.get(value, name) === "function";
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const describeActual = (value: unknown): string => {
|
|
92
|
+
if (value === null) {
|
|
93
|
+
return "null";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (typeof value !== "object" && typeof value !== "function") {
|
|
97
|
+
return typeof value;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const names = propertyNames(value);
|
|
101
|
+
const methods = names.filter((name) => isMethod(value, name));
|
|
102
|
+
const properties = names.filter((name) => !methods.includes(name));
|
|
103
|
+
const details = [
|
|
104
|
+
methods.length > 0 ? `methods ${methods.join(", ")}` : undefined,
|
|
105
|
+
properties.length > 0 ? `properties ${properties.join(", ")}` : undefined,
|
|
106
|
+
].filter((detail) => detail !== undefined);
|
|
107
|
+
|
|
108
|
+
if (details.length === 0) {
|
|
109
|
+
return getObjectName(value);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return `${getObjectName(value)} with ${details.join("; ")}`;
|
|
113
|
+
};
|
|
114
|
+
|
|
22
115
|
const getBinding = <Resource>(
|
|
23
116
|
env: WorkerEnv,
|
|
24
117
|
binding: string,
|
|
25
118
|
isResource: (value: unknown) => value is Resource,
|
|
119
|
+
options?: ValidationOptions,
|
|
26
120
|
): Effect.Effect<Resource, BindingNotFoundError | BindingValidationError> =>
|
|
27
121
|
Effect.gen(function* () {
|
|
28
122
|
if (!isPropertyTarget(env)) {
|
|
29
|
-
|
|
123
|
+
const actual = describeActual(env);
|
|
124
|
+
return yield* Effect.fail(
|
|
125
|
+
new BindingValidationError({
|
|
126
|
+
binding,
|
|
127
|
+
expected: "WorkerEnvironment object",
|
|
128
|
+
actual,
|
|
129
|
+
message: `Cloudflare binding "${binding}" failed validation. Expected WorkerEnvironment object; got ${actual}`,
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
30
132
|
}
|
|
31
133
|
|
|
32
134
|
const resource = Reflect.get(env, binding);
|
|
33
135
|
|
|
34
136
|
if (resource === undefined) {
|
|
35
|
-
return yield* Effect.fail(
|
|
137
|
+
return yield* Effect.fail(
|
|
138
|
+
new BindingNotFoundError({
|
|
139
|
+
binding,
|
|
140
|
+
message: `Cloudflare binding "${binding}" was not found in WorkerEnvironment`,
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
36
143
|
}
|
|
37
144
|
|
|
38
145
|
if (!isResource(resource)) {
|
|
39
|
-
|
|
146
|
+
const expected = options?.expected ?? defaultExpected;
|
|
147
|
+
const actual = describeActual(resource);
|
|
148
|
+
return yield* Effect.fail(
|
|
149
|
+
new BindingValidationError({
|
|
150
|
+
binding,
|
|
151
|
+
expected,
|
|
152
|
+
actual,
|
|
153
|
+
message: `Cloudflare binding "${binding}" failed validation. Expected ${expected}; got ${actual}`,
|
|
154
|
+
}),
|
|
155
|
+
);
|
|
40
156
|
}
|
|
41
157
|
|
|
42
158
|
return resource;
|
|
@@ -64,12 +180,13 @@ export const layer = <Self, Resource, Service = Resource>(
|
|
|
64
180
|
binding: string,
|
|
65
181
|
isResource: (value: unknown) => value is Resource,
|
|
66
182
|
wrap?: (resource: Resource) => Service,
|
|
183
|
+
options?: ValidationOptions,
|
|
67
184
|
): Layer.Layer<Self, BindingNotFoundError | BindingValidationError, WorkerEnvironment> =>
|
|
68
185
|
Layer.effect(
|
|
69
186
|
tag,
|
|
70
187
|
Effect.gen(function* () {
|
|
71
188
|
const env = yield* WorkerEnvironment;
|
|
72
|
-
const resource = yield* getBinding(env, binding, isResource);
|
|
189
|
+
const resource = yield* getBinding(env, binding, isResource, options);
|
|
73
190
|
return wrap === undefined ? (resource as unknown as Service) : wrap(resource);
|
|
74
191
|
}),
|
|
75
192
|
);
|
|
@@ -81,9 +198,10 @@ export const Service =
|
|
|
81
198
|
binding: string,
|
|
82
199
|
isResource: (value: unknown) => value is Resource,
|
|
83
200
|
wrap?: (resource: Resource) => Service,
|
|
201
|
+
options?: ValidationOptions,
|
|
84
202
|
): BindingService<Self, Id, Service> => {
|
|
85
203
|
const tag = Context.Service<Self, Service>()(id);
|
|
86
|
-
const serviceLayer = layer(tag, binding, isResource, wrap);
|
|
204
|
+
const serviceLayer = layer(tag, binding, isResource, wrap, options);
|
|
87
205
|
|
|
88
206
|
return Object.assign(tag, {
|
|
89
207
|
[TypeId]: TypeId,
|
package/src/D1.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Effect, Layer } from "effect";
|
|
|
4
4
|
import * as Binding from "./Binding";
|
|
5
5
|
|
|
6
6
|
const TypeId = "effect-cf/D1" as const;
|
|
7
|
+
const expectedD1Database = "D1 database binding with prepare(), batch(), and exec()";
|
|
7
8
|
|
|
8
9
|
/** Typed D1 binding definition. */
|
|
9
10
|
export interface D1Definition {
|
|
@@ -61,7 +62,9 @@ export const make = <Id extends string>(id: Id, definition: D1Definition) =>
|
|
|
61
62
|
export const Service =
|
|
62
63
|
<Self>() =>
|
|
63
64
|
<Id extends string>(id: Id, definition: D1Definition) => {
|
|
64
|
-
const tag = Binding.Service<Self>()(id, definition.binding, isD1Database
|
|
65
|
+
const tag = Binding.Service<Self>()(id, definition.binding, isD1Database, undefined, {
|
|
66
|
+
expected: expectedD1Database,
|
|
67
|
+
});
|
|
65
68
|
|
|
66
69
|
const sqlLayer = (options?: D1SqlLayerOptions) =>
|
|
67
70
|
Layer.unwrap(
|
|
@@ -6,6 +6,9 @@ import type * as DurableObjectDefinition from "./DurableObjectDefinition";
|
|
|
6
6
|
import * as RpcDefinition from "./RpcDefinition";
|
|
7
7
|
import * as RpcInvocation from "./internal/RpcInvocation";
|
|
8
8
|
|
|
9
|
+
const expectedDurableObjectNamespace =
|
|
10
|
+
"Durable Object namespace binding with getByName(), get(), idFromName(), idFromString(), newUniqueId(), and jurisdiction()";
|
|
11
|
+
|
|
9
12
|
interface DurableObjectFetcher {
|
|
10
13
|
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
11
14
|
}
|
|
@@ -548,6 +551,7 @@ export const layer = <
|
|
|
548
551
|
(value): value is DurableObjectNamespaceClient<Api> =>
|
|
549
552
|
isDurableObjectNamespaceClient<Api>(value),
|
|
550
553
|
makeClient<Api, Definition>(definition),
|
|
554
|
+
{ expected: expectedDurableObjectNamespace },
|
|
551
555
|
);
|
|
552
556
|
|
|
553
557
|
export const makeDirectMethods = <
|
package/src/Hyperdrive.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { Context, Effect, type Layer } from "effect";
|
|
|
3
3
|
import * as Binding from "./Binding";
|
|
4
4
|
import type { WorkerEnvironment } from "./Environment";
|
|
5
5
|
|
|
6
|
+
const expectedHyperdrive = "Hyperdrive binding with connectionString";
|
|
7
|
+
|
|
6
8
|
/** Typed Hyperdrive binding definition. */
|
|
7
9
|
export interface HyperdriveDefinition {
|
|
8
10
|
/** Binding name as configured in `wrangler.jsonc`. */
|
|
@@ -64,7 +66,10 @@ export const makeClient =
|
|
|
64
66
|
export const layer = <Self>(
|
|
65
67
|
tag: Context.Service<Self, HyperdriveClient>,
|
|
66
68
|
definition: HyperdriveDefinition,
|
|
67
|
-
) =>
|
|
69
|
+
) =>
|
|
70
|
+
Binding.layer(tag, definition.binding, isHyperdrive, makeClient(definition), {
|
|
71
|
+
expected: expectedHyperdrive,
|
|
72
|
+
});
|
|
68
73
|
|
|
69
74
|
export const make = <Id extends string>(id: Id) => Tag<HyperdriveService<Id>>()<Id>(id);
|
|
70
75
|
|
package/src/Images.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as Binding from "./Binding";
|
|
|
4
4
|
import type { WorkerEnvironment } from "./Environment";
|
|
5
5
|
|
|
6
6
|
const TypeId = "effect-cf/Images/Steps" as const;
|
|
7
|
+
const expectedImagesBinding = "Images binding with info() and input()";
|
|
7
8
|
|
|
8
9
|
/** Error raised when a Cloudflare Images operation fails. */
|
|
9
10
|
export class ImagesOperationError extends Data.TaggedError("ImagesOperationError")<{
|
|
@@ -314,7 +315,10 @@ export const makeClient =
|
|
|
314
315
|
export const layer = <Self>(
|
|
315
316
|
tag: Context.Service<Self, ImagesClient>,
|
|
316
317
|
definition: ImagesDefinition,
|
|
317
|
-
) =>
|
|
318
|
+
) =>
|
|
319
|
+
Binding.layer(tag, definition.binding, isImagesBinding, makeClient(definition), {
|
|
320
|
+
expected: expectedImagesBinding,
|
|
321
|
+
});
|
|
318
322
|
|
|
319
323
|
export const make = <Id extends string>(id: Id) => Tag<ImagesService<Id>>()<Id>(id);
|
|
320
324
|
|
package/src/Kv.ts
CHANGED
|
@@ -3,6 +3,9 @@ import { Context, Data, Effect, Option, Schema as S, type Layer } from "effect";
|
|
|
3
3
|
import * as Binding from "./Binding";
|
|
4
4
|
import type { WorkerEnvironment } from "./Environment";
|
|
5
5
|
|
|
6
|
+
const expectedKvNamespace =
|
|
7
|
+
"KV namespace binding with get(), put(), delete(), getWithMetadata(), and list()";
|
|
8
|
+
|
|
6
9
|
/** Error raised when a KV operation fails. */
|
|
7
10
|
export class KvOperationError extends Data.TaggedError("KvOperationError")<{
|
|
8
11
|
readonly binding: string;
|
|
@@ -250,6 +253,7 @@ export const layer = <Self, Key, Value, EncodedValue>(
|
|
|
250
253
|
definition.binding,
|
|
251
254
|
isKvNamespace,
|
|
252
255
|
makeClient<Key, Value, EncodedValue>(definition),
|
|
256
|
+
{ expected: expectedKvNamespace },
|
|
253
257
|
);
|
|
254
258
|
|
|
255
259
|
const makeDefinition = <Id extends string, Key, Value, EncodedValue>(
|
package/src/QueueBinding.ts
CHANGED
|
@@ -10,6 +10,11 @@ export type QueueSendBatchResponse = globalThis.QueueSendBatchResponse;
|
|
|
10
10
|
export type QueueMetrics = globalThis.QueueMetrics;
|
|
11
11
|
export type MessageSendRequest<Body> = globalThis.MessageSendRequest<Body>;
|
|
12
12
|
|
|
13
|
+
export type QueueProducer<Body> = Pick<globalThis.Queue<Body>, "send"> &
|
|
14
|
+
Partial<Pick<globalThis.Queue<Body>, "sendBatch" | "metrics">>;
|
|
15
|
+
|
|
16
|
+
const expectedQueueProducer = "Queue producer binding with send(); optional sendBatch()/metrics()";
|
|
17
|
+
|
|
13
18
|
export interface QueueBindingDefinition<Message extends RpcDefinition.ServiceFreeSchema> {
|
|
14
19
|
/** Binding name as configured in `wrangler.jsonc`. */
|
|
15
20
|
readonly binding: string;
|
|
@@ -34,11 +39,9 @@ export class QueueOperationError extends Data.TaggedError("QueueOperationError")
|
|
|
34
39
|
readonly binding: string;
|
|
35
40
|
readonly operation: string;
|
|
36
41
|
readonly cause: unknown;
|
|
42
|
+
readonly message: string;
|
|
37
43
|
}> {}
|
|
38
44
|
|
|
39
|
-
const queueError = (binding: string, operation: string, cause: unknown) =>
|
|
40
|
-
new QueueOperationError({ binding, operation, cause });
|
|
41
|
-
|
|
42
45
|
const tryQueuePromise = <A>(
|
|
43
46
|
binding: string,
|
|
44
47
|
operation: string,
|
|
@@ -46,26 +49,28 @@ const tryQueuePromise = <A>(
|
|
|
46
49
|
): Effect.Effect<A, QueueOperationError> =>
|
|
47
50
|
Effect.tryPromise({
|
|
48
51
|
try: evaluate,
|
|
49
|
-
catch: (cause) =>
|
|
52
|
+
catch: (cause) =>
|
|
53
|
+
new QueueOperationError({
|
|
54
|
+
binding,
|
|
55
|
+
operation,
|
|
56
|
+
cause,
|
|
57
|
+
message: `Cloudflare queue binding "${binding}" operation "${operation}" failed`,
|
|
58
|
+
}),
|
|
50
59
|
});
|
|
51
60
|
|
|
52
|
-
export const isQueue = <Body>(value: unknown): value is
|
|
61
|
+
export const isQueue = <Body>(value: unknown): value is QueueProducer<Body> => {
|
|
53
62
|
if (typeof value !== "object" || value === null) {
|
|
54
63
|
return false;
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
const resource = value as Record<string, unknown>;
|
|
58
67
|
|
|
59
|
-
return
|
|
60
|
-
typeof resource.send === "function" &&
|
|
61
|
-
typeof resource.sendBatch === "function" &&
|
|
62
|
-
typeof resource.metrics === "function"
|
|
63
|
-
);
|
|
68
|
+
return typeof resource.send === "function";
|
|
64
69
|
};
|
|
65
70
|
|
|
66
71
|
export const makeClient = <Message extends RpcDefinition.ServiceFreeSchema>(
|
|
67
72
|
definition: QueueBindingDefinition<Message>,
|
|
68
|
-
): ((queue:
|
|
73
|
+
): ((queue: QueueProducer<S.Codec.Encoded<Message>>) => QueueBindingClient<Message>) => {
|
|
69
74
|
type Body = S.Schema.Type<Message>;
|
|
70
75
|
type EncodedBody = S.Codec.Encoded<Message>;
|
|
71
76
|
|
|
@@ -90,12 +95,41 @@ export const makeClient = <Message extends RpcDefinition.ServiceFreeSchema>(
|
|
|
90
95
|
});
|
|
91
96
|
}
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
98
|
+
const sendBatch = queue.sendBatch;
|
|
99
|
+
|
|
100
|
+
if (sendBatch !== undefined) {
|
|
101
|
+
yield* tryQueuePromise(definition.binding, "sendBatch", () =>
|
|
102
|
+
sendBatch.call(queue, encodedMessages, options),
|
|
103
|
+
);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
yield* tryQueuePromise(definition.binding, "sendBatch", async () => {
|
|
108
|
+
for (const message of encodedMessages) {
|
|
109
|
+
await queue.send(message.body, {
|
|
110
|
+
contentType: message.contentType,
|
|
111
|
+
delaySeconds: message.delaySeconds ?? options?.delaySeconds,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
96
115
|
}),
|
|
97
|
-
metrics: () =>
|
|
98
|
-
|
|
116
|
+
metrics: () => {
|
|
117
|
+
const metrics = queue.metrics;
|
|
118
|
+
|
|
119
|
+
if (metrics === undefined) {
|
|
120
|
+
return Effect.fail(
|
|
121
|
+
new QueueOperationError({
|
|
122
|
+
binding: definition.binding,
|
|
123
|
+
operation: "metrics",
|
|
124
|
+
cause: new Error(`Queue binding "${definition.binding}" does not provide metrics()`),
|
|
125
|
+
message: `Cloudflare queue binding "${definition.binding}" does not provide metrics()`,
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return tryQueuePromise(definition.binding, "metrics", () => metrics.call(queue));
|
|
131
|
+
},
|
|
132
|
+
unsafeRaw: Effect.succeed(queue as globalThis.Queue<EncodedBody>),
|
|
99
133
|
});
|
|
100
134
|
};
|
|
101
135
|
|
|
@@ -106,7 +140,8 @@ export const layer = <Self, Message extends RpcDefinition.ServiceFreeSchema>(
|
|
|
106
140
|
Binding.layer(
|
|
107
141
|
tag,
|
|
108
142
|
definition.binding,
|
|
109
|
-
(value): value is
|
|
143
|
+
(value): value is QueueProducer<S.Codec.Encoded<Message>> =>
|
|
110
144
|
isQueue<S.Codec.Encoded<Message>>(value),
|
|
111
145
|
makeClient(definition),
|
|
146
|
+
{ expected: expectedQueueProducer },
|
|
112
147
|
);
|
package/src/R2.ts
CHANGED
|
@@ -3,6 +3,9 @@ import { Context, Data, Effect, Option, type Layer } from "effect";
|
|
|
3
3
|
import * as Binding from "./Binding";
|
|
4
4
|
import type { WorkerEnvironment } from "./Environment";
|
|
5
5
|
|
|
6
|
+
const expectedR2Bucket =
|
|
7
|
+
"R2 bucket binding with head(), get(), put(), createMultipartUpload(), resumeMultipartUpload(), delete(), and list()";
|
|
8
|
+
|
|
6
9
|
/** Error raised when an R2 operation fails. */
|
|
7
10
|
export class R2OperationError extends Data.TaggedError("R2OperationError")<{
|
|
8
11
|
readonly binding: string;
|
|
@@ -24,6 +27,18 @@ export type R2UploadPartOptions = globalThis.R2UploadPartOptions;
|
|
|
24
27
|
export type R2PutValue = ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob;
|
|
25
28
|
export type R2UploadPartValue = ReadableStream | ArrayBuffer | ArrayBufferView | string | Blob;
|
|
26
29
|
|
|
30
|
+
export interface R2ObjectBodyClient extends Omit<
|
|
31
|
+
globalThis.R2ObjectBody,
|
|
32
|
+
"arrayBuffer" | "blob" | "bytes" | "json" | "text"
|
|
33
|
+
> {
|
|
34
|
+
readonly raw: globalThis.R2ObjectBody;
|
|
35
|
+
readonly arrayBuffer: Effect.Effect<ArrayBuffer, R2OperationError>;
|
|
36
|
+
readonly bytes: Effect.Effect<Uint8Array, R2OperationError>;
|
|
37
|
+
readonly text: Effect.Effect<string, R2OperationError>;
|
|
38
|
+
readonly json: <T = unknown>() => Effect.Effect<T, R2OperationError>;
|
|
39
|
+
readonly blob: Effect.Effect<Blob, R2OperationError>;
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
export interface R2MultipartUploadClient {
|
|
28
43
|
readonly raw: globalThis.R2MultipartUpload;
|
|
29
44
|
readonly key: string;
|
|
@@ -47,14 +62,11 @@ export interface R2Client {
|
|
|
47
62
|
(
|
|
48
63
|
key: string,
|
|
49
64
|
options: R2GetOptions & { readonly onlyIf: globalThis.R2Conditional | Headers },
|
|
50
|
-
): Effect.Effect<
|
|
51
|
-
Option.Option<globalThis.R2ObjectBody | globalThis.R2Object>,
|
|
52
|
-
R2OperationError
|
|
53
|
-
>;
|
|
65
|
+
): Effect.Effect<Option.Option<R2ObjectBodyClient | globalThis.R2Object>, R2OperationError>;
|
|
54
66
|
(
|
|
55
67
|
key: string,
|
|
56
68
|
options?: R2GetOptions,
|
|
57
|
-
): Effect.Effect<Option.Option<
|
|
69
|
+
): Effect.Effect<Option.Option<R2ObjectBodyClient>, R2OperationError>;
|
|
58
70
|
};
|
|
59
71
|
readonly put: {
|
|
60
72
|
(
|
|
@@ -136,6 +148,53 @@ const tryR2Sync = <A>(
|
|
|
136
148
|
const maybe = <A>(value: A | null): Option.Option<A> =>
|
|
137
149
|
value === null ? Option.none() : Option.some(value);
|
|
138
150
|
|
|
151
|
+
const isR2ObjectBody = (
|
|
152
|
+
value: globalThis.R2ObjectBody | globalThis.R2Object,
|
|
153
|
+
): value is globalThis.R2ObjectBody =>
|
|
154
|
+
"body" in value &&
|
|
155
|
+
typeof (value as globalThis.R2ObjectBody).arrayBuffer === "function" &&
|
|
156
|
+
typeof (value as globalThis.R2ObjectBody).bytes === "function" &&
|
|
157
|
+
typeof (value as globalThis.R2ObjectBody).text === "function" &&
|
|
158
|
+
typeof (value as globalThis.R2ObjectBody).json === "function" &&
|
|
159
|
+
typeof (value as globalThis.R2ObjectBody).blob === "function";
|
|
160
|
+
|
|
161
|
+
const wrapObjectBody = (binding: string, object: globalThis.R2ObjectBody): R2ObjectBodyClient => ({
|
|
162
|
+
key: object.key,
|
|
163
|
+
version: object.version,
|
|
164
|
+
size: object.size,
|
|
165
|
+
etag: object.etag,
|
|
166
|
+
httpEtag: object.httpEtag,
|
|
167
|
+
checksums: object.checksums,
|
|
168
|
+
uploaded: object.uploaded,
|
|
169
|
+
httpMetadata: object.httpMetadata,
|
|
170
|
+
customMetadata: object.customMetadata,
|
|
171
|
+
range: object.range,
|
|
172
|
+
storageClass: object.storageClass,
|
|
173
|
+
ssecKeyMd5: object.ssecKeyMd5,
|
|
174
|
+
writeHttpMetadata: (headers) => object.writeHttpMetadata(headers),
|
|
175
|
+
raw: object,
|
|
176
|
+
body: object.body,
|
|
177
|
+
get bodyUsed() {
|
|
178
|
+
return object.bodyUsed;
|
|
179
|
+
},
|
|
180
|
+
arrayBuffer: tryR2Promise(binding, "arrayBuffer", () => object.arrayBuffer()),
|
|
181
|
+
bytes: tryR2Promise(binding, "bytes", () => object.bytes()),
|
|
182
|
+
text: tryR2Promise(binding, "text", () => object.text()),
|
|
183
|
+
json: <T = unknown>() => tryR2Promise(binding, "json", () => object.json<T>()),
|
|
184
|
+
blob: tryR2Promise(binding, "blob", () => object.blob()),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const wrapGetResult = (
|
|
188
|
+
binding: string,
|
|
189
|
+
object: globalThis.R2ObjectBody | globalThis.R2Object | null,
|
|
190
|
+
): R2ObjectBodyClient | globalThis.R2Object | null => {
|
|
191
|
+
if (object === null || !isR2ObjectBody(object)) {
|
|
192
|
+
return object;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return wrapObjectBody(binding, object);
|
|
196
|
+
};
|
|
197
|
+
|
|
139
198
|
export const isR2Bucket = (value: unknown): value is globalThis.R2Bucket => {
|
|
140
199
|
if (typeof value !== "object" || value === null) {
|
|
141
200
|
return false;
|
|
@@ -172,47 +231,53 @@ export const makeClient = (
|
|
|
172
231
|
),
|
|
173
232
|
});
|
|
174
233
|
|
|
175
|
-
return (bucket) =>
|
|
176
|
-
|
|
177
|
-
head: (key) =>
|
|
178
|
-
tryR2Promise(definition.binding, "head", () => bucket.head(key)).pipe(Effect.map(maybe)),
|
|
179
|
-
get: ((key: string, options?: R2GetOptions) =>
|
|
234
|
+
return (bucket) => {
|
|
235
|
+
const get = ((key: string, options?: R2GetOptions) =>
|
|
180
236
|
tryR2Promise(definition.binding, "get", () => bucket.get(key, options)).pipe(
|
|
181
|
-
Effect.map(maybe),
|
|
182
|
-
)) as R2Client["get"]
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
237
|
+
Effect.map((object) => maybe(wrapGetResult(definition.binding, object))),
|
|
238
|
+
)) as R2Client["get"];
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
definition,
|
|
242
|
+
head: (key) =>
|
|
243
|
+
tryR2Promise(definition.binding, "head", () => bucket.head(key)).pipe(Effect.map(maybe)),
|
|
244
|
+
get,
|
|
245
|
+
put: ((key: string, value: R2PutValue, options?: R2PutOptions) =>
|
|
246
|
+
tryR2Promise(definition.binding, "put", () => bucket.put(key, value, options)).pipe(
|
|
247
|
+
Effect.map((object) => {
|
|
248
|
+
if (object === null) {
|
|
249
|
+
return Option.none<globalThis.R2Object>();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (options !== undefined && "onlyIf" in options) {
|
|
253
|
+
return Option.some(object);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return object;
|
|
257
|
+
}),
|
|
258
|
+
)) as R2Client["put"],
|
|
259
|
+
createMultipartUpload: (key, options) =>
|
|
260
|
+
tryR2Promise(definition.binding, "createMultipartUpload", () =>
|
|
261
|
+
bucket.createMultipartUpload(key, options),
|
|
262
|
+
).pipe(Effect.map(wrapUpload)),
|
|
263
|
+
resumeMultipartUpload: (key, uploadId) =>
|
|
264
|
+
tryR2Sync(definition.binding, "resumeMultipartUpload", () =>
|
|
265
|
+
wrapUpload(bucket.resumeMultipartUpload(key, uploadId)),
|
|
266
|
+
),
|
|
267
|
+
delete: (keys) => {
|
|
268
|
+
const nativeKeys = typeof keys === "string" ? keys : [...keys];
|
|
269
|
+
return tryR2Promise(definition.binding, "delete", () => bucket.delete(nativeKeys));
|
|
270
|
+
},
|
|
271
|
+
list: (options) => tryR2Promise(definition.binding, "list", () => bucket.list(options)),
|
|
272
|
+
unsafeRaw: Effect.succeed(bucket),
|
|
273
|
+
};
|
|
274
|
+
};
|
|
212
275
|
};
|
|
213
276
|
|
|
214
277
|
export const layer = <Self>(tag: Context.Service<Self, R2Client>, definition: R2Definition) =>
|
|
215
|
-
Binding.layer(tag, definition.binding, isR2Bucket, makeClient(definition)
|
|
278
|
+
Binding.layer(tag, definition.binding, isR2Bucket, makeClient(definition), {
|
|
279
|
+
expected: expectedR2Bucket,
|
|
280
|
+
});
|
|
216
281
|
|
|
217
282
|
export const make = <Id extends string>(id: Id) => Tag<R2Service<Id>>()<Id>(id);
|
|
218
283
|
|
package/src/ServiceBinding.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type * as WorkerDefinition from "./WorkerDefinition";
|
|
|
7
7
|
import * as RpcInvocation from "./internal/RpcInvocation";
|
|
8
8
|
|
|
9
9
|
const TypeId = "effect-cf/ServiceBinding" as const;
|
|
10
|
+
const expectedServiceBinding = "Worker service binding with fetch()";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Minimum shape for a Cloudflare service binding.
|
|
@@ -359,6 +360,7 @@ export const layer = <
|
|
|
359
360
|
definition.binding,
|
|
360
361
|
(value): value is ServiceBindingClient<Api> => isServiceBindingClient<Api>(value),
|
|
361
362
|
makeClient<Api, Definition>(definition),
|
|
363
|
+
{ expected: expectedServiceBinding },
|
|
362
364
|
);
|
|
363
365
|
|
|
364
366
|
/**
|
package/src/WorkflowBinding.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { Context, Data, Effect, Option, Schema as S } from "effect";
|
|
|
3
3
|
import * as Binding from "./Binding";
|
|
4
4
|
import type * as RpcDefinition from "./RpcDefinition";
|
|
5
5
|
|
|
6
|
+
const expectedWorkflow = "Workflow binding with create(), createBatch(), and get()";
|
|
7
|
+
|
|
6
8
|
export type WorkflowInstanceCreateOptions<Payload> = Omit<
|
|
7
9
|
globalThis.WorkflowInstanceCreateOptions<Payload>,
|
|
8
10
|
"params"
|
|
@@ -242,4 +244,5 @@ export const layer = <
|
|
|
242
244
|
(value): value is globalThis.Workflow<S.Codec.Encoded<Payload>> =>
|
|
243
245
|
isWorkflow<S.Codec.Encoded<Payload>>(value),
|
|
244
246
|
makeClient(definition),
|
|
247
|
+
{ expected: expectedWorkflow },
|
|
245
248
|
);
|