@discomedia/utils 1.0.31 → 1.0.33

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.
@@ -267,7 +267,7 @@ const safeJSON = (text) => {
267
267
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
268
268
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
269
269
 
270
- const VERSION = '5.12.2'; // x-release-please-version
270
+ const VERSION = '5.16.0'; // x-release-please-version
271
271
 
272
272
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
273
273
  const isRunningInBrowser = () => {
@@ -1607,6 +1607,36 @@ class CursorPage extends AbstractPage {
1607
1607
  };
1608
1608
  }
1609
1609
  }
1610
+ class ConversationCursorPage extends AbstractPage {
1611
+ constructor(client, response, body, options) {
1612
+ super(client, response, body, options);
1613
+ this.data = body.data || [];
1614
+ this.has_more = body.has_more || false;
1615
+ this.last_id = body.last_id || '';
1616
+ }
1617
+ getPaginatedItems() {
1618
+ return this.data ?? [];
1619
+ }
1620
+ hasNextPage() {
1621
+ if (this.has_more === false) {
1622
+ return false;
1623
+ }
1624
+ return super.hasNextPage();
1625
+ }
1626
+ nextPageRequestOptions() {
1627
+ const cursor = this.last_id;
1628
+ if (!cursor) {
1629
+ return null;
1630
+ }
1631
+ return {
1632
+ ...this.options,
1633
+ query: {
1634
+ ...maybeObj(this.options.query),
1635
+ after: cursor,
1636
+ },
1637
+ };
1638
+ }
1639
+ }
1610
1640
 
1611
1641
  const checkFileSupport = () => {
1612
1642
  if (typeof File === 'undefined') {
@@ -4697,6 +4727,74 @@ class Containers extends APIResource {
4697
4727
  }
4698
4728
  Containers.Files = Files$2;
4699
4729
 
4730
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4731
+ class Items extends APIResource {
4732
+ /**
4733
+ * Create items in a conversation with the given ID.
4734
+ */
4735
+ create(conversationID, params, options) {
4736
+ const { include, ...body } = params;
4737
+ return this._client.post(path `/conversations/${conversationID}/items`, {
4738
+ query: { include },
4739
+ body,
4740
+ ...options,
4741
+ });
4742
+ }
4743
+ /**
4744
+ * Get a single item from a conversation with the given IDs.
4745
+ */
4746
+ retrieve(itemID, params, options) {
4747
+ const { conversation_id, ...query } = params;
4748
+ return this._client.get(path `/conversations/${conversation_id}/items/${itemID}`, { query, ...options });
4749
+ }
4750
+ /**
4751
+ * List all items for a conversation with the given ID.
4752
+ */
4753
+ list(conversationID, query = {}, options) {
4754
+ return this._client.getAPIList(path `/conversations/${conversationID}/items`, (ConversationCursorPage), { query, ...options });
4755
+ }
4756
+ /**
4757
+ * Delete an item from a conversation with the given IDs.
4758
+ */
4759
+ delete(itemID, params, options) {
4760
+ const { conversation_id } = params;
4761
+ return this._client.delete(path `/conversations/${conversation_id}/items/${itemID}`, options);
4762
+ }
4763
+ }
4764
+
4765
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4766
+ class Conversations extends APIResource {
4767
+ constructor() {
4768
+ super(...arguments);
4769
+ this.items = new Items(this._client);
4770
+ }
4771
+ /**
4772
+ * Create a conversation.
4773
+ */
4774
+ create(body, options) {
4775
+ return this._client.post('/conversations', { body, ...options });
4776
+ }
4777
+ /**
4778
+ * Get a conversation with the given ID.
4779
+ */
4780
+ retrieve(conversationID, options) {
4781
+ return this._client.get(path `/conversations/${conversationID}`, options);
4782
+ }
4783
+ /**
4784
+ * Update a conversation's metadata with the given ID.
4785
+ */
4786
+ update(conversationID, body, options) {
4787
+ return this._client.post(path `/conversations/${conversationID}`, { body, ...options });
4788
+ }
4789
+ /**
4790
+ * Delete a conversation with the given ID.
4791
+ */
4792
+ delete(conversationID, options) {
4793
+ return this._client.delete(path `/conversations/${conversationID}`, options);
4794
+ }
4795
+ }
4796
+ Conversations.Items = Items;
4797
+
4700
4798
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4701
4799
  class Embeddings extends APIResource {
4702
4800
  /**
@@ -4861,7 +4959,7 @@ let Files$1 = class Files extends APIResource {
4861
4959
  /**
4862
4960
  * Upload a file that can be used across various endpoints. Individual files can be
4863
4961
  * up to 512 MB, and the size of all files uploaded by one organization can be up
4864
- * to 100 GB.
4962
+ * to 1 TB.
4865
4963
  *
4866
4964
  * The Assistants API supports files up to 2 million tokens and of specific file
4867
4965
  * types. See the
@@ -6290,6 +6388,7 @@ class OpenAI {
6290
6388
  this.batches = new Batches(this);
6291
6389
  this.uploads = new Uploads(this);
6292
6390
  this.responses = new Responses(this);
6391
+ this.conversations = new Conversations(this);
6293
6392
  this.evals = new Evals(this);
6294
6393
  this.containers = new Containers(this);
6295
6394
  if (apiKey === undefined) {
@@ -6680,7 +6779,7 @@ class OpenAI {
6680
6779
  // Preserve legacy string encoding behavior for now
6681
6780
  headers.values.has('content-type')) ||
6682
6781
  // `Blob` is superset of `File`
6683
- body instanceof Blob ||
6782
+ (globalThis.Blob && body instanceof globalThis.Blob) ||
6684
6783
  // `FormData` -> `multipart/form-data`
6685
6784
  body instanceof FormData ||
6686
6785
  // `URLSearchParams` -> `application/x-www-form-urlencoded`
@@ -6735,6 +6834,7 @@ OpenAI.Beta = Beta;
6735
6834
  OpenAI.Batches = Batches;
6736
6835
  OpenAI.Uploads = Uploads;
6737
6836
  OpenAI.Responses = Responses;
6837
+ OpenAI.Conversations = Conversations;
6738
6838
  OpenAI.Evals = Evals;
6739
6839
  OpenAI.Containers = Containers;
6740
6840
 
@@ -13780,6 +13880,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13780
13880
  dataURL;
13781
13881
  apiURL;
13782
13882
  v1beta1url;
13883
+ v1beta3url;
13783
13884
  stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip'; // production values
13784
13885
  optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // production values
13785
13886
  stockWs = null;
@@ -13822,6 +13923,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
13822
13923
  ? 'https://paper-api.alpaca.markets/v2'
13823
13924
  : 'https://api.alpaca.markets/v2'; // used by some, e.g. getAssets
13824
13925
  this.v1beta1url = 'https://data.alpaca.markets/v1beta1'; // used for options endpoints
13926
+ this.v1beta3url = 'https://data.alpaca.markets/v1beta3'; // used for crypto endpoints
13825
13927
  this.setMode('production'); // sets stockStreamUrl and optionStreamUrl
13826
13928
  this.headers = {
13827
13929
  'APCA-API-KEY-ID': process.env.ALPACA_API_KEY,
@@ -13959,7 +14061,9 @@ class AlpacaMarketDataAPI extends EventEmitter {
13959
14061
  }
13960
14062
  }
13961
14063
  async makeRequest(endpoint, method = 'GET', params, baseUrlName = 'data') {
13962
- const baseUrl = baseUrlName === 'data' ? this.dataURL : baseUrlName === 'api' ? this.apiURL : this.v1beta1url;
14064
+ const baseUrl = baseUrlName === 'data' ? this.dataURL :
14065
+ baseUrlName === 'api' ? this.apiURL :
14066
+ baseUrlName === 'v1beta1' ? this.v1beta1url : this.v1beta3url;
13963
14067
  const url = new URL(`${baseUrl}${endpoint}`);
13964
14068
  try {
13965
14069
  if (params) {
@@ -14667,6 +14771,253 @@ class AlpacaMarketDataAPI extends EventEmitter {
14667
14771
  }
14668
14772
  return newsArticles;
14669
14773
  }
14774
+ // ===== CRYPTO MARKET DATA METHODS =====
14775
+ /**
14776
+ * Get historical OHLCV bars for crypto symbols
14777
+ * Automatically handles pagination to fetch all available data
14778
+ * @param params Parameters for crypto historical bars request
14779
+ * @returns Historical bars data with all pages combined
14780
+ */
14781
+ async getCryptoHistoricalBars(params) {
14782
+ const symbols = params.symbols;
14783
+ const symbolsStr = symbols.join(',');
14784
+ let allBars = {};
14785
+ let pageToken = null;
14786
+ let hasMorePages = true;
14787
+ let totalBarsCount = 0;
14788
+ let pageCount = 0;
14789
+ // Initialize bar arrays for each symbol
14790
+ symbols.forEach((symbol) => {
14791
+ allBars[symbol] = [];
14792
+ });
14793
+ log(`Starting crypto historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
14794
+ while (hasMorePages) {
14795
+ pageCount++;
14796
+ const requestParams = {
14797
+ ...params,
14798
+ symbols: symbolsStr,
14799
+ ...(pageToken && { page_token: pageToken }),
14800
+ };
14801
+ const response = await this.makeRequest('/crypto/us/bars', 'GET', requestParams, 'v1beta3');
14802
+ if (!response.bars) {
14803
+ log(`No crypto bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
14804
+ break;
14805
+ }
14806
+ // Combine bars for each symbol
14807
+ let pageBarsCount = 0;
14808
+ Object.entries(response.bars).forEach(([symbol, bars]) => {
14809
+ if (bars && bars.length > 0) {
14810
+ allBars[symbol] = [...allBars[symbol], ...bars];
14811
+ pageBarsCount += bars.length;
14812
+ }
14813
+ });
14814
+ totalBarsCount += pageBarsCount;
14815
+ pageToken = response.next_page_token || null;
14816
+ hasMorePages = !!pageToken;
14817
+ log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} crypto bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
14818
+ // Prevent infinite loops
14819
+ if (pageCount > 1000) {
14820
+ log(`Stopping crypto bars pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
14821
+ break;
14822
+ }
14823
+ }
14824
+ log(`Crypto historical bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages`, { type: 'info' });
14825
+ return {
14826
+ bars: allBars,
14827
+ next_page_token: null, // Always null since we fetch all pages
14828
+ };
14829
+ }
14830
+ /**
14831
+ * Get the most recent minute bar for requested crypto symbols
14832
+ * @param symbols Array of crypto symbols to query
14833
+ * @returns Latest bar data for each symbol
14834
+ */
14835
+ async getCryptoLatestBars(symbols) {
14836
+ return this.makeRequest('/crypto/us/latest/bars', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14837
+ }
14838
+ /**
14839
+ * Get historical quotes for crypto symbols
14840
+ * Automatically handles pagination to fetch all available data
14841
+ * @param params Parameters for crypto historical quotes request
14842
+ * @returns Historical quotes data with all pages combined
14843
+ */
14844
+ async getCryptoHistoricalQuotes(params) {
14845
+ const symbols = params.symbols;
14846
+ const symbolsStr = symbols.join(',');
14847
+ let allQuotes = {};
14848
+ let pageToken = null;
14849
+ let hasMorePages = true;
14850
+ let totalQuotesCount = 0;
14851
+ let pageCount = 0;
14852
+ // Initialize quotes arrays for each symbol
14853
+ symbols.forEach((symbol) => {
14854
+ allQuotes[symbol] = [];
14855
+ });
14856
+ log(`Starting crypto historical quotes fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
14857
+ while (hasMorePages) {
14858
+ pageCount++;
14859
+ const requestParams = {
14860
+ ...params,
14861
+ symbols: symbolsStr,
14862
+ ...(pageToken && { page_token: pageToken }),
14863
+ };
14864
+ const response = await this.makeRequest('/crypto/us/quotes', 'GET', requestParams, 'v1beta3');
14865
+ if (!response.quotes) {
14866
+ log(`No crypto quotes data found in response for ${symbols.length} symbols`, { type: 'warn' });
14867
+ break;
14868
+ }
14869
+ // Combine quotes for each symbol
14870
+ let pageQuotesCount = 0;
14871
+ Object.entries(response.quotes).forEach(([symbol, quotes]) => {
14872
+ if (quotes && quotes.length > 0) {
14873
+ allQuotes[symbol] = [...allQuotes[symbol], ...quotes];
14874
+ pageQuotesCount += quotes.length;
14875
+ }
14876
+ });
14877
+ totalQuotesCount += pageQuotesCount;
14878
+ pageToken = response.next_page_token || null;
14879
+ hasMorePages = !!pageToken;
14880
+ log(`Page ${pageCount}: Fetched ${pageQuotesCount.toLocaleString()} crypto quotes (total: ${totalQuotesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
14881
+ // Prevent infinite loops
14882
+ if (pageCount > 1000) {
14883
+ log(`Stopping crypto quotes pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
14884
+ break;
14885
+ }
14886
+ }
14887
+ log(`Crypto historical quotes fetch complete: ${totalQuotesCount.toLocaleString()} total quotes across ${pageCount} pages`, { type: 'info' });
14888
+ return {
14889
+ quotes: allQuotes,
14890
+ next_page_token: null, // Always null since we fetch all pages
14891
+ };
14892
+ }
14893
+ /**
14894
+ * Get the most recent quotes for requested crypto symbols
14895
+ * @param symbols Array of crypto symbols to query
14896
+ * @returns Latest quote data for each symbol
14897
+ */
14898
+ async getCryptoLatestQuotes(symbols) {
14899
+ if (!symbols || symbols.length === 0) {
14900
+ log('No symbols provided to getCryptoLatestQuotes, returning empty response', { type: 'warn' });
14901
+ return { quotes: {} };
14902
+ }
14903
+ return this.makeRequest('/crypto/us/latest/quotes', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14904
+ }
14905
+ /**
14906
+ * Get historical trades for crypto symbols
14907
+ * Automatically handles pagination to fetch all available data
14908
+ * @param params Parameters for crypto historical trades request
14909
+ * @returns Historical trades data with all pages combined
14910
+ */
14911
+ async getCryptoHistoricalTrades(params) {
14912
+ const symbols = params.symbols;
14913
+ const symbolsStr = symbols.join(',');
14914
+ let allTrades = {};
14915
+ let pageToken = null;
14916
+ let hasMorePages = true;
14917
+ let totalTradesCount = 0;
14918
+ let pageCount = 0;
14919
+ // Initialize trades arrays for each symbol
14920
+ symbols.forEach((symbol) => {
14921
+ allTrades[symbol] = [];
14922
+ });
14923
+ log(`Starting crypto historical trades fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
14924
+ while (hasMorePages) {
14925
+ pageCount++;
14926
+ const requestParams = {
14927
+ ...params,
14928
+ symbols: symbolsStr,
14929
+ ...(pageToken && { page_token: pageToken }),
14930
+ };
14931
+ const response = await this.makeRequest('/crypto/us/trades', 'GET', requestParams, 'v1beta3');
14932
+ if (!response.trades) {
14933
+ log(`No crypto trades data found in response for ${symbols.length} symbols`, { type: 'warn' });
14934
+ break;
14935
+ }
14936
+ // Combine trades for each symbol
14937
+ let pageTradesCount = 0;
14938
+ Object.entries(response.trades).forEach(([symbol, trades]) => {
14939
+ if (trades && trades.length > 0) {
14940
+ allTrades[symbol] = [...allTrades[symbol], ...trades];
14941
+ pageTradesCount += trades.length;
14942
+ }
14943
+ });
14944
+ totalTradesCount += pageTradesCount;
14945
+ pageToken = response.next_page_token || null;
14946
+ hasMorePages = !!pageToken;
14947
+ log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} crypto trades (total: ${totalTradesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
14948
+ // Prevent infinite loops
14949
+ if (pageCount > 1000) {
14950
+ log(`Stopping crypto trades pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
14951
+ break;
14952
+ }
14953
+ }
14954
+ log(`Crypto historical trades fetch complete: ${totalTradesCount.toLocaleString()} total trades across ${pageCount} pages`, { type: 'info' });
14955
+ return {
14956
+ trades: allTrades,
14957
+ next_page_token: null, // Always null since we fetch all pages
14958
+ };
14959
+ }
14960
+ /**
14961
+ * Get the most recent trades for requested crypto symbols
14962
+ * @param symbols Array of crypto symbols to query
14963
+ * @returns Latest trade data for each symbol
14964
+ */
14965
+ async getCryptoLatestTrades(symbols) {
14966
+ if (!symbols || symbols.length === 0) {
14967
+ log('No symbols provided to getCryptoLatestTrades, returning empty response', { type: 'warn' });
14968
+ return { trades: {} };
14969
+ }
14970
+ return this.makeRequest('/crypto/us/latest/trades', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14971
+ }
14972
+ /**
14973
+ * Get snapshots for crypto symbols
14974
+ * Returns the latest trade, latest quote, latest minute bar, latest daily bar, and previous daily bar data
14975
+ * @param symbols Array of crypto symbols to query
14976
+ * @returns Snapshot data for each symbol
14977
+ */
14978
+ async getCryptoSnapshots(symbols) {
14979
+ if (!symbols || symbols.length === 0) {
14980
+ log('No symbols provided to getCryptoSnapshots, returning empty response', { type: 'warn' });
14981
+ return { snapshots: {} };
14982
+ }
14983
+ return this.makeRequest('/crypto/us/snapshots', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14984
+ }
14985
+ /**
14986
+ * Get the latest orderbook for requested crypto symbols
14987
+ * @param symbols Array of crypto symbols to query
14988
+ * @returns Latest orderbook data for each symbol
14989
+ */
14990
+ async getCryptoLatestOrderbooks(symbols) {
14991
+ if (!symbols || symbols.length === 0) {
14992
+ log('No symbols provided to getCryptoLatestOrderbooks, returning empty response', { type: 'warn' });
14993
+ return { orderbooks: {} };
14994
+ }
14995
+ return this.makeRequest('/crypto/us/latest/orderbooks', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
14996
+ }
14997
+ /**
14998
+ * Analyzes an array of crypto bars and returns a summary string
14999
+ * @param bars Array of crypto bars to analyze
15000
+ * @returns A string summarizing the crypto price data
15001
+ */
15002
+ static analyzeCryptoBars(bars) {
15003
+ if (!bars || bars.length === 0) {
15004
+ return 'No crypto price data available';
15005
+ }
15006
+ const firstBar = bars[0];
15007
+ const lastBar = bars[bars.length - 1];
15008
+ const priceChange = lastBar.c - firstBar.o;
15009
+ const percentChange = (priceChange / firstBar.o) * 100;
15010
+ const volumeChange = lastBar.v - firstBar.v;
15011
+ const percentVolumeChange = firstBar.v > 0 ? (volumeChange / firstBar.v) * 100 : 0;
15012
+ const high = Math.max(...bars.map((bar) => bar.h));
15013
+ const low = Math.min(...bars.map((bar) => bar.l));
15014
+ const totalVolume = bars.reduce((sum, bar) => sum + bar.v, 0);
15015
+ const avgVolume = totalVolume / bars.length;
15016
+ return (`Crypto Price: $${firstBar.o.toFixed(6)} -> $${lastBar.c.toFixed(6)} (${percentChange.toFixed(2)}%), ` +
15017
+ `Volume: ${firstBar.v.toLocaleString()} -> ${lastBar.v.toLocaleString()} (${percentVolumeChange.toFixed(2)}%), ` +
15018
+ `High: $${high.toFixed(6)}, Low: $${low.toFixed(6)}, ` +
15019
+ `Avg Volume: ${avgVolume.toLocaleString()}`);
15020
+ }
14670
15021
  }
14671
15022
  // Export the singleton instance
14672
15023
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();