@chadwin/sdk 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/dist/index.cjs ADDED
@@ -0,0 +1,729 @@
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
+ APIError: () => APIError,
24
+ Chadwin: () => Chadwin
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/error.ts
29
+ var APIError = class extends Error {
30
+ status;
31
+ code;
32
+ headers;
33
+ constructor(message, options = {}) {
34
+ super(message);
35
+ this.name = "APIError";
36
+ this.status = options.status;
37
+ this.code = options.code;
38
+ this.headers = options.headers;
39
+ }
40
+ };
41
+
42
+ // src/promise.ts
43
+ function createPromiseWithResponse(response) {
44
+ const promise = response.then(({ data }) => data);
45
+ Object.defineProperty(promise, "withResponse", {
46
+ value: () => response
47
+ });
48
+ void promise.catch(() => void 0);
49
+ return promise;
50
+ }
51
+
52
+ // src/internal/transport.ts
53
+ var ATTEMPT_TIMEOUT_MS = 3e4;
54
+ var MAX_RETRIES = 2;
55
+ var BASE_RETRY_DELAY_MS = 250;
56
+ var MAX_RETRY_DELAY_MS = 3e4;
57
+ var PRODUCTION_BASE_URL = "https://api.chadwin.co";
58
+ var INVALID_BASE_URL_MESSAGE = "baseURL must be an HTTP or HTTPS URL without credentials, a query, or a fragment";
59
+ var SAFE_ERROR_CODE = /^[a-z][a-z0-9_]{0,63}$/;
60
+ var AttemptFailure = class extends Error {
61
+ constructor(timedOut) {
62
+ super(timedOut ? "Request timed out" : "Network request failed");
63
+ this.timedOut = timedOut;
64
+ }
65
+ timedOut;
66
+ };
67
+ var HttpTransport = class {
68
+ #apiKey;
69
+ #fetch;
70
+ #baseURL;
71
+ constructor(options) {
72
+ this.#apiKey = options.apiKey;
73
+ this.#baseURL = normalizeBaseURL(options.baseURL ?? PRODUCTION_BASE_URL);
74
+ this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
75
+ }
76
+ request(request) {
77
+ return createPromiseWithResponse(this.#performRequest(request));
78
+ }
79
+ async #performRequest(request) {
80
+ const url = buildUrl(this.#baseURL, request);
81
+ let lastFailureWasTimeout = false;
82
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {
83
+ let result;
84
+ try {
85
+ result = await this.#attempt(url, request.responseType ?? "json");
86
+ } catch (error) {
87
+ if (error instanceof APIError) {
88
+ throw error;
89
+ }
90
+ if (!(error instanceof AttemptFailure)) {
91
+ throw new APIError("API request failed");
92
+ }
93
+ lastFailureWasTimeout = error.timedOut;
94
+ if (attempt === MAX_RETRIES) {
95
+ throw new APIError(
96
+ lastFailureWasTimeout ? "API request timed out" : "API request failed"
97
+ );
98
+ }
99
+ await wait(backoffDelay(attempt));
100
+ continue;
101
+ }
102
+ if ("data" in result) {
103
+ return result;
104
+ }
105
+ if (shouldRetry(result.response.status, result.code) && attempt < MAX_RETRIES) {
106
+ await wait(retryDelay(result.response, attempt));
107
+ continue;
108
+ }
109
+ throw errorFromResponse(result.response, result.code, this.#apiKey);
110
+ }
111
+ throw new APIError(
112
+ lastFailureWasTimeout ? "API request timed out" : "API request failed"
113
+ );
114
+ }
115
+ async #attempt(url, responseType) {
116
+ const controller = new AbortController();
117
+ let timedOut = false;
118
+ const timeout = setTimeout(() => {
119
+ timedOut = true;
120
+ controller.abort();
121
+ }, ATTEMPT_TIMEOUT_MS);
122
+ try {
123
+ let response;
124
+ try {
125
+ response = await this.#fetch(url, {
126
+ headers: {
127
+ authorization: `Bearer ${this.#apiKey}`
128
+ },
129
+ method: "GET",
130
+ signal: controller.signal
131
+ });
132
+ } catch {
133
+ throw new AttemptFailure(timedOut);
134
+ }
135
+ if (!response.ok) {
136
+ return {
137
+ code: await readErrorCode(response, this.#apiKey),
138
+ response
139
+ };
140
+ }
141
+ try {
142
+ const data = responseType === "text" ? await response.text() : await response.json();
143
+ return { data, response };
144
+ } catch (error) {
145
+ if (responseType === "json" && error instanceof SyntaxError) {
146
+ throw new APIError("API returned an invalid JSON response", {
147
+ headers: safeErrorHeaders(response.headers, this.#apiKey),
148
+ status: response.status
149
+ });
150
+ }
151
+ throw new APIError("API returned an invalid response body", {
152
+ headers: safeErrorHeaders(response.headers, this.#apiKey),
153
+ status: response.status
154
+ });
155
+ }
156
+ } finally {
157
+ clearTimeout(timeout);
158
+ }
159
+ }
160
+ };
161
+ function normalizeBaseURL(value) {
162
+ let url;
163
+ try {
164
+ url = new URL(value);
165
+ } catch {
166
+ throw new TypeError(INVALID_BASE_URL_MESSAGE);
167
+ }
168
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username || url.password || url.href !== `${url.origin}${url.pathname}`) {
169
+ throw new TypeError(INVALID_BASE_URL_MESSAGE);
170
+ }
171
+ url.pathname = `${url.pathname.replace(/\/+$/, "")}/`;
172
+ return url;
173
+ }
174
+ function buildUrl(baseURL, request) {
175
+ const path = request.path.map((segment) => encodeURIComponent(segment)).join("/");
176
+ const url = new URL(path, baseURL);
177
+ for (const [key, value] of Object.entries(request.query ?? {})) {
178
+ if (value === void 0 || value === null) continue;
179
+ const values = Array.isArray(value) ? value : [value];
180
+ for (const entry of values) {
181
+ url.searchParams.append(key, String(entry));
182
+ }
183
+ }
184
+ return url;
185
+ }
186
+ async function readErrorCode(response, apiKey) {
187
+ try {
188
+ const body = await response.json();
189
+ if (body !== null && typeof body === "object" && "error" in body && typeof body.error === "string" && body.error !== apiKey && SAFE_ERROR_CODE.test(body.error)) {
190
+ return body.error;
191
+ }
192
+ } catch {
193
+ }
194
+ return void 0;
195
+ }
196
+ function shouldRetry(status, code) {
197
+ if (code === "billing_period_quota_exceeded") return false;
198
+ return code === "rate_limited" || status === 502 || status === 503 || status === 504;
199
+ }
200
+ function retryDelay(response, retry) {
201
+ const retryAfter = response.headers.get("retry-after");
202
+ if (retryAfter === null) return backoffDelay(retry);
203
+ const seconds = Number(retryAfter);
204
+ if (Number.isFinite(seconds) && seconds >= 0) {
205
+ return Math.min(seconds * 1e3, MAX_RETRY_DELAY_MS);
206
+ }
207
+ const date = Date.parse(retryAfter);
208
+ if (!Number.isNaN(date)) {
209
+ return Math.min(Math.max(0, date - Date.now()), MAX_RETRY_DELAY_MS);
210
+ }
211
+ return backoffDelay(retry);
212
+ }
213
+ function backoffDelay(retry) {
214
+ return Math.min(BASE_RETRY_DELAY_MS * 2 ** retry, MAX_RETRY_DELAY_MS);
215
+ }
216
+ function wait(milliseconds) {
217
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
218
+ }
219
+ function errorFromResponse(response, code, apiKey) {
220
+ const suffix = code ? ` (${code})` : "";
221
+ return new APIError(
222
+ `API request failed with status ${response.status}${suffix}`,
223
+ {
224
+ ...code === void 0 ? {} : { code },
225
+ headers: safeErrorHeaders(response.headers, apiKey),
226
+ status: response.status
227
+ }
228
+ );
229
+ }
230
+ function safeErrorHeaders(headers, apiKey) {
231
+ const safe = new Headers();
232
+ for (const [name, value] of headers) {
233
+ const normalizedName = name.toLowerCase();
234
+ if (normalizedName === "authorization" || normalizedName === "cookie" || normalizedName === "proxy-authorization" || normalizedName === "set-cookie" || value.includes(apiKey)) {
235
+ continue;
236
+ }
237
+ safe.append(name, value);
238
+ }
239
+ return safe;
240
+ }
241
+
242
+ // src/resources/feeds.ts
243
+ var Feeds = class {
244
+ companyReports;
245
+ insiderActivity;
246
+ insiderTransactions;
247
+ institutionalFilings;
248
+ constructor(transport) {
249
+ this.companyReports = new CompanyReportFeed(transport);
250
+ this.insiderActivity = new InsiderActivityFeed(transport);
251
+ this.insiderTransactions = new InsiderTransactionFeed(transport);
252
+ this.institutionalFilings = new InstitutionalFilingFeed(transport);
253
+ }
254
+ };
255
+ var CompanyReportFeed = class {
256
+ constructor(transport) {
257
+ this.transport = transport;
258
+ }
259
+ transport;
260
+ listLatest(filters = {}) {
261
+ return this.#request(filters, {});
262
+ }
263
+ listAfter({
264
+ cursor,
265
+ ...filters
266
+ }) {
267
+ return this.#request(filters, { cursor });
268
+ }
269
+ startFromNow(filters = {}) {
270
+ return this.#request(filters, { start: "now" });
271
+ }
272
+ #request(filters, mode) {
273
+ return this.transport.request({
274
+ path: ["v1", "company-reports", "feed"],
275
+ query: {
276
+ ...mode,
277
+ forms: filters.forms?.join(","),
278
+ limit: filters.limit
279
+ }
280
+ });
281
+ }
282
+ };
283
+ var InsiderActivityFeed = class {
284
+ constructor(transport) {
285
+ this.transport = transport;
286
+ }
287
+ transport;
288
+ listLatest(filters = {}) {
289
+ return this.#request(filters, {});
290
+ }
291
+ listAfter({
292
+ cursor,
293
+ ...filters
294
+ }) {
295
+ return this.#request(filters, { cursor });
296
+ }
297
+ startFromNow(filters = {}) {
298
+ return this.#request(filters, { start: "now" });
299
+ }
300
+ #request(filters, mode) {
301
+ return this.transport.request({
302
+ path: ["v1", "insider-activity", "feed"],
303
+ query: {
304
+ ...mode,
305
+ forms: filters.forms?.join(","),
306
+ limit: filters.limit
307
+ }
308
+ });
309
+ }
310
+ };
311
+ var InsiderTransactionFeed = class {
312
+ constructor(transport) {
313
+ this.transport = transport;
314
+ }
315
+ transport;
316
+ listLatest(filters = {}) {
317
+ return this.#request(filters, {});
318
+ }
319
+ listAfter({
320
+ cursor,
321
+ ...filters
322
+ }) {
323
+ return this.#request(filters, { cursor });
324
+ }
325
+ startFromNow(filters = {}) {
326
+ return this.#request(filters, { start: "now" });
327
+ }
328
+ #request(filters, mode) {
329
+ return this.transport.request({
330
+ path: ["v1", "insider-transactions", "feed"],
331
+ query: {
332
+ ...mode,
333
+ limit: filters.limit,
334
+ transaction_codes: filters.transactionCodes?.join(",")
335
+ }
336
+ });
337
+ }
338
+ };
339
+ var InstitutionalFilingFeed = class {
340
+ constructor(transport) {
341
+ this.transport = transport;
342
+ }
343
+ transport;
344
+ listLatest(filters = {}) {
345
+ return this.#request(filters, {});
346
+ }
347
+ listAfter({
348
+ cursor,
349
+ ...filters
350
+ }) {
351
+ return this.#request(filters, { cursor });
352
+ }
353
+ startFromNow(filters = {}) {
354
+ return this.#request(filters, { start: "now" });
355
+ }
356
+ #request(filters, mode) {
357
+ return this.transport.request({
358
+ path: ["v1", "institutional-holdings", "feed"],
359
+ query: {
360
+ ...mode,
361
+ forms: filters.forms?.join(","),
362
+ limit: filters.limit
363
+ }
364
+ });
365
+ }
366
+ };
367
+
368
+ // src/resources/products.ts
369
+ var Companies = class {
370
+ constructor(transport) {
371
+ this.transport = transport;
372
+ }
373
+ transport;
374
+ search({ query }) {
375
+ return this.transport.request({
376
+ path: ["v1", "companies", "US", "search"],
377
+ query: { query }
378
+ });
379
+ }
380
+ get({ ticker }) {
381
+ return this.transport.request({
382
+ path: ["v1", "companies", "US", ticker]
383
+ });
384
+ }
385
+ };
386
+ var CompanyReports = class {
387
+ constructor(transport) {
388
+ this.transport = transport;
389
+ }
390
+ transport;
391
+ get({
392
+ accessionNumber
393
+ }) {
394
+ return this.transport.request({
395
+ path: ["v1", "sec", "company-reports", accessionNumber]
396
+ });
397
+ }
398
+ getByFiscalYear({
399
+ ticker,
400
+ fiscalYear
401
+ }) {
402
+ return this.transport.request({
403
+ path: [
404
+ "v1",
405
+ "companies",
406
+ "US",
407
+ ticker,
408
+ "reports",
409
+ "annual",
410
+ String(fiscalYear)
411
+ ]
412
+ });
413
+ }
414
+ getHtml({
415
+ accessionNumber
416
+ }) {
417
+ return this.transport.request({
418
+ path: [
419
+ "v1",
420
+ "sec",
421
+ "company-reports",
422
+ accessionNumber,
423
+ "content.html"
424
+ ],
425
+ responseType: "text"
426
+ });
427
+ }
428
+ };
429
+ var InsiderTransactions = class {
430
+ constructor(transport) {
431
+ this.transport = transport;
432
+ }
433
+ transport;
434
+ list({
435
+ filingDateFrom,
436
+ filingDateUntil,
437
+ tickers,
438
+ transactionCodes
439
+ }) {
440
+ return this.transport.request({
441
+ path: ["v1", "insider-transactions"],
442
+ query: {
443
+ from: filingDateFrom,
444
+ tickers: commaSeparated(tickers),
445
+ transaction_codes: commaSeparated(transactionCodes),
446
+ until: filingDateUntil
447
+ }
448
+ });
449
+ }
450
+ listForCompany({
451
+ ticker,
452
+ filingDateFrom,
453
+ filingDateUntil,
454
+ transactionCodes
455
+ }) {
456
+ return this.transport.request({
457
+ path: ["v1", "companies", "US", ticker, "insider-transactions"],
458
+ query: {
459
+ from: filingDateFrom,
460
+ transaction_codes: commaSeparated(transactionCodes),
461
+ until: filingDateUntil
462
+ }
463
+ });
464
+ }
465
+ };
466
+ var InsiderFilings = class {
467
+ constructor(transport) {
468
+ this.transport = transport;
469
+ }
470
+ transport;
471
+ get({
472
+ accessionNumber
473
+ }) {
474
+ return this.transport.request({
475
+ path: ["v1", "sec", "insider-filings", accessionNumber]
476
+ });
477
+ }
478
+ };
479
+ var ProposedSales = class {
480
+ constructor(transport) {
481
+ this.transport = transport;
482
+ }
483
+ transport;
484
+ list({
485
+ filingDateFrom,
486
+ filingDateUntil,
487
+ tickers
488
+ }) {
489
+ return this.transport.request({
490
+ path: ["v1", "proposed-sales"],
491
+ query: {
492
+ from: filingDateFrom,
493
+ tickers: commaSeparated(tickers),
494
+ until: filingDateUntil
495
+ }
496
+ });
497
+ }
498
+ listForCompany({
499
+ ticker,
500
+ filingDateFrom,
501
+ filingDateUntil
502
+ }) {
503
+ return this.transport.request({
504
+ path: ["v1", "companies", "US", ticker, "proposed-sales"],
505
+ query: { from: filingDateFrom, until: filingDateUntil }
506
+ });
507
+ }
508
+ getFiling({
509
+ accessionNumber
510
+ }) {
511
+ return this.transport.request({
512
+ path: ["v1", "sec", "proposed-sales", accessionNumber]
513
+ });
514
+ }
515
+ };
516
+ var InstitutionalFilings = class {
517
+ constructor(transport) {
518
+ this.transport = transport;
519
+ }
520
+ transport;
521
+ get({
522
+ accessionNumber
523
+ }) {
524
+ return this.transport.request({
525
+ path: ["v1", "sec", "institutional-filings", accessionNumber]
526
+ });
527
+ }
528
+ listHoldings({
529
+ accessionNumber,
530
+ cursor,
531
+ limit
532
+ }) {
533
+ return this.transport.request({
534
+ path: [
535
+ "v1",
536
+ "sec",
537
+ "institutional-filings",
538
+ accessionNumber,
539
+ "holdings"
540
+ ],
541
+ query: { cursor, limit }
542
+ });
543
+ }
544
+ async *iterateHoldings({
545
+ accessionNumber,
546
+ limit
547
+ }) {
548
+ let cursor;
549
+ do {
550
+ const page = await this.listHoldings({
551
+ accessionNumber,
552
+ ...cursor === void 0 ? {} : { cursor },
553
+ ...limit === void 0 ? {} : { limit }
554
+ });
555
+ yield* page.holdings;
556
+ cursor = page.next_cursor ?? void 0;
557
+ } while (cursor !== void 0);
558
+ }
559
+ };
560
+ var InstitutionalManagers = class {
561
+ constructor(transport) {
562
+ this.transport = transport;
563
+ }
564
+ transport;
565
+ list({
566
+ cik,
567
+ form13fFileNumber,
568
+ cursor,
569
+ limit
570
+ } = {}) {
571
+ return this.transport.request({
572
+ path: ["v1", "institutional-holdings", "managers"],
573
+ query: {
574
+ cik,
575
+ cursor,
576
+ form_13f_file_number: form13fFileNumber,
577
+ limit
578
+ }
579
+ });
580
+ }
581
+ get({
582
+ form13fFileNumber
583
+ }) {
584
+ return this.transport.request({
585
+ path: [
586
+ "v1",
587
+ "institutional-holdings",
588
+ "managers",
589
+ form13fFileNumber
590
+ ]
591
+ });
592
+ }
593
+ listPeriods({
594
+ form13fFileNumber
595
+ }) {
596
+ return this.transport.request({
597
+ path: [
598
+ "v1",
599
+ "institutional-holdings",
600
+ "managers",
601
+ form13fFileNumber,
602
+ "periods"
603
+ ]
604
+ });
605
+ }
606
+ listHoldings({
607
+ form13fFileNumber,
608
+ reportPeriod,
609
+ cursor,
610
+ limit
611
+ }) {
612
+ return this.transport.request({
613
+ path: [
614
+ "v1",
615
+ "institutional-holdings",
616
+ "managers",
617
+ form13fFileNumber,
618
+ "holdings"
619
+ ],
620
+ query: { cursor, limit, period: reportPeriod }
621
+ });
622
+ }
623
+ listPositions({
624
+ form13fFileNumber,
625
+ reportPeriod,
626
+ cursor,
627
+ limit
628
+ }) {
629
+ return this.transport.request({
630
+ path: [
631
+ "v1",
632
+ "institutional-holdings",
633
+ "managers",
634
+ form13fFileNumber,
635
+ "positions"
636
+ ],
637
+ query: { cursor, limit, period: reportPeriod }
638
+ });
639
+ }
640
+ async *iterate({
641
+ cik,
642
+ form13fFileNumber,
643
+ limit
644
+ } = {}) {
645
+ let cursor;
646
+ do {
647
+ const page = await this.list({
648
+ ...cik === void 0 ? {} : { cik },
649
+ ...cursor === void 0 ? {} : { cursor },
650
+ ...form13fFileNumber === void 0 ? {} : { form13fFileNumber },
651
+ ...limit === void 0 ? {} : { limit }
652
+ });
653
+ yield* page.managers;
654
+ cursor = page.next_cursor ?? void 0;
655
+ } while (cursor !== void 0);
656
+ }
657
+ async *iterateHoldings({
658
+ form13fFileNumber,
659
+ reportPeriod,
660
+ limit
661
+ }) {
662
+ let cursor;
663
+ do {
664
+ const page = await this.listHoldings({
665
+ form13fFileNumber,
666
+ reportPeriod,
667
+ ...cursor === void 0 ? {} : { cursor },
668
+ ...limit === void 0 ? {} : { limit }
669
+ });
670
+ yield* page.holdings;
671
+ cursor = page.next_cursor ?? void 0;
672
+ } while (cursor !== void 0);
673
+ }
674
+ async *iteratePositions({
675
+ form13fFileNumber,
676
+ reportPeriod,
677
+ limit
678
+ }) {
679
+ let cursor;
680
+ do {
681
+ const page = await this.listPositions({
682
+ form13fFileNumber,
683
+ reportPeriod,
684
+ ...cursor === void 0 ? {} : { cursor },
685
+ ...limit === void 0 ? {} : { limit }
686
+ });
687
+ yield* page.positions;
688
+ cursor = page.next_cursor ?? void 0;
689
+ } while (cursor !== void 0);
690
+ }
691
+ };
692
+ function commaSeparated(values) {
693
+ return values?.join(",");
694
+ }
695
+
696
+ // src/client.ts
697
+ var Chadwin = class {
698
+ companies;
699
+ companyReports;
700
+ feeds;
701
+ insiderFilings;
702
+ insiderTransactions;
703
+ institutionalFilings;
704
+ institutionalManagers;
705
+ proposedSales;
706
+ constructor(options) {
707
+ if (!options || typeof options.apiKey !== "string" || options.apiKey.trim().length === 0) {
708
+ throw new TypeError("A non-empty Chadwin API key is required");
709
+ }
710
+ const transport = new HttpTransport({
711
+ apiKey: options.apiKey,
712
+ ...options.baseURL === void 0 ? {} : { baseURL: options.baseURL }
713
+ });
714
+ this.companies = new Companies(transport);
715
+ this.companyReports = new CompanyReports(transport);
716
+ this.feeds = new Feeds(transport);
717
+ this.insiderFilings = new InsiderFilings(transport);
718
+ this.insiderTransactions = new InsiderTransactions(transport);
719
+ this.institutionalFilings = new InstitutionalFilings(transport);
720
+ this.institutionalManagers = new InstitutionalManagers(transport);
721
+ this.proposedSales = new ProposedSales(transport);
722
+ }
723
+ };
724
+ // Annotate the CommonJS export names for ESM import in node:
725
+ 0 && (module.exports = {
726
+ APIError,
727
+ Chadwin
728
+ });
729
+ //# sourceMappingURL=index.cjs.map