@adaptic/utils 0.0.376 → 0.0.378

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
@@ -1,4 +1,4 @@
1
- import adaptic$1, { getApolloClient } from '@adaptic/backend-legacy';
1
+ import adaptic$1, { setTokenProvider, getApolloClient } from '@adaptic/backend-legacy';
2
2
  import { format, sub, set, add, startOfDay, endOfDay, isBefore, differenceInMilliseconds } from 'date-fns';
3
3
  import { formatInTimeZone, toZonedTime, fromZonedTime } from 'date-fns-tz';
4
4
  import ms from 'ms';
@@ -18,6 +18,54 @@ import * as path from 'path';
18
18
 
19
19
  // Keep track of a single instance of Apollo client
20
20
  let apolloClientInstance = null;
21
+ // Track if auth has been configured
22
+ let authConfigured = false;
23
+ /**
24
+ * Configure the Apollo client authentication with a dynamic token provider.
25
+ * This should be called once during app initialization before making any
26
+ * @adaptic/backend-legacy API calls.
27
+ *
28
+ * The token provider function will be called for each GraphQL request,
29
+ * allowing for dynamic token retrieval (e.g., from session storage, SecretsManager, etc.)
30
+ *
31
+ * @param provider - Function that returns the auth token (sync or async)
32
+ *
33
+ * @example
34
+ * // Configure with an environment variable
35
+ * configureAuth(() => process.env.GRAPHQL_API_KEY || '');
36
+ *
37
+ * @example
38
+ * // Configure with NextAuth session token (async)
39
+ * configureAuth(async () => {
40
+ * const session = await auth();
41
+ * return session?.accessToken || '';
42
+ * });
43
+ *
44
+ * @example
45
+ * // Configure with SecretsManager
46
+ * configureAuth(() => {
47
+ * const secrets = getSecretsManager();
48
+ * return secrets.getGraphQLConfig().apiKey || '';
49
+ * });
50
+ */
51
+ const configureAuth = (provider) => {
52
+ if (authConfigured) {
53
+ console.warn('[adaptic] Auth provider already configured. Calling configureAuth again will reset the client.');
54
+ }
55
+ setTokenProvider(provider);
56
+ authConfigured = true;
57
+ // Reset the cached client so it picks up the new auth on next request
58
+ if (apolloClientInstance) {
59
+ apolloClientInstance = null;
60
+ console.log('[adaptic] Apollo client reset due to auth configuration change');
61
+ }
62
+ };
63
+ /**
64
+ * Check if Apollo auth has been configured.
65
+ */
66
+ const isAuthConfigured = () => {
67
+ return authConfigured;
68
+ };
21
69
  /**
22
70
  * Returns a shared Apollo client instance with connection pooling.
23
71
  * This should be used for all @adaptic/backend-legacy operations.
@@ -4225,6 +4273,12 @@ class Queue {
4225
4273
 
4226
4274
  this.#head = this.#head.next;
4227
4275
  this.#size--;
4276
+
4277
+ // Clean up tail reference when queue becomes empty
4278
+ if (!this.#head) {
4279
+ this.#tail = undefined;
4280
+ }
4281
+
4228
4282
  return current.value;
4229
4283
  }
4230
4284
 
@@ -13841,6 +13895,7 @@ class LRUCache {
13841
13895
  #sizes;
13842
13896
  #starts;
13843
13897
  #ttls;
13898
+ #autopurgeTimers;
13844
13899
  #hasDispose;
13845
13900
  #hasFetchMethod;
13846
13901
  #hasDisposeAfter;
@@ -13859,6 +13914,7 @@ class LRUCache {
13859
13914
  // properties
13860
13915
  starts: c.#starts,
13861
13916
  ttls: c.#ttls,
13917
+ autopurgeTimers: c.#autopurgeTimers,
13862
13918
  sizes: c.#sizes,
13863
13919
  keyMap: c.#keyMap,
13864
13920
  keyList: c.#keyList,
@@ -13960,13 +14016,11 @@ class LRUCache {
13960
14016
  throw new TypeError('sizeCalculation set to non-function');
13961
14017
  }
13962
14018
  }
13963
- if (memoMethod !== undefined &&
13964
- typeof memoMethod !== 'function') {
14019
+ if (memoMethod !== undefined && typeof memoMethod !== 'function') {
13965
14020
  throw new TypeError('memoMethod must be a function if defined');
13966
14021
  }
13967
14022
  this.#memoMethod = memoMethod;
13968
- if (fetchMethod !== undefined &&
13969
- typeof fetchMethod !== 'function') {
14023
+ if (fetchMethod !== undefined && typeof fetchMethod !== 'function') {
13970
14024
  throw new TypeError('fetchMethod must be a function if specified');
13971
14025
  }
13972
14026
  this.#fetchMethod = fetchMethod;
@@ -14021,9 +14075,7 @@ class LRUCache {
14021
14075
  this.updateAgeOnGet = !!updateAgeOnGet;
14022
14076
  this.updateAgeOnHas = !!updateAgeOnHas;
14023
14077
  this.ttlResolution =
14024
- isPosInt(ttlResolution) || ttlResolution === 0 ?
14025
- ttlResolution
14026
- : 1;
14078
+ isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1;
14027
14079
  this.ttlAutopurge = !!ttlAutopurge;
14028
14080
  this.ttl = ttl || 0;
14029
14081
  if (this.ttl) {
@@ -14058,10 +14110,21 @@ class LRUCache {
14058
14110
  const starts = new ZeroArray(this.#max);
14059
14111
  this.#ttls = ttls;
14060
14112
  this.#starts = starts;
14113
+ const purgeTimers = this.ttlAutopurge ?
14114
+ new Array(this.#max)
14115
+ : undefined;
14116
+ this.#autopurgeTimers = purgeTimers;
14061
14117
  this.#setItemTTL = (index, ttl, start = this.#perf.now()) => {
14062
14118
  starts[index] = ttl !== 0 ? start : 0;
14063
14119
  ttls[index] = ttl;
14064
- if (ttl !== 0 && this.ttlAutopurge) {
14120
+ // clear out the purge timer if we're setting TTL to 0, and
14121
+ // previously had a ttl purge timer running, so it doesn't
14122
+ // fire unnecessarily.
14123
+ if (purgeTimers?.[index]) {
14124
+ clearTimeout(purgeTimers[index]);
14125
+ purgeTimers[index] = undefined;
14126
+ }
14127
+ if (ttl !== 0 && purgeTimers) {
14065
14128
  const t = setTimeout(() => {
14066
14129
  if (this.#isStale(index)) {
14067
14130
  this.#delete(this.#keyList[index], 'expire');
@@ -14073,6 +14136,7 @@ class LRUCache {
14073
14136
  t.unref();
14074
14137
  }
14075
14138
  /* c8 ignore stop */
14139
+ purgeTimers[index] = t;
14076
14140
  }
14077
14141
  };
14078
14142
  this.#updateItemAge = index => {
@@ -14264,8 +14328,7 @@ class LRUCache {
14264
14328
  *keys() {
14265
14329
  for (const i of this.#indexes()) {
14266
14330
  const k = this.#keyList[i];
14267
- if (k !== undefined &&
14268
- !this.#isBackgroundFetch(this.#valList[i])) {
14331
+ if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
14269
14332
  yield k;
14270
14333
  }
14271
14334
  }
@@ -14279,8 +14342,7 @@ class LRUCache {
14279
14342
  *rkeys() {
14280
14343
  for (const i of this.#rindexes()) {
14281
14344
  const k = this.#keyList[i];
14282
- if (k !== undefined &&
14283
- !this.#isBackgroundFetch(this.#valList[i])) {
14345
+ if (k !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
14284
14346
  yield k;
14285
14347
  }
14286
14348
  }
@@ -14292,8 +14354,7 @@ class LRUCache {
14292
14354
  *values() {
14293
14355
  for (const i of this.#indexes()) {
14294
14356
  const v = this.#valList[i];
14295
- if (v !== undefined &&
14296
- !this.#isBackgroundFetch(this.#valList[i])) {
14357
+ if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
14297
14358
  yield this.#valList[i];
14298
14359
  }
14299
14360
  }
@@ -14307,8 +14368,7 @@ class LRUCache {
14307
14368
  *rvalues() {
14308
14369
  for (const i of this.#rindexes()) {
14309
14370
  const v = this.#valList[i];
14310
- if (v !== undefined &&
14311
- !this.#isBackgroundFetch(this.#valList[i])) {
14371
+ if (v !== undefined && !this.#isBackgroundFetch(this.#valList[i])) {
14312
14372
  yield this.#valList[i];
14313
14373
  }
14314
14374
  }
@@ -14666,6 +14726,10 @@ class LRUCache {
14666
14726
  }
14667
14727
  }
14668
14728
  this.#removeItemSize(head);
14729
+ if (this.#autopurgeTimers?.[head]) {
14730
+ clearTimeout(this.#autopurgeTimers[head]);
14731
+ this.#autopurgeTimers[head] = undefined;
14732
+ }
14669
14733
  // if we aren't about to use the index, then null these out
14670
14734
  if (free) {
14671
14735
  this.#keyList[head] = undefined;
@@ -14738,8 +14802,7 @@ class LRUCache {
14738
14802
  peek(k, peekOptions = {}) {
14739
14803
  const { allowStale = this.allowStale } = peekOptions;
14740
14804
  const index = this.#keyMap.get(k);
14741
- if (index === undefined ||
14742
- (!allowStale && this.#isStale(index))) {
14805
+ if (index === undefined || (!allowStale && this.#isStale(index))) {
14743
14806
  return;
14744
14807
  }
14745
14808
  const v = this.#valList[index];
@@ -14785,7 +14848,7 @@ class LRUCache {
14785
14848
  // cache and ignore the abort, or if it's still pending on this specific
14786
14849
  // background request, then write it to the cache.
14787
14850
  const vl = this.#valList[index];
14788
- if (vl === p || ignoreAbort && updateCache && vl === undefined) {
14851
+ if (vl === p || (ignoreAbort && updateCache && vl === undefined)) {
14789
14852
  if (v === undefined) {
14790
14853
  if (bf.__staleWhileFetching !== undefined) {
14791
14854
  this.#valList[index] = bf.__staleWhileFetching;
@@ -14849,8 +14912,7 @@ class LRUCache {
14849
14912
  // defer check until we are actually aborting,
14850
14913
  // so fetchMethod can override.
14851
14914
  ac.signal.addEventListener('abort', () => {
14852
- if (!options.ignoreFetchAbort ||
14853
- options.allowStaleOnFetchAbort) {
14915
+ if (!options.ignoreFetchAbort || options.allowStaleOnFetchAbort) {
14854
14916
  res(undefined);
14855
14917
  // when it eventually resolves, update the cache.
14856
14918
  if (options.allowStaleOnFetchAbort) {
@@ -15082,6 +15144,10 @@ class LRUCache {
15082
15144
  if (this.#size !== 0) {
15083
15145
  const index = this.#keyMap.get(k);
15084
15146
  if (index !== undefined) {
15147
+ if (this.#autopurgeTimers?.[index]) {
15148
+ clearTimeout(this.#autopurgeTimers?.[index]);
15149
+ this.#autopurgeTimers[index] = undefined;
15150
+ }
15085
15151
  deleted = true;
15086
15152
  if (this.#size === 1) {
15087
15153
  this.#clear(reason);
@@ -15157,6 +15223,11 @@ class LRUCache {
15157
15223
  if (this.#ttls && this.#starts) {
15158
15224
  this.#ttls.fill(0);
15159
15225
  this.#starts.fill(0);
15226
+ for (const t of this.#autopurgeTimers ?? []) {
15227
+ if (t !== undefined)
15228
+ clearTimeout(t);
15229
+ }
15230
+ this.#autopurgeTimers?.fill(undefined);
15160
15231
  }
15161
15232
  if (this.#sizes) {
15162
15233
  this.#sizes.fill(0);
@@ -16768,6 +16839,8 @@ const adaptic = {
16768
16839
  backend: {
16769
16840
  fetchAssetOverview: fetchAssetOverview,
16770
16841
  getApolloClient: getSharedApolloClient,
16842
+ configureAuth: configureAuth,
16843
+ isAuthConfigured: isAuthConfigured,
16771
16844
  },
16772
16845
  alpaca: {
16773
16846
  TradingAPI: AlpacaTradingAPI,