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