@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,224 +1,2 @@
1
- import { success, failure } from "../types/Result.js";
2
- import { DnsRecordTypeSchema, BIND_DIRECTIVES, ALIAS_PREFIX } from "./types.js";
3
- const RECORD_TYPES = new Set(DnsRecordTypeSchema.options);
4
- function isRecordType(token) {
5
- return RECORD_TYPES.has(token.toUpperCase());
6
- }
7
- function stripTrailingDot(value) {
8
- return value.endsWith(".") ? value.slice(0, -1) : value;
9
- }
10
- function stripInlineComment(line) {
11
- let inQuote = false;
12
- for (let i = 0; i < line.length; i++) {
13
- if (line[i] === '"') {
14
- inQuote = !inQuote;
15
- }
16
- else if (line[i] === ";" && !inQuote) {
17
- return line.slice(0, i).trimEnd();
18
- }
19
- }
20
- return line;
21
- }
22
- function parseTxtValue(tokens, startIndex) {
23
- const raw = tokens.slice(startIndex).join(" ");
24
- const parts = [];
25
- let current = "";
26
- let inQuote = false;
27
- for (const ch of raw) {
28
- if (ch === '"') {
29
- if (inQuote) {
30
- parts.push(current);
31
- current = "";
32
- }
33
- inQuote = !inQuote;
34
- }
35
- else if (inQuote) {
36
- current += ch;
37
- }
38
- }
39
- return parts.join(" ");
40
- }
41
- function parseDelegateDirective(line) {
42
- const auto = /;\s*auto\s*$/.test(line);
43
- const cleaned = line.replace(/;\s*auto\s*$/, "").trim();
44
- const match = cleaned.match(/^\$FJALL-DELEGATE\s+(\S+)\s+TO\s+(\S+)$/i);
45
- if (!match)
46
- return undefined;
47
- return { subdomain: match[1], targetAccount: match[2], auto };
48
- }
49
- function parseCertDirective(tokens) {
50
- if (tokens.length < 2)
51
- return undefined;
52
- const domainName = tokens[1];
53
- const sans = tokens.slice(2);
54
- return { domainName, subjectAlternativeNames: sans };
55
- }
56
- function tokenise(line) {
57
- const tokens = [];
58
- let current = "";
59
- let inQuote = false;
60
- for (const ch of line) {
61
- if (ch === '"') {
62
- inQuote = !inQuote;
63
- current += ch;
64
- }
65
- else if (/\s/.test(ch) && !inQuote) {
66
- if (current.length > 0) {
67
- tokens.push(current);
68
- current = "";
69
- }
70
- }
71
- else {
72
- current += ch;
73
- }
74
- }
75
- if (current.length > 0)
76
- tokens.push(current);
77
- return tokens;
78
- }
79
- function parseRecord(tokens) {
80
- if (tokens.length < 2)
81
- return undefined;
82
- let idx = 0;
83
- const name = tokens[idx++];
84
- let ttl;
85
- let type;
86
- // Next token could be TTL, class (IN), or record type
87
- while (idx < tokens.length && type === undefined) {
88
- const upper = tokens[idx].toUpperCase();
89
- if (upper === "IN") {
90
- idx++;
91
- }
92
- else if (isRecordType(upper)) {
93
- type = upper;
94
- idx++;
95
- }
96
- else if (/^\d+$/.test(tokens[idx])) {
97
- ttl = parseInt(tokens[idx], 10);
98
- idx++;
99
- }
100
- else {
101
- return undefined;
102
- }
103
- }
104
- if (!type)
105
- return undefined;
106
- const remaining = tokens.slice(idx);
107
- switch (type) {
108
- case "MX": {
109
- if (remaining.length < 2)
110
- return undefined;
111
- const priority = parseInt(remaining[0], 10);
112
- const value = remaining.slice(1).join(" ");
113
- return { name, type, ttl, value, priority };
114
- }
115
- case "SRV": {
116
- if (remaining.length < 4)
117
- return undefined;
118
- const priority = parseInt(remaining[0], 10);
119
- const weight = parseInt(remaining[1], 10);
120
- const port = parseInt(remaining[2], 10);
121
- const value = remaining[3];
122
- return { name, type, ttl, value, priority, weight, port };
123
- }
124
- case "TXT": {
125
- const value = parseTxtValue(tokens, idx);
126
- return { name, type, ttl, value };
127
- }
128
- case "CAA": {
129
- const value = remaining.join(" ");
130
- return { name, type, ttl, value };
131
- }
132
- case "A": {
133
- if (remaining.length === 0)
134
- return undefined;
135
- const firstToken = remaining[0];
136
- if (firstToken.toUpperCase() === "ALIAS") {
137
- if (remaining.length < 2)
138
- return undefined;
139
- const value = remaining.slice(1).join(" ");
140
- return { name, type, ttl, value: `${ALIAS_PREFIX}${value}` };
141
- }
142
- return { name, type, ttl, value: remaining[0] };
143
- }
144
- default: {
145
- if (remaining.length === 0)
146
- return undefined;
147
- return { name, type, ttl, value: remaining[0] };
148
- }
149
- }
150
- }
151
- export function parseZoneFile(content) {
152
- const lines = content.split("\n");
153
- let origin;
154
- let ttl = 300;
155
- const records = [];
156
- const delegations = [];
157
- const certificates = [];
158
- for (const rawLine of lines) {
159
- const trimmed = rawLine.trim();
160
- if (trimmed === "" || trimmed.startsWith(";"))
161
- continue;
162
- if (trimmed.toUpperCase().startsWith(BIND_DIRECTIVES.FJALL_DELEGATE)) {
163
- const directive = parseDelegateDirective(trimmed);
164
- if (directive) {
165
- delegations.push(directive);
166
- }
167
- continue;
168
- }
169
- const stripped = stripInlineComment(trimmed);
170
- if (stripped === "")
171
- continue;
172
- const tokens = tokenise(stripped);
173
- if (tokens.length === 0)
174
- continue;
175
- const directive = tokens[0].toUpperCase();
176
- if (directive === BIND_DIRECTIVES.ORIGIN) {
177
- if (tokens.length < 2)
178
- continue;
179
- origin = stripTrailingDot(tokens[1]);
180
- continue;
181
- }
182
- if (directive === BIND_DIRECTIVES.TTL) {
183
- if (tokens.length < 2)
184
- continue;
185
- ttl = parseInt(tokens[1], 10);
186
- continue;
187
- }
188
- if (directive === BIND_DIRECTIVES.FJALL_CERT) {
189
- const cert = parseCertDirective(tokens);
190
- if (cert) {
191
- certificates.push(cert);
192
- }
193
- continue;
194
- }
195
- const record = parseRecord(tokens);
196
- if (record) {
197
- records.push(record);
198
- }
199
- }
200
- if (!origin) {
201
- return failure(new Error("Missing $ORIGIN directive"));
202
- }
203
- return success({ origin, ttl, records, delegations, certificates });
204
- }
205
- /**
206
- * Resolve an ALIAS value (e.g. "fjall:ecs:my-app") to a target DNS name
207
- * using a map of app outputs. Returns undefined if the alias cannot be resolved.
208
- *
209
- * Alias format: fjall:<type>:<name>
210
- * - fjall:ecs:<name> — resolves to ALB DNS name
211
- * - fjall:cdn:<name> — resolves to CloudFront domain name
212
- * - fjall:s3:<name> — resolves to S3 website endpoint
213
- */
214
- export function resolveAlias(alias, appOutputs) {
215
- if (!alias.startsWith("fjall:"))
216
- return undefined;
217
- const parts = alias.split(":");
218
- if (parts.length < 3)
219
- return undefined;
220
- const resourceType = parts[1];
221
- const resourceName = parts[2];
222
- const lookupKey = `${resourceType}:${resourceName}`;
223
- return appOutputs.get(lookupKey) ?? appOutputs.get(alias);
224
- }
1
+ import{success as g,failure as m}from"../types/Result.js";import{DnsRecordTypeSchema as I,BIND_DIRECTIVES as p,ALIAS_PREFIX as T}from"./types.js";const A=new Set(I.options);function E(e){return A.has(e.toUpperCase())}function v(e){return e.endsWith(".")?e.slice(0,-1):e}function L(e){let n=!1;for(let t=0;t<e.length;t++)if(e[t]==='"')n=!n;else if(e[t]===";"&&!n)return e.slice(0,t).trimEnd();return e}function C(e,n){const t=e.slice(n).join(" "),s=[];let i="",r=!1;for(const o of t)o==='"'?(r&&(s.push(i),i=""),r=!r):r&&(i+=o);return s.join(" ")}function R(e){const n=/;\s*auto\s*$/.test(e),s=e.replace(/;\s*auto\s*$/,"").trim().match(/^\$FJALL-DELEGATE\s+(\S+)\s+TO\s+(\S+)$/i);if(s)return{subdomain:s[1],targetAccount:s[2],auto:n}}function D(e){if(e.length<2)return;const n=e[1],t=e.slice(2);return{domainName:n,subjectAlternativeNames:t}}function $(e){const n=[];let t="",s=!1;for(const i of e)i==='"'?(s=!s,t+=i):/\s/.test(i)&&!s?t.length>0&&(n.push(t),t=""):t+=i;return t.length>0&&n.push(t),n}function S(e){if(e.length<2)return;let n=0;const t=e[n++];let s,i;for(;n<e.length&&i===void 0;){const o=e[n].toUpperCase();if(o==="IN")n++;else if(E(o))i=o,n++;else if(/^\d+$/.test(e[n]))s=parseInt(e[n],10),n++;else return}if(!i)return;const r=e.slice(n);switch(i){case"MX":{if(r.length<2)return;const o=parseInt(r[0],10),u=r.slice(1).join(" ");return{name:t,type:i,ttl:s,value:u,priority:o}}case"SRV":{if(r.length<4)return;const o=parseInt(r[0],10),u=parseInt(r[1],10),f=parseInt(r[2],10),a=r[3];return{name:t,type:i,ttl:s,value:a,priority:o,weight:u,port:f}}case"TXT":{const o=C(e,n);return{name:t,type:i,ttl:s,value:o}}case"CAA":{const o=r.join(" ");return{name:t,type:i,ttl:s,value:o}}case"A":{if(r.length===0)return;if(r[0].toUpperCase()==="ALIAS"){if(r.length<2)return;const u=r.slice(1).join(" ");return{name:t,type:i,ttl:s,value:`${T}${u}`}}return{name:t,type:i,ttl:s,value:r[0]}}default:return r.length===0?void 0:{name:t,type:i,ttl:s,value:r[0]}}}function y(e){const n=e.split(`
2
+ `);let t,s=300;const i=[],r=[],o=[];for(const u of n){const f=u.trim();if(f===""||f.startsWith(";"))continue;if(f.toUpperCase().startsWith(p.FJALL_DELEGATE)){const l=R(f);l&&r.push(l);continue}const a=L(f);if(a==="")continue;const c=$(a);if(c.length===0)continue;const d=c[0].toUpperCase();if(d===p.ORIGIN){if(c.length<2)continue;t=v(c[1]);continue}if(d===p.TTL){if(c.length<2)continue;s=parseInt(c[1],10);continue}if(d===p.FJALL_CERT){const l=D(c);l&&o.push(l);continue}const h=S(c);h&&i.push(h)}return t?g({origin:t,ttl:s,records:i,delegations:r,certificates:o}):m(new Error("Missing $ORIGIN directive"))}function N(e,n){if(!e.startsWith("fjall:"))return;const t=e.split(":");if(t.length<3)return;const s=t[1],i=t[2],r=`${s}:${i}`;return n.get(r)??n.get(e)}export{y as parseZoneFile,N as resolveAlias};
@@ -1,52 +1,3 @@
1
- import { BIND_DIRECTIVES, ALIAS_PREFIX } from "./types.js";
2
- function padRight(value, width) {
3
- return value.length >= width
4
- ? value
5
- : value + " ".repeat(width - value.length);
6
- }
7
- function formatRecord(record, defaultTtl) {
8
- const nameCol = padRight(record.name, 24);
9
- const hasTtl = record.ttl !== undefined && record.ttl !== defaultTtl;
10
- const ttlStr = hasTtl ? `${record.ttl} ` : "";
11
- switch (record.type) {
12
- case "MX":
13
- return `${nameCol} ${ttlStr}IN MX ${record.priority ?? 10} ${record.value}`;
14
- case "SRV":
15
- return `${nameCol} ${ttlStr}IN SRV ${record.priority ?? 0} ${record.weight ?? 0} ${record.port ?? 0} ${record.value}`;
16
- case "TXT":
17
- return `${nameCol} ${ttlStr}IN TXT "${record.value}"`;
18
- case "CAA":
19
- return `${nameCol} ${ttlStr}IN CAA ${record.value}`;
20
- default: {
21
- if (record.type === "A" && record.value.startsWith(ALIAS_PREFIX)) {
22
- return `${nameCol} ${ttlStr}IN A ${record.value}`;
23
- }
24
- return `${nameCol} ${ttlStr}IN ${record.type} ${record.value}`;
25
- }
26
- }
27
- }
28
- export function generateZoneFile(zone) {
29
- const lines = [];
30
- lines.push("; zone.bind — managed by fjall");
31
- lines.push(`${BIND_DIRECTIVES.ORIGIN} ${zone.origin}.`);
32
- lines.push(`${BIND_DIRECTIVES.TTL} ${zone.ttl}`);
33
- lines.push("");
34
- if (zone.certificates.length > 0) {
35
- for (const cert of zone.certificates) {
36
- const parts = [cert.domainName, ...cert.subjectAlternativeNames];
37
- lines.push(`${BIND_DIRECTIVES.FJALL_CERT} ${parts.join(" ")}`);
38
- }
39
- lines.push("");
40
- }
41
- if (zone.delegations.length > 0) {
42
- for (const delegation of zone.delegations) {
43
- const suffix = delegation.auto ? " ; auto" : "";
44
- lines.push(`${BIND_DIRECTIVES.FJALL_DELEGATE} ${delegation.subdomain} TO ${delegation.targetAccount}${suffix}`);
45
- }
46
- lines.push("");
47
- }
48
- for (const record of zone.records) {
49
- lines.push(formatRecord(record, zone.ttl));
50
- }
51
- return lines.join("\n") + "\n";
52
- }
1
+ import{BIND_DIRECTIVES as $,ALIAS_PREFIX as i}from"./types.js";function u(t,n){return t.length>=n?t:t+" ".repeat(n-t.length)}function l(t,n){const s=u(t.name,24),e=t.ttl!==void 0&&t.ttl!==n?`${t.ttl} `:"";switch(t.type){case"MX":return`${s} ${e}IN MX ${t.priority??10} ${t.value}`;case"SRV":return`${s} ${e}IN SRV ${t.priority??0} ${t.weight??0} ${t.port??0} ${t.value}`;case"TXT":return`${s} ${e}IN TXT "${t.value}"`;case"CAA":return`${s} ${e}IN CAA ${t.value}`;default:return t.type==="A"&&t.value.startsWith(i)?`${s} ${e}IN A ${t.value}`:`${s} ${e}IN ${t.type} ${t.value}`}}function p(t){const n=[];if(n.push("; zone.bind \u2014 managed by fjall"),n.push(`${$.ORIGIN} ${t.origin}.`),n.push(`${$.TTL} ${t.ttl}`),n.push(""),t.certificates.length>0){for(const s of t.certificates){const a=[s.domainName,...s.subjectAlternativeNames];n.push(`${$.FJALL_CERT} ${a.join(" ")}`)}n.push("")}if(t.delegations.length>0){for(const s of t.delegations){const a=s.auto?" ; auto":"";n.push(`${$.FJALL_DELEGATE} ${s.subdomain} TO ${s.targetAccount}${a}`)}n.push("")}for(const s of t.records)n.push(l(s,t.ttl));return n.join(`
2
+ `)+`
3
+ `}export{p as generateZoneFile};
@@ -0,0 +1,20 @@
1
+ import type { DnsRecord } from "./types.js";
2
+ export interface GeneratedFile {
3
+ /** Path relative to the repo root (e.g. "fjall/domains/example.com/zone.bind") */
4
+ readonly path: string;
5
+ /** File content as a string */
6
+ readonly content: string;
7
+ }
8
+ export interface DomainGenerationOptions {
9
+ /** The domain name (e.g. "example.com") */
10
+ readonly domainName: string;
11
+ /** AWS hosted zone ID — set when importing an existing Route53 zone */
12
+ readonly hostedZoneId?: string;
13
+ /** Pre-populated DNS records (e.g. from Route53 import) */
14
+ readonly records?: DnsRecord[];
15
+ }
16
+ /**
17
+ * Generates all files for a domain component as pure strings.
18
+ * Returns a `GeneratedFile[]` ready for the GitHub PR pipeline.
19
+ */
20
+ export declare function generateDomainFiles(options: DomainGenerationOptions): GeneratedFile[];
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Pure domain file generation — replaces CLI's EJS template + copySharedFiles approach.
3
+ * Produces all files needed for a domain component as string arrays.
4
+ */
5
+ import { generateZoneFile } from "./bindWriter.js";
6
+ import { toPascalCase } from "../generation/common.js";
7
+ import { GENERATOR_VERSION } from "../version.js";
8
+ /**
9
+ * Generates all files for a domain component as pure strings.
10
+ * Returns a `GeneratedFile[]` ready for the GitHub PR pipeline.
11
+ */
12
+ export function generateDomainFiles(options) {
13
+ const { domainName, hostedZoneId, records = [] } = options;
14
+ const basePath = `fjall/domains/${domainName}`;
15
+ const zone = {
16
+ origin: domainName,
17
+ ttl: 300,
18
+ records,
19
+ delegations: [],
20
+ certificates: [{ domainName, subjectAlternativeNames: [] }],
21
+ };
22
+ return [
23
+ {
24
+ path: `${basePath}/zone.bind`,
25
+ content: records.length > 0
26
+ ? generateZoneFile(zone)
27
+ : generateDefaultZoneFile(domainName),
28
+ },
29
+ {
30
+ path: `${basePath}/infrastructure.ts`,
31
+ content: generateDefaultInfrastructureTs({
32
+ domainName,
33
+ records,
34
+ hostedZoneId,
35
+ }),
36
+ },
37
+ {
38
+ path: `${basePath}/package.json`,
39
+ content: generateDomainPackageJson(domainName),
40
+ },
41
+ {
42
+ path: `${basePath}/tsconfig.json`,
43
+ content: TSCONFIG_CONTENT,
44
+ },
45
+ {
46
+ path: `${basePath}/cdk.json`,
47
+ content: CDK_JSON_CONTENT,
48
+ },
49
+ {
50
+ path: `${basePath}/.gitignore`,
51
+ content: GITIGNORE_CONTENT,
52
+ },
53
+ ];
54
+ }
55
+ // Lightweight default-scaffold emitter for the webapp PR pipeline.
56
+ // The CLI uses the richer `tsEmitter` in `@fjall/cli` for import-flow output.
57
+ function generateDefaultInfrastructureTs(args) {
58
+ const { domainName, records, hostedZoneId } = args;
59
+ const safeZoneName = toPascalCase(domainName.split(".").join(""));
60
+ const recordLines = records.map((r) => {
61
+ const ttl = r.ttl !== undefined ? `, ttl: ${r.ttl}` : "";
62
+ return ` { type: ${JSON.stringify(r.type)}, name: ${JSON.stringify(r.name)}, value: ${JSON.stringify(r.value)}${ttl} },`;
63
+ });
64
+ const recordsBlock = recordLines.length > 0 ? `[\n${recordLines.join("\n")}\n ]` : "[]";
65
+ const hostedZoneLine = hostedZoneId
66
+ ? ` hostedZoneId: ${JSON.stringify(hostedZoneId)},\n`
67
+ : "";
68
+ return `import {
69
+ App,
70
+ Domain,
71
+ } from "@fjall/components-infrastructure";
72
+
73
+ // Fjall-managed domain — edit this file to add or change records.
74
+ // See docs/domains.md for the Domain construct API.
75
+
76
+ const app = App.getApp(${JSON.stringify(`${safeZoneName}Domain`)});
77
+
78
+ new Domain(app, ${JSON.stringify(safeZoneName)}, {
79
+ registrar: "route53",
80
+ zoneName: ${JSON.stringify(domainName)},
81
+ ${hostedZoneLine} certificates: [
82
+ { domainName: ${JSON.stringify(domainName)} },
83
+ ],
84
+ records: ${recordsBlock},
85
+ });
86
+ `;
87
+ }
88
+ // ─── File content generators ────────────────────────────────────────────────
89
+ function generateDefaultZoneFile(domainName) {
90
+ return `; zone.bind — advisory copy only (Fjall reads infrastructure.ts)
91
+ ; This file is written at import time with --preserve-bind; edit infrastructure.ts to change records
92
+ $ORIGIN ${domainName}.
93
+ $TTL 300
94
+
95
+ ; Certificate for this domain
96
+ $FJALL-CERT ${domainName}
97
+
98
+ ; Add your DNS records below
99
+ ; Examples:
100
+ ; @ IN A ALIAS fjall:ecs:my-app
101
+ ; www IN CNAME ${domainName}.
102
+ ; api IN A ALIAS fjall:ecs:my-api
103
+ ; @ IN MX 10 mail.${domainName}.
104
+ `;
105
+ }
106
+ function generateDomainPackageJson(domainName) {
107
+ return (JSON.stringify({
108
+ name: `domain-${domainName}`,
109
+ version: GENERATOR_VERSION,
110
+ scripts: {},
111
+ devDependencies: {
112
+ "@types/node": "20.4.9",
113
+ "ts-node": "^10.9.1",
114
+ typescript: "~5.1.6",
115
+ },
116
+ dependencies: {
117
+ "@fjall/components-infrastructure": `^${GENERATOR_VERSION}`,
118
+ "aws-cdk-lib": "^2.239.0",
119
+ constructs: "^10.0.0",
120
+ },
121
+ }, null, 2) + "\n");
122
+ }
123
+ const GITIGNORE_CONTENT = `node_modules/
124
+ cdk.out/
125
+ *.d.ts
126
+ *.js
127
+ .deploy-state.json
128
+ .deploy-state.json.tmp
129
+ `;
130
+ const TSCONFIG_CONTENT = JSON.stringify({
131
+ compilerOptions: {
132
+ target: "ES2020",
133
+ module: "commonjs",
134
+ lib: ["es2020", "dom"],
135
+ declaration: true,
136
+ strict: true,
137
+ noImplicitAny: true,
138
+ strictNullChecks: true,
139
+ noImplicitThis: true,
140
+ alwaysStrict: true,
141
+ noUnusedLocals: false,
142
+ noUnusedParameters: false,
143
+ noImplicitReturns: true,
144
+ noFallthroughCasesInSwitch: false,
145
+ inlineSourceMap: true,
146
+ inlineSources: true,
147
+ experimentalDecorators: true,
148
+ strictPropertyInitialization: false,
149
+ typeRoots: ["./node_modules/@types"],
150
+ },
151
+ exclude: ["node_modules", "cdk.out"],
152
+ }, null, 2) + "\n";
153
+ const CDK_JSON_CONTENT = JSON.stringify({
154
+ app: "npx ts-node --prefer-ts-exts --transpile-only infrastructure.ts",
155
+ assetMetadata: false,
156
+ pathMetadata: false,
157
+ versionReporting: false,
158
+ watch: {
159
+ include: ["**"],
160
+ exclude: [
161
+ "README.md",
162
+ "cdk*.json",
163
+ "**/*.d.ts",
164
+ "**/*.js",
165
+ "tsconfig.json",
166
+ "package*.json",
167
+ "yarn.lock",
168
+ "node_modules",
169
+ "test",
170
+ ],
171
+ },
172
+ context: {
173
+ "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
174
+ "@aws-cdk/core:checkSecretUsage": true,
175
+ "@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
176
+ "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
177
+ "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
178
+ "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
179
+ "@aws-cdk/aws-iam:minimizePolicies": true,
180
+ "@aws-cdk/core:validateSnapshotRemovalPolicy": true,
181
+ "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
182
+ "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
183
+ "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
184
+ "@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
185
+ "@aws-cdk/core:enablePartitionLiterals": true,
186
+ "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
187
+ "@aws-cdk/aws-iam:standardizedServicePrincipals": true,
188
+ "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
189
+ "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
190
+ "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
191
+ "@aws-cdk/aws-route53-patters:useCertificate": true,
192
+ "@aws-cdk/customresources:installLatestAwsSdkDefault": false,
193
+ "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
194
+ "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
195
+ "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
196
+ "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
197
+ "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
198
+ "@aws-cdk/aws-redshift:columnId": true,
199
+ "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
200
+ "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
201
+ "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
202
+ "@aws-cdk/aws-kms:aliasNameRef": true,
203
+ "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
204
+ "@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
205
+ "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
206
+ },
207
+ }, null, 2) + "\n";
@@ -0,0 +1,164 @@
1
+ import { z } from "zod";
2
+ export declare const TypedFjallTargetSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
3
+ kind: z.ZodLiteral<"ecs">;
4
+ appName: z.ZodString;
5
+ }, z.core.$strict>, z.ZodObject<{
6
+ kind: z.ZodLiteral<"cdn">;
7
+ appName: z.ZodString;
8
+ }, z.core.$strict>, z.ZodObject<{
9
+ kind: z.ZodLiteral<"bucket">;
10
+ bucketName: z.ZodString;
11
+ }, z.core.$strict>, z.ZodObject<{
12
+ kind: z.ZodLiteral<"custom">;
13
+ dnsName: z.ZodString;
14
+ hostedZoneId: z.ZodString;
15
+ }, z.core.$strict>], "kind">;
16
+ export declare const ParsedStandardRecordSchema: z.ZodObject<{
17
+ kind: z.ZodLiteral<"standard">;
18
+ type: z.ZodEnum<{
19
+ A: "A";
20
+ AAAA: "AAAA";
21
+ CNAME: "CNAME";
22
+ MX: "MX";
23
+ TXT: "TXT";
24
+ NS: "NS";
25
+ SRV: "SRV";
26
+ CAA: "CAA";
27
+ }>;
28
+ name: z.ZodString;
29
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
30
+ ttl: z.ZodOptional<z.ZodNumber>;
31
+ }, z.core.$strict>;
32
+ export declare const ParsedAliasRecordSchema: z.ZodObject<{
33
+ kind: z.ZodLiteral<"alias">;
34
+ type: z.ZodEnum<{
35
+ A: "A";
36
+ AAAA: "AAAA";
37
+ }>;
38
+ name: z.ZodString;
39
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
40
+ kind: z.ZodLiteral<"ecs">;
41
+ appName: z.ZodString;
42
+ }, z.core.$strict>, z.ZodObject<{
43
+ kind: z.ZodLiteral<"cdn">;
44
+ appName: z.ZodString;
45
+ }, z.core.$strict>, z.ZodObject<{
46
+ kind: z.ZodLiteral<"bucket">;
47
+ bucketName: z.ZodString;
48
+ }, z.core.$strict>, z.ZodObject<{
49
+ kind: z.ZodLiteral<"custom">;
50
+ dnsName: z.ZodString;
51
+ hostedZoneId: z.ZodString;
52
+ }, z.core.$strict>], "kind">;
53
+ }, z.core.$strict>;
54
+ export declare const ParsedDnsRecordSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
55
+ kind: z.ZodLiteral<"standard">;
56
+ type: z.ZodEnum<{
57
+ A: "A";
58
+ AAAA: "AAAA";
59
+ CNAME: "CNAME";
60
+ MX: "MX";
61
+ TXT: "TXT";
62
+ NS: "NS";
63
+ SRV: "SRV";
64
+ CAA: "CAA";
65
+ }>;
66
+ name: z.ZodString;
67
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
68
+ ttl: z.ZodOptional<z.ZodNumber>;
69
+ }, z.core.$strict>, z.ZodObject<{
70
+ kind: z.ZodLiteral<"alias">;
71
+ type: z.ZodEnum<{
72
+ A: "A";
73
+ AAAA: "AAAA";
74
+ }>;
75
+ name: z.ZodString;
76
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
77
+ kind: z.ZodLiteral<"ecs">;
78
+ appName: z.ZodString;
79
+ }, z.core.$strict>, z.ZodObject<{
80
+ kind: z.ZodLiteral<"cdn">;
81
+ appName: z.ZodString;
82
+ }, z.core.$strict>, z.ZodObject<{
83
+ kind: z.ZodLiteral<"bucket">;
84
+ bucketName: z.ZodString;
85
+ }, z.core.$strict>, z.ZodObject<{
86
+ kind: z.ZodLiteral<"custom">;
87
+ dnsName: z.ZodString;
88
+ hostedZoneId: z.ZodString;
89
+ }, z.core.$strict>], "kind">;
90
+ }, z.core.$strict>], "kind">;
91
+ export declare const ParsedCertificateSchema: z.ZodObject<{
92
+ domainName: z.ZodString;
93
+ subjectAlternativeNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
94
+ transparencyLogging: z.ZodOptional<z.ZodBoolean>;
95
+ }, z.core.$strict>;
96
+ export declare const ParsedSubdomainDelegationSchema: z.ZodObject<{
97
+ subdomain: z.ZodString;
98
+ toAccount: z.ZodString;
99
+ auto: z.ZodOptional<z.ZodBoolean>;
100
+ }, z.core.$strict>;
101
+ export declare const ParsedDomainRecordsSchema: z.ZodObject<{
102
+ zoneName: z.ZodString;
103
+ registrar: z.ZodEnum<{
104
+ route53: "route53";
105
+ "external-delegated": "external-delegated";
106
+ "external-records": "external-records";
107
+ }>;
108
+ hostedZoneId: z.ZodOptional<z.ZodString>;
109
+ delegatedSubdomain: z.ZodOptional<z.ZodString>;
110
+ records: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
111
+ kind: z.ZodLiteral<"standard">;
112
+ type: z.ZodEnum<{
113
+ A: "A";
114
+ AAAA: "AAAA";
115
+ CNAME: "CNAME";
116
+ MX: "MX";
117
+ TXT: "TXT";
118
+ NS: "NS";
119
+ SRV: "SRV";
120
+ CAA: "CAA";
121
+ }>;
122
+ name: z.ZodString;
123
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
124
+ ttl: z.ZodOptional<z.ZodNumber>;
125
+ }, z.core.$strict>, z.ZodObject<{
126
+ kind: z.ZodLiteral<"alias">;
127
+ type: z.ZodEnum<{
128
+ A: "A";
129
+ AAAA: "AAAA";
130
+ }>;
131
+ name: z.ZodString;
132
+ target: z.ZodDiscriminatedUnion<[z.ZodObject<{
133
+ kind: z.ZodLiteral<"ecs">;
134
+ appName: z.ZodString;
135
+ }, z.core.$strict>, z.ZodObject<{
136
+ kind: z.ZodLiteral<"cdn">;
137
+ appName: z.ZodString;
138
+ }, z.core.$strict>, z.ZodObject<{
139
+ kind: z.ZodLiteral<"bucket">;
140
+ bucketName: z.ZodString;
141
+ }, z.core.$strict>, z.ZodObject<{
142
+ kind: z.ZodLiteral<"custom">;
143
+ dnsName: z.ZodString;
144
+ hostedZoneId: z.ZodString;
145
+ }, z.core.$strict>], "kind">;
146
+ }, z.core.$strict>], "kind">>;
147
+ certificates: z.ZodArray<z.ZodObject<{
148
+ domainName: z.ZodString;
149
+ subjectAlternativeNames: z.ZodOptional<z.ZodArray<z.ZodString>>;
150
+ transparencyLogging: z.ZodOptional<z.ZodBoolean>;
151
+ }, z.core.$strict>>;
152
+ delegations: z.ZodArray<z.ZodObject<{
153
+ subdomain: z.ZodString;
154
+ toAccount: z.ZodString;
155
+ auto: z.ZodOptional<z.ZodBoolean>;
156
+ }, z.core.$strict>>;
157
+ }, z.core.$strict>;
158
+ export type ParsedDomainRecords = z.infer<typeof ParsedDomainRecordsSchema>;
159
+ export type ParsedDnsRecord = z.infer<typeof ParsedDnsRecordSchema>;
160
+ export type ParsedStandardRecord = z.infer<typeof ParsedStandardRecordSchema>;
161
+ export type ParsedAliasRecord = z.infer<typeof ParsedAliasRecordSchema>;
162
+ export type TypedFjallTarget = z.infer<typeof TypedFjallTargetSchema>;
163
+ export type ParsedCertificate = z.infer<typeof ParsedCertificateSchema>;
164
+ export type ParsedSubdomainDelegation = z.infer<typeof ParsedSubdomainDelegationSchema>;
@@ -0,0 +1 @@
1
+ import{z as e}from"zod";const r=e.discriminatedUnion("kind",[e.object({kind:e.literal("ecs"),appName:e.string()}).strict(),e.object({kind:e.literal("cdn"),appName:e.string()}).strict(),e.object({kind:e.literal("bucket"),bucketName:e.string()}).strict(),e.object({kind:e.literal("custom"),dnsName:e.string(),hostedZoneId:e.string()}).strict()]),a=e.object({kind:e.literal("standard"),type:e.enum(["A","AAAA","CNAME","MX","TXT","NS","SRV","CAA"]),name:e.string(),value:e.union([e.string(),e.array(e.string())]),ttl:e.number().int().positive().optional()}).strict(),n=e.object({kind:e.literal("alias"),type:e.enum(["A","AAAA"]),name:e.string(),target:r}).strict(),i=e.discriminatedUnion("kind",[a,n]),o=e.object({domainName:e.string(),subjectAlternativeNames:e.array(e.string()).optional(),transparencyLogging:e.boolean().optional()}).strict(),s=e.object({subdomain:e.string(),toAccount:e.string(),auto:e.boolean().optional()}).strict(),c=e.object({zoneName:e.string(),registrar:e.enum(["route53","external-delegated","external-records"]),hostedZoneId:e.string().optional(),delegatedSubdomain:e.string().optional(),records:e.array(i),certificates:e.array(o),delegations:e.array(s)}).strict().refine(t=>t.registrar!=="external-delegated"||t.delegatedSubdomain!==void 0,{message:"external-delegated requires delegatedSubdomain"}).refine(t=>t.registrar!=="external-records"||t.delegations.length===0,{message:"external-records forbids delegations"});export{n as ParsedAliasRecordSchema,o as ParsedCertificateSchema,i as ParsedDnsRecordSchema,c as ParsedDomainRecordsSchema,a as ParsedStandardRecordSchema,s as ParsedSubdomainDelegationSchema,r as TypedFjallTargetSchema};