@nvisy/sdk 0.1.0 → 0.3.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.
package/dist/index.js CHANGED
@@ -3,62 +3,29 @@ import createClient from 'openapi-fetch';
3
3
  // src/client.ts
4
4
 
5
5
  // src/config.ts
6
- var ENV_VARS = {
7
- API_KEY: "NVISY_API_KEY",
8
- BASE_URL: "NVISY_BASE_URL",
9
- TIMEOUT: "NVISY_TIMEOUT",
10
- MAX_RETRIES: "NVISY_MAX_RETRIES"
11
- };
6
+ var VERSION = "0.3.0";
12
7
  var DEFAULTS = {
13
- baseUrl: "https://api.nvisy.com",
14
- timeout: 3e4,
15
- maxRetries: 3,
16
- headers: {}
8
+ /**
9
+ * Default base URL for the Nvisy API.
10
+ */
11
+ BASE_URL: "https://api.nvisy.com",
12
+ /**
13
+ * Default user agent string.
14
+ */
15
+ USER_AGENT: `@nvisy/sdk v.${VERSION}`
17
16
  };
18
- function loadConfigFromEnv() {
19
- const config = {};
20
- const apiKey = process.env[ENV_VARS.API_KEY];
21
- if (apiKey) {
22
- config.apiKey = apiKey;
23
- }
24
- const baseUrl = process.env[ENV_VARS.BASE_URL];
25
- if (baseUrl) {
26
- config.baseUrl = baseUrl;
27
- }
28
- const timeout = process.env[ENV_VARS.TIMEOUT];
29
- if (timeout) {
30
- const timeoutMs = parseInt(timeout, 10);
31
- if (!Number.isNaN(timeoutMs)) {
32
- config.timeout = timeoutMs;
33
- }
34
- }
35
- const maxRetries = process.env[ENV_VARS.MAX_RETRIES];
36
- if (maxRetries) {
37
- const retries = parseInt(maxRetries, 10);
38
- if (!Number.isNaN(retries)) {
39
- config.maxRetries = retries;
40
- }
41
- }
42
- return config;
43
- }
44
- function resolveConfig(userConfig) {
45
- const envConfig = loadConfigFromEnv();
46
- const mergedConfig = { ...envConfig, ...userConfig };
47
- return {
48
- apiKey: mergedConfig.apiKey || "",
49
- baseUrl: mergedConfig.baseUrl || DEFAULTS.baseUrl,
50
- timeout: mergedConfig.timeout ?? DEFAULTS.timeout,
51
- maxRetries: mergedConfig.maxRetries ?? DEFAULTS.maxRetries,
52
- headers: { ...DEFAULTS.headers, ...mergedConfig.headers }
53
- };
54
- }
55
- function getEnvironmentVariables() {
56
- return { ...ENV_VARS };
57
- }
58
17
 
59
18
  // src/errors.ts
60
- var ClientError = class extends Error {
19
+ var NvisyError = class extends Error {
20
+ /**
21
+ * The error class name.
22
+ */
61
23
  name;
24
+ /**
25
+ * Creates a new NvisyError.
26
+ *
27
+ * @param message - The error message
28
+ */
62
29
  constructor(message) {
63
30
  super(message);
64
31
  this.name = this.constructor.name;
@@ -66,490 +33,1448 @@ var ClientError = class extends Error {
66
33
  Error.captureStackTrace(this, this.constructor);
67
34
  }
68
35
  }
36
+ };
37
+ var NvisyApiError = class extends NvisyError {
69
38
  /**
70
- * Convert error to JSON representation
39
+ * The error type identifier (e.g., "ValidationError", "NotFoundError").
40
+ */
41
+ name;
42
+ /**
43
+ * Human-readable error message safe for display to end users.
44
+ */
45
+ message;
46
+ /**
47
+ * The resource type that the error relates to (e.g., "account", "project").
48
+ * May be null if the error is not resource-specific.
49
+ */
50
+ resource;
51
+ /**
52
+ * A helpful suggestion for resolving the error.
53
+ * May be null if no suggestion is available.
54
+ */
55
+ suggestion;
56
+ /**
57
+ * Field-specific validation errors.
58
+ * Present when the error is due to invalid input data.
59
+ */
60
+ validation;
61
+ /**
62
+ * HTTP status code of the response (e.g., 400, 404, 500).
63
+ */
64
+ statusCode;
65
+ /**
66
+ * Creates a new NvisyApiError from an API error response.
67
+ *
68
+ * @param response - The error response from the API
69
+ * @param statusCode - The HTTP status code of the response
70
+ */
71
+ constructor(response, statusCode) {
72
+ super(response.message);
73
+ this.name = response.name;
74
+ this.message = response.message;
75
+ this.resource = response.resource;
76
+ this.suggestion = response.suggestion;
77
+ this.validation = response.validation;
78
+ this.statusCode = statusCode;
79
+ if (Error.captureStackTrace) {
80
+ Error.captureStackTrace(this, this.constructor);
81
+ }
82
+ }
83
+ /**
84
+ * Checks if this is a client error (4xx status code).
85
+ *
86
+ * Client errors indicate problems with the request itself, such as
87
+ * invalid input, missing authentication, or accessing non-existent resources.
88
+ *
89
+ * @returns True if the status code is in the 4xx range
90
+ */
91
+ isClientError() {
92
+ return this.statusCode >= 400 && this.statusCode < 500;
93
+ }
94
+ /**
95
+ * Checks if this is a server error (5xx status code).
96
+ *
97
+ * Server errors indicate problems on the API side. These are typically
98
+ * transient and may succeed if retried.
99
+ *
100
+ * @returns True if the status code is in the 5xx range
101
+ */
102
+ isServerError() {
103
+ return this.statusCode >= 500;
104
+ }
105
+ /**
106
+ * Determines if this error is safe to retry.
107
+ *
108
+ * An error is considered retryable if it's a server error (5xx),
109
+ * a request timeout (408), or rate limiting (429).
110
+ *
111
+ * @returns True if the request may succeed on retry
112
+ */
113
+ isRetryable() {
114
+ return this.statusCode >= 500 || // Server errors
115
+ this.statusCode === 408 || // Request timeout
116
+ this.statusCode === 429;
117
+ }
118
+ /**
119
+ * Converts the error to a plain {@link ErrorResponse} object.
120
+ *
121
+ * Useful for serialization or logging.
122
+ *
123
+ * @returns A plain object representation of the error
71
124
  */
72
125
  toJSON() {
73
126
  return {
74
127
  name: this.name,
75
128
  message: this.message,
76
- context: ""
129
+ resource: this.resource,
130
+ suggestion: this.suggestion,
131
+ validation: this.validation
77
132
  };
78
133
  }
79
134
  };
80
- var ConfigError = class _ConfigError extends ClientError {
81
- /** Field that caused the error (for validation errors) */
82
- field;
83
- /** Reason why the configuration is invalid */
84
- reason;
85
- constructor(message, options) {
86
- super(message);
87
- this.field = options?.field;
88
- this.reason = options?.reason;
135
+
136
+ // src/middleware/error.ts
137
+ var errorMiddleware = {
138
+ async onResponse({ response }) {
139
+ if (!response.ok) {
140
+ const error = await response.clone().json();
141
+ throw new NvisyApiError(error, response.status);
142
+ }
143
+ return response;
144
+ },
145
+ onError({ error }) {
146
+ if (error instanceof Error) {
147
+ throw new NvisyError(error.message);
148
+ }
149
+ throw new NvisyError("An unknown network error occurred");
150
+ }
151
+ };
152
+
153
+ // src/middleware/logging.ts
154
+ function createLoggingMiddleware() {
155
+ return {
156
+ async onRequest({ request }) {
157
+ const start = performance.now();
158
+ request._startTime = start;
159
+ return request;
160
+ },
161
+ async onResponse({ request, response }) {
162
+ const start = request._startTime ?? performance.now();
163
+ const duration = Math.round(performance.now() - start);
164
+ const url = new URL(request.url);
165
+ console.log(
166
+ `[nvisy] ${request.method} ${url.pathname} ${response.status} (${duration}ms)`
167
+ );
168
+ if (!response.ok) {
169
+ const cloned = response.clone();
170
+ try {
171
+ const body = await cloned.json();
172
+ console.error("[nvisy] Response:", JSON.stringify(body, null, 2));
173
+ } catch {
174
+ const text = await cloned.text();
175
+ if (text) console.error("[nvisy] Response:", text);
176
+ }
177
+ }
178
+ return response;
179
+ },
180
+ onError({ request, error }) {
181
+ const start = request._startTime ?? performance.now();
182
+ const duration = Math.round(performance.now() - start);
183
+ const url = new URL(request.url);
184
+ console.error(
185
+ `[nvisy] ${request.method} ${url.pathname} ERROR (${duration}ms):`,
186
+ error instanceof Error ? error.message : error
187
+ );
188
+ }
189
+ };
190
+ }
191
+
192
+ // src/services/account.ts
193
+ var Account = class {
194
+ #api;
195
+ constructor(api) {
196
+ this.#api = api;
197
+ }
198
+ /**
199
+ * Get the authenticated user's account details
200
+ * @returns Promise that resolves with the account details
201
+ * @throws {ApiError} if the request fails
202
+ */
203
+ async getAccount() {
204
+ const { data } = await this.#api.GET("/account");
205
+ return data;
89
206
  }
90
207
  /**
91
- * Create error for missing API key
208
+ * Update the authenticated user's account details
209
+ * @param updates - Account update request
210
+ * @returns Promise that resolves with the updated account
211
+ * @throws {ApiError} if the request fails
92
212
  */
93
- static missingApiKey() {
94
- return new _ConfigError("API key is required", {
95
- field: "apiKey",
96
- reason: "API key must be provided in configuration"
213
+ async updateAccount(updates) {
214
+ const { data } = await this.#api.PATCH("/account", {
215
+ body: updates
97
216
  });
217
+ return data;
98
218
  }
99
219
  /**
100
- * Create error for invalid configuration field
220
+ * Delete the authenticated user's account
221
+ * @returns Promise that resolves when the account is deleted
222
+ * @throws {ApiError} if the request fails
101
223
  */
102
- static invalidField(field, reason) {
103
- return new _ConfigError(`Invalid configuration for ${field}: ${reason}`, {
104
- field,
105
- reason
224
+ async deleteAccount() {
225
+ await this.#api.DELETE("/account");
226
+ }
227
+ };
228
+
229
+ // src/services/activities.ts
230
+ var Activities = class {
231
+ #api;
232
+ constructor(api) {
233
+ this.#api = api;
234
+ }
235
+ /**
236
+ * List activities for a workspace
237
+ * @param workspaceId - Workspace ID
238
+ * @param query - Optional pagination parameters (limit, after)
239
+ * @returns Promise that resolves with a paginated list of activities
240
+ * @throws {ApiError} if the request fails
241
+ */
242
+ async listActivities(workspaceId, query) {
243
+ const { data } = await this.#api.GET(
244
+ "/workspaces/{workspaceId}/activities/",
245
+ {
246
+ params: { path: { workspaceId }, query }
247
+ }
248
+ );
249
+ return data;
250
+ }
251
+ };
252
+
253
+ // src/services/annotations.ts
254
+ var Annotations = class {
255
+ #api;
256
+ constructor(api) {
257
+ this.#api = api;
258
+ }
259
+ /**
260
+ * List annotations for a file
261
+ * @param fileId - File ID
262
+ * @param query - Optional pagination parameters (limit, after)
263
+ * @returns Promise that resolves with a paginated list of annotations
264
+ * @throws {ApiError} if the request fails
265
+ */
266
+ async listAnnotations(fileId, query) {
267
+ const { data } = await this.#api.GET("/files/{fileId}/annotations/", {
268
+ params: { path: { fileId }, query }
106
269
  });
270
+ return data;
107
271
  }
108
272
  /**
109
- * Create error for missing required field
273
+ * Get annotation details by ID
274
+ * @param annotationId - Annotation ID
275
+ * @returns Promise that resolves with the annotation details
276
+ * @throws {ApiError} if the request fails
110
277
  */
111
- static missingField(field) {
112
- return new _ConfigError(`Missing required configuration field: ${field}`, {
113
- field,
114
- reason: "This field is required"
278
+ async getAnnotation(annotationId) {
279
+ const { data } = await this.#api.GET("/annotations/{annotationId}", {
280
+ params: { path: { annotationId } }
115
281
  });
282
+ return data;
116
283
  }
117
284
  /**
118
- * Convert error to JSON representation
285
+ * Create a new annotation
286
+ * @param fileId - File ID
287
+ * @param annotation - Annotation creation request
288
+ * @returns Promise that resolves with the created annotation
289
+ * @throws {ApiError} if the request fails
119
290
  */
120
- toJSON() {
121
- return {
122
- name: this.name,
123
- message: this.message,
124
- context: this.field || this.reason ? `field: ${this.field}, reason: ${this.reason}` : ""
125
- };
291
+ async createAnnotation(fileId, annotation) {
292
+ const { data } = await this.#api.POST("/files/{fileId}/annotations/", {
293
+ params: { path: { fileId } },
294
+ body: annotation
295
+ });
296
+ return data;
297
+ }
298
+ /**
299
+ * Update an existing annotation
300
+ * @param annotationId - Annotation ID
301
+ * @param updates - Annotation update request
302
+ * @returns Promise that resolves with the updated annotation
303
+ * @throws {ApiError} if the request fails
304
+ */
305
+ async updateAnnotation(annotationId, updates) {
306
+ const { data } = await this.#api.PATCH("/annotations/{annotationId}", {
307
+ params: { path: { annotationId } },
308
+ body: updates
309
+ });
310
+ return data;
311
+ }
312
+ /**
313
+ * Delete an annotation
314
+ * @param annotationId - Annotation ID
315
+ * @returns Promise that resolves when the annotation is deleted
316
+ * @throws {ApiError} if the request fails
317
+ */
318
+ async deleteAnnotation(annotationId) {
319
+ await this.#api.DELETE("/annotations/{annotationId}", {
320
+ params: { path: { annotationId } }
321
+ });
126
322
  }
127
323
  };
128
- var NetworkError = class _NetworkError extends ClientError {
129
- /** Original error that caused this network error */
130
- cause;
131
- constructor(message, cause) {
132
- super(message);
133
- this.cause = cause;
324
+
325
+ // src/services/api-tokens.ts
326
+ var ApiTokens = class {
327
+ #api;
328
+ constructor(api) {
329
+ this.#api = api;
134
330
  }
135
331
  /**
136
- * Create error for network/connection issues
332
+ * List all API tokens for the authenticated account
333
+ * @param query - Optional pagination parameters (limit, after)
334
+ * @returns Promise that resolves with a paginated list of API tokens
335
+ * @throws {ApiError} if the request fails
137
336
  */
138
- static connection(message, cause) {
139
- return new _NetworkError(message, cause);
337
+ async listApiTokens(query) {
338
+ const { data } = await this.#api.GET("/api-tokens/", {
339
+ params: { query }
340
+ });
341
+ return data;
140
342
  }
141
343
  /**
142
- * Create error for request timeout
344
+ * Get a specific API token by token ID
345
+ * @param tokenId - The token identifier
346
+ * @returns Promise that resolves with the API token details
347
+ * @throws {ApiError} if the request fails
143
348
  */
144
- static timeout(timeoutMs) {
145
- return new _NetworkError(`Request timed out after ${timeoutMs}ms`);
349
+ async getApiToken(tokenId) {
350
+ const { data } = await this.#api.GET("/api-tokens/{tokenId}/", {
351
+ params: { path: { tokenId } }
352
+ });
353
+ return data;
146
354
  }
147
355
  /**
148
- * Create error for aborted request
356
+ * Create a new API token
357
+ * @param token - Token creation request
358
+ * @returns Promise that resolves with the created token (includes JWT, shown only once)
359
+ * @throws {ApiError} if the request fails
149
360
  */
150
- static aborted() {
151
- return new _NetworkError("Request was aborted");
361
+ async createApiToken(token) {
362
+ const { data } = await this.#api.POST("/api-tokens/", {
363
+ body: token
364
+ });
365
+ return data;
152
366
  }
153
367
  /**
154
- * Create error for DNS resolution failure
368
+ * Update an existing API token
369
+ * @param tokenId - The token identifier
370
+ * @param updates - Token update request
371
+ * @returns Promise that resolves with the updated token
372
+ * @throws {ApiError} if the request fails
155
373
  */
156
- static dnsResolution(hostname) {
157
- return new _NetworkError(`Failed to resolve hostname: ${hostname}`);
374
+ async updateApiToken(tokenId, updates) {
375
+ const { data } = await this.#api.PATCH("/api-tokens/{tokenId}/", {
376
+ params: { path: { tokenId } },
377
+ body: updates
378
+ });
379
+ return data;
158
380
  }
159
381
  /**
160
- * Convert error to JSON representation
382
+ * Revoke an API token
383
+ * @param tokenId - The token identifier
384
+ * @returns Promise that resolves when the token is revoked
385
+ * @throws {ApiError} if the request fails
161
386
  */
162
- toJSON() {
163
- return {
164
- name: this.name,
165
- message: this.message,
166
- context: this.cause ? `cause: ${this.cause.message}` : ""
167
- };
387
+ async revokeApiToken(tokenId) {
388
+ await this.#api.DELETE("/api-tokens/{tokenId}/", {
389
+ params: { path: { tokenId } }
390
+ });
168
391
  }
169
392
  };
170
- var ApiError = class _ApiError extends ClientError {
171
- /** Error response from server */
172
- errorResponse;
173
- /** HTTP status code */
174
- statusCode;
175
- /** Request ID for debugging */
176
- requestId;
177
- constructor(message, statusCode, options) {
178
- super(message);
179
- this.statusCode = statusCode;
180
- this.errorResponse = options?.errorResponse;
181
- this.requestId = options?.requestId;
393
+
394
+ // src/services/auth.ts
395
+ var Auth = class {
396
+ #api;
397
+ constructor(api) {
398
+ this.#api = api;
182
399
  }
183
400
  /**
184
- * Create error from HTTP response
401
+ * Login with email and password
402
+ * @param credentials - Login credentials
403
+ * @returns Promise that resolves with the auth response containing access token
404
+ * @throws {ApiError} if the request fails
185
405
  */
186
- static fromResponse(response, errorData, requestId) {
187
- const message = errorData?.message || `HTTP ${response.status}: ${response.statusText}`;
188
- return new _ApiError(message, response.status, {
189
- errorResponse: errorData,
190
- requestId
406
+ async loginAccount(credentials) {
407
+ const { data } = await this.#api.POST("/auth/login", {
408
+ body: credentials
191
409
  });
410
+ return data;
192
411
  }
193
412
  /**
194
- * Create error for rate limiting
413
+ * Sign up a new account
414
+ * @param credentials - Signup details
415
+ * @returns Promise that resolves with the auth response containing access token
416
+ * @throws {ApiError} if the request fails
195
417
  */
196
- static rateLimited(retryAfter, requestId) {
197
- const message = retryAfter ? `Rate limited. Retry after ${retryAfter} seconds` : "Rate limited";
198
- return new _ApiError(message, 429, {
199
- requestId,
200
- errorResponse: {
201
- name: "RateLimitError",
202
- message,
203
- context: retryAfter ? `retryAfter: ${retryAfter}` : ""
204
- }
418
+ async signupAccount(credentials) {
419
+ const { data } = await this.#api.POST("/auth/signup", {
420
+ body: credentials
205
421
  });
422
+ return data;
206
423
  }
207
424
  /**
208
- * Check if error is a client error (4xx)
425
+ * Logout and invalidate the current access token
426
+ * @returns Promise that resolves when logout is complete
427
+ * @throws {ApiError} if the request fails
209
428
  */
210
- isClientError() {
211
- return this.statusCode ? this.statusCode >= 400 && this.statusCode < 500 : false;
429
+ async logoutAccount() {
430
+ await this.#api.POST("/auth/logout");
431
+ }
432
+ };
433
+
434
+ // src/services/comments.ts
435
+ var Comments = class {
436
+ #api;
437
+ constructor(api) {
438
+ this.#api = api;
212
439
  }
213
440
  /**
214
- * Check if error is a server error (5xx)
441
+ * List all comments on a file
442
+ * @param fileId - File ID
443
+ * @param query - Optional pagination parameters (limit, after)
444
+ * @returns Promise that resolves with a paginated list of comments
445
+ * @throws {ApiError} if the request fails
215
446
  */
216
- isServerError() {
217
- return this.statusCode ? this.statusCode >= 500 : false;
447
+ async listComments(fileId, query) {
448
+ const { data } = await this.#api.GET("/files/{fileId}/comments", {
449
+ params: { path: { fileId }, query }
450
+ });
451
+ return data;
218
452
  }
219
453
  /**
220
- * Check if error is retryable based on HTTP status
454
+ * Create a new comment on a file
455
+ * @param fileId - File ID
456
+ * @param comment - Comment creation request
457
+ * @returns Promise that resolves with the created comment
458
+ * @throws {ApiError} if the request fails
221
459
  */
222
- isRetryable() {
223
- if (!this.statusCode) return false;
224
- return this.statusCode >= 500 || // Server errors
225
- this.statusCode === 408 || // Request timeout
226
- this.statusCode === 429;
460
+ async createComment(fileId, comment) {
461
+ const { data } = await this.#api.POST("/files/{fileId}/comments", {
462
+ params: { path: { fileId } },
463
+ body: comment
464
+ });
465
+ return data;
466
+ }
467
+ /**
468
+ * Update a comment
469
+ * @param commentId - Comment ID
470
+ * @param updates - Comment update request
471
+ * @returns Promise that resolves with the updated comment
472
+ * @throws {ApiError} if the request fails
473
+ */
474
+ async updateComment(commentId, updates) {
475
+ const { data } = await this.#api.PATCH("/comments/{commentId}", {
476
+ params: { path: { commentId } },
477
+ body: updates
478
+ });
479
+ return data;
480
+ }
481
+ /**
482
+ * Delete a comment
483
+ * @param commentId - Comment ID
484
+ * @returns Promise that resolves when the comment is deleted
485
+ * @throws {ApiError} if the request fails
486
+ */
487
+ async deleteComment(commentId) {
488
+ await this.#api.DELETE("/comments/{commentId}", {
489
+ params: { path: { commentId } }
490
+ });
491
+ }
492
+ };
493
+
494
+ // src/services/documents.ts
495
+ var Documents = class {
496
+ #api;
497
+ constructor(api) {
498
+ this.#api = api;
227
499
  }
228
500
  /**
229
- * Get retry delay in milliseconds (returns null if not retryable)
501
+ * List documents in a workspace
502
+ * @param workspaceId - Workspace ID
503
+ * @param query - Optional pagination parameters (limit, after)
504
+ * @returns Promise that resolves with a paginated list of documents
505
+ * @throws {ApiError} if the request fails
230
506
  */
231
- getRetryDelay() {
232
- if (!this.isRetryable()) return null;
233
- if (this.statusCode === 429 && this.errorResponse?.context) {
234
- const match = this.errorResponse.context.match(/retryAfter: (\d+)/);
235
- if (match) {
236
- const retryAfter = parseInt(match[1], 10);
237
- return retryAfter * 1e3;
507
+ async listDocuments(workspaceId, query) {
508
+ const { data } = await this.#api.GET(
509
+ "/workspaces/{workspaceId}/documents",
510
+ {
511
+ params: { path: { workspaceId }, query }
238
512
  }
513
+ );
514
+ return data;
515
+ }
516
+ /**
517
+ * Get document details by ID
518
+ * @param documentId - Document ID
519
+ * @returns Promise that resolves with the document details
520
+ * @throws {ApiError} if the request fails
521
+ */
522
+ async getDocument(documentId) {
523
+ const { data } = await this.#api.GET("/documents/{documentId}", {
524
+ params: { path: { documentId } }
525
+ });
526
+ return data;
527
+ }
528
+ /**
529
+ * Create a new document
530
+ * @param workspaceId - Workspace ID
531
+ * @param document - Document creation request
532
+ * @returns Promise that resolves with the created document
533
+ * @throws {ApiError} if the request fails
534
+ */
535
+ async createDocument(workspaceId, document) {
536
+ const { data } = await this.#api.POST(
537
+ "/workspaces/{workspaceId}/documents",
538
+ {
539
+ params: { path: { workspaceId } },
540
+ body: document
541
+ }
542
+ );
543
+ return data;
544
+ }
545
+ /**
546
+ * Update an existing document
547
+ * @param documentId - Document ID
548
+ * @param updates - Document update request
549
+ * @returns Promise that resolves with the updated document
550
+ * @throws {ApiError} if the request fails
551
+ */
552
+ async updateDocument(documentId, updates) {
553
+ const { data } = await this.#api.PATCH("/documents/{documentId}", {
554
+ params: { path: { documentId } },
555
+ body: updates
556
+ });
557
+ return data;
558
+ }
559
+ /**
560
+ * Delete a document
561
+ * @param documentId - Document ID
562
+ * @returns Promise that resolves when the document is deleted
563
+ * @throws {ApiError} if the request fails
564
+ */
565
+ async deleteDocument(documentId) {
566
+ await this.#api.DELETE("/documents/{documentId}", {
567
+ params: { path: { documentId } }
568
+ });
569
+ }
570
+ };
571
+
572
+ // src/services/files.ts
573
+ var Files = class {
574
+ #api;
575
+ constructor(api) {
576
+ this.#api = api;
577
+ }
578
+ /**
579
+ * Upload one or more files to a workspace
580
+ * @param workspaceId - Workspace ID
581
+ * @param files - File or array of files to upload
582
+ * @returns Promise that resolves with the uploaded file metadata
583
+ * @throws {ApiError} if the request fails
584
+ */
585
+ async uploadFiles(workspaceId, files) {
586
+ const formData = new FormData();
587
+ const fileArray = Array.isArray(files) ? files : [files];
588
+ for (const file of fileArray) {
589
+ const name = file instanceof File ? file.name : "file";
590
+ formData.append("files", file, name);
239
591
  }
240
- if (this.statusCode >= 500) {
241
- return 1e3;
242
- }
243
- return 1e3;
592
+ const { data } = await this.#api.POST("/workspaces/{workspaceId}/files/", {
593
+ params: { path: { workspaceId } },
594
+ // Schema types multipart as unknown[], but openapi-fetch needs FormData.
595
+ body: formData,
596
+ bodySerializer: (formData2) => formData2,
597
+ // Remove Content-Type so browser sets multipart/form-data with boundary.
598
+ headers: { "Content-Type": null }
599
+ });
600
+ return data;
244
601
  }
245
602
  /**
246
- * Convert error to JSON representation
603
+ * List files in a workspace
604
+ * @param workspaceId - Workspace ID
605
+ * @param query - Optional query parameters (formats, limit, after)
606
+ * @returns Promise that resolves with a paginated list of files
607
+ * @throws {ApiError} if the request fails
247
608
  */
248
- toJSON() {
249
- return {
250
- name: this.name,
251
- message: this.message,
252
- context: `statusCode: ${this.statusCode}${this.requestId ? `, requestId: ${this.requestId}` : ""}${this.errorResponse ? `, errorResponse: ${JSON.stringify(this.errorResponse)}` : ""}`
253
- };
609
+ async listFiles(workspaceId, query) {
610
+ const { data } = await this.#api.GET("/workspaces/{workspaceId}/files/", {
611
+ params: { path: { workspaceId }, query }
612
+ });
613
+ return data;
614
+ }
615
+ /**
616
+ * Get file metadata by ID
617
+ * @param fileId - File ID
618
+ * @returns Promise that resolves with the file metadata
619
+ * @throws {ApiError} if the request fails
620
+ */
621
+ async getFile(fileId) {
622
+ const { data } = await this.#api.GET("/files/{fileId}", {
623
+ params: { path: { fileId } }
624
+ });
625
+ return data;
626
+ }
627
+ /**
628
+ * Download a file by ID
629
+ * @param fileId - File ID
630
+ * @returns Promise that resolves with the file response
631
+ * @throws {ApiError} if the request fails
632
+ */
633
+ async downloadFile(fileId) {
634
+ const { response } = await this.#api.GET("/files/{fileId}/content", {
635
+ params: { path: { fileId } },
636
+ parseAs: "stream"
637
+ });
638
+ return response;
639
+ }
640
+ /**
641
+ * Update a file's metadata
642
+ * @param fileId - File ID
643
+ * @param updates - File update request
644
+ * @returns Promise that resolves with the updated file
645
+ * @throws {ApiError} if the request fails
646
+ */
647
+ async updateFile(fileId, updates) {
648
+ const { data } = await this.#api.PATCH("/files/{fileId}", {
649
+ params: { path: { fileId } },
650
+ body: updates
651
+ });
652
+ return data;
653
+ }
654
+ /**
655
+ * Delete a file
656
+ * @param fileId - File ID
657
+ * @returns Promise that resolves when the file is deleted
658
+ * @throws {ApiError} if the request fails
659
+ */
660
+ async deleteFile(fileId) {
661
+ await this.#api.DELETE("/files/{fileId}", {
662
+ params: { path: { fileId } }
663
+ });
664
+ }
665
+ /**
666
+ * Download files as an archive
667
+ * @param workspaceId - Workspace ID
668
+ * @param request - Download request with format and optional file IDs
669
+ * @returns Promise that resolves with the archive response
670
+ * @throws {ApiError} if the request fails
671
+ */
672
+ async downloadFiles(workspaceId, request) {
673
+ const { response } = await this.#api.GET(
674
+ "/workspaces/{workspaceId}/files/batch",
675
+ {
676
+ params: { path: { workspaceId } },
677
+ body: request,
678
+ parseAs: "stream"
679
+ }
680
+ );
681
+ return response;
682
+ }
683
+ /**
684
+ * Delete multiple files
685
+ * @param workspaceId - Workspace ID
686
+ * @param request - Delete request with file IDs
687
+ * @returns Promise that resolves when the files are deleted
688
+ * @throws {ApiError} if the request fails
689
+ */
690
+ async deleteFiles(workspaceId, request) {
691
+ await this.#api.DELETE("/workspaces/{workspaceId}/files/batch", {
692
+ params: { path: { workspaceId } },
693
+ body: request
694
+ });
254
695
  }
255
696
  };
256
697
 
257
- // src/client.ts
258
- var Client = class _Client {
259
- #config;
260
- #openApiClient;
698
+ // src/services/integrations.ts
699
+ var Integrations = class {
700
+ #api;
701
+ constructor(api) {
702
+ this.#api = api;
703
+ }
261
704
  /**
262
- * Create a new Nvisy client instance
705
+ * List integrations for a workspace
706
+ * @param workspaceId - Workspace ID
707
+ * @param query - Optional pagination parameters (limit, after)
708
+ * @returns Promise that resolves with a paginated list of integrations
709
+ * @throws {ApiError} if the request fails
263
710
  */
264
- constructor(userConfig) {
265
- try {
266
- this.#validateConfig(userConfig);
267
- this.#config = resolveConfig(userConfig);
268
- } catch (error) {
269
- if (error instanceof ConfigError) {
270
- throw error;
711
+ async listIntegrations(workspaceId, query) {
712
+ const { data } = await this.#api.GET(
713
+ "/workspaces/{workspaceId}/integrations/",
714
+ {
715
+ params: { path: { workspaceId }, query }
271
716
  }
272
- throw ConfigError.invalidField(
273
- "config",
274
- `Configuration error: ${String(error)}`
275
- );
276
- }
277
- this.#openApiClient = createClient({
278
- baseUrl: this.#config.baseUrl,
279
- headers: {
280
- Authorization: `Bearer ${this.#config.apiKey}`,
281
- "Content-Type": "application/json",
282
- "User-Agent": this.#buildUserAgent(),
283
- ...this.#config.headers
717
+ );
718
+ return data;
719
+ }
720
+ /**
721
+ * Get integration details by ID
722
+ * @param integrationId - Integration ID
723
+ * @returns Promise that resolves with the integration details
724
+ * @throws {ApiError} if the request fails
725
+ */
726
+ async getIntegration(integrationId) {
727
+ const { data } = await this.#api.GET("/integrations/{integrationId}/", {
728
+ params: { path: { integrationId } }
729
+ });
730
+ return data;
731
+ }
732
+ /**
733
+ * Create a new integration
734
+ * @param workspaceId - Workspace ID
735
+ * @param integration - Integration creation request
736
+ * @returns Promise that resolves with the created integration
737
+ * @throws {ApiError} if the request fails
738
+ */
739
+ async createIntegration(workspaceId, integration) {
740
+ const { data } = await this.#api.POST(
741
+ "/workspaces/{workspaceId}/integrations/",
742
+ {
743
+ params: { path: { workspaceId } },
744
+ body: integration
284
745
  }
746
+ );
747
+ return data;
748
+ }
749
+ /**
750
+ * Update an existing integration
751
+ * @param integrationId - Integration ID
752
+ * @param updates - Integration update request
753
+ * @returns Promise that resolves with the updated integration
754
+ * @throws {ApiError} if the request fails
755
+ */
756
+ async updateIntegration(integrationId, updates) {
757
+ const { data } = await this.#api.PUT("/integrations/{integrationId}/", {
758
+ params: { path: { integrationId } },
759
+ body: updates
285
760
  });
761
+ return data;
286
762
  }
287
763
  /**
288
- * Create a new ClientBuilder for fluent configuration
764
+ * Update integration credentials
765
+ * @param integrationId - Integration ID
766
+ * @param credentials - New credentials
767
+ * @returns Promise that resolves with the updated integration
768
+ * @throws {ApiError} if the request fails
289
769
  */
290
- static builder() {
291
- return new ClientBuilder();
770
+ async updateIntegrationCredentials(integrationId, credentials) {
771
+ const { data } = await this.#api.PATCH(
772
+ "/integrations/{integrationId}/credentials/",
773
+ {
774
+ params: { path: { integrationId } },
775
+ body: credentials
776
+ }
777
+ );
778
+ return data;
292
779
  }
293
780
  /**
294
- * Create a client from environment variables
781
+ * Delete an integration
782
+ * @param integrationId - Integration ID
783
+ * @returns Promise that resolves when the integration is deleted
784
+ * @throws {ApiError} if the request fails
295
785
  */
296
- static fromEnvironment() {
297
- return ClientBuilder.fromEnvironment().build();
786
+ async deleteIntegration(integrationId) {
787
+ await this.#api.DELETE("/integrations/{integrationId}/", {
788
+ params: { path: { integrationId } }
789
+ });
790
+ }
791
+ };
792
+
793
+ // src/services/invites.ts
794
+ var Invites = class {
795
+ #api;
796
+ constructor(api) {
797
+ this.#api = api;
298
798
  }
299
799
  /**
300
- * Get the current configuration (readonly copy)
800
+ * List all invitations for a workspace
801
+ * @param workspaceId - Workspace ID
802
+ * @param query - Optional query parameters (role, sortBy, order, limit, after)
803
+ * @returns Promise that resolves with a paginated list of invitations
804
+ * @throws {ApiError} if the request fails
301
805
  */
302
- getConfig() {
303
- return Object.freeze({ ...this.#config });
806
+ async listInvites(workspaceId, query) {
807
+ const { data } = await this.#api.GET("/workspaces/{workspaceId}/invites/", {
808
+ params: { path: { workspaceId }, query }
809
+ });
810
+ return data;
304
811
  }
305
812
  /**
306
- * Get the underlying openapi-fetch client for advanced usage
813
+ * Send an invitation to join a workspace
814
+ * @param workspaceId - Workspace ID
815
+ * @param invite - Invitation request
816
+ * @returns Promise that resolves with the created invitation
817
+ * @throws {ApiError} if the request fails
307
818
  */
308
- getOpenApiClient() {
309
- return this.#openApiClient;
819
+ async sendInvite(workspaceId, invite) {
820
+ const { data } = await this.#api.POST(
821
+ "/workspaces/{workspaceId}/invites/",
822
+ {
823
+ params: { path: { workspaceId } },
824
+ body: invite
825
+ }
826
+ );
827
+ return data;
310
828
  }
311
829
  /**
312
- * Validate configuration by reusing ClientBuilder validation
830
+ * Cancel a pending invitation
831
+ * @param inviteId - Invite ID
832
+ * @returns Promise that resolves when the invitation is canceled
833
+ * @throws {ApiError} if the request fails
313
834
  */
314
- #validateConfig(config) {
315
- const builder = new ClientBuilder().withApiKey(config.apiKey);
316
- if (config.baseUrl !== void 0) {
317
- builder.withBaseUrl(config.baseUrl);
318
- }
319
- if (config.timeout !== void 0) {
320
- builder.withTimeout(config.timeout);
321
- }
322
- if (config.maxRetries !== void 0) {
323
- builder.withMaxRetries(config.maxRetries);
324
- }
325
- if (config.headers !== void 0) {
326
- builder.withHeaders(config.headers);
327
- }
835
+ async cancelInvite(inviteId) {
836
+ await this.#api.DELETE("/invites/{inviteId}/", {
837
+ params: { path: { inviteId } }
838
+ });
328
839
  }
329
840
  /**
330
- * Build user agent string
841
+ * Reply to an invitation (accept or decline)
842
+ * @param inviteId - Invite ID
843
+ * @param reply - Reply request
844
+ * @returns Promise that resolves with the updated invitation
845
+ * @throws {ApiError} if the request fails
331
846
  */
332
- #buildUserAgent() {
333
- const sdkVersion = "1.0.0";
334
- const nodeVersion = process.version;
335
- const platform = process.platform;
336
- return `@nvisy/sdk/${sdkVersion} (${platform}; Node.js ${nodeVersion})`;
847
+ async replyToInvite(inviteId, reply) {
848
+ const { data } = await this.#api.POST("/invites/{inviteId}/", {
849
+ params: { path: { inviteId } },
850
+ body: reply
851
+ });
852
+ return data;
337
853
  }
338
854
  /**
339
- * Create a new client with modified configuration
855
+ * Generate a shareable invite code for a workspace
856
+ * @param workspaceId - Workspace ID
857
+ * @param options - Invite code generation options
858
+ * @returns Promise that resolves with the generated invite code
859
+ * @throws {ApiError} if the request fails
340
860
  */
341
- withConfig(configChanges) {
342
- const newConfig = {
343
- apiKey: this.#config.apiKey,
344
- baseUrl: this.#config.baseUrl,
345
- timeout: this.#config.timeout,
346
- maxRetries: this.#config.maxRetries,
347
- headers: this.#config.headers,
348
- ...configChanges
349
- };
350
- return new _Client(newConfig);
861
+ async generateInviteCode(workspaceId, options) {
862
+ const { data } = await this.#api.POST(
863
+ "/workspaces/{workspaceId}/invites/code/",
864
+ {
865
+ params: { path: { workspaceId } },
866
+ body: options
867
+ }
868
+ );
869
+ return data;
870
+ }
871
+ /**
872
+ * Reply to an invite code (accept or decline)
873
+ * @param inviteCode - The invite code
874
+ * @param reply - Optional reply request (defaults to accept if not provided)
875
+ * @returns Promise that resolves with the member details if accepted, null if declined
876
+ * @throws {ApiError} if the request fails
877
+ */
878
+ async replyToInviteCode(inviteCode, reply) {
879
+ const { data } = await this.#api.POST("/invites/code/{inviteCode}/", {
880
+ params: { path: { inviteCode } },
881
+ body: reply ?? null
882
+ });
883
+ return data;
884
+ }
885
+ /**
886
+ * Preview an invite code without joining
887
+ * @param inviteCode - The invite code
888
+ * @returns Promise that resolves with the invite preview details
889
+ * @throws {ApiError} if the request fails
890
+ */
891
+ async previewInvite(inviteCode) {
892
+ const { data } = await this.#api.GET("/invites/code/{inviteCode}/", {
893
+ params: { path: { inviteCode } }
894
+ });
895
+ return data;
896
+ }
897
+ };
898
+
899
+ // src/services/members.ts
900
+ var Members = class {
901
+ #api;
902
+ constructor(api) {
903
+ this.#api = api;
351
904
  }
352
905
  /**
353
- * Create a new client with additional headers
906
+ * List members of a workspace
907
+ * @param workspaceId - Workspace ID
908
+ * @param query - Optional query parameters (role, has2fa, sortBy, order, limit, after)
909
+ * @returns Promise that resolves with a paginated list of members
910
+ * @throws {ApiError} if the request fails
354
911
  */
355
- withHeaders(additionalHeaders) {
356
- return this.withConfig({
357
- headers: { ...this.#config.headers, ...additionalHeaders }
912
+ async listMembers(workspaceId, query) {
913
+ const { data } = await this.#api.GET("/workspaces/{workspaceId}/members/", {
914
+ params: { path: { workspaceId }, query }
358
915
  });
916
+ return data;
917
+ }
918
+ /**
919
+ * Get member details by account ID
920
+ * @param workspaceId - Workspace ID
921
+ * @param accountId - Account ID
922
+ * @returns Promise that resolves with the member details
923
+ * @throws {ApiError} if the request fails
924
+ */
925
+ async getMember(workspaceId, accountId) {
926
+ const { data } = await this.#api.GET(
927
+ "/workspaces/{workspaceId}/members/{accountId}/",
928
+ {
929
+ params: {
930
+ path: { workspaceId, accountId }
931
+ }
932
+ }
933
+ );
934
+ return data;
359
935
  }
360
936
  /**
361
- * Create a new client with a different timeout
937
+ * Update a member's role
938
+ * @param workspaceId - Workspace ID
939
+ * @param accountId - Account ID
940
+ * @param updates - New role for the member
941
+ * @returns Promise that resolves with the updated member
942
+ * @throws {ApiError} if the request fails
362
943
  */
363
- withTimeout(timeoutMs) {
364
- return this.withConfig({ timeout: timeoutMs });
944
+ async updateMember(workspaceId, accountId, updates) {
945
+ const { data } = await this.#api.PATCH(
946
+ "/workspaces/{workspaceId}/members/{accountId}/role",
947
+ {
948
+ params: {
949
+ path: { workspaceId, accountId }
950
+ },
951
+ body: updates
952
+ }
953
+ );
954
+ return data;
365
955
  }
366
956
  /**
367
- * Create a new client with different retry settings
957
+ * Remove a member from a workspace
958
+ * @param workspaceId - Workspace ID
959
+ * @param accountId - Account ID
960
+ * @returns Promise that resolves when the member is removed
961
+ * @throws {ApiError} if the request fails
368
962
  */
369
- withMaxRetries(maxRetries) {
370
- return this.withConfig({ maxRetries });
963
+ async removeMember(workspaceId, accountId) {
964
+ await this.#api.DELETE("/workspaces/{workspaceId}/members/{accountId}/", {
965
+ params: { path: { workspaceId, accountId } }
966
+ });
967
+ }
968
+ /**
969
+ * Leave a workspace
970
+ * @param workspaceId - Workspace ID
971
+ * @returns Promise that resolves when the member has left
972
+ * @throws {ApiError} if the request fails
973
+ */
974
+ async leaveWorkspace(workspaceId) {
975
+ await this.#api.POST("/workspaces/{workspaceId}/members/leave", {
976
+ params: { path: { workspaceId } }
977
+ });
371
978
  }
372
979
  };
373
980
 
374
- // src/builder.ts
375
- var RESERVED_HEADERS = ["authorization", "content-type", "user-agent"];
376
- var ClientBuilder = class _ClientBuilder {
377
- #config = {};
981
+ // src/services/notifications.ts
982
+ var Notifications = class {
983
+ #api;
984
+ constructor(api) {
985
+ this.#api = api;
986
+ }
378
987
  /**
379
- * Create a ClientBuilder instance with an API key
988
+ * List notifications for the authenticated account
989
+ * @param query - Optional pagination parameters (limit, after)
990
+ * @returns Promise that resolves with a paginated list of notifications
991
+ * @throws {ApiError} if the request fails
380
992
  */
381
- static fromApiKey(apiKey) {
382
- return new _ClientBuilder().withApiKey(apiKey);
993
+ async listNotifications(query) {
994
+ const { data } = await this.#api.GET("/notifications/", {
995
+ params: { query }
996
+ });
997
+ return data;
383
998
  }
384
999
  /**
385
- * Create a ClientBuilder instance from environment variables
1000
+ * Get the unread notifications count for the authenticated account
1001
+ * @returns Promise that resolves with the unread status
1002
+ * @throws {ApiError} if the request fails
386
1003
  */
387
- static fromEnvironment() {
388
- const envConfig = loadConfigFromEnv();
389
- if (!envConfig.apiKey) {
390
- throw ConfigError.missingApiKey();
391
- }
392
- const builder = new _ClientBuilder().withApiKey(envConfig.apiKey);
393
- if (envConfig.baseUrl) {
394
- builder.withBaseUrl(envConfig.baseUrl);
395
- }
396
- if (envConfig.timeout) {
397
- builder.withTimeout(envConfig.timeout);
398
- }
399
- if (envConfig.maxRetries !== void 0) {
400
- builder.withMaxRetries(envConfig.maxRetries);
401
- }
402
- if (envConfig.headers) {
403
- builder.withHeaders(envConfig.headers);
404
- }
405
- return builder;
1004
+ async getUnreadNotificationsStatus() {
1005
+ const { data } = await this.#api.GET("/notifications/unread");
1006
+ return data;
1007
+ }
1008
+ };
1009
+
1010
+ // src/services/runs.ts
1011
+ var Runs = class {
1012
+ #api;
1013
+ constructor(api) {
1014
+ this.#api = api;
406
1015
  }
407
1016
  /**
408
- * Set the API key for authentication
1017
+ * List integration runs for a workspace
1018
+ * @param workspaceId - Workspace ID
1019
+ * @param query - Optional pagination parameters (limit, after)
1020
+ * @returns Promise that resolves with a paginated list of integration runs
1021
+ * @throws {ApiError} if the request fails
409
1022
  */
410
- withApiKey(apiKey) {
411
- this.#validateString("apiKey", apiKey);
412
- const trimmedKey = apiKey.trim();
413
- if (trimmedKey.length < 10) {
414
- throw ConfigError.invalidField(
415
- "apiKey",
416
- "must be at least 10 characters"
417
- );
418
- }
419
- if (!/^[a-zA-Z0-9_-]+$/.test(trimmedKey)) {
420
- throw ConfigError.invalidField("apiKey", "contains invalid characters");
421
- }
422
- this.#config.apiKey = trimmedKey;
423
- return this;
1023
+ async listRuns(workspaceId, query) {
1024
+ const { data } = await this.#api.GET("/workspaces/{workspaceId}/runs/", {
1025
+ params: { path: { workspaceId }, query }
1026
+ });
1027
+ return data;
424
1028
  }
425
1029
  /**
426
- * Set the base URL for the API
1030
+ * Get integration run details by ID
1031
+ * @param runId - Run ID
1032
+ * @returns Promise that resolves with the integration run details
1033
+ * @throws {ApiError} if the request fails
427
1034
  */
428
- withBaseUrl(baseUrl) {
429
- this.#validateString("baseUrl", baseUrl);
430
- this.#validateUrl(baseUrl);
431
- this.#config.baseUrl = baseUrl;
432
- return this;
1035
+ async getRun(runId) {
1036
+ const { data } = await this.#api.GET("/runs/{runId}", {
1037
+ params: { path: { runId } }
1038
+ });
1039
+ return data;
1040
+ }
1041
+ };
1042
+
1043
+ // src/services/status.ts
1044
+ var Status = class {
1045
+ #api;
1046
+ constructor(api) {
1047
+ this.#api = api;
433
1048
  }
434
1049
  /**
435
- * Set the request timeout in milliseconds
1050
+ * Check the health status of the API
1051
+ * @param options - Health check options
1052
+ * @returns Promise that resolves with the API health status
436
1053
  */
437
- withTimeout(timeoutMs) {
438
- this.#validateInteger("timeout", timeoutMs, 1e3, 3e5);
439
- this.#config.timeout = timeoutMs;
440
- return this;
1054
+ async checkHealth(options) {
1055
+ const { data, error } = await this.#api.GET("/health", {
1056
+ params: { path: { version: "v1" } },
1057
+ body: options ?? {}
1058
+ });
1059
+ return data ?? error;
1060
+ }
1061
+ };
1062
+
1063
+ // src/services/webhooks.ts
1064
+ var Webhooks = class {
1065
+ #api;
1066
+ constructor(api) {
1067
+ this.#api = api;
441
1068
  }
442
1069
  /**
443
- * Set the maximum number of retry attempts
1070
+ * List all webhooks in a workspace
1071
+ * @param workspaceId - Workspace ID
1072
+ * @param query - Optional pagination parameters (limit, after)
1073
+ * @returns Promise that resolves with a paginated list of webhooks
1074
+ * @throws {ApiError} if the request fails
444
1075
  */
445
- withMaxRetries(maxRetries) {
446
- this.#validateInteger("maxRetries", maxRetries, 0, 5);
447
- this.#config.maxRetries = maxRetries;
448
- return this;
1076
+ async listWebhooks(workspaceId, query) {
1077
+ const { data } = await this.#api.GET(
1078
+ "/workspaces/{workspaceId}/webhooks/",
1079
+ {
1080
+ params: { path: { workspaceId }, query }
1081
+ }
1082
+ );
1083
+ return data;
449
1084
  }
450
1085
  /**
451
- * Add a single custom header (merges with existing headers)
1086
+ * Get a specific webhook by ID
1087
+ * @param webhookId - Webhook ID
1088
+ * @returns Promise that resolves with the webhook details
1089
+ * @throws {ApiError} if the request fails
452
1090
  */
453
- withHeader(name, value) {
454
- this.#validateSingleHeader(name, value);
455
- if (!this.#config.headers) {
456
- this.#config.headers = {};
457
- }
458
- this.#config.headers[name] = value;
459
- return this;
1091
+ async getWebhook(webhookId) {
1092
+ const { data } = await this.#api.GET("/webhooks/{webhookId}/", {
1093
+ params: { path: { webhookId } }
1094
+ });
1095
+ return data;
460
1096
  }
461
1097
  /**
462
- * Set custom headers (merges with existing headers)
1098
+ * Create a new webhook
1099
+ * @param workspaceId - Workspace ID
1100
+ * @param webhook - Webhook creation request
1101
+ * @returns Promise that resolves with the created webhook
1102
+ * @throws {ApiError} if the request fails
463
1103
  */
464
- withHeaders(headers) {
465
- if (!headers || typeof headers !== "object" || Array.isArray(headers)) {
466
- throw ConfigError.invalidField("headers", "must be a valid object");
467
- }
468
- for (const [name, value] of Object.entries(headers)) {
469
- this.withHeader(name, value);
470
- }
471
- return this;
1104
+ async createWebhook(workspaceId, webhook) {
1105
+ const { data } = await this.#api.POST(
1106
+ "/workspaces/{workspaceId}/webhooks/",
1107
+ {
1108
+ params: { path: { workspaceId } },
1109
+ body: webhook
1110
+ }
1111
+ );
1112
+ return data;
472
1113
  }
473
1114
  /**
474
- * Build and return the configured client instance
1115
+ * Update an existing webhook
1116
+ * @param webhookId - Webhook ID
1117
+ * @param updates - Webhook update request
1118
+ * @returns Promise that resolves with the updated webhook
1119
+ * @throws {ApiError} if the request fails
475
1120
  */
476
- build() {
477
- if (!this.#config.apiKey) {
478
- throw ConfigError.missingApiKey();
479
- }
480
- return new Client(this.#config);
1121
+ async updateWebhook(webhookId, updates) {
1122
+ const { data } = await this.#api.PUT("/webhooks/{webhookId}/", {
1123
+ params: { path: { webhookId } },
1124
+ body: updates
1125
+ });
1126
+ return data;
481
1127
  }
482
1128
  /**
483
- * Get the current configuration (for debugging/testing)
1129
+ * Delete a webhook
1130
+ * @param webhookId - Webhook ID
1131
+ * @returns Promise that resolves when the webhook is deleted
1132
+ * @throws {ApiError} if the request fails
484
1133
  */
485
- getConfig() {
486
- return { ...this.#config };
1134
+ async deleteWebhook(webhookId) {
1135
+ await this.#api.DELETE("/webhooks/{webhookId}/", {
1136
+ params: { path: { webhookId } }
1137
+ });
487
1138
  }
488
1139
  /**
489
- * Validate string field
1140
+ * Test a webhook by sending a test payload
1141
+ * @param webhookId - Webhook ID
1142
+ * @param options - Test webhook options
1143
+ * @returns Promise that resolves with the test result
1144
+ * @throws {ApiError} if the request fails
490
1145
  */
491
- #validateString(fieldName, value) {
492
- if (!value || typeof value !== "string" || value.trim().length === 0) {
493
- throw ConfigError.invalidField(fieldName, "must be a non-empty string");
494
- }
1146
+ async testWebhook(webhookId, options) {
1147
+ const { data } = await this.#api.POST("/webhooks/{webhookId}/test/", {
1148
+ params: { path: { webhookId } },
1149
+ body: options ?? {}
1150
+ });
1151
+ return data;
1152
+ }
1153
+ };
1154
+
1155
+ // src/services/workspaces.ts
1156
+ var Workspaces = class {
1157
+ #api;
1158
+ constructor(api) {
1159
+ this.#api = api;
495
1160
  }
496
1161
  /**
497
- * Validate integer field with range
1162
+ * List all workspaces
1163
+ * @param query - Optional pagination parameters (limit, after)
1164
+ * @returns Promise that resolves with a paginated list of workspaces
1165
+ * @throws {ApiError} if the request fails
498
1166
  */
499
- #validateInteger(fieldName, value, min, max) {
500
- if (!Number.isInteger(value) || value < min) {
501
- throw ConfigError.invalidField(fieldName, `must be an integer >= ${min}`);
502
- }
503
- if (value > max) {
504
- throw ConfigError.invalidField(fieldName, `must not exceed ${max}`);
505
- }
1167
+ async listWorkspaces(query) {
1168
+ const { data } = await this.#api.GET("/workspaces/", {
1169
+ params: { query }
1170
+ });
1171
+ return data;
506
1172
  }
507
1173
  /**
508
- * Validate URL format
1174
+ * Get workspace details by ID
1175
+ * @param workspaceId - Workspace ID
1176
+ * @returns Promise that resolves with the workspace details
1177
+ * @throws {ApiError} if the request fails
509
1178
  */
510
- #validateUrl(baseUrl) {
511
- let url;
512
- try {
513
- url = new URL(baseUrl);
514
- } catch {
515
- throw ConfigError.invalidField("baseUrl", "must be a valid URL");
516
- }
517
- const allowedProtocols = ["https:", "http:"];
518
- if (!allowedProtocols.includes(url.protocol)) {
519
- throw ConfigError.invalidField(
520
- "baseUrl",
521
- `protocol must be one of: ${allowedProtocols.join(", ")}`
522
- );
523
- }
1179
+ async getWorkspace(workspaceId) {
1180
+ const { data } = await this.#api.GET("/workspaces/{workspaceId}/", {
1181
+ params: { path: { workspaceId } }
1182
+ });
1183
+ return data;
524
1184
  }
525
1185
  /**
526
- * Validate single header name and value
1186
+ * Create a new workspace
1187
+ * @param workspace - Workspace creation request
1188
+ * @returns Promise that resolves with the created workspace
1189
+ * @throws {ApiError} if the request fails
527
1190
  */
528
- #validateSingleHeader(name, value) {
529
- if (!name || typeof name !== "string" || name.trim().length === 0) {
530
- throw ConfigError.invalidField(
531
- "header name",
532
- "must be a non-empty string"
533
- );
1191
+ async createWorkspace(workspace) {
1192
+ const { data } = await this.#api.POST("/workspaces/", {
1193
+ body: workspace
1194
+ });
1195
+ return data;
1196
+ }
1197
+ /**
1198
+ * Update an existing workspace
1199
+ * @param workspaceId - Workspace ID
1200
+ * @param updates - Workspace update request
1201
+ * @returns Promise that resolves with the updated workspace
1202
+ * @throws {ApiError} if the request fails
1203
+ */
1204
+ async updateWorkspace(workspaceId, updates) {
1205
+ const { data } = await this.#api.PATCH("/workspaces/{workspaceId}/", {
1206
+ params: { path: { workspaceId } },
1207
+ body: updates
1208
+ });
1209
+ return data;
1210
+ }
1211
+ /**
1212
+ * Delete a workspace
1213
+ * @param workspaceId - Workspace ID
1214
+ * @returns Promise that resolves when the workspace is deleted
1215
+ * @throws {ApiError} if the request fails
1216
+ */
1217
+ async deleteWorkspace(workspaceId) {
1218
+ await this.#api.DELETE("/workspaces/{workspaceId}/", {
1219
+ params: { path: { workspaceId } }
1220
+ });
1221
+ }
1222
+ /**
1223
+ * Get notification settings for the authenticated user in a workspace
1224
+ * @param workspaceId - Workspace ID
1225
+ * @returns Promise that resolves with the notification settings
1226
+ * @throws {ApiError} if the request fails
1227
+ */
1228
+ async getNotificationSettings(workspaceId) {
1229
+ const { data } = await this.#api.GET(
1230
+ "/workspaces/{workspaceId}/notifications",
1231
+ {
1232
+ params: { path: { workspaceId } }
1233
+ }
1234
+ );
1235
+ return data;
1236
+ }
1237
+ /**
1238
+ * Update notification settings for the authenticated user in a workspace
1239
+ * @param workspaceId - Workspace ID
1240
+ * @param settings - Notification settings update request
1241
+ * @returns Promise that resolves with the updated notification settings
1242
+ * @throws {ApiError} if the request fails
1243
+ */
1244
+ async updateNotificationSettings(workspaceId, settings) {
1245
+ const { data } = await this.#api.PATCH(
1246
+ "/workspaces/{workspaceId}/notifications",
1247
+ {
1248
+ params: { path: { workspaceId } },
1249
+ body: settings
1250
+ }
1251
+ );
1252
+ return data;
1253
+ }
1254
+ };
1255
+
1256
+ // src/client.ts
1257
+ var Nvisy = class _Nvisy {
1258
+ /**
1259
+ * The resolved client configuration with defaults applied.
1260
+ * @internal
1261
+ */
1262
+ #config;
1263
+ /**
1264
+ * The underlying openapi-fetch client instance.
1265
+ * @internal
1266
+ */
1267
+ #api;
1268
+ /**
1269
+ * Creates a new Nvisy client instance.
1270
+ *
1271
+ * @param config - Configuration options with required `apiToken`
1272
+ * @throws {NvisyError} If the API token is invalid
1273
+ *
1274
+ * @example
1275
+ * ```typescript
1276
+ * const nvisy = new Nvisy({
1277
+ * apiToken: "your-api-token",
1278
+ * baseUrl: "https://custom.api.nvisy.com",
1279
+ * });
1280
+ * const account = await nvisy.account.getAccount();
1281
+ * ```
1282
+ */
1283
+ constructor(config) {
1284
+ const validatedToken = this.#validateApiToken(config.apiToken);
1285
+ this.#config = {
1286
+ apiToken: validatedToken,
1287
+ baseUrl: config.baseUrl ?? DEFAULTS.BASE_URL,
1288
+ headers: config.headers ?? {},
1289
+ userAgent: config.userAgent ?? DEFAULTS.USER_AGENT,
1290
+ withLogging: config.withLogging ?? false
1291
+ };
1292
+ this.#api = this.#createApiClient();
1293
+ }
1294
+ /**
1295
+ * Creates and configures the underlying openapi-fetch client.
1296
+ *
1297
+ * @returns A configured ApiClient instance
1298
+ * @internal
1299
+ */
1300
+ #createApiClient() {
1301
+ const headers = {
1302
+ "Content-Type": "application/json",
1303
+ "User-Agent": this.#config.userAgent,
1304
+ Authorization: `Bearer ${this.#config.apiToken}`,
1305
+ ...this.#config.headers
1306
+ };
1307
+ const api = createClient({
1308
+ baseUrl: this.#config.baseUrl,
1309
+ headers
1310
+ });
1311
+ if (this.#config.withLogging) {
1312
+ api.use(createLoggingMiddleware());
534
1313
  }
535
- if (typeof value !== "string") {
536
- throw ConfigError.invalidField("header value", "must be a string");
1314
+ api.use(errorMiddleware);
1315
+ return api;
1316
+ }
1317
+ /**
1318
+ * Validates an API token format.
1319
+ *
1320
+ * @param apiToken - The API token to validate
1321
+ * @returns The trimmed API token if valid
1322
+ * @throws {NvisyError} If the API token is invalid
1323
+ * @internal
1324
+ */
1325
+ #validateApiToken(apiToken) {
1326
+ if (typeof apiToken !== "string" || apiToken.trim().length === 0) {
1327
+ throw new NvisyError("API token must be a non-empty string");
537
1328
  }
538
- if (!/^[a-zA-Z0-9!#$%&'*+\-.^_`|~]+$/.test(name)) {
539
- throw ConfigError.invalidField(
540
- "header name",
541
- `invalid header name: ${name}`
542
- );
1329
+ const trimmedToken = apiToken.trim();
1330
+ if (trimmedToken.length < 10) {
1331
+ throw new NvisyError("API token must be at least 10 characters");
543
1332
  }
544
- if (RESERVED_HEADERS.includes(name.toLowerCase())) {
545
- throw ConfigError.invalidField(
546
- "header name",
547
- `header "${name}" is reserved and cannot be overridden`
548
- );
1333
+ if (!/^[a-zA-Z0-9_.\-]+$/.test(trimmedToken)) {
1334
+ throw new NvisyError("API token contains invalid characters");
549
1335
  }
1336
+ return trimmedToken;
1337
+ }
1338
+ /**
1339
+ * Creates a new client with a different API token.
1340
+ *
1341
+ * Returns a new client instance with the new token. The original client
1342
+ * remains unchanged. All other configuration (base URL, headers, etc.)
1343
+ * is preserved in the new client.
1344
+ *
1345
+ * @param apiToken - The new API token
1346
+ * @returns A new Nvisy instance with the new token
1347
+ * @throws {NvisyError} If the API token is invalid
1348
+ *
1349
+ * @example
1350
+ * ```typescript
1351
+ * const nvisy = new Nvisy({ apiToken: "original-token" });
1352
+ * const newNvisy = nvisy.withApiToken("new-token");
1353
+ *
1354
+ * // newNvisy uses the new token
1355
+ * // nvisy still uses the original token
1356
+ * ```
1357
+ */
1358
+ withApiToken(apiToken) {
1359
+ return new _Nvisy({ ...this.#config, apiToken });
1360
+ }
1361
+ /**
1362
+ * The base URL used for API requests.
1363
+ *
1364
+ * @returns The configured base URL
1365
+ */
1366
+ get baseUrl() {
1367
+ return this.#config.baseUrl;
1368
+ }
1369
+ /**
1370
+ * The underlying openapi-fetch client for direct API access.
1371
+ *
1372
+ * Use this for advanced scenarios where you need direct access to the
1373
+ * HTTP client, such as calling endpoints not covered by the service classes.
1374
+ *
1375
+ * @returns The configured ApiClient instance
1376
+ */
1377
+ get api() {
1378
+ return this.#api;
1379
+ }
1380
+ /**
1381
+ * Service for authentication operations (login, signup, logout).
1382
+ */
1383
+ get auth() {
1384
+ return new Auth(this.#api);
1385
+ }
1386
+ /**
1387
+ * Service for API status and health checks.
1388
+ */
1389
+ get status() {
1390
+ return new Status(this.#api);
1391
+ }
1392
+ /**
1393
+ * Service for managing the authenticated user's account.
1394
+ */
1395
+ get account() {
1396
+ return new Account(this.#api);
1397
+ }
1398
+ /**
1399
+ * Service for viewing workspace activities.
1400
+ */
1401
+ get activities() {
1402
+ return new Activities(this.#api);
1403
+ }
1404
+ /**
1405
+ * Service for managing file annotations.
1406
+ */
1407
+ get annotations() {
1408
+ return new Annotations(this.#api);
1409
+ }
1410
+ /**
1411
+ * Service for managing API tokens.
1412
+ */
1413
+ get apiTokens() {
1414
+ return new ApiTokens(this.#api);
1415
+ }
1416
+ /**
1417
+ * Service for managing file comments.
1418
+ */
1419
+ get comments() {
1420
+ return new Comments(this.#api);
1421
+ }
1422
+ /**
1423
+ * Service for document operations.
1424
+ */
1425
+ get documents() {
1426
+ return new Documents(this.#api);
1427
+ }
1428
+ /**
1429
+ * Service for file operations (upload, download, delete).
1430
+ */
1431
+ get files() {
1432
+ return new Files(this.#api);
1433
+ }
1434
+ /**
1435
+ * Service for managing integrations.
1436
+ */
1437
+ get integrations() {
1438
+ return new Integrations(this.#api);
1439
+ }
1440
+ /**
1441
+ * Service for managing workspace invitations.
1442
+ */
1443
+ get invites() {
1444
+ return new Invites(this.#api);
1445
+ }
1446
+ /**
1447
+ * Service for managing workspace members.
1448
+ */
1449
+ get members() {
1450
+ return new Members(this.#api);
1451
+ }
1452
+ /**
1453
+ * Service for managing notifications.
1454
+ */
1455
+ get notifications() {
1456
+ return new Notifications(this.#api);
1457
+ }
1458
+ /**
1459
+ * Service for managing processing runs.
1460
+ */
1461
+ get runs() {
1462
+ return new Runs(this.#api);
1463
+ }
1464
+ /**
1465
+ * Service for managing webhooks.
1466
+ */
1467
+ get webhooks() {
1468
+ return new Webhooks(this.#api);
1469
+ }
1470
+ /**
1471
+ * Service for managing workspaces.
1472
+ */
1473
+ get workspaces() {
1474
+ return new Workspaces(this.#api);
550
1475
  }
551
1476
  };
552
1477
 
553
- export { ApiError, Client, ClientBuilder, ClientError, ConfigError, NetworkError, getEnvironmentVariables, loadConfigFromEnv, resolveConfig };
1478
+ export { DEFAULTS, Nvisy, NvisyApiError, NvisyError, VERSION };
554
1479
  //# sourceMappingURL=index.js.map
555
1480
  //# sourceMappingURL=index.js.map