@fjall/generator 0.88.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.
Files changed (57) hide show
  1. package/dist/src/ast/astComputeParser.d.ts +4 -0
  2. package/dist/src/ast/astComputeParser.js +427 -0
  3. package/dist/src/ast/astInfrastructureParser.d.ts +357 -0
  4. package/dist/src/ast/astInfrastructureParser.js +1925 -0
  5. package/dist/src/ast/astSurgicalModification.d.ts +47 -0
  6. package/dist/src/ast/astSurgicalModification.js +400 -0
  7. package/dist/src/ast/index.d.ts +2 -0
  8. package/dist/src/ast/index.js +2 -0
  9. package/dist/src/aws/regions.d.ts +30 -0
  10. package/dist/src/aws/regions.js +254 -0
  11. package/dist/src/generation/common.d.ts +86 -0
  12. package/dist/src/generation/common.js +187 -0
  13. package/dist/src/generation/compute.d.ts +6 -0
  14. package/dist/src/generation/compute.js +547 -0
  15. package/dist/src/generation/database.d.ts +54 -0
  16. package/dist/src/generation/database.js +201 -0
  17. package/dist/src/generation/index.d.ts +12 -0
  18. package/dist/src/generation/index.js +18 -0
  19. package/dist/src/generation/infrastructure.d.ts +44 -0
  20. package/dist/src/generation/infrastructure.js +389 -0
  21. package/dist/src/generation/storage.d.ts +23 -0
  22. package/dist/src/generation/storage.js +174 -0
  23. package/dist/src/generation/storageConnections.d.ts +37 -0
  24. package/dist/src/generation/storageConnections.js +71 -0
  25. package/dist/src/index.d.ts +10 -0
  26. package/dist/src/index.js +19 -0
  27. package/dist/src/planning/index.d.ts +1 -0
  28. package/dist/src/planning/index.js +1 -0
  29. package/dist/src/planning/resourcePlanning.d.ts +58 -0
  30. package/dist/src/planning/resourcePlanning.js +216 -0
  31. package/dist/src/presets/index.d.ts +3 -0
  32. package/dist/src/presets/index.js +3 -0
  33. package/dist/src/presets/patternTierPresets.d.ts +93 -0
  34. package/dist/src/presets/patternTierPresets.js +131 -0
  35. package/dist/src/presets/storagePresets.d.ts +11 -0
  36. package/dist/src/presets/storagePresets.js +36 -0
  37. package/dist/src/presets/tierPresets.d.ts +59 -0
  38. package/dist/src/presets/tierPresets.js +384 -0
  39. package/dist/src/presets/tierTypes.d.ts +301 -0
  40. package/dist/src/presets/tierTypes.js +7 -0
  41. package/dist/src/schemas/constants.d.ts +74 -0
  42. package/dist/src/schemas/constants.js +208 -0
  43. package/dist/src/schemas/index.d.ts +3 -0
  44. package/dist/src/schemas/index.js +3 -0
  45. package/dist/src/schemas/instanceTypeArchitecture.d.ts +35 -0
  46. package/dist/src/schemas/instanceTypeArchitecture.js +75 -0
  47. package/dist/src/schemas/resourceSchemas.d.ts +3534 -0
  48. package/dist/src/schemas/resourceSchemas.js +2015 -0
  49. package/dist/src/types/Result.d.ts +19 -0
  50. package/dist/src/types/Result.js +31 -0
  51. package/dist/src/util/errorUtils.d.ts +2 -0
  52. package/dist/src/util/errorUtils.js +15 -0
  53. package/dist/src/validation/patterns.d.ts +300 -0
  54. package/dist/src/validation/patterns.js +360 -0
  55. package/dist/src/version.d.ts +1 -0
  56. package/dist/src/version.js +1 -0
  57. package/package.json +32 -0
@@ -0,0 +1,254 @@
1
+ export const DEFAULT_REGION = "us-east-2";
2
+ export const regions = Object.freeze([
3
+ "us-east-2",
4
+ "us-west-2",
5
+ "us-east-1",
6
+ "eu-west-1",
7
+ "eu-central-1",
8
+ "ap-southeast-1",
9
+ "ap-northeast-1",
10
+ "ap-southeast-2",
11
+ "us-west-1",
12
+ "ca-central-1",
13
+ "af-south-1",
14
+ "ap-east-1",
15
+ "ap-northeast-2",
16
+ "ap-northeast-3",
17
+ "ap-south-1",
18
+ "ap-south-2",
19
+ "ap-southeast-3",
20
+ "ap-southeast-4",
21
+ "eu-central-2",
22
+ "eu-north-1",
23
+ "eu-south-1",
24
+ "eu-south-2",
25
+ "eu-west-2",
26
+ "eu-west-3",
27
+ "me-central-1",
28
+ "me-south-1",
29
+ "sa-east-1",
30
+ ]);
31
+ export const AWS_REGIONS_METADATA = Object.freeze([
32
+ { code: "us-east-2", name: "US East", city: "Ohio", country: "USA" },
33
+ { code: "us-west-2", name: "US West", city: "Oregon", country: "USA" },
34
+ { code: "us-east-1", name: "US East", city: "N. Virginia", country: "USA" },
35
+ { code: "eu-west-1", name: "EU West", city: "Ireland", country: "Ireland" },
36
+ {
37
+ code: "eu-central-1",
38
+ name: "EU Central",
39
+ city: "Frankfurt",
40
+ country: "Germany",
41
+ },
42
+ {
43
+ code: "ap-southeast-1",
44
+ name: "Asia Pacific",
45
+ city: "Singapore",
46
+ country: "Singapore",
47
+ },
48
+ {
49
+ code: "ap-northeast-1",
50
+ name: "Asia Pacific",
51
+ city: "Tokyo",
52
+ country: "Japan",
53
+ },
54
+ {
55
+ code: "ap-southeast-2",
56
+ name: "Asia Pacific",
57
+ city: "Sydney",
58
+ country: "Australia",
59
+ },
60
+ { code: "us-west-1", name: "US West", city: "N. California", country: "USA" },
61
+ { code: "ca-central-1", name: "Canada", city: "Central", country: "Canada" },
62
+ {
63
+ code: "af-south-1",
64
+ name: "Africa",
65
+ city: "Cape Town",
66
+ country: "South Africa",
67
+ },
68
+ {
69
+ code: "ap-east-1",
70
+ name: "Asia Pacific",
71
+ city: "Hong Kong",
72
+ country: "China",
73
+ },
74
+ {
75
+ code: "ap-northeast-2",
76
+ name: "Asia Pacific",
77
+ city: "Seoul",
78
+ country: "South Korea",
79
+ },
80
+ {
81
+ code: "ap-northeast-3",
82
+ name: "Asia Pacific",
83
+ city: "Osaka",
84
+ country: "Japan",
85
+ },
86
+ {
87
+ code: "ap-south-1",
88
+ name: "Asia Pacific",
89
+ city: "Mumbai",
90
+ country: "India",
91
+ },
92
+ {
93
+ code: "ap-south-2",
94
+ name: "Asia Pacific",
95
+ city: "Hyderabad",
96
+ country: "India",
97
+ },
98
+ {
99
+ code: "ap-southeast-3",
100
+ name: "Asia Pacific",
101
+ city: "Jakarta",
102
+ country: "Indonesia",
103
+ },
104
+ {
105
+ code: "ap-southeast-4",
106
+ name: "Asia Pacific",
107
+ city: "Melbourne",
108
+ country: "Australia",
109
+ },
110
+ {
111
+ code: "eu-central-2",
112
+ name: "EU Central",
113
+ city: "Zurich",
114
+ country: "Switzerland",
115
+ },
116
+ {
117
+ code: "eu-north-1",
118
+ name: "EU North",
119
+ city: "Stockholm",
120
+ country: "Sweden",
121
+ },
122
+ { code: "eu-south-1", name: "EU South", city: "Milan", country: "Italy" },
123
+ { code: "eu-south-2", name: "EU South", city: "Spain", country: "Spain" },
124
+ { code: "eu-west-2", name: "EU West", city: "London", country: "UK" },
125
+ { code: "eu-west-3", name: "EU West", city: "Paris", country: "France" },
126
+ { code: "me-central-1", name: "Middle East", city: "UAE", country: "UAE" },
127
+ {
128
+ code: "me-south-1",
129
+ name: "Middle East",
130
+ city: "Bahrain",
131
+ country: "Bahrain",
132
+ },
133
+ {
134
+ code: "sa-east-1",
135
+ name: "South America",
136
+ city: "S\u00e3o Paulo",
137
+ country: "Brazil",
138
+ },
139
+ ]);
140
+ export const topRegions = Object.freeze([
141
+ { code: "us-east-2", name: "US East", city: "Ohio", country: "USA" },
142
+ { code: "us-west-2", name: "US West", city: "Oregon", country: "USA" },
143
+ { code: "us-east-1", name: "US East", city: "N. Virginia", country: "USA" },
144
+ { code: "eu-west-1", name: "EU West", city: "Ireland", country: "Ireland" },
145
+ {
146
+ code: "eu-central-1",
147
+ name: "EU Central",
148
+ city: "Frankfurt",
149
+ country: "Germany",
150
+ },
151
+ {
152
+ code: "ap-southeast-1",
153
+ name: "Asia Pacific",
154
+ city: "Singapore",
155
+ country: "Singapore",
156
+ },
157
+ ]);
158
+ export const commonRegions = Object.freeze([
159
+ "us-east-2",
160
+ "us-west-2",
161
+ "us-east-1",
162
+ "eu-west-1",
163
+ "ap-southeast-1",
164
+ ]);
165
+ export function parseRegionList(value) {
166
+ if (!value)
167
+ return [];
168
+ return value
169
+ .split(",")
170
+ .map((region) => region.trim())
171
+ .filter(Boolean);
172
+ }
173
+ export function isValidRegion(region) {
174
+ return regions.includes(region);
175
+ }
176
+ export function isValidRegionFormat(region) {
177
+ // AWS region format pattern
178
+ const regionRegex = /^[a-z]{2}-[a-z]+-[1-9]$/;
179
+ return regionRegex.test(region);
180
+ }
181
+ export function getSuggestions(input) {
182
+ // Return common regions if input is empty
183
+ if (!input) {
184
+ return commonRegions.slice(0, 3);
185
+ }
186
+ // Get prefix and direction from input
187
+ const parts = input.toLowerCase().split("-");
188
+ const prefix = parts[0];
189
+ const direction = parts.length > 1 ? parts[1] : "";
190
+ // First try to match both prefix and direction
191
+ let matches = [];
192
+ if (prefix && direction) {
193
+ matches = regions.filter((region) => region.startsWith(`${prefix}-${direction}`));
194
+ }
195
+ // If no matches, try just the prefix
196
+ if (matches.length === 0 && prefix) {
197
+ matches = regions.filter((region) => region.startsWith(`${prefix}-`));
198
+ }
199
+ // If still no matches, return common regions
200
+ return matches.length > 0 ? matches.slice(0, 3) : commonRegions.slice(0, 3);
201
+ }
202
+ export function validateRegion(region) {
203
+ // Check if the region is valid
204
+ if (isValidRegion(region)) {
205
+ return true;
206
+ }
207
+ // Get suggestions for invalid region
208
+ const suggestions = getSuggestions(region);
209
+ // If format is invalid, return format error
210
+ if (!isValidRegionFormat(region)) {
211
+ return `Invalid format. Try: ${suggestions.join(", ")}`;
212
+ }
213
+ // If format is valid but region not recognized
214
+ return `Unknown region. Try: ${suggestions.join(", ")}`;
215
+ }
216
+ export function validateRegionList(value) {
217
+ if (!value || value.trim() === "")
218
+ return true;
219
+ const regionsList = parseRegionList(value);
220
+ for (const region of regionsList) {
221
+ const regionValidation = validateRegion(region);
222
+ if (regionValidation !== true) {
223
+ return regionValidation;
224
+ }
225
+ }
226
+ return true;
227
+ }
228
+ export function filterDuplicateRegions(regions, primaryRegion) {
229
+ return regions.filter((region) => region !== primaryRegion);
230
+ }
231
+ export function getRegionOptions() {
232
+ return AWS_REGIONS_METADATA.map((region) => ({
233
+ label: region.city,
234
+ value: region.code,
235
+ description: region.code,
236
+ }));
237
+ }
238
+ export function getRegionOptionsExcluding(excludeCode) {
239
+ return AWS_REGIONS_METADATA.filter((region) => region.code !== excludeCode).map((region) => ({
240
+ label: region.city,
241
+ value: region.code,
242
+ description: region.code,
243
+ }));
244
+ }
245
+ export function getRegionName(code) {
246
+ const region = AWS_REGIONS_METADATA.find((r) => r.code === code);
247
+ return region ? region.city : code;
248
+ }
249
+ export function createRegionFormatter(primaryRegion) {
250
+ return (value) => {
251
+ const secondaryRegions = parseRegionList(value);
252
+ return filterDuplicateRegions(secondaryRegions, primaryRegion);
253
+ };
254
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Common Utilities for Code Generation
3
+ *
4
+ * Shared helper functions used across all generation modules.
5
+ */
6
+ import type { IdentifierValue, ExpressionValue, CallValue, SpecialValue } from "../schemas/resourceSchemas.js";
7
+ export type { IdentifierValue, ExpressionValue, CallValue, SpecialValue };
8
+ /**
9
+ * Type guard to check if a value is a special code generation value
10
+ */
11
+ export declare function isSpecialValue(value: unknown): value is SpecialValue;
12
+ /**
13
+ * Convert hyphenated/underscored names to PascalCase for resource names
14
+ */
15
+ export declare function toPascalCase(str: string): string;
16
+ /**
17
+ * Convert any case (PascalCase, camelCase, kebab-case, snake_case) to kebab-case.
18
+ * Three-pass regex: split acronyms, split camel boundaries, replace separators.
19
+ * e.g., "MyApp" -> "my-app", "AWSLambda" -> "aws-lambda", "myApp" -> "my-app"
20
+ */
21
+ export declare function toKebab(str: string): string;
22
+ /**
23
+ * Convert a name to a valid RDS database name (snake_case).
24
+ * RDS API allows letters, numbers, and underscores for PostgreSQL/MySQL DatabaseName.
25
+ * Hyphens are rejected, so convert them to underscores.
26
+ */
27
+ export declare function toValidDatabaseName(name: string): string;
28
+ /**
29
+ * Convert resource names to camelCase variable names
30
+ */
31
+ export declare function toVariableName(name: string): string;
32
+ /**
33
+ * Get the variable name for a resource, preferring preserved variableName
34
+ * from round-trip parsing over the generated default.
35
+ */
36
+ export declare function getVariableName(resource: {
37
+ name: string;
38
+ variableName?: string;
39
+ }): string;
40
+ /**
41
+ * Resolve the variable name for a resource by name, looking up across all
42
+ * resource arrays in the plan. Used for cross-resource references (e.g.,
43
+ * compute → database connection variable names).
44
+ */
45
+ export declare function resolveResourceVariable(plan: {
46
+ database: Array<{
47
+ name: string;
48
+ variableName?: string;
49
+ }>;
50
+ s3: Array<{
51
+ name: string;
52
+ variableName?: string;
53
+ }>;
54
+ compute: Array<{
55
+ name: string;
56
+ variableName?: string;
57
+ }>;
58
+ dynamodb?: Array<{
59
+ name: string;
60
+ variableName?: string;
61
+ }>;
62
+ sqs?: Array<{
63
+ name: string;
64
+ variableName?: string;
65
+ }>;
66
+ }, resourceName: string): string;
67
+ /**
68
+ * Format values for code generation, handling complex types.
69
+ * Simple objects (few primitive values) are kept on one line.
70
+ */
71
+ export declare function formatValue(value: unknown, baseIndent?: string): string;
72
+ type PropertyFormat = "raw" | "string" | "object" | "boolean-or-object";
73
+ /**
74
+ * Build a property string for code generation.
75
+ * Handles different value formats: raw (numbers/booleans), string (quoted),
76
+ * object (using formatValue), and boolean-or-object (special false handling).
77
+ */
78
+ export declare function buildProperty(condition: boolean, name: string, value: unknown, format?: PropertyFormat): string;
79
+ /**
80
+ * Emit extra properties preserved verbatim during round-trip.
81
+ * Each property is emitted as `key: sourceText,` at the given indentation.
82
+ */
83
+ export declare function emitExtraProperties(extraProperties: Array<{
84
+ key: string;
85
+ sourceText: string;
86
+ }> | undefined, indent?: string): string;
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Common Utilities for Code Generation
3
+ *
4
+ * Shared helper functions used across all generation modules.
5
+ */
6
+ /**
7
+ * Type guard to check if a value is a special code generation value
8
+ */
9
+ export function isSpecialValue(value) {
10
+ if (typeof value !== "object" || value === null) {
11
+ return false;
12
+ }
13
+ const obj = value;
14
+ return (typeof obj.__identifier === "string" ||
15
+ typeof obj.__expression === "string" ||
16
+ typeof obj.__call === "string");
17
+ }
18
+ /**
19
+ * Convert hyphenated/underscored names to PascalCase for resource names
20
+ */
21
+ export function toPascalCase(str) {
22
+ return str
23
+ .replace(/[-_](.)/g, (_, c) => c.toUpperCase())
24
+ .replace(/^./, (c) => c.toUpperCase());
25
+ }
26
+ /**
27
+ * Convert any case (PascalCase, camelCase, kebab-case, snake_case) to kebab-case.
28
+ * Three-pass regex: split acronyms, split camel boundaries, replace separators.
29
+ * e.g., "MyApp" -> "my-app", "AWSLambda" -> "aws-lambda", "myApp" -> "my-app"
30
+ */
31
+ export function toKebab(str) {
32
+ return str
33
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
34
+ .replace(/([a-z\d])([A-Z])/g, "$1-$2")
35
+ .replace(/[\s_]+/g, "-")
36
+ .toLowerCase();
37
+ }
38
+ /**
39
+ * Convert a name to a valid RDS database name (snake_case).
40
+ * RDS API allows letters, numbers, and underscores for PostgreSQL/MySQL DatabaseName.
41
+ * Hyphens are rejected, so convert them to underscores.
42
+ */
43
+ export function toValidDatabaseName(name) {
44
+ return toKebab(name).replace(/-/g, "_");
45
+ }
46
+ /**
47
+ * Convert resource names to camelCase variable names
48
+ */
49
+ export function toVariableName(name) {
50
+ return name
51
+ .replace(/[^a-zA-Z0-9]/g, " ")
52
+ .split(" ")
53
+ .map((word, index) => index === 0
54
+ ? word.charAt(0).toLowerCase() + word.slice(1)
55
+ : word.charAt(0).toUpperCase() + word.slice(1))
56
+ .join("");
57
+ }
58
+ /**
59
+ * Get the variable name for a resource, preferring preserved variableName
60
+ * from round-trip parsing over the generated default.
61
+ */
62
+ export function getVariableName(resource) {
63
+ return resource.variableName ?? toVariableName(resource.name);
64
+ }
65
+ /**
66
+ * Resolve the variable name for a resource by name, looking up across all
67
+ * resource arrays in the plan. Used for cross-resource references (e.g.,
68
+ * compute → database connection variable names).
69
+ */
70
+ export function resolveResourceVariable(plan, resourceName) {
71
+ for (const db of plan.database) {
72
+ if (db.name === resourceName && db.variableName)
73
+ return db.variableName;
74
+ }
75
+ for (const s3 of plan.s3) {
76
+ if (s3.name === resourceName && s3.variableName)
77
+ return s3.variableName;
78
+ }
79
+ for (const c of plan.compute) {
80
+ if (c.name === resourceName && c.variableName)
81
+ return c.variableName;
82
+ }
83
+ for (const ddb of plan.dynamodb ?? []) {
84
+ if (ddb.name === resourceName && ddb.variableName)
85
+ return ddb.variableName;
86
+ }
87
+ for (const sqs of plan.sqs ?? []) {
88
+ if (sqs.name === resourceName && sqs.variableName)
89
+ return sqs.variableName;
90
+ }
91
+ return toVariableName(resourceName);
92
+ }
93
+ /**
94
+ * Check if an object is simple enough to format inline (single line).
95
+ * Simple = no nested objects/arrays and few primitive values.
96
+ */
97
+ function isSimpleObject(value) {
98
+ const entries = Object.entries(value);
99
+ if (entries.length === 0)
100
+ return true;
101
+ if (entries.length > 1)
102
+ return false;
103
+ return entries.every(([, v]) => typeof v === "string" ||
104
+ typeof v === "number" ||
105
+ typeof v === "boolean" ||
106
+ v === null);
107
+ }
108
+ /**
109
+ * Format values for code generation, handling complex types.
110
+ * Simple objects (few primitive values) are kept on one line.
111
+ */
112
+ export function formatValue(value, baseIndent = " ") {
113
+ if (value === undefined || value === null) {
114
+ return "undefined";
115
+ }
116
+ if (typeof value === "string") {
117
+ return JSON.stringify(value);
118
+ }
119
+ if (typeof value === "number" || typeof value === "boolean") {
120
+ return String(value);
121
+ }
122
+ if (isSpecialValue(value)) {
123
+ if ("__identifier" in value)
124
+ return value.__identifier;
125
+ if ("__expression" in value)
126
+ return value.__expression;
127
+ if ("__call" in value)
128
+ return value.__call;
129
+ }
130
+ if (Array.isArray(value)) {
131
+ return `[${value.map((v) => formatValue(v, baseIndent)).join(", ")}]`;
132
+ }
133
+ if (typeof value === "object") {
134
+ // Filter out undefined values — prevents emitting `key: undefined` in generated code
135
+ const definedEntries = Object.entries(value).filter(([, v]) => v !== undefined);
136
+ // Simple objects stay on one line
137
+ if (isSimpleObject(value)) {
138
+ const formatted = definedEntries
139
+ .map(([k, v]) => `${k}: ${formatValue(v, baseIndent)}`)
140
+ .join(", ");
141
+ return formatted ? `{ ${formatted} }` : "{}";
142
+ }
143
+ // Complex objects get multi-line formatting
144
+ const innerIndent = baseIndent + " ";
145
+ const formatted = definedEntries
146
+ .map(([k, v]) => `${innerIndent}${k}: ${formatValue(v, innerIndent)}`)
147
+ .join(",\n");
148
+ return formatted ? `{\n${formatted}\n${baseIndent}}` : "{}";
149
+ }
150
+ return JSON.stringify(value);
151
+ }
152
+ /**
153
+ * Build a property string for code generation.
154
+ * Handles different value formats: raw (numbers/booleans), string (quoted),
155
+ * object (using formatValue), and boolean-or-object (special false handling).
156
+ */
157
+ export function buildProperty(condition, name, value, format = "raw") {
158
+ if (!condition)
159
+ return "";
160
+ let formattedValue;
161
+ switch (format) {
162
+ case "string":
163
+ formattedValue = `"${value}"`;
164
+ break;
165
+ case "object":
166
+ formattedValue = formatValue(value);
167
+ break;
168
+ case "boolean-or-object":
169
+ formattedValue = value === false ? "false" : formatValue(value);
170
+ break;
171
+ default:
172
+ formattedValue = String(value);
173
+ }
174
+ return `
175
+ ${name}: ${formattedValue},`;
176
+ }
177
+ /**
178
+ * Emit extra properties preserved verbatim during round-trip.
179
+ * Each property is emitted as `key: sourceText,` at the given indentation.
180
+ */
181
+ export function emitExtraProperties(extraProperties, indent = " ") {
182
+ if (!extraProperties?.length)
183
+ return "";
184
+ return extraProperties
185
+ .map((prop) => `\n${indent}${prop.key}: ${prop.sourceText},`)
186
+ .join("");
187
+ }
@@ -0,0 +1,6 @@
1
+ import type { ApplicationResourcePlan, ComputeResourcePlan } from "../schemas/resourceSchemas.js";
2
+ export declare function generateLambdaCode(compute: ComputeResourcePlan, _plan: ApplicationResourcePlan): string;
3
+ export declare function generateLambdaConnectionsCode(compute: ComputeResourcePlan, plan: ApplicationResourcePlan): string;
4
+ export declare function generateEc2Code(compute: ComputeResourcePlan): string;
5
+ export declare function generateEcsCode(compute: ComputeResourcePlan, plan: ApplicationResourcePlan): string;
6
+ export declare function generateComputeCode(plan: ApplicationResourcePlan, cdnReferencedResources: Set<string>): string;