@rustrak/client 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2026 Abian Suarez
5
+
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ This program is free software: you can redistribute it and/or modify
10
+ it under the terms of the GNU General Public License as published by
11
+ the Free Software Foundation, either version 3 of the License, or
12
+ (at your option) any later version.
13
+
14
+ This program is distributed in the hope that it will be useful,
15
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ GNU General Public License for more details.
18
+
19
+ You should have received a copy of the GNU General Public License
20
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
21
+
22
+ For the full license text, see: https://www.gnu.org/licenses/gpl-3.0.txt
package/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # @rustrak/client
2
+
3
+ TypeScript client for the Rustrak error tracking API. Provides a type-safe, fully-featured interface for interacting with Rustrak's REST API.
4
+
5
+ ## Features
6
+
7
+ - **Type-Safe**: Full TypeScript support with runtime validation using Zod
8
+ - **Lightweight**: ~28KB total bundle size (ky 3KB + zod 10KB + client 15KB)
9
+ - **Automatic Retry**: Built-in retry logic for transient failures
10
+ - **Error Handling**: Structured error classes for different failure scenarios
11
+ - **Pagination**: First-class support for cursor-based pagination
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ pnpm add @rustrak/client
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```typescript
22
+ import { RustrakClient } from '@rustrak/client';
23
+
24
+ const client = new RustrakClient({
25
+ baseUrl: 'http://localhost:8080',
26
+ token: 'your-api-token',
27
+ });
28
+
29
+ // List all projects
30
+ const projects = await client.projects.list();
31
+
32
+ // Get issues for a project
33
+ const { items, next_cursor, has_more } = await client.issues.list(1);
34
+
35
+ // Get events for an issue
36
+ const events = await client.events.list(1, 'issue-uuid');
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ### Configuration
42
+
43
+ ```typescript
44
+ import { RustrakClient } from '@rustrak/client';
45
+
46
+ const client = new RustrakClient({
47
+ baseUrl: 'https://rustrak.example.com',
48
+ token: 'your-bearer-token',
49
+ timeout: 30000, // Optional: request timeout in ms (default: 30000)
50
+ maxRetries: 2, // Optional: max retry attempts (default: 2)
51
+ headers: {
52
+ // Optional: custom headers
53
+ 'X-Custom-Header': 'value',
54
+ },
55
+ });
56
+ ```
57
+
58
+ ### Projects
59
+
60
+ ```typescript
61
+ // List all projects
62
+ const projects = await client.projects.list();
63
+
64
+ // Get a single project
65
+ const project = await client.projects.get(1);
66
+
67
+ // Create a project
68
+ const newProject = await client.projects.create({
69
+ name: 'My App',
70
+ slug: 'my-app', // Optional
71
+ });
72
+
73
+ // Update a project
74
+ const updated = await client.projects.update(1, {
75
+ name: 'Updated Name',
76
+ });
77
+
78
+ // Delete a project
79
+ await client.projects.delete(1);
80
+ ```
81
+
82
+ ### Issues
83
+
84
+ ```typescript
85
+ // List issues with pagination
86
+ const response = await client.issues.list(projectId, {
87
+ sort: 'last_seen', // 'digest_order' | 'last_seen'
88
+ order: 'desc', // 'asc' | 'desc'
89
+ include_resolved: false,
90
+ cursor: 'eyJzb3J0...', // Optional: pagination cursor
91
+ });
92
+
93
+ // Paginate through all issues
94
+ let cursor: string | undefined;
95
+ do {
96
+ const { items, next_cursor, has_more } = await client.issues.list(projectId, {
97
+ cursor,
98
+ });
99
+
100
+ // Process items...
101
+ cursor = next_cursor;
102
+ } while (cursor);
103
+
104
+ // Get a single issue
105
+ const issue = await client.issues.get(projectId, issueId);
106
+
107
+ // Update issue state
108
+ const resolved = await client.issues.updateState(projectId, issueId, {
109
+ is_resolved: true,
110
+ is_muted: false,
111
+ });
112
+
113
+ // Delete an issue
114
+ await client.issues.delete(projectId, issueId);
115
+ ```
116
+
117
+ ### Events
118
+
119
+ ```typescript
120
+ // List events for an issue
121
+ const { items, next_cursor } = await client.events.list(projectId, issueId, {
122
+ order: 'desc',
123
+ cursor: 'optional-cursor',
124
+ });
125
+
126
+ // Get event details
127
+ const event = await client.events.get(projectId, issueId, eventId);
128
+
129
+ // Access full Sentry event data
130
+ console.log(event.data); // Full JSON payload
131
+ ```
132
+
133
+ ### Auth Tokens
134
+
135
+ ```typescript
136
+ // List tokens (masked)
137
+ const tokens = await client.tokens.list();
138
+
139
+ // Get a single token (masked)
140
+ const token = await client.tokens.get(1);
141
+
142
+ // Create a token (full token only shown once!)
143
+ const created = await client.tokens.create({
144
+ description: 'CI/CD token',
145
+ });
146
+ console.log(created.token); // Save this! Won't be shown again
147
+
148
+ // Delete a token
149
+ await client.tokens.delete(1);
150
+ ```
151
+
152
+ ## Error Handling
153
+
154
+ The client throws structured error classes for different scenarios:
155
+
156
+ ```typescript
157
+ import {
158
+ RustrakError,
159
+ NetworkError,
160
+ AuthenticationError,
161
+ RateLimitError,
162
+ NotFoundError,
163
+ ValidationError,
164
+ } from '@rustrak/client';
165
+
166
+ try {
167
+ await client.projects.list();
168
+ } catch (error) {
169
+ if (error instanceof RateLimitError) {
170
+ console.log(`Rate limited. Retry after ${error.retryAfter}s`);
171
+ } else if (error instanceof AuthenticationError) {
172
+ console.log('Invalid token');
173
+ } else if (error instanceof NotFoundError) {
174
+ console.log('Resource not found');
175
+ } else if (error instanceof NetworkError) {
176
+ console.log('Network error - will retry automatically');
177
+ } else if (error instanceof ValidationError) {
178
+ console.log('API response validation failed');
179
+ console.log(error.getValidationDetails());
180
+ }
181
+ }
182
+ ```
183
+
184
+ ### Error Types
185
+
186
+ | Error Class | HTTP Status | Retryable | Use Case |
187
+ |-------------|-------------|-----------|----------|
188
+ | `NetworkError` | - | ✅ | Connection issues, timeouts |
189
+ | `AuthenticationError` | 401 | ❌ | Invalid credentials |
190
+ | `AuthorizationError` | 403 | ❌ | Insufficient permissions |
191
+ | `NotFoundError` | 404 | ❌ | Resource doesn't exist |
192
+ | `BadRequestError` | 400 | ❌ | Invalid request payload |
193
+ | `RateLimitError` | 429 | ✅ | Rate limit exceeded |
194
+ | `ServerError` | 500+ | ✅ | Server-side errors |
195
+ | `ValidationError` | - | ❌ | Response schema mismatch |
196
+
197
+ ## Usage with Next.js
198
+
199
+ ### Server Component
200
+
201
+ ```typescript
202
+ import { RustrakClient } from '@rustrak/client';
203
+
204
+ export default async function ProjectsPage() {
205
+ const client = new RustrakClient({
206
+ baseUrl: process.env.RUSTRAK_API_URL!,
207
+ token: process.env.RUSTRAK_API_TOKEN!,
208
+ });
209
+
210
+ const projects = await client.projects.list();
211
+
212
+ return (
213
+ <div>
214
+ {projects.map((project) => (
215
+ <div key={project.id}>{project.name}</div>
216
+ ))}
217
+ </div>
218
+ );
219
+ }
220
+ ```
221
+
222
+ ### Client Component with SWR
223
+
224
+ ```typescript
225
+ 'use client';
226
+
227
+ import useSWR from 'swr';
228
+ import { RustrakClient } from '@rustrak/client';
229
+
230
+ const client = new RustrakClient({
231
+ baseUrl: process.env.NEXT_PUBLIC_RUSTRAK_API_URL!,
232
+ token: process.env.NEXT_PUBLIC_RUSTRAK_API_TOKEN!,
233
+ });
234
+
235
+ export function IssuesList({ projectId }: { projectId: number }) {
236
+ const { data, error, isLoading } = useSWR(
237
+ ['issues', projectId],
238
+ () => client.issues.list(projectId)
239
+ );
240
+
241
+ if (error) return <div>Error: {error.message}</div>;
242
+ if (isLoading) return <div>Loading...</div>;
243
+
244
+ return (
245
+ <div>
246
+ {data?.items.map((issue) => (
247
+ <div key={issue.id}>{issue.title}</div>
248
+ ))}
249
+ </div>
250
+ );
251
+ }
252
+ ```
253
+
254
+ ### Server Action
255
+
256
+ ```typescript
257
+ 'use server';
258
+
259
+ import { RustrakClient } from '@rustrak/client';
260
+
261
+ export async function resolveIssue(projectId: number, issueId: string) {
262
+ const client = new RustrakClient({
263
+ baseUrl: process.env.RUSTRAK_API_URL!,
264
+ token: process.env.RUSTRAK_API_TOKEN!,
265
+ });
266
+
267
+ return await client.issues.updateState(projectId, issueId, {
268
+ is_resolved: true,
269
+ });
270
+ }
271
+ ```
272
+
273
+ ## TypeScript
274
+
275
+ The client is written in TypeScript with strict mode enabled. All types are exported:
276
+
277
+ ```typescript
278
+ import type {
279
+ Project,
280
+ Issue,
281
+ Event,
282
+ EventDetail,
283
+ PaginatedResponse,
284
+ CreateProject,
285
+ UpdateIssueState,
286
+ } from '@rustrak/client';
287
+
288
+ const project: Project = await client.projects.get(1);
289
+ const issues: PaginatedResponse<Issue> = await client.issues.list(1);
290
+ ```
291
+
292
+ ## Development
293
+
294
+ ```bash
295
+ # Install dependencies
296
+ pnpm install
297
+
298
+ # Build
299
+ pnpm build
300
+
301
+ # Type check
302
+ pnpm check-types
303
+
304
+ # Run tests
305
+ pnpm test
306
+
307
+ # Run tests in watch mode
308
+ pnpm test:watch
309
+
310
+ # Generate coverage
311
+ pnpm test:coverage
312
+ ```
313
+
314
+ ## License
315
+
316
+ GPL-3.0