@air/api-rest 0.1.1

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 ADDED
@@ -0,0 +1,570 @@
1
+ // src/api.ts
2
+ import { AirBase } from "@air/api-core";
3
+
4
+ // src/resources/assets.ts
5
+ var Assets = class {
6
+ constructor(client) {
7
+ this.client = client;
8
+ }
9
+ list(params = {}) {
10
+ const query = {
11
+ limit: params.limit,
12
+ cursor: params.cursor,
13
+ parentBoardId: params.parentBoardId,
14
+ search: params.search
15
+ };
16
+ if (params.includeNestedAssets !== void 0) {
17
+ query.includeNestedAssets = params.includeNestedAssets;
18
+ }
19
+ if (params.tag) {
20
+ query.tag = params.tag;
21
+ }
22
+ if (params.customField) {
23
+ query.customField = params.customField;
24
+ }
25
+ if (params["createdAt[gte]"]) {
26
+ query["createdAt[gte]"] = params["createdAt[gte]"];
27
+ }
28
+ if (params["createdAt[lte]"]) {
29
+ query["createdAt[lte]"] = params["createdAt[lte]"];
30
+ }
31
+ return this.client.requestCursorPage({ method: "GET", path: "/assets", query }, query);
32
+ }
33
+ async get(assetId) {
34
+ return this.client.request({
35
+ method: "GET",
36
+ path: `/assets/${assetId}`
37
+ });
38
+ }
39
+ async delete(assetId) {
40
+ return this.client.request({
41
+ method: "DELETE",
42
+ path: `/assets/${assetId}`
43
+ });
44
+ }
45
+ async setCustomField(assetId, customFieldId, params) {
46
+ return this.client.request({
47
+ method: "PUT",
48
+ path: `/assets/${assetId}/customfields/${customFieldId}`,
49
+ body: params
50
+ });
51
+ }
52
+ async listVersions(assetId) {
53
+ return this.client.request({
54
+ method: "GET",
55
+ path: `/assets/${assetId}/versions`
56
+ });
57
+ }
58
+ async getVersion(assetId, versionId) {
59
+ return this.client.request({
60
+ method: "GET",
61
+ path: `/assets/${assetId}/versions/${versionId}`
62
+ });
63
+ }
64
+ async updateVersion(assetId, versionId, params) {
65
+ return this.client.request({
66
+ method: "PATCH",
67
+ path: `/assets/${assetId}/versions/${versionId}`,
68
+ body: params
69
+ });
70
+ }
71
+ async getVersionDownloadUrl(assetId, versionId) {
72
+ return this.client.request({
73
+ method: "GET",
74
+ path: `/assets/${assetId}/versions/${versionId}/download`
75
+ });
76
+ }
77
+ async addVersionTag(assetId, versionId, params) {
78
+ return this.client.request({
79
+ method: "POST",
80
+ path: `/assets/${assetId}/versions/${versionId}/tags`,
81
+ body: params
82
+ });
83
+ }
84
+ async removeVersionTag(assetId, versionId, tagId) {
85
+ return this.client.request({
86
+ method: "DELETE",
87
+ path: `/assets/${assetId}/versions/${versionId}/tags/${tagId}`
88
+ });
89
+ }
90
+ listBoards(assetId, params = {}) {
91
+ const query = {
92
+ limit: params.limit,
93
+ cursor: params.cursor
94
+ };
95
+ if (params.includeCustomFields !== void 0) {
96
+ query.includeCustomFields = params.includeCustomFields;
97
+ }
98
+ return this.client.requestCursorPage(
99
+ { method: "GET", path: `/assets/${assetId}/boards`, query },
100
+ query
101
+ );
102
+ }
103
+ };
104
+
105
+ // src/resources/boards.ts
106
+ var Boards = class {
107
+ constructor(client) {
108
+ this.client = client;
109
+ }
110
+ list(params = {}) {
111
+ const query = {
112
+ limit: params.limit,
113
+ cursor: params.cursor,
114
+ name: params.name,
115
+ parentBoardId: params.parentBoardId
116
+ };
117
+ if (params.customField) {
118
+ query.customField = params.customField;
119
+ }
120
+ return this.client.requestCursorPage({ method: "GET", path: "/boards", query }, query);
121
+ }
122
+ async get(boardId) {
123
+ return this.client.request({
124
+ method: "GET",
125
+ path: `/boards/${boardId}`
126
+ });
127
+ }
128
+ async create(params) {
129
+ return this.client.request({
130
+ method: "POST",
131
+ path: "/boards",
132
+ body: params
133
+ });
134
+ }
135
+ async update(boardId, params) {
136
+ return this.client.request({
137
+ method: "PATCH",
138
+ path: `/boards/${boardId}`,
139
+ body: params
140
+ });
141
+ }
142
+ async delete(boardId) {
143
+ return this.client.request({
144
+ method: "DELETE",
145
+ path: `/boards/${boardId}`
146
+ });
147
+ }
148
+ async addAssets(boardId, params) {
149
+ return this.client.request({
150
+ method: "POST",
151
+ path: `/boards/${boardId}/assets`,
152
+ body: params
153
+ });
154
+ }
155
+ async removeAsset(boardId, assetId) {
156
+ return this.client.request({
157
+ method: "DELETE",
158
+ path: `/boards/${boardId}/assets/${assetId}`
159
+ });
160
+ }
161
+ async setCustomField(boardId, customFieldId, params) {
162
+ return this.client.request({
163
+ method: "PUT",
164
+ path: `/boards/${boardId}/customFields/${customFieldId}`,
165
+ body: params
166
+ });
167
+ }
168
+ async addGuest(boardId, params) {
169
+ return this.client.request({
170
+ method: "POST",
171
+ path: `/boards/${boardId}/guests`,
172
+ body: params
173
+ });
174
+ }
175
+ async listGuests(boardId, params = {}) {
176
+ const response = await this.client.request({
177
+ method: "GET",
178
+ path: `/boards/${boardId}/guests`,
179
+ query: params.email ? { email: params.email } : void 0
180
+ });
181
+ return response.data;
182
+ }
183
+ async updateGuest(boardId, guestId, params) {
184
+ return this.client.request({
185
+ method: "PATCH",
186
+ path: `/boards/${boardId}/guests/${guestId}`,
187
+ body: params
188
+ });
189
+ }
190
+ async removeGuest(boardId, guestId) {
191
+ return this.client.request({
192
+ method: "DELETE",
193
+ path: `/boards/${boardId}/guests/${guestId}`
194
+ });
195
+ }
196
+ };
197
+
198
+ // src/resources/custom-fields.ts
199
+ var CustomFields = class {
200
+ constructor(client) {
201
+ this.client = client;
202
+ }
203
+ list(params = {}) {
204
+ return this.client.requestCursorPage(
205
+ {
206
+ method: "GET",
207
+ path: "/customfields",
208
+ query: params
209
+ },
210
+ params
211
+ );
212
+ }
213
+ async get(customFieldId) {
214
+ return this.client.request({
215
+ method: "GET",
216
+ path: `/customfields/${customFieldId}`
217
+ });
218
+ }
219
+ async create(params) {
220
+ return this.client.request({
221
+ method: "POST",
222
+ path: "/customfields",
223
+ body: params
224
+ });
225
+ }
226
+ async update(customFieldId, params) {
227
+ return this.client.request({
228
+ method: "PATCH",
229
+ path: `/customfields/${customFieldId}`,
230
+ body: params
231
+ });
232
+ }
233
+ async delete(customFieldId) {
234
+ return this.client.request({
235
+ method: "DELETE",
236
+ path: `/customfields/${customFieldId}`
237
+ });
238
+ }
239
+ async createValue(customFieldId, params) {
240
+ return this.client.request({
241
+ method: "POST",
242
+ path: `/customfields/${customFieldId}/values`,
243
+ body: params
244
+ });
245
+ }
246
+ async updateValue(customFieldId, valueId, params) {
247
+ return this.client.request({
248
+ method: "PATCH",
249
+ path: `/customfields/${customFieldId}/values/${valueId}`,
250
+ body: params
251
+ });
252
+ }
253
+ async deleteValue(customFieldId, valueId) {
254
+ return this.client.request({
255
+ method: "DELETE",
256
+ path: `/customfields/${customFieldId}/values/${valueId}`
257
+ });
258
+ }
259
+ };
260
+
261
+ // src/resources/imports.ts
262
+ var Imports = class {
263
+ constructor(client) {
264
+ this.client = client;
265
+ }
266
+ async create(params) {
267
+ return this.client.request({
268
+ method: "POST",
269
+ path: "/imports",
270
+ body: params
271
+ });
272
+ }
273
+ async getStatus(importId) {
274
+ return this.client.request({
275
+ method: "GET",
276
+ path: `/imports/${importId}/status`
277
+ });
278
+ }
279
+ };
280
+
281
+ // src/resources/roles.ts
282
+ var Roles = class {
283
+ constructor(client) {
284
+ this.client = client;
285
+ }
286
+ async list(params = { type: "guest" }) {
287
+ const response = await this.client.request({
288
+ method: "GET",
289
+ path: "/roles",
290
+ query: { type: params.type }
291
+ });
292
+ return response.data;
293
+ }
294
+ };
295
+
296
+ // src/resources/tags.ts
297
+ var Tags = class {
298
+ constructor(client) {
299
+ this.client = client;
300
+ }
301
+ list(params = {}) {
302
+ return this.client.requestCursorPage(
303
+ {
304
+ method: "GET",
305
+ path: "/tags",
306
+ query: params
307
+ },
308
+ params
309
+ );
310
+ }
311
+ async get(tagId) {
312
+ return this.client.request({
313
+ method: "GET",
314
+ path: `/tags/${tagId}`
315
+ });
316
+ }
317
+ async create(params) {
318
+ return this.client.request({
319
+ method: "POST",
320
+ path: "/tags",
321
+ body: params
322
+ });
323
+ }
324
+ async update(tagId, params) {
325
+ return this.client.request({
326
+ method: "PATCH",
327
+ path: `/tags/${tagId}`,
328
+ body: params
329
+ });
330
+ }
331
+ async delete(tagId) {
332
+ return this.client.request({
333
+ method: "DELETE",
334
+ path: `/tags/${tagId}`
335
+ });
336
+ }
337
+ };
338
+
339
+ // src/resources/uploads.ts
340
+ var DEFAULT_PART_SIZE = 100 * 1024 * 1024;
341
+ var Uploads = class {
342
+ constructor(client) {
343
+ this.client = client;
344
+ }
345
+ async create(params) {
346
+ return this.client.request({
347
+ method: "POST",
348
+ path: "/uploads",
349
+ body: params
350
+ });
351
+ }
352
+ async getPartUploadUrl(params) {
353
+ return this.client.request({
354
+ method: "POST",
355
+ path: "/uploads/uploadPart",
356
+ body: params
357
+ });
358
+ }
359
+ async completeMultipart(params) {
360
+ return this.client.request({
361
+ method: "POST",
362
+ path: "/uploads/completeMultipart",
363
+ body: params
364
+ });
365
+ }
366
+ async uploadFile(params, options = {}) {
367
+ const { fileData, fileName, ext, mime, size } = await resolveFileData(params);
368
+ const uploadResponse = await this.create({
369
+ fileName,
370
+ ext,
371
+ size,
372
+ mime,
373
+ parentBoardId: options.parentBoardId,
374
+ assetId: options.assetId,
375
+ customFields: options.customFields,
376
+ tags: options.tags
377
+ });
378
+ if (isSmallUpload(uploadResponse)) {
379
+ await this._uploadSmallFile(uploadResponse.uploadUrl, fileData, options.onProgress, size);
380
+ return { assetId: uploadResponse.assetId, versionId: uploadResponse.versionId };
381
+ }
382
+ await this._uploadLargeFile(uploadResponse, fileData, size, options.onProgress);
383
+ return { assetId: uploadResponse.assetId, versionId: uploadResponse.versionId };
384
+ }
385
+ async _uploadSmallFile(url, data, onProgress, totalSize) {
386
+ const fetchFn = this.client.fetchFn;
387
+ const response = await fetchFn(url, {
388
+ method: "PUT",
389
+ body: data
390
+ });
391
+ if (!response.ok) {
392
+ throw new Error(`Upload failed with status ${response.status}`);
393
+ }
394
+ if (onProgress && totalSize) {
395
+ onProgress({ percentage: 100, uploadedBytes: totalSize, totalBytes: totalSize });
396
+ }
397
+ }
398
+ async _uploadLargeFile(uploadInfo, data, totalSize, onProgress) {
399
+ const { multiPartUploadId, key } = uploadInfo;
400
+ const buffer = data instanceof Buffer ? data : Buffer.from(data);
401
+ const partCount = Math.ceil(totalSize / DEFAULT_PART_SIZE);
402
+ const parts = [];
403
+ let uploadedBytes = 0;
404
+ const fetchFn = this.client.fetchFn;
405
+ for (let i = 0; i < partCount; i++) {
406
+ const partNumber = i + 1;
407
+ const start = i * DEFAULT_PART_SIZE;
408
+ const end = Math.min(start + DEFAULT_PART_SIZE, totalSize);
409
+ const chunk = buffer.subarray(start, end);
410
+ const { url } = await this.getPartUploadUrl({
411
+ multiPartUploadId,
412
+ key,
413
+ partNumber
414
+ });
415
+ const response = await fetchFn(url, {
416
+ method: "PUT",
417
+ body: chunk
418
+ });
419
+ if (!response.ok) {
420
+ throw new Error(`Part ${partNumber} upload failed with status ${response.status}`);
421
+ }
422
+ const etag = response.headers.get("etag");
423
+ if (!etag) {
424
+ throw new Error(`Part ${partNumber} upload did not return an ETag`);
425
+ }
426
+ parts.push({ etag, partNumber });
427
+ uploadedBytes += chunk.byteLength;
428
+ if (onProgress) {
429
+ onProgress({
430
+ percentage: Math.round(uploadedBytes / totalSize * 100),
431
+ uploadedBytes,
432
+ totalBytes: totalSize
433
+ });
434
+ }
435
+ }
436
+ await this.completeMultipart({ multiPartUploadId, key, parts });
437
+ }
438
+ };
439
+ function isSmallUpload(response) {
440
+ return "uploadUrl" in response;
441
+ }
442
+ async function resolveFileData(params) {
443
+ if (params.filePath) {
444
+ const fs = await import("fs");
445
+ const path = await import("path");
446
+ const fileData = fs.readFileSync(params.filePath);
447
+ const parsed = path.parse(params.filePath);
448
+ const fileName = params.fileName ?? parsed.name;
449
+ const ext = params.ext ?? parsed.ext.replace(/^\./, "");
450
+ const mime = params.mime ?? guessMime(ext);
451
+ return { fileData, fileName, ext, mime, size: fileData.byteLength };
452
+ }
453
+ if (params.buffer) {
454
+ const data = params.buffer instanceof Buffer ? params.buffer : Buffer.from(params.buffer);
455
+ if (!params.fileName || !params.ext) {
456
+ throw new Error("fileName and ext are required when uploading from a buffer");
457
+ }
458
+ const mime = params.mime ?? guessMime(params.ext);
459
+ return {
460
+ fileData: data,
461
+ fileName: params.fileName,
462
+ ext: params.ext,
463
+ mime,
464
+ size: data.byteLength
465
+ };
466
+ }
467
+ if (params.file) {
468
+ const arrayBuffer = await params.file.arrayBuffer();
469
+ const data = new Uint8Array(arrayBuffer);
470
+ const name = params.file.name;
471
+ const dotIndex = name.lastIndexOf(".");
472
+ const fileName = params.fileName ?? (dotIndex > 0 ? name.substring(0, dotIndex) : name);
473
+ const ext = params.ext ?? (dotIndex > 0 ? name.substring(dotIndex + 1) : "");
474
+ const mime = params.mime ?? (params.file.type || guessMime(ext));
475
+ return { fileData: data, fileName, ext, mime, size: data.byteLength };
476
+ }
477
+ throw new Error("One of filePath, buffer, or file must be provided");
478
+ }
479
+ function guessMime(ext) {
480
+ const mimeMap = {
481
+ jpg: "image/jpeg",
482
+ jpeg: "image/jpeg",
483
+ png: "image/png",
484
+ gif: "image/gif",
485
+ webp: "image/webp",
486
+ svg: "image/svg+xml",
487
+ mp4: "video/mp4",
488
+ mov: "video/quicktime",
489
+ avi: "video/x-msvideo",
490
+ webm: "video/webm",
491
+ mp3: "audio/mpeg",
492
+ wav: "audio/wav",
493
+ pdf: "application/pdf",
494
+ doc: "application/msword",
495
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
496
+ xls: "application/vnd.ms-excel",
497
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
498
+ ppt: "application/vnd.ms-powerpoint",
499
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
500
+ zip: "application/zip",
501
+ psd: "image/vnd.adobe.photoshop",
502
+ ai: "application/postscript",
503
+ eps: "application/postscript",
504
+ tif: "image/tiff",
505
+ tiff: "image/tiff",
506
+ bmp: "image/bmp"
507
+ };
508
+ return mimeMap[ext.toLowerCase()] ?? "application/octet-stream";
509
+ }
510
+
511
+ // src/api.ts
512
+ var AirApi = class extends AirBase {
513
+ assets;
514
+ boards;
515
+ tags;
516
+ customFields;
517
+ roles;
518
+ imports;
519
+ uploads;
520
+ constructor(options = {}) {
521
+ super(options);
522
+ this.assets = new Assets(this);
523
+ this.boards = new Boards(this);
524
+ this.tags = new Tags(this);
525
+ this.customFields = new CustomFields(this);
526
+ this.roles = new Roles(this);
527
+ this.imports = new Imports(this);
528
+ this.uploads = new Uploads(this);
529
+ }
530
+ };
531
+
532
+ // src/index.ts
533
+ import {
534
+ AirBase as AirBase2,
535
+ APIError,
536
+ AuthenticationError,
537
+ BadRequestError,
538
+ ConnectionError,
539
+ InternalServerError,
540
+ NotFoundError,
541
+ PermissionError,
542
+ RateLimitError,
543
+ TimeoutError,
544
+ CursorPage,
545
+ PagePromise,
546
+ VERSION
547
+ } from "@air/api-core";
548
+ export {
549
+ APIError,
550
+ AirApi,
551
+ AirBase2 as AirBase,
552
+ Assets,
553
+ AuthenticationError,
554
+ BadRequestError,
555
+ Boards,
556
+ ConnectionError,
557
+ CursorPage,
558
+ CustomFields,
559
+ Imports,
560
+ InternalServerError,
561
+ NotFoundError,
562
+ PagePromise,
563
+ PermissionError,
564
+ RateLimitError,
565
+ Roles,
566
+ Tags,
567
+ TimeoutError,
568
+ Uploads,
569
+ VERSION
570
+ };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@air/api-rest",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "description": "Typed REST client for Air API",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "prepublishOnly": "npm run build",
26
+ "build": "tsup",
27
+ "test": "vitest run",
28
+ "typecheck": "tsc -b",
29
+ "clean": "rm -rf dist tsconfig.tsbuildinfo"
30
+ },
31
+ "dependencies": {
32
+ "@air/api-core": "0.1.1"
33
+ },
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/AirLabsTeam/air-api-sdk.git",
43
+ "directory": "packages/rest"
44
+ },
45
+ "license": "UNLICENSED"
46
+ }