alchemy 0.80.0 → 0.81.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.
Files changed (52) hide show
  1. package/bin/alchemy.js +11 -5
  2. package/bin/services/execute-alchemy.ts +18 -6
  3. package/lib/alchemy.js +1 -1
  4. package/lib/alchemy.js.map +1 -1
  5. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  6. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  7. package/lib/cloudflare/hyperdrive.d.ts +23 -5
  8. package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
  9. package/lib/cloudflare/hyperdrive.js.map +1 -1
  10. package/lib/cloudflare/index.d.ts +3 -0
  11. package/lib/cloudflare/index.d.ts.map +1 -1
  12. package/lib/cloudflare/index.js +3 -0
  13. package/lib/cloudflare/index.js.map +1 -1
  14. package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
  15. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +7 -0
  16. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
  17. package/lib/cloudflare/queue.d.ts +0 -3
  18. package/lib/cloudflare/queue.d.ts.map +1 -1
  19. package/lib/cloudflare/queue.js +53 -31
  20. package/lib/cloudflare/queue.js.map +1 -1
  21. package/lib/cloudflare/tunnel-route.d.ts +172 -0
  22. package/lib/cloudflare/tunnel-route.d.ts.map +1 -0
  23. package/lib/cloudflare/tunnel-route.js +251 -0
  24. package/lib/cloudflare/tunnel-route.js.map +1 -0
  25. package/lib/cloudflare/vite/vite.d.ts.map +1 -1
  26. package/lib/cloudflare/vite/vite.js +38 -1
  27. package/lib/cloudflare/vite/vite.js.map +1 -1
  28. package/lib/cloudflare/warp-default-profile.d.ts +133 -0
  29. package/lib/cloudflare/warp-default-profile.d.ts.map +1 -0
  30. package/lib/cloudflare/warp-default-profile.js +138 -0
  31. package/lib/cloudflare/warp-default-profile.js.map +1 -0
  32. package/lib/cloudflare/warp-device-profile.d.ts +272 -0
  33. package/lib/cloudflare/warp-device-profile.d.ts.map +1 -0
  34. package/lib/cloudflare/warp-device-profile.js +291 -0
  35. package/lib/cloudflare/warp-device-profile.js.map +1 -0
  36. package/lib/scope.d.ts +1 -1
  37. package/lib/scope.d.ts.map +1 -1
  38. package/lib/scope.js +1 -1
  39. package/lib/scope.js.map +1 -1
  40. package/package.json +1 -1
  41. package/src/alchemy.ts +1 -1
  42. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  43. package/src/cloudflare/hyperdrive.ts +27 -5
  44. package/src/cloudflare/index.ts +3 -0
  45. package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +11 -0
  46. package/src/cloudflare/queue.ts +97 -56
  47. package/src/cloudflare/tunnel-route.ts +495 -0
  48. package/src/cloudflare/vite/vite.ts +29 -1
  49. package/src/cloudflare/warp-default-profile.ts +320 -0
  50. package/src/cloudflare/warp-device-profile.ts +616 -0
  51. package/src/scope.ts +2 -2
  52. package/workers/tunnel-proxy.js +1 -1
@@ -1,3 +1,4 @@
1
+ import path from "pathe";
1
2
  import { getPackageManagerRunner } from "../../util/detect-package-manager.ts";
2
3
  import type { Assets } from "../assets.ts";
3
4
  import type { Bindings } from "../bindings.ts";
@@ -20,6 +21,29 @@ export async function Vite<B extends Bindings>(
20
21
  props: ViteProps<B>,
21
22
  ): Promise<Vite<B>> {
22
23
  const runner = await getPackageManagerRunner();
24
+ let dev = spreadDevProps(props, `${runner} vite dev`);
25
+ let domain = typeof dev === "object" ? dev.domain : undefined;
26
+ const command = typeof dev === "object" ? dev.command! : dev;
27
+ if (!domain) {
28
+ let port;
29
+ const args = command.split(" ");
30
+ if (args.find((arg) => arg.startsWith("--port="))) {
31
+ port = args.find((arg) => arg.startsWith("--port="))?.split("=")[1];
32
+ } else if (args.includes("--port")) {
33
+ const index = args.indexOf("--port");
34
+ port = args[index + 1];
35
+ } else {
36
+ try {
37
+ const config = await import(
38
+ path.resolve(props.cwd ?? process.cwd(), "vite.config.ts")
39
+ );
40
+ port = config.default?.server?.port ?? 5173;
41
+ } catch {}
42
+ }
43
+ if (port) {
44
+ domain = `localhost:${port}`;
45
+ }
46
+ }
23
47
  return await Website(id, {
24
48
  spa: true,
25
49
  ...props,
@@ -33,6 +57,10 @@ export async function Vite<B extends Bindings>(
33
57
  (props.entrypoint || props.script ? "dist/client" : "dist"),
34
58
  },
35
59
  build: spreadBuildProps(props, `${runner} vite build`),
36
- dev: spreadDevProps(props, `${runner} vite dev`),
60
+ dev: domain
61
+ ? typeof dev === "string"
62
+ ? { command: dev, domain }
63
+ : { ...dev, domain }
64
+ : dev,
37
65
  });
38
66
  }
@@ -0,0 +1,320 @@
1
+ import type { Context } from "../context.ts";
2
+ import { Resource, ResourceKind } from "../resource.ts";
3
+ import { handleApiError } from "./api-error.ts";
4
+ import {
5
+ createCloudflareApi,
6
+ type CloudflareApi,
7
+ type CloudflareApiOptions,
8
+ } from "./api.ts";
9
+ import type {
10
+ ServiceModeV2,
11
+ SplitTunnelConfig,
12
+ } from "./warp-device-profile.ts";
13
+
14
+ /**
15
+ * Properties for updating the WARP Default Profile
16
+ */
17
+ export interface WarpDefaultProfileProps extends CloudflareApiOptions {
18
+ /**
19
+ * Service mode configuration for WARP client
20
+ */
21
+ serviceModeV2?: ServiceModeV2;
22
+
23
+ /**
24
+ * Disable automatic fallback to direct connection if tunnel fails
25
+ */
26
+ disableAutoFallback?: boolean;
27
+
28
+ /**
29
+ * Allow users to manually switch WARP modes
30
+ */
31
+ allowModeSwitch?: boolean;
32
+
33
+ /**
34
+ * Lock the WARP toggle switch (users cannot change it)
35
+ */
36
+ switchLocked?: boolean;
37
+
38
+ /**
39
+ * Tunnel protocol to use
40
+ */
41
+ tunnelProtocol?: "wireguard" | "masque";
42
+
43
+ /**
44
+ * Auto-connect timeout in seconds
45
+ * Set to 0 to disable auto-connect
46
+ */
47
+ autoConnect?: number;
48
+
49
+ /**
50
+ * Allow users to disconnect from WARP
51
+ */
52
+ allowedToLeave?: boolean;
53
+
54
+ /**
55
+ * Captive portal timeout in seconds
56
+ * Time before showing captive portal
57
+ */
58
+ captivePortal?: number;
59
+
60
+ /**
61
+ * Support URL for feedback button in WARP client
62
+ */
63
+ supportUrl?: string;
64
+
65
+ /**
66
+ * Exclude office IPs from WARP tunnel
67
+ */
68
+ excludeOfficeIps?: boolean;
69
+
70
+ /**
71
+ * LAN allow duration in minutes
72
+ */
73
+ lanAllowMinutes?: number;
74
+
75
+ /**
76
+ * LAN subnet size for local network access
77
+ */
78
+ lanAllowSubnetSize?: number;
79
+
80
+ /**
81
+ * Split tunnel configuration
82
+ * Controls which routes bypass or use the WARP tunnel
83
+ */
84
+ splitTunnel?: SplitTunnelConfig;
85
+
86
+ /**
87
+ * Whether to delete the default profile settings when removed from Alchemy
88
+ * Note: This does not actually delete the default profile (which always exists),
89
+ * but removes it from Alchemy state management
90
+ *
91
+ * @default false
92
+ */
93
+ delete?: boolean;
94
+ }
95
+
96
+ export function isWarpDefaultProfile(
97
+ resource: any,
98
+ ): resource is WarpDefaultProfile {
99
+ return resource?.[ResourceKind] === "cloudflare::WarpDefaultProfile";
100
+ }
101
+
102
+ /**
103
+ * Output returned after WARP Default Profile update
104
+ */
105
+ export type WarpDefaultProfile = WarpDefaultProfileProps & {
106
+ /**
107
+ * Time at which the profile was last modified
108
+ */
109
+ modifiedAt: number;
110
+ };
111
+
112
+ /**
113
+ * Manages the Cloudflare WARP Default Device Profile, which defines the
114
+ * account-wide default WARP client settings that apply when no custom profile matches.
115
+ *
116
+ * The default profile always exists and cannot be deleted. This resource allows you
117
+ * to update its settings programmatically.
118
+ *
119
+ * @see https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/configure-warp/device-profiles/
120
+ *
121
+ * @example
122
+ * ## Update default WARP settings
123
+ *
124
+ * Configure the default behavior for all devices
125
+ *
126
+ * const defaultProfile = await WarpDefaultProfile("default", {
127
+ * serviceModeV2: { mode: "warp" },
128
+ * autoConnect: 0,
129
+ * captivePortal: 180,
130
+ * supportUrl: "https://support.example.com"
131
+ * });
132
+ *
133
+ * @example
134
+ * ## Default profile with split tunnel
135
+ *
136
+ * Configure default split tunnel behavior for all devices
137
+ *
138
+ * const defaultWithSplitTunnel = await WarpDefaultProfile("default", {
139
+ * serviceModeV2: { mode: "warp" },
140
+ * splitTunnel: {
141
+ * mode: "exclude",
142
+ * entries: [
143
+ * { address: "10.0.0.0/8", description: "Internal network" },
144
+ * { address: "192.168.0.0/16", description: "Local network" }
145
+ * ]
146
+ * }
147
+ * });
148
+ *
149
+ * @example
150
+ * ## Locked default profile
151
+ *
152
+ * Create a locked-down default configuration
153
+ *
154
+ * const lockedDefault = await WarpDefaultProfile("default", {
155
+ * serviceModeV2: { mode: "warp" },
156
+ * switchLocked: true,
157
+ * allowedToLeave: false,
158
+ * disableAutoFallback: true,
159
+ * tunnelProtocol: "wireguard"
160
+ * });
161
+ */
162
+ export const WarpDefaultProfile = Resource(
163
+ "cloudflare::WarpDefaultProfile",
164
+ async function (
165
+ this: Context<WarpDefaultProfile>,
166
+ id: string,
167
+ props: WarpDefaultProfileProps = {},
168
+ ): Promise<WarpDefaultProfile> {
169
+ const api = await createCloudflareApi(props);
170
+
171
+ if (this.phase === "delete") {
172
+ // Default profile cannot be deleted, just remove from state
173
+ return this.destroy();
174
+ }
175
+
176
+ // Update default profile (single PATCH endpoint)
177
+ await updateDefaultPolicy(api, props);
178
+
179
+ return {
180
+ ...props,
181
+ modifiedAt: Date.now(),
182
+ };
183
+ },
184
+ );
185
+
186
+ /**
187
+ * Internal API response type for default policy
188
+ * @internal
189
+ */
190
+ interface CloudflareDefaultPolicyResponse {
191
+ device_settings?: {
192
+ service_mode_v2?: {
193
+ mode: string;
194
+ port?: number;
195
+ };
196
+ disable_auto_fallback?: boolean;
197
+ allow_mode_switch?: boolean;
198
+ switch_locked?: boolean;
199
+ tunnel_protocol?: string;
200
+ auto_connect?: number;
201
+ allowed_to_leave?: boolean;
202
+ captive_portal?: number;
203
+ support_url?: string;
204
+ exclude_office_ips?: boolean;
205
+ lan_allow_minutes?: number;
206
+ lan_allow_subnet_size?: number;
207
+ };
208
+ }
209
+
210
+ // Keys with special handling (not simple 1:1 mapping)
211
+ type SpecialKeys = "serviceModeV2" | "splitTunnel" | "delete";
212
+
213
+ // Simple device setting keys that map directly to snake_case API fields
214
+ type SimpleDeviceSettingKey = Exclude<
215
+ keyof WarpDefaultProfileProps,
216
+ keyof CloudflareApiOptions | SpecialKeys
217
+ >;
218
+
219
+ // Type-safe mapping - TypeScript errors if any SimpleDeviceSettingKey is missing
220
+ const DEVICE_SETTINGS_MAP: Record<SimpleDeviceSettingKey, string> = {
221
+ disableAutoFallback: "disable_auto_fallback",
222
+ allowModeSwitch: "allow_mode_switch",
223
+ switchLocked: "switch_locked",
224
+ tunnelProtocol: "tunnel_protocol",
225
+ autoConnect: "auto_connect",
226
+ allowedToLeave: "allowed_to_leave",
227
+ captivePortal: "captive_portal",
228
+ supportUrl: "support_url",
229
+ excludeOfficeIps: "exclude_office_ips",
230
+ lanAllowMinutes: "lan_allow_minutes",
231
+ lanAllowSubnetSize: "lan_allow_subnet_size",
232
+ };
233
+
234
+ async function updateDefaultPolicy(
235
+ api: CloudflareApi,
236
+ props: WarpDefaultProfileProps,
237
+ ): Promise<void> {
238
+ // Build device settings object
239
+ const deviceSettings: Record<string, unknown> = {};
240
+
241
+ if (props.serviceModeV2) {
242
+ deviceSettings.service_mode_v2 = {
243
+ mode: props.serviceModeV2.mode,
244
+ ...(props.serviceModeV2.port && { port: props.serviceModeV2.port }),
245
+ };
246
+ }
247
+
248
+ // Apply all simple field mappings
249
+ for (const propKey of Object.keys(
250
+ DEVICE_SETTINGS_MAP,
251
+ ) as SimpleDeviceSettingKey[]) {
252
+ deviceSettings[DEVICE_SETTINGS_MAP[propKey]] = props[propKey];
253
+ }
254
+
255
+ // Split tunnel config is part of the body for default policy
256
+ if (props.splitTunnel) {
257
+ if (props.splitTunnel.mode === "include") {
258
+ deviceSettings.include = props.splitTunnel.entries.map((entry) => ({
259
+ address: entry.address,
260
+ ...(entry.description && { description: entry.description }),
261
+ }));
262
+ } else {
263
+ deviceSettings.exclude = props.splitTunnel.entries.map((entry) => ({
264
+ address: entry.address,
265
+ ...(entry.description && { description: entry.description }),
266
+ }));
267
+ }
268
+ }
269
+
270
+ const requestBody: any = {
271
+ ...(Object.keys(deviceSettings).length > 0 && { ...deviceSettings }),
272
+ };
273
+
274
+ const response = await api.patch(
275
+ `/accounts/${api.accountId}/devices/policy`,
276
+ requestBody,
277
+ );
278
+
279
+ if (!response.ok) {
280
+ await handleApiError(response, "update", "warp_default_profile", "default");
281
+ }
282
+ }
283
+
284
+ async function updateDefaultSplitTunnel(
285
+ api: CloudflareApi,
286
+ config: SplitTunnelConfig,
287
+ ): Promise<void> {
288
+ const routes = config.entries.map((entry) => ({
289
+ address: entry.address,
290
+ ...(entry.description && { description: entry.description }),
291
+ }));
292
+
293
+ if (config.mode === "include") {
294
+ const response = await api.put(
295
+ `/accounts/${api.accountId}/devices/policies/default/includes`,
296
+ routes,
297
+ );
298
+ if (!response.ok) {
299
+ await handleApiError(
300
+ response,
301
+ "update split tunnel includes",
302
+ "warp_default_profile",
303
+ "default",
304
+ );
305
+ }
306
+ } else {
307
+ const response = await api.put(
308
+ `/accounts/${api.accountId}/devices/policies/default/excludes`,
309
+ routes,
310
+ );
311
+ if (!response.ok) {
312
+ await handleApiError(
313
+ response,
314
+ "update split tunnel excludes",
315
+ "warp_default_profile",
316
+ "default",
317
+ );
318
+ }
319
+ }
320
+ }