@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.
package/dist/index.mjs CHANGED
@@ -1447,7 +1447,7 @@ function pLimit(concurrency) {
1447
1447
  },
1448
1448
  map: {
1449
1449
  async value(array, function_) {
1450
- const promises = array.map(value => this(function_, value));
1450
+ const promises = array.map((value, index) => this(function_, value, index));
1451
1451
  return Promise.all(promises);
1452
1452
  },
1453
1453
  },
@@ -2393,7 +2393,7 @@ const safeJSON = (text) => {
2393
2393
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2394
2394
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2395
2395
 
2396
- const VERSION = '5.12.2'; // x-release-please-version
2396
+ const VERSION = '5.16.0'; // x-release-please-version
2397
2397
 
2398
2398
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2399
2399
  const isRunningInBrowser = () => {
@@ -3733,6 +3733,36 @@ class CursorPage extends AbstractPage {
3733
3733
  };
3734
3734
  }
3735
3735
  }
3736
+ class ConversationCursorPage extends AbstractPage {
3737
+ constructor(client, response, body, options) {
3738
+ super(client, response, body, options);
3739
+ this.data = body.data || [];
3740
+ this.has_more = body.has_more || false;
3741
+ this.last_id = body.last_id || '';
3742
+ }
3743
+ getPaginatedItems() {
3744
+ return this.data ?? [];
3745
+ }
3746
+ hasNextPage() {
3747
+ if (this.has_more === false) {
3748
+ return false;
3749
+ }
3750
+ return super.hasNextPage();
3751
+ }
3752
+ nextPageRequestOptions() {
3753
+ const cursor = this.last_id;
3754
+ if (!cursor) {
3755
+ return null;
3756
+ }
3757
+ return {
3758
+ ...this.options,
3759
+ query: {
3760
+ ...maybeObj(this.options.query),
3761
+ after: cursor,
3762
+ },
3763
+ };
3764
+ }
3765
+ }
3736
3766
 
3737
3767
  const checkFileSupport = () => {
3738
3768
  if (typeof File === 'undefined') {
@@ -6823,6 +6853,74 @@ class Containers extends APIResource {
6823
6853
  }
6824
6854
  Containers.Files = Files$2;
6825
6855
 
6856
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
6857
+ class Items extends APIResource {
6858
+ /**
6859
+ * Create items in a conversation with the given ID.
6860
+ */
6861
+ create(conversationID, params, options) {
6862
+ const { include, ...body } = params;
6863
+ return this._client.post(path `/conversations/${conversationID}/items`, {
6864
+ query: { include },
6865
+ body,
6866
+ ...options,
6867
+ });
6868
+ }
6869
+ /**
6870
+ * Get a single item from a conversation with the given IDs.
6871
+ */
6872
+ retrieve(itemID, params, options) {
6873
+ const { conversation_id, ...query } = params;
6874
+ return this._client.get(path `/conversations/${conversation_id}/items/${itemID}`, { query, ...options });
6875
+ }
6876
+ /**
6877
+ * List all items for a conversation with the given ID.
6878
+ */
6879
+ list(conversationID, query = {}, options) {
6880
+ return this._client.getAPIList(path `/conversations/${conversationID}/items`, (ConversationCursorPage), { query, ...options });
6881
+ }
6882
+ /**
6883
+ * Delete an item from a conversation with the given IDs.
6884
+ */
6885
+ delete(itemID, params, options) {
6886
+ const { conversation_id } = params;
6887
+ return this._client.delete(path `/conversations/${conversation_id}/items/${itemID}`, options);
6888
+ }
6889
+ }
6890
+
6891
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
6892
+ class Conversations extends APIResource {
6893
+ constructor() {
6894
+ super(...arguments);
6895
+ this.items = new Items(this._client);
6896
+ }
6897
+ /**
6898
+ * Create a conversation.
6899
+ */
6900
+ create(body, options) {
6901
+ return this._client.post('/conversations', { body, ...options });
6902
+ }
6903
+ /**
6904
+ * Get a conversation with the given ID.
6905
+ */
6906
+ retrieve(conversationID, options) {
6907
+ return this._client.get(path `/conversations/${conversationID}`, options);
6908
+ }
6909
+ /**
6910
+ * Update a conversation's metadata with the given ID.
6911
+ */
6912
+ update(conversationID, body, options) {
6913
+ return this._client.post(path `/conversations/${conversationID}`, { body, ...options });
6914
+ }
6915
+ /**
6916
+ * Delete a conversation with the given ID.
6917
+ */
6918
+ delete(conversationID, options) {
6919
+ return this._client.delete(path `/conversations/${conversationID}`, options);
6920
+ }
6921
+ }
6922
+ Conversations.Items = Items;
6923
+
6826
6924
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
6827
6925
  class Embeddings extends APIResource {
6828
6926
  /**
@@ -6987,7 +7085,7 @@ let Files$1 = class Files extends APIResource {
6987
7085
  /**
6988
7086
  * Upload a file that can be used across various endpoints. Individual files can be
6989
7087
  * up to 512 MB, and the size of all files uploaded by one organization can be up
6990
- * to 100 GB.
7088
+ * to 1 TB.
6991
7089
  *
6992
7090
  * The Assistants API supports files up to 2 million tokens and of specific file
6993
7091
  * types. See the
@@ -8416,6 +8514,7 @@ class OpenAI {
8416
8514
  this.batches = new Batches(this);
8417
8515
  this.uploads = new Uploads(this);
8418
8516
  this.responses = new Responses(this);
8517
+ this.conversations = new Conversations(this);
8419
8518
  this.evals = new Evals(this);
8420
8519
  this.containers = new Containers(this);
8421
8520
  if (apiKey === undefined) {
@@ -8806,7 +8905,7 @@ class OpenAI {
8806
8905
  // Preserve legacy string encoding behavior for now
8807
8906
  headers.values.has('content-type')) ||
8808
8907
  // `Blob` is superset of `File`
8809
- body instanceof Blob ||
8908
+ (globalThis.Blob && body instanceof globalThis.Blob) ||
8810
8909
  // `FormData` -> `multipart/form-data`
8811
8910
  body instanceof FormData ||
8812
8911
  // `URLSearchParams` -> `application/x-www-form-urlencoded`
@@ -8861,6 +8960,7 @@ OpenAI.Beta = Beta;
8861
8960
  OpenAI.Batches = Batches;
8862
8961
  OpenAI.Uploads = Uploads;
8863
8962
  OpenAI.Responses = Responses;
8963
+ OpenAI.Conversations = Conversations;
8864
8964
  OpenAI.Evals = Evals;
8865
8965
  OpenAI.Containers = Containers;
8866
8966
 
@@ -16213,6 +16313,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
16213
16313
  dataURL;
16214
16314
  apiURL;
16215
16315
  v1beta1url;
16316
+ v1beta3url;
16216
16317
  stockStreamUrl = 'wss://stream.data.alpaca.markets/v2/sip'; // production values
16217
16318
  optionStreamUrl = 'wss://stream.data.alpaca.markets/v1beta3/options'; // production values
16218
16319
  stockWs = null;
@@ -16255,6 +16356,7 @@ class AlpacaMarketDataAPI extends EventEmitter {
16255
16356
  ? 'https://paper-api.alpaca.markets/v2'
16256
16357
  : 'https://api.alpaca.markets/v2'; // used by some, e.g. getAssets
16257
16358
  this.v1beta1url = 'https://data.alpaca.markets/v1beta1'; // used for options endpoints
16359
+ this.v1beta3url = 'https://data.alpaca.markets/v1beta3'; // used for crypto endpoints
16258
16360
  this.setMode('production'); // sets stockStreamUrl and optionStreamUrl
16259
16361
  this.headers = {
16260
16362
  'APCA-API-KEY-ID': process.env.ALPACA_API_KEY,
@@ -16392,7 +16494,9 @@ class AlpacaMarketDataAPI extends EventEmitter {
16392
16494
  }
16393
16495
  }
16394
16496
  async makeRequest(endpoint, method = 'GET', params, baseUrlName = 'data') {
16395
- const baseUrl = baseUrlName === 'data' ? this.dataURL : baseUrlName === 'api' ? this.apiURL : this.v1beta1url;
16497
+ const baseUrl = baseUrlName === 'data' ? this.dataURL :
16498
+ baseUrlName === 'api' ? this.apiURL :
16499
+ baseUrlName === 'v1beta1' ? this.v1beta1url : this.v1beta3url;
16396
16500
  const url = new URL(`${baseUrl}${endpoint}`);
16397
16501
  try {
16398
16502
  if (params) {
@@ -17100,6 +17204,253 @@ class AlpacaMarketDataAPI extends EventEmitter {
17100
17204
  }
17101
17205
  return newsArticles;
17102
17206
  }
17207
+ // ===== CRYPTO MARKET DATA METHODS =====
17208
+ /**
17209
+ * Get historical OHLCV bars for crypto symbols
17210
+ * Automatically handles pagination to fetch all available data
17211
+ * @param params Parameters for crypto historical bars request
17212
+ * @returns Historical bars data with all pages combined
17213
+ */
17214
+ async getCryptoHistoricalBars(params) {
17215
+ const symbols = params.symbols;
17216
+ const symbolsStr = symbols.join(',');
17217
+ let allBars = {};
17218
+ let pageToken = null;
17219
+ let hasMorePages = true;
17220
+ let totalBarsCount = 0;
17221
+ let pageCount = 0;
17222
+ // Initialize bar arrays for each symbol
17223
+ symbols.forEach((symbol) => {
17224
+ allBars[symbol] = [];
17225
+ });
17226
+ log(`Starting crypto historical bars fetch for ${symbols.length} symbols (${params.timeframe}, ${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
17227
+ while (hasMorePages) {
17228
+ pageCount++;
17229
+ const requestParams = {
17230
+ ...params,
17231
+ symbols: symbolsStr,
17232
+ ...(pageToken && { page_token: pageToken }),
17233
+ };
17234
+ const response = await this.makeRequest('/crypto/us/bars', 'GET', requestParams, 'v1beta3');
17235
+ if (!response.bars) {
17236
+ log(`No crypto bars data found in response for ${symbols.length} symbols`, { type: 'warn' });
17237
+ break;
17238
+ }
17239
+ // Combine bars for each symbol
17240
+ let pageBarsCount = 0;
17241
+ Object.entries(response.bars).forEach(([symbol, bars]) => {
17242
+ if (bars && bars.length > 0) {
17243
+ allBars[symbol] = [...allBars[symbol], ...bars];
17244
+ pageBarsCount += bars.length;
17245
+ }
17246
+ });
17247
+ totalBarsCount += pageBarsCount;
17248
+ pageToken = response.next_page_token || null;
17249
+ hasMorePages = !!pageToken;
17250
+ log(`Page ${pageCount}: Fetched ${pageBarsCount.toLocaleString()} crypto bars (total: ${totalBarsCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
17251
+ // Prevent infinite loops
17252
+ if (pageCount > 1000) {
17253
+ log(`Stopping crypto bars pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
17254
+ break;
17255
+ }
17256
+ }
17257
+ log(`Crypto historical bars fetch complete: ${totalBarsCount.toLocaleString()} total bars across ${pageCount} pages`, { type: 'info' });
17258
+ return {
17259
+ bars: allBars,
17260
+ next_page_token: null, // Always null since we fetch all pages
17261
+ };
17262
+ }
17263
+ /**
17264
+ * Get the most recent minute bar for requested crypto symbols
17265
+ * @param symbols Array of crypto symbols to query
17266
+ * @returns Latest bar data for each symbol
17267
+ */
17268
+ async getCryptoLatestBars(symbols) {
17269
+ return this.makeRequest('/crypto/us/latest/bars', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
17270
+ }
17271
+ /**
17272
+ * Get historical quotes for crypto symbols
17273
+ * Automatically handles pagination to fetch all available data
17274
+ * @param params Parameters for crypto historical quotes request
17275
+ * @returns Historical quotes data with all pages combined
17276
+ */
17277
+ async getCryptoHistoricalQuotes(params) {
17278
+ const symbols = params.symbols;
17279
+ const symbolsStr = symbols.join(',');
17280
+ let allQuotes = {};
17281
+ let pageToken = null;
17282
+ let hasMorePages = true;
17283
+ let totalQuotesCount = 0;
17284
+ let pageCount = 0;
17285
+ // Initialize quotes arrays for each symbol
17286
+ symbols.forEach((symbol) => {
17287
+ allQuotes[symbol] = [];
17288
+ });
17289
+ log(`Starting crypto historical quotes fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
17290
+ while (hasMorePages) {
17291
+ pageCount++;
17292
+ const requestParams = {
17293
+ ...params,
17294
+ symbols: symbolsStr,
17295
+ ...(pageToken && { page_token: pageToken }),
17296
+ };
17297
+ const response = await this.makeRequest('/crypto/us/quotes', 'GET', requestParams, 'v1beta3');
17298
+ if (!response.quotes) {
17299
+ log(`No crypto quotes data found in response for ${symbols.length} symbols`, { type: 'warn' });
17300
+ break;
17301
+ }
17302
+ // Combine quotes for each symbol
17303
+ let pageQuotesCount = 0;
17304
+ Object.entries(response.quotes).forEach(([symbol, quotes]) => {
17305
+ if (quotes && quotes.length > 0) {
17306
+ allQuotes[symbol] = [...allQuotes[symbol], ...quotes];
17307
+ pageQuotesCount += quotes.length;
17308
+ }
17309
+ });
17310
+ totalQuotesCount += pageQuotesCount;
17311
+ pageToken = response.next_page_token || null;
17312
+ hasMorePages = !!pageToken;
17313
+ log(`Page ${pageCount}: Fetched ${pageQuotesCount.toLocaleString()} crypto quotes (total: ${totalQuotesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
17314
+ // Prevent infinite loops
17315
+ if (pageCount > 1000) {
17316
+ log(`Stopping crypto quotes pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
17317
+ break;
17318
+ }
17319
+ }
17320
+ log(`Crypto historical quotes fetch complete: ${totalQuotesCount.toLocaleString()} total quotes across ${pageCount} pages`, { type: 'info' });
17321
+ return {
17322
+ quotes: allQuotes,
17323
+ next_page_token: null, // Always null since we fetch all pages
17324
+ };
17325
+ }
17326
+ /**
17327
+ * Get the most recent quotes for requested crypto symbols
17328
+ * @param symbols Array of crypto symbols to query
17329
+ * @returns Latest quote data for each symbol
17330
+ */
17331
+ async getCryptoLatestQuotes(symbols) {
17332
+ if (!symbols || symbols.length === 0) {
17333
+ log('No symbols provided to getCryptoLatestQuotes, returning empty response', { type: 'warn' });
17334
+ return { quotes: {} };
17335
+ }
17336
+ return this.makeRequest('/crypto/us/latest/quotes', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
17337
+ }
17338
+ /**
17339
+ * Get historical trades for crypto symbols
17340
+ * Automatically handles pagination to fetch all available data
17341
+ * @param params Parameters for crypto historical trades request
17342
+ * @returns Historical trades data with all pages combined
17343
+ */
17344
+ async getCryptoHistoricalTrades(params) {
17345
+ const symbols = params.symbols;
17346
+ const symbolsStr = symbols.join(',');
17347
+ let allTrades = {};
17348
+ let pageToken = null;
17349
+ let hasMorePages = true;
17350
+ let totalTradesCount = 0;
17351
+ let pageCount = 0;
17352
+ // Initialize trades arrays for each symbol
17353
+ symbols.forEach((symbol) => {
17354
+ allTrades[symbol] = [];
17355
+ });
17356
+ log(`Starting crypto historical trades fetch for ${symbols.length} symbols (${params.start || 'no start'} to ${params.end || 'no end'})`, { type: 'info' });
17357
+ while (hasMorePages) {
17358
+ pageCount++;
17359
+ const requestParams = {
17360
+ ...params,
17361
+ symbols: symbolsStr,
17362
+ ...(pageToken && { page_token: pageToken }),
17363
+ };
17364
+ const response = await this.makeRequest('/crypto/us/trades', 'GET', requestParams, 'v1beta3');
17365
+ if (!response.trades) {
17366
+ log(`No crypto trades data found in response for ${symbols.length} symbols`, { type: 'warn' });
17367
+ break;
17368
+ }
17369
+ // Combine trades for each symbol
17370
+ let pageTradesCount = 0;
17371
+ Object.entries(response.trades).forEach(([symbol, trades]) => {
17372
+ if (trades && trades.length > 0) {
17373
+ allTrades[symbol] = [...allTrades[symbol], ...trades];
17374
+ pageTradesCount += trades.length;
17375
+ }
17376
+ });
17377
+ totalTradesCount += pageTradesCount;
17378
+ pageToken = response.next_page_token || null;
17379
+ hasMorePages = !!pageToken;
17380
+ log(`Page ${pageCount}: Fetched ${pageTradesCount.toLocaleString()} crypto trades (total: ${totalTradesCount.toLocaleString()}) for ${symbols.length} symbols${hasMorePages ? ', more pages available' : ', complete'}`);
17381
+ // Prevent infinite loops
17382
+ if (pageCount > 1000) {
17383
+ log(`Stopping crypto trades pagination after ${pageCount} pages to prevent infinite loop`, { type: 'warn' });
17384
+ break;
17385
+ }
17386
+ }
17387
+ log(`Crypto historical trades fetch complete: ${totalTradesCount.toLocaleString()} total trades across ${pageCount} pages`, { type: 'info' });
17388
+ return {
17389
+ trades: allTrades,
17390
+ next_page_token: null, // Always null since we fetch all pages
17391
+ };
17392
+ }
17393
+ /**
17394
+ * Get the most recent trades for requested crypto symbols
17395
+ * @param symbols Array of crypto symbols to query
17396
+ * @returns Latest trade data for each symbol
17397
+ */
17398
+ async getCryptoLatestTrades(symbols) {
17399
+ if (!symbols || symbols.length === 0) {
17400
+ log('No symbols provided to getCryptoLatestTrades, returning empty response', { type: 'warn' });
17401
+ return { trades: {} };
17402
+ }
17403
+ return this.makeRequest('/crypto/us/latest/trades', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
17404
+ }
17405
+ /**
17406
+ * Get snapshots for crypto symbols
17407
+ * Returns the latest trade, latest quote, latest minute bar, latest daily bar, and previous daily bar data
17408
+ * @param symbols Array of crypto symbols to query
17409
+ * @returns Snapshot data for each symbol
17410
+ */
17411
+ async getCryptoSnapshots(symbols) {
17412
+ if (!symbols || symbols.length === 0) {
17413
+ log('No symbols provided to getCryptoSnapshots, returning empty response', { type: 'warn' });
17414
+ return { snapshots: {} };
17415
+ }
17416
+ return this.makeRequest('/crypto/us/snapshots', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
17417
+ }
17418
+ /**
17419
+ * Get the latest orderbook for requested crypto symbols
17420
+ * @param symbols Array of crypto symbols to query
17421
+ * @returns Latest orderbook data for each symbol
17422
+ */
17423
+ async getCryptoLatestOrderbooks(symbols) {
17424
+ if (!symbols || symbols.length === 0) {
17425
+ log('No symbols provided to getCryptoLatestOrderbooks, returning empty response', { type: 'warn' });
17426
+ return { orderbooks: {} };
17427
+ }
17428
+ return this.makeRequest('/crypto/us/latest/orderbooks', 'GET', { symbols: symbols.join(',') }, 'v1beta3');
17429
+ }
17430
+ /**
17431
+ * Analyzes an array of crypto bars and returns a summary string
17432
+ * @param bars Array of crypto bars to analyze
17433
+ * @returns A string summarizing the crypto price data
17434
+ */
17435
+ static analyzeCryptoBars(bars) {
17436
+ if (!bars || bars.length === 0) {
17437
+ return 'No crypto price data available';
17438
+ }
17439
+ const firstBar = bars[0];
17440
+ const lastBar = bars[bars.length - 1];
17441
+ const priceChange = lastBar.c - firstBar.o;
17442
+ const percentChange = (priceChange / firstBar.o) * 100;
17443
+ const volumeChange = lastBar.v - firstBar.v;
17444
+ const percentVolumeChange = firstBar.v > 0 ? (volumeChange / firstBar.v) * 100 : 0;
17445
+ const high = Math.max(...bars.map((bar) => bar.h));
17446
+ const low = Math.min(...bars.map((bar) => bar.l));
17447
+ const totalVolume = bars.reduce((sum, bar) => sum + bar.v, 0);
17448
+ const avgVolume = totalVolume / bars.length;
17449
+ return (`Crypto Price: $${firstBar.o.toFixed(6)} -> $${lastBar.c.toFixed(6)} (${percentChange.toFixed(2)}%), ` +
17450
+ `Volume: ${firstBar.v.toLocaleString()} -> ${lastBar.v.toLocaleString()} (${percentVolumeChange.toFixed(2)}%), ` +
17451
+ `High: $${high.toFixed(6)}, Low: $${low.toFixed(6)}, ` +
17452
+ `Avg Volume: ${avgVolume.toLocaleString()}`);
17453
+ }
17103
17454
  }
17104
17455
  // Export the singleton instance
17105
17456
  const marketDataAPI = AlpacaMarketDataAPI.getInstance();