@koralabs/kora-labs-common 6.18.0 → 6.18.2

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.
@@ -15,7 +15,11 @@ export declare const statedWaitMs: (headers: HeaderReader | undefined, body: unk
15
15
  /** Record that `key` must not be called for `waitMs` (only ever extends an existing block). */
16
16
  export declare const recordRateLimit: (key: string, waitMs: number, now?: number) => void;
17
17
  export declare const rateLimitWaitRemainingMs: (key: string, now?: number) => number;
18
- /** Wait out a recorded limit before calling `key`; throw (returning the wait) when it exceeds `maxWaitMs`. */
18
+ /**
19
+ * Wait out a recorded limit before calling `key`; throw (returning the wait) when it exceeds `maxWaitMs`.
20
+ * A timer runs on the monotonic clock while the block is wall-clock ms (Date.now()), so it can fire a
21
+ * millisecond or more before Date.now() reaches the stated time: re-check and keep waiting until it has.
22
+ */
19
23
  export declare const waitForRateLimit: (key: string, maxWaitMs: number) => Promise<void>;
20
24
  /** Test seam: forget every recorded limit. */
21
25
  export declare const resetRateLimits: () => void;
@@ -86,14 +86,17 @@ exports.recordRateLimit = recordRateLimit;
86
86
  const rateLimitWaitRemainingMs = (key, now = Date.now()) => { var _a; return Math.max(0, ((_a = blockedUntilByKey.get(key)) !== null && _a !== void 0 ? _a : 0) - now); };
87
87
  exports.rateLimitWaitRemainingMs = rateLimitWaitRemainingMs;
88
88
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
89
- /** Wait out a recorded limit before calling `key`; throw (returning the wait) when it exceeds `maxWaitMs`. */
89
+ /**
90
+ * Wait out a recorded limit before calling `key`; throw (returning the wait) when it exceeds `maxWaitMs`.
91
+ * A timer runs on the monotonic clock while the block is wall-clock ms (Date.now()), so it can fire a
92
+ * millisecond or more before Date.now() reaches the stated time: re-check and keep waiting until it has.
93
+ */
90
94
  const waitForRateLimit = async (key, maxWaitMs) => {
91
- const remaining = (0, exports.rateLimitWaitRemainingMs)(key);
92
- if (remaining === 0)
93
- return;
94
- if (remaining > maxWaitMs)
95
- throw new RateLimitedError(key, remaining);
96
- await sleep(remaining);
95
+ for (let remaining = (0, exports.rateLimitWaitRemainingMs)(key); remaining > 0; remaining = (0, exports.rateLimitWaitRemainingMs)(key)) {
96
+ if (remaining > maxWaitMs)
97
+ throw new RateLimitedError(key, remaining);
98
+ await sleep(remaining);
99
+ }
97
100
  };
98
101
  exports.waitForRateLimit = waitForRateLimit;
99
102
  /** Test seam: forget every recorded limit. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koralabs/kora-labs-common",
3
- "version": "6.18.0",
3
+ "version": "6.18.2",
4
4
  "description": "Kora Labs Common Utilities",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -33,7 +33,12 @@ export interface TxConfirmation {
33
33
  block?: string;
34
34
  validContract?: boolean;
35
35
  }
36
- /** Poll until the tx is in a block. A tx in a block with `valid_contract: false` is NOT a success. */
36
+ /**
37
+ * Poll until the tx is in a block AND Blockfrost serves its UTxOs. Right after inclusion Blockfrost can
38
+ * answer /txs/<hash> while /txs/<hash>/utxos still 404s (seen on preview: the live suite's reads right
39
+ * after "confirmed" failed), so "confirmed" means every follow-up read of the tx will answer.
40
+ * A tx in a block with `valid_contract: false` is NOT a success.
41
+ */
37
42
  export declare const waitForTxConfirmation: (chain: ChainConfig, txHash: string, timeoutMs?: number, pollMs?: number) => Promise<TxConfirmation>;
38
43
  /** Poll a tx output until it is spent; returns the spending tx hash, or null on timeout. */
39
44
  export declare const waitForOutputConsumed: (chain: ChainConfig, txHash: string, outputIndex: number, timeoutMs: number, pollMs?: number) => Promise<string | null>;
@@ -28,17 +28,23 @@ const fetchTxUtxos = (chain, txHash) => getJson(chain, `/txs/${txHash}/utxos`);
28
28
  exports.fetchTxUtxos = fetchTxUtxos;
29
29
  const lovelaceOf = (amount) => { var _a, _b; return BigInt((_b = (_a = amount.find((a) => a.unit === 'lovelace')) === null || _a === void 0 ? void 0 : _a.quantity) !== null && _b !== void 0 ? _b : '0'); };
30
30
  exports.lovelaceOf = lovelaceOf;
31
- /** Poll until the tx is in a block. A tx in a block with `valid_contract: false` is NOT a success. */
31
+ /**
32
+ * Poll until the tx is in a block AND Blockfrost serves its UTxOs. Right after inclusion Blockfrost can
33
+ * answer /txs/<hash> while /txs/<hash>/utxos still 404s (seen on preview: the live suite's reads right
34
+ * after "confirmed" failed), so "confirmed" means every follow-up read of the tx will answer.
35
+ * A tx in a block with `valid_contract: false` is NOT a success.
36
+ */
32
37
  const waitForTxConfirmation = async (chain, txHash, timeoutMs = 180000, pollMs = 5000) => {
33
38
  const start = Date.now();
39
+ // A transient provider failure is another poll; a rate limit too long to sit out is returned.
40
+ const poll = (path) => (0, blockfrost_1.blockfrostGet)(chain, path).catch((error) => {
41
+ if (error instanceof rateLimit_1.RateLimitedError)
42
+ throw error;
43
+ return null;
44
+ });
34
45
  for (;;) {
35
- // A transient provider failure is another poll; a rate limit too long to sit out is returned.
36
- const data = await (0, blockfrost_1.blockfrostGet)(chain, `/txs/${txHash}`).catch((error) => {
37
- if (error instanceof rateLimit_1.RateLimitedError)
38
- throw error;
39
- return null;
40
- });
41
- if (data)
46
+ const data = await poll(`/txs/${txHash}`);
47
+ if (data && (await poll(`/txs/${txHash}/utxos`)))
42
48
  return { confirmed: true, block: data.block, validContract: data.valid_contract };
43
49
  if (Date.now() - start >= timeoutMs)
44
50
  return { confirmed: false };