@valve-tech/tx-tracker 0.6.0 → 0.7.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.
@@ -0,0 +1,299 @@
1
+ /**
2
+ * `TxEvent` — the discriminated union of every observation the
3
+ * tracker emits, plus payload builders that guarantee every event
4
+ * carries a complete envelope.
5
+ *
6
+ * Per `docs/tx-tracker-spec.md` §6, this taxonomy is the contract
7
+ * between the tracker and every consumer. Naming is **strictly
8
+ * neutral** (§2.1): `seen-in-mempool` not `pending`, `seen-in-block`
9
+ * not `mined`, `vanished-from-block` not `reorged`. The tracker
10
+ * publishes facts; the consumer writes the policy on top.
11
+ *
12
+ * Every event variant extends a common `Envelope` carrying the
13
+ * tracked `hash`, the `chainId`, the `source` discriminator (per
14
+ * §2.2 — never silently downgrade) and the `at` block coordinate
15
+ * the observation was made at. Builders here produce that envelope
16
+ * once so the state machine in `tracker.ts` cannot accidentally
17
+ * publish a partial event.
18
+ *
19
+ * No I/O, no wall-clock, no mutation — pure data shape + pure
20
+ * builders. Browser/mobile safe (§2.4).
21
+ */
22
+ import type { Capabilities, EventSource, RawTx } from '@valve-tech/chain-source';
23
+ /**
24
+ * Hash type carried on every event. The chain-source layer keeps
25
+ * hashes as plain `string` rather than viem's `Hash` brand to stay
26
+ * permissive at the JSON boundary; tx-tracker mirrors that posture
27
+ * — every consumer can `as Hash` at the seam if they need the
28
+ * branded form.
29
+ */
30
+ export type Hash = string;
31
+ /**
32
+ * EVM address. Same posture as `Hash` — plain `string` so consumers
33
+ * who normalize to lowercase or to checksum form don't trip a brand
34
+ * check at the boundary.
35
+ */
36
+ export type Address = string;
37
+ /**
38
+ * Block coordinate the observation was made at. `blockNumber` is the
39
+ * canonical-tip number when the observation landed; `timestamp` is
40
+ * the tip block's timestamp (seconds since epoch, same units the
41
+ * EVM exposes). Both `bigint` per the toolkit's wire-format rule
42
+ * (§2.5).
43
+ */
44
+ export interface At {
45
+ blockNumber: bigint;
46
+ timestamp: bigint;
47
+ }
48
+ /**
49
+ * Common envelope on every event. The state machine builds this once
50
+ * per emit and merges it into the variant-specific payload below.
51
+ */
52
+ export interface Envelope {
53
+ hash: Hash;
54
+ chainId: number;
55
+ source: EventSource;
56
+ at: At;
57
+ }
58
+ /**
59
+ * Per-variant payload shapes. Consumers narrow on `event.kind` to
60
+ * access variant-specific fields. The eslint config disallows TS
61
+ * namespaces, so each variant is a top-level interface and the
62
+ * `TxEvent` union below sums them.
63
+ */
64
+ /**
65
+ * Synthetic first event of a subscription when `emitInitial: true`
66
+ * (the default). Carries the capability snapshot at subscribe time
67
+ * so consumers can decide their fallback posture without a separate
68
+ * call.
69
+ */
70
+ export interface TxEventStarted extends Envelope {
71
+ kind: 'started';
72
+ capabilities: Capabilities;
73
+ }
74
+ /**
75
+ * Tracked hash was observed in the upstream's mempool snapshot or
76
+ * pushed via `eth_subscribe('newPendingTransactions')`. `bucket`
77
+ * disambiguates `txpool_content`'s pending-vs-queued split.
78
+ */
79
+ export interface TxEventSeenInMempool extends Envelope {
80
+ kind: 'seen-in-mempool';
81
+ bucket: 'pending' | 'queued';
82
+ tx: RawTx;
83
+ }
84
+ /**
85
+ * Tracked hash is no longer in the mempool snapshot. The tx may
86
+ * have been mined, replaced, or evicted by the upstream node;
87
+ * subsequent `seen-in-block` / `replaced-by` / `unseen-for-N-blocks`
88
+ * disambiguates which of those happened.
89
+ */
90
+ export interface TxEventLeftMempool extends Envelope {
91
+ kind: 'left-mempool';
92
+ }
93
+ /**
94
+ * Tracked hash was found in the canonical block at
95
+ * `blockNumber` / `blockHash`, at index `transactionIndex`.
96
+ * `confirmations` counts blocks observed since this inclusion
97
+ * inclusive of the inclusion block itself (so the first
98
+ * inclusion event has `confirmations: 1`).
99
+ */
100
+ export interface TxEventSeenInBlock extends Envelope {
101
+ kind: 'seen-in-block';
102
+ blockHash: Hash;
103
+ blockNumber: bigint;
104
+ transactionIndex: number;
105
+ confirmations: number;
106
+ }
107
+ /**
108
+ * Tx was previously seen at block `blockNumber` with hash
109
+ * `previousBlockHash`, but the canonical block at the same height
110
+ * now has `canonicalBlockHash` and the tracked tx is not in its
111
+ * `transactions`. Reorg.
112
+ *
113
+ * Per spec §12.3, this event never carries
114
+ * `source: 'receipt-poll'` — receipt-poll cannot detect a reorg
115
+ * because most providers happily return a receipt for a tx in a
116
+ * no-longer-canonical block.
117
+ */
118
+ export interface TxEventVanishedFromBlock extends Envelope {
119
+ kind: 'vanished-from-block';
120
+ previousBlockHash: Hash;
121
+ canonicalBlockHash: Hash;
122
+ blockNumber: bigint;
123
+ }
124
+ /**
125
+ * A different hash with the same `(from, nonce)` pair was either
126
+ * seen in the mempool or mined. `replacementBlockNumber` is `null`
127
+ * when the replacement was only observed in the mempool; it
128
+ * carries the mined block's number when the replacement reached
129
+ * inclusion before the original.
130
+ */
131
+ export interface TxEventReplacedBy extends Envelope {
132
+ kind: 'replaced-by';
133
+ replacementHash: Hash;
134
+ replacementBlockNumber: bigint | null;
135
+ }
136
+ /**
137
+ * Tracked hash has not been in the mempool nor in any polled
138
+ * block for `blocks` consecutive observations. Threshold is
139
+ * configurable per subscription (`unseenThresholdBlocks`,
140
+ * default 30 — see `tracker.ts`). Consumer interprets as
141
+ * "likely dropped" / "stuck" / "rejected" in their own UX.
142
+ */
143
+ export interface TxEventUnseenForNBlocks extends Envelope {
144
+ kind: 'unseen-for-N-blocks';
145
+ blocks: number;
146
+ }
147
+ /**
148
+ * A capability the tracker had been relying on for this hash is
149
+ * no longer authoritative — typically because the WS subscription
150
+ * dropped or `txpool_content` was newly gated. Tracking continues
151
+ * via `fallbackSource`; the consumer's interpretation of subsequent
152
+ * events should weigh that lower authority.
153
+ */
154
+ export interface TxEventSignalDegraded extends Envelope {
155
+ kind: 'signal-degraded';
156
+ capabilityLost: keyof Capabilities;
157
+ fallbackSource: EventSource;
158
+ }
159
+ /**
160
+ * A previously-degraded capability is back. Fired after a
161
+ * matching `signal-degraded` only.
162
+ */
163
+ export interface TxEventSignalRecovered extends Envelope {
164
+ kind: 'signal-recovered';
165
+ capabilityRestored: keyof Capabilities;
166
+ }
167
+ /**
168
+ * Subscription teardown — fires once per subscription. Always the
169
+ * final event in the stream for that subscription. `reason`
170
+ * disambiguates which lifecycle path closed the iterator:
171
+ *
172
+ * - `'unsubscribed'`: consumer called the returned unsubscribe
173
+ * handle (or `break`'d an async iterator).
174
+ * - `'retention-expired'`: store's retention window elapsed past
175
+ * the last terminal observation; the record was GC'd.
176
+ * - `'tracker-stopped'`: `tracker.stop()` was called.
177
+ */
178
+ export interface TxEventStopped extends Envelope {
179
+ kind: 'stopped';
180
+ reason: 'unsubscribed' | 'retention-expired' | 'tracker-stopped';
181
+ }
182
+ /**
183
+ * Discriminated union of every event variant. Narrow on `kind` to
184
+ * access variant-specific fields.
185
+ */
186
+ export type TxEvent = TxEventStarted | TxEventSeenInMempool | TxEventLeftMempool | TxEventSeenInBlock | TxEventVanishedFromBlock | TxEventReplacedBy | TxEventUnseenForNBlocks | TxEventSignalDegraded | TxEventSignalRecovered | TxEventStopped;
187
+ /**
188
+ * `TxStatus` — cached snapshot the state machine maintains per
189
+ * tracked hash. `getTxStatus(hash)` returns this; the iterator and
190
+ * callback adapters read from the same backing store so all three
191
+ * consumption shapes see consistent state.
192
+ *
193
+ * Carries the **last observation** rather than a derived editorial
194
+ * verb. Consumers interpret the fields as policy.
195
+ */
196
+ export interface TxStatus {
197
+ hash: Hash;
198
+ chainId: number;
199
+ /** Last observed inclusion (null if never observed in a block). */
200
+ lastSeenInBlock: {
201
+ blockHash: Hash;
202
+ blockNumber: bigint;
203
+ transactionIndex: number;
204
+ /** Most recent confirmations count emitted. */
205
+ confirmations: number;
206
+ source: EventSource;
207
+ } | null;
208
+ /** Last observed mempool placement (null if never observed). */
209
+ lastSeenInMempool: {
210
+ bucket: 'pending' | 'queued';
211
+ tx: RawTx;
212
+ at: At;
213
+ source: EventSource;
214
+ } | null;
215
+ /** Replacement hash if ever observed (null otherwise). */
216
+ replacedBy: {
217
+ hash: Hash;
218
+ blockNumber: bigint | null;
219
+ } | null;
220
+ /** Last vanished-from-block observation, when applicable. */
221
+ vanishedAt: {
222
+ previousBlockHash: Hash;
223
+ canonicalBlockHash: Hash;
224
+ blockNumber: bigint;
225
+ } | null;
226
+ /** Number of consecutive observed blocks the hash has been unseen. */
227
+ unseenStreak: number;
228
+ /** First observation block number — used by retention. */
229
+ firstObservedAtBlock: bigint | null;
230
+ /** Most recent observation block number — used by retention. */
231
+ lastObservedAtBlock: bigint | null;
232
+ /** Capabilities at the most recent emit. */
233
+ capabilities: Capabilities;
234
+ }
235
+ /** Build a `started` event. */
236
+ export declare const buildStarted: (input: Envelope & {
237
+ capabilities: Capabilities;
238
+ }) => TxEventStarted;
239
+ /** Build a `seen-in-mempool` event. */
240
+ export declare const buildSeenInMempool: (input: Envelope & {
241
+ bucket: "pending" | "queued";
242
+ tx: RawTx;
243
+ }) => TxEventSeenInMempool;
244
+ /** Build a `left-mempool` event. */
245
+ export declare const buildLeftMempool: (input: Envelope) => TxEventLeftMempool;
246
+ /** Build a `seen-in-block` event. */
247
+ export declare const buildSeenInBlock: (input: Envelope & {
248
+ blockHash: Hash;
249
+ blockNumber: bigint;
250
+ transactionIndex: number;
251
+ confirmations: number;
252
+ }) => TxEventSeenInBlock;
253
+ /**
254
+ * Build a `vanished-from-block` event. Spec §12.3 forbids
255
+ * `source: 'receipt-poll'` for this kind — receipt-poll cannot
256
+ * detect a reorg authoritatively, so the builder rejects that
257
+ * combination at construction rather than letting a malformed
258
+ * event ship to consumers.
259
+ */
260
+ export declare const buildVanishedFromBlock: (input: Envelope & {
261
+ previousBlockHash: Hash;
262
+ canonicalBlockHash: Hash;
263
+ blockNumber: bigint;
264
+ }) => TxEventVanishedFromBlock;
265
+ /** Build a `replaced-by` event. */
266
+ export declare const buildReplacedBy: (input: Envelope & {
267
+ replacementHash: Hash;
268
+ replacementBlockNumber: bigint | null;
269
+ }) => TxEventReplacedBy;
270
+ /** Build an `unseen-for-N-blocks` event. */
271
+ export declare const buildUnseenForNBlocks: (input: Envelope & {
272
+ blocks: number;
273
+ }) => TxEventUnseenForNBlocks;
274
+ /** Build a `signal-degraded` event. */
275
+ export declare const buildSignalDegraded: (input: Envelope & {
276
+ capabilityLost: keyof Capabilities;
277
+ fallbackSource: EventSource;
278
+ }) => TxEventSignalDegraded;
279
+ /** Build a `signal-recovered` event. */
280
+ export declare const buildSignalRecovered: (input: Envelope & {
281
+ capabilityRestored: keyof Capabilities;
282
+ }) => TxEventSignalRecovered;
283
+ /** Build a `stopped` event. */
284
+ export declare const buildStopped: (input: Envelope & {
285
+ reason: "unsubscribed" | "retention-expired" | "tracker-stopped";
286
+ }) => TxEventStopped;
287
+ /**
288
+ * Snapshot constructor for a freshly-tracked hash. Used by the
289
+ * tracker to seed its in-memory record before any observation has
290
+ * landed. All "last observed" fields start `null`; `unseenStreak`
291
+ * starts `0`. Capabilities are passed in (sourced from
292
+ * `source.capabilities()` at construction).
293
+ */
294
+ export declare const buildInitialStatus: (input: {
295
+ hash: Hash;
296
+ chainId: number;
297
+ capabilities: Capabilities;
298
+ }) => TxStatus;
299
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
package/dist/events.js ADDED
@@ -0,0 +1,131 @@
1
+ /**
2
+ * `TxEvent` — the discriminated union of every observation the
3
+ * tracker emits, plus payload builders that guarantee every event
4
+ * carries a complete envelope.
5
+ *
6
+ * Per `docs/tx-tracker-spec.md` §6, this taxonomy is the contract
7
+ * between the tracker and every consumer. Naming is **strictly
8
+ * neutral** (§2.1): `seen-in-mempool` not `pending`, `seen-in-block`
9
+ * not `mined`, `vanished-from-block` not `reorged`. The tracker
10
+ * publishes facts; the consumer writes the policy on top.
11
+ *
12
+ * Every event variant extends a common `Envelope` carrying the
13
+ * tracked `hash`, the `chainId`, the `source` discriminator (per
14
+ * §2.2 — never silently downgrade) and the `at` block coordinate
15
+ * the observation was made at. Builders here produce that envelope
16
+ * once so the state machine in `tracker.ts` cannot accidentally
17
+ * publish a partial event.
18
+ *
19
+ * No I/O, no wall-clock, no mutation — pure data shape + pure
20
+ * builders. Browser/mobile safe (§2.4).
21
+ */
22
+ /**
23
+ * Build an event envelope from the per-tracker context plus the
24
+ * call-site overrides. Used by every builder below so the envelope
25
+ * shape is centralized.
26
+ */
27
+ const makeEnvelope = (input) => ({
28
+ hash: input.hash,
29
+ chainId: input.chainId,
30
+ source: input.source,
31
+ at: { blockNumber: input.at.blockNumber, timestamp: input.at.timestamp },
32
+ });
33
+ /** Build a `started` event. */
34
+ export const buildStarted = (input) => ({
35
+ ...makeEnvelope(input),
36
+ kind: 'started',
37
+ capabilities: input.capabilities,
38
+ });
39
+ /** Build a `seen-in-mempool` event. */
40
+ export const buildSeenInMempool = (input) => ({
41
+ ...makeEnvelope(input),
42
+ kind: 'seen-in-mempool',
43
+ bucket: input.bucket,
44
+ tx: input.tx,
45
+ });
46
+ /** Build a `left-mempool` event. */
47
+ export const buildLeftMempool = (input) => ({
48
+ ...makeEnvelope(input),
49
+ kind: 'left-mempool',
50
+ });
51
+ /** Build a `seen-in-block` event. */
52
+ export const buildSeenInBlock = (input) => ({
53
+ ...makeEnvelope(input),
54
+ kind: 'seen-in-block',
55
+ blockHash: input.blockHash,
56
+ blockNumber: input.blockNumber,
57
+ transactionIndex: input.transactionIndex,
58
+ confirmations: input.confirmations,
59
+ });
60
+ /**
61
+ * Build a `vanished-from-block` event. Spec §12.3 forbids
62
+ * `source: 'receipt-poll'` for this kind — receipt-poll cannot
63
+ * detect a reorg authoritatively, so the builder rejects that
64
+ * combination at construction rather than letting a malformed
65
+ * event ship to consumers.
66
+ */
67
+ export const buildVanishedFromBlock = (input) => {
68
+ if (input.source === 'receipt-poll') {
69
+ throw new Error('buildVanishedFromBlock: receipt-poll cannot detect reorgs ' +
70
+ '(spec §12.3). Use block-poll or subscription source.');
71
+ }
72
+ return {
73
+ ...makeEnvelope(input),
74
+ kind: 'vanished-from-block',
75
+ previousBlockHash: input.previousBlockHash,
76
+ canonicalBlockHash: input.canonicalBlockHash,
77
+ blockNumber: input.blockNumber,
78
+ };
79
+ };
80
+ /** Build a `replaced-by` event. */
81
+ export const buildReplacedBy = (input) => ({
82
+ ...makeEnvelope(input),
83
+ kind: 'replaced-by',
84
+ replacementHash: input.replacementHash,
85
+ replacementBlockNumber: input.replacementBlockNumber,
86
+ });
87
+ /** Build an `unseen-for-N-blocks` event. */
88
+ export const buildUnseenForNBlocks = (input) => ({
89
+ ...makeEnvelope(input),
90
+ kind: 'unseen-for-N-blocks',
91
+ blocks: input.blocks,
92
+ });
93
+ /** Build a `signal-degraded` event. */
94
+ export const buildSignalDegraded = (input) => ({
95
+ ...makeEnvelope(input),
96
+ kind: 'signal-degraded',
97
+ capabilityLost: input.capabilityLost,
98
+ fallbackSource: input.fallbackSource,
99
+ });
100
+ /** Build a `signal-recovered` event. */
101
+ export const buildSignalRecovered = (input) => ({
102
+ ...makeEnvelope(input),
103
+ kind: 'signal-recovered',
104
+ capabilityRestored: input.capabilityRestored,
105
+ });
106
+ /** Build a `stopped` event. */
107
+ export const buildStopped = (input) => ({
108
+ ...makeEnvelope(input),
109
+ kind: 'stopped',
110
+ reason: input.reason,
111
+ });
112
+ /**
113
+ * Snapshot constructor for a freshly-tracked hash. Used by the
114
+ * tracker to seed its in-memory record before any observation has
115
+ * landed. All "last observed" fields start `null`; `unseenStreak`
116
+ * starts `0`. Capabilities are passed in (sourced from
117
+ * `source.capabilities()` at construction).
118
+ */
119
+ export const buildInitialStatus = (input) => ({
120
+ hash: input.hash,
121
+ chainId: input.chainId,
122
+ lastSeenInBlock: null,
123
+ lastSeenInMempool: null,
124
+ replacedBy: null,
125
+ vanishedAt: null,
126
+ unseenStreak: 0,
127
+ firstObservedAtBlock: null,
128
+ lastObservedAtBlock: null,
129
+ capabilities: input.capabilities,
130
+ });
131
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,16 +1,52 @@
1
1
  /**
2
- * @valve-tech/tx-tracker — placeholder for v0.0.1.
2
+ * `@valve-tech/tx-tracker`per-tx state machine for EVM chains.
3
3
  *
4
- * The implementation lands in v0.1.0. This stub claims the npm name
5
- * and documents the planned shape so consumers reading the package
6
- * via `npm view` know what it is.
4
+ * Emits **neutral observations** (`seen-in-mempool`, `seen-in-block`,
5
+ * `replaced-by`, `vanished-from-block`, `unseen-for-N-blocks`,
6
+ * `signal-degraded`, `signal-recovered`, `stopped`) so wallet UIs,
7
+ * indexers, and relays can write their own interpretations on top.
8
+ * The tracker itself never says "confirmed" or "stuck"; it gives
9
+ * you the data to decide.
7
10
  *
8
- * Design contract: see `docs/tx-tracker-spec.md` in the
9
- * `valve-tech/evm-toolkit` repo, which defines the `TxTracker`,
10
- * `TxEvent`, `TxTrackerStore`, and bulk-subscription surfaces this
11
- * package will export.
11
+ * Three consumption shapes (callback, async iterator, snapshot)
12
+ * over one push-based core. Per-method capability detection keeps
13
+ * the "no silent downgrade" invariant — every emitted event carries
14
+ * a `source` discriminator (`'subscription' | 'block-poll' |
15
+ * 'mempool-snapshot' | 'receipt-poll'`).
12
16
  *
13
- * Until v0.1.0 is published, no symbols are exported.
17
+ * Consumes `@valve-tech/chain-source` for upstream signals; sibling
18
+ * to `@valve-tech/gas-oracle` (both consume the same source, neither
19
+ * depends on the other).
20
+ *
21
+ * @example
22
+ * import { createPublicClient, http } from 'viem'
23
+ * import { mainnet } from 'viem/chains'
24
+ * import { createChainSource } from '@valve-tech/chain-source'
25
+ * import { createTxTracker } from '@valve-tech/tx-tracker'
26
+ *
27
+ * const client = createPublicClient({ chain: mainnet, transport: http() })
28
+ * const source = createChainSource({ client })
29
+ * const tracker = createTxTracker({ source, chainId: 1 })
30
+ *
31
+ * source.start()
32
+ * tracker.start()
33
+ *
34
+ * for await (const event of tracker.track('0xabc...')) {
35
+ * console.log(event.kind, event.source, event.at.blockNumber)
36
+ * if (event.kind === 'seen-in-block' && event.confirmations >= 6) break
37
+ * }
38
+ *
39
+ * tracker.stop()
40
+ * source.stop()
14
41
  */
15
- export {};
42
+ export { createTxTracker } from './tracker.js';
43
+ export type { CreateTxTrackerOptions, TxTracker, TrackOptions, BulkTrackOptions, TxMatchEvent, TxSubscription, LostSignalPolicy, } from './tracker.js';
44
+ export { buildStarted, buildSeenInMempool, buildLeftMempool, buildSeenInBlock, buildVanishedFromBlock, buildReplacedBy, buildUnseenForNBlocks, buildSignalDegraded, buildSignalRecovered, buildStopped, buildInitialStatus, } from './events.js';
45
+ export type { Address, At, Envelope, Hash, TxEvent, TxEventStarted, TxEventSeenInMempool, TxEventLeftMempool, TxEventSeenInBlock, TxEventVanishedFromBlock, TxEventReplacedBy, TxEventUnseenForNBlocks, TxEventSignalDegraded, TxEventSignalRecovered, TxEventStopped, TxStatus, } from './events.js';
46
+ export { createInMemoryStore, computeRetentionExpiry, defaultRetentionBlocks, } from './store.js';
47
+ export type { BulkSelector, HashSelector, InMemoryStoreOptions, PersistedSubscription, TrackedTxRecord, TxTrackerStore, } from './store.js';
48
+ export { appendBlock, defaultReorgDepthBlocks, detectDivergences, } from './reorg.js';
49
+ export type { BlockDivergence, BlockSample } from './reorg.js';
50
+ export { compileSelector, defaultMaxBulkSubscriptions, matchAll, } from './selectors.js';
51
+ export type { BulkMatchPayload, CompiledSelector } from './selectors.js';
16
52
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,YAAY,EACV,sBAAsB,EACtB,SAAS,EACT,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,gBAAgB,GACjB,MAAM,cAAc,CAAA;AAErB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAA;AACpB,YAAY,EACV,OAAO,EACP,EAAE,EACF,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,QAAQ,GACT,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,WAAW,EACX,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE9D,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA"}
package/dist/index.js CHANGED
@@ -1,2 +1,47 @@
1
- export {};
1
+ /**
2
+ * `@valve-tech/tx-tracker` — per-tx state machine for EVM chains.
3
+ *
4
+ * Emits **neutral observations** (`seen-in-mempool`, `seen-in-block`,
5
+ * `replaced-by`, `vanished-from-block`, `unseen-for-N-blocks`,
6
+ * `signal-degraded`, `signal-recovered`, `stopped`) so wallet UIs,
7
+ * indexers, and relays can write their own interpretations on top.
8
+ * The tracker itself never says "confirmed" or "stuck"; it gives
9
+ * you the data to decide.
10
+ *
11
+ * Three consumption shapes (callback, async iterator, snapshot)
12
+ * over one push-based core. Per-method capability detection keeps
13
+ * the "no silent downgrade" invariant — every emitted event carries
14
+ * a `source` discriminator (`'subscription' | 'block-poll' |
15
+ * 'mempool-snapshot' | 'receipt-poll'`).
16
+ *
17
+ * Consumes `@valve-tech/chain-source` for upstream signals; sibling
18
+ * to `@valve-tech/gas-oracle` (both consume the same source, neither
19
+ * depends on the other).
20
+ *
21
+ * @example
22
+ * import { createPublicClient, http } from 'viem'
23
+ * import { mainnet } from 'viem/chains'
24
+ * import { createChainSource } from '@valve-tech/chain-source'
25
+ * import { createTxTracker } from '@valve-tech/tx-tracker'
26
+ *
27
+ * const client = createPublicClient({ chain: mainnet, transport: http() })
28
+ * const source = createChainSource({ client })
29
+ * const tracker = createTxTracker({ source, chainId: 1 })
30
+ *
31
+ * source.start()
32
+ * tracker.start()
33
+ *
34
+ * for await (const event of tracker.track('0xabc...')) {
35
+ * console.log(event.kind, event.source, event.at.blockNumber)
36
+ * if (event.kind === 'seen-in-block' && event.confirmations >= 6) break
37
+ * }
38
+ *
39
+ * tracker.stop()
40
+ * source.stop()
41
+ */
42
+ export { createTxTracker } from './tracker.js';
43
+ export { buildStarted, buildSeenInMempool, buildLeftMempool, buildSeenInBlock, buildVanishedFromBlock, buildReplacedBy, buildUnseenForNBlocks, buildSignalDegraded, buildSignalRecovered, buildStopped, buildInitialStatus, } from './events.js';
44
+ export { createInMemoryStore, computeRetentionExpiry, defaultRetentionBlocks, } from './store.js';
45
+ export { appendBlock, defaultReorgDepthBlocks, detectDivergences, } from './reorg.js';
46
+ export { compileSelector, defaultMaxBulkSubscriptions, matchAll, } from './selectors.js';
2
47
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAW9C,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAA;AAoBpB,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,YAAY,CAAA;AAUnB,OAAO,EACL,WAAW,EACX,uBAAuB,EACvB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,eAAe,EACf,2BAA2B,EAC3B,QAAQ,GACT,MAAM,gBAAgB,CAAA"}