@certd/acme-client 1.20.14 → 1.20.16
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 +19 -20
- package/package.json +11 -11
- package/src/api.js +1 -17
- package/src/auto.js +36 -30
- package/src/axios.js +1 -4
- package/src/client.js +18 -42
- package/src/crypto/forge.js +18 -31
- package/src/crypto/index.js +25 -40
- package/src/http.js +3 -14
- package/src/index.js +4 -8
- package/src/logger.js +1 -3
- package/src/util.js +2 -13
- package/src/verify.js +1 -6
- package/src/wait.js +9 -0
- package/types/index.d.ts +0 -6
- package/types/index.test-d.ts +0 -1
- package/types/rfc8555.d.ts +0 -4
package/README.md
CHANGED
|
@@ -9,13 +9,13 @@ This module is written to handle communication with a Boulder/Let's Encrypt-styl
|
|
|
9
9
|
|
|
10
10
|
## Compatibility
|
|
11
11
|
|
|
12
|
-
| acme-client
|
|
13
|
-
|
|
|
14
|
-
| v5.x
|
|
15
|
-
| v4.x
|
|
16
|
-
| v3.x
|
|
17
|
-
| v2.x
|
|
18
|
-
| v1.x
|
|
12
|
+
| acme-client | Node.js | |
|
|
13
|
+
| ----------- | ------- | ----------------------------------------- |
|
|
14
|
+
| v5.x | >= v16 | [Upgrade guide](docs/upgrade-v5.md) |
|
|
15
|
+
| v4.x | >= v10 | [Changelog](CHANGELOG.md#v400-2020-05-29) |
|
|
16
|
+
| v3.x | >= v8 | [Changelog](CHANGELOG.md#v300-2019-07-13) |
|
|
17
|
+
| v2.x | >= v4 | [Changelog](CHANGELOG.md#v200-2018-04-02) |
|
|
18
|
+
| v1.x | >= v4 | [Changelog](CHANGELOG.md#v100-2017-10-20) |
|
|
19
19
|
|
|
20
20
|
## Table of contents
|
|
21
21
|
|
|
@@ -49,7 +49,7 @@ const accountPrivateKey = '<PEM encoded private key>';
|
|
|
49
49
|
|
|
50
50
|
const client = new acme.Client({
|
|
51
51
|
directoryUrl: acme.directory.letsencrypt.staging,
|
|
52
|
-
accountKey: accountPrivateKey
|
|
52
|
+
accountKey: accountPrivateKey,
|
|
53
53
|
});
|
|
54
54
|
```
|
|
55
55
|
|
|
@@ -75,8 +75,8 @@ const client = new acme.Client({
|
|
|
75
75
|
accountKey: accountPrivateKey,
|
|
76
76
|
externalAccountBinding: {
|
|
77
77
|
kid: 'YOUR-EAB-KID',
|
|
78
|
-
hmacKey: 'YOUR-EAB-HMAC-KEY'
|
|
79
|
-
}
|
|
78
|
+
hmacKey: 'YOUR-EAB-HMAC-KEY',
|
|
79
|
+
},
|
|
80
80
|
});
|
|
81
81
|
```
|
|
82
82
|
|
|
@@ -90,7 +90,7 @@ In some cases, for example with some EAB providers, this account creation step m
|
|
|
90
90
|
const client = new acme.Client({
|
|
91
91
|
directoryUrl: acme.directory.letsencrypt.staging,
|
|
92
92
|
accountKey: accountPrivateKey,
|
|
93
|
-
accountUrl: 'https://acme-v02.api.letsencrypt.org/acme/acct/12345678'
|
|
93
|
+
accountUrl: 'https://acme-v02.api.letsencrypt.org/acme/acct/12345678',
|
|
94
94
|
});
|
|
95
95
|
```
|
|
96
96
|
|
|
@@ -113,8 +113,7 @@ const privateRsaKey = await acme.crypto.createPrivateRsaKey();
|
|
|
113
113
|
const privateEcdsaKey = await acme.crypto.createPrivateEcdsaKey();
|
|
114
114
|
|
|
115
115
|
const [certificateKey, certificateCsr] = await acme.crypto.createCsr({
|
|
116
|
-
|
|
117
|
-
altNames: ['example.com']
|
|
116
|
+
altNames: ['example.com', '*.example.com'],
|
|
118
117
|
});
|
|
119
118
|
```
|
|
120
119
|
|
|
@@ -139,7 +138,7 @@ const autoOpts = {
|
|
|
139
138
|
email: 'test@example.com',
|
|
140
139
|
termsOfServiceAgreed: true,
|
|
141
140
|
challengeCreateFn: async (authz, challenge, keyAuthorization) => {},
|
|
142
|
-
challengeRemoveFn: async (authz, challenge, keyAuthorization) => {}
|
|
141
|
+
challengeRemoveFn: async (authz, challenge, keyAuthorization) => {},
|
|
143
142
|
};
|
|
144
143
|
|
|
145
144
|
const certificate = await client.auto(autoOpts);
|
|
@@ -156,7 +155,7 @@ To modify challenge priority, provide a list of challenge types in `challengePri
|
|
|
156
155
|
```js
|
|
157
156
|
await client.auto({
|
|
158
157
|
...,
|
|
159
|
-
challengePriority: ['http-01', 'dns-01']
|
|
158
|
+
challengePriority: ['http-01', 'dns-01'],
|
|
160
159
|
});
|
|
161
160
|
```
|
|
162
161
|
|
|
@@ -171,7 +170,7 @@ To completely disable `acme-client`s internal challenge verification, enable `sk
|
|
|
171
170
|
```js
|
|
172
171
|
await client.auto({
|
|
173
172
|
...,
|
|
174
|
-
skipChallengeVerification: true
|
|
173
|
+
skipChallengeVerification: true,
|
|
175
174
|
});
|
|
176
175
|
```
|
|
177
176
|
|
|
@@ -185,14 +184,14 @@ For more fine-grained control you can interact with the ACME API using the metho
|
|
|
185
184
|
```js
|
|
186
185
|
const account = await client.createAccount({
|
|
187
186
|
termsOfServiceAgreed: true,
|
|
188
|
-
contact: ['mailto:test@example.com']
|
|
187
|
+
contact: ['mailto:test@example.com'],
|
|
189
188
|
});
|
|
190
189
|
|
|
191
190
|
const order = await client.createOrder({
|
|
192
191
|
identifiers: [
|
|
193
192
|
{ type: 'dns', value: 'example.com' },
|
|
194
|
-
{ type: 'dns', value: '*.example.com' }
|
|
195
|
-
]
|
|
193
|
+
{ type: 'dns', value: '*.example.com' },
|
|
194
|
+
],
|
|
196
195
|
});
|
|
197
196
|
```
|
|
198
197
|
|
|
@@ -207,7 +206,7 @@ const acme = require('acme-client');
|
|
|
207
206
|
|
|
208
207
|
acme.axios.defaults.proxy = {
|
|
209
208
|
host: '127.0.0.1',
|
|
210
|
-
port: 9000
|
|
209
|
+
port: 9000,
|
|
211
210
|
};
|
|
212
211
|
```
|
|
213
212
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Simple and unopinionated ACME client",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "nmorsman",
|
|
6
|
-
"version": "1.20.
|
|
6
|
+
"version": "1.20.16",
|
|
7
7
|
"main": "src/index.js",
|
|
8
8
|
"types": "types/index.d.ts",
|
|
9
9
|
"license": "MIT",
|
|
@@ -16,24 +16,24 @@
|
|
|
16
16
|
"types"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@peculiar/x509": "^1.
|
|
19
|
+
"@peculiar/x509": "^1.10.0",
|
|
20
20
|
"asn1js": "^3.0.5",
|
|
21
|
-
"axios": "^1.
|
|
21
|
+
"axios": "^1.7.2",
|
|
22
22
|
"debug": "^4.1.1",
|
|
23
23
|
"https-proxy-agent": "^7.0.4",
|
|
24
24
|
"node-forge": "^1.3.1"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/node": "^20.
|
|
27
|
+
"@types/node": "^20.12.12",
|
|
28
28
|
"chai": "^4.4.1",
|
|
29
|
-
"chai-as-promised": "^7.1.
|
|
30
|
-
"eslint": "^8.
|
|
29
|
+
"chai-as-promised": "^7.1.2",
|
|
30
|
+
"eslint": "^8.57.0",
|
|
31
31
|
"eslint-config-airbnb-base": "^15.0.0",
|
|
32
32
|
"eslint-plugin-import": "^2.29.1",
|
|
33
|
-
"jsdoc-to-markdown": "^8.0.
|
|
34
|
-
"mocha": "^10.
|
|
35
|
-
"nock": "^13.5.
|
|
36
|
-
"tsd": "^0.
|
|
33
|
+
"jsdoc-to-markdown": "^8.0.1",
|
|
34
|
+
"mocha": "^10.4.0",
|
|
35
|
+
"nock": "^13.5.4",
|
|
36
|
+
"tsd": "^0.31.0",
|
|
37
37
|
"typescript": "^4.8.4",
|
|
38
38
|
"uuid": "^8.3.2"
|
|
39
39
|
},
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"bugs": {
|
|
60
60
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "d46dab4fdda738fac0be87a60a3af6037feb42d4"
|
|
63
63
|
}
|
package/src/api.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
const util = require('./util');
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
/**
|
|
9
8
|
* AcmeApi
|
|
10
9
|
*
|
|
@@ -18,7 +17,6 @@ class AcmeApi {
|
|
|
18
17
|
this.accountUrl = accountUrl;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
|
|
22
20
|
/**
|
|
23
21
|
* Get account URL
|
|
24
22
|
*
|
|
@@ -34,7 +32,6 @@ class AcmeApi {
|
|
|
34
32
|
return this.accountUrl;
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
|
|
38
35
|
/**
|
|
39
36
|
* ACME API request
|
|
40
37
|
*
|
|
@@ -59,7 +56,6 @@ class AcmeApi {
|
|
|
59
56
|
return resp;
|
|
60
57
|
}
|
|
61
58
|
|
|
62
|
-
|
|
63
59
|
/**
|
|
64
60
|
* ACME API request by resource name helper
|
|
65
61
|
*
|
|
@@ -78,7 +74,6 @@ class AcmeApi {
|
|
|
78
74
|
return this.apiRequest(resourceUrl, payload, validStatusCodes, { includeJwsKid, includeExternalAccountBinding });
|
|
79
75
|
}
|
|
80
76
|
|
|
81
|
-
|
|
82
77
|
/**
|
|
83
78
|
* Get Terms of Service URL if available
|
|
84
79
|
*
|
|
@@ -91,7 +86,6 @@ class AcmeApi {
|
|
|
91
86
|
return this.http.getMetaField('termsOfService');
|
|
92
87
|
}
|
|
93
88
|
|
|
94
|
-
|
|
95
89
|
/**
|
|
96
90
|
* Create new account
|
|
97
91
|
*
|
|
@@ -104,7 +98,7 @@ class AcmeApi {
|
|
|
104
98
|
async createAccount(data) {
|
|
105
99
|
const resp = await this.apiResourceRequest('newAccount', data, [200, 201], {
|
|
106
100
|
includeJwsKid: false,
|
|
107
|
-
includeExternalAccountBinding: (data.onlyReturnExisting !== true)
|
|
101
|
+
includeExternalAccountBinding: (data.onlyReturnExisting !== true),
|
|
108
102
|
});
|
|
109
103
|
|
|
110
104
|
/* Set account URL */
|
|
@@ -115,7 +109,6 @@ class AcmeApi {
|
|
|
115
109
|
return resp;
|
|
116
110
|
}
|
|
117
111
|
|
|
118
|
-
|
|
119
112
|
/**
|
|
120
113
|
* Update account
|
|
121
114
|
*
|
|
@@ -129,7 +122,6 @@ class AcmeApi {
|
|
|
129
122
|
return this.apiRequest(this.getAccountUrl(), data, [200, 202]);
|
|
130
123
|
}
|
|
131
124
|
|
|
132
|
-
|
|
133
125
|
/**
|
|
134
126
|
* Update account key
|
|
135
127
|
*
|
|
@@ -143,7 +135,6 @@ class AcmeApi {
|
|
|
143
135
|
return this.apiResourceRequest('keyChange', data, [200]);
|
|
144
136
|
}
|
|
145
137
|
|
|
146
|
-
|
|
147
138
|
/**
|
|
148
139
|
* Create new order
|
|
149
140
|
*
|
|
@@ -157,7 +148,6 @@ class AcmeApi {
|
|
|
157
148
|
return this.apiResourceRequest('newOrder', data, [201]);
|
|
158
149
|
}
|
|
159
150
|
|
|
160
|
-
|
|
161
151
|
/**
|
|
162
152
|
* Get order
|
|
163
153
|
*
|
|
@@ -171,7 +161,6 @@ class AcmeApi {
|
|
|
171
161
|
return this.apiRequest(url, null, [200]);
|
|
172
162
|
}
|
|
173
163
|
|
|
174
|
-
|
|
175
164
|
/**
|
|
176
165
|
* Finalize order
|
|
177
166
|
*
|
|
@@ -186,7 +175,6 @@ class AcmeApi {
|
|
|
186
175
|
return this.apiRequest(url, data, [200]);
|
|
187
176
|
}
|
|
188
177
|
|
|
189
|
-
|
|
190
178
|
/**
|
|
191
179
|
* Get identifier authorization
|
|
192
180
|
*
|
|
@@ -200,7 +188,6 @@ class AcmeApi {
|
|
|
200
188
|
return this.apiRequest(url, null, [200]);
|
|
201
189
|
}
|
|
202
190
|
|
|
203
|
-
|
|
204
191
|
/**
|
|
205
192
|
* Update identifier authorization
|
|
206
193
|
*
|
|
@@ -215,7 +202,6 @@ class AcmeApi {
|
|
|
215
202
|
return this.apiRequest(url, data, [200]);
|
|
216
203
|
}
|
|
217
204
|
|
|
218
|
-
|
|
219
205
|
/**
|
|
220
206
|
* Complete challenge
|
|
221
207
|
*
|
|
@@ -230,7 +216,6 @@ class AcmeApi {
|
|
|
230
216
|
return this.apiRequest(url, data, [200]);
|
|
231
217
|
}
|
|
232
218
|
|
|
233
|
-
|
|
234
219
|
/**
|
|
235
220
|
* Revoke certificate
|
|
236
221
|
*
|
|
@@ -245,6 +230,5 @@ class AcmeApi {
|
|
|
245
230
|
}
|
|
246
231
|
}
|
|
247
232
|
|
|
248
|
-
|
|
249
233
|
/* Export API */
|
|
250
234
|
module.exports = AcmeApi;
|
package/src/auto.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const { readCsrDomains } = require('./crypto');
|
|
6
6
|
const { log } = require('./logger');
|
|
7
|
+
const { wait } = require('./wait');
|
|
7
8
|
|
|
8
9
|
const defaultOpts = {
|
|
9
10
|
csr: null,
|
|
@@ -13,10 +14,9 @@ const defaultOpts = {
|
|
|
13
14
|
skipChallengeVerification: false,
|
|
14
15
|
challengePriority: ['http-01', 'dns-01'],
|
|
15
16
|
challengeCreateFn: async () => { throw new Error('Missing challengeCreateFn()'); },
|
|
16
|
-
challengeRemoveFn: async () => { throw new Error('Missing challengeRemoveFn()'); }
|
|
17
|
+
challengeRemoveFn: async () => { throw new Error('Missing challengeRemoveFn()'); },
|
|
17
18
|
};
|
|
18
19
|
|
|
19
|
-
|
|
20
20
|
/**
|
|
21
21
|
* ACME client auto mode
|
|
22
22
|
*
|
|
@@ -25,8 +25,8 @@ const defaultOpts = {
|
|
|
25
25
|
* @returns {Promise<buffer>} Certificate
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
module.exports = async
|
|
29
|
-
const opts =
|
|
28
|
+
module.exports = async (client, userOpts) => {
|
|
29
|
+
const opts = { ...defaultOpts, ...userOpts };
|
|
30
30
|
const accountPayload = { termsOfServiceAgreed: opts.termsOfServiceAgreed };
|
|
31
31
|
|
|
32
32
|
if (!Buffer.isBuffer(opts.csr)) {
|
|
@@ -37,7 +37,6 @@ module.exports = async function(client, userOpts) {
|
|
|
37
37
|
accountPayload.contact = [`mailto:${opts.email}`];
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
40
|
/**
|
|
42
41
|
* Register account
|
|
43
42
|
*/
|
|
@@ -53,7 +52,6 @@ module.exports = async function(client, userOpts) {
|
|
|
53
52
|
await client.createAccount(accountPayload);
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
|
|
57
55
|
/**
|
|
58
56
|
* Parse domains from CSR
|
|
59
57
|
*/
|
|
@@ -64,7 +62,6 @@ module.exports = async function(client, userOpts) {
|
|
|
64
62
|
|
|
65
63
|
log(`[auto] Resolved ${uniqueDomains.length} unique domains from parsing the Certificate Signing Request`);
|
|
66
64
|
|
|
67
|
-
|
|
68
65
|
/**
|
|
69
66
|
* Place order
|
|
70
67
|
*/
|
|
@@ -76,7 +73,6 @@ module.exports = async function(client, userOpts) {
|
|
|
76
73
|
|
|
77
74
|
log(`[auto] Placed certificate order successfully, received ${authorizations.length} identity authorizations`);
|
|
78
75
|
|
|
79
|
-
|
|
80
76
|
/**
|
|
81
77
|
* Resolve and satisfy challenges
|
|
82
78
|
*/
|
|
@@ -118,7 +114,18 @@ module.exports = async function(client, userOpts) {
|
|
|
118
114
|
let recordItem = null;
|
|
119
115
|
try {
|
|
120
116
|
recordItem = await opts.challengeCreateFn(authz, challenge, keyAuthorization);
|
|
121
|
-
|
|
117
|
+
log(`[auto] [${d}] challengeCreateFn success`);
|
|
118
|
+
log(`[auto] [${d}] add challengeRemoveFn()`);
|
|
119
|
+
clearTasks.push(async () => {
|
|
120
|
+
/* Trigger challengeRemoveFn(), suppress errors */
|
|
121
|
+
log(`[auto] [${d}] Trigger challengeRemoveFn()`);
|
|
122
|
+
try {
|
|
123
|
+
await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem);
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
log(`[auto] [${d}] challengeRemoveFn threw error: ${e.message}`);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
122
129
|
// throw new Error('测试异常');
|
|
123
130
|
/* Challenge verification */
|
|
124
131
|
if (opts.skipChallengeVerification === true) {
|
|
@@ -140,19 +147,6 @@ module.exports = async function(client, userOpts) {
|
|
|
140
147
|
log(`[auto] [${d}] challengeCreateFn threw error: ${e.message}`);
|
|
141
148
|
throw e;
|
|
142
149
|
}
|
|
143
|
-
finally {
|
|
144
|
-
log(`[auto] [${d}] add challengeRemoveFn()`);
|
|
145
|
-
clearTasks.push(async () => {
|
|
146
|
-
/* Trigger challengeRemoveFn(), suppress errors */
|
|
147
|
-
log(`[auto] [${d}] Trigger challengeRemoveFn()`);
|
|
148
|
-
try {
|
|
149
|
-
await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem);
|
|
150
|
-
}
|
|
151
|
-
catch (e) {
|
|
152
|
-
log(`[auto] [${d}] challengeRemoveFn threw error: ${e.message}`);
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
150
|
}
|
|
157
151
|
catch (e) {
|
|
158
152
|
/* Deactivate pending authz when unable to complete challenge */
|
|
@@ -177,7 +171,6 @@ module.exports = async function(client, userOpts) {
|
|
|
177
171
|
await challengeFunc(authz);
|
|
178
172
|
});
|
|
179
173
|
|
|
180
|
-
|
|
181
174
|
function runAllPromise(tasks) {
|
|
182
175
|
let promise = Promise.resolve();
|
|
183
176
|
tasks.forEach((task) => {
|
|
@@ -186,14 +179,20 @@ module.exports = async function(client, userOpts) {
|
|
|
186
179
|
return promise;
|
|
187
180
|
}
|
|
188
181
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
182
|
+
async function runPromisePa(tasks) {
|
|
183
|
+
const results = [];
|
|
184
|
+
// eslint-disable-next-line no-await-in-loop,no-restricted-syntax
|
|
185
|
+
for (const task of tasks) {
|
|
186
|
+
results.push(task());
|
|
187
|
+
// eslint-disable-next-line no-await-in-loop
|
|
188
|
+
await wait(10000);
|
|
189
|
+
}
|
|
190
|
+
return Promise.all(results);
|
|
191
|
+
}
|
|
193
192
|
|
|
194
193
|
try {
|
|
195
194
|
log('开始challenge');
|
|
196
|
-
await
|
|
195
|
+
await runPromisePa(challengePromises);
|
|
197
196
|
|
|
198
197
|
log('challenge结束');
|
|
199
198
|
|
|
@@ -210,11 +209,18 @@ module.exports = async function(client, userOpts) {
|
|
|
210
209
|
}
|
|
211
210
|
catch (e) {
|
|
212
211
|
log('证书申请失败');
|
|
213
|
-
|
|
212
|
+
log(e);
|
|
213
|
+
throw new Error(`证书申请失败:${e.message}`);
|
|
214
214
|
}
|
|
215
215
|
finally {
|
|
216
216
|
log(`清理challenge痕迹,length:${clearTasks.length}`);
|
|
217
|
-
|
|
217
|
+
try {
|
|
218
|
+
await runAllPromise(clearTasks);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
log('清理challenge失败');
|
|
222
|
+
log(e);
|
|
223
|
+
}
|
|
218
224
|
}
|
|
219
225
|
|
|
220
226
|
// try {
|
package/src/axios.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
const axios = require('axios');
|
|
6
6
|
const pkg = require('./../package.json');
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
/**
|
|
10
9
|
* Instance
|
|
11
10
|
*/
|
|
@@ -19,9 +18,8 @@ instance.defaults.headers.common['User-Agent'] = `node-${pkg.name}/${pkg.version
|
|
|
19
18
|
instance.defaults.acmeSettings = {
|
|
20
19
|
httpChallengePort: 80,
|
|
21
20
|
httpsChallengePort: 443,
|
|
22
|
-
tlsAlpnChallengePort: 443
|
|
21
|
+
tlsAlpnChallengePort: 443,
|
|
23
22
|
};
|
|
24
|
-
|
|
25
23
|
// instance.defaults.proxy = {
|
|
26
24
|
// host: '192.168.34.139',
|
|
27
25
|
// port: 10811
|
|
@@ -35,7 +33,6 @@ instance.defaults.acmeSettings = {
|
|
|
35
33
|
|
|
36
34
|
instance.defaults.adapter = 'http';
|
|
37
35
|
|
|
38
|
-
|
|
39
36
|
/**
|
|
40
37
|
* Export instance
|
|
41
38
|
*/
|