alchemy 0.13.0 → 0.14.1
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/apply.d.ts.map +1 -1
- package/lib/apply.js +0 -1
- package/lib/apply.js.map +1 -1
- package/lib/aws/function.js +9 -1
- package/lib/aws/function.js.map +1 -1
- package/lib/cloudflare/api.d.ts +5 -5
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +51 -10
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/auth.d.ts +24 -55
- package/lib/cloudflare/auth.d.ts.map +1 -1
- package/lib/cloudflare/auth.js +15 -72
- package/lib/cloudflare/auth.js.map +1 -1
- package/lib/cloudflare/bundle/alias-plugin.d.ts +6 -0
- package/lib/cloudflare/bundle/alias-plugin.d.ts.map +1 -0
- package/lib/cloudflare/bundle/alias-plugin.js +36 -0
- package/lib/cloudflare/bundle/alias-plugin.js.map +1 -0
- package/lib/cloudflare/bundle/build-failures.d.ts +12 -0
- package/lib/cloudflare/bundle/build-failures.d.ts.map +1 -0
- package/lib/cloudflare/bundle/build-failures.js +45 -0
- package/lib/cloudflare/bundle/build-failures.js.map +1 -0
- package/lib/cloudflare/bundle/bundle-worker.d.ts +7 -0
- package/lib/cloudflare/bundle/bundle-worker.d.ts.map +1 -0
- package/lib/cloudflare/bundle/bundle-worker.js +69 -0
- package/lib/cloudflare/bundle/bundle-worker.js.map +1 -0
- package/lib/cloudflare/bundle/external.d.ts +1 -0
- package/lib/cloudflare/bundle/external.d.ts.map +1 -1
- package/lib/cloudflare/bundle/external.js +8 -1
- package/lib/cloudflare/bundle/external.js.map +1 -1
- package/lib/cloudflare/bundle/local-dev-cloudflare-shim.d.ts +1 -1
- package/lib/cloudflare/bundle/local-dev-cloudflare-shim.d.ts.map +1 -1
- package/lib/cloudflare/bundle/local-dev-cloudflare-shim.js +6 -6
- package/lib/cloudflare/bundle/local-dev-cloudflare-shim.js.map +1 -1
- package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts +17 -0
- package/lib/cloudflare/bundle/nodejs-compat-mode.d.ts.map +1 -0
- package/lib/cloudflare/bundle/nodejs-compat-mode.js +27 -0
- package/lib/cloudflare/bundle/nodejs-compat-mode.js.map +1 -0
- package/lib/cloudflare/bundle/nodejs-compat.d.ts +11 -0
- package/lib/cloudflare/bundle/nodejs-compat.d.ts.map +1 -0
- package/lib/cloudflare/bundle/{hybrid-nodejs-compat.js → nodejs-compat.js} +6 -5
- package/lib/cloudflare/bundle/nodejs-compat.js.map +1 -0
- package/lib/cloudflare/queue-consumer.d.ts.map +1 -1
- package/lib/cloudflare/queue-consumer.js +0 -4
- package/lib/cloudflare/queue-consumer.js.map +1 -1
- package/lib/cloudflare/user.d.ts +51 -0
- package/lib/cloudflare/user.d.ts.map +1 -0
- package/lib/cloudflare/user.js +62 -0
- package/lib/cloudflare/user.js.map +1 -0
- package/lib/cloudflare/worker.d.ts +18 -3
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +7 -44
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/esbuild/bundle.d.ts.map +1 -1
- package/lib/esbuild/bundle.js +0 -1
- package/lib/esbuild/bundle.js.map +1 -1
- package/lib/test/bun.d.ts.map +1 -1
- package/lib/test/bun.js +2 -8
- package/lib/test/bun.js.map +1 -1
- package/package.json +4 -1
- package/src/apply.ts +0 -1
- package/src/aws/function.ts +11 -2
- package/src/cloudflare/api.ts +63 -14
- package/src/cloudflare/auth.ts +43 -158
- package/src/cloudflare/bundle/alias-plugin.ts +48 -0
- package/src/cloudflare/bundle/build-failures.ts +55 -0
- package/src/cloudflare/bundle/bundle-worker.ts +89 -0
- package/src/cloudflare/bundle/external.ts +9 -1
- package/src/cloudflare/bundle/local-dev-cloudflare-shim.ts +7 -6
- package/src/cloudflare/bundle/nodejs-compat-mode.ts +49 -0
- package/src/cloudflare/bundle/{hybrid-nodejs-compat.ts → nodejs-compat.ts} +6 -5
- package/src/cloudflare/queue-consumer.ts +1 -7
- package/src/cloudflare/user.ts +142 -0
- package/src/cloudflare/worker.ts +26 -51
- package/src/esbuild/bundle.ts +0 -1
- package/src/test/bun.ts +2 -7
- package/lib/cloudflare/account-id.d.ts +0 -6
- package/lib/cloudflare/account-id.d.ts.map +0 -1
- package/lib/cloudflare/account-id.js +0 -6
- package/lib/cloudflare/account-id.js.map +0 -1
- package/lib/cloudflare/bundle/hybrid-nodejs-compat.d.ts +0 -8
- package/lib/cloudflare/bundle/hybrid-nodejs-compat.d.ts.map +0 -1
- package/lib/cloudflare/bundle/hybrid-nodejs-compat.js.map +0 -1
- package/src/cloudflare/account-id.ts +0 -12
package/src/cloudflare/auth.ts
CHANGED
|
@@ -2,201 +2,86 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import type { Secret } from "../secret.js";
|
|
5
|
-
import type { CloudflareAccountId } from "./account-id.js";
|
|
6
|
-
import { handleApiError } from "./api-error.js";
|
|
7
5
|
/**
|
|
8
6
|
* Authentication options for Cloudflare API
|
|
9
7
|
*/
|
|
10
|
-
export
|
|
8
|
+
export type CloudflareAuthOptions =
|
|
9
|
+
| CloudflareApiKeyAuthOptions
|
|
10
|
+
| CloudflareApiTokenAuthOptions;
|
|
11
|
+
|
|
12
|
+
export type CloudflareApiKeyAuthOptions = {
|
|
11
13
|
/**
|
|
12
14
|
* API Key to use with API Key
|
|
13
15
|
*/
|
|
14
|
-
apiKey
|
|
15
|
-
|
|
16
|
+
apiKey: Secret;
|
|
16
17
|
/**
|
|
17
|
-
*
|
|
18
|
+
* Email to use with API Key
|
|
19
|
+
* If not provided, will attempt to discover from Cloudflare API
|
|
18
20
|
*/
|
|
19
|
-
|
|
21
|
+
email: string;
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
|
-
*
|
|
23
|
-
* If not provided, will attempt to discover from Cloudflare API
|
|
24
|
+
* API Token to use with API Key
|
|
24
25
|
*/
|
|
25
|
-
|
|
26
|
-
}
|
|
26
|
+
apiToken?: undefined;
|
|
27
|
+
};
|
|
27
28
|
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
settings: {
|
|
33
|
-
enforce_twofactor: boolean;
|
|
34
|
-
api_access_enabled: null;
|
|
35
|
-
access_approval_expiry: null;
|
|
36
|
-
abuse_contact_email: null;
|
|
37
|
-
};
|
|
38
|
-
legacy_flags: {
|
|
39
|
-
enterprise_zone_quota: {
|
|
40
|
-
maximum: number;
|
|
41
|
-
current: number;
|
|
42
|
-
available: number;
|
|
43
|
-
};
|
|
44
|
-
};
|
|
29
|
+
export function isCloudflareApiKeyAuthOptions(
|
|
30
|
+
options: CloudflareAuthOptions | undefined,
|
|
31
|
+
): options is CloudflareApiKeyAuthOptions {
|
|
32
|
+
return options !== undefined && options.apiKey !== undefined;
|
|
45
33
|
}
|
|
46
34
|
|
|
47
|
-
export
|
|
35
|
+
export type CloudflareApiTokenAuthOptions = {
|
|
36
|
+
/**
|
|
37
|
+
* API Token to use with API Key
|
|
38
|
+
*/
|
|
48
39
|
apiToken?: Secret;
|
|
49
|
-
apiKey?: Secret;
|
|
50
|
-
email: string;
|
|
51
|
-
username: string;
|
|
52
|
-
accounts: CloudflareAccount[];
|
|
53
|
-
organizations: CloudflareOrganization[];
|
|
54
|
-
tokenPermissions: string[] | undefined;
|
|
55
|
-
first_name: string | null;
|
|
56
|
-
last_name: string | null;
|
|
57
|
-
telephone: string | null;
|
|
58
|
-
country: string | null;
|
|
59
|
-
zipcode: string | null;
|
|
60
|
-
two_factor_authentication_enabled: boolean;
|
|
61
|
-
two_factor_authentication_locked: boolean;
|
|
62
|
-
has_pro_zones: boolean;
|
|
63
|
-
has_business_zones: boolean;
|
|
64
|
-
has_enterprise_zones: boolean;
|
|
65
|
-
suspended: boolean;
|
|
66
|
-
betas: string[];
|
|
67
|
-
}
|
|
68
40
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
permissions: string[];
|
|
74
|
-
roles: string[];
|
|
75
|
-
}
|
|
41
|
+
/**
|
|
42
|
+
* API Key to use with API Token
|
|
43
|
+
*/
|
|
44
|
+
apiKey?: undefined;
|
|
76
45
|
|
|
77
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Email to use with API Token
|
|
48
|
+
*/
|
|
49
|
+
email?: undefined;
|
|
50
|
+
};
|
|
78
51
|
|
|
79
|
-
export
|
|
80
|
-
options: CloudflareAuthOptions,
|
|
81
|
-
):
|
|
82
|
-
|
|
83
|
-
apiKey: options.apiKey?.unencrypted,
|
|
84
|
-
apiToken: options.apiToken?.unencrypted,
|
|
85
|
-
email: options.email,
|
|
86
|
-
});
|
|
87
|
-
if (userInfoCache[cacheKey]) {
|
|
88
|
-
return userInfoCache[cacheKey];
|
|
89
|
-
}
|
|
90
|
-
const headers = await getCloudflareAuthHeaders(options);
|
|
91
|
-
const user = await fetch("https://api.cloudflare.com/client/v4/user", {
|
|
92
|
-
headers,
|
|
93
|
-
});
|
|
94
|
-
if (!user.ok) {
|
|
95
|
-
if (user.status === 403) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
"Cloudflare authentication required. Did you forget to login with `wrangler login` or set CLOUDFLARE_API_TOKEN, CLOUDFLARE_API_KEY, or CLOUDFLARE_EMAIL, or CLOUDFLARE_API_KEY?",
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
await handleApiError(user, "getting", "user", "user");
|
|
101
|
-
}
|
|
102
|
-
const accounts = await fetch(
|
|
103
|
-
"https://api.cloudflare.com/client/v4/accounts",
|
|
104
|
-
{
|
|
105
|
-
headers,
|
|
106
|
-
},
|
|
107
|
-
);
|
|
108
|
-
const userInfo: CloudflareUserInfo = {
|
|
109
|
-
...((await user.json()) as any).result,
|
|
110
|
-
accounts: ((await accounts.json()) as any).result,
|
|
111
|
-
};
|
|
112
|
-
userInfoCache[cacheKey] = userInfo;
|
|
113
|
-
return userInfo;
|
|
52
|
+
export function isCloudflareApiTokenAuthOptions(
|
|
53
|
+
options: CloudflareAuthOptions | undefined,
|
|
54
|
+
): options is CloudflareApiTokenAuthOptions {
|
|
55
|
+
return options !== undefined && options.apiToken !== undefined;
|
|
114
56
|
}
|
|
115
57
|
|
|
116
58
|
export async function getCloudflareAuthHeaders(
|
|
117
|
-
options: CloudflareAuthOptions
|
|
59
|
+
options: CloudflareAuthOptions | undefined,
|
|
118
60
|
): Promise<Record<string, string>> {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
options.apiToken?.unencrypted ?? process.env.CLOUDFLARE_API_TOKEN;
|
|
122
|
-
if (apiToken) {
|
|
61
|
+
if (isCloudflareApiKeyAuthOptions(options)) {
|
|
62
|
+
// Global API Key
|
|
123
63
|
return {
|
|
124
|
-
|
|
64
|
+
"X-Auth-Key": options.apiKey.unencrypted,
|
|
65
|
+
"X-Auth-Email": options.email,
|
|
125
66
|
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Check for API Key
|
|
129
|
-
const apiKey = options.apiKey?.unencrypted ?? process.env.CLOUDFLARE_API_KEY;
|
|
130
|
-
if (apiKey) {
|
|
67
|
+
} else if (isCloudflareApiTokenAuthOptions(options)) {
|
|
68
|
+
// API Token
|
|
131
69
|
return {
|
|
132
|
-
|
|
133
|
-
"X-Auth-Email":
|
|
134
|
-
options.email ??
|
|
135
|
-
process.env.CLOUDFLARE_EMAIL ??
|
|
136
|
-
(await getUserEmailFromApiKey(apiKey)),
|
|
70
|
+
Authorization: `Bearer ${options.apiToken?.unencrypted}`,
|
|
137
71
|
};
|
|
138
72
|
}
|
|
139
|
-
|
|
73
|
+
// Wrangler OAuth Token
|
|
140
74
|
const authConfig = await getRefreshedAuthConfig();
|
|
141
75
|
if (authConfig.oauth_token) {
|
|
142
76
|
return {
|
|
143
77
|
Authorization: `Bearer ${authConfig.oauth_token}`,
|
|
144
78
|
};
|
|
145
79
|
}
|
|
146
|
-
|
|
147
80
|
throw new Error(
|
|
148
|
-
"Cloudflare authentication required. Did you forget to login with `wrangler login` or set CLOUDFLARE_API_TOKEN, CLOUDFLARE_API_KEY
|
|
81
|
+
"Cloudflare authentication required. Did you forget to login with `wrangler login` or set CLOUDFLARE_API_TOKEN, CLOUDFLARE_API_KEY?",
|
|
149
82
|
);
|
|
150
83
|
}
|
|
151
84
|
|
|
152
|
-
const emailCache: Record<string, string> = {};
|
|
153
|
-
|
|
154
|
-
async function getUserEmailFromApiKey(apiKey: string): Promise<string> {
|
|
155
|
-
if (emailCache[apiKey]) {
|
|
156
|
-
return emailCache[apiKey];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
const baseUrl = "https://api.cloudflare.com/client/v4";
|
|
161
|
-
|
|
162
|
-
// Call the /user endpoint to get user information
|
|
163
|
-
const response = await fetch(`${baseUrl}/user`, {
|
|
164
|
-
headers: {
|
|
165
|
-
"Content-Type": "application/json",
|
|
166
|
-
"X-Auth-Key": apiKey,
|
|
167
|
-
},
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
if (!response.ok) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`Failed to get user information: ${response.status} ${response.statusText}`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const data = (await response.json()) as {
|
|
177
|
-
success: boolean;
|
|
178
|
-
result: {
|
|
179
|
-
id: string;
|
|
180
|
-
email: string;
|
|
181
|
-
name: string;
|
|
182
|
-
[key: string]: any;
|
|
183
|
-
};
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
if (!data.success || !data.result || !data.result.email) {
|
|
187
|
-
throw new Error("Cloudflare API did not return valid user information");
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
emailCache[apiKey] = data.result.email;
|
|
191
|
-
return data.result.email;
|
|
192
|
-
} catch (error) {
|
|
193
|
-
console.error("Error retrieving email from Cloudflare API:", error);
|
|
194
|
-
throw new Error(
|
|
195
|
-
"Failed to automatically discover email for API Key authentication",
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
85
|
async function refreshAuthToken(
|
|
201
86
|
options: WranglerConfig,
|
|
202
87
|
): Promise<WranglerConfig> {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Plugin } from "esbuild";
|
|
2
|
+
|
|
3
|
+
export const createAliasPlugin = ({
|
|
4
|
+
alias,
|
|
5
|
+
projectRoot,
|
|
6
|
+
}: {
|
|
7
|
+
alias: Record<string, string>;
|
|
8
|
+
projectRoot: string;
|
|
9
|
+
}): Plugin => ({
|
|
10
|
+
name: "alias",
|
|
11
|
+
setup(build) {
|
|
12
|
+
if (!alias) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// filter the hook calls to only those that match the alias keys
|
|
17
|
+
// this should avoid slowing down builds which don't use aliasing
|
|
18
|
+
const filter = new RegExp(
|
|
19
|
+
Object.keys(alias)
|
|
20
|
+
.map((key) => escapeRegex(key))
|
|
21
|
+
.join("|"),
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// reimplement module aliasing as an esbuild plugin onResolve hook
|
|
25
|
+
build.onResolve({ filter }, (args) => {
|
|
26
|
+
const aliasPath = alias[args.path];
|
|
27
|
+
if (aliasPath) {
|
|
28
|
+
return {
|
|
29
|
+
// resolve with node resolution
|
|
30
|
+
path: require.resolve(aliasPath, {
|
|
31
|
+
// From the esbuild alias docs: "Note that when an import path is substituted using an alias, the resulting import path is resolved in the working directory instead of in the directory containing the source file with the import path."
|
|
32
|
+
// https://esbuild.github.io/api/#alias:~:text=Note%20that%20when%20an%20import%20path%20is%20substituted%20using%20an%20alias%2C%20the%20resulting%20import%20path%20is%20resolved%20in%20the%20working%20directory%20instead%20of%20in%20the%20directory%20containing%20the%20source%20file%20with%20the%20import%20path.
|
|
33
|
+
paths: [projectRoot],
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Taken from https://stackoverflow.com/a/3561711
|
|
42
|
+
// which is everything from the tc39 proposal, plus the following two characters: ^/
|
|
43
|
+
// It's also everything included in the URLPattern escape (https://wicg.github.io/urlpattern/#escape-a-regexp-string), plus the following: -
|
|
44
|
+
// As the answer says, there's no downside to escaping these extra characters, so better safe than sorry
|
|
45
|
+
const ESCAPE_REGEX_CHARACTERS = /[-/\\^$*+?.()|[\]{}]/g;
|
|
46
|
+
const escapeRegex = (str: string) => {
|
|
47
|
+
return str.replace(ESCAPE_REGEX_CHARACTERS, "\\$&");
|
|
48
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type * as esbuild from "esbuild";
|
|
2
|
+
import type { NodeJSCompatMode } from "miniflare";
|
|
3
|
+
import { builtinModules } from "node:module";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* RegExp matching against esbuild's error text when it is unable to resolve
|
|
7
|
+
* a Node built-in module. If we detect this when node_compat is disabled,
|
|
8
|
+
* we'll rewrite the error to suggest enabling it.
|
|
9
|
+
*/
|
|
10
|
+
const nodeBuiltinResolveErrorText = new RegExp(
|
|
11
|
+
'^Could not resolve "(' +
|
|
12
|
+
builtinModules.join("|") +
|
|
13
|
+
"|" +
|
|
14
|
+
builtinModules.map((module) => `node:${module}`).join("|") +
|
|
15
|
+
')"$',
|
|
16
|
+
);
|
|
17
|
+
/**
|
|
18
|
+
* Rewrites esbuild BuildFailures for failing to resolve Node built-in modules
|
|
19
|
+
* to suggest enabling Node compat as opposed to `platform: "node"`.
|
|
20
|
+
*/
|
|
21
|
+
export function rewriteNodeCompatBuildFailure(
|
|
22
|
+
errors: esbuild.Message[],
|
|
23
|
+
compatMode: NodeJSCompatMode = null,
|
|
24
|
+
) {
|
|
25
|
+
for (const error of errors) {
|
|
26
|
+
const match = nodeBuiltinResolveErrorText.exec(error.text);
|
|
27
|
+
if (match !== null) {
|
|
28
|
+
let text = `The package "${match[1]}" wasn't found on the file system but is built into node.\n`;
|
|
29
|
+
|
|
30
|
+
if (compatMode === null || compatMode === "als") {
|
|
31
|
+
text += `- Add the "nodejs_compat" compatibility flag to your project.\n`;
|
|
32
|
+
} else if (compatMode === "v1" && !match[1].startsWith("node:")) {
|
|
33
|
+
text += `- Make sure to prefix the module name with "node:" or update your compatibility_date to 2024-09-23 or later.\n`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
error.notes = [
|
|
37
|
+
{
|
|
38
|
+
location: null,
|
|
39
|
+
text,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Returns true if the passed value looks like an esbuild BuildFailure object
|
|
47
|
+
*/
|
|
48
|
+
export function isBuildFailure(err: unknown): err is esbuild.BuildFailure {
|
|
49
|
+
return (
|
|
50
|
+
typeof err === "object" &&
|
|
51
|
+
err !== null &&
|
|
52
|
+
"errors" in err &&
|
|
53
|
+
"warnings" in err
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { Bundle } from "../../esbuild/bundle.js";
|
|
3
|
+
import type { Bindings } from "../bindings.js";
|
|
4
|
+
import type { WorkerProps } from "../worker.js";
|
|
5
|
+
import { createAliasPlugin } from "./alias-plugin.js";
|
|
6
|
+
import {
|
|
7
|
+
isBuildFailure,
|
|
8
|
+
rewriteNodeCompatBuildFailure,
|
|
9
|
+
} from "./build-failures.js";
|
|
10
|
+
import { external, external_als } from "./external.js";
|
|
11
|
+
import { getNodeJSCompatMode } from "./nodejs-compat-mode.js";
|
|
12
|
+
import { nodeJsCompatPlugin } from "./nodejs-compat.js";
|
|
13
|
+
|
|
14
|
+
export async function bundleWorkerScript<B extends Bindings>(
|
|
15
|
+
props: WorkerProps<B> & {
|
|
16
|
+
compatibilityDate: string;
|
|
17
|
+
compatibilityFlags: string[];
|
|
18
|
+
},
|
|
19
|
+
) {
|
|
20
|
+
const projectRoot = props.projectRoot ?? process.cwd();
|
|
21
|
+
|
|
22
|
+
const nodeJsCompatMode = getNodeJSCompatMode(
|
|
23
|
+
props.compatibilityDate,
|
|
24
|
+
props.compatibilityFlags,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (nodeJsCompatMode === "v1") {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"You must set your compatibilty date >= 2024-09-23 when using 'nodejs_compat' compatibility flag",
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const bundle = await Bundle("bundle", {
|
|
35
|
+
entryPoint: props.entrypoint!,
|
|
36
|
+
format: props.format === "cjs" ? "cjs" : "esm", // Use the specified format or default to ESM
|
|
37
|
+
target: "esnext",
|
|
38
|
+
platform: "node",
|
|
39
|
+
minify: false,
|
|
40
|
+
...(props.bundle || {}),
|
|
41
|
+
conditions: ["workerd", "worker", "browser"],
|
|
42
|
+
options: {
|
|
43
|
+
absWorkingDir: projectRoot,
|
|
44
|
+
...(props.bundle?.options || {}),
|
|
45
|
+
keepNames: true, // Important for Durable Object classes
|
|
46
|
+
loader: {
|
|
47
|
+
".sql": "text",
|
|
48
|
+
".json": "json",
|
|
49
|
+
},
|
|
50
|
+
plugins: [
|
|
51
|
+
...(nodeJsCompatMode === "v2" ? [await nodeJsCompatPlugin()] : []),
|
|
52
|
+
...(props.bundle?.alias
|
|
53
|
+
? [
|
|
54
|
+
createAliasPlugin({
|
|
55
|
+
alias: props.bundle?.alias,
|
|
56
|
+
projectRoot,
|
|
57
|
+
}),
|
|
58
|
+
]
|
|
59
|
+
: []),
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
external: [
|
|
63
|
+
...(nodeJsCompatMode === "als" ? external_als : external),
|
|
64
|
+
...(props.bundle?.external ?? []),
|
|
65
|
+
...(props.bundle?.options?.external ?? []),
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
if (bundle.content) {
|
|
69
|
+
return bundle.content;
|
|
70
|
+
}
|
|
71
|
+
if (bundle.path) {
|
|
72
|
+
return await fs.readFile(bundle.path, "utf-8");
|
|
73
|
+
}
|
|
74
|
+
throw new Error("Failed to create bundle");
|
|
75
|
+
} catch (e: any) {
|
|
76
|
+
if (e.message?.includes("No such module 'node:")) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`${e.message}.\nMake sure to set 'nodejs_compat' compatibility flag and compatibilityDate > 2024-09-23`,
|
|
79
|
+
{ cause: e },
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (isBuildFailure(e)) {
|
|
83
|
+
rewriteNodeCompatBuildFailure(e.errors, nodeJsCompatMode);
|
|
84
|
+
throw e;
|
|
85
|
+
}
|
|
86
|
+
console.error("Error reading bundle:", e);
|
|
87
|
+
throw new Error("Error reading bundle");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -5,13 +5,14 @@ const nodejs_compat = [
|
|
|
5
5
|
"node:buffer",
|
|
6
6
|
"node:console",
|
|
7
7
|
"node:crypto",
|
|
8
|
+
"node:debug",
|
|
8
9
|
"node:diagnostics_channel",
|
|
9
10
|
"node:dns",
|
|
10
11
|
"node:events",
|
|
11
12
|
"node:inspector",
|
|
12
13
|
"node:net",
|
|
13
14
|
"node:path",
|
|
14
|
-
"node:perf_hooks",
|
|
15
|
+
"node:perf_hooks", // partially supported
|
|
15
16
|
"node:process",
|
|
16
17
|
"node:querystring",
|
|
17
18
|
"node:stream",
|
|
@@ -30,3 +31,10 @@ export const external = [
|
|
|
30
31
|
"cloudflare:workers",
|
|
31
32
|
// "cloudflare:*",
|
|
32
33
|
];
|
|
34
|
+
|
|
35
|
+
export const external_als = [
|
|
36
|
+
//
|
|
37
|
+
"node:async_hooks",
|
|
38
|
+
"async_hooks",
|
|
39
|
+
"cloudflare:*",
|
|
40
|
+
];
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { dedent } from "../../util/dedent.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* TanStackStart server functions and middleware run in Node.js intead of Miniflare when using `vite dev`.
|
|
3
5
|
*
|
|
@@ -13,12 +15,11 @@ export function cloudflareWorkersDevEnvironmentShim() {
|
|
|
13
15
|
},
|
|
14
16
|
load(id: string) {
|
|
15
17
|
if (id === "cloudflare:workers")
|
|
16
|
-
return `
|
|
17
|
-
import { getPlatformProxy } from "wrangler";
|
|
18
|
-
// TODO: should we export default cloudflare; ??
|
|
19
|
-
const cloudflare = await getPlatformProxy();
|
|
20
|
-
export const env = cloudflare.env
|
|
21
|
-
`;
|
|
18
|
+
return dedent`
|
|
19
|
+
import { getPlatformProxy } from "wrangler";
|
|
20
|
+
// TODO: should we export default cloudflare; ??
|
|
21
|
+
const cloudflare = await getPlatformProxy();
|
|
22
|
+
export const env = cloudflare.env;`;
|
|
22
23
|
},
|
|
23
24
|
} as const;
|
|
24
25
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { NodeJSCompatMode } from "miniflare";
|
|
2
|
+
import { getNodeCompat } from "miniflare";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Computes and validates the Node.js compatibility mode we are running.
|
|
6
|
+
*
|
|
7
|
+
* NOTES:
|
|
8
|
+
* - The v2 mode is configured via `nodejs_compat_v2` compat flag or via `nodejs_compat` plus a compatibility date of Sept 23rd. 2024 or later.
|
|
9
|
+
* - See `EnvironmentInheritable` for `noBundle`.
|
|
10
|
+
*
|
|
11
|
+
* @param compatibilityDateStr The compatibility date
|
|
12
|
+
* @param compatibilityFlags The compatibility flags
|
|
13
|
+
* @param noBundle Whether to skip internal build steps and directly deploy script
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
export function getNodeJSCompatMode(
|
|
17
|
+
compatibilityDateStr: string,
|
|
18
|
+
compatibilityFlags: string[],
|
|
19
|
+
props?: {
|
|
20
|
+
noBundle?: boolean;
|
|
21
|
+
},
|
|
22
|
+
): NodeJSCompatMode {
|
|
23
|
+
const {
|
|
24
|
+
mode,
|
|
25
|
+
hasNodejsCompatFlag,
|
|
26
|
+
hasNodejsCompatV2Flag,
|
|
27
|
+
hasExperimentalNodejsCompatV2Flag,
|
|
28
|
+
} = getNodeCompat(compatibilityDateStr, compatibilityFlags);
|
|
29
|
+
|
|
30
|
+
if (hasExperimentalNodejsCompatV2Flag) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
"The `experimental:` prefix on `nodejs_compat_v2` is no longer valid. Please remove it and try again.",
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (hasNodejsCompatFlag && hasNodejsCompatV2Flag) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
"The `nodejs_compat` and `nodejs_compat_v2` compatibility flags cannot be used in together. Please select just one.",
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (props?.noBundle && hasNodejsCompatV2Flag) {
|
|
43
|
+
console.warn(
|
|
44
|
+
"`nodejs_compat_v2` compatibility flag and `--no-bundle` can't be used together. If you want to polyfill Node.js built-ins and disable Wrangler's bundling, please polyfill as part of your own bundling process.",
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return mode;
|
|
49
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copied from https://github.com/cloudflare/workers-sdk/blob/main/packages/wrangler/src/deployment-bundle/esbuild-plugins/hybrid-nodejs-compat.ts#L17
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
import type { Plugin, PluginBuild } from "esbuild";
|
|
2
6
|
import assert from "node:assert";
|
|
3
7
|
import { builtinModules } from "node:module";
|
|
4
8
|
import nodePath from "node:path";
|
|
5
9
|
import { dedent } from "../../util/dedent.js";
|
|
6
10
|
|
|
7
|
-
/**
|
|
8
|
-
* Copied from https://github.com/cloudflare/workers-sdk/blob/main/packages/wrangler/src/deployment-bundle/esbuild-plugins/hybrid-nodejs-compat.ts#L17
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
11
|
const REQUIRED_NODE_BUILT_IN_NAMESPACE = "node-built-in-modules";
|
|
12
12
|
const REQUIRED_UNENV_ALIAS_NAMESPACE = "required-unenv-alias";
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@ const REQUIRED_UNENV_ALIAS_NAMESPACE = "required-unenv-alias";
|
|
|
16
16
|
*
|
|
17
17
|
* @returns ESBuild plugin
|
|
18
18
|
*/
|
|
19
|
-
export async function
|
|
19
|
+
export async function nodeJsCompatPlugin(): Promise<Plugin> {
|
|
20
20
|
// `unenv` and `@cloudflare/unenv-preset` only publish esm
|
|
21
21
|
const { defineEnv } = await import("unenv");
|
|
22
22
|
const { cloudflare } = await import("@cloudflare/unenv-preset");
|
|
@@ -212,6 +212,7 @@ function handleNodeJSGlobals(
|
|
|
212
212
|
module,
|
|
213
213
|
);
|
|
214
214
|
}
|
|
215
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
215
216
|
injectsByModule.get(module)!.push({ injectedName, exportName, importName });
|
|
216
217
|
}
|
|
217
218
|
|
|
@@ -2,8 +2,8 @@ import type { Context } from "../context.js";
|
|
|
2
2
|
import { Resource } from "../resource.js";
|
|
3
3
|
import { CloudflareApiError, handleApiError } from "./api-error.js";
|
|
4
4
|
import {
|
|
5
|
-
type CloudflareApi,
|
|
6
5
|
createCloudflareApi,
|
|
6
|
+
type CloudflareApi,
|
|
7
7
|
type CloudflareApiOptions,
|
|
8
8
|
} from "./api.js";
|
|
9
9
|
import type { Queue } from "./queue.js";
|
|
@@ -163,10 +163,8 @@ export const QueueConsumer = Resource(
|
|
|
163
163
|
let consumerData: CloudflareQueueConsumerResponse;
|
|
164
164
|
|
|
165
165
|
if (this.phase === "create") {
|
|
166
|
-
console.log(`Creating Queue Consumer for queue ${queueId}`);
|
|
167
166
|
consumerData = await createQueueConsumer(api, queueId, props);
|
|
168
167
|
} else if (this.output?.id) {
|
|
169
|
-
console.log(`Updating Queue Consumer ${this.output.id}`);
|
|
170
168
|
consumerData = await updateQueueConsumer(
|
|
171
169
|
api,
|
|
172
170
|
queueId,
|
|
@@ -174,10 +172,6 @@ export const QueueConsumer = Resource(
|
|
|
174
172
|
props,
|
|
175
173
|
);
|
|
176
174
|
} else {
|
|
177
|
-
// If no ID exists, fall back to creating a new consumer
|
|
178
|
-
console.log(
|
|
179
|
-
`No existing Consumer ID found, creating new Queue Consumer for queue ${queueId}`,
|
|
180
|
-
);
|
|
181
175
|
consumerData = await createQueueConsumer(api, queueId, props);
|
|
182
176
|
}
|
|
183
177
|
|