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 +1 -1
- package/lib/alchemy.js +5 -24
- package/lib/cloudflare/index.d.ts +3 -1
- package/lib/cloudflare/index.js +3 -1
- package/lib/cloudflare/tanstack-start.d.ts +12 -0
- package/lib/cloudflare/tanstack-start.js +11 -0
- package/lib/cloudflare/vite.d.ts +12 -0
- package/lib/cloudflare/vite.js +18 -0
- package/lib/cloudflare/{vite-site.d.ts → website.d.ts} +14 -10
- package/lib/cloudflare/website.js +72 -0
- package/lib/env.d.ts +5 -0
- package/lib/env.js +24 -0
- package/lib/internal/docs/providers.js +2 -2
- package/lib/secret.d.ts +5 -1
- package/lib/secret.js +6 -3
- package/package.json +5 -2
- package/src/alchemy.ts +6 -26
- package/src/cloudflare/index.ts +3 -1
- package/src/cloudflare/tanstack-start.ts +26 -0
- package/src/cloudflare/vite.ts +32 -0
- package/src/cloudflare/website.ts +140 -0
- package/src/env.ts +31 -0
- package/src/internal/docs/providers.ts +2 -2
- package/src/secret.ts +13 -2
- package/src/web/vitepress/process-front-matter-files.ts +1 -0
- package/lib/cloudflare/vite-site.js +0 -71
- package/src/cloudflare/vite-site.ts +0 -126
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
|
|
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";
|
package/lib/cloudflare/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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.
|
|
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": [
|
|
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
|
-
}
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -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
|
|
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:
|
|
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:
|
|
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
|
|
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,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
|
-
}
|