@gitkraken/core-gitlens 0.5.102 → 0.5.104
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.
- package/CHANGELOG.md +26 -1
- package/dist/git/models/issue.d.ts +78 -0
- package/dist/git/models/issue.d.ts.map +1 -1
- package/dist/git/models/issue.js.map +1 -1
- package/dist/plus/git-github/api/config.d.ts +12 -0
- package/dist/plus/git-github/api/config.d.ts.map +1 -1
- package/dist/plus/git-github/api/config.js +12 -1
- package/dist/plus/git-github/api/config.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts +77 -7
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +241 -120
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/api/issueSearchQuery.d.ts +70 -0
- package/dist/plus/git-github/api/issueSearchQuery.d.ts.map +1 -0
- package/dist/plus/git-github/api/issueSearchQuery.js +143 -0
- package/dist/plus/git-github/api/issueSearchQuery.js.map +1 -0
- package/dist/plus/integrations/collectionMetadata.d.ts +15 -1
- package/dist/plus/integrations/collectionMetadata.d.ts.map +1 -1
- package/dist/plus/integrations/collectionMetadata.js +21 -2
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +2 -0
- package/dist/plus/integrations/index.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +39 -2
- package/dist/plus/integrations/integrationService.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.js +17 -0
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +88 -2
- package/dist/plus/integrations/manager.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +61 -3
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +51 -0
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +3 -46
- package/dist/plus/integrations/models/integration.d.ts.map +1 -1
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +70 -0
- package/dist/plus/integrations/models/issueReads.d.ts.map +1 -0
- package/dist/plus/integrations/models/issueReads.js +2 -0
- package/dist/plus/integrations/models/issueReads.js.map +1 -0
- package/dist/plus/integrations/providerFilters.d.ts +10 -0
- package/dist/plus/integrations/providerFilters.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +26 -3
- package/dist/plus/integrations/providers/github.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.js +34 -0
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +22 -0
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js +26 -0
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/reads/broaden.d.ts +10 -0
- package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/counts.d.ts +64 -0
- package/dist/plus/integrations/reads/counts.d.ts.map +1 -0
- package/dist/plus/integrations/reads/counts.js +164 -0
- package/dist/plus/integrations/reads/counts.js.map +1 -0
- package/dist/plus/integrations/reads/filters.d.ts +63 -1
- package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
- package/dist/plus/integrations/reads/filters.js +125 -0
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.d.ts.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +34 -17
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issues.js +22 -41
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/paging.d.ts +59 -0
- package/dist/plus/integrations/reads/paging.d.ts.map +1 -1
- package/dist/plus/integrations/reads/paging.js +60 -0
- package/dist/plus/integrations/reads/paging.js.map +1 -1
- package/dist/plus/integrations/reads/searchIssues.d.ts +55 -0
- package/dist/plus/integrations/reads/searchIssues.d.ts.map +1 -0
- package/dist/plus/integrations/reads/searchIssues.js +144 -0
- package/dist/plus/integrations/reads/searchIssues.js.map +1 -0
- package/dist/plus/integrations/reads/warnings.d.ts +33 -3
- package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +87 -6
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/docs/integrations.md +110 -2
- package/docs/kepler-read-api-parity.md +57 -0
- package/package.json +1 -1
- package/src/git/models/issue.ts +81 -0
- package/src/plus/git-github/api/config.ts +13 -0
- package/src/plus/git-github/api/github.ts +332 -148
- package/src/plus/git-github/api/issueSearchQuery.ts +165 -0
- package/src/plus/integrations/collectionMetadata.ts +22 -3
- package/src/plus/integrations/index.ts +5 -0
- package/src/plus/integrations/integrationService.ts +47 -1
- package/src/plus/integrations/manager.ts +88 -2
- package/src/plus/integrations/models/gitHostIntegration.ts +102 -2
- package/src/plus/integrations/models/integration.ts +7 -46
- package/src/plus/integrations/models/issueReads.ts +73 -0
- package/src/plus/integrations/providerFilters.ts +15 -0
- package/src/plus/integrations/providers/github.ts +64 -2
- package/src/plus/integrations/providers/models.ts +49 -0
- package/src/plus/integrations/reads/broaden.ts +10 -0
- package/src/plus/integrations/reads/counts.ts +285 -0
- package/src/plus/integrations/reads/filters.ts +162 -1
- package/src/plus/integrations/reads/hierarchy.ts +43 -32
- package/src/plus/integrations/reads/issues.ts +22 -45
- package/src/plus/integrations/reads/paging.ts +102 -0
- package/src/plus/integrations/reads/searchIssues.ts +252 -0
- package/src/plus/integrations/reads/warnings.ts +129 -11
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { IssueSearchCriteria, IssueSearchRelationship } from '../../../git/models/issue.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Translation of the provider-neutral issue-search criteria into GitHub search qualifiers, and the sanitizing
|
|
5
|
+
* that keeps user input from becoming one.
|
|
6
|
+
*
|
|
7
|
+
* A module of its own because it is pure string building with no client, no token and no network — the part of the
|
|
8
|
+
* search that is worth reading, testing and reasoning about on its own, and the part where a mistake is a
|
|
9
|
+
* SECURITY mistake rather than a failed request: an unsanitized value that escapes its qualifier re-scopes
|
|
10
|
+
* someone's search. Keeping it out of the 4000-line API client is what makes that reviewable.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Neutralizes everything in a user-supplied value that could break out of the qualifier it lands in: the double
|
|
15
|
+
* quote that would close its own `"…"`, and the control characters that cannot appear in a GitHub search query
|
|
16
|
+
* and exist here only as a smuggling vector.
|
|
17
|
+
*
|
|
18
|
+
* Not a general escape — GitHub search has no escape syntax — so the only safe transformation is removal. But a
|
|
19
|
+
* control character is REPLACED WITH A SPACE rather than deleted, because deleting it FUSES the words it separated:
|
|
20
|
+
* a pasted or multi-line `graph\nperformance` became the single token `graphperformance`, which matches nothing —
|
|
21
|
+
* turning a search that had results into a silent zero (measured: 2 results vs 0). Quotes are still deleted
|
|
22
|
+
* outright; they separate nothing, so a space there would only pad the value.
|
|
23
|
+
*
|
|
24
|
+
* Runs of whitespace are collapsed afterwards so the emitted qualifier stays canonical — a doubled space inside
|
|
25
|
+
* `label:"a b"` would not match the label `a b`.
|
|
26
|
+
*/
|
|
27
|
+
export function sanitizeGitHubQualifierValue(value: string): string {
|
|
28
|
+
return (
|
|
29
|
+
value
|
|
30
|
+
.replace(/"/g, '')
|
|
31
|
+
// eslint-disable-next-line no-control-regex
|
|
32
|
+
.replace(/[\u0000-\u001f\u007f]/g, ' ')
|
|
33
|
+
.replace(/\s+/g, ' ')
|
|
34
|
+
.trim()
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sanitizes free text, which is a different problem from a qualifier value: bare words are the POINT here, so it
|
|
40
|
+
* can't just be quoted. What must not survive is anything that would read as a qualifier — GitHub parses any
|
|
41
|
+
* whitespace-delimited `key:value` token as one, so a user typing `foo org:someone-else` would otherwise re-scope
|
|
42
|
+
* the search to another org.
|
|
43
|
+
*
|
|
44
|
+
* Tokens containing `:` are dropped whole rather than having the colon removed: the remainder would silently
|
|
45
|
+
* change what the user searched for, and a dropped token at least matches nothing extra. The structured criteria
|
|
46
|
+
* are the qualifier channel.
|
|
47
|
+
*/
|
|
48
|
+
export function sanitizeGitHubSearchText(text: string): string {
|
|
49
|
+
return sanitizeGitHubQualifierValue(text)
|
|
50
|
+
.split(/\s+/)
|
|
51
|
+
.filter(token => token.length > 0 && !token.includes(':'))
|
|
52
|
+
.join(' ');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* How each relationship is expressed: its GitHub search qualifier (`@me` binds to the token's own user), and the
|
|
57
|
+
* GraphQL alias its search runs under.
|
|
58
|
+
*
|
|
59
|
+
* The aliases are LITERALS rather than derived from the relationship name, for two reasons: `any-assignee` is not
|
|
60
|
+
* a valid GraphQL name, and an alias is also a persisted cursor key (see {@link GitHubApi.searchIssuesByAlias}),
|
|
61
|
+
* so it must be stable across releases — spelling them out makes that immovability visible instead of an
|
|
62
|
+
* emergent property of a transform. Being a `Record` over the union, the type fails the build if a relationship
|
|
63
|
+
* is added without both, rather than silently emitting an unconstrained search.
|
|
64
|
+
*/
|
|
65
|
+
export const gitHubIssueSearchRelationships: Record<IssueSearchRelationship, { qualifier: string; alias: string }> = {
|
|
66
|
+
authored: { qualifier: 'author:@me', alias: 'authored' },
|
|
67
|
+
assigned: { qualifier: 'assignee:@me', alias: 'assigned' },
|
|
68
|
+
mentioned: { qualifier: 'mentions:@me', alias: 'mentioned' },
|
|
69
|
+
'any-assignee': { qualifier: 'assignee:*', alias: 'anyAssignee' },
|
|
70
|
+
unassigned: { qualifier: 'no:assignee', alias: 'unassigned' },
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The scope half of an issue search query: `org:` plus one `repo:` per repository.
|
|
75
|
+
*
|
|
76
|
+
* Shared by the search and the count probe rather than written twice, because the count is only meaningful if it
|
|
77
|
+
* applies EXACTLY the qualifiers the search would — a count under different constraints is a wrong number, not a
|
|
78
|
+
* missing one. A value emptied by sanitizing is dropped rather than emitted as a bare `org:`/`repo:`, which
|
|
79
|
+
* GitHub rejects; that is the same rule {@link toGitHubIssueSearchQualifiers} applies to its own values.
|
|
80
|
+
*/
|
|
81
|
+
export function toGitHubIssueSearchScopeQualifiers(
|
|
82
|
+
org: string | undefined,
|
|
83
|
+
repos: readonly string[] | undefined,
|
|
84
|
+
): string[] {
|
|
85
|
+
const qualifiers: string[] = [];
|
|
86
|
+
|
|
87
|
+
if (org != null) {
|
|
88
|
+
const value = sanitizeGitHubQualifierValue(org);
|
|
89
|
+
if (value.length > 0) {
|
|
90
|
+
qualifiers.push(`org:${value}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const repo of repos ?? []) {
|
|
95
|
+
const value = sanitizeGitHubQualifierValue(repo);
|
|
96
|
+
if (value.length > 0) {
|
|
97
|
+
qualifiers.push(`repo:${value}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return qualifiers;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Translates the provider-neutral criteria into GitHub search qualifiers, EXCLUDING the relationship (which
|
|
106
|
+
* becomes its own aliased search, since GitHub AND-s qualifiers and relationships are OR-ed) and excluding the
|
|
107
|
+
* repository/org scope (which {@link toGitHubIssueSearchScopeQualifiers} owns).
|
|
108
|
+
*
|
|
109
|
+
* `sort:updated` is always emitted: the read's contract is most-recently-updated-first, so a consumer's "show the
|
|
110
|
+
* N most recent" policy at the result ceiling is correct. Free-form values go through the sanitizers above, and
|
|
111
|
+
* one emptied by sanitizing is dropped rather than emitted as an empty qualifier.
|
|
112
|
+
*/
|
|
113
|
+
export function toGitHubIssueSearchQualifiers(criteria: IssueSearchCriteria | undefined): string[] {
|
|
114
|
+
const qualifiers = ['type:issue'];
|
|
115
|
+
|
|
116
|
+
switch (criteria?.state) {
|
|
117
|
+
case 'closed':
|
|
118
|
+
qualifiers.push('is:closed');
|
|
119
|
+
break;
|
|
120
|
+
// Every state — no qualifier constrains it.
|
|
121
|
+
case 'all':
|
|
122
|
+
break;
|
|
123
|
+
default:
|
|
124
|
+
qualifiers.push('is:open');
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (criteria?.includeArchived !== true) {
|
|
129
|
+
qualifiers.push('archived:false');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// One place to keep the "a value emptied by sanitizing is DROPPED, never emitted as an empty qualifier" rule,
|
|
133
|
+
// so a criterion added later can't skip it — a bare `milestone:""` is rejected by GitHub outright.
|
|
134
|
+
const pushSanitized = (value: string | undefined, toQualifier: (sanitized: string) => string): void => {
|
|
135
|
+
if (value == null) return;
|
|
136
|
+
|
|
137
|
+
const sanitized = sanitizeGitHubQualifierValue(value);
|
|
138
|
+
if (sanitized.length > 0) {
|
|
139
|
+
qualifiers.push(toQualifier(sanitized));
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
for (const label of criteria?.labels ?? []) {
|
|
144
|
+
pushSanitized(label, v => `label:"${v}"`);
|
|
145
|
+
}
|
|
146
|
+
pushSanitized(criteria?.milestone, v => `milestone:"${v}"`);
|
|
147
|
+
pushSanitized(criteria?.updatedAfter, v => `updated:>=${v}`);
|
|
148
|
+
pushSanitized(criteria?.createdAfter, v => `created:>=${v}`);
|
|
149
|
+
|
|
150
|
+
if (criteria?.withoutLinkedPullRequest === true) {
|
|
151
|
+
qualifiers.push('-linked:pr');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (criteria?.text != null) {
|
|
155
|
+
const text = sanitizeGitHubSearchText(criteria.text);
|
|
156
|
+
if (text.length > 0) {
|
|
157
|
+
qualifiers.push(text);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Contract, not an option: see `GitHubApi.searchIssuesPage`.
|
|
162
|
+
qualifiers.push('sort:updated');
|
|
163
|
+
|
|
164
|
+
return qualifiers;
|
|
165
|
+
}
|
|
@@ -244,17 +244,36 @@ export function assessCollectionMetadata(
|
|
|
244
244
|
return { warnings: warnings, fetchFailed: failures.length > 0, truncated: incomplete };
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
/**
|
|
247
|
+
/**
|
|
248
|
+
* Appends neutral warnings derived from SDK metadata and returns its failure/truncation assessment.
|
|
249
|
+
*
|
|
250
|
+
* `reported` says whether the metadata described this read's incompleteness at all. A caller that would
|
|
251
|
+
* otherwise add its own generic "this was truncated" warning uses it to stay quiet — the same rule
|
|
252
|
+
* {@link assessCollectionMetadata} applies to its own generic fallback, one layer up: whatever the metadata
|
|
253
|
+
* reported already names this provider and, unless it was that same fallback, says something a consumer can
|
|
254
|
+
* act on. Restating it in a weaker vocabulary is how one gap becomes two warnings with two different verdicts.
|
|
255
|
+
*
|
|
256
|
+
* `truncated` is the near-synonym that must NOT be used for that question: it comes from
|
|
257
|
+
* {@link isIncompleteCollection}, which does not consider failures, so metadata carrying a typed failure
|
|
258
|
+
* alongside `completeness: 'complete'` reports `truncated: false` while having already appended the warning
|
|
259
|
+
* that explains everything. Gating on it would restate exactly the case this field exists to keep quiet.
|
|
260
|
+
*/
|
|
248
261
|
export function mergeAssessmentInto(
|
|
249
262
|
warnings: ProviderWarning[],
|
|
250
263
|
providerId: IntegrationIds,
|
|
251
264
|
domain: string | undefined,
|
|
252
265
|
connectionId: string | undefined,
|
|
253
266
|
metadata: CollectionMetadata | undefined,
|
|
254
|
-
): { fetchFailed: boolean; truncated: boolean } {
|
|
267
|
+
): { fetchFailed: boolean; truncated: boolean; reported: boolean } {
|
|
255
268
|
const assessment = assessCollectionMetadata(providerId, domain, connectionId, metadata);
|
|
256
269
|
for (const warning of assessment.warnings) {
|
|
257
270
|
appendDedupedWarning(warnings, warning);
|
|
258
271
|
}
|
|
259
|
-
return {
|
|
272
|
+
return {
|
|
273
|
+
fetchFailed: assessment.fetchFailed,
|
|
274
|
+
truncated: assessment.truncated,
|
|
275
|
+
// What the assessment PRODUCED, not what `appendDedupedWarning` accepted: a warning dropped as a
|
|
276
|
+
// duplicate is still one the consumer will see, so it still makes a restatement redundant.
|
|
277
|
+
reported: assessment.warnings.length > 0,
|
|
278
|
+
};
|
|
260
279
|
}
|
|
@@ -215,6 +215,11 @@ export type {
|
|
|
215
215
|
} from './results.js';
|
|
216
216
|
// Runtime enums — re-exported as values (not `export type`) so consumers can read their members.
|
|
217
217
|
export { IssueFilter, PullRequestFilter } from './providerFilters.js';
|
|
218
|
+
// The filtered issue search's criteria model and the capability table that says which of it a provider honors.
|
|
219
|
+
// String unions and plain interfaces, so a consumer builds criteria without importing an internal subpath.
|
|
220
|
+
export type { IssueSearchCapabilities, IssueSearchCriteria, IssueSearchRelationship } from './providerFilters.js';
|
|
221
|
+
// The count-only issue probe's input and result shapes.
|
|
222
|
+
export type { IssueCountResult, IssueCountScope } from './reads/counts.js';
|
|
218
223
|
// Cross-provider PR/issue state filters (string unions in the git models).
|
|
219
224
|
export type { PullRequestStateFilter } from '../../git/models/pullRequest.js';
|
|
220
225
|
export type { IssueStateFilter } from '../../git/models/issue.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Account } from '../../git/models/author.js';
|
|
2
|
-
import type { IssueShape } from '../../git/models/issue.js';
|
|
2
|
+
import type { IssueSearchCriteria, IssueShape } from '../../git/models/issue.js';
|
|
3
3
|
import type { PullRequest, PullRequestShape, PullRequestStateFilter } from '../../git/models/pullRequest.js';
|
|
4
4
|
import type { GitRemote } from '../../git/models/remote.js';
|
|
5
5
|
import type { RemoteProviderId } from '../../git/models/remoteProvider.js';
|
|
@@ -62,6 +62,7 @@ import { createApiClients } from './providers/apiClients.js';
|
|
|
62
62
|
import type { GitHubApi } from './providers/github/github.js';
|
|
63
63
|
import type {
|
|
64
64
|
IssueFilter,
|
|
65
|
+
IssueSearchCapabilities,
|
|
65
66
|
ProviderOrganization,
|
|
66
67
|
ProviderReposInput,
|
|
67
68
|
ProviderRepositoryShape,
|
|
@@ -71,12 +72,15 @@ import { providersMetadata } from './providers/models.js';
|
|
|
71
72
|
import type { ProvidersApi } from './providers/providersApi.js';
|
|
72
73
|
import { broadenIssues } from './reads/broaden.js';
|
|
73
74
|
import type { RepositoryResolutionContext } from './reads/context.js';
|
|
75
|
+
import type { IssueCountResult, IssueCountScope } from './reads/counts.js';
|
|
76
|
+
import { countIssues } from './reads/counts.js';
|
|
74
77
|
import { getSupportedFilters } from './reads/filters.js';
|
|
75
78
|
import { listOrgs, listProjects, listRepos } from './reads/hierarchy.js';
|
|
76
79
|
import { listIssuesPage } from './reads/issues.js';
|
|
77
80
|
import { listIssueTrackerIssuesPage } from './reads/issueTracker.js';
|
|
78
81
|
import { listPullRequestsPage } from './reads/pullRequests.js';
|
|
79
82
|
import { resolveRepository } from './reads/resolveRepository.js';
|
|
83
|
+
import { searchIssuesPage } from './reads/searchIssues.js';
|
|
80
84
|
import { sweepClosedPullRequests, sweepPullRequests } from './reads/sweeps.js';
|
|
81
85
|
import { noConnectionWarning } from './reads/warnings.js';
|
|
82
86
|
import type {
|
|
@@ -531,6 +535,7 @@ export class IntegrationService implements Disposable, RepositoryResolutionConte
|
|
|
531
535
|
pullRequestsAccountWide: PullRequestFilter[];
|
|
532
536
|
issues: IssueFilter[];
|
|
533
537
|
issuesAccountWide: IssueFilter[];
|
|
538
|
+
issueSearch: IssueSearchCapabilities;
|
|
534
539
|
} {
|
|
535
540
|
return getSupportedFilters(providerId);
|
|
536
541
|
}
|
|
@@ -1077,6 +1082,47 @@ export class IntegrationService implements Disposable, RepositoryResolutionConte
|
|
|
1077
1082
|
return listIssuesPage(this, options);
|
|
1078
1083
|
}
|
|
1079
1084
|
|
|
1085
|
+
/**
|
|
1086
|
+
* Issues matching structured criteria over a repository/org scope, with no forced relationship to the current
|
|
1087
|
+
* user — see {@link IntegrationManager.searchIssuesPage} for the contract (mandatory scope, guaranteed
|
|
1088
|
+
* most-recently-updated-first ordering, and the result-ceiling omission that carries the total match count).
|
|
1089
|
+
*/
|
|
1090
|
+
async searchIssuesPage(options: {
|
|
1091
|
+
providerId: IntegrationIds;
|
|
1092
|
+
repos?: ProviderReposInput;
|
|
1093
|
+
org?: string;
|
|
1094
|
+
criteria?: IssueSearchCriteria;
|
|
1095
|
+
page?: number;
|
|
1096
|
+
cursor?: string;
|
|
1097
|
+
itemsPerPage?: number;
|
|
1098
|
+
forceSync?: boolean;
|
|
1099
|
+
connectionId?: string;
|
|
1100
|
+
/**
|
|
1101
|
+
* Explicit self-managed host domain. Used only when the requested connection has no configured domain;
|
|
1102
|
+
* it must come from the trusted authentication configuration, not repository or remote data.
|
|
1103
|
+
*/
|
|
1104
|
+
domain?: string;
|
|
1105
|
+
}): Promise<ProviderPagedResult<IssueShape>> {
|
|
1106
|
+
return searchIssuesPage(this, options);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* How many issues match each scope, without fetching any — see {@link IntegrationManager.countIssues} for the
|
|
1111
|
+
* cost model, the per-scope isolation rules, and why `count: undefined` must not be rendered as zero.
|
|
1112
|
+
*/
|
|
1113
|
+
async countIssues(options: {
|
|
1114
|
+
providerId: IntegrationIds;
|
|
1115
|
+
scopes: readonly IssueCountScope[];
|
|
1116
|
+
connectionId?: string;
|
|
1117
|
+
/**
|
|
1118
|
+
* Explicit self-managed host domain. Used only when the requested connection has no configured domain;
|
|
1119
|
+
* it must come from the trusted authentication configuration, not repository or remote data.
|
|
1120
|
+
*/
|
|
1121
|
+
domain?: string;
|
|
1122
|
+
}): Promise<ProviderResult<IssueCountResult>> {
|
|
1123
|
+
return countIssues(this, options);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1080
1126
|
/**
|
|
1081
1127
|
* Reads the user's issues from an issue-tracker provider (Jira/Linear/Trello), whose issues live under
|
|
1082
1128
|
* resource → project (not repos), so they can't go through {@link listIssuesPage} (git-host, repo-scoped).
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import type { IssueShape } from '../../git/models/issue.js';
|
|
1
|
+
import type { IssueSearchCriteria, IssueShape } from '../../git/models/issue.js';
|
|
2
2
|
import type { PullRequestShape, PullRequestStateFilter } from '../../git/models/pullRequest.js';
|
|
3
3
|
import type { Event } from '../../utils/event.js';
|
|
4
4
|
import type { ConfiguredIntegrationsChangeEvent } from './authentication/configuredIntegrationService.js';
|
|
5
5
|
import type { ConfiguredIntegrationDescriptor } from './authentication/models.js';
|
|
6
6
|
import type { IntegrationIds } from './constants.js';
|
|
7
|
-
import type { IssueFilter, PullRequestFilter } from './providerFilters.js';
|
|
7
|
+
import type { IssueFilter, IssueSearchCapabilities, PullRequestFilter } from './providerFilters.js';
|
|
8
|
+
import type { IssueCountResult, IssueCountScope } from './reads/counts.js';
|
|
8
9
|
import type {
|
|
9
10
|
ConnectionStateChangeEvent,
|
|
10
11
|
ProviderBroadenResult,
|
|
@@ -200,6 +201,12 @@ export interface IntegrationManager {
|
|
|
200
201
|
pullRequestsAccountWide?: PullRequestFilter[];
|
|
201
202
|
issues: IssueFilter[];
|
|
202
203
|
issuesAccountWide: IssueFilter[];
|
|
204
|
+
/**
|
|
205
|
+
* What {@link searchIssuesPage} (and {@link countIssues}, over the same criteria) can express for this
|
|
206
|
+
* provider. Always present: a provider with no filtered issue search reports an empty `relationships` and
|
|
207
|
+
* all-false flags, which is the signal to hide that surface rather than to hide individual chips.
|
|
208
|
+
*/
|
|
209
|
+
issueSearch: IssueSearchCapabilities;
|
|
203
210
|
};
|
|
204
211
|
/** Forces an authoritative cloud connection refresh. Rejects if the backend connection list cannot be read. */
|
|
205
212
|
refreshConnections(): Promise<void>;
|
|
@@ -306,6 +313,85 @@ export interface IntegrationManager {
|
|
|
306
313
|
/** Self-managed host domain fallback; see {@link ProviderSweepTarget.domain}. */
|
|
307
314
|
domain?: string;
|
|
308
315
|
}): Promise<ProviderPagedResult<IssueShape>>;
|
|
316
|
+
/**
|
|
317
|
+
* Issues matching structured criteria over a repository/org scope, with NO forced relationship to the current
|
|
318
|
+
* user — the issue counterpart of the PR search, and the read to use for "every issue in these repos matching
|
|
319
|
+
* X" rather than "my issues".
|
|
320
|
+
*
|
|
321
|
+
* Why this and not {@link listIssuesPage}: that read is either bound to the user's own relationships
|
|
322
|
+
* (account-wide) or routed through the SDK's repo-scoped read, whose over-limit recovery walk can spend up to
|
|
323
|
+
* 128 sequential requests and still return an incomplete set. This one is a single request per page.
|
|
324
|
+
*
|
|
325
|
+
* Three parts of the contract worth reading before calling:
|
|
326
|
+
*
|
|
327
|
+
* - **Scope is mandatory.** Pass `repos`, `org`, or a user relationship (`authored`/`assigned`/`mentioned`).
|
|
328
|
+
* `any-assignee`/`unassigned` do NOT scope anything — either alone matches every such issue on the host —
|
|
329
|
+
* so a call carrying only those is refused (warning + `fetchFailed`).
|
|
330
|
+
* - **Ordering is always most-recently-updated-first**, not an option. A "show the N most recent" policy at
|
|
331
|
+
* the provider's result ceiling is only correct under a guaranteed order.
|
|
332
|
+
* - **At the result ceiling the read SUCCEEDS.** It reports an omission carrying `totalCount` (how many
|
|
333
|
+
* matched) and `limit` (how many are reachable) with `recovery: 'none'`, so a consumer can say "19.240
|
|
334
|
+
* matched, showing the 1.000 most recent" and know not to offer a "load more". It never falls back to a
|
|
335
|
+
* per-repository recovery walk.
|
|
336
|
+
*
|
|
337
|
+
* Check `getSupportedFilters().issueSearch` first: a provider with no filtered issue search reports empty
|
|
338
|
+
* relationships (and this read refuses), and a criterion it can't express refuses the whole read rather than
|
|
339
|
+
* serving a wider result than asked for.
|
|
340
|
+
*/
|
|
341
|
+
searchIssuesPage(options: {
|
|
342
|
+
providerId: IntegrationIds;
|
|
343
|
+
/** Repositories to search. Combines with `org`; both constrain the same query. */
|
|
344
|
+
repos?: ProviderRepositoriesInput;
|
|
345
|
+
/** Organization/account to search. Combines with `repos`. */
|
|
346
|
+
org?: string;
|
|
347
|
+
criteria?: IssueSearchCriteria;
|
|
348
|
+
/**
|
|
349
|
+
* Requested 1-based page. This read is cursor-only, so without a `cursor` reaching page N costs O(N)
|
|
350
|
+
* upstream requests; pass the previous page's cursor to make it exactly one.
|
|
351
|
+
*/
|
|
352
|
+
page?: number;
|
|
353
|
+
cursor?: string;
|
|
354
|
+
/**
|
|
355
|
+
* Page size PER RELATIONSHIP, not per page. Each requested relationship is its own provider query, so a
|
|
356
|
+
* page of a 2-relationship search returns up to `2 × itemsPerPage` items before deduplication — and fewer
|
|
357
|
+
* than that when the two overlap. `page.itemsPerPage` reports what actually came back, so size the UI off
|
|
358
|
+
* that rather than off this. A provider may also cap it below what is asked for.
|
|
359
|
+
*/
|
|
360
|
+
itemsPerPage?: number;
|
|
361
|
+
forceSync?: boolean;
|
|
362
|
+
connectionId?: string;
|
|
363
|
+
/** Self-managed host domain fallback; see {@link ProviderSweepTarget.domain}. */
|
|
364
|
+
domain?: string;
|
|
365
|
+
}): Promise<ProviderPagedResult<IssueShape>>;
|
|
366
|
+
/**
|
|
367
|
+
* How many issues MATCH each scope, without fetching any of them — the probe behind a "this will fetch ~N
|
|
368
|
+
* issues" preview, and behind a live count next to a filter the user hasn't applied yet.
|
|
369
|
+
*
|
|
370
|
+
* Cheap by design: every scope that can share a request does, and no issue data crosses the wire (measured
|
|
371
|
+
* against GitHub, 30 counts are a single rate-limit point). Still a network request per batch, so debounce and
|
|
372
|
+
* cache it if it's driven from UI state.
|
|
373
|
+
*
|
|
374
|
+
* A separate method rather than a flag on {@link searchIssuesPage} because transferring zero issues is the
|
|
375
|
+
* whole point: a `countOnly` read would hand back a paged result whose `items`/`cursor`/`hasMore` are all
|
|
376
|
+
* meaningless.
|
|
377
|
+
*
|
|
378
|
+
* Results are echoed under the caller's own `key`, so no positional matching is needed. Per-scope isolation is
|
|
379
|
+
* the rule: a scope refused for its own reasons, or a batch that failed upstream, warns and drops only its own
|
|
380
|
+
* scopes (with `fetchFailed` set) while every other count still comes back.
|
|
381
|
+
*
|
|
382
|
+
* `count: undefined` means the provider didn't report one — NOT zero, which is a real answer. Render the
|
|
383
|
+
* difference: showing an unknown count as 0 tells the user a filter matches nothing when it may match
|
|
384
|
+
* thousands. A provider that can't count at all (only GitHub/GHE can today) refuses the probe outright rather
|
|
385
|
+
* than returning fabricated numbers.
|
|
386
|
+
*/
|
|
387
|
+
countIssues(options: {
|
|
388
|
+
providerId: IntegrationIds;
|
|
389
|
+
/** Each needs its own scope, and each requested relationship its own entry — see {@link IssueCountScope}. */
|
|
390
|
+
scopes: readonly IssueCountScope[];
|
|
391
|
+
connectionId?: string;
|
|
392
|
+
/** Self-managed host domain fallback; see {@link ProviderSweepTarget.domain}. */
|
|
393
|
+
domain?: string;
|
|
394
|
+
}): Promise<ProviderResult<IssueCountResult>>;
|
|
309
395
|
/** Issue trackers are cloud-only, so this read takes no `domain`. */
|
|
310
396
|
listIssueTrackerIssuesPage(options: {
|
|
311
397
|
providerId: IntegrationIds;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CollectionMetadata, CollectionScopeFailure } from '@gitkraken/provider-apis';
|
|
2
2
|
import type { Account, UnidentifiedAuthor } from '../../../git/models/author.js';
|
|
3
3
|
import type { DefaultBranch } from '../../../git/models/defaultBranch.js';
|
|
4
|
-
import type { IssueShape, IssueStateFilter } from '../../../git/models/issue.js';
|
|
4
|
+
import type { IssueSearchCriteria, IssueShape, IssueStateFilter } from '../../../git/models/issue.js';
|
|
5
5
|
import type { IssueOrPullRequestState as PullRequestState } from '../../../git/models/issueOrPullRequest.js';
|
|
6
6
|
import type { PullRequest, PullRequestMergeMethod, PullRequestStateFilter } from '../../../git/models/pullRequest.js';
|
|
7
7
|
import type { RepositoryMetadata } from '../../../git/models/repositoryMetadata.js';
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
} from '../providers/models.js';
|
|
45
45
|
import type { ProvidersApi } from '../providers/providersApi.js';
|
|
46
46
|
import { mergeCollectionMetadata } from '../providers/utils/providerPaging.js';
|
|
47
|
-
import type { IntegrationResult, IntegrationType } from './integration.js';
|
|
47
|
+
import type { IntegrationResult, IntegrationType, ProviderIssueSearchPage } from './integration.js';
|
|
48
48
|
import { IntegrationBase } from './integration.js';
|
|
49
49
|
|
|
50
50
|
function isAzureDevOpsProvider(
|
|
@@ -1614,6 +1614,106 @@ export abstract class GitHostIntegration<
|
|
|
1614
1614
|
options?: { include?: PullRequestState[] },
|
|
1615
1615
|
): Promise<PullRequest[] | undefined>;
|
|
1616
1616
|
|
|
1617
|
+
/**
|
|
1618
|
+
* Result-returning wrapper for the FILTERED issue search — issues matching structured criteria over a
|
|
1619
|
+
* repository/org scope, with no forced relationship to the current user. Recovers thrown errors into
|
|
1620
|
+
* `{ error }` so the facade surfaces a warning instead of a silent empty page.
|
|
1621
|
+
*
|
|
1622
|
+
* Distinct from every other issue read on this class: {@link searchMyIssuesWithTruncationResult} is bound to
|
|
1623
|
+
* the current user by construction, and {@link getMyIssuesForReposAsShapesResult} goes through the SDK's
|
|
1624
|
+
* repo-scoped read (whose over-limit recovery walk can cost up to 128 requests). This one is one request per
|
|
1625
|
+
* page and carries no relationship it wasn't asked for.
|
|
1626
|
+
*/
|
|
1627
|
+
async searchIssuesPageResult(
|
|
1628
|
+
options: {
|
|
1629
|
+
/**
|
|
1630
|
+
* Repositories to search, as namespace/name descriptors. NOT repository ids: a search query names
|
|
1631
|
+
* repositories by path, so an id-based input can't be expressed and the facade rejects it before here.
|
|
1632
|
+
*/
|
|
1633
|
+
repos?: ProviderRepoInput[];
|
|
1634
|
+
org?: string;
|
|
1635
|
+
criteria?: IssueSearchCriteria;
|
|
1636
|
+
cursor?: string;
|
|
1637
|
+
pageSize?: number;
|
|
1638
|
+
},
|
|
1639
|
+
cancellation?: AbortSignal,
|
|
1640
|
+
connectionId?: string,
|
|
1641
|
+
): Promise<IntegrationResult<ProviderIssueSearchPage | undefined>> {
|
|
1642
|
+
const scope = getScopedLogger();
|
|
1643
|
+
// `connectionId` targets a specific account (multi-account); omitted reads the primary.
|
|
1644
|
+
const session = await this.resolveReadSession(connectionId, scope);
|
|
1645
|
+
if (session == null) return undefined;
|
|
1646
|
+
|
|
1647
|
+
const start = performance.now();
|
|
1648
|
+
try {
|
|
1649
|
+
const result = await this.searchProviderIssuesPage?.(session, options, cancellation);
|
|
1650
|
+
this.resetRequestExceptionCount('searchIssuesPage');
|
|
1651
|
+
return { value: result, duration: performance.now() - start };
|
|
1652
|
+
} catch (ex) {
|
|
1653
|
+
this.handleProviderException('searchIssuesPage', ex, { scope: scope, connectionId: connectionId });
|
|
1654
|
+
return { error: toError(ex), duration: performance.now() - start };
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
/**
|
|
1659
|
+
* OPTIONAL, like {@link searchProviderPullRequests}: a provider that can't express the criteria server-side
|
|
1660
|
+
* doesn't implement it, and the facade refuses the read (warning + `fetchFailed`) rather than serving a list
|
|
1661
|
+
* that was never narrowed. Whether a provider implements this must agree with what
|
|
1662
|
+
* `ProviderMetadata.supportedIssueSearch` declares.
|
|
1663
|
+
*/
|
|
1664
|
+
protected searchProviderIssuesPage?(
|
|
1665
|
+
session: ProviderAuthenticationSession,
|
|
1666
|
+
options: {
|
|
1667
|
+
repos?: ProviderRepoInput[];
|
|
1668
|
+
org?: string;
|
|
1669
|
+
criteria?: IssueSearchCriteria;
|
|
1670
|
+
cursor?: string;
|
|
1671
|
+
pageSize?: number;
|
|
1672
|
+
},
|
|
1673
|
+
cancellation?: AbortSignal,
|
|
1674
|
+
): Promise<ProviderIssueSearchPage | undefined>;
|
|
1675
|
+
|
|
1676
|
+
/**
|
|
1677
|
+
* Result-returning wrapper for the count-only probe: how many issues MATCH each scope, transferring no issues
|
|
1678
|
+
* at all. Recovers thrown errors into `{ error }` like the reads around it.
|
|
1679
|
+
*
|
|
1680
|
+
* Counts come back POSITIONALLY — one per input scope, in order — because a caller's key must never reach the
|
|
1681
|
+
* provider query. `undefined` in a slot means the provider didn't report a count for it, never zero matches.
|
|
1682
|
+
*/
|
|
1683
|
+
async countIssuesResult(
|
|
1684
|
+
scopes: readonly { repos?: ProviderRepoInput[]; org?: string; criteria?: IssueSearchCriteria }[],
|
|
1685
|
+
cancellation?: AbortSignal,
|
|
1686
|
+
connectionId?: string,
|
|
1687
|
+
): Promise<IntegrationResult<(number | undefined)[] | undefined>> {
|
|
1688
|
+
const scope = getScopedLogger();
|
|
1689
|
+
// `connectionId` targets a specific account (multi-account); omitted reads the primary.
|
|
1690
|
+
const session = await this.resolveReadSession(connectionId, scope);
|
|
1691
|
+
if (session == null) return undefined;
|
|
1692
|
+
|
|
1693
|
+
const start = performance.now();
|
|
1694
|
+
try {
|
|
1695
|
+
const counts = await this.countProviderIssues?.(session, scopes, cancellation);
|
|
1696
|
+
this.resetRequestExceptionCount('countIssues');
|
|
1697
|
+
return { value: counts, duration: performance.now() - start };
|
|
1698
|
+
} catch (ex) {
|
|
1699
|
+
this.handleProviderException('countIssues', ex, { scope: scope, connectionId: connectionId });
|
|
1700
|
+
return { error: toError(ex), duration: performance.now() - start };
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* OPTIONAL: only a provider that can answer "how many match" WITHOUT fetching the matches implements this.
|
|
1706
|
+
* GitHub's search reports `issueCount` on a zero-node selection; GitLab's REST exposes a total on some
|
|
1707
|
+
* endpoints but not for a search-shaped query, and Azure has no equivalent. A provider that can't answer
|
|
1708
|
+
* doesn't implement it and the facade refuses the probe, so a consumer hides its count rather than being shown
|
|
1709
|
+
* a fabricated one.
|
|
1710
|
+
*/
|
|
1711
|
+
protected countProviderIssues?(
|
|
1712
|
+
session: ProviderAuthenticationSession,
|
|
1713
|
+
scopes: readonly { repos?: ProviderRepoInput[]; org?: string; criteria?: IssueSearchCriteria }[],
|
|
1714
|
+
cancellation?: AbortSignal,
|
|
1715
|
+
): Promise<(number | undefined)[] | undefined>;
|
|
1716
|
+
|
|
1617
1717
|
getPullRequestIdentityFromMaybeUrl(search: string): PullRequestUrlIdentity | undefined {
|
|
1618
1718
|
return this.getProviderPullRequestIdentityFromMaybeUrl?.(search);
|
|
1619
1719
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { CollectionMetadata } from '@gitkraken/provider-apis';
|
|
2
1
|
import type { Account } from '../../../git/models/author.js';
|
|
3
2
|
import type { AutolinkReference, DynamicAutolinkReference } from '../../../git/models/autolink.js';
|
|
4
3
|
import type { Issue, IssueShape } from '../../../git/models/issue.js';
|
|
@@ -26,12 +25,12 @@ import { GitCloudHostIntegrationId } from '../constants.js';
|
|
|
26
25
|
import type { IntegrationServiceContext } from '../context.js';
|
|
27
26
|
import { AuthenticationError, RequestClientError, toError } from '../errors.js';
|
|
28
27
|
import type { IntegrationConnectionChangeEvent } from '../integrationService.js';
|
|
29
|
-
import type { IssueFilter } from '../providerFilters.js';
|
|
30
28
|
import type { ProvidersApi } from '../providers/providersApi.js';
|
|
31
29
|
import type { Sources } from '../telemetry.js';
|
|
32
30
|
import { areDomainsOnSameHost } from '../utils/domain.utils.js';
|
|
33
31
|
import { isGitSelfManagedHostIntegrationId } from '../utils/integration.utils.js';
|
|
34
32
|
import type { GitHostIntegration } from './gitHostIntegration.js';
|
|
33
|
+
import type { AccountWideIssuesResult, SearchMyIssuesOptions } from './issueReads.js';
|
|
35
34
|
import type { IssuesIntegration } from './issuesIntegration.js';
|
|
36
35
|
|
|
37
36
|
export type Integration = GitHostIntegration | IssuesIntegration;
|
|
@@ -40,6 +39,10 @@ export type IntegrationById<T extends IntegrationIds> = T extends IssuesCloudHos
|
|
|
40
39
|
: GitHostIntegration;
|
|
41
40
|
export type IntegrationType = 'git' | 'issues';
|
|
42
41
|
|
|
42
|
+
// The issue-read contracts live in their own module (pure data, and their relationship to each other is the
|
|
43
|
+
// point of reading them together); re-exported here so the providers that implement these reads keep one import.
|
|
44
|
+
export type { AccountWideIssuesResult, ProviderIssueSearchPage, SearchMyIssuesOptions } from './issueReads.js';
|
|
45
|
+
|
|
43
46
|
export type IntegrationKey<T extends IntegrationIds = IntegrationIds> = T extends
|
|
44
47
|
| GitCloudHostIntegrationId
|
|
45
48
|
| IssuesCloudHostIntegrationId
|
|
@@ -53,50 +56,6 @@ export type IntegrationResult<T> =
|
|
|
53
56
|
| { error: Error; duration?: number; value?: never }
|
|
54
57
|
| undefined;
|
|
55
58
|
|
|
56
|
-
/**
|
|
57
|
-
* Account-wide issue read result. `truncated` is a provider-native incompleteness signal (GitHub's search
|
|
58
|
-
* ceiling, an Azure per-project backstop); cursor-capable providers also expose `cursor`/`hasMore`/`page`.
|
|
59
|
-
* `metadata` optionally carries structured per-scope failures from a fan-out (Azure across projects).
|
|
60
|
-
*/
|
|
61
|
-
export type AccountWideIssuesResult = {
|
|
62
|
-
values: IssueShape[];
|
|
63
|
-
truncated: boolean;
|
|
64
|
-
metadata?: CollectionMetadata;
|
|
65
|
-
cursor?: string;
|
|
66
|
-
hasMore?: boolean;
|
|
67
|
-
page?: number;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Options for the account-wide issue read. `includeAllAssignees` drops the "assigned to me" scoping so the
|
|
72
|
-
* read broadens to issues assigned to anyone (the account-wide equivalent of the repo-scoped
|
|
73
|
-
* {@link GitHostIntegration.getMyIssuesForReposResult}'s toggle). Authored/mentioned categories, where a
|
|
74
|
-
* provider has them, stay user-relative — they're meaningless without a user.
|
|
75
|
-
*/
|
|
76
|
-
export type SearchMyIssuesOptions = {
|
|
77
|
-
includeAllAssignees?: boolean;
|
|
78
|
-
/**
|
|
79
|
-
* Narrows the account-wide read to the requested relationship(s) instead of the provider's own definition of
|
|
80
|
-
* "my issues" (GitHub/GHE: authored ∪ assigned ∪ mentioned; Azure: assigned ∪ authored; GitLab:
|
|
81
|
-
* assigned-to-me). Narrowing has to happen HERE, not on the returned page: the excluded items still counted
|
|
82
|
-
* toward the provider's page/cursor, so a client-side filter desynchronizes `items` from `hasMore`.
|
|
83
|
-
*
|
|
84
|
-
* Only filters the provider can express server-side are accepted; the facade validates the set against
|
|
85
|
-
* `ProviderMetadata.supportedAccountWideIssueFilters` (all-or-nothing) and refuses the read rather than
|
|
86
|
-
* serving the unnarrowed union as if it had been filtered. Omitted keeps the provider's definition.
|
|
87
|
-
*/
|
|
88
|
-
filters?: IssueFilter[];
|
|
89
|
-
cursor?: string;
|
|
90
|
-
/**
|
|
91
|
-
* Narrows the account-wide read to one org/account (Azure: the organization) and/or one project within it.
|
|
92
|
-
* Only honored by a host with a project layer (Azure), whose account-wide read otherwise fans out over every
|
|
93
|
-
* project of every org; the caller checks `supportsProjectDiscovery` before asking, so a host without a
|
|
94
|
-
* project layer never silently returns an unscoped list as if it had been narrowed.
|
|
95
|
-
*/
|
|
96
|
-
org?: string;
|
|
97
|
-
project?: string;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
59
|
type SyncReqUsecase = Exclude<
|
|
101
60
|
| 'getAccountForCommit'
|
|
102
61
|
| 'getAccountForEmail'
|
|
@@ -120,7 +79,9 @@ type SyncReqUsecase = Exclude<
|
|
|
120
79
|
| 'getRepositoryMetadata'
|
|
121
80
|
| 'getResourcesForUser'
|
|
122
81
|
| 'getSshSigningKeysForEmails'
|
|
82
|
+
| 'countIssues'
|
|
123
83
|
| 'mergePullRequest'
|
|
84
|
+
| 'searchIssuesPage'
|
|
124
85
|
| 'searchMyIssues'
|
|
125
86
|
| 'searchMyPullRequests'
|
|
126
87
|
| 'searchPullRequests',
|