@adaptic/utils 0.0.902 → 0.0.904

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 CHANGED
@@ -14,33 +14,12 @@ var require$$0$3 = require('stream');
14
14
  var require$$7 = require('url');
15
15
  var require$$0$1 = require('zlib');
16
16
  var require$$0$2 = require('buffer');
17
- var readline = require('readline');
18
- var fs = require('fs');
19
- var path = require('path');
17
+ var require$$0$5 = require('fs');
18
+ var require$$1$2 = require('path');
20
19
  var require$$4$1 = require('assert');
21
- var require$$1$2 = require('tty');
22
- var require$$1$3 = require('util');
23
- var require$$0$5 = require('os');
24
-
25
- function _interopNamespaceDefault(e) {
26
- var n = Object.create(null);
27
- if (e) {
28
- Object.keys(e).forEach(function (k) {
29
- if (k !== 'default') {
30
- var d = Object.getOwnPropertyDescriptor(e, k);
31
- Object.defineProperty(n, k, d.get ? d : {
32
- enumerable: true,
33
- get: function () { return e[k]; }
34
- });
35
- }
36
- });
37
- }
38
- n.default = e;
39
- return Object.freeze(n);
40
- }
41
-
42
- var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
43
- var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
20
+ var require$$1$3 = require('tty');
21
+ var require$$1$4 = require('util');
22
+ var require$$0$6 = require('os');
44
23
 
45
24
  /**
46
25
  * Configurable logger interface compatible with Pino and other logging libraries.
@@ -645,7 +624,7 @@ async function createOrder$1(auth, params) {
645
624
  try {
646
625
  const { APIKey, APISecret, type } = await validateAuth(auth);
647
626
  const apiBaseUrl = getTradingApiUrl(type);
648
- const response = await fetch(`${apiBaseUrl}/v2/orders`, {
627
+ const response = await fetch(`${apiBaseUrl}/orders`, {
649
628
  method: "POST",
650
629
  headers: {
651
630
  "APCA-API-KEY-ID": APIKey,
@@ -694,7 +673,7 @@ async function getOrders$1(auth, params = {}) {
694
673
  queryParams.append("symbols", params.symbols.join(","));
695
674
  if (params.side)
696
675
  queryParams.append("side", params.side);
697
- const response = await fetch(`${apiBaseUrl}/v2/orders?${queryParams}`, {
676
+ const response = await fetch(`${apiBaseUrl}/orders?${queryParams}`, {
698
677
  method: "GET",
699
678
  headers: {
700
679
  "APCA-API-KEY-ID": APIKey,
@@ -732,7 +711,7 @@ async function cancelAllOrders$1(auth) {
732
711
  try {
733
712
  const { APIKey, APISecret, type } = await validateAuth(auth);
734
713
  const apiBaseUrl = getTradingApiUrl(type);
735
- const response = await fetch(`${apiBaseUrl}/v2/orders`, {
714
+ const response = await fetch(`${apiBaseUrl}/orders`, {
736
715
  method: "DELETE",
737
716
  headers: {
738
717
  "APCA-API-KEY-ID": APIKey,
@@ -765,7 +744,7 @@ async function getOrder$1(auth, orderId, nested) {
765
744
  const queryParams = new URLSearchParams();
766
745
  if (nested)
767
746
  queryParams.append("nested", "true");
768
- const response = await fetch(`${apiBaseUrl}/v2/orders/${orderId}?${queryParams}`, {
747
+ const response = await fetch(`${apiBaseUrl}/orders/${orderId}?${queryParams}`, {
769
748
  method: "GET",
770
749
  headers: {
771
750
  "APCA-API-KEY-ID": APIKey,
@@ -795,7 +774,7 @@ async function replaceOrder$1(auth, orderId, params) {
795
774
  try {
796
775
  const { APIKey, APISecret, type } = await validateAuth(auth);
797
776
  const apiBaseUrl = getTradingApiUrl(type);
798
- const response = await fetch(`${apiBaseUrl}/v2/orders/${orderId}`, {
777
+ const response = await fetch(`${apiBaseUrl}/orders/${orderId}`, {
799
778
  method: "PATCH",
800
779
  headers: {
801
780
  "APCA-API-KEY-ID": APIKey,
@@ -827,7 +806,7 @@ async function cancelOrder$1(auth, orderId) {
827
806
  try {
828
807
  const { APIKey, APISecret, type } = await validateAuth(auth);
829
808
  const apiBaseUrl = getTradingApiUrl(type);
830
- const response = await fetch(`${apiBaseUrl}/v2/orders/${orderId}`, {
809
+ const response = await fetch(`${apiBaseUrl}/orders/${orderId}`, {
831
810
  method: "DELETE",
832
811
  headers: {
833
812
  "APCA-API-KEY-ID": APIKey,
@@ -886,7 +865,7 @@ async function createLimitOrder(auth, params = {
886
865
  body.client_order_id = client_order_id;
887
866
  }
888
867
  return makeRequest(auth, {
889
- endpoint: "/v2/orders",
868
+ endpoint: "/orders",
890
869
  method: "POST",
891
870
  body,
892
871
  });
@@ -1515,7 +1494,7 @@ async function fetchAllPositions(auth) {
1515
1494
  try {
1516
1495
  const { APIKey, APISecret, type } = await validateAuth(auth);
1517
1496
  const apiBaseUrl = getTradingApiUrl(type);
1518
- const apiUrl = `${apiBaseUrl}/v2/positions`;
1497
+ const apiUrl = `${apiBaseUrl}/positions`;
1519
1498
  const response = await fetch(apiUrl, {
1520
1499
  method: "GET",
1521
1500
  headers: {
@@ -1546,7 +1525,7 @@ async function fetchPosition(auth, symbolOrAssetId) {
1546
1525
  try {
1547
1526
  const { APIKey, APISecret, type } = await validateAuth(auth);
1548
1527
  const apiBaseUrl = getTradingApiUrl(type);
1549
- const response = await fetch(`${apiBaseUrl}/v2/positions/${symbolOrAssetId}`, {
1528
+ const response = await fetch(`${apiBaseUrl}/positions/${symbolOrAssetId}`, {
1550
1529
  method: "GET",
1551
1530
  headers: {
1552
1531
  "APCA-API-KEY-ID": APIKey,
@@ -1663,7 +1642,7 @@ async function closePosition$1(auth, symbolOrAssetId, params) {
1663
1642
  queryParams.append("percentage", params.percentage.toString());
1664
1643
  }
1665
1644
  const queryString = queryParams.toString();
1666
- const url = `${apiBaseUrl}/v2/positions/${encodeURIComponent(symbolOrAssetId)}${queryString ? `?${queryString}` : ""}`;
1645
+ const url = `${apiBaseUrl}/positions/${encodeURIComponent(symbolOrAssetId)}${queryString ? `?${queryString}` : ""}`;
1667
1646
  const response = await fetch(url, {
1668
1647
  method: "DELETE",
1669
1648
  headers: {
@@ -1769,7 +1748,7 @@ async function closeAllPositions$1(auth, params = { cancel_orders: true, useLimi
1769
1748
  }
1770
1749
  else {
1771
1750
  const response = await makeRequest(auth, {
1772
- endpoint: "/v2/positions",
1751
+ endpoint: "/positions",
1773
1752
  method: "DELETE",
1774
1753
  queryString: cancel_orders ? "?cancel_orders=true" : "",
1775
1754
  });
@@ -1935,7 +1914,7 @@ async function fetchPortfolioHistory({ params, accountId, client, alpacaAccount,
1935
1914
  }
1936
1915
  const { APIKey, APISecret, type } = alpacaAccountObj;
1937
1916
  const apiBaseUrl = getTradingApiUrl(type);
1938
- const apiUrl = `${apiBaseUrl}/v2/account/portfolio/history`;
1917
+ const apiUrl = `${apiBaseUrl}/account/portfolio/history`;
1939
1918
  const { start, end, period } = params;
1940
1919
  // Validate date formats
1941
1920
  if (start) {
@@ -2242,7 +2221,7 @@ async function getAsset(auth, symbolOrAssetId) {
2242
2221
  const apiBaseUrl = getTradingApiUrl(type);
2243
2222
  // Use encodeURIComponent to handle special characters in symbols (e.g., BTC/USDT)
2244
2223
  const encodedSymbolOrAssetId = encodeURIComponent(symbolOrAssetId);
2245
- const response = await fetch(`${apiBaseUrl}/v2/assets/${encodedSymbolOrAssetId}`, {
2224
+ const response = await fetch(`${apiBaseUrl}/assets/${encodedSymbolOrAssetId}`, {
2246
2225
  method: "GET",
2247
2226
  headers: {
2248
2227
  "APCA-API-KEY-ID": APIKey,
@@ -5186,6 +5165,11 @@ function validateConcurrency(concurrency) {
5186
5165
  /**********************************************************************************
5187
5166
  * Polygon.io calls
5188
5167
  **********************************************************************************/
5168
+ /**
5169
+ * Set of Polygon API response statuses that indicate valid, usable data.
5170
+ * "OK" = real-time data, "DELAYED" = delayed data (still valid, e.g. free-tier plans).
5171
+ */
5172
+ const POLYGON_VALID_STATUSES = new Set(["OK", "DELAYED"]);
5189
5173
  // Constants from environment variables
5190
5174
  const POLYGON_API_KEY = process.env.POLYGON_API_KEY;
5191
5175
  // Define concurrency limits per API
@@ -5300,7 +5284,7 @@ const fetchLastTrade = async (symbol, options) => {
5300
5284
  try {
5301
5285
  const response = await fetchWithRetry(`${baseUrl}?${params.toString()}`, {}, 3, 1000);
5302
5286
  const data = await response.json();
5303
- if (data.status !== "OK" || !data.results) {
5287
+ if (!POLYGON_VALID_STATUSES.has(data.status) || !data.results) {
5304
5288
  throw new Error(`Polygon.io API error: ${data.status || "No results"} ${data.error || ""}`);
5305
5289
  }
5306
5290
  const { p: price, s: vol, t: timestamp } = data.results;
@@ -5372,9 +5356,12 @@ const fetchPrices = async (params, options) => {
5372
5356
  //getLogger().info(`Debug: Fetching ${nextUrl}`);
5373
5357
  const response = await fetchWithRetry(nextUrl, {}, 3, 1000);
5374
5358
  const data = await response.json();
5375
- if (data.status !== "OK") {
5359
+ if (!POLYGON_VALID_STATUSES.has(data.status)) {
5376
5360
  throw new Error(`Polygon.io API responded with status: ${data.status}`);
5377
5361
  }
5362
+ if (data.status === "DELAYED") {
5363
+ getLogger().warn(`Polygon.io returned DELAYED data for ${params.ticker} — using delayed results`, { ticker: params.ticker, source: "PolygonAPI.fetchPrices" });
5364
+ }
5378
5365
  if (data.results) {
5379
5366
  allResults = [...allResults, ...data.results];
5380
5367
  }
@@ -5486,7 +5473,7 @@ const fetchGroupedDaily = async (date, options) => {
5486
5473
  try {
5487
5474
  const response = await fetchWithRetry(`${baseUrl}?${params.toString()}`, {}, 3, 1000);
5488
5475
  const data = await response.json();
5489
- if (data.status !== "OK") {
5476
+ if (!POLYGON_VALID_STATUSES.has(data.status)) {
5490
5477
  throw new Error(`Polygon.io API responded with status: ${data.status}`);
5491
5478
  }
5492
5479
  return {
@@ -5577,7 +5564,7 @@ symbol, date = new Date(), options) => {
5577
5564
  return polygonLimit(async () => {
5578
5565
  const response = await fetchWithRetry(`${baseUrl}?${params.toString()}`, {}, 3, 1000);
5579
5566
  const data = await response.json();
5580
- if (data.status !== "OK") {
5567
+ if (!POLYGON_VALID_STATUSES.has(data.status)) {
5581
5568
  throw new Error(`Failed to fetch daily open/close data for ${symbol}: ${data.status}`);
5582
5569
  }
5583
5570
  return data;
@@ -12156,6 +12143,33 @@ Object.defineProperties(createChalk.prototype, styles);
12156
12143
  const chalk = createChalk();
12157
12144
  createChalk({level: stderrColor ? stderrColor.level : 0});
12158
12145
 
12146
+ // Detect whether we are running in a Node.js environment with a real stdout.
12147
+ // In browsers (or environments without process.stdout) all terminal / fs
12148
+ // operations become no-ops so the library can be safely imported client-side.
12149
+ const isNode = typeof process !== "undefined" &&
12150
+ typeof process.stdout !== "undefined" &&
12151
+ typeof process.stdout.write === "function";
12152
+ // Lazy-load Node-only dependencies so bundlers can tree-shake / stub them.
12153
+ // chalk is kept as a static import (ESM-only in v5) — Rollup handles it.
12154
+ // readline, fs, and path are loaded at runtime only in Node.
12155
+ let clearLine;
12156
+ let cursorTo;
12157
+ let fs;
12158
+ let path;
12159
+ if (isNode) {
12160
+ try {
12161
+ /* eslint-disable @typescript-eslint/no-require-imports */
12162
+ const readline = require("readline");
12163
+ clearLine = readline.clearLine;
12164
+ cursorTo = readline.cursorTo;
12165
+ fs = require("fs");
12166
+ path = require("path");
12167
+ /* eslint-enable @typescript-eslint/no-require-imports */
12168
+ }
12169
+ catch {
12170
+ // Silently degrade — all operations will be no-ops.
12171
+ }
12172
+ }
12159
12173
  class DisplayManager {
12160
12174
  static instance;
12161
12175
  promptText = "";
@@ -12173,9 +12187,19 @@ class DisplayManager {
12173
12187
  * Logs a message while preserving the prompt at the bottom
12174
12188
  */
12175
12189
  log(message, options) {
12190
+ if (!isNode) {
12191
+ // In browser environments, fall back to console
12192
+ const level = options?.type === "error"
12193
+ ? "error"
12194
+ : options?.type === "warn"
12195
+ ? "warn"
12196
+ : "log";
12197
+ console[level](message);
12198
+ return;
12199
+ }
12176
12200
  // Clear the current prompt line
12177
- readline.clearLine(process.stdout, 0);
12178
- readline.cursorTo(process.stdout, 0);
12201
+ clearLine?.(process.stdout, 0);
12202
+ cursorTo?.(process.stdout, 0);
12179
12203
  // Format the timestamp
12180
12204
  const date = new Date();
12181
12205
  const timestamp = date.toLocaleString("en-US", {
@@ -12186,11 +12210,13 @@ class DisplayManager {
12186
12210
  // Build the log message
12187
12211
  let logMessage = `[${timestamp}]${options?.source ? ` [${options.source}] ` : ""}${account ? ` [${account}] ` : ""}${symbol ? ` [${symbol}] ` : ""}${message}`;
12188
12212
  // Add color based on type
12189
- if (options?.type === "error") {
12190
- logMessage = chalk.red(logMessage);
12191
- }
12192
- else if (options?.type === "warn") {
12193
- logMessage = chalk.yellow(logMessage);
12213
+ if (chalk) {
12214
+ if (options?.type === "error") {
12215
+ logMessage = chalk.red(logMessage);
12216
+ }
12217
+ else if (options?.type === "warn") {
12218
+ logMessage = chalk.yellow(logMessage);
12219
+ }
12194
12220
  }
12195
12221
  // Write the log message
12196
12222
  process.stdout.write(logMessage + "\n");
@@ -12210,10 +12236,12 @@ class DisplayManager {
12210
12236
  * Writes a log entry to a symbol-specific log file
12211
12237
  */
12212
12238
  writeSymbolLog(symbol, date, logMessage, options) {
12239
+ if (!fs || !path)
12240
+ return;
12213
12241
  try {
12214
12242
  // Create logs directory if it doesn't exist
12215
- if (!fs__namespace.existsSync("logs")) {
12216
- fs__namespace.mkdirSync("logs", { recursive: true });
12243
+ if (!fs.existsSync("logs")) {
12244
+ fs.mkdirSync("logs", { recursive: true });
12217
12245
  }
12218
12246
  // Format date for filename: YYYY-MM-DD
12219
12247
  const year = date.getFullYear();
@@ -12221,11 +12249,11 @@ class DisplayManager {
12221
12249
  const day = String(date.getDate()).padStart(2, "0");
12222
12250
  // Create filename: SYM-YYYY-MM-DD.log
12223
12251
  const filename = `${symbol}-${year}-${month}-${day}.log`;
12224
- const filePath = path__namespace.join("logs", filename);
12252
+ const filePath = path.join("logs", filename);
12225
12253
  // Strip ANSI color codes from log message
12226
12254
  const plainLogMessage = logMessage.replace(/\x1B\[\d+m/g, "");
12227
12255
  // Write to file (append if exists, create if not)
12228
- fs__namespace.appendFileSync(filePath, plainLogMessage + "\n");
12256
+ fs.appendFileSync(filePath, plainLogMessage + "\n");
12229
12257
  }
12230
12258
  catch (error) {
12231
12259
  // Only log to console - don't try to log to file again to avoid potential infinite loop
@@ -12236,10 +12264,12 @@ class DisplayManager {
12236
12264
  * Writes a log entry to a generic log file when no symbol is provided
12237
12265
  */
12238
12266
  writeGenericLog(date, logMessage, options) {
12267
+ if (!fs || !path)
12268
+ return;
12239
12269
  try {
12240
12270
  // Create logs directory if it doesn't exist
12241
- if (!fs__namespace.existsSync("logs")) {
12242
- fs__namespace.mkdirSync("logs", { recursive: true });
12271
+ if (!fs.existsSync("logs")) {
12272
+ fs.mkdirSync("logs", { recursive: true });
12243
12273
  }
12244
12274
  // Format date for filename: YYYY-MM-DD
12245
12275
  const year = date.getFullYear();
@@ -12248,11 +12278,11 @@ class DisplayManager {
12248
12278
  // Create filename: system-YYYY-MM-DD.log
12249
12279
  const source = options?.source?.toLowerCase().replace(/\s+/g, "-") || "system";
12250
12280
  const filename = `${source}-${year}-${month}-${day}.log`;
12251
- const filePath = path__namespace.join("logs", filename);
12281
+ const filePath = path.join("logs", filename);
12252
12282
  // Strip ANSI color codes from log message
12253
12283
  const plainLogMessage = logMessage.replace(/\x1B\[\d+m/g, "");
12254
12284
  // Write to file (append if exists, create if not)
12255
- fs__namespace.appendFileSync(filePath, plainLogMessage + "\n");
12285
+ fs.appendFileSync(filePath, plainLogMessage + "\n");
12256
12286
  }
12257
12287
  catch (error) {
12258
12288
  // Only log to console - don't try to log to file again to avoid potential infinite loop
@@ -12260,11 +12290,15 @@ class DisplayManager {
12260
12290
  }
12261
12291
  }
12262
12292
  writePrompt() {
12293
+ if (!isNode)
12294
+ return;
12263
12295
  process.stdout.write(this.promptText);
12264
12296
  }
12265
12297
  clearPrompt() {
12266
- readline.clearLine(process.stdout, 0);
12267
- readline.cursorTo(process.stdout, 0);
12298
+ if (!isNode)
12299
+ return;
12300
+ clearLine?.(process.stdout, 0);
12301
+ cursorTo?.(process.stdout, 0);
12268
12302
  }
12269
12303
  restorePrompt() {
12270
12304
  this.writePrompt();
@@ -15237,8 +15271,8 @@ function requireMain () {
15237
15271
 
15238
15272
  */
15239
15273
 
15240
- const fs$1 = fs;
15241
- const path$1 = path;
15274
+ const fs = require$$0$5;
15275
+ const path = require$$1$2;
15242
15276
 
15243
15277
  function log (message /*: string */) {
15244
15278
  console.log(`[dotenv][DEBUG] ${message}`);
@@ -15280,7 +15314,7 @@ function requireMain () {
15280
15314
 
15281
15315
  // Populates process.env from .env file
15282
15316
  function config (options /*: ?DotenvConfigOptions */) /*: DotenvConfigOutput */ {
15283
- let dotenvPath = path$1.resolve(process.cwd(), '.env');
15317
+ let dotenvPath = path.resolve(process.cwd(), '.env');
15284
15318
  let encoding /*: string */ = 'utf8';
15285
15319
  let debug = false;
15286
15320
 
@@ -15298,7 +15332,7 @@ function requireMain () {
15298
15332
 
15299
15333
  try {
15300
15334
  // specifying an encoding returns a string instead of a buffer
15301
- const parsed = parse(fs$1.readFileSync(dotenvPath, { encoding }), { debug });
15335
+ const parsed = parse(fs.readFileSync(dotenvPath, { encoding }), { debug });
15302
15336
 
15303
15337
  Object.keys(parsed).forEach(function (key) {
15304
15338
  if (!process.env.hasOwnProperty(key)) {
@@ -17046,8 +17080,8 @@ var hasRequiredSupportsColor;
17046
17080
  function requireSupportsColor () {
17047
17081
  if (hasRequiredSupportsColor) return supportsColor_1;
17048
17082
  hasRequiredSupportsColor = 1;
17049
- const os = require$$0$5;
17050
- const tty = require$$1$2;
17083
+ const os = require$$0$6;
17084
+ const tty = require$$1$3;
17051
17085
  const hasFlag = requireHasFlag();
17052
17086
 
17053
17087
  const {env} = process;
@@ -17193,8 +17227,8 @@ function requireNode$1 () {
17193
17227
  if (hasRequiredNode$1) return node$1.exports;
17194
17228
  hasRequiredNode$1 = 1;
17195
17229
  (function (module, exports$1) {
17196
- const tty = require$$1$2;
17197
- const util = require$$1$3;
17230
+ const tty = require$$1$3;
17231
+ const util = require$$1$4;
17198
17232
 
17199
17233
  /**
17200
17234
  * This is the Node.js implementation of `debug()`.
@@ -19540,7 +19574,7 @@ function requireUrljoin () {
19540
19574
 
19541
19575
  var extend = requireExtend();
19542
19576
  var url = require$$7;
19543
- var path$1 = path;
19577
+ var path = require$$1$2;
19544
19578
 
19545
19579
  /**
19546
19580
  * Join two or more url pieces into one.
@@ -19585,7 +19619,7 @@ function requireUrljoin () {
19585
19619
 
19586
19620
  delete first.search; //we use query instead of search
19587
19621
  first.query = query;
19588
- first.pathname = path$1.join.apply(path$1, paths).replace(new RegExp('\\' + path$1.sep, 'g'), '/');
19622
+ first.pathname = path.join.apply(path, paths).replace(new RegExp('\\' + path.sep, 'g'), '/');
19589
19623
  return url.format(first);
19590
19624
  };
19591
19625
  return urljoin;
@@ -47179,7 +47213,7 @@ function requireBuffer_list () {
47179
47213
  function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (String )(input); }
47180
47214
  var _require = require$$0$2,
47181
47215
  Buffer = _require.Buffer;
47182
- var _require2 = require$$1$3,
47216
+ var _require2 = require$$1$4,
47183
47217
  inspect = _require2.inspect;
47184
47218
  var custom = inspect && inspect.custom || 'inspect';
47185
47219
  function copyBuffer(src, target, offset) {
@@ -47678,7 +47712,7 @@ function requireNode () {
47678
47712
  * For Node.js, simply re-export the core `util.deprecate` function.
47679
47713
  */
47680
47714
 
47681
- node = require$$1$3.deprecate;
47715
+ node = require$$1$4.deprecate;
47682
47716
  return node;
47683
47717
  }
47684
47718
 
@@ -49064,7 +49098,7 @@ function require_stream_readable () {
49064
49098
  }
49065
49099
 
49066
49100
  /*<replacement>*/
49067
- var debugUtil = require$$1$3;
49101
+ var debugUtil = require$$1$4;
49068
49102
  var debug;
49069
49103
  if (debugUtil && debugUtil.debuglog) {
49070
49104
  debug = debugUtil.debuglog('stream');
@@ -50916,7 +50950,7 @@ function requireHelpers () {
50916
50950
  if (hasRequiredHelpers) return helpers;
50917
50951
  hasRequiredHelpers = 1;
50918
50952
 
50919
- const util = require$$1$3;
50953
+ const util = require$$1$4;
50920
50954
 
50921
50955
  helpers.IncompleteBufferError = IncompleteBufferError;
50922
50956