catastrogps 1.1.0 → 1.2.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 +114 -55
- package/dist/index.d.mts +169 -2
- package/dist/index.d.ts +169 -2
- package/dist/index.js +195 -58
- package/dist/index.mjs +190 -57
- package/package.json +9 -4
package/dist/index.mjs
CHANGED
|
@@ -25,8 +25,24 @@ var NotFoundError = class extends CatastroGPSError {
|
|
|
25
25
|
var ValidationError = class extends CatastroGPSError {
|
|
26
26
|
name = "ValidationError";
|
|
27
27
|
};
|
|
28
|
+
var PlaceNameError = class extends ValidationError {
|
|
29
|
+
name = "PlaceNameError";
|
|
30
|
+
get location() {
|
|
31
|
+
const details = this.details;
|
|
32
|
+
return details?.location;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
28
35
|
var CoverageError = class extends CatastroGPSError {
|
|
29
36
|
name = "CoverageError";
|
|
37
|
+
get country() {
|
|
38
|
+
return this.details?.country;
|
|
39
|
+
}
|
|
40
|
+
get feature() {
|
|
41
|
+
return this.details?.feature;
|
|
42
|
+
}
|
|
43
|
+
get supportedCountries() {
|
|
44
|
+
return this.details?.supportedCountries ?? [];
|
|
45
|
+
}
|
|
30
46
|
};
|
|
31
47
|
var AmbiguousReferenceError = class extends CatastroGPSError {
|
|
32
48
|
name = "AmbiguousReferenceError";
|
|
@@ -37,6 +53,10 @@ var AmbiguousReferenceError = class extends CatastroGPSError {
|
|
|
37
53
|
};
|
|
38
54
|
var QuotaExceededError = class extends CatastroGPSError {
|
|
39
55
|
name = "QuotaExceededError";
|
|
56
|
+
quota;
|
|
57
|
+
get prepaidBalanceEmpty() {
|
|
58
|
+
return this.quota?.overage?.enabled === true;
|
|
59
|
+
}
|
|
40
60
|
};
|
|
41
61
|
var RateLimitError = class extends CatastroGPSError {
|
|
42
62
|
name = "RateLimitError";
|
|
@@ -55,14 +75,19 @@ var TimeoutError = class extends CatastroGPSError {
|
|
|
55
75
|
var NetworkError = class extends CatastroGPSError {
|
|
56
76
|
name = "NetworkError";
|
|
57
77
|
};
|
|
58
|
-
function errorFromResponse(status, body, retryAfter) {
|
|
78
|
+
function errorFromResponse(status, body, retryAfter, quota) {
|
|
59
79
|
const code = typeof body.code === "string" ? body.code : void 0;
|
|
60
80
|
const message = typeof body.error === "string" && body.error || typeof body.message === "string" && body.message || `HTTP ${status}`;
|
|
61
81
|
const details = body.data ?? body.parsed;
|
|
62
82
|
const options = { status, code, details, body };
|
|
63
83
|
if (code === "CNV_AMBIGUOUS" || status === 300) return new AmbiguousReferenceError(message, options);
|
|
64
84
|
if (code === "CNV_COVERAGE") return new CoverageError(message, options);
|
|
65
|
-
if (code === "
|
|
85
|
+
if (code === "CNV_PLACE_NAME") return new PlaceNameError(message, options);
|
|
86
|
+
if (code === "KEY_AUTH_004") {
|
|
87
|
+
const error = new QuotaExceededError(message, options);
|
|
88
|
+
error.quota = quota;
|
|
89
|
+
return error;
|
|
90
|
+
}
|
|
66
91
|
if (status === 429) {
|
|
67
92
|
const error = new RateLimitError(message, options);
|
|
68
93
|
error.retryAfterSeconds = retryAfter;
|
|
@@ -100,7 +125,75 @@ var ExportResource = class {
|
|
|
100
125
|
}
|
|
101
126
|
};
|
|
102
127
|
|
|
128
|
+
// src/types.ts
|
|
129
|
+
var COUNTRY_CODES = [
|
|
130
|
+
"ES",
|
|
131
|
+
"PV",
|
|
132
|
+
"NA",
|
|
133
|
+
"PT",
|
|
134
|
+
"FR",
|
|
135
|
+
"IT",
|
|
136
|
+
"DE",
|
|
137
|
+
"AT",
|
|
138
|
+
"CH",
|
|
139
|
+
"LI",
|
|
140
|
+
"BE",
|
|
141
|
+
"NL",
|
|
142
|
+
"LU",
|
|
143
|
+
"PL",
|
|
144
|
+
"CZ",
|
|
145
|
+
"SK",
|
|
146
|
+
"SI",
|
|
147
|
+
"HR",
|
|
148
|
+
"BG",
|
|
149
|
+
"GR",
|
|
150
|
+
"CY",
|
|
151
|
+
"DK",
|
|
152
|
+
"SE",
|
|
153
|
+
"NO",
|
|
154
|
+
"FI",
|
|
155
|
+
"IS",
|
|
156
|
+
"EE",
|
|
157
|
+
"LV",
|
|
158
|
+
"LT",
|
|
159
|
+
"IE",
|
|
160
|
+
"UK"
|
|
161
|
+
];
|
|
162
|
+
var ENRICHMENT_COUNTRY_CODES = ["ES", "PV", "NA", "PT", "FR", "IT", "DE"];
|
|
163
|
+
var ADDRESS_COUNTRY_CODES = [
|
|
164
|
+
"ES",
|
|
165
|
+
"FR",
|
|
166
|
+
"IT",
|
|
167
|
+
"DE",
|
|
168
|
+
"AT",
|
|
169
|
+
"NL",
|
|
170
|
+
"BE",
|
|
171
|
+
"PL",
|
|
172
|
+
"CH",
|
|
173
|
+
"CZ",
|
|
174
|
+
"DK",
|
|
175
|
+
"NO",
|
|
176
|
+
"FI",
|
|
177
|
+
"EE",
|
|
178
|
+
"LV",
|
|
179
|
+
"LT",
|
|
180
|
+
"SI",
|
|
181
|
+
"SK",
|
|
182
|
+
"BG",
|
|
183
|
+
"GR",
|
|
184
|
+
"CY",
|
|
185
|
+
"LU",
|
|
186
|
+
"LI",
|
|
187
|
+
"IS",
|
|
188
|
+
"IE",
|
|
189
|
+
"UK",
|
|
190
|
+
"PT"
|
|
191
|
+
];
|
|
192
|
+
var UNITS_COUNTRY_CODES = ["ES", "PV", "NA"];
|
|
193
|
+
|
|
103
194
|
// src/resources/parcels.ts
|
|
195
|
+
var MIN_COMPARE = 2;
|
|
196
|
+
var MAX_COMPARE = 3;
|
|
104
197
|
var ParcelsResource = class {
|
|
105
198
|
constructor(client) {
|
|
106
199
|
this.client = client;
|
|
@@ -133,7 +226,46 @@ var ParcelsResource = class {
|
|
|
133
226
|
});
|
|
134
227
|
return data.agro ?? data;
|
|
135
228
|
}
|
|
229
|
+
async terrain(reference, options = {}) {
|
|
230
|
+
return this.client.request("GET", parcelPath(reference, "/terrain"), { query: locatedQuery(options) });
|
|
231
|
+
}
|
|
232
|
+
async groundMotion(reference, options = {}) {
|
|
233
|
+
return this.client.request("GET", parcelPath(reference, "/ground-motion"), { query: locatedQuery(options) });
|
|
234
|
+
}
|
|
235
|
+
async market(reference, options = {}) {
|
|
236
|
+
return this.client.request("GET", parcelPath(reference, "/market"), { query: { country: options.country } });
|
|
237
|
+
}
|
|
238
|
+
async score(reference, options = {}) {
|
|
239
|
+
return this.client.request("GET", parcelPath(reference, "/score"), { query: { country: options.country } });
|
|
240
|
+
}
|
|
241
|
+
async valueHistory(reference, options = {}) {
|
|
242
|
+
return this.client.request("GET", parcelPath(reference, "/value-history"), {
|
|
243
|
+
query: { country: options.country }
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async compare(parcels) {
|
|
247
|
+
if (parcels.length < MIN_COMPARE || parcels.length > MAX_COMPARE) {
|
|
248
|
+
throw new CatastroGPSError(`compare takes ${MIN_COMPARE} or ${MAX_COMPARE} parcels`);
|
|
249
|
+
}
|
|
250
|
+
const parcelas = parcels.map((parcel) => {
|
|
251
|
+
if (!parcel.reference?.trim()) throw new CatastroGPSError("reference must not be empty");
|
|
252
|
+
if (!ENRICHMENT_COUNTRY_CODES.includes(parcel.country)) {
|
|
253
|
+
throw new CatastroGPSError(`country must be one of ${ENRICHMENT_COUNTRY_CODES.join(", ")}`);
|
|
254
|
+
}
|
|
255
|
+
return { ref_catastral: parcel.reference.trim(), country: parcel.country };
|
|
256
|
+
});
|
|
257
|
+
return this.client.request("POST", "/api/catastro/compare", { body: { parcelas } });
|
|
258
|
+
}
|
|
136
259
|
};
|
|
260
|
+
function locatedQuery(options) {
|
|
261
|
+
const hasLat = options.lat !== void 0;
|
|
262
|
+
const hasLng = options.lng !== void 0;
|
|
263
|
+
if (hasLat !== hasLng) throw new CatastroGPSError("lat and lng go together");
|
|
264
|
+
if (hasLat && (!Number.isFinite(options.lat) || !Number.isFinite(options.lng))) {
|
|
265
|
+
throw new CatastroGPSError("lat and lng must be finite numbers");
|
|
266
|
+
}
|
|
267
|
+
return { country: options.country, lat: options.lat, lng: options.lng };
|
|
268
|
+
}
|
|
137
269
|
function parcelPath(reference, suffix = "") {
|
|
138
270
|
if (!reference.trim()) throw new CatastroGPSError("reference must not be empty");
|
|
139
271
|
return `/api/catastro/${encodeURIComponent(reference.trim())}${suffix}`;
|
|
@@ -151,6 +283,7 @@ var FincasResource = class {
|
|
|
151
283
|
if (!params.q?.trim() && !params.street?.trim()) throw new CatastroGPSError("q or street is required");
|
|
152
284
|
return this.client.request("GET", "/api/search/address/candidates", {
|
|
153
285
|
query: {
|
|
286
|
+
country: params.country,
|
|
154
287
|
q: params.q?.trim(),
|
|
155
288
|
street: params.street?.trim(),
|
|
156
289
|
number: params.number,
|
|
@@ -167,7 +300,7 @@ var FincasResource = class {
|
|
|
167
300
|
for (let count = 0; count < MAX_PAGES; count += 1) {
|
|
168
301
|
const previous = previousByCursor.get(cursor ?? "");
|
|
169
302
|
const response = await this.client.conditionalRequest("GET", path, {
|
|
170
|
-
query: { cursor },
|
|
303
|
+
query: { cursor, country: options.country },
|
|
171
304
|
ifNoneMatch: previous?.etag
|
|
172
305
|
});
|
|
173
306
|
const data = response.notModified && previous ? previous.data : response.data;
|
|
@@ -205,7 +338,7 @@ var FincasResource = class {
|
|
|
205
338
|
};
|
|
206
339
|
|
|
207
340
|
// src/version.ts
|
|
208
|
-
var VERSION = "1.
|
|
341
|
+
var VERSION = "1.2.0";
|
|
209
342
|
|
|
210
343
|
// src/client.ts
|
|
211
344
|
var DEFAULT_BASE_URL = "https://api.catastrogps.es";
|
|
@@ -300,17 +433,22 @@ var CatastroGPS = class {
|
|
|
300
433
|
} finally {
|
|
301
434
|
clearTimeout(timer);
|
|
302
435
|
}
|
|
303
|
-
this.captureQuota(response.headers);
|
|
436
|
+
const quota = this.captureQuota(response.headers);
|
|
304
437
|
const etag = response.headers.get("ETag") ?? void 0;
|
|
305
|
-
if (response.status === NOT_MODIFIED) return { etag: etag ?? options.ifNoneMatch, notModified: true };
|
|
438
|
+
if (response.status === NOT_MODIFIED) return { etag: etag ?? options.ifNoneMatch, notModified: true, quota };
|
|
306
439
|
if (!response.ok) {
|
|
307
440
|
const body2 = await readJson(response);
|
|
308
441
|
const retryAfter = Number(response.headers.get("Retry-After"));
|
|
309
|
-
throw errorFromResponse(
|
|
442
|
+
throw errorFromResponse(
|
|
443
|
+
response.status,
|
|
444
|
+
body2,
|
|
445
|
+
Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter : void 0,
|
|
446
|
+
readQuota(response.headers)
|
|
447
|
+
);
|
|
310
448
|
}
|
|
311
|
-
if (options.raw) return { data: await response.arrayBuffer(), etag, notModified: false };
|
|
449
|
+
if (options.raw) return { data: await response.arrayBuffer(), etag, notModified: false, quota };
|
|
312
450
|
const body = await readJson(response);
|
|
313
|
-
return { data: "data" in body ? body.data : body, etag, notModified: false };
|
|
451
|
+
return { data: "data" in body ? body.data : body, etag, notModified: false, quota };
|
|
314
452
|
}
|
|
315
453
|
buildUrl(path, query) {
|
|
316
454
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
@@ -334,20 +472,47 @@ var CatastroGPS = class {
|
|
|
334
472
|
captureQuota(headers) {
|
|
335
473
|
const quota = readQuota(headers);
|
|
336
474
|
if (quota) this.lastQuota = quota;
|
|
475
|
+
return quota;
|
|
337
476
|
}
|
|
338
477
|
};
|
|
339
478
|
function readQuota(headers) {
|
|
340
|
-
const
|
|
341
|
-
const
|
|
342
|
-
if (
|
|
343
|
-
const limit = headers
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
479
|
+
const quota = {};
|
|
480
|
+
const plan = headers.get("X-Quota-Tier");
|
|
481
|
+
if (plan !== null) quota.plan = plan;
|
|
482
|
+
const limit = numberHeader(headers, "X-Quota-Limit");
|
|
483
|
+
if (limit !== void 0) quota.limit = limit;
|
|
484
|
+
const remaining = numberHeader(headers, "X-Quota-Remaining");
|
|
485
|
+
if (remaining !== void 0) quota.remaining = remaining;
|
|
486
|
+
const resetsAt = headers.get("X-Quota-Reset");
|
|
487
|
+
if (resetsAt !== null) quota.resetsAt = resetsAt;
|
|
488
|
+
if (headers.get("X-Quota-Overage")?.toLowerCase() === "on") {
|
|
489
|
+
quota.overage = {
|
|
490
|
+
enabled: true,
|
|
491
|
+
balance: numberHeader(headers, "X-Quota-Balance"),
|
|
492
|
+
unitPrice: numberHeader(headers, "X-Quota-Overage-Unit-Price"),
|
|
493
|
+
remainingUnits: numberHeader(headers, "X-Quota-Overage-Remaining")
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
if (headers.has("X-RateLimit-Limit") || headers.has("X-RateLimit-Remaining")) {
|
|
497
|
+
quota.rateLimit = {
|
|
498
|
+
limit: numberHeader(headers, "X-RateLimit-Limit"),
|
|
499
|
+
remaining: numberHeader(headers, "X-RateLimit-Remaining"),
|
|
500
|
+
resetSeconds: numberHeader(headers, "X-RateLimit-Reset")
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
if (headers.has("X-Quota-Finca-Cap")) {
|
|
504
|
+
quota.fincaCap = {
|
|
505
|
+
cap: numberHeader(headers, "X-Quota-Finca-Cap"),
|
|
506
|
+
used: numberHeader(headers, "X-Quota-Finca-Cap-Used")
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return Object.keys(quota).length > 0 ? quota : void 0;
|
|
510
|
+
}
|
|
511
|
+
function numberHeader(headers, name) {
|
|
512
|
+
const raw = headers.get(name);
|
|
513
|
+
if (raw === null || raw.trim() === "") return void 0;
|
|
514
|
+
const value = Number(raw);
|
|
515
|
+
return Number.isFinite(value) ? value : void 0;
|
|
351
516
|
}
|
|
352
517
|
async function readJson(response) {
|
|
353
518
|
try {
|
|
@@ -364,43 +529,8 @@ function readEnv(name) {
|
|
|
364
529
|
function sleep(ms) {
|
|
365
530
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
366
531
|
}
|
|
367
|
-
|
|
368
|
-
// src/types.ts
|
|
369
|
-
var COUNTRY_CODES = [
|
|
370
|
-
"ES",
|
|
371
|
-
"PV",
|
|
372
|
-
"NA",
|
|
373
|
-
"PT",
|
|
374
|
-
"FR",
|
|
375
|
-
"IT",
|
|
376
|
-
"DE",
|
|
377
|
-
"AT",
|
|
378
|
-
"CH",
|
|
379
|
-
"LI",
|
|
380
|
-
"BE",
|
|
381
|
-
"NL",
|
|
382
|
-
"LU",
|
|
383
|
-
"PL",
|
|
384
|
-
"CZ",
|
|
385
|
-
"SK",
|
|
386
|
-
"SI",
|
|
387
|
-
"HR",
|
|
388
|
-
"BG",
|
|
389
|
-
"GR",
|
|
390
|
-
"CY",
|
|
391
|
-
"DK",
|
|
392
|
-
"SE",
|
|
393
|
-
"NO",
|
|
394
|
-
"FI",
|
|
395
|
-
"IS",
|
|
396
|
-
"EE",
|
|
397
|
-
"LV",
|
|
398
|
-
"LT",
|
|
399
|
-
"IE",
|
|
400
|
-
"UK"
|
|
401
|
-
];
|
|
402
|
-
var ENRICHMENT_COUNTRY_CODES = ["ES", "PV", "NA", "PT", "FR", "IT", "DE"];
|
|
403
532
|
export {
|
|
533
|
+
ADDRESS_COUNTRY_CODES,
|
|
404
534
|
AmbiguousReferenceError,
|
|
405
535
|
AuthenticationError,
|
|
406
536
|
COUNTRY_CODES,
|
|
@@ -414,11 +544,14 @@ export {
|
|
|
414
544
|
ForbiddenError,
|
|
415
545
|
NetworkError,
|
|
416
546
|
NotFoundError,
|
|
547
|
+
PlaceNameError,
|
|
417
548
|
QuotaExceededError,
|
|
418
549
|
RateLimitError,
|
|
419
550
|
ServerError,
|
|
420
551
|
ServiceUnavailableError,
|
|
421
552
|
TimeoutError,
|
|
553
|
+
UNITS_COUNTRY_CODES,
|
|
422
554
|
VERSION,
|
|
423
|
-
ValidationError
|
|
555
|
+
ValidationError,
|
|
556
|
+
readQuota
|
|
424
557
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catastrogps",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Official JavaScript/TypeScript client for the Catastro GPS API:
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Official JavaScript/TypeScript client for the Catastro GPS API: parcels from 26 European cadastres plus the Basque Country and Navarre by reference, coordinates or address; building units, terrain, ground motion, solar and more",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
29
29
|
"typecheck": "tsc --noEmit",
|
|
30
30
|
"test": "vitest run",
|
|
31
|
+
"test:coverage": "vitest run --coverage",
|
|
31
32
|
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
32
33
|
},
|
|
33
34
|
"keywords": [
|
|
@@ -48,6 +49,9 @@
|
|
|
48
49
|
"germany",
|
|
49
50
|
"real-estate",
|
|
50
51
|
"proptech",
|
|
52
|
+
"copernicus",
|
|
53
|
+
"natura-2000",
|
|
54
|
+
"ground-motion",
|
|
51
55
|
"sdk"
|
|
52
56
|
],
|
|
53
57
|
"author": "The Hidden Panda <soporte@catastrogps.es>",
|
|
@@ -57,10 +61,11 @@
|
|
|
57
61
|
"email": "soporte@catastrogps.es"
|
|
58
62
|
},
|
|
59
63
|
"devDependencies": {
|
|
64
|
+
"@types/node": "^22.0.0",
|
|
65
|
+
"@vitest/coverage-v8": "^3.2.7",
|
|
60
66
|
"tsup": "^8.0.0",
|
|
61
67
|
"typescript": "^5.4.0",
|
|
62
|
-
"vitest": "^3.0.0"
|
|
63
|
-
"@types/node": "^22.0.0"
|
|
68
|
+
"vitest": "^3.0.0"
|
|
64
69
|
},
|
|
65
70
|
"engines": {
|
|
66
71
|
"node": ">=18.0.0"
|