@pellux/goodvibes-transport-core 0.30.2 → 0.33.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.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @pellux/goodvibes-transport-core
2
2
 
3
- Internal workspace package backing `@pellux/goodvibes-sdk/transport-core`.
3
+ Public GoodVibes transport-core package for shared transport, event-feed, observer, and middleware primitives.
4
4
 
5
- Consumers should install `@pellux/goodvibes-sdk` and import this surface from the umbrella package.
5
+ Most applications should install `@pellux/goodvibes-sdk` and import `@pellux/goodvibes-sdk/transport-core`. Install this package directly when you only need the transport primitives.
6
6
 
7
7
  Use this surface only when you are composing your own transport/client abstraction.
8
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOpD;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA2BpE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAOhF"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAOpD;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA2BpE;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAoBhF"}
package/dist/errors.js CHANGED
@@ -37,9 +37,22 @@ export function describeUnknownTransportError(error) {
37
37
  export function transportErrorFromUnknown(error, context) {
38
38
  if (error instanceof Error)
39
39
  return error;
40
+ const code = typeof error === 'object'
41
+ && error !== null
42
+ && typeof error.code === 'string'
43
+ ? error.code
44
+ : '';
45
+ // Include undici/Bun error codes alongside Node-style errno codes.
46
+ // recoverable is ONLY set to true for recognized network-layer error codes
47
+ // (errno values, undici codes, and 'fetch failed'). Unknown/unrecognized codes
48
+ // (including programmer errors wrapped as non-Error objects) get recoverable:false
49
+ // so they do not trigger retry loops.
50
+ const recoverable = /^(?:EAI_AGAIN|ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|EPIPE|ECONNABORTED)$/.test(code)
51
+ || /^UND_ERR_/.test(code)
52
+ || code === 'fetch failed';
40
53
  return new GoodVibesSdkError(`${context}: ${describeUnknownTransportError(error)}`, {
41
54
  category: 'network',
42
55
  source: 'transport',
43
- recoverable: true,
56
+ recoverable,
44
57
  });
45
58
  }
@@ -1,21 +1,21 @@
1
1
  export interface EventEnvelope<TType extends string, TPayload> {
2
2
  readonly type: TType;
3
3
  readonly ts: number;
4
- readonly traceId: string;
5
- readonly sessionId: string;
6
- readonly turnId?: string;
7
- readonly agentId?: string;
8
- readonly taskId?: string;
9
- readonly source: string;
4
+ readonly traceId?: string | undefined;
5
+ readonly sessionId?: string | undefined;
6
+ readonly turnId?: string | undefined;
7
+ readonly agentId?: string | undefined;
8
+ readonly taskId?: string | undefined;
9
+ readonly source?: string | undefined;
10
10
  readonly payload: TPayload;
11
11
  }
12
12
  export interface EventEnvelopeContext {
13
13
  readonly sessionId: string;
14
14
  readonly source: string;
15
- readonly traceId?: string;
16
- readonly turnId?: string;
17
- readonly agentId?: string;
18
- readonly taskId?: string;
15
+ readonly traceId?: string | undefined;
16
+ readonly turnId?: string | undefined;
17
+ readonly agentId?: string | undefined;
18
+ readonly taskId?: string | undefined;
19
19
  }
20
20
  export declare function createEventEnvelope<TType extends string, TPayload>(type: TType, payload: TPayload, context: EventEnvelopeContext): EventEnvelope<TType, TPayload>;
21
21
  //# sourceMappingURL=event-envelope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"event-envelope.d.ts","sourceRoot":"","sources":["../src/event-envelope.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ;IAC3D,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ,EAChE,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,CAYhC"}
1
+ {"version":3,"file":"event-envelope.d.ts","sourceRoot":"","sources":["../src/event-envelope.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,aAAa,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ;IAC3D,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,wBAAgB,mBAAmB,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ,EAChE,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,QAAQ,EACjB,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,KAAK,EAAE,QAAQ,CAAC,CAiBhC"}
@@ -1,9 +1,13 @@
1
- import { createUuidV4 } from './uuid.js';
2
1
  export function createEventEnvelope(type, payload, context) {
3
2
  return Object.freeze({
4
3
  type,
5
4
  ts: Date.now(),
6
- traceId: context.traceId ?? createUuidV4(),
5
+ // Callers that want multiple fan-out envelopes correlated under one trace
6
+ // must provide a shared traceId.
7
+ // default to undefined instead of generating entropy on every envelope.
8
+ // High-volume callers (telemetry, streaming deltas) should provide a shared
9
+ // traceId in context to correlate fan-out envelopes under one trace.
10
+ traceId: context.traceId,
7
11
  sessionId: context.sessionId,
8
12
  turnId: context.turnId,
9
13
  agentId: context.agentId,
@@ -1,8 +1,13 @@
1
1
  import type { EventEnvelope } from './event-envelope.js';
2
- type EventLike = {
2
+ /**
3
+ * Minimal structural constraint for runtime events. Matches the `{ type: string }` shape
4
+ * of `AnyRuntimeEvent` from `@pellux/goodvibes-contracts` without taking on that dependency.
5
+ * Re-exported so downstream packages can share the same public type identity.
6
+ */
7
+ export type EventLike = {
3
8
  readonly type: string;
4
9
  };
5
- type EventForType<TEvent extends EventLike, TType extends TEvent['type']> = Extract<TEvent, {
10
+ export type EventForType<TEvent extends EventLike, TType extends TEvent['type']> = Extract<TEvent, {
6
11
  type: TType;
7
12
  }>;
8
13
  export interface RuntimeEventFeed<TEvent extends EventLike = EventLike> {
@@ -18,5 +23,4 @@ export type RuntimeEventFeeds<TDomain extends string, TEvent extends EventLike =
18
23
  export type EnvelopeSubscriber<TEvent extends EventLike> = <TType extends TEvent['type']>(type: TType, listener: (envelope: EventEnvelope<TType, EventForType<TEvent, TType>>) => void) => () => void;
19
24
  export declare function createRuntimeEventFeed<TEvent extends EventLike>(subscribe: EnvelopeSubscriber<TEvent>): RuntimeEventFeed<TEvent>;
20
25
  export declare function createRuntimeEventFeeds<TDomain extends string, TEvent extends EventLike>(domains: readonly TDomain[], createFeed: (domain: TDomain) => RuntimeEventFeed<TEvent>): RuntimeEventFeeds<TDomain, TEvent>;
21
- export {};
22
26
  //# sourceMappingURL=event-feeds.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"event-feeds.d.ts","sourceRoot":"","sources":["../src/event-feeds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,KAAK,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C,KAAK,YAAY,CACf,MAAM,SAAS,SAAS,EACxB,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,IAC1B,OAAO,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAErC,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,SAAS,GAAG,SAAS;IACpE,EAAE,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,EAC7B,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,GACvD,MAAM,IAAI,CAAC;IACd,UAAU,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,EACrC,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,GAC9E,MAAM,IAAI,CAAC;CACf;AAED,MAAM,MAAM,iBAAiB,CAC3B,OAAO,SAAS,MAAM,EACtB,MAAM,SAAS,SAAS,GAAG,SAAS,IAClC;IACF,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;CACnD,GAAG;IACF,QAAQ,EAAE,CAAC,IAAI,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,MAAM,SAAS,SAAS,IAAI,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,EACtF,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,KAC5E,MAAM,IAAI,CAAC;AAEhB,wBAAgB,sBAAsB,CAAC,MAAM,SAAS,SAAS,EAC7D,SAAS,EAAE,kBAAkB,CAAC,MAAM,CAAC,GACpC,gBAAgB,CAAC,MAAM,CAAC,CAiB1B;AAED,wBAAgB,uBAAuB,CACrC,OAAO,SAAS,MAAM,EACtB,MAAM,SAAS,SAAS,EAExB,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,gBAAgB,CAAC,MAAM,CAAC,GACxD,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,CAYpC"}
1
+ {"version":3,"file":"event-feeds.d.ts","sourceRoot":"","sources":["../src/event-feeds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD,MAAM,MAAM,YAAY,CACtB,MAAM,SAAS,SAAS,EACxB,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,IAC1B,OAAO,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAErC,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,SAAS,GAAG,SAAS;IACpE,EAAE,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,EAC7B,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,IAAI,GACvD,MAAM,IAAI,CAAC;IACd,UAAU,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,EACrC,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,GAC9E,MAAM,IAAI,CAAC;CACf;AAED,MAAM,MAAM,iBAAiB,CAC3B,OAAO,SAAS,MAAM,EACtB,MAAM,SAAS,SAAS,GAAG,SAAS,IAClC;IACF,QAAQ,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;CACnD,GAAG;IACF,QAAQ,EAAE,CAAC,IAAI,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,MAAM,SAAS,SAAS,IAAI,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,CAAC,EACtF,IAAI,EAAE,KAAK,EACX,QAAQ,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,KAC5E,MAAM,IAAI,CAAC;AAEhB,wBAAgB,sBAAsB,CAAC,MAAM,SAAS,SAAS,EAC7D,SAAS,EAAE,kBAAkB,CAAC,MAAM,CAAC,GACpC,gBAAgB,CAAC,MAAM,CAAC,CAiB1B;AAED,wBAAgB,uBAAuB,CACrC,OAAO,SAAS,MAAM,EACtB,MAAM,SAAS,SAAS,EAExB,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,gBAAgB,CAAC,MAAM,CAAC,GACxD,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,CAcpC"}
@@ -15,9 +15,11 @@ export function createRuntimeEventFeeds(domains, createFeed) {
15
15
  for (const domain of domains) {
16
16
  feeds[domain] = createFeed(domain);
17
17
  }
18
+ // Snapshot the domains array defensively.
19
+ const frozenDomains = Object.freeze([...domains]);
18
20
  return Object.freeze({
19
21
  ...feeds,
20
- domains,
22
+ domains: frozenDomains,
21
23
  domain(domain) {
22
24
  return feeds[domain];
23
25
  },
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type { EventEnvelope, EventEnvelopeContext } from './event-envelope.js';
2
2
  export { createEventEnvelope } from './event-envelope.js';
3
- export type { RuntimeEventFeed, RuntimeEventFeeds, EnvelopeSubscriber } from './event-feeds.js';
3
+ export type { EventForType, EventLike, RuntimeEventFeed, RuntimeEventFeeds, EnvelopeSubscriber } from './event-feeds.js';
4
4
  export { createRuntimeEventFeed, createRuntimeEventFeeds } from './event-feeds.js';
5
5
  export type { ClientTransport } from './client-transport.js';
6
6
  export { createClientTransport } from './client-transport.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AACnF,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,6BAA6B,EAAE,YAAY,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACzH,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AACnF,YAAY,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,6BAA6B,EAAE,YAAY,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC"}
@@ -29,29 +29,36 @@ export interface TransportContext {
29
29
  body: unknown;
30
30
  /** Per-request options forwarded from the caller. */
31
31
  options: {
32
- readonly signal?: AbortSignal;
33
- readonly retry?: unknown;
32
+ readonly signal?: AbortSignal | undefined;
33
+ readonly retry?: unknown | undefined;
34
34
  [key: string]: unknown;
35
35
  };
36
36
  /** AbortSignal for the request. Propagated from caller options. */
37
- signal?: AbortSignal;
37
+ signal?: AbortSignal | undefined;
38
38
  /** The HTTP response object — set after `next()` resolves successfully. */
39
- response?: Response;
39
+ response?: Response | undefined;
40
40
  /** Round-trip duration in milliseconds — set after `next()` resolves. */
41
- durationMs?: number;
41
+ durationMs?: number | undefined;
42
42
  /** Error thrown by the fetch or a downstream middleware — set on failure. */
43
- error?: unknown;
43
+ error?: unknown | undefined;
44
44
  /**
45
45
  * Set to `true` when the error originated from within the middleware chain
46
46
  * (as opposed to from the real fetch). Used by the transport to wrap
47
47
  * middleware errors as `SDKError{kind:'unknown'}`.
48
48
  */
49
- middlewareError?: boolean;
49
+ middlewareError?: boolean | undefined;
50
50
  /**
51
51
  * Name (or index) of the middleware that was active when the error occurred.
52
52
  * Set alongside `middlewareError` for error identity in cause objects.
53
53
  */
54
- activeMiddlewareName?: string;
54
+ activeMiddlewareName?: string | undefined;
55
+ /**
56
+ * the already-parsed response body, stashed by innerFetch after
57
+ * the real fetch resolves. If set, middleware callers should read this
58
+ * field directly instead of calling `ctx.response.json()` to avoid an
59
+ * unnecessary JSON stringify → parse round-trip.
60
+ */
61
+ parsedBody?: unknown | undefined;
55
62
  }
56
63
  /**
57
64
  * A transport middleware function.
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,6CAA6C;IAC7C,IAAI,EAAE,OAAO,CAAC;IACd,qDAAqD;IACrD,OAAO,EAAE;QACP,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;QAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QACzB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,mEAAmE;IACnE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;AAInB;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,UAAU,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,GACvD,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAwD1C"}
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,6CAA6C;IAC7C,IAAI,EAAE,OAAO,CAAC;IACd,qDAAqD;IACrD,OAAO,EAAE;QACP,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;QAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB,CAAC;IACF,mEAAmE;IACnE,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC5B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACtC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAClC;AAED;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;AAInB;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,UAAU,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,CAAC,GACvD,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAkE1C"}
@@ -22,8 +22,12 @@ export function composeMiddleware(middleware, innerFetch) {
22
22
  }
23
23
  return async (ctx) => {
24
24
  let index = -1;
25
+ let dispatchCount = 0;
25
26
  const dispatch = async (i) => {
26
- if (i > MAX_MIDDLEWARE_DEPTH) {
27
+ // count actual re-entrant dispatch() invocations rather than
28
+ // reusing `i` (which is the chain index and cannot exceed chain length).
29
+ dispatchCount += 1;
30
+ if (dispatchCount > MAX_MIDDLEWARE_DEPTH) {
27
31
  throw new GoodVibesSdkError(`Transport middleware recursion exceeded the maximum depth of ${MAX_MIDDLEWARE_DEPTH}.`, {
28
32
  category: 'internal',
29
33
  source: 'transport',
@@ -44,6 +48,12 @@ export function composeMiddleware(middleware, innerFetch) {
44
48
  ctx.activeMiddlewareName = mwName;
45
49
  try {
46
50
  await mw(ctx, () => dispatch(i + 1));
51
+ // clear middlewareError and activeMiddlewareName when the middleware
52
+ // handled the error internally and did not re-throw. Leaving
53
+ // activeMiddlewareName set would wrongly attribute a later unrelated error
54
+ // to this middleware in the cause chain.
55
+ ctx.middlewareError = false;
56
+ ctx.activeMiddlewareName = undefined;
47
57
  }
48
58
  catch (err) {
49
59
  // Mark that the error originated from this middleware (not the real fetch).
@@ -1,14 +1,3 @@
1
- /**
2
- * TransportObserver — first-class observability interface at the transport layer.
3
- *
4
- * Defined here (transport-core) so that HTTP and realtime sibling transports can
5
- * accept it through their shared options types without depending on the SDK layer.
6
- * `SDKObserver` in `@pellux/goodvibes-sdk` extends this interface and adds
7
- * SDK-level callbacks (`onEvent`, `onAuthTransition`).
8
- *
9
- * All methods are optional. Observer exceptions are always swallowed via
10
- * `invokeObserver` — they must never propagate into transport control flow.
11
- */
12
1
  /**
13
2
  * Transport activity metadata surfaced at request/response boundaries.
14
3
  */
@@ -18,19 +7,23 @@ export interface TransportActivityInfo {
18
7
  /** The full URL of the request. */
19
8
  readonly url: string;
20
9
  /** HTTP response status code (only present on `'recv'`). */
21
- readonly status?: number;
10
+ readonly status?: number | undefined;
22
11
  /** Round-trip duration in milliseconds (only present on `'recv'`). */
23
- readonly durationMs?: number;
12
+ readonly durationMs?: number | undefined;
24
13
  /** Transport kind. */
25
- readonly kind?: 'http' | 'sse' | 'ws';
14
+ readonly kind?: 'http' | 'sse' | 'ws' | undefined;
26
15
  }
27
16
  /**
28
17
  * Minimal observer interface at the transport layer.
29
18
  *
30
19
  * Implement any subset; the SDK works identically whether an observer is
31
- * present or not. All call sites are wrapped in a silent try/catch.
20
+ * present or not. All call sites are isolated from transport control flow.
32
21
  */
33
22
  export interface TransportObserver {
23
+ /**
24
+ * Called when another observer callback throws. This is notification only.
25
+ */
26
+ onObserverError?(err: Error): void;
34
27
  /**
35
28
  * Called when the SDK catches and is about to rethrow a transport error.
36
29
  * The error is still rethrown; this is notification only.
@@ -44,10 +37,11 @@ export interface TransportObserver {
44
37
  onTransportActivity?(activity: TransportActivityInfo): void;
45
38
  }
46
39
  /**
47
- * Safely invoke an observer method. Observer errors are swallowed so they
48
- * never disrupt transport control flow.
40
+ * Safely invoke an observer method. Observer errors are reported through
41
+ * `onObserverError` when available and never disrupt transport control flow.
49
42
  *
50
43
  * @param fn - Zero-argument thunk wrapping the observer call.
44
+ * @param onObserverError - Optional observer failure reporter.
51
45
  */
52
- export declare function invokeTransportObserver(fn: () => void): void;
46
+ export declare function invokeTransportObserver(fn: () => void, onObserverError?: ((err: Error) => void) | undefined): void;
53
47
  //# sourceMappingURL=observer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;CACvC;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAE3B;;;;OAIG;IACH,mBAAmB,CAAC,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;CAC7D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAO5D"}
1
+ {"version":3,"file":"observer.d.ts","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"AAcA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,sEAAsE;IACtE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,sBAAsB;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC;CACnD;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,eAAe,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAEnC;;;OAGG;IACH,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC;IAE3B;;;;OAIG;IACH,mBAAmB,CAAC,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;CAC7D;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,SAAS,GAAG,IAAI,CAoBlH"}
package/dist/observer.js CHANGED
@@ -2,25 +2,43 @@
2
2
  * TransportObserver — first-class observability interface at the transport layer.
3
3
  *
4
4
  * Defined here (transport-core) so that HTTP and realtime sibling transports can
5
- * accept it through their shared options types without depending on the SDK layer.
6
- * `SDKObserver` in `@pellux/goodvibes-sdk` extends this interface and adds
7
- * SDK-level callbacks (`onEvent`, `onAuthTransition`).
5
+ * accept it through their shared options types without depending on higher-level packages.
6
+ * Higher-level SDK packages can extend this interface with product-specific
7
+ * callbacks while sharing the same transport option shape.
8
8
  *
9
- * All methods are optional. Observer exceptions are always swallowed via
10
- * `invokeObserver` they must never propagate into transport control flow.
9
+ * All methods are optional. Observer callback errors are reported through
10
+ * `onObserverError` when supplied and never propagate into transport control flow.
11
11
  */
12
+ import { GoodVibesSdkError } from '@pellux/goodvibes-errors';
13
+ import { transportErrorFromUnknown } from './errors.js';
12
14
  /**
13
- * Safely invoke an observer method. Observer errors are swallowed so they
14
- * never disrupt transport control flow.
15
+ * Safely invoke an observer method. Observer errors are reported through
16
+ * `onObserverError` when available and never disrupt transport control flow.
15
17
  *
16
18
  * @param fn - Zero-argument thunk wrapping the observer call.
19
+ * @param onObserverError - Optional observer failure reporter.
17
20
  */
18
- export function invokeTransportObserver(fn) {
21
+ export function invokeTransportObserver(fn, onObserverError) {
19
22
  try {
20
23
  fn();
21
24
  }
22
25
  catch (error) {
23
- void error;
24
- // Observer errors must not propagate into transport logic.
26
+ if (!onObserverError)
27
+ return;
28
+ try {
29
+ const observerError = error instanceof Error
30
+ ? new GoodVibesSdkError(`Transport observer callback failed: ${error.message}`, {
31
+ category: 'internal',
32
+ source: 'transport',
33
+ recoverable: false,
34
+ cause: error,
35
+ })
36
+ : transportErrorFromUnknown(error, 'Transport observer callback failed');
37
+ onObserverError(observerError);
38
+ }
39
+ catch {
40
+ // The observer failure reporter itself failed; no remaining observer hook
41
+ // can report that without risking recursive failure.
42
+ }
25
43
  }
26
44
  }
@@ -0,0 +1,22 @@
1
+ export interface SpanContext {
2
+ traceId: string;
3
+ spanId: string;
4
+ traceFlags: number;
5
+ traceState?: {
6
+ serialize(): string;
7
+ } | null | undefined;
8
+ }
9
+ export interface Span {
10
+ spanContext(): SpanContext;
11
+ }
12
+ export interface OtelApi {
13
+ trace: {
14
+ getActiveSpan(): Span | undefined;
15
+ };
16
+ }
17
+ export declare function readCachedOtelApi(): OtelApi | null | undefined;
18
+ export declare function cacheOtelApi(api: OtelApi | null): void;
19
+ export declare function readOtelModuleOverride(): OtelApi | null | undefined;
20
+ export declare function setOtelModuleOverride(api: OtelApi | null | undefined): void;
21
+ export declare function resetOtelState(): void;
22
+ //# sourceMappingURL=otel-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel-state.d.ts","sourceRoot":"","sources":["../src/otel-state.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,SAAS,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,GAAG,SAAS,CAAC;CACzD;AAED,MAAM,WAAW,IAAI;IACnB,WAAW,IAAI,WAAW,CAAC;CAC5B;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE;QACL,aAAa,IAAI,IAAI,GAAG,SAAS,CAAC;KACnC,CAAC;CACH;AAKD,wBAAgB,iBAAiB,IAAI,OAAO,GAAG,IAAI,GAAG,SAAS,CAE9D;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAEtD;AAED,wBAAgB,sBAAsB,IAAI,OAAO,GAAG,IAAI,GAAG,SAAS,CAEnE;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAE3E;AAED,wBAAgB,cAAc,IAAI,IAAI,CAGrC"}
@@ -0,0 +1,18 @@
1
+ let cachedOtelApi = undefined;
2
+ let otelModuleOverride = undefined;
3
+ export function readCachedOtelApi() {
4
+ return cachedOtelApi;
5
+ }
6
+ export function cacheOtelApi(api) {
7
+ cachedOtelApi = api;
8
+ }
9
+ export function readOtelModuleOverride() {
10
+ return otelModuleOverride;
11
+ }
12
+ export function setOtelModuleOverride(api) {
13
+ otelModuleOverride = api;
14
+ }
15
+ export function resetOtelState() {
16
+ cachedOtelApi = undefined;
17
+ otelModuleOverride = undefined;
18
+ }
package/dist/otel.d.ts CHANGED
@@ -11,41 +11,6 @@
11
11
  *
12
12
  * W3C Trace Context spec: https://www.w3.org/TR/trace-context/
13
13
  */
14
- interface SpanContext {
15
- traceId: string;
16
- spanId: string;
17
- traceFlags: number;
18
- traceState?: {
19
- serialize(): string;
20
- } | null;
21
- }
22
- interface Span {
23
- spanContext(): SpanContext;
24
- }
25
- interface OtelApi {
26
- trace: {
27
- getActiveSpan(): Span | undefined;
28
- };
29
- }
30
- /**
31
- * Set the OTel module override for testing. Pass `undefined` to clear.
32
- * Calling this bypasses dynamic import and require-based detection.
33
- *
34
- * @internal — for testing only, do NOT use in production code.
35
- */
36
- export declare function setOtelModuleOverride(api: OtelApi | null | undefined): void;
37
- /**
38
- * Get the current OTel module override (for test inspection).
39
- * @internal
40
- */
41
- export declare function getOtelModuleOverride(): OtelApi | null | undefined;
42
- /**
43
- * Reset both the module-level cache and the test injection seam.
44
- * Call this in `afterEach` when using `setOtelModuleOverride` in tests.
45
- *
46
- * @internal — for testing only.
47
- */
48
- export declare function __resetOtelCache(): void;
49
14
  /**
50
15
  * Inject W3C Trace Context headers (`traceparent`, `tracestate`) if an active
51
16
  * OTel span is available. Synchronous; uses require-based detection.
@@ -60,5 +25,4 @@ export declare function injectTraceparent(headers: Record<string, string>): void
60
25
  * @param headers - Mutable header record to augment in-place.
61
26
  */
62
27
  export declare function injectTraceparentAsync(headers: Record<string, string>): Promise<void>;
63
- export {};
64
28
  //# sourceMappingURL=otel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE;QAAE,SAAS,IAAI,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC7C;AAED,UAAU,IAAI;IACZ,WAAW,IAAI,WAAW,CAAC;CAC5B;AAED,UAAU,OAAO;IACf,KAAK,EAAE;QACL,aAAa,IAAI,IAAI,GAAG,SAAS,CAAC;KACnC,CAAC;CACH;AAQD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAE3E;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,GAAG,IAAI,GAAG,SAAS,CAElE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC;AAoDD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAiBvE;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB3F"}
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AA4EH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAiBvE;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB3F"}
package/dist/otel.js CHANGED
@@ -11,84 +11,69 @@
11
11
  *
12
12
  * W3C Trace Context spec: https://www.w3.org/TR/trace-context/
13
13
  */
14
- /** Cached result of OTel detection. `null` = not available. `undefined` = not yet probed. */
15
- let otelApi = undefined;
16
- /** Internal override value for the test injection seam. */
17
- let _otelModuleOverride = undefined;
18
- /**
19
- * Set the OTel module override for testing. Pass `undefined` to clear.
20
- * Calling this bypasses dynamic import and require-based detection.
21
- *
22
- * @internal — for testing only, do NOT use in production code.
23
- */
24
- export function setOtelModuleOverride(api) {
25
- _otelModuleOverride = api;
26
- }
27
- /**
28
- * Get the current OTel module override (for test inspection).
29
- * @internal
30
- */
31
- export function getOtelModuleOverride() {
32
- return _otelModuleOverride;
33
- }
34
- /**
35
- * Reset both the module-level cache and the test injection seam.
36
- * Call this in `afterEach` when using `setOtelModuleOverride` in tests.
37
- *
38
- * @internal — for testing only.
39
- */
40
- export function __resetOtelCache() {
41
- otelApi = undefined;
42
- _otelModuleOverride = undefined;
43
- }
14
+ import { cacheOtelApi, readCachedOtelApi, readOtelModuleOverride, } from './otel-state.js';
44
15
  /**
45
16
  * Dynamic import that is opaque to bundlers.
46
17
  * `new Function(...)` is not statically analysed for import() specifiers.
47
18
  */
48
19
  function dynamicImport(moduleName) {
49
- // Using Function constructor prevents bundler static analysis of import().
20
+ // `new Function(...)` is intentional: it prevents bundlers (esbuild, Rollup,
21
+ // Miniflare/workerd) from statically analysing the import() specifier and either
22
+ // bundling @opentelemetry/api or raising an unresolvable-specifier error.
23
+ //
24
+ // CSP note: `new Function` violates strict `script-src 'self'` Content Security
25
+ // Policies (equivalent to eval). Call sites must guard with the WorkerGlobalScope /
26
+ // window checks in probeOtel() so this is never called in restricted browser / worker
27
+ // environments. In Node.js and non-CSP environments it is safe.
50
28
  // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
51
29
  return new Function('m', 'return import(m)')(moduleName);
52
30
  }
53
31
  async function probeOtel() {
54
- // Test injection seam takes highest priority.
55
- if (_otelModuleOverride !== undefined)
56
- return _otelModuleOverride;
57
- if (otelApi !== undefined)
58
- return otelApi;
32
+ const override = readOtelModuleOverride();
33
+ if (override !== undefined)
34
+ return override;
35
+ const cached = readCachedOtelApi();
36
+ if (cached !== undefined)
37
+ return cached;
38
+ // skip dynamic import in browser windows AND Service Workers / workerd
39
+ // (which have no `window` but do have WorkerGlobalScope). The Function constructor
40
+ // used in dynamicImport violates strict `script-src 'self'` CSPs in workers.
41
+ if (typeof window !== 'undefined' || 'WorkerGlobalScope' in globalThis) {
42
+ cacheOtelApi(null);
43
+ return null;
44
+ }
59
45
  try {
60
46
  const mod = await dynamicImport('@opentelemetry/api');
61
- otelApi = mod;
47
+ cacheOtelApi(mod);
62
48
  }
63
- catch (error) {
64
- void error;
65
- otelApi = null;
49
+ catch {
50
+ cacheOtelApi(null);
66
51
  }
67
- return otelApi;
52
+ return readCachedOtelApi() ?? null;
68
53
  }
69
54
  function probeOtelSync() {
70
- // Test injection seam takes highest priority.
71
- if (_otelModuleOverride !== undefined)
72
- return _otelModuleOverride;
73
- if (otelApi !== undefined)
74
- return otelApi;
55
+ const override = readOtelModuleOverride();
56
+ if (override !== undefined)
57
+ return override;
58
+ const cached = readCachedOtelApi();
59
+ if (cached !== undefined)
60
+ return cached;
75
61
  try {
76
62
  // Use globalThis.require via indirect reference to avoid bundler module resolution.
77
63
  const nodeRequire = typeof globalThis !== 'undefined'
78
64
  ? globalThis.require
79
65
  : undefined;
80
66
  if (typeof nodeRequire === 'function') {
81
- otelApi = nodeRequire('@opentelemetry/api');
67
+ cacheOtelApi(nodeRequire('@opentelemetry/api'));
82
68
  }
83
69
  else {
84
- otelApi = null;
70
+ cacheOtelApi(null);
85
71
  }
86
72
  }
87
- catch (error) {
88
- void error;
89
- otelApi = null;
73
+ catch {
74
+ cacheOtelApi(null);
90
75
  }
91
- return otelApi;
76
+ return readCachedOtelApi() ?? null;
92
77
  }
93
78
  function buildTraceparent(ctx) {
94
79
  const flags = (ctx.traceFlags & 0xff).toString(16).padStart(2, '0');
@@ -117,9 +102,9 @@ export function injectTraceparent(headers) {
117
102
  headers['tracestate'] = traceState;
118
103
  }
119
104
  }
120
- catch (error) {
121
- void error;
105
+ catch {
122
106
  // Never let OTel errors propagate into transport logic.
107
+ return;
123
108
  }
124
109
  }
125
110
  /**
@@ -145,8 +130,8 @@ export async function injectTraceparentAsync(headers) {
145
130
  headers['tracestate'] = traceState;
146
131
  }
147
132
  }
148
- catch (error) {
149
- void error;
133
+ catch {
150
134
  // Never let OTel errors propagate into transport logic.
135
+ return;
151
136
  }
152
137
  }
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-transport-core",
3
- "version": "0.30.2",
3
+ "version": "0.33.0",
4
4
  "engines": {
5
- "node": ">=20.0.0"
5
+ "bun": "1.3.10",
6
+ "node": ">=22.0.0"
6
7
  },
7
8
  "description": "Shared GoodVibes client transport primitives for direct, HTTP, and realtime integrations.",
8
9
  "type": "module",
@@ -17,6 +18,14 @@
17
18
  "types": "./dist/client-transport.d.ts",
18
19
  "import": "./dist/client-transport.js"
19
20
  },
21
+ "./direct": {
22
+ "types": "./dist/direct.d.ts",
23
+ "import": "./dist/direct.js"
24
+ },
25
+ "./errors": {
26
+ "types": "./dist/errors.d.ts",
27
+ "import": "./dist/errors.js"
28
+ },
20
29
  "./event-envelope": {
21
30
  "types": "./dist/event-envelope.d.ts",
22
31
  "import": "./dist/event-envelope.js"
@@ -48,7 +57,7 @@
48
57
  ],
49
58
  "sideEffects": false,
50
59
  "dependencies": {
51
- "@pellux/goodvibes-errors": "0.30.2"
60
+ "@pellux/goodvibes-errors": "0.33.0"
52
61
  },
53
62
  "license": "MIT",
54
63
  "repository": {