@fjall/generator 0.89.5 → 0.94.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.
Files changed (168) hide show
  1. package/LICENSE +50 -21
  2. package/README.md +28 -0
  3. package/dist/.minified +1 -0
  4. package/dist/src/ast/astCdnParser.d.ts +5 -0
  5. package/dist/src/ast/astCdnParser.js +1 -114
  6. package/dist/src/ast/astCommonParser.d.ts +6 -17
  7. package/dist/src/ast/astCommonParser.js +1 -351
  8. package/dist/src/ast/astComputeConnectionParser.d.ts +18 -0
  9. package/dist/src/ast/astComputeConnectionParser.js +1 -0
  10. package/dist/src/ast/astComputeParser.d.ts +6 -0
  11. package/dist/src/ast/astComputeParser.js +1 -473
  12. package/dist/src/ast/astComputeParserHelpers.d.ts +21 -0
  13. package/dist/src/ast/astComputeParserHelpers.js +1 -0
  14. package/dist/src/ast/astDatabaseParser.d.ts +9 -24
  15. package/dist/src/ast/astDatabaseParser.js +1 -275
  16. package/dist/src/ast/astDomainParser.d.ts +139 -0
  17. package/dist/src/ast/astDomainParser.js +1 -0
  18. package/dist/src/ast/astDynamoDBParser.d.ts +35 -0
  19. package/dist/src/ast/astDynamoDBParser.js +1 -0
  20. package/dist/src/ast/astExpressionEvaluator.d.ts +23 -0
  21. package/dist/src/ast/astExpressionEvaluator.js +1 -0
  22. package/dist/src/ast/astInfrastructureParser.d.ts +12 -49
  23. package/dist/src/ast/astInfrastructureParser.js +1 -552
  24. package/dist/src/ast/astMessagingParser.d.ts +5 -0
  25. package/dist/src/ast/astMessagingParser.js +1 -78
  26. package/dist/src/ast/astNetworkParser.d.ts +6 -0
  27. package/dist/src/ast/astNetworkParser.js +1 -219
  28. package/dist/src/ast/astPatternParser.d.ts +6 -0
  29. package/dist/src/ast/astPatternParser.js +1 -155
  30. package/dist/src/ast/astPlanConverter.d.ts +11 -0
  31. package/dist/src/ast/astPlanConverter.js +2 -0
  32. package/dist/src/ast/astStatementClassifier.d.ts +24 -0
  33. package/dist/src/ast/astStatementClassifier.js +1 -0
  34. package/dist/src/ast/astStatementQueries.d.ts +21 -0
  35. package/dist/src/ast/astStatementQueries.js +3 -0
  36. package/dist/src/ast/astStorageParser.d.ts +5 -0
  37. package/dist/src/ast/astStorageParser.js +1 -164
  38. package/dist/src/ast/astSurgicalModification.js +19 -400
  39. package/dist/src/ast/astTestHelpers.d.ts +635 -0
  40. package/dist/src/ast/astTestHelpers.js +1 -0
  41. package/dist/src/ast/index.d.ts +1 -0
  42. package/dist/src/ast/index.js +1 -6
  43. package/dist/src/aws/regions.js +1 -254
  44. package/dist/src/codemod/_internal.d.ts +12 -0
  45. package/dist/src/codemod/_internal.js +1 -0
  46. package/dist/src/codemod/edits/addResource/bodyIndex.d.ts +34 -0
  47. package/dist/src/codemod/edits/addResource/bodyIndex.js +1 -0
  48. package/dist/src/codemod/edits/addResource/propertyBuilder.d.ts +7 -0
  49. package/dist/src/codemod/edits/addResource/propertyBuilder.js +1 -0
  50. package/dist/src/codemod/edits/addResource.d.ts +9 -0
  51. package/dist/src/codemod/edits/addResource.js +1 -0
  52. package/dist/src/codemod/edits/ensureImports.d.ts +26 -0
  53. package/dist/src/codemod/edits/ensureImports.js +1 -0
  54. package/dist/src/codemod/edits/findInsertionPosition.d.ts +39 -0
  55. package/dist/src/codemod/edits/findInsertionPosition.js +1 -0
  56. package/dist/src/codemod/edits/index.d.ts +5 -0
  57. package/dist/src/codemod/edits/index.js +1 -0
  58. package/dist/src/codemod/edits/modifyResource/literalConversion.d.ts +37 -0
  59. package/dist/src/codemod/edits/modifyResource/literalConversion.js +1 -0
  60. package/dist/src/codemod/edits/modifyResource.d.ts +9 -0
  61. package/dist/src/codemod/edits/modifyResource.js +1 -0
  62. package/dist/src/codemod/edits/removeResource/commentHeuristic.d.ts +31 -0
  63. package/dist/src/codemod/edits/removeResource/commentHeuristic.js +1 -0
  64. package/dist/src/codemod/edits/removeResource/importPruning.d.ts +8 -0
  65. package/dist/src/codemod/edits/removeResource/importPruning.js +1 -0
  66. package/dist/src/codemod/edits/removeResource.d.ts +10 -0
  67. package/dist/src/codemod/edits/removeResource.js +1 -0
  68. package/dist/src/codemod/fileRewriter/builders.d.ts +57 -0
  69. package/dist/src/codemod/fileRewriter/builders.js +1 -0
  70. package/dist/src/codemod/fileRewriter/index.d.ts +4 -0
  71. package/dist/src/codemod/fileRewriter/index.js +1 -0
  72. package/dist/src/codemod/fileRewriter/locateByRange.d.ts +65 -0
  73. package/dist/src/codemod/fileRewriter/locateByRange.js +1 -0
  74. package/dist/src/codemod/fileRewriter/parse.d.ts +18 -0
  75. package/dist/src/codemod/fileRewriter/parse.js +2 -0
  76. package/dist/src/codemod/fileRewriter/print.d.ts +46 -0
  77. package/dist/src/codemod/fileRewriter/print.js +4 -0
  78. package/dist/src/codemod/historyPaths.d.ts +2 -0
  79. package/dist/src/codemod/historyPaths.js +1 -0
  80. package/dist/src/codemod/index.d.ts +7 -0
  81. package/dist/src/codemod/index.js +1 -0
  82. package/dist/src/codemod/listResources.d.ts +4 -0
  83. package/dist/src/codemod/listResources.js +1 -0
  84. package/dist/src/codemod/registry.d.ts +42 -0
  85. package/dist/src/codemod/registry.js +1 -0
  86. package/dist/src/codemod/semanticIndex/findReferences.d.ts +15 -0
  87. package/dist/src/codemod/semanticIndex/findReferences.js +2 -0
  88. package/dist/src/codemod/semanticIndex/index.d.ts +4 -0
  89. package/dist/src/codemod/semanticIndex/index.js +1 -0
  90. package/dist/src/codemod/semanticIndex/listImports.d.ts +24 -0
  91. package/dist/src/codemod/semanticIndex/listImports.js +1 -0
  92. package/dist/src/codemod/semanticIndex/locateByShape.d.ts +28 -0
  93. package/dist/src/codemod/semanticIndex/locateByShape.js +1 -0
  94. package/dist/src/codemod/semanticIndex/projectCache.d.ts +14 -0
  95. package/dist/src/codemod/semanticIndex/projectCache.js +1 -0
  96. package/dist/src/codemod/types.d.ts +172 -0
  97. package/dist/src/codemod/types.js +1 -0
  98. package/dist/src/dns/bindParser.js +2 -224
  99. package/dist/src/dns/bindWriter.js +3 -52
  100. package/dist/src/dns/domainFileGenerator.d.ts +20 -0
  101. package/dist/src/dns/domainFileGenerator.js +207 -0
  102. package/dist/src/dns/domainRecords.d.ts +164 -0
  103. package/dist/src/dns/domainRecords.js +1 -0
  104. package/dist/src/dns/index.d.ts +2 -1
  105. package/dist/src/dns/index.js +1 -4
  106. package/dist/src/dns/types.js +1 -52
  107. package/dist/src/generation/common.js +6 -161
  108. package/dist/src/generation/compute.js +82 -590
  109. package/dist/src/generation/database.js +12 -198
  110. package/dist/src/generation/generatePatternCode.d.ts +58 -0
  111. package/dist/src/generation/generatePatternCode.js +33 -0
  112. package/dist/src/generation/index.js +1 -20
  113. package/dist/src/generation/infrastructure.d.ts +1 -5
  114. package/dist/src/generation/infrastructure.js +35 -377
  115. package/dist/src/generation/messagingConnections.js +1 -73
  116. package/dist/src/generation/storage.d.ts +0 -15
  117. package/dist/src/generation/storage.js +35 -168
  118. package/dist/src/generation/storageConnections.js +1 -75
  119. package/dist/src/planning/generateResourceChange.d.ts +21 -0
  120. package/dist/src/planning/generateResourceChange.js +1 -0
  121. package/dist/src/planning/index.d.ts +3 -0
  122. package/dist/src/planning/index.js +1 -1
  123. package/dist/src/planning/resourceAddition.d.ts +154 -0
  124. package/dist/src/planning/resourceAddition.js +1 -0
  125. package/dist/src/planning/resourceConnections.d.ts +19 -0
  126. package/dist/src/planning/resourceConnections.js +1 -0
  127. package/dist/src/planning/resourcePlanning.js +1 -214
  128. package/dist/src/presets/index.js +1 -3
  129. package/dist/src/presets/patternTierPresets.js +1 -131
  130. package/dist/src/presets/storagePresets.js +1 -36
  131. package/dist/src/presets/tierPresets.d.ts +5 -8
  132. package/dist/src/presets/tierPresets.js +1 -384
  133. package/dist/src/presets/tierTypes.d.ts +1 -1
  134. package/dist/src/presets/tierTypes.js +0 -7
  135. package/dist/src/schemas/alarmSchemas.d.ts +19 -0
  136. package/dist/src/schemas/alarmSchemas.js +1 -0
  137. package/dist/src/schemas/applicationSchemas.d.ts +22 -6
  138. package/dist/src/schemas/applicationSchemas.js +1 -80
  139. package/dist/src/schemas/baseSchemas.d.ts +8 -3
  140. package/dist/src/schemas/baseSchemas.js +2 -248
  141. package/dist/src/schemas/cdnSchemas.js +1 -62
  142. package/dist/src/schemas/computeSchemas.d.ts +25 -3
  143. package/dist/src/schemas/computeSchemas.js +1 -727
  144. package/dist/src/schemas/constants.d.ts +5 -7
  145. package/dist/src/schemas/constants.js +1 -218
  146. package/dist/src/schemas/databaseSchemas.d.ts +6 -1
  147. package/dist/src/schemas/databaseSchemas.js +1 -366
  148. package/dist/src/schemas/index.js +1 -3
  149. package/dist/src/schemas/instanceTypeArchitecture.js +1 -75
  150. package/dist/src/schemas/messagingSchemas.js +1 -29
  151. package/dist/src/schemas/networkSchemas.js +1 -125
  152. package/dist/src/schemas/patternSchemas.d.ts +1 -1
  153. package/dist/src/schemas/patternSchemas.js +1 -294
  154. package/dist/src/schemas/resourceSchemas.d.ts +1 -0
  155. package/dist/src/schemas/resourceSchemas.js +1 -28
  156. package/dist/src/schemas/sharedTypes.d.ts +18 -0
  157. package/dist/src/schemas/sharedTypes.js +1 -0
  158. package/dist/src/schemas/storageSchemas.d.ts +1 -0
  159. package/dist/src/schemas/storageSchemas.js +1 -119
  160. package/dist/src/types/Result.js +1 -31
  161. package/dist/src/util/errorUtils.js +1 -1
  162. package/dist/src/validation/patterns.d.ts +9 -0
  163. package/dist/src/validation/patterns.js +1 -369
  164. package/dist/src/version.d.ts +1 -1
  165. package/dist/src/version.js +1 -1
  166. package/package.json +29 -9
  167. package/dist/src/dns/infrastructureWriter.d.ts +0 -2
  168. package/dist/src/dns/infrastructureWriter.js +0 -58
@@ -1,182 +1,49 @@
1
- /**
2
- * Storage Code Generation
3
- *
4
- * Functions for generating storage infrastructure code including
5
- * S3 buckets, DynamoDB tables, and SQS queues.
6
- */
7
- import { buildProperty, getVariableName, emitExtraProperties, } from "./common.js";
8
- /**
9
- * Generate S3 bucket configuration properties using buildProperty for consistent formatting
10
- */
11
- function generateS3BucketProps(bucket) {
12
- let code = "";
13
- code += buildProperty(bucket.bucketName !== undefined, "bucketName", bucket.bucketName, "string");
14
- code += buildProperty(bucket.stackPlacement !== undefined && bucket.stackPlacement !== "storage", "stackPlacement", bucket.stackPlacement, "string");
15
- code += buildProperty(bucket.publicReadAccess === true, "publicReadAccess", true);
16
- code += buildProperty(bucket.websiteHosting !== undefined, "websiteHosting", bucket.websiteHosting, "object");
17
- code += buildProperty(bucket.backupVaultTier !== undefined, "backupVaultTier", bucket.backupVaultTier, "string");
18
- code += buildProperty(bucket.versioned !== undefined, "versioned", bucket.versioned);
19
- code += buildProperty(bucket.encryption !== undefined, "encryption", bucket.encryption, "string");
20
- code += buildProperty(bucket.kmsKeyArn !== undefined, "kmsKeyArn", bucket.kmsKeyArn, "string");
21
- code += buildProperty(bucket.cors !== undefined && bucket.cors.length > 0, "cors", bucket.cors, "object");
22
- code += buildProperty(bucket.deployment !== undefined, "deployment", bucket.deployment, "object");
23
- return code;
24
- }
25
- /**
26
- * Check if an S3 bucket needs a variable assignment (referenced by compute or CDN)
27
- */
28
- export function s3NeedsVariable(bucket, plan) {
29
- // Referenced by compute via connectedStorage
30
- const computeRef = plan.compute.some((compute) => compute.connectedStorage?.includes(bucket.name));
31
- if (computeRef)
32
- return true;
33
- // Referenced by CDN as an origin
34
- if (plan.cdn) {
35
- if (plan.cdn.defaultOriginRef === bucket.name)
36
- return true;
37
- if (plan.cdn.behaviours?.some((b) => b.originRef === bucket.name))
38
- return true;
39
- }
40
- return false;
41
- }
42
- /**
43
- * Generate S3 bucket infrastructure code
44
- */
45
- export function generateS3Code(plan) {
46
- if (plan.s3.length === 0)
47
- return "";
48
- let code = "";
49
- for (let i = 0; i < plan.s3.length; i++) {
50
- const bucket = plan.s3[i];
51
- const bucketVariable = getVariableName(bucket);
52
- const hasReferences = s3NeedsVariable(bucket, plan);
53
- const needsLeadingNewline = plan.database.length > 0 || i > 0;
54
- const leadingNewline = needsLeadingNewline ? "\n" : "";
55
- const props = generateS3BucketProps(bucket);
56
- const prefix = hasReferences ? `const ${bucketVariable} = ` : "";
57
- const extraPropsStr = emitExtraProperties(bucket.extraProperties);
58
- if (props || extraPropsStr) {
59
- code += `${leadingNewline}${prefix}app.addStorage(
60
- StorageFactory.build("${bucket.name}", {${props}${extraPropsStr}
1
+ import{buildProperty as o,getVariableName as d,emitExtraProperties as c}from"./common.js";function f(e){let t="";return t+=o(e.bucketName!==void 0,"bucketName",e.bucketName,"string"),t+=o(e.stackPlacement!==void 0&&e.stackPlacement!=="storage","stackPlacement",e.stackPlacement,"string"),t+=o(e.publicReadAccess===!0,"publicReadAccess",!0),t+=o(e.websiteHosting!==void 0,"websiteHosting",e.websiteHosting,"object"),t+=o(e.backupVaultTier!==void 0,"backupVaultTier",e.backupVaultTier,"string"),t+=o(e.versioned!==void 0,"versioned",e.versioned),t+=o(e.encryption!==void 0,"encryption",e.encryption,"string"),t+=o(e.kmsKeyArn!==void 0,"kmsKeyArn",e.kmsKeyArn,"string"),t+=o(e.cors!==void 0&&e.cors.length>0,"cors",e.cors,"object"),t+=o(e.deployment!==void 0,"deployment",e.deployment,"object"),t+=o(e.retain===!0,"retain",!0),t}function p(e,t){return!!(t.compute.some(r=>r.connectedStorage?.includes(e.name))||t.cdn&&(t.cdn.defaultOriginRef===e.name||t.cdn.behaviours?.some(r=>r.originRef===e.name)))}function b(e){if(e.s3.length===0)return"";let t="";for(let n=0;n<e.s3.length;n++){const r=e.s3[n],a=d(r),i=p(r,e),u=e.database.length>0||n>0?`
2
+ `:"",m=f(r),y=i?`const ${a} = `:"",l=c(r.extraProperties);m||l?t+=`${u}${y}app.addStorage(
3
+ StorageFactory.build("${r.name}", {${m}${l}
61
4
  })
62
5
  );
63
- `;
64
- }
65
- else {
66
- code += `${leadingNewline}${prefix}app.addStorage(
67
- StorageFactory.build("${bucket.name}", {})
6
+ `:t+=`${u}${y}app.addStorage(
7
+ StorageFactory.build("${r.name}", {})
68
8
  );
69
- `;
70
- }
71
- }
72
- return code;
73
- }
74
- /**
75
- * Generate DynamoDB table infrastructure code
76
- */
77
- export function generateDynamoDBCode(plan) {
78
- if (!plan.dynamodb || plan.dynamodb.length === 0)
79
- return "";
80
- let code = "";
81
- for (const table of plan.dynamodb) {
82
- const tableVariable = getVariableName(table);
83
- code += `
84
- const ${tableVariable} = app.addDatabase(
85
- DatabaseFactory.build("${table.name}", {
9
+ `}return t}function x(e){if(!e.dynamodb||e.dynamodb.length===0)return"";let t="";for(const n of e.dynamodb){const r=d(n);if(t+=`
10
+ const ${r} = app.addDatabase(
11
+ DatabaseFactory.build("${n.name}", {
86
12
  type: "DynamoDB",
87
13
  partitionKey: {
88
- name: "${table.partitionKey.name}",
89
- type: "${table.partitionKey.type}",
90
- }`;
91
- if (table.sortKey) {
92
- code += `,
14
+ name: "${n.partitionKey.name}",
15
+ type: "${n.partitionKey.type}",
16
+ }`,n.sortKey&&(t+=`,
93
17
  sortKey: {
94
- name: "${table.sortKey.name}",
95
- type: "${table.sortKey.type}",
96
- }`;
97
- }
98
- if (table.globalSecondaryIndexes &&
99
- table.globalSecondaryIndexes.length > 0) {
100
- const gsiStr = table.globalSecondaryIndexes
101
- .map((gsi) => {
102
- let gsiCode = `{
103
- indexName: "${gsi.indexName}",
18
+ name: "${n.sortKey.name}",
19
+ type: "${n.sortKey.type}",
20
+ }`),n.globalSecondaryIndexes&&n.globalSecondaryIndexes.length>0){const a=n.globalSecondaryIndexes.map(i=>{let s=`{
21
+ indexName: "${i.indexName}",
104
22
  partitionKey: {
105
- name: "${gsi.partitionKey.name}",
106
- type: "${gsi.partitionKey.type}",
107
- },`;
108
- if (gsi.sortKey) {
109
- gsiCode += `
23
+ name: "${i.partitionKey.name}",
24
+ type: "${i.partitionKey.type}",
25
+ },`;return i.sortKey&&(s+=`
110
26
  sortKey: {
111
- name: "${gsi.sortKey.name}",
112
- type: "${gsi.sortKey.type}",
113
- },`;
114
- }
115
- gsiCode += `
116
- }`;
117
- return gsiCode;
118
- })
119
- .join(",\n ");
120
- code += `,
27
+ name: "${i.sortKey.name}",
28
+ type: "${i.sortKey.type}",
29
+ },`),s+=`
30
+ }`,s}).join(`,
31
+ `);t+=`,
121
32
  globalSecondaryIndexes: [
122
- ${gsiStr}
123
- ]`;
124
- }
125
- if (table.ttlAttribute) {
126
- code += `,
127
- ttlAttribute: "${table.ttlAttribute}"`;
128
- }
129
- if (table.stream) {
130
- code += `,
131
- stream: true`;
132
- }
133
- code += emitExtraProperties(table.extraProperties);
134
- code += `
33
+ ${a}
34
+ ]`}n.ttlAttribute&&(t+=`,
35
+ ttlAttribute: "${n.ttlAttribute}"`),n.stream&&(t+=`,
36
+ stream: true`),t+=c(n.extraProperties),t+=`
135
37
  })
136
38
  );
137
- `;
138
- }
139
- return code;
140
- }
141
- /**
142
- * Check if an SQS queue needs a variable assignment (referenced by compute)
143
- */
144
- export function sqsNeedsVariable(queue, plan) {
145
- return plan.compute.some((compute) => compute.connectedMessaging?.includes(queue.name));
146
- }
147
- /**
148
- * Generate SQS queue infrastructure code
149
- */
150
- export function generateSQSCode(plan) {
151
- if (!plan.sqs || plan.sqs.length === 0)
152
- return "";
153
- let code = "";
154
- for (const queue of plan.sqs) {
155
- const queueVariable = getVariableName(queue);
156
- const hasReferences = sqsNeedsVariable(queue, plan);
157
- const prefix = hasReferences ? `const ${queueVariable} = ` : "";
158
- code += `
159
- ${prefix}app.addMessaging(
160
- MessagingFactory.build("${queue.name}", {
39
+ `}return t}function g(e,t){return t.compute.some(n=>n.connectedMessaging?.includes(e.name))}function K(e){if(!e.sqs||e.sqs.length===0)return"";let t="";for(const n of e.sqs){const r=d(n),i=g(n,e)?`const ${r} = `:"";t+=`
40
+ ${i}app.addMessaging(
41
+ MessagingFactory.build("${n.name}", {
161
42
  type: "queue",
162
- queueType: "${queue.queueType}"`;
163
- if (queue.visibilityTimeout !== undefined) {
164
- code += `,
165
- visibilityTimeout: ${queue.visibilityTimeout}`;
166
- }
167
- if (queue.retentionPeriod !== undefined) {
168
- code += `,
169
- messageRetentionPeriod: ${queue.retentionPeriod}`;
170
- }
171
- if (queue.contentBasedDeduplication) {
172
- code += `,
173
- contentBasedDeduplication: true`;
174
- }
175
- code += emitExtraProperties(queue.extraProperties);
176
- code += `
43
+ queueType: "${n.queueType}"`,n.visibilityTimeout!==void 0&&(t+=`,
44
+ visibilityTimeout: ${n.visibilityTimeout}`),n.retentionPeriod!==void 0&&(t+=`,
45
+ messageRetentionPeriod: ${n.retentionPeriod}`),n.contentBasedDeduplication&&(t+=`,
46
+ contentBasedDeduplication: true`),t+=c(n.extraProperties),t+=`
177
47
  })
178
48
  );
179
- `;
180
- }
181
- return code;
182
- }
49
+ `}return t}export{x as generateDynamoDBCode,b as generateS3Code,K as generateSQSCode,p as s3NeedsVariable,g as sqsNeedsVariable};
@@ -1,75 +1 @@
1
- /**
2
- * Storage Connection Code Generation
3
- *
4
- * Functions for generating storage connection code (env vars, connections array)
5
- * following the same pattern as database connections.
6
- */
7
- import { getVariableName } from "./common.js";
8
- export const STORAGE_ENV_VARS = Object.freeze({
9
- NAME: "BUCKET_NAME",
10
- });
11
- /**
12
- * Get S3 buckets connected to a compute resource
13
- */
14
- export function getConnectedStorage(plan, compute) {
15
- const { connectedStorage } = compute;
16
- if (!connectedStorage?.length)
17
- return [];
18
- return plan.s3.filter((bucket) => connectedStorage.includes(bucket.name));
19
- }
20
- /**
21
- * Generate storage environment variable entries for a list of connected buckets.
22
- * S3 uses IAM-based access, so there are no secrets — only BUCKET_NAME env vars.
23
- */
24
- export function generateStorageEnvVarEntries(connectedStorage) {
25
- const entries = [];
26
- if (connectedStorage.length === 1) {
27
- const bucket = connectedStorage[0];
28
- if (!bucket)
29
- return entries;
30
- const bucketVar = getVariableName(bucket);
31
- entries.push({
32
- key: STORAGE_ENV_VARS.NAME,
33
- expression: `${bucketVar}.getBucketName()`,
34
- });
35
- }
36
- else {
37
- for (const [index, bucket] of connectedStorage.entries()) {
38
- const bucketVar = getVariableName(bucket);
39
- const suffix = index === 0 ? "" : `_${index + 1}`;
40
- entries.push({
41
- key: `${STORAGE_ENV_VARS.NAME}${suffix}`,
42
- expression: `${bucketVar}.getBucketName()`,
43
- });
44
- }
45
- }
46
- return entries;
47
- }
48
- /**
49
- * Build storage environment variables for a compute resource.
50
- * S3 always uses IAM — secrets is always empty.
51
- */
52
- export function buildStorageEnvVars(plan, compute) {
53
- const env = {};
54
- const secrets = {};
55
- if (!compute.connectedStorage?.length) {
56
- return { env, secrets };
57
- }
58
- const connectedStorage = getConnectedStorage(plan, compute);
59
- const entries = generateStorageEnvVarEntries(connectedStorage);
60
- for (const entry of entries) {
61
- env[entry.key] = { __expression: entry.expression };
62
- }
63
- return { env, secrets };
64
- }
65
- /**
66
- * Format combined connections array for generated infrastructure code.
67
- * Merges database, storage, and messaging variable names into a single connections line.
68
- */
69
- export function formatAllConnectionsCode(connectedDatabases, connectedStorage, connectedMessaging = []) {
70
- const dbVars = connectedDatabases.map((db) => getVariableName(db));
71
- const storageVars = connectedStorage.map((b) => getVariableName(b));
72
- const messagingVars = connectedMessaging.map((q) => getVariableName(q));
73
- const allVars = [...dbVars, ...storageVars, ...messagingVars].join(", ");
74
- return `connections: [${allVars}],`;
75
- }
1
+ import{getVariableName as a}from"./common.js";const u=Object.freeze({NAME:"BUCKET_NAME"});function f(r,t){const{connectedStorage:e}=t;return e?.length?r.s3.filter(n=>e.includes(n.name)):[]}function g(r){const t=[];if(r.length===1){const e=r[0];if(!e)return t;const n=a(e);t.push({key:u.NAME,expression:`${n}.getBucketName()`})}else for(const[e,n]of r.entries()){const o=a(n),s=e===0?"":`_${e+1}`;t.push({key:`${u.NAME}${s}`,expression:`${o}.getBucketName()`})}return t}function p(r,t){const e={},n={};if(!t.connectedStorage?.length)return{env:e,secrets:n};const o=f(r,t),s=g(o);for(const i of s)e[i.key]={__expression:i.expression};return{env:e,secrets:n}}function V(r,t,e=[]){const n=r.map(c=>a(c)),o=t.map(c=>a(c)),s=e.map(c=>a(c));return`connections: [${[...n,...o,...s].join(", ")}],`}export{u as STORAGE_ENV_VARS,p as buildStorageEnvVars,V as formatAllConnectionsCode,g as generateStorageEnvVarEntries,f as getConnectedStorage};
@@ -0,0 +1,21 @@
1
+ import { type Result } from "../types/Result.js";
2
+ import { type AddResourceOptions } from "./resourceAddition.js";
3
+ export interface ResourceChangeResult {
4
+ /** Original infrastructure.ts content */
5
+ readonly originalContent: string;
6
+ /** Modified infrastructure.ts content with resource added */
7
+ readonly modifiedContent: string;
8
+ /** Details of the added resource */
9
+ readonly addedResource: {
10
+ readonly type: string;
11
+ readonly name: string;
12
+ };
13
+ }
14
+ /**
15
+ * Generates a resource change by parsing existing infrastructure code,
16
+ * adding a resource to the plan, and regenerating the code.
17
+ *
18
+ * Custom code blocks in infrastructure.ts are preserved through the
19
+ * parse → modify → regenerate cycle via `extractCustomCode: true`.
20
+ */
21
+ export declare function generateResourceChange(infrastructureContent: string, appName: string, options: AddResourceOptions): Result<ResourceChangeResult, Error>;
@@ -0,0 +1 @@
1
+ import{parseInfrastructure as d,convertToResourcePlan as l}from"../ast/index.js";import{success as m,failure as n}from"../types/Result.js";import{generateInfrastructureFromPlan as f}from"./resourcePlanning.js";import{addResourceToPlan as g,validateResourceAddition as p}from"./resourceAddition.js";function h(c,i,t){let r;try{r=d(c,{extractCustomCode:!0})}catch(e){return n(new Error(`Failed to parse infrastructure.ts: ${e instanceof Error?e.message:String(e)}`))}let o;try{o=l(r,i,{skipValidation:!0})}catch(e){return n(new Error(`Failed to build resource plan: ${e instanceof Error?e.message:String(e)}`))}r.customCodeBlocks&&r.customCodeBlocks.length>0&&(o.customCodeBlocks=r.customCodeBlocks);const u=p(o,t);if(!u.success)return u;const s=g(o,t);if(!s.success)return s;const a=f(s.data);return a.success?m({originalContent:c,modifiedContent:a.data,addedResource:{type:t.type,name:t.resourceName}}):n(new Error(`Code generation failed: ${a.error.message}`))}export{h as generateResourceChange};
@@ -1 +1,4 @@
1
1
  export { planApplicationResources, type OpenNextResourceOptions, planOpenNextResources, type GenerationOptions, generateInfrastructureFromPlan, } from "./resourcePlanning.js";
2
+ export { type ConnectionType, applyComputeConnections, applyServiceConnections, } from "./resourceConnections.js";
3
+ export { type AddableResourceType, type AddResourceOptions, type DatabaseAddOptions, type ProxyAddOptions, type S3AddOptions, type NetworkAddOptions, type EcsComputeAddOptions, type LambdaComputeAddOptions, type CdnAddOptions, type TunnelAddOptions, addResourceToPlan, validateResourceAddition, listAvailableResources, } from "./resourceAddition.js";
4
+ export { type ResourceChangeResult, generateResourceChange, } from "./generateResourceChange.js";
@@ -1 +1 @@
1
- export { planApplicationResources, planOpenNextResources, generateInfrastructureFromPlan, } from "./resourcePlanning.js";
1
+ import{planApplicationResources as r,planOpenNextResources as n,generateInfrastructureFromPlan as a}from"./resourcePlanning.js";import{applyComputeConnections as p,applyServiceConnections as s}from"./resourceConnections.js";import{addResourceToPlan as l,validateResourceAddition as i,listAvailableResources as u}from"./resourceAddition.js";import{generateResourceChange as R}from"./generateResourceChange.js";export{l as addResourceToPlan,p as applyComputeConnections,s as applyServiceConnections,a as generateInfrastructureFromPlan,R as generateResourceChange,u as listAvailableResources,r as planApplicationResources,n as planOpenNextResources,i as validateResourceAddition};
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Pure resource addition functions for manipulating ApplicationResourcePlan.
3
+ * Shared by both CLI generators and webapp API routes.
4
+ * No I/O, no Tree, no prompts — pure plan manipulation.
5
+ */
6
+ import type { ApplicationResourcePlan } from "../schemas/resourceSchemas.js";
7
+ import { type Result } from "../types/Result.js";
8
+ export type AddableResourceType = "database" | "database:proxy" | "storage:s3" | "network" | "compute:ecs" | "compute:lambda" | "cdn" | "tunnel";
9
+ export type AddResourceOptions = DatabaseAddOptions | ProxyAddOptions | S3AddOptions | NetworkAddOptions | EcsComputeAddOptions | LambdaComputeAddOptions | CdnAddOptions | TunnelAddOptions;
10
+ export interface DatabaseAddOptions {
11
+ readonly type: "database";
12
+ readonly resourceName: string;
13
+ readonly databaseType: "Instance" | "Aurora" | "GlobalAurora";
14
+ readonly databaseName?: string;
15
+ readonly engine?: "postgres" | "mysql";
16
+ readonly instanceType?: string;
17
+ readonly multiAz?: boolean;
18
+ readonly readReplica?: false | {
19
+ instanceType?: string;
20
+ availabilityZone?: string;
21
+ };
22
+ readonly publiclyAccessible?: boolean;
23
+ readonly writer?: {
24
+ enableDatabaseInsights?: boolean;
25
+ identifierSuffix?: string;
26
+ availabilityZone?: string;
27
+ };
28
+ readonly readers?: false | {
29
+ instances?: Array<{
30
+ scaleWithWriter?: boolean;
31
+ enableDatabaseInsights?: boolean;
32
+ identifierSuffix?: string;
33
+ availabilityZone?: string;
34
+ }>;
35
+ };
36
+ readonly backupRetention?: number;
37
+ readonly preferredMaintenanceWindow?: string;
38
+ readonly primaryRegion?: string;
39
+ readonly secondaryRegions?: string[];
40
+ readonly connectToCompute?: string[];
41
+ readonly connectToServices?: string[];
42
+ }
43
+ export interface ProxyAddOptions {
44
+ readonly type: "database:proxy";
45
+ readonly resourceName: string;
46
+ readonly targetDatabase: string;
47
+ readonly maxConnections?: number;
48
+ readonly maxIdleConnections?: number;
49
+ readonly connectionBorrowTimeout?: number;
50
+ readonly requireTLS?: boolean;
51
+ }
52
+ export interface S3AddOptions {
53
+ readonly type: "storage:s3";
54
+ readonly resourceName: string;
55
+ readonly bucketName?: string;
56
+ readonly publicReadAccess?: boolean;
57
+ readonly websiteHosting?: boolean;
58
+ readonly versioned?: boolean;
59
+ readonly encryption?: "AES256" | "KMS";
60
+ readonly connectToCompute?: string[];
61
+ readonly connectToServices?: string[];
62
+ }
63
+ export interface NetworkAddOptions {
64
+ readonly type: "network";
65
+ readonly resourceName: string;
66
+ /** If provided, creates a named additional network. Otherwise merges into default network. */
67
+ readonly networkName?: string;
68
+ readonly maxAzs?: number;
69
+ /** NAT gateway config — false to disable, or config object */
70
+ readonly natGateways?: false | {
71
+ count?: number;
72
+ };
73
+ /** Flow logs config — false to disable, or config object */
74
+ readonly flowLogs?: false | {
75
+ destination?: "cloudwatch" | "s3";
76
+ retentionDays?: number;
77
+ };
78
+ /** VPC endpoints config — false to disable */
79
+ readonly vpcEndpoints?: false | {
80
+ gateway?: false | {
81
+ s3?: boolean;
82
+ dynamodb?: boolean;
83
+ };
84
+ interface?: false | {
85
+ ecr?: boolean;
86
+ logs?: boolean;
87
+ sts?: boolean;
88
+ };
89
+ };
90
+ }
91
+ export interface EcsComputeAddOptions {
92
+ readonly type: "compute:ecs";
93
+ readonly resourceName: string;
94
+ readonly services: ReadonlyArray<{
95
+ name: string;
96
+ port?: number;
97
+ cpu?: number;
98
+ memory?: number;
99
+ capacityProvider?: "FARGATE" | "FARGATE_SPOT" | "EC2";
100
+ }>;
101
+ readonly loadBalancer?: false | "public" | "internal";
102
+ readonly dockerfilePath?: string;
103
+ readonly connectedDatabase?: string[];
104
+ }
105
+ export interface LambdaComputeAddOptions {
106
+ readonly type: "compute:lambda";
107
+ readonly resourceName: string;
108
+ readonly timeout?: number;
109
+ readonly memory?: number;
110
+ readonly handler?: string;
111
+ readonly runtime?: string;
112
+ readonly connectedDatabase?: string[];
113
+ }
114
+ export interface CdnAddOptions {
115
+ readonly type: "cdn";
116
+ readonly resourceName: string;
117
+ readonly defaultOriginRef: string;
118
+ readonly customDomain?: string;
119
+ readonly certificateArn?: string;
120
+ readonly behaviours?: ReadonlyArray<{
121
+ pathPattern: string;
122
+ originRef: string;
123
+ cachePolicy?: "CACHING_OPTIMIZED" | "CACHING_DISABLED";
124
+ }>;
125
+ }
126
+ export interface TunnelAddOptions {
127
+ readonly type: "tunnel";
128
+ readonly resourceName: string;
129
+ readonly instanceType?: string;
130
+ }
131
+ interface AvailableResource {
132
+ readonly type: AddableResourceType;
133
+ readonly label: string;
134
+ readonly available: boolean;
135
+ readonly reason?: string;
136
+ }
137
+ /**
138
+ * Lists which resource types can be added to the given plan,
139
+ * with reasons for any unavailable types.
140
+ */
141
+ export declare function listAvailableResources(plan: ApplicationResourcePlan): AvailableResource[];
142
+ /**
143
+ * Validates that a resource can be added to the plan without actually modifying it.
144
+ */
145
+ export declare function validateResourceAddition(plan: ApplicationResourcePlan, options: AddResourceOptions): Result<void, Error>;
146
+ /**
147
+ * Adds a resource to an ApplicationResourcePlan. Returns a new plan with the
148
+ * resource added. Validates before modifying. Pure function — no I/O.
149
+ *
150
+ * For database and storage resources, optional `connectToCompute` / `connectToServices`
151
+ * fields wire the new resource to existing compute resources in the plan.
152
+ */
153
+ export declare function addResourceToPlan(plan: ApplicationResourcePlan, options: AddResourceOptions): Result<ApplicationResourcePlan, Error>;
154
+ export {};
@@ -0,0 +1 @@
1
+ import{success as d,failure as u}from"../types/Result.js";import{toPascalCase as l}from"../generation/common.js";import{applyComputeConnections as b,applyServiceConnections as o}from"./resourceConnections.js";function $(a){const e=a.database.length>0,n=a.cdn!==void 0;return[{type:"database",label:"Database (RDS)",available:!0},{type:"database:proxy",label:"RDS Proxy",available:e,reason:e?void 0:"Requires at least one database"},{type:"storage:s3",label:"S3 Bucket",available:!0},{type:"network",label:"Network (VPC)",available:!0},{type:"compute:ecs",label:"Compute (ECS)",available:!0},{type:"compute:lambda",label:"Compute (Lambda)",available:!0},{type:"cdn",label:"CDN (CloudFront)",available:!n,reason:n?"A CDN is already configured. Remove existing CDN first.":void 0},{type:"tunnel",label:"Database Tunnel",available:e&&!a.tunnel,reason:e?a.tunnel?"Tunnel is already enabled":void 0:"Requires at least one database"}]}function y(a,e){if(E(a,e.resourceName))return u(new Error(`Resource name '${e.resourceName}' is already used in this application.`));switch(e.type){case"database":return w(a,e);case"database:proxy":return g(a,e);case"storage:s3":return d(void 0);case"network":return d(void 0);case"compute:ecs":return!e.services||e.services.length===0?u(new Error("ECS compute requires at least one service.")):d(void 0);case"compute:lambda":return d(void 0);case"cdn":return v(a,e);case"tunnel":return h(a);default:return e}}function w(a,e){const n=e.databaseName??e.resourceName.toLowerCase();return a.database.some(t=>t.databaseName===n)?u(new Error(`Database name '${n}' already exists. Choose a different database name.`)):e.databaseType==="GlobalAurora"&&!e.primaryRegion?u(new Error("GlobalAurora databases require a primaryRegion.")):d(void 0)}function g(a,e){const n=a.database.find(r=>r.name===e.targetDatabase||r.databaseName===e.targetDatabase);if(!n){const r=a.database.map(t=>t.name).join(", ")||"none";return u(new Error(`Database '${e.targetDatabase}' not found. Available databases: ${r}`))}return n.proxy!==void 0&&n.proxy!==!1?u(new Error(`Database '${n.name}' already has RDS Proxy enabled.`)):d(void 0)}function v(a,e){if(a.cdn)return u(new Error("A CDN is already configured for this application. Remove the existing CDN first."));if(!m(a,e.defaultOriginRef))return u(new Error(`Origin '${e.defaultOriginRef}' not found in plan. CDN origin must reference an existing resource.`));if(e.behaviours){for(const n of e.behaviours)if(!m(a,n.originRef))return u(new Error(`Behaviour origin '${n.originRef}' not found in plan.`))}return d(void 0)}function h(a){return a.tunnel&&typeof a.tunnel=="object"?u(new Error("Tunnel is already enabled for this application.")):a.database.length===0?u(new Error("No databases found \u2014 tunnel requires at least one relational database.")):d(void 0)}function q(a,e){const n=y(a,e);if(!n.success)return n;switch(e.type){case"database":return C(a,e);case"database:proxy":return R(a,e);case"storage:s3":return x(a,e);case"network":return D(a,e);case"compute:ecs":return k(a,e);case"compute:lambda":return N(a,e);case"cdn":return A(a,e);case"tunnel":return T(a,e);default:return e}}function C(a,e){const n=e.databaseName??e.resourceName.toLowerCase(),r={name:e.resourceName,type:e.databaseType,databaseName:n};let t;switch(e.databaseType){case"Instance":t={...r,...e.instanceType!==void 0?{instanceType:e.instanceType}:{},...e.multiAz!==void 0?{multiAz:e.multiAz}:{},...e.readReplica!==void 0?{readReplica:e.readReplica}:{},...e.publiclyAccessible!==void 0?{publiclyAccessible:e.publiclyAccessible}:{},...e.backupRetention!==void 0?{backupRetention:e.backupRetention}:{}};break;case"Aurora":t={...r,...e.writer!==void 0?{writer:e.writer}:{},...e.readers!==void 0?{readers:e.readers}:{},...e.backupRetention!==void 0?{backupRetention:e.backupRetention}:{},...e.preferredMaintenanceWindow!==void 0?{preferredMaintenanceWindow:e.preferredMaintenanceWindow}:{}};break;case"GlobalAurora":t={...r,primaryRegion:e.primaryRegion,...e.secondaryRegions!==void 0?{secondaryRegions:e.secondaryRegions}:{}};break;default:{const i=e.databaseType;return u(new Error(`Unknown database type: ${String(i)}`))}}const c={...a,database:[...a.database,t]};return f(c,e.resourceName,"database",e),d(c)}function R(a,e){const n=a.database.map(c=>({...c})),r=n.find(c=>c.name===e.targetDatabase||c.databaseName===e.targetDatabase);if(!r)return u(new Error(`Database '${e.targetDatabase}' not found.`));const t={};return e.maxConnections!==void 0&&(t.maxConnections=e.maxConnections),e.maxIdleConnections!==void 0&&(t.maxIdleConnections=e.maxIdleConnections),e.connectionBorrowTimeout!==void 0&&(t.connectionBorrowTimeout=e.connectionBorrowTimeout),e.requireTLS!==void 0&&(t.requireTLS=e.requireTLS),r.proxy=Object.keys(t).length>0?t:{},d({...a,database:n})}function x(a,e){const n=e.bucketName??e.resourceName.toLowerCase(),r=l(n),t=a.s3.length,c=t===0?"":`${t+1}`,i={name:r,bucketName:n,variableName:`bucket${c}`,...e.publicReadAccess!==void 0?{publicReadAccess:e.publicReadAccess}:{},...e.versioned!==void 0?{versioned:e.versioned}:{},...e.encryption!==void 0?{encryption:e.encryption}:{},...e.websiteHosting?{websiteHosting:{indexDocument:"index.html"}}:{}},s={...a,s3:[...a.s3,i]};return f(s,r,"storage",e),d(s)}function D(a,e){const n={};if(e.maxAzs!==void 0&&(n.maxAzs=e.maxAzs),e.natGateways!==void 0&&(n.natGateways=e.natGateways),e.flowLogs!==void 0&&(n.flowLogs=e.flowLogs),e.vpcEndpoints!==void 0&&(n.vpcEndpoints=e.vpcEndpoints),e.networkName){const r={name:e.networkName,...n};return d({...a,additionalNetworks:[...a.additionalNetworks??[],r]})}return d({...a,network:{...a.network??{},...n}})}function k(a,e){const n=e.connectedDatabase??[],r={name:e.resourceName,type:"ecs",needsConnection:n.length>0,connectedDatabase:n,...e.loadBalancer!==void 0?{cluster:{loadBalancer:e.loadBalancer}}:{},services:e.services.map(t=>({name:t.name,capacityProvider:t.capacityProvider??"FARGATE",...t.port!==void 0?{containers:[{port:t.port}]}:{},...t.cpu!==void 0?{cpu:t.cpu}:{},...t.memory!==void 0?{memory:t.memory}:{}})),dockerfilePath:e.dockerfilePath};return d({...a,compute:[...a.compute,r]})}function N(a,e){const n=e.connectedDatabase??[],r={name:e.resourceName,type:"lambda",needsConnection:n.length>0,connectedDatabase:n,...e.timeout!==void 0?{timeout:e.timeout}:{},...e.memory!==void 0?{memory:e.memory}:{},...e.handler!==void 0?{handler:e.handler}:{},...e.runtime!==void 0?{runtime:e.runtime}:{}};return d({...a,compute:[...a.compute,r]})}function A(a,e){const n={name:e.resourceName,defaultOriginRef:e.defaultOriginRef,...e.customDomain!==void 0?{customDomain:e.customDomain}:{},...e.certificateArn!==void 0?{certificateArn:e.certificateArn}:{},...e.behaviours&&e.behaviours.length>0?{behaviours:[...e.behaviours].map(r=>({pathPattern:r.pathPattern,originRef:r.originRef,...r.cachePolicy!==void 0?{cachePolicy:r.cachePolicy}:{}}))}:{}};return d({...a,cdn:n})}function T(a,e){const n=e.instanceType?{instanceType:e.instanceType}:{};return d({...a,tunnel:n})}function E(a,e){const n=[...a.database.map(r=>r.name),...a.s3.map(r=>r.name),...a.compute.map(r=>r.name),...(a.dynamodb??[]).map(r=>r.name),...(a.sqs??[]).map(r=>r.name)];return a.cdn&&n.push(a.cdn.name),n.includes(e)}function m(a,e){return a.database.some(n=>n.name===e)||a.s3.some(n=>n.name===e)||a.compute.some(n=>n.name===e)||(a.dynamodb??[]).some(n=>n.name===e)||(a.sqs??[]).some(n=>n.name===e)}function f(a,e,n,r){r.connectToCompute&&r.connectToCompute.length>0&&b(a,[...r.connectToCompute],e,n),r.connectToServices&&r.connectToServices.length>0&&o(a,[...r.connectToServices],e,n)}export{q as addResourceToPlan,$ as listAvailableResources,y as validateResourceAddition};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Pure resource connection functions extracted from CLI connectionHelpers.
3
+ * These wire new resources (databases, S3 buckets) to existing compute resources
4
+ * in the application resource plan. No I/O, no prompts — pure plan manipulation.
5
+ */
6
+ import type { ApplicationResourcePlan } from "../schemas/resourceSchemas.js";
7
+ export type ConnectionType = "database" | "storage";
8
+ /**
9
+ * Apply connections at the service level within ECS clusters.
10
+ * Service references use the format "ClusterName/ServiceName".
11
+ * Mutates the plan in place (same as CLI pattern).
12
+ */
13
+ export declare function applyServiceConnections(currentPlan: ApplicationResourcePlan, connectToServices: string[], resourceName: string, type: ConnectionType): void;
14
+ /**
15
+ * Apply connections at the compute resource level.
16
+ * Flags all services within matched compute resources.
17
+ * Mutates the plan in place (same as CLI pattern).
18
+ */
19
+ export declare function applyComputeConnections(currentPlan: ApplicationResourcePlan, connectToCompute: string[], resourceName: string, type: ConnectionType): void;
@@ -0,0 +1 @@
1
+ function u(e){const n=e.split("/");if(n.length!==2||!n[0]||!n[1])throw new Error(`Invalid service reference format: '${e}'. Expected 'ClusterName/ServiceName'.`);return n[0]}function f(e,n,o){o==="database"?(e.needsConnection=!0,e.connectedDatabase=[...new Set([...e.connectedDatabase??[],n])]):e.connectedStorage=[...new Set([...e.connectedStorage??[],n])]}function l(e,n,o,s){const t=new Set(n),a=[...new Set(n.map(u))];for(const i of a){const r=e.compute.find(c=>c.name===i);if(r?.services){f(r,o,s);for(const c of r.services){const d=`${i}/${c.name}`;t.has(d)&&(s==="database"?c.needsDatabaseConnection=!0:c.needsStorageConnection=!0)}}}}function v(e,n,o,s){for(const t of e.compute)if(n.includes(t.name)&&(f(t,o,s),t.services))for(const a of t.services)s==="database"?a.needsDatabaseConnection=!0:a.needsStorageConnection=!0}export{v as applyComputeConnections,l as applyServiceConnections};