Repository navigation
ref(toolkit-core): Move pagination, cache, retry, and transport under api-client - #1736
Merged
Merged
Conversation
… api-client Generated by the ts-morph codemod move-transport-layer.mjs (script in the PR description), run from a scratch directory with ts-morph@28.0.0 installed: node move-transport-layer.mjs <repo-root> oxfmt <added and modified files> It moves pagination.ts, api-retry.ts (now retry.ts), api-transport.ts (now transport.ts), and the response-cache policy modules into toolkit-core/src/api-client/, with their tests. The node:zlib codec stays at src/response-cache/codec.ts as a host-side module. It rewrites every importer, including the CLI and MCP package subpaths, and the toolkit-core exports. Co-Authored-By: BYK <burak.kaya@sentry.io>
Comments in the CLI, MCP, and transport, the toolkit-core README, and api-patterns.md name the moved modules by their new api-client paths. Co-Authored-By: BYK <burak.kaya@sentry.io>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
BYK
approved these changes
Oct 29, 2026
BYK
pushed a commit
that referenced
this pull request
Oct 29, 2026
#1737) Adds the shared request core in `toolkit-core/src/api-client/core.ts` and switches `SentryApiService` onto it. This is the first slice of #1669 task 8.4. #1736 has merged, so this PR now targets `main`. 8.4 as written covers the request core *and* every domain's endpoint wrappers. `SentryApiService` is about 6,000 lines with about 100 endpoint methods, so I'm splitting 8.4 into the core first, then domain PRs on top. This PR is the core. ## What changes - **`createApiClientCore`** (toolkit-core, browser-portable). The host injects its transport, per-request headers, and logger. `request(origin, path, init, { allowStatuses })`: - sends to `<origin>/api/0<path>` with `Content-Type: application/json` plus the host's headers (they replace any the caller passes, as before); - turns network failures into `Unable to connect to <url> - …`, as a `ConfigurationError` for ENOTFOUND, EAI_AGAIN, ECONNREFUSED, ETIMEDOUT, and connect timeouts; - rethrows the caller's abort unchanged; - maps an error response to the status's `ApiError` through `readErrorResponse`, with the same three warnings as before. `requestJson` also parses the body with `readJsonResponse`. - **`SentryApiService`.** `request` and `requestJSON` keep their signatures and delegate to the core, so no endpoint method changes. `SentryApiService` supplies `User-Agent`, `Authorization` (still a `ConfigurationError` for a malformed token, before anything is sent), the `X-Sentry-MCP-*` attribution headers, the existing shared transport, and `logWarn` under the `api.client` scope. Its copies of the network error table and the error-response mapping are deleted. - **Still in mcp-core:** `sdkClient`, which now goes through the core via `request`, and `sdkListPage`. - Moving `sdkClient` means toolkit-core depends on `@sentry/api`. Today that resolves a second `@sentry/api` copy, because toolkit-core pins TypeScript through the `^5.8.3` catalog while mcp-core resolves 5.9.3. The domain-wrapper PRs will fix that skew first. - `sdkListPage` stays because its rules differ from the shared `paginate`: an exact `per_page`, and an error on extra rows or a repeated cursor. Merging them would change behavior, so that waits for the merge step. - **Docs:** the toolkit-core README's API client section and `api-patterns.md` describe the core. ## Behavior changes - **Warning `host` field.** The three error-response warnings (HTML page, unparseable JSON, no string `detail`) now log the host the request actually went to. Before, they logged the service's default host even when a call overrode it, e.g. a regional host. Messages, error classes, `detail`, `responseBody`, and `retryAfterMs` don't change. This PR does not touch `packages/mcp-server`, `packages/cli/src/lib/mcp.ts`, or the evals stdio entry. The `@sentry/mcp-server` retirement PR owns those. ## Commits Both commits are hand-written. No codemod. 1. `5710b8e8d` Add `createApiClientCore` with unit tests 2. `fc6aa0875` Switch `SentryApiService` onto it, plus docs ## Verification - `pnpm run tsc` (including toolkit-core's browser typecheck), `pnpm run lint`, and `node scripts/check-doc-links.mjs` pass. - toolkit-core: 319 passed, including 13 new `core.test.ts` cases covering headers, header errors, detail mapping, allowed statuses, rate-limit wait, HTML and unreadable bodies, network errors, abort, and `requestJson`. - mcp-core: 1881 passed, 6 skipped. The existing `client.test.ts` MSW suites cover the delegated path unchanged. - mcp-server: 87 passed. mcp-cloudflare: 443 passed. - The CLI is untouched in this PR; its turn is 8.5. <!-- junior-session-footer:start --> <!-- junior-conversation-id:agent%3Aa6eddc0161cff61914454da9f74fcdf1 --> -- [View Junior Session in Sentry](https://sentry.sentry.io/explore/conversations/agent%3Aa6eddc0161cff61914454da9f74fcdf1/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
sentry-junior Bot
added a commit
that referenced
this pull request
Oct 29, 2026
Co-Authored-By: BYK <burak.kaya@sentry.io>
BYK
added a commit
that referenced
this pull request
Oct 29, 2026
…mar (#1730) Part of #1564. Dashboard commands parsed their positionals by hand inside `func`, and issue commands used their own grammar (#1729). This moves both onto the shared ID grammar (`grammar/id-arguments.pegjs`, #1714), parsed before `func` runs. Event view's issue checks (#1733, #1738) use the new issue kind too. **Shared grammar: kinds with names.** `IdKind.names` adds one idea to `id-arguments.pegjs`: a kind may name an item without its ID, and anything that is not, and may not be, an ID is a name. Kinds without names keep their grammar paths exactly as before. Kinds with names keep each line whole (no splitting at spaces), so `Top 10` stays one title and never becomes title `Top` + ID `10`. - `ID_KINDS.dashboard`: a numeric ID or a title, which may hold spaces and slashes (`Latency/p95`). `<org>/[<project>/]<digits>` reads as a path only when slugs come before the digits, so `Errors.v2/123` and `/123` are titles. After a target, the title takes the rest of the arguments (`my-org/ My Dashboard`). A bare first word names an org (`orgScoped`). - `ID_KINDS.issue`: an identifier (numeric ID, short ID, selector, issue URL) is one whole token (`wholeIds`). `src/lib/issue-identifier.ts` takes it apart with the #1729 grammar, which is unchanged. Text with spaces is a name, meaning a search. - `dashboard-arguments.pegjs` is gone. `dashboard list` keeps its thin `parseDashboardListArgs`, because a bare word there is a title filter (see #1711). **Bare first word: org or title?** For `dashboard view My Dashboard`, the parser makes no decision. It returns the structural reading (org `My`, title `Dashboard`) and the whole input as one title (`nameWithTarget`, surfaced as `orgOrTitle`). `resolveDashboardInput` decides after auth: - First it checks whether `My` is an org: the cached org list and region cache, then `GET /organizations/My/` only on a cache miss. - If the org exists, the title lookup in that org is final, even when it misses. - On a 404 or 403 it warns ``No org 'My'; treating "My Dashboard" as the dashboard title. Use my-org/ to name an org.`` and looks up "My Dashboard" in the auto-detected org. - If that misses too, one error names both readings (no org `My` to find `Dashboard` in, and no `My Dashboard` in `<detected-org>`) and hints at `<org>/ Dashboard` and at quoting. - A 5xx or network error from the org check propagates, with no fallback. - It never falls back for `my-org/ …` (explicit org), a numeric ref, or a URL. **Issue search text.** `issue view "login timeout"` resolves through `GET /organizations/{org}/issues/?query=…` in the detected org. One hit is the issue. Zero or several hits raise a `ResolutionError` that lists up to 5 candidates as runnable commands and points at `sentry issue list <org>/ --query '…'`. I checked the endpoint against getsentry/sentry (`organization_group_index.py`, `group_index/index.py`): `query` takes free text, an exact event ID or short ID gets a fast lookup, and an explicit query replaces the default `is:unresolved`. **Search hints.** Text where a trace, span, log, or replay ID belongs (`trace view login`) now fails with ``Not a trace ID: "login". To search, try `sentry trace list --query 'login'`.``. The hint comes from `validate()`, so commands that recover malformed IDs (`trace view`, `span view`, `log view`) recover first and use the hint only as their final error. Event IDs get no hint, because `event list` lists one issue's events. **event view.** `ID_KINDS.issue.distinctForm` (uppercase short ID or numeric) replaces `looksLikeIssueShortId` + `isAllDigits` and keeps #1738's order: `<org>/<short-id>`, then `<short-id>/<event-id>` (even an all-digit event ID), then `<org>/<numeric-id>`. I added a regression test for the all-digit event ID and one for that order. **Behavior changes:** - `dashboard view my-org My Dashboard` (unquoted title) looks up "My Dashboard". Before, it used `My` and dropped `Dashboard`. The same goes for `my-org/ My Dashboard` and `https://acme.sentry.io/ My Dashboard`. - `dashboard view My Dashboard`: when there is no org `My` (404/403), it falls back to the title "My Dashboard" in the detected org, with the warning above. If neither reading finds a dashboard, one error names both. Dashboard commands with one dashboard ref get this (`view`, `restore`, `revisions`, `widget edit/delete`, and `widget add` after it splits off its widget title). - A numeric ref first is the dashboard, as with every ID kind: `dashboard view 42 X`, `my-org/42 X`. Before, the first of two arguments was always the target. After a target, a numeric ID keeps the rest as extra (`my-org 123 extra` → ID 123, ignored `extra`) instead of joining them into a title. Arguments after the ID go through the shared rules: extra, reversed (`42 my-org/`), or the shared "reference different projects" error when they name another target. - A lone non-Sentry URL is no longer taken as a dashboard title. - `issue view "login timeout"` (text with spaces) searches instead of failing as a malformed identifier. Control characters and URLs are still rejected. - Trace/span/log/replay IDs that are plain text get a search hint instead of "Invalid trace ID". - Parse errors (no dashboard, an org URL without a dashboard, a dashboard URL passed to `list`, an invalid target slug) fail before the auth guard, as in the earlier commits on this branch. They print `Failed to parse "…" for <placeholder>: …` and exit 252. - `widget add/edit/delete`: an argument error comes before flag errors like "Specify --index or --title". `widget add`'s positional completes cached dashboards. `parseDashboardTarget` had no callers left and is removed. **Lint ratchet (#1739):** the dashboard entries are off both allow-lists, since these commands now parse before `func`. The issue entries (`issue/utils.ts`, `issue/merge.ts`, which need an array issue positional) stay. **Syncing:** this branch merges `main` (no rebase). 54ebfa2 and 7f52f19 merge #1729, #1732–#1734, #1736, #1738 and #1739. Verification: - `tsc`, `pnpm run lint` (oxlint, format, ast-grep) and `scripts/cli-lint-rules.test.mjs` (70/70) pass. - vitest on `test/lib`, `test/grammar` and the dashboard, issue, event, trace, span, log, replay and feedback command tests: 10,158 passed. The 6 failures are the sandbox-only permission tests (completions EACCES/EPERM, code-scanner, file-changes, telemetry readonly, upgrade lock), which also fail on `main`. The full CLI suite shows the same 6, plus e2e/eval files that need a build or credentials. - New tests: - The bare-org fallback: the org from cache and from the API (no fallback), org missing then fallback hit with the warning, a 403, org missing then fallback miss with the combined error, a 5xx (propagates, no fallback), `my-org/ My Dashboard`, and a numeric ref. - Issue search with 0, 1 and several hits. - Search hints, including `validate: false` deferral. - #1738's event view order. - Differential runs against this PR's previous parsers: - Issue arguments: 8,575 cases. 1,388 former errors are now searches (text with spaces), with 0 other differences. - Dashboard view: 32,552 cases over 0–3 arguments. Every difference is in one of the categories above: a numeric ID first (2,600), arguments after a dashboard URL that name another target (688), a numeric ID after a target with more words (475), and a lone non-Sentry URL (1). <!-- junior-request-attribution:start --> via **BYK**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:agent%3A5a5e7e4543124325a44d731c08dd6089 --> -- [View Junior Session in Sentry](https://sentry.sentry.io/explore/conversations/agent%3A5a5e7e4543124325a44d731c08dd6089/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <burak.kaya@sentry.io>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves pagination, the response-cache policy, retry, and transport under
toolkit-core/src/api-client/, the layout they will have inside@sentry/api(#1669, task 8.3). Every importer is rewritten. Nothing else changes.It's the second PR in the group 8 stack. #1732, the foundation, has merged, so this one targets
main.What changes
pagination.ts→api-client/pagination.tsapi-retry.ts→api-client/retry.tsapi-transport.ts→api-client/transport.tsresponse-cache/{body,index,invalidation,keys,memory-store,policy,store,tiers}.ts→api-client/response-cache/response-cache/codec.ts. It's thenode:zlibcodec, host storage that won't move into@sentry/api. It's still excluded from the browser typecheck, and the./response-cache/codecexport doesn't change../pagination,./api-retry,./api-transport, and./response-cacheare replaced by the existing./api-client/*wildcard plus one./api-client/response-cacheentry for the directory index.@sentry/toolkit-coreisprivate, and every consumer is in this workspace and rewritten here.script/generate-api-schema.ts),mcp-core/src/api-client/client.ts, and toolkit-core.api-patterns.md, the toolkit-core README, and code comments name the new subpaths.Behavior changes
None.
This PR does not touch
packages/mcp-server,packages/cli/src/lib/mcp.ts, or the evals stdio entry. The@sentry/mcp-serverretirement PR owns those.Commits
85fae9151Move the modules and rewrite importers (generated,move-transport-layer.mjs)85df504bePoint comments and docs at the new paths (hand)Verification
origin/main(9409280c1), run the command below, then oxfmt on the changed files. The tree matches85fae9151.pnpm run tsc(including toolkit-core's browser typecheck),pnpm run lint, andnode scripts/check-doc-links.mjspass.test/lib test/commands test/script11895 passed, 46 skipped, 6 failed.CAP_DAC_OVERRIDE, so these permission tests can't make a file unreadable:completions(EACCES, EPERM),dsn/code-scanner(unreadable file),init/tools/file-changes(I/O failure),telemetry(read-only DB), andupgrade(acquireLockpermission error). CI runs them normally.Codemod
The script isn't in the tree. To re-run it, check out the parent of the generated commit and run it from a scratch directory with
ts-morph@28.0.0installed. Then run oxfmt on the added, modified, and renamed package files.move-transport-layer.mjs(commit 85fae91)via BYK.
--
View Junior Session in Sentry