@gitkraken/core-gitlens 0.15.6 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/git/cache.d.ts +13 -0
  3. package/dist/git/cache.js +43 -6
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/features.d.ts +1 -1
  6. package/dist/git/features.js +1 -0
  7. package/dist/git/features.js.map +1 -1
  8. package/dist/git/models/issue.d.ts +14 -1
  9. package/dist/git/models/issue.js +3 -1
  10. package/dist/git/models/issue.js.map +1 -1
  11. package/dist/git/providers/branches.d.ts +29 -0
  12. package/dist/git/service.d.ts +4 -2
  13. package/dist/git/service.js +6 -4
  14. package/dist/git/service.js.map +1 -1
  15. package/dist/git/utils/issue.utils.js +4 -0
  16. package/dist/git/utils/issue.utils.js.map +1 -1
  17. package/dist/git/watching/worktreeSummary.d.ts +19 -4
  18. package/dist/git/watching/worktreeSummary.js +121 -36
  19. package/dist/git/watching/worktreeSummary.js.map +1 -1
  20. package/dist/git-cli/cliGitProvider.js +1 -1
  21. package/dist/git-cli/providers/branches.d.ts +50 -0
  22. package/dist/git-cli/providers/branches.js +280 -21
  23. package/dist/git-cli/providers/branches.js.map +1 -1
  24. package/dist/git-cli/providers/config.js +3 -2
  25. package/dist/git-cli/providers/config.js.map +1 -1
  26. package/dist/git-cli/providers/revision.js +1 -1
  27. package/dist/git-cli/providers/stash.js +1 -1
  28. package/dist/git-cli/providers/worktrees.js +23 -6
  29. package/dist/git-cli/providers/worktrees.js.map +1 -1
  30. package/dist/git-cli/service.d.ts +3 -0
  31. package/dist/git-cli/service.js +1 -0
  32. package/dist/git-cli/service.js.map +1 -1
  33. package/dist/plus/integrations/fieldPresence.d.ts +1 -1
  34. package/dist/plus/integrations/fieldPresence.js.map +1 -1
  35. package/dist/plus/integrations/models/integration.d.ts +18 -0
  36. package/dist/plus/integrations/models/integration.js +13 -1
  37. package/dist/plus/integrations/models/integration.js.map +1 -1
  38. package/dist/plus/integrations/providers/azure/azure.d.ts +7 -0
  39. package/dist/plus/integrations/providers/azure/azure.js +26 -4
  40. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  41. package/dist/plus/integrations/providers/azure/models.d.ts +11 -3
  42. package/dist/plus/integrations/providers/azure/models.js +37 -23
  43. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  44. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +14 -4
  45. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +31 -4
  46. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  47. package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
  48. package/dist/plus/integrations/providers/models.d.ts +2 -1
  49. package/dist/plus/integrations/providers/models.js +9 -2
  50. package/dist/plus/integrations/providers/models.js.map +1 -1
  51. package/dist/plus/integrations/providers/providersApi.d.ts +6 -0
  52. package/dist/plus/integrations/providers/providersApi.js +20 -4
  53. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  54. package/dist/plus/integrations/reads/drains.d.ts +1 -0
  55. package/dist/plus/integrations/reads/etag.js +8 -3
  56. package/dist/plus/integrations/reads/etag.js.map +1 -1
  57. package/docs/integrations.md +15 -6
  58. package/package.json +2 -2
@@ -2,6 +2,7 @@ import { ResourceDescriptor } from "../../../git/models/resourceDescriptor.js";
2
2
  import { PullRequest, PullRequestFilter, PullRequestStateFilter } from "../../../git/models/pullRequest.js";
3
3
  import { IntegrationIds } from "../constants.js";
4
4
  import { ProviderWarning } from "../results.js";
5
+ import "../providerFilters.js";
5
6
  import { ProviderPullRequest, ProviderReposInput, ProviderRepository } from "../providers/models.js";
6
7
  import { GitHostIntegration } from "../models/gitHostIntegration.js";
7
8
  import { IssuesIntegration } from "../models/issuesIntegration.js";
@@ -16,8 +16,11 @@ import { issueEtagIncludes, pullRequestEtagIncludes, pullRequestRevision } from
16
16
  * `null` mergeability and a `null` rollup). A stamp from another scheme or another set just compares unequal and
17
17
  * costs a full read.
18
18
  */
19
- /** The hosts that keep no update time on a pull request, whose etag adds a {@link pullRequestRevision}. */
20
- const revisionProviderIds = /* @__PURE__ */ new Set(["azureDevOps", "azure-devops-server"]);
19
+ /**
20
+ * Azure DevOps: a pull request there keeps no update time, so its etag adds a {@link pullRequestRevision}, and a work
21
+ * item's etag adds the rule its body is read by (see `azureWorkItemDescriptionRule`).
22
+ */
23
+ const azureProviderIds = /* @__PURE__ */ new Set(["azureDevOps", "azure-devops-server"]);
21
24
  function pullRequestEtagFieldsFromShape(pr) {
22
25
  const fields = {
23
26
  state: pr.state,
@@ -28,7 +31,7 @@ function pullRequestEtagFieldsFromShape(pr) {
28
31
  reviewDecision: pr.reviewDecision,
29
32
  statusCheckRollupState: "statusCheckRollupState" in pr ? pr.statusCheckRollupState : void 0
30
33
  };
31
- if (pr.provider != null && revisionProviderIds.has(pr.provider.id)) fields.revision = pullRequestRevision(pr);
34
+ if (pr.provider != null && azureProviderIds.has(pr.provider.id)) fields.revision = pullRequestRevision(pr);
32
35
  return fields;
33
36
  }
34
37
  function issueEtagFieldsFromShape(issue) {
@@ -37,6 +40,7 @@ function issueEtagFieldsFromShape(issue) {
37
40
  updatedDate: issue.updatedDate
38
41
  };
39
42
  if (issue.thumbsUpCount != null && issue.provider != null && getIssueFieldPresence(issue)?.reactions === "fetched") fields.thumbsUpCount = issue.thumbsUpCount;
43
+ if (issue.provider != null && azureProviderIds.has(issue.provider.id)) fields.descriptionRule = 1;
40
44
  return fields;
41
45
  }
42
46
  /**
@@ -87,6 +91,7 @@ function findInvalidIssueEtagInclude(includes) {
87
91
  function issueEtag(fields, includes) {
88
92
  const covered = normalizeIssueEtagIncludes(includes);
89
93
  const state = [fields.state, timeOf(fields.updatedDate)];
94
+ if (fields.descriptionRule != null) state.push(fields.descriptionRule);
90
95
  if (covered.length === 0) return `is1:${JSON.stringify(state)}`;
91
96
  for (const include of covered) switch (include) {
92
97
  case "reactions": state.push(fields.thumbsUpCount ?? null);
@@ -1 +1 @@
1
- {"version":3,"file":"etag.js","names":[],"sources":["../../../../../plus/integrations/src/reads/etag.ts"],"sourcesContent":["import type { IssueShape } from '@gitlens/git/models/issue.js';\nimport type { PullRequest, PullRequestShape } from '@gitlens/git/models/pullRequest.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from '../constants.js';\nimport { getIssueFieldPresence } from '../fieldPresence.js';\nimport type {\n\tIssueEtagFields,\n\tIssueEtagInclude,\n\tPullRequestEtagFields,\n\tPullRequestEtagInclude,\n} from '../models/integration.js';\nimport { issueEtagIncludes, pullRequestEtagIncludes, pullRequestRevision } from '../models/integration.js';\n\n/**\n * The etags the batch reads hand back (`getPullRequestsBatch`, `getIssuesBatch`): an opaque stamp of an item's\n * change state, which a caller sends back so a cheap check can answer \"unchanged\" without a full read.\n *\n * Computed by core, never the provider's HTTP ETag, and from the fields alone — not the item's id, since the\n * caller's key already identifies the target and ids cross vocabularies between reads. Not hashed: the stamp IS\n * the serialized fields, so two different change states can never collide into a false `unchanged` — except for an\n * Azure DevOps pull request's `revision`, a 64-bit hash of the fields Azure changes without a timestamp, where a\n * collision (about 2^-64) is the only way a change can hide. The prefix names the scheme (`pr1`, `is1`) and every\n * include it covers ({@link PullRequestEtagInclude}, {@link IssueEtagInclude}), in canonical order\n * (`pr1+mergeable+checks:`, `is1+reactions:`), so two different sets can never collide even when their values do (a\n * `null` mergeability and a `null` rollup). A stamp from another scheme or another set just compares unequal and\n * costs a full read.\n */\n\n/** The hosts that keep no update time on a pull request, whose etag adds a {@link pullRequestRevision}. */\nconst revisionProviderIds: ReadonlySet<string> = new Set([\n\tGitCloudHostIntegrationId.AzureDevOps,\n\tGitSelfManagedHostIntegrationId.AzureDevOpsServer,\n]);\n\nexport function pullRequestEtagFieldsFromShape(pr: PullRequestShape): PullRequestEtagFields {\n\tconst fields: PullRequestEtagFields = {\n\t\tstate: pr.state,\n\t\tisDraft: pr.isDraft,\n\t\tupdatedDate: pr.updatedDate,\n\t\theadSha: pr.refs?.head.sha,\n\t\tmergeableState: pr.mergeableState,\n\t\treviewDecision: pr.reviewDecision,\n\t\t// On the class, not the shape: every provider's mapper builds a `PullRequest`. Read by name rather than by\n\t\t// `instanceof`, which a second copy of the model module fails, silently dropping the rollup from every full\n\t\t// row's etag so that no cheap check ever matches it again.\n\t\tstatusCheckRollupState: 'statusCheckRollupState' in pr ? (pr as PullRequest).statusCheckRollupState : undefined,\n\t};\n\t// Read defensively, as `timeOf` reads the date: a row without a provider must not fail the read.\n\tif (pr.provider != null && revisionProviderIds.has(pr.provider.id)) {\n\t\tfields.revision = pullRequestRevision(pr);\n\t}\n\treturn fields;\n}\n\nexport function issueEtagFieldsFromShape(issue: IssueShape): IssueEtagFields {\n\tconst fields: IssueEtagFields = { state: issue.state, updatedDate: issue.updatedDate };\n\t// Only where the row's read really fetched reactions: elsewhere a count is a placeholder or a tracker's votes,\n\t// which no cheap check reads, so taking it would fail every check of that row under `'reactions'`.\n\tif (\n\t\tissue.thumbsUpCount != null &&\n\t\tissue.provider != null &&\n\t\tgetIssueFieldPresence(issue)?.reactions === 'fetched'\n\t) {\n\t\tfields.thumbsUpCount = issue.thumbsUpCount;\n\t}\n\treturn fields;\n}\n\n/**\n * The distinct `includes` in canonical order, whatever order and repetition the caller gave. Values it doesn't know\n * are dropped, so a caller validates them first (see {@link findInvalidPullRequestEtagInclude}).\n */\nexport function normalizePullRequestEtagIncludes(\n\tincludes: readonly PullRequestEtagInclude[],\n): readonly PullRequestEtagInclude[] {\n\treturn pullRequestEtagIncludes.filter(include => includes.includes(include));\n}\n\n/** The first of `includes` that isn't a {@link PullRequestEtagInclude}, if any. */\nexport function findInvalidPullRequestEtagInclude(includes: readonly string[]): string | undefined {\n\treturn includes.find(include => !(pullRequestEtagIncludes as readonly string[]).includes(include));\n}\n\n/**\n * Each of `includes` adds one input — mergeability, review decision or check rollup — which a host may change\n * without moving `updatedDate`. An input that isn't listed is left out, so its churn never forces a full read.\n */\nexport function pullRequestEtag(fields: PullRequestEtagFields, includes: readonly PullRequestEtagInclude[]): string {\n\tconst covered = normalizePullRequestEtagIncludes(includes);\n\tconst state: unknown[] = [fields.state, fields.isDraft ?? null, timeOf(fields.updatedDate), fields.headSha ?? null];\n\t// Only where it is set, so every other host's etag stays exactly what it was.\n\tif (fields.revision != null) {\n\t\tstate.push(fields.revision);\n\t}\n\n\tif (covered.length === 0) return `pr1:${JSON.stringify(state)}`;\n\n\tfor (const include of covered) {\n\t\tswitch (include) {\n\t\t\tcase 'mergeable':\n\t\t\t\tstate.push(fields.mergeableState ?? null);\n\t\t\t\tbreak;\n\t\t\tcase 'reviewDecision':\n\t\t\t\tstate.push(fields.reviewDecision ?? null);\n\t\t\t\tbreak;\n\t\t\tcase 'checks':\n\t\t\t\tstate.push(fields.statusCheckRollupState ?? null);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn `pr1+${covered.join('+')}:${JSON.stringify(state)}`;\n}\n\n/** The issue twin of {@link normalizePullRequestEtagIncludes}; validate with {@link findInvalidIssueEtagInclude}. */\nexport function normalizeIssueEtagIncludes(includes: readonly IssueEtagInclude[]): readonly IssueEtagInclude[] {\n\treturn issueEtagIncludes.filter(include => includes.includes(include));\n}\n\n/** The first of `includes` that isn't an {@link IssueEtagInclude}, if any. */\nexport function findInvalidIssueEtagInclude(includes: readonly string[]): string | undefined {\n\treturn includes.find(include => !(issueEtagIncludes as readonly string[]).includes(include));\n}\n\n/** `'reactions'` adds the thumbs-up count, which a host changes without moving `updatedDate`. */\nexport function issueEtag(fields: IssueEtagFields, includes: readonly IssueEtagInclude[]): string {\n\tconst covered = normalizeIssueEtagIncludes(includes);\n\tconst state: unknown[] = [fields.state, timeOf(fields.updatedDate)];\n\tif (covered.length === 0) return `is1:${JSON.stringify(state)}`;\n\n\tfor (const include of covered) {\n\t\tswitch (include) {\n\t\t\tcase 'reactions':\n\t\t\t\tstate.push(fields.thumbsUpCount ?? null);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn `is1+${covered.join('+')}:${JSON.stringify(state)}`;\n}\n\n/**\n * Never throws: an etag is computed for every fully read row, so a mapper that hands over a missing or unparseable\n * date must not fail the whole read. Such a row's etag just won't match a cheap check's, which always has a date,\n * so the cost is a full read, never a false `unchanged`.\n */\nfunction timeOf(date: Date): number | null {\n\tconst time = new Date(date).getTime();\n\treturn Number.isNaN(time) ? null : time;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA4BA,MAAM,sCAA2C,IAAI,IAAI,CAAA,eAAA,qBAGzD,CAAC;AAED,SAAgB,+BAA+B,IAA6C;CAC3F,MAAM,SAAgC;EACrC,OAAO,GAAG;EACV,SAAS,GAAG;EACZ,aAAa,GAAG;EAChB,SAAS,GAAG,MAAM,KAAK;EACvB,gBAAgB,GAAG;EACnB,gBAAgB,GAAG;EAInB,wBAAwB,4BAA4B,KAAM,GAAmB,yBAAyB,KAAA;CACvG;CAEA,IAAI,GAAG,YAAY,QAAQ,oBAAoB,IAAI,GAAG,SAAS,EAAE,GAChE,OAAO,WAAW,oBAAoB,EAAE;CAEzC,OAAO;AACR;AAEA,SAAgB,yBAAyB,OAAoC;CAC5E,MAAM,SAA0B;EAAE,OAAO,MAAM;EAAO,aAAa,MAAM;CAAY;CAGrF,IACC,MAAM,iBAAiB,QACvB,MAAM,YAAY,QAClB,sBAAsB,KAAK,CAAC,EAAE,cAAc,WAE5C,OAAO,gBAAgB,MAAM;CAE9B,OAAO;AACR;;;;;AAMA,SAAgB,iCACf,UACoC;CACpC,OAAO,wBAAwB,QAAO,YAAW,SAAS,SAAS,OAAO,CAAC;AAC5E;;AAGA,SAAgB,kCAAkC,UAAiD;CAClG,OAAO,SAAS,MAAK,YAAW,CAAE,wBAA8C,SAAS,OAAO,CAAC;AAClG;;;;;AAMA,SAAgB,gBAAgB,QAA+B,UAAqD;CACnH,MAAM,UAAU,iCAAiC,QAAQ;CACzD,MAAM,QAAmB;EAAC,OAAO;EAAO,OAAO,WAAW;EAAM,OAAO,OAAO,WAAW;EAAG,OAAO,WAAW;CAAI;CAElH,IAAI,OAAO,YAAY,MACtB,MAAM,KAAK,OAAO,QAAQ;CAG3B,IAAI,QAAQ,WAAW,GAAG,OAAO,OAAO,KAAK,UAAU,KAAK;CAE5D,KAAK,MAAM,WAAW,SACrB,QAAQ,SAAR;EACC,KAAK;GACJ,MAAM,KAAK,OAAO,kBAAkB,IAAI;GACxC;EACD,KAAK;GACJ,MAAM,KAAK,OAAO,kBAAkB,IAAI;GACxC;EACD,KAAK,UACJ,MAAM,KAAK,OAAO,0BAA0B,IAAI;CAElD;CAGD,OAAO,OAAO,QAAQ,KAAK,GAAG,EAAE,GAAG,KAAK,UAAU,KAAK;AACxD;;AAGA,SAAgB,2BAA2B,UAAoE;CAC9G,OAAO,kBAAkB,QAAO,YAAW,SAAS,SAAS,OAAO,CAAC;AACtE;;AAGA,SAAgB,4BAA4B,UAAiD;CAC5F,OAAO,SAAS,MAAK,YAAW,CAAE,kBAAwC,SAAS,OAAO,CAAC;AAC5F;;AAGA,SAAgB,UAAU,QAAyB,UAA+C;CACjG,MAAM,UAAU,2BAA2B,QAAQ;CACnD,MAAM,QAAmB,CAAC,OAAO,OAAO,OAAO,OAAO,WAAW,CAAC;CAClE,IAAI,QAAQ,WAAW,GAAG,OAAO,OAAO,KAAK,UAAU,KAAK;CAE5D,KAAK,MAAM,WAAW,SACrB,QAAQ,SAAR;EACC,KAAK,aACJ,MAAM,KAAK,OAAO,iBAAiB,IAAI;CAEzC;CAGD,OAAO,OAAO,QAAQ,KAAK,GAAG,EAAE,GAAG,KAAK,UAAU,KAAK;AACxD;;;;;;AAOA,SAAS,OAAO,MAA2B;CAC1C,MAAM,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,QAAQ;CACpC,OAAO,OAAO,MAAM,IAAI,IAAI,OAAO;AACpC"}
1
+ {"version":3,"file":"etag.js","names":[],"sources":["../../../../../plus/integrations/src/reads/etag.ts"],"sourcesContent":["import type { IssueShape } from '@gitlens/git/models/issue.js';\nimport type { PullRequest, PullRequestShape } from '@gitlens/git/models/pullRequest.js';\nimport { GitCloudHostIntegrationId, GitSelfManagedHostIntegrationId } from '../constants.js';\nimport { getIssueFieldPresence } from '../fieldPresence.js';\nimport type {\n\tIssueEtagFields,\n\tIssueEtagInclude,\n\tPullRequestEtagFields,\n\tPullRequestEtagInclude,\n} from '../models/integration.js';\nimport {\n\tazureWorkItemDescriptionRule,\n\tissueEtagIncludes,\n\tpullRequestEtagIncludes,\n\tpullRequestRevision,\n} from '../models/integration.js';\n\n/**\n * The etags the batch reads hand back (`getPullRequestsBatch`, `getIssuesBatch`): an opaque stamp of an item's\n * change state, which a caller sends back so a cheap check can answer \"unchanged\" without a full read.\n *\n * Computed by core, never the provider's HTTP ETag, and from the fields alone — not the item's id, since the\n * caller's key already identifies the target and ids cross vocabularies between reads. Not hashed: the stamp IS\n * the serialized fields, so two different change states can never collide into a false `unchanged` — except for an\n * Azure DevOps pull request's `revision`, a 64-bit hash of the fields Azure changes without a timestamp, where a\n * collision (about 2^-64) is the only way a change can hide. The prefix names the scheme (`pr1`, `is1`) and every\n * include it covers ({@link PullRequestEtagInclude}, {@link IssueEtagInclude}), in canonical order\n * (`pr1+mergeable+checks:`, `is1+reactions:`), so two different sets can never collide even when their values do (a\n * `null` mergeability and a `null` rollup). A stamp from another scheme or another set just compares unequal and\n * costs a full read.\n */\n\n/**\n * Azure DevOps: a pull request there keeps no update time, so its etag adds a {@link pullRequestRevision}, and a work\n * item's etag adds the rule its body is read by (see `azureWorkItemDescriptionRule`).\n */\nconst azureProviderIds: ReadonlySet<string> = new Set([\n\tGitCloudHostIntegrationId.AzureDevOps,\n\tGitSelfManagedHostIntegrationId.AzureDevOpsServer,\n]);\n\nexport function pullRequestEtagFieldsFromShape(pr: PullRequestShape): PullRequestEtagFields {\n\tconst fields: PullRequestEtagFields = {\n\t\tstate: pr.state,\n\t\tisDraft: pr.isDraft,\n\t\tupdatedDate: pr.updatedDate,\n\t\theadSha: pr.refs?.head.sha,\n\t\tmergeableState: pr.mergeableState,\n\t\treviewDecision: pr.reviewDecision,\n\t\t// On the class, not the shape: every provider's mapper builds a `PullRequest`. Read by name rather than by\n\t\t// `instanceof`, which a second copy of the model module fails, silently dropping the rollup from every full\n\t\t// row's etag so that no cheap check ever matches it again.\n\t\tstatusCheckRollupState: 'statusCheckRollupState' in pr ? (pr as PullRequest).statusCheckRollupState : undefined,\n\t};\n\t// Read defensively, as `timeOf` reads the date: a row without a provider must not fail the read.\n\tif (pr.provider != null && azureProviderIds.has(pr.provider.id)) {\n\t\tfields.revision = pullRequestRevision(pr);\n\t}\n\treturn fields;\n}\n\nexport function issueEtagFieldsFromShape(issue: IssueShape): IssueEtagFields {\n\tconst fields: IssueEtagFields = { state: issue.state, updatedDate: issue.updatedDate };\n\t// Only where the row's read really fetched reactions: elsewhere a count is a placeholder or a tracker's votes,\n\t// which no cheap check reads, so taking it would fail every check of that row under `'reactions'`.\n\tif (\n\t\tissue.thumbsUpCount != null &&\n\t\tissue.provider != null &&\n\t\tgetIssueFieldPresence(issue)?.reactions === 'fetched'\n\t) {\n\t\tfields.thumbsUpCount = issue.thumbsUpCount;\n\t}\n\tif (issue.provider != null && azureProviderIds.has(issue.provider.id)) {\n\t\tfields.descriptionRule = azureWorkItemDescriptionRule;\n\t}\n\treturn fields;\n}\n\n/**\n * The distinct `includes` in canonical order, whatever order and repetition the caller gave. Values it doesn't know\n * are dropped, so a caller validates them first (see {@link findInvalidPullRequestEtagInclude}).\n */\nexport function normalizePullRequestEtagIncludes(\n\tincludes: readonly PullRequestEtagInclude[],\n): readonly PullRequestEtagInclude[] {\n\treturn pullRequestEtagIncludes.filter(include => includes.includes(include));\n}\n\n/** The first of `includes` that isn't a {@link PullRequestEtagInclude}, if any. */\nexport function findInvalidPullRequestEtagInclude(includes: readonly string[]): string | undefined {\n\treturn includes.find(include => !(pullRequestEtagIncludes as readonly string[]).includes(include));\n}\n\n/**\n * Each of `includes` adds one input — mergeability, review decision or check rollup — which a host may change\n * without moving `updatedDate`. An input that isn't listed is left out, so its churn never forces a full read.\n */\nexport function pullRequestEtag(fields: PullRequestEtagFields, includes: readonly PullRequestEtagInclude[]): string {\n\tconst covered = normalizePullRequestEtagIncludes(includes);\n\tconst state: unknown[] = [fields.state, fields.isDraft ?? null, timeOf(fields.updatedDate), fields.headSha ?? null];\n\t// Only where it is set, so every other host's etag stays exactly what it was.\n\tif (fields.revision != null) {\n\t\tstate.push(fields.revision);\n\t}\n\n\tif (covered.length === 0) return `pr1:${JSON.stringify(state)}`;\n\n\tfor (const include of covered) {\n\t\tswitch (include) {\n\t\t\tcase 'mergeable':\n\t\t\t\tstate.push(fields.mergeableState ?? null);\n\t\t\t\tbreak;\n\t\t\tcase 'reviewDecision':\n\t\t\t\tstate.push(fields.reviewDecision ?? null);\n\t\t\t\tbreak;\n\t\t\tcase 'checks':\n\t\t\t\tstate.push(fields.statusCheckRollupState ?? null);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn `pr1+${covered.join('+')}:${JSON.stringify(state)}`;\n}\n\n/** The issue twin of {@link normalizePullRequestEtagIncludes}; validate with {@link findInvalidIssueEtagInclude}. */\nexport function normalizeIssueEtagIncludes(includes: readonly IssueEtagInclude[]): readonly IssueEtagInclude[] {\n\treturn issueEtagIncludes.filter(include => includes.includes(include));\n}\n\n/** The first of `includes` that isn't an {@link IssueEtagInclude}, if any. */\nexport function findInvalidIssueEtagInclude(includes: readonly string[]): string | undefined {\n\treturn includes.find(include => !(issueEtagIncludes as readonly string[]).includes(include));\n}\n\n/** `'reactions'` adds the thumbs-up count, which a host changes without moving `updatedDate`. */\nexport function issueEtag(fields: IssueEtagFields, includes: readonly IssueEtagInclude[]): string {\n\tconst covered = normalizeIssueEtagIncludes(includes);\n\tconst state: unknown[] = [fields.state, timeOf(fields.updatedDate)];\n\t// Only where it is set, so every other provider's etag stays exactly what it was.\n\tif (fields.descriptionRule != null) {\n\t\tstate.push(fields.descriptionRule);\n\t}\n\tif (covered.length === 0) return `is1:${JSON.stringify(state)}`;\n\n\tfor (const include of covered) {\n\t\tswitch (include) {\n\t\t\tcase 'reactions':\n\t\t\t\tstate.push(fields.thumbsUpCount ?? null);\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn `is1+${covered.join('+')}:${JSON.stringify(state)}`;\n}\n\n/**\n * Never throws: an etag is computed for every fully read row, so a mapper that hands over a missing or unparseable\n * date must not fail the whole read. Such a row's etag just won't match a cheap check's, which always has a date,\n * so the cost is a full read, never a false `unchanged`.\n */\nfunction timeOf(date: Date): number | null {\n\tconst time = new Date(date).getTime();\n\treturn Number.isNaN(time) ? null : time;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAoCA,MAAM,mCAAwC,IAAI,IAAI,CAAA,eAAA,qBAGtD,CAAC;AAED,SAAgB,+BAA+B,IAA6C;CAC3F,MAAM,SAAgC;EACrC,OAAO,GAAG;EACV,SAAS,GAAG;EACZ,aAAa,GAAG;EAChB,SAAS,GAAG,MAAM,KAAK;EACvB,gBAAgB,GAAG;EACnB,gBAAgB,GAAG;EAInB,wBAAwB,4BAA4B,KAAM,GAAmB,yBAAyB,KAAA;CACvG;CAEA,IAAI,GAAG,YAAY,QAAQ,iBAAiB,IAAI,GAAG,SAAS,EAAE,GAC7D,OAAO,WAAW,oBAAoB,EAAE;CAEzC,OAAO;AACR;AAEA,SAAgB,yBAAyB,OAAoC;CAC5E,MAAM,SAA0B;EAAE,OAAO,MAAM;EAAO,aAAa,MAAM;CAAY;CAGrF,IACC,MAAM,iBAAiB,QACvB,MAAM,YAAY,QAClB,sBAAsB,KAAK,CAAC,EAAE,cAAc,WAE5C,OAAO,gBAAgB,MAAM;CAE9B,IAAI,MAAM,YAAY,QAAQ,iBAAiB,IAAI,MAAM,SAAS,EAAE,GACnE,OAAO,kBAAA;CAER,OAAO;AACR;;;;;AAMA,SAAgB,iCACf,UACoC;CACpC,OAAO,wBAAwB,QAAO,YAAW,SAAS,SAAS,OAAO,CAAC;AAC5E;;AAGA,SAAgB,kCAAkC,UAAiD;CAClG,OAAO,SAAS,MAAK,YAAW,CAAE,wBAA8C,SAAS,OAAO,CAAC;AAClG;;;;;AAMA,SAAgB,gBAAgB,QAA+B,UAAqD;CACnH,MAAM,UAAU,iCAAiC,QAAQ;CACzD,MAAM,QAAmB;EAAC,OAAO;EAAO,OAAO,WAAW;EAAM,OAAO,OAAO,WAAW;EAAG,OAAO,WAAW;CAAI;CAElH,IAAI,OAAO,YAAY,MACtB,MAAM,KAAK,OAAO,QAAQ;CAG3B,IAAI,QAAQ,WAAW,GAAG,OAAO,OAAO,KAAK,UAAU,KAAK;CAE5D,KAAK,MAAM,WAAW,SACrB,QAAQ,SAAR;EACC,KAAK;GACJ,MAAM,KAAK,OAAO,kBAAkB,IAAI;GACxC;EACD,KAAK;GACJ,MAAM,KAAK,OAAO,kBAAkB,IAAI;GACxC;EACD,KAAK,UACJ,MAAM,KAAK,OAAO,0BAA0B,IAAI;CAElD;CAGD,OAAO,OAAO,QAAQ,KAAK,GAAG,EAAE,GAAG,KAAK,UAAU,KAAK;AACxD;;AAGA,SAAgB,2BAA2B,UAAoE;CAC9G,OAAO,kBAAkB,QAAO,YAAW,SAAS,SAAS,OAAO,CAAC;AACtE;;AAGA,SAAgB,4BAA4B,UAAiD;CAC5F,OAAO,SAAS,MAAK,YAAW,CAAE,kBAAwC,SAAS,OAAO,CAAC;AAC5F;;AAGA,SAAgB,UAAU,QAAyB,UAA+C;CACjG,MAAM,UAAU,2BAA2B,QAAQ;CACnD,MAAM,QAAmB,CAAC,OAAO,OAAO,OAAO,OAAO,WAAW,CAAC;CAElE,IAAI,OAAO,mBAAmB,MAC7B,MAAM,KAAK,OAAO,eAAe;CAElC,IAAI,QAAQ,WAAW,GAAG,OAAO,OAAO,KAAK,UAAU,KAAK;CAE5D,KAAK,MAAM,WAAW,SACrB,QAAQ,SAAR;EACC,KAAK,aACJ,MAAM,KAAK,OAAO,iBAAiB,IAAI;CAEzC;CAGD,OAAO,OAAO,QAAQ,KAAK,GAAG,EAAE,GAAG,KAAK,UAAU,KAAK;AACxD;;;;;;AAOA,SAAS,OAAO,MAA2B;CAC1C,MAAM,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,QAAQ;CACpC,OAAO,OAAO,MAAM,IAAI,IAAI,OAAO;AACpC"}
@@ -314,9 +314,9 @@ reads, and is `undefined` elsewhere), `reviewDecision`, `assignees`, `checks` (`
314
314
  (`mergeableState`), `diffStats` (`additions`, `deletions`, `filesChanged`), `commitCount`, `comments` (`commentsCount`),
315
315
  `reactions` (`thumbsUpCount`), `access` (`viewerCanUpdate` and `repository.accessLevel`), `authoredByMe` (with `viewer`,
316
316
  the identity it was matched against) and `stack`. `statusCheckRollupState`, `commitCount` and `viewerCanUpdate` live on
317
- the `PullRequest` class every row is, not on the `PullRequestShape` type. Issue groups: `description` (`body`),
318
- `assignees`, `labels`, `comments` (`commentsCount`), `reactions` (`thumbsUpCount`) and `access`
319
- (`repository.accessLevel`).
317
+ the `PullRequest` class every row is, not on the `PullRequestShape` type. Issue groups: `description` (`body`, and
318
+ `bodyField` where the provider sets it, so take both from the same row), `assignees`, `labels`, `comments`
319
+ (`commentsCount`), `reactions` (`thumbsUpCount`) and `access` (`repository.accessLevel`).
320
320
 
321
321
  Each table below covers a cloud host and its self-managed variant: where a cell reads `cloud / self-managed` the two
322
322
  differ, and `—` means that host never produces the tag.
@@ -1337,9 +1337,18 @@ while GitHub, GitLab, and Linear descriptions are Markdown. Jira issues therefor
1337
1337
  `bodyFormat: 'jira-wiki'`; an omitted `bodyFormat` means consumers should preserve the existing behavior and treat
1338
1338
  `body` as Markdown. The `'markdown'` value is reserved for providers that need to make that format explicit.
1339
1339
 
1340
- Two exceptions carry HTML with `bodyFormat` omitted: Azure DevOps work items (`System.Description`) and Bitbucket
1341
- Cloud issues (`content.html`). Their `bodyFormat` is left unset rather than changed, so check the provider before
1342
- treating either as Markdown.
1340
+ Two exceptions carry HTML with `bodyFormat` omitted: Azure DevOps work items and Bitbucket Cloud issues
1341
+ (`content.html`). Their `bodyFormat` is left unset rather than changed, so check the provider before treating either
1342
+ as Markdown.
1343
+
1344
+ An Azure DevOps work item's body is its first rich text field with content, since its type decides where the content
1345
+ lives: `System.Description`, then the built-in description fields (`Microsoft.VSTS.TCM.ReproSteps` on Agile, Scrum and
1346
+ CMMI Bugs, `Microsoft.VSTS.Common.AcceptanceCriteria`, `Microsoft.VSTS.CMMI.Symptom`), then any other `html` field
1347
+ of the organization's field catalog, custom fields first (provider-apis' `getAzureWorkItemDescription`, which every
1348
+ read applies). `bodyField` names the field (`{ id: 'Microsoft.VSTS.TCM.ReproSteps', name: 'Repro Steps' }`) so a
1349
+ host can label the body; it is absent for other providers. The catalog is read only for a work item no built-in field
1350
+ describes, and kept for an hour per organization, credential and client (provider-apis keeps its own for list rows); when it can't be read the body falls back to the built-in
1351
+ fields.
1343
1352
 
1344
1353
  Rendering and conversion remain consumer concerns. In particular, the Jira body is neither ADF nor converted to
1345
1354
  Markdown by this package.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gitkraken/core-gitlens",
3
3
  "description": "GitLens core — shared Git / AI / GitHub primitives for internal GitKraken consumption",
4
- "version": "0.15.6",
4
+ "version": "0.16.0",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "GitKraken",
@@ -239,7 +239,7 @@
239
239
  }
240
240
  },
241
241
  "dependencies": {
242
- "@gitkraken/provider-apis": "0.65.3",
242
+ "@gitkraken/provider-apis": "0.65.4",
243
243
  "@octokit/graphql": "9.0.4",
244
244
  "@octokit/request": "10.0.14",
245
245
  "@octokit/request-error": "7.1.1",