alchemy 0.39.0 → 0.40.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/lib/alchemy.d.ts +6 -0
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +5 -0
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.js +1 -1
- package/lib/apply.js.map +1 -1
- package/lib/build-date.d.ts +1 -1
- package/lib/build-date.js +1 -1
- package/lib/cloudflare/bucket.d.ts +10 -0
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +1 -0
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker-dev.d.ts +25 -0
- package/lib/cloudflare/bundle/bundle-worker-dev.d.ts.map +1 -0
- package/lib/cloudflare/bundle/bundle-worker-dev.js +85 -0
- package/lib/cloudflare/bundle/bundle-worker-dev.js.map +1 -0
- package/lib/cloudflare/bundle/bundle-worker.d.ts.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker.js +0 -3
- package/lib/cloudflare/bundle/bundle-worker.js.map +1 -1
- package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts +1 -2
- package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts.map +1 -1
- package/lib/cloudflare/bundle/nodejs-compat-mode.js +6 -1
- package/lib/cloudflare/bundle/nodejs-compat-mode.js.map +1 -1
- package/lib/cloudflare/d1-database.d.ts +10 -0
- package/lib/cloudflare/d1-database.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.js +1 -0
- package/lib/cloudflare/d1-database.js.map +1 -1
- package/lib/cloudflare/do-state-store/internal.d.ts +1 -0
- package/lib/cloudflare/do-state-store/internal.d.ts.map +1 -1
- package/lib/cloudflare/do-state-store/internal.js +27 -29
- package/lib/cloudflare/do-state-store/internal.js.map +1 -1
- package/lib/cloudflare/do-state-store/store.d.ts.map +1 -1
- package/lib/cloudflare/do-state-store/store.js +7 -20
- package/lib/cloudflare/do-state-store/store.js.map +1 -1
- package/lib/cloudflare/images.d.ts +12 -0
- package/lib/cloudflare/images.d.ts.map +1 -1
- package/lib/cloudflare/images.js +4 -0
- package/lib/cloudflare/images.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +10 -0
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js +1 -0
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/queue.d.ts +10 -0
- package/lib/cloudflare/queue.d.ts.map +1 -1
- package/lib/cloudflare/queue.js +1 -0
- package/lib/cloudflare/queue.js.map +1 -1
- package/lib/cloudflare/vite.d.ts.map +1 -1
- package/lib/cloudflare/vite.js +12 -0
- package/lib/cloudflare/vite.js.map +1 -1
- package/lib/cloudflare/website.d.ts +7 -0
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +16 -6
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker/get-worker-template.d.ts +2 -0
- package/lib/cloudflare/worker/get-worker-template.d.ts.map +1 -0
- package/lib/cloudflare/worker/get-worker-template.js +12 -0
- package/lib/cloudflare/worker/get-worker-template.js.map +1 -0
- package/lib/cloudflare/worker/http-server.d.ts +14 -0
- package/lib/cloudflare/worker/http-server.d.ts.map +1 -0
- package/lib/cloudflare/worker/http-server.js +67 -0
- package/lib/cloudflare/worker/http-server.js.map +1 -0
- package/lib/cloudflare/worker/miniflare-worker-options.d.ts +13 -0
- package/lib/cloudflare/worker/miniflare-worker-options.d.ts.map +1 -0
- package/lib/cloudflare/worker/miniflare-worker-options.js +408 -0
- package/lib/cloudflare/worker/miniflare-worker-options.js.map +1 -0
- package/lib/cloudflare/worker/miniflare.d.ts +31 -0
- package/lib/cloudflare/worker/miniflare.d.ts.map +1 -0
- package/lib/cloudflare/worker/miniflare.js +150 -0
- package/lib/cloudflare/worker/miniflare.js.map +1 -0
- package/lib/cloudflare/worker/mixed-mode.d.ts +17 -0
- package/lib/cloudflare/worker/mixed-mode.d.ts.map +1 -0
- package/lib/cloudflare/worker/mixed-mode.js +145 -0
- package/lib/cloudflare/worker/mixed-mode.js.map +1 -0
- package/lib/cloudflare/worker/subdomain.d.ts +3 -0
- package/lib/cloudflare/worker/subdomain.d.ts.map +1 -0
- package/lib/cloudflare/worker/subdomain.js +10 -0
- package/lib/cloudflare/worker/subdomain.js.map +1 -0
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +7 -3
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +23 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +229 -8
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/workflow.d.ts +7 -0
- package/lib/cloudflare/workflow.d.ts.map +1 -1
- package/lib/cloudflare/workflow.js.map +1 -1
- package/lib/destroy.d.ts.map +1 -1
- package/lib/destroy.js +9 -5
- package/lib/destroy.js.map +1 -1
- package/lib/scope.d.ts +2 -0
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +10 -2
- package/lib/scope.js.map +1 -1
- package/lib/util/assert-never.d.ts +1 -1
- package/lib/util/assert-never.d.ts.map +1 -1
- package/lib/util/assert-never.js +2 -2
- package/lib/util/assert-never.js.map +1 -1
- package/lib/util/cli.d.ts +21 -7
- package/lib/util/cli.d.ts.map +1 -1
- package/lib/util/cli.js +8 -0
- package/lib/util/cli.js.map +1 -1
- package/lib/util/deferred-promise.d.ts +8 -0
- package/lib/util/deferred-promise.d.ts.map +1 -0
- package/lib/util/deferred-promise.js +16 -0
- package/lib/util/deferred-promise.js.map +1 -0
- package/lib/util/detect-package-manager.d.ts +3 -0
- package/lib/util/detect-package-manager.d.ts.map +1 -0
- package/lib/util/detect-package-manager.js +25 -0
- package/lib/util/detect-package-manager.js.map +1 -0
- package/lib/util/logger.d.ts.map +1 -1
- package/lib/util/logger.js +1 -0
- package/lib/util/logger.js.map +1 -1
- package/lib/util/memoize.d.ts +4 -0
- package/lib/util/memoize.d.ts.map +1 -0
- package/lib/util/memoize.js +17 -0
- package/lib/util/memoize.js.map +1 -0
- package/package.json +7 -5
- package/src/alchemy.ts +14 -0
- package/src/apply.ts +2 -1
- package/src/build-date.ts +1 -1
- package/src/cloudflare/bucket.ts +12 -0
- package/src/cloudflare/bundle/bundle-worker-dev.ts +126 -0
- package/src/cloudflare/bundle/bundle-worker.ts +0 -5
- package/src/cloudflare/bundle/nodejs-compat-mode.ts +12 -3
- package/src/cloudflare/d1-database.ts +11 -0
- package/src/cloudflare/do-state-store/internal.ts +30 -31
- package/src/cloudflare/do-state-store/store.ts +9 -26
- package/src/cloudflare/images.ts +14 -0
- package/src/cloudflare/kv-namespace.ts +12 -0
- package/src/cloudflare/queue.ts +12 -0
- package/src/cloudflare/vite.ts +12 -0
- package/src/cloudflare/website.ts +25 -6
- package/src/cloudflare/worker/get-worker-template.ts +14 -0
- package/src/cloudflare/worker/http-server.ts +80 -0
- package/src/cloudflare/worker/miniflare-worker-options.ts +531 -0
- package/src/cloudflare/worker/miniflare.ts +198 -0
- package/src/cloudflare/worker/mixed-mode.ts +220 -0
- package/src/cloudflare/worker/subdomain.ts +16 -0
- package/src/cloudflare/worker-metadata.ts +12 -4
- package/src/cloudflare/worker.ts +284 -18
- package/src/cloudflare/workflow.ts +7 -0
- package/src/destroy.ts +9 -5
- package/src/scope.ts +20 -1
- package/src/util/assert-never.ts +2 -2
- package/src/util/cli.ts +19 -7
- package/src/util/deferred-promise.ts +19 -0
- package/src/util/detect-package-manager.ts +23 -0
- package/src/util/logger.ts +1 -0
- package/src/util/memoize.ts +23 -0
- package/workers/do-state-store.js +826 -0
- package/{src/cloudflare/do-state-store/worker.ts → workers/do-state-store.ts} +55 -23
- package/workers/mixed-mode-proxy-worker.js +39 -0
- package/workers/mixed-mode-proxy-worker.ts +46 -0
- package/lib/cloudflare/do-state-store/worker.d.ts +0 -27
- package/lib/cloudflare/do-state-store/worker.d.ts.map +0 -1
- package/lib/cloudflare/do-state-store/worker.js +0 -179
- package/lib/cloudflare/do-state-store/worker.js.map +0 -1
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { bundle } from "../../esbuild/index.ts";
|
|
4
|
-
import { exists } from "../../util/exists.ts";
|
|
1
|
+
import { logger } from "../../util/logger.ts";
|
|
5
2
|
import { withExponentialBackoff } from "../../util/retry.ts";
|
|
6
3
|
import { handleApiError } from "../api-error.ts";
|
|
7
4
|
import type { CloudflareApi } from "../api.ts";
|
|
8
5
|
import type { WorkerMetadata } from "../worker-metadata.ts";
|
|
6
|
+
import { getWorkerTemplate } from "../worker/get-worker-template.ts";
|
|
9
7
|
import type { DOStateStoreAPI } from "./types.ts";
|
|
10
8
|
|
|
11
9
|
interface DOStateStoreClientOptions {
|
|
@@ -84,6 +82,30 @@ export class DOStateStoreClient {
|
|
|
84
82
|
});
|
|
85
83
|
}
|
|
86
84
|
|
|
85
|
+
async waitUntilReady(): Promise<void> {
|
|
86
|
+
// This ensures the token is correct and the worker is ready to use.
|
|
87
|
+
let last: Response | undefined;
|
|
88
|
+
let delay = 1000;
|
|
89
|
+
for (let i = 0; i < 20; i++) {
|
|
90
|
+
const res = await this.validate();
|
|
91
|
+
if (res.ok) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!last) {
|
|
95
|
+
logger.log("Waiting for state store deployment...");
|
|
96
|
+
}
|
|
97
|
+
last = res;
|
|
98
|
+
// Exponential backoff with jitter
|
|
99
|
+
const jitter = Math.random() * 0.1 * delay;
|
|
100
|
+
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
101
|
+
delay *= 1.5; // Increase the delay for next attempt
|
|
102
|
+
delay = Math.min(delay, 10000); // Cap at 10 seconds
|
|
103
|
+
}
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Failed to access state store: ${last?.status} ${last?.statusText}`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
87
109
|
async fetch(path: string, init: RequestInit = {}): Promise<Response> {
|
|
88
110
|
const url = new URL(path, this.options.url);
|
|
89
111
|
url.searchParams.set("app", this.options.app);
|
|
@@ -98,7 +120,7 @@ export class DOStateStoreClient {
|
|
|
98
120
|
}
|
|
99
121
|
}
|
|
100
122
|
|
|
101
|
-
const TAG = "alchemy-state-store:2025-06-
|
|
123
|
+
const TAG = "alchemy-state-store:2025-06-23";
|
|
102
124
|
|
|
103
125
|
const cache = new Map<string, string>();
|
|
104
126
|
|
|
@@ -119,12 +141,13 @@ export async function upsertStateStoreWorker(
|
|
|
119
141
|
return;
|
|
120
142
|
}
|
|
121
143
|
const formData = new FormData();
|
|
122
|
-
|
|
144
|
+
const worker = await getWorkerTemplate("do-state-store");
|
|
145
|
+
formData.append(worker.name, worker);
|
|
123
146
|
formData.append(
|
|
124
147
|
"metadata",
|
|
125
148
|
new Blob([
|
|
126
149
|
JSON.stringify({
|
|
127
|
-
main_module:
|
|
150
|
+
main_module: worker.name,
|
|
128
151
|
compatibility_date: "2025-06-01",
|
|
129
152
|
compatibility_flags: ["nodejs_compat"],
|
|
130
153
|
bindings: [
|
|
@@ -217,27 +240,3 @@ export async function getAccountSubdomain(api: CloudflareApi) {
|
|
|
217
240
|
cache.set(key, subdomain);
|
|
218
241
|
return subdomain;
|
|
219
242
|
}
|
|
220
|
-
|
|
221
|
-
async function bundleWorkerScript() {
|
|
222
|
-
const thisDir =
|
|
223
|
-
typeof __dirname !== "undefined"
|
|
224
|
-
? __dirname
|
|
225
|
-
: path.dirname(fileURLToPath(import.meta.url));
|
|
226
|
-
// we can be running from .ts or .js, so resolve it defensively
|
|
227
|
-
const workerTs = path.join(thisDir, "worker.ts");
|
|
228
|
-
const workerJs = path.join(thisDir, "worker.js");
|
|
229
|
-
const result = await bundle({
|
|
230
|
-
entryPoint: (await exists(workerTs)) ? workerTs : workerJs,
|
|
231
|
-
bundle: true,
|
|
232
|
-
format: "esm",
|
|
233
|
-
target: "es2022",
|
|
234
|
-
external: ["cloudflare:*", "node:crypto"],
|
|
235
|
-
write: false,
|
|
236
|
-
});
|
|
237
|
-
if (!result.outputFiles?.[0]) {
|
|
238
|
-
throw new Error("Failed to bundle worker.ts");
|
|
239
|
-
}
|
|
240
|
-
return new File([result.outputFiles[0].text], "worker.js", {
|
|
241
|
-
type: "application/javascript+module",
|
|
242
|
-
});
|
|
243
|
-
}
|
|
@@ -4,11 +4,8 @@ import type { Scope } from "../../scope.ts";
|
|
|
4
4
|
import type { State, StateStore } from "../../state.ts";
|
|
5
5
|
import { deserializeState } from "../../state.ts";
|
|
6
6
|
import { createCloudflareApi, type CloudflareApiOptions } from "../api.ts";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
getAccountSubdomain,
|
|
10
|
-
upsertStateStoreWorker,
|
|
11
|
-
} from "./internal.ts";
|
|
7
|
+
import { getAccountSubdomain } from "../worker/subdomain.ts";
|
|
8
|
+
import { DOStateStoreClient, upsertStateStoreWorker } from "./internal.ts";
|
|
12
9
|
|
|
13
10
|
export interface DOStateStoreOptions extends CloudflareApiOptions {
|
|
14
11
|
/**
|
|
@@ -95,33 +92,19 @@ export class DOStateStore implements StateStore {
|
|
|
95
92
|
this.options.worker?.force ?? false,
|
|
96
93
|
),
|
|
97
94
|
]);
|
|
95
|
+
if (!subdomain) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
"Failed to access state store worker because the workers.dev subdomain is not available.",
|
|
98
|
+
);
|
|
99
|
+
}
|
|
98
100
|
const client = new DOStateStoreClient({
|
|
99
101
|
app: this.scope.appName ?? "alchemy",
|
|
100
102
|
stage: this.scope.stage,
|
|
101
103
|
url: `https://${workerName}.${subdomain}.workers.dev`,
|
|
102
104
|
token,
|
|
103
105
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
let delay = 1000;
|
|
107
|
-
for (let i = 0; i < 20; i++) {
|
|
108
|
-
const res = await client.validate();
|
|
109
|
-
if (res.ok) {
|
|
110
|
-
return client;
|
|
111
|
-
}
|
|
112
|
-
if (!last) {
|
|
113
|
-
console.log("Waiting for state store deployment...");
|
|
114
|
-
}
|
|
115
|
-
last = res;
|
|
116
|
-
// Exponential backoff with jitter
|
|
117
|
-
const jitter = Math.random() * 0.1 * delay;
|
|
118
|
-
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
119
|
-
delay *= 1.5; // Increase the delay for next attempt
|
|
120
|
-
delay = Math.min(delay, 10000); // Cap at 10 seconds
|
|
121
|
-
}
|
|
122
|
-
throw new Error(
|
|
123
|
-
`Failed to access state store: ${last?.status} ${last?.statusText}`,
|
|
124
|
-
);
|
|
106
|
+
await client.waitUntilReady();
|
|
107
|
+
return client;
|
|
125
108
|
}
|
|
126
109
|
|
|
127
110
|
private async getClient() {
|
package/src/cloudflare/images.ts
CHANGED
|
@@ -56,4 +56,18 @@
|
|
|
56
56
|
*/
|
|
57
57
|
export class Images {
|
|
58
58
|
public readonly type = "images";
|
|
59
|
+
public readonly dev?: {
|
|
60
|
+
remote?: boolean;
|
|
61
|
+
};
|
|
62
|
+
constructor(props?: {
|
|
63
|
+
dev?: {
|
|
64
|
+
/**
|
|
65
|
+
* Whether to run the images binding remotely instead of locally
|
|
66
|
+
* @default false
|
|
67
|
+
*/
|
|
68
|
+
remote?: boolean;
|
|
69
|
+
};
|
|
70
|
+
}) {
|
|
71
|
+
this.dev = props?.dev;
|
|
72
|
+
}
|
|
59
73
|
}
|
|
@@ -41,6 +41,17 @@ export interface KVNamespaceProps extends CloudflareApiOptions {
|
|
|
41
41
|
* @default true
|
|
42
42
|
*/
|
|
43
43
|
delete?: boolean;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether to emulate the KV namespace locally when Alchemy is running in watch mode.
|
|
47
|
+
*/
|
|
48
|
+
dev?: {
|
|
49
|
+
/**
|
|
50
|
+
* Whether to run the KV namespace remotely instead of locally
|
|
51
|
+
* @default false
|
|
52
|
+
*/
|
|
53
|
+
remote?: boolean;
|
|
54
|
+
};
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
/**
|
|
@@ -251,6 +262,7 @@ const _KVNamespace = Resource(
|
|
|
251
262
|
namespaceId,
|
|
252
263
|
title: props.title,
|
|
253
264
|
values: props.values,
|
|
265
|
+
dev: props.dev,
|
|
254
266
|
createdAt: createdAt,
|
|
255
267
|
modifiedAt: Date.now(),
|
|
256
268
|
});
|
package/src/cloudflare/queue.ts
CHANGED
|
@@ -72,6 +72,17 @@ export interface QueueProps extends CloudflareApiOptions {
|
|
|
72
72
|
* @default false
|
|
73
73
|
*/
|
|
74
74
|
adopt?: boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether to emulate the queue locally when Alchemy is running in watch mode.
|
|
78
|
+
*/
|
|
79
|
+
dev?: {
|
|
80
|
+
/**
|
|
81
|
+
* Whether to run the queue remotely instead of locally
|
|
82
|
+
* @default false
|
|
83
|
+
*/
|
|
84
|
+
remote?: boolean;
|
|
85
|
+
};
|
|
75
86
|
}
|
|
76
87
|
|
|
77
88
|
export function isQueue(eventSource: any): eventSource is Queue {
|
|
@@ -289,6 +300,7 @@ const QueueResource = Resource("cloudflare::Queue", async function <
|
|
|
289
300
|
createdOn: queueData.result.created_on || new Date().toISOString(),
|
|
290
301
|
modifiedOn: queueData.result.modified_on || new Date().toISOString(),
|
|
291
302
|
accountId: api.accountId,
|
|
303
|
+
dev: props.dev,
|
|
292
304
|
// phantom properties
|
|
293
305
|
Body: undefined as T,
|
|
294
306
|
Batch: undefined! as MessageBatch<T>,
|
package/src/cloudflare/vite.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { detectPackageManager } from "../util/detect-package-manager.ts";
|
|
2
3
|
import type { Assets } from "./assets.ts";
|
|
3
4
|
import type { Bindings } from "./bindings.ts";
|
|
4
5
|
import { Website, type WebsiteProps } from "./website.ts";
|
|
@@ -17,6 +18,13 @@ export async function Vite<B extends Bindings>(
|
|
|
17
18
|
props: ViteProps<B>,
|
|
18
19
|
): Promise<Vite<B>> {
|
|
19
20
|
const defaultAssets = path.join("dist", "client");
|
|
21
|
+
const packageManager = detectPackageManager();
|
|
22
|
+
const devCommand = {
|
|
23
|
+
npm: "npx vite dev",
|
|
24
|
+
bun: "bun vite dev",
|
|
25
|
+
pnpm: "pnpm vite dev",
|
|
26
|
+
yarn: "yarn vite dev",
|
|
27
|
+
}[packageManager];
|
|
20
28
|
return Website(id, {
|
|
21
29
|
...props,
|
|
22
30
|
spa: true,
|
|
@@ -27,5 +35,9 @@ export async function Vite<B extends Bindings>(
|
|
|
27
35
|
dist: props.assets.dist ?? defaultAssets,
|
|
28
36
|
}
|
|
29
37
|
: (props.assets ?? defaultAssets),
|
|
38
|
+
dev: props.dev ?? {
|
|
39
|
+
command: devCommand,
|
|
40
|
+
url: "http://localhost:5173",
|
|
41
|
+
},
|
|
30
42
|
});
|
|
31
43
|
}
|
|
@@ -94,6 +94,14 @@ export interface WebsiteProps<B extends Bindings>
|
|
|
94
94
|
* @default false
|
|
95
95
|
*/
|
|
96
96
|
spa?: boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Configure the command to use in development mode
|
|
100
|
+
*/
|
|
101
|
+
dev?: {
|
|
102
|
+
command: string;
|
|
103
|
+
url: string;
|
|
104
|
+
};
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
export type Website<B extends Bindings> = B extends { ASSETS: any }
|
|
@@ -114,7 +122,7 @@ export async function Website<B extends Bindings>(
|
|
|
114
122
|
{
|
|
115
123
|
parent: Scope.current,
|
|
116
124
|
},
|
|
117
|
-
async () => {
|
|
125
|
+
async (scope) => {
|
|
118
126
|
// directory from which all relative paths are resolved
|
|
119
127
|
const cwd = path.resolve(props.cwd || process.cwd());
|
|
120
128
|
|
|
@@ -177,6 +185,13 @@ export default {
|
|
|
177
185
|
};`,
|
|
178
186
|
url: true,
|
|
179
187
|
adopt: true,
|
|
188
|
+
dev: props.dev
|
|
189
|
+
? {
|
|
190
|
+
command: props.dev.command,
|
|
191
|
+
url: props.dev.url,
|
|
192
|
+
cwd,
|
|
193
|
+
}
|
|
194
|
+
: undefined,
|
|
180
195
|
} as WorkerProps<any> & { name: string };
|
|
181
196
|
|
|
182
197
|
if (wrangler) {
|
|
@@ -196,7 +211,9 @@ export default {
|
|
|
196
211
|
});
|
|
197
212
|
}
|
|
198
213
|
|
|
199
|
-
|
|
214
|
+
const isDev = scope.dev && !!props.dev;
|
|
215
|
+
|
|
216
|
+
if (props.command && !isDev) {
|
|
200
217
|
await Exec("build", {
|
|
201
218
|
cwd,
|
|
202
219
|
command: props.command,
|
|
@@ -211,10 +228,12 @@ export default {
|
|
|
211
228
|
...workerProps.bindings,
|
|
212
229
|
// we don't include the Assets binding until after build to make sure the asset manifest is correct
|
|
213
230
|
// we generate the wrangler.json using all the bind
|
|
214
|
-
ASSETS:
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
231
|
+
ASSETS: isDev
|
|
232
|
+
? undefined
|
|
233
|
+
: await Assets("assets", {
|
|
234
|
+
// Assets are discovered from proces.cwd(), not Website.cwd or wrangler.jsonc
|
|
235
|
+
path: path.relative(process.cwd(), assetsDirPath),
|
|
236
|
+
}),
|
|
218
237
|
},
|
|
219
238
|
} as WorkerProps<any> & { name: string })) as Website<B>;
|
|
220
239
|
},
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
export async function getWorkerTemplate(
|
|
6
|
+
name: "do-state-store" | "mixed-mode-proxy-worker",
|
|
7
|
+
) {
|
|
8
|
+
const dir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const path = join(dir, "..", "..", "..", "workers", `${name}.js`);
|
|
10
|
+
const template = await readFile(path, "utf8");
|
|
11
|
+
return new File([template], `${name}.js`, {
|
|
12
|
+
type: "application/javascript+module",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import type { AddressInfo } from "node:net";
|
|
3
|
+
import { Readable } from "node:stream";
|
|
4
|
+
|
|
5
|
+
export class HTTPServer {
|
|
6
|
+
server: http.Server;
|
|
7
|
+
ready: Promise<void>;
|
|
8
|
+
|
|
9
|
+
constructor(options: {
|
|
10
|
+
port?: number;
|
|
11
|
+
fetch: (request: Request) => Promise<Response>;
|
|
12
|
+
}) {
|
|
13
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
14
|
+
this.ready = promise;
|
|
15
|
+
this.server = http
|
|
16
|
+
.createServer(async (req, res) => {
|
|
17
|
+
const response = await options.fetch(toWebRequest(req));
|
|
18
|
+
await writeNodeResponse(res, response);
|
|
19
|
+
})
|
|
20
|
+
.listen(options.port, resolve);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get port() {
|
|
24
|
+
return (this.server.address() as AddressInfo).port;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get hostname() {
|
|
28
|
+
const address = (this.server.address() as AddressInfo)?.address;
|
|
29
|
+
if (address === "::") {
|
|
30
|
+
return "localhost";
|
|
31
|
+
}
|
|
32
|
+
return address;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get url() {
|
|
36
|
+
return `http://${this.hostname}:${this.port}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
stop() {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
this.server.close((err) => {
|
|
42
|
+
if (err) {
|
|
43
|
+
reject(err);
|
|
44
|
+
} else {
|
|
45
|
+
resolve(undefined);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function toWebRequest(request: http.IncomingMessage): Request {
|
|
53
|
+
const method = request.method ?? "GET";
|
|
54
|
+
return new Request(`http://${request.headers.host}${request.url ?? "/"}`, {
|
|
55
|
+
method,
|
|
56
|
+
headers: request.headers as Record<string, string>,
|
|
57
|
+
body:
|
|
58
|
+
["GET", "HEAD", "OPTIONS"].includes(method) || !request.readable
|
|
59
|
+
? undefined
|
|
60
|
+
: (Readable.toWeb(request) as unknown as BodyInit),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function writeNodeResponse(res: http.ServerResponse, response: Response) {
|
|
65
|
+
res.statusCode = response.status;
|
|
66
|
+
response.headers.forEach((value, key) => {
|
|
67
|
+
res.setHeader(key, value);
|
|
68
|
+
});
|
|
69
|
+
await response.body?.pipeTo(
|
|
70
|
+
new WritableStream({
|
|
71
|
+
write(chunk) {
|
|
72
|
+
res.write(chunk);
|
|
73
|
+
},
|
|
74
|
+
close() {
|
|
75
|
+
res.end();
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
res.end();
|
|
80
|
+
}
|