alchemy 0.3.0 → 0.4.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/README.md +8 -3
- package/lib/ai/astro-file.js +7 -24
- package/lib/ai/client.d.ts +7 -0
- package/lib/ai/client.js +45 -0
- package/lib/ai/css-file.js +7 -24
- package/lib/ai/data.d.ts +26 -21
- package/lib/ai/data.js +33 -22
- package/lib/ai/document.d.ts +76 -5
- package/lib/ai/document.js +112 -56
- package/lib/ai/html-file.js +7 -24
- package/lib/ai/json-file.js +11 -32
- package/lib/ai/typescript-file.d.ts +1 -2
- package/lib/ai/typescript-file.js +8 -39
- package/lib/ai/vue-file.js +7 -24
- package/lib/ai/yaml-file.js +12 -48
- package/lib/alchemy.d.ts +5 -0
- package/lib/alchemy.js +61 -3
- package/lib/apply.js +18 -11
- package/lib/aws/account-id.d.ts +4 -1
- package/lib/aws/account-id.js +1 -1
- package/lib/aws/credentials.d.ts +5 -0
- package/lib/aws/credentials.js +0 -0
- package/lib/aws/oidc/oidc-provider.js +2 -2
- package/lib/aws/role.js +35 -23
- package/lib/cloudflare/account-api-token.d.ts +168 -0
- package/lib/cloudflare/account-api-token.js +179 -0
- package/lib/cloudflare/account-id.d.ts +1 -0
- package/lib/cloudflare/account-id.js +0 -0
- package/lib/cloudflare/api.d.ts +38 -1
- package/lib/cloudflare/api.js +55 -4
- package/lib/cloudflare/bucket.d.ts +118 -3
- package/lib/cloudflare/bucket.js +269 -89
- package/lib/cloudflare/custom-domain.d.ts +75 -0
- package/lib/cloudflare/custom-domain.js +154 -0
- package/lib/cloudflare/{dns.js → dns-records.js} +14 -1
- package/lib/cloudflare/generate-asset-manifest.js +1 -1
- package/lib/cloudflare/index.d.ts +5 -1
- package/lib/cloudflare/index.js +5 -1
- package/lib/cloudflare/permission-groups.d.ts +78 -0
- package/lib/cloudflare/permission-groups.js +48 -0
- package/lib/cloudflare/r2-rest-state-store.d.ts +2 -1
- package/lib/cloudflare/r2-rest-state-store.js +3 -2
- package/lib/cloudflare/static-site-router.js +16 -7
- package/lib/cloudflare/static-site.d.ts +17 -18
- package/lib/cloudflare/static-site.js +19 -7
- package/lib/cloudflare/upload-asset-manifest.js +4 -2
- package/lib/{util/encrypt.d.ts → encrypt.d.ts} +1 -1
- package/lib/{util/encrypt.js → encrypt.js} +1 -1
- package/lib/fs/copy-file.d.ts +54 -0
- package/lib/fs/copy-file.js +63 -0
- package/lib/fs/file-system-state-store.js +1 -1
- package/lib/fs/file.d.ts +31 -0
- package/lib/fs/file.js +30 -5
- package/lib/fs/index.d.ts +6 -0
- package/lib/fs/index.js +6 -0
- package/lib/fs/static-astro-file.d.ts +34 -0
- package/lib/fs/static-astro-file.js +39 -0
- package/lib/fs/static-css-file.d.ts +24 -0
- package/lib/fs/static-css-file.js +29 -0
- package/lib/fs/static-html-file.d.ts +31 -0
- package/lib/fs/static-html-file.js +36 -0
- package/lib/fs/static-json-file.d.ts +2 -2
- package/lib/fs/static-json-file.js +3 -2
- package/lib/fs/static-text-file.d.ts +1 -1
- package/lib/fs/static-text-file.js +3 -2
- package/lib/fs/static-typescript-file.d.ts +2 -2
- package/lib/fs/static-typescript-file.js +3 -2
- package/lib/fs/static-vue-file.d.ts +28 -0
- package/lib/fs/static-vue-file.js +33 -0
- package/lib/fs/static-yaml-file.d.ts +11 -8
- package/lib/fs/static-yaml-file.js +3 -2
- package/lib/internal/{providers.d.ts → docs/providers.d.ts} +14 -4
- package/lib/internal/docs/providers.js +196 -0
- package/lib/secret.d.ts +3 -0
- package/lib/secret.js +13 -0
- package/lib/{util/serde.d.ts → serde.d.ts} +1 -1
- package/lib/{util/serde.js → serde.js} +12 -4
- package/lib/test/bun.js +3 -2
- package/lib/util/sha256.d.ts +1 -0
- package/lib/util/sha256.js +4 -0
- package/lib/web/vitepress/config.d.ts +11 -0
- package/lib/web/vitepress/config.js +32 -0
- package/lib/web/vitepress/home-page.d.ts +5 -3
- package/lib/web/vitepress/home-page.js +48 -35
- package/lib/web/vitepress/index.d.ts +2 -0
- package/lib/web/vitepress/index.js +2 -0
- package/lib/web/vitepress/process-front-matter-files.d.ts +18 -0
- package/lib/web/vitepress/process-front-matter-files.js +68 -0
- package/lib/web/vitepress/vitepress.d.ts +5 -4
- package/lib/web/vitepress/vitepress.js +20 -36
- package/package.json +8 -4
- package/src/ai/astro-file.ts +11 -30
- package/src/ai/client.ts +59 -0
- package/src/ai/css-file.ts +11 -30
- package/src/ai/data.ts +50 -25
- package/src/ai/document.ts +171 -66
- package/src/ai/html-file.ts +11 -30
- package/src/ai/json-file.ts +13 -37
- package/src/ai/typescript-file.ts +14 -48
- package/src/ai/vue-file.ts +11 -30
- package/src/ai/yaml-file.ts +13 -53
- package/src/alchemy.ts +74 -9
- package/src/apply.ts +27 -26
- package/src/aws/account-id.ts +6 -2
- package/src/aws/credentials.ts +6 -0
- package/src/aws/oidc/oidc-provider.ts +17 -17
- package/src/aws/role.ts +68 -54
- package/src/cloudflare/account-api-token.ts +365 -0
- package/src/cloudflare/account-id.ts +0 -0
- package/src/cloudflare/api.ts +88 -17
- package/src/cloudflare/bucket.ts +493 -133
- package/src/cloudflare/custom-domain.ts +318 -0
- package/src/cloudflare/{dns.ts → dns-records.ts} +36 -22
- package/src/cloudflare/generate-asset-manifest.ts +4 -4
- package/src/cloudflare/index.ts +5 -1
- package/src/cloudflare/permission-groups.ts +137 -0
- package/src/cloudflare/r2-rest-state-store.ts +14 -13
- package/src/cloudflare/static-site-router.ts +18 -8
- package/src/cloudflare/static-site.ts +27 -35
- package/src/cloudflare/upload-asset-manifest.ts +6 -4
- package/src/cloudflare/worker.ts +44 -43
- package/src/dns/import-dns.ts +4 -4
- package/src/{util/encrypt.ts → encrypt.ts} +7 -10
- package/src/fs/copy-file.ts +115 -0
- package/src/fs/file-system-state-store.ts +5 -5
- package/src/fs/file.ts +64 -9
- package/src/fs/index.ts +6 -0
- package/src/fs/static-astro-file.ts +45 -0
- package/src/fs/static-css-file.ts +35 -0
- package/src/fs/static-html-file.ts +42 -0
- package/src/fs/static-json-file.ts +4 -3
- package/src/fs/static-text-file.ts +5 -2
- package/src/fs/static-typescript-file.ts +4 -3
- package/src/fs/static-vue-file.ts +39 -0
- package/src/fs/static-yaml-file.ts +13 -9
- package/src/internal/docs/providers.ts +281 -0
- package/src/secret.ts +16 -0
- package/src/{util/serde.ts → serde.ts} +16 -10
- package/src/test/bun.ts +8 -7
- package/src/util/sha256.ts +5 -0
- package/src/web/vitepress/config.ts +50 -0
- package/src/web/vitepress/home-page.ts +54 -42
- package/src/web/vitepress/index.ts +2 -0
- package/src/web/vitepress/process-front-matter-files.ts +98 -0
- package/src/web/vitepress/vitepress.ts +34 -50
- package/lib/internal/getting-started.d.ts +0 -10
- package/lib/internal/getting-started.js +0 -94
- package/lib/internal/project.d.ts +0 -13
- package/lib/internal/project.js +0 -101
- package/lib/internal/providers.js +0 -148
- package/src/internal/getting-started.ts +0 -107
- package/src/internal/project.ts +0 -119
- package/src/internal/providers.ts +0 -209
- /package/lib/cloudflare/{dns.d.ts → dns-records.d.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { decryptWithKey, encrypt } from "./encrypt";
|
|
2
|
+
import { Scope } from "./scope";
|
|
3
|
+
import { Secret } from "./secret";
|
|
4
4
|
|
|
5
5
|
import type { Type } from "arktype";
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ export async function serialize(
|
|
|
18
18
|
value: any,
|
|
19
19
|
options?: {
|
|
20
20
|
encrypt?: boolean;
|
|
21
|
-
}
|
|
21
|
+
}
|
|
22
22
|
): Promise<any> {
|
|
23
23
|
if (Array.isArray(value)) {
|
|
24
24
|
return Promise.all(value.map((value) => serialize(scope, value, options)));
|
|
@@ -29,13 +29,17 @@ export async function serialize(
|
|
|
29
29
|
return {
|
|
30
30
|
"@secret":
|
|
31
31
|
options?.encrypt !== false
|
|
32
|
-
? await
|
|
32
|
+
? await encrypt(value.unencrypted, scope.password)
|
|
33
33
|
: value.unencrypted,
|
|
34
34
|
};
|
|
35
35
|
} else if (isType(value)) {
|
|
36
36
|
return {
|
|
37
37
|
"@schema": value.toJSON(),
|
|
38
38
|
};
|
|
39
|
+
} else if (value instanceof Date) {
|
|
40
|
+
return {
|
|
41
|
+
"@date": value.toISOString(),
|
|
42
|
+
};
|
|
39
43
|
} else if (value instanceof Scope) {
|
|
40
44
|
return undefined;
|
|
41
45
|
} else if (value && typeof value === "object") {
|
|
@@ -44,8 +48,8 @@ export async function serialize(
|
|
|
44
48
|
Object.entries(value).map(async ([key, value]) => [
|
|
45
49
|
key,
|
|
46
50
|
await serialize(scope, value, options),
|
|
47
|
-
])
|
|
48
|
-
)
|
|
51
|
+
])
|
|
52
|
+
)
|
|
49
53
|
);
|
|
50
54
|
}
|
|
51
55
|
return value;
|
|
@@ -54,7 +58,7 @@ export async function serialize(
|
|
|
54
58
|
export async function deserialize(scope: Scope, value: any): Promise<any> {
|
|
55
59
|
if (Array.isArray(value)) {
|
|
56
60
|
return await Promise.all(
|
|
57
|
-
value.map(async (item) => await deserialize(scope, item))
|
|
61
|
+
value.map(async (item) => await deserialize(scope, item))
|
|
58
62
|
);
|
|
59
63
|
} else if (value && typeof value === "object") {
|
|
60
64
|
if (typeof value["@secret"] === "string") {
|
|
@@ -64,14 +68,16 @@ export async function deserialize(scope: Scope, value: any): Promise<any> {
|
|
|
64
68
|
return new Secret(await decryptWithKey(value["@secret"], scope.password));
|
|
65
69
|
} else if ("@schema" in value) {
|
|
66
70
|
return value["@schema"];
|
|
71
|
+
} else if ("@date" in value) {
|
|
72
|
+
return new Date(value["@date"]);
|
|
67
73
|
} else {
|
|
68
74
|
return Object.fromEntries(
|
|
69
75
|
await Promise.all(
|
|
70
76
|
Object.entries(value).map(async ([key, value]) => [
|
|
71
77
|
key,
|
|
72
78
|
await deserialize(scope, value),
|
|
73
|
-
])
|
|
74
|
-
)
|
|
79
|
+
])
|
|
80
|
+
)
|
|
75
81
|
);
|
|
76
82
|
}
|
|
77
83
|
}
|
package/src/test/bun.ts
CHANGED
|
@@ -78,7 +78,7 @@ type test = {
|
|
|
78
78
|
name: string,
|
|
79
79
|
options: TestOptions,
|
|
80
80
|
fn: (scope: Scope) => Promise<any>,
|
|
81
|
-
timeout?: number
|
|
81
|
+
timeout?: number
|
|
82
82
|
): void;
|
|
83
83
|
|
|
84
84
|
/**
|
|
@@ -121,6 +121,7 @@ type test = {
|
|
|
121
121
|
* ```
|
|
122
122
|
*/
|
|
123
123
|
export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
124
|
+
let isFailed = false;
|
|
124
125
|
defaultOptions = defaultOptions ?? {};
|
|
125
126
|
if (
|
|
126
127
|
defaultOptions.stateStore === undefined &&
|
|
@@ -129,7 +130,7 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
129
130
|
) {
|
|
130
131
|
defaultOptions.stateStore = (scope) =>
|
|
131
132
|
new R2RestStateStore(scope, {
|
|
132
|
-
apiKey: process.env.CLOUDFLARE_API_KEY,
|
|
133
|
+
apiKey: alchemy.secret(process.env.CLOUDFLARE_API_KEY),
|
|
133
134
|
email: process.env.CLOUDFLARE_EMAIL,
|
|
134
135
|
bucketName: process.env.CLOUDFLARE_BUCKET_NAME!,
|
|
135
136
|
});
|
|
@@ -150,7 +151,7 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
150
151
|
{
|
|
151
152
|
// parent: globalTestScope,
|
|
152
153
|
stateStore: defaultOptions?.stateStore,
|
|
153
|
-
}
|
|
154
|
+
}
|
|
154
155
|
);
|
|
155
156
|
test.scope = localTestScope;
|
|
156
157
|
|
|
@@ -170,7 +171,7 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
170
171
|
|
|
171
172
|
// Clean up test scope after all tests complete
|
|
172
173
|
afterAll(async () => {
|
|
173
|
-
if (defaultOptions?.destroy !== false) {
|
|
174
|
+
if (defaultOptions?.destroy !== false && !isFailed) {
|
|
174
175
|
await alchemy.destroy(test.scope);
|
|
175
176
|
}
|
|
176
177
|
});
|
|
@@ -192,8 +193,8 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
192
193
|
obj && typeof obj === "object"
|
|
193
194
|
? Object.fromEntries(
|
|
194
195
|
Object.entries(obj).flatMap(([k, v]) =>
|
|
195
|
-
v !== undefined ? [[k, v]] : []
|
|
196
|
-
)
|
|
196
|
+
v !== undefined ? [[k, v]] : []
|
|
197
|
+
)
|
|
197
198
|
)
|
|
198
199
|
: {};
|
|
199
200
|
|
|
@@ -229,7 +230,7 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
229
230
|
}
|
|
230
231
|
}
|
|
231
232
|
});
|
|
232
|
-
}
|
|
233
|
+
}
|
|
233
234
|
);
|
|
234
235
|
}
|
|
235
236
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import type { DefaultTheme, HeadConfig, ThemeOptions } from "vitepress";
|
|
3
|
+
import { StaticTypeScriptFile } from "../../fs/static-typescript-file";
|
|
4
|
+
|
|
5
|
+
export type VitePressConfigProps = {
|
|
6
|
+
cwd: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
head?: HeadConfig[];
|
|
10
|
+
theme?: ThemeOptions;
|
|
11
|
+
themeConfig: DefaultTheme.Config;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type VitePressConfig = Awaited<ReturnType<typeof VitePressConfig>>;
|
|
15
|
+
|
|
16
|
+
export function VitePressConfig(props: VitePressConfigProps) {
|
|
17
|
+
return StaticTypeScriptFile(
|
|
18
|
+
path.join(props.cwd, ".vitepress", "config.mts"),
|
|
19
|
+
`import { transformerTwoslash } from "@shikijs/vitepress-twoslash";
|
|
20
|
+
import footnotePlugin from "markdown-it-footnote";
|
|
21
|
+
import { defineConfig } from "vitepress";
|
|
22
|
+
|
|
23
|
+
// https://vitepress.dev/reference/site-config
|
|
24
|
+
export default defineConfig({
|
|
25
|
+
title: ${JSON.stringify(props.title || "Alchemy")},
|
|
26
|
+
description: ${JSON.stringify(props.description || "Alchemy Docs")},
|
|
27
|
+
head: ${JSON.stringify(props.head || [])},
|
|
28
|
+
markdown: {
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
codeTransformers: [transformerTwoslash()],
|
|
31
|
+
theme: ${JSON.stringify(
|
|
32
|
+
props.theme ?? {
|
|
33
|
+
light: "light-plus",
|
|
34
|
+
dark: "dark-plus",
|
|
35
|
+
}
|
|
36
|
+
)},
|
|
37
|
+
config: (md) => md.use(footnotePlugin),
|
|
38
|
+
},
|
|
39
|
+
// https://vitepress.dev/reference/default-theme-config
|
|
40
|
+
themeConfig: ${JSON.stringify({
|
|
41
|
+
...props.themeConfig,
|
|
42
|
+
search: props.themeConfig.search ?? { provider: "local" },
|
|
43
|
+
nav: props.themeConfig.nav ?? [{ text: "Home", link: "/" }],
|
|
44
|
+
sidebar: props.themeConfig.sidebar ?? [],
|
|
45
|
+
socialLinks: props.themeConfig.socialLinks ?? [],
|
|
46
|
+
})}
|
|
47
|
+
});
|
|
48
|
+
`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import alchemy from "alchemy";
|
|
2
1
|
import yaml from "yaml";
|
|
3
2
|
import { Document } from "../../ai/document";
|
|
3
|
+
import { alchemy } from "../../alchemy";
|
|
4
|
+
import { StaticTextFile } from "../../fs";
|
|
4
5
|
import type { Secret } from "../../secret";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -184,12 +185,12 @@ export interface HomePageProps {
|
|
|
184
185
|
/**
|
|
185
186
|
* User prompt describing the design of the home page
|
|
186
187
|
*/
|
|
187
|
-
prompt
|
|
188
|
+
prompt?: string;
|
|
188
189
|
|
|
189
190
|
/**
|
|
190
191
|
* Optional extension to the built-in system prompt
|
|
191
192
|
*/
|
|
192
|
-
|
|
193
|
+
system?: string;
|
|
193
194
|
|
|
194
195
|
/**
|
|
195
196
|
* Base URL for the OpenAI API
|
|
@@ -218,7 +219,7 @@ export interface HomePageProps {
|
|
|
218
219
|
/**
|
|
219
220
|
* Output type for the HomePage resource
|
|
220
221
|
*/
|
|
221
|
-
export type HomePage =
|
|
222
|
+
export type HomePage = StaticTextFile | Document;
|
|
222
223
|
|
|
223
224
|
/**
|
|
224
225
|
* Parse YAML frontmatter to home page config
|
|
@@ -313,7 +314,7 @@ Follow these guidelines:
|
|
|
313
314
|
6. Ensure all links are properly formatted
|
|
314
315
|
7. Use appropriate markdown formatting for headings, lists, etc.
|
|
315
316
|
|
|
316
|
-
${props.
|
|
317
|
+
${props.system || ""}
|
|
317
318
|
`;
|
|
318
319
|
|
|
319
320
|
// Convert hero and features to YAML strings if provided
|
|
@@ -322,42 +323,53 @@ ${props.systemPromptExtension || ""}
|
|
|
322
323
|
? JSON.stringify(props.features, null, 2)
|
|
323
324
|
: "";
|
|
324
325
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
${systemPrompt}
|
|
338
|
-
|
|
339
|
-
Create a VitePress homepage based on the following description:
|
|
340
|
-
${props.prompt}
|
|
341
|
-
|
|
342
|
-
${
|
|
343
|
-
props.hero
|
|
344
|
-
? `Use this hero section configuration:
|
|
345
|
-
\`\`\`json
|
|
346
|
-
${heroYaml}
|
|
347
|
-
\`\`\``
|
|
348
|
-
: ""
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
${
|
|
352
|
-
props.features
|
|
353
|
-
? `Use these features:
|
|
354
|
-
\`\`\`json
|
|
355
|
-
${featuresYaml}
|
|
356
|
-
\`\`\``
|
|
357
|
-
: ""
|
|
358
|
-
}
|
|
326
|
+
if (props.prompt) {
|
|
327
|
+
return Document(id, {
|
|
328
|
+
title: props.title,
|
|
329
|
+
path: props.outFile,
|
|
330
|
+
baseURL: props.baseURL,
|
|
331
|
+
apiKey: props.apiKey,
|
|
332
|
+
model: props.model ?? {
|
|
333
|
+
id: "claude-3-7-sonnet-latest",
|
|
334
|
+
provider: "anthropic",
|
|
335
|
+
},
|
|
336
|
+
temperature: props.temperature ?? 0.7,
|
|
337
|
+
prompt: await alchemy`
|
|
338
|
+
${systemPrompt}
|
|
359
339
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
340
|
+
Create a VitePress homepage based on the following description:
|
|
341
|
+
${props.prompt}
|
|
342
|
+
|
|
343
|
+
${
|
|
344
|
+
props.hero
|
|
345
|
+
? `Use this hero section configuration:
|
|
346
|
+
\`\`\`json
|
|
347
|
+
${heroYaml}
|
|
348
|
+
\`\`\``
|
|
349
|
+
: ""
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
${
|
|
353
|
+
props.features
|
|
354
|
+
? `Use these features:
|
|
355
|
+
\`\`\`json
|
|
356
|
+
${featuresYaml}
|
|
357
|
+
\`\`\``
|
|
358
|
+
: ""
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
The output should be a complete index.md file with proper YAML frontmatter and markdown content.
|
|
362
|
+
`,
|
|
363
|
+
});
|
|
364
|
+
} else {
|
|
365
|
+
return StaticTextFile(id, props.outFile, `---
|
|
366
|
+
${yaml.stringify({
|
|
367
|
+
layout: "home",
|
|
368
|
+
name: props.title,
|
|
369
|
+
hero: props.hero,
|
|
370
|
+
features: props.features,
|
|
371
|
+
})}
|
|
372
|
+
---
|
|
373
|
+
`);
|
|
374
|
+
}
|
|
363
375
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Process markdown files with frontmatter to generate navigation items
|
|
5
|
+
* @param directoryPath The directory containing markdown files
|
|
6
|
+
* @param linkPrefix The prefix for navigation links
|
|
7
|
+
* @returns Sorted array of navigation items with text and link properties
|
|
8
|
+
*/
|
|
9
|
+
export async function processFrontmatterFiles(
|
|
10
|
+
directoryPath: string,
|
|
11
|
+
linkPrefix: string
|
|
12
|
+
): Promise<
|
|
13
|
+
{
|
|
14
|
+
text: string;
|
|
15
|
+
link: string;
|
|
16
|
+
items?: {
|
|
17
|
+
text: string;
|
|
18
|
+
link: string;
|
|
19
|
+
items?: { text: string; link: string }[];
|
|
20
|
+
}[];
|
|
21
|
+
}[]
|
|
22
|
+
> {
|
|
23
|
+
const entries = await fs.readdir(directoryPath, { withFileTypes: true });
|
|
24
|
+
|
|
25
|
+
const processedEntries = await Promise.all(
|
|
26
|
+
entries
|
|
27
|
+
.filter((entry) => !entry.name.endsWith("index.md"))
|
|
28
|
+
.map(async (entry) => {
|
|
29
|
+
const fullPath = path.join(directoryPath, entry.name);
|
|
30
|
+
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
// Process subdirectory recursively
|
|
33
|
+
const items = await processFrontmatterFiles(
|
|
34
|
+
fullPath,
|
|
35
|
+
`${linkPrefix}/${entry.name}`
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// Create section for subdirectory
|
|
39
|
+
return {
|
|
40
|
+
text: entry.name.charAt(0).toUpperCase() + entry.name.slice(1),
|
|
41
|
+
collapsed: false,
|
|
42
|
+
items,
|
|
43
|
+
order: 10000, // Default order for directories
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Process markdown file
|
|
48
|
+
if (!entry.name.endsWith(".md")) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
53
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
54
|
+
let order = 10000;
|
|
55
|
+
let title;
|
|
56
|
+
|
|
57
|
+
if (frontmatterMatch) {
|
|
58
|
+
const frontmatter = frontmatterMatch[1];
|
|
59
|
+
const orderMatch = frontmatter.match(/order:\s*(\d+)/);
|
|
60
|
+
if (orderMatch) {
|
|
61
|
+
order = parseInt(orderMatch[1]);
|
|
62
|
+
}
|
|
63
|
+
const titleMatch = frontmatter.match(/title:\s*(.+)/);
|
|
64
|
+
if (titleMatch) {
|
|
65
|
+
title = titleMatch[1].trim();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// If no title in frontmatter, try to get first heading
|
|
70
|
+
if (!title) {
|
|
71
|
+
const headingMatch = content.match(/^#\s+(.+)$/m);
|
|
72
|
+
if (headingMatch) {
|
|
73
|
+
title = headingMatch[1].trim();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Fall back to filename if no title found
|
|
78
|
+
if (!title) {
|
|
79
|
+
const name = entry.name.replace(".md", "");
|
|
80
|
+
title = name.charAt(0).toUpperCase() + name.slice(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
text: title,
|
|
85
|
+
link: `${linkPrefix}/${entry.name}`,
|
|
86
|
+
order,
|
|
87
|
+
};
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Filter out null entries and sort by order
|
|
92
|
+
return processedEntries
|
|
93
|
+
.filter((entry) => entry !== null)
|
|
94
|
+
.sort((a, b) => a.order - b.order)
|
|
95
|
+
.map(({ text, link, items }) =>
|
|
96
|
+
items ? { text, items, collapsed: true } : { text, link }
|
|
97
|
+
) as any;
|
|
98
|
+
}
|
|
@@ -2,7 +2,6 @@ import { exec } from "child_process";
|
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { promisify } from "util";
|
|
5
|
-
import type { DefaultTheme, ThemeOptions } from "vitepress";
|
|
6
5
|
import type { Context } from "../../context";
|
|
7
6
|
import {
|
|
8
7
|
Folder,
|
|
@@ -70,12 +69,12 @@ export interface VitePressProjectProps {
|
|
|
70
69
|
/**
|
|
71
70
|
* The theme options to use
|
|
72
71
|
*/
|
|
73
|
-
theme?: ThemeOptions;
|
|
72
|
+
// theme?: ThemeOptions;
|
|
74
73
|
|
|
75
74
|
/**
|
|
76
75
|
* The theme config to use
|
|
77
76
|
*/
|
|
78
|
-
themeConfig: DefaultTheme.Config;
|
|
77
|
+
// themeConfig: DefaultTheme.Config;
|
|
79
78
|
|
|
80
79
|
/**
|
|
81
80
|
* Whether to delete the project folder during the delete phase
|
|
@@ -96,25 +95,31 @@ export interface VitePressProject extends VitePressProjectProps, Resource {
|
|
|
96
95
|
* The name/path of the project
|
|
97
96
|
*/
|
|
98
97
|
name: string;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The directory of the project
|
|
101
|
+
*/
|
|
102
|
+
dir: string;
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
export const
|
|
102
|
-
"project::
|
|
105
|
+
export const VitepressProject = Resource(
|
|
106
|
+
"project::VitepressProject",
|
|
103
107
|
{
|
|
104
108
|
alwaysUpdate: true,
|
|
105
109
|
},
|
|
106
110
|
async function (
|
|
107
111
|
this: Context<VitePressProject>,
|
|
108
112
|
id: string,
|
|
109
|
-
props: VitePressProjectProps
|
|
113
|
+
props: VitePressProjectProps
|
|
110
114
|
): Promise<VitePressProject> {
|
|
111
115
|
const dir = props.dir ?? props.name;
|
|
112
116
|
if (this.phase === "delete") {
|
|
113
117
|
try {
|
|
114
118
|
if (props.delete !== false) {
|
|
115
119
|
if (await fs.exists(dir)) {
|
|
116
|
-
|
|
117
|
-
|
|
120
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
121
|
+
// // Delete the entire project directory
|
|
122
|
+
// await execAsync(`rm -rf ${dir}`);
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
} catch (error) {
|
|
@@ -130,7 +135,7 @@ export const VitePressProject = Resource(
|
|
|
130
135
|
path: dir,
|
|
131
136
|
delete: props.delete,
|
|
132
137
|
})
|
|
133
|
-
).path
|
|
138
|
+
).path
|
|
134
139
|
);
|
|
135
140
|
|
|
136
141
|
// Initialize package.json
|
|
@@ -164,11 +169,19 @@ export const VitePressProject = Resource(
|
|
|
164
169
|
|
|
165
170
|
await Promise.all([
|
|
166
171
|
StaticTextFile(path.join(cwd, ".gitignore"), `.vitepress/cache\n`),
|
|
167
|
-
StaticJsonFile(path.join(cwd, "tsconfig.json"), {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
+
// StaticJsonFile(path.join(cwd, "tsconfig.json"), {
|
|
173
|
+
// extends: props.tsconfig?.extends,
|
|
174
|
+
// references: props.tsconfig?.references?.map((path) => ({ path })),
|
|
175
|
+
// compilerOptions: props.tsconfig?.compilerOptions ?? {
|
|
176
|
+
// target: "ES2020",
|
|
177
|
+
// module: "ESNext",
|
|
178
|
+
// moduleResolution: "Node",
|
|
179
|
+
// allowJs: true,
|
|
180
|
+
// skipLibCheck: true,
|
|
181
|
+
// forceConsistentCasingInFileNames: true,
|
|
182
|
+
|
|
183
|
+
// },
|
|
184
|
+
// }),
|
|
172
185
|
|
|
173
186
|
// TypeScriptFile(
|
|
174
187
|
// path.join(cwd, "alchemy.run.ts"),
|
|
@@ -206,7 +219,7 @@ export default {
|
|
|
206
219
|
ctx.app.use(TwoslashFloatingVue);
|
|
207
220
|
},
|
|
208
221
|
} satisfies ThemeConfig;
|
|
209
|
-
|
|
222
|
+
`
|
|
210
223
|
),
|
|
211
224
|
StaticTextFile(
|
|
212
225
|
path.join(cwd, ".vitepress", "theme", "style.css"),
|
|
@@ -348,42 +361,13 @@ export default {
|
|
|
348
361
|
.DocSearch {
|
|
349
362
|
--docsearch-primary-color: var(--vp-c-brand-1) !important;
|
|
350
363
|
}
|
|
351
|
-
|
|
352
|
-
),
|
|
353
|
-
StaticTypeScriptFile(
|
|
354
|
-
path.join(cwd, ".vitepress", "config.mts"),
|
|
355
|
-
`import { transformerTwoslash } from "@shikijs/vitepress-twoslash";
|
|
356
|
-
import footnotePlugin from "markdown-it-footnote";
|
|
357
|
-
import { defineConfig } from "vitepress";
|
|
358
|
-
|
|
359
|
-
// https://vitepress.dev/reference/site-config
|
|
360
|
-
export default defineConfig({
|
|
361
|
-
title: ${JSON.stringify(props.title || "Alchemy")},
|
|
362
|
-
description: ${JSON.stringify(props.description || "Alchemy Docs")},
|
|
363
|
-
markdown: {
|
|
364
|
-
// @ts-ignore
|
|
365
|
-
codeTransformers: [transformerTwoslash()],
|
|
366
|
-
theme: ${JSON.stringify(
|
|
367
|
-
props.theme ?? {
|
|
368
|
-
light: "light-plus",
|
|
369
|
-
dark: "dark-plus",
|
|
370
|
-
},
|
|
371
|
-
)},
|
|
372
|
-
config: (md) => md.use(footnotePlugin),
|
|
373
|
-
},
|
|
374
|
-
// https://vitepress.dev/reference/default-theme-config
|
|
375
|
-
themeConfig: ${JSON.stringify({
|
|
376
|
-
...props.themeConfig,
|
|
377
|
-
search: props.themeConfig.search ?? { provider: "local" },
|
|
378
|
-
nav: props.themeConfig.nav ?? [{ text: "Home", link: "/" }],
|
|
379
|
-
sidebar: props.themeConfig.sidebar ?? [],
|
|
380
|
-
socialLinks: props.themeConfig.socialLinks ?? [],
|
|
381
|
-
})}
|
|
382
|
-
});
|
|
383
|
-
`,
|
|
364
|
+
`
|
|
384
365
|
),
|
|
385
366
|
]);
|
|
386
367
|
|
|
387
|
-
return this(
|
|
388
|
-
|
|
368
|
+
return this({
|
|
369
|
+
...props,
|
|
370
|
+
dir,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
389
373
|
);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Document } from "alchemy/ai";
|
|
2
|
-
import type { Folder } from "alchemy/fs";
|
|
3
|
-
export interface GettingStartedProps {
|
|
4
|
-
/**
|
|
5
|
-
* The output directory for the getting started document.
|
|
6
|
-
*/
|
|
7
|
-
outDir: string | Folder;
|
|
8
|
-
}
|
|
9
|
-
export type GettingStarted = Document;
|
|
10
|
-
export declare function GettingStarted({ outDir, }: GettingStartedProps): Promise<Document>;
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import alchemy from "alchemy";
|
|
2
|
-
import { Document } from "alchemy/ai";
|
|
3
|
-
import path from "path";
|
|
4
|
-
export async function GettingStarted({ outDir, }) {
|
|
5
|
-
outDir = typeof outDir === "string" ? outDir : outDir.path;
|
|
6
|
-
return Document(`docs/getting-started`, {
|
|
7
|
-
title: "Getting Started with Alchemy",
|
|
8
|
-
path: path.join(outDir, "index.md"),
|
|
9
|
-
prompt: await alchemy `
|
|
10
|
-
You are a technical writer creating a getting started guide for Alchemy, an infrastructure as code (IaC) framework.
|
|
11
|
-
See ${alchemy.file("../README.md")} to understand the overview of Alchemy.
|
|
12
|
-
See ${alchemy.file("../.cursorrules")} to better understand the structure and conventions of Alchemy.
|
|
13
|
-
|
|
14
|
-
See ${alchemy.file("../alchemy/test/cloudflare/worker.test.ts")} for an example of how testing works.
|
|
15
|
-
|
|
16
|
-
Write a comprehensive getting started guide for Alchemy that covers the following topics:
|
|
17
|
-
|
|
18
|
-
# Getting Started with Alchemy
|
|
19
|
-
|
|
20
|
-
(Brief introduction to Alchemy as an infrastructure as code framework)
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
\`\`\`bash
|
|
25
|
-
# Install Alchemy
|
|
26
|
-
bun add alchemy
|
|
27
|
-
\`\`\`
|
|
28
|
-
|
|
29
|
-
## Creating Your First Alchemy Project
|
|
30
|
-
|
|
31
|
-
(Step-by-step guide to creating a basic Alchemy project)
|
|
32
|
-
|
|
33
|
-
\`\`\`ts
|
|
34
|
-
// Example of a simple Alchemy project
|
|
35
|
-
import alchemy from "alchemy";
|
|
36
|
-
|
|
37
|
-
// Sample code here
|
|
38
|
-
\`\`\`
|
|
39
|
-
|
|
40
|
-
## Core Concepts
|
|
41
|
-
|
|
42
|
-
### Resources
|
|
43
|
-
|
|
44
|
-
(Explanation of Alchemy resources and how they work)
|
|
45
|
-
|
|
46
|
-
\`\`\`ts
|
|
47
|
-
// Example of creating a resource
|
|
48
|
-
\`\`\`
|
|
49
|
-
|
|
50
|
-
### Context
|
|
51
|
-
|
|
52
|
-
(Explanation of Alchemy context and lifecycle management)
|
|
53
|
-
|
|
54
|
-
\`\`\`ts
|
|
55
|
-
// Example of working with context
|
|
56
|
-
\`\`\`
|
|
57
|
-
|
|
58
|
-
## Working with Secrets
|
|
59
|
-
|
|
60
|
-
(How to handle secrets in Alchemy)
|
|
61
|
-
|
|
62
|
-
\`\`\`ts
|
|
63
|
-
// Example of using alchemy.secret()
|
|
64
|
-
const apiKey = alchemy.secret("API_KEY");
|
|
65
|
-
\`\`\`
|
|
66
|
-
|
|
67
|
-
## Testing
|
|
68
|
-
|
|
69
|
-
(How to test Alchemy resources)
|
|
70
|
-
|
|
71
|
-
\`\`\`ts
|
|
72
|
-
// Example of testing Alchemy resources
|
|
73
|
-
\`\`\`
|
|
74
|
-
|
|
75
|
-
## Deployment
|
|
76
|
-
|
|
77
|
-
(How to deploy resources with Alchemy)
|
|
78
|
-
|
|
79
|
-
\`\`\`ts
|
|
80
|
-
// Example of deployment
|
|
81
|
-
\`\`\`
|
|
82
|
-
|
|
83
|
-
## Next Steps
|
|
84
|
-
|
|
85
|
-
(Where to go next to learn more about Alchemy)
|
|
86
|
-
|
|
87
|
-
> [!CAUTION]
|
|
88
|
-
> Avoid the temptation to over explain or over describe. Focus on concise, simple, high value snippets.
|
|
89
|
-
|
|
90
|
-
> [!TIP]
|
|
91
|
-
> Make sure the examples follow a natural progression from minimal examples to more complex use cases.
|
|
92
|
-
`,
|
|
93
|
-
});
|
|
94
|
-
}
|