alchemy 0.70.1 → 0.71.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.js +13 -15
- package/bin/commands/init.ts +0 -10
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +2 -0
- package/lib/apply.js.map +1 -1
- package/lib/clickhouse/organization.d.ts +1 -1
- package/lib/clickhouse/organization.js +2 -2
- package/lib/clickhouse/organization.js.map +1 -1
- package/lib/clickhouse/service.d.ts +28 -18
- package/lib/clickhouse/service.d.ts.map +1 -1
- package/lib/clickhouse/service.js +61 -9
- package/lib/clickhouse/service.js.map +1 -1
- package/lib/cloudflare/account-api-token.js +1 -1
- package/lib/cloudflare/account-api-token.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +12 -2
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bindings.js.map +1 -1
- package/lib/cloudflare/bound.d.ts +3 -2
- package/lib/cloudflare/bound.d.ts.map +1 -1
- package/lib/cloudflare/bucket.d.ts +6 -0
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +5 -3
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts +6 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.js +29 -9
- package/lib/cloudflare/bun-spa/bun-spa.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/d1-database.d.ts +1 -1
- package/lib/cloudflare/d1-database.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.js +1 -1
- package/lib/cloudflare/d1-database.js.map +1 -1
- package/lib/cloudflare/index.d.ts +2 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +2 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/logpush-job.d.ts +349 -0
- package/lib/cloudflare/logpush-job.d.ts.map +1 -0
- package/lib/cloudflare/logpush-job.js +277 -0
- package/lib/cloudflare/logpush-job.js.map +1 -0
- package/lib/cloudflare/miniflare/build-worker-options.d.ts +1 -0
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +16 -6
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.js +2 -0
- package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
- package/lib/cloudflare/permission-groups.d.ts +1489 -26
- package/lib/cloudflare/permission-groups.d.ts.map +1 -1
- package/lib/cloudflare/permission-groups.js +1787 -6
- package/lib/cloudflare/permission-groups.js.map +1 -1
- package/lib/cloudflare/queue-consumer.js +2 -2
- package/lib/cloudflare/queue-consumer.js.map +1 -1
- package/lib/cloudflare/worker-loader.d.ts +9 -0
- package/lib/cloudflare/worker-loader.d.ts.map +1 -0
- package/lib/cloudflare/worker-loader.js +6 -0
- package/lib/cloudflare/worker-loader.js.map +1 -0
- package/lib/cloudflare/worker-metadata.d.ts +14 -2
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +10 -1
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker-stub.d.ts +3 -1
- package/lib/cloudflare/worker-stub.d.ts.map +1 -1
- package/lib/cloudflare/worker-stub.js +19 -7
- package/lib/cloudflare/worker-stub.js.map +1 -1
- package/lib/cloudflare/worker-subdomain.js +1 -1
- package/lib/cloudflare/worker.d.ts +74 -10
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +23 -7
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +3 -0
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +9 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/github/comment.d.ts.map +1 -1
- package/lib/github/comment.js +14 -7
- package/lib/github/comment.js.map +1 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +6 -7
- package/lib/scope.js.map +1 -1
- package/lib/util/camel-to-snake.d.ts +12 -0
- package/lib/util/camel-to-snake.d.ts.map +1 -0
- package/lib/util/camel-to-snake.js +16 -0
- package/lib/util/camel-to-snake.js.map +1 -0
- package/lib/util/retry.d.ts +1 -1
- package/lib/util/retry.d.ts.map +1 -1
- package/lib/util/retry.js +2 -2
- package/lib/util/retry.js.map +1 -1
- package/lib/util/validate-resource-id.d.ts +2 -0
- package/lib/util/validate-resource-id.d.ts.map +1 -0
- package/lib/util/validate-resource-id.js +11 -0
- package/lib/util/validate-resource-id.js.map +1 -0
- package/package.json +2 -2
- package/src/apply.ts +2 -0
- package/src/clickhouse/organization.ts +2 -2
- package/src/clickhouse/service.ts +124 -33
- package/src/cloudflare/account-api-token.ts +1 -1
- package/src/cloudflare/bindings.ts +13 -0
- package/src/cloudflare/bound.ts +36 -32
- package/src/cloudflare/bucket.ts +13 -4
- package/src/cloudflare/bun-spa/bun-spa.ts +42 -11
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/d1-database.ts +2 -2
- package/src/cloudflare/index.ts +2 -0
- package/src/cloudflare/logpush-job.ts +768 -0
- package/src/cloudflare/miniflare/build-worker-options.ts +18 -6
- package/src/cloudflare/miniflare/remote-binding-proxy.ts +2 -0
- package/src/cloudflare/permission-groups.ts +1832 -45
- package/src/cloudflare/queue-consumer.ts +2 -2
- package/src/cloudflare/worker-loader.ts +12 -0
- package/src/cloudflare/worker-metadata.ts +25 -3
- package/src/cloudflare/worker-stub.ts +28 -11
- package/src/cloudflare/worker-subdomain.ts +1 -1
- package/src/cloudflare/worker.ts +106 -17
- package/src/cloudflare/wrangler.json.ts +15 -0
- package/src/github/comment.ts +17 -8
- package/src/scope.ts +7 -10
- package/src/util/camel-to-snake.ts +88 -0
- package/src/util/retry.ts +2 -1
- package/src/util/validate-resource-id.ts +10 -0
- package/workers/tunnel-proxy.js +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-resource-id.d.ts","sourceRoot":"","sources":["../../src/util/validate-resource-id.ts"],"names":[],"mappings":"AAAA,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,SAAa,GAAG,IAAI,CAS3E"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function validateResourceID(id, qualifier = "Resource") {
|
|
2
|
+
//todo(sam): during effect rewrite, this should be much more limiting
|
|
3
|
+
//todo: its hard to undo the flexibility we've offered to users now
|
|
4
|
+
if (!id) {
|
|
5
|
+
throw new Error(`${qualifier} ID cannot be an empty string: "${id}"`);
|
|
6
|
+
}
|
|
7
|
+
if (id.includes(":")) {
|
|
8
|
+
throw new Error(`${qualifier} ID cannot include colons: "${id}"`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=validate-resource-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-resource-id.js","sourceRoot":"","sources":["../../src/util/validate-resource-id.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAC,EAAU,EAAE,SAAS,GAAG,UAAU;IACnE,qEAAqE;IACrE,wEAAwE;IACxE,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,mCAAmC,EAAE,GAAG,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,+BAA+B,EAAE,GAAG,CAAC,CAAC;IACpE,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.71.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Sam Goodwin <sam@alchemy.run>",
|
|
6
6
|
"homepage": "https://alchemy.run",
|
|
@@ -161,6 +161,7 @@
|
|
|
161
161
|
"@aws-sdk/credential-providers": "^3.0.0",
|
|
162
162
|
"@cloudflare/unenv-preset": "^2.6.3",
|
|
163
163
|
"@iarna/toml": "^2.2.5",
|
|
164
|
+
"@octokit/rest": "^21.1.1",
|
|
164
165
|
"@smithy/node-config-provider": "^4.0.0",
|
|
165
166
|
"@smithy/types": "^4.6.0",
|
|
166
167
|
"aws4fetch": "^1.0.20",
|
|
@@ -199,7 +200,6 @@
|
|
|
199
200
|
"@cloudflare/vite-plugin": "^1.10.1",
|
|
200
201
|
"@cloudflare/workers-types": "^4.20250805.0",
|
|
201
202
|
"@libsql/client": "^0.15.12",
|
|
202
|
-
"@octokit/rest": "^21.1.1",
|
|
203
203
|
"@opennextjs/cloudflare": "^1.6.5",
|
|
204
204
|
"astro": "^5.13.2",
|
|
205
205
|
"drizzle-orm": "^0.44.2",
|
package/src/apply.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type { State } from "./state.ts";
|
|
|
20
20
|
import { formatFQN } from "./util/cli.ts";
|
|
21
21
|
import { logger } from "./util/logger.ts";
|
|
22
22
|
import { createAndSendEvent } from "./util/telemetry.ts";
|
|
23
|
+
import { validateResourceID } from "./util/validate-resource-id.ts";
|
|
23
24
|
|
|
24
25
|
export interface ApplyOptions {
|
|
25
26
|
quiet?: boolean;
|
|
@@ -56,6 +57,7 @@ async function _apply<Out extends ResourceAttributes>(
|
|
|
56
57
|
): Promise<Awaited<Out> & Resource> {
|
|
57
58
|
const scope = resource[ResourceScope];
|
|
58
59
|
const start = performance.now();
|
|
60
|
+
validateResourceID(resource[ResourceID], "Resource");
|
|
59
61
|
try {
|
|
60
62
|
const quiet = props?.quiet ?? scope.quiet;
|
|
61
63
|
await scope.init();
|
|
@@ -5,7 +5,7 @@ import { createClickhouseApi } from "./api.ts";
|
|
|
5
5
|
* Get an organization by name.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
8
|
-
* const organization = await
|
|
8
|
+
* const organization = await OrganizationRef("Alchemy's Organization");
|
|
9
9
|
*
|
|
10
10
|
* @param name The name of the organization to get.
|
|
11
11
|
* @param options The api credentials to use.
|
|
@@ -22,7 +22,7 @@ export async function OrganizationRef(
|
|
|
22
22
|
|
|
23
23
|
const organizations = await api.listAvailableOrganizations();
|
|
24
24
|
const organization = organizations.data.result?.find(
|
|
25
|
-
(organization) => organization.name === name,
|
|
25
|
+
(organization) => organization.id === name || organization.name === name,
|
|
26
26
|
);
|
|
27
27
|
if (!organization) {
|
|
28
28
|
throw new Error(`Organization ${name} not found`);
|
|
@@ -5,6 +5,26 @@ import { diff } from "../util/diff.ts";
|
|
|
5
5
|
import { createClickhouseApi } from "./api.ts";
|
|
6
6
|
import type { ClickhouseClient } from "./api/sdk.gen.ts";
|
|
7
7
|
import type { Service as ApiService, Organization } from "./api/types.gen.ts";
|
|
8
|
+
import { OrganizationRef } from "./organization.ts";
|
|
9
|
+
|
|
10
|
+
type MysqlEndpoint = {
|
|
11
|
+
protocol: "mysql";
|
|
12
|
+
host: string;
|
|
13
|
+
port: number;
|
|
14
|
+
username: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type HttpsEndpoint = {
|
|
18
|
+
protocol: "https";
|
|
19
|
+
host: string;
|
|
20
|
+
port: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type NativesecureEndpoint = {
|
|
24
|
+
protocol: "nativesecure";
|
|
25
|
+
host: string;
|
|
26
|
+
port: number;
|
|
27
|
+
};
|
|
8
28
|
|
|
9
29
|
export interface ServiceProps {
|
|
10
30
|
/**
|
|
@@ -18,7 +38,7 @@ export interface ServiceProps {
|
|
|
18
38
|
secret?: string | Secret<string>;
|
|
19
39
|
|
|
20
40
|
/**
|
|
21
|
-
* The id of Clickhouse cloud organization to create the service in.
|
|
41
|
+
* The id, name, or OrganizationRef of Clickhouse cloud organization to create the service in.
|
|
22
42
|
*/
|
|
23
43
|
organization: string | Organization;
|
|
24
44
|
|
|
@@ -128,6 +148,13 @@ export interface ServiceProps {
|
|
|
128
148
|
*/
|
|
129
149
|
complianceType?: ApiService["complianceType"];
|
|
130
150
|
|
|
151
|
+
/**
|
|
152
|
+
* wait for http service to be ready before marking the resource as created
|
|
153
|
+
*
|
|
154
|
+
* @default true
|
|
155
|
+
*/
|
|
156
|
+
waitForHttpEndpointReady?: boolean;
|
|
157
|
+
|
|
131
158
|
//todo(michael): I need to understand more about what these properties do before documenting
|
|
132
159
|
//todo(michael): support linking to BYOC infrastructure directly
|
|
133
160
|
byocId?: ApiService["byocId"];
|
|
@@ -269,30 +296,17 @@ export interface Service {
|
|
|
269
296
|
/**
|
|
270
297
|
* The mysql endpoint details of the Clickhouse service.
|
|
271
298
|
*/
|
|
272
|
-
mysqlEndpoint?:
|
|
273
|
-
protocol: "mysql";
|
|
274
|
-
host: string;
|
|
275
|
-
port: number;
|
|
276
|
-
username: string;
|
|
277
|
-
};
|
|
299
|
+
mysqlEndpoint?: MysqlEndpoint;
|
|
278
300
|
|
|
279
301
|
/**
|
|
280
302
|
* The https endpoint details of the Clickhouse service.
|
|
281
303
|
*/
|
|
282
|
-
httpsEndpoint?:
|
|
283
|
-
protocol: "https";
|
|
284
|
-
host: string;
|
|
285
|
-
port: number;
|
|
286
|
-
};
|
|
304
|
+
httpsEndpoint?: HttpsEndpoint;
|
|
287
305
|
|
|
288
306
|
/**
|
|
289
307
|
* The nativesecure endpoint details of the Clickhouse service.
|
|
290
308
|
*/
|
|
291
|
-
nativesecureEndpoint?:
|
|
292
|
-
protocol: "nativesecure";
|
|
293
|
-
host: string;
|
|
294
|
-
port: number;
|
|
295
|
-
};
|
|
309
|
+
nativesecureEndpoint?: NativesecureEndpoint;
|
|
296
310
|
|
|
297
311
|
/**
|
|
298
312
|
* The desired state of the Clickhouse service.
|
|
@@ -310,7 +324,7 @@ export interface Service {
|
|
|
310
324
|
*
|
|
311
325
|
* @example
|
|
312
326
|
* // Create a basic Clickhouse service on aws
|
|
313
|
-
* const organization = await
|
|
327
|
+
* const organization = await OrganizationRef("Alchemy's Organization");
|
|
314
328
|
* const service = await Service("clickhouse", {
|
|
315
329
|
* organization,
|
|
316
330
|
* provider: "aws",
|
|
@@ -348,6 +362,7 @@ export const Service = Resource(
|
|
|
348
362
|
if (enableMysqlEndpoint) {
|
|
349
363
|
endpoints.push({ protocol: "mysql", enabled: true });
|
|
350
364
|
}
|
|
365
|
+
const waitForHttpEndpointReady = props.waitForHttpEndpointReady ?? true;
|
|
351
366
|
//todo(michael): comment these in when disabling is supported
|
|
352
367
|
// const enableHttpsEndpoint = props.enableHttpsEndpoint ?? true;
|
|
353
368
|
// if (enableHttpsEndpoint) {
|
|
@@ -374,7 +389,9 @@ export const Service = Resource(
|
|
|
374
389
|
|
|
375
390
|
const organizationId =
|
|
376
391
|
typeof props.organization === "string"
|
|
377
|
-
? props.organization
|
|
392
|
+
? await OrganizationRef(props.organization)
|
|
393
|
+
.then((organization) => organization.id!)
|
|
394
|
+
.catch(() => props.organization as string)
|
|
378
395
|
: props.organization.id!;
|
|
379
396
|
|
|
380
397
|
if (this.phase === "delete") {
|
|
@@ -393,7 +410,6 @@ export const Service = Resource(
|
|
|
393
410
|
organizationId,
|
|
394
411
|
this.output.clickhouseId,
|
|
395
412
|
(state) => state === "stopped",
|
|
396
|
-
10 * 60,
|
|
397
413
|
);
|
|
398
414
|
|
|
399
415
|
await api.deleteService({
|
|
@@ -403,6 +419,12 @@ export const Service = Resource(
|
|
|
403
419
|
},
|
|
404
420
|
});
|
|
405
421
|
|
|
422
|
+
await waitForServiceDeletion(
|
|
423
|
+
api,
|
|
424
|
+
organizationId,
|
|
425
|
+
this.output.clickhouseId,
|
|
426
|
+
);
|
|
427
|
+
|
|
406
428
|
return this.destroy();
|
|
407
429
|
}
|
|
408
430
|
if (this.phase === "update") {
|
|
@@ -481,13 +503,13 @@ export const Service = Resource(
|
|
|
481
503
|
updates.releaseChannel = response.releaseChannel!;
|
|
482
504
|
updates.mysqlEndpoint = response!.endpoints!.find(
|
|
483
505
|
(endpoint) => endpoint.protocol === "mysql",
|
|
484
|
-
) as
|
|
506
|
+
) as MysqlEndpoint;
|
|
485
507
|
updates.httpsEndpoint = response!.endpoints!.find(
|
|
486
508
|
(endpoint) => endpoint.protocol === "https",
|
|
487
|
-
) as
|
|
509
|
+
) as HttpsEndpoint;
|
|
488
510
|
updates.nativesecureEndpoint = response!.endpoints!.find(
|
|
489
511
|
(endpoint) => endpoint.protocol === "nativesecure",
|
|
490
|
-
) as
|
|
512
|
+
) as NativesecureEndpoint;
|
|
491
513
|
}
|
|
492
514
|
|
|
493
515
|
if (stateTarget !== this.output.stateTarget) {
|
|
@@ -579,14 +601,24 @@ export const Service = Resource(
|
|
|
579
601
|
const password = response.password!;
|
|
580
602
|
const service = response.service!;
|
|
581
603
|
|
|
604
|
+
const httpEndpoint = service.endpoints!.find(
|
|
605
|
+
(endpoint) => endpoint.protocol === "https",
|
|
606
|
+
) as HttpsEndpoint;
|
|
607
|
+
|
|
582
608
|
await waitForServiceState(
|
|
583
609
|
api,
|
|
584
610
|
organizationId,
|
|
585
611
|
response.service!.id!,
|
|
586
612
|
(state) => state === "running" || state === "idle",
|
|
587
|
-
10 * 60,
|
|
588
613
|
);
|
|
589
614
|
|
|
615
|
+
if (waitForHttpEndpointReady && httpEndpoint) {
|
|
616
|
+
await waitForHttpServiceReady(httpEndpoint as HttpsEndpoint, {
|
|
617
|
+
password: password!,
|
|
618
|
+
username: "default",
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
590
622
|
return {
|
|
591
623
|
organizationId: organizationId,
|
|
592
624
|
name: service.name!,
|
|
@@ -614,13 +646,11 @@ export const Service = Resource(
|
|
|
614
646
|
state: service.state,
|
|
615
647
|
mysqlEndpoint: service.endpoints!.find(
|
|
616
648
|
(endpoint) => endpoint.protocol === "mysql",
|
|
617
|
-
) as
|
|
618
|
-
httpsEndpoint:
|
|
619
|
-
(endpoint) => endpoint.protocol === "https",
|
|
620
|
-
) as any,
|
|
649
|
+
) as MysqlEndpoint,
|
|
650
|
+
httpsEndpoint: httpEndpoint,
|
|
621
651
|
nativesecureEndpoint: service.endpoints!.find(
|
|
622
652
|
(endpoint) => endpoint.protocol === "nativesecure",
|
|
623
|
-
) as
|
|
653
|
+
) as NativesecureEndpoint,
|
|
624
654
|
};
|
|
625
655
|
},
|
|
626
656
|
);
|
|
@@ -630,9 +660,8 @@ async function waitForServiceState(
|
|
|
630
660
|
organizationId: string,
|
|
631
661
|
serviceId: string,
|
|
632
662
|
stateChecker: (state: string) => boolean,
|
|
633
|
-
maxWaitSeconds: number,
|
|
634
663
|
) {
|
|
635
|
-
|
|
664
|
+
async function checkState(): Promise<void> {
|
|
636
665
|
const service = await api.getServiceDetails({
|
|
637
666
|
path: {
|
|
638
667
|
organizationId: organizationId,
|
|
@@ -646,8 +675,70 @@ async function waitForServiceState(
|
|
|
646
675
|
}
|
|
647
676
|
|
|
648
677
|
throw new Error(`Service ${serviceId} is in state ${serviceState}`);
|
|
649
|
-
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
await waitFor(checkState, 10 * 60);
|
|
681
|
+
}
|
|
650
682
|
|
|
683
|
+
async function waitForHttpServiceReady(
|
|
684
|
+
endpoint: {
|
|
685
|
+
protocol: "https";
|
|
686
|
+
host: string;
|
|
687
|
+
port: number;
|
|
688
|
+
},
|
|
689
|
+
credentials: { password: string; username: string },
|
|
690
|
+
) {
|
|
691
|
+
async function checkHttpEndpoint() {
|
|
692
|
+
await fetch(
|
|
693
|
+
`https://${endpoint.host}:${endpoint.port}/?query=SELECT%20count%28%29%20FROM%20system.databases%20WHERE%20name%20%3D%20%27default%27`,
|
|
694
|
+
{
|
|
695
|
+
headers: {
|
|
696
|
+
Authorization: `Basic ${btoa(`${credentials.username}:${credentials.password}`)}`,
|
|
697
|
+
},
|
|
698
|
+
},
|
|
699
|
+
)
|
|
700
|
+
.then((res) => res.text())
|
|
701
|
+
.then((text) => {
|
|
702
|
+
if (text === "0") {
|
|
703
|
+
throw new Error("Service is not ready");
|
|
704
|
+
}
|
|
705
|
+
return;
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
await waitFor(checkHttpEndpoint, 10 * 60);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
async function waitForServiceDeletion(
|
|
713
|
+
api: ClickhouseClient,
|
|
714
|
+
organizationId: string,
|
|
715
|
+
serviceId: string,
|
|
716
|
+
) {
|
|
717
|
+
async function checkDeletion() {
|
|
718
|
+
const status = await api
|
|
719
|
+
.getServiceDetails({
|
|
720
|
+
path: {
|
|
721
|
+
organizationId: organizationId,
|
|
722
|
+
serviceId: serviceId,
|
|
723
|
+
},
|
|
724
|
+
})
|
|
725
|
+
.then((service) => service.response.status)
|
|
726
|
+
.catch((error) => {
|
|
727
|
+
return (error.status as number) ?? 500;
|
|
728
|
+
});
|
|
729
|
+
if (status === 404) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
throw new Error(`Service ${serviceId} is not deleted`);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
await waitFor(checkDeletion, 10 * 60);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async function waitFor(
|
|
739
|
+
checkFunction: () => Promise<void>,
|
|
740
|
+
maxWaitSeconds: number,
|
|
741
|
+
) {
|
|
651
742
|
if (maxWaitSeconds < 5) {
|
|
652
743
|
maxWaitSeconds = 5;
|
|
653
744
|
}
|
|
@@ -656,7 +747,7 @@ async function waitForServiceState(
|
|
|
656
747
|
|
|
657
748
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
658
749
|
try {
|
|
659
|
-
await
|
|
750
|
+
await checkFunction();
|
|
660
751
|
return;
|
|
661
752
|
} catch (error) {
|
|
662
753
|
if (attempt === maxRetries - 1) {
|
|
@@ -282,7 +282,7 @@ export const AccountApiToken = Resource(
|
|
|
282
282
|
return this.destroy();
|
|
283
283
|
}
|
|
284
284
|
|
|
285
|
-
const permissionGroups = await PermissionGroups(
|
|
285
|
+
const permissionGroups = await PermissionGroups();
|
|
286
286
|
|
|
287
287
|
// Transform our properties to API format
|
|
288
288
|
const apiPayload = {
|
|
@@ -29,6 +29,7 @@ import type { VersionMetadata } from "./version-metadata.ts";
|
|
|
29
29
|
import type { WorkerRef } from "./worker-ref.ts";
|
|
30
30
|
import type { WorkerStub } from "./worker-stub.ts";
|
|
31
31
|
import type { Worker } from "./worker.ts";
|
|
32
|
+
import type { WorkerLoader } from "./worker-loader.ts";
|
|
32
33
|
import type { Workflow } from "./workflow.ts";
|
|
33
34
|
|
|
34
35
|
export type Bindings = {
|
|
@@ -74,6 +75,7 @@ export type Binding =
|
|
|
74
75
|
| WorkerStub
|
|
75
76
|
| WorkerRef
|
|
76
77
|
| WorkerEntrypoint
|
|
78
|
+
| WorkerLoader
|
|
77
79
|
| Workflow
|
|
78
80
|
| BrowserRendering
|
|
79
81
|
| VersionMetadata
|
|
@@ -138,6 +140,7 @@ export type WorkerBindingSpec =
|
|
|
138
140
|
| WorkerBindingVectorize
|
|
139
141
|
| WorkerBindingVersionMetadata
|
|
140
142
|
| WorkerBindingWasmModule
|
|
143
|
+
| WorkerBindingWorkerLoader
|
|
141
144
|
| WorkerBindingWorkflow;
|
|
142
145
|
|
|
143
146
|
/**
|
|
@@ -515,3 +518,13 @@ export interface WorkerBindingPipeline {
|
|
|
515
518
|
/** Pipeline name */
|
|
516
519
|
pipeline: string;
|
|
517
520
|
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Worker Loader binding type
|
|
524
|
+
*/
|
|
525
|
+
export interface WorkerBindingWorkerLoader {
|
|
526
|
+
/** The name of the binding */
|
|
527
|
+
name: string;
|
|
528
|
+
/** Type identifier for Worker Loader binding */
|
|
529
|
+
type: "worker_loader";
|
|
530
|
+
}
|
package/src/cloudflare/bound.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
D1Database,
|
|
5
5
|
DispatchNamespace,
|
|
6
6
|
DurableObjectNamespace,
|
|
7
|
+
WorkerLoader,
|
|
7
8
|
Fetcher,
|
|
8
9
|
Hyperdrive,
|
|
9
10
|
ImagesBinding,
|
|
@@ -43,6 +44,7 @@ import type { VersionMetadata as _VersionMetadata } from "./version-metadata.ts"
|
|
|
43
44
|
import type { WorkerRef } from "./worker-ref.ts";
|
|
44
45
|
import type { WorkerStub } from "./worker-stub.ts";
|
|
45
46
|
import type { Worker as _Worker } from "./worker.ts";
|
|
47
|
+
import type { WorkerLoader as _WorkerLoader } from "./worker-loader.ts";
|
|
46
48
|
import type { Workflow as _Workflow } from "./workflow.ts";
|
|
47
49
|
|
|
48
50
|
type BoundWorker<
|
|
@@ -86,35 +88,37 @@ export type Bound<T extends Binding> = T extends _DurableObjectNamespace<
|
|
|
86
88
|
? D1Database
|
|
87
89
|
: T extends DispatchNamespace
|
|
88
90
|
? DispatchNamespace
|
|
89
|
-
: T extends
|
|
90
|
-
?
|
|
91
|
-
: T extends
|
|
92
|
-
?
|
|
93
|
-
: T extends
|
|
94
|
-
?
|
|
95
|
-
: T extends
|
|
96
|
-
?
|
|
97
|
-
: T extends
|
|
98
|
-
?
|
|
99
|
-
: T extends
|
|
100
|
-
?
|
|
101
|
-
: T extends
|
|
102
|
-
?
|
|
103
|
-
: T extends
|
|
104
|
-
?
|
|
105
|
-
: T extends
|
|
106
|
-
?
|
|
107
|
-
: T extends
|
|
108
|
-
?
|
|
109
|
-
: T extends
|
|
110
|
-
?
|
|
111
|
-
: T extends
|
|
112
|
-
?
|
|
113
|
-
: T extends
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
91
|
+
: T extends _WorkerLoader
|
|
92
|
+
? WorkerLoader
|
|
93
|
+
: T extends _VectorizeIndex
|
|
94
|
+
? VectorizeIndex
|
|
95
|
+
: T extends _Queue<infer Body>
|
|
96
|
+
? Queue<Body>
|
|
97
|
+
: T extends _AnalyticsEngineDataset
|
|
98
|
+
? AnalyticsEngineDataset
|
|
99
|
+
: T extends _Pipeline<infer R>
|
|
100
|
+
? Pipeline<R>
|
|
101
|
+
: T extends _RateLimit
|
|
102
|
+
? RateLimit
|
|
103
|
+
: T extends string
|
|
104
|
+
? T
|
|
105
|
+
: T extends BrowserRendering
|
|
106
|
+
? Fetcher
|
|
107
|
+
: T extends _Ai<infer M>
|
|
108
|
+
? Ai<M>
|
|
109
|
+
: T extends _Images
|
|
110
|
+
? ImagesBinding
|
|
111
|
+
: T extends _VersionMetadata
|
|
112
|
+
? WorkerVersionMetadata
|
|
113
|
+
: T extends Self
|
|
114
|
+
? Service
|
|
115
|
+
: T extends Json<infer T>
|
|
116
|
+
? T
|
|
117
|
+
: T extends _Container<
|
|
118
|
+
infer Obj
|
|
119
|
+
>
|
|
120
|
+
? DurableObjectNamespace<
|
|
121
|
+
Obj &
|
|
122
|
+
Rpc.DurableObjectBranded
|
|
123
|
+
>
|
|
124
|
+
: Service;
|
package/src/cloudflare/bucket.ts
CHANGED
|
@@ -347,6 +347,13 @@ type _R2Bucket = Omit<BucketProps, "delete" | "dev"> & {
|
|
|
347
347
|
* Whether the bucket is running remotely
|
|
348
348
|
*/
|
|
349
349
|
remote: boolean;
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Whether the bucket has been deployed
|
|
353
|
+
*
|
|
354
|
+
* @internal
|
|
355
|
+
*/
|
|
356
|
+
isDeployed?: boolean;
|
|
350
357
|
};
|
|
351
358
|
|
|
352
359
|
/**
|
|
@@ -587,15 +594,17 @@ const _R2Bucket = Resource(
|
|
|
587
594
|
}
|
|
588
595
|
|
|
589
596
|
const allowPublicAccess = props.allowPublicAccess === true;
|
|
597
|
+
const isLocal = this.scope.local && !props.dev?.remote;
|
|
590
598
|
const dev = {
|
|
591
599
|
id: this.output?.dev?.id ?? bucketName,
|
|
592
600
|
remote: props.dev?.remote ?? false,
|
|
593
|
-
|
|
601
|
+
isDeployed: this.output?.dev?.isDeployed || !isLocal,
|
|
602
|
+
} satisfies _R2Bucket["dev"];
|
|
594
603
|
const adopt = props.adopt ?? this.scope.adopt;
|
|
595
604
|
|
|
596
|
-
if (
|
|
605
|
+
if (isLocal) {
|
|
597
606
|
return {
|
|
598
|
-
name:
|
|
607
|
+
name: bucketName,
|
|
599
608
|
location: this.output?.location ?? "",
|
|
600
609
|
creationDate: this.output?.creationDate ?? new Date(),
|
|
601
610
|
jurisdiction: this.output?.jurisdiction ?? "default",
|
|
@@ -626,7 +635,7 @@ const _R2Bucket = Resource(
|
|
|
626
635
|
return this.destroy();
|
|
627
636
|
}
|
|
628
637
|
|
|
629
|
-
if (this.phase === "create" || !this.output?.
|
|
638
|
+
if (this.phase === "create" || !this.output?.dev?.isDeployed) {
|
|
630
639
|
const bucket = await createBucket(api, bucketName, props).catch(
|
|
631
640
|
async (err) => {
|
|
632
641
|
if (
|
|
@@ -2,7 +2,6 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { Scope } from "../../scope.ts";
|
|
4
4
|
import { exists } from "../../util/exists.ts";
|
|
5
|
-
import type { Assets } from "../assets.ts";
|
|
6
5
|
import type { Bindings } from "../bindings.ts";
|
|
7
6
|
import {
|
|
8
7
|
extractStringAndSecretBindings,
|
|
@@ -16,7 +15,12 @@ import {
|
|
|
16
15
|
} from "../website.ts";
|
|
17
16
|
|
|
18
17
|
export interface BunSPAProps<B extends Bindings> extends WebsiteProps<B> {
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* The path to the frontend entrypoints that bun should bundle for deployment & serve in dev mode.
|
|
20
|
+
* These are usually html files. Glob patterns are supported.
|
|
21
|
+
* Typically set to index.html
|
|
22
|
+
*/
|
|
23
|
+
frontend: string | string[];
|
|
20
24
|
outDir?: string;
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -26,15 +30,39 @@ export async function BunSPA<B extends Bindings>(
|
|
|
26
30
|
id: string,
|
|
27
31
|
props: BunSPAProps<B>,
|
|
28
32
|
): Promise<BunSPA<B> & { apiUrl: string }> {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const frontendPaths = Array.isArray(props.frontend)
|
|
34
|
+
? props.frontend.map((p) => path.resolve(p))
|
|
35
|
+
: [path.resolve(props.frontend)];
|
|
36
|
+
|
|
37
|
+
// Helper to check if a path contains glob patterns
|
|
38
|
+
const isGlobPattern = (p: string) =>
|
|
39
|
+
p.includes("*") || p.includes("?") || p.includes("[") || p.includes("]");
|
|
40
|
+
|
|
41
|
+
// Only validate non-glob paths
|
|
42
|
+
const nonGlobPaths = frontendPaths.filter((p) => !isGlobPattern(p));
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
if (nonGlobPaths.length > 0) {
|
|
45
|
+
const existsPromises = nonGlobPaths.map((p) => exists(p));
|
|
46
|
+
const existsResults = await Promise.all(existsPromises);
|
|
47
|
+
const missingPaths = nonGlobPaths.filter((_p, i) => !existsResults[i]);
|
|
48
|
+
if (missingPaths.length > 0) {
|
|
49
|
+
if (missingPaths.length === 1) {
|
|
50
|
+
throw new Error(`Frontend path ${missingPaths[0]} does not exist`);
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`Frontend paths ${missingPaths.join(", ")} do not exist`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const statsPromises = nonGlobPaths.map((p) => fs.stat(p));
|
|
56
|
+
const statsResults = await Promise.all(statsPromises);
|
|
57
|
+
const notFiles = nonGlobPaths.filter((_, i) => !statsResults[i].isFile());
|
|
58
|
+
if (notFiles.length > 0) {
|
|
59
|
+
if (notFiles.length === 1) {
|
|
60
|
+
throw new Error(`Frontend path ${notFiles[0]} is not a file`);
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Frontend paths ${notFiles.join(", ")} are not files`);
|
|
63
|
+
}
|
|
37
64
|
}
|
|
65
|
+
|
|
38
66
|
const outDir = path.resolve(props.outDir ?? "dist/client");
|
|
39
67
|
|
|
40
68
|
if (props.assets) {
|
|
@@ -59,7 +87,7 @@ export async function BunSPA<B extends Bindings>(
|
|
|
59
87
|
},
|
|
60
88
|
build: spreadBuildProps(
|
|
61
89
|
props,
|
|
62
|
-
`bun build '${
|
|
90
|
+
`bun build '${frontendPaths.join("' '")}' --outdir ${outDir}`,
|
|
63
91
|
),
|
|
64
92
|
});
|
|
65
93
|
|
|
@@ -68,9 +96,12 @@ export async function BunSPA<B extends Bindings>(
|
|
|
68
96
|
if (scope.local) {
|
|
69
97
|
const cwd = props.cwd ?? process.cwd();
|
|
70
98
|
await validateBunfigToml(cwd);
|
|
99
|
+
const frontendPathsRelativeToCwd = frontendPaths.map((p) =>
|
|
100
|
+
path.relative(cwd, p),
|
|
101
|
+
);
|
|
71
102
|
const dev = spreadDevProps(
|
|
72
103
|
props,
|
|
73
|
-
`bun '${
|
|
104
|
+
`bun '${frontendPathsRelativeToCwd.join("' '")}'`,
|
|
74
105
|
);
|
|
75
106
|
const secrets = props.wrangler?.secrets ?? !props.wrangler?.path;
|
|
76
107
|
const env = {
|
|
@@ -113,7 +113,7 @@ export interface D1DatabaseProps extends CloudflareApiOptions {
|
|
|
113
113
|
* Set when `Scope.local` is true to force update to the database even if it was already deployed live.
|
|
114
114
|
* @internal
|
|
115
115
|
*/
|
|
116
|
-
force?: boolean;
|
|
116
|
+
force?: boolean | number;
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -250,7 +250,7 @@ export async function D1Database(
|
|
|
250
250
|
...(props.dev ?? {}),
|
|
251
251
|
// force local migrations to run even if the database was already deployed live
|
|
252
252
|
// this property will oscillate from true to false depending on the dev vs live deployment
|
|
253
|
-
force: Scope.current.local,
|
|
253
|
+
force: Scope.current.local ? Date.now() : false,
|
|
254
254
|
},
|
|
255
255
|
});
|
|
256
256
|
}
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ export * from "./hyperdrive-ref.ts";
|
|
|
36
36
|
export * from "./hyperdrive.ts";
|
|
37
37
|
export * from "./images.ts";
|
|
38
38
|
export * from "./kv-namespace.ts";
|
|
39
|
+
export * from "./logpush-job.ts";
|
|
39
40
|
export * from "./next.ts";
|
|
40
41
|
export * from "./nuxt/nuxt.ts";
|
|
41
42
|
export * from "./orange.ts";
|
|
@@ -69,5 +70,6 @@ export * from "./worker-stub.ts";
|
|
|
69
70
|
export * from "./worker-subdomain.ts";
|
|
70
71
|
export * from "./worker.ts";
|
|
71
72
|
export { Workflow } from "./workflow.ts";
|
|
73
|
+
export { WorkerLoader } from "./worker-loader.ts";
|
|
72
74
|
export * from "./wrangler.json.ts";
|
|
73
75
|
export * from "./zone.ts";
|