alchemy 0.2.2 → 0.2.3
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 -0
- package/lib/alchemy.js +84 -8
- package/lib/apply.d.ts +1 -1
- package/lib/apply.js +13 -10
- package/lib/aws/bucket.d.ts +1 -1
- package/lib/aws/function.d.ts +1 -1
- package/lib/aws/oidc/oidc-provider.d.ts +1 -1
- package/lib/aws/policy-attachment.d.ts +1 -1
- package/lib/aws/policy.d.ts +1 -1
- package/lib/aws/queue.d.ts +1 -1
- package/lib/aws/role.d.ts +1 -1
- package/lib/aws/ses.d.ts +1 -1
- package/lib/aws/table.d.ts +1 -1
- package/lib/cloudflare/bucket.d.ts +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +1 -1
- package/lib/cloudflare/static-site.d.ts +1 -1
- package/lib/cloudflare/worker.d.ts +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +1 -1
- package/lib/cloudflare/zone.d.ts +1 -1
- package/lib/context.d.ts +9 -4
- package/lib/context.js +4 -2
- package/lib/destroy.js +8 -7
- package/lib/docs/document.d.ts +123 -0
- package/lib/docs/document.js +67 -0
- package/lib/docs/index.d.ts +1 -0
- package/lib/docs/index.js +1 -0
- package/lib/esbuild/bundle.d.ts +1 -1
- package/lib/fs/file.d.ts +30 -3
- package/lib/fs/file.js +63 -3
- package/lib/fs/folder.d.ts +3 -3
- package/lib/fs/folder.js +2 -1
- package/lib/github/secret.d.ts +1 -1
- 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/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 +1 -1
- package/lib/stripe/product.d.ts +1 -1
- package/lib/stripe/webhook.d.ts +1 -1
- package/lib/util/serde.d.ts +3 -1
- package/lib/util/serde.js +6 -4
- package/lib/vite/vite.d.ts +1 -1
- package/lib/vitepress/dependencies.d.ts +16 -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 +60 -0
- package/lib/vitepress/vitepress.js +260 -0
- package/package.json +16 -9
- package/src/alchemy.ts +105 -13
- package/src/apply.ts +25 -13
- package/src/context.ts +30 -10
- package/src/destroy.ts +10 -7
- package/src/docs/document.ts +225 -0
- package/src/docs/index.ts +1 -0
- package/src/fs/file.ts +114 -6
- package/src/fs/folder.ts +2 -1
- package/src/resource.ts +7 -2
- package/src/scope.ts +9 -9
- package/src/state.ts +9 -2
- package/src/util/serde.ts +13 -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 +361 -0
- package/lib/project/vite.d.ts +0 -90
- package/lib/project/vite.js +0 -228
- package/src/project/vite.ts +0 -406
package/lib/alchemy.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export interface Alchemy {
|
|
|
10
10
|
destroy: typeof destroy;
|
|
11
11
|
secret: typeof secret;
|
|
12
12
|
(...parameters: Parameters<typeof scope>): ReturnType<typeof scope>;
|
|
13
|
+
(template: TemplateStringsArray, ...values: any[]): Promise<string>;
|
|
13
14
|
}
|
|
14
15
|
export interface AlchemyOptions {
|
|
15
16
|
/**
|
package/lib/alchemy.js
CHANGED
|
@@ -1,20 +1,91 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
1
3
|
import { DestroyedSignal, destroy } from "./destroy";
|
|
2
4
|
import { Scope } from "./scope";
|
|
3
5
|
import { secret } from "./secret";
|
|
4
6
|
// TODO: support browser
|
|
5
7
|
const DEFAULT_STAGE = process.env.ALCHEMY_STAGE ?? process.env.USER ?? "dev";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
appName,
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
export const alchemy = _alchemy;
|
|
9
|
+
function _alchemy(...args) {
|
|
10
|
+
if (typeof args[0] === "string") {
|
|
11
|
+
const [appName, options] = args;
|
|
12
|
+
return scope(undefined, {
|
|
13
|
+
...options,
|
|
14
|
+
appName,
|
|
15
|
+
stage: options?.stage,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const [template, ...values] = args;
|
|
20
|
+
const [, secondLine] = template[0].split("\n");
|
|
21
|
+
const leadingSpaces = secondLine
|
|
22
|
+
? secondLine.match(/^(\s*)/)?.[1]?.length || 0
|
|
23
|
+
: 0;
|
|
24
|
+
const indent = " ".repeat(leadingSpaces);
|
|
25
|
+
return (async () => {
|
|
26
|
+
const { isFileCollection, isFileRef } = await import("./fs/file");
|
|
27
|
+
const appendices = {};
|
|
28
|
+
const stringValues = await Promise.all(values.map(async function resolve(value) {
|
|
29
|
+
if (typeof value === "string") {
|
|
30
|
+
return indent + value;
|
|
31
|
+
}
|
|
32
|
+
else if (value instanceof Promise) {
|
|
33
|
+
return resolve(await value);
|
|
34
|
+
}
|
|
35
|
+
else if (isFileRef(value)) {
|
|
36
|
+
if (!(value.path in appendices)) {
|
|
37
|
+
appendices[value.path] = await fs.readFile(value.path, "utf-8");
|
|
38
|
+
}
|
|
39
|
+
return `[${path.basename(value.path)}](${value.path})`;
|
|
40
|
+
}
|
|
41
|
+
else if (isFileCollection(value)) {
|
|
42
|
+
return Object.entries(value.files)
|
|
43
|
+
.map(([filePath, content]) => {
|
|
44
|
+
appendices[filePath] = content;
|
|
45
|
+
return `[${path.basename(filePath)}](${filePath})`;
|
|
46
|
+
})
|
|
47
|
+
.join("\n\n");
|
|
48
|
+
}
|
|
49
|
+
else if (Array.isArray(value)) {
|
|
50
|
+
return (await Promise.all(value.map(async (value, i) => `${i}. ${await resolve(value)}`))).join("\n");
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// TODO: support other types
|
|
54
|
+
throw new Error(`Unsupported value type: ${JSON.stringify(value)}`);
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
// Construct the string template by joining template parts with interpolated values
|
|
58
|
+
const lines = template
|
|
59
|
+
.map((part) => part
|
|
60
|
+
.split("\n")
|
|
61
|
+
.map((line) => line.startsWith(indent) ? line.slice(indent.length) : line)
|
|
62
|
+
.join("\n"))
|
|
63
|
+
.flatMap((part, i) => i < stringValues.length ? [part, stringValues[i] ?? ""] : [part])
|
|
64
|
+
.join("")
|
|
65
|
+
.split("\n");
|
|
66
|
+
// Collect and sort appendices by file path
|
|
67
|
+
return [
|
|
68
|
+
// format the user prompt and trim the first line if it's empty
|
|
69
|
+
lines.length > 1 && lines[0].replaceAll(" ", "").length === 0
|
|
70
|
+
? lines.slice(1).join("\n")
|
|
71
|
+
: lines.join("\n"),
|
|
72
|
+
// sort appendices by path and include at the end of the prompt
|
|
73
|
+
Object.entries(appendices)
|
|
74
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
75
|
+
.map(([filePath, content]) => {
|
|
76
|
+
const extension = path.extname(filePath).slice(1);
|
|
77
|
+
const codeTag = extension ? extension : "";
|
|
78
|
+
return `// ${filePath}\n\`\`\`${codeTag}\n${content}\n\`\`\``;
|
|
79
|
+
})
|
|
80
|
+
.join("\n\n"),
|
|
81
|
+
].join("\n");
|
|
82
|
+
})();
|
|
83
|
+
}
|
|
12
84
|
}
|
|
13
85
|
_alchemy.destroy = destroy;
|
|
14
86
|
_alchemy.run = run;
|
|
15
87
|
_alchemy.scope = scope;
|
|
16
88
|
_alchemy.secret = secret;
|
|
17
|
-
export const alchemy = _alchemy;
|
|
18
89
|
/**
|
|
19
90
|
* Enter a new scope synchronously.
|
|
20
91
|
* @param options
|
|
@@ -35,7 +106,7 @@ async function run(...args) {
|
|
|
35
106
|
const [id, options, fn] = typeof args[1] === "function"
|
|
36
107
|
? [args[0], undefined, args[1]]
|
|
37
108
|
: args;
|
|
38
|
-
|
|
109
|
+
const scope = alchemy.scope(id, options);
|
|
39
110
|
try {
|
|
40
111
|
return await fn.bind(scope)(scope);
|
|
41
112
|
}
|
|
@@ -43,6 +114,11 @@ async function run(...args) {
|
|
|
43
114
|
if (!(error instanceof DestroyedSignal)) {
|
|
44
115
|
scope.fail();
|
|
45
116
|
}
|
|
117
|
+
else {
|
|
118
|
+
}
|
|
46
119
|
throw error;
|
|
47
120
|
}
|
|
121
|
+
finally {
|
|
122
|
+
await scope.finalize();
|
|
123
|
+
}
|
|
48
124
|
}
|
package/lib/apply.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export interface ApplyOptions {
|
|
|
3
3
|
quiet?: boolean;
|
|
4
4
|
alwaysUpdate?: boolean;
|
|
5
5
|
}
|
|
6
|
-
export declare function apply<Out extends Resource>(resource: PendingResource<Out>, props: ResourceProps, options?: ApplyOptions): Promise<Awaited<Out>>;
|
|
6
|
+
export declare function apply<Out extends Resource>(resource: PendingResource<Out>, props: ResourceProps | undefined, options?: ApplyOptions): Promise<Awaited<Out>>;
|
package/lib/apply.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { alchemy } from "./alchemy";
|
|
2
2
|
import { context } from "./context";
|
|
3
3
|
import { PROVIDERS, } from "./resource";
|
|
4
|
+
import { Secret } from "./secret";
|
|
4
5
|
import { serialize } from "./util/serde";
|
|
5
6
|
export async function apply(resource, props, options) {
|
|
6
7
|
const scope = resource.Scope;
|
|
7
8
|
try {
|
|
8
|
-
const quiet = props
|
|
9
|
+
const quiet = props?.quiet ?? scope.quiet;
|
|
9
10
|
await scope.init();
|
|
10
11
|
let state = (await scope.state.get(resource.ID));
|
|
11
12
|
const provider = PROVIDERS.get(resource.Kind);
|
|
@@ -35,15 +36,18 @@ export async function apply(resource, props, options) {
|
|
|
35
36
|
const alwaysUpdate = options?.alwaysUpdate ?? provider.options?.alwaysUpdate ?? false;
|
|
36
37
|
// Skip update if inputs haven't changed and resource is in a stable state
|
|
37
38
|
if (state.status === "created" || state.status === "updated") {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const oldProps = JSON.stringify(state.props, (_, value) => value instanceof Secret
|
|
40
|
+
? {
|
|
41
|
+
"@secret": value.unencrypted,
|
|
42
|
+
}
|
|
43
|
+
: value, 2);
|
|
44
|
+
const newProps = JSON.stringify(await serialize(scope, props, {
|
|
45
|
+
encrypt: false,
|
|
46
|
+
}), null, 2);
|
|
47
|
+
if (oldProps === newProps && alwaysUpdate !== true) {
|
|
41
48
|
if (!quiet) {
|
|
42
49
|
console.log(`Skip: "${resource.FQN}" (no changes)`);
|
|
43
50
|
}
|
|
44
|
-
// if (resourceState.output !== undefined) {
|
|
45
|
-
// resource[Provide](resourceState.output);
|
|
46
|
-
// }
|
|
47
51
|
return state.output;
|
|
48
52
|
}
|
|
49
53
|
}
|
|
@@ -63,6 +67,7 @@ export async function apply(resource, props, options) {
|
|
|
63
67
|
id: resource.ID,
|
|
64
68
|
fqn: resource.FQN,
|
|
65
69
|
seq: resource.Seq,
|
|
70
|
+
props: state.oldProps,
|
|
66
71
|
state,
|
|
67
72
|
replace: () => {
|
|
68
73
|
if (isReplaced) {
|
|
@@ -72,9 +77,7 @@ export async function apply(resource, props, options) {
|
|
|
72
77
|
isReplaced = true;
|
|
73
78
|
},
|
|
74
79
|
});
|
|
75
|
-
const output = await alchemy.run(resource.ID, async () =>
|
|
76
|
-
return provider.handler.bind(ctx)(resource.ID, props);
|
|
77
|
-
});
|
|
80
|
+
const output = await alchemy.run(resource.ID, async () => provider.handler.bind(ctx)(resource.ID, props));
|
|
78
81
|
if (!quiet) {
|
|
79
82
|
console.log(`${phase === "create" ? "Created" : "Updated"}: "${resource.FQN}"`);
|
|
80
83
|
}
|
package/lib/aws/bucket.d.ts
CHANGED
|
@@ -15,4 +15,4 @@ export interface Bucket extends Resource<"s3::Bucket">, BucketProps {
|
|
|
15
15
|
versioningEnabled?: boolean;
|
|
16
16
|
acl?: string;
|
|
17
17
|
}
|
|
18
|
-
export declare const Bucket: ((this: Context<Bucket>, id: string, props: BucketProps) => Promise<Bucket>) | (((this: any, id: string, props
|
|
18
|
+
export declare const Bucket: ((this: Context<Bucket>, id: string, props: BucketProps) => Promise<Bucket>) | (((this: any, id: string, props?: BucketProps | undefined) => never) & (new (_: never) => never));
|
package/lib/aws/function.d.ts
CHANGED
|
@@ -47,4 +47,4 @@ export interface Function extends Resource<"lambda::Function">, FunctionProps {
|
|
|
47
47
|
signingProfileVersionArn?: string;
|
|
48
48
|
signingJobArn?: string;
|
|
49
49
|
}
|
|
50
|
-
export declare const Function: ((this: Context<Function>, id: string, props: FunctionProps) => Promise<Function>) | (((this: any, id: string, props
|
|
50
|
+
export declare const Function: ((this: Context<Function>, id: string, props: FunctionProps) => Promise<Function>) | (((this: any, id: string, props?: FunctionProps | undefined) => never) & (new (_: never) => never));
|
|
@@ -55,4 +55,4 @@ export interface OIDCProvider extends Resource<"aws::OIDCProvider">, OIDCProvide
|
|
|
55
55
|
/**
|
|
56
56
|
* Resource for configuring AWS OIDC provider for GitHub Actions
|
|
57
57
|
*/
|
|
58
|
-
export declare const OIDCProvider: ((this: Context<OIDCProvider>, id: string, props: OIDCProviderProps) => Promise<OIDCProvider>) | (((this: any, id: string, props
|
|
58
|
+
export declare const OIDCProvider: ((this: Context<OIDCProvider>, id: string, props: OIDCProviderProps) => Promise<OIDCProvider>) | (((this: any, id: string, props?: OIDCProviderProps | undefined) => never) & (new (_: never) => never));
|
|
@@ -6,4 +6,4 @@ export interface PolicyAttachmentProps {
|
|
|
6
6
|
}
|
|
7
7
|
export interface PolicyAttachment extends Resource<"iam::PolicyAttachment">, PolicyAttachmentProps {
|
|
8
8
|
}
|
|
9
|
-
export declare const PolicyAttachment: ((this: Context<PolicyAttachment>, id: string, props: PolicyAttachmentProps) => Promise<PolicyAttachment>) | (((this: any, id: string, props
|
|
9
|
+
export declare const PolicyAttachment: ((this: Context<PolicyAttachment>, id: string, props: PolicyAttachmentProps) => Promise<PolicyAttachment>) | (((this: any, id: string, props?: PolicyAttachmentProps | undefined) => never) & (new (_: never) => never));
|
package/lib/aws/policy.d.ts
CHANGED
|
@@ -31,4 +31,4 @@ export interface Policy extends Resource<"iam::Policy">, PolicyProps {
|
|
|
31
31
|
updateDate: Date;
|
|
32
32
|
isAttachable: boolean;
|
|
33
33
|
}
|
|
34
|
-
export declare const Policy: ((this: Context<Policy>, id: string, props: PolicyProps) => Promise<Policy>) | (((this: any, id: string, props
|
|
34
|
+
export declare const Policy: ((this: Context<Policy>, id: string, props: PolicyProps) => Promise<Policy>) | (((this: any, id: string, props?: PolicyProps | undefined) => never) & (new (_: never) => never));
|
package/lib/aws/queue.d.ts
CHANGED
|
@@ -29,4 +29,4 @@ export interface Queue extends Resource<"sqs::Queue">, QueueProps {
|
|
|
29
29
|
arn: string;
|
|
30
30
|
url: string;
|
|
31
31
|
}
|
|
32
|
-
export declare const Queue: ((this: Context<Queue>, id: string, props: QueueProps) => Promise<Queue>) | (((this: any, id: string, props
|
|
32
|
+
export declare const Queue: ((this: Context<Queue>, id: string, props: QueueProps) => Promise<Queue>) | (((this: any, id: string, props?: QueueProps | undefined) => never) & (new (_: never) => never));
|
package/lib/aws/role.d.ts
CHANGED
|
@@ -21,4 +21,4 @@ export interface Role extends Resource<"iam::Role">, RoleProps {
|
|
|
21
21
|
roleId: string;
|
|
22
22
|
createDate: Date;
|
|
23
23
|
}
|
|
24
|
-
export declare const Role: ((this: Context<Role>, id: string, props: RoleProps) => Promise<Role>) | (((this: any, id: string, props
|
|
24
|
+
export declare const Role: ((this: Context<Role>, id: string, props: RoleProps) => Promise<Role>) | (((this: any, id: string, props?: RoleProps | undefined) => never) & (new (_: never) => never));
|
package/lib/aws/ses.d.ts
CHANGED
|
@@ -66,4 +66,4 @@ export interface SES extends Resource<"aws::SES">, SESProps {
|
|
|
66
66
|
/**
|
|
67
67
|
* AWS SES resource for creating and configuring Simple Email Service
|
|
68
68
|
*/
|
|
69
|
-
export declare const SES: ((this: Context<SES>, id: string, props: SESProps) => Promise<SES>) | (((this: any, id: string, props
|
|
69
|
+
export declare const SES: ((this: Context<SES>, id: string, props: SESProps) => Promise<SES>) | (((this: any, id: string, props?: SESProps | undefined) => never) & (new (_: never) => never));
|
package/lib/aws/table.d.ts
CHANGED
|
@@ -20,4 +20,4 @@ export interface Table extends Resource<"dynamo::Table">, TableProps {
|
|
|
20
20
|
streamArn?: string;
|
|
21
21
|
tableId: string;
|
|
22
22
|
}
|
|
23
|
-
export declare const Table: ((this: Context<Table>, id: string, props: TableProps) => Promise<Table>) | (((this: any, id: string, props
|
|
23
|
+
export declare const Table: ((this: Context<Table>, id: string, props: TableProps) => Promise<Table>) | (((this: any, id: string, props?: TableProps | undefined) => never) & (new (_: never) => never));
|
|
@@ -43,4 +43,4 @@ export interface R2Bucket extends Resource<"cloudflare::R2Bucket">, BucketProps
|
|
|
43
43
|
*/
|
|
44
44
|
creationDate: number;
|
|
45
45
|
}
|
|
46
|
-
export declare const R2Bucket: ((this: Context<R2Bucket>, id: string, props: BucketProps) => Promise<R2Bucket>) | (((this: any, id: string, props
|
|
46
|
+
export declare const R2Bucket: ((this: Context<R2Bucket>, id: string, props: BucketProps) => Promise<R2Bucket>) | (((this: any, id: string, props?: BucketProps | undefined) => never) & (new (_: never) => never));
|
|
@@ -58,4 +58,4 @@ export interface KVNamespace extends Resource<"cloudflare::KVNamespace">, KVName
|
|
|
58
58
|
*/
|
|
59
59
|
modifiedAt: number;
|
|
60
60
|
}
|
|
61
|
-
export declare const KVNamespace: ((this: Context<KVNamespace>, id: string, props: KVNamespaceProps) => Promise<KVNamespace>) | (((this: any, id: string, props
|
|
61
|
+
export declare const KVNamespace: ((this: Context<KVNamespace>, id: string, props: KVNamespaceProps) => Promise<KVNamespace>) | (((this: any, id: string, props?: KVNamespaceProps | undefined) => never) & (new (_: never) => never));
|
|
@@ -157,4 +157,4 @@ export interface StaticSite extends Resource<"cloudflare::StaticSite"> {
|
|
|
157
157
|
*/
|
|
158
158
|
routes?: Record<string, string>;
|
|
159
159
|
}
|
|
160
|
-
export declare const StaticSite: ((this: Context<StaticSite>, id: string, props: StaticSiteProps) => Promise<StaticSite>) | (((this: any, id: string, props
|
|
160
|
+
export declare const StaticSite: ((this: Context<StaticSite>, id: string, props: StaticSiteProps) => Promise<StaticSite>) | (((this: any, id: string, props?: StaticSiteProps | undefined) => never) & (new (_: never) => never));
|
|
@@ -105,4 +105,4 @@ export interface Worker<B extends Bindings = Bindings> extends Resource<"cloudfl
|
|
|
105
105
|
[bindingName in keyof B]: Bound<B[bindingName]>;
|
|
106
106
|
};
|
|
107
107
|
}
|
|
108
|
-
export declare const Worker: (<const B extends Bindings>(this: Context<Worker<NoInfer<B>>>, id: string, props: WorkerProps<B>) => Promise<Worker<B>>) | (((this: any, id: string, props
|
|
108
|
+
export declare const Worker: (<const B extends Bindings>(this: Context<Worker<NoInfer<B>>>, id: string, props: WorkerProps<B>) => Promise<Worker<B>>) | (((this: any, id: string, props?: WorkerProps<Bindings> | undefined) => never) & (new (_: never) => never));
|
|
@@ -240,4 +240,4 @@ export interface WranglerJson extends Resource<"cloudflare::WranglerJson">, Wran
|
|
|
240
240
|
/**
|
|
241
241
|
* Resource for managing wrangler.json configuration files
|
|
242
242
|
*/
|
|
243
|
-
export declare const WranglerJson: ((this: Context<WranglerJson>, id: string, props: WranglerJsonProps) => Promise<WranglerJson>) | (((this: any, id: string, props
|
|
243
|
+
export declare const WranglerJson: ((this: Context<WranglerJson>, id: string, props: WranglerJsonProps) => Promise<WranglerJson>) | (((this: any, id: string, props?: WranglerJsonProps | undefined) => never) & (new (_: never) => never));
|
package/lib/cloudflare/zone.d.ts
CHANGED
|
@@ -193,4 +193,4 @@ export interface Zone extends Resource<"cloudflare::Zone"> {
|
|
|
193
193
|
minTlsVersion: MinTLSVersionValue;
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
|
-
export declare const Zone: ((this: Context<Zone>, id: string, props: ZoneProps) => Promise<Zone>) | (((this: any, id: string, props
|
|
196
|
+
export declare const Zone: ((this: Context<Zone>, id: string, props: ZoneProps) => Promise<Zone>) | (((this: any, id: string, props?: ZoneProps | undefined) => never) & (new (_: never) => never));
|
package/lib/context.d.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { Resource, ResourceFQN, ResourceID, ResourceKind, ResourceProps } from "./resource";
|
|
2
2
|
import type { Scope } from "./scope";
|
|
3
3
|
import type { State } from "./state";
|
|
4
|
-
export type Context<Out extends Resource> = CreateContext<Out> | UpdateContext<Out> | DeleteContext<Out>;
|
|
4
|
+
export type Context<Out extends Resource, Props extends ResourceProps = ResourceProps> = CreateContext<Out> | UpdateContext<Out, Props> | DeleteContext<Out, Props>;
|
|
5
5
|
export interface CreateContext<Out extends Resource> extends BaseContext<Out> {
|
|
6
6
|
phase: "create";
|
|
7
7
|
output?: undefined;
|
|
8
|
+
props?: undefined;
|
|
8
9
|
}
|
|
9
|
-
export interface UpdateContext<Out extends Resource> extends BaseContext<Out> {
|
|
10
|
+
export interface UpdateContext<Out extends Resource, Props extends ResourceProps = ResourceProps> extends BaseContext<Out> {
|
|
10
11
|
phase: "update";
|
|
11
12
|
output: Out;
|
|
13
|
+
props: Props;
|
|
12
14
|
}
|
|
13
|
-
export interface DeleteContext<Out extends Resource> extends BaseContext<Out> {
|
|
15
|
+
export interface DeleteContext<Out extends Resource, Props extends ResourceProps = ResourceProps> extends BaseContext<Out> {
|
|
14
16
|
phase: "delete";
|
|
15
17
|
output: Out;
|
|
18
|
+
props: Props;
|
|
16
19
|
}
|
|
17
20
|
export interface BaseContext<Out extends Resource> {
|
|
18
21
|
quiet: boolean;
|
|
@@ -44,15 +47,17 @@ export interface BaseContext<Out extends Resource> {
|
|
|
44
47
|
/**
|
|
45
48
|
* Create the Resource envelope (with Alchemy + User properties)
|
|
46
49
|
*/
|
|
50
|
+
(id: string, props: Omit<Out, keyof Resource>): Out;
|
|
47
51
|
(props: Omit<Out, keyof Resource>): Out;
|
|
48
52
|
}
|
|
49
|
-
export declare function context<Kind extends string, Props extends ResourceProps, Out extends Resource>({ scope, phase, kind, id, fqn, seq, state, replace, }: {
|
|
53
|
+
export declare function context<Kind extends string, Props extends ResourceProps | undefined, Out extends Resource>({ scope, phase, kind, id, fqn, seq, state, replace, }: {
|
|
50
54
|
scope: Scope;
|
|
51
55
|
phase: "create" | "update" | "delete";
|
|
52
56
|
kind: ResourceKind;
|
|
53
57
|
id: ResourceID;
|
|
54
58
|
fqn: ResourceFQN;
|
|
55
59
|
seq: number;
|
|
60
|
+
props: Props;
|
|
56
61
|
state: State<Kind, Props, Out>;
|
|
57
62
|
replace: () => void;
|
|
58
63
|
}): Context<Out>;
|
package/lib/context.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { DestroyedSignal } from "./destroy";
|
|
2
2
|
export function context({ scope, phase, kind, id, fqn, seq, state, replace, }) {
|
|
3
|
-
function create(
|
|
3
|
+
function create(...args) {
|
|
4
|
+
const [ID, props] = typeof args[0] === "string" ? args : [id, args[0]];
|
|
4
5
|
return {
|
|
5
6
|
...props,
|
|
6
7
|
Kind: kind,
|
|
7
|
-
ID
|
|
8
|
+
ID,
|
|
8
9
|
FQN: fqn,
|
|
9
10
|
Scope: scope,
|
|
10
11
|
Seq: seq,
|
|
@@ -17,6 +18,7 @@ export function context({ scope, phase, kind, id, fqn, seq, state, replace, }) {
|
|
|
17
18
|
fqn: fqn,
|
|
18
19
|
phase,
|
|
19
20
|
output: state.output,
|
|
21
|
+
props: state.props,
|
|
20
22
|
replace,
|
|
21
23
|
get: (key) => state.data[key],
|
|
22
24
|
set: async (key, value) => {
|
package/lib/destroy.js
CHANGED
|
@@ -12,12 +12,13 @@ function isScopeArgs(a) {
|
|
|
12
12
|
*/
|
|
13
13
|
export async function destroy(...args) {
|
|
14
14
|
if (isScopeArgs(args)) {
|
|
15
|
-
const [scope
|
|
16
|
-
const
|
|
15
|
+
const [scope] = args;
|
|
16
|
+
const options = {
|
|
17
|
+
strategy: "sequential",
|
|
18
|
+
...(args[1] ?? {}),
|
|
19
|
+
};
|
|
17
20
|
// destroy all active resources
|
|
18
|
-
await destroy.all(Array.from(scope.resources.values()),
|
|
19
|
-
strategy,
|
|
20
|
-
});
|
|
21
|
+
await destroy.all(Array.from(scope.resources.values()), options);
|
|
21
22
|
// then detect orphans and destroy them
|
|
22
23
|
const orphans = await scope.state.all();
|
|
23
24
|
await destroy.all(Object.values(orphans).map((orphan) => ({
|
|
@@ -45,9 +46,8 @@ export async function destroy(...args) {
|
|
|
45
46
|
if (!quiet) {
|
|
46
47
|
console.log(`Delete: "${instance.FQN}"`);
|
|
47
48
|
}
|
|
48
|
-
const state =
|
|
49
|
+
const state = await scope.state.get(instance.ID);
|
|
49
50
|
if (state === undefined) {
|
|
50
|
-
console.warn(`Resource "${instance.FQN}" not found`);
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
const ctx = context({
|
|
@@ -57,6 +57,7 @@ export async function destroy(...args) {
|
|
|
57
57
|
id: instance.ID,
|
|
58
58
|
fqn: instance.FQN,
|
|
59
59
|
seq: instance.Seq,
|
|
60
|
+
props: state.props,
|
|
60
61
|
state,
|
|
61
62
|
replace: () => {
|
|
62
63
|
throw new Error("Cannot replace a resource that is being deleted");
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Context } from "../context";
|
|
2
|
+
import { Resource } from "../resource";
|
|
3
|
+
import type { Secret } from "../secret";
|
|
4
|
+
/**
|
|
5
|
+
* Base properties for a Document
|
|
6
|
+
*/
|
|
7
|
+
interface BaseDocumentProps {
|
|
8
|
+
/**
|
|
9
|
+
* Path to the markdown document
|
|
10
|
+
*/
|
|
11
|
+
path: string;
|
|
12
|
+
/**
|
|
13
|
+
* Base URL for the OpenAI API
|
|
14
|
+
* @default 'https://api.openai.com/v1'
|
|
15
|
+
*/
|
|
16
|
+
baseURL?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Properties for creating a document with static content
|
|
20
|
+
*/
|
|
21
|
+
interface StaticDocumentProps extends BaseDocumentProps {
|
|
22
|
+
/**
|
|
23
|
+
* Static content of the document
|
|
24
|
+
*/
|
|
25
|
+
content: string;
|
|
26
|
+
/**
|
|
27
|
+
* Prompt for generating content
|
|
28
|
+
* @internal This is marked as never to ensure content and prompt are mutually exclusive
|
|
29
|
+
*/
|
|
30
|
+
prompt?: never;
|
|
31
|
+
/**
|
|
32
|
+
* OpenAI API key
|
|
33
|
+
* @internal This is marked as never since it's not needed for static content
|
|
34
|
+
*/
|
|
35
|
+
apiKey?: never;
|
|
36
|
+
/**
|
|
37
|
+
* Model configuration
|
|
38
|
+
* @internal This is marked as never since it's not needed for static content
|
|
39
|
+
*/
|
|
40
|
+
model?: never;
|
|
41
|
+
/**
|
|
42
|
+
* Context files
|
|
43
|
+
* @internal This is marked as never since it's not needed for static content
|
|
44
|
+
*/
|
|
45
|
+
context?: never;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Properties for creating a document with AI-generated content
|
|
49
|
+
*/
|
|
50
|
+
interface GeneratedDocumentProps extends BaseDocumentProps {
|
|
51
|
+
/**
|
|
52
|
+
* Static content of the document
|
|
53
|
+
* @internal This is marked as never to ensure content and prompt are mutually exclusive
|
|
54
|
+
*/
|
|
55
|
+
content?: never;
|
|
56
|
+
/**
|
|
57
|
+
* Prompt for generating content
|
|
58
|
+
* Can be either a single string or an array of strings that will be joined with newlines
|
|
59
|
+
*/
|
|
60
|
+
prompt: string | string[];
|
|
61
|
+
/**
|
|
62
|
+
* Optional context files to include in the prompt
|
|
63
|
+
* Key is the relative path to the file
|
|
64
|
+
* Value can be either the content string or another Document resource
|
|
65
|
+
*/
|
|
66
|
+
context?: {
|
|
67
|
+
[relativePath: string]: string | Document | {
|
|
68
|
+
path: string;
|
|
69
|
+
content: string;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* OpenAI API key to use for generating content
|
|
74
|
+
* If not provided, will use OPENAI_API_KEY environment variable
|
|
75
|
+
*/
|
|
76
|
+
apiKey?: Secret;
|
|
77
|
+
/**
|
|
78
|
+
* Model configuration
|
|
79
|
+
*/
|
|
80
|
+
model?: {
|
|
81
|
+
/**
|
|
82
|
+
* Model ID to use
|
|
83
|
+
* @default 'gpt-4o'
|
|
84
|
+
*/
|
|
85
|
+
id?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Model provider name
|
|
88
|
+
* @default 'openai'
|
|
89
|
+
*/
|
|
90
|
+
provider?: string;
|
|
91
|
+
/**
|
|
92
|
+
* Model-specific options
|
|
93
|
+
*/
|
|
94
|
+
options?: Record<string, unknown>;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Properties for creating or updating a Document
|
|
99
|
+
* Must provide either static content or a prompt for AI generation
|
|
100
|
+
*/
|
|
101
|
+
export type DocumentProps = StaticDocumentProps | GeneratedDocumentProps;
|
|
102
|
+
/**
|
|
103
|
+
* A markdown document that can be created, updated, and deleted
|
|
104
|
+
*/
|
|
105
|
+
export interface Document extends Omit<DocumentProps, "content" | "prompt" | "context">, Resource<"docs::Document"> {
|
|
106
|
+
/**
|
|
107
|
+
* Content of the document
|
|
108
|
+
*/
|
|
109
|
+
content: string;
|
|
110
|
+
/**
|
|
111
|
+
* Time at which the document was created
|
|
112
|
+
*/
|
|
113
|
+
createdAt: number;
|
|
114
|
+
/**
|
|
115
|
+
* Time at which the document was last updated
|
|
116
|
+
*/
|
|
117
|
+
updatedAt: number;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Resource for managing markdown documents using the Vercel AI SDK
|
|
121
|
+
*/
|
|
122
|
+
export declare const Document: ((this: Context<Document, DocumentProps>, id: string, props: DocumentProps) => Promise<Document>) | (((this: any, id: string, props?: DocumentProps | undefined) => never) & (new (_: never) => never));
|
|
123
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
2
|
+
import { generateText } from "ai";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { Resource } from "../resource";
|
|
6
|
+
/**
|
|
7
|
+
* Resource for managing markdown documents using the Vercel AI SDK
|
|
8
|
+
*/
|
|
9
|
+
export const Document = Resource("docs::Document", async function (id, props) {
|
|
10
|
+
// Ensure directory exists
|
|
11
|
+
await fs.mkdir(path.dirname(props.path), { recursive: true });
|
|
12
|
+
if (this.phase === "delete") {
|
|
13
|
+
try {
|
|
14
|
+
await fs.unlink(props.path);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
// Ignore if file doesn't exist
|
|
18
|
+
if (error.code !== "ENOENT") {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return this.destroy();
|
|
23
|
+
}
|
|
24
|
+
let content;
|
|
25
|
+
console.log("Generating content for", props.path);
|
|
26
|
+
// Generate content if prompt is provided
|
|
27
|
+
if ("prompt" in props) {
|
|
28
|
+
// Get API key from props or environment
|
|
29
|
+
const apiKey = props.apiKey?.unencrypted || process.env.OPENAI_API_KEY;
|
|
30
|
+
if (!apiKey) {
|
|
31
|
+
throw new Error("OpenAI API key is required for content generation");
|
|
32
|
+
}
|
|
33
|
+
// Initialize OpenAI compatible provider
|
|
34
|
+
const provider = createOpenAICompatible({
|
|
35
|
+
name: props.model?.provider || "openai",
|
|
36
|
+
apiKey,
|
|
37
|
+
baseURL: props.baseURL || "https://api.openai.com/v1",
|
|
38
|
+
});
|
|
39
|
+
// Build the full prompt with context if provided
|
|
40
|
+
const fullPrompt = Array.isArray(props.prompt)
|
|
41
|
+
? props.prompt.join("\n")
|
|
42
|
+
: props.prompt;
|
|
43
|
+
// console.log(fullPrompt);
|
|
44
|
+
const { text } = await generateText({
|
|
45
|
+
model: provider(props.model?.id || "gpt-4o"),
|
|
46
|
+
prompt: fullPrompt,
|
|
47
|
+
...(props.model?.options || {}),
|
|
48
|
+
});
|
|
49
|
+
content = text;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Use static content
|
|
53
|
+
content = props.content;
|
|
54
|
+
}
|
|
55
|
+
// Write content to file
|
|
56
|
+
await fs.writeFile(props.path, content);
|
|
57
|
+
// Get file stats for timestamps
|
|
58
|
+
const stats = await fs.stat(props.path);
|
|
59
|
+
// Return the resource
|
|
60
|
+
return this({
|
|
61
|
+
path: props.path,
|
|
62
|
+
baseURL: props.baseURL,
|
|
63
|
+
content,
|
|
64
|
+
createdAt: stats.birthtimeMs,
|
|
65
|
+
updatedAt: stats.mtimeMs,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./document";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./document";
|
package/lib/esbuild/bundle.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export interface Bundle extends Resource<"esbuild::Bundle"> {
|
|
|
53
53
|
*/
|
|
54
54
|
hash: string;
|
|
55
55
|
}
|
|
56
|
-
export declare const Bundle: ((this: Context<Bundle>, id: string, props: BundleProps) => Promise<Bundle>) | (((this: any, id: string, props
|
|
56
|
+
export declare const Bundle: ((this: Context<Bundle>, id: string, props: BundleProps) => Promise<Bundle>) | (((this: any, id: string, props?: BundleProps | undefined) => never) & (new (_: never) => never));
|
|
57
57
|
export declare function bundle(props: BundleProps): Promise<esbuild.BuildResult<{
|
|
58
58
|
bundle: boolean;
|
|
59
59
|
splitting?: boolean;
|