lambdaimg 0.1.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 ADDED
@@ -0,0 +1,12 @@
1
+ # lambdaimg
2
+
3
+ Create deployable LambdaImg apps.
4
+
5
+ ```sh
6
+ bunx lambdaimg create my-images
7
+ cd my-images
8
+ bun install
9
+ bun run deploy
10
+ ```
11
+
12
+ The CLI writes an Alchemy app but does not deploy it automatically.
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ export interface CreateAppOptions {
3
+ cwd?: string;
4
+ force?: boolean;
5
+ }
6
+ export interface CreateAppResult {
7
+ targetDir: string;
8
+ files: string[];
9
+ }
10
+ export declare function createApp(targetDir: string, options?: CreateAppOptions): Promise<CreateAppResult>;
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAKA,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAoC1B"}
package/dist/index.js ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ import { mkdir, readdir, stat, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ export async function createApp(targetDir, options = {}) {
6
+ if (!targetDir) {
7
+ throw new Error("A target directory is required.");
8
+ }
9
+ const cwd = options.cwd ?? process.cwd();
10
+ const resolvedTarget = path.resolve(cwd, targetDir);
11
+ const targetStatus = await getPathStatus(resolvedTarget);
12
+ if (targetStatus === "file") {
13
+ throw new Error(`Target exists and is not a directory: ${resolvedTarget}`);
14
+ }
15
+ if (targetStatus === "directory" && !options.force) {
16
+ const entries = await readdir(resolvedTarget);
17
+ if (entries.length > 0) {
18
+ throw new Error(`Target directory is not empty: ${resolvedTarget}. Pass --force to write into it.`);
19
+ }
20
+ }
21
+ await mkdir(resolvedTarget, { recursive: true });
22
+ const packageName = packageNameFromDir(resolvedTarget);
23
+ const files = appFiles(packageName);
24
+ await Promise.all(Object.entries(files).map(([filename, contents]) => writeFile(path.join(resolvedTarget, filename), contents)));
25
+ return {
26
+ targetDir: resolvedTarget,
27
+ files: Object.keys(files),
28
+ };
29
+ }
30
+ async function main(argv = process.argv.slice(2)) {
31
+ const parsed = parseArgs(argv);
32
+ if (parsed.help) {
33
+ process.stdout.write(usage());
34
+ return;
35
+ }
36
+ if (parsed.command !== "create" || !parsed.targetDir) {
37
+ process.stderr.write(usage());
38
+ process.exitCode = 1;
39
+ return;
40
+ }
41
+ const result = await createApp(parsed.targetDir, { force: parsed.force });
42
+ process.stdout.write(`Created LambdaImg app at ${result.targetDir}\n`);
43
+ }
44
+ function parseArgs(argv) {
45
+ const force = argv.includes("--force");
46
+ const help = argv.includes("--help") || argv.includes("-h");
47
+ const positional = argv.filter((arg) => !arg.startsWith("-"));
48
+ return {
49
+ command: positional[0],
50
+ targetDir: positional[1],
51
+ force,
52
+ help,
53
+ };
54
+ }
55
+ async function getPathStatus(targetPath) {
56
+ try {
57
+ const stats = await stat(targetPath);
58
+ return stats.isDirectory() ? "directory" : "file";
59
+ }
60
+ catch (error) {
61
+ if (isNodeError(error) && error.code === "ENOENT") {
62
+ return "missing";
63
+ }
64
+ throw error;
65
+ }
66
+ }
67
+ function packageNameFromDir(targetDir) {
68
+ const basename = path.basename(targetDir);
69
+ const normalized = basename
70
+ .toLowerCase()
71
+ .replace(/[^a-z0-9._-]+/g, "-")
72
+ .replace(/^-+|-+$/g, "");
73
+ return normalized || "lambdaimg-app";
74
+ }
75
+ function appFiles(packageName) {
76
+ return {
77
+ "package.json": `${JSON.stringify({
78
+ name: packageName,
79
+ version: "0.1.0",
80
+ private: true,
81
+ type: "module",
82
+ scripts: {
83
+ check: "tsc --noEmit -p tsconfig.json",
84
+ deploy: "alchemy deploy",
85
+ destroy: "alchemy destroy",
86
+ },
87
+ dependencies: {
88
+ "@lambdaimg/alchemy": "^0.1.0",
89
+ alchemy: "2.0.0-beta.58",
90
+ effect: "^4.0.0-beta.90",
91
+ },
92
+ devDependencies: {
93
+ "@effect/platform-bun": "^4.0.0-beta.90",
94
+ "@types/node": "^24.0.0",
95
+ typescript: "^5.9.3",
96
+ },
97
+ }, null, 2)}\n`,
98
+ "alchemy.run.ts": `import { ImagesStack } from "@lambdaimg/alchemy";
99
+ import * as Alchemy from "alchemy";
100
+ import * as AWS from "alchemy/AWS";
101
+ import { Stack } from "alchemy/Stack";
102
+ import * as Effect from "effect/Effect";
103
+
104
+ const STAGE_CONFIG = {
105
+ dev: {
106
+ domain: "images.dev.example.com",
107
+ hostedZoneId: "Z0000000000000",
108
+ },
109
+ prod: {
110
+ domain: "images.example.com",
111
+ hostedZoneId: "Z0000000000000",
112
+ },
113
+ } as const;
114
+
115
+ type ImagesStage = keyof typeof STAGE_CONFIG;
116
+
117
+ export default Alchemy.Stack(
118
+ "lambdaimg",
119
+ {
120
+ providers: AWS.providers(),
121
+ state: AWS.state(),
122
+ },
123
+ Effect.gen(function* () {
124
+ const { stage } = yield* Stack;
125
+ const config = STAGE_CONFIG[stage as ImagesStage];
126
+ if (!config) {
127
+ return yield* Effect.die(
128
+ \`Unknown stage "\${stage}". Expected one of: \${Object.keys(STAGE_CONFIG).join(", ")}\`,
129
+ );
130
+ }
131
+
132
+ return yield* ImagesStack("Images", config);
133
+ }),
134
+ );
135
+ `,
136
+ "tsconfig.json": `${JSON.stringify({
137
+ compilerOptions: {
138
+ target: "ES2022",
139
+ lib: ["ES2022", "DOM"],
140
+ module: "NodeNext",
141
+ moduleResolution: "NodeNext",
142
+ strict: true,
143
+ skipLibCheck: true,
144
+ types: ["node"],
145
+ },
146
+ include: ["alchemy.run.ts"],
147
+ }, null, 2)}\n`,
148
+ ".env.example": "AWS_REGION=us-east-1\nAWS_PROFILE=default\n",
149
+ ".gitignore": "node_modules\n.alchemy\n.env\n.env.*\n!.env.example\ndist\n",
150
+ "README.md": `# ${packageName}
151
+
152
+ Deployable LambdaImg app generated by \`lambdaimg create\`.
153
+
154
+ ## Setup
155
+
156
+ 1. Install dependencies:
157
+
158
+ \`\`\`sh
159
+ bun install
160
+ \`\`\`
161
+
162
+ 2. Edit \`alchemy.run.ts\` and replace the example domains and hosted zone ids.
163
+
164
+ 3. Configure AWS credentials:
165
+
166
+ \`\`\`sh
167
+ cp .env.example .env
168
+ \`\`\`
169
+
170
+ 4. Deploy:
171
+
172
+ \`\`\`sh
173
+ bun run deploy
174
+ \`\`\`
175
+
176
+ The stack creates an S3 bucket for originals, a Lambda resize function, and a CloudFront distribution for original and resized image URLs.
177
+ `,
178
+ };
179
+ }
180
+ function usage() {
181
+ return `Usage:
182
+ lambdaimg create <dir> [--force]
183
+
184
+ Commands:
185
+ create <dir> Create a deployable LambdaImg Alchemy app.
186
+
187
+ Options:
188
+ --force Write files into a non-empty directory.
189
+ -h, --help Show this help message.
190
+ `;
191
+ }
192
+ function isNodeError(error) {
193
+ return error instanceof Error && "code" in error;
194
+ }
195
+ function isCliEntrypoint() {
196
+ const argvPath = process.argv[1];
197
+ if (!argvPath) {
198
+ return false;
199
+ }
200
+ return path.resolve(argvPath) === fileURLToPath(import.meta.url);
201
+ }
202
+ if (isCliEntrypoint()) {
203
+ main().catch((error) => {
204
+ const message = error instanceof Error ? error.message : String(error);
205
+ process.stderr.write(`${message}\n`);
206
+ process.exitCode = 1;
207
+ });
208
+ }
209
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "lambdaimg",
3
+ "version": "0.1.0",
4
+ "description": "CLI for creating LambdaImg deployable apps.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/jperezrealini/lambdaimg.git",
9
+ "directory": "packages/cli"
10
+ },
11
+ "bin": {
12
+ "lambdaimg": "./dist/index.js"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "type": "module",
19
+ "exports": {
20
+ ".": {
21
+ "default": "./dist/index.js",
22
+ "types": "./dist/index.d.ts"
23
+ }
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "scripts": {
29
+ "build": "tsc -p tsconfig.build.json",
30
+ "check": "oxlint && oxfmt --check && tsc --noEmit -p tsconfig.json",
31
+ "fix": "oxlint --fix && oxfmt",
32
+ "test": "bun test"
33
+ },
34
+ "devDependencies": {
35
+ "@types/bun": "catalog:",
36
+ "@types/node": "catalog:"
37
+ }
38
+ }