@suss/contract-terraform 0.32.0 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -4
- package/dist/index.cjs +876 -155
- package/dist/index.d.cts +199 -36
- package/dist/index.d.ts +199 -36
- package/dist/index.js +882 -156
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,15 +1,371 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import hcl2 from "hcl2-parser";
|
|
5
4
|
import semver from "semver";
|
|
6
5
|
import {
|
|
6
|
+
ecsContainerInstanceName,
|
|
7
|
+
hasNameHole,
|
|
7
8
|
messageBusBinding,
|
|
8
9
|
metricBinding,
|
|
9
10
|
namePatternFromSub,
|
|
10
|
-
|
|
11
|
+
PLATFORM_INJECTED_ENV_VARS,
|
|
12
|
+
parseHandler,
|
|
13
|
+
runtimeConfigBinding,
|
|
14
|
+
storageBinding,
|
|
15
|
+
withRuntimeContractMetadata
|
|
11
16
|
} from "@suss/behavioral-ir";
|
|
12
17
|
|
|
18
|
+
// src/hclDocument.ts
|
|
19
|
+
import hcl2 from "hcl2-parser";
|
|
20
|
+
var WRAPPER = "value";
|
|
21
|
+
function parseHclDocument(source) {
|
|
22
|
+
let read;
|
|
23
|
+
try {
|
|
24
|
+
const [parsed] = hcl2.parseToObject(source);
|
|
25
|
+
read = parsed;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return typeof read === "object" && read !== null && !Array.isArray(read) ? read : null;
|
|
30
|
+
}
|
|
31
|
+
function parseHclExpression(expression) {
|
|
32
|
+
return parseHclDocument(`${WRAPPER} = ${expression}`)?.[WRAPPER] ?? null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/hclValue.ts
|
|
36
|
+
function arrayOf(value) {
|
|
37
|
+
if (value === void 0 || value === null) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
return Array.isArray(value) ? value : [value];
|
|
41
|
+
}
|
|
42
|
+
function asRecord(value) {
|
|
43
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
44
|
+
}
|
|
45
|
+
function stringOf(value) {
|
|
46
|
+
return typeof value === "string" ? value : null;
|
|
47
|
+
}
|
|
48
|
+
function recordsIn(value) {
|
|
49
|
+
return arrayOf(value).map(asRecord).filter((record) => record !== null);
|
|
50
|
+
}
|
|
51
|
+
function mapStrings(value, leaf) {
|
|
52
|
+
if (typeof value === "string") {
|
|
53
|
+
return leaf(value);
|
|
54
|
+
}
|
|
55
|
+
if (Array.isArray(value)) {
|
|
56
|
+
return value.map((entry) => mapStrings(entry, leaf));
|
|
57
|
+
}
|
|
58
|
+
const record = asRecord(value);
|
|
59
|
+
if (record === null) {
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
return Object.fromEntries(
|
|
63
|
+
Object.entries(record).map(([name, entry]) => [
|
|
64
|
+
name,
|
|
65
|
+
mapStrings(entry, leaf)
|
|
66
|
+
])
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function someString(value, test) {
|
|
70
|
+
if (typeof value === "string") {
|
|
71
|
+
return test(value);
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return value.some((entry) => someString(entry, test));
|
|
75
|
+
}
|
|
76
|
+
return Object.values(asRecord(value) ?? {}).some(
|
|
77
|
+
(entry) => someString(entry, test)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/mapValue.ts
|
|
82
|
+
var WHOLE_INTERPOLATION = /^\$\{([\s\S]*)\}$/;
|
|
83
|
+
var MERGE_CALL = /^merge\(([\s\S]*)\)$/;
|
|
84
|
+
var LOCAL_MAP = /^local\.([A-Za-z_][\w-]*)$/;
|
|
85
|
+
var VARIABLE_MAP = /^var\.([A-Za-z_][\w-]*)$/;
|
|
86
|
+
var CHAIN_LIMIT = 4;
|
|
87
|
+
var OPENERS = "([{";
|
|
88
|
+
var CLOSERS = ")]}";
|
|
89
|
+
function statedMap(value, scope) {
|
|
90
|
+
return mapFrom(value, scope, 0);
|
|
91
|
+
}
|
|
92
|
+
function mapFrom(value, scope, depth) {
|
|
93
|
+
if (depth > CHAIN_LIMIT) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const record = asRecord(value);
|
|
97
|
+
if (record !== null) {
|
|
98
|
+
return record;
|
|
99
|
+
}
|
|
100
|
+
if (typeof value !== "string") {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const inner = WHOLE_INTERPOLATION.exec(value.trim());
|
|
104
|
+
return inner === null ? null : mapFromExpression(inner[1].trim(), scope, depth);
|
|
105
|
+
}
|
|
106
|
+
function mapFromExpression(expression, scope, depth) {
|
|
107
|
+
const merged = MERGE_CALL.exec(expression);
|
|
108
|
+
if (merged !== null) {
|
|
109
|
+
return mergedMaps(splitArguments(merged[1]), scope, depth);
|
|
110
|
+
}
|
|
111
|
+
const local = LOCAL_MAP.exec(expression);
|
|
112
|
+
if (local !== null) {
|
|
113
|
+
return mapFrom(scope.locals[local[1]], scope, depth + 1);
|
|
114
|
+
}
|
|
115
|
+
const variable = VARIABLE_MAP.exec(expression);
|
|
116
|
+
if (variable !== null) {
|
|
117
|
+
const name = variable[1];
|
|
118
|
+
return mapFrom(scope.arguments[name], scope, depth + 1) ?? mapFrom(scope.defaults[name], scope, depth + 1);
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
function mergedMaps(argumentTexts, scope, depth) {
|
|
123
|
+
const merged = {};
|
|
124
|
+
for (const text of argumentTexts) {
|
|
125
|
+
const stated = mapFrom(parseHclExpression(text), scope, depth + 1);
|
|
126
|
+
if (stated === null) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
Object.assign(merged, stated);
|
|
130
|
+
}
|
|
131
|
+
return merged;
|
|
132
|
+
}
|
|
133
|
+
function splitArguments(text) {
|
|
134
|
+
const found = [];
|
|
135
|
+
let depth = 0;
|
|
136
|
+
let quoted = false;
|
|
137
|
+
let start = 0;
|
|
138
|
+
for (let at = 0; at < text.length; at += 1) {
|
|
139
|
+
const character = text[at];
|
|
140
|
+
if (quoted) {
|
|
141
|
+
quoted = character !== '"' || text[at - 1] === "\\";
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (character === '"') {
|
|
145
|
+
quoted = true;
|
|
146
|
+
} else if (OPENERS.includes(character)) {
|
|
147
|
+
depth += 1;
|
|
148
|
+
} else if (CLOSERS.includes(character)) {
|
|
149
|
+
depth -= 1;
|
|
150
|
+
} else if (character === "," && depth === 0) {
|
|
151
|
+
found.push(text.slice(start, at));
|
|
152
|
+
start = at + 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
found.push(text.slice(start));
|
|
156
|
+
return found.map((argument) => argument.trim()).filter((argument) => argument.length > 0);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/references.ts
|
|
160
|
+
var SUB_TOKEN = /\$\{([^}]*)\}/g;
|
|
161
|
+
function replaceInterpolations(text, settle) {
|
|
162
|
+
return text.replace(
|
|
163
|
+
SUB_TOKEN,
|
|
164
|
+
(written, inner) => settle(inner.trim()) ?? written
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
function interpolatedReferences(text) {
|
|
168
|
+
return [...text.matchAll(SUB_TOKEN)].map(
|
|
169
|
+
(match) => match[1].trim()
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
var RESOURCE_ATTRIBUTE = /^([A-Za-z][\w-]*)\.([A-Za-z_][\w-]*)\.([A-Za-z_][\w-]*)$/;
|
|
173
|
+
var WHOLE_REFERENCE = /^\$\{([^}]*)\}$/;
|
|
174
|
+
var LOCAL_VALUE = /^local\.([A-Za-z_][\w-]*)$/;
|
|
175
|
+
var VARIABLE_VALUE = /^var\.([A-Za-z_][\w-]*)$/;
|
|
176
|
+
var CHAIN_LIMIT2 = 4;
|
|
177
|
+
function referenceScope(opts) {
|
|
178
|
+
const resources = /* @__PURE__ */ new Map();
|
|
179
|
+
for (const [resourceType, label, body] of opts.resources) {
|
|
180
|
+
const address = `${resourceType}.${label}`;
|
|
181
|
+
if (!resources.has(address)) {
|
|
182
|
+
resources.set(address, body);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
resources,
|
|
187
|
+
locals: firstOfEach(opts.locals ?? []),
|
|
188
|
+
arguments: opts.arguments ?? {},
|
|
189
|
+
defaults: opts.defaults ?? {},
|
|
190
|
+
namePrefix: opts.namePrefix ?? ""
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function firstOfEach(blocks) {
|
|
194
|
+
const stated = {};
|
|
195
|
+
for (const block of blocks) {
|
|
196
|
+
for (const [name, value] of Object.entries(block)) {
|
|
197
|
+
if (!(name in stated)) {
|
|
198
|
+
stated[name] = value;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return stated;
|
|
203
|
+
}
|
|
204
|
+
function referencedResource(value, scope) {
|
|
205
|
+
const whole = WHOLE_REFERENCE.exec(value.trim());
|
|
206
|
+
if (whole === null) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
const parsed = RESOURCE_ATTRIBUTE.exec(whole[1].trim());
|
|
210
|
+
if (parsed === null) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const [, resourceType, label] = parsed;
|
|
214
|
+
return scope.resources.has(`${resourceType}.${label}`) ? `${scope.namePrefix}${label}` : null;
|
|
215
|
+
}
|
|
216
|
+
function resolveReferences(value, scope) {
|
|
217
|
+
return expand(value, scope, []) ?? value;
|
|
218
|
+
}
|
|
219
|
+
function expand(value, scope, resolving) {
|
|
220
|
+
let cycled = false;
|
|
221
|
+
const expandOne = (reference) => {
|
|
222
|
+
if (resolving.includes(reference)) {
|
|
223
|
+
cycled = true;
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
const stated = statedValue(reference, scope);
|
|
227
|
+
if (stated === null || resolving.length >= CHAIN_LIMIT2) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const nested = expand(stated, scope, [...resolving, reference]);
|
|
231
|
+
if (nested === null) {
|
|
232
|
+
cycled = true;
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return nested;
|
|
236
|
+
};
|
|
237
|
+
const expanded = replaceInterpolations(value, expandOne);
|
|
238
|
+
return cycled ? null : expanded;
|
|
239
|
+
}
|
|
240
|
+
function statedValue(reference, scope) {
|
|
241
|
+
const local = LOCAL_VALUE.exec(reference);
|
|
242
|
+
if (local !== null) {
|
|
243
|
+
return stringOrNull(scope.locals[local[1]]);
|
|
244
|
+
}
|
|
245
|
+
const variable = VARIABLE_VALUE.exec(reference);
|
|
246
|
+
if (variable !== null) {
|
|
247
|
+
return stringOrNull(scope.arguments[variable[1]]);
|
|
248
|
+
}
|
|
249
|
+
const parsed = RESOURCE_ATTRIBUTE.exec(reference);
|
|
250
|
+
if (parsed === null) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const [, resourceType, label, attribute] = parsed;
|
|
254
|
+
return stringOrNull(
|
|
255
|
+
scope.resources.get(`${resourceType}.${label}`)?.[attribute]
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
function stringOrNull(value) {
|
|
259
|
+
return typeof value === "string" ? value : null;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/blockExpansion.ts
|
|
263
|
+
var DYNAMIC = "dynamic";
|
|
264
|
+
var BARE_NAME = /^\$\{([A-Za-z_][\w-]*)\}$/;
|
|
265
|
+
var FOR_EXPRESSION = /^\$\{\s*\[\s*for\s+([A-Za-z_]\w*)\s*,\s*([A-Za-z_]\w*)\s+in\s+([^:]+):([\s\S]*)\]\s*\}$/;
|
|
266
|
+
function dynamicBlocks(body, block, scope) {
|
|
267
|
+
const written = [];
|
|
268
|
+
const labelled = arrayOf(body[DYNAMIC]).flatMap(
|
|
269
|
+
(entry) => recordsIn(asRecord(entry)?.[block])
|
|
270
|
+
);
|
|
271
|
+
for (const declared of labelled) {
|
|
272
|
+
const stated = statedMap(declared.for_each, scope);
|
|
273
|
+
if (stated === null) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const iterator = iteratorName(declared, block);
|
|
277
|
+
for (const [key, value] of Object.entries(stated)) {
|
|
278
|
+
const settle = entrySettle(iterator, key, value);
|
|
279
|
+
for (const content of recordsIn(declared.content)) {
|
|
280
|
+
written.push(...filledBlock(content, settle, [iterator]));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return written;
|
|
285
|
+
}
|
|
286
|
+
function filledBlock(content, settle, iterators) {
|
|
287
|
+
const filled = substituted(content, settle);
|
|
288
|
+
return mentions(filled, iterators) ? [] : [filled];
|
|
289
|
+
}
|
|
290
|
+
function iteratedRecords(value, scope) {
|
|
291
|
+
if (typeof value !== "string") {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const parsed = FOR_EXPRESSION.exec(value.trim());
|
|
295
|
+
if (parsed === null) {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
const [, keyName, valueName, collection, body] = parsed;
|
|
299
|
+
const stated = statedMap(`\${${collection.trim()}}`, scope);
|
|
300
|
+
if (stated === null) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
const template = parseBody(body);
|
|
304
|
+
if (template === null) {
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
return Object.entries(stated).flatMap(
|
|
308
|
+
([key, entry]) => filledBlock(
|
|
309
|
+
template,
|
|
310
|
+
pairSettle(keyName, valueName, key, entry),
|
|
311
|
+
[keyName, valueName]
|
|
312
|
+
)
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
function parseBody(body) {
|
|
316
|
+
const trimmed = body.trim();
|
|
317
|
+
return trimmed.startsWith("{") ? asRecord(parseHclExpression(trimmed)) : null;
|
|
318
|
+
}
|
|
319
|
+
function iteratorName(declared, block) {
|
|
320
|
+
const stated = declared.iterator;
|
|
321
|
+
if (typeof stated !== "string") {
|
|
322
|
+
return block;
|
|
323
|
+
}
|
|
324
|
+
const bare = BARE_NAME.exec(stated.trim());
|
|
325
|
+
return bare === null ? block : bare[1];
|
|
326
|
+
}
|
|
327
|
+
function entrySettle(iterator, key, value) {
|
|
328
|
+
return (reference) => {
|
|
329
|
+
const steps = reference.split(".");
|
|
330
|
+
if (steps[0] !== iterator) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
if (steps.length === 2 && steps[1] === "key") {
|
|
334
|
+
return key;
|
|
335
|
+
}
|
|
336
|
+
return steps[1] === "value" ? fieldOf(value, steps.slice(2)) : null;
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function pairSettle(keyName, valueName, key, value) {
|
|
340
|
+
return (reference) => {
|
|
341
|
+
const steps = reference.split(".");
|
|
342
|
+
if (steps.length === 1 && steps[0] === keyName) {
|
|
343
|
+
return key;
|
|
344
|
+
}
|
|
345
|
+
return steps[0] === valueName ? fieldOf(value, steps.slice(1)) : null;
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function fieldOf(value, steps) {
|
|
349
|
+
if (steps.length === 0) {
|
|
350
|
+
return typeof value === "string" ? value : null;
|
|
351
|
+
}
|
|
352
|
+
if (steps.length > 1) {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
const field = asRecord(value)?.[steps[0]];
|
|
356
|
+
return typeof field === "string" ? field : null;
|
|
357
|
+
}
|
|
358
|
+
function substituted(value, settle) {
|
|
359
|
+
const fill = (text) => replaceInterpolations(text, settle);
|
|
360
|
+
return mapStrings(value, fill);
|
|
361
|
+
}
|
|
362
|
+
function mentions(value, iterators) {
|
|
363
|
+
const mentionsOne = (text) => interpolatedReferences(text).some(
|
|
364
|
+
(reference) => iterators.includes(reference.split(".")[0] ?? "")
|
|
365
|
+
);
|
|
366
|
+
return someString(value, mentionsOne);
|
|
367
|
+
}
|
|
368
|
+
|
|
13
369
|
// src/filterQuery.ts
|
|
14
370
|
var OPERATORS = ["!=", ">=", "<=", "=", ">", "<", ":"];
|
|
15
371
|
var KEYWORDS = /* @__PURE__ */ new Set(["AND", "OR", "NOT"]);
|
|
@@ -271,96 +627,147 @@ var TokenReader = class {
|
|
|
271
627
|
}
|
|
272
628
|
};
|
|
273
629
|
|
|
274
|
-
// src/
|
|
275
|
-
var
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
var CHAIN_LIMIT = 4;
|
|
280
|
-
function referenceScope(resources, locals = []) {
|
|
281
|
-
const scope = /* @__PURE__ */ new Map();
|
|
282
|
-
for (const [resourceType, label, body] of resources) {
|
|
283
|
-
const address = `${resourceType}.${label}`;
|
|
284
|
-
if (!scope.has(address)) {
|
|
285
|
-
scope.set(address, body);
|
|
286
|
-
}
|
|
630
|
+
// src/jsonAttribute.ts
|
|
631
|
+
var JSON_ENCODE = /^\$\{\s*jsonencode\((.*)\)\s*\}$/s;
|
|
632
|
+
function jsonAttributeValue(value) {
|
|
633
|
+
if (typeof value !== "string") {
|
|
634
|
+
return null;
|
|
287
635
|
}
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (!(name in stated)) {
|
|
292
|
-
stated[name] = value;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
636
|
+
const literal = parseJson(value);
|
|
637
|
+
if (literal !== null) {
|
|
638
|
+
return literal;
|
|
295
639
|
}
|
|
296
|
-
|
|
297
|
-
return
|
|
298
|
-
}
|
|
299
|
-
function resolveReferences(value, scope) {
|
|
300
|
-
return expand(value, scope, []) ?? value;
|
|
301
|
-
}
|
|
302
|
-
function expand(value, scope, resolving) {
|
|
303
|
-
let cycled = false;
|
|
304
|
-
const expanded = value.replace(SUB_TOKEN, (written, inner) => {
|
|
305
|
-
const reference = inner.trim();
|
|
306
|
-
if (resolving.includes(reference)) {
|
|
307
|
-
cycled = true;
|
|
308
|
-
return written;
|
|
309
|
-
}
|
|
310
|
-
const stated = statedValue(reference, scope);
|
|
311
|
-
if (stated === null || resolving.length >= CHAIN_LIMIT) {
|
|
312
|
-
return written;
|
|
313
|
-
}
|
|
314
|
-
const nested = expand(stated, scope, [...resolving, reference]);
|
|
315
|
-
if (nested === null) {
|
|
316
|
-
cycled = true;
|
|
317
|
-
return written;
|
|
318
|
-
}
|
|
319
|
-
return nested;
|
|
320
|
-
});
|
|
321
|
-
return cycled ? null : expanded;
|
|
640
|
+
const encoded = JSON_ENCODE.exec(value);
|
|
641
|
+
return encoded === null ? null : parseHclExpression(encoded[1]);
|
|
322
642
|
}
|
|
323
|
-
function
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
return typeof stated2 === "string" ? stated2 : null;
|
|
328
|
-
}
|
|
329
|
-
const parsed = RESOURCE_ATTRIBUTE.exec(reference);
|
|
330
|
-
if (parsed === null) {
|
|
643
|
+
function parseJson(text) {
|
|
644
|
+
try {
|
|
645
|
+
return JSON.parse(text);
|
|
646
|
+
} catch {
|
|
331
647
|
return null;
|
|
332
648
|
}
|
|
333
|
-
const [, resourceType, label, attribute] = parsed;
|
|
334
|
-
const stated = scope.get(`${resourceType}.${label}`)?.[attribute];
|
|
335
|
-
return typeof stated === "string" ? stated : null;
|
|
336
649
|
}
|
|
337
650
|
|
|
338
651
|
// src/index.ts
|
|
339
652
|
function terraformToSummaries(source, sourceFile, options) {
|
|
340
|
-
return
|
|
653
|
+
return moduleSummaries(
|
|
654
|
+
moduleOf({
|
|
655
|
+
files: [{ source, sourceFile }],
|
|
656
|
+
directory: path.dirname(sourceFile),
|
|
657
|
+
namePrefix: "",
|
|
658
|
+
arguments: {},
|
|
659
|
+
ancestors: []
|
|
660
|
+
}),
|
|
661
|
+
options
|
|
662
|
+
);
|
|
341
663
|
}
|
|
342
664
|
function terraformFileToSummaries(target, options) {
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
665
|
+
const directory = fs.statSync(target).isDirectory() ? target : path.dirname(target);
|
|
666
|
+
const files = fs.statSync(target).isDirectory() ? sourceFilesIn(target) : [readSourceFile(target)];
|
|
667
|
+
return moduleSummaries(
|
|
668
|
+
moduleOf({
|
|
669
|
+
files,
|
|
670
|
+
directory,
|
|
671
|
+
namePrefix: "",
|
|
672
|
+
arguments: {},
|
|
673
|
+
ancestors: [directory]
|
|
674
|
+
}),
|
|
349
675
|
options
|
|
350
676
|
);
|
|
351
677
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
678
|
+
var LOCAL_SOURCE = /^\.\.?[/\\]/;
|
|
679
|
+
var MODULE_SOURCE = "source";
|
|
680
|
+
var MODULE_DEPTH_LIMIT = 8;
|
|
681
|
+
function sourceFilesIn(directory) {
|
|
682
|
+
return fs.readdirSync(directory).filter((name) => name.endsWith(".tf")).map((name) => readSourceFile(path.join(directory, name)));
|
|
683
|
+
}
|
|
684
|
+
function readSourceFile(file) {
|
|
685
|
+
return { source: fs.readFileSync(file, "utf8"), sourceFile: file };
|
|
686
|
+
}
|
|
687
|
+
function moduleOf(opts) {
|
|
688
|
+
const files = opts.files.map((file) => parseSource(file)).filter((file) => file !== null);
|
|
689
|
+
const scope = referenceScope({
|
|
690
|
+
resources: files.flatMap((file) => file.resources),
|
|
691
|
+
locals: files.flatMap((file) => file.locals),
|
|
692
|
+
defaults: Object.assign({}, ...files.map((file) => file.defaults)),
|
|
693
|
+
arguments: opts.arguments,
|
|
694
|
+
namePrefix: opts.namePrefix
|
|
695
|
+
});
|
|
696
|
+
const children = [];
|
|
697
|
+
for (const [label, call] of files.flatMap((file) => file.modules)) {
|
|
698
|
+
const child = childModule({ ...opts, scope, label, call });
|
|
699
|
+
if (child === null) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
children.push(child);
|
|
703
|
+
scope.resources.set(`module.${label}`, outputValues(child));
|
|
704
|
+
}
|
|
705
|
+
return { files, scope, children };
|
|
706
|
+
}
|
|
707
|
+
function childModule(opts) {
|
|
708
|
+
const source = stringOf(opts.call[MODULE_SOURCE]);
|
|
709
|
+
if (source === null || !LOCAL_SOURCE.test(source)) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const directory = path.resolve(opts.directory, source);
|
|
713
|
+
if (opts.ancestors.includes(directory) || opts.ancestors.length >= MODULE_DEPTH_LIMIT || !isDirectory(directory)) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
return moduleOf({
|
|
717
|
+
files: sourceFilesIn(directory),
|
|
718
|
+
directory,
|
|
719
|
+
namePrefix: `${opts.namePrefix}module.${opts.label}.`,
|
|
720
|
+
arguments: passedArguments(opts.call, opts.scope),
|
|
721
|
+
ancestors: [...opts.ancestors, directory]
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
function isDirectory(target) {
|
|
725
|
+
return fs.existsSync(target) && fs.statSync(target).isDirectory();
|
|
726
|
+
}
|
|
727
|
+
function passedArguments(call, parent) {
|
|
728
|
+
const passed = {};
|
|
729
|
+
for (const [name, value] of Object.entries(call)) {
|
|
730
|
+
if (name === MODULE_SOURCE) {
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
const stated = asRecord(value);
|
|
734
|
+
if (stated !== null) {
|
|
735
|
+
passed[name] = resolvedThroughout(stated, parent);
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
const written = stringOf(value);
|
|
739
|
+
const settled = written === null ? null : resolveReferences(written, parent);
|
|
740
|
+
if (settled !== null && !settled.includes("${")) {
|
|
741
|
+
passed[name] = settled;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return passed;
|
|
745
|
+
}
|
|
746
|
+
function resolvedThroughout(value, scope) {
|
|
747
|
+
const resolveHere = (text) => resolveReferences(text, scope);
|
|
748
|
+
return mapStrings(value, resolveHere);
|
|
749
|
+
}
|
|
750
|
+
function outputValues(child) {
|
|
751
|
+
const stated = {};
|
|
752
|
+
for (const [name, value] of child.files.flatMap((file) => file.outputs)) {
|
|
753
|
+
const written = stringOf(value);
|
|
754
|
+
if (written === null || name in stated) {
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
const settled = resolveReferences(written, child.scope);
|
|
758
|
+
if (!settled.includes("${")) {
|
|
759
|
+
stated[name] = settled;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return stated;
|
|
763
|
+
}
|
|
764
|
+
function moduleSummaries(module, options) {
|
|
358
765
|
const summaries = [];
|
|
359
|
-
for (const file of
|
|
766
|
+
for (const file of module.files) {
|
|
360
767
|
for (const [resourceType, label, body] of file.resources) {
|
|
361
768
|
for (const pack of options.packs) {
|
|
362
|
-
const pattern = patternFor(pack, resourceType, file.constraints);
|
|
363
|
-
if (pattern === void 0
|
|
769
|
+
const pattern = patternFor(pack, resourceType, file.constraints, body);
|
|
770
|
+
if (pattern === void 0) {
|
|
364
771
|
continue;
|
|
365
772
|
}
|
|
366
773
|
summaries.push(
|
|
@@ -370,23 +777,20 @@ function summariesForFiles(files, options) {
|
|
|
370
777
|
body,
|
|
371
778
|
sourceFile: file.sourceFile,
|
|
372
779
|
resourceType,
|
|
373
|
-
scope
|
|
780
|
+
scope: module.scope,
|
|
781
|
+
options
|
|
374
782
|
})
|
|
375
783
|
);
|
|
376
784
|
}
|
|
377
785
|
}
|
|
378
786
|
}
|
|
787
|
+
for (const child of module.children) {
|
|
788
|
+
summaries.push(...moduleSummaries(child, options));
|
|
789
|
+
}
|
|
379
790
|
return summaries;
|
|
380
791
|
}
|
|
381
792
|
function parseSource(file) {
|
|
382
|
-
|
|
383
|
-
try {
|
|
384
|
-
const [parsed] = hcl2.parseToObject(file.source);
|
|
385
|
-
read = parsed;
|
|
386
|
-
} catch {
|
|
387
|
-
return null;
|
|
388
|
-
}
|
|
389
|
-
const document = asRecord(read);
|
|
793
|
+
const document = parseHclDocument(file.source);
|
|
390
794
|
if (document === null) {
|
|
391
795
|
return null;
|
|
392
796
|
}
|
|
@@ -394,9 +798,43 @@ function parseSource(file) {
|
|
|
394
798
|
sourceFile: file.sourceFile,
|
|
395
799
|
constraints: providerConstraints(document),
|
|
396
800
|
resources: resourcesIn(document),
|
|
397
|
-
locals: localsIn(document)
|
|
801
|
+
locals: localsIn(document),
|
|
802
|
+
defaults: variableDefaults(document),
|
|
803
|
+
modules: labelledBlocks(document.module),
|
|
804
|
+
outputs: labelledBlocks(document.output).map(([name, block]) => [
|
|
805
|
+
name,
|
|
806
|
+
block.value
|
|
807
|
+
])
|
|
398
808
|
};
|
|
399
809
|
}
|
|
810
|
+
function labelledBlocks(declared) {
|
|
811
|
+
const found = [];
|
|
812
|
+
for (const group of arrayOf(declared)) {
|
|
813
|
+
for (const [label, bodies] of Object.entries(asRecord(group) ?? {})) {
|
|
814
|
+
for (const body of arrayOf(bodies)) {
|
|
815
|
+
const read = asRecord(body);
|
|
816
|
+
if (read !== null) {
|
|
817
|
+
found.push([label, read]);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return found;
|
|
823
|
+
}
|
|
824
|
+
function variableDefaults(document) {
|
|
825
|
+
const defaults = {};
|
|
826
|
+
for (const group of arrayOf(document.variable)) {
|
|
827
|
+
for (const [name, declared] of Object.entries(asRecord(group) ?? {})) {
|
|
828
|
+
for (const block of arrayOf(declared)) {
|
|
829
|
+
const stated = asRecord(block)?.default;
|
|
830
|
+
if (stated !== void 0 && !(name in defaults)) {
|
|
831
|
+
defaults[name] = stated;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return defaults;
|
|
837
|
+
}
|
|
400
838
|
function providerConstraints(document) {
|
|
401
839
|
const constraints = /* @__PURE__ */ new Map();
|
|
402
840
|
for (const block of arrayOf(document.terraform)) {
|
|
@@ -417,23 +855,21 @@ function providerConstraints(document) {
|
|
|
417
855
|
}
|
|
418
856
|
return constraints;
|
|
419
857
|
}
|
|
420
|
-
function patternFor(pack, resourceType, constraints) {
|
|
858
|
+
function patternFor(pack, resourceType, constraints, body) {
|
|
421
859
|
const pinned = constraints.get(pack.provider);
|
|
422
|
-
return pack.resources.find(
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
});
|
|
860
|
+
return pack.resources.find(
|
|
861
|
+
(pattern) => pattern.resource === resourceType && versionAllows(pinned, pattern.providerVersions) && entryApplies(pattern, body)
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
function versionAllows(pinned, providerVersions) {
|
|
865
|
+
if (pinned === void 0) {
|
|
866
|
+
return true;
|
|
867
|
+
}
|
|
868
|
+
try {
|
|
869
|
+
return semver.intersects(pinned, providerVersions, { loose: true });
|
|
870
|
+
} catch {
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
437
873
|
}
|
|
438
874
|
function entryApplies(pattern, body) {
|
|
439
875
|
const gate = pattern.appliesWhen;
|
|
@@ -445,7 +881,7 @@ function entryApplies(pattern, body) {
|
|
|
445
881
|
return gate.whenUnset === "read";
|
|
446
882
|
}
|
|
447
883
|
const text = stringOf(value);
|
|
448
|
-
return text !== null && gate.equals.includes(text);
|
|
884
|
+
return text !== null && (gate.equals ?? []).includes(text);
|
|
449
885
|
}
|
|
450
886
|
var READERS = {
|
|
451
887
|
storage: (site, boundary) => {
|
|
@@ -456,7 +892,8 @@ var READERS = {
|
|
|
456
892
|
},
|
|
457
893
|
"message-bus": (site, boundary) => [busSummary({ ...site, boundary })],
|
|
458
894
|
metric: (site, boundary) => [metricSummary({ ...site, boundary })],
|
|
459
|
-
"metric-reading": (site, boundary) => readingSummaries({ ...site, boundary })
|
|
895
|
+
"metric-reading": (site, boundary) => readingSummaries({ ...site, boundary }),
|
|
896
|
+
deployable: (site, boundary) => deployableSummaries({ ...site, boundary })
|
|
460
897
|
};
|
|
461
898
|
function summariesFor(opts) {
|
|
462
899
|
const { pattern, ...site } = opts;
|
|
@@ -465,9 +902,11 @@ function summariesFor(opts) {
|
|
|
465
902
|
}
|
|
466
903
|
function storageSummary(opts) {
|
|
467
904
|
const { boundary, label, shape, types } = opts;
|
|
468
|
-
const address = `${opts.resourceType}.${label}`;
|
|
905
|
+
const address = `${opts.scope.namePrefix}${opts.resourceType}.${label}`;
|
|
906
|
+
const storageSystem = storageSystemOf(opts.body, boundary, opts.scope);
|
|
469
907
|
const declaresContainer = boundary.declares !== "store";
|
|
470
908
|
const physicalTable = !declaresContainer || boundary.nameAttribute === void 0 ? null : namePattern(opts.body[boundary.nameAttribute], opts.scope);
|
|
909
|
+
const listedFields = jsonFields(opts.body, boundary);
|
|
471
910
|
return {
|
|
472
911
|
kind: "library",
|
|
473
912
|
location: {
|
|
@@ -480,35 +919,102 @@ function storageSummary(opts) {
|
|
|
480
919
|
exportPath: null,
|
|
481
920
|
boundaryBinding: storageBinding({
|
|
482
921
|
recognition: "terraform",
|
|
483
|
-
storageSystem
|
|
922
|
+
storageSystem,
|
|
484
923
|
...boundary.transport !== void 0 ? { transport: boundary.transport } : {},
|
|
485
|
-
scope:
|
|
924
|
+
scope: containerScope(opts.body, boundary, opts.scope),
|
|
486
925
|
container: declaresContainer ? label : null,
|
|
487
926
|
accessPath: shape.accessPath
|
|
488
927
|
})
|
|
489
928
|
},
|
|
490
929
|
inputs: [],
|
|
491
930
|
transitions: [],
|
|
492
|
-
gaps:
|
|
931
|
+
gaps: unknownEngineGaps(storageSystem, boundary),
|
|
493
932
|
confidence: { source: "declared", level: "high" },
|
|
494
933
|
metadata: {
|
|
495
934
|
storageContract: {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
fieldSet: shape.serves === null ? boundary.fieldSet : "exhaustive",
|
|
499
|
-
...boundary.identifies === void 0 ? {} : {
|
|
500
|
-
identifies: { kind: "keyFields", fields: shape.keyFields },
|
|
501
|
-
fields: (shape.serves ?? shape.keyFields).map((field) => ({
|
|
502
|
-
name: field,
|
|
503
|
-
...types.has(field) ? { type: types.get(field) } : {},
|
|
504
|
-
...shape.keyFields.includes(field) ? { primary: true } : {}
|
|
505
|
-
}))
|
|
506
|
-
},
|
|
935
|
+
fieldSet: declaredFieldSet(boundary, shape, listedFields),
|
|
936
|
+
...contractFields(boundary, shape, types, listedFields),
|
|
507
937
|
...physicalTable !== null ? { physicalTable } : {}
|
|
508
938
|
}
|
|
509
939
|
}
|
|
510
940
|
};
|
|
511
941
|
}
|
|
942
|
+
function storageSystemOf(body, boundary, scope) {
|
|
943
|
+
const spec = boundary.storageSystem;
|
|
944
|
+
if (typeof spec === "string") {
|
|
945
|
+
return spec;
|
|
946
|
+
}
|
|
947
|
+
return meaningOf(body, spec, scope) ?? null;
|
|
948
|
+
}
|
|
949
|
+
function unknownEngineGaps(storageSystem, boundary) {
|
|
950
|
+
const spec = boundary.storageSystem;
|
|
951
|
+
if (storageSystem !== null || typeof spec === "string") {
|
|
952
|
+
return [];
|
|
953
|
+
}
|
|
954
|
+
return [
|
|
955
|
+
{
|
|
956
|
+
type: "unreadOutcome",
|
|
957
|
+
conditions: [],
|
|
958
|
+
consequence: "unknown",
|
|
959
|
+
description: `"${spec.attribute}" states an engine this run could not settle, so which store this is stays unknown and it pairs with an access on any engine.`
|
|
960
|
+
}
|
|
961
|
+
];
|
|
962
|
+
}
|
|
963
|
+
function contractFields(boundary, shape, types, listedFields) {
|
|
964
|
+
if (boundary.identifies !== void 0) {
|
|
965
|
+
return {
|
|
966
|
+
identifies: { kind: "keyFields", fields: shape.keyFields },
|
|
967
|
+
fields: (shape.serves ?? shape.keyFields).map((field) => ({
|
|
968
|
+
name: field,
|
|
969
|
+
...types.has(field) ? { type: types.get(field) } : {},
|
|
970
|
+
...shape.keyFields.includes(field) ? { primary: true } : {}
|
|
971
|
+
}))
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
return listedFields === null ? {} : { fields: listedFields };
|
|
975
|
+
}
|
|
976
|
+
function declaredFieldSet(boundary, shape, listedFields) {
|
|
977
|
+
if (shape.serves !== null) {
|
|
978
|
+
return "exhaustive";
|
|
979
|
+
}
|
|
980
|
+
return listedFields === null ? boundary.fieldSet : "exhaustive";
|
|
981
|
+
}
|
|
982
|
+
function jsonFields(body, boundary) {
|
|
983
|
+
const spec = boundary.fieldsFromJson;
|
|
984
|
+
if (spec === void 0) {
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
const stated = jsonAttributeValue(valueAt(body, spec.attribute));
|
|
988
|
+
if (!Array.isArray(stated)) {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
const fields = stated.flatMap((entry) => {
|
|
992
|
+
const field = asRecord(entry);
|
|
993
|
+
const name = field === null ? null : stringOf(field[spec.nameKey]);
|
|
994
|
+
if (field === null || name === null) {
|
|
995
|
+
return [];
|
|
996
|
+
}
|
|
997
|
+
const type = spec.typeKey === void 0 ? null : stringOf(field[spec.typeKey]);
|
|
998
|
+
return [
|
|
999
|
+
{
|
|
1000
|
+
name,
|
|
1001
|
+
...type !== null ? { type } : {},
|
|
1002
|
+
...spec.requires === void 0 ? {} : { nullable: !alwaysSet(field, spec.requires) }
|
|
1003
|
+
}
|
|
1004
|
+
];
|
|
1005
|
+
});
|
|
1006
|
+
return fields.length === 0 ? null : fields;
|
|
1007
|
+
}
|
|
1008
|
+
function alwaysSet(field, requires) {
|
|
1009
|
+
const stated = stringOf(field[requires.key]);
|
|
1010
|
+
return stated !== null && requires.values.includes(stated);
|
|
1011
|
+
}
|
|
1012
|
+
function containerScope(body, boundary, scope) {
|
|
1013
|
+
if (boundary.scopeAttribute === void 0) {
|
|
1014
|
+
return "default";
|
|
1015
|
+
}
|
|
1016
|
+
return namePattern(valueAt(body, boundary.scopeAttribute), scope) ?? "default";
|
|
1017
|
+
}
|
|
512
1018
|
function busSummary(opts) {
|
|
513
1019
|
const { boundary, label } = opts;
|
|
514
1020
|
const physicalName = boundary.nameAttribute === void 0 ? null : namePattern(opts.body[boundary.nameAttribute], opts.scope);
|
|
@@ -520,7 +1026,7 @@ function busSummary(opts) {
|
|
|
520
1026
|
exportName: null
|
|
521
1027
|
},
|
|
522
1028
|
identity: {
|
|
523
|
-
name: `${opts.resourceType}.${label}`,
|
|
1029
|
+
name: `${opts.scope.namePrefix}${opts.resourceType}.${label}`,
|
|
524
1030
|
exportPath: null,
|
|
525
1031
|
boundaryBinding: messageBusBinding({
|
|
526
1032
|
recognition: "terraform",
|
|
@@ -541,14 +1047,26 @@ function busSummary(opts) {
|
|
|
541
1047
|
}
|
|
542
1048
|
};
|
|
543
1049
|
}
|
|
544
|
-
var
|
|
1050
|
+
var TEMPLATE_HOLE = /\{([^{}]+)\}/g;
|
|
1051
|
+
function metricTypeFrom(template, body, scope) {
|
|
1052
|
+
let missing = false;
|
|
1053
|
+
const spelled = template.replace(TEMPLATE_HOLE, (_whole, path2) => {
|
|
1054
|
+
const stated = namePattern(valueAt(body, path2), scope);
|
|
1055
|
+
if (stated === null) {
|
|
1056
|
+
missing = true;
|
|
1057
|
+
return "";
|
|
1058
|
+
}
|
|
1059
|
+
return stated;
|
|
1060
|
+
});
|
|
1061
|
+
return missing ? null : spelled;
|
|
1062
|
+
}
|
|
545
1063
|
function metricSummary(opts) {
|
|
546
1064
|
const { boundary } = opts;
|
|
547
|
-
const
|
|
548
|
-
|
|
1065
|
+
const metricType = metricTypeFrom(
|
|
1066
|
+
boundary.metricTypeTemplate,
|
|
1067
|
+
opts.body,
|
|
549
1068
|
opts.scope
|
|
550
1069
|
);
|
|
551
|
-
const metricType = declaredName === null ? null : boundary.metricTypeTemplate.replace(NAME_HOLE, declaredName);
|
|
552
1070
|
return {
|
|
553
1071
|
kind: "library",
|
|
554
1072
|
location: {
|
|
@@ -557,7 +1075,7 @@ function metricSummary(opts) {
|
|
|
557
1075
|
exportName: null
|
|
558
1076
|
},
|
|
559
1077
|
identity: {
|
|
560
|
-
name: `${opts.resourceType}.${opts.label}`,
|
|
1078
|
+
name: `${opts.scope.namePrefix}${opts.resourceType}.${opts.label}`,
|
|
561
1079
|
exportPath: null,
|
|
562
1080
|
boundaryBinding: metricBinding({
|
|
563
1081
|
recognition: "terraform",
|
|
@@ -569,12 +1087,14 @@ function metricSummary(opts) {
|
|
|
569
1087
|
transitions: [],
|
|
570
1088
|
gaps: [],
|
|
571
1089
|
confidence: { source: "declared", level: "high" },
|
|
572
|
-
metadata: {
|
|
1090
|
+
metadata: {
|
|
1091
|
+
metricContract: metricContract(opts.body, boundary, opts.scope)
|
|
1092
|
+
}
|
|
573
1093
|
};
|
|
574
1094
|
}
|
|
575
|
-
function metricContract(body, boundary) {
|
|
576
|
-
const values = meaningOf(body, boundary.values);
|
|
577
|
-
const accumulates = meaningOf(body, boundary.accumulates);
|
|
1095
|
+
function metricContract(body, boundary, scope) {
|
|
1096
|
+
const values = meaningOf(body, boundary.values, scope);
|
|
1097
|
+
const accumulates = meaningOf(body, boundary.accumulates, scope);
|
|
578
1098
|
return {
|
|
579
1099
|
...values !== void 0 ? { values } : {},
|
|
580
1100
|
...accumulates !== void 0 ? { accumulates } : {}
|
|
@@ -583,11 +1103,14 @@ function metricContract(body, boundary) {
|
|
|
583
1103
|
function readingSummaries(opts) {
|
|
584
1104
|
const { boundary } = opts;
|
|
585
1105
|
const summaries = [];
|
|
586
|
-
for (const reading of blocksAt(
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
1106
|
+
for (const reading of blocksAt(
|
|
1107
|
+
opts.body,
|
|
1108
|
+
boundary.readingBlocks,
|
|
1109
|
+
opts.scope
|
|
1110
|
+
)) {
|
|
1111
|
+
for (const metricType of metricsRead(
|
|
1112
|
+
reading,
|
|
1113
|
+
boundary.identifies,
|
|
591
1114
|
opts.scope
|
|
592
1115
|
)) {
|
|
593
1116
|
summaries.push({
|
|
@@ -598,7 +1121,7 @@ function readingSummaries(opts) {
|
|
|
598
1121
|
exportName: null
|
|
599
1122
|
},
|
|
600
1123
|
identity: {
|
|
601
|
-
name: `${opts.resourceType}.${opts.label}#${summaries.length}`,
|
|
1124
|
+
name: `${opts.scope.namePrefix}${opts.resourceType}.${opts.label}#${summaries.length}`,
|
|
602
1125
|
exportPath: null,
|
|
603
1126
|
boundaryBinding: metricBinding({
|
|
604
1127
|
recognition: "terraform",
|
|
@@ -610,12 +1133,28 @@ function readingSummaries(opts) {
|
|
|
610
1133
|
transitions: [],
|
|
611
1134
|
gaps: [],
|
|
612
1135
|
confidence: { source: "declared", level: "high" },
|
|
613
|
-
metadata: {
|
|
1136
|
+
metadata: {
|
|
1137
|
+
metricReading: metricReading(reading, boundary, opts.scope)
|
|
1138
|
+
}
|
|
614
1139
|
});
|
|
615
1140
|
}
|
|
616
1141
|
}
|
|
617
1142
|
return summaries;
|
|
618
1143
|
}
|
|
1144
|
+
var READING_IDENTITIES = {
|
|
1145
|
+
attributes: (reading, identity, scope) => [
|
|
1146
|
+
metricTypeFrom(identity.template, reading, scope)
|
|
1147
|
+
],
|
|
1148
|
+
query: (reading, identity, scope) => metricTypesIn(
|
|
1149
|
+
stringOf(valueAt(reading, identity.attribute)),
|
|
1150
|
+
identity.key,
|
|
1151
|
+
scope
|
|
1152
|
+
)
|
|
1153
|
+
};
|
|
1154
|
+
function metricsRead(reading, identity, scope) {
|
|
1155
|
+
const read = READING_IDENTITIES[identity.from];
|
|
1156
|
+
return read(reading, identity, scope);
|
|
1157
|
+
}
|
|
619
1158
|
function metricTypesIn(query, identityKey, scope) {
|
|
620
1159
|
if (query === null) {
|
|
621
1160
|
return [null];
|
|
@@ -627,23 +1166,212 @@ function metricTypesIn(query, identityKey, scope) {
|
|
|
627
1166
|
const found = filterValuesFor(parsed.query, identityKey).map((value) => namePattern(value, scope)).filter((value) => value !== null);
|
|
628
1167
|
return found.length === 0 ? [null] : found;
|
|
629
1168
|
}
|
|
630
|
-
function metricReading(reading, boundary) {
|
|
1169
|
+
function metricReading(reading, boundary, scope) {
|
|
631
1170
|
const compares = boundary.comparesTo;
|
|
632
1171
|
const comparesTo = compares !== void 0 && valueAt(reading, compares.attribute) !== void 0 ? compares.whenSet : void 0;
|
|
633
1172
|
const reduces = boundary.reducesTo;
|
|
634
|
-
const reducesTo = meaningOf(reading, reduces);
|
|
1173
|
+
const reducesTo = meaningOf(reading, reduces, scope);
|
|
635
1174
|
return {
|
|
636
1175
|
...comparesTo !== void 0 ? { comparesTo } : {},
|
|
637
1176
|
...reducesTo !== void 0 ? { reducesTo } : {},
|
|
638
1177
|
...reduces === void 0 ? {} : { reduction: { setting: reduces.attribute, leaves: reduces.means } }
|
|
639
1178
|
};
|
|
640
1179
|
}
|
|
641
|
-
function
|
|
1180
|
+
function deployableSummaries(opts) {
|
|
1181
|
+
const { boundary, label } = opts;
|
|
1182
|
+
return deployedProcesses(opts.body, boundary, opts.scope).map(
|
|
1183
|
+
(process) => deployableSummary({
|
|
1184
|
+
...opts,
|
|
1185
|
+
boundary,
|
|
1186
|
+
process,
|
|
1187
|
+
instanceName: process.containerName === null ? label : ecsContainerInstanceName(label, process.containerName)
|
|
1188
|
+
})
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
function deployableSummary(opts) {
|
|
1192
|
+
const { boundary, instanceName, process } = opts;
|
|
1193
|
+
const declared = declaredEnv(process.body, boundary.env ?? [], opts.scope);
|
|
1194
|
+
const platform = boundary.platformEnvVars ?? PLATFORM_INJECTED_ENV_VARS[boundary.deploymentTarget];
|
|
1195
|
+
const code = codePointer(process.body, boundary, opts.scope);
|
|
1196
|
+
const image = attributePattern(
|
|
1197
|
+
process.body,
|
|
1198
|
+
boundary.code?.imageAttribute,
|
|
1199
|
+
opts.scope
|
|
1200
|
+
);
|
|
1201
|
+
const runtime = attributePattern(
|
|
1202
|
+
process.body,
|
|
1203
|
+
boundary.runtimeAttribute,
|
|
1204
|
+
opts.scope
|
|
1205
|
+
);
|
|
1206
|
+
const prefix = opts.scope.namePrefix;
|
|
1207
|
+
const deployableUnit = {
|
|
1208
|
+
deploymentTarget: boundary.deploymentTarget,
|
|
1209
|
+
instanceName: `${prefix}${instanceName}`
|
|
1210
|
+
};
|
|
1211
|
+
return {
|
|
1212
|
+
kind: "library",
|
|
1213
|
+
location: {
|
|
1214
|
+
file: opts.sourceFile,
|
|
1215
|
+
range: { start: 1, end: 1 },
|
|
1216
|
+
exportName: null
|
|
1217
|
+
},
|
|
1218
|
+
identity: {
|
|
1219
|
+
name: `${prefix}${opts.resourceType}.${instanceName.replace("/", "#")}`,
|
|
1220
|
+
exportPath: null,
|
|
1221
|
+
boundaryBinding: runtimeConfigBinding({
|
|
1222
|
+
recognition: "terraform",
|
|
1223
|
+
...deployableUnit
|
|
1224
|
+
}),
|
|
1225
|
+
deployableUnit
|
|
1226
|
+
},
|
|
1227
|
+
inputs: [],
|
|
1228
|
+
transitions: [],
|
|
1229
|
+
gaps: [],
|
|
1230
|
+
confidence: { source: "declared", level: "high" },
|
|
1231
|
+
metadata: withRuntimeContractMetadata(
|
|
1232
|
+
{
|
|
1233
|
+
codeScope: declaredCodeScope(
|
|
1234
|
+
opts.options.codeScopes?.[deployableUnit.instanceName],
|
|
1235
|
+
code.entry
|
|
1236
|
+
)
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
envVars: [.../* @__PURE__ */ new Set([...declared.names, ...platform])].sort(),
|
|
1240
|
+
envVarSources: envVarSources(declared.names, platform),
|
|
1241
|
+
...Object.keys(declared.targets).length > 0 ? { envVarTargets: declared.targets } : {},
|
|
1242
|
+
...Object.keys(declared.values).length > 0 ? { envVarValues: declared.values } : {},
|
|
1243
|
+
...code.entryPoint !== null ? { entryPoint: code.entryPoint } : {},
|
|
1244
|
+
...image !== null ? { image } : {},
|
|
1245
|
+
...runtime !== null ? { runtime } : {}
|
|
1246
|
+
}
|
|
1247
|
+
)
|
|
1248
|
+
};
|
|
1249
|
+
}
|
|
1250
|
+
function declaredCodeScope(directory, entry) {
|
|
1251
|
+
const written = entry !== null ? { entry } : {};
|
|
1252
|
+
if (directory !== void 0) {
|
|
1253
|
+
return { kind: "codeUri", path: directory, ...written };
|
|
1254
|
+
}
|
|
1255
|
+
return { kind: "unknown", ...written };
|
|
1256
|
+
}
|
|
1257
|
+
function envVarSources(declared, platform) {
|
|
1258
|
+
const sources = {};
|
|
1259
|
+
for (const name of declared) {
|
|
1260
|
+
sources[name] = "template";
|
|
1261
|
+
}
|
|
1262
|
+
for (const name of platform) {
|
|
1263
|
+
sources[name] ??= "platform";
|
|
1264
|
+
}
|
|
1265
|
+
return sources;
|
|
1266
|
+
}
|
|
1267
|
+
function deployedProcesses(body, boundary, scope) {
|
|
1268
|
+
const containers = boundary.containers;
|
|
1269
|
+
if (containers === void 0) {
|
|
1270
|
+
return [{ body, containerName: null }];
|
|
1271
|
+
}
|
|
1272
|
+
return blocksAt(body, containers.blocks, scope).map((container) => ({
|
|
1273
|
+
body: container,
|
|
1274
|
+
containerName: containers.nameAttribute === void 0 ? null : stringOf(container[containers.nameAttribute])
|
|
1275
|
+
}));
|
|
1276
|
+
}
|
|
1277
|
+
var HANDLER_MODULE = {
|
|
1278
|
+
"module.export": (written) => parseHandler(written)?.modulePath ?? null,
|
|
1279
|
+
name: () => null
|
|
1280
|
+
};
|
|
1281
|
+
function codePointer(body, boundary, scope) {
|
|
1282
|
+
const spec = boundary.code?.handler;
|
|
1283
|
+
const written = attributePattern(body, spec?.attribute, scope);
|
|
1284
|
+
if (spec === void 0 || written === null) {
|
|
1285
|
+
return { entryPoint: null, entry: null };
|
|
1286
|
+
}
|
|
1287
|
+
return {
|
|
1288
|
+
entryPoint: written,
|
|
1289
|
+
entry: hasNameHole(written) ? null : HANDLER_MODULE[spec.spelling](written)
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
var ENV_READERS = {
|
|
1293
|
+
map: (body, declaration, into, scope) => {
|
|
1294
|
+
const stated = asRecord(valueAt(body, declaration.attribute)) ?? {};
|
|
1295
|
+
for (const [name, value] of Object.entries(stated)) {
|
|
1296
|
+
setVariable(into, name, stringOf(value), scope);
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
entries: (body, declaration, into, scope) => {
|
|
1300
|
+
for (const entry of blocksAt(body, declaration.block.split("."), scope)) {
|
|
1301
|
+
const name = stringOf(entry[declaration.nameAttribute]);
|
|
1302
|
+
if (name === null) {
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
setVariable(
|
|
1306
|
+
into,
|
|
1307
|
+
name,
|
|
1308
|
+
attributeText(entry, declaration.valueAttribute),
|
|
1309
|
+
scope
|
|
1310
|
+
);
|
|
1311
|
+
setTarget(
|
|
1312
|
+
into,
|
|
1313
|
+
name,
|
|
1314
|
+
attributeText(entry, declaration.secretAttribute),
|
|
1315
|
+
scope
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
function declaredEnv(body, declarations, scope) {
|
|
1321
|
+
const declared = { names: [], values: {}, targets: {} };
|
|
1322
|
+
for (const declaration of declarations) {
|
|
1323
|
+
const read = ENV_READERS[declaration.style];
|
|
1324
|
+
read(body, declaration, declared, scope);
|
|
1325
|
+
}
|
|
1326
|
+
return declared;
|
|
1327
|
+
}
|
|
1328
|
+
function setVariable(into, name, written, scope) {
|
|
1329
|
+
if (!into.names.includes(name)) {
|
|
1330
|
+
into.names.push(name);
|
|
1331
|
+
}
|
|
1332
|
+
if (written === null) {
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
const pattern = namePattern(written, scope);
|
|
1336
|
+
if (pattern !== null) {
|
|
1337
|
+
into.values[name] = pattern;
|
|
1338
|
+
}
|
|
1339
|
+
setTarget(into, name, written, scope);
|
|
1340
|
+
}
|
|
1341
|
+
function setTarget(into, name, written, scope) {
|
|
1342
|
+
const target = written === null ? null : referencedResource(written, scope);
|
|
1343
|
+
if (target !== null) {
|
|
1344
|
+
into.targets[name] = { kind: "ref", logicalId: target };
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
function attributeText(body, attribute) {
|
|
1348
|
+
return attribute === void 0 ? null : stringOf(valueAt(body, attribute));
|
|
1349
|
+
}
|
|
1350
|
+
function attributePattern(body, attribute, scope) {
|
|
1351
|
+
return attribute === void 0 ? null : namePattern(valueAt(body, attribute), scope);
|
|
1352
|
+
}
|
|
1353
|
+
var MEANING_KEYS = {
|
|
1354
|
+
value: (stated, keys) => keys.find((key) => key === stated),
|
|
1355
|
+
prefix: (stated, keys) => keys.find((key) => stated.startsWith(key))
|
|
1356
|
+
};
|
|
1357
|
+
function meaningOf(body, spec, scope) {
|
|
642
1358
|
if (spec === void 0) {
|
|
643
1359
|
return void 0;
|
|
644
1360
|
}
|
|
645
|
-
const
|
|
646
|
-
|
|
1361
|
+
const value = valueAt(body, spec.attribute);
|
|
1362
|
+
if (value === void 0) {
|
|
1363
|
+
return spec.whenUnset;
|
|
1364
|
+
}
|
|
1365
|
+
const written = stringOf(value);
|
|
1366
|
+
const stated = written === null ? null : resolveReferences(written, scope);
|
|
1367
|
+
if (stated === null || stated.includes("${")) {
|
|
1368
|
+
return void 0;
|
|
1369
|
+
}
|
|
1370
|
+
const key = MEANING_KEYS[spec.matches ?? "value"](
|
|
1371
|
+
stated,
|
|
1372
|
+
Object.keys(spec.means)
|
|
1373
|
+
);
|
|
1374
|
+
return key === void 0 ? void 0 : spec.means[key];
|
|
647
1375
|
}
|
|
648
1376
|
function valueAt(body, path2) {
|
|
649
1377
|
const steps = path2.split(".");
|
|
@@ -658,15 +1386,24 @@ function valueAt(body, path2) {
|
|
|
658
1386
|
}
|
|
659
1387
|
return current[last];
|
|
660
1388
|
}
|
|
661
|
-
function blocksAt(body, blocks) {
|
|
1389
|
+
function blocksAt(body, blocks, scope) {
|
|
662
1390
|
let found = [body];
|
|
663
1391
|
for (const block of blocks) {
|
|
664
|
-
found = found.flatMap(
|
|
665
|
-
(record
|
|
666
|
-
|
|
1392
|
+
found = found.flatMap((record) => [
|
|
1393
|
+
...nestedRecords(record[block], scope),
|
|
1394
|
+
...dynamicBlocks(record, block, scope)
|
|
1395
|
+
]);
|
|
667
1396
|
}
|
|
668
1397
|
return found;
|
|
669
1398
|
}
|
|
1399
|
+
function nestedRecords(value, scope) {
|
|
1400
|
+
const iterated = iteratedRecords(value, scope);
|
|
1401
|
+
if (iterated !== null) {
|
|
1402
|
+
return iterated;
|
|
1403
|
+
}
|
|
1404
|
+
const stated = typeof value === "string" ? jsonAttributeValue(value) : value;
|
|
1405
|
+
return arrayOf(stated).map(asRecord).filter((nested) => nested !== null);
|
|
1406
|
+
}
|
|
670
1407
|
function keyedShapes(body, boundary) {
|
|
671
1408
|
if (boundary.identifies === void 0) {
|
|
672
1409
|
return [{ accessPath: null, keyFields: [], serves: null }];
|
|
@@ -772,22 +1509,11 @@ function namePattern(value, scope) {
|
|
|
772
1509
|
typeof value === "string" ? resolveReferences(value, scope) : value
|
|
773
1510
|
);
|
|
774
1511
|
}
|
|
775
|
-
function arrayOf(value) {
|
|
776
|
-
if (value === void 0 || value === null) {
|
|
777
|
-
return [];
|
|
778
|
-
}
|
|
779
|
-
return Array.isArray(value) ? value : [value];
|
|
780
|
-
}
|
|
781
|
-
function asRecord(value) {
|
|
782
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
|
|
783
|
-
}
|
|
784
|
-
function stringOf(value) {
|
|
785
|
-
return typeof value === "string" ? value : null;
|
|
786
|
-
}
|
|
787
1512
|
export {
|
|
788
1513
|
filterCalls,
|
|
789
1514
|
filterTerms,
|
|
790
1515
|
filterValuesFor,
|
|
1516
|
+
jsonAttributeValue,
|
|
791
1517
|
parseFilterQuery,
|
|
792
1518
|
terraformFileToSummaries,
|
|
793
1519
|
terraformToSummaries
|