@bundleup/sdk 0.0.18 → 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
@@ -3,21 +3,45 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@bundleup/sdk.svg)](https://www.npmjs.com/package/@bundleup/sdk)
4
4
  [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
5
 
6
- Official JavaScript/TypeScript SDK for the [BundleUp](https://bundleup.io) API. Connect to 100+ integrations with a single, unified API.
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)
7
27
 
8
28
  ## Installation
9
29
 
30
+ Install the SDK using your preferred package manager:
31
+
32
+ **npm:**
33
+
10
34
  ```bash
11
35
  npm install @bundleup/sdk
12
36
  ```
13
37
 
14
- Or with yarn:
38
+ **yarn:**
15
39
 
16
40
  ```bash
17
41
  yarn add @bundleup/sdk
18
42
  ```
19
43
 
20
- Or with pnpm:
44
+ **pnpm:**
21
45
 
22
46
  ```bash
23
47
  pnpm add @bundleup/sdk
@@ -25,34 +49,76 @@ pnpm add @bundleup/sdk
25
49
 
26
50
  ## Requirements
27
51
 
28
- - Node.js 16 or higher
29
- - TypeScript 5.0+ (for TypeScript projects)
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
+ ```
30
70
 
31
71
  ## Features
32
72
 
33
- - **TypeScript First** - Built with TypeScript, includes full type definitions
34
- - **Modern JavaScript** - ESM and CommonJS support for maximum compatibility
35
- - **Promise-based API** - Uses native fetch for all HTTP requests
36
- - **Comprehensive Coverage** - Full support for Connections, Integrations, Webhooks, Proxy, and Unify APIs
37
- - **Lightweight** - Zero dependencies beyond native fetch API
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
38
82
 
39
83
  ## Quick Start
40
84
 
85
+ Get started with BundleUp in just a few lines of code:
86
+
41
87
  ```javascript
42
88
  import { BundleUp } from '@bundleup/sdk';
43
89
 
44
- // Initialize the client with your API key
90
+ // Initialize the client
45
91
  const client = new BundleUp(process.env.BUNDLEUP_API_KEY);
46
92
 
47
- // List all connections
93
+ // List all active connections
48
94
  const connections = await client.connections.list();
49
- console.log(connections);
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);
50
107
  ```
51
108
 
52
109
  ## Authentication
53
110
 
54
111
  The BundleUp SDK uses API keys for authentication. You can obtain your API key from the [BundleUp Dashboard](https://app.bundleup.io).
55
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
+
56
122
  ```javascript
57
123
  import { BundleUp } from '@bundleup/sdk';
58
124
 
@@ -63,273 +129,957 @@ const client = new BundleUp('your_api_key_here');
63
129
  const client = new BundleUp(process.env.BUNDLEUP_API_KEY);
64
130
  ```
65
131
 
66
- **Security Best Practice:** Never commit your API keys to version control. Use environment variables or a secure credential management system.
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
67
157
 
68
- ## Usage
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
69
171
 
70
172
  ### Connections
71
173
 
72
- Manage your integration connections:
174
+ Manage your integration connections.
175
+
176
+ #### List Connections
177
+
178
+ Retrieve a list of all connections in your account.
73
179
 
74
180
  ```javascript
75
- // List all connections
76
181
  const connections = await client.connections.list();
182
+ ```
77
183
 
78
- // List with query parameters
79
- const connections = await client.connections.list({ status: 'active', limit: '10' });
184
+ **With query parameters:**
80
185
 
81
- // Retrieve a specific connection
82
- const connection = await client.connections.retrieve('conn_123');
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
83
202
 
84
- // Delete a connection
85
- await client.connections.del('conn_123');
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
+ ];
86
219
  ```
87
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
+
88
254
  ### Integrations
89
255
 
90
- Work with available integrations:
256
+ Discover and work with available integrations.
257
+
258
+ #### List Integrations
259
+
260
+ Get a list of all available integrations.
91
261
 
92
262
  ```javascript
93
- // List all integrations
94
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
95
299
 
96
- // Retrieve a specific integration
97
- const integration = await client.integrations.retrieve('int_123');
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
+ }
98
317
  ```
99
318
 
100
319
  ### Webhooks
101
320
 
102
- Manage webhook subscriptions:
321
+ Manage webhook subscriptions for real-time event notifications.
322
+
323
+ #### List Webhooks
324
+
325
+ Get all registered webhooks.
103
326
 
104
327
  ```javascript
105
- // List all webhooks
106
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
107
360
 
108
- // Create a webhook
361
+ Register a new webhook endpoint.
362
+
363
+ ```javascript
109
364
  const webhook = await client.webhooks.create({
365
+ name: 'Connection Events Webhook',
110
366
  url: 'https://example.com/webhook',
111
367
  events: {
112
368
  'connection.created': true,
113
- 'connection.deleted': true
114
- }
369
+ 'connection.deleted': true,
370
+ 'connection.updated': true,
371
+ },
115
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.
116
413
 
117
- // Retrieve a webhook
414
+ ```javascript
118
415
  const webhook = await client.webhooks.retrieve('webhook_123');
416
+ ```
417
+
418
+ #### Update a Webhook
119
419
 
120
- // Update a webhook
420
+ Modify an existing webhook.
421
+
422
+ ```javascript
121
423
  const updated = await client.webhooks.update('webhook_123', {
122
- url: 'https://example.com/new-webhook'
424
+ name: 'Updated Webhook Name',
425
+ url: 'https://example.com/new-webhook',
426
+ events: {
427
+ 'connection.created': true,
428
+ 'connection.deleted': false,
429
+ },
123
430
  });
431
+ ```
432
+
433
+ #### Delete a Webhook
124
434
 
125
- // Delete a webhook
435
+ Remove a webhook subscription.
436
+
437
+ ```javascript
126
438
  await client.webhooks.del('webhook_123');
127
439
  ```
128
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
+
129
489
  ### Proxy API
130
490
 
131
- Make direct calls to the underlying integration APIs:
491
+ Make direct HTTP requests to integration APIs through BundleUp.
492
+
493
+ #### Creating a Proxy Instance
132
494
 
133
495
  ```javascript
134
- // Create a proxy instance
135
- const proxy = client.proxy('conn_123');
496
+ const proxy = client.proxy('conn_123abc');
497
+ ```
498
+
499
+ #### GET Request
136
500
 
137
- // Make GET request
138
- const users = await proxy.get('/api/users');
139
- const data = await users.json();
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
+ );
140
527
 
141
- // Make POST request
142
- const response = await proxy.post('/api/users', JSON.stringify({
143
- name: 'John Doe',
144
- email: 'john@example.com'
145
- }));
146
528
  const newUser = await response.json();
529
+ console.log('Created user:', newUser);
530
+ ```
531
+
532
+ **With custom headers:**
147
533
 
148
- // Make PUT request
149
- const response = await proxy.put('/api/users/123', JSON.stringify({
150
- name: 'Jane Doe'
151
- }));
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
+ ```
152
540
 
153
- // Make PATCH request
154
- const response = await proxy.patch('/api/users/123', JSON.stringify({
155
- email: 'jane@example.com'
156
- }));
541
+ #### PUT Request
157
542
 
158
- // Make DELETE request
159
- await proxy.delete('/api/users/123');
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();
160
553
  ```
161
554
 
162
- ### Unify API
555
+ #### PATCH Request
163
556
 
164
- Access unified, normalized data across different integrations.
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
+ ```
165
567
 
166
- #### Chat (Slack, Discord, Microsoft Teams, etc.)
568
+ #### DELETE Request
167
569
 
168
570
  ```javascript
169
- // Get unified API instance for a connection
170
- const unify = client.unify('conn_123');
571
+ const response = await proxy.delete('/api/users/123');
171
572
 
172
- // List channels
173
- const channels = await unify.chat.channels({ limit: 100 });
573
+ if (response.ok) {
574
+ console.log('User deleted successfully');
575
+ }
576
+ ```
174
577
 
175
- // List channels with pagination
176
- const channels = await unify.chat.channels({
177
- limit: 50,
178
- after: 'cursor_token'
179
- });
578
+ #### Working with Different Content Types
180
579
 
181
- // Include raw response from the integration
182
- const channels = await unify.chat.channels({
183
- limit: 100,
184
- includeRaw: true
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',
185
589
  });
186
590
  ```
187
591
 
188
- #### Git (GitHub, GitLab, Bitbucket, etc.)
592
+ **Uploading files:**
189
593
 
190
594
  ```javascript
191
- const unify = client.unify('conn_123');
595
+ import FormData from 'form-data';
596
+ import fs from 'fs';
192
597
 
193
- // List repositories
194
- const repos = await unify.git.repos({ limit: 50 });
598
+ const form = new FormData();
599
+ form.append('file', fs.createReadStream('document.pdf'));
600
+ form.append('title', 'My Document');
195
601
 
196
- // List pull requests for a repository
197
- const pulls = await unify.git.pulls({
198
- repoName: 'owner/repo',
199
- limit: 20
200
- });
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.
201
626
 
202
- // List tags for a repository
203
- const tags = await unify.git.tags({ repoName: 'owner/repo' });
627
+ ##### List Channels
204
628
 
205
- // List releases for a repository
206
- const releases = await unify.git.releases({
207
- repoName: 'owner/repo',
208
- limit: 10
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,
209
636
  });
210
637
 
211
- // Include raw response
212
- const repos = await unify.git.repos({ includeRaw: true });
638
+ console.log('Channels:', result.data);
639
+ console.log('Next cursor:', result.metadata.next);
213
640
  ```
214
641
 
215
- #### Project Management (Jira, Linear, Asana, etc.)
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:**
216
672
 
217
673
  ```javascript
218
- // Get issues
219
- const issues = await client.unify('conn_123').pm.issues({ limit: 100 });
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
220
691
 
221
- // List with pagination
222
- const issues = await client.unify('conn_123').pm.issues({
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({
223
698
  limit: 50,
224
- after: 'cursor_token'
699
+ after: null,
700
+ include_raw: false,
225
701
  });
226
702
 
227
- // Include raw response
228
- const issues = await client.unify('conn_123').pm.issues({
229
- includeRaw: true
230
- });
703
+ console.log('Repositories:', result.data);
231
704
  ```
232
705
 
233
- ## Error Handling
706
+ **Response:**
234
707
 
235
- The SDK throws standard JavaScript errors. You can catch and handle them as needed:
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
236
729
 
237
730
  ```javascript
238
- try {
239
- const client = new BundleUp('your-api-key');
240
- const connections = await client.connections.list();
241
- } catch (error) {
242
- if (error.message.includes('401')) {
243
- console.error('Authentication failed: Invalid API key');
244
- } else if (error.message.includes('404')) {
245
- console.error('Resource not found');
246
- } else if (error.message.includes('429')) {
247
- console.error('Rate limit exceeded');
248
- } else {
249
- console.error('API error:', error.message);
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
250
768
  }
251
769
  }
252
770
  ```
253
771
 
254
- ## Advanced Usage
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:**
255
783
 
256
- ### Query Parameters
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
+ ```
257
801
 
258
- The `list()` method supports query parameters:
802
+ ##### List Releases
259
803
 
260
804
  ```javascript
261
- // List with filters
262
- const connections = await client.connections.list({
263
- status: 'active',
264
- limit: '50',
265
- page: '1'
805
+ const result = await unify.git.releases('organization/repo-name', {
806
+ limit: 10,
266
807
  });
808
+
809
+ console.log('Releases:', result.data);
267
810
  ```
268
811
 
269
- ### Custom Headers
812
+ **Response:**
270
813
 
271
- When using the Proxy API, you can pass custom headers:
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
272
839
 
273
840
  ```javascript
274
- const proxy = client.proxy('conn_123');
275
- const response = await proxy.get('/api/users', {}, {
276
- 'X-Custom-Header': 'value'
841
+ const result = await unify.pm.issues({
842
+ limit: 100,
843
+ after: null,
844
+ include_raw: false,
277
845
  });
278
- ```
279
846
 
280
- ### TypeScript Support
847
+ console.log('Issues:', result.data);
848
+ ```
281
849
 
282
- The SDK is written in TypeScript and includes comprehensive type definitions:
850
+ **Response:**
283
851
 
284
852
  ```typescript
285
- import { BundleUp } from '@bundleup/sdk';
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
+ ```
286
870
 
287
- const client = new BundleUp(process.env.BUNDLEUP_API_KEY!);
871
+ **Filtering and sorting:**
288
872
 
289
- // Types are inferred automatically
290
- const connections = await client.connections.list();
291
- const webhook = await client.webhooks.create({
292
- url: 'https://example.com/webhook',
293
- events: {
294
- 'connection.created': true
295
- }
296
- });
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
+ }
297
888
  ```
298
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
+
299
906
  ## Development
300
907
 
301
- After cloning the repository, install dependencies:
908
+ ### Setting Up Development Environment
302
909
 
303
910
  ```bash
911
+ # Clone the repository
912
+ git clone https://github.com/bundleup/javascript.git
913
+ cd javascript/packages/sdk
914
+
915
+ # Install dependencies
304
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
305
947
  ```
306
948
 
307
- Build the package:
949
+ ### Running Tests
308
950
 
309
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
310
969
  npm run build
970
+
971
+ # Clean build artifacts
972
+ npm run clean
973
+
974
+ # Build and watch for changes
975
+ npm run dev
311
976
  ```
312
977
 
313
- Run tests:
978
+ ### Linting
314
979
 
315
980
  ```bash
316
- npm test
981
+ # Run ESLint
982
+ npm run lint
983
+
984
+ # Fix linting issues
985
+ npm run lint -- --fix
317
986
  ```
318
987
 
319
988
  ## Contributing
320
989
 
321
- Bug reports and pull requests are welcome on GitHub at https://github.com/bundleup/javascript.
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
322
1025
 
323
1026
  ## License
324
1027
 
325
1028
  This package is available as open source under the terms of the [ISC License](https://opensource.org/licenses/ISC).
326
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
+
327
1046
  ## Support
328
1047
 
329
- - Documentation: [https://docs.bundleup.io](https://docs.bundleup.io)
330
- - Email: [support@bundleup.io](mailto:support@bundleup.io)
331
- - GitHub Issues: [https://github.com/bundleup/javascript/issues](https://github.com/bundleup/javascript/issues)
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.
332
1078
 
333
1079
  ## Code of Conduct
334
1080
 
335
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