@discomedia/utils 1.0.39 → 1.0.41

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.0.39",
6
+ "version": "1.0.41",
7
7
  "author": "Disco Media",
8
8
  "description": "Utility functions used in Disco Media apps",
9
9
  "always-build-npm": true,
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "dotenv": "^17.2.3",
36
- "openai": "^6.1.0",
36
+ "openai": "^6.2.0",
37
37
  "p-limit": "^7.1.1",
38
38
  "tslib": "^2.8.1",
39
39
  "ws": "^8.18.3"
package/dist/test.js CHANGED
@@ -799,7 +799,7 @@ const safeJSON = (text) => {
799
799
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
800
800
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
801
801
 
802
- const VERSION = '6.1.0'; // x-release-please-version
802
+ const VERSION = '6.2.0'; // x-release-please-version
803
803
 
804
804
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
805
805
  const isRunningInBrowser = () => {
@@ -2200,6 +2200,15 @@ function getName(value) {
2200
2200
  .pop() || undefined);
2201
2201
  }
2202
2202
  const isAsyncIterable = (value) => value != null && typeof value === 'object' && typeof value[Symbol.asyncIterator] === 'function';
2203
+ /**
2204
+ * Returns a multipart/form-data request if any part of the given request body contains a File / Blob value.
2205
+ * Otherwise returns the request as is.
2206
+ */
2207
+ const maybeMultipartFormRequestOptions = async (opts, fetch) => {
2208
+ if (!hasUploadableValue(opts.body))
2209
+ return opts;
2210
+ return { ...opts, body: await createForm(opts.body, fetch) };
2211
+ };
2203
2212
  const multipartFormRequestOptions = async (opts, fetch) => {
2204
2213
  return { ...opts, body: await createForm(opts.body, fetch) };
2205
2214
  };
@@ -2245,6 +2254,22 @@ const createForm = async (body, fetch) => {
2245
2254
  // We check for Blob not File because Bun.File doesn't inherit from File,
2246
2255
  // but they both inherit from Blob and have a `name` property at runtime.
2247
2256
  const isNamedBlob = (value) => value instanceof Blob && 'name' in value;
2257
+ const isUploadable = (value) => typeof value === 'object' &&
2258
+ value !== null &&
2259
+ (value instanceof Response || isAsyncIterable(value) || isNamedBlob(value));
2260
+ const hasUploadableValue = (value) => {
2261
+ if (isUploadable(value))
2262
+ return true;
2263
+ if (Array.isArray(value))
2264
+ return value.some(hasUploadableValue);
2265
+ if (value && typeof value === 'object') {
2266
+ for (const k in value) {
2267
+ if (hasUploadableValue(value[k]))
2268
+ return true;
2269
+ }
2270
+ }
2271
+ return false;
2272
+ };
2248
2273
  const addFormValue = async (form, key, value) => {
2249
2274
  if (value === undefined)
2250
2275
  return;
@@ -2299,7 +2324,7 @@ const isResponseLike = (value) => value != null &&
2299
2324
  typeof value.blob === 'function';
2300
2325
  /**
2301
2326
  * Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats
2302
- * @param value the raw content of the file. Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s
2327
+ * @param value the raw content of the file. Can be an {@link Uploadable}, BlobLikePart, or AsyncIterable of BlobLikeParts
2303
2328
  * @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible
2304
2329
  * @param {Object=} options additional properties
2305
2330
  * @param {string=} options.type the MIME type of the content
@@ -4150,7 +4175,7 @@ class Assistants extends APIResource {
4150
4175
  }
4151
4176
 
4152
4177
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4153
- class Sessions extends APIResource {
4178
+ let Sessions$1 = class Sessions extends APIResource {
4154
4179
  /**
4155
4180
  * Create an ephemeral API token for use in client-side applications with the
4156
4181
  * Realtime API. Can be configured with the same session parameters as the
@@ -4173,7 +4198,7 @@ class Sessions extends APIResource {
4173
4198
  headers: buildHeaders([{ 'OpenAI-Beta': 'assistants=v2' }, options?.headers]),
4174
4199
  });
4175
4200
  }
4176
- }
4201
+ };
4177
4202
 
4178
4203
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4179
4204
  class TranscriptionSessions extends APIResource {
@@ -4208,13 +4233,144 @@ class TranscriptionSessions extends APIResource {
4208
4233
  let Realtime$1 = class Realtime extends APIResource {
4209
4234
  constructor() {
4210
4235
  super(...arguments);
4211
- this.sessions = new Sessions(this._client);
4236
+ this.sessions = new Sessions$1(this._client);
4212
4237
  this.transcriptionSessions = new TranscriptionSessions(this._client);
4213
4238
  }
4214
4239
  };
4215
- Realtime$1.Sessions = Sessions;
4240
+ Realtime$1.Sessions = Sessions$1;
4216
4241
  Realtime$1.TranscriptionSessions = TranscriptionSessions;
4217
4242
 
4243
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4244
+ class Sessions extends APIResource {
4245
+ /**
4246
+ * Create a ChatKit session
4247
+ *
4248
+ * @example
4249
+ * ```ts
4250
+ * const chatSession =
4251
+ * await client.beta.chatkit.sessions.create({
4252
+ * user: 'x',
4253
+ * workflow: { id: 'id' },
4254
+ * });
4255
+ * ```
4256
+ */
4257
+ create(body, options) {
4258
+ return this._client.post('/chatkit/sessions', {
4259
+ body,
4260
+ ...options,
4261
+ headers: buildHeaders([{ 'OpenAI-Beta': 'chatkit_beta=v1' }, options?.headers]),
4262
+ });
4263
+ }
4264
+ /**
4265
+ * Cancel a ChatKit session
4266
+ *
4267
+ * @example
4268
+ * ```ts
4269
+ * const chatSession =
4270
+ * await client.beta.chatkit.sessions.cancel('cksess_123');
4271
+ * ```
4272
+ */
4273
+ cancel(sessionID, options) {
4274
+ return this._client.post(path `/chatkit/sessions/${sessionID}/cancel`, {
4275
+ ...options,
4276
+ headers: buildHeaders([{ 'OpenAI-Beta': 'chatkit_beta=v1' }, options?.headers]),
4277
+ });
4278
+ }
4279
+ }
4280
+
4281
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4282
+ let Threads$1 = class Threads extends APIResource {
4283
+ /**
4284
+ * Retrieve a ChatKit thread
4285
+ *
4286
+ * @example
4287
+ * ```ts
4288
+ * const chatkitThread =
4289
+ * await client.beta.chatkit.threads.retrieve('cthr_123');
4290
+ * ```
4291
+ */
4292
+ retrieve(threadID, options) {
4293
+ return this._client.get(path `/chatkit/threads/${threadID}`, {
4294
+ ...options,
4295
+ headers: buildHeaders([{ 'OpenAI-Beta': 'chatkit_beta=v1' }, options?.headers]),
4296
+ });
4297
+ }
4298
+ /**
4299
+ * List ChatKit threads
4300
+ *
4301
+ * @example
4302
+ * ```ts
4303
+ * // Automatically fetches more pages as needed.
4304
+ * for await (const chatkitThread of client.beta.chatkit.threads.list()) {
4305
+ * // ...
4306
+ * }
4307
+ * ```
4308
+ */
4309
+ list(query = {}, options) {
4310
+ return this._client.getAPIList('/chatkit/threads', (ConversationCursorPage), {
4311
+ query,
4312
+ ...options,
4313
+ headers: buildHeaders([{ 'OpenAI-Beta': 'chatkit_beta=v1' }, options?.headers]),
4314
+ });
4315
+ }
4316
+ /**
4317
+ * Delete a ChatKit thread
4318
+ *
4319
+ * @example
4320
+ * ```ts
4321
+ * const thread = await client.beta.chatkit.threads.delete(
4322
+ * 'cthr_123',
4323
+ * );
4324
+ * ```
4325
+ */
4326
+ delete(threadID, options) {
4327
+ return this._client.delete(path `/chatkit/threads/${threadID}`, {
4328
+ ...options,
4329
+ headers: buildHeaders([{ 'OpenAI-Beta': 'chatkit_beta=v1' }, options?.headers]),
4330
+ });
4331
+ }
4332
+ /**
4333
+ * List ChatKit thread items
4334
+ *
4335
+ * @example
4336
+ * ```ts
4337
+ * // Automatically fetches more pages as needed.
4338
+ * for await (const thread of client.beta.chatkit.threads.listItems(
4339
+ * 'cthr_123',
4340
+ * )) {
4341
+ * // ...
4342
+ * }
4343
+ * ```
4344
+ */
4345
+ listItems(threadID, query = {}, options) {
4346
+ return this._client.getAPIList(path `/chatkit/threads/${threadID}/items`, (ConversationCursorPage), { query, ...options, headers: buildHeaders([{ 'OpenAI-Beta': 'chatkit_beta=v1' }, options?.headers]) });
4347
+ }
4348
+ };
4349
+
4350
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4351
+ class ChatKit extends APIResource {
4352
+ constructor() {
4353
+ super(...arguments);
4354
+ this.sessions = new Sessions(this._client);
4355
+ this.threads = new Threads$1(this._client);
4356
+ }
4357
+ /**
4358
+ * Upload a ChatKit file
4359
+ *
4360
+ * @example
4361
+ * ```ts
4362
+ * const response = await client.beta.chatkit.uploadFile({
4363
+ * file: fs.createReadStream('path/to/file'),
4364
+ * });
4365
+ * ```
4366
+ */
4367
+ uploadFile(body, options) {
4368
+ return this._client.post('/chatkit/files', maybeMultipartFormRequestOptions({ body, ...options, headers: buildHeaders([{ 'OpenAI-Beta': 'chatkit_beta=v1' }, options?.headers]) }, this._client));
4369
+ }
4370
+ }
4371
+ ChatKit.Sessions = Sessions;
4372
+ ChatKit.Threads = Threads$1;
4373
+
4218
4374
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
4219
4375
  /**
4220
4376
  * @deprecated The Assistants API is deprecated in favor of the Responses API
@@ -5152,11 +5308,13 @@ class Beta extends APIResource {
5152
5308
  constructor() {
5153
5309
  super(...arguments);
5154
5310
  this.realtime = new Realtime$1(this._client);
5311
+ this.chatkit = new ChatKit(this._client);
5155
5312
  this.assistants = new Assistants(this._client);
5156
5313
  this.threads = new Threads(this._client);
5157
5314
  }
5158
5315
  }
5159
5316
  Beta.Realtime = Realtime$1;
5317
+ Beta.ChatKit = ChatKit;
5160
5318
  Beta.Assistants = Assistants;
5161
5319
  Beta.Threads = Threads;
5162
5320
 
@@ -6909,6 +7067,51 @@ class VectorStores extends APIResource {
6909
7067
  VectorStores.Files = Files;
6910
7068
  VectorStores.FileBatches = FileBatches;
6911
7069
 
7070
+ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
7071
+ class Videos extends APIResource {
7072
+ /**
7073
+ * Create a video
7074
+ */
7075
+ create(body, options) {
7076
+ return this._client.post('/videos', maybeMultipartFormRequestOptions({ body, ...options }, this._client));
7077
+ }
7078
+ /**
7079
+ * Retrieve a video
7080
+ */
7081
+ retrieve(videoID, options) {
7082
+ return this._client.get(path `/videos/${videoID}`, options);
7083
+ }
7084
+ /**
7085
+ * List videos
7086
+ */
7087
+ list(query = {}, options) {
7088
+ return this._client.getAPIList('/videos', (ConversationCursorPage), { query, ...options });
7089
+ }
7090
+ /**
7091
+ * Delete a video
7092
+ */
7093
+ delete(videoID, options) {
7094
+ return this._client.delete(path `/videos/${videoID}`, options);
7095
+ }
7096
+ /**
7097
+ * Download video content
7098
+ */
7099
+ downloadContent(videoID, query = {}, options) {
7100
+ return this._client.get(path `/videos/${videoID}/content`, {
7101
+ query,
7102
+ ...options,
7103
+ headers: buildHeaders([{ Accept: 'application/binary' }, options?.headers]),
7104
+ __binaryResponse: true,
7105
+ });
7106
+ }
7107
+ /**
7108
+ * Create a video remix
7109
+ */
7110
+ remix(videoID, body, options) {
7111
+ return this._client.post(path `/videos/${videoID}/remix`, maybeMultipartFormRequestOptions({ body, ...options }, this._client));
7112
+ }
7113
+ }
7114
+
6912
7115
  // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
6913
7116
  var _Webhooks_instances, _Webhooks_validateSecret, _Webhooks_getRequiredHeader;
6914
7117
  class Webhooks extends APIResource {
@@ -7047,6 +7250,7 @@ class OpenAI {
7047
7250
  this.conversations = new Conversations(this);
7048
7251
  this.evals = new Evals(this);
7049
7252
  this.containers = new Containers(this);
7253
+ this.videos = new Videos(this);
7050
7254
  if (apiKey === undefined) {
7051
7255
  throw new OpenAIError('Missing credentials. Please pass an `apiKey`, or set the `OPENAI_API_KEY` environment variable.');
7052
7256
  }
@@ -7517,6 +7721,7 @@ OpenAI.Realtime = Realtime;
7517
7721
  OpenAI.Conversations = Conversations;
7518
7722
  OpenAI.Evals = Evals;
7519
7723
  OpenAI.Containers = Containers;
7724
+ OpenAI.Videos = Videos;
7520
7725
 
7521
7726
  function getDefaultExportFromCjs (x) {
7522
7727
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
@@ -13526,14 +13731,25 @@ class AlpacaMarketDataAPI extends EventEmitter {
13526
13731
  `Avg Volume: ${avgVolume.toLocaleString()}`);
13527
13732
  }
13528
13733
  /**
13529
- * Get all assets available for trade and data consumption from Alpaca
13530
- * @param params Optional query params: status (e.g. 'active'), asset_class (e.g. 'us_equity', 'crypto')
13734
+ * Get assets available for trade and data consumption from Alpaca
13735
+ * @param params Optional query params
13736
+ * - status: 'active' | 'inactive' (default 'active')
13737
+ * - asset_class: 'us_equity' | 'us_option' | 'crypto' (default 'us_equity')
13738
+ * - shortable: if true (default), filters to assets with shortable=true and easy_to_borrow=true
13531
13739
  * @returns Array of AlpacaAsset objects
13532
13740
  * @see https://docs.alpaca.markets/reference/get-v2-assets-1
13533
13741
  */
13534
13742
  async getAssets(params) {
13535
13743
  // Endpoint: GET /v2/assets
13536
- return this.makeRequest('/assets', 'GET', params, 'api'); // use apiURL
13744
+ const { status = 'active', asset_class = 'us_equity', shortable = true, } = {
13745
+ status: params?.status ?? 'active',
13746
+ asset_class: params?.asset_class ?? 'us_equity',
13747
+ shortable: params?.shortable ?? true,
13748
+ };
13749
+ const assets = await this.makeRequest('/assets', 'GET', { status, asset_class }, 'api');
13750
+ if (!shortable)
13751
+ return assets;
13752
+ return assets.filter((a) => a.shortable === true && a.easy_to_borrow === true);
13537
13753
  }
13538
13754
  /**
13539
13755
  * Get a single asset by symbol or asset_id
@@ -14177,88 +14393,84 @@ class AlpacaMarketDataAPI extends EventEmitter {
14177
14393
  }
14178
14394
  }
14179
14395
  // Export the singleton instance
14180
- AlpacaMarketDataAPI.getInstance();
14396
+ const marketDataAPI = AlpacaMarketDataAPI.getInstance();
14397
+
14398
+ var alpacaMarketDataApi = /*#__PURE__*/Object.freeze({
14399
+ __proto__: null,
14400
+ AlpacaMarketDataAPI: AlpacaMarketDataAPI,
14401
+ marketDataAPI: marketDataAPI
14402
+ });
14181
14403
 
14182
14404
  // Test file for context functionality
14183
- //testOpenRouter();
14184
- //testGetMarketStatus();
14185
- //testCryptoMarketData();
14186
- //testGetPortfolioDailyHistory();
14187
- async function testWebSocketConnectAndDisconnect() {
14188
- console.log('\n--- Testing WebSocket Connect and Disconnect ---');
14405
+ async function testGetAssetsShortableFilter() {
14406
+ console.log('\n--- Testing getAssets params and shortable filter ---');
14189
14407
  const log = (message, options = { type: 'info' }) => {
14190
14408
  log$1(message, { ...options, source: 'Test' });
14191
14409
  };
14192
- // Ensure market data env vars are populated from TRADING_* if needed
14193
- if (!process.env.ALPACA_API_KEY && process.env.ALPACA_TRADING_API_KEY) {
14194
- process.env.ALPACA_API_KEY = process.env.ALPACA_TRADING_API_KEY;
14195
- }
14196
- if (!process.env.ALPACA_SECRET_KEY && process.env.ALPACA_TRADING_SECRET_KEY) {
14197
- process.env.ALPACA_SECRET_KEY = process.env.ALPACA_TRADING_SECRET_KEY;
14198
- }
14199
- if (!process.env.ALPACA_ACCOUNT_TYPE && process.env.ALPACA_TRADING_ACCOUNT_TYPE) {
14200
- process.env.ALPACA_ACCOUNT_TYPE = process.env.ALPACA_TRADING_ACCOUNT_TYPE;
14201
- }
14202
- const apiKey = process.env.ALPACA_API_KEY;
14203
- const secretKey = process.env.ALPACA_SECRET_KEY;
14204
- if (!apiKey || !secretKey) {
14205
- console.error('Missing Alpaca API credentials. Check .env ALPACA_TRADING_API_KEY/SECRET or ALPACA_API_KEY/SECRET.');
14410
+ if (!process.env.ALPACA_API_KEY || !process.env.ALPACA_SECRET_KEY) {
14411
+ console.log('Skipping getAssets test: Missing environment variables (ALPACA_API_KEY, ALPACA_SECRET_KEY)');
14206
14412
  return;
14207
14413
  }
14208
- const md = AlpacaMarketDataAPI.getInstance();
14209
- const symbols = ['SPY', 'TSLA', 'AAPL'];
14210
- // Track received minute bars per symbol
14211
- const barCounts = Object.fromEntries(symbols.map((s) => [s, 0]));
14212
- const firstSeenAt = {};
14213
- // Connect and subscribe to minute bars
14214
- log(`Connecting stock stream and subscribing to minute bars for ${symbols.join(', ')}`);
14215
- md.connectStockStream();
14216
- md.subscribe('stock', { bars: symbols });
14217
- const formatNY = (d) => d.toLocaleString('en-US', { timeZone: 'America/New_York' });
14218
- const onBar = (msg) => {
14219
- if (!symbols.includes(msg.S))
14220
- return;
14221
- barCounts[msg.S] += 1;
14222
- if (!firstSeenAt[msg.S]) {
14223
- firstSeenAt[msg.S] = formatNY(new Date(msg.t));
14414
+ try {
14415
+ const { marketDataAPI } = await Promise.resolve().then(function () { return alpacaMarketDataApi; });
14416
+ // 1) Default call: should filter to shortable && easy_to_borrow and default to active us_equity
14417
+ log('Calling getAssets() with defaults');
14418
+ const filtered = await marketDataAPI.getAssets();
14419
+ console.log(`Received ${filtered.length} filtered assets (default params)`);
14420
+ const allFilteredValid = filtered.every((a) => a.shortable === true && a.easy_to_borrow === true);
14421
+ if (!allFilteredValid) {
14422
+ console.error(' Default getAssets() included assets that are not shortable and easy_to_borrow');
14224
14423
  }
14225
- log(`Bar ${msg.S} o=${msg.o.toFixed(2)} h=${msg.h.toFixed(2)} l=${msg.l.toFixed(2)} c=${msg.c.toFixed(2)} @ ${formatNY(new Date(msg.t))}`, { type: 'debug', symbol: msg.S });
14226
- };
14227
- md.on('stock-b', onBar);
14228
- // Wait until we have at least 2 bars per symbol, or timeout after 3 minutes
14229
- const minBarsPerSymbol = 2;
14230
- const timeoutMs = 3 * 60 * 1000;
14231
- const done = await new Promise((resolve) => {
14232
- const startedAt = Date.now();
14233
- const interval = setInterval(() => {
14234
- const hasAll = symbols.every((s) => barCounts[s] >= minBarsPerSymbol);
14235
- const elapsed = Date.now() - startedAt;
14236
- if (hasAll) {
14237
- clearInterval(interval);
14238
- resolve(true);
14239
- }
14240
- else if (elapsed >= timeoutMs) {
14241
- clearInterval(interval);
14242
- resolve(false);
14243
- }
14244
- }, 1000);
14245
- });
14246
- // Unsubscribe and disconnect
14247
- md.unsubscribe('stock', { bars: symbols });
14248
- md.disconnectStockStream();
14249
- md.removeListener('stock-b', onBar);
14250
- // Report results
14251
- const nowNY = formatNY(new Date());
14252
- console.log('\nStock bars summary:');
14253
- symbols.forEach((s) => {
14254
- console.log(` ${s}: ${barCounts[s]} bars (first at ${firstSeenAt[s] ?? 'n/a'}; finished at ${nowNY})`);
14255
- });
14256
- if (done) {
14257
- console.log('✓ Received minimum bars for all symbols and disconnected.');
14258
- }
14259
- else {
14260
- console.warn('Completed with timeout before receiving minimum bars for all symbols.');
14261
- }
14262
- }
14263
- testWebSocketConnectAndDisconnect();
14424
+ else {
14425
+ console.log('✓ Default getAssets() correctly filtered shortable and easy_to_borrow assets');
14426
+ }
14427
+ // 2) Unfiltered call: shortable=false
14428
+ log('Calling getAssets({ shortable: false })');
14429
+ const unfiltered = await marketDataAPI.getAssets({ shortable: false });
14430
+ console.log(`Received ${unfiltered.length} unfiltered assets`);
14431
+ if (unfiltered.length < filtered.length) {
14432
+ console.warn('Unfiltered list is smaller than filtered. This is unexpected but not fatal.');
14433
+ }
14434
+ else {
14435
+ console.log('✓ Unfiltered list length is >= filtered list length');
14436
+ }
14437
+ // 3) Specific constraints: inactive crypto, not asserting non-empty
14438
+ log("Calling getAssets({ status: 'inactive', asset_class: 'crypto', shortable: false })");
14439
+ const inactiveCrypto = await marketDataAPI.getAssets({ status: 'inactive', asset_class: 'crypto', shortable: false });
14440
+ console.log(`Received ${inactiveCrypto.length} inactive crypto assets`);
14441
+ if (inactiveCrypto.length > 0) {
14442
+ const valid = inactiveCrypto.every((a) => a.class === 'crypto' && a.status === 'inactive');
14443
+ if (!valid) {
14444
+ console.error('✗ Inactive crypto request returned assets with mismatched class or status');
14445
+ }
14446
+ else {
14447
+ console.log('✓ Inactive crypto request returned only crypto with inactive status');
14448
+ }
14449
+ }
14450
+ else {
14451
+ console.log('No inactive crypto assets returned; skipping class/status assertions.');
14452
+ }
14453
+ }
14454
+ catch (error) {
14455
+ console.error('✗ getAssets test failed:', error);
14456
+ if (error instanceof Error) {
14457
+ console.error('Error message:', error.message);
14458
+ console.error('Stack trace:', error.stack);
14459
+ }
14460
+ }
14461
+ }
14462
+ // testGetTradingDate();
14463
+ // testGetTradingStartAndEndDates();
14464
+ // testGetLastFullTradingDate();
14465
+ // testGetMarketOpenClose();
14466
+ // testGetNYTimeZone();
14467
+ // testGetNextMarketDay();
14468
+ // testCountTradingDays();
14469
+ // testGetPreviousMarketDay();
14470
+ // testOpenRouter();
14471
+ // testGetMarketStatus();
14472
+ // testCryptoMarketData();
14473
+ // testGetPortfolioDailyHistory();
14474
+ // testWebSocketConnectAndDisconnect();
14475
+ testGetAssetsShortableFilter();
14264
14476
  //# sourceMappingURL=test.js.map