@hardfin/cli 0.1.0-dev.25 → 0.1.0-dev.26
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/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ process.stdout.on("error", (error) => {
|
|
|
8
8
|
});
|
|
9
9
|
const answer = await toQuickAnswer(process.argv.slice(2));
|
|
10
10
|
if (answer === void 0) {
|
|
11
|
-
const { toCli } = await import("./program-
|
|
11
|
+
const { toCli } = await import("./program-C_9qi3K1.js");
|
|
12
12
|
await toCli().parseAsync(process.argv);
|
|
13
13
|
} else process.stdout.write(`${answer}\n`);
|
|
14
14
|
/**
|
|
@@ -435,6 +435,111 @@ function writeFile(held) {
|
|
|
435
435
|
renameSync(pending, path);
|
|
436
436
|
}
|
|
437
437
|
//#endregion
|
|
438
|
+
//#region src/http/reach.ts
|
|
439
|
+
/** Unreachable is a request that never reached a server, so there is no answer to report. */
|
|
440
|
+
var Unreachable = class extends Error {
|
|
441
|
+
constructor(message) {
|
|
442
|
+
super(message);
|
|
443
|
+
this.name = "Unreachable";
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
const OFFLINE = "Couldn't connect to Hardfin. Check your internet connection and try again.";
|
|
447
|
+
const INSECURE = "Couldn't connect to Hardfin securely, so nothing was sent. A work network or security software may be blocking the connection. If this keeps happening, contact your IT team.";
|
|
448
|
+
/** The reasons Node gives, by the code on the error behind fetch's own. */
|
|
449
|
+
const REASONS = {
|
|
450
|
+
ECONNREFUSED: {
|
|
451
|
+
message: OFFLINE,
|
|
452
|
+
detail: "connection refused"
|
|
453
|
+
},
|
|
454
|
+
ENOTFOUND: {
|
|
455
|
+
message: OFFLINE,
|
|
456
|
+
detail: "address not found"
|
|
457
|
+
},
|
|
458
|
+
EAI_AGAIN: {
|
|
459
|
+
message: OFFLINE,
|
|
460
|
+
detail: "address lookup failed"
|
|
461
|
+
},
|
|
462
|
+
ETIMEDOUT: {
|
|
463
|
+
message: OFFLINE,
|
|
464
|
+
detail: "timed out"
|
|
465
|
+
},
|
|
466
|
+
UND_ERR_CONNECT_TIMEOUT: {
|
|
467
|
+
message: OFFLINE,
|
|
468
|
+
detail: "timed out"
|
|
469
|
+
},
|
|
470
|
+
EHOSTUNREACH: {
|
|
471
|
+
message: OFFLINE,
|
|
472
|
+
detail: "no route to host"
|
|
473
|
+
},
|
|
474
|
+
ENETUNREACH: {
|
|
475
|
+
message: OFFLINE,
|
|
476
|
+
detail: "network unreachable"
|
|
477
|
+
},
|
|
478
|
+
ECONNRESET: {
|
|
479
|
+
message: OFFLINE,
|
|
480
|
+
detail: "connection closed"
|
|
481
|
+
},
|
|
482
|
+
UND_ERR_SOCKET: {
|
|
483
|
+
message: OFFLINE,
|
|
484
|
+
detail: "connection closed"
|
|
485
|
+
},
|
|
486
|
+
DEPTH_ZERO_SELF_SIGNED_CERT: {
|
|
487
|
+
message: INSECURE,
|
|
488
|
+
detail: "certificate not trusted"
|
|
489
|
+
},
|
|
490
|
+
SELF_SIGNED_CERT_IN_CHAIN: {
|
|
491
|
+
message: INSECURE,
|
|
492
|
+
detail: "certificate not trusted"
|
|
493
|
+
},
|
|
494
|
+
UNABLE_TO_VERIFY_LEAF_SIGNATURE: {
|
|
495
|
+
message: INSECURE,
|
|
496
|
+
detail: "certificate not trusted"
|
|
497
|
+
},
|
|
498
|
+
UNABLE_TO_GET_ISSUER_CERT_LOCALLY: {
|
|
499
|
+
message: INSECURE,
|
|
500
|
+
detail: "certificate not trusted"
|
|
501
|
+
},
|
|
502
|
+
CERT_HAS_EXPIRED: {
|
|
503
|
+
message: INSECURE,
|
|
504
|
+
detail: "certificate expired"
|
|
505
|
+
},
|
|
506
|
+
ERR_TLS_CERT_ALTNAME_INVALID: {
|
|
507
|
+
message: INSECURE,
|
|
508
|
+
detail: "certificate is for another host"
|
|
509
|
+
},
|
|
510
|
+
ERR_SSL_WRONG_VERSION_NUMBER: {
|
|
511
|
+
message: OFFLINE,
|
|
512
|
+
detail: "not an HTTPS server"
|
|
513
|
+
},
|
|
514
|
+
EPROTO: {
|
|
515
|
+
message: OFFLINE,
|
|
516
|
+
detail: "not an HTTPS server"
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
/** send is fetch, with a failure to connect reported in words anyone can act on. */
|
|
520
|
+
async function send(url, init) {
|
|
521
|
+
try {
|
|
522
|
+
return await fetch(url, init);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
throw toUnreachable(url, error);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* toUnreachable describes a request that could not connect. Fetch reports every such failure
|
|
529
|
+
* as "fetch failed", and the reason is on the error it wraps. The person reads a plain
|
|
530
|
+
* message, and the line after it names the reason and host for whoever helps them.
|
|
531
|
+
*/
|
|
532
|
+
function toUnreachable(url, error) {
|
|
533
|
+
const host = new URL(url).host;
|
|
534
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
535
|
+
const code = cause instanceof Error ? cause.code : void 0;
|
|
536
|
+
const reason = (error instanceof Error && error.name === "TimeoutError" ? REASONS["ETIMEDOUT"] : code === void 0 ? void 0 : REASONS[code]) ?? {
|
|
537
|
+
message: OFFLINE,
|
|
538
|
+
detail: cause instanceof Error ? cause.message : error instanceof Error ? error.message : String(error)
|
|
539
|
+
};
|
|
540
|
+
return new Unreachable(`${reason.message}\nDetails: ${reason.detail} (${host})`);
|
|
541
|
+
}
|
|
542
|
+
//#endregion
|
|
438
543
|
//#region src/auth/metadata.ts
|
|
439
544
|
const METADATA_PATH = "/.well-known/oauth-authorization-server";
|
|
440
545
|
const AuthorizationServerMetadata = z.looseObject({
|
|
@@ -461,12 +566,7 @@ async function toMetadata(issuer) {
|
|
|
461
566
|
const cached = toCached(issuer);
|
|
462
567
|
if (cached) return cached;
|
|
463
568
|
const url = `${issuer.replace(/\/+$/, "")}${METADATA_PATH}`;
|
|
464
|
-
|
|
465
|
-
try {
|
|
466
|
-
response = await fetch(url, { headers: { Accept: "application/json" } });
|
|
467
|
-
} catch (error) {
|
|
468
|
-
throw new DiscoveryFailure(`${url} could not be reached: ${error instanceof Error ? error.message : String(error)}`);
|
|
469
|
-
}
|
|
569
|
+
const response = await send(url, { headers: { Accept: "application/json" } });
|
|
470
570
|
if (!response.ok) throw new DiscoveryFailure(`${url} answered ${response.status}, so this host publishes no authorization server`);
|
|
471
571
|
const parsed = AuthorizationServerMetadata.safeParse(await response.json().catch(() => void 0));
|
|
472
572
|
if (!parsed.success) throw new DiscoveryFailure(`${url} does not describe an authorization server`);
|
|
@@ -543,7 +643,7 @@ async function toTokensFromRefresh(tokenUrl, clientId, refreshToken) {
|
|
|
543
643
|
}
|
|
544
644
|
/** revoke tells the server to forget a token, so signing out reaches every machine. */
|
|
545
645
|
async function revoke(revocationUrl, clientId, token) {
|
|
546
|
-
await
|
|
646
|
+
await send(revocationUrl, {
|
|
547
647
|
method: "POST",
|
|
548
648
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
549
649
|
body: new URLSearchParams({
|
|
@@ -555,7 +655,7 @@ async function revoke(revocationUrl, clientId, token) {
|
|
|
555
655
|
}
|
|
556
656
|
/** toTokens asks the token endpoint, which answers a flat RFC 6749 body, not the envelope. */
|
|
557
657
|
async function toTokens(url, form) {
|
|
558
|
-
const response = await
|
|
658
|
+
const response = await send(url, {
|
|
559
659
|
method: "POST",
|
|
560
660
|
headers: {
|
|
561
661
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -800,7 +900,7 @@ async function request(options) {
|
|
|
800
900
|
Accept: options.downloads ? "*/*" : "application/json"
|
|
801
901
|
};
|
|
802
902
|
if (options.body !== void 0 && options.form === void 0) headers["Content-Type"] = "application/json";
|
|
803
|
-
const response = await
|
|
903
|
+
const response = await send(url, {
|
|
804
904
|
method: options.method,
|
|
805
905
|
headers,
|
|
806
906
|
body: options.form ?? (options.body === void 0 ? void 0 : JSON.stringify(options.body))
|
|
@@ -1593,7 +1693,7 @@ const DeviceResponse = z.looseObject({
|
|
|
1593
1693
|
});
|
|
1594
1694
|
/** toDeviceAuthorization asks for a code a person can type on another machine. */
|
|
1595
1695
|
async function toDeviceAuthorization(endpoint, clientId, scope) {
|
|
1596
|
-
const response = await
|
|
1696
|
+
const response = await send(endpoint, {
|
|
1597
1697
|
method: "POST",
|
|
1598
1698
|
headers: {
|
|
1599
1699
|
"Content-Type": "application/x-www-form-urlencoded",
|