@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,168 @@
1
+ /**
2
+ * T021: Resource type extractor
3
+ * Generic extract logic: list resources via IApimClient, write each to IArtifactStore.
4
+ * Handles all 33 types using ResourceType metadata. Preserves opaque JSON per FR-009.
5
+ */
6
+ import { RESOURCE_TYPE_METADATA } from '../models/resource-types.js';
7
+ import { redactSecrets } from './secret-redactor.js';
8
+ import { shouldIncludeResource } from './filter-service.js';
9
+ import { logger } from '../lib/logger.js';
10
+ import { buildResourceLabel } from '../lib/resource-uri.js';
11
+ /**
12
+ * Check if a resource type's LIST endpoint returns shallow data that omits
13
+ * fields required for round-trip publish. When true, extraction must issue
14
+ * an individual GET per item to fetch the complete resource.
15
+ */
16
+ function typeNeedsFullFetch(type) {
17
+ return RESOURCE_TYPE_METADATA[type].listOmitsFields === true;
18
+ }
19
+ /**
20
+ * Extract the ARM resource name from a raw JSON object.
21
+ * ARM resources have a 'name' field at the top level.
22
+ */
23
+ export function extractResourceName(json) {
24
+ const name = json.name;
25
+ if (!name) {
26
+ throw new Error('Resource JSON missing required "name" field');
27
+ }
28
+ return name;
29
+ }
30
+ /**
31
+ * Extract all resources of a given type from APIM and write to artifact store.
32
+ *
33
+ * @param client - APIM REST client
34
+ * @param store - Artifact file store
35
+ * @param context - APIM service context
36
+ * @param type - Resource type to extract
37
+ * @param outputDir - Output directory
38
+ * @param filter - Optional filter config
39
+ * @param parent - Parent descriptor for child resources
40
+ * @param workspace - Optional workspace name
41
+ * @returns Extraction result
42
+ */
43
+ export async function extractResourceType(client, store, context, type, outputDir, filter, parent, workspace) {
44
+ const result = {
45
+ type,
46
+ extracted: [],
47
+ totalCount: 0,
48
+ errorCount: 0,
49
+ };
50
+ try {
51
+ const resources = client.listResources(context, type, parent);
52
+ for await (const listJson of resources) {
53
+ result.totalCount++;
54
+ let descriptor;
55
+ try {
56
+ const name = extractResourceName(listJson);
57
+ descriptor = buildDescriptor(type, name, parent, workspace);
58
+ // Apply filter
59
+ if (!shouldIncludeResource(descriptor, filter)) {
60
+ logger.debug(`Filtered out ${buildResourceLabel(descriptor)}`);
61
+ continue;
62
+ }
63
+ // Some APIM list endpoints return a shallow response that omits the
64
+ // heavyweight payload we need for round-trip publish. ApiSchema list
65
+ // omits `properties.document` (the GraphQL SDL / XSD / JSON-schema
66
+ // body); an individual GET returns it. Fetch the full resource so the
67
+ // extract captures what publish requires. Falls back to the list
68
+ // payload if the GET returns undefined (shouldn't normally happen —
69
+ // we just listed it).
70
+ let json = listJson;
71
+ if (typeNeedsFullFetch(type)) {
72
+ const full = await client.getResource(context, descriptor);
73
+ if (full) {
74
+ json = full;
75
+ }
76
+ }
77
+ // Apply secret redaction
78
+ const safeJson = redactSecrets(descriptor, json);
79
+ // Write to artifact store (preserves opaque JSON per FR-009)
80
+ await store.writeResource(outputDir, descriptor, safeJson);
81
+ result.extracted.push({
82
+ descriptor,
83
+ json: safeJson,
84
+ status: 'success',
85
+ });
86
+ logger.info(`Extracted ${buildResourceLabel(descriptor)}`);
87
+ }
88
+ catch (error) {
89
+ result.errorCount++;
90
+ const errorMessage = error instanceof Error ? error.message : String(error);
91
+ if (descriptor) {
92
+ logger.error(`Failed to extract ${buildResourceLabel(descriptor)}: ${errorMessage}`);
93
+ result.extracted.push({
94
+ descriptor,
95
+ json: {},
96
+ status: 'error',
97
+ error: errorMessage,
98
+ });
99
+ }
100
+ else {
101
+ logger.error(`Failed to extract ${type} resource: ${errorMessage}`);
102
+ }
103
+ }
104
+ }
105
+ }
106
+ catch (error) {
107
+ const errorMessage = error instanceof Error ? error.message : String(error);
108
+ logger.error(`Failed to list ${type}: ${errorMessage}`);
109
+ result.errorCount++;
110
+ }
111
+ return result;
112
+ }
113
+ /**
114
+ * Extract a single resource by descriptor and write to artifact store.
115
+ */
116
+ export async function extractSingleResource(client, store, context, descriptor, outputDir) {
117
+ try {
118
+ const json = await client.getResource(context, descriptor);
119
+ if (!json) {
120
+ return {
121
+ descriptor,
122
+ json: {},
123
+ status: 'error',
124
+ error: `Resource not found: ${buildResourceLabel(descriptor)}`,
125
+ };
126
+ }
127
+ // Apply secret redaction
128
+ const safeJson = redactSecrets(descriptor, json);
129
+ // Write to artifact store
130
+ await store.writeResource(outputDir, descriptor, safeJson);
131
+ logger.info(`Extracted ${buildResourceLabel(descriptor)}`);
132
+ return { descriptor, json: safeJson, status: 'success' };
133
+ }
134
+ catch (error) {
135
+ const errorMessage = error instanceof Error ? error.message : String(error);
136
+ logger.error(`Failed to extract ${buildResourceLabel(descriptor)}: ${errorMessage}`);
137
+ return { descriptor, json: {}, status: 'error', error: errorMessage };
138
+ }
139
+ }
140
+ /**
141
+ * Build a ResourceDescriptor for a given type, name, optional parent, and workspace.
142
+ *
143
+ * The nameParts array is derived generically using the count of positional
144
+ * placeholders in armPathSuffix:
145
+ * - placeholderCount === parent.nameParts.length → singleton child (policy, wiki):
146
+ * nameParts = parent.nameParts (own fixed name not encoded in path)
147
+ * - placeholderCount > parent.nameParts.length → named child:
148
+ * nameParts = [...parent.nameParts, name]
149
+ * - no parent → top-level: nameParts = [name] (or [] for zero-placeholder types)
150
+ */
151
+ function buildDescriptor(type, name, parent, workspace) {
152
+ const metadata = RESOURCE_TYPE_METADATA[type];
153
+ const placeholderCount = (metadata.armPathSuffix.match(/\{\d+\}/g) ?? []).length;
154
+ let nameParts;
155
+ if (!parent) {
156
+ nameParts = placeholderCount === 0 ? [] : [name];
157
+ }
158
+ else if (parent.nameParts.length >= placeholderCount) {
159
+ // Singleton child (policy, wiki): identified solely by parent's name-parts
160
+ nameParts = [...parent.nameParts];
161
+ }
162
+ else {
163
+ // Named child: has its own distinct position in the ARM path
164
+ nameParts = [...parent.nameParts, name];
165
+ }
166
+ return { type, nameParts, workspace };
167
+ }
168
+ //# sourceMappingURL=resource-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-extractor.js","sourceRoot":"","sources":["../../src/services/resource-extractor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAgB,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAkB;IAC5C,OAAO,sBAAsB,CAAC,IAAI,CAAC,CAAC,eAAe,KAAK,IAAI,CAAC;AAC/D,CAAC;AAsBD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,IAA0B,CAAC;IAC7C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAmB,EACnB,KAAqB,EACrB,OAA2B,EAC3B,IAAkB,EAClB,SAAiB,EACjB,MAAqB,EACrB,MAA2B,EAC3B,SAAkB;IAElB,MAAM,MAAM,GAAyB;QACnC,IAAI;QACJ,SAAS,EAAE,EAAE;QACb,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,CAAC;KACd,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAE9D,IAAI,KAAK,EAAE,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACvC,MAAM,CAAC,UAAU,EAAE,CAAC;YAEpB,IAAI,UAA0C,CAAC;YAC/C,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;gBAC3C,UAAU,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;gBAE5D,eAAe;gBACf,IAAI,CAAC,qBAAqB,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;oBAC/C,MAAM,CAAC,KAAK,CAAC,gBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;oBAC/D,SAAS;gBACX,CAAC;gBAED,oEAAoE;gBACpE,qEAAqE;gBACrE,mEAAmE;gBACnE,sEAAsE;gBACtE,iEAAiE;gBACjE,oEAAoE;gBACpE,sBAAsB;gBACtB,IAAI,IAAI,GAA4B,QAAQ,CAAC;gBAC7C,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;oBAC3D,IAAI,IAAI,EAAE,CAAC;wBACT,IAAI,GAAG,IAAI,CAAC;oBACd,CAAC;gBACH,CAAC;gBAED,yBAAyB;gBACzB,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;gBAEjD,6DAA6D;gBAC7D,MAAM,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;gBAE3D,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;oBACpB,UAAU;oBACV,IAAI,EAAE,QAAQ;oBACd,MAAM,EAAE,SAAS;iBAClB,CAAC,CAAC;gBAEH,MAAM,CAAC,IAAI,CAAC,aAAa,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC7D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,UAAU,EAAE,CAAC;gBACpB,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5E,IAAI,UAAU,EAAE,CAAC;oBACf,MAAM,CAAC,KAAK,CAAC,qBAAqB,kBAAkB,CAAC,UAAU,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC;oBACrF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;wBACpB,UAAU;wBACV,IAAI,EAAE,EAAE;wBACR,MAAM,EAAE,OAAO;wBACf,KAAK,EAAE,YAAY;qBACpB,CAAC,CAAC;gBACL,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,KAAK,CAAC,qBAAqB,IAAI,cAAc,YAAY,EAAE,CAAC,CAAC;gBACtE,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,kBAAkB,IAAI,KAAK,YAAY,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,UAAU,EAAE,CAAC;IACtB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAmB,EACnB,KAAqB,EACrB,OAA2B,EAC3B,UAA8B,EAC9B,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAE3D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;gBACL,UAAU;gBACV,IAAI,EAAE,EAAE;gBACR,MAAM,EAAE,OAAO;gBACf,KAAK,EAAE,uBAAuB,kBAAkB,CAAC,UAAU,CAAC,EAAE;aAC/D,CAAC;QACJ,CAAC;QAED,yBAAyB;QACzB,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAEjD,0BAA0B;QAC1B,MAAM,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;QAE3D,MAAM,CAAC,IAAI,CAAC,aAAa,kBAAkB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAE3D,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC3D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,MAAM,CAAC,KAAK,CAAC,qBAAqB,kBAAkB,CAAC,UAAU,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC;QACrF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IACxE,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,eAAe,CACtB,IAAkB,EAClB,IAAY,EACZ,MAA2B,EAC3B,SAAkB;IAElB,MAAM,QAAQ,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,gBAAgB,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAEjF,IAAI,SAAmB,CAAC;IACxB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,SAAS,GAAG,gBAAgB,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC;SAAM,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;QACvD,2EAA2E;QAC3E,SAAS,GAAG,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;SAAM,CAAC;QACN,6DAA6D;QAC7D,SAAS,GAAG,CAAC,GAAG,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * T031: Generic resource publisher
3
+ * Read resource from IArtifactStore, apply overrides, PUT via IApimClient.
4
+ * Handles all 33 resource types using ResourceType metadata.
5
+ * MUST preserve opaque JSON per FR-009.
6
+ */
7
+ import type { IApimClient } from '../clients/iapim-client.js';
8
+ import type { IArtifactStore } from '../clients/iartifact-store.js';
9
+ import type { ApimServiceContext, ResourceDescriptor } from '../models/types.js';
10
+ import type { PublishConfig } from '../models/config.js';
11
+ export interface ResourcePublishResult {
12
+ descriptor: ResourceDescriptor;
13
+ status: 'success' | 'failed' | 'skipped';
14
+ action: 'put' | 'delete' | 'noop';
15
+ error?: Error;
16
+ }
17
+ /**
18
+ * Publish a single resource: read from store, apply overrides, PUT to APIM.
19
+ * Preserves opaque JSON (FR-009). Uses applyOverrides for env-specific values.
20
+ * Returns 'skipped' if resource file doesn't exist in store.
21
+ */
22
+ export declare function publishResource(client: IApimClient, store: IArtifactStore, context: ApimServiceContext, descriptor: ResourceDescriptor, config: PublishConfig): Promise<ResourcePublishResult>;
23
+ //# sourceMappingURL=resource-publisher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-publisher.d.ts","sourceRoot":"","sources":["../../src/services/resource-publisher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAMzD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAyCD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,kBAAkB,EAC3B,UAAU,EAAE,kBAAkB,EAC9B,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,qBAAqB,CAAC,CAoIhC"}
@@ -0,0 +1,349 @@
1
+ /**
2
+ * T031: Generic resource publisher
3
+ * Read resource from IArtifactStore, apply overrides, PUT via IApimClient.
4
+ * Handles all 33 resource types using ResourceType metadata.
5
+ * MUST preserve opaque JSON per FR-009.
6
+ */
7
+ import { ResourceType } from '../models/resource-types.js';
8
+ import { applyOverrides } from './override-merger.js';
9
+ import { checkKeyVaultSecretAccess } from './keyvault-checker.js';
10
+ import { getNamePart } from '../lib/resource-path.js';
11
+ /**
12
+ * Policy resource types that have external XML content
13
+ */
14
+ const POLICY_TYPES = new Set([
15
+ ResourceType.ServicePolicy,
16
+ ResourceType.ProductPolicy,
17
+ ResourceType.ApiPolicy,
18
+ ResourceType.ApiOperationPolicy,
19
+ ResourceType.GraphQLResolverPolicy,
20
+ ]);
21
+ /**
22
+ * Association resource types that read from association files
23
+ */
24
+ const ASSOCIATION_TYPES = new Map([
25
+ [ResourceType.ProductApi, 'apis'],
26
+ [ResourceType.ProductGroup, 'groups'],
27
+ [ResourceType.GatewayApi, 'apis'],
28
+ ]);
29
+ /**
30
+ * Maps association resource types to their parent resource types.
31
+ * readAssociation / buildAssociationFilePath expect a Product or Gateway
32
+ * descriptor, not the association child type itself.
33
+ */
34
+ const ASSOCIATION_PARENT_TYPES = new Map([
35
+ [ResourceType.ProductApi, ResourceType.Product],
36
+ [ResourceType.ProductGroup, ResourceType.Product],
37
+ [ResourceType.GatewayApi, ResourceType.Gateway],
38
+ ]);
39
+ /**
40
+ * Wiki resource types that read markdown content
41
+ */
42
+ const WIKI_TYPES = new Set([
43
+ ResourceType.ApiWiki,
44
+ ResourceType.ProductWiki,
45
+ ]);
46
+ /**
47
+ * Publish a single resource: read from store, apply overrides, PUT to APIM.
48
+ * Preserves opaque JSON (FR-009). Uses applyOverrides for env-specific values.
49
+ * Returns 'skipped' if resource file doesn't exist in store.
50
+ */
51
+ export async function publishResource(client, store, context, descriptor, config) {
52
+ try {
53
+ // Handle association types (ProductApi, ProductGroup, GatewayApi)
54
+ const associationType = ASSOCIATION_TYPES.get(descriptor.type);
55
+ if (associationType) {
56
+ return await publishAssociation(client, store, context, descriptor, config, associationType);
57
+ }
58
+ // Handle wiki types
59
+ if (WIKI_TYPES.has(descriptor.type)) {
60
+ return await publishWiki(client, store, context, descriptor, config);
61
+ }
62
+ // Handle policy types — artifact is policy.xml (raw XML), not a JSON info file
63
+ if (POLICY_TYPES.has(descriptor.type)) {
64
+ return await publishPolicy(client, store, context, descriptor, config);
65
+ }
66
+ // Read resource JSON from store
67
+ let json = await store.readResource(config.sourceDir, descriptor);
68
+ if (!json) {
69
+ return {
70
+ descriptor,
71
+ status: 'skipped',
72
+ action: 'noop',
73
+ };
74
+ }
75
+ // Apply overrides (deep merge, preserves opaque structure)
76
+ json = applyOverrides(descriptor, json, config.overrides);
77
+ // For KeyVault-backed NamedValues:
78
+ // 1. Strip properties.value — APIM must not receive both keyVault and value
79
+ // in the same PUT body, as it causes indefinite provisioning or rejection.
80
+ // 2. Pre-flight access check — verify the managed identity has GET access
81
+ // to the secret before attempting the PUT. Surfaces permission errors
82
+ // early and fails fast instead of polling until timeout.
83
+ if (descriptor.type === ResourceType.NamedValue) {
84
+ const props = json.properties;
85
+ const kvBlock = props?.keyVault;
86
+ if (kvBlock != null) {
87
+ const { value: _omit, ...propsWithoutValue } = props;
88
+ json = { ...json, properties: propsWithoutValue };
89
+ const secretIdentifier = kvBlock.secretIdentifier;
90
+ const identityClientId = kvBlock.identityClientId;
91
+ if (secretIdentifier) {
92
+ await checkKeyVaultSecretAccess(secretIdentifier, identityClientId, {
93
+ subscriptionId: context.subscriptionId,
94
+ resourceGroup: context.resourceGroup,
95
+ serviceName: context.serviceName,
96
+ });
97
+ }
98
+ }
99
+ }
100
+ // Normalize Subscription scope.
101
+ // APIM stores scope as a full ARM resource path (e.g.
102
+ // "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ApiManagement/service/{svc}/apis")
103
+ // but the PUT endpoint requires a relative APIM path (e.g. "/apis" or
104
+ // "/apis/{apiId}" or "/products/{productId}"). Strip the service base path
105
+ // prefix when it is present so that the publish round-trip works correctly.
106
+ // HOWEVER: Skip publishing subscriptions with root-level scope (e.g. the built-in
107
+ // "master" subscription), as APIM treats these as read-only system resources
108
+ // and returns ValidationError when attempting to update them.
109
+ if (descriptor.type === ResourceType.Subscription) {
110
+ const props = json.properties;
111
+ const scope = props?.scope;
112
+ // Built-in master subscription has scope ending with the service path (no /apis or /products suffix)
113
+ // Skip it since APIM doesn't allow updates to built-in subscriptions
114
+ if (scope && (scope.endsWith('/') || (!scope.includes('/apis') && !scope.includes('/products')))) {
115
+ return {
116
+ descriptor,
117
+ status: 'skipped',
118
+ action: 'noop',
119
+ };
120
+ }
121
+ json = normalizeSubscriptionScope(json, context);
122
+ }
123
+ // ApiRelease: normalize properties.apiId from source ARM path to target ARM path.
124
+ // APIM stores apiId as the full source ARM resource path, e.g.:
125
+ // /subscriptions/{src-sub}/resourceGroups/{src-rg}/providers/
126
+ // Microsoft.ApiManagement/service/{src-svc}/apis/my-api;rev=2
127
+ // The PUT to the target service must reference the target service path,
128
+ // otherwise APIM rejects the request with a validation error.
129
+ if (descriptor.type === ResourceType.ApiRelease) {
130
+ json = normalizeApiReleaseApiId(json, context);
131
+ }
132
+ // API Revisions (e.g., "my-api;rev=2") need sourceApiId so APIM knows which
133
+ // base API to copy structure from. Also strip null properties that cause
134
+ // validation errors in APIM's revision creation.
135
+ if (descriptor.type === ResourceType.Api) {
136
+ const apiName = getNamePart(descriptor.nameParts, 0);
137
+ if (apiName.includes(';rev=')) {
138
+ const baseApiName = apiName.split(';rev=')[0];
139
+ const props = json.properties;
140
+ const cleanProps = {};
141
+ for (const [key, val] of Object.entries(props ?? {})) {
142
+ if (val !== null)
143
+ cleanProps[key] = val;
144
+ }
145
+ cleanProps.sourceApiId = `/subscriptions/${context.subscriptionId}/resourceGroups/${context.resourceGroup}/providers/Microsoft.ApiManagement/service/${context.serviceName}/apis/${baseApiName}`;
146
+ json = { ...json, properties: cleanProps };
147
+ }
148
+ }
149
+ // PUT to APIM
150
+ await client.putResource(context, descriptor, json);
151
+ return {
152
+ descriptor,
153
+ status: 'success',
154
+ action: 'put',
155
+ };
156
+ }
157
+ catch (error) {
158
+ return {
159
+ descriptor,
160
+ status: 'failed',
161
+ action: 'noop',
162
+ error: error instanceof Error ? error : new Error(String(error)),
163
+ };
164
+ }
165
+ }
166
+ /**
167
+ * Publish association resource (ProductApi, ProductGroup, GatewayApi)
168
+ */
169
+ async function publishAssociation(client, store, context, descriptor, config, associationType) {
170
+ try {
171
+ // buildAssociationFilePath (called inside readAssociation) only accepts
172
+ // Product or Gateway descriptors — not the association child types
173
+ // (ProductApi, ProductGroup, GatewayApi). Derive the parent descriptor.
174
+ const parentType = ASSOCIATION_PARENT_TYPES.get(descriptor.type);
175
+ const parentDescriptor = {
176
+ type: parentType,
177
+ nameParts: [getNamePart(descriptor.nameParts, 0)],
178
+ workspace: descriptor.workspace,
179
+ };
180
+ const names = await store.readAssociation(config.sourceDir, parentDescriptor, associationType);
181
+ // Create association for each name
182
+ for (const name of names) {
183
+ const assocDescriptor = {
184
+ type: descriptor.type,
185
+ nameParts: [getNamePart(descriptor.nameParts, 0), name],
186
+ };
187
+ // PUT empty body for association (APIM uses PUT to create association)
188
+ await client.putResource(context, assocDescriptor, {});
189
+ }
190
+ return {
191
+ descriptor,
192
+ status: 'success',
193
+ action: 'put',
194
+ };
195
+ }
196
+ catch (error) {
197
+ return {
198
+ descriptor,
199
+ status: 'failed',
200
+ action: 'noop',
201
+ error: error instanceof Error ? error : new Error(String(error)),
202
+ };
203
+ }
204
+ }
205
+ /**
206
+ * Publish wiki resource (ApiWiki, ProductWiki)
207
+ */
208
+ async function publishWiki(client, store, context, descriptor, config) {
209
+ try {
210
+ const wikiContent = await store.readContent(config.sourceDir, descriptor, 'specification');
211
+ if (!wikiContent) {
212
+ return {
213
+ descriptor,
214
+ status: 'skipped',
215
+ action: 'noop',
216
+ };
217
+ }
218
+ // Build wiki payload
219
+ const payload = {
220
+ properties: {
221
+ documents: [
222
+ {
223
+ documentId: 'default',
224
+ content: wikiContent.content,
225
+ },
226
+ ],
227
+ },
228
+ };
229
+ await client.putResource(context, descriptor, payload);
230
+ return {
231
+ descriptor,
232
+ status: 'success',
233
+ action: 'put',
234
+ };
235
+ }
236
+ catch (error) {
237
+ return {
238
+ descriptor,
239
+ status: 'failed',
240
+ action: 'noop',
241
+ error: error instanceof Error ? error : new Error(String(error)),
242
+ };
243
+ }
244
+ }
245
+ /**
246
+ * Publish policy resource (ServicePolicy, ApiPolicy, ProductPolicy, ApiOperationPolicy,
247
+ * GraphQLResolverPolicy). The artifact on disk is a raw policy.xml file; there is no
248
+ * separate JSON info file for these types. Reads the XML and PUTs it with format=rawxml.
249
+ */
250
+ async function publishPolicy(client, store, context, descriptor, config) {
251
+ try {
252
+ const policyContent = await store.readContent(config.sourceDir, descriptor, 'policy');
253
+ if (!policyContent) {
254
+ return {
255
+ descriptor,
256
+ status: 'skipped',
257
+ action: 'noop',
258
+ };
259
+ }
260
+ const payload = {
261
+ properties: {
262
+ value: policyContent.content,
263
+ format: 'rawxml',
264
+ },
265
+ };
266
+ await client.putResource(context, descriptor, payload);
267
+ return {
268
+ descriptor,
269
+ status: 'success',
270
+ action: 'put',
271
+ };
272
+ }
273
+ catch (error) {
274
+ return {
275
+ descriptor,
276
+ status: 'failed',
277
+ action: 'noop',
278
+ error: error instanceof Error ? error : new Error(String(error)),
279
+ };
280
+ }
281
+ }
282
+ /**
283
+ * Normalise the `properties.scope` field of a Subscription resource.
284
+ *
285
+ * APIM returns scope as a full ARM resource path on GET, e.g.:
286
+ * /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ApiManagement/service/{svc}/apis
287
+ *
288
+ * The PUT endpoint requires a relative APIM path, e.g.:
289
+ * /apis
290
+ * /apis/{apiId}
291
+ * /products/{productId}
292
+ *
293
+ * Strip the service base ARM path prefix when it is present so that the
294
+ * extract → publish round-trip works without manual edits.
295
+ */
296
+ function normalizeSubscriptionScope(json, context) {
297
+ const props = json.properties;
298
+ if (!props)
299
+ return json;
300
+ const scope = props.scope;
301
+ if (!scope)
302
+ return json;
303
+ // The ARM path prefix lives between "management.azure.com" and the first
304
+ // APIM-relative segment. Derive it from baseUrl by stripping the protocol+host.
305
+ const armPathPrefix = context.baseUrl.replace(/^https?:\/\/[^/]+/, '');
306
+ if (scope.startsWith(armPathPrefix)) {
307
+ const relativeScope = scope.slice(armPathPrefix.length) || '/';
308
+ return {
309
+ ...json,
310
+ properties: { ...props, scope: relativeScope },
311
+ };
312
+ }
313
+ return json;
314
+ }
315
+ /**
316
+ * Normalise the `properties.apiId` field of an ApiRelease resource.
317
+ *
318
+ * APIM returns apiId as a full ARM resource path that includes the source
319
+ * service coordinates, e.g.:
320
+ * /subscriptions/{src-sub}/resourceGroups/{src-rg}/providers/
321
+ * Microsoft.ApiManagement/service/{src-svc}/apis/my-api;rev=2
322
+ *
323
+ * The PUT to the target service must reference the TARGET service path;
324
+ * otherwise APIM rejects the request because the apiId does not match the
325
+ * service being written to. Rebuild the apiId using the target ARM prefix
326
+ * derived from the publish context.
327
+ */
328
+ function normalizeApiReleaseApiId(json, context) {
329
+ const props = json.properties;
330
+ if (!props)
331
+ return json;
332
+ const apiId = props.apiId;
333
+ if (!apiId)
334
+ return json;
335
+ // The target ARM prefix for this service (no https:// host, no trailing slash)
336
+ const targetArmPrefix = context.baseUrl.replace(/^https?:\/\/[^/]+/, '');
337
+ // Extract the /apis/... suffix and rebuild with the target ARM prefix.
338
+ // The suffix includes the full API name, e.g. "/apis/my-api;rev=2".
339
+ const apisIndex = apiId.indexOf('/apis/');
340
+ if (apisIndex !== -1) {
341
+ const apisSuffix = apiId.slice(apisIndex);
342
+ return {
343
+ ...json,
344
+ properties: { ...props, apiId: targetArmPrefix + apisSuffix },
345
+ };
346
+ }
347
+ return json;
348
+ }
349
+ //# sourceMappingURL=resource-publisher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-publisher.js","sourceRoot":"","sources":["../../src/services/resource-publisher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * T026: Secret redaction service
3
+ * Detect properties.secret === true on named values,
4
+ * replace properties.value with redaction marker.
5
+ */
6
+ import { ResourceDescriptor } from '../models/types.js';
7
+ /** Marker used to replace secret values in extracted artifacts */
8
+ export declare const REDACTION_MARKER = "*** REDACTED ***";
9
+ /**
10
+ * Redact secret values from a resource's JSON payload.
11
+ *
12
+ * Only applies to NamedValue resources with properties.secret === true.
13
+ * KeyVault references are preserved as-is (the URL is not itself secret).
14
+ *
15
+ * @param descriptor - Resource descriptor
16
+ * @param json - Raw resource JSON (will be cloned, not mutated)
17
+ * @returns JSON with secrets redacted
18
+ */
19
+ export declare function redactSecrets(descriptor: ResourceDescriptor, json: Record<string, unknown>): Record<string, unknown>;
20
+ //# sourceMappingURL=secret-redactor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-redactor.d.ts","sourceRoot":"","sources":["../../src/services/secret-redactor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAIxD,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAEnD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,kBAAkB,EAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * T026: Secret redaction service
3
+ * Detect properties.secret === true on named values,
4
+ * replace properties.value with redaction marker.
5
+ */
6
+ import { ResourceType } from '../models/resource-types.js';
7
+ import { logger } from '../lib/logger.js';
8
+ import { getNamePart } from '../lib/resource-path.js';
9
+ /** Marker used to replace secret values in extracted artifacts */
10
+ export const REDACTION_MARKER = '*** REDACTED ***';
11
+ /**
12
+ * Redact secret values from a resource's JSON payload.
13
+ *
14
+ * Only applies to NamedValue resources with properties.secret === true.
15
+ * KeyVault references are preserved as-is (the URL is not itself secret).
16
+ *
17
+ * @param descriptor - Resource descriptor
18
+ * @param json - Raw resource JSON (will be cloned, not mutated)
19
+ * @returns JSON with secrets redacted
20
+ */
21
+ export function redactSecrets(descriptor, json) {
22
+ if (descriptor.type !== ResourceType.NamedValue) {
23
+ return json;
24
+ }
25
+ const properties = json.properties;
26
+ if (!properties) {
27
+ return json;
28
+ }
29
+ // Only redact if explicitly marked as secret
30
+ if (properties.secret !== true) {
31
+ return json;
32
+ }
33
+ // Skip KeyVault-backed named values — the keyVault block is not secret
34
+ if (properties.keyVault !== undefined && properties.keyVault !== null) {
35
+ logger.debug(`Named value "${getNamePart(descriptor.nameParts, 0)}" is KeyVault-backed, preserving reference`);
36
+ return json;
37
+ }
38
+ // Deep clone to avoid mutating the original
39
+ const redacted = JSON.parse(JSON.stringify(json));
40
+ const redactedProps = redacted.properties;
41
+ redactedProps.value = REDACTION_MARKER;
42
+ logger.debug(`Redacted secret value for named value "${getNamePart(descriptor.nameParts, 0)}"`);
43
+ return redacted;
44
+ }
45
+ //# sourceMappingURL=secret-redactor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-redactor.js","sourceRoot":"","sources":["../../src/services/secret-redactor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,kEAAkE;AAClE,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,UAA8B,EAC9B,IAA6B;IAE7B,IAAI,UAAU,CAAC,IAAI,KAAK,YAAY,CAAC,UAAU,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,UAAiD,CAAC;IAC1E,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6CAA6C;IAC7C,IAAI,UAAU,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,uEAAuE;IACvE,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,IAAI,UAAU,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtE,MAAM,CAAC,KAAK,CAAC,gBAAgB,WAAW,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,4CAA4C,CAAC,CAAC;QAC/G,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4CAA4C;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAA4B,CAAC;IAC7E,MAAM,aAAa,GAAG,QAAQ,CAAC,UAAqC,CAAC;IACrE,aAAa,CAAC,KAAK,GAAG,gBAAgB,CAAC;IAEvC,MAAM,CAAC,KAAK,CAAC,0CAA0C,WAAW,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;IAChG,OAAO,QAAQ,CAAC;AAClB,CAAC"}