@actuallyjamez/elysian 0.5.1 → 0.6.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/dist/cli/commands/build.js +21 -62
- package/dist/cli/commands/dev.d.ts +6 -1
- package/dist/cli/commands/dev.js +282 -38
- package/dist/cli/commands/generate-iac.js +41 -23
- package/dist/cli/commands/init/scaffold.js +6 -6
- package/dist/cli/commands/init.js +19 -19
- package/dist/cli/ui.d.ts +114 -0
- package/dist/cli/ui.js +233 -0
- package/dist/core/localstack.d.ts +59 -0
- package/dist/core/localstack.js +168 -0
- package/package.json +1 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalStack detection and tflocal execution utilities
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
/**
|
|
7
|
+
* Check if LocalStack is running by hitting the health endpoint
|
|
8
|
+
*/
|
|
9
|
+
export async function isLocalStackRunning(endpoint = "http://localhost:4566") {
|
|
10
|
+
try {
|
|
11
|
+
const controller = new AbortController();
|
|
12
|
+
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
13
|
+
const response = await fetch(`${endpoint}/_localstack/health`, {
|
|
14
|
+
signal: controller.signal,
|
|
15
|
+
});
|
|
16
|
+
clearTimeout(timeout);
|
|
17
|
+
return response.ok;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Check if tflocal CLI is installed
|
|
25
|
+
*/
|
|
26
|
+
export async function isTfLocalInstalled() {
|
|
27
|
+
try {
|
|
28
|
+
const proc = Bun.spawn(["tflocal", "--version"], {
|
|
29
|
+
stdout: "pipe",
|
|
30
|
+
stderr: "pipe",
|
|
31
|
+
});
|
|
32
|
+
const exitCode = await proc.exited;
|
|
33
|
+
return exitCode === 0;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if terraform has been initialized in the given directory
|
|
41
|
+
*/
|
|
42
|
+
export function isTerraformInitialized(terraformDir) {
|
|
43
|
+
return existsSync(join(terraformDir, ".terraform"));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Run tflocal init
|
|
47
|
+
*/
|
|
48
|
+
export async function runTfLocalInit(terraformDir) {
|
|
49
|
+
try {
|
|
50
|
+
const proc = Bun.spawn(["tflocal", "init", "-input=false"], {
|
|
51
|
+
cwd: terraformDir,
|
|
52
|
+
stdout: "pipe",
|
|
53
|
+
stderr: "pipe",
|
|
54
|
+
});
|
|
55
|
+
const exitCode = await proc.exited;
|
|
56
|
+
if (exitCode !== 0) {
|
|
57
|
+
const stderr = await new Response(proc.stderr).text();
|
|
58
|
+
return { success: false, error: stderr };
|
|
59
|
+
}
|
|
60
|
+
return { success: true };
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
error: error instanceof Error ? error.message : String(error),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Run tflocal apply with auto-approve
|
|
71
|
+
*/
|
|
72
|
+
export async function runTfLocalApply(terraformDir) {
|
|
73
|
+
try {
|
|
74
|
+
const proc = Bun.spawn(["tflocal", "apply", "-auto-approve", "-input=false"], {
|
|
75
|
+
cwd: terraformDir,
|
|
76
|
+
stdout: "pipe",
|
|
77
|
+
stderr: "pipe",
|
|
78
|
+
});
|
|
79
|
+
const exitCode = await proc.exited;
|
|
80
|
+
if (exitCode !== 0) {
|
|
81
|
+
const stderr = await new Response(proc.stderr).text();
|
|
82
|
+
const stdout = await new Response(proc.stdout).text();
|
|
83
|
+
// Terraform often outputs errors to stdout
|
|
84
|
+
return { success: false, error: stderr || stdout };
|
|
85
|
+
}
|
|
86
|
+
return { success: true };
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: error instanceof Error ? error.message : String(error),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Transform AWS URLs to LocalStack URLs
|
|
97
|
+
* Converts URLs like:
|
|
98
|
+
* https://abc123.execute-api.eu-west-2.amazonaws.com/
|
|
99
|
+
* To:
|
|
100
|
+
* http://abc123.execute-api.localhost.localstack.cloud:4566/
|
|
101
|
+
*/
|
|
102
|
+
export function transformToLocalStackUrl(value) {
|
|
103
|
+
if (typeof value !== "string") {
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
// Match API Gateway URLs: https://{api-id}.execute-api.{region}.amazonaws.com
|
|
107
|
+
const apiGatewayPattern = /https:\/\/([a-z0-9]+)\.execute-api\.([a-z0-9-]+)\.amazonaws\.com(\/.*)?/gi;
|
|
108
|
+
return value.replace(apiGatewayPattern, (match, apiId, region, path) => {
|
|
109
|
+
const pathSuffix = path || "";
|
|
110
|
+
return `http://${apiId}.execute-api.localhost.localstack.cloud:4566${pathSuffix}`;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Transform all URLs in terraform outputs to LocalStack format
|
|
115
|
+
*/
|
|
116
|
+
export function transformOutputsForLocalStack(outputs) {
|
|
117
|
+
const result = {};
|
|
118
|
+
for (const [key, value] of Object.entries(outputs)) {
|
|
119
|
+
result[key] = transformToLocalStackUrl(value);
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get terraform outputs as JSON
|
|
125
|
+
*/
|
|
126
|
+
export async function getTerraformOutputs(terraformDir, transformForLocalStack = true) {
|
|
127
|
+
try {
|
|
128
|
+
const proc = Bun.spawn(["tflocal", "output", "-json"], {
|
|
129
|
+
cwd: terraformDir,
|
|
130
|
+
stdout: "pipe",
|
|
131
|
+
stderr: "pipe",
|
|
132
|
+
});
|
|
133
|
+
const exitCode = await proc.exited;
|
|
134
|
+
if (exitCode !== 0) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const stdout = await new Response(proc.stdout).text();
|
|
138
|
+
const outputs = JSON.parse(stdout);
|
|
139
|
+
// Extract just the values from the output structure
|
|
140
|
+
const result = {};
|
|
141
|
+
for (const [key, output] of Object.entries(outputs)) {
|
|
142
|
+
result[key] = output.value;
|
|
143
|
+
}
|
|
144
|
+
// Transform URLs to LocalStack format if requested
|
|
145
|
+
if (transformForLocalStack) {
|
|
146
|
+
return transformOutputsForLocalStack(result);
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Detect LocalStack availability
|
|
156
|
+
* Returns an object with detection results and reasons
|
|
157
|
+
*/
|
|
158
|
+
export async function detectLocalStack() {
|
|
159
|
+
const [localstackRunning, tfLocalInstalled] = await Promise.all([
|
|
160
|
+
isLocalStackRunning(),
|
|
161
|
+
isTfLocalInstalled(),
|
|
162
|
+
]);
|
|
163
|
+
return {
|
|
164
|
+
available: localstackRunning && tfLocalInstalled,
|
|
165
|
+
localstackRunning,
|
|
166
|
+
tfLocalInstalled,
|
|
167
|
+
};
|
|
168
|
+
}
|