lob 7.0.1 → 8.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 +27 -9
- package/lib/index.js +7 -7
- package/lib/resources/bankAccounts.js +1 -1
- package/lib/resources/cards.js +1 -1
- package/lib/resources/letters.js +1 -1
- package/lib/resources/postcards.js +1 -1
- package/lib/resources/resourceBase.js +101 -39
- package/lib/resources/selfMailers.js +1 -1
- package/lib/resources/templates.js +2 -2
- package/lib/resources/usReverseGeocodeLookups.js +1 -1
- package/package.json +27 -17
package/README.md
CHANGED
|
@@ -3,12 +3,8 @@
|
|
|
3
3
|
[downloads-image]: http://img.shields.io/npm/dm/lob.svg
|
|
4
4
|
[npm-url]: https://npmjs.org/package/lob
|
|
5
5
|
[npm-image]: https://badge.fury.io/js/lob.svg
|
|
6
|
-
[travis-url]: https://travis-ci.org/lob/lob-node
|
|
7
|
-
[travis-image]: https://travis-ci.org/lob/lob-node.svg?branch=master
|
|
8
|
-
[depstat-url]: https://david-dm.org/Lob/Lob-node
|
|
9
|
-
[depstat-image]: https://david-dm.org/Lob/Lob-node.svg
|
|
10
6
|
|
|
11
|
-
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url]
|
|
7
|
+
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [](https://github.com/lob/lob-node/actions/workflows/run_tests.yml) [](https://coveralls.io/r/lob/lob-node?branch=master)
|
|
12
8
|
|
|
13
9
|
Node.js wrapper for the [Lob.com](https://lob.com) API. See full Lob.com documentation [here](https://lob.com/docs/node).
|
|
14
10
|
******
|
|
@@ -63,6 +59,8 @@ $ git clone git@github.com:lob/lob-node.git
|
|
|
63
59
|
$ npm install
|
|
64
60
|
```
|
|
65
61
|
|
|
62
|
+
**Requirements:** Node.js >= 24.15.0, npm >= 11.5.1
|
|
63
|
+
|
|
66
64
|
### Usage
|
|
67
65
|
```javascript
|
|
68
66
|
const Lob = require('lob')('YOUR API KEY');
|
|
@@ -146,18 +144,38 @@ To contribute, please see the [CONTRIBUTING.md](https://github.com/lob/lob-node/
|
|
|
146
144
|
|
|
147
145
|
## Testing
|
|
148
146
|
|
|
149
|
-
To run
|
|
147
|
+
To run unit tests with coverage:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
npm test
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
To run integration tests (requires API keys):
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
TEST_API_KEY=your_test_key npm run test:integration
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Some integration tests require a live API key:
|
|
150
160
|
|
|
151
161
|
```
|
|
152
|
-
|
|
162
|
+
TEST_API_KEY=your_test_key LIVE_API_KEY=your_live_key npm run test:integration
|
|
153
163
|
```
|
|
154
164
|
|
|
155
|
-
To run
|
|
165
|
+
To run lint:
|
|
156
166
|
|
|
157
167
|
```
|
|
158
|
-
|
|
168
|
+
npm run lint
|
|
159
169
|
```
|
|
160
170
|
|
|
171
|
+
To check for vulnerabilities:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
npm audit
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Target: zero `moderate` and zero `high` findings. Current status: **0 vulnerabilities**.
|
|
178
|
+
|
|
161
179
|
=======================
|
|
162
180
|
|
|
163
181
|
Copyright © 2013 Lob.com
|
package/lib/index.js
CHANGED
|
@@ -6,10 +6,10 @@ const Resources = require('./resources');
|
|
|
6
6
|
const LOB_HOST = process.env.LOB_HOST || 'https://api.lob.com/v1/';
|
|
7
7
|
const LOB_USERAGENT = `Lob/v1 NodeBindings/${ClientVersion}`;
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const LobClient = function (apiKey, options) {
|
|
10
10
|
|
|
11
|
-
if (!(this instanceof
|
|
12
|
-
return new
|
|
11
|
+
if (!(this instanceof LobClient)) {
|
|
12
|
+
return new LobClient(apiKey, options);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
this.resourceBase = require('./resources/resourceBase');
|
|
@@ -28,7 +28,7 @@ const Lob = function (apiKey, options) {
|
|
|
28
28
|
|
|
29
29
|
if (options && typeof options === 'object') {
|
|
30
30
|
|
|
31
|
-
if (Object.prototype.hasOwnProperty
|
|
31
|
+
if (Reflect.apply(Object.prototype.hasOwnProperty, options, ['apiVersion'])) {
|
|
32
32
|
this.options.headers['Lob-Version'] = options.apiVersion;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -41,7 +41,7 @@ const Lob = function (apiKey, options) {
|
|
|
41
41
|
this._initResources();
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
-
(function () {
|
|
44
|
+
Reflect.apply(function () {
|
|
45
45
|
|
|
46
46
|
this._initResources = function () {
|
|
47
47
|
const services = Object.keys(Resources);
|
|
@@ -52,6 +52,6 @@ const Lob = function (apiKey, options) {
|
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
}
|
|
55
|
+
}, LobClient.prototype, []);
|
|
56
56
|
|
|
57
|
-
module.exports =
|
|
57
|
+
module.exports = LobClient;
|
|
@@ -30,7 +30,7 @@ class BankAccounts extends ResourceBase {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
verify (id, params, callback) {
|
|
33
|
-
return this._transmit('POST', `${id}/verify`, null, params, callback);
|
|
33
|
+
return this._transmit('POST', `${encodeURIComponent(id)}/verify`, null, params, callback);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
}
|
package/lib/resources/cards.js
CHANGED
package/lib/resources/letters.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const axios = require('axios');
|
|
4
|
+
const FormDataLib = require('form-data');
|
|
5
|
+
const Stream = require('stream');
|
|
6
|
+
|
|
7
|
+
// Helper to create a compatibility response object from axios response
|
|
8
|
+
const createCompatResponse = (axiosResp) => Object.assign({}, axiosResp, {
|
|
9
|
+
statusCode: axiosResp.status,
|
|
10
|
+
statusMessage: axiosResp.statusText
|
|
11
|
+
});
|
|
5
12
|
|
|
6
13
|
class ResourceBase {
|
|
7
14
|
|
|
@@ -20,94 +27,149 @@ class ResourceBase {
|
|
|
20
27
|
|
|
21
28
|
const allHeaders = Object.assign({}, this.config.headers, headers);
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
// Encode URI to prevent path traversal, but only for simple IDs
|
|
31
|
+
// Composite paths (containing /) should be encoded by the caller
|
|
32
|
+
const encodedUri = uri && !uri.includes('/') ? encodeURIComponent(uri) : uri;
|
|
33
|
+
|
|
34
|
+
const config = {
|
|
35
|
+
url: `${this.uri}${encodedUri ? `/${encodedUri}` : ''}`,
|
|
25
36
|
method,
|
|
26
|
-
auth: {
|
|
37
|
+
auth: { username: this.config.apiKey, password: '' },
|
|
27
38
|
headers: allHeaders,
|
|
28
|
-
|
|
39
|
+
validateStatus: () => true
|
|
29
40
|
};
|
|
30
41
|
|
|
31
42
|
if (this.config.agent) {
|
|
32
|
-
|
|
43
|
+
const isHttps = this.uri.startsWith('https');
|
|
44
|
+
if (isHttps) {
|
|
45
|
+
config.httpsAgent = this.config.agent;
|
|
46
|
+
} else {
|
|
47
|
+
config.httpAgent = this.config.agent;
|
|
48
|
+
}
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
let isMultiPartForm = false;
|
|
52
|
+
let requiresJson = false;
|
|
36
53
|
|
|
37
|
-
|
|
54
|
+
Object.keys(form || {}).forEach((key) => {
|
|
38
55
|
if (form[key] === undefined) {
|
|
39
|
-
|
|
56
|
+
Reflect.deleteProperty(form, key);
|
|
40
57
|
}
|
|
41
58
|
if (form[key] === true || form[key] === false) {
|
|
42
59
|
form[key] = form[key].toString();
|
|
43
60
|
}
|
|
44
|
-
}
|
|
61
|
+
});
|
|
45
62
|
|
|
46
|
-
|
|
63
|
+
Object.keys(form || {}).forEach((param) => {
|
|
47
64
|
const val = form[param];
|
|
48
65
|
|
|
49
66
|
if (val instanceof Stream.Stream) {
|
|
50
67
|
isMultiPartForm = true;
|
|
51
|
-
break;
|
|
52
68
|
}
|
|
53
69
|
|
|
54
|
-
if (val !== undefined && val !== null && Object.prototype.hasOwnProperty
|
|
70
|
+
if (val !== undefined && val !== null && Reflect.apply(Object.prototype.hasOwnProperty, val, ['value'])) {
|
|
55
71
|
isMultiPartForm = true;
|
|
56
|
-
break;
|
|
57
72
|
}
|
|
58
|
-
|
|
73
|
+
|
|
74
|
+
// Check if array contains objects (requires JSON encoding)
|
|
75
|
+
if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'object') {
|
|
76
|
+
requiresJson = true;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
59
79
|
|
|
60
80
|
if (qs) {
|
|
61
|
-
|
|
81
|
+
config.params = qs;
|
|
62
82
|
}
|
|
63
83
|
|
|
64
84
|
if (form) {
|
|
65
85
|
if (isMultiPartForm) {
|
|
66
|
-
|
|
86
|
+
const formData = new FormDataLib();
|
|
87
|
+
|
|
88
|
+
Object.keys(form).forEach((key) => {
|
|
89
|
+
const val = form[key];
|
|
90
|
+
|
|
91
|
+
if (val instanceof Stream.Stream) {
|
|
92
|
+
formData.append(key, val);
|
|
93
|
+
} else if (val !== undefined && val !== null && Reflect.apply(Object.prototype.hasOwnProperty, val, ['value'])) {
|
|
94
|
+
formData.append(key, val.value, val.options);
|
|
95
|
+
} else if (val !== undefined && val !== null) {
|
|
96
|
+
formData.append(key, val);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
config.data = formData;
|
|
101
|
+
config.headers = Object.assign({}, config.headers, formData.getHeaders());
|
|
102
|
+
} else if (requiresJson) {
|
|
103
|
+
// Use JSON for requests with nested objects (e.g., bulk verifications)
|
|
104
|
+
config.data = form;
|
|
105
|
+
config.headers['Content-Type'] = 'application/json';
|
|
67
106
|
} else {
|
|
68
|
-
|
|
107
|
+
const params = new URLSearchParams();
|
|
108
|
+
Object.keys(form).forEach((key) => {
|
|
109
|
+
const val = form[key];
|
|
110
|
+
if (val !== undefined && val !== null) {
|
|
111
|
+
if (Array.isArray(val)) {
|
|
112
|
+
// Handle arrays: amounts[0]=23&amounts[1]=34
|
|
113
|
+
val.forEach((item, index) => {
|
|
114
|
+
params.append(`${key}[${index}]`, item);
|
|
115
|
+
});
|
|
116
|
+
} else {
|
|
117
|
+
params.append(key, val);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
config.data = params;
|
|
122
|
+
config.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
69
123
|
}
|
|
70
124
|
}
|
|
71
125
|
|
|
72
|
-
const promise =
|
|
73
|
-
|
|
126
|
+
const promise = axios(config)
|
|
127
|
+
.then((resp) => {
|
|
74
128
|
/* istanbul ignore next */
|
|
75
|
-
body =
|
|
76
|
-
|
|
77
|
-
/* istanbul ignore next */
|
|
78
|
-
if (err) {
|
|
79
|
-
return reject(err);
|
|
80
|
-
}
|
|
129
|
+
const body = resp.data || {};
|
|
81
130
|
|
|
82
131
|
if (body && body.error) {
|
|
83
132
|
const error = new Error(body.error.message);
|
|
84
133
|
error.status_code = body.error.status_code;
|
|
85
|
-
error._response = resp;
|
|
86
|
-
|
|
134
|
+
error._response = createCompatResponse(resp);
|
|
135
|
+
throw error;
|
|
87
136
|
}
|
|
88
137
|
|
|
89
|
-
if (resp
|
|
90
|
-
const error = new Error(resp.
|
|
91
|
-
error.status_code = resp.
|
|
92
|
-
error._response = resp;
|
|
93
|
-
|
|
138
|
+
if (resp.status >= 500) {
|
|
139
|
+
const error = new Error(resp.statusText);
|
|
140
|
+
error.status_code = resp.status;
|
|
141
|
+
error._response = createCompatResponse(resp);
|
|
142
|
+
throw error;
|
|
94
143
|
}
|
|
95
144
|
|
|
96
|
-
|
|
145
|
+
const compatResponse = createCompatResponse(resp);
|
|
146
|
+
|
|
147
|
+
Reflect.defineProperty(body, '_response', {
|
|
97
148
|
enumerable: false,
|
|
98
149
|
writable: false,
|
|
99
|
-
value:
|
|
150
|
+
value: compatResponse
|
|
100
151
|
});
|
|
101
152
|
|
|
102
|
-
return
|
|
153
|
+
return body;
|
|
154
|
+
})
|
|
155
|
+
.catch((err) => {
|
|
156
|
+
// Re-throw errors that were already processed in .then() block
|
|
157
|
+
// (they have _response attached)
|
|
158
|
+
/* istanbul ignore next */
|
|
159
|
+
if (err._response) {
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Network errors (no response from server) - just re-throw
|
|
164
|
+
// Note: HTTP status errors are handled in .then() since validateStatus: () => true
|
|
165
|
+
throw err;
|
|
103
166
|
});
|
|
104
|
-
})
|
|
105
167
|
|
|
106
168
|
if (callback) {
|
|
107
|
-
promise.then(body => callback(null, body), err => callback(err));
|
|
169
|
+
promise.then((body) => callback(null, body), (err) => callback(err));
|
|
108
170
|
}
|
|
109
171
|
|
|
110
|
-
return promise
|
|
172
|
+
return promise;
|
|
111
173
|
}
|
|
112
174
|
|
|
113
175
|
}
|
|
@@ -4,7 +4,7 @@ const ResourceBase = require('./resourceBase');
|
|
|
4
4
|
|
|
5
5
|
class Template extends ResourceBase {
|
|
6
6
|
constructor (config) {
|
|
7
|
-
|
|
7
|
+
super('templates', config);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
list (options, callback) {
|
|
@@ -29,4 +29,4 @@ class Template extends ResourceBase {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
module.exports = Template;
|
|
32
|
+
module.exports = Template;
|
package/package.json
CHANGED
|
@@ -10,27 +10,32 @@
|
|
|
10
10
|
"Lob.com",
|
|
11
11
|
"printing"
|
|
12
12
|
],
|
|
13
|
-
"version": "
|
|
13
|
+
"version": "8.1.0",
|
|
14
14
|
"homepage": "https://github.com/lob/lob-node",
|
|
15
15
|
"author": "Lob <support@lob.com> (https://lob.com/)",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"
|
|
17
|
+
"axios": "^1.16.1",
|
|
18
|
+
"form-data": "^4.0.5"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
21
|
+
"@eslint/js": "^10.0.1",
|
|
22
|
+
"@stylistic/eslint-plugin": "^5.10.0",
|
|
20
23
|
"agentkeepalive": "^4.1.0",
|
|
21
|
-
"chai": "^2.2
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"eslint": "^
|
|
26
|
-
"eslint-
|
|
24
|
+
"chai": "^6.2.2",
|
|
25
|
+
"cross-env": "^10.1.0",
|
|
26
|
+
"csv-parse": "^6.2.1",
|
|
27
|
+
"eslint": "^10.4.0",
|
|
28
|
+
"eslint-config-lob": "^7.0.0",
|
|
29
|
+
"eslint-plugin-jsdoc": "^62.9.0",
|
|
30
|
+
"eslint-plugin-lob": "^3.0.2",
|
|
27
31
|
"generate-changelog": "^1.0.0",
|
|
28
|
-
"
|
|
29
|
-
"
|
|
32
|
+
"globals": "^17.6.0",
|
|
33
|
+
"json-2-csv": "^5.5.10",
|
|
34
|
+
"mocha": "^11.7.5",
|
|
30
35
|
"moment": "^2.22.1",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
36
|
+
"nock": "^14.0.15",
|
|
37
|
+
"nyc": "^18.0.0",
|
|
38
|
+
"p-map": "^7.0.4"
|
|
34
39
|
},
|
|
35
40
|
"repository": {
|
|
36
41
|
"type": "git",
|
|
@@ -39,12 +44,13 @@
|
|
|
39
44
|
"bugs:": "https://github.com/lob/lob-node/issues",
|
|
40
45
|
"main": "./lib/index",
|
|
41
46
|
"engines": {
|
|
42
|
-
"node": ">=
|
|
47
|
+
"node": ">= 24.15.0",
|
|
43
48
|
"npm": ">= 11.5.1"
|
|
44
49
|
},
|
|
45
50
|
"scripts": {
|
|
46
|
-
"test": "cross-env NODE_ENV=test nyc mocha test
|
|
47
|
-
"test
|
|
51
|
+
"test": "cross-env NODE_ENV=test nyc mocha test",
|
|
52
|
+
"test:integration": "mocha --config test/integration/.mocharc.json",
|
|
53
|
+
"test-no-cover": "cross-env NODE_ENV=test mocha test",
|
|
48
54
|
"coverage": "nyc report --reporter=text",
|
|
49
55
|
"enforce": "nyc check-coverage --statements 100 --lines 100 --functions 100 --branches 100",
|
|
50
56
|
"test-lcov": "nyc report --reporter=lcov",
|
|
@@ -63,5 +69,9 @@
|
|
|
63
69
|
"lib",
|
|
64
70
|
"LICENSE.txt"
|
|
65
71
|
],
|
|
66
|
-
"license": "MIT"
|
|
72
|
+
"license": "MIT",
|
|
73
|
+
"overrides": {
|
|
74
|
+
"serialize-javascript": "^7.0.5",
|
|
75
|
+
"diff": "^9.0.0"
|
|
76
|
+
}
|
|
67
77
|
}
|