alchemy 0.12.9 → 0.12.10
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/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +17 -12
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/bucket.d.ts +4 -0
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +13 -5
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/external.d.ts +2 -0
- package/lib/cloudflare/external.d.ts.map +1 -0
- package/lib/cloudflare/external.js +30 -0
- package/lib/cloudflare/external.js.map +1 -0
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/pipeline.d.ts +7 -1
- package/lib/cloudflare/pipeline.d.ts.map +1 -1
- package/lib/cloudflare/pipeline.js +1 -1
- package/lib/cloudflare/pipeline.js.map +1 -1
- package/lib/cloudflare/redwood.d.ts +1 -33
- package/lib/cloudflare/redwood.d.ts.map +1 -1
- package/lib/cloudflare/redwood.js +7 -46
- package/lib/cloudflare/redwood.js.map +1 -1
- package/lib/cloudflare/website.d.ts +4 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +10 -2
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +17 -33
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +6 -0
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +1 -1
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/esbuild/bundle.d.ts +4 -4
- package/lib/esbuild/bundle.d.ts.map +1 -1
- package/lib/esbuild/bundle.js +9 -7
- package/lib/esbuild/bundle.js.map +1 -1
- package/lib/os/exec.d.ts +15 -15
- package/lib/os/exec.d.ts.map +1 -1
- package/lib/os/exec.js +47 -26
- package/lib/os/exec.js.map +1 -1
- package/package.json +1 -1
- package/src/cloudflare/api.ts +15 -13
- package/src/cloudflare/bucket.ts +16 -5
- package/src/cloudflare/external.ts +30 -0
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/pipeline.ts +9 -2
- package/src/cloudflare/redwood.ts +8 -46
- package/src/cloudflare/website.ts +19 -3
- package/src/cloudflare/worker.ts +22 -42
- package/src/cloudflare/wrangler.json.ts +8 -1
- package/src/esbuild/bundle.ts +11 -9
- package/src/os/exec.ts +61 -34
package/src/cloudflare/bucket.ts
CHANGED
|
@@ -79,6 +79,11 @@ export interface BucketProps {
|
|
|
79
79
|
* Secret Access Key to use for the bucket
|
|
80
80
|
*/
|
|
81
81
|
secretAccessKey?: Secret;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Whether to adopt an existing bucket
|
|
85
|
+
*/
|
|
86
|
+
adopt?: boolean;
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
/**
|
|
@@ -163,7 +168,6 @@ export const R2Bucket = Resource(
|
|
|
163
168
|
const bucketName = props.name || this.id;
|
|
164
169
|
|
|
165
170
|
if (this.phase === "delete") {
|
|
166
|
-
console.log("Deleting R2 bucket:", bucketName);
|
|
167
171
|
if (props.delete !== false) {
|
|
168
172
|
if (props.empty) {
|
|
169
173
|
console.log("Emptying R2 bucket:", bucketName);
|
|
@@ -175,8 +179,6 @@ export const R2Bucket = Resource(
|
|
|
175
179
|
await emptyBucket(r2Client, bucketName, props.jurisdiction);
|
|
176
180
|
}
|
|
177
181
|
|
|
178
|
-
// Delete R2 bucket
|
|
179
|
-
console.log("Deleting R2 bucket:", bucketName);
|
|
180
182
|
await deleteBucket(api, bucketName, props);
|
|
181
183
|
}
|
|
182
184
|
|
|
@@ -184,8 +186,17 @@ export const R2Bucket = Resource(
|
|
|
184
186
|
return this.destroy();
|
|
185
187
|
} else {
|
|
186
188
|
if (this.phase === "create") {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
+
try {
|
|
190
|
+
await createBucket(api, bucketName, props);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
if (err instanceof CloudflareApiError && err.status === 409) {
|
|
193
|
+
if (!props.adopt) {
|
|
194
|
+
throw err;
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
throw err;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
189
200
|
}
|
|
190
201
|
|
|
191
202
|
await updatePublicAccess(
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// https://developers.cloudflare.com/workers/runtime-apis/nodejs/#supported-nodejs-apis
|
|
2
|
+
const nodejs_compat = [
|
|
3
|
+
"node:async_hooks",
|
|
4
|
+
"node:assert",
|
|
5
|
+
"node:buffer",
|
|
6
|
+
"node:console",
|
|
7
|
+
"node:crypto",
|
|
8
|
+
"node:diagnostics_channel",
|
|
9
|
+
"node:dns",
|
|
10
|
+
"node:events",
|
|
11
|
+
"node:inspector",
|
|
12
|
+
"node:net",
|
|
13
|
+
"node:path",
|
|
14
|
+
"node:perf_hooks",
|
|
15
|
+
"node:process",
|
|
16
|
+
"node:querystring",
|
|
17
|
+
"node:stream",
|
|
18
|
+
"node:string_decoder",
|
|
19
|
+
"node:timers",
|
|
20
|
+
"node:tls", // partially supported
|
|
21
|
+
"node:url",
|
|
22
|
+
"node:util",
|
|
23
|
+
"node:zlib",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export const external = [
|
|
27
|
+
...nodejs_compat,
|
|
28
|
+
...nodejs_compat.map((p) => p.split(":")[1]),
|
|
29
|
+
"cloudflare:workers",
|
|
30
|
+
];
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./custom-domain.js";
|
|
|
9
9
|
export * from "./d1-database.js";
|
|
10
10
|
export * from "./dns-records.js";
|
|
11
11
|
export * from "./durable-object-namespace.js";
|
|
12
|
+
export * from "./external.js";
|
|
12
13
|
export * from "./hyperdrive.js";
|
|
13
14
|
export * from "./kv-namespace.js";
|
|
14
15
|
export * from "./local-dev-cloudflare-shim.js";
|
|
@@ -181,8 +181,10 @@ export type PipelineDestination = R2DestinationConfig;
|
|
|
181
181
|
export interface PipelineProps extends CloudflareApiOptions {
|
|
182
182
|
/**
|
|
183
183
|
* Name of the pipeline
|
|
184
|
+
*
|
|
185
|
+
* @default id
|
|
184
186
|
*/
|
|
185
|
-
name
|
|
187
|
+
name?: string;
|
|
186
188
|
|
|
187
189
|
/**
|
|
188
190
|
* Source configuration for the pipeline
|
|
@@ -232,6 +234,11 @@ export interface Pipeline<T extends PipelineRecord = PipelineRecord>
|
|
|
232
234
|
*/
|
|
233
235
|
id: string;
|
|
234
236
|
|
|
237
|
+
/**
|
|
238
|
+
* The name of the pipeline
|
|
239
|
+
*/
|
|
240
|
+
name: string;
|
|
241
|
+
|
|
235
242
|
/**
|
|
236
243
|
* HTTP endpoint URL for the pipeline
|
|
237
244
|
*/
|
|
@@ -315,7 +322,7 @@ export const Pipeline = Resource("cloudflare::Pipeline", async function <
|
|
|
315
322
|
Pipeline<T>
|
|
316
323
|
> {
|
|
317
324
|
const api = await createCloudflareApi(props);
|
|
318
|
-
const pipelineName = props.name
|
|
325
|
+
const pipelineName = props.name ?? id;
|
|
319
326
|
|
|
320
327
|
if (this.phase === "delete") {
|
|
321
328
|
if (props.delete !== false) {
|
|
@@ -23,7 +23,7 @@ export type Redwood<B extends Bindings> = B extends { ASSETS: any }
|
|
|
23
23
|
*
|
|
24
24
|
* @example
|
|
25
25
|
* // Deploy with a database binding
|
|
26
|
-
* import { D1Database } from
|
|
26
|
+
* import { D1Database } from alchemy/cloudflare";
|
|
27
27
|
*
|
|
28
28
|
* const database = await D1Database("redwood-db");
|
|
29
29
|
*
|
|
@@ -33,38 +33,6 @@ export type Redwood<B extends Bindings> = B extends { ASSETS: any }
|
|
|
33
33
|
* }
|
|
34
34
|
* });
|
|
35
35
|
*
|
|
36
|
-
* @example
|
|
37
|
-
* // Deploy with custom build command and environment variables
|
|
38
|
-
* const redwoodApp = await Redwood("custom-redwood", {
|
|
39
|
-
* command: "bun run test && RWSDK_DEPLOY=1 bun run build:production",
|
|
40
|
-
* bindings: {
|
|
41
|
-
* API_KEY: alchemy.secret("api-key-secret")
|
|
42
|
-
* },
|
|
43
|
-
* vars: {
|
|
44
|
-
* NODE_ENV: "production",
|
|
45
|
-
* APP_ENV: "staging"
|
|
46
|
-
* }
|
|
47
|
-
* });
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* // Deploy with custom paths and additional compatibility flags
|
|
51
|
-
* const redwoodApp = await Redwood("redwood-custom-paths", {
|
|
52
|
-
* main: "custom/worker.js",
|
|
53
|
-
* assets: "custom/static",
|
|
54
|
-
* compatibilityFlags: ["nodejs_compat", "fetch_refused_to_set_cookies"]
|
|
55
|
-
* });
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* // Deploy with custom bundle configuration
|
|
59
|
-
* const redwoodApp = await Redwood("redwood-custom-bundle", {
|
|
60
|
-
* bundle: {
|
|
61
|
-
* options: {
|
|
62
|
-
* external: ["node:events", "node:stream", "node:crypto"],
|
|
63
|
-
* format: "esm"
|
|
64
|
-
* }
|
|
65
|
-
* }
|
|
66
|
-
* });
|
|
67
|
-
*
|
|
68
36
|
* @param id - Unique identifier for the RedwoodJS application
|
|
69
37
|
* @param props - Configuration properties for the RedwoodJS deployment
|
|
70
38
|
* @returns A Cloudflare Worker resource representing the deployed RedwoodJS application
|
|
@@ -76,21 +44,15 @@ export async function Redwood<B extends Bindings>(
|
|
|
76
44
|
return Website(id, {
|
|
77
45
|
...props,
|
|
78
46
|
command: props?.command ?? "bun run clean && RWSDK_DEPLOY=1 bun run build",
|
|
79
|
-
wrangler:
|
|
80
|
-
|
|
47
|
+
wrangler:
|
|
48
|
+
props?.wrangler === false
|
|
49
|
+
? false
|
|
50
|
+
: {
|
|
51
|
+
main: props?.main ?? path.join("src", "worker.tsx"),
|
|
52
|
+
},
|
|
53
|
+
main: props?.main ?? path.join("dist", "worker", "worker.js"),
|
|
81
54
|
assets: props?.assets ?? path.join("dist", "client"),
|
|
82
55
|
compatibilityFlags: ["nodejs_compat", ...(props?.compatibilityFlags ?? [])],
|
|
83
56
|
compatibilityDate: props?.compatibilityDate ?? "2025-04-02",
|
|
84
|
-
bundle: {
|
|
85
|
-
...props?.bundle,
|
|
86
|
-
options: {
|
|
87
|
-
...props?.bundle?.options,
|
|
88
|
-
external: [
|
|
89
|
-
"node:events",
|
|
90
|
-
"node:stream",
|
|
91
|
-
...(props?.bundle?.options?.external ?? []),
|
|
92
|
-
],
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
57
|
});
|
|
96
58
|
}
|
|
@@ -45,7 +45,14 @@ export interface WebsiteProps<B extends Bindings>
|
|
|
45
45
|
*
|
|
46
46
|
* @default - no wrangler.jsonc file is written
|
|
47
47
|
*/
|
|
48
|
-
wrangler?:
|
|
48
|
+
wrangler?:
|
|
49
|
+
| boolean
|
|
50
|
+
| string
|
|
51
|
+
| {
|
|
52
|
+
path?: string;
|
|
53
|
+
// override main
|
|
54
|
+
main?: string;
|
|
55
|
+
};
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
export type Website<B extends Bindings> = B extends { ASSETS: any }
|
|
@@ -66,9 +73,17 @@ export async function Website<B extends Bindings>(
|
|
|
66
73
|
|
|
67
74
|
const cwd = path.resolve(props.cwd || process.cwd());
|
|
68
75
|
const fileName =
|
|
69
|
-
typeof props.wrangler === "boolean"
|
|
76
|
+
typeof props.wrangler === "boolean"
|
|
77
|
+
? "wrangler.jsonc"
|
|
78
|
+
: typeof props.wrangler === "string"
|
|
79
|
+
? props.wrangler
|
|
80
|
+
: (props.wrangler?.path ?? "wrangler.jsonc");
|
|
70
81
|
const wranglerPath =
|
|
71
82
|
fileName && path.relative(cwd, path.join(cwd, fileName));
|
|
83
|
+
const wranglerMain =
|
|
84
|
+
typeof props.wrangler === "object"
|
|
85
|
+
? (props.wrangler.main ?? props.main)
|
|
86
|
+
: props.main;
|
|
72
87
|
|
|
73
88
|
if (props.wrangler) {
|
|
74
89
|
try {
|
|
@@ -79,7 +94,7 @@ export async function Website<B extends Bindings>(
|
|
|
79
94
|
JSON.stringify(
|
|
80
95
|
{
|
|
81
96
|
name: id,
|
|
82
|
-
main:
|
|
97
|
+
main: wranglerMain,
|
|
83
98
|
compatibility_date: new Date().toISOString().split("T")[0],
|
|
84
99
|
},
|
|
85
100
|
null,
|
|
@@ -128,6 +143,7 @@ export default {
|
|
|
128
143
|
await WranglerJson("wrangler.jsonc", {
|
|
129
144
|
path: wranglerPath,
|
|
130
145
|
worker,
|
|
146
|
+
main: wranglerMain,
|
|
131
147
|
});
|
|
132
148
|
}
|
|
133
149
|
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type { Bindings, WorkerBindingSpec } from "./bindings.js";
|
|
|
17
17
|
import type { Bound } from "./bound.js";
|
|
18
18
|
import type { DurableObjectNamespace } from "./durable-object-namespace.js";
|
|
19
19
|
import { type EventSource, isQueueEventSource } from "./event-source.js";
|
|
20
|
+
import { external } from "./external.js";
|
|
20
21
|
import {
|
|
21
22
|
QueueConsumer,
|
|
22
23
|
deleteQueueConsumer,
|
|
@@ -576,8 +577,6 @@ async function putWorker(
|
|
|
576
577
|
})
|
|
577
578
|
);
|
|
578
579
|
|
|
579
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
580
|
-
|
|
581
580
|
// Upload worker script with bindings
|
|
582
581
|
const uploadResponse = await api.put(
|
|
583
582
|
`/accounts/${api.accountId}/workers/scripts/${workerName}`,
|
|
@@ -591,36 +590,22 @@ async function putWorker(
|
|
|
591
590
|
|
|
592
591
|
// Check if the upload was successful
|
|
593
592
|
if (!uploadResponse.ok) {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
if (
|
|
601
|
-
uploadResponse.status === 400 &&
|
|
602
|
-
errorMessage.includes("not found")
|
|
603
|
-
) {
|
|
604
|
-
throw new NotFoundError(errorMessage);
|
|
605
|
-
}
|
|
606
|
-
throw new Error(errorMessage);
|
|
593
|
+
await handleApiError(
|
|
594
|
+
uploadResponse,
|
|
595
|
+
"uploading worker script",
|
|
596
|
+
"worker",
|
|
597
|
+
workerName
|
|
598
|
+
);
|
|
607
599
|
}
|
|
608
600
|
|
|
609
601
|
return formData;
|
|
610
602
|
},
|
|
611
|
-
(err) => err
|
|
603
|
+
(err) => err.status === 404 || err.status === 500 || err.status === 503,
|
|
612
604
|
10,
|
|
613
605
|
100
|
|
614
606
|
);
|
|
615
607
|
}
|
|
616
608
|
|
|
617
|
-
class NotFoundError extends Error {
|
|
618
|
-
constructor(message: string) {
|
|
619
|
-
super(message);
|
|
620
|
-
this.name = "NotFoundError";
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
|
|
624
609
|
interface WorkerMetadata {
|
|
625
610
|
compatibility_date: string;
|
|
626
611
|
compatibility_flags?: string[];
|
|
@@ -889,6 +874,9 @@ async function prepareWorkerMetadata<B extends Bindings>(
|
|
|
889
874
|
// For service worker format (CJS)
|
|
890
875
|
meta.body_part = scriptName;
|
|
891
876
|
}
|
|
877
|
+
if (process.env.DEBUG) {
|
|
878
|
+
console.log(meta);
|
|
879
|
+
}
|
|
892
880
|
return meta;
|
|
893
881
|
}
|
|
894
882
|
|
|
@@ -931,34 +919,26 @@ async function assertWorkerDoesNotExist<B extends Bindings>(
|
|
|
931
919
|
}
|
|
932
920
|
|
|
933
921
|
async function bundleWorkerScript<B extends Bindings>(props: WorkerProps) {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
// Create and use a Bundle resource with worker-optimized configuration
|
|
937
|
-
const defaultBundleOptions: Omit<BundleProps, "entryPoint"> = {
|
|
922
|
+
const bundle = await Bundle("bundle", {
|
|
923
|
+
entryPoint: props.entrypoint!,
|
|
938
924
|
format: props.format === "cjs" ? "cjs" : "esm", // Use the specified format or default to ESM
|
|
939
|
-
target: "
|
|
940
|
-
platform: "
|
|
925
|
+
target: "esnext",
|
|
926
|
+
platform: "neutral",
|
|
941
927
|
minify: true,
|
|
928
|
+
...(props.bundle || {}),
|
|
942
929
|
options: {
|
|
930
|
+
...(props.bundle?.options || {}),
|
|
943
931
|
keepNames: true, // Important for Durable Object classes
|
|
944
932
|
loader: {
|
|
945
933
|
".sql": "text",
|
|
946
934
|
".json": "json",
|
|
947
935
|
},
|
|
948
936
|
},
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
...(props.bundle || {}),
|
|
955
|
-
};
|
|
956
|
-
|
|
957
|
-
// Create the bundle
|
|
958
|
-
const bundle = await Bundle("bundle", {
|
|
959
|
-
entryPoint: props.entrypoint!,
|
|
960
|
-
...bundleOptions,
|
|
961
|
-
external: [...(bundleOptions.external ?? []), "cloudflare:workers"],
|
|
937
|
+
external: [
|
|
938
|
+
...external,
|
|
939
|
+
...(props.bundle?.external ?? []),
|
|
940
|
+
...(props.bundle?.options?.external ?? []),
|
|
941
|
+
],
|
|
962
942
|
});
|
|
963
943
|
|
|
964
944
|
try {
|
|
@@ -23,6 +23,13 @@ export interface WranglerJsonProps {
|
|
|
23
23
|
* @default cwd/wrangler.json
|
|
24
24
|
*/
|
|
25
25
|
path?: string;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The main entry point for the worker
|
|
29
|
+
*
|
|
30
|
+
* @default worker.entrypoint
|
|
31
|
+
*/
|
|
32
|
+
main?: string;
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
/**
|
|
@@ -75,7 +82,7 @@ export const WranglerJson = Resource(
|
|
|
75
82
|
const spec: WranglerJsonSpec = {
|
|
76
83
|
name: worker.name,
|
|
77
84
|
// Use entrypoint as main if it exists
|
|
78
|
-
main: worker.entrypoint,
|
|
85
|
+
main: props.main ?? worker.entrypoint,
|
|
79
86
|
// see: https://developers.cloudflare.com/workers/configuration/compatibility-dates/
|
|
80
87
|
compatibility_date: worker.compatibilityDate,
|
|
81
88
|
compatibility_flags: props.worker.compatibilityFlags,
|
package/src/esbuild/bundle.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { Resource } from "../resource.js";
|
|
|
8
8
|
/**
|
|
9
9
|
* Properties for creating or updating an esbuild bundle
|
|
10
10
|
*/
|
|
11
|
-
export interface BundleProps {
|
|
11
|
+
export interface BundleProps extends Partial<esbuild.BuildOptions> {
|
|
12
12
|
/**
|
|
13
13
|
* Entry point for the bundle
|
|
14
14
|
* Path to the source file to bundle (e.g., "src/handler.ts")
|
|
@@ -143,7 +143,7 @@ export const Bundle = Resource(
|
|
|
143
143
|
|
|
144
144
|
const result = await bundle(props);
|
|
145
145
|
|
|
146
|
-
const bundlePath = Object.entries(result.metafile
|
|
146
|
+
const bundlePath = Object.entries(result.metafile!.outputs).find(
|
|
147
147
|
([_, output]) => {
|
|
148
148
|
if (output.entryPoint === undefined) {
|
|
149
149
|
return false;
|
|
@@ -186,7 +186,9 @@ export const Bundle = Resource(
|
|
|
186
186
|
);
|
|
187
187
|
|
|
188
188
|
export async function bundle(props: BundleProps) {
|
|
189
|
-
|
|
189
|
+
const { entryPoint, options: _, ...rest } = props;
|
|
190
|
+
const options = {
|
|
191
|
+
...rest,
|
|
190
192
|
...props.options,
|
|
191
193
|
write: !(props.outdir === undefined && props.outfile === undefined),
|
|
192
194
|
// write:
|
|
@@ -200,12 +202,12 @@ export async function bundle(props: BundleProps) {
|
|
|
200
202
|
target: props.target,
|
|
201
203
|
minify: props.minify,
|
|
202
204
|
sourcemap: props.sourcemap,
|
|
203
|
-
external: [
|
|
204
|
-
"node:*",
|
|
205
|
-
...(props.external ?? []),
|
|
206
|
-
...(props.options?.external ?? []),
|
|
207
|
-
],
|
|
205
|
+
external: [...(props.external ?? []), ...(props.options?.external ?? [])],
|
|
208
206
|
platform: props.platform,
|
|
209
207
|
metafile: true,
|
|
210
|
-
}
|
|
208
|
+
};
|
|
209
|
+
if (process.env.DEBUG) {
|
|
210
|
+
console.log(options);
|
|
211
|
+
}
|
|
212
|
+
return await esbuild.build(options);
|
|
211
213
|
}
|
package/src/os/exec.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { promisify } from "node:util";
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
3
2
|
import type { Context } from "../context.js";
|
|
4
3
|
import { Resource } from "../resource.js";
|
|
5
4
|
|
|
6
|
-
const execAsync = promisify(exec);
|
|
7
|
-
|
|
8
5
|
/**
|
|
9
6
|
* Properties for executing a shell command
|
|
10
7
|
*/
|
|
@@ -32,15 +29,15 @@ export interface ExecProps {
|
|
|
32
29
|
env?: Record<string, string>;
|
|
33
30
|
|
|
34
31
|
/**
|
|
35
|
-
*
|
|
36
|
-
* @default 1024 * 1024 (1MB)
|
|
32
|
+
* Whether to throw an error if the command exits with a non-zero status
|
|
37
33
|
*/
|
|
38
|
-
|
|
34
|
+
throwOnError?: boolean;
|
|
39
35
|
|
|
40
36
|
/**
|
|
41
|
-
* Whether to
|
|
37
|
+
* Whether to inherit stdio from parent process
|
|
38
|
+
* @default true
|
|
42
39
|
*/
|
|
43
|
-
|
|
40
|
+
inheritStdio?: boolean;
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
/**
|
|
@@ -58,12 +55,12 @@ export interface Exec extends Resource<"os::Exec">, ExecProps {
|
|
|
58
55
|
exitCode: number;
|
|
59
56
|
|
|
60
57
|
/**
|
|
61
|
-
* Standard output from the command
|
|
58
|
+
* Standard output from the command (only available when inheritStdio is false)
|
|
62
59
|
*/
|
|
63
60
|
stdout: string;
|
|
64
61
|
|
|
65
62
|
/**
|
|
66
|
-
* Standard error from the command
|
|
63
|
+
* Standard error from the command (only available when inheritStdio is false)
|
|
67
64
|
*/
|
|
68
65
|
stderr: string;
|
|
69
66
|
|
|
@@ -82,11 +79,18 @@ export interface Exec extends Resource<"os::Exec">, ExecProps {
|
|
|
82
79
|
* Execute a shell command
|
|
83
80
|
*
|
|
84
81
|
* @example
|
|
85
|
-
* // Run a simple command
|
|
82
|
+
* // Run a simple command with inherited stdio (default)
|
|
86
83
|
* const result = await Exec("list-files", {
|
|
87
84
|
* command: "ls -la"
|
|
88
85
|
* });
|
|
89
86
|
*
|
|
87
|
+
* @example
|
|
88
|
+
* // Run a command and capture output instead of inheriting stdio
|
|
89
|
+
* const result = await Exec("list-files", {
|
|
90
|
+
* command: "ls -la",
|
|
91
|
+
* inheritStdio: false
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
90
94
|
* console.log(result.stdout);
|
|
91
95
|
*
|
|
92
96
|
* @example
|
|
@@ -98,13 +102,6 @@ export interface Exec extends Resource<"os::Exec">, ExecProps {
|
|
|
98
102
|
* });
|
|
99
103
|
*
|
|
100
104
|
* @example
|
|
101
|
-
* // Run a command with a larger buffer for output
|
|
102
|
-
* const logs = await Exec("get-logs", {
|
|
103
|
-
* command: "cat large-log-file.log",
|
|
104
|
-
* maxBuffer: 10 * 1024 * 1024 // 10MB
|
|
105
|
-
* });
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
105
|
* // Run a memoized command that only re-executes when the command changes
|
|
109
106
|
* const memoizedCmd = await Exec("status-check", {
|
|
110
107
|
* command: "git status",
|
|
@@ -143,31 +140,61 @@ export const Exec = Resource(
|
|
|
143
140
|
let stderr = "";
|
|
144
141
|
let exitCode = 0;
|
|
145
142
|
|
|
143
|
+
// Default to inheriting stdio unless explicitly set to false
|
|
144
|
+
const inheritStdio = props.inheritStdio !== false;
|
|
145
|
+
|
|
146
146
|
try {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
// Parse the command into command and args
|
|
148
|
+
const [cmd, ...args] = props.command.split(/\s+/);
|
|
149
|
+
|
|
150
|
+
// Use spawn for better stdio control
|
|
151
|
+
const childProcess = spawn(cmd, args, {
|
|
150
152
|
cwd: props.cwd || process.cwd(),
|
|
151
153
|
env: { ...process.env, ...props.env },
|
|
152
|
-
|
|
154
|
+
shell: true, // Use shell to handle complex commands
|
|
155
|
+
stdio: inheritStdio ? "inherit" : "pipe", // Inherit stdio when requested
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (!inheritStdio) {
|
|
159
|
+
// If not inheriting stdio, collect output manually
|
|
160
|
+
childProcess.stdout?.on("data", (data) => {
|
|
161
|
+
stdout += data.toString();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
childProcess.stderr?.on("data", (data) => {
|
|
165
|
+
stderr += data.toString();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Wait for the process to complete
|
|
170
|
+
exitCode = await new Promise<number>((resolve, reject) => {
|
|
171
|
+
childProcess.on("close", (code) => {
|
|
172
|
+
resolve(code || 0);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
childProcess.on("error", (err) => {
|
|
176
|
+
if (props.throwOnError) {
|
|
177
|
+
reject(err);
|
|
178
|
+
} else {
|
|
179
|
+
stderr += err.toString();
|
|
180
|
+
resolve(1);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
153
183
|
});
|
|
154
184
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
185
|
+
if (exitCode !== 0 && props.throwOnError) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Command failed with exit code ${exitCode}: ${props.command}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
159
190
|
} catch (error: any) {
|
|
160
|
-
console.log("error", error);
|
|
161
191
|
if (props.throwOnError) {
|
|
162
192
|
throw error;
|
|
163
193
|
}
|
|
164
194
|
|
|
165
195
|
// If not throwing, capture the error information
|
|
166
|
-
exitCode =
|
|
167
|
-
|
|
168
|
-
stderr = error.stderr || String(error);
|
|
169
|
-
console.log(stdout);
|
|
170
|
-
console.error(stderr);
|
|
196
|
+
exitCode = 1;
|
|
197
|
+
stderr += String(error);
|
|
171
198
|
}
|
|
172
199
|
|
|
173
200
|
// Return the execution result
|
|
@@ -176,9 +203,9 @@ export const Exec = Resource(
|
|
|
176
203
|
command: props.command,
|
|
177
204
|
cwd: props.cwd,
|
|
178
205
|
env: props.env,
|
|
179
|
-
maxBuffer: props.maxBuffer,
|
|
180
206
|
throwOnError: props.throwOnError,
|
|
181
207
|
memoize: props.memoize,
|
|
208
|
+
inheritStdio,
|
|
182
209
|
exitCode,
|
|
183
210
|
stdout,
|
|
184
211
|
stderr,
|