@intentius/chant-lexicon-azure 0.124.33 → 0.124.35

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 +1 @@
1
- {"version":3,"file":"read-client.d.ts","sourceRoot":"","sources":["../../src/api/read-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,2BAA2B,CAAC;AASxD,MAAM,WAAW,sBAAsB;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,qFAAqF;AACrF,qBAAa,cAAe,SAAQ,KAAK;IAGrC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;IAHxB,YACE,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA,EAIvB;CACF;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEhD;AAgBD,sFAAsF;AACtF,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,SAAsB,GAC/B,MAAM,CAIR;AAwBD,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAgB/F;AAED,8FAA8F;AAC9F,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,EAC/B,EAAE,EAAE,MAAM,EACV,UAAU,SAAsB,GAC/B,OAAO,CAAC,eAAe,CAAC,CAc1B;AAED,sHAAsH;AACtH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC,CAa1B"}
1
+ {"version":3,"file":"read-client.d.ts","sourceRoot":"","sources":["../../src/api/read-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,2BAA2B,CAAC;AASxD,MAAM,WAAW,sBAAsB;IACrC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,qFAAqF;AACrF,qBAAa,cAAe,SAAQ,KAAK;IAGrC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;IAHxB,YACE,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA,EAIvB;CACF;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEhD;AAgBD,sFAAsF;AACtF,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,SAAsB,GAC/B,MAAM,CAIR;AAwBD,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAgB/F;AAED,8FAA8F;AAC9F,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,EAC/B,EAAE,EAAE,MAAM,EACV,UAAU,SAAsB,GAC/B,OAAO,CAAC,eAAe,CAAC,CAc1B;AAED,sHAAsH;AACtH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC,CAa1B"}
@@ -18,6 +18,7 @@
18
18
  * wants a bearer token. The `az` CLI path remains for a signed read until
19
19
  * token acquisition lands here.
20
20
  */
21
+ import { armResourcePath } from "../arm-type.js";
21
22
  /** Default ARM host, matching the applier's. */
22
23
  const DEFAULT_ENDPOINT = "https://management.azure.com";
23
24
  /** floci-az's fixed local subscription, matching the applier's. */
@@ -53,7 +54,7 @@ const defaultHttp = async (method, url, body, signal) => {
53
54
  export function armResourceReadUrl(options, type, name, apiVersion = DEFAULT_API_VERSION) {
54
55
  const base = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
55
56
  const subscription = options.subscriptionId ?? DEFAULT_SUBSCRIPTION;
56
- return `${base}/subscriptions/${subscription}/resourceGroups/${options.resourceGroup}/providers/${type}/${name}?api-version=${apiVersion}`;
57
+ return `${base}/subscriptions/${subscription}/resourceGroups/${options.resourceGroup}/providers/${armResourcePath(type, name)}?api-version=${apiVersion}`;
57
58
  }
58
59
  /**
59
60
  * ARM's error envelope is `{ error: { code, message } }`. Reading the code
@@ -0,0 +1,21 @@
1
+ /**
2
+ * ARM resource types as ARM spells them (#4230).
3
+ *
4
+ * The lexicon's entity types come from the ARM schema ids, which join a child
5
+ * type to its parent with `_` (`Microsoft.Network/virtualNetworks_subnets`).
6
+ * ARM itself joins them with `/` (`Microsoft.Network/virtualNetworks/subnets`),
7
+ * in a template's `type`, in `resourceId()` and in the resource URL. Resource
8
+ * type segments never contain `_`, so every `_` after the namespace is a
9
+ * child separator.
10
+ */
11
+ /** `Microsoft.Network/virtualNetworks_subnets` → `Microsoft.Network/virtualNetworks/subnets`. Idempotent. */
12
+ export declare function armType(entityType: string): string;
13
+ /**
14
+ * The provider-relative path of one resource: each type segment followed by
15
+ * its name segment, as ARM's `resourceId()` builds it.
16
+ * `('Microsoft.Network/virtualNetworks/subnets', 'vnet/apps')` →
17
+ * `Microsoft.Network/virtualNetworks/vnet/subnets/apps`. A name with fewer
18
+ * segments than the type has kinds is joined as given.
19
+ */
20
+ export declare function armResourcePath(type: string, name: string): string;
21
+ //# sourceMappingURL=arm-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arm-type.d.ts","sourceRoot":"","sources":["../src/arm-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,6GAA6G;AAC7G,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAKlE"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * ARM resource types as ARM spells them (#4230).
3
+ *
4
+ * The lexicon's entity types come from the ARM schema ids, which join a child
5
+ * type to its parent with `_` (`Microsoft.Network/virtualNetworks_subnets`).
6
+ * ARM itself joins them with `/` (`Microsoft.Network/virtualNetworks/subnets`),
7
+ * in a template's `type`, in `resourceId()` and in the resource URL. Resource
8
+ * type segments never contain `_`, so every `_` after the namespace is a
9
+ * child separator.
10
+ */
11
+ /** `Microsoft.Network/virtualNetworks_subnets` → `Microsoft.Network/virtualNetworks/subnets`. Idempotent. */
12
+ export function armType(entityType) {
13
+ const slash = entityType.indexOf("/");
14
+ if (slash < 0)
15
+ return entityType;
16
+ return entityType.slice(0, slash + 1) + entityType.slice(slash + 1).replace(/_/g, "/");
17
+ }
18
+ /**
19
+ * The provider-relative path of one resource: each type segment followed by
20
+ * its name segment, as ARM's `resourceId()` builds it.
21
+ * `('Microsoft.Network/virtualNetworks/subnets', 'vnet/apps')` →
22
+ * `Microsoft.Network/virtualNetworks/vnet/subnets/apps`. A name with fewer
23
+ * segments than the type has kinds is joined as given.
24
+ */
25
+ export function armResourcePath(type, name) {
26
+ const [namespace, ...kinds] = armType(type).split("/");
27
+ const names = name.split("/");
28
+ if (kinds.length <= 1 || names.length !== kinds.length)
29
+ return `${armType(type)}/${name}`;
30
+ return [namespace, ...kinds.flatMap((kind, i) => [kind, names[i]])].join("/");
31
+ }
@@ -79,6 +79,15 @@ export declare const AZURE_SERVER_COMPUTED_NAMES: ReadonlySet<string>;
79
79
  * additive and needs no contract change.
80
80
  */
81
81
  export declare const AZURE_SERVICE_DEFAULTS: Record<string, Record<string, unknown>>;
82
+ /**
83
+ * The canonical key for one entry of `identity.userAssignedIdentities`
84
+ * (#4231): `<type>/<name>` lowercased, from either spelling. Source keys the
85
+ * map by `[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities',
86
+ * 'appId')]`; ARM answers with the evaluated id, `/subscriptions/.../providers/
87
+ * Microsoft.ManagedIdentity/userAssignedIdentities/appId`, in whatever case it
88
+ * stored. A key in neither form is left as it is.
89
+ */
90
+ export declare function userAssignedIdentityKey(key: string): string;
82
91
  /**
83
92
  * The azure lexicon's noise rules. Three classes, same as AWS: server-
84
93
  * populated fields (by name, unconditional), controller-injected boilerplate
@@ -1 +1 @@
1
- {"version":3,"file":"deep-observe.d.ts","sourceRoot":"","sources":["../src/deep-observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAIH,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,KAAK,EAGV,sBAAsB,EACtB,qBAAqB,EAGtB,MAAM,0BAA0B,CAAC;AA+DlC;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,MAAM,CASpD,CAAC;AAUH;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,EAAE,WAAW,CAAC,MAAM,CAI1D,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI1E,CAAC;AA+DF;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,EAAE,sBAyEzC,CAAC;AAIF,MAAM,WAAW,uBAAuB;IACtC,4FAA4F;IAC5F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/E;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CAkEhC"}
1
+ {"version":3,"file":"deep-observe.d.ts","sourceRoot":"","sources":["../src/deep-observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAIH,OAAO,EAA4B,KAAK,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACjF,OAAO,KAAK,EAGV,sBAAsB,EACtB,qBAAqB,EAGtB,MAAM,0BAA0B,CAAC;AA+DlC;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,MAAM,CASpD,CAAC;AAUH;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,EAAE,WAAW,CAAC,MAAM,CAI1D,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI1E,CAAC;AA+DF;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAM3D;AAyBD;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,EAAE,sBA2EzC,CAAC;AAIF,MAAM,WAAW,uBAAuB;IACtC,4FAA4F;IAC5F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;CAC/E;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CAkEhC"}
@@ -203,6 +203,47 @@ function lastSegment(pattern) {
203
203
  const dot = withoutIndex.lastIndexOf(".");
204
204
  return dot === -1 ? withoutIndex : withoutIndex.slice(dot + 1);
205
205
  }
206
+ /**
207
+ * The canonical key for one entry of `identity.userAssignedIdentities`
208
+ * (#4231): `<type>/<name>` lowercased, from either spelling. Source keys the
209
+ * map by `[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities',
210
+ * 'appId')]`; ARM answers with the evaluated id, `/subscriptions/.../providers/
211
+ * Microsoft.ManagedIdentity/userAssignedIdentities/appId`, in whatever case it
212
+ * stored. A key in neither form is left as it is.
213
+ */
214
+ export function userAssignedIdentityKey(key) {
215
+ const expr = /^\[\s*resourceId\(\s*'([^']+)'\s*,\s*'([^']+)'\s*\)\s*\]$/.exec(key);
216
+ if (expr)
217
+ return `${expr[1]}/${expr[2]}`.toLowerCase();
218
+ const at = key.toLowerCase().lastIndexOf("/providers/");
219
+ if (at !== -1 && key.startsWith("/subscriptions/"))
220
+ return key.slice(at + "/providers/".length).toLowerCase();
221
+ return key;
222
+ }
223
+ /**
224
+ * Rekey `identity.userAssignedIdentities` by {@link userAssignedIdentityKey},
225
+ * on a copy, and drop the `principalId` and `clientId` ARM fills in on each
226
+ * entry. Nobody declares them, and pruning them later would leave the live
227
+ * entry empty and gone, where source declares `{}`.
228
+ */
229
+ function rekeyUserAssignedIdentities(tree) {
230
+ const identity = tree.identity;
231
+ if (!identity || typeof identity !== "object" || Array.isArray(identity))
232
+ return tree;
233
+ const assigned = identity.userAssignedIdentities;
234
+ if (!assigned || typeof assigned !== "object" || Array.isArray(assigned))
235
+ return tree;
236
+ const rekeyed = {};
237
+ for (const [k, v] of Object.entries(assigned)) {
238
+ let entry = v;
239
+ if (entry && typeof entry === "object" && !Array.isArray(entry)) {
240
+ const { principalId: _p, clientId: _c, ...rest } = entry;
241
+ entry = rest;
242
+ }
243
+ rekeyed[userAssignedIdentityKey(k)] = entry;
244
+ }
245
+ return { ...tree, identity: { ...identity, userAssignedIdentities: rekeyed } };
246
+ }
206
247
  /**
207
248
  * The azure lexicon's noise rules. Three classes, same as AWS: server-
208
249
  * populated fields (by name, unconditional), controller-injected boilerplate
@@ -213,6 +254,7 @@ function lastSegment(pattern) {
213
254
  * since it is a plain object; only the *default empty map* needs a rule here).
214
255
  */
215
256
  export const azureDeepNormalizationHooks = {
257
+ prepare: (tree) => rekeyUserAssignedIdentities(tree),
216
258
  prune(node) {
217
259
  const name = lastSegment(node.pattern);
218
260
  if (AZURE_READ_ONLY_NAMES.has(name) || AZURE_UNCONDITIONAL_PRUNE_NAMES.has(name))
@@ -0,0 +1,21 @@
1
+ /**
2
+ * References written as ARM expressions, as graph edges (#4232).
3
+ *
4
+ * `chant graph` draws an edge for every `AttrRef` it finds in props, but an
5
+ * Azure reference is often an expression instead: `reference(resourceId(...))
6
+ * .customerId` and `listKeys(...)` for a value the resource has no attribute
7
+ * for, or a `resourceId(...)` used as an object key, which is how
8
+ * `identity.userAssignedIdentities` names an identity. The serializer already
9
+ * reads these to write `dependsOn` (./serializer.ts), with
10
+ * `findArmResourceRefs` (./lint/post-synth/arm-refs.ts). This module runs the
11
+ * same extraction over each prop as the template writes it and reports each
12
+ * reference to a declared resource through the plugin's `graphEdges` hook.
13
+ *
14
+ * A reference matches by resource name, never by type, as the serializer's
15
+ * inference does. A name nothing in the project declares is left out: it is
16
+ * an existing resource the template reads, not a dangling reference.
17
+ */
18
+ import type { Declarable } from "@intentius/chant/declarable";
19
+ import type { LexiconGraphEdges } from "@intentius/chant/graph-ir";
20
+ export declare function azureGraphEdges(entities: ReadonlyMap<string, Declarable>): LexiconGraphEdges | undefined;
21
+ //# sourceMappingURL=graph-edges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-edges.d.ts","sourceRoot":"","sources":["../src/graph-edges.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,KAAK,EAAE,iBAAiB,EAAyB,MAAM,2BAA2B,CAAC;AAQ1F,wBAAgB,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,iBAAiB,GAAG,SAAS,CA2CxG"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * References written as ARM expressions, as graph edges (#4232).
3
+ *
4
+ * `chant graph` draws an edge for every `AttrRef` it finds in props, but an
5
+ * Azure reference is often an expression instead: `reference(resourceId(...))
6
+ * .customerId` and `listKeys(...)` for a value the resource has no attribute
7
+ * for, or a `resourceId(...)` used as an object key, which is how
8
+ * `identity.userAssignedIdentities` names an identity. The serializer already
9
+ * reads these to write `dependsOn` (./serializer.ts), with
10
+ * `findArmResourceRefs` (./lint/post-synth/arm-refs.ts). This module runs the
11
+ * same extraction over each prop as the template writes it and reports each
12
+ * reference to a declared resource through the plugin's `graphEdges` hook.
13
+ *
14
+ * A reference matches by resource name, never by type, as the serializer's
15
+ * inference does. A name nothing in the project declares is left out: it is
16
+ * an existing resource the template reads, not a dangling reference.
17
+ */
18
+ import { isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
19
+ import { findArmResourceRefs } from "./lint/post-synth/arm-refs.js";
20
+ import { armResourceName, toArmValue } from "./serializer.js";
21
+ function isAzureResource(entity) {
22
+ return entity.lexicon === "azure" && isResourceDeclarable(entity) && !isPropertyDeclarable(entity);
23
+ }
24
+ export function azureGraphEdges(entities) {
25
+ const entityNames = new Map();
26
+ for (const [key, entity] of entities)
27
+ entityNames.set(entity, key);
28
+ // Template name -> entity key, for the resources whose name is a literal.
29
+ const byArmName = new Map();
30
+ for (const [key, entity] of entities) {
31
+ if (!isAzureResource(entity))
32
+ continue;
33
+ const name = armResourceName(entity, key, entityNames);
34
+ if (typeof name === "string")
35
+ byArmName.set(name, key);
36
+ }
37
+ if (byArmName.size === 0)
38
+ return undefined;
39
+ const references = [];
40
+ const seen = new Set();
41
+ const add = (from, to, viaAttr) => {
42
+ if (!to || to === from)
43
+ return;
44
+ const id = `${from}\0${to}\0${viaAttr}`;
45
+ if (seen.has(id))
46
+ return;
47
+ seen.add(id);
48
+ references.push({ from, to, kind: "ref", viaAttr });
49
+ };
50
+ for (const [key, entity] of entities) {
51
+ if (!isAzureResource(entity))
52
+ continue;
53
+ const props = entity.props;
54
+ if (props && typeof props === "object") {
55
+ for (const [attr, value] of Object.entries(props)) {
56
+ if (value === undefined)
57
+ continue;
58
+ for (const ref of findArmResourceRefs(toArmValue(value, entityNames))) {
59
+ add(key, byArmName.get(ref), attr);
60
+ }
61
+ }
62
+ }
63
+ // A child resource named "vnet/subnet" depends on "vnet", as the
64
+ // serializer infers for dependsOn.
65
+ const name = armResourceName(entity, key, entityNames);
66
+ if (typeof name === "string" && name.includes("/")) {
67
+ add(key, byArmName.get(name.split("/")[0]), "name");
68
+ }
69
+ }
70
+ return references.length > 0 ? { references } : undefined;
71
+ }
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "75da2d173ccd6370dd6b0574b09ce00ef7c611c3bf83b2bde013ea9d8625b239",
4
+ "manifest.json": "ba6e35e3b3333b4a65ea642cf28d7470635bf95437cfe4534290d34046ed862f",
5
5
  "meta.json": "d2521221ed616d882ded4296f0071685bd74e4b645d55dabc9685733c37dc419",
6
6
  "types/index.d.ts": "29631032bda9533849178968290e65d72250eab0b8bab424f2d9ef9b4e8546a4",
7
7
  "rules/hardcoded-location.ts": "0b895449c8c94066e81721c46be24249231029fe13b471477456df5c4e45b8bf",
8
8
  "rules/nsg-wildcard.ts": "8f9d44833ba364778c05731f84c20f00734bb0d40d9fd0d91645965d18a258ae",
9
9
  "rules/storage-https.ts": "d340ba970bd9b35304bd7d288a9e99610a7677e6c56af4846305a98d4df0fbbc",
10
- "rules/arm-refs.ts": "1ff54e29511d5b0572586d64d7c5ca476a13f401f14f89b4d601b01aa6dde891",
10
+ "rules/arm-refs.ts": "56521a730d566538cebd21037b7a291f4cdb909fa0d7213c80cac2633f154501",
11
11
  "rules/azr010.ts": "c61da411a3b325d49f2f790030f11aa4d518a671a23ff3d13be5206e2ddfedab",
12
12
  "rules/azr011.ts": "81b05ecaaf6e90a8afc692cd203d28eddf6126a3d7d9d4d4955e4aa6f2c7fa4c",
13
13
  "rules/azr012.ts": "e59ddbab64c0bddd2d65bd5cb66e976f858d86216825a18837653d14224a321e",
@@ -37,5 +37,5 @@
37
37
  "skills/chant-azure-patterns.md": "1a3bacfac612826b77332d2692a59195638db9f1b5e8ea2eda7579621d25f603",
38
38
  "skills/chant-azure-aks.md": "2d4e0098c1a22b54ffadd410564d9df0f3a04cb4eb7c6261f20ae33106e7aca8"
39
39
  },
40
- "composite": "990e7a1acfe3ab9a04d7fb6de37a56774ed9a172cbc590ba4f1d35956d96700c"
40
+ "composite": "53b437466dbe9ae27fc4351f26207a22250bf67910b00fb3b29cc8058cf1b3ef"
41
41
  }
@@ -43,6 +43,8 @@ export declare function parseArmTemplate(output: string | {
43
43
  * - [reference('myStorage')]
44
44
  * - [reference('myStorage').primaryEndpoints.blob]
45
45
  *
46
+ * Object keys are read as well as values.
47
+ *
46
48
  * Returns the set of resource names found in these references.
47
49
  */
48
50
  export declare function findArmResourceRefs(value: unknown): Set<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"arm-refs.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/arm-refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,GAAG,IAAI,CAWzF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAI/D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBhE"}
1
+ {"version":3,"file":"arm-refs.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/arm-refs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,GAAG,IAAI,CAWzF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAI/D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBhE"}
@@ -32,6 +32,8 @@ export function parseArmTemplate(output) {
32
32
  * - [reference('myStorage')]
33
33
  * - [reference('myStorage').primaryEndpoints.blob]
34
34
  *
35
+ * Object keys are read as well as values.
36
+ *
35
37
  * Returns the set of resource names found in these references.
36
38
  */
37
39
  export function findArmResourceRefs(value) {
@@ -89,7 +91,10 @@ function walkArmValue(value, refs) {
89
91
  }
90
92
  if (typeof value === "object") {
91
93
  const obj = value;
92
- for (const val of Object.values(obj)) {
94
+ for (const [key, val] of Object.entries(obj)) {
95
+ // A key can be an expression too: a user-assigned identity is named by
96
+ // its resource ID as the key of identity.userAssignedIdentities.
97
+ walkArmValue(key, refs);
93
98
  walkArmValue(val, refs);
94
99
  }
95
100
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azure",
3
- "version": "0.124.33",
3
+ "version": "0.124.35",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Azure",
6
6
  "intrinsics": [
@@ -9,6 +9,11 @@ export interface ArmResource {
9
9
  sku?: unknown;
10
10
  kind?: unknown;
11
11
  tags?: Record<string, string>;
12
+ identity?: unknown;
13
+ zones?: unknown;
14
+ plan?: unknown;
15
+ extendedLocation?: unknown;
16
+ managedBy?: unknown;
12
17
  dependsOn?: unknown;
13
18
  }
14
19
  /** Injectable HTTP client — mirrors the GCP applier so tests avoid the network. */
@@ -33,7 +38,11 @@ export interface ArmEvalCtx {
33
38
  }
34
39
  /** Evaluate an ARM expression string (`"[...]"`); a plain string is returned as-is. */
35
40
  export declare function evalArmString(s: string, ctx: ArmEvalCtx): Promise<unknown>;
36
- /** Recursively evaluate every string in a value against the ARM context. */
41
+ /**
42
+ * Recursively evaluate every string in a value against the ARM context,
43
+ * object keys included: ARM evaluates a property name too, and
44
+ * `identity.userAssignedIdentities` is keyed by `[resourceId(...)]` (#4231).
45
+ */
37
46
  export declare function evalArm(value: unknown, ctx: ArmEvalCtx): Promise<unknown>;
38
47
  /** Resource names this resource references via `resourceId('type','name')` / `reference('name')`. Pure. */
39
48
  export declare function armDependencies(resource: ArmResource, names: Set<string>): string[];
@@ -45,7 +54,7 @@ export declare function armDependencies(resource: ArmResource, names: Set<string
45
54
  export declare function orderArmResources(resources: ArmResource[]): ArmResource[];
46
55
  /** The ARM resource-ID PUT URL for a resource (name expression evaluated). */
47
56
  export declare function armResourceUrl(resource: ArmResource, ctx: ArmEvalCtx): Promise<string>;
48
- /** The ARM resource PUT body (location/properties/sku/kind/tags), expressions evaluated. */
57
+ /** The ARM resource PUT body: `location` and {@link ARM_BODY_FIELDS}, expressions evaluated. */
49
58
  export declare function armResourceBody(resource: ArmResource, ctx: ArmEvalCtx): Promise<Record<string, unknown>>;
50
59
  export interface AzApplyArgs {
51
60
  /** Path to a built ARM template (`deploymentTemplate.json`). */
@@ -1 +1 @@
1
- {"version":3,"file":"az-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/az-apply.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"az-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/az-apply.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { armResourcePath } from "../../arm-type.js";
2
3
  import { createHash } from "node:crypto";
3
4
  import { hasOwnershipMarker, OWNERSHIP_MANAGED_BY_VALUE } from "@intentius/chant/ownership";
4
5
  import { AZURE_TAG_OWNERSHIP_KEYS } from "../../ownership.js";
@@ -25,7 +26,11 @@ export async function evalArmString(s, ctx) {
25
26
  return s.slice(1); // escaped literal "["
26
27
  return new ArmExpr(s.slice(1, -1), ctx).parse();
27
28
  }
28
- /** Recursively evaluate every string in a value against the ARM context. */
29
+ /**
30
+ * Recursively evaluate every string in a value against the ARM context,
31
+ * object keys included: ARM evaluates a property name too, and
32
+ * `identity.userAssignedIdentities` is keyed by `[resourceId(...)]` (#4231).
33
+ */
29
34
  export async function evalArm(value, ctx) {
30
35
  if (typeof value === "string")
31
36
  return evalArmString(value, ctx);
@@ -34,7 +39,7 @@ export async function evalArm(value, ctx) {
34
39
  if (value && typeof value === "object") {
35
40
  const out = {};
36
41
  for (const [k, v] of Object.entries(value))
37
- out[k] = await evalArm(v, ctx);
42
+ out[String(await evalArmString(k, ctx))] = await evalArm(v, ctx);
38
43
  return out;
39
44
  }
40
45
  return value;
@@ -108,7 +113,7 @@ class ArmExpr {
108
113
  return { subscriptionId: this.ctx.subscriptionId, id: `/subscriptions/${this.ctx.subscriptionId}` };
109
114
  case "resourceId":
110
115
  // resourceId('Microsoft.X/y', 'name'[, 'child']) → the resource-id path.
111
- return `/subscriptions/${this.ctx.subscriptionId}/resourceGroups/${this.ctx.resourceGroup}/providers/${args.map(String).join("/")}`;
116
+ return `/subscriptions/${this.ctx.subscriptionId}/resourceGroups/${this.ctx.resourceGroup}/providers/${armResourcePath(String(args[0]), args.slice(1).map(String).join("/"))}`;
112
117
  case "reference": {
113
118
  // reference('name') → the runtime `properties` of an already-applied resource.
114
119
  const dep = this.ctx.deployed.get(String(args[0]));
@@ -224,21 +229,23 @@ export function orderArmResources(resources) {
224
229
  /** The ARM resource-ID PUT URL for a resource (name expression evaluated). */
225
230
  export async function armResourceUrl(resource, ctx) {
226
231
  const name = await evalArmString(resource.name, ctx);
227
- return `${ctx.base}/subscriptions/${ctx.subscriptionId}/resourceGroups/${ctx.resourceGroup}/providers/${resource.type}/${name}?api-version=${resource.apiVersion}`;
232
+ return `${ctx.base}/subscriptions/${ctx.subscriptionId}/resourceGroups/${ctx.resourceGroup}/providers/${armResourcePath(resource.type, String(name))}?api-version=${resource.apiVersion}`;
228
233
  }
229
- /** The ARM resource PUT body (location/properties/sku/kind/tags), expressions evaluated. */
234
+ /**
235
+ * The top-level ARM resource fields a PUT takes beside `location` (#4231). A
236
+ * field missing here is dropped from the body, which is how a declared
237
+ * `identity` came back absent.
238
+ */
239
+ const ARM_BODY_FIELDS = ["properties", "sku", "kind", "tags", "identity", "zones", "plan", "extendedLocation", "managedBy"];
240
+ /** The ARM resource PUT body: `location` and {@link ARM_BODY_FIELDS}, expressions evaluated. */
230
241
  export async function armResourceBody(resource, ctx) {
231
242
  const body = {};
232
243
  if (resource.location)
233
244
  body.location = await evalArmString(resource.location, ctx);
234
- if (resource.properties !== undefined)
235
- body.properties = await evalArm(resource.properties, ctx);
236
- if (resource.sku !== undefined)
237
- body.sku = await evalArm(resource.sku, ctx);
238
- if (resource.kind !== undefined)
239
- body.kind = await evalArm(resource.kind, ctx);
240
- if (resource.tags !== undefined)
241
- body.tags = await evalArm(resource.tags, ctx);
245
+ for (const field of ARM_BODY_FIELDS) {
246
+ if (resource[field] !== undefined)
247
+ body[field] = await evalArm(resource[field], ctx);
248
+ }
242
249
  return body;
243
250
  }
244
251
  /**
@@ -366,7 +373,7 @@ export async function listGroupResources(ctx, http = defaultHttp, signal) {
366
373
  }
367
374
  /** Idempotently delete one ARM resource by type/name/apiVersion. A 404 means it is already gone. */
368
375
  export async function deleteArmResource(type, name, apiVersion, ctx, http = defaultHttp, signal) {
369
- const url = `${ctx.base}/subscriptions/${ctx.subscriptionId}/resourceGroups/${ctx.resourceGroup}/providers/${type}/${name}?api-version=${apiVersion}`;
376
+ const url = `${ctx.base}/subscriptions/${ctx.subscriptionId}/resourceGroups/${ctx.resourceGroup}/providers/${armResourcePath(type, name)}?api-version=${apiVersion}`;
370
377
  const res = await http("DELETE", url, undefined, signal);
371
378
  if (res.status === 404)
372
379
  return { type, name, deleted: false };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,0BAA0B,CAAC;AA0B5E;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,aA4dzB,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,0BAA0B,CAAC;AA2B5E;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,aA8dzB,CAAC"}
package/dist/plugin.js CHANGED
@@ -9,6 +9,7 @@ import { join, dirname } from "path";
9
9
  import { fileURLToPath } from "url";
10
10
  import { moduleSourceDir } from "@intentius/chant/module-source";
11
11
  import { azureSerializer } from "./serializer.js";
12
+ import { azureGraphEdges } from "./graph-edges.js";
12
13
  import { hardcodedLocationRule } from "./lint/rules/hardcoded-location.js";
13
14
  import { storageHttpsRule } from "./lint/rules/storage-https.js";
14
15
  import { nsgWildcardRule } from "./lint/rules/nsg-wildcard.js";
@@ -30,6 +31,8 @@ export const azurePlugin = {
30
31
  emulator: FLOCI_AZ_EMULATOR,
31
32
  auditCatalog: () => azureAuditCatalog,
32
33
  serializer: azureSerializer,
34
+ // References written as ARM expressions (resourceId, reference, listKeys) as graph edges (#4232).
35
+ graphEdges: (entities) => azureGraphEdges(entities),
33
36
  lintRules() {
34
37
  return [hardcodedLocationRule, storageHttpsRule, nsgWildcardRule];
35
38
  },
@@ -56,6 +56,8 @@ export function parseArmTemplate(output: string | { primary: string }): ArmTempl
56
56
  * - [reference('myStorage')]
57
57
  * - [reference('myStorage').primaryEndpoints.blob]
58
58
  *
59
+ * Object keys are read as well as values.
60
+ *
59
61
  * Returns the set of resource names found in these references.
60
62
  */
61
63
  export function findArmResourceRefs(value: unknown): Set<string> {
@@ -121,7 +123,10 @@ function walkArmValue(value: unknown, refs: Set<string>): void {
121
123
 
122
124
  if (typeof value === "object") {
123
125
  const obj = value as Record<string, unknown>;
124
- for (const val of Object.values(obj)) {
126
+ for (const [key, val] of Object.entries(obj)) {
127
+ // A key can be an expression too: a user-assigned identity is named by
128
+ // its resource ID as the key of identity.userAssignedIdentities.
129
+ walkArmValue(key, refs);
125
130
  walkArmValue(val, refs);
126
131
  }
127
132
  }
@@ -31,6 +31,15 @@ export declare function getApiVersion(resourceType: string): string;
31
31
  * reads `reference()` by the ARM name.
32
32
  */
33
33
  export declare function armAttrExpression(entityNames: Map<Declarable, string>, name: string, attr: string): string;
34
+ /** A prop value as the template writes it: references become bracket expressions. */
35
+ export declare function toArmValue(value: unknown, entityNames: Map<Declarable, string>): unknown;
36
+ /**
37
+ * The name a resource takes in the template: its `name` prop, or the entity
38
+ * key when it sets none. `findArmResourceRefs` reports references by this
39
+ * name, so the dependsOn inference below and the graph edges
40
+ * (./graph-edges.ts) both resolve a reference through it.
41
+ */
42
+ export declare function armResourceName(entity: Declarable, key: string, entityNames: Map<Declarable, string>): unknown;
34
43
  /**
35
44
  * Azure ARM template serializer implementation.
36
45
  */
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,6BAA6B,CAAC;AAE7E,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AA8ElG,wBAAgB,4BAA4B,CAAC,GAAG,GAAE,MAAqB,GAAG,MAAM,EAAE,CAKjF;AAED,wBAAgB,eAAe,CAAC,UAAU,GAAE,MAAM,EAAmC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAwB1G;AAcD,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWzE;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAO1D;AA6BD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK1G;AAiWD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,UA+C7B,CAAC"}
1
+ {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,6BAA6B,CAAC;AAE7E,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AA8ElG,wBAAgB,4BAA4B,CAAC,GAAG,GAAE,MAAqB,GAAG,MAAM,EAAE,CAKjF;AAED,wBAAgB,eAAe,CAAC,UAAU,GAAE,MAAM,EAAmC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAwB1G;AAcD,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAWzE;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAO1D;AA6BD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK1G;AAoDD,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,OAAO,CAExF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,OAAO,CAK9G;AA2SD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,UA+C7B,CAAC"}
@@ -9,6 +9,7 @@
9
9
  * - ARM bracket expression references
10
10
  * - parameters, outputs sections
11
11
  */
12
+ import { armType } from "./arm-type.js";
12
13
  import { isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
13
14
  import { ownershipEntries } from "@intentius/chant/ownership";
14
15
  import { AZURE_TAG_OWNERSHIP_KEYS } from "./ownership.js";
@@ -144,7 +145,7 @@ function entityNamed(entityNames, name) {
144
145
  function armResourceId(entityNames, name) {
145
146
  const entity = entityNamed(entityNames, name);
146
147
  const { args } = armNameArgs(entity, name);
147
- return entity ? `resourceId('${entity.entityType}', ${args})` : `resourceId(${args})`;
148
+ return entity ? `resourceId('${armType(entity.entityType)}', ${args})` : `resourceId(${args})`;
148
149
  }
149
150
  /**
150
151
  * An attribute of an export. `id` is `resourceId()`, since `reference()`
@@ -208,9 +209,22 @@ function isPlainObject(value) {
208
209
  const proto = Object.getPrototypeOf(value);
209
210
  return proto === Object.prototype || proto === null;
210
211
  }
211
- function toArmValue(value, entityNames) {
212
+ /** A prop value as the template writes it: references become bracket expressions. */
213
+ export function toArmValue(value, entityNames) {
212
214
  return walkValue(value, entityNames, armVisitor(entityNames));
213
215
  }
216
+ /**
217
+ * The name a resource takes in the template: its `name` prop, or the entity
218
+ * key when it sets none. `findArmResourceRefs` reports references by this
219
+ * name, so the dependsOn inference below and the graph edges
220
+ * (./graph-edges.ts) both resolve a reference through it.
221
+ */
222
+ export function armResourceName(entity, key, entityNames) {
223
+ const props = isResourceDeclarable(entity) && typeof entity.props === "object" && entity.props !== null
224
+ ? entity.props
225
+ : undefined;
226
+ return props?.name !== undefined ? toArmValue(props.name, entityNames) : key;
227
+ }
214
228
  /**
215
229
  * Serialize entities into an ARM template object.
216
230
  */
@@ -237,7 +251,7 @@ function serializeToTemplate(entities, outputs, ownership, warnings = []) {
237
251
  emittedTypes.push("Microsoft.Resources/deployments");
238
252
  }
239
253
  else if (!isPropertyDeclarable(entity)) {
240
- emittedTypes.push(entity.entityType);
254
+ emittedTypes.push(armType(entity.entityType));
241
255
  }
242
256
  }
243
257
  const templateScope = pinnedScope ?? resolveTemplateScope(emittedTypes);
@@ -307,9 +321,9 @@ function serializeToTemplate(entities, outputs, ownership, warnings = []) {
307
321
  : undefined;
308
322
  const apiVersion = (typeof attrs0?.apiVersion === "string") ? attrs0.apiVersion : getApiVersion(resourceType);
309
323
  const resource = {
310
- type: resourceType,
324
+ type: armType(resourceType),
311
325
  apiVersion,
312
- name: name,
326
+ name: armResourceName(entity, name, entityNames),
313
327
  };
314
328
  // Extract all props
315
329
  if (isResourceDeclarable(entity) && typeof entity.props === "object" && entity.props !== null) {
@@ -323,7 +337,7 @@ function serializeToTemplate(entities, outputs, ownership, warnings = []) {
323
337
  resource[key] = toArmValue(value, entityNames);
324
338
  }
325
339
  else if (key === "name") {
326
- resource.name = toArmValue(value, entityNames);
340
+ // Set above by armResourceName.
327
341
  }
328
342
  else if (key === "properties" && isPlainObject(value)) {
329
343
  // The generated types take ARM's own `properties` object, so
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-azure",
3
- "version": "0.124.33",
3
+ "version": "0.124.35",
4
4
  "description": "Azure lexicon for chant — declarative IaC in TypeScript",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -74,6 +74,6 @@
74
74
  "typescript": "^5.9.3"
75
75
  },
76
76
  "peerDependencies": {
77
- "@intentius/chant": "^0.124.33"
77
+ "@intentius/chant": "^0.124.35"
78
78
  }
79
79
  }