alchemy 0.70.0 → 0.70.2
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 +97 -60
- package/bin/alchemy.ts +2 -0
- package/bin/commands/telemetry.ts +33 -0
- package/bin/services/execute-alchemy.ts +7 -0
- package/lib/alchemy.d.ts +4 -0
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +1 -0
- package/lib/alchemy.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/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +2 -0
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts +6 -8
- package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.js +3 -3
- package/lib/cloudflare/bun-spa/bun-spa.js.map +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/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +30 -3
- package/lib/cloudflare/miniflare/build-worker-options.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/worker-metadata.d.ts +14 -2
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +4 -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.d.ts +74 -10
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +16 -7
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/scope.d.ts +5 -0
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +3 -1
- 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/idempotent-spawn.js +1 -1
- package/lib/util/idempotent-spawn.js.map +1 -1
- package/lib/util/telemetry.d.ts +3 -2
- package/lib/util/telemetry.d.ts.map +1 -1
- package/lib/util/telemetry.js +34 -10
- package/lib/util/telemetry.js.map +1 -1
- package/package.json +1 -1
- package/src/alchemy.ts +5 -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/api.ts +2 -0
- package/src/cloudflare/bun-spa/bun-spa.ts +5 -9
- package/src/cloudflare/d1-database.ts +2 -2
- package/src/cloudflare/miniflare/build-worker-options.ts +32 -4
- package/src/cloudflare/permission-groups.ts +1832 -45
- package/src/cloudflare/worker-metadata.ts +20 -3
- package/src/cloudflare/worker-stub.ts +28 -11
- package/src/cloudflare/worker.ts +98 -17
- package/src/scope.ts +7 -0
- package/src/util/camel-to-snake.ts +88 -0
- package/src/util/idempotent-spawn.ts +1 -1
- package/src/util/telemetry.ts +41 -10
- package/templates/tanstack-start/package.json +2 -2
- package/workers/tunnel-proxy.js +1 -1
|
@@ -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 = {
|
package/src/cloudflare/api.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Provider, type Credentials } from "../auth.ts";
|
|
2
|
+
import { Scope } from "../scope.ts";
|
|
2
3
|
import type { Secret } from "../secret.ts";
|
|
3
4
|
import { isBinary } from "../serde.ts";
|
|
4
5
|
import { memoize } from "../util/memoize.ts";
|
|
@@ -99,6 +100,7 @@ export const createCloudflareApi = memoize(
|
|
|
99
100
|
try {
|
|
100
101
|
const profile =
|
|
101
102
|
options.profile ??
|
|
103
|
+
Scope.getScope()?.profile ??
|
|
102
104
|
process.env.CLOUDFLARE_PROFILE ??
|
|
103
105
|
process.env.ALCHEMY_PROFILE ??
|
|
104
106
|
"default";
|
|
@@ -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,
|
|
@@ -14,21 +13,18 @@ import {
|
|
|
14
13
|
Website,
|
|
15
14
|
type WebsiteProps,
|
|
16
15
|
} from "../website.ts";
|
|
17
|
-
import type { Worker } from "../worker.ts";
|
|
18
16
|
|
|
19
17
|
export interface BunSPAProps<B extends Bindings> extends WebsiteProps<B> {
|
|
20
18
|
frontend: string;
|
|
21
19
|
outDir?: string;
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
export type BunSPA<B extends Bindings> = B
|
|
25
|
-
? never
|
|
26
|
-
: Worker<B & { ASSETS: Assets }>;
|
|
22
|
+
export type BunSPA<B extends Bindings> = Website<B> & { apiUrl: string };
|
|
27
23
|
|
|
28
24
|
export async function BunSPA<B extends Bindings>(
|
|
29
25
|
id: string,
|
|
30
26
|
props: BunSPAProps<B>,
|
|
31
|
-
): Promise<BunSPA<B
|
|
27
|
+
): Promise<BunSPA<B> & { apiUrl: string }> {
|
|
32
28
|
const frontendPath = path.resolve(props.frontend);
|
|
33
29
|
if (!(await exists(frontendPath))) {
|
|
34
30
|
throw new Error(`Frontend path ${frontendPath} does not exist`);
|
|
@@ -66,6 +62,7 @@ export async function BunSPA<B extends Bindings>(
|
|
|
66
62
|
),
|
|
67
63
|
});
|
|
68
64
|
|
|
65
|
+
let apiUrl = website.url!;
|
|
69
66
|
// in dev
|
|
70
67
|
if (scope.local) {
|
|
71
68
|
const cwd = props.cwd ?? process.cwd();
|
|
@@ -74,7 +71,6 @@ export async function BunSPA<B extends Bindings>(
|
|
|
74
71
|
props,
|
|
75
72
|
`bun '${path.relative(cwd, frontendPath)}'`,
|
|
76
73
|
);
|
|
77
|
-
console.log("backend url", website.url);
|
|
78
74
|
const secrets = props.wrangler?.secrets ?? !props.wrangler?.path;
|
|
79
75
|
const env = {
|
|
80
76
|
...(process.env ?? {}),
|
|
@@ -101,11 +97,11 @@ export async function BunSPA<B extends Bindings>(
|
|
|
101
97
|
...process.env,
|
|
102
98
|
NODE_ENV: "development",
|
|
103
99
|
ALCHEMY_ROOT: Scope.current.rootDir,
|
|
104
|
-
PUBLIC_BACKEND_URL:
|
|
100
|
+
PUBLIC_BACKEND_URL: apiUrl,
|
|
105
101
|
},
|
|
106
102
|
});
|
|
107
103
|
}
|
|
108
|
-
return website
|
|
104
|
+
return { ...website, apiUrl } as BunSPA<B>;
|
|
109
105
|
}
|
|
110
106
|
|
|
111
107
|
async function validateBunfigToml(cwd: string): Promise<void> {
|
|
@@ -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
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as miniflare from "miniflare";
|
|
2
2
|
import { assertNever } from "../../util/assert-never.ts";
|
|
3
3
|
import type { HTTPServer } from "../../util/http.ts";
|
|
4
|
+
import { logger } from "../../util/logger.ts";
|
|
4
5
|
import type { CloudflareApi } from "../api.ts";
|
|
5
6
|
import type {
|
|
6
7
|
Binding,
|
|
@@ -79,10 +80,7 @@ export const buildWorkerOptions = async (
|
|
|
79
80
|
],
|
|
80
81
|
containerEngine: {
|
|
81
82
|
localDocker: {
|
|
82
|
-
socketPath:
|
|
83
|
-
process.platform === "win32"
|
|
84
|
-
? "//./pipe/docker_engine"
|
|
85
|
-
: "unix:///var/run/docker.sock",
|
|
83
|
+
socketPath: await getDockerSocketPath(),
|
|
86
84
|
},
|
|
87
85
|
},
|
|
88
86
|
// This exposes the worker as a route that can be accessed by setting the MF-Route-Override header.
|
|
@@ -480,3 +478,33 @@ const isRemoteBinding = (binding: Binding) => {
|
|
|
480
478
|
!!binding.dev.remote
|
|
481
479
|
);
|
|
482
480
|
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* DOCKER_HOST env is standardized
|
|
484
|
+
* docker has an option to expose on tcp://localhost:2375; so we check 2375 if the user has it enabled
|
|
485
|
+
* the pipe on windows doesn't work half the time(even though the pipe exists). This seems like a strange error on how miniflare parses the pipe
|
|
486
|
+
* @returns The Docker path
|
|
487
|
+
*/
|
|
488
|
+
async function getDockerSocketPath() {
|
|
489
|
+
if (process.env.DOCKER_HOST) {
|
|
490
|
+
return process.env.DOCKER_HOST;
|
|
491
|
+
}
|
|
492
|
+
// Check if docker is running on tcp://localhost:2375 using fetch
|
|
493
|
+
try {
|
|
494
|
+
const url = "http://localhost:2375/_ping";
|
|
495
|
+
const res = await fetch(url, { method: "GET" });
|
|
496
|
+
if (res.ok) {
|
|
497
|
+
const text = await res.text();
|
|
498
|
+
if (text.trim() === "OK") {
|
|
499
|
+
return "localhost:2375";
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
} catch {}
|
|
503
|
+
if (process.platform === "win32") {
|
|
504
|
+
logger.warn(
|
|
505
|
+
"Using the pipe on Windows is unstable. If you have issues, try setting DOCKER_HOST or enabling 'Expose daemon on tcp://localhost:2375 without TLS' in docker desktop",
|
|
506
|
+
);
|
|
507
|
+
return "//./pipe/docker_engine";
|
|
508
|
+
}
|
|
509
|
+
return "unix:///var/run/docker.sock";
|
|
510
|
+
}
|