@push.rocks/smartacme 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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/acme/acme.classes.account.d.ts +20 -0
- package/dist_ts/acme/acme.classes.account.js +37 -0
- package/dist_ts/acme/acme.classes.challenge.d.ts +22 -0
- package/dist_ts/acme/acme.classes.challenge.js +36 -0
- package/dist_ts/acme/acme.classes.client.d.ts +72 -0
- package/dist_ts/acme/acme.classes.client.js +77 -0
- package/dist_ts/acme/acme.classes.crypto.d.ts +50 -0
- package/dist_ts/acme/acme.classes.crypto.js +180 -0
- package/dist_ts/acme/acme.classes.directory.d.ts +13 -0
- package/dist_ts/acme/acme.classes.directory.js +14 -0
- package/dist_ts/acme/acme.classes.error.d.ts +44 -0
- package/dist_ts/acme/acme.classes.error.js +41 -0
- package/dist_ts/acme/acme.classes.http-client.d.ts +42 -0
- package/dist_ts/acme/acme.classes.http-client.js +211 -0
- package/dist_ts/acme/acme.classes.order.d.ts +40 -0
- package/dist_ts/acme/acme.classes.order.js +100 -0
- package/dist_ts/acme/acme.interfaces.d.ts +64 -0
- package/dist_ts/acme/acme.interfaces.js +5 -0
- package/dist_ts/acme/index.d.ts +5 -0
- package/dist_ts/acme/index.js +5 -0
- package/dist_ts/certmanagers/mongo.js +1 -2
- package/dist_ts/plugins.d.ts +2 -7
- package/dist_ts/plugins.js +5 -11
- package/dist_ts/smartacme.classes.smartacme.d.ts +3 -1
- package/dist_ts/smartacme.classes.smartacme.js +48 -13
- package/npmextra.json +12 -6
- package/package.json +23 -24
- package/readme.hints.md +27 -3
- package/readme.md +245 -267
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/acme/acme.classes.account.ts +45 -0
- package/ts/acme/acme.classes.challenge.ts +45 -0
- package/ts/acme/acme.classes.client.ts +106 -0
- package/ts/acme/acme.classes.crypto.ts +220 -0
- package/ts/acme/acme.classes.directory.ts +13 -0
- package/ts/acme/acme.classes.error.ts +55 -0
- package/ts/acme/acme.classes.http-client.ts +249 -0
- package/ts/acme/acme.classes.order.ts +125 -0
- package/ts/acme/acme.interfaces.ts +74 -0
- package/ts/acme/index.ts +16 -0
- package/ts/certmanagers/mongo.ts +0 -1
- package/ts/plugins.ts +3 -14
- package/ts/smartacme.classes.smartacme.ts +49 -16
- package/dist_ts/certmanagers.d.ts +0 -42
- package/dist_ts/certmanagers.js +0 -86
- package/dist_ts/smartacme.classes.certmanager.d.ts +0 -43
- package/dist_ts/smartacme.classes.certmanager.js +0 -92
- package/dist_ts/smartacme.plugins.d.ts +0 -21
- package/dist_ts/smartacme.plugins.js +0 -28
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartacme',
|
|
6
|
-
version: '
|
|
6
|
+
version: '9.0.1',
|
|
7
7
|
description: 'A TypeScript-based ACME client for LetsEncrypt certificate management with a focus on simplicity and power.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx1QkFBdUI7SUFDN0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDZHQUE2RztDQUMzSCxDQUFBIn0=
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AcmeHttpClient } from './acme.classes.http-client.js';
|
|
2
|
+
import type { IAcmeAccount, IAcmeAccountCreateRequest } from './acme.interfaces.js';
|
|
3
|
+
/**
|
|
4
|
+
* ACME account management - registration and key management
|
|
5
|
+
*/
|
|
6
|
+
export declare class AcmeAccount {
|
|
7
|
+
private httpClient;
|
|
8
|
+
private accountUrl;
|
|
9
|
+
constructor(httpClient: AcmeHttpClient);
|
|
10
|
+
/**
|
|
11
|
+
* Register or retrieve an ACME account.
|
|
12
|
+
* Uses JWK (not kid) since account URL is not yet known.
|
|
13
|
+
* Captures account URL from Location header for subsequent requests.
|
|
14
|
+
*/
|
|
15
|
+
create(request: IAcmeAccountCreateRequest): Promise<IAcmeAccount>;
|
|
16
|
+
/**
|
|
17
|
+
* Get the account URL (kid) for use in JWS headers
|
|
18
|
+
*/
|
|
19
|
+
getAccountUrl(): string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACME account management - registration and key management
|
|
3
|
+
*/
|
|
4
|
+
export class AcmeAccount {
|
|
5
|
+
constructor(httpClient) {
|
|
6
|
+
this.accountUrl = null;
|
|
7
|
+
this.httpClient = httpClient;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Register or retrieve an ACME account.
|
|
11
|
+
* Uses JWK (not kid) since account URL is not yet known.
|
|
12
|
+
* Captures account URL from Location header for subsequent requests.
|
|
13
|
+
*/
|
|
14
|
+
async create(request) {
|
|
15
|
+
const dir = await this.httpClient.getDirectory();
|
|
16
|
+
const response = await this.httpClient.signedRequest(dir.newAccount, request, {
|
|
17
|
+
useJwk: true,
|
|
18
|
+
});
|
|
19
|
+
// Capture account URL from Location header (used as kid for future requests)
|
|
20
|
+
const location = response.headers['location'];
|
|
21
|
+
if (location) {
|
|
22
|
+
this.accountUrl = location;
|
|
23
|
+
this.httpClient.kid = location;
|
|
24
|
+
}
|
|
25
|
+
return response.data;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get the account URL (kid) for use in JWS headers
|
|
29
|
+
*/
|
|
30
|
+
getAccountUrl() {
|
|
31
|
+
if (!this.accountUrl) {
|
|
32
|
+
throw new Error('Account not yet created - call create() first');
|
|
33
|
+
}
|
|
34
|
+
return this.accountUrl;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5jbGFzc2VzLmFjY291bnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9hY21lL2FjbWUuY2xhc3Nlcy5hY2NvdW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUdBOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFdBQVc7SUFJdEIsWUFBWSxVQUEwQjtRQUY5QixlQUFVLEdBQWtCLElBQUksQ0FBQztRQUd2QyxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQztJQUMvQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILEtBQUssQ0FBQyxNQUFNLENBQUMsT0FBa0M7UUFDN0MsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFJLENBQUMsVUFBVSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ2pELE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLFVBQVUsQ0FBQyxhQUFhLENBQUMsR0FBRyxDQUFDLFVBQVUsRUFBRSxPQUFPLEVBQUU7WUFDNUUsTUFBTSxFQUFFLElBQUk7U0FDYixDQUFDLENBQUM7UUFFSCw2RUFBNkU7UUFDN0UsTUFBTSxRQUFRLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUM5QyxJQUFJLFFBQVEsRUFBRSxDQUFDO1lBQ2IsSUFBSSxDQUFDLFVBQVUsR0FBRyxRQUFRLENBQUM7WUFDM0IsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLEdBQUcsUUFBUSxDQUFDO1FBQ2pDLENBQUM7UUFFRCxPQUFPLFFBQVEsQ0FBQyxJQUFvQixDQUFDO0lBQ3ZDLENBQUM7SUFFRDs7T0FFRztJQUNILGFBQWE7UUFDWCxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3JCLE1BQU0sSUFBSSxLQUFLLENBQUMsK0NBQStDLENBQUMsQ0FBQztRQUNuRSxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDO0lBQ3pCLENBQUM7Q0FDRiJ9
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AcmeHttpClient } from './acme.classes.http-client.js';
|
|
2
|
+
import type { IAcmeChallenge } from './acme.interfaces.js';
|
|
3
|
+
/**
|
|
4
|
+
* ACME challenge operations - key authorization computation and challenge completion
|
|
5
|
+
*/
|
|
6
|
+
export declare class AcmeChallengeManager {
|
|
7
|
+
private httpClient;
|
|
8
|
+
private accountKeyPem;
|
|
9
|
+
constructor(httpClient: AcmeHttpClient, accountKeyPem: string);
|
|
10
|
+
/**
|
|
11
|
+
* Compute the key authorization for a challenge.
|
|
12
|
+
* For http-01: returns `token.thumbprint`
|
|
13
|
+
* For dns-01: returns `base64url(sha256(token.thumbprint))`
|
|
14
|
+
*
|
|
15
|
+
* This is a synchronous, pure-crypto computation.
|
|
16
|
+
*/
|
|
17
|
+
getKeyAuthorization(challenge: IAcmeChallenge): string;
|
|
18
|
+
/**
|
|
19
|
+
* Notify the ACME server to validate a challenge (POST {} to challenge URL)
|
|
20
|
+
*/
|
|
21
|
+
complete(challenge: IAcmeChallenge): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import { AcmeCrypto } from './acme.classes.crypto.js';
|
|
3
|
+
/**
|
|
4
|
+
* ACME challenge operations - key authorization computation and challenge completion
|
|
5
|
+
*/
|
|
6
|
+
export class AcmeChallengeManager {
|
|
7
|
+
constructor(httpClient, accountKeyPem) {
|
|
8
|
+
this.httpClient = httpClient;
|
|
9
|
+
this.accountKeyPem = accountKeyPem;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Compute the key authorization for a challenge.
|
|
13
|
+
* For http-01: returns `token.thumbprint`
|
|
14
|
+
* For dns-01: returns `base64url(sha256(token.thumbprint))`
|
|
15
|
+
*
|
|
16
|
+
* This is a synchronous, pure-crypto computation.
|
|
17
|
+
*/
|
|
18
|
+
getKeyAuthorization(challenge) {
|
|
19
|
+
const jwk = AcmeCrypto.getJwk(this.accountKeyPem);
|
|
20
|
+
const thumbprint = AcmeCrypto.getJwkThumbprint(jwk);
|
|
21
|
+
const keyAuth = `${challenge.token}.${thumbprint}`;
|
|
22
|
+
if (challenge.type === 'dns-01') {
|
|
23
|
+
// DNS-01 uses base64url(SHA-256(keyAuthorization))
|
|
24
|
+
return crypto.createHash('sha256').update(keyAuth).digest().toString('base64url');
|
|
25
|
+
}
|
|
26
|
+
// HTTP-01 and others use the raw key authorization
|
|
27
|
+
return keyAuth;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Notify the ACME server to validate a challenge (POST {} to challenge URL)
|
|
31
|
+
*/
|
|
32
|
+
async complete(challenge) {
|
|
33
|
+
await this.httpClient.signedRequest(challenge.url, {});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5jbGFzc2VzLmNoYWxsZW5nZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2FjbWUvYWNtZS5jbGFzc2VzLmNoYWxsZW5nZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUN0QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFJdEQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sb0JBQW9CO0lBSS9CLFlBQVksVUFBMEIsRUFBRSxhQUFxQjtRQUMzRCxJQUFJLENBQUMsVUFBVSxHQUFHLFVBQVUsQ0FBQztRQUM3QixJQUFJLENBQUMsYUFBYSxHQUFHLGFBQWEsQ0FBQztJQUNyQyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0gsbUJBQW1CLENBQUMsU0FBeUI7UUFDM0MsTUFBTSxHQUFHLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDbEQsTUFBTSxVQUFVLEdBQUcsVUFBVSxDQUFDLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3BELE1BQU0sT0FBTyxHQUFHLEdBQUcsU0FBUyxDQUFDLEtBQUssSUFBSSxVQUFVLEVBQUUsQ0FBQztRQUVuRCxJQUFJLFNBQVMsQ0FBQyxJQUFJLEtBQUssUUFBUSxFQUFFLENBQUM7WUFDaEMsbURBQW1EO1lBQ25ELE9BQU8sTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3BGLENBQUM7UUFFRCxtREFBbUQ7UUFDbkQsT0FBTyxPQUFPLENBQUM7SUFDakIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLFFBQVEsQ0FBQyxTQUF5QjtRQUN0QyxNQUFNLElBQUksQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDekQsQ0FBQztDQUNGIn0=
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { AcmeCrypto } from './acme.classes.crypto.js';
|
|
2
|
+
import { type TAcmeLogger } from './acme.classes.http-client.js';
|
|
3
|
+
import type { IAcmeAccount, IAcmeAccountCreateRequest, IAcmeAuthorization, IAcmeChallenge, IAcmeIdentifier, IAcmeOrder } from './acme.interfaces.js';
|
|
4
|
+
export interface IAcmeClientOptions {
|
|
5
|
+
directoryUrl: string;
|
|
6
|
+
accountKeyPem: string;
|
|
7
|
+
logger?: TAcmeLogger;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Top-level ACME client facade.
|
|
11
|
+
* Composes HTTP transport, account management, order lifecycle, and challenge handling.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AcmeClient {
|
|
14
|
+
private httpClient;
|
|
15
|
+
private account;
|
|
16
|
+
private orderManager;
|
|
17
|
+
private challengeManager;
|
|
18
|
+
/** Well-known CA directory URLs */
|
|
19
|
+
static directory: {
|
|
20
|
+
readonly letsencrypt: {
|
|
21
|
+
readonly production: "https://acme-v02.api.letsencrypt.org/directory";
|
|
22
|
+
readonly staging: "https://acme-staging-v02.api.letsencrypt.org/directory";
|
|
23
|
+
};
|
|
24
|
+
readonly buypass: {
|
|
25
|
+
readonly production: "https://api.buypass.com/acme/directory";
|
|
26
|
+
readonly staging: "https://api.test4.buypass.no/acme/directory";
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
/** Crypto utilities */
|
|
30
|
+
static crypto: typeof AcmeCrypto;
|
|
31
|
+
constructor(options: IAcmeClientOptions);
|
|
32
|
+
/**
|
|
33
|
+
* Register or retrieve an ACME account
|
|
34
|
+
*/
|
|
35
|
+
createAccount(request: IAcmeAccountCreateRequest): Promise<IAcmeAccount>;
|
|
36
|
+
/**
|
|
37
|
+
* Create a new certificate order
|
|
38
|
+
*/
|
|
39
|
+
createOrder(opts: {
|
|
40
|
+
identifiers: IAcmeIdentifier[];
|
|
41
|
+
}): Promise<IAcmeOrder>;
|
|
42
|
+
/**
|
|
43
|
+
* Get all authorizations for an order
|
|
44
|
+
*/
|
|
45
|
+
getAuthorizations(order: IAcmeOrder): Promise<IAcmeAuthorization[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Compute the key authorization string for a challenge (sync)
|
|
48
|
+
*/
|
|
49
|
+
getChallengeKeyAuthorization(challenge: IAcmeChallenge): string;
|
|
50
|
+
/**
|
|
51
|
+
* Notify the ACME server to validate a challenge
|
|
52
|
+
*/
|
|
53
|
+
completeChallenge(challenge: IAcmeChallenge): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Poll an ACME resource until it reaches valid/ready status
|
|
56
|
+
*/
|
|
57
|
+
waitForValidStatus(item: {
|
|
58
|
+
url: string;
|
|
59
|
+
}): Promise<any>;
|
|
60
|
+
/**
|
|
61
|
+
* Finalize an order by submitting the CSR
|
|
62
|
+
*/
|
|
63
|
+
finalizeOrder(order: IAcmeOrder, csrPem: string): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Download the certificate chain (PEM)
|
|
66
|
+
*/
|
|
67
|
+
getCertificate(order: IAcmeOrder): Promise<string>;
|
|
68
|
+
/**
|
|
69
|
+
* Destroy HTTP transport to release sockets and allow process exit.
|
|
70
|
+
*/
|
|
71
|
+
destroy(): void;
|
|
72
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { AcmeCrypto } from './acme.classes.crypto.js';
|
|
2
|
+
import { ACME_DIRECTORY_URLS } from './acme.classes.directory.js';
|
|
3
|
+
import { AcmeHttpClient } from './acme.classes.http-client.js';
|
|
4
|
+
import { AcmeAccount } from './acme.classes.account.js';
|
|
5
|
+
import { AcmeOrderManager } from './acme.classes.order.js';
|
|
6
|
+
import { AcmeChallengeManager } from './acme.classes.challenge.js';
|
|
7
|
+
/**
|
|
8
|
+
* Top-level ACME client facade.
|
|
9
|
+
* Composes HTTP transport, account management, order lifecycle, and challenge handling.
|
|
10
|
+
*/
|
|
11
|
+
export class AcmeClient {
|
|
12
|
+
/** Well-known CA directory URLs */
|
|
13
|
+
static { this.directory = ACME_DIRECTORY_URLS; }
|
|
14
|
+
/** Crypto utilities */
|
|
15
|
+
static { this.crypto = AcmeCrypto; }
|
|
16
|
+
constructor(options) {
|
|
17
|
+
this.httpClient = new AcmeHttpClient(options.directoryUrl, options.accountKeyPem, options.logger);
|
|
18
|
+
this.account = new AcmeAccount(this.httpClient);
|
|
19
|
+
this.orderManager = new AcmeOrderManager(this.httpClient);
|
|
20
|
+
this.challengeManager = new AcmeChallengeManager(this.httpClient, options.accountKeyPem);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register or retrieve an ACME account
|
|
24
|
+
*/
|
|
25
|
+
async createAccount(request) {
|
|
26
|
+
return this.account.create(request);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create a new certificate order
|
|
30
|
+
*/
|
|
31
|
+
async createOrder(opts) {
|
|
32
|
+
return this.orderManager.create(opts);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get all authorizations for an order
|
|
36
|
+
*/
|
|
37
|
+
async getAuthorizations(order) {
|
|
38
|
+
return this.orderManager.getAuthorizations(order);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Compute the key authorization string for a challenge (sync)
|
|
42
|
+
*/
|
|
43
|
+
getChallengeKeyAuthorization(challenge) {
|
|
44
|
+
return this.challengeManager.getKeyAuthorization(challenge);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Notify the ACME server to validate a challenge
|
|
48
|
+
*/
|
|
49
|
+
async completeChallenge(challenge) {
|
|
50
|
+
return this.challengeManager.complete(challenge);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Poll an ACME resource until it reaches valid/ready status
|
|
54
|
+
*/
|
|
55
|
+
async waitForValidStatus(item) {
|
|
56
|
+
return this.orderManager.waitForValidStatus(item);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Finalize an order by submitting the CSR
|
|
60
|
+
*/
|
|
61
|
+
async finalizeOrder(order, csrPem) {
|
|
62
|
+
return this.orderManager.finalize(order, csrPem);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Download the certificate chain (PEM)
|
|
66
|
+
*/
|
|
67
|
+
async getCertificate(order) {
|
|
68
|
+
return this.orderManager.getCertificate(order);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Destroy HTTP transport to release sockets and allow process exit.
|
|
72
|
+
*/
|
|
73
|
+
destroy() {
|
|
74
|
+
this.httpClient.destroy();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5jbGFzc2VzLmNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2FjbWUvYWNtZS5jbGFzc2VzLmNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDdEQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDbEUsT0FBTyxFQUFFLGNBQWMsRUFBb0IsTUFBTSwrQkFBK0IsQ0FBQztBQUNqRixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDeEQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDM0QsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFnQm5FOzs7R0FHRztBQUNILE1BQU0sT0FBTyxVQUFVO0lBTXJCLG1DQUFtQzthQUM1QixjQUFTLEdBQUcsbUJBQW1CLENBQUM7SUFDdkMsdUJBQXVCO2FBQ2hCLFdBQU0sR0FBRyxVQUFVLENBQUM7SUFFM0IsWUFBWSxPQUEyQjtRQUNyQyxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksY0FBYyxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsT0FBTyxDQUFDLGFBQWEsRUFBRSxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDbEcsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLFdBQVcsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEQsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLGdCQUFnQixDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUMxRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsSUFBSSxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUMzRixDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsYUFBYSxDQUFDLE9BQWtDO1FBQ3BELE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLFdBQVcsQ0FBQyxJQUF3QztRQUN4RCxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3hDLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxLQUFpQjtRQUN2QyxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsaUJBQWlCLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDcEQsQ0FBQztJQUVEOztPQUVHO0lBQ0gsNEJBQTRCLENBQUMsU0FBeUI7UUFDcEQsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsbUJBQW1CLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDOUQsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLGlCQUFpQixDQUFDLFNBQXlCO1FBQy9DLE9BQU8sSUFBSSxDQUFDLGdCQUFnQixDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNuRCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxLQUFLLENBQUMsa0JBQWtCLENBQUMsSUFBcUI7UUFDNUMsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3BELENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxhQUFhLENBQUMsS0FBaUIsRUFBRSxNQUFjO1FBQ25ELE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ25ELENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssQ0FBQyxjQUFjLENBQUMsS0FBaUI7UUFDcEMsT0FBTyxJQUFJLENBQUMsWUFBWSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxPQUFPO1FBQ0wsSUFBSSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsQ0FBQztJQUM1QixDQUFDIn0=
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { IAcmeCsrOptions } from './acme.interfaces.js';
|
|
2
|
+
/**
|
|
3
|
+
* All cryptographic operations for the ACME protocol.
|
|
4
|
+
* Uses node:crypto for key gen, JWK, JWS signing.
|
|
5
|
+
* Uses @peculiar/x509 for CSR generation (no native Node.js CSR API).
|
|
6
|
+
*/
|
|
7
|
+
export declare class AcmeCrypto {
|
|
8
|
+
/**
|
|
9
|
+
* Generate an RSA private key in PEM format
|
|
10
|
+
*/
|
|
11
|
+
static createRsaPrivateKey(modulusLength?: number): string;
|
|
12
|
+
/**
|
|
13
|
+
* Export public JWK from PEM private key, keys sorted alphabetically per RFC 7638
|
|
14
|
+
*/
|
|
15
|
+
static getJwk(keyPem: string): Record<string, string>;
|
|
16
|
+
/**
|
|
17
|
+
* Compute JWK Thumbprint (SHA-256, base64url) per RFC 7638
|
|
18
|
+
*/
|
|
19
|
+
static getJwkThumbprint(jwk: Record<string, string>): string;
|
|
20
|
+
/**
|
|
21
|
+
* Create a flattened JWS for ACME requests (RFC 7515)
|
|
22
|
+
* payload=null means POST-as-GET (empty string payload)
|
|
23
|
+
*/
|
|
24
|
+
static createJws(keyPem: string, url: string, payload: any | null, options: {
|
|
25
|
+
nonce: string;
|
|
26
|
+
kid?: string;
|
|
27
|
+
jwk?: Record<string, string>;
|
|
28
|
+
}): {
|
|
29
|
+
protected: string;
|
|
30
|
+
payload: string;
|
|
31
|
+
signature: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Create a CSR (PKCS#10) via @peculiar/x509
|
|
35
|
+
* Returns [privateKeyPem, csrPem]
|
|
36
|
+
*/
|
|
37
|
+
static createCsr(options: IAcmeCsrOptions, existingKeyPem?: string): Promise<[string, string]>;
|
|
38
|
+
/**
|
|
39
|
+
* Convert PEM to raw DER Buffer (strip headers, decode base64)
|
|
40
|
+
*/
|
|
41
|
+
static pemToBuffer(pem: string): Buffer;
|
|
42
|
+
/**
|
|
43
|
+
* Determine JWS algorithm from key type
|
|
44
|
+
*/
|
|
45
|
+
private static getAlg;
|
|
46
|
+
/**
|
|
47
|
+
* Import a PEM private key into WebCrypto as a CryptoKeyPair
|
|
48
|
+
*/
|
|
49
|
+
private static importKeyPairToWebCrypto;
|
|
50
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* All cryptographic operations for the ACME protocol.
|
|
4
|
+
* Uses node:crypto for key gen, JWK, JWS signing.
|
|
5
|
+
* Uses @peculiar/x509 for CSR generation (no native Node.js CSR API).
|
|
6
|
+
*/
|
|
7
|
+
export class AcmeCrypto {
|
|
8
|
+
/**
|
|
9
|
+
* Generate an RSA private key in PEM format
|
|
10
|
+
*/
|
|
11
|
+
static createRsaPrivateKey(modulusLength = 2048) {
|
|
12
|
+
const { privateKey } = crypto.generateKeyPairSync('rsa', {
|
|
13
|
+
modulusLength,
|
|
14
|
+
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
15
|
+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
|
|
16
|
+
});
|
|
17
|
+
return privateKey;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Export public JWK from PEM private key, keys sorted alphabetically per RFC 7638
|
|
21
|
+
*/
|
|
22
|
+
static getJwk(keyPem) {
|
|
23
|
+
const keyObj = crypto.createPublicKey(keyPem);
|
|
24
|
+
const jwk = keyObj.export({ format: 'jwk' });
|
|
25
|
+
if (jwk.kty === 'RSA') {
|
|
26
|
+
return { e: jwk.e, kty: jwk.kty, n: jwk.n };
|
|
27
|
+
}
|
|
28
|
+
else if (jwk.kty === 'EC') {
|
|
29
|
+
return { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
|
|
30
|
+
}
|
|
31
|
+
throw new Error(`Unsupported key type: ${jwk.kty}`);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Compute JWK Thumbprint (SHA-256, base64url) per RFC 7638
|
|
35
|
+
*/
|
|
36
|
+
static getJwkThumbprint(jwk) {
|
|
37
|
+
let canonical;
|
|
38
|
+
if (jwk.kty === 'RSA') {
|
|
39
|
+
canonical = JSON.stringify({ e: jwk.e, kty: jwk.kty, n: jwk.n });
|
|
40
|
+
}
|
|
41
|
+
else if (jwk.kty === 'EC') {
|
|
42
|
+
canonical = JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y });
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new Error(`Unsupported key type: ${jwk.kty}`);
|
|
46
|
+
}
|
|
47
|
+
const hash = crypto.createHash('sha256').update(canonical).digest();
|
|
48
|
+
return hash.toString('base64url');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a flattened JWS for ACME requests (RFC 7515)
|
|
52
|
+
* payload=null means POST-as-GET (empty string payload)
|
|
53
|
+
*/
|
|
54
|
+
static createJws(keyPem, url, payload, options) {
|
|
55
|
+
const header = {
|
|
56
|
+
alg: AcmeCrypto.getAlg(keyPem),
|
|
57
|
+
nonce: options.nonce,
|
|
58
|
+
url,
|
|
59
|
+
};
|
|
60
|
+
if (options.kid) {
|
|
61
|
+
header.kid = options.kid;
|
|
62
|
+
}
|
|
63
|
+
else if (options.jwk) {
|
|
64
|
+
header.jwk = options.jwk;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
header.jwk = AcmeCrypto.getJwk(keyPem);
|
|
68
|
+
}
|
|
69
|
+
const protectedB64 = Buffer.from(JSON.stringify(header)).toString('base64url');
|
|
70
|
+
const payloadB64 = payload !== null ? Buffer.from(JSON.stringify(payload)).toString('base64url') : '';
|
|
71
|
+
const signingInput = `${protectedB64}.${payloadB64}`;
|
|
72
|
+
const keyObj = crypto.createPrivateKey(keyPem);
|
|
73
|
+
const alg = AcmeCrypto.getAlg(keyPem);
|
|
74
|
+
let signature;
|
|
75
|
+
if (alg.startsWith('RS')) {
|
|
76
|
+
signature = crypto.sign('sha256', Buffer.from(signingInput), keyObj);
|
|
77
|
+
}
|
|
78
|
+
else if (alg.startsWith('ES')) {
|
|
79
|
+
signature = crypto.sign('sha256', Buffer.from(signingInput), {
|
|
80
|
+
key: keyObj,
|
|
81
|
+
dsaEncoding: 'ieee-p1363',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
throw new Error(`Unsupported algorithm: ${alg}`);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
protected: protectedB64,
|
|
89
|
+
payload: payloadB64,
|
|
90
|
+
signature: signature.toString('base64url'),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Create a CSR (PKCS#10) via @peculiar/x509
|
|
95
|
+
* Returns [privateKeyPem, csrPem]
|
|
96
|
+
*/
|
|
97
|
+
static async createCsr(options, existingKeyPem) {
|
|
98
|
+
const x509 = await import('@peculiar/x509');
|
|
99
|
+
const { webcrypto } = crypto;
|
|
100
|
+
x509.cryptoProvider.set(webcrypto);
|
|
101
|
+
let keys;
|
|
102
|
+
let keyPem;
|
|
103
|
+
if (existingKeyPem) {
|
|
104
|
+
keys = await AcmeCrypto.importKeyPairToWebCrypto(existingKeyPem, webcrypto);
|
|
105
|
+
keyPem = existingKeyPem;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
keys = (await webcrypto.subtle.generateKey({
|
|
109
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
110
|
+
modulusLength: 2048,
|
|
111
|
+
publicExponent: new Uint8Array([1, 0, 1]),
|
|
112
|
+
hash: 'SHA-256',
|
|
113
|
+
}, true, ['sign', 'verify']));
|
|
114
|
+
const pkcs8 = await webcrypto.subtle.exportKey('pkcs8', keys.privateKey);
|
|
115
|
+
const b64 = Buffer.from(pkcs8).toString('base64');
|
|
116
|
+
const lines = b64.match(/.{1,64}/g);
|
|
117
|
+
keyPem = `-----BEGIN PRIVATE KEY-----\n${lines.join('\n')}\n-----END PRIVATE KEY-----\n`;
|
|
118
|
+
}
|
|
119
|
+
// Collect all DNS names for SAN (CN is always included)
|
|
120
|
+
const sanNames = [options.commonName];
|
|
121
|
+
if (options.altNames) {
|
|
122
|
+
for (const name of options.altNames) {
|
|
123
|
+
if (!sanNames.includes(name)) {
|
|
124
|
+
sanNames.push(name);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const csr = await x509.Pkcs10CertificateRequestGenerator.create({
|
|
129
|
+
name: `CN=${options.commonName}`,
|
|
130
|
+
keys,
|
|
131
|
+
signingAlgorithm: { name: 'RSASSA-PKCS1-v1_5' },
|
|
132
|
+
extensions: [
|
|
133
|
+
new x509.SubjectAlternativeNameExtension(sanNames.map((name) => ({ type: 'dns', value: name }))),
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
// Convert to PEM
|
|
137
|
+
const csrPem = csr.toString('pem');
|
|
138
|
+
return [keyPem, csrPem];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Convert PEM to raw DER Buffer (strip headers, decode base64)
|
|
142
|
+
*/
|
|
143
|
+
static pemToBuffer(pem) {
|
|
144
|
+
const lines = pem
|
|
145
|
+
.split('\n')
|
|
146
|
+
.filter((line) => !line.startsWith('-----') && line.trim().length > 0);
|
|
147
|
+
return Buffer.from(lines.join(''), 'base64');
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Determine JWS algorithm from key type
|
|
151
|
+
*/
|
|
152
|
+
static getAlg(keyPem) {
|
|
153
|
+
const keyObj = crypto.createPrivateKey(keyPem);
|
|
154
|
+
const keyType = keyObj.asymmetricKeyType;
|
|
155
|
+
if (keyType === 'rsa')
|
|
156
|
+
return 'RS256';
|
|
157
|
+
if (keyType === 'ec') {
|
|
158
|
+
const details = keyObj.asymmetricKeyDetails;
|
|
159
|
+
if (details?.namedCurve === 'prime256v1' || details?.namedCurve === 'P-256')
|
|
160
|
+
return 'ES256';
|
|
161
|
+
if (details?.namedCurve === 'secp384r1' || details?.namedCurve === 'P-384')
|
|
162
|
+
return 'ES384';
|
|
163
|
+
return 'ES256';
|
|
164
|
+
}
|
|
165
|
+
throw new Error(`Unsupported key type: ${keyType}`);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Import a PEM private key into WebCrypto as a CryptoKeyPair
|
|
169
|
+
*/
|
|
170
|
+
static async importKeyPairToWebCrypto(keyPem, wc) {
|
|
171
|
+
const keyObj = crypto.createPrivateKey(keyPem);
|
|
172
|
+
const pkcs8Der = keyObj.export({ type: 'pkcs8', format: 'der' });
|
|
173
|
+
const privateKey = await wc.subtle.importKey('pkcs8', pkcs8Der, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, true, ['sign']);
|
|
174
|
+
const pubKeyObj = crypto.createPublicKey(keyPem);
|
|
175
|
+
const spkiDer = pubKeyObj.export({ type: 'spki', format: 'der' });
|
|
176
|
+
const publicKey = await wc.subtle.importKey('spki', spkiDer, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, true, ['verify']);
|
|
177
|
+
return { privateKey: privateKey, publicKey: publicKey };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5jbGFzc2VzLmNyeXB0by5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2FjbWUvYWNtZS5jbGFzc2VzLmNyeXB0by50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUd0Qzs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLFVBQVU7SUFDckI7O09BRUc7SUFDSCxNQUFNLENBQUMsbUJBQW1CLENBQUMsYUFBYSxHQUFHLElBQUk7UUFDN0MsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLEVBQUU7WUFDdkQsYUFBYTtZQUNiLGlCQUFpQixFQUFFLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFO1lBQ2xELGtCQUFrQixFQUFFLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFO1NBQ3JELENBQUMsQ0FBQztRQUNILE9BQU8sVUFBVSxDQUFDO0lBQ3BCLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBYztRQUMxQixNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQzlDLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQXdCLENBQUM7UUFDcEUsSUFBSSxHQUFHLENBQUMsR0FBRyxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ3RCLE9BQU8sRUFBRSxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQzlDLENBQUM7YUFBTSxJQUFJLEdBQUcsQ0FBQyxHQUFHLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDNUIsT0FBTyxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDNUQsQ0FBQztRQUNELE1BQU0sSUFBSSxLQUFLLENBQUMseUJBQXlCLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQ3RELENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxHQUEyQjtRQUNqRCxJQUFJLFNBQWlCLENBQUM7UUFDdEIsSUFBSSxHQUFHLENBQUMsR0FBRyxLQUFLLEtBQUssRUFBRSxDQUFDO1lBQ3RCLFNBQVMsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ25FLENBQUM7YUFBTSxJQUFJLEdBQUcsQ0FBQyxHQUFHLEtBQUssSUFBSSxFQUFFLENBQUM7WUFDNUIsU0FBUyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDakYsQ0FBQzthQUFNLENBQUM7WUFDTixNQUFNLElBQUksS0FBSyxDQUFDLHlCQUF5QixHQUFHLENBQUMsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUN0RCxDQUFDO1FBQ0QsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDcEUsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFRDs7O09BR0c7SUFDSCxNQUFNLENBQUMsU0FBUyxDQUNkLE1BQWMsRUFDZCxHQUFXLEVBQ1gsT0FBbUIsRUFDbkIsT0FBc0U7UUFFdEUsTUFBTSxNQUFNLEdBQXdCO1lBQ2xDLEdBQUcsRUFBRSxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQztZQUM5QixLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDcEIsR0FBRztTQUNKLENBQUM7UUFDRixJQUFJLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNoQixNQUFNLENBQUMsR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUM7UUFDM0IsQ0FBQzthQUFNLElBQUksT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ3ZCLE1BQU0sQ0FBQyxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQztRQUMzQixDQUFDO2FBQU0sQ0FBQztZQUNOLE1BQU0sQ0FBQyxHQUFHLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUN6QyxDQUFDO1FBRUQsTUFBTSxZQUFZLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQy9FLE1BQU0sVUFBVSxHQUNkLE9BQU8sS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBRXJGLE1BQU0sWUFBWSxHQUFHLEdBQUcsWUFBWSxJQUFJLFVBQVUsRUFBRSxDQUFDO1FBQ3JELE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMvQyxNQUFNLEdBQUcsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBRXRDLElBQUksU0FBaUIsQ0FBQztRQUN0QixJQUFJLEdBQUcsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUN6QixTQUFTLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQztRQUN2RSxDQUFDO2FBQU0sSUFBSSxHQUFHLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDaEMsU0FBUyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLEVBQUU7Z0JBQzNELEdBQUcsRUFBRSxNQUFNO2dCQUNYLFdBQVcsRUFBRSxZQUFZO2FBQzFCLENBQUMsQ0FBQztRQUNMLENBQUM7YUFBTSxDQUFDO1lBQ04sTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUNuRCxDQUFDO1FBRUQsT0FBTztZQUNMLFNBQVMsRUFBRSxZQUFZO1lBQ3ZCLE9BQU8sRUFBRSxVQUFVO1lBQ25CLFNBQVMsRUFBRSxTQUFTLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQztTQUMzQyxDQUFDO0lBQ0osQ0FBQztJQUVEOzs7T0FHRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUNwQixPQUF3QixFQUN4QixjQUF1QjtRQUV2QixNQUFNLElBQUksR0FBRyxNQUFNLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQzVDLE1BQU0sRUFBRSxTQUFTLEVBQUUsR0FBRyxNQUFNLENBQUM7UUFDN0IsSUFBSSxDQUFDLGNBQWMsQ0FBQyxHQUFHLENBQUMsU0FBZ0IsQ0FBQyxDQUFDO1FBRTFDLElBQUksSUFBbUIsQ0FBQztRQUN4QixJQUFJLE1BQWMsQ0FBQztRQUVuQixJQUFJLGNBQWMsRUFBRSxDQUFDO1lBQ25CLElBQUksR0FBRyxNQUFNLFVBQVUsQ0FBQyx3QkFBd0IsQ0FBQyxjQUFjLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFDNUUsTUFBTSxHQUFHLGNBQWMsQ0FBQztRQUMxQixDQUFDO2FBQU0sQ0FBQztZQUNOLElBQUksR0FBRyxDQUFDLE1BQU0sU0FBUyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQ3hDO2dCQUNFLElBQUksRUFBRSxtQkFBbUI7Z0JBQ3pCLGFBQWEsRUFBRSxJQUFJO2dCQUNuQixjQUFjLEVBQUUsSUFBSSxVQUFVLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUN6QyxJQUFJLEVBQUUsU0FBUzthQUNoQixFQUNELElBQUksRUFDSixDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FDbkIsQ0FBa0IsQ0FBQztZQUVwQixNQUFNLEtBQUssR0FBRyxNQUFNLFNBQVMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDekUsTUFBTSxHQUFHLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDbEQsTUFBTSxLQUFLLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUUsQ0FBQztZQUNyQyxNQUFNLEdBQUcsZ0NBQWdDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLCtCQUErQixDQUFDO1FBQzNGLENBQUM7UUFFRCx3REFBd0Q7UUFDeEQsTUFBTSxRQUFRLEdBQWEsQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDaEQsSUFBSSxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDckIsS0FBSyxNQUFNLElBQUksSUFBSSxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7Z0JBQ3BDLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7b0JBQzdCLFFBQVEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7Z0JBQ3RCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUVELE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBSSxDQUFDLGlDQUFpQyxDQUFDLE1BQU0sQ0FBQztZQUM5RCxJQUFJLEVBQUUsTUFBTSxPQUFPLENBQUMsVUFBVSxFQUFFO1lBQ2hDLElBQUk7WUFDSixnQkFBZ0IsRUFBRSxFQUFFLElBQUksRUFBRSxtQkFBbUIsRUFBRTtZQUMvQyxVQUFVLEVBQUU7Z0JBQ1YsSUFBSSxJQUFJLENBQUMsK0JBQStCLENBQ3RDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsS0FBYyxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQ2hFO2FBQ0Y7U0FDRixDQUFDLENBQUM7UUFFSCxpQkFBaUI7UUFDakIsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUVuQyxPQUFPLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQzFCLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxXQUFXLENBQUMsR0FBVztRQUM1QixNQUFNLEtBQUssR0FBRyxHQUFHO2FBQ2QsS0FBSyxDQUFDLElBQUksQ0FBQzthQUNYLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDekUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQUVEOztPQUVHO0lBQ0ssTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFjO1FBQ2xDLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMvQyxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsaUJBQWlCLENBQUM7UUFDekMsSUFBSSxPQUFPLEtBQUssS0FBSztZQUFFLE9BQU8sT0FBTyxDQUFDO1FBQ3RDLElBQUksT0FBTyxLQUFLLElBQUksRUFBRSxDQUFDO1lBQ3JCLE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxvQkFBb0IsQ0FBQztZQUM1QyxJQUFJLE9BQU8sRUFBRSxVQUFVLEtBQUssWUFBWSxJQUFJLE9BQU8sRUFBRSxVQUFVLEtBQUssT0FBTztnQkFBRSxPQUFPLE9BQU8sQ0FBQztZQUM1RixJQUFJLE9BQU8sRUFBRSxVQUFVLEtBQUssV0FBVyxJQUFJLE9BQU8sRUFBRSxVQUFVLEtBQUssT0FBTztnQkFBRSxPQUFPLE9BQU8sQ0FBQztZQUMzRixPQUFPLE9BQU8sQ0FBQztRQUNqQixDQUFDO1FBQ0QsTUFBTSxJQUFJLEtBQUssQ0FBQyx5QkFBeUIsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUN0RCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxNQUFNLENBQUMsS0FBSyxDQUFDLHdCQUF3QixDQUMzQyxNQUFjLEVBQ2QsRUFBMkI7UUFFM0IsTUFBTSxNQUFNLEdBQUcsTUFBTSxDQUFDLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxDQUFDO1FBQy9DLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ2pFLE1BQU0sVUFBVSxHQUFHLE1BQU0sRUFBRSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQzFDLE9BQU8sRUFDUCxRQUFRLEVBQ1IsRUFBRSxJQUFJLEVBQUUsbUJBQW1CLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxFQUM5QyxJQUFJLEVBQ0osQ0FBQyxNQUFNLENBQUMsQ0FDVCxDQUFDO1FBRUYsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLGVBQWUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNqRCxNQUFNLE9BQU8sR0FBRyxTQUFTLENBQUMsTUFBTSxDQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUNsRSxNQUFNLFNBQVMsR0FBRyxNQUFNLEVBQUUsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUN6QyxNQUFNLEVBQ04sT0FBTyxFQUNQLEVBQUUsSUFBSSxFQUFFLG1CQUFtQixFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsRUFDOUMsSUFBSSxFQUNKLENBQUMsUUFBUSxDQUFDLENBQ1gsQ0FBQztRQUVGLE9BQU8sRUFBRSxVQUFVLEVBQUUsVUFBa0MsRUFBRSxTQUFTLEVBQUUsU0FBaUMsRUFBRSxDQUFDO0lBQzFHLENBQUM7Q0FDRiJ9
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACME directory URL constants for well-known CAs
|
|
3
|
+
*/
|
|
4
|
+
export declare const ACME_DIRECTORY_URLS: {
|
|
5
|
+
readonly letsencrypt: {
|
|
6
|
+
readonly production: "https://acme-v02.api.letsencrypt.org/directory";
|
|
7
|
+
readonly staging: "https://acme-staging-v02.api.letsencrypt.org/directory";
|
|
8
|
+
};
|
|
9
|
+
readonly buypass: {
|
|
10
|
+
readonly production: "https://api.buypass.com/acme/directory";
|
|
11
|
+
readonly staging: "https://api.test4.buypass.no/acme/directory";
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACME directory URL constants for well-known CAs
|
|
3
|
+
*/
|
|
4
|
+
export const ACME_DIRECTORY_URLS = {
|
|
5
|
+
letsencrypt: {
|
|
6
|
+
production: 'https://acme-v02.api.letsencrypt.org/directory',
|
|
7
|
+
staging: 'https://acme-staging-v02.api.letsencrypt.org/directory',
|
|
8
|
+
},
|
|
9
|
+
buypass: {
|
|
10
|
+
production: 'https://api.buypass.com/acme/directory',
|
|
11
|
+
staging: 'https://api.test4.buypass.no/acme/directory',
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5jbGFzc2VzLmRpcmVjdG9yeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2FjbWUvYWNtZS5jbGFzc2VzLmRpcmVjdG9yeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLG1CQUFtQixHQUFHO0lBQ2pDLFdBQVcsRUFBRTtRQUNYLFVBQVUsRUFBRSxnREFBZ0Q7UUFDNUQsT0FBTyxFQUFFLHdEQUF3RDtLQUNsRTtJQUNELE9BQU8sRUFBRTtRQUNQLFVBQVUsRUFBRSx3Q0FBd0M7UUFDcEQsT0FBTyxFQUFFLDZDQUE2QztLQUN2RDtDQUNPLENBQUMifQ==
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured ACME protocol error with RFC 8555 fields.
|
|
3
|
+
* Provides type URN, subproblems, Retry-After, and retryability classification.
|
|
4
|
+
*/
|
|
5
|
+
export declare class AcmeError extends Error {
|
|
6
|
+
readonly status: number;
|
|
7
|
+
readonly type: string;
|
|
8
|
+
readonly detail: string;
|
|
9
|
+
readonly subproblems: Array<{
|
|
10
|
+
type: string;
|
|
11
|
+
detail: string;
|
|
12
|
+
identifier?: {
|
|
13
|
+
type: string;
|
|
14
|
+
value: string;
|
|
15
|
+
};
|
|
16
|
+
}>;
|
|
17
|
+
readonly url: string;
|
|
18
|
+
readonly retryAfter: number;
|
|
19
|
+
constructor(options: {
|
|
20
|
+
message?: string;
|
|
21
|
+
status: number;
|
|
22
|
+
type?: string;
|
|
23
|
+
detail?: string;
|
|
24
|
+
subproblems?: Array<{
|
|
25
|
+
type: string;
|
|
26
|
+
detail: string;
|
|
27
|
+
identifier?: {
|
|
28
|
+
type: string;
|
|
29
|
+
value: string;
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
url?: string;
|
|
33
|
+
retryAfter?: number;
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* True for HTTP 429 or ACME rateLimited type URN
|
|
37
|
+
*/
|
|
38
|
+
get isRateLimited(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* True for transient/retryable errors: 429, 503, 5xx, badNonce.
|
|
41
|
+
* False for definitive client errors: 400 (non-badNonce), 403, 404, 409.
|
|
42
|
+
*/
|
|
43
|
+
get isRetryable(): boolean;
|
|
44
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured ACME protocol error with RFC 8555 fields.
|
|
3
|
+
* Provides type URN, subproblems, Retry-After, and retryability classification.
|
|
4
|
+
*/
|
|
5
|
+
export class AcmeError extends Error {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
const type = options.type || '';
|
|
8
|
+
const detail = options.detail || '';
|
|
9
|
+
const url = options.url || '';
|
|
10
|
+
const msg = options.message ||
|
|
11
|
+
`ACME error: ${type || 'unknown'} (HTTP ${options.status}) at ${url || 'unknown'} - ${detail || 'no detail'}`;
|
|
12
|
+
super(msg);
|
|
13
|
+
this.name = 'AcmeError';
|
|
14
|
+
this.status = options.status;
|
|
15
|
+
this.type = type;
|
|
16
|
+
this.detail = detail;
|
|
17
|
+
this.subproblems = options.subproblems || [];
|
|
18
|
+
this.url = url;
|
|
19
|
+
this.retryAfter = options.retryAfter || 0;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* True for HTTP 429 or ACME rateLimited type URN
|
|
23
|
+
*/
|
|
24
|
+
get isRateLimited() {
|
|
25
|
+
return this.status === 429 || this.type === 'urn:ietf:params:acme:error:rateLimited';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* True for transient/retryable errors: 429, 503, 5xx, badNonce.
|
|
29
|
+
* False for definitive client errors: 400 (non-badNonce), 403, 404, 409.
|
|
30
|
+
*/
|
|
31
|
+
get isRetryable() {
|
|
32
|
+
if (this.type === 'urn:ietf:params:acme:error:badNonce')
|
|
33
|
+
return true;
|
|
34
|
+
if (this.status === 429 || this.status === 503)
|
|
35
|
+
return true;
|
|
36
|
+
if (this.status >= 500)
|
|
37
|
+
return true;
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNtZS5jbGFzc2VzLmVycm9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvYWNtZS9hY21lLmNsYXNzZXMuZXJyb3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFNBQVUsU0FBUSxLQUFLO0lBUWxDLFlBQVksT0FRWDtRQUNDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDO1FBQ2hDLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLElBQUksRUFBRSxDQUFDO1FBQ3BDLE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLElBQUksRUFBRSxDQUFDO1FBQzlCLE1BQU0sR0FBRyxHQUNQLE9BQU8sQ0FBQyxPQUFPO1lBQ2YsZUFBZSxJQUFJLElBQUksU0FBUyxVQUFVLE9BQU8sQ0FBQyxNQUFNLFFBQVEsR0FBRyxJQUFJLFNBQVMsTUFBTSxNQUFNLElBQUksV0FBVyxFQUFFLENBQUM7UUFDaEgsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ1gsSUFBSSxDQUFDLElBQUksR0FBRyxXQUFXLENBQUM7UUFDeEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDO1FBQzdCLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxXQUFXLEdBQUcsT0FBTyxDQUFDLFdBQVcsSUFBSSxFQUFFLENBQUM7UUFDN0MsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUM7UUFDZixJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDO0lBQzVDLENBQUM7SUFFRDs7T0FFRztJQUNILElBQUksYUFBYTtRQUNmLE9BQU8sSUFBSSxDQUFDLE1BQU0sS0FBSyxHQUFHLElBQUksSUFBSSxDQUFDLElBQUksS0FBSyx3Q0FBd0MsQ0FBQztJQUN2RixDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsSUFBSSxXQUFXO1FBQ2IsSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLHFDQUFxQztZQUFFLE9BQU8sSUFBSSxDQUFDO1FBQ3JFLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxHQUFHLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxHQUFHO1lBQUUsT0FBTyxJQUFJLENBQUM7UUFDNUQsSUFBSSxJQUFJLENBQUMsTUFBTSxJQUFJLEdBQUc7WUFBRSxPQUFPLElBQUksQ0FBQztRQUNwQyxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7Q0FDRiJ9
|