@0xmonaco/types 1.0.50 → 1.0.51

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.
@@ -84,6 +84,51 @@ export interface Order {
84
84
  positionId?: string;
85
85
  /** Stable cause when status is CANCELLED */
86
86
  terminalReason?: OrderTerminalReason;
87
+ /**
88
+ * Opaque, compare-only revision counter for this order's state: a strictly
89
+ * higher value is strictly newer state for that order, and equal values
90
+ * mean identical state.
91
+ *
92
+ * Compare two REST reads of the same order with it — whether a re-poll
93
+ * advanced, or which of `getOrder` and a list row is fresher.
94
+ *
95
+ * The SAME counter is on every WebSocket order event and on the `orders`
96
+ * snapshot frame, so it is also the snapshot-versus-stream reconciliation
97
+ * key: after a disconnect, keep applying events as they arrive and merge a
98
+ * REST read per order — take the REST row when
99
+ * `snapshot.version >= local.version`, else keep local. No buffering, so no
100
+ * quote blackout on reconnect. The rule is `>=` rather than `>` because one
101
+ * sequencer step emits several events for one order (an `OrderPlaced` and
102
+ * then its fills) and a batch shares one step across every item, so equal
103
+ * means "same step" on the stream — and a REST row is that step's final
104
+ * state, so it wins the tie. Between two REST reads, equal does mean
105
+ * identical state.
106
+ *
107
+ * The snapshot frame alone does not close a reconnect gap: an order that
108
+ * filled or was cancelled while the socket was down is in neither the
109
+ * resting snapshot nor the stream. Recover those explicitly — `listUserTrades`
110
+ * for fills, and a targeted `getOrder` for anything local state believes is
111
+ * resting that the snapshot no longer lists.
112
+ *
113
+ * Read nothing else into it. It is not a count of updates; consecutive
114
+ * values are not expected (it is drawn from a global sequence shared by
115
+ * every order, so per-order values are sparse and gaps are normal); and it
116
+ * is not comparable across different orders.
117
+ *
118
+ * Absent means unknown — an order last written before this field existed.
119
+ * Fall back to a full field comparison for those rather than treating
120
+ * absent as version zero. Note that a comparison against an absent value
121
+ * cannot be true either way, so an order held locally without a version is
122
+ * never refreshed by the merge above: take the REST row unconditionally for
123
+ * those rather than letting them go stale.
124
+ *
125
+ * Prefer this over `updatedAt`: `version` is identical on every endpoint
126
+ * that serves the same order state and never advances without a state
127
+ * change, whereas `updatedAt` carries a different clock per endpoint (order
128
+ * detail reports the matching-engine event time, order lists report the
129
+ * database commit time) and can move without the order changing.
130
+ */
131
+ version?: number;
87
132
  }
88
133
  /** Stable terminal causes for accepted orders that end as CANCELLED. */
89
134
  export type OrderTerminalReason = "USER_REQUESTED" | "REPLACED" | "LIQUIDATION" | "INSUFFICIENT_MARGIN" | "REDUCE_ONLY_EXHAUSTED" | "POSITION_CLOSED" | "OCO_SIBLING_TRIGGERED" | "SYSTEM";
@@ -168,6 +213,11 @@ export type ConditionalOrderState = "PENDING_PARENT" | "ACTIVE" | "TRIGGERING" |
168
213
  * - ACTIVE: inside the execution window; child slices are firing
169
214
  * - COMPLETED: window ended (fully filled or hard-stopped with a shortfall)
170
215
  * - CANCELLED: cancelled by the user before the window ended
216
+ *
217
+ * These are the values the server sends today, not a runtime guarantee:
218
+ * `@0xmonaco/core` parses the `twap_orders` WebSocket frame leniently, so a
219
+ * state added server-side after this version ships is delivered as-is rather
220
+ * than dropping the update (0XM-2628). Match it with a default branch.
171
221
  */
172
222
  export type TwapOrderState = "PENDING" | "ACTIVE" | "COMPLETED" | "CANCELLED";
173
223
  /**
@@ -456,6 +456,28 @@ export interface ConditionalOrder {
456
456
  failureReason?: string;
457
457
  createdAt: string;
458
458
  updatedAt: string;
459
+ /**
460
+ * Opaque, compare-only revision counter for this conditional order's state —
461
+ * the sequencer step that wrote it (0XM-2440). A strictly higher value is
462
+ * strictly newer state for that conditional order.
463
+ *
464
+ * Present on REST reads and on the subscribe-time `conditional_orders`
465
+ * snapshot frame. **Absent on live conditional events**, by design: those are
466
+ * emitted from a model captured before the sequencer acted, so the only value
467
+ * available there is the one the row held *before* the change being
468
+ * announced — a client merging on `>=` would let a snapshot read at that
469
+ * version win the tie and undo the transition.
470
+ *
471
+ * So seed conditional state from the snapshot and REST, comparing with
472
+ * `snapshot.version >= local.version`, and apply live events as state without
473
+ * ranking them. `>=` rather than `>`: one sequencer step can write several
474
+ * conditional rows (a trigger writes the triggered row and cancels its OCO
475
+ * sibling), so equal means "same step", not identical state.
476
+ *
477
+ * Absent means unknown — a row last written before the field existed — never
478
+ * zero. Wire: `version`.
479
+ */
480
+ version?: number;
459
481
  }
460
482
  export interface ListConditionalOrdersResponse {
461
483
  orders: ConditionalOrder[];
@@ -29,6 +29,55 @@ export interface WebSocketResyncInfo {
29
29
  }
30
30
  /** Handler invoked after a reconnect resumes the live stream. */
31
31
  export type ResyncHandler = (info: WebSocketResyncInfo) => void;
32
+ /**
33
+ * Machine-readable classification the server puts on every `Error` frame so a
34
+ * client can branch on the failure without string-matching the human message.
35
+ * Mirrors `ws-api`'s `WsErrorCode` enum, whose SCREAMING_SNAKE_CASE wire
36
+ * strings are pinned on the Rust side in `ws-api/src/websocket/protocol.rs`
37
+ * and cross-checked against this union by `@0xmonaco/core`'s
38
+ * `websocket-enum-parity` test.
39
+ */
40
+ export type WebSocketErrorCode =
41
+ /** A user-specific channel was subscribed before authenticating. */
42
+ "AUTH_REQUIRED"
43
+ /** The `Authenticate` handshake was rejected; the connection is closed after this frame. */
44
+ | "AUTH_FAILED"
45
+ /** The per-user concurrent-connection cap was reached; this connection is closed after the frame. */
46
+ | "CONNECTION_LIMIT"
47
+ /** The per-connection subscription-cardinality cap was reached. */
48
+ | "SUBSCRIPTION_LIMIT"
49
+ /** A subscription request was malformed or failed parameter validation. */
50
+ | "INVALID_SUBSCRIPTION"
51
+ /** An inbound frame could not be parsed, or an unsupported frame type was sent. */
52
+ | "INVALID_MESSAGE"
53
+ /**
54
+ * A previously-authenticated session became invalid after the handshake
55
+ * (revoked, expired, or the account was banned/deactivated). Re-authenticate
56
+ * rather than blindly reconnecting into the same rejection.
57
+ */
58
+ | "SESSION_INVALID"
59
+ /**
60
+ * The subscribe-time snapshot could not be read. The subscription is still
61
+ * active and live diffs flow, but the baseline is missing — refetch it (e.g.
62
+ * from REST) rather than assuming the stream started from a coherent state.
63
+ */
64
+ | "SNAPSHOT_UNAVAILABLE";
65
+ /**
66
+ * An `Error` frame the server sent on this connection.
67
+ *
68
+ * `code` is widened past {@link WebSocketErrorCode} on purpose: a code added
69
+ * server-side after this SDK version shipped is delivered as-is rather than
70
+ * dropped, so an older client still sees that something failed and can log it.
71
+ * Narrow with a `switch` on the known values and treat the default as unknown.
72
+ */
73
+ export interface WebSocketErrorInfo {
74
+ /** Stable classification of the failure. */
75
+ code: WebSocketErrorCode | (string & {});
76
+ /** Human-readable description; for logging and display, never for branching. */
77
+ message: string;
78
+ }
79
+ /** Handler invoked for every `Error` frame the server sends. */
80
+ export type ErrorHandler = (info: WebSocketErrorInfo) => void;
32
81
  /**
33
82
  * Reconnect/resync knobs and callbacks for the Monaco WebSocket client.
34
83
  *
@@ -62,6 +111,26 @@ export interface MonacoWebSocketConfig {
62
111
  * the snapshot was in flight are newer than the snapshot rows they duplicate.
63
112
  */
64
113
  onResync?: ResyncHandler;
114
+ /**
115
+ * Called for every `Error` frame the server sends on this connection.
116
+ *
117
+ * `Error` frames are connection-scoped, not channel-scoped, so they reach no
118
+ * subscription handler — without this callback a failed subscribe, an
119
+ * invalidated session, or an unreadable subscribe-time snapshot is invisible
120
+ * to the app. Branch on `code`:
121
+ *
122
+ * - `SNAPSHOT_UNAVAILABLE` — live diffs flow but the baseline is missing;
123
+ * refetch it. The subscription itself is fine.
124
+ * - `SESSION_INVALID` — re-authenticate rather than reconnect into the same
125
+ * rejection.
126
+ * - `AUTH_REQUIRED` — a rejected SUBSCRIBE, not an invalidated session: the
127
+ * channel was never registered, and this client still holds your handler,
128
+ * so it treats the channel as subscribed and sends no new `Subscribe`.
129
+ * Re-authenticating alone leaves it dead. Release the last handler for the
130
+ * channel and subscribe again after authenticating (0XM-2644 tracks doing
131
+ * this automatically).
132
+ */
133
+ onError?: ErrorHandler;
65
134
  }
66
135
  /**
67
136
  * Raw event message interface
@@ -45,7 +45,7 @@ export interface UserBalanceEventData {
45
45
  /** Total balance in raw format (wei) */
46
46
  totalRaw: string;
47
47
  /** Reason for the balance update */
48
- reason: BalanceUpdateReason;
48
+ reason: BalanceUpdateReason | (string & {});
49
49
  /** Reference ID for related entity (e.g., order ID, deposit ID) */
50
50
  referenceId: string | null;
51
51
  /** Timestamp when the balance was updated (ISO 8601) */
@@ -13,7 +13,10 @@ export type ConditionalOrderEventReason = "created" | "activated" | "cancelled"
13
13
  * A conditional (TP/SL) order's current state.
14
14
  *
15
15
  * **The value unions below are the values the server can send today, not a
16
- * runtime guarantee.** `@0xmonaco/core` parses this frame leniently — an
16
+ * runtime guarantee** — which is why each of those fields is typed
17
+ * `Union | (string & {})`: autocomplete still offers the known values, but a
18
+ * `switch` over one needs a `default` branch because an unrecognized value
19
+ * really can arrive (0XM-2628). `@0xmonaco/core` parses this frame leniently — an
17
20
  * unrecognized `orderType`, `state`, `side`, `positionSide`, `conditionType`,
18
21
  * `triggerSource`, `associationType` or `timeInForce` is passed through as-is
19
22
  * rather than validated, because throwing inside the WebSocket message handler
@@ -36,13 +39,13 @@ export interface ConditionalOrderEventData {
36
39
  marginAccountId: string;
37
40
  positionId?: string;
38
41
  parentOrderId?: string;
39
- associationType?: "POSITION" | "PARENT_ORDER";
42
+ associationType?: "POSITION" | "PARENT_ORDER" | (string & {});
40
43
  linkedGroupId?: string;
41
- conditionType: ConditionalOrderConditionType;
42
- triggerSource: ConditionalOrderTriggerSource;
44
+ conditionType: ConditionalOrderConditionType | (string & {});
45
+ triggerSource: ConditionalOrderTriggerSource | (string & {});
43
46
  triggerPrice: string;
44
- side: OrderSide;
45
- positionSide: PositionSide;
47
+ side: OrderSide | (string & {});
48
+ positionSide: PositionSide | (string & {});
46
49
  /**
47
50
  * Order shape the conditional order places when it triggers.
48
51
  *
@@ -58,7 +61,7 @@ export interface ConditionalOrderEventData {
58
61
  * the value would arrive as e.g. `STOPLOSS` rather than the canonical
59
62
  * `STOP_LOSS`. The parser passes such a value through untouched.
60
63
  */
61
- orderType: OrderType;
64
+ orderType: OrderType | (string & {});
62
65
  limitPrice?: string;
63
66
  quantity?: string;
64
67
  slippageToleranceBps?: number;
@@ -71,8 +74,8 @@ export interface ConditionalOrderEventData {
71
74
  * the server maps exhaustively over every variant, so a value written by any
72
75
  * other path reaches the wire as-is. Narrow before relying on the subset.
73
76
  */
74
- timeInForce?: TimeInForce;
75
- state: ConditionalOrderState;
77
+ timeInForce?: TimeInForce | (string & {});
78
+ state: ConditionalOrderState | (string & {});
76
79
  triggeredOrderId?: string;
77
80
  triggeredAt?: string;
78
81
  activatedAt?: string;
@@ -87,6 +90,28 @@ export interface ConditionalOrderEventData {
87
90
  */
88
91
  reason: string;
89
92
  updatedAt: string;
93
+ /**
94
+ * Opaque, compare-only revision counter for this conditional order's state —
95
+ * the sequencer step that wrote it (0XM-2440). A strictly higher value is
96
+ * strictly newer state for that conditional order.
97
+ *
98
+ * Present on REST reads and on the subscribe-time `conditional_orders`
99
+ * snapshot frame. **Absent on live conditional events**, by design: those are
100
+ * emitted from a model captured before the sequencer acted, so the only value
101
+ * available there is the one the row held *before* the change being
102
+ * announced — a client merging on `>=` would let a snapshot read at that
103
+ * version win the tie and undo the transition.
104
+ *
105
+ * So seed conditional state from the snapshot and REST, comparing with
106
+ * `snapshot.version >= local.version`, and apply live events as state without
107
+ * ranking them. `>=` rather than `>`: one sequencer step can write several
108
+ * conditional rows (a trigger writes the triggered row and cancels its OCO
109
+ * sibling), so equal means "same step", not identical state.
110
+ *
111
+ * Absent means unknown — a row last written before the field existed — never
112
+ * zero. Wire: `version`.
113
+ */
114
+ version?: number;
90
115
  }
91
116
  export interface ConditionalOrderEvent {
92
117
  eventType: "conditional_order_update";
@@ -36,8 +36,18 @@ export type InstrumentChangeKind = "listing" | "delisting" | "halt" | "unhalt" |
36
36
  * Full current configuration for a market on the `instrument` channel.
37
37
  */
38
38
  export interface InstrumentEventData {
39
- /** What changed for this market */
40
- change: InstrumentChangeKind;
39
+ /**
40
+ * What changed for this market.
41
+ *
42
+ * Widened past {@link InstrumentChangeKind} on purpose: `@0xmonaco/core`
43
+ * parses this frame leniently, so a change kind added server-side after this
44
+ * SDK version ships is delivered as-is rather than dropping the frame — and
45
+ * with it the full market config the frame carries, which can hide a market
46
+ * going inactive behind an unfamiliar label (0XM-2628).
47
+ * `InstrumentChangeKind` names the values the server sends today — narrow
48
+ * with a `switch` that keeps a `default` branch.
49
+ */
50
+ change: InstrumentChangeKind | (string & {});
41
51
  /** Trading pair identifier (UUID) */
42
52
  tradingPairId: string;
43
53
  /** Market symbol, e.g. "BTC/USDC" */
@@ -85,6 +85,11 @@ export interface MarketStatsEvent {
85
85
  /**
86
86
  * A single market's row in the all-markets summary frame. A lighter shape than
87
87
  * {@link MarketStatsData}: enough to render a markets table.
88
+ *
89
+ * `marketStatus` is typed `MarketStatus | (string & {})`: `@0xmonaco/core`
90
+ * parses this frame leniently, so an unrecognized status is passed through
91
+ * rather than throwing and dropping the summary row (0XM-2628). `MarketStatus`
92
+ * names the values the server sends today — match it with a `default` branch.
88
93
  */
89
94
  export interface MarketStatsSummary {
90
95
  /** Trading pair identifier (UUID) */
@@ -114,7 +119,7 @@ export interface MarketStatsSummary {
114
119
  /** Next funding time as a millisecond epoch. Perp-only; omitted for spot. */
115
120
  fundingTimestamp?: number;
116
121
  /** Tradability label (always present) */
117
- marketStatus: MarketStatus;
122
+ marketStatus: MarketStatus | (string & {});
118
123
  }
119
124
  /**
120
125
  * All-markets summary event on the `market_stats` channel: one
@@ -21,15 +21,20 @@ export interface OHLCVEvent {
21
21
  /** Trading pair ID (UUID) */
22
22
  tradingPairId: string;
23
23
  /** Candlestick interval */
24
- interval: Interval;
24
+ interval: Interval | (string & {});
25
25
  /** Trading mode */
26
- tradingMode: TradingMode;
26
+ tradingMode: TradingMode | (string & {});
27
27
  /**
28
28
  * Price series this candle belongs to. Always a concrete series on the wire
29
29
  * (`trade` or `mark`, never the `*` selector); lets a wildcard subscriber
30
30
  * tell the two streams apart. Absent only on legacy frames that omit it.
31
+ *
32
+ * Widened past the two known values on purpose: a wildcard subscription
33
+ * matches any suffix, so a series added server-side after this version still
34
+ * arrives here, and telling the streams apart is the entire point of the
35
+ * field. Match it with a `default` branch.
31
36
  */
32
- priceType?: "trade" | "mark";
37
+ priceType?: "trade" | "mark" | (string & {});
33
38
  /** OHLCV candlestick data */
34
39
  candlestick: Candlestick;
35
40
  }
@@ -29,7 +29,7 @@ export interface OrderbookLevel {
29
29
  */
30
30
  export interface PriceChange {
31
31
  /** Order side (BUY or SELL) */
32
- side: OrderSide;
32
+ side: OrderSide | (string & {});
33
33
  /** Old price before the change */
34
34
  oldPrice?: string;
35
35
  /** New price after the change */
@@ -46,7 +46,7 @@ export interface OrderbookEvent {
46
46
  /** Trading pair ID (UUID) */
47
47
  tradingPairId: string;
48
48
  /** Trading mode (SPOT, MARGIN) */
49
- tradingMode: TradingMode;
49
+ tradingMode: TradingMode | (string & {});
50
50
  /** Bid orders (buy orders) - Full depth */
51
51
  bids: OrderbookLevel[];
52
52
  /** Ask orders (sell orders) - Full depth */
@@ -18,7 +18,7 @@ export interface OrderEvent {
18
18
  /** Order ID (UUID) */
19
19
  orderId: string;
20
20
  /** Event type */
21
- eventType: OrderEventType;
21
+ eventType: OrderEventType | (string & {});
22
22
  /** ISO 8601 timestamp */
23
23
  timestamp: string;
24
24
  /** Event-specific data */
@@ -46,11 +46,11 @@ export interface CommonOrderEventData {
46
46
  */
47
47
  tradingPair: string;
48
48
  /** Trading mode (e.g., "SPOT", "MARGIN") */
49
- tradingMode: TradingMode;
49
+ tradingMode: TradingMode | (string & {});
50
50
  /** Order type - present in most events */
51
- orderType?: OrderType;
51
+ orderType?: OrderType | (string & {});
52
52
  /** Order side - present in most events */
53
- side?: OrderSide;
53
+ side?: OrderSide | (string & {});
54
54
  /** Order price - present in most events */
55
55
  price?: string;
56
56
  /** Order quantity - present in most events */
@@ -58,7 +58,7 @@ export interface CommonOrderEventData {
58
58
  /** Order quantity in RAW (integer, base-unit) precision. Wire: `quantity_raw`. */
59
59
  quantityRaw?: string;
60
60
  /** Time in force - present in most events */
61
- timeInForce?: TimeInForce;
61
+ timeInForce?: TimeInForce | (string & {});
62
62
  /**
63
63
  * Client-assigned correlation handle, echoed so an event can be matched to
64
64
  * the request that placed the order without a round trip to `getOrder`.
@@ -79,6 +79,65 @@ export interface CommonOrderEventData {
79
79
  * silence. Omitted when the order carried none. Wire: `client_order_id`.
80
80
  */
81
81
  clientOrderId?: string;
82
+ /**
83
+ * Sequencer step that produced this event (0XM-2440) — the SAME counter
84
+ * `getOrder` and `getPaginatedOrders` return, so the two are directly
85
+ * comparable. Higher is strictly newer for that order.
86
+ *
87
+ * This is what makes a reconnect reconcilable without buffering: apply
88
+ * events as they arrive, and when the REST snapshot lands take the snapshot
89
+ * for an order when `snapshot.version >= local.version`, else keep local.
90
+ *
91
+ * **Equal does NOT mean identical state here.** One sequencer step emits
92
+ * several events for one order (an `OrderPlaced` and then its fills), and a
93
+ * batch place/replace shares one step across every item — so equal means
94
+ * "same step". A snapshot row is that step's FINAL state, which is why the
95
+ * rule is `>=` and not `>`: on a tie the snapshot is the authority.
96
+ *
97
+ * Values are sparse per order (it comes from a global sequence) and are not
98
+ * comparable between different orders. Omitted, never zero, when unknown —
99
+ * an order last written before the field existed. Wire: `version`.
100
+ */
101
+ version?: number;
102
+ /**
103
+ * `true` when the order was admitted post-only (maker-only), so a subscriber
104
+ * can confirm the flag was honoured without a round trip to `getOrder`.
105
+ * Carried on the `OrderPlaced` acknowledgement, the taker fill, and the
106
+ * resting order's lifecycle — `OrderPartiallyCancelled` (an in-place
107
+ * reduction, which is how an MM's own `remainingQuantityTarget` replace is
108
+ * announced), `OrderCancelled` and `OrderExpired`.
109
+ *
110
+ * Declared on the shared block, but two variants can never carry it, and
111
+ * this optional field is the only thing that says so: a **maker fill**,
112
+ * which is out of scope because a maker already learned the flag from its
113
+ * own acknowledgement; and **`OrderRejected`**, which only ever describes a
114
+ * MARKET order with no liquidity or an unfilled IOC/FOK — and post-only is
115
+ * refused on both, so no post-only order can reach it.
116
+ *
117
+ * Only ever present as `true`: on a variant that CARRIES the field, an order
118
+ * that is not post-only omits the key rather than sending `postOnly: false`,
119
+ * exactly as the REST read model's `postOnly?: boolean` does, so
120
+ * `event.data.postOnly ?? false` reads the same as REST *for that event*.
121
+ *
122
+ * That equivalence is per-event, and it does NOT hold on a maker fill.
123
+ * A post-only order rests and therefore fills as a maker, and the maker
124
+ * payload never declares the field — so `?? false` there would return
125
+ * `false` for an order REST reports as `true`. Absence on a maker fill
126
+ * carries NO information. Track `postOnly` as order-level state learned from
127
+ * the `OrderPlaced` acknowledgement (or a REST read) and carry it forward;
128
+ * never re-derive it from whichever event arrived last.
129
+ *
130
+ * On a carrying variant, absent is not evidence the flag was dropped — it is
131
+ * the ordinary shape of a non-post-only order, and also what an order placed
132
+ * before the flag was persisted reports.
133
+ *
134
+ * A post-only order that WOULD have crossed the book is rejected before
135
+ * acceptance and emits no event at all (see `clientOrderId`), so in practice
136
+ * the flag reaches a subscriber on a resting order's lifecycle — never on a
137
+ * taker fill, which an admitted post-only order cannot produce.
138
+ * Wire: `post_only`.
139
+ */
140
+ postOnly?: boolean;
82
141
  }
83
142
  /**
84
143
  * OrderPlaced event - emitted when a new order is placed
@@ -87,54 +146,94 @@ export interface OrderPlacedEvent extends OrderEvent {
87
146
  eventType: "OrderPlaced";
88
147
  data: CommonOrderEventData & {
89
148
  /** Order status */
90
- status: OrderStatus;
149
+ status: OrderStatus | (string & {});
91
150
  };
92
151
  }
93
152
  /**
94
153
  * OrderMatched event - emitted when an order is matched but not necessarily filled
95
- * This event is only emitted for taker orders
154
+ * This event is only emitted for taker orders.
155
+ *
156
+ * Carries the SAME payload as `OrderFilled`/`OrderPartiallyFilled`: the
157
+ * matching engine builds one `TakerOrderEventData` and picks the event type
158
+ * from the resulting status, so every execution field below is on the wire
159
+ * here too.
160
+ *
161
+ * Because this event carries only that one shape, the fields below follow it
162
+ * exactly rather than being uniformly optional. A match that filled nothing
163
+ * still SENDS the keys — `tradeId`, `executionPrice` and `executedAt` arrive as
164
+ * `null`, not absent. Only `lastFillQuantity` and `lastFillQuantityRaw` are
165
+ * ever omitted, being the pair the server skips when there is no incremental
166
+ * fill.
96
167
  */
97
168
  export interface OrderMatchedEvent extends OrderEvent {
98
169
  eventType: "OrderMatched";
99
170
  data: CommonOrderEventData & {
100
- /** Taker order ID */
101
- takerOrderId?: string;
102
- /** Reference price used for slippage calculation */
103
- referencePrice?: string;
104
- /** Best price achieved in matching */
105
- bestPrice?: string;
106
- /** Worst price achieved in matching */
107
- worstPrice?: string;
108
- /** Actual slippage in basis points */
109
- actualSlippageBps?: number;
110
- /** Maximum allowed slippage in basis points */
111
- maxSlippageBps?: number;
112
- /** Number of trades executed (taker only) */
113
- tradesCount?: number;
114
- /** Total quantity filled (taker only) */
115
- totalFilled?: string;
116
- /** Remaining quantity to be filled (taker only) */
117
- remainingQuantity?: string;
118
- /** Average fill price across all trades */
119
- averageFillPrice?: string;
120
- /** Order status */
121
- status?: OrderStatus;
171
+ /** Fill role of this order (`taker` on this event). */
172
+ role: OrderRole | (string & {});
173
+ /** Trade ID of the first trade this event covers. `null` when it covers none. */
174
+ tradeId: string | null;
175
+ /** Execution price of the first trade this event covers. `null` when it covers none. */
176
+ executionPrice: string | null;
177
+ /** Cumulative quantity filled on the order at this event. Wire: `filled_quantity`. */
178
+ filledQuantity: string;
179
+ /** `filledQuantity` in RAW (integer, base-unit) precision. Wire: `filled_quantity_raw`. */
180
+ filledQuantityRaw: string;
181
+ /** Incremental quantity filled by THIS event. Omitted when there is no fill. Wire: `last_fill_quantity`. */
182
+ lastFillQuantity?: string;
183
+ /** `lastFillQuantity` in RAW (integer, base-unit) precision. Omitted with it. Wire: `last_fill_quantity_raw`. */
184
+ lastFillQuantityRaw?: string;
185
+ /** `remainingQuantity` in RAW (integer, base-unit) precision. Wire: `remaining_quantity_raw`. */
186
+ remainingQuantityRaw: string;
187
+ /**
188
+ * Monaco fee for the fills this event covers, in normalized decimal QUOTE
189
+ * units (negative = maker rebate). Excludes the application taker fee, so
190
+ * it is smaller than the order echo's `totalTakerFees` for an application
191
+ * that charges one, and equal to it for one that does not. Wire: `fee`.
192
+ */
193
+ fee: string;
194
+ /**
195
+ * `fee` in RAW quote-unit precision (scaled by 10^quoteDecimals). Always a
196
+ * whole number -- the engine truncates every fee to an integral raw unit
197
+ * when it stores it -- so `fee` is exactly this value divided by
198
+ * 10^quoteDecimals, and `BigInt(feeRaw)` is safe. Wire: `fee_raw`.
199
+ */
200
+ feeRaw: string;
201
+ /** Execution timestamp of the first trade this event covers (ISO 8601). `null` when it covers none. */
202
+ executedAt: string | null;
203
+ /** Reference price used for slippage calculation. `null` when unset. */
204
+ referencePrice: string | null;
205
+ /** Best price achieved in matching. `null` when unset. */
206
+ bestPrice: string | null;
207
+ /** Worst price achieved in matching. `null` when unset. */
208
+ worstPrice: string | null;
209
+ /** Actual slippage in basis points. `null` when unset. */
210
+ actualSlippageBps: number | null;
211
+ /** Maximum allowed slippage in basis points. `null` when unset. */
212
+ maxSlippageBps: number | null;
213
+ /** Number of trades executed. */
214
+ tradesCount: number;
215
+ /** Remaining quantity to be filled. */
216
+ remainingQuantity: string;
217
+ /** Average fill price across all trades. `null` when there is none. */
218
+ averageFillPrice: string | null;
219
+ /** Order status. */
220
+ status: OrderStatus | (string & {});
122
221
  };
123
222
  }
124
223
  /**
125
224
  * OrderPartiallyFilled event - emitted when an order is partially filled
126
- * Can be emitted for both taker and maker orders with different field sets
225
+ * Can be emitted for both taker and maker orders with different field sets.
127
226
  *
128
- * Taker events include: trades_count, total_filled, slippage data
129
- * Maker events include: trade_id, execution_price, execution_quantity, maker_fee
227
+ * Taker events add `trades_count` and the slippage fields; maker events add
228
+ * `trade_id` and `execution_price`. Both roles report the cumulative fill as
229
+ * `filled_quantity` and this event's increment as `last_fill_quantity`, and
230
+ * both carry the fill charge as `fee` (0XM-2389). `total_filled`,
231
+ * `execution_quantity` and `maker_fee` were never emitted on a fill payload
232
+ * and are not declared here.
130
233
  */
131
234
  export interface OrderPartiallyFilledEvent extends OrderEvent {
132
235
  eventType: "OrderPartiallyFilled";
133
236
  data: CommonOrderEventData & {
134
- /** Taker order ID (taker orders only) */
135
- takerOrderId?: string;
136
- /** Maker order ID (maker orders only) */
137
- makerOrderId?: string;
138
237
  /** Reference price (taker orders only) */
139
238
  referencePrice?: string;
140
239
  /** Best price (taker orders only) */
@@ -147,14 +246,12 @@ export interface OrderPartiallyFilledEvent extends OrderEvent {
147
246
  maxSlippageBps?: number;
148
247
  /** Number of trades (taker orders only - not present for maker) */
149
248
  tradesCount?: number;
150
- /** Total filled quantity (taker orders only - not present for maker) */
151
- totalFilled?: string;
152
249
  /** Remaining quantity (present for both taker and maker) */
153
250
  remainingQuantity?: string;
154
251
  /** Average fill price (taker orders only) */
155
252
  averageFillPrice?: string;
156
253
  /** Order status (taker orders only) */
157
- status?: OrderStatus;
254
+ status?: OrderStatus | (string & {});
158
255
  /** Trade ID (maker orders only) */
159
256
  tradeId?: string;
160
257
  /** Execution price (maker orders only) */
@@ -180,30 +277,43 @@ export interface OrderPartiallyFilledEvent extends OrderEvent {
180
277
  /** `remainingQuantity` in RAW (integer, base-unit) precision. Wire: `remaining_quantity_raw`. */
181
278
  remainingQuantityRaw?: string;
182
279
  /**
183
- * Fee charged for this fill (maker orders only). The wire field is `fee`
184
- * (→ `fee`), not `makerFee`; taker fills carry the same `fee` field.
280
+ * Monaco fee for the fills this event covers, in normalized decimal QUOTE
281
+ * units (negative = maker rebate). The wire field is `fee`, not `makerFee`;
282
+ * taker and maker carry the same field. A taker event aggregates every
283
+ * trade it covers; a maker event is one trade.
284
+ *
285
+ * Excludes the application taker fee, so it is NOT the order echo's
286
+ * `totalTakerFees` (which is `fee + applicationTakerFee`). It previously
287
+ * held the raw integer value now exposed as `feeRaw`.
185
288
  */
186
289
  fee?: string;
290
+ /**
291
+ * `fee` in RAW quote-unit precision (scaled by 10^quoteDecimals). Always a
292
+ * whole number -- the engine truncates every fee to an integral raw unit
293
+ * when it stores it -- so `fee` is exactly this value divided by
294
+ * 10^quoteDecimals, and `BigInt(feeRaw)` is safe. Wire: `fee_raw`.
295
+ */
296
+ feeRaw?: string;
187
297
  /** Trade execution timestamp (maker orders only) */
188
298
  executedAt?: string;
189
299
  /** Role of the order in the trade (taker or maker) */
190
- role?: OrderRole;
300
+ role?: OrderRole | (string & {});
191
301
  };
192
302
  }
193
303
  /**
194
304
  * OrderFilled event - emitted when an order is completely filled
195
- * Can be emitted for both taker and maker orders with different field sets
305
+ * Can be emitted for both taker and maker orders with different field sets.
196
306
  *
197
- * Taker events include: trades_count, total_filled, slippage data
198
- * Maker events include: trade_id, execution_price, execution_quantity, maker_fee
307
+ * Taker events add `trades_count` and the slippage fields; maker events add
308
+ * `trade_id` and `execution_price`. Both roles report the cumulative fill as
309
+ * `filled_quantity` and this event's increment as `last_fill_quantity`, and
310
+ * both carry the fill charge as `fee` (0XM-2389). `total_filled`,
311
+ * `execution_quantity` and `maker_fee` were never emitted on a fill payload
312
+ * and are not declared here.
199
313
  */
200
314
  export interface OrderFilledEvent extends OrderEvent {
201
315
  eventType: "OrderFilled";
202
316
  data: CommonOrderEventData & {
203
- /** Taker order ID (taker orders only) */
204
- takerOrderId?: string;
205
- /** Maker order ID (maker orders only) */
206
- makerOrderId?: string;
207
317
  /** Reference price (taker orders only) */
208
318
  referencePrice?: string;
209
319
  /** Best price (taker orders only) */
@@ -216,14 +326,12 @@ export interface OrderFilledEvent extends OrderEvent {
216
326
  maxSlippageBps?: number;
217
327
  /** Number of trades (taker orders only - not present for maker) */
218
328
  tradesCount?: number;
219
- /** Total filled quantity (taker orders only - not present for maker) */
220
- totalFilled?: string;
221
329
  /** Remaining quantity (present for both, should be 0 for filled orders) */
222
330
  remainingQuantity?: string;
223
331
  /** Average fill price (taker orders only) */
224
332
  averageFillPrice?: string;
225
333
  /** Order status (taker orders only) */
226
- status?: OrderStatus;
334
+ status?: OrderStatus | (string & {});
227
335
  /** Trade ID (maker orders only) */
228
336
  tradeId?: string;
229
337
  /** Execution price (maker orders only) */
@@ -249,14 +357,27 @@ export interface OrderFilledEvent extends OrderEvent {
249
357
  /** `remainingQuantity` in RAW (integer, base-unit) precision. Wire: `remaining_quantity_raw`. */
250
358
  remainingQuantityRaw?: string;
251
359
  /**
252
- * Fee charged for this fill (maker orders only). The wire field is `fee`
253
- * (→ `fee`), not `makerFee`; taker fills carry the same `fee` field.
360
+ * Monaco fee for the fills this event covers, in normalized decimal QUOTE
361
+ * units (negative = maker rebate). The wire field is `fee`, not `makerFee`;
362
+ * taker and maker carry the same field. A taker event aggregates every
363
+ * trade it covers; a maker event is one trade.
364
+ *
365
+ * Excludes the application taker fee, so it is NOT the order echo's
366
+ * `totalTakerFees` (which is `fee + applicationTakerFee`). It previously
367
+ * held the raw integer value now exposed as `feeRaw`.
254
368
  */
255
369
  fee?: string;
370
+ /**
371
+ * `fee` in RAW quote-unit precision (scaled by 10^quoteDecimals). Always a
372
+ * whole number -- the engine truncates every fee to an integral raw unit
373
+ * when it stores it -- so `fee` is exactly this value divided by
374
+ * 10^quoteDecimals, and `BigInt(feeRaw)` is safe. Wire: `fee_raw`.
375
+ */
376
+ feeRaw?: string;
256
377
  /** Trade execution timestamp (maker orders only) */
257
378
  executedAt?: string;
258
379
  /** Role of the order in the trade (taker or maker) */
259
- role?: OrderRole;
380
+ role?: OrderRole | (string & {});
260
381
  };
261
382
  }
262
383
  /**
@@ -268,7 +389,7 @@ export interface OrderCancelledEvent extends OrderEvent {
268
389
  /** Reason for cancellation */
269
390
  reason?: string;
270
391
  /** Stable machine-readable cancellation cause */
271
- terminalReason?: OrderTerminalReason;
392
+ terminalReason?: OrderTerminalReason | (string & {});
272
393
  /**
273
394
  * Cumulative order fill at cancellation, mirroring `filledQuantity` on the
274
395
  * taker/maker fill events so consumers read one field across every order
@@ -308,11 +429,15 @@ export interface OrderRejectedEvent extends OrderEvent {
308
429
  /** Reason for rejection */
309
430
  reason: string;
310
431
  /** Order status */
311
- status: OrderStatus;
432
+ status: OrderStatus | (string & {});
312
433
  /** Total filled quantity (may-be partially filled) */
313
434
  totalFilled: string;
435
+ /** `totalFilled` in RAW (integer, base-unit) precision. Wire: `total_filled_raw`. */
436
+ totalFilledRaw: string;
314
437
  /** Remaining quantity that couldn't be filled */
315
438
  remainingQuantity: string;
439
+ /** `remainingQuantity` in RAW (integer, base-unit) precision. Wire: `remaining_quantity_raw`. */
440
+ remainingQuantityRaw: string;
316
441
  };
317
442
  }
318
443
  /**
@@ -321,12 +446,16 @@ export interface OrderRejectedEvent extends OrderEvent {
321
446
  export interface OrderExpiredEvent extends OrderEvent {
322
447
  eventType: "OrderExpired";
323
448
  data: CommonOrderEventData & {
324
- /** Filled quantity before expiration */
325
- filledQuantity?: string;
326
- /** Remaining quantity at expiration */
327
- remainingQuantity?: string;
328
- /** Average fill price (if any fills occurred) */
329
- averageFillPrice?: string;
449
+ /** Filled quantity before expiration. `null` when the order never filled. */
450
+ filledQuantity: string | null;
451
+ /** `filledQuantity` in RAW (integer, base-unit) precision. Wire: `filled_quantity_raw`. */
452
+ filledQuantityRaw: string | null;
453
+ /** Remaining quantity at expiration. `null` when unset. */
454
+ remainingQuantity: string | null;
455
+ /** `remainingQuantity` in RAW (integer, base-unit) precision. Wire: `remaining_quantity_raw`. */
456
+ remainingQuantityRaw: string | null;
457
+ /** Average fill price. `null` when no fills occurred. */
458
+ averageFillPrice: string | null;
330
459
  /** Reason for expiration */
331
460
  reason: string;
332
461
  };
@@ -342,7 +471,7 @@ export interface OrderPartiallyCancelledEvent extends OrderEvent {
342
471
  eventType: "OrderPartiallyCancelled";
343
472
  data: CommonOrderEventData & {
344
473
  /** Order status — non-terminal (the order still rests) */
345
- status: OrderStatus;
474
+ status: OrderStatus | (string & {});
346
475
  /** Cumulative filled quantity, unchanged by the reduction */
347
476
  filledQuantity: string;
348
477
  /** Cumulative filled quantity in RAW precision. Wire: `filled_quantity_raw`. */
@@ -23,6 +23,12 @@ export type PositionEventSide = "LONG" | "SHORT";
23
23
  export type PositionEventStatus = "OPEN" | "LIQUIDATING" | "CLOSED" | "LIQUIDATED" | "EXPIRED";
24
24
  /**
25
25
  * Position event data containing the current state of a single position.
26
+ *
27
+ * `side` and `status` are typed `Union | (string & {})`: `@0xmonaco/core`
28
+ * parses this frame leniently, so an unrecognized value is passed through
29
+ * rather than throwing inside the WebSocket message handler and dropping a
30
+ * risk-critical update entirely (0XM-2627/0XM-2628). The unions name the values
31
+ * the server sends today — `switch` over them with a `default` branch.
26
32
  */
27
33
  export interface PositionEventData {
28
34
  /** Position identifier (UUID) */
@@ -32,9 +38,9 @@ export interface PositionEventData {
32
38
  /** Trading pair identifier (UUID) */
33
39
  tradingPairId: string;
34
40
  /** Position side */
35
- side: PositionEventSide;
41
+ side: PositionEventSide | (string & {});
36
42
  /** Position lifecycle status (SCREAMING_SNAKE_CASE on the wire, e.g. "OPEN") */
37
- status: PositionEventStatus;
43
+ status: PositionEventStatus | (string & {});
38
44
  /** Absolute position size (decimal string) */
39
45
  size: string;
40
46
  /** Average entry price (decimal string) */
@@ -15,7 +15,7 @@ export interface TradeEventData {
15
15
  /** Executed quantity */
16
16
  quantity: string;
17
17
  /** Side of the maker order (BUY or SELL) */
18
- makerSide: OrderSide;
18
+ makerSide: OrderSide | (string & {});
19
19
  /** Timestamp when the trade was executed (ISO 8601) */
20
20
  executedAt: string;
21
21
  }
@@ -28,7 +28,7 @@ export interface TradeEvent {
28
28
  /** Trading pair ID (UUID) */
29
29
  tradingPairId: string;
30
30
  /** Trading mode (SPOT or MARGIN) */
31
- tradingMode: TradingMode;
31
+ tradingMode: TradingMode | (string & {});
32
32
  /** Trade execution data */
33
33
  data: TradeEventData;
34
34
  }
@@ -18,12 +18,20 @@ export type TwapOrderEventReason = "created" | "slice_placed" | "slice_skipped"
18
18
  * Field names mirror the backend `TwapOrderEventData` payload (snake_case on
19
19
  * the wire, camelCased by the client); the SDK parser normalizes explicit
20
20
  * `null` values to absent, so nullable wire fields are optional here.
21
+ *
22
+ * **The value unions below are the values the server sends today, not a
23
+ * runtime guarantee**, which is why each is typed `Union | (string & {})`:
24
+ * `@0xmonaco/core` hands this whole payload through with a single cast and
25
+ * validates no value field, so an unrecognized `side`, `tradingMode`,
26
+ * `executionStyle`, `state`, `triggerDirection` or `reason` arrives as-is
27
+ * rather than dropping the update (0XM-2628). Autocomplete still offers the
28
+ * known values; `switch` over one with a `default` branch.
21
29
  */
22
30
  export interface TwapOrderEventData {
23
31
  twapOrderId: string;
24
32
  tradingPairId: string;
25
- side: OrderSide;
26
- tradingMode: TradingMode;
33
+ side: OrderSide | (string & {});
34
+ tradingMode: TradingMode | (string & {});
27
35
  totalQuantity: string;
28
36
  executedQuantity: string;
29
37
  executedNotional: string;
@@ -41,7 +49,7 @@ export interface TwapOrderEventData {
41
49
  clientTwapId?: string;
42
50
  randomize: boolean;
43
51
  /** Child execution style: "TAKER" (banded MARKET slices) or "PASSIVE" (post-only children at the touch, taker sweep at each slice deadline) */
44
- executionStyle: TwapExecutionStyle;
52
+ executionStyle: TwapExecutionStyle | (string & {});
45
53
  /** Window start (ISO 8601) */
46
54
  startAt: string;
47
55
  /** Window end (ISO 8601). Execution stops hard at this time. */
@@ -55,13 +63,23 @@ export interface TwapOrderEventData {
55
63
  nextSliceAt?: string;
56
64
  slicesPlaced: number;
57
65
  slicesSkipped: number;
58
- state: TwapOrderState;
66
+ /**
67
+ * Lifecycle state of the parent order.
68
+ *
69
+ * Widened past {@link TwapOrderState} on purpose: `@0xmonaco/core` parses
70
+ * this frame leniently, so a state added server-side after this SDK version
71
+ * ships is delivered as-is rather than dropping the whole update (0XM-2628).
72
+ * `TwapOrderState` names the values the server sends today — narrow with a
73
+ * `switch` that keeps a `default` branch rather than relying on
74
+ * exhaustiveness.
75
+ */
76
+ state: TwapOrderState | (string & {});
59
77
  /** Terminal annotation, e.g. "ended with shortfall 1.5"; absent for non-terminal parents */
60
78
  terminalReason?: string;
61
79
  /** Conditional configuration + activation stamp (parity with the REST read
62
80
  * model). All absent on unconditional parents. */
63
81
  triggerPrice?: string;
64
- triggerDirection?: "ABOVE" | "BELOW";
82
+ triggerDirection?: "ABOVE" | "BELOW" | (string & {});
65
83
  conditionalExpiry?: string;
66
84
  triggeredAt?: string;
67
85
  createdAt: string;
@@ -69,7 +87,7 @@ export interface TwapOrderEventData {
69
87
  cancelledAt?: string;
70
88
  completedAt?: string;
71
89
  /** Mutation that produced this snapshot */
72
- reason: TwapOrderEventReason;
90
+ reason: TwapOrderEventReason | (string & {});
73
91
  }
74
92
  export interface TwapOrderEvent {
75
93
  eventType: "twap_order_update";
@@ -3167,6 +3167,12 @@ export interface components {
3167
3167
  parentOrderId?: string | null;
3168
3168
  associationType?: string | null;
3169
3169
  activatedAt?: string | null;
3170
+ /**
3171
+ * Format: int64
3172
+ * @description Opaque, compare-only revision counter for this conditional order's state: the sequencer step that wrote it. A strictly higher value is strictly newer state for that conditional order. The SAME counter is carried on the `conditional_orders` WebSocket SNAPSHOT frame — but deliberately NOT on the live `conditional_orders` events, which build from the row as it stood before the sequencer acted and so could only report the version the row held BEFORE the change they announce. So seed conditional state from the snapshot frame and REST, reconciling on `(conditionalOrderId, version)` — take the REST row when `snapshot.version >= local.version`, else keep local state — and apply the live events as state without ranking them. Note `>=`, not `>`: equal versions mean the SAME STEP rather than identical state, because one step can write several conditional rows (a trigger writes the triggered row and cancels its OCO sibling under one step). A REST row is that step's FINAL state, so it wins a tie. One consequence to plan for: the snapshot is read from the database, which lags the matching engine, so a transition that fires during the read is in neither the snapshot nor the delivered stream and cannot be ranked — re-read the affected rows if a conditional order's state matters at reconnect. Do not read anything else into the number: it is drawn from the same global sequence plain orders use, so values are sparse, gaps are normal, and it is not comparable across different conditional orders — nor against a plain order's version. Absent means unknown: a row last written before this field existed. Fall back to a full field comparison for those rather than treating absent as version zero.
3173
+ * @example 918342
3174
+ */
3175
+ version?: number | null;
3170
3176
  };
3171
3177
  CreateDelegatedSessionRequest: {
3172
3178
  /**
@@ -4527,6 +4533,12 @@ export interface components {
4527
4533
  * @example false
4528
4534
  */
4529
4535
  postOnly?: boolean | null;
4536
+ /**
4537
+ * Format: int64
4538
+ * @description Opaque, compare-only revision counter for this order's state: the sequencer step that wrote it. A strictly higher value is strictly newer state for that order. The SAME counter is carried on every order WebSocket event and on the `orders` snapshot frame, so a reconnecting client reconciles on `(id, version)` — take the REST row when `snapshot.version >= local.version`, else keep local state. Note `>=`, not `>`: on the stream, equal versions mean the SAME STEP rather than identical state, because one step emits several events for one order (an `OrderPlaced` and then its fills) and a batch shares one step across every item. A REST row is that step's FINAL state, so it wins a tie. Between two REST reads the comparison is simpler — equal there does mean identical state. Do not read anything else into the number: it is not a count of updates, consecutive values are not expected (it is drawn from a global sequence shared by every order, so per-order values are sparse and gaps are normal), and it is not comparable across different orders. Absent means unknown: an order last written before this field existed. Fall back to a full field comparison for those rather than treating absent as version zero — and note that a comparison against an absent value is false in both directions, so an order held locally without one is never refreshed by the merge above; take the REST row unconditionally for those. Reconciling on this counter does NOT on its own close a reconnect gap: an order that filled or was cancelled while the socket was down appears in neither the resting snapshot nor the stream, so recover fills from `listUserTrades` (ordered by execution time) and resolve anything your local state believes is resting that the snapshot no longer lists with a targeted `getOrder`. Unlike `updatedAt`, this value is identical on every REST endpoint that serves the same order state, and never advances without a state change.
4539
+ * @example 918342
4540
+ */
4541
+ version?: number | null;
4530
4542
  };
4531
4543
  /** @description Orderbook snapshot with token metadata and sequence number. */
4532
4544
  GetOrderbookResponse: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/types",
3
- "version": "1.0.50",
3
+ "version": "1.0.51",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.0xmonaco.com/sdk/typescript",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,7 @@
16
16
  "lint": "biome lint ."
17
17
  },
18
18
  "dependencies": {
19
- "@0xmonaco/contracts": "1.0.50",
19
+ "@0xmonaco/contracts": "1.0.51",
20
20
  "zod": "^4.1.12"
21
21
  },
22
22
  "peerDependencies": {