brasil-ceps-offline 2.0.5 → 2.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 +76 -20
- package/dist/index.d.ts +48 -1
- package/dist/index.js +151 -0
- package/dist/scripts/download-db.js +18 -11
- package/dist/types.d.ts +48 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,10 +7,11 @@ Esqueça limites de requisição (Rate Limits), bloqueios de IP (HTTP 429) ou la
|
|
|
7
7
|
## ✨ Superpoderes
|
|
8
8
|
|
|
9
9
|
- ⚡ **Zero Latência:** Consultas locais em SQLite (tempo de resposta na casa dos ~1ms).
|
|
10
|
-
-
|
|
11
|
-
- 📍 **
|
|
12
|
-
-
|
|
13
|
-
-
|
|
10
|
+
- 📦 **Cálculo de Frete Inteligente:** Calcule a distância real em km entre o seu galpão e o cliente final usando a Fórmula de Haversine — sem custo de API, sem limite de chamadas.
|
|
11
|
+
- 📍 **Geofencing Offline:** Verifique se um CEP está dentro de um raio de entrega em uma única chamada. Ideal para definir zonas de cobertura logística.
|
|
12
|
+
- 🕐 **Gestão de Fusos Horários:** Retorna o fuso horário IANA e o offset UTC atual de qualquer CEP — essencial para sistemas de agendamento que atendem o Acre, Amazonas ou Fernando de Noronha.
|
|
13
|
+
- 🏢 **Enriquecimento de ERP (NFe):** Retorna e valida o código IBGE municipal para emissão de Notas Fiscais, integração com SAP, TOTVS e outros ERPs de forma instantânea.
|
|
14
|
+
- 🛡️ **Sistema de Fallback Seguro:** Se o CEP pesquisado não estiver no banco local, a biblioteca busca os dados silenciosamente em APIs externas (BrasilAPI ou ViaCEP), garantindo que seu app nunca falhe.
|
|
14
15
|
|
|
15
16
|
---
|
|
16
17
|
|
|
@@ -42,19 +43,28 @@ npx brasil-ceps-offline sync
|
|
|
42
43
|
A API foi desenhada para ser simples e tipada.
|
|
43
44
|
|
|
44
45
|
```ts
|
|
45
|
-
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
}
|
|
46
|
+
import { findByCep, getDistance, getCepsInRadius, getTimezone, validateIbge } from 'brasil-ceps-offline';
|
|
47
|
+
|
|
48
|
+
// Busca básica por CEP
|
|
49
|
+
const endereco = findByCep('74740-300');
|
|
50
|
+
console.log(endereco);
|
|
51
|
+
|
|
52
|
+
// Distância entre dois CEPs
|
|
53
|
+
const distancia = getDistance('74740300', '01310100'); // Goiânia → São Paulo
|
|
54
|
+
console.log(`${distancia?.distanceKm.toFixed(1)} km`); // ~873 km
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
// CEPs dentro de um raio de 50 km
|
|
57
|
+
const vizinhos = getCepsInRadius('01310100', 50);
|
|
58
|
+
console.log(`${vizinhos.length} CEPs encontrados no raio de 50km`);
|
|
59
|
+
|
|
60
|
+
// Fuso horário de um CEP
|
|
61
|
+
const fuso = getTimezone('69050001'); // Manaus
|
|
62
|
+
console.log(fuso?.timezone); // "America/Manaus"
|
|
63
|
+
console.log(fuso?.utcOffsetLabel); // "UTC-4"
|
|
64
|
+
|
|
65
|
+
// Validação de IBGE para NFe
|
|
66
|
+
const ibge = validateIbge('74740300', 5208707);
|
|
67
|
+
console.log(ibge?.valid); // true
|
|
58
68
|
```
|
|
59
69
|
|
|
60
70
|
---
|
|
@@ -80,14 +90,60 @@ O retorno é um objeto padronizado com todos os dados fiscais e geográficos pro
|
|
|
80
90
|
|
|
81
91
|
---
|
|
82
92
|
|
|
83
|
-
## 🛠️
|
|
93
|
+
## 🛠️ API Completa
|
|
94
|
+
|
|
95
|
+
### `findByCep(cep)`
|
|
96
|
+
Busca um endereço pelo CEP. Aceita qualquer formato (`"74740300"`, `"74.740-300"`).
|
|
97
|
+
|
|
98
|
+
### `getDistance(cepA, cepB)`
|
|
99
|
+
Calcula a distância em km entre dois CEPs via Fórmula de Haversine.
|
|
100
|
+
```ts
|
|
101
|
+
const result = getDistance('74740300', '01310100');
|
|
102
|
+
// { distanceKm: 872.4, from: '74740300', to: '01310100' }
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `getCepsInRadius(centerCep, radiusKm, limit?)`
|
|
106
|
+
Retorna endereços dentro do raio informado, ordenados por distância crescente.
|
|
107
|
+
```ts
|
|
108
|
+
const zona = getCepsInRadius('01310100', 30); // todos os CEPs a até 30km de São Paulo
|
|
109
|
+
// [{ ...address, distanceKm: 0.3 }, { ...address, distanceKm: 1.1 }, ...]
|
|
110
|
+
```
|
|
84
111
|
|
|
85
|
-
|
|
112
|
+
### `getTimezone(cep)`
|
|
113
|
+
Retorna fuso horário IANA e offset UTC calculado dinamicamente (inclui horário de verão).
|
|
114
|
+
```ts
|
|
115
|
+
const fuso = getTimezone('69050001');
|
|
116
|
+
// { cep: '69050001', timezone: 'America/Manaus', utcOffset: -4, utcOffsetLabel: 'UTC-4' }
|
|
117
|
+
```
|
|
86
118
|
|
|
119
|
+
### `validateIbge(cep, ibgeProvided)`
|
|
120
|
+
Valida o código IBGE informado contra o banco de dados.
|
|
87
121
|
```ts
|
|
88
|
-
|
|
122
|
+
const ok = validateIbge('74740300', 5208707);
|
|
123
|
+
// { valid: true, expected: 5208707, provided: 5208707 }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `findByAddress(state, city?, street?)`
|
|
127
|
+
Busca CEPs por estado, cidade e/ou rua.
|
|
128
|
+
|
|
129
|
+
### `search(pattern, state?, limit?)`
|
|
130
|
+
Busca avançada com padrão LIKE em cidade, bairro e logradouro.
|
|
89
131
|
|
|
90
|
-
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 🛠️ Tipagem (TypeScript)
|
|
135
|
+
|
|
136
|
+
A biblioteca exporta todas as interfaces para facilitar a tipagem no seu projeto:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import type {
|
|
140
|
+
Address,
|
|
141
|
+
DistanceResult,
|
|
142
|
+
AddressWithDistance,
|
|
143
|
+
TimezoneResult,
|
|
144
|
+
IbgeValidationResult,
|
|
145
|
+
Coordinates,
|
|
146
|
+
} from 'brasil-ceps-offline';
|
|
91
147
|
```
|
|
92
148
|
|
|
93
149
|
---
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Database from 'better-sqlite3';
|
|
2
|
-
import { Address, BrasilCepsConfig, FallbackOptions } from './types';
|
|
2
|
+
import { Address, BrasilCepsConfig, FallbackOptions, DistanceResult, AddressWithDistance, TimezoneResult, IbgeValidationResult, Coordinates } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Motor de Inteligência Geográfica Offline para CEPs brasileiros.
|
|
5
5
|
* Dados do Censo IBGE 2022 — sem rede, sem API keys, sem limites.
|
|
@@ -55,6 +55,44 @@ export declare class BrasilCepsOffline {
|
|
|
55
55
|
totalRecords: number;
|
|
56
56
|
databaseSize: string;
|
|
57
57
|
};
|
|
58
|
+
/**
|
|
59
|
+
* Calcula a distância em km entre dois CEPs usando a Fórmula de Haversine.
|
|
60
|
+
* @returns DistanceResult com a distância, ou null se algum dos CEPs não for encontrado
|
|
61
|
+
*/
|
|
62
|
+
getDistance(cepA: string, cepB: string): DistanceResult | null;
|
|
63
|
+
/**
|
|
64
|
+
* Calcula a distância em km entre duas coordenadas geográficas.
|
|
65
|
+
*/
|
|
66
|
+
calculateDistance(coordA: Coordinates, coordB: Coordinates): number;
|
|
67
|
+
/**
|
|
68
|
+
* Retorna todos os endereços dentro de um raio em km a partir de um CEP central.
|
|
69
|
+
* Usa bounding-box SQL para limitar o scan antes de aplicar Haversine exato.
|
|
70
|
+
*
|
|
71
|
+
* @param centerCep CEP do ponto central
|
|
72
|
+
* @param radiusKm Raio em quilômetros
|
|
73
|
+
* @param limit Máximo de resultados (padrão: 500)
|
|
74
|
+
*/
|
|
75
|
+
getCepsInRadius(centerCep: string, radiusKm: number, limit?: number): AddressWithDistance[];
|
|
76
|
+
/**
|
|
77
|
+
* Retorna o fuso horário IANA e o offset UTC atual para um CEP.
|
|
78
|
+
* O offset é calculado dinamicamente (considera horário de verão se aplicável).
|
|
79
|
+
*/
|
|
80
|
+
getTimezone(cep: string): TimezoneResult | null;
|
|
81
|
+
/**
|
|
82
|
+
* Valida se o código IBGE informado corresponde ao CEP.
|
|
83
|
+
* Útil para sistemas de checkout e emissão de NFe.
|
|
84
|
+
*
|
|
85
|
+
* @param cep CEP a consultar
|
|
86
|
+
* @param ibgeProvided Código IBGE informado pelo usuário ou ERP
|
|
87
|
+
*/
|
|
88
|
+
validateIbge(cep: string, ibgeProvided: number): IbgeValidationResult | null;
|
|
89
|
+
/** Fórmula de Haversine — retorna distância em km. */
|
|
90
|
+
private static haversine;
|
|
91
|
+
/**
|
|
92
|
+
* Resolve o offset UTC atual de um fuso IANA usando Intl.DateTimeFormat.
|
|
93
|
+
* Funciona corretamente com horário de verão (onde aplicável).
|
|
94
|
+
*/
|
|
95
|
+
private static resolveUtcOffset;
|
|
58
96
|
/**
|
|
59
97
|
* Fecha a conexão com o banco de dados.
|
|
60
98
|
*/
|
|
@@ -70,3 +108,12 @@ export declare function findByCep(cep: string): Address | null;
|
|
|
70
108
|
export declare function findByAddress(state: string, city?: string, street?: string): Address[];
|
|
71
109
|
/** Busca avançada com padrão LIKE. */
|
|
72
110
|
export declare function search(pattern: string, state?: string, limit?: number): Address[];
|
|
111
|
+
/** Calcula a distância em km entre dois CEPs. */
|
|
112
|
+
export declare function getDistance(cepA: string, cepB: string): DistanceResult | null;
|
|
113
|
+
/** Retorna endereços dentro de um raio em km a partir de um CEP central. */
|
|
114
|
+
export declare function getCepsInRadius(centerCep: string, radiusKm: number, limit?: number): AddressWithDistance[];
|
|
115
|
+
/** Retorna fuso horário IANA e offset UTC atual de um CEP. */
|
|
116
|
+
export declare function getTimezone(cep: string): TimezoneResult | null;
|
|
117
|
+
/** Valida se o código IBGE informado corresponde ao CEP. */
|
|
118
|
+
export declare function validateIbge(cep: string, ibgeProvided: number): IbgeValidationResult | null;
|
|
119
|
+
export type { Address, DistanceResult, AddressWithDistance, TimezoneResult, IbgeValidationResult, Coordinates, FallbackOptions, BrasilCepsConfig, } from './types';
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,10 @@ exports.init = init;
|
|
|
8
8
|
exports.findByCep = findByCep;
|
|
9
9
|
exports.findByAddress = findByAddress;
|
|
10
10
|
exports.search = search;
|
|
11
|
+
exports.getDistance = getDistance;
|
|
12
|
+
exports.getCepsInRadius = getCepsInRadius;
|
|
13
|
+
exports.getTimezone = getTimezone;
|
|
14
|
+
exports.validateIbge = validateIbge;
|
|
11
15
|
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
12
16
|
const config_1 = require("./config");
|
|
13
17
|
const database_1 = require("./database");
|
|
@@ -194,6 +198,133 @@ class BrasilCepsOffline {
|
|
|
194
198
|
databaseSize: `${(size / 1024 / 1024).toFixed(2)} MB`,
|
|
195
199
|
};
|
|
196
200
|
}
|
|
201
|
+
// ─── Cálculo de distância (Haversine) ────────────────────────────────────
|
|
202
|
+
/**
|
|
203
|
+
* Calcula a distância em km entre dois CEPs usando a Fórmula de Haversine.
|
|
204
|
+
* @returns DistanceResult com a distância, ou null se algum dos CEPs não for encontrado
|
|
205
|
+
*/
|
|
206
|
+
getDistance(cepA, cepB) {
|
|
207
|
+
const a = this.findAddressByCep(cepA);
|
|
208
|
+
const b = this.findAddressByCep(cepB);
|
|
209
|
+
if (!a || !b)
|
|
210
|
+
return null;
|
|
211
|
+
return {
|
|
212
|
+
distanceKm: BrasilCepsOffline.haversine({ latitude: a.latitude, longitude: a.longitude }, { latitude: b.latitude, longitude: b.longitude }),
|
|
213
|
+
from: a.cep,
|
|
214
|
+
to: b.cep,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Calcula a distância em km entre duas coordenadas geográficas.
|
|
219
|
+
*/
|
|
220
|
+
calculateDistance(coordA, coordB) {
|
|
221
|
+
return BrasilCepsOffline.haversine(coordA, coordB);
|
|
222
|
+
}
|
|
223
|
+
// ─── Busca por raio (geofencing) ─────────────────────────────────────────
|
|
224
|
+
/**
|
|
225
|
+
* Retorna todos os endereços dentro de um raio em km a partir de um CEP central.
|
|
226
|
+
* Usa bounding-box SQL para limitar o scan antes de aplicar Haversine exato.
|
|
227
|
+
*
|
|
228
|
+
* @param centerCep CEP do ponto central
|
|
229
|
+
* @param radiusKm Raio em quilômetros
|
|
230
|
+
* @param limit Máximo de resultados (padrão: 500)
|
|
231
|
+
*/
|
|
232
|
+
getCepsInRadius(centerCep, radiusKm, limit = 500) {
|
|
233
|
+
this.ensureInitialized();
|
|
234
|
+
const center = this.findAddressByCep(centerCep);
|
|
235
|
+
if (!center || !center.latitude || !center.longitude)
|
|
236
|
+
return [];
|
|
237
|
+
// 1° de latitude ≈ 111 km; 1° de longitude ≈ 111 km * cos(lat)
|
|
238
|
+
const latDelta = radiusKm / 111;
|
|
239
|
+
const lonDelta = radiusKm / (111 * Math.cos((center.latitude * Math.PI) / 180));
|
|
240
|
+
const rows = this.db.prepare(`
|
|
241
|
+
SELECT ${SELECT_ALL} FROM addresses
|
|
242
|
+
WHERE latitude BETWEEN ? AND ?
|
|
243
|
+
AND longitude BETWEEN ? AND ?
|
|
244
|
+
AND latitude IS NOT NULL
|
|
245
|
+
AND longitude IS NOT NULL
|
|
246
|
+
`).all(center.latitude - latDelta, center.latitude + latDelta, center.longitude - lonDelta, center.longitude + lonDelta);
|
|
247
|
+
return rows
|
|
248
|
+
.map((r) => ({
|
|
249
|
+
...r,
|
|
250
|
+
distanceKm: BrasilCepsOffline.haversine({ latitude: center.latitude, longitude: center.longitude }, { latitude: r.latitude, longitude: r.longitude }),
|
|
251
|
+
}))
|
|
252
|
+
.filter((r) => r.distanceKm <= radiusKm)
|
|
253
|
+
.sort((a, b) => a.distanceKm - b.distanceKm)
|
|
254
|
+
.slice(0, limit);
|
|
255
|
+
}
|
|
256
|
+
// ─── Fuso horário ─────────────────────────────────────────────────────────
|
|
257
|
+
/**
|
|
258
|
+
* Retorna o fuso horário IANA e o offset UTC atual para um CEP.
|
|
259
|
+
* O offset é calculado dinamicamente (considera horário de verão se aplicável).
|
|
260
|
+
*/
|
|
261
|
+
getTimezone(cep) {
|
|
262
|
+
const addr = this.findAddressByCep(cep);
|
|
263
|
+
if (!addr || !addr.timezone)
|
|
264
|
+
return null;
|
|
265
|
+
const utcOffset = BrasilCepsOffline.resolveUtcOffset(addr.timezone);
|
|
266
|
+
return {
|
|
267
|
+
cep: addr.cep,
|
|
268
|
+
timezone: addr.timezone,
|
|
269
|
+
utcOffset,
|
|
270
|
+
utcOffsetLabel: `UTC${utcOffset >= 0 ? '+' : ''}${utcOffset}`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// ─── Validação IBGE ───────────────────────────────────────────────────────
|
|
274
|
+
/**
|
|
275
|
+
* Valida se o código IBGE informado corresponde ao CEP.
|
|
276
|
+
* Útil para sistemas de checkout e emissão de NFe.
|
|
277
|
+
*
|
|
278
|
+
* @param cep CEP a consultar
|
|
279
|
+
* @param ibgeProvided Código IBGE informado pelo usuário ou ERP
|
|
280
|
+
*/
|
|
281
|
+
validateIbge(cep, ibgeProvided) {
|
|
282
|
+
const addr = this.findAddressByCep(cep);
|
|
283
|
+
if (!addr)
|
|
284
|
+
return null;
|
|
285
|
+
return {
|
|
286
|
+
valid: addr.ibge === ibgeProvided,
|
|
287
|
+
expected: addr.ibge,
|
|
288
|
+
provided: ibgeProvided,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// ─── Helpers estáticos ────────────────────────────────────────────────────
|
|
292
|
+
/** Fórmula de Haversine — retorna distância em km. */
|
|
293
|
+
static haversine(a, b) {
|
|
294
|
+
const R = 6371; // raio médio da Terra em km
|
|
295
|
+
const φ1 = (a.latitude * Math.PI) / 180;
|
|
296
|
+
const φ2 = (b.latitude * Math.PI) / 180;
|
|
297
|
+
const Δφ = ((b.latitude - a.latitude) * Math.PI) / 180;
|
|
298
|
+
const Δλ = ((b.longitude - a.longitude) * Math.PI) / 180;
|
|
299
|
+
const sinΔφ = Math.sin(Δφ / 2);
|
|
300
|
+
const sinΔλ = Math.sin(Δλ / 2);
|
|
301
|
+
const h = sinΔφ * sinΔφ + Math.cos(φ1) * Math.cos(φ2) * sinΔλ * sinΔλ;
|
|
302
|
+
return R * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h));
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Resolve o offset UTC atual de um fuso IANA usando Intl.DateTimeFormat.
|
|
306
|
+
* Funciona corretamente com horário de verão (onde aplicável).
|
|
307
|
+
*/
|
|
308
|
+
static resolveUtcOffset(ianaTimezone) {
|
|
309
|
+
try {
|
|
310
|
+
const now = new Date();
|
|
311
|
+
const parts = new Intl.DateTimeFormat('en', {
|
|
312
|
+
timeZone: ianaTimezone, timeZoneName: 'shortOffset',
|
|
313
|
+
}).formatToParts(now);
|
|
314
|
+
const offset = parts.find((p) => p.type === 'timeZoneName')?.value ?? 'GMT+0';
|
|
315
|
+
// offset é algo como "GMT-3" ou "GMT+5:30"
|
|
316
|
+
const match = offset.match(/GMT([+-])(\d+)(?::(\d+))?/);
|
|
317
|
+
if (!match)
|
|
318
|
+
return 0;
|
|
319
|
+
const sign = match[1] === '+' ? 1 : -1;
|
|
320
|
+
const hours = parseInt(match[2], 10);
|
|
321
|
+
const minutes = parseInt(match[3] ?? '0', 10);
|
|
322
|
+
return sign * (hours + minutes / 60);
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
197
328
|
/**
|
|
198
329
|
* Fecha a conexão com o banco de dados.
|
|
199
330
|
*/
|
|
@@ -232,3 +363,23 @@ function search(pattern, state, limit) {
|
|
|
232
363
|
exports.brasilCeps.initialize();
|
|
233
364
|
return exports.brasilCeps.searchAddress(pattern, state, limit);
|
|
234
365
|
}
|
|
366
|
+
/** Calcula a distância em km entre dois CEPs. */
|
|
367
|
+
function getDistance(cepA, cepB) {
|
|
368
|
+
exports.brasilCeps.initialize();
|
|
369
|
+
return exports.brasilCeps.getDistance(cepA, cepB);
|
|
370
|
+
}
|
|
371
|
+
/** Retorna endereços dentro de um raio em km a partir de um CEP central. */
|
|
372
|
+
function getCepsInRadius(centerCep, radiusKm, limit) {
|
|
373
|
+
exports.brasilCeps.initialize();
|
|
374
|
+
return exports.brasilCeps.getCepsInRadius(centerCep, radiusKm, limit);
|
|
375
|
+
}
|
|
376
|
+
/** Retorna fuso horário IANA e offset UTC atual de um CEP. */
|
|
377
|
+
function getTimezone(cep) {
|
|
378
|
+
exports.brasilCeps.initialize();
|
|
379
|
+
return exports.brasilCeps.getTimezone(cep);
|
|
380
|
+
}
|
|
381
|
+
/** Valida se o código IBGE informado corresponde ao CEP. */
|
|
382
|
+
function validateIbge(cep, ibgeProvided) {
|
|
383
|
+
exports.brasilCeps.initialize();
|
|
384
|
+
return exports.brasilCeps.validateIbge(cep, ibgeProvided);
|
|
385
|
+
}
|
|
@@ -8,8 +8,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
8
8
|
const promises_1 = require("stream/promises");
|
|
9
9
|
const stream_1 = require("stream");
|
|
10
10
|
const zlib_1 = __importDefault(require("zlib"));
|
|
11
|
-
// ⚠️ Atualizado para v2.0.
|
|
12
|
-
const URL_DO_BANCO = 'https://github.com/kaique-oliveira/brasil-ceps-offline/releases/download/v2.0.
|
|
11
|
+
// ⚠️ Atualizado para v2.0.5 - Schema IBGE 2022 com timezone, DDD e coordenadas
|
|
12
|
+
const URL_DO_BANCO = 'https://github.com/kaique-oliveira/brasil-ceps-offline/releases/download/v2.0.5/brasil-ceps.sqlite.gz';
|
|
13
13
|
const DB_DIR = path_1.default.join(__dirname, '..', '..', '.db');
|
|
14
14
|
const DB_PATH = path_1.default.join(DB_DIR, 'ceps.sqlite');
|
|
15
15
|
async function downloadDatabase() {
|
|
@@ -20,17 +20,24 @@ async function downloadDatabase() {
|
|
|
20
20
|
if (!fs_1.default.existsSync(DB_DIR)) {
|
|
21
21
|
fs_1.default.mkdirSync(DB_DIR, { recursive: true });
|
|
22
22
|
}
|
|
23
|
-
// Verificação de
|
|
23
|
+
// Verificação de schema — garante que o banco tem as colunas do schema v2
|
|
24
24
|
if (fs_1.default.existsSync(DB_PATH)) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
try {
|
|
26
|
+
const Database = require('better-sqlite3');
|
|
27
|
+
const db = new Database(DB_PATH, { readonly: true, fileMustExist: true });
|
|
28
|
+
const cols = db.pragma('table_info(addresses)');
|
|
29
|
+
db.close();
|
|
30
|
+
const hasTimezone = cols.some((c) => c.name === 'timezone');
|
|
31
|
+
if (hasTimezone) {
|
|
32
|
+
console.log('✅ Banco de CEPs (schema v2) já está instalado.');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
console.log('⚠️ Schema desatualizado detectado (falta timezone). Baixando banco v2...');
|
|
31
36
|
}
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
catch {
|
|
38
|
+
console.log('⚠️ Banco existente não pôde ser verificado. Baixando novamente...');
|
|
39
|
+
}
|
|
40
|
+
fs_1.default.unlinkSync(DB_PATH);
|
|
34
41
|
}
|
|
35
42
|
console.log('📦 Baixando banco de dados de CEPs do Brasil offline (baseada no Censo 2022)...');
|
|
36
43
|
try {
|
package/dist/types.d.ts
CHANGED
|
@@ -24,6 +24,54 @@ export interface Address {
|
|
|
24
24
|
/** Longitude do centróide do logradouro — Censo 2022 (ex: -49.2019) */
|
|
25
25
|
longitude: number;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Resultado do cálculo de distância entre dois pontos.
|
|
29
|
+
*/
|
|
30
|
+
export interface DistanceResult {
|
|
31
|
+
/** Distância em quilômetros */
|
|
32
|
+
distanceKm: number;
|
|
33
|
+
/** CEP de origem */
|
|
34
|
+
from: string;
|
|
35
|
+
/** CEP de destino */
|
|
36
|
+
to: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Endereço acrescido da distância ao ponto de referência (em km).
|
|
40
|
+
*/
|
|
41
|
+
export interface AddressWithDistance extends Address {
|
|
42
|
+
distanceKm: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resultado da consulta de fuso horário de um CEP.
|
|
46
|
+
*/
|
|
47
|
+
export interface TimezoneResult {
|
|
48
|
+
/** CEP consultado */
|
|
49
|
+
cep: string;
|
|
50
|
+
/** Fuso horário IANA (ex: "America/Sao_Paulo") */
|
|
51
|
+
timezone: string;
|
|
52
|
+
/** Offset UTC atual em horas (ex: -3 para BRT, -4 para AMT) */
|
|
53
|
+
utcOffset: number;
|
|
54
|
+
/** Representação legível (ex: "UTC-3") */
|
|
55
|
+
utcOffsetLabel: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resultado da validação de código IBGE.
|
|
59
|
+
*/
|
|
60
|
+
export interface IbgeValidationResult {
|
|
61
|
+
/** Se o código IBGE informado confere com o CEP */
|
|
62
|
+
valid: boolean;
|
|
63
|
+
/** Código IBGE real do CEP */
|
|
64
|
+
expected: number;
|
|
65
|
+
/** Código IBGE informado para validação */
|
|
66
|
+
provided: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Par de coordenadas geográficas.
|
|
70
|
+
*/
|
|
71
|
+
export interface Coordinates {
|
|
72
|
+
latitude: number;
|
|
73
|
+
longitude: number;
|
|
74
|
+
}
|
|
27
75
|
/**
|
|
28
76
|
* Opções para busca com fallback online.
|
|
29
77
|
* Usadas apenas pelo método assíncrono `findAddressByCepWithFallback`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brasil-ceps-offline",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Motor de Inteligência Geográfica Offline para CEPs brasileiros — dados do Censo IBGE 2022 com IBGE, DDD, fuso horário e coordenadas",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|