@pax8-cta/core 0.1.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 (226) hide show
  1. package/LICENSE +198 -0
  2. package/dist/auth/device-code-login.d.ts +40 -0
  3. package/dist/auth/device-code-login.d.ts.map +1 -0
  4. package/dist/auth/device-code-login.js +59 -0
  5. package/dist/auth/device-code-login.js.map +1 -0
  6. package/dist/auth/gdap-client.d.ts +81 -0
  7. package/dist/auth/gdap-client.d.ts.map +1 -0
  8. package/dist/auth/gdap-client.js +128 -0
  9. package/dist/auth/gdap-client.js.map +1 -0
  10. package/dist/auth/index.d.ts +19 -0
  11. package/dist/auth/index.d.ts.map +1 -0
  12. package/dist/auth/index.js +19 -0
  13. package/dist/auth/index.js.map +1 -0
  14. package/dist/auth/token-manager.d.ts +54 -0
  15. package/dist/auth/token-manager.d.ts.map +1 -0
  16. package/dist/auth/token-manager.js +150 -0
  17. package/dist/auth/token-manager.js.map +1 -0
  18. package/dist/client.d.ts +27 -0
  19. package/dist/client.d.ts.map +1 -0
  20. package/dist/client.js +27 -0
  21. package/dist/client.js.map +1 -0
  22. package/dist/config/client.d.ts +24 -0
  23. package/dist/config/client.d.ts.map +1 -0
  24. package/dist/config/client.js +18 -0
  25. package/dist/config/client.js.map +1 -0
  26. package/dist/config/index.d.ts +18 -0
  27. package/dist/config/index.d.ts.map +1 -0
  28. package/dist/config/index.js +18 -0
  29. package/dist/config/index.js.map +1 -0
  30. package/dist/config/loader.d.ts +81 -0
  31. package/dist/config/loader.d.ts.map +1 -0
  32. package/dist/config/loader.js +271 -0
  33. package/dist/config/loader.js.map +1 -0
  34. package/dist/config/schema.d.ts +751 -0
  35. package/dist/config/schema.d.ts.map +1 -0
  36. package/dist/config/schema.js +556 -0
  37. package/dist/config/schema.js.map +1 -0
  38. package/dist/constants.d.ts +116 -0
  39. package/dist/constants.d.ts.map +1 -0
  40. package/dist/constants.js +170 -0
  41. package/dist/constants.js.map +1 -0
  42. package/dist/dataverse/agent-resolver.d.ts +98 -0
  43. package/dist/dataverse/agent-resolver.d.ts.map +1 -0
  44. package/dist/dataverse/agent-resolver.js +185 -0
  45. package/dist/dataverse/agent-resolver.js.map +1 -0
  46. package/dist/dataverse/client.d.ts +104 -0
  47. package/dist/dataverse/client.d.ts.map +1 -0
  48. package/dist/dataverse/client.js +272 -0
  49. package/dist/dataverse/client.js.map +1 -0
  50. package/dist/dataverse/connection-refs.d.ts +115 -0
  51. package/dist/dataverse/connection-refs.d.ts.map +1 -0
  52. package/dist/dataverse/connection-refs.js +203 -0
  53. package/dist/dataverse/connection-refs.js.map +1 -0
  54. package/dist/dataverse/index.d.ts +20 -0
  55. package/dist/dataverse/index.d.ts.map +1 -0
  56. package/dist/dataverse/index.js +20 -0
  57. package/dist/dataverse/index.js.map +1 -0
  58. package/dist/dataverse/solution-ops.d.ts +100 -0
  59. package/dist/dataverse/solution-ops.d.ts.map +1 -0
  60. package/dist/dataverse/solution-ops.js +288 -0
  61. package/dist/dataverse/solution-ops.js.map +1 -0
  62. package/dist/errors.d.ts +171 -0
  63. package/dist/errors.d.ts.map +1 -0
  64. package/dist/errors.js +178 -0
  65. package/dist/errors.js.map +1 -0
  66. package/dist/index.d.ts +27 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +40 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/mock/demo-data.d.ts +213 -0
  71. package/dist/mock/demo-data.d.ts.map +1 -0
  72. package/dist/mock/demo-data.js +1096 -0
  73. package/dist/mock/demo-data.js.map +1 -0
  74. package/dist/mock/demo-deployment-store.d.ts +77 -0
  75. package/dist/mock/demo-deployment-store.d.ts.map +1 -0
  76. package/dist/mock/demo-deployment-store.js +85 -0
  77. package/dist/mock/demo-deployment-store.js.map +1 -0
  78. package/dist/powerplatform/admin-client.d.ts +226 -0
  79. package/dist/powerplatform/admin-client.d.ts.map +1 -0
  80. package/dist/powerplatform/admin-client.js +315 -0
  81. package/dist/powerplatform/admin-client.js.map +1 -0
  82. package/dist/powerplatform/index.d.ts +18 -0
  83. package/dist/powerplatform/index.d.ts.map +1 -0
  84. package/dist/powerplatform/index.js +18 -0
  85. package/dist/powerplatform/index.js.map +1 -0
  86. package/dist/powerplatform/tenant-discovery.d.ts +100 -0
  87. package/dist/powerplatform/tenant-discovery.d.ts.map +1 -0
  88. package/dist/powerplatform/tenant-discovery.js +205 -0
  89. package/dist/powerplatform/tenant-discovery.js.map +1 -0
  90. package/dist/preconditions/check.d.ts +41 -0
  91. package/dist/preconditions/check.d.ts.map +1 -0
  92. package/dist/preconditions/check.js +173 -0
  93. package/dist/preconditions/check.js.map +1 -0
  94. package/dist/preconditions/index.d.ts +20 -0
  95. package/dist/preconditions/index.d.ts.map +1 -0
  96. package/dist/preconditions/index.js +20 -0
  97. package/dist/preconditions/index.js.map +1 -0
  98. package/dist/preconditions/loader.d.ts +33 -0
  99. package/dist/preconditions/loader.d.ts.map +1 -0
  100. package/dist/preconditions/loader.js +65 -0
  101. package/dist/preconditions/loader.js.map +1 -0
  102. package/dist/preconditions/schema.d.ts +103 -0
  103. package/dist/preconditions/schema.d.ts.map +1 -0
  104. package/dist/preconditions/schema.js +93 -0
  105. package/dist/preconditions/schema.js.map +1 -0
  106. package/dist/preconditions/types.d.ts +118 -0
  107. package/dist/preconditions/types.d.ts.map +1 -0
  108. package/dist/preconditions/types.js +17 -0
  109. package/dist/preconditions/types.js.map +1 -0
  110. package/dist/queue/index.d.ts +17 -0
  111. package/dist/queue/index.d.ts.map +1 -0
  112. package/dist/queue/index.js +17 -0
  113. package/dist/queue/index.js.map +1 -0
  114. package/dist/queue/memory-queue.d.ts +86 -0
  115. package/dist/queue/memory-queue.d.ts.map +1 -0
  116. package/dist/queue/memory-queue.js +221 -0
  117. package/dist/queue/memory-queue.js.map +1 -0
  118. package/dist/services/audit-log.d.ts +59 -0
  119. package/dist/services/audit-log.d.ts.map +1 -0
  120. package/dist/services/audit-log.js +193 -0
  121. package/dist/services/audit-log.js.map +1 -0
  122. package/dist/services/auth-error-parser.d.ts +36 -0
  123. package/dist/services/auth-error-parser.d.ts.map +1 -0
  124. package/dist/services/auth-error-parser.js +90 -0
  125. package/dist/services/auth-error-parser.js.map +1 -0
  126. package/dist/services/deployment-doctor.d.ts +109 -0
  127. package/dist/services/deployment-doctor.d.ts.map +1 -0
  128. package/dist/services/deployment-doctor.js +476 -0
  129. package/dist/services/deployment-doctor.js.map +1 -0
  130. package/dist/services/deployment-notifications.d.ts +41 -0
  131. package/dist/services/deployment-notifications.d.ts.map +1 -0
  132. package/dist/services/deployment-notifications.js +161 -0
  133. package/dist/services/deployment-notifications.js.map +1 -0
  134. package/dist/services/deployment-progress.d.ts +89 -0
  135. package/dist/services/deployment-progress.d.ts.map +1 -0
  136. package/dist/services/deployment-progress.js +244 -0
  137. package/dist/services/deployment-progress.js.map +1 -0
  138. package/dist/services/deployment-service.d.ts +97 -0
  139. package/dist/services/deployment-service.d.ts.map +1 -0
  140. package/dist/services/deployment-service.js +375 -0
  141. package/dist/services/deployment-service.js.map +1 -0
  142. package/dist/services/drift-analyzer.d.ts +86 -0
  143. package/dist/services/drift-analyzer.d.ts.map +1 -0
  144. package/dist/services/drift-analyzer.js +273 -0
  145. package/dist/services/drift-analyzer.js.map +1 -0
  146. package/dist/services/environment-setup.d.ts +97 -0
  147. package/dist/services/environment-setup.d.ts.map +1 -0
  148. package/dist/services/environment-setup.js +250 -0
  149. package/dist/services/environment-setup.js.map +1 -0
  150. package/dist/services/health-check.d.ts +168 -0
  151. package/dist/services/health-check.d.ts.map +1 -0
  152. package/dist/services/health-check.js +705 -0
  153. package/dist/services/health-check.js.map +1 -0
  154. package/dist/services/index.d.ts +39 -0
  155. package/dist/services/index.d.ts.map +1 -0
  156. package/dist/services/index.js +39 -0
  157. package/dist/services/index.js.map +1 -0
  158. package/dist/services/logger.d.ts +139 -0
  159. package/dist/services/logger.d.ts.map +1 -0
  160. package/dist/services/logger.js +268 -0
  161. package/dist/services/logger.js.map +1 -0
  162. package/dist/services/notification-service.d.ts +55 -0
  163. package/dist/services/notification-service.d.ts.map +1 -0
  164. package/dist/services/notification-service.js +184 -0
  165. package/dist/services/notification-service.js.map +1 -0
  166. package/dist/services/risk-analyzer.d.ts +252 -0
  167. package/dist/services/risk-analyzer.d.ts.map +1 -0
  168. package/dist/services/risk-analyzer.js +866 -0
  169. package/dist/services/risk-analyzer.js.map +1 -0
  170. package/dist/services/rollback.d.ts +57 -0
  171. package/dist/services/rollback.d.ts.map +1 -0
  172. package/dist/services/rollback.js +270 -0
  173. package/dist/services/rollback.js.map +1 -0
  174. package/dist/services/scheduler.d.ts +80 -0
  175. package/dist/services/scheduler.d.ts.map +1 -0
  176. package/dist/services/scheduler.js +350 -0
  177. package/dist/services/scheduler.js.map +1 -0
  178. package/dist/services/secrets.d.ts +31 -0
  179. package/dist/services/secrets.d.ts.map +1 -0
  180. package/dist/services/secrets.js +206 -0
  181. package/dist/services/secrets.js.map +1 -0
  182. package/dist/services/settings-service.d.ts +132 -0
  183. package/dist/services/settings-service.d.ts.map +1 -0
  184. package/dist/services/settings-service.js +378 -0
  185. package/dist/services/settings-service.js.map +1 -0
  186. package/dist/services/solution-diff.d.ts +127 -0
  187. package/dist/services/solution-diff.d.ts.map +1 -0
  188. package/dist/services/solution-diff.js +260 -0
  189. package/dist/services/solution-diff.js.map +1 -0
  190. package/dist/services/solution-mode-detector.d.ts +35 -0
  191. package/dist/services/solution-mode-detector.d.ts.map +1 -0
  192. package/dist/services/solution-mode-detector.js +84 -0
  193. package/dist/services/solution-mode-detector.js.map +1 -0
  194. package/dist/services/tenant-resolver.d.ts +55 -0
  195. package/dist/services/tenant-resolver.d.ts.map +1 -0
  196. package/dist/services/tenant-resolver.js +126 -0
  197. package/dist/services/tenant-resolver.js.map +1 -0
  198. package/dist/services/unmanaged-customizations.d.ts +104 -0
  199. package/dist/services/unmanaged-customizations.d.ts.map +1 -0
  200. package/dist/services/unmanaged-customizations.js +521 -0
  201. package/dist/services/unmanaged-customizations.js.map +1 -0
  202. package/dist/services/url-templater.d.ts +184 -0
  203. package/dist/services/url-templater.d.ts.map +1 -0
  204. package/dist/services/url-templater.js +327 -0
  205. package/dist/services/url-templater.js.map +1 -0
  206. package/dist/services/version-checker.d.ts +108 -0
  207. package/dist/services/version-checker.d.ts.map +1 -0
  208. package/dist/services/version-checker.js +403 -0
  209. package/dist/services/version-checker.js.map +1 -0
  210. package/dist/services/waves.d.ts +90 -0
  211. package/dist/services/waves.d.ts.map +1 -0
  212. package/dist/services/waves.js +222 -0
  213. package/dist/services/waves.js.map +1 -0
  214. package/dist/services/webhook.d.ts +95 -0
  215. package/dist/services/webhook.d.ts.map +1 -0
  216. package/dist/services/webhook.js +244 -0
  217. package/dist/services/webhook.js.map +1 -0
  218. package/dist/utils/deployment-tools.d.ts +110 -0
  219. package/dist/utils/deployment-tools.d.ts.map +1 -0
  220. package/dist/utils/deployment-tools.js +121 -0
  221. package/dist/utils/deployment-tools.js.map +1 -0
  222. package/dist/utils/index.d.ts +17 -0
  223. package/dist/utils/index.d.ts.map +1 -0
  224. package/dist/utils/index.js +18 -0
  225. package/dist/utils/index.js.map +1 -0
  226. package/package.json +49 -0
@@ -0,0 +1,184 @@
1
+ export type UrlTemplateType = "sharepoint" | "dynamics_crm" | "onmicrosoft" | "custom";
2
+ export interface ZipFile {
3
+ dir: boolean;
4
+ async(type: "text"): Promise<string>;
5
+ }
6
+ /**
7
+ * Map of ZIP output type identifiers to their concrete TS types.
8
+ *
9
+ * Mirrors the shape of JSZip's `OutputByType` so that {@link ZipArchive.generateAsync}
10
+ * can return the correct concrete type based on the requested `type` option,
11
+ * instead of falling back to `any`.
12
+ */
13
+ export interface ZipOutputByType {
14
+ nodebuffer: Buffer;
15
+ arraybuffer: ArrayBuffer;
16
+ blob: Blob;
17
+ uint8array: Uint8Array;
18
+ base64: string;
19
+ string: string;
20
+ }
21
+ export type ZipOutputType = keyof ZipOutputByType;
22
+ /**
23
+ * Options for generating a ZIP archive.
24
+ *
25
+ * The `type` field is a generic so that callers like `zip.generateAsync({ type: "nodebuffer" })`
26
+ * yield a `Promise<Buffer>` rather than `Promise<any>`.
27
+ */
28
+ export interface ZipGenerateOptions<T extends ZipOutputType = ZipOutputType> {
29
+ type: T;
30
+ compression?: "STORE" | "DEFLATE";
31
+ compressionOptions?: {
32
+ level: number;
33
+ };
34
+ }
35
+ /**
36
+ * Generic ZIP archive interface (compatible with JSZip)
37
+ */
38
+ export interface ZipArchive {
39
+ files: Record<string, ZipFile>;
40
+ file(path: string, content: string): void;
41
+ generateAsync<T extends ZipOutputType>(options: ZipGenerateOptions<T>): Promise<ZipOutputByType[T]>;
42
+ }
43
+ /**
44
+ * Interface for loading ZIP files (compatible with JSZip)
45
+ */
46
+ export interface ZipLoader {
47
+ loadAsync(data: Buffer | ArrayBuffer): Promise<ZipArchive>;
48
+ }
49
+ export interface DetectedUrl {
50
+ type: UrlTemplateType;
51
+ originalUrl: string;
52
+ extractedTenant: string;
53
+ templatePattern: string;
54
+ fileLocation: string;
55
+ }
56
+ export interface UrlTemplate {
57
+ id: string;
58
+ type: UrlTemplateType;
59
+ originalUrl: string;
60
+ templatePattern: string;
61
+ extractedTenant: string;
62
+ fileLocations: string[];
63
+ description?: string;
64
+ confirmed: boolean;
65
+ }
66
+ export interface AgentUrlTemplates {
67
+ sourceTenant: string;
68
+ templates: UrlTemplate[];
69
+ createdAt: string;
70
+ confirmedAt?: string;
71
+ }
72
+ export interface TenantUrlValues {
73
+ tenant: string;
74
+ sharepoint: string;
75
+ dynamicsCrm: string;
76
+ onmicrosoft: string;
77
+ region?: string;
78
+ }
79
+ export declare class UrlTemplater {
80
+ /**
81
+ * Scan a solution ZIP for tenant-specific URLs
82
+ *
83
+ * @param zip - A ZipArchive instance (JSZip or compatible)
84
+ * @returns Array of detected tenant-specific URLs with their locations
85
+ */
86
+ scanSolution(zip: ZipArchive): Promise<DetectedUrl[]>;
87
+ /**
88
+ * Extract tenant-specific URLs from text content using pattern matching
89
+ *
90
+ * This function scans text content (typically from XML, JSON, or YAML files
91
+ * within a Power Platform solution) and identifies tenant-specific URLs that
92
+ * should be templated for multi-tenant deployment.
93
+ *
94
+ * **Supported URL patterns:**
95
+ * - SharePoint: `https://{tenant}.sharepoint.com/...`
96
+ * - Dynamics 365: `https://{tenant}.crm[N].dynamics.com/...`
97
+ * - Microsoft 365: `https://{tenant}.onmicrosoft.com/...`
98
+ *
99
+ * Each detected URL is converted to a template pattern where the tenant
100
+ * identifier is replaced with `{tenant}`, allowing the URL to be resolved
101
+ * to different values for each target tenant during deployment.
102
+ *
103
+ * @param content - The text content to scan for URLs
104
+ * @param fileLocation - Path of the file being scanned (for tracking purposes)
105
+ * @returns Array of detected URLs with their types, extracted tenants, and template patterns
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * const urls = templater.extractUrlsFromContent(
110
+ * '<SharePointUrl>https://contoso.sharepoint.com/sites/docs</SharePointUrl>',
111
+ * 'solution/connections.xml'
112
+ * );
113
+ * // Returns: [{
114
+ * // type: 'sharepoint',
115
+ * // originalUrl: 'https://contoso.sharepoint.com/sites/docs',
116
+ * // extractedTenant: 'contoso',
117
+ * // templatePattern: 'https://{tenant}.sharepoint.com/sites/docs',
118
+ * // fileLocation: 'solution/connections.xml'
119
+ * // }]
120
+ * ```
121
+ */
122
+ extractUrlsFromContent(content: string, fileLocation: string): DetectedUrl[];
123
+ /**
124
+ * Infer the source tenant from detected URLs (most common tenant identifier)
125
+ */
126
+ inferSourceTenant(urls: DetectedUrl[]): string | null;
127
+ /**
128
+ * Generate URL templates from detected URLs
129
+ */
130
+ generateTemplates(detectedUrls: DetectedUrl[]): UrlTemplate[];
131
+ /**
132
+ * Get a human-readable description for a URL type
133
+ */
134
+ private getDescriptionForType;
135
+ /**
136
+ * Resolve a template pattern to an actual URL for a target tenant
137
+ */
138
+ resolveTemplate(templatePattern: string, tenantUrls: TenantUrlValues): string;
139
+ /**
140
+ * Modify a Power Platform solution ZIP by replacing tenant-specific URLs
141
+ *
142
+ * This function takes a solution ZIP file and performs URL replacements
143
+ * in all text files (XML, JSON, YAML) within the archive. It's used during
144
+ * deployment to customize solutions for specific target tenants.
145
+ *
146
+ * **How it works:**
147
+ * 1. Loads the ZIP file into memory
148
+ * 2. Iterates through all files in the archive
149
+ * 3. For text files, performs string replacements for each URL mapping
150
+ * 4. Generates a new ZIP buffer with the modified content
151
+ *
152
+ * **Important notes:**
153
+ * - Binary files are skipped (images, compiled resources, etc.)
154
+ * - The original ZIP is not modified; a new buffer is returned
155
+ * - If no replacements are needed, the original buffer is returned unchanged
156
+ * - Requires a JSZip-compatible loader to be passed in
157
+ *
158
+ * @param zipBuffer - The original solution ZIP file as a Buffer
159
+ * @param replacements - Map of original URLs to resolved URLs
160
+ * @param zipLoader - JSZip-compatible loader for reading/writing ZIP files
161
+ * @returns New Buffer containing the modified solution ZIP
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * const JSZip = require('jszip');
166
+ * const replacements = new Map([
167
+ * ['https://contoso.sharepoint.com', 'https://fabrikam.sharepoint.com'],
168
+ * ['https://contoso.crm.dynamics.com', 'https://fabrikam.crm.dynamics.com'],
169
+ * ]);
170
+ * const modifiedZip = await templater.modifySolution(
171
+ * originalZipBuffer,
172
+ * replacements,
173
+ * new JSZip()
174
+ * );
175
+ * ```
176
+ */
177
+ modifySolution(zipBuffer: Buffer, replacements: Map<string, string>, zipLoader: ZipLoader): Promise<Buffer>;
178
+ /**
179
+ * Create a complete AgentUrlTemplates object from scanning results
180
+ */
181
+ createAgentUrlTemplates(detectedUrls: DetectedUrl[]): AgentUrlTemplates | null;
182
+ }
183
+ export declare const urlTemplater: UrlTemplater;
184
+ //# sourceMappingURL=url-templater.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-templater.d.ts","sourceRoot":"","sources":["../../src/services/url-templater.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Copyright 2024 Pax8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import crypto from "node:crypto";
17
+ /**
18
+ * URL Templating Service
19
+ *
20
+ * Detects tenant-specific URLs in Power Platform solutions and converts them to templates.
21
+ * At deploy time, templates are resolved to target tenant values.
22
+ */
23
+ import { coreLogger } from "./logger.js";
24
+ const logger = coreLogger;
25
+ const URL_PATTERNS = [
26
+ {
27
+ type: "sharepoint",
28
+ // Matches: https://tenant.sharepoint.com/sites/path or https://tenant.sharepoint.com
29
+ regex: /https?:\/\/([a-zA-Z0-9-]+)\.sharepoint\.com(\/[^\s"'<>]*)?/gi,
30
+ extractTenant: (match) => match[1],
31
+ generateTemplate: (url, _tenant) => url.replace(new RegExp(`https?://([a-zA-Z0-9-]+)\\.sharepoint\\.com`, "i"), "https://{tenant}.sharepoint.com"),
32
+ },
33
+ {
34
+ type: "dynamics_crm",
35
+ // Matches: https://tenant.crm.dynamics.com or https://tenant.crm4.dynamics.com
36
+ regex: /https?:\/\/([a-zA-Z0-9-]+)\.(crm[0-9]*)\.dynamics\.com(\/[^\s"'<>]*)?/gi,
37
+ extractTenant: (match) => match[1],
38
+ extractRegion: (match) => match[2],
39
+ generateTemplate: (url, _tenant, region) => url.replace(new RegExp(`https?://([a-zA-Z0-9-]+)\\.(crm[0-9]*)\\.dynamics\\.com`, "i"), `https://{tenant}.${region || "crm"}.dynamics.com`),
40
+ },
41
+ {
42
+ type: "onmicrosoft",
43
+ // Matches: https://tenant.onmicrosoft.com or tenant.onmicrosoft.com in email/UPN
44
+ regex: /https?:\/\/([a-zA-Z0-9-]+)\.onmicrosoft\.com(\/[^\s"'<>]*)?/gi,
45
+ extractTenant: (match) => match[1],
46
+ generateTemplate: (url, _tenant) => url.replace(new RegExp(`https?://([a-zA-Z0-9-]+)\\.onmicrosoft\\.com`, "i"), "https://{tenant}.onmicrosoft.com"),
47
+ },
48
+ ];
49
+ // File extensions to scan for URLs
50
+ const TEXT_FILE_EXTENSIONS = [".xml", ".json", ".yaml", ".yml"];
51
+ const TEXT_FILE_PATTERNS = [/\/data$/i, /\.data$/i];
52
+ export class UrlTemplater {
53
+ /**
54
+ * Scan a solution ZIP for tenant-specific URLs
55
+ *
56
+ * @param zip - A ZipArchive instance (JSZip or compatible)
57
+ * @returns Array of detected tenant-specific URLs with their locations
58
+ */
59
+ async scanSolution(zip) {
60
+ const detectedUrls = [];
61
+ const seenUrls = new Set();
62
+ for (const [path, zipEntry] of Object.entries(zip.files)) {
63
+ const entry = zipEntry;
64
+ if (entry.dir)
65
+ continue;
66
+ // Check if this is a text file we should scan
67
+ const isTextFile = TEXT_FILE_EXTENSIONS.some((ext) => path.toLowerCase().endsWith(ext)) ||
68
+ TEXT_FILE_PATTERNS.some((pattern) => pattern.test(path));
69
+ if (!isTextFile)
70
+ continue;
71
+ try {
72
+ const content = await entry.async("text");
73
+ const urlsInFile = this.extractUrlsFromContent(content, path);
74
+ for (const url of urlsInFile) {
75
+ // Dedupe by original URL
76
+ if (!seenUrls.has(url.originalUrl)) {
77
+ seenUrls.add(url.originalUrl);
78
+ detectedUrls.push(url);
79
+ }
80
+ }
81
+ }
82
+ catch (error) {
83
+ // Log at debug level - binary files will fail to parse as text and that's expected
84
+ logger.debug("Skipping file that cannot be read as text", {
85
+ path,
86
+ error: error instanceof Error ? error.message : String(error),
87
+ });
88
+ }
89
+ }
90
+ return detectedUrls;
91
+ }
92
+ /**
93
+ * Extract tenant-specific URLs from text content using pattern matching
94
+ *
95
+ * This function scans text content (typically from XML, JSON, or YAML files
96
+ * within a Power Platform solution) and identifies tenant-specific URLs that
97
+ * should be templated for multi-tenant deployment.
98
+ *
99
+ * **Supported URL patterns:**
100
+ * - SharePoint: `https://{tenant}.sharepoint.com/...`
101
+ * - Dynamics 365: `https://{tenant}.crm[N].dynamics.com/...`
102
+ * - Microsoft 365: `https://{tenant}.onmicrosoft.com/...`
103
+ *
104
+ * Each detected URL is converted to a template pattern where the tenant
105
+ * identifier is replaced with `{tenant}`, allowing the URL to be resolved
106
+ * to different values for each target tenant during deployment.
107
+ *
108
+ * @param content - The text content to scan for URLs
109
+ * @param fileLocation - Path of the file being scanned (for tracking purposes)
110
+ * @returns Array of detected URLs with their types, extracted tenants, and template patterns
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const urls = templater.extractUrlsFromContent(
115
+ * '<SharePointUrl>https://contoso.sharepoint.com/sites/docs</SharePointUrl>',
116
+ * 'solution/connections.xml'
117
+ * );
118
+ * // Returns: [{
119
+ * // type: 'sharepoint',
120
+ * // originalUrl: 'https://contoso.sharepoint.com/sites/docs',
121
+ * // extractedTenant: 'contoso',
122
+ * // templatePattern: 'https://{tenant}.sharepoint.com/sites/docs',
123
+ * // fileLocation: 'solution/connections.xml'
124
+ * // }]
125
+ * ```
126
+ */
127
+ extractUrlsFromContent(content, fileLocation) {
128
+ const urls = [];
129
+ for (const pattern of URL_PATTERNS) {
130
+ // Reset regex lastIndex
131
+ pattern.regex.lastIndex = 0;
132
+ let match;
133
+ while ((match = pattern.regex.exec(content)) !== null) {
134
+ const originalUrl = match[0].replace(/[,;:'")\]}>]+$/, ""); // Clean trailing punctuation
135
+ const tenant = pattern.extractTenant(match);
136
+ const region = pattern.extractRegion?.(match);
137
+ urls.push({
138
+ type: pattern.type,
139
+ originalUrl,
140
+ extractedTenant: tenant,
141
+ templatePattern: pattern.generateTemplate(originalUrl, tenant, region),
142
+ fileLocation,
143
+ });
144
+ }
145
+ }
146
+ return urls;
147
+ }
148
+ /**
149
+ * Infer the source tenant from detected URLs (most common tenant identifier)
150
+ */
151
+ inferSourceTenant(urls) {
152
+ if (urls.length === 0)
153
+ return null;
154
+ // Count occurrences of each tenant
155
+ const tenantCounts = new Map();
156
+ for (const url of urls) {
157
+ const count = tenantCounts.get(url.extractedTenant) || 0;
158
+ tenantCounts.set(url.extractedTenant, count + 1);
159
+ }
160
+ // Return most common tenant
161
+ let maxCount = 0;
162
+ let mostCommonTenant = null;
163
+ for (const [tenant, count] of tenantCounts) {
164
+ if (count > maxCount) {
165
+ maxCount = count;
166
+ mostCommonTenant = tenant;
167
+ }
168
+ }
169
+ return mostCommonTenant;
170
+ }
171
+ /**
172
+ * Generate URL templates from detected URLs
173
+ */
174
+ generateTemplates(detectedUrls) {
175
+ // Group by original URL to combine file locations
176
+ const urlMap = new Map();
177
+ for (const url of detectedUrls) {
178
+ const existing = urlMap.get(url.originalUrl) || [];
179
+ existing.push(url);
180
+ urlMap.set(url.originalUrl, existing);
181
+ }
182
+ // Create templates
183
+ const templates = [];
184
+ for (const [originalUrl, urls] of urlMap) {
185
+ const first = urls[0];
186
+ templates.push({
187
+ id: crypto.randomUUID(),
188
+ type: first.type,
189
+ originalUrl,
190
+ templatePattern: first.templatePattern,
191
+ extractedTenant: first.extractedTenant,
192
+ fileLocations: urls.map((u) => u.fileLocation),
193
+ description: this.getDescriptionForType(first.type),
194
+ confirmed: false,
195
+ });
196
+ }
197
+ return templates;
198
+ }
199
+ /**
200
+ * Get a human-readable description for a URL type
201
+ */
202
+ getDescriptionForType(type) {
203
+ switch (type) {
204
+ case "sharepoint":
205
+ return "SharePoint site URL - will be updated per tenant";
206
+ case "dynamics_crm":
207
+ return "Dynamics 365 / Dataverse URL - will be updated per tenant";
208
+ case "onmicrosoft":
209
+ return "Microsoft 365 tenant domain - will be updated per tenant";
210
+ case "custom":
211
+ return "Custom URL pattern";
212
+ }
213
+ }
214
+ /**
215
+ * Resolve a template pattern to an actual URL for a target tenant
216
+ */
217
+ resolveTemplate(templatePattern, tenantUrls) {
218
+ let resolved = templatePattern;
219
+ // Replace {tenant} placeholder
220
+ resolved = resolved.replace(/\{tenant\}/g, tenantUrls.tenant);
221
+ // Replace full domain patterns if present
222
+ resolved = resolved.replace(/\{tenant\}\.sharepoint\.com/g, tenantUrls.sharepoint);
223
+ resolved = resolved.replace(/\{tenant\}\.(crm[0-9]*)\.dynamics\.com/g, tenantUrls.dynamicsCrm);
224
+ resolved = resolved.replace(/\{tenant\}\.onmicrosoft\.com/g, tenantUrls.onmicrosoft);
225
+ return resolved;
226
+ }
227
+ /**
228
+ * Modify a Power Platform solution ZIP by replacing tenant-specific URLs
229
+ *
230
+ * This function takes a solution ZIP file and performs URL replacements
231
+ * in all text files (XML, JSON, YAML) within the archive. It's used during
232
+ * deployment to customize solutions for specific target tenants.
233
+ *
234
+ * **How it works:**
235
+ * 1. Loads the ZIP file into memory
236
+ * 2. Iterates through all files in the archive
237
+ * 3. For text files, performs string replacements for each URL mapping
238
+ * 4. Generates a new ZIP buffer with the modified content
239
+ *
240
+ * **Important notes:**
241
+ * - Binary files are skipped (images, compiled resources, etc.)
242
+ * - The original ZIP is not modified; a new buffer is returned
243
+ * - If no replacements are needed, the original buffer is returned unchanged
244
+ * - Requires a JSZip-compatible loader to be passed in
245
+ *
246
+ * @param zipBuffer - The original solution ZIP file as a Buffer
247
+ * @param replacements - Map of original URLs to resolved URLs
248
+ * @param zipLoader - JSZip-compatible loader for reading/writing ZIP files
249
+ * @returns New Buffer containing the modified solution ZIP
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * const JSZip = require('jszip');
254
+ * const replacements = new Map([
255
+ * ['https://contoso.sharepoint.com', 'https://fabrikam.sharepoint.com'],
256
+ * ['https://contoso.crm.dynamics.com', 'https://fabrikam.crm.dynamics.com'],
257
+ * ]);
258
+ * const modifiedZip = await templater.modifySolution(
259
+ * originalZipBuffer,
260
+ * replacements,
261
+ * new JSZip()
262
+ * );
263
+ * ```
264
+ */
265
+ async modifySolution(zipBuffer, replacements, zipLoader) {
266
+ if (replacements.size === 0) {
267
+ return zipBuffer;
268
+ }
269
+ const zip = await zipLoader.loadAsync(zipBuffer);
270
+ for (const [path, zipEntry] of Object.entries(zip.files)) {
271
+ const entry = zipEntry;
272
+ if (entry.dir)
273
+ continue;
274
+ // Check if this is a text file
275
+ const isTextFile = TEXT_FILE_EXTENSIONS.some((ext) => path.toLowerCase().endsWith(ext)) ||
276
+ TEXT_FILE_PATTERNS.some((pattern) => pattern.test(path));
277
+ if (!isTextFile)
278
+ continue;
279
+ try {
280
+ let content = await entry.async("text");
281
+ let modified = false;
282
+ // Apply all replacements
283
+ for (const [original, resolved] of replacements) {
284
+ if (content.includes(original)) {
285
+ content = content.split(original).join(resolved);
286
+ modified = true;
287
+ }
288
+ }
289
+ if (modified) {
290
+ zip.file(path, content);
291
+ }
292
+ }
293
+ catch (error) {
294
+ // Log at debug level - binary files will fail to process and that's expected
295
+ logger.debug("Skipping file that cannot be processed for URL replacement", {
296
+ path,
297
+ error: error instanceof Error ? error.message : String(error),
298
+ });
299
+ }
300
+ }
301
+ // Generate modified ZIP
302
+ return await zip.generateAsync({
303
+ type: "nodebuffer",
304
+ compression: "DEFLATE",
305
+ });
306
+ }
307
+ /**
308
+ * Create a complete AgentUrlTemplates object from scanning results
309
+ */
310
+ createAgentUrlTemplates(detectedUrls) {
311
+ if (detectedUrls.length === 0) {
312
+ return null;
313
+ }
314
+ const sourceTenant = this.inferSourceTenant(detectedUrls);
315
+ if (!sourceTenant) {
316
+ return null;
317
+ }
318
+ return {
319
+ sourceTenant,
320
+ templates: this.generateTemplates(detectedUrls),
321
+ createdAt: new Date().toISOString(),
322
+ };
323
+ }
324
+ }
325
+ // Export singleton instance
326
+ export const urlTemplater = new UrlTemplater();
327
+ //# sourceMappingURL=url-templater.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-templater.js","sourceRoot":"","sources":["../../src/services/url-templater.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,MAAM,GAAG,UAAU,CAAC;AA0G1B,MAAM,YAAY,GAAiB;IACjC;QACE,IAAI,EAAE,YAAY;QAClB,qFAAqF;QACrF,KAAK,EAAE,8DAA8D;QACrE,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAClC,gBAAgB,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CACjC,GAAG,CAAC,OAAO,CACT,IAAI,MAAM,CAAC,6CAA6C,EAAE,GAAG,CAAC,EAC9D,iCAAiC,CAClC;KACJ;IACD;QACE,IAAI,EAAE,cAAc;QACpB,+EAA+E;QAC/E,KAAK,EAAE,yEAAyE;QAChF,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAClC,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAClC,gBAAgB,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CACzC,GAAG,CAAC,OAAO,CACT,IAAI,MAAM,CAAC,yDAAyD,EAAE,GAAG,CAAC,EAC1E,oBAAoB,MAAM,IAAI,KAAK,eAAe,CACnD;KACJ;IACD;QACE,IAAI,EAAE,aAAa;QACnB,iFAAiF;QACjF,KAAK,EAAE,+DAA+D;QACtE,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAClC,gBAAgB,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CACjC,GAAG,CAAC,OAAO,CACT,IAAI,MAAM,CAAC,8CAA8C,EAAE,GAAG,CAAC,EAC/D,kCAAkC,CACnC;KACJ;CACF,CAAC;AAEF,mCAAmC;AACnC,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAChE,MAAM,kBAAkB,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;AAEpD,MAAM,OAAO,YAAY;IACvB;;;;;OAKG;IACH,KAAK,CAAC,YAAY,CAAC,GAAe;QAChC,MAAM,YAAY,GAAkB,EAAE,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;QAEnC,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACzD,MAAM,KAAK,GAAG,QAAmB,CAAC;YAClC,IAAI,KAAK,CAAC,GAAG;gBAAE,SAAS;YAExB,8CAA8C;YAC9C,MAAM,UAAU,GACd,oBAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACpE,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAE3D,IAAI,CAAC,UAAU;gBAAE,SAAS;YAE1B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;gBAE9D,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;oBAC7B,yBAAyB;oBACzB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;wBACnC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;wBAC9B,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACzB,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,mFAAmF;gBACnF,MAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE;oBACxD,IAAI;oBACJ,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAC9D,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,YAAY,CAAC;IACtB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,sBAAsB,CAAC,OAAe,EAAE,YAAoB;QAC1D,MAAM,IAAI,GAAkB,EAAE,CAAC;QAE/B,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;YACnC,wBAAwB;YACxB,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;YAE5B,IAAI,KAAK,CAAC;YACV,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBACtD,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,6BAA6B;gBACzF,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;gBAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC,CAAC;gBAE9C,IAAI,CAAC,IAAI,CAAC;oBACR,IAAI,EAAE,OAAO,CAAC,IAAI;oBAClB,WAAW;oBACX,eAAe,EAAE,MAAM;oBACvB,eAAe,EAAE,OAAO,CAAC,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC;oBACtE,YAAY;iBACb,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,iBAAiB,CAAC,IAAmB;QACnC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAEnC,mCAAmC;QACnC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC/C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;YACzD,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;QAED,4BAA4B;QAC5B,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,gBAAgB,GAAkB,IAAI,CAAC;QAC3C,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;YAC3C,IAAI,KAAK,GAAG,QAAQ,EAAE,CAAC;gBACrB,QAAQ,GAAG,KAAK,CAAC;gBACjB,gBAAgB,GAAG,MAAM,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,iBAAiB,CAAC,YAA2B;QAC3C,kDAAkD;QAClD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyB,CAAC;QAChD,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACnD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACxC,CAAC;QAED,mBAAmB;QACnB,MAAM,SAAS,GAAkB,EAAE,CAAC;QACpC,KAAK,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACtB,SAAS,CAAC,IAAI,CAAC;gBACb,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;gBACvB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,WAAW;gBACX,eAAe,EAAE,KAAK,CAAC,eAAe;gBACtC,eAAe,EAAE,KAAK,CAAC,eAAe;gBACtC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;gBAC9C,WAAW,EAAE,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC;gBACnD,SAAS,EAAE,KAAK;aACjB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACK,qBAAqB,CAAC,IAAqB;QACjD,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,YAAY;gBACf,OAAO,kDAAkD,CAAC;YAC5D,KAAK,cAAc;gBACjB,OAAO,2DAA2D,CAAC;YACrE,KAAK,aAAa;gBAChB,OAAO,0DAA0D,CAAC;YACpE,KAAK,QAAQ;gBACX,OAAO,oBAAoB,CAAC;QAChC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,eAAuB,EAAE,UAA2B;QAClE,IAAI,QAAQ,GAAG,eAAe,CAAC;QAE/B,+BAA+B;QAC/B,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QAE9D,0CAA0C;QAC1C,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,8BAA8B,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;QACnF,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,yCAAyC,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;QAC/F,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,+BAA+B,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;QAErF,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,KAAK,CAAC,cAAc,CAClB,SAAiB,EACjB,YAAiC,EACjC,SAAoB;QAEpB,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAEjD,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACzD,MAAM,KAAK,GAAG,QAAmB,CAAC;YAClC,IAAI,KAAK,CAAC,GAAG;gBAAE,SAAS;YAExB,+BAA+B;YAC/B,MAAM,UAAU,GACd,oBAAoB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACpE,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAE3D,IAAI,CAAC,UAAU;gBAAE,SAAS;YAE1B,IAAI,CAAC;gBACH,IAAI,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACxC,IAAI,QAAQ,GAAG,KAAK,CAAC;gBAErB,yBAAyB;gBACzB,KAAK,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,YAAY,EAAE,CAAC;oBAChD,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAC/B,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;wBACjD,QAAQ,GAAG,IAAI,CAAC;oBAClB,CAAC;gBACH,CAAC;gBAED,IAAI,QAAQ,EAAE,CAAC;oBACb,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,6EAA6E;gBAC7E,MAAM,CAAC,KAAK,CAAC,4DAA4D,EAAE;oBACzE,IAAI;oBACJ,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;iBAC9D,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,OAAO,MAAM,GAAG,CAAC,aAAa,CAAC;YAC7B,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,SAAS;SACvB,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,uBAAuB,CAAC,YAA2B;QACjD,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;QAC1D,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO;YACL,YAAY;YACZ,SAAS,EAAE,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC;YAC/C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;IACJ,CAAC;CACF;AAED,4BAA4B;AAC5B,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Copyright 2024 Pax8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { TokenManager } from "../auth/token-manager.js";
17
+ import { TenantConfig } from "../config/schema.js";
18
+ export interface SolutionVersionInfo {
19
+ uniqueName: string;
20
+ friendlyName: string;
21
+ expectedVersion: string;
22
+ deployedVersion: string | null;
23
+ isManaged: boolean;
24
+ status: "current" | "outdated" | "ahead" | "not_deployed" | "unknown";
25
+ versionDrift: number;
26
+ }
27
+ export interface TenantVersionStatus {
28
+ tenantId: string;
29
+ tenantName: string;
30
+ environmentUrl: string;
31
+ solutions: SolutionVersionInfo[];
32
+ overallStatus: "current" | "outdated" | "mixed" | "unknown";
33
+ lastChecked: string;
34
+ error?: string;
35
+ }
36
+ export interface VersionDriftSummary {
37
+ totalTenants: number;
38
+ currentTenants: number;
39
+ outdatedTenants: number;
40
+ unknownTenants: number;
41
+ solutionSummary: {
42
+ uniqueName: string;
43
+ friendlyName: string;
44
+ expectedVersion: string;
45
+ tenantsAtVersion: number;
46
+ tenantsBehind: number;
47
+ tenantsNotDeployed: number;
48
+ }[];
49
+ }
50
+ /**
51
+ * Compare two version strings (e.g., "1.0.0.5" vs "1.0.0.4")
52
+ * Returns: negative if a < b, 0 if equal, positive if a > b
53
+ */
54
+ export declare function compareVersions(a: string, b: string): number;
55
+ /**
56
+ * Version Checker class for checking solution versions across tenants
57
+ */
58
+ export declare class VersionChecker {
59
+ private cache;
60
+ private readonly CACHE_DURATION_MS;
61
+ /**
62
+ * Check solution versions for a single tenant
63
+ */
64
+ checkTenantVersions(tenant: TenantConfig, expectedSolutions: Array<{
65
+ uniqueName: string;
66
+ friendlyName: string;
67
+ version: string;
68
+ }>, tokenManager?: TokenManager, skipCache?: boolean): Promise<TenantVersionStatus>;
69
+ /**
70
+ * Check versions for multiple tenants
71
+ */
72
+ checkMultipleTenants(tenants: TenantConfig[], expectedSolutions: Array<{
73
+ uniqueName: string;
74
+ friendlyName: string;
75
+ version: string;
76
+ }>, tokenManager?: TokenManager, skipCache?: boolean): Promise<TenantVersionStatus[]>;
77
+ /**
78
+ * Get a summary of version drift across all tenants
79
+ */
80
+ getVersionDriftSummary(tenants: TenantConfig[], expectedSolutions: Array<{
81
+ uniqueName: string;
82
+ friendlyName: string;
83
+ version: string;
84
+ }>, tokenManager?: TokenManager): Promise<VersionDriftSummary>;
85
+ /**
86
+ * Clear cache for a tenant or all tenants
87
+ */
88
+ clearCache(tenantId?: string): void;
89
+ private calculateOverallStatus;
90
+ private getDemoVersionStatus;
91
+ /**
92
+ * Compute the version-drift status by comparing the rightmost-changed
93
+ * version segment. Used for demo-mode classification so `versionDrift`
94
+ * reflects how many releases behind the deployed version is — which is
95
+ * what the risk analyzer's `versions_behind` factor keys off of.
96
+ */
97
+ private diffPatchVersion;
98
+ }
99
+ export declare const versionChecker: VersionChecker;
100
+ /**
101
+ * Get demo version status for all tenants and solutions
102
+ */
103
+ export declare function getDemoVersionDriftSummary(): VersionDriftSummary;
104
+ /**
105
+ * Get demo version status for a specific tenant
106
+ */
107
+ export declare function getDemoTenantVersionStatus(tenantId: string): TenantVersionStatus | null;
108
+ //# sourceMappingURL=version-checker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-checker.d.ts","sourceRoot":"","sources":["../../src/services/version-checker.ts"],"names":[],"mappings":"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"}