@rustrak/client 0.24.0 → 0.26.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 (56) hide show
  1. package/README.md +76 -61
  2. package/dist/client.cjs +4 -3
  3. package/dist/client.cjs.map +1 -1
  4. package/dist/client.d.cts +2 -1
  5. package/dist/client.d.cts.map +1 -1
  6. package/dist/client.d.mts +2 -1
  7. package/dist/client.d.mts.map +1 -1
  8. package/dist/client.mjs +5 -4
  9. package/dist/client.mjs.map +1 -1
  10. package/dist/config.d.cts.map +1 -1
  11. package/dist/config.d.mts.map +1 -1
  12. package/dist/errors.cjs +19 -6
  13. package/dist/errors.cjs.map +1 -1
  14. package/dist/errors.d.cts +1 -1
  15. package/dist/errors.d.cts.map +1 -1
  16. package/dist/errors.d.mts +1 -1
  17. package/dist/errors.d.mts.map +1 -1
  18. package/dist/errors.mjs +19 -7
  19. package/dist/errors.mjs.map +1 -1
  20. package/dist/generated/binary.gen.cjs +6 -0
  21. package/dist/generated/binary.gen.cjs.map +1 -0
  22. package/dist/generated/binary.gen.d.cts +2 -0
  23. package/dist/generated/binary.gen.d.cts.map +1 -0
  24. package/dist/generated/binary.gen.d.mts +2 -0
  25. package/dist/generated/binary.gen.d.mts.map +1 -0
  26. package/dist/generated/binary.gen.mjs +6 -0
  27. package/dist/generated/binary.gen.mjs.map +1 -0
  28. package/dist/generated/sdk.gen.cjs +371 -199
  29. package/dist/generated/sdk.gen.cjs.map +1 -1
  30. package/dist/generated/sdk.gen.d.cts +24 -2
  31. package/dist/generated/sdk.gen.d.cts.map +1 -1
  32. package/dist/generated/sdk.gen.d.mts +24 -2
  33. package/dist/generated/sdk.gen.d.mts.map +1 -1
  34. package/dist/generated/sdk.gen.mjs +370 -201
  35. package/dist/generated/sdk.gen.mjs.map +1 -1
  36. package/dist/generated/types.gen.d.cts +313 -47
  37. package/dist/generated/types.gen.d.cts.map +1 -1
  38. package/dist/generated/types.gen.d.mts +313 -47
  39. package/dist/generated/types.gen.d.mts.map +1 -1
  40. package/dist/generated/zod.gen.cjs +315 -88
  41. package/dist/generated/zod.gen.cjs.map +1 -1
  42. package/dist/generated/zod.gen.mjs +275 -84
  43. package/dist/generated/zod.gen.mjs.map +1 -1
  44. package/dist/index.d.cts +2 -2
  45. package/dist/index.d.mts +2 -2
  46. package/dist/result.cjs +0 -2
  47. package/dist/result.cjs.map +1 -1
  48. package/dist/result.d.cts.map +1 -1
  49. package/dist/result.d.mts.map +1 -1
  50. package/dist/result.mjs +0 -2
  51. package/dist/result.mjs.map +1 -1
  52. package/dist/utils/http.cjs +18 -31
  53. package/dist/utils/http.cjs.map +1 -1
  54. package/dist/utils/http.mjs +20 -32
  55. package/dist/utils/http.mjs.map +1 -1
  56. package/package.json +3 -5
package/README.md CHANGED
@@ -87,7 +87,7 @@ const resolved = await client.issues.update({
87
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
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
+ - **Retries**: exponential backoff on an unreachable server and on 408, 500, 502, 503 and 504, for idempotent methods (`GET`, `PUT`, `DELETE`). Not on a timeout or a 429: `isRetryable` says yes to both, but when to retry is the caller's call
91
91
  - **Redacted 5xx**: a server error never carries a server-supplied message, so internal detail cannot reach a UI
92
92
 
93
93
  ## Configuration
@@ -157,8 +157,8 @@ console.log(result.data);
157
157
  | `gone` | 410 | ✅ | ❌ | |
158
158
  | `payload_too_large` | 413 | ✅ | ❌ | Envelope ingestion only |
159
159
  | `rate_limited` | 429 | ✅ | ✅ | Optional `retryAfter` in seconds |
160
- | `client_error` | other 4xx | ✅ | ❌ | Catch-all, so no status is unrepresentable |
161
- | `server_error` | 5xx | ✅ | ✅ | **Message is always a fixed generic string** |
160
+ | `client_error` | other 4xx | ✅ | 408 only | Catch-all, so no status is unrepresentable |
161
+ | `server_error` | 5xx | ✅ | 500, 502, 503, 504 | **Message is always a fixed generic string** |
162
162
  | `network` | n/a | no | ✅ | DNS, refused connection, TLS, timeout |
163
163
  | `invalid_response` | n/a | no | ❌ | 2xx whose body failed its schema |
164
164
  | `invalid_request` | n/a | no | ❌ | Your input failed its schema; nothing was sent |
@@ -226,6 +226,19 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
226
226
  <!-- api:start -->
227
227
  <!-- Generated by `pnpm generate` from openapi.json. Do not edit by hand. -->
228
228
 
229
+ ### Access
230
+
231
+ | Method | Endpoint | Description |
232
+ |---|---|---|
233
+ | `access.catalog()` | `GET /api/access/catalog` | Lists every resource a permission can name, with its module, scope and actions. |
234
+ | `access.me()` | `GET /api/access/me` | Returns what the caller may do, on the instance and on each project. |
235
+ | `roles.create({ body })` | `POST /api/roles` | Creates a role from permissions the caller holds themselves. |
236
+ | `roles.delete({ path })` | `DELETE /api/roles/{id}` | Deletes a role nobody holds. |
237
+ | `roles.list()` | `GET /api/roles` | Lists every role with its permissions and how often it is assigned. |
238
+ | `roles.update({ path, body })` | `PUT /api/roles/{id}` | Replaces a role's name, description and permissions. |
239
+ | `users.assignments({ path })` | `GET /api/users/{id}/assignments` | Lists the roles a user holds, on the instance and on each project. |
240
+ | `users.setAssignments({ path, body })` | `PUT /api/users/{id}/assignments` | Replaces every role and personal permission a user holds. |
241
+
229
242
  ### Agents
230
243
 
231
244
  | Method | Endpoint | Description |
@@ -235,54 +248,53 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
235
248
  | `agents.duration({ path, query? })` | `GET /api/projects/{project_id}/agents/duration` | Time-bucketed avg/p95 duration for `agent`/`ai_client` spans. |
236
249
  | `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. |
237
250
  | `agents.modelsCalls({ path, query? })` | `GET /api/projects/{project_id}/agents/models/calls` | Top models by LLM call count (`gen_ai.operation.type:ai_client`). |
238
- | `agents.modelsTable({ path, query? })` | `GET /api/projects/{project_id}/agents/models` | |
251
+ | `agents.modelsTable({ path, query? })` | `GET /api/projects/{project_id}/agents/models` | Lists the AI models a project's agents called, with requests, tokens and latency. |
239
252
  | `agents.modelsTokens({ path, query? })` | `GET /api/projects/{project_id}/agents/models/tokens` | Top models by total tokens used (`gen_ai.operation.type:ai_client`). |
240
253
  | `agents.runs({ path, query? })` | `GET /api/projects/{project_id}/agents/runs` | Time-bucketed count of agent-run spans (`gen_ai.operation.type:agent`). |
241
254
  | `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. |
242
255
  | `agents.tools({ path, query? })` | `GET /api/projects/{project_id}/agents/tools` | Top tools by call count (`gen_ai.operation.type:tool`). |
243
- | `agents.toolsTable({ path, query? })` | `GET /api/projects/{project_id}/agents/tools/stats` | |
256
+ | `agents.toolsTable({ path, query? })` | `GET /api/projects/{project_id}/agents/tools/stats` | Lists the tools a project's agents called, with calls, errors and latency. |
244
257
  | `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. |
245
258
 
246
259
  ### Alert Channels
247
260
 
248
261
  | Method | Endpoint | Description |
249
262
  |---|---|---|
250
- | `alertChannels.create({ body })` | `POST /api/integrations` | |
251
- | `alertChannels.delete({ path })` | `DELETE /api/integrations/{id}` | |
252
- | `alertChannels.get({ path })` | `GET /api/integrations/{id}` | |
253
- | `alertChannels.list()` | `GET /api/integrations` | |
263
+ | `alertChannels.create({ body })` | `POST /api/integrations` | Creates a notification integration (webhook, Slack, email…). |
264
+ | `alertChannels.delete({ path })` | `DELETE /api/integrations/{id}` | Deletes a notification integration. |
265
+ | `alertChannels.get({ path })` | `GET /api/integrations/{id}` | Gets one notification integration. |
266
+ | `alertChannels.list()` | `GET /api/integrations` | Lists the channels alerts can go through. |
254
267
  | `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. |
255
- | `alertChannels.test({ path, body })` | `POST /api/integrations/{id}/test` | |
256
- | `alertChannels.update({ path, body })` | `PATCH /api/integrations/{id}` | |
268
+ | `alertChannels.test({ path, body })` | `POST /api/integrations/{id}/test` | Sends a test notification through an integration. |
269
+ | `alertChannels.update({ path, body })` | `PATCH /api/integrations/{id}` | Updates a notification integration. |
257
270
 
258
271
  ### Alert Rules
259
272
 
260
273
  | Method | Endpoint | Description |
261
274
  |---|---|---|
262
- | `alertRules.create({ path, body })` | `POST /api/projects/{project_id}/alert-rules` | |
263
- | `alertRules.delete({ path })` | `DELETE /api/projects/{project_id}/alert-rules/{rule_id}` | |
264
- | `alertRules.get({ path })` | `GET /api/projects/{project_id}/alert-rules/{rule_id}` | |
275
+ | `alertRules.create({ path, body })` | `POST /api/projects/{project_id}/alert-rules` | Creates an alert rule in a project. |
276
+ | `alertRules.delete({ path })` | `DELETE /api/projects/{project_id}/alert-rules/{rule_id}` | Deletes an alert rule. |
277
+ | `alertRules.get({ path })` | `GET /api/projects/{project_id}/alert-rules/{rule_id}` | Gets one alert rule. |
265
278
  | `alertRules.historyByDay({ path, query? })` | `GET /api/projects/{project_id}/alert-history/daily` | Count a project's alert deliveries per day, by outcome. |
266
- | `alertRules.list({ path })` | `GET /api/projects/{project_id}/alert-rules` | |
267
- | `alertRules.listHistory({ path, query? })` | `GET /api/projects/{project_id}/alert-history` | |
268
- | `alertRules.update({ path, body })` | `PATCH /api/projects/{project_id}/alert-rules/{rule_id}` | |
279
+ | `alertRules.list({ path })` | `GET /api/projects/{project_id}/alert-rules` | Lists a project's alert rules. |
280
+ | `alertRules.listHistory({ path, query? })` | `GET /api/projects/{project_id}/alert-history` | Lists the alerts a project sent, newest first. |
281
+ | `alertRules.update({ path, body })` | `PATCH /api/projects/{project_id}/alert-rules/{rule_id}` | Updates an alert rule. |
269
282
 
270
283
  ### Auth
271
284
 
272
285
  | Method | Endpoint | Description |
273
286
  |---|---|---|
274
287
  | `auth.acceptInvitation({ body })` | `POST /auth/accept-invitation` | Accept a pending invitation: creates the user (with the invite's email + role) and logs in. |
275
- | `auth.changePassword({ body })` | `POST /auth/me/password` | Change the current user's password |
288
+ | `auth.changePassword({ body })` | `POST /auth/me/password` | Change the current user's password. |
276
289
  | `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. |
277
290
  | `auth.getCurrentUser()` | `GET /auth/me` | Get current authenticated user |
278
291
  | `auth.getInvitation({ path })` | `GET /auth/invitation/{token}` | Returns the invitation's details if it is still acceptable (for the accept page). |
279
292
  | `auth.login({ body })` | `POST /auth/login` | Authenticate user and create session |
280
293
  | `auth.logout()` | `POST /auth/logout` | Clear session |
281
- | `auth.register({ body })` | `POST /auth/register` | Registration is invite-only — always rejected. |
282
294
  | `auth.ssoCallback({ query? })` | `GET /auth/sso/callback` | Validate the provider response, resolve or provision the local account, and establish the normal Rustrak session. |
283
295
  | `auth.ssoConfig()` | `GET /auth/sso/config` | Public, non-sensitive information used to render the login page. |
284
296
  | `auth.ssoLink()` | `GET /auth/sso/link` | The account an SSO login matched, shown while its owner confirms. |
285
- | `auth.ssoStart()` | `POST /auth/sso/start` | Create a one-time OIDC authorization request and retain its protections in the encrypted session cookie. |
297
+ | `auth.ssoStart()` | `POST /auth/sso/start` | Create a one-time OIDC authorization request and retain its protections in the session. |
286
298
  | `auth.updateCurrentUser({ body })` | `PATCH /auth/me` | Update the current user's presentation preferences |
287
299
 
288
300
  ### Events
@@ -301,20 +313,20 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
301
313
  |---|---|---|
302
314
  | `github.callback({ query })` | `GET /api/github/callback` | GitHub sends the browser here after creating the app; the code trades for its credentials. |
303
315
  | `github.createIssue({ path, body })` | `POST /api/projects/{project_id}/issues/{issue_id}/github/issues` | Opens a GitHub issue and links it. |
304
- | `github.deleteProjectRepository({ path })` | `DELETE /api/projects/{project_id}/github` | |
305
- | `github.disconnect()` | `DELETE /api/github` | |
316
+ | `github.deleteProjectRepository({ path })` | `DELETE /api/projects/{project_id}/github` | Unlinks a project from its GitHub repository. |
317
+ | `github.disconnect()` | `DELETE /api/github` | Disconnects the GitHub App from this instance. |
306
318
  | `github.getMyLogin()` | `GET /api/github/me` | The GitHub username assignees sync through. |
307
- | `github.getProjectRepository({ path })` | `GET /api/projects/{project_id}/github` | |
319
+ | `github.getProjectRepository({ path })` | `GET /api/projects/{project_id}/github` | Gets the GitHub repository a project is linked to. |
308
320
  | `github.installed({ query })` | `GET /api/github/installed` | GitHub sends the browser here after the app is installed on an account. |
309
321
  | `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. |
310
322
  | `github.issueDraft({ path })` | `GET /api/projects/{project_id}/issues/{issue_id}/github/draft` | What creating a GitHub issue sends by default, with the templates applied, for the person to edit first. |
311
323
  | `github.linkIssue({ path, body })` | `POST /api/projects/{project_id}/issues/{issue_id}/github/links` | Links an existing GitHub issue or pull request. |
312
324
  | `github.manifest({ body })` | `POST /api/github/manifest` | What the dashboard posts to GitHub so GitHub creates an app already configured for this server. |
313
325
  | `github.putIssueTemplate({ body })` | `PUT /api/github/issue-template` | The labels, title prefix and body footer every GitHub issue starts with, before a project's own. |
314
- | `github.putMyLogin({ body })` | `PUT /api/github/me` | |
315
- | `github.putProjectRepository({ path, body })` | `PUT /api/projects/{project_id}/github` | |
326
+ | `github.putMyLogin({ body })` | `PUT /api/github/me` | Sets or clears the caller's GitHub username, which assignees sync through. |
327
+ | `github.putProjectRepository({ path, body })` | `PUT /api/projects/{project_id}/github` | Links a project to a GitHub repository. |
316
328
  | `github.repositories({ query })` | `GET /api/github/repositories` | The repositories a project can point at, for whoever may change that project's settings. |
317
- | `github.status()` | `GET /api/github` | |
329
+ | `github.status()` | `GET /api/github` | Shows whether a GitHub App is connected, and how. |
318
330
  | `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. |
319
331
  | `github.unlinkIssue({ path })` | `DELETE /api/projects/{project_id}/issues/{issue_id}/github/links/{link_id}` | |
320
332
  | `github.webhook({ body })` | `POST /api/github/webhook` | Public, and trusted only through the signature GitHub computes with the app's webhook secret. |
@@ -331,9 +343,11 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
331
343
 
332
344
  | Method | Endpoint | Description |
333
345
  |---|---|---|
334
- | `invitations.create({ body })` | `POST /api/invitations` | Create an invitation (admin only) |
335
- | `invitations.list()` | `GET /api/invitations` | List invitations (admin only) |
336
- | `invitations.revoke({ path })` | `DELETE /api/invitations/{token}` | Revoke a pending invitation (admin only) |
346
+ | `invitations.create({ body })` | `POST /api/invitations` | Create an invitation (needs `invitations:create`) |
347
+ | `invitations.emailStatus()` | `GET /api/invitations/email-status` | Whether invitation emails can be sent (needs `invitations:read`) |
348
+ | `invitations.list()` | `GET /api/invitations` | List invitations (needs `invitations:read`) |
349
+ | `invitations.resend({ path })` | `POST /api/invitations/{id}/resend` | Email the invite link again (needs `invitations:create`) |
350
+ | `invitations.revoke({ path })` | `DELETE /api/invitations/{id}` | Revoke a pending invitation (needs `invitations:delete`) |
337
351
 
338
352
  ### Issues
339
353
 
@@ -341,8 +355,8 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
341
355
  |---|---|---|
342
356
  | `environments.list({ path })` | `GET /api/projects/{project_id}/environments` | Lists the environments a project's issues have been seen in, by name. |
343
357
  | `feedback.list({ path, query? })` | `GET /api/projects/{project_id}/feedback` | Lists user feedback with what each user submitted. |
344
- | `issues.bulkDelete({ path, body })` | `DELETE /api/projects/{project_id}/issues` | Bulk delete by ids or filter |
345
- | `issues.bulkUpdate({ path, body })` | `PUT /api/projects/{project_id}/issues` | Bulk mutate |
358
+ | `issues.bulkDelete({ path, body })` | `DELETE /api/projects/{project_id}/issues` | Deletes many issues at once, selected by id or by filter. |
359
+ | `issues.bulkUpdate({ path, body })` | `PUT /api/projects/{project_id}/issues` | Sets the status or priority of many issues at once, by id. |
346
360
  | `issues.createComment({ path, body })` | `POST /api/projects/{project_id}/issues/{issue_id}/comments` | Add a note. |
347
361
  | `issues.createUserReport({ path, body })` | `POST /api/projects/{project_id}/issues/{issue_id}/user-reports` | Attach user feedback to an issue. |
348
362
  | `issues.delete({ path })` | `DELETE /api/projects/{project_id}/issues/{issue_id}` | Hard-deletes an issue and all associated events |
@@ -371,21 +385,22 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
371
385
 
372
386
  | Method | Endpoint | Description |
373
387
  |---|---|---|
374
- | `members.list({ path })` | `GET /api/projects/{project_id}/members` | |
375
- | `members.remove({ path })` | `DELETE /api/projects/{project_id}/members/{user_id}` | |
376
- | `members.upsert({ path, body })` | `PUT /api/projects/{project_id}/members` | |
388
+ | `members.candidates({ path })` | `GET /api/projects/{project_id}/members/candidates` | Lists the people and the roles the caller can give on the project, so it takes neither `users:read` nor `roles:read` to manage its members. |
389
+ | `members.list({ path })` | `GET /api/projects/{project_id}/members` | Lists everyone holding a role on the project, with those roles; personal permissions are not roles. |
390
+ | `members.remove({ path })` | `DELETE /api/projects/{project_id}/members/{user_id}` | Removes the roles a user holds on this project; roles on all projects and personal permissions stay. |
391
+ | `members.upsert({ path, body })` | `PUT /api/projects/{project_id}/members` | Sets the roles a user holds on the project. |
377
392
 
378
393
  ### Monitors
379
394
 
380
395
  | Method | Endpoint | Description |
381
396
  |---|---|---|
382
- | `monitors.create({ path, body })` | `POST /api/projects/{project_id}/monitors` | |
383
- | `monitors.delete({ path })` | `DELETE /api/projects/{project_id}/monitors/{slug}` | |
384
- | `monitors.get({ path })` | `GET /api/projects/{project_id}/monitors/{slug}` | |
385
- | `monitors.list({ path })` | `GET /api/projects/{project_id}/monitors` | |
386
- | `monitors.listCheckins({ path, query? })` | `GET /api/projects/{project_id}/monitors/{slug}/checkins` | |
397
+ | `monitors.create({ path, body })` | `POST /api/projects/{project_id}/monitors` | Creates a cron monitor in a project. |
398
+ | `monitors.delete({ path })` | `DELETE /api/projects/{project_id}/monitors/{slug}` | Deletes a cron monitor and its check-ins. |
399
+ | `monitors.get({ path })` | `GET /api/projects/{project_id}/monitors/{slug}` | Gets one cron monitor. |
400
+ | `monitors.list({ path })` | `GET /api/projects/{project_id}/monitors` | Lists a project's cron monitors. |
401
+ | `monitors.listCheckins({ path, query? })` | `GET /api/projects/{project_id}/monitors/{slug}/checkins` | Lists a cron monitor's check-ins, newest first. |
387
402
  | `monitors.stats({ path, query? })` | `GET /api/projects/{project_id}/monitor-stats` | Counts the check-ins of a project's monitors by status over a window, bucket by bucket. |
388
- | `monitors.update({ path, body })` | `PUT /api/projects/{project_id}/monitors/{slug}` | |
403
+ | `monitors.update({ path, body })` | `PUT /api/projects/{project_id}/monitors/{slug}` | Updates a cron monitor. |
389
404
 
390
405
  ### OAuth
391
406
 
@@ -404,7 +419,7 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
404
419
  | `projects.delete({ path })` | `DELETE /api/projects/{id}` | Delete a project |
405
420
  | `projects.get({ path })` | `GET /api/projects/{id}` | Get a project by ID |
406
421
  | `projects.getRateLimits()` | `GET /api/rate-limits` | What a project without limits of its own follows |
407
- | `projects.list({ query? })` | `GET /api/projects` | List projects with pagination |
422
+ | `projects.list({ query? })` | `GET /api/projects` | List projects with pagination. |
408
423
  | `projects.update({ path, body })` | `PATCH /api/projects/{id}` | Update a project |
409
424
 
410
425
  ### Releases
@@ -415,7 +430,7 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
415
430
  | `releases.finalize({ path, body })` | `PUT /api/0/projects/{org_slug}/{project_slug}/releases/{version}/` | Generic partial update (`ref`, `url`, `dateReleased`). |
416
431
  | `releases.get({ path, query? })` | `GET /api/projects/{project_id}/releases/{release}` | Gets one release with its events, new issues, environments and session health. |
417
432
  | `releases.list({ path, query? })` | `GET /api/projects/{project_id}/releases` | Lists a project's releases, newest first, with the events, new issues, environments and session health of each. |
418
- | `releases.newIssues({ path, query? })` | `GET /api/projects/{project_id}/releases/{release}/new-issues` | |
433
+ | `releases.newIssues({ path, query? })` | `GET /api/projects/{project_id}/releases/{release}/new-issues` | Lists the issues first seen in a release. |
419
434
 
420
435
  ### Schedules
421
436
 
@@ -429,7 +444,7 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
429
444
  |---|---|---|
430
445
  | `sentry.getIssue({ path })` | `GET /api/0/issues/{issue_id}/` | One issue with its releases and its 24h and 30d series (Sentry's `GroupDetailsEndpoint`). |
431
446
  | `sentry.getLatestEvent({ path })` | `GET /api/0/issues/{issue_id}/events/latest/` | The issue's most recent event (Sentry's `GroupEventDetailsEndpoint`, `issues/endpoints/group_event_details.py`). |
432
- | `sentry.listIssues({ path, query? })` | `GET /api/0/projects/{org_slug}/{project_slug}/issues/` | A project's issues, filtered by `query`, sorted by `sort` and paginated by `cursor` (Sentry's `ProjectGroupIndexEndpoint`, `issues/endpoints/project_group_index.py`). |
447
+ | `sentry.listIssues({ path, query? })` | `GET /api/0/projects/{org_slug}/{project_slug}/issues/` | Issues, filtered by `query`, sorted by `sort` and paginated by `cursor` (Sentry's `ProjectGroupIndexEndpoint`, `issues/endpoints/project_group_index.py`). |
433
448
  | `sentry.listOrganizations()` | `GET /api/0/organizations/` | The organizations the token belongs to (Sentry's `OrganizationIndexEndpoint`): always the one synthetic org. |
434
449
  | `sentry.listProjects({ query? })` | `GET /api/0/projects/` | The projects the token can see, newest first (Sentry's `ProjectIndexEndpoint`, `core/endpoints/project_index.py`). |
435
450
  | `sentry.listRepos({ path })` | `GET /api/0/organizations/{org_slug}/repos/` | The code repositories connected to the organization (Sentry's `OrganizationRepositoriesEndpoint`). |
@@ -441,16 +456,16 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
441
456
 
442
457
  | Method | Endpoint | Description |
443
458
  |---|---|---|
444
- | `sessions.getStats({ path, query? })` | `GET /api/projects/{project_id}/sessions/stats` | |
445
- | `sessions.getSummary({ path, query? })` | `GET /api/projects/{project_id}/sessions/summary` | |
446
- | `sessions.getTimeseries({ path, query? })` | `GET /api/projects/{project_id}/sessions/timeseries` | |
459
+ | `sessions.getStats({ path, query? })` | `GET /api/projects/{project_id}/sessions/stats` | Session counts by release, for release health. |
460
+ | `sessions.getSummary({ path, query? })` | `GET /api/projects/{project_id}/sessions/summary` | Totals and crash-free rates for a project's sessions. |
461
+ | `sessions.getTimeseries({ path, query? })` | `GET /api/projects/{project_id}/sessions/timeseries` | Session counts over time, bucketed for a chart. |
447
462
 
448
463
  ### Settings
449
464
 
450
465
  | Method | Endpoint | Description |
451
466
  |---|---|---|
452
467
  | `settings.getMcp()` | `GET /api/settings/mcp` | Whether the MCP endpoint at `/mcp` is on. |
453
- | `settings.updateMcp({ body })` | `PUT /api/settings/mcp` | Turns the MCP endpoint at `/mcp` on or off. |
468
+ | `settings.updateMcp({ body })` | `PUT /api/settings/mcp` | Turns the MCP endpoint at `/mcp` on or off (needs `mcp:update`). |
454
469
 
455
470
  ### Source Maps
456
471
 
@@ -458,10 +473,10 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
458
473
  |---|---|---|
459
474
  | `debugFiles.assemble({ path, body })` | `POST /api/0/projects/{org_slug}/{project_slug}/files/difs/assemble/` | Assemble uploaded chunks into debug information files (ProGuard mappings, JVM source bundles). |
460
475
  | `debugFiles.upload({ path, body })` | `POST /api/0/projects/{org_slug}/{project_slug}/files/dsyms/` | Upload a zip of debug information files (the path `sentry-cli upload-proguard` takes). |
461
- | `sourceMaps.artifactBundleAssemble({ path, body })` | `POST /api/0/organizations/{org_slug}/artifactbundle/assemble/` | |
462
- | `sourceMaps.chunkUpload({ path, body })` | `POST /api/0/organizations/{org_slug}/chunk-upload/` | |
463
- | `sourceMaps.chunkUploadCapability({ path })` | `GET /api/0/organizations/{org_slug}/chunk-upload/` | |
464
- | `sourceMaps.list({ path })` | `GET /api/0/projects/{org_slug}/{project_slug}/files/source-maps/` | |
476
+ | `sourceMaps.artifactBundleAssemble({ path, body })` | `POST /api/0/organizations/{org_slug}/artifactbundle/assemble/` | Assembles uploaded chunks into an artifact bundle (sentry-cli `sourcemaps upload`). |
477
+ | `sourceMaps.chunkUpload({ path, body })` | `POST /api/0/organizations/{org_slug}/chunk-upload/` | Receives chunks of a source map or debug file upload from sentry-cli. |
478
+ | `sourceMaps.chunkUploadCapability({ path })` | `GET /api/0/organizations/{org_slug}/chunk-upload/` | Describes how sentry-cli should upload chunks: size, concurrency and accepted kinds. |
479
+ | `sourceMaps.list({ path })` | `GET /api/0/projects/{org_slug}/{project_slug}/files/source-maps/` | Lists a project's uploaded source map artifacts (sentry-cli). |
465
480
  | `sourceMaps.orgDetails({ path })` | `GET /api/0/organizations/{org_slug}/` | Always returns 200 with synthetic org data — sentry-cli validates this before uploading. |
466
481
 
467
482
  ### Spans
@@ -475,8 +490,8 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
475
490
 
476
491
  | Method | Endpoint | Description |
477
492
  |---|---|---|
478
- | `stats.getEventTimeseries({ path, query? })` | `GET /api/projects/{project_id}/events/stats` | |
479
- | `stats.getSummary({ path, query? })` | `GET /api/projects/{project_id}/stats/summary` | |
493
+ | `stats.getEventTimeseries({ path, query? })` | `GET /api/projects/{project_id}/events/stats` | Counts a project's events over time, bucketed for a chart. |
494
+ | `stats.getSummary({ path, query? })` | `GET /api/projects/{project_id}/stats/summary` | Headline figures for a project's overview: events, new and open issues. |
480
495
 
481
496
  ### Storage
482
497
 
@@ -489,35 +504,35 @@ Every method, grouped as it hangs off the client (`client.issues.list(...)`).
489
504
  | `storage.getCleanupStatus()` | `GET /api/storage/cleanup/status` | Progress of the running cleanup, or the outcome of the last one. |
490
505
  | `storage.getCompaction()` | `GET /api/storage/compaction` | Whether the database file shrinks when data is deleted, and the disk a one-off compaction needs. |
491
506
  | `storage.getProjects()` | `GET /api/storage/projects` | Per-project counts and estimated weight. |
492
- | `storage.getRetention()` | `GET /api/storage/retention` | |
507
+ | `storage.getRetention()` | `GET /api/storage/retention` | Gets the retention settings and the cleanup schedule. |
493
508
  | `storage.getSummary()` | `GET /api/storage/summary` | Row counts, DB size, and source-map weight. |
494
509
  | `storage.previewCleanup({ body })` | `POST /api/storage/cleanup/preview` | Dry-run count, mutates nothing. |
495
510
  | `storage.previewRetention({ body })` | `POST /api/storage/retention/preview` | What saving these settings would delete on the next cleanup, without saving or deleting anything. |
496
511
  | `storage.previewSourceMapGc()` | `POST /api/storage/source-maps/gc/preview` | Dry-run orphan count, deletes nothing. |
497
512
  | `storage.runRetention()` | `POST /api/storage/retention/run` | Enforces the saved retention now, without waiting for the schedule. |
498
- | `storage.updateRetention({ body })` | `PUT /api/storage/retention` | |
513
+ | `storage.updateRetention({ body })` | `PUT /api/storage/retention` | Updates the retention settings; the next cleanup is scheduled at once. |
499
514
 
500
515
  ### Team
501
516
 
502
517
  | Method | Endpoint | Description |
503
518
  |---|---|---|
504
- | `team.deleteUser({ path })` | `DELETE /api/team/{user_id}` | Remove a user from the instance (admin only) |
505
- | `team.list()` | `GET /api/team` | List all users (admin only) |
506
- | `team.updateRole({ path, body })` | `PATCH /api/team/{user_id}/role` | Change a user's global role (admin only) |
519
+ | `team.deleteUser({ path })` | `DELETE /api/team/{user_id}` | Remove a user from the instance (needs `users:delete`) |
520
+ | `team.list()` | `GET /api/team` | List all users (needs `users:read`) |
521
+ | `team.updateRole({ path, body })` | `PATCH /api/team/{user_id}/role` | Change a user's global role (needs `users:update`) |
507
522
 
508
523
  ### Telemetry
509
524
 
510
525
  | Method | Endpoint | Description |
511
526
  |---|---|---|
512
- | `telemetry.preview()` | `GET /api/telemetry/preview` | |
527
+ | `telemetry.preview()` | `GET /api/telemetry/preview` | Shows the exact anonymous telemetry report this instance would send. |
513
528
 
514
529
  ### Tokens
515
530
 
516
531
  | Method | Endpoint | Description |
517
532
  |---|---|---|
518
- | `tokens.create({ body })` | `POST /api/tokens` | Create a new token (scoped to the acting user) |
533
+ | `tokens.create({ body })` | `POST /api/tokens` | Create a new token (from a signed-in session only) |
519
534
  | `tokens.delete({ path })` | `DELETE /api/tokens/{id}` | Revoke a token |
520
- | `tokens.get({ path })` | `GET /api/tokens/{id}` | Reveal full token value |
535
+ | `tokens.get({ path })` | `GET /api/tokens/{id}` | Token details, masked |
521
536
  | `tokens.list()` | `GET /api/tokens` | List tokens (masked) |
522
537
 
523
538
  ### Transactions
package/dist/client.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ const require_binary_gen = require("./generated/binary.gen.cjs");
1
2
  const require_utils_gen = require("./generated/client/utils.gen.cjs");
2
3
  const require_client_gen = require("./generated/client/client.gen.cjs");
3
4
  const require_sdk_gen = require("./generated/sdk.gen.cjs");
@@ -5,7 +6,7 @@ const require_result = require("./result.cjs");
5
6
  const require_http = require("./utils/http.cjs");
6
7
  let zod = require("zod");
7
8
  // src/client.ts
8
- const BINARY = /* @__PURE__ */ new Set(["events.downloadAttachment"]);
9
+ const BINARY_OPERATIONS = new Set(require_binary_gen.BINARY);
9
10
  /**
10
11
  * Main Rustrak API client. Every method returns a `Result`; nothing throws for
11
12
  * an expected failure.
@@ -48,7 +49,7 @@ function createApi(config) {
48
49
  methods[method] = async (options = {}) => toResult(await resource[method]({
49
50
  ...options,
50
51
  headers: options.headers instanceof Headers || Array.isArray(options.headers) ? Object.fromEntries(new Headers(options.headers)) : options.headers,
51
- ...BINARY.has(`${name}.${method}`) && { parseAs: "blob" },
52
+ ...BINARY_OPERATIONS.has(`${name}.${method}`) && { parseAs: "blob" },
52
53
  throwOnError: false
53
54
  }));
54
55
  }
@@ -66,7 +67,7 @@ function createApi(config) {
66
67
  */
67
68
  function toResult({ data, error, response }) {
68
69
  if (error === void 0) return require_result.Ok(data);
69
- if (require_http.isTransportFailure(error)) return require_result.Err(error.rustrakError);
70
+ if (error instanceof require_http.RustrakTransportFailure) return require_result.Err(error.rustrakError);
70
71
  if (response && !response.ok) return require_result.Err(require_http.transformHttpError(response, error));
71
72
  if (!response && error instanceof zod.ZodError) return require_result.Err({
72
73
  kind: "invalid_request",
@@ -1 +1 @@
1
- {"version":3,"file":"client.cjs","names":["Rustrak","createClient","createConfig","createFetch","Ok","isTransportFailure","Err","transformHttpError","ZodError"],"sources":["../src/client.ts"],"sourcesContent":["import { ZodError } from 'zod';\nimport type { ClientConfig } from './config.js';\nimport type { RustrakError } from './errors.js';\nimport { createClient, createConfig } from './generated/client/index.js';\nimport { Rustrak } from './generated/sdk.gen.js';\nimport { Err, Ok, type Result } from './result.js';\nimport {\n createFetch,\n isTransportFailure,\n transformHttpError,\n} from './utils/http.js';\n\n/**\n * Operations whose 2xx is a file, not JSON: read as a `Blob`. The spec leaves\n * the body untyped (zod has no binary type), so the type comes from here.\n */\ntype Binary = 'events.downloadAttachment';\nconst BINARY: ReadonlySet<string> = new Set<Binary>([\n 'events.downloadAttachment',\n]);\n\n/** What a caller may pass: the request's own parts, never client plumbing. */\ntype RequestKeys = 'path' | 'query' | 'body' | 'headers' | 'signal';\n\ntype Call<F, Bytes extends boolean> = F extends (options: infer O) => infer R\n ? (\n ...args: {} extends Pick<O, Extract<keyof O, RequestKeys>>\n ? [options?: Pick<O, Extract<keyof O, RequestKeys>>]\n : [options: Pick<O, Extract<keyof O, RequestKeys>>]\n ) => Promise<\n Result<Bytes extends true ? Blob : Data<Awaited<R>>, RustrakError>\n >\n : never;\n\n/** The success half of the generated `{ data, error }` union. */\ntype Data<R> =\n Extract<R, { error: undefined }> extends { data: infer D } ? D : never;\n\ntype Resource<T, Name extends string> = {\n readonly [M in keyof T]: Call<\n T[M],\n `${Name}.${M & string}` extends Binary ? true : false\n >;\n};\n\n/**\n * Every operation in `openapi.json`, grouped by tag:\n * `client.issues.list({ path: { project_id } })`.\n *\n * Generated in `src/generated/`; this file only changes what a call returns.\n */\nexport type RustrakClient = {\n readonly [R in keyof Rustrak]: Resource<Rustrak[R], R & string>;\n};\n\n/**\n * Main Rustrak API client. Every method returns a `Result`; nothing throws for\n * an expected failure.\n *\n * @example\n * ```typescript\n * const client = new RustrakClient({\n * baseUrl: 'http://localhost:8080',\n * token: 'your-api-token',\n * });\n *\n * const issues = await client.issues.list({ path: { project_id: 1 } });\n * if (issues.success) console.log(issues.data.items);\n * ```\n */\nexport const RustrakClient = class {\n constructor(config: ClientConfig) {\n // biome-ignore lint/correctness/noConstructorReturn: the instance is the generated SDK with every method wrapped\n return Object.setPrototypeOf(createApi(config), new.target.prototype);\n }\n} as unknown as new (\n config: ClientConfig,\n) => RustrakClient;\n\nfunction createApi(config: ClientConfig): RustrakClient {\n const sdk = new Rustrak({\n client: createClient(\n createConfig({\n baseUrl: config.baseUrl,\n credentials: 'include',\n fetch: createFetch(config),\n // Every 2xx in the spec is JSON or empty, except the files in\n // `BINARY`. `auto` would hand back an HTML page or a stream as a\n // success without validating it.\n parseAs: 'json',\n headers: {\n // Without it a content-negotiating hop can answer with HTML.\n Accept: 'application/json',\n ...config.headers,\n ...(config.token && { Authorization: `Bearer ${config.token}` }),\n },\n }),\n ),\n });\n\n const api: Record<string, Record<string, unknown>> = {};\n for (const name of Object.getOwnPropertyNames(Rustrak.prototype)) {\n if (name === 'constructor') continue;\n const resource = sdk[name as keyof Rustrak] as unknown as Record<\n string,\n (options: object) => Promise<Outcome>\n >;\n const methods: Record<string, unknown> = {};\n for (const method of Object.getOwnPropertyNames(\n Object.getPrototypeOf(resource),\n )) {\n if (method === 'constructor') continue;\n methods[method] = async (options: { headers?: unknown } = {}) =>\n toResult(\n await resource[method]!({\n ...options,\n // A JSON write spreads these into an object, which keeps nothing\n // of a `Headers` or an array of pairs.\n headers:\n options.headers instanceof Headers ||\n Array.isArray(options.headers)\n ? Object.fromEntries(new Headers(options.headers))\n : options.headers,\n ...(BINARY.has(`${name}.${method}`) && { parseAs: 'blob' }),\n throwOnError: false,\n }),\n );\n }\n api[name] = methods;\n }\n return api as unknown as RustrakClient;\n}\n\ntype Outcome = {\n data?: unknown;\n error?: unknown;\n response?: Response;\n};\n\n/**\n * The boundary: the generated client's `{ data, error, response }` becomes a\n * `Result`.\n *\n * Only failures this file recognises become an `Err`. Anything else, a\n * `TypeError` from a genuine bug, is rethrown so it crashes where it can be\n * debugged instead of being laundered into `{ success: false }`.\n */\nfunction toResult({\n data,\n error,\n response,\n}: Outcome): Result<unknown, RustrakError> {\n if (error === undefined) return Ok(data);\n if (isTransportFailure(error)) return Err(error.rustrakError);\n if (response && !response.ok) {\n // `error` is the parsed body, or its text when it is not JSON.\n return Err(transformHttpError(response, error));\n }\n if (!response && error instanceof ZodError) {\n return Err({\n kind: 'invalid_request',\n message: 'The request input did not match the expected schema.',\n });\n }\n if (response && error instanceof SyntaxError) {\n return Err({\n kind: 'invalid_response',\n message: 'The server returned a body that is not valid JSON.',\n });\n }\n if (response && error instanceof ZodError) {\n // No Zod issues: they embed the offending response data.\n return Err({\n kind: 'invalid_response',\n message: 'The server response did not match the expected schema.',\n });\n }\n throw error;\n}\n"],"mappings":";;;;;;;AAiBA,MAAM,yBAA8B,IAAI,IAAY,CAClD,2BACF,CAAC;;;;;;;;;;;;;;;;AAmDD,MAAa,gBAAgB,MAAM;CACjC,YAAY,QAAsB;EAEhC,OAAO,OAAO,eAAe,UAAU,MAAM,GAAG,WAAW,SAAS;CACtE;AACF;AAIA,SAAS,UAAU,QAAqC;CACtD,MAAM,MAAM,IAAIA,gBAAAA,QAAQ,EACtB,QAAQC,mBAAAA,aACNC,kBAAAA,aAAa;EACX,SAAS,OAAO;EAChB,aAAa;EACb,OAAOC,aAAAA,YAAY,MAAM;EAIzB,SAAS;EACT,SAAS;GAEP,QAAQ;GACR,GAAG,OAAO;GACV,GAAI,OAAO,SAAS,EAAE,eAAe,UAAU,OAAO,QAAQ;EAChE;CACF,CAAC,CACH,EACF,CAAC;CAED,MAAM,MAA+C,CAAC;CACtD,KAAK,MAAM,QAAQ,OAAO,oBAAoBH,gBAAAA,QAAQ,SAAS,GAAG;EAChE,IAAI,SAAS,eAAe;EAC5B,MAAM,WAAW,IAAI;EAIrB,MAAM,UAAmC,CAAC;EAC1C,KAAK,MAAM,UAAU,OAAO,oBAC1B,OAAO,eAAe,QAAQ,CAChC,GAAG;GACD,IAAI,WAAW,eAAe;GAC9B,QAAQ,UAAU,OAAO,UAAiC,CAAC,MACzD,SACE,MAAM,SAAS,OAAO,CAAE;IACtB,GAAG;IAGH,SACE,QAAQ,mBAAmB,WAC3B,MAAM,QAAQ,QAAQ,OAAO,IACzB,OAAO,YAAY,IAAI,QAAQ,QAAQ,OAAO,CAAC,IAC/C,QAAQ;IACd,GAAI,OAAO,IAAI,GAAG,KAAK,GAAG,QAAQ,KAAK,EAAE,SAAS,OAAO;IACzD,cAAc;GAChB,CAAC,CACH;EACJ;EACA,IAAI,QAAQ;CACd;CACA,OAAO;AACT;;;;;;;;;AAgBA,SAAS,SAAS,EAChB,MACA,OACA,YACyC;CACzC,IAAI,UAAU,KAAA,GAAW,OAAOI,eAAAA,GAAG,IAAI;CACvC,IAAIC,aAAAA,mBAAmB,KAAK,GAAG,OAAOC,eAAAA,IAAI,MAAM,YAAY;CAC5D,IAAI,YAAY,CAAC,SAAS,IAExB,OAAOA,eAAAA,IAAIC,aAAAA,mBAAmB,UAAU,KAAK,CAAC;CAEhD,IAAI,CAAC,YAAY,iBAAiBC,IAAAA,UAChC,OAAOF,eAAAA,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,IAAI,YAAY,iBAAiB,aAC/B,OAAOA,eAAAA,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,IAAI,YAAY,iBAAiBE,IAAAA,UAE/B,OAAOF,eAAAA,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,MAAM;AACR"}
1
+ {"version":3,"file":"client.cjs","names":["BINARY","Rustrak","createClient","createConfig","createFetch","Ok","RustrakTransportFailure","Err","transformHttpError","ZodError"],"sources":["../src/client.ts"],"sourcesContent":["import { ZodError } from 'zod';\nimport type { ClientConfig } from './config.js';\nimport type { RustrakError } from './errors.js';\nimport { BINARY } from './generated/binary.gen.js';\nimport { createClient, createConfig } from './generated/client/index.js';\nimport { Rustrak } from './generated/sdk.gen.js';\nimport { Err, Ok, type Result } from './result.js';\nimport {\n createFetch,\n RustrakTransportFailure,\n transformHttpError,\n} from './utils/http.js';\n\n/**\n * Operations whose 2xx is a file, read as a `Blob`. The spec leaves the body\n * untyped (zod has no binary type), so the type comes from this list.\n */\ntype Binary = (typeof BINARY)[number];\nconst BINARY_OPERATIONS: ReadonlySet<string> = new Set(BINARY);\n\n/** What a caller may pass: the request's own parts, never client plumbing. */\ntype RequestKeys = 'path' | 'query' | 'body' | 'headers' | 'signal';\n\ntype Call<F, Bytes extends boolean> = F extends (options: infer O) => infer R\n ? (\n ...args: {} extends Pick<O, Extract<keyof O, RequestKeys>>\n ? [options?: Pick<O, Extract<keyof O, RequestKeys>>]\n : [options: Pick<O, Extract<keyof O, RequestKeys>>]\n ) => Promise<\n Result<Bytes extends true ? Blob : Data<Awaited<R>>, RustrakError>\n >\n : never;\n\n/** The success half of the generated `{ data, error }` union. */\ntype Data<R> =\n Extract<R, { error: undefined }> extends { data: infer D } ? D : never;\n\ntype Resource<T, Name extends string> = {\n readonly [M in keyof T]: Call<\n T[M],\n `${Name}.${M & string}` extends Binary ? true : false\n >;\n};\n\n/**\n * Every operation in `openapi.json`, grouped by tag:\n * `client.issues.list({ path: { project_id } })`.\n *\n * Generated in `src/generated/`; this file only changes what a call returns.\n */\nexport type RustrakClient = {\n readonly [R in keyof Rustrak]: Resource<Rustrak[R], R & string>;\n};\n\n/**\n * Main Rustrak API client. Every method returns a `Result`; nothing throws for\n * an expected failure.\n *\n * @example\n * ```typescript\n * const client = new RustrakClient({\n * baseUrl: 'http://localhost:8080',\n * token: 'your-api-token',\n * });\n *\n * const issues = await client.issues.list({ path: { project_id: 1 } });\n * if (issues.success) console.log(issues.data.items);\n * ```\n */\nexport const RustrakClient = class {\n constructor(config: ClientConfig) {\n // biome-ignore lint/correctness/noConstructorReturn: the instance is the generated SDK with every method wrapped\n return Object.setPrototypeOf(createApi(config), new.target.prototype);\n }\n} as unknown as new (\n config: ClientConfig,\n) => RustrakClient;\n\nfunction createApi(config: ClientConfig): RustrakClient {\n const sdk = new Rustrak({\n client: createClient(\n createConfig({\n baseUrl: config.baseUrl,\n credentials: 'include',\n fetch: createFetch(config),\n // Every 2xx in the spec is JSON or empty, except the files in\n // `BINARY`. `auto` would hand back an HTML page or a stream as a\n // success without validating it.\n parseAs: 'json',\n headers: {\n // Without it a content-negotiating hop can answer with HTML.\n Accept: 'application/json',\n ...config.headers,\n ...(config.token && { Authorization: `Bearer ${config.token}` }),\n },\n }),\n ),\n });\n\n const api: Record<string, Record<string, unknown>> = {};\n for (const name of Object.getOwnPropertyNames(Rustrak.prototype)) {\n if (name === 'constructor') continue;\n const resource = sdk[name as keyof Rustrak] as unknown as Record<\n string,\n (options: object) => Promise<Outcome>\n >;\n const methods: Record<string, unknown> = {};\n for (const method of Object.getOwnPropertyNames(\n Object.getPrototypeOf(resource),\n )) {\n if (method === 'constructor') continue;\n methods[method] = async (options: { headers?: unknown } = {}) =>\n toResult(\n await resource[method]!({\n ...options,\n // A JSON write spreads these into an object, which keeps nothing\n // of a `Headers` or an array of pairs.\n headers:\n options.headers instanceof Headers ||\n Array.isArray(options.headers)\n ? Object.fromEntries(new Headers(options.headers))\n : options.headers,\n ...(BINARY_OPERATIONS.has(`${name}.${method}`) && {\n parseAs: 'blob',\n }),\n throwOnError: false,\n }),\n );\n }\n api[name] = methods;\n }\n return api as unknown as RustrakClient;\n}\n\ntype Outcome = {\n data?: unknown;\n error?: unknown;\n response?: Response;\n};\n\n/**\n * The boundary: the generated client's `{ data, error, response }` becomes a\n * `Result`.\n *\n * Only failures this file recognises become an `Err`. Anything else, a\n * `TypeError` from a genuine bug, is rethrown so it crashes where it can be\n * debugged instead of being laundered into `{ success: false }`.\n */\nfunction toResult({\n data,\n error,\n response,\n}: Outcome): Result<unknown, RustrakError> {\n if (error === undefined) return Ok(data);\n if (error instanceof RustrakTransportFailure) return Err(error.rustrakError);\n if (response && !response.ok) {\n // `error` is the parsed body, or its text when it is not JSON.\n return Err(transformHttpError(response, error));\n }\n if (!response && error instanceof ZodError) {\n return Err({\n kind: 'invalid_request',\n message: 'The request input did not match the expected schema.',\n });\n }\n if (response && error instanceof SyntaxError) {\n return Err({\n kind: 'invalid_response',\n message: 'The server returned a body that is not valid JSON.',\n });\n }\n if (response && error instanceof ZodError) {\n // No Zod issues: they embed the offending response data.\n return Err({\n kind: 'invalid_response',\n message: 'The server response did not match the expected schema.',\n });\n }\n throw error;\n}\n"],"mappings":";;;;;;;;AAkBA,MAAM,oBAAyC,IAAI,IAAIA,mBAAAA,MAAM;;;;;;;;;;;;;;;;AAmD7D,MAAa,gBAAgB,MAAM;CACjC,YAAY,QAAsB;EAEhC,OAAO,OAAO,eAAe,UAAU,MAAM,GAAG,WAAW,SAAS;CACtE;AACF;AAIA,SAAS,UAAU,QAAqC;CACtD,MAAM,MAAM,IAAIC,gBAAAA,QAAQ,EACtB,QAAQC,mBAAAA,aACNC,kBAAAA,aAAa;EACX,SAAS,OAAO;EAChB,aAAa;EACb,OAAOC,aAAAA,YAAY,MAAM;EAIzB,SAAS;EACT,SAAS;GAEP,QAAQ;GACR,GAAG,OAAO;GACV,GAAI,OAAO,SAAS,EAAE,eAAe,UAAU,OAAO,QAAQ;EAChE;CACF,CAAC,CACH,EACF,CAAC;CAED,MAAM,MAA+C,CAAC;CACtD,KAAK,MAAM,QAAQ,OAAO,oBAAoBH,gBAAAA,QAAQ,SAAS,GAAG;EAChE,IAAI,SAAS,eAAe;EAC5B,MAAM,WAAW,IAAI;EAIrB,MAAM,UAAmC,CAAC;EAC1C,KAAK,MAAM,UAAU,OAAO,oBAC1B,OAAO,eAAe,QAAQ,CAChC,GAAG;GACD,IAAI,WAAW,eAAe;GAC9B,QAAQ,UAAU,OAAO,UAAiC,CAAC,MACzD,SACE,MAAM,SAAS,OAAO,CAAE;IACtB,GAAG;IAGH,SACE,QAAQ,mBAAmB,WAC3B,MAAM,QAAQ,QAAQ,OAAO,IACzB,OAAO,YAAY,IAAI,QAAQ,QAAQ,OAAO,CAAC,IAC/C,QAAQ;IACd,GAAI,kBAAkB,IAAI,GAAG,KAAK,GAAG,QAAQ,KAAK,EAChD,SAAS,OACX;IACA,cAAc;GAChB,CAAC,CACH;EACJ;EACA,IAAI,QAAQ;CACd;CACA,OAAO;AACT;;;;;;;;;AAgBA,SAAS,SAAS,EAChB,MACA,OACA,YACyC;CACzC,IAAI,UAAU,KAAA,GAAW,OAAOI,eAAAA,GAAG,IAAI;CACvC,IAAI,iBAAiBC,aAAAA,yBAAyB,OAAOC,eAAAA,IAAI,MAAM,YAAY;CAC3E,IAAI,YAAY,CAAC,SAAS,IAExB,OAAOA,eAAAA,IAAIC,aAAAA,mBAAmB,UAAU,KAAK,CAAC;CAEhD,IAAI,CAAC,YAAY,iBAAiBC,IAAAA,UAChC,OAAOF,eAAAA,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,IAAI,YAAY,iBAAiB,aAC/B,OAAOA,eAAAA,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,IAAI,YAAY,iBAAiBE,IAAAA,UAE/B,OAAOF,eAAAA,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,MAAM;AACR"}
package/dist/client.d.cts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { ClientConfig } from "./config.cjs";
2
2
  import { RustrakError } from "./errors.cjs";
3
+ import { BINARY } from "./generated/binary.gen.cjs";
3
4
  import { Rustrak } from "./generated/sdk.gen.cjs";
4
5
  import { Result } from "./result.cjs";
5
- type Binary = 'events.downloadAttachment';
6
+ type Binary = (typeof BINARY)[number];
6
7
  type RequestKeys = 'path' | 'query' | 'body' | 'headers' | 'signal';
7
8
  type Call<F, Bytes extends boolean> = F extends ((options: infer O) => infer R) ? (...args: {} extends Pick<O, Extract<keyof O, RequestKeys>> ? [options?: Pick<O, Extract<keyof O, RequestKeys>>] : [options: Pick<O, Extract<keyof O, RequestKeys>>]) => Promise<Result<Bytes extends true ? Blob : Data<Awaited<R>>, RustrakError>> : never;
8
9
  type Data<R> = Extract<R, {
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;;;KAgBK;KAMA;KAEA,KAAK,GAAG,yBAAyB,YAAW,eAAe,YAAY,SAEnE,iBAAiB,KAAK,GAAG,cAAc,GAAG,iBACxC,UAAU,KAAK,GAAG,cAAc,GAAG,kBACnC,SAAS,KAAK,GAAG,cAAc,GAAG,mBACpC,QACH,OAAO,qBAAqB,OAAO,KAAK,QAAQ,KAAK;KAKtD,KAAK,KACR,QAAQ;EAAK;;EAA8B,YAAY;IAAM;KAE1D,SAAS,GAAG,mCACL,WAAW,IAAI,KACvB,EAAE,OACC,QAAQ,qBAAqB;YAUxB,4BACA,WAAW,UAAU,SAAS,QAAQ,IAAI;qBAkBzC,oBAMX,QAAQ,iBACL"}
1
+ {"version":3,"file":"client.d.cts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;KAiBK,iBAAiB;KAIjB;KAEA,KAAK,GAAG,yBAAyB,YAAW,eAAe,YAAY,SAEnE,iBAAiB,KAAK,GAAG,cAAc,GAAG,iBACxC,UAAU,KAAK,GAAG,cAAc,GAAG,kBACnC,SAAS,KAAK,GAAG,cAAc,GAAG,mBACpC,QACH,OAAO,qBAAqB,OAAO,KAAK,QAAQ,KAAK;KAKtD,KAAK,KACR,QAAQ;EAAK;;EAA8B,YAAY;IAAM;KAE1D,SAAS,GAAG,mCACL,WAAW,IAAI,KACvB,EAAE,OACC,QAAQ,qBAAqB;YAUxB,4BACA,WAAW,UAAU,SAAS,QAAQ,IAAI;qBAkBzC,oBAMX,QAAQ,iBACL"}
package/dist/client.d.mts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { ClientConfig } from "./config.mjs";
2
2
  import { RustrakError } from "./errors.mjs";
3
+ import { BINARY } from "./generated/binary.gen.mjs";
3
4
  import { Rustrak } from "./generated/sdk.gen.mjs";
4
5
  import { Result } from "./result.mjs";
5
- type Binary = 'events.downloadAttachment';
6
+ type Binary = (typeof BINARY)[number];
6
7
  type RequestKeys = 'path' | 'query' | 'body' | 'headers' | 'signal';
7
8
  type Call<F, Bytes extends boolean> = F extends ((options: infer O) => infer R) ? (...args: {} extends Pick<O, Extract<keyof O, RequestKeys>> ? [options?: Pick<O, Extract<keyof O, RequestKeys>>] : [options: Pick<O, Extract<keyof O, RequestKeys>>]) => Promise<Result<Bytes extends true ? Blob : Data<Awaited<R>>, RustrakError>> : never;
8
9
  type Data<R> = Extract<R, {
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;;;KAgBK;KAMA;KAEA,KAAK,GAAG,yBAAyB,YAAW,eAAe,YAAY,SAEnE,iBAAiB,KAAK,GAAG,cAAc,GAAG,iBACxC,UAAU,KAAK,GAAG,cAAc,GAAG,kBACnC,SAAS,KAAK,GAAG,cAAc,GAAG,mBACpC,QACH,OAAO,qBAAqB,OAAO,KAAK,QAAQ,KAAK;KAKtD,KAAK,KACR,QAAQ;EAAK;;EAA8B,YAAY;IAAM;KAE1D,SAAS,GAAG,mCACL,WAAW,IAAI,KACvB,EAAE,OACC,QAAQ,qBAAqB;YAUxB,4BACA,WAAW,UAAU,SAAS,QAAQ,IAAI;qBAkBzC,oBAMX,QAAQ,iBACL"}
1
+ {"version":3,"file":"client.d.mts","names":[],"sources":["../src/client.ts"],"mappings":";;;;;KAiBK,iBAAiB;KAIjB;KAEA,KAAK,GAAG,yBAAyB,YAAW,eAAe,YAAY,SAEnE,iBAAiB,KAAK,GAAG,cAAc,GAAG,iBACxC,UAAU,KAAK,GAAG,cAAc,GAAG,kBACnC,SAAS,KAAK,GAAG,cAAc,GAAG,mBACpC,QACH,OAAO,qBAAqB,OAAO,KAAK,QAAQ,KAAK;KAKtD,KAAK,KACR,QAAQ;EAAK;;EAA8B,YAAY;IAAM;KAE1D,SAAS,GAAG,mCACL,WAAW,IAAI,KACvB,EAAE,OACC,QAAQ,qBAAqB;YAUxB,4BACA,WAAW,UAAU,SAAS,QAAQ,IAAI;qBAkBzC,oBAMX,QAAQ,iBACL"}
package/dist/client.mjs CHANGED
@@ -1,11 +1,12 @@
1
+ import { BINARY } from "./generated/binary.gen.mjs";
1
2
  import { createConfig } from "./generated/client/utils.gen.mjs";
2
3
  import { createClient } from "./generated/client/client.gen.mjs";
3
4
  import { Rustrak } from "./generated/sdk.gen.mjs";
4
5
  import { Err, Ok } from "./result.mjs";
5
- import { createFetch, isTransportFailure, transformHttpError } from "./utils/http.mjs";
6
+ import { RustrakTransportFailure, createFetch, transformHttpError } from "./utils/http.mjs";
6
7
  import { ZodError } from "zod";
7
8
  // src/client.ts
8
- const BINARY = /* @__PURE__ */ new Set(["events.downloadAttachment"]);
9
+ const BINARY_OPERATIONS = new Set(BINARY);
9
10
  /**
10
11
  * Main Rustrak API client. Every method returns a `Result`; nothing throws for
11
12
  * an expected failure.
@@ -48,7 +49,7 @@ function createApi(config) {
48
49
  methods[method] = async (options = {}) => toResult(await resource[method]({
49
50
  ...options,
50
51
  headers: options.headers instanceof Headers || Array.isArray(options.headers) ? Object.fromEntries(new Headers(options.headers)) : options.headers,
51
- ...BINARY.has(`${name}.${method}`) && { parseAs: "blob" },
52
+ ...BINARY_OPERATIONS.has(`${name}.${method}`) && { parseAs: "blob" },
52
53
  throwOnError: false
53
54
  }));
54
55
  }
@@ -66,7 +67,7 @@ function createApi(config) {
66
67
  */
67
68
  function toResult({ data, error, response }) {
68
69
  if (error === void 0) return Ok(data);
69
- if (isTransportFailure(error)) return Err(error.rustrakError);
70
+ if (error instanceof RustrakTransportFailure) return Err(error.rustrakError);
70
71
  if (response && !response.ok) return Err(transformHttpError(response, error));
71
72
  if (!response && error instanceof ZodError) return Err({
72
73
  kind: "invalid_request",
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import { ZodError } from 'zod';\nimport type { ClientConfig } from './config.js';\nimport type { RustrakError } from './errors.js';\nimport { createClient, createConfig } from './generated/client/index.js';\nimport { Rustrak } from './generated/sdk.gen.js';\nimport { Err, Ok, type Result } from './result.js';\nimport {\n createFetch,\n isTransportFailure,\n transformHttpError,\n} from './utils/http.js';\n\n/**\n * Operations whose 2xx is a file, not JSON: read as a `Blob`. The spec leaves\n * the body untyped (zod has no binary type), so the type comes from here.\n */\ntype Binary = 'events.downloadAttachment';\nconst BINARY: ReadonlySet<string> = new Set<Binary>([\n 'events.downloadAttachment',\n]);\n\n/** What a caller may pass: the request's own parts, never client plumbing. */\ntype RequestKeys = 'path' | 'query' | 'body' | 'headers' | 'signal';\n\ntype Call<F, Bytes extends boolean> = F extends (options: infer O) => infer R\n ? (\n ...args: {} extends Pick<O, Extract<keyof O, RequestKeys>>\n ? [options?: Pick<O, Extract<keyof O, RequestKeys>>]\n : [options: Pick<O, Extract<keyof O, RequestKeys>>]\n ) => Promise<\n Result<Bytes extends true ? Blob : Data<Awaited<R>>, RustrakError>\n >\n : never;\n\n/** The success half of the generated `{ data, error }` union. */\ntype Data<R> =\n Extract<R, { error: undefined }> extends { data: infer D } ? D : never;\n\ntype Resource<T, Name extends string> = {\n readonly [M in keyof T]: Call<\n T[M],\n `${Name}.${M & string}` extends Binary ? true : false\n >;\n};\n\n/**\n * Every operation in `openapi.json`, grouped by tag:\n * `client.issues.list({ path: { project_id } })`.\n *\n * Generated in `src/generated/`; this file only changes what a call returns.\n */\nexport type RustrakClient = {\n readonly [R in keyof Rustrak]: Resource<Rustrak[R], R & string>;\n};\n\n/**\n * Main Rustrak API client. Every method returns a `Result`; nothing throws for\n * an expected failure.\n *\n * @example\n * ```typescript\n * const client = new RustrakClient({\n * baseUrl: 'http://localhost:8080',\n * token: 'your-api-token',\n * });\n *\n * const issues = await client.issues.list({ path: { project_id: 1 } });\n * if (issues.success) console.log(issues.data.items);\n * ```\n */\nexport const RustrakClient = class {\n constructor(config: ClientConfig) {\n // biome-ignore lint/correctness/noConstructorReturn: the instance is the generated SDK with every method wrapped\n return Object.setPrototypeOf(createApi(config), new.target.prototype);\n }\n} as unknown as new (\n config: ClientConfig,\n) => RustrakClient;\n\nfunction createApi(config: ClientConfig): RustrakClient {\n const sdk = new Rustrak({\n client: createClient(\n createConfig({\n baseUrl: config.baseUrl,\n credentials: 'include',\n fetch: createFetch(config),\n // Every 2xx in the spec is JSON or empty, except the files in\n // `BINARY`. `auto` would hand back an HTML page or a stream as a\n // success without validating it.\n parseAs: 'json',\n headers: {\n // Without it a content-negotiating hop can answer with HTML.\n Accept: 'application/json',\n ...config.headers,\n ...(config.token && { Authorization: `Bearer ${config.token}` }),\n },\n }),\n ),\n });\n\n const api: Record<string, Record<string, unknown>> = {};\n for (const name of Object.getOwnPropertyNames(Rustrak.prototype)) {\n if (name === 'constructor') continue;\n const resource = sdk[name as keyof Rustrak] as unknown as Record<\n string,\n (options: object) => Promise<Outcome>\n >;\n const methods: Record<string, unknown> = {};\n for (const method of Object.getOwnPropertyNames(\n Object.getPrototypeOf(resource),\n )) {\n if (method === 'constructor') continue;\n methods[method] = async (options: { headers?: unknown } = {}) =>\n toResult(\n await resource[method]!({\n ...options,\n // A JSON write spreads these into an object, which keeps nothing\n // of a `Headers` or an array of pairs.\n headers:\n options.headers instanceof Headers ||\n Array.isArray(options.headers)\n ? Object.fromEntries(new Headers(options.headers))\n : options.headers,\n ...(BINARY.has(`${name}.${method}`) && { parseAs: 'blob' }),\n throwOnError: false,\n }),\n );\n }\n api[name] = methods;\n }\n return api as unknown as RustrakClient;\n}\n\ntype Outcome = {\n data?: unknown;\n error?: unknown;\n response?: Response;\n};\n\n/**\n * The boundary: the generated client's `{ data, error, response }` becomes a\n * `Result`.\n *\n * Only failures this file recognises become an `Err`. Anything else, a\n * `TypeError` from a genuine bug, is rethrown so it crashes where it can be\n * debugged instead of being laundered into `{ success: false }`.\n */\nfunction toResult({\n data,\n error,\n response,\n}: Outcome): Result<unknown, RustrakError> {\n if (error === undefined) return Ok(data);\n if (isTransportFailure(error)) return Err(error.rustrakError);\n if (response && !response.ok) {\n // `error` is the parsed body, or its text when it is not JSON.\n return Err(transformHttpError(response, error));\n }\n if (!response && error instanceof ZodError) {\n return Err({\n kind: 'invalid_request',\n message: 'The request input did not match the expected schema.',\n });\n }\n if (response && error instanceof SyntaxError) {\n return Err({\n kind: 'invalid_response',\n message: 'The server returned a body that is not valid JSON.',\n });\n }\n if (response && error instanceof ZodError) {\n // No Zod issues: they embed the offending response data.\n return Err({\n kind: 'invalid_response',\n message: 'The server response did not match the expected schema.',\n });\n }\n throw error;\n}\n"],"mappings":";;;;;;;AAiBA,MAAM,yBAA8B,IAAI,IAAY,CAClD,2BACF,CAAC;;;;;;;;;;;;;;;;AAmDD,MAAa,gBAAgB,MAAM;CACjC,YAAY,QAAsB;EAEhC,OAAO,OAAO,eAAe,UAAU,MAAM,GAAG,WAAW,SAAS;CACtE;AACF;AAIA,SAAS,UAAU,QAAqC;CACtD,MAAM,MAAM,IAAI,QAAQ,EACtB,QAAQ,aACN,aAAa;EACX,SAAS,OAAO;EAChB,aAAa;EACb,OAAO,YAAY,MAAM;EAIzB,SAAS;EACT,SAAS;GAEP,QAAQ;GACR,GAAG,OAAO;GACV,GAAI,OAAO,SAAS,EAAE,eAAe,UAAU,OAAO,QAAQ;EAChE;CACF,CAAC,CACH,EACF,CAAC;CAED,MAAM,MAA+C,CAAC;CACtD,KAAK,MAAM,QAAQ,OAAO,oBAAoB,QAAQ,SAAS,GAAG;EAChE,IAAI,SAAS,eAAe;EAC5B,MAAM,WAAW,IAAI;EAIrB,MAAM,UAAmC,CAAC;EAC1C,KAAK,MAAM,UAAU,OAAO,oBAC1B,OAAO,eAAe,QAAQ,CAChC,GAAG;GACD,IAAI,WAAW,eAAe;GAC9B,QAAQ,UAAU,OAAO,UAAiC,CAAC,MACzD,SACE,MAAM,SAAS,OAAO,CAAE;IACtB,GAAG;IAGH,SACE,QAAQ,mBAAmB,WAC3B,MAAM,QAAQ,QAAQ,OAAO,IACzB,OAAO,YAAY,IAAI,QAAQ,QAAQ,OAAO,CAAC,IAC/C,QAAQ;IACd,GAAI,OAAO,IAAI,GAAG,KAAK,GAAG,QAAQ,KAAK,EAAE,SAAS,OAAO;IACzD,cAAc;GAChB,CAAC,CACH;EACJ;EACA,IAAI,QAAQ;CACd;CACA,OAAO;AACT;;;;;;;;;AAgBA,SAAS,SAAS,EAChB,MACA,OACA,YACyC;CACzC,IAAI,UAAU,KAAA,GAAW,OAAO,GAAG,IAAI;CACvC,IAAI,mBAAmB,KAAK,GAAG,OAAO,IAAI,MAAM,YAAY;CAC5D,IAAI,YAAY,CAAC,SAAS,IAExB,OAAO,IAAI,mBAAmB,UAAU,KAAK,CAAC;CAEhD,IAAI,CAAC,YAAY,iBAAiB,UAChC,OAAO,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,IAAI,YAAY,iBAAiB,aAC/B,OAAO,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,IAAI,YAAY,iBAAiB,UAE/B,OAAO,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,MAAM;AACR"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import { ZodError } from 'zod';\nimport type { ClientConfig } from './config.js';\nimport type { RustrakError } from './errors.js';\nimport { BINARY } from './generated/binary.gen.js';\nimport { createClient, createConfig } from './generated/client/index.js';\nimport { Rustrak } from './generated/sdk.gen.js';\nimport { Err, Ok, type Result } from './result.js';\nimport {\n createFetch,\n RustrakTransportFailure,\n transformHttpError,\n} from './utils/http.js';\n\n/**\n * Operations whose 2xx is a file, read as a `Blob`. The spec leaves the body\n * untyped (zod has no binary type), so the type comes from this list.\n */\ntype Binary = (typeof BINARY)[number];\nconst BINARY_OPERATIONS: ReadonlySet<string> = new Set(BINARY);\n\n/** What a caller may pass: the request's own parts, never client plumbing. */\ntype RequestKeys = 'path' | 'query' | 'body' | 'headers' | 'signal';\n\ntype Call<F, Bytes extends boolean> = F extends (options: infer O) => infer R\n ? (\n ...args: {} extends Pick<O, Extract<keyof O, RequestKeys>>\n ? [options?: Pick<O, Extract<keyof O, RequestKeys>>]\n : [options: Pick<O, Extract<keyof O, RequestKeys>>]\n ) => Promise<\n Result<Bytes extends true ? Blob : Data<Awaited<R>>, RustrakError>\n >\n : never;\n\n/** The success half of the generated `{ data, error }` union. */\ntype Data<R> =\n Extract<R, { error: undefined }> extends { data: infer D } ? D : never;\n\ntype Resource<T, Name extends string> = {\n readonly [M in keyof T]: Call<\n T[M],\n `${Name}.${M & string}` extends Binary ? true : false\n >;\n};\n\n/**\n * Every operation in `openapi.json`, grouped by tag:\n * `client.issues.list({ path: { project_id } })`.\n *\n * Generated in `src/generated/`; this file only changes what a call returns.\n */\nexport type RustrakClient = {\n readonly [R in keyof Rustrak]: Resource<Rustrak[R], R & string>;\n};\n\n/**\n * Main Rustrak API client. Every method returns a `Result`; nothing throws for\n * an expected failure.\n *\n * @example\n * ```typescript\n * const client = new RustrakClient({\n * baseUrl: 'http://localhost:8080',\n * token: 'your-api-token',\n * });\n *\n * const issues = await client.issues.list({ path: { project_id: 1 } });\n * if (issues.success) console.log(issues.data.items);\n * ```\n */\nexport const RustrakClient = class {\n constructor(config: ClientConfig) {\n // biome-ignore lint/correctness/noConstructorReturn: the instance is the generated SDK with every method wrapped\n return Object.setPrototypeOf(createApi(config), new.target.prototype);\n }\n} as unknown as new (\n config: ClientConfig,\n) => RustrakClient;\n\nfunction createApi(config: ClientConfig): RustrakClient {\n const sdk = new Rustrak({\n client: createClient(\n createConfig({\n baseUrl: config.baseUrl,\n credentials: 'include',\n fetch: createFetch(config),\n // Every 2xx in the spec is JSON or empty, except the files in\n // `BINARY`. `auto` would hand back an HTML page or a stream as a\n // success without validating it.\n parseAs: 'json',\n headers: {\n // Without it a content-negotiating hop can answer with HTML.\n Accept: 'application/json',\n ...config.headers,\n ...(config.token && { Authorization: `Bearer ${config.token}` }),\n },\n }),\n ),\n });\n\n const api: Record<string, Record<string, unknown>> = {};\n for (const name of Object.getOwnPropertyNames(Rustrak.prototype)) {\n if (name === 'constructor') continue;\n const resource = sdk[name as keyof Rustrak] as unknown as Record<\n string,\n (options: object) => Promise<Outcome>\n >;\n const methods: Record<string, unknown> = {};\n for (const method of Object.getOwnPropertyNames(\n Object.getPrototypeOf(resource),\n )) {\n if (method === 'constructor') continue;\n methods[method] = async (options: { headers?: unknown } = {}) =>\n toResult(\n await resource[method]!({\n ...options,\n // A JSON write spreads these into an object, which keeps nothing\n // of a `Headers` or an array of pairs.\n headers:\n options.headers instanceof Headers ||\n Array.isArray(options.headers)\n ? Object.fromEntries(new Headers(options.headers))\n : options.headers,\n ...(BINARY_OPERATIONS.has(`${name}.${method}`) && {\n parseAs: 'blob',\n }),\n throwOnError: false,\n }),\n );\n }\n api[name] = methods;\n }\n return api as unknown as RustrakClient;\n}\n\ntype Outcome = {\n data?: unknown;\n error?: unknown;\n response?: Response;\n};\n\n/**\n * The boundary: the generated client's `{ data, error, response }` becomes a\n * `Result`.\n *\n * Only failures this file recognises become an `Err`. Anything else, a\n * `TypeError` from a genuine bug, is rethrown so it crashes where it can be\n * debugged instead of being laundered into `{ success: false }`.\n */\nfunction toResult({\n data,\n error,\n response,\n}: Outcome): Result<unknown, RustrakError> {\n if (error === undefined) return Ok(data);\n if (error instanceof RustrakTransportFailure) return Err(error.rustrakError);\n if (response && !response.ok) {\n // `error` is the parsed body, or its text when it is not JSON.\n return Err(transformHttpError(response, error));\n }\n if (!response && error instanceof ZodError) {\n return Err({\n kind: 'invalid_request',\n message: 'The request input did not match the expected schema.',\n });\n }\n if (response && error instanceof SyntaxError) {\n return Err({\n kind: 'invalid_response',\n message: 'The server returned a body that is not valid JSON.',\n });\n }\n if (response && error instanceof ZodError) {\n // No Zod issues: they embed the offending response data.\n return Err({\n kind: 'invalid_response',\n message: 'The server response did not match the expected schema.',\n });\n }\n throw error;\n}\n"],"mappings":";;;;;;;;AAkBA,MAAM,oBAAyC,IAAI,IAAI,MAAM;;;;;;;;;;;;;;;;AAmD7D,MAAa,gBAAgB,MAAM;CACjC,YAAY,QAAsB;EAEhC,OAAO,OAAO,eAAe,UAAU,MAAM,GAAG,WAAW,SAAS;CACtE;AACF;AAIA,SAAS,UAAU,QAAqC;CACtD,MAAM,MAAM,IAAI,QAAQ,EACtB,QAAQ,aACN,aAAa;EACX,SAAS,OAAO;EAChB,aAAa;EACb,OAAO,YAAY,MAAM;EAIzB,SAAS;EACT,SAAS;GAEP,QAAQ;GACR,GAAG,OAAO;GACV,GAAI,OAAO,SAAS,EAAE,eAAe,UAAU,OAAO,QAAQ;EAChE;CACF,CAAC,CACH,EACF,CAAC;CAED,MAAM,MAA+C,CAAC;CACtD,KAAK,MAAM,QAAQ,OAAO,oBAAoB,QAAQ,SAAS,GAAG;EAChE,IAAI,SAAS,eAAe;EAC5B,MAAM,WAAW,IAAI;EAIrB,MAAM,UAAmC,CAAC;EAC1C,KAAK,MAAM,UAAU,OAAO,oBAC1B,OAAO,eAAe,QAAQ,CAChC,GAAG;GACD,IAAI,WAAW,eAAe;GAC9B,QAAQ,UAAU,OAAO,UAAiC,CAAC,MACzD,SACE,MAAM,SAAS,OAAO,CAAE;IACtB,GAAG;IAGH,SACE,QAAQ,mBAAmB,WAC3B,MAAM,QAAQ,QAAQ,OAAO,IACzB,OAAO,YAAY,IAAI,QAAQ,QAAQ,OAAO,CAAC,IAC/C,QAAQ;IACd,GAAI,kBAAkB,IAAI,GAAG,KAAK,GAAG,QAAQ,KAAK,EAChD,SAAS,OACX;IACA,cAAc;GAChB,CAAC,CACH;EACJ;EACA,IAAI,QAAQ;CACd;CACA,OAAO;AACT;;;;;;;;;AAgBA,SAAS,SAAS,EAChB,MACA,OACA,YACyC;CACzC,IAAI,UAAU,KAAA,GAAW,OAAO,GAAG,IAAI;CACvC,IAAI,iBAAiB,yBAAyB,OAAO,IAAI,MAAM,YAAY;CAC3E,IAAI,YAAY,CAAC,SAAS,IAExB,OAAO,IAAI,mBAAmB,UAAU,KAAK,CAAC;CAEhD,IAAI,CAAC,YAAY,iBAAiB,UAChC,OAAO,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,IAAI,YAAY,iBAAiB,aAC/B,OAAO,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,IAAI,YAAY,iBAAiB,UAE/B,OAAO,IAAI;EACT,MAAM;EACN,SAAS;CACX,CAAC;CAEH,MAAM;AACR"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.cts","names":[],"sources":["../src/config.ts"],"mappings":"A,iBAGiB;EAKf;EAOA;EAMA;EAMA;EAKA,UAAU"}
1
+ {"version":3,"file":"config.d.cts","names":[],"sources":["../src/config.ts"],"mappings":"A,iBAAiB;EAEf;EAGA;EAMA;EAMA;EAEA,UAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":"A,iBAGiB;EAKf;EAOA;EAMA;EAMA;EAKA,UAAU"}
1
+ {"version":3,"file":"config.d.mts","names":[],"sources":["../src/config.ts"],"mappings":"A,iBAAiB;EAEf;EAGA;EAMA;EAMA;EAEA,UAAU"}
package/dist/errors.cjs CHANGED
@@ -16,6 +16,7 @@ const FIELD_ERROR_CODES = [
16
16
  "already_exists",
17
17
  "too_short",
18
18
  "too_long",
19
+ "incorrect",
19
20
  "custom"
20
21
  ];
21
22
  /**
@@ -33,19 +34,31 @@ const NETWORK_ERROR_MESSAGE = "The request could not reach the server.";
33
34
  /** The fixed message a `network` error carries when the client timed out. */
34
35
  const TIMEOUT_ERROR_MESSAGE = "The request timed out.";
35
36
  /**
36
- * Whether retrying the same call could plausibly succeed.
37
- *
38
- * `network` and `server_error` are transient by nature; `rate_limited` clears
39
- * once its window rolls over. Everything else is deterministic: the same
40
- * request will fail the same way.
37
+ * Statuses where the same request may well succeed a moment later. The one
38
+ * rule behind {@link isRetryable} and the transport's automatic retries.
39
+ */
40
+ const TRANSIENT_STATUSES = /* @__PURE__ */ new Set([
41
+ 408,
42
+ 429,
43
+ 500,
44
+ 502,
45
+ 503,
46
+ 504
47
+ ]);
48
+ /**
49
+ * Whether retrying the same call could plausibly succeed: a request that never
50
+ * got a response, or a transient status. A `rate_limited` error says how long
51
+ * to wait in `retryAfter`. Everything else is deterministic: the same request
52
+ * will fail the same way.
41
53
  */
42
54
  function isRetryable(error) {
43
- return error.kind === "network" || error.kind === "server_error" || error.kind === "rate_limited";
55
+ return error.kind === "network" || "status" in error && TRANSIENT_STATUSES.has(error.status);
44
56
  }
45
57
  exports.FIELD_ERROR_CODES = FIELD_ERROR_CODES;
46
58
  exports.NETWORK_ERROR_MESSAGE = NETWORK_ERROR_MESSAGE;
47
59
  exports.SERVER_ERROR_MESSAGE = SERVER_ERROR_MESSAGE;
48
60
  exports.TIMEOUT_ERROR_MESSAGE = TIMEOUT_ERROR_MESSAGE;
61
+ exports.TRANSIENT_STATUSES = TRANSIENT_STATUSES;
49
62
  exports.isRetryable = isRetryable;
50
63
 
51
64
  //# sourceMappingURL=errors.cjs.map