alchemy 0.46.1 → 0.48.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/bin/alchemy.mjs +560 -98
- package/lib/alchemy.d.ts +9 -3
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +19 -39
- package/lib/alchemy.js.map +1 -1
- package/lib/build-date.d.ts +1 -1
- package/lib/build-date.js +1 -1
- package/lib/cloudflare/astro.d.ts.map +1 -1
- package/lib/cloudflare/astro.js +3 -0
- package/lib/cloudflare/astro.js.map +1 -1
- package/lib/cloudflare/container.js +1 -1
- package/lib/cloudflare/container.js.map +1 -1
- package/lib/cloudflare/nuxt.d.ts.map +1 -1
- package/lib/cloudflare/nuxt.js +3 -0
- package/lib/cloudflare/nuxt.js.map +1 -1
- package/lib/cloudflare/oauth.d.ts.map +1 -1
- package/lib/cloudflare/oauth.js +4 -3
- package/lib/cloudflare/oauth.js.map +1 -1
- package/lib/cloudflare/pipeline.d.ts.map +1 -1
- package/lib/cloudflare/pipeline.js +1 -3
- package/lib/cloudflare/pipeline.js.map +1 -1
- package/lib/cloudflare/react-router.d.ts.map +1 -1
- package/lib/cloudflare/react-router.js +3 -0
- package/lib/cloudflare/react-router.js.map +1 -1
- package/lib/cloudflare/vite.d.ts.map +1 -1
- package/lib/cloudflare/vite.js +0 -1
- package/lib/cloudflare/vite.js.map +1 -1
- package/lib/cloudflare/website.d.ts +0 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +13 -7
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +6 -0
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +1 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +33 -3
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +91 -70
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/zone.d.ts +1 -1
- package/lib/cloudflare/zone.d.ts.map +1 -1
- package/lib/cloudflare/zone.js +38 -56
- package/lib/cloudflare/zone.js.map +1 -1
- package/lib/scope.d.ts +14 -2
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +8 -3
- package/lib/scope.js.map +1 -1
- package/lib/util/safe-fetch.d.ts.map +1 -1
- package/lib/util/safe-fetch.js +1 -0
- package/lib/util/safe-fetch.js.map +1 -1
- package/lib/util/telemetry/constants.js +2 -2
- package/lib/util/telemetry/constants.js.map +1 -1
- package/package.json +4 -2
- package/src/alchemy.ts +28 -49
- package/src/build-date.ts +1 -1
- package/src/cloudflare/astro.ts +3 -0
- package/src/cloudflare/container.ts +1 -1
- package/src/cloudflare/nuxt.ts +3 -0
- package/src/cloudflare/oauth.ts +5 -4
- package/src/cloudflare/pipeline.ts +1 -3
- package/src/cloudflare/react-router.ts +3 -0
- package/src/cloudflare/vite.ts +0 -1
- package/src/cloudflare/website.ts +14 -8
- package/src/cloudflare/worker-metadata.ts +7 -0
- package/src/cloudflare/worker.ts +138 -102
- package/src/cloudflare/zone.ts +50 -70
- package/src/scope.ts +21 -4
- package/src/util/safe-fetch.ts +1 -0
- package/src/util/telemetry/constants.ts +2 -2
- package/templates/react-router/package.json +1 -1
- package/templates/react-router/tsconfig.cloudflare.json +5 -1
- package/templates/react-router/tsconfig.json +0 -3
- package/templates/react-router/tsconfig.node.json +0 -4
- package/lib/util/xdg-paths.d.ts +0 -9
- package/lib/util/xdg-paths.d.ts.map +0 -1
- package/lib/util/xdg-paths.js +0 -41
- package/lib/util/xdg-paths.js.map +0 -1
- package/src/util/xdg-paths.ts +0 -48
package/src/alchemy.ts
CHANGED
|
@@ -20,50 +20,6 @@ import type { LoggerApi } from "./util/cli.ts";
|
|
|
20
20
|
import { logger } from "./util/logger.ts";
|
|
21
21
|
import { TelemetryClient } from "./util/telemetry/client.ts";
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
* Parses CLI arguments to extract alchemy options
|
|
25
|
-
*/
|
|
26
|
-
function parseCliArgs(): Partial<AlchemyOptions> {
|
|
27
|
-
const args = process.argv.slice(2);
|
|
28
|
-
const options: Partial<AlchemyOptions> = {};
|
|
29
|
-
|
|
30
|
-
// Parse phase from CLI arguments
|
|
31
|
-
if (args.includes("--destroy")) {
|
|
32
|
-
options.phase = "destroy";
|
|
33
|
-
} else if (args.includes("--read")) {
|
|
34
|
-
options.phase = "read";
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (args.includes("--local") || args.includes("--dev")) {
|
|
38
|
-
options.dev = "prefer-local";
|
|
39
|
-
} else if (
|
|
40
|
-
args.includes("--remote") ||
|
|
41
|
-
args.includes("--watch") ||
|
|
42
|
-
process.execArgv.includes("--watch")
|
|
43
|
-
) {
|
|
44
|
-
options.dev = "prefer-remote";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Parse quiet flag
|
|
48
|
-
if (args.includes("--quiet")) {
|
|
49
|
-
options.quiet = true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Parse stage argument (--stage my-stage)
|
|
53
|
-
const stageIndex = args.indexOf("--stage");
|
|
54
|
-
if (stageIndex !== -1 && stageIndex + 1 < args.length) {
|
|
55
|
-
options.stage = args[stageIndex + 1];
|
|
56
|
-
}
|
|
57
|
-
options.stage ??= process.env.STAGE;
|
|
58
|
-
|
|
59
|
-
// Get password from environment variables
|
|
60
|
-
if (process.env.ALCHEMY_PASSWORD) {
|
|
61
|
-
options.password = process.env.ALCHEMY_PASSWORD;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return options;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
23
|
/**
|
|
68
24
|
* Type alias for semantic highlighting of `alchemy` as a type keyword
|
|
69
25
|
*/
|
|
@@ -175,8 +131,25 @@ async function _alchemy(
|
|
|
175
131
|
if (typeof args[0] === "string") {
|
|
176
132
|
const [appName, options] = args as [string, AlchemyOptions?];
|
|
177
133
|
|
|
178
|
-
|
|
179
|
-
const cliOptions =
|
|
134
|
+
const cliArgs = process.argv.slice(2);
|
|
135
|
+
const cliOptions = {
|
|
136
|
+
phase: cliArgs.includes("--destroy")
|
|
137
|
+
? "destroy"
|
|
138
|
+
: cliArgs.includes("--read")
|
|
139
|
+
? "read"
|
|
140
|
+
: "up",
|
|
141
|
+
local: cliArgs.includes("--local") || cliArgs.includes("--dev"),
|
|
142
|
+
watch: cliArgs.includes("--watch"),
|
|
143
|
+
quiet: cliArgs.includes("--quiet"),
|
|
144
|
+
// Parse stage argument (--stage my-stage) functionally and inline as a property declaration
|
|
145
|
+
stage: (function parseStage() {
|
|
146
|
+
const i = cliArgs.indexOf("--stage");
|
|
147
|
+
return i !== -1 && i + 1 < cliArgs.length
|
|
148
|
+
? cliArgs[i + 1]
|
|
149
|
+
: process.env.STAGE;
|
|
150
|
+
})(),
|
|
151
|
+
password: process.env.ALCHEMY_PASSWORD,
|
|
152
|
+
} satisfies Partial<AlchemyOptions>;
|
|
180
153
|
const mergedOptions = {
|
|
181
154
|
...cliOptions,
|
|
182
155
|
...options,
|
|
@@ -348,11 +321,17 @@ export interface AlchemyOptions {
|
|
|
348
321
|
*/
|
|
349
322
|
phase?: Phase;
|
|
350
323
|
/**
|
|
351
|
-
* Determines
|
|
324
|
+
* Determines if resources should be simulated locally (where possible)
|
|
325
|
+
*
|
|
326
|
+
* @default - `true` if ran with `alchemy dev` or `bun ./alchemy.run.ts --dev`
|
|
327
|
+
*/
|
|
328
|
+
local?: boolean;
|
|
329
|
+
/**
|
|
330
|
+
* Determines if local changes to resources should be reactively pushed to the local or remote environment.
|
|
352
331
|
*
|
|
353
|
-
* @default - `
|
|
332
|
+
* @default - `true` if ran with `alchemy dev`, `alchemy watch`, `bun --watch ./alchemy.run.ts`
|
|
354
333
|
*/
|
|
355
|
-
|
|
334
|
+
watch?: boolean;
|
|
356
335
|
/**
|
|
357
336
|
* Name to scope the resource state under (e.g. `.alchemy/{stage}/..`).
|
|
358
337
|
*
|
package/src/build-date.ts
CHANGED
package/src/cloudflare/astro.ts
CHANGED
|
@@ -191,7 +191,7 @@ export async function Container<T>(
|
|
|
191
191
|
adopt: props.adopt,
|
|
192
192
|
};
|
|
193
193
|
|
|
194
|
-
const isDev = Scope.current.
|
|
194
|
+
const isDev = Scope.current.local && !props.dev?.remote;
|
|
195
195
|
if (isDev) {
|
|
196
196
|
const image = await Image(id, {
|
|
197
197
|
name: `cloudflare-dev/${name}`, // prefix used by Miniflare
|
package/src/cloudflare/nuxt.ts
CHANGED
|
@@ -64,5 +64,8 @@ export async function Nuxt<B extends Bindings>(
|
|
|
64
64
|
compatibilityFlags: ["nodejs_compat", ...(props?.compatibilityFlags ?? [])],
|
|
65
65
|
// Enable wrangler by default, common for Nuxt/Cloudflare deployments
|
|
66
66
|
wrangler: props?.wrangler ?? true,
|
|
67
|
+
dev: props?.dev ?? {
|
|
68
|
+
command: "nuxt dev",
|
|
69
|
+
},
|
|
67
70
|
});
|
|
68
71
|
}
|
package/src/cloudflare/oauth.ts
CHANGED
|
@@ -6,6 +6,7 @@ import fs from "node:fs/promises";
|
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import open from "open";
|
|
9
|
+
import xdgAppPaths from "xdg-app-paths";
|
|
9
10
|
import { HTTPServer } from "../util/http-server.ts";
|
|
10
11
|
import { memoize } from "../util/memoize.ts";
|
|
11
12
|
import {
|
|
@@ -15,7 +16,6 @@ import {
|
|
|
15
16
|
HTTPError,
|
|
16
17
|
singleFlight,
|
|
17
18
|
} from "../util/neverthrow.ts";
|
|
18
|
-
import { createXdgAppPaths } from "../util/xdg-paths.ts";
|
|
19
19
|
|
|
20
20
|
const CLIENT_ID = "54d11594-84e4-41aa-b438-e81b8fa78ee7";
|
|
21
21
|
const REDIRECT_URI = "http://localhost:8976/oauth/callback";
|
|
@@ -70,7 +70,7 @@ export const getRefreshedWranglerConfig = singleFlight(() =>
|
|
|
70
70
|
);
|
|
71
71
|
|
|
72
72
|
const getWranglerConfigPath = memoize(async () => {
|
|
73
|
-
const xdgConfigDir =
|
|
73
|
+
const xdgConfigDir = xdgAppPaths(".wrangler").config();
|
|
74
74
|
const legacyConfigDir = path.join(os.homedir(), ".wrangler");
|
|
75
75
|
const configDir = (await fs
|
|
76
76
|
.stat(legacyConfigDir)
|
|
@@ -92,12 +92,12 @@ const readWranglerConfig = () => {
|
|
|
92
92
|
const config = toml.parse(text.replace(/\r\n/g, "\n"));
|
|
93
93
|
assert(typeof config.oauth_token === "string");
|
|
94
94
|
assert(typeof config.refresh_token === "string");
|
|
95
|
-
assert(config.expiration_time
|
|
95
|
+
assert(!!config.expiration_time);
|
|
96
96
|
assert(Array.isArray(config.scopes));
|
|
97
97
|
return {
|
|
98
98
|
oauth_token: config.oauth_token,
|
|
99
99
|
refresh_token: config.refresh_token,
|
|
100
|
-
expiration_time: new Date(config.expiration_time),
|
|
100
|
+
expiration_time: new Date(config.expiration_time as any),
|
|
101
101
|
scopes: config.scopes as string[],
|
|
102
102
|
};
|
|
103
103
|
}),
|
|
@@ -110,6 +110,7 @@ const writeWranglerConfig = (config: WranglerConfig) => {
|
|
|
110
110
|
return ResultAsync.fromSafePromise(getWranglerConfigPath()).map(
|
|
111
111
|
async (configPath) => {
|
|
112
112
|
const toml = await import("@iarna/toml");
|
|
113
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
113
114
|
await fs.writeFile(
|
|
114
115
|
configPath,
|
|
115
116
|
toml.stringify({
|
|
@@ -368,11 +368,9 @@ const PipelineResource = Resource("cloudflare::Pipeline", async function <
|
|
|
368
368
|
let pipelineData: CloudflarePipelineResponse;
|
|
369
369
|
|
|
370
370
|
if (this.phase === "create") {
|
|
371
|
-
console.log(props);
|
|
372
371
|
// Check if we should adopt an existing pipeline
|
|
373
372
|
try {
|
|
374
373
|
// Try to create pipeline first
|
|
375
|
-
console.log("Creating new Cloudflare Pipeline:", pipelineName);
|
|
376
374
|
pipelineData = await createPipeline(api, pipelineName, props);
|
|
377
375
|
} catch (error) {
|
|
378
376
|
// If creation fails with 409 (conflict), adopt existing pipeline
|
|
@@ -383,7 +381,7 @@ const PipelineResource = Resource("cloudflare::Pipeline", async function <
|
|
|
383
381
|
error.message.includes("Pipeline with this name already exists")))
|
|
384
382
|
) {
|
|
385
383
|
if (props.adopt) {
|
|
386
|
-
console.
|
|
384
|
+
console.warn(
|
|
387
385
|
"Pipeline already exists, adopting existing Cloudflare Pipeline:",
|
|
388
386
|
pipelineName,
|
|
389
387
|
);
|
package/src/cloudflare/vite.ts
CHANGED
|
@@ -2,6 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { alchemy } from "../alchemy.ts";
|
|
3
3
|
import { Exec } from "../os/exec.ts";
|
|
4
4
|
import { Scope } from "../scope.ts";
|
|
5
|
+
import { detectPackageManager } from "../util/detect-package-manager.ts";
|
|
5
6
|
import { Assets } from "./assets.ts";
|
|
6
7
|
import type { Bindings } from "./bindings.ts";
|
|
7
8
|
import {
|
|
@@ -100,7 +101,6 @@ export interface WebsiteProps<B extends Bindings>
|
|
|
100
101
|
*/
|
|
101
102
|
dev?: {
|
|
102
103
|
command: string;
|
|
103
|
-
url: string;
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
/**
|
|
@@ -123,6 +123,15 @@ export interface WebsiteProps<B extends Bindings>
|
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
const packageManager = await detectPackageManager();
|
|
127
|
+
const devCommand = {
|
|
128
|
+
npm: "npx vite dev",
|
|
129
|
+
bun: "bun vite dev",
|
|
130
|
+
pnpm: "pnpm vite dev",
|
|
131
|
+
yarn: "yarn vite dev",
|
|
132
|
+
deno: "deno task dev",
|
|
133
|
+
}[packageManager];
|
|
134
|
+
|
|
126
135
|
export type Website<B extends Bindings> = B extends { ASSETS: any }
|
|
127
136
|
? never
|
|
128
137
|
: Worker<B & { ASSETS: Assets }>;
|
|
@@ -189,12 +198,9 @@ export default {
|
|
|
189
198
|
};`,
|
|
190
199
|
url: props.url ?? true,
|
|
191
200
|
adopt: props.adopt ?? true,
|
|
192
|
-
dev:
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
url: props.dev.url,
|
|
196
|
-
}
|
|
197
|
-
: undefined,
|
|
201
|
+
dev: {
|
|
202
|
+
command: props.dev?.command ?? devCommand,
|
|
203
|
+
},
|
|
198
204
|
} as WorkerProps<any> & { name: string };
|
|
199
205
|
|
|
200
206
|
if (wrangler) {
|
|
@@ -216,7 +222,7 @@ export default {
|
|
|
216
222
|
});
|
|
217
223
|
}
|
|
218
224
|
|
|
219
|
-
const isDev = scope.
|
|
225
|
+
const isDev = scope.local;
|
|
220
226
|
|
|
221
227
|
if (props.command && !isDev) {
|
|
222
228
|
await Exec("build", {
|
|
@@ -197,6 +197,9 @@ export interface WorkerMetadata {
|
|
|
197
197
|
suspended: boolean;
|
|
198
198
|
}[];
|
|
199
199
|
containers?: { class_name: string }[];
|
|
200
|
+
placement?: {
|
|
201
|
+
mode: "smart";
|
|
202
|
+
};
|
|
200
203
|
}
|
|
201
204
|
|
|
202
205
|
export async function prepareWorkerMetadata(
|
|
@@ -213,6 +216,9 @@ export async function prepareWorkerMetadata(
|
|
|
213
216
|
};
|
|
214
217
|
tags?: string[];
|
|
215
218
|
unstable_cacheWorkerSettings?: boolean;
|
|
219
|
+
placement?: {
|
|
220
|
+
mode: "smart";
|
|
221
|
+
};
|
|
216
222
|
},
|
|
217
223
|
): Promise<WorkerMetadata> {
|
|
218
224
|
const oldSettings = await (props.unstable_cacheWorkerSettings
|
|
@@ -336,6 +342,7 @@ export async function prepareWorkerMetadata(
|
|
|
336
342
|
transferred_classes: [],
|
|
337
343
|
new_sqlite_classes: [],
|
|
338
344
|
},
|
|
345
|
+
placement: props.placement,
|
|
339
346
|
};
|
|
340
347
|
|
|
341
348
|
const assetUploadResult = props.assetUploadResult;
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import type esbuild from "esbuild";
|
|
2
2
|
import kleur from "kleur";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
-
import
|
|
5
|
-
existsSync,
|
|
6
|
-
mkdirSync,
|
|
7
|
-
readFileSync,
|
|
8
|
-
unlinkSync,
|
|
9
|
-
writeFileSync,
|
|
10
|
-
} from "node:fs";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
11
5
|
import { isDeepStrictEqual } from "node:util";
|
|
12
6
|
import path from "pathe";
|
|
13
7
|
import { BUILD_DATE } from "../build-date.ts";
|
|
@@ -18,7 +12,7 @@ import { getBindKey, tryGetBinding } from "../runtime/bind.ts";
|
|
|
18
12
|
import { isRuntime } from "../runtime/global.ts";
|
|
19
13
|
import { bootstrapPlugin } from "../runtime/plugin.ts";
|
|
20
14
|
import { Scope } from "../scope.ts";
|
|
21
|
-
import { Secret, secret } from "../secret.ts";
|
|
15
|
+
import { Secret, isSecret, secret } from "../secret.ts";
|
|
22
16
|
import { serializeScope } from "../serde.ts";
|
|
23
17
|
import type { type } from "../type.ts";
|
|
24
18
|
import { DeferredPromise } from "../util/deferred-promise.ts";
|
|
@@ -92,6 +86,7 @@ import { Workflow, isWorkflow, upsertWorkflow } from "./workflow.ts";
|
|
|
92
86
|
// Previous versions of `Worker` used the `Bundle` resource.
|
|
93
87
|
// This import is here to avoid errors when destroying the `Bundle` resource.
|
|
94
88
|
import "../esbuild/bundle.ts";
|
|
89
|
+
import { exists } from "../util/exists.ts";
|
|
95
90
|
|
|
96
91
|
/**
|
|
97
92
|
* Configuration options for static assets
|
|
@@ -181,6 +176,8 @@ export interface BaseWorkerProps<
|
|
|
181
176
|
* Environment variables to attach to the worker
|
|
182
177
|
*
|
|
183
178
|
* These will be converted to plain_text bindings
|
|
179
|
+
*
|
|
180
|
+
* @deprecated - use `bindings` instead
|
|
184
181
|
*/
|
|
185
182
|
env?: {
|
|
186
183
|
[key: string]: string;
|
|
@@ -351,7 +348,6 @@ export interface BaseWorkerProps<
|
|
|
351
348
|
* the worker will be emulated locally and available at a randomly selected port.
|
|
352
349
|
*/
|
|
353
350
|
dev?:
|
|
354
|
-
| boolean
|
|
355
351
|
| {
|
|
356
352
|
/**
|
|
357
353
|
* Port to use for local development
|
|
@@ -363,12 +359,35 @@ export interface BaseWorkerProps<
|
|
|
363
359
|
* @default false
|
|
364
360
|
*/
|
|
365
361
|
remote?: boolean;
|
|
362
|
+
/** @internal */
|
|
363
|
+
command?: undefined;
|
|
366
364
|
}
|
|
367
365
|
| {
|
|
368
366
|
command: string;
|
|
369
|
-
url: string;
|
|
370
367
|
cwd?: string;
|
|
368
|
+
/** @internal */
|
|
369
|
+
port?: undefined;
|
|
370
|
+
/** @internal */
|
|
371
|
+
remote?: undefined;
|
|
371
372
|
};
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Smart placement configuration for the worker.
|
|
376
|
+
*
|
|
377
|
+
* Controls how Cloudflare places the worker across its network for optimal performance.
|
|
378
|
+
*
|
|
379
|
+
* When omitted, smart placement is disabled (default behavior).
|
|
380
|
+
*/
|
|
381
|
+
placement?: {
|
|
382
|
+
/**
|
|
383
|
+
* The placement mode for the worker.
|
|
384
|
+
*
|
|
385
|
+
* - "smart": Automatically optimize placement based on performance metrics
|
|
386
|
+
*
|
|
387
|
+
* @default undefined (smart placement disabled)
|
|
388
|
+
*/
|
|
389
|
+
mode: "smart";
|
|
390
|
+
};
|
|
372
391
|
}
|
|
373
392
|
|
|
374
393
|
export interface InlineWorkerProps<
|
|
@@ -563,6 +582,13 @@ export type Worker<
|
|
|
563
582
|
* Version label for this worker deployment
|
|
564
583
|
*/
|
|
565
584
|
version?: string;
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Smart placement configuration for the worker
|
|
588
|
+
*/
|
|
589
|
+
placement?: {
|
|
590
|
+
mode: "smart";
|
|
591
|
+
};
|
|
566
592
|
};
|
|
567
593
|
|
|
568
594
|
/**
|
|
@@ -1020,8 +1046,6 @@ export const _Worker = Resource(
|
|
|
1020
1046
|
? props.namespace
|
|
1021
1047
|
: props.namespace?.namespaceName;
|
|
1022
1048
|
|
|
1023
|
-
const dev = normalizeDev(this, props.dev);
|
|
1024
|
-
|
|
1025
1049
|
const [bundle, error] = wrap(() =>
|
|
1026
1050
|
normalizeWorkerBundle({
|
|
1027
1051
|
entrypoint: props.entrypoint,
|
|
@@ -1040,34 +1064,45 @@ export const _Worker = Resource(
|
|
|
1040
1064
|
}),
|
|
1041
1065
|
);
|
|
1042
1066
|
|
|
1043
|
-
|
|
1067
|
+
// run locally if
|
|
1068
|
+
const local = this.scope.local && !props.dev?.remote;
|
|
1069
|
+
const watch = this.scope.watch;
|
|
1070
|
+
|
|
1071
|
+
if (local) {
|
|
1044
1072
|
let url: string | undefined;
|
|
1045
1073
|
if (error) {
|
|
1046
1074
|
throw error;
|
|
1047
1075
|
}
|
|
1048
1076
|
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1077
|
+
if (props.dev?.command) {
|
|
1078
|
+
const { url: commandUrl } = await createDevCommand({
|
|
1079
|
+
id,
|
|
1080
|
+
command: props.dev.command,
|
|
1081
|
+
cwd: props.dev.cwd || props.cwd || process.cwd(),
|
|
1082
|
+
env: {
|
|
1083
|
+
...props.env,
|
|
1084
|
+
...Object.fromEntries(
|
|
1085
|
+
Object.entries(props.bindings ?? {}).flatMap(([key, value]) =>
|
|
1086
|
+
typeof value === "string"
|
|
1087
|
+
? [[key, value]]
|
|
1088
|
+
: isSecret(value)
|
|
1089
|
+
? [[key, value.unencrypted]]
|
|
1090
|
+
: [],
|
|
1091
|
+
),
|
|
1092
|
+
),
|
|
1093
|
+
},
|
|
1094
|
+
});
|
|
1095
|
+
url = commandUrl;
|
|
1096
|
+
} else {
|
|
1097
|
+
url = await createMiniflare({
|
|
1098
|
+
id,
|
|
1099
|
+
workerName,
|
|
1100
|
+
compatibilityDate,
|
|
1101
|
+
compatibilityFlags,
|
|
1102
|
+
bindings: props.bindings,
|
|
1103
|
+
bundle,
|
|
1104
|
+
port: props.dev?.port,
|
|
1105
|
+
});
|
|
1071
1106
|
}
|
|
1072
1107
|
|
|
1073
1108
|
return this({
|
|
@@ -1091,6 +1126,8 @@ export const _Worker = Resource(
|
|
|
1091
1126
|
assets: props.assets,
|
|
1092
1127
|
// Include cron triggers in the output
|
|
1093
1128
|
crons: props.crons,
|
|
1129
|
+
// Include placement configuration in the output
|
|
1130
|
+
placement: props.placement,
|
|
1094
1131
|
// phantom property
|
|
1095
1132
|
Env: undefined!,
|
|
1096
1133
|
} as unknown as Worker<B>);
|
|
@@ -1189,7 +1226,7 @@ export const _Worker = Resource(
|
|
|
1189
1226
|
}
|
|
1190
1227
|
|
|
1191
1228
|
let putWorkerResult: PutWorkerResult;
|
|
1192
|
-
if (
|
|
1229
|
+
if (watch) {
|
|
1193
1230
|
// todo(john): clean this up and add log tail
|
|
1194
1231
|
const controller = new AbortController();
|
|
1195
1232
|
cleanups.push(() => controller.abort());
|
|
@@ -1383,6 +1420,8 @@ export const _Worker = Resource(
|
|
|
1383
1420
|
namespace: props.namespace,
|
|
1384
1421
|
// Include version information in the output
|
|
1385
1422
|
version: props.version,
|
|
1423
|
+
// Include placement configuration in the output
|
|
1424
|
+
placement: props.placement,
|
|
1386
1425
|
// phantom property
|
|
1387
1426
|
Env: undefined!,
|
|
1388
1427
|
} as unknown as Worker<B>);
|
|
@@ -1405,57 +1444,6 @@ process.on("SIGINT", async () => {
|
|
|
1405
1444
|
process.exit(0);
|
|
1406
1445
|
});
|
|
1407
1446
|
|
|
1408
|
-
type Dev =
|
|
1409
|
-
| {
|
|
1410
|
-
type: "none";
|
|
1411
|
-
local: false;
|
|
1412
|
-
}
|
|
1413
|
-
| {
|
|
1414
|
-
type: "miniflare";
|
|
1415
|
-
port?: number;
|
|
1416
|
-
local: true;
|
|
1417
|
-
}
|
|
1418
|
-
| {
|
|
1419
|
-
type: "command";
|
|
1420
|
-
command: string;
|
|
1421
|
-
url: string;
|
|
1422
|
-
cwd?: string;
|
|
1423
|
-
local: true;
|
|
1424
|
-
}
|
|
1425
|
-
| {
|
|
1426
|
-
type: "remote";
|
|
1427
|
-
local: false;
|
|
1428
|
-
};
|
|
1429
|
-
|
|
1430
|
-
const normalizeDev = (ctx: Context<any>, dev: WorkerProps["dev"]): Dev => {
|
|
1431
|
-
if (!ctx.scope.dev || ctx.phase === "delete" || dev === false) {
|
|
1432
|
-
return {
|
|
1433
|
-
type: "none",
|
|
1434
|
-
local: false,
|
|
1435
|
-
};
|
|
1436
|
-
}
|
|
1437
|
-
const devObj = dev === true ? {} : (dev ?? {});
|
|
1438
|
-
if ("command" in devObj) {
|
|
1439
|
-
// Commands are always local
|
|
1440
|
-
return {
|
|
1441
|
-
type: "command",
|
|
1442
|
-
...devObj,
|
|
1443
|
-
local: true,
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
|
-
if (devObj.remote === false || ctx.scope.dev === "prefer-local") {
|
|
1447
|
-
return {
|
|
1448
|
-
type: "miniflare",
|
|
1449
|
-
port: devObj.port,
|
|
1450
|
-
local: true,
|
|
1451
|
-
};
|
|
1452
|
-
}
|
|
1453
|
-
return {
|
|
1454
|
-
type: "remote",
|
|
1455
|
-
local: false,
|
|
1456
|
-
};
|
|
1457
|
-
};
|
|
1458
|
-
|
|
1459
1447
|
const assertUnique = <T, Key extends keyof T>(
|
|
1460
1448
|
inputs: T[],
|
|
1461
1449
|
key: Key,
|
|
@@ -1827,15 +1815,15 @@ async function createMiniflare(props: {
|
|
|
1827
1815
|
return await startPromise.value;
|
|
1828
1816
|
}
|
|
1829
1817
|
|
|
1830
|
-
function createDevCommand(props: {
|
|
1818
|
+
async function createDevCommand(props: {
|
|
1831
1819
|
id: string;
|
|
1832
1820
|
command: string;
|
|
1833
1821
|
cwd: string;
|
|
1834
1822
|
env: Record<string, string>;
|
|
1835
|
-
}) {
|
|
1823
|
+
}): Promise<{ url: string }> {
|
|
1836
1824
|
const persistFile = path.join(process.cwd(), ".alchemy", `${props.id}.pid`);
|
|
1837
|
-
if (
|
|
1838
|
-
const pid = Number.parseInt(
|
|
1825
|
+
if (await exists(persistFile)) {
|
|
1826
|
+
const pid = Number.parseInt(await fs.readFile(persistFile, "utf8"));
|
|
1839
1827
|
try {
|
|
1840
1828
|
// Actually kill the process if it's alive
|
|
1841
1829
|
process.kill(pid, "SIGTERM");
|
|
@@ -1843,12 +1831,20 @@ function createDevCommand(props: {
|
|
|
1843
1831
|
// ignore
|
|
1844
1832
|
}
|
|
1845
1833
|
try {
|
|
1846
|
-
|
|
1834
|
+
await fs.unlink(persistFile);
|
|
1847
1835
|
} catch {
|
|
1848
1836
|
// ignore
|
|
1849
1837
|
}
|
|
1850
1838
|
}
|
|
1851
1839
|
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
|
+
});
|
|
1852
1848
|
const proc = spawn(command[0], command.slice(1), {
|
|
1853
1849
|
cwd: props.cwd,
|
|
1854
1850
|
env: {
|
|
@@ -1859,21 +1855,61 @@ function createDevCommand(props: {
|
|
|
1859
1855
|
".alchemy",
|
|
1860
1856
|
"miniflare",
|
|
1861
1857
|
),
|
|
1858
|
+
// Force colors in the child process since we're piping output
|
|
1859
|
+
// FORCE_COLOR: "1",
|
|
1862
1860
|
},
|
|
1863
|
-
stdio: ["inherit", "
|
|
1861
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
1864
1862
|
});
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1863
|
+
const { url } = await new Promise<{ url: string }>((resolve, reject) => {
|
|
1864
|
+
let urlFound = false;
|
|
1865
|
+
let stdout = "";
|
|
1866
|
+
let stderr = "";
|
|
1867
|
+
const urlRegex =
|
|
1868
|
+
/http:\/\/(?:(?:localhost|0\.0\.0\.0|127\.0\.0\.1)|(?:\d{1,3}\.){3}\d{1,3}):\d+(?:\/)?/;
|
|
1869
|
+
|
|
1870
|
+
const parseOutput = (data: string) => {
|
|
1871
|
+
if (!urlFound) {
|
|
1872
|
+
const match = data.match(urlRegex);
|
|
1873
|
+
if (match) {
|
|
1874
|
+
urlFound = true;
|
|
1875
|
+
resolve({ url: match[0] });
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
};
|
|
1879
|
+
|
|
1880
|
+
// Handle stdout - parse for URL and write through with colors preserved
|
|
1881
|
+
proc.stdout?.on("data", (data) => {
|
|
1882
|
+
parseOutput(
|
|
1883
|
+
(stdout += data.toString().replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "")),
|
|
1884
|
+
);
|
|
1885
|
+
process.stdout.write(data);
|
|
1886
|
+
});
|
|
1887
|
+
|
|
1888
|
+
proc.stderr?.on("data", (data) => {
|
|
1889
|
+
parseOutput(
|
|
1890
|
+
(stderr += data.toString().replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "")),
|
|
1891
|
+
);
|
|
1892
|
+
process.stderr.write(data);
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1895
|
+
proc.on("error", (error) => {
|
|
1896
|
+
reject(error);
|
|
1897
|
+
});
|
|
1898
|
+
|
|
1899
|
+
cleanups.push(async () => {
|
|
1900
|
+
try {
|
|
1901
|
+
await fs.unlink(persistFile);
|
|
1902
|
+
} catch {
|
|
1903
|
+
// ignore
|
|
1904
|
+
}
|
|
1905
|
+
proc.kill();
|
|
1906
|
+
});
|
|
1872
1907
|
});
|
|
1873
1908
|
if (proc.pid) {
|
|
1874
|
-
|
|
1875
|
-
|
|
1909
|
+
await fs.mkdir(path.dirname(persistFile), { recursive: true });
|
|
1910
|
+
await fs.writeFile(persistFile, proc.pid.toString());
|
|
1876
1911
|
}
|
|
1912
|
+
return { url };
|
|
1877
1913
|
}
|
|
1878
1914
|
|
|
1879
1915
|
type PutWorkerOptions = Omit<WorkerProps, "entrypoint"> & {
|