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.
- package/bin/alchemy-effect.js +29 -21
- package/bin/alchemy-effect.js.map +1 -1
- package/bin/alchemy-effect.ts +32 -68
- package/lib/cli/index.d.ts +1 -0
- package/lib/cli/index.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/AWS/AutoScaling/LaunchTemplate.ts +5 -4
- package/src/AWS/EC2/Instance.ts +3 -4
- package/src/AWS/ECS/Task.ts +36 -34
- package/src/AWS/Lambda/Function.ts +68 -68
- package/src/Apply.ts +2 -0
- package/src/Artifacts.ts +10 -1
- package/src/Binding.ts +20 -5
- package/src/Cloudflare/Container.ts +210 -109
- package/src/Cloudflare/D1/D1Database.ts +32 -32
- package/src/Cloudflare/KV/Delete.ts +7 -5
- package/src/Cloudflare/KV/Get.ts +5 -5
- package/src/Cloudflare/KV/GetWithMetadata.ts +5 -5
- package/src/Cloudflare/KV/{Namespace.ts → KVNamespace.ts} +6 -6
- package/src/Cloudflare/KV/{NamespaceBinding.ts → KVNamespaceBinding.ts} +2 -2
- package/src/Cloudflare/KV/List.ts +5 -5
- package/src/Cloudflare/KV/Put.ts +5 -5
- package/src/Cloudflare/KV/index.ts +1 -1
- package/src/Cloudflare/Providers.ts +2 -8
- package/src/Cloudflare/R2/{Bucket.ts → R2Bucket.ts} +19 -19
- package/src/Cloudflare/R2/R2BucketBinding.ts +322 -0
- package/src/Cloudflare/R2/index.ts +2 -9
- package/src/Cloudflare/Workers/Assets.ts +11 -5
- package/src/Cloudflare/Workers/DurableObject.ts +4 -33
- package/src/Cloudflare/Workers/HttpServer.ts +1 -0
- package/src/Cloudflare/Workers/InferEnv.ts +23 -0
- package/src/Cloudflare/Workers/WebSocket.ts +4 -3
- package/src/Cloudflare/Workers/Worker.ts +480 -165
- package/src/Cloudflare/Workers/cloudflare_workers.ts +13 -21
- package/src/Cloudflare/Workers/index.ts +1 -0
- package/src/Cloudflare/index.ts +2 -2
- package/src/Platform.ts +36 -55
- package/src/Provider.ts +1 -0
- package/src/Resource.ts +3 -2
- package/src/Stack.ts +5 -4
- package/src/Test/Bun.ts +207 -0
- package/src/Util/ConfigProvider.ts +25 -0
- package/src/Util/FileLogger.ts +22 -0
- package/src/Util/effect.ts +24 -0
- package/src/Cloudflare/R2/BucketBinding.ts +0 -31
- package/src/Cloudflare/R2/CreateMultipartUpload.ts +0 -59
- package/src/Cloudflare/R2/DeleteObject.ts +0 -41
- package/src/Cloudflare/R2/GetObject.ts +0 -47
- package/src/Cloudflare/R2/HeadObject.ts +0 -41
- package/src/Cloudflare/R2/ListObjects.ts +0 -45
- package/src/Cloudflare/R2/MultipartUploadClient.ts +0 -40
- package/src/Cloudflare/R2/PutObject.ts +0 -55
- package/src/Cloudflare/R2/ResumeMultipartUpload.ts +0 -48
- 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",
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
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*
|
|
860
|
-
|
|
917
|
+
yield* retryForContainerApplicationReadiness(
|
|
918
|
+
"rollout",
|
|
861
919
|
applicationId,
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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
|
-
|
|
1022
|
+
durableObjects
|
|
961
1023
|
? Effect.gen(function* () {
|
|
962
1024
|
const existing = yield* findApplicationByNamespace(
|
|
963
1025
|
durableObjects.namespaceId,
|
|
964
1026
|
);
|
|
965
|
-
|
|
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
|
|
1037
|
+
if (!existing) {
|
|
973
1038
|
return yield* Effect.fail(
|
|
974
1039
|
new Error(
|
|
975
|
-
`
|
|
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
|
-
|
|
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*
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
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
|
-
|
|
1386
|
+
readByName(output.applicationName),
|
|
1275
1387
|
),
|
|
1276
1388
|
);
|
|
1277
1389
|
}
|
|
1278
1390
|
|
|
1279
1391
|
const name = yield* createApplicationName(id, olds?.name);
|
|
1280
|
-
yield*
|
|
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 {
|
|
7
|
-
import { NamespaceBinding } from "./
|
|
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
|
-
(
|
|
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:
|
|
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:
|
|
37
|
+
(namespace: KVNamespace) => Effect.Effect<void>
|
|
36
38
|
>()("Cloudflare.KV.Delete") {}
|
|
37
39
|
|
|
38
40
|
export const DeletePolicyLive = DeletePolicy.layer.succeed(NamespaceBinding);
|
package/src/Cloudflare/KV/Get.ts
CHANGED
|
@@ -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 {
|
|
7
|
-
import { NamespaceBinding } from "./
|
|
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:
|
|
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:
|
|
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:
|
|
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 {
|
|
7
|
-
import { NamespaceBinding } from "./
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
25
|
-
"Cloudflare.
|
|
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
|
|
35
|
+
export const KVNamespace = Resource<KVNamespace>("Cloudflare.KVNamespace");
|
|
36
36
|
|
|
37
37
|
export const NamespaceProvider = (): Layer<
|
|
38
|
-
Provider<
|
|
38
|
+
Provider<KVNamespace>,
|
|
39
39
|
never,
|
|
40
40
|
Account | Credentials | HttpClient | Stack | Stage
|
|
41
41
|
> =>
|
|
42
|
-
|
|
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 {
|
|
4
|
+
import type { KVNamespace } from "./KVNamespace.ts";
|
|
5
5
|
|
|
6
6
|
export const NamespaceBinding = Effect.fn(function* (
|
|
7
7
|
host: ResourceLike,
|
|
8
|
-
namespace:
|
|
8
|
+
namespace: KVNamespace,
|
|
9
9
|
) {
|
|
10
10
|
if (isWorker(host)) {
|
|
11
11
|
yield* host.bind`Bind(${namespace})`({
|