@secondlayer/sdk 10.4.3 → 10.6.0

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.
@@ -159,6 +159,30 @@ function parseRetryAfter(value) {
159
159
  }
160
160
 
161
161
  // src/base.ts
162
+ class MemoryEtagCache {
163
+ limit;
164
+ entries = new Map;
165
+ constructor(limit = 64) {
166
+ this.limit = limit;
167
+ }
168
+ get(key) {
169
+ const entry = this.entries.get(key);
170
+ if (entry) {
171
+ this.entries.delete(key);
172
+ this.entries.set(key, entry);
173
+ }
174
+ return entry;
175
+ }
176
+ set(key, entry) {
177
+ this.entries.delete(key);
178
+ this.entries.set(key, entry);
179
+ if (this.entries.size > this.limit) {
180
+ const oldest = this.entries.keys().next().value;
181
+ if (oldest !== undefined)
182
+ this.entries.delete(oldest);
183
+ }
184
+ }
185
+ }
162
186
  var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
163
187
  function abortError() {
164
188
  return new DOMException("This operation was aborted", "AbortError");
@@ -269,12 +293,14 @@ class BaseClient {
269
293
  origin;
270
294
  fetchImpl;
271
295
  requestTimeoutMs;
296
+ etagCache;
272
297
  constructor(options = {}) {
273
298
  this.baseUrl = resolveBaseUrl(options.baseUrl);
274
299
  this.apiKey = resolveApiKey(options.apiKey);
275
300
  this.origin = options.origin ?? "cli";
276
301
  this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
277
302
  this.fetchImpl = options.fetchImpl ?? ((...args) => fetch(...args));
303
+ this.etagCache = options.etagCache === false ? null : options.etagCache ?? new MemoryEtagCache;
278
304
  }
279
305
  static authHeaders(apiKey) {
280
306
  const headers = {
@@ -287,11 +313,25 @@ class BaseClient {
287
313
  }
288
314
  async request(method, path, body, opts = {}) {
289
315
  return this.withRequestBudget(opts.signal, async (signal) => {
290
- const response = await this.fetchResponse(method, path, body, signal);
316
+ const cacheKey = method === "GET" && this.etagCache ? `${this.baseUrl}${path}` : null;
317
+ const cached = cacheKey ? await this.etagCache?.get(cacheKey) : undefined;
318
+ const response = await this.fetchResponse(method, path, body, signal, cached ? { "If-None-Match": cached.etag } : undefined);
291
319
  if (response.status === 204) {
292
320
  return;
293
321
  }
294
- return await raceAbort(response.json(), signal);
322
+ if (response.status === 304) {
323
+ if (!cached) {
324
+ throw new ApiError(304, "Not Modified without a cached copy");
325
+ }
326
+ return JSON.parse(cached.body);
327
+ }
328
+ const etag = cacheKey ? response.headers.get("ETag") : null;
329
+ if (!cacheKey || !etag) {
330
+ return await raceAbort(response.json(), signal);
331
+ }
332
+ const text = await raceAbort(response.text(), signal);
333
+ await this.etagCache?.set(cacheKey, { etag, body: text });
334
+ return JSON.parse(text);
295
335
  });
296
336
  }
297
337
  async requestOrNull(method, path, body, opts = {}) {
@@ -334,9 +374,12 @@ class BaseClient {
334
374
  callerSignal?.removeEventListener("abort", forward);
335
375
  }
336
376
  }
337
- async fetchResponse(method, path, body, signal) {
377
+ async fetchResponse(method, path, body, signal, extraHeaders) {
338
378
  const url = `${this.baseUrl}${path}`;
339
- const headers = BaseClient.authHeaders(this.apiKey);
379
+ const headers = {
380
+ ...BaseClient.authHeaders(this.apiKey),
381
+ ...extraHeaders
382
+ };
340
383
  headers["x-sl-origin"] = this.origin;
341
384
  let serializedBody;
342
385
  if (body !== undefined && body !== null) {
@@ -361,6 +404,8 @@ class BaseClient {
361
404
  throw err;
362
405
  throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`, undefined, undefined, { retryable: true, cause: err instanceof Error ? err : undefined });
363
406
  }
407
+ if (response.status === 304)
408
+ return response;
364
409
  if (!response.ok) {
365
410
  const errorBody = signal ? await raceAbort(response.text(), signal) : await response.text();
366
411
  const envelope = parseErrorEnvelope(errorBody);
@@ -1475,38 +1520,38 @@ import {
1475
1520
  STREAMS_EVENT_TYPES
1476
1521
  } from "@secondlayer/shared/streams-rows";
1477
1522
  export {
1478
- isStxTransfer,
1479
- isStxMint,
1480
- isStxLock,
1481
- isStxBurn,
1482
- isPrint,
1483
- isNftTransfer,
1484
- isNftMint,
1485
- isNftBurn,
1486
- isFtTransfer,
1487
- isFtMint,
1488
- isFtBurn,
1489
- decodeStxTransfer,
1490
- decodeStxMint,
1491
- decodeStxLock,
1492
- decodeStxBurn,
1493
- decodePrint,
1494
- decodeNftTransfer,
1495
- decodeNftMint,
1496
- decodeNftBurn,
1497
- decodeFtTransfer,
1498
- decodeFtMint,
1499
- decodeFtBurn,
1500
- decode,
1501
- createStreamsClient,
1502
- ValidationError,
1503
- StreamsSignatureError,
1504
- StreamsServerError,
1505
- STREAMS_EVENT_TYPES,
1506
- RateLimitError,
1523
+ AuthError,
1507
1524
  Cursor,
1508
- AuthError
1525
+ RateLimitError,
1526
+ STREAMS_EVENT_TYPES,
1527
+ StreamsServerError,
1528
+ StreamsSignatureError,
1529
+ ValidationError,
1530
+ createStreamsClient,
1531
+ decode,
1532
+ decodeFtBurn,
1533
+ decodeFtMint,
1534
+ decodeFtTransfer,
1535
+ decodeNftBurn,
1536
+ decodeNftMint,
1537
+ decodeNftTransfer,
1538
+ decodePrint,
1539
+ decodeStxBurn,
1540
+ decodeStxLock,
1541
+ decodeStxMint,
1542
+ decodeStxTransfer,
1543
+ isFtBurn,
1544
+ isFtMint,
1545
+ isFtTransfer,
1546
+ isNftBurn,
1547
+ isNftMint,
1548
+ isNftTransfer,
1549
+ isPrint,
1550
+ isStxBurn,
1551
+ isStxLock,
1552
+ isStxMint,
1553
+ isStxTransfer
1509
1554
  };
1510
1555
 
1511
- //# debugId=67918C210E187D1F64756E2164756E21
1556
+ //# debugId=DA295DDA55543B7064756E2164756E21
1512
1557
  //# sourceMappingURL=index.js.map