@peterhauge/apiops-cli 0.1.3-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +135 -0
  3. package/dist/cli/extract-command.d.ts +12 -0
  4. package/dist/cli/extract-command.d.ts.map +1 -0
  5. package/dist/cli/extract-command.js +157 -0
  6. package/dist/cli/extract-command.js.map +1 -0
  7. package/dist/cli/index.d.ts +7 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +74 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/cli/init-command.d.ts +11 -0
  12. package/dist/cli/init-command.d.ts.map +1 -0
  13. package/dist/cli/init-command.js +87 -0
  14. package/dist/cli/init-command.js.map +1 -0
  15. package/dist/cli/publish-command.d.ts +12 -0
  16. package/dist/cli/publish-command.d.ts.map +1 -0
  17. package/dist/cli/publish-command.js +159 -0
  18. package/dist/cli/publish-command.js.map +1 -0
  19. package/dist/clients/apim-client.d.ts +110 -0
  20. package/dist/clients/apim-client.d.ts.map +1 -0
  21. package/dist/clients/apim-client.js +586 -0
  22. package/dist/clients/apim-client.js.map +1 -0
  23. package/dist/clients/artifact-store.d.ts +23 -0
  24. package/dist/clients/artifact-store.d.ts.map +1 -0
  25. package/dist/clients/artifact-store.js +188 -0
  26. package/dist/clients/artifact-store.js.map +1 -0
  27. package/dist/clients/iapim-client.d.ts +52 -0
  28. package/dist/clients/iapim-client.d.ts.map +1 -0
  29. package/dist/clients/iapim-client.js +6 -0
  30. package/dist/clients/iapim-client.js.map +1 -0
  31. package/dist/clients/iartifact-store.d.ts +50 -0
  32. package/dist/clients/iartifact-store.d.ts.map +1 -0
  33. package/dist/clients/iartifact-store.js +6 -0
  34. package/dist/clients/iartifact-store.js.map +1 -0
  35. package/dist/lib/auto-generated.d.ts +27 -0
  36. package/dist/lib/auto-generated.d.ts.map +1 -0
  37. package/dist/lib/auto-generated.js +34 -0
  38. package/dist/lib/auto-generated.js.map +1 -0
  39. package/dist/lib/cloud-config.d.ts +29 -0
  40. package/dist/lib/cloud-config.d.ts.map +1 -0
  41. package/dist/lib/cloud-config.js +60 -0
  42. package/dist/lib/cloud-config.js.map +1 -0
  43. package/dist/lib/config-loader.d.ts +21 -0
  44. package/dist/lib/config-loader.d.ts.map +1 -0
  45. package/dist/lib/config-loader.js +131 -0
  46. package/dist/lib/config-loader.js.map +1 -0
  47. package/dist/lib/dependency-graph.d.ts +43 -0
  48. package/dist/lib/dependency-graph.d.ts.map +1 -0
  49. package/dist/lib/dependency-graph.js +163 -0
  50. package/dist/lib/dependency-graph.js.map +1 -0
  51. package/dist/lib/exit-codes.d.ts +27 -0
  52. package/dist/lib/exit-codes.d.ts.map +1 -0
  53. package/dist/lib/exit-codes.js +33 -0
  54. package/dist/lib/exit-codes.js.map +1 -0
  55. package/dist/lib/logger.d.ts +39 -0
  56. package/dist/lib/logger.d.ts.map +1 -0
  57. package/dist/lib/logger.js +128 -0
  58. package/dist/lib/logger.js.map +1 -0
  59. package/dist/lib/parallel-runner.d.ts +38 -0
  60. package/dist/lib/parallel-runner.d.ts.map +1 -0
  61. package/dist/lib/parallel-runner.js +70 -0
  62. package/dist/lib/parallel-runner.js.map +1 -0
  63. package/dist/lib/resource-path.d.ts +205 -0
  64. package/dist/lib/resource-path.d.ts.map +1 -0
  65. package/dist/lib/resource-path.js +401 -0
  66. package/dist/lib/resource-path.js.map +1 -0
  67. package/dist/lib/resource-uri.d.ts +40 -0
  68. package/dist/lib/resource-uri.d.ts.map +1 -0
  69. package/dist/lib/resource-uri.js +86 -0
  70. package/dist/lib/resource-uri.js.map +1 -0
  71. package/dist/lib/user-agent.d.ts +2 -0
  72. package/dist/lib/user-agent.d.ts.map +1 -0
  73. package/dist/lib/user-agent.js +5 -0
  74. package/dist/lib/user-agent.js.map +1 -0
  75. package/dist/models/config.d.ts +83 -0
  76. package/dist/models/config.d.ts.map +1 -0
  77. package/dist/models/config.js +6 -0
  78. package/dist/models/config.js.map +1 -0
  79. package/dist/models/resource-types.d.ts +66 -0
  80. package/dist/models/resource-types.d.ts.map +1 -0
  81. package/dist/models/resource-types.js +243 -0
  82. package/dist/models/resource-types.js.map +1 -0
  83. package/dist/models/types.d.ts +47 -0
  84. package/dist/models/types.d.ts.map +1 -0
  85. package/dist/models/types.js +6 -0
  86. package/dist/models/types.js.map +1 -0
  87. package/dist/services/api-extractor.d.ts +36 -0
  88. package/dist/services/api-extractor.d.ts.map +1 -0
  89. package/dist/services/api-extractor.js +319 -0
  90. package/dist/services/api-extractor.js.map +1 -0
  91. package/dist/services/api-publisher.d.ts +18 -0
  92. package/dist/services/api-publisher.d.ts.map +1 -0
  93. package/dist/services/api-publisher.js +290 -0
  94. package/dist/services/api-publisher.js.map +1 -0
  95. package/dist/services/delete-unmatched-service.d.ts +17 -0
  96. package/dist/services/delete-unmatched-service.d.ts.map +1 -0
  97. package/dist/services/delete-unmatched-service.js +143 -0
  98. package/dist/services/delete-unmatched-service.js.map +1 -0
  99. package/dist/services/dry-run-reporter.d.ts +30 -0
  100. package/dist/services/dry-run-reporter.d.ts.map +1 -0
  101. package/dist/services/dry-run-reporter.js +111 -0
  102. package/dist/services/dry-run-reporter.js.map +1 -0
  103. package/dist/services/extract-service.d.ts +47 -0
  104. package/dist/services/extract-service.d.ts.map +1 -0
  105. package/dist/services/extract-service.js +374 -0
  106. package/dist/services/extract-service.js.map +1 -0
  107. package/dist/services/filter-service.d.ts +29 -0
  108. package/dist/services/filter-service.d.ts.map +1 -0
  109. package/dist/services/filter-service.js +143 -0
  110. package/dist/services/filter-service.js.map +1 -0
  111. package/dist/services/git-diff-service.d.ts +23 -0
  112. package/dist/services/git-diff-service.d.ts.map +1 -0
  113. package/dist/services/git-diff-service.js +135 -0
  114. package/dist/services/git-diff-service.js.map +1 -0
  115. package/dist/services/identity-guide-service.d.ts +11 -0
  116. package/dist/services/identity-guide-service.d.ts.map +1 -0
  117. package/dist/services/identity-guide-service.js +227 -0
  118. package/dist/services/identity-guide-service.js.map +1 -0
  119. package/dist/services/init-service.d.ts +16 -0
  120. package/dist/services/init-service.d.ts.map +1 -0
  121. package/dist/services/init-service.js +304 -0
  122. package/dist/services/init-service.js.map +1 -0
  123. package/dist/services/keyvault-checker.d.ts +58 -0
  124. package/dist/services/keyvault-checker.d.ts.map +1 -0
  125. package/dist/services/keyvault-checker.js +390 -0
  126. package/dist/services/keyvault-checker.js.map +1 -0
  127. package/dist/services/override-merger.d.ts +20 -0
  128. package/dist/services/override-merger.d.ts.map +1 -0
  129. package/dist/services/override-merger.js +102 -0
  130. package/dist/services/override-merger.js.map +1 -0
  131. package/dist/services/product-extractor.d.ts +26 -0
  132. package/dist/services/product-extractor.d.ts.map +1 -0
  133. package/dist/services/product-extractor.js +141 -0
  134. package/dist/services/product-extractor.js.map +1 -0
  135. package/dist/services/product-publisher.d.ts +15 -0
  136. package/dist/services/product-publisher.d.ts.map +1 -0
  137. package/dist/services/product-publisher.js +113 -0
  138. package/dist/services/product-publisher.js.map +1 -0
  139. package/dist/services/prompt-service.d.ts +13 -0
  140. package/dist/services/prompt-service.d.ts.map +1 -0
  141. package/dist/services/prompt-service.js +69 -0
  142. package/dist/services/prompt-service.js.map +1 -0
  143. package/dist/services/publish-service.d.ts +31 -0
  144. package/dist/services/publish-service.d.ts.map +1 -0
  145. package/dist/services/publish-service.js +445 -0
  146. package/dist/services/publish-service.js.map +1 -0
  147. package/dist/services/resource-extractor.d.ts +52 -0
  148. package/dist/services/resource-extractor.d.ts.map +1 -0
  149. package/dist/services/resource-extractor.js +168 -0
  150. package/dist/services/resource-extractor.js.map +1 -0
  151. package/dist/services/resource-publisher.d.ts +23 -0
  152. package/dist/services/resource-publisher.d.ts.map +1 -0
  153. package/dist/services/resource-publisher.js +349 -0
  154. package/dist/services/resource-publisher.js.map +1 -0
  155. package/dist/services/secret-redactor.d.ts +20 -0
  156. package/dist/services/secret-redactor.d.ts.map +1 -0
  157. package/dist/services/secret-redactor.js +45 -0
  158. package/dist/services/secret-redactor.js.map +1 -0
  159. package/dist/services/transitive-resolver.d.ts +45 -0
  160. package/dist/services/transitive-resolver.d.ts.map +1 -0
  161. package/dist/services/transitive-resolver.js +177 -0
  162. package/dist/services/transitive-resolver.js.map +1 -0
  163. package/dist/services/workspace-extractor.d.ts +34 -0
  164. package/dist/services/workspace-extractor.d.ts.map +1 -0
  165. package/dist/services/workspace-extractor.js +120 -0
  166. package/dist/services/workspace-extractor.js.map +1 -0
  167. package/dist/templates/azure-devops/extract-pipeline.d.ts +9 -0
  168. package/dist/templates/azure-devops/extract-pipeline.d.ts.map +1 -0
  169. package/dist/templates/azure-devops/extract-pipeline.js +95 -0
  170. package/dist/templates/azure-devops/extract-pipeline.js.map +1 -0
  171. package/dist/templates/azure-devops/publish-pipeline.d.ts +10 -0
  172. package/dist/templates/azure-devops/publish-pipeline.d.ts.map +1 -0
  173. package/dist/templates/azure-devops/publish-pipeline.js +100 -0
  174. package/dist/templates/azure-devops/publish-pipeline.js.map +1 -0
  175. package/dist/templates/configs/filter-config.d.ts +6 -0
  176. package/dist/templates/configs/filter-config.d.ts.map +1 -0
  177. package/dist/templates/configs/filter-config.js +51 -0
  178. package/dist/templates/configs/filter-config.js.map +1 -0
  179. package/dist/templates/configs/override-config.d.ts +6 -0
  180. package/dist/templates/configs/override-config.d.ts.map +1 -0
  181. package/dist/templates/configs/override-config.js +45 -0
  182. package/dist/templates/configs/override-config.js.map +1 -0
  183. package/dist/templates/configs/package-json.d.ts +10 -0
  184. package/dist/templates/configs/package-json.d.ts.map +1 -0
  185. package/dist/templates/configs/package-json.js +19 -0
  186. package/dist/templates/configs/package-json.js.map +1 -0
  187. package/dist/templates/copilot/identity-setup-prompt.d.ts +13 -0
  188. package/dist/templates/copilot/identity-setup-prompt.d.ts.map +1 -0
  189. package/dist/templates/copilot/identity-setup-prompt.js +279 -0
  190. package/dist/templates/copilot/identity-setup-prompt.js.map +1 -0
  191. package/dist/templates/github-actions/extract-workflow.d.ts +9 -0
  192. package/dist/templates/github-actions/extract-workflow.d.ts.map +1 -0
  193. package/dist/templates/github-actions/extract-workflow.js +126 -0
  194. package/dist/templates/github-actions/extract-workflow.js.map +1 -0
  195. package/dist/templates/github-actions/publish-workflow.d.ts +10 -0
  196. package/dist/templates/github-actions/publish-workflow.d.ts.map +1 -0
  197. package/dist/templates/github-actions/publish-workflow.js +105 -0
  198. package/dist/templates/github-actions/publish-workflow.js.map +1 -0
  199. package/package.json +65 -0
@@ -0,0 +1,401 @@
1
+ /**
2
+ * T013: Resource descriptor ↔ artifact file path mapping
3
+ * Map descriptor to directory/file paths per data-model.md artifact conventions
4
+ */
5
+ import * as path from 'node:path';
6
+ import { RESOURCE_TYPE_METADATA, ResourceType } from '../models/resource-types.js';
7
+ /**
8
+ * Association resource types that represent parent-child relationships
9
+ * (not independently publishable resources).
10
+ * These are handled specially during publishing via association files
11
+ * (apis.json, groups.json) and should not be discovered as individual resources.
12
+ */
13
+ const ASSOCIATION_TYPES = new Set([
14
+ ResourceType.ProductApi,
15
+ ResourceType.ProductGroup,
16
+ ResourceType.ProductTag,
17
+ ResourceType.GatewayApi,
18
+ ]);
19
+ /**
20
+ * Fills all positional `{i}` tokens in a template string with `nameParts[i]`.
21
+ * Throws if a placeholder index has no corresponding entry in `nameParts`.
22
+ *
23
+ * Examples:
24
+ * formatTemplatePath('apis/{0}/operations/{1}', ['petstore', 'get-user'])
25
+ * → 'apis/petstore/operations/get-user'
26
+ * formatTemplatePath('', []) → ''
27
+ */
28
+ export function formatTemplatePath(template, nameParts) {
29
+ return template.replace(/\{(\d+)\}/g, (_match, idx) => {
30
+ const i = +idx;
31
+ const value = nameParts[i];
32
+ if (value === undefined) {
33
+ throw new Error(`formatTemplatePath: nameParts[${i}] is undefined for template "${template}" ` +
34
+ `(nameParts has ${nameParts.length} entries)`);
35
+ }
36
+ return value;
37
+ });
38
+ }
39
+ /**
40
+ * Returns the number of positional `{i}` placeholders in a template string.
41
+ *
42
+ * Used by callers that need to validate that enough name-parts have been
43
+ * supplied before filling a template — without performing regex matching
44
+ * themselves.
45
+ *
46
+ * Example:
47
+ * countTemplatePlaceholders('apis/{0}/operations/{1}') → 2
48
+ * countTemplatePlaceholders('policies/policy') → 0
49
+ */
50
+ export function countTemplatePlaceholders(template) {
51
+ return (template.match(/\{\d+\}/g) ?? []).length;
52
+ }
53
+ /**
54
+ * Ensures a path starts with a single leading slash.
55
+ *
56
+ * Example:
57
+ * makeFullPath('namedValues/my-nv') → '/namedValues/my-nv'
58
+ * makeFullPath('/namedValues/my-nv') → '/namedValues/my-nv'
59
+ */
60
+ export function makeFullPath(relativePath) {
61
+ return relativePath.startsWith('/') ? relativePath : `/${relativePath}`;
62
+ }
63
+ /**
64
+ * Strips a single leading slash from a path, if present.
65
+ *
66
+ * Example:
67
+ * makeRelativePath('/namedValues/my-nv') → 'namedValues/my-nv'
68
+ * makeRelativePath('namedValues/my-nv') → 'namedValues/my-nv'
69
+ */
70
+ export function makeRelativePath(absolutePath) {
71
+ return absolutePath.startsWith('/') ? absolutePath.substring(1) : absolutePath;
72
+ }
73
+ /**
74
+ * Returns the resource's own name — the last element of `nameParts`.
75
+ *
76
+ * For 1-part types (e.g. Api, Product) `nameParts` contains only the own
77
+ * name, so this is equivalent to `nameParts[0]`. For 2-part types (e.g.
78
+ * ApiOperation, ProductTag) the own name is always the final element, with
79
+ * the parent name preceding it. Using this helper instead of a hard-coded
80
+ * index guards against future arity changes and avoids accidentally
81
+ * returning the parent name.
82
+ *
83
+ * Throws a `RangeError` if `nameParts` is empty.
84
+ *
85
+ * Examples:
86
+ * getNameFromNameParts(['petstore']) → 'petstore'
87
+ * getNameFromNameParts(['petstore', 'get-user']) → 'get-user'
88
+ * getNameFromNameParts([]) → throws RangeError
89
+ */
90
+ export function getNameFromNameParts(nameParts) {
91
+ const value = nameParts[nameParts.length - 1];
92
+ if (value === undefined) {
93
+ throw new RangeError('getNameFromNameParts: nameParts is empty');
94
+ }
95
+ return value;
96
+ }
97
+ /**
98
+ * Returns `nameParts[index]`, throwing a descriptive `RangeError` if the
99
+ * index is out of range.
100
+ *
101
+ * Prefer this over direct bracket access (`nameParts[0]`) so that missing
102
+ * name-parts surface as an explicit error rather than a silent `undefined`.
103
+ *
104
+ * Examples:
105
+ * getNamePart(['petstore', 'get-user'], 0) → 'petstore'
106
+ * getNamePart(['petstore', 'get-user'], 1) → 'get-user'
107
+ * getNamePart([], 0) → throws RangeError
108
+ */
109
+ export function getNamePart(nameParts, index) {
110
+ const value = nameParts[index];
111
+ if (value === undefined) {
112
+ throw new RangeError(`getNamePart: nameParts[${index}] is out of range ` +
113
+ `(nameParts has ${nameParts.length} ${nameParts.length === 1 ? 'entry' : 'entries'})`);
114
+ }
115
+ return value;
116
+ }
117
+ /**
118
+ * Converts a positional template string to a capturing regex.
119
+ * Each `{i}` placeholder becomes a `([^/]+)` capture group; all other
120
+ * regex-special characters are escaped. A trailing slash (if present)
121
+ * is stripped before building the pattern.
122
+ *
123
+ * This function is intentionally **not exported** — callers should use the
124
+ * higher-level `parseTemplatePath` helper rather than constructing regexes
125
+ * directly.
126
+ */
127
+ function templateToRegex(template) {
128
+ const source = template
129
+ .replace(/\/+$/, '') // strip any trailing slash
130
+ .replace(/[.+^${}()|[\]\\]/g, (ch) => (ch === '{' || ch === '}' ? ch : `\\${ch}`))
131
+ .replace(/\{\d+\}/g, '([^/]+)');
132
+ return new RegExp(`^${source}$`);
133
+ }
134
+ /**
135
+ * Matches a positional template against a slash-delimited path string and
136
+ * returns the captured name-part values in positional order, or `undefined`
137
+ * if the path does not match the template.
138
+ *
139
+ * Both artifact-directory paths and ARM path suffixes use the same
140
+ * `{0}`, `{1}` placeholder syntax, so this single function handles both.
141
+ * Callers are not required to know anything about regexes.
142
+ *
143
+ * Examples:
144
+ * parseTemplatePath('apis/{0}/operations/{1}', 'apis/petstore/operations/get')
145
+ * → ['petstore', 'get']
146
+ * parseTemplatePath('policies/policy', 'policies/policy')
147
+ * → []
148
+ * parseTemplatePath('apis/{0}', 'backends/b1')
149
+ * → undefined
150
+ */
151
+ export function parseTemplatePath(template, path) {
152
+ const match = templateToRegex(template).exec(path);
153
+ if (!match)
154
+ return undefined;
155
+ // Captures correspond directly to {0}, {1}, … positions in the template
156
+ return match.slice(1);
157
+ }
158
+ /**
159
+ * Builds the artifact directory path for a given descriptor.
160
+ * Returns the directory where this resource's files should be stored.
161
+ *
162
+ * @param baseDir - Root artifact directory
163
+ * @param descriptor - Resource descriptor
164
+ * @returns Full directory path (OS-normalized)
165
+ */
166
+ export function buildArtifactDirectory(baseDir, descriptor) {
167
+ const metadata = RESOURCE_TYPE_METADATA[descriptor.type];
168
+ let resolvedDir = formatTemplatePath(metadata.artifactDirectory, descriptor.nameParts);
169
+ // Handle workspace prefix
170
+ if (descriptor.workspace) {
171
+ resolvedDir = path.join('workspaces', descriptor.workspace, resolvedDir);
172
+ }
173
+ return path.join(baseDir, resolvedDir);
174
+ }
175
+ /**
176
+ * Builds the full artifact file path for a given descriptor.
177
+ * Returns the path to the info file (JSON/XML/MD).
178
+ *
179
+ * @param baseDir - Root artifact directory
180
+ * @param descriptor - Resource descriptor
181
+ * @returns Full file path (OS-normalized), or undefined if resource has no info file
182
+ */
183
+ export function buildArtifactFilePath(baseDir, descriptor) {
184
+ const metadata = RESOURCE_TYPE_METADATA[descriptor.type];
185
+ if (!metadata.infoFile) {
186
+ return undefined;
187
+ }
188
+ const directory = buildArtifactDirectory(baseDir, descriptor);
189
+ return path.join(directory, metadata.infoFile);
190
+ }
191
+ /**
192
+ * Builds the policy file path for a resource that supports policies.
193
+ *
194
+ * @param baseDir - Root artifact directory
195
+ * @param descriptor - Resource descriptor
196
+ * @returns Full path to policy.xml file
197
+ */
198
+ export function buildPolicyFilePath(baseDir, descriptor) {
199
+ const directory = buildArtifactDirectory(baseDir, descriptor);
200
+ return path.join(directory, 'policy.xml');
201
+ }
202
+ /**
203
+ * Builds the API specification file path.
204
+ *
205
+ * @param baseDir - Root artifact directory
206
+ * @param descriptor - API descriptor
207
+ * @param format - Specification format (yaml, json, graphql, wsdl, wadl)
208
+ * @returns Full path to specification file
209
+ */
210
+ export function buildSpecificationFilePath(baseDir, descriptor, format) {
211
+ if (descriptor.type !== ResourceType.Api) {
212
+ throw new Error(`Specification path only valid for API resources, got ${descriptor.type}`);
213
+ }
214
+ const directory = buildArtifactDirectory(baseDir, descriptor);
215
+ const extensionMap = {
216
+ yaml: 'yaml',
217
+ json: 'json',
218
+ graphql: 'graphql',
219
+ wsdl: 'wsdl',
220
+ wadl: 'wadl',
221
+ };
222
+ const extension = extensionMap[format] ?? 'txt';
223
+ return path.join(directory, `specification.${extension}`);
224
+ }
225
+ /**
226
+ * Builds the association file path (apis.json or groups.json).
227
+ *
228
+ * @param baseDir - Root artifact directory
229
+ * @param descriptor - Product or Gateway descriptor
230
+ * @param associationType - Type of association (apis, groups, or tags)
231
+ * @returns Full path to association file
232
+ */
233
+ export function buildAssociationFilePath(baseDir, descriptor, associationType) {
234
+ const validTypes = [ResourceType.Product, ResourceType.Gateway];
235
+ if (!validTypes.includes(descriptor.type)) {
236
+ throw new Error(`Association path only valid for Product/Gateway resources, got ${descriptor.type}`);
237
+ }
238
+ const directory = buildArtifactDirectory(baseDir, descriptor);
239
+ return path.join(directory, `${associationType}.json`);
240
+ }
241
+ /**
242
+ * Derives the list URL path segment(s) from an ARM path suffix template.
243
+ *
244
+ * The list path is structurally derivable from `armPathSuffix`:
245
+ * - Last segment is a fixed word (no placeholder) → singleton; neither path is present.
246
+ * - Exactly one `{N}` placeholder and it is the last segment → top-level resource;
247
+ * `listPath` = the path before the placeholder (with a leading '/').
248
+ * - Two or more `{N}` placeholders and the last segment is the highest-index one →
249
+ * child resource; `childListPath` = the collection segment immediately before the
250
+ * last placeholder (with a leading '/').
251
+ *
252
+ * Examples:
253
+ * deriveListPaths('namedValues/{0}') → { listPath: '/namedValues' }
254
+ * deriveListPaths('apis/{0}/operations/{1}') → { childListPath: '/operations' }
255
+ * deriveListPaths('policies/policy') → {} (singleton)
256
+ * deriveListPaths('apis/{0}/policies/policy') → {} (singleton)
257
+ */
258
+ export function deriveListPaths(template) {
259
+ const segments = template.split('/');
260
+ const lastSeg = segments[segments.length - 1];
261
+ // Singleton: last segment is a fixed word (not a `{N}` placeholder)
262
+ if (!lastSeg || !/^\{\d+\}$/.test(lastSeg)) {
263
+ return {};
264
+ }
265
+ const placeholderCount = countTemplatePlaceholders(template);
266
+ if (placeholderCount === 1) {
267
+ // Top-level resource: list path = everything before the placeholder
268
+ const listBase = segments.slice(0, -1).join('/');
269
+ return { listPath: `/${listBase}` };
270
+ }
271
+ // Child resource: child-list path = collection segment immediately before the last placeholder
272
+ const collectionSeg = segments[segments.length - 2];
273
+ return { childListPath: `/${collectionSeg}` };
274
+ }
275
+ /**
276
+ * Parses an artifact file path back into a ResourceDescriptor.
277
+ * Inverse of buildArtifactFilePath.
278
+ *
279
+ * @param baseDir - Root artifact directory
280
+ * @param filePath - Full path to artifact file
281
+ * @returns ResourceDescriptor or undefined if path doesn't match known patterns
282
+ */
283
+ export function parseArtifactPath(baseDir, filePath) {
284
+ // Get relative path from base directory
285
+ const relativePath = path.relative(baseDir, filePath);
286
+ const parts = relativePath.split(path.sep);
287
+ // Check for workspace prefix
288
+ let workspace;
289
+ let startIndex = 0;
290
+ if (parts[0] === 'workspaces' && parts[1]) {
291
+ workspace = parts[1];
292
+ startIndex = 2;
293
+ }
294
+ // Get the file name
295
+ const fileName = parts[parts.length - 1];
296
+ if (!fileName) {
297
+ return undefined;
298
+ }
299
+ // Try to match against each resource type's pattern
300
+ for (const [typeKey, metadata] of Object.entries(RESOURCE_TYPE_METADATA)) {
301
+ const type = typeKey;
302
+ if (metadata.infoFile !== fileName) {
303
+ continue;
304
+ }
305
+ // Skip association resource types — these are handled specially during publishing
306
+ // via their parent's association files (apis.json, groups.json)
307
+ if (ASSOCIATION_TYPES.has(type)) {
308
+ return undefined;
309
+ }
310
+ const nameParts = parseTemplatePath(metadata.artifactDirectory, parts.slice(startIndex, -1).join('/'));
311
+ if (nameParts !== undefined) {
312
+ return { type, nameParts, workspace };
313
+ }
314
+ }
315
+ return undefined;
316
+ }
317
+ /**
318
+ * Check if a resource type is a singleton (no list, only get).
319
+ * Singletons have armPathSuffix ending with a fixed segment (no `{n}` placeholder).
320
+ * E.g., ServicePolicy (`policies/policy`), ApiWiki (`apis/{0}/wikis/default`).
321
+ */
322
+ export function isSingletonType(type) {
323
+ const meta = RESOURCE_TYPE_METADATA[type];
324
+ // Singleton if the last segment doesn't contain a placeholder
325
+ const lastSegment = meta.armPathSuffix.split('/').pop() ?? '';
326
+ return !lastSegment.includes('{');
327
+ }
328
+ /**
329
+ * Check if a resource type is a child type requiring a parent.
330
+ * Child types have armPathSuffix with more path segments after the first placeholder.
331
+ * E.g., `apis/{0}/tags/{1}` or `apis/{0}/policies/policy`.
332
+ */
333
+ export function isChildType(type) {
334
+ const meta = RESOURCE_TYPE_METADATA[type];
335
+ const placeholderCount = countTemplatePlaceholders(meta.armPathSuffix);
336
+ // 2+ placeholders means it's definitely a child (e.g., apis/{0}/tags/{1})
337
+ if (placeholderCount >= 2)
338
+ return true;
339
+ // Check for nested fixed-name resources under a parent (e.g., apis/{0}/policies/policy)
340
+ const parts = meta.armPathSuffix.split('/');
341
+ const firstPlaceholderIdx = parts.findIndex(p => p.includes('{'));
342
+ return firstPlaceholderIdx >= 0 && firstPlaceholderIdx < parts.length - 1;
343
+ }
344
+ /**
345
+ * Compute the publish tier for a resource type based on ARM path structure.
346
+ * Resources are published from lowest tier to highest; same tier runs in parallel.
347
+ *
348
+ * Tier formula: `placeholderCount * 2 + (hasSegmentsAfterLastPlaceholder ? 1 : 0)`
349
+ *
350
+ * This ensures:
351
+ * - Fewer placeholders = earlier tier (parents before children)
352
+ * - Within same placeholder count, resources ending at a placeholder come
353
+ * before those with fixed segments after (e.g., operations before operation policies)
354
+ *
355
+ * Examples:
356
+ * `apis/{0}` → tier 2 (1 placeholder, ends at placeholder)
357
+ * `apis/{0}/policies/policy` → tier 3 (1 placeholder, has suffix)
358
+ * `apis/{0}/operations/{1}` → tier 4 (2 placeholders, ends at placeholder)
359
+ * `apis/{0}/operations/{1}/policies/policy` → tier 5 (2 placeholders, has suffix)
360
+ */
361
+ export function getPublishTier(type) {
362
+ const meta = RESOURCE_TYPE_METADATA[type];
363
+ const parts = meta.armPathSuffix.split('/');
364
+ const placeholderCount = countTemplatePlaceholders(meta.armPathSuffix);
365
+ // Find the index of the last segment containing a placeholder
366
+ let lastPlaceholderIdx = -1;
367
+ for (let i = 0; i < parts.length; i++) {
368
+ if (parts[i].includes('{')) {
369
+ lastPlaceholderIdx = i;
370
+ }
371
+ }
372
+ // Check if there are segments after the last placeholder
373
+ const hasSegmentsAfter = lastPlaceholderIdx >= 0 && lastPlaceholderIdx < parts.length - 1;
374
+ return placeholderCount * 2 + (hasSegmentsAfter ? 1 : 0);
375
+ }
376
+ /**
377
+ * Check if a resource type is a "grandchild" - has path segments after the last placeholder.
378
+ * These types depend on an intermediate parent that must exist first.
379
+ *
380
+ * @deprecated Use getPublishTier() for N-tier ordering instead
381
+ */
382
+ export function hasNestedParent(type) {
383
+ const meta = RESOURCE_TYPE_METADATA[type];
384
+ const parts = meta.armPathSuffix.split('/');
385
+ // Find the index of the last segment containing a placeholder
386
+ let lastPlaceholderIdx = -1;
387
+ for (let i = 0; i < parts.length; i++) {
388
+ if (parts[i].includes('{')) {
389
+ lastPlaceholderIdx = i;
390
+ }
391
+ }
392
+ // No placeholders = top-level singleton (ServicePolicy), not a grandchild
393
+ if (lastPlaceholderIdx === -1)
394
+ return false;
395
+ // Grandchild if there are segments after the last placeholder
396
+ // AND there are at least 2 placeholders (meaning there's an intermediate parent)
397
+ const hasSegmentsAfter = lastPlaceholderIdx < parts.length - 1;
398
+ const placeholderCount = countTemplatePlaceholders(meta.armPathSuffix);
399
+ return hasSegmentsAfter && placeholderCount >= 2;
400
+ }
401
+ //# sourceMappingURL=resource-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-path.js","sourceRoot":"","sources":["../../src/lib/resource-path.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * T012: Resource descriptor ↔ ARM URI mapping
3
+ * Build full ARM URL from ApimServiceContext + ResourceDescriptor
4
+ */
5
+ import { ApimServiceContext, ResourceDescriptor } from '../models/types.js';
6
+ /**
7
+ * Builds the full ARM resource URI for a given descriptor and service context.
8
+ * Includes workspace prefix if descriptor.workspace is set.
9
+ *
10
+ * @param context - APIM service context containing base URL
11
+ * @param descriptor - Resource descriptor with type and nameParts
12
+ * @returns Full ARM URL including API version query parameter
13
+ */
14
+ export declare function buildArmUri(context: ApimServiceContext, descriptor: ResourceDescriptor): string;
15
+ /**
16
+ * Parses an ARM resource URI into a ResourceDescriptor.
17
+ * Inverse of buildArmUri.
18
+ *
19
+ * @param uri - Full ARM resource URI
20
+ * @param context - APIM service context for validation
21
+ * @returns ResourceDescriptor or undefined if URI doesn't match expected pattern
22
+ */
23
+ export declare function parseArmUri(uri: string, context: ApimServiceContext): ResourceDescriptor | undefined;
24
+ /**
25
+ * Builds a human-readable ARM-path-style label for log output.
26
+ *
27
+ * Fills every `{i}` placeholder in `armPathSuffix` with `nameParts[i]`
28
+ * (no encoding — this is for display only).
29
+ *
30
+ * Examples:
31
+ * NamedValue ['mySecret'] → 'namedValues/mySecret'
32
+ * ApiOperation ['petstore', 'get-user'] → 'apis/petstore/operations/get-user'
33
+ * ApiOperationPolicy ['petstore', 'get'] → 'apis/petstore/operations/get/policies/policy'
34
+ * ServicePolicy [] → 'policies/policy'
35
+ *
36
+ * @param descriptor - Resource descriptor
37
+ * @returns ARM-path-style label string
38
+ */
39
+ export declare function buildResourceLabel(descriptor: ResourceDescriptor): string;
40
+ //# sourceMappingURL=resource-uri.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-uri.d.ts","sourceRoot":"","sources":["../../src/lib/resource-uri.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAI5E;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,kBAAkB,EAC3B,UAAU,EAAE,kBAAkB,GAC7B,MAAM,CAoBR;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,kBAAkB,GAC1B,kBAAkB,GAAG,SAAS,CAmChC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,kBAAkB,GAAG,MAAM,CAIzE"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * T012: Resource descriptor ↔ ARM URI mapping
3
+ * Build full ARM URL from ApimServiceContext + ResourceDescriptor
4
+ */
5
+ import { RESOURCE_TYPE_METADATA } from '../models/resource-types.js';
6
+ import { formatTemplatePath, parseTemplatePath, countTemplatePlaceholders, makeFullPath, makeRelativePath } from './resource-path.js';
7
+ /**
8
+ * Builds the full ARM resource URI for a given descriptor and service context.
9
+ * Includes workspace prefix if descriptor.workspace is set.
10
+ *
11
+ * @param context - APIM service context containing base URL
12
+ * @param descriptor - Resource descriptor with type and nameParts
13
+ * @returns Full ARM URL including API version query parameter
14
+ */
15
+ export function buildArmUri(context, descriptor) {
16
+ const metadata = RESOURCE_TYPE_METADATA[descriptor.type];
17
+ // Validate that all positional placeholders have a corresponding name-part
18
+ const placeholderCount = countTemplatePlaceholders(metadata.armPathSuffix);
19
+ if (descriptor.nameParts.length < placeholderCount) {
20
+ throw new Error(`Unresolved placeholder in ARM path for ${descriptor.type}: expected ${placeholderCount} name-parts, got ${descriptor.nameParts.length}`);
21
+ }
22
+ // URL-encode each name part before filling the ARM path template
23
+ const armPath = formatTemplatePath(metadata.armPathSuffix, descriptor.nameParts.map(encodeURIComponent));
24
+ // Add workspace prefix if workspace-scoped; prepend '/' to produce an absolute path
25
+ const fullPath = descriptor.workspace
26
+ ? makeFullPath(`workspaces/${encodeURIComponent(descriptor.workspace)}/${armPath}`)
27
+ : makeFullPath(armPath);
28
+ return `${context.baseUrl}${fullPath}?api-version=${context.apiVersion}`;
29
+ }
30
+ /**
31
+ * Parses an ARM resource URI into a ResourceDescriptor.
32
+ * Inverse of buildArmUri.
33
+ *
34
+ * @param uri - Full ARM resource URI
35
+ * @param context - APIM service context for validation
36
+ * @returns ResourceDescriptor or undefined if URI doesn't match expected pattern
37
+ */
38
+ export function parseArmUri(uri, context) {
39
+ // Remove query parameters
40
+ const urlWithoutQuery = uri.split('?')[0];
41
+ // Remove base URL to get relative path
42
+ if (!urlWithoutQuery?.startsWith(context.baseUrl)) {
43
+ return undefined;
44
+ }
45
+ let relativePath = urlWithoutQuery.substring(context.baseUrl.length);
46
+ // Extract workspace if present
47
+ let workspace;
48
+ const workspaceMatch = relativePath.match(/^\/workspaces\/([^/]+)(.*)/);
49
+ if (workspaceMatch && workspaceMatch[1] && workspaceMatch[2]) {
50
+ workspace = decodeURIComponent(workspaceMatch[1]);
51
+ relativePath = workspaceMatch[2];
52
+ }
53
+ // Strip the leading '/' — armPathSuffix templates have no leading slash
54
+ relativePath = makeRelativePath(relativePath);
55
+ // Try to match against each resource type's ARM path pattern
56
+ for (const [typeKey, metadata] of Object.entries(RESOURCE_TYPE_METADATA)) {
57
+ const type = typeKey;
58
+ // Use the shared parseTemplatePath helper — no direct regex construction here
59
+ const nameParts = parseTemplatePath(metadata.armPathSuffix, relativePath);
60
+ if (nameParts) {
61
+ return { type, nameParts: nameParts.map((m) => decodeURIComponent(m)), workspace };
62
+ }
63
+ }
64
+ return undefined;
65
+ }
66
+ /**
67
+ * Builds a human-readable ARM-path-style label for log output.
68
+ *
69
+ * Fills every `{i}` placeholder in `armPathSuffix` with `nameParts[i]`
70
+ * (no encoding — this is for display only).
71
+ *
72
+ * Examples:
73
+ * NamedValue ['mySecret'] → 'namedValues/mySecret'
74
+ * ApiOperation ['petstore', 'get-user'] → 'apis/petstore/operations/get-user'
75
+ * ApiOperationPolicy ['petstore', 'get'] → 'apis/petstore/operations/get/policies/policy'
76
+ * ServicePolicy [] → 'policies/policy'
77
+ *
78
+ * @param descriptor - Resource descriptor
79
+ * @returns ARM-path-style label string
80
+ */
81
+ export function buildResourceLabel(descriptor) {
82
+ const metadata = RESOURCE_TYPE_METADATA[descriptor.type];
83
+ // armPathSuffix has no leading slash, so the result is already relative
84
+ return formatTemplatePath(metadata.armPathSuffix, descriptor.nameParts);
85
+ }
86
+ //# sourceMappingURL=resource-uri.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-uri.js","sourceRoot":"","sources":["../../src/lib/resource-uri.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const USER_AGENT: string;
2
+ //# sourceMappingURL=user-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-agent.d.ts","sourceRoot":"","sources":["../../src/lib/user-agent.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,QAA8B,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { createRequire } from 'module';
2
+ const require = createRequire(import.meta.url);
3
+ const pkg = require('../../package.json');
4
+ export const USER_AGENT = `apiops-cli/${pkg.version}`;
5
+ //# sourceMappingURL=user-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-agent.js","sourceRoot":"","sources":["../../src/lib/user-agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEvC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,oBAAoB,CAAwB,CAAC;AAEjE,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,GAAG,CAAC,OAAO,EAAE,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * T008: Config interfaces
3
+ * ExtractConfig, FilterConfig, PublishConfig, OverrideConfig, InitConfig
4
+ */
5
+ import { ApimServiceContext } from './types.js';
6
+ import { LogLevel } from '../lib/logger.js';
7
+ export interface ExtractConfig {
8
+ service: ApimServiceContext;
9
+ outputDir: string;
10
+ filter?: FilterConfig;
11
+ includeTransitive: boolean;
12
+ specFormat?: string;
13
+ logLevel: LogLevel;
14
+ otelConfigPath?: string;
15
+ }
16
+ export interface FilterConfig {
17
+ apiNames?: string[];
18
+ backendNames?: string[];
19
+ productNames?: string[];
20
+ namedValueNames?: string[];
21
+ loggerNames?: string[];
22
+ diagnosticNames?: string[];
23
+ tagNames?: string[];
24
+ policyFragmentNames?: string[];
25
+ gatewayNames?: string[];
26
+ versionSetNames?: string[];
27
+ groupNames?: string[];
28
+ subscriptionNames?: string[];
29
+ schemaNames?: string[];
30
+ policyRestrictionNames?: string[];
31
+ documentationNames?: string[];
32
+ workspaceNames?: string[];
33
+ }
34
+ export interface PublishConfig {
35
+ service: ApimServiceContext;
36
+ sourceDir: string;
37
+ overrides?: OverrideConfig;
38
+ dryRun: boolean;
39
+ deleteUnmatched: boolean;
40
+ commitId?: string;
41
+ logLevel: LogLevel;
42
+ otelConfigPath?: string;
43
+ }
44
+ export interface OverrideConfig {
45
+ namedValues?: Record<string, NamedValueOverride>;
46
+ backends?: Record<string, BackendOverride>;
47
+ apis?: Record<string, ApiOverride>;
48
+ diagnostics?: Record<string, DiagnosticOverride>;
49
+ loggers?: Record<string, LoggerOverride>;
50
+ }
51
+ export interface NamedValueOverride {
52
+ value?: string;
53
+ displayName?: string;
54
+ tags?: string[];
55
+ keyVault?: {
56
+ identityClientId?: string;
57
+ secretIdentifier?: string;
58
+ };
59
+ }
60
+ export interface BackendOverride {
61
+ url?: string;
62
+ credentials?: Record<string, unknown>;
63
+ }
64
+ export interface ApiOverride {
65
+ serviceUrl?: string;
66
+ }
67
+ export interface DiagnosticOverride {
68
+ loggerId?: string;
69
+ }
70
+ export interface LoggerOverride {
71
+ credentials?: Record<string, unknown>;
72
+ resourceId?: string;
73
+ }
74
+ export interface InitConfig {
75
+ ciProvider?: 'github-actions' | 'azure-devops';
76
+ nonInteractive: boolean;
77
+ artifactDir: string;
78
+ environments: string[];
79
+ outputDir: string;
80
+ cliPackage: string;
81
+ force: boolean;
82
+ }
83
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/models/config.ts"],"names":[],"mappings":"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"}