@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 +22 -0
- package/README.md +316 -0
- package/dist/index.cjs +781 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +433 -0
- package/dist/index.d.ts +433 -0
- package/dist/index.js +766 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
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
|