alchemy 0.47.0 → 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/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/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/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.d.ts +7 -2
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +87 -70
- package/lib/cloudflare/worker.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/package.json +3 -2
- package/src/alchemy.ts +28 -49
- package/src/cloudflare/astro.ts +3 -0
- package/src/cloudflare/container.ts +1 -1
- package/src/cloudflare/nuxt.ts +3 -0
- 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.ts +107 -102
- package/src/scope.ts +21 -4
- package/src/util/safe-fetch.ts +1 -0
- 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/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/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
|
}
|
|
@@ -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", {
|
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
|
|
@@ -353,7 +348,6 @@ export interface BaseWorkerProps<
|
|
|
353
348
|
* the worker will be emulated locally and available at a randomly selected port.
|
|
354
349
|
*/
|
|
355
350
|
dev?:
|
|
356
|
-
| boolean
|
|
357
351
|
| {
|
|
358
352
|
/**
|
|
359
353
|
* Port to use for local development
|
|
@@ -365,11 +359,16 @@ export interface BaseWorkerProps<
|
|
|
365
359
|
* @default false
|
|
366
360
|
*/
|
|
367
361
|
remote?: boolean;
|
|
362
|
+
/** @internal */
|
|
363
|
+
command?: undefined;
|
|
368
364
|
}
|
|
369
365
|
| {
|
|
370
366
|
command: string;
|
|
371
|
-
url: string;
|
|
372
367
|
cwd?: string;
|
|
368
|
+
/** @internal */
|
|
369
|
+
port?: undefined;
|
|
370
|
+
/** @internal */
|
|
371
|
+
remote?: undefined;
|
|
373
372
|
};
|
|
374
373
|
|
|
375
374
|
/**
|
|
@@ -1047,8 +1046,6 @@ export const _Worker = Resource(
|
|
|
1047
1046
|
? props.namespace
|
|
1048
1047
|
: props.namespace?.namespaceName;
|
|
1049
1048
|
|
|
1050
|
-
const dev = normalizeDev(this, props.dev);
|
|
1051
|
-
|
|
1052
1049
|
const [bundle, error] = wrap(() =>
|
|
1053
1050
|
normalizeWorkerBundle({
|
|
1054
1051
|
entrypoint: props.entrypoint,
|
|
@@ -1067,34 +1064,45 @@ export const _Worker = Resource(
|
|
|
1067
1064
|
}),
|
|
1068
1065
|
);
|
|
1069
1066
|
|
|
1070
|
-
|
|
1067
|
+
// run locally if
|
|
1068
|
+
const local = this.scope.local && !props.dev?.remote;
|
|
1069
|
+
const watch = this.scope.watch;
|
|
1070
|
+
|
|
1071
|
+
if (local) {
|
|
1071
1072
|
let url: string | undefined;
|
|
1072
1073
|
if (error) {
|
|
1073
1074
|
throw error;
|
|
1074
1075
|
}
|
|
1075
1076
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
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
|
+
});
|
|
1098
1106
|
}
|
|
1099
1107
|
|
|
1100
1108
|
return this({
|
|
@@ -1218,7 +1226,7 @@ export const _Worker = Resource(
|
|
|
1218
1226
|
}
|
|
1219
1227
|
|
|
1220
1228
|
let putWorkerResult: PutWorkerResult;
|
|
1221
|
-
if (
|
|
1229
|
+
if (watch) {
|
|
1222
1230
|
// todo(john): clean this up and add log tail
|
|
1223
1231
|
const controller = new AbortController();
|
|
1224
1232
|
cleanups.push(() => controller.abort());
|
|
@@ -1436,57 +1444,6 @@ process.on("SIGINT", async () => {
|
|
|
1436
1444
|
process.exit(0);
|
|
1437
1445
|
});
|
|
1438
1446
|
|
|
1439
|
-
type Dev =
|
|
1440
|
-
| {
|
|
1441
|
-
type: "none";
|
|
1442
|
-
local: false;
|
|
1443
|
-
}
|
|
1444
|
-
| {
|
|
1445
|
-
type: "miniflare";
|
|
1446
|
-
port?: number;
|
|
1447
|
-
local: true;
|
|
1448
|
-
}
|
|
1449
|
-
| {
|
|
1450
|
-
type: "command";
|
|
1451
|
-
command: string;
|
|
1452
|
-
url: string;
|
|
1453
|
-
cwd?: string;
|
|
1454
|
-
local: true;
|
|
1455
|
-
}
|
|
1456
|
-
| {
|
|
1457
|
-
type: "remote";
|
|
1458
|
-
local: false;
|
|
1459
|
-
};
|
|
1460
|
-
|
|
1461
|
-
const normalizeDev = (ctx: Context<any>, dev: WorkerProps["dev"]): Dev => {
|
|
1462
|
-
if (!ctx.scope.dev || ctx.phase === "delete" || dev === false) {
|
|
1463
|
-
return {
|
|
1464
|
-
type: "none",
|
|
1465
|
-
local: false,
|
|
1466
|
-
};
|
|
1467
|
-
}
|
|
1468
|
-
const devObj = dev === true ? {} : (dev ?? {});
|
|
1469
|
-
if ("command" in devObj) {
|
|
1470
|
-
// Commands are always local
|
|
1471
|
-
return {
|
|
1472
|
-
type: "command",
|
|
1473
|
-
...devObj,
|
|
1474
|
-
local: true,
|
|
1475
|
-
};
|
|
1476
|
-
}
|
|
1477
|
-
if (devObj.remote === false || ctx.scope.dev === "prefer-local") {
|
|
1478
|
-
return {
|
|
1479
|
-
type: "miniflare",
|
|
1480
|
-
port: devObj.port,
|
|
1481
|
-
local: true,
|
|
1482
|
-
};
|
|
1483
|
-
}
|
|
1484
|
-
return {
|
|
1485
|
-
type: "remote",
|
|
1486
|
-
local: false,
|
|
1487
|
-
};
|
|
1488
|
-
};
|
|
1489
|
-
|
|
1490
1447
|
const assertUnique = <T, Key extends keyof T>(
|
|
1491
1448
|
inputs: T[],
|
|
1492
1449
|
key: Key,
|
|
@@ -1858,15 +1815,15 @@ async function createMiniflare(props: {
|
|
|
1858
1815
|
return await startPromise.value;
|
|
1859
1816
|
}
|
|
1860
1817
|
|
|
1861
|
-
function createDevCommand(props: {
|
|
1818
|
+
async function createDevCommand(props: {
|
|
1862
1819
|
id: string;
|
|
1863
1820
|
command: string;
|
|
1864
1821
|
cwd: string;
|
|
1865
1822
|
env: Record<string, string>;
|
|
1866
|
-
}) {
|
|
1823
|
+
}): Promise<{ url: string }> {
|
|
1867
1824
|
const persistFile = path.join(process.cwd(), ".alchemy", `${props.id}.pid`);
|
|
1868
|
-
if (
|
|
1869
|
-
const pid = Number.parseInt(
|
|
1825
|
+
if (await exists(persistFile)) {
|
|
1826
|
+
const pid = Number.parseInt(await fs.readFile(persistFile, "utf8"));
|
|
1870
1827
|
try {
|
|
1871
1828
|
// Actually kill the process if it's alive
|
|
1872
1829
|
process.kill(pid, "SIGTERM");
|
|
@@ -1874,12 +1831,20 @@ function createDevCommand(props: {
|
|
|
1874
1831
|
// ignore
|
|
1875
1832
|
}
|
|
1876
1833
|
try {
|
|
1877
|
-
|
|
1834
|
+
await fs.unlink(persistFile);
|
|
1878
1835
|
} catch {
|
|
1879
1836
|
// ignore
|
|
1880
1837
|
}
|
|
1881
1838
|
}
|
|
1882
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
|
+
});
|
|
1883
1848
|
const proc = spawn(command[0], command.slice(1), {
|
|
1884
1849
|
cwd: props.cwd,
|
|
1885
1850
|
env: {
|
|
@@ -1890,21 +1855,61 @@ function createDevCommand(props: {
|
|
|
1890
1855
|
".alchemy",
|
|
1891
1856
|
"miniflare",
|
|
1892
1857
|
),
|
|
1858
|
+
// Force colors in the child process since we're piping output
|
|
1859
|
+
// FORCE_COLOR: "1",
|
|
1893
1860
|
},
|
|
1894
|
-
stdio: ["inherit", "
|
|
1861
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
1895
1862
|
});
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
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
|
+
});
|
|
1903
1907
|
});
|
|
1904
1908
|
if (proc.pid) {
|
|
1905
|
-
|
|
1906
|
-
|
|
1909
|
+
await fs.mkdir(path.dirname(persistFile), { recursive: true });
|
|
1910
|
+
await fs.writeFile(persistFile, proc.pid.toString());
|
|
1907
1911
|
}
|
|
1912
|
+
return { url };
|
|
1908
1913
|
}
|
|
1909
1914
|
|
|
1910
1915
|
type PutWorkerOptions = Omit<WorkerProps, "entrypoint"> & {
|
package/src/scope.ts
CHANGED
|
@@ -38,7 +38,18 @@ export interface ScopeOptions {
|
|
|
38
38
|
stateStore?: StateStoreType;
|
|
39
39
|
quiet?: boolean;
|
|
40
40
|
phase?: Phase;
|
|
41
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Determines if resources should be simulated locally (where possible)
|
|
43
|
+
*
|
|
44
|
+
* @default - `true` if ran with `alchemy dev` or `bun ./alchemy.run.ts --dev`
|
|
45
|
+
*/
|
|
46
|
+
local?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Determines if local changes to resources should be reactively pushed to the local or remote environment.
|
|
49
|
+
*
|
|
50
|
+
* @default - `true` if ran with `alchemy dev`, `alchemy watch`, `bun --watch ./alchemy.run.ts`
|
|
51
|
+
*/
|
|
52
|
+
watch?: boolean;
|
|
42
53
|
telemetryClient?: ITelemetryClient;
|
|
43
54
|
logger?: LoggerApi;
|
|
44
55
|
}
|
|
@@ -107,7 +118,8 @@ export class Scope {
|
|
|
107
118
|
public readonly stateStore: StateStoreType;
|
|
108
119
|
public readonly quiet: boolean;
|
|
109
120
|
public readonly phase: Phase;
|
|
110
|
-
public readonly
|
|
121
|
+
public readonly local: boolean;
|
|
122
|
+
public readonly watch: boolean;
|
|
111
123
|
public readonly logger: LoggerApi;
|
|
112
124
|
public readonly telemetryClient: ITelemetryClient;
|
|
113
125
|
public readonly dataMutex: AsyncMutex;
|
|
@@ -162,9 +174,10 @@ export class Scope {
|
|
|
162
174
|
options.logger,
|
|
163
175
|
);
|
|
164
176
|
|
|
165
|
-
this.
|
|
177
|
+
this.local = options.local ?? this.parent?.local ?? false;
|
|
178
|
+
this.watch = options.watch ?? this.parent?.watch ?? false;
|
|
166
179
|
|
|
167
|
-
if (this.
|
|
180
|
+
if (this.local) {
|
|
168
181
|
this.logger.warnOnce(
|
|
169
182
|
"Development mode is in beta. Please report any issues to https://github.com/sam-goodwin/alchemy/issues.",
|
|
170
183
|
);
|
|
@@ -415,6 +428,10 @@ export class Scope {
|
|
|
415
428
|
await this.rootTelemetryClient?.finalize()?.catch((error) => {
|
|
416
429
|
this.logger.warn("Telemetry finalization failed:", error);
|
|
417
430
|
});
|
|
431
|
+
|
|
432
|
+
if (!this.parent && process.env.ALCHEMY_TEST_KILL_ON_FINALIZE) {
|
|
433
|
+
process.exit(0);
|
|
434
|
+
}
|
|
418
435
|
}
|
|
419
436
|
|
|
420
437
|
public async destroyPendingDeletions() {
|
package/src/util/safe-fetch.ts
CHANGED
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
"composite": true,
|
|
12
12
|
"strict": true,
|
|
13
13
|
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
|
14
|
-
"types": [
|
|
14
|
+
"types": [
|
|
15
|
+
"vite/client",
|
|
16
|
+
"@cloudflare/workers-types",
|
|
17
|
+
"./types/env.d.ts"
|
|
18
|
+
],
|
|
15
19
|
"target": "ES2022",
|
|
16
20
|
"module": "ES2022",
|
|
17
21
|
"moduleResolution": "bundler",
|