@bundleup/sdk 0.0.17 → 0.1.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/README.md CHANGED
@@ -1,4 +1,1085 @@
1
- # Official Bundle Up SDK Library
1
+ # BundleUp JavaScript SDK
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@bundleup/sdk.svg)](https://www.npmjs.com/package/@bundleup/sdk)
4
- [![Documentation](https://img.shields.io/badge/documentation-bundleup.io-green.svg)](https://bundleup.io/docs/sdk)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+
6
+ Official JavaScript/TypeScript SDK for the [BundleUp](https://bundleup.io) API. Connect to 100+ integrations with a single, unified API. Build once, integrate everywhere.
7
+
8
+ ## Table of Contents
9
+
10
+ - [Installation](#installation)
11
+ - [Requirements](#requirements)
12
+ - [Features](#features)
13
+ - [Quick Start](#quick-start)
14
+ - [Authentication](#authentication)
15
+ - [Core Concepts](#core-concepts)
16
+ - [API Reference](#api-reference)
17
+ - [Connections](#connections)
18
+ - [Integrations](#integrations)
19
+ - [Webhooks](#webhooks)
20
+ - [Proxy API](#proxy-api)
21
+ - [Unify API](#unify-api)
22
+ - [Error Handling](#error-handling)
23
+ - [Development](#development)
24
+ - [Contributing](#contributing)
25
+ - [License](#license)
26
+ - [Support](#support)
27
+
28
+ ## Installation
29
+
30
+ Install the SDK using your preferred package manager:
31
+
32
+ **npm:**
33
+
34
+ ```bash
35
+ npm install @bundleup/sdk
36
+ ```
37
+
38
+ **yarn:**
39
+
40
+ ```bash
41
+ yarn add @bundleup/sdk
42
+ ```
43
+
44
+ **pnpm:**
45
+
46
+ ```bash
47
+ pnpm add @bundleup/sdk
48
+ ```
49
+
50
+ ## Requirements
51
+
52
+ - **Node.js**: 16.0.0 or higher
53
+ - **TypeScript**: 5.0+ (for TypeScript projects)
54
+ - **Fetch API**: Native fetch support (Node.js 18+) or polyfill for older versions
55
+
56
+ ### Node.js Compatibility
57
+
58
+ For Node.js versions below 18, you'll need to install a fetch polyfill:
59
+
60
+ ```bash
61
+ npm install node-fetch
62
+ ```
63
+
64
+ Then import it before using the SDK:
65
+
66
+ ```javascript
67
+ import fetch from 'node-fetch';
68
+ globalThis.fetch = fetch;
69
+ ```
70
+
71
+ ## Features
72
+
73
+ - 🚀 **TypeScript First** - Built with TypeScript, includes comprehensive type definitions
74
+ - ðŸ“Ķ **Modern JavaScript** - ESM and CommonJS support for maximum compatibility
75
+ - ⚡ **Promise-based API** - Async/await support using native fetch
76
+ - 🔌 **100+ Integrations** - Connect to Slack, GitHub, Jira, Linear, and many more
77
+ - ðŸŽŊ **Unified API** - Consistent interface across all integrations via Unify API
78
+ - 🔑 **Proxy API** - Direct access to underlying integration APIs
79
+ - ðŸŠķ **Lightweight** - Zero dependencies beyond native fetch API
80
+ - ðŸ›Ąïļ **Error Handling** - Comprehensive error messages and validation
81
+ - 📚 **Well Documented** - Extensive documentation and examples
82
+
83
+ ## Quick Start
84
+
85
+ Get started with BundleUp in just a few lines of code:
86
+
87
+ ```javascript
88
+ import { BundleUp } from '@bundleup/sdk';
89
+
90
+ // Initialize the client
91
+ const client = new BundleUp(process.env.BUNDLEUP_API_KEY);
92
+
93
+ // List all active connections
94
+ const connections = await client.connections.list();
95
+ console.log(`You have ${connections.length} active connections`);
96
+
97
+ // Use the Proxy API to make requests to integrated services
98
+ const proxy = client.proxy('conn_123');
99
+ const response = await proxy.get('/api/users');
100
+ const users = await response.json();
101
+ console.log('Users:', users);
102
+
103
+ // Use the Unify API for standardized data across integrations
104
+ const unify = client.unify('conn_456');
105
+ const channels = await unify.chat.channels({ limit: 10 });
106
+ console.log('Chat channels:', channels.data);
107
+ ```
108
+
109
+ ## Authentication
110
+
111
+ The BundleUp SDK uses API keys for authentication. You can obtain your API key from the [BundleUp Dashboard](https://app.bundleup.io).
112
+
113
+ ### Getting Your API Key
114
+
115
+ 1. Sign in to your [BundleUp Dashboard](https://app.bundleup.io)
116
+ 2. Navigate to **API Keys**
117
+ 3. Click **Create API Key**
118
+ 4. Copy your API key and store it securely
119
+
120
+ ### Initializing the SDK
121
+
122
+ ```javascript
123
+ import { BundleUp } from '@bundleup/sdk';
124
+
125
+ // Initialize with API key
126
+ const client = new BundleUp('your_api_key_here');
127
+
128
+ // Or use environment variable (recommended)
129
+ const client = new BundleUp(process.env.BUNDLEUP_API_KEY);
130
+ ```
131
+
132
+ ### Security Best Practices
133
+
134
+ - ✅ **DO** store API keys in environment variables
135
+ - ✅ **DO** use a secrets management service in production
136
+ - ✅ **DO** rotate API keys regularly
137
+ - ❌ **DON'T** commit API keys to version control
138
+ - ❌ **DON'T** hardcode API keys in your source code
139
+ - ❌ **DON'T** share API keys in public channels
140
+
141
+ **Example `.env` file:**
142
+
143
+ ```bash
144
+ BUNDLEUP_API_KEY=bu_live_1234567890abcdefghijklmnopqrstuvwxyz
145
+ ```
146
+
147
+ **Loading environment variables:**
148
+
149
+ ```javascript
150
+ import 'dotenv/config'; // For Node.js projects
151
+ import { BundleUp } from '@bundleup/sdk';
152
+
153
+ const client = new BundleUp(process.env.BUNDLEUP_API_KEY);
154
+ ```
155
+
156
+ ## Core Concepts
157
+
158
+ ### Platform API
159
+
160
+ The **Platform API** provides access to core BundleUp features like managing connections and integrations. Use this API to list, retrieve, and delete connections, as well as discover available integrations.
161
+
162
+ ### Proxy API
163
+
164
+ The **Proxy API** allows you to make direct HTTP requests to the underlying integration's API through BundleUp. This is useful when you need access to integration-specific features not covered by the Unify API.
165
+
166
+ ### Unify API
167
+
168
+ The **Unify API** provides a standardized, normalized interface across different integrations. For example, you can fetch chat channels from Slack, Discord, or Microsoft Teams using the same API call.
169
+
170
+ ## API Reference
171
+
172
+ ### Connections
173
+
174
+ Manage your integration connections.
175
+
176
+ #### List Connections
177
+
178
+ Retrieve a list of all connections in your account.
179
+
180
+ ```javascript
181
+ const connections = await client.connections.list();
182
+ ```
183
+
184
+ **With query parameters:**
185
+
186
+ ```javascript
187
+ const connections = await client.connections.list({
188
+ integration_id: 'int_slack',
189
+ limit: 50,
190
+ offset: 0,
191
+ external_id: 'user_123',
192
+ });
193
+ ```
194
+
195
+ **Query Parameters:**
196
+
197
+ - `integration_id` (string): Filter by integration ID
198
+ - `integration_identifier` (string): Filter by integration identifier (e.g., 'slack', 'github')
199
+ - `external_id` (string): Filter by external user/account ID
200
+ - `limit` (number): Maximum number of results (default: 50, max: 100)
201
+ - `offset` (number): Number of results to skip for pagination
202
+
203
+ **Response:**
204
+
205
+ ```typescript
206
+ [
207
+ {
208
+ id: 'conn_123abc',
209
+ externalId: 'user_456',
210
+ integrationId: 'int_slack',
211
+ isValid: true,
212
+ createdAt: '2024-01-15T10:30:00Z',
213
+ updatedAt: '2024-01-20T14:22:00Z',
214
+ refreshedAt: '2024-01-20T14:22:00Z',
215
+ expiresAt: '2024-04-20T14:22:00Z',
216
+ },
217
+ // ... more connections
218
+ ];
219
+ ```
220
+
221
+ #### Retrieve a Connection
222
+
223
+ Get details of a specific connection by ID.
224
+
225
+ ```javascript
226
+ const connection = await client.connections.retrieve('conn_123abc');
227
+ ```
228
+
229
+ **Response:**
230
+
231
+ ```typescript
232
+ {
233
+ id: 'conn_123abc',
234
+ externalId: 'user_456',
235
+ integrationId: 'int_slack',
236
+ isValid: true,
237
+ createdAt: '2024-01-15T10:30:00Z',
238
+ updatedAt: '2024-01-20T14:22:00Z',
239
+ refreshedAt: '2024-01-20T14:22:00Z',
240
+ expiresAt: '2024-04-20T14:22:00Z'
241
+ }
242
+ ```
243
+
244
+ #### Delete a Connection
245
+
246
+ Remove a connection from your account.
247
+
248
+ ```javascript
249
+ await client.connections.del('conn_123abc');
250
+ ```
251
+
252
+ **Note:** Deleting a connection will revoke access to the integration and cannot be undone.
253
+
254
+ ### Integrations
255
+
256
+ Discover and work with available integrations.
257
+
258
+ #### List Integrations
259
+
260
+ Get a list of all available integrations.
261
+
262
+ ```javascript
263
+ const integrations = await client.integrations.list();
264
+ ```
265
+
266
+ **With query parameters:**
267
+
268
+ ```javascript
269
+ const integrations = await client.integrations.list({
270
+ status: 'active',
271
+ limit: 100,
272
+ offset: 0,
273
+ });
274
+ ```
275
+
276
+ **Query Parameters:**
277
+
278
+ - `status` (string): Filter by status ('active', 'inactive', 'beta')
279
+ - `limit` (number): Maximum number of results
280
+ - `offset` (number): Number of results to skip for pagination
281
+
282
+ **Response:**
283
+
284
+ ```typescript
285
+ [
286
+ {
287
+ id: 'int_slack',
288
+ identifier: 'slack',
289
+ name: 'Slack',
290
+ category: 'chat',
291
+ createdAt: '2023-01-01T00:00:00Z',
292
+ updatedAt: '2024-01-15T10:00:00Z',
293
+ },
294
+ // ... more integrations
295
+ ];
296
+ ```
297
+
298
+ #### Retrieve an Integration
299
+
300
+ Get details of a specific integration.
301
+
302
+ ```javascript
303
+ const integration = await client.integrations.retrieve('int_slack');
304
+ ```
305
+
306
+ **Response:**
307
+
308
+ ```typescript
309
+ {
310
+ id: 'int_slack',
311
+ identifier: 'slack',
312
+ name: 'Slack',
313
+ category: 'chat',
314
+ createdAt: '2023-01-01T00:00:00Z',
315
+ updatedAt: '2024-01-15T10:00:00Z'
316
+ }
317
+ ```
318
+
319
+ ### Webhooks
320
+
321
+ Manage webhook subscriptions for real-time event notifications.
322
+
323
+ #### List Webhooks
324
+
325
+ Get all registered webhooks.
326
+
327
+ ```javascript
328
+ const webhooks = await client.webhooks.list();
329
+ ```
330
+
331
+ **With pagination:**
332
+
333
+ ```javascript
334
+ const webhooks = await client.webhooks.list({
335
+ limit: 50,
336
+ offset: 0,
337
+ });
338
+ ```
339
+
340
+ **Response:**
341
+
342
+ ```typescript
343
+ [
344
+ {
345
+ id: 'webhook_123',
346
+ name: 'My Webhook',
347
+ url: 'https://example.com/webhook',
348
+ events: {
349
+ 'connection.created': true,
350
+ 'connection.deleted': true,
351
+ },
352
+ createdAt: '2024-01-15T10:30:00Z',
353
+ updatedAt: '2024-01-20T14:22:00Z',
354
+ lastTriggeredAt: '2024-01-20T14:22:00Z',
355
+ },
356
+ ];
357
+ ```
358
+
359
+ #### Create a Webhook
360
+
361
+ Register a new webhook endpoint.
362
+
363
+ ```javascript
364
+ const webhook = await client.webhooks.create({
365
+ name: 'Connection Events Webhook',
366
+ url: 'https://example.com/webhook',
367
+ events: {
368
+ 'connection.created': true,
369
+ 'connection.deleted': true,
370
+ 'connection.updated': true,
371
+ },
372
+ });
373
+ ```
374
+
375
+ **Webhook Events:**
376
+
377
+ - `connection.created` - Triggered when a new connection is established
378
+ - `connection.deleted` - Triggered when a connection is removed
379
+ - `connection.updated` - Triggered when a connection is modified
380
+
381
+ **Request Body:**
382
+
383
+ ```typescript
384
+ {
385
+ name: string; // Friendly name for the webhook
386
+ url: string; // Your webhook endpoint URL
387
+ events: { // Events to subscribe to
388
+ [eventName: string]: boolean;
389
+ };
390
+ }
391
+ ```
392
+
393
+ **Response:**
394
+
395
+ ```typescript
396
+ {
397
+ id: 'webhook_123',
398
+ name: 'Connection Events Webhook',
399
+ url: 'https://example.com/webhook',
400
+ events: {
401
+ 'connection.created': true,
402
+ 'connection.deleted': true,
403
+ 'connection.updated': true
404
+ },
405
+ createdAt: '2024-01-15T10:30:00Z',
406
+ updatedAt: '2024-01-15T10:30:00Z'
407
+ }
408
+ ```
409
+
410
+ #### Retrieve a Webhook
411
+
412
+ Get details of a specific webhook.
413
+
414
+ ```javascript
415
+ const webhook = await client.webhooks.retrieve('webhook_123');
416
+ ```
417
+
418
+ #### Update a Webhook
419
+
420
+ Modify an existing webhook.
421
+
422
+ ```javascript
423
+ const updated = await client.webhooks.update('webhook_123', {
424
+ name: 'Updated Webhook Name',
425
+ url: 'https://example.com/new-webhook',
426
+ events: {
427
+ 'connection.created': true,
428
+ 'connection.deleted': false,
429
+ },
430
+ });
431
+ ```
432
+
433
+ #### Delete a Webhook
434
+
435
+ Remove a webhook subscription.
436
+
437
+ ```javascript
438
+ await client.webhooks.del('webhook_123');
439
+ ```
440
+
441
+ #### Webhook Payload Example
442
+
443
+ When an event occurs, BundleUp sends a POST request to your webhook URL with the following payload:
444
+
445
+ ```json
446
+ {
447
+ "id": "evt_1234567890",
448
+ "type": "connection.created",
449
+ "created_at": "2024-01-15T10:30:00Z",
450
+ "data": {
451
+ "id": "conn_123abc",
452
+ "external_id": "user_456",
453
+ "integration_id": "int_slack",
454
+ "is_valid": true,
455
+ "created_at": "2024-01-15T10:30:00Z"
456
+ }
457
+ }
458
+ ```
459
+
460
+ #### Webhook Security
461
+
462
+ To verify webhook signatures:
463
+
464
+ ```javascript
465
+ import crypto from 'crypto';
466
+
467
+ function verifyWebhookSignature(payload, signature, secret) {
468
+ const hmac = crypto.createHmac('sha256', secret);
469
+ hmac.update(payload);
470
+ const digest = hmac.digest('hex');
471
+ return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
472
+ }
473
+
474
+ // In your webhook handler
475
+ app.post('/webhook', (req, res) => {
476
+ const signature = req.headers['x-bundleup-signature'];
477
+ const payload = JSON.stringify(req.body);
478
+
479
+ if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
480
+ return res.status(401).send('Invalid signature');
481
+ }
482
+
483
+ // Process the webhook
484
+ console.log('Webhook received:', req.body);
485
+ res.status(200).send('OK');
486
+ });
487
+ ```
488
+
489
+ ### Proxy API
490
+
491
+ Make direct HTTP requests to integration APIs through BundleUp.
492
+
493
+ #### Creating a Proxy Instance
494
+
495
+ ```javascript
496
+ const proxy = client.proxy('conn_123abc');
497
+ ```
498
+
499
+ #### GET Request
500
+
501
+ ```javascript
502
+ const response = await proxy.get('/api/users');
503
+ const data = await response.json();
504
+ console.log(data);
505
+ ```
506
+
507
+ **With custom headers:**
508
+
509
+ ```javascript
510
+ const response = await proxy.get('/api/users', {
511
+ 'X-Custom-Header': 'value',
512
+ Accept: 'application/json',
513
+ });
514
+ ```
515
+
516
+ #### POST Request
517
+
518
+ ```javascript
519
+ const response = await proxy.post(
520
+ '/api/users',
521
+ JSON.stringify({
522
+ name: 'John Doe',
523
+ email: 'john@example.com',
524
+ role: 'developer',
525
+ }),
526
+ );
527
+
528
+ const newUser = await response.json();
529
+ console.log('Created user:', newUser);
530
+ ```
531
+
532
+ **With custom headers:**
533
+
534
+ ```javascript
535
+ const response = await proxy.post('/api/users', JSON.stringify({ name: 'John Doe' }), {
536
+ 'Content-Type': 'application/json',
537
+ 'X-API-Version': '2.0',
538
+ });
539
+ ```
540
+
541
+ #### PUT Request
542
+
543
+ ```javascript
544
+ const response = await proxy.put(
545
+ '/api/users/123',
546
+ JSON.stringify({
547
+ name: 'Jane Doe',
548
+ email: 'jane@example.com',
549
+ }),
550
+ );
551
+
552
+ const updatedUser = await response.json();
553
+ ```
554
+
555
+ #### PATCH Request
556
+
557
+ ```javascript
558
+ const response = await proxy.patch(
559
+ '/api/users/123',
560
+ JSON.stringify({
561
+ email: 'newemail@example.com',
562
+ }),
563
+ );
564
+
565
+ const partiallyUpdated = await response.json();
566
+ ```
567
+
568
+ #### DELETE Request
569
+
570
+ ```javascript
571
+ const response = await proxy.delete('/api/users/123');
572
+
573
+ if (response.ok) {
574
+ console.log('User deleted successfully');
575
+ }
576
+ ```
577
+
578
+ #### Working with Different Content Types
579
+
580
+ **Sending form data:**
581
+
582
+ ```javascript
583
+ const formData = new URLSearchParams();
584
+ formData.append('name', 'John Doe');
585
+ formData.append('email', 'john@example.com');
586
+
587
+ const response = await proxy.post('/api/users', formData.toString(), {
588
+ 'Content-Type': 'application/x-www-form-urlencoded',
589
+ });
590
+ ```
591
+
592
+ **Uploading files:**
593
+
594
+ ```javascript
595
+ import FormData from 'form-data';
596
+ import fs from 'fs';
597
+
598
+ const form = new FormData();
599
+ form.append('file', fs.createReadStream('document.pdf'));
600
+ form.append('title', 'My Document');
601
+
602
+ const response = await proxy.post('/api/documents', form, form.getHeaders());
603
+ ```
604
+
605
+ #### Handling Binary Data
606
+
607
+ ```javascript
608
+ const response = await proxy.get('/api/files/download/123');
609
+ const buffer = await response.arrayBuffer();
610
+ fs.writeFileSync('downloaded-file.pdf', Buffer.from(buffer));
611
+ ```
612
+
613
+ ### Unify API
614
+
615
+ Access unified, normalized data across different integrations with a consistent interface.
616
+
617
+ #### Creating a Unify Instance
618
+
619
+ ```javascript
620
+ const unify = client.unify('conn_123abc');
621
+ ```
622
+
623
+ #### Chat API
624
+
625
+ The Chat API provides a unified interface for chat platforms like Slack, Discord, and Microsoft Teams.
626
+
627
+ ##### List Channels
628
+
629
+ Retrieve a list of channels from the connected chat platform.
630
+
631
+ ```javascript
632
+ const result = await unify.chat.channels({
633
+ limit: 100,
634
+ after: null,
635
+ include_raw: false,
636
+ });
637
+
638
+ console.log('Channels:', result.data);
639
+ console.log('Next cursor:', result.metadata.next);
640
+ ```
641
+
642
+ **Parameters:**
643
+
644
+ - `limit` (number, optional): Maximum number of channels to return (default: 100, max: 1000)
645
+ - `after` (string, optional): Pagination cursor from previous response
646
+ - `include_raw` (boolean, optional): Include raw API response from the integration (default: false)
647
+
648
+ **Response:**
649
+
650
+ ```typescript
651
+ {
652
+ data: [
653
+ {
654
+ id: 'C1234567890',
655
+ name: 'general'
656
+ },
657
+ {
658
+ id: 'C0987654321',
659
+ name: 'engineering'
660
+ }
661
+ ],
662
+ metadata: {
663
+ next: 'cursor_abc123' // Use this for pagination
664
+ },
665
+ _raw?: { // Only present if include_raw: true
666
+ // Original response from the integration API
667
+ }
668
+ }
669
+ ```
670
+
671
+ **Pagination example:**
672
+
673
+ ```javascript
674
+ let allChannels = [];
675
+ let cursor = null;
676
+
677
+ do {
678
+ const result = await unify.chat.channels({
679
+ limit: 100,
680
+ after: cursor,
681
+ });
682
+
683
+ allChannels = [...allChannels, ...result.data];
684
+ cursor = result.metadata.next;
685
+ } while (cursor);
686
+
687
+ console.log(`Fetched ${allChannels.length} total channels`);
688
+ ```
689
+
690
+ #### Git API
691
+
692
+ The Git API provides a unified interface for version control platforms like GitHub, GitLab, and Bitbucket.
693
+
694
+ ##### List Repositories
695
+
696
+ ```javascript
697
+ const result = await unify.git.repos({
698
+ limit: 50,
699
+ after: null,
700
+ include_raw: false,
701
+ });
702
+
703
+ console.log('Repositories:', result.data);
704
+ ```
705
+
706
+ **Response:**
707
+
708
+ ```typescript
709
+ {
710
+ data: [
711
+ {
712
+ id: '123456',
713
+ name: 'my-awesome-project',
714
+ full_name: 'organization/my-awesome-project',
715
+ description: 'An awesome project',
716
+ url: 'https://github.com/organization/my-awesome-project',
717
+ created_at: '2023-01-15T10:30:00Z',
718
+ updated_at: '2024-01-20T14:22:00Z',
719
+ pushed_at: '2024-01-20T14:22:00Z'
720
+ }
721
+ ],
722
+ metadata: {
723
+ next: 'cursor_xyz789'
724
+ }
725
+ }
726
+ ```
727
+
728
+ ##### List Pull Requests
729
+
730
+ ```javascript
731
+ const result = await unify.git.pulls('organization/repo-name', {
732
+ limit: 20,
733
+ after: null,
734
+ include_raw: false,
735
+ });
736
+
737
+ console.log('Pull Requests:', result.data);
738
+ ```
739
+
740
+ **Parameters:**
741
+
742
+ - `repoName` (string, required): Repository name in the format 'owner/repo'
743
+ - `limit` (number, optional): Maximum number of PRs to return
744
+ - `after` (string, optional): Pagination cursor
745
+ - `include_raw` (boolean, optional): Include raw API response
746
+
747
+ **Response:**
748
+
749
+ ```typescript
750
+ {
751
+ data: [
752
+ {
753
+ id: '12345',
754
+ number: 42,
755
+ title: 'Add new feature',
756
+ description: 'This PR adds an awesome new feature',
757
+ draft: false,
758
+ state: 'open',
759
+ url: 'https://github.com/org/repo/pull/42',
760
+ user: 'john-doe',
761
+ created_at: '2024-01-15T10:30:00Z',
762
+ updated_at: '2024-01-20T14:22:00Z',
763
+ merged_at: null
764
+ }
765
+ ],
766
+ metadata: {
767
+ next: null
768
+ }
769
+ }
770
+ ```
771
+
772
+ ##### List Tags
773
+
774
+ ```javascript
775
+ const result = await unify.git.tags('organization/repo-name', {
776
+ limit: 50,
777
+ });
778
+
779
+ console.log('Tags:', result.data);
780
+ ```
781
+
782
+ **Response:**
783
+
784
+ ```typescript
785
+ {
786
+ data: [
787
+ {
788
+ name: 'v1.0.0',
789
+ commit_sha: 'abc123def456'
790
+ },
791
+ {
792
+ name: 'v0.9.0',
793
+ commit_sha: 'def456ghi789'
794
+ }
795
+ ],
796
+ metadata: {
797
+ next: null
798
+ }
799
+ }
800
+ ```
801
+
802
+ ##### List Releases
803
+
804
+ ```javascript
805
+ const result = await unify.git.releases('organization/repo-name', {
806
+ limit: 10,
807
+ });
808
+
809
+ console.log('Releases:', result.data);
810
+ ```
811
+
812
+ **Response:**
813
+
814
+ ```typescript
815
+ {
816
+ data: [
817
+ {
818
+ id: '54321',
819
+ name: 'Version 1.0.0',
820
+ tag_name: 'v1.0.0',
821
+ description: 'Initial release with all the features',
822
+ prerelease: false,
823
+ url: 'https://github.com/org/repo/releases/tag/v1.0.0',
824
+ created_at: '2024-01-15T10:30:00Z',
825
+ released_at: '2024-01-15T10:30:00Z'
826
+ }
827
+ ],
828
+ metadata: {
829
+ next: null
830
+ }
831
+ }
832
+ ```
833
+
834
+ #### Project Management API
835
+
836
+ The PM API provides a unified interface for project management platforms like Jira, Linear, and Asana.
837
+
838
+ ##### List Issues
839
+
840
+ ```javascript
841
+ const result = await unify.pm.issues({
842
+ limit: 100,
843
+ after: null,
844
+ include_raw: false,
845
+ });
846
+
847
+ console.log('Issues:', result.data);
848
+ ```
849
+
850
+ **Response:**
851
+
852
+ ```typescript
853
+ {
854
+ data: [
855
+ {
856
+ id: 'PROJ-123',
857
+ url: 'https://jira.example.com/browse/PROJ-123',
858
+ title: 'Fix login bug',
859
+ status: 'in_progress',
860
+ description: 'Users are unable to log in',
861
+ created_at: '2024-01-15T10:30:00Z',
862
+ updated_at: '2024-01-20T14:22:00Z'
863
+ }
864
+ ],
865
+ metadata: {
866
+ next: 'cursor_def456'
867
+ }
868
+ }
869
+ ```
870
+
871
+ **Filtering and sorting:**
872
+
873
+ ```javascript
874
+ const openIssues = result.data.filter(issue => issue.status === 'open');
875
+ const sortedByDate = result.data.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
876
+ ```
877
+
878
+ ## Error Handling
879
+
880
+ The SDK throws standard JavaScript errors with descriptive messages. Always wrap SDK calls in try-catch blocks for proper error handling.
881
+
882
+ ```javascript
883
+ try {
884
+ const connections = await client.connections.list();
885
+ } catch (error) {
886
+ console.error('Failed to fetch connections:', error.message);
887
+ }
888
+ ```
889
+
890
+ ### Getting Help
891
+
892
+ If you're still experiencing issues:
893
+
894
+ 1. Check the [BundleUp Documentation](https://docs.bundleup.io)
895
+ 2. Search [GitHub Issues](https://github.com/bundleup/javascript/issues)
896
+ 3. Join our [Community Discord](https://discord.gg/bundleup)
897
+ 4. Contact [support@bundleup.io](mailto:support@bundleup.io)
898
+
899
+ When reporting issues, please include:
900
+
901
+ - SDK version (`@bundleup/sdk` version from package.json)
902
+ - Node.js version (`node --version`)
903
+ - Minimal code to reproduce the issue
904
+ - Full error message and stack trace
905
+
906
+ ## Development
907
+
908
+ ### Setting Up Development Environment
909
+
910
+ ```bash
911
+ # Clone the repository
912
+ git clone https://github.com/bundleup/javascript.git
913
+ cd javascript/packages/sdk
914
+
915
+ # Install dependencies
916
+ npm install
917
+
918
+ # Build the package
919
+ npm run build
920
+
921
+ # Run tests
922
+ npm test
923
+
924
+ # Watch mode for development
925
+ npm run dev
926
+ ```
927
+
928
+ ### Project Structure
929
+
930
+ ```
931
+ src/
932
+ ├── index.ts # Main entry point
933
+ ├── proxy.ts # Proxy API implementation
934
+ ├── unify.ts # Unify API implementation
935
+ ├── utils.ts # Utility functions
936
+ ├── resources/
937
+ │ ├── base.ts # Base resource class
938
+ │ ├── connection.ts # Connections API
939
+ │ ├── integration.ts # Integrations API
940
+ │ └── webhooks.ts # Webhooks API
941
+ ├── unify/
942
+ │ ├── base.ts # Base Unify class
943
+ │ ├── chat.ts # Chat Unify API
944
+ │ ├── git.ts # Git Unify API
945
+ │ └── pm.ts # PM Unify API
946
+ └── __tests__/ # Test files
947
+ ```
948
+
949
+ ### Running Tests
950
+
951
+ ```bash
952
+ # Run all tests
953
+ npm test
954
+
955
+ # Run tests in watch mode
956
+ npm test -- --watch
957
+
958
+ # Run specific test file
959
+ npm test -- proxy.test.ts
960
+
961
+ # Run with coverage
962
+ npm test -- --coverage
963
+ ```
964
+
965
+ ### Building
966
+
967
+ ```bash
968
+ # Build for production
969
+ npm run build
970
+
971
+ # Clean build artifacts
972
+ npm run clean
973
+
974
+ # Build and watch for changes
975
+ npm run dev
976
+ ```
977
+
978
+ ### Linting
979
+
980
+ ```bash
981
+ # Run ESLint
982
+ npm run lint
983
+
984
+ # Fix linting issues
985
+ npm run lint -- --fix
986
+ ```
987
+
988
+ ## Contributing
989
+
990
+ We welcome contributions to the BundleUp JavaScript SDK! Here's how you can help:
991
+
992
+ ### Reporting Bugs
993
+
994
+ 1. Check if the bug has already been reported in [GitHub Issues](https://github.com/bundleup/javascript/issues)
995
+ 2. If not, create a new issue with:
996
+ - Clear title and description
997
+ - Steps to reproduce
998
+ - Expected vs actual behavior
999
+ - SDK version and environment details
1000
+
1001
+ ### Suggesting Features
1002
+
1003
+ 1. Open a new issue with the "feature request" label
1004
+ 2. Describe the feature and its use case
1005
+ 3. Explain why this feature would be useful
1006
+
1007
+ ### Pull Requests
1008
+
1009
+ 1. Fork the repository
1010
+ 2. Create a new branch: `git checkout -b feature/my-new-feature`
1011
+ 3. Make your changes
1012
+ 4. Write or update tests
1013
+ 5. Ensure all tests pass: `npm test`
1014
+ 6. Commit your changes: `git commit -am 'Add new feature'`
1015
+ 7. Push to the branch: `git push origin feature/my-new-feature`
1016
+ 8. Submit a pull request
1017
+
1018
+ ### Development Guidelines
1019
+
1020
+ - Follow the existing code style
1021
+ - Add tests for new features
1022
+ - Update documentation for API changes
1023
+ - Keep commits focused and atomic
1024
+ - Write clear commit messages
1025
+
1026
+ ## License
1027
+
1028
+ This package is available as open source under the terms of the [ISC License](https://opensource.org/licenses/ISC).
1029
+
1030
+ ```
1031
+ Copyright (c) 2024 BundleUp
1032
+
1033
+ Permission to use, copy, modify, and/or distribute this software for any
1034
+ purpose with or without fee is hereby granted, provided that the above
1035
+ copyright notice and this permission notice appear in all copies.
1036
+
1037
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1038
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1039
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1040
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1041
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1042
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1043
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1044
+ ```
1045
+
1046
+ ## Support
1047
+
1048
+ Need help? We're here for you!
1049
+
1050
+ ### Documentation
1051
+
1052
+ - **Official Docs**: [https://docs.bundleup.io](https://docs.bundleup.io)
1053
+ - **API Reference**: [https://docs.bundleup.io/api](https://docs.bundleup.io/api)
1054
+ - **SDK Guides**: [https://docs.bundleup.io/sdk/javascript](https://docs.bundleup.io/sdk/javascript)
1055
+
1056
+ ### Community
1057
+
1058
+ - **Discord**: [https://discord.gg/bundleup](https://discord.gg/bundleup)
1059
+ - **GitHub Discussions**: [https://github.com/bundleup/javascript/discussions](https://github.com/bundleup/javascript/discussions)
1060
+ - **Stack Overflow**: Tag your questions with `bundleup`
1061
+
1062
+ ### Direct Support
1063
+
1064
+ - **Email**: [support@bundleup.io](mailto:support@bundleup.io)
1065
+ - **GitHub Issues**: [https://github.com/bundleup/javascript/issues](https://github.com/bundleup/javascript/issues)
1066
+ - **Twitter**: [@bundleup_io](https://twitter.com/bundleup_io)
1067
+
1068
+ ### Enterprise Support
1069
+
1070
+ For enterprise customers, we offer:
1071
+
1072
+ - Priority support with SLA
1073
+ - Dedicated support channel
1074
+ - Architecture consultation
1075
+ - Custom integration assistance
1076
+
1077
+ Contact [enterprise@bundleup.io](mailto:enterprise@bundleup.io) for more information.
1078
+
1079
+ ## Code of Conduct
1080
+
1081
+ Everyone interacting in the BundleUp project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/bundleup/javascript/blob/main/CODE_OF_CONDUCT).
1082
+
1083
+ ---
1084
+
1085
+ Made with âĪïļ by the [BundleUp](https://bundleup.io) team