alchemy 0.48.0 → 0.48.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.mjs +63 -56
- package/lib/apply.js +1 -0
- package/lib/apply.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +5 -0
- package/lib/cloudflare/compatibility-date.gen.d.ts.map +1 -0
- package/lib/cloudflare/compatibility-date.gen.js +7 -0
- package/lib/cloudflare/compatibility-date.gen.js.map +1 -0
- package/lib/cloudflare/d1-migrations.d.ts.map +1 -1
- package/lib/cloudflare/d1-migrations.js +6 -11
- package/lib/cloudflare/d1-migrations.js.map +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts +2 -2
- package/lib/cloudflare/miniflare/miniflare-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-options.js +11 -2
- package/lib/cloudflare/miniflare/miniflare-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts +20 -0
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -0
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +80 -0
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -0
- package/lib/cloudflare/miniflare/miniflare.d.ts +6 -6
- package/lib/cloudflare/miniflare/miniflare.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare.js +25 -16
- package/lib/cloudflare/miniflare/miniflare.js.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts +10 -10
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.js +34 -44
- package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
- package/lib/cloudflare/oauth.js +3 -3
- package/lib/cloudflare/oauth.js.map +1 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +2 -1
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +1 -2
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +3 -10
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/scope.d.ts +6 -0
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +15 -1
- package/lib/scope.js.map +1 -1
- package/lib/state/cloudflare-state-store.d.ts.map +1 -1
- package/lib/state/cloudflare-state-store.js +2 -2
- package/lib/state/cloudflare-state-store.js.map +1 -1
- package/lib/util/http.d.ts +13 -0
- package/lib/util/http.d.ts.map +1 -0
- package/lib/util/http.js +78 -0
- package/lib/util/http.js.map +1 -0
- package/package.json +3 -3
- package/src/apply.ts +1 -0
- package/src/cloudflare/compatibility-date.gen.ts +7 -0
- package/src/cloudflare/d1-migrations.ts +8 -21
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/miniflare-worker-options.ts +13 -1
- package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +97 -0
- package/src/cloudflare/miniflare/miniflare.ts +30 -21
- package/src/cloudflare/miniflare/remote-binding-proxy.ts +40 -52
- package/src/cloudflare/oauth.ts +3 -3
- package/src/cloudflare/website.ts +2 -6
- package/src/cloudflare/worker.ts +5 -12
- package/src/scope.ts +17 -1
- package/src/state/cloudflare-state-store.ts +2 -2
- package/src/util/http.ts +94 -0
- package/lib/build-date.d.ts +0 -6
- package/lib/build-date.d.ts.map +0 -1
- package/lib/build-date.js +0 -8
- package/lib/build-date.js.map +0 -1
- package/lib/util/http-server.d.ts +0 -14
- package/lib/util/http-server.d.ts.map +0 -1
- package/lib/util/http-server.js +0 -71
- package/lib/util/http-server.js.map +0 -1
- package/src/build-date.ts +0 -8
- package/src/util/http-server.ts +0 -84
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type http from "node:http";
|
|
2
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
3
|
+
import { HTTPServer } from "../../util/http.ts";
|
|
4
|
+
|
|
5
|
+
export interface MiniflareWorkerProxyOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Used to proxy websocket connections to the worker.
|
|
8
|
+
* (This is a hack; used because trying to interact with `response.webSocket` doesn't work on Bun.)
|
|
9
|
+
*/
|
|
10
|
+
getDirectURL: () => Promise<URL>;
|
|
11
|
+
/** Used to proxy HTTP requests to the worker. */
|
|
12
|
+
fetch: (request: Request) => Promise<Response>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class MiniflareWorkerProxy extends HTTPServer {
|
|
16
|
+
wsServer: WebSocketServer;
|
|
17
|
+
wsReconnectAttempts = new Map<string, number>();
|
|
18
|
+
|
|
19
|
+
constructor(private readonly options: MiniflareWorkerProxyOptions) {
|
|
20
|
+
super({
|
|
21
|
+
fetch: options.fetch,
|
|
22
|
+
});
|
|
23
|
+
this.wsServer = new WebSocketServer({ noServer: true });
|
|
24
|
+
this.httpServer.on("upgrade", async (req, socket, head) => {
|
|
25
|
+
this.wsServer.handleUpgrade(req, socket, head, async (client) => {
|
|
26
|
+
const id = crypto.randomUUID();
|
|
27
|
+
await this.handleUpgrade(id, client, req);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private async handleUpgrade(
|
|
33
|
+
id: string,
|
|
34
|
+
client: WebSocket,
|
|
35
|
+
request: http.IncomingMessage,
|
|
36
|
+
) {
|
|
37
|
+
const attempt = this.wsReconnectAttempts.get(id) ?? 0;
|
|
38
|
+
this.wsReconnectAttempts.set(id, attempt + 1);
|
|
39
|
+
if (attempt > 3) {
|
|
40
|
+
client.close(1006, "Too many reconnect attempts");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (attempt > 0) {
|
|
44
|
+
const delay = attempt * 1000;
|
|
45
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const target = await this.options.getDirectURL();
|
|
49
|
+
const url = new URL(request.url ?? "/", target);
|
|
50
|
+
const headers = { ...request.headers };
|
|
51
|
+
// All of these headers are set by the WebSocket constructor,
|
|
52
|
+
// so if we don't delete them, the request will fail.
|
|
53
|
+
delete headers.host;
|
|
54
|
+
delete headers.connection;
|
|
55
|
+
delete headers.upgrade;
|
|
56
|
+
delete headers["sec-websocket-version"];
|
|
57
|
+
delete headers["sec-websocket-key"];
|
|
58
|
+
delete headers["sec-websocket-protocol"];
|
|
59
|
+
delete headers["sec-websocket-extensions"];
|
|
60
|
+
delete headers["sec-websocket-accept"];
|
|
61
|
+
const server = new WebSocket(url.toString(), {
|
|
62
|
+
protocol: request.headers["sec-websocket-protocol"],
|
|
63
|
+
key: request.headers["sec-websocket-key"],
|
|
64
|
+
headers,
|
|
65
|
+
});
|
|
66
|
+
server.on("open", () => {
|
|
67
|
+
// Reset the reconnect attempts when the connection is established successfully.
|
|
68
|
+
this.wsReconnectAttempts.set(id, 0);
|
|
69
|
+
});
|
|
70
|
+
server.on("message", (data, binary) => {
|
|
71
|
+
client.send(data, { binary });
|
|
72
|
+
});
|
|
73
|
+
server.on("close", async (code, reason) => {
|
|
74
|
+
if (code === 1006) {
|
|
75
|
+
// When the worker hot reloads, the websocket connection is closed with this code.
|
|
76
|
+
// Reconnecting allows the client to maintain the same connection.
|
|
77
|
+
await this.handleUpgrade(id, client, request);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
client.close(code, reason);
|
|
81
|
+
});
|
|
82
|
+
client.on("message", (data, binary) => {
|
|
83
|
+
server.send(data, { binary });
|
|
84
|
+
});
|
|
85
|
+
client.on("close", (code, reason) => {
|
|
86
|
+
this.wsReconnectAttempts.delete(id);
|
|
87
|
+
if (server.readyState === WebSocket.OPEN) {
|
|
88
|
+
server.close(code, reason);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async close() {
|
|
94
|
+
this.wsServer.close();
|
|
95
|
+
await super.close();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
} from "miniflare";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { findOpenPort } from "../../util/find-open-port.ts";
|
|
10
|
-
import { HTTPServer } from "../../util/http-server.ts";
|
|
11
10
|
import { logger } from "../../util/logger.ts";
|
|
12
11
|
import {
|
|
13
12
|
promiseWithResolvers,
|
|
@@ -18,6 +17,7 @@ import {
|
|
|
18
17
|
buildRemoteBindings,
|
|
19
18
|
type MiniflareWorkerOptions,
|
|
20
19
|
} from "./miniflare-worker-options.ts";
|
|
20
|
+
import { MiniflareWorkerProxy } from "./miniflare-worker-proxy.ts";
|
|
21
21
|
import {
|
|
22
22
|
createRemoteProxyWorker,
|
|
23
23
|
type RemoteBindingProxy,
|
|
@@ -26,12 +26,12 @@ import {
|
|
|
26
26
|
class MiniflareServer {
|
|
27
27
|
miniflare?: Miniflare;
|
|
28
28
|
workers = new Map<string, WorkerOptions>();
|
|
29
|
-
|
|
29
|
+
workerProxies = new Map<string, MiniflareWorkerProxy>();
|
|
30
30
|
remoteBindingProxies = new Map<string, RemoteBindingProxy>();
|
|
31
31
|
|
|
32
32
|
stream = new WritableStream<{
|
|
33
33
|
worker: MiniflareWorkerOptions;
|
|
34
|
-
promise: PromiseWithResolvers<
|
|
34
|
+
promise: PromiseWithResolvers<MiniflareWorkerProxy>;
|
|
35
35
|
}>({
|
|
36
36
|
write: async ({ worker, promise }) => {
|
|
37
37
|
try {
|
|
@@ -48,7 +48,7 @@ class MiniflareServer {
|
|
|
48
48
|
writer = this.stream.getWriter();
|
|
49
49
|
|
|
50
50
|
async push(worker: MiniflareWorkerOptions) {
|
|
51
|
-
const promise = promiseWithResolvers<
|
|
51
|
+
const promise = promiseWithResolvers<MiniflareWorkerProxy>();
|
|
52
52
|
const [, server] = await Promise.all([
|
|
53
53
|
this.writer.write({ worker, promise }),
|
|
54
54
|
promise.promise,
|
|
@@ -62,11 +62,10 @@ class MiniflareServer {
|
|
|
62
62
|
|
|
63
63
|
private async set(worker: MiniflareWorkerOptions) {
|
|
64
64
|
this.workers.set(
|
|
65
|
-
worker.name
|
|
65
|
+
worker.name,
|
|
66
66
|
await buildMiniflareWorkerOptions({
|
|
67
67
|
...worker,
|
|
68
|
-
remoteProxyConnectionString:
|
|
69
|
-
await this.maybeCreateMixedModeProxy(worker),
|
|
68
|
+
remoteProxyConnectionString: await this.maybeCreateRemoteProxy(worker),
|
|
70
69
|
}),
|
|
71
70
|
);
|
|
72
71
|
if (this.miniflare) {
|
|
@@ -90,33 +89,41 @@ class MiniflareServer {
|
|
|
90
89
|
this.miniflare = new Miniflare(await this.miniflareOptions());
|
|
91
90
|
await withErrorRewrite(this.miniflare.ready);
|
|
92
91
|
}
|
|
93
|
-
const existing = this.
|
|
92
|
+
const existing = this.workerProxies.get(worker.name);
|
|
94
93
|
if (existing) {
|
|
95
94
|
return existing;
|
|
96
95
|
}
|
|
97
|
-
const server = new
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
const server = new MiniflareWorkerProxy({
|
|
97
|
+
getDirectURL: async () => {
|
|
98
|
+
const url = await this.miniflare?.unsafeGetDirectURL(worker.name);
|
|
99
|
+
if (!url) {
|
|
100
|
+
throw new Error(`Worker "${worker.name}" is not running`);
|
|
101
|
+
}
|
|
102
|
+
return url;
|
|
103
|
+
},
|
|
104
|
+
fetch: this.createRequestHandler(worker.name),
|
|
100
105
|
});
|
|
101
|
-
this.
|
|
102
|
-
await server.
|
|
106
|
+
this.workerProxies.set(worker.name, server);
|
|
107
|
+
await server.listen(worker.port ?? (await findOpenPort()));
|
|
103
108
|
return server;
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
private async dispose() {
|
|
107
112
|
await Promise.all([
|
|
108
113
|
this.miniflare?.dispose(),
|
|
109
|
-
...Array.from(this.
|
|
114
|
+
...Array.from(this.workerProxies.values()).map((server) =>
|
|
115
|
+
server.close(),
|
|
116
|
+
),
|
|
110
117
|
...Array.from(this.remoteBindingProxies.values()).map((proxy) =>
|
|
111
|
-
proxy.server.
|
|
118
|
+
proxy.server.close(),
|
|
112
119
|
),
|
|
113
120
|
]);
|
|
114
121
|
this.miniflare = undefined;
|
|
115
122
|
this.workers.clear();
|
|
116
|
-
this.
|
|
123
|
+
this.workerProxies.clear();
|
|
117
124
|
}
|
|
118
125
|
|
|
119
|
-
private async
|
|
126
|
+
private async maybeCreateRemoteProxy(
|
|
120
127
|
worker: MiniflareWorkerOptions,
|
|
121
128
|
): Promise<RemoteProxyConnectionString | undefined> {
|
|
122
129
|
const bindings = buildRemoteBindings(worker);
|
|
@@ -130,6 +137,8 @@ class MiniflareServer {
|
|
|
130
137
|
)
|
|
131
138
|
) {
|
|
132
139
|
return existing.connectionString;
|
|
140
|
+
} else if (existing) {
|
|
141
|
+
await existing.server.close();
|
|
133
142
|
}
|
|
134
143
|
const proxy = await createRemoteProxyWorker({
|
|
135
144
|
name: `mixed-mode-proxy-${crypto.randomUUID()}`,
|
|
@@ -150,8 +159,8 @@ class MiniflareServer {
|
|
|
150
159
|
},
|
|
151
160
|
);
|
|
152
161
|
}
|
|
153
|
-
const
|
|
154
|
-
if (!
|
|
162
|
+
const worker = await this.miniflare?.getWorker(name);
|
|
163
|
+
if (!worker) {
|
|
155
164
|
return new Response(
|
|
156
165
|
`[Alchemy] Cannot find worker "${name}". Please try again.`,
|
|
157
166
|
{
|
|
@@ -159,12 +168,12 @@ class MiniflareServer {
|
|
|
159
168
|
},
|
|
160
169
|
);
|
|
161
170
|
}
|
|
162
|
-
const res = await
|
|
171
|
+
const res = await worker.fetch(req.url, {
|
|
163
172
|
method: req.method,
|
|
164
173
|
headers: req.headers as any,
|
|
165
174
|
body: req.body as any,
|
|
166
|
-
redirect: "manual",
|
|
167
175
|
duplex: "half",
|
|
176
|
+
redirect: "manual",
|
|
168
177
|
});
|
|
169
178
|
return res as unknown as Response;
|
|
170
179
|
} catch (error) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RemoteProxyConnectionString } from "miniflare";
|
|
2
|
-
import { HTTPServer } from "../../util/http
|
|
2
|
+
import { HTTPServer } from "../../util/http.ts";
|
|
3
3
|
import { extractCloudflareResult } from "../api-response.ts";
|
|
4
4
|
import { createCloudflareApi, type CloudflareApi } from "../api.ts";
|
|
5
5
|
import type { WorkerBindingSpec } from "../bindings.ts";
|
|
@@ -22,6 +22,12 @@ interface WorkersPreviewSession {
|
|
|
22
22
|
token: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export interface RemoteBindingProxy {
|
|
26
|
+
server: HTTPServer;
|
|
27
|
+
bindings: WorkerBindingSpec[];
|
|
28
|
+
connectionString: RemoteProxyConnectionString;
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
export async function createRemoteProxyWorker(input: {
|
|
26
32
|
name: string;
|
|
27
33
|
bindings: WorkerBindingSpec[];
|
|
@@ -47,62 +53,44 @@ export async function createRemoteProxyWorker(input: {
|
|
|
47
53
|
}),
|
|
48
54
|
import("../worker-subdomain.ts").then((m) => m.getAccountSubdomain(api)),
|
|
49
55
|
]);
|
|
50
|
-
return new RemoteBindingProxy(
|
|
51
|
-
`https://${input.name}.${subdomain}.workers.dev`,
|
|
52
|
-
token,
|
|
53
|
-
input.bindings,
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
server
|
|
57
|
+
const proxyURL = new URL(`https://${input.name}.${subdomain}.workers.dev`);
|
|
58
|
+
const server = new HTTPServer({
|
|
59
|
+
fetch: async (req) => {
|
|
60
|
+
const origin = new URL(req.url);
|
|
61
|
+
const url = new URL(origin.pathname, proxyURL.toString());
|
|
62
|
+
url.search = origin.search;
|
|
63
|
+
url.hash = origin.hash;
|
|
59
64
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
) {
|
|
65
|
-
this.server = new HTTPServer({
|
|
66
|
-
fetch: this.fetch.bind(this),
|
|
67
|
-
});
|
|
68
|
-
}
|
|
65
|
+
const requestHeaders = new Headers(req.headers);
|
|
66
|
+
requestHeaders.set("cf-workers-preview-token", token);
|
|
67
|
+
requestHeaders.set("host", proxyURL.hostname);
|
|
68
|
+
requestHeaders.delete("cf-connecting-ip");
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async fetch(req: Request) {
|
|
79
|
-
const origin = new URL(req.url);
|
|
80
|
-
const url = new URL(origin.pathname, this.url);
|
|
81
|
-
url.search = origin.search;
|
|
82
|
-
url.hash = origin.hash;
|
|
83
|
-
|
|
84
|
-
const headers = new Headers(req.headers);
|
|
85
|
-
headers.set("cf-workers-preview-token", this.token);
|
|
86
|
-
headers.set("host", new URL(this.url).hostname);
|
|
87
|
-
headers.delete("cf-connecting-ip");
|
|
88
|
-
|
|
89
|
-
const res = await fetch(url, {
|
|
90
|
-
method: req.method,
|
|
91
|
-
headers,
|
|
92
|
-
body: req.body,
|
|
93
|
-
redirect: "manual",
|
|
94
|
-
});
|
|
70
|
+
const res = await fetch(url, {
|
|
71
|
+
method: req.method,
|
|
72
|
+
headers: requestHeaders,
|
|
73
|
+
body: req.body,
|
|
74
|
+
redirect: "manual",
|
|
75
|
+
});
|
|
95
76
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
77
|
+
// Remove headers that are not supported by miniflare
|
|
78
|
+
const responseHeaders = new Headers(res.headers);
|
|
79
|
+
responseHeaders.delete("transfer-encoding");
|
|
80
|
+
responseHeaders.delete("content-encoding");
|
|
100
81
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
82
|
+
return new Response(res.body, {
|
|
83
|
+
status: res.status,
|
|
84
|
+
headers: responseHeaders,
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
await server.listen();
|
|
89
|
+
return {
|
|
90
|
+
server,
|
|
91
|
+
bindings: input.bindings,
|
|
92
|
+
connectionString: new URL(server.url) as RemoteProxyConnectionString,
|
|
93
|
+
};
|
|
106
94
|
}
|
|
107
95
|
|
|
108
96
|
async function createWorkersPreviewToken(
|
package/src/cloudflare/oauth.ts
CHANGED
|
@@ -7,7 +7,7 @@ import os from "node:os";
|
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import open from "open";
|
|
9
9
|
import xdgAppPaths from "xdg-app-paths";
|
|
10
|
-
import { HTTPServer } from "../util/http
|
|
10
|
+
import { HTTPServer } from "../util/http.ts";
|
|
11
11
|
import { memoize } from "../util/memoize.ts";
|
|
12
12
|
import {
|
|
13
13
|
detached,
|
|
@@ -145,7 +145,6 @@ export const wranglerLogin = (
|
|
|
145
145
|
return Response.redirect(`${ALCHEMY_BASE_URL}/auth/${type}`);
|
|
146
146
|
};
|
|
147
147
|
const server = new HTTPServer({
|
|
148
|
-
port: 8976,
|
|
149
148
|
fetch: async (request) => {
|
|
150
149
|
const url = new URL(request.url);
|
|
151
150
|
if (url.pathname !== "/oauth/callback") {
|
|
@@ -190,6 +189,7 @@ export const wranglerLogin = (
|
|
|
190
189
|
return renderResponse("success");
|
|
191
190
|
},
|
|
192
191
|
});
|
|
192
|
+
server.listen(8976);
|
|
193
193
|
const timeout = setTimeout(
|
|
194
194
|
() => {
|
|
195
195
|
err(
|
|
@@ -204,7 +204,7 @@ export const wranglerLogin = (
|
|
|
204
204
|
);
|
|
205
205
|
return ensure(result, () => {
|
|
206
206
|
clearTimeout(timeout);
|
|
207
|
-
void server.
|
|
207
|
+
void server.close();
|
|
208
208
|
});
|
|
209
209
|
};
|
|
210
210
|
|
|
@@ -5,12 +5,8 @@ import { Scope } from "../scope.ts";
|
|
|
5
5
|
import { detectPackageManager } from "../util/detect-package-manager.ts";
|
|
6
6
|
import { Assets } from "./assets.ts";
|
|
7
7
|
import type { Bindings } from "./bindings.ts";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
Worker,
|
|
11
|
-
type AssetsConfig,
|
|
12
|
-
type WorkerProps,
|
|
13
|
-
} from "./worker.ts";
|
|
8
|
+
import { DEFAULT_COMPATIBILITY_DATE } from "./compatibility-date.gen.ts";
|
|
9
|
+
import { Worker, type AssetsConfig, type WorkerProps } from "./worker.ts";
|
|
14
10
|
import { WranglerJson, type WranglerJsonSpec } from "./wrangler.json.ts";
|
|
15
11
|
|
|
16
12
|
export interface WebsiteProps<B extends Bindings>
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { spawn } from "node:child_process";
|
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import { isDeepStrictEqual } from "node:util";
|
|
6
6
|
import path from "pathe";
|
|
7
|
-
import { BUILD_DATE } from "../build-date.ts";
|
|
8
7
|
import type { Context } from "../context.ts";
|
|
9
8
|
import type { BundleProps } from "../esbuild/bundle.ts";
|
|
10
9
|
import { InnerResourceScope, Resource, ResourceKind } from "../resource.ts";
|
|
@@ -43,6 +42,7 @@ import {
|
|
|
43
42
|
normalizeWorkerBundle,
|
|
44
43
|
} from "./bundle/index.ts";
|
|
45
44
|
import { wrap } from "./bundle/normalize.ts";
|
|
45
|
+
import { DEFAULT_COMPATIBILITY_DATE } from "./compatibility-date.gen.ts";
|
|
46
46
|
import {
|
|
47
47
|
type CompatibilityPreset,
|
|
48
48
|
unionCompatibilityFlags,
|
|
@@ -212,7 +212,7 @@ export interface BaseWorkerProps<
|
|
|
212
212
|
|
|
213
213
|
/**
|
|
214
214
|
* The compatibility date for the worker
|
|
215
|
-
* @default
|
|
215
|
+
* @default DEFAULT_WORKER_COMPATIBILITY_DATE - automatically pinned to the latest Workers release
|
|
216
216
|
*/
|
|
217
217
|
compatibilityDate?: string;
|
|
218
218
|
|
|
@@ -1013,8 +1013,6 @@ export function Worker<const B extends Bindings>(
|
|
|
1013
1013
|
});
|
|
1014
1014
|
}
|
|
1015
1015
|
|
|
1016
|
-
export const DEFAULT_COMPATIBILITY_DATE = BUILD_DATE;
|
|
1017
|
-
|
|
1018
1016
|
export const _Worker = Resource(
|
|
1019
1017
|
"cloudflare::Worker",
|
|
1020
1018
|
{
|
|
@@ -1102,6 +1100,7 @@ export const _Worker = Resource(
|
|
|
1102
1100
|
bindings: props.bindings,
|
|
1103
1101
|
bundle,
|
|
1104
1102
|
port: props.dev?.port,
|
|
1103
|
+
assets: props.assets,
|
|
1105
1104
|
});
|
|
1106
1105
|
}
|
|
1107
1106
|
|
|
@@ -1671,6 +1670,7 @@ async function createMiniflare(props: {
|
|
|
1671
1670
|
bindings: Bindings | undefined;
|
|
1672
1671
|
port: number | undefined;
|
|
1673
1672
|
bundle: WorkerBundleProvider;
|
|
1673
|
+
assets: AssetsConfig | undefined;
|
|
1674
1674
|
}) {
|
|
1675
1675
|
const sharedOptions: Omit<MiniflareWorkerOptions, "bundle"> = {
|
|
1676
1676
|
name: props.workerName,
|
|
@@ -1678,6 +1678,7 @@ async function createMiniflare(props: {
|
|
|
1678
1678
|
compatibilityFlags: props.compatibilityFlags,
|
|
1679
1679
|
bindings: props.bindings,
|
|
1680
1680
|
port: props.port,
|
|
1681
|
+
assets: props.assets,
|
|
1681
1682
|
};
|
|
1682
1683
|
|
|
1683
1684
|
const startPromise = new DeferredPromise<string>();
|
|
@@ -1837,14 +1838,6 @@ async function createDevCommand(props: {
|
|
|
1837
1838
|
}
|
|
1838
1839
|
}
|
|
1839
1840
|
const command = props.command.split(" ");
|
|
1840
|
-
console.log({
|
|
1841
|
-
command,
|
|
1842
|
-
args: command.slice(1),
|
|
1843
|
-
cwd: props.cwd,
|
|
1844
|
-
env: {
|
|
1845
|
-
...props.env,
|
|
1846
|
-
},
|
|
1847
|
-
});
|
|
1848
1841
|
const proc = spawn(command[0], command.slice(1), {
|
|
1849
1842
|
cwd: props.cwd,
|
|
1850
1843
|
env: {
|
package/src/scope.ts
CHANGED
|
@@ -125,6 +125,7 @@ export class Scope {
|
|
|
125
125
|
public readonly dataMutex: AsyncMutex;
|
|
126
126
|
|
|
127
127
|
private isErrored = false;
|
|
128
|
+
private isSkipped = false;
|
|
128
129
|
private finalized = false;
|
|
129
130
|
private startedAt = performance.now();
|
|
130
131
|
|
|
@@ -194,6 +195,17 @@ export class Scope {
|
|
|
194
195
|
this.dataMutex = new AsyncMutex();
|
|
195
196
|
}
|
|
196
197
|
|
|
198
|
+
/**
|
|
199
|
+
* @internal
|
|
200
|
+
*/
|
|
201
|
+
public clear() {
|
|
202
|
+
for (const child of this.children.values()) {
|
|
203
|
+
child.clear();
|
|
204
|
+
}
|
|
205
|
+
this.resources.clear();
|
|
206
|
+
this.children.clear();
|
|
207
|
+
}
|
|
208
|
+
|
|
197
209
|
public get root(): Scope {
|
|
198
210
|
let root: Scope = this;
|
|
199
211
|
while (root.parent) {
|
|
@@ -239,6 +251,10 @@ export class Scope {
|
|
|
239
251
|
this.isErrored = true;
|
|
240
252
|
}
|
|
241
253
|
|
|
254
|
+
public skip() {
|
|
255
|
+
this.isSkipped = true;
|
|
256
|
+
}
|
|
257
|
+
|
|
242
258
|
public async init() {
|
|
243
259
|
await Promise.all([
|
|
244
260
|
this.state.init?.(),
|
|
@@ -378,7 +394,7 @@ export class Scope {
|
|
|
378
394
|
this.finalized = true;
|
|
379
395
|
// trigger and await all deferred promises
|
|
380
396
|
await Promise.all(this.deferred.map((fn) => fn()));
|
|
381
|
-
if (!this.isErrored) {
|
|
397
|
+
if (!this.isErrored && !this.isSkipped) {
|
|
382
398
|
// TODO: need to detect if it is in error
|
|
383
399
|
const resourceIds = await this.state.list();
|
|
384
400
|
const aliveIds = new Set(this.resources.keys());
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { alchemy } from "../alchemy.ts";
|
|
2
|
-
import { BUILD_DATE } from "../build-date.ts";
|
|
3
2
|
import type { CloudflareApiOptions } from "../cloudflare/api.ts";
|
|
4
3
|
import { createCloudflareApi } from "../cloudflare/api.ts";
|
|
5
4
|
import { getInternalWorkerBundle } from "../cloudflare/bundle/internal-worker-bundle.ts";
|
|
5
|
+
import { DEFAULT_COMPATIBILITY_DATE } from "../cloudflare/compatibility-date.gen.ts";
|
|
6
6
|
import { DurableObjectNamespace } from "../cloudflare/durable-object-namespace.ts";
|
|
7
7
|
import { getWorkerSettings } from "../cloudflare/worker-metadata.ts";
|
|
8
8
|
import {
|
|
@@ -109,7 +109,7 @@ const provision = memoize(async (options: CloudflareStateStoreOptions) => {
|
|
|
109
109
|
);
|
|
110
110
|
await putWorker(api, {
|
|
111
111
|
workerName: scriptName,
|
|
112
|
-
compatibilityDate:
|
|
112
|
+
compatibilityDate: DEFAULT_COMPATIBILITY_DATE,
|
|
113
113
|
format: "esm",
|
|
114
114
|
scriptBundle: {
|
|
115
115
|
entrypoint: bundle.file.name,
|
package/src/util/http.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
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
|
+
httpServer: http.Server;
|
|
7
|
+
|
|
8
|
+
constructor(options: {
|
|
9
|
+
fetch: (request: Request) => Promise<Response>;
|
|
10
|
+
}) {
|
|
11
|
+
this.httpServer = http.createServer();
|
|
12
|
+
this.httpServer.on("request", async (req, res) => {
|
|
13
|
+
const response = await options.fetch(toWebRequest(req));
|
|
14
|
+
await writeNodeResponse(res, response);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
listen(port?: number) {
|
|
19
|
+
return new Promise<this>((resolve, reject) => {
|
|
20
|
+
this.httpServer.on("listening", () => {
|
|
21
|
+
resolve(this);
|
|
22
|
+
});
|
|
23
|
+
this.httpServer.on("error", (error) => {
|
|
24
|
+
reject(error);
|
|
25
|
+
});
|
|
26
|
+
this.httpServer.listen(port);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get url() {
|
|
31
|
+
const address = this.httpServer.address() as AddressInfo | null;
|
|
32
|
+
if (!address) {
|
|
33
|
+
throw new Error("Server is not listening");
|
|
34
|
+
}
|
|
35
|
+
const hostname = address.address === "::" ? "localhost" : address.address;
|
|
36
|
+
return `http://${hostname}:${address.port}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
close() {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
this.httpServer.close((err) => {
|
|
42
|
+
if (err) {
|
|
43
|
+
reject(err);
|
|
44
|
+
} else {
|
|
45
|
+
resolve(undefined);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function toWebRequest(
|
|
53
|
+
req: http.IncomingMessage,
|
|
54
|
+
host?: string,
|
|
55
|
+
): Request {
|
|
56
|
+
const method = req.method ?? "GET";
|
|
57
|
+
const url = new URL(req.url ?? "/", `http://${host ?? req.headers.host}`);
|
|
58
|
+
const body =
|
|
59
|
+
["GET", "HEAD", "OPTIONS"].includes(method) || !req.readable
|
|
60
|
+
? undefined
|
|
61
|
+
: Readable.toWeb(req);
|
|
62
|
+
return new Request(url.toString(), {
|
|
63
|
+
method,
|
|
64
|
+
headers: req.headers as Record<string, string>,
|
|
65
|
+
body: body as unknown as BodyInit,
|
|
66
|
+
// @ts-expect-error - caused by @cloudflare/workers-types
|
|
67
|
+
duplex: body ? "half" : undefined,
|
|
68
|
+
redirect: "manual",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function writeNodeResponse(
|
|
73
|
+
res: http.ServerResponse,
|
|
74
|
+
response: Response,
|
|
75
|
+
) {
|
|
76
|
+
res.statusCode = response.status;
|
|
77
|
+
response.headers.forEach((value, key) => {
|
|
78
|
+
res.setHeader(key, value);
|
|
79
|
+
});
|
|
80
|
+
if (response.body) {
|
|
81
|
+
await response.body.pipeTo(
|
|
82
|
+
new WritableStream({
|
|
83
|
+
write(chunk) {
|
|
84
|
+
res.write(chunk);
|
|
85
|
+
},
|
|
86
|
+
close() {
|
|
87
|
+
res.end();
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
res.end();
|
|
93
|
+
}
|
|
94
|
+
}
|
package/lib/build-date.d.ts
DELETED
package/lib/build-date.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-date.d.ts","sourceRoot":"","sources":["../src/build-date.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,eAAO,MAAM,UAAU,eAAe,CAAC"}
|
package/lib/build-date.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// This file is auto-generated during build
|
|
2
|
-
// Do not edit manually
|
|
3
|
-
/**
|
|
4
|
-
* The build date used as the default worker compatibility date.
|
|
5
|
-
* This is set to the date when the package was built.
|
|
6
|
-
*/
|
|
7
|
-
export const BUILD_DATE = "2025-07-16";
|
|
8
|
-
//# sourceMappingURL=build-date.js.map
|
package/lib/build-date.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-date.js","sourceRoot":"","sources":["../src/build-date.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,uBAAuB;AAEvB;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,YAAY,CAAC"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import http from "node:http";
|
|
2
|
-
export declare class HTTPServer {
|
|
3
|
-
server: http.Server;
|
|
4
|
-
ready: Promise<void>;
|
|
5
|
-
constructor(options: {
|
|
6
|
-
port?: number;
|
|
7
|
-
fetch: (request: Request) => Promise<Response>;
|
|
8
|
-
});
|
|
9
|
-
get port(): number;
|
|
10
|
-
get hostname(): string;
|
|
11
|
-
get url(): string;
|
|
12
|
-
stop(): Promise<unknown>;
|
|
13
|
-
}
|
|
14
|
-
//# sourceMappingURL=http-server.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../../src/util/http-server.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAK7B,qBAAa,UAAU;IACrB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;gBAET,OAAO,EAAE;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;KAChD;IAWD,IAAI,IAAI,WAEP;IAED,IAAI,QAAQ,WAMX;IAED,IAAI,GAAG,WAEN;IAED,IAAI;CAWL"}
|