@intentius/chant-lexicon-aws 0.27.0 → 0.28.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "9deee69f09d9cec4b892b1bb97a0dad9b3354bc794cab5acad4d6fa237a0a396",
4
+ "manifest.json": "32758168db27150b8085c2f4f7a0c5c5d4926107c82da54b50050aa8addaf002",
5
5
  "meta.json": "8d6445b7ea5f7803c83cf49daab639b1df66388c1512f466b888b0ad57f60a9a",
6
6
  "types/index.d.ts": "da899ff303a18a7fdb270570bce784703283d4081f806e69ac8c9dfd4bac7c50",
7
7
  "rules/hardcoded-region.ts": "5a0eaf7ab391231fe6cd51426ece29539cb4b36f31c8dd060956638fed55722a",
@@ -59,5 +59,5 @@
59
59
  "skills/chant-aws-eks.md": "8789255709ff004ad0a875fd5999edcdc66fc6e33d710db058d9f42703bcfdfe",
60
60
  "skills/chant-aws-carve-terraform.md": "f4c6fe1c702250665f90d82b3bdaba5ea50ae75e380a8ae321dad6eb1c39683e"
61
61
  },
62
- "composite": "60753f891c32263de6e6c9116b8acef09ddcc19d0fd93472d2008aec80052cfe"
62
+ "composite": "0f0089bc6c3816dc668a4e12e03450e54a7ddf9aee278dc693f79075c900d9ff"
63
63
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aws",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "AWS",
6
6
  "intrinsics": [
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AAiYlG;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAoE3B,CAAC"}
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AA0YlG;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAoE3B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-aws",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "AWS CloudFormation lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -80,7 +80,7 @@
80
80
  "typescript": "^5.9.3"
81
81
  },
82
82
  "peerDependencies": {
83
- "@intentius/chant": "^0.27.0",
83
+ "@intentius/chant": "^0.28.0",
84
84
  "typescript": "^5.9.3"
85
85
  }
86
86
  }
@@ -2,10 +2,18 @@ import { describe, test, expect, vi, beforeEach } from "vitest";
2
2
 
3
3
  // AWS exportResources reaches the cloud through the runtime adapter's spawn
4
4
  // (not node:child_process), so the I/O seam is the runtime-adapter module.
5
+ // Partial mock (`importOriginal`) rather than a full replacement: this module
6
+ // is reachable — via `@intentius/chant`'s own root barrel, not just this
7
+ // test's direct imports — from other real exports the plugin/import path
8
+ // touches (e.g. `moduleDir`, which `../../lint/config.ts` calls at module
9
+ // scope), so replacing the whole module wholesale breaks anything that
10
+ // transitively loads one of those, for reasons entirely unrelated to what
11
+ // this test is mocking (`spawn`).
5
12
  const spawnMock = vi.fn();
6
- vi.mock("@intentius/chant/runtime-adapter", () => ({
7
- getRuntime: () => ({ spawn: spawnMock }),
8
- }));
13
+ vi.mock("@intentius/chant/runtime-adapter", async (importOriginal) => {
14
+ const actual = await importOriginal<typeof import("@intentius/chant/runtime-adapter")>();
15
+ return { ...actual, getRuntime: () => ({ ...actual.getRuntime(), spawn: spawnMock }) };
16
+ });
9
17
 
10
18
  import { awsPlugin } from "../plugin";
11
19
 
@@ -11,10 +11,18 @@ import { mkdtempSync, rmSync, readdirSync, readFileSync } from "node:fs";
11
11
  import { tmpdir } from "node:os";
12
12
  import { join } from "node:path";
13
13
 
14
+ // Partial mock (`importOriginal`) rather than a full replacement: this module
15
+ // is reachable — via `@intentius/chant`'s own root barrel, not just this
16
+ // test's direct imports — from other real exports the plugin/import path
17
+ // touches (e.g. `moduleDir`, which `../../lint/config.ts` calls at module
18
+ // scope), so replacing the whole module wholesale breaks anything that
19
+ // transitively loads one of those, for reasons entirely unrelated to what
20
+ // this test is mocking (`spawn`).
14
21
  const spawnMock = vi.fn();
15
- vi.mock("@intentius/chant/runtime-adapter", () => ({
16
- getRuntime: () => ({ spawn: spawnMock }),
17
- }));
22
+ vi.mock("@intentius/chant/runtime-adapter", async (importOriginal) => {
23
+ const actual = await importOriginal<typeof import("@intentius/chant/runtime-adapter")>();
24
+ return { ...actual, getRuntime: () => ({ ...actual.getRuntime(), spawn: spawnMock }) };
25
+ });
18
26
 
19
27
  const { awsPlugin } = await import("./plugin");
20
28
  const { liveImportFromPlugins } = await import("@intentius/chant/cli/commands/import");
@@ -941,3 +941,31 @@ describe("default tags serialization", () => {
941
941
  expect(template.Resources.MyBucket.Properties.Tags).toBeUndefined();
942
942
  });
943
943
  });
944
+
945
+
946
+ describe("stack output exports and literals", () => {
947
+ test("exportName emits Output.Export.Name", () => {
948
+ const bucket = new MockBucket({ BucketName: "my-bucket" });
949
+ const arnRef = new AttrRef(bucket, "Arn");
950
+ arnRef._setLogicalName("MyBucket");
951
+ const output = stackOutput(arnRef, { exportName: "my-stack-BucketArn" });
952
+
953
+ const entities = new Map<string, Declarable>();
954
+ entities.set("MyBucket", bucket);
955
+ entities.set("MyBucketArn", output as unknown as Declarable);
956
+
957
+ const template = JSON.parse(awsSerializer.serialize(entities) as string);
958
+ expect(template.Outputs.MyBucketArn.Export).toEqual({ Name: "my-stack-BucketArn" });
959
+ });
960
+
961
+ test("literal output serializes its string value with export", () => {
962
+ const output = stackOutput("22", { lexicon: "aws", exportName: "my-stack-OpenSSHPort" });
963
+
964
+ const entities = new Map<string, Declarable>();
965
+ entities.set("OpenSSHPort", output as unknown as Declarable);
966
+
967
+ const template = JSON.parse(awsSerializer.serialize(entities) as string);
968
+ expect(template.Outputs.OpenSSHPort.Value).toBe("22");
969
+ expect(template.Outputs.OpenSSHPort.Export).toEqual({ Name: "my-stack-OpenSSHPort" });
970
+ });
971
+ });
package/src/serializer.ts CHANGED
@@ -341,7 +341,10 @@ function serializeToTemplate(
341
341
  // AttrRef type resolves across the workspace/published boundary.
342
342
  const ref: unknown = stackOutput.sourceRef;
343
343
  let value: unknown;
344
- if (isAttrRefLike(ref)) {
344
+ if (typeof ref === "string") {
345
+ // Literal output (constants a stack publishes, e.g. a port number).
346
+ value = ref;
347
+ } else if (isAttrRefLike(ref)) {
345
348
  const logicalName = ref.getLogicalName();
346
349
  if (!logicalName) continue;
347
350
  // Use Ref for primary identifier ("Id") since not all resources
@@ -359,6 +362,12 @@ function serializeToTemplate(
359
362
  if (stackOutput.description) {
360
363
  output.Description = stackOutput.description;
361
364
  }
365
+ // Read defensively: a project may pair this lexicon with an older
366
+ // published core whose StackOutput type predates exportName.
367
+ const exportName = (stackOutput as { exportName?: string }).exportName;
368
+ if (exportName) {
369
+ output.Export = { Name: exportName };
370
+ }
362
371
  template.Outputs[name] = output;
363
372
  }
364
373
  }