alchemy 0.3.0 → 0.4.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/README.md +8 -3
- package/lib/ai/astro-file.js +7 -24
- package/lib/ai/client.d.ts +7 -0
- package/lib/ai/client.js +45 -0
- package/lib/ai/css-file.js +7 -24
- package/lib/ai/data.d.ts +26 -21
- package/lib/ai/data.js +33 -22
- package/lib/ai/document.d.ts +76 -5
- package/lib/ai/document.js +112 -56
- package/lib/ai/html-file.js +7 -24
- package/lib/ai/json-file.js +11 -32
- package/lib/ai/typescript-file.d.ts +1 -2
- package/lib/ai/typescript-file.js +8 -39
- package/lib/ai/vue-file.js +7 -24
- package/lib/ai/yaml-file.js +12 -48
- package/lib/alchemy.d.ts +5 -0
- package/lib/alchemy.js +61 -3
- package/lib/apply.js +18 -11
- package/lib/aws/account-id.d.ts +4 -1
- package/lib/aws/account-id.js +1 -1
- package/lib/aws/credentials.d.ts +5 -0
- package/lib/aws/credentials.js +0 -0
- package/lib/aws/oidc/oidc-provider.js +2 -2
- package/lib/aws/role.js +35 -23
- package/lib/cloudflare/account-api-token.d.ts +168 -0
- package/lib/cloudflare/account-api-token.js +179 -0
- package/lib/cloudflare/account-id.d.ts +1 -0
- package/lib/cloudflare/account-id.js +0 -0
- package/lib/cloudflare/api.d.ts +38 -1
- package/lib/cloudflare/api.js +55 -4
- package/lib/cloudflare/bucket.d.ts +118 -3
- package/lib/cloudflare/bucket.js +269 -89
- package/lib/cloudflare/custom-domain.d.ts +75 -0
- package/lib/cloudflare/custom-domain.js +154 -0
- package/lib/cloudflare/{dns.js → dns-records.js} +14 -1
- package/lib/cloudflare/generate-asset-manifest.js +1 -1
- package/lib/cloudflare/index.d.ts +5 -1
- package/lib/cloudflare/index.js +5 -1
- package/lib/cloudflare/permission-groups.d.ts +78 -0
- package/lib/cloudflare/permission-groups.js +48 -0
- package/lib/cloudflare/r2-rest-state-store.d.ts +2 -1
- package/lib/cloudflare/r2-rest-state-store.js +3 -2
- package/lib/cloudflare/static-site-router.js +16 -7
- package/lib/cloudflare/static-site.d.ts +17 -18
- package/lib/cloudflare/static-site.js +19 -7
- package/lib/cloudflare/upload-asset-manifest.js +4 -2
- package/lib/{util/encrypt.d.ts → encrypt.d.ts} +1 -1
- package/lib/{util/encrypt.js → encrypt.js} +1 -1
- package/lib/fs/copy-file.d.ts +54 -0
- package/lib/fs/copy-file.js +63 -0
- package/lib/fs/file-system-state-store.js +1 -1
- package/lib/fs/file.d.ts +31 -0
- package/lib/fs/file.js +30 -5
- package/lib/fs/index.d.ts +6 -0
- package/lib/fs/index.js +6 -0
- package/lib/fs/static-astro-file.d.ts +34 -0
- package/lib/fs/static-astro-file.js +39 -0
- package/lib/fs/static-css-file.d.ts +24 -0
- package/lib/fs/static-css-file.js +29 -0
- package/lib/fs/static-html-file.d.ts +31 -0
- package/lib/fs/static-html-file.js +36 -0
- package/lib/fs/static-json-file.d.ts +2 -2
- package/lib/fs/static-json-file.js +3 -2
- package/lib/fs/static-text-file.d.ts +1 -1
- package/lib/fs/static-text-file.js +3 -2
- package/lib/fs/static-typescript-file.d.ts +2 -2
- package/lib/fs/static-typescript-file.js +3 -2
- package/lib/fs/static-vue-file.d.ts +28 -0
- package/lib/fs/static-vue-file.js +33 -0
- package/lib/fs/static-yaml-file.d.ts +11 -8
- package/lib/fs/static-yaml-file.js +3 -2
- package/lib/internal/{providers.d.ts → docs/providers.d.ts} +14 -4
- package/lib/internal/docs/providers.js +196 -0
- package/lib/secret.d.ts +3 -0
- package/lib/secret.js +13 -0
- package/lib/{util/serde.d.ts → serde.d.ts} +1 -1
- package/lib/{util/serde.js → serde.js} +12 -4
- package/lib/test/bun.js +3 -2
- package/lib/util/sha256.d.ts +1 -0
- package/lib/util/sha256.js +4 -0
- package/lib/web/vitepress/config.d.ts +11 -0
- package/lib/web/vitepress/config.js +32 -0
- package/lib/web/vitepress/home-page.d.ts +5 -3
- package/lib/web/vitepress/home-page.js +48 -35
- package/lib/web/vitepress/index.d.ts +2 -0
- package/lib/web/vitepress/index.js +2 -0
- package/lib/web/vitepress/process-front-matter-files.d.ts +18 -0
- package/lib/web/vitepress/process-front-matter-files.js +68 -0
- package/lib/web/vitepress/vitepress.d.ts +5 -4
- package/lib/web/vitepress/vitepress.js +20 -36
- package/package.json +8 -4
- package/src/ai/astro-file.ts +11 -30
- package/src/ai/client.ts +59 -0
- package/src/ai/css-file.ts +11 -30
- package/src/ai/data.ts +50 -25
- package/src/ai/document.ts +171 -66
- package/src/ai/html-file.ts +11 -30
- package/src/ai/json-file.ts +13 -37
- package/src/ai/typescript-file.ts +14 -48
- package/src/ai/vue-file.ts +11 -30
- package/src/ai/yaml-file.ts +13 -53
- package/src/alchemy.ts +74 -9
- package/src/apply.ts +27 -26
- package/src/aws/account-id.ts +6 -2
- package/src/aws/credentials.ts +6 -0
- package/src/aws/oidc/oidc-provider.ts +17 -17
- package/src/aws/role.ts +68 -54
- package/src/cloudflare/account-api-token.ts +365 -0
- package/src/cloudflare/account-id.ts +0 -0
- package/src/cloudflare/api.ts +88 -17
- package/src/cloudflare/bucket.ts +493 -133
- package/src/cloudflare/custom-domain.ts +318 -0
- package/src/cloudflare/{dns.ts → dns-records.ts} +36 -22
- package/src/cloudflare/generate-asset-manifest.ts +4 -4
- package/src/cloudflare/index.ts +5 -1
- package/src/cloudflare/permission-groups.ts +137 -0
- package/src/cloudflare/r2-rest-state-store.ts +14 -13
- package/src/cloudflare/static-site-router.ts +18 -8
- package/src/cloudflare/static-site.ts +27 -35
- package/src/cloudflare/upload-asset-manifest.ts +6 -4
- package/src/cloudflare/worker.ts +44 -43
- package/src/dns/import-dns.ts +4 -4
- package/src/{util/encrypt.ts → encrypt.ts} +7 -10
- package/src/fs/copy-file.ts +115 -0
- package/src/fs/file-system-state-store.ts +5 -5
- package/src/fs/file.ts +64 -9
- package/src/fs/index.ts +6 -0
- package/src/fs/static-astro-file.ts +45 -0
- package/src/fs/static-css-file.ts +35 -0
- package/src/fs/static-html-file.ts +42 -0
- package/src/fs/static-json-file.ts +4 -3
- package/src/fs/static-text-file.ts +5 -2
- package/src/fs/static-typescript-file.ts +4 -3
- package/src/fs/static-vue-file.ts +39 -0
- package/src/fs/static-yaml-file.ts +13 -9
- package/src/internal/docs/providers.ts +281 -0
- package/src/secret.ts +16 -0
- package/src/{util/serde.ts → serde.ts} +16 -10
- package/src/test/bun.ts +8 -7
- package/src/util/sha256.ts +5 -0
- package/src/web/vitepress/config.ts +50 -0
- package/src/web/vitepress/home-page.ts +54 -42
- package/src/web/vitepress/index.ts +2 -0
- package/src/web/vitepress/process-front-matter-files.ts +98 -0
- package/src/web/vitepress/vitepress.ts +34 -50
- package/lib/internal/getting-started.d.ts +0 -10
- package/lib/internal/getting-started.js +0 -94
- package/lib/internal/project.d.ts +0 -13
- package/lib/internal/project.js +0 -101
- package/lib/internal/providers.js +0 -148
- package/src/internal/getting-started.ts +0 -107
- package/src/internal/project.ts +0 -119
- package/src/internal/providers.ts +0 -209
- /package/lib/cloudflare/{dns.d.ts → dns-records.d.ts} +0 -0
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
export * from "./account-api-token";
|
|
2
|
+
export * from "./api";
|
|
1
3
|
export * from "./bindings";
|
|
2
4
|
export * from "./bucket";
|
|
3
|
-
export * from "./
|
|
5
|
+
export * from "./custom-domain";
|
|
6
|
+
export * from "./dns-records";
|
|
4
7
|
export * from "./durable-object-namespace";
|
|
5
8
|
export * from "./kv-namespace";
|
|
9
|
+
export * from "./permission-groups";
|
|
6
10
|
export * from "./r2-rest-state-store";
|
|
7
11
|
export * from "./static-site";
|
|
8
12
|
export * from "./worker";
|
package/lib/cloudflare/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
export * from "./account-api-token";
|
|
2
|
+
export * from "./api";
|
|
1
3
|
export * from "./bindings";
|
|
2
4
|
export * from "./bucket";
|
|
3
|
-
export * from "./
|
|
5
|
+
export * from "./custom-domain";
|
|
6
|
+
export * from "./dns-records";
|
|
4
7
|
export * from "./durable-object-namespace";
|
|
5
8
|
export * from "./kv-namespace";
|
|
9
|
+
export * from "./permission-groups";
|
|
6
10
|
export * from "./r2-rest-state-store";
|
|
7
11
|
export * from "./static-site";
|
|
8
12
|
export * from "./worker";
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
import { type CloudflareApiOptions } from "./api";
|
|
4
|
+
/**
|
|
5
|
+
* Cloudflare permission group as returned by the API
|
|
6
|
+
*/
|
|
7
|
+
export interface PermissionGroup {
|
|
8
|
+
/**
|
|
9
|
+
* Unique identifier for the permission group
|
|
10
|
+
*/
|
|
11
|
+
id: string;
|
|
12
|
+
/**
|
|
13
|
+
* Human-readable name of the permission group
|
|
14
|
+
*/
|
|
15
|
+
name: string;
|
|
16
|
+
/**
|
|
17
|
+
* Scopes included in this permission group
|
|
18
|
+
*/
|
|
19
|
+
scopes: string[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* All Cloudflare permission groups mapped by name to ID
|
|
23
|
+
*
|
|
24
|
+
* @see https://developers.cloudflare.com/r2/api/tokens/#permissions
|
|
25
|
+
*/
|
|
26
|
+
export type PermissionGroups = Resource<"cloudflare::PermissionGroups"> & {
|
|
27
|
+
/**
|
|
28
|
+
* Admin Read & Write - Allows create, list, delete buckets and edit bucket configurations
|
|
29
|
+
* plus list, write, and read object access
|
|
30
|
+
*/
|
|
31
|
+
"Workers R2 Storage Write": PermissionGroup;
|
|
32
|
+
/**
|
|
33
|
+
* Admin Read only - Allows list buckets and view bucket configuration
|
|
34
|
+
* plus list and read object access
|
|
35
|
+
*/
|
|
36
|
+
"Workers R2 Storage Read": PermissionGroup;
|
|
37
|
+
/**
|
|
38
|
+
* Object Read & Write - Allows read, write, and list objects in specific buckets
|
|
39
|
+
*/
|
|
40
|
+
"Workers R2 Storage Bucket Item Write": PermissionGroup;
|
|
41
|
+
/**
|
|
42
|
+
* Object Read only - Allows read and list objects in specific buckets
|
|
43
|
+
*/
|
|
44
|
+
"Workers R2 Storage Bucket Item Read": PermissionGroup;
|
|
45
|
+
/**
|
|
46
|
+
* Dynamically discovered permission groups
|
|
47
|
+
*/
|
|
48
|
+
[name: string]: PermissionGroup;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Lists all permission groups available for the Cloudflare account
|
|
52
|
+
* and returns a typed map of permission names to their IDs.
|
|
53
|
+
*
|
|
54
|
+
* This is primarily used when creating API tokens for Cloudflare services like R2.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* // Get all permission groups including those for R2
|
|
58
|
+
* const permissions = await PermissionGroups("cloudflare-permissions");
|
|
59
|
+
*
|
|
60
|
+
* // Use with AccountApiToken to create a token with proper permissions
|
|
61
|
+
* const token = await AccountApiToken("r2-token", {
|
|
62
|
+
* name: "R2 Read-Only Token",
|
|
63
|
+
* policies: [
|
|
64
|
+
* {
|
|
65
|
+
* effect: "allow",
|
|
66
|
+
* resources: {
|
|
67
|
+
* "com.cloudflare.edge.r2.bucket.abc123_default_my-bucket": "*"
|
|
68
|
+
* },
|
|
69
|
+
* permissionGroups: [
|
|
70
|
+
* {
|
|
71
|
+
* id: permissions["Workers R2 Storage Bucket Item Read"]
|
|
72
|
+
* }
|
|
73
|
+
* ]
|
|
74
|
+
* }
|
|
75
|
+
* ]
|
|
76
|
+
* });
|
|
77
|
+
*/
|
|
78
|
+
export declare const PermissionGroups: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<PermissionGroups>, id: string, options?: CloudflareApiOptions) => Promise<PermissionGroups>);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Resource } from "../resource";
|
|
2
|
+
import { createCloudflareApi } from "./api";
|
|
3
|
+
/**
|
|
4
|
+
* Lists all permission groups available for the Cloudflare account
|
|
5
|
+
* and returns a typed map of permission names to their IDs.
|
|
6
|
+
*
|
|
7
|
+
* This is primarily used when creating API tokens for Cloudflare services like R2.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Get all permission groups including those for R2
|
|
11
|
+
* const permissions = await PermissionGroups("cloudflare-permissions");
|
|
12
|
+
*
|
|
13
|
+
* // Use with AccountApiToken to create a token with proper permissions
|
|
14
|
+
* const token = await AccountApiToken("r2-token", {
|
|
15
|
+
* name: "R2 Read-Only Token",
|
|
16
|
+
* policies: [
|
|
17
|
+
* {
|
|
18
|
+
* effect: "allow",
|
|
19
|
+
* resources: {
|
|
20
|
+
* "com.cloudflare.edge.r2.bucket.abc123_default_my-bucket": "*"
|
|
21
|
+
* },
|
|
22
|
+
* permissionGroups: [
|
|
23
|
+
* {
|
|
24
|
+
* id: permissions["Workers R2 Storage Bucket Item Read"]
|
|
25
|
+
* }
|
|
26
|
+
* ]
|
|
27
|
+
* }
|
|
28
|
+
* ]
|
|
29
|
+
* });
|
|
30
|
+
*/
|
|
31
|
+
export const PermissionGroups = Resource("cloudflare::PermissionGroups", async function (id, options) {
|
|
32
|
+
// Only create and update phases are supported
|
|
33
|
+
if (this.phase === "delete") {
|
|
34
|
+
return this.destroy();
|
|
35
|
+
}
|
|
36
|
+
// Initialize API client
|
|
37
|
+
const api = await createCloudflareApi(options);
|
|
38
|
+
// Fetch permission groups from Cloudflare API
|
|
39
|
+
const response = await api.get(`/accounts/${api.accountId}/tokens/permission_groups`);
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`Failed to fetch permission groups: ${response.statusText}`);
|
|
42
|
+
}
|
|
43
|
+
const data = (await response.json());
|
|
44
|
+
if (!data.success || !data.result) {
|
|
45
|
+
throw new Error(`API returned error: ${data.errors?.[0]?.message || "Unknown error"}`);
|
|
46
|
+
}
|
|
47
|
+
return this(Object.fromEntries(data.result.map((group) => [group.name, group])));
|
|
48
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Scope } from "../scope";
|
|
2
|
+
import type { Secret } from "../secret";
|
|
2
3
|
import type { State, StateStore } from "../state";
|
|
3
4
|
/**
|
|
4
5
|
* Options for CloudflareR2StateStore
|
|
@@ -17,7 +18,7 @@ export interface CloudflareR2StateStoreOptions {
|
|
|
17
18
|
/**
|
|
18
19
|
* API key to use (overrides CLOUDFLARE_API_KEY env var)
|
|
19
20
|
*/
|
|
20
|
-
apiKey?:
|
|
21
|
+
apiKey?: Secret;
|
|
21
22
|
/**
|
|
22
23
|
* Account ID to use (overrides CLOUDFLARE_ACCOUNT_ID env var)
|
|
23
24
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { deserialize, serialize } from "../serde";
|
|
1
2
|
import { withExponentialBackoff } from "../util/retry";
|
|
2
|
-
import { deserialize, serialize } from "../util/serde";
|
|
3
3
|
import { createCloudflareApi } from "./api";
|
|
4
4
|
/**
|
|
5
5
|
* State store implementation using Cloudflare R2 API
|
|
@@ -198,7 +198,8 @@ export class R2RestStateStore {
|
|
|
198
198
|
},
|
|
199
199
|
// Retry on transient errors
|
|
200
200
|
(error) => error.message?.includes("503") || error.message?.includes("timeout"), 5, // 5 retry attempts
|
|
201
|
-
1000
|
|
201
|
+
1000 // Start with 1 second delay
|
|
202
|
+
);
|
|
202
203
|
}
|
|
203
204
|
/**
|
|
204
205
|
* Delete a state by key
|
|
@@ -35,34 +35,44 @@ app.notFound(async (ctx) => {
|
|
|
35
35
|
}
|
|
36
36
|
// Fetch from KV
|
|
37
37
|
{
|
|
38
|
-
const object = await env.ASSETS.getWithMetadata(filePath
|
|
38
|
+
const object = await env.ASSETS.getWithMetadata(filePath, {
|
|
39
|
+
type: "arrayBuffer",
|
|
40
|
+
});
|
|
39
41
|
if (object.value) {
|
|
40
42
|
return await respond(200, filePath, object);
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
{
|
|
44
46
|
const guess = filePath + (filePath.endsWith("/") ? "" : "/") + "index.html";
|
|
45
|
-
const object = await env.ASSETS.getWithMetadata(guess
|
|
47
|
+
const object = await env.ASSETS.getWithMetadata(guess, {
|
|
48
|
+
type: "arrayBuffer",
|
|
49
|
+
});
|
|
46
50
|
if (object.value) {
|
|
47
51
|
return await respond(200, guess, object);
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
{
|
|
51
55
|
const guess = filePath + ".html";
|
|
52
|
-
const object = await env.ASSETS.getWithMetadata(guess
|
|
56
|
+
const object = await env.ASSETS.getWithMetadata(guess, {
|
|
57
|
+
type: "arrayBuffer",
|
|
58
|
+
});
|
|
53
59
|
if (object.value) {
|
|
54
60
|
return await respond(200, guess, object);
|
|
55
61
|
}
|
|
56
62
|
}
|
|
57
63
|
// Handle error page
|
|
58
64
|
if (env.ERROR_PAGE) {
|
|
59
|
-
const object = await env.ASSETS.getWithMetadata(env.ERROR_PAGE
|
|
65
|
+
const object = await env.ASSETS.getWithMetadata(env.ERROR_PAGE, {
|
|
66
|
+
type: "arrayBuffer",
|
|
67
|
+
});
|
|
60
68
|
if (object.value) {
|
|
61
69
|
return await respond(404, env.ERROR_PAGE, object);
|
|
62
70
|
}
|
|
63
71
|
}
|
|
64
72
|
else {
|
|
65
|
-
const object = await env.ASSETS.getWithMetadata(env.INDEX_PAGE
|
|
73
|
+
const object = await env.ASSETS.getWithMetadata(env.INDEX_PAGE, {
|
|
74
|
+
type: "arrayBuffer",
|
|
75
|
+
});
|
|
66
76
|
if (object.value) {
|
|
67
77
|
return await respond(200, env.INDEX_PAGE, object);
|
|
68
78
|
}
|
|
@@ -92,8 +102,7 @@ app.notFound(async (ctx) => {
|
|
|
92
102
|
headers.set("content-type", object.metadata.contentType);
|
|
93
103
|
headers.set("cache-control", object.metadata.cacheControl);
|
|
94
104
|
}
|
|
95
|
-
//
|
|
96
|
-
// const response = new Response(base64ToArrayBuffer(object.value), {
|
|
105
|
+
// Create response with the raw data directly
|
|
97
106
|
const response = new Response(object.value, {
|
|
98
107
|
status,
|
|
99
108
|
headers,
|
|
@@ -72,13 +72,6 @@ export interface StaticSiteProps {
|
|
|
72
72
|
* @default true
|
|
73
73
|
*/
|
|
74
74
|
production?: boolean;
|
|
75
|
-
/**
|
|
76
|
-
* Custom domain for the site
|
|
77
|
-
*/
|
|
78
|
-
domain?: string | {
|
|
79
|
-
name: string;
|
|
80
|
-
redirects?: string[];
|
|
81
|
-
};
|
|
82
75
|
build?: {
|
|
83
76
|
/**
|
|
84
77
|
* Command to run before deploying the site
|
|
@@ -137,13 +130,6 @@ export interface StaticSite extends Resource<"cloudflare::StaticSite"> {
|
|
|
137
130
|
* List of uploaded asset files
|
|
138
131
|
*/
|
|
139
132
|
assets: string[];
|
|
140
|
-
/**
|
|
141
|
-
* Custom domain for the site
|
|
142
|
-
*/
|
|
143
|
-
domain?: string | {
|
|
144
|
-
name: string;
|
|
145
|
-
redirects?: string[];
|
|
146
|
-
};
|
|
147
133
|
/**
|
|
148
134
|
* Whether the site is deployed to production
|
|
149
135
|
*/
|
|
@@ -198,13 +184,26 @@ export interface StaticSite extends Resource<"cloudflare::StaticSite"> {
|
|
|
198
184
|
* });
|
|
199
185
|
*
|
|
200
186
|
* @example
|
|
201
|
-
* // Create a static site with custom
|
|
202
|
-
* const
|
|
187
|
+
* // Create a static site with a custom domain using the CustomDomain resource
|
|
188
|
+
* const site = await StaticSite("custom-site", {
|
|
203
189
|
* name: "custom-site",
|
|
204
190
|
* dir: "./www",
|
|
205
191
|
* errorPage: "404.html",
|
|
206
|
-
* indexPage: "home.html"
|
|
207
|
-
*
|
|
192
|
+
* indexPage: "home.html"
|
|
193
|
+
* });
|
|
194
|
+
*
|
|
195
|
+
* // Then configure the custom domain separately
|
|
196
|
+
* const domain = await CustomDomain("custom-domain", {
|
|
197
|
+
* name: "www.example.com",
|
|
198
|
+
* zoneId: "abcdef123456789",
|
|
199
|
+
* workerName: site.name
|
|
200
|
+
* });
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* // Create a static site with multiple domains (primary + redirects)
|
|
204
|
+
* const site = await StaticSite("multi-domain-site", {
|
|
205
|
+
* name: "multi-domain-site",
|
|
206
|
+
* dir: "./public"
|
|
208
207
|
* });
|
|
209
208
|
*
|
|
210
209
|
* @see https://developers.cloudflare.com/workers/platform/sites
|
|
@@ -50,13 +50,26 @@ const __dirname = import.meta.dirname;
|
|
|
50
50
|
* });
|
|
51
51
|
*
|
|
52
52
|
* @example
|
|
53
|
-
* // Create a static site with custom
|
|
54
|
-
* const
|
|
53
|
+
* // Create a static site with a custom domain using the CustomDomain resource
|
|
54
|
+
* const site = await StaticSite("custom-site", {
|
|
55
55
|
* name: "custom-site",
|
|
56
56
|
* dir: "./www",
|
|
57
57
|
* errorPage: "404.html",
|
|
58
|
-
* indexPage: "home.html"
|
|
59
|
-
*
|
|
58
|
+
* indexPage: "home.html"
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* // Then configure the custom domain separately
|
|
62
|
+
* const domain = await CustomDomain("custom-domain", {
|
|
63
|
+
* name: "www.example.com",
|
|
64
|
+
* zoneId: "abcdef123456789",
|
|
65
|
+
* workerName: site.name
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* // Create a static site with multiple domains (primary + redirects)
|
|
70
|
+
* const site = await StaticSite("multi-domain-site", {
|
|
71
|
+
* name: "multi-domain-site",
|
|
72
|
+
* dir: "./public"
|
|
60
73
|
* });
|
|
61
74
|
*
|
|
62
75
|
* @see https://developers.cloudflare.com/workers/platform/sites
|
|
@@ -64,6 +77,8 @@ const __dirname = import.meta.dirname;
|
|
|
64
77
|
export const StaticSite = Resource("cloudflare::StaticSite", {
|
|
65
78
|
alwaysUpdate: true,
|
|
66
79
|
}, async function (id, props) {
|
|
80
|
+
// Create Cloudflare API client with automatic account discovery
|
|
81
|
+
const api = await createCloudflareApi();
|
|
67
82
|
if (this.phase === "delete") {
|
|
68
83
|
// For delete operations, we'll rely on the Worker delete to clean up
|
|
69
84
|
// Return empty output for deleted state
|
|
@@ -113,8 +128,6 @@ export const StaticSite = Resource("cloudflare::StaticSite", {
|
|
|
113
128
|
// Use the provided name
|
|
114
129
|
const siteName = props.name;
|
|
115
130
|
const indexPage = props.indexPage || "index.html";
|
|
116
|
-
// Create Cloudflare API client with automatic account discovery
|
|
117
|
-
const api = await createCloudflareApi();
|
|
118
131
|
// Step 1: Create or get the KV namespace for assets
|
|
119
132
|
const [kv, assetManifest] = await Promise.all([
|
|
120
133
|
KVNamespace("assets", {
|
|
@@ -193,7 +206,6 @@ export const StaticSite = Resource("cloudflare::StaticSite", {
|
|
|
193
206
|
assets: assetManifest.map((item) => item.key),
|
|
194
207
|
createdAt: this.output?.createdAt || now,
|
|
195
208
|
updatedAt: now,
|
|
196
|
-
domain: props.domain,
|
|
197
209
|
production: props.production !== false,
|
|
198
210
|
url: worker.url,
|
|
199
211
|
routes,
|
|
@@ -34,8 +34,10 @@ export async function uploadAssetManifest(api, namespaceId, manifest) {
|
|
|
34
34
|
// Create FormData for multipart upload
|
|
35
35
|
const formData = new FormData();
|
|
36
36
|
formData.append("metadata", JSON.stringify(metadata));
|
|
37
|
-
// Add file content as 'value'
|
|
38
|
-
const blob = new Blob([content]
|
|
37
|
+
// Add file content as 'value' - Fix for binary file corruption
|
|
38
|
+
const blob = new Blob([new Uint8Array(content)], {
|
|
39
|
+
type: item.contentType,
|
|
40
|
+
});
|
|
39
41
|
formData.append("value", blob);
|
|
40
42
|
try {
|
|
41
43
|
const response = await api.put(`/accounts/${api.accountId}/storage/kv/namespaces/${namespaceId}/values/${item.key}`, formData);
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @param key - The encryption key
|
|
6
6
|
* @returns The base64-encoded encrypted value with nonce
|
|
7
7
|
*/
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function encrypt(value: string, key: string): Promise<string>;
|
|
9
9
|
/**
|
|
10
10
|
* Decrypt a value encrypted with a symmetric key
|
|
11
11
|
*
|
|
@@ -6,7 +6,7 @@ import sodium from "libsodium-wrappers";
|
|
|
6
6
|
* @param key - The encryption key
|
|
7
7
|
* @returns The base64-encoded encrypted value with nonce
|
|
8
8
|
*/
|
|
9
|
-
export async function
|
|
9
|
+
export async function encrypt(value, key) {
|
|
10
10
|
// Initialize libsodium
|
|
11
11
|
await sodium.ready;
|
|
12
12
|
// Derive a key from the passphrase
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
/**
|
|
4
|
+
* Properties for creating a CopyFile resource
|
|
5
|
+
*/
|
|
6
|
+
export interface CopyFileProps {
|
|
7
|
+
/**
|
|
8
|
+
* Source path of the file to copy
|
|
9
|
+
*/
|
|
10
|
+
src: string;
|
|
11
|
+
/**
|
|
12
|
+
* Destination path where the file should be copied to
|
|
13
|
+
*/
|
|
14
|
+
dest: string;
|
|
15
|
+
/**
|
|
16
|
+
* Whether to overwrite the destination file if it already exists
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
overwrite?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Output returned after CopyFile creation/update
|
|
23
|
+
*/
|
|
24
|
+
export interface CopyFile extends Resource<"fs::CopyFile">, CopyFileProps {
|
|
25
|
+
/**
|
|
26
|
+
* Time at which the object was created
|
|
27
|
+
*/
|
|
28
|
+
createdAt: number;
|
|
29
|
+
/**
|
|
30
|
+
* Whether the file was successfully copied
|
|
31
|
+
*/
|
|
32
|
+
copied: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* CopyFile Resource
|
|
36
|
+
*
|
|
37
|
+
* Copies a file from a source location to a destination location.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* // Copy a file to a new location
|
|
41
|
+
* const copiedFile = await CopyFile("config-copy", {
|
|
42
|
+
* src: "config.json",
|
|
43
|
+
* dest: "backup/config.json"
|
|
44
|
+
* });
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Copy a file without overwriting if destination exists
|
|
48
|
+
* const safeCopy = await CopyFile("safe-copy", {
|
|
49
|
+
* src: "data.json",
|
|
50
|
+
* dest: "backup/data.json",
|
|
51
|
+
* overwrite: false
|
|
52
|
+
* });
|
|
53
|
+
*/
|
|
54
|
+
export declare const CopyFile: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<CopyFile>, id: string, props: CopyFileProps) => Promise<CopyFile>);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
import { ignore } from "../util/ignore";
|
|
4
|
+
/**
|
|
5
|
+
* CopyFile Resource
|
|
6
|
+
*
|
|
7
|
+
* Copies a file from a source location to a destination location.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Copy a file to a new location
|
|
11
|
+
* const copiedFile = await CopyFile("config-copy", {
|
|
12
|
+
* src: "config.json",
|
|
13
|
+
* dest: "backup/config.json"
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Copy a file without overwriting if destination exists
|
|
18
|
+
* const safeCopy = await CopyFile("safe-copy", {
|
|
19
|
+
* src: "data.json",
|
|
20
|
+
* dest: "backup/data.json",
|
|
21
|
+
* overwrite: false
|
|
22
|
+
* });
|
|
23
|
+
*/
|
|
24
|
+
export const CopyFile = Resource("fs::CopyFile", async function (id, props) {
|
|
25
|
+
const { src, dest, overwrite = true } = props;
|
|
26
|
+
if (this.phase === "delete") {
|
|
27
|
+
// When deleting, remove the destination file
|
|
28
|
+
await ignore("ENOENT", async () => fs.promises.unlink(dest));
|
|
29
|
+
return this.destroy();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
try {
|
|
33
|
+
// Check if source file exists
|
|
34
|
+
await fs.promises.access(src, fs.constants.F_OK);
|
|
35
|
+
// If this is an update and the destination has changed, delete the old file
|
|
36
|
+
if (this.phase === "update" &&
|
|
37
|
+
this.output?.dest &&
|
|
38
|
+
this.output.dest !== dest) {
|
|
39
|
+
await ignore("ENOENT", async () => fs.promises.unlink(this.output.dest));
|
|
40
|
+
}
|
|
41
|
+
// Check if destination file exists
|
|
42
|
+
const destinationExists = await fs.promises
|
|
43
|
+
.access(dest, fs.constants.F_OK)
|
|
44
|
+
.then(() => true)
|
|
45
|
+
.catch(() => false);
|
|
46
|
+
// Copy file if destination doesn't exist or overwrite is true
|
|
47
|
+
if (!destinationExists || overwrite) {
|
|
48
|
+
await fs.promises.copyFile(src, dest);
|
|
49
|
+
}
|
|
50
|
+
return this({
|
|
51
|
+
src,
|
|
52
|
+
dest,
|
|
53
|
+
overwrite,
|
|
54
|
+
copied: true,
|
|
55
|
+
createdAt: Date.now(),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error(`Error copying file from ${src} to ${dest}:`, error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { deserialize, serialize } from "../serde";
|
|
3
4
|
import { ignore } from "../util/ignore";
|
|
4
|
-
import { deserialize, serialize } from "../util/serde";
|
|
5
5
|
const stateRootDir = path.join(process.cwd(), ".alchemy");
|
|
6
6
|
export class FileSystemStateStore {
|
|
7
7
|
scope;
|
package/lib/fs/file.d.ts
CHANGED
|
@@ -45,6 +45,24 @@ declare module "../alchemy" {
|
|
|
45
45
|
*/
|
|
46
46
|
files(paths: string[]): Promise<FileCollection>;
|
|
47
47
|
files(path: string, ...paths: string[]): Promise<FileCollection>;
|
|
48
|
+
/**
|
|
49
|
+
* Gets all of the files in a directory.
|
|
50
|
+
* @param path Path to the directory
|
|
51
|
+
* @param props Optional properties
|
|
52
|
+
* @returns Promise resolving to a FileCollection
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* // Get all files in a directory
|
|
56
|
+
* const files = await alchemy.folder("./docs");
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
59
|
+
folder(path: string, props?: {
|
|
60
|
+
/**
|
|
61
|
+
* Whether to recursively get all files in the directory
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
recursive?: boolean;
|
|
65
|
+
}): Promise<FileCollection>;
|
|
48
66
|
}
|
|
49
67
|
}
|
|
50
68
|
/**
|
|
@@ -79,6 +97,19 @@ export interface File extends Resource<"fs::File"> {
|
|
|
79
97
|
* path: "logs/app.log",
|
|
80
98
|
* content: "application log entry"
|
|
81
99
|
* });
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* // Update file content and path
|
|
103
|
+
* let file = await File("config.json", {
|
|
104
|
+
* path: "config.json",
|
|
105
|
+
* content: '{ "version": "1.0.0" }'
|
|
106
|
+
* });
|
|
107
|
+
*
|
|
108
|
+
* // Later, update the path and content (old file will be removed)
|
|
109
|
+
* file = await File("config.json", {
|
|
110
|
+
* path: "config/config.json",
|
|
111
|
+
* content: '{ "version": "1.0.1" }'
|
|
112
|
+
* });
|
|
82
113
|
*/
|
|
83
114
|
export declare const File: (((this: any, id: string, props?: {}) => never) & (new (_: never) => never)) | ((this: Context<File>, id: string, props: {
|
|
84
115
|
path: string;
|
package/lib/fs/file.js
CHANGED
|
@@ -17,6 +17,12 @@ alchemy.files = async (...args) => {
|
|
|
17
17
|
]))),
|
|
18
18
|
};
|
|
19
19
|
};
|
|
20
|
+
alchemy.folder = async (dir, props) => {
|
|
21
|
+
const files = await fs.promises.readdir(dir, {
|
|
22
|
+
recursive: props?.recursive ?? false,
|
|
23
|
+
});
|
|
24
|
+
return alchemy.files(files.map((file) => path.join(dir, file)));
|
|
25
|
+
};
|
|
20
26
|
/**
|
|
21
27
|
* File Resource
|
|
22
28
|
*
|
|
@@ -36,6 +42,19 @@ alchemy.files = async (...args) => {
|
|
|
36
42
|
* path: "logs/app.log",
|
|
37
43
|
* content: "application log entry"
|
|
38
44
|
* });
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // Update file content and path
|
|
48
|
+
* let file = await File("config.json", {
|
|
49
|
+
* path: "config.json",
|
|
50
|
+
* content: '{ "version": "1.0.0" }'
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // Later, update the path and content (old file will be removed)
|
|
54
|
+
* file = await File("config.json", {
|
|
55
|
+
* path: "config/config.json",
|
|
56
|
+
* content: '{ "version": "1.0.1" }'
|
|
57
|
+
* });
|
|
39
58
|
*/
|
|
40
59
|
export const File = Resource("fs::File", async function (id, props) {
|
|
41
60
|
const filePath = props?.path ?? id;
|
|
@@ -43,12 +62,18 @@ export const File = Resource("fs::File", async function (id, props) {
|
|
|
43
62
|
await ignore("ENOENT", async () => fs.promises.unlink(filePath));
|
|
44
63
|
return this.destroy();
|
|
45
64
|
}
|
|
46
|
-
else
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
else if (this.phase === "update" &&
|
|
66
|
+
this.output &&
|
|
67
|
+
this.output.path !== filePath) {
|
|
68
|
+
// If path has changed, delete the old file
|
|
69
|
+
console.log(`File: Path changed from ${this.output.path} to ${filePath}, removing old file`);
|
|
70
|
+
await ignore("ENOENT", async () => fs.promises.unlink(this.output.path));
|
|
51
71
|
}
|
|
72
|
+
// Create directory and write file
|
|
73
|
+
await fs.promises.mkdir(path.dirname(filePath), {
|
|
74
|
+
recursive: true,
|
|
75
|
+
});
|
|
76
|
+
await fs.promises.writeFile(filePath, props.content);
|
|
52
77
|
return this({
|
|
53
78
|
path: filePath,
|
|
54
79
|
content: props.content,
|
package/lib/fs/index.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
export * from "./copy-file";
|
|
1
2
|
export * from "./file";
|
|
2
3
|
export * from "./file-collection";
|
|
3
4
|
export * from "./file-ref";
|
|
5
|
+
export * from "./file-system-state-store";
|
|
4
6
|
export * from "./folder";
|
|
7
|
+
export * from "./static-astro-file";
|
|
8
|
+
export * from "./static-css-file";
|
|
9
|
+
export * from "./static-html-file";
|
|
5
10
|
export * from "./static-json-file";
|
|
6
11
|
export * from "./static-text-file";
|
|
7
12
|
export * from "./static-typescript-file";
|
|
13
|
+
export * from "./static-vue-file";
|
|
8
14
|
export * from "./static-yaml-file";
|
package/lib/fs/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
export * from "./copy-file";
|
|
1
2
|
export * from "./file";
|
|
2
3
|
export * from "./file-collection";
|
|
3
4
|
export * from "./file-ref";
|
|
5
|
+
export * from "./file-system-state-store";
|
|
4
6
|
export * from "./folder";
|
|
7
|
+
export * from "./static-astro-file";
|
|
8
|
+
export * from "./static-css-file";
|
|
9
|
+
export * from "./static-html-file";
|
|
5
10
|
export * from "./static-json-file";
|
|
6
11
|
export * from "./static-text-file";
|
|
7
12
|
export * from "./static-typescript-file";
|
|
13
|
+
export * from "./static-vue-file";
|
|
8
14
|
export * from "./static-yaml-file";
|