@intentius/chant-lexicon-azure 0.124.31 → 0.124.33

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 (144) hide show
  1. package/dist/actions/appservice.js +7 -0
  2. package/dist/actions/compute.js +9 -0
  3. package/dist/actions/container.js +14 -0
  4. package/dist/actions/identity.js +11 -0
  5. package/dist/actions/index.js +11 -0
  6. package/dist/actions/keyvault.js +12 -0
  7. package/dist/actions/network.js +9 -0
  8. package/dist/actions/sql.js +9 -0
  9. package/dist/actions/storage.js +15 -0
  10. package/dist/api/read-client.js +146 -0
  11. package/dist/codegen/docs-cli.js +2 -0
  12. package/dist/codegen/docs.js +83 -0
  13. package/dist/codegen/generate-cli.js +11 -0
  14. package/dist/codegen/generate-lexicon.js +77 -0
  15. package/dist/codegen/generate-typescript.js +163 -0
  16. package/dist/codegen/generate.js +106 -0
  17. package/dist/codegen/naming.js +139 -0
  18. package/dist/codegen/package.js +64 -0
  19. package/dist/composites/aks-cluster.js +68 -0
  20. package/dist/composites/app-service.js +62 -0
  21. package/dist/composites/application-gateway.js +107 -0
  22. package/dist/composites/catalog.js +856 -0
  23. package/dist/composites/container-instance.js +36 -0
  24. package/dist/composites/container-registry.js +62 -0
  25. package/dist/composites/cosmos-database.js +56 -0
  26. package/dist/composites/from-arm.js +47 -0
  27. package/dist/composites/function-app.js +57 -0
  28. package/dist/composites/index.js +16 -0
  29. package/dist/composites/keyvault.js +69 -0
  30. package/dist/composites/landing-zone.js +119 -0
  31. package/dist/composites/private-endpoint.js +61 -0
  32. package/dist/composites/redis-cache.js +21 -0
  33. package/dist/composites/service-bus-pipeline.js +54 -0
  34. package/dist/composites/sql-database.js +62 -0
  35. package/dist/composites/storage-account.js +59 -0
  36. package/dist/composites/vm-linux.js +139 -0
  37. package/dist/composites/vnet-default.js +102 -0
  38. package/dist/coverage.js +28 -0
  39. package/dist/deep-observe.js +356 -0
  40. package/dist/default-tags.js +50 -0
  41. package/dist/deploy-scopes.d.ts.map +1 -1
  42. package/dist/deploy-scopes.js +157 -0
  43. package/dist/describe-resources.js +187 -0
  44. package/dist/detect.js +17 -0
  45. package/dist/export-resources.js +108 -0
  46. package/dist/generated/index.d.ts.map +1 -1
  47. package/dist/generated/index.js +16598 -0
  48. package/dist/generated/runtime.js +4 -0
  49. package/dist/governance.js +144 -0
  50. package/dist/health.d.ts +42 -0
  51. package/dist/health.d.ts.map +1 -0
  52. package/dist/health.js +137 -0
  53. package/dist/import/generator.js +140 -0
  54. package/dist/import/live-export.js +39 -0
  55. package/dist/import/parser.js +192 -0
  56. package/dist/index.js +44 -0
  57. package/dist/integrity.json +5 -5
  58. package/dist/intrinsics.js +179 -0
  59. package/dist/lint/audit-catalog.js +36 -0
  60. package/dist/lint/audit-lineage.js +71 -0
  61. package/dist/lint/post-synth/arm-refs.d.ts +1 -0
  62. package/dist/lint/post-synth/arm-refs.d.ts.map +1 -1
  63. package/dist/lint/post-synth/arm-refs.js +96 -0
  64. package/dist/lint/post-synth/azr010.js +45 -0
  65. package/dist/lint/post-synth/azr011.js +43 -0
  66. package/dist/lint/post-synth/azr012.js +41 -0
  67. package/dist/lint/post-synth/azr013.js +50 -0
  68. package/dist/lint/post-synth/azr014.js +49 -0
  69. package/dist/lint/post-synth/azr015.js +66 -0
  70. package/dist/lint/post-synth/azr016.js +37 -0
  71. package/dist/lint/post-synth/azr017.js +37 -0
  72. package/dist/lint/post-synth/azr018.js +42 -0
  73. package/dist/lint/post-synth/azr019.js +41 -0
  74. package/dist/lint/post-synth/azr020.js +37 -0
  75. package/dist/lint/post-synth/azr021.js +36 -0
  76. package/dist/lint/post-synth/azr022.js +39 -0
  77. package/dist/lint/post-synth/azr023.js +39 -0
  78. package/dist/lint/post-synth/azr024.js +39 -0
  79. package/dist/lint/post-synth/azr025.js +37 -0
  80. package/dist/lint/post-synth/azr026.js +38 -0
  81. package/dist/lint/post-synth/azr027.js +37 -0
  82. package/dist/lint/post-synth/azr028.js +37 -0
  83. package/dist/lint/post-synth/azr029.js +38 -0
  84. package/dist/lint/post-synth/azr030.js +44 -0
  85. package/dist/lint/post-synth/azr031.js +36 -0
  86. package/dist/lint/post-synth/azr032.js +53 -0
  87. package/dist/lint/post-synth/azr033.js +35 -0
  88. package/dist/lint/post-synth/index.js +50 -0
  89. package/dist/lint/rules/hardcoded-location.js +82 -0
  90. package/dist/lint/rules/index.js +6 -0
  91. package/dist/lint/rules/nsg-wildcard.js +44 -0
  92. package/dist/lint/rules/storage-https.js +62 -0
  93. package/dist/lsp/completions.d.ts.map +1 -1
  94. package/dist/lsp/completions.js +18 -0
  95. package/dist/lsp/hover.d.ts.map +1 -1
  96. package/dist/lsp/hover.js +44 -0
  97. package/dist/manifest.json +1 -1
  98. package/dist/meta.json +6 -1
  99. package/dist/okf/types/containerApps.md +5 -0
  100. package/dist/op/activities/az-apply.js +563 -0
  101. package/dist/op/activities/azure.js +48 -0
  102. package/dist/op/activities/floci-az.js +37 -0
  103. package/dist/op/activities/index.js +10 -0
  104. package/dist/op/builders.js +70 -0
  105. package/dist/ownership.js +13 -0
  106. package/dist/package-cli.js +21 -0
  107. package/dist/parameter.js +13 -0
  108. package/dist/plugin.d.ts.map +1 -1
  109. package/dist/plugin.js +478 -0
  110. package/dist/pseudo.js +40 -0
  111. package/dist/rules/arm-refs.ts +5 -3
  112. package/dist/serializer.d.ts +7 -0
  113. package/dist/serializer.d.ts.map +1 -1
  114. package/dist/serializer.js +543 -0
  115. package/dist/spec/api-versions.js +130 -0
  116. package/dist/spec/fetch.js +196 -0
  117. package/dist/spec/parse.d.ts.map +1 -1
  118. package/dist/spec/parse.js +318 -0
  119. package/dist/taggable.js +34 -0
  120. package/dist/types/index.d.ts +5 -0
  121. package/dist/validate-cli.js +16 -0
  122. package/dist/validate.js +53 -0
  123. package/package.json +8 -8
  124. package/src/attributes.test.ts +17 -0
  125. package/src/codegen/generate-typescript.ts +1 -1
  126. package/src/codegen/generate.ts +1 -1
  127. package/src/codegen/package.ts +2 -2
  128. package/src/deploy-scopes.ts +2 -1
  129. package/src/generated/index.d.ts +5 -0
  130. package/src/generated/index.ts +1 -1
  131. package/src/generated/lexicon-azure.json +6 -1
  132. package/src/health.test.ts +66 -0
  133. package/src/health.ts +162 -0
  134. package/src/lint/post-synth/arm-refs.ts +5 -3
  135. package/src/lint/post-synth/post-synth.test.ts +1 -1
  136. package/src/lsp/completions.ts +2 -1
  137. package/src/lsp/hover.ts +2 -1
  138. package/src/plugin.ts +11 -2
  139. package/src/serializer-names.test.ts +83 -0
  140. package/src/serializer-outputs.test.ts +65 -0
  141. package/src/serializer.ts +112 -30
  142. package/src/spec/parse.test.ts +15 -0
  143. package/src/spec/parse.ts +10 -0
  144. package/src/taggable.ts +2 -2
@@ -0,0 +1,130 @@
1
+ /**
2
+ * API version resolution for Azure Resource Manager schemas.
3
+ *
4
+ * ARM schemas are organized by API date:
5
+ * schemas/{date}/Microsoft.{Provider}.json
6
+ *
7
+ * This module picks the latest API version per provider.
8
+ */
9
+ /**
10
+ * Parse an API date string (e.g. "2023-01-01" or "2023-01-01-preview").
11
+ * Returns a numeric value for comparison: date timestamp with preview
12
+ * versions ranked below their GA counterpart.
13
+ */
14
+ export function parseApiDate(dateStr) {
15
+ const isPreview = dateStr.endsWith("-preview");
16
+ const cleaned = isPreview ? dateStr.replace(/-preview$/, "") : dateStr;
17
+ const ts = new Date(cleaned).getTime();
18
+ // Preview versions sort just below their GA date
19
+ return isPreview ? ts - 1 : ts;
20
+ }
21
+ /**
22
+ * Compare two API date strings. Returns positive if a > b.
23
+ */
24
+ export function compareApiDates(a, b) {
25
+ return parseApiDate(a) - parseApiDate(b);
26
+ }
27
+ /**
28
+ * Extract provider name and API version from a schema file path.
29
+ *
30
+ * Input: "azure-resource-manager-schemas-main/schemas/2023-01-01/Microsoft.Storage.json"
31
+ * Output: { provider: "Microsoft.Storage", apiVersion: "2023-01-01" }
32
+ */
33
+ export function parseSchemaPath(path) {
34
+ // Match schemas/{date}/Microsoft.{Provider}.json
35
+ const match = path.match(/schemas\/(\d{4}-\d{2}-\d{2}(?:-preview)?)\/(Microsoft\.[^/]+)\.json$/);
36
+ if (!match)
37
+ return null;
38
+ return { apiVersion: match[1], provider: match[2] };
39
+ }
40
+ /**
41
+ * Per-provider API-version pins.
42
+ *
43
+ * `latestVersionPerProvider` picks the single newest version per provider, but
44
+ * Azure spreads resources across versions — a newer version can DROP a resource
45
+ * an older one defined. Microsoft.Authorization's latest preview no longer
46
+ * includes the plain `roleAssignments` / `roleDefinitions` resources; they live
47
+ * in 2022-04-01, the latest stable that still has them. Pin it so those
48
+ * generate (the naming table maps both). See #223.
49
+ *
50
+ * Microsoft.Compute has the same issue (#1144): at the schema commit pinned
51
+ * in ../spec/fetch.ts, the newest dated `Microsoft.Compute.json` (2026-03-02)
52
+ * is a narrow disk-only delta that drops `virtualMachines`,
53
+ * `virtualMachineScaleSets`, and `availabilitySets` entirely — not a rename,
54
+ * an omission, since Azure alternates between a "VM flavor" and a "disk
55
+ * flavor" of this file across dates rather than publishing one cumulative
56
+ * file. 2026-03-01 is the most recent date that still has the VM family
57
+ * (validate.ts's REQUIRED_NAMES and composites/vm-linux.ts depend on it);
58
+ * it drops the disk-only resources (disks, diskAccesses, diskEncryptionSets,
59
+ * snapshots), which nothing here currently references.
60
+ *
61
+ * A pin can list several versions (#1545): Azure publishes some providers as
62
+ * per-family deltas, so no single date carries every resource we need. The
63
+ * files merge in the order listed — the first file that defines a resource
64
+ * name wins, later files only contribute resources not yet seen.
65
+ * Microsoft.Authorization needs three: 2022-04-01 for roleAssignments /
66
+ * roleDefinitions (#223), 2026-06-01 (latest stable policy file) for
67
+ * policyAssignments / policyDefinitions / policySetDefinitions and their
68
+ * _versions children, and 2022-07-01-preview for policyExemptions, which has
69
+ * no GA date and whose newer preview files (2024-12-01-preview and later)
70
+ * drag in policy variables we don't want yet.
71
+ *
72
+ * Microsoft.AzureStackHCI splits the same way (#1795): the newest dated file
73
+ * (2026-05-01-preview) carries only the cluster/edge-device family, while the
74
+ * VM family (virtualMachineInstances, virtualNetworks, networkInterfaces,
75
+ * publicIPAddresses, galleryImages, virtualHardDisks, ...) last appears in
76
+ * 2026-04-01-preview. Pin both, newest first — the cluster/edge family
77
+ * generates from 2026-05-01-preview and the VM family fills in from
78
+ * 2026-04-01-preview. `Microsoft.DataReplication/replicationVaults_
79
+ * privateEndpointConnectionProxies` dropped out in the same re-baseline but
80
+ * is NOT pinnable back: no dated Microsoft.DataReplication.json at the
81
+ * AZURE_SCHEMA_COMMIT pin (2021-02-16-preview, 2024-09-01, 2026-05-01)
82
+ * defines it — upstream removed the resource, so it stays gone.
83
+ *
84
+ * Microsoft.Management and Microsoft.Subscription are pinned to their latest
85
+ * GA dates: the naive latest for both is a preview (2024-02-01-preview adds
86
+ * serviceGroups; 2025-11-01-preview adds changeTenantRequest), and the
87
+ * management-group hierarchy should not author against preview apiVersions.
88
+ */
89
+ export const PROVIDER_VERSION_OVERRIDES = {
90
+ "Microsoft.Authorization": ["2022-04-01", "2026-06-01", "2022-07-01-preview"],
91
+ "Microsoft.AzureStackHCI": ["2026-05-01-preview", "2026-04-01-preview"],
92
+ "Microsoft.Compute": ["2026-03-01"],
93
+ "Microsoft.Management": ["2023-04-01"],
94
+ "Microsoft.Subscription": ["2021-10-01"],
95
+ };
96
+ /**
97
+ * Given a set of schema paths, return the schema files to generate from, per
98
+ * provider: the single latest API version, unless
99
+ * {@link PROVIDER_VERSION_OVERRIDES} pins the provider to one or more chosen
100
+ * versions (used where "latest" drops resources we depend on). For pinned
101
+ * providers the array preserves the pin order — callers merge the files
102
+ * first-wins per resource name.
103
+ *
104
+ * Returns a Map of provider → [{ path, apiVersion }, ...].
105
+ */
106
+ export function latestVersionPerProvider(paths) {
107
+ const best = new Map();
108
+ for (const p of paths) {
109
+ const parsed = parseSchemaPath(p);
110
+ if (!parsed)
111
+ continue;
112
+ const pinned = PROVIDER_VERSION_OVERRIDES[parsed.provider];
113
+ if (pinned) {
114
+ // Only accept the pinned versions for an overridden provider,
115
+ // slotted into pin order.
116
+ if (pinned.includes(parsed.apiVersion)) {
117
+ const files = best.get(parsed.provider) ?? [];
118
+ files.push({ path: p, apiVersion: parsed.apiVersion });
119
+ files.sort((a, b) => pinned.indexOf(a.apiVersion) - pinned.indexOf(b.apiVersion));
120
+ best.set(parsed.provider, files);
121
+ }
122
+ continue;
123
+ }
124
+ const existing = best.get(parsed.provider);
125
+ if (!existing || compareApiDates(parsed.apiVersion, existing[0].apiVersion) > 0) {
126
+ best.set(parsed.provider, [{ path: p, apiVersion: parsed.apiVersion }]);
127
+ }
128
+ }
129
+ return best;
130
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Azure Resource Manager schema fetching.
3
+ *
4
+ * Downloads the azure-resource-manager-schemas repo tarball,
5
+ * extracts provider JSON schemas, deduplicates by latest API version,
6
+ * and "explodes" multi-resource files into one entry per resource.
7
+ */
8
+ import { homedir } from "os";
9
+ import { join } from "path";
10
+ import { fetchWithCache, extractFromTar, clearCacheFile } from "@intentius/chant/codegen/fetch";
11
+ import { latestVersionPerProvider } from "./api-versions.js";
12
+ /** Per-attempt download timeout for a multi-megabyte archive. */
13
+ const ARCHIVE_ATTEMPT_TIMEOUT_MS = 120000;
14
+ /**
15
+ * Pinned commit of Azure/azure-resource-manager-schemas (#1144).
16
+ *
17
+ * The repo has no tagged releases, so unlike gcp (`KCC_VERSION`) or k8s
18
+ * (`K8S_SCHEMA_VERSION`) there is no version tag to pin against — codegen
19
+ * used to fetch `refs/heads/main` directly. That meant a cold cache picked
20
+ * up whatever upstream state had landed since the cache last expired, while
21
+ * a warm cache kept serving whatever was current when it was last filled.
22
+ * Azure republishes per-provider schema files under new dates constantly,
23
+ * and a later date can drop resources an earlier one defined (see
24
+ * PROVIDER_VERSION_OVERRIDES in ./api-versions.ts) — so which resources
25
+ * even exist to generate depends on exactly when you fetch, not just what
26
+ * the fetch URL happens to be. That broke composites/vm-linux.ts at tsc
27
+ * nondeterministically, depending on which runner's cache was warm.
28
+ *
29
+ * Pinning to a commit SHA makes generation reproducible regardless of cache
30
+ * state. Bump policy: #523's scheduled lexicon-upgrade cron (rolling-spec
31
+ * bucket, sub-issue #526) is meant to propose bumps to this constant as a
32
+ * reviewable PR once built. Until then, bump by hand: take the current
33
+ * `main` HEAD sha from
34
+ * https://github.com/Azure/azure-resource-manager-schemas/commits/main,
35
+ * regenerate, fix up any composite left referencing a renamed or vanished
36
+ * export (adding a PROVIDER_VERSION_OVERRIDES entry if a resource dropped
37
+ * out of the naive "latest by date" pick), and update this constant +
38
+ * comment.
39
+ */
40
+ export const AZURE_SCHEMA_COMMIT = "0085cb6f3a98e735359807143bcb1667aeec930f";
41
+ const TARBALL_URL = `https://github.com/Azure/azure-resource-manager-schemas/archive/${AZURE_SCHEMA_COMMIT}.tar.gz`;
42
+ const CACHE_DIR = join(homedir(), ".chant");
43
+ // The cache filename embeds the pin so a stale tarball from before a bump
44
+ // can never mask the bump: restoring an old ~/.chant (e.g. a CI cache
45
+ // partial-key fallback, or a developer's pre-existing cache) simply misses
46
+ // this filename and re-downloads, instead of silently serving pre-bump
47
+ // schemas under a name that still matches (#1144).
48
+ const CACHE_FILE = join(CACHE_DIR, `azure-resource-manager-schemas-${AZURE_SCHEMA_COMMIT}.tar.gz`);
49
+ /** Paths to skip (common-types, non-provider files). */
50
+ function isProviderSchema(path) {
51
+ if (path.includes("common-types/"))
52
+ return false;
53
+ if (path.includes("test/"))
54
+ return false;
55
+ if (!path.includes("/schemas/"))
56
+ return false;
57
+ // Must match schemas/{date}/Microsoft.*.json
58
+ return /schemas\/\d{4}-\d{2}-\d{2}(?:-preview)?\/Microsoft\.[^/]+\.json$/.test(path);
59
+ }
60
+ /**
61
+ * Normalize schema-file provider name to canonical ARM provider.
62
+ * "Microsoft.Network.NRP" → "Microsoft.Network"
63
+ * "Microsoft.Sql.Legacy" → "Microsoft.Sql"
64
+ * "Microsoft.Compute" → "Microsoft.Compute" (unchanged)
65
+ */
66
+ function normalizeProvider(fileProvider) {
67
+ const parts = fileProvider.split(".");
68
+ // Microsoft.X.Y... → Microsoft.X
69
+ return parts.length > 2 ? `${parts[0]}.${parts[1]}` : fileProvider;
70
+ }
71
+ /**
72
+ * Providers whose scope-specific resource definition sections are read in
73
+ * addition to `resourceDefinitions` (#1545).
74
+ *
75
+ * Deliberately an allowlist: reading the scoped sections for every provider
76
+ * would pull hundreds of subscription/tenant-scope resources into the
77
+ * generated surface at once (and back into tsc time — see the #438 bounding
78
+ * story). Only the org-hierarchy and policy providers need them today; grow
79
+ * this set intentionally, checking generated size and azure tsc time after.
80
+ */
81
+ const SCOPED_SECTION_PROVIDERS = new Set([
82
+ "Microsoft.Management",
83
+ "Microsoft.Subscription",
84
+ "Microsoft.Authorization",
85
+ ]);
86
+ /** Scope sections of a provider file, in definition-priority order. */
87
+ const SCOPE_SECTIONS = [
88
+ { key: "resourceDefinitions", scope: "resourceGroup" },
89
+ { key: "subscription_resourceDefinitions", scope: "subscription" },
90
+ { key: "managementGroup_resourceDefinitions", scope: "managementGroup" },
91
+ { key: "tenant_resourceDefinitions", scope: "tenant" },
92
+ ];
93
+ /**
94
+ * Explode one provider schema file into per-resource schema entries,
95
+ * writing into `out` keyed by resource type. Resource types already present
96
+ * in `out` are left untouched (first file wins across a multi-version pin).
97
+ *
98
+ * For providers in {@link SCOPED_SECTION_PROVIDERS} all deployment-scope
99
+ * sections are read; the definition comes from the highest-priority section
100
+ * that has it, and `deployScopes` unions every section that defines it.
101
+ */
102
+ export function explodeProviderSchema(provider, apiVersion, providerSchema, out) {
103
+ const definitions = providerSchema.definitions ?? {};
104
+ const sections = SCOPED_SECTION_PROVIDERS.has(normalizeProvider(provider))
105
+ ? SCOPE_SECTIONS
106
+ : SCOPE_SECTIONS.slice(0, 1);
107
+ // Gather each resource name's definition (highest-priority section wins)
108
+ // and the union of scopes that define it.
109
+ const byName = new Map();
110
+ for (const { key, scope } of sections) {
111
+ const defs = providerSchema[key];
112
+ if (!defs)
113
+ continue;
114
+ for (const [resourceName, resourceDef] of Object.entries(defs)) {
115
+ const existing = byName.get(resourceName);
116
+ if (existing) {
117
+ existing.scopes.push(scope);
118
+ }
119
+ else {
120
+ byName.set(resourceName, { def: resourceDef, scopes: [scope] });
121
+ }
122
+ }
123
+ }
124
+ for (const [resourceName, { def, scopes }] of byName) {
125
+ const resourceType = `${normalizeProvider(provider)}/${resourceName}`;
126
+ if (out.has(resourceType))
127
+ continue;
128
+ // Build a per-resource schema that includes the resource def + shared definitions + apiVersion
129
+ const perResourceSchema = {
130
+ resourceType,
131
+ apiVersion,
132
+ provider,
133
+ resourceName,
134
+ deployScopes: scopes,
135
+ resourceDefinition: def,
136
+ definitions,
137
+ };
138
+ out.set(resourceType, Buffer.from(JSON.stringify(perResourceSchema)));
139
+ }
140
+ }
141
+ /**
142
+ * Fetch ARM schemas and return a Map keyed by resource type
143
+ * (e.g. "Microsoft.Storage/storageAccounts") to raw JSON bytes.
144
+ *
145
+ * Each provider file is "exploded" so that every resourceDefinition
146
+ * becomes its own entry with the shared definitions included.
147
+ */
148
+ export async function fetchArmSchemas(force = false) {
149
+ const tarGz = await fetchWithCache({ url: TARBALL_URL, cacheFile: CACHE_FILE, attemptTimeoutMs: ARCHIVE_ATTEMPT_TIMEOUT_MS }, force);
150
+ // Gunzip
151
+ const { gunzipSync } = await import("fflate");
152
+ const tarData = gunzipSync(new Uint8Array(tarGz));
153
+ // Extract all provider schema files
154
+ const allFiles = extractFromTar(tarData, isProviderSchema);
155
+ // Deduplicate: keep only the latest API version per provider, or the pinned
156
+ // versions (in pin order) for providers in PROVIDER_VERSION_OVERRIDES.
157
+ const paths = [...allFiles.keys()];
158
+ const selected = latestVersionPerProvider(paths);
159
+ // Explode: one provider file has N resourceDefinitions → emit N entries.
160
+ // With a multi-version pin the first file that defines a resource wins.
161
+ const schemas = new Map();
162
+ for (const [provider, files] of selected) {
163
+ // First-wins applies within one provider's pin list; across providers the
164
+ // pre-#1545 behavior is kept (a later provider file may overwrite a
165
+ // normalized-name collision, e.g. Microsoft.Sql.Legacy vs Microsoft.Sql).
166
+ const providerSchemas = new Map();
167
+ for (const { path, apiVersion } of files) {
168
+ const data = allFiles.get(path);
169
+ if (!data)
170
+ continue;
171
+ try {
172
+ const providerSchema = JSON.parse(data.toString("utf-8"));
173
+ explodeProviderSchema(provider, apiVersion, providerSchema, providerSchemas);
174
+ }
175
+ catch {
176
+ // Skip files that can't be parsed
177
+ }
178
+ }
179
+ for (const [resourceType, buf] of providerSchemas) {
180
+ schemas.set(resourceType, buf);
181
+ }
182
+ }
183
+ return schemas;
184
+ }
185
+ /**
186
+ * Get the cache file path (for testing).
187
+ */
188
+ export function getCachePath() {
189
+ return CACHE_FILE;
190
+ }
191
+ /**
192
+ * Clear the cache (for testing).
193
+ */
194
+ export function clearCache() {
195
+ clearCacheFile(CACHE_FILE);
196
+ }
@@ -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"}
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Azure Resource Manager JSON Schema parser.
3
+ *
4
+ * Parses each exploded ARM schema into typed structures suitable for code generation:
5
+ * resources with properties and attributes, property types from definitions,
6
+ * and enum types from string enum definitions.
7
+ *
8
+ * Key differences from CloudFormation parsing:
9
+ * - ARM has resource-level fields (name, type, apiVersion, location, sku, kind,
10
+ * identity, tags, zones, plan) separate from properties.properties
11
+ * - ARM schemas use oneOf wrappers with expression refs that must be unwrapped
12
+ * - ARM schemas lack readOnlyProperties — attributes are curated
13
+ */
14
+ import { resolvePropertyType as coreResolvePropertyType, extractConstraints as coreExtractConstraints, constraintsIsEmpty as coreConstraintsIsEmpty, isEnumDefinition as coreIsEnumDefinition, } from "@intentius/chant/codegen/json-schema";
15
+ import { boundPropertyTypes } from "@intentius/chant/codegen/bound-property-types";
16
+ /** ARM resource-level fields that live outside properties.properties. */
17
+ const RESOURCE_LEVEL_FIELDS = new Set([
18
+ "name",
19
+ "type",
20
+ "apiVersion",
21
+ "location",
22
+ "sku",
23
+ "kind",
24
+ "identity",
25
+ "tags",
26
+ "zones",
27
+ "plan",
28
+ ]);
29
+ /** Expression reference pattern in ARM oneOf wrappers. */
30
+ const EXPRESSION_REF_PATTERN = /definitions\.json#\/definitions\/expression|common\/definitions\.json/;
31
+ /**
32
+ * Curated attributes for common ARM resources.
33
+ * ARM schemas lack readOnlyProperties, so we maintain this list.
34
+ */
35
+ const CURATED_ATTRIBUTES = {
36
+ "Microsoft.Storage/storageAccounts": [
37
+ { name: "id", tsType: "string" },
38
+ { name: "primaryEndpoints", tsType: "string" },
39
+ { name: "primaryLocation", tsType: "string" },
40
+ { name: "provisioningState", tsType: "string" },
41
+ ],
42
+ "Microsoft.Compute/virtualMachines": [
43
+ { name: "id", tsType: "string" },
44
+ { name: "vmId", tsType: "string" },
45
+ { name: "provisioningState", tsType: "string" },
46
+ ],
47
+ "Microsoft.Network/virtualNetworks": [
48
+ { name: "id", tsType: "string" },
49
+ { name: "provisioningState", tsType: "string" },
50
+ ],
51
+ "Microsoft.Network/networkSecurityGroups": [
52
+ { name: "id", tsType: "string" },
53
+ { name: "provisioningState", tsType: "string" },
54
+ ],
55
+ "Microsoft.Network/publicIPAddresses": [
56
+ { name: "id", tsType: "string" },
57
+ { name: "ipAddress", tsType: "string" },
58
+ { name: "provisioningState", tsType: "string" },
59
+ ],
60
+ "Microsoft.Network/networkInterfaces": [
61
+ { name: "id", tsType: "string" },
62
+ { name: "provisioningState", tsType: "string" },
63
+ ],
64
+ "Microsoft.Web/serverfarms": [
65
+ { name: "id", tsType: "string" },
66
+ { name: "provisioningState", tsType: "string" },
67
+ ],
68
+ "Microsoft.Web/sites": [
69
+ { name: "id", tsType: "string" },
70
+ { name: "defaultHostName", tsType: "string" },
71
+ { name: "provisioningState", tsType: "string" },
72
+ ],
73
+ "Microsoft.ContainerService/managedClusters": [
74
+ { name: "id", tsType: "string" },
75
+ { name: "fqdn", tsType: "string" },
76
+ { name: "provisioningState", tsType: "string" },
77
+ { name: "nodeResourceGroup", tsType: "string" },
78
+ ],
79
+ "Microsoft.Sql/servers": [
80
+ { name: "id", tsType: "string" },
81
+ { name: "fullyQualifiedDomainName", tsType: "string" },
82
+ { name: "provisioningState", tsType: "string" },
83
+ ],
84
+ "Microsoft.Sql/servers_databases": [
85
+ { name: "id", tsType: "string" },
86
+ { name: "provisioningState", tsType: "string" },
87
+ ],
88
+ "Microsoft.KeyVault/vaults": [
89
+ { name: "id", tsType: "string" },
90
+ { name: "vaultUri", tsType: "string" },
91
+ { name: "provisioningState", tsType: "string" },
92
+ ],
93
+ "Microsoft.ContainerRegistry/registries": [
94
+ { name: "id", tsType: "string" },
95
+ { name: "loginServer", tsType: "string" },
96
+ { name: "provisioningState", tsType: "string" },
97
+ ],
98
+ "Microsoft.DocumentDB/databaseAccounts": [
99
+ { name: "id", tsType: "string" },
100
+ { name: "documentEndpoint", tsType: "string" },
101
+ { name: "provisioningState", tsType: "string" },
102
+ ],
103
+ "Microsoft.Network/loadBalancers": [
104
+ { name: "id", tsType: "string" },
105
+ { name: "provisioningState", tsType: "string" },
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
+ ],
117
+ };
118
+ /**
119
+ * Maximum nesting depth of generated property-type interfaces.
120
+ *
121
+ * ARM schemas embed a named definition for every nested object, and fully
122
+ * expanding all of them per-resource produces a ~273MB declaration that is not
123
+ * shippable as `.d.ts`. We instead emit only the property types reachable from
124
+ * a resource's top-level properties within this depth; references that are
125
+ * deeper, or to definitions no resource property reaches, are loosened to
126
+ * `Record<string, unknown>`. Depth 1 keeps top-level props + their immediate
127
+ * nested object shapes typed; deeper ARM config trees become loose. (#438)
128
+ */
129
+ const MAX_PROPERTY_TYPE_DEPTH = 1;
130
+ /**
131
+ * Property-type definition names that are always emitted as typed interfaces,
132
+ * regardless of reachability/depth — common nested ARM shapes worth typing for
133
+ * authoring ergonomics. Kept in sync with the azure `validate` required-names
134
+ * check (it asserts these survive code generation). (#438)
135
+ */
136
+ export const CURATED_PROPERTY_TYPE_DEFS = new Set([
137
+ "Sku",
138
+ "Identity",
139
+ "NetworkProfile",
140
+ "StorageProfile",
141
+ "OsDisk",
142
+ "ImageReference",
143
+ "IpConfiguration",
144
+ "SecurityRule",
145
+ "SubnetProperties",
146
+ "SiteConfig",
147
+ ]);
148
+ /**
149
+ * Parse an ARM schema (exploded per-resource format) into typed structures.
150
+ */
151
+ export function parseArmSchema(data) {
152
+ const raw = JSON.parse(typeof data === "string" ? data : data.toString("utf-8"));
153
+ const resourceType = raw.resourceType;
154
+ const apiVersion = raw.apiVersion;
155
+ const resourceDef = raw.resourceDefinition;
156
+ const definitions = raw.definitions ?? {};
157
+ // Deployment scopes stamped by the fetch explode (#1545). Only carried
158
+ // through when the resource exists somewhere other than plain
159
+ // resource-group scope, so the common case stays field-free.
160
+ const rawScopes = raw.deployScopes;
161
+ const deployScopes = rawScopes && !(rawScopes.length === 1 && rawScopes[0] === "resourceGroup")
162
+ ? rawScopes
163
+ : undefined;
164
+ const shortName = armShortName(resourceType);
165
+ const fakeSchema = { definitions };
166
+ // Parse resource-level fields and nested properties
167
+ const props = [];
168
+ const resourceLevelFields = [];
169
+ if (resourceDef.properties) {
170
+ for (const [name, prop] of Object.entries(resourceDef.properties)) {
171
+ if (name === "type" || name === "apiVersion")
172
+ continue; // These are injected
173
+ const unwrapped = unwrapExpressionOneOf(prop);
174
+ if (RESOURCE_LEVEL_FIELDS.has(name)) {
175
+ resourceLevelFields.push(name);
176
+ }
177
+ // If this is the "properties" container, expand its sub-properties
178
+ if (name === "properties" && unwrapped.properties) {
179
+ const innerRequired = new Set(unwrapped.required ?? []);
180
+ for (const [innerName, innerProp] of Object.entries(unwrapped.properties)) {
181
+ const innerUnwrapped = unwrapExpressionOneOf(innerProp);
182
+ const tsType = resolvePropertyType(innerUnwrapped, definitions, shortName);
183
+ props.push({
184
+ name: innerName,
185
+ tsType,
186
+ required: innerRequired.has(innerName),
187
+ description: innerUnwrapped.description,
188
+ enum: innerUnwrapped.enum,
189
+ constraints: extractConstraints(innerUnwrapped),
190
+ });
191
+ }
192
+ continue;
193
+ }
194
+ const tsType = resolvePropertyType(unwrapped, definitions, shortName);
195
+ const requiredSet = new Set(resourceDef.required ?? []);
196
+ props.push({
197
+ name,
198
+ tsType,
199
+ required: requiredSet.has(name),
200
+ description: unwrapped.description,
201
+ enum: unwrapped.enum,
202
+ constraints: extractConstraints(unwrapped),
203
+ });
204
+ }
205
+ }
206
+ // Parse definitions into property types and enums
207
+ const propertyTypes = [];
208
+ const enums = [];
209
+ for (const [defName, def] of Object.entries(definitions)) {
210
+ // Skip expression-related definitions
211
+ if (defName === "expression" || defName.startsWith("http"))
212
+ continue;
213
+ if (isEnumDefinition(def)) {
214
+ enums.push({
215
+ name: `${shortName}_${defName}`,
216
+ values: def.enum,
217
+ });
218
+ continue;
219
+ }
220
+ if (def.properties) {
221
+ const defRequired = new Set(def.required ?? []);
222
+ const defProps = [];
223
+ for (const [propName, prop] of Object.entries(def.properties)) {
224
+ const unwrapped = unwrapExpressionOneOf(prop);
225
+ const tsType = resolvePropertyType(unwrapped, definitions, shortName);
226
+ defProps.push({
227
+ name: propName,
228
+ tsType,
229
+ required: defRequired.has(propName),
230
+ description: unwrapped.description,
231
+ enum: unwrapped.enum,
232
+ constraints: extractConstraints(unwrapped),
233
+ });
234
+ }
235
+ propertyTypes.push({
236
+ name: `${shortName}_${defName}`,
237
+ specType: defName,
238
+ properties: defProps,
239
+ });
240
+ }
241
+ }
242
+ // Attributes from curated map
243
+ const attrs = CURATED_ATTRIBUTES[resourceType] ?? [
244
+ { name: "id", tsType: "string" },
245
+ ];
246
+ // Bound the emitted property types to keep the generated declaration shippable (#438).
247
+ const boundedPropertyTypes = boundPropertyTypes(shortName, props, propertyTypes, new Set(enums.map((e) => e.name)), { maxDepth: MAX_PROPERTY_TYPE_DEPTH, curatedDefs: CURATED_PROPERTY_TYPE_DEFS });
248
+ // Detect tagging support (if tags field exists)
249
+ const hasTags = resourceLevelFields.includes("tags");
250
+ const tagging = hasTags
251
+ ? { taggable: true, tagOnCreate: true, tagUpdatable: true }
252
+ : undefined;
253
+ return {
254
+ resource: {
255
+ typeName: resourceType,
256
+ apiVersion,
257
+ properties: props,
258
+ attributes: attrs,
259
+ resourceLevelFields,
260
+ ...(tagging && { tagging }),
261
+ ...(deployScopes && { deployScopes }),
262
+ },
263
+ propertyTypes: boundedPropertyTypes,
264
+ enums,
265
+ };
266
+ }
267
+ // --- Unwrap oneOf expression wrappers ---
268
+ /**
269
+ * ARM schemas wrap real types in oneOf with an expression $ref.
270
+ * Detect this pattern and return the real type.
271
+ */
272
+ function unwrapExpressionOneOf(prop) {
273
+ if (!prop.oneOf || prop.oneOf.length < 2)
274
+ return prop;
275
+ const nonExpr = prop.oneOf.filter((item) => {
276
+ if (item.$ref && EXPRESSION_REF_PATTERN.test(item.$ref))
277
+ return false;
278
+ return true;
279
+ });
280
+ if (nonExpr.length === 1) {
281
+ // Merge description from parent if present
282
+ const result = { ...nonExpr[0] };
283
+ if (prop.description && !result.description) {
284
+ result.description = prop.description;
285
+ }
286
+ return result;
287
+ }
288
+ return prop;
289
+ }
290
+ // --- Type resolution (delegated to core) ---
291
+ function resolvePropertyType(prop, definitions, shortName) {
292
+ const fakeDoc = { definitions };
293
+ return coreResolvePropertyType(prop, fakeDoc, (defName) => `${shortName}_${defName}`);
294
+ }
295
+ function extractConstraints(prop) {
296
+ return coreExtractConstraints(prop);
297
+ }
298
+ export const constraintsIsEmpty = coreConstraintsIsEmpty;
299
+ function isEnumDefinition(def) {
300
+ return coreIsEnumDefinition(def);
301
+ }
302
+ /**
303
+ * Extract short resource name:
304
+ * "Microsoft.Storage/storageAccounts" → "storageAccounts"
305
+ */
306
+ export function armShortName(typeName) {
307
+ const slashIdx = typeName.lastIndexOf("/");
308
+ return slashIdx >= 0 ? typeName.slice(slashIdx + 1) : typeName;
309
+ }
310
+ /**
311
+ * Extract service name:
312
+ * "Microsoft.Storage/storageAccounts" → "Storage"
313
+ */
314
+ export function armServiceName(typeName) {
315
+ const match = typeName.match(/^Microsoft\.([^/]+)/);
316
+ // Strip dots from hierarchical providers (e.g. "App.ContainerApps" → "AppContainerApps")
317
+ return match ? match[1].replace(/\./g, "") : typeName;
318
+ }