@ratelkey/burrow-sdk 0.1.0
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 +108 -0
- package/dist/cjs/Burrow.js +30 -0
- package/dist/cjs/Burrow.js.map +1 -0
- package/dist/cjs/BurrowClient.js +110 -0
- package/dist/cjs/BurrowClient.js.map +1 -0
- package/dist/cjs/Errors.js +80 -0
- package/dist/cjs/Errors.js.map +1 -0
- package/dist/cjs/Identity.js +274 -0
- package/dist/cjs/Identity.js.map +1 -0
- package/dist/cjs/Model.js +143 -0
- package/dist/cjs/Model.js.map +1 -0
- package/dist/cjs/Signer.js +42 -0
- package/dist/cjs/Signer.js.map +1 -0
- package/dist/cjs/Tls.js +85 -0
- package/dist/cjs/Tls.js.map +1 -0
- package/dist/cjs/Transport.js +198 -0
- package/dist/cjs/Transport.js.map +1 -0
- package/dist/cjs/index.js +28 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/Burrow.d.ts +59 -0
- package/dist/esm/Burrow.d.ts.map +1 -0
- package/dist/esm/Burrow.js +27 -0
- package/dist/esm/Burrow.js.map +1 -0
- package/dist/esm/BurrowClient.d.ts +40 -0
- package/dist/esm/BurrowClient.d.ts.map +1 -0
- package/dist/esm/BurrowClient.js +106 -0
- package/dist/esm/BurrowClient.js.map +1 -0
- package/dist/esm/Errors.d.ts +57 -0
- package/dist/esm/Errors.d.ts.map +1 -0
- package/dist/esm/Errors.js +67 -0
- package/dist/esm/Errors.js.map +1 -0
- package/dist/esm/Identity.d.ts +62 -0
- package/dist/esm/Identity.d.ts.map +1 -0
- package/dist/esm/Identity.js +269 -0
- package/dist/esm/Identity.js.map +1 -0
- package/dist/esm/Model.d.ts +75 -0
- package/dist/esm/Model.d.ts.map +1 -0
- package/dist/esm/Model.js +137 -0
- package/dist/esm/Model.js.map +1 -0
- package/dist/esm/Signer.d.ts +19 -0
- package/dist/esm/Signer.d.ts.map +1 -0
- package/dist/esm/Signer.js +39 -0
- package/dist/esm/Signer.js.map +1 -0
- package/dist/esm/Tls.d.ts +71 -0
- package/dist/esm/Tls.d.ts.map +1 -0
- package/dist/esm/Tls.js +79 -0
- package/dist/esm/Tls.js.map +1 -0
- package/dist/esm/Transport.d.ts +29 -0
- package/dist/esm/Transport.d.ts.map +1 -0
- package/dist/esm/Transport.js +195 -0
- package/dist/esm/Transport.js.map +1 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/package.json +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { KeyObject } from "node:crypto";
|
|
2
|
+
import { ProjectAccess, Secret, SecretCoordinate } from "./Model.js";
|
|
3
|
+
import type { TlsMode } from "./Tls.js";
|
|
4
|
+
/**
|
|
5
|
+
* Reads secrets from a Burrow as a machine identity. Every call is independently Ed25519-signed — there is
|
|
6
|
+
* no session and no long-lived bearer token. Instances are reusable; construct one via `Burrow`.
|
|
7
|
+
*/
|
|
8
|
+
export interface BurrowClient {
|
|
9
|
+
/** The Burrow this client reads from. */
|
|
10
|
+
readonly burrowUrl: string;
|
|
11
|
+
/** The machine identity this client acts as. */
|
|
12
|
+
readonly machineId: string;
|
|
13
|
+
/**
|
|
14
|
+
* Read a secret by its `project/env/NAME` coordinate, whole, in one signed call. Project the result
|
|
15
|
+
* locally with `Secret.value`, `Secret.toMap()`, `Secret.getField()`, or `Secret.getFields()` — the
|
|
16
|
+
* projections don't re-fetch.
|
|
17
|
+
*/
|
|
18
|
+
getSecret(coordinate: string): Promise<Secret>;
|
|
19
|
+
/** Every secret coordinate this machine can read, resolved server-side. */
|
|
20
|
+
listSecrets(): Promise<SecretCoordinate[]>;
|
|
21
|
+
/** Every project this machine can read, and the environment categories it holds in each. */
|
|
22
|
+
listProjects(): Promise<ProjectAccess[]>;
|
|
23
|
+
}
|
|
24
|
+
/** The default {@link BurrowClient}: signs each request and talks to the Burrow over the pinned transport. */
|
|
25
|
+
export declare class DefaultBurrowClient implements BurrowClient {
|
|
26
|
+
readonly burrowUrl: string;
|
|
27
|
+
readonly machineId: string;
|
|
28
|
+
private readonly key;
|
|
29
|
+
private readonly clientTag;
|
|
30
|
+
private readonly now;
|
|
31
|
+
private readonly transport;
|
|
32
|
+
constructor(burrowUrl: string, machineId: string, key: KeyObject, clientTag: string, tls: TlsMode,
|
|
33
|
+
/** Where this Burrow's pinned key is kept — its identity directory. Null for a caller that has none. */
|
|
34
|
+
pinStoreDir: string | null, now?: () => number);
|
|
35
|
+
getSecret(coordinate: string): Promise<Secret>;
|
|
36
|
+
listSecrets(): Promise<SecretCoordinate[]>;
|
|
37
|
+
listProjects(): Promise<ProjectAccess[]>;
|
|
38
|
+
private signedGet;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=BurrowClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BurrowClient.d.ts","sourceRoot":"","sources":["../../src/BurrowClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,EAAoB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEvF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGxC;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;;;OAIG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE/C,2EAA2E;IAC3E,WAAW,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAE3C,4FAA4F;IAC5F,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;CAC1C;AAED,8GAA8G;AAC9G,qBAAa,mBAAoB,YAAW,YAAY;IAIpD,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAI1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IAVtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAG3B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACT,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,MAAM,EAClC,GAAG,EAAE,OAAO;IACZ,wGAAwG;IACxG,WAAW,EAAE,MAAM,GAAG,IAAI,EACT,GAAG,GAAE,MAAM,MAAiB;IAKzC,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAa9C,WAAW,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAQ1C,YAAY,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAe9C,OAAO,CAAC,SAAS;CAWlB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { BurrowTransportException } from "./Errors.js";
|
|
2
|
+
import { ProjectAccess, Secret, SecretCoordinate } from "./Model.js";
|
|
3
|
+
import { MachineSigner } from "./Signer.js";
|
|
4
|
+
import { Transport } from "./Transport.js";
|
|
5
|
+
/** The default {@link BurrowClient}: signs each request and talks to the Burrow over the pinned transport. */
|
|
6
|
+
export class DefaultBurrowClient {
|
|
7
|
+
burrowUrl;
|
|
8
|
+
machineId;
|
|
9
|
+
key;
|
|
10
|
+
clientTag;
|
|
11
|
+
now;
|
|
12
|
+
transport;
|
|
13
|
+
constructor(burrowUrl, machineId, key, clientTag, tls,
|
|
14
|
+
/** Where this Burrow's pinned key is kept — its identity directory. Null for a caller that has none. */
|
|
15
|
+
pinStoreDir, now = Date.now) {
|
|
16
|
+
this.burrowUrl = burrowUrl;
|
|
17
|
+
this.machineId = machineId;
|
|
18
|
+
this.key = key;
|
|
19
|
+
this.clientTag = clientTag;
|
|
20
|
+
this.now = now;
|
|
21
|
+
this.transport = new Transport(burrowUrl, tls, pinStoreDir);
|
|
22
|
+
}
|
|
23
|
+
async getSecret(coordinate) {
|
|
24
|
+
const c = SecretCoordinate.parse(coordinate);
|
|
25
|
+
if (c.project.length === 0 || c.environment.length === 0 || c.name.length === 0) {
|
|
26
|
+
throw new TypeError(`secret coordinate must be 'project/environment/NAME', got '${coordinate}'`);
|
|
27
|
+
}
|
|
28
|
+
const body = await this.signedGet("secret.read", [c.project, c.environment, c.name], `/v1/machine/secrets/${encode(c.project)}/${encode(c.environment)}/${encode(c.name)}`);
|
|
29
|
+
return parseSecret(body);
|
|
30
|
+
}
|
|
31
|
+
async listSecrets() {
|
|
32
|
+
const body = await this.signedGet("secrets.list", [], "/v1/machine/secrets");
|
|
33
|
+
const dto = decode(body);
|
|
34
|
+
const secrets = dto["secrets"];
|
|
35
|
+
if (!Array.isArray(secrets))
|
|
36
|
+
return [];
|
|
37
|
+
return secrets.filter((s) => typeof s === "string").map((s) => SecretCoordinate.parse(s));
|
|
38
|
+
}
|
|
39
|
+
async listProjects() {
|
|
40
|
+
const body = await this.signedGet("projects.list", [], "/v1/machine/projects");
|
|
41
|
+
const dto = decode(body);
|
|
42
|
+
const projects = dto["projects"];
|
|
43
|
+
if (!Array.isArray(projects))
|
|
44
|
+
return [];
|
|
45
|
+
return projects.map((p) => {
|
|
46
|
+
const o = (p ?? {});
|
|
47
|
+
const categories = o["categories"];
|
|
48
|
+
return new ProjectAccess(typeof o["project"] === "string" ? o["project"] : "", Array.isArray(categories) ? categories.filter((c) => typeof c === "string") : []);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
signedGet(action, resource, path) {
|
|
52
|
+
const headers = MachineSigner.sign(this.machineId, this.key, action, resource, this.clientTag, this.now());
|
|
53
|
+
return this.transport.get(path, headers);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The read response. `value`/`version` are present for a single value, `fields` for a whole structured
|
|
58
|
+
* secret. Mirrors the Burrow's MachineSecretView / MachineStructuredView; unknown keys are ignored.
|
|
59
|
+
*/
|
|
60
|
+
function parseSecret(body) {
|
|
61
|
+
const dto = decode(body);
|
|
62
|
+
const name = typeof dto["name"] === "string" ? dto["name"] : "";
|
|
63
|
+
const rawFields = dto["fields"];
|
|
64
|
+
if (Array.isArray(rawFields)) {
|
|
65
|
+
const fields = new Map();
|
|
66
|
+
for (const raw of rawFields) {
|
|
67
|
+
const f = (raw ?? {});
|
|
68
|
+
const fieldName = typeof f["name"] === "string" ? f["name"] : "";
|
|
69
|
+
fields.set(fieldName, {
|
|
70
|
+
name: fieldName,
|
|
71
|
+
value: typeof f["value"] === "string" ? f["value"] : "",
|
|
72
|
+
type: typeof f["type"] === "string" ? f["type"] : "",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return Secret.structured(name, fields);
|
|
76
|
+
}
|
|
77
|
+
return Secret.single(name, typeof dto["value"] === "string" ? dto["value"] : "", typeof dto["type"] === "string" ? dto["type"] : "", typeof dto["version"] === "number" ? dto["version"] : 0);
|
|
78
|
+
}
|
|
79
|
+
function decode(body) {
|
|
80
|
+
let parsed;
|
|
81
|
+
try {
|
|
82
|
+
parsed = JSON.parse(body);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
throw new BurrowTransportException(`decoding burrow response: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
86
|
+
}
|
|
87
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
88
|
+
throw new BurrowTransportException("decoding burrow response: expected a JSON object");
|
|
89
|
+
}
|
|
90
|
+
return parsed;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Percent-encode one URL path segment (RFC 3986 unreserved set), so a coordinate reaches the Burrow intact
|
|
94
|
+
* and decodes back to the exact value the signature covers. `encodeURIComponent` leaves `!'()*` unescaped,
|
|
95
|
+
* which is why this is spelled out.
|
|
96
|
+
*/
|
|
97
|
+
function encode(s) {
|
|
98
|
+
let out = "";
|
|
99
|
+
for (const b of Buffer.from(s, "utf8")) {
|
|
100
|
+
const ch = String.fromCharCode(b);
|
|
101
|
+
out += UNRESERVED.test(ch) ? ch : `%${b.toString(16).toUpperCase().padStart(2, "0")}`;
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
const UNRESERVED = /^[A-Za-z0-9\-._~]$/;
|
|
106
|
+
//# sourceMappingURL=BurrowClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BurrowClient.js","sourceRoot":"","sources":["../../src/BurrowClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base type for every error the SDK raises. Catch this to handle any Burrow failure uniformly.
|
|
3
|
+
*
|
|
4
|
+
* The names mirror the Kotlin SDK's exception hierarchy one-for-one, so a failure means the same thing and
|
|
5
|
+
* is documented the same way in every language we ship.
|
|
6
|
+
*/
|
|
7
|
+
export declare class BurrowException extends Error {
|
|
8
|
+
constructor(message: string, options?: {
|
|
9
|
+
cause?: unknown;
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The Burrow rejected the signed request (HTTP 401). Every authentication failure collapses to one reason:
|
|
14
|
+
* an unknown or disabled machine, a bad signature, a replayed nonce, or a clock skew past the freshness
|
|
15
|
+
* window. The message is the Burrow's own verbatim reason.
|
|
16
|
+
*/
|
|
17
|
+
export declare class BurrowAuthException extends BurrowException {
|
|
18
|
+
}
|
|
19
|
+
/** The machine authenticated but holds no grant for the requested project/environment (HTTP 403). */
|
|
20
|
+
export declare class BurrowForbiddenException extends BurrowException {
|
|
21
|
+
}
|
|
22
|
+
/** The requested project, environment, secret, or field does not exist (HTTP 404). */
|
|
23
|
+
export declare class BurrowNotFoundException extends BurrowException {
|
|
24
|
+
}
|
|
25
|
+
/** The Burrow failed to serve the request (HTTP 5xx) — e.g. server-side decryption failed. */
|
|
26
|
+
export declare class BurrowServerException extends BurrowException {
|
|
27
|
+
readonly status: number;
|
|
28
|
+
constructor(status: number, message: string);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The Burrow's TLS certificate did not match the pinned one. Either the Burrow's certificate was re-issued
|
|
32
|
+
* (remove its entry from the pin store and retry) or the connection is being intercepted.
|
|
33
|
+
*/
|
|
34
|
+
export declare class BurrowCertificateException extends BurrowException {
|
|
35
|
+
}
|
|
36
|
+
/** The Burrow could not be reached, or its response could not be parsed. */
|
|
37
|
+
export declare class BurrowTransportException extends BurrowException {
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The machine identity could not be resolved or loaded: no identity on this host, an ambiguous selector, a
|
|
41
|
+
* missing or invalid private key, or a malformed identity file.
|
|
42
|
+
*/
|
|
43
|
+
export declare class BurrowIdentityException extends BurrowException {
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A projection didn't match the secret's shape — `Secret.value` on a structured secret, or
|
|
47
|
+
* `Secret.toMap()` / `Secret.getField()` / `Secret.getFields()` on a single-value one.
|
|
48
|
+
*/
|
|
49
|
+
export declare class SecretShapeException extends BurrowException {
|
|
50
|
+
}
|
|
51
|
+
/** A requested field is not present in a structured secret. */
|
|
52
|
+
export declare class FieldNotFoundException extends BurrowException {
|
|
53
|
+
readonly secret: string;
|
|
54
|
+
readonly field: string;
|
|
55
|
+
constructor(secret: string, field: string, available: Iterable<string>);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=Errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Errors.d.ts","sourceRoot":"","sources":["../../src/Errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAM3D;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,eAAe;CAAG;AAE3D,qGAAqG;AACrG,qBAAa,wBAAyB,SAAQ,eAAe;CAAG;AAEhE,sFAAsF;AACtF,qBAAa,uBAAwB,SAAQ,eAAe;CAAG;AAE/D,8FAA8F;AAC9F,qBAAa,qBAAsB,SAAQ,eAAe;IAEtD,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM;CAIlB;AAED;;;GAGG;AACH,qBAAa,0BAA2B,SAAQ,eAAe;CAAG;AAElE,4EAA4E;AAC5E,qBAAa,wBAAyB,SAAQ,eAAe;CAAG;AAEhE;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,eAAe;CAAG;AAE/D;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,eAAe;CAAG;AAE5D,+DAA+D;AAC/D,qBAAa,sBAAuB,SAAQ,eAAe;IAEvD,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM;gBADb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACtB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC;CAI9B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base type for every error the SDK raises. Catch this to handle any Burrow failure uniformly.
|
|
3
|
+
*
|
|
4
|
+
* The names mirror the Kotlin SDK's exception hierarchy one-for-one, so a failure means the same thing and
|
|
5
|
+
* is documented the same way in every language we ship.
|
|
6
|
+
*/
|
|
7
|
+
export class BurrowException extends Error {
|
|
8
|
+
constructor(message, options) {
|
|
9
|
+
super(message, options);
|
|
10
|
+
// `new.target` is the concrete subclass, so every error reports its own name without each subclass
|
|
11
|
+
// having to restate it.
|
|
12
|
+
this.name = new.target.name;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The Burrow rejected the signed request (HTTP 401). Every authentication failure collapses to one reason:
|
|
17
|
+
* an unknown or disabled machine, a bad signature, a replayed nonce, or a clock skew past the freshness
|
|
18
|
+
* window. The message is the Burrow's own verbatim reason.
|
|
19
|
+
*/
|
|
20
|
+
export class BurrowAuthException extends BurrowException {
|
|
21
|
+
}
|
|
22
|
+
/** The machine authenticated but holds no grant for the requested project/environment (HTTP 403). */
|
|
23
|
+
export class BurrowForbiddenException extends BurrowException {
|
|
24
|
+
}
|
|
25
|
+
/** The requested project, environment, secret, or field does not exist (HTTP 404). */
|
|
26
|
+
export class BurrowNotFoundException extends BurrowException {
|
|
27
|
+
}
|
|
28
|
+
/** The Burrow failed to serve the request (HTTP 5xx) — e.g. server-side decryption failed. */
|
|
29
|
+
export class BurrowServerException extends BurrowException {
|
|
30
|
+
status;
|
|
31
|
+
constructor(status, message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.status = status;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The Burrow's TLS certificate did not match the pinned one. Either the Burrow's certificate was re-issued
|
|
38
|
+
* (remove its entry from the pin store and retry) or the connection is being intercepted.
|
|
39
|
+
*/
|
|
40
|
+
export class BurrowCertificateException extends BurrowException {
|
|
41
|
+
}
|
|
42
|
+
/** The Burrow could not be reached, or its response could not be parsed. */
|
|
43
|
+
export class BurrowTransportException extends BurrowException {
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The machine identity could not be resolved or loaded: no identity on this host, an ambiguous selector, a
|
|
47
|
+
* missing or invalid private key, or a malformed identity file.
|
|
48
|
+
*/
|
|
49
|
+
export class BurrowIdentityException extends BurrowException {
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A projection didn't match the secret's shape — `Secret.value` on a structured secret, or
|
|
53
|
+
* `Secret.toMap()` / `Secret.getField()` / `Secret.getFields()` on a single-value one.
|
|
54
|
+
*/
|
|
55
|
+
export class SecretShapeException extends BurrowException {
|
|
56
|
+
}
|
|
57
|
+
/** A requested field is not present in a structured secret. */
|
|
58
|
+
export class FieldNotFoundException extends BurrowException {
|
|
59
|
+
secret;
|
|
60
|
+
field;
|
|
61
|
+
constructor(secret, field, available) {
|
|
62
|
+
super(`no field '${field}' in '${secret}' (has: ${[...available].sort().join(", ")})`);
|
|
63
|
+
this.secret = secret;
|
|
64
|
+
this.field = field;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=Errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Errors.js","sourceRoot":"","sources":["../../src/Errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACxC,YAAY,OAAe,EAAE,OAA6B;QACxD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,mGAAmG;QACnG,wBAAwB;QACxB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;IAC9B,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,eAAe;CAAG;AAE3D,qGAAqG;AACrG,MAAM,OAAO,wBAAyB,SAAQ,eAAe;CAAG;AAEhE,sFAAsF;AACtF,MAAM,OAAO,uBAAwB,SAAQ,eAAe;CAAG;AAE/D,8FAA8F;AAC9F,MAAM,OAAO,qBAAsB,SAAQ,eAAe;IAE7C;IADX,YACW,MAAc,EACvB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,WAAM,GAAN,MAAM,CAAQ;IAIzB,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,0BAA2B,SAAQ,eAAe;CAAG;AAElE,4EAA4E;AAC5E,MAAM,OAAO,wBAAyB,SAAQ,eAAe;CAAG;AAEhE;;;GAGG;AACH,MAAM,OAAO,uBAAwB,SAAQ,eAAe;CAAG;AAE/D;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,eAAe;CAAG;AAE5D,+DAA+D;AAC/D,MAAM,OAAO,sBAAuB,SAAQ,eAAe;IAE9C;IACA;IAFX,YACW,MAAc,EACd,KAAa,EACtB,SAA2B;QAE3B,KAAK,CAAC,aAAa,KAAK,SAAS,MAAM,WAAW,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAJ9E,WAAM,GAAN,MAAM,CAAQ;QACd,UAAK,GAAL,KAAK,CAAQ;IAIxB,CAAC;CACF"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type KeyObject } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* A machine identity for one Burrow — the record the `curl | sh` bootstrap writes to
|
|
4
|
+
* `~/.burrow/burrows/<slug>/identity.json`, plus a reference to the Ed25519 private key it generated. One
|
|
5
|
+
* identity per Burrow; a client acts as exactly one of them.
|
|
6
|
+
*
|
|
7
|
+
* The private key never leaves the machine: it stays in the referenced PEM file and only signs requests.
|
|
8
|
+
*/
|
|
9
|
+
export declare class MachineIdentity {
|
|
10
|
+
readonly burrowUrl: string;
|
|
11
|
+
readonly machineId: string;
|
|
12
|
+
readonly privateKeyPath: string;
|
|
13
|
+
readonly slug: string;
|
|
14
|
+
readonly burrowName: string;
|
|
15
|
+
readonly name: string;
|
|
16
|
+
constructor(burrowUrl: string, machineId: string, privateKeyPath: string, slug?: string, burrowName?: string, name?: string);
|
|
17
|
+
/** The Burrow's shown label, falling back to slug, then the URL host. */
|
|
18
|
+
get label(): string;
|
|
19
|
+
/**
|
|
20
|
+
* The directory this identity lives in — `<home>/burrows/<slug>/`, where its pinned key sits beside it.
|
|
21
|
+
*
|
|
22
|
+
* Derived from `privateKeyPath` rather than recorded, because identity.json does not carry it: the
|
|
23
|
+
* bootstrap writes the key as `<dir>/identity.pem`, so the parent IS the directory.
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
get directory(): string;
|
|
28
|
+
/** @internal */
|
|
29
|
+
matches(selector: string): boolean;
|
|
30
|
+
/** Load and parse the Ed25519 private key (PKCS#8 PEM) this identity references. */
|
|
31
|
+
loadPrivateKey(): KeyObject;
|
|
32
|
+
/**
|
|
33
|
+
* Read one identity record. The three fields the bootstrap always writes are required; the rest are
|
|
34
|
+
* display metadata that a hand-written file may omit.
|
|
35
|
+
*/
|
|
36
|
+
/** @internal */
|
|
37
|
+
static fromJson(raw: unknown): MachineIdentity;
|
|
38
|
+
}
|
|
39
|
+
/** Discovers and resolves the machine identities the bootstrap writes under `~/.burrow`. */
|
|
40
|
+
export declare const Identities: {
|
|
41
|
+
/** The identity root: `$BURROW_HOME` if set, else `~/.burrow`. */
|
|
42
|
+
home(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Every identity under `<home>/burrows/<slug>/identity.json`, sorted by label then machine id.
|
|
45
|
+
* Directories without a readable, well-formed identity.json are skipped.
|
|
46
|
+
*/
|
|
47
|
+
discover(home?: string): MachineIdentity[];
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the identity a client should act as. A single identity on file resolves automatically; with
|
|
50
|
+
* several, `selector` (a burrow name, slug, machine id, or URL) must disambiguate.
|
|
51
|
+
*/
|
|
52
|
+
resolve(selector?: string | null, home?: string): MachineIdentity;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve `target` to an identity for `Burrow`: null/undefined → the single identity (`resolve`); a
|
|
55
|
+
* filesystem path (a burrow directory or an identity.json) → load it directly; anything else → a selector
|
|
56
|
+
* (`resolve`). A URL (contains `://`) is always treated as a selector, never a path.
|
|
57
|
+
*/
|
|
58
|
+
select(target?: string | null, home?: string): MachineIdentity;
|
|
59
|
+
};
|
|
60
|
+
/** Parse an Ed25519 private key from a PKCS#8 PEM string (what `openssl genpkey -algorithm ed25519` writes). */
|
|
61
|
+
export declare function parseEd25519PrivateKey(pem: string, source: string): KeyObject;
|
|
62
|
+
//# sourceMappingURL=Identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Identity.d.ts","sourceRoot":"","sources":["../../src/Identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAM/D;;;;;;GAMG;AACH,qBAAa,eAAe;IAExB,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM;gBALZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,IAAI,GAAE,MAAW,EACjB,UAAU,GAAE,MAAW,EACvB,IAAI,GAAE,MAAW;IAG5B,yEAAyE;IACzE,IAAI,KAAK,IAAI,MAAM,CAIlB;IAED;;;;;;;OAOG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,gBAAgB;IAChB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWlC,oFAAoF;IACpF,cAAc,IAAI,SAAS;IAa3B;;;OAGG;IACH,gBAAgB;IAChB,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe;CAc/C;AAED,4FAA4F;AAC5F,eAAO,MAAM,UAAU;IACrB,kEAAkE;YAC1D,MAAM;IAMd;;;OAGG;oBACY,MAAM,GAAuB,eAAe,EAAE;IAyB7D;;;OAGG;uBACgB,MAAM,GAAG,IAAI,SAAQ,MAAM,GAAuB,eAAe;IA6BpF;;;;OAIG;oBACa,MAAM,GAAG,IAAI,SAAQ,MAAM,GAAuB,eAAe;CAOlF,CAAC;AAEF,gHAAgH;AAChH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAmB7E"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { createPrivateKey } from "node:crypto";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
5
|
+
import { BurrowIdentityException } from "./Errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* A machine identity for one Burrow — the record the `curl | sh` bootstrap writes to
|
|
8
|
+
* `~/.burrow/burrows/<slug>/identity.json`, plus a reference to the Ed25519 private key it generated. One
|
|
9
|
+
* identity per Burrow; a client acts as exactly one of them.
|
|
10
|
+
*
|
|
11
|
+
* The private key never leaves the machine: it stays in the referenced PEM file and only signs requests.
|
|
12
|
+
*/
|
|
13
|
+
export class MachineIdentity {
|
|
14
|
+
burrowUrl;
|
|
15
|
+
machineId;
|
|
16
|
+
privateKeyPath;
|
|
17
|
+
slug;
|
|
18
|
+
burrowName;
|
|
19
|
+
name;
|
|
20
|
+
constructor(burrowUrl, machineId, privateKeyPath, slug = "", burrowName = "", name = "") {
|
|
21
|
+
this.burrowUrl = burrowUrl;
|
|
22
|
+
this.machineId = machineId;
|
|
23
|
+
this.privateKeyPath = privateKeyPath;
|
|
24
|
+
this.slug = slug;
|
|
25
|
+
this.burrowName = burrowName;
|
|
26
|
+
this.name = name;
|
|
27
|
+
}
|
|
28
|
+
/** The Burrow's shown label, falling back to slug, then the URL host. */
|
|
29
|
+
get label() {
|
|
30
|
+
return (blankTo(this.burrowName, blankTo(this.slug, blankTo(hostOf(this.burrowUrl), "(unnamed burrow)"))));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The directory this identity lives in — `<home>/burrows/<slug>/`, where its pinned key sits beside it.
|
|
34
|
+
*
|
|
35
|
+
* Derived from `privateKeyPath` rather than recorded, because identity.json does not carry it: the
|
|
36
|
+
* bootstrap writes the key as `<dir>/identity.pem`, so the parent IS the directory.
|
|
37
|
+
*
|
|
38
|
+
* @internal
|
|
39
|
+
*/
|
|
40
|
+
get directory() {
|
|
41
|
+
return dirname(this.privateKeyPath);
|
|
42
|
+
}
|
|
43
|
+
/** @internal */
|
|
44
|
+
matches(selector) {
|
|
45
|
+
const s = selector.trim();
|
|
46
|
+
return (eq(s, this.burrowName) ||
|
|
47
|
+
eq(s, this.slug) ||
|
|
48
|
+
eq(s, this.machineId) ||
|
|
49
|
+
eq(s, this.burrowUrl) ||
|
|
50
|
+
eq(s, this.label));
|
|
51
|
+
}
|
|
52
|
+
/** Load and parse the Ed25519 private key (PKCS#8 PEM) this identity references. */
|
|
53
|
+
loadPrivateKey() {
|
|
54
|
+
let pem;
|
|
55
|
+
try {
|
|
56
|
+
pem = readFileSync(this.privateKeyPath, "utf8");
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
throw new BurrowIdentityException(`reading private key ${this.privateKeyPath}: ${messageOf(e)}`, { cause: e });
|
|
60
|
+
}
|
|
61
|
+
return parseEd25519PrivateKey(pem, this.privateKeyPath);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Read one identity record. The three fields the bootstrap always writes are required; the rest are
|
|
65
|
+
* display metadata that a hand-written file may omit.
|
|
66
|
+
*/
|
|
67
|
+
/** @internal */
|
|
68
|
+
static fromJson(raw) {
|
|
69
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
70
|
+
throw new BurrowIdentityException("identity is not a JSON object");
|
|
71
|
+
}
|
|
72
|
+
const o = raw;
|
|
73
|
+
return new MachineIdentity(required(o, "burrowUrl"), required(o, "machineId"), required(o, "privateKeyPath"), optional(o, "slug"), optional(o, "burrowName"), optional(o, "name"));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Discovers and resolves the machine identities the bootstrap writes under `~/.burrow`. */
|
|
77
|
+
export const Identities = {
|
|
78
|
+
/** The identity root: `$BURROW_HOME` if set, else `~/.burrow`. */
|
|
79
|
+
home() {
|
|
80
|
+
const env = process.env["BURROW_HOME"];
|
|
81
|
+
if (env !== undefined && env.trim().length > 0)
|
|
82
|
+
return env;
|
|
83
|
+
return join(homedir(), ".burrow");
|
|
84
|
+
},
|
|
85
|
+
/**
|
|
86
|
+
* Every identity under `<home>/burrows/<slug>/identity.json`, sorted by label then machine id.
|
|
87
|
+
* Directories without a readable, well-formed identity.json are skipped.
|
|
88
|
+
*/
|
|
89
|
+
discover(home = Identities.home()) {
|
|
90
|
+
const burrows = join(home, "burrows");
|
|
91
|
+
if (!isDirectory(burrows))
|
|
92
|
+
return [];
|
|
93
|
+
let entries;
|
|
94
|
+
try {
|
|
95
|
+
entries = readdirSync(burrows);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const found = [];
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
const dir = join(burrows, entry);
|
|
103
|
+
if (!isDirectory(dir))
|
|
104
|
+
continue;
|
|
105
|
+
const file = join(dir, "identity.json");
|
|
106
|
+
if (!existsSync(file))
|
|
107
|
+
continue;
|
|
108
|
+
try {
|
|
109
|
+
found.push(MachineIdentity.fromJson(JSON.parse(readFileSync(file, "utf8"))));
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// A half-written or foreign file is not this host's identity; skip it rather than fail discovery.
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Ordinal, not locale-aware: the order has to be the same on every host that reads the same identities.
|
|
116
|
+
return found.sort((a, b) => ordinal(a.label.toLowerCase(), b.label.toLowerCase()) || ordinal(a.machineId, b.machineId));
|
|
117
|
+
},
|
|
118
|
+
/**
|
|
119
|
+
* Resolve the identity a client should act as. A single identity on file resolves automatically; with
|
|
120
|
+
* several, `selector` (a burrow name, slug, machine id, or URL) must disambiguate.
|
|
121
|
+
*/
|
|
122
|
+
resolve(selector, home = Identities.home()) {
|
|
123
|
+
const ids = Identities.discover(home);
|
|
124
|
+
if (ids.length === 0) {
|
|
125
|
+
throw new BurrowIdentityException(`no Burrow identities under ${join(home, "burrows")}. Bootstrap this machine into a Burrow ` +
|
|
126
|
+
"first (its Machines page, Add machine).");
|
|
127
|
+
}
|
|
128
|
+
if (selector === undefined || selector === null || selector.trim().length === 0) {
|
|
129
|
+
const only = ids[0];
|
|
130
|
+
if (ids.length === 1 && only !== undefined)
|
|
131
|
+
return only;
|
|
132
|
+
throw new BurrowIdentityException(`this machine holds ${ids.length} Burrow identities; select one by name, slug, or machine id. ` +
|
|
133
|
+
`Known: ${ids.map((i) => i.label).join(", ")}`);
|
|
134
|
+
}
|
|
135
|
+
const matched = ids.filter((i) => i.matches(selector));
|
|
136
|
+
const first = matched[0];
|
|
137
|
+
if (matched.length === 1 && first !== undefined)
|
|
138
|
+
return first;
|
|
139
|
+
if (matched.length === 0) {
|
|
140
|
+
throw new BurrowIdentityException(`no Burrow identity matches "${selector}". Known: ${ids.map((i) => i.label).join(", ")}`);
|
|
141
|
+
}
|
|
142
|
+
throw new BurrowIdentityException(`"${selector}" matches more than one identity; select by machine id.`);
|
|
143
|
+
},
|
|
144
|
+
/**
|
|
145
|
+
* Resolve `target` to an identity for `Burrow`: null/undefined → the single identity (`resolve`); a
|
|
146
|
+
* filesystem path (a burrow directory or an identity.json) → load it directly; anything else → a selector
|
|
147
|
+
* (`resolve`). A URL (contains `://`) is always treated as a selector, never a path.
|
|
148
|
+
*/
|
|
149
|
+
select(target, home = Identities.home()) {
|
|
150
|
+
if (target === undefined || target === null || target.trim().length === 0) {
|
|
151
|
+
return Identities.resolve(null, home);
|
|
152
|
+
}
|
|
153
|
+
if (!target.includes("://") && looksLikePath(target))
|
|
154
|
+
return loadFromPath(expandUser(target));
|
|
155
|
+
return Identities.resolve(target, home);
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
/** Parse an Ed25519 private key from a PKCS#8 PEM string (what `openssl genpkey -algorithm ed25519` writes). */
|
|
159
|
+
export function parseEd25519PrivateKey(pem, source) {
|
|
160
|
+
const der = pemBody(pem);
|
|
161
|
+
if (der === null)
|
|
162
|
+
throw new BurrowIdentityException(`no PEM private-key block in ${source}`);
|
|
163
|
+
let key;
|
|
164
|
+
try {
|
|
165
|
+
// DER + pkcs8 explicitly, so a SEC1 or PKCS#1 body fails here rather than being silently accepted.
|
|
166
|
+
key = createPrivateKey({ key: der, format: "der", type: "pkcs8" });
|
|
167
|
+
}
|
|
168
|
+
catch (e) {
|
|
169
|
+
throw new BurrowIdentityException(`parsing Ed25519 private key in ${source}: ${messageOf(e)}`, { cause: e });
|
|
170
|
+
}
|
|
171
|
+
if (key.asymmetricKeyType !== "ed25519") {
|
|
172
|
+
throw new BurrowIdentityException(`parsing Ed25519 private key in ${source}: key is ${key.asymmetricKeyType ?? "of an unknown type"}, not Ed25519`);
|
|
173
|
+
}
|
|
174
|
+
return key;
|
|
175
|
+
}
|
|
176
|
+
/** Strip the PEM armor and base64-decode the body. Null when there is no PEM block. */
|
|
177
|
+
function pemBody(pem) {
|
|
178
|
+
const begin = pem.indexOf("-----BEGIN");
|
|
179
|
+
if (begin < 0)
|
|
180
|
+
return null;
|
|
181
|
+
const afterBeginLine = pem.indexOf("\n", begin);
|
|
182
|
+
if (afterBeginLine < 0)
|
|
183
|
+
return null;
|
|
184
|
+
const end = pem.indexOf("-----END", afterBeginLine + 1);
|
|
185
|
+
if (end < 0)
|
|
186
|
+
return null;
|
|
187
|
+
const base64 = pem.slice(afterBeginLine + 1, end).replace(/\s/g, "");
|
|
188
|
+
try {
|
|
189
|
+
return Buffer.from(base64, "base64");
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/** The host[:port] of a URL, for a display-only label fallback. */
|
|
196
|
+
function hostOf(url) {
|
|
197
|
+
let s = url;
|
|
198
|
+
const scheme = s.indexOf("://");
|
|
199
|
+
if (scheme >= 0)
|
|
200
|
+
s = s.slice(scheme + 3);
|
|
201
|
+
const slash = s.indexOf("/");
|
|
202
|
+
if (slash >= 0)
|
|
203
|
+
s = s.slice(0, slash);
|
|
204
|
+
return s;
|
|
205
|
+
}
|
|
206
|
+
function looksLikePath(s) {
|
|
207
|
+
if (s.startsWith("/") || s.startsWith("~") || s.startsWith("./") || s.startsWith("../"))
|
|
208
|
+
return true;
|
|
209
|
+
if (s.endsWith(".json"))
|
|
210
|
+
return true;
|
|
211
|
+
if (isAbsolute(s))
|
|
212
|
+
return true;
|
|
213
|
+
try {
|
|
214
|
+
return existsSync(s);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function expandUser(s) {
|
|
221
|
+
if (!s.startsWith("~"))
|
|
222
|
+
return s;
|
|
223
|
+
return join(homedir(), s.slice(1).replace(/^\//, ""));
|
|
224
|
+
}
|
|
225
|
+
function loadFromPath(path) {
|
|
226
|
+
const file = isDirectory(path) ? join(path, "identity.json") : path;
|
|
227
|
+
if (!existsSync(file))
|
|
228
|
+
throw new BurrowIdentityException(`no identity file at ${file}`);
|
|
229
|
+
try {
|
|
230
|
+
return MachineIdentity.fromJson(JSON.parse(readFileSync(file, "utf8")));
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
throw new BurrowIdentityException(`failed to parse identity file ${file}: ${messageOf(e)}`, {
|
|
234
|
+
cause: e,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function isDirectory(path) {
|
|
239
|
+
try {
|
|
240
|
+
return statSync(path).isDirectory();
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function required(o, key) {
|
|
247
|
+
const v = o[key];
|
|
248
|
+
if (typeof v !== "string" || v.length === 0) {
|
|
249
|
+
throw new BurrowIdentityException(`identity is missing '${key}'`);
|
|
250
|
+
}
|
|
251
|
+
return v;
|
|
252
|
+
}
|
|
253
|
+
function optional(o, key) {
|
|
254
|
+
const v = o[key];
|
|
255
|
+
return typeof v === "string" ? v : "";
|
|
256
|
+
}
|
|
257
|
+
function blankTo(value, fallback) {
|
|
258
|
+
return value.trim().length > 0 ? value : fallback;
|
|
259
|
+
}
|
|
260
|
+
function eq(a, b) {
|
|
261
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
262
|
+
}
|
|
263
|
+
function ordinal(a, b) {
|
|
264
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
265
|
+
}
|
|
266
|
+
function messageOf(e) {
|
|
267
|
+
return e instanceof Error ? e.message : String(e);
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=Identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Identity.js","sourceRoot":"","sources":["../../src/Identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAkB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,OAAO,eAAe;IAEf;IACA;IACA;IACA;IACA;IACA;IANX,YACW,SAAiB,EACjB,SAAiB,EACjB,cAAsB,EACtB,OAAe,EAAE,EACjB,aAAqB,EAAE,EACvB,OAAe,EAAE;QALjB,cAAS,GAAT,SAAS,CAAQ;QACjB,cAAS,GAAT,SAAS,CAAQ;QACjB,mBAAc,GAAd,cAAc,CAAQ;QACtB,SAAI,GAAJ,IAAI,CAAa;QACjB,eAAU,GAAV,UAAU,CAAa;QACvB,SAAI,GAAJ,IAAI,CAAa;IACzB,CAAC;IAEJ,yEAAyE;IACzE,IAAI,KAAK;QACP,OAAO,CACL,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAClG,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACH,IAAI,SAAS;QACX,OAAO,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACtC,CAAC;IAED,gBAAgB;IAChB,OAAO,CAAC,QAAgB;QACtB,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC1B,OAAO,CACL,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC;YACtB,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;YAChB,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC;YACrB,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC;YACrB,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAClB,CAAC;IACJ,CAAC;IAED,oFAAoF;IACpF,cAAc;QACZ,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,IAAI,uBAAuB,CAC/B,uBAAuB,IAAI,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,EAC7D,EAAE,KAAK,EAAE,CAAC,EAAE,CACb,CAAC;QACJ,CAAC;QACD,OAAO,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACH,gBAAgB;IAChB,MAAM,CAAC,QAAQ,CAAC,GAAY;QAC1B,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAClE,MAAM,IAAI,uBAAuB,CAAC,+BAA+B,CAAC,CAAC;QACrE,CAAC;QACD,MAAM,CAAC,GAAG,GAA8B,CAAC;QACzC,OAAO,IAAI,eAAe,CACxB,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,EACxB,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,EACxB,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,EAC7B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,EACnB,QAAQ,CAAC,CAAC,EAAE,YAAY,CAAC,EACzB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,CACpB,CAAC;IACJ,CAAC;CACF;AAED,4FAA4F;AAC5F,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,kEAAkE;IAClE,IAAI;QACF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;QAC3D,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,OAAe,UAAU,CAAC,IAAI,EAAE;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACtC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QACrC,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,GAAsB,EAAE,CAAC;QACpC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACjC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;gBAAE,SAAS;YAChC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;YACxC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,SAAS;YAChC,IAAI,CAAC;gBACH,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/E,CAAC;YAAC,MAAM,CAAC;gBACP,kGAAkG;YACpG,CAAC;QACH,CAAC;QACD,wGAAwG;QACxG,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1H,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,QAAwB,EAAE,OAAe,UAAU,CAAC,IAAI,EAAE;QAChE,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,uBAAuB,CAC/B,8BAA8B,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,yCAAyC;gBAC1F,yCAAyC,CAC5C,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChF,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC;YACxD,MAAM,IAAI,uBAAuB,CAC/B,sBAAsB,GAAG,CAAC,MAAM,+DAA+D;gBAC7F,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACjD,CAAC;QACJ,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACzB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC9D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,uBAAuB,CAC/B,+BAA+B,QAAQ,aAAa,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,uBAAuB,CAC/B,IAAI,QAAQ,yDAAyD,CACtE,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,MAAsB,EAAE,OAAe,UAAU,CAAC,IAAI,EAAE;QAC7D,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1E,OAAO,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,MAAM,CAAC;YAAE,OAAO,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9F,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAC;AAEF,gHAAgH;AAChH,MAAM,UAAU,sBAAsB,CAAC,GAAW,EAAE,MAAc;IAChE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACzB,IAAI,GAAG,KAAK,IAAI;QAAE,MAAM,IAAI,uBAAuB,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC;IAC7F,IAAI,GAAc,CAAC;IACnB,IAAI,CAAC;QACH,mGAAmG;QACnG,GAAG,GAAG,gBAAgB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,uBAAuB,CAC/B,kCAAkC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,EAC3D,EAAE,KAAK,EAAE,CAAC,EAAE,CACb,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;QACxC,MAAM,IAAI,uBAAuB,CAC/B,kCAAkC,MAAM,YAAY,GAAG,CAAC,iBAAiB,IAAI,oBAAoB,eAAe,CACjH,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uFAAuF;AACvF,SAAS,OAAO,CAAC,GAAW;IAC1B,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACxC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAChD,IAAI,cAAc,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,cAAc,GAAG,CAAC,CAAC,CAAC;IACxD,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,SAAS,MAAM,CAAC,GAAW;IACzB,IAAI,CAAC,GAAG,GAAG,CAAC;IACZ,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,MAAM,IAAI,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,KAAK,IAAI,CAAC;QAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,aAAa,CAAC,CAAS;IAC9B,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrG,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACpE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,uBAAuB,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IACxF,IAAI,CAAC;QACH,OAAO,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,uBAAuB,CAAC,iCAAiC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE;YAC1F,KAAK,EAAE,CAAC;SACT,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,CAA0B,EAAE,GAAW;IACvD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,uBAAuB,CAAC,wBAAwB,GAAG,GAAG,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,QAAQ,CAAC,CAA0B,EAAE,GAAW;IACvD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACjB,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACxC,CAAC;AAED,SAAS,OAAO,CAAC,KAAa,EAAE,QAAgB;IAC9C,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AACpD,CAAC;AAED,SAAS,EAAE,CAAC,CAAS,EAAE,CAAS;IAC9B,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;AAC7C,CAAC;AAED,SAAS,OAAO,CAAC,CAAS,EAAE,CAAS;IACnC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,SAAS,CAAC,CAAU;IAC3B,OAAO,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC"}
|