@imbingox/acex 0.4.0-beta.16 → 0.4.0-beta.18

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.
@@ -42,14 +42,178 @@ export interface RateLimitUsage {
42
42
  orderCount?: Record<string, number>;
43
43
  }
44
44
 
45
+ /**
46
+ * Request priority used by rate-limit plans and per-request contexts.
47
+ *
48
+ * `"normal"` is the default path. `"cancel"` is intended for cancellation or
49
+ * other unwind traffic that may use reserved bucket headroom. `"risk"` is for
50
+ * risk/account maintenance traffic. Custom strings are allowed so venue
51
+ * adapters can add narrower priorities without changing the public union.
52
+ */
53
+ export type RateLimitPriority = "normal" | "cancel" | "risk" | (string & {});
54
+
55
+ /**
56
+ * Logical bucket family reported by an exchange.
57
+ *
58
+ * `"request_weight"` tracks REST weight-style budgets. `"orders"` tracks order
59
+ * count budgets separately. Custom strings are allowed for venue-specific
60
+ * bucket families.
61
+ */
62
+ export type RateLimitBucketKind = "request_weight" | "orders" | (string & {});
63
+
64
+ /**
65
+ * Scope dimensions that define how a bucket state is keyed.
66
+ *
67
+ * `"venue"` shares one bucket per venue, `"account"` adds accountId isolation,
68
+ * and `"endpoint"` adds endpointKey isolation.
69
+ */
70
+ export type RateLimitScopeDimension = "venue" | "account" | "endpoint";
71
+
72
+ /** Priority-specific bucket headroom held back from other priorities. */
73
+ export interface RateLimitBucketReserve {
74
+ /** Priority that may consume the full published bucket limit. */
75
+ priority: RateLimitPriority;
76
+ /** Number of bucket units reserved from non-matching priorities. */
77
+ units: number;
78
+ }
79
+
80
+ /** Fixed-window budget bucket owned by a venue topology. */
81
+ export interface RateLimitBucketDescriptor {
82
+ /** Stable bucket id referenced by RateLimitCost.bucketId. */
83
+ id: string;
84
+ /** Exchange budget family, such as request weight or order count. */
85
+ kind: RateLimitBucketKind;
86
+ /** Published bucket capacity in the same units as RateLimitCost.cost. */
87
+ limit: number;
88
+ /** Fixed-window interval length in milliseconds. */
89
+ intervalMs: number;
90
+ /** Dimensions included when deriving the bucket state key. */
91
+ scope: readonly RateLimitScopeDimension[];
92
+ /**
93
+ * Fraction of limit the default limiter should normally target before
94
+ * reserve handling. Omitted means the limiter-wide default.
95
+ */
96
+ utilizationTarget?: number;
97
+ /**
98
+ * Optional priority reserve. Non-matching priorities use the target limit
99
+ * minus reserve.units, clamped to zero; the matching priority may use limit.
100
+ */
101
+ reserve?: RateLimitBucketReserve;
102
+ }
103
+
104
+ /** Cost of one plan against one bucket. */
105
+ export interface RateLimitCost {
106
+ /** Bucket id declared in the same topology. */
107
+ bucketId: string;
108
+ /** Units consumed in that bucket when this plan is admitted. */
109
+ cost: number;
110
+ }
111
+
112
+ /** Request admission plan selected by an adapter for a semantic operation. */
113
+ export interface RateLimitPlan {
114
+ /** Stable semantic plan id, not necessarily identical to endpointKey. */
115
+ id: string;
116
+ /** Bucket costs that must all be admitted atomically. */
117
+ costs: readonly RateLimitCost[];
118
+ /** Default priority for requests using this plan. */
119
+ priority?: RateLimitPriority;
120
+ }
121
+
122
+ /**
123
+ * Venue-owned rate-limit topology.
124
+ *
125
+ * Buckets describe fixed-window budgets. Plans map adapter operations to the
126
+ * bucket costs consumed by one request.
127
+ */
128
+ export interface RateLimitTopology {
129
+ /** Stable topology id, typically venue-scoped. */
130
+ id: string;
131
+ /** Bucket descriptors referenced by plans. */
132
+ buckets: readonly RateLimitBucketDescriptor[];
133
+ /** Operation plans that map requests to bucket costs. */
134
+ plans: readonly RateLimitPlan[];
135
+ }
136
+
137
+ /**
138
+ * Opaque admission token returned by RateLimiter.beforeRequest().
139
+ *
140
+ * Adapters must not inspect or construct this token. They pass it back through
141
+ * RateLimitResponseContext.reservation or RateLimitTransportErrorContext.
142
+ */
143
+ export interface RateLimitReservation {
144
+ readonly __opaqueRateLimitReservation?: never;
145
+ }
146
+
147
+ export interface RateLimitTopologyRegistry {
148
+ /**
149
+ * Registers a venue topology with the limiter.
150
+ *
151
+ * Re-registering an identical descriptor is idempotent. A conflicting bucket
152
+ * or plan descriptor should be rejected instead of overwritten.
153
+ */
154
+ registerRateLimitTopology(topology: RateLimitTopology): void;
155
+ }
156
+
157
+ /** Explicit no-token result for custom limiters that only wait or observe. */
158
+ // biome-ignore lint/suspicious/noConfusingVoidType: Existing custom limiters return void; the SPI must keep that source-compatible.
159
+ export type RateLimitNoReservation = void;
160
+
161
+ /**
162
+ * Return type for RateLimiter.beforeRequest().
163
+ *
164
+ * Return void/Promise<void> when no token is needed. Return a reservation when
165
+ * later response/error hooks need to reconcile the admitted request.
166
+ */
167
+ export type RateLimitBeforeRequestResult =
168
+ | Promise<RateLimitReservation | RateLimitNoReservation>
169
+ | RateLimitReservation
170
+ | RateLimitNoReservation;
171
+
172
+ /** Diagnostic snapshot for one registered bucket at the current scope. */
173
+ export interface RateLimitBucketSnapshot {
174
+ /** Bucket id from the registered descriptor. */
175
+ bucketId: string;
176
+ /** Bucket family from the registered descriptor. */
177
+ kind: RateLimitBucketKind;
178
+ /** Published bucket capacity. */
179
+ limit: number;
180
+ /** Fixed-window interval length in milliseconds. */
181
+ intervalMs: number;
182
+ /** Effective utilization target used for normal admission. */
183
+ utilizationTarget?: number;
184
+ /** Priority reserve copied from the descriptor, when configured. */
185
+ reserve?: RateLimitBucketReserve;
186
+ /** Units observed or pre-reserved in the active window. */
187
+ used?: number;
188
+ /** Active fixed-window start time as epoch milliseconds. */
189
+ windowStartMs?: number;
190
+ /** Active fixed-window end time as epoch milliseconds. */
191
+ windowEndMs?: number;
192
+ /** Epoch milliseconds until which this bucket is blocked. */
193
+ blockedUntil?: number;
194
+ /** Most recent Retry-After duration in milliseconds, when available. */
195
+ retryAfterMs?: number;
196
+ /** `"ok"` admits normally, `"rate_limited"` waits, `"banned"` is a ban. */
197
+ state: "ok" | "rate_limited" | "banned";
198
+ /** Last update time as epoch milliseconds. */
199
+ updatedAt?: number;
200
+ }
201
+
202
+ export interface RateLimitOptions {
203
+ utilizationTarget?: number;
204
+ }
205
+
45
206
  export interface RateLimitRequestContext {
46
207
  scope: RateLimitScope;
208
+ planId?: string;
209
+ priority?: RateLimitPriority;
47
210
  }
48
211
 
49
212
  export interface RateLimitResponseContext {
50
213
  status: number;
51
214
  headers?: Headers;
52
215
  usage?: RateLimitUsage;
216
+ reservation?: RateLimitReservation;
53
217
  }
54
218
 
55
219
  export interface RateLimitTransportErrorContext {
@@ -57,6 +221,8 @@ export interface RateLimitTransportErrorContext {
57
221
  headers?: Headers;
58
222
  retryAfterMs?: number;
59
223
  usage?: RateLimitUsage;
224
+ reservation?: RateLimitReservation;
225
+ requestNotSent?: boolean;
60
226
  }
61
227
 
62
228
  export interface RateLimitSnapshot {
@@ -66,10 +232,38 @@ export interface RateLimitSnapshot {
66
232
  retryAfterMs?: number;
67
233
  state: "ok" | "rate_limited" | "banned";
68
234
  updatedAt?: number;
235
+ buckets?: RateLimitBucketSnapshot[];
69
236
  }
70
237
 
71
238
  export interface RateLimiter {
72
- beforeRequest(ctx: RateLimitRequestContext): Promise<void> | void;
239
+ /**
240
+ * Waits for request admission and optionally returns an opaque reservation.
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * const reservations = new WeakMap<RateLimitReservation, { cost: number }>();
245
+ *
246
+ * const limiter: RateLimiter = {
247
+ * async beforeRequest() {
248
+ * const reservation: RateLimitReservation = {};
249
+ * reservations.set(reservation, { cost: 1 });
250
+ * return reservation;
251
+ * },
252
+ * afterResponse(_ctx, response) {
253
+ * if (response.reservation) reservations.delete(response.reservation);
254
+ * },
255
+ * onTransportError(_ctx, error) {
256
+ * if (error.reservation && error.requestNotSent) {
257
+ * reservations.delete(error.reservation);
258
+ * }
259
+ * },
260
+ * getSnapshot(scope) {
261
+ * return { scope, state: "ok" };
262
+ * },
263
+ * };
264
+ * ```
265
+ */
266
+ beforeRequest(ctx: RateLimitRequestContext): RateLimitBeforeRequestResult;
73
267
  afterResponse(
74
268
  ctx: RateLimitRequestContext,
75
269
  response: RateLimitResponseContext,
@@ -116,6 +310,7 @@ export interface CreateClientOptions {
116
310
  /** Request/signing clock; local receivedAt/freshness clocks stay independent. */
117
311
  clock?: TimeProvider;
118
312
  rateLimiter?: RateLimiter;
313
+ rateLimit?: RateLimitOptions;
119
314
  logger?: Logger;
120
315
  logLevel?: LogLevel;
121
316
  market?: MarketRuntimeOptions;
@@ -175,11 +370,24 @@ export interface StopOptions {
175
370
  timeoutMs?: number;
176
371
  }
177
372
 
373
+ export type EventStreamMode = "buffer" | "conflate";
374
+
375
+ export interface EventStreamOptions {
376
+ mode?: EventStreamMode;
377
+ maxBuffer?: number;
378
+ }
379
+
380
+ export interface BufferedEventStreamOptions {
381
+ maxBuffer?: number;
382
+ }
383
+
178
384
  export interface AcexInternalError {
179
385
  source: "client" | "market" | "account" | "order" | "adapter" | "runtime";
180
386
  venue?: Venue;
181
387
  accountId?: string;
182
388
  symbol?: string;
389
+ stream?: string;
390
+ maxBuffer?: number;
183
391
  error: Error;
184
392
  ts: number;
185
393
  }