alchemy 0.2.2 → 0.2.4
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/ai/ark.d.ts +11 -0
- package/lib/ai/ark.js +86 -0
- package/lib/ai/client.d.ts +51 -0
- package/lib/ai/client.js +29 -0
- package/lib/ai/document.d.ts +97 -0
- package/lib/ai/document.js +82 -0
- package/lib/ai/index.d.ts +2 -0
- package/lib/ai/index.js +2 -0
- package/lib/ai/object.d.ts +101 -0
- package/lib/ai/object.js +70 -0
- package/lib/alchemy.d.ts +84 -2
- package/lib/alchemy.js +114 -10
- package/lib/apply.d.ts +1 -1
- package/lib/apply.js +13 -10
- package/lib/aws/bucket.d.ts +87 -1
- package/lib/aws/bucket.js +48 -1
- package/lib/aws/function.d.ts +186 -1
- package/lib/aws/function.js +62 -8
- package/lib/aws/oidc/github-oidc-provider.d.ts +81 -0
- package/lib/aws/oidc/github-oidc-provider.js +68 -0
- package/lib/aws/oidc/oidc-provider.d.ts +14 -6
- package/lib/aws/oidc/oidc-provider.js +26 -5
- package/lib/aws/policy-attachment.d.ts +44 -1
- package/lib/aws/policy-attachment.js +31 -0
- package/lib/aws/policy.d.ts +160 -1
- package/lib/aws/policy.js +78 -0
- package/lib/aws/queue.d.ts +83 -1
- package/lib/aws/queue.js +39 -0
- package/lib/aws/role.d.ts +162 -1
- package/lib/aws/role.js +114 -0
- package/lib/aws/ses.d.ts +57 -2
- package/lib/aws/ses.js +42 -1
- package/lib/aws/table.d.ts +96 -1
- package/lib/aws/table.js +36 -0
- package/lib/cloudflare/asset-manifest.js +0 -1
- package/lib/cloudflare/bound.js +0 -1
- package/lib/cloudflare/bucket.d.ts +37 -1
- package/lib/cloudflare/bucket.js +36 -0
- package/lib/cloudflare/durable-object-namespace.d.ts +25 -0
- package/lib/cloudflare/durable-object-namespace.js +22 -0
- package/lib/cloudflare/kv-namespace.d.ts +37 -1
- package/lib/cloudflare/kv-namespace.js +36 -0
- package/lib/cloudflare/static-site.d.ts +53 -1
- package/lib/cloudflare/static-site.js +53 -1
- package/lib/cloudflare/worker-metadata.js +0 -1
- package/lib/cloudflare/worker.d.ts +64 -1
- package/lib/cloudflare/worker.js +63 -0
- package/lib/cloudflare/wrangler.json.d.ts +1 -1
- package/lib/cloudflare/zone-settings.js +0 -1
- package/lib/cloudflare/zone.d.ts +56 -1
- package/lib/cloudflare/zone.js +55 -0
- package/lib/context.d.ts +9 -4
- package/lib/context.js +4 -2
- package/lib/destroy.js +9 -8
- package/lib/esbuild/bundle.d.ts +43 -4
- package/lib/esbuild/bundle.js +16 -0
- package/lib/fs/file-collection.d.ts +16 -0
- package/lib/fs/file-collection.js +6 -0
- package/lib/fs/file-ref.d.ts +14 -0
- package/lib/fs/file-ref.js +6 -0
- package/lib/fs/file.d.ts +78 -5
- package/lib/fs/file.js +39 -3
- package/lib/fs/folder.d.ts +37 -5
- package/lib/fs/folder.js +28 -3
- package/lib/fs/index.d.ts +6 -0
- package/lib/fs/index.js +6 -0
- package/lib/fs/json-file.d.ts +16 -0
- package/lib/fs/json-file.js +7 -0
- package/lib/fs/text-file.d.ts +12 -0
- package/lib/fs/text-file.js +7 -0
- package/lib/fs/typescript-file.d.ts +19 -0
- package/lib/fs/typescript-file.js +14 -0
- package/lib/fs/yaml-file.d.ts +19 -0
- package/lib/fs/yaml-file.js +8 -0
- package/lib/github/secret.d.ts +63 -2
- package/lib/github/secret.js +61 -1
- package/lib/internal/docs.d.ts +5 -0
- package/lib/internal/docs.js +324 -0
- package/lib/resource.d.ts +2 -2
- package/lib/resource.js +4 -0
- package/lib/scope.d.ts +1 -1
- package/lib/scope.js +6 -6
- package/lib/secret.d.ts +67 -0
- package/lib/secret.js +67 -0
- package/lib/shadcn/component.d.ts +1 -1
- package/lib/state.d.ts +1 -1
- package/lib/state.js +8 -1
- package/lib/stripe/price.d.ts +51 -1
- package/lib/stripe/price.js +50 -0
- package/lib/stripe/product.d.ts +39 -1
- package/lib/stripe/product.js +38 -0
- package/lib/stripe/webhook.d.ts +46 -1
- package/lib/stripe/webhook.js +45 -0
- package/lib/test/bun.d.ts +64 -0
- package/lib/test/bun.js +35 -3
- package/lib/util/serde.d.ts +3 -1
- package/lib/util/serde.js +20 -4
- package/lib/vite/vite.d.ts +1 -1
- package/lib/vitepress/dependencies.d.ts +12 -0
- package/lib/vitepress/dependencies.js +35 -0
- package/lib/vitepress/home-page.d.ts +133 -0
- package/lib/vitepress/home-page.js +11 -0
- package/lib/vitepress/index.d.ts +2 -0
- package/lib/vitepress/index.js +2 -0
- package/lib/vitepress/vitepress.d.ts +71 -0
- package/lib/vitepress/vitepress.js +266 -0
- package/package.json +22 -10
- package/src/ai/ark.ts +118 -0
- package/src/ai/client.ts +78 -0
- package/src/ai/document.ts +160 -0
- package/src/ai/index.ts +2 -0
- package/src/ai/object.ts +149 -0
- package/src/alchemy.ts +192 -17
- package/src/apply.ts +25 -13
- package/src/aws/bucket.ts +111 -7
- package/src/aws/function.ts +231 -10
- package/src/aws/oidc/github-oidc-provider.ts +83 -0
- package/src/aws/oidc/oidc-provider.ts +39 -7
- package/src/aws/policy-attachment.ts +44 -1
- package/src/aws/policy.ts +177 -2
- package/src/aws/queue.ts +89 -0
- package/src/aws/role.ts +174 -2
- package/src/aws/ses.ts +56 -1
- package/src/aws/table.ts +103 -0
- package/src/cloudflare/bucket.ts +36 -0
- package/src/cloudflare/durable-object-namespace.ts +25 -0
- package/src/cloudflare/kv-namespace.ts +36 -0
- package/src/cloudflare/static-site.ts +53 -1
- package/src/cloudflare/worker.ts +63 -0
- package/src/cloudflare/zone.ts +55 -0
- package/src/context.ts +30 -10
- package/src/destroy.ts +11 -8
- package/src/esbuild/bundle.ts +53 -3
- package/src/fs/file-collection.ts +24 -0
- package/src/fs/file-ref.ts +22 -0
- package/src/fs/file.ts +107 -6
- package/src/fs/folder.ts +45 -5
- package/src/fs/index.ts +6 -0
- package/src/fs/json-file.ts +23 -0
- package/src/fs/text-file.ts +19 -0
- package/src/fs/typescript-file.ts +36 -0
- package/src/fs/yaml-file.ts +26 -0
- package/src/github/secret.ts +65 -2
- package/src/internal/docs.ts +369 -0
- package/src/resource.ts +7 -2
- package/src/scope.ts +9 -9
- package/src/secret.ts +67 -0
- package/src/state.ts +9 -2
- package/src/stripe/price.ts +50 -0
- package/src/stripe/product.ts +38 -0
- package/src/stripe/webhook.ts +45 -0
- package/src/test/bun.ts +83 -3
- package/src/util/serde.ts +30 -4
- package/src/vitepress/dependencies.ts +68 -0
- package/src/vitepress/home-page.ts +166 -0
- package/src/vitepress/index.md +75 -0
- package/src/vitepress/index.ts +2 -0
- package/src/vitepress/vitepress.ts +382 -0
- package/lib/project/vite.d.ts +0 -90
- package/lib/project/vite.js +0 -228
- package/src/project/vite.ts +0 -406
package/src/stripe/webhook.ts
CHANGED
|
@@ -86,6 +86,51 @@ export interface WebhookEndpoint
|
|
|
86
86
|
status: string;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Create and manage Stripe webhook endpoints
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* // Create a basic webhook for payment events
|
|
94
|
+
* const paymentWebhook = await WebhookEndpoint("payment-webhook", {
|
|
95
|
+
* url: "https://api.example.com/stripe/payments",
|
|
96
|
+
* enabledEvents: [
|
|
97
|
+
* "payment_intent.succeeded",
|
|
98
|
+
* "payment_intent.payment_failed"
|
|
99
|
+
* ],
|
|
100
|
+
* description: "Webhook for payment notifications"
|
|
101
|
+
* });
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // Create a webhook for subscription management
|
|
105
|
+
* const subscriptionWebhook = await WebhookEndpoint("subscription-webhook", {
|
|
106
|
+
* url: "https://api.example.com/stripe/subscriptions",
|
|
107
|
+
* enabledEvents: [
|
|
108
|
+
* "customer.subscription.created",
|
|
109
|
+
* "customer.subscription.updated",
|
|
110
|
+
* "customer.subscription.deleted",
|
|
111
|
+
* "invoice.payment_succeeded",
|
|
112
|
+
* "invoice.payment_failed"
|
|
113
|
+
* ],
|
|
114
|
+
* description: "Webhook for subscription lifecycle events"
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* // Create a webhook for Connect platform events
|
|
119
|
+
* const connectWebhook = await WebhookEndpoint("connect-webhook", {
|
|
120
|
+
* url: "https://api.example.com/stripe/connect",
|
|
121
|
+
* enabledEvents: [
|
|
122
|
+
* "account.updated",
|
|
123
|
+
* "account.application.deauthorized",
|
|
124
|
+
* "payout.created",
|
|
125
|
+
* "payout.failed"
|
|
126
|
+
* ],
|
|
127
|
+
* connect: true,
|
|
128
|
+
* metadata: {
|
|
129
|
+
* platform: "connect",
|
|
130
|
+
* environment: "production"
|
|
131
|
+
* }
|
|
132
|
+
* });
|
|
133
|
+
*/
|
|
89
134
|
export const WebhookEndpoint = Resource(
|
|
90
135
|
"stripe::WebhookEndpoint",
|
|
91
136
|
async function (
|
package/src/test/bun.ts
CHANGED
|
@@ -6,35 +6,111 @@ import { alchemy } from "../alchemy";
|
|
|
6
6
|
import { destroy } from "../destroy";
|
|
7
7
|
import type { Scope } from "../scope";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Extend the Alchemy interface to include test functionality
|
|
11
|
+
*/
|
|
9
12
|
declare module "../alchemy" {
|
|
10
13
|
interface Alchemy {
|
|
11
14
|
test: typeof test;
|
|
12
15
|
}
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Add test functionality to alchemy instance
|
|
20
|
+
*/
|
|
15
21
|
alchemy.test = test;
|
|
16
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Options for configuring test behavior
|
|
25
|
+
*/
|
|
17
26
|
export interface TestOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Whether to automatically destroy resources after test.
|
|
29
|
+
* @default true.
|
|
30
|
+
*/
|
|
18
31
|
destroy?: boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Whether to suppress logging output.
|
|
35
|
+
* @default false.
|
|
36
|
+
*/
|
|
19
37
|
quiet?: boolean;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Password to use for test resources.
|
|
41
|
+
* @default "test-password".
|
|
42
|
+
*/
|
|
20
43
|
password?: string;
|
|
21
44
|
}
|
|
22
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Global test scope for all tests
|
|
48
|
+
*/
|
|
23
49
|
const globalTestScope = alchemy.scope("test");
|
|
24
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Test function type definition with overloads
|
|
53
|
+
*/
|
|
25
54
|
type test = {
|
|
55
|
+
/**
|
|
56
|
+
* Create a test with default options
|
|
57
|
+
* @param name Test name
|
|
58
|
+
* @param fn Test function
|
|
59
|
+
* @param timeout Optional timeout in milliseconds
|
|
60
|
+
*/
|
|
26
61
|
(name: string, fn: (scope: Scope) => Promise<any>, timeout?: number): void;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a test with custom options
|
|
65
|
+
* @param name Test name
|
|
66
|
+
* @param options Test configuration options
|
|
67
|
+
* @param fn Test function
|
|
68
|
+
* @param timeout Optional timeout in milliseconds
|
|
69
|
+
*/
|
|
27
70
|
(
|
|
28
71
|
name: string,
|
|
29
72
|
options: TestOptions,
|
|
30
73
|
fn: (scope: Scope) => Promise<any>,
|
|
31
74
|
timeout?: number,
|
|
32
75
|
): void;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Skip test conditionally
|
|
79
|
+
* @param condition If true, test will be skipped
|
|
80
|
+
*/
|
|
33
81
|
skipIf(condition: boolean): test;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Current test scope
|
|
85
|
+
*/
|
|
34
86
|
scope: Scope;
|
|
35
87
|
};
|
|
36
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Creates a test helper function that provides scoped resource management
|
|
91
|
+
*
|
|
92
|
+
* @param meta Import meta object from the test file
|
|
93
|
+
* @param defaultOptions Default options to apply to all tests
|
|
94
|
+
* @returns Test function with scope management
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const test = alchemy.test(import.meta);
|
|
99
|
+
*
|
|
100
|
+
* describe("My Resource", () => {
|
|
101
|
+
* test("create and delete", async (scope) => {
|
|
102
|
+
* try {
|
|
103
|
+
* const resource = await MyResource("test", { ... });
|
|
104
|
+
* expect(resource.id).toBeTruthy();
|
|
105
|
+
* } finally {
|
|
106
|
+
* await alchemy.destroy(scope);
|
|
107
|
+
* }
|
|
108
|
+
* });
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
37
112
|
export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
113
|
+
// Add skipIf functionality
|
|
38
114
|
test.skipIf = (condition: boolean) => {
|
|
39
115
|
if (condition) {
|
|
40
116
|
// TODO: proxy through to bun:test.skipIf
|
|
@@ -43,11 +119,13 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
43
119
|
return test;
|
|
44
120
|
};
|
|
45
121
|
|
|
122
|
+
// Create local test scope based on filename
|
|
46
123
|
const localTestScope = alchemy.scope(path.basename(meta.filename), {
|
|
47
124
|
parent: globalTestScope,
|
|
48
125
|
});
|
|
49
126
|
test.scope = localTestScope;
|
|
50
127
|
|
|
128
|
+
// Clean up test scope after all tests complete
|
|
51
129
|
afterAll(() => alchemy.destroy(test.scope));
|
|
52
130
|
|
|
53
131
|
return test as any;
|
|
@@ -71,13 +149,16 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
71
149
|
),
|
|
72
150
|
)
|
|
73
151
|
: {};
|
|
152
|
+
|
|
153
|
+
// Merge options with defaults
|
|
74
154
|
const options = {
|
|
75
|
-
destroy:
|
|
155
|
+
destroy: true,
|
|
76
156
|
quiet: false,
|
|
77
157
|
password: "test-password",
|
|
78
158
|
...spread(defaultOptions),
|
|
79
159
|
...spread(_options),
|
|
80
160
|
};
|
|
161
|
+
|
|
81
162
|
const fn = typeof args[1] === "function" ? args[1] : args[2]!;
|
|
82
163
|
|
|
83
164
|
return alchemy.run(
|
|
@@ -88,13 +169,12 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
|
|
|
88
169
|
},
|
|
89
170
|
async (scope) => {
|
|
90
171
|
return it(testName, async () => {
|
|
91
|
-
//
|
|
172
|
+
// Enter test scope since bun calls from different scope
|
|
92
173
|
scope.enter();
|
|
93
174
|
try {
|
|
94
175
|
await fn(scope);
|
|
95
176
|
} finally {
|
|
96
177
|
if (options.destroy !== false) {
|
|
97
|
-
// TODO: auto-destroy resources
|
|
98
178
|
await destroy(scope);
|
|
99
179
|
}
|
|
100
180
|
}
|
package/src/util/serde.ts
CHANGED
|
@@ -2,15 +2,39 @@ import { Scope } from "../scope";
|
|
|
2
2
|
import { Secret } from "../secret";
|
|
3
3
|
import { decryptWithKey, encryptWithKey } from "./encrypt";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import type { Type } from "arktype";
|
|
6
|
+
|
|
7
|
+
// zero-dependency type guard for ArkType
|
|
8
|
+
function isType(value: any): value is Type<any, any> {
|
|
9
|
+
return (
|
|
10
|
+
value &&
|
|
11
|
+
typeof value === "object" &&
|
|
12
|
+
typeof value.toJsonSchema === "function"
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function serialize(
|
|
17
|
+
scope: Scope,
|
|
18
|
+
value: any,
|
|
19
|
+
options?: {
|
|
20
|
+
encrypt?: boolean;
|
|
21
|
+
},
|
|
22
|
+
): Promise<any> {
|
|
6
23
|
if (Array.isArray(value)) {
|
|
7
|
-
return Promise.all(value.map((value) => serialize(scope, value)));
|
|
24
|
+
return Promise.all(value.map((value) => serialize(scope, value, options)));
|
|
8
25
|
} else if (value instanceof Secret) {
|
|
9
26
|
if (!scope.password) {
|
|
10
27
|
throw new Error("Cannot serialize secret without password");
|
|
11
28
|
}
|
|
12
29
|
return {
|
|
13
|
-
"@secret":
|
|
30
|
+
"@secret":
|
|
31
|
+
options?.encrypt !== false
|
|
32
|
+
? await encryptWithKey(value.unencrypted, scope.password)
|
|
33
|
+
: value.unencrypted,
|
|
34
|
+
};
|
|
35
|
+
} else if (isType(value)) {
|
|
36
|
+
return {
|
|
37
|
+
"@schema": value.toJSON(),
|
|
14
38
|
};
|
|
15
39
|
} else if (value instanceof Scope) {
|
|
16
40
|
return undefined;
|
|
@@ -19,7 +43,7 @@ export async function serialize(scope: Scope, value: any): Promise<any> {
|
|
|
19
43
|
await Promise.all(
|
|
20
44
|
Object.entries(value).map(async ([key, value]) => [
|
|
21
45
|
key,
|
|
22
|
-
await serialize(scope, value),
|
|
46
|
+
await serialize(scope, value, options),
|
|
23
47
|
]),
|
|
24
48
|
),
|
|
25
49
|
);
|
|
@@ -38,6 +62,8 @@ export async function deserialize(scope: Scope, value: any): Promise<any> {
|
|
|
38
62
|
throw new Error("Cannot deserialize secret without password");
|
|
39
63
|
}
|
|
40
64
|
return new Secret(await decryptWithKey(value["@secret"], scope.password));
|
|
65
|
+
} else if ("@schema" in value) {
|
|
66
|
+
return value["@schema"];
|
|
41
67
|
} else {
|
|
42
68
|
return Object.fromEntries(
|
|
43
69
|
await Promise.all(
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
import type { Context } from "../context";
|
|
4
|
+
import { Resource } from "../resource";
|
|
5
|
+
|
|
6
|
+
const execAsync = promisify(exec);
|
|
7
|
+
|
|
8
|
+
export type InstallDependencies = Resource<"project::InstallDependencies">;
|
|
9
|
+
|
|
10
|
+
export const InstallDependencies = Resource(
|
|
11
|
+
"project::InstallDependencies",
|
|
12
|
+
{
|
|
13
|
+
alwaysUpdate: true,
|
|
14
|
+
},
|
|
15
|
+
async function (
|
|
16
|
+
this: Context<InstallDependencies>,
|
|
17
|
+
id: string,
|
|
18
|
+
{
|
|
19
|
+
cwd,
|
|
20
|
+
dependencies,
|
|
21
|
+
devDependencies,
|
|
22
|
+
}: {
|
|
23
|
+
cwd: string;
|
|
24
|
+
dependencies?: Record<string, string>;
|
|
25
|
+
devDependencies?: Record<string, string>;
|
|
26
|
+
},
|
|
27
|
+
) {
|
|
28
|
+
if (this.phase === "delete") {
|
|
29
|
+
return this.destroy();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await installDependencies(cwd, dependencies);
|
|
33
|
+
await installDependencies(cwd, devDependencies, "-D");
|
|
34
|
+
await execAsync("bun install", { cwd });
|
|
35
|
+
|
|
36
|
+
return this({});
|
|
37
|
+
},
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
function isInstallableVersion(version: string) {
|
|
41
|
+
// TODO: file:// ?
|
|
42
|
+
return !version.startsWith("workspace:");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function fixedDependencies(dependencies?: Record<string, string>) {
|
|
46
|
+
if (!dependencies) return {};
|
|
47
|
+
return Object.fromEntries(
|
|
48
|
+
Object.entries(dependencies).filter(
|
|
49
|
+
([, value]) => !isInstallableVersion(value),
|
|
50
|
+
),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function installDependencies(
|
|
55
|
+
cwd: string,
|
|
56
|
+
dependencies: Record<string, string> | undefined,
|
|
57
|
+
...args: string[]
|
|
58
|
+
) {
|
|
59
|
+
if (!dependencies) return;
|
|
60
|
+
const deps = Object.entries(dependencies)
|
|
61
|
+
.filter(([, value]) => isInstallableVersion(value))
|
|
62
|
+
.map(([pkg, version]) => `${pkg}@${version}`)
|
|
63
|
+
.join(" ");
|
|
64
|
+
|
|
65
|
+
const cmd = `bun add ${args.join(" ")} ${deps}`;
|
|
66
|
+
console.log(cmd);
|
|
67
|
+
await execAsync(cmd, { cwd });
|
|
68
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import yaml from "yaml";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Image that can be themed for light/dark mode
|
|
5
|
+
*/
|
|
6
|
+
export interface ThemeableImage {
|
|
7
|
+
/**
|
|
8
|
+
* Image source URL
|
|
9
|
+
*/
|
|
10
|
+
src?: string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Image alt text
|
|
14
|
+
*/
|
|
15
|
+
alt?: string;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Light theme image URL
|
|
19
|
+
*/
|
|
20
|
+
light?: string;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Dark theme image URL
|
|
24
|
+
*/
|
|
25
|
+
dark?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Hero action button configuration
|
|
30
|
+
*/
|
|
31
|
+
export interface HeroAction {
|
|
32
|
+
/**
|
|
33
|
+
* Color theme of the button
|
|
34
|
+
* @default "brand"
|
|
35
|
+
*/
|
|
36
|
+
theme?: "brand" | "alt";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Label of the button
|
|
40
|
+
*/
|
|
41
|
+
text: string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Destination link of the button
|
|
45
|
+
*/
|
|
46
|
+
link: string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Link target attribute
|
|
50
|
+
*/
|
|
51
|
+
target?: string;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Link rel attribute
|
|
55
|
+
*/
|
|
56
|
+
rel?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Hero section configuration
|
|
61
|
+
*/
|
|
62
|
+
export interface Hero {
|
|
63
|
+
/**
|
|
64
|
+
* The string shown top of text. Comes with brand color
|
|
65
|
+
* and expected to be short, such as product name.
|
|
66
|
+
*/
|
|
67
|
+
name?: string;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The main text for the hero section. This will be defined
|
|
71
|
+
* as h1 tag.
|
|
72
|
+
*/
|
|
73
|
+
text: string;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Tagline displayed below text
|
|
77
|
+
*/
|
|
78
|
+
tagline?: string;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The image displayed next to the text and tagline area
|
|
82
|
+
*/
|
|
83
|
+
image?: string | ThemeableImage;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Action buttons to display in home hero section
|
|
87
|
+
*/
|
|
88
|
+
actions?: HeroAction[];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Feature item configuration
|
|
93
|
+
*/
|
|
94
|
+
export interface Feature {
|
|
95
|
+
/**
|
|
96
|
+
* Show icon on each feature box
|
|
97
|
+
*/
|
|
98
|
+
icon?: string | ThemeableImage;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Title of the feature
|
|
102
|
+
*/
|
|
103
|
+
title: string;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Details of the feature
|
|
107
|
+
*/
|
|
108
|
+
details: string;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Link when clicked on feature component
|
|
112
|
+
*/
|
|
113
|
+
link?: string;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Link text to be shown inside feature component
|
|
117
|
+
*/
|
|
118
|
+
linkText?: string;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Link rel attribute
|
|
122
|
+
*/
|
|
123
|
+
rel?: string;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Link target attribute
|
|
127
|
+
*/
|
|
128
|
+
target?: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* VitePress home page configuration
|
|
133
|
+
*/
|
|
134
|
+
export interface HomePage {
|
|
135
|
+
/**
|
|
136
|
+
* Use home page layout
|
|
137
|
+
*/
|
|
138
|
+
layout: "home";
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Hero section configuration
|
|
142
|
+
*/
|
|
143
|
+
hero?: Hero;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Features section configuration
|
|
147
|
+
*/
|
|
148
|
+
features?: Feature[];
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Whether to apply default markdown styles
|
|
152
|
+
* @default true
|
|
153
|
+
*/
|
|
154
|
+
markdownStyles?: boolean;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Parse YAML frontmatter to home page config
|
|
159
|
+
*/
|
|
160
|
+
export function parseHomePage(content: string): HomePage {
|
|
161
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
162
|
+
if (!match) {
|
|
163
|
+
throw new Error("Invalid frontmatter format");
|
|
164
|
+
}
|
|
165
|
+
return yaml.parse(match[1]) as HomePage;
|
|
166
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Docs Index
|
|
2
|
+
|
|
3
|
+
```ts twoslash
|
|
4
|
+
// @noErrors
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
import alchemy, { Resource, Context } from "alchemy";
|
|
8
|
+
|
|
9
|
+
import { Zone } from "alchemy/cloudflare";
|
|
10
|
+
|
|
11
|
+
type alchemy = any;
|
|
12
|
+
|
|
13
|
+
await using app = alchemy("github:alchemy", {
|
|
14
|
+
// ^?
|
|
15
|
+
stage: "prod",
|
|
16
|
+
phase: process.argv.includes("--destroy") ? "destroy" : "up",
|
|
17
|
+
// pass the password in (you can get it from anywhere, e.g. stdin)
|
|
18
|
+
password: process.env.SECRET_PASSPHRASE,
|
|
19
|
+
quiet: process.argv.includes("--verbose") ? false : true,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export interface Product {}
|
|
23
|
+
|
|
24
|
+
export const Product = Resource(
|
|
25
|
+
"stripe::Product",
|
|
26
|
+
async function (
|
|
27
|
+
this: Context<any>,
|
|
28
|
+
id: string,
|
|
29
|
+
props: any,
|
|
30
|
+
): Promise<Product> {
|
|
31
|
+
this.
|
|
32
|
+
// ^|
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```ts twoslash
|
|
38
|
+
// @noErrors
|
|
39
|
+
// @esModuleInterop
|
|
40
|
+
import express from "express"
|
|
41
|
+
const app = {
|
|
42
|
+
get(path: string, fn: (req: any, res: { send(): void }))
|
|
43
|
+
}
|
|
44
|
+
app.get("/", function (req, res) {
|
|
45
|
+
res.sen
|
|
46
|
+
// ^|
|
|
47
|
+
})
|
|
48
|
+
app.listen(3000)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```ts twoslash
|
|
52
|
+
// @module: esnext
|
|
53
|
+
// @filename: maths.ts
|
|
54
|
+
export function absolute(num: number) {
|
|
55
|
+
if (num < 0) return num * -1;
|
|
56
|
+
return num;
|
|
57
|
+
}
|
|
58
|
+
// @filename: index.ts
|
|
59
|
+
import { absolute } from "./maths";
|
|
60
|
+
const value = absolute(-1);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```ts twoslash
|
|
64
|
+
// @module: esnext
|
|
65
|
+
// @filename: maths.ts
|
|
66
|
+
export function absolute(num: number) {
|
|
67
|
+
if (num < 0) return num * -1;
|
|
68
|
+
return num;
|
|
69
|
+
}
|
|
70
|
+
// @filename: index.ts
|
|
71
|
+
// ---cut---
|
|
72
|
+
import { absolute } from "./maths";
|
|
73
|
+
const value = absolute(-1);
|
|
74
|
+
// ^?
|
|
75
|
+
```
|