@intentius/chant 0.0.16 → 0.0.22
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/bin/chant +4 -1
- package/package.json +20 -1
- package/src/build.test.ts +4 -2
- package/src/build.ts +3 -0
- package/src/builder.test.ts +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -3
- package/src/cli/commands/build.ts +5 -12
- package/src/cli/commands/diff.test.ts +2 -1
- package/src/cli/commands/diff.ts +2 -1
- package/src/cli/commands/init-lexicon.test.ts +0 -9
- package/src/cli/commands/init-lexicon.ts +0 -94
- package/src/cli/commands/init.ts +2 -20
- package/src/cli/handlers/build.ts +3 -3
- package/src/cli/handlers/lint.ts +2 -2
- package/src/cli/handlers/spell.ts +396 -0
- package/src/cli/handlers/state.ts +230 -0
- package/src/cli/lsp/server.test.ts +4 -0
- package/src/cli/main.ts +37 -3
- package/src/cli/mcp/server.test.ts +13 -9
- package/src/cli/mcp/server.ts +220 -6
- package/src/cli/mcp/tools/build.ts +2 -1
- package/src/cli/plugins.ts +1 -1
- package/src/cli/reporters/stylish.test.ts +2 -2
- package/src/cli/reporters/stylish.ts +1 -1
- package/src/codegen/docs.ts +13 -2
- package/src/composite.test.ts +1 -1
- package/src/config.ts +4 -0
- package/src/declarable.test.ts +2 -1
- package/src/declarable.ts +1 -1
- package/src/discovery/graph.test.ts +40 -0
- package/src/discovery/import.test.ts +5 -5
- package/src/discovery/resolve.test.ts +20 -0
- package/src/discovery/resolve.ts +2 -2
- package/src/index.ts +2 -0
- package/src/lexicon.ts +24 -0
- package/src/lint/rule-options.test.ts +3 -3
- package/src/lint/rule-registry.test.ts +1 -1
- package/src/lint/rules/composite-scope.ts +1 -1
- package/src/serializer-walker.ts +2 -1
- package/src/spell/discovery.ts +183 -0
- package/src/spell/index.ts +3 -0
- package/src/spell/prompt.ts +133 -0
- package/src/spell/types.ts +89 -0
- package/src/state/digest.ts +88 -0
- package/src/state/git.ts +317 -0
- package/src/state/index.ts +4 -0
- package/src/state/snapshot.ts +179 -0
- package/src/state/types.ts +59 -0
- package/src/types.ts +2 -1
- package/src/utils.test.ts +16 -3
- package/src/utils.ts +31 -1
- package/src/validation.test.ts +11 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/getting-started.mdx +0 -6
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/lint-rules.mdx +0 -6
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/serialization.mdx +0 -6
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/actions/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/composites/.gitkeep +0 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/coverage.ts +0 -11
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/generator.ts +0 -10
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/parser.ts +0 -10
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/post-synth/.gitkeep +0 -0
|
@@ -12,6 +12,7 @@ describe("buildDependencyGraph", () => {
|
|
|
12
12
|
|
|
13
13
|
test("returns graph with no dependencies for single entity", () => {
|
|
14
14
|
const entity: Declarable = {
|
|
15
|
+
lexicon: "test",
|
|
15
16
|
entityType: "test",
|
|
16
17
|
[DECLARABLE_MARKER]: true,
|
|
17
18
|
};
|
|
@@ -25,11 +26,13 @@ describe("buildDependencyGraph", () => {
|
|
|
25
26
|
|
|
26
27
|
test("returns graph with no dependencies for multiple unrelated entities", () => {
|
|
27
28
|
const entity1: Declarable = {
|
|
29
|
+
lexicon: "test",
|
|
28
30
|
entityType: "test",
|
|
29
31
|
[DECLARABLE_MARKER]: true,
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
const entity2: Declarable = {
|
|
35
|
+
lexicon: "test",
|
|
33
36
|
entityType: "test",
|
|
34
37
|
[DECLARABLE_MARKER]: true,
|
|
35
38
|
};
|
|
@@ -47,11 +50,13 @@ describe("buildDependencyGraph", () => {
|
|
|
47
50
|
|
|
48
51
|
test("detects dependency from AttrRef", () => {
|
|
49
52
|
const parent: Declarable = {
|
|
53
|
+
lexicon: "test",
|
|
50
54
|
entityType: "parent",
|
|
51
55
|
[DECLARABLE_MARKER]: true,
|
|
52
56
|
};
|
|
53
57
|
|
|
54
58
|
const child: Declarable & { ref: AttrRef } = {
|
|
59
|
+
lexicon: "test",
|
|
55
60
|
entityType: "child",
|
|
56
61
|
[DECLARABLE_MARKER]: true,
|
|
57
62
|
ref: new AttrRef(parent, "someAttr"),
|
|
@@ -71,11 +76,13 @@ describe("buildDependencyGraph", () => {
|
|
|
71
76
|
|
|
72
77
|
test("detects dependency from direct Declarable reference", () => {
|
|
73
78
|
const entity1: Declarable = {
|
|
79
|
+
lexicon: "test",
|
|
74
80
|
entityType: "type1",
|
|
75
81
|
[DECLARABLE_MARKER]: true,
|
|
76
82
|
};
|
|
77
83
|
|
|
78
84
|
const entity2: Declarable & { dependency: Declarable } = {
|
|
85
|
+
lexicon: "test",
|
|
79
86
|
entityType: "type2",
|
|
80
87
|
[DECLARABLE_MARKER]: true,
|
|
81
88
|
dependency: entity1,
|
|
@@ -95,16 +102,19 @@ describe("buildDependencyGraph", () => {
|
|
|
95
102
|
|
|
96
103
|
test("detects multiple dependencies from one entity", () => {
|
|
97
104
|
const entity1: Declarable = {
|
|
105
|
+
lexicon: "test",
|
|
98
106
|
entityType: "type1",
|
|
99
107
|
[DECLARABLE_MARKER]: true,
|
|
100
108
|
};
|
|
101
109
|
|
|
102
110
|
const entity2: Declarable = {
|
|
111
|
+
lexicon: "test",
|
|
103
112
|
entityType: "type2",
|
|
104
113
|
[DECLARABLE_MARKER]: true,
|
|
105
114
|
};
|
|
106
115
|
|
|
107
116
|
const entity3: Declarable & { dep1: Declarable; dep2: Declarable } = {
|
|
117
|
+
lexicon: "test",
|
|
108
118
|
entityType: "type3",
|
|
109
119
|
[DECLARABLE_MARKER]: true,
|
|
110
120
|
dep1: entity1,
|
|
@@ -126,11 +136,13 @@ describe("buildDependencyGraph", () => {
|
|
|
126
136
|
|
|
127
137
|
test("detects dependencies in nested objects", () => {
|
|
128
138
|
const entity1: Declarable = {
|
|
139
|
+
lexicon: "test",
|
|
129
140
|
entityType: "type1",
|
|
130
141
|
[DECLARABLE_MARKER]: true,
|
|
131
142
|
};
|
|
132
143
|
|
|
133
144
|
const entity2: Declarable & { nested: { deep: Declarable } } = {
|
|
145
|
+
lexicon: "test",
|
|
134
146
|
entityType: "type2",
|
|
135
147
|
[DECLARABLE_MARKER]: true,
|
|
136
148
|
nested: {
|
|
@@ -150,16 +162,19 @@ describe("buildDependencyGraph", () => {
|
|
|
150
162
|
|
|
151
163
|
test("detects dependencies in arrays", () => {
|
|
152
164
|
const entity1: Declarable = {
|
|
165
|
+
lexicon: "test",
|
|
153
166
|
entityType: "type1",
|
|
154
167
|
[DECLARABLE_MARKER]: true,
|
|
155
168
|
};
|
|
156
169
|
|
|
157
170
|
const entity2: Declarable = {
|
|
171
|
+
lexicon: "test",
|
|
158
172
|
entityType: "type2",
|
|
159
173
|
[DECLARABLE_MARKER]: true,
|
|
160
174
|
};
|
|
161
175
|
|
|
162
176
|
const entity3: Declarable & { deps: Declarable[] } = {
|
|
177
|
+
lexicon: "test",
|
|
163
178
|
entityType: "type3",
|
|
164
179
|
[DECLARABLE_MARKER]: true,
|
|
165
180
|
deps: [entity1, entity2],
|
|
@@ -179,16 +194,19 @@ describe("buildDependencyGraph", () => {
|
|
|
179
194
|
|
|
180
195
|
test("detects mixed AttrRef and Declarable dependencies", () => {
|
|
181
196
|
const entity1: Declarable = {
|
|
197
|
+
lexicon: "test",
|
|
182
198
|
entityType: "type1",
|
|
183
199
|
[DECLARABLE_MARKER]: true,
|
|
184
200
|
};
|
|
185
201
|
|
|
186
202
|
const entity2: Declarable = {
|
|
203
|
+
lexicon: "test",
|
|
187
204
|
entityType: "type2",
|
|
188
205
|
[DECLARABLE_MARKER]: true,
|
|
189
206
|
};
|
|
190
207
|
|
|
191
208
|
const entity3: Declarable & { ref: AttrRef; dep: Declarable } = {
|
|
209
|
+
lexicon: "test",
|
|
192
210
|
entityType: "type3",
|
|
193
211
|
[DECLARABLE_MARKER]: true,
|
|
194
212
|
ref: new AttrRef(entity1, "attr"),
|
|
@@ -209,17 +227,20 @@ describe("buildDependencyGraph", () => {
|
|
|
209
227
|
|
|
210
228
|
test("handles transitive dependencies correctly", () => {
|
|
211
229
|
const entity1: Declarable = {
|
|
230
|
+
lexicon: "test",
|
|
212
231
|
entityType: "type1",
|
|
213
232
|
[DECLARABLE_MARKER]: true,
|
|
214
233
|
};
|
|
215
234
|
|
|
216
235
|
const entity2: Declarable & { dep: Declarable } = {
|
|
236
|
+
lexicon: "test",
|
|
217
237
|
entityType: "type2",
|
|
218
238
|
[DECLARABLE_MARKER]: true,
|
|
219
239
|
dep: entity1,
|
|
220
240
|
};
|
|
221
241
|
|
|
222
242
|
const entity3: Declarable & { dep: Declarable } = {
|
|
243
|
+
lexicon: "test",
|
|
223
244
|
entityType: "type3",
|
|
224
245
|
[DECLARABLE_MARKER]: true,
|
|
225
246
|
dep: entity2,
|
|
@@ -241,16 +262,19 @@ describe("buildDependencyGraph", () => {
|
|
|
241
262
|
|
|
242
263
|
test("ignores non-entity declarables", () => {
|
|
243
264
|
const entity: Declarable = {
|
|
265
|
+
lexicon: "test",
|
|
244
266
|
entityType: "test",
|
|
245
267
|
[DECLARABLE_MARKER]: true,
|
|
246
268
|
};
|
|
247
269
|
|
|
248
270
|
const notInEntities: Declarable = {
|
|
271
|
+
lexicon: "test",
|
|
249
272
|
entityType: "external",
|
|
250
273
|
[DECLARABLE_MARKER]: true,
|
|
251
274
|
};
|
|
252
275
|
|
|
253
276
|
const entityWithExternal: Declarable & { dep: Declarable } = {
|
|
277
|
+
lexicon: "test",
|
|
254
278
|
entityType: "test",
|
|
255
279
|
[DECLARABLE_MARKER]: true,
|
|
256
280
|
dep: notInEntities,
|
|
@@ -267,11 +291,13 @@ describe("buildDependencyGraph", () => {
|
|
|
267
291
|
|
|
268
292
|
test("ignores AttrRef with parent not in entities", () => {
|
|
269
293
|
const externalParent: Declarable = {
|
|
294
|
+
lexicon: "test",
|
|
270
295
|
entityType: "external",
|
|
271
296
|
[DECLARABLE_MARKER]: true,
|
|
272
297
|
};
|
|
273
298
|
|
|
274
299
|
const entity: Declarable & { ref: AttrRef } = {
|
|
300
|
+
lexicon: "test",
|
|
275
301
|
entityType: "test",
|
|
276
302
|
[DECLARABLE_MARKER]: true,
|
|
277
303
|
ref: new AttrRef(externalParent, "attr"),
|
|
@@ -285,11 +311,13 @@ describe("buildDependencyGraph", () => {
|
|
|
285
311
|
|
|
286
312
|
test("handles circular references without infinite loop", () => {
|
|
287
313
|
const entity1: Declarable & { other?: Declarable } = {
|
|
314
|
+
lexicon: "test",
|
|
288
315
|
entityType: "type1",
|
|
289
316
|
[DECLARABLE_MARKER]: true,
|
|
290
317
|
};
|
|
291
318
|
|
|
292
319
|
const entity2: Declarable & { other: Declarable } = {
|
|
320
|
+
lexicon: "test",
|
|
293
321
|
entityType: "type2",
|
|
294
322
|
[DECLARABLE_MARKER]: true,
|
|
295
323
|
other: entity1,
|
|
@@ -309,6 +337,7 @@ describe("buildDependencyGraph", () => {
|
|
|
309
337
|
|
|
310
338
|
test("handles self-reference without infinite loop", () => {
|
|
311
339
|
const entity: Declarable & { self?: Declarable } = {
|
|
340
|
+
lexicon: "test",
|
|
312
341
|
entityType: "test",
|
|
313
342
|
[DECLARABLE_MARKER]: true,
|
|
314
343
|
};
|
|
@@ -329,6 +358,7 @@ describe("buildDependencyGraph", () => {
|
|
|
329
358
|
bool: boolean;
|
|
330
359
|
nul: null;
|
|
331
360
|
} = {
|
|
361
|
+
lexicon: "test",
|
|
332
362
|
entityType: "test",
|
|
333
363
|
[DECLARABLE_MARKER]: true,
|
|
334
364
|
str: "value",
|
|
@@ -348,6 +378,7 @@ describe("buildDependencyGraph", () => {
|
|
|
348
378
|
// parent is the resource itself (e.g. bucket.arn, bucket.bucketName).
|
|
349
379
|
// These are not real dependencies — they're just attribute accessors.
|
|
350
380
|
const resource: Declarable & { arn?: AttrRef; bucketName?: AttrRef } = {
|
|
381
|
+
lexicon: "test",
|
|
351
382
|
entityType: "AWS::S3::Bucket",
|
|
352
383
|
[DECLARABLE_MARKER]: true,
|
|
353
384
|
};
|
|
@@ -364,6 +395,7 @@ describe("buildDependencyGraph", () => {
|
|
|
364
395
|
// A resource has its own AttrRefs (self-pointing) AND a property that
|
|
365
396
|
// references a different entity. Only the cross-resource dep should appear.
|
|
366
397
|
const defaults: Declarable = {
|
|
398
|
+
lexicon: "test",
|
|
367
399
|
entityType: "AWS::S3::VersioningConfiguration",
|
|
368
400
|
[DECLARABLE_MARKER]: true,
|
|
369
401
|
};
|
|
@@ -372,6 +404,7 @@ describe("buildDependencyGraph", () => {
|
|
|
372
404
|
arn?: AttrRef;
|
|
373
405
|
versioningConfiguration?: Declarable;
|
|
374
406
|
} = {
|
|
407
|
+
lexicon: "test",
|
|
375
408
|
entityType: "AWS::S3::Bucket",
|
|
376
409
|
[DECLARABLE_MARKER]: true,
|
|
377
410
|
};
|
|
@@ -391,6 +424,7 @@ describe("buildDependencyGraph", () => {
|
|
|
391
424
|
|
|
392
425
|
test("ignores plain objects without markers", () => {
|
|
393
426
|
const entity: Declarable & { data: { key: string } } = {
|
|
427
|
+
lexicon: "test",
|
|
394
428
|
entityType: "test",
|
|
395
429
|
[DECLARABLE_MARKER]: true,
|
|
396
430
|
data: { key: "value" },
|
|
@@ -404,6 +438,7 @@ describe("buildDependencyGraph", () => {
|
|
|
404
438
|
|
|
405
439
|
test("handles AttrRef with garbage collected parent gracefully", () => {
|
|
406
440
|
const entity: Declarable & { ref: AttrRef } = {
|
|
441
|
+
lexicon: "test",
|
|
407
442
|
entityType: "test",
|
|
408
443
|
[DECLARABLE_MARKER]: true,
|
|
409
444
|
ref: new AttrRef({}, "attr"), // Using plain object that will be GC'd
|
|
@@ -418,6 +453,7 @@ describe("buildDependencyGraph", () => {
|
|
|
418
453
|
|
|
419
454
|
test("detects dependencies deeply nested in arrays and objects", () => {
|
|
420
455
|
const entity1: Declarable = {
|
|
456
|
+
lexicon: "test",
|
|
421
457
|
entityType: "type1",
|
|
422
458
|
[DECLARABLE_MARKER]: true,
|
|
423
459
|
};
|
|
@@ -425,6 +461,7 @@ describe("buildDependencyGraph", () => {
|
|
|
425
461
|
const entity2: Declarable & {
|
|
426
462
|
complex: { nested: { array: Array<{ item: Declarable }> } };
|
|
427
463
|
} = {
|
|
464
|
+
lexicon: "test",
|
|
428
465
|
entityType: "type2",
|
|
429
466
|
[DECLARABLE_MARKER]: true,
|
|
430
467
|
complex: {
|
|
@@ -446,17 +483,20 @@ describe("buildDependencyGraph", () => {
|
|
|
446
483
|
|
|
447
484
|
test("does not traverse into referenced declarables", () => {
|
|
448
485
|
const entity1: Declarable = {
|
|
486
|
+
lexicon: "test",
|
|
449
487
|
entityType: "type1",
|
|
450
488
|
[DECLARABLE_MARKER]: true,
|
|
451
489
|
};
|
|
452
490
|
|
|
453
491
|
const entity2: Declarable & { internal: { data: string } } = {
|
|
492
|
+
lexicon: "test",
|
|
454
493
|
entityType: "type2",
|
|
455
494
|
[DECLARABLE_MARKER]: true,
|
|
456
495
|
internal: { data: "should not traverse this" },
|
|
457
496
|
};
|
|
458
497
|
|
|
459
498
|
const entity3: Declarable & { dep: Declarable } = {
|
|
499
|
+
lexicon: "test",
|
|
460
500
|
entityType: "type3",
|
|
461
501
|
[DECLARABLE_MARKER]: true,
|
|
462
502
|
dep: entity2,
|
|
@@ -28,8 +28,8 @@ describe("importModule", () => {
|
|
|
28
28
|
|
|
29
29
|
const module = await importModule(filePath);
|
|
30
30
|
expect(module.default).toBeDefined();
|
|
31
|
-
expect(module.default.name).toBe("test");
|
|
32
|
-
expect(module.default.value).toBe(123);
|
|
31
|
+
expect((module.default as any).name).toBe("test");
|
|
32
|
+
expect((module.default as any).value).toBe(123);
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
35
|
|
|
@@ -125,7 +125,7 @@ describe("importModule", () => {
|
|
|
125
125
|
|
|
126
126
|
const module = await importModule(filePath);
|
|
127
127
|
expect(module.MyClass).toBeDefined();
|
|
128
|
-
const instance = new module.MyClass(100);
|
|
128
|
+
const instance = new (module.MyClass as any)(100);
|
|
129
129
|
expect(instance.getValue()).toBe(100);
|
|
130
130
|
});
|
|
131
131
|
});
|
|
@@ -142,8 +142,8 @@ describe("importModule", () => {
|
|
|
142
142
|
const module = await importModule(filePath);
|
|
143
143
|
expect(module.add).toBeInstanceOf(Function);
|
|
144
144
|
expect(module.multiply).toBeInstanceOf(Function);
|
|
145
|
-
expect(module.add(2, 3)).toBe(5);
|
|
146
|
-
expect(module.multiply(4, 5)).toBe(20);
|
|
145
|
+
expect((module.add as Function)(2, 3)).toBe(5);
|
|
146
|
+
expect((module.multiply as Function)(4, 5)).toBe(20);
|
|
147
147
|
});
|
|
148
148
|
});
|
|
149
149
|
|
|
@@ -8,11 +8,13 @@ import { LOGICAL_NAME_SYMBOL, getLogicalName } from "../utils";
|
|
|
8
8
|
describe("resolveAttrRefs", () => {
|
|
9
9
|
test("sets logical names on all entities", () => {
|
|
10
10
|
const entity1: Declarable = {
|
|
11
|
+
lexicon: "test",
|
|
11
12
|
entityType: "Test1",
|
|
12
13
|
[DECLARABLE_MARKER]: true,
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
const entity2: Declarable = {
|
|
17
|
+
lexicon: "test",
|
|
16
18
|
entityType: "Test2",
|
|
17
19
|
[DECLARABLE_MARKER]: true,
|
|
18
20
|
};
|
|
@@ -30,11 +32,13 @@ describe("resolveAttrRefs", () => {
|
|
|
30
32
|
|
|
31
33
|
test("resolves AttrRef with parent in entities collection", () => {
|
|
32
34
|
const parent: Declarable = {
|
|
35
|
+
lexicon: "test",
|
|
33
36
|
entityType: "Parent",
|
|
34
37
|
[DECLARABLE_MARKER]: true,
|
|
35
38
|
};
|
|
36
39
|
|
|
37
40
|
const child: Declarable & { ref: AttrRef } = {
|
|
41
|
+
lexicon: "test",
|
|
38
42
|
entityType: "Child",
|
|
39
43
|
[DECLARABLE_MARKER]: true,
|
|
40
44
|
ref: new AttrRef(parent, "Arn"),
|
|
@@ -55,11 +59,13 @@ describe("resolveAttrRefs", () => {
|
|
|
55
59
|
|
|
56
60
|
test("resolves multiple AttrRefs on same entity", () => {
|
|
57
61
|
const parent: Declarable = {
|
|
62
|
+
lexicon: "test",
|
|
58
63
|
entityType: "Parent",
|
|
59
64
|
[DECLARABLE_MARKER]: true,
|
|
60
65
|
};
|
|
61
66
|
|
|
62
67
|
const child: Declarable & { arn: AttrRef; name: AttrRef } = {
|
|
68
|
+
lexicon: "test",
|
|
63
69
|
entityType: "Child",
|
|
64
70
|
[DECLARABLE_MARKER]: true,
|
|
65
71
|
arn: new AttrRef(parent, "Arn"),
|
|
@@ -83,16 +89,19 @@ describe("resolveAttrRefs", () => {
|
|
|
83
89
|
|
|
84
90
|
test("resolves AttrRefs with different parents", () => {
|
|
85
91
|
const parent1: Declarable = {
|
|
92
|
+
lexicon: "test",
|
|
86
93
|
entityType: "Parent1",
|
|
87
94
|
[DECLARABLE_MARKER]: true,
|
|
88
95
|
};
|
|
89
96
|
|
|
90
97
|
const parent2: Declarable = {
|
|
98
|
+
lexicon: "test",
|
|
91
99
|
entityType: "Parent2",
|
|
92
100
|
[DECLARABLE_MARKER]: true,
|
|
93
101
|
};
|
|
94
102
|
|
|
95
103
|
const child: Declarable & { ref1: AttrRef; ref2: AttrRef } = {
|
|
104
|
+
lexicon: "test",
|
|
96
105
|
entityType: "Child",
|
|
97
106
|
[DECLARABLE_MARKER]: true,
|
|
98
107
|
ref1: new AttrRef(parent1, "Arn"),
|
|
@@ -119,6 +128,7 @@ describe("resolveAttrRefs", () => {
|
|
|
119
128
|
const parent = {}; // Not a Declarable, not in entities
|
|
120
129
|
|
|
121
130
|
const child: Declarable & { ref: AttrRef } = {
|
|
131
|
+
lexicon: "test",
|
|
122
132
|
entityType: "Child",
|
|
123
133
|
[DECLARABLE_MARKER]: true,
|
|
124
134
|
ref: new AttrRef(parent, "Arn"),
|
|
@@ -133,11 +143,13 @@ describe("resolveAttrRefs", () => {
|
|
|
133
143
|
|
|
134
144
|
test("handles entities with no AttrRefs", () => {
|
|
135
145
|
const entity1: Declarable = {
|
|
146
|
+
lexicon: "test",
|
|
136
147
|
entityType: "Test1",
|
|
137
148
|
[DECLARABLE_MARKER]: true,
|
|
138
149
|
};
|
|
139
150
|
|
|
140
151
|
const entity2: Declarable & { prop: string } = {
|
|
152
|
+
lexicon: "test",
|
|
141
153
|
entityType: "Test2",
|
|
142
154
|
[DECLARABLE_MARKER]: true,
|
|
143
155
|
prop: "value",
|
|
@@ -162,17 +174,20 @@ describe("resolveAttrRefs", () => {
|
|
|
162
174
|
|
|
163
175
|
test("resolves chain of entities with AttrRefs", () => {
|
|
164
176
|
const root: Declarable = {
|
|
177
|
+
lexicon: "test",
|
|
165
178
|
entityType: "Root",
|
|
166
179
|
[DECLARABLE_MARKER]: true,
|
|
167
180
|
};
|
|
168
181
|
|
|
169
182
|
const middle: Declarable & { rootRef: AttrRef } = {
|
|
183
|
+
lexicon: "test",
|
|
170
184
|
entityType: "Middle",
|
|
171
185
|
[DECLARABLE_MARKER]: true,
|
|
172
186
|
rootRef: new AttrRef(root, "Id"),
|
|
173
187
|
};
|
|
174
188
|
|
|
175
189
|
const leaf: Declarable & { middleRef: AttrRef } = {
|
|
190
|
+
lexicon: "test",
|
|
176
191
|
entityType: "Leaf",
|
|
177
192
|
[DECLARABLE_MARKER]: true,
|
|
178
193
|
middleRef: new AttrRef(middle, "Name"),
|
|
@@ -196,6 +211,7 @@ describe("resolveAttrRefs", () => {
|
|
|
196
211
|
|
|
197
212
|
test("handles entity referencing itself", () => {
|
|
198
213
|
const entity: Declarable & { selfRef: AttrRef } = {
|
|
214
|
+
lexicon: "test",
|
|
199
215
|
entityType: "SelfReferencing",
|
|
200
216
|
[DECLARABLE_MARKER]: true,
|
|
201
217
|
selfRef: null as unknown as AttrRef, // Will be set below
|
|
@@ -214,6 +230,7 @@ describe("resolveAttrRefs", () => {
|
|
|
214
230
|
|
|
215
231
|
test("preserves logical name symbol on entities", () => {
|
|
216
232
|
const entity: Declarable = {
|
|
233
|
+
lexicon: "test",
|
|
217
234
|
entityType: "Test",
|
|
218
235
|
[DECLARABLE_MARKER]: true,
|
|
219
236
|
};
|
|
@@ -228,6 +245,7 @@ describe("resolveAttrRefs", () => {
|
|
|
228
245
|
|
|
229
246
|
test("uses export name as logical name", () => {
|
|
230
247
|
const entity: Declarable = {
|
|
248
|
+
lexicon: "test",
|
|
231
249
|
entityType: "Test",
|
|
232
250
|
[DECLARABLE_MARKER]: true,
|
|
233
251
|
};
|
|
@@ -243,11 +261,13 @@ describe("resolveAttrRefs", () => {
|
|
|
243
261
|
|
|
244
262
|
test("handles complex attribute names in AttrRef", () => {
|
|
245
263
|
const parent: Declarable = {
|
|
264
|
+
lexicon: "test",
|
|
246
265
|
entityType: "Parent",
|
|
247
266
|
[DECLARABLE_MARKER]: true,
|
|
248
267
|
};
|
|
249
268
|
|
|
250
269
|
const child: Declarable & { ref: AttrRef } = {
|
|
270
|
+
lexicon: "test",
|
|
251
271
|
entityType: "Child",
|
|
252
272
|
[DECLARABLE_MARKER]: true,
|
|
253
273
|
ref: new AttrRef(parent, "Outputs.WebsiteURL"),
|
package/src/discovery/resolve.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Declarable } from "../declarable";
|
|
2
2
|
import { AttrRef } from "../attrref";
|
|
3
|
-
import { LOGICAL_NAME_SYMBOL, getAttributes } from "../utils";
|
|
3
|
+
import { LOGICAL_NAME_SYMBOL, getAttributes, isAttrRefLike } from "../utils";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Resolves all AttrRef instances in a collection of entities
|
|
@@ -22,7 +22,7 @@ export function resolveAttrRefs(entities: Map<string, Declarable>): void {
|
|
|
22
22
|
for (const attrName of attributes) {
|
|
23
23
|
const attrRef = (entity as unknown as Record<string, unknown>)[attrName];
|
|
24
24
|
|
|
25
|
-
if (attrRef
|
|
25
|
+
if (isAttrRefLike(attrRef)) {
|
|
26
26
|
const parent = attrRef.parent.deref();
|
|
27
27
|
|
|
28
28
|
if (!parent) {
|
package/src/index.ts
CHANGED
package/src/lexicon.ts
CHANGED
|
@@ -193,6 +193,30 @@ export interface LexiconPlugin {
|
|
|
193
193
|
|
|
194
194
|
/** Return MCP resource contributions */
|
|
195
195
|
mcpResources?(): McpResourceContribution[];
|
|
196
|
+
|
|
197
|
+
// State
|
|
198
|
+
/** Query deployed resources and return API metadata. Opt-in. */
|
|
199
|
+
describeResources?(options: {
|
|
200
|
+
environment: string;
|
|
201
|
+
buildOutput: string;
|
|
202
|
+
entityNames: string[];
|
|
203
|
+
}): Promise<Record<string, ResourceMetadata>>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Metadata about a deployed resource, returned by describeResources.
|
|
208
|
+
*/
|
|
209
|
+
export interface ResourceMetadata {
|
|
210
|
+
/** Entity type (e.g. AWS::S3::Bucket, K8s::Apps::Deployment) */
|
|
211
|
+
type: string;
|
|
212
|
+
/** Provider-assigned physical ID (ARN, resource ID, pod name) */
|
|
213
|
+
physicalId?: string;
|
|
214
|
+
/** Provider-specific status string */
|
|
215
|
+
status: string;
|
|
216
|
+
/** ISO timestamp of last update */
|
|
217
|
+
lastUpdated?: string;
|
|
218
|
+
/** Cloud-assigned output properties */
|
|
219
|
+
attributes?: Record<string, unknown>;
|
|
196
220
|
}
|
|
197
221
|
|
|
198
222
|
/**
|
|
@@ -2,7 +2,7 @@ import { describe, test, expect, beforeEach, afterEach } from "bun:test";
|
|
|
2
2
|
import { parseRuleConfig, loadConfig } from "./config";
|
|
3
3
|
import { fileDeclarableLimitRule } from "./rules/file-declarable-limit";
|
|
4
4
|
import * as ts from "typescript";
|
|
5
|
-
import type { LintContext } from "./rule";
|
|
5
|
+
import type { LintContext, RuleConfig } from "./rule";
|
|
6
6
|
import { writeFileSync, mkdirSync, rmSync } from "fs";
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
|
|
@@ -37,12 +37,12 @@ describe("parseRuleConfig", () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
test("parses [severity, options] tuple", () => {
|
|
40
|
-
const result = parseRuleConfig(["warning", { max: 12 }]);
|
|
40
|
+
const result = parseRuleConfig(["warning" as const, { max: 12 }]);
|
|
41
41
|
expect(result).toEqual({ severity: "warning", options: { max: 12 } });
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
test("throws for invalid tuple length", () => {
|
|
45
|
-
expect(() => parseRuleConfig([] as unknown as
|
|
45
|
+
expect(() => parseRuleConfig([] as unknown as RuleConfig)).toThrow(
|
|
46
46
|
/expected a severity string or \[severity, options\] tuple/
|
|
47
47
|
);
|
|
48
48
|
});
|
|
@@ -88,7 +88,7 @@ describe("buildRuleRegistry", () => {
|
|
|
88
88
|
|
|
89
89
|
test("falls back to rule ID when description is missing", () => {
|
|
90
90
|
const rule = mockRule("COR001");
|
|
91
|
-
delete (rule as Record<string, unknown>).description;
|
|
91
|
+
delete (rule as unknown as Record<string, unknown>).description;
|
|
92
92
|
|
|
93
93
|
const entries = buildRuleRegistry([rule]);
|
|
94
94
|
expect(entries[0].description).toBe("COR001");
|
|
@@ -14,7 +14,7 @@ export function isInsideCompositeFactory(node: ts.Node): boolean {
|
|
|
14
14
|
|
|
15
15
|
while (current) {
|
|
16
16
|
if (ts.isArrowFunction(current) || ts.isFunctionExpression(current)) {
|
|
17
|
-
const parent = current.parent;
|
|
17
|
+
const parent: ts.Node | undefined = current.parent;
|
|
18
18
|
if (parent && ts.isCallExpression(parent) && parent.arguments[0] === current) {
|
|
19
19
|
const callee = parent.expression;
|
|
20
20
|
if (isCompositeCallee(callee)) {
|
package/src/serializer-walker.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { Declarable } from "./declarable";
|
|
|
9
9
|
import { isPropertyDeclarable } from "./declarable";
|
|
10
10
|
import { INTRINSIC_MARKER } from "./intrinsic";
|
|
11
11
|
import { AttrRef } from "./attrref";
|
|
12
|
+
import { isAttrRefLike } from "./utils";
|
|
12
13
|
|
|
13
14
|
export interface SerializerVisitor {
|
|
14
15
|
/** Format an attribute reference (e.g. CFN Fn::GetAttr). */
|
|
@@ -33,7 +34,7 @@ export function walkValue(
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
// Handle AttrRef
|
|
36
|
-
if (value
|
|
37
|
+
if (isAttrRefLike(value)) {
|
|
37
38
|
const name = value.getLogicalName();
|
|
38
39
|
if (!name) {
|
|
39
40
|
throw new Error(
|