@smartmemory/sdk-js 1.4.119 → 1.4.120

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
@@ -269,23 +269,83 @@ client.authAPI.listAPIKeys();
269
269
  client.authAPI.revokeAPIKey(keyId);
270
270
  ```
271
271
 
272
- ## Fetch Utilities
272
+ ## Session and connection recovery
273
273
 
274
274
  ```javascript
275
275
  import { createAuthFetch, installInterceptor } from '@smartmemory/sdk-js/fetch';
276
+ import { subscribeProgress } from '@smartmemory/sdk-js/progress';
276
277
 
277
- // Wrap individual fetch calls
278
- const authFetch = createAuthFetch(client.auth);
279
- const response = await authFetch('http://api.example.com/data');
278
+ // Inject into app services/adapters. Neither helper changes globalThis.fetch.
279
+ const apiFetch = createAuthFetch(client.auth);
280
+ const scopedFetch = installInterceptor(client.auth, {
281
+ apiBases: ['https://studio-api.example.com'], // explicitly trusted extra API base
282
+ urlPatterns: ['/api/', '/memory/'], // optional additional restriction
283
+ });
284
+ const response = await apiFetch(`${client.auth.apiBaseUrl}/memory/list`);
280
285
 
281
- // Or intercept all fetch calls globally
282
- const uninstall = installInterceptor(client.auth, {
283
- urlPatterns: ['localhost:9001']
286
+ const unsubscribe = client.connection.subscribe(({ status, reason }) => {
287
+ // Render connected | reconnecting | signed_out and the reason (string or null).
288
+ renderConnectionStatus(status, reason);
289
+ });
290
+ const stream = subscribeProgress({
291
+ baseUrl: client.auth.apiBaseUrl,
292
+ auth: client.auth, // current headers on EVERY connection, including cookie sessions
293
+ onEvent: event => renderProgress(event),
294
+ onReconnect: () => console.warn('Progress reconnecting'),
295
+ onError: error => showTerminalError(error),
284
296
  });
285
- // ... all matching fetch calls now include auth headers
286
- uninstall(); // restore original fetch
297
+ // On unmount, logout, or workspace change:
298
+ stream.close();
299
+ unsubscribe();
287
300
  ```
288
301
 
302
+ `BaseAPI` JSON/binary requests and the fetch helpers share one policy: a 401
303
+ refreshes the session and retries once with current headers. Refresh is
304
+ single-flight per AuthCore, includes cookies when configured, and echoes the
305
+ `sm_csrf` cookie as `x-csrf-token` through `getRequestOptions`. A refresh 401/403
306
+ or a second request 401 clears local auth. Ordinary request 403 does not refresh
307
+ or sign out. A network/429/5xx refresh failure throws `SessionRefreshError` with
308
+ `recoverable: true`, retains auth, and reports `reconnecting`. No mutation is
309
+ replayed after an ambiguous network failure. Request bodies, cancellation, and
310
+ workspace scope are preserved; a workspace change cancels recovery.
311
+
312
+ `client.connection` and `client.auth.connection` are the same observable.
313
+ `subscribe(listener)` immediately emits `{ status, reason }` and returns an
314
+ unsubscribe function; `snapshot` reads current state. Failures are tracked per
315
+ request URL and per stream, so unrelated successes cannot hide them. `connected`
316
+ means no currently recorded connection failure, not a proactive health probe.
317
+ Requests are retried only on 401; apps retain ownership of ordinary polling and
318
+ retrying failed reads. Streams reconnect automatically with exponential delay
319
+ from 1 second to a 30-second cap, indefinitely for transient failures and EOF.
320
+ Online/visible events resume immediately. Auth recovery retries once on 401;
321
+ other 4xx except 408/429 terminate through `onError`.
322
+
323
+ Scope streams resume using the exact SSE `id` in `since` and `Last-Event-ID`.
324
+ Run streams use `runId` and the next inclusive `fromSeq` boundary. Server scope
325
+ replay may repeat the boundary event: consumers should deduplicate event IDs or
326
+ `(run_id, seq)`. `close()` cancels timers, aborts transport, removes listeners,
327
+ and suppresses stale callbacks. Recreate a subscription on workspace change;
328
+ the SDK refuses to carry a cursor across workspaces. For deliberate finite
329
+ replay, set `reconnect: false` and optionally `onComplete`; EOF then completes.
330
+ Static `token`/`apiKey` remain supported, but automatic session refresh requires
331
+ `auth`. `getHeaders()` can provide live synchronous headers. `fetchFn` injects
332
+ a transport for either fetch helpers or progress; passing a raw transport avoids
333
+ stacking recovery wrappers.
334
+
335
+ **Interceptor migration:** `installInterceptor(auth, options)` now returns an
336
+ injectable fetch function, **not an uninstaller**. Replace old global-install
337
+ call sites and route their API calls through that function (or
338
+ `createAuthFetch`). No global fetch mutation is performed. Credentials/recovery
339
+ are restricted to `auth.apiBaseUrl` plus explicitly configured `apiBases`, with
340
+ origin and path-boundary matching. `/auth/*` requests bypass the wrapper to avoid
341
+ recursive refresh; auth bootstrap remains owned by AuthCore/app code. Pass the
342
+ actual method/headers to `getRequestOptions` for custom cookie-auth mutations.
343
+
344
+ TypeScript declarations ship for `/fetch`, `/progress`, and `/connection`.
345
+ The latter exports `ConnectionStatus`, `SessionRefreshError`, and the structural
346
+ `RecoveryClient`, `RecoveryAuth`, `ConnectionSnapshot`, and `ConnectionState`
347
+ types. Existing JavaScript client/domain APIs retain their prior typing surface.
348
+
289
349
  ## Migration from AuthService.js
290
350
 
291
351
  Replace the per-app AuthService pattern:
@@ -0,0 +1,5 @@
1
+ import { C as s, S as e } from "./index-BKI1h_Wn.js";
2
+ export {
3
+ s as ConnectionStatus,
4
+ e as SessionRefreshError
5
+ };