@eusilvio/cep-lookup 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.
package/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # @eusilvio/cep-lookup
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/@eusilvio/cep-lookup.svg)](https://www.npmjs.com/package/@eusilvio/cep-lookup)
4
+ [![Build Status](https://img.shields.io/github/workflow/status/eusilvio/cep-lookup/CI)](https://github.com/eusilvio/cep-lookup/actions)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ A modern, flexible, and agnostic CEP (Brazilian postal code) lookup library written in TypeScript.
8
+
9
+ ## About
10
+
11
+ `@eusilvio/cep-lookup` was created to solve address lookup from a CEP in a different way. Instead of relying on a single data source, it queries multiple services simultaneously and returns the response from the fastest one.
12
+
13
+ Its agnostic design allows it to be used in any JavaScript environment with any HTTP client, and its powerful "mapper" system allows you to format the data output exactly as you need.
14
+
15
+ ## Key Features
16
+
17
+ - **Multiple Providers (Race Strategy)**: Queries multiple CEP APIs at the same time and uses the first valid response.
18
+ - **Class-Based API**: Create a reusable instance with your settings.
19
+ - **Customizable Return Format**: Provide a `mapper` function to transform the address data into any format your application needs.
20
+ - **HTTP Client Agnostic**: You provide the fetch function, giving you full control over the requests. Defaults to global `fetch` if not provided.
21
+ - **Modular and Extensible Architecture**: Adding a new CEP data source is trivial.
22
+ - **Fully Typed**: Developed with TypeScript to ensure type safety and a great developer experience.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @eusilvio/cep-lookup
28
+ ```
29
+
30
+ ## How to Use
31
+
32
+ ### Example 1: Basic Usage
33
+
34
+ ```typescript
35
+ import { CepLookup, Address } from "@eusilvio/cep-lookup";
36
+ import {
37
+ viaCepProvider,
38
+ brasilApiProvider,
39
+ } from "@eusilvio/cep-lookup/providers";
40
+
41
+ // 1. Create an instance of CepLookup (fetcher is now optional and defaults to global fetch)
42
+ const cepLookup = new CepLookup({
43
+ providers: [viaCepProvider, brasilApiProvider],
44
+ });
45
+
46
+ // 2. Look up a CEP
47
+ cepLookup.lookup("01001-000").then((address: Address) => {
48
+ console.log("Address found:", address);
49
+ // Output:
50
+ // {
51
+ // cep: '01001-000',
52
+ // state: 'SP',
53
+ // city: 'São Paulo',
54
+ // neighborhood: 'Sé',
55
+ // street: 'Praça da Sé',
56
+ // service: 'ViaCEP'
57
+ // }
58
+ });
59
+ ```
60
+
61
+ ### Example 2: Custom Return with `mapper`
62
+
63
+ ```typescript
64
+ import { CepLookup, Address } from "@eusilvio/cep-lookup";
65
+ import { viaCepProvider } from "@eusilvio/cep-lookup/providers";
66
+
67
+ const cepLookup = new CepLookup({
68
+ providers: [viaCepProvider],
69
+ });
70
+
71
+ // 1. Define your "mapper" function
72
+ interface CustomAddress {
73
+ postalCode: string;
74
+ fullAddress: string;
75
+ source: string;
76
+ }
77
+
78
+ const myMapper = (address: Address): CustomAddress => {
79
+ return {
80
+ postalCode: address.cep,
81
+ fullAddress: `${address.street}, ${address.neighborhood} - ${address.city}/${address.state}`,
82
+ source: address.service,
83
+ };
84
+ };
85
+
86
+ // 2. Look up a CEP with the mapper
87
+ cepLookup.lookup("01001-000", myMapper).then((customAddress: CustomAddress) => {
88
+ console.log("Address found (custom format):", customAddress);
89
+ // Output:
90
+ // {
91
+ // postalCode: '01001-000',
92
+ // fullAddress: 'Praça da Sé, Sé - São Paulo/SP',
93
+ // source: 'ViaCEP'
94
+ // }
95
+ });
96
+ ```
97
+
98
+ ## API
99
+
100
+ ### `new CepLookup(options)`
101
+
102
+ Creates a new `CepLookup` instance.
103
+
104
+ - `options`: A configuration object.
105
+ - `providers` (Provider[], **required**): An array of providers that will be queried.
106
+ - `fetcher` (Fetcher, _optional_): Your asynchronous function that fetches data from a URL. Defaults to global `fetch` if not provided.
107
+
108
+ ### `cepLookup.lookup<T = Address>(cep, mapper?): Promise<T>`
109
+
110
+ Returns a `Promise` that resolves to the address in the default format (`Address`) or in the custom format `T` if a `mapper` is provided.
111
+
112
+ - `cep` (string, **required**): The CEP to be queried.
113
+ - `mapper` ((address: Address) => T, _optional_): A function that receives the default `Address` object and transforms it into a new format `T`.
114
+
115
+ ## Examples
116
+
117
+ You can find more detailed examples in the `examples/` directory:
118
+
119
+ - **Basic Usage**: `examples/example.ts`
120
+ - **Custom Provider**: `examples/custom-provider-example.ts`
121
+ - **Node.js Usage**: `examples/node-example.ts`
122
+ - **React Component**: `examples/react-example.tsx`
123
+ - **React Hook**: `examples/react-hook-example.ts`
124
+ - **Angular Component/Service**: `examples/angular-example.ts`
125
+
126
+ To run the examples, use the following commands:
127
+
128
+ ```bash
129
+ npm run example
130
+ npm run custom-example
131
+ npm run node-example
132
+ ```
133
+
134
+ ## Creating a Custom Provider
135
+
136
+ Your custom provider must always transform the API response to the library's default `Address` interface. The user's `mapper` will handle the final customization.
137
+
138
+ ```typescript
139
+ import { Provider, Address } from "@eusilvio/cep-lookup";
140
+
141
+ const myCustomProvider: Provider = {
142
+ name: "MyCustomAPI",
143
+ buildUrl: (cep: string) => `https://myapi.com/cep/${cep}`,
144
+ transform: (response: any): Address => {
145
+ // Transforms the response from "MyCustomAPI" to the "Address" format
146
+ return {
147
+ cep: response.postal_code,
148
+ state: response.data.state_short,
149
+ city: response.data.city_name,
150
+ neighborhood: response.data.neighborhood,
151
+ street: response.data.street_name,
152
+ service: "MyCustomAPI",
153
+ };
154
+ },
155
+ };
156
+ ```
157
+
158
+ ## Running Tests
159
+
160
+ ```bash
161
+ npm test
162
+ ```
163
+
164
+ ## License
165
+
166
+ Distributed under the MIT License.
@@ -0,0 +1,42 @@
1
+ import { Address, Fetcher, Provider, CepLookupOptions } from "./types";
2
+ export { Address, Fetcher, Provider, CepLookupOptions };
3
+ /**
4
+ * @class CepLookup
5
+ * @description A class for looking up Brazilian postal codes (CEPs) using multiple providers.
6
+ * It queries multiple services simultaneously and returns the response from the fastest one.
7
+ */
8
+ export declare class CepLookup {
9
+ private providers;
10
+ private fetcher;
11
+ /**
12
+ * @constructor
13
+ * @param {CepLookupOptions} options - The options for initializing the CepLookup instance.
14
+ */
15
+ constructor(options: CepLookupOptions);
16
+ /**
17
+ * @method lookup
18
+ * @description Looks up an address for a given CEP.
19
+ * @template T - The expected return type, defaults to `Address`.
20
+ * @param {string} cep - The CEP to be queried.
21
+ * @param {(address: Address) => T} [mapper] - An optional function to transform the `Address` object into a custom format `T`.
22
+ * @returns {Promise<T>} A Promise that resolves to the address in the default `Address` format or a custom format `T` if a mapper is provided.
23
+ * @throws {Error} If the CEP is invalid or if all providers fail to find the CEP.
24
+ */
25
+ lookup<T = Address>(cep: string, mapper?: (address: Address) => T): Promise<T>;
26
+ }
27
+ /**
28
+ * @function lookupCep
29
+ * @description Backward-compatible function for looking up a CEP. Internally creates a `CepLookup` instance.
30
+ * @template T - The expected return type, defaults to `Address`.
31
+ * @param {object} options - Options for the lookup.
32
+ * @param {string} options.cep - The CEP to be queried.
33
+ * @param {Provider[]} options.providers - An array of `Provider` instances.
34
+ * @param {Fetcher} [options.fetcher] - The `Fetcher` function. Defaults to global `fetch` if not provided.
35
+ * @param {(address: Address) => T} [options.mapper] - An optional function to transform the `Address` object.
36
+ * @returns {Promise<T>} A Promise that resolves to the address.
37
+ * @throws {Error} If the CEP is invalid or if all providers fail.
38
+ */
39
+ export declare function lookupCep<T = Address>(options: CepLookupOptions & {
40
+ cep: string;
41
+ mapper?: (address: Address) => T;
42
+ }): Promise<T>;
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CepLookup = void 0;
4
+ exports.lookupCep = lookupCep;
5
+ /**
6
+ * @function validateCep
7
+ * @description Validates and cleans a CEP string. Removes non-digit characters and checks for an 8-digit length.
8
+ * @param {string} cep - The CEP string to validate.
9
+ * @returns {string} The cleaned, 8-digit CEP string.
10
+ * @throws {Error} If the CEP is invalid (not 8 digits after cleaning).
11
+ */
12
+ function validateCep(cep) {
13
+ const cleanedCep = cep.replace(/\D/g, "");
14
+ if (cleanedCep.length !== 8) {
15
+ throw new Error("Invalid CEP. It must have 8 digits.");
16
+ }
17
+ return cleanedCep;
18
+ }
19
+ /**
20
+ * @class CepLookup
21
+ * @description A class for looking up Brazilian postal codes (CEPs) using multiple providers.
22
+ * It queries multiple services simultaneously and returns the response from the fastest one.
23
+ */
24
+ class CepLookup {
25
+ /**
26
+ * @constructor
27
+ * @param {CepLookupOptions} options - The options for initializing the CepLookup instance.
28
+ */
29
+ constructor(options) {
30
+ this.providers = options.providers;
31
+ this.fetcher = options.fetcher || (async (url) => {
32
+ const response = await fetch(url);
33
+ if (!response.ok) {
34
+ throw new Error(`HTTP error! status: ${response.status}`);
35
+ }
36
+ return response.json();
37
+ });
38
+ }
39
+ /**
40
+ * @method lookup
41
+ * @description Looks up an address for a given CEP.
42
+ * @template T - The expected return type, defaults to `Address`.
43
+ * @param {string} cep - The CEP to be queried.
44
+ * @param {(address: Address) => T} [mapper] - An optional function to transform the `Address` object into a custom format `T`.
45
+ * @returns {Promise<T>} A Promise that resolves to the address in the default `Address` format or a custom format `T` if a mapper is provided.
46
+ * @throws {Error} If the CEP is invalid or if all providers fail to find the CEP.
47
+ */
48
+ async lookup(cep, mapper) {
49
+ const cleanedCep = validateCep(cep);
50
+ const promises = this.providers.map((provider) => {
51
+ const url = provider.buildUrl(cleanedCep);
52
+ return this.fetcher(url)
53
+ .then((response) => provider.transform(response))
54
+ .then((address) => (mapper ? mapper(address) : address));
55
+ });
56
+ return Promise.any(promises);
57
+ }
58
+ }
59
+ exports.CepLookup = CepLookup;
60
+ /**
61
+ * @function lookupCep
62
+ * @description Backward-compatible function for looking up a CEP. Internally creates a `CepLookup` instance.
63
+ * @template T - The expected return type, defaults to `Address`.
64
+ * @param {object} options - Options for the lookup.
65
+ * @param {string} options.cep - The CEP to be queried.
66
+ * @param {Provider[]} options.providers - An array of `Provider` instances.
67
+ * @param {Fetcher} [options.fetcher] - The `Fetcher` function. Defaults to global `fetch` if not provided.
68
+ * @param {(address: Address) => T} [options.mapper] - An optional function to transform the `Address` object.
69
+ * @returns {Promise<T>} A Promise that resolves to the address.
70
+ * @throws {Error} If the CEP is invalid or if all providers fail.
71
+ */
72
+ function lookupCep(options) {
73
+ const { cep, providers, fetcher, mapper } = options;
74
+ const cepLookup = new CepLookup({ providers, fetcher });
75
+ return cepLookup.lookup(cep, mapper);
76
+ }
@@ -0,0 +1,9 @@
1
+ import { Provider } from "../types";
2
+ /**
3
+ * @const {Provider} apicepProvider
4
+ * @description Provider for the ApiCEP service.
5
+ * @property {string} name - "ApiCEP".
6
+ * @property {(cep: string) => string} buildUrl - Constructs the URL for ApiCEP.
7
+ * @property {(response: any) => Address} transform - Transforms ApiCEP's response into a standardized `Address` object.
8
+ */
9
+ export declare const apicepProvider: Provider;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apicepProvider = void 0;
4
+ /**
5
+ * @const {Provider} apicepProvider
6
+ * @description Provider for the ApiCEP service.
7
+ * @property {string} name - "ApiCEP".
8
+ * @property {(cep: string) => string} buildUrl - Constructs the URL for ApiCEP.
9
+ * @property {(response: any) => Address} transform - Transforms ApiCEP's response into a standardized `Address` object.
10
+ */
11
+ exports.apicepProvider = {
12
+ name: "ApiCEP",
13
+ buildUrl: (cep) => `https://cdn.apicep.com/file/apicep/${cep}.json`,
14
+ transform: (response) => {
15
+ return {
16
+ cep: response.code,
17
+ state: response.state,
18
+ city: response.city,
19
+ neighborhood: response.district,
20
+ street: response.address,
21
+ service: "ApiCEP",
22
+ };
23
+ },
24
+ };
@@ -0,0 +1,9 @@
1
+ import { Provider } from "../types";
2
+ /**
3
+ * @const {Provider} brasilApiProvider
4
+ * @description Provider for the BrasilAPI service.
5
+ * @property {string} name - "BrasilAPI".
6
+ * @property {(cep: string) => string} buildUrl - Constructs the URL for BrasilAPI.
7
+ * @property {(response: any) => Address} transform - Transforms BrasilAPI's response into a standardized `Address` object.
8
+ */
9
+ export declare const brasilApiProvider: Provider;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.brasilApiProvider = void 0;
4
+ /**
5
+ * @const {Provider} brasilApiProvider
6
+ * @description Provider for the BrasilAPI service.
7
+ * @property {string} name - "BrasilAPI".
8
+ * @property {(cep: string) => string} buildUrl - Constructs the URL for BrasilAPI.
9
+ * @property {(response: any) => Address} transform - Transforms BrasilAPI's response into a standardized `Address` object.
10
+ */
11
+ exports.brasilApiProvider = {
12
+ name: "BrasilAPI",
13
+ buildUrl: (cep) => `https://brasilapi.com.br/api/cep/v1/${cep}`,
14
+ transform: (response) => {
15
+ return {
16
+ cep: response.cep,
17
+ state: response.state,
18
+ city: response.city,
19
+ neighborhood: response.neighborhood,
20
+ street: response.street,
21
+ service: "BrasilAPI",
22
+ };
23
+ },
24
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./viacep";
2
+ export * from "./brasil-api";
3
+ export * from "./apicep";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./viacep"), exports);
18
+ __exportStar(require("./brasil-api"), exports);
19
+ __exportStar(require("./apicep"), exports);
@@ -0,0 +1,10 @@
1
+ import { Provider } from "../types";
2
+ /**
3
+ * @const {Provider} viaCepProvider
4
+ * @description Provider for the ViaCEP service.
5
+ * @property {string} name - "ViaCEP".
6
+ * @property {(cep: string) => string} buildUrl - Constructs the URL for ViaCEP API.
7
+ * @property {(response: any) => Address} transform - Transforms ViaCEP's response into a standardized `Address` object.
8
+ * @throws {Error} If ViaCEP response indicates an error (e.g., CEP not found).
9
+ */
10
+ export declare const viaCepProvider: Provider;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.viaCepProvider = void 0;
4
+ /**
5
+ * @const {Provider} viaCepProvider
6
+ * @description Provider for the ViaCEP service.
7
+ * @property {string} name - "ViaCEP".
8
+ * @property {(cep: string) => string} buildUrl - Constructs the URL for ViaCEP API.
9
+ * @property {(response: any) => Address} transform - Transforms ViaCEP's response into a standardized `Address` object.
10
+ * @throws {Error} If ViaCEP response indicates an error (e.g., CEP not found).
11
+ */
12
+ exports.viaCepProvider = {
13
+ name: "ViaCEP",
14
+ buildUrl: (cep) => `https://viacep.com.br/ws/${cep}/json/`,
15
+ transform: (response) => {
16
+ if (response.erro) {
17
+ throw new Error("CEP not found");
18
+ }
19
+ return {
20
+ cep: response.cep,
21
+ state: response.uf,
22
+ city: response.localidade,
23
+ neighborhood: response.bairro,
24
+ street: response.logradouro,
25
+ service: "ViaCEP",
26
+ };
27
+ },
28
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @interface Address
3
+ * @description Represents a standardized address object returned by the CEP lookup.
4
+ * @property {string} cep - The postal code.
5
+ * @property {string} state - The state abbreviation (e.g., 'SP', 'RJ').
6
+ * @property {string} city - The city name.
7
+ * @property {string} neighborhood - The neighborhood name.
8
+ * @property {string} street - The street name.
9
+ * @property {string} service - The name of the service that provided the address (e.g., 'ViaCEP', 'BrasilAPI').
10
+ */
11
+ export interface Address {
12
+ cep: string;
13
+ state: string;
14
+ city: string;
15
+ neighborhood: string;
16
+ street: string;
17
+ service: string;
18
+ }
19
+ /**
20
+ * @interface Provider
21
+ * @description Defines the contract for a CEP lookup provider.
22
+ * @property {string} name - The name of the provider.
23
+ * @property {(cep: string) => string} buildUrl - A function that constructs the API URL for a given CEP.
24
+ * @property {(response: any) => Address} transform - A function that transforms the raw API response into a standardized `Address` object.
25
+ */
26
+ export interface Provider {
27
+ name: string;
28
+ buildUrl: (cep: string) => string;
29
+ transform: (response: any) => Address;
30
+ }
31
+ /**
32
+ * @typedef {function(url: string): Promise<any>}
33
+ * @description A function that fetches data from a given URL and returns a Promise resolving to the response data.
34
+ */
35
+ export type Fetcher = (url: string) => Promise<any>;
36
+ /**
37
+ * @interface CepLookupOptions
38
+ * @description Options for initializing the `CepLookup` class.
39
+ * @property {Provider[]} providers - An array of `Provider` instances to be used for CEP lookup.
40
+ * @property {Fetcher} [fetcher] - The `Fetcher` function to be used for making HTTP requests. Defaults to global `fetch` if not provided.
41
+ */
42
+ export interface CepLookupOptions {
43
+ providers: Provider[];
44
+ fetcher?: Fetcher;
45
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@eusilvio/cep-lookup",
3
+ "version": "1.0.0",
4
+ "description": "A modern, flexible, and agnostic CEP lookup library written in TypeScript.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "exports": {
14
+ ".": "./dist/index.js",
15
+ "./providers": "./dist/providers/index.js"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "test": "jest",
20
+ "example": "ts-node -r tsconfig-paths/register examples/example.ts",
21
+ "custom-example": "ts-node -r tsconfig-paths/register examples/custom-provider-example.ts",
22
+ "node-example": "ts-node -r tsconfig-paths/register examples/node-example.ts"
23
+ },
24
+ "keywords": [
25
+ "cep",
26
+ "lookup",
27
+ "typescript",
28
+ "address",
29
+ "correios"
30
+ ],
31
+ "author": "",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/eusilvio/cep-lookup.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/eusilvio/cep-lookup/issues"
39
+ },
40
+ "homepage": "https://github.com/eusilvio/cep-lookup#readme",
41
+ "devDependencies": {
42
+ "@types/jest": "^30.0.0",
43
+ "jest": "^30.1.3",
44
+ "ts-jest": "^29.4.4",
45
+ "ts-node": "^10.9.2",
46
+ "tsconfig-paths": "^4.2.0",
47
+ "typescript": "^5.9.2"
48
+ }
49
+ }