@forwardslashns/fws-geo-location-api 1.0.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.
Files changed (59) hide show
  1. package/README.md +509 -0
  2. package/dist/client/geo-location.client.d.ts +121 -0
  3. package/dist/client/geo-location.client.d.ts.map +1 -0
  4. package/dist/client/geo-location.client.js +389 -0
  5. package/dist/constants/api.constants.d.ts +21 -0
  6. package/dist/constants/api.constants.d.ts.map +1 -0
  7. package/dist/constants/api.constants.js +28 -0
  8. package/dist/constants/continent.constants.d.ts +13 -0
  9. package/dist/constants/continent.constants.d.ts.map +1 -0
  10. package/dist/constants/continent.constants.js +21 -0
  11. package/dist/constants/feature.constants.d.ts +53 -0
  12. package/dist/constants/feature.constants.d.ts.map +1 -0
  13. package/dist/constants/feature.constants.js +55 -0
  14. package/dist/errors/geo-location.error.d.ts +6 -0
  15. package/dist/errors/geo-location.error.d.ts.map +1 -0
  16. package/dist/errors/geo-location.error.js +13 -0
  17. package/dist/index.d.ts +7 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +14 -0
  20. package/dist/mappers/city.mapper.d.ts +4 -0
  21. package/dist/mappers/city.mapper.d.ts.map +1 -0
  22. package/dist/mappers/city.mapper.js +18 -0
  23. package/dist/mappers/country.mapper.d.ts +4 -0
  24. package/dist/mappers/country.mapper.d.ts.map +1 -0
  25. package/dist/mappers/country.mapper.js +19 -0
  26. package/dist/mappers/postal-code.mapper.d.ts +4 -0
  27. package/dist/mappers/postal-code.mapper.d.ts.map +1 -0
  28. package/dist/mappers/postal-code.mapper.js +24 -0
  29. package/dist/mappers/region.mapper.d.ts +4 -0
  30. package/dist/mappers/region.mapper.d.ts.map +1 -0
  31. package/dist/mappers/region.mapper.js +20 -0
  32. package/dist/services/http.service.d.ts +8 -0
  33. package/dist/services/http.service.d.ts.map +1 -0
  34. package/dist/services/http.service.js +60 -0
  35. package/dist/types/city.types.d.ts +21 -0
  36. package/dist/types/city.types.d.ts.map +1 -0
  37. package/dist/types/city.types.js +2 -0
  38. package/dist/types/client.types.d.ts +133 -0
  39. package/dist/types/client.types.d.ts.map +1 -0
  40. package/dist/types/client.types.js +2 -0
  41. package/dist/types/common.types.d.ts +29 -0
  42. package/dist/types/common.types.d.ts.map +1 -0
  43. package/dist/types/common.types.js +2 -0
  44. package/dist/types/country.types.d.ts +25 -0
  45. package/dist/types/country.types.d.ts.map +1 -0
  46. package/dist/types/country.types.js +2 -0
  47. package/dist/types/geonames-raw.types.d.ts +77 -0
  48. package/dist/types/geonames-raw.types.d.ts.map +1 -0
  49. package/dist/types/geonames-raw.types.js +2 -0
  50. package/dist/types/index.d.ts +7 -0
  51. package/dist/types/index.d.ts.map +1 -0
  52. package/dist/types/index.js +2 -0
  53. package/dist/types/postal-code.types.d.ts +31 -0
  54. package/dist/types/postal-code.types.d.ts.map +1 -0
  55. package/dist/types/postal-code.types.js +2 -0
  56. package/dist/types/region.types.d.ts +19 -0
  57. package/dist/types/region.types.d.ts.map +1 -0
  58. package/dist/types/region.types.js +2 -0
  59. package/package.json +42 -0
@@ -0,0 +1,389 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GeoLocationClient = void 0;
4
+ const http_service_js_1 = require("../services/http.service.js");
5
+ const country_mapper_js_1 = require("../mappers/country.mapper.js");
6
+ const region_mapper_js_1 = require("../mappers/region.mapper.js");
7
+ const city_mapper_js_1 = require("../mappers/city.mapper.js");
8
+ const postal_code_mapper_js_1 = require("../mappers/postal-code.mapper.js");
9
+ const api_constants_js_1 = require("../constants/api.constants.js");
10
+ const feature_constants_js_1 = require("../constants/feature.constants.js");
11
+ const geo_location_error_js_1 = require("../errors/geo-location.error.js");
12
+ class GeoLocationClient {
13
+ httpService;
14
+ restrictions;
15
+ /** In-memory cache: ISO2 countryCode → Country. Populated on first full country fetch. */
16
+ countryCache = new Map();
17
+ constructor(options) {
18
+ this.httpService = new http_service_js_1.HttpService(options.usernames);
19
+ this.restrictions = options.restrictions ?? {};
20
+ }
21
+ // ---------------------------------------------------------------------------
22
+ // Countries
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Returns a list of countries.
26
+ *
27
+ * @param options - Optional per-call overrides for continent and country code filters.
28
+ * When omitted the global `restrictions` passed to the constructor are used.
29
+ */
30
+ async getCountries(options) {
31
+ const continents = options?.continents ?? this.restrictions.continents;
32
+ const countryCodes = options?.countryCodes ?? this.restrictions.countries;
33
+ let countries;
34
+ if (continents && continents.length > 0) {
35
+ const results = await Promise.all(continents.map((continent) => this.fetchCountriesByContinent(continent)));
36
+ countries = results.flat();
37
+ }
38
+ else {
39
+ countries = await this.fetchAllCountries();
40
+ }
41
+ if (countryCodes && countryCodes.length > 0) {
42
+ const upper = countryCodes.map((c) => c.toUpperCase());
43
+ countries = countries.filter((c) => upper.includes(c.countryCode));
44
+ }
45
+ return countries;
46
+ }
47
+ // ---------------------------------------------------------------------------
48
+ // Regions (states / provinces / prefectures)
49
+ // ---------------------------------------------------------------------------
50
+ /**
51
+ * Returns first-order administrative divisions (states, provinces, etc.)
52
+ * for the given country.
53
+ *
54
+ * @param countryCode - ISO 3166-1 alpha-2 country code, e.g. `"US"`.
55
+ * @param options - Optional per-call options.
56
+ */
57
+ async getRegions(countryCode, options) {
58
+ this.assertNonEmptyCountryCode(countryCode);
59
+ const pageSize = options?.maxRows ?? feature_constants_js_1.DEFAULT_MAX_ROWS;
60
+ const startRow = options?.page != null ? (options.page - 1) * pageSize : (options?.startRow ?? 0);
61
+ const currentPage = options?.page ?? (options?.startRow != null ? Math.floor(options.startRow / pageSize) + 1 : 1);
62
+ const params = {
63
+ country: countryCode.toUpperCase(),
64
+ featureCode: feature_constants_js_1.FEATURE_CODES.ADM1,
65
+ maxRows: pageSize,
66
+ orderby: feature_constants_js_1.GEONAMES_ORDERBY.RELEVANCE,
67
+ style: feature_constants_js_1.GEONAMES_STYLE.FULL,
68
+ };
69
+ if (startRow > 0) {
70
+ params['startRow'] = startRow;
71
+ }
72
+ const raw = await this.httpService.get(`${api_constants_js_1.GEONAMES_BASE_URL}${api_constants_js_1.GEONAMES_ENDPOINTS.SEARCH}`, params);
73
+ const data = raw.geonames.map(region_mapper_js_1.mapRegion);
74
+ return {
75
+ data,
76
+ total: raw.totalResultsCount,
77
+ page: currentPage,
78
+ pageSize,
79
+ hasMore: startRow + data.length < raw.totalResultsCount,
80
+ };
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // Cities
84
+ // ---------------------------------------------------------------------------
85
+ /**
86
+ * Returns populated places (cities, towns) for the given country,
87
+ * ordered by population descending.
88
+ *
89
+ * @param countryCode - ISO 3166-1 alpha-2 country code, e.g. `"DE"`.
90
+ * @param options - Optional filters: `regionCode`, `maxRows`, `orderBy`.
91
+ */
92
+ async getCities(countryCode, options) {
93
+ const pageSize = options?.maxRows ?? feature_constants_js_1.DEFAULT_MAX_ROWS;
94
+ const startRow = options?.page != null ? (options.page - 1) * pageSize : (options?.startRow ?? 0);
95
+ const currentPage = options?.page ?? (options?.startRow != null ? Math.floor(options.startRow / pageSize) + 1 : 1);
96
+ const params = {
97
+ featureClass: feature_constants_js_1.FEATURE_CLASSES.POPULATED_PLACE,
98
+ maxRows: pageSize,
99
+ orderby: options?.orderBy ?? feature_constants_js_1.GEONAMES_ORDERBY.POPULATION,
100
+ style: feature_constants_js_1.GEONAMES_STYLE.FULL,
101
+ };
102
+ if (startRow > 0) {
103
+ params['startRow'] = startRow;
104
+ }
105
+ if (countryCode && countryCode.trim().length > 0) {
106
+ params['country'] = countryCode.trim().toUpperCase();
107
+ }
108
+ if (options?.regionCode) {
109
+ params['adminCode1'] = options.regionCode.toUpperCase();
110
+ }
111
+ if (options?.searchTerm && options.searchTerm.trim().length > 0) {
112
+ params['q'] = options.searchTerm.trim();
113
+ }
114
+ const raw = await this.httpService.get(`${api_constants_js_1.GEONAMES_BASE_URL}${api_constants_js_1.GEONAMES_ENDPOINTS.SEARCH}`, params);
115
+ const data = raw.geonames.map(city_mapper_js_1.mapCity);
116
+ return {
117
+ data,
118
+ total: raw.totalResultsCount,
119
+ page: currentPage,
120
+ pageSize,
121
+ hasMore: startRow + data.length < raw.totalResultsCount,
122
+ };
123
+ }
124
+ // ---------------------------------------------------------------------------
125
+ // Postal codes
126
+ // ---------------------------------------------------------------------------
127
+ /**
128
+ * Returns postal / zip codes for the given country. Supports optional
129
+ * prefix and place-name filters.
130
+ *
131
+ * Each result includes a `displayName` formatted as:
132
+ * `"{postalCode} — {placeName}, {adminName1}, {countryName}"`
133
+ *
134
+ * @param countryCode - ISO 3166-1 alpha-2 country code, e.g. `"US"`.
135
+ * @param options - Optional filters: `postalCodePrefix`, `placeName`, `maxRows`, `startRow`, `page`.
136
+ *
137
+ * **GeoNames limitation — a filter is required for large countries.**
138
+ * The underlying GeoNames `postalCodeSearchJSON` endpoint rejects bare country-only
139
+ * queries for countries with large postal code datasets (e.g. US, DE, FR) by returning
140
+ * status 15 ("no result found"). Providing either `postalCodePrefix` or `placeName`
141
+ * always works regardless of country size.
142
+ *
143
+ * To retrieve **all** postal codes for a country without knowing a prefix in advance,
144
+ * use {@link getAllPostalCodes} instead — it handles this constraint transparently by
145
+ * iterating through alphanumeric prefix buckets and supports a progressive `onProgress`
146
+ * callback for streaming results into the UI.
147
+ */
148
+ async getPostalCodes(countryCode, options) {
149
+ this.assertNonEmptyCountryCode(countryCode);
150
+ const upper = countryCode.toUpperCase();
151
+ const countryName = await this.resolveCountryName(upper);
152
+ const pageSize = options?.maxRows ?? feature_constants_js_1.DEFAULT_MAX_ROWS;
153
+ const startRow = options?.page != null ? (options.page - 1) * pageSize : (options?.startRow ?? 0);
154
+ const currentPage = options?.page ?? (options?.startRow != null ? Math.floor(options.startRow / pageSize) + 1 : 1);
155
+ const hasFilter = !!(options?.postalCodePrefix || options?.placeName);
156
+ const params = {
157
+ country: upper,
158
+ maxRows: pageSize,
159
+ };
160
+ if (startRow > 0) {
161
+ params['startRow'] = startRow;
162
+ }
163
+ if (options?.postalCodePrefix) {
164
+ params['postalcode_startsWith'] = options.postalCodePrefix;
165
+ }
166
+ if (options?.placeName) {
167
+ params['placename_startsWith'] = options.placeName;
168
+ }
169
+ try {
170
+ const raw = await this.httpService.get(`${api_constants_js_1.GEONAMES_BASE_URL}${api_constants_js_1.GEONAMES_ENDPOINTS.POSTAL_CODE_SEARCH}`, params);
171
+ const data = raw.postalCodes.map((pc) => (0, postal_code_mapper_js_1.mapPostalCode)(pc, countryName));
172
+ return { data, total: -1, page: currentPage, pageSize, hasMore: data.length === pageSize };
173
+ }
174
+ catch (err) {
175
+ if (err instanceof geo_location_error_js_1.GeoLocationError && err.code === api_constants_js_1.GEONAMES_ERROR_CODES.NO_RESULT_FOUND) {
176
+ if (!hasFilter) {
177
+ // GeoNames rejects bare country-only queries for large datasets.
178
+ // Surface a descriptive error so callers understand what went wrong.
179
+ throw new geo_location_error_js_1.GeoLocationError(`GeoNames does not support listing postal codes for "${upper}" without a filter — the country's ` +
180
+ `dataset is too large for an unfiltered query. ` +
181
+ `Provide a "postalCodePrefix" or "placeName" option, or use getAllPostalCodes() to retrieve ` +
182
+ `the full dataset via prefix iteration.`, api_constants_js_1.GEONAMES_ERROR_CODES.NO_RESULT_FOUND);
183
+ }
184
+ // With a filter, status 15 simply means no matches — return an empty page.
185
+ return { data: [], total: -1, page: currentPage, pageSize, hasMore: false };
186
+ }
187
+ throw err;
188
+ }
189
+ }
190
+ /**
191
+ * Returns **all** postal codes for the given country by iterating through
192
+ * alphanumeric prefix buckets (`0–9`, `A–Z`) and paginating each bucket.
193
+ *
194
+ * Because GeoNames requires at least one search term, a bare country-only
195
+ * query always returns status 15. This method works around that constraint
196
+ * transparently: it fires one request per character prefix (up to 36), each
197
+ * with `postalcode_startsWith=<char>`, and pages through results in chunks
198
+ * of 1 000 until the bucket is exhausted. Prefixes that yield no results
199
+ * (status 15 or empty page) are skipped automatically.
200
+ *
201
+ * The results are deduplicated and returned as a single flat array sorted by
202
+ * postal code.
203
+ *
204
+ * **API credit cost**: roughly 1 request per 1 000 postal codes in the
205
+ * country, spread across up to 36 prefix buckets. For small countries
206
+ * (< 5 000 codes) expect 10–20 requests; for large ones (US ~43 000) expect
207
+ * ~80 requests.
208
+ *
209
+ * @param countryCode - ISO 3166-1 alpha-2 country code, e.g. `"US"`.
210
+ */
211
+ async getAllPostalCodes(countryCode, options) {
212
+ this.assertNonEmptyCountryCode(countryCode);
213
+ const upper = countryCode.toUpperCase();
214
+ const countryName = await this.resolveCountryName(upper);
215
+ const PREFIXES = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
216
+ const seen = new Set();
217
+ const all = [];
218
+ for (const prefix of PREFIXES) {
219
+ let startRow = 0;
220
+ while (true) {
221
+ let page;
222
+ try {
223
+ const raw = await this.httpService.get(`${api_constants_js_1.GEONAMES_BASE_URL}${api_constants_js_1.GEONAMES_ENDPOINTS.POSTAL_CODE_SEARCH}`, { country: upper, postalcode_startsWith: prefix, maxRows: feature_constants_js_1.MAX_ROWS_LIMIT, startRow });
224
+ page = raw.postalCodes.map((pc) => (0, postal_code_mapper_js_1.mapPostalCode)(pc, countryName));
225
+ }
226
+ catch (err) {
227
+ if (err instanceof geo_location_error_js_1.GeoLocationError && err.code === api_constants_js_1.GEONAMES_ERROR_CODES.NO_RESULT_FOUND) {
228
+ break; // this prefix has no results — move on
229
+ }
230
+ throw err;
231
+ }
232
+ for (const pc of page) {
233
+ const key = `${pc.postalCode}|${pc.placeName}|${pc.adminName1}`;
234
+ if (!seen.has(key)) {
235
+ seen.add(key);
236
+ all.push(pc);
237
+ }
238
+ }
239
+ if (page.length < feature_constants_js_1.MAX_ROWS_LIMIT)
240
+ break; // last page for this prefix
241
+ startRow += feature_constants_js_1.MAX_ROWS_LIMIT;
242
+ }
243
+ // Notify caller of progress after each prefix bucket
244
+ if (options?.onProgress && all.length > 0) {
245
+ all.sort((a, b) => a.postalCode.localeCompare(b.postalCode, undefined, { numeric: true }));
246
+ options.onProgress(all.slice());
247
+ }
248
+ }
249
+ all.sort((a, b) => a.postalCode.localeCompare(b.postalCode, undefined, { numeric: true }));
250
+ return all;
251
+ }
252
+ /**
253
+ * Returns one page of postal codes for a country, working around the GeoNames
254
+ * per-country query limit that prevents retrieving all codes for large
255
+ * countries (e.g. US) in a single request.
256
+ *
257
+ * Internally the library iterates 36 alphanumeric prefix buckets (0–9, A–Z)
258
+ * and stores progress as an opaque cursor so callers never need to track
259
+ * offsets themselves.
260
+ *
261
+ * **Typical usage**
262
+ * ```ts
263
+ * let page = await client.getPostalCodesPage('US', { maxRows: 200 });
264
+ * while (page.hasMore) {
265
+ * // process page.data …
266
+ * page = await client.getPostalCodesPage('US', { cursor: page.nextCursor! });
267
+ * }
268
+ * // process last page.data …
269
+ * ```
270
+ *
271
+ * **API credit cost**: 1 GeoNames request per call (2 at the boundary
272
+ * between two prefix buckets). Far cheaper than `getAllPostalCodes` when
273
+ * you only need the first few pages.
274
+ *
275
+ * @param countryCode - ISO 3166-1 alpha-2 country code, e.g. `"US"`.
276
+ * @param options - Optional cursor and page-size settings.
277
+ */
278
+ async getPostalCodesPage(countryCode, options) {
279
+ this.assertNonEmptyCountryCode(countryCode);
280
+ const upper = countryCode.toUpperCase();
281
+ const countryName = await this.resolveCountryName(upper);
282
+ const PREFIXES = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
283
+ const pageSize = Math.min(options?.maxRows ?? feature_constants_js_1.DEFAULT_MAX_ROWS, feature_constants_js_1.MAX_ROWS_LIMIT);
284
+ let prefixIdx = 0;
285
+ let startRow = 0;
286
+ if (options?.cursor) {
287
+ const raw = atob(options.cursor);
288
+ const colonAt = raw.indexOf(':');
289
+ prefixIdx = parseInt(raw.substring(0, colonAt), 10);
290
+ startRow = parseInt(raw.substring(colonAt + 1), 10);
291
+ }
292
+ const collected = [];
293
+ while (collected.length < pageSize && prefixIdx < PREFIXES.length) {
294
+ const prefix = PREFIXES[prefixIdx];
295
+ const remaining = pageSize - collected.length;
296
+ let page;
297
+ try {
298
+ const raw = await this.httpService.get(`${api_constants_js_1.GEONAMES_BASE_URL}${api_constants_js_1.GEONAMES_ENDPOINTS.POSTAL_CODE_SEARCH}`, { country: upper, postalcode_startsWith: prefix, maxRows: remaining, startRow });
299
+ page = raw.postalCodes.map((pc) => (0, postal_code_mapper_js_1.mapPostalCode)(pc, countryName));
300
+ }
301
+ catch (err) {
302
+ if (err instanceof geo_location_error_js_1.GeoLocationError && err.code === api_constants_js_1.GEONAMES_ERROR_CODES.NO_RESULT_FOUND) {
303
+ // No codes with this prefix — move on
304
+ prefixIdx++;
305
+ startRow = 0;
306
+ continue;
307
+ }
308
+ throw err;
309
+ }
310
+ collected.push(...page);
311
+ if (page.length < remaining) {
312
+ // Prefix exhausted — advance to the next bucket
313
+ prefixIdx++;
314
+ startRow = 0;
315
+ }
316
+ else {
317
+ // Filled the page; more may remain in this prefix
318
+ startRow += page.length;
319
+ break;
320
+ }
321
+ }
322
+ const exhausted = prefixIdx >= PREFIXES.length;
323
+ const nextCursor = exhausted ? null : btoa(`${prefixIdx}:${startRow}`);
324
+ return {
325
+ data: collected,
326
+ nextCursor,
327
+ hasMore: !exhausted,
328
+ prefixProgress: { done: Math.min(prefixIdx, PREFIXES.length), total: 36 },
329
+ };
330
+ }
331
+ /**
332
+ * Finds a single postal code by its exact code string within a given country.
333
+ * Returns `null` when not found.
334
+ *
335
+ * @param postalCode - The exact postal / zip code to look up, e.g. `"10001"`.
336
+ * @param countryCode - ISO 3166-1 alpha-2 country code, e.g. `"US"`.
337
+ */
338
+ async findPostalCode(postalCode, countryCode) {
339
+ this.assertNonEmptyCountryCode(countryCode);
340
+ if (!postalCode || postalCode.trim().length === 0) {
341
+ throw new geo_location_error_js_1.GeoLocationError('postalCode must not be empty.');
342
+ }
343
+ const upper = countryCode.toUpperCase();
344
+ const countryName = await this.resolveCountryName(upper);
345
+ const raw = await this.httpService.get(`${api_constants_js_1.GEONAMES_BASE_URL}${api_constants_js_1.GEONAMES_ENDPOINTS.POSTAL_CODE_SEARCH}`, {
346
+ country: upper,
347
+ postalcode: postalCode.trim(),
348
+ maxRows: 1,
349
+ });
350
+ if (!raw.postalCodes.length) {
351
+ return null;
352
+ }
353
+ return (0, postal_code_mapper_js_1.mapPostalCode)(raw.postalCodes[0], countryName);
354
+ }
355
+ // ---------------------------------------------------------------------------
356
+ // Private helpers
357
+ // ---------------------------------------------------------------------------
358
+ async fetchAllCountries() {
359
+ const raw = await this.httpService.get(`${api_constants_js_1.GEONAMES_BASE_URL}${api_constants_js_1.GEONAMES_ENDPOINTS.COUNTRY_INFO}`, {});
360
+ const countries = raw.geonames.map(country_mapper_js_1.mapCountry);
361
+ this.cacheCountries(countries);
362
+ return countries;
363
+ }
364
+ async fetchCountriesByContinent(continent) {
365
+ const raw = await this.httpService.get(`${api_constants_js_1.GEONAMES_BASE_URL}${api_constants_js_1.GEONAMES_ENDPOINTS.COUNTRY_INFO}`, { continent });
366
+ const countries = raw.geonames.map(country_mapper_js_1.mapCountry);
367
+ this.cacheCountries(countries);
368
+ return countries;
369
+ }
370
+ cacheCountries(countries) {
371
+ for (const country of countries) {
372
+ this.countryCache.set(country.countryCode, country);
373
+ }
374
+ }
375
+ async resolveCountryName(upperCountryCode) {
376
+ if (this.countryCache.has(upperCountryCode)) {
377
+ return this.countryCache.get(upperCountryCode).countryName;
378
+ }
379
+ const countries = await this.fetchAllCountries();
380
+ const found = countries.find((c) => c.countryCode === upperCountryCode);
381
+ return found?.countryName ?? upperCountryCode;
382
+ }
383
+ assertNonEmptyCountryCode(countryCode) {
384
+ if (!countryCode || countryCode.trim().length === 0) {
385
+ throw new geo_location_error_js_1.GeoLocationError('countryCode must not be empty.');
386
+ }
387
+ }
388
+ }
389
+ exports.GeoLocationClient = GeoLocationClient;
@@ -0,0 +1,21 @@
1
+ export declare const GEONAMES_BASE_URL: "https://secure.geonames.org";
2
+ export declare const GEONAMES_ENDPOINTS: {
3
+ readonly COUNTRY_INFO: "/countryInfoJSON";
4
+ readonly SEARCH: "/searchJSON";
5
+ readonly POSTAL_CODE_SEARCH: "/postalCodeSearchJSON";
6
+ };
7
+ export declare const GEONAMES_ERROR_CODES: {
8
+ readonly AUTHORIZATION_EXCEPTION: 10;
9
+ readonly RECORD_DOES_NOT_EXIST: 11;
10
+ readonly OTHER_ERROR: 12;
11
+ readonly DATABASE_TIMEOUT: 13;
12
+ readonly INVALID_PARAMETER: 14;
13
+ readonly NO_RESULT_FOUND: 15;
14
+ readonly DUPLICATE_EXCEPTION: 16;
15
+ readonly POST_LIMIT_EXCEEDED: 17;
16
+ readonly DAILY_LIMIT_EXCEEDED: 18;
17
+ readonly HOURLY_LIMIT_EXCEEDED: 19;
18
+ readonly WEEKLY_LIMIT_EXCEEDED: 20;
19
+ };
20
+ export declare const GEONAMES_RATE_LIMIT_CODES: ReadonlyArray<number>;
21
+ //# sourceMappingURL=api.constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.constants.d.ts","sourceRoot":"","sources":["../../src/constants/api.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,EAAG,6BAAsC,CAAC;AAExE,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAEX,eAAO,MAAM,oBAAoB;;;;;;;;;;;;CAYvB,CAAC;AAEX,eAAO,MAAM,yBAAyB,EAAE,aAAa,CAAC,MAAM,CAK3D,CAAC"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GEONAMES_RATE_LIMIT_CODES = exports.GEONAMES_ERROR_CODES = exports.GEONAMES_ENDPOINTS = exports.GEONAMES_BASE_URL = void 0;
4
+ exports.GEONAMES_BASE_URL = 'https://secure.geonames.org';
5
+ exports.GEONAMES_ENDPOINTS = {
6
+ COUNTRY_INFO: '/countryInfoJSON',
7
+ SEARCH: '/searchJSON',
8
+ POSTAL_CODE_SEARCH: '/postalCodeSearchJSON',
9
+ };
10
+ exports.GEONAMES_ERROR_CODES = {
11
+ AUTHORIZATION_EXCEPTION: 10,
12
+ RECORD_DOES_NOT_EXIST: 11,
13
+ OTHER_ERROR: 12,
14
+ DATABASE_TIMEOUT: 13,
15
+ INVALID_PARAMETER: 14,
16
+ NO_RESULT_FOUND: 15,
17
+ DUPLICATE_EXCEPTION: 16,
18
+ POST_LIMIT_EXCEEDED: 17,
19
+ DAILY_LIMIT_EXCEEDED: 18,
20
+ HOURLY_LIMIT_EXCEEDED: 19,
21
+ WEEKLY_LIMIT_EXCEEDED: 20,
22
+ };
23
+ exports.GEONAMES_RATE_LIMIT_CODES = [
24
+ exports.GEONAMES_ERROR_CODES.DAILY_LIMIT_EXCEEDED,
25
+ exports.GEONAMES_ERROR_CODES.HOURLY_LIMIT_EXCEEDED,
26
+ exports.GEONAMES_ERROR_CODES.WEEKLY_LIMIT_EXCEEDED,
27
+ exports.GEONAMES_ERROR_CODES.POST_LIMIT_EXCEEDED,
28
+ ];
@@ -0,0 +1,13 @@
1
+ export declare const CONTINENT_CODES: {
2
+ readonly AFRICA: "AF";
3
+ readonly ASIA: "AS";
4
+ readonly EUROPE: "EU";
5
+ readonly NORTH_AMERICA: "NA";
6
+ readonly OCEANIA: "OC";
7
+ readonly SOUTH_AMERICA: "SA";
8
+ readonly ANTARCTICA: "AN";
9
+ };
10
+ export declare const CONTINENT_NAMES: Readonly<Record<string, string>>;
11
+ /** Union of all valid GeoNames continent codes. */
12
+ export type ContinentCode = (typeof CONTINENT_CODES)[keyof typeof CONTINENT_CODES];
13
+ //# sourceMappingURL=continent.constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"continent.constants.d.ts","sourceRoot":"","sources":["../../src/constants/continent.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe;;;;;;;;CAQlB,CAAC;AAEX,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQnD,CAAC;AAEX,mDAAmD;AACnD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,eAAe,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONTINENT_NAMES = exports.CONTINENT_CODES = void 0;
4
+ exports.CONTINENT_CODES = {
5
+ AFRICA: 'AF',
6
+ ASIA: 'AS',
7
+ EUROPE: 'EU',
8
+ NORTH_AMERICA: 'NA',
9
+ OCEANIA: 'OC',
10
+ SOUTH_AMERICA: 'SA',
11
+ ANTARCTICA: 'AN',
12
+ };
13
+ exports.CONTINENT_NAMES = {
14
+ AF: 'Africa',
15
+ AS: 'Asia',
16
+ EU: 'Europe',
17
+ NA: 'North America',
18
+ OC: 'Oceania',
19
+ SA: 'South America',
20
+ AN: 'Antarctica',
21
+ };
@@ -0,0 +1,53 @@
1
+ export declare const FEATURE_CLASSES: {
2
+ readonly POPULATED_PLACE: "P";
3
+ readonly ADMINISTRATIVE: "A";
4
+ readonly HYDROGRAPHIC: "H";
5
+ readonly AREA: "L";
6
+ readonly ROAD: "R";
7
+ readonly SPOT: "S";
8
+ readonly HYPSOGRAPHIC: "T";
9
+ readonly UNDERSEA: "U";
10
+ readonly VEGETATION: "V";
11
+ };
12
+ export declare const FEATURE_CODES: {
13
+ /** First-order administrative division (state, province, region) */
14
+ readonly ADM1: "ADM1";
15
+ /** Second-order administrative division (county, district, department) */
16
+ readonly ADM2: "ADM2";
17
+ /** Third-order administrative division */
18
+ readonly ADM3: "ADM3";
19
+ /** Fourth-order administrative division */
20
+ readonly ADM4: "ADM4";
21
+ /** Miscellaneous administrative division */
22
+ readonly ADMD: "ADMD";
23
+ /** Populated place (city, village, town) */
24
+ readonly PPL: "PPL";
25
+ /** Seat of a first-order administrative division (state capital) */
26
+ readonly PPLA: "PPLA";
27
+ /** Seat of a second-order administrative division */
28
+ readonly PPLA2: "PPLA2";
29
+ /** Seat of a third-order administrative division */
30
+ readonly PPLA3: "PPLA3";
31
+ /** Capital of a political entity (national capital) */
32
+ readonly PPLC: "PPLC";
33
+ /** Seat of government of a political entity */
34
+ readonly PPLG: "PPLG";
35
+ /** Section of a populated place */
36
+ readonly PPLX: "PPLX";
37
+ };
38
+ export declare const GEONAMES_ORDERBY: {
39
+ readonly POPULATION: "population";
40
+ readonly ELEVATION: "elevation";
41
+ readonly RELEVANCE: "relevance";
42
+ };
43
+ export declare const GEONAMES_STYLE: {
44
+ readonly SHORT: "SHORT";
45
+ readonly MEDIUM: "MEDIUM";
46
+ readonly LONG: "LONG";
47
+ readonly FULL: "FULL";
48
+ };
49
+ /** Default page size returned by all paginated library methods (GeoNames free tier allows up to 500). */
50
+ export declare const DEFAULT_MAX_ROWS: 100;
51
+ /** Hard maximum rows per single GeoNames request on the free tier. */
52
+ export declare const MAX_ROWS_LIMIT: 500;
53
+ //# sourceMappingURL=feature.constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature.constants.d.ts","sourceRoot":"","sources":["../../src/constants/feature.constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe;;;;;;;;;;CAUlB,CAAC;AAEX,eAAO,MAAM,aAAa;IACxB,oEAAoE;;IAEpE,0EAA0E;;IAE1E,0CAA0C;;IAE1C,2CAA2C;;IAE3C,4CAA4C;;IAE5C,4CAA4C;;IAE5C,oEAAoE;;IAEpE,qDAAqD;;IAErD,oDAAoD;;IAEpD,uDAAuD;;IAEvD,+CAA+C;;IAE/C,mCAAmC;;CAE3B,CAAC;AAEX,eAAO,MAAM,gBAAgB;;;;CAInB,CAAC;AAEX,eAAO,MAAM,cAAc;;;;;CAKjB,CAAC;AAEX,yGAAyG;AACzG,eAAO,MAAM,gBAAgB,EAAG,GAAY,CAAC;AAC7C,sEAAsE;AACtE,eAAO,MAAM,cAAc,EAAG,GAAY,CAAC"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_ROWS_LIMIT = exports.DEFAULT_MAX_ROWS = exports.GEONAMES_STYLE = exports.GEONAMES_ORDERBY = exports.FEATURE_CODES = exports.FEATURE_CLASSES = void 0;
4
+ exports.FEATURE_CLASSES = {
5
+ POPULATED_PLACE: 'P',
6
+ ADMINISTRATIVE: 'A',
7
+ HYDROGRAPHIC: 'H',
8
+ AREA: 'L',
9
+ ROAD: 'R',
10
+ SPOT: 'S',
11
+ HYPSOGRAPHIC: 'T',
12
+ UNDERSEA: 'U',
13
+ VEGETATION: 'V',
14
+ };
15
+ exports.FEATURE_CODES = {
16
+ /** First-order administrative division (state, province, region) */
17
+ ADM1: 'ADM1',
18
+ /** Second-order administrative division (county, district, department) */
19
+ ADM2: 'ADM2',
20
+ /** Third-order administrative division */
21
+ ADM3: 'ADM3',
22
+ /** Fourth-order administrative division */
23
+ ADM4: 'ADM4',
24
+ /** Miscellaneous administrative division */
25
+ ADMD: 'ADMD',
26
+ /** Populated place (city, village, town) */
27
+ PPL: 'PPL',
28
+ /** Seat of a first-order administrative division (state capital) */
29
+ PPLA: 'PPLA',
30
+ /** Seat of a second-order administrative division */
31
+ PPLA2: 'PPLA2',
32
+ /** Seat of a third-order administrative division */
33
+ PPLA3: 'PPLA3',
34
+ /** Capital of a political entity (national capital) */
35
+ PPLC: 'PPLC',
36
+ /** Seat of government of a political entity */
37
+ PPLG: 'PPLG',
38
+ /** Section of a populated place */
39
+ PPLX: 'PPLX',
40
+ };
41
+ exports.GEONAMES_ORDERBY = {
42
+ POPULATION: 'population',
43
+ ELEVATION: 'elevation',
44
+ RELEVANCE: 'relevance',
45
+ };
46
+ exports.GEONAMES_STYLE = {
47
+ SHORT: 'SHORT',
48
+ MEDIUM: 'MEDIUM',
49
+ LONG: 'LONG',
50
+ FULL: 'FULL',
51
+ };
52
+ /** Default page size returned by all paginated library methods (GeoNames free tier allows up to 500). */
53
+ exports.DEFAULT_MAX_ROWS = 100;
54
+ /** Hard maximum rows per single GeoNames request on the free tier. */
55
+ exports.MAX_ROWS_LIMIT = 500;
@@ -0,0 +1,6 @@
1
+ export declare class GeoLocationError extends Error {
2
+ readonly code?: number | undefined;
3
+ readonly name = "GeoLocationError";
4
+ constructor(message: string, code?: number | undefined);
5
+ }
6
+ //# sourceMappingURL=geo-location.error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo-location.error.d.ts","sourceRoot":"","sources":["../../src/errors/geo-location.error.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAiB,SAAQ,KAAK;aAKvB,IAAI,CAAC,EAAE,MAAM;IAJ/B,SAAyB,IAAI,sBAAsB;gBAGjD,OAAO,EAAE,MAAM,EACC,IAAI,CAAC,EAAE,MAAM,YAAA;CAKhC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GeoLocationError = void 0;
4
+ class GeoLocationError extends Error {
5
+ code;
6
+ name = 'GeoLocationError';
7
+ constructor(message, code) {
8
+ super(message);
9
+ this.code = code;
10
+ Object.setPrototypeOf(this, new.target.prototype);
11
+ }
12
+ }
13
+ exports.GeoLocationError = GeoLocationError;
@@ -0,0 +1,7 @@
1
+ export { GeoLocationClient } from './client/geo-location.client.js';
2
+ export { GeoLocationError } from './errors/geo-location.error.js';
3
+ export { CONTINENT_CODES, CONTINENT_NAMES } from './constants/continent.constants.js';
4
+ export { FEATURE_CLASSES, FEATURE_CODES, DEFAULT_MAX_ROWS } from './constants/feature.constants.js';
5
+ export type { ContinentCode } from './constants/continent.constants.js';
6
+ export type { AtLeastOne, PaginatedResult, GeoLocationClientOptions, GeoLocationRestrictions, GetCountriesOptions, GetRegionsOptions, GetCitiesOptions, GetPostalCodesOptions, GetAllPostalCodesOptions, GetPostalCodePageOptions, PostalCodePage, Country, Region, City, PostalCode, } from './types/index.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AACpG,YAAY,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EACV,UAAU,EACV,eAAe,EACf,wBAAwB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACxB,wBAAwB,EACxB,cAAc,EACd,OAAO,EACP,MAAM,EACN,IAAI,EACJ,UAAU,GACX,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_MAX_ROWS = exports.FEATURE_CODES = exports.FEATURE_CLASSES = exports.CONTINENT_NAMES = exports.CONTINENT_CODES = exports.GeoLocationError = exports.GeoLocationClient = void 0;
4
+ var geo_location_client_js_1 = require("./client/geo-location.client.js");
5
+ Object.defineProperty(exports, "GeoLocationClient", { enumerable: true, get: function () { return geo_location_client_js_1.GeoLocationClient; } });
6
+ var geo_location_error_js_1 = require("./errors/geo-location.error.js");
7
+ Object.defineProperty(exports, "GeoLocationError", { enumerable: true, get: function () { return geo_location_error_js_1.GeoLocationError; } });
8
+ var continent_constants_js_1 = require("./constants/continent.constants.js");
9
+ Object.defineProperty(exports, "CONTINENT_CODES", { enumerable: true, get: function () { return continent_constants_js_1.CONTINENT_CODES; } });
10
+ Object.defineProperty(exports, "CONTINENT_NAMES", { enumerable: true, get: function () { return continent_constants_js_1.CONTINENT_NAMES; } });
11
+ var feature_constants_js_1 = require("./constants/feature.constants.js");
12
+ Object.defineProperty(exports, "FEATURE_CLASSES", { enumerable: true, get: function () { return feature_constants_js_1.FEATURE_CLASSES; } });
13
+ Object.defineProperty(exports, "FEATURE_CODES", { enumerable: true, get: function () { return feature_constants_js_1.FEATURE_CODES; } });
14
+ Object.defineProperty(exports, "DEFAULT_MAX_ROWS", { enumerable: true, get: function () { return feature_constants_js_1.DEFAULT_MAX_ROWS; } });
@@ -0,0 +1,4 @@
1
+ import type { GeoNamesSearchResultRaw } from '../types/geonames-raw.types.js';
2
+ import type { City } from '../types/city.types.js';
3
+ export declare function mapCity(raw: GeoNamesSearchResultRaw): City;
4
+ //# sourceMappingURL=city.mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"city.mapper.d.ts","sourceRoot":"","sources":["../../src/mappers/city.mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAC9E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,wBAAgB,OAAO,CAAC,GAAG,EAAE,uBAAuB,GAAG,IAAI,CAc1D"}