colacloud 1.0.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 ADDED
@@ -0,0 +1,671 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthenticationError: () => AuthenticationError,
24
+ ColaCloud: () => ColaCloud,
25
+ ColaCloudError: () => ColaCloudError,
26
+ NetworkError: () => NetworkError,
27
+ NotFoundError: () => NotFoundError,
28
+ RateLimitError: () => RateLimitError,
29
+ ServerError: () => ServerError,
30
+ TimeoutError: () => TimeoutError,
31
+ ValidationError: () => ValidationError,
32
+ collectAll: () => collectAll,
33
+ createPaginatedIterator: () => createPaginatedIterator,
34
+ createPaginatedIteratorWithMetadata: () => createPaginatedIteratorWithMetadata,
35
+ take: () => take
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/errors.ts
40
+ var ColaCloudError = class _ColaCloudError extends Error {
41
+ /** HTTP status code (if applicable) */
42
+ statusCode;
43
+ /** Error code from the API */
44
+ code;
45
+ /** Additional error details */
46
+ details;
47
+ constructor(message, statusCode, code, details) {
48
+ super(message);
49
+ this.name = "ColaCloudError";
50
+ this.statusCode = statusCode;
51
+ this.code = code;
52
+ this.details = details;
53
+ if (Error.captureStackTrace) {
54
+ Error.captureStackTrace(this, _ColaCloudError);
55
+ }
56
+ }
57
+ /**
58
+ * Create a ColaCloudError from an API error response
59
+ */
60
+ static fromResponse(response, statusCode) {
61
+ return new _ColaCloudError(
62
+ response.error,
63
+ statusCode,
64
+ response.code,
65
+ response.details
66
+ );
67
+ }
68
+ };
69
+ var AuthenticationError = class _AuthenticationError extends ColaCloudError {
70
+ constructor(message = "Invalid or missing API key") {
71
+ super(message, 401, "authentication_error");
72
+ this.name = "AuthenticationError";
73
+ if (Error.captureStackTrace) {
74
+ Error.captureStackTrace(this, _AuthenticationError);
75
+ }
76
+ }
77
+ };
78
+ var RateLimitError = class _RateLimitError extends ColaCloudError {
79
+ /** Rate limit information from headers */
80
+ rateLimit;
81
+ /** Seconds until the rate limit resets */
82
+ retryAfter;
83
+ constructor(message = "Rate limit exceeded", rateLimit = null, retryAfter = null) {
84
+ super(message, 429, "rate_limit_exceeded");
85
+ this.name = "RateLimitError";
86
+ this.rateLimit = rateLimit;
87
+ this.retryAfter = retryAfter;
88
+ if (Error.captureStackTrace) {
89
+ Error.captureStackTrace(this, _RateLimitError);
90
+ }
91
+ }
92
+ };
93
+ var NotFoundError = class _NotFoundError extends ColaCloudError {
94
+ /** The resource type that was not found */
95
+ resourceType;
96
+ /** The identifier that was searched for */
97
+ resourceId;
98
+ constructor(resourceType, resourceId) {
99
+ super(`${resourceType} not found: ${resourceId}`, 404, "not_found");
100
+ this.name = "NotFoundError";
101
+ this.resourceType = resourceType;
102
+ this.resourceId = resourceId;
103
+ if (Error.captureStackTrace) {
104
+ Error.captureStackTrace(this, _NotFoundError);
105
+ }
106
+ }
107
+ };
108
+ var ValidationError = class _ValidationError extends ColaCloudError {
109
+ /** Field-level validation errors */
110
+ fieldErrors;
111
+ constructor(message = "Invalid request parameters", fieldErrors) {
112
+ super(message, 400, "validation_error", fieldErrors);
113
+ this.name = "ValidationError";
114
+ this.fieldErrors = fieldErrors;
115
+ if (Error.captureStackTrace) {
116
+ Error.captureStackTrace(this, _ValidationError);
117
+ }
118
+ }
119
+ };
120
+ var ServerError = class _ServerError extends ColaCloudError {
121
+ constructor(message = "Internal server error", statusCode = 500) {
122
+ super(message, statusCode, "server_error");
123
+ this.name = "ServerError";
124
+ if (Error.captureStackTrace) {
125
+ Error.captureStackTrace(this, _ServerError);
126
+ }
127
+ }
128
+ };
129
+ var TimeoutError = class _TimeoutError extends ColaCloudError {
130
+ /** The timeout value in milliseconds */
131
+ timeoutMs;
132
+ constructor(timeoutMs) {
133
+ super(`Request timed out after ${timeoutMs}ms`, void 0, "timeout");
134
+ this.name = "TimeoutError";
135
+ this.timeoutMs = timeoutMs;
136
+ if (Error.captureStackTrace) {
137
+ Error.captureStackTrace(this, _TimeoutError);
138
+ }
139
+ }
140
+ };
141
+ var NetworkError = class _NetworkError extends ColaCloudError {
142
+ /** The original error that caused this */
143
+ cause;
144
+ constructor(message = "Network error", cause) {
145
+ super(message, void 0, "network_error");
146
+ this.name = "NetworkError";
147
+ this.cause = cause;
148
+ if (Error.captureStackTrace) {
149
+ Error.captureStackTrace(this, _NetworkError);
150
+ }
151
+ }
152
+ };
153
+
154
+ // src/pagination.ts
155
+ function createPaginatedIterator(options) {
156
+ const { params, fetchPage, startPage = 1, maxPages } = options;
157
+ return {
158
+ [Symbol.asyncIterator]() {
159
+ let currentPage = startPage;
160
+ let currentItems = [];
161
+ let currentIndex = 0;
162
+ let pagination = null;
163
+ let pagesFetched = 0;
164
+ let done = false;
165
+ return {
166
+ async next() {
167
+ while (currentIndex >= currentItems.length && !done) {
168
+ if (maxPages !== void 0 && pagesFetched >= maxPages) {
169
+ done = true;
170
+ break;
171
+ }
172
+ if (pagination !== null && currentPage > pagination.pages) {
173
+ done = true;
174
+ break;
175
+ }
176
+ const result = await fetchPage({
177
+ ...params,
178
+ page: currentPage
179
+ });
180
+ pagination = result.response.pagination;
181
+ currentItems = result.response.data;
182
+ currentIndex = 0;
183
+ currentPage++;
184
+ pagesFetched++;
185
+ if (currentItems.length === 0) {
186
+ done = true;
187
+ break;
188
+ }
189
+ }
190
+ if (currentIndex < currentItems.length) {
191
+ const item = currentItems[currentIndex];
192
+ currentIndex++;
193
+ if (item === void 0) {
194
+ return { done: true, value: void 0 };
195
+ }
196
+ return { done: false, value: item };
197
+ }
198
+ return { done: true, value: void 0 };
199
+ }
200
+ };
201
+ }
202
+ };
203
+ }
204
+ function createPaginatedIteratorWithMetadata(options) {
205
+ const { params, fetchPage, startPage = 1, maxPages } = options;
206
+ return {
207
+ [Symbol.asyncIterator]() {
208
+ let currentPage = startPage;
209
+ let currentItems = [];
210
+ let currentIndex = 0;
211
+ let pagination = null;
212
+ let rateLimit = null;
213
+ let pagesFetched = 0;
214
+ let done = false;
215
+ return {
216
+ async next() {
217
+ while (currentIndex >= currentItems.length && !done) {
218
+ if (maxPages !== void 0 && pagesFetched >= maxPages) {
219
+ done = true;
220
+ break;
221
+ }
222
+ if (pagination !== null && currentPage > pagination.pages) {
223
+ done = true;
224
+ break;
225
+ }
226
+ const result = await fetchPage({
227
+ ...params,
228
+ page: currentPage
229
+ });
230
+ pagination = result.response.pagination;
231
+ rateLimit = result.rateLimit;
232
+ currentItems = result.response.data;
233
+ currentIndex = 0;
234
+ currentPage++;
235
+ pagesFetched++;
236
+ if (currentItems.length === 0) {
237
+ done = true;
238
+ break;
239
+ }
240
+ }
241
+ if (currentIndex < currentItems.length) {
242
+ const item = currentItems[currentIndex];
243
+ if (item === void 0) {
244
+ return { done: true, value: void 0 };
245
+ }
246
+ const result = {
247
+ item,
248
+ page: currentPage - 1,
249
+ // We already incremented after fetch
250
+ indexInPage: currentIndex,
251
+ total: pagination?.total ?? 0,
252
+ rateLimit
253
+ };
254
+ currentIndex++;
255
+ return { done: false, value: result };
256
+ }
257
+ return { done: true, value: void 0 };
258
+ }
259
+ };
260
+ }
261
+ };
262
+ }
263
+ async function collectAll(iterator, maxItems) {
264
+ const items = [];
265
+ for await (const item of iterator) {
266
+ items.push(item);
267
+ if (maxItems !== void 0 && items.length >= maxItems) {
268
+ break;
269
+ }
270
+ }
271
+ return items;
272
+ }
273
+ async function take(iterator, count) {
274
+ return collectAll(iterator, count);
275
+ }
276
+
277
+ // src/client.ts
278
+ var DEFAULT_BASE_URL = "https://app.colacloud.us/api/v1";
279
+ var DEFAULT_TIMEOUT = 3e4;
280
+ function toSnakeCase(params) {
281
+ const result = {};
282
+ for (const [key, value] of Object.entries(params)) {
283
+ if (value === void 0 || value === null) continue;
284
+ const snakeKey = key.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase();
285
+ result[snakeKey] = String(value);
286
+ }
287
+ return result;
288
+ }
289
+ function parseRateLimitHeaders(headers) {
290
+ const limit = headers.get("X-RateLimit-Limit");
291
+ const remaining = headers.get("X-RateLimit-Remaining");
292
+ const reset = headers.get("X-RateLimit-Reset");
293
+ if (limit === null || remaining === null || reset === null) {
294
+ return null;
295
+ }
296
+ return {
297
+ limit: parseInt(limit, 10),
298
+ remaining: parseInt(remaining, 10),
299
+ reset: parseInt(reset, 10)
300
+ };
301
+ }
302
+ var ColaCloud = class {
303
+ apiKey;
304
+ baseUrl;
305
+ timeout;
306
+ /** COLA (Certificate of Label Approval) endpoints */
307
+ colas;
308
+ /** Permittee (business/permit holder) endpoints */
309
+ permittees;
310
+ /** Barcode lookup endpoint */
311
+ barcodes;
312
+ /** Usage statistics endpoint */
313
+ usage;
314
+ /**
315
+ * Create a new COLA Cloud API client
316
+ * @param config Configuration options
317
+ */
318
+ constructor(config) {
319
+ if (!config.apiKey) {
320
+ throw new Error("API key is required");
321
+ }
322
+ this.apiKey = config.apiKey;
323
+ this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
324
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
325
+ this.colas = new ColasResource(this);
326
+ this.permittees = new PermitteesResource(this);
327
+ this.barcodes = new BarcodesResource(this);
328
+ this.usage = new UsageResource(this);
329
+ }
330
+ /**
331
+ * Make an authenticated request to the API
332
+ * @internal
333
+ */
334
+ async request(method, path, params) {
335
+ let url = `${this.baseUrl}${path}`;
336
+ if (method === "GET" && params && Object.keys(params).length > 0) {
337
+ const searchParams = new URLSearchParams(toSnakeCase(params));
338
+ url += `?${searchParams.toString()}`;
339
+ }
340
+ const controller = new AbortController();
341
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
342
+ try {
343
+ const response = await fetch(url, {
344
+ method,
345
+ headers: {
346
+ "X-API-Key": this.apiKey,
347
+ "Content-Type": "application/json",
348
+ Accept: "application/json"
349
+ },
350
+ signal: controller.signal
351
+ });
352
+ clearTimeout(timeoutId);
353
+ const rateLimit = parseRateLimitHeaders(response.headers);
354
+ if (!response.ok) {
355
+ await this.handleErrorResponse(response, rateLimit);
356
+ }
357
+ const data = await response.json();
358
+ return { data, rateLimit };
359
+ } catch (error) {
360
+ clearTimeout(timeoutId);
361
+ if (error instanceof Error && error.name === "AbortError") {
362
+ throw new TimeoutError(this.timeout);
363
+ }
364
+ if (error instanceof ColaCloudError) {
365
+ throw error;
366
+ }
367
+ throw new NetworkError(
368
+ error instanceof Error ? error.message : "Unknown error",
369
+ error instanceof Error ? error : void 0
370
+ );
371
+ }
372
+ }
373
+ /**
374
+ * Handle error responses from the API
375
+ */
376
+ async handleErrorResponse(response, rateLimit) {
377
+ let errorData;
378
+ try {
379
+ errorData = await response.json();
380
+ } catch {
381
+ errorData = { error: response.statusText || "Unknown error" };
382
+ }
383
+ const message = errorData.error || "Unknown error";
384
+ switch (response.status) {
385
+ case 401:
386
+ throw new AuthenticationError(message);
387
+ case 404:
388
+ throw new NotFoundError("Resource", "unknown");
389
+ case 429: {
390
+ const retryAfter = response.headers.get("Retry-After");
391
+ throw new RateLimitError(
392
+ message,
393
+ rateLimit,
394
+ retryAfter ? parseInt(retryAfter, 10) : null
395
+ );
396
+ }
397
+ case 400:
398
+ throw new ValidationError(
399
+ message,
400
+ errorData.details
401
+ );
402
+ default:
403
+ if (response.status >= 500) {
404
+ throw new ServerError(message, response.status);
405
+ }
406
+ throw ColaCloudError.fromResponse(errorData, response.status);
407
+ }
408
+ }
409
+ };
410
+ var ColasResource = class {
411
+ constructor(client) {
412
+ this.client = client;
413
+ }
414
+ /**
415
+ * List and search COLAs with pagination
416
+ * @param params Search and filter parameters
417
+ * @returns Paginated list of COLA summaries
418
+ */
419
+ async list(params = {}) {
420
+ const result = await this.client.request(
421
+ "GET",
422
+ "/colas",
423
+ params
424
+ );
425
+ return result.data;
426
+ }
427
+ /**
428
+ * List COLAs with rate limit information
429
+ * @param params Search and filter parameters
430
+ * @returns Paginated list with rate limit info
431
+ */
432
+ async listWithRateLimit(params = {}) {
433
+ return this.client.request(
434
+ "GET",
435
+ "/colas",
436
+ params
437
+ );
438
+ }
439
+ /**
440
+ * Get a single COLA by TTB ID
441
+ * @param ttbId The unique TTB identifier
442
+ * @returns Full COLA details including images and barcodes
443
+ */
444
+ async get(ttbId) {
445
+ try {
446
+ const result = await this.client.request(
447
+ "GET",
448
+ `/colas/${encodeURIComponent(ttbId)}`
449
+ );
450
+ return result.data.data;
451
+ } catch (error) {
452
+ if (error instanceof NotFoundError) {
453
+ throw new NotFoundError("COLA", ttbId);
454
+ }
455
+ throw error;
456
+ }
457
+ }
458
+ /**
459
+ * Get a single COLA with rate limit information
460
+ * @param ttbId The unique TTB identifier
461
+ * @returns COLA details with rate limit info
462
+ */
463
+ async getWithRateLimit(ttbId) {
464
+ try {
465
+ const result = await this.client.request(
466
+ "GET",
467
+ `/colas/${encodeURIComponent(ttbId)}`
468
+ );
469
+ return { data: result.data.data, rateLimit: result.rateLimit };
470
+ } catch (error) {
471
+ if (error instanceof NotFoundError) {
472
+ throw new NotFoundError("COLA", ttbId);
473
+ }
474
+ throw error;
475
+ }
476
+ }
477
+ /**
478
+ * Create an async iterator that automatically pages through all results
479
+ * @param params Search and filter parameters (page is ignored)
480
+ * @returns Async iterable that yields individual COLA summaries
481
+ *
482
+ * @example
483
+ * ```typescript
484
+ * for await (const cola of client.colas.iterate({ q: 'bourbon' })) {
485
+ * console.log(cola.ttb_id, cola.brand_name);
486
+ * }
487
+ * ```
488
+ */
489
+ iterate(params = {}) {
490
+ return createPaginatedIterator({
491
+ params,
492
+ fetchPage: async (p) => {
493
+ const result = await this.client.request(
494
+ "GET",
495
+ "/colas",
496
+ p
497
+ );
498
+ return { response: result.data, rateLimit: result.rateLimit };
499
+ }
500
+ });
501
+ }
502
+ };
503
+ var PermitteesResource = class {
504
+ constructor(client) {
505
+ this.client = client;
506
+ }
507
+ /**
508
+ * List and search permittees with pagination
509
+ * @param params Search and filter parameters
510
+ * @returns Paginated list of permittee summaries
511
+ */
512
+ async list(params = {}) {
513
+ const result = await this.client.request(
514
+ "GET",
515
+ "/permittees",
516
+ params
517
+ );
518
+ return result.data;
519
+ }
520
+ /**
521
+ * List permittees with rate limit information
522
+ * @param params Search and filter parameters
523
+ * @returns Paginated list with rate limit info
524
+ */
525
+ async listWithRateLimit(params = {}) {
526
+ return this.client.request(
527
+ "GET",
528
+ "/permittees",
529
+ params
530
+ );
531
+ }
532
+ /**
533
+ * Get a single permittee by permit number
534
+ * @param permitNumber The unique permit number
535
+ * @returns Full permittee details including recent COLAs
536
+ */
537
+ async get(permitNumber) {
538
+ try {
539
+ const result = await this.client.request(
540
+ "GET",
541
+ `/permittees/${encodeURIComponent(permitNumber)}`
542
+ );
543
+ return result.data.data;
544
+ } catch (error) {
545
+ if (error instanceof NotFoundError) {
546
+ throw new NotFoundError("Permittee", permitNumber);
547
+ }
548
+ throw error;
549
+ }
550
+ }
551
+ /**
552
+ * Get a single permittee with rate limit information
553
+ * @param permitNumber The unique permit number
554
+ * @returns Permittee details with rate limit info
555
+ */
556
+ async getWithRateLimit(permitNumber) {
557
+ try {
558
+ const result = await this.client.request(
559
+ "GET",
560
+ `/permittees/${encodeURIComponent(permitNumber)}`
561
+ );
562
+ return { data: result.data.data, rateLimit: result.rateLimit };
563
+ } catch (error) {
564
+ if (error instanceof NotFoundError) {
565
+ throw new NotFoundError("Permittee", permitNumber);
566
+ }
567
+ throw error;
568
+ }
569
+ }
570
+ /**
571
+ * Create an async iterator that automatically pages through all results
572
+ * @param params Search and filter parameters (page is ignored)
573
+ * @returns Async iterable that yields individual permittee summaries
574
+ *
575
+ * @example
576
+ * ```typescript
577
+ * for await (const permittee of client.permittees.iterate({ state: 'CA' })) {
578
+ * console.log(permittee.company_name);
579
+ * }
580
+ * ```
581
+ */
582
+ iterate(params = {}) {
583
+ return createPaginatedIterator({
584
+ params,
585
+ fetchPage: async (p) => {
586
+ const result = await this.client.request("GET", "/permittees", p);
587
+ return { response: result.data, rateLimit: result.rateLimit };
588
+ }
589
+ });
590
+ }
591
+ };
592
+ var BarcodesResource = class {
593
+ constructor(client) {
594
+ this.client = client;
595
+ }
596
+ /**
597
+ * Look up COLAs by barcode value
598
+ * @param barcodeValue The barcode to search for (UPC, EAN, etc.)
599
+ * @returns Barcode information and associated COLAs
600
+ */
601
+ async lookup(barcodeValue) {
602
+ try {
603
+ const result = await this.client.request("GET", `/barcode/${encodeURIComponent(barcodeValue)}`);
604
+ return result.data.data;
605
+ } catch (error) {
606
+ if (error instanceof NotFoundError) {
607
+ throw new NotFoundError("Barcode", barcodeValue);
608
+ }
609
+ throw error;
610
+ }
611
+ }
612
+ /**
613
+ * Look up barcode with rate limit information
614
+ * @param barcodeValue The barcode to search for
615
+ * @returns Barcode lookup result with rate limit info
616
+ */
617
+ async lookupWithRateLimit(barcodeValue) {
618
+ try {
619
+ const result = await this.client.request("GET", `/barcode/${encodeURIComponent(barcodeValue)}`);
620
+ return { data: result.data.data, rateLimit: result.rateLimit };
621
+ } catch (error) {
622
+ if (error instanceof NotFoundError) {
623
+ throw new NotFoundError("Barcode", barcodeValue);
624
+ }
625
+ throw error;
626
+ }
627
+ }
628
+ };
629
+ var UsageResource = class {
630
+ constructor(client) {
631
+ this.client = client;
632
+ }
633
+ /**
634
+ * Get API usage statistics for the current account
635
+ * @returns Usage statistics including limits and current usage
636
+ */
637
+ async get() {
638
+ const result = await this.client.request(
639
+ "GET",
640
+ "/usage"
641
+ );
642
+ return result.data.data;
643
+ }
644
+ /**
645
+ * Get usage statistics with rate limit information
646
+ * @returns Usage statistics with rate limit info
647
+ */
648
+ async getWithRateLimit() {
649
+ const result = await this.client.request(
650
+ "GET",
651
+ "/usage"
652
+ );
653
+ return { data: result.data.data, rateLimit: result.rateLimit };
654
+ }
655
+ };
656
+ // Annotate the CommonJS export names for ESM import in node:
657
+ 0 && (module.exports = {
658
+ AuthenticationError,
659
+ ColaCloud,
660
+ ColaCloudError,
661
+ NetworkError,
662
+ NotFoundError,
663
+ RateLimitError,
664
+ ServerError,
665
+ TimeoutError,
666
+ ValidationError,
667
+ collectAll,
668
+ createPaginatedIterator,
669
+ createPaginatedIteratorWithMetadata,
670
+ take
671
+ });