@remit/api-http-client 0.0.20 → 0.0.22

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.
@@ -14,8 +14,8 @@ import type { Client, ClientOptions, Config, RequestOptions } from './types.gen.
14
14
  export const createQuerySerializer = <T = unknown>({
15
15
  parameters = {},
16
16
  ...args
17
- }: QuerySerializerOptions = {}) => {
18
- const querySerializer = (queryParams: T) => {
17
+ }: QuerySerializerOptions = {}): ((queryParams: T) => string) => {
18
+ const querySerializer = (queryParams: T): string => {
19
19
  const search: string[] = [];
20
20
  if (queryParams && typeof queryParams === 'object') {
21
21
  for (const name in queryParams) {
@@ -65,9 +65,7 @@ export const createQuerySerializer = <T = unknown>({
65
65
  /**
66
66
  * Infers parseAs value from provided Content-Type header.
67
67
  */
68
- export const getParseAs = (
69
- contentType: string | null,
70
- ): Exclude<Config['parseAs'], 'auto'> => {
68
+ export const getParseAs = (contentType: string | null): Exclude<Config['parseAs'], 'auto'> => {
71
69
  if (!contentType) {
72
70
  // If no Content-Type header is provided, the best we can do is return the raw response body,
73
71
  // which is effectively the same as the 'stream' option.
@@ -80,10 +78,7 @@ export const getParseAs = (
80
78
  return;
81
79
  }
82
80
 
83
- if (
84
- cleanContent.startsWith('application/json') ||
85
- cleanContent.endsWith('+json')
86
- ) {
81
+ if (cleanContent.startsWith('application/json') || cleanContent.endsWith('+json')) {
87
82
  return 'json';
88
83
  }
89
84
 
@@ -92,9 +87,7 @@ export const getParseAs = (
92
87
  }
93
88
 
94
89
  if (
95
- ['application/', 'audio/', 'image/', 'video/'].some((type) =>
96
- cleanContent.startsWith(type),
97
- )
90
+ ['application/', 'audio/', 'image/', 'video/'].some((type) => cleanContent.startsWith(type))
98
91
  ) {
99
92
  return 'blob';
100
93
  }
@@ -125,14 +118,12 @@ const checkForExistence = (
125
118
  return false;
126
119
  };
127
120
 
128
- export const setAuthParams = async ({
129
- security,
130
- ...options
131
- }: Pick<Required<RequestOptions>, 'security'> &
132
- Pick<RequestOptions, 'auth' | 'query'> & {
121
+ export async function setAuthParams(
122
+ options: Pick<RequestOptions, 'auth' | 'query' | 'security'> & {
133
123
  headers: Headers;
134
- }) => {
135
- for (const auth of security) {
124
+ },
125
+ ): Promise<void> {
126
+ for (const auth of options.security ?? []) {
136
127
  if (checkForExistence(options, auth.name)) {
137
128
  continue;
138
129
  }
@@ -161,7 +152,7 @@ export const setAuthParams = async ({
161
152
  break;
162
153
  }
163
154
  }
164
- };
155
+ }
165
156
 
166
157
  export const buildUrl: Client['buildUrl'] = (options) =>
167
158
  getUrl({
@@ -201,10 +192,7 @@ export const mergeHeaders = (
201
192
  continue;
202
193
  }
203
194
 
204
- const iterator =
205
- header instanceof Headers
206
- ? headersEntries(header)
207
- : Object.entries(header);
195
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
208
196
 
209
197
  for (const [key, value] of iterator) {
210
198
  if (value === null) {
@@ -214,7 +202,7 @@ export const mergeHeaders = (
214
202
  mergedHeaders.append(key, v as string);
215
203
  }
216
204
  } else if (value !== undefined) {
217
- // assume object headers are meant to be JSON stringified, i.e. their
205
+ // assume object headers are meant to be JSON stringified, i.e., their
218
206
  // content value in OpenAPI specification is 'application/json'
219
207
  mergedHeaders.set(
220
208
  key,
@@ -228,15 +216,14 @@ export const mergeHeaders = (
228
216
 
229
217
  type ErrInterceptor<Err, Res, Req, Options> = (
230
218
  error: Err,
231
- response: Res,
232
- request: Req,
219
+ /** response may be undefined due to a network error where no response object is produced */
220
+ response: Res | undefined,
221
+ /** request may be undefined, because error may be from building the request object itself */
222
+ request: Req | undefined,
233
223
  options: Options,
234
224
  ) => Err | Promise<Err>;
235
225
 
236
- type ReqInterceptor<Req, Options> = (
237
- request: Req,
238
- options: Options,
239
- ) => Req | Promise<Req>;
226
+ type ReqInterceptor<Req, Options> = (request: Req, options: Options) => Req | Promise<Req>;
240
227
 
241
228
  type ResInterceptor<Res, Req, Options> = (
242
229
  response: Res,
@@ -270,10 +257,7 @@ class Interceptors<Interceptor> {
270
257
  return this.fns.indexOf(id);
271
258
  }
272
259
 
273
- update(
274
- id: number | Interceptor,
275
- fn: Interceptor,
276
- ): number | Interceptor | false {
260
+ update(id: number | Interceptor, fn: Interceptor): number | Interceptor | false {
277
261
  const index = this.getInterceptorIndex(id);
278
262
  if (this.fns[index]) {
279
263
  this.fns[index] = fn;
package/client.gen.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // This file is auto-generated by @hey-api/openapi-ts
2
2
 
3
- import { type ClientOptions, type Config, createClient, createConfig } from './client/index.js';
3
+ import { type Client, type ClientOptions, type Config, createClient, createConfig } from './client/index.js';
4
4
  import type { ClientOptions as ClientOptions2 } from './types.gen.js';
5
5
 
6
6
  /**
@@ -13,4 +13,4 @@ import type { ClientOptions as ClientOptions2 } from './types.gen.js';
13
13
  */
14
14
  export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T>;
15
15
 
16
- export const client = createClient(createConfig<ClientOptions2>({ baseUrl: 'https://api.remit.example.com' }));
16
+ export const client: Client = createClient(createConfig<ClientOptions2>({ baseUrl: 'https://api.remit.example.com' }));
package/core/auth.gen.ts CHANGED
@@ -9,6 +9,13 @@ export interface Auth {
9
9
  * @default 'header'
10
10
  */
11
11
  in?: 'header' | 'query' | 'cookie';
12
+ /**
13
+ * A unique identifier for the security scheme.
14
+ *
15
+ * Defined only when there are multiple security schemes whose `Auth`
16
+ * shape would otherwise be identical.
17
+ */
18
+ key?: string;
12
19
  /**
13
20
  * Header or query parameter name.
14
21
  *
@@ -23,8 +30,7 @@ export const getAuthToken = async (
23
30
  auth: Auth,
24
31
  callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken,
25
32
  ): Promise<string | undefined> => {
26
- const token =
27
- typeof callback === 'function' ? await callback(auth) : callback;
33
+ const token = typeof callback === 'function' ? await callback(auth) : callback;
28
34
 
29
35
  if (!token) {
30
36
  return;
@@ -1,14 +1,10 @@
1
1
  // This file is auto-generated by @hey-api/openapi-ts
2
2
 
3
- import type {
4
- ArrayStyle,
5
- ObjectStyle,
6
- SerializerOptions,
7
- } from './pathSerializer.gen.js';
3
+ import type { ArrayStyle, ObjectStyle, SerializerOptions } from './pathSerializer.gen.js';
8
4
 
9
5
  export type QuerySerializer = (query: Record<string, unknown>) => string;
10
6
 
11
- export type BodySerializer = (body: any) => any;
7
+ export type BodySerializer = (body: unknown) => unknown;
12
8
 
13
9
  type QuerySerializerOptionsObject = {
14
10
  allowReserved?: boolean;
@@ -24,11 +20,7 @@ export type QuerySerializerOptions = QuerySerializerOptionsObject & {
24
20
  parameters?: Record<string, QuerySerializerOptionsObject>;
25
21
  };
26
22
 
27
- const serializeFormDataPair = (
28
- data: FormData,
29
- key: string,
30
- value: unknown,
31
- ): void => {
23
+ const serializeFormDataPair = (data: FormData, key: string, value: unknown): void => {
32
24
  if (typeof value === 'string' || value instanceof Blob) {
33
25
  data.append(key, value);
34
26
  } else if (value instanceof Date) {
@@ -38,11 +30,7 @@ const serializeFormDataPair = (
38
30
  }
39
31
  };
40
32
 
41
- const serializeUrlSearchParamsPair = (
42
- data: URLSearchParams,
43
- key: string,
44
- value: unknown,
45
- ): void => {
33
+ const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value: unknown): void => {
46
34
  if (typeof value === 'string') {
47
35
  data.append(key, value);
48
36
  } else {
@@ -51,12 +39,10 @@ const serializeUrlSearchParamsPair = (
51
39
  };
52
40
 
53
41
  export const formDataBodySerializer = {
54
- bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(
55
- body: T,
56
- ): FormData => {
42
+ bodySerializer: (body: unknown): FormData => {
57
43
  const data = new FormData();
58
44
 
59
- Object.entries(body).forEach(([key, value]) => {
45
+ Object.entries(body as Record<string, unknown>).forEach(([key, value]) => {
60
46
  if (value === undefined || value === null) {
61
47
  return;
62
48
  }
@@ -72,19 +58,15 @@ export const formDataBodySerializer = {
72
58
  };
73
59
 
74
60
  export const jsonBodySerializer = {
75
- bodySerializer: <T>(body: T): string =>
76
- JSON.stringify(body, (_key, value) =>
77
- typeof value === 'bigint' ? value.toString() : value,
78
- ),
61
+ bodySerializer: (body: unknown): string =>
62
+ JSON.stringify(body, (_key, value) => (typeof value === 'bigint' ? value.toString() : value)),
79
63
  };
80
64
 
81
65
  export const urlSearchParamsBodySerializer = {
82
- bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(
83
- body: T,
84
- ): string => {
66
+ bodySerializer: (body: unknown): string => {
85
67
  const data = new URLSearchParams();
86
68
 
87
- Object.entries(body).forEach(([key, value]) => {
69
+ Object.entries(body as Record<string, unknown>).forEach(([key, value]) => {
88
70
  if (value === undefined || value === null) {
89
71
  return;
90
72
  }
@@ -62,7 +62,7 @@ type KeyMap = Map<
62
62
  }
63
63
  >;
64
64
 
65
- const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
65
+ function buildKeyMap(fields: FieldsConfig, map?: KeyMap): KeyMap {
66
66
  if (!map) {
67
67
  map = new Map();
68
68
  }
@@ -85,36 +85,42 @@ const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
85
85
  }
86
86
 
87
87
  return map;
88
- };
88
+ }
89
89
 
90
90
  interface Params {
91
- body: unknown;
91
+ body?: unknown;
92
92
  headers: Record<string, unknown>;
93
93
  path: Record<string, unknown>;
94
94
  query: Record<string, unknown>;
95
95
  }
96
96
 
97
- const stripEmptySlots = (params: Params) => {
97
+ function stripEmptySlots(params: Params): void {
98
98
  for (const [slot, value] of Object.entries(params)) {
99
- if (value && typeof value === 'object' && !Object.keys(value).length) {
99
+ if (slot === 'body') continue;
100
+ if (value && typeof value === 'object' && !Array.isArray(value) && !Object.keys(value).length) {
100
101
  delete params[slot as Slot];
101
102
  }
102
103
  }
103
- };
104
+ }
104
105
 
105
- export const buildClientParams = (
106
- args: ReadonlyArray<unknown>,
107
- fields: FieldsConfig,
108
- ) => {
106
+ export function buildClientParams(args: ReadonlyArray<unknown>, fields: FieldsConfig): Params {
109
107
  const params: Params = {
110
- body: {},
111
- headers: {},
112
- path: {},
113
- query: {},
108
+ headers: Object.create(null),
109
+ path: Object.create(null),
110
+ query: Object.create(null),
114
111
  };
115
112
 
116
113
  const map = buildKeyMap(fields);
117
114
 
115
+ function writeSlot(slot: Slot, key: string, value: unknown): void {
116
+ let record = params[slot] as Record<string, unknown> | undefined;
117
+ if (record === undefined) {
118
+ record = Object.create(null) as Record<string, unknown>;
119
+ params[slot] = record;
120
+ }
121
+ record[key] = value;
122
+ }
123
+
118
124
  let config: FieldsConfig[number] | undefined;
119
125
 
120
126
  for (const [index, arg] of args.entries()) {
@@ -131,7 +137,7 @@ export const buildClientParams = (
131
137
  const field = map.get(config.key)!;
132
138
  const name = field.map || config.key;
133
139
  if (field.in) {
134
- (params[field.in] as Record<string, unknown>)[name] = arg;
140
+ writeSlot(field.in, name, arg);
135
141
  }
136
142
  } else {
137
143
  params.body = arg;
@@ -143,24 +149,20 @@ export const buildClientParams = (
143
149
  if (field) {
144
150
  if (field.in) {
145
151
  const name = field.map || key;
146
- (params[field.in] as Record<string, unknown>)[name] = value;
152
+ writeSlot(field.in, name, value);
147
153
  } else {
148
154
  params[field.map] = value;
149
155
  }
150
156
  } else {
151
- const extra = extraPrefixes.find(([prefix]) =>
152
- key.startsWith(prefix),
153
- );
157
+ const extra = extraPrefixes.find(([prefix]) => key.startsWith(prefix));
154
158
 
155
159
  if (extra) {
156
160
  const [prefix, slot] = extra;
157
- (params[slot] as Record<string, unknown>)[
158
- key.slice(prefix.length)
159
- ] = value;
161
+ writeSlot(slot, key.slice(prefix.length), value);
160
162
  } else if ('allowExtra' in config && config.allowExtra) {
161
163
  for (const [slot, allowed] of Object.entries(config.allowExtra)) {
162
164
  if (allowed) {
163
- (params[slot as Slot] as Record<string, unknown>)[key] = value;
165
+ writeSlot(slot as Slot, key, value);
164
166
  break;
165
167
  }
166
168
  }
@@ -173,4 +175,4 @@ export const buildClientParams = (
173
175
  stripEmptySlots(params);
174
176
 
175
177
  return params;
176
- };
178
+ }
@@ -1,8 +1,6 @@
1
1
  // This file is auto-generated by @hey-api/openapi-ts
2
2
 
3
- interface SerializeOptions<T>
4
- extends SerializePrimitiveOptions,
5
- SerializerOptions<T> {}
3
+ interface SerializeOptions<T> extends SerializePrimitiveOptions, SerializerOptions<T> {}
6
4
 
7
5
  interface SerializePrimitiveOptions {
8
6
  allowReserved?: boolean;
@@ -27,7 +25,7 @@ interface SerializePrimitiveParam extends SerializePrimitiveOptions {
27
25
  value: string;
28
26
  }
29
27
 
30
- export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
28
+ export const separatorArrayExplode = (style: ArraySeparatorStyle): '.' | ';' | ',' | '&' => {
31
29
  switch (style) {
32
30
  case 'label':
33
31
  return '.';
@@ -40,7 +38,7 @@ export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
40
38
  }
41
39
  };
42
40
 
43
- export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
41
+ export const separatorArrayNoExplode = (style: ArraySeparatorStyle): ',' | '|' | '%20' => {
44
42
  switch (style) {
45
43
  case 'form':
46
44
  return ',';
@@ -53,7 +51,7 @@ export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
53
51
  }
54
52
  };
55
53
 
56
- export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
54
+ export const separatorObjectExplode = (style: ObjectSeparatorStyle): '.' | ';' | ',' | '&' => {
57
55
  switch (style) {
58
56
  case 'label':
59
57
  return '.';
@@ -74,7 +72,7 @@ export const serializeArrayParam = ({
74
72
  value,
75
73
  }: SerializeOptions<ArraySeparatorStyle> & {
76
74
  value: unknown[];
77
- }) => {
75
+ }): string => {
78
76
  if (!explode) {
79
77
  const joinedValues = (
80
78
  allowReserved ? value : value.map((v) => encodeURIComponent(v as string))
@@ -105,16 +103,14 @@ export const serializeArrayParam = ({
105
103
  });
106
104
  })
107
105
  .join(separator);
108
- return style === 'label' || style === 'matrix'
109
- ? separator + joinedValues
110
- : joinedValues;
106
+ return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues;
111
107
  };
112
108
 
113
109
  export const serializePrimitiveParam = ({
114
110
  allowReserved,
115
111
  name,
116
112
  value,
117
- }: SerializePrimitiveParam) => {
113
+ }: SerializePrimitiveParam): string => {
118
114
  if (value === undefined || value === null) {
119
115
  return '';
120
116
  }
@@ -138,7 +134,7 @@ export const serializeObjectParam = ({
138
134
  }: SerializeOptions<ObjectSeparatorStyle> & {
139
135
  value: Record<string, unknown> | Date;
140
136
  valueOnly?: boolean;
141
- }) => {
137
+ }): string => {
142
138
  if (value instanceof Date) {
143
139
  return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
144
140
  }
@@ -146,11 +142,7 @@ export const serializeObjectParam = ({
146
142
  if (style !== 'deepObject' && !explode) {
147
143
  let values: string[] = [];
148
144
  Object.entries(value).forEach(([key, v]) => {
149
- values = [
150
- ...values,
151
- key,
152
- allowReserved ? (v as string) : encodeURIComponent(v as string),
153
- ];
145
+ values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)];
154
146
  });
155
147
  const joinedValues = values.join(',');
156
148
  switch (style) {
@@ -175,7 +167,5 @@ export const serializeObjectParam = ({
175
167
  }),
176
168
  )
177
169
  .join(separator);
178
- return style === 'label' || style === 'matrix'
179
- ? separator + joinedValues
180
- : joinedValues;
170
+ return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues;
181
171
  };
@@ -14,12 +14,8 @@ export type JsonValue =
14
14
  /**
15
15
  * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.
16
16
  */
17
- export const queryKeyJsonReplacer = (_key: string, value: unknown) => {
18
- if (
19
- value === undefined ||
20
- typeof value === 'function' ||
21
- typeof value === 'symbol'
22
- ) {
17
+ export const queryKeyJsonReplacer = (_key: string, value: unknown): unknown | undefined => {
18
+ if (value === undefined || typeof value === 'function' || typeof value === 'symbol') {
23
19
  return undefined;
24
20
  }
25
21
  if (typeof value === 'bigint') {
@@ -61,9 +57,7 @@ const isPlainObject = (value: unknown): value is Record<string, unknown> => {
61
57
  * Turns URLSearchParams into a sorted JSON object for deterministic keys.
62
58
  */
63
59
  const serializeSearchParams = (params: URLSearchParams): JsonValue => {
64
- const entries = Array.from(params.entries()).sort(([a], [b]) =>
65
- a.localeCompare(b),
66
- );
60
+ const entries = Array.from(params.entries()).sort(([a], [b]) => a.localeCompare(b));
67
61
  const result: Record<string, JsonValue> = {};
68
62
 
69
63
  for (const [key, value] of entries) {
@@ -86,26 +80,16 @@ const serializeSearchParams = (params: URLSearchParams): JsonValue => {
86
80
  /**
87
81
  * Normalizes any accepted value into a JSON-friendly shape for query keys.
88
82
  */
89
- export const serializeQueryKeyValue = (
90
- value: unknown,
91
- ): JsonValue | undefined => {
83
+ export const serializeQueryKeyValue = (value: unknown): JsonValue | undefined => {
92
84
  if (value === null) {
93
85
  return null;
94
86
  }
95
87
 
96
- if (
97
- typeof value === 'string' ||
98
- typeof value === 'number' ||
99
- typeof value === 'boolean'
100
- ) {
88
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
101
89
  return value;
102
90
  }
103
91
 
104
- if (
105
- value === undefined ||
106
- typeof value === 'function' ||
107
- typeof value === 'symbol'
108
- ) {
92
+ if (value === undefined || typeof value === 'function' || typeof value === 'symbol') {
109
93
  return undefined;
110
94
  }
111
95
 
@@ -121,10 +105,7 @@ export const serializeQueryKeyValue = (
121
105
  return stringifyToJsonValue(value);
122
106
  }
123
107
 
124
- if (
125
- typeof URLSearchParams !== 'undefined' &&
126
- value instanceof URLSearchParams
127
- ) {
108
+ if (typeof URLSearchParams !== 'undefined' && value instanceof URLSearchParams) {
128
109
  return serializeSearchParams(value);
129
110
  }
130
111
 
@@ -2,10 +2,7 @@
2
2
 
3
3
  import type { Config } from './types.gen.js';
4
4
 
5
- export type ServerSentEventsOptions<TData = unknown> = Omit<
6
- RequestInit,
7
- 'method'
8
- > &
5
+ export type ServerSentEventsOptions<TData = unknown> = Omit<RequestInit, 'method'> &
9
6
  Pick<Config, 'method' | 'responseTransformer' | 'responseValidator'> & {
10
7
  /**
11
8
  * Fetch API implementation. You can use this option to provide a custom
@@ -74,11 +71,7 @@ export interface StreamEvent<TData = unknown> {
74
71
  retry?: number;
75
72
  }
76
73
 
77
- export type ServerSentEventsResult<
78
- TData = unknown,
79
- TReturn = void,
80
- TNext = unknown,
81
- > = {
74
+ export type ServerSentEventsResult<TData = unknown, TReturn = void, TNext = unknown> = {
82
75
  stream: AsyncGenerator<
83
76
  TData extends Record<string, unknown> ? TData[keyof TData] : TData,
84
77
  TReturn,
@@ -86,7 +79,7 @@ export type ServerSentEventsResult<
86
79
  >;
87
80
  };
88
81
 
89
- export const createSseClient = <TData = unknown>({
82
+ export function createSseClient<TData = unknown>({
90
83
  onRequest,
91
84
  onSseError,
92
85
  onSseEvent,
@@ -98,12 +91,10 @@ export const createSseClient = <TData = unknown>({
98
91
  sseSleepFn,
99
92
  url,
100
93
  ...options
101
- }: ServerSentEventsOptions): ServerSentEventsResult<TData> => {
94
+ }: ServerSentEventsOptions): ServerSentEventsResult<TData> {
102
95
  let lastEventId: string | undefined;
103
96
 
104
- const sleep =
105
- sseSleepFn ??
106
- ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
97
+ const sleep = sseSleepFn ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));
107
98
 
108
99
  const createStream = async function* () {
109
100
  let retryDelay: number = sseDefaultRetryDelay ?? 3000;
@@ -141,16 +132,11 @@ export const createSseClient = <TData = unknown>({
141
132
  const _fetch = options.fetch ?? globalThis.fetch;
142
133
  const response = await _fetch(request);
143
134
 
144
- if (!response.ok)
145
- throw new Error(
146
- `SSE failed: ${response.status} ${response.statusText}`,
147
- );
135
+ if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
148
136
 
149
137
  if (!response.body) throw new Error('No body in SSE response');
150
138
 
151
- const reader = response.body
152
- .pipeThrough(new TextDecoderStream())
153
- .getReader();
139
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
154
140
 
155
141
  let buffer = '';
156
142
 
@@ -169,8 +155,7 @@ export const createSseClient = <TData = unknown>({
169
155
  const { done, value } = await reader.read();
170
156
  if (done) break;
171
157
  buffer += value;
172
- // Normalize line endings: CRLF -> LF, then CR -> LF
173
- buffer = buffer.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
158
+ buffer = buffer.replace(/\r\n?/g, '\n'); // normalize line endings
174
159
 
175
160
  const chunks = buffer.split('\n\n');
176
161
  buffer = chunks.pop() ?? '';
@@ -188,10 +173,7 @@ export const createSseClient = <TData = unknown>({
188
173
  } else if (line.startsWith('id:')) {
189
174
  lastEventId = line.replace(/^id:\s*/, '');
190
175
  } else if (line.startsWith('retry:')) {
191
- const parsed = Number.parseInt(
192
- line.replace(/^retry:\s*/, ''),
193
- 10,
194
- );
176
+ const parsed = Number.parseInt(line.replace(/^retry:\s*/, ''), 10);
195
177
  if (!Number.isNaN(parsed)) {
196
178
  retryDelay = parsed;
197
179
  }
@@ -243,18 +225,12 @@ export const createSseClient = <TData = unknown>({
243
225
  // connection failed or aborted; retry after delay
244
226
  onSseError?.(error);
245
227
 
246
- if (
247
- sseMaxRetryAttempts !== undefined &&
248
- attempt >= sseMaxRetryAttempts
249
- ) {
228
+ if (sseMaxRetryAttempts !== undefined && attempt >= sseMaxRetryAttempts) {
250
229
  break; // stop after firing error
251
230
  }
252
231
 
253
232
  // exponential backoff: double retry each attempt, cap at 30s
254
- const backoff = Math.min(
255
- retryDelay * 2 ** (attempt - 1),
256
- sseMaxRetryDelay ?? 30000,
257
- );
233
+ const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 30000);
258
234
  await sleep(backoff);
259
235
  }
260
236
  }
@@ -263,4 +239,4 @@ export const createSseClient = <TData = unknown>({
263
239
  const stream = createStream();
264
240
 
265
241
  return { stream };
266
- };
242
+ }