@paklo/core 0.9.0 → 0.11.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.
@@ -1,58 +1,581 @@
1
- import { n as logger } from "../logger-5PEqZVLr.mjs";
2
- import { F as parseDependabotConfig, M as POSSIBLE_CONFIG_FILE_PATHS, c as DependabotExistingGroupPRSchema, l as DependabotExistingPRSchema } from "../job-BxOZ-hqF.mjs";
1
+ import { n as logger } from "../logger-3Qfh9NUj.mjs";
2
+ import { T as CONFIG_FILE_PATHS_AZURE, _ as DependabotPersistedPrSchema, z as parseDependabotConfig } from "../job-COuliaYg.mjs";
3
+ import { n as getDependencyNames, o as normalizeBranchName, s as normalizeFilePath, t as areEqual } from "../dependabot-DVf7lAEG.mjs";
3
4
  import { z } from "zod";
4
5
  import ky, { isHTTPError } from "ky";
5
6
  import * as path from "node:path";
6
7
  import { existsSync } from "node:fs";
7
8
  import { readFile } from "node:fs/promises";
8
9
 
9
- //#region src/azure/models.ts
10
+ //#region src/azure/client/constants.ts
11
+ const API_VERSION = "5.0";
12
+ const API_VERSION_PREVIEW = "5.0-preview";
13
+ /** Returned when no user is authenticated */
14
+ const ANONYMOUS_USER_ID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
10
15
  /**
11
16
  * Pull request property names used to store metadata about the pull request.
12
17
  * https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-request-properties
13
18
  */
14
- const DEVOPS_PR_PROPERTY_MICROSOFT_GIT_SOURCE_REF_NAME = "Microsoft.Git.PullRequest.SourceRefName";
15
- const DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER = "Dependabot.PackageManager";
16
- const DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES = "Dependabot.Dependencies";
19
+ const PR_PROPERTY_MICROSOFT_GIT_SOURCE_REF_NAME = "Microsoft.Git.PullRequest.SourceRefName";
20
+ const PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER = "Dependabot.PackageManager";
21
+ const PR_PROPERTY_DEPENDABOT_DEPENDENCIES = "Dependabot.Dependencies";
22
+ const PR_DESCRIPTION_MAX_LENGTH = 4e3;
17
23
 
18
24
  //#endregion
19
- //#region src/azure/utils.ts
20
- function normalizeFilePath(path$1) {
21
- return path$1?.replace(/\\/g, "/")?.replace(/^\.\//, "/")?.replace(/^([^/])/, "/$1");
22
- }
23
- function normalizeBranchName(branch) {
24
- return branch?.replace(/^refs\/heads\//i, "");
25
- }
26
- const DependenciesPrPropertySchema = DependabotExistingPRSchema.array().or(DependabotExistingGroupPRSchema);
25
+ //#region src/azure/client/client-base.ts
26
+ var BaseAzureDevOpsClient = class {
27
+ constructor(client) {
28
+ this.client = client;
29
+ }
30
+ makeUrl(path$1, params, apiVersion = API_VERSION) {
31
+ if (typeof params === "string") {
32
+ apiVersion = params;
33
+ params = {};
34
+ }
35
+ return `${path$1}?${Object.entries({
36
+ "api-version": apiVersion,
37
+ ...params
38
+ }).filter(([, value]) => value).map(([key, value]) => `${key}=${value}`).join("&")}`;
39
+ }
40
+ };
41
+
42
+ //#endregion
43
+ //#region src/azure/client/client-connection.ts
44
+ var ConnectionClient = class extends BaseAzureDevOpsClient {
45
+ /**
46
+ * Get the connection data for the current user.
47
+ */
48
+ async get() {
49
+ return await this.client.get(this.makeUrl("_apis/connectiondata", API_VERSION_PREVIEW)).json();
50
+ }
51
+ };
52
+
53
+ //#endregion
54
+ //#region src/azure/client/client-git.ts
55
+ var GitClient = class extends BaseAzureDevOpsClient {
56
+ async getItem(projectIdOrName, repositoryIdOrName, path$1, includeContent = true, latestProcessedChange = true) {
57
+ try {
58
+ return await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/items`, {
59
+ path: path$1,
60
+ includeContent,
61
+ latestProcessedChange
62
+ })).json();
63
+ } catch (e) {
64
+ if (isHTTPError(e) && e.response.status === 404) return;
65
+ throw e;
66
+ }
67
+ }
68
+ async getPush(projectIdOrName, repositoryIdOrName, pushId, includeCommits, includeRefUpdates) {
69
+ return await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pushes/${pushId}`, {
70
+ includeCommits,
71
+ includeRefUpdates
72
+ })).json();
73
+ }
74
+ async createPush(projectIdOrName, repositoryIdOrName, push) {
75
+ return await this.client.post(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pushes`), { json: push }).json();
76
+ }
77
+ async getDiffCommits(projectIdOrName, repositoryIdOrName, baseVersion, targetVersion) {
78
+ return await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/diffs/commits`, {
79
+ baseVersion,
80
+ baseVersionType: "commit",
81
+ targetVersion,
82
+ targetVersionType: "commit"
83
+ })).json();
84
+ }
85
+ async updateRef(projectIdOrName, repositoryIdOrName, ref) {
86
+ return (await this.client.post(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/refs`), { json: ref }).json()).value;
87
+ }
88
+ };
89
+
90
+ //#endregion
91
+ //#region src/azure/client/client-identity.ts
92
+ var IdentityClient = class extends BaseAzureDevOpsClient {
93
+ /**
94
+ * Get the identities that match the given user name, email, or group name.
95
+ * Requires scope "Identity (Read)" (vso.identity).
96
+ * @param filterValue username, email, or group name
97
+ * @returns
98
+ */
99
+ async get(filterValue) {
100
+ return (await this.client.get(this.makeUrl("_apis/identities", {
101
+ searchFilter: "General",
102
+ filterValue,
103
+ queryMembership: "None"
104
+ })).json())?.value;
105
+ }
106
+ };
107
+
108
+ //#endregion
109
+ //#region src/azure/client/client-projects.ts
110
+ var ProjectsClient = class extends BaseAzureDevOpsClient {
111
+ async list() {
112
+ return (await this.client.get(this.makeUrl("_apis/projects")).json())?.value;
113
+ }
114
+ async get(idOrName) {
115
+ return await this.client.get(this.makeUrl(`_apis/projects/${encodeURIComponent(idOrName)}`)).json();
116
+ }
117
+ };
118
+
119
+ //#endregion
120
+ //#region src/azure/client/client-pull-requests.ts
121
+ var PullRequestsClient = class extends BaseAzureDevOpsClient {
122
+ /**
123
+ * List pull requests
124
+ * Requires scope "Code (Read)" (vso.code).
125
+ * @param projectIdOrName
126
+ * @param repositoryIdOrName
127
+ * @param creatorId ID of the user who created the pull requests
128
+ * @param status The status of the pull requests to filter by
129
+ */
130
+ async list(projectIdOrName, repositoryIdOrName, creatorId, status) {
131
+ return (await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests`, {
132
+ "searchCriteria.creatorId": creatorId,
133
+ "searchCriteria.status": status
134
+ })).json())?.value;
135
+ }
136
+ async get(projectIdOrName, repositoryIdOrName, pullRequestId) {
137
+ return await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests/${pullRequestId}`)).json();
138
+ }
139
+ async create(projectIdOrName, repositoryIdOrName, pr) {
140
+ return await this.client.post(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests`), { json: pr }).json();
141
+ }
142
+ async update(projectIdOrName, repositoryIdOrName, pullRequestId, pr) {
143
+ return await this.client.patch(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests/${pullRequestId}`), { json: pr }).json();
144
+ }
145
+ async getProperties(projectIdOrName, repositoryIdOrName, pullRequestId) {
146
+ const response = await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests/${pullRequestId}/properties`)).json();
147
+ return Object.entries(response?.value || {}).filter(([, val]) => val?.$value).map(([key, val]) => ({
148
+ name: key,
149
+ value: val.$value
150
+ }));
151
+ }
152
+ async setProperties(projectIdOrName, repositoryIdOrName, pullRequestId, properties) {
153
+ return await this.client.patch(this.makeUrl(`${projectIdOrName}/_apis/git/repositories/${repositoryIdOrName}/pullrequests/${pullRequestId}/properties`), {
154
+ headers: { "Content-Type": "application/json-patch+json" },
155
+ json: properties.map((property) => {
156
+ return {
157
+ op: "add",
158
+ path: `/${property.name}`,
159
+ value: property.value
160
+ };
161
+ })
162
+ }).json();
163
+ }
164
+ /**
165
+ * Approve a pull request.
166
+ * Requires scope "Code (Write)" (vso.code_write).
167
+ */
168
+ async approve(projectIdOrName, repositoryIdOrName, pullRequestId, userId) {
169
+ return await this.client.put(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests/${pullRequestId}/reviewers/${userId}`, "7.1"), { json: {
170
+ vote: 10,
171
+ isReapprove: true
172
+ } }).json();
173
+ }
174
+ /**
175
+ * Abandon a pull request.
176
+ * Requires scope "Code (Write)" (vso.code_write).
177
+ */
178
+ async abandon(projectIdOrName, repositoryIdOrName, pullRequestId, userId) {
179
+ return await this.client.patch(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests/${pullRequestId}`), { json: {
180
+ status: "abandoned",
181
+ closedBy: { id: userId }
182
+ } }).json();
183
+ }
184
+ /**
185
+ * Get commits of a pull request.
186
+ * Requires scope "Code (Read)" (vso.code_read).
187
+ */
188
+ async getCommits(projectIdOrName, repositoryIdOrName, pullRequestId) {
189
+ return (await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests/${pullRequestId}/commits`)).json())?.value;
190
+ }
191
+ /**
192
+ * Create a comment thread on a pull request.
193
+ * Requires scope "Code (Write)" (vso.code_write).
194
+ */
195
+ async createCommentThread(projectIdOrName, repositoryIdOrName, pullRequestId, thread) {
196
+ return await this.client.post(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/pullrequests/${pullRequestId}/threads`), { json: thread }).json();
197
+ }
198
+ };
199
+
200
+ //#endregion
201
+ //#region src/azure/client/client-repositories.ts
202
+ var RepositoriesClient = class extends BaseAzureDevOpsClient {
203
+ async list(projectIdOrName) {
204
+ return (await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories`)).json())?.value;
205
+ }
206
+ async get(projectIdOrName, repositoryIdOrName) {
207
+ try {
208
+ return await this.client.get(this.makeUrl(`${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}`)).json();
209
+ } catch (e) {
210
+ if (isHTTPError(e) && e.response.status === 404) return;
211
+ throw e;
212
+ }
213
+ }
214
+ /**
215
+ * Get the list of refs (a.k.a branch names) for a repository.
216
+ * Requires scope "Code (Read)" (vso.code).
217
+ * @param projectIdOrName
218
+ * @param repositoryIdOrName
219
+ * @returns
220
+ */
221
+ async getRefs(projectIdOrName, repositoryIdOrName) {
222
+ return (await this.client.get(this.makeUrl(`${projectIdOrName}/_apis/git/repositories/${repositoryIdOrName}/refs`)).json())?.value;
223
+ }
224
+ async getBranchStats(projectIdOrName, repositoryIdOrName, branchName) {
225
+ return await this.client.get(this.makeUrl(`${projectIdOrName}/_apis/git/repositories/${repositoryIdOrName}/stats/branches`, { name: branchName })).json();
226
+ }
227
+ };
228
+
229
+ //#endregion
230
+ //#region src/azure/client/client-subscriptions.ts
231
+ var HookSubscriptionsClient = class extends BaseAzureDevOpsClient {
232
+ async query(query) {
233
+ return (await this.client.post(this.makeUrl("_apis/hooks/subscriptionsquery"), { json: query }).json())?.results;
234
+ }
235
+ async create(subscription) {
236
+ return await this.client.post(this.makeUrl("_apis/hooks/subscriptions"), { json: subscription }).json();
237
+ }
238
+ async replace(subscriptionId, subscription) {
239
+ return await this.client.put(this.makeUrl(`_apis/hooks/subscriptions/${subscriptionId}`), { json: subscription }).json();
240
+ }
241
+ async delete(subscriptionId) {
242
+ await this.client.delete(this.makeUrl(`_apis/hooks/subscriptions/${subscriptionId}`)).json();
243
+ }
244
+ };
245
+
246
+ //#endregion
247
+ //#region src/azure/client/client.ts
248
+ var AzureDevOpsClient = class AzureDevOpsClient {
249
+ organizationSlug;
250
+ organizationUrl;
251
+ connection;
252
+ identity;
253
+ projects;
254
+ repositories;
255
+ git;
256
+ pullRequests;
257
+ subscriptions;
258
+ constructor(url, accessToken, debug = false) {
259
+ this.organizationSlug = url.organisation;
260
+ const organizationUrl = url.value.toString().replace(/\/$/, "");
261
+ this.organizationUrl = organizationUrl;
262
+ const mainClientOptions = AzureDevOpsClient.createClientOptions(accessToken, debug, { prefixUrl: organizationUrl });
263
+ const mainClient = ky.create(mainClientOptions);
264
+ this.connection = new ConnectionClient(mainClient);
265
+ this.projects = new ProjectsClient(mainClient);
266
+ this.repositories = new RepositoriesClient(mainClient);
267
+ this.git = new GitClient(mainClient);
268
+ this.pullRequests = new PullRequestsClient(mainClient);
269
+ this.subscriptions = new HookSubscriptionsClient(mainClient);
270
+ const identityApiUrl = url["identity-api-url"].toString().replace(/\/$/, "");
271
+ this.identity = new IdentityClient(ky.create({
272
+ ...mainClientOptions,
273
+ prefixUrl: identityApiUrl
274
+ }));
275
+ }
276
+ static createClientOptions(accessToken, debug, options) {
277
+ return {
278
+ headers: {
279
+ Authorization: `Basic ${Buffer.from(`:${accessToken}`).toString("base64")}`,
280
+ Accept: "application/json"
281
+ },
282
+ hooks: {
283
+ beforeRequest: [async (request, options$1) => {
284
+ if (debug) logger.debug(`🌎 🠊 [${request.method}] ${request.url}`);
285
+ }],
286
+ afterResponse: [async (request, options$1, response) => {
287
+ if (debug) {
288
+ logger.debug(`🌎 🠈 [${response.status}] ${response.statusText}`);
289
+ if (request.body) logger.debug(`REQUEST: ${JSON.stringify(request.body)}`);
290
+ }
291
+ }],
292
+ beforeRetry: [async ({ request, options: options$1, error, retryCount }) => {
293
+ if (debug && isHTTPError(error)) logger.debug(`⏳ Retrying failed request with status code: ${error.response.status}`);
294
+ }]
295
+ },
296
+ retry: {
297
+ limit: 3,
298
+ delay: (attempt) => 3e3
299
+ },
300
+ ...options
301
+ };
302
+ }
303
+ };
304
+
305
+ //#endregion
306
+ //#region src/azure/client/types.ts
307
+ const AzdoVersionControlChangeTypeSchema = z.enum([
308
+ "none",
309
+ "add",
310
+ "edit",
311
+ "encoding",
312
+ "rename",
313
+ "delete",
314
+ "undelete",
315
+ "branch",
316
+ "merge",
317
+ "lock",
318
+ "rollback",
319
+ "sourceRename",
320
+ "targetRename",
321
+ "property",
322
+ "all"
323
+ ]);
324
+ const AZDO_PULL_REQUEST_MERGE_STRATEGIES = [
325
+ "noFastForward",
326
+ "squash",
327
+ "rebase",
328
+ "rebaseMerge"
329
+ ];
330
+ const AzdoPullRequestMergeStrategySchema = z.enum(AZDO_PULL_REQUEST_MERGE_STRATEGIES);
331
+ const AzdoCommentThreadStatusSchema = z.enum([
332
+ "unknown",
333
+ "active",
334
+ "fixed",
335
+ "wontFix",
336
+ "closed",
337
+ "byDesign",
338
+ "pending"
339
+ ]);
340
+ const AzdoCommentTypeSchema = z.enum([
341
+ "unknown",
342
+ "text",
343
+ "codeChange",
344
+ "system"
345
+ ]);
346
+ const AzdoPullRequestAsyncStatusSchema = z.enum([
347
+ "notSet",
348
+ "queued",
349
+ "conflicts",
350
+ "succeeded",
351
+ "rejectedByPolicy",
352
+ "failure"
353
+ ]);
354
+ const AzdoPullRequestStatusSchema = z.enum([
355
+ "notSet",
356
+ "active",
357
+ "abandoned",
358
+ "completed",
359
+ "all"
360
+ ]);
361
+ const AzdoProjectSchema = z.object({
362
+ id: z.string(),
363
+ name: z.string(),
364
+ description: z.string().optional(),
365
+ url: z.string(),
366
+ state: z.enum([
367
+ "deleting",
368
+ "new",
369
+ "wellFormed",
370
+ "createPending",
371
+ "all",
372
+ "unchanged",
373
+ "deleted"
374
+ ]),
375
+ _links: z.object({
376
+ self: z.object({ href: z.string() }),
377
+ collection: z.object({ href: z.string() }),
378
+ web: z.object({ href: z.string() })
379
+ }).optional()
380
+ });
381
+ const AzdoRepositorySchema = z.object({
382
+ id: z.string(),
383
+ name: z.string(),
384
+ defaultBranch: z.string().optional(),
385
+ project: AzdoProjectSchema,
386
+ isDisabled: z.boolean().optional(),
387
+ isFork: z.boolean().optional(),
388
+ url: z.string(),
389
+ remoteUrl: z.string(),
390
+ webUrl: z.string()
391
+ });
392
+ const AzdoIdentitySchema = z.object({
393
+ id: z.string(),
394
+ displayName: z.string(),
395
+ url: z.string()
396
+ });
397
+ const AzdoIdentityRefSchema = z.object({
398
+ id: z.string().optional(),
399
+ displayName: z.string().optional(),
400
+ uniqueName: z.string().optional(),
401
+ url: z.string().optional()
402
+ });
403
+ const AzdoConnectionDataSchema = z.object({
404
+ authenticatedUser: AzdoIdentitySchema,
405
+ authorizedUser: AzdoIdentitySchema
406
+ });
407
+ const AzdoGitUserDateSchema = z.object({
408
+ name: z.string(),
409
+ email: z.string(),
410
+ date: z.string().optional()
411
+ });
412
+ const AzdoGitRefSchema = z.object({
413
+ name: z.string(),
414
+ objectId: z.string(),
415
+ isLocked: z.boolean().optional()
416
+ });
417
+ const AzdoGitRefUpdateResultSchema = AzdoGitRefSchema.extend({
418
+ oldObjectId: z.string(),
419
+ newObjectId: z.string(),
420
+ success: z.boolean(),
421
+ customMessage: z.string().optional()
422
+ });
423
+ const AzdoGitChangeSchema = z.object({
424
+ changeType: AzdoVersionControlChangeTypeSchema,
425
+ item: z.object({ path: z.string() }).optional(),
426
+ newContent: z.object({
427
+ content: z.string(),
428
+ contentType: z.enum(["rawtext", "base64encoded"])
429
+ }).optional(),
430
+ originalPath: z.string().optional()
431
+ });
432
+ const AzdoGitCommitRefSchema = z.object({
433
+ commitId: z.string(),
434
+ author: AzdoGitUserDateSchema.optional(),
435
+ committer: AzdoGitUserDateSchema.optional(),
436
+ changes: AzdoGitChangeSchema.array().optional()
437
+ });
438
+ const AzdoGitPushSchema = z.object({
439
+ commits: AzdoGitCommitRefSchema.array(),
440
+ refUpdates: AzdoGitRefSchema.array()
441
+ });
442
+ const AzdoGitRefUpdateSchema = z.object({
443
+ name: z.string(),
444
+ oldObjectId: z.string(),
445
+ newObjectId: z.string().optional(),
446
+ isLocked: z.boolean().optional()
447
+ });
448
+ const AzdoGitPushCreateSchema = z.object({
449
+ refUpdates: AzdoGitRefUpdateSchema.array(),
450
+ commits: z.object({
451
+ comment: z.string(),
452
+ author: AzdoGitUserDateSchema.optional(),
453
+ changes: AzdoGitChangeSchema.array()
454
+ }).array()
455
+ });
456
+ const AzdoGitBranchStatsSchema = z.object({
457
+ aheadCount: z.number(),
458
+ behindCount: z.number()
459
+ });
460
+ const AzdoGitCommitDiffsSchema = z.object({
461
+ allChangesIncluded: z.boolean(),
462
+ baseCommit: z.string(),
463
+ changes: AzdoGitChangeSchema.array(),
464
+ targetCommit: z.string()
465
+ });
466
+ const AzdoRepositoryItemSchema = z.object({
467
+ latestProcessedChange: AzdoGitCommitRefSchema.optional(),
468
+ content: z.string().optional()
469
+ });
470
+ const AzdoIdentityRefWithVoteSchema = z.object({
471
+ id: z.string().optional(),
472
+ displayName: z.string().optional(),
473
+ vote: z.number().optional(),
474
+ hasDeclined: z.boolean().optional(),
475
+ isFlagged: z.boolean().optional(),
476
+ isRequired: z.boolean().optional()
477
+ });
478
+ const AzdoPullRequestSchema = z.object({
479
+ pullRequestId: z.number(),
480
+ status: AzdoPullRequestStatusSchema,
481
+ isDraft: z.boolean(),
482
+ sourceRefName: z.string(),
483
+ targetRefName: z.string(),
484
+ title: z.string(),
485
+ description: z.string().optional(),
486
+ lastMergeCommit: AzdoGitCommitRefSchema,
487
+ lastMergeSourceCommit: AzdoGitCommitRefSchema,
488
+ mergeStatus: AzdoPullRequestAsyncStatusSchema,
489
+ reviewers: AzdoIdentityRefWithVoteSchema.array().optional(),
490
+ workItemRefs: z.object({ id: z.string() }).array().optional(),
491
+ labels: z.object({ name: z.string() }).array().optional(),
492
+ autoCompleteSetBy: AzdoIdentityRefSchema.optional(),
493
+ completionOptions: z.object({
494
+ autoCompleteIgnoreConfigIds: z.number().array().optional(),
495
+ deleteSourceBranch: z.boolean().optional(),
496
+ mergeCommitMessage: z.string().optional(),
497
+ mergeStrategy: AzdoPullRequestMergeStrategySchema.optional(),
498
+ transitionWorkItems: z.boolean().optional()
499
+ }).optional(),
500
+ closedBy: AzdoIdentityRefSchema.optional()
501
+ });
502
+ const AzdoPropertiesSchema = z.record(z.string(), z.object({
503
+ $type: z.string(),
504
+ $value: z.string()
505
+ }));
506
+ const AzdoPullRequestCommentSchema = z.object({
507
+ id: z.number().optional(),
508
+ parentCommentId: z.number().optional(),
509
+ content: z.string(),
510
+ commentType: AzdoCommentTypeSchema,
511
+ publishedDate: z.string().optional(),
512
+ author: AzdoIdentityRefSchema
513
+ });
514
+ const AzdoPullRequestCommentThreadSchema = z.object({
515
+ id: z.number(),
516
+ comments: AzdoPullRequestCommentSchema.array(),
517
+ status: AzdoCommentThreadStatusSchema
518
+ });
519
+ const AzdoSubscriptionSchema = z.object({
520
+ id: z.string(),
521
+ status: z.enum([
522
+ "enabled",
523
+ "onProbation",
524
+ "disabledByUser",
525
+ "disabledBySystem",
526
+ "disabledByInactiveIdentity"
527
+ ]),
528
+ publisherId: z.string(),
529
+ publisherInputs: z.record(z.string(), z.string()),
530
+ consumerId: z.string().optional(),
531
+ consumerActionId: z.string().optional(),
532
+ consumerInputs: z.record(z.string(), z.string()),
533
+ eventType: z.string(),
534
+ resourceVersion: z.string(),
535
+ eventDescription: z.string().optional(),
536
+ actionDescription: z.string().optional()
537
+ });
538
+ const AzdoSubscriptionsQueryResponseSchema = z.object({ results: AzdoSubscriptionSchema.array() });
539
+ const AzdoSubscriptionsQueryInputFilterSchema = z.object({ conditions: z.object({
540
+ caseSensitive: z.boolean().optional(),
541
+ inputId: z.string().optional(),
542
+ inputValue: z.string().optional(),
543
+ operator: z.enum(["equals", "notEquals"])
544
+ }).array().optional() });
545
+ const AzdoSubscriptionsQuerySchema = z.object({
546
+ consumerActionId: z.string().optional(),
547
+ consumerId: z.string().optional(),
548
+ consumerInputFilters: AzdoSubscriptionsQueryInputFilterSchema.array().optional(),
549
+ eventType: z.string().optional(),
550
+ publisherId: z.string().optional(),
551
+ publisherInputFilters: AzdoSubscriptionsQueryInputFilterSchema.array().optional(),
552
+ subscriberId: z.string().optional()
553
+ });
554
+
555
+ //#endregion
556
+ //#region src/azure/client/utils.ts
27
557
  function buildPullRequestProperties(packageManager, dependencies) {
28
558
  return [{
29
- name: DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER,
559
+ name: PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER,
30
560
  value: packageManager
31
561
  }, {
32
- name: DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES,
562
+ name: PR_PROPERTY_DEPENDABOT_DEPENDENCIES,
33
563
  value: JSON.stringify(dependencies)
34
564
  }];
35
565
  }
36
566
  function parsePullRequestProperties(pullRequests, packageManager) {
37
567
  return Object.fromEntries(pullRequests.filter((pr) => {
38
- return pr.properties?.find((p) => p.name === DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER && (packageManager === null || p.value === packageManager));
568
+ return pr.properties?.find((p) => p.name === PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER && (packageManager === null || p.value === packageManager));
39
569
  }).map((pr) => {
40
- return [pr.id, DependenciesPrPropertySchema.parse(JSON.parse(pr.properties.find((p) => p.name === DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES).value))];
570
+ return [pr.pullRequestId, DependabotPersistedPrSchema.parse(JSON.parse(pr.properties.find((p) => p.name === PR_PROPERTY_DEPENDABOT_DEPENDENCIES).value))];
41
571
  }));
42
572
  }
43
573
  function getPullRequestForDependencyNames(existingPullRequests, packageManager, dependencyNames) {
44
574
  return existingPullRequests.find((pr) => {
45
- return pr.properties?.find((p) => p.name === DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER && p.value === packageManager) && pr.properties?.find((p) => p.name === DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES && areEqual(getDependencyNames(DependenciesPrPropertySchema.parse(JSON.parse(p.value))), dependencyNames));
575
+ return pr.properties?.find((p) => p.name === PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER && p.value === packageManager) && pr.properties?.find((p) => p.name === PR_PROPERTY_DEPENDABOT_DEPENDENCIES && areEqual(getDependencyNames(DependabotPersistedPrSchema.parse(JSON.parse(p.value))), dependencyNames));
46
576
  });
47
577
  }
48
- function getDependencyNames(dependencies) {
49
- return (Array.isArray(dependencies) ? dependencies : dependencies.dependencies).map((dep) => dep["dependency-name"]?.toString());
50
- }
51
- function areEqual(a, b) {
52
- if (a.length !== b.length) return false;
53
- return a.every((name) => b.includes(name));
54
- }
55
- function getPullRequestChangedFilesForOutputData(data) {
578
+ function getPullRequestChangedFiles(data) {
56
579
  return data["updated-dependency-files"].filter((file) => file.type === "file").map((file) => {
57
580
  let changeType = "none";
58
581
  if (file.deleted === true || file.operation === "delete") changeType = "delete";
@@ -66,229 +589,91 @@ function getPullRequestChangedFilesForOutputData(data) {
66
589
  };
67
590
  });
68
591
  }
69
- function getPullRequestCloseReasonForOutputData(data) {
70
- const leadDependencyName = data["dependency-names"][0];
71
- let reason;
72
- switch (data.reason) {
73
- case "dependencies_changed":
74
- reason = `Looks like the dependencies have changed`;
75
- break;
76
- case "dependency_group_empty":
77
- reason = `Looks like the dependencies in this group are now empty`;
78
- break;
79
- case "dependency_removed":
80
- reason = `Looks like ${leadDependencyName} is no longer a dependency`;
81
- break;
82
- case "up_to_date":
83
- reason = `Looks like ${leadDependencyName} is up-to-date now`;
84
- break;
85
- case "update_no_longer_possible":
86
- reason = `Looks like ${leadDependencyName} can no longer be updated`;
87
- break;
88
- }
89
- if (reason && reason.length > 0) reason += ", so this is no longer needed.";
90
- return reason;
91
- }
92
- function getPullRequestDependenciesPropertyValueForOutputData(data) {
93
- const dependencies = data.dependencies?.map((dep) => {
94
- return {
95
- "dependency-name": dep.name,
96
- "dependency-version": dep.version,
97
- directory: dep.directory
98
- };
99
- });
100
- const dependencyGroupName = data["dependency-group"]?.name;
101
- if (!dependencyGroupName) return dependencies;
102
- return {
103
- "dependency-group-name": dependencyGroupName,
104
- dependencies
105
- };
106
- }
107
- function getPullRequestDescription(packageManager, body, dependencies) {
108
- let header = "";
109
- const footer = "";
110
- const description = (body || "").replace(new RegExp(decodeURIComponent("%EF%BF%BD%EF%BF%BD%EF%BF%BD"), "g"), "");
111
- if (dependencies.length === 1) {
112
- const compatibilityScoreBadges = dependencies.map((dep) => {
113
- return `![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=${dep.name}&package-manager=${packageManager}&previous-version=${dep["previous-version"]}&new-version=${dep.version})`;
114
- });
115
- header += `${compatibilityScoreBadges.join(" ")}\n\n`;
116
- }
117
- const maxDescriptionLengthAfterHeaderAndFooter = 4e3 - header.length - 0;
118
- return `${header}${description.substring(0, maxDescriptionLengthAfterHeaderAndFooter)}${footer}`;
119
- }
120
592
 
121
593
  //#endregion
122
- //#region src/azure/client.ts
123
- /** Returned from AzureDevOpsWebApiClient.getUserId() when no user is authenticated */
124
- const ANONYMOUS_USER_ID = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
125
- /** Azure DevOps REST API client. */
126
- var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
127
- organisationApiUrl;
128
- identityApiUrl;
129
- accessToken;
130
- debug;
131
- client;
594
+ //#region src/azure/client/wrapper.ts
595
+ var AzureDevOpsClientWrapper = class {
596
+ inner;
132
597
  authenticatedUserId;
133
- resolvedUserIds;
134
- static API_VERSION = "5.0";
135
- static API_VERSION_PREVIEW = "5.0-preview";
598
+ resolvedUserIds = {};
599
+ /**
600
+ * Create a new Azure DevOps client wrapper.
601
+ * @param url The Azure DevOps organization URL
602
+ * @param accessToken The personal access token for authentication
603
+ * @param debug Enable debug logging for API requests (default: false)
604
+ */
136
605
  constructor(url, accessToken, debug = false) {
137
- this.organisationApiUrl = url.value.toString().replace(/\/$/, "");
138
- this.identityApiUrl = url["identity-api-url"].toString().replace(/\/$/, "");
139
- this.accessToken = accessToken;
140
- this.debug = debug;
141
- this.resolvedUserIds = {};
142
- this.client = this.createClient();
606
+ this.inner = new AzureDevOpsClient(url, accessToken, debug);
143
607
  }
144
608
  /**
145
609
  * Get the identity of the authenticated user.
146
- * @returns
610
+ * The result is cached after the first call to avoid repeated API requests.
147
611
  */
148
612
  async getUserId() {
149
613
  if (!this.authenticatedUserId) {
150
- this.authenticatedUserId = (await this.client.get(this.makeUrl(`${this.organisationApiUrl}/_apis/connectiondata`, AzureDevOpsWebApiClient.API_VERSION_PREVIEW)).json())?.authenticatedUser?.id;
614
+ this.authenticatedUserId = (await this.inner.connection.get())?.authenticatedUser?.id;
151
615
  if (!this.authenticatedUserId) throw new Error("Failed to get authenticated user ID");
152
616
  }
153
617
  return this.authenticatedUserId;
154
618
  }
155
619
  /**
156
620
  * Get the identity id from a user name, email, or group name.
621
+ * Results are cached to avoid repeated API requests for the same identifier.
622
+ *
157
623
  * Requires scope "Identity (Read)" (vso.identity).
158
- * @param userNameEmailOrGroupName
159
- * @returns
624
+ * @param identifier Username, email, or group name to resolve
625
+ * @returns The resolved identity ID, or undefined if not found or on error
160
626
  */
161
- async resolveIdentityId(userNameEmailOrGroupName) {
162
- if (this.resolvedUserIds[userNameEmailOrGroupName]) return this.resolvedUserIds[userNameEmailOrGroupName];
627
+ async resolveIdentityId(identifier) {
628
+ if (this.resolvedUserIds[identifier]) return this.resolvedUserIds[identifier];
163
629
  try {
164
- const identities = await this.client.get(this.makeUrl(`${this.identityApiUrl}/_apis/identities`, {
165
- searchFilter: "General",
166
- filterValue: userNameEmailOrGroupName,
167
- queryMembership: "None"
168
- })).json();
169
- if (!identities?.value || identities.value.length === 0) return;
170
- this.resolvedUserIds[userNameEmailOrGroupName] = identities.value[0].id;
171
- return this.resolvedUserIds[userNameEmailOrGroupName];
630
+ const identities = await this.inner.identity.get(identifier);
631
+ if (!identities || identities.length === 0) return;
632
+ this.resolvedUserIds[identifier] = identities[0].id;
633
+ return this.resolvedUserIds[identifier];
172
634
  } catch (e) {
173
635
  logger.error(`Failed to resolve user id: ${e}`);
174
636
  logger.debug(e);
175
637
  return;
176
638
  }
177
639
  }
178
- async getProjects() {
179
- try {
180
- return await this.client.get(this.makeUrl(`${this.organisationApiUrl}/_apis/projects`)).json();
181
- } catch (e) {
182
- logger.error(`Failed to get projects: ${e}`);
183
- logger.debug(e);
184
- return;
185
- }
186
- }
187
- async getProject(idOrName) {
188
- try {
189
- return await this.client.get(this.makeUrl(`${this.organisationApiUrl}/_apis/projects/${encodeURIComponent(idOrName)}`)).json();
190
- } catch (e) {
191
- logger.error(`Failed to get project: ${e}`);
192
- logger.debug(e);
193
- return;
194
- }
195
- }
196
- async getRepositories(projectIdOrName) {
197
- try {
198
- return await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${encodeURIComponent(projectIdOrName)}/_apis/git/repositories`)).json();
199
- } catch (e) {
200
- logger.error(`Failed to get repositories: ${e}`);
201
- logger.debug(e);
202
- return;
203
- }
204
- }
205
- async getRepository(projectIdOrName, repositoryIdOrName) {
206
- try {
207
- return await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}`)).json();
208
- } catch (e) {
209
- if (isHTTPError(e) && e.response.status === 404) return;
210
- else {
211
- logger.error(`Failed to get repository: ${e}`);
212
- logger.debug(e);
213
- }
214
- return;
215
- }
216
- }
217
- async getRepositoryItem(projectIdOrName, repositoryIdOrName, path$1, includeContent = true, latestProcessedChange = true) {
218
- try {
219
- return await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${encodeURIComponent(projectIdOrName)}/_apis/git/repositories/${encodeURIComponent(repositoryIdOrName)}/items`, {
220
- path: path$1,
221
- includeContent,
222
- latestProcessedChange
223
- })).json();
224
- } catch (e) {
225
- if (isHTTPError(e) && e.response.status === 404) return;
226
- else {
227
- logger.error(`Failed to get repository item: ${e}`);
228
- logger.debug(e);
229
- }
230
- return;
231
- }
232
- }
233
640
  /**
234
641
  * Get the default branch for a repository.
642
+ *
235
643
  * Requires scope "Code (Read)" (vso.code).
236
- * @param project
237
- * @param repository
238
- * @returns
644
+ * @param options Repository identification options (project and repository)
645
+ * @returns The normalized default branch name (e.g., "main"), or undefined if not found
239
646
  */
240
- async getDefaultBranch(project, repository) {
241
- try {
242
- const repo = await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${project}/_apis/git/repositories/${repository}`)).json();
243
- if (!repo) throw new Error(`Repository '${project}/${repository}' not found`);
244
- return normalizeBranchName(repo.defaultBranch);
245
- } catch (e) {
246
- logger.error(`Failed to get default branch for '${project}/${repository}': ${e}`);
247
- logger.debug(e);
248
- return;
249
- }
647
+ async getDefaultBranch(options) {
648
+ return normalizeBranchName((await this.inner.repositories.get(options.project, options.repository))?.defaultBranch);
250
649
  }
251
650
  /**
252
651
  * Get the list of branch names for a repository.
652
+ *
253
653
  * Requires scope "Code (Read)" (vso.code).
254
- * @param project
255
- * @param repository
256
- * @returns
654
+ * @param options Repository identification options (project and repository)
655
+ * @returns Array of normalized branch names, or undefined if not found
257
656
  */
258
- async getBranchNames(project, repository) {
259
- try {
260
- const refs = await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${project}/_apis/git/repositories/${repository}/refs`)).json();
261
- if (!refs) throw new Error(`Repository '${project}/${repository}' not found`);
262
- return refs.value?.map((r) => normalizeBranchName(r.name)) || [];
263
- } catch (e) {
264
- logger.error(`Failed to list branch names for '${project}/${repository}': ${e}`);
265
- logger.debug(e);
266
- return;
267
- }
657
+ async getBranchNames(options) {
658
+ return (await this.inner.repositories.getRefs(options.project, options.repository))?.map((r) => normalizeBranchName(r.name));
268
659
  }
269
660
  /**
270
- * Get the properties for all active pull request created by the supplied user.
661
+ * Get the properties for all active pull requests created by the specified user.
662
+ * This retrieves both the pull request IDs and their associated properties.
663
+ *
271
664
  * Requires scope "Code (Read)" (vso.code).
272
- * @param project
273
- * @param repository
274
- * @param creator
275
- * @returns
665
+ * @param options Repository identification options including the creator user ID
666
+ * @returns Array of pull request IDs with their properties, empty array on error
276
667
  */
277
- async getActivePullRequestProperties(project, repository, creator) {
668
+ async getActivePullRequestProperties({ project, repository, creatorId }) {
278
669
  try {
279
- const pullRequests = await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${project}/_apis/git/repositories/${repository}/pullrequests`, {
280
- "searchCriteria.creatorId": isGuid(creator) ? creator : await this.getUserId(),
281
- "searchCriteria.status": "Active"
282
- })).json();
283
- if (!pullRequests?.value || pullRequests.value.length === 0) return [];
284
- return await Promise.all(pullRequests.value.map(async (pr) => {
285
- const properties = await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${project}/_apis/git/repositories/${repository}/pullrequests/${pr.pullRequestId}/properties`)).json();
670
+ const pullRequests = await this.inner.pullRequests.list(project, repository, creatorId, "active");
671
+ if (!pullRequests || pullRequests.length === 0) return [];
672
+ return await Promise.all(pullRequests.map(async (pr) => {
673
+ const properties = await this.inner.pullRequests.getProperties(project, repository, pr.pullRequestId);
286
674
  return {
287
- id: pr.pullRequestId,
288
- properties: Object.entries(properties?.value || {}).map(([key, val]) => ({
289
- name: key,
290
- value: val?.$value
291
- }))
675
+ pullRequestId: pr.pullRequestId,
676
+ properties
292
677
  };
293
678
  }));
294
679
  } catch (e) {
@@ -298,32 +683,39 @@ var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
298
683
  }
299
684
  }
300
685
  /**
301
- * Create a new pull request.
686
+ * Create a new pull request with the specified changes.
687
+ * This method performs the following operations:
688
+ * 1. Resolves assignee identities for assignees (if specified)
689
+ * 2. Creates a new branch and pushes changes
690
+ * 3. Creates the pull request with assignees (optional reviewers), labels, and work items
691
+ * 4. Sets pull request properties for dependency metadata
692
+ * 5. Configures auto-complete options (if specified)
693
+ *
302
694
  * Requires scope "Code (Write)" (vso.code_write).
303
695
  * Requires scope "Identity (Read)" (vso.identity), if assignees are specified.
304
- * @param pr
305
- * @returns
696
+ * @param options Pull request creation options including changes, assignees, and auto-complete settings
697
+ * @returns The created pull request ID, or null on error
306
698
  */
307
- async createPullRequest(pr) {
308
- logger.info(`Creating pull request '${pr.title}'...`);
699
+ async createPullRequest(options) {
700
+ logger.info(`Creating pull request '${options.title}'...`);
309
701
  try {
310
702
  const userId = await this.getUserId();
311
703
  const reviewers = [];
312
- if (pr.assignees && pr.assignees.length > 0) for (const assignee of pr.assignees) {
313
- const identityId = isGuid(assignee) ? assignee : await this.resolveIdentityId(assignee);
704
+ if (options.assignees && options.assignees.length > 0) for (const assignee of options.assignees) {
705
+ const identityId = this.isGuid(assignee) ? assignee : await this.resolveIdentityId(assignee);
314
706
  if (identityId && !reviewers.some((r) => r.id === identityId)) reviewers.push({ id: identityId });
315
707
  else logger.warn(`Unable to resolve assignee identity '${assignee}'`);
316
708
  }
317
- logger.info(` - Pushing ${pr.changes.length} file change(s) to branch '${pr.source.branch}'...`);
318
- const push = await this.client.post(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pushes`), { json: {
709
+ logger.info(` - Pushing ${options.changes.length} file change(s) to branch '${options.source.branch}'...`);
710
+ const push = await this.inner.git.createPush(options.project, options.repository, {
319
711
  refUpdates: [{
320
- name: `refs/heads/${pr.source.branch}`,
321
- oldObjectId: pr.source.commit
712
+ name: `refs/heads/${options.source.branch}`,
713
+ oldObjectId: options.source.commit
322
714
  }],
323
715
  commits: [{
324
- comment: pr.commitMessage,
325
- author: pr.author,
326
- changes: pr.changes.filter((change) => change.changeType !== "none").map(({ changeType, ...change }) => {
716
+ comment: options.commitMessage,
717
+ author: options.author,
718
+ changes: options.changes.filter((change) => change.changeType !== "none").map(({ changeType, ...change }) => {
327
719
  return {
328
720
  changeType,
329
721
  item: { path: normalizeFilePath(change.path) },
@@ -334,46 +726,37 @@ var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
334
726
  };
335
727
  })
336
728
  }]
337
- } }).json();
729
+ });
338
730
  if (!push?.commits?.length) throw new Error("Failed to push changes to source branch, no commits were created");
339
731
  logger.info(` - Pushed commit: ${push.commits.map((c) => c.commitId).join(", ")}.`);
340
- logger.info(` - Creating pull request to merge '${pr.source.branch}' into '${pr.target.branch}'...`);
341
- const pullRequest = await this.client.post(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests`), { json: {
342
- sourceRefName: `refs/heads/${pr.source.branch}`,
343
- targetRefName: `refs/heads/${pr.target.branch}`,
344
- title: pr.title,
345
- description: pr.description,
732
+ logger.info(` - Creating pull request to merge '${options.source.branch}' into '${options.target.branch}'...`);
733
+ const pullRequest = await this.inner.pullRequests.create(options.project, options.repository, {
734
+ sourceRefName: `refs/heads/${options.source.branch}`,
735
+ targetRefName: `refs/heads/${options.target.branch}`,
736
+ title: options.title,
737
+ description: options.description,
346
738
  reviewers,
347
- workItemRefs: pr.workItems?.map((id) => ({ id })),
348
- labels: pr.labels?.map((label) => ({ name: label }))
349
- } }).json();
739
+ workItemRefs: options.workItems?.map((id) => ({ id })),
740
+ labels: options.labels?.map((label) => ({ name: label }))
741
+ });
350
742
  if (!pullRequest?.pullRequestId) throw new Error("Failed to create pull request, no pull request id was returned");
351
743
  logger.info(` - Created pull request: #${pullRequest.pullRequestId}.`);
352
- if (pr.properties && pr.properties.length > 0) {
744
+ if (options.properties && options.properties.length > 0) {
353
745
  logger.info(` - Adding dependency metadata to pull request properties...`);
354
- if (!(await this.client.patch(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pullRequest.pullRequestId}/properties`), {
355
- json: pr.properties.map((property) => {
356
- return {
357
- op: "add",
358
- path: `/${property.name}`,
359
- value: property.value
360
- };
361
- }),
362
- headers: { "Content-Type": "application/json-patch+json" }
363
- }).json())?.count) throw new Error("Failed to add dependency metadata properties to pull request");
746
+ if (!(await this.inner.pullRequests.setProperties(options.project, options.repository, pullRequest.pullRequestId, options.properties))?.count) throw new Error("Failed to add dependency metadata properties to pull request");
364
747
  }
365
- if (pr.autoComplete) {
748
+ if (options.autoComplete) {
366
749
  logger.info(` - Updating auto-complete options...`);
367
- const updatedPullRequest = await this.client.patch(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pullRequest.pullRequestId}`), { json: {
750
+ const updatedPullRequest = await this.inner.pullRequests.update(options.project, options.repository, pullRequest.pullRequestId, {
368
751
  autoCompleteSetBy: { id: userId },
369
752
  completionOptions: {
370
- autoCompleteIgnoreConfigIds: pr.autoComplete.ignorePolicyConfigIds,
753
+ autoCompleteIgnoreConfigIds: options.autoComplete.ignorePolicyConfigIds,
371
754
  deleteSourceBranch: true,
372
- mergeCommitMessage: mergeCommitMessage(pullRequest.pullRequestId, pr.title, pr.description),
373
- mergeStrategy: pr.autoComplete.mergeStrategy,
755
+ mergeCommitMessage: this.mergeCommitMessage(pullRequest.pullRequestId, options.title, options.description),
756
+ mergeStrategy: options.autoComplete.mergeStrategy,
374
757
  transitionWorkItems: false
375
758
  }
376
- } }).json();
759
+ });
377
760
  if (!updatedPullRequest || updatedPullRequest.autoCompleteSetBy?.id !== userId) throw new Error("Failed to set auto-complete on pull request");
378
761
  }
379
762
  logger.info(` - Pull request was created successfully.`);
@@ -385,29 +768,29 @@ var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
385
768
  }
386
769
  }
387
770
  /**
388
- * Update a pull request.
771
+ * Update an existing pull request with new changes.
772
+ * This method performs the following operations:
773
+ * 1. Validates the pull request hasn't been modified by another author
774
+ * 2. Checks if the source branch is behind the target branch
775
+ * 3. Rebases the target branch into the source branch if needed
776
+ * 4. Pushes the new changes to the source branch
777
+ *
389
778
  * Requires scope "Code (Read & Write)" (vso.code, vso.code_write).
390
- * @param pr
391
- * @returns
779
+ * @param options Pull request update options including the commit and changes
780
+ * @returns True if successful, false on error
392
781
  */
393
- async updatePullRequest(pr) {
394
- logger.info(`Updating pull request #${pr.pullRequestId}...`);
782
+ async updatePullRequest(options) {
783
+ logger.info(`Updating pull request #${options.pullRequestId}...`);
395
784
  try {
396
- const pullRequest = await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}`)).json();
397
- if (!pullRequest) throw new Error(`Pull request #${pr.pullRequestId} not found`);
398
- if (pr.skipIfDraft && pullRequest.isDraft) {
399
- logger.info(` - Skipping update as pull request is currently marked as a draft.`);
785
+ const pullRequest = await this.inner.pullRequests.get(options.project, options.repository, options.pullRequestId);
786
+ if (!pullRequest) throw new Error(`Pull request #${options.pullRequestId} not found`);
787
+ if ((await this.inner.pullRequests.getCommits(options.project, options.repository, options.pullRequestId))?.some((c) => c.author?.email !== options.author.email)) {
788
+ logger.info(` - Skipping update as pull request has been modified by another user.`);
400
789
  return true;
401
790
  }
402
- if (pr.skipIfCommitsFromAuthorsOtherThan) {
403
- if ((await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}/commits`)).json())?.value?.some((c) => c.author?.email !== pr.skipIfCommitsFromAuthorsOtherThan)) {
404
- logger.info(` - Skipping update as pull request has been modified by another user.`);
405
- return true;
406
- }
407
- }
408
- const stats = await this.client.get(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/stats/branches`, { name: normalizeBranchName(pullRequest.sourceRefName) })).json();
791
+ const stats = await this.inner.repositories.getBranchStats(options.project, options.repository, normalizeBranchName(pullRequest.sourceRefName));
409
792
  if (stats?.behindCount === void 0) throw new Error(`Failed to get branch stats for '${pullRequest.sourceRefName}'`);
410
- if (pr.skipIfNotBehindTargetBranch && stats.behindCount === 0) {
793
+ if (stats.behindCount === 0) {
411
794
  logger.info(` - Skipping update as source branch is not behind target branch.`);
412
795
  return true;
413
796
  }
@@ -415,22 +798,22 @@ var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
415
798
  const targetBranchName = normalizeBranchName(pullRequest.targetRefName);
416
799
  if (stats.behindCount > 0) {
417
800
  logger.info(` - Rebasing '${targetBranchName}' into '${sourceBranchName}' (${stats.behindCount} commit(s) behind)...`);
418
- if ((await this.client.post(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/refs`), { json: [{
801
+ if ((await this.inner.git.updateRef(options.project, options.repository, [{
419
802
  name: pullRequest.sourceRefName,
420
803
  oldObjectId: pullRequest.lastMergeSourceCommit.commitId,
421
- newObjectId: pr.commit
422
- }] }).json())?.value?.[0]?.success !== true) throw new Error("Failed to rebase the target branch into the source branch");
804
+ newObjectId: options.commit
805
+ }]))?.[0]?.success !== true) throw new Error("Failed to rebase the target branch into the source branch");
423
806
  }
424
- logger.info(` - Pushing ${pr.changes.length} file change(s) to branch '${pullRequest.sourceRefName}'...`);
425
- const push = await this.client.post(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pushes`), { json: {
807
+ logger.info(` - Pushing ${options.changes.length} file change(s) to branch '${pullRequest.sourceRefName}'...`);
808
+ const push = await this.inner.git.createPush(options.project, options.repository, {
426
809
  refUpdates: [{
427
810
  name: pullRequest.sourceRefName,
428
- oldObjectId: pr.commit
811
+ oldObjectId: options.commit
429
812
  }],
430
813
  commits: [{
431
814
  comment: pullRequest.mergeStatus === "conflicts" ? "Resolve merge conflicts" : `Rebase '${sourceBranchName}' onto '${targetBranchName}'`,
432
- author: pr.author,
433
- changes: pr.changes.filter((change) => change.changeType !== "none").map(({ changeType, ...change }) => {
815
+ author: options.author,
816
+ changes: options.changes.filter((change) => change.changeType !== "none").map(({ changeType, ...change }) => {
434
817
  return {
435
818
  changeType,
436
819
  item: { path: normalizeFilePath(change.path) },
@@ -441,7 +824,7 @@ var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
441
824
  };
442
825
  })
443
826
  }]
444
- } }).json();
827
+ });
445
828
  if (!push?.commits?.length) throw new Error("Failed to push changes to source branch, no commits were created");
446
829
  logger.info(` - Pushed commit: ${push.commits.map((c) => c.commitId).join(", ")}.`);
447
830
  logger.info(` - Pull request was updated successfully.`);
@@ -453,20 +836,19 @@ var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
453
836
  }
454
837
  }
455
838
  /**
456
- * Approve a pull request.
839
+ * Approve a pull request as the authenticated user.
840
+ * Sets the reviewer vote to 10 (approved).
841
+ *
457
842
  * Requires scope "Code (Write)" (vso.code_write).
458
- * @param pr
459
- * @returns
843
+ * @param options Pull request identification options
844
+ * @returns True if successful, false on error
460
845
  */
461
- async approvePullRequest(pr) {
462
- logger.info(`Approving pull request #${pr.pullRequestId}...`);
846
+ async approvePullRequest(options) {
847
+ logger.info(`Approving pull request #${options.pullRequestId}...`);
463
848
  try {
464
849
  logger.info(` - Updating reviewer vote on pull request...`);
465
850
  const userId = await this.getUserId();
466
- if ((await this.client.put(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}/reviewers/${userId}`, "7.1"), { json: {
467
- vote: 10,
468
- isReapprove: true
469
- } }).json())?.vote !== 10) throw new Error("Failed to approve pull request, vote was not recorded");
851
+ if ((await this.inner.pullRequests.approve(options.project, options.repository, options.pullRequestId, userId))?.vote !== 10) throw new Error("Failed to approve pull request, vote was not recorded");
470
852
  logger.info(` - Pull request was approved successfully.`);
471
853
  return true;
472
854
  } catch (e) {
@@ -476,37 +858,39 @@ var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
476
858
  }
477
859
  }
478
860
  /**
479
- * Abandon a pull request.
861
+ * Abandon a pull request and optionally delete its source branch.
862
+ * This method performs the following operations:
863
+ * 1. Adds an optional comment explaining the abandonment reason
864
+ * 2. Sets the pull request status to abandoned
865
+ * 3. Deletes the source branch if requested
866
+ *
480
867
  * Requires scope "Code (Write)" (vso.code_write).
481
- * @param pr
482
- * @returns
868
+ * @param options Pull request abandonment options including optional comment and branch deletion flag
869
+ * @returns True if successful, false on error
483
870
  */
484
- async abandonPullRequest(pr) {
485
- logger.info(`Abandoning pull request #${pr.pullRequestId}...`);
871
+ async abandonPullRequest(options) {
872
+ logger.info(`Abandoning pull request #${options.pullRequestId}...`);
486
873
  try {
487
874
  const userId = await this.getUserId();
488
- if (pr.comment) {
875
+ if (options.comment) {
489
876
  logger.info(` - Adding abandonment reason comment to pull request...`);
490
877
  if (!await this.addCommentThread({
491
- ...pr,
492
- content: pr.comment,
878
+ ...options,
879
+ content: options.comment,
493
880
  userId
494
881
  })) throw new Error("Failed to add comment to pull request, thread was not created");
495
882
  }
496
883
  logger.info(` - Abandoning pull request...`);
497
- const abandonedPullRequest = await this.client.patch(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/pullrequests/${pr.pullRequestId}`), { json: {
498
- status: "abandoned",
499
- closedBy: { id: userId }
500
- } }).json();
884
+ const abandonedPullRequest = await this.inner.pullRequests.abandon(options.project, options.repository, options.pullRequestId, userId);
501
885
  if (abandonedPullRequest?.status !== "abandoned") throw new Error("Failed to abandon pull request, status was not updated");
502
- if (pr.deleteSourceBranch) {
886
+ if (options.deleteSourceBranch) {
503
887
  logger.info(` - Deleting source branch...`);
504
- if ((await this.client.post(this.makeUrl(`${this.organisationApiUrl}/${pr.project}/_apis/git/repositories/${pr.repository}/refs`), { json: [{
888
+ if ((await this.inner.git.updateRef(options.project, options.repository, [{
505
889
  name: abandonedPullRequest.sourceRefName,
506
890
  oldObjectId: abandonedPullRequest.lastMergeSourceCommit.commitId,
507
891
  newObjectId: "0000000000000000000000000000000000000000",
508
892
  isLocked: false
509
- }] }).json())?.value?.[0]?.success !== true) throw new Error("Failed to delete the source branch");
893
+ }]))?.[0]?.success !== true) throw new Error("Failed to delete the source branch");
510
894
  }
511
895
  logger.info(` - Pull request was abandoned successfully.`);
512
896
  return true;
@@ -518,68 +902,149 @@ var AzureDevOpsWebApiClient = class AzureDevOpsWebApiClient {
518
902
  }
519
903
  /**
520
904
  * Add a comment thread on a pull request.
905
+ * The comment thread is created with a closed status and system comment type.
906
+ *
521
907
  * Requires scope "Code (Write)" (vso.code_write).
908
+ * @param options Comment creation options including content and optional user ID
909
+ * @returns The created thread ID, or undefined on error
522
910
  */
523
- async addCommentThread(comment) {
524
- const userId = comment.userId ?? await this.getUserId();
525
- return (await this.client.post(this.makeUrl(`${this.organisationApiUrl}/${comment.project}/_apis/git/repositories/${comment.repository}/pullrequests/${comment.pullRequestId}/threads`), { json: {
911
+ async addCommentThread(options) {
912
+ const userId = options.userId ?? await this.getUserId();
913
+ return (await this.inner.pullRequests.createCommentThread(options.project, options.repository, options.pullRequestId, {
526
914
  status: "closed",
527
915
  comments: [{
528
916
  author: { id: userId },
529
- content: comment.content,
917
+ content: options.content,
530
918
  commentType: "system"
531
919
  }]
532
- } }).json())?.id;
920
+ }))?.id;
533
921
  }
534
- makeUrl(url, params, apiVersion = AzureDevOpsWebApiClient.API_VERSION) {
535
- if (typeof params === "string") {
536
- apiVersion = params;
537
- params = {};
922
+ /**
923
+ * Create or update webhook subscriptions for Azure DevOps events.
924
+ * This sets up subscriptions for various git events (push, pull request updates, repository changes, etc.)
925
+ * and ensures they are configured to send webhooks to the specified URL.
926
+ * Existing subscriptions matching the URL will be updated, otherwise new subscriptions are created.
927
+ *
928
+ * Requires scope "Service Hooks (Read & Write)" (vso.hooks_write).
929
+ * @returns Array of subscription IDs that were created or updated
930
+ */
931
+ async createOrUpdateHookSubscriptions({ url, headers, project }) {
932
+ const subscriptionTypes = new Map([
933
+ ["git.push", "1.0"],
934
+ ["git.pullrequest.updated", "1.0"],
935
+ ["git.pullrequest.merged", "1.0"],
936
+ ["git.repo.created", "1.0-preview.1"],
937
+ ["git.repo.deleted", "1.0-preview.1"],
938
+ ["git.repo.renamed", "1.0-preview.1"],
939
+ ["git.repo.statuschanged", "1.0-preview.1"],
940
+ ["ms.vss-code.git-pullrequest-comment-event", "2.0"]
941
+ ]);
942
+ const query = this.buildSubscriptionsQuery({
943
+ url,
944
+ project
945
+ });
946
+ const subscriptions = await this.inner.subscriptions.query(query);
947
+ const ids = [];
948
+ for (const [eventType, resourceVersion] of subscriptionTypes) {
949
+ const existing = subscriptions.find((sub) => {
950
+ return sub.eventType === eventType && sub.resourceVersion === resourceVersion;
951
+ });
952
+ let subscription;
953
+ if (existing) {
954
+ existing.status = "enabled";
955
+ existing.eventType = eventType;
956
+ existing.resourceVersion = resourceVersion;
957
+ existing.publisherInputs = this.makeTfsPublisherInputs({
958
+ eventType,
959
+ project
960
+ });
961
+ existing.consumerInputs = this.makeWebhookConsumerInputs({
962
+ url,
963
+ headers
964
+ });
965
+ subscription = await this.inner.subscriptions.replace(existing.id, existing);
966
+ } else subscription = await this.inner.subscriptions.create({
967
+ status: "enabled",
968
+ eventType,
969
+ resourceVersion,
970
+ publisherId: "tfs",
971
+ publisherInputs: this.makeTfsPublisherInputs({
972
+ eventType,
973
+ project
974
+ }),
975
+ consumerId: "webHooks",
976
+ consumerActionId: "httpRequest",
977
+ consumerInputs: this.makeWebhookConsumerInputs({
978
+ url,
979
+ headers
980
+ })
981
+ });
982
+ ids.push(subscription.id);
538
983
  }
539
- return `${url}?${Object.entries({
540
- "api-version": apiVersion,
541
- ...params
542
- }).map(([key, value]) => `${key}=${value}`).join("&")}`;
984
+ for (const sub of subscriptions) if (!ids.includes(sub.id)) await this.inner.subscriptions.delete(sub.id);
543
985
  }
544
- createClient() {
545
- return ky.create({
546
- headers: {
547
- Authorization: `Basic ${Buffer.from(`:${this.accessToken}`).toString("base64")}`,
548
- Accept: "application/json"
549
- },
550
- hooks: {
551
- beforeRequest: [async (request, options) => {
552
- if (this.debug) logger.debug(`🌎 🠊 [${request.method}] ${request.url}`);
553
- }],
554
- afterResponse: [async (request, options, response) => {
555
- if (this.debug) {
556
- logger.debug(`🌎 🠈 [${response.status}] ${response.statusText}`);
557
- if (request.body) logger.debug(`REQUEST: ${JSON.stringify(request.body)}`);
558
- }
559
- }],
560
- beforeRetry: [async ({ request, options, error, retryCount }) => {
561
- if (this.debug && isHTTPError(error)) logger.debug(`⏳ Retrying failed request with status code: ${error.response.status}`);
562
- }]
563
- },
564
- retry: {
565
- limit: 3,
566
- delay: (attempt) => 3e3
567
- }
986
+ /**
987
+ * Remove all webhook subscriptions for a specific URL.
988
+ * This finds all subscriptions matching the provided URL and deletes them.
989
+ *
990
+ * Requires scope "Service Hooks (Read & Write)" (vso.hooks_write).
991
+ */
992
+ async deleteHookSubscriptions({ url, project }) {
993
+ const query = this.buildSubscriptionsQuery({
994
+ url,
995
+ project
568
996
  });
997
+ const subscriptions = await this.inner.subscriptions.query(query);
998
+ for (const sub of subscriptions) await this.inner.subscriptions.delete(sub.id);
999
+ }
1000
+ mergeCommitMessage(id, title, description) {
1001
+ return `Merged PR ${id}: ${title}\n\n${description}`.slice(0, 3500);
1002
+ }
1003
+ isGuid(guid) {
1004
+ return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(guid);
1005
+ }
1006
+ buildSubscriptionsQuery({ url, project }) {
1007
+ return {
1008
+ publisherId: "tfs",
1009
+ publisherInputFilters: [{ conditions: [{
1010
+ operator: "equals",
1011
+ inputId: "projectId",
1012
+ caseSensitive: false,
1013
+ inputValue: project
1014
+ }] }],
1015
+ consumerId: "webHooks",
1016
+ consumerActionId: "httpRequest",
1017
+ consumerInputFilters: [{ conditions: [{
1018
+ operator: "equals",
1019
+ inputId: "url",
1020
+ caseSensitive: false,
1021
+ inputValue: url
1022
+ }] }]
1023
+ };
1024
+ }
1025
+ makeTfsPublisherInputs({ eventType, project }) {
1026
+ return {
1027
+ projectId: project,
1028
+ ...eventType === "git.pullrequest.updated" && { notificationType: "StatusUpdateNotification" },
1029
+ ...eventType === "git.pullrequest.merged" && { mergeResult: "Conflicts" }
1030
+ };
1031
+ }
1032
+ makeWebhookConsumerInputs({ url, headers }) {
1033
+ return {
1034
+ url,
1035
+ acceptUntrustedCerts: "false",
1036
+ httpHeaders: Object.entries(headers).map(([key, value]) => `${key}:${value}`).join("\n"),
1037
+ messagesToSend: "none",
1038
+ detailedMessagesToSend: "none"
1039
+ };
569
1040
  }
570
1041
  };
571
- function mergeCommitMessage(id, title, description) {
572
- return `Merged PR ${id}: ${title}\n\n${description}`.slice(0, 3500);
573
- }
574
- function isGuid(guid) {
575
- return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(guid);
576
- }
577
1042
 
578
1043
  //#endregion
579
1044
  //#region src/azure/config.ts
580
1045
  /**
581
1046
  * Parse the dependabot config YAML file to specify update configuration.
582
- * The file should be located at any of `POSSIBLE_CONFIG_FILE_PATHS`.
1047
+ * The file should be located at any of `CONFIG_FILE_PATHS_AZURE`.
583
1048
  *
584
1049
  * To view YAML file format, visit
585
1050
  * https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#allow
@@ -591,7 +1056,7 @@ async function getDependabotConfig({ url, token, remote, rootDir = process.cwd()
591
1056
  let configContents;
592
1057
  if (remote) {
593
1058
  logger.debug(`Attempting to fetch configuration file via REST API ...`);
594
- for (const fp of POSSIBLE_CONFIG_FILE_PATHS) {
1059
+ for (const fp of CONFIG_FILE_PATHS_AZURE) {
595
1060
  const requestUrl = `${url.value}${url.project}/_apis/git/repositories/${url.repository}/items?path=/${fp}`;
596
1061
  logger.debug(`GET ${requestUrl}`);
597
1062
  try {
@@ -622,7 +1087,7 @@ async function getDependabotConfig({ url, token, remote, rootDir = process.cwd()
622
1087
  else throw error;
623
1088
  }
624
1089
  }
625
- } else for (const fp of POSSIBLE_CONFIG_FILE_PATHS) {
1090
+ } else for (const fp of CONFIG_FILE_PATHS_AZURE) {
626
1091
  const filePath = path.join(rootDir, fp);
627
1092
  if (existsSync(filePath)) {
628
1093
  logger.debug(`Found configuration file cloned at ${filePath}`);
@@ -631,7 +1096,7 @@ async function getDependabotConfig({ url, token, remote, rootDir = process.cwd()
631
1096
  break;
632
1097
  } else logger.trace(`No configuration file cloned at ${filePath}`);
633
1098
  }
634
- if (!configContents || !configPath || typeof configContents !== "string") throw new Error(`Configuration file not found at possible locations: ${POSSIBLE_CONFIG_FILE_PATHS.join(", ")}`);
1099
+ if (!configContents || !configPath || typeof configContents !== "string") throw new Error(`Configuration file not found at possible locations: ${CONFIG_FILE_PATHS_AZURE.join(", ")}`);
635
1100
  else logger.trace("Configuration file contents read.");
636
1101
  return await parseDependabotConfig({
637
1102
  configContents,
@@ -641,252 +1106,45 @@ async function getDependabotConfig({ url, token, remote, rootDir = process.cwd()
641
1106
  }
642
1107
 
643
1108
  //#endregion
644
- //#region src/azure/types.ts
645
- const AzdoVersionControlChangeTypeSchema = z.enum([
646
- "none",
647
- "add",
648
- "edit",
649
- "encoding",
650
- "rename",
651
- "delete",
652
- "undelete",
653
- "branch",
654
- "merge",
655
- "lock",
656
- "rollback",
657
- "sourceRename",
658
- "targetRename",
659
- "property",
660
- "all"
661
- ]);
662
- const AZDO_PULL_REQUEST_MERGE_STRATEGIES = [
663
- "noFastForward",
664
- "squash",
665
- "rebase",
666
- "rebaseMerge"
667
- ];
668
- const AzdoPullRequestMergeStrategySchema = z.enum(AZDO_PULL_REQUEST_MERGE_STRATEGIES);
669
- const AzdoCommentThreadStatusSchema = z.enum([
670
- "unknown",
671
- "active",
672
- "fixed",
673
- "wontFix",
674
- "closed",
675
- "byDesign",
676
- "pending"
677
- ]);
678
- const AzdoCommentTypeSchema = z.enum([
679
- "unknown",
680
- "text",
681
- "codeChange",
682
- "system"
683
- ]);
684
- const AzdoPullRequestAsyncStatusSchema = z.enum([
685
- "notSet",
686
- "queued",
687
- "conflicts",
688
- "succeeded",
689
- "rejectedByPolicy",
690
- "failure"
691
- ]);
692
- const AzdoPullRequestStatusSchema = z.enum([
693
- "notSet",
694
- "active",
695
- "abandoned",
696
- "completed",
697
- "all"
1109
+ //#region src/azure/events.ts
1110
+ const AzdoEventTypeSchema = z.enum([
1111
+ "git.push",
1112
+ "git.pullrequest.updated",
1113
+ "git.pullrequest.merged",
1114
+ "git.repo.created",
1115
+ "git.repo.deleted",
1116
+ "git.repo.renamed",
1117
+ "git.repo.statuschanged",
1118
+ "ms.vss-code.git-pullrequest-comment-event"
698
1119
  ]);
699
- const AzdoProjectSchema = z.object({
1120
+ const AzdoEventProjectSchema = z.object({
700
1121
  id: z.string(),
701
1122
  name: z.string(),
702
- description: z.string().optional(),
703
- url: z.string(),
704
- state: z.enum([
705
- "deleting",
706
- "new",
707
- "wellFormed",
708
- "createPending",
709
- "all",
710
- "unchanged",
711
- "deleted"
712
- ]),
713
- _links: z.object({
714
- self: z.object({ href: z.string() }),
715
- collection: z.object({ href: z.string() }),
716
- web: z.object({ href: z.string() })
717
- }).optional()
1123
+ url: z.string()
718
1124
  });
719
- const AzdoRepositorySchema = z.object({
1125
+ const AzdoEventRepositorySchema = z.object({
720
1126
  id: z.string(),
721
1127
  name: z.string(),
1128
+ project: AzdoEventProjectSchema,
722
1129
  defaultBranch: z.string().optional(),
723
- project: AzdoProjectSchema,
724
- isDisabled: z.boolean().optional(),
725
- isFork: z.boolean().optional(),
726
- url: z.string(),
727
- remoteUrl: z.string(),
728
- webUrl: z.string()
729
- });
730
- const AzdoIdentitySchema = z.object({
731
- id: z.string(),
732
- displayName: z.string(),
733
- url: z.string()
734
- });
735
- const AzdoIdentityRefSchema = z.object({
736
- id: z.string().optional(),
737
- displayName: z.string().optional(),
738
- url: z.string().optional()
739
- });
740
- const AzdoConnectionDataSchema = z.object({
741
- authenticatedUser: AzdoIdentitySchema,
742
- authorizedUser: AzdoIdentitySchema
743
- });
744
- const AzdoGitUserDateSchema = z.object({
745
- name: z.string(),
746
- email: z.string(),
747
- date: z.string().optional()
748
- });
749
- const AzdoGitRefSchema = z.object({
750
- name: z.string(),
751
- objectId: z.string(),
752
- isLocked: z.boolean().optional()
753
- });
754
- const AzdoGitRefUpdateResultSchema = AzdoGitRefSchema.extend({
755
- oldObjectId: z.string(),
756
- newObjectId: z.string(),
757
- success: z.boolean(),
758
- customMessage: z.string().optional()
759
- });
760
- const AzdoGitChangeSchema = z.object({
761
- changeType: AzdoVersionControlChangeTypeSchema,
762
- newContent: z.object({
763
- content: z.string(),
764
- contentType: z.enum(["rawtext", "base64encoded"])
765
- }).optional()
766
- });
767
- const AzdoGitCommitRefSchema = z.object({
768
- commitId: z.string().optional(),
769
- author: AzdoGitUserDateSchema.optional(),
770
- committer: AzdoGitUserDateSchema.optional(),
771
- changes: AzdoGitChangeSchema.array()
1130
+ remoteUrl: z.string()
772
1131
  });
773
- const AzdoGitPushSchema = z.object({
1132
+ const AzdoEventCodePushResourceSchema = z.object({
1133
+ repository: AzdoEventRepositorySchema,
774
1134
  commits: AzdoGitCommitRefSchema.array(),
775
- refUpdates: AzdoGitRefSchema.array()
776
- });
777
- const AzdoGitPushCreateSchema = z.object({
778
1135
  refUpdates: z.object({
779
1136
  name: z.string(),
780
- oldObjectId: z.string(),
781
- newObjectId: z.string().optional()
1137
+ oldObjectId: z.string().nullish(),
1138
+ newObjectId: z.string().nullish()
782
1139
  }).array(),
783
- commits: z.object({
784
- comment: z.string(),
785
- author: AzdoGitUserDateSchema.optional(),
786
- changes: AzdoGitChangeSchema.extend({ item: z.object({ path: z.string() }) }).array()
787
- }).array()
788
- });
789
- const AzdoGitBranchStatsSchema = z.object({
790
- aheadCount: z.number(),
791
- behindCount: z.number()
792
- });
793
- const AzdoRepositoryItemSchema = z.object({
794
- latestProcessedChange: AzdoGitCommitRefSchema.optional(),
795
- content: z.string().optional()
796
- });
797
- const AzdoPullRequestSchema = z.object({
798
- pullRequestId: z.number(),
799
- status: AzdoPullRequestStatusSchema,
800
- isDraft: z.boolean(),
801
- sourceRefName: z.string(),
802
- targetRefName: z.string(),
803
- lastMergeCommit: AzdoGitCommitRefSchema,
804
- lastMergeSourceCommit: AzdoGitCommitRefSchema,
805
- mergeStatus: AzdoPullRequestAsyncStatusSchema,
806
- autoCompleteSetBy: AzdoIdentityRefSchema.optional(),
807
- closedBy: AzdoIdentityRefSchema.optional()
808
- });
809
- const AdoPropertiesSchema = z.record(z.string(), z.object({
810
- $type: z.string(),
811
- $value: z.string()
812
- }));
813
- const AzdoIdentityRefWithVoteSchema = z.object({
814
- id: z.string().optional(),
815
- displayName: z.string().optional(),
816
- vote: z.number().optional(),
817
- hasDeclined: z.boolean().optional(),
818
- isFlagged: z.boolean().optional(),
819
- isRequired: z.boolean().optional()
820
- });
821
- const AzdoPullRequestCommentSchema = z.object({
822
- id: z.number(),
823
- parentCommentId: z.number().nullable(),
824
- content: z.string(),
825
- commentType: AzdoCommentTypeSchema,
826
- publishedDate: z.string(),
827
- author: AzdoIdentityRefSchema.optional()
828
- });
829
- const AzdoPullRequestCommentThreadSchema = z.object({
830
- id: z.number(),
831
- comments: AzdoPullRequestCommentSchema.array(),
832
- status: AzdoCommentThreadStatusSchema
833
- });
834
- const AzdoSubscriptionSchema = z.object({
835
- id: z.string().optional(),
836
- status: z.enum([
837
- "enabled",
838
- "onProbation",
839
- "disabledByUser",
840
- "disabledBySystem",
841
- "disabledByInactiveIdentity"
842
- ]),
843
- publisherId: z.string(),
844
- publisherInputs: z.record(z.string(), z.string()),
845
- consumerId: z.string().optional(),
846
- consumerActionId: z.string().optional(),
847
- consumerInputs: z.record(z.string(), z.string()),
848
- eventType: z.string(),
849
- resourceVersion: z.string(),
850
- eventDescription: z.string().optional(),
851
- actionDescription: z.string().optional()
852
- });
853
- const AzdoSubscriptionsQueryResponseSchema = z.object({ results: AzdoSubscriptionSchema.array() });
854
- const AzdoSubscriptionsQueryInputFilterSchema = z.object({ conditions: z.object({
855
- caseSensitive: z.boolean().optional(),
856
- inputId: z.string().optional(),
857
- inputValue: z.string().optional(),
858
- operator: z.enum(["equals", "notEquals"])
859
- }).array().optional() });
860
- const AzdoSubscriptionsQuerySchema = z.object({
861
- consumerActionId: z.string().optional(),
862
- consumerId: z.string().optional(),
863
- consumerInputFilters: AzdoSubscriptionsQueryInputFilterSchema.array().optional(),
864
- eventType: z.string().optional(),
865
- publisherId: z.string().optional(),
866
- publisherInputFilters: AzdoSubscriptionsQueryInputFilterSchema.array().optional(),
867
- subscriberId: z.string().optional()
868
- });
869
- const AzdoEventTypeSchema = z.enum([
870
- "git.push",
871
- "git.pullrequest.updated",
872
- "git.pullrequest.merged",
873
- "ms.vss-code.git-pullrequest-comment-event"
874
- ]);
875
- const AzdoEventRepositorySchema = z.object({
876
- id: z.string(),
877
- name: z.string(),
878
- project: z.object({
879
- id: z.string(),
880
- name: z.string(),
881
- url: z.string()
882
- }),
883
- defaultBranch: z.string().optional(),
884
- remoteUrl: z.string()
1140
+ pushId: z.number(),
1141
+ url: z.string()
885
1142
  });
886
1143
  const AzdoEventPullRequestResourceSchema = z.object({
887
1144
  repository: AzdoEventRepositorySchema,
888
1145
  pullRequestId: z.number(),
889
1146
  status: AzdoPullRequestStatusSchema,
1147
+ createdBy: AzdoIdentityRefSchema,
890
1148
  title: z.string(),
891
1149
  sourceRefName: z.string(),
892
1150
  targetRefName: z.string(),
@@ -894,21 +1152,37 @@ const AzdoEventPullRequestResourceSchema = z.object({
894
1152
  mergeId: z.string(),
895
1153
  url: z.string()
896
1154
  });
1155
+ const AzdoEventRepositoryCreatedResourceSchema = z.object({ repository: AzdoEventRepositorySchema });
1156
+ const AzdoEventRepositoryDeletedResourceSchema = z.object({
1157
+ project: AzdoEventProjectSchema,
1158
+ repositoryId: z.string(),
1159
+ repositoryName: z.string(),
1160
+ isHardDelete: z.boolean()
1161
+ });
1162
+ const AzdoEventRepositoryRenamedResourceSchema = z.object({
1163
+ oldName: z.string(),
1164
+ newName: z.string(),
1165
+ repository: AzdoEventRepositorySchema
1166
+ });
1167
+ const AzdoEventRepositoryStatusChangedResourceSchema = z.object({
1168
+ disabled: z.boolean(),
1169
+ repository: AzdoEventRepositorySchema
1170
+ });
897
1171
  const AzdoEventPullRequestCommentEventResourceSchema = z.object({
898
1172
  pullRequest: AzdoEventPullRequestResourceSchema,
899
1173
  comment: AzdoPullRequestCommentSchema
900
1174
  });
901
- const AzdoEventCodePushResourceSchema = z.object({
902
- repository: AzdoEventRepositorySchema,
903
- refUpdates: z.object({
904
- name: z.string(),
905
- oldObjectId: z.string().nullish(),
906
- newObjectId: z.string().nullish()
907
- }).array()
908
- });
909
1175
  const AzdoEventSchema = z.object({
910
1176
  subscriptionId: z.string(),
911
- notificationId: z.number()
1177
+ notificationId: z.number(),
1178
+ id: z.string(),
1179
+ publisherId: z.string(),
1180
+ resourceVersion: z.enum([
1181
+ "1.0",
1182
+ "1.0-preview.1",
1183
+ "2.0"
1184
+ ]),
1185
+ createdDate: z.coerce.date()
912
1186
  }).and(z.discriminatedUnion("eventType", [
913
1187
  z.object({
914
1188
  eventType: z.literal("git.push"),
@@ -922,6 +1196,22 @@ const AzdoEventSchema = z.object({
922
1196
  eventType: z.literal("git.pullrequest.merged"),
923
1197
  resource: AzdoEventPullRequestResourceSchema
924
1198
  }),
1199
+ z.object({
1200
+ eventType: z.literal("git.repo.created"),
1201
+ resource: AzdoEventRepositoryCreatedResourceSchema
1202
+ }),
1203
+ z.object({
1204
+ eventType: z.literal("git.repo.deleted"),
1205
+ resource: AzdoEventRepositoryDeletedResourceSchema
1206
+ }),
1207
+ z.object({
1208
+ eventType: z.literal("git.repo.renamed"),
1209
+ resource: AzdoEventRepositoryRenamedResourceSchema
1210
+ }),
1211
+ z.object({
1212
+ eventType: z.literal("git.repo.statuschanged"),
1213
+ resource: AzdoEventRepositoryStatusChangedResourceSchema
1214
+ }),
925
1215
  z.object({
926
1216
  eventType: z.literal("ms.vss-code.git-pullrequest-comment-event"),
927
1217
  resource: AzdoEventPullRequestCommentEventResourceSchema
@@ -950,10 +1240,10 @@ function extractOrganizationUrl({ organisationUrl }) {
950
1240
  }
951
1241
  function extractProjectUrl({ organisationUrl, project }) {
952
1242
  const extracted = extractOrganizationUrl({ organisationUrl });
953
- const escapedProject = encodeURI(project);
1243
+ const decodedProject = decodeURIComponent(project);
954
1244
  return {
955
1245
  ...extracted,
956
- project: escapedProject
1246
+ project: decodedProject
957
1247
  };
958
1248
  }
959
1249
  function extractRepositoryUrl({ organisationUrl, project, repository }) {
@@ -961,12 +1251,12 @@ function extractRepositoryUrl({ organisationUrl, project, repository }) {
961
1251
  organisationUrl,
962
1252
  project
963
1253
  });
964
- const { organisation, "virtual-directory": virtualDirectory, project: escapedProject } = extracted;
965
- const escapedRepository = encodeURI(repository);
966
- const repoSlug = `${virtualDirectory ? `${virtualDirectory}/` : ""}${organisation}/${escapedProject}/_git/${escapedRepository}`;
1254
+ const { organisation, "virtual-directory": virtualDirectory, project: decodedProject } = extracted;
1255
+ const decodedRepository = decodeURIComponent(repository);
1256
+ const repoSlug = `${virtualDirectory ? `${virtualDirectory}/` : ""}${organisation}/${encodeURI(decodedProject)}/_git/${encodeURI(decodedRepository)}`;
967
1257
  return {
968
1258
  ...extracted,
969
- repository: escapedRepository,
1259
+ repository: decodedRepository,
970
1260
  "repository-slug": repoSlug
971
1261
  };
972
1262
  }
@@ -1001,5 +1291,5 @@ function extractVirtualDirectory(organisationUrl) {
1001
1291
  }
1002
1292
 
1003
1293
  //#endregion
1004
- export { ANONYMOUS_USER_ID, AZDO_PULL_REQUEST_MERGE_STRATEGIES, AdoPropertiesSchema, AzdoCommentThreadStatusSchema, AzdoCommentTypeSchema, AzdoConnectionDataSchema, AzdoEventCodePushResourceSchema, AzdoEventPullRequestCommentEventResourceSchema, AzdoEventPullRequestResourceSchema, AzdoEventRepositorySchema, AzdoEventSchema, AzdoEventTypeSchema, AzdoGitBranchStatsSchema, AzdoGitChangeSchema, AzdoGitCommitRefSchema, AzdoGitPushCreateSchema, AzdoGitPushSchema, AzdoGitRefSchema, AzdoGitRefUpdateResultSchema, AzdoGitUserDateSchema, AzdoIdentityRefSchema, AzdoIdentityRefWithVoteSchema, AzdoIdentitySchema, AzdoProjectSchema, AzdoPullRequestAsyncStatusSchema, AzdoPullRequestCommentSchema, AzdoPullRequestCommentThreadSchema, AzdoPullRequestMergeStrategySchema, AzdoPullRequestSchema, AzdoPullRequestStatusSchema, AzdoRepositoryItemSchema, AzdoRepositorySchema, AzdoSubscriptionSchema, AzdoSubscriptionsQueryInputFilterSchema, AzdoSubscriptionsQueryResponseSchema, AzdoSubscriptionsQuerySchema, AzdoVersionControlChangeTypeSchema, AzureDevOpsWebApiClient, DEVOPS_PR_PROPERTY_DEPENDABOT_DEPENDENCIES, DEVOPS_PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER, DEVOPS_PR_PROPERTY_MICROSOFT_GIT_SOURCE_REF_NAME, DependenciesPrPropertySchema, buildPullRequestProperties, extractOrganizationUrl, extractProjectUrl, extractRepositoryUrl, getDependabotConfig, getPullRequestChangedFilesForOutputData, getPullRequestCloseReasonForOutputData, getPullRequestDependenciesPropertyValueForOutputData, getPullRequestDescription, getPullRequestForDependencyNames, normalizeBranchName, normalizeFilePath, parsePullRequestProperties };
1294
+ export { ANONYMOUS_USER_ID, API_VERSION, API_VERSION_PREVIEW, AZDO_PULL_REQUEST_MERGE_STRATEGIES, AzdoCommentThreadStatusSchema, AzdoCommentTypeSchema, AzdoConnectionDataSchema, AzdoEventCodePushResourceSchema, AzdoEventPullRequestCommentEventResourceSchema, AzdoEventPullRequestResourceSchema, AzdoEventRepositoryCreatedResourceSchema, AzdoEventRepositoryDeletedResourceSchema, AzdoEventRepositoryRenamedResourceSchema, AzdoEventRepositorySchema, AzdoEventRepositoryStatusChangedResourceSchema, AzdoEventSchema, AzdoEventTypeSchema, AzdoGitBranchStatsSchema, AzdoGitChangeSchema, AzdoGitCommitDiffsSchema, AzdoGitCommitRefSchema, AzdoGitPushCreateSchema, AzdoGitPushSchema, AzdoGitRefSchema, AzdoGitRefUpdateResultSchema, AzdoGitRefUpdateSchema, AzdoGitUserDateSchema, AzdoIdentityRefSchema, AzdoIdentityRefWithVoteSchema, AzdoIdentitySchema, AzdoProjectSchema, AzdoPropertiesSchema, AzdoPullRequestAsyncStatusSchema, AzdoPullRequestCommentSchema, AzdoPullRequestCommentThreadSchema, AzdoPullRequestMergeStrategySchema, AzdoPullRequestSchema, AzdoPullRequestStatusSchema, AzdoRepositoryItemSchema, AzdoRepositorySchema, AzdoSubscriptionSchema, AzdoSubscriptionsQueryInputFilterSchema, AzdoSubscriptionsQueryResponseSchema, AzdoSubscriptionsQuerySchema, AzdoVersionControlChangeTypeSchema, AzureDevOpsClient, AzureDevOpsClientWrapper, PR_DESCRIPTION_MAX_LENGTH, PR_PROPERTY_DEPENDABOT_DEPENDENCIES, PR_PROPERTY_DEPENDABOT_PACKAGE_MANAGER, PR_PROPERTY_MICROSOFT_GIT_SOURCE_REF_NAME, buildPullRequestProperties, extractOrganizationUrl, extractProjectUrl, extractRepositoryUrl, getDependabotConfig, getPullRequestChangedFiles, getPullRequestForDependencyNames, parsePullRequestProperties };
1005
1295
  //# sourceMappingURL=index.mjs.map