@ganaka/sdk 1.9.0 → 1.10.1

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
@@ -4319,7 +4319,7 @@ const y = object({
4319
4319
  end_time: string(),
4320
4320
  interval_in_minutes: number$1()
4321
4321
  })
4322
- }), O = {
4322
+ }), P = {
4323
4323
  query: object({
4324
4324
  symbol: string(),
4325
4325
  interval: _enum(b),
@@ -4330,7 +4330,7 @@ const y = object({
4330
4330
  response: t.extend({
4331
4331
  data: y
4332
4332
  })
4333
- }, q = {
4333
+ }, O = {
4334
4334
  query: object({
4335
4335
  symbol: string(),
4336
4336
  exchange: _enum(["NSE", "BSE"]).optional(),
@@ -4341,11 +4341,11 @@ const y = object({
4341
4341
  response: t.extend({
4342
4342
  data: r.nullable()
4343
4343
  })
4344
- }, z = {
4344
+ }, q = {
4345
4345
  response: t.extend({
4346
4346
  data: string()
4347
4347
  })
4348
- }, w = {
4348
+ }, z = {
4349
4349
  query: object({
4350
4350
  symbol: string(),
4351
4351
  end_datetime: o,
@@ -4389,7 +4389,7 @@ const y = object({
4389
4389
  )
4390
4390
  })
4391
4391
  })
4392
- }, D = {
4392
+ }, M = {
4393
4393
  query: object({
4394
4394
  datetime: o.optional(),
4395
4395
  timezone: a.optional()
@@ -4397,20 +4397,20 @@ const y = object({
4397
4397
  response: t.extend({
4398
4398
  data: r.nullable()
4399
4399
  })
4400
- }, Ce = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4400
+ }, Ee = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4401
4401
  __proto__: null,
4402
- getGrowwHistoricalCandles: O,
4403
- getGrowwNiftyQuote: D,
4402
+ getGrowwHistoricalCandles: P,
4403
+ getGrowwNiftyQuote: M,
4404
4404
  getGrowwNiftyQuoteTimeline: A,
4405
- getGrowwQuote: q,
4406
- getGrowwQuoteTimeline: w,
4407
- getGrowwToken: z,
4405
+ getGrowwQuote: O,
4406
+ getGrowwQuoteTimeline: z,
4407
+ getGrowwToken: q,
4408
4408
  growwHistoricalCandlesSchema: y
4409
4409
  }, Symbol.toStringTag, { value: "Module" })), j = object({
4410
4410
  name: string(),
4411
4411
  price: number$1(),
4412
4412
  nseSymbol: string()
4413
- }), M = {
4413
+ }), D = {
4414
4414
  query: object({
4415
4415
  type: _enum(["top-gainers", "volume-shockers"]),
4416
4416
  datetime: o.optional(),
@@ -4420,9 +4420,9 @@ const y = object({
4420
4420
  response: t.extend({
4421
4421
  data: array(j).nullable()
4422
4422
  })
4423
- }, Re = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4423
+ }, Ie = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4424
4424
  __proto__: null,
4425
- getLists: M,
4425
+ getLists: D,
4426
4426
  listSchema: j
4427
4427
  }, Symbol.toStringTag, { value: "Module" })), C = {
4428
4428
  query: object({
@@ -4449,7 +4449,7 @@ const y = object({
4449
4449
  )
4450
4450
  })
4451
4451
  })
4452
- }, ke = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4452
+ }, He = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4453
4453
  __proto__: null,
4454
4454
  getShortlistPersistence: C
4455
4455
  }, Symbol.toStringTag, { value: "Module" }));
@@ -4482,7 +4482,7 @@ const y = object({
4482
4482
  })
4483
4483
  })
4484
4484
  });
4485
- const E = {
4485
+ const G = {
4486
4486
  query: object({}),
4487
4487
  response: t.extend({
4488
4488
  data: object({
@@ -4496,10 +4496,10 @@ const E = {
4496
4496
  )
4497
4497
  })
4498
4498
  })
4499
- }, H = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4499
+ }, E = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4500
4500
  __proto__: null,
4501
- getAvailableDatetimes: E
4502
- }, Symbol.toStringTag, { value: "Module" })), L = {
4501
+ getAvailableDatetimes: G
4502
+ }, Symbol.toStringTag, { value: "Module" })), I = {
4503
4503
  query: object({
4504
4504
  symbol: string(),
4505
4505
  date: n,
@@ -4521,10 +4521,10 @@ const E = {
4521
4521
  interval_in_minutes: number$1()
4522
4522
  })
4523
4523
  })
4524
- }, I = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4524
+ }, H = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4525
4525
  __proto__: null,
4526
- getCandles: L
4527
- }, Symbol.toStringTag, { value: "Module" })), N = {
4526
+ getCandles: I
4527
+ }, Symbol.toStringTag, { value: "Module" })), L = {
4528
4528
  query: object({
4529
4529
  date: n,
4530
4530
  type: _enum(t$1)
@@ -4544,10 +4544,10 @@ const E = {
4544
4544
  )
4545
4545
  })
4546
4546
  })
4547
- }, $ = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4547
+ }, N = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4548
4548
  __proto__: null,
4549
- getDailyPersistentCompanies: N
4550
- }, Symbol.toStringTag, { value: "Module" })), G = {
4549
+ getDailyPersistentCompanies: L
4550
+ }, Symbol.toStringTag, { value: "Module" })), $ = {
4551
4551
  query: object({
4552
4552
  date: n,
4553
4553
  type: _enum(t$1)
@@ -4561,7 +4561,7 @@ const E = {
4561
4561
  })
4562
4562
  }, Q = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4563
4563
  __proto__: null,
4564
- getDailyUniqueCompanies: G
4564
+ getDailyUniqueCompanies: $
4565
4565
  }, Symbol.toStringTag, { value: "Module" })), U = object({
4566
4566
  id: uuid(),
4567
4567
  username: string()
@@ -4572,10 +4572,10 @@ const E = {
4572
4572
  response: t.extend({
4573
4573
  data: U
4574
4574
  })
4575
- }, F = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4575
+ }, K = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4576
4576
  __proto__: null,
4577
4577
  signIn: Y
4578
- }, Symbol.toStringTag, { value: "Module" })), B = {
4578
+ }, Symbol.toStringTag, { value: "Module" })), F = {
4579
4579
  query: object({
4580
4580
  symbol: string(),
4581
4581
  date: n
@@ -4597,9 +4597,9 @@ const E = {
4597
4597
  )
4598
4598
  })
4599
4599
  })
4600
- }, K = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4600
+ }, B = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4601
4601
  __proto__: null,
4602
- getQuoteTimeline: B
4602
+ getQuoteTimeline: F
4603
4603
  }, Symbol.toStringTag, { value: "Module" })), Z = object({
4604
4604
  id: uuid(),
4605
4605
  start_datetime: string(),
@@ -4763,15 +4763,45 @@ const E = {
4763
4763
  __proto__: null,
4764
4764
  getShortlists: me,
4765
4765
  shortlistEntryWithMetricsSchema: h
4766
- }, Symbol.toStringTag, { value: "Module" })), Le = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4766
+ }, Symbol.toStringTag, { value: "Module" })), pe = {
4767
+ response: t.extend({
4768
+ data: object({
4769
+ hasGrowwApiKey: boolean(),
4770
+ hasGrowwApiSecret: boolean(),
4771
+ growwApiKeyMasked: string().nullable()
4772
+ })
4773
+ })
4774
+ }, ge = {
4775
+ body: object({
4776
+ growwApiKey: string().min(1, "Groww API key is required"),
4777
+ growwApiSecret: string().min(1, "Groww API secret is required")
4778
+ }),
4779
+ response: t.extend({
4780
+ data: object({
4781
+ success: boolean()
4782
+ })
4783
+ })
4784
+ }, _e = {
4785
+ response: t.extend({
4786
+ data: object({
4787
+ success: boolean()
4788
+ })
4789
+ })
4790
+ }, ye = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4767
4791
  __proto__: null,
4768
- v1_dashboard_auth_schemas: F,
4769
- v1_dashboard_available_datetimes_schemas: H,
4770
- v1_dashboard_candles_schemas: I,
4771
- v1_dashboard_daily_persistent_companies_schemas: $,
4792
+ deleteGrowwCredentials: _e,
4793
+ getGrowwCredentials: pe,
4794
+ updateGrowwCredentials: ge
4795
+ }, Symbol.toStringTag, { value: "Module" })), $e = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4796
+ __proto__: null,
4797
+ v1_dashboard_auth_schemas: K,
4798
+ v1_dashboard_available_datetimes_schemas: E,
4799
+ v1_dashboard_candles_schemas: H,
4800
+ v1_dashboard_daily_persistent_companies_schemas: N,
4772
4801
  v1_dashboard_daily_unique_companies_schemas: Q,
4773
- v1_dashboard_quote_timeline_schemas: K,
4802
+ v1_dashboard_quote_timeline_schemas: B,
4774
4803
  v1_dashboard_runs_schemas: ue,
4804
+ v1_dashboard_settings_schemas: ye,
4775
4805
  v1_dashboard_shortlists_schemas: be
4776
4806
  }, Symbol.toStringTag, { value: "Module" }));
4777
4807
  ({
@@ -6058,7 +6088,7 @@ function matchHeaderValue(context, value, header, filter2, isHeaderNameFilter) {
6058
6088
  }
6059
6089
  }
6060
6090
  function formatHeader(header) {
6061
- return header.trim().toLowerCase().replace(/([a-z\d])(\w*)/g, (w2, char, str) => {
6091
+ return header.trim().toLowerCase().replace(/([a-z\d])(\w*)/g, (w, char, str) => {
6062
6092
  return char.toUpperCase() + str;
6063
6093
  });
6064
6094
  }
@@ -8119,6 +8149,147 @@ const logger = pino$1({
8119
8149
  target: "pino-pretty"
8120
8150
  }
8121
8151
  });
8152
+ class RateLimiter {
8153
+ constructor(config2) {
8154
+ this.queue = [];
8155
+ this.secondTimestamps = [];
8156
+ this.minuteTimestamps = [];
8157
+ this.processing = false;
8158
+ this.inFlight = 0;
8159
+ this.maxPerSecond = config2.maxPerSecond;
8160
+ this.maxPerMinute = config2.maxPerMinute;
8161
+ this.maxConcurrency = config2.maxConcurrency ?? 10;
8162
+ this.requestTimeoutMs = config2.requestTimeoutMs ?? 3e4;
8163
+ }
8164
+ /**
8165
+ * Execute a function with rate limiting.
8166
+ * The function will be queued and executed when rate limits allow.
8167
+ */
8168
+ async execute(fn) {
8169
+ return new Promise((resolve, reject) => {
8170
+ this.queue.push({ execute: fn, resolve, reject });
8171
+ this.processQueue();
8172
+ });
8173
+ }
8174
+ /**
8175
+ * Process the queue of pending requests.
8176
+ * Dispatches requests concurrently up to maxConcurrency while respecting rate limits.
8177
+ */
8178
+ async processQueue() {
8179
+ if (this.processing) {
8180
+ return;
8181
+ }
8182
+ this.processing = true;
8183
+ while (this.queue.length > 0) {
8184
+ this.cleanupTimestamps();
8185
+ if (this.inFlight < this.maxConcurrency && this.canMakeRequest()) {
8186
+ const request = this.queue.shift();
8187
+ if (!request) {
8188
+ break;
8189
+ }
8190
+ const now = Date.now();
8191
+ this.secondTimestamps.push(now);
8192
+ this.minuteTimestamps.push(now);
8193
+ this.inFlight++;
8194
+ this.executeWithTimeout(request.execute, this.requestTimeoutMs).then((result) => {
8195
+ request.resolve(result);
8196
+ }).catch((error) => {
8197
+ request.reject(error instanceof Error ? error : new Error(String(error)));
8198
+ }).finally(() => {
8199
+ this.inFlight--;
8200
+ this.processQueue();
8201
+ });
8202
+ } else {
8203
+ if (this.inFlight >= this.maxConcurrency) {
8204
+ break;
8205
+ } else {
8206
+ const waitTime = this.getWaitTime();
8207
+ if (waitTime > 0) {
8208
+ await this.sleep(waitTime);
8209
+ } else {
8210
+ await this.sleep(10);
8211
+ }
8212
+ }
8213
+ }
8214
+ }
8215
+ this.processing = false;
8216
+ }
8217
+ /**
8218
+ * Check if a new request can be made based on current rate limits.
8219
+ */
8220
+ canMakeRequest() {
8221
+ const now = Date.now();
8222
+ const oneSecondAgo = now - 1e3;
8223
+ const oneMinuteAgo = now - 6e4;
8224
+ const recentSecondCount = this.secondTimestamps.filter((ts) => ts > oneSecondAgo).length;
8225
+ const recentMinuteCount = this.minuteTimestamps.filter((ts) => ts > oneMinuteAgo).length;
8226
+ return recentSecondCount < this.maxPerSecond && recentMinuteCount < this.maxPerMinute;
8227
+ }
8228
+ /**
8229
+ * Remove timestamps that are outside the tracking windows.
8230
+ */
8231
+ cleanupTimestamps() {
8232
+ const now = Date.now();
8233
+ const oneSecondAgo = now - 1e3;
8234
+ const oneMinuteAgo = now - 6e4;
8235
+ this.secondTimestamps = this.secondTimestamps.filter((ts) => ts > oneSecondAgo);
8236
+ this.minuteTimestamps = this.minuteTimestamps.filter((ts) => ts > oneMinuteAgo);
8237
+ }
8238
+ /**
8239
+ * Calculate the minimum wait time needed before the next request can be made.
8240
+ */
8241
+ getWaitTime() {
8242
+ const now = Date.now();
8243
+ const oneSecondAgo = now - 1e3;
8244
+ const oneMinuteAgo = now - 6e4;
8245
+ let waitTime = 0;
8246
+ const recentSecondCount = this.secondTimestamps.filter((ts) => ts > oneSecondAgo).length;
8247
+ if (recentSecondCount >= this.maxPerSecond) {
8248
+ const oldestInSecond = Math.min(...this.secondTimestamps.filter((ts) => ts > oneSecondAgo));
8249
+ const waitForSecond = oldestInSecond + 1e3 - now;
8250
+ waitTime = Math.max(waitTime, waitForSecond);
8251
+ }
8252
+ const recentMinuteCount = this.minuteTimestamps.filter((ts) => ts > oneMinuteAgo).length;
8253
+ if (recentMinuteCount >= this.maxPerMinute) {
8254
+ const oldestInMinute = Math.min(...this.minuteTimestamps.filter((ts) => ts > oneMinuteAgo));
8255
+ const waitForMinute = oldestInMinute + 6e4 - now;
8256
+ waitTime = Math.max(waitTime, waitForMinute);
8257
+ }
8258
+ return Math.max(waitTime, 0);
8259
+ }
8260
+ /**
8261
+ * Execute a function with a timeout.
8262
+ * If the function doesn't complete within the timeout, it will be rejected.
8263
+ */
8264
+ async executeWithTimeout(fn, timeoutMs) {
8265
+ return Promise.race([
8266
+ fn(),
8267
+ new Promise(
8268
+ (_2, reject) => setTimeout(
8269
+ () => reject(new Error(`Request timed out after ${timeoutMs}ms`)),
8270
+ timeoutMs
8271
+ )
8272
+ )
8273
+ ]);
8274
+ }
8275
+ /**
8276
+ * Sleep for a given number of milliseconds.
8277
+ */
8278
+ sleep(ms) {
8279
+ return new Promise((resolve) => setTimeout(resolve, ms));
8280
+ }
8281
+ }
8282
+ const limiters = /* @__PURE__ */ new Map();
8283
+ limiters.set(
8284
+ "groww",
8285
+ new RateLimiter({
8286
+ maxPerSecond: 10,
8287
+ maxPerMinute: 300,
8288
+ maxConcurrency: 7,
8289
+ requestTimeoutMs: 3e4
8290
+ })
8291
+ );
8292
+ const growwRateLimiter = limiters.get("groww");
8122
8293
  dayjs.extend(utc);
8123
8294
  dayjs.extend(timezone);
8124
8295
  const fetchCandles = ({
@@ -8134,7 +8305,7 @@ const fetchCandles = ({
8134
8305
  );
8135
8306
  }
8136
8307
  try {
8137
- const validatedParams = Ce.getGrowwHistoricalCandles.query.parse(params);
8308
+ const validatedParams = Ee.getGrowwHistoricalCandles.query.parse(params);
8138
8309
  const headers = {
8139
8310
  Authorization: `Bearer ${developerToken}`
8140
8311
  };
@@ -8147,10 +8318,15 @@ const fetchCandles = ({
8147
8318
  if (currentTimezone) {
8148
8319
  headers["X-Current-Timezone"] = currentTimezone;
8149
8320
  }
8150
- const response = await axios.get(`${apiDomain}/v1/developer/historical-candles`, {
8151
- params: validatedParams,
8152
- headers
8153
- });
8321
+ const response = await growwRateLimiter.execute(
8322
+ () => axios.get(
8323
+ `${apiDomain}/v1/developer/historical-candles`,
8324
+ {
8325
+ params: validatedParams,
8326
+ headers
8327
+ }
8328
+ )
8329
+ );
8154
8330
  return response.data.data;
8155
8331
  } catch (error) {
8156
8332
  if (axios.isAxiosError(error)) {
@@ -8178,7 +8354,7 @@ const fetchQuote = ({
8178
8354
  );
8179
8355
  }
8180
8356
  try {
8181
- const validatedParams = Ce.getGrowwQuote.query.parse(params);
8357
+ const validatedParams = Ee.getGrowwQuote.query.parse(params);
8182
8358
  const headers = {
8183
8359
  Authorization: `Bearer ${developerToken}`
8184
8360
  };
@@ -8218,7 +8394,7 @@ const fetchQuoteTimeline = ({
8218
8394
  throw new Error("Developer token not found. Please set DEVELOPER_KEY environment variable.");
8219
8395
  }
8220
8396
  try {
8221
- const validatedParams = Ce.getGrowwQuoteTimeline.query.parse({
8397
+ const validatedParams = Ee.getGrowwQuoteTimeline.query.parse({
8222
8398
  symbol,
8223
8399
  end_datetime
8224
8400
  });
@@ -8265,7 +8441,7 @@ const fetchShortlist = ({
8265
8441
  );
8266
8442
  }
8267
8443
  try {
8268
- const validatedParams = Re.getLists.query.parse(queryParams);
8444
+ const validatedParams = Ie.getLists.query.parse(queryParams);
8269
8445
  const headers = {
8270
8446
  Authorization: `Bearer ${developerToken}`
8271
8447
  };
@@ -8309,7 +8485,7 @@ const fetchShortlistPersistence = ({
8309
8485
  );
8310
8486
  }
8311
8487
  try {
8312
- const validatedParams = ke.getShortlistPersistence.query.parse(queryParams);
8488
+ const validatedParams = He.getShortlistPersistence.query.parse(queryParams);
8313
8489
  const headers = {
8314
8490
  Authorization: `Bearer ${developerToken}`
8315
8491
  };
@@ -8353,7 +8529,7 @@ const fetchNiftyQuote = ({
8353
8529
  throw new Error("Developer token not found. Please set DEVELOPER_KEY environment variable.");
8354
8530
  }
8355
8531
  try {
8356
- const validatedParams = Ce.getGrowwNiftyQuote.query.parse(params);
8532
+ const validatedParams = Ee.getGrowwNiftyQuote.query.parse(params);
8357
8533
  const headers = {
8358
8534
  Authorization: `Bearer ${developerToken}`
8359
8535
  };
@@ -8395,7 +8571,7 @@ const fetchNiftyQuoteTimeline = ({
8395
8571
  throw new Error("Developer token not found. Please set DEVELOPER_KEY environment variable.");
8396
8572
  }
8397
8573
  try {
8398
- const validatedParams = Ce.getGrowwNiftyQuoteTimeline.query.parse({
8574
+ const validatedParams = Ee.getGrowwNiftyQuoteTimeline.query.parse({
8399
8575
  end_datetime
8400
8576
  });
8401
8577
  const headers = {
@@ -8584,7 +8760,7 @@ const placeOrder = ({ runId, apiClient }) => async (data) => {
8584
8760
  try {
8585
8761
  await retryWithBackoff(
8586
8762
  async () => {
8587
- const validatedData = Le.v1_dashboard_runs_schemas.createOrder.body.parse(data);
8763
+ const validatedData = $e.v1_dashboard_runs_schemas.createOrder.body.parse(data);
8588
8764
  await apiClient.post(`/v1/dashboard/runs/${runId}/orders`, validatedData);
8589
8765
  logger.debug(`Order persisted for ${data.nseSymbol} in runId: ${runId}`);
8590
8766
  },