alchemy-effect 0.7.1 → 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.
Files changed (54) hide show
  1. package/bin/alchemy-effect.js +29 -21
  2. package/bin/alchemy-effect.js.map +1 -1
  3. package/bin/alchemy-effect.ts +32 -68
  4. package/lib/cli/index.d.ts +1 -0
  5. package/lib/cli/index.d.ts.map +1 -1
  6. package/package.json +3 -2
  7. package/src/AWS/AutoScaling/LaunchTemplate.ts +5 -4
  8. package/src/AWS/EC2/Instance.ts +3 -4
  9. package/src/AWS/ECS/Task.ts +36 -34
  10. package/src/AWS/Lambda/Function.ts +68 -68
  11. package/src/Apply.ts +2 -0
  12. package/src/Artifacts.ts +10 -1
  13. package/src/Binding.ts +20 -5
  14. package/src/Cloudflare/Container.ts +210 -109
  15. package/src/Cloudflare/D1/D1Database.ts +32 -32
  16. package/src/Cloudflare/KV/Delete.ts +7 -5
  17. package/src/Cloudflare/KV/Get.ts +5 -5
  18. package/src/Cloudflare/KV/GetWithMetadata.ts +5 -5
  19. package/src/Cloudflare/KV/{Namespace.ts → KVNamespace.ts} +6 -6
  20. package/src/Cloudflare/KV/{NamespaceBinding.ts → KVNamespaceBinding.ts} +2 -2
  21. package/src/Cloudflare/KV/List.ts +5 -5
  22. package/src/Cloudflare/KV/Put.ts +5 -5
  23. package/src/Cloudflare/KV/index.ts +1 -1
  24. package/src/Cloudflare/Providers.ts +2 -8
  25. package/src/Cloudflare/R2/{Bucket.ts → R2Bucket.ts} +19 -19
  26. package/src/Cloudflare/R2/R2BucketBinding.ts +322 -0
  27. package/src/Cloudflare/R2/index.ts +2 -9
  28. package/src/Cloudflare/Workers/Assets.ts +11 -5
  29. package/src/Cloudflare/Workers/DurableObject.ts +4 -33
  30. package/src/Cloudflare/Workers/HttpServer.ts +1 -0
  31. package/src/Cloudflare/Workers/InferEnv.ts +23 -0
  32. package/src/Cloudflare/Workers/WebSocket.ts +4 -3
  33. package/src/Cloudflare/Workers/Worker.ts +480 -165
  34. package/src/Cloudflare/Workers/cloudflare_workers.ts +13 -21
  35. package/src/Cloudflare/Workers/index.ts +1 -0
  36. package/src/Cloudflare/index.ts +2 -2
  37. package/src/Platform.ts +36 -55
  38. package/src/Provider.ts +1 -0
  39. package/src/Resource.ts +3 -2
  40. package/src/Stack.ts +5 -4
  41. package/src/Test/Bun.ts +207 -0
  42. package/src/Util/ConfigProvider.ts +25 -0
  43. package/src/Util/FileLogger.ts +22 -0
  44. package/src/Util/effect.ts +24 -0
  45. package/src/Cloudflare/R2/BucketBinding.ts +0 -31
  46. package/src/Cloudflare/R2/CreateMultipartUpload.ts +0 -59
  47. package/src/Cloudflare/R2/DeleteObject.ts +0 -41
  48. package/src/Cloudflare/R2/GetObject.ts +0 -47
  49. package/src/Cloudflare/R2/HeadObject.ts +0 -41
  50. package/src/Cloudflare/R2/ListObjects.ts +0 -45
  51. package/src/Cloudflare/R2/MultipartUploadClient.ts +0 -40
  52. package/src/Cloudflare/R2/PutObject.ts +0 -55
  53. package/src/Cloudflare/R2/ResumeMultipartUpload.ts +0 -48
  54. package/src/Cloudflare/R2/UploadValue.ts +0 -10
@@ -268,80 +268,82 @@ export const Container: Platform<
268
268
  ContainerShape,
269
269
  Server.ProcessContext,
270
270
  Container
271
- > = Platform("Cloudflare.Container", (id: string): Server.ProcessContext => {
272
- const runners: Effect.Effect<void, never, any>[] = [];
273
- const env: Record<string, any> = {};
271
+ > = Platform("Cloudflare.Container", {
272
+ createExecutionContext: (id: string): Server.ProcessContext => {
273
+ const runners: Effect.Effect<void, never, any>[] = [];
274
+ const env: Record<string, any> = {};
274
275
 
275
- const serve = <Req = never>(handler: HttpEffect<Req>) =>
276
- Effect.sync(() => {
277
- runners.push(
278
- Effect.gen(function* () {
279
- const httpServer = yield* Effect.serviceOption(HttpServer).pipe(
280
- Effect.map(Option.getOrUndefined),
281
- );
282
- if (httpServer) {
283
- yield* httpServer.serve(handler);
284
- yield* Effect.never;
285
- } else {
286
- // this should only happen at plantime, validate?
287
- }
288
- }).pipe(Effect.orDie),
289
- );
290
- });
291
-
292
- return {
293
- Type: ContainerTypeId,
294
- LogicalId: id,
295
- id,
296
- env,
297
- set: (bindingId: string, output: Output.Output) =>
276
+ const serve = <Req = never>(handler: HttpEffect<Req>) =>
298
277
  Effect.sync(() => {
299
- const key = bindingId.replaceAll(/[^a-zA-Z0-9]/g, "_");
300
- env[key] = output.pipe(Output.map((value) => JSON.stringify(value)));
301
- return key;
302
- }),
303
- get: <T>(key: string) =>
304
- Config.string(key)
305
- .asEffect()
306
- .pipe(
307
- Effect.flatMap((value) =>
308
- Effect.try({
309
- try: () => JSON.parse(value) as T,
310
- catch: (error) => error as Error,
311
- }),
312
- ),
313
- Effect.catch((cause) =>
314
- Effect.die(
315
- new Error(`Failed to get environment variable: ${key}`, {
316
- cause,
278
+ runners.push(
279
+ Effect.gen(function* () {
280
+ const httpServer = yield* Effect.serviceOption(HttpServer).pipe(
281
+ Effect.map(Option.getOrUndefined),
282
+ );
283
+ if (httpServer) {
284
+ yield* httpServer.serve(handler);
285
+ yield* Effect.never;
286
+ } else {
287
+ // this should only happen at plantime, validate?
288
+ }
289
+ }).pipe(Effect.orDie),
290
+ );
291
+ });
292
+
293
+ return {
294
+ Type: ContainerTypeId,
295
+ LogicalId: id,
296
+ id,
297
+ env,
298
+ set: (bindingId: string, output: Output.Output) =>
299
+ Effect.sync(() => {
300
+ const key = bindingId.replaceAll(/[^a-zA-Z0-9]/g, "_");
301
+ env[key] = output.pipe(Output.map((value) => JSON.stringify(value)));
302
+ return key;
303
+ }),
304
+ get: <T>(key: string) =>
305
+ Config.string(key)
306
+ .asEffect()
307
+ .pipe(
308
+ Effect.flatMap((value) =>
309
+ Effect.try({
310
+ try: () => JSON.parse(value) as T,
311
+ catch: (error) => error as Error,
317
312
  }),
318
313
  ),
314
+ Effect.catch((cause) =>
315
+ Effect.die(
316
+ new Error(`Failed to get environment variable: ${key}`, {
317
+ cause,
318
+ }),
319
+ ),
320
+ ),
319
321
  ),
320
- ),
321
- run: ((effect: Effect.Effect<void, never, any>) =>
322
- Effect.sync(() => {
323
- runners.push(effect);
324
- })) as unknown as Server.ProcessContext["run"],
325
- serve,
326
- exports: Effect.sync(() => ({
327
- default: Effect.all(
328
- runners.map((eff) =>
329
- Effect.forever(
330
- eff.pipe(
331
- // Log and ignore errors (daemon mode, it should just re-run)
332
- Effect.tapError((err) => Effect.logError(err)),
333
- Effect.ignore,
334
- // TODO(sam): ignore cause? for now, let that actually kill the server
335
- // Effect.ignoreCause
322
+ run: ((effect: Effect.Effect<void, never, any>) =>
323
+ Effect.sync(() => {
324
+ runners.push(effect);
325
+ })) as unknown as Server.ProcessContext["run"],
326
+ serve,
327
+ exports: Effect.sync(() => ({
328
+ default: Effect.all(
329
+ runners.map((eff) =>
330
+ Effect.forever(
331
+ eff.pipe(
332
+ // Log and ignore errors (daemon mode, it should just re-run)
333
+ Effect.tapError((err) => Effect.logError(err)),
334
+ Effect.ignore,
335
+ // TODO(sam): ignore cause? for now, let that actually kill the server
336
+ // Effect.ignoreCause
337
+ ),
336
338
  ),
337
339
  ),
340
+ {
341
+ concurrency: "unbounded",
342
+ },
338
343
  ),
339
- {
340
- concurrency: "unbounded",
341
- },
342
- ),
343
- })),
344
- } as Server.ProcessContext;
344
+ })),
345
+ } as Server.ProcessContext;
346
+ },
345
347
  });
346
348
 
347
349
  export const bindContainer = Effect.fnUntraced(function* <Shape, Req = never>(
@@ -410,6 +412,32 @@ export const bindContainer = Effect.fnUntraced(function* <Shape, Req = never>(
410
412
  });
411
413
  });
412
414
 
415
+ export const resolveDurableObjectApplicationRecovery = ({
416
+ namespaceId,
417
+ expectedName,
418
+ existingName,
419
+ }: {
420
+ namespaceId: string;
421
+ expectedName: string;
422
+ existingName: string | undefined;
423
+ }) => {
424
+ if (!existingName) {
425
+ return {
426
+ canAdopt: false as const,
427
+ message: `Container application for Durable Object namespace "${namespaceId}" already exists but could not be found for adoption.`,
428
+ };
429
+ }
430
+ if (existingName !== expectedName) {
431
+ return {
432
+ canAdopt: false as const,
433
+ message: `Existing container application "${existingName}" is already attached to Durable Object namespace "${namespaceId}". Use that application name to adopt it.`,
434
+ };
435
+ }
436
+ return {
437
+ canAdopt: true as const,
438
+ };
439
+ };
440
+
413
441
  /**
414
442
  * Runs the Container in a Durable Object and monitors it, providing a durable fetch and RPC interface to it.
415
443
  */
@@ -527,6 +555,37 @@ export interface Rollout {
527
555
  stepPercentage?: number;
528
556
  }
529
557
 
558
+ const containerApplicationReadinessSchedule = Schedule.exponential(100).pipe(
559
+ Schedule.both(Schedule.recurs(20)),
560
+ );
561
+
562
+ const isContainerApplicationNotFound = (
563
+ error: unknown,
564
+ ): error is Containers.ContainerApplicationNotFound =>
565
+ typeof error === "object" &&
566
+ error !== null &&
567
+ "_tag" in error &&
568
+ error._tag === "ContainerApplicationNotFound";
569
+
570
+ export const retryForContainerApplicationReadiness = <A, E, R>(
571
+ operation: string,
572
+ applicationId: string,
573
+ effect: Effect.Effect<A, E, R>,
574
+ ) =>
575
+ effect.pipe(
576
+ Effect.tapError((error) =>
577
+ isContainerApplicationNotFound(error)
578
+ ? Effect.logDebug(
579
+ `Cloudflare Container ${operation}: application ${applicationId} not found yet, retrying`,
580
+ )
581
+ : Effect.void,
582
+ ),
583
+ Effect.retry({
584
+ while: isContainerApplicationNotFound,
585
+ schedule: containerApplicationReadinessSchedule,
586
+ }),
587
+ );
588
+
530
589
  export const ContainerProvider = () =>
531
590
  Container.provider.effect(
532
591
  Effect.gen(function* () {
@@ -637,7 +696,6 @@ export const ContainerProvider = () =>
637
696
  });
638
697
 
639
698
  const bundleProgram = Effect.fnUntraced(function* ({
640
- id,
641
699
  main,
642
700
  runtime,
643
701
  handler = "default",
@@ -856,18 +914,22 @@ await Effect.runPromise(serverEffect).catch((err) => {
856
914
  const stepPercentage =
857
915
  strategy === "immediate" ? 100 : (rollout?.stepPercentage ?? 25);
858
916
 
859
- yield* createContainerApplicationRollout({
860
- accountId,
917
+ yield* retryForContainerApplicationReadiness(
918
+ "rollout",
861
919
  applicationId,
862
- description:
863
- strategy === "immediate"
864
- ? "Immediate update"
865
- : "Progressive update",
866
- strategy: "rolling",
867
- kind: rollout?.kind ?? "full_auto",
868
- stepPercentage,
869
- targetConfiguration: configuration,
870
- });
920
+ createContainerApplicationRollout({
921
+ accountId,
922
+ applicationId,
923
+ description:
924
+ strategy === "immediate"
925
+ ? "Immediate update"
926
+ : "Progressive update",
927
+ strategy: "rolling",
928
+ kind: rollout?.kind ?? "full_auto",
929
+ stepPercentage,
930
+ targetConfiguration: configuration,
931
+ }),
932
+ );
871
933
  });
872
934
 
873
935
  const createApplication = Effect.fnUntraced(function* ({
@@ -957,22 +1019,25 @@ await Effect.runPromise(serverEffect).catch((err) => {
957
1019
  durableObjects,
958
1020
  }).pipe(
959
1021
  Effect.catchTag("DurableObjectAlreadyHasApplication", () =>
960
- adoptPolicy && durableObjects
1022
+ durableObjects
961
1023
  ? Effect.gen(function* () {
962
1024
  const existing = yield* findApplicationByNamespace(
963
1025
  durableObjects.namespaceId,
964
1026
  );
965
- if (!existing) {
1027
+ const recovery = resolveDurableObjectApplicationRecovery({
1028
+ namespaceId: durableObjects.namespaceId,
1029
+ expectedName: name,
1030
+ existingName: existing?.name,
1031
+ });
1032
+ if (!recovery.canAdopt) {
966
1033
  return yield* Effect.fail(
967
- new Error(
968
- `Container application for Durable Object namespace "${durableObjects.namespaceId}" already exists but could not be found for adoption.`,
969
- ),
1034
+ new Error(recovery.message),
970
1035
  );
971
1036
  }
972
- if (existing.name !== name) {
1037
+ if (!existing) {
973
1038
  return yield* Effect.fail(
974
1039
  new Error(
975
- `Existing container application "${existing.name}" is already attached to Durable Object namespace "${durableObjects.namespaceId}". Use that application name to adopt it.`,
1040
+ `Container application for Durable Object namespace "${durableObjects.namespaceId}" already exists but could not be found for adoption.`,
976
1041
  ),
977
1042
  );
978
1043
  }
@@ -985,7 +1050,7 @@ await Effect.runPromise(serverEffect).catch((err) => {
985
1050
  })
986
1051
  : Effect.fail(
987
1052
  new Error(
988
- `Durable Object namespace "${durableObjects?.namespaceId ?? "unknown"}" already has a container application. Set AdoptPolicy to adopt it.`,
1053
+ "Durable Object namespace already has a container application. Set AdoptPolicy to adopt it.",
989
1054
  ),
990
1055
  ),
991
1056
  ),
@@ -1031,16 +1096,20 @@ await Effect.runPromise(serverEffect).catch((err) => {
1031
1096
  yield* session.note(
1032
1097
  `Updating container application ${existing.applicationName}...`,
1033
1098
  );
1034
- const application = yield* updateContainerApplication({
1035
- accountId,
1036
- applicationId: existing.applicationId,
1037
- instances: news.instances ?? 1,
1038
- maxInstances: news.maxInstances ?? 1,
1039
- schedulingPolicy: news.schedulingPolicy ?? "default",
1040
- constraints: news.constraints ?? {},
1041
- affinities: news.affinities,
1042
- configuration,
1043
- });
1099
+ const application = yield* retryForContainerApplicationReadiness(
1100
+ "update",
1101
+ existing.applicationId,
1102
+ updateContainerApplication({
1103
+ accountId,
1104
+ applicationId: existing.applicationId,
1105
+ instances: news.instances ?? 1,
1106
+ maxInstances: news.maxInstances ?? 1,
1107
+ schedulingPolicy: news.schedulingPolicy ?? "default",
1108
+ constraints: news.constraints ?? {},
1109
+ affinities: news.affinities,
1110
+ configuration,
1111
+ }),
1112
+ );
1044
1113
  const updated = toAttributes(application);
1045
1114
  if (!deepEqual(existing.configuration, configuration)) {
1046
1115
  yield* Effect.logInfo(
@@ -1174,6 +1243,31 @@ await Effect.runPromise(serverEffect).catch((err) => {
1174
1243
  !adoptPolicy &&
1175
1244
  !deepEqual(output.durableObjects, durableObjects)
1176
1245
  ) {
1246
+ if (durableObjects) {
1247
+ const existing = yield* findApplicationByNamespace(
1248
+ durableObjects.namespaceId,
1249
+ );
1250
+ const recovery = resolveDurableObjectApplicationRecovery({
1251
+ namespaceId: durableObjects.namespaceId,
1252
+ expectedName: name,
1253
+ existingName: existing?.name,
1254
+ });
1255
+ if (recovery.canAdopt) {
1256
+ if (!existing) {
1257
+ return yield* Effect.fail(
1258
+ new Error(
1259
+ `Container application for Durable Object namespace "${durableObjects.namespaceId}" already exists but could not be found for adoption.`,
1260
+ ),
1261
+ );
1262
+ }
1263
+ return yield* upsertApplication({
1264
+ id,
1265
+ news,
1266
+ existing: toAttributes(existing),
1267
+ session,
1268
+ });
1269
+ }
1270
+ }
1177
1271
  yield* Effect.logInfo(
1178
1272
  `Cloudflare Container create: recreating pre-created application ${name} with durable object binding`,
1179
1273
  );
@@ -1258,6 +1352,24 @@ await Effect.runPromise(serverEffect).catch((err) => {
1258
1352
  );
1259
1353
  }),
1260
1354
  read: Effect.fnUntraced(function* ({ id, olds, output }) {
1355
+ const readByName = (name: string) =>
1356
+ Effect.gen(function* () {
1357
+ yield* Effect.logInfo(
1358
+ `Cloudflare Container read: looking up ${name}`,
1359
+ );
1360
+ const existing = yield* findApplicationByName(name);
1361
+ if (!existing) {
1362
+ yield* Effect.logInfo(
1363
+ `Cloudflare Container read: ${name} not found`,
1364
+ );
1365
+ return undefined;
1366
+ }
1367
+ return {
1368
+ ...toAttributes(existing),
1369
+ hash: output?.hash,
1370
+ };
1371
+ });
1372
+
1261
1373
  if (output?.applicationId) {
1262
1374
  yield* Effect.logInfo(
1263
1375
  `Cloudflare Container read: checking ${output.applicationName}`,
@@ -1271,24 +1383,13 @@ await Effect.runPromise(serverEffect).catch((err) => {
1271
1383
  hash: output.hash,
1272
1384
  })),
1273
1385
  Effect.catchTag("ContainerApplicationNotFound", () =>
1274
- Effect.succeed(undefined),
1386
+ readByName(output.applicationName),
1275
1387
  ),
1276
1388
  );
1277
1389
  }
1278
1390
 
1279
1391
  const name = yield* createApplicationName(id, olds?.name);
1280
- yield* Effect.logInfo(
1281
- `Cloudflare Container read: looking up ${name}`,
1282
- );
1283
- const existing = yield* findApplicationByName(name);
1284
- if (!existing) {
1285
- yield* Effect.logInfo(
1286
- `Cloudflare Container read: ${name} not found`,
1287
- );
1288
- }
1289
- return existing
1290
- ? { ...toAttributes(existing), hash: output?.hash }
1291
- : undefined;
1392
+ return yield* readByName(name);
1292
1393
  }),
1293
1394
  tail: ({ output }) =>
1294
1395
  telemetry.tailStream({
@@ -123,6 +123,38 @@ export const DatabaseProvider = (): Layer<
123
123
  return { action: "update" } as const;
124
124
  }
125
125
  }),
126
+ read: Effect.fn(function* ({ id, output, olds }) {
127
+ if (output?.databaseId) {
128
+ return yield* getDb({
129
+ accountId: output.accountId,
130
+ databaseId: output.databaseId,
131
+ }).pipe(
132
+ Effect.map((db) => ({
133
+ databaseId: db.uuid ?? output.databaseId,
134
+ databaseName: db.name ?? output.databaseName,
135
+ jurisdiction: output.jurisdiction,
136
+ readReplication: db.readReplication ?? undefined,
137
+ accountId: output.accountId,
138
+ })),
139
+ Effect.catchTag("DatabaseNotFound", () =>
140
+ Effect.succeed(undefined),
141
+ ),
142
+ );
143
+ }
144
+ const name = yield* createDatabaseName(id, olds?.name);
145
+ const dbs = yield* listDbs({ accountId, name });
146
+ const match = dbs.result.find((db) => db.name === name);
147
+ if (match) {
148
+ return {
149
+ databaseId: match.uuid!,
150
+ databaseName: match.name ?? name,
151
+ jurisdiction: (olds?.jurisdiction ?? "default") as Jurisdiction,
152
+ readReplication: olds?.readReplication,
153
+ accountId,
154
+ };
155
+ }
156
+ return undefined;
157
+ }),
126
158
  create: Effect.fn(function* ({ id, news = {} }) {
127
159
  const name = yield* createDatabaseName(id, news.name);
128
160
  const jurisdiction = news.jurisdiction ?? "default";
@@ -185,38 +217,6 @@ export const DatabaseProvider = (): Layer<
185
217
  databaseId: output.databaseId,
186
218
  }).pipe(Effect.catchTag("DatabaseNotFound", () => Effect.void));
187
219
  }),
188
- read: Effect.fn(function* ({ id, output, olds }) {
189
- if (output?.databaseId) {
190
- return yield* getDb({
191
- accountId: output.accountId,
192
- databaseId: output.databaseId,
193
- }).pipe(
194
- Effect.map((db) => ({
195
- databaseId: db.uuid ?? output.databaseId,
196
- databaseName: db.name ?? output.databaseName,
197
- jurisdiction: output.jurisdiction,
198
- readReplication: db.readReplication ?? undefined,
199
- accountId: output.accountId,
200
- })),
201
- Effect.catchTag("DatabaseNotFound", () =>
202
- Effect.succeed(undefined),
203
- ),
204
- );
205
- }
206
- const name = yield* createDatabaseName(id, olds?.name);
207
- const dbs = yield* listDbs({ accountId, name });
208
- const match = dbs.result.find((db) => db.name === name);
209
- if (match) {
210
- return {
211
- databaseId: match.uuid!,
212
- databaseName: match.name ?? name,
213
- jurisdiction: (olds?.jurisdiction ?? "default") as Jurisdiction,
214
- readReplication: olds?.readReplication,
215
- accountId,
216
- };
217
- }
218
- return undefined;
219
- }),
220
220
  };
221
221
  }),
222
222
  );
@@ -3,12 +3,14 @@ import * as Effect from "effect/Effect";
3
3
  import * as Layer from "effect/Layer";
4
4
  import * as Binding from "../../Binding.ts";
5
5
  import { WorkerEnvironment } from "../Workers/Worker.ts";
6
- import type { Namespace } from "./Namespace.ts";
7
- import { NamespaceBinding } from "./NamespaceBinding.ts";
6
+ import type { KVNamespace } from "./KVNamespace.ts";
7
+ import { NamespaceBinding } from "./KVNamespaceBinding.ts";
8
8
 
9
9
  export class Delete extends Binding.Service<
10
10
  Delete,
11
- (namespace: Namespace) => Effect.Effect<(key: string) => Effect.Effect<void>>
11
+ (
12
+ namespace: KVNamespace,
13
+ ) => Effect.Effect<(key: string) => Effect.Effect<void>>
12
14
  >()("Cloudflare.KV.Delete") {}
13
15
 
14
16
  export const DeleteLive = Layer.effect(
@@ -17,7 +19,7 @@ export const DeleteLive = Layer.effect(
17
19
  const Policy = yield* DeletePolicy;
18
20
  const env = yield* WorkerEnvironment;
19
21
 
20
- return Effect.fn(function* (namespace: Namespace) {
22
+ return Effect.fn(function* (namespace: KVNamespace) {
21
23
  yield* Policy(namespace);
22
24
  const kvNamespace = (env as Record<string, runtime.KVNamespace>)[
23
25
  namespace.LogicalId
@@ -32,7 +34,7 @@ export const DeleteLive = Layer.effect(
32
34
 
33
35
  export class DeletePolicy extends Binding.Policy<
34
36
  DeletePolicy,
35
- (namespace: Namespace) => Effect.Effect<void>
37
+ (namespace: KVNamespace) => Effect.Effect<void>
36
38
  >()("Cloudflare.KV.Delete") {}
37
39
 
38
40
  export const DeletePolicyLive = DeletePolicy.layer.succeed(NamespaceBinding);
@@ -3,13 +3,13 @@ import * as Effect from "effect/Effect";
3
3
  import * as Layer from "effect/Layer";
4
4
  import * as Binding from "../../Binding.ts";
5
5
  import { WorkerEnvironment } from "../Workers/Worker.ts";
6
- import type { Namespace } from "./Namespace.ts";
7
- import { NamespaceBinding } from "./NamespaceBinding.ts";
6
+ import type { KVNamespace } from "./KVNamespace.ts";
7
+ import { NamespaceBinding } from "./KVNamespaceBinding.ts";
8
8
 
9
9
  export class Get extends Binding.Service<
10
10
  Get,
11
11
  (
12
- namespace: Namespace,
12
+ namespace: KVNamespace,
13
13
  ) => Effect.Effect<(key: string) => Effect.Effect<string | null>>
14
14
  >()("Cloudflare.KV.Get") {}
15
15
 
@@ -19,7 +19,7 @@ export const GetLive = Layer.effect(
19
19
  const Policy = yield* GetPolicy;
20
20
  const env = yield* WorkerEnvironment;
21
21
 
22
- return Effect.fn(function* (namespace: Namespace) {
22
+ return Effect.fn(function* (namespace: KVNamespace) {
23
23
  yield* Policy(namespace);
24
24
  const kvNamespace = (env as Record<string, runtime.KVNamespace>)[
25
25
  namespace.LogicalId
@@ -34,7 +34,7 @@ export const GetLive = Layer.effect(
34
34
 
35
35
  export class GetPolicy extends Binding.Policy<
36
36
  GetPolicy,
37
- (namespace: Namespace) => Effect.Effect<void>
37
+ (namespace: KVNamespace) => Effect.Effect<void>
38
38
  >()("Cloudflare.KV.Get") {}
39
39
 
40
40
  export const GetPolicyLive = GetPolicy.layer.succeed(NamespaceBinding);
@@ -3,15 +3,15 @@ import * as Effect from "effect/Effect";
3
3
  import * as Layer from "effect/Layer";
4
4
  import * as Binding from "../../Binding.ts";
5
5
  import { WorkerEnvironment } from "../Workers/Worker.ts";
6
- import type { Namespace } from "./Namespace.ts";
7
- import { NamespaceBinding } from "./NamespaceBinding.ts";
6
+ import type { KVNamespace } from "./KVNamespace.ts";
7
+ import { NamespaceBinding } from "./KVNamespaceBinding.ts";
8
8
 
9
9
  export interface GetWithMetadataOptions extends runtime.KVNamespaceGetOptions<undefined> {}
10
10
 
11
11
  export class GetWithMetadata extends Binding.Service<
12
12
  GetWithMetadata,
13
13
  (
14
- namespace: Namespace,
14
+ namespace: KVNamespace,
15
15
  ) => Effect.Effect<
16
16
  <Metadata = unknown>(
17
17
  key: string,
@@ -28,7 +28,7 @@ export const GetWithMetadataLive = Layer.effect(
28
28
  const Policy = yield* GetWithMetadataPolicy;
29
29
  const env = yield* WorkerEnvironment;
30
30
 
31
- return Effect.fn(function* (namespace: Namespace) {
31
+ return Effect.fn(function* (namespace: KVNamespace) {
32
32
  yield* Policy(namespace);
33
33
  const kvNamespace = (env as Record<string, runtime.KVNamespace>)[
34
34
  namespace.LogicalId
@@ -47,7 +47,7 @@ export const GetWithMetadataLive = Layer.effect(
47
47
 
48
48
  export class GetWithMetadataPolicy extends Binding.Policy<
49
49
  GetWithMetadataPolicy,
50
- (namespace: Namespace) => Effect.Effect<void>
50
+ (namespace: KVNamespace) => Effect.Effect<void>
51
51
  >()("Cloudflare.KV.GetWithMetadata") {}
52
52
 
53
53
  export const GetWithMetadataPolicyLive =
@@ -21,8 +21,8 @@ export type NamespaceProps = {
21
21
  title?: string;
22
22
  };
23
23
 
24
- export interface Namespace extends Resource<
25
- "Cloudflare.KV.Namespace",
24
+ export type KVNamespace = Resource<
25
+ "Cloudflare.KVNamespace",
26
26
  NamespaceProps,
27
27
  {
28
28
  title: string;
@@ -30,16 +30,16 @@ export interface Namespace extends Resource<
30
30
  supportsUrlEncoding: boolean | undefined;
31
31
  accountId: string;
32
32
  }
33
- > {}
33
+ >;
34
34
 
35
- export const Namespace = Resource<Namespace>("Cloudflare.KV.Namespace");
35
+ export const KVNamespace = Resource<KVNamespace>("Cloudflare.KVNamespace");
36
36
 
37
37
  export const NamespaceProvider = (): Layer<
38
- Provider<Namespace>,
38
+ Provider<KVNamespace>,
39
39
  never,
40
40
  Account | Credentials | HttpClient | Stack | Stage
41
41
  > =>
42
- Namespace.provider.effect(
42
+ KVNamespace.provider.effect(
43
43
  Effect.gen(function* () {
44
44
  const accountId = yield* Account;
45
45
  const createNamespace = yield* kv.createNamespace;
@@ -1,11 +1,11 @@
1
1
  import * as Effect from "effect/Effect";
2
2
  import type { ResourceLike } from "../../Resource.ts";
3
3
  import { isWorker } from "../Workers/Worker.ts";
4
- import type { Namespace } from "./Namespace.ts";
4
+ import type { KVNamespace } from "./KVNamespace.ts";
5
5
 
6
6
  export const NamespaceBinding = Effect.fn(function* (
7
7
  host: ResourceLike,
8
- namespace: Namespace,
8
+ namespace: KVNamespace,
9
9
  ) {
10
10
  if (isWorker(host)) {
11
11
  yield* host.bind`Bind(${namespace})`({