@truenas/api-client 8.0.0 → 9.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/README.md +37 -8
- package/dist/{chunk-RH7UXULE.js → chunk-K7BL6J3Z.js} +313 -81
- package/dist/chunk-K7BL6J3Z.js.map +1 -0
- package/dist/{chunk-DIZAGV4B.cjs → chunk-U6V2Y3AE.cjs} +315 -81
- package/dist/chunk-U6V2Y3AE.cjs.map +1 -0
- package/dist/index.cjs +44 -40
- package/dist/index.d.cts +68 -21
- package/dist/index.d.ts +68 -21
- package/dist/index.js +1 -1
- package/dist/testing/index.cjs +28 -16
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +15 -2
- package/dist/testing/index.d.ts +15 -2
- package/dist/testing/index.js +18 -6
- package/dist/testing/index.js.map +1 -1
- package/dist/{truenas-auth-mechanism.enum-DUitTxen.d.cts → truenas-auth-mechanism.enum-Bp2XMMwK.d.cts} +596 -122
- package/dist/{truenas-auth-mechanism.enum-DUitTxen.d.ts → truenas-auth-mechanism.enum-Bp2XMMwK.d.ts} +596 -122
- package/package.json +2 -2
- package/dist/chunk-DIZAGV4B.cjs.map +0 -1
- package/dist/chunk-RH7UXULE.js.map +0 -1
|
@@ -30,6 +30,34 @@ function getApiErrorMessage(error, fallback = "API call failed") {
|
|
|
30
30
|
return fallback;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// src/errors/api.errors.ts
|
|
34
|
+
var TrueNasApiError = class _TrueNasApiError extends Error {
|
|
35
|
+
constructor(frame, method) {
|
|
36
|
+
super(getApiErrorMessage(frame, "API call failed"));
|
|
37
|
+
this.frame = frame;
|
|
38
|
+
this.method = method;
|
|
39
|
+
this.name = "TrueNasApiError";
|
|
40
|
+
Object.setPrototypeOf(this, _TrueNasApiError.prototype);
|
|
41
|
+
}
|
|
42
|
+
/** The JSON-RPC code: `-32001` for a refused call, others for a malformed one. */
|
|
43
|
+
get code() {
|
|
44
|
+
return this.frame.code;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The TrueNAS payload — `errname`, `reason`, `extra`, `trace`.
|
|
48
|
+
*
|
|
49
|
+
* `undefined` for the codes middleware sends without one, such as
|
|
50
|
+
* `-32601 "Method does not exist"`.
|
|
51
|
+
*/
|
|
52
|
+
get data() {
|
|
53
|
+
return this.frame.data;
|
|
54
|
+
}
|
|
55
|
+
/** `data.errname`, e.g. `EINVAL`, or `undefined` when there is no payload. */
|
|
56
|
+
get errname() {
|
|
57
|
+
return this.frame.data?.errname;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
33
61
|
// src/types/job.type.ts
|
|
34
62
|
var JobState = /* @__PURE__ */ ((JobState2) => {
|
|
35
63
|
JobState2["Pending"] = "PENDING";
|
|
@@ -1648,9 +1676,9 @@ __export(v25_10_5_exports, {
|
|
|
1648
1676
|
ZfsTierRewriteJobEntryStatusInput: () => ZfsTierRewriteJobEntryStatusInput
|
|
1649
1677
|
});
|
|
1650
1678
|
|
|
1651
|
-
// src/generated/
|
|
1652
|
-
var
|
|
1653
|
-
__export(
|
|
1679
|
+
// src/generated/v27_0_0/index.ts
|
|
1680
|
+
var v27_0_0_exports = {};
|
|
1681
|
+
__export(v27_0_0_exports, {
|
|
1654
1682
|
ACLTemplateEntryAcltypeInput: () => ACLTemplateEntryAcltypeInput,
|
|
1655
1683
|
Access: () => Access,
|
|
1656
1684
|
Aclmode: () => Aclmode,
|
|
@@ -1783,7 +1811,7 @@ __export(v26_0_0_exports, {
|
|
|
1783
1811
|
ZpoolScrubRunAction: () => ZpoolScrubRunAction
|
|
1784
1812
|
});
|
|
1785
1813
|
|
|
1786
|
-
// src/generated/
|
|
1814
|
+
// src/generated/v27_0_0/api-types.ts
|
|
1787
1815
|
var Access = {
|
|
1788
1816
|
Readonly: "READONLY",
|
|
1789
1817
|
Writeonly: "WRITEONLY",
|
|
@@ -1905,9 +1933,9 @@ var ZpoolScrubRunAction = {
|
|
|
1905
1933
|
Cancel: "CANCEL"
|
|
1906
1934
|
};
|
|
1907
1935
|
|
|
1908
|
-
// src/generated/
|
|
1909
|
-
var
|
|
1910
|
-
__export(
|
|
1936
|
+
// src/generated/v28_0_0/index.ts
|
|
1937
|
+
var v28_0_0_exports = {};
|
|
1938
|
+
__export(v28_0_0_exports, {
|
|
1911
1939
|
ACLTemplateEntryAcltypeInput: () => ACLTemplateEntryAcltypeInput,
|
|
1912
1940
|
Access: () => Access,
|
|
1913
1941
|
Aclmode: () => Aclmode,
|
|
@@ -1930,7 +1958,7 @@ __export(v27_0_0_exports, {
|
|
|
1930
1958
|
CloudTaskAttributesEncryption: () => CloudTaskAttributesEncryption,
|
|
1931
1959
|
CloudTaskAttributesEncryptionInput: () => CloudTaskAttributesEncryptionInput,
|
|
1932
1960
|
Code: () => Code,
|
|
1933
|
-
Compression: () =>
|
|
1961
|
+
Compression: () => Compression2,
|
|
1934
1962
|
ContainerNICDeviceType: () => ContainerNICDeviceType,
|
|
1935
1963
|
ContainerNICDeviceTypeInput: () => ContainerNICDeviceTypeInput,
|
|
1936
1964
|
ContainerStatusState: () => ContainerStatusState,
|
|
@@ -2008,6 +2036,7 @@ __export(v27_0_0_exports, {
|
|
|
2008
2036
|
Snapdir: () => Snapdir,
|
|
2009
2037
|
SourceInput: () => SourceInput,
|
|
2010
2038
|
SourceValueType: () => SourceValueType,
|
|
2039
|
+
SourceValueTypeInput: () => SourceValueTypeInput,
|
|
2011
2040
|
SupportNewTicketCommunityTypeInput: () => SupportNewTicketCommunityTypeInput,
|
|
2012
2041
|
Sync: () => Sync,
|
|
2013
2042
|
SyslogServerTransport: () => SyslogServerTransport,
|
|
@@ -2041,7 +2070,67 @@ __export(v27_0_0_exports, {
|
|
|
2041
2070
|
ZpoolScrubRunAction: () => ZpoolScrubRunAction
|
|
2042
2071
|
});
|
|
2043
2072
|
|
|
2044
|
-
// src/generated/
|
|
2073
|
+
// src/generated/v28_0_0/api-types.ts
|
|
2074
|
+
var Compression2 = {
|
|
2075
|
+
On: "ON",
|
|
2076
|
+
Off: "OFF",
|
|
2077
|
+
Lz4: "LZ4",
|
|
2078
|
+
Gzip: "GZIP",
|
|
2079
|
+
Gzip1: "GZIP-1",
|
|
2080
|
+
Gzip2: "GZIP-2",
|
|
2081
|
+
Gzip3: "GZIP-3",
|
|
2082
|
+
Gzip4: "GZIP-4",
|
|
2083
|
+
Gzip5: "GZIP-5",
|
|
2084
|
+
Gzip6: "GZIP-6",
|
|
2085
|
+
Gzip7: "GZIP-7",
|
|
2086
|
+
Gzip8: "GZIP-8",
|
|
2087
|
+
Gzip9: "GZIP-9",
|
|
2088
|
+
Zstd: "ZSTD",
|
|
2089
|
+
ZstdFast: "ZSTD-FAST",
|
|
2090
|
+
Zle: "ZLE",
|
|
2091
|
+
Lzjb: "LZJB",
|
|
2092
|
+
Zstd1: "ZSTD-1",
|
|
2093
|
+
Zstd2: "ZSTD-2",
|
|
2094
|
+
Zstd3: "ZSTD-3",
|
|
2095
|
+
Zstd4: "ZSTD-4",
|
|
2096
|
+
Zstd5: "ZSTD-5",
|
|
2097
|
+
Zstd6: "ZSTD-6",
|
|
2098
|
+
Zstd7: "ZSTD-7",
|
|
2099
|
+
Zstd8: "ZSTD-8",
|
|
2100
|
+
Zstd9: "ZSTD-9",
|
|
2101
|
+
Zstd10: "ZSTD-10",
|
|
2102
|
+
Zstd11: "ZSTD-11",
|
|
2103
|
+
Zstd12: "ZSTD-12",
|
|
2104
|
+
Zstd13: "ZSTD-13",
|
|
2105
|
+
Zstd14: "ZSTD-14",
|
|
2106
|
+
Zstd15: "ZSTD-15",
|
|
2107
|
+
Zstd16: "ZSTD-16",
|
|
2108
|
+
Zstd17: "ZSTD-17",
|
|
2109
|
+
Zstd18: "ZSTD-18",
|
|
2110
|
+
Zstd19: "ZSTD-19",
|
|
2111
|
+
ZstdFast1: "ZSTD-FAST-1",
|
|
2112
|
+
ZstdFast2: "ZSTD-FAST-2",
|
|
2113
|
+
ZstdFast3: "ZSTD-FAST-3",
|
|
2114
|
+
ZstdFast4: "ZSTD-FAST-4",
|
|
2115
|
+
ZstdFast5: "ZSTD-FAST-5",
|
|
2116
|
+
ZstdFast6: "ZSTD-FAST-6",
|
|
2117
|
+
ZstdFast7: "ZSTD-FAST-7",
|
|
2118
|
+
ZstdFast8: "ZSTD-FAST-8",
|
|
2119
|
+
ZstdFast9: "ZSTD-FAST-9",
|
|
2120
|
+
ZstdFast10: "ZSTD-FAST-10",
|
|
2121
|
+
ZstdFast20: "ZSTD-FAST-20",
|
|
2122
|
+
ZstdFast30: "ZSTD-FAST-30",
|
|
2123
|
+
ZstdFast40: "ZSTD-FAST-40",
|
|
2124
|
+
ZstdFast50: "ZSTD-FAST-50",
|
|
2125
|
+
ZstdFast60: "ZSTD-FAST-60",
|
|
2126
|
+
ZstdFast70: "ZSTD-FAST-70",
|
|
2127
|
+
ZstdFast80: "ZSTD-FAST-80",
|
|
2128
|
+
ZstdFast90: "ZSTD-FAST-90",
|
|
2129
|
+
ZstdFast100: "ZSTD-FAST-100",
|
|
2130
|
+
ZstdFast500: "ZSTD-FAST-500",
|
|
2131
|
+
ZstdFast1000: "ZSTD-FAST-1000",
|
|
2132
|
+
Inherit: "INHERIT"
|
|
2133
|
+
};
|
|
2045
2134
|
var Service2 = {
|
|
2046
2135
|
Middleware: "MIDDLEWARE",
|
|
2047
2136
|
S3: "S3",
|
|
@@ -2049,6 +2138,14 @@ var Service2 = {
|
|
|
2049
2138
|
Sudo: "SUDO",
|
|
2050
2139
|
System: "SYSTEM"
|
|
2051
2140
|
};
|
|
2141
|
+
var SourceValueTypeInput = {
|
|
2142
|
+
None: "NONE",
|
|
2143
|
+
Default: "DEFAULT",
|
|
2144
|
+
Temporary: "TEMPORARY",
|
|
2145
|
+
Local: "LOCAL",
|
|
2146
|
+
Inherited: "INHERITED",
|
|
2147
|
+
Received: "RECEIVED"
|
|
2148
|
+
};
|
|
2052
2149
|
var VMWareMatchDatastoresWithDatasetsResultFilesystemTypeInput = {
|
|
2053
2150
|
Filesystem: "FILESYSTEM",
|
|
2054
2151
|
Volume: "VOLUME"
|
|
@@ -2062,8 +2159,8 @@ var SUPPORTED_API_VERSIONS = [
|
|
|
2062
2159
|
"v25.10.3",
|
|
2063
2160
|
"v25.10.4",
|
|
2064
2161
|
"v25.10.5",
|
|
2065
|
-
"
|
|
2066
|
-
"
|
|
2162
|
+
"v27.0.0",
|
|
2163
|
+
"v28.0.0"
|
|
2067
2164
|
];
|
|
2068
2165
|
|
|
2069
2166
|
// src/types/api-version.type.ts
|
|
@@ -2071,6 +2168,7 @@ var VersionCompatibility = /* @__PURE__ */ ((VersionCompatibility2) => {
|
|
|
2071
2168
|
VersionCompatibility2["Compatible"] = "compatible";
|
|
2072
2169
|
VersionCompatibility2["TooOld"] = "too-old";
|
|
2073
2170
|
VersionCompatibility2["TooNew"] = "too-new";
|
|
2171
|
+
VersionCompatibility2["Unsupported"] = "unsupported";
|
|
2074
2172
|
VersionCompatibility2["Invalid"] = "invalid";
|
|
2075
2173
|
return VersionCompatibility2;
|
|
2076
2174
|
})(VersionCompatibility || {});
|
|
@@ -2154,6 +2252,19 @@ function createJsonRpcMessage(method, params) {
|
|
|
2154
2252
|
};
|
|
2155
2253
|
}
|
|
2156
2254
|
|
|
2255
|
+
// src/utils/subscription.utils.ts
|
|
2256
|
+
function subscriptionName(event, params) {
|
|
2257
|
+
return params === void 0 ? event : `${event}:${JSON.stringify(params, sortKeys)}`;
|
|
2258
|
+
}
|
|
2259
|
+
function sortKeys(_key, value) {
|
|
2260
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2261
|
+
return value;
|
|
2262
|
+
}
|
|
2263
|
+
return Object.fromEntries(
|
|
2264
|
+
Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
|
|
2265
|
+
);
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2157
2268
|
// src/api/truenas-api.ts
|
|
2158
2269
|
var EVENT_KINDS = ["added", "changed", "removed"];
|
|
2159
2270
|
var TrueNasApi = class {
|
|
@@ -2163,8 +2274,13 @@ var TrueNasApi = class {
|
|
|
2163
2274
|
/**
|
|
2164
2275
|
* Stream of job events from websocket.
|
|
2165
2276
|
* JSON-RPC 2.0 events have structure: { method: 'collection_update', params: { collection, fields, ... } }
|
|
2277
|
+
*
|
|
2278
|
+
* Reads `messages()` when first subscribed, not when this is built, as every
|
|
2279
|
+
* other verb does. A consumer that wraps the connection's `messages()` after
|
|
2280
|
+
* the client is created — a debug panel mixing in mocked answers — is
|
|
2281
|
+
* otherwise heard by `call` and `events` but not by job tracking.
|
|
2166
2282
|
*/
|
|
2167
|
-
this.jobEvents = this.connection.messages().pipe(
|
|
2283
|
+
this.jobEvents = rxjs.defer(() => this.connection.messages()).pipe(
|
|
2168
2284
|
rxjs.filter(
|
|
2169
2285
|
(res) => res.method === "collection_update" && res.params?.collection === "core.get_jobs"
|
|
2170
2286
|
),
|
|
@@ -2173,11 +2289,20 @@ var TrueNasApi = class {
|
|
|
2173
2289
|
rxjs.share()
|
|
2174
2290
|
);
|
|
2175
2291
|
/**
|
|
2176
|
-
* One shared stream per
|
|
2177
|
-
*
|
|
2178
|
-
* for lives on the server and is per socket, not per caller.
|
|
2292
|
+
* One shared stream per subscription — see {@link events}. Keyed by the
|
|
2293
|
+
* name *with* its arguments: a tail of one file is not a tail of another.
|
|
2179
2294
|
*/
|
|
2180
2295
|
this.eventStreams = /* @__PURE__ */ new Map();
|
|
2296
|
+
/**
|
|
2297
|
+
* Releases awaiting their `core.unsubscribe` reply, per name. Middleware
|
|
2298
|
+
* sends a source's release notice (`terminate`) before that reply, and a
|
|
2299
|
+
* notice names only the collection; so an error-free one arriving inside
|
|
2300
|
+
* that window belongs to the release, not to a later subscription. One per
|
|
2301
|
+
* window, which closes on the reply whether or not a notice came.
|
|
2302
|
+
*/
|
|
2303
|
+
this.releasing = /* @__PURE__ */ new Map();
|
|
2304
|
+
/** The live subscription per name, told when the server ends it. */
|
|
2305
|
+
this.serverEnds = /* @__PURE__ */ new Map();
|
|
2181
2306
|
this.initializeJobEventsSubscription();
|
|
2182
2307
|
}
|
|
2183
2308
|
/**
|
|
@@ -2215,11 +2340,7 @@ var TrueNasApi = class {
|
|
|
2215
2340
|
withId(messageId),
|
|
2216
2341
|
rxjs.map((msg) => {
|
|
2217
2342
|
if (msg.error) {
|
|
2218
|
-
|
|
2219
|
-
msg.error,
|
|
2220
|
-
"API call failed"
|
|
2221
|
-
);
|
|
2222
|
-
throw new Error(errorMessage);
|
|
2343
|
+
throw new TrueNasApiError(msg.error, method);
|
|
2223
2344
|
}
|
|
2224
2345
|
return msg.result;
|
|
2225
2346
|
}),
|
|
@@ -2272,21 +2393,32 @@ var TrueNasApi = class {
|
|
|
2272
2393
|
*
|
|
2273
2394
|
* The id comes from the first job event whose `message_ids` carries this
|
|
2274
2395
|
* request's id, not from the response: what a job method returns on the wire
|
|
2275
|
-
* differs by version (v25.10 answers with the id,
|
|
2396
|
+
* differs by version (v25.10 answers with the id, v27 with `null`), while
|
|
2276
2397
|
* the event correlation holds for both.
|
|
2277
2398
|
*
|
|
2278
|
-
* @returns
|
|
2399
|
+
* @returns the job's id; errors with a {@link TrueNasApiError} if the method is refused before a job starts
|
|
2279
2400
|
*/
|
|
2280
2401
|
callAndGetJobId(method, ...params) {
|
|
2281
2402
|
return rxjs.defer(() => {
|
|
2282
2403
|
const message = createJsonRpcMessage(method, params[0]);
|
|
2283
|
-
const
|
|
2404
|
+
const started = this.jobEvents.pipe(
|
|
2284
2405
|
rxjs.filter((job) => job.message_ids?.includes(message.id ?? "") ?? false),
|
|
2285
|
-
rxjs.map((job) => job.id)
|
|
2286
|
-
|
|
2406
|
+
rxjs.map((job) => job.id)
|
|
2407
|
+
);
|
|
2408
|
+
const refused = this.connection.messages().pipe(
|
|
2409
|
+
withId(message.id ?? ""),
|
|
2410
|
+
rxjs.map((reply) => {
|
|
2411
|
+
if (reply.error) {
|
|
2412
|
+
throw new TrueNasApiError(reply.error, method);
|
|
2413
|
+
}
|
|
2414
|
+
}),
|
|
2415
|
+
rxjs.ignoreElements()
|
|
2287
2416
|
);
|
|
2288
2417
|
const sending = this.connection.send(message);
|
|
2289
|
-
return
|
|
2418
|
+
return rxjs.merge(started, refused).pipe(
|
|
2419
|
+
rxjs.take(1),
|
|
2420
|
+
rxjs.finalize(() => sending.unsubscribe())
|
|
2421
|
+
);
|
|
2290
2422
|
});
|
|
2291
2423
|
}
|
|
2292
2424
|
/**
|
|
@@ -2305,41 +2437,118 @@ var TrueNasApi = class {
|
|
|
2305
2437
|
rxjs.switchMap((jobId) => this.trackJob(jobId))
|
|
2306
2438
|
);
|
|
2307
2439
|
}
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
* api.events('app.query').subscribe(event => {
|
|
2313
|
-
* if (event.msg === 'removed') return drop(event.id);
|
|
2314
|
-
* render(event.fields); // only reachable once narrowed
|
|
2315
|
-
* });
|
|
2316
|
-
* ```
|
|
2317
|
-
*
|
|
2318
|
-
* Narrow before reading `fields`: `removed` events almost never carry it.
|
|
2319
|
-
* Sources taking subscribe-time arguments are excluded; see {@link EventName}.
|
|
2320
|
-
*/
|
|
2321
|
-
events(event) {
|
|
2322
|
-
const existing = this.eventStreams.get(event);
|
|
2440
|
+
events(event, params) {
|
|
2441
|
+
const name = subscriptionName(event, params);
|
|
2442
|
+
this.listenForEndNotices();
|
|
2443
|
+
const existing = this.eventStreams.get(name);
|
|
2323
2444
|
if (existing) return existing;
|
|
2445
|
+
const forget = () => {
|
|
2446
|
+
if (this.eventStreams.get(name) === stream) this.eventStreams.delete(name);
|
|
2447
|
+
};
|
|
2324
2448
|
const stream = rxjs.defer(() => {
|
|
2325
|
-
|
|
2326
|
-
|
|
2449
|
+
let subscriptionId;
|
|
2450
|
+
let released = false;
|
|
2451
|
+
let serverEnded = false;
|
|
2452
|
+
const ended = new rxjs.Subject();
|
|
2453
|
+
this.serverEnds.set(name, ended);
|
|
2454
|
+
const subscribing = this.authenticated.pipe(rxjs.distinctUntilChanged()).subscribe((isAuthenticated) => {
|
|
2455
|
+
subscriptionId = void 0;
|
|
2456
|
+
if (!isAuthenticated) return;
|
|
2457
|
+
const request = createJsonRpcMessage("core.subscribe", [name]);
|
|
2458
|
+
this.connection.messages().pipe(
|
|
2459
|
+
withId(request.id ?? ""),
|
|
2460
|
+
rxjs.take(1),
|
|
2461
|
+
rxjs.takeUntil(this.authenticated.pipe(rxjs.filter((isUp) => !isUp)))
|
|
2462
|
+
).subscribe((reply) => {
|
|
2463
|
+
if (typeof reply.result !== "string") return;
|
|
2464
|
+
if (!released) subscriptionId = reply.result;
|
|
2465
|
+
else if (!serverEnded) this.unsubscribe(name, reply.result);
|
|
2466
|
+
});
|
|
2467
|
+
this.connection.send(request);
|
|
2327
2468
|
});
|
|
2328
|
-
|
|
2329
|
-
rxjs.filter((res) => res.method === "collection_update"),
|
|
2330
|
-
rxjs.map((res) => res.params),
|
|
2469
|
+
const updates = this.connection.messages().pipe(
|
|
2331
2470
|
rxjs.filter(
|
|
2332
|
-
(
|
|
2471
|
+
(res) => res.method === "collection_update" && res.params?.collection === name
|
|
2333
2472
|
),
|
|
2473
|
+
rxjs.map((res) => res.params),
|
|
2474
|
+
rxjs.filter((params2) => EVENT_KINDS.includes(params2.msg)),
|
|
2475
|
+
// `merge` below ends only when both inputs do. The connection closing
|
|
2476
|
+
// for good ends this one; `ended` has to follow, or the stream never
|
|
2477
|
+
// completes and nothing tears down the resubscription.
|
|
2478
|
+
rxjs.finalize(() => ended.complete())
|
|
2479
|
+
);
|
|
2480
|
+
const end = ended.pipe(
|
|
2481
|
+
rxjs.map((notice) => {
|
|
2482
|
+
subscriptionId = void 0;
|
|
2483
|
+
serverEnded = true;
|
|
2484
|
+
if (notice.error) {
|
|
2485
|
+
throw new TrueNasApiError(
|
|
2486
|
+
{ code: -32001, message: notice.error.reason, data: notice.error },
|
|
2487
|
+
name
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
})
|
|
2491
|
+
);
|
|
2492
|
+
return rxjs.merge(updates, end).pipe(
|
|
2493
|
+
rxjs.takeWhile((change) => change !== void 0),
|
|
2334
2494
|
// `collection` is the subscription the caller already named; what is
|
|
2335
2495
|
// left is exactly the payload the directory declares, plus its tag.
|
|
2336
2496
|
rxjs.map(({ collection, ...change }) => change),
|
|
2337
|
-
|
|
2497
|
+
// An ended stream stays ended (below), so the next `events()` call for
|
|
2498
|
+
// this name needs a new one.
|
|
2499
|
+
rxjs.tap({ complete: forget, error: forget }),
|
|
2500
|
+
rxjs.finalize(() => {
|
|
2501
|
+
released = true;
|
|
2502
|
+
subscribing.unsubscribe();
|
|
2503
|
+
if (this.serverEnds.get(name) === ended) this.serverEnds.delete(name);
|
|
2504
|
+
if (subscriptionId) this.unsubscribe(name, subscriptionId);
|
|
2505
|
+
})
|
|
2338
2506
|
);
|
|
2339
|
-
}).pipe(
|
|
2340
|
-
|
|
2507
|
+
}).pipe(
|
|
2508
|
+
rxjs.share({
|
|
2509
|
+
resetOnRefCountZero: true,
|
|
2510
|
+
resetOnComplete: false,
|
|
2511
|
+
resetOnError: false
|
|
2512
|
+
})
|
|
2513
|
+
);
|
|
2514
|
+
this.eventStreams.set(name, stream);
|
|
2341
2515
|
return stream;
|
|
2342
2516
|
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Release a subscription by the id `core.subscribe` answered with, holding
|
|
2519
|
+
* its notice window (see {@link releasing}) open until the reply.
|
|
2520
|
+
*/
|
|
2521
|
+
unsubscribe(name, subscriptionId) {
|
|
2522
|
+
const release = { noticed: false };
|
|
2523
|
+
this.releasing.set(name, [...this.releasing.get(name) ?? [], release]);
|
|
2524
|
+
const request = createJsonRpcMessage("core.unsubscribe", [subscriptionId]);
|
|
2525
|
+
this.connection.messages().pipe(
|
|
2526
|
+
withId(request.id ?? ""),
|
|
2527
|
+
rxjs.take(1),
|
|
2528
|
+
rxjs.takeUntil(this.authenticated.pipe(rxjs.filter((isUp) => !isUp))),
|
|
2529
|
+
rxjs.finalize(() => {
|
|
2530
|
+
const open = (this.releasing.get(name) ?? []).filter((r) => r !== release);
|
|
2531
|
+
if (open.length) this.releasing.set(name, open);
|
|
2532
|
+
else this.releasing.delete(name);
|
|
2533
|
+
})
|
|
2534
|
+
).subscribe();
|
|
2535
|
+
this.connection.send(request);
|
|
2536
|
+
}
|
|
2537
|
+
/**
|
|
2538
|
+
* Route each `notify_unsubscribed` once: an error-free one inside an open
|
|
2539
|
+
* release window is that release's, anything else ends the name's live
|
|
2540
|
+
* subscription. Installed on the first `events()` call, before any stream
|
|
2541
|
+
* listens.
|
|
2542
|
+
*/
|
|
2543
|
+
listenForEndNotices() {
|
|
2544
|
+
if (this.endNotices) return;
|
|
2545
|
+
this.endNotices = this.connection.messages().pipe(rxjs.filter((res) => res.method === "notify_unsubscribed")).subscribe((res) => {
|
|
2546
|
+
const notice = res.params;
|
|
2547
|
+
const release = notice.error ? void 0 : this.releasing.get(notice.collection)?.find((r) => !r.noticed);
|
|
2548
|
+
if (release) release.noticed = true;
|
|
2549
|
+
else this.serverEnds.get(notice.collection)?.next(notice);
|
|
2550
|
+
});
|
|
2551
|
+
}
|
|
2343
2552
|
/**
|
|
2344
2553
|
* Convenience wrapper for auth.generate_token API call.
|
|
2345
2554
|
*
|
|
@@ -2372,12 +2581,10 @@ var TrueNasApi = class {
|
|
|
2372
2581
|
rxjs.filter((job) => job.id === jobId),
|
|
2373
2582
|
rxjs.map((job) => job)
|
|
2374
2583
|
);
|
|
2375
|
-
const
|
|
2376
|
-
|
|
2377
|
-
]).pipe(
|
|
2378
|
-
rxjs.map((jobs) => jobs[0]),
|
|
2379
|
-
rxjs.share()
|
|
2584
|
+
const readJob = () => this.dispatch("core.get_jobs", [[["id", "=", jobId]]]).pipe(
|
|
2585
|
+
rxjs.map((jobs) => jobs[0])
|
|
2380
2586
|
);
|
|
2587
|
+
const read$ = readJob().pipe(rxjs.share());
|
|
2381
2588
|
const missing$ = read$.pipe(
|
|
2382
2589
|
rxjs.filter((job) => job === void 0),
|
|
2383
2590
|
rxjs.takeUntil(updates$)
|
|
@@ -2386,10 +2593,29 @@ var TrueNasApi = class {
|
|
|
2386
2593
|
rxjs.filter((job) => job !== void 0),
|
|
2387
2594
|
rxjs.takeUntil(updates$)
|
|
2388
2595
|
);
|
|
2389
|
-
|
|
2596
|
+
const reread$ = this.authenticated.pipe(
|
|
2597
|
+
rxjs.distinctUntilChanged(),
|
|
2598
|
+
rxjs.skipWhile((isUp) => !isUp),
|
|
2599
|
+
rxjs.pairwise(),
|
|
2600
|
+
rxjs.filter(([wasUp, isUp]) => !wasUp && isUp),
|
|
2601
|
+
rxjs.switchMap(
|
|
2602
|
+
() => readJob().pipe(
|
|
2603
|
+
// Stale once events flow again, as the opening read is; they carry the end.
|
|
2604
|
+
rxjs.takeUntil(updates$),
|
|
2605
|
+
// A failed read waits for the next reconnect.
|
|
2606
|
+
rxjs.catchError(() => rxjs.EMPTY)
|
|
2607
|
+
)
|
|
2608
|
+
),
|
|
2609
|
+
rxjs.share()
|
|
2610
|
+
);
|
|
2611
|
+
const gone$ = reread$.pipe(rxjs.filter((job) => job === void 0));
|
|
2612
|
+
const rereadJob$ = reread$.pipe(
|
|
2613
|
+
rxjs.filter((job) => job !== void 0)
|
|
2614
|
+
);
|
|
2615
|
+
return rxjs.merge(snapshot$, updates$, rereadJob$).pipe(
|
|
2390
2616
|
rxjs.takeWhile((job) => !isJobFinished(job), true),
|
|
2391
2617
|
// Include the final state
|
|
2392
|
-
rxjs.takeUntil(missing$)
|
|
2618
|
+
rxjs.takeUntil(rxjs.merge(missing$, gone$))
|
|
2393
2619
|
);
|
|
2394
2620
|
}
|
|
2395
2621
|
/**
|
|
@@ -2429,7 +2655,7 @@ var apiVersionConfig = {
|
|
|
2429
2655
|
* not write its client, so the ceiling is the newest version in
|
|
2430
2656
|
* `CLIENT_BY_VERSION_KEY`. A factory test asserts the two agree.
|
|
2431
2657
|
*/
|
|
2432
|
-
MAX_SUPPORTED_VERSION: "
|
|
2658
|
+
MAX_SUPPORTED_VERSION: "v28.0.0",
|
|
2433
2659
|
/**
|
|
2434
2660
|
* Version assumed when discovery is CORS-blocked, as v25.10.0's
|
|
2435
2661
|
* `/api/versions` is. Used only for an appliance that answers a reachability
|
|
@@ -2489,6 +2715,9 @@ function checkVersionCompatibility(version) {
|
|
|
2489
2715
|
if (compareVersions(version, maxVersion) > 0) {
|
|
2490
2716
|
return "too-new" /* TooNew */;
|
|
2491
2717
|
}
|
|
2718
|
+
if (!SUPPORTED_API_VERSIONS.includes(version.version)) {
|
|
2719
|
+
return "unsupported" /* Unsupported */;
|
|
2720
|
+
}
|
|
2492
2721
|
return "compatible" /* Compatible */;
|
|
2493
2722
|
}
|
|
2494
2723
|
function isVersionSupported(version) {
|
|
@@ -2594,7 +2823,7 @@ var TrueNasAuthenticator = class _TrueNasAuthenticator {
|
|
|
2594
2823
|
/**
|
|
2595
2824
|
* `login_options` asking for a reconnect token, when the server understands it.
|
|
2596
2825
|
*
|
|
2597
|
-
* Omitted below
|
|
2826
|
+
* Omitted below v27. `AuthCommonOptions` is `additionalProperties: false`
|
|
2598
2827
|
* there and has only `user_info`, so sending the member is a validation
|
|
2599
2828
|
* error, not an ignored field — it would fail login outright on the oldest
|
|
2600
2829
|
* version this client supports.
|
|
@@ -2602,7 +2831,7 @@ var TrueNasAuthenticator = class _TrueNasAuthenticator {
|
|
|
2602
2831
|
/**
|
|
2603
2832
|
* Two things this does not do, both deliberate and both worth knowing.
|
|
2604
2833
|
*
|
|
2605
|
-
* There is no way for a consumer to decline: every
|
|
2834
|
+
* There is no way for a consumer to decline: every v27+ password login now
|
|
2606
2835
|
* mints a single-use credential carrying that session's roles, whether or not
|
|
2607
2836
|
* the caller wants one. And the auto-relogin in the constructor subscribes
|
|
2608
2837
|
* with no observer, so the token it mints is dropped — a caller reconnecting
|
|
@@ -2737,7 +2966,7 @@ var TrueNasAuthenticator = class _TrueNasAuthenticator {
|
|
|
2737
2966
|
* e.g. to open another connection without asking for the password again.
|
|
2738
2967
|
*
|
|
2739
2968
|
* Tokens are single-use, short-lived, and voided by a `middlewared` restart.
|
|
2740
|
-
* On
|
|
2969
|
+
* On v27+ each successful login mints a new one; store the newest. Token
|
|
2741
2970
|
* sessions are not re-logged automatically on reconnect — the caller must.
|
|
2742
2971
|
*/
|
|
2743
2972
|
loginWithToken(token) {
|
|
@@ -2796,7 +3025,7 @@ var TrueNasAuthenticator = class _TrueNasAuthenticator {
|
|
|
2796
3025
|
);
|
|
2797
3026
|
}
|
|
2798
3027
|
/**
|
|
2799
|
-
* No reconnect token is requested here, though
|
|
3028
|
+
* No reconnect token is requested here, though v27+ would mint one: an
|
|
2800
3029
|
* api-key session already reconnects without a prompt, since the key is held
|
|
2801
3030
|
* and replayed. The token exists for the credential that cannot be.
|
|
2802
3031
|
*/
|
|
@@ -3459,7 +3688,7 @@ function toAppState(state) {
|
|
|
3459
3688
|
case "STARTING":
|
|
3460
3689
|
case "DEPLOYING":
|
|
3461
3690
|
return "DEPLOYING" /* Deploying */;
|
|
3462
|
-
// v25.10's freezer states are the same idea as
|
|
3691
|
+
// v25.10's freezer states are the same idea as v27's `SUSPENDED`: the
|
|
3463
3692
|
// processes are paused, not exited. `FROZEN` is that pause completed, so it
|
|
3464
3693
|
// and `SUSPENDED` are the same answer.
|
|
3465
3694
|
case "SUSPENDED":
|
|
@@ -3571,14 +3800,14 @@ function toContainer(instance) {
|
|
|
3571
3800
|
// declares only `description`, but the API returns `architecture`, `os`,
|
|
3572
3801
|
// `release` and more, and callers were already receiving them. Narrowing
|
|
3573
3802
|
// to the declared field would take data away to match a type that was
|
|
3574
|
-
// always an under-declaration — the same call as the
|
|
3803
|
+
// always an under-declaration — the same call as the v27 `description`
|
|
3575
3804
|
// widening, decided the same way.
|
|
3576
3805
|
image: instance.image.description === null ? void 0 : { ...instance.image, description: instance.image.description }
|
|
3577
3806
|
};
|
|
3578
3807
|
}
|
|
3579
|
-
var
|
|
3808
|
+
var TrueNasApiClientV27 = class extends TrueNasApiClient {
|
|
3580
3809
|
/**
|
|
3581
|
-
* Create
|
|
3810
|
+
* Create v27-specific operation mappings
|
|
3582
3811
|
*
|
|
3583
3812
|
* Operations return Observable<Job | null>:
|
|
3584
3813
|
* - Async operations emit Job updates until complete
|
|
@@ -3592,7 +3821,7 @@ var TrueNasApiClientV26 = class extends TrueNasApiClient {
|
|
|
3592
3821
|
containerQuery: () => this.api.query("container.query").pipe(
|
|
3593
3822
|
rxjs.map((containers) => containers.map(toContainer2))
|
|
3594
3823
|
),
|
|
3595
|
-
// container.start is synchronous in
|
|
3824
|
+
// container.start is synchronous in v27.0.0 - emit null
|
|
3596
3825
|
containerStart: (id) => this.api.call("container.start", [parseInt(id, 10)]).pipe(rxjs.map(() => null)),
|
|
3597
3826
|
// container.stop emits job updates
|
|
3598
3827
|
containerStop: (id, options) => this.api.job("container.stop", [
|
|
@@ -3602,7 +3831,7 @@ var TrueNasApiClientV26 = class extends TrueNasApiClient {
|
|
|
3602
3831
|
force_after_timeout: options.force
|
|
3603
3832
|
}
|
|
3604
3833
|
]),
|
|
3605
|
-
//
|
|
3834
|
+
// v27.0.0 doesn't have container.restart - chain stop + start
|
|
3606
3835
|
// Emits Job updates during stop, then null when start completes
|
|
3607
3836
|
containerRestart: (id, options) => {
|
|
3608
3837
|
const numericId = parseInt(id, 10);
|
|
@@ -3654,9 +3883,9 @@ function toContainer2(container) {
|
|
|
3654
3883
|
description
|
|
3655
3884
|
};
|
|
3656
3885
|
}
|
|
3657
|
-
var
|
|
3886
|
+
var TrueNasApiClientV28 = class extends TrueNasApiClient {
|
|
3658
3887
|
/**
|
|
3659
|
-
* Create
|
|
3888
|
+
* Create v28-specific operation mappings
|
|
3660
3889
|
*
|
|
3661
3890
|
* Operations return Observable<Job | null>:
|
|
3662
3891
|
* - Async operations emit Job updates until complete
|
|
@@ -3670,7 +3899,7 @@ var TrueNasApiClientV27 = class extends TrueNasApiClient {
|
|
|
3670
3899
|
containerQuery: () => this.api.query("container.query").pipe(
|
|
3671
3900
|
rxjs.map((containers) => containers.map(toContainer3))
|
|
3672
3901
|
),
|
|
3673
|
-
// container.start is synchronous in
|
|
3902
|
+
// container.start is synchronous in v28 - emit null
|
|
3674
3903
|
containerStart: (id) => this.api.call("container.start", [parseInt(id, 10)]).pipe(rxjs.map(() => null)),
|
|
3675
3904
|
// container.stop emits job updates
|
|
3676
3905
|
containerStop: (id, options) => this.api.job("container.stop", [
|
|
@@ -3680,7 +3909,7 @@ var TrueNasApiClientV27 = class extends TrueNasApiClient {
|
|
|
3680
3909
|
force_after_timeout: options.force
|
|
3681
3910
|
}
|
|
3682
3911
|
]),
|
|
3683
|
-
//
|
|
3912
|
+
// v28 still has no container.restart - chain stop + start.
|
|
3684
3913
|
// Emits Job updates during stop, then null when start completes.
|
|
3685
3914
|
containerRestart: (id, options) => {
|
|
3686
3915
|
const numericId = parseInt(id, 10);
|
|
@@ -3702,7 +3931,7 @@ var TrueNasApiClientV27 = class extends TrueNasApiClient {
|
|
|
3702
3931
|
)
|
|
3703
3932
|
);
|
|
3704
3933
|
},
|
|
3705
|
-
// Absent options are omitted, as in
|
|
3934
|
+
// Absent options are omitted, as in v27: `[id, null]` fails validation.
|
|
3706
3935
|
containerDelete: (id, options) => this.api.job(
|
|
3707
3936
|
"container.delete",
|
|
3708
3937
|
options ? [parseInt(id, 10), options] : [parseInt(id, 10)]
|
|
@@ -3799,13 +4028,16 @@ var NoCompatibleVersionsError = class extends VersionDiscoveryError {
|
|
|
3799
4028
|
constructor(hostname, availableVersions, supportedRange = {
|
|
3800
4029
|
min: apiVersionConfig.MIN_SUPPORTED_VERSION,
|
|
3801
4030
|
max: apiVersionConfig.MAX_SUPPORTED_VERSION
|
|
3802
|
-
}) {
|
|
4031
|
+
}, supportedVersions = SUPPORTED_API_VERSIONS) {
|
|
3803
4032
|
super(
|
|
3804
|
-
`No compatible API versions available. Supported: ${
|
|
4033
|
+
`No compatible API versions available. Supported: ${supportedVersions.join(
|
|
4034
|
+
", "
|
|
4035
|
+
)}. Available: ${availableVersions.join(", ")}`,
|
|
3805
4036
|
hostname
|
|
3806
4037
|
);
|
|
3807
4038
|
this.availableVersions = availableVersions;
|
|
3808
4039
|
this.supportedRange = supportedRange;
|
|
4040
|
+
this.supportedVersions = supportedVersions;
|
|
3809
4041
|
}
|
|
3810
4042
|
};
|
|
3811
4043
|
var InvalidVersionResponseError = class extends VersionDiscoveryError {
|
|
@@ -4224,8 +4456,8 @@ function selectRepresentativeFailure(failures) {
|
|
|
4224
4456
|
}
|
|
4225
4457
|
var CLIENT_BY_VERSION_KEY = {
|
|
4226
4458
|
"25.10": TrueNasApiClientV2510,
|
|
4227
|
-
"
|
|
4228
|
-
"
|
|
4459
|
+
"27": TrueNasApiClientV27,
|
|
4460
|
+
"28": TrueNasApiClientV28
|
|
4229
4461
|
};
|
|
4230
4462
|
function clientVersionKey(version) {
|
|
4231
4463
|
if (version.year <= legacyCutoffYear) {
|
|
@@ -4292,8 +4524,9 @@ exports.NoCompatibleVersionsError = NoCompatibleVersionsError;
|
|
|
4292
4524
|
exports.SUPPORTED_API_VERSIONS = SUPPORTED_API_VERSIONS;
|
|
4293
4525
|
exports.TrueNasApiClient = TrueNasApiClient;
|
|
4294
4526
|
exports.TrueNasApiClientV2510 = TrueNasApiClientV2510;
|
|
4295
|
-
exports.TrueNasApiClientV26 = TrueNasApiClientV26;
|
|
4296
4527
|
exports.TrueNasApiClientV27 = TrueNasApiClientV27;
|
|
4528
|
+
exports.TrueNasApiClientV28 = TrueNasApiClientV28;
|
|
4529
|
+
exports.TrueNasApiError = TrueNasApiError;
|
|
4297
4530
|
exports.TrueNasAuthMechanism = TrueNasAuthMechanism;
|
|
4298
4531
|
exports.TrueNasAuthenticator = TrueNasAuthenticator;
|
|
4299
4532
|
exports.TrueNasConnection = TrueNasConnection;
|
|
@@ -4315,13 +4548,14 @@ exports.isJobFinished = isJobFinished;
|
|
|
4315
4548
|
exports.noopLogger = noopLogger;
|
|
4316
4549
|
exports.parseApiVersion = parseApiVersion;
|
|
4317
4550
|
exports.policyViolationCloseCode = policyViolationCloseCode;
|
|
4551
|
+
exports.subscriptionName = subscriptionName;
|
|
4318
4552
|
exports.v25_10_0_exports = v25_10_0_exports;
|
|
4319
4553
|
exports.v25_10_1_exports = v25_10_1_exports;
|
|
4320
4554
|
exports.v25_10_2_exports = v25_10_2_exports;
|
|
4321
4555
|
exports.v25_10_3_exports = v25_10_3_exports;
|
|
4322
4556
|
exports.v25_10_4_exports = v25_10_4_exports;
|
|
4323
4557
|
exports.v25_10_5_exports = v25_10_5_exports;
|
|
4324
|
-
exports.v26_0_0_exports = v26_0_0_exports;
|
|
4325
4558
|
exports.v27_0_0_exports = v27_0_0_exports;
|
|
4326
|
-
|
|
4327
|
-
//# sourceMappingURL=chunk-
|
|
4559
|
+
exports.v28_0_0_exports = v28_0_0_exports;
|
|
4560
|
+
//# sourceMappingURL=chunk-U6V2Y3AE.cjs.map
|
|
4561
|
+
//# sourceMappingURL=chunk-U6V2Y3AE.cjs.map
|