@gitkraken/core-gitlens 0.15.6 → 0.15.7
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 +12 -1
- package/dist/git/models/issue.d.ts +14 -1
- package/dist/git/models/issue.js +3 -1
- package/dist/git/models/issue.js.map +1 -1
- package/dist/git/utils/issue.utils.js +4 -0
- package/dist/git/utils/issue.utils.js.map +1 -1
- package/dist/plus/integrations/fieldPresence.d.ts +1 -1
- package/dist/plus/integrations/fieldPresence.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +18 -0
- package/dist/plus/integrations/models/integration.js +13 -1
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.d.ts +7 -0
- package/dist/plus/integrations/providers/azure/azure.js +26 -4
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/models.d.ts +11 -3
- package/dist/plus/integrations/providers/azure/models.js +37 -23
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +14 -4
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js +31 -4
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
- package/dist/plus/integrations/providers/models.d.ts +2 -1
- package/dist/plus/integrations/providers/models.js +9 -2
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +6 -0
- package/dist/plus/integrations/providers/providersApi.js +20 -4
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +1 -0
- package/dist/plus/integrations/reads/etag.js +8 -3
- package/dist/plus/integrations/reads/etag.js.map +1 -1
- package/docs/integrations.md +15 -6
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.15.7] - 2026-10-22
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Adds `bodyField` to `IssueShape` and `Issue`: the provider field `body` was read from, for providers whose issues keep it in different fields. Set on Azure DevOps work items (`{ id: 'Microsoft.VSTS.TCM.ReproSteps', name: 'Repro Steps' }`), so a host can label the body by its field; absent for other providers and when there is no body (git, plus/integrations)
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Fixes Azure DevOps work items whose type keeps its content outside Description (Repro Steps on Agile, Scrum and CMMI Bugs, Acceptance Criteria, a custom HTML field on an inherited process) reading with no `body`. The body is now the work item's first rich text field with content: Description, then the built-in description fields, then any other `html` field from the organization's field catalog (custom fields first), in every read (lists, `getIssue`, `getIssuesBatch` and the work item search), as provider-apis converts its list rows. The catalog is read only for a work item no built-in field describes, once per organization and credential, kept for an hour; a failed read leaves the body to the built-in fields rather than failing the read. An Azure work item's etag now carries the rule its body is read by, so `getIssuesBatch` reads a row held from before in full once ([#6047](https://github.com/gitkraken/vscode-gitlens/issues/6047)) (plus/integrations)
|
|
18
|
+
|
|
9
19
|
## [0.15.6] - 2026-10-22
|
|
10
20
|
|
|
11
21
|
### Fixed
|
|
@@ -852,7 +862,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
|
|
|
852
862
|
|
|
853
863
|
- Initial release. Bundles `@gitlens/utils`, `@gitlens/git`, `@gitlens/git-cli`, `@gitlens/ai`, and `@gitlens/git-github` into a single core npm package with subpath exports.
|
|
854
864
|
|
|
855
|
-
[unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.
|
|
865
|
+
[unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.7...HEAD
|
|
866
|
+
[0.15.7]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.6...gitkraken:releases/core/v0.15.7
|
|
856
867
|
[0.15.6]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.5...gitkraken:releases/core/v0.15.6
|
|
857
868
|
[0.15.5]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.4...gitkraken:releases/core/v0.15.5
|
|
858
869
|
[0.15.4]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.3...gitkraken:releases/core/v0.15.4
|
|
@@ -184,6 +184,12 @@ export interface IssueShape extends IssueOrPullRequest {
|
|
|
184
184
|
* work items and Bitbucket Cloud issues carry HTML with no `bodyFormat` set.
|
|
185
185
|
*/
|
|
186
186
|
bodyFormat?: IssueBodyFormat;
|
|
187
|
+
/**
|
|
188
|
+
* The provider field `body` was read from, for providers whose issues keep it in different fields: an Azure
|
|
189
|
+
* DevOps work item's type decides it (Description, Repro Steps on a Bug, a custom field on an inherited process).
|
|
190
|
+
* Absent when the provider has a single body field, or there is no body.
|
|
191
|
+
*/
|
|
192
|
+
bodyField?: IssueBodyField;
|
|
187
193
|
project?: IssueProject;
|
|
188
194
|
issueType?: string;
|
|
189
195
|
/** An issue can belong to several Jira sprints; Azure reports one iteration. */
|
|
@@ -216,11 +222,18 @@ export declare class Issue implements IssueShape {
|
|
|
216
222
|
readonly bodyFormat?: IssueBodyFormat | undefined;
|
|
217
223
|
readonly iterations?: IssueIteration[] | undefined;
|
|
218
224
|
readonly projection?: IssueProjection | undefined;
|
|
225
|
+
readonly bodyField?: IssueBodyField | undefined;
|
|
219
226
|
readonly type = "issue";
|
|
220
|
-
constructor(provider: ProviderReference, id: string, nodeId: string | undefined, title: string, url: string, createdDate: Date, updatedDate: Date, closed: boolean, state: IssueOrPullRequestState | undefined, author: IssueMember | undefined, assignees: IssueMember[], repository?: IssueRepository | undefined, closedDate?: Date | undefined, labels?: IssueLabel[] | undefined, commentsCount?: number | undefined, thumbsUpCount?: number | undefined, body?: string | undefined, project?: IssueProject | undefined, number?: string | undefined, issueType?: string | undefined, providerState?: IssueProviderState | undefined, bodyFormat?: IssueBodyFormat | undefined, iterations?: IssueIteration[] | undefined, projection?: IssueProjection | undefined);
|
|
227
|
+
constructor(provider: ProviderReference, id: string, nodeId: string | undefined, title: string, url: string, createdDate: Date, updatedDate: Date, closed: boolean, state: IssueOrPullRequestState | undefined, author: IssueMember | undefined, assignees: IssueMember[], repository?: IssueRepository | undefined, closedDate?: Date | undefined, labels?: IssueLabel[] | undefined, commentsCount?: number | undefined, thumbsUpCount?: number | undefined, body?: string | undefined, project?: IssueProject | undefined, number?: string | undefined, issueType?: string | undefined, providerState?: IssueProviderState | undefined, bodyFormat?: IssueBodyFormat | undefined, iterations?: IssueIteration[] | undefined, projection?: IssueProjection | undefined, bodyField?: IssueBodyField | undefined);
|
|
221
228
|
static is(issue: unknown): issue is Issue;
|
|
222
229
|
}
|
|
223
230
|
export type IssueBodyFormat = 'markdown' | 'jira-wiki';
|
|
231
|
+
export interface IssueBodyField {
|
|
232
|
+
/** The provider's id for the field, e.g. `Microsoft.VSTS.TCM.ReproSteps`. */
|
|
233
|
+
readonly id: string;
|
|
234
|
+
/** The field's display name, e.g. `Repro Steps`. */
|
|
235
|
+
readonly name: string;
|
|
236
|
+
}
|
|
224
237
|
export declare const enum RepositoryAccessLevel {
|
|
225
238
|
Admin = 100,
|
|
226
239
|
Maintain = 40,
|
package/dist/git/models/issue.js
CHANGED
|
@@ -44,8 +44,9 @@ let Issue = _Issue = class Issue {
|
|
|
44
44
|
bodyFormat;
|
|
45
45
|
iterations;
|
|
46
46
|
projection;
|
|
47
|
+
bodyField;
|
|
47
48
|
type = "issue";
|
|
48
|
-
constructor(provider, id, nodeId, title, url, createdDate, updatedDate, closed, state, author, assignees, repository, closedDate, labels, commentsCount, thumbsUpCount, body, project, number, issueType, providerState, bodyFormat, iterations, projection) {
|
|
49
|
+
constructor(provider, id, nodeId, title, url, createdDate, updatedDate, closed, state, author, assignees, repository, closedDate, labels, commentsCount, thumbsUpCount, body, project, number, issueType, providerState, bodyFormat, iterations, projection, bodyField) {
|
|
49
50
|
this.provider = provider;
|
|
50
51
|
this.id = id;
|
|
51
52
|
this.nodeId = nodeId;
|
|
@@ -70,6 +71,7 @@ let Issue = _Issue = class Issue {
|
|
|
70
71
|
this.bodyFormat = bodyFormat;
|
|
71
72
|
this.iterations = iterations;
|
|
72
73
|
this.projection = projection;
|
|
74
|
+
this.bodyField = bodyField;
|
|
73
75
|
}
|
|
74
76
|
static is(issue) {
|
|
75
77
|
return issue instanceof _Issue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue.js","names":[],"sources":["../../../../git/src/models/issue.ts"],"sourcesContent":["import { loggable } from '@gitlens/utils/decorators/log.js';\nimport type { RequireSome, RequireSomeWithProps } from '@gitlens/utils/types.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestState } from './issueOrPullRequest.js';\nimport type { ProviderReference } from './remoteProvider.js';\nimport type { RepositoryIdentityDescriptor } from './repositoryIdentities.js';\n\n/** Selects which issue states a read should include. `all` covers open + closed. */\nexport type IssueStateFilter = 'open' | 'closed' | 'all';\n\n/**\n * One relationship a filtered issue search can constrain on.\n *\n * The last two are USER-INDEPENDENT — they describe the issue, not the caller — and neither is a SCOPE: either\n * one alone matches every such issue on the host, so a search using one still needs a repository/org scope.\n */\nexport type IssueSearchRelationship =\n\t/** Authored by the current user. */\n\t| 'authored'\n\t/** Assigned to the current user. */\n\t| 'assigned'\n\t/** Mentions the current user. */\n\t| 'mentioned'\n\t/** Assigned to ANYONE. Excludes unassigned issues — see {@link IssueSearchCriteria.relationships}. */\n\t| 'any-assignee'\n\t/** Assigned to nobody. The complement of `any-assignee`, not a broadening of it. */\n\t| 'unassigned';\n\n/**\n * A field an issue read can be ordered by, provider-neutral. No provider expresses all of them: each read\n * validates the requested key against the capability its provider declares and refuses one it can't express\n * server-side, rather than serving a differently-ordered list under the name of the one that was asked for.\n *\n * `closed` and `resolved` are deliberately SEPARATE, not two names for the same date. Azure DevOps tracks both\n * and they differ (an issue can be closed without being resolved and resolved without being closed), and Jira\n * has no close date at all — it models the equivalent as `resolutiondate`. Collapsing them would force one of\n * those two providers to lie about which date it ordered by.\n */\nexport type IssueSortField =\n\t/** Creation date. */\n\t| 'created'\n\t/** Last activity. */\n\t| 'updated'\n\t/** Close date. Only where the provider orders by it server-side — notably NOT GitHub. */\n\t| 'closed'\n\t/** Resolution date (Jira `resolutiondate`, Azure `ResolvedDate`). Distinct from `closed`. */\n\t| 'resolved'\n\t/** Number of comments. */\n\t| 'comments'\n\t/** Reactions/upvotes. */\n\t| 'reactions'\n\t/** The provider's own priority field. */\n\t| 'priority'\n\t/** Due date. */\n\t| 'dueDate'\n\t/** Alphabetical by title. */\n\t| 'title';\n\n/**\n * How an issue read is ordered, as `field:direction` — the same shape `BranchSorting`/`TagSorting` already use,\n * so it is one serializable string a consumer can persist, compare, or bind straight to a setting or a dropdown.\n */\nexport type IssueSorting = `${IssueSortField}:asc` | `${IssueSortField}:desc`;\n\n/**\n * The order every issue read that HAS a default applies when the caller asks for none: most recently updated\n * first, which is what those reads served before ordering was an option.\n *\n * One definition rather than one per layer, because it is a promise made in several places at once — the criteria\n * model documents it, the GitHub query emits it, and the result-ceiling warning quotes it — and three copies would\n * be free to disagree about what \"the default\" is.\n *\n * Note it is not a default every read has, and the two layers differ. The API client's account-wide \"my issues\"\n * search (`GitHubApi.searchMyIssues`) never requested an order, so a direct caller omitting `sort` still gets\n * GitHub's relevance order — the default is opt-in there, to keep that read's already-shipped results unchanged.\n * The FACADE does apply it: GitHub declares `updated:desc` among its account-wide keys, so\n * `readAccountWideIssuesPage` resolves the omission to this default and forwards it, and that read's emitted query\n * gained a `sort:updated` qualifier it did not have before. A read whose surface cannot express even this key is\n * the only one the facade leaves in the provider's own order.\n */\nexport const defaultIssueSort: IssueSorting = 'updated:desc';\n\n/**\n * What a filtered issue search narrows on: a provider-neutral criteria set, translated to each provider's own\n * query language by its integration.\n *\n * Structured rather than a provider query string so a consumer can render filter chips and hide the ones a\n * provider can't express, and so the same criteria mean the same thing on a provider whose query language looks\n * nothing like GitHub's search syntax. Every field is validated against the provider's declared capability\n * BEFORE the read runs, all-or-nothing: a criterion the provider can't express server-side refuses the whole\n * read rather than serving a list that was never narrowed. Client-side narrowing is not an option — the dropped\n * items still counted toward the page and cursor the provider produced, so a post-filter would leave the items\n * describing a different result set than the paging that came with them.\n */\nexport interface IssueSearchCriteria {\n\t/**\n\t * Relationships to constrain on, OR-ed together (each becomes its own provider query, unioned and deduped).\n\t * Omitted means NO relationship constraint at all — every issue in scope, assigned or not — which requires a\n\t * repository/org scope.\n\t *\n\t * `any-assignee` and `unassigned` partition the scope between them and are mutually exclusive; passing both is\n\t * refused rather than silently dropping one. Note that \"all visible issues\" is the OMITTED case, NOT\n\t * `any-assignee`, which excludes unassigned issues.\n\t */\n\trelationships?: IssueSearchRelationship[];\n\t/**\n\t * Free text, matched by the provider's own relevance rules — NOT a substring match, and not a way to smuggle\n\t * qualifiers: a token that would read as one is dropped, as are quotes and control characters, so text can\n\t * never re-scope the search. The structured criteria are the qualifier channel.\n\t */\n\ttext?: string;\n\t/** Issue states to include. Omitted reads open issues only. */\n\tstate?: IssueStateFilter;\n\t/** Labels the issue must carry — AND-ed, matching GitHub's own `label:` semantics. */\n\tlabels?: string[];\n\tmilestone?: string;\n\t/** ISO date (`YYYY-MM-DD`). The most effective narrowing criterion by far on a large scope. */\n\tupdatedAfter?: string;\n\t/** ISO date (`YYYY-MM-DD`). */\n\tcreatedAfter?: string;\n\t/** Issues with no linked pull request. */\n\twithoutLinkedPullRequest?: boolean;\n\t/** Includes issues in archived repositories, which are excluded by default. */\n\tincludeArchived?: boolean;\n\t/**\n\t * How to order the results. Omitted means `updated:desc`, which is what this search has always served.\n\t *\n\t * Validated like every other criterion — all-or-nothing against {@link IssueSearchCapabilities.sorts}: a key\n\t * the provider can't express server-side refuses the WHOLE read rather than falling back to the default.\n\t * Serving another order is not a smaller error than serving a wider result: combined with the provider's\n\t * result ceiling it returns a different subset than was asked for, and the paging that comes with it describes\n\t * that other subset.\n\t *\n\t * Do not change it mid-pagination. A cursor carries the sort it was produced under, and threading it under a\n\t * different key is refused rather than serving a sequence with gaps and repeats: drop the cursor instead.\n\t */\n\tsort?: IssueSorting;\n}\n\n/**\n * Which {@link IssueSearchCriteria} fields a provider's filtered issue search can express server-side.\n *\n * An empty `relationships` with every flag false means the provider has NO filtered issue search — the read is\n * refused outright, so a consumer should offer neither the surface nor its chips.\n */\nexport interface IssueSearchCapabilities {\n\t/** Relationships the search can constrain on. Empty means the search itself is unsupported. */\n\trelationships: IssueSearchRelationship[];\n\ttext: boolean;\n\tlabels: boolean;\n\tmilestone: boolean;\n\tupdatedAfter: boolean;\n\tcreatedAfter: boolean;\n\twithoutLinkedPullRequest: boolean;\n\t/** Whether {@link IssueSearchCriteria.state} can select anything other than the provider's default (open). */\n\tstates: boolean;\n\t/**\n\t * Sort keys the search can express IN SERVER. Always contains at least `updated:desc` — the historical default\n\t * — when the search exists at all, so it is never empty for a usable surface; a provider without a filtered\n\t * search reports an empty `relationships`, which is already the signal that there is no surface to order.\n\t */\n\tsorts: IssueSorting[];\n}\n\n/**\n * The read that produced an issue row, as a key for `getIssueFieldPresence` (`@gitlens/integrations`). It names a\n * READ, not a field set: the same tag can carry different fields on different providers.\n */\nexport type IssueProjection =\n\t/** A single-item read. */\n\t| 'point'\n\t/** The filtered search. */\n\t| 'search'\n\t/** The account-wide list, and the trackers' \"my issues\". */\n\t| 'account'\n\t/** A repository-scoped list. */\n\t| 'repos'\n\t/** A tracker project's issues (`listIssueTrackerIssuesPage`, `getIssuesForProject`). */\n\t| 'project'\n\t/** The batch read. */\n\t| 'batch';\n\nexport interface IssueShape extends IssueOrPullRequest {\n\t/** `undefined` when the provider can't resolve the author, e.g. a deleted GitHub account */\n\tauthor: IssueMember | undefined;\n\tassignees: IssueMember[];\n\t/** The provider's own workflow state, when it exposes one beyond the normalized open/closed state. */\n\tproviderState?: IssueProviderState;\n\trepository?: IssueRepository;\n\tlabels?: IssueLabel[];\n\tbody?: string;\n\t/**\n\t * The syntax used by `body`. When omitted, consumers should treat `body` as Markdown — except that Azure DevOps\n\t * work items and Bitbucket Cloud issues carry HTML with no `bodyFormat` set.\n\t */\n\tbodyFormat?: IssueBodyFormat;\n\tproject?: IssueProject;\n\tissueType?: string;\n\t/** An issue can belong to several Jira sprints; Azure reports one iteration. */\n\titerations?: IssueIteration[];\n\t/** The read that produced this row. Absent means unknown: a legacy row, or a provider or read not yet tagged. */\n\treadonly projection?: IssueProjection;\n}\n\n@loggable(i => i.id)\nexport class Issue implements IssueShape {\n\treadonly type = 'issue';\n\n\tconstructor(\n\t\tpublic readonly provider: ProviderReference,\n\t\tpublic readonly id: string,\n\t\tpublic readonly nodeId: string | undefined,\n\t\tpublic readonly title: string,\n\t\tpublic readonly url: string,\n\t\tpublic readonly createdDate: Date,\n\t\tpublic readonly updatedDate: Date,\n\t\tpublic readonly closed: boolean,\n\t\tpublic readonly state: IssueOrPullRequestState | undefined,\n\t\tpublic readonly author: IssueMember | undefined,\n\t\tpublic readonly assignees: IssueMember[],\n\t\tpublic readonly repository?: IssueRepository,\n\t\tpublic readonly closedDate?: Date,\n\t\tpublic readonly labels?: IssueLabel[],\n\t\tpublic readonly commentsCount?: number,\n\t\tpublic readonly thumbsUpCount?: number,\n\t\tpublic readonly body?: string,\n\t\tpublic readonly project?: IssueProject,\n\t\tpublic readonly number?: string,\n\t\tpublic readonly issueType?: string,\n\t\tpublic readonly providerState?: IssueProviderState,\n\t\tpublic readonly bodyFormat?: IssueBodyFormat,\n\t\tpublic readonly iterations?: IssueIteration[],\n\t\tpublic readonly projection?: IssueProjection,\n\t) {}\n\n\tstatic is(issue: unknown): issue is Issue {\n\t\treturn issue instanceof Issue;\n\t}\n}\n\nexport type IssueBodyFormat = 'markdown' | 'jira-wiki';\n\nexport const enum RepositoryAccessLevel {\n\tAdmin = 100,\n\tMaintain = 40,\n\tWrite = 30,\n\tTriage = 20,\n\tRead = 10,\n\tNone = 0,\n}\n\nexport interface IssueLabel {\n\tcolor?: string;\n\tname: string;\n}\n\n/**\n * Sprint metadata is optional because some providers only report an iteration path. An absent `isActive`\n * means unknown, not inactive, so consumers must not use it to exclude those iterations by default.\n */\nexport interface IssueIteration {\n\t/**\n\t * A sprint id on Jira, the verbatim iteration path on Azure — so it is only meaningful within the provider's\n\t * project, never across providers. Unique at read time but NOT durable: Azure rewrites the path when an\n\t * iteration node is renamed or re-parented, so don't persist it as a long-lived id.\n\t */\n\tid: string;\n\tname: string;\n\tisActive?: boolean;\n\tstartDate?: Date;\n\tendDate?: Date;\n}\n\nexport interface IssueMember {\n\tid: string;\n\t/**\n\t * Absent when the provider exposes no display name for the member. Optional on purpose: a fallback string\n\t * invented here (`'unknown'`, `''`) can't be told apart from a real name downstream, so it can't be undone by a\n\t * consumer that needs to render something else — or nothing at all, as when the member feeds an AI prompt.\n\t * Each consumer picks its own presentation.\n\t */\n\tname?: string;\n\t/** The provider's handle for this person, when it has one — GitHub's login, Azure's `uniqueName` (a UPN,\n\t * so an email), Bitbucket's mutable `nickname`. Display/labelling only: it is neither guaranteed present\n\t * (GitLab's native mapper has none) nor a stable identity, so never key a match off it. */\n\tusername?: string;\n\tavatarUrl?: string;\n\turl?: string;\n}\n\nexport type IssueProviderState = {\n\tid?: string;\n\tname: string;\n\tcolor?: string;\n\t/** The provider's workflow category. `CANCELED` is abandoned work (Linear's canceled and duplicate states, Azure's\n\t * `Removed` category): closed like `DONE`, kept apart so a consumer can tell the two outcomes apart. */\n\tcategory?: 'TO_DO' | 'IN_PROGRESS' | 'DONE' | 'CANCELED';\n};\n\nexport interface IssueProject {\n\tid: string;\n\tname: string;\n\tresourceId: string;\n\tresourceName: string;\n}\n\nexport interface IssueRepository {\n\towner: string;\n\trepo: string;\n\taccessLevel?: RepositoryAccessLevel;\n\turl?: string;\n\tid?: string;\n}\n\nexport type IssueRepositoryIdentityDescriptor = RequireSomeWithProps<\n\tRequireSome<RepositoryIdentityDescriptor<string>, 'provider'>,\n\t'provider',\n\t'id' | 'domain' | 'repoDomain' | 'repoName'\n> &\n\tRequireSomeWithProps<RequireSome<RepositoryIdentityDescriptor<string>, 'remote'>, 'remote', 'domain'>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA+EA,MAAa,mBAAiC;AA6HvC,IAAM,QAAA,SAAN,MAAM,MAA4B;CAIvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CA1BjB,OAAgB;CAEhB,YACC,UACA,IACA,QACA,OACA,KACA,aACA,aACA,QACA,OACA,QACA,WACA,YACA,YACA,QACA,eACA,eACA,MACA,SACA,QACA,WACA,eACA,YACA,YACA,YACC;EAxBe,KAAA,WAAA;EACA,KAAA,KAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EACA,KAAA,cAAA;EACA,KAAA,cAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EACA,KAAA,SAAA;EACA,KAAA,YAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,SAAA;EACA,KAAA,gBAAA;EACA,KAAA,gBAAA;EACA,KAAA,OAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;EACA,KAAA,YAAA;EACA,KAAA,gBAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;CACd;CAEH,OAAO,GAAG,OAAgC;EACzC,OAAO,iBAAA;CACR;AACD;AAlCC,QAAA,SAAA,WAAA,CAAA,UAAS,MAAK,EAAE,EAAE,CAAA,GAAA,KAAA;AAsCnB,IAAkB,wBAAX,yBAAA,uBAAA;CACN,sBAAA,sBAAA,WAAA,OAAA;CACA,sBAAA,sBAAA,cAAA,MAAA;CACA,sBAAA,sBAAA,WAAA,MAAA;CACA,sBAAA,sBAAA,YAAA,MAAA;CACA,sBAAA,sBAAA,UAAA,MAAA;CACA,sBAAA,sBAAA,UAAA,KAAA;;AACD,EAAA,CAAA,CAAA"}
|
|
1
|
+
{"version":3,"file":"issue.js","names":[],"sources":["../../../../git/src/models/issue.ts"],"sourcesContent":["import { loggable } from '@gitlens/utils/decorators/log.js';\nimport type { RequireSome, RequireSomeWithProps } from '@gitlens/utils/types.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestState } from './issueOrPullRequest.js';\nimport type { ProviderReference } from './remoteProvider.js';\nimport type { RepositoryIdentityDescriptor } from './repositoryIdentities.js';\n\n/** Selects which issue states a read should include. `all` covers open + closed. */\nexport type IssueStateFilter = 'open' | 'closed' | 'all';\n\n/**\n * One relationship a filtered issue search can constrain on.\n *\n * The last two are USER-INDEPENDENT — they describe the issue, not the caller — and neither is a SCOPE: either\n * one alone matches every such issue on the host, so a search using one still needs a repository/org scope.\n */\nexport type IssueSearchRelationship =\n\t/** Authored by the current user. */\n\t| 'authored'\n\t/** Assigned to the current user. */\n\t| 'assigned'\n\t/** Mentions the current user. */\n\t| 'mentioned'\n\t/** Assigned to ANYONE. Excludes unassigned issues — see {@link IssueSearchCriteria.relationships}. */\n\t| 'any-assignee'\n\t/** Assigned to nobody. The complement of `any-assignee`, not a broadening of it. */\n\t| 'unassigned';\n\n/**\n * A field an issue read can be ordered by, provider-neutral. No provider expresses all of them: each read\n * validates the requested key against the capability its provider declares and refuses one it can't express\n * server-side, rather than serving a differently-ordered list under the name of the one that was asked for.\n *\n * `closed` and `resolved` are deliberately SEPARATE, not two names for the same date. Azure DevOps tracks both\n * and they differ (an issue can be closed without being resolved and resolved without being closed), and Jira\n * has no close date at all — it models the equivalent as `resolutiondate`. Collapsing them would force one of\n * those two providers to lie about which date it ordered by.\n */\nexport type IssueSortField =\n\t/** Creation date. */\n\t| 'created'\n\t/** Last activity. */\n\t| 'updated'\n\t/** Close date. Only where the provider orders by it server-side — notably NOT GitHub. */\n\t| 'closed'\n\t/** Resolution date (Jira `resolutiondate`, Azure `ResolvedDate`). Distinct from `closed`. */\n\t| 'resolved'\n\t/** Number of comments. */\n\t| 'comments'\n\t/** Reactions/upvotes. */\n\t| 'reactions'\n\t/** The provider's own priority field. */\n\t| 'priority'\n\t/** Due date. */\n\t| 'dueDate'\n\t/** Alphabetical by title. */\n\t| 'title';\n\n/**\n * How an issue read is ordered, as `field:direction` — the same shape `BranchSorting`/`TagSorting` already use,\n * so it is one serializable string a consumer can persist, compare, or bind straight to a setting or a dropdown.\n */\nexport type IssueSorting = `${IssueSortField}:asc` | `${IssueSortField}:desc`;\n\n/**\n * The order every issue read that HAS a default applies when the caller asks for none: most recently updated\n * first, which is what those reads served before ordering was an option.\n *\n * One definition rather than one per layer, because it is a promise made in several places at once — the criteria\n * model documents it, the GitHub query emits it, and the result-ceiling warning quotes it — and three copies would\n * be free to disagree about what \"the default\" is.\n *\n * Note it is not a default every read has, and the two layers differ. The API client's account-wide \"my issues\"\n * search (`GitHubApi.searchMyIssues`) never requested an order, so a direct caller omitting `sort` still gets\n * GitHub's relevance order — the default is opt-in there, to keep that read's already-shipped results unchanged.\n * The FACADE does apply it: GitHub declares `updated:desc` among its account-wide keys, so\n * `readAccountWideIssuesPage` resolves the omission to this default and forwards it, and that read's emitted query\n * gained a `sort:updated` qualifier it did not have before. A read whose surface cannot express even this key is\n * the only one the facade leaves in the provider's own order.\n */\nexport const defaultIssueSort: IssueSorting = 'updated:desc';\n\n/**\n * What a filtered issue search narrows on: a provider-neutral criteria set, translated to each provider's own\n * query language by its integration.\n *\n * Structured rather than a provider query string so a consumer can render filter chips and hide the ones a\n * provider can't express, and so the same criteria mean the same thing on a provider whose query language looks\n * nothing like GitHub's search syntax. Every field is validated against the provider's declared capability\n * BEFORE the read runs, all-or-nothing: a criterion the provider can't express server-side refuses the whole\n * read rather than serving a list that was never narrowed. Client-side narrowing is not an option — the dropped\n * items still counted toward the page and cursor the provider produced, so a post-filter would leave the items\n * describing a different result set than the paging that came with them.\n */\nexport interface IssueSearchCriteria {\n\t/**\n\t * Relationships to constrain on, OR-ed together (each becomes its own provider query, unioned and deduped).\n\t * Omitted means NO relationship constraint at all — every issue in scope, assigned or not — which requires a\n\t * repository/org scope.\n\t *\n\t * `any-assignee` and `unassigned` partition the scope between them and are mutually exclusive; passing both is\n\t * refused rather than silently dropping one. Note that \"all visible issues\" is the OMITTED case, NOT\n\t * `any-assignee`, which excludes unassigned issues.\n\t */\n\trelationships?: IssueSearchRelationship[];\n\t/**\n\t * Free text, matched by the provider's own relevance rules — NOT a substring match, and not a way to smuggle\n\t * qualifiers: a token that would read as one is dropped, as are quotes and control characters, so text can\n\t * never re-scope the search. The structured criteria are the qualifier channel.\n\t */\n\ttext?: string;\n\t/** Issue states to include. Omitted reads open issues only. */\n\tstate?: IssueStateFilter;\n\t/** Labels the issue must carry — AND-ed, matching GitHub's own `label:` semantics. */\n\tlabels?: string[];\n\tmilestone?: string;\n\t/** ISO date (`YYYY-MM-DD`). The most effective narrowing criterion by far on a large scope. */\n\tupdatedAfter?: string;\n\t/** ISO date (`YYYY-MM-DD`). */\n\tcreatedAfter?: string;\n\t/** Issues with no linked pull request. */\n\twithoutLinkedPullRequest?: boolean;\n\t/** Includes issues in archived repositories, which are excluded by default. */\n\tincludeArchived?: boolean;\n\t/**\n\t * How to order the results. Omitted means `updated:desc`, which is what this search has always served.\n\t *\n\t * Validated like every other criterion — all-or-nothing against {@link IssueSearchCapabilities.sorts}: a key\n\t * the provider can't express server-side refuses the WHOLE read rather than falling back to the default.\n\t * Serving another order is not a smaller error than serving a wider result: combined with the provider's\n\t * result ceiling it returns a different subset than was asked for, and the paging that comes with it describes\n\t * that other subset.\n\t *\n\t * Do not change it mid-pagination. A cursor carries the sort it was produced under, and threading it under a\n\t * different key is refused rather than serving a sequence with gaps and repeats: drop the cursor instead.\n\t */\n\tsort?: IssueSorting;\n}\n\n/**\n * Which {@link IssueSearchCriteria} fields a provider's filtered issue search can express server-side.\n *\n * An empty `relationships` with every flag false means the provider has NO filtered issue search — the read is\n * refused outright, so a consumer should offer neither the surface nor its chips.\n */\nexport interface IssueSearchCapabilities {\n\t/** Relationships the search can constrain on. Empty means the search itself is unsupported. */\n\trelationships: IssueSearchRelationship[];\n\ttext: boolean;\n\tlabels: boolean;\n\tmilestone: boolean;\n\tupdatedAfter: boolean;\n\tcreatedAfter: boolean;\n\twithoutLinkedPullRequest: boolean;\n\t/** Whether {@link IssueSearchCriteria.state} can select anything other than the provider's default (open). */\n\tstates: boolean;\n\t/**\n\t * Sort keys the search can express IN SERVER. Always contains at least `updated:desc` — the historical default\n\t * — when the search exists at all, so it is never empty for a usable surface; a provider without a filtered\n\t * search reports an empty `relationships`, which is already the signal that there is no surface to order.\n\t */\n\tsorts: IssueSorting[];\n}\n\n/**\n * The read that produced an issue row, as a key for `getIssueFieldPresence` (`@gitlens/integrations`). It names a\n * READ, not a field set: the same tag can carry different fields on different providers.\n */\nexport type IssueProjection =\n\t/** A single-item read. */\n\t| 'point'\n\t/** The filtered search. */\n\t| 'search'\n\t/** The account-wide list, and the trackers' \"my issues\". */\n\t| 'account'\n\t/** A repository-scoped list. */\n\t| 'repos'\n\t/** A tracker project's issues (`listIssueTrackerIssuesPage`, `getIssuesForProject`). */\n\t| 'project'\n\t/** The batch read. */\n\t| 'batch';\n\nexport interface IssueShape extends IssueOrPullRequest {\n\t/** `undefined` when the provider can't resolve the author, e.g. a deleted GitHub account */\n\tauthor: IssueMember | undefined;\n\tassignees: IssueMember[];\n\t/** The provider's own workflow state, when it exposes one beyond the normalized open/closed state. */\n\tproviderState?: IssueProviderState;\n\trepository?: IssueRepository;\n\tlabels?: IssueLabel[];\n\tbody?: string;\n\t/**\n\t * The syntax used by `body`. When omitted, consumers should treat `body` as Markdown — except that Azure DevOps\n\t * work items and Bitbucket Cloud issues carry HTML with no `bodyFormat` set.\n\t */\n\tbodyFormat?: IssueBodyFormat;\n\t/**\n\t * The provider field `body` was read from, for providers whose issues keep it in different fields: an Azure\n\t * DevOps work item's type decides it (Description, Repro Steps on a Bug, a custom field on an inherited process).\n\t * Absent when the provider has a single body field, or there is no body.\n\t */\n\tbodyField?: IssueBodyField;\n\tproject?: IssueProject;\n\tissueType?: string;\n\t/** An issue can belong to several Jira sprints; Azure reports one iteration. */\n\titerations?: IssueIteration[];\n\t/** The read that produced this row. Absent means unknown: a legacy row, or a provider or read not yet tagged. */\n\treadonly projection?: IssueProjection;\n}\n\n@loggable(i => i.id)\nexport class Issue implements IssueShape {\n\treadonly type = 'issue';\n\n\tconstructor(\n\t\tpublic readonly provider: ProviderReference,\n\t\tpublic readonly id: string,\n\t\tpublic readonly nodeId: string | undefined,\n\t\tpublic readonly title: string,\n\t\tpublic readonly url: string,\n\t\tpublic readonly createdDate: Date,\n\t\tpublic readonly updatedDate: Date,\n\t\tpublic readonly closed: boolean,\n\t\tpublic readonly state: IssueOrPullRequestState | undefined,\n\t\tpublic readonly author: IssueMember | undefined,\n\t\tpublic readonly assignees: IssueMember[],\n\t\tpublic readonly repository?: IssueRepository,\n\t\tpublic readonly closedDate?: Date,\n\t\tpublic readonly labels?: IssueLabel[],\n\t\tpublic readonly commentsCount?: number,\n\t\tpublic readonly thumbsUpCount?: number,\n\t\tpublic readonly body?: string,\n\t\tpublic readonly project?: IssueProject,\n\t\tpublic readonly number?: string,\n\t\tpublic readonly issueType?: string,\n\t\tpublic readonly providerState?: IssueProviderState,\n\t\tpublic readonly bodyFormat?: IssueBodyFormat,\n\t\tpublic readonly iterations?: IssueIteration[],\n\t\tpublic readonly projection?: IssueProjection,\n\t\tpublic readonly bodyField?: IssueBodyField,\n\t) {}\n\n\tstatic is(issue: unknown): issue is Issue {\n\t\treturn issue instanceof Issue;\n\t}\n}\n\nexport type IssueBodyFormat = 'markdown' | 'jira-wiki';\n\nexport interface IssueBodyField {\n\t/** The provider's id for the field, e.g. `Microsoft.VSTS.TCM.ReproSteps`. */\n\treadonly id: string;\n\t/** The field's display name, e.g. `Repro Steps`. */\n\treadonly name: string;\n}\n\nexport const enum RepositoryAccessLevel {\n\tAdmin = 100,\n\tMaintain = 40,\n\tWrite = 30,\n\tTriage = 20,\n\tRead = 10,\n\tNone = 0,\n}\n\nexport interface IssueLabel {\n\tcolor?: string;\n\tname: string;\n}\n\n/**\n * Sprint metadata is optional because some providers only report an iteration path. An absent `isActive`\n * means unknown, not inactive, so consumers must not use it to exclude those iterations by default.\n */\nexport interface IssueIteration {\n\t/**\n\t * A sprint id on Jira, the verbatim iteration path on Azure — so it is only meaningful within the provider's\n\t * project, never across providers. Unique at read time but NOT durable: Azure rewrites the path when an\n\t * iteration node is renamed or re-parented, so don't persist it as a long-lived id.\n\t */\n\tid: string;\n\tname: string;\n\tisActive?: boolean;\n\tstartDate?: Date;\n\tendDate?: Date;\n}\n\nexport interface IssueMember {\n\tid: string;\n\t/**\n\t * Absent when the provider exposes no display name for the member. Optional on purpose: a fallback string\n\t * invented here (`'unknown'`, `''`) can't be told apart from a real name downstream, so it can't be undone by a\n\t * consumer that needs to render something else — or nothing at all, as when the member feeds an AI prompt.\n\t * Each consumer picks its own presentation.\n\t */\n\tname?: string;\n\t/** The provider's handle for this person, when it has one — GitHub's login, Azure's `uniqueName` (a UPN,\n\t * so an email), Bitbucket's mutable `nickname`. Display/labelling only: it is neither guaranteed present\n\t * (GitLab's native mapper has none) nor a stable identity, so never key a match off it. */\n\tusername?: string;\n\tavatarUrl?: string;\n\turl?: string;\n}\n\nexport type IssueProviderState = {\n\tid?: string;\n\tname: string;\n\tcolor?: string;\n\t/** The provider's workflow category. `CANCELED` is abandoned work (Linear's canceled and duplicate states, Azure's\n\t * `Removed` category): closed like `DONE`, kept apart so a consumer can tell the two outcomes apart. */\n\tcategory?: 'TO_DO' | 'IN_PROGRESS' | 'DONE' | 'CANCELED';\n};\n\nexport interface IssueProject {\n\tid: string;\n\tname: string;\n\tresourceId: string;\n\tresourceName: string;\n}\n\nexport interface IssueRepository {\n\towner: string;\n\trepo: string;\n\taccessLevel?: RepositoryAccessLevel;\n\turl?: string;\n\tid?: string;\n}\n\nexport type IssueRepositoryIdentityDescriptor = RequireSomeWithProps<\n\tRequireSome<RepositoryIdentityDescriptor<string>, 'provider'>,\n\t'provider',\n\t'id' | 'domain' | 'repoDomain' | 'repoName'\n> &\n\tRequireSomeWithProps<RequireSome<RepositoryIdentityDescriptor<string>, 'remote'>, 'remote', 'domain'>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA+EA,MAAa,mBAAiC;AAmIvC,IAAM,QAAA,SAAN,MAAM,MAA4B;CAIvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CA3BjB,OAAgB;CAEhB,YACC,UACA,IACA,QACA,OACA,KACA,aACA,aACA,QACA,OACA,QACA,WACA,YACA,YACA,QACA,eACA,eACA,MACA,SACA,QACA,WACA,eACA,YACA,YACA,YACA,WACC;EAzBe,KAAA,WAAA;EACA,KAAA,KAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EACA,KAAA,cAAA;EACA,KAAA,cAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EACA,KAAA,SAAA;EACA,KAAA,YAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,SAAA;EACA,KAAA,gBAAA;EACA,KAAA,gBAAA;EACA,KAAA,OAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;EACA,KAAA,YAAA;EACA,KAAA,gBAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,YAAA;CACd;CAEH,OAAO,GAAG,OAAgC;EACzC,OAAO,iBAAA;CACR;AACD;AAnCC,QAAA,SAAA,WAAA,CAAA,UAAS,MAAK,EAAE,EAAE,CAAA,GAAA,KAAA;AA8CnB,IAAkB,wBAAX,yBAAA,uBAAA;CACN,sBAAA,sBAAA,WAAA,OAAA;CACA,sBAAA,sBAAA,cAAA,MAAA;CACA,sBAAA,sBAAA,WAAA,MAAA;CACA,sBAAA,sBAAA,YAAA,MAAA;CACA,sBAAA,sBAAA,UAAA,MAAA;CACA,sBAAA,sBAAA,UAAA,KAAA;;AACD,EAAA,CAAA,CAAA"}
|
|
@@ -155,6 +155,10 @@ function serializeIssue(value) {
|
|
|
155
155
|
thumbsUpCount: value.thumbsUpCount,
|
|
156
156
|
body: value.body,
|
|
157
157
|
bodyFormat: value.bodyFormat,
|
|
158
|
+
bodyField: value.bodyField == null ? void 0 : {
|
|
159
|
+
id: value.bodyField.id,
|
|
160
|
+
name: value.bodyField.name
|
|
161
|
+
},
|
|
158
162
|
iterations: value.iterations == null ? void 0 : value.iterations.map((iteration) => ({
|
|
159
163
|
id: iteration.id,
|
|
160
164
|
name: iteration.name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue.utils.js","names":[],"sources":["../../../../git/src/utils/issue.utils.ts"],"sourcesContent":["import type {\n\tIssue,\n\tIssueProviderState,\n\tIssueRepositoryIdentityDescriptor,\n\tIssueShape,\n\tIssueSortField,\n\tIssueSorting,\n} from '../models/issue.js';\nimport { defaultIssueSort } from '../models/issue.js';\n\n/**\n * How each sort field is read off an {@link IssueShape}, for the reads that MERGE several provider queries and so\n * have to order the union themselves instead of letting the provider do it.\n *\n * Only the fields the shape actually carries appear. `priority`, `dueDate` and `resolved` are absent because\n * `IssueOrPullRequest`/`IssueShape` doesn't model them: a provider may well order by them server-side on a\n * single-query read, but a merged one cannot — which is exactly what {@link getIssueComparator} returning\n * `undefined` lets its caller refuse, instead of concatenating unordered runs and calling them sorted.\n */\nconst issueSortValues: Partial<Record<IssueSortField, (issue: IssueShape) => number | Date | string | undefined>> = {\n\tcreated: i => i.createdDate,\n\tupdated: i => i.updatedDate,\n\tclosed: i => i.closedDate,\n\tcomments: i => i.commentsCount,\n\treactions: i => i.thumbsUpCount,\n\ttitle: i => i.title,\n};\n\n/**\n * The order a read that ALWAYS orders is being made under: the caller's key, or the facade's default.\n *\n * The one place `?? defaultIssueSort` may be written. Several things downstream must agree on the answer — the\n * emitted provider qualifier, the fingerprint sealed into a cursor, the comparator that re-sorts a merged page and\n * the ceiling warning that names the order — and re-deriving the fallback at each is how they come to disagree,\n * which is not cosmetic: a cursor sealed with a key its query wasn't built with makes the fingerprint check refuse\n * valid resumptions.\n *\n * Note this is for a surface that CAN always order. A read whose provider may not express even the default resolves\n * through `resolveIssueSort` instead, which can answer \"leave the provider's own order\".\n */\nexport function effectiveIssueSort(sort: IssueSorting | undefined): IssueSorting {\n\treturn sort ?? defaultIssueSort;\n}\n\n/**\n * A comparator for one sort key over normalized issues, or `undefined` when the field isn't derivable from an\n * {@link IssueShape} — the signal that the key is only honorable on a SINGLE-origin read, where the provider\n * already ordered the page.\n *\n * `@gitkraken/provider-apis` carries a twin of this over its own `Issue` shape (`src/issueSort.ts`), which it uses\n * for the merges it performs itself. It is not importable — the SDK does not export it, and it takes a different\n * shape — so the two are separate implementations of one rule, and the rule below is the part that must not drift.\n *\n * Missing values sort LAST in both directions. That is deliberately not the same as negating the descending\n * comparator (which would float them to the front when ascending), and it is why they're partitioned before any\n * arithmetic rather than mapped to an infinite sentinel: two missing values have to compare equal, and\n * `-Infinity - -Infinity` is `NaN`, which makes the comparator inconsistent and the resulting order\n * implementation-defined.\n *\n * Note it orders whatever it is handed. Sorting a page that was already capped does not make it the top N.\n *\n * KNOWN DIVERGENCE, `title` only: strings are compared by code unit, so `'B' < 'a'`. A single-scope read is\n * ordered by the provider's own database instead — case-insensitive and accent-aware — so the same `title:asc`\n * yields \"Banana, apple, ábaco\" from a merged page and \"ábaco, apple, Banana\" from one repository. Deliberately\n * NOT fixed with `localeCompare`: the SDK's twin comparator (which performs the merges core doesn't, notably\n * GitLab's repo-ids aggregate) compares by code unit, so switching here would make two paths of the SAME\n * multi-repo read disagree — a worse divergence than the one it closes, and it would break the parity test in\n * `issueSortCapabilities.test.ts`. Closing this properly means changing the rule on BOTH sides at once; until\n * then it is a cosmetic ordering difference on one key, not wrong data.\n */\nexport function getIssueComparator(sort: IssueSorting): ((a: IssueShape, b: IssueShape) => number) | undefined {\n\t// The union's shape guarantees both halves, so this needs no validation — only the cast tsc can't infer.\n\tconst [field, direction] = sort.split(':') as [IssueSortField, 'asc' | 'desc'];\n\tconst getValue = issueSortValues[field];\n\tif (getValue == null) return undefined;\n\n\treturn (a, b) => {\n\t\tconst left = toComparable(getValue(a));\n\t\tconst right = toComparable(getValue(b));\n\t\tif (left == null || right == null) {\n\t\t\tif (left == null && right == null) return 0;\n\t\t\treturn left == null ? 1 : -1;\n\t\t}\n\n\t\tconst ordered = left < right ? -1 : left > right ? 1 : 0;\n\t\treturn direction === 'asc' ? ordered : -ordered;\n\t};\n}\n\nfunction toComparable(value: number | Date | string | undefined): number | string | undefined {\n\treturn value instanceof Date ? value.getTime() : value;\n}\n\nexport function getRepositoryIdentityForIssue(issue: IssueShape | Issue): IssueRepositoryIdentityDescriptor {\n\tif (issue.repository == null) throw new Error('Missing repository');\n\n\treturn {\n\t\tremote: {\n\t\t\turl: issue.repository.url,\n\t\t\tdomain: issue.provider.domain,\n\t\t},\n\t\tname: `${issue.repository.owner}/${issue.repository.repo}`,\n\t\tprovider: {\n\t\t\tid: issue.provider.id,\n\t\t\tdomain: issue.provider.domain,\n\t\t\trepoDomain: issue.repository.owner,\n\t\t\trepoName: issue.repository.repo,\n\t\t\trepoOwnerDomain: issue.repository.owner,\n\t\t},\n\t};\n}\n\n/**\n * Deliberately leaves `projection` out: this copy drops `repository.accessLevel`, so a tag would have field presence\n * report the viewer's access as fetched when the copy no longer has it.\n */\nexport function serializeIssue(value: IssueShape): IssueShape {\n\tconst serialized: IssueShape = {\n\t\ttype: value.type,\n\t\tprovider: {\n\t\t\tid: value.provider.id,\n\t\t\tname: value.provider.name,\n\t\t\tdomain: value.provider.domain,\n\t\t\ticon: value.provider.icon,\n\t\t},\n\t\tid: value.id,\n\t\tnodeId: value.nodeId,\n\t\tissueType: value.issueType,\n\t\ttitle: value.title,\n\t\turl: value.url,\n\t\tcreatedDate: value.createdDate,\n\t\tupdatedDate: value.updatedDate,\n\t\tclosedDate: value.closedDate,\n\t\tclosed: value.closed,\n\t\tstate: value.state,\n\t\tproviderState:\n\t\t\tvalue.providerState == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.providerState.id,\n\t\t\t\t\t\tname: value.providerState.name,\n\t\t\t\t\t\tcolor: value.providerState.color,\n\t\t\t\t\t\tcategory: value.providerState.category,\n\t\t\t\t\t},\n\t\tauthor:\n\t\t\tvalue.author == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.author.id,\n\t\t\t\t\t\tname: value.author.name,\n\t\t\t\t\t\tusername: value.author.username,\n\t\t\t\t\t\tavatarUrl: value.author.avatarUrl,\n\t\t\t\t\t\turl: value.author.url,\n\t\t\t\t\t},\n\t\trepository:\n\t\t\tvalue.repository == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\towner: value.repository.owner,\n\t\t\t\t\t\trepo: value.repository.repo,\n\t\t\t\t\t\turl: value.repository.url,\n\t\t\t\t\t},\n\t\tproject:\n\t\t\tvalue.project == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.project.id,\n\t\t\t\t\t\tname: value.project.name,\n\t\t\t\t\t\tresourceId: value.project.resourceId,\n\t\t\t\t\t\tresourceName: value.project.resourceName,\n\t\t\t\t\t},\n\t\tassignees: value.assignees.map(assignee => ({\n\t\t\tid: assignee.id,\n\t\t\tname: assignee.name,\n\t\t\tusername: assignee.username,\n\t\t\tavatarUrl: assignee.avatarUrl,\n\t\t\turl: assignee.url,\n\t\t})),\n\t\tlabels:\n\t\t\tvalue.labels == null\n\t\t\t\t? undefined\n\t\t\t\t: value.labels.map(label => ({\n\t\t\t\t\t\tcolor: label.color,\n\t\t\t\t\t\tname: label.name,\n\t\t\t\t\t})),\n\t\tcommentsCount: value.commentsCount,\n\t\tthumbsUpCount: value.thumbsUpCount,\n\t\tbody: value.body,\n\t\tbodyFormat: value.bodyFormat,\n\t\titerations:\n\t\t\tvalue.iterations == null\n\t\t\t\t? undefined\n\t\t\t\t: value.iterations.map(iteration => ({\n\t\t\t\t\t\tid: iteration.id,\n\t\t\t\t\t\tname: iteration.name,\n\t\t\t\t\t\tisActive: iteration.isActive,\n\t\t\t\t\t\tstartDate: iteration.startDate,\n\t\t\t\t\t\tendDate: iteration.endDate,\n\t\t\t\t\t})),\n\t};\n\treturn serialized;\n}\n\n/** Whether a provider's workflow category ends the issue: done or abandoned (see {@link IssueProviderState.category}). */\nexport function isTerminalIssueStateCategory(category: IssueProviderState['category']): boolean {\n\treturn category === 'DONE' || category === 'CANCELED';\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"issue.utils.js","names":[],"sources":["../../../../git/src/utils/issue.utils.ts"],"sourcesContent":["import type {\n\tIssue,\n\tIssueProviderState,\n\tIssueRepositoryIdentityDescriptor,\n\tIssueShape,\n\tIssueSortField,\n\tIssueSorting,\n} from '../models/issue.js';\nimport { defaultIssueSort } from '../models/issue.js';\n\n/**\n * How each sort field is read off an {@link IssueShape}, for the reads that MERGE several provider queries and so\n * have to order the union themselves instead of letting the provider do it.\n *\n * Only the fields the shape actually carries appear. `priority`, `dueDate` and `resolved` are absent because\n * `IssueOrPullRequest`/`IssueShape` doesn't model them: a provider may well order by them server-side on a\n * single-query read, but a merged one cannot — which is exactly what {@link getIssueComparator} returning\n * `undefined` lets its caller refuse, instead of concatenating unordered runs and calling them sorted.\n */\nconst issueSortValues: Partial<Record<IssueSortField, (issue: IssueShape) => number | Date | string | undefined>> = {\n\tcreated: i => i.createdDate,\n\tupdated: i => i.updatedDate,\n\tclosed: i => i.closedDate,\n\tcomments: i => i.commentsCount,\n\treactions: i => i.thumbsUpCount,\n\ttitle: i => i.title,\n};\n\n/**\n * The order a read that ALWAYS orders is being made under: the caller's key, or the facade's default.\n *\n * The one place `?? defaultIssueSort` may be written. Several things downstream must agree on the answer — the\n * emitted provider qualifier, the fingerprint sealed into a cursor, the comparator that re-sorts a merged page and\n * the ceiling warning that names the order — and re-deriving the fallback at each is how they come to disagree,\n * which is not cosmetic: a cursor sealed with a key its query wasn't built with makes the fingerprint check refuse\n * valid resumptions.\n *\n * Note this is for a surface that CAN always order. A read whose provider may not express even the default resolves\n * through `resolveIssueSort` instead, which can answer \"leave the provider's own order\".\n */\nexport function effectiveIssueSort(sort: IssueSorting | undefined): IssueSorting {\n\treturn sort ?? defaultIssueSort;\n}\n\n/**\n * A comparator for one sort key over normalized issues, or `undefined` when the field isn't derivable from an\n * {@link IssueShape} — the signal that the key is only honorable on a SINGLE-origin read, where the provider\n * already ordered the page.\n *\n * `@gitkraken/provider-apis` carries a twin of this over its own `Issue` shape (`src/issueSort.ts`), which it uses\n * for the merges it performs itself. It is not importable — the SDK does not export it, and it takes a different\n * shape — so the two are separate implementations of one rule, and the rule below is the part that must not drift.\n *\n * Missing values sort LAST in both directions. That is deliberately not the same as negating the descending\n * comparator (which would float them to the front when ascending), and it is why they're partitioned before any\n * arithmetic rather than mapped to an infinite sentinel: two missing values have to compare equal, and\n * `-Infinity - -Infinity` is `NaN`, which makes the comparator inconsistent and the resulting order\n * implementation-defined.\n *\n * Note it orders whatever it is handed. Sorting a page that was already capped does not make it the top N.\n *\n * KNOWN DIVERGENCE, `title` only: strings are compared by code unit, so `'B' < 'a'`. A single-scope read is\n * ordered by the provider's own database instead — case-insensitive and accent-aware — so the same `title:asc`\n * yields \"Banana, apple, ábaco\" from a merged page and \"ábaco, apple, Banana\" from one repository. Deliberately\n * NOT fixed with `localeCompare`: the SDK's twin comparator (which performs the merges core doesn't, notably\n * GitLab's repo-ids aggregate) compares by code unit, so switching here would make two paths of the SAME\n * multi-repo read disagree — a worse divergence than the one it closes, and it would break the parity test in\n * `issueSortCapabilities.test.ts`. Closing this properly means changing the rule on BOTH sides at once; until\n * then it is a cosmetic ordering difference on one key, not wrong data.\n */\nexport function getIssueComparator(sort: IssueSorting): ((a: IssueShape, b: IssueShape) => number) | undefined {\n\t// The union's shape guarantees both halves, so this needs no validation — only the cast tsc can't infer.\n\tconst [field, direction] = sort.split(':') as [IssueSortField, 'asc' | 'desc'];\n\tconst getValue = issueSortValues[field];\n\tif (getValue == null) return undefined;\n\n\treturn (a, b) => {\n\t\tconst left = toComparable(getValue(a));\n\t\tconst right = toComparable(getValue(b));\n\t\tif (left == null || right == null) {\n\t\t\tif (left == null && right == null) return 0;\n\t\t\treturn left == null ? 1 : -1;\n\t\t}\n\n\t\tconst ordered = left < right ? -1 : left > right ? 1 : 0;\n\t\treturn direction === 'asc' ? ordered : -ordered;\n\t};\n}\n\nfunction toComparable(value: number | Date | string | undefined): number | string | undefined {\n\treturn value instanceof Date ? value.getTime() : value;\n}\n\nexport function getRepositoryIdentityForIssue(issue: IssueShape | Issue): IssueRepositoryIdentityDescriptor {\n\tif (issue.repository == null) throw new Error('Missing repository');\n\n\treturn {\n\t\tremote: {\n\t\t\turl: issue.repository.url,\n\t\t\tdomain: issue.provider.domain,\n\t\t},\n\t\tname: `${issue.repository.owner}/${issue.repository.repo}`,\n\t\tprovider: {\n\t\t\tid: issue.provider.id,\n\t\t\tdomain: issue.provider.domain,\n\t\t\trepoDomain: issue.repository.owner,\n\t\t\trepoName: issue.repository.repo,\n\t\t\trepoOwnerDomain: issue.repository.owner,\n\t\t},\n\t};\n}\n\n/**\n * Deliberately leaves `projection` out: this copy drops `repository.accessLevel`, so a tag would have field presence\n * report the viewer's access as fetched when the copy no longer has it.\n */\nexport function serializeIssue(value: IssueShape): IssueShape {\n\tconst serialized: IssueShape = {\n\t\ttype: value.type,\n\t\tprovider: {\n\t\t\tid: value.provider.id,\n\t\t\tname: value.provider.name,\n\t\t\tdomain: value.provider.domain,\n\t\t\ticon: value.provider.icon,\n\t\t},\n\t\tid: value.id,\n\t\tnodeId: value.nodeId,\n\t\tissueType: value.issueType,\n\t\ttitle: value.title,\n\t\turl: value.url,\n\t\tcreatedDate: value.createdDate,\n\t\tupdatedDate: value.updatedDate,\n\t\tclosedDate: value.closedDate,\n\t\tclosed: value.closed,\n\t\tstate: value.state,\n\t\tproviderState:\n\t\t\tvalue.providerState == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.providerState.id,\n\t\t\t\t\t\tname: value.providerState.name,\n\t\t\t\t\t\tcolor: value.providerState.color,\n\t\t\t\t\t\tcategory: value.providerState.category,\n\t\t\t\t\t},\n\t\tauthor:\n\t\t\tvalue.author == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.author.id,\n\t\t\t\t\t\tname: value.author.name,\n\t\t\t\t\t\tusername: value.author.username,\n\t\t\t\t\t\tavatarUrl: value.author.avatarUrl,\n\t\t\t\t\t\turl: value.author.url,\n\t\t\t\t\t},\n\t\trepository:\n\t\t\tvalue.repository == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\towner: value.repository.owner,\n\t\t\t\t\t\trepo: value.repository.repo,\n\t\t\t\t\t\turl: value.repository.url,\n\t\t\t\t\t},\n\t\tproject:\n\t\t\tvalue.project == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.project.id,\n\t\t\t\t\t\tname: value.project.name,\n\t\t\t\t\t\tresourceId: value.project.resourceId,\n\t\t\t\t\t\tresourceName: value.project.resourceName,\n\t\t\t\t\t},\n\t\tassignees: value.assignees.map(assignee => ({\n\t\t\tid: assignee.id,\n\t\t\tname: assignee.name,\n\t\t\tusername: assignee.username,\n\t\t\tavatarUrl: assignee.avatarUrl,\n\t\t\turl: assignee.url,\n\t\t})),\n\t\tlabels:\n\t\t\tvalue.labels == null\n\t\t\t\t? undefined\n\t\t\t\t: value.labels.map(label => ({\n\t\t\t\t\t\tcolor: label.color,\n\t\t\t\t\t\tname: label.name,\n\t\t\t\t\t})),\n\t\tcommentsCount: value.commentsCount,\n\t\tthumbsUpCount: value.thumbsUpCount,\n\t\tbody: value.body,\n\t\tbodyFormat: value.bodyFormat,\n\t\tbodyField: value.bodyField == null ? undefined : { id: value.bodyField.id, name: value.bodyField.name },\n\t\titerations:\n\t\t\tvalue.iterations == null\n\t\t\t\t? undefined\n\t\t\t\t: value.iterations.map(iteration => ({\n\t\t\t\t\t\tid: iteration.id,\n\t\t\t\t\t\tname: iteration.name,\n\t\t\t\t\t\tisActive: iteration.isActive,\n\t\t\t\t\t\tstartDate: iteration.startDate,\n\t\t\t\t\t\tendDate: iteration.endDate,\n\t\t\t\t\t})),\n\t};\n\treturn serialized;\n}\n\n/** Whether a provider's workflow category ends the issue: done or abandoned (see {@link IssueProviderState.category}). */\nexport function isTerminalIssueStateCategory(category: IssueProviderState['category']): boolean {\n\treturn category === 'DONE' || category === 'CANCELED';\n}\n"],"mappings":"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"}
|
|
@@ -49,7 +49,7 @@ export type PullRequestFieldGroup =
|
|
|
49
49
|
'stack';
|
|
50
50
|
/** A group is `fetched` only when every field in it is. */
|
|
51
51
|
export type IssueFieldGroup =
|
|
52
|
-
/** `body
|
|
52
|
+
/** `body`, and `bodyField` where the provider sets it. */
|
|
53
53
|
'description' |
|
|
54
54
|
/** `assignees`. */
|
|
55
55
|
'assignees' |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fieldPresence.js","names":[],"sources":["../../../../plus/integrations/src/fieldPresence.ts"],"sourcesContent":["import type { IssueProjection, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { PullRequestProjection, PullRequestShape } from '@gitlens/git/models/pullRequest.js';\nimport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n\tIssuesSelfManagedHostIntegrationId,\n} from './constants.js';\n\n/**\n * Whether a row's read fetched a field group.\n *\n * - `fetched`: the read asked for it and surfaces it, so an empty value means \"none\".\n * - `not-requested`: this read didn't ask for it, or asked but doesn't surface it faithfully. Ignore the value even\n * when one is present (some reads fill placeholders); another read may supply it.\n * - `unavailable`: this provider or host can't supply it on this read.\n */\nexport type FieldPresence = 'fetched' | 'not-requested' | 'unavailable';\n\n/** A group is `fetched` only when every field in it is. */\nexport type PullRequestFieldGroup =\n\t/** `body`. */\n\t| 'description'\n\t/** `latestReviews`. */\n\t| 'reviews'\n\t/**\n\t * `reviewRequests`: who is requested. Not each request's `isCodeOwner`, which only GitLens' own GitHub reads\n\t * report; every other row leaves it undefined.\n\t */\n\t| 'reviewRequests'\n\t/**\n\t * `reviewDecision`. Only GitHub reports one; elsewhere it is derived from the reviewers' states on the row, which\n\t * ignores the host's own approval rules, so it is never `fetched` there — derive it from `reviews` and\n\t * `reviewRequests` instead.\n\t */\n\t| 'reviewDecision'\n\t/** `assignees`. */\n\t| 'assignees'\n\t/** `statusCheckRollupState`, on the `PullRequest` class. */\n\t| 'checks'\n\t/** `mergeableState`. */\n\t| 'mergeable'\n\t/** `additions`, `deletions` and `filesChanged`. */\n\t| 'diffStats'\n\t/** `commitCount`, on the `PullRequest` class. */\n\t| 'commitCount'\n\t/** `commentsCount`. */\n\t| 'comments'\n\t/** `thumbsUpCount`. */\n\t| 'reactions'\n\t/** `viewerCanUpdate`, on the `PullRequest` class, and `repository.accessLevel`. */\n\t| 'access'\n\t/** `authoredByMe`, and the `viewer` it was matched against. */\n\t| 'authoredByMe'\n\t/** `stack`. */\n\t| 'stack';\n\n/** A group is `fetched` only when every field in it is. */\nexport type IssueFieldGroup =\n\t/** `body`. */\n\t| 'description'\n\t/** `assignees`. */\n\t| 'assignees'\n\t/** `labels`. */\n\t| 'labels'\n\t/** `commentsCount`. */\n\t| 'comments'\n\t/** `thumbsUpCount`. */\n\t| 'reactions'\n\t/** `repository.accessLevel`. */\n\t| 'access';\n\ntype Presence<G extends string> = Readonly<Record<G, FieldPresence>>;\ntype PresenceTable<P extends string, G extends string> = Readonly<Partial<Record<P, Presence<G>>>>;\n\nconst pullRequestFieldGroups: readonly PullRequestFieldGroup[] = [\n\t'description',\n\t'reviews',\n\t'reviewRequests',\n\t'reviewDecision',\n\t'assignees',\n\t'checks',\n\t'mergeable',\n\t'diffStats',\n\t'commitCount',\n\t'comments',\n\t'reactions',\n\t'access',\n\t'authoredByMe',\n\t'stack',\n];\n\nconst issueFieldGroups: readonly IssueFieldGroup[] = [\n\t'description',\n\t'assignees',\n\t'labels',\n\t'comments',\n\t'reactions',\n\t'access',\n];\n\n/** Every group in `groups` listed as neither `fetched` nor `unavailable` is `not-requested`. */\nfunction presenceOf<G extends string>(\n\tgroups: readonly G[],\n\tfetched: readonly G[],\n\tunavailable: readonly G[] = [],\n): Presence<G> {\n\treturn Object.freeze(\n\t\tObject.fromEntries(\n\t\t\tgroups.map(\n\t\t\t\tgroup =>\n\t\t\t\t\t[\n\t\t\t\t\t\tgroup,\n\t\t\t\t\t\tfetched.includes(group)\n\t\t\t\t\t\t\t? 'fetched'\n\t\t\t\t\t\t\t: unavailable.includes(group)\n\t\t\t\t\t\t\t\t? 'unavailable'\n\t\t\t\t\t\t\t\t: 'not-requested',\n\t\t\t\t\t] as const,\n\t\t\t),\n\t\t) as Record<G, FieldPresence>,\n\t);\n}\n\nfunction pullRequestPresenceOf(\n\tfetched: readonly PullRequestFieldGroup[],\n\tunavailable?: readonly PullRequestFieldGroup[],\n): Presence<PullRequestFieldGroup> {\n\treturn presenceOf(pullRequestFieldGroups, fetched, unavailable);\n}\n\nfunction issuePresenceOf(\n\tfetched: readonly IssueFieldGroup[],\n\tunavailable?: readonly IssueFieldGroup[],\n): Presence<IssueFieldGroup> {\n\treturn presenceOf(issueFieldGroups, fetched, unavailable);\n}\n\n/** GitLens' own lite fragment (`fromGitHubPullRequestLite`). `stack` is only selected against github.com. */\nconst gitHubLite = pullRequestPresenceOf(['description', 'stack']);\n\n/**\n * GitLens' own full fragment, which selects no reactions. The account-wide and batch reads return its rows as they\n * are, GitHub's own review decision included: none stays none, even while a review is requested.\n * `authoredByMe` needs the current account, which a read may fail to resolve, and the other reads never set it.\n */\nconst gitHubFull = pullRequestPresenceOf([\n\t'description',\n\t'reviews',\n\t'reviewRequests',\n\t'reviewDecision',\n\t'assignees',\n\t'checks',\n\t'mergeable',\n\t'diffStats',\n\t'commitCount',\n\t'comments',\n\t'access',\n\t'stack',\n]);\n\nconst gitHubPullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\tpoint: gitHubLite,\n\tsearch: gitHubFull,\n\t'search-summary': gitHubLite,\n\t'text-search': gitHubFull,\n\taccount: gitHubFull,\n\t// The lite fragment's own rows.\n\t'account-summary': gitHubLite,\n\t// provider-apis' own fragment and normalizer. Its review-state map has no DISMISSED, so a dismissed review is\n\t// dropped (and none carries a `commitOid`); it removes code-owner requests; it derives the review decision from\n\t// that list rather than reading GitHub's; and it doesn't read `repository.accessLevel`.\n\trepos: pullRequestPresenceOf([\n\t\t'description',\n\t\t'assignees',\n\t\t'checks',\n\t\t'mergeable',\n\t\t'diffStats',\n\t\t'commitCount',\n\t\t'comments',\n\t\t'reactions',\n\t]),\n\t// `summary` drops the `commits` subtree that carries both the check rollup and the commit count.\n\t'repos-summary': pullRequestPresenceOf([\n\t\t'description',\n\t\t'assignees',\n\t\t'mergeable',\n\t\t'diffStats',\n\t\t'comments',\n\t\t'reactions',\n\t]),\n\tbatch: gitHubFull,\n};\n\n/**\n * GitHub Enterprise Server rejects the stack selection, so a read that carries `stack` on github.com can't here.\n * provider-apis also gates check runs on the server version, which this table can't see, so its rollup on the\n * repository-scoped read may come from commit statuses alone.\n */\nconst gitHubEnterprisePullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = Object.fromEntries(\n\tObject.entries(gitHubPullRequests).map(([projection, presence]) => {\n\t\tconst enterprise: Record<PullRequestFieldGroup, FieldPresence> = { ...presence };\n\t\tif (enterprise.stack === 'fetched') {\n\t\t\tenterprise.stack = 'unavailable';\n\t\t}\n\t\tif (projection === 'repos') {\n\t\t\tenterprise.checks = 'not-requested';\n\t\t}\n\t\treturn [projection, Object.freeze(enterprise)];\n\t}),\n);\n\n/** GitLens' own issue fragment, which every native read selects with the body. */\nconst gitHubNativeIssue = presenceOf(issueFieldGroups, issueFieldGroups);\n\nconst gitHubIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: gitHubNativeIssue,\n\tsearch: gitHubNativeIssue,\n\taccount: gitHubNativeIssue,\n\t// provider-apis' own fragment selects neither the body nor the repository's `viewerPermission`.\n\trepos: presenceOf(issueFieldGroups, ['assignees', 'labels', 'comments', 'reactions']),\n\tbatch: gitHubNativeIssue,\n};\n\n/*\n * Every other provider. None of them reads the viewer's access (`repository.accessLevel` is absent on every row),\n * resolves `authoredByMe` reliably, or has stacks. provider-apis honors neither `summary` nor\n * `includeReviews` for them, so each `-summary` tag reads like its full one.\n */\n\n/**\n * GitLens' own GitLab reads: the point reads, and the free-text search. They select the description but leave it,\n * and everything else beyond the merge request's identity and refs, off the row.\n */\nconst gitLabOwn = pullRequestPresenceOf([], ['stack']);\n\n/**\n * provider-apis' merge request fragment and normalizer, which every other GitLab read goes through. It turns a null\n * count into 0: GitLab's schema makes `diffStatsSummary`, `commitCount` and `userNotesCount` nullable, so none is\n * promised — except that GitLab.com's comment count resolver answers 0 rather than null, which a self-managed\n * version may not.\n */\nfunction gitLabProviderApis(comments: boolean): Presence<PullRequestFieldGroup> {\n\treturn pullRequestPresenceOf(\n\t\t[\n\t\t\t'description',\n\t\t\t'reviews',\n\t\t\t'reviewRequests',\n\t\t\t'assignees',\n\t\t\t'checks',\n\t\t\t'mergeable',\n\t\t\t'reactions',\n\t\t\t...(comments ? (['comments'] as const) : []),\n\t\t],\n\t\t['stack'],\n\t);\n}\n\nfunction gitLabPullRequestsFor(\n\tproviderApis: Presence<PullRequestFieldGroup>,\n): PresenceTable<PullRequestProjection, PullRequestFieldGroup> {\n\treturn {\n\t\tpoint: gitLabOwn,\n\t\tsearch: providerApis,\n\t\t'text-search': gitLabOwn,\n\t\taccount: providerApis,\n\t\t'account-summary': providerApis,\n\t\trepos: providerApis,\n\t\t'repos-summary': providerApis,\n\t\tbatch: providerApis,\n\t};\n}\n\n/**\n * Azure DevOps pull requests have no assignees (both converters fill `assignees` with the reviewers, which `reviews` and\n * `reviewRequests` already hold) and no reactions. Neither converter reads checks or counts.\n */\nconst azureUnavailable: readonly PullRequestFieldGroup[] = ['assignees', 'reactions', 'stack'];\n/** Every list read: Azure truncates a listed pull request's description (to 400 characters). */\nconst azureListed = pullRequestPresenceOf(['reviews', 'reviewRequests', 'mergeable'], azureUnavailable);\n/** The batch reads fetch each pull request by id, which returns the whole description. */\nconst azureById = pullRequestPresenceOf(['description', 'reviews', 'reviewRequests', 'mergeable'], azureUnavailable);\n\nconst azurePullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\t// GitLens' own converter, which leaves the description off the row.\n\tpoint: azureListed,\n\tsearch: azureListed,\n\t'text-search': azureListed,\n\taccount: azureListed,\n\t'account-summary': azureListed,\n\trepos: azureListed,\n\t'repos-summary': azureListed,\n\tbatch: azureById,\n};\n\nconst azureServerPullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\t...azurePullRequests,\n\t'search-summary': azureListed,\n};\n\n/**\n * Bitbucket pull requests have no assignees or reactions. Neither provider-apis nor GitLens reads their build\n * statuses or mergeability: provider-apis fills a literal Mergeable, and GitLens' own converters leave it unset.\n */\nconst bitbucketUnavailable: readonly PullRequestFieldGroup[] = ['assignees', 'reactions', 'stack'];\nconst bitbucketProviderApis = pullRequestPresenceOf(\n\t['description', 'reviews', 'reviewRequests', 'comments'],\n\tbitbucketUnavailable,\n);\n\nconst bitbucketPullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\t// GitLens' own converter, which drops the comment count. The commit read selects `+values.*`, which isn't\n\t// known to bring the participants the reviews come from, as the other two point reads do.\n\tpoint: pullRequestPresenceOf(['description'], bitbucketUnavailable),\n\tsearch: bitbucketProviderApis,\n\t'text-search': bitbucketProviderApis,\n\taccount: bitbucketProviderApis,\n\t'account-summary': bitbucketProviderApis,\n\trepos: bitbucketProviderApis,\n\t'repos-summary': bitbucketProviderApis,\n\t// GitLens' own converter over reads that do select the participants.\n\tbatch: pullRequestPresenceOf(['description', 'reviews', 'reviewRequests'], bitbucketUnavailable),\n};\n\n/** provider-apis' and GitLens' own Data Center converters read the same fields. */\nconst bitbucketServerAll = pullRequestPresenceOf(\n\t['description', 'reviews', 'reviewRequests', 'comments'],\n\tbitbucketUnavailable,\n);\nconst bitbucketServerPullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\tpoint: bitbucketServerAll,\n\tsearch: bitbucketServerAll,\n\t'search-summary': bitbucketServerAll,\n\t'text-search': bitbucketServerAll,\n\taccount: bitbucketServerAll,\n\t'account-summary': bitbucketServerAll,\n\trepos: bitbucketServerAll,\n\t'repos-summary': bitbucketServerAll,\n\tbatch: bitbucketServerAll,\n};\n\n/**\n * provider-apis' GitLab issue reads (GraphQL, and REST for the account-wide one). The repository's access isn't read.\n * The GraphQL comment count is nullable like the merge request's, and passed through, so only GitLab.com's is\n * promised; the REST one is always a number.\n */\nfunction gitLabIssuesFor(comments: boolean): PresenceTable<IssueProjection, IssueFieldGroup> {\n\tconst graphQL = issuePresenceOf([\n\t\t'description',\n\t\t'assignees',\n\t\t'labels',\n\t\t'reactions',\n\t\t...(comments ? (['comments'] as const) : []),\n\t]);\n\treturn {\n\t\tpoint: graphQL,\n\t\taccount: issuePresenceOf(['description', 'assignees', 'labels', 'comments', 'reactions']),\n\t\trepos: graphQL,\n\t\tbatch: graphQL,\n\t};\n}\n\n/**\n * Azure DevOps work items have no reactions (provider-apis' converter fills 0; GitLens' leaves it unset) and belong to\n * a project, not a repository.\n */\nconst azureWorkItemUnavailable: readonly IssueFieldGroup[] = ['reactions', 'access'];\n/** provider-apis' work item converter, and GitLens' copy of it for the batch read. */\nconst azureWorkItem = issuePresenceOf(['description', 'assignees', 'labels', 'comments'], azureWorkItemUnavailable);\n/** GitLens' own work item converter, which drops the tags. */\nconst azureOwnWorkItem = issuePresenceOf(['description', 'assignees', 'comments'], azureWorkItemUnavailable);\n\nconst azureIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: azureOwnWorkItem,\n\taccount: azureWorkItem,\n\trepos: azureWorkItem,\n\tbatch: azureWorkItem,\n};\n\nconst azureServerIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\t...azureIssues,\n\tsearch: azureOwnWorkItem,\n};\n\n/**\n * Bitbucket Cloud's legacy issues, which only the host's own `getIssue` and `searchMyIssues` read. They have no\n * labels, their votes aren't reactions, and neither a comment count nor the repository's access is read.\n */\nconst bitbucketIssue = issuePresenceOf(['description', 'assignees'], ['labels', 'reactions']);\nconst bitbucketIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: bitbucketIssue,\n\taccount: bitbucketIssue,\n};\n\n/**\n * Tracker issues belong to no repository, and a tracker's votes aren't reactions (and read 0 where voting is off), so\n * neither group can be supplied.\n */\nconst trackerUnavailable: readonly IssueFieldGroup[] = ['reactions', 'access'];\n/**\n * provider-apis' Jira converter counts the comments embedded in the issue, which Jira may cap, rather than reading\n * their total.\n */\nconst jiraProviderApis = issuePresenceOf(['description', 'assignees', 'labels'], trackerUnavailable);\n\nconst jiraIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: jiraProviderApis,\n\taccount: jiraProviderApis,\n\tproject: jiraProviderApis,\n\t// GitLens' own by-key read, which reads the comment total.\n\tbatch: issuePresenceOf(['description', 'assignees', 'labels', 'comments'], trackerUnavailable),\n};\n\nconst jiraServerIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\t...jiraIssues,\n\tbatch: jiraProviderApis,\n};\n\n/** provider-apis' Linear fragment selects no labels (it fills an empty list), comments or reactions. */\nconst linearIssue = issuePresenceOf(['description', 'assignees'], ['access']);\nconst linearIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: linearIssue,\n\taccount: linearIssue,\n\tproject: linearIssue,\n\tbatch: linearIssue,\n};\n\n/** A Trello card's description is never read; its members are its assignees. */\nconst trelloCard = issuePresenceOf(['assignees', 'labels', 'comments'], trackerUnavailable);\nconst trelloIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: trelloCard,\n\tproject: trelloCard,\n};\n\nconst pullRequestPresence = new Map<string, PresenceTable<PullRequestProjection, PullRequestFieldGroup>>([\n\t[GitCloudHostIntegrationId.GitHub, gitHubPullRequests],\n\t[GitSelfManagedHostIntegrationId.CloudGitHubEnterprise, gitHubEnterprisePullRequests],\n\t[GitCloudHostIntegrationId.GitLab, gitLabPullRequestsFor(gitLabProviderApis(true))],\n\t[GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted, gitLabPullRequestsFor(gitLabProviderApis(false))],\n\t[GitCloudHostIntegrationId.AzureDevOps, azurePullRequests],\n\t[GitSelfManagedHostIntegrationId.AzureDevOpsServer, azureServerPullRequests],\n\t[GitCloudHostIntegrationId.Bitbucket, bitbucketPullRequests],\n\t[GitSelfManagedHostIntegrationId.BitbucketServer, bitbucketServerPullRequests],\n]);\n\nconst issuePresence = new Map<string, PresenceTable<IssueProjection, IssueFieldGroup>>([\n\t[GitCloudHostIntegrationId.GitHub, gitHubIssues],\n\t[GitSelfManagedHostIntegrationId.CloudGitHubEnterprise, gitHubIssues],\n\t[GitCloudHostIntegrationId.GitLab, gitLabIssuesFor(true)],\n\t[GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted, gitLabIssuesFor(false)],\n\t[GitCloudHostIntegrationId.AzureDevOps, azureIssues],\n\t[GitSelfManagedHostIntegrationId.AzureDevOpsServer, azureServerIssues],\n\t[GitCloudHostIntegrationId.Bitbucket, bitbucketIssues],\n\t[IssuesCloudHostIntegrationId.Jira, jiraIssues],\n\t[IssuesSelfManagedHostIntegrationId.JiraServer, jiraServerIssues],\n\t[IssuesCloudHostIntegrationId.Linear, linearIssues],\n\t[IssuesCloudHostIntegrationId.Trello, trelloIssues],\n]);\n\nfunction lookup<P extends string, G extends string>(\n\ttables: ReadonlyMap<string, PresenceTable<P, G>>,\n\tproviderId: string,\n\tprojection: P | undefined,\n): Presence<G> | undefined {\n\tif (projection == null) return undefined;\n\n\tconst table = tables.get(providerId);\n\t// `hasOwn` so a projection read off an untrusted row can't resolve to an `Object.prototype` member.\n\treturn table != null && Object.hasOwn(table, projection) ? table[projection] : undefined;\n}\n\n/**\n * Which field groups the read that produced `pr` fetched, keyed by its provider and {@link PullRequestShape.projection}.\n * `undefined` when either is unknown or the provider has no table yet — then nothing can be said about any field.\n */\nexport function getPullRequestFieldPresence(\n\tpr: PullRequestShape,\n): Readonly<Record<PullRequestFieldGroup, FieldPresence>> | undefined {\n\treturn lookup(pullRequestPresence, pr.provider.id, pr.projection);\n}\n\n/**\n * Which field groups the read that produced `issue` fetched, keyed by its provider and {@link IssueShape.projection}.\n * `undefined` when either is unknown or the provider has no table yet — then nothing can be said about any field.\n */\nexport function getIssueFieldPresence(issue: IssueShape): Readonly<Record<IssueFieldGroup, FieldPresence>> | undefined {\n\treturn lookup(issuePresence, issue.provider.id, issue.projection);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fieldPresence.js","names":[],"sources":["../../../../plus/integrations/src/fieldPresence.ts"],"sourcesContent":["import type { IssueProjection, IssueShape } from '@gitlens/git/models/issue.js';\nimport type { PullRequestProjection, PullRequestShape } from '@gitlens/git/models/pullRequest.js';\nimport {\n\tGitCloudHostIntegrationId,\n\tGitSelfManagedHostIntegrationId,\n\tIssuesCloudHostIntegrationId,\n\tIssuesSelfManagedHostIntegrationId,\n} from './constants.js';\n\n/**\n * Whether a row's read fetched a field group.\n *\n * - `fetched`: the read asked for it and surfaces it, so an empty value means \"none\".\n * - `not-requested`: this read didn't ask for it, or asked but doesn't surface it faithfully. Ignore the value even\n * when one is present (some reads fill placeholders); another read may supply it.\n * - `unavailable`: this provider or host can't supply it on this read.\n */\nexport type FieldPresence = 'fetched' | 'not-requested' | 'unavailable';\n\n/** A group is `fetched` only when every field in it is. */\nexport type PullRequestFieldGroup =\n\t/** `body`. */\n\t| 'description'\n\t/** `latestReviews`. */\n\t| 'reviews'\n\t/**\n\t * `reviewRequests`: who is requested. Not each request's `isCodeOwner`, which only GitLens' own GitHub reads\n\t * report; every other row leaves it undefined.\n\t */\n\t| 'reviewRequests'\n\t/**\n\t * `reviewDecision`. Only GitHub reports one; elsewhere it is derived from the reviewers' states on the row, which\n\t * ignores the host's own approval rules, so it is never `fetched` there — derive it from `reviews` and\n\t * `reviewRequests` instead.\n\t */\n\t| 'reviewDecision'\n\t/** `assignees`. */\n\t| 'assignees'\n\t/** `statusCheckRollupState`, on the `PullRequest` class. */\n\t| 'checks'\n\t/** `mergeableState`. */\n\t| 'mergeable'\n\t/** `additions`, `deletions` and `filesChanged`. */\n\t| 'diffStats'\n\t/** `commitCount`, on the `PullRequest` class. */\n\t| 'commitCount'\n\t/** `commentsCount`. */\n\t| 'comments'\n\t/** `thumbsUpCount`. */\n\t| 'reactions'\n\t/** `viewerCanUpdate`, on the `PullRequest` class, and `repository.accessLevel`. */\n\t| 'access'\n\t/** `authoredByMe`, and the `viewer` it was matched against. */\n\t| 'authoredByMe'\n\t/** `stack`. */\n\t| 'stack';\n\n/** A group is `fetched` only when every field in it is. */\nexport type IssueFieldGroup =\n\t/** `body`, and `bodyField` where the provider sets it. */\n\t| 'description'\n\t/** `assignees`. */\n\t| 'assignees'\n\t/** `labels`. */\n\t| 'labels'\n\t/** `commentsCount`. */\n\t| 'comments'\n\t/** `thumbsUpCount`. */\n\t| 'reactions'\n\t/** `repository.accessLevel`. */\n\t| 'access';\n\ntype Presence<G extends string> = Readonly<Record<G, FieldPresence>>;\ntype PresenceTable<P extends string, G extends string> = Readonly<Partial<Record<P, Presence<G>>>>;\n\nconst pullRequestFieldGroups: readonly PullRequestFieldGroup[] = [\n\t'description',\n\t'reviews',\n\t'reviewRequests',\n\t'reviewDecision',\n\t'assignees',\n\t'checks',\n\t'mergeable',\n\t'diffStats',\n\t'commitCount',\n\t'comments',\n\t'reactions',\n\t'access',\n\t'authoredByMe',\n\t'stack',\n];\n\nconst issueFieldGroups: readonly IssueFieldGroup[] = [\n\t'description',\n\t'assignees',\n\t'labels',\n\t'comments',\n\t'reactions',\n\t'access',\n];\n\n/** Every group in `groups` listed as neither `fetched` nor `unavailable` is `not-requested`. */\nfunction presenceOf<G extends string>(\n\tgroups: readonly G[],\n\tfetched: readonly G[],\n\tunavailable: readonly G[] = [],\n): Presence<G> {\n\treturn Object.freeze(\n\t\tObject.fromEntries(\n\t\t\tgroups.map(\n\t\t\t\tgroup =>\n\t\t\t\t\t[\n\t\t\t\t\t\tgroup,\n\t\t\t\t\t\tfetched.includes(group)\n\t\t\t\t\t\t\t? 'fetched'\n\t\t\t\t\t\t\t: unavailable.includes(group)\n\t\t\t\t\t\t\t\t? 'unavailable'\n\t\t\t\t\t\t\t\t: 'not-requested',\n\t\t\t\t\t] as const,\n\t\t\t),\n\t\t) as Record<G, FieldPresence>,\n\t);\n}\n\nfunction pullRequestPresenceOf(\n\tfetched: readonly PullRequestFieldGroup[],\n\tunavailable?: readonly PullRequestFieldGroup[],\n): Presence<PullRequestFieldGroup> {\n\treturn presenceOf(pullRequestFieldGroups, fetched, unavailable);\n}\n\nfunction issuePresenceOf(\n\tfetched: readonly IssueFieldGroup[],\n\tunavailable?: readonly IssueFieldGroup[],\n): Presence<IssueFieldGroup> {\n\treturn presenceOf(issueFieldGroups, fetched, unavailable);\n}\n\n/** GitLens' own lite fragment (`fromGitHubPullRequestLite`). `stack` is only selected against github.com. */\nconst gitHubLite = pullRequestPresenceOf(['description', 'stack']);\n\n/**\n * GitLens' own full fragment, which selects no reactions. The account-wide and batch reads return its rows as they\n * are, GitHub's own review decision included: none stays none, even while a review is requested.\n * `authoredByMe` needs the current account, which a read may fail to resolve, and the other reads never set it.\n */\nconst gitHubFull = pullRequestPresenceOf([\n\t'description',\n\t'reviews',\n\t'reviewRequests',\n\t'reviewDecision',\n\t'assignees',\n\t'checks',\n\t'mergeable',\n\t'diffStats',\n\t'commitCount',\n\t'comments',\n\t'access',\n\t'stack',\n]);\n\nconst gitHubPullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\tpoint: gitHubLite,\n\tsearch: gitHubFull,\n\t'search-summary': gitHubLite,\n\t'text-search': gitHubFull,\n\taccount: gitHubFull,\n\t// The lite fragment's own rows.\n\t'account-summary': gitHubLite,\n\t// provider-apis' own fragment and normalizer. Its review-state map has no DISMISSED, so a dismissed review is\n\t// dropped (and none carries a `commitOid`); it removes code-owner requests; it derives the review decision from\n\t// that list rather than reading GitHub's; and it doesn't read `repository.accessLevel`.\n\trepos: pullRequestPresenceOf([\n\t\t'description',\n\t\t'assignees',\n\t\t'checks',\n\t\t'mergeable',\n\t\t'diffStats',\n\t\t'commitCount',\n\t\t'comments',\n\t\t'reactions',\n\t]),\n\t// `summary` drops the `commits` subtree that carries both the check rollup and the commit count.\n\t'repos-summary': pullRequestPresenceOf([\n\t\t'description',\n\t\t'assignees',\n\t\t'mergeable',\n\t\t'diffStats',\n\t\t'comments',\n\t\t'reactions',\n\t]),\n\tbatch: gitHubFull,\n};\n\n/**\n * GitHub Enterprise Server rejects the stack selection, so a read that carries `stack` on github.com can't here.\n * provider-apis also gates check runs on the server version, which this table can't see, so its rollup on the\n * repository-scoped read may come from commit statuses alone.\n */\nconst gitHubEnterprisePullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = Object.fromEntries(\n\tObject.entries(gitHubPullRequests).map(([projection, presence]) => {\n\t\tconst enterprise: Record<PullRequestFieldGroup, FieldPresence> = { ...presence };\n\t\tif (enterprise.stack === 'fetched') {\n\t\t\tenterprise.stack = 'unavailable';\n\t\t}\n\t\tif (projection === 'repos') {\n\t\t\tenterprise.checks = 'not-requested';\n\t\t}\n\t\treturn [projection, Object.freeze(enterprise)];\n\t}),\n);\n\n/** GitLens' own issue fragment, which every native read selects with the body. */\nconst gitHubNativeIssue = presenceOf(issueFieldGroups, issueFieldGroups);\n\nconst gitHubIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: gitHubNativeIssue,\n\tsearch: gitHubNativeIssue,\n\taccount: gitHubNativeIssue,\n\t// provider-apis' own fragment selects neither the body nor the repository's `viewerPermission`.\n\trepos: presenceOf(issueFieldGroups, ['assignees', 'labels', 'comments', 'reactions']),\n\tbatch: gitHubNativeIssue,\n};\n\n/*\n * Every other provider. None of them reads the viewer's access (`repository.accessLevel` is absent on every row),\n * resolves `authoredByMe` reliably, or has stacks. provider-apis honors neither `summary` nor\n * `includeReviews` for them, so each `-summary` tag reads like its full one.\n */\n\n/**\n * GitLens' own GitLab reads: the point reads, and the free-text search. They select the description but leave it,\n * and everything else beyond the merge request's identity and refs, off the row.\n */\nconst gitLabOwn = pullRequestPresenceOf([], ['stack']);\n\n/**\n * provider-apis' merge request fragment and normalizer, which every other GitLab read goes through. It turns a null\n * count into 0: GitLab's schema makes `diffStatsSummary`, `commitCount` and `userNotesCount` nullable, so none is\n * promised — except that GitLab.com's comment count resolver answers 0 rather than null, which a self-managed\n * version may not.\n */\nfunction gitLabProviderApis(comments: boolean): Presence<PullRequestFieldGroup> {\n\treturn pullRequestPresenceOf(\n\t\t[\n\t\t\t'description',\n\t\t\t'reviews',\n\t\t\t'reviewRequests',\n\t\t\t'assignees',\n\t\t\t'checks',\n\t\t\t'mergeable',\n\t\t\t'reactions',\n\t\t\t...(comments ? (['comments'] as const) : []),\n\t\t],\n\t\t['stack'],\n\t);\n}\n\nfunction gitLabPullRequestsFor(\n\tproviderApis: Presence<PullRequestFieldGroup>,\n): PresenceTable<PullRequestProjection, PullRequestFieldGroup> {\n\treturn {\n\t\tpoint: gitLabOwn,\n\t\tsearch: providerApis,\n\t\t'text-search': gitLabOwn,\n\t\taccount: providerApis,\n\t\t'account-summary': providerApis,\n\t\trepos: providerApis,\n\t\t'repos-summary': providerApis,\n\t\tbatch: providerApis,\n\t};\n}\n\n/**\n * Azure DevOps pull requests have no assignees (both converters fill `assignees` with the reviewers, which `reviews` and\n * `reviewRequests` already hold) and no reactions. Neither converter reads checks or counts.\n */\nconst azureUnavailable: readonly PullRequestFieldGroup[] = ['assignees', 'reactions', 'stack'];\n/** Every list read: Azure truncates a listed pull request's description (to 400 characters). */\nconst azureListed = pullRequestPresenceOf(['reviews', 'reviewRequests', 'mergeable'], azureUnavailable);\n/** The batch reads fetch each pull request by id, which returns the whole description. */\nconst azureById = pullRequestPresenceOf(['description', 'reviews', 'reviewRequests', 'mergeable'], azureUnavailable);\n\nconst azurePullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\t// GitLens' own converter, which leaves the description off the row.\n\tpoint: azureListed,\n\tsearch: azureListed,\n\t'text-search': azureListed,\n\taccount: azureListed,\n\t'account-summary': azureListed,\n\trepos: azureListed,\n\t'repos-summary': azureListed,\n\tbatch: azureById,\n};\n\nconst azureServerPullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\t...azurePullRequests,\n\t'search-summary': azureListed,\n};\n\n/**\n * Bitbucket pull requests have no assignees or reactions. Neither provider-apis nor GitLens reads their build\n * statuses or mergeability: provider-apis fills a literal Mergeable, and GitLens' own converters leave it unset.\n */\nconst bitbucketUnavailable: readonly PullRequestFieldGroup[] = ['assignees', 'reactions', 'stack'];\nconst bitbucketProviderApis = pullRequestPresenceOf(\n\t['description', 'reviews', 'reviewRequests', 'comments'],\n\tbitbucketUnavailable,\n);\n\nconst bitbucketPullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\t// GitLens' own converter, which drops the comment count. The commit read selects `+values.*`, which isn't\n\t// known to bring the participants the reviews come from, as the other two point reads do.\n\tpoint: pullRequestPresenceOf(['description'], bitbucketUnavailable),\n\tsearch: bitbucketProviderApis,\n\t'text-search': bitbucketProviderApis,\n\taccount: bitbucketProviderApis,\n\t'account-summary': bitbucketProviderApis,\n\trepos: bitbucketProviderApis,\n\t'repos-summary': bitbucketProviderApis,\n\t// GitLens' own converter over reads that do select the participants.\n\tbatch: pullRequestPresenceOf(['description', 'reviews', 'reviewRequests'], bitbucketUnavailable),\n};\n\n/** provider-apis' and GitLens' own Data Center converters read the same fields. */\nconst bitbucketServerAll = pullRequestPresenceOf(\n\t['description', 'reviews', 'reviewRequests', 'comments'],\n\tbitbucketUnavailable,\n);\nconst bitbucketServerPullRequests: PresenceTable<PullRequestProjection, PullRequestFieldGroup> = {\n\tpoint: bitbucketServerAll,\n\tsearch: bitbucketServerAll,\n\t'search-summary': bitbucketServerAll,\n\t'text-search': bitbucketServerAll,\n\taccount: bitbucketServerAll,\n\t'account-summary': bitbucketServerAll,\n\trepos: bitbucketServerAll,\n\t'repos-summary': bitbucketServerAll,\n\tbatch: bitbucketServerAll,\n};\n\n/**\n * provider-apis' GitLab issue reads (GraphQL, and REST for the account-wide one). The repository's access isn't read.\n * The GraphQL comment count is nullable like the merge request's, and passed through, so only GitLab.com's is\n * promised; the REST one is always a number.\n */\nfunction gitLabIssuesFor(comments: boolean): PresenceTable<IssueProjection, IssueFieldGroup> {\n\tconst graphQL = issuePresenceOf([\n\t\t'description',\n\t\t'assignees',\n\t\t'labels',\n\t\t'reactions',\n\t\t...(comments ? (['comments'] as const) : []),\n\t]);\n\treturn {\n\t\tpoint: graphQL,\n\t\taccount: issuePresenceOf(['description', 'assignees', 'labels', 'comments', 'reactions']),\n\t\trepos: graphQL,\n\t\tbatch: graphQL,\n\t};\n}\n\n/**\n * Azure DevOps work items have no reactions (provider-apis' converter fills 0; GitLens' leaves it unset) and belong to\n * a project, not a repository.\n */\nconst azureWorkItemUnavailable: readonly IssueFieldGroup[] = ['reactions', 'access'];\n/** provider-apis' work item converter, and GitLens' copy of it for the batch read. */\nconst azureWorkItem = issuePresenceOf(['description', 'assignees', 'labels', 'comments'], azureWorkItemUnavailable);\n/** GitLens' own work item converter, which drops the tags. */\nconst azureOwnWorkItem = issuePresenceOf(['description', 'assignees', 'comments'], azureWorkItemUnavailable);\n\nconst azureIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: azureOwnWorkItem,\n\taccount: azureWorkItem,\n\trepos: azureWorkItem,\n\tbatch: azureWorkItem,\n};\n\nconst azureServerIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\t...azureIssues,\n\tsearch: azureOwnWorkItem,\n};\n\n/**\n * Bitbucket Cloud's legacy issues, which only the host's own `getIssue` and `searchMyIssues` read. They have no\n * labels, their votes aren't reactions, and neither a comment count nor the repository's access is read.\n */\nconst bitbucketIssue = issuePresenceOf(['description', 'assignees'], ['labels', 'reactions']);\nconst bitbucketIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: bitbucketIssue,\n\taccount: bitbucketIssue,\n};\n\n/**\n * Tracker issues belong to no repository, and a tracker's votes aren't reactions (and read 0 where voting is off), so\n * neither group can be supplied.\n */\nconst trackerUnavailable: readonly IssueFieldGroup[] = ['reactions', 'access'];\n/**\n * provider-apis' Jira converter counts the comments embedded in the issue, which Jira may cap, rather than reading\n * their total.\n */\nconst jiraProviderApis = issuePresenceOf(['description', 'assignees', 'labels'], trackerUnavailable);\n\nconst jiraIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: jiraProviderApis,\n\taccount: jiraProviderApis,\n\tproject: jiraProviderApis,\n\t// GitLens' own by-key read, which reads the comment total.\n\tbatch: issuePresenceOf(['description', 'assignees', 'labels', 'comments'], trackerUnavailable),\n};\n\nconst jiraServerIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\t...jiraIssues,\n\tbatch: jiraProviderApis,\n};\n\n/** provider-apis' Linear fragment selects no labels (it fills an empty list), comments or reactions. */\nconst linearIssue = issuePresenceOf(['description', 'assignees'], ['access']);\nconst linearIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: linearIssue,\n\taccount: linearIssue,\n\tproject: linearIssue,\n\tbatch: linearIssue,\n};\n\n/** A Trello card's description is never read; its members are its assignees. */\nconst trelloCard = issuePresenceOf(['assignees', 'labels', 'comments'], trackerUnavailable);\nconst trelloIssues: PresenceTable<IssueProjection, IssueFieldGroup> = {\n\tpoint: trelloCard,\n\tproject: trelloCard,\n};\n\nconst pullRequestPresence = new Map<string, PresenceTable<PullRequestProjection, PullRequestFieldGroup>>([\n\t[GitCloudHostIntegrationId.GitHub, gitHubPullRequests],\n\t[GitSelfManagedHostIntegrationId.CloudGitHubEnterprise, gitHubEnterprisePullRequests],\n\t[GitCloudHostIntegrationId.GitLab, gitLabPullRequestsFor(gitLabProviderApis(true))],\n\t[GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted, gitLabPullRequestsFor(gitLabProviderApis(false))],\n\t[GitCloudHostIntegrationId.AzureDevOps, azurePullRequests],\n\t[GitSelfManagedHostIntegrationId.AzureDevOpsServer, azureServerPullRequests],\n\t[GitCloudHostIntegrationId.Bitbucket, bitbucketPullRequests],\n\t[GitSelfManagedHostIntegrationId.BitbucketServer, bitbucketServerPullRequests],\n]);\n\nconst issuePresence = new Map<string, PresenceTable<IssueProjection, IssueFieldGroup>>([\n\t[GitCloudHostIntegrationId.GitHub, gitHubIssues],\n\t[GitSelfManagedHostIntegrationId.CloudGitHubEnterprise, gitHubIssues],\n\t[GitCloudHostIntegrationId.GitLab, gitLabIssuesFor(true)],\n\t[GitSelfManagedHostIntegrationId.CloudGitLabSelfHosted, gitLabIssuesFor(false)],\n\t[GitCloudHostIntegrationId.AzureDevOps, azureIssues],\n\t[GitSelfManagedHostIntegrationId.AzureDevOpsServer, azureServerIssues],\n\t[GitCloudHostIntegrationId.Bitbucket, bitbucketIssues],\n\t[IssuesCloudHostIntegrationId.Jira, jiraIssues],\n\t[IssuesSelfManagedHostIntegrationId.JiraServer, jiraServerIssues],\n\t[IssuesCloudHostIntegrationId.Linear, linearIssues],\n\t[IssuesCloudHostIntegrationId.Trello, trelloIssues],\n]);\n\nfunction lookup<P extends string, G extends string>(\n\ttables: ReadonlyMap<string, PresenceTable<P, G>>,\n\tproviderId: string,\n\tprojection: P | undefined,\n): Presence<G> | undefined {\n\tif (projection == null) return undefined;\n\n\tconst table = tables.get(providerId);\n\t// `hasOwn` so a projection read off an untrusted row can't resolve to an `Object.prototype` member.\n\treturn table != null && Object.hasOwn(table, projection) ? table[projection] : undefined;\n}\n\n/**\n * Which field groups the read that produced `pr` fetched, keyed by its provider and {@link PullRequestShape.projection}.\n * `undefined` when either is unknown or the provider has no table yet — then nothing can be said about any field.\n */\nexport function getPullRequestFieldPresence(\n\tpr: PullRequestShape,\n): Readonly<Record<PullRequestFieldGroup, FieldPresence>> | undefined {\n\treturn lookup(pullRequestPresence, pr.provider.id, pr.projection);\n}\n\n/**\n * Which field groups the read that produced `issue` fetched, keyed by its provider and {@link IssueShape.projection}.\n * `undefined` when either is unknown or the provider has no table yet — then nothing can be said about any field.\n */\nexport function getIssueFieldPresence(issue: IssueShape): Readonly<Record<IssueFieldGroup, FieldPresence>> | undefined {\n\treturn lookup(issuePresence, issue.provider.id, issue.projection);\n}\n"],"mappings":"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"}
|
|
@@ -118,7 +118,25 @@ export interface IssueEtagFields {
|
|
|
118
118
|
updatedDate: Date;
|
|
119
119
|
/** Counts only when `'reactions'` is requested, and only from a row whose read fetched reactions. */
|
|
120
120
|
thumbsUpCount?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Azure DevOps only: the version of the rule a work item's body is read by ({@link azureWorkItemDescriptionRule}).
|
|
123
|
+
* A change of rule changes the body without moving `updatedDate`, so a row read under an earlier one must not
|
|
124
|
+
* compare unchanged.
|
|
125
|
+
*/
|
|
126
|
+
descriptionRule?: number;
|
|
121
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* {@link IssueEtagFields.descriptionRule}: the version of the rule an Azure DevOps work item's body is read by, stamped
|
|
130
|
+
* on both sides of its etag. Bumped whenever the rule changes which field a work item's body comes from, so every row
|
|
131
|
+
* a caller holds from before is read in full once. 1: the first rich text field with content (provider-apis'
|
|
132
|
+
* `getAzureWorkItemDescription`), where it used to be only Description. The rule lives in provider-apis, so a bump of
|
|
133
|
+
* that package that changes it must bump this too; the etag tests pin the rule to catch one.
|
|
134
|
+
*
|
|
135
|
+
* It can't cover a row whose read found the organization's field catalog unavailable: that row's body falls back to
|
|
136
|
+
* the built-in fields, and a cheap check, which reads no body, can't tell it from a complete one, so it stays as read
|
|
137
|
+
* until the work item changes.
|
|
138
|
+
*/
|
|
139
|
+
export declare const azureWorkItemDescriptionRule = 1;
|
|
122
140
|
/** A batch read's target, in the form its provider addresses it by: a repository coordinate, or a tracker's resource. */
|
|
123
141
|
type BatchTarget = {
|
|
124
142
|
owner: string;
|
|
@@ -56,6 +56,18 @@ function toRevisionReviewers(reviewers) {
|
|
|
56
56
|
/** Every {@link IssueEtagInclude}, in the canonical order an etag lists them in. */
|
|
57
57
|
const issueEtagIncludes = ["reactions"];
|
|
58
58
|
/**
|
|
59
|
+
* {@link IssueEtagFields.descriptionRule}: the version of the rule an Azure DevOps work item's body is read by, stamped
|
|
60
|
+
* on both sides of its etag. Bumped whenever the rule changes which field a work item's body comes from, so every row
|
|
61
|
+
* a caller holds from before is read in full once. 1: the first rich text field with content (provider-apis'
|
|
62
|
+
* `getAzureWorkItemDescription`), where it used to be only Description. The rule lives in provider-apis, so a bump of
|
|
63
|
+
* that package that changes it must bump this too; the etag tests pin the rule to catch one.
|
|
64
|
+
*
|
|
65
|
+
* It can't cover a row whose read found the organization's field catalog unavailable: that row's body falls back to
|
|
66
|
+
* the built-in fields, and a cheap check, which reads no body, can't tell it from a complete one, so it stays as read
|
|
67
|
+
* until the work item changes.
|
|
68
|
+
*/
|
|
69
|
+
const azureWorkItemDescriptionRule = 1;
|
|
70
|
+
/**
|
|
59
71
|
* The scope a batch read records a refused target under. It is decided here rather than in the read, which has the
|
|
60
72
|
* same targets, because naming the refusal's cause (`describeRefusal`) needs it too. Azure DevOps: the organization
|
|
61
73
|
* and project, since its refusals come from those (an organization's OAuth policy or tenant, a project's permissions)
|
|
@@ -950,6 +962,6 @@ __decorate([trace()], IntegrationBase.prototype, "getLinkedIssueOrPullRequest",
|
|
|
950
962
|
__decorate([trace()], IntegrationBase.prototype, "getIssueResultCore", null);
|
|
951
963
|
__decorate([trace()], IntegrationBase.prototype, "getPullRequest", null);
|
|
952
964
|
//#endregion
|
|
953
|
-
export { IntegrationBase, ReadSessionFailure, isReadSessionFailure, issueEtagIncludes, pullRequestEtagIncludes, pullRequestRevision };
|
|
965
|
+
export { IntegrationBase, ReadSessionFailure, azureWorkItemDescriptionRule, isReadSessionFailure, issueEtagIncludes, pullRequestEtagIncludes, pullRequestRevision };
|
|
954
966
|
|
|
955
967
|
//# sourceMappingURL=integration.js.map
|