@gitkraken/core-gitlens 0.5.102 → 0.5.104
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -1
- package/dist/git/models/issue.d.ts +78 -0
- package/dist/git/models/issue.d.ts.map +1 -1
- package/dist/git/models/issue.js.map +1 -1
- package/dist/plus/git-github/api/config.d.ts +12 -0
- package/dist/plus/git-github/api/config.d.ts.map +1 -1
- package/dist/plus/git-github/api/config.js +12 -1
- package/dist/plus/git-github/api/config.js.map +1 -1
- package/dist/plus/git-github/api/github.d.ts +77 -7
- package/dist/plus/git-github/api/github.d.ts.map +1 -1
- package/dist/plus/git-github/api/github.js +241 -120
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/api/issueSearchQuery.d.ts +70 -0
- package/dist/plus/git-github/api/issueSearchQuery.d.ts.map +1 -0
- package/dist/plus/git-github/api/issueSearchQuery.js +143 -0
- package/dist/plus/git-github/api/issueSearchQuery.js.map +1 -0
- package/dist/plus/integrations/collectionMetadata.d.ts +15 -1
- package/dist/plus/integrations/collectionMetadata.d.ts.map +1 -1
- package/dist/plus/integrations/collectionMetadata.js +21 -2
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +2 -0
- package/dist/plus/integrations/index.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +39 -2
- package/dist/plus/integrations/integrationService.d.ts.map +1 -1
- package/dist/plus/integrations/integrationService.js +17 -0
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +88 -2
- package/dist/plus/integrations/manager.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +61 -3
- package/dist/plus/integrations/models/gitHostIntegration.d.ts.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.js +51 -0
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +3 -46
- package/dist/plus/integrations/models/integration.d.ts.map +1 -1
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +70 -0
- package/dist/plus/integrations/models/issueReads.d.ts.map +1 -0
- package/dist/plus/integrations/models/issueReads.js +2 -0
- package/dist/plus/integrations/models/issueReads.js.map +1 -0
- package/dist/plus/integrations/providerFilters.d.ts +10 -0
- package/dist/plus/integrations/providerFilters.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.d.ts +26 -3
- package/dist/plus/integrations/providers/github.d.ts.map +1 -1
- package/dist/plus/integrations/providers/github.js +34 -0
- package/dist/plus/integrations/providers/github.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +22 -0
- package/dist/plus/integrations/providers/models.d.ts.map +1 -1
- package/dist/plus/integrations/providers/models.js +26 -0
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/reads/broaden.d.ts +10 -0
- package/dist/plus/integrations/reads/broaden.d.ts.map +1 -1
- package/dist/plus/integrations/reads/broaden.js.map +1 -1
- package/dist/plus/integrations/reads/counts.d.ts +64 -0
- package/dist/plus/integrations/reads/counts.d.ts.map +1 -0
- package/dist/plus/integrations/reads/counts.js +164 -0
- package/dist/plus/integrations/reads/counts.js.map +1 -0
- package/dist/plus/integrations/reads/filters.d.ts +63 -1
- package/dist/plus/integrations/reads/filters.d.ts.map +1 -1
- package/dist/plus/integrations/reads/filters.js +125 -0
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.d.ts.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.js +34 -17
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts.map +1 -1
- package/dist/plus/integrations/reads/issues.js +22 -41
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/paging.d.ts +59 -0
- package/dist/plus/integrations/reads/paging.d.ts.map +1 -1
- package/dist/plus/integrations/reads/paging.js +60 -0
- package/dist/plus/integrations/reads/paging.js.map +1 -1
- package/dist/plus/integrations/reads/searchIssues.d.ts +55 -0
- package/dist/plus/integrations/reads/searchIssues.d.ts.map +1 -0
- package/dist/plus/integrations/reads/searchIssues.js +144 -0
- package/dist/plus/integrations/reads/searchIssues.js.map +1 -0
- package/dist/plus/integrations/reads/warnings.d.ts +33 -3
- package/dist/plus/integrations/reads/warnings.d.ts.map +1 -1
- package/dist/plus/integrations/reads/warnings.js +87 -6
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/docs/integrations.md +110 -2
- package/docs/kepler-read-api-parity.md +57 -0
- package/package.json +1 -1
- package/src/git/models/issue.ts +81 -0
- package/src/plus/git-github/api/config.ts +13 -0
- package/src/plus/git-github/api/github.ts +332 -148
- package/src/plus/git-github/api/issueSearchQuery.ts +165 -0
- package/src/plus/integrations/collectionMetadata.ts +22 -3
- package/src/plus/integrations/index.ts +5 -0
- package/src/plus/integrations/integrationService.ts +47 -1
- package/src/plus/integrations/manager.ts +88 -2
- package/src/plus/integrations/models/gitHostIntegration.ts +102 -2
- package/src/plus/integrations/models/integration.ts +7 -46
- package/src/plus/integrations/models/issueReads.ts +73 -0
- package/src/plus/integrations/providerFilters.ts +15 -0
- package/src/plus/integrations/providers/github.ts +64 -2
- package/src/plus/integrations/providers/models.ts +49 -0
- package/src/plus/integrations/reads/broaden.ts +10 -0
- package/src/plus/integrations/reads/counts.ts +285 -0
- package/src/plus/integrations/reads/filters.ts +162 -1
- package/src/plus/integrations/reads/hierarchy.ts +43 -32
- package/src/plus/integrations/reads/issues.ts +22 -45
- package/src/plus/integrations/reads/paging.ts +102 -0
- package/src/plus/integrations/reads/searchIssues.ts +252 -0
- package/src/plus/integrations/reads/warnings.ts +129 -11
package/docs/integrations.md
CHANGED
|
@@ -120,6 +120,8 @@ it with `page` + `hasMore` + `cursor?`. **No read throws for a provider-side fai
|
|
|
120
120
|
| `listRepos` | `ProviderRepositoryShape` | Repos of an `org`, or account-wide user-affiliated repos when `org` is omitted. |
|
|
121
121
|
| `listPullRequestsPage` | `PullRequestShape` | With `repos`: those repos' PRs. Without: the user's PRs account-wide. |
|
|
122
122
|
| `listIssuesPage` | `IssueShape` | Same split, for a **git host**'s issues. |
|
|
123
|
+
| `searchIssuesPage` | `IssueShape` | Issues matching structured criteria over a repo/org scope — **no** `@me` binding. |
|
|
124
|
+
| `countIssues` | `IssueCountResult` | How many match each scope, fetching none of them. See §5.1. |
|
|
123
125
|
| `listIssueTrackerIssuesPage` | `IssueShape` | Jira / Linear / Trello (issues live under resource → project). |
|
|
124
126
|
| `sweepPullRequests` | `ProviderSweepResult` | Drains **every** page across providers (`maxPages`, default 100). |
|
|
125
127
|
| `sweepClosedPullRequests` | `ProviderSweepResult` | Same, pinned to `['closed','merged']`. |
|
|
@@ -169,6 +171,77 @@ Invariants worth relying on:
|
|
|
169
171
|
set" on `page.allPages === true`, which is false for _both_ truncation and failure — unlike
|
|
170
172
|
`page.truncated`, which can be misread as a benign cap.
|
|
171
173
|
|
|
174
|
+
### 5.1 The filtered issue search and its count probe
|
|
175
|
+
|
|
176
|
+
`searchIssuesPage` answers "every issue in this scope matching X", which no other issue read can: the
|
|
177
|
+
account-wide `listIssuesPage` is bound to the user's own relationships, and its repo-scoped path goes through
|
|
178
|
+
the SDK read whose over-limit recovery walk can spend up to 128 sequential requests and still return an
|
|
179
|
+
incomplete set. This one is a single request per page.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const caps = manager.getSupportedFilters(providerId).issueSearch;
|
|
183
|
+
if (caps.relationships.length === 0) return; // provider has no filtered issue search — hide the surface
|
|
184
|
+
|
|
185
|
+
const result = await manager.searchIssuesPage({
|
|
186
|
+
providerId: providerId,
|
|
187
|
+
repos: [{ namespace: 'gitkraken', name: 'vscode-gitlens' }],
|
|
188
|
+
criteria: {
|
|
189
|
+
relationships: ['unassigned'],
|
|
190
|
+
...(caps.updatedAfter ? { updatedAfter: '2026-05-05' } : {}),
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Three parts of the contract that are decisions, not incidentals:
|
|
196
|
+
|
|
197
|
+
- **Scope is mandatory.** Pass `repos`, `org`, or a user relationship (`authored` / `assigned` / `mentioned`).
|
|
198
|
+
`any-assignee` and `unassigned` do **not** scope anything — they describe the issue, not the caller, so
|
|
199
|
+
either one alone matches every such issue on the host. A call carrying only those is refused (warning +
|
|
200
|
+
`fetchFailed`), as is one scoping by repository **id**: a search names repositories by path, so ids would
|
|
201
|
+
silently widen the read to the whole org.
|
|
202
|
+
- **Ordering is always most-recently-updated-first.** Not an option: a "show the N most recent" policy at the
|
|
203
|
+
result ceiling is only correct under a guaranteed order.
|
|
204
|
+
- **`itemsPerPage` is per RELATIONSHIP**, since each becomes its own provider query: a page of an
|
|
205
|
+
N-relationship search returns up to `N × itemsPerPage` items before deduplication, and fewer where they
|
|
206
|
+
overlap. Read `page.itemsPerPage` for what actually came back.
|
|
207
|
+
- **At the result ceiling the read SUCCEEDS.** More matches than the provider will serve is an _omission_, not
|
|
208
|
+
a failure: `fetchFailed` stays absent, and the warning carries `omission.totalCount` (how many matched),
|
|
209
|
+
`omission.limit` (how many are reachable) and `recovery: 'none'` — the rest is unreachable however you page,
|
|
210
|
+
so never offer a "load more" here. Narrowing the criteria is the only way through.
|
|
211
|
+
|
|
212
|
+
`criteria` is validated all-or-nothing against the capability table before the read runs, exactly like
|
|
213
|
+
`filters` (§7). Free-form values (`text`, `labels`, `milestone`) are sanitized so user input cannot inject a
|
|
214
|
+
qualifier and re-scope the search; `text` additionally drops tokens containing `:`, since the structured
|
|
215
|
+
criteria are the qualifier channel.
|
|
216
|
+
|
|
217
|
+
**`countIssues`** answers "how many match" without fetching any — what a "this will fetch ~N issues" preview
|
|
218
|
+
needs, and what a live count beside an unapplied filter chip needs. Measured against GitHub, 30 counts are a
|
|
219
|
+
single rate-limit point, but each batch is still a network request: debounce and cache it if it's driven from
|
|
220
|
+
UI state.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
const counts = await manager.countIssues({
|
|
224
|
+
providerId: providerId,
|
|
225
|
+
scopes: [
|
|
226
|
+
{ key: 'unassigned', repos: repos, criteria: { relationships: ['unassigned'] } },
|
|
227
|
+
{ key: 'recent', repos: repos, criteria: { updatedAfter: '2026-05-05' } },
|
|
228
|
+
],
|
|
229
|
+
});
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
- Results are echoed under your own `key`, so no positional matching. A **duplicate key refuses the whole
|
|
233
|
+
call** — two results under one key make matching ambiguous for every scope.
|
|
234
|
+
- **`count: undefined` means "not reported", never zero.** Render the difference: showing an unknown count as
|
|
235
|
+
0 tells the user a filter matches nothing when it may match thousands. A provider that can't count at all
|
|
236
|
+
refuses rather than fabricating.
|
|
237
|
+
- `exceedsProviderLimit` is the signal to warn before starting an expensive fetch.
|
|
238
|
+
- Isolation is per scope and per batch: a refused scope (unscoped, id-based repos, inexpressible criteria)
|
|
239
|
+
costs no request and drops only itself, and a failed batch drops only its own scopes — `fetchFailed` is set
|
|
240
|
+
and every other count still comes back.
|
|
241
|
+
- One relationship per scope. A relationship set is OR-ed, which a single count can neither sum (it would
|
|
242
|
+
double-count overlaps) nor max (it would under-report), so such a scope is refused. Give each relationship
|
|
243
|
+
its own `key`.
|
|
244
|
+
|
|
172
245
|
## 6. Failures: warnings, `fetchFailed`, `truncated`
|
|
173
246
|
|
|
174
247
|
A per-provider (or per-connection, or per-scope) failure degrades to a **warning attached to a partial
|
|
@@ -299,6 +372,11 @@ const filters = wanted.filter(f => capability.includes(f));
|
|
|
299
372
|
(`listIssueTrackerIssuesPage` validates against this field).
|
|
300
373
|
- `issuesAccountWide` — the account-wide git-host read only. Usually narrower (GitLab can express
|
|
301
374
|
`Assignee` and `Author`, but not `Mention`), and empty for issue trackers.
|
|
375
|
+
- `issueSearch` — the **filtered issue search** (`searchIssuesPage`, and `countIssues` over the same
|
|
376
|
+
criteria). A third, wider surface: not bound to the user at all, so it takes relationships the other two
|
|
377
|
+
can't name. Reported as per-criterion flags rather than a list, and **always present** — a provider with no
|
|
378
|
+
filtered issue search reports empty `relationships` and all-false flags, which is the signal to hide the
|
|
379
|
+
surface rather than individual chips.
|
|
302
380
|
|
|
303
381
|
It's a _capability_ table, not a recommendation: passing fewer filters than listed is fine.
|
|
304
382
|
|
|
@@ -316,6 +394,12 @@ On the account-wide issue read, `filters` **replaces** the provider's own defini
|
|
|
316
394
|
`[Assignee]` means `assignee:@me` wherever it's expressible. `includeAllAssignees`
|
|
317
395
|
does the opposite (drops the user scope); passing both on that account-wide read is refused as contradictory.
|
|
318
396
|
|
|
397
|
+
`searchIssuesPage`'s `criteria.relationships` follows the same all-or-nothing rule and the same OR semantics
|
|
398
|
+
(one provider query per member, unioned and deduped), with two additions that are **not** about the user:
|
|
399
|
+
`any-assignee` (assigned to anyone) and `unassigned` (assigned to nobody). They partition the scope between
|
|
400
|
+
them, so requesting both is refused. Note that "all visible issues" is the **omitted** relationship set, not
|
|
401
|
+
`any-assignee` — which excludes unassigned issues.
|
|
402
|
+
|
|
319
403
|
`includeReviewRequested` is a legacy account-wide breadth option used only when no explicit `filters` are
|
|
320
404
|
supplied. It remains useful for Bitbucket Cloud, where the reviewer slice requires an expensive
|
|
321
405
|
O(workspaces × repos) fan-out; prefer `filters: [ReviewRequested]` when an exact relationship is required.
|
|
@@ -339,6 +423,8 @@ Derived from the provider models and `providersMetadata`. ✓ supported · ✗ r
|
|
|
339
423
|
| PR `states` account-wide | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — |
|
|
340
424
|
| Issues, repo-scoped | ✓ | ✓ | ✗ | ✗ | ✓ | — | — | — |
|
|
341
425
|
| Issues, account-wide | ✓ | ✓ | ✗ | ✗ | ✓ | — | — | — |
|
|
426
|
+
| `searchIssuesPage` | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
|
|
427
|
+
| `countIssues` | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
|
|
342
428
|
| Issues by `org`/`project` | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
|
|
343
429
|
| `listIssueTrackerIssuesPage` | — | — | — | — | — | ✓ | ✓ | ✓ |
|
|
344
430
|
| `broadenIssues` | ✓ | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ |
|
|
@@ -354,6 +440,13 @@ Issue filters: GitHub/GHE + Azure + Jira `Author, Assignee, Mention` · GitLab `
|
|
|
354
440
|
Linear + Trello `Assignee` · Bitbucket family none.
|
|
355
441
|
Account-wide issue filters: GitHub/GHE `Author, Assignee, Mention` · Azure `Author, Assignee` · GitLab
|
|
356
442
|
`Assignee, Author` · everything else none.
|
|
443
|
+
Issue **search** criteria (`getSupportedFilters().issueSearch`): GitHub/GHE express all of them —
|
|
444
|
+
relationships `authored, assigned, mentioned, any-assignee, unassigned`, plus `text`, `labels`, `milestone`,
|
|
445
|
+
`updatedAfter`, `createdAfter`, `withoutLinkedPullRequest`, `state` — and every other provider declares none,
|
|
446
|
+
so the read is refused there rather than serving a list that was never narrowed. GitLab and Azure could
|
|
447
|
+
express most of it (GitLab: `search`, `updated_after`, `labels`, `milestone`, one relationship per REST call;
|
|
448
|
+
Azure: WIQL per project), so the gap is unimplemented rather than impossible; `withoutLinkedPullRequest` and
|
|
449
|
+
free text have no equivalent on either.
|
|
357
450
|
|
|
358
451
|
> `supportedCloudIntegrationDescriptors.supports` (in `constants.ts`) describes what GitLens _advertises in
|
|
359
452
|
> its connect UI_, including enrichment-only capabilities. It is **not** the read-capability answer — use
|
|
@@ -364,8 +457,12 @@ Account-wide issue filters: GitHub/GHE `Author, Assignee, Mention` · Azure `Aut
|
|
|
364
457
|
- **GitHub / GHE** — cursor-only everywhere. The account-wide issue read is three searches (`author:@me`,
|
|
365
458
|
`assignee:@me`, `mentions:@me`) behind one composite cursor; a filtered account-wide PR read is one search
|
|
366
459
|
per state × relationship facet behind a composite cursor that resumes only active facets. Each search caps
|
|
367
|
-
at GitHub's own result ceiling, surfaced as `page.truncated
|
|
368
|
-
|
|
460
|
+
at GitHub's own 1.000-result ceiling, surfaced as `page.truncated` — and on `searchIssuesPage` additionally
|
|
461
|
+
as an omission carrying the total match count (§5.1). The only provider with a filtered issue search today.
|
|
462
|
+
`includeAllAssignees` is refused on the **account-wide** issue read: it becomes `assignee:*`, which needs a
|
|
463
|
+
scope to mean anything (unscoped it matches millions of issues across all of GitHub) and that read has none
|
|
464
|
+
to offer. Any scope works, though — one repository, several, or an org — so "assigned to anyone over these
|
|
465
|
+
repos" is `searchIssuesPage` with `relationships: ['any-assignee']`.
|
|
369
466
|
- **GitLab / self-hosted** — numbered per-repo cursors for repo-scoped reads. Account-wide PR state selection
|
|
370
467
|
is forwarded to each relationship query. Account-wide issues can independently narrow to assignee or author;
|
|
371
468
|
the unfiltered read unions both.
|
|
@@ -392,6 +489,17 @@ Account-wide issue filters: GitHub/GHE `Author, Assignee, Mention` · Azure `Aut
|
|
|
392
489
|
recovered. `hasMore` reports only untouched forward progress. A cursor can therefore remain with
|
|
393
490
|
`hasMore: false`; reusing it is an explicit manual retry of failed work, not a normal paging loop.
|
|
394
491
|
|
|
492
|
+
**`broadenIssues` vs `searchIssuesPage`.** If you already know your repository set, prefer
|
|
493
|
+
`searchIssuesPage({ repos })`: `broadenIssues` has to discover each org's repositories first (a paged drain)
|
|
494
|
+
and then reads their issues through the SDK path with the recovery walk, so it costs strictly more for the
|
|
495
|
+
same answer. It remains the read for "fan out across these orgs, whatever repos they turn out to contain",
|
|
496
|
+
with per-provider attribution (`broadenedProviderIds` / `failedProviderIds` / `incompleteProviderIds`) that
|
|
497
|
+
the single-provider search doesn't produce.
|
|
498
|
+
|
|
499
|
+
If you do migrate: broaden means **all visible** — it drops the assignee constraint entirely, so unassigned
|
|
500
|
+
issues are included. The equivalent is therefore an **omitted** `relationships`, **not**
|
|
501
|
+
`['any-assignee']` — `assignee:*` means "has some assignee" and would silently exclude every unassigned issue.
|
|
502
|
+
|
|
395
503
|
The same warning holds for `IssueShape.id` generally: it's the provider's **display** number/key (rendered
|
|
396
504
|
as `#{id}`, used for branch names). `nodeId` is the stable provider-native id, but its uniqueness scope is
|
|
397
505
|
provider-specific (Azure work-item ids are organization-scoped). For cross-scope correlation, key by
|
|
@@ -110,3 +110,60 @@ unchanged.
|
|
|
110
110
|
|
|
111
111
|
**Caveat:** Azure DevOps is scoped within a single organization; multi-org scoping remains unsupported (the
|
|
112
112
|
existing single-org guard stands).
|
|
113
|
+
|
|
114
|
+
## 7. Filtered issue search + count probe
|
|
115
|
+
|
|
116
|
+
Gap raised by [kepler#1745](https://github.com/gitkraken/kepler/issues/1745) Part 2 (the "All visible" issue
|
|
117
|
+
explorer). Kepler needed three things no existing read provided, and the reason is one asymmetry: the PR side
|
|
118
|
+
had a free-text, repo-scoped, relationship-optional search primitive (`searchPullRequests` /
|
|
119
|
+
`searchMyPullRequestsPage`) and the issue side had none. Every issue read was either bound to `@me` or routed
|
|
120
|
+
through `getIssuesForRepos`, which is the 128-request `recoverOverLimitSearch` path — the source of the
|
|
121
|
+
reported 116 s / 88 omissions.
|
|
122
|
+
|
|
123
|
+
**`searchIssuesPage`** (`IssueShape`, one request per page) is the issue counterpart. Structured
|
|
124
|
+
`IssueSearchCriteria` rather than a raw query string: a raw string is exactly why `searchPullRequests` is
|
|
125
|
+
GitHub-only in practice and why its capability is undeclarable, and Kepler has to render filter chips and hide
|
|
126
|
+
the unsupported ones, which needs a typed model plus `getSupportedFilters().issueSearch`.
|
|
127
|
+
|
|
128
|
+
**`countIssues`** answers "how many match" with zero issues transferred, which is what makes the cost dialog
|
|
129
|
+
and the live count label affordable. Verified against the live GitHub API: 30 aliased counts cost **1**
|
|
130
|
+
rate-limit point.
|
|
131
|
+
|
|
132
|
+
Decisions worth recording, because each closes off a plausible-looking alternative:
|
|
133
|
+
|
|
134
|
+
- **A separate method, not `countOnly` on the read.** Transferring zero issues is the whole value; a flag
|
|
135
|
+
would return a `ProviderPagedResult` whose `items`, `cursor` and `hasMore` are all meaningless.
|
|
136
|
+
- **A sibling read, not a mode of `listIssuesPage`.** That read is already two divergent branches around a
|
|
137
|
+
contract where `filters` replaces the provider's definition of "my issues"; `any-assignee` / `unassigned`
|
|
138
|
+
aren't "my issues" at all. Same reasoning as `broaden.ts` / `sweeps.ts` being separate files.
|
|
139
|
+
- **`sort:updated` is a contract, not an option.** Kepler's cap policy ("the 1.000 most recent") is only
|
|
140
|
+
correct under a guaranteed order; an option invites picking relevance order and then truncating to an
|
|
141
|
+
arbitrary subset.
|
|
142
|
+
- **The result ceiling is an omission, not a failure.** `fetchFailed` absent, `recovery: 'none'`, and
|
|
143
|
+
`omission.totalCount` populated — which is the number in Kepler's "This will fetch ~19.240 issues". The
|
|
144
|
+
`ProviderWarningOmission.totalCount` field already existed and was documented as "Only GitHub's search cap
|
|
145
|
+
does today"; it simply had never been populated on this path, because `searchMyIssues` computed
|
|
146
|
+
`issueCount > 1000` into a boolean and discarded the number.
|
|
147
|
+
- **Scope is mandatory, and `any-assignee` / `unassigned` do not count as one.** They describe the issue, not
|
|
148
|
+
the caller (measured: unscoped `no:assignee` is tens of millions of results).
|
|
149
|
+
|
|
150
|
+
**Corrected along the way:** the source claimed GitHub honors `assignee:*` only for a single repository, which
|
|
151
|
+
was the standing argument for refusing a multi-repo "assigned to anyone" read. Measured
|
|
152
|
+
(`is:issue is:open archived:false`): kepler 111 + vscode-gitlens 133 = **244** for both repos together, and
|
|
153
|
+
`org:gitkraken` 315. Any scope works; only the unscoped form is meaningless (6.7 M), which is what the guards
|
|
154
|
+
actually refuse.
|
|
155
|
+
|
|
156
|
+
**Provider coverage:** GitHub/GHE only. GitLab and Azure declare no `issueSearch` capability, so the read is
|
|
157
|
+
refused there rather than serving an unnarrowed list — unimplemented, not impossible: GitLab maps to `search` /
|
|
158
|
+
`updated_after` / `labels` / `milestone` with one relationship per REST call (its `assignee_username` +
|
|
159
|
+
`author_username` compose with AND, so relationships must stay separate drains), and Azure to per-project WIQL.
|
|
160
|
+
`withoutLinkedPullRequest` and free text have no equivalent on either.
|
|
161
|
+
|
|
162
|
+
**Not done, deliberately:** `broadenIssues` was left as-is rather than reimplemented on top of this. It is a
|
|
163
|
+
multi-provider, multi-org fan-out with its own result type and per-org cursor bundle, so only its inner
|
|
164
|
+
per-org read could be swapped; and its "all visible" breadth maps to an OMITTED relationship set, not to
|
|
165
|
+
`any-assignee`, which excludes unassigned issues. See the note in `reads/broaden.ts` and
|
|
166
|
+
[`integrations.md` §9](./integrations.md#9-per-provider-behavior-worth-designing-around).
|
|
167
|
+
|
|
168
|
+
**Kepler-side follow-up:** `ProviderScopeFilter` carries a single `repo?: string` today and needs the criteria
|
|
169
|
+
set; the `provider-data` adapter then routes "All visible" to `searchIssuesPage` + `countIssues`.
|
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.5.
|
|
4
|
+
"version": "0.5.104",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "GitKraken",
|
package/src/git/models/issue.ts
CHANGED
|
@@ -7,6 +7,87 @@ import type { RepositoryIdentityDescriptor } from './repositoryIdentities.js';
|
|
|
7
7
|
/** Selects which issue states a read should include. `all` covers open + closed. */
|
|
8
8
|
export type IssueStateFilter = 'open' | 'closed' | 'all';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* One relationship a filtered issue search can constrain on.
|
|
12
|
+
*
|
|
13
|
+
* The last two are USER-INDEPENDENT — they describe the issue, not the caller — and neither is a SCOPE: either
|
|
14
|
+
* one alone matches every such issue on the host, so a search using one still needs a repository/org scope.
|
|
15
|
+
*/
|
|
16
|
+
export type IssueSearchRelationship =
|
|
17
|
+
/** Authored by the current user. */
|
|
18
|
+
| 'authored'
|
|
19
|
+
/** Assigned to the current user. */
|
|
20
|
+
| 'assigned'
|
|
21
|
+
/** Mentions the current user. */
|
|
22
|
+
| 'mentioned'
|
|
23
|
+
/** Assigned to ANYONE. Excludes unassigned issues — see {@link IssueSearchCriteria.relationships}. */
|
|
24
|
+
| 'any-assignee'
|
|
25
|
+
/** Assigned to nobody. The complement of `any-assignee`, not a broadening of it. */
|
|
26
|
+
| 'unassigned';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* What a filtered issue search narrows on: a provider-neutral criteria set, translated to each provider's own
|
|
30
|
+
* query language by its integration.
|
|
31
|
+
*
|
|
32
|
+
* Structured rather than a provider query string so a consumer can render filter chips and hide the ones a
|
|
33
|
+
* provider can't express, and so the same criteria mean the same thing on a provider whose query language looks
|
|
34
|
+
* nothing like GitHub's search syntax. Every field is validated against the provider's declared capability
|
|
35
|
+
* BEFORE the read runs, all-or-nothing: a criterion the provider can't express server-side refuses the whole
|
|
36
|
+
* read rather than serving a list that was never narrowed. Client-side narrowing is not an option — the dropped
|
|
37
|
+
* items still counted toward the page and cursor the provider produced, so a post-filter would leave the items
|
|
38
|
+
* describing a different result set than the paging that came with them.
|
|
39
|
+
*/
|
|
40
|
+
export interface IssueSearchCriteria {
|
|
41
|
+
/**
|
|
42
|
+
* Relationships to constrain on, OR-ed together (each becomes its own provider query, unioned and deduped).
|
|
43
|
+
* Omitted means NO relationship constraint at all — every issue in scope, assigned or not — which requires a
|
|
44
|
+
* repository/org scope.
|
|
45
|
+
*
|
|
46
|
+
* `any-assignee` and `unassigned` partition the scope between them and are mutually exclusive; passing both is
|
|
47
|
+
* refused rather than silently dropping one. Note that "all visible issues" is the OMITTED case, NOT
|
|
48
|
+
* `any-assignee`, which excludes unassigned issues.
|
|
49
|
+
*/
|
|
50
|
+
relationships?: IssueSearchRelationship[];
|
|
51
|
+
/**
|
|
52
|
+
* Free text, matched by the provider's own relevance rules — NOT a substring match, and not a way to smuggle
|
|
53
|
+
* qualifiers: a token that would read as one is dropped, as are quotes and control characters, so text can
|
|
54
|
+
* never re-scope the search. The structured criteria are the qualifier channel.
|
|
55
|
+
*/
|
|
56
|
+
text?: string;
|
|
57
|
+
/** Issue states to include. Omitted reads open issues only. */
|
|
58
|
+
state?: IssueStateFilter;
|
|
59
|
+
/** Labels the issue must carry — AND-ed, matching GitHub's own `label:` semantics. */
|
|
60
|
+
labels?: string[];
|
|
61
|
+
milestone?: string;
|
|
62
|
+
/** ISO date (`YYYY-MM-DD`). The most effective narrowing criterion by far on a large scope. */
|
|
63
|
+
updatedAfter?: string;
|
|
64
|
+
/** ISO date (`YYYY-MM-DD`). */
|
|
65
|
+
createdAfter?: string;
|
|
66
|
+
/** Issues with no linked pull request. */
|
|
67
|
+
withoutLinkedPullRequest?: boolean;
|
|
68
|
+
/** Includes issues in archived repositories, which are excluded by default. */
|
|
69
|
+
includeArchived?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Which {@link IssueSearchCriteria} fields a provider's filtered issue search can express server-side.
|
|
74
|
+
*
|
|
75
|
+
* An empty `relationships` with every flag false means the provider has NO filtered issue search — the read is
|
|
76
|
+
* refused outright, so a consumer should offer neither the surface nor its chips.
|
|
77
|
+
*/
|
|
78
|
+
export interface IssueSearchCapabilities {
|
|
79
|
+
/** Relationships the search can constrain on. Empty means the search itself is unsupported. */
|
|
80
|
+
relationships: IssueSearchRelationship[];
|
|
81
|
+
text: boolean;
|
|
82
|
+
labels: boolean;
|
|
83
|
+
milestone: boolean;
|
|
84
|
+
updatedAfter: boolean;
|
|
85
|
+
createdAfter: boolean;
|
|
86
|
+
withoutLinkedPullRequest: boolean;
|
|
87
|
+
/** Whether {@link IssueSearchCriteria.state} can select anything other than the provider's default (open). */
|
|
88
|
+
states: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
10
91
|
export interface IssueShape extends IssueOrPullRequest {
|
|
11
92
|
/** `undefined` when the provider can't resolve the author, e.g. a deleted GitHub account */
|
|
12
93
|
author: IssueMember | undefined;
|
|
@@ -76,3 +76,16 @@ export interface GitHubApiConfig {
|
|
|
76
76
|
*/
|
|
77
77
|
getLaunchpadIgnoredOrganizations?(): string[];
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The maximum number of results GitHub's search will serve for ONE query, however it is paged. A search whose
|
|
82
|
+
* `issueCount` exceeds this cannot be read in full: the items past the ceiling are UNREACHABLE, not merely
|
|
83
|
+
* unfetched — no budget, no retry and no cursor returns them.
|
|
84
|
+
*
|
|
85
|
+
* It lives in this leaf module rather than beside the search that detects it because the facade's
|
|
86
|
+
* provider-agnostic metadata table reports the same ceiling to consumers, and the two must be one number: a
|
|
87
|
+
* duplicated literal drifts silently, and drift here means quoting a limit the client doesn't enforce. Importing
|
|
88
|
+
* it from `github.ts` would have pulled that module — and its octokit dependencies — eagerly into the graph of
|
|
89
|
+
* one of the most widely imported modules in the integrations package, for the sake of a single integer.
|
|
90
|
+
*/
|
|
91
|
+
export const githubSearchResultLimit = 1000;
|