@rustrak/client 0.18.1 → 0.19.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 (119) hide show
  1. package/README.md +312 -123
  2. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  3. package/dist/client.cjs +86 -0
  4. package/dist/client.cjs.map +1 -0
  5. package/dist/client.d.cts +18 -0
  6. package/dist/client.d.cts.map +1 -0
  7. package/dist/client.d.mts +18 -0
  8. package/dist/client.d.mts.map +1 -0
  9. package/dist/client.mjs +86 -0
  10. package/dist/client.mjs.map +1 -0
  11. package/dist/config.d.cts +10 -0
  12. package/dist/config.d.cts.map +1 -0
  13. package/dist/config.d.mts +10 -0
  14. package/dist/config.d.mts.map +1 -0
  15. package/dist/errors.cjs +52 -0
  16. package/dist/errors.cjs.map +1 -0
  17. package/dist/errors.d.cts +77 -0
  18. package/dist/errors.d.cts.map +1 -0
  19. package/dist/errors.d.mts +77 -0
  20. package/dist/errors.d.mts.map +1 -0
  21. package/dist/errors.mjs +48 -0
  22. package/dist/errors.mjs.map +1 -0
  23. package/dist/generated/client/client.gen.cjs +174 -0
  24. package/dist/generated/client/client.gen.cjs.map +1 -0
  25. package/dist/generated/client/client.gen.mjs +174 -0
  26. package/dist/generated/client/client.gen.mjs.map +1 -0
  27. package/dist/generated/client/types.gen.d.cts +71 -0
  28. package/dist/generated/client/types.gen.d.cts.map +1 -0
  29. package/dist/generated/client/types.gen.d.mts +71 -0
  30. package/dist/generated/client/types.gen.d.mts.map +1 -0
  31. package/dist/generated/client/utils.gen.cjs +184 -0
  32. package/dist/generated/client/utils.gen.cjs.map +1 -0
  33. package/dist/generated/client/utils.gen.d.cts +21 -0
  34. package/dist/generated/client/utils.gen.d.cts.map +1 -0
  35. package/dist/generated/client/utils.gen.d.mts +21 -0
  36. package/dist/generated/client/utils.gen.d.mts.map +1 -0
  37. package/dist/generated/client/utils.gen.mjs +177 -0
  38. package/dist/generated/client/utils.gen.mjs.map +1 -0
  39. package/dist/generated/client.gen.cjs +7 -0
  40. package/dist/generated/client.gen.cjs.map +1 -0
  41. package/dist/generated/client.gen.mjs +8 -0
  42. package/dist/generated/client.gen.mjs.map +1 -0
  43. package/dist/generated/core/auth.gen.cjs +12 -0
  44. package/dist/generated/core/auth.gen.cjs.map +1 -0
  45. package/dist/generated/core/auth.gen.d.cts +11 -0
  46. package/dist/generated/core/auth.gen.d.cts.map +1 -0
  47. package/dist/generated/core/auth.gen.d.mts +11 -0
  48. package/dist/generated/core/auth.gen.d.mts.map +1 -0
  49. package/dist/generated/core/auth.gen.mjs +12 -0
  50. package/dist/generated/core/auth.gen.mjs.map +1 -0
  51. package/dist/generated/core/bodySerializer.gen.cjs +21 -0
  52. package/dist/generated/core/bodySerializer.gen.cjs.map +1 -0
  53. package/dist/generated/core/bodySerializer.gen.d.cts +15 -0
  54. package/dist/generated/core/bodySerializer.gen.d.cts.map +1 -0
  55. package/dist/generated/core/bodySerializer.gen.d.mts +15 -0
  56. package/dist/generated/core/bodySerializer.gen.d.mts.map +1 -0
  57. package/dist/generated/core/bodySerializer.gen.mjs +20 -0
  58. package/dist/generated/core/bodySerializer.gen.mjs.map +1 -0
  59. package/dist/generated/core/pathSerializer.gen.cjs +87 -0
  60. package/dist/generated/core/pathSerializer.gen.cjs.map +1 -0
  61. package/dist/generated/core/pathSerializer.gen.d.cts +9 -0
  62. package/dist/generated/core/pathSerializer.gen.d.cts.map +1 -0
  63. package/dist/generated/core/pathSerializer.gen.d.mts +9 -0
  64. package/dist/generated/core/pathSerializer.gen.d.mts.map +1 -0
  65. package/dist/generated/core/pathSerializer.gen.mjs +82 -0
  66. package/dist/generated/core/pathSerializer.gen.mjs.map +1 -0
  67. package/dist/generated/core/serverSentEvents.gen.cjs +96 -0
  68. package/dist/generated/core/serverSentEvents.gen.cjs.map +1 -0
  69. package/dist/generated/core/serverSentEvents.gen.d.cts +25 -0
  70. package/dist/generated/core/serverSentEvents.gen.d.cts.map +1 -0
  71. package/dist/generated/core/serverSentEvents.gen.d.mts +25 -0
  72. package/dist/generated/core/serverSentEvents.gen.d.mts.map +1 -0
  73. package/dist/generated/core/serverSentEvents.gen.mjs +96 -0
  74. package/dist/generated/core/serverSentEvents.gen.mjs.map +1 -0
  75. package/dist/generated/core/types.gen.d.cts +27 -0
  76. package/dist/generated/core/types.gen.d.cts.map +1 -0
  77. package/dist/generated/core/types.gen.d.mts +27 -0
  78. package/dist/generated/core/types.gen.d.mts.map +1 -0
  79. package/dist/generated/core/utils.gen.cjs +81 -0
  80. package/dist/generated/core/utils.gen.cjs.map +1 -0
  81. package/dist/generated/core/utils.gen.mjs +78 -0
  82. package/dist/generated/core/utils.gen.mjs.map +1 -0
  83. package/dist/generated/sdk.gen.cjs +3138 -0
  84. package/dist/generated/sdk.gen.cjs.map +1 -0
  85. package/dist/generated/sdk.gen.d.cts +276 -0
  86. package/dist/generated/sdk.gen.d.cts.map +1 -0
  87. package/dist/generated/sdk.gen.d.mts +276 -0
  88. package/dist/generated/sdk.gen.d.mts.map +1 -0
  89. package/dist/generated/sdk.gen.mjs +3110 -0
  90. package/dist/generated/sdk.gen.mjs.map +1 -0
  91. package/dist/generated/types.gen.d.cts +3679 -0
  92. package/dist/generated/types.gen.d.cts.map +1 -0
  93. package/dist/generated/types.gen.d.mts +3679 -0
  94. package/dist/generated/types.gen.d.mts.map +1 -0
  95. package/dist/generated/zod.gen.cjs +3076 -0
  96. package/dist/generated/zod.gen.cjs.map +1 -0
  97. package/dist/generated/zod.gen.mjs +2618 -0
  98. package/dist/generated/zod.gen.mjs.map +1 -0
  99. package/dist/index.cjs +15 -3248
  100. package/dist/index.d.cts +6 -2037
  101. package/dist/index.d.mts +6 -0
  102. package/dist/index.mjs +4 -0
  103. package/dist/result.cjs +59 -0
  104. package/dist/result.cjs.map +1 -0
  105. package/dist/result.d.cts +15 -0
  106. package/dist/result.d.cts.map +1 -0
  107. package/dist/result.d.mts +15 -0
  108. package/dist/result.d.mts.map +1 -0
  109. package/dist/result.mjs +55 -0
  110. package/dist/result.mjs.map +1 -0
  111. package/dist/utils/http.cjs +339 -0
  112. package/dist/utils/http.cjs.map +1 -0
  113. package/dist/utils/http.mjs +336 -0
  114. package/dist/utils/http.mjs.map +1 -0
  115. package/package.json +17 -12
  116. package/dist/index.cjs.map +0 -1
  117. package/dist/index.d.ts +0 -2037
  118. package/dist/index.js +0 -3232
  119. package/dist/index.js.map +0 -1
package/README.md CHANGED
@@ -34,7 +34,7 @@
34
34
 
35
35
  ---
36
36
 
37
- `@rustrak/client` is the official TypeScript client for [Rustrak](https://rustrak.dev) — an ultra-lightweight, self-hosted error tracking system compatible with any Sentry SDK. This package wraps the Rustrak REST API with full type safety, runtime validation via Zod, built-in retry logic, and structured error handling. Total bundle size: ~28 KB.
37
+ `@rustrak/client` is the official TypeScript client for [Rustrak](https://rustrak.dev) — an ultra-lightweight, self-hosted error tracking system compatible with any Sentry SDK. It is generated from the server's OpenAPI spec, so it covers every endpoint the server documents, and wraps each one with runtime validation, retries and structured errors. No runtime dependency besides Zod; HTTP is the platform's `fetch`.
38
38
 
39
39
  ## Installation
40
40
 
@@ -46,7 +46,7 @@ pnpm add @rustrak/client
46
46
  yarn add @rustrak/client
47
47
  ```
48
48
 
49
- **Requirements**: Node.js ≥ 18, TypeScript ≥ 5
49
+ **Requirements**: Node.js ≥ 20 (or any runtime with `fetch`), TypeScript ≥ 5
50
50
 
51
51
  ## Quick Start
52
52
 
@@ -66,15 +66,16 @@ if (!projects.success) {
66
66
  }
67
67
  console.log(projects.data.items);
68
68
 
69
- // Paginate through open issues
70
- const issues = await client.issues.list(1, { sort: 'last_seen', order: 'desc' });
71
- if (issues.success) {
72
- const { items, next_cursor, has_more } = issues.data;
73
- }
69
+ // Path parameters, query and body go in one object, named as the API names them
70
+ const issues = await client.issues.list({
71
+ path: { project_id: 1 },
72
+ query: { sort: 'last_seen', order: 'desc', per_page: 50 },
73
+ });
74
74
 
75
75
  // Resolve an issue
76
- const resolved = await client.issues.updateState(1, 'issue-id', {
77
- is_resolved: true,
76
+ const resolved = await client.issues.update({
77
+ path: { project_id: 1, issue_id: 'issue-id' },
78
+ body: { status: 'resolved' },
78
79
  });
79
80
  ```
80
81
 
@@ -82,78 +83,31 @@ const resolved = await client.issues.updateState(1, 'issue-id', {
82
83
 
83
84
  ## Features
84
85
 
85
- - **Type-safe** — All API responses validated at runtime with Zod; types are inferred from schemas
86
- - **Lightweight** — ~28 KB total (ky 3 KB + zod 10 KB + client 15 KB)
87
- - **Automatic retry** — Exponential backoff on transient failures (408, 429, 5xx)
86
+ - **Complete**: one method per operation in the server's OpenAPI spec, regenerated with the server, never written by hand
87
+ - **Validated both ways**: requests and responses are checked against Zod schemas generated from the same spec; a mismatch is a typed error, not a crash
88
88
  - **No exceptions**: every method returns `Result<T, RustrakError>`, so failure is in the type, and the value survives the React server/client boundary
89
- - **Cursor pagination** — First-class support for paginated responses across all list endpoints
89
+ - **Tree-shakeable**: ESM is published one module per file with `sideEffects: false`
90
+ - **Retries**: exponential backoff on 408 and 5xx for idempotent methods (`GET`, `PUT`, `DELETE`)
90
91
  - **Redacted 5xx**: a server error never carries a server-supplied message, so internal detail cannot reach a UI
91
- - **High test coverage**: 430+ tests (unit + integration with MSW)
92
-
93
- ## API Reference
94
92
 
95
- ### Configuration
93
+ ## Configuration
96
94
 
97
95
  ```typescript
98
96
  const client = new RustrakClient({
99
97
  baseUrl: 'https://rustrak.example.com', // required
100
- token: 'your-bearer-token', // required
101
- timeout: 30000, // optional, ms (default: 30000)
98
+ token: 'your-bearer-token', // optional: omit to use the session cookie
99
+ timeout: 30000, // optional, ms per attempt (default: 30000)
102
100
  maxRetries: 2, // optional (default: 2)
103
101
  headers: {}, // optional custom headers
104
102
  });
105
103
  ```
106
104
 
107
- ### Projects
108
-
109
- ```typescript
110
- const projects = await client.projects.list();
111
- const project = await client.projects.get(1);
112
- const created = await client.projects.create({ name: 'My App', slug: 'my-app' });
113
- const updated = await client.projects.update(1, { name: 'New Name' });
114
- await client.projects.delete(1);
115
- ```
116
-
117
- ### Issues
118
-
119
- ```typescript
120
- // List with filters and cursor pagination
121
- const { items, next_cursor, has_more } = await client.issues.list(projectId, {
122
- sort: 'last_seen', // 'digest_order' | 'last_seen'
123
- order: 'desc', // 'asc' | 'desc'
124
- include_resolved: false,
125
- cursor: 'eyJzb3J0...', // from previous response
126
- });
127
-
128
- const issue = await client.issues.get(projectId, issueId);
129
- await client.issues.updateState(projectId, issueId, { is_resolved: true });
130
- await client.issues.delete(projectId, issueId);
131
- ```
132
-
133
- ### Events
134
-
135
- ```typescript
136
- const { items } = await client.events.list(projectId, issueId, { order: 'desc' });
137
- const event = await client.events.get(projectId, issueId, eventId);
138
- // Resolve the ID returned by a Sentry SDK without knowing its issue first.
139
- const captured = await client.events.getBySentryId(projectId, sentryEventId);
140
- console.log(event.data); // Full Sentry event payload
141
- ```
142
-
143
- ### Auth Tokens
144
-
145
- ```typescript
146
- const tokens = await client.tokens.list();
147
- const created = await client.tokens.create({ description: 'CI token' });
148
- if (created.success) {
149
- console.log(created.data.token); // Save this, shown only once
150
- }
151
- await client.tokens.delete(1);
152
- ```
105
+ Every method takes the request's own parts and nothing else: `path`, `query`,
106
+ `body`, plus `headers` and an `AbortSignal` as `signal`.
153
107
 
154
108
  ## Error Handling
155
109
 
156
- Resource methods do not throw. Each returns a `Result`, a plain discriminated
110
+ Methods do not throw. Each returns a `Result`, a plain discriminated
157
111
  union mirroring Zod's `safeParse`:
158
112
 
159
113
  ```typescript
@@ -188,7 +142,7 @@ if (!result.success) {
188
142
  return;
189
143
  }
190
144
 
191
- console.log(result.data.items);
145
+ console.log(result.data);
192
146
  ```
193
147
 
194
148
  ### The error union
@@ -207,7 +161,7 @@ console.log(result.data.items);
207
161
  | `server_error` | 5xx | ✅ | ✅ | **Message is always a fixed generic string** |
208
162
  | `network` | n/a | no | ✅ | DNS, refused connection, TLS, timeout |
209
163
  | `invalid_response` | n/a | no | ❌ | 2xx whose body failed its schema |
210
- | `invalid_request` | n/a | no | ❌ | Your input failed a pre-flight check; nothing was sent |
164
+ | `invalid_request` | n/a | no | ❌ | Your input failed its schema; nothing was sent |
211
165
 
212
166
  `status` is `number`, never a literal, so a proxy-generated 502 or a future
213
167
  status is a value you can log rather than a type error.
@@ -229,9 +183,10 @@ information that must not cross a serialization boundary:
229
183
  ```typescript
230
184
  import { Ok, Err, unwrap, unwrapOr, mapResult } from '@rustrak/client';
231
185
 
232
- mapResult(await client.projects.get(1), (project) => project.name);
233
- unwrap(await client.projects.get(1)); // throws on failure: opting back in
234
- unwrapOr(await client.stats.summary(1, "24h"), null); // see the warning below
186
+ const project = await client.projects.get({ path: { id: 1 } });
187
+ mapResult(project, (p) => p.name);
188
+ unwrap(project); // throws on failure: opting back in
189
+ unwrapOr(project, null); // see the warning below
235
190
  ```
236
191
 
237
192
  > **`unwrapOr` is not for rendering.** `unwrapOr(await client.projects.list(),
@@ -248,74 +203,308 @@ what lets a failure be returned from a Server Component or a Server Action and
248
203
  read in a Client Component: React's Flight serializer rejects class instances,
249
204
  which is why there are no error classes here.
250
205
 
251
- ## Next.js Integration
206
+ ## TypeScript
252
207
 
253
- ### Server Component
208
+ Every request and response type in the spec is exported under the name the
209
+ spec gives it:
254
210
 
255
211
  ```typescript
256
- import { RustrakClient } from '@rustrak/client';
212
+ import type {
213
+ IssueResponse, // a schema
214
+ ProjectResponse,
215
+ IssuesListData, // what `client.issues.list` takes
216
+ IssuesListResponse, // what it returns on success
217
+ Result,
218
+ RustrakError,
219
+ } from '@rustrak/client';
220
+ ```
257
221
 
258
- export default async function ProjectsPage() {
259
- const client = new RustrakClient({
260
- baseUrl: process.env.RUSTRAK_API_URL!,
261
- token: process.env.RUSTRAK_API_TOKEN!,
262
- });
263
- const projects = await client.projects.list();
264
-
265
- if (!projects.success) {
266
- // `kind` is what decides: only 'unauthenticated' means "send to login".
267
- // Redirecting on 'network' or 'server_error' turns a flaky connection
268
- // into a login loop.
269
- if (projects.error.kind === 'unauthenticated') redirect('/login');
270
- return <LoadFailed error={projects.error} />;
271
- }
222
+ ## API Reference
272
223
 
273
- return <ProjectsList projects={projects.data.items} />;
274
- }
275
- ```
224
+ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
225
+
226
+ <!-- api:start -->
227
+ <!-- Generated by `pnpm generate` from openapi.json. Do not edit by hand. -->
228
+
229
+ ### Agents
230
+
231
+ | Method | Endpoint | Description |
232
+ |---|---|---|
233
+ | `agents.duration({ path, query? })` | `GET /api/projects/{project_id}/agents/duration` | Time-bucketed avg/p95 duration for `agent`/`ai_client` spans. |
234
+ | `agents.environments({ path })` | `GET /api/projects/{project_id}/agents/environments` | The options the environment filter offers, read from the data rather than hardcoded — an installation may name its environments anything. |
235
+ | `agents.modelsCalls({ path, query? })` | `GET /api/projects/{project_id}/agents/models/calls` | Top models by LLM call count (`gen_ai.operation.type:ai_client`). |
236
+ | `agents.modelsTable({ path, query? })` | `GET /api/projects/{project_id}/agents/models` | |
237
+ | `agents.modelsTokens({ path, query? })` | `GET /api/projects/{project_id}/agents/models/tokens` | Top models by total tokens used (`gen_ai.operation.type:ai_client`). |
238
+ | `agents.runs({ path, query? })` | `GET /api/projects/{project_id}/agents/runs` | Time-bucketed count of agent-run spans (`gen_ai.operation.type:agent`). |
239
+ | `agents.summary({ path, query? })` | `GET /api/projects/{project_id}/agents/summary` | Agent runs, LLM calls, tool calls, errors, tokens and latency over the selected window — the numbers the six charts cannot state outright. |
240
+ | `agents.tools({ path, query? })` | `GET /api/projects/{project_id}/agents/tools` | Top tools by call count (`gen_ai.operation.type:tool`). |
241
+ | `agents.toolsTable({ path, query? })` | `GET /api/projects/{project_id}/agents/tools/stats` | |
242
+ | `agents.traces({ path, query? })` | `GET /api/projects/{project_id}/agents/traces` | Paginated per-trace_id aggregate (duration, tokens, tool usage) across all AI spans sharing that trace, regardless of origin. |
243
+
244
+ ### Alert Channels
245
+
246
+ | Method | Endpoint | Description |
247
+ |---|---|---|
248
+ | `alertChannels.create({ body })` | `POST /api/integrations` | |
249
+ | `alertChannels.delete({ path })` | `DELETE /api/integrations/{id}` | |
250
+ | `alertChannels.get({ path })` | `GET /api/integrations/{id}` | |
251
+ | `alertChannels.list()` | `GET /api/integrations` | |
252
+ | `alertChannels.previewTemplate({ body })` | `POST /api/integrations/preview-template` | Renders a Custom Webhook template against a sample payload and hands back the body that would be sent. |
253
+ | `alertChannels.test({ path, body })` | `POST /api/integrations/{id}/test` | |
254
+ | `alertChannels.update({ path, body })` | `PATCH /api/integrations/{id}` | |
255
+
256
+ ### Alert Rules
257
+
258
+ | Method | Endpoint | Description |
259
+ |---|---|---|
260
+ | `alertRules.create({ path, body })` | `POST /api/projects/{project_id}/alert-rules` | |
261
+ | `alertRules.delete({ path })` | `DELETE /api/projects/{project_id}/alert-rules/{rule_id}` | |
262
+ | `alertRules.get({ path })` | `GET /api/projects/{project_id}/alert-rules/{rule_id}` | |
263
+ | `alertRules.list({ path })` | `GET /api/projects/{project_id}/alert-rules` | |
264
+ | `alertRules.listHistory({ path, query? })` | `GET /api/projects/{project_id}/alert-history` | |
265
+ | `alertRules.update({ path, body })` | `PATCH /api/projects/{project_id}/alert-rules/{rule_id}` | |
266
+
267
+ ### Auth
268
+
269
+ | Method | Endpoint | Description |
270
+ |---|---|---|
271
+ | `auth.acceptInvitation({ body })` | `POST /auth/accept-invitation` | Accept a pending invitation: creates the user (with the invite's email + role) and logs in. |
272
+ | `auth.changePassword({ body })` | `POST /auth/me/password` | Change the current user's password |
273
+ | `auth.confirmSsoLink({ body })` | `POST /auth/sso/link` | Link the pending SSO identity to its account once the account's password is given, and sign in. |
274
+ | `auth.getCurrentUser()` | `GET /auth/me` | Get current authenticated user |
275
+ | `auth.getInvitation({ path })` | `GET /auth/invitation/{token}` | Returns the invitation's details if it is still acceptable (for the accept page). |
276
+ | `auth.login({ body })` | `POST /auth/login` | Authenticate user and create session |
277
+ | `auth.logout()` | `POST /auth/logout` | Clear session |
278
+ | `auth.register({ body })` | `POST /auth/register` | Registration is invite-only — always rejected. |
279
+ | `auth.ssoCallback({ query? })` | `GET /auth/sso/callback` | Validate the provider response, resolve or provision the local account, and establish the normal Rustrak session. |
280
+ | `auth.ssoConfig()` | `GET /auth/sso/config` | Public, non-sensitive information used to render the login page. |
281
+ | `auth.ssoLink()` | `GET /auth/sso/link` | The account an SSO login matched, shown while its owner confirms. |
282
+ | `auth.ssoStart()` | `POST /auth/sso/start` | Create a one-time OIDC authorization request and retain its protections in the encrypted session cookie. |
283
+ | `auth.updateCurrentUser({ body })` | `PATCH /auth/me` | Update the current user's presentation preferences |
276
284
 
277
- ### Server Action
285
+ ### Events
278
286
 
279
- ```typescript
280
- 'use server';
281
- import { RustrakClient } from '@rustrak/client';
287
+ | Method | Endpoint | Description |
288
+ |---|---|---|
289
+ | `events.get({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}/events/{event_id}` | Gets a single event with full data |
290
+ | `events.getBySentryId({ path })` | `GET /api/projects/{project_id}/events/sentry/{event_id}` | Resolve the ID returned by a Sentry SDK without already knowing the issue. |
291
+ | `events.list({ path, query? })` | `GET /api/projects/{project_id}/issues/{issue_id}/events` | Lists events for an issue with cursor-based pagination |
292
+
293
+ ### GitHub
294
+
295
+ | Method | Endpoint | Description |
296
+ |---|---|---|
297
+ | `github.callback({ query })` | `GET /api/github/callback` | GitHub sends the browser here after creating the app; the code trades for its credentials. |
298
+ | `github.createIssue({ path, body })` | `POST /api/projects/{project_id}/issues/{issue_id}/github/issues` | Opens a GitHub issue and links it. |
299
+ | `github.deleteProjectRepository({ path })` | `DELETE /api/projects/{project_id}/github` | |
300
+ | `github.disconnect()` | `DELETE /api/github` | |
301
+ | `github.getMyLogin()` | `GET /api/github/me` | The GitHub username assignees sync through. |
302
+ | `github.getProjectRepository({ path })` | `GET /api/projects/{project_id}/github` | |
303
+ | `github.installed({ query })` | `GET /api/github/installed` | GitHub sends the browser here after the app is installed on an account. |
304
+ | `github.issue({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}/github` | The issue's links, the commits that may have caused it, and who owns its culprit file. |
305
+ | `github.linkIssue({ path, body })` | `POST /api/projects/{project_id}/issues/{issue_id}/github/links` | Links an existing GitHub issue or pull request. |
306
+ | `github.manifest({ body })` | `POST /api/github/manifest` | What the dashboard posts to GitHub so GitHub creates an app already configured for this server. |
307
+ | `github.putMyLogin({ body })` | `PUT /api/github/me` | |
308
+ | `github.putProjectRepository({ path, body })` | `PUT /api/projects/{project_id}/github` | |
309
+ | `github.repositories({ query })` | `GET /api/github/repositories` | The repositories a project can point at, for whoever may change that project's settings. |
310
+ | `github.status()` | `GET /api/github` | |
311
+ | `github.syncCodeowners({ path })` | `POST /api/projects/{project_id}/github/codeowners` | Copies the repository's CODEOWNERS so ownership can be answered without asking GitHub each time. |
312
+ | `github.unlinkIssue({ path })` | `DELETE /api/projects/{project_id}/issues/{issue_id}/github/links/{link_id}` | |
313
+ | `github.webhook({ body })` | `POST /api/github/webhook` | Public, and trusted only through the signature GitHub computes with the app's webhook secret. |
314
+
315
+ ### Health
316
+
317
+ | Method | Endpoint | Description |
318
+ |---|---|---|
319
+ | `health.liveness()` | `GET /health` | Liveness check - is the process running? |
320
+ | `health.readiness()` | `GET /health/ready` | Readiness check - is the service ready to handle requests? |
321
+ | `health.version()` | `GET /health/version` | Version check - returns the server version compiled from Cargo.toml. |
322
+
323
+ ### Invitations
324
+
325
+ | Method | Endpoint | Description |
326
+ |---|---|---|
327
+ | `invitations.create({ body })` | `POST /api/invitations` | Create an invitation (admin only) |
328
+ | `invitations.list()` | `GET /api/invitations` | List invitations (admin only) |
329
+ | `invitations.revoke({ path })` | `DELETE /api/invitations/{token}` | Revoke a pending invitation (admin only) |
282
330
 
283
- export async function resolveIssue(projectId: number, issueId: string) {
284
- const client = new RustrakClient({
285
- baseUrl: process.env.RUSTRAK_API_URL!,
286
- token: process.env.RUSTRAK_API_TOKEN!,
287
- });
288
- // The Result is returned as-is: it is serializable, so the client component
289
- // receives the failure instead of an opaque "An error occurred" digest.
290
- return client.issues.updateState(projectId, issueId, { is_resolved: true });
291
- }
292
- ```
331
+ ### Issues
293
332
 
294
- ## TypeScript
333
+ | Method | Endpoint | Description |
334
+ |---|---|---|
335
+ | `issues.bulkDelete({ path, body })` | `DELETE /api/projects/{project_id}/issues` | Bulk delete by ids or filter |
336
+ | `issues.bulkUpdate({ path, body })` | `PUT /api/projects/{project_id}/issues` | Bulk mutate |
337
+ | `issues.createComment({ path, body })` | `POST /api/projects/{project_id}/issues/{issue_id}/comments` | Add a note. |
338
+ | `issues.createUserReport({ path, body })` | `POST /api/projects/{project_id}/issues/{issue_id}/user-reports` | Attach user feedback to an issue. |
339
+ | `issues.delete({ path })` | `DELETE /api/projects/{project_id}/issues/{issue_id}` | Hard-deletes an issue and all associated events |
340
+ | `issues.get({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}` | Gets a single issue by ID |
341
+ | `issues.getActivity({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}/activity` | Chronological activity log (incl. comments). |
342
+ | `issues.getAggregates({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}/aggregates` | Returns the unique user count and top tags for the issue. |
343
+ | `issues.getHashes({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}/hashes` | Lists the grouping hashes that map to an issue. |
344
+ | `issues.getStats({ path, query? })` | `GET /api/projects/{project_id}/issues/{issue_id}/stats` | Zero-filled event-count timeseries. |
345
+ | `issues.getTagValues({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}/tags/{key}` | Lists the distinct values (with counts) for a tag key across the issue. |
346
+ | `issues.list({ path, query? })` | `GET /api/projects/{project_id}/issues` | Lists issues for a project with offset-based pagination |
347
+ | `issues.listUserReports({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}/user-reports` | List user feedback for an issue. |
348
+ | `issues.markSeen({ path })` | `POST /api/projects/{project_id}/issues/{issue_id}/seen` | Mark the issue as seen by the current user. |
349
+ | `issues.setBookmark({ path, body })` | `PUT /api/projects/{project_id}/issues/{issue_id}/bookmark` | Set/clear the per-user bookmark. |
350
+ | `issues.setSubscription({ path, body })` | `PUT /api/projects/{project_id}/issues/{issue_id}/subscription` | Set/clear the per-user subscription. |
351
+ | `issues.update({ path, body })` | `PATCH /api/projects/{project_id}/issues/{issue_id}` | Updates issue state (resolve, mute, etc.) |
352
+
353
+ ### Logs
354
+
355
+ | Method | Endpoint | Description |
356
+ |---|---|---|
357
+ | `logs.list({ path, query? })` | `GET /api/projects/{project_id}/logs` | Lists logs for a project with offset-based pagination (newest first by log timestamp), optionally filtered by level/trace_id. |
358
+
359
+ ### Members
360
+
361
+ | Method | Endpoint | Description |
362
+ |---|---|---|
363
+ | `members.list({ path })` | `GET /api/projects/{project_id}/members` | |
364
+ | `members.remove({ path })` | `DELETE /api/projects/{project_id}/members/{user_id}` | |
365
+ | `members.upsert({ path, body })` | `PUT /api/projects/{project_id}/members` | |
366
+
367
+ ### Monitors
368
+
369
+ | Method | Endpoint | Description |
370
+ |---|---|---|
371
+ | `monitors.create({ path, body })` | `POST /api/projects/{project_id}/monitors` | |
372
+ | `monitors.delete({ path })` | `DELETE /api/projects/{project_id}/monitors/{slug}` | |
373
+ | `monitors.get({ path })` | `GET /api/projects/{project_id}/monitors/{slug}` | |
374
+ | `monitors.list({ path })` | `GET /api/projects/{project_id}/monitors` | |
375
+ | `monitors.listCheckins({ path, query? })` | `GET /api/projects/{project_id}/monitors/{slug}/checkins` | |
376
+ | `monitors.update({ path, body })` | `PUT /api/projects/{project_id}/monitors/{slug}` | |
377
+
378
+ ### OAuth
379
+
380
+ | Method | Endpoint | Description |
381
+ |---|---|---|
382
+ | `oauth.decideAuthorization({ body })` | `POST /api/oauth/authorize` | Approves or denies an authorization request as the signed-in user. |
383
+ | `oauth.getAuthorization({ query })` | `GET /api/oauth/authorize` | Verifies an authorization request and says who is asking. |
384
+ | `oauth.listGrants()` | `GET /api/oauth/grants` | Lists the assistants connected to the signed-in user's account. |
385
+ | `oauth.revokeGrant({ path })` | `DELETE /api/oauth/grants/{id}` | Disconnects an assistant: its tokens stop working at once. |
295
386
 
296
- All types are exported and inferred from Zod schemas — single source of truth:
387
+ ### Projects
297
388
 
298
- ```typescript
299
- import type {
300
- Project,
301
- Issue,
302
- Event,
303
- EventDetail,
304
- AuthToken,
305
- PaginatedResponse,
306
- CreateProject,
307
- UpdateIssueState,
308
- Result,
309
- RustrakError,
310
- RustrakErrorKind,
311
- } from '@rustrak/client';
312
- ```
389
+ | Method | Endpoint | Description |
390
+ |---|---|---|
391
+ | `projects.create({ body })` | `POST /api/projects` | Create a new project |
392
+ | `projects.delete({ path })` | `DELETE /api/projects/{id}` | Delete a project |
393
+ | `projects.get({ path })` | `GET /api/projects/{id}` | Get a project by ID |
394
+ | `projects.getRateLimits()` | `GET /api/rate-limits` | What a project without limits of its own follows |
395
+ | `projects.list({ query? })` | `GET /api/projects` | List projects with pagination |
396
+ | `projects.update({ path, body })` | `PATCH /api/projects/{id}` | Update a project |
397
+
398
+ ### Releases
399
+
400
+ | Method | Endpoint | Description |
401
+ |---|---|---|
402
+ | `releases.create({ path, body })` | `POST /api/0/projects/{org_slug}/{project_slug}/releases/` | Sentry-compatible release creation. |
403
+ | `releases.finalize({ path, body })` | `PUT /api/0/projects/{org_slug}/{project_slug}/releases/{version}/` | Generic partial update (`ref`, `url`, `dateReleased`). |
404
+ | `releases.newIssues({ path, query? })` | `GET /api/projects/{project_id}/releases/{release}/new-issues` | |
405
+
406
+ ### Schedules
407
+
408
+ | Method | Endpoint | Description |
409
+ |---|---|---|
410
+ | `schedules.preview({ body })` | `POST /api/schedules/preview` | Validates a schedule and lists its next five runs from now, so no client ever parses a crontab itself. |
411
+
412
+ ### Sessions
413
+
414
+ | Method | Endpoint | Description |
415
+ |---|---|---|
416
+ | `sessions.getStats({ path, query? })` | `GET /api/projects/{project_id}/sessions/stats` | |
417
+ | `sessions.getSummary({ path, query? })` | `GET /api/projects/{project_id}/sessions/summary` | |
418
+ | `sessions.getTimeseries({ path, query? })` | `GET /api/projects/{project_id}/sessions/timeseries` | |
419
+
420
+ ### Settings
421
+
422
+ | Method | Endpoint | Description |
423
+ |---|---|---|
424
+ | `settings.getMcp()` | `GET /api/settings/mcp` | Whether the MCP endpoint at `/mcp` is on. |
425
+ | `settings.updateMcp({ body })` | `PUT /api/settings/mcp` | Turns the MCP endpoint at `/mcp` on or off. |
426
+
427
+ ### Source Maps
428
+
429
+ | Method | Endpoint | Description |
430
+ |---|---|---|
431
+ | `sourceMaps.artifactBundleAssemble({ path, body })` | `POST /api/0/organizations/{org_slug}/artifactbundle/assemble/` | |
432
+ | `sourceMaps.chunkUpload({ path, body })` | `POST /api/0/organizations/{org_slug}/chunk-upload/` | |
433
+ | `sourceMaps.chunkUploadCapability({ path })` | `GET /api/0/organizations/{org_slug}/chunk-upload/` | |
434
+ | `sourceMaps.list({ path })` | `GET /api/0/projects/{org_slug}/{project_slug}/files/source-maps/` | |
435
+ | `sourceMaps.orgDetails({ path })` | `GET /api/0/organizations/{org_slug}/` | Always returns 200 with synthetic org data — sentry-cli validates this before uploading. |
436
+
437
+ ### Spans
438
+
439
+ | Method | Endpoint | Description |
440
+ |---|---|---|
441
+ | `spans.get({ path })` | `GET /api/projects/{project_id}/spans/{span_id}` | Returns one span together with its attributes and tags — the `gen_ai.*` payload (prompts, responses, tool arguments and results) that the list response deliberately omits, since `spans.data` is untrimmed and a whole trace's worth of it would dwarf the waterfall it is drawn from. |
442
+ | `spans.list({ path, query? })` | `GET /api/projects/{project_id}/spans` | Lists spans for a project with offset-based pagination (newest by start_timestamp first), optionally filtered by op/status/trace_id. |
443
+
444
+ ### Stats
445
+
446
+ | Method | Endpoint | Description |
447
+ |---|---|---|
448
+ | `stats.getEventTimeseries({ path, query? })` | `GET /api/projects/{project_id}/events/stats` | |
449
+ | `stats.getSummary({ path, query? })` | `GET /api/projects/{project_id}/stats/summary` | |
450
+
451
+ ### Storage
452
+
453
+ | Method | Endpoint | Description |
454
+ |---|---|---|
455
+ | `storage.executeCleanup({ body })` | `POST /api/storage/cleanup` | Starts deleting old data and the issues it empties. |
456
+ | `storage.gcSourceMaps()` | `POST /api/storage/source-maps/gc` | Delete orphaned source maps (DB + disk). |
457
+ | `storage.getAge()` | `GET /api/storage/age` | Rows per age bucket, per project and kind. |
458
+ | `storage.getCleanupStatus()` | `GET /api/storage/cleanup/status` | Progress of the running cleanup, or the outcome of the last one. |
459
+ | `storage.getProjects()` | `GET /api/storage/projects` | Per-project counts and estimated weight. |
460
+ | `storage.getRetention()` | `GET /api/storage/retention` | |
461
+ | `storage.getSummary()` | `GET /api/storage/summary` | Row counts, DB size, and source-map weight. |
462
+ | `storage.previewCleanup({ body })` | `POST /api/storage/cleanup/preview` | Dry-run count, mutates nothing. |
463
+ | `storage.previewRetention({ body })` | `POST /api/storage/retention/preview` | What saving these settings would delete on the next cleanup, without saving or deleting anything. |
464
+ | `storage.previewSourceMapGc()` | `POST /api/storage/source-maps/gc/preview` | Dry-run orphan count, deletes nothing. |
465
+ | `storage.runRetention()` | `POST /api/storage/retention/run` | Enforces the saved retention now, without waiting for the schedule. |
466
+ | `storage.updateRetention({ body })` | `PUT /api/storage/retention` | |
467
+
468
+ ### Team
469
+
470
+ | Method | Endpoint | Description |
471
+ |---|---|---|
472
+ | `team.deleteUser({ path })` | `DELETE /api/team/{user_id}` | Remove a user from the instance (admin only) |
473
+ | `team.list()` | `GET /api/team` | List all users (admin only) |
474
+ | `team.updateRole({ path, body })` | `PATCH /api/team/{user_id}/role` | Change a user's global role (admin only) |
475
+
476
+ ### Telemetry
477
+
478
+ | Method | Endpoint | Description |
479
+ |---|---|---|
480
+ | `telemetry.preview()` | `GET /api/telemetry/preview` | |
481
+
482
+ ### Tokens
483
+
484
+ | Method | Endpoint | Description |
485
+ |---|---|---|
486
+ | `tokens.create({ body })` | `POST /api/tokens` | Create a new token (scoped to the acting user) |
487
+ | `tokens.delete({ path })` | `DELETE /api/tokens/{id}` | Revoke a token |
488
+ | `tokens.get({ path })` | `GET /api/tokens/{id}` | Reveal full token value |
489
+ | `tokens.list()` | `GET /api/tokens` | List tokens (masked) |
490
+
491
+ ### Transactions
492
+
493
+ | Method | Endpoint | Description |
494
+ |---|---|---|
495
+ | `transactions.get({ path })` | `GET /api/projects/{project_id}/transactions/{transaction_id}` | Returns a single transaction with its full Sentry payload (spans, contexts, measurements, tags) for the performance detail view. |
496
+ | `transactions.getSpans({ path })` | `GET /api/projects/{project_id}/transactions/{transaction_id}/spans` | Returns the indexed spans extracted from a transaction, ordered for the waterfall view (by start_timestamp ascending). |
497
+ | `transactions.getStatGroup({ path, query })` | `GET /api/projects/{project_id}/transactions/stats/group` | Returns the aggregate stats for a single transaction group — a direct lookup so the summary header works regardless of how many groups the project has. |
498
+ | `transactions.getStats({ path, query? })` | `GET /api/projects/{project_id}/transactions/stats` | Returns paginated aggregate performance stats (count, p50/p95/p99 latency, failure rate) grouped by transaction name + op, most frequent first. |
499
+ | `transactions.list({ path, query? })` | `GET /api/projects/{project_id}/transactions` | Lists transactions for a project with offset-based pagination (newest first). |
500
+
501
+ <!-- api:end -->
313
502
 
314
503
  ## Related Packages
315
504
 
316
505
  | Package | Description |
317
506
  |---|---|
318
- | [`@rustrak/mcp`](https://www.npmjs.com/package/@rustrak/mcp) | MCP server — gives Claude Desktop, Cursor, and Continue direct access to your Rustrak instance via 18 tools |
507
+ | [MCP server](https://rustrak.dev/sdks/mcp) | Built into the Rustrak server at `/mcp` — gives Claude, Cursor and other MCP clients direct access to your instance |
319
508
 
320
509
  ## What is Rustrak?
321
510
 
@@ -0,0 +1,23 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ exports.__toESM = __toESM;
@@ -0,0 +1,86 @@
1
+ const require_utils_gen = require("./generated/client/utils.gen.cjs");
2
+ const require_client_gen = require("./generated/client/client.gen.cjs");
3
+ const require_sdk_gen = require("./generated/sdk.gen.cjs");
4
+ const require_result = require("./result.cjs");
5
+ const require_http = require("./utils/http.cjs");
6
+ let zod = require("zod");
7
+ //#region src/client.ts
8
+ /**
9
+ * Main Rustrak API client. Every method returns a `Result`; nothing throws for
10
+ * an expected failure.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const client = new RustrakClient({
15
+ * baseUrl: 'http://localhost:8080',
16
+ * token: 'your-api-token',
17
+ * });
18
+ *
19
+ * const issues = await client.issues.list({ path: { project_id: 1 } });
20
+ * if (issues.success) console.log(issues.data.items);
21
+ * ```
22
+ */
23
+ const RustrakClient = class {
24
+ constructor(config) {
25
+ return Object.setPrototypeOf(createApi(config), new.target.prototype);
26
+ }
27
+ };
28
+ function createApi(config) {
29
+ const sdk = new require_sdk_gen.Rustrak({ client: require_client_gen.createClient(require_utils_gen.createConfig({
30
+ baseUrl: config.baseUrl,
31
+ credentials: "include",
32
+ fetch: require_http.createFetch(config),
33
+ parseAs: "json",
34
+ headers: {
35
+ Accept: "application/json",
36
+ ...config.headers,
37
+ ...config.token && { Authorization: `Bearer ${config.token}` }
38
+ }
39
+ })) });
40
+ const api = {};
41
+ for (const name of Object.getOwnPropertyNames(require_sdk_gen.Rustrak.prototype)) {
42
+ if (name === "constructor") continue;
43
+ const resource = sdk[name];
44
+ const methods = {};
45
+ for (const method of Object.getOwnPropertyNames(Object.getPrototypeOf(resource))) {
46
+ if (method === "constructor") continue;
47
+ methods[method] = async (options = {}) => toResult(await resource[method]({
48
+ ...options,
49
+ headers: options.headers instanceof Headers || Array.isArray(options.headers) ? Object.fromEntries(new Headers(options.headers)) : options.headers,
50
+ throwOnError: false
51
+ }));
52
+ }
53
+ api[name] = methods;
54
+ }
55
+ return api;
56
+ }
57
+ /**
58
+ * The boundary: the generated client's `{ data, error, response }` becomes a
59
+ * `Result`.
60
+ *
61
+ * Only failures this file recognises become an `Err`. Anything else, a
62
+ * `TypeError` from a genuine bug, is rethrown so it crashes where it can be
63
+ * debugged instead of being laundered into `{ success: false }`.
64
+ */
65
+ function toResult({ data, error, response }) {
66
+ if (error === void 0) return require_result.Ok(data);
67
+ if (require_http.isTransportFailure(error)) return require_result.Err(error.rustrakError);
68
+ if (response && !response.ok) return require_result.Err(require_http.transformHttpError(response, error));
69
+ if (!response && error instanceof zod.ZodError) return require_result.Err({
70
+ kind: "invalid_request",
71
+ message: "The request input did not match the expected schema."
72
+ });
73
+ if (response && error instanceof SyntaxError) return require_result.Err({
74
+ kind: "invalid_response",
75
+ message: "The server returned a body that is not valid JSON."
76
+ });
77
+ if (response && error instanceof zod.ZodError) return require_result.Err({
78
+ kind: "invalid_response",
79
+ message: "The server response did not match the expected schema."
80
+ });
81
+ throw error;
82
+ }
83
+ //#endregion
84
+ exports.RustrakClient = RustrakClient;
85
+
86
+ //# sourceMappingURL=client.cjs.map