@valve-tech/tx-flight-react 0.0.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/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/components/actions.d.ts +32 -0
- package/dist/components/actions.d.ts.map +1 -0
- package/dist/components/actions.js +10 -0
- package/dist/components/actions.js.map +1 -0
- package/dist/components/age.d.ts +21 -0
- package/dist/components/age.d.ts.map +1 -0
- package/dist/components/age.js +29 -0
- package/dist/components/age.js.map +1 -0
- package/dist/components/hash-link.d.ts +26 -0
- package/dist/components/hash-link.d.ts.map +1 -0
- package/dist/components/hash-link.js +20 -0
- package/dist/components/hash-link.js.map +1 -0
- package/dist/components/item.d.ts +27 -0
- package/dist/components/item.d.ts.map +1 -0
- package/dist/components/item.js +18 -0
- package/dist/components/item.js.map +1 -0
- package/dist/components/list.d.ts +27 -0
- package/dist/components/list.d.ts.map +1 -0
- package/dist/components/list.js +14 -0
- package/dist/components/list.js.map +1 -0
- package/dist/components/status-icon.d.ts +20 -0
- package/dist/components/status-icon.d.ts.map +1 -0
- package/dist/components/status-icon.js +28 -0
- package/dist/components/status-icon.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/tx-tracker.d.ts +48 -0
- package/dist/integrations/tx-tracker.d.ts.map +1 -0
- package/dist/integrations/tx-tracker.js +169 -0
- package/dist/integrations/tx-tracker.js.map +1 -0
- package/dist/integrations/wallet-adapter.d.ts +34 -0
- package/dist/integrations/wallet-adapter.d.ts.map +1 -0
- package/dist/integrations/wallet-adapter.js +97 -0
- package/dist/integrations/wallet-adapter.js.map +1 -0
- package/dist/provider.d.ts +67 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +182 -0
- package/dist/provider.js.map +1 -0
- package/dist/storage/index.d.ts +7 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +7 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/indexed-db.d.ts +21 -0
- package/dist/storage/indexed-db.d.ts.map +1 -0
- package/dist/storage/indexed-db.js +58 -0
- package/dist/storage/indexed-db.js.map +1 -0
- package/dist/storage/local-storage.d.ts +18 -0
- package/dist/storage/local-storage.d.ts.map +1 -0
- package/dist/storage/local-storage.js +39 -0
- package/dist/storage/local-storage.js.map +1 -0
- package/dist/storage/memory.d.ts +7 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +17 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/serialize.d.ts +13 -0
- package/dist/storage/serialize.d.ts.map +1 -0
- package/dist/storage/serialize.js +29 -0
- package/dist/storage/serialize.js.map +1 -0
- package/dist/store/reducers.d.ts +53 -0
- package/dist/store/reducers.d.ts.map +1 -0
- package/dist/store/reducers.js +135 -0
- package/dist/store/reducers.js.map +1 -0
- package/dist/store/store.d.ts +56 -0
- package/dist/store/store.d.ts.map +1 -0
- package/dist/store/store.js +93 -0
- package/dist/store/store.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +21 -0
- package/dist/types.js.map +1 -0
- package/dist/use-tx-flight.d.ts +31 -0
- package/dist/use-tx-flight.d.ts.map +1 -0
- package/dist/use-tx-flight.js +47 -0
- package/dist/use-tx-flight.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure reducers for the tx-flight store.
|
|
3
|
+
*
|
|
4
|
+
* Side effects (start watcher, save storage, call unsub) belong in the
|
|
5
|
+
* Provider. Reducers fold state changes only. The Provider invokes
|
|
6
|
+
* reducers with `now` parameters so timestamping is testable.
|
|
7
|
+
*/
|
|
8
|
+
const TERMINAL_STATUSES = new Set([
|
|
9
|
+
'confirmed',
|
|
10
|
+
'failed',
|
|
11
|
+
'dropped',
|
|
12
|
+
'replaced',
|
|
13
|
+
]);
|
|
14
|
+
const isTerminal = (tx) => TERMINAL_STATUSES.has(tx.status);
|
|
15
|
+
/**
|
|
16
|
+
* The "when did this tx settle / start" timestamp used for both
|
|
17
|
+
* eviction passes. For terminals: `confirmedAt` if set (the Provider
|
|
18
|
+
* stamps it on status flip via `updateReducer`), else `submittedAt`
|
|
19
|
+
* as a conservative fallback for terminals that were added directly
|
|
20
|
+
* (e.g., via `addManual`). For non-terminals: `submittedAt`.
|
|
21
|
+
*/
|
|
22
|
+
const settledOrSubmittedAt = (tx) => isTerminal(tx) ? (tx.confirmedAt ?? tx.submittedAt) : tx.submittedAt;
|
|
23
|
+
/**
|
|
24
|
+
* Insert (or overwrite) a TrackedTx and an optional watcher unsub.
|
|
25
|
+
*
|
|
26
|
+
* When overwriting a tx that previously had a watcher, the caller MUST
|
|
27
|
+
* call the previous watcher's unsub BEFORE invoking this reducer —
|
|
28
|
+
* reducers are pure and don't perform side effects. The Provider owns
|
|
29
|
+
* watcher lifecycle.
|
|
30
|
+
*/
|
|
31
|
+
export const addReducer = (state, tx, watcher) => {
|
|
32
|
+
const txs = new Map(state.txs);
|
|
33
|
+
txs.set(tx.id, tx);
|
|
34
|
+
const watchers = new Map(state.watchers);
|
|
35
|
+
if (watcher) {
|
|
36
|
+
watchers.set(tx.id, watcher);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
watchers.delete(tx.id);
|
|
40
|
+
}
|
|
41
|
+
return { txs, watchers };
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Patch an existing tx's fields. No-op (returns the same state
|
|
45
|
+
* reference) on a missing id.
|
|
46
|
+
*
|
|
47
|
+
* If the patch flips status to a terminal value and `confirmedAt` is
|
|
48
|
+
* not yet set, stamps `confirmedAt` to `now`. This gives `evictReducer`
|
|
49
|
+
* a uniform "settled at" timestamp regardless of which terminal status
|
|
50
|
+
* the tx ended in.
|
|
51
|
+
*/
|
|
52
|
+
export const updateReducer = (state, txId, patch, now) => {
|
|
53
|
+
const existing = state.txs.get(txId);
|
|
54
|
+
if (!existing)
|
|
55
|
+
return state;
|
|
56
|
+
const next = { ...existing, ...patch };
|
|
57
|
+
if (isTerminal(next) && next.confirmedAt === undefined) {
|
|
58
|
+
next.confirmedAt = now;
|
|
59
|
+
}
|
|
60
|
+
const txs = new Map(state.txs);
|
|
61
|
+
txs.set(txId, next);
|
|
62
|
+
return { txs, watchers: state.watchers };
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Drop a tx and its watcher map entry. The Provider is responsible for
|
|
66
|
+
* calling the watcher's unsub BEFORE this reducer runs.
|
|
67
|
+
*
|
|
68
|
+
* No-op (returns the same state reference) on a missing id.
|
|
69
|
+
*/
|
|
70
|
+
export const removeReducer = (state, txId) => {
|
|
71
|
+
if (!state.txs.has(txId) && !state.watchers.has(txId))
|
|
72
|
+
return state;
|
|
73
|
+
const txs = new Map(state.txs);
|
|
74
|
+
txs.delete(txId);
|
|
75
|
+
const watchers = new Map(state.watchers);
|
|
76
|
+
watchers.delete(txId);
|
|
77
|
+
return { txs, watchers };
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Two-pass eviction. Returns the same state reference when nothing
|
|
81
|
+
* changed.
|
|
82
|
+
*
|
|
83
|
+
* Pass 1: drop terminal entries older than `terminalRetentionMs` —
|
|
84
|
+
* `now - (confirmedAt ?? submittedAt) > terminalRetentionMs`.
|
|
85
|
+
* Pass 2: if the surviving set is still larger than `maxItems`, drop
|
|
86
|
+
* in priority order: terminals first (oldest first by their settled
|
|
87
|
+
* timestamp), then non-terminals (oldest first by submittedAt).
|
|
88
|
+
* Non-terminals are preserved over terminals at the same age — an
|
|
89
|
+
* active in-flight tx is more valuable than a settled one.
|
|
90
|
+
*/
|
|
91
|
+
export const evictReducer = (state, opts) => {
|
|
92
|
+
const { maxItems, terminalRetentionMs, now } = opts;
|
|
93
|
+
// Pass 1: collect terminals past their retention window.
|
|
94
|
+
const expired = new Set();
|
|
95
|
+
for (const [id, tx] of state.txs) {
|
|
96
|
+
if (!isTerminal(tx))
|
|
97
|
+
continue;
|
|
98
|
+
if (now - settledOrSubmittedAt(tx) > terminalRetentionMs)
|
|
99
|
+
expired.add(id);
|
|
100
|
+
}
|
|
101
|
+
// Pass 2: if still over cap, sort survivors by (terminal-first,
|
|
102
|
+
// then oldest-first) and slice off the head until under cap.
|
|
103
|
+
const survivorsAfterPass1 = state.txs.size - expired.size;
|
|
104
|
+
let extraEvictions = [];
|
|
105
|
+
if (survivorsAfterPass1 > maxItems) {
|
|
106
|
+
const survivors = [];
|
|
107
|
+
for (const [id, tx] of state.txs) {
|
|
108
|
+
if (!expired.has(id))
|
|
109
|
+
survivors.push(tx);
|
|
110
|
+
}
|
|
111
|
+
survivors.sort((a, b) => {
|
|
112
|
+
const aT = isTerminal(a) ? 0 : 1;
|
|
113
|
+
const bT = isTerminal(b) ? 0 : 1;
|
|
114
|
+
if (aT !== bT)
|
|
115
|
+
return aT - bT;
|
|
116
|
+
return settledOrSubmittedAt(a) - settledOrSubmittedAt(b);
|
|
117
|
+
});
|
|
118
|
+
const evictCount = survivorsAfterPass1 - maxItems;
|
|
119
|
+
extraEvictions = survivors.slice(0, evictCount).map((t) => t.id);
|
|
120
|
+
}
|
|
121
|
+
if (expired.size === 0 && extraEvictions.length === 0)
|
|
122
|
+
return state;
|
|
123
|
+
const txs = new Map(state.txs);
|
|
124
|
+
const watchers = new Map(state.watchers);
|
|
125
|
+
for (const id of expired) {
|
|
126
|
+
txs.delete(id);
|
|
127
|
+
watchers.delete(id);
|
|
128
|
+
}
|
|
129
|
+
for (const id of extraEvictions) {
|
|
130
|
+
txs.delete(id);
|
|
131
|
+
watchers.delete(id);
|
|
132
|
+
}
|
|
133
|
+
return { txs, watchers };
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=reducers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reducers.js","sourceRoot":"","sources":["../../src/store/reducers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Subscribable store wrapping the pure reducers with the
|
|
3
|
+
* side effects the Provider needs:
|
|
4
|
+
*
|
|
5
|
+
* - notify subscribers on state change (so React's
|
|
6
|
+
* `useSyncExternalStore` re-renders consumers)
|
|
7
|
+
* - call watcher unsub on remove / clear / evict / overwrite (the
|
|
8
|
+
* reducers are forbidden from doing this — see AGENTS.md invariant 1)
|
|
9
|
+
* - cache the txs-as-array projection so `useSyncExternalStore` sees
|
|
10
|
+
* a stable reference between dispatches
|
|
11
|
+
*
|
|
12
|
+
* Storage IO and the eviction `setInterval` belong in the Provider,
|
|
13
|
+
* not here. The store exposes `dispatch.evict()` so the Provider can
|
|
14
|
+
* invoke it on its own schedule (testable + composable).
|
|
15
|
+
*/
|
|
16
|
+
import type { TrackedTx } from '@valve-tech/wallet-adapter';
|
|
17
|
+
import type { InternalState } from '../types.js';
|
|
18
|
+
export interface TxFlightStoreOptions {
|
|
19
|
+
/** Cap on retained entries. Eviction tick prunes terminals first, then non-terminals if still over. */
|
|
20
|
+
maxItems: number;
|
|
21
|
+
/** How long terminals linger before eviction. */
|
|
22
|
+
terminalRetentionMs: number;
|
|
23
|
+
/**
|
|
24
|
+
* Routed for watcher-unsub failures. Storage errors come from the
|
|
25
|
+
* Provider, not the store. Optional — undefined silently swallows.
|
|
26
|
+
*/
|
|
27
|
+
onError?: (method: string, err: unknown) => void;
|
|
28
|
+
}
|
|
29
|
+
export interface TxFlightStore {
|
|
30
|
+
getState(): InternalState;
|
|
31
|
+
/** Stable-reference array projection of txs. Recomputed only on state change. */
|
|
32
|
+
getTxs(): readonly TrackedTx[];
|
|
33
|
+
subscribe(listener: () => void): () => void;
|
|
34
|
+
dispatch: {
|
|
35
|
+
/**
|
|
36
|
+
* Insert (or overwrite) a TrackedTx with an optional watcher unsub.
|
|
37
|
+
* If a prior watcher was registered for the same `tx.id`, it is
|
|
38
|
+
* unsubscribed BEFORE the state changes — so its in-flight callbacks
|
|
39
|
+
* cannot pollute the new entry.
|
|
40
|
+
*/
|
|
41
|
+
addWithTx: (tx: TrackedTx, watcher: (() => void) | null) => void;
|
|
42
|
+
update: (txId: string, patch: Partial<TrackedTx>) => void;
|
|
43
|
+
/** Remove a tx + its watcher. Calls the watcher's unsub before state changes. */
|
|
44
|
+
remove: (txId: string) => void;
|
|
45
|
+
/** Empty the store. Calls every watcher's unsub before state changes. */
|
|
46
|
+
clear: () => void;
|
|
47
|
+
/**
|
|
48
|
+
* Periodic eviction tick. Calls `evictReducer` with `Date.now()`;
|
|
49
|
+
* when the resulting state drops txs that had watchers, those
|
|
50
|
+
* watchers are unsubscribed.
|
|
51
|
+
*/
|
|
52
|
+
evict: () => void;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export declare const createTxFlightStore: (options: TxFlightStoreOptions) => TxFlightStore;
|
|
56
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/store/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAE3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAQhD,MAAM,WAAW,oBAAoB;IACnC,uGAAuG;IACvG,QAAQ,EAAE,MAAM,CAAA;IAChB,iDAAiD;IACjD,mBAAmB,EAAE,MAAM,CAAA;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CACjD;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,IAAI,aAAa,CAAA;IACzB,iFAAiF;IACjF,MAAM,IAAI,SAAS,SAAS,EAAE,CAAA;IAC9B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,QAAQ,EAAE;QACR;;;;;WAKG;QACH,SAAS,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,KAAK,IAAI,CAAA;QAChE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,KAAK,IAAI,CAAA;QACzD,iFAAiF;QACjF,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;QAC9B,yEAAyE;QACzE,KAAK,EAAE,MAAM,IAAI,CAAA;QACjB;;;;WAIG;QACH,KAAK,EAAE,MAAM,IAAI,CAAA;KAClB,CAAA;CACF;AAOD,eAAO,MAAM,mBAAmB,YACrB,oBAAoB,KAC5B,aAiEF,CAAA"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Subscribable store wrapping the pure reducers with the
|
|
3
|
+
* side effects the Provider needs:
|
|
4
|
+
*
|
|
5
|
+
* - notify subscribers on state change (so React's
|
|
6
|
+
* `useSyncExternalStore` re-renders consumers)
|
|
7
|
+
* - call watcher unsub on remove / clear / evict / overwrite (the
|
|
8
|
+
* reducers are forbidden from doing this — see AGENTS.md invariant 1)
|
|
9
|
+
* - cache the txs-as-array projection so `useSyncExternalStore` sees
|
|
10
|
+
* a stable reference between dispatches
|
|
11
|
+
*
|
|
12
|
+
* Storage IO and the eviction `setInterval` belong in the Provider,
|
|
13
|
+
* not here. The store exposes `dispatch.evict()` so the Provider can
|
|
14
|
+
* invoke it on its own schedule (testable + composable).
|
|
15
|
+
*/
|
|
16
|
+
import { addReducer, evictReducer, removeReducer, updateReducer, } from './reducers.js';
|
|
17
|
+
const emptyInternalState = () => ({
|
|
18
|
+
txs: new Map(),
|
|
19
|
+
watchers: new Map(),
|
|
20
|
+
});
|
|
21
|
+
export const createTxFlightStore = (options) => {
|
|
22
|
+
let state = emptyInternalState();
|
|
23
|
+
let cachedTxs = null;
|
|
24
|
+
const listeners = new Set();
|
|
25
|
+
const safeUnsub = (unsub) => {
|
|
26
|
+
try {
|
|
27
|
+
unsub();
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
options.onError?.('watcher-unsub', err);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const setState = (next) => {
|
|
34
|
+
if (next === state)
|
|
35
|
+
return;
|
|
36
|
+
state = next;
|
|
37
|
+
cachedTxs = null;
|
|
38
|
+
for (const fn of listeners)
|
|
39
|
+
fn();
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
getState: () => state,
|
|
43
|
+
getTxs: () => {
|
|
44
|
+
if (cachedTxs === null)
|
|
45
|
+
cachedTxs = Array.from(state.txs.values());
|
|
46
|
+
return cachedTxs;
|
|
47
|
+
},
|
|
48
|
+
subscribe: (listener) => {
|
|
49
|
+
listeners.add(listener);
|
|
50
|
+
return () => {
|
|
51
|
+
listeners.delete(listener);
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
dispatch: {
|
|
55
|
+
addWithTx: (tx, watcher) => {
|
|
56
|
+
const priorUnsub = state.watchers.get(tx.id);
|
|
57
|
+
if (priorUnsub)
|
|
58
|
+
safeUnsub(priorUnsub);
|
|
59
|
+
setState(addReducer(state, tx, watcher));
|
|
60
|
+
},
|
|
61
|
+
update: (txId, patch) => {
|
|
62
|
+
setState(updateReducer(state, txId, patch, Date.now()));
|
|
63
|
+
},
|
|
64
|
+
remove: (txId) => {
|
|
65
|
+
const unsub = state.watchers.get(txId);
|
|
66
|
+
if (unsub)
|
|
67
|
+
safeUnsub(unsub);
|
|
68
|
+
setState(removeReducer(state, txId));
|
|
69
|
+
},
|
|
70
|
+
clear: () => {
|
|
71
|
+
const unsubs = Array.from(state.watchers.values());
|
|
72
|
+
for (const unsub of unsubs)
|
|
73
|
+
safeUnsub(unsub);
|
|
74
|
+
setState(emptyInternalState());
|
|
75
|
+
},
|
|
76
|
+
evict: () => {
|
|
77
|
+
const next = evictReducer(state, {
|
|
78
|
+
maxItems: options.maxItems,
|
|
79
|
+
terminalRetentionMs: options.terminalRetentionMs,
|
|
80
|
+
now: Date.now(),
|
|
81
|
+
});
|
|
82
|
+
if (next === state)
|
|
83
|
+
return;
|
|
84
|
+
for (const [id, unsub] of state.watchers) {
|
|
85
|
+
if (!next.watchers.has(id))
|
|
86
|
+
safeUnsub(unsub);
|
|
87
|
+
}
|
|
88
|
+
setState(next);
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/store/store.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Public + internal type surface for `@valve-tech/tx-flight-react`.
|
|
3
|
+
*
|
|
4
|
+
* Public types: the three add-input shapes (`AddWithWalletAdapterInput`,
|
|
5
|
+
* `AddByHashInput`, `AddManualInput`), the `TxFlightStorage` adapter
|
|
6
|
+
* contract, and provider/hook option bags.
|
|
7
|
+
*
|
|
8
|
+
* Internal types: `InternalState` (used by reducers + store) is exported
|
|
9
|
+
* for cross-module use within the package but is NOT re-exported from
|
|
10
|
+
* `index.ts` — consumers don't need it.
|
|
11
|
+
*
|
|
12
|
+
* wallet-adapter and viem types are imported as `type`-only so the
|
|
13
|
+
* optional-peer-dep posture (spec §6.1) holds: a wallet-adapter-only
|
|
14
|
+
* consumer doesn't pay any tx-tracker bundle cost, and a hash-only
|
|
15
|
+
* consumer doesn't pay any wallet-adapter cost.
|
|
16
|
+
*/
|
|
17
|
+
import type { Hex, PublicClient } from 'viem';
|
|
18
|
+
import type { TrackedTx, TxFlow, WriteHookParams, WalletSendTransactionRequest } from '@valve-tech/wallet-adapter';
|
|
19
|
+
export type { TrackedTx, TxFlow };
|
|
20
|
+
/**
|
|
21
|
+
* Input for `addWithWalletAdapter(input)`. Use when the caller is
|
|
22
|
+
* submitting their tx via `sendTransactionWithHooks` from
|
|
23
|
+
* `@valve-tech/wallet-adapter`. The strip wraps `hooks` so each phase
|
|
24
|
+
* fans out to both the caller's original callbacks AND the strip's
|
|
25
|
+
* store dispatch.
|
|
26
|
+
*/
|
|
27
|
+
export interface AddWithWalletAdapterInput {
|
|
28
|
+
/** The hooks bag the consumer is already passing to sendTransactionWithHooks. */
|
|
29
|
+
hooks: WriteHookParams;
|
|
30
|
+
/** Echoed onto TrackedTx.flow. Caller-defined string. */
|
|
31
|
+
flow: TxFlow;
|
|
32
|
+
/** Echoed onto TrackedTx.chainId. */
|
|
33
|
+
chainId: number;
|
|
34
|
+
/** Echoed onto TrackedTx.request — used for replay/replace flows. */
|
|
35
|
+
request: WalletSendTransactionRequest;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Input for `addByHash(input)`. Use when the caller already has a tx
|
|
39
|
+
* hash + chainId and a `PublicClient` to watch with. Internally builds
|
|
40
|
+
* a private `ChainSource` + `TxTracker` pair (dynamic-imports
|
|
41
|
+
* `@valve-tech/tx-tracker` + `@valve-tech/chain-source`).
|
|
42
|
+
*/
|
|
43
|
+
export interface AddByHashInput {
|
|
44
|
+
hash: Hex;
|
|
45
|
+
chainId: number;
|
|
46
|
+
/** viem PublicClient pointed at the chain. */
|
|
47
|
+
client: PublicClient;
|
|
48
|
+
flow?: TxFlow;
|
|
49
|
+
/** Confirmations before TrackedTx.status flips to mined. Default 1. */
|
|
50
|
+
confirmations?: number;
|
|
51
|
+
/** Blocks of unseen observation before TrackedTx.status flips to dropped. Default 12. */
|
|
52
|
+
staleAfterBlocks?: number;
|
|
53
|
+
/**
|
|
54
|
+
* If true, fetches the receipt at inclusion. When the receipt's
|
|
55
|
+
* status is reverted, the strip surfaces `failed`. Adds one RPC.
|
|
56
|
+
*/
|
|
57
|
+
withReceipts?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Input for `addManual(input)`. Use when the caller already has a
|
|
61
|
+
* fully-formed `TrackedTx` (e.g., back-filling from a server push,
|
|
62
|
+
* surfacing a tx observed elsewhere). The strip stores the entry
|
|
63
|
+
* verbatim and does NOT auto-update — caller drives subsequent state
|
|
64
|
+
* by calling `addManual` again with the same `tx.id` (overwrites in
|
|
65
|
+
* place) or `remove(id)`.
|
|
66
|
+
*/
|
|
67
|
+
export interface AddManualInput {
|
|
68
|
+
tx: TrackedTx;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Pluggable persistence contract for the tx-flight strip. The Provider
|
|
72
|
+
* loads on mount and saves on state change (debounced ~250ms).
|
|
73
|
+
*
|
|
74
|
+
* Built-in adapters live at `@valve-tech/tx-flight-react/storage`:
|
|
75
|
+
* `localStorageAdapter` (default), `indexedDBAdapter`, `memoryAdapter`.
|
|
76
|
+
* Custom adapters just satisfy this two-method interface.
|
|
77
|
+
*/
|
|
78
|
+
export interface TxFlightStorage {
|
|
79
|
+
/** Returns null if no entry exists for the given id. */
|
|
80
|
+
load(id: string): Promise<TrackedTx[] | null>;
|
|
81
|
+
/** Replace stored value. Called debounced ~250ms by the Provider. */
|
|
82
|
+
save(id: string, txs: TrackedTx[]): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
export interface InternalState {
|
|
85
|
+
/** TrackedTx records keyed by their assigned id. */
|
|
86
|
+
txs: ReadonlyMap<string, TrackedTx>;
|
|
87
|
+
/**
|
|
88
|
+
* Per-tx unsubscribe handles for active watchers (addByHash spawns
|
|
89
|
+
* one; addWithWalletAdapter and addManual leave this empty for the
|
|
90
|
+
* tx). The reducers don't call these — the Provider does on remove
|
|
91
|
+
* or unmount, keeping reducers pure.
|
|
92
|
+
*/
|
|
93
|
+
watchers: ReadonlyMap<string, () => void>;
|
|
94
|
+
}
|
|
95
|
+
export declare const emptyState: InternalState;
|
|
96
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,MAAM,CAAA;AAC7C,OAAO,KAAK,EACV,SAAS,EACT,MAAM,EACN,eAAe,EACf,4BAA4B,EAC7B,MAAM,4BAA4B,CAAA;AASnC,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,CAAA;AAIjC;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC,iFAAiF;IACjF,KAAK,EAAE,eAAe,CAAA;IACtB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,OAAO,EAAE,4BAA4B,CAAA;CACtC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,GAAG,CAAA;IACT,OAAO,EAAE,MAAM,CAAA;IACf,8CAA8C;IAC9C,MAAM,EAAE,YAAY,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,SAAS,CAAA;CACd;AAID;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,CAAA;IAC7C,qEAAqE;IACrE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAClD;AAQD,MAAM,WAAW,aAAa;IAC5B,oDAAoD;IACpD,GAAG,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;IACnC;;;;;OAKG;IACH,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,CAAA;CAC1C;AAED,eAAO,MAAM,UAAU,EAAE,aAGxB,CAAA"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Public + internal type surface for `@valve-tech/tx-flight-react`.
|
|
3
|
+
*
|
|
4
|
+
* Public types: the three add-input shapes (`AddWithWalletAdapterInput`,
|
|
5
|
+
* `AddByHashInput`, `AddManualInput`), the `TxFlightStorage` adapter
|
|
6
|
+
* contract, and provider/hook option bags.
|
|
7
|
+
*
|
|
8
|
+
* Internal types: `InternalState` (used by reducers + store) is exported
|
|
9
|
+
* for cross-module use within the package but is NOT re-exported from
|
|
10
|
+
* `index.ts` — consumers don't need it.
|
|
11
|
+
*
|
|
12
|
+
* wallet-adapter and viem types are imported as `type`-only so the
|
|
13
|
+
* optional-peer-dep posture (spec §6.1) holds: a wallet-adapter-only
|
|
14
|
+
* consumer doesn't pay any tx-tracker bundle cost, and a hash-only
|
|
15
|
+
* consumer doesn't pay any wallet-adapter cost.
|
|
16
|
+
*/
|
|
17
|
+
export const emptyState = {
|
|
18
|
+
txs: new Map(),
|
|
19
|
+
watchers: new Map(),
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA6GH,MAAM,CAAC,MAAM,UAAU,GAAkB;IACvC,GAAG,EAAE,IAAI,GAAG,EAAE;IACd,QAAQ,EAAE,IAAI,GAAG,EAAE;CACpB,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TrackedTx, WriteHookParams } from '@valve-tech/wallet-adapter';
|
|
2
|
+
import { type AddWithWalletAdapterResult } from './integrations/wallet-adapter.js';
|
|
3
|
+
import type { AddByHashInput, AddManualInput, AddWithWalletAdapterInput } from './types.js';
|
|
4
|
+
export interface UseTxFlightReturn {
|
|
5
|
+
/** Reactive snapshot — re-renders when state changes. */
|
|
6
|
+
txs: readonly TrackedTx[];
|
|
7
|
+
/**
|
|
8
|
+
* Add a tx the consumer is submitting via @valve-tech/wallet-adapter.
|
|
9
|
+
* Returns the assigned id and a wrapped `WriteHookParams` to pass to
|
|
10
|
+
* `sendTransactionWithHooks`. Each phase fans out to the consumer's
|
|
11
|
+
* original callbacks AND a store dispatch reflecting the new state.
|
|
12
|
+
*/
|
|
13
|
+
addWithWalletAdapter: (input: AddWithWalletAdapterInput) => AddWithWalletAdapterResult;
|
|
14
|
+
/**
|
|
15
|
+
* Add a tx by its hash + chainId. Internally builds a private
|
|
16
|
+
* ChainSource + TxTracker and watches the hash. Async because
|
|
17
|
+
* `@valve-tech/tx-tracker` is dynamic-imported (optional peer dep).
|
|
18
|
+
*/
|
|
19
|
+
addByHash: (input: AddByHashInput) => Promise<string>;
|
|
20
|
+
/** Add a fully-formed TrackedTx. Returns the supplied id. */
|
|
21
|
+
addManual: (input: AddManualInput) => string;
|
|
22
|
+
/** Remove an entry by id. No-op if not found. */
|
|
23
|
+
remove: (id: string) => void;
|
|
24
|
+
/** Empty the strip (terminal + non-terminal). */
|
|
25
|
+
clear: () => void;
|
|
26
|
+
/** Imperative read; doesn't subscribe to re-renders. */
|
|
27
|
+
get: (id: string) => TrackedTx | null;
|
|
28
|
+
}
|
|
29
|
+
export type { WriteHookParams, AddWithWalletAdapterResult };
|
|
30
|
+
export declare const useTxFlight: (id?: string) => UseTxFlightReturn;
|
|
31
|
+
//# sourceMappingURL=use-tx-flight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-tx-flight.d.ts","sourceRoot":"","sources":["../src/use-tx-flight.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAG5E,OAAO,EAEL,KAAK,0BAA0B,EAChC,MAAM,kCAAkC,CAAA;AAEzC,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,yBAAyB,EAC1B,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,GAAG,EAAE,SAAS,SAAS,EAAE,CAAA;IACzB;;;;;OAKG;IACH,oBAAoB,EAAE,CAAC,KAAK,EAAE,yBAAyB,KAAK,0BAA0B,CAAA;IACtF;;;;OAIG;IACH,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACrD,6DAA6D;IAC7D,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,MAAM,CAAA;IAC5C,iDAAiD;IACjD,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,iDAAiD;IACjD,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,wDAAwD;IACxD,GAAG,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,SAAS,GAAG,IAAI,CAAA;CACtC;AAED,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,CAAA;AAE3D,eAAO,MAAM,WAAW,QAAS,MAAM,KAAG,iBA4BzC,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview `useTxFlight(id?)` — the consumer's entry point to a
|
|
4
|
+
* Provider's store.
|
|
5
|
+
*
|
|
6
|
+
* Resolution order for the id:
|
|
7
|
+
* 1. Explicit argument — `useTxFlight('settings-page')`.
|
|
8
|
+
* 2. Ambient context from the nearest `<TxFlightProvider>`.
|
|
9
|
+
* 3. Fallback `'default'`.
|
|
10
|
+
*
|
|
11
|
+
* Throws if no Provider has registered a store for the resolved id.
|
|
12
|
+
*
|
|
13
|
+
* For Task 5 the hook surface is `txs` + `addManual` + `remove` +
|
|
14
|
+
* `clear` + `get`. The wallet-adapter (`addWithWalletAdapter`) and
|
|
15
|
+
* tx-tracker (`addByHash`) integrations land in Tasks 7 and 8 and
|
|
16
|
+
* extend this surface.
|
|
17
|
+
*/
|
|
18
|
+
import { useSyncExternalStore } from 'react';
|
|
19
|
+
import { addByHashImpl } from './integrations/tx-tracker.js';
|
|
20
|
+
import { addWithWalletAdapterImpl, } from './integrations/wallet-adapter.js';
|
|
21
|
+
import { _getStoreForId, _useTxFlightContext } from './provider.js';
|
|
22
|
+
export const useTxFlight = (id) => {
|
|
23
|
+
const ambient = _useTxFlightContext();
|
|
24
|
+
const resolvedId = id ?? ambient?.id ?? 'default';
|
|
25
|
+
const store = _getStoreForId(resolvedId);
|
|
26
|
+
if (!store) {
|
|
27
|
+
throw new Error(`[@valve-tech/tx-flight-react] No <TxFlightProvider id="${resolvedId}"> found in tree`);
|
|
28
|
+
}
|
|
29
|
+
const txs = useSyncExternalStore(store.subscribe, store.getTxs, store.getTxs);
|
|
30
|
+
return {
|
|
31
|
+
txs,
|
|
32
|
+
addWithWalletAdapter: (input) => addWithWalletAdapterImpl(store, input),
|
|
33
|
+
addByHash: (input) => addByHashImpl(store, input),
|
|
34
|
+
addManual: (input) => {
|
|
35
|
+
store.dispatch.addWithTx(input.tx, null);
|
|
36
|
+
return input.tx.id;
|
|
37
|
+
},
|
|
38
|
+
remove: (txId) => {
|
|
39
|
+
store.dispatch.remove(txId);
|
|
40
|
+
},
|
|
41
|
+
clear: () => {
|
|
42
|
+
store.dispatch.clear();
|
|
43
|
+
},
|
|
44
|
+
get: (txId) => store.getState().txs.get(txId) ?? null,
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=use-tx-flight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-tx-flight.js","sourceRoot":"","sources":["../src/use-tx-flight.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAG5C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAC5D,OAAO,EACL,wBAAwB,GAEzB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAmCnE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAAW,EAAqB,EAAE;IAC5D,MAAM,OAAO,GAAG,mBAAmB,EAAE,CAAA;IACrC,MAAM,UAAU,GAAG,EAAE,IAAI,OAAO,EAAE,EAAE,IAAI,SAAS,CAAA;IACjD,MAAM,KAAK,GAAG,cAAc,CAAC,UAAU,CAAC,CAAA;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,0DAA0D,UAAU,kBAAkB,CACvF,CAAA;IACH,CAAC;IAED,MAAM,GAAG,GAAG,oBAAoB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAE7E,OAAO;QACL,GAAG;QACH,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,CAAC;QACvE,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC;QACjD,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YACnB,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAA;YACxC,OAAO,KAAK,CAAC,EAAE,CAAC,EAAE,CAAA;QACpB,CAAC;QACD,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACf,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC7B,CAAC;QACD,KAAK,EAAE,GAAG,EAAE;YACV,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;QACxB,CAAC;QACD,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI;KACtD,CAAA;AACH,CAAC,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@valve-tech/tx-flight-react",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "React UI primitives for rendering an in-flight transaction strip. Provider with multi-instance scoping (id + storage key), three add methods (addWithWalletAdapter for callers using @valve-tech/wallet-adapter, addByHash for raw hash + chain via @valve-tech/tx-tracker, addManual for back-fill), pluggable storage (localStorage default, IndexedDB and memory adapters included), atomic + layout components for headless composition, SSR-safe (the package's React-side hooks are client-only; storage adapters no-op on the server). Part of the valve-tech/evm-toolkit synchronized release line.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/valve-tech/evm-toolkit/tree/main/packages/tx-flight-react#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/valve-tech/evm-toolkit.git",
|
|
10
|
+
"directory": "packages/tx-flight-react"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/valve-tech/evm-toolkit/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ethereum",
|
|
17
|
+
"evm",
|
|
18
|
+
"viem",
|
|
19
|
+
"react",
|
|
20
|
+
"tx-flight",
|
|
21
|
+
"tx-status",
|
|
22
|
+
"transaction-tracker",
|
|
23
|
+
"wallet-adapter",
|
|
24
|
+
"ui-primitives"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "dist/index.js",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./storage": {
|
|
35
|
+
"types": "./dist/storage/index.d.ts",
|
|
36
|
+
"import": "./dist/storage/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"README.md",
|
|
42
|
+
"CHANGELOG.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"sideEffects": false,
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc -p .",
|
|
48
|
+
"typecheck": "tsc -p . --noEmit",
|
|
49
|
+
"lint": "eslint src",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:coverage": "vitest run --coverage",
|
|
52
|
+
"prepare": "yarn build"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@valve-tech/chain-source": "^0.8.1",
|
|
56
|
+
"@valve-tech/tx-tracker": "^0.8.1",
|
|
57
|
+
"@valve-tech/wallet-adapter": "^0.8.1",
|
|
58
|
+
"react": "^18.2.0 || ^19.0.0",
|
|
59
|
+
"react-dom": "^18.2.0 || ^19.0.0",
|
|
60
|
+
"viem": "^2.0.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"@valve-tech/chain-source": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"@valve-tech/tx-tracker": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"@valve-tech/wallet-adapter": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@testing-library/dom": "^10.4.0",
|
|
75
|
+
"@testing-library/react": "^16.1.0",
|
|
76
|
+
"@types/react": "^18.3.0",
|
|
77
|
+
"@types/react-dom": "^18.3.0",
|
|
78
|
+
"fake-indexeddb": "^6.0.0",
|
|
79
|
+
"happy-dom": "^15.0.0",
|
|
80
|
+
"react": "^18.3.1",
|
|
81
|
+
"react-dom": "^18.3.1"
|
|
82
|
+
}
|
|
83
|
+
}
|