@truenas/api-client 2.0.0 → 2.0.1
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/dist/index.cjs +36 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -3
- package/dist/index.d.ts +13 -3
- package/dist/index.js +36 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -27443,7 +27443,11 @@ type DefaultApiDirectory = ApiDirectory$7;
|
|
|
27443
27443
|
interface CreateClientOptions {
|
|
27444
27444
|
/** System UUID. */
|
|
27445
27445
|
uuid: string;
|
|
27446
|
-
/**
|
|
27446
|
+
/**
|
|
27447
|
+
* Hostnames to connect to. Order carries no precedence for version discovery
|
|
27448
|
+
* (every hostname is asked at once); it only breaks ties when deciding which
|
|
27449
|
+
* failure to report if none of them answer.
|
|
27450
|
+
*/
|
|
27447
27451
|
hostnames: string[];
|
|
27448
27452
|
/**
|
|
27449
27453
|
* Initial connection gate. The client only opens a socket while this is `true`;
|
|
@@ -27462,7 +27466,8 @@ interface CreateClientOptions {
|
|
|
27462
27466
|
/**
|
|
27463
27467
|
* Creates a version-specific TrueNAS API client.
|
|
27464
27468
|
*
|
|
27465
|
-
* 1. Discovers the API version
|
|
27469
|
+
* 1. Discovers the API version (`GET /api/versions`), asking every hostname in
|
|
27470
|
+
* parallel. The first usable answer wins.
|
|
27466
27471
|
* 2. Selects the matching client implementation (`v25.10.x` -> `TrueNasApiClientV2510`,
|
|
27467
27472
|
* `v26.x.y` -> `TrueNasApiClientV26`).
|
|
27468
27473
|
* 3. Instantiates and returns it.
|
|
@@ -27498,7 +27503,12 @@ interface CreateClientOptions {
|
|
|
27498
27503
|
* it, so naming a method this surface does not have is a build error.
|
|
27499
27504
|
* @returns a Promise that resolves with the created client, or rejects with a
|
|
27500
27505
|
* {@link VersionDiscoveryError} subclass (or a client-selection error).
|
|
27501
|
-
* Rejects if
|
|
27506
|
+
* Rejects if version discovery on all hostnames *fails* and is not recoverable.
|
|
27507
|
+
* Note that when the selected failure is a `VersionDiscoveryNetworkError`,
|
|
27508
|
+
* this function attempts to use a fallback API version (see `FALLBACK_VERSION`)
|
|
27509
|
+
* because network errors are actually expected on 25.10.0 systems due to a CORS
|
|
27510
|
+
* bug. A network error alongside a version-compatibility error or a 404 does
|
|
27511
|
+
* *not* reach the fallback — see `selectRepresentativeFailure`.
|
|
27502
27512
|
*/
|
|
27503
27513
|
declare function createTrueNasClient<D extends ApiDirectoryShape = DefaultApiDirectory>(opts: CreateClientOptions): Promise<TrueNasApiClient<D>>;
|
|
27504
27514
|
|
package/dist/index.d.ts
CHANGED
|
@@ -27443,7 +27443,11 @@ type DefaultApiDirectory = ApiDirectory$7;
|
|
|
27443
27443
|
interface CreateClientOptions {
|
|
27444
27444
|
/** System UUID. */
|
|
27445
27445
|
uuid: string;
|
|
27446
|
-
/**
|
|
27446
|
+
/**
|
|
27447
|
+
* Hostnames to connect to. Order carries no precedence for version discovery
|
|
27448
|
+
* (every hostname is asked at once); it only breaks ties when deciding which
|
|
27449
|
+
* failure to report if none of them answer.
|
|
27450
|
+
*/
|
|
27447
27451
|
hostnames: string[];
|
|
27448
27452
|
/**
|
|
27449
27453
|
* Initial connection gate. The client only opens a socket while this is `true`;
|
|
@@ -27462,7 +27466,8 @@ interface CreateClientOptions {
|
|
|
27462
27466
|
/**
|
|
27463
27467
|
* Creates a version-specific TrueNAS API client.
|
|
27464
27468
|
*
|
|
27465
|
-
* 1. Discovers the API version
|
|
27469
|
+
* 1. Discovers the API version (`GET /api/versions`), asking every hostname in
|
|
27470
|
+
* parallel. The first usable answer wins.
|
|
27466
27471
|
* 2. Selects the matching client implementation (`v25.10.x` -> `TrueNasApiClientV2510`,
|
|
27467
27472
|
* `v26.x.y` -> `TrueNasApiClientV26`).
|
|
27468
27473
|
* 3. Instantiates and returns it.
|
|
@@ -27498,7 +27503,12 @@ interface CreateClientOptions {
|
|
|
27498
27503
|
* it, so naming a method this surface does not have is a build error.
|
|
27499
27504
|
* @returns a Promise that resolves with the created client, or rejects with a
|
|
27500
27505
|
* {@link VersionDiscoveryError} subclass (or a client-selection error).
|
|
27501
|
-
* Rejects if
|
|
27506
|
+
* Rejects if version discovery on all hostnames *fails* and is not recoverable.
|
|
27507
|
+
* Note that when the selected failure is a `VersionDiscoveryNetworkError`,
|
|
27508
|
+
* this function attempts to use a fallback API version (see `FALLBACK_VERSION`)
|
|
27509
|
+
* because network errors are actually expected on 25.10.0 systems due to a CORS
|
|
27510
|
+
* bug. A network error alongside a version-compatibility error or a 404 does
|
|
27511
|
+
* *not* reach the fallback — see `selectRepresentativeFailure`.
|
|
27502
27512
|
*/
|
|
27503
27513
|
declare function createTrueNasClient<D extends ApiDirectoryShape = DefaultApiDirectory>(opts: CreateClientOptions): Promise<TrueNasApiClient<D>>;
|
|
27504
27514
|
|
package/dist/index.js
CHANGED
|
@@ -3304,7 +3304,7 @@ var VersionDiscovery = class {
|
|
|
3304
3304
|
map$1((versionStrings) => this.selectVersion(hostname, versionStrings)),
|
|
3305
3305
|
catchError((error) => {
|
|
3306
3306
|
this.versionCache.delete(hostname);
|
|
3307
|
-
this.logger.
|
|
3307
|
+
this.logger.warn("Version discovery failed", { hostname, error });
|
|
3308
3308
|
return throwError(() => this.classify(error, hostname));
|
|
3309
3309
|
}),
|
|
3310
3310
|
shareReplay(1)
|
|
@@ -3375,7 +3375,7 @@ var VersionDiscovery = class {
|
|
|
3375
3375
|
});
|
|
3376
3376
|
const parsedVersions = versionStrings.map(parseApiVersion).filter((v) => v !== null);
|
|
3377
3377
|
if (parsedVersions.length === 0) {
|
|
3378
|
-
this.logger.
|
|
3378
|
+
this.logger.warn("No valid versions in response", {
|
|
3379
3379
|
hostname,
|
|
3380
3380
|
versionStrings
|
|
3381
3381
|
});
|
|
@@ -3477,29 +3477,34 @@ async function createTrueNasClient(opts) {
|
|
|
3477
3477
|
`Cannot create client for system ${uuid}: hostnames array is empty`
|
|
3478
3478
|
);
|
|
3479
3479
|
}
|
|
3480
|
-
const primaryHostname = hostnames[0];
|
|
3481
3480
|
const versionDiscovery = new VersionDiscovery(logger);
|
|
3482
3481
|
logger.info("Creating versioned API client", {
|
|
3483
3482
|
uuid: uuid.slice(0, 8),
|
|
3484
|
-
|
|
3483
|
+
hostnames: hostnames.join(", "),
|
|
3485
3484
|
systemName
|
|
3486
3485
|
});
|
|
3487
3486
|
let version;
|
|
3488
3487
|
try {
|
|
3489
|
-
|
|
3490
|
-
|
|
3488
|
+
const winner = await discoverVersionFromAnyHostname(
|
|
3489
|
+
hostnames,
|
|
3490
|
+
versionDiscovery
|
|
3491
3491
|
);
|
|
3492
|
+
version = winner.version;
|
|
3492
3493
|
logger.info("API version discovered, instantiating client", {
|
|
3493
3494
|
uuid: uuid.slice(0, 8),
|
|
3495
|
+
// Which hostname answered is log context only. The client is built with
|
|
3496
|
+
// the full hostname list regardless — the websocket connection races all
|
|
3497
|
+
// of them anyway.
|
|
3498
|
+
hostname: winner.hostname,
|
|
3494
3499
|
version: version.version,
|
|
3495
3500
|
websocketPath: version.websocketPath
|
|
3496
3501
|
});
|
|
3497
3502
|
} catch (error) {
|
|
3498
3503
|
const errorMessage = errorMessageOrDefault(error, "Unknown error");
|
|
3499
3504
|
if (!(error instanceof VersionDiscoveryNetworkError)) {
|
|
3500
|
-
logger.error("Version discovery failed", {
|
|
3505
|
+
logger.error("Version discovery failed on every hostname", {
|
|
3501
3506
|
uuid: uuid.slice(0, 8),
|
|
3502
|
-
|
|
3507
|
+
hostnames: hostnames.join(", "),
|
|
3503
3508
|
error: errorMessage,
|
|
3504
3509
|
errorType: error instanceof Error ? error.constructor.name : typeof error
|
|
3505
3510
|
});
|
|
@@ -3510,7 +3515,7 @@ async function createTrueNasClient(opts) {
|
|
|
3510
3515
|
if (!fallbackVersion) {
|
|
3511
3516
|
logger.error("Invalid fallback version configuration", {
|
|
3512
3517
|
uuid: uuid.slice(0, 8),
|
|
3513
|
-
|
|
3518
|
+
hostnames: hostnames.join(", "),
|
|
3514
3519
|
fallbackVersion: fallbackVersionString
|
|
3515
3520
|
});
|
|
3516
3521
|
throw error;
|
|
@@ -3519,7 +3524,7 @@ async function createTrueNasClient(opts) {
|
|
|
3519
3524
|
"Version discovery failed with a network error (possible CORS or network issue), falling back to assumed version",
|
|
3520
3525
|
{
|
|
3521
3526
|
uuid: uuid.slice(0, 8),
|
|
3522
|
-
|
|
3527
|
+
hostnames: hostnames.join(", "),
|
|
3523
3528
|
fallbackVersion: fallbackVersionString,
|
|
3524
3529
|
originalError: errorMessage,
|
|
3525
3530
|
warning: "A network error has multiple causes (CORS, network down, DNS failure). The connection may still fail during the WebSocket handshake."
|
|
@@ -3529,6 +3534,27 @@ async function createTrueNasClient(opts) {
|
|
|
3529
3534
|
}
|
|
3530
3535
|
return instantiateClientForVersion(version, opts, logger);
|
|
3531
3536
|
}
|
|
3537
|
+
async function discoverVersionFromAnyHostname(hostnames, versionDiscovery) {
|
|
3538
|
+
const attempts = hostnames.map(
|
|
3539
|
+
(hostname) => firstValueFrom(versionDiscovery.discoverVersion(hostname)).then(
|
|
3540
|
+
(version) => ({ hostname, version })
|
|
3541
|
+
)
|
|
3542
|
+
);
|
|
3543
|
+
try {
|
|
3544
|
+
return await Promise.any(attempts);
|
|
3545
|
+
} catch (error) {
|
|
3546
|
+
const failures = error instanceof AggregateError ? error.errors : [error];
|
|
3547
|
+
throw selectRepresentativeFailure(failures);
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
function selectRepresentativeFailure(failures) {
|
|
3551
|
+
const isVersionError = (error) => (
|
|
3552
|
+
// cases: valid response, but the given versions won't work for us
|
|
3553
|
+
error instanceof VersionTooOldError || error instanceof VersionTooNewError || error instanceof NoCompatibleVersionsError || error instanceof VersionEndpointNotFoundError
|
|
3554
|
+
);
|
|
3555
|
+
const isNetworkError = (error) => error instanceof VersionDiscoveryNetworkError;
|
|
3556
|
+
return failures.find(isVersionError) ?? failures.find(isNetworkError) ?? failures[0];
|
|
3557
|
+
}
|
|
3532
3558
|
var CLIENT_BY_VERSION_KEY = {
|
|
3533
3559
|
"25.10": TrueNasApiClientV2510,
|
|
3534
3560
|
"26": TrueNasApiClientV26
|