@0xinsider/mcp 2.13.0 → 2.14.2
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/README.md +10 -0
- package/dist/cli-lib.d.ts.map +1 -1
- package/dist/cli-lib.js +14 -10
- package/dist/cli-lib.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +37 -22
- package/dist/client.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/product-cli/index.d.ts.map +1 -1
- package/dist/product-cli/index.js +25 -7
- package/dist/product-cli/index.js.map +1 -1
- package/dist/product-cli/oauth.js +6 -6
- package/dist/product-cli/oauth.js.map +1 -1
- package/dist/product-cli/storage.d.ts +1 -1
- package/dist/product-cli/storage.js +3 -3
- package/dist/product-cli/storage.js.map +1 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/resources/index.js +4 -4
- package/dist/resources/index.js.map +1 -1
- package/dist/sdk/client.d.ts +32 -81
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/client.js +65 -28
- package/dist/sdk/client.js.map +1 -1
- package/dist/sdk/data-quality.d.ts +4 -4
- package/dist/sdk/data-quality.d.ts.map +1 -1
- package/dist/sdk/data-quality.js +1 -1
- package/dist/sdk/data-quality.js.map +1 -1
- package/dist/sdk/errors.d.ts +5 -6
- package/dist/sdk/errors.d.ts.map +1 -1
- package/dist/sdk/errors.js +4 -5
- package/dist/sdk/errors.js.map +1 -1
- package/dist/sdk/index.d.ts +5 -5
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +3 -3
- package/dist/sdk/index.js.map +1 -1
- package/dist/sdk/pagination.d.ts +3 -3
- package/dist/sdk/pagination.d.ts.map +1 -1
- package/dist/sdk/pagination.js +3 -3
- package/dist/sdk/pagination.js.map +1 -1
- package/dist/sdk/retry.js +4 -2
- package/dist/sdk/retry.js.map +1 -1
- package/dist/sdk/schema.d.ts +242 -113
- package/dist/sdk/schema.d.ts.map +1 -1
- package/dist/sdk/stream.d.ts +28 -8
- package/dist/sdk/stream.d.ts.map +1 -1
- package/dist/sdk/stream.js +277 -76
- package/dist/sdk/stream.js.map +1 -1
- package/dist/sdk/webhooks.d.ts +14 -6
- package/dist/sdk/webhooks.d.ts.map +1 -1
- package/dist/sdk/webhooks.js +11 -3
- package/dist/sdk/webhooks.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/explore-markets.d.ts +7 -7
- package/dist/tools/explore-markets.d.ts.map +1 -1
- package/dist/tools/get-large-positions.d.ts +6 -6
- package/dist/tools/get-large-positions.d.ts.map +1 -1
- package/dist/tools/get-large-positions.js +2 -4
- package/dist/tools/get-large-positions.js.map +1 -1
- package/dist/tools/get-leaderboard.d.ts.map +1 -1
- package/dist/tools/get-leaderboard.js +7 -1
- package/dist/tools/get-leaderboard.js.map +1 -1
- package/dist/tools/get-position-timeline.d.ts +4 -4
- package/dist/tools/get-position-timeline.d.ts.map +1 -1
- package/dist/tools/get-position-timeline.js +2 -4
- package/dist/tools/get-position-timeline.js.map +1 -1
- package/dist/tools/get-positions.d.ts +8 -8
- package/dist/tools/get-positions.d.ts.map +1 -1
- package/dist/tools/get-trending-wallets.d.ts +3 -3
- package/dist/tools/get-trending-wallets.d.ts.map +1 -1
- package/dist/tools/get-trending-wallets.js +2 -4
- package/dist/tools/get-trending-wallets.js.map +1 -1
- package/dist/tools/get-whale-trades.d.ts.map +1 -1
- package/dist/tools/get-whale-trades.js +2 -4
- package/dist/tools/get-whale-trades.js.map +1 -1
- package/dist/tools/pick-of-the-day.js +3 -3
- package/dist/tools/read-parity.js +2 -2
- package/dist/tools/read-parity.js.map +1 -1
- package/dist/tools/result.d.ts +5 -1
- package/dist/tools/result.d.ts.map +1 -1
- package/dist/tools/result.js +11 -7
- package/dist/tools/result.js.map +1 -1
- package/package.json +7 -3
package/dist/sdk/stream.d.ts
CHANGED
|
@@ -85,7 +85,7 @@ export interface StreamOptions extends StreamFilters {
|
|
|
85
85
|
* remains valid across replicas and process restarts while retained. When
|
|
86
86
|
* omitted the stream starts from the live head.
|
|
87
87
|
*/
|
|
88
|
-
lastEventId?: number | string;
|
|
88
|
+
lastEventId?: number | string | undefined;
|
|
89
89
|
/** Cooperative cancellation; abort to close the stream. */
|
|
90
90
|
signal?: AbortSignal;
|
|
91
91
|
/** Invoked once for the resync marker frame, if one is emitted. */
|
|
@@ -104,7 +104,7 @@ export interface StreamOptions extends StreamFilters {
|
|
|
104
104
|
* transport progress.
|
|
105
105
|
*/
|
|
106
106
|
cursor?: {
|
|
107
|
-
seq?: number;
|
|
107
|
+
seq?: number | undefined;
|
|
108
108
|
};
|
|
109
109
|
/**
|
|
110
110
|
* Byte ceiling for one undelivered frame (#16248). Default
|
|
@@ -115,9 +115,28 @@ export interface StreamOptions extends StreamFilters {
|
|
|
115
115
|
* A positive finite integer; the largest real frame is a few KB.
|
|
116
116
|
*/
|
|
117
117
|
maxFrameBytes?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Deadline for response headers and a finite refusal body, per connection.
|
|
120
|
+
* Default `DEFAULT_TIMEOUT_MS` (15 seconds); `null` explicitly disables it.
|
|
121
|
+
* Cleared once a readable `text/event-stream` response is established.
|
|
122
|
+
* Independent of the client's REST `timeoutMs` and the stream's lifetime.
|
|
123
|
+
*/
|
|
124
|
+
openTimeoutMs?: number | null;
|
|
125
|
+
/** Maximum refusal body bytes retained before cancellation. Default 64 KiB. */
|
|
126
|
+
maxErrorBodyBytes?: number;
|
|
118
127
|
}
|
|
119
128
|
/** Default for `StreamOptions.maxFrameBytes`: 1 MiB. */
|
|
120
129
|
export declare const DEFAULT_MAX_STREAM_FRAME_BYTES = 1048576;
|
|
130
|
+
/** Default for `StreamOptions.maxErrorBodyBytes`: 64 KiB. */
|
|
131
|
+
export declare const DEFAULT_MAX_STREAM_ERROR_BODY_BYTES = 65536;
|
|
132
|
+
/** A refusal's HTTP error retains this diagnostic as `cause`, without raw text. */
|
|
133
|
+
export type StreamRefusalBodyErrorReason = "absent" | "empty" | "invalid_json" | "too_large" | "deadline_exceeded" | "read_failed";
|
|
134
|
+
export declare class StreamRefusalBodyError extends Error {
|
|
135
|
+
readonly reason: StreamRefusalBodyErrorReason;
|
|
136
|
+
readonly bytes: number;
|
|
137
|
+
readonly maxBytes: number;
|
|
138
|
+
constructor(reason: StreamRefusalBodyErrorReason, bytes: number, maxBytes: number, cause?: unknown);
|
|
139
|
+
}
|
|
121
140
|
/** What the stream did that the SSE contract does not allow. */
|
|
122
141
|
export type StreamProtocolErrorReason =
|
|
123
142
|
/** A 2xx whose `Content-Type` is not `text/event-stream`. */
|
|
@@ -156,8 +175,8 @@ export declare class StreamProtocolError extends Error {
|
|
|
156
175
|
readonly mediaType: string | undefined;
|
|
157
176
|
constructor(reason: StreamProtocolErrorReason, detail: {
|
|
158
177
|
lastSeq: number | undefined;
|
|
159
|
-
frameId?: number;
|
|
160
|
-
event?: string;
|
|
178
|
+
frameId?: number | undefined;
|
|
179
|
+
event?: string | undefined;
|
|
161
180
|
bytes?: number;
|
|
162
181
|
mediaType?: string | null;
|
|
163
182
|
});
|
|
@@ -179,7 +198,7 @@ export type StreamEvent = {
|
|
|
179
198
|
* `StreamEvent`: `kind: "event"` carries the `FeedEnvelope`, `kind: "resync"`
|
|
180
199
|
* carries the `ResyncMarker`.
|
|
181
200
|
*
|
|
182
|
-
* Pass `options.cursor` (a `{ seq?: number }` object) to have the last
|
|
201
|
+
* Pass `options.cursor` (a `{ seq?: number | undefined }` object) to have the last
|
|
183
202
|
* delivered `seq` written back as frames arrive; reconnect with
|
|
184
203
|
* `lastEventId: cursor.seq` to resume after a transport drop.
|
|
185
204
|
*
|
|
@@ -274,7 +293,8 @@ export declare class StreamRetryDeferredError extends Error {
|
|
|
274
293
|
* - Aborting `signal` ends the iterator without throwing, during a connection
|
|
275
294
|
* or a backoff. Each connection's body reader is cancelled when it ends, so
|
|
276
295
|
* reconnects do not accumulate sockets.
|
|
277
|
-
* -
|
|
296
|
+
* - `openTimeoutMs` bounds only opening and refusal reads; healthy streams
|
|
297
|
+
* have no lifetime deadline, as with `streamFeed`.
|
|
278
298
|
*
|
|
279
299
|
* `options.cursor`, when passed, tracks the last delivered seq across every
|
|
280
300
|
* connection -- delivered, not processed (#16247). This loop resumes from it,
|
|
@@ -495,8 +515,8 @@ export interface CheckpointedStreamHandlers {
|
|
|
495
515
|
*/
|
|
496
516
|
export declare function consumeStreamCheckpointed(client: OxinsiderApiClient, handlers: CheckpointedStreamHandlers, options?: CheckpointedStreamOptions): Promise<void>;
|
|
497
517
|
export interface ParsedSseFrame {
|
|
498
|
-
event?: string;
|
|
499
|
-
id?: number;
|
|
518
|
+
event?: string | undefined;
|
|
519
|
+
id?: number | undefined;
|
|
500
520
|
data: string;
|
|
501
521
|
}
|
|
502
522
|
/** Parse one SSE frame (without the trailing blank line). */
|
package/dist/sdk/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../sdk/src/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../sdk/src/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,EAGL,KAAK,KAAK,EACV,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAYrB,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,mEAAmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,YAAY,CAAC,EAAE;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,SAAS,CAAC,EAAE,KAAK,CAAC;CACnB;AAED,MAAM,WAAW,aAAc,SAAQ,aAAa;IAClD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1C,2DAA2D;IAC3D,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,mEAAmE;IACnE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC1C;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACtC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wDAAwD;AACxD,eAAO,MAAM,8BAA8B,UAAY,CAAC;AAExD,6DAA6D;AAC7D,eAAO,MAAM,mCAAmC,QAAS,CAAC;AAE1D,mFAAmF;AACnF,MAAM,MAAM,4BAA4B,GACpC,QAAQ,GACR,OAAO,GACP,cAAc,GACd,WAAW,GACX,mBAAmB,GACnB,aAAa,CAAC;AAElB,qBAAa,sBAAuB,SAAQ,KAAK;IAE7C,QAAQ,CAAC,MAAM,EAAE,4BAA4B;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAH3B,YACW,MAAM,EAAE,4BAA4B,EACpC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EACzB,KAAK,CAAC,EAAE,OAAO,EAOhB;CACF;AAED,gEAAgE;AAChE,MAAM,MAAM,yBAAyB;AACnC,6DAA6D;AAC3D,uBAAuB;AACzB,8CAA8C;GAC5C,cAAc;AAChB,qFAAqF;GACnF,kBAAkB;AACpB,gFAAgF;GAC9E,mBAAmB;AACrB,sFAAsF;GACpF,gBAAgB;AAClB,iEAAiE;GAC/D,iBAAiB,CAAC;AAEtB;;;;;;;;;;;;;;GAcG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,yBAAyB,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAEvC,YACE,MAAM,EAAE,yBAAyB,EACjC,MAAM,EAAE;QACN,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;QAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,EAUF;CACF;AAgQD,6FAA6F;AAC7F,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAI1E;AAED,iDAAiD;AACjD,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC;AAEjE;;;;;;;;;;;;;GAaG;AACH,wBAAuB,UAAU,CAC/B,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,aAAkB,GAC1B,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,CAoL9C;AAED,MAAM,WAAW,sBAAuB,SAAQ,aAAa;IAC3D;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,KAAK,EAAE,OAAO,KACX,IAAI,CAAC;IACV;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,0DAA0D;AAC1D,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,4DAA4D;AAC5D,eAAO,MAAM,iCAAiC,QAAyB,CAAC;AAMxE;;;;GAIG;AACH,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YAAY,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,EAQxE;CACF;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,YACE,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EAYf;CACF;AAsED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAuB,mBAAmB,CACxC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,GAAE,sBAA2B,GACnC,cAAc,CAAC,WAAW,EAAE,IAAI,EAAE,SAAS,CAAC,CAqE9C;AAQD;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE;IACR,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E,EACD,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,+EAA+E;AAC/E,MAAM,MAAM,sBAAsB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAExD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,CAAC;AAEnE,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,mCAAmC;IACnC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,uEAAuE;IACvE,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAExC,YACE,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;QACxB,KAAK,EAAE,kBAAkB,CAAC;QAC1B,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;QAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;KAChC,EACD,KAAK,EAAE,OAAO,EAgBf;CACF;AAED,iEAAiE;AACjE,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,MAAM,WAAW,yBAA0B,SAAQ,sBAAsB;IACvE;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;CAC1E;AAED,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E;;;;OAIG;IACH,YAAY,CAAC,EAAE,CACb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,sBAAsB,KAC3B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,0BAA0B,EACpC,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,IAAI,CAAC,CAwIf;AAqBD,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CA0ChE;AA8BD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,WAAW,CA2Cb"}
|
package/dist/sdk/stream.js
CHANGED
|
@@ -45,11 +45,25 @@
|
|
|
45
45
|
* Comment lines (`: keep-alive`), LF and CRLF framing, unknown SSE fields
|
|
46
46
|
* and unknown-but-valid envelope `type`s are compatible as before.
|
|
47
47
|
*/
|
|
48
|
-
import { resolveApiUrl } from "./client.js";
|
|
49
|
-
import { errorFromResponse, OxinsiderApiError } from "./errors.js";
|
|
48
|
+
import { DEFAULT_TIMEOUT_MS, resolveApiUrl, } from "./client.js";
|
|
49
|
+
import { errorFromResponse, OxinsiderApiError, RequestTimeoutError, } from "./errors.js";
|
|
50
50
|
import { RETRY_AFTER_CEILING_MS, retryAfterSeconds, waitUnlessAborted, } from "./retry.js";
|
|
51
51
|
/** Default for `StreamOptions.maxFrameBytes`: 1 MiB. */
|
|
52
52
|
export const DEFAULT_MAX_STREAM_FRAME_BYTES = 1_048_576;
|
|
53
|
+
/** Default for `StreamOptions.maxErrorBodyBytes`: 64 KiB. */
|
|
54
|
+
export const DEFAULT_MAX_STREAM_ERROR_BODY_BYTES = 65_536;
|
|
55
|
+
export class StreamRefusalBodyError extends Error {
|
|
56
|
+
reason;
|
|
57
|
+
bytes;
|
|
58
|
+
maxBytes;
|
|
59
|
+
constructor(reason, bytes, maxBytes, cause) {
|
|
60
|
+
super(`0xinsider stream refusal body ${reason} (${String(bytes)} bytes read; limit ${String(maxBytes)})`, { cause });
|
|
61
|
+
this.reason = reason;
|
|
62
|
+
this.bytes = bytes;
|
|
63
|
+
this.maxBytes = maxBytes;
|
|
64
|
+
this.name = "StreamRefusalBodyError";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
53
67
|
/**
|
|
54
68
|
* The stream broke the SSE contract (#16248). The connection is closed and
|
|
55
69
|
* the reader released before this is thrown. `lastSeq` is the last sequence
|
|
@@ -113,6 +127,163 @@ function assertMaxFrameBytes(value) {
|
|
|
113
127
|
}
|
|
114
128
|
return value;
|
|
115
129
|
}
|
|
130
|
+
/** Validate transport options before a reconnect can spend an attempt. */
|
|
131
|
+
function streamConnectionOptions(options) {
|
|
132
|
+
const maxFrameBytes = assertMaxFrameBytes(options.maxFrameBytes ?? DEFAULT_MAX_STREAM_FRAME_BYTES);
|
|
133
|
+
const maxErrorBodyBytes = options.maxErrorBodyBytes ?? DEFAULT_MAX_STREAM_ERROR_BODY_BYTES;
|
|
134
|
+
if (!Number.isSafeInteger(maxErrorBodyBytes) || maxErrorBodyBytes < 1) {
|
|
135
|
+
throw new Error(`maxErrorBodyBytes must be a positive safe integer, got ${String(maxErrorBodyBytes)}`);
|
|
136
|
+
}
|
|
137
|
+
const openTimeoutMs = options.openTimeoutMs === undefined
|
|
138
|
+
? DEFAULT_TIMEOUT_MS
|
|
139
|
+
: options.openTimeoutMs;
|
|
140
|
+
// Node's timer contract clamps larger values to 1 ms. Reject rather than
|
|
141
|
+
// making a requested long opening window expire immediately.
|
|
142
|
+
if (openTimeoutMs !== null &&
|
|
143
|
+
(!Number.isInteger(openTimeoutMs) ||
|
|
144
|
+
openTimeoutMs < 1 ||
|
|
145
|
+
openTimeoutMs > 2_147_483_647)) {
|
|
146
|
+
throw new Error(`openTimeoutMs must be an integer from 1 to 2147483647 ms or null, got ${String(openTimeoutMs)}`);
|
|
147
|
+
}
|
|
148
|
+
return { maxFrameBytes, maxErrorBodyBytes, openTimeoutMs };
|
|
149
|
+
}
|
|
150
|
+
/** Clear only the owned opening timer; caller cancellation lives until disposal. */
|
|
151
|
+
function streamConnectionSignal(timeoutMs, caller) {
|
|
152
|
+
const controller = new AbortController();
|
|
153
|
+
let timer;
|
|
154
|
+
const established = () => {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
timer = undefined;
|
|
157
|
+
};
|
|
158
|
+
const onCallerAbort = () => {
|
|
159
|
+
established();
|
|
160
|
+
controller.abort(caller?.reason);
|
|
161
|
+
};
|
|
162
|
+
// Manual forwarding supports every Node >=18, including versions without
|
|
163
|
+
// AbortSignal.any, and allows both sources to be disposed explicitly.
|
|
164
|
+
if (caller?.aborted)
|
|
165
|
+
onCallerAbort();
|
|
166
|
+
else
|
|
167
|
+
caller?.addEventListener("abort", onCallerAbort, { once: true });
|
|
168
|
+
if (!controller.signal.aborted && timeoutMs !== null) {
|
|
169
|
+
timer = setTimeout(() => controller.abort(new RequestTimeoutError("getStream", timeoutMs)), timeoutMs);
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
signal: controller.signal,
|
|
173
|
+
established,
|
|
174
|
+
dispose: () => {
|
|
175
|
+
established();
|
|
176
|
+
caller?.removeEventListener("abort", onCallerAbort);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/** Bound injected readers too: their stream may not observe fetch's signal. */
|
|
181
|
+
async function streamWork(work, signal) {
|
|
182
|
+
let onAbort;
|
|
183
|
+
const aborted = new Promise((_, reject) => {
|
|
184
|
+
onAbort = () => {
|
|
185
|
+
const reason = signal.reason;
|
|
186
|
+
reject(reason);
|
|
187
|
+
};
|
|
188
|
+
if (signal.aborted)
|
|
189
|
+
onAbort();
|
|
190
|
+
else
|
|
191
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
192
|
+
});
|
|
193
|
+
try {
|
|
194
|
+
return await Promise.race([aborted, work]);
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
if (onAbort)
|
|
198
|
+
signal.removeEventListener("abort", onAbort);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const STREAM_CLEANUP_TIMEOUT_MS = 2_000;
|
|
202
|
+
/** A non-cooperative injected cancel cannot hold a reconnect indefinitely. */
|
|
203
|
+
async function cancelStreamBody(cancel) {
|
|
204
|
+
if (!cancel)
|
|
205
|
+
return undefined;
|
|
206
|
+
let timer;
|
|
207
|
+
const cancelled = Promise.resolve()
|
|
208
|
+
.then(cancel)
|
|
209
|
+
.then(() => undefined, (cause) => new Error("0xinsider stream body cancellation failed", { cause }));
|
|
210
|
+
const deadline = new Promise((resolve) => {
|
|
211
|
+
timer = setTimeout(() => resolve(new Error(`0xinsider stream body cancellation did not settle within ${String(STREAM_CLEANUP_TIMEOUT_MS)} ms; completion is unknown`)), STREAM_CLEANUP_TIMEOUT_MS);
|
|
212
|
+
});
|
|
213
|
+
try {
|
|
214
|
+
return await Promise.race([cancelled, deadline]);
|
|
215
|
+
}
|
|
216
|
+
finally {
|
|
217
|
+
clearTimeout(timer);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/** Preserve HTTP classification when cleanup also fails. */
|
|
221
|
+
function withStreamCleanupCause(primary, cleanup) {
|
|
222
|
+
if (primary instanceof Error) {
|
|
223
|
+
const cause = primary.cause;
|
|
224
|
+
try {
|
|
225
|
+
primary.cause =
|
|
226
|
+
cause === undefined
|
|
227
|
+
? cleanup
|
|
228
|
+
: new AggregateError([cause, cleanup], "Stream failure and body cleanup failure");
|
|
229
|
+
return primary;
|
|
230
|
+
}
|
|
231
|
+
catch (annotationFailure) {
|
|
232
|
+
return new AggregateError([primary, cleanup, annotationFailure], "Stream failure could not retain its cleanup diagnostic", { cause: primary });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return new AggregateError([primary, cleanup], "Stream failure and body cleanup failure", { cause: primary });
|
|
236
|
+
}
|
|
237
|
+
async function streamRefusal(response, reader, signal, maxBytes, caller) {
|
|
238
|
+
const retryAfter = retryAfterSeconds(response);
|
|
239
|
+
const requestId = response.headers.get("x-request-id");
|
|
240
|
+
let bytes = 0;
|
|
241
|
+
let body = null;
|
|
242
|
+
let diagnostic;
|
|
243
|
+
if (!reader)
|
|
244
|
+
diagnostic = new StreamRefusalBodyError("absent", bytes, maxBytes);
|
|
245
|
+
else {
|
|
246
|
+
const decoder = new TextDecoder();
|
|
247
|
+
let text = "";
|
|
248
|
+
try {
|
|
249
|
+
for (;;) {
|
|
250
|
+
const { value, done } = await streamWork(reader.read(), signal);
|
|
251
|
+
if (done)
|
|
252
|
+
break;
|
|
253
|
+
bytes += value.byteLength;
|
|
254
|
+
if (bytes > maxBytes) {
|
|
255
|
+
diagnostic = new StreamRefusalBodyError("too_large", bytes, maxBytes);
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
text += decoder.decode(value, { stream: true });
|
|
259
|
+
}
|
|
260
|
+
if (!diagnostic) {
|
|
261
|
+
text += decoder.decode();
|
|
262
|
+
if (text === "")
|
|
263
|
+
diagnostic = new StreamRefusalBodyError("empty", bytes, maxBytes);
|
|
264
|
+
else {
|
|
265
|
+
try {
|
|
266
|
+
body = JSON.parse(text);
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
diagnostic = new StreamRefusalBodyError("invalid_json", bytes, maxBytes);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch (cause) {
|
|
275
|
+
if (caller?.aborted)
|
|
276
|
+
throw caller.reason;
|
|
277
|
+
diagnostic = new StreamRefusalBodyError(signal.aborted ? "deadline_exceeded" : "read_failed", bytes, maxBytes, cause);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const error = errorFromResponse(response.status, body, retryAfter, {
|
|
281
|
+
requestId,
|
|
282
|
+
});
|
|
283
|
+
if (diagnostic)
|
|
284
|
+
error.cause = diagnostic;
|
|
285
|
+
return error;
|
|
286
|
+
}
|
|
116
287
|
/** `Content-Type` names an event stream: `text/event-stream`, with or without parameters. */
|
|
117
288
|
export function isEventStreamMediaType(contentType) {
|
|
118
289
|
if (contentType === null)
|
|
@@ -125,7 +296,7 @@ export function isEventStreamMediaType(contentType) {
|
|
|
125
296
|
* `StreamEvent`: `kind: "event"` carries the `FeedEnvelope`, `kind: "resync"`
|
|
126
297
|
* carries the `ResyncMarker`.
|
|
127
298
|
*
|
|
128
|
-
* Pass `options.cursor` (a `{ seq?: number }` object) to have the last
|
|
299
|
+
* Pass `options.cursor` (a `{ seq?: number | undefined }` object) to have the last
|
|
129
300
|
* delivered `seq` written back as frames arrive; reconnect with
|
|
130
301
|
* `lastEventId: cursor.seq` to resume after a transport drop.
|
|
131
302
|
*
|
|
@@ -135,7 +306,7 @@ export function isEventStreamMediaType(contentType) {
|
|
|
135
306
|
* }
|
|
136
307
|
*/
|
|
137
308
|
export async function* streamFeed(client, options = {}) {
|
|
138
|
-
const maxFrameBytes =
|
|
309
|
+
const { maxFrameBytes, maxErrorBodyBytes, openTimeoutMs } = streamConnectionOptions(options);
|
|
139
310
|
const apiKey = client.getApiKey();
|
|
140
311
|
// The sandbox takes no credential; it answers the stream route with a 400
|
|
141
312
|
// saying streams are not simulated, and that answer is the server's to
|
|
@@ -150,41 +321,73 @@ export async function* streamFeed(client, options = {}) {
|
|
|
150
321
|
if (options.lastEventId !== undefined) {
|
|
151
322
|
headers.set("last-event-id", String(options.lastEventId));
|
|
152
323
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
});
|
|
159
|
-
if (!response.ok) {
|
|
160
|
-
const body = await safeText(response);
|
|
161
|
-
throw errorFromResponse(response.status, tryParse(body), retryAfterSeconds(response), { requestId: response.headers.get("x-request-id") });
|
|
162
|
-
}
|
|
163
|
-
// A 2xx that is not an event stream (an HTML page from a proxy, a JSON body
|
|
164
|
-
// from a route that moved) used to read as an empty stream that closed
|
|
165
|
-
// cleanly, which the reconnect loop then retried as an outage (#16248).
|
|
166
|
-
const mediaType = response.headers.get("content-type");
|
|
167
|
-
if (!isEventStreamMediaType(mediaType)) {
|
|
168
|
-
await response.body?.cancel().catch(() => undefined);
|
|
169
|
-
throw new StreamProtocolError("unexpected_media_type", {
|
|
170
|
-
lastSeq: undefined,
|
|
171
|
-
mediaType,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
if (!response.body) {
|
|
175
|
-
throw new Error("Stream response has no readable body");
|
|
176
|
-
}
|
|
177
|
-
const reader = response.body.getReader();
|
|
178
|
-
const decoder = new TextDecoder();
|
|
179
|
-
const encoder = new TextEncoder();
|
|
180
|
-
let buffer = "";
|
|
181
|
-
// Bytes of `buffer`: exact, since every chunk adds its own byte length and
|
|
182
|
-
// a cut re-measures the remainder. Bounded by `maxFrameBytes`.
|
|
183
|
-
let bufferedBytes = 0;
|
|
184
|
-
let lastSeq;
|
|
324
|
+
const connection = streamConnectionSignal(openTimeoutMs, options.signal);
|
|
325
|
+
let response;
|
|
326
|
+
let reader;
|
|
327
|
+
let failure;
|
|
328
|
+
let failed = false;
|
|
185
329
|
try {
|
|
330
|
+
connection.signal.throwIfAborted();
|
|
331
|
+
const pendingResponse = client.getFetch()(url, {
|
|
332
|
+
method: "GET",
|
|
333
|
+
headers,
|
|
334
|
+
signal: connection.signal,
|
|
335
|
+
});
|
|
336
|
+
try {
|
|
337
|
+
response = await streamWork(pendingResponse, connection.signal);
|
|
338
|
+
}
|
|
339
|
+
catch (error) {
|
|
340
|
+
// An injected fetch may ignore abort. Observe its eventual response and
|
|
341
|
+
// cancel it instead of abandoning a late connection. The timeout error
|
|
342
|
+
// keeps any cleanup diagnostic; an uncooperative fetch itself is outside
|
|
343
|
+
// the SDK's control.
|
|
344
|
+
pendingResponse.then(async (lateResponse) => {
|
|
345
|
+
const body = lateResponse.body;
|
|
346
|
+
const cleanup = await cancelStreamBody(body ? () => body.cancel(error) : undefined);
|
|
347
|
+
if (cleanup)
|
|
348
|
+
withStreamCleanupCause(error, cleanup);
|
|
349
|
+
}, (lateFailure) => {
|
|
350
|
+
if (lateFailure !== error && error instanceof Error) {
|
|
351
|
+
withStreamCleanupCause(error, new Error("Opening fetch failed after cancellation", {
|
|
352
|
+
cause: lateFailure,
|
|
353
|
+
}));
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
if (!response.ok) {
|
|
359
|
+
try {
|
|
360
|
+
reader = response.body?.getReader();
|
|
361
|
+
}
|
|
362
|
+
catch (cause) {
|
|
363
|
+
const error = errorFromResponse(response.status, null, retryAfterSeconds(response), {
|
|
364
|
+
requestId: response.headers.get("x-request-id"),
|
|
365
|
+
});
|
|
366
|
+
error.cause = new StreamRefusalBodyError("read_failed", 0, maxErrorBodyBytes, cause);
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
throw await streamRefusal(response, reader, connection.signal, maxErrorBodyBytes, options.signal);
|
|
370
|
+
}
|
|
371
|
+
const mediaType = response.headers.get("content-type");
|
|
372
|
+
if (!isEventStreamMediaType(mediaType)) {
|
|
373
|
+
throw new StreamProtocolError("unexpected_media_type", {
|
|
374
|
+
lastSeq: undefined,
|
|
375
|
+
mediaType,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
if (!response.body)
|
|
379
|
+
throw new Error("Stream response has no readable body");
|
|
380
|
+
reader = response.body.getReader();
|
|
381
|
+
connection.established();
|
|
382
|
+
const decoder = new TextDecoder();
|
|
383
|
+
const encoder = new TextEncoder();
|
|
384
|
+
let buffer = "";
|
|
385
|
+
// Bytes of `buffer`: exact, since every chunk adds its own byte length and
|
|
386
|
+
// a cut re-measures the remainder. Bounded by `maxFrameBytes`.
|
|
387
|
+
let bufferedBytes = 0;
|
|
388
|
+
let lastSeq;
|
|
186
389
|
for (;;) {
|
|
187
|
-
const { value, done } = await reader.read();
|
|
390
|
+
const { value, done } = await streamWork(reader.read(), connection.signal);
|
|
188
391
|
if (done)
|
|
189
392
|
break;
|
|
190
393
|
buffer += decoder.decode(value, { stream: true });
|
|
@@ -236,18 +439,25 @@ export async function* streamFeed(client, options = {}) {
|
|
|
236
439
|
}
|
|
237
440
|
}
|
|
238
441
|
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
failed = true;
|
|
444
|
+
failure = error;
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
239
447
|
finally {
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
448
|
+
// Cancel even when the consumer breaks, and always release the lock.
|
|
449
|
+
// Cancellation failures remain visible without replacing an HTTP status.
|
|
450
|
+
connection.dispose();
|
|
451
|
+
const ownedReader = reader;
|
|
452
|
+
const ownedBody = response?.body;
|
|
453
|
+
const cleanup = await cancelStreamBody(ownedReader
|
|
454
|
+
? () => ownedReader.cancel(failure)
|
|
455
|
+
: ownedBody
|
|
456
|
+
? () => ownedBody.cancel(failure)
|
|
457
|
+
: undefined);
|
|
458
|
+
ownedReader?.releaseLock();
|
|
459
|
+
if (cleanup)
|
|
460
|
+
throw failed ? withStreamCleanupCause(failure, cleanup) : cleanup;
|
|
251
461
|
}
|
|
252
462
|
}
|
|
253
463
|
/** Default for `ResilientStreamOptions.maxReconnects`. */
|
|
@@ -353,7 +563,7 @@ function streamBackoffMs(attempt) {
|
|
|
353
563
|
return ceiling / 2 + Math.random() * (ceiling / 2);
|
|
354
564
|
}
|
|
355
565
|
function assertMaxRetryAfterMs(value) {
|
|
356
|
-
if (Number.isNaN(value) || value < 0) {
|
|
566
|
+
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
357
567
|
throw new Error(`maxRetryAfterMs must be a non-negative number of milliseconds or Infinity, got ${String(value)}`);
|
|
358
568
|
}
|
|
359
569
|
return value;
|
|
@@ -381,7 +591,8 @@ function assertMaxRetryAfterMs(value) {
|
|
|
381
591
|
* - Aborting `signal` ends the iterator without throwing, during a connection
|
|
382
592
|
* or a backoff. Each connection's body reader is cancelled when it ends, so
|
|
383
593
|
* reconnects do not accumulate sockets.
|
|
384
|
-
* -
|
|
594
|
+
* - `openTimeoutMs` bounds only opening and refusal reads; healthy streams
|
|
595
|
+
* have no lifetime deadline, as with `streamFeed`.
|
|
385
596
|
*
|
|
386
597
|
* `options.cursor`, when passed, tracks the last delivered seq across every
|
|
387
598
|
* connection -- delivered, not processed (#16247). This loop resumes from it,
|
|
@@ -406,6 +617,7 @@ export async function* streamFeedResilient(client, options = {}) {
|
|
|
406
617
|
throw new Error(`maxReconnects must be a non-negative integer, got ${String(maxReconnects)}`);
|
|
407
618
|
}
|
|
408
619
|
const retryAfterCeilingMs = assertMaxRetryAfterMs(maxRetryAfterMs);
|
|
620
|
+
const connectionOptions = streamConnectionOptions(streamOptions);
|
|
409
621
|
// The one configuration error `streamFeed` throws before any request; it
|
|
410
622
|
// would otherwise count as a transport failure and burn every reconnect.
|
|
411
623
|
if (!client.getApiKey() && !client.isSandbox()) {
|
|
@@ -422,6 +634,7 @@ export async function* streamFeedResilient(client, options = {}) {
|
|
|
422
634
|
try {
|
|
423
635
|
for await (const frame of streamFeed(client, {
|
|
424
636
|
...streamOptions,
|
|
637
|
+
...connectionOptions,
|
|
425
638
|
cursor,
|
|
426
639
|
lastEventId: cursor.seq ?? initialLastEventId,
|
|
427
640
|
})) {
|
|
@@ -607,6 +820,12 @@ export async function consumeStreamCheckpointed(client, handlers, options = {})
|
|
|
607
820
|
if (!Number.isInteger(maxHandlerRetries) || maxHandlerRetries < 0) {
|
|
608
821
|
throw new Error(`maxHandlerRetries must be a non-negative integer, got ${String(maxHandlerRetries)}`);
|
|
609
822
|
}
|
|
823
|
+
streamConnectionOptions(streamOptions);
|
|
824
|
+
const maxReconnects = streamOptions.maxReconnects ?? DEFAULT_MAX_STREAM_RECONNECTS;
|
|
825
|
+
if (!Number.isInteger(maxReconnects) || maxReconnects < 0) {
|
|
826
|
+
throw new Error(`maxReconnects must be a non-negative integer, got ${String(maxReconnects)}`);
|
|
827
|
+
}
|
|
828
|
+
assertMaxRetryAfterMs(streamOptions.maxRetryAfterMs ?? DEFAULT_MAX_STREAM_RETRY_AFTER_MS);
|
|
610
829
|
const signal = streamOptions.signal;
|
|
611
830
|
// The caller's received cursor, if they passed one. It keeps its delivery
|
|
612
831
|
// meaning: this consumer mirrors every frame into it and never reads it
|
|
@@ -829,7 +1048,7 @@ export function decodeStreamFrame(parsed, lastSeq) {
|
|
|
829
1048
|
if (!isPlainObject(payload)) {
|
|
830
1049
|
throw new StreamProtocolError("invalid_resync", detail);
|
|
831
1050
|
}
|
|
832
|
-
if ("type" in payload && payload
|
|
1051
|
+
if ("type" in payload && payload["type"] !== "resync") {
|
|
833
1052
|
throw new StreamProtocolError("invalid_resync", detail);
|
|
834
1053
|
}
|
|
835
1054
|
return {
|
|
@@ -844,8 +1063,8 @@ export function decodeStreamFrame(parsed, lastSeq) {
|
|
|
844
1063
|
if (!isPlainObject(payload)) {
|
|
845
1064
|
throw new StreamProtocolError("invalid_envelope", detail);
|
|
846
1065
|
}
|
|
847
|
-
const seq = typeof payload
|
|
848
|
-
? payload
|
|
1066
|
+
const seq = typeof payload["seq"] === "number" && Number.isFinite(payload["seq"])
|
|
1067
|
+
? payload["seq"]
|
|
849
1068
|
: parsed.id;
|
|
850
1069
|
if (seq === undefined || !Number.isFinite(seq)) {
|
|
851
1070
|
throw new StreamProtocolError("unusable_sequence", detail);
|
|
@@ -892,13 +1111,13 @@ const permanentTerminalErrors = new WeakSet();
|
|
|
892
1111
|
* consumer's reconnect path like any 5xx.
|
|
893
1112
|
*/
|
|
894
1113
|
function terminalStreamError(payload, detail) {
|
|
895
|
-
if (!isPlainObject(payload) || !isPlainObject(payload
|
|
1114
|
+
if (!isPlainObject(payload) || !isPlainObject(payload["error"])) {
|
|
896
1115
|
return new StreamProtocolError("invalid_envelope", detail);
|
|
897
1116
|
}
|
|
898
|
-
const body = payload
|
|
899
|
-
const retry = payload
|
|
900
|
-
const code = typeof body
|
|
901
|
-
const reason = typeof body
|
|
1117
|
+
const body = payload["error"];
|
|
1118
|
+
const retry = payload["retry"] === true;
|
|
1119
|
+
const code = typeof body["code"] === "string" ? body["code"] : undefined;
|
|
1120
|
+
const reason = typeof body["reason"] === "string" ? body["reason"] : undefined;
|
|
902
1121
|
const status = reason !== undefined && STREAM_ERROR_UNAVAILABLE_REASONS.has(reason)
|
|
903
1122
|
? 503
|
|
904
1123
|
: ((code !== undefined ? STREAM_ERROR_CODE_STATUS[code] : undefined) ??
|
|
@@ -911,22 +1130,4 @@ function terminalStreamError(payload, detail) {
|
|
|
911
1130
|
function isPlainObject(value) {
|
|
912
1131
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
913
1132
|
}
|
|
914
|
-
function tryParse(text) {
|
|
915
|
-
if (text === null || text === "")
|
|
916
|
-
return null;
|
|
917
|
-
try {
|
|
918
|
-
return JSON.parse(text);
|
|
919
|
-
}
|
|
920
|
-
catch {
|
|
921
|
-
return null;
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
async function safeText(response) {
|
|
925
|
-
try {
|
|
926
|
-
return await response.text();
|
|
927
|
-
}
|
|
928
|
-
catch {
|
|
929
|
-
return "";
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
1133
|
//# sourceMappingURL=stream.js.map
|