effect-cf 0.25.3 → 0.27.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.
@@ -0,0 +1,255 @@
1
+ import type {
2
+ ArtifactClient as CloudflareComputerArtifactClient,
3
+ ArtifactImportOptions as CloudflareComputerArtifactImportOptions,
4
+ ArtifactImportSource as CloudflareComputerArtifactImportSource,
5
+ ArtifactRepoSummary as CloudflareComputerArtifactRepoSummary,
6
+ ArtifactScope as CloudflareComputerArtifactScope,
7
+ ArtifactsCLIInput as CloudflareComputerArtifactsCliInput,
8
+ ArtifactsCLIResult as CloudflareComputerArtifactsCliResult,
9
+ RemoteAddFn as CloudflareComputerRemoteAdd,
10
+ } from "@cloudflare/computer/artifacts";
11
+ import { Effect } from "effect";
12
+
13
+ import * as Artifacts from "./Artifacts";
14
+
15
+ /** External Git source accepted by a session-scoped repository import. */
16
+ export type ComputerArtifactImportSource = Readonly<CloudflareComputerArtifactImportSource>;
17
+
18
+ /** Target options accepted by a session-scoped repository import. */
19
+ export type ComputerArtifactImportOptions = Readonly<CloudflareComputerArtifactImportOptions>;
20
+
21
+ /** Repository metadata returned by a session-scoped list operation. */
22
+ export type ComputerArtifactRepoSummary = Readonly<CloudflareComputerArtifactRepoSummary>;
23
+
24
+ /** Token scope accepted by the upstream session facade. */
25
+ export type ComputerArtifactScope = CloudflareComputerArtifactScope;
26
+
27
+ /** Optional Git remote-registration seam accepted by the argv API. */
28
+ export type ComputerArtifactsRemoteAdd = CloudflareComputerRemoteAdd;
29
+
30
+ /** Input to the session-scoped `artifacts` argv API. */
31
+ export type ComputerArtifactsCliInput = CloudflareComputerArtifactsCliInput;
32
+
33
+ /** Result from the session-scoped `artifacts` argv API. */
34
+ export type ComputerArtifactsCliResult = Readonly<CloudflareComputerArtifactsCliResult>;
35
+
36
+ /** Options used when wrapping a session-scoped Artifacts client. */
37
+ export interface ComputerArtifactsClientOptions {
38
+ /** Binding name attached to errors and tracing spans. Defaults to `ARTIFACTS`. */
39
+ readonly binding?: string;
40
+ }
41
+
42
+ /**
43
+ * Effect-native facade over `@cloudflare/computer/artifacts`.
44
+ *
45
+ * Repository names are local to {@link sessionId}. Prefixing and filtering are
46
+ * intentionally delegated to the upstream `createArtifact` implementation so
47
+ * this client and the worker-shell `artifacts` command always use the same
48
+ * namespace convention.
49
+ */
50
+ export interface ComputerArtifactsClient {
51
+ readonly sessionId: string;
52
+ readonly create: (
53
+ name: Artifacts.RepoName,
54
+ options?: Artifacts.ArtifactsCreateOptions,
55
+ ) => Effect.Effect<Artifacts.ArtifactsCreateRepoResult, Artifacts.ArtifactsOperationError>;
56
+ readonly get: (
57
+ name: Artifacts.RepoName,
58
+ ) => Effect.Effect<Artifacts.ArtifactsRepoInfo, Artifacts.ArtifactsOperationError>;
59
+ readonly list: Effect.Effect<
60
+ ReadonlyArray<ComputerArtifactRepoSummary>,
61
+ Artifacts.ArtifactsOperationError
62
+ >;
63
+ readonly import: (
64
+ name: Artifacts.RepoName,
65
+ source: ComputerArtifactImportSource,
66
+ options?: ComputerArtifactImportOptions,
67
+ ) => Effect.Effect<Artifacts.ArtifactsCreateRepoResult, Artifacts.ArtifactsOperationError>;
68
+ readonly delete: (
69
+ name: Artifacts.RepoName,
70
+ ) => Effect.Effect<boolean, Artifacts.ArtifactsOperationError>;
71
+ readonly createToken: (
72
+ name: Artifacts.RepoName,
73
+ scope?: ComputerArtifactScope,
74
+ ttl?: number,
75
+ ) => Effect.Effect<Artifacts.ArtifactsCreateTokenResult, Artifacts.ArtifactsOperationError>;
76
+ readonly listTokens: (
77
+ name: Artifacts.RepoName,
78
+ ) => Effect.Effect<Artifacts.ArtifactsTokenListResult, Artifacts.ArtifactsOperationError>;
79
+ readonly getToken: (
80
+ name: Artifacts.RepoName,
81
+ id: string,
82
+ ) => Effect.Effect<Artifacts.ArtifactsTokenInfo, Artifacts.ArtifactsOperationError>;
83
+ readonly revokeToken: (
84
+ name: Artifacts.RepoName,
85
+ tokenOrId: string,
86
+ ) => Effect.Effect<boolean, Artifacts.ArtifactsOperationError>;
87
+ readonly cli: (
88
+ input: ComputerArtifactsCliInput,
89
+ ) => Effect.Effect<ComputerArtifactsCliResult, Artifacts.ArtifactsOperationError>;
90
+ readonly rawUnsafe: Effect.Effect<CloudflareComputerArtifactClient>;
91
+ }
92
+
93
+ const artifactsErrorDetails = (
94
+ cause: unknown,
95
+ ): Pick<Artifacts.ArtifactsOperationError, "code" | "numericCode"> => {
96
+ if (typeof cause !== "object" || cause === null) {
97
+ return {};
98
+ }
99
+
100
+ const code = Reflect.get(cause, "code");
101
+ const numericCode = Reflect.get(cause, "numericCode");
102
+
103
+ return {
104
+ ...(typeof code === "string" ? { code } : {}),
105
+ ...(typeof numericCode === "number" ? { numericCode } : {}),
106
+ };
107
+ };
108
+
109
+ const operationError = (binding: string, operation: Artifacts.ArtifactsOperation, cause: unknown) =>
110
+ new Artifacts.ArtifactsOperationError({
111
+ binding,
112
+ operation,
113
+ cause,
114
+ ...artifactsErrorDetails(cause),
115
+ });
116
+
117
+ const tryOperation = <A>(
118
+ binding: string,
119
+ operation: Artifacts.ArtifactsOperation,
120
+ evaluate: () => Promise<A>,
121
+ ) =>
122
+ Effect.tryPromise({
123
+ try: evaluate,
124
+ catch: (cause) => operationError(binding, operation, cause),
125
+ });
126
+
127
+ const spanOptions = (
128
+ binding: string,
129
+ sessionId: string,
130
+ operation: Artifacts.ArtifactsOperation,
131
+ ) => ({ attributes: { binding, sessionId, operation } });
132
+
133
+ /** Wraps an already-created upstream session client. */
134
+ export const fromClient = (
135
+ client: CloudflareComputerArtifactClient,
136
+ options: ComputerArtifactsClientOptions = {},
137
+ ): ComputerArtifactsClient => {
138
+ const binding = options.binding ?? "ARTIFACTS";
139
+ const { sessionId } = client;
140
+
141
+ return {
142
+ sessionId,
143
+ create: Effect.fn(
144
+ "ComputerArtifacts.create",
145
+ spanOptions(binding, sessionId, "create"),
146
+ )(function* (name: Artifacts.RepoName, createOptions?: Artifacts.ArtifactsCreateOptions) {
147
+ yield* Effect.annotateCurrentSpan("repo", name);
148
+
149
+ return yield* tryOperation(binding, "create", () => client.create(name, createOptions));
150
+ }),
151
+ get: Effect.fn(
152
+ "ComputerArtifacts.get",
153
+ spanOptions(binding, sessionId, "get"),
154
+ )(function* (name: Artifacts.RepoName) {
155
+ yield* Effect.annotateCurrentSpan("repo", name);
156
+
157
+ return yield* tryOperation(binding, "get", () => client.get(name));
158
+ }),
159
+ list: tryOperation(binding, "list", () => client.list()).pipe(
160
+ Effect.withSpan("ComputerArtifacts.list", spanOptions(binding, sessionId, "list")),
161
+ ),
162
+ import: Effect.fn(
163
+ "ComputerArtifacts.import",
164
+ spanOptions(binding, sessionId, "import"),
165
+ )(function* (
166
+ name: Artifacts.RepoName,
167
+ source: ComputerArtifactImportSource,
168
+ importOptions?: ComputerArtifactImportOptions,
169
+ ) {
170
+ yield* Effect.annotateCurrentSpan("repo", name);
171
+
172
+ return yield* tryOperation(binding, "import", () =>
173
+ client.import(name, source, importOptions),
174
+ );
175
+ }),
176
+ delete: Effect.fn(
177
+ "ComputerArtifacts.delete",
178
+ spanOptions(binding, sessionId, "delete"),
179
+ )(function* (name: Artifacts.RepoName) {
180
+ yield* Effect.annotateCurrentSpan("repo", name);
181
+
182
+ return yield* tryOperation(binding, "delete", () => client.delete(name));
183
+ }),
184
+ createToken: Effect.fn(
185
+ "ComputerArtifacts.createToken",
186
+ spanOptions(binding, sessionId, "createToken"),
187
+ )(function* (name: Artifacts.RepoName, scope?: ComputerArtifactScope, ttl?: number) {
188
+ yield* Effect.annotateCurrentSpan("repo", name);
189
+
190
+ return yield* tryOperation(binding, "createToken", () =>
191
+ client.createToken(name, scope, ttl),
192
+ );
193
+ }),
194
+ listTokens: Effect.fn(
195
+ "ComputerArtifacts.listTokens",
196
+ spanOptions(binding, sessionId, "listTokens"),
197
+ )(function* (name: Artifacts.RepoName) {
198
+ yield* Effect.annotateCurrentSpan("repo", name);
199
+
200
+ return yield* tryOperation(binding, "listTokens", () => client.listTokens(name));
201
+ }),
202
+ getToken: Effect.fn(
203
+ "ComputerArtifacts.getToken",
204
+ spanOptions(binding, sessionId, "getToken"),
205
+ )(function* (name: Artifacts.RepoName, id: string) {
206
+ yield* Effect.annotateCurrentSpan({ repo: name, tokenId: id });
207
+
208
+ return yield* tryOperation(binding, "getToken", () => client.getToken(name, id));
209
+ }),
210
+ revokeToken: Effect.fn(
211
+ "ComputerArtifacts.revokeToken",
212
+ spanOptions(binding, sessionId, "revokeToken"),
213
+ )(function* (name: Artifacts.RepoName, tokenOrId: string) {
214
+ yield* Effect.annotateCurrentSpan("repo", name);
215
+
216
+ return yield* tryOperation(binding, "revokeToken", () => client.revokeToken(name, tokenOrId));
217
+ }),
218
+ cli: Effect.fn(
219
+ "ComputerArtifacts.cli",
220
+ spanOptions(binding, sessionId, "cli"),
221
+ )(function* (input: ComputerArtifactsCliInput) {
222
+ yield* Effect.annotateCurrentSpan("argv", input.argv.join(" "));
223
+
224
+ return yield* tryOperation(binding, "cli", () => client.cli(input));
225
+ }),
226
+ rawUnsafe: Effect.succeed(client),
227
+ };
228
+ };
229
+
230
+ /**
231
+ * Creates an Effect-native session client directly from a Worker Artifacts
232
+ * binding. `@cloudflare/computer` is loaded only when this Effect runs, keeping
233
+ * the optional peer out of applications that do not use this integration.
234
+ */
235
+ export const makeClient = Effect.fn("ComputerArtifacts.makeClient")(function* (
236
+ bindingValue: Artifacts.ArtifactsBinding,
237
+ sessionId: string,
238
+ options: ComputerArtifactsClientOptions = {},
239
+ ) {
240
+ const binding = options.binding ?? "ARTIFACTS";
241
+ const computerArtifacts = yield* Effect.tryPromise({
242
+ try: () => import("@cloudflare/computer/artifacts"),
243
+ catch: (cause) => operationError(binding, "createClient", cause),
244
+ });
245
+ const client = yield* Effect.try({
246
+ try: () =>
247
+ computerArtifacts.createArtifact(
248
+ bindingValue as unknown as Parameters<typeof computerArtifacts.createArtifact>[0],
249
+ sessionId,
250
+ ),
251
+ catch: (cause) => operationError(binding, "createClient", cause),
252
+ });
253
+
254
+ return fromClient(client, { binding });
255
+ });