@0xinsider/mcp 2.1.0 → 2.1.3

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.
Files changed (92) hide show
  1. package/README.md +25 -6
  2. package/dist/cli-lib.d.ts +1 -0
  3. package/dist/cli-lib.d.ts.map +1 -1
  4. package/dist/cli-lib.js +48 -8
  5. package/dist/cli-lib.js.map +1 -1
  6. package/dist/client.d.ts +33 -2
  7. package/dist/client.d.ts.map +1 -1
  8. package/dist/client.js +42 -4
  9. package/dist/client.js.map +1 -1
  10. package/dist/constants.d.ts +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/product-cli/client.d.ts +3 -2
  13. package/dist/product-cli/client.d.ts.map +1 -1
  14. package/dist/product-cli/client.js.map +1 -1
  15. package/dist/sdk/client.d.ts +690 -133
  16. package/dist/sdk/client.d.ts.map +1 -1
  17. package/dist/sdk/client.js +699 -125
  18. package/dist/sdk/client.js.map +1 -1
  19. package/dist/sdk/errors.d.ts +42 -4
  20. package/dist/sdk/errors.d.ts.map +1 -1
  21. package/dist/sdk/errors.js +103 -2
  22. package/dist/sdk/errors.js.map +1 -1
  23. package/dist/sdk/index.d.ts +9 -8
  24. package/dist/sdk/index.d.ts.map +1 -1
  25. package/dist/sdk/index.js +6 -4
  26. package/dist/sdk/index.js.map +1 -1
  27. package/dist/sdk/pagination.d.ts +133 -15
  28. package/dist/sdk/pagination.d.ts.map +1 -1
  29. package/dist/sdk/pagination.js +132 -34
  30. package/dist/sdk/pagination.js.map +1 -1
  31. package/dist/sdk/retry.d.ts +68 -0
  32. package/dist/sdk/retry.d.ts.map +1 -0
  33. package/dist/sdk/retry.js +125 -0
  34. package/dist/sdk/retry.js.map +1 -0
  35. package/dist/sdk/schema.d.ts +1381 -439
  36. package/dist/sdk/schema.d.ts.map +1 -1
  37. package/dist/sdk/stream.d.ts +318 -8
  38. package/dist/sdk/stream.d.ts.map +1 -1
  39. package/dist/sdk/stream.js +503 -56
  40. package/dist/sdk/stream.js.map +1 -1
  41. package/dist/sdk/webhooks.d.ts +18 -3
  42. package/dist/sdk/webhooks.d.ts.map +1 -1
  43. package/dist/sdk/webhooks.js +13 -1
  44. package/dist/sdk/webhooks.js.map +1 -1
  45. package/dist/tools/explore-markets.d.ts.map +1 -1
  46. package/dist/tools/explore-markets.js +2 -5
  47. package/dist/tools/explore-markets.js.map +1 -1
  48. package/dist/tools/get-insider-radar.d.ts.map +1 -1
  49. package/dist/tools/get-insider-radar.js +3 -10
  50. package/dist/tools/get-insider-radar.js.map +1 -1
  51. package/dist/tools/get-large-positions.d.ts.map +1 -1
  52. package/dist/tools/get-large-positions.js +3 -10
  53. package/dist/tools/get-large-positions.js.map +1 -1
  54. package/dist/tools/get-leaderboard.d.ts.map +1 -1
  55. package/dist/tools/get-leaderboard.js +3 -10
  56. package/dist/tools/get-leaderboard.js.map +1 -1
  57. package/dist/tools/get-market-intel.d.ts.map +1 -1
  58. package/dist/tools/get-market-intel.js +3 -6
  59. package/dist/tools/get-market-intel.js.map +1 -1
  60. package/dist/tools/get-position-timeline.d.ts.map +1 -1
  61. package/dist/tools/get-position-timeline.js +3 -10
  62. package/dist/tools/get-position-timeline.js.map +1 -1
  63. package/dist/tools/get-positions.d.ts +2 -0
  64. package/dist/tools/get-positions.d.ts.map +1 -1
  65. package/dist/tools/get-positions.js +8 -11
  66. package/dist/tools/get-positions.js.map +1 -1
  67. package/dist/tools/get-trader-pnl.d.ts.map +1 -1
  68. package/dist/tools/get-trader-pnl.js +3 -6
  69. package/dist/tools/get-trader-pnl.js.map +1 -1
  70. package/dist/tools/get-trader.d.ts.map +1 -1
  71. package/dist/tools/get-trader.js +3 -6
  72. package/dist/tools/get-trader.js.map +1 -1
  73. package/dist/tools/get-trending-wallets.d.ts.map +1 -1
  74. package/dist/tools/get-trending-wallets.js +3 -10
  75. package/dist/tools/get-trending-wallets.js.map +1 -1
  76. package/dist/tools/get-whale-trades.d.ts.map +1 -1
  77. package/dist/tools/get-whale-trades.js +4 -11
  78. package/dist/tools/get-whale-trades.js.map +1 -1
  79. package/dist/tools/pick-of-the-day.d.ts.map +1 -1
  80. package/dist/tools/pick-of-the-day.js +5 -21
  81. package/dist/tools/pick-of-the-day.js.map +1 -1
  82. package/dist/tools/read-parity.d.ts.map +1 -1
  83. package/dist/tools/read-parity.js +36 -44
  84. package/dist/tools/read-parity.js.map +1 -1
  85. package/dist/tools/result.d.ts +31 -0
  86. package/dist/tools/result.d.ts.map +1 -0
  87. package/dist/tools/result.js +70 -0
  88. package/dist/tools/result.js.map +1 -0
  89. package/dist/tools/search-markets.d.ts.map +1 -1
  90. package/dist/tools/search-markets.js +16 -11
  91. package/dist/tools/search-markets.js.map +1 -1
  92. package/package.json +4 -4
@@ -1,26 +1,108 @@
1
1
  /**
2
2
  * Cursor pagination over the Stripe-style V1 list envelope.
3
3
  *
4
- * Every list endpoint returns `{ object: "list", data, has_more, next_cursor }`.
5
- * `paginate()` yields each `data[]` item across pages, following `next_cursor`
6
- * while `has_more` is true. `paginatePages()` yields whole pages when you need
7
- * the envelope (e.g. to read `meta.cost` or `total`).
4
+ * Every list endpoint returns `{ object: "list", data, has_more, next_cursor }`
5
+ * (`backend/src/api_v1/types.rs` `ApiList`: `has_more` is always a boolean,
6
+ * `next_cursor` is a string or omitted). `paginate()` yields each `data[]`
7
+ * item across pages, following `next_cursor` while `has_more` is true.
8
+ * `paginatePages()` yields whole pages when you need the envelope (e.g. to
9
+ * read `meta.cost` or `total`).
8
10
  *
9
- * The loop stops on the first page where `has_more` is false OR `next_cursor`
10
- * is null/absent, so a buggy server that returns `has_more: true` with no
11
- * cursor cannot spin forever.
11
+ * The walker is a progress-checked consumer of that protocol (#16246):
12
+ *
13
+ * - A page is validated BEFORE it is yielded. `has_more: true` with no usable
14
+ * `next_cursor` is a `PaginationError` (`missing_cursor`); the backend's own
15
+ * cursor builders refuse to produce that page ("has_more is true for an
16
+ * empty page", `insider_radar.rs`), so a client seeing it is looking at a
17
+ * malformed response, not the end of the collection. Before #16246 the
18
+ * walk returned as if the collection were exhausted.
19
+ * - A `next_cursor` that repeats a cursor this walk already requested is a
20
+ * `PaginationError` (`repeated_cursor`), thrown before the duplicate
21
+ * request. The last `CURSOR_HISTORY_LIMIT` cursors are remembered; a cycle
22
+ * longer than that is not detected, which bounds memory on a long walk.
23
+ * - `maxPages` is validated before any request: a positive integer or
24
+ * `Infinity`; `0`, a negative, fractional or `NaN` value throws `RangeError`.
25
+ * - `has_more: false` ends the walk normally, whatever `next_cursor` says.
26
+ * - A `PaginationError` carries the offending `page`, so its data is not lost,
27
+ * and registers a resume point like any other failure.
28
+ *
29
+ * `options.progress`, when passed, is updated as the walk goes: how many
30
+ * pages were fetched, the last cursor requested, the `next_cursor` of the
31
+ * last page, and why the walk stopped, so a caller that stops at `maxPages`
32
+ * can tell "the collection has more" from "the collection is exhausted" and
33
+ * keep the continuation.
12
34
  */
13
- import type { ApiListEnvelope, ApiOperationId, ApiRequestOptions, ListParams, OxinsiderApiClient } from "./client.js";
35
+ import type { ApiListEnvelope, ApiOperationId, ApiRequestOptions, ListOperationId, ListParams, OperationEnvelope, OperationItem, OxinsiderApiClient } from "./client.js";
36
+ import type { OperationPath, OperationQuery } from "./schema.js";
37
+ /**
38
+ * How many requested cursors a walk remembers for the repeat check. A cycle
39
+ * longer than this is not detected; the bound keeps a long walk's memory
40
+ * flat instead of growing with every page.
41
+ */
42
+ export declare const CURSOR_HISTORY_LIMIT = 1024;
43
+ /** Why a walk ended; see `PaginationProgress.stoppedBy`. */
44
+ export type PaginationStop =
45
+ /** The server said `has_more: false`: the collection is exhausted. */
46
+ "exhausted"
47
+ /** The caller's `maxPages` was reached; `nextCursor` continues the walk. */
48
+ | "max_pages";
49
+ /**
50
+ * Live progress of a walk (#16246). Pass an object as `options.progress`
51
+ * and read it after the loop; it is updated before each page is yielded and
52
+ * when the walk ends. On a throw it holds the state at the failed page, the
53
+ * same information `paginationResumePoint(error)` returns.
54
+ */
55
+ export interface PaginationProgress {
56
+ /** Pages fetched and yielded so far. */
57
+ pagesFetched?: number;
58
+ /** The cursor the most recent page was requested with; `undefined` for the first page. */
59
+ cursor?: string;
60
+ /**
61
+ * The most recent page's `next_cursor`. After `stoppedBy: "max_pages"`
62
+ * this is the continuation: pass it as `query.cursor` to carry on.
63
+ * `null` or `undefined` once the collection is exhausted.
64
+ */
65
+ nextCursor?: string | null;
66
+ /** Set when the walk ends without throwing. */
67
+ stoppedBy?: PaginationStop;
68
+ }
14
69
  export interface PaginateOptions {
15
70
  /** Path parameters for templated list paths (e.g. `{ id }` for deliveries). */
16
71
  path?: ApiRequestOptions["path"];
17
72
  /** Initial query params (filters, `limit`, an initial `cursor`, ...). */
18
73
  query?: ListParams;
74
+ /** Reject query names the operation does not publish before the first request. */
75
+ strictQuery?: ApiRequestOptions["strictQuery"];
19
76
  /** Forwarded to each request for cooperative cancellation. */
20
77
  signal?: AbortSignal;
21
- /** Hard cap on pages fetched; defaults to unbounded. */
78
+ /** Per-request deadline, forwarded to each page; see `ApiRequestOptions.timeoutMs`. */
79
+ timeoutMs?: number | null;
80
+ /** Retries per page, forwarded to each request; see `ApiRequestOptions.maxRetries`. */
81
+ maxRetries?: number;
82
+ /** Extra headers on every page, such as a conditional read's `If-None-Match`. */
83
+ headers?: Record<string, string>;
84
+ /**
85
+ * Hard cap on pages fetched. A positive integer, or `Infinity` / omitted
86
+ * for no cap. Anything else (`0`, negative, fractional, `NaN`) throws a
87
+ * `RangeError` before any request. Reaching it is a caller choice, not
88
+ * exhaustion: `progress.stoppedBy` reads `"max_pages"` and
89
+ * `progress.nextCursor` holds the continuation.
90
+ */
22
91
  maxPages?: number;
92
+ /** Updated as the walk goes; see `PaginationProgress`. */
93
+ progress?: PaginationProgress;
23
94
  }
95
+ /**
96
+ * `PaginateOptions` bound to one list operation (#16136): `path` required
97
+ * exactly when the route has path parameters, `query` the documented query.
98
+ */
99
+ export type OperationPaginateOptions<K extends ListOperationId> = Omit<PaginateOptions, "path" | "query"> & (OperationPath[K] extends Record<string, never> ? {
100
+ path?: OperationPath[K];
101
+ } : {
102
+ path: OperationPath[K];
103
+ }) & {
104
+ query?: OperationQuery[K];
105
+ };
24
106
  /** Where an interrupted walk can pick up again; see `paginationResumePoint`. */
25
107
  export interface PaginationResumePoint {
26
108
  /**
@@ -32,6 +114,34 @@ export interface PaginationResumePoint {
32
114
  /** Pages already yielded before the failure. */
33
115
  pagesFetched: number;
34
116
  }
117
+ /** What a page did that the list protocol does not allow. */
118
+ export type PaginationErrorReason =
119
+ /** `has_more: true` with no usable `next_cursor` (missing, `null` or empty). */
120
+ "missing_cursor"
121
+ /** `next_cursor` repeats a cursor this walk already requested: a cycle. */
122
+ | "repeated_cursor";
123
+ /**
124
+ * The server's list response broke the pagination protocol (#16246). The
125
+ * walk stops before yielding the page and before any further request.
126
+ *
127
+ * `page` is the response as received, so its `data` is still available to
128
+ * you; `cursor` is what that page was requested with (`undefined` for the
129
+ * first page) and `nextCursor` is what it offered next. `pagesFetched`
130
+ * counts the pages already yielded. `paginationResumePoint(error)` returns
131
+ * `{ cursor, pagesFetched }` for this error too. Retrying the same page later
132
+ * is reasonable for a transient server fault; a repeat on the same cursor
133
+ * means the route's cursor is broken and is worth reporting with
134
+ * `page.meta.request_id`.
135
+ */
136
+ export declare class PaginationError<T = unknown> extends Error {
137
+ readonly reason: PaginationErrorReason;
138
+ readonly operationId: ApiOperationId;
139
+ readonly cursor: string | undefined;
140
+ readonly nextCursor: string | null | undefined;
141
+ readonly pagesFetched: number;
142
+ readonly page: ApiListEnvelope<T>;
143
+ constructor(reason: PaginationErrorReason, operationId: ApiOperationId, page: ApiListEnvelope<T>, cursor: string | undefined, pagesFetched: number);
144
+ }
35
145
  /**
36
146
  * The resume point of an error thrown out of `paginatePages`, `paginate` or
37
147
  * `collect`, or `undefined` for any other error (#14281).
@@ -40,6 +150,7 @@ export interface PaginationResumePoint {
40
150
  * retries are exhausted the ORIGINAL error is rethrown unchanged, so an
41
151
  * `instanceof RateLimitedError` check keeps working, and this function recovers
42
152
  * the cursor that the throw would otherwise have destroyed with the generator.
153
+ * A `PaginationError` (#16246) is registered the same way.
43
154
  *
44
155
  * @example
45
156
  * try {
@@ -54,23 +165,30 @@ export declare function paginationResumePoint(error: unknown): PaginationResumeP
54
165
  * Async-iterate whole list pages, following `next_cursor` until exhausted.
55
166
  * Yields the full envelope so callers can read `meta`/`total`/`has_more`.
56
167
  * A failed page throws its original error; `paginationResumePoint(error)`
57
- * returns the cursor to continue from.
168
+ * returns the cursor to continue from. A page that breaks the protocol
169
+ * throws `PaginationError` before it is yielded (#16246).
58
170
  */
59
- export declare function paginatePages<T>(client: OxinsiderApiClient, operationId: ApiOperationId, options?: PaginateOptions): AsyncGenerator<ApiListEnvelope<T>, void, undefined>;
171
+ export declare function paginatePages<K extends ListOperationId>(client: OxinsiderApiClient, operationId: K, options?: OperationPaginateOptions<K>): AsyncGenerator<OperationEnvelope<K>, void, undefined>;
172
+ /** The untyped form: a runtime-chosen operation, or a caller-asserted `T`. */
173
+ export declare function paginatePages<T = unknown>(client: OxinsiderApiClient, operationId: ApiOperationId, options?: PaginateOptions): AsyncGenerator<ApiListEnvelope<T>, void, undefined>;
60
174
  /**
61
175
  * Async-iterate individual `data[]` items across all pages.
62
176
  *
63
177
  * @example
64
- * for await (const trade of paginate<WhaleTrade>(client, "listWhaleTrades", {
178
+ * for await (const trade of paginate(client, "listWhaleTrades", {
65
179
  * query: { min_grade: "S", limit: 100 },
66
180
  * })) {
67
- * handle(trade);
181
+ * handle(trade); // typed as the route's whale trade (#16136)
68
182
  * }
69
183
  */
70
- export declare function paginate<T>(client: OxinsiderApiClient, operationId: ApiOperationId, options?: PaginateOptions): AsyncGenerator<T, void, undefined>;
184
+ export declare function paginate<K extends ListOperationId>(client: OxinsiderApiClient, operationId: K, options?: OperationPaginateOptions<K>): AsyncGenerator<OperationItem<K>, void, undefined>;
185
+ /** The untyped form: a runtime-chosen operation, or a caller-asserted `T`. */
186
+ export declare function paginate<T = unknown>(client: OxinsiderApiClient, operationId: ApiOperationId, options?: PaginateOptions): AsyncGenerator<T, void, undefined>;
71
187
  /**
72
188
  * Collect every item across all pages into one array. Convenience over
73
189
  * `paginate()` for small result sets; prefer the iterator for large ones.
74
190
  */
75
- export declare function collect<T>(client: OxinsiderApiClient, operationId: ApiOperationId, options?: PaginateOptions): Promise<T[]>;
191
+ export declare function collect<K extends ListOperationId>(client: OxinsiderApiClient, operationId: K, options?: OperationPaginateOptions<K>): Promise<OperationItem<K>[]>;
192
+ /** The untyped form: a runtime-chosen operation, or a caller-asserted `T`. */
193
+ export declare function collect<T = unknown>(client: OxinsiderApiClient, operationId: ApiOperationId, options?: PaginateOptions): Promise<T[]>;
76
194
  //# sourceMappingURL=pagination.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../sdk/src/pagination.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,IAAI,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACjC,yEAAyE;IACzE,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,gFAAgF;AAChF,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;CACtB;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,GACb,qBAAqB,GAAG,SAAS,CAInC;AAED;;;;;GAKG;AACH,wBAAuB,aAAa,CAAC,CAAC,EACpC,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,cAAc,EAC3B,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAsCrD;AAED;;;;;;;;;GASG;AACH,wBAAuB,QAAQ,CAAC,CAAC,EAC/B,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,cAAc,EAC3B,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAMpC;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAAC,CAAC,EAC7B,MAAM,EAAE,kBAAkB,EAC1B,WAAW,EAAE,cAAc,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,CAAC,EAAE,CAAC,CAMd"}
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../sdk/src/pagination.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,77 @@
1
1
  /**
2
2
  * Cursor pagination over the Stripe-style V1 list envelope.
3
3
  *
4
- * Every list endpoint returns `{ object: "list", data, has_more, next_cursor }`.
5
- * `paginate()` yields each `data[]` item across pages, following `next_cursor`
6
- * while `has_more` is true. `paginatePages()` yields whole pages when you need
7
- * the envelope (e.g. to read `meta.cost` or `total`).
4
+ * Every list endpoint returns `{ object: "list", data, has_more, next_cursor }`
5
+ * (`backend/src/api_v1/types.rs` `ApiList`: `has_more` is always a boolean,
6
+ * `next_cursor` is a string or omitted). `paginate()` yields each `data[]`
7
+ * item across pages, following `next_cursor` while `has_more` is true.
8
+ * `paginatePages()` yields whole pages when you need the envelope (e.g. to
9
+ * read `meta.cost` or `total`).
8
10
  *
9
- * The loop stops on the first page where `has_more` is false OR `next_cursor`
10
- * is null/absent, so a buggy server that returns `has_more: true` with no
11
- * cursor cannot spin forever.
11
+ * The walker is a progress-checked consumer of that protocol (#16246):
12
+ *
13
+ * - A page is validated BEFORE it is yielded. `has_more: true` with no usable
14
+ * `next_cursor` is a `PaginationError` (`missing_cursor`); the backend's own
15
+ * cursor builders refuse to produce that page ("has_more is true for an
16
+ * empty page", `insider_radar.rs`), so a client seeing it is looking at a
17
+ * malformed response, not the end of the collection. Before #16246 the
18
+ * walk returned as if the collection were exhausted.
19
+ * - A `next_cursor` that repeats a cursor this walk already requested is a
20
+ * `PaginationError` (`repeated_cursor`), thrown before the duplicate
21
+ * request. The last `CURSOR_HISTORY_LIMIT` cursors are remembered; a cycle
22
+ * longer than that is not detected, which bounds memory on a long walk.
23
+ * - `maxPages` is validated before any request: a positive integer or
24
+ * `Infinity`; `0`, a negative, fractional or `NaN` value throws `RangeError`.
25
+ * - `has_more: false` ends the walk normally, whatever `next_cursor` says.
26
+ * - A `PaginationError` carries the offending `page`, so its data is not lost,
27
+ * and registers a resume point like any other failure.
28
+ *
29
+ * `options.progress`, when passed, is updated as the walk goes: how many
30
+ * pages were fetched, the last cursor requested, the `next_cursor` of the
31
+ * last page, and why the walk stopped, so a caller that stops at `maxPages`
32
+ * can tell "the collection has more" from "the collection is exhausted" and
33
+ * keep the continuation.
34
+ */
35
+ /**
36
+ * How many requested cursors a walk remembers for the repeat check. A cycle
37
+ * longer than this is not detected; the bound keeps a long walk's memory
38
+ * flat instead of growing with every page.
12
39
  */
40
+ export const CURSOR_HISTORY_LIMIT = 1_024;
41
+ /**
42
+ * The server's list response broke the pagination protocol (#16246). The
43
+ * walk stops before yielding the page and before any further request.
44
+ *
45
+ * `page` is the response as received, so its `data` is still available to
46
+ * you; `cursor` is what that page was requested with (`undefined` for the
47
+ * first page) and `nextCursor` is what it offered next. `pagesFetched`
48
+ * counts the pages already yielded. `paginationResumePoint(error)` returns
49
+ * `{ cursor, pagesFetched }` for this error too. Retrying the same page later
50
+ * is reasonable for a transient server fault; a repeat on the same cursor
51
+ * means the route's cursor is broken and is worth reporting with
52
+ * `page.meta.request_id`.
53
+ */
54
+ export class PaginationError extends Error {
55
+ reason;
56
+ operationId;
57
+ cursor;
58
+ nextCursor;
59
+ pagesFetched;
60
+ page;
61
+ constructor(reason, operationId, page, cursor, pagesFetched) {
62
+ const requestId = page.meta?.request_id;
63
+ super(reason === "missing_cursor"
64
+ ? `0xinsider API ${operationId} page ${String(pagesFetched + 1)} says has_more but carries no next_cursor${requestId ? ` (request ${requestId})` : ""}; the walk cannot continue safely`
65
+ : `0xinsider API ${operationId} page ${String(pagesFetched + 1)} offers next_cursor ${JSON.stringify(page.next_cursor)}, which this walk already requested${requestId ? ` (request ${requestId})` : ""}; stopping before the duplicate request`);
66
+ this.name = "PaginationError";
67
+ this.reason = reason;
68
+ this.operationId = operationId;
69
+ this.cursor = cursor;
70
+ this.nextCursor = page.next_cursor;
71
+ this.pagesFetched = pagesFetched;
72
+ this.page = page;
73
+ }
74
+ }
13
75
  const resumePoints = new WeakMap();
14
76
  /**
15
77
  * The resume point of an error thrown out of `paginatePages`, `paginate` or
@@ -19,6 +81,7 @@ const resumePoints = new WeakMap();
19
81
  * retries are exhausted the ORIGINAL error is rethrown unchanged, so an
20
82
  * `instanceof RateLimitedError` check keeps working, and this function recovers
21
83
  * the cursor that the throw would otherwise have destroyed with the generator.
84
+ * A `PaginationError` (#16246) is registered the same way.
22
85
  *
23
86
  * @example
24
87
  * try {
@@ -33,58 +96,97 @@ export function paginationResumePoint(error) {
33
96
  ? resumePoints.get(error)
34
97
  : undefined;
35
98
  }
36
- /**
37
- * Async-iterate whole list pages, following `next_cursor` until exhausted.
38
- * Yields the full envelope so callers can read `meta`/`total`/`has_more`.
39
- * A failed page throws its original error; `paginationResumePoint(error)`
40
- * returns the cursor to continue from.
41
- */
99
+ function assertMaxPages(value) {
100
+ if (value === undefined || value === Infinity)
101
+ return Infinity;
102
+ if (!Number.isInteger(value) || value < 1) {
103
+ throw new RangeError(`maxPages must be a positive integer or Infinity, got ${String(value)}`);
104
+ }
105
+ return value;
106
+ }
107
+ /** A `next_cursor` the walk can request: a non-empty string. */
108
+ function usableCursor(value) {
109
+ return typeof value === "string" && value !== "";
110
+ }
42
111
  export async function* paginatePages(client, operationId, options = {}) {
43
- const { path, query, signal, maxPages } = options;
112
+ const { path, query, strictQuery, signal, progress, timeoutMs, maxRetries, headers } = options;
113
+ const maxPages = assertMaxPages(options.maxPages);
44
114
  let cursor = query?.cursor;
45
115
  let pages = 0;
116
+ // Every cursor requested so far, newest last, capped at CURSOR_HISTORY_LIMIT.
117
+ const requested = new Set();
118
+ const requestedOrder = [];
119
+ const remember = (value) => {
120
+ if (requested.has(value))
121
+ return;
122
+ requested.add(value);
123
+ requestedOrder.push(value);
124
+ if (requestedOrder.length > CURSOR_HISTORY_LIMIT) {
125
+ const oldest = requestedOrder.shift();
126
+ if (oldest !== undefined)
127
+ requested.delete(oldest);
128
+ }
129
+ };
130
+ const record = (fields) => {
131
+ if (progress)
132
+ Object.assign(progress, fields);
133
+ };
134
+ const fail = (error) => {
135
+ if (!resumePoints.has(error)) {
136
+ resumePoints.set(error, { cursor, pagesFetched: pages });
137
+ }
138
+ return error;
139
+ };
46
140
  for (;;) {
47
141
  if (signal?.aborted) {
48
142
  throw signal.reason instanceof Error
49
143
  ? signal.reason
50
144
  : new DOMException("The pagination was aborted", "AbortError");
51
145
  }
146
+ if (cursor !== undefined)
147
+ remember(cursor);
52
148
  let page;
53
149
  try {
54
150
  page = await client.list(operationId, {
55
151
  path,
56
152
  query: { ...query, ...(cursor === undefined ? {} : { cursor }) },
153
+ strictQuery,
57
154
  signal,
155
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
156
+ ...(maxRetries === undefined ? {} : { maxRetries }),
157
+ ...(headers === undefined ? {} : { headers }),
58
158
  });
59
159
  }
60
160
  catch (error) {
61
- if (typeof error === "object" && error !== null && !resumePoints.has(error)) {
62
- resumePoints.set(error, { cursor, pagesFetched: pages });
63
- }
161
+ if (typeof error === "object" && error !== null)
162
+ fail(error);
64
163
  throw error;
65
164
  }
165
+ record({ pagesFetched: pages, cursor, nextCursor: page.next_cursor });
166
+ const next = page.next_cursor;
167
+ if (page.has_more) {
168
+ if (!usableCursor(next)) {
169
+ throw fail(new PaginationError("missing_cursor", operationId, page, cursor, pages));
170
+ }
171
+ if (requested.has(next)) {
172
+ throw fail(new PaginationError("repeated_cursor", operationId, page, cursor, pages));
173
+ }
174
+ }
66
175
  yield page;
67
176
  pages += 1;
68
- const next = page.next_cursor;
69
- if (!page.has_more || next === null || next === undefined || next === "") {
177
+ record({ pagesFetched: pages, cursor, nextCursor: next });
178
+ if (!page.has_more) {
179
+ record({ pagesFetched: pages, cursor, nextCursor: next, stoppedBy: "exhausted" });
70
180
  return;
71
181
  }
72
- if (maxPages !== undefined && pages >= maxPages) {
182
+ if (pages >= maxPages) {
183
+ record({ pagesFetched: pages, cursor, nextCursor: next, stoppedBy: "max_pages" });
73
184
  return;
74
185
  }
186
+ // `has_more` was true and `next` passed both checks above.
75
187
  cursor = next;
76
188
  }
77
189
  }
78
- /**
79
- * Async-iterate individual `data[]` items across all pages.
80
- *
81
- * @example
82
- * for await (const trade of paginate<WhaleTrade>(client, "listWhaleTrades", {
83
- * query: { min_grade: "S", limit: 100 },
84
- * })) {
85
- * handle(trade);
86
- * }
87
- */
88
190
  export async function* paginate(client, operationId, options = {}) {
89
191
  for await (const page of paginatePages(client, operationId, options)) {
90
192
  for (const item of page.data) {
@@ -92,10 +194,6 @@ export async function* paginate(client, operationId, options = {}) {
92
194
  }
93
195
  }
94
196
  }
95
- /**
96
- * Collect every item across all pages into one array. Convenience over
97
- * `paginate()` for small result sets; prefer the iterator for large ones.
98
- */
99
197
  export async function collect(client, operationId, options = {}) {
100
198
  const items = [];
101
199
  for await (const item of paginate(client, operationId, options)) {
@@ -1 +1 @@
1
- {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../../sdk/src/pagination.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../../sdk/src/pagination.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `Retry-After` parsing and the waits it drives, shared by the REST retry
3
+ * loop (`client.ts`) and the SSE reconnect loop (`stream.ts`) so the two
4
+ * cannot read the same header differently (#16249).
5
+ *
6
+ * Two facts shape this module:
7
+ *
8
+ * 1. `Retry-After` is either delta-seconds or an HTTP-date (RFC 9110 §10.2.3,
9
+ * https://www.rfc-editor.org/rfc/rfc9110.html#name-retry-after). Both
10
+ * loops parsed only the number and treated a date as "no header".
11
+ * 2. Node schedules a `setTimeout` delay above 2147483647 ms (about 24.8 days)
12
+ * as 1 ms, with a warning
13
+ * (https://nodejs.org/api/timers.html#settimeoutcallback-delay-args). The
14
+ * stream loop multiplied any `Retry-After` by 1000 and handed it straight
15
+ * to the timer, so a long server-requested wait -- `monthly_quota_exceeded`
16
+ * names the first of next month -- would have reconnected at once, the
17
+ * opposite of what the server asked.
18
+ *
19
+ * The policy is therefore: parse both forms into a non-negative number of
20
+ * seconds; never wait in-process past a ceiling the caller can see (60 s by
21
+ * default, the same for REST and SSE); when the server asks for longer, hand
22
+ * the caller a typed error carrying the not-before instant and the resume
23
+ * context, never a clamped earlier retry; and when a caller opts into waiting
24
+ * longer, wait in timer-sized chunks so the timer range is never exceeded.
25
+ */
26
+ /**
27
+ * The largest delay one `setTimeout` can hold in Node (2^31 - 1 ms). A larger
28
+ * value is scheduled as 1 ms, so every wait here is cut into chunks of at
29
+ * most this size.
30
+ */
31
+ export declare const MAX_TIMER_DELAY_MS = 2147483647;
32
+ /**
33
+ * The longest server-requested wait either loop holds in-process by default.
34
+ * A `Retry-After` beyond it is the caller's to schedule: the REST loop throws
35
+ * the response's error (`retryAfterSeconds`, `retryAt`), the stream loop
36
+ * throws `StreamRetryDeferredError`.
37
+ */
38
+ export declare const RETRY_AFTER_CEILING_MS = 60000;
39
+ /**
40
+ * Parse a `Retry-After` header value into seconds to wait, or `null` when
41
+ * there is no usable value.
42
+ *
43
+ * - Delta-seconds: a finite, non-negative number (`"60"`, `"1.5"`). A negative
44
+ * or non-finite number (`"-1"`, `"Infinity"`, `"NaN"`) is `null`: the
45
+ * grammar does not allow it, and guessing a wait from it would be inventing
46
+ * a server instruction.
47
+ * - HTTP-date (`"Wed, 01 Oct 2026 00:00:00 GMT"`): the seconds from `nowMs`
48
+ * to that instant. A date already past is `0` (the server said "after an
49
+ * instant that has passed", which is "now"), never negative.
50
+ * - Anything else (`""`, `"soon"`) is `null`.
51
+ *
52
+ * `null` means "as if the header were absent": the caller falls back to its
53
+ * own backoff. It never means "retry at once".
54
+ */
55
+ export declare function parseRetryAfter(header: string | null | undefined, nowMs?: number): number | null;
56
+ /** `parseRetryAfter` over a response's `Retry-After` header. */
57
+ export declare function retryAfterSeconds(response: Response): number | null;
58
+ /**
59
+ * Resolve after `ms`, or reject with the signal's reason the moment it
60
+ * aborts. A wait longer than `MAX_TIMER_DELAY_MS` runs as consecutive timers
61
+ * of at most that size, so it is never handed to `setTimeout` as one value
62
+ * that Node would schedule as 1 ms. `ms` must be a non-negative number;
63
+ * `Infinity` is refused because it can never elapse.
64
+ */
65
+ export declare function sleepUnlessAborted(ms: number, signal: AbortSignal | undefined): Promise<void>;
66
+ /** Resolve `true` after `ms`, or `false` the moment `signal` aborts. */
67
+ export declare function waitUnlessAborted(ms: number, signal: AbortSignal | undefined): Promise<boolean>;
68
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../../sdk/src/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,aAAgB,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,KAAK,GAAE,MAAmB,GACzB,MAAM,GAAG,IAAI,CAaf;AAED,gEAAgE;AAChE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAEnE;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wEAAwE;AACxE,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,OAAO,CAAC,CAQlB"}