@truenas/api-client 9.0.3 → 9.0.4

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
@@ -186,6 +186,15 @@ The client then works against an older or newer release call for call; a
186
186
  method or field that release lacks fails when it is called rather than at
187
187
  compile time.
188
188
 
189
+ Each version's models are under its namespace, and its enums are values there
190
+ as well as types, so a request can be built from them:
191
+
192
+ ```typescript
193
+ import { v28_0_0 } from '@truenas/api-client';
194
+
195
+ const type: v28_0_0.KeychainCredentialEntryType = v28_0_0.KeychainCredentialEntryType.SshKeyPair;
196
+ ```
197
+
189
198
  For code that must run against any supported release, use `client.ops`. It
190
199
  offers the same calls on every version and maps them onto whatever that version
191
200
  provides:
@@ -249,6 +258,29 @@ version stays the same, so a different version needs a new client:
249
258
  client.connection.setEndpoint({ hostnames: ['10.0.0.5'], protocol: 'http:' });
250
259
  ```
251
260
 
261
+ An `interceptor` sees the connection's traffic, for a debug panel or for
262
+ mocking calls in a running app. `outgoing` gets every frame before it is
263
+ written, including the client's own `core.set_options` and `core.ping`, and
264
+ returning `false` keeps a frame from `send` off the socket. `injected$` frames
265
+ are read as if the appliance had sent them, one tick later. `incoming` gets
266
+ every frame read:
267
+
268
+ ```typescript
269
+ const mocked$ = new Subject<TrueNasMessage>();
270
+ const client = await createTrueNasClient({
271
+ uuid, hostnames, enabled: true,
272
+ interceptor: {
273
+ outgoing: (frame) => {
274
+ if (frame.method !== 'system.info') return;
275
+ mocked$.next({ jsonrpc: '2.0', id: frame.id, result: fakeInfo });
276
+ return false;
277
+ },
278
+ incoming: (frame) => console.debug('<-', frame),
279
+ injected$: mocked$,
280
+ },
281
+ });
282
+ ```
283
+
252
284
  An appliance served without TLS needs `protocol: 'http:'`, which switches
253
285
  discovery to `http` and the socket to `ws`. It describes the appliance, not the
254
286
  page, and defaults to `https:`.
@@ -1,4 +1,4 @@
1
- import { BehaviorSubject, filter, switchMap, catchError, EMPTY, tap, map, finalize, take, Subject, distinctUntilChanged, of, startWith, concat, throwError, retry, race, timer, skip, takeUntil, shareReplay, interval, Observable, timeout, TimeoutError, toArray, from, defer, firstValueFrom, share, ignoreElements, merge, takeWhile, skipWhile, pairwise } from 'rxjs';
1
+ import { BehaviorSubject, filter, switchMap, catchError, EMPTY, tap, map, finalize, take, Subject, distinctUntilChanged, of, startWith, concat, throwError, retry, race, timer, skip, takeUntil, shareReplay, merge, observeOn, asapScheduler, share, interval, Subscription, Observable, timeout, TimeoutError, toArray, from, defer, firstValueFrom, ignoreElements, takeWhile, skipWhile, pairwise } from 'rxjs';
2
2
  import { webSocket } from 'rxjs/webSocket';
3
3
  import { map as map$1, catchError as catchError$1, shareReplay as shareReplay$1 } from 'rxjs/operators';
4
4
 
@@ -3203,13 +3203,14 @@ function socketScheme(protocol) {
3203
3203
  var tenSeconds = 10 * 1e3;
3204
3204
  var twentySeconds = 20 * 1e3;
3205
3205
  var TrueNasConnection = class {
3206
- constructor(initialEnabled, hostnames, systemUuid, websocketPath, systemName, retryDelay = tenSeconds, maxRetry = 3, logger = noopLogger, protocol = "https:") {
3206
+ constructor(initialEnabled, hostnames, systemUuid, websocketPath, systemName, retryDelay = tenSeconds, maxRetry = 3, logger = noopLogger, protocol = "https:", interceptor) {
3207
3207
  this.systemUuid = systemUuid;
3208
3208
  this.websocketPath = websocketPath;
3209
3209
  this.systemName = systemName;
3210
3210
  this.retryDelay = retryDelay;
3211
3211
  this.maxRetry = maxRetry;
3212
3212
  this.logger = logger;
3213
+ this.interceptor = interceptor;
3213
3214
  // compatibility properties
3214
3215
  this.opened = new BehaviorSubject(false);
3215
3216
  this.closed = new Subject();
@@ -3267,6 +3268,7 @@ var TrueNasConnection = class {
3267
3268
  const initMessage = createJsonRpcMessage("core.set_options", [
3268
3269
  { legacy_jobs: false }
3269
3270
  ]);
3271
+ this.interceptor?.outgoing?.(initMessage);
3270
3272
  connection.ws.next(initMessage);
3271
3273
  return connection.ws.messages().pipe(
3272
3274
  filter((msg) => msg.id === initMessage.id),
@@ -3365,22 +3367,27 @@ var TrueNasConnection = class {
3365
3367
  map((conn) => conn.state === "error"),
3366
3368
  takeUntil(this.closeConnection)
3367
3369
  );
3368
- /**
3369
- * observable which always emits messages from the current socket.
3370
- */
3371
- this.messages$ = this.ws$.pipe(
3370
+ /** What the appliance sends, on whichever socket is current. */
3371
+ this.socketMessages$ = this.ws$.pipe(
3372
3372
  filter((ws) => ws !== null),
3373
3373
  switchMap((ws) => ws.messages().pipe(
3374
3374
  catchError(() => EMPTY)
3375
3375
  ))
3376
3376
  );
3377
+ this.messages$ = interceptor?.injected$ ? merge(this.socketMessages$, interceptor.injected$.pipe(observeOn(asapScheduler), share())) : this.socketMessages$;
3377
3378
  this.endpoint$.next(resolveEndpoint(hostnames, protocol));
3378
3379
  this.ws$.pipe(
3379
3380
  switchMap((ws) => ws ? interval(twentySeconds).pipe(map(() => ws)) : EMPTY),
3380
3381
  takeUntil(this.closeConnection)
3381
3382
  ).subscribe((ws) => {
3382
- ws.next(createJsonRpcMessage("core.ping"));
3383
+ const ping = createJsonRpcMessage("core.ping");
3384
+ this.interceptor?.outgoing?.(ping);
3385
+ ws.next(ping);
3383
3386
  });
3387
+ if (interceptor?.incoming) {
3388
+ const incoming = interceptor.incoming.bind(interceptor);
3389
+ this.messages$.pipe(takeUntil(this.closeConnection)).subscribe((message) => incoming(message));
3390
+ }
3384
3391
  this.opened$.subscribe((val) => this.opened.next(val));
3385
3392
  this.closed$.subscribe(() => this.closed.next());
3386
3393
  this.hostname$.pipe(filter(Boolean)).subscribe((name) => this.hostname.next(name));
@@ -3449,6 +3456,9 @@ var TrueNasConnection = class {
3449
3456
  * the next `closed$` emission.
3450
3457
  */
3451
3458
  send(message) {
3459
+ if (this.interceptor?.outgoing?.(message) === false) {
3460
+ return Subscription.EMPTY;
3461
+ }
3452
3462
  return this.ws$.pipe(
3453
3463
  filter((ws) => ws !== null),
3454
3464
  take(1)
@@ -3646,7 +3656,8 @@ var TrueNasApiClient = class {
3646
3656
  this.reconnect.retryDelay,
3647
3657
  this.reconnect.maxRetry,
3648
3658
  this.logger,
3649
- this.protocol
3659
+ this.protocol,
3660
+ this.reconnect.interceptor
3650
3661
  );
3651
3662
  }
3652
3663
  /**
@@ -4511,7 +4522,7 @@ function instantiateClientForVersion(version, opts, logger) {
4511
4522
  systemName,
4512
4523
  logger,
4513
4524
  opts.protocol,
4514
- { retryDelay: opts.retryDelay, maxRetry: opts.maxRetry }
4525
+ { retryDelay: opts.retryDelay, maxRetry: opts.maxRetry, interceptor: opts.interceptor }
4515
4526
  );
4516
4527
  }
4517
4528
  function errorMessageOrDefault(error, fallback) {
@@ -4531,5 +4542,5 @@ var UserRole = /* @__PURE__ */ ((UserRole2) => {
4531
4542
  })(UserRole || {});
4532
4543
 
4533
4544
  export { AppState, AuthError, AuthErrorCode, AuthResponseType, InvalidVersionResponseError, JobState, NoCompatibleVersionsError, SUPPORTED_API_VERSIONS, TrueNasApiClient, TrueNasApiClientV2510, TrueNasApiClientV27, TrueNasApiClientV28, TrueNasApiError, TrueNasAuthMechanism, TrueNasAuthenticator, TrueNasConnection, UserRole, VersionCompatibility, VersionDiscovery, VersionDiscoveryError, VersionDiscoveryNetworkError, VersionDiscoveryTimeoutError, VersionEndpointNotFoundError, VersionTooNewError, VersionTooOldError, clientClassFor, consoleLogger, createTrueNasClient, getApiErrorMessage, getCloseMessage, isJobFinished, noopLogger, parseApiVersion, policyViolationCloseCode, subscriptionName, v25_10_0_exports, v25_10_1_exports, v25_10_2_exports, v25_10_3_exports, v25_10_4_exports, v25_10_5_exports, v27_0_0_exports, v28_0_0_exports };
4534
- //# sourceMappingURL=chunk-K5ICTA2D.js.map
4535
- //# sourceMappingURL=chunk-K5ICTA2D.js.map
4545
+ //# sourceMappingURL=chunk-PEOXWWBS.js.map
4546
+ //# sourceMappingURL=chunk-PEOXWWBS.js.map