@pellux/goodvibes-transport-http 0.18.3 → 0.30.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"reconnect.d.ts","sourceRoot":"","sources":["../src/reconnect.ts"],"names":[],"mappings":"AACA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE3H,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,6BAA8B,SAAQ,qBAAqB;IAC1E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,eAAO,MAAM,+BAA+B,EAAE,6BAM7C,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,MAAM,CAAC,EAAE,qBAAqB,GAC7B,6BAA6B,CAM/B;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,6BAA6B,GACpC,MAAM,CAER"}
1
+ {"version":3,"file":"reconnect.d.ts","sourceRoot":"","sources":["../src/reconnect.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE3H,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,6BAA8B,SAAQ,qBAAqB;IAC1E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,gGAAgG;AAChG,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAE9C,eAAO,MAAM,+BAA+B,EAAE,6BAM7C,CAAC;AAEF,wBAAgB,8BAA8B,CAC5C,MAAM,CAAC,EAAE,qBAAqB,GAC7B,6BAA6B,CAM/B;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,6BAA6B,GACpC,MAAM,CAER"}
package/dist/reconnect.js CHANGED
@@ -1,10 +1,11 @@
1
- // Synced from goodvibes-tui/src/runtime/transports/stream-reconnect.ts
2
1
  import { computeBackoffDelay, normalizeBackoffPolicy } from './backoff.js';
2
+ /** Maximum reconnect attempts when reconnect is enabled and the caller does not set a limit. */
3
+ export const DEFAULT_STREAM_MAX_ATTEMPTS = 10;
3
4
  export const DEFAULT_STREAM_RECONNECT_POLICY = {
4
5
  enabled: false,
5
- maxAttempts: Number.POSITIVE_INFINITY,
6
+ maxAttempts: DEFAULT_STREAM_MAX_ATTEMPTS,
6
7
  baseDelayMs: 500,
7
- maxDelayMs: 5_000,
8
+ maxDelayMs: 30_000,
8
9
  backoffFactor: 2,
9
10
  };
10
11
  export function normalizeStreamReconnectPolicy(policy) {
package/dist/retry.d.ts CHANGED
@@ -1,17 +1,32 @@
1
1
  import { type BackoffPolicy, type ResolvedBackoffPolicy } from './backoff.js';
2
+ export interface PerMethodRetryPolicy {
3
+ readonly maxAttempts?: number;
4
+ readonly baseDelayMs?: number;
5
+ readonly maxDelayMs?: number;
6
+ readonly backoffFactor?: number;
7
+ }
2
8
  export interface HttpRetryPolicy extends BackoffPolicy {
3
9
  readonly retryOnStatuses?: readonly number[];
4
10
  readonly retryOnMethods?: readonly string[];
5
11
  readonly retryOnNetworkError?: boolean;
12
+ /** Per-method retry policy overrides keyed by method ID. */
13
+ readonly perMethodPolicy?: Readonly<Record<string, PerMethodRetryPolicy>>;
6
14
  }
7
15
  export interface ResolvedHttpRetryPolicy extends ResolvedBackoffPolicy {
8
16
  readonly retryOnStatuses: readonly number[];
9
17
  readonly retryOnMethods: readonly string[];
10
18
  readonly retryOnNetworkError: boolean;
19
+ /** Per-method retry policy overrides keyed by method ID. */
20
+ readonly perMethodPolicy: Readonly<Record<string, PerMethodRetryPolicy>>;
11
21
  }
12
22
  export declare const DEFAULT_HTTP_RETRY_POLICY: ResolvedHttpRetryPolicy;
13
23
  export declare function normalizeHttpRetryPolicy(policy?: HttpRetryPolicy): ResolvedHttpRetryPolicy;
14
24
  export declare function resolveHttpRetryPolicy(defaultPolicy?: HttpRetryPolicy, override?: false | HttpRetryPolicy): ResolvedHttpRetryPolicy;
25
+ /**
26
+ * Resolve a per-method retry policy override by method ID.
27
+ * Returns the base policy with overrides applied, or the base policy unchanged.
28
+ */
29
+ export declare function applyPerMethodPolicy(base: ResolvedHttpRetryPolicy, methodId: string): ResolvedHttpRetryPolicy;
15
30
  export declare function getHttpRetryDelay(attempt: number, policy: ResolvedHttpRetryPolicy): number;
16
31
  export declare function isRetryableHttpStatus(method: string, status: number, policy: ResolvedHttpRetryPolicy): boolean;
17
32
  export declare function isRetryableNetworkError(method: string, policy: ResolvedHttpRetryPolicy): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AACA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE3H,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,uBAAwB,SAAQ,qBAAqB;IACpE,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;CACvC;AAED,eAAO,MAAM,yBAAyB,EAAE,uBAQvC,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,MAAM,CAAC,EAAE,eAAe,GACvB,uBAAuB,CAQzB;AAED,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,eAAe,EAC/B,QAAQ,CAAC,EAAE,KAAK,GAAG,eAAe,GACjC,uBAAuB,CAazB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,uBAAuB,GAC9B,MAAM,CAER;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAGT;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAET"}
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AACA,OAAO,EAA+C,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE3H,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,QAAQ,CAAC,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CAC3E;AAED,MAAM,WAAW,uBAAwB,SAAQ,qBAAqB;IACpE,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,4DAA4D;IAC5D,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;CAC1E;AAED,eAAO,MAAM,yBAAyB,EAAE,uBASvC,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,MAAM,CAAC,EAAE,eAAe,GACvB,uBAAuB,CASzB;AAED,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,eAAe,EAC/B,QAAQ,CAAC,EAAE,KAAK,GAAG,eAAe,GACjC,uBAAuB,CAczB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,uBAAuB,EAC7B,QAAQ,EAAE,MAAM,GACf,uBAAuB,CAUzB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,uBAAuB,GAC9B,MAAM,CAER;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAGT;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAET"}
package/dist/retry.js CHANGED
@@ -1,13 +1,14 @@
1
- // Synced from goodvibes-tui/src/runtime/transports/http-retry.ts
1
+ import { RETRYABLE_STATUS_CODES } from '@pellux/goodvibes-errors';
2
2
  import { computeBackoffDelay, normalizeBackoffPolicy } from './backoff.js';
3
3
  export const DEFAULT_HTTP_RETRY_POLICY = {
4
4
  maxAttempts: 1,
5
5
  baseDelayMs: 250,
6
6
  maxDelayMs: 2_000,
7
7
  backoffFactor: 2,
8
- retryOnStatuses: [408, 429, 500, 502, 503, 504],
8
+ retryOnStatuses: RETRYABLE_STATUS_CODES,
9
9
  retryOnMethods: ['GET', 'HEAD', 'OPTIONS'],
10
10
  retryOnNetworkError: true,
11
+ perMethodPolicy: {},
11
12
  };
12
13
  export function normalizeHttpRetryPolicy(policy) {
13
14
  const normalized = normalizeBackoffPolicy(policy, DEFAULT_HTTP_RETRY_POLICY);
@@ -16,6 +17,7 @@ export function normalizeHttpRetryPolicy(policy) {
16
17
  retryOnStatuses: [...(policy?.retryOnStatuses ?? DEFAULT_HTTP_RETRY_POLICY.retryOnStatuses)],
17
18
  retryOnMethods: [...(policy?.retryOnMethods ?? DEFAULT_HTTP_RETRY_POLICY.retryOnMethods)].map((method) => method.toUpperCase()),
18
19
  retryOnNetworkError: policy?.retryOnNetworkError ?? DEFAULT_HTTP_RETRY_POLICY.retryOnNetworkError,
20
+ perMethodPolicy: policy?.perMethodPolicy ?? DEFAULT_HTTP_RETRY_POLICY.perMethodPolicy,
19
21
  };
20
22
  }
21
23
  export function resolveHttpRetryPolicy(defaultPolicy, override) {
@@ -31,6 +33,23 @@ export function resolveHttpRetryPolicy(defaultPolicy, override) {
31
33
  retryOnStatuses: override.retryOnStatuses ? [...override.retryOnStatuses] : base.retryOnStatuses,
32
34
  retryOnMethods: override.retryOnMethods ? override.retryOnMethods.map((method) => method.toUpperCase()) : base.retryOnMethods,
33
35
  retryOnNetworkError: override.retryOnNetworkError ?? base.retryOnNetworkError,
36
+ perMethodPolicy: override.perMethodPolicy ?? base.perMethodPolicy,
37
+ };
38
+ }
39
+ /**
40
+ * Resolve a per-method retry policy override by method ID.
41
+ * Returns the base policy with overrides applied, or the base policy unchanged.
42
+ */
43
+ export function applyPerMethodPolicy(base, methodId) {
44
+ const override = base.perMethodPolicy[methodId];
45
+ if (!override)
46
+ return base;
47
+ return {
48
+ ...base,
49
+ maxAttempts: override.maxAttempts ?? base.maxAttempts,
50
+ baseDelayMs: override.baseDelayMs ?? base.baseDelayMs,
51
+ maxDelayMs: override.maxDelayMs ?? base.maxDelayMs,
52
+ backoffFactor: override.backoffFactor ?? base.backoffFactor,
34
53
  };
35
54
  }
36
55
  export function getHttpRetryDelay(attempt, policy) {
@@ -1,5 +1,6 @@
1
1
  import { type AuthTokenResolver } from './auth.js';
2
2
  import { type StreamReconnectPolicy } from './reconnect.js';
3
+ import { isAbortError } from '@pellux/goodvibes-transport-core';
3
4
  export interface ServerSentEventHandlers {
4
5
  readonly onEvent?: (eventName: string, payload: unknown) => void;
5
6
  readonly onReady?: (payload: unknown) => void;
@@ -8,6 +9,11 @@ export interface ServerSentEventHandlers {
8
9
  readonly attempt: number;
9
10
  readonly delayMs: number;
10
11
  }) => void;
12
+ readonly onClose?: () => void;
13
+ readonly onTerminate?: (input: {
14
+ readonly error: unknown;
15
+ readonly reconnectAttempts: number;
16
+ }) => void;
11
17
  }
12
18
  export interface ServerSentEventOptions {
13
19
  readonly signal?: AbortSignal;
@@ -17,6 +23,7 @@ export interface ServerSentEventOptions {
17
23
  readonly lastEventId?: string | null;
18
24
  readonly reconnect?: StreamReconnectPolicy;
19
25
  }
20
- export declare function isAbortError(error: unknown): boolean;
21
- export declare function openServerSentEventStream(fetchImpl: typeof fetch, url: string, handlers: ServerSentEventHandlers, options?: ServerSentEventOptions): Promise<() => void>;
26
+ export { isAbortError };
27
+ export { openRawServerSentEventStream as openServerSentEventStream };
28
+ export declare function openRawServerSentEventStream(fetchImpl: typeof fetch, url: string, handlers: ServerSentEventHandlers, options?: ServerSentEventOptions): Promise<() => void>;
22
29
  //# sourceMappingURL=sse-stream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sse-stream.d.ts","sourceRoot":"","sources":["../src/sse-stream.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkC,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AAGxB,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAChG;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,qBAAqB,CAAC;CAC5C;AAWD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASpD;AA8BD,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,OAAO,KAAK,EACvB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,uBAAuB,EACjC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,IAAI,CAAC,CAkLrB"}
1
+ {"version":3,"file":"sse-stream.d.ts","sourceRoot":"","sources":["../src/sse-stream.ts"],"names":[],"mappings":"AACA,OAAO,EAAkC,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAGhE,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5C,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CACzG;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,qBAAqB,CAAC;CAC5C;AAYD,OAAO,EAAE,YAAY,EAAE,CAAC;AAwCxB,OAAO,EAAE,4BAA4B,IAAI,yBAAyB,EAAE,CAAC;AAMrE,wBAAsB,4BAA4B,CAChD,SAAS,EAAE,OAAO,KAAK,EACvB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,uBAAuB,EACjC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,IAAI,CAAC,CAyLrB"}
@@ -1,46 +1,58 @@
1
- // Synced from goodvibes-tui/src/runtime/transports/sse-stream.ts
2
1
  import { sleepWithSignal } from './backoff.js';
3
2
  import { mergeHeaders, resolveAuthToken } from './auth.js';
4
3
  import { getStreamReconnectDelay, normalizeStreamReconnectPolicy, } from './reconnect.js';
4
+ import { createHttpStatusError, HttpStatusError } from '@pellux/goodvibes-errors';
5
+ import { isAbortError } from '@pellux/goodvibes-transport-core';
5
6
  function readEventPayload(data) {
6
7
  if (!data.trim())
7
8
  return null;
8
9
  try {
9
10
  return JSON.parse(data);
10
11
  }
11
- catch {
12
+ catch (error) {
13
+ void error;
12
14
  return data;
13
15
  }
14
16
  }
15
- export function isAbortError(error) {
16
- return (error instanceof DOMException && error.name === 'AbortError') || (typeof error === 'object'
17
- && error !== null
18
- && 'name' in error
19
- && error.name === 'AbortError');
20
- }
17
+ export { isAbortError };
21
18
  function createStreamError(status, url, body) {
22
19
  const message = body.trim()
23
20
  ? `Unable to open SSE stream: ${status} ${body}`.trim()
24
21
  : `Unable to open SSE stream: ${status}`;
25
- return Object.assign(new Error(message), {
26
- transport: {
27
- status,
28
- body,
22
+ const error = status > 0
23
+ ? status >= 500
24
+ ? new HttpStatusError(message, {
25
+ status,
26
+ category: 'network',
27
+ source: 'transport',
28
+ recoverable: true,
29
+ url,
30
+ method: 'GET',
31
+ body,
32
+ })
33
+ : createHttpStatusError(status, url, 'GET', body)
34
+ : new HttpStatusError(message, {
35
+ status: undefined,
36
+ category: 'network',
37
+ source: 'transport',
38
+ recoverable: true,
29
39
  url,
30
40
  method: 'GET',
31
- },
32
- });
41
+ body,
42
+ });
43
+ const transportPayload = {
44
+ status,
45
+ body,
46
+ url,
47
+ method: 'GET',
48
+ };
49
+ return Object.assign(error, { transport: transportPayload });
33
50
  }
51
+ export { openRawServerSentEventStream as openServerSentEventStream };
34
52
  function reportStreamError(error, handlers) {
35
- if (handlers.onError) {
36
- handlers.onError(error);
37
- return;
38
- }
39
- queueMicrotask(() => {
40
- throw error;
41
- });
53
+ handlers.onError?.(error);
42
54
  }
43
- export async function openServerSentEventStream(fetchImpl, url, handlers, options = {}) {
55
+ export async function openRawServerSentEventStream(fetchImpl, url, handlers, options = {}) {
44
56
  const outerController = new AbortController();
45
57
  const reconnectPolicy = normalizeStreamReconnectPolicy(options.reconnect);
46
58
  let lastEventId = options.lastEventId ?? null;
@@ -173,6 +185,7 @@ export async function openServerSentEventStream(fetchImpl, url, handlers, option
173
185
  try {
174
186
  await runConnection();
175
187
  reconnectAttempts = 0;
188
+ handlers.onClose?.();
176
189
  return;
177
190
  }
178
191
  catch (error) {
@@ -180,13 +193,15 @@ export async function openServerSentEventStream(fetchImpl, url, handlers, option
180
193
  return;
181
194
  }
182
195
  const nextAttempt = reconnectAttempts + 1;
183
- const shouldReconnect = reconnectPolicy.enabled && nextAttempt < reconnectPolicy.maxAttempts;
196
+ const shouldReconnect = reconnectPolicy.enabled && nextAttempt <= reconnectPolicy.maxAttempts;
184
197
  if (!shouldReconnect) {
198
+ handlers.onTerminate?.({ error, reconnectAttempts: nextAttempt });
185
199
  reportStreamError(error, handlers);
186
200
  return;
187
201
  }
188
202
  reconnectAttempts = nextAttempt;
189
- const delayMs = getStreamReconnectDelay(nextAttempt + 1, reconnectPolicy);
203
+ // Use the same 1-based attempt counter as the WS connector for a symmetric schedule.
204
+ const delayMs = getStreamReconnectDelay(nextAttempt, reconnectPolicy);
190
205
  handlers.onReconnect?.({ attempt: nextAttempt, delayMs });
191
206
  handlers.onError?.(error);
192
207
  try {
@@ -201,7 +216,11 @@ export async function openServerSentEventStream(fetchImpl, url, handlers, option
201
216
  }
202
217
  }
203
218
  };
204
- void loop();
219
+ void loop().catch((error) => {
220
+ if (!isAbortError(error)) {
221
+ reportStreamError(error, handlers);
222
+ }
223
+ });
205
224
  });
206
225
  await readyPromise;
207
226
  return () => {
package/dist/sse.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type ServerSentEventHandlers, type ServerSentEventOptions as CoreServerSentEventOptions } from './sse-stream.js';
2
- import { type HttpTransport } from './http.js';
2
+ import type { HttpTransport } from './http.js';
3
3
  export type { ServerSentEventHandlers };
4
4
  export interface ServerSentEventOptions extends Omit<CoreServerSentEventOptions, 'authToken'> {
5
5
  }
package/dist/sse.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8D,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,IAAI,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACtL,OAAO,EAAE,KAAK,aAAa,EAA2B,MAAM,WAAW,CAAC;AAExE,YAAY,EAAE,uBAAuB,EAAE,CAAC;AACxC,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAAC,0BAA0B,EAAE,WAAW,CAAC;CAAG;AAEhG,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,uBAAuB,EACjC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,IAAI,CAAC,CAarB"}
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,uBAAuB,EAAE,KAAK,sBAAsB,IAAI,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACxJ,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,YAAY,EAAE,uBAAuB,EAAE,CAAC;AACxC,MAAM,WAAW,sBAAuB,SAAQ,IAAI,CAAC,0BAA0B,EAAE,WAAW,CAAC;CAAG;AAEhG,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,uBAAuB,EACjC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,IAAI,CAAC,CASrB"}
package/dist/sse.js CHANGED
@@ -1,17 +1,11 @@
1
- import { openServerSentEventStream as openServerSentEventStreamCore } from './sse-stream.js';
2
- import { normalizeTransportError } from './http.js';
1
+ import { openRawServerSentEventStream } from './sse-stream.js';
3
2
  export async function openServerSentEventStream(transport, pathOrUrl, handlers, options = {}) {
4
3
  const url = pathOrUrl.startsWith('http://') || pathOrUrl.startsWith('https://')
5
4
  ? pathOrUrl
6
5
  : transport.buildUrl(pathOrUrl);
7
- try {
8
- return await openServerSentEventStreamCore(transport.fetchImpl, url, handlers, {
9
- ...options,
10
- authToken: transport.authToken,
11
- getAuthToken: transport.getAuthToken.bind(transport),
12
- });
13
- }
14
- catch (error) {
15
- throw normalizeTransportError(error);
16
- }
6
+ return await openRawServerSentEventStream(transport.fetchImpl, url, handlers, {
7
+ ...options,
8
+ authToken: transport.authToken,
9
+ getAuthToken: transport.getAuthToken.bind(transport),
10
+ });
17
11
  }
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-transport-http",
3
- "version": "0.18.3",
3
+ "version": "0.30.0",
4
+ "engines": {
5
+ "node": ">=20.0.0"
6
+ },
4
7
  "description": "HTTP, JSON, path, and SSE transport primitives for GoodVibes client integrations.",
5
8
  "type": "module",
6
9
  "main": "./dist/index.js",
@@ -10,6 +13,50 @@
10
13
  "types": "./dist/index.d.ts",
11
14
  "import": "./dist/index.js"
12
15
  },
16
+ "./auth": {
17
+ "types": "./dist/auth.d.ts",
18
+ "import": "./dist/auth.js"
19
+ },
20
+ "./backoff": {
21
+ "types": "./dist/backoff.d.ts",
22
+ "import": "./dist/backoff.js"
23
+ },
24
+ "./contract-client": {
25
+ "types": "./dist/contract-client.d.ts",
26
+ "import": "./dist/contract-client.js"
27
+ },
28
+ "./client-plumbing": {
29
+ "types": "./dist/client-plumbing.d.ts",
30
+ "import": "./dist/client-plumbing.js"
31
+ },
32
+ "./http-core": {
33
+ "types": "./dist/http-core.d.ts",
34
+ "import": "./dist/http-core.js"
35
+ },
36
+ "./http": {
37
+ "types": "./dist/http.d.ts",
38
+ "import": "./dist/http.js"
39
+ },
40
+ "./paths": {
41
+ "types": "./dist/paths.d.ts",
42
+ "import": "./dist/paths.js"
43
+ },
44
+ "./reconnect": {
45
+ "types": "./dist/reconnect.d.ts",
46
+ "import": "./dist/reconnect.js"
47
+ },
48
+ "./retry": {
49
+ "types": "./dist/retry.d.ts",
50
+ "import": "./dist/retry.js"
51
+ },
52
+ "./sse-stream": {
53
+ "types": "./dist/sse-stream.d.ts",
54
+ "import": "./dist/sse-stream.js"
55
+ },
56
+ "./sse": {
57
+ "types": "./dist/sse.d.ts",
58
+ "import": "./dist/sse.js"
59
+ },
13
60
  "./package.json": "./package.json"
14
61
  },
15
62
  "files": [
@@ -32,10 +79,12 @@
32
79
  "sse",
33
80
  "transport"
34
81
  ],
82
+ "dependencies": {
83
+ "@pellux/goodvibes-errors": "0.30.0",
84
+ "@pellux/goodvibes-transport-core": "0.30.0",
85
+ "zod": "^4.3.6"
86
+ },
35
87
  "publishConfig": {
36
88
  "access": "public"
37
- },
38
- "dependencies": {
39
- "@pellux/goodvibes-errors": "0.18.3"
40
89
  }
41
90
  }