alchemy 0.11.4 → 0.12.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/alchemy.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { destroy } from "./destroy";
2
+ import { env } from "./env";
2
3
  import { Scope } from "./scope";
3
4
  import { secret } from "./secret";
4
5
  import type { StateStoreType } from "./state";
@@ -143,5 +144,4 @@ declare function run<T>(...args: [id: string, fn: (this: Scope, scope: Scope) =>
143
144
  options: RunOptions,
144
145
  fn: (this: Scope, scope: Scope) => Promise<T>
145
146
  ]): Promise<T>;
146
- export declare function env<T = string>(name: string, value?: T | undefined, error?: string): Promise<T>;
147
147
  export {};
package/lib/alchemy.js CHANGED
@@ -1,9 +1,14 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { destroy, DestroyedSignal } from "./destroy";
4
+ import { env } from "./env";
4
5
  import { Scope } from "./scope";
5
6
  import { secret } from "./secret";
6
7
  export const alchemy = _alchemy;
8
+ _alchemy.destroy = destroy;
9
+ _alchemy.run = run;
10
+ _alchemy.secret = secret;
11
+ _alchemy.env = env;
7
12
  /**
8
13
  * Implementation of the alchemy function that handles both application scoping
9
14
  * and template string interpolation.
@@ -119,10 +124,6 @@ async function _alchemy(...args) {
119
124
  ].join("\n");
120
125
  }
121
126
  }
122
- _alchemy.destroy = destroy;
123
- _alchemy.run = run;
124
- _alchemy.secret = secret;
125
- _alchemy.env = env;
126
127
  /**
127
128
  * Run a function in a new scope asynchronously.
128
129
  * Useful for isolating secret handling with a specific password.
@@ -183,23 +184,3 @@ async function run(...args) {
183
184
  await _scope.finalize();
184
185
  }
185
186
  }
186
- export async function env(name, value, error) {
187
- if (value !== undefined) {
188
- return value;
189
- }
190
- else if (typeof process !== undefined) {
191
- // we are in a node environment
192
- return process.env[name];
193
- }
194
- else {
195
- // we are in a browser environment
196
- try {
197
- const { env } = await import("cloudflare:workers");
198
- if (name in env) {
199
- return env[name];
200
- }
201
- }
202
- catch (error) { }
203
- }
204
- throw new Error(error ?? `Environment variable ${name} is not set`);
205
- }
@@ -13,9 +13,11 @@ export * from "./permission-groups";
13
13
  export * from "./pipeline";
14
14
  export * from "./queue";
15
15
  export * from "./r2-rest-state-store";
16
+ export * from "./tanstack-start";
16
17
  export * from "./vectorize-index";
17
18
  export * from "./vectorize-metadata-index";
18
- export * from "./vite-site";
19
+ export * from "./vite";
20
+ export * from "./website";
19
21
  export * from "./worker";
20
22
  export { Workflow } from "./workflow";
21
23
  export * from "./wrangler.json";
@@ -13,9 +13,11 @@ export * from "./permission-groups";
13
13
  export * from "./pipeline";
14
14
  export * from "./queue";
15
15
  export * from "./r2-rest-state-store";
16
+ export * from "./tanstack-start";
16
17
  export * from "./vectorize-index";
17
18
  export * from "./vectorize-metadata-index";
18
- export * from "./vite-site";
19
+ export * from "./vite";
20
+ export * from "./website";
19
21
  export * from "./worker";
20
22
  export { Workflow } from "./workflow";
21
23
  export * from "./wrangler.json";
@@ -0,0 +1,12 @@
1
+ import type { Assets } from "./assets";
2
+ import type { Bindings } from "./bindings";
3
+ import { type WebsiteProps } from "./website";
4
+ import type { Worker } from "./worker";
5
+ export interface TanStackStartProps<B extends Bindings> extends WebsiteProps<B> {
6
+ }
7
+ export type TanStackStart<B extends Bindings> = B extends {
8
+ ASSETS: any;
9
+ } ? never : Worker<B & {
10
+ ASSETS: Assets;
11
+ }>;
12
+ export declare function TanStackStart<B extends Bindings>(id: string, props?: Partial<TanStackStartProps<B>>): Promise<TanStackStart<B>>;
@@ -0,0 +1,11 @@
1
+ import { Website } from "./website";
2
+ export async function TanStackStart(id, props) {
3
+ return Website(id, {
4
+ ...props,
5
+ command: props?.command ?? "bun run build",
6
+ wrangler: props?.wrangler ?? false,
7
+ main: props?.main ?? ".output/server/index.mjs",
8
+ compatibilityFlags: ["nodejs_compat", ...(props?.compatibilityFlags ?? [])],
9
+ assets: props?.assets ?? ".output/public",
10
+ });
11
+ }
@@ -0,0 +1,12 @@
1
+ import type { Assets } from "./assets";
2
+ import type { Bindings } from "./bindings";
3
+ import { type WebsiteProps } from "./website";
4
+ import type { Worker } from "./worker";
5
+ export interface ViteProps<B extends Bindings> extends WebsiteProps<B> {
6
+ }
7
+ export type Vite<B extends Bindings> = B extends {
8
+ ASSETS: any;
9
+ } ? never : Worker<B & {
10
+ ASSETS: Assets;
11
+ }>;
12
+ export declare function Vite<B extends Bindings>(id: string, props: ViteProps<B>): Promise<Vite<B>>;
@@ -0,0 +1,18 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { Website } from "./website";
4
+ export async function Vite(id, props) {
5
+ return Website(id, {
6
+ ...props,
7
+ assets: props.assets ??
8
+ (await (async () => {
9
+ try {
10
+ await fs.access(path.join("dist", "client", "index.html"));
11
+ return path.join(".", "dist", "client");
12
+ }
13
+ catch {
14
+ return path.join(".", "dist");
15
+ }
16
+ })()),
17
+ });
18
+ }
@@ -1,7 +1,7 @@
1
1
  import { Assets } from "./assets";
2
2
  import type { Bindings } from "./bindings";
3
- import { Worker } from "./worker";
4
- export interface ViteSiteProps<B extends Bindings> {
3
+ import { Worker, type AssetsConfig, type WorkerProps } from "./worker";
4
+ export interface WebsiteProps<B extends Bindings> extends Omit<WorkerProps<B>, "name" | "assets" | "entrypoint"> {
5
5
  /**
6
6
  * The command to run to build the site
7
7
  */
@@ -23,19 +23,23 @@ export interface ViteSiteProps<B extends Bindings> {
23
23
  *
24
24
  * @default "./dist"
25
25
  */
26
- assets?: string;
27
- /**
28
- * The bindings to pass to the worker
29
- */
30
- bindings?: B;
26
+ assets?: string | ({
27
+ dist?: string;
28
+ } & AssetsConfig);
31
29
  /**
32
30
  * @default process.cwd()
33
31
  */
34
32
  cwd?: string;
33
+ /**
34
+ * Write a wrangler.jsonc file
35
+ *
36
+ * @default - no wrangler.jsonc file is written
37
+ */
38
+ wrangler?: boolean | string;
35
39
  }
36
- export type ViteSite<B extends Bindings> = Promise<B extends {
40
+ export type Website<B extends Bindings> = B extends {
37
41
  ASSETS: any;
38
42
  } ? never : Worker<B & {
39
43
  ASSETS: Assets;
40
- }>>;
41
- export declare function ViteSite<B extends Bindings>(id: string, props: ViteSiteProps<B>): ViteSite<B>;
44
+ }>;
45
+ export declare function Website<B extends Bindings>(id: string, props: WebsiteProps<B>): Promise<Website<B>>;
@@ -0,0 +1,72 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { alchemy } from "../alchemy";
4
+ import { Exec } from "../os";
5
+ import { Assets } from "./assets";
6
+ import { Worker } from "./worker";
7
+ import { WranglerJson } from "./wrangler.json";
8
+ export async function Website(id, props) {
9
+ if (props.bindings?.ASSETS) {
10
+ throw new Error("ASSETS binding is reserved for internal use");
11
+ }
12
+ return alchemy.run(id, async () => {
13
+ // building the site requires a wrangler.jsonc file to start
14
+ // - so initialize an empty one if it doesn't exist
15
+ const cwd = path.resolve(props.cwd || process.cwd());
16
+ const fileName = typeof props.wrangler === "boolean" ? "wrangler.jsonc" : props.wrangler;
17
+ const wranglerPath = fileName && path.relative(cwd, path.join(cwd, fileName));
18
+ if (props.wrangler) {
19
+ try {
20
+ await fs.access(wranglerPath);
21
+ }
22
+ catch {
23
+ await fs.writeFile(wranglerPath, JSON.stringify({
24
+ name: id,
25
+ main: props.main,
26
+ compatibility_date: new Date().toISOString().split("T")[0],
27
+ }, null, 2));
28
+ }
29
+ }
30
+ await Exec("build", {
31
+ command: props.command,
32
+ });
33
+ const dist = typeof props.assets === "string"
34
+ ? props.assets
35
+ : (props.assets?.dist ?? "dist");
36
+ const staticAssets = await Assets("assets", {
37
+ path: dist,
38
+ });
39
+ const worker = await Worker("worker", {
40
+ ...props,
41
+ name: props.name ?? id,
42
+ entrypoint: props.main,
43
+ assets: {
44
+ html_handling: "auto-trailing-slash",
45
+ not_found_handling: "single-page-application",
46
+ run_worker_first: false,
47
+ ...(typeof props.assets === "string" ? {} : props.assets),
48
+ },
49
+ script: props.main
50
+ ? undefined
51
+ : `
52
+ export default {
53
+ async fetch(request, env) {
54
+ return new Response("Not Found", { status: 404 });
55
+ },
56
+ };`,
57
+ url: true,
58
+ adopt: true,
59
+ bindings: {
60
+ ...props.bindings,
61
+ ASSETS: staticAssets,
62
+ },
63
+ });
64
+ if (props.wrangler) {
65
+ await WranglerJson("wrangler.jsonc", {
66
+ path: wranglerPath,
67
+ worker,
68
+ });
69
+ }
70
+ return worker;
71
+ });
72
+ }
package/lib/env.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export interface Env {
2
+ [key: string]: Promise<string>;
3
+ <T = string>(name: string, value?: T | undefined, error?: string): Promise<T>;
4
+ }
5
+ export declare const env: Env;
package/lib/env.js ADDED
@@ -0,0 +1,24 @@
1
+ export const env = new Proxy(_env, {
2
+ get: (_, name) => _env(name),
3
+ apply: (_, __, args) => _env(...args),
4
+ });
5
+ async function _env(name, value, error) {
6
+ if (value !== undefined) {
7
+ return value;
8
+ }
9
+ else if (typeof process !== undefined) {
10
+ // we are in a node environment
11
+ return process.env[name];
12
+ }
13
+ else {
14
+ // we are in a browser environment
15
+ try {
16
+ const { env } = await import("cloudflare:workers");
17
+ if (name in env) {
18
+ return env[name];
19
+ }
20
+ }
21
+ catch (error) { }
22
+ }
23
+ throw new Error(error ?? `Environment variable ${name} is not set`);
24
+ }
@@ -49,7 +49,7 @@ async function generateProviderDocs({ provider, outDir, parallel, }) {
49
49
  reasoningEffort: "high",
50
50
  },
51
51
  },
52
- freeze: true,
52
+ freeze: false,
53
53
  temperature: 0.1,
54
54
  schema: type({
55
55
  groups: type({
@@ -96,7 +96,7 @@ async function generateProviderDocs({ provider, outDir, parallel, }) {
96
96
  return Document(`docs/${providerName}/${g.identifier}`, {
97
97
  title: g.identifier,
98
98
  path: path.join(providerDocsDir, `${g.filename.replace(".ts", "").replace(".md", "")}.md`),
99
- freeze: true,
99
+ freeze: false,
100
100
  model: {
101
101
  id: "claude-3-5-sonnet-latest",
102
102
  provider: "anthropic",
package/lib/secret.d.ts CHANGED
@@ -73,5 +73,9 @@ export declare function isSecret(binding: any): binding is Secret;
73
73
  */
74
74
  export declare function secret<S extends string | undefined>(unencrypted: S): Secret;
75
75
  export declare namespace secret {
76
- function env(name: string, value?: string, error?: string): Promise<Secret>;
76
+ interface Env {
77
+ [key: string]: Promise<Secret>;
78
+ (name: string, value?: string, error?: string): Promise<Secret>;
79
+ }
80
+ const env: Env;
77
81
  }
package/lib/secret.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { alchemy } from "./alchemy";
1
2
  /**
2
3
  * Internal wrapper for sensitive values like API keys and credentials.
3
4
  * When stored in alchemy state files, the value is automatically encrypted
@@ -83,8 +84,11 @@ export function secret(unencrypted) {
83
84
  return new Secret(unencrypted);
84
85
  }
85
86
  (function (secret) {
86
- async function env(name, value, error) {
87
- const alchemy = await import("./alchemy");
87
+ secret.env = new Proxy(_env, {
88
+ get: (_, name) => _env(name),
89
+ apply: (_, __, args) => _env(...args),
90
+ });
91
+ async function _env(name, value, error) {
88
92
  const result = await alchemy.env(name, value, error);
89
93
  if (typeof result === "string") {
90
94
  return secret(result);
@@ -93,5 +97,4 @@ export function secret(unencrypted) {
93
97
  throw new Error(`Secret environment variable ${name} is not a string`);
94
98
  }
95
99
  }
96
- secret.env = env;
97
100
  })(secret || (secret = {}));
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "alchemy",
3
- "version": "0.11.4",
3
+ "version": "0.12.1",
4
4
  "type": "module",
5
5
  "module": "./lib/index.js",
6
6
  "scripts": {
7
7
  "publish:npm": "cp ../README.md . && rm -rf ./*.tsbuildinfo && rm -rf ./lib && tsc -b && npm publish && rm README.md",
8
8
  "docs:gen": "rm -rf ./docs && typedoc"
9
9
  },
10
- "files": ["lib", "src"],
10
+ "files": [
11
+ "lib",
12
+ "src"
13
+ ],
11
14
  "exports": {
12
15
  ".": "./lib/index.js",
13
16
  "./ai": "./lib/ai/index.js",
package/src/alchemy.ts CHANGED
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
4
  import { destroy, DestroyedSignal } from "./destroy";
5
+ import { env } from "./env";
5
6
  import type { PendingResource } from "./resource";
6
7
  import { Scope } from "./scope";
7
8
  import { secret } from "./secret";
@@ -81,6 +82,11 @@ export interface Alchemy {
81
82
  (template: TemplateStringsArray, ...values: any[]): Promise<string>;
82
83
  }
83
84
 
85
+ _alchemy.destroy = destroy;
86
+ _alchemy.run = run;
87
+ _alchemy.secret = secret;
88
+ _alchemy.env = env;
89
+
84
90
  /**
85
91
  * Implementation of the alchemy function that handles both application scoping
86
92
  * and template string interpolation.
@@ -214,10 +220,6 @@ async function _alchemy(
214
220
  ].join("\n");
215
221
  }
216
222
  }
217
- _alchemy.destroy = destroy;
218
- _alchemy.run = run;
219
- _alchemy.secret = secret;
220
- _alchemy.env = env;
221
223
 
222
224
  export interface AlchemyOptions {
223
225
  /**
@@ -349,25 +351,3 @@ async function run<T>(
349
351
  await _scope.finalize();
350
352
  }
351
353
  }
352
-
353
- export async function env<T = string>(
354
- name: string,
355
- value?: T | undefined,
356
- error?: string
357
- ): Promise<T> {
358
- if (value !== undefined) {
359
- return value;
360
- } else if (typeof process !== undefined) {
361
- // we are in a node environment
362
- return process.env[name]! as T;
363
- } else {
364
- // we are in a browser environment
365
- try {
366
- const { env } = await import("cloudflare:workers");
367
- if (name in env) {
368
- return env[name as keyof typeof env];
369
- }
370
- } catch (error) {}
371
- }
372
- throw new Error(error ?? `Environment variable ${name} is not set`);
373
- }
@@ -13,9 +13,11 @@ export * from "./permission-groups";
13
13
  export * from "./pipeline";
14
14
  export * from "./queue";
15
15
  export * from "./r2-rest-state-store";
16
+ export * from "./tanstack-start";
16
17
  export * from "./vectorize-index";
17
18
  export * from "./vectorize-metadata-index";
18
- export * from "./vite-site";
19
+ export * from "./vite";
20
+ export * from "./website";
19
21
  export * from "./worker";
20
22
  export { Workflow } from "./workflow";
21
23
  export * from "./wrangler.json";
@@ -0,0 +1,26 @@
1
+ import type { Assets } from "./assets";
2
+ import type { Bindings } from "./bindings";
3
+ import { Website, type WebsiteProps } from "./website";
4
+ import type { Worker } from "./worker";
5
+
6
+ export interface TanStackStartProps<B extends Bindings>
7
+ extends WebsiteProps<B> {}
8
+
9
+ // don't allow the ASSETS to be overriden
10
+ export type TanStackStart<B extends Bindings> = B extends { ASSETS: any }
11
+ ? never
12
+ : Worker<B & { ASSETS: Assets }>;
13
+
14
+ export async function TanStackStart<B extends Bindings>(
15
+ id: string,
16
+ props?: Partial<TanStackStartProps<B>>
17
+ ): Promise<TanStackStart<B>> {
18
+ return Website(id, {
19
+ ...props,
20
+ command: props?.command ?? "bun run build",
21
+ wrangler: props?.wrangler ?? false,
22
+ main: props?.main ?? ".output/server/index.mjs",
23
+ compatibilityFlags: ["nodejs_compat", ...(props?.compatibilityFlags ?? [])],
24
+ assets: props?.assets ?? ".output/public",
25
+ });
26
+ }
@@ -0,0 +1,32 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import type { Assets } from "./assets";
4
+ import type { Bindings } from "./bindings";
5
+ import { Website, type WebsiteProps } from "./website";
6
+ import type { Worker } from "./worker";
7
+
8
+ export interface ViteProps<B extends Bindings> extends WebsiteProps<B> {}
9
+
10
+ // don't allow the ASSETS to be overriden
11
+ export type Vite<B extends Bindings> = B extends { ASSETS: any }
12
+ ? never
13
+ : Worker<B & { ASSETS: Assets }>;
14
+
15
+ export async function Vite<B extends Bindings>(
16
+ id: string,
17
+ props: ViteProps<B>
18
+ ): Promise<Vite<B>> {
19
+ return Website(id, {
20
+ ...props,
21
+ assets:
22
+ props.assets ??
23
+ (await (async () => {
24
+ try {
25
+ await fs.access(path.join("dist", "client", "index.html"));
26
+ return path.join(".", "dist", "client");
27
+ } catch {
28
+ return path.join(".", "dist");
29
+ }
30
+ })()),
31
+ });
32
+ }
@@ -0,0 +1,140 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { alchemy } from "../alchemy";
4
+ import { Exec } from "../os";
5
+ import { Assets } from "./assets";
6
+ import type { Bindings } from "./bindings";
7
+ import { Worker, type AssetsConfig, type WorkerProps } from "./worker";
8
+ import { WranglerJson } from "./wrangler.json";
9
+
10
+ export interface WebsiteProps<B extends Bindings>
11
+ extends Omit<WorkerProps<B>, "name" | "assets" | "entrypoint"> {
12
+ /**
13
+ * The command to run to build the site
14
+ */
15
+ command: string;
16
+ /**
17
+ * The name of the worker
18
+ *
19
+ * @default id
20
+ */
21
+ name?: string;
22
+ /**
23
+ * The entrypoint to your server
24
+ *
25
+ * @default - a simple server that serves static assets is generated
26
+ */
27
+ main?: string;
28
+ /**
29
+ * The directory containing your static assets
30
+ *
31
+ * @default "./dist"
32
+ */
33
+ assets?:
34
+ | string
35
+ | ({
36
+ dist?: string;
37
+ } & AssetsConfig);
38
+ /**
39
+ * @default process.cwd()
40
+ */
41
+ cwd?: string;
42
+
43
+ /**
44
+ * Write a wrangler.jsonc file
45
+ *
46
+ * @default - no wrangler.jsonc file is written
47
+ */
48
+ wrangler?: boolean | string;
49
+ }
50
+
51
+ export type Website<B extends Bindings> = B extends { ASSETS: any }
52
+ ? never
53
+ : Worker<B & { ASSETS: Assets }>;
54
+
55
+ export async function Website<B extends Bindings>(
56
+ id: string,
57
+ props: WebsiteProps<B>
58
+ ): Promise<Website<B>> {
59
+ if (props.bindings?.ASSETS) {
60
+ throw new Error("ASSETS binding is reserved for internal use");
61
+ }
62
+
63
+ return alchemy.run(id, async () => {
64
+ // building the site requires a wrangler.jsonc file to start
65
+ // - so initialize an empty one if it doesn't exist
66
+
67
+ const cwd = path.resolve(props.cwd || process.cwd());
68
+ const fileName =
69
+ typeof props.wrangler === "boolean" ? "wrangler.jsonc" : props.wrangler;
70
+ const wranglerPath =
71
+ fileName && path.relative(cwd, path.join(cwd, fileName));
72
+
73
+ if (props.wrangler) {
74
+ try {
75
+ await fs.access(wranglerPath!);
76
+ } catch {
77
+ await fs.writeFile(
78
+ wranglerPath!,
79
+ JSON.stringify(
80
+ {
81
+ name: id,
82
+ main: props.main,
83
+ compatibility_date: new Date().toISOString().split("T")[0],
84
+ },
85
+ null,
86
+ 2
87
+ )
88
+ );
89
+ }
90
+ }
91
+
92
+ await Exec("build", {
93
+ command: props.command,
94
+ });
95
+
96
+ const dist =
97
+ typeof props.assets === "string"
98
+ ? props.assets
99
+ : (props.assets?.dist ?? "dist");
100
+
101
+ const staticAssets = await Assets("assets", {
102
+ path: dist,
103
+ });
104
+
105
+ const worker = await Worker("worker", {
106
+ ...props,
107
+ name: props.name ?? id,
108
+ entrypoint: props.main,
109
+ assets: {
110
+ html_handling: "auto-trailing-slash",
111
+ not_found_handling: "single-page-application",
112
+ run_worker_first: false,
113
+ ...(typeof props.assets === "string" ? {} : props.assets),
114
+ },
115
+ script: props.main
116
+ ? undefined
117
+ : `
118
+ export default {
119
+ async fetch(request, env) {
120
+ return new Response("Not Found", { status: 404 });
121
+ },
122
+ };`,
123
+ url: true,
124
+ adopt: true,
125
+ bindings: {
126
+ ...props.bindings,
127
+ ASSETS: staticAssets,
128
+ },
129
+ });
130
+
131
+ if (props.wrangler) {
132
+ await WranglerJson("wrangler.jsonc", {
133
+ path: wranglerPath,
134
+ worker,
135
+ });
136
+ }
137
+
138
+ return worker as Website<B>;
139
+ });
140
+ }
package/src/env.ts ADDED
@@ -0,0 +1,31 @@
1
+ export interface Env {
2
+ [key: string]: Promise<string>;
3
+ <T = string>(name: string, value?: T | undefined, error?: string): Promise<T>;
4
+ }
5
+
6
+ export const env = new Proxy(_env, {
7
+ get: (_, name: string) => _env(name),
8
+ apply: (_, __, args: [string, any?, string?]) => _env(...args),
9
+ }) as Env;
10
+
11
+ async function _env<T = string>(
12
+ name: string,
13
+ value?: T | undefined,
14
+ error?: string
15
+ ): Promise<T> {
16
+ if (value !== undefined) {
17
+ return value;
18
+ } else if (typeof process !== undefined) {
19
+ // we are in a node environment
20
+ return process.env[name]! as T;
21
+ } else {
22
+ // we are in a browser environment
23
+ try {
24
+ const { env } = await import("cloudflare:workers");
25
+ if (name in env) {
26
+ return env[name as keyof typeof env];
27
+ }
28
+ } catch (error) {}
29
+ }
30
+ throw new Error(error ?? `Environment variable ${name} is not set`);
31
+ }
@@ -118,7 +118,7 @@ async function generateProviderDocs({
118
118
  reasoningEffort: "high",
119
119
  },
120
120
  },
121
- freeze: true,
121
+ freeze: false,
122
122
  temperature: 0.1,
123
123
  schema: type({
124
124
  groups: type({
@@ -178,7 +178,7 @@ async function generateProviderDocs({
178
178
  providerDocsDir,
179
179
  `${g.filename.replace(".ts", "").replace(".md", "")}.md`
180
180
  ),
181
- freeze: true,
181
+ freeze: false,
182
182
  model: {
183
183
  id: "claude-3-5-sonnet-latest",
184
184
  provider: "anthropic",
package/src/secret.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { alchemy } from "./alchemy";
2
+
1
3
  /**
2
4
  * Internal wrapper for sensitive values like API keys and credentials.
3
5
  * When stored in alchemy state files, the value is automatically encrypted
@@ -85,12 +87,21 @@ export function secret<S extends string | undefined>(unencrypted: S): Secret {
85
87
  }
86
88
 
87
89
  export namespace secret {
88
- export async function env(
90
+ export interface Env {
91
+ [key: string]: Promise<Secret>;
92
+ (name: string, value?: string, error?: string): Promise<Secret>;
93
+ }
94
+
95
+ export const env = new Proxy(_env, {
96
+ get: (_, name: string) => _env(name),
97
+ apply: (_, __, args: [string, any?, string?]) => _env(...args),
98
+ }) as Env;
99
+
100
+ async function _env(
89
101
  name: string,
90
102
  value?: string,
91
103
  error?: string
92
104
  ): Promise<Secret> {
93
- const alchemy = await import("./alchemy");
94
105
  const result = await alchemy.env(name, value, error);
95
106
  if (typeof result === "string") {
96
107
  return secret(result);
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
+
3
4
  /**
4
5
  * Process markdown files with frontmatter to generate navigation items
5
6
  * @param directoryPath The directory containing markdown files
@@ -1,71 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { alchemy } from "../alchemy";
4
- import { Exec } from "../os";
5
- import { Assets } from "./assets";
6
- import { Worker } from "./worker";
7
- import { WranglerJson } from "./wrangler.json";
8
- export async function ViteSite(id, props) {
9
- if (props.bindings?.ASSETS) {
10
- throw new Error("ASSETS binding is reserved for internal use");
11
- }
12
- // @ts-ignore - we know the types are correct
13
- return await alchemy.run(id, async () => {
14
- // Create minimal wrangler.jsonc if it doesn't exist
15
- // `building the site requires a wrangler.jsonc file to start - so initialize an empty one if it doesn't exist`
16
- const cwd = props.cwd || process.cwd();
17
- const wranglerPath = path.join(cwd, "wrangler.jsonc");
18
- try {
19
- await fs.access(wranglerPath);
20
- }
21
- catch {
22
- await fs.writeFile(wranglerPath, JSON.stringify({
23
- name: id,
24
- main: props.main,
25
- compatibility_date: new Date().toISOString().split("T")[0],
26
- }, null, 2));
27
- }
28
- await Exec("build", {
29
- command: props.command,
30
- });
31
- const staticAssets = await Assets("assets", {
32
- path: props.assets ??
33
- (await (async () => {
34
- try {
35
- await fs.access(path.join("dist", "client", "index.html"));
36
- return path.join(".", "dist", "client");
37
- }
38
- catch {
39
- return path.join(".", "dist");
40
- }
41
- })()),
42
- });
43
- const worker = await Worker("worker", {
44
- name: props.name ?? id,
45
- entrypoint: props.main,
46
- assets: {
47
- html_handling: "auto-trailing-slash",
48
- not_found_handling: "single-page-application",
49
- run_worker_first: false,
50
- },
51
- script: props.main
52
- ? undefined
53
- : `
54
- export default {
55
- async fetch(request, env) {
56
- return new Response("Not Found", { status: 404 });
57
- },
58
- };`,
59
- url: true,
60
- adopt: true,
61
- bindings: {
62
- ...props.bindings,
63
- ASSETS: staticAssets,
64
- },
65
- });
66
- await WranglerJson("wrangler.jsonc", {
67
- worker,
68
- });
69
- return worker;
70
- });
71
- }
@@ -1,126 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { alchemy } from "../alchemy";
4
- import { Exec } from "../os";
5
- import { Assets } from "./assets";
6
- import type { Bindings } from "./bindings";
7
- import { Worker } from "./worker";
8
- import { WranglerJson } from "./wrangler.json";
9
-
10
- export interface ViteSiteProps<B extends Bindings> {
11
- /**
12
- * The command to run to build the site
13
- */
14
- command: string;
15
- /**
16
- * The name of the worker
17
- *
18
- * @default id
19
- */
20
- name?: string;
21
- /**
22
- * The entrypoint to your server
23
- *
24
- * @default - a simple server that serves static assets is generated
25
- */
26
- main?: string;
27
- /**
28
- * The directory containing your static assets
29
- *
30
- * @default "./dist"
31
- */
32
- assets?: string;
33
- /**
34
- * The bindings to pass to the worker
35
- */
36
- bindings?: B;
37
- /**
38
- * @default process.cwd()
39
- */
40
- cwd?: string;
41
- }
42
-
43
- export type ViteSite<B extends Bindings> = Promise<
44
- // don't allow the ASSETS to be overriden
45
- B extends { ASSETS: any } ? never : Worker<B & { ASSETS: Assets }>
46
- >;
47
-
48
- export async function ViteSite<B extends Bindings>(
49
- id: string,
50
- props: ViteSiteProps<B>
51
- ): ViteSite<B> {
52
- if (props.bindings?.ASSETS) {
53
- throw new Error("ASSETS binding is reserved for internal use");
54
- }
55
-
56
- // @ts-ignore - we know the types are correct
57
- return await alchemy.run(id, async () => {
58
- // Create minimal wrangler.jsonc if it doesn't exist
59
-
60
- // `building the site requires a wrangler.jsonc file to start - so initialize an empty one if it doesn't exist`
61
- const cwd = props.cwd || process.cwd();
62
- const wranglerPath = path.join(cwd, "wrangler.jsonc");
63
- try {
64
- await fs.access(wranglerPath);
65
- } catch {
66
- await fs.writeFile(
67
- wranglerPath,
68
- JSON.stringify(
69
- {
70
- name: id,
71
- main: props.main,
72
- compatibility_date: new Date().toISOString().split("T")[0],
73
- },
74
- null,
75
- 2
76
- )
77
- );
78
- }
79
-
80
- await Exec("build", {
81
- command: props.command,
82
- });
83
-
84
- const staticAssets = await Assets("assets", {
85
- path:
86
- props.assets ??
87
- (await (async () => {
88
- try {
89
- await fs.access(path.join("dist", "client", "index.html"));
90
- return path.join(".", "dist", "client");
91
- } catch {
92
- return path.join(".", "dist");
93
- }
94
- })()),
95
- });
96
- const worker = await Worker("worker", {
97
- name: props.name ?? id,
98
- entrypoint: props.main,
99
- assets: {
100
- html_handling: "auto-trailing-slash",
101
- not_found_handling: "single-page-application",
102
- run_worker_first: false,
103
- },
104
- script: props.main
105
- ? undefined
106
- : `
107
- export default {
108
- async fetch(request, env) {
109
- return new Response("Not Found", { status: 404 });
110
- },
111
- };`,
112
- url: true,
113
- adopt: true,
114
- bindings: {
115
- ...props.bindings,
116
- ASSETS: staticAssets,
117
- },
118
- });
119
-
120
- await WranglerJson("wrangler.jsonc", {
121
- worker,
122
- });
123
-
124
- return worker;
125
- });
126
- }