effect-cf 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +110 -0
- package/dist/Hyperdrive-By6DrSS_.d.mts +140 -0
- package/dist/Hyperdrive-LuQaVhXU.mjs +86 -0
- package/dist/HyperdrivePg.d.mts +14 -0
- package/dist/HyperdrivePg.mjs +13 -0
- package/dist/index.d.mts +267 -201
- package/dist/index.mjs +253 -190
- package/package.json +13 -1
- package/src/D1.ts +3 -59
- package/src/DurableObject.ts +14 -0
- package/src/Hyperdrive.ts +82 -0
- package/src/HyperdrivePg.ts +27 -0
- package/src/Images.ts +332 -0
- package/src/Kv.ts +0 -62
- package/src/R2.ts +230 -0
- package/src/index.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-cf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Effect-native primitives for Cloudflare Workers and bindings.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
"import": "./dist/index.mjs",
|
|
22
22
|
"default": "./dist/index.mjs"
|
|
23
23
|
},
|
|
24
|
+
"./hyperdrive-pg": {
|
|
25
|
+
"types": "./dist/HyperdrivePg.d.mts",
|
|
26
|
+
"import": "./dist/HyperdrivePg.mjs",
|
|
27
|
+
"default": "./dist/HyperdrivePg.mjs"
|
|
28
|
+
},
|
|
24
29
|
"./package.json": "./package.json"
|
|
25
30
|
},
|
|
26
31
|
"publishConfig": {
|
|
@@ -37,6 +42,7 @@
|
|
|
37
42
|
"@cloudflare/vitest-pool-workers": "^0.15.0",
|
|
38
43
|
"@cloudflare/workers-types": "^4.20260421.1",
|
|
39
44
|
"@effect/sql-d1": "catalog:",
|
|
45
|
+
"@effect/sql-pg": "4.0.0-beta.65",
|
|
40
46
|
"@effect/vitest": "catalog:",
|
|
41
47
|
"effect": "catalog:",
|
|
42
48
|
"typescript": "^6.0.2",
|
|
@@ -45,6 +51,12 @@
|
|
|
45
51
|
},
|
|
46
52
|
"peerDependencies": {
|
|
47
53
|
"@effect/sql-d1": "^4.0.0-beta.65",
|
|
54
|
+
"@effect/sql-pg": "^4.0.0-beta.65",
|
|
48
55
|
"effect": "^4.0.0-beta.65"
|
|
56
|
+
},
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"@effect/sql-pg": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
49
61
|
}
|
|
50
62
|
}
|
package/src/D1.ts
CHANGED
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import { D1Client } from "@effect/sql-d1";
|
|
2
|
-
import {
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
3
|
|
|
4
4
|
import * as Binding from "./Binding";
|
|
5
5
|
|
|
6
6
|
const TypeId = "effect-cf/D1" as const;
|
|
7
7
|
|
|
8
|
-
/** Error raised when a D1 operation fails. */
|
|
9
|
-
export class D1OperationError extends Data.TaggedError("D1OperationError")<{
|
|
10
|
-
readonly binding: string;
|
|
11
|
-
readonly operation: string;
|
|
12
|
-
readonly cause: unknown;
|
|
13
|
-
}> {}
|
|
14
|
-
|
|
15
8
|
/** Typed D1 binding definition. */
|
|
16
9
|
export interface D1Definition {
|
|
17
10
|
/** Binding name as configured in `wrangler.jsonc`. */
|
|
@@ -44,29 +37,6 @@ const isD1Database = (value: unknown): value is D1Database => {
|
|
|
44
37
|
);
|
|
45
38
|
};
|
|
46
39
|
|
|
47
|
-
const d1Error = (binding: string, operation: string, cause: unknown) =>
|
|
48
|
-
new D1OperationError({ binding, operation, cause });
|
|
49
|
-
|
|
50
|
-
const tryD1Promise = <A>(
|
|
51
|
-
binding: string,
|
|
52
|
-
operation: string,
|
|
53
|
-
evaluate: () => Promise<A>,
|
|
54
|
-
): Effect.Effect<A, D1OperationError> =>
|
|
55
|
-
Effect.tryPromise({
|
|
56
|
-
try: evaluate,
|
|
57
|
-
catch: (cause) => d1Error(binding, operation, cause),
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const tryD1Sync = <A>(
|
|
61
|
-
binding: string,
|
|
62
|
-
operation: string,
|
|
63
|
-
evaluate: () => A,
|
|
64
|
-
): Effect.Effect<A, D1OperationError> =>
|
|
65
|
-
Effect.try({
|
|
66
|
-
try: evaluate,
|
|
67
|
-
catch: (cause) => d1Error(binding, operation, cause),
|
|
68
|
-
});
|
|
69
|
-
|
|
70
40
|
/**
|
|
71
41
|
* Creates a typed D1 service tag plus Effect helpers.
|
|
72
42
|
*/
|
|
@@ -76,9 +46,8 @@ export const make = <Id extends string>(id: Id, definition: D1Definition) =>
|
|
|
76
46
|
/**
|
|
77
47
|
* Builds a D1 service around a Cloudflare D1 database binding.
|
|
78
48
|
*
|
|
79
|
-
* The returned service exposes the raw `D1Database` binding
|
|
80
|
-
*
|
|
81
|
-
* `@effect/sql-d1`.
|
|
49
|
+
* The returned service exposes the raw `D1Database` binding and `sqlLayer(...)`
|
|
50
|
+
* for providing `effect/unstable/sql` via `@effect/sql-d1`.
|
|
82
51
|
*
|
|
83
52
|
* @example
|
|
84
53
|
* ```ts
|
|
@@ -102,34 +71,9 @@ export const Service =
|
|
|
102
71
|
}),
|
|
103
72
|
).pipe(Layer.provide(tag.layer));
|
|
104
73
|
|
|
105
|
-
const prepare = Effect.fnUntraced(function* (query: string) {
|
|
106
|
-
const db = yield* tag;
|
|
107
|
-
return yield* tryD1Sync(definition.binding, "prepare", () => db.prepare(query));
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const batch = Effect.fnUntraced(function* <T = unknown>(
|
|
111
|
-
statements: ReadonlyArray<D1PreparedStatement>,
|
|
112
|
-
) {
|
|
113
|
-
const db = yield* tag;
|
|
114
|
-
return yield* tryD1Promise(definition.binding, "batch", () => db.batch<T>([...statements]));
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const exec = Effect.fnUntraced(function* (query: string) {
|
|
118
|
-
const db = yield* tag;
|
|
119
|
-
return yield* tryD1Promise(definition.binding, "exec", () => db.exec(query));
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const unsafeRaw = Effect.fnUntraced(function* () {
|
|
123
|
-
return yield* tag;
|
|
124
|
-
});
|
|
125
|
-
|
|
126
74
|
return Object.assign(tag, {
|
|
127
75
|
[TypeId]: TypeId,
|
|
128
76
|
definition,
|
|
129
77
|
sqlLayer,
|
|
130
|
-
prepare,
|
|
131
|
-
batch,
|
|
132
|
-
exec,
|
|
133
|
-
unsafeRaw,
|
|
134
78
|
});
|
|
135
79
|
};
|
package/src/DurableObject.ts
CHANGED
|
@@ -73,6 +73,15 @@ export type RpcHandlers<ROut, Api> = {
|
|
|
73
73
|
* Options for creating a Durable Object class backed by Effect handlers.
|
|
74
74
|
*/
|
|
75
75
|
export interface DurableObjectOptions<ROut, Rpc extends DurableObjectRpc<ROut>> {
|
|
76
|
+
/**
|
|
77
|
+
* Effect run when Cloudflare loads this Durable Object instance into memory.
|
|
78
|
+
*
|
|
79
|
+
* Use `DurableObjectState.blockConcurrencyWhile` inside this hook when
|
|
80
|
+
* incoming events should wait for setup to finish. Cloudflare may construct
|
|
81
|
+
* the same Durable Object id again after eviction or restart; use Durable
|
|
82
|
+
* Object storage if work must happen only once per id.
|
|
83
|
+
*/
|
|
84
|
+
readonly initialize?: Effect.Effect<void, unknown, HandlerContext<ROut>>;
|
|
76
85
|
/** Optional RPC methods exposed as Durable Object instance methods. */
|
|
77
86
|
readonly rpc?: Rpc;
|
|
78
87
|
/** Optional fetch handler for HTTP/WebSocket requests. */
|
|
@@ -137,6 +146,11 @@ export const make = <
|
|
|
137
146
|
const runtimeLayer = Entrypoint.provideEntrypointServices(layer, services);
|
|
138
147
|
|
|
139
148
|
this.runtime = ManagedRuntime.make(runtimeLayer);
|
|
149
|
+
|
|
150
|
+
const initialize = options.initialize;
|
|
151
|
+
if (initialize !== undefined) {
|
|
152
|
+
state.waitUntil(this[RunSymbol](initialize));
|
|
153
|
+
}
|
|
140
154
|
}
|
|
141
155
|
|
|
142
156
|
[RunSymbol]<A, E>(effect: Effect.Effect<A, E, HandlerContext<ROut>>): Promise<A> {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Context, Effect, type Layer } from "effect";
|
|
2
|
+
|
|
3
|
+
import * as Binding from "./Binding";
|
|
4
|
+
import type { WorkerEnvironment } from "./Environment";
|
|
5
|
+
|
|
6
|
+
/** Typed Hyperdrive binding definition. */
|
|
7
|
+
export interface HyperdriveDefinition {
|
|
8
|
+
/** Binding name as configured in `wrangler.jsonc`. */
|
|
9
|
+
readonly binding: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface HyperdriveClient {
|
|
13
|
+
readonly connectionString: string;
|
|
14
|
+
readonly unsafeRaw: Effect.Effect<Hyperdrive>;
|
|
15
|
+
readonly definition: HyperdriveDefinition;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare const HyperdriveServiceTypeId: unique symbol;
|
|
19
|
+
|
|
20
|
+
/** Nominal service marker for Hyperdrive services created with {@link make}. */
|
|
21
|
+
export interface HyperdriveService<Id extends string> {
|
|
22
|
+
readonly [HyperdriveServiceTypeId]: {
|
|
23
|
+
readonly id: Id;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type LayerOptions = {
|
|
28
|
+
readonly binding: string;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export interface TagClass<Self, Id extends string> extends Context.ServiceClass<
|
|
32
|
+
Self,
|
|
33
|
+
Id,
|
|
34
|
+
HyperdriveClient
|
|
35
|
+
> {
|
|
36
|
+
readonly id: Id;
|
|
37
|
+
readonly layer: (
|
|
38
|
+
options: LayerOptions,
|
|
39
|
+
) => Layer.Layer<
|
|
40
|
+
Self,
|
|
41
|
+
Binding.BindingNotFoundError | Binding.BindingValidationError,
|
|
42
|
+
WorkerEnvironment
|
|
43
|
+
>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const isHyperdrive = (value: unknown): value is Hyperdrive => {
|
|
47
|
+
if (typeof value !== "object" || value === null) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const resource = value as Record<string, unknown>;
|
|
52
|
+
|
|
53
|
+
return typeof resource.connectionString === "string";
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const makeClient =
|
|
57
|
+
(definition: HyperdriveDefinition) =>
|
|
58
|
+
(hyperdrive: Hyperdrive): HyperdriveClient => ({
|
|
59
|
+
definition,
|
|
60
|
+
connectionString: hyperdrive.connectionString,
|
|
61
|
+
unsafeRaw: Effect.succeed(hyperdrive),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export const layer = <Self>(
|
|
65
|
+
tag: Context.Service<Self, HyperdriveClient>,
|
|
66
|
+
definition: HyperdriveDefinition,
|
|
67
|
+
) => Binding.layer(tag, definition.binding, isHyperdrive, makeClient(definition));
|
|
68
|
+
|
|
69
|
+
export const make = <Id extends string>(id: Id) => Tag<HyperdriveService<Id>>()<Id>(id);
|
|
70
|
+
|
|
71
|
+
export const Tag =
|
|
72
|
+
<Self>() =>
|
|
73
|
+
<Id extends string>(id: Id) => {
|
|
74
|
+
const tag = Context.Service<Self, HyperdriveClient>()(id);
|
|
75
|
+
|
|
76
|
+
const makeLayer = (definition: LayerOptions) => layer(tag, definition);
|
|
77
|
+
|
|
78
|
+
return Object.assign(tag, {
|
|
79
|
+
id,
|
|
80
|
+
layer: makeLayer,
|
|
81
|
+
}) as TagClass<Self, Id>;
|
|
82
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PgClient } from "@effect/sql-pg";
|
|
2
|
+
import { Effect, Layer, Redacted } from "effect";
|
|
3
|
+
|
|
4
|
+
import * as Hyperdrive from "./Hyperdrive";
|
|
5
|
+
|
|
6
|
+
export type PgLayerOptions = Omit<
|
|
7
|
+
PgClient.PgClientConfig & { readonly acquireForStream?: boolean },
|
|
8
|
+
"url" | "host" | "port" | "path" | "ssl" | "database" | "username" | "password" | "stream"
|
|
9
|
+
>;
|
|
10
|
+
|
|
11
|
+
export const layer = <Self, Id extends string>(
|
|
12
|
+
tag: Hyperdrive.TagClass<Self, Id>,
|
|
13
|
+
binding: Hyperdrive.LayerOptions,
|
|
14
|
+
options?: PgLayerOptions,
|
|
15
|
+
) =>
|
|
16
|
+
Layer.unwrap(
|
|
17
|
+
Effect.gen(function* () {
|
|
18
|
+
const hyperdrive = yield* tag;
|
|
19
|
+
|
|
20
|
+
return PgClient.layerFrom(
|
|
21
|
+
PgClient.makeClient({
|
|
22
|
+
...options,
|
|
23
|
+
url: Redacted.make(hyperdrive.connectionString),
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
}),
|
|
27
|
+
).pipe(Layer.provide(tag.layer(binding)));
|
package/src/Images.ts
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { Context, Data, Effect, Option, type Layer } from "effect";
|
|
2
|
+
|
|
3
|
+
import * as Binding from "./Binding";
|
|
4
|
+
import type { WorkerEnvironment } from "./Environment";
|
|
5
|
+
|
|
6
|
+
const TypeId = "effect-cf/Images/Steps" as const;
|
|
7
|
+
|
|
8
|
+
/** Error raised when a Cloudflare Images operation fails. */
|
|
9
|
+
export class ImagesOperationError extends Data.TaggedError("ImagesOperationError")<{
|
|
10
|
+
readonly binding: string;
|
|
11
|
+
readonly operation: string;
|
|
12
|
+
readonly cause: unknown;
|
|
13
|
+
}> {}
|
|
14
|
+
|
|
15
|
+
/** Typed Cloudflare Images binding definition. */
|
|
16
|
+
export interface ImagesDefinition {
|
|
17
|
+
/** Binding name as configured in `wrangler.jsonc`. */
|
|
18
|
+
readonly binding: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ImageInfoResponse = globalThis.ImageInfoResponse;
|
|
22
|
+
export type ImageTransform = globalThis.ImageTransform;
|
|
23
|
+
export type ImageDrawOptions = globalThis.ImageDrawOptions;
|
|
24
|
+
export type ImageInputOptions = globalThis.ImageInputOptions;
|
|
25
|
+
export type ImageOutputOptions = globalThis.ImageOutputOptions;
|
|
26
|
+
export type ImageTransformationOutputOptions = globalThis.ImageTransformationOutputOptions;
|
|
27
|
+
export type ImageTransformationResult = globalThis.ImageTransformationResult;
|
|
28
|
+
export type ImageUploadOptions = globalThis.ImageUploadOptions;
|
|
29
|
+
export type ImageUpdateOptions = globalThis.ImageUpdateOptions;
|
|
30
|
+
export type ImageListOptions = globalThis.ImageListOptions;
|
|
31
|
+
export type ImageList = globalThis.ImageList;
|
|
32
|
+
export type ImageMetadata = globalThis.ImageMetadata;
|
|
33
|
+
export type ImageInputValue = ReadableStream<Uint8Array> | ArrayBuffer;
|
|
34
|
+
export type ImageUploadValue = ReadableStream<Uint8Array> | ArrayBuffer;
|
|
35
|
+
|
|
36
|
+
export interface DrawStepOptions {
|
|
37
|
+
readonly image: ReadableStream<Uint8Array> | globalThis.ImageTransformer;
|
|
38
|
+
readonly options?: ImageDrawOptions;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type Step = Data.TaggedEnum<{
|
|
42
|
+
readonly Transform: {
|
|
43
|
+
readonly transform: ImageTransform;
|
|
44
|
+
};
|
|
45
|
+
readonly Draw: DrawStepOptions;
|
|
46
|
+
}>;
|
|
47
|
+
|
|
48
|
+
export interface Steps {
|
|
49
|
+
readonly [TypeId]: typeof TypeId;
|
|
50
|
+
readonly steps: ReadonlyArray<Step>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ProcessOptions {
|
|
54
|
+
readonly stream: ImageInputValue;
|
|
55
|
+
readonly inputOptions?: ImageInputOptions;
|
|
56
|
+
readonly outputOptions: ImageOutputOptions;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ImagesTransformationResultClient {
|
|
60
|
+
readonly raw: globalThis.ImageTransformationResult;
|
|
61
|
+
readonly response: Effect.Effect<globalThis.Response, ImagesOperationError>;
|
|
62
|
+
readonly contentType: Effect.Effect<string, ImagesOperationError>;
|
|
63
|
+
readonly image: (
|
|
64
|
+
options?: ImageTransformationOutputOptions,
|
|
65
|
+
) => Effect.Effect<ReadableStream<Uint8Array>, ImagesOperationError>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface ImageHandleClient {
|
|
69
|
+
readonly raw: globalThis.ImageHandle;
|
|
70
|
+
readonly details: Effect.Effect<Option.Option<ImageMetadata>, ImagesOperationError>;
|
|
71
|
+
readonly bytes: Effect.Effect<Option.Option<ReadableStream<Uint8Array>>, ImagesOperationError>;
|
|
72
|
+
readonly update: (
|
|
73
|
+
options: ImageUpdateOptions,
|
|
74
|
+
) => Effect.Effect<ImageMetadata, ImagesOperationError>;
|
|
75
|
+
readonly delete: Effect.Effect<boolean, ImagesOperationError>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface HostedImagesClient {
|
|
79
|
+
readonly image: (imageId: string) => ImageHandleClient;
|
|
80
|
+
readonly upload: (
|
|
81
|
+
image: ImageUploadValue,
|
|
82
|
+
options?: ImageUploadOptions,
|
|
83
|
+
) => Effect.Effect<ImageMetadata, ImagesOperationError>;
|
|
84
|
+
readonly list: (options?: ImageListOptions) => Effect.Effect<ImageList, ImagesOperationError>;
|
|
85
|
+
readonly unsafeRaw: Effect.Effect<globalThis.HostedImagesBinding>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ImagesRuntimeBinding {
|
|
89
|
+
readonly info: (
|
|
90
|
+
image: ImageInputValue,
|
|
91
|
+
options?: ImageInputOptions,
|
|
92
|
+
) => Promise<ImageInfoResponse>;
|
|
93
|
+
readonly input: (
|
|
94
|
+
image: ImageInputValue,
|
|
95
|
+
options?: ImageInputOptions,
|
|
96
|
+
) => globalThis.ImageTransformer;
|
|
97
|
+
readonly hosted?: globalThis.HostedImagesBinding;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface ImagesClient {
|
|
101
|
+
readonly info: (
|
|
102
|
+
image: ImageInputValue,
|
|
103
|
+
options?: ImageInputOptions,
|
|
104
|
+
) => Effect.Effect<ImageInfoResponse, ImagesOperationError>;
|
|
105
|
+
readonly input: (
|
|
106
|
+
image: ImageInputValue,
|
|
107
|
+
options?: ImageInputOptions,
|
|
108
|
+
) => Effect.Effect<globalThis.ImageTransformer, ImagesOperationError>;
|
|
109
|
+
readonly process: (
|
|
110
|
+
steps: Steps,
|
|
111
|
+
options: ProcessOptions,
|
|
112
|
+
) => Effect.Effect<ImagesTransformationResultClient, ImagesOperationError>;
|
|
113
|
+
readonly hosted: Option.Option<HostedImagesClient>;
|
|
114
|
+
readonly unsafeRaw: Effect.Effect<ImagesRuntimeBinding>;
|
|
115
|
+
readonly definition: ImagesDefinition;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
declare const ImagesServiceTypeId: unique symbol;
|
|
119
|
+
|
|
120
|
+
/** Nominal service marker for Images services created with {@link make}. */
|
|
121
|
+
export interface ImagesService<Id extends string> {
|
|
122
|
+
readonly [ImagesServiceTypeId]: {
|
|
123
|
+
readonly id: Id;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type LayerOptions = {
|
|
128
|
+
readonly binding: string;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export interface TagClass<Self, Id extends string> extends Context.ServiceClass<
|
|
132
|
+
Self,
|
|
133
|
+
Id,
|
|
134
|
+
ImagesClient
|
|
135
|
+
> {
|
|
136
|
+
readonly id: Id;
|
|
137
|
+
readonly layer: (
|
|
138
|
+
options: LayerOptions,
|
|
139
|
+
) => Layer.Layer<
|
|
140
|
+
Self,
|
|
141
|
+
Binding.BindingNotFoundError | Binding.BindingValidationError,
|
|
142
|
+
WorkerEnvironment
|
|
143
|
+
>;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const makeSteps = (steps: ReadonlyArray<Step>): Steps => ({
|
|
147
|
+
[TypeId]: TypeId,
|
|
148
|
+
steps,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
/** Empty Images transformation pipeline. */
|
|
152
|
+
export const empty: Steps = makeSteps([]);
|
|
153
|
+
|
|
154
|
+
export function transform(transform: ImageTransform): (steps: Steps) => Steps;
|
|
155
|
+
export function transform(steps: Steps, transform: ImageTransform): Steps;
|
|
156
|
+
export function transform(
|
|
157
|
+
stepsOrTransform: Steps | ImageTransform,
|
|
158
|
+
transformValue?: ImageTransform,
|
|
159
|
+
): Steps | ((steps: Steps) => Steps) {
|
|
160
|
+
if (transformValue === undefined) {
|
|
161
|
+
return (steps) => transform(steps, stepsOrTransform as ImageTransform);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const steps = stepsOrTransform as Steps;
|
|
165
|
+
|
|
166
|
+
return makeSteps([
|
|
167
|
+
...steps.steps,
|
|
168
|
+
{
|
|
169
|
+
_tag: "Transform",
|
|
170
|
+
transform: transformValue,
|
|
171
|
+
},
|
|
172
|
+
]);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function draw(draw: DrawStepOptions): (steps: Steps) => Steps;
|
|
176
|
+
export function draw(steps: Steps, draw: DrawStepOptions): Steps;
|
|
177
|
+
export function draw(
|
|
178
|
+
stepsOrDraw: Steps | DrawStepOptions,
|
|
179
|
+
drawValue?: DrawStepOptions,
|
|
180
|
+
): Steps | ((steps: Steps) => Steps) {
|
|
181
|
+
if (drawValue === undefined) {
|
|
182
|
+
return (steps) => draw(steps, stepsOrDraw as DrawStepOptions);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const steps = stepsOrDraw as Steps;
|
|
186
|
+
|
|
187
|
+
return makeSteps([
|
|
188
|
+
...steps.steps,
|
|
189
|
+
{
|
|
190
|
+
_tag: "Draw",
|
|
191
|
+
...drawValue,
|
|
192
|
+
},
|
|
193
|
+
]);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const imagesError = (binding: string, operation: string, cause: unknown) =>
|
|
197
|
+
new ImagesOperationError({ binding, operation, cause });
|
|
198
|
+
|
|
199
|
+
const tryImagesPromise = <A>(
|
|
200
|
+
binding: string,
|
|
201
|
+
operation: string,
|
|
202
|
+
evaluate: () => Promise<A>,
|
|
203
|
+
): Effect.Effect<A, ImagesOperationError> =>
|
|
204
|
+
Effect.tryPromise({
|
|
205
|
+
try: evaluate,
|
|
206
|
+
catch: (cause) => imagesError(binding, operation, cause),
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const tryImagesSync = <A>(
|
|
210
|
+
binding: string,
|
|
211
|
+
operation: string,
|
|
212
|
+
evaluate: () => A,
|
|
213
|
+
): Effect.Effect<A, ImagesOperationError> =>
|
|
214
|
+
Effect.try({
|
|
215
|
+
try: evaluate,
|
|
216
|
+
catch: (cause) => imagesError(binding, operation, cause),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const maybe = <A>(value: A | null): Option.Option<A> =>
|
|
220
|
+
value === null ? Option.none() : Option.some(value);
|
|
221
|
+
|
|
222
|
+
const hasFunction = (value: object, key: string): boolean =>
|
|
223
|
+
typeof Reflect.get(value, key) === "function";
|
|
224
|
+
|
|
225
|
+
const isHostedImagesBinding = (value: unknown): value is globalThis.HostedImagesBinding =>
|
|
226
|
+
typeof value === "object" &&
|
|
227
|
+
value !== null &&
|
|
228
|
+
hasFunction(value, "image") &&
|
|
229
|
+
hasFunction(value, "upload") &&
|
|
230
|
+
hasFunction(value, "list");
|
|
231
|
+
|
|
232
|
+
export const isImagesBinding = (value: unknown): value is ImagesRuntimeBinding =>
|
|
233
|
+
typeof value === "object" &&
|
|
234
|
+
value !== null &&
|
|
235
|
+
hasFunction(value, "info") &&
|
|
236
|
+
hasFunction(value, "input");
|
|
237
|
+
|
|
238
|
+
const wrapResult = (
|
|
239
|
+
binding: string,
|
|
240
|
+
result: globalThis.ImageTransformationResult,
|
|
241
|
+
): ImagesTransformationResultClient => ({
|
|
242
|
+
raw: result,
|
|
243
|
+
response: tryImagesSync(binding, "response", () => result.response()),
|
|
244
|
+
contentType: tryImagesSync(binding, "contentType", () => result.contentType()),
|
|
245
|
+
image: (options) => tryImagesSync(binding, "image", () => result.image(options)),
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const wrapHandle = (binding: string, handle: globalThis.ImageHandle): ImageHandleClient => ({
|
|
249
|
+
raw: handle,
|
|
250
|
+
details: tryImagesPromise(binding, "details", () => handle.details()).pipe(Effect.map(maybe)),
|
|
251
|
+
bytes: tryImagesPromise(binding, "bytes", () => handle.bytes()).pipe(Effect.map(maybe)),
|
|
252
|
+
update: (options) => tryImagesPromise(binding, "update", () => handle.update(options)),
|
|
253
|
+
delete: tryImagesPromise(binding, "delete", () => handle.delete()),
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const wrapHosted = (
|
|
257
|
+
binding: string,
|
|
258
|
+
hosted: globalThis.HostedImagesBinding,
|
|
259
|
+
): HostedImagesClient => ({
|
|
260
|
+
image: (imageId) => wrapHandle(binding, hosted.image(imageId)),
|
|
261
|
+
upload: (image, options) =>
|
|
262
|
+
tryImagesPromise(binding, "upload", () => hosted.upload(image, options)),
|
|
263
|
+
list: (options) => tryImagesPromise(binding, "list", () => hosted.list(options)),
|
|
264
|
+
unsafeRaw: Effect.succeed(hosted),
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
export const makeClient =
|
|
268
|
+
(definition: ImagesDefinition) =>
|
|
269
|
+
(images: ImagesRuntimeBinding): ImagesClient => {
|
|
270
|
+
const input = (image: ImageInputValue, options?: ImageInputOptions) =>
|
|
271
|
+
tryImagesSync(definition.binding, "input", () => images.input(image, options));
|
|
272
|
+
|
|
273
|
+
const process = (steps: Steps, options: ProcessOptions) =>
|
|
274
|
+
Effect.gen(function* () {
|
|
275
|
+
let transformer = yield* input(options.stream, options.inputOptions);
|
|
276
|
+
|
|
277
|
+
for (const step of steps.steps) {
|
|
278
|
+
switch (step._tag) {
|
|
279
|
+
case "Draw": {
|
|
280
|
+
transformer = yield* tryImagesSync(definition.binding, "draw", () =>
|
|
281
|
+
transformer.draw(step.image, step.options),
|
|
282
|
+
);
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
case "Transform": {
|
|
286
|
+
transformer = yield* tryImagesSync(definition.binding, "transform", () =>
|
|
287
|
+
transformer.transform(step.transform),
|
|
288
|
+
);
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const result = yield* tryImagesPromise(definition.binding, "output", () =>
|
|
295
|
+
transformer.output(options.outputOptions),
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
return wrapResult(definition.binding, result);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
definition,
|
|
303
|
+
info: (image, options) =>
|
|
304
|
+
tryImagesPromise(definition.binding, "info", () => images.info(image, options)),
|
|
305
|
+
input,
|
|
306
|
+
process,
|
|
307
|
+
hosted: isHostedImagesBinding(images.hosted)
|
|
308
|
+
? Option.some(wrapHosted(definition.binding, images.hosted))
|
|
309
|
+
: Option.none(),
|
|
310
|
+
unsafeRaw: Effect.succeed(images),
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export const layer = <Self>(
|
|
315
|
+
tag: Context.Service<Self, ImagesClient>,
|
|
316
|
+
definition: ImagesDefinition,
|
|
317
|
+
) => Binding.layer(tag, definition.binding, isImagesBinding, makeClient(definition));
|
|
318
|
+
|
|
319
|
+
export const make = <Id extends string>(id: Id) => Tag<ImagesService<Id>>()<Id>(id);
|
|
320
|
+
|
|
321
|
+
export const Tag =
|
|
322
|
+
<Self>() =>
|
|
323
|
+
<Id extends string>(id: Id) => {
|
|
324
|
+
const tag = Context.Service<Self, ImagesClient>()(id);
|
|
325
|
+
|
|
326
|
+
const makeLayer = (definition: LayerOptions) => layer(tag, definition);
|
|
327
|
+
|
|
328
|
+
return Object.assign(tag, {
|
|
329
|
+
id,
|
|
330
|
+
layer: makeLayer,
|
|
331
|
+
}) as TagClass<Self, Id>;
|
|
332
|
+
};
|
package/src/Kv.ts
CHANGED
|
@@ -114,27 +114,6 @@ export interface TagClass<
|
|
|
114
114
|
Binding.BindingNotFoundError | Binding.BindingValidationError,
|
|
115
115
|
WorkerEnvironment
|
|
116
116
|
>;
|
|
117
|
-
readonly put: (
|
|
118
|
-
key: Key,
|
|
119
|
-
value: Value,
|
|
120
|
-
options?: KvPutOptions,
|
|
121
|
-
) => Effect.Effect<void, KvOperationError | S.SchemaError, Self>;
|
|
122
|
-
readonly get: (
|
|
123
|
-
key: Key,
|
|
124
|
-
) => Effect.Effect<Option.Option<Value>, KvOperationError | S.SchemaError, Self>;
|
|
125
|
-
readonly getWithMetadata: <Metadata>(
|
|
126
|
-
key: Key,
|
|
127
|
-
metadataSchema: S.Codec<Metadata, unknown>,
|
|
128
|
-
) => Effect.Effect<
|
|
129
|
-
Option.Option<KvWithMetadata<Value, Metadata>>,
|
|
130
|
-
KvOperationError | S.SchemaError,
|
|
131
|
-
Self
|
|
132
|
-
>;
|
|
133
|
-
readonly list: <Metadata = unknown>(
|
|
134
|
-
options?: KvListOptions<Metadata>,
|
|
135
|
-
) => Effect.Effect<KvListResult<Key, Metadata>, KvOperationError | S.SchemaError, Self>;
|
|
136
|
-
readonly remove: (key: Key) => Effect.Effect<void, KvOperationError | S.SchemaError, Self>;
|
|
137
|
-
readonly unsafeRaw: () => Effect.Effect<KVNamespace, never, Self>;
|
|
138
117
|
}
|
|
139
118
|
|
|
140
119
|
const maybeString = (value: string | null | undefined): Option.Option<string> =>
|
|
@@ -311,51 +290,10 @@ export const Tag =
|
|
|
311
290
|
|
|
312
291
|
const makeLayer = (binding: LayerOptions) => kvDefinition.layer(tag, binding);
|
|
313
292
|
|
|
314
|
-
const put = Effect.fnUntraced(function* (key: Key, value: Value, options?: KvPutOptions) {
|
|
315
|
-
const kv = yield* tag;
|
|
316
|
-
yield* kv.put(key, value, options);
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
const get = Effect.fnUntraced(function* (key: Key) {
|
|
320
|
-
const kv = yield* tag;
|
|
321
|
-
return yield* kv.get(key);
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
const getWithMetadata = Effect.fnUntraced(function* <Metadata>(
|
|
325
|
-
key: Key,
|
|
326
|
-
metadataSchema: S.Codec<Metadata, unknown>,
|
|
327
|
-
) {
|
|
328
|
-
const kv = yield* tag;
|
|
329
|
-
return yield* kv.getWithMetadata(key, metadataSchema);
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
const list = Effect.fnUntraced(function* <Metadata = unknown>(
|
|
333
|
-
options?: KvListOptions<Metadata>,
|
|
334
|
-
) {
|
|
335
|
-
const kv = yield* tag;
|
|
336
|
-
return yield* kv.list(options);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
const remove = Effect.fnUntraced(function* (key: Key) {
|
|
340
|
-
const kv = yield* tag;
|
|
341
|
-
yield* kv.remove(key);
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
const unsafeRaw = Effect.fnUntraced(function* () {
|
|
345
|
-
const kv = yield* tag;
|
|
346
|
-
return yield* kv.unsafeRaw;
|
|
347
|
-
});
|
|
348
|
-
|
|
349
293
|
return Object.assign(tag, {
|
|
350
294
|
id: kvDefinition.id,
|
|
351
295
|
keySchema: kvDefinition.key,
|
|
352
296
|
valueSchema: kvDefinition.value,
|
|
353
297
|
layer: makeLayer,
|
|
354
|
-
put,
|
|
355
|
-
get,
|
|
356
|
-
getWithMetadata,
|
|
357
|
-
list,
|
|
358
|
-
remove,
|
|
359
|
-
unsafeRaw,
|
|
360
298
|
}) as TagClass<Self, Id, Key, Value, EncodedValue>;
|
|
361
299
|
};
|