geocodio-library-node 1.11.0 → 1.13.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 +409 -1
- package/lib/__tests__/distance.test.js +365 -0
- package/lib/__tests__/geocodioLibraryNode.test.js +4 -4
- package/lib/index.d.ts +224 -0
- package/lib/index.js +493 -11
- package/package.json +3 -1
package/lib/index.js
CHANGED
|
@@ -4,18 +4,138 @@ const axios = require("axios");
|
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const FormData = require("form-data");
|
|
6
6
|
|
|
7
|
+
// Distance API Enums
|
|
8
|
+
const DistanceMode = {
|
|
9
|
+
Straightline: "straightline",
|
|
10
|
+
Driving: "driving",
|
|
11
|
+
Haversine: "haversine" // Alias for straightline (backward compat)
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const DistanceUnits = {
|
|
15
|
+
Miles: "miles",
|
|
16
|
+
Kilometers: "km",
|
|
17
|
+
Km: "km" // Alias
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const DistanceOrderBy = {
|
|
21
|
+
Distance: "distance",
|
|
22
|
+
Duration: "duration"
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const DistanceSortOrder = {
|
|
26
|
+
Asc: "asc",
|
|
27
|
+
Desc: "desc"
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Coordinate class for distance calculations
|
|
31
|
+
class Coordinate {
|
|
32
|
+
constructor(lat, lng, id) {
|
|
33
|
+
if (typeof lat !== "number" || isNaN(lat)) {
|
|
34
|
+
throw new Error(`Latitude must be a number, got ${typeof lat}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof lng !== "number" || isNaN(lng)) {
|
|
38
|
+
throw new Error(`Longitude must be a number, got ${typeof lng}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (lat < -90 || lat > 90) {
|
|
42
|
+
throw new Error(`Latitude must be between -90 and 90, got ${lat}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (lng < -180 || lng > 180) {
|
|
46
|
+
throw new Error(`Longitude must be between -180 and 180, got ${lng}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.lat = lat;
|
|
50
|
+
this.lng = lng;
|
|
51
|
+
this.id = id || undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static from(input) {
|
|
55
|
+
if (input instanceof Coordinate) {
|
|
56
|
+
return input;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof input === "string") {
|
|
60
|
+
return Coordinate.fromString(input);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(input)) {
|
|
64
|
+
return Coordinate.fromArray(input);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof input === "object" && input !== null) {
|
|
68
|
+
return new Coordinate(input.lat, input.lng, input.id);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
throw new Error(`Invalid coordinate input: ${input}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static fromString(input) {
|
|
75
|
+
const parts = input.split(",").map(p => p.trim());
|
|
76
|
+
if (parts.length < 2) {
|
|
77
|
+
throw new Error(`Invalid coordinate string: ${input}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const lat = parseFloat(parts[0]);
|
|
81
|
+
const lng = parseFloat(parts[1]);
|
|
82
|
+
const id = parts[2] || undefined;
|
|
83
|
+
|
|
84
|
+
if (isNaN(lat) || isNaN(lng)) {
|
|
85
|
+
throw new Error(`Invalid coordinate values in string: ${input}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return new Coordinate(lat, lng, id);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static fromArray(input) {
|
|
92
|
+
if (input.length < 2) {
|
|
93
|
+
throw new Error(`Coordinate array must have at least 2 elements`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const lat = parseFloat(input[0]);
|
|
97
|
+
const lng = parseFloat(input[1]);
|
|
98
|
+
const id = input[2] === undefined ? undefined : String(input[2]);
|
|
99
|
+
|
|
100
|
+
return new Coordinate(lat, lng, id);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// For GET requests (string format): "lat,lng" or "lat,lng,id"
|
|
104
|
+
toString() {
|
|
105
|
+
let str = `${this.lat},${this.lng}`;
|
|
106
|
+
if (this.id !== undefined) {
|
|
107
|
+
str += `,${this.id}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return str;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// For POST requests (object format): {lat, lng, id?}
|
|
114
|
+
toObject() {
|
|
115
|
+
const obj = {
|
|
116
|
+
lat: this.lat,
|
|
117
|
+
lng: this.lng
|
|
118
|
+
};
|
|
119
|
+
if (this.id !== undefined) {
|
|
120
|
+
obj.id = this.id;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return obj;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
7
127
|
class Geocodio {
|
|
8
128
|
constructor(apiKey, hostname, apiVersion) {
|
|
9
129
|
this.apiKey = apiKey || process.env.GEOCODIO_API_KEY || null;
|
|
10
130
|
this.hostname =
|
|
11
131
|
hostname || process.env.GEOCODIO_HOSTNAME || "api.geocod.io";
|
|
12
|
-
this.apiVersion = apiVersion || process.env.GEOCODIO_API_VERSION || "v1.
|
|
132
|
+
this.apiVersion = apiVersion || process.env.GEOCODIO_API_VERSION || "v1.9";
|
|
13
133
|
|
|
14
134
|
this.SINGLE_TIMEOUT_MS = 5000;
|
|
15
135
|
this.BATCH_TIMEOUT_MS = 30 * 60 * 1000;
|
|
16
136
|
|
|
17
137
|
this.HTTP_HEADERS = {
|
|
18
|
-
"User-Agent": "geocodio-library-node/1.
|
|
138
|
+
"User-Agent": "geocodio-library-node/1.12.0",
|
|
19
139
|
Authorization: `Bearer ${this.apiKey}`
|
|
20
140
|
};
|
|
21
141
|
|
|
@@ -30,15 +150,343 @@ class Geocodio {
|
|
|
30
150
|
this.list = this.list();
|
|
31
151
|
}
|
|
32
152
|
|
|
33
|
-
|
|
34
|
-
|
|
153
|
+
// Helper: Serialize params with array support (e.g., destinations[])
|
|
154
|
+
serializeParams(params) {
|
|
155
|
+
const parts = [];
|
|
156
|
+
|
|
157
|
+
for (const key in params) {
|
|
158
|
+
if (Object.prototype.hasOwnProperty.call(params, key)) {
|
|
159
|
+
const value = params[key];
|
|
160
|
+
|
|
161
|
+
if (Array.isArray(value)) {
|
|
162
|
+
value.forEach(v => {
|
|
163
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
|
|
164
|
+
});
|
|
165
|
+
} else if (value !== undefined && value !== null && value !== "") {
|
|
166
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return parts.join("&");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
geocode(query, fields = [], limit = null, distanceOptions = null) {
|
|
175
|
+
return this.handleRequest("geocode", query, fields, limit, distanceOptions);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
reverse(query, fields = [], limit = null, distanceOptions = null) {
|
|
179
|
+
return this.handleRequest("reverse", query, fields, limit, distanceOptions);
|
|
35
180
|
}
|
|
36
181
|
|
|
37
|
-
|
|
38
|
-
|
|
182
|
+
// Distance API - Single origin to multiple destinations (GET)
|
|
183
|
+
distance(origin, destinations, options = {}) {
|
|
184
|
+
const url = this.formatUrl("distance");
|
|
185
|
+
const originCoord = Coordinate.from(origin);
|
|
186
|
+
|
|
187
|
+
const queryParameters = {
|
|
188
|
+
origin: originCoord.toString()
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Add destinations as array params
|
|
192
|
+
const destStrings = destinations.map(d => Coordinate.from(d).toString());
|
|
193
|
+
queryParameters["destinations[]"] = destStrings;
|
|
194
|
+
|
|
195
|
+
// Add distance options
|
|
196
|
+
this.addDistanceOptionsToParams(queryParameters, options);
|
|
197
|
+
|
|
198
|
+
return this.handleResponse(
|
|
199
|
+
axios.get(url, {
|
|
200
|
+
params: queryParameters,
|
|
201
|
+
timeout: this.SINGLE_TIMEOUT_MS,
|
|
202
|
+
headers: this.HTTP_HEADERS,
|
|
203
|
+
paramsSerializer: params => this.serializeParams(params)
|
|
204
|
+
})
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Distance Matrix API - Multiple origins × destinations (POST)
|
|
209
|
+
distanceMatrix(origins, destinations, options = {}) {
|
|
210
|
+
const url = this.formatUrl("distance-matrix");
|
|
211
|
+
|
|
212
|
+
// Convert to object format for POST requests
|
|
213
|
+
const originsObjects = origins.map(o => Coordinate.from(o).toObject());
|
|
214
|
+
const destObjects = destinations.map(d => Coordinate.from(d).toObject());
|
|
215
|
+
|
|
216
|
+
const body = {
|
|
217
|
+
origins: originsObjects,
|
|
218
|
+
destinations: destObjects
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Add distance options to body
|
|
222
|
+
this.addDistanceOptionsToBody(body, options);
|
|
223
|
+
|
|
224
|
+
return this.handleResponse(
|
|
225
|
+
axios.post(url, body, {
|
|
226
|
+
timeout: this.BATCH_TIMEOUT_MS,
|
|
227
|
+
headers: this.HTTP_HEADERS
|
|
228
|
+
})
|
|
229
|
+
);
|
|
39
230
|
}
|
|
40
231
|
|
|
41
|
-
|
|
232
|
+
// Async Distance Matrix Job - Create
|
|
233
|
+
createDistanceMatrixJob(name, origins, destinations, options = {}) {
|
|
234
|
+
const url = this.formatUrl("distance-jobs");
|
|
235
|
+
|
|
236
|
+
const body = {
|
|
237
|
+
name
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// Origins can be array of coordinates or list ID (number)
|
|
241
|
+
if (typeof origins === "number") {
|
|
242
|
+
body.origins = origins;
|
|
243
|
+
} else {
|
|
244
|
+
body.origins = origins.map(o => Coordinate.from(o).toObject());
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Destinations can be array of coordinates or list ID (number)
|
|
248
|
+
if (typeof destinations === "number") {
|
|
249
|
+
body.destinations = destinations;
|
|
250
|
+
} else {
|
|
251
|
+
body.destinations = destinations.map(d => Coordinate.from(d).toObject());
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Add distance options to body
|
|
255
|
+
this.addDistanceOptionsToBody(body, options);
|
|
256
|
+
|
|
257
|
+
// Add callback URL if provided
|
|
258
|
+
if (options.callbackUrl) {
|
|
259
|
+
body.callback_url = options.callbackUrl;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return this.handleResponse(
|
|
263
|
+
axios.post(url, body, {
|
|
264
|
+
timeout: this.SINGLE_TIMEOUT_MS,
|
|
265
|
+
headers: this.HTTP_HEADERS
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Async Distance Matrix Job - Get Status
|
|
271
|
+
distanceMatrixJobStatus(id) {
|
|
272
|
+
const url = this.formatUrl(`distance-jobs/${id}`);
|
|
273
|
+
|
|
274
|
+
return this.handleResponse(
|
|
275
|
+
axios.get(url, {
|
|
276
|
+
timeout: this.SINGLE_TIMEOUT_MS,
|
|
277
|
+
headers: this.HTTP_HEADERS
|
|
278
|
+
})
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Async Distance Matrix Job - List All
|
|
283
|
+
distanceMatrixJobs(page = null) {
|
|
284
|
+
const url = this.formatUrl("distance-jobs");
|
|
285
|
+
|
|
286
|
+
const params = {};
|
|
287
|
+
if (page !== null) {
|
|
288
|
+
params.page = page;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return this.handleResponse(
|
|
292
|
+
axios.get(url, {
|
|
293
|
+
params,
|
|
294
|
+
timeout: this.SINGLE_TIMEOUT_MS,
|
|
295
|
+
headers: this.HTTP_HEADERS
|
|
296
|
+
})
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Async Distance Matrix Job - Get Results
|
|
301
|
+
getDistanceMatrixJobResults(id) {
|
|
302
|
+
const url = this.formatUrl(`distance-jobs/${id}/download`);
|
|
303
|
+
|
|
304
|
+
return this.handleResponse(
|
|
305
|
+
axios.get(url, {
|
|
306
|
+
timeout: this.BATCH_TIMEOUT_MS,
|
|
307
|
+
headers: this.HTTP_HEADERS
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Async Distance Matrix Job - Download to File
|
|
313
|
+
downloadDistanceMatrixJob(id, filePath) {
|
|
314
|
+
const url = this.formatUrl(`distance-jobs/${id}/download`);
|
|
315
|
+
const writer = fs.createWriteStream(filePath);
|
|
316
|
+
|
|
317
|
+
return this.handleResponse(
|
|
318
|
+
axios({
|
|
319
|
+
method: "get",
|
|
320
|
+
url,
|
|
321
|
+
headers: this.HTTP_HEADERS,
|
|
322
|
+
responseType: "stream"
|
|
323
|
+
}).then(response => {
|
|
324
|
+
return new Promise((resolve, reject) => {
|
|
325
|
+
response.data.pipe(writer);
|
|
326
|
+
let error = null;
|
|
327
|
+
writer.on("error", err => {
|
|
328
|
+
error = err;
|
|
329
|
+
writer.close();
|
|
330
|
+
reject(err);
|
|
331
|
+
});
|
|
332
|
+
writer.on("close", () => {
|
|
333
|
+
if (!error) {
|
|
334
|
+
resolve(true);
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
})
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Async Distance Matrix Job - Delete
|
|
343
|
+
deleteDistanceMatrixJob(id) {
|
|
344
|
+
const url = this.formatUrl(`distance-jobs/${id}`);
|
|
345
|
+
|
|
346
|
+
return this.handleResponse(
|
|
347
|
+
axios.delete(url, {
|
|
348
|
+
timeout: this.SINGLE_TIMEOUT_MS,
|
|
349
|
+
headers: this.HTTP_HEADERS
|
|
350
|
+
})
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Helper: Add distance options to GET query parameters
|
|
355
|
+
addDistanceOptionsToParams(params, options) {
|
|
356
|
+
if (options.mode) {
|
|
357
|
+
params.mode = options.mode;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (options.units) {
|
|
361
|
+
params.units = options.units;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (options.maxResults !== undefined) {
|
|
365
|
+
params.max_results = options.maxResults;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (options.maxDistance !== undefined) {
|
|
369
|
+
params.max_distance = options.maxDistance;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (options.maxDuration !== undefined) {
|
|
373
|
+
params.max_duration = options.maxDuration;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (options.minDistance !== undefined) {
|
|
377
|
+
params.min_distance = options.minDistance;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (options.minDuration !== undefined) {
|
|
381
|
+
params.min_duration = options.minDuration;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (options.orderBy) {
|
|
385
|
+
params.order_by = options.orderBy;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (options.sortOrder) {
|
|
389
|
+
params.sort = options.sortOrder;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Helper: Add distance options to POST body
|
|
394
|
+
addDistanceOptionsToBody(body, options) {
|
|
395
|
+
if (options.mode) {
|
|
396
|
+
body.mode = options.mode;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (options.units) {
|
|
400
|
+
body.units = options.units;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (options.maxResults !== undefined) {
|
|
404
|
+
body.max_results = options.maxResults;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (options.maxDistance !== undefined) {
|
|
408
|
+
body.max_distance = options.maxDistance;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (options.maxDuration !== undefined) {
|
|
412
|
+
body.max_duration = options.maxDuration;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (options.minDistance !== undefined) {
|
|
416
|
+
body.min_distance = options.minDistance;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (options.minDuration !== undefined) {
|
|
420
|
+
body.min_duration = options.minDuration;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (options.orderBy) {
|
|
424
|
+
body.order_by = options.orderBy;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (options.sortOrder) {
|
|
428
|
+
body.sort = options.sortOrder;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Helper: Add distance options for geocode/reverse
|
|
433
|
+
addDistanceOptionsToGeocodeParams(params, distanceOptions) {
|
|
434
|
+
if (!distanceOptions) return;
|
|
435
|
+
|
|
436
|
+
if (
|
|
437
|
+
distanceOptions.destinations &&
|
|
438
|
+
distanceOptions.destinations.length > 0
|
|
439
|
+
) {
|
|
440
|
+
const destStrings = distanceOptions.destinations.map(d =>
|
|
441
|
+
Coordinate.from(d).toString()
|
|
442
|
+
);
|
|
443
|
+
params["destinations[]"] = destStrings;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (distanceOptions.distanceMode) {
|
|
447
|
+
params.distance_mode = distanceOptions.distanceMode;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (distanceOptions.distanceUnits) {
|
|
451
|
+
params.distance_units = distanceOptions.distanceUnits;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (distanceOptions.distanceMaxResults !== undefined) {
|
|
455
|
+
params.distance_max_results = distanceOptions.distanceMaxResults;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (distanceOptions.distanceMaxDistance !== undefined) {
|
|
459
|
+
params.distance_max_distance = distanceOptions.distanceMaxDistance;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (distanceOptions.distanceMaxDuration !== undefined) {
|
|
463
|
+
params.distance_max_duration = distanceOptions.distanceMaxDuration;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (distanceOptions.distanceMinDistance !== undefined) {
|
|
467
|
+
params.distance_min_distance = distanceOptions.distanceMinDistance;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (distanceOptions.distanceMinDuration !== undefined) {
|
|
471
|
+
params.distance_min_duration = distanceOptions.distanceMinDuration;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (distanceOptions.distanceOrderBy) {
|
|
475
|
+
params.distance_order_by = distanceOptions.distanceOrderBy;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (distanceOptions.distanceSortOrder) {
|
|
479
|
+
params.distance_sort = distanceOptions.distanceSortOrder;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
handleRequest(
|
|
484
|
+
endpoint,
|
|
485
|
+
query,
|
|
486
|
+
fields = [],
|
|
487
|
+
limit = null,
|
|
488
|
+
distanceOptions = null
|
|
489
|
+
) {
|
|
42
490
|
const url = this.formatUrl(endpoint);
|
|
43
491
|
|
|
44
492
|
let queryParameters = {
|
|
@@ -49,11 +497,24 @@ class Geocodio {
|
|
|
49
497
|
queryParameters.limit = limit;
|
|
50
498
|
}
|
|
51
499
|
|
|
500
|
+
// Add distance options for geocode/reverse
|
|
501
|
+
this.addDistanceOptionsToGeocodeParams(queryParameters, distanceOptions);
|
|
502
|
+
|
|
52
503
|
query = this.preprocessQuery(query, endpoint);
|
|
53
504
|
|
|
54
505
|
let response = null;
|
|
506
|
+
const hasDistanceOptions =
|
|
507
|
+
distanceOptions &&
|
|
508
|
+
distanceOptions.destinations &&
|
|
509
|
+
distanceOptions.destinations.length > 0;
|
|
510
|
+
|
|
55
511
|
if (this.isSingleQuery(query)) {
|
|
56
|
-
response = this.performSingleRequest(
|
|
512
|
+
response = this.performSingleRequest(
|
|
513
|
+
url,
|
|
514
|
+
query,
|
|
515
|
+
queryParameters,
|
|
516
|
+
hasDistanceOptions
|
|
517
|
+
);
|
|
57
518
|
} else {
|
|
58
519
|
query = this.preprocessQueryList(query, endpoint);
|
|
59
520
|
response = this.performBatchRequest(url, query, queryParameters);
|
|
@@ -125,7 +586,12 @@ class Geocodio {
|
|
|
125
586
|
return true;
|
|
126
587
|
}
|
|
127
588
|
|
|
128
|
-
performSingleRequest(
|
|
589
|
+
performSingleRequest(
|
|
590
|
+
url,
|
|
591
|
+
query,
|
|
592
|
+
queryParameters,
|
|
593
|
+
useArrayParamsSerializer = false
|
|
594
|
+
) {
|
|
129
595
|
if (typeof query === "object") {
|
|
130
596
|
queryParameters = {
|
|
131
597
|
...queryParameters,
|
|
@@ -135,11 +601,18 @@ class Geocodio {
|
|
|
135
601
|
queryParameters.q = query;
|
|
136
602
|
}
|
|
137
603
|
|
|
138
|
-
|
|
604
|
+
const axiosConfig = {
|
|
139
605
|
params: queryParameters,
|
|
140
606
|
timeout: this.SINGLE_TIMEOUT_MS,
|
|
141
607
|
headers: this.HTTP_HEADERS
|
|
142
|
-
}
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
// Use custom params serializer when we have array parameters (e.g., destinations[])
|
|
611
|
+
if (useArrayParamsSerializer) {
|
|
612
|
+
axiosConfig.paramsSerializer = params => this.serializeParams(params);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
return axios.get(url, axiosConfig);
|
|
143
616
|
}
|
|
144
617
|
|
|
145
618
|
performBatchRequest(url, queries, queryParameters) {
|
|
@@ -239,4 +712,13 @@ class Geocodio {
|
|
|
239
712
|
}
|
|
240
713
|
}
|
|
241
714
|
|
|
715
|
+
// Default export for backward compatibility
|
|
242
716
|
module.exports = Geocodio;
|
|
717
|
+
|
|
718
|
+
// Named exports for ES modules and additional utilities
|
|
719
|
+
module.exports.Geocodio = Geocodio;
|
|
720
|
+
module.exports.Coordinate = Coordinate;
|
|
721
|
+
module.exports.DistanceMode = DistanceMode;
|
|
722
|
+
module.exports.DistanceUnits = DistanceUnits;
|
|
723
|
+
module.exports.DistanceOrderBy = DistanceOrderBy;
|
|
724
|
+
module.exports.DistanceSortOrder = DistanceSortOrder;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "geocodio-library-node",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "geocod.io geocoding API library",
|
|
5
5
|
"homepage": "https://github.com/Geocodio/geocodio-library-node",
|
|
6
6
|
"author": {
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"geocode",
|
|
19
19
|
"geo",
|
|
20
20
|
"address",
|
|
21
|
+
"distance",
|
|
22
|
+
"distance-matrix",
|
|
21
23
|
"congress",
|
|
22
24
|
"timezone",
|
|
23
25
|
"census"
|