@intentius/chant-lexicon-azure 0.124.32 → 0.124.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/codegen/generate-typescript.js +1 -1
  2. package/dist/codegen/generate.js +1 -1
  3. package/dist/generated/index.d.ts.map +1 -1
  4. package/dist/generated/index.js +1 -1
  5. package/dist/health.d.ts +42 -0
  6. package/dist/health.d.ts.map +1 -0
  7. package/dist/health.js +137 -0
  8. package/dist/integrity.json +5 -5
  9. package/dist/lint/post-synth/arm-refs.d.ts +1 -0
  10. package/dist/lint/post-synth/arm-refs.d.ts.map +1 -1
  11. package/dist/lint/post-synth/arm-refs.js +5 -3
  12. package/dist/manifest.json +1 -1
  13. package/dist/meta.json +6 -1
  14. package/dist/okf/types/containerApps.md +5 -0
  15. package/dist/plugin.d.ts.map +1 -1
  16. package/dist/plugin.js +6 -0
  17. package/dist/rules/arm-refs.ts +5 -3
  18. package/dist/serializer.d.ts +7 -0
  19. package/dist/serializer.d.ts.map +1 -1
  20. package/dist/serializer.js +115 -29
  21. package/dist/spec/parse.d.ts.map +1 -1
  22. package/dist/spec/parse.js +10 -0
  23. package/dist/types/index.d.ts +5 -0
  24. package/package.json +2 -2
  25. package/src/attributes.test.ts +17 -0
  26. package/src/codegen/generate-typescript.ts +1 -1
  27. package/src/codegen/generate.ts +1 -1
  28. package/src/generated/index.d.ts +5 -0
  29. package/src/generated/index.ts +1 -1
  30. package/src/generated/lexicon-azure.json +6 -1
  31. package/src/health.test.ts +66 -0
  32. package/src/health.ts +162 -0
  33. package/src/lint/post-synth/arm-refs.ts +5 -3
  34. package/src/lint/post-synth/post-synth.test.ts +1 -1
  35. package/src/plugin.ts +8 -0
  36. package/src/serializer-names.test.ts +83 -0
  37. package/src/serializer-outputs.test.ts +65 -0
  38. package/src/serializer.ts +110 -28
  39. package/src/spec/parse.test.ts +15 -0
  40. package/src/spec/parse.ts +10 -0
@@ -313,7 +313,7 @@ export const connections_flows = /* @__PURE__ */ createResource("Microsoft.Azure
313
313
  export const connectors = /* @__PURE__ */ createResource("Microsoft.Workloads/connectors", "azure", { "id": "id" });
314
314
  export const connectors_acssBackups = /* @__PURE__ */ createResource("Microsoft.Workloads/connectors_acssBackups", "azure", { "id": "id" });
315
315
  export const Consumptionbudgets = /* @__PURE__ */ createResource("Microsoft.Consumption/budgets", "azure", { "id": "id" });
316
- export const containerApps = /* @__PURE__ */ createResource("Microsoft.App/containerApps", "azure", { "id": "id" });
316
+ export const containerApps = /* @__PURE__ */ createResource("Microsoft.App/containerApps", "azure", { "id": "id", "configuration_ingress_fqdn": "configuration.ingress.fqdn", "latestRevisionFqdn": "latestRevisionFqdn", "latestRevisionName": "latestRevisionName", "outboundIpAddresses": "outboundIpAddresses", "provisioningState": "provisioningState" });
317
317
  export const containerApps_authConfigs = /* @__PURE__ */ createResource("Microsoft.App/containerApps_authConfigs", "azure", { "id": "id" });
318
318
  export const containerApps_resiliencyPolicies = /* @__PURE__ */ createResource("Microsoft.App/containerApps_resiliencyPolicies", "azure", { "id": "id" });
319
319
  export const containerApps_sourcecontrols = /* @__PURE__ */ createResource("Microsoft.App/containerApps_sourcecontrols", "azure", { "id": "id" });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Runtime health for Azure (#4177): Container Apps.
3
+ *
4
+ * For each declared `Microsoft.App/containerApps`, GET the app (a 404 is
5
+ * absent), then its revisions, and judge the active ones by their running and
6
+ * health state. Same transport and endpoint rule as `describe-resources.ts`, so
7
+ * `AZURE_ENDPOINT_URL` (floci-az) answers.
8
+ *
9
+ * When the revisions list cannot be read but the app carries a
10
+ * `runningStatus` (newer API versions do), that answers instead, and the detail
11
+ * says so.
12
+ */
13
+ import type { HealthEntry, HealthReadOptions, HealthReadResult } from "@intentius/chant/lexicon";
14
+ import type { AzHttp } from "./op/activities/az-apply.js";
15
+ export declare const AZURE_HEALTH_TYPES: readonly ["Microsoft.App/containerApps"];
16
+ export interface AzureHealthOptions extends HealthReadOptions {
17
+ http?: AzHttp;
18
+ now?: () => Date;
19
+ }
20
+ /** The fields of one revision that decide health. */
21
+ export interface RevisionState {
22
+ name?: string;
23
+ properties?: {
24
+ active?: boolean;
25
+ runningState?: string;
26
+ healthState?: string;
27
+ replicas?: number;
28
+ trafficWeight?: number;
29
+ lastActiveTime?: string;
30
+ createdTime?: string;
31
+ };
32
+ }
33
+ /**
34
+ * The active revisions' verdict: unhealthy if none is active or any active one
35
+ * failed, degraded, stopped or reports `Unhealthy`; healthy if every active one
36
+ * is running and none is unhealthy; otherwise unknown (still provisioning).
37
+ */
38
+ export declare function containerAppEntry(revisions: RevisionState[], observedAt: string): HealthEntry;
39
+ /** An app's own `runningStatus`, for when its revisions cannot be listed. */
40
+ export declare function appRunningStatusEntry(runningStatus: string, observedAt: string): HealthEntry;
41
+ export declare function observeHealthAzure(options: AzureHealthOptions): Promise<HealthReadResult>;
42
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAoB,MAAM,0BAA0B,CAAC;AAGnH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAEvD,eAAO,MAAM,kBAAkB,YAAI,6BAA6B,CAAU,CAAC;AAK3E,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAKD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,aAAa,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAyB7F;AAED,6EAA6E;AAC7E,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAS5F;AAiCD,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyC/F"}
package/dist/health.js ADDED
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Runtime health for Azure (#4177): Container Apps.
3
+ *
4
+ * For each declared `Microsoft.App/containerApps`, GET the app (a 404 is
5
+ * absent), then its revisions, and judge the active ones by their running and
6
+ * health state. Same transport and endpoint rule as `describe-resources.ts`, so
7
+ * `AZURE_ENDPOINT_URL` (floci-az) answers.
8
+ *
9
+ * When the revisions list cannot be read but the app carries a
10
+ * `runningStatus` (newer API versions do), that answers instead, and the detail
11
+ * says so.
12
+ */
13
+ import { armResourceReadUrl, AzureReadError, getResource, isNotFound } from "./api/read-client.js";
14
+ import { classifyArmFailure } from "./describe-resources.js";
15
+ export const AZURE_HEALTH_TYPES = ["Microsoft.App/containerApps"];
16
+ /** The Container Apps API version whose revisions carry `runningState` and `healthState`. */
17
+ const CONTAINER_APPS_API_VERSION = "2024-03-01";
18
+ const RUNNING = new Set(["running", "runningatmaxscale"]);
19
+ const DOWN = new Set(["failed", "degraded", "stopped"]);
20
+ /**
21
+ * The active revisions' verdict: unhealthy if none is active or any active one
22
+ * failed, degraded, stopped or reports `Unhealthy`; healthy if every active one
23
+ * is running and none is unhealthy; otherwise unknown (still provisioning).
24
+ */
25
+ export function containerAppEntry(revisions, observedAt) {
26
+ const type = "Microsoft.App/containerApps";
27
+ const active = revisions.filter((r) => r.properties?.active === true);
28
+ if (active.length === 0) {
29
+ return { type, state: "unhealthy", detail: `no active revision (${revisions.length} revision(s))`, observedAt };
30
+ }
31
+ const describe = (r) => {
32
+ const p = r.properties ?? {};
33
+ const bits = [p.runningState ?? "no running state"];
34
+ if (p.healthState && p.healthState !== "None")
35
+ bits.push(p.healthState);
36
+ if (typeof p.replicas === "number")
37
+ bits.push(`${p.replicas} replica(s)`);
38
+ if (typeof p.trafficWeight === "number")
39
+ bits.push(`${p.trafficWeight}% traffic`);
40
+ return `${r.name ?? "revision"} ${bits.join(", ")}`;
41
+ };
42
+ const detail = active.map(describe).join("; ");
43
+ const running = (r) => RUNNING.has((r.properties?.runningState ?? "").toLowerCase());
44
+ const down = (r) => DOWN.has((r.properties?.runningState ?? "").toLowerCase()) || r.properties?.healthState?.toLowerCase() === "unhealthy";
45
+ const checkedAt = active
46
+ .map((r) => r.properties?.lastActiveTime ?? r.properties?.createdTime)
47
+ .filter((t) => typeof t === "string")
48
+ .sort()
49
+ .pop();
50
+ const state = active.some(down) ? "unhealthy" : active.every(running) ? "healthy" : "unknown";
51
+ return { type, state, detail, observedAt, ...(checkedAt ? { checkedAt } : {}) };
52
+ }
53
+ /** An app's own `runningStatus`, for when its revisions cannot be listed. */
54
+ export function appRunningStatusEntry(runningStatus, observedAt) {
55
+ const lower = runningStatus.toLowerCase();
56
+ const state = RUNNING.has(lower) ? "healthy" : DOWN.has(lower) ? "unhealthy" : "unknown";
57
+ return {
58
+ type: "Microsoft.App/containerApps",
59
+ state,
60
+ detail: `app runningStatus ${runningStatus} (revisions not readable)`,
61
+ observedAt,
62
+ };
63
+ }
64
+ function hole(err) {
65
+ const { reason, detail } = classifyArmFailure(err);
66
+ return { type: "Microsoft.App/containerApps", reason, detail };
67
+ }
68
+ async function listRevisions(client, name) {
69
+ const http = client.http;
70
+ const url = armResourceReadUrl(client, "Microsoft.App/containerApps", `${name}/revisions`, CONTAINER_APPS_API_VERSION);
71
+ const res = await http("GET", url, undefined, client.signal);
72
+ if (res.status >= 300)
73
+ throw new AzureReadError(`revisions read failed with HTTP ${res.status}`, res.status);
74
+ let body;
75
+ try {
76
+ body = JSON.parse(res.text);
77
+ }
78
+ catch {
79
+ throw new AzureReadError("unparseable revisions list", res.status);
80
+ }
81
+ const value = body && typeof body === "object" ? body.value : undefined;
82
+ if (!Array.isArray(value))
83
+ throw new AzureReadError("unparseable revisions list", res.status);
84
+ return value;
85
+ }
86
+ const defaultHttp = async (method, url, body, signal) => {
87
+ const res = await fetch(url, {
88
+ method,
89
+ headers: body === undefined ? undefined : { "content-type": "application/json" },
90
+ body: body === undefined ? undefined : JSON.stringify(body),
91
+ signal,
92
+ });
93
+ return { status: res.status, text: await res.text() };
94
+ };
95
+ export async function observeHealthAzure(options) {
96
+ const observedAt = (options.now?.() ?? new Date()).toISOString();
97
+ const client = {
98
+ resourceGroup: options.environment,
99
+ ...(process.env.AZURE_ENDPOINT_URL ? { endpoint: process.env.AZURE_ENDPOINT_URL } : {}),
100
+ ...(process.env.AZURE_SUBSCRIPTION_ID ? { subscriptionId: process.env.AZURE_SUBSCRIPTION_ID } : {}),
101
+ http: options.http ?? defaultHttp,
102
+ };
103
+ const entities = {};
104
+ const unobserved = {};
105
+ const absent = [];
106
+ await Promise.all([...options.entities].map(async ([entityName, { entityType, props }]) => {
107
+ const name = props.name;
108
+ if (!name) {
109
+ unobserved[entityName] = { type: entityType, reason: "read-failed", detail: "declared entity has no name to query by" };
110
+ return;
111
+ }
112
+ let app;
113
+ try {
114
+ app = await getResource(client, entityType, name, CONTAINER_APPS_API_VERSION);
115
+ }
116
+ catch (err) {
117
+ if (isNotFound(err))
118
+ absent.push(entityName);
119
+ else
120
+ unobserved[entityName] = hole(err);
121
+ return;
122
+ }
123
+ try {
124
+ entities[entityName] = containerAppEntry(await listRevisions(client, name), observedAt);
125
+ }
126
+ catch (err) {
127
+ const runningStatus = app.properties?.runningStatus;
128
+ if (typeof runningStatus === "string" && runningStatus.length > 0) {
129
+ entities[entityName] = appRunningStatusEntry(runningStatus, observedAt);
130
+ }
131
+ else {
132
+ unobserved[entityName] = hole(err);
133
+ }
134
+ }
135
+ }));
136
+ return { entities, unobserved, absent };
137
+ }
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "73f94421607348da234c7b2fe2431c917da7c16edc29fa1606a5a41498acf91f",
5
- "meta.json": "8c7a1ea3cfaf39461d3e758fa2b0cbd70c7800fac736e56df8d14a3e07ef1840",
6
- "types/index.d.ts": "b20aaa3eeb957a6a5833dcbf5fd99ce1365285eafd4b78dc9383371b1a9d4b61",
4
+ "manifest.json": "b7cb2339ce19b5990cef0993cd7beddc1d038585d6578f379d7a7eadb5f868a7",
5
+ "meta.json": "d2521221ed616d882ded4296f0071685bd74e4b645d55dabc9685733c37dc419",
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": "913317ef4f61d1a879f0f559bc3dfc00d73020f7c1106d2cb743acad1de4a00d",
10
+ "rules/arm-refs.ts": "1ff54e29511d5b0572586d64d7c5ca476a13f401f14f89b4d601b01aa6dde891",
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": "95455caf05c28b24f7081e35276586087791d81c4b2d88d516a7fd35f5d8a59c"
40
+ "composite": "039009b3a310fec3985cbd427c59397fa4b2cd76797302c53c1a7d1e1d8e1e77"
41
41
  }
@@ -55,6 +55,7 @@ export declare function isBracketExpression(value: unknown): boolean;
55
55
  *
56
56
  * Handles:
57
57
  * - [resourceId('Type', 'name')] -> extracts 'name'
58
+ * - [resourceId('Type', 'a', 'b')] -> extracts 'a/b'
58
59
  * - [reference('name')] -> extracts 'name'
59
60
  * - [reference('name').prop] -> extracts 'name'
60
61
  */
@@ -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;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBhE"}
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"}
@@ -50,16 +50,18 @@ export function isBracketExpression(value) {
50
50
  *
51
51
  * Handles:
52
52
  * - [resourceId('Type', 'name')] -> extracts 'name'
53
+ * - [resourceId('Type', 'a', 'b')] -> extracts 'a/b'
53
54
  * - [reference('name')] -> extracts 'name'
54
55
  * - [reference('name').prop] -> extracts 'name'
55
56
  */
56
57
  export function extractRefsFromExpression(expr) {
57
58
  const refs = [];
58
- // Match resourceId('...', 'name') — extract the last quoted argument (the resource name)
59
- const resourceIdPattern = /resourceId\s*\([^)]*,\s*'([^']+)'\s*\)/g;
59
+ // Match resourceId('Type', 'name', ...): the name is every quoted argument
60
+ // after the type, joined with "/" for a child resource ('vnet', 'subnet').
61
+ const resourceIdPattern = /resourceId\s*\(\s*'[^']+'((?:\s*,\s*'[^']+')+)\s*\)/g;
60
62
  let match;
61
63
  while ((match = resourceIdPattern.exec(expr)) !== null) {
62
- refs.push(match[1]);
64
+ refs.push([...match[1].matchAll(/'([^']+)'/g)].map((m) => m[1]).join("/"));
63
65
  }
64
66
  // Match reference('name') — extract the name
65
67
  const referencePattern = /reference\s*\(\s*'([^']+)'\s*\)/g;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "azure",
3
- "version": "0.124.32",
3
+ "version": "0.124.34",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "Azure",
6
6
  "intrinsics": [
package/dist/meta.json CHANGED
@@ -33235,7 +33235,12 @@
33235
33235
  "lexicon": "azure",
33236
33236
  "apiVersion": "2025-02-02-preview",
33237
33237
  "attrs": {
33238
- "id": "id"
33238
+ "id": "id",
33239
+ "configuration_ingress_fqdn": "configuration.ingress.fqdn",
33240
+ "latestRevisionFqdn": "latestRevisionFqdn",
33241
+ "latestRevisionName": "latestRevisionName",
33242
+ "outboundIpAddresses": "outboundIpAddresses",
33243
+ "provisioningState": "provisioningState"
33239
33244
  },
33240
33245
  "propertyConstraints": {
33241
33246
  "kind": {
@@ -22,4 +22,9 @@ resource_type: Microsoft.App/containerApps
22
22
 
23
23
  ## Attributes
24
24
 
25
+ - `configuration.ingress.fqdn`
25
26
  - `id`
27
+ - `latestRevisionFqdn`
28
+ - `latestRevisionName`
29
+ - `outboundIpAddresses`
30
+ - `provisioningState`
@@ -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,aAodzB,CAAC"}
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"}
package/dist/plugin.js CHANGED
@@ -457,6 +457,12 @@ export const tags = defaultTags([
457
457
  },
458
458
  /** The noise rules the deep pass applies to both the live and declared trees. */
459
459
  deepNormalizationHooks: azureDeepNormalizationHooks,
460
+ /** Runtime health (#4177): Container Apps. Implementation in ./health.ts. */
461
+ healthTypes: ["Microsoft.App/containerApps"],
462
+ async observeHealth(options) {
463
+ const { observeHealthAzure } = await import("./health.js");
464
+ return observeHealthAzure(options);
465
+ },
460
466
  async exportResources(options) {
461
467
  const { exportResources } = await import("./export-resources.js");
462
468
  return exportResources(options);
@@ -76,17 +76,19 @@ export function isBracketExpression(value: unknown): boolean {
76
76
  *
77
77
  * Handles:
78
78
  * - [resourceId('Type', 'name')] -> extracts 'name'
79
+ * - [resourceId('Type', 'a', 'b')] -> extracts 'a/b'
79
80
  * - [reference('name')] -> extracts 'name'
80
81
  * - [reference('name').prop] -> extracts 'name'
81
82
  */
82
83
  export function extractRefsFromExpression(expr: string): string[] {
83
84
  const refs: string[] = [];
84
85
 
85
- // Match resourceId('...', 'name') — extract the last quoted argument (the resource name)
86
- const resourceIdPattern = /resourceId\s*\([^)]*,\s*'([^']+)'\s*\)/g;
86
+ // Match resourceId('Type', 'name', ...): the name is every quoted argument
87
+ // after the type, joined with "/" for a child resource ('vnet', 'subnet').
88
+ const resourceIdPattern = /resourceId\s*\(\s*'[^']+'((?:\s*,\s*'[^']+')+)\s*\)/g;
87
89
  let match: RegExpExecArray | null;
88
90
  while ((match = resourceIdPattern.exec(expr)) !== null) {
89
- refs.push(match[1]);
91
+ refs.push([...match[1].matchAll(/'([^']+)'/g)].map((m) => m[1]).join("/"));
90
92
  }
91
93
 
92
94
  // Match reference('name') — extract the name
@@ -9,6 +9,7 @@
9
9
  * - ARM bracket expression references
10
10
  * - parameters, outputs sections
11
11
  */
12
+ import type { Declarable } from "@intentius/chant/declarable";
12
13
  import type { Serializer } from "@intentius/chant/serializer";
13
14
  export declare function apiVersionRegistryCandidates(dir?: string): string[];
14
15
  export declare function loadApiVersions(candidates?: string[]): Map<string, string>;
@@ -24,6 +25,12 @@ export declare function resetApiVersionCache(): void;
24
25
  */
25
26
  export declare function lookupApiVersion(resourceType: string): string | undefined;
26
27
  export declare function getApiVersion(resourceType: string): string;
28
+ /**
29
+ * An attribute of an export. `id` is `resourceId()`, since `reference()`
30
+ * returns the resource's properties and they have no `id`; anything else
31
+ * reads `reference()` by the ARM name.
32
+ */
33
+ export declare function armAttrExpression(entityNames: Map<Declarable, string>, name: string, attr: string): string;
27
34
  /**
28
35
  * Azure ARM template serializer implementation.
29
36
  */
@@ -1 +1 @@
1
- {"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,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;AA2TD;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,UA0C7B,CAAC"}
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"}
@@ -121,20 +121,50 @@ export function getApiVersion(resourceType) {
121
121
  console.warn(`azure: no pinned apiVersion for ${resourceType} in the lexicon registry; falling back to 2023-01-01`);
122
122
  return "2023-01-01";
123
123
  }
124
+ /**
125
+ * The ARM name of a chant export, as the arguments an ARM function takes
126
+ * (#4183). A resource's `name` prop is its ARM name, and the export name is
127
+ * used only when it has none. A literal name is quoted, one argument per
128
+ * segment of a child's `parent/child` name; a bracket expression such as
129
+ * `[parameters('n')]` is passed unwrapped.
130
+ */
131
+ function armNameArgs(entity, exportName) {
132
+ const props = entity && isResourceDeclarable(entity) && typeof entity.props === "object" && entity.props !== null
133
+ ? entity.props
134
+ : undefined;
135
+ const name = typeof props?.name === "string" ? props.name : exportName;
136
+ if (name.startsWith("[") && name.endsWith("]"))
137
+ return { args: name.slice(1, -1), literal: null };
138
+ return { args: name.split("/").map((seg) => `'${seg}'`).join(", "), literal: name };
139
+ }
140
+ function entityNamed(entityNames, name) {
141
+ return [...entityNames.entries()].find(([, n]) => n === name)?.[0];
142
+ }
143
+ /** `resourceId(...)` for an export: the type and its ARM name. */
144
+ function armResourceId(entityNames, name) {
145
+ const entity = entityNamed(entityNames, name);
146
+ const { args } = armNameArgs(entity, name);
147
+ return entity ? `resourceId('${entity.entityType}', ${args})` : `resourceId(${args})`;
148
+ }
149
+ /**
150
+ * An attribute of an export. `id` is `resourceId()`, since `reference()`
151
+ * returns the resource's properties and they have no `id`; anything else
152
+ * reads `reference()` by the ARM name.
153
+ */
154
+ export function armAttrExpression(entityNames, name, attr) {
155
+ if (attr === "id")
156
+ return `[${armResourceId(entityNames, name)}]`;
157
+ const { args, literal } = armNameArgs(entityNamed(entityNames, name), name);
158
+ const target = literal !== null && !literal.includes("/") ? args : armResourceId(entityNames, name);
159
+ return `[reference(${target}).${attr}]`;
160
+ }
124
161
  /**
125
162
  * ARM-specific visitor for the generic serializer walker.
126
163
  */
127
164
  function armVisitor(entityNames) {
128
165
  return {
129
- attrRef: (name, attr) => `[reference('${name}').${attr}]`,
130
- resourceRef: (name) => {
131
- // Look up the resource type from entityNames
132
- const entity = [...entityNames.entries()].find(([, n]) => n === name)?.[0];
133
- if (entity) {
134
- return `[resourceId('${entity.entityType}', '${name}')]`;
135
- }
136
- return `[resourceId('${name}')]`;
137
- },
166
+ attrRef: (name, attr) => armAttrExpression(entityNames, name, attr),
167
+ resourceRef: (name) => `[${armResourceId(entityNames, name)}]`,
138
168
  propertyDeclarable: (entity, walk) => {
139
169
  if (!isResourceDeclarable(entity) || typeof entity.props !== "object" || entity.props === null) {
140
170
  return undefined;
@@ -153,13 +183,38 @@ function armVisitor(entityNames) {
153
183
  /**
154
184
  * Convert a value to ARM-compatible JSON using the generic walker.
155
185
  */
186
+ /** An ARM output for a literal, or for an azure intrinsic's `[...]` expression. */
187
+ function armOutputValue(value) {
188
+ if (typeof value === "string")
189
+ return { type: "string", value };
190
+ if (typeof value === "number" && Number.isInteger(value))
191
+ return { type: "int", value };
192
+ if (typeof value === "boolean")
193
+ return { type: "bool", value };
194
+ return undefined;
195
+ }
196
+ function isIntrinsicLike(value) {
197
+ return typeof value === "object" && value !== null && typeof value.toJSON === "function";
198
+ }
199
+ function isCloudFormationShaped(value) {
200
+ if (typeof value !== "object" || value === null || Array.isArray(value))
201
+ return false;
202
+ const keys = Object.keys(value);
203
+ return keys.length === 1 && (keys[0] === "Ref" || keys[0].startsWith("Fn::"));
204
+ }
205
+ function isPlainObject(value) {
206
+ if (value === null || typeof value !== "object" || Array.isArray(value))
207
+ return false;
208
+ const proto = Object.getPrototypeOf(value);
209
+ return proto === Object.prototype || proto === null;
210
+ }
156
211
  function toArmValue(value, entityNames) {
157
212
  return walkValue(value, entityNames, armVisitor(entityNames));
158
213
  }
159
214
  /**
160
215
  * Serialize entities into an ARM template object.
161
216
  */
162
- function serializeToTemplate(entities, outputs, ownership) {
217
+ function serializeToTemplate(entities, outputs, ownership, warnings = []) {
163
218
  // Resolve the deployment scope from the resource types being emitted
164
219
  // (#1545): a template of tenant-only resources (management groups, policy
165
220
  // definitions at management-group scope, ...) must carry the matching
@@ -270,6 +325,14 @@ function serializeToTemplate(entities, outputs, ownership) {
270
325
  else if (key === "name") {
271
326
  resource.name = toArmValue(value, entityNames);
272
327
  }
328
+ else if (key === "properties" && isPlainObject(value)) {
329
+ // The generated types take ARM's own `properties` object, so
330
+ // merge it, not nest it (#4183).
331
+ for (const [inner, innerValue] of Object.entries(value)) {
332
+ if (innerValue !== undefined)
333
+ armProperties[inner] = toArmValue(innerValue, entityNames);
334
+ }
335
+ }
273
336
  else {
274
337
  armProperties[key] = toArmValue(value, entityNames);
275
338
  }
@@ -292,14 +355,7 @@ function serializeToTemplate(entities, outputs, ownership) {
292
355
  if (attrs?.DependsOn !== undefined) {
293
356
  const resolved = resolveDependsOn(attrs.DependsOn, entityNames, name);
294
357
  if (resolved.length > 0) {
295
- resource.dependsOn = resolved.map((dep) => {
296
- // Convert to resourceId expression
297
- const depEntity = [...entityNames.entries()].find(([, n]) => n === dep)?.[0];
298
- if (depEntity) {
299
- return `[resourceId('${depEntity.entityType}', '${dep}')]`;
300
- }
301
- return dep;
302
- });
358
+ resource.dependsOn = resolved.map((dep) => entityNamed(entityNames, dep) ? `[${armResourceId(entityNames, dep)}]` : dep);
303
359
  }
304
360
  }
305
361
  template.resources.push(resource);
@@ -358,7 +414,7 @@ function serializeToTemplate(entities, outputs, ownership) {
358
414
  // Find the resource to build the resourceId expression
359
415
  const depResource = template.resources.find((r) => r.name === refName);
360
416
  if (depResource) {
361
- const depExpr = `[resourceId('${depResource.type}', '${refName}')]`;
417
+ const depExpr = `[resourceId('${depResource.type}', ${refName.split("/").map((seg) => `'${seg}'`).join(", ")})]`;
362
418
  if (!existing.has(depExpr)) {
363
419
  existing.add(depExpr);
364
420
  }
@@ -376,29 +432,54 @@ function serializeToTemplate(entities, outputs, ownership) {
376
432
  template.outputs = {};
377
433
  const stackOutput = entity;
378
434
  // `unknown` so isAttrRefLike narrows cleanly across the workspace/published
379
- // type boundary. An intrinsic-wrapped output (#517) has no bare ARM
380
- // reference form, so skip it here (only aws/CFN emits those).
435
+ // type boundary.
381
436
  const ref = stackOutput.sourceRef;
382
437
  if (isAttrRefLike(ref)) {
383
438
  const logicalName = ref.getLogicalName();
384
439
  if (logicalName) {
385
440
  template.outputs[name] = {
386
441
  type: "string",
387
- value: `[reference('${logicalName}').${ref.attribute}]`,
442
+ value: armAttrExpression(entityNames, logicalName, ref.attribute),
388
443
  };
389
444
  }
390
445
  }
446
+ else {
447
+ const value = armOutputValue(isIntrinsicLike(ref) ? ref.toJSON() : ref);
448
+ if (value)
449
+ template.outputs[name] = value;
450
+ else
451
+ warnings.push(`azure: stack output "${name}" is not written: its value has no ARM form.`);
452
+ }
391
453
  }
392
454
  }
393
455
  // Add LexiconOutputs
394
456
  if (outputs && outputs.length > 0) {
395
457
  template.outputs = template.outputs ?? {};
396
458
  for (const output of outputs) {
397
- template.outputs[output.outputName] = {
398
- type: "string",
399
- value: `[reference('${output.sourceEntity}').${output.sourceAttribute}]`,
400
- };
459
+ if (output.sourceAttribute !== null) {
460
+ if (output.sourceEntity) {
461
+ template.outputs[output.outputName] = {
462
+ type: "string",
463
+ value: armAttrExpression(entityNames, output.sourceEntity, output.sourceAttribute),
464
+ };
465
+ }
466
+ else {
467
+ warnings.push(`azure: output "${output.outputName}" is not written: its resource has no name in this build.`);
468
+ }
469
+ continue;
470
+ }
471
+ // A literal or an intrinsic (#4181). It has no source lexicon, so every
472
+ // lexicon receives it; a CloudFormation-shaped value is aws's to write.
473
+ const raw = output.getOutputValue();
474
+ const value = armOutputValue(raw);
475
+ if (value)
476
+ template.outputs[output.outputName] = value;
477
+ else if (!isCloudFormationShaped(raw)) {
478
+ warnings.push(`azure: output "${output.outputName}" is not written: its value has no ARM form.`);
479
+ }
401
480
  }
481
+ if (Object.keys(template.outputs).length === 0)
482
+ delete template.outputs;
402
483
  }
403
484
  return template;
404
485
  }
@@ -447,11 +528,16 @@ export const azureSerializer = {
447
528
  }
448
529
  }
449
530
  }
450
- const template = serializeToTemplate(entities, outputs, context?.ownership);
531
+ const warnings = [];
532
+ const template = serializeToTemplate(entities, outputs, context?.ownership, warnings);
451
533
  const primary = JSON.stringify(template, null, 2);
452
- if (!hasChildProjects) {
534
+ if (!hasChildProjects && warnings.length === 0) {
453
535
  return primary;
454
536
  }
455
- return { primary, files: allFiles };
537
+ return {
538
+ primary,
539
+ ...(hasChildProjects ? { files: allFiles } : {}),
540
+ ...(warnings.length > 0 ? { warnings } : {}),
541
+ };
456
542
  },
457
543
  };
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAiE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC1G,OAAO,EAGL,kBAAkB,IAAI,sBAAsB,EAE5C,KAAK,mBAAmB,EAIzB,MAAM,sCAAsC,CAAC;AAI9C,YAAY,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAEhF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,mBAAmB,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7E;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAC;IACzB,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AA+GD;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,aAWrC,CAAC;AAEH;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,oBAAoB,CAwI1E;AA+CD,eAAO,MAAM,kBAAkB,+BAAyB,CAAC;AAMzD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIvD"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAiE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC1G,OAAO,EAGL,kBAAkB,IAAI,sBAAsB,EAE5C,KAAK,mBAAmB,EAIzB,MAAM,sCAAsC,CAAC;AAI9C,YAAY,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAEhF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,mBAAmB,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7E;;;;OAIG;IACH,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAC;IACzB,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAyHD;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,aAWrC,CAAC;AAEH;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,oBAAoB,CAwI1E;AA+CD,eAAO,MAAM,kBAAkB,+BAAyB,CAAC;AAMzD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIvD"}
@@ -104,6 +104,16 @@ const CURATED_ATTRIBUTES = {
104
104
  { name: "id", tsType: "string" },
105
105
  { name: "provisioningState", tsType: "string" },
106
106
  ],
107
+ // The app's hostname, for DNS in front of it (#4179). Dotted names read a
108
+ // nested field of reference()'s properties, as AWS's Endpoint.Address does.
109
+ "Microsoft.App/containerApps": [
110
+ { name: "id", tsType: "string" },
111
+ { name: "configuration.ingress.fqdn", tsType: "string" },
112
+ { name: "latestRevisionFqdn", tsType: "string" },
113
+ { name: "latestRevisionName", tsType: "string" },
114
+ { name: "outboundIpAddresses", tsType: "string[]" },
115
+ { name: "provisioningState", tsType: "string" },
116
+ ],
107
117
  };
108
118
  /**
109
119
  * Maximum nesting depth of generated property-type interfaces.
@@ -4232,7 +4232,12 @@ export declare class containerApps {
4232
4232
  /** Resource tags. */
4233
4233
  tags?: Record<string, unknown>;
4234
4234
  }, attributes?: ArmResourceAttributes);
4235
+ readonly configuration_ingress_fqdn: string;
4235
4236
  readonly id: string;
4237
+ readonly latestRevisionFqdn: string;
4238
+ readonly latestRevisionName: string;
4239
+ readonly outboundIpAddresses: string[];
4240
+ readonly provisioningState: string;
4236
4241
  }
4237
4242
 
4238
4243
  export declare class containerApps_authConfigs {