@pellux/goodvibes-sdk 0.19.6 → 0.19.9

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 (169) hide show
  1. package/dist/_internal/contracts/index.d.ts +1 -0
  2. package/dist/_internal/contracts/index.d.ts.map +1 -1
  3. package/dist/_internal/contracts/index.js +2 -0
  4. package/dist/_internal/contracts/types.d.ts +4 -0
  5. package/dist/_internal/contracts/types.d.ts.map +1 -1
  6. package/dist/_internal/contracts/zod-schemas/accounts.d.ts +81 -0
  7. package/dist/_internal/contracts/zod-schemas/accounts.d.ts.map +1 -0
  8. package/dist/_internal/contracts/zod-schemas/accounts.js +47 -0
  9. package/dist/_internal/contracts/zod-schemas/auth.d.ts +42 -0
  10. package/dist/_internal/contracts/zod-schemas/auth.d.ts.map +1 -0
  11. package/dist/_internal/contracts/zod-schemas/auth.js +29 -0
  12. package/dist/_internal/contracts/zod-schemas/events.d.ts +37 -0
  13. package/dist/_internal/contracts/zod-schemas/events.d.ts.map +1 -0
  14. package/dist/_internal/contracts/zod-schemas/events.js +26 -0
  15. package/dist/_internal/contracts/zod-schemas/index.d.ts +9 -0
  16. package/dist/_internal/contracts/zod-schemas/index.d.ts.map +1 -0
  17. package/dist/_internal/contracts/zod-schemas/index.js +4 -0
  18. package/dist/_internal/contracts/zod-schemas/session.d.ts +22 -0
  19. package/dist/_internal/contracts/zod-schemas/session.d.ts.map +1 -0
  20. package/dist/_internal/contracts/zod-schemas/session.js +19 -0
  21. package/dist/_internal/daemon/api-router.d.ts.map +1 -1
  22. package/dist/_internal/daemon/api-router.js +0 -1
  23. package/dist/_internal/daemon/automation.d.ts.map +1 -1
  24. package/dist/_internal/daemon/channel-route-types.d.ts.map +1 -1
  25. package/dist/_internal/daemon/channel-routes.d.ts.map +1 -1
  26. package/dist/_internal/daemon/context.d.ts.map +1 -1
  27. package/dist/_internal/daemon/control-routes.d.ts.map +1 -1
  28. package/dist/_internal/daemon/http-policy.d.ts.map +1 -1
  29. package/dist/_internal/daemon/http-policy.js +0 -1
  30. package/dist/_internal/daemon/integration-route-types.d.ts.map +1 -1
  31. package/dist/_internal/daemon/integration-routes.d.ts.map +1 -1
  32. package/dist/_internal/daemon/knowledge-route-types.d.ts.map +1 -1
  33. package/dist/_internal/daemon/knowledge-routes.d.ts.map +1 -1
  34. package/dist/_internal/daemon/knowledge-routes.js +5 -4
  35. package/dist/_internal/daemon/media-route-types.d.ts.map +1 -1
  36. package/dist/_internal/daemon/media-routes.d.ts.map +1 -1
  37. package/dist/_internal/daemon/operator.d.ts.map +1 -1
  38. package/dist/_internal/daemon/remote-routes.d.ts.map +1 -1
  39. package/dist/_internal/daemon/remote.d.ts.map +1 -1
  40. package/dist/_internal/daemon/route-helpers.d.ts.map +1 -1
  41. package/dist/_internal/daemon/runtime-automation-routes.d.ts.map +1 -1
  42. package/dist/_internal/daemon/runtime-route-types.d.ts +14 -1
  43. package/dist/_internal/daemon/runtime-route-types.d.ts.map +1 -1
  44. package/dist/_internal/daemon/runtime-routes.d.ts.map +1 -1
  45. package/dist/_internal/daemon/runtime-session-routes.d.ts.map +1 -1
  46. package/dist/_internal/daemon/runtime-session-routes.js +0 -2
  47. package/dist/_internal/daemon/sessions.d.ts.map +1 -1
  48. package/dist/_internal/daemon/system-route-types.d.ts.map +1 -1
  49. package/dist/_internal/daemon/system-routes.d.ts.map +1 -1
  50. package/dist/_internal/daemon/tasks.d.ts.map +1 -1
  51. package/dist/_internal/daemon/telemetry-routes.d.ts.map +1 -1
  52. package/dist/_internal/errors/daemon-error-contract.d.ts.map +1 -1
  53. package/dist/_internal/errors/index.d.ts +2 -2
  54. package/dist/_internal/errors/index.js +2 -2
  55. package/dist/_internal/operator/client-core.d.ts.map +1 -1
  56. package/dist/_internal/operator/client-core.js +8 -2
  57. package/dist/_internal/operator/client.d.ts +7 -0
  58. package/dist/_internal/operator/client.d.ts.map +1 -1
  59. package/dist/_internal/operator/client.js +32 -1
  60. package/dist/_internal/peer/client-core.d.ts.map +1 -1
  61. package/dist/_internal/platform/agents/orchestrator.d.ts +7 -0
  62. package/dist/_internal/platform/agents/orchestrator.d.ts.map +1 -1
  63. package/dist/_internal/platform/agents/orchestrator.js +8 -0
  64. package/dist/_internal/platform/auth/android-keystore-token-store.d.ts +110 -0
  65. package/dist/_internal/platform/auth/android-keystore-token-store.d.ts.map +1 -0
  66. package/dist/_internal/platform/auth/android-keystore-token-store.js +164 -0
  67. package/dist/_internal/platform/auth/auto-refresh-middleware.d.ts +46 -0
  68. package/dist/_internal/platform/auth/auto-refresh-middleware.d.ts.map +1 -0
  69. package/dist/_internal/platform/auth/auto-refresh-middleware.js +155 -0
  70. package/dist/_internal/platform/auth/auto-refresh.d.ts +123 -0
  71. package/dist/_internal/platform/auth/auto-refresh.d.ts.map +1 -0
  72. package/dist/_internal/platform/auth/auto-refresh.js +236 -0
  73. package/dist/_internal/platform/auth/expo-secure-token-store.d.ts +82 -0
  74. package/dist/_internal/platform/auth/expo-secure-token-store.d.ts.map +1 -0
  75. package/dist/_internal/platform/auth/expo-secure-token-store.js +135 -0
  76. package/dist/_internal/platform/auth/index.d.ts +3 -0
  77. package/dist/_internal/platform/auth/index.d.ts.map +1 -1
  78. package/dist/_internal/platform/auth/index.js +2 -0
  79. package/dist/_internal/platform/auth/ios-keychain-token-store.d.ts +88 -0
  80. package/dist/_internal/platform/auth/ios-keychain-token-store.d.ts.map +1 -0
  81. package/dist/_internal/platform/auth/ios-keychain-token-store.js +147 -0
  82. package/dist/_internal/platform/auth/session-manager.d.ts +2 -0
  83. package/dist/_internal/platform/auth/session-manager.d.ts.map +1 -1
  84. package/dist/_internal/platform/auth/session-manager.js +9 -1
  85. package/dist/_internal/platform/auth/token-store.d.ts +13 -0
  86. package/dist/_internal/platform/auth/token-store.d.ts.map +1 -1
  87. package/dist/_internal/platform/auth/token-store.js +23 -0
  88. package/dist/_internal/platform/companion/companion-chat-manager.d.ts +64 -11
  89. package/dist/_internal/platform/companion/companion-chat-manager.d.ts.map +1 -1
  90. package/dist/_internal/platform/companion/companion-chat-manager.js +158 -12
  91. package/dist/_internal/platform/companion/companion-chat-persistence.d.ts +33 -0
  92. package/dist/_internal/platform/companion/companion-chat-persistence.d.ts.map +1 -0
  93. package/dist/_internal/platform/companion/companion-chat-persistence.js +115 -0
  94. package/dist/_internal/platform/companion/companion-chat-rate-limiter.d.ts +47 -0
  95. package/dist/_internal/platform/companion/companion-chat-rate-limiter.d.ts.map +1 -0
  96. package/dist/_internal/platform/companion/companion-chat-rate-limiter.js +117 -0
  97. package/dist/_internal/platform/companion/companion-chat-types.d.ts +2 -4
  98. package/dist/_internal/platform/companion/companion-chat-types.d.ts.map +1 -1
  99. package/dist/_internal/platform/companion/companion-chat-types.js +2 -4
  100. package/dist/_internal/platform/companion/index.d.ts +4 -0
  101. package/dist/_internal/platform/companion/index.d.ts.map +1 -1
  102. package/dist/_internal/platform/companion/index.js +2 -0
  103. package/dist/_internal/platform/daemon/facade-composition.d.ts.map +1 -1
  104. package/dist/_internal/platform/daemon/facade-composition.js +5 -0
  105. package/dist/_internal/platform/daemon/facade.d.ts.map +1 -1
  106. package/dist/_internal/platform/daemon/facade.js +3 -0
  107. package/dist/_internal/platform/daemon/http/runtime-route-types.d.ts +0 -7
  108. package/dist/_internal/platform/daemon/http/runtime-route-types.d.ts.map +1 -1
  109. package/dist/_internal/platform/state/db.d.ts.map +1 -1
  110. package/dist/_internal/platform/state/db.js +0 -1
  111. package/dist/_internal/platform/state/sqlite-store.d.ts.map +1 -1
  112. package/dist/_internal/platform/state/sqlite-store.js +0 -1
  113. package/dist/_internal/platform/version.js +1 -1
  114. package/dist/_internal/transport-core/client-transport.d.ts.map +1 -1
  115. package/dist/_internal/transport-core/event-envelope.d.ts.map +1 -1
  116. package/dist/_internal/transport-core/event-feeds.d.ts.map +1 -1
  117. package/dist/_internal/transport-core/index.d.ts +5 -0
  118. package/dist/_internal/transport-core/index.d.ts.map +1 -1
  119. package/dist/_internal/transport-core/index.js +3 -0
  120. package/dist/_internal/transport-core/middleware.d.ts +76 -0
  121. package/dist/_internal/transport-core/middleware.d.ts.map +1 -0
  122. package/dist/_internal/transport-core/middleware.js +67 -0
  123. package/dist/_internal/transport-core/observer.d.ts +53 -0
  124. package/dist/_internal/transport-core/observer.d.ts.map +1 -0
  125. package/dist/_internal/transport-core/observer.js +26 -0
  126. package/dist/_internal/transport-core/otel.d.ts +64 -0
  127. package/dist/_internal/transport-core/otel.d.ts.map +1 -0
  128. package/dist/_internal/transport-core/otel.js +149 -0
  129. package/dist/_internal/transport-direct/index.d.ts.map +1 -1
  130. package/dist/_internal/transport-direct/index.js +0 -1
  131. package/dist/_internal/transport-http/contract-client.d.ts +11 -1
  132. package/dist/_internal/transport-http/contract-client.d.ts.map +1 -1
  133. package/dist/_internal/transport-http/contract-client.js +18 -4
  134. package/dist/_internal/transport-http/http-core.d.ts +27 -1
  135. package/dist/_internal/transport-http/http-core.d.ts.map +1 -1
  136. package/dist/_internal/transport-http/http-core.js +180 -12
  137. package/dist/_internal/transport-http/http.d.ts +3 -3
  138. package/dist/_internal/transport-http/http.d.ts.map +1 -1
  139. package/dist/_internal/transport-http/http.js +2 -2
  140. package/dist/_internal/transport-http/index.d.ts +4 -2
  141. package/dist/_internal/transport-http/index.d.ts.map +1 -1
  142. package/dist/_internal/transport-http/index.js +2 -1
  143. package/dist/_internal/transport-http/paths.js +1 -1
  144. package/dist/_internal/transport-http/reconnect.d.ts +2 -0
  145. package/dist/_internal/transport-http/reconnect.d.ts.map +1 -1
  146. package/dist/_internal/transport-http/reconnect.js +4 -2
  147. package/dist/_internal/transport-http/retry.d.ts +15 -0
  148. package/dist/_internal/transport-http/retry.d.ts.map +1 -1
  149. package/dist/_internal/transport-http/retry.js +19 -0
  150. package/dist/_internal/transport-realtime/domain-events.d.ts.map +1 -1
  151. package/dist/_internal/transport-realtime/runtime-events.d.ts +10 -3
  152. package/dist/_internal/transport-realtime/runtime-events.d.ts.map +1 -1
  153. package/dist/_internal/transport-realtime/runtime-events.js +73 -8
  154. package/dist/auth.d.ts +38 -3
  155. package/dist/auth.d.ts.map +1 -1
  156. package/dist/auth.js +68 -3
  157. package/dist/client.d.ts +61 -2
  158. package/dist/client.d.ts.map +1 -1
  159. package/dist/client.js +64 -3
  160. package/dist/expo.d.ts +1 -0
  161. package/dist/expo.d.ts.map +1 -1
  162. package/dist/expo.js +1 -0
  163. package/dist/observer/index.d.ts +16 -25
  164. package/dist/observer/index.d.ts.map +1 -1
  165. package/dist/platform/runtime/transports/http.js +1 -1
  166. package/dist/react-native.d.ts +2 -0
  167. package/dist/react-native.d.ts.map +1 -1
  168. package/dist/react-native.js +2 -0
  169. package/package.json +16 -3
@@ -1,8 +1,8 @@
1
1
  // Synced from packages/transport-realtime/src/runtime-events.ts
2
- // Extracted from legacy source: src/runtime/transports/runtime-events-client.ts
3
2
  import { RUNTIME_EVENT_DOMAINS } from '../contracts/index.js';
4
3
  import { normalizeAuthToken, openRawServerSentEventStream as openServerSentEventStream, normalizeStreamReconnectPolicy, getStreamReconnectDelay, } from '../transport-http/index.js';
5
4
  import { buildUrl, normalizeBaseUrl } from '../transport-http/index.js';
5
+ import { injectTraceparentAsync, invokeTransportObserver } from '../transport-core/index.js';
6
6
  import { createRemoteDomainEvents, } from './domain-events.js';
7
7
  /**
8
8
  * Returns a filtered view of a {@link RemoteRuntimeEvents} object where every
@@ -33,8 +33,8 @@ import { createRemoteDomainEvents, } from './domain-events.js';
33
33
  export { forSession as forSessionRuntime } from './domain-events.js';
34
34
  /** Default max reconnect attempts for WebSocket connections (finite to prevent infinite auth loops). */
35
35
  export const DEFAULT_WS_MAX_ATTEMPTS = 10;
36
- /** Maximum number of messages that may be queued in outboundQueue pending a producer API. */
37
- const MAX_OUTBOUND_QUEUE = 256;
36
+ /** Maximum number of messages that may be queued in the outbound queue before the oldest entry is dropped. */
37
+ const MAX_OUTBOUND_QUEUE = 1024;
38
38
  export function createRemoteRuntimeEvents(connect) {
39
39
  return createRemoteDomainEvents(RUNTIME_EVENT_DOMAINS, connect);
40
40
  }
@@ -53,26 +53,45 @@ export function buildWebSocketUrl(baseUrl, domains) {
53
53
  return url.toString();
54
54
  }
55
55
  export function createEventSourceConnector(baseUrl, token, fetchImpl, options = {}) {
56
+ const { observer } = options;
56
57
  const handleError = options.onError ?? (options.reconnect?.enabled ? (() => { }) : undefined);
57
58
  return async (domain, onEnvelope) => {
58
59
  const url = buildEventSourceUrl(baseUrl, domain);
59
60
  const getAuthToken = normalizeAuthToken(token ?? undefined);
61
+ // Inject W3C traceparent if OTel is active (async probe for SSE cold-start).
62
+ const sseHeaders = {};
63
+ await injectTraceparentAsync(sseHeaders);
64
+ // Notify observer of outbound SSE connection attempt.
65
+ invokeTransportObserver(() => observer?.onTransportActivity?.({ direction: 'send', url, kind: 'sse' }));
60
66
  return await openServerSentEventStream(fetchImpl, url, {
61
67
  onEvent: (eventName, payload) => {
62
68
  if (eventName !== domain)
63
69
  return;
64
70
  if (!payload || typeof payload !== 'object')
65
71
  return;
66
- onEnvelope(payload);
72
+ const envelope = payload;
73
+ onEnvelope(envelope);
74
+ // Notify observer of inbound event.
75
+ invokeTransportObserver(() => {
76
+ observer?.onTransportActivity?.({ direction: 'recv', url, kind: 'sse' });
77
+ if (envelope.payload) {
78
+ observer?.onEvent?.(envelope.payload);
79
+ }
80
+ });
81
+ },
82
+ onError: (err) => {
83
+ invokeTransportObserver(() => observer?.onError?.(err instanceof Error ? err : new Error(String(err))));
84
+ handleError?.(err);
67
85
  },
68
- onError: handleError,
69
86
  }, {
70
87
  reconnect: options.reconnect,
71
88
  getAuthToken,
89
+ headers: Object.keys(sseHeaders).length > 0 ? sseHeaders : undefined,
72
90
  });
73
91
  };
74
92
  }
75
93
  export function createWebSocketConnector(baseUrl, token, WebSocketImpl, options = {}) {
94
+ const { observer } = options;
76
95
  return async (domain, onEnvelope) => {
77
96
  const url = buildWebSocketUrl(baseUrl, [domain]);
78
97
  const reconnect = options.reconnect;
@@ -88,9 +107,39 @@ export function createWebSocketConnector(baseUrl, token, WebSocketImpl, options
88
107
  let hasReceivedMessage = false;
89
108
  let socket = null;
90
109
  let reconnectTimer = null;
91
- // TODO: producer API not yet wired. When added, bound queue size via
92
- // MAX_OUTBOUND_QUEUE (see below) and drop oldest or reject with onError.
110
+ // Bounded outbound message queue max MAX_OUTBOUND_QUEUE entries, drop-oldest policy.
111
+ // Messages pushed while the socket is not yet open or is reconnecting are buffered here
112
+ // and flushed on the next successful open event.
93
113
  const outboundQueue = [];
114
+ let droppedOutboundCount = 0;
115
+ /**
116
+ * Enqueue a message for delivery over this WebSocket connection.
117
+ *
118
+ * If the socket is currently open the message is sent immediately.
119
+ * If the socket is not yet open (or is reconnecting), the message is
120
+ * buffered and will be flushed once the connection is re-established.
121
+ *
122
+ * When the buffer is full (> MAX_OUTBOUND_QUEUE), the oldest pending
123
+ * message is silently dropped and a counter is incremented. Callers that
124
+ * need back-pressure should check `socket?.readyState` before calling.
125
+ *
126
+ * @param data - Serialised message string to send.
127
+ */
128
+ const emitLocal = (data) => {
129
+ if (socket && socket.readyState === WebSocket.OPEN) {
130
+ socket.send(data);
131
+ return;
132
+ }
133
+ if (outboundQueue.length >= MAX_OUTBOUND_QUEUE) {
134
+ // Drop oldest message to make room (drop-oldest policy).
135
+ outboundQueue.shift();
136
+ droppedOutboundCount += 1;
137
+ options.onError?.(new Error(`WebSocket outbound queue full (limit ${MAX_OUTBOUND_QUEUE}). Oldest message dropped; total dropped: ${droppedOutboundCount}.`));
138
+ }
139
+ outboundQueue.push(data);
140
+ };
141
+ // Notify caller of the emitter handle (for test/shim use cases).
142
+ options.onEmitter?.(emitLocal);
94
143
  const closeSocket = () => {
95
144
  if (!socket)
96
145
  return;
@@ -124,11 +173,18 @@ export function createWebSocketConnector(baseUrl, token, WebSocketImpl, options
124
173
  const authToken = (await normalizeAuthToken(token ?? undefined)()) ?? null;
125
174
  if (!authToken || !socket)
126
175
  return;
176
+ // Notify observer of outbound WS connection.
177
+ invokeTransportObserver(() => observer?.onTransportActivity?.({ direction: 'send', url, kind: 'ws' }));
127
178
  // Send auth frame first, then drain any messages buffered during resolution.
179
+ // Inject traceparent into the auth frame for W3C Trace Context propagation over WebSocket.
180
+ const wsTraceHeaders = {};
181
+ await injectTraceparentAsync(wsTraceHeaders);
128
182
  socket.send(JSON.stringify({
129
183
  type: 'auth',
130
184
  token: authToken,
131
185
  domains: [domain],
186
+ ...(wsTraceHeaders['traceparent'] ? { traceparent: wsTraceHeaders['traceparent'] } : {}),
187
+ ...(wsTraceHeaders['tracestate'] ? { tracestate: wsTraceHeaders['tracestate'] } : {}),
132
188
  }));
133
189
  flushOutboundQueue(socket);
134
190
  };
@@ -142,7 +198,15 @@ export function createWebSocketConnector(baseUrl, token, WebSocketImpl, options
142
198
  hasReceivedMessage = true;
143
199
  reconnectAttempt = 0;
144
200
  }
145
- onEnvelope(frame.payload);
201
+ const wsPayload = frame.payload;
202
+ onEnvelope(wsPayload);
203
+ // Notify observer of inbound WS event.
204
+ invokeTransportObserver(() => {
205
+ observer?.onTransportActivity?.({ direction: 'recv', url, kind: 'ws' });
206
+ if (wsPayload.payload) {
207
+ observer?.onEvent?.(wsPayload.payload);
208
+ }
209
+ });
146
210
  }
147
211
  }
148
212
  catch {
@@ -155,6 +219,7 @@ export function createWebSocketConnector(baseUrl, token, WebSocketImpl, options
155
219
  scheduleReconnect();
156
220
  };
157
221
  const onError = (event) => {
222
+ invokeTransportObserver(() => observer?.onError?.(event instanceof Error ? event : new Error(String(event))));
158
223
  options.onError?.(event);
159
224
  };
160
225
  const connect = async () => {
package/dist/auth.d.ts CHANGED
@@ -2,16 +2,38 @@ import type { SDKObserver } from './observer/index.js';
2
2
  import type { OperatorMethodInput, OperatorMethodOutput } from './_internal/contracts/index.js';
3
3
  import type { AuthTokenResolver } from './_internal/transport-http/index.js';
4
4
  import type { OperatorSdk } from './_internal/operator/index.js';
5
- import { PermissionResolver, SessionManager, TokenStore } from './_internal/platform/auth/index.js';
5
+ import { AutoRefreshCoordinator, PermissionResolver, SessionManager, TokenStore } from './_internal/platform/auth/index.js';
6
+ import type { AutoRefreshOptions } from './_internal/platform/auth/index.js';
6
7
  export { PermissionResolver, SessionManager, TokenStore } from './_internal/platform/auth/index.js';
7
8
  export type { OAuthStartState, OAuthTokenPayload } from './_internal/platform/auth/oauth-types.js';
8
9
  export type GoodVibesCurrentAuth = OperatorMethodOutput<'control.auth.current'>;
9
10
  export type GoodVibesLoginInput = OperatorMethodInput<'control.auth.login'>;
10
11
  export type GoodVibesLoginOutput = OperatorMethodOutput<'control.auth.login'>;
12
+ /**
13
+ * Token storage interface for the GoodVibes SDK.
14
+ *
15
+ * Implement all three core methods. The optional `getTokenEntry` / `setTokenEntry`
16
+ * methods are used by auto-refresh to track token expiry without requiring
17
+ * consumers to change their existing `GoodVibesTokenStore` implementations.
18
+ */
11
19
  export interface GoodVibesTokenStore {
12
20
  getToken(): Promise<string | null>;
13
21
  setToken(token: string | null): Promise<void>;
14
22
  clearToken(): Promise<void>;
23
+ /**
24
+ * Return the current token and its expiry timestamp (unix ms).
25
+ * Optional: when absent, auto-refresh falls back to `getToken()` with no
26
+ * expiry tracking (leeway-based pre-flight checks are disabled).
27
+ */
28
+ getTokenEntry?(): Promise<{
29
+ token: string | null;
30
+ expiresAt?: number;
31
+ }>;
32
+ /**
33
+ * Persist a token alongside its expiry timestamp (unix ms).
34
+ * Optional: when absent, auto-refresh falls back to `setToken()`.
35
+ */
36
+ setTokenEntry?(token: string | null, expiresAt?: number): Promise<void>;
15
37
  }
16
38
  export interface BrowserTokenStoreOptions {
17
39
  readonly key?: string;
@@ -57,6 +79,12 @@ export interface GoodVibesAuthClient {
57
79
  /** Clear the stored token. Delegates to `TokenStore.clearToken()`. */
58
80
  clearToken(): Promise<void>;
59
81
  }
82
+ /**
83
+ * Options for the auto-refresh feature on the auth client.
84
+ *
85
+ * @see AutoRefreshOptions
86
+ */
87
+ export type { AutoRefreshOptions };
60
88
  /**
61
89
  * Create a simple in-memory token store.
62
90
  *
@@ -70,7 +98,7 @@ export interface GoodVibesAuthClient {
70
98
  * const sdk = createGoodVibesSdk({ baseUrl: '...', tokenStore: store });
71
99
  * await sdk.auth.clearToken(); // clears only in-memory
72
100
  */
73
- export declare function createMemoryTokenStore(initialToken?: string | null): GoodVibesTokenStore;
101
+ export declare function createMemoryTokenStore(initialToken?: string | null, initialExpiresAt?: number): GoodVibesTokenStore;
74
102
  /**
75
103
  * Create a token store backed by `localStorage` (or a custom `Storage`).
76
104
  *
@@ -108,5 +136,12 @@ export declare function createBrowserTokenStore(options?: BrowserTokenStoreOptio
108
136
  * // Later: clear the session
109
137
  * await sdk.auth.clearToken();
110
138
  */
111
- export declare function createGoodVibesAuthClient(operator: OperatorSdk, tokenStore: GoodVibesTokenStore | null, getAuthToken?: AuthTokenResolver, observer?: SDKObserver): GoodVibesAuthClient;
139
+ export declare function createGoodVibesAuthClient(operator: OperatorSdk, tokenStore: GoodVibesTokenStore | null, getAuthToken?: AuthTokenResolver, observer?: SDKObserver, autoRefreshOptions?: AutoRefreshOptions,
140
+ /**
141
+ * Optional pre-built `AutoRefreshCoordinator`. When provided, it is reused
142
+ * directly (no new coordinator is constructed). This allows `createGoodVibesSdk`
143
+ * to share a single coordinator instance between the transport middleware and
144
+ * the auth client, avoiding duplicate refresh calls.
145
+ */
146
+ existingCoordinator?: AutoRefreshCoordinator | null): GoodVibesAuthClient;
112
147
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,UAAU,EACX,MAAM,oCAAoC,CAAC;AAW5C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AACpG,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAEnG,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,CAAC,sBAAsB,CAAC,CAAC;AAChF,MAAM,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,oBAAoB,CAAC,CAAC;AAC5E,MAAM,MAAM,oBAAoB,GAAG,oBAAoB,CAAC,oBAAoB,CAAC,CAAC;AAE9E,MAAM,WAAW,mBAAmB;IAClC,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC,CAAC;CACxE;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,2FAA2F;IAC3F,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IACvC,qFAAqF;IACrF,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC;;;;;;;OAOG;IACH,kBAAkB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,kBAAkB,CAAC;IACvE,OAAO,IAAI,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACzC,KAAK,CAAC,KAAK,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE,yBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACtG,wEAAwE;IACxE,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,sEAAsE;IACtE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAYD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,GAAE,MAAM,GAAG,IAAW,GAAG,mBAAmB,CAa9F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,mBAAmB,CAmBnG;AAwBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,WAAW,EACrB,UAAU,EAAE,mBAAmB,GAAG,IAAI,EACtC,YAAY,CAAC,EAAE,iBAAiB,EAChC,QAAQ,CAAC,EAAE,WAAW,GACrB,mBAAmB,CAiErB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/auth.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ConfigurationError } from './_internal/errors/index.js';
2
2
  import { invokeObserver } from './observer/index.js';
3
- import { PermissionResolver, SessionManager, TokenStore, } from './_internal/platform/auth/index.js';
3
+ import { AutoRefreshCoordinator, PermissionResolver, SessionManager, TokenStore, } from './_internal/platform/auth/index.js';
4
4
  // Re-export focused responsibility classes for consumers who prefer
5
5
  // narrower, single-concern APIs over the combined GoodVibesAuthClient facade.
6
6
  // NOTE: OAuthClient is intentionally NOT re-exported here. It depends on
@@ -30,17 +30,29 @@ function requireStorage(storage) {
30
30
  * const sdk = createGoodVibesSdk({ baseUrl: '...', tokenStore: store });
31
31
  * await sdk.auth.clearToken(); // clears only in-memory
32
32
  */
33
- export function createMemoryTokenStore(initialToken = null) {
33
+ export function createMemoryTokenStore(initialToken = null, initialExpiresAt) {
34
34
  let token = initialToken;
35
+ let expiresAt = initialExpiresAt;
35
36
  return {
36
37
  async getToken() {
37
38
  return token;
38
39
  },
39
40
  async setToken(nextToken) {
40
41
  token = nextToken;
42
+ // When setToken is called directly (not via setTokenEntry), clear expiry
43
+ // to avoid stale expiry data attached to a manually-set token.
44
+ expiresAt = undefined;
41
45
  },
42
46
  async clearToken() {
43
47
  token = null;
48
+ expiresAt = undefined;
49
+ },
50
+ async getTokenEntry() {
51
+ return { token, expiresAt };
52
+ },
53
+ async setTokenEntry(nextToken, nextExpiresAt) {
54
+ token = nextToken;
55
+ expiresAt = nextExpiresAt;
44
56
  },
45
57
  };
46
58
  }
@@ -62,6 +74,7 @@ export function createMemoryTokenStore(initialToken = null) {
62
74
  export function createBrowserTokenStore(options = {}) {
63
75
  const storage = requireStorage(options.storage);
64
76
  const key = options.key?.trim() || 'goodvibes.token';
77
+ const expiresAtKey = `${key}.expiresAt`;
65
78
  return {
66
79
  async getToken() {
67
80
  const value = storage.getItem(key);
@@ -70,12 +83,37 @@ export function createBrowserTokenStore(options = {}) {
70
83
  async setToken(token) {
71
84
  if (!token) {
72
85
  storage.removeItem(key);
86
+ storage.removeItem(expiresAtKey);
73
87
  return;
74
88
  }
75
89
  storage.setItem(key, token);
90
+ // Clear expiry when set via setToken (no expiry info provided).
91
+ storage.removeItem(expiresAtKey);
76
92
  },
77
93
  async clearToken() {
78
94
  storage.removeItem(key);
95
+ storage.removeItem(expiresAtKey);
96
+ },
97
+ async getTokenEntry() {
98
+ const value = storage.getItem(key);
99
+ const token = value && value.trim() ? value : null;
100
+ const expiresAtStr = storage.getItem(expiresAtKey);
101
+ const expiresAt = expiresAtStr ? parseInt(expiresAtStr, 10) : undefined;
102
+ return { token, expiresAt: Number.isFinite(expiresAt) ? expiresAt : undefined };
103
+ },
104
+ async setTokenEntry(token, expiresAt) {
105
+ if (!token) {
106
+ storage.removeItem(key);
107
+ storage.removeItem(expiresAtKey);
108
+ return;
109
+ }
110
+ storage.setItem(key, token);
111
+ if (expiresAt !== undefined) {
112
+ storage.setItem(expiresAtKey, String(expiresAt));
113
+ }
114
+ else {
115
+ storage.removeItem(expiresAtKey);
116
+ }
79
117
  },
80
118
  };
81
119
  }
@@ -115,11 +153,34 @@ function assertWritableTokenStore(tokenStore) {
115
153
  * // Later: clear the session
116
154
  * await sdk.auth.clearToken();
117
155
  */
118
- export function createGoodVibesAuthClient(operator, tokenStore, getAuthToken, observer) {
156
+ export function createGoodVibesAuthClient(operator, tokenStore, getAuthToken, observer, autoRefreshOptions,
157
+ /**
158
+ * Optional pre-built `AutoRefreshCoordinator`. When provided, it is reused
159
+ * directly (no new coordinator is constructed). This allows `createGoodVibesSdk`
160
+ * to share a single coordinator instance between the transport middleware and
161
+ * the auth client, avoiding duplicate refresh calls.
162
+ */
163
+ existingCoordinator) {
119
164
  // Construct the split-class instances that own each concern.
120
165
  // The facade delegates to these rather than duplicating logic.
121
166
  const ts = tokenStore ? new TokenStore(tokenStore) : null;
122
167
  const sm = new SessionManager(operator, ts);
168
+ // Use the provided coordinator if available; otherwise build one.
169
+ const coordinator = existingCoordinator !== undefined
170
+ ? existingCoordinator
171
+ : (() => {
172
+ const autoRefresh = autoRefreshOptions?.autoRefresh ?? true;
173
+ const refreshLeewayMs = autoRefreshOptions?.refreshLeewayMs ?? 60_000;
174
+ return tokenStore && autoRefresh
175
+ ? new AutoRefreshCoordinator({
176
+ tokenStore,
177
+ autoRefresh,
178
+ refreshLeewayMs,
179
+ refresh: autoRefreshOptions?.refresh,
180
+ observer,
181
+ })
182
+ : null;
183
+ })();
123
184
  return {
124
185
  get writable() {
125
186
  return sm.writable;
@@ -134,6 +195,10 @@ export function createGoodVibesAuthClient(operator, tokenStore, getAuthToken, ob
134
195
  return new PermissionResolver(snapshot);
135
196
  },
136
197
  async current() {
198
+ if (coordinator) {
199
+ await coordinator.ensureFreshToken();
200
+ return coordinator.withRetryOn401(() => sm.current());
201
+ }
137
202
  return sm.current();
138
203
  },
139
204
  async login(input, options = {}) {
package/dist/client.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { type OperatorSdk } from './_internal/operator/index.js';
2
2
  import { type PeerSdk } from './_internal/peer/index.js';
3
- import type { AuthTokenResolver, HeaderResolver, HttpRetryPolicy, StreamReconnectPolicy } from './_internal/transport-http/index.js';
3
+ import type { AuthTokenResolver, HeaderResolver, HttpRetryPolicy, StreamReconnectPolicy, TransportMiddleware } from './_internal/transport-http/index.js';
4
4
  import { type RemoteRuntimeEvents } from './_internal/transport-realtime/index.js';
5
5
  import type { AnyRuntimeEvent } from './_internal/platform/runtime/events/index.js';
6
- import { type GoodVibesAuthClient, type GoodVibesTokenStore } from './auth.js';
6
+ import { type AutoRefreshOptions, type GoodVibesAuthClient, type GoodVibesTokenStore } from './auth.js';
7
+ import type { SDKObserver } from './observer/index.js';
7
8
  /**
8
9
  * Discriminated union of all runtime events emitted by the GoodVibes daemon.
9
10
  *
@@ -108,6 +109,50 @@ export interface GoodVibesSdkOptions {
108
109
  * reconnect policies, error callback).
109
110
  */
110
111
  readonly realtime?: GoodVibesRealtimeOptions;
112
+ /**
113
+ * Optional observer for SDK-level observability hooks.
114
+ *
115
+ * Pass a `SDKObserver` implementation (or one of the built-in adapters
116
+ * like `createConsoleObserver` / `createOpenTelemetryObserver`) to receive
117
+ * callbacks for auth transitions, transport activity, events, and errors.
118
+ *
119
+ * All observer methods are wrapped in a silent try/catch — observer
120
+ * exceptions never propagate into SDK logic.
121
+ */
122
+ readonly observer?: SDKObserver;
123
+ /**
124
+ * Initial middleware chain applied to every HTTP request/response cycle.
125
+ *
126
+ * Middleware functions receive a mutable `TransportContext` and a `next()`
127
+ * callback. They run in the order provided (outer-first, onion model).
128
+ *
129
+ * Additional middleware can be appended at any time via `sdk.use(mw)`.
130
+ *
131
+ * @example
132
+ * const sdk = createGoodVibesSdk({
133
+ * baseUrl: 'https://daemon.example.com',
134
+ * middleware: [
135
+ * async (ctx, next) => {
136
+ * ctx.headers['X-Request-Id'] = crypto.randomUUID();
137
+ * await next();
138
+ * },
139
+ * ],
140
+ * });
141
+ */
142
+ readonly middleware?: TransportMiddleware[];
143
+ /**
144
+ * Options for silent token auto-refresh.
145
+ *
146
+ * - `autoRefresh` — when `false`, disables silent refresh entirely and lets
147
+ * 401 responses propagate to the caller immediately. Default: `true`.
148
+ * - `refreshLeewayMs` — milliseconds before token expiry to trigger a
149
+ * pre-flight refresh. Default: 60_000 (1 minute).
150
+ * - `refresh` — optional callback invoked to obtain a new token on pre-flight
151
+ * leeway trigger or reactive 401. When absent, pre-flight is a no-op and
152
+ * 401 retry re-reads the token store (useful when an external party updates
153
+ * it). See `AutoRefreshOptions.refresh` for a full example.
154
+ */
155
+ readonly autoRefresh?: AutoRefreshOptions;
111
156
  }
112
157
  /**
113
158
  * Options controlling realtime transport behaviour.
@@ -184,6 +229,20 @@ export interface GoodVibesSdk {
184
229
  * @see https://github.com/mgd34msu/goodvibes-sdk/blob/main/docs/realtime-and-telemetry.md
185
230
  */
186
231
  readonly realtime: GoodVibesRealtime;
232
+ /**
233
+ * Append a middleware to the SDK's HTTP transport chain.
234
+ *
235
+ * Multiple `use()` calls compose in order (outer-first). The method is
236
+ * idempotent in the sense that each call simply appends — call it once per
237
+ * middleware to avoid double-registration.
238
+ *
239
+ * @example
240
+ * sdk.use(async (ctx, next) => {
241
+ * ctx.headers['X-Tenant-Id'] = 'acme';
242
+ * await next();
243
+ * });
244
+ */
245
+ use(middleware: TransportMiddleware): void;
187
246
  }
188
247
  /**
189
248
  * Create a GoodVibes SDK instance.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,OAAO,EAEb,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,qBAAqB,EACtB,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8CAA8C,CAAC;AACpF,OAAO,EAGL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACzB,MAAM,WAAW,CAAC;AAEnB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAEjD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEnC;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,iBAAiB,CAAC;IAE1C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC;IAE1C;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAE9B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC;IAErC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IAEjC;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,SAAS,CAAC;IAE1C;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,wBAAwB,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAC9C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,qBAAqB,CAAC;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,CAAC,CAAC;IAClD,YAAY,CAAC,aAAa,CAAC,EAAE,OAAO,SAAS,GAAG,mBAAmB,CAAC,kBAAkB,CAAC,CAAC;CACzF;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACtC;AA+CD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,mBAAmB,GAC3B,YAAY,CA+Cd"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js CHANGED
@@ -4,10 +4,11 @@ import { createPeerSdk, } from './_internal/peer/index.js';
4
4
  import { normalizeAuthToken } from './_internal/transport-http/index.js';
5
5
  import { createEventSourceConnector, createRemoteRuntimeEvents, createWebSocketConnector, } from './_internal/transport-realtime/index.js';
6
6
  import { createGoodVibesAuthClient, createMemoryTokenStore, } from './auth.js';
7
+ import { AutoRefreshCoordinator, createAutoRefreshMiddleware, } from './_internal/platform/auth/index.js';
7
8
  function requireBaseUrl(baseUrl) {
8
9
  const normalized = baseUrl.trim();
9
10
  if (!normalized) {
10
- throw new ConfigurationError('GoodVibes baseUrl is required');
11
+ throw new ConfigurationError('GoodVibes baseUrl is required. Pass a non-empty baseUrl in your createGoodVibesSdk options (e.g. "https://my-daemon.example.com").');
11
12
  }
12
13
  return normalized;
13
14
  }
@@ -37,6 +38,7 @@ function createClientOptions(options) {
37
38
  ...(options.headers ? { headers: options.headers } : {}),
38
39
  ...(options.getHeaders ? { getHeaders: options.getHeaders } : {}),
39
40
  ...(options.retry ? { retry: options.retry } : {}),
41
+ ...(options.middleware ? { middleware: options.middleware } : {}),
40
42
  };
41
43
  }
42
44
  /**
@@ -63,36 +65,95 @@ function createClientOptions(options) {
63
65
  export function createGoodVibesSdk(options) {
64
66
  const baseUrl = requireBaseUrl(options.baseUrl);
65
67
  const tokenStore = options.tokenStore ?? (options.getAuthToken ? null : createMemoryTokenStore(options.authToken ?? null));
66
- const authToken = options.authToken ?? null;
67
68
  const getAuthToken = tokenStore
68
69
  ? () => tokenStore.getToken()
69
70
  : options.getAuthToken;
70
71
  // Single normalized resolver used by realtime connectors.
71
72
  const tokenResolver = normalizeAuthToken(getAuthToken ?? options.authToken ?? undefined);
72
73
  const fetchImpl = () => requireFetchImplementation(options.fetch);
74
+ const { observer } = options;
75
+ // Build the auto-refresh coordinator when a writable token store is present
76
+ // and autoRefresh is not explicitly disabled.
77
+ const autoRefreshEnabled = (options.autoRefresh?.autoRefresh ?? true) && tokenStore !== null;
78
+ const coordinator = autoRefreshEnabled && tokenStore
79
+ ? new AutoRefreshCoordinator({
80
+ tokenStore,
81
+ autoRefresh: true,
82
+ refreshLeewayMs: options.autoRefresh?.refreshLeewayMs ?? 60_000,
83
+ refresh: options.autoRefresh?.refresh,
84
+ observer,
85
+ })
86
+ : null;
87
+ // Build the merged middleware list: auto-refresh first (if enabled), then
88
+ // consumer-provided middleware. This ensures the auto-refresh pre-flight runs
89
+ // before any consumer middleware sees ctx.headers.Authorization, and that the
90
+ // reactive 401 retry is transparent to consumer middleware.
91
+ //
92
+ // The transport reference for the retry callback is resolved lazily: we pass
93
+ // a proxy object whose `requestJson` property is populated immediately after
94
+ // createOperatorSdk / createPeerSdk returns. Because the middleware only calls
95
+ // `transport.requestJson` on a reactive 401 (not at construction time), the
96
+ // holder is always populated before it is accessed.
97
+ // Lazy transport proxy: populated after createOperatorSdk / createPeerSdk.
98
+ // The middleware only invokes requestJson on a reactive 401 — never at
99
+ // construction time — so the proxy is always populated before first use.
100
+ let operatorRequestJson = null;
101
+ let peerRequestJson = null;
102
+ const buildMiddleware = (getRequestJson) => {
103
+ const mws = [];
104
+ if (coordinator) {
105
+ // Wrap the coordinator + lazy transport reference into a minimal proxy
106
+ // that satisfies createAutoRefreshMiddleware's Pick<HttpJsonTransport, 'requestJson'>.
107
+ const transportProxy = {
108
+ requestJson(url, opts) {
109
+ const rj = getRequestJson();
110
+ if (!rj)
111
+ throw new Error('Auto-refresh: transport not yet initialised');
112
+ return rj(url, opts);
113
+ },
114
+ };
115
+ mws.push(createAutoRefreshMiddleware(coordinator, transportProxy, tokenStore));
116
+ }
117
+ if (options.middleware) {
118
+ mws.push(...options.middleware);
119
+ }
120
+ return mws;
121
+ };
73
122
  const operator = createOperatorSdk(createClientOptions({
74
123
  ...options,
75
124
  tokenStore: tokenStore ?? undefined,
125
+ middleware: buildMiddleware(() => operatorRequestJson),
76
126
  }));
127
+ // Populate the lazy reference now that the transport is fully constructed.
128
+ operatorRequestJson = (url, opts) => operator.transport.requestJson(url, opts);
77
129
  const peer = createPeerSdk(createClientOptions({
78
130
  ...options,
79
131
  tokenStore: tokenStore ?? undefined,
132
+ middleware: buildMiddleware(() => peerRequestJson),
80
133
  }));
134
+ // Populate the lazy reference now that the transport is fully constructed.
135
+ peerRequestJson = (url, opts) => peer.transport.requestJson(url, opts);
81
136
  return {
82
137
  operator,
83
138
  peer,
84
- auth: createGoodVibesAuthClient(operator, tokenStore, getAuthToken),
139
+ auth: createGoodVibesAuthClient(operator, tokenStore, getAuthToken, observer, options.autoRefresh, coordinator),
140
+ use(middleware) {
141
+ operator.transport.use(middleware);
142
+ peer.transport.use(middleware);
143
+ },
85
144
  realtime: {
86
145
  viaSse() {
87
146
  return createRemoteRuntimeEvents(createEventSourceConnector(baseUrl, tokenResolver, fetchImpl(), {
88
147
  reconnect: options.realtime?.sseReconnect,
89
148
  onError: options.realtime?.onError,
149
+ observer,
90
150
  }));
91
151
  },
92
152
  viaWebSocket(webSocketImpl) {
93
153
  return createRemoteRuntimeEvents(createWebSocketConnector(baseUrl, tokenResolver, requireWebSocketImplementation(webSocketImpl ?? options.WebSocketImpl), {
94
154
  reconnect: options.realtime?.webSocketReconnect,
95
155
  onError: options.realtime?.onError,
156
+ observer,
96
157
  }));
97
158
  },
98
159
  },
package/dist/expo.d.ts CHANGED
@@ -17,4 +17,5 @@ export interface ExpoGoodVibesSdkOptions extends ReactNativeGoodVibesSdkOptions
17
17
  */
18
18
  export { forSession } from './transport-realtime.js';
19
19
  export declare function createExpoGoodVibesSdk(options: ExpoGoodVibesSdkOptions): ReactNativeGoodVibesSdk;
20
+ export { createExpoSecureTokenStore, type ExpoSecureTokenStore, type ExpoSecureTokenStoreOptions, type ExpoSecureStoreAccessible, } from './_internal/platform/auth/expo-secure-token-store.js';
20
21
  //# sourceMappingURL=expo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"expo.d.ts","sourceRoot":"","sources":["../src/expo.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,8BAA8B,EACpC,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,uBAAwB,SAAQ,8BAA8B;CAAG;AAElF;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,uBAAuB,CAEhG"}
1
+ {"version":3,"file":"expo.d.ts","sourceRoot":"","sources":["../src/expo.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,8BAA8B,EACpC,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,uBAAwB,SAAQ,8BAA8B;CAAG;AAElF;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,uBAAuB,CAEhG;AAED,OAAO,EACL,0BAA0B,EAC1B,KAAK,oBAAoB,EACzB,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,GAC/B,MAAM,sDAAsD,CAAC"}
package/dist/expo.js CHANGED
@@ -17,3 +17,4 @@ export { forSession } from './transport-realtime.js';
17
17
  export function createExpoGoodVibesSdk(options) {
18
18
  return createReactNativeGoodVibesSdk(options);
19
19
  }
20
+ export { createExpoSecureTokenStore, } from './_internal/platform/auth/expo-secure-token-store.js';