alchemy 2.0.0-beta.82 → 2.0.0-beta.83
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/lib/Cloudflare/Containers/Container.d.ts +128 -40
- package/lib/Cloudflare/Containers/Container.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/Container.js +124 -24
- package/lib/Cloudflare/Containers/Container.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerApplication.d.ts +59 -50
- package/lib/Cloudflare/Containers/ContainerApplication.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerApplication.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerClient.d.ts +20 -7
- package/lib/Cloudflare/Containers/ContainerClient.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerClient.js +22 -5
- package/lib/Cloudflare/Containers/ContainerClient.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerConfiguration.d.ts +132 -13
- package/lib/Cloudflare/Containers/ContainerConfiguration.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerConfiguration.js +219 -35
- package/lib/Cloudflare/Containers/ContainerConfiguration.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerPlatform.d.ts +1 -1
- package/lib/Cloudflare/Containers/ContainerPlatform.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerPlatform.js +21 -2
- package/lib/Cloudflare/Containers/ContainerPlatform.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerProvider.js +74 -177
- package/lib/Cloudflare/Containers/ContainerProvider.js.map +1 -1
- package/lib/Cloudflare/Containers/DurableObjectApplication.d.ts +84 -0
- package/lib/Cloudflare/Containers/DurableObjectApplication.d.ts.map +1 -0
- package/lib/Cloudflare/Containers/DurableObjectApplication.js +249 -0
- package/lib/Cloudflare/Containers/DurableObjectApplication.js.map +1 -0
- package/lib/Cloudflare/Containers/LocalContainerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/LocalContainerProvider.js +20 -41
- package/lib/Cloudflare/Containers/LocalContainerProvider.js.map +1 -1
- package/lib/Cloudflare/Containers/index.d.ts +2 -1
- package/lib/Cloudflare/Containers/index.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/index.js +2 -1
- package/lib/Cloudflare/Containers/index.js.map +1 -1
- package/lib/Cloudflare/Providers.d.ts.map +1 -1
- package/lib/Cloudflare/Providers.js +2 -1
- package/lib/Cloudflare/Providers.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +3 -0
- package/lib/Cloudflare/Workers/DurableObject.d.ts +75 -1
- package/lib/Cloudflare/Workers/DurableObject.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObject.js +50 -0
- package/lib/Cloudflare/Workers/DurableObject.js.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +29 -23
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +2 -0
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +67 -4
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts +6 -0
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerProvider.js +54 -8
- package/lib/Cloudflare/Workers/WorkerProvider.js.map +1 -1
- package/lib/State/HttpStateApi.d.ts +13 -13
- package/package.json +6 -6
- package/src/Cloudflare/Containers/Container.ts +126 -56
- package/src/Cloudflare/Containers/ContainerApplication.ts +60 -53
- package/src/Cloudflare/Containers/ContainerClient.ts +47 -13
- package/src/Cloudflare/Containers/ContainerConfiguration.ts +316 -44
- package/src/Cloudflare/Containers/ContainerPlatform.ts +27 -3
- package/src/Cloudflare/Containers/ContainerProvider.ts +96 -256
- package/src/Cloudflare/Containers/DurableObjectApplication.ts +382 -0
- package/src/Cloudflare/Containers/LocalContainerProvider.ts +20 -48
- package/src/Cloudflare/Containers/index.ts +10 -0
- package/src/Cloudflare/Providers.ts +2 -0
- package/src/Cloudflare/Workers/DurableObject.ts +77 -1
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +32 -21
- package/src/Cloudflare/Workers/Worker.ts +2 -0
- package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +95 -4
- package/src/Cloudflare/Workers/WorkerProvider.ts +78 -13
|
@@ -27,7 +27,6 @@ import { CloudflareLogs, type TelemetryFilter } from "../Logs.ts";
|
|
|
27
27
|
import type {
|
|
28
28
|
AnyContainerApplicationProps,
|
|
29
29
|
ContainerApplication,
|
|
30
|
-
DevContainerImage,
|
|
31
30
|
DurableObjectContainerProps,
|
|
32
31
|
} from "./ContainerApplication.ts";
|
|
33
32
|
import {
|
|
@@ -41,15 +40,15 @@ import {
|
|
|
41
40
|
} from "./ContainerBundle.ts";
|
|
42
41
|
import {
|
|
43
42
|
ContainerConfigurationError,
|
|
44
|
-
isDurableObjectContainer,
|
|
45
43
|
durableObjectPlaceholder,
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
hasImageSource,
|
|
45
|
+
isDurableObjectContainer,
|
|
48
46
|
validateContainerConfiguration,
|
|
49
47
|
} from "./ContainerConfiguration.ts";
|
|
50
48
|
import { waitForContainerImage } from "./ContainerImagePreparation.ts";
|
|
51
49
|
import { ContainerPlatform } from "./ContainerPlatform.ts";
|
|
52
50
|
import { retryContainerPublication } from "./ContainerPublication.ts";
|
|
51
|
+
import { retryNamespacePropagation } from "./DurableObjectApplication.ts";
|
|
53
52
|
|
|
54
53
|
/**
|
|
55
54
|
* The image source resolved from a {@link ContainerApplicationProps}. Selects
|
|
@@ -126,6 +125,9 @@ class ContainerRegistryError extends Schema.TaggedError<ContainerRegistryError>(
|
|
|
126
125
|
},
|
|
127
126
|
) {}
|
|
128
127
|
|
|
128
|
+
/** The explicit application name. Durable Object-managed Containers have none. */
|
|
129
|
+
const nameOf = (props: AnyContainerApplicationProps | undefined) => props?.name;
|
|
130
|
+
|
|
129
131
|
const digestFromImageRef = (imageRef: string) => {
|
|
130
132
|
const separator = imageRef.lastIndexOf("@");
|
|
131
133
|
if (separator === -1) return undefined;
|
|
@@ -766,29 +768,11 @@ export const LiveContainerProvider = () =>
|
|
|
766
768
|
});
|
|
767
769
|
|
|
768
770
|
// ---------------------------------------------------------------
|
|
769
|
-
// Durable Object-managed
|
|
770
|
-
// "durable_object").
|
|
771
|
-
//
|
|
771
|
+
// Durable Object-managed Containers (schedulingPolicy:
|
|
772
|
+
// "durable_object"). A Container publishes one image; the application
|
|
773
|
+
// belongs to the Durable Object class (see DurableObjectApplication).
|
|
772
774
|
// ---------------------------------------------------------------
|
|
773
775
|
|
|
774
|
-
/** Resolve the build inputs and content hash of every named image. */
|
|
775
|
-
const computeNamedImages = Effect.fn(function* (
|
|
776
|
-
id: string,
|
|
777
|
-
props: DurableObjectContainerProps,
|
|
778
|
-
) {
|
|
779
|
-
return yield* Effect.forEach(
|
|
780
|
-
Object.entries(props.images ?? {}),
|
|
781
|
-
Effect.fn(function* ([name, source]) {
|
|
782
|
-
const imageId = `${id}-${name}`;
|
|
783
|
-
const image = yield* computeImage(imageId, source, {});
|
|
784
|
-
return { name, imageId, source, ...image };
|
|
785
|
-
}),
|
|
786
|
-
);
|
|
787
|
-
});
|
|
788
|
-
|
|
789
|
-
const hashesByName = (images: { name: string; imageHash: string }[]) =>
|
|
790
|
-
Object.fromEntries(images.map((image) => [image.name, image.imageHash]));
|
|
791
|
-
|
|
792
776
|
/**
|
|
793
777
|
* Cloudflare must prepare an image before a Worker upload may reference
|
|
794
778
|
* it. Preparation is per image reference, so remember the ones that are
|
|
@@ -813,198 +797,98 @@ export const LiveContainerProvider = () =>
|
|
|
813
797
|
preparedImages.add(image);
|
|
814
798
|
});
|
|
815
799
|
|
|
800
|
+
/** The build inputs and content hash of a Durable Object Container's image. */
|
|
801
|
+
const computeDurableObjectImage = (id: string, props: DurableObjectContainerProps) =>
|
|
802
|
+
hasImageSource(props) ? computeImage(id, props, {}) : Effect.succeed(undefined);
|
|
803
|
+
|
|
816
804
|
/**
|
|
817
|
-
* Publish
|
|
818
|
-
*
|
|
819
|
-
*
|
|
805
|
+
* Publish the Container's image to Cloudflare's registry and wait until
|
|
806
|
+
* it is prepared. An unchanged content hash keeps the previously
|
|
807
|
+
* published reference.
|
|
820
808
|
*/
|
|
821
|
-
const
|
|
822
|
-
id: string,
|
|
823
|
-
props: DurableObjectContainerProps,
|
|
824
|
-
output: ContainerApplication["Attributes"] | undefined,
|
|
825
|
-
session: ScopedPlanStatusSession,
|
|
826
|
-
) {
|
|
827
|
-
const images: Record<string, string> = {};
|
|
828
|
-
const devImages: Record<string, DevContainerImage> = {};
|
|
829
|
-
const hashes: Record<string, string> = {};
|
|
830
|
-
|
|
831
|
-
for (const image of yield* computeNamedImages(id, props)) {
|
|
832
|
-
const previousRef = output?.images?.[image.name];
|
|
833
|
-
const unchanged = output?.hash?.images?.[image.name] === image.imageHash;
|
|
834
|
-
|
|
835
|
-
let imageRef: string;
|
|
836
|
-
if (previousRef !== undefined && unchanged) {
|
|
837
|
-
imageRef = previousRef;
|
|
838
|
-
} else {
|
|
839
|
-
const published = yield* buildAndPushImage(
|
|
840
|
-
image.imageId,
|
|
841
|
-
image.source,
|
|
842
|
-
image.build,
|
|
843
|
-
image.imageRef,
|
|
844
|
-
image.imageHash,
|
|
845
|
-
undefined,
|
|
846
|
-
session,
|
|
847
|
-
// The registry tag keyed by image inputs is the durable publication checkpoint.
|
|
848
|
-
// Reuse it after an interrupted preparation, including mirrored images.
|
|
849
|
-
true,
|
|
850
|
-
);
|
|
851
|
-
imageRef = published.imageRef;
|
|
852
|
-
}
|
|
853
|
-
yield* waitForImagePrepared(imageRef, image.name, props.imagePreparationTimeout, session);
|
|
854
|
-
|
|
855
|
-
images[image.name] = imageRef;
|
|
856
|
-
devImages[image.name] = image.dev;
|
|
857
|
-
hashes[image.name] = image.imageHash;
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
return {
|
|
861
|
-
images,
|
|
862
|
-
devImages,
|
|
863
|
-
hash: yield* namedImageHash(hashes),
|
|
864
|
-
};
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
const diffDurableObjectApplication = Effect.fn(function* ({
|
|
809
|
+
const reconcileDurableObjectContainer = Effect.fn(function* ({
|
|
868
810
|
id,
|
|
869
811
|
news,
|
|
870
812
|
name,
|
|
871
|
-
oldName,
|
|
872
|
-
accountId,
|
|
873
813
|
output,
|
|
874
|
-
|
|
814
|
+
session,
|
|
875
815
|
}: {
|
|
876
816
|
id: string;
|
|
877
817
|
news: DurableObjectContainerProps;
|
|
878
818
|
name: string;
|
|
879
|
-
oldName: string;
|
|
880
|
-
accountId: string;
|
|
881
819
|
output: ContainerApplication["Attributes"] | undefined;
|
|
882
|
-
|
|
820
|
+
session: ScopedPlanStatusSession;
|
|
883
821
|
}) {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
if (output.images === undefined || !deepEqual(hashes, output.hash?.images)) {
|
|
917
|
-
return { action: "update" } as const;
|
|
822
|
+
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
823
|
+
const attributes = durableObjectPlaceholder({
|
|
824
|
+
// The application is owned by the Durable Object class.
|
|
825
|
+
applicationId: "",
|
|
826
|
+
applicationName: name,
|
|
827
|
+
accountId,
|
|
828
|
+
createdAt: output?.createdAt ?? "",
|
|
829
|
+
observability: news.observability,
|
|
830
|
+
});
|
|
831
|
+
const image = yield* computeDurableObjectImage(id, news);
|
|
832
|
+
if (image === undefined) return attributes;
|
|
833
|
+
|
|
834
|
+
let imageRef: string;
|
|
835
|
+
let digest: string | undefined;
|
|
836
|
+
if (output?.image !== undefined && output.hash?.image === image.imageHash) {
|
|
837
|
+
imageRef = output.image;
|
|
838
|
+
digest = output.hash.digest;
|
|
839
|
+
} else {
|
|
840
|
+
const published = yield* buildAndPushImage(
|
|
841
|
+
id,
|
|
842
|
+
news,
|
|
843
|
+
image.build,
|
|
844
|
+
image.imageRef,
|
|
845
|
+
image.imageHash,
|
|
846
|
+
undefined,
|
|
847
|
+
session,
|
|
848
|
+
// The registry tag keyed by image inputs is the durable publication checkpoint.
|
|
849
|
+
// Reuse it after an interrupted preparation, including mirrored images.
|
|
850
|
+
true,
|
|
851
|
+
);
|
|
852
|
+
imageRef = published.imageRef;
|
|
853
|
+
digest = published.digest;
|
|
918
854
|
}
|
|
919
|
-
|
|
855
|
+
yield* waitForImagePrepared(imageRef, id, news.imagePreparationTimeout, session);
|
|
856
|
+
return {
|
|
857
|
+
...attributes,
|
|
858
|
+
image: imageRef,
|
|
859
|
+
dev: image.dev,
|
|
860
|
+
hash: { image: image.imageHash, digest },
|
|
861
|
+
} satisfies ContainerApplication["Attributes"];
|
|
920
862
|
});
|
|
921
863
|
|
|
922
|
-
const
|
|
864
|
+
const diffDurableObjectContainer = Effect.fn(function* ({
|
|
923
865
|
id,
|
|
924
866
|
news,
|
|
925
|
-
olds,
|
|
926
|
-
name,
|
|
927
|
-
durableObjects,
|
|
928
867
|
output,
|
|
929
|
-
session,
|
|
930
868
|
}: {
|
|
931
869
|
id: string;
|
|
932
870
|
news: DurableObjectContainerProps;
|
|
933
|
-
olds: AnyContainerApplicationProps | undefined;
|
|
934
|
-
name: string;
|
|
935
|
-
durableObjects: { namespaceId: string } | undefined;
|
|
936
871
|
output: ContainerApplication["Attributes"] | undefined;
|
|
937
|
-
session: ScopedPlanStatusSession;
|
|
938
872
|
}) {
|
|
939
|
-
if (
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
const applicationId = durableObjects.namespaceId;
|
|
947
|
-
const getApplication = Containers.getContainerApplication({
|
|
948
|
-
accountId,
|
|
949
|
-
applicationId,
|
|
950
|
-
});
|
|
951
|
-
const assertMatchesDeclaration = (
|
|
952
|
-
application: Containers.GetContainerApplicationResponse,
|
|
953
|
-
) => {
|
|
954
|
-
const matches =
|
|
955
|
-
application.id === applicationId &&
|
|
956
|
-
application.name === name &&
|
|
957
|
-
application.schedulingPolicy === "durable_object" &&
|
|
958
|
-
application.durableObjects?.namespaceId === applicationId;
|
|
959
|
-
if (matches) return Effect.void;
|
|
960
|
-
return Effect.fail(
|
|
961
|
-
new ContainerConfigurationError({
|
|
962
|
-
message: `The application for Durable Object namespace '${applicationId}' does not match '${name}'. Its name, namespace, and scheduling policy must match before updating it.`,
|
|
963
|
-
}),
|
|
964
|
-
);
|
|
965
|
-
};
|
|
966
|
-
|
|
967
|
-
// 1. Observe
|
|
968
|
-
let application = yield* getApplication.pipe(
|
|
969
|
-
Effect.catchTag("ContainerApplicationNotFound", () => Effect.succeed(undefined)),
|
|
970
|
-
);
|
|
971
|
-
if (application) yield* assertMatchesDeclaration(application);
|
|
972
|
-
|
|
973
|
-
// 2. Publish images. The Worker upload that follows references them.
|
|
974
|
-
const published = yield* publishNamedImages(id, news, output, session);
|
|
975
|
-
|
|
976
|
-
const configuration: Containers.DurableObjectContainerConfiguration = {
|
|
977
|
-
...(news.ssh !== undefined ? { wranglerSsh: news.ssh } : {}),
|
|
978
|
-
...(news.authorizedKeys !== undefined ? { authorizedKeys: news.authorizedKeys } : {}),
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
// 3. Ensure the application exists
|
|
982
|
-
if (!application) {
|
|
983
|
-
yield* session.note(`Creating container application ${name}...`);
|
|
984
|
-
application = yield* retryNamespacePropagation(
|
|
985
|
-
Containers.createDurableObjectContainerApplication({
|
|
986
|
-
accountId,
|
|
987
|
-
name,
|
|
988
|
-
schedulingPolicy: "durable_object",
|
|
989
|
-
durableObjects,
|
|
990
|
-
configuration,
|
|
991
|
-
observability: news.observability,
|
|
992
|
-
}),
|
|
993
|
-
).pipe(Effect.catchTag("DurableObjectAlreadyHasApplication", () => getApplication));
|
|
994
|
-
yield* assertMatchesDeclaration(application);
|
|
873
|
+
if (output === undefined) return undefined;
|
|
874
|
+
// State written before one Container meant one image recorded the
|
|
875
|
+
// Container as the application itself. The application now belongs to
|
|
876
|
+
// the Durable Object class; drop the identity so deleting this
|
|
877
|
+
// Container never deletes the class's application.
|
|
878
|
+
if (output.applicationId !== "") {
|
|
879
|
+
return { action: "update" as const, stables: ["accountId"] };
|
|
995
880
|
}
|
|
996
|
-
|
|
997
|
-
//
|
|
998
|
-
const
|
|
999
|
-
if (
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
}
|
|
881
|
+
// Image content (a Dockerfile, a context file) can change without any
|
|
882
|
+
// prop changing, so compare content hashes as well.
|
|
883
|
+
const image = yield* computeDurableObjectImage(id, news);
|
|
884
|
+
if (
|
|
885
|
+
image?.imageHash !== output.hash?.image ||
|
|
886
|
+
(image !== undefined && output.image === undefined) ||
|
|
887
|
+
!deepEqual(image?.dev, output.dev)
|
|
888
|
+
) {
|
|
889
|
+
return { action: "update" } as const;
|
|
1005
890
|
}
|
|
1006
|
-
|
|
1007
|
-
return { ...toAttributes(application), ...published };
|
|
891
|
+
return undefined;
|
|
1008
892
|
});
|
|
1009
893
|
|
|
1010
894
|
const maybeCreateRollout = Effect.fn(function* ({
|
|
@@ -1325,22 +1209,15 @@ export const LiveContainerProvider = () =>
|
|
|
1325
1209
|
if (!isResolved(newBindings)) return;
|
|
1326
1210
|
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
1327
1211
|
|
|
1328
|
-
const oldName =
|
|
1212
|
+
const oldName =
|
|
1213
|
+
output?.applicationName ?? (yield* createApplicationName(id, nameOf(olds)));
|
|
1329
1214
|
// Auto-generated names are engine-owned: the deployed name stays
|
|
1330
1215
|
// authoritative even if the generator would name this id differently
|
|
1331
1216
|
// today. Only an explicit user-provided name can force a replace.
|
|
1332
|
-
const name = news
|
|
1217
|
+
const name = nameOf(news) ?? oldName;
|
|
1333
1218
|
|
|
1334
1219
|
if (isDurableObjectContainer(news)) {
|
|
1335
|
-
return yield*
|
|
1336
|
-
id,
|
|
1337
|
-
news,
|
|
1338
|
-
name,
|
|
1339
|
-
oldName,
|
|
1340
|
-
accountId,
|
|
1341
|
-
output,
|
|
1342
|
-
newBindings,
|
|
1343
|
-
});
|
|
1220
|
+
return yield* diffDurableObjectContainer({ id, news, output });
|
|
1344
1221
|
}
|
|
1345
1222
|
|
|
1346
1223
|
if ((output?.accountId ?? accountId) !== accountId || name !== oldName) {
|
|
@@ -1392,24 +1269,18 @@ export const LiveContainerProvider = () =>
|
|
|
1392
1269
|
}),
|
|
1393
1270
|
precreate: Effect.fn(function* ({ id, news = {}, session }) {
|
|
1394
1271
|
yield* validateContainerConfiguration(news);
|
|
1395
|
-
const name = yield* createApplicationName(id, news
|
|
1272
|
+
const name = yield* createApplicationName(id, nameOf(news));
|
|
1396
1273
|
yield* Effect.logInfo(`Cloudflare Container precreate: starting ${name}`);
|
|
1397
1274
|
|
|
1398
1275
|
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
1399
1276
|
if (isDurableObjectContainer(news)) {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
accountId,
|
|
1408
|
-
createdAt: "",
|
|
1409
|
-
observability: news.observability,
|
|
1410
|
-
}),
|
|
1411
|
-
...(yield* publishNamedImages(id, news, undefined, session)),
|
|
1412
|
-
} satisfies ContainerApplication["Attributes"];
|
|
1277
|
+
return yield* reconcileDurableObjectContainer({
|
|
1278
|
+
id,
|
|
1279
|
+
news,
|
|
1280
|
+
name,
|
|
1281
|
+
output: undefined,
|
|
1282
|
+
session,
|
|
1283
|
+
});
|
|
1413
1284
|
}
|
|
1414
1285
|
const env = makeContainerEnv(news, accountId);
|
|
1415
1286
|
const { build, imageRef, imageHash, dev } = yield* computeImage(id, news, env);
|
|
@@ -1458,24 +1329,16 @@ export const LiveContainerProvider = () =>
|
|
|
1458
1329
|
dev,
|
|
1459
1330
|
};
|
|
1460
1331
|
}),
|
|
1461
|
-
reconcile: Effect.fn(function* ({ id, news = {},
|
|
1332
|
+
reconcile: Effect.fn(function* ({ id, news = {}, bindings, output, session }) {
|
|
1462
1333
|
yield* validateContainerConfiguration(news, output?.schedulingPolicy);
|
|
1463
1334
|
// Prefer the deployed name: regenerating would target a different
|
|
1464
1335
|
// resource if the generator's output for this id ever drifts.
|
|
1465
|
-
const name = output?.applicationName ?? (yield* createApplicationName(id, news
|
|
1336
|
+
const name = output?.applicationName ?? (yield* createApplicationName(id, nameOf(news)));
|
|
1466
1337
|
yield* Effect.logInfo(`Cloudflare Container reconcile: starting ${name}`);
|
|
1467
|
-
const durableObjects = yield* getDurableObjects(bindings);
|
|
1468
1338
|
if (isDurableObjectContainer(news)) {
|
|
1469
|
-
return yield*
|
|
1470
|
-
id,
|
|
1471
|
-
news,
|
|
1472
|
-
olds,
|
|
1473
|
-
name,
|
|
1474
|
-
durableObjects,
|
|
1475
|
-
output,
|
|
1476
|
-
session,
|
|
1477
|
-
});
|
|
1339
|
+
return yield* reconcileDurableObjectContainer({ id, news, name, output, session });
|
|
1478
1340
|
}
|
|
1341
|
+
const durableObjects = yield* getDurableObjects(bindings);
|
|
1479
1342
|
const hasUnresolvedAttachment =
|
|
1480
1343
|
durableObjects === undefined &&
|
|
1481
1344
|
bindings.some((binding) => binding.data.durableObjects !== undefined);
|
|
@@ -1701,8 +1564,7 @@ export const LiveContainerProvider = () =>
|
|
|
1701
1564
|
// API can't return — preserve the persisted one so a refresh
|
|
1702
1565
|
// doesn't wipe it (which would break a later `alchemy dev`).
|
|
1703
1566
|
dev: output?.dev,
|
|
1704
|
-
|
|
1705
|
-
devImages: output?.devImages,
|
|
1567
|
+
image: output?.image,
|
|
1706
1568
|
};
|
|
1707
1569
|
});
|
|
1708
1570
|
|
|
@@ -1723,8 +1585,7 @@ export const LiveContainerProvider = () =>
|
|
|
1723
1585
|
...toAttributes(app),
|
|
1724
1586
|
hash: output.hash,
|
|
1725
1587
|
dev: output.dev,
|
|
1726
|
-
|
|
1727
|
-
devImages: output.devImages,
|
|
1588
|
+
image: output.image,
|
|
1728
1589
|
})),
|
|
1729
1590
|
Effect.catchTag("ContainerApplicationNotFound", () =>
|
|
1730
1591
|
readByName(output.applicationName),
|
|
@@ -1734,12 +1595,12 @@ export const LiveContainerProvider = () =>
|
|
|
1734
1595
|
return attrs;
|
|
1735
1596
|
}
|
|
1736
1597
|
|
|
1737
|
-
const name = yield* createApplicationName(id, olds
|
|
1598
|
+
const name = yield* createApplicationName(id, nameOf(olds));
|
|
1738
1599
|
attrs = yield* readByName(name);
|
|
1739
1600
|
if (!attrs) return undefined;
|
|
1740
1601
|
// Generated names identify this instance by its random suffix.
|
|
1741
1602
|
// Explicit names alone do not establish ownership.
|
|
1742
|
-
return olds
|
|
1603
|
+
return nameOf(olds) === undefined ? attrs : Unowned(attrs);
|
|
1743
1604
|
}),
|
|
1744
1605
|
list: () =>
|
|
1745
1606
|
Effect.gen(function* () {
|
|
@@ -1831,27 +1692,6 @@ const isContainerApplicationNotFound = (
|
|
|
1831
1692
|
"_tag" in error &&
|
|
1832
1693
|
error._tag === "ContainerApplicationNotFound";
|
|
1833
1694
|
|
|
1834
|
-
/**
|
|
1835
|
-
* Right after a Worker upload creates a Durable Object namespace, the
|
|
1836
|
-
* containers API can report the namespace missing for a few seconds before it
|
|
1837
|
-
* sees it. Retry creates that attach to it, bounded at ~30s.
|
|
1838
|
-
*/
|
|
1839
|
-
const retryNamespacePropagation = <A, E extends { readonly _tag: string }, R>(
|
|
1840
|
-
effect: Effect.Effect<A, E, R>,
|
|
1841
|
-
) =>
|
|
1842
|
-
effect.pipe(
|
|
1843
|
-
Effect.tapError((error) =>
|
|
1844
|
-
error._tag === "DurableObjectNamespaceNotFound"
|
|
1845
|
-
? Effect.logDebug("Cloudflare Container create: namespace not visible yet, retrying")
|
|
1846
|
-
: Effect.void,
|
|
1847
|
-
),
|
|
1848
|
-
Effect.retry({
|
|
1849
|
-
while: (error) => error._tag === "DurableObjectNamespaceNotFound",
|
|
1850
|
-
schedule: Schedule.spaced("2 seconds"),
|
|
1851
|
-
times: 15,
|
|
1852
|
-
}),
|
|
1853
|
-
);
|
|
1854
|
-
|
|
1855
1695
|
export const retryForContainerApplicationReadiness = <A, E, R>(
|
|
1856
1696
|
operation: string,
|
|
1857
1697
|
applicationId: string,
|