alchemy 0.1.13 → 0.1.14
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 +12 -2
- package/lib/aws/account-id.d.ts +5 -0
- package/lib/aws/account-id.d.ts.map +1 -0
- package/lib/aws/account-id.js +10 -0
- package/lib/aws/account-id.js.map +1 -0
- package/lib/aws/index.d.ts +1 -0
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +1 -0
- package/lib/aws/index.js.map +1 -1
- package/lib/aws/oidc/github-oidc-provider.d.ts +11 -0
- package/lib/aws/oidc/github-oidc-provider.d.ts.map +1 -0
- package/lib/aws/oidc/github-oidc-provider.js +22 -0
- package/lib/aws/oidc/github-oidc-provider.js.map +1 -0
- package/lib/aws/oidc/index.d.ts +3 -0
- package/lib/aws/oidc/index.d.ts.map +1 -0
- package/lib/aws/oidc/index.js +3 -0
- package/lib/aws/oidc/index.js.map +1 -0
- package/lib/aws/oidc/oidc-provider.d.ts +60 -0
- package/lib/aws/oidc/oidc-provider.d.ts.map +1 -0
- package/lib/aws/oidc/oidc-provider.js +140 -0
- package/lib/aws/oidc/oidc-provider.js.map +1 -0
- package/lib/aws/queue.d.ts +24 -0
- package/lib/aws/queue.d.ts.map +1 -1
- package/lib/aws/queue.js +6 -1
- package/lib/aws/queue.js.map +1 -1
- package/lib/aws/role.d.ts.map +1 -1
- package/lib/aws/role.js +49 -1
- package/lib/aws/role.js.map +1 -1
- package/lib/aws/ses.d.ts.map +1 -1
- package/lib/aws/ses.js +20 -28
- package/lib/aws/ses.js.map +1 -1
- package/lib/aws/table.d.ts.map +1 -1
- package/lib/aws/table.js +95 -73
- package/lib/aws/table.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +7 -2
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/durable-object-namespace.d.ts +2 -5
- package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -1
- package/lib/cloudflare/durable-object-namespace.js +1 -6
- package/lib/cloudflare/durable-object-namespace.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +3 -0
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js +4 -0
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/static-site.d.ts.map +1 -1
- package/lib/cloudflare/static-site.js +12 -30
- package/lib/cloudflare/static-site.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +6 -3
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +41 -15
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/github/client.d.ts +34 -0
- package/lib/github/client.d.ts.map +1 -0
- package/lib/github/client.js +91 -0
- package/lib/github/client.js.map +1 -0
- package/lib/github/index.d.ts +2 -0
- package/lib/github/index.d.ts.map +1 -0
- package/lib/github/index.js +2 -0
- package/lib/github/index.js.map +1 -0
- package/lib/github/secret.d.ts +46 -0
- package/lib/github/secret.d.ts.map +1 -0
- package/lib/github/secret.js +103 -0
- package/lib/github/secret.js.map +1 -0
- package/lib/utils/retry.d.ts +13 -0
- package/lib/utils/retry.d.ts.map +1 -0
- package/lib/utils/retry.js +31 -0
- package/lib/utils/retry.js.map +1 -0
- package/package.json +10 -2
- package/src/aws/account-id.ts +11 -0
- package/src/aws/index.ts +1 -0
- package/src/aws/oidc/github-oidc-provider.ts +34 -0
- package/src/aws/oidc/index.ts +2 -0
- package/src/aws/oidc/oidc-provider.ts +253 -0
- package/src/aws/queue.ts +23 -1
- package/src/aws/role.ts +69 -0
- package/src/aws/ses.ts +27 -31
- package/src/aws/table.ts +113 -82
- package/src/cloudflare/bindings.ts +8 -2
- package/src/cloudflare/durable-object-namespace.ts +4 -10
- package/src/cloudflare/kv-namespace.ts +11 -0
- package/src/cloudflare/static-site.ts +13 -32
- package/src/cloudflare/worker.ts +52 -22
- package/src/github/client.ts +115 -0
- package/src/github/index.ts +1 -0
- package/src/github/secret.ts +172 -0
- package/src/utils/retry.ts +36 -0
package/src/aws/table.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "@aws-sdk/client-dynamodb";
|
|
10
10
|
import { ignore } from "../error";
|
|
11
11
|
import { type Context, Resource } from "../resource";
|
|
12
|
+
import { withExponentialBackoff } from "../utils/retry";
|
|
12
13
|
|
|
13
14
|
export interface TableProps {
|
|
14
15
|
tableName: string;
|
|
@@ -38,7 +39,7 @@ export class Table extends Resource(
|
|
|
38
39
|
async (
|
|
39
40
|
ctx: Context<TableOutput>,
|
|
40
41
|
props: TableProps,
|
|
41
|
-
): Promise<TableOutput> => {
|
|
42
|
+
): Promise<TableOutput | void> => {
|
|
42
43
|
const client = new DynamoDBClient({});
|
|
43
44
|
|
|
44
45
|
if (ctx.event === "delete") {
|
|
@@ -70,103 +71,133 @@ export class Table extends Resource(
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
|
|
73
|
-
return
|
|
74
|
-
...props,
|
|
75
|
-
id: props.tableName,
|
|
76
|
-
arn: `arn:aws:dynamodb:${client.config.region}:${process.env.AWS_ACCOUNT_ID}:table/${props.tableName}`,
|
|
77
|
-
tableId: "",
|
|
78
|
-
};
|
|
74
|
+
return;
|
|
79
75
|
} else {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const keySchema: KeySchemaElement[] = [
|
|
89
|
-
{
|
|
90
|
-
AttributeName: props.partitionKey.name,
|
|
91
|
-
KeyType: "HASH",
|
|
92
|
-
},
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
if (props.sortKey) {
|
|
96
|
-
attributeDefinitions.push({
|
|
97
|
-
AttributeName: props.sortKey.name,
|
|
98
|
-
AttributeType: props.sortKey.type,
|
|
99
|
-
});
|
|
100
|
-
keySchema.push({
|
|
101
|
-
AttributeName: props.sortKey.name,
|
|
102
|
-
KeyType: "RANGE",
|
|
103
|
-
});
|
|
104
|
-
}
|
|
76
|
+
// Setup for table creation
|
|
77
|
+
const attributeDefinitions = [
|
|
78
|
+
{
|
|
79
|
+
AttributeName: props.partitionKey.name,
|
|
80
|
+
AttributeType: props.partitionKey.type,
|
|
81
|
+
},
|
|
82
|
+
];
|
|
105
83
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
)
|
|
84
|
+
const keySchema: KeySchemaElement[] = [
|
|
85
|
+
{
|
|
86
|
+
AttributeName: props.partitionKey.name,
|
|
87
|
+
KeyType: "HASH",
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
if (props.sortKey) {
|
|
92
|
+
attributeDefinitions.push({
|
|
93
|
+
AttributeName: props.sortKey.name,
|
|
94
|
+
AttributeType: props.sortKey.type,
|
|
95
|
+
});
|
|
96
|
+
keySchema.push({
|
|
97
|
+
AttributeName: props.sortKey.name,
|
|
98
|
+
KeyType: "RANGE",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Attempt to create the table with exponential backoff for ResourceInUseException
|
|
103
|
+
await withExponentialBackoff(
|
|
104
|
+
async () => {
|
|
105
|
+
try {
|
|
106
|
+
// First check if table already exists
|
|
107
|
+
const describeResponse = await client.send(
|
|
108
|
+
new DescribeTableCommand({
|
|
109
|
+
TableName: props.tableName,
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// If table exists and is ACTIVE, no need to create it
|
|
114
|
+
if (describeResponse.Table?.TableStatus === "ACTIVE") {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
127
117
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
118
|
+
// If table exists but not ACTIVE, wait for it in the polling loop below
|
|
119
|
+
if (describeResponse.Table) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error instanceof ResourceNotFoundException) {
|
|
124
|
+
// Table doesn't exist, try to create it
|
|
125
|
+
await client.send(
|
|
126
|
+
new CreateTableCommand({
|
|
127
|
+
TableName: props.tableName,
|
|
128
|
+
AttributeDefinitions: attributeDefinitions,
|
|
129
|
+
KeySchema: keySchema,
|
|
130
|
+
BillingMode: props.billingMode || "PAY_PER_REQUEST",
|
|
131
|
+
ProvisionedThroughput:
|
|
132
|
+
props.billingMode === "PROVISIONED"
|
|
133
|
+
? {
|
|
134
|
+
ReadCapacityUnits: props.readCapacity || 5,
|
|
135
|
+
WriteCapacityUnits: props.writeCapacity || 5,
|
|
136
|
+
}
|
|
137
|
+
: undefined,
|
|
138
|
+
Tags: props.tags
|
|
139
|
+
? Object.entries(props.tags).map(([Key, Value]) => ({
|
|
140
|
+
Key,
|
|
141
|
+
Value,
|
|
142
|
+
}))
|
|
143
|
+
: undefined,
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
(error) => error instanceof ResourceInUseException,
|
|
152
|
+
10, // Max attempts
|
|
153
|
+
200, // Initial delay in ms
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// Wait for table to be active with timeout
|
|
157
|
+
let tableActive = false;
|
|
158
|
+
let tableDescription;
|
|
159
|
+
let retryCount = 0;
|
|
160
|
+
const maxRetries = 60; // Wait up to 60 seconds
|
|
161
|
+
|
|
162
|
+
while (!tableActive && retryCount < maxRetries) {
|
|
163
|
+
try {
|
|
132
164
|
const response = await client.send(
|
|
133
165
|
new DescribeTableCommand({
|
|
134
166
|
TableName: props.tableName,
|
|
135
167
|
}),
|
|
136
168
|
);
|
|
169
|
+
|
|
137
170
|
tableActive = response.Table?.TableStatus === "ACTIVE";
|
|
138
171
|
if (tableActive) {
|
|
139
172
|
tableDescription = response.Table;
|
|
140
173
|
} else {
|
|
141
|
-
|
|
174
|
+
retryCount++;
|
|
175
|
+
// Increasing delay for each retry with some jitter
|
|
176
|
+
const delay = Math.min(1000 * (1 + 0.1 * Math.random()), 5000);
|
|
177
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
142
178
|
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
retryCount++;
|
|
181
|
+
if (!(error instanceof ResourceNotFoundException)) {
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
143
185
|
}
|
|
186
|
+
}
|
|
144
187
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
streamArn: tableDescription!.LatestStreamArn,
|
|
150
|
-
tableId: tableDescription!.TableId!,
|
|
151
|
-
};
|
|
152
|
-
} catch (error) {
|
|
153
|
-
if (error instanceof ResourceInUseException) {
|
|
154
|
-
// Table already exists, get its details
|
|
155
|
-
const response = await client.send(
|
|
156
|
-
new DescribeTableCommand({
|
|
157
|
-
TableName: props.tableName,
|
|
158
|
-
}),
|
|
159
|
-
);
|
|
160
|
-
return {
|
|
161
|
-
...props,
|
|
162
|
-
id: props.tableName,
|
|
163
|
-
arn: response.Table!.TableArn!,
|
|
164
|
-
streamArn: response.Table!.LatestStreamArn,
|
|
165
|
-
tableId: response.Table!.TableId!,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
throw error;
|
|
188
|
+
if (!tableActive) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`Timed out waiting for table ${props.tableName} to become active`,
|
|
191
|
+
);
|
|
169
192
|
}
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
...props,
|
|
196
|
+
id: props.tableName,
|
|
197
|
+
arn: tableDescription!.TableArn!,
|
|
198
|
+
streamArn: tableDescription!.LatestStreamArn,
|
|
199
|
+
tableId: tableDescription!.TableId!,
|
|
200
|
+
};
|
|
170
201
|
}
|
|
171
202
|
},
|
|
172
203
|
) {}
|
|
@@ -4,11 +4,17 @@
|
|
|
4
4
|
* https://developers.cloudflare.com/api/resources/workers/subresources/scripts/methods/update/
|
|
5
5
|
*/
|
|
6
6
|
import type { DurableObjectNamespace } from "./durable-object-namespace";
|
|
7
|
+
import type { KVNamespace } from "./kv-namespace";
|
|
8
|
+
import type { Worker } from "./worker";
|
|
9
|
+
|
|
10
|
+
export type Bindings = {
|
|
11
|
+
[bindingName: string]: Binding;
|
|
12
|
+
};
|
|
7
13
|
|
|
8
14
|
/**
|
|
9
|
-
*
|
|
15
|
+
* L2 Binding Resources.
|
|
10
16
|
*/
|
|
11
|
-
export type
|
|
17
|
+
export type Binding = DurableObjectNamespace | KVNamespace | Worker | string;
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
20
|
* Union type for all Worker binding types (API spec)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Resolved } from "../output";
|
|
2
2
|
|
|
3
3
|
export interface DurableObjectNamespaceInput {
|
|
4
|
-
bindingName: string;
|
|
5
4
|
className: string;
|
|
6
5
|
scriptName?: string | undefined;
|
|
7
6
|
environment?: string | undefined;
|
|
@@ -10,19 +9,16 @@ export interface DurableObjectNamespaceInput {
|
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
export function isDurableObjectNamespace(
|
|
13
|
-
binding:
|
|
14
|
-
): binding is DurableObjectNamespace {
|
|
12
|
+
binding: any,
|
|
13
|
+
): binding is Resolved<DurableObjectNamespace> {
|
|
15
14
|
return (
|
|
16
|
-
binding.type === "durable_object_namespace"
|
|
17
|
-
typeof (binding as any).bindingName === "string"
|
|
15
|
+
typeof binding === "object" && binding.type === "durable_object_namespace"
|
|
18
16
|
);
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
export class DurableObjectNamespace implements DurableObjectNamespaceInput {
|
|
22
20
|
public readonly type = "durable_object_namespace" as const;
|
|
23
21
|
// alias for bindingName to be consistent with other bindings
|
|
24
|
-
public readonly name: string;
|
|
25
|
-
public readonly bindingName: string;
|
|
26
22
|
public readonly className: string;
|
|
27
23
|
public readonly scriptName?: string | undefined;
|
|
28
24
|
public readonly environment?: string | undefined;
|
|
@@ -33,8 +29,6 @@ export class DurableObjectNamespace implements DurableObjectNamespaceInput {
|
|
|
33
29
|
public readonly id: string,
|
|
34
30
|
input: DurableObjectNamespaceInput,
|
|
35
31
|
) {
|
|
36
|
-
this.bindingName = input.bindingName;
|
|
37
|
-
this.name = this.bindingName;
|
|
38
32
|
this.className = input.className;
|
|
39
33
|
this.scriptName = input.scriptName;
|
|
40
34
|
this.environment = input.environment;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Resolved } from "../output";
|
|
1
2
|
import { type Context, Resource } from "../resource";
|
|
2
3
|
import { createCloudflareApi } from "./api";
|
|
3
4
|
|
|
@@ -51,6 +52,7 @@ export interface KVPair {
|
|
|
51
52
|
* Output returned after KV Namespace creation/update
|
|
52
53
|
*/
|
|
53
54
|
export interface KVNamespaceOutput extends KVNamespaceProps {
|
|
55
|
+
type: "kv_namespace";
|
|
54
56
|
/**
|
|
55
57
|
* The ID of the namespace
|
|
56
58
|
*/
|
|
@@ -67,6 +69,14 @@ export interface KVNamespaceOutput extends KVNamespaceProps {
|
|
|
67
69
|
modifiedAt: number;
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
export function isKVNamespace(
|
|
73
|
+
resource: any,
|
|
74
|
+
): resource is Resolved<KVNamespace> {
|
|
75
|
+
return (
|
|
76
|
+
resource && typeof resource === "object" && resource.type === "kv_namespace"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
70
80
|
export class KVNamespace extends Resource(
|
|
71
81
|
"cloudflare::KVNamespace",
|
|
72
82
|
async (ctx: Context<KVNamespaceOutput>, props: KVNamespaceProps) => {
|
|
@@ -182,6 +192,7 @@ export class KVNamespace extends Resource(
|
|
|
182
192
|
|
|
183
193
|
// Construct the output
|
|
184
194
|
const output: KVNamespaceOutput = {
|
|
195
|
+
type: "kv_namespace",
|
|
185
196
|
id: namespaceId,
|
|
186
197
|
title: props.title,
|
|
187
198
|
values: props.values,
|
|
@@ -7,7 +7,7 @@ import type { BundleProps } from "../esbuild";
|
|
|
7
7
|
import type { Input } from "../input";
|
|
8
8
|
import { type Context, Resource } from "../resource";
|
|
9
9
|
import { createCloudflareApi } from "./api";
|
|
10
|
-
import type {
|
|
10
|
+
import type { Bindings } from "./bindings";
|
|
11
11
|
import { generateAssetManifest } from "./generate-asset-manifest";
|
|
12
12
|
import { KVNamespace } from "./kv-namespace";
|
|
13
13
|
import { uploadAssetManifest } from "./upload-asset-manifest";
|
|
@@ -276,12 +276,12 @@ export class StaticSite extends Resource(
|
|
|
276
276
|
const api = await createCloudflareApi();
|
|
277
277
|
|
|
278
278
|
// Step 1: Create or get the KV namespace for assets
|
|
279
|
-
const
|
|
279
|
+
const kv = new KVNamespace("assets", {
|
|
280
280
|
title: `${siteName}-assets`,
|
|
281
281
|
});
|
|
282
282
|
|
|
283
283
|
const [{ id: kvNamespaceId }, assetManifest] = await Promise.all([
|
|
284
|
-
apply(
|
|
284
|
+
apply(kv, {
|
|
285
285
|
quiet: ctx.quiet,
|
|
286
286
|
}),
|
|
287
287
|
generateAssetManifest(props.dir),
|
|
@@ -291,39 +291,20 @@ export class StaticSite extends Resource(
|
|
|
291
291
|
await uploadAssetManifest(api, kvNamespaceId, assetManifest);
|
|
292
292
|
|
|
293
293
|
// Prepare the bindings for the worker
|
|
294
|
-
const bindings: Input<
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
{
|
|
301
|
-
name: "INDEX_PAGE",
|
|
302
|
-
type: "plain_text",
|
|
303
|
-
text: indexPage,
|
|
304
|
-
},
|
|
305
|
-
];
|
|
306
|
-
|
|
307
|
-
// Add error page binding if specified
|
|
308
|
-
if (props.errorPage) {
|
|
309
|
-
bindings.push({
|
|
310
|
-
name: "ERROR_PAGE",
|
|
311
|
-
type: "plain_text",
|
|
312
|
-
text: props.errorPage,
|
|
313
|
-
});
|
|
314
|
-
}
|
|
294
|
+
const bindings: Input<Bindings> = {
|
|
295
|
+
ASSETS: kv,
|
|
296
|
+
INDEX_PAGE: indexPage,
|
|
297
|
+
// Add error page binding if specified
|
|
298
|
+
...(props.errorPage ? { ERROR_PAGE: props.errorPage } : {}),
|
|
299
|
+
};
|
|
315
300
|
|
|
316
301
|
const routes: Record<string, string> = {};
|
|
317
302
|
// Create backend worker if configured
|
|
318
303
|
if (props.routes) {
|
|
319
|
-
for (const [path,
|
|
304
|
+
for (const [path, worker] of Object.entries(props.routes)) {
|
|
320
305
|
// @ts-ignore - TODO: need to use Resolved<Worker> to get the string ...
|
|
321
|
-
routes[path] =
|
|
322
|
-
bindings.
|
|
323
|
-
type: "service",
|
|
324
|
-
name: `ROUTE_${entrypoint.id}`,
|
|
325
|
-
service: entrypoint.id,
|
|
326
|
-
});
|
|
306
|
+
routes[path] = worker.id;
|
|
307
|
+
bindings[`ROUTE_${worker.id}`] = worker;
|
|
327
308
|
}
|
|
328
309
|
}
|
|
329
310
|
|
|
@@ -366,7 +347,7 @@ export class StaticSite extends Resource(
|
|
|
366
347
|
},
|
|
367
348
|
},
|
|
368
349
|
// place a dependency on the namespace
|
|
369
|
-
|
|
350
|
+
kv.id,
|
|
370
351
|
);
|
|
371
352
|
|
|
372
353
|
// Get current timestamp
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import * as fs from "fs/promises";
|
|
2
2
|
import { apply } from "../apply";
|
|
3
3
|
import { Bundle, type BundleProps } from "../esbuild";
|
|
4
|
+
import type { Resolved } from "../output";
|
|
4
5
|
import { type Context, Resource } from "../resource";
|
|
5
6
|
import { type CloudflareApi, createCloudflareApi } from "./api";
|
|
6
|
-
import type {
|
|
7
|
+
import type { Bindings, WorkerBindingSpec } from "./bindings";
|
|
7
8
|
import type { DurableObjectNamespace } from "./durable-object-namespace";
|
|
8
9
|
import { isDurableObjectNamespace } from "./durable-object-namespace";
|
|
10
|
+
import { isKVNamespace } from "./kv-namespace";
|
|
9
11
|
import type { WorkerScriptMetadata } from "./worker-metadata";
|
|
10
12
|
import type { SingleStepMigration } from "./worker-migration";
|
|
11
13
|
/**
|
|
@@ -54,7 +56,7 @@ export interface WorkerProps {
|
|
|
54
56
|
/**
|
|
55
57
|
* Bindings to attach to the worker
|
|
56
58
|
*/
|
|
57
|
-
bindings?:
|
|
59
|
+
bindings?: Bindings;
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
62
|
* Environment variables to attach to the worker
|
|
@@ -94,6 +96,8 @@ export interface WorkerProps {
|
|
|
94
96
|
* Output returned after Worker creation/update
|
|
95
97
|
*/
|
|
96
98
|
export interface WorkerOutput extends Omit<WorkerProps, "url"> {
|
|
99
|
+
type: "service";
|
|
100
|
+
|
|
97
101
|
/**
|
|
98
102
|
* The ID of the worker
|
|
99
103
|
*/
|
|
@@ -118,7 +122,11 @@ export interface WorkerOutput extends Omit<WorkerProps, "url"> {
|
|
|
118
122
|
/**
|
|
119
123
|
* The bindings that were created
|
|
120
124
|
*/
|
|
121
|
-
bindings:
|
|
125
|
+
bindings: Bindings;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function isWorker(resource: any): resource is Resolved<Worker> {
|
|
129
|
+
return resource && typeof resource === "object" && resource.type === "worker";
|
|
122
130
|
}
|
|
123
131
|
|
|
124
132
|
export class Worker extends Resource(
|
|
@@ -144,7 +152,7 @@ export class Worker extends Resource(
|
|
|
144
152
|
await assertWorkerDoesNotExist(ctx, api, workerName);
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
const oldBindings = await ctx.get<
|
|
155
|
+
const oldBindings = await ctx.get<Bindings>("bindings");
|
|
148
156
|
|
|
149
157
|
const scriptMetadata = await prepareWorkerMetadata(ctx, oldBindings, props);
|
|
150
158
|
|
|
@@ -174,12 +182,13 @@ export class Worker extends Resource(
|
|
|
174
182
|
|
|
175
183
|
// Construct the output
|
|
176
184
|
const output: WorkerOutput = {
|
|
185
|
+
type: "service",
|
|
177
186
|
id: workerName,
|
|
178
187
|
name: workerName,
|
|
179
188
|
script: scriptContent,
|
|
180
189
|
format: props.format || "esm", // Include format in the output
|
|
181
190
|
routes: props.routes || [],
|
|
182
|
-
bindings: props.bindings
|
|
191
|
+
bindings: props.bindings ?? {},
|
|
183
192
|
env: props.env,
|
|
184
193
|
observability: scriptMetadata.observability,
|
|
185
194
|
createdAt: now,
|
|
@@ -283,7 +292,7 @@ async function putWorker(
|
|
|
283
292
|
scriptName,
|
|
284
293
|
);
|
|
285
294
|
|
|
286
|
-
console.log("Script Metadata:", JSON.stringify(scriptMetadata, null, 2));
|
|
295
|
+
// console.log("Script Metadata:", JSON.stringify(scriptMetadata, null, 2));
|
|
287
296
|
|
|
288
297
|
// Add metadata as JSON
|
|
289
298
|
formData.append(
|
|
@@ -318,7 +327,7 @@ async function putWorker(
|
|
|
318
327
|
}
|
|
319
328
|
|
|
320
329
|
interface WorkerMetadata {
|
|
321
|
-
bindings:
|
|
330
|
+
bindings: WorkerBindingSpec[];
|
|
322
331
|
observability: {
|
|
323
332
|
enabled: boolean;
|
|
324
333
|
};
|
|
@@ -330,12 +339,12 @@ interface WorkerMetadata {
|
|
|
330
339
|
|
|
331
340
|
async function prepareWorkerMetadata(
|
|
332
341
|
ctx: Context<WorkerOutput>,
|
|
333
|
-
oldBindings:
|
|
342
|
+
oldBindings: Bindings | undefined,
|
|
334
343
|
props: WorkerProps,
|
|
335
344
|
): Promise<WorkerMetadata> {
|
|
336
345
|
// Prepare metadata with bindings
|
|
337
346
|
const meta: WorkerMetadata = {
|
|
338
|
-
bindings: []
|
|
347
|
+
bindings: [],
|
|
339
348
|
observability: {
|
|
340
349
|
enabled: props.observability?.enabled !== false,
|
|
341
350
|
},
|
|
@@ -349,30 +358,51 @@ async function prepareWorkerMetadata(
|
|
|
349
358
|
},
|
|
350
359
|
};
|
|
351
360
|
|
|
361
|
+
const bindings = (props.bindings ?? {}) as Resolved<Bindings>;
|
|
362
|
+
|
|
352
363
|
// Convert bindings to the format expected by the API
|
|
353
|
-
for (const binding of
|
|
364
|
+
for (const [bindingName, binding] of Object.entries(bindings)) {
|
|
354
365
|
// Create a copy of the binding to avoid modifying the original
|
|
355
|
-
const bindingData = { ...binding };
|
|
356
366
|
|
|
357
|
-
if (
|
|
358
|
-
|
|
359
|
-
|
|
367
|
+
if (isKVNamespace(binding)) {
|
|
368
|
+
meta.bindings.push({
|
|
369
|
+
type: "kv_namespace",
|
|
370
|
+
name: bindingName,
|
|
371
|
+
namespace_id: binding.id,
|
|
372
|
+
});
|
|
373
|
+
} else if (typeof binding === "string") {
|
|
374
|
+
meta.bindings.push({
|
|
375
|
+
type: "plain_text",
|
|
376
|
+
name: bindingName,
|
|
377
|
+
text: binding,
|
|
378
|
+
});
|
|
379
|
+
} else if (binding.type === "service") {
|
|
380
|
+
meta.bindings.push({
|
|
381
|
+
type: "service",
|
|
382
|
+
name: bindingName,
|
|
383
|
+
service: binding.id,
|
|
384
|
+
});
|
|
385
|
+
} else if (isDurableObjectNamespace(binding)) {
|
|
386
|
+
const stableId = binding.id;
|
|
387
|
+
const className = binding.className;
|
|
360
388
|
|
|
361
389
|
meta.bindings.push({
|
|
362
390
|
type: "durable_object_namespace",
|
|
363
|
-
name:
|
|
391
|
+
name: bindingName,
|
|
364
392
|
class_name: className,
|
|
365
|
-
script_name:
|
|
366
|
-
environment:
|
|
367
|
-
namespace_id:
|
|
393
|
+
script_name: binding.scriptName,
|
|
394
|
+
environment: binding.environment,
|
|
395
|
+
namespace_id: binding.namespaceId,
|
|
368
396
|
});
|
|
369
397
|
|
|
370
|
-
const oldBinding: DurableObjectNamespace | undefined =
|
|
398
|
+
const oldBinding: DurableObjectNamespace | undefined = Object.values(
|
|
399
|
+
oldBindings ?? {},
|
|
400
|
+
)
|
|
371
401
|
?.filter(isDurableObjectNamespace)
|
|
372
402
|
?.find((b) => b.id === stableId);
|
|
373
403
|
|
|
374
404
|
if (!oldBinding) {
|
|
375
|
-
if (
|
|
405
|
+
if (binding.sqlite) {
|
|
376
406
|
meta.migrations!.new_sqlite_classes!.push(className);
|
|
377
407
|
} else {
|
|
378
408
|
meta.migrations!.new_classes!.push(className);
|
|
@@ -384,8 +414,8 @@ async function prepareWorkerMetadata(
|
|
|
384
414
|
});
|
|
385
415
|
}
|
|
386
416
|
} else {
|
|
387
|
-
//
|
|
388
|
-
|
|
417
|
+
// @ts-expect-error - we should never reach here
|
|
418
|
+
throw new Error(`Unsupported binding type: ${binding.type}`);
|
|
389
419
|
}
|
|
390
420
|
}
|
|
391
421
|
|