@0xmonaco/react 1.0.47 → 1.0.50
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/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useInstruments/index.d.ts +2 -0
- package/dist/hooks/useInstruments/index.js +2 -0
- package/dist/hooks/useInstruments/types.d.ts +34 -0
- package/dist/hooks/useInstruments/types.js +0 -0
- package/dist/hooks/useInstruments/useInstruments.d.ts +27 -0
- package/dist/hooks/useInstruments/useInstruments.js +94 -0
- package/package.json +3 -3
package/dist/hooks/index.d.ts
CHANGED
package/dist/hooks/index.js
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { InstrumentEventData } from "@0xmonaco/types";
|
|
2
|
+
/**
|
|
3
|
+
* Return type for the useInstruments hook
|
|
4
|
+
*/
|
|
5
|
+
export interface UseInstrumentsReturn {
|
|
6
|
+
/**
|
|
7
|
+
* Current configuration of every market seen on the channel, ordered by
|
|
8
|
+
* symbol and then trading pair id. Delisted markets stay in the list carrying
|
|
9
|
+
* `isActive: false` — filter on `isActive` for the tradable set.
|
|
10
|
+
*/
|
|
11
|
+
instruments: InstrumentEventData[];
|
|
12
|
+
/** The same entries keyed by trading pair UUID, for direct lookup */
|
|
13
|
+
instrumentsByPair: Record<string, InstrumentEventData>;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a subscription to the instrument channel is currently registered.
|
|
16
|
+
* This is not a connection indicator: the client accepts subscriptions while
|
|
17
|
+
* the socket is down and sends them on the next open, so `true` means
|
|
18
|
+
* "registered", not "receiving".
|
|
19
|
+
*/
|
|
20
|
+
subscribed: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* A rejected subscription request. The core helper validates its arguments
|
|
23
|
+
* synchronously — passing an empty `tradingPairId` is the reachable case — so
|
|
24
|
+
* treat this as a bad-usage signal.
|
|
25
|
+
*
|
|
26
|
+
* It is deliberately NOT a transport signal: the client registers handlers
|
|
27
|
+
* regardless of socket state, and logs malformed-frame failures internally,
|
|
28
|
+
* so connection loss and unparseable frames never appear here. Drive
|
|
29
|
+
* connection UI from the client's own status/resync callbacks instead.
|
|
30
|
+
*/
|
|
31
|
+
error: Error | null;
|
|
32
|
+
/** Clear the current error state */
|
|
33
|
+
clearError: () => void;
|
|
34
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { UseInstrumentsReturn } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Hook for subscribing to real-time instrument configuration via WebSocket (public)
|
|
4
|
+
*
|
|
5
|
+
* On subscribe the channel delivers a snapshot of the current active markets
|
|
6
|
+
* (one `listing` frame each), then live listing, delisting, halt/unhalt and
|
|
7
|
+
* config-change frames. Every frame carries a market's FULL current
|
|
8
|
+
* configuration rather than a delta, so state is kept as a terminal-state
|
|
9
|
+
* entry per trading pair: each event replaces that market's entry outright.
|
|
10
|
+
*
|
|
11
|
+
* A delisted market keeps its entry with `isActive: false` rather than
|
|
12
|
+
* disappearing, so a consumer can distinguish "no longer tradable" from "never
|
|
13
|
+
* seen"; read `instruments.filter((instrument) => instrument.isActive)` for the
|
|
14
|
+
* tradable set.
|
|
15
|
+
*
|
|
16
|
+
* Caveat on reconnect: the server's post-reconnect snapshot covers the markets
|
|
17
|
+
* active at that moment, and the core helper delivers it as ordinary per-market
|
|
18
|
+
* events. A market delisted while the socket was down is simply absent from
|
|
19
|
+
* that snapshot, so its entry here keeps its pre-outage state until the market
|
|
20
|
+
* next emits. Closing that needs a snapshot-boundary signal the channel's
|
|
21
|
+
* handler contract does not currently carry.
|
|
22
|
+
*
|
|
23
|
+
* @param tradingPairId - Optional trading pair UUID to filter on; omit to
|
|
24
|
+
* receive every market. An empty string is rejected by the core helper and is
|
|
25
|
+
* surfaced through `error`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useInstruments(tradingPairId?: string): UseInstrumentsReturn;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useMonacoSDK } from "../useMonaco";
|
|
3
|
+
const IDLE = {
|
|
4
|
+
source: undefined,
|
|
5
|
+
tradingPairId: undefined,
|
|
6
|
+
byPair: {},
|
|
7
|
+
subscribed: false,
|
|
8
|
+
error: null,
|
|
9
|
+
};
|
|
10
|
+
/** Shared empty map, so an out-of-scope render keeps a stable identity. */
|
|
11
|
+
const NO_INSTRUMENTS = {};
|
|
12
|
+
/**
|
|
13
|
+
* Hook for subscribing to real-time instrument configuration via WebSocket (public)
|
|
14
|
+
*
|
|
15
|
+
* On subscribe the channel delivers a snapshot of the current active markets
|
|
16
|
+
* (one `listing` frame each), then live listing, delisting, halt/unhalt and
|
|
17
|
+
* config-change frames. Every frame carries a market's FULL current
|
|
18
|
+
* configuration rather than a delta, so state is kept as a terminal-state
|
|
19
|
+
* entry per trading pair: each event replaces that market's entry outright.
|
|
20
|
+
*
|
|
21
|
+
* A delisted market keeps its entry with `isActive: false` rather than
|
|
22
|
+
* disappearing, so a consumer can distinguish "no longer tradable" from "never
|
|
23
|
+
* seen"; read `instruments.filter((instrument) => instrument.isActive)` for the
|
|
24
|
+
* tradable set.
|
|
25
|
+
*
|
|
26
|
+
* Caveat on reconnect: the server's post-reconnect snapshot covers the markets
|
|
27
|
+
* active at that moment, and the core helper delivers it as ordinary per-market
|
|
28
|
+
* events. A market delisted while the socket was down is simply absent from
|
|
29
|
+
* that snapshot, so its entry here keeps its pre-outage state until the market
|
|
30
|
+
* next emits. Closing that needs a snapshot-boundary signal the channel's
|
|
31
|
+
* handler contract does not currently carry.
|
|
32
|
+
*
|
|
33
|
+
* @param tradingPairId - Optional trading pair UUID to filter on; omit to
|
|
34
|
+
* receive every market. An empty string is rejected by the core helper and is
|
|
35
|
+
* surfaced through `error`.
|
|
36
|
+
*/
|
|
37
|
+
export function useInstruments(tradingPairId) {
|
|
38
|
+
const { sdk } = useMonacoSDK();
|
|
39
|
+
const ws = sdk?.ws;
|
|
40
|
+
const [subscription, setSubscription] = useState(IDLE);
|
|
41
|
+
// Entries only count while they belong to the subscription the caller is
|
|
42
|
+
// asking for right now; the effect below realigns them a tick later.
|
|
43
|
+
const inScope = subscription.source === ws && subscription.tradingPairId === tradingPairId;
|
|
44
|
+
const clearError = useCallback(() => {
|
|
45
|
+
setSubscription((prev) => (prev.error === null ? prev : { ...prev, error: null }));
|
|
46
|
+
}, []);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!ws) {
|
|
49
|
+
setSubscription({ ...IDLE, tradingPairId });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Only a frame that belongs to *this* subscription may touch state — a
|
|
53
|
+
// late arrival from a torn-down one must not resurrect old markets.
|
|
54
|
+
const belongsToThisSubscription = (prev) => prev.source === ws && prev.tradingPairId === tradingPairId;
|
|
55
|
+
// Claim the scope before subscribing so the first frame, whenever it lands,
|
|
56
|
+
// already has somewhere in-scope to go.
|
|
57
|
+
setSubscription({ source: ws, tradingPairId, byPair: {}, subscribed: false, error: null });
|
|
58
|
+
// `instruments` throws synchronously on a missing handler or an empty pair
|
|
59
|
+
// id — surface that through `error` instead of letting it escape the effect.
|
|
60
|
+
let unsubscribe;
|
|
61
|
+
try {
|
|
62
|
+
unsubscribe = ws.instruments((event) => {
|
|
63
|
+
setSubscription((prev) => (belongsToThisSubscription(prev) ? { ...prev, byPair: { ...prev.byPair, [event.tradingPairId]: event.data } } : prev));
|
|
64
|
+
}, tradingPairId);
|
|
65
|
+
setSubscription((prev) => (belongsToThisSubscription(prev) ? { ...prev, subscribed: true } : prev));
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
69
|
+
setSubscription((prev) => (belongsToThisSubscription(prev) ? { ...prev, subscribed: false, error } : prev));
|
|
70
|
+
}
|
|
71
|
+
return () => {
|
|
72
|
+
unsubscribe?.();
|
|
73
|
+
setSubscription((prev) => (belongsToThisSubscription(prev) ? { ...prev, subscribed: false } : prev));
|
|
74
|
+
};
|
|
75
|
+
}, [ws, tradingPairId]);
|
|
76
|
+
const instrumentsByPair = inScope ? subscription.byPair : NO_INSTRUMENTS;
|
|
77
|
+
// Sorted with plain codepoint comparison rather than `localeCompare`, so the
|
|
78
|
+
// order does not shift with the runtime locale, and tie-broken on the pair id
|
|
79
|
+
// because a spot and a perp market can carry the same symbol.
|
|
80
|
+
const instruments = useMemo(() => Object.values(instrumentsByPair).sort((left, right) => {
|
|
81
|
+
if (left.symbol !== right.symbol)
|
|
82
|
+
return left.symbol < right.symbol ? -1 : 1;
|
|
83
|
+
if (left.tradingPairId === right.tradingPairId)
|
|
84
|
+
return 0;
|
|
85
|
+
return left.tradingPairId < right.tradingPairId ? -1 : 1;
|
|
86
|
+
}), [instrumentsByPair]);
|
|
87
|
+
return {
|
|
88
|
+
instruments,
|
|
89
|
+
instrumentsByPair,
|
|
90
|
+
subscribed: inScope && subscription.subscribed,
|
|
91
|
+
error: inScope ? subscription.error : null,
|
|
92
|
+
clearError,
|
|
93
|
+
};
|
|
94
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xmonaco/react",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.50",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"homepage": "https://docs.0xmonaco.com/sdk/typescript",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"lint": "biome lint ."
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@0xmonaco/core": "1.0.
|
|
20
|
-
"@0xmonaco/types": "1.0.
|
|
19
|
+
"@0xmonaco/core": "1.0.50",
|
|
20
|
+
"@0xmonaco/types": "1.0.50"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/react": "^19.1.12",
|