@truenas/api-client 3.0.2 → 3.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 +92 -7
- package/dist/index.cjs +57 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +138 -8
- package/dist/index.d.ts +138 -8
- package/dist/index.js +57 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -12460,6 +12460,16 @@ declare class TrueNasSocket {
|
|
|
12460
12460
|
complete(): void;
|
|
12461
12461
|
}
|
|
12462
12462
|
|
|
12463
|
+
/**
|
|
12464
|
+
* The scheme used to reach the appliance, in `location.protocol` form.
|
|
12465
|
+
*
|
|
12466
|
+
* Colon included, so a caller served *by the appliance* can pass
|
|
12467
|
+
* `location.protocol` unchanged. That equivalence only holds same-origin: this
|
|
12468
|
+
* value is spliced onto the hostname the client connects to, so a page served
|
|
12469
|
+
* from somewhere else must say what the appliance uses, not what it uses.
|
|
12470
|
+
*/
|
|
12471
|
+
type ApplianceProtocol = 'http:' | 'https:';
|
|
12472
|
+
|
|
12463
12473
|
interface ActiveConnection {
|
|
12464
12474
|
ws: TrueNasSocket;
|
|
12465
12475
|
hostname: string;
|
|
@@ -12482,6 +12492,7 @@ declare class TrueNasConnection {
|
|
|
12482
12492
|
readonly retryDelay: number;
|
|
12483
12493
|
readonly maxRetry: number;
|
|
12484
12494
|
readonly logger: Logger;
|
|
12495
|
+
readonly protocol: ApplianceProtocol;
|
|
12485
12496
|
opened: BehaviorSubject<boolean>;
|
|
12486
12497
|
closed: Subject<void>;
|
|
12487
12498
|
hostname: BehaviorSubject<string>;
|
|
@@ -12544,7 +12555,7 @@ declare class TrueNasConnection {
|
|
|
12544
12555
|
* observable which always emits messages from the current socket.
|
|
12545
12556
|
*/
|
|
12546
12557
|
messages$: Observable<TrueNasMessage>;
|
|
12547
|
-
constructor(initialEnabled: boolean, hostnames: string[], systemUuid: string, websocketPath: string, systemName?: string | undefined, retryDelay?: number, maxRetry?: number, logger?: Logger);
|
|
12558
|
+
constructor(initialEnabled: boolean, hostnames: string[], systemUuid: string, websocketPath: string, systemName?: string | undefined, retryDelay?: number, maxRetry?: number, logger?: Logger, protocol?: ApplianceProtocol);
|
|
12548
12559
|
/**
|
|
12549
12560
|
* whether the connection has exhausted its retries — the **cumulative** snapshot, read
|
|
12550
12561
|
* synchronously. (Formerly `hasConnectionError()`; renamed to disambiguate it from the
|
|
@@ -13192,7 +13203,8 @@ declare abstract class TrueNasApiClient<D extends ApiDirectoryShape = BaseApiDir
|
|
|
13192
13203
|
protected readonly systemName: string | undefined;
|
|
13193
13204
|
/** Logger forwarded to the connection (defaults to a no-op). */
|
|
13194
13205
|
protected readonly logger: Logger;
|
|
13195
|
-
|
|
13206
|
+
protected readonly protocol: ApplianceProtocol;
|
|
13207
|
+
constructor(uuid: string, hostnames: string[], version: ApiVersion, enabled: boolean, systemName?: string, logger?: Logger, protocol?: ApplianceProtocol);
|
|
13196
13208
|
/**
|
|
13197
13209
|
* Get current connection status.
|
|
13198
13210
|
* @returns true if WebSocket is connected
|
|
@@ -27559,6 +27571,19 @@ declare const SUPPORTED_API_VERSIONS: readonly ["v25.10.0", "v25.10.1", "v25.10.
|
|
|
27559
27571
|
* how the missing 22 methods went unnoticed in the first place.
|
|
27560
27572
|
*/
|
|
27561
27573
|
type DefaultApiDirectory = ApiDirectory$7;
|
|
27574
|
+
/**
|
|
27575
|
+
* The surface a named version derives, falling back when nothing was narrowed.
|
|
27576
|
+
*
|
|
27577
|
+
* `V` only pins a directory when inference narrowed it. A wrapper typed
|
|
27578
|
+
* `(version: SupportedApiVersion)` widens it back to the whole union, and
|
|
27579
|
+
* indexing by a union yields a union of directories whose usable methods are
|
|
27580
|
+
* their *intersection* — narrower than the default surface, so naming the
|
|
27581
|
+
* version would buy fewer methods than naming nothing. That case falls back to
|
|
27582
|
+
* {@link DefaultApiDirectory} instead, matching every other shape that loses
|
|
27583
|
+
* the literal. A partial union still derives: methods common to the versions
|
|
27584
|
+
* named is the right answer for "one of these".
|
|
27585
|
+
*/
|
|
27586
|
+
type DerivedDirectory<V extends SupportedApiVersion> = SupportedApiVersion extends V ? DefaultApiDirectory : ApiDirectoryByVersion[V];
|
|
27562
27587
|
/** Options for {@link createTrueNasClient}. */
|
|
27563
27588
|
interface CreateClientOptions {
|
|
27564
27589
|
/** System UUID. */
|
|
@@ -27582,12 +27607,44 @@ interface CreateClientOptions {
|
|
|
27582
27607
|
* through the client, to the connection.
|
|
27583
27608
|
*/
|
|
27584
27609
|
logger?: Logger;
|
|
27610
|
+
/**
|
|
27611
|
+
* The appliance's API version, when the caller already knows it.
|
|
27612
|
+
*
|
|
27613
|
+
* Supplying it skips version discovery entirely — no `GET /api/versions`, no
|
|
27614
|
+
* CORS fallback — and *derives* the client's typed surface from the string,
|
|
27615
|
+
* so `version: 'v27.0.0'` yields `TrueNasApiClient<ApiDirectoryV27_0_0>`
|
|
27616
|
+
* without the caller asserting it through a type parameter.
|
|
27617
|
+
*/
|
|
27618
|
+
version?: SupportedApiVersion;
|
|
27619
|
+
/**
|
|
27620
|
+
* The scheme to reach the appliance on, in `location.protocol` form.
|
|
27621
|
+
*
|
|
27622
|
+
* Selects both halves of the transport: `https:` gives `https` discovery and
|
|
27623
|
+
* a `wss` socket, `http:` gives `http` and `ws`. Defaults to `https:`, which
|
|
27624
|
+
* is what an appliance serves and what Connect uses.
|
|
27625
|
+
*
|
|
27626
|
+
* This describes the *appliance*, not the page. Passing
|
|
27627
|
+
* `location.protocol` is correct when the appliance serves the page — the
|
|
27628
|
+
* same-origin case this exists for — and wrong otherwise. A page on
|
|
27629
|
+
* `http://localhost:5173` talking to an https appliance gets both halves
|
|
27630
|
+
* wrong, but only one of them says so: `fetch` follows the redirect and
|
|
27631
|
+
* discovery appears to work, while the WebSocket has no such tolerance and
|
|
27632
|
+
* fails the handshake without naming the scheme.
|
|
27633
|
+
*
|
|
27634
|
+
* Omitting it against a plaintext appliance is the quieter failure and the
|
|
27635
|
+
* likelier one, since it is the case this option exists for. Discovery tries
|
|
27636
|
+
* `https`, `fetch` rejects, and that is indistinguishable from the CORS block
|
|
27637
|
+
* v25.10.0 has on `/api/versions` — so the fallback fires and the caller gets
|
|
27638
|
+
* a client pinned to v25.10.0, warned about only in the log.
|
|
27639
|
+
*/
|
|
27640
|
+
protocol?: ApplianceProtocol;
|
|
27585
27641
|
}
|
|
27586
27642
|
/**
|
|
27587
27643
|
* Creates a version-specific TrueNAS API client.
|
|
27588
27644
|
*
|
|
27589
27645
|
* 1. Discovers the API version (`GET /api/versions`), asking every hostname in
|
|
27590
|
-
* parallel. The first usable answer wins.
|
|
27646
|
+
* parallel. The first usable answer wins — *unless* `opts.version` says
|
|
27647
|
+
* which version this is, in which case discovery is skipped entirely.
|
|
27591
27648
|
* 2. Selects the matching client implementation (`v25.10.x` -> `TrueNasApiClientV2510`,
|
|
27592
27649
|
* `v26.x.y` -> `TrueNasApiClientV26`, `v27.x.y` -> `TrueNasApiClientV27`).
|
|
27593
27650
|
* 3. Instantiates and returns it.
|
|
@@ -27618,9 +27675,77 @@ interface CreateClientOptions {
|
|
|
27618
27675
|
* supported version. Operations that must work across versions belong on
|
|
27619
27676
|
* `client.ops`, which resolves them at runtime.
|
|
27620
27677
|
*
|
|
27678
|
+
* ## Naming the version instead
|
|
27679
|
+
*
|
|
27680
|
+
* A caller that already knows its appliance — a UI served by the appliance
|
|
27681
|
+
* itself, a test harness against a pinned image — can say so and skip discovery
|
|
27682
|
+
* altogether:
|
|
27683
|
+
*
|
|
27684
|
+
* ```typescript
|
|
27685
|
+
* const client = await createTrueNasClient({
|
|
27686
|
+
* uuid, hostnames, enabled: true, version: 'v27.0.0',
|
|
27687
|
+
* });
|
|
27688
|
+
* // client: TrueNasApiClient<ApiDirectoryV27_0_0>, derived from the string
|
|
27689
|
+
* ```
|
|
27690
|
+
*
|
|
27691
|
+
* The surface is *derived* rather than asserted: `ApiDirectoryByVersion` maps
|
|
27692
|
+
* the version string to its directory, so the caller writes no cast and names
|
|
27693
|
+
* no directory type. A version this package ships no types for does not
|
|
27694
|
+
* compile.
|
|
27695
|
+
*
|
|
27696
|
+
* **The derivation needs the version to be literal at the call site.** It comes
|
|
27697
|
+
* from inference on `{ version: V }`, so it holds for a string literal written
|
|
27698
|
+
* in the options object (or a `const`-typed one). It does not survive
|
|
27699
|
+
* indirection:
|
|
27700
|
+
*
|
|
27701
|
+
* - `createTrueNasClient<D>({ …, version: 'v27.0.0' })` — an explicit type
|
|
27702
|
+
* argument makes the derived overload inapplicable, so `D` wins.
|
|
27703
|
+
* - `(v?: SupportedApiVersion) => createTrueNasClient({ …, version: v })` — the
|
|
27704
|
+
* property is `SupportedApiVersion | undefined`, which no `V` satisfies.
|
|
27705
|
+
* - `(v: SupportedApiVersion) => …` — reaches this overload, but `V` widens to
|
|
27706
|
+
* the whole union, which derives nothing.
|
|
27707
|
+
* - `const opts: CreateClientOptions = { …, version: 'v27.0.0' }` — the
|
|
27708
|
+
* annotation widens the property before the call sees it.
|
|
27709
|
+
*
|
|
27710
|
+
* All of them compile, run against the named version, and type as
|
|
27711
|
+
* {@link DefaultApiDirectory}. That fails in the safe direction — understated
|
|
27712
|
+
* types give a compile error at the method call rather than a runtime surprise —
|
|
27713
|
+
* but it is silent, so a wrapper that forwards a version gets none of the
|
|
27714
|
+
* surface it named. Keep the literal at the call site, and if you are adding
|
|
27715
|
+
* `version` to an existing `createTrueNasClient<ApiDirectoryV26_0_0>(opts)`
|
|
27716
|
+
* call, delete the type argument in the same edit.
|
|
27717
|
+
*
|
|
27718
|
+
* Two consequences worth knowing before reaching for it.
|
|
27719
|
+
*
|
|
27720
|
+
* It is a stronger claim than `D` alone, because it also picks the websocket
|
|
27721
|
+
* path. Naming `v27.0.0` at a v26 appliance connects on `/api/v27.0.0` with v27
|
|
27722
|
+
* types over a v26 server, and discovery cannot correct it — declining
|
|
27723
|
+
* discovery is the whole point. `D` on its own only mistyped the surface; this
|
|
27724
|
+
* mistypes the surface *and* dials the wrong number.
|
|
27725
|
+
*
|
|
27726
|
+
* Compatibility is still checked. Skipping discovery skips the network round
|
|
27727
|
+
* trip, not the range check, which is local and free. Two refusals reach a
|
|
27728
|
+
* caller, and they are not interchangeable:
|
|
27729
|
+
*
|
|
27730
|
+
* - a string that is not a `SupportedApiVersion` at all — only reachable from
|
|
27731
|
+
* JavaScript — throws a plain `Error` naming the versions that are.
|
|
27732
|
+
* - a version this package ships types for but cannot build a client for
|
|
27733
|
+
* throws {@link VersionTooNewError}, the same error discovery raises.
|
|
27734
|
+
*
|
|
27735
|
+
* The second is not hypothetical. `MAX_SUPPORTED_VERSION` is a hand-written
|
|
27736
|
+
* literal, and while it matches the newest generated version today, a
|
|
27737
|
+
* regeneration can add a year before anyone writes its client — at which point
|
|
27738
|
+
* that version is nameable, promised by the overload, and has nothing to build.
|
|
27739
|
+
* `VersionTooOldError` has no counterpart here: `MIN_SUPPORTED_VERSION` is
|
|
27740
|
+
* derived from the same list that constrains the type, so nothing nameable is
|
|
27741
|
+
* below it.
|
|
27742
|
+
*
|
|
27743
|
+
* @typeParam V - the version named in `opts.version`, when one is. The returned
|
|
27744
|
+
* surface is `ApiDirectoryByVersion[V]`, so it is derived rather than chosen.
|
|
27621
27745
|
* @typeParam D - the generated API surface the client is typed against, as a
|
|
27622
|
-
* whole (`call`, `job`, `event`)
|
|
27623
|
-
*
|
|
27746
|
+
* whole (`call`, `job`, `event`), for the discovery path where no version is
|
|
27747
|
+
* named. Every verb resolves method names against it, so naming a method this
|
|
27748
|
+
* surface does not have is a build error.
|
|
27624
27749
|
* @returns a Promise that resolves with the created client, or rejects with a
|
|
27625
27750
|
* {@link VersionDiscoveryError} subclass (or a client-selection error).
|
|
27626
27751
|
* Rejects if version discovery on all hostnames *fails* and is not recoverable.
|
|
@@ -27630,6 +27755,9 @@ interface CreateClientOptions {
|
|
|
27630
27755
|
* bug. A network error alongside a version-compatibility error or a 404 does
|
|
27631
27756
|
* *not* reach the fallback — see `selectRepresentativeFailure`.
|
|
27632
27757
|
*/
|
|
27758
|
+
declare function createTrueNasClient<V extends SupportedApiVersion>(opts: CreateClientOptions & {
|
|
27759
|
+
version: V;
|
|
27760
|
+
}): Promise<TrueNasApiClient<DerivedDirectory<V>>>;
|
|
27633
27761
|
declare function createTrueNasClient<D extends ApiDirectoryShape = DefaultApiDirectory>(opts: CreateClientOptions): Promise<TrueNasApiClient<D>>;
|
|
27634
27762
|
|
|
27635
27763
|
/**
|
|
@@ -27789,12 +27917,14 @@ declare class TrueNasApiClientV27 extends TrueNasApiClient<ApiDirectory> {
|
|
|
27789
27917
|
*/
|
|
27790
27918
|
declare class VersionDiscovery {
|
|
27791
27919
|
private readonly logger;
|
|
27920
|
+
private readonly protocol;
|
|
27792
27921
|
private versionCache;
|
|
27793
|
-
constructor(logger?: Logger);
|
|
27922
|
+
constructor(logger?: Logger, protocol?: ApplianceProtocol);
|
|
27923
|
+
private versionsUrl;
|
|
27794
27924
|
/**
|
|
27795
27925
|
* Discovers the API version for a given hostname.
|
|
27796
27926
|
*
|
|
27797
|
-
* Makes a GET request to `
|
|
27927
|
+
* Makes a GET request to `{protocol}//{hostname}/api/versions` and returns the latest
|
|
27798
27928
|
* compatible version. Results are cached per hostname; the cache entry is removed
|
|
27799
27929
|
* on failure so the next call retries.
|
|
27800
27930
|
*
|
|
@@ -27973,4 +28103,4 @@ type ApiError = JsonRpcError | TrueNasError;
|
|
|
27973
28103
|
*/
|
|
27974
28104
|
declare function getApiErrorMessage(error: unknown, fallback?: string): string;
|
|
27975
28105
|
|
|
27976
|
-
export { type ApiCallDirectory$7 as ApiCallDirectoryV25_10_0, type ApiCallDirectory$6 as ApiCallDirectoryV25_10_1, type ApiCallDirectory$5 as ApiCallDirectoryV25_10_2, type ApiCallDirectory$4 as ApiCallDirectoryV25_10_3, type ApiCallDirectory$3 as ApiCallDirectoryV25_10_4, type ApiCallDirectory$2 as ApiCallDirectoryV25_10_5, type ApiCallDirectory$1 as ApiCallDirectoryV26_0_0, type ApiCallDirectory as ApiCallDirectoryV27_0_0, type ApiDirectoryByVersion, type ApiDirectoryShape, type ApiDirectory$7 as ApiDirectoryV25_10_0, type ApiDirectory$6 as ApiDirectoryV25_10_1, type ApiDirectory$5 as ApiDirectoryV25_10_2, type ApiDirectory$4 as ApiDirectoryV25_10_3, type ApiDirectory$3 as ApiDirectoryV25_10_4, type ApiDirectory$2 as ApiDirectoryV25_10_5, type ApiDirectory$1 as ApiDirectoryV26_0_0, type ApiDirectory as ApiDirectoryV27_0_0, type ApiError, type ApiEventDirectory$7 as ApiEventDirectoryV25_10_0, type ApiEventDirectory$6 as ApiEventDirectoryV25_10_1, type ApiEventDirectory$5 as ApiEventDirectoryV25_10_2, type ApiEventDirectory$4 as ApiEventDirectoryV25_10_3, type ApiEventDirectory$3 as ApiEventDirectoryV25_10_4, type ApiEventDirectory$2 as ApiEventDirectoryV25_10_5, type ApiEventDirectory$1 as ApiEventDirectoryV26_0_0, type ApiEventDirectory as ApiEventDirectoryV27_0_0, type ApiJobDirectory$7 as ApiJobDirectoryV25_10_0, type ApiJobDirectory$6 as ApiJobDirectoryV25_10_1, type ApiJobDirectory$5 as ApiJobDirectoryV25_10_2, type ApiJobDirectory$4 as ApiJobDirectoryV25_10_3, type ApiJobDirectory$3 as ApiJobDirectoryV25_10_4, type ApiJobDirectory$2 as ApiJobDirectoryV25_10_5, type ApiJobDirectory$1 as ApiJobDirectoryV26_0_0, type ApiJobDirectory as ApiJobDirectoryV27_0_0, type ApiKeyCreate, type ApiVersion, type ApiVersionResponse, AppState, type ArgsOf, AuthError, AuthErrorCode, type AuthResponse, type BaseApiDirectory, type CallMethod, type CallParams, type CallResponse, type Container, type CreateClientOptions, type DefaultApiDirectory, type EventKind, type EventName, type EventUnion, InvalidVersionResponseError, type Job, type JobMethod, type JobParams, type JobProgress, type JobResult, JobState, type Logger, NoCompatibleVersionsError, type OperationMappings, type QueryDirectory, type QueryEntity, type QueryListOptions, type QueryMethod, type QuerySingleOptions, SUPPORTED_API_VERSIONS, type SupportedApiVersion, TrueNasApiClient, TrueNasApiClientV2510, TrueNasApiClientV26, TrueNasApiClientV27, TrueNasAuthMechanism, type TrueNasDate, VersionCompatibility, VersionDiscovery, VersionDiscoveryError, VersionDiscoveryNetworkError, VersionDiscoveryTimeoutError, VersionEndpointNotFoundError, VersionTooNewError, VersionTooOldError, consoleLogger, createTrueNasClient, getApiErrorMessage, isJobFinished, noopLogger, index$7 as v25_10_0, index$6 as v25_10_1, index$5 as v25_10_2, index$4 as v25_10_3, index$3 as v25_10_4, index$2 as v25_10_5, index$1 as v26_0_0, index as v27_0_0 };
|
|
28106
|
+
export { type ApiCallDirectory$7 as ApiCallDirectoryV25_10_0, type ApiCallDirectory$6 as ApiCallDirectoryV25_10_1, type ApiCallDirectory$5 as ApiCallDirectoryV25_10_2, type ApiCallDirectory$4 as ApiCallDirectoryV25_10_3, type ApiCallDirectory$3 as ApiCallDirectoryV25_10_4, type ApiCallDirectory$2 as ApiCallDirectoryV25_10_5, type ApiCallDirectory$1 as ApiCallDirectoryV26_0_0, type ApiCallDirectory as ApiCallDirectoryV27_0_0, type ApiDirectoryByVersion, type ApiDirectoryShape, type ApiDirectory$7 as ApiDirectoryV25_10_0, type ApiDirectory$6 as ApiDirectoryV25_10_1, type ApiDirectory$5 as ApiDirectoryV25_10_2, type ApiDirectory$4 as ApiDirectoryV25_10_3, type ApiDirectory$3 as ApiDirectoryV25_10_4, type ApiDirectory$2 as ApiDirectoryV25_10_5, type ApiDirectory$1 as ApiDirectoryV26_0_0, type ApiDirectory as ApiDirectoryV27_0_0, type ApiError, type ApiEventDirectory$7 as ApiEventDirectoryV25_10_0, type ApiEventDirectory$6 as ApiEventDirectoryV25_10_1, type ApiEventDirectory$5 as ApiEventDirectoryV25_10_2, type ApiEventDirectory$4 as ApiEventDirectoryV25_10_3, type ApiEventDirectory$3 as ApiEventDirectoryV25_10_4, type ApiEventDirectory$2 as ApiEventDirectoryV25_10_5, type ApiEventDirectory$1 as ApiEventDirectoryV26_0_0, type ApiEventDirectory as ApiEventDirectoryV27_0_0, type ApiJobDirectory$7 as ApiJobDirectoryV25_10_0, type ApiJobDirectory$6 as ApiJobDirectoryV25_10_1, type ApiJobDirectory$5 as ApiJobDirectoryV25_10_2, type ApiJobDirectory$4 as ApiJobDirectoryV25_10_3, type ApiJobDirectory$3 as ApiJobDirectoryV25_10_4, type ApiJobDirectory$2 as ApiJobDirectoryV25_10_5, type ApiJobDirectory$1 as ApiJobDirectoryV26_0_0, type ApiJobDirectory as ApiJobDirectoryV27_0_0, type ApiKeyCreate, type ApiVersion, type ApiVersionResponse, AppState, type ApplianceProtocol, type ArgsOf, AuthError, AuthErrorCode, type AuthResponse, type BaseApiDirectory, type CallMethod, type CallParams, type CallResponse, type Container, type CreateClientOptions, type DefaultApiDirectory, type EventKind, type EventName, type EventUnion, InvalidVersionResponseError, type Job, type JobMethod, type JobParams, type JobProgress, type JobResult, JobState, type Logger, NoCompatibleVersionsError, type OperationMappings, type QueryDirectory, type QueryEntity, type QueryListOptions, type QueryMethod, type QuerySingleOptions, SUPPORTED_API_VERSIONS, type SupportedApiVersion, TrueNasApiClient, TrueNasApiClientV2510, TrueNasApiClientV26, TrueNasApiClientV27, TrueNasAuthMechanism, type TrueNasDate, VersionCompatibility, VersionDiscovery, VersionDiscoveryError, VersionDiscoveryNetworkError, VersionDiscoveryTimeoutError, VersionEndpointNotFoundError, VersionTooNewError, VersionTooOldError, consoleLogger, createTrueNasClient, getApiErrorMessage, isJobFinished, noopLogger, index$7 as v25_10_0, index$6 as v25_10_1, index$5 as v25_10_2, index$4 as v25_10_3, index$3 as v25_10_4, index$2 as v25_10_5, index$1 as v26_0_0, index as v27_0_0 };
|
package/dist/index.d.ts
CHANGED
|
@@ -12460,6 +12460,16 @@ declare class TrueNasSocket {
|
|
|
12460
12460
|
complete(): void;
|
|
12461
12461
|
}
|
|
12462
12462
|
|
|
12463
|
+
/**
|
|
12464
|
+
* The scheme used to reach the appliance, in `location.protocol` form.
|
|
12465
|
+
*
|
|
12466
|
+
* Colon included, so a caller served *by the appliance* can pass
|
|
12467
|
+
* `location.protocol` unchanged. That equivalence only holds same-origin: this
|
|
12468
|
+
* value is spliced onto the hostname the client connects to, so a page served
|
|
12469
|
+
* from somewhere else must say what the appliance uses, not what it uses.
|
|
12470
|
+
*/
|
|
12471
|
+
type ApplianceProtocol = 'http:' | 'https:';
|
|
12472
|
+
|
|
12463
12473
|
interface ActiveConnection {
|
|
12464
12474
|
ws: TrueNasSocket;
|
|
12465
12475
|
hostname: string;
|
|
@@ -12482,6 +12492,7 @@ declare class TrueNasConnection {
|
|
|
12482
12492
|
readonly retryDelay: number;
|
|
12483
12493
|
readonly maxRetry: number;
|
|
12484
12494
|
readonly logger: Logger;
|
|
12495
|
+
readonly protocol: ApplianceProtocol;
|
|
12485
12496
|
opened: BehaviorSubject<boolean>;
|
|
12486
12497
|
closed: Subject<void>;
|
|
12487
12498
|
hostname: BehaviorSubject<string>;
|
|
@@ -12544,7 +12555,7 @@ declare class TrueNasConnection {
|
|
|
12544
12555
|
* observable which always emits messages from the current socket.
|
|
12545
12556
|
*/
|
|
12546
12557
|
messages$: Observable<TrueNasMessage>;
|
|
12547
|
-
constructor(initialEnabled: boolean, hostnames: string[], systemUuid: string, websocketPath: string, systemName?: string | undefined, retryDelay?: number, maxRetry?: number, logger?: Logger);
|
|
12558
|
+
constructor(initialEnabled: boolean, hostnames: string[], systemUuid: string, websocketPath: string, systemName?: string | undefined, retryDelay?: number, maxRetry?: number, logger?: Logger, protocol?: ApplianceProtocol);
|
|
12548
12559
|
/**
|
|
12549
12560
|
* whether the connection has exhausted its retries — the **cumulative** snapshot, read
|
|
12550
12561
|
* synchronously. (Formerly `hasConnectionError()`; renamed to disambiguate it from the
|
|
@@ -13192,7 +13203,8 @@ declare abstract class TrueNasApiClient<D extends ApiDirectoryShape = BaseApiDir
|
|
|
13192
13203
|
protected readonly systemName: string | undefined;
|
|
13193
13204
|
/** Logger forwarded to the connection (defaults to a no-op). */
|
|
13194
13205
|
protected readonly logger: Logger;
|
|
13195
|
-
|
|
13206
|
+
protected readonly protocol: ApplianceProtocol;
|
|
13207
|
+
constructor(uuid: string, hostnames: string[], version: ApiVersion, enabled: boolean, systemName?: string, logger?: Logger, protocol?: ApplianceProtocol);
|
|
13196
13208
|
/**
|
|
13197
13209
|
* Get current connection status.
|
|
13198
13210
|
* @returns true if WebSocket is connected
|
|
@@ -27559,6 +27571,19 @@ declare const SUPPORTED_API_VERSIONS: readonly ["v25.10.0", "v25.10.1", "v25.10.
|
|
|
27559
27571
|
* how the missing 22 methods went unnoticed in the first place.
|
|
27560
27572
|
*/
|
|
27561
27573
|
type DefaultApiDirectory = ApiDirectory$7;
|
|
27574
|
+
/**
|
|
27575
|
+
* The surface a named version derives, falling back when nothing was narrowed.
|
|
27576
|
+
*
|
|
27577
|
+
* `V` only pins a directory when inference narrowed it. A wrapper typed
|
|
27578
|
+
* `(version: SupportedApiVersion)` widens it back to the whole union, and
|
|
27579
|
+
* indexing by a union yields a union of directories whose usable methods are
|
|
27580
|
+
* their *intersection* — narrower than the default surface, so naming the
|
|
27581
|
+
* version would buy fewer methods than naming nothing. That case falls back to
|
|
27582
|
+
* {@link DefaultApiDirectory} instead, matching every other shape that loses
|
|
27583
|
+
* the literal. A partial union still derives: methods common to the versions
|
|
27584
|
+
* named is the right answer for "one of these".
|
|
27585
|
+
*/
|
|
27586
|
+
type DerivedDirectory<V extends SupportedApiVersion> = SupportedApiVersion extends V ? DefaultApiDirectory : ApiDirectoryByVersion[V];
|
|
27562
27587
|
/** Options for {@link createTrueNasClient}. */
|
|
27563
27588
|
interface CreateClientOptions {
|
|
27564
27589
|
/** System UUID. */
|
|
@@ -27582,12 +27607,44 @@ interface CreateClientOptions {
|
|
|
27582
27607
|
* through the client, to the connection.
|
|
27583
27608
|
*/
|
|
27584
27609
|
logger?: Logger;
|
|
27610
|
+
/**
|
|
27611
|
+
* The appliance's API version, when the caller already knows it.
|
|
27612
|
+
*
|
|
27613
|
+
* Supplying it skips version discovery entirely — no `GET /api/versions`, no
|
|
27614
|
+
* CORS fallback — and *derives* the client's typed surface from the string,
|
|
27615
|
+
* so `version: 'v27.0.0'` yields `TrueNasApiClient<ApiDirectoryV27_0_0>`
|
|
27616
|
+
* without the caller asserting it through a type parameter.
|
|
27617
|
+
*/
|
|
27618
|
+
version?: SupportedApiVersion;
|
|
27619
|
+
/**
|
|
27620
|
+
* The scheme to reach the appliance on, in `location.protocol` form.
|
|
27621
|
+
*
|
|
27622
|
+
* Selects both halves of the transport: `https:` gives `https` discovery and
|
|
27623
|
+
* a `wss` socket, `http:` gives `http` and `ws`. Defaults to `https:`, which
|
|
27624
|
+
* is what an appliance serves and what Connect uses.
|
|
27625
|
+
*
|
|
27626
|
+
* This describes the *appliance*, not the page. Passing
|
|
27627
|
+
* `location.protocol` is correct when the appliance serves the page — the
|
|
27628
|
+
* same-origin case this exists for — and wrong otherwise. A page on
|
|
27629
|
+
* `http://localhost:5173` talking to an https appliance gets both halves
|
|
27630
|
+
* wrong, but only one of them says so: `fetch` follows the redirect and
|
|
27631
|
+
* discovery appears to work, while the WebSocket has no such tolerance and
|
|
27632
|
+
* fails the handshake without naming the scheme.
|
|
27633
|
+
*
|
|
27634
|
+
* Omitting it against a plaintext appliance is the quieter failure and the
|
|
27635
|
+
* likelier one, since it is the case this option exists for. Discovery tries
|
|
27636
|
+
* `https`, `fetch` rejects, and that is indistinguishable from the CORS block
|
|
27637
|
+
* v25.10.0 has on `/api/versions` — so the fallback fires and the caller gets
|
|
27638
|
+
* a client pinned to v25.10.0, warned about only in the log.
|
|
27639
|
+
*/
|
|
27640
|
+
protocol?: ApplianceProtocol;
|
|
27585
27641
|
}
|
|
27586
27642
|
/**
|
|
27587
27643
|
* Creates a version-specific TrueNAS API client.
|
|
27588
27644
|
*
|
|
27589
27645
|
* 1. Discovers the API version (`GET /api/versions`), asking every hostname in
|
|
27590
|
-
* parallel. The first usable answer wins.
|
|
27646
|
+
* parallel. The first usable answer wins — *unless* `opts.version` says
|
|
27647
|
+
* which version this is, in which case discovery is skipped entirely.
|
|
27591
27648
|
* 2. Selects the matching client implementation (`v25.10.x` -> `TrueNasApiClientV2510`,
|
|
27592
27649
|
* `v26.x.y` -> `TrueNasApiClientV26`, `v27.x.y` -> `TrueNasApiClientV27`).
|
|
27593
27650
|
* 3. Instantiates and returns it.
|
|
@@ -27618,9 +27675,77 @@ interface CreateClientOptions {
|
|
|
27618
27675
|
* supported version. Operations that must work across versions belong on
|
|
27619
27676
|
* `client.ops`, which resolves them at runtime.
|
|
27620
27677
|
*
|
|
27678
|
+
* ## Naming the version instead
|
|
27679
|
+
*
|
|
27680
|
+
* A caller that already knows its appliance — a UI served by the appliance
|
|
27681
|
+
* itself, a test harness against a pinned image — can say so and skip discovery
|
|
27682
|
+
* altogether:
|
|
27683
|
+
*
|
|
27684
|
+
* ```typescript
|
|
27685
|
+
* const client = await createTrueNasClient({
|
|
27686
|
+
* uuid, hostnames, enabled: true, version: 'v27.0.0',
|
|
27687
|
+
* });
|
|
27688
|
+
* // client: TrueNasApiClient<ApiDirectoryV27_0_0>, derived from the string
|
|
27689
|
+
* ```
|
|
27690
|
+
*
|
|
27691
|
+
* The surface is *derived* rather than asserted: `ApiDirectoryByVersion` maps
|
|
27692
|
+
* the version string to its directory, so the caller writes no cast and names
|
|
27693
|
+
* no directory type. A version this package ships no types for does not
|
|
27694
|
+
* compile.
|
|
27695
|
+
*
|
|
27696
|
+
* **The derivation needs the version to be literal at the call site.** It comes
|
|
27697
|
+
* from inference on `{ version: V }`, so it holds for a string literal written
|
|
27698
|
+
* in the options object (or a `const`-typed one). It does not survive
|
|
27699
|
+
* indirection:
|
|
27700
|
+
*
|
|
27701
|
+
* - `createTrueNasClient<D>({ …, version: 'v27.0.0' })` — an explicit type
|
|
27702
|
+
* argument makes the derived overload inapplicable, so `D` wins.
|
|
27703
|
+
* - `(v?: SupportedApiVersion) => createTrueNasClient({ …, version: v })` — the
|
|
27704
|
+
* property is `SupportedApiVersion | undefined`, which no `V` satisfies.
|
|
27705
|
+
* - `(v: SupportedApiVersion) => …` — reaches this overload, but `V` widens to
|
|
27706
|
+
* the whole union, which derives nothing.
|
|
27707
|
+
* - `const opts: CreateClientOptions = { …, version: 'v27.0.0' }` — the
|
|
27708
|
+
* annotation widens the property before the call sees it.
|
|
27709
|
+
*
|
|
27710
|
+
* All of them compile, run against the named version, and type as
|
|
27711
|
+
* {@link DefaultApiDirectory}. That fails in the safe direction — understated
|
|
27712
|
+
* types give a compile error at the method call rather than a runtime surprise —
|
|
27713
|
+
* but it is silent, so a wrapper that forwards a version gets none of the
|
|
27714
|
+
* surface it named. Keep the literal at the call site, and if you are adding
|
|
27715
|
+
* `version` to an existing `createTrueNasClient<ApiDirectoryV26_0_0>(opts)`
|
|
27716
|
+
* call, delete the type argument in the same edit.
|
|
27717
|
+
*
|
|
27718
|
+
* Two consequences worth knowing before reaching for it.
|
|
27719
|
+
*
|
|
27720
|
+
* It is a stronger claim than `D` alone, because it also picks the websocket
|
|
27721
|
+
* path. Naming `v27.0.0` at a v26 appliance connects on `/api/v27.0.0` with v27
|
|
27722
|
+
* types over a v26 server, and discovery cannot correct it — declining
|
|
27723
|
+
* discovery is the whole point. `D` on its own only mistyped the surface; this
|
|
27724
|
+
* mistypes the surface *and* dials the wrong number.
|
|
27725
|
+
*
|
|
27726
|
+
* Compatibility is still checked. Skipping discovery skips the network round
|
|
27727
|
+
* trip, not the range check, which is local and free. Two refusals reach a
|
|
27728
|
+
* caller, and they are not interchangeable:
|
|
27729
|
+
*
|
|
27730
|
+
* - a string that is not a `SupportedApiVersion` at all — only reachable from
|
|
27731
|
+
* JavaScript — throws a plain `Error` naming the versions that are.
|
|
27732
|
+
* - a version this package ships types for but cannot build a client for
|
|
27733
|
+
* throws {@link VersionTooNewError}, the same error discovery raises.
|
|
27734
|
+
*
|
|
27735
|
+
* The second is not hypothetical. `MAX_SUPPORTED_VERSION` is a hand-written
|
|
27736
|
+
* literal, and while it matches the newest generated version today, a
|
|
27737
|
+
* regeneration can add a year before anyone writes its client — at which point
|
|
27738
|
+
* that version is nameable, promised by the overload, and has nothing to build.
|
|
27739
|
+
* `VersionTooOldError` has no counterpart here: `MIN_SUPPORTED_VERSION` is
|
|
27740
|
+
* derived from the same list that constrains the type, so nothing nameable is
|
|
27741
|
+
* below it.
|
|
27742
|
+
*
|
|
27743
|
+
* @typeParam V - the version named in `opts.version`, when one is. The returned
|
|
27744
|
+
* surface is `ApiDirectoryByVersion[V]`, so it is derived rather than chosen.
|
|
27621
27745
|
* @typeParam D - the generated API surface the client is typed against, as a
|
|
27622
|
-
* whole (`call`, `job`, `event`)
|
|
27623
|
-
*
|
|
27746
|
+
* whole (`call`, `job`, `event`), for the discovery path where no version is
|
|
27747
|
+
* named. Every verb resolves method names against it, so naming a method this
|
|
27748
|
+
* surface does not have is a build error.
|
|
27624
27749
|
* @returns a Promise that resolves with the created client, or rejects with a
|
|
27625
27750
|
* {@link VersionDiscoveryError} subclass (or a client-selection error).
|
|
27626
27751
|
* Rejects if version discovery on all hostnames *fails* and is not recoverable.
|
|
@@ -27630,6 +27755,9 @@ interface CreateClientOptions {
|
|
|
27630
27755
|
* bug. A network error alongside a version-compatibility error or a 404 does
|
|
27631
27756
|
* *not* reach the fallback — see `selectRepresentativeFailure`.
|
|
27632
27757
|
*/
|
|
27758
|
+
declare function createTrueNasClient<V extends SupportedApiVersion>(opts: CreateClientOptions & {
|
|
27759
|
+
version: V;
|
|
27760
|
+
}): Promise<TrueNasApiClient<DerivedDirectory<V>>>;
|
|
27633
27761
|
declare function createTrueNasClient<D extends ApiDirectoryShape = DefaultApiDirectory>(opts: CreateClientOptions): Promise<TrueNasApiClient<D>>;
|
|
27634
27762
|
|
|
27635
27763
|
/**
|
|
@@ -27789,12 +27917,14 @@ declare class TrueNasApiClientV27 extends TrueNasApiClient<ApiDirectory> {
|
|
|
27789
27917
|
*/
|
|
27790
27918
|
declare class VersionDiscovery {
|
|
27791
27919
|
private readonly logger;
|
|
27920
|
+
private readonly protocol;
|
|
27792
27921
|
private versionCache;
|
|
27793
|
-
constructor(logger?: Logger);
|
|
27922
|
+
constructor(logger?: Logger, protocol?: ApplianceProtocol);
|
|
27923
|
+
private versionsUrl;
|
|
27794
27924
|
/**
|
|
27795
27925
|
* Discovers the API version for a given hostname.
|
|
27796
27926
|
*
|
|
27797
|
-
* Makes a GET request to `
|
|
27927
|
+
* Makes a GET request to `{protocol}//{hostname}/api/versions` and returns the latest
|
|
27798
27928
|
* compatible version. Results are cached per hostname; the cache entry is removed
|
|
27799
27929
|
* on failure so the next call retries.
|
|
27800
27930
|
*
|
|
@@ -27973,4 +28103,4 @@ type ApiError = JsonRpcError | TrueNasError;
|
|
|
27973
28103
|
*/
|
|
27974
28104
|
declare function getApiErrorMessage(error: unknown, fallback?: string): string;
|
|
27975
28105
|
|
|
27976
|
-
export { type ApiCallDirectory$7 as ApiCallDirectoryV25_10_0, type ApiCallDirectory$6 as ApiCallDirectoryV25_10_1, type ApiCallDirectory$5 as ApiCallDirectoryV25_10_2, type ApiCallDirectory$4 as ApiCallDirectoryV25_10_3, type ApiCallDirectory$3 as ApiCallDirectoryV25_10_4, type ApiCallDirectory$2 as ApiCallDirectoryV25_10_5, type ApiCallDirectory$1 as ApiCallDirectoryV26_0_0, type ApiCallDirectory as ApiCallDirectoryV27_0_0, type ApiDirectoryByVersion, type ApiDirectoryShape, type ApiDirectory$7 as ApiDirectoryV25_10_0, type ApiDirectory$6 as ApiDirectoryV25_10_1, type ApiDirectory$5 as ApiDirectoryV25_10_2, type ApiDirectory$4 as ApiDirectoryV25_10_3, type ApiDirectory$3 as ApiDirectoryV25_10_4, type ApiDirectory$2 as ApiDirectoryV25_10_5, type ApiDirectory$1 as ApiDirectoryV26_0_0, type ApiDirectory as ApiDirectoryV27_0_0, type ApiError, type ApiEventDirectory$7 as ApiEventDirectoryV25_10_0, type ApiEventDirectory$6 as ApiEventDirectoryV25_10_1, type ApiEventDirectory$5 as ApiEventDirectoryV25_10_2, type ApiEventDirectory$4 as ApiEventDirectoryV25_10_3, type ApiEventDirectory$3 as ApiEventDirectoryV25_10_4, type ApiEventDirectory$2 as ApiEventDirectoryV25_10_5, type ApiEventDirectory$1 as ApiEventDirectoryV26_0_0, type ApiEventDirectory as ApiEventDirectoryV27_0_0, type ApiJobDirectory$7 as ApiJobDirectoryV25_10_0, type ApiJobDirectory$6 as ApiJobDirectoryV25_10_1, type ApiJobDirectory$5 as ApiJobDirectoryV25_10_2, type ApiJobDirectory$4 as ApiJobDirectoryV25_10_3, type ApiJobDirectory$3 as ApiJobDirectoryV25_10_4, type ApiJobDirectory$2 as ApiJobDirectoryV25_10_5, type ApiJobDirectory$1 as ApiJobDirectoryV26_0_0, type ApiJobDirectory as ApiJobDirectoryV27_0_0, type ApiKeyCreate, type ApiVersion, type ApiVersionResponse, AppState, type ArgsOf, AuthError, AuthErrorCode, type AuthResponse, type BaseApiDirectory, type CallMethod, type CallParams, type CallResponse, type Container, type CreateClientOptions, type DefaultApiDirectory, type EventKind, type EventName, type EventUnion, InvalidVersionResponseError, type Job, type JobMethod, type JobParams, type JobProgress, type JobResult, JobState, type Logger, NoCompatibleVersionsError, type OperationMappings, type QueryDirectory, type QueryEntity, type QueryListOptions, type QueryMethod, type QuerySingleOptions, SUPPORTED_API_VERSIONS, type SupportedApiVersion, TrueNasApiClient, TrueNasApiClientV2510, TrueNasApiClientV26, TrueNasApiClientV27, TrueNasAuthMechanism, type TrueNasDate, VersionCompatibility, VersionDiscovery, VersionDiscoveryError, VersionDiscoveryNetworkError, VersionDiscoveryTimeoutError, VersionEndpointNotFoundError, VersionTooNewError, VersionTooOldError, consoleLogger, createTrueNasClient, getApiErrorMessage, isJobFinished, noopLogger, index$7 as v25_10_0, index$6 as v25_10_1, index$5 as v25_10_2, index$4 as v25_10_3, index$3 as v25_10_4, index$2 as v25_10_5, index$1 as v26_0_0, index as v27_0_0 };
|
|
28106
|
+
export { type ApiCallDirectory$7 as ApiCallDirectoryV25_10_0, type ApiCallDirectory$6 as ApiCallDirectoryV25_10_1, type ApiCallDirectory$5 as ApiCallDirectoryV25_10_2, type ApiCallDirectory$4 as ApiCallDirectoryV25_10_3, type ApiCallDirectory$3 as ApiCallDirectoryV25_10_4, type ApiCallDirectory$2 as ApiCallDirectoryV25_10_5, type ApiCallDirectory$1 as ApiCallDirectoryV26_0_0, type ApiCallDirectory as ApiCallDirectoryV27_0_0, type ApiDirectoryByVersion, type ApiDirectoryShape, type ApiDirectory$7 as ApiDirectoryV25_10_0, type ApiDirectory$6 as ApiDirectoryV25_10_1, type ApiDirectory$5 as ApiDirectoryV25_10_2, type ApiDirectory$4 as ApiDirectoryV25_10_3, type ApiDirectory$3 as ApiDirectoryV25_10_4, type ApiDirectory$2 as ApiDirectoryV25_10_5, type ApiDirectory$1 as ApiDirectoryV26_0_0, type ApiDirectory as ApiDirectoryV27_0_0, type ApiError, type ApiEventDirectory$7 as ApiEventDirectoryV25_10_0, type ApiEventDirectory$6 as ApiEventDirectoryV25_10_1, type ApiEventDirectory$5 as ApiEventDirectoryV25_10_2, type ApiEventDirectory$4 as ApiEventDirectoryV25_10_3, type ApiEventDirectory$3 as ApiEventDirectoryV25_10_4, type ApiEventDirectory$2 as ApiEventDirectoryV25_10_5, type ApiEventDirectory$1 as ApiEventDirectoryV26_0_0, type ApiEventDirectory as ApiEventDirectoryV27_0_0, type ApiJobDirectory$7 as ApiJobDirectoryV25_10_0, type ApiJobDirectory$6 as ApiJobDirectoryV25_10_1, type ApiJobDirectory$5 as ApiJobDirectoryV25_10_2, type ApiJobDirectory$4 as ApiJobDirectoryV25_10_3, type ApiJobDirectory$3 as ApiJobDirectoryV25_10_4, type ApiJobDirectory$2 as ApiJobDirectoryV25_10_5, type ApiJobDirectory$1 as ApiJobDirectoryV26_0_0, type ApiJobDirectory as ApiJobDirectoryV27_0_0, type ApiKeyCreate, type ApiVersion, type ApiVersionResponse, AppState, type ApplianceProtocol, type ArgsOf, AuthError, AuthErrorCode, type AuthResponse, type BaseApiDirectory, type CallMethod, type CallParams, type CallResponse, type Container, type CreateClientOptions, type DefaultApiDirectory, type EventKind, type EventName, type EventUnion, InvalidVersionResponseError, type Job, type JobMethod, type JobParams, type JobProgress, type JobResult, JobState, type Logger, NoCompatibleVersionsError, type OperationMappings, type QueryDirectory, type QueryEntity, type QueryListOptions, type QueryMethod, type QuerySingleOptions, SUPPORTED_API_VERSIONS, type SupportedApiVersion, TrueNasApiClient, TrueNasApiClientV2510, TrueNasApiClientV26, TrueNasApiClientV27, TrueNasAuthMechanism, type TrueNasDate, VersionCompatibility, VersionDiscovery, VersionDiscoveryError, VersionDiscoveryNetworkError, VersionDiscoveryTimeoutError, VersionEndpointNotFoundError, VersionTooNewError, VersionTooOldError, consoleLogger, createTrueNasClient, getApiErrorMessage, isJobFinished, noopLogger, index$7 as v25_10_0, index$6 as v25_10_1, index$5 as v25_10_2, index$4 as v25_10_3, index$3 as v25_10_4, index$2 as v25_10_5, index$1 as v26_0_0, index as v27_0_0 };
|
package/dist/index.js
CHANGED
|
@@ -692,11 +692,19 @@ var TrueNasSocket = class {
|
|
|
692
692
|
}
|
|
693
693
|
};
|
|
694
694
|
|
|
695
|
+
// src/types/transport.type.ts
|
|
696
|
+
function httpScheme(protocol) {
|
|
697
|
+
return protocol === "http:" ? "http:" : "https:";
|
|
698
|
+
}
|
|
699
|
+
function socketScheme(protocol) {
|
|
700
|
+
return protocol === "http:" ? "ws:" : "wss:";
|
|
701
|
+
}
|
|
702
|
+
|
|
695
703
|
// src/connection/truenas-connection.ts
|
|
696
704
|
var tenSeconds = 10 * 1e3;
|
|
697
705
|
var twentySeconds = 20 * 1e3;
|
|
698
706
|
var TrueNasConnection = class {
|
|
699
|
-
constructor(initialEnabled, hostnames, systemUuid, websocketPath, systemName, retryDelay = tenSeconds, maxRetry = 3, logger = noopLogger) {
|
|
707
|
+
constructor(initialEnabled, hostnames, systemUuid, websocketPath, systemName, retryDelay = tenSeconds, maxRetry = 3, logger = noopLogger, protocol = "https:") {
|
|
700
708
|
this.hostnames = hostnames;
|
|
701
709
|
this.systemUuid = systemUuid;
|
|
702
710
|
this.websocketPath = websocketPath;
|
|
@@ -704,6 +712,7 @@ var TrueNasConnection = class {
|
|
|
704
712
|
this.retryDelay = retryDelay;
|
|
705
713
|
this.maxRetry = maxRetry;
|
|
706
714
|
this.logger = logger;
|
|
715
|
+
this.protocol = protocol;
|
|
707
716
|
// compatibility properties
|
|
708
717
|
this.opened = new BehaviorSubject(false);
|
|
709
718
|
this.closed = new Subject();
|
|
@@ -900,7 +909,7 @@ var TrueNasConnection = class {
|
|
|
900
909
|
* error if the connection is never established and will not complete until unsubscribed from or closed.
|
|
901
910
|
*/
|
|
902
911
|
createSocket(hostname) {
|
|
903
|
-
const url =
|
|
912
|
+
const url = `${socketScheme(this.protocol)}//${hostname}${this.websocketPath}`;
|
|
904
913
|
let hasOpened = false;
|
|
905
914
|
return new Observable((subscriber) => {
|
|
906
915
|
const ws = new TrueNasSocket({
|
|
@@ -2999,13 +3008,14 @@ function getWebSocketPath(version) {
|
|
|
2999
3008
|
|
|
3000
3009
|
// src/client/truenas-api-client.ts
|
|
3001
3010
|
var TrueNasApiClient = class {
|
|
3002
|
-
constructor(uuid, hostnames, version, enabled, systemName, logger = noopLogger) {
|
|
3011
|
+
constructor(uuid, hostnames, version, enabled, systemName, logger = noopLogger, protocol = "https:") {
|
|
3003
3012
|
this.uuid = uuid;
|
|
3004
3013
|
this.hostnames = hostnames;
|
|
3005
3014
|
this.version = version;
|
|
3006
3015
|
this.enabled = enabled;
|
|
3007
3016
|
this.systemName = systemName;
|
|
3008
3017
|
this.logger = logger;
|
|
3018
|
+
this.protocol = protocol;
|
|
3009
3019
|
this.connection = this.createConnection();
|
|
3010
3020
|
this.authenticator = this.createAuthenticator();
|
|
3011
3021
|
this.api = this.createApi();
|
|
@@ -3048,7 +3058,8 @@ var TrueNasApiClient = class {
|
|
|
3048
3058
|
// retryDelay (use default)
|
|
3049
3059
|
void 0,
|
|
3050
3060
|
// maxRetry (use default)
|
|
3051
|
-
this.logger
|
|
3061
|
+
this.logger,
|
|
3062
|
+
this.protocol
|
|
3052
3063
|
);
|
|
3053
3064
|
}
|
|
3054
3065
|
/**
|
|
@@ -3442,14 +3453,18 @@ function hasErrorName(error, expected) {
|
|
|
3442
3453
|
return typeof error === "object" && error !== null && "name" in error && error.name === expected;
|
|
3443
3454
|
}
|
|
3444
3455
|
var VersionDiscovery = class {
|
|
3445
|
-
constructor(logger = noopLogger) {
|
|
3456
|
+
constructor(logger = noopLogger, protocol = "https:") {
|
|
3446
3457
|
this.logger = logger;
|
|
3458
|
+
this.protocol = protocol;
|
|
3447
3459
|
this.versionCache = /* @__PURE__ */ new Map();
|
|
3448
3460
|
}
|
|
3461
|
+
versionsUrl(hostname) {
|
|
3462
|
+
return `${httpScheme(this.protocol)}//${hostname}/api/versions`;
|
|
3463
|
+
}
|
|
3449
3464
|
/**
|
|
3450
3465
|
* Discovers the API version for a given hostname.
|
|
3451
3466
|
*
|
|
3452
|
-
* Makes a GET request to `
|
|
3467
|
+
* Makes a GET request to `{protocol}//{hostname}/api/versions` and returns the latest
|
|
3453
3468
|
* compatible version. Results are cached per hostname; the cache entry is removed
|
|
3454
3469
|
* on failure so the next call retries.
|
|
3455
3470
|
*
|
|
@@ -3463,8 +3478,10 @@ var VersionDiscovery = class {
|
|
|
3463
3478
|
this.logger.info("Version discovery cache hit", { hostname });
|
|
3464
3479
|
return cached;
|
|
3465
3480
|
}
|
|
3466
|
-
|
|
3467
|
-
|
|
3481
|
+
this.logger.info("Starting version discovery", {
|
|
3482
|
+
hostname,
|
|
3483
|
+
url: this.versionsUrl(hostname)
|
|
3484
|
+
});
|
|
3468
3485
|
const discovery$ = defer(() => from(this.fetchVersions(hostname))).pipe(
|
|
3469
3486
|
map$1((versionStrings) => this.selectVersion(hostname, versionStrings)),
|
|
3470
3487
|
catchError((error) => {
|
|
@@ -3502,7 +3519,7 @@ var VersionDiscovery = class {
|
|
|
3502
3519
|
* misfile as a network error. Validating here keeps it an `InvalidVersionResponseError`.
|
|
3503
3520
|
*/
|
|
3504
3521
|
async fetchVersions(hostname) {
|
|
3505
|
-
const url =
|
|
3522
|
+
const url = this.versionsUrl(hostname);
|
|
3506
3523
|
const controller = new AbortController();
|
|
3507
3524
|
const timer2 = setTimeout(() => controller.abort(), discoveryTimeoutMs);
|
|
3508
3525
|
try {
|
|
@@ -3642,12 +3659,40 @@ async function createTrueNasClient(opts) {
|
|
|
3642
3659
|
`Cannot create client for system ${uuid}: hostnames array is empty`
|
|
3643
3660
|
);
|
|
3644
3661
|
}
|
|
3645
|
-
const versionDiscovery = new VersionDiscovery(logger);
|
|
3646
3662
|
logger.info("Creating versioned API client", {
|
|
3647
3663
|
uuid: uuid.slice(0, 8),
|
|
3648
3664
|
hostnames: hostnames.join(", "),
|
|
3649
3665
|
systemName
|
|
3650
3666
|
});
|
|
3667
|
+
if (opts.version !== void 0) {
|
|
3668
|
+
if (!SUPPORTED_API_VERSIONS.includes(opts.version)) {
|
|
3669
|
+
throw new Error(
|
|
3670
|
+
`Cannot create client for system ${uuid}: '${opts.version}' is not a version this package ships types for. Supported: ${SUPPORTED_API_VERSIONS.join(", ")}.`
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3673
|
+
const known = parseApiVersion(opts.version);
|
|
3674
|
+
if (!known) {
|
|
3675
|
+
throw new Error(
|
|
3676
|
+
`Cannot create client for system ${uuid}: supported version '${opts.version}' failed to parse.`
|
|
3677
|
+
);
|
|
3678
|
+
}
|
|
3679
|
+
const compatibility = checkVersionCompatibility(known);
|
|
3680
|
+
if (compatibility === "too-new" /* TooNew */) {
|
|
3681
|
+
throw new VersionTooNewError(hostnames[0], [known.version]);
|
|
3682
|
+
}
|
|
3683
|
+
if (compatibility !== "compatible" /* Compatible */) {
|
|
3684
|
+
throw new Error(
|
|
3685
|
+
`Cannot create client for system ${uuid}: the supported version range is not usable (${apiVersionConfig.MIN_SUPPORTED_VERSION}..${apiVersionConfig.MAX_SUPPORTED_VERSION}).`
|
|
3686
|
+
);
|
|
3687
|
+
}
|
|
3688
|
+
logger.info("API version supplied by the caller, skipping discovery", {
|
|
3689
|
+
uuid: uuid.slice(0, 8),
|
|
3690
|
+
version: known.version,
|
|
3691
|
+
websocketPath: known.websocketPath
|
|
3692
|
+
});
|
|
3693
|
+
return instantiateClientForVersion(known, opts, logger);
|
|
3694
|
+
}
|
|
3695
|
+
const versionDiscovery = new VersionDiscovery(logger, opts.protocol);
|
|
3651
3696
|
let version;
|
|
3652
3697
|
try {
|
|
3653
3698
|
const winner = await discoverVersionFromAnyHostname(
|
|
@@ -3756,7 +3801,8 @@ function instantiateClientForVersion(version, opts, logger) {
|
|
|
3756
3801
|
version,
|
|
3757
3802
|
enabled,
|
|
3758
3803
|
systemName,
|
|
3759
|
-
logger
|
|
3804
|
+
logger,
|
|
3805
|
+
opts.protocol
|
|
3760
3806
|
);
|
|
3761
3807
|
}
|
|
3762
3808
|
function errorMessageOrDefault(error, fallback) {
|