latinfo 0.1.0 → 0.3.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/dist/index.js CHANGED
@@ -5,8 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const fs_1 = __importDefault(require("fs"));
8
+ const http_1 = __importDefault(require("http"));
8
9
  const path_1 = __importDefault(require("path"));
9
10
  const os_1 = __importDefault(require("os"));
11
+ const child_process_1 = require("child_process");
10
12
  const API_URL = process.env.LATINFO_API_URL || 'https://api.latinfo.dev';
11
13
  const GITHUB_CLIENT_ID = 'Ov23li5fcQaiCsVtaMKK';
12
14
  const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), '.latinfo');
@@ -29,75 +31,68 @@ function deleteConfig() {
29
31
  }
30
32
  catch { }
31
33
  }
32
- // --- GitHub Device Flow ---
33
- async function login() {
34
- console.log('Iniciando login con GitHub...\n');
35
- // 1. Request device code
36
- const codeRes = await fetch('https://github.com/login/device/code', {
37
- method: 'POST',
38
- headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
39
- body: JSON.stringify({ client_id: GITHUB_CLIENT_ID, scope: 'read:user,user:email' }),
40
- });
41
- const codeData = await codeRes.json();
42
- console.log(` Abre: ${codeData.verification_uri}`);
43
- console.log(` Código: ${codeData.user_code}\n`);
44
- console.log('Esperando autorización...');
45
- // 2. Poll for access token
46
- const interval = (codeData.interval || 5) * 1000;
47
- const deadline = Date.now() + codeData.expires_in * 1000;
48
- let accessToken = null;
49
- while (Date.now() < deadline) {
50
- await new Promise(r => setTimeout(r, interval));
51
- const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
52
- method: 'POST',
53
- headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
54
- body: JSON.stringify({
55
- client_id: GITHUB_CLIENT_ID,
56
- device_code: codeData.device_code,
57
- grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
58
- }),
34
+ // --- GitHub Authorization Code Flow ---
35
+ function openBrowser(url) {
36
+ const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
37
+ (0, child_process_1.exec)(`${cmd} "${url}"`);
38
+ }
39
+ async function waitForCallback(port) {
40
+ return new Promise((resolve, reject) => {
41
+ const server = http_1.default.createServer((req, res) => {
42
+ const url = new URL(req.url || '', `http://localhost:${port}`);
43
+ const code = url.searchParams.get('code');
44
+ if (code) {
45
+ res.writeHead(200, { 'Content-Type': 'text/html' });
46
+ res.end('<h2>Done. You can close this window.</h2>');
47
+ server.close();
48
+ resolve(code);
49
+ }
50
+ else {
51
+ res.writeHead(400);
52
+ res.end('Missing code');
53
+ server.close();
54
+ reject(new Error('No code received from GitHub'));
55
+ }
59
56
  });
60
- const tokenData = await tokenRes.json();
61
- if (tokenData.access_token) {
62
- accessToken = tokenData.access_token;
63
- break;
64
- }
65
- if (tokenData.error === 'expired_token') {
66
- console.error('Código expirado. Intenta de nuevo.');
67
- process.exit(1);
68
- }
69
- // authorization_pending or slow_down → keep polling
70
- }
71
- if (!accessToken) {
72
- console.error('Timeout. Intenta de nuevo.');
73
- process.exit(1);
74
- }
75
- // 3. Exchange for Latinfo API key
57
+ server.listen(port, () => { });
58
+ setTimeout(() => { server.close(); reject(new Error('Timeout waiting for authorization')); }, 120_000);
59
+ });
60
+ }
61
+ async function login() {
62
+ const port = 8400;
63
+ const redirectUri = `http://localhost:${port}/callback`;
64
+ const scope = 'read:user,user:email';
65
+ const authUrl = `https://github.com/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}`;
66
+ console.log('Opening GitHub...');
67
+ openBrowser(authUrl);
68
+ // 1. Wait for GitHub to redirect back with code
69
+ const code = await waitForCallback(port);
70
+ // 2. Exchange code for access token (server-side, needs client_secret)
76
71
  const authRes = await fetch(`${API_URL}/auth/github`, {
77
72
  method: 'POST',
78
73
  headers: { 'Content-Type': 'application/json' },
79
- body: JSON.stringify({ access_token: accessToken }),
74
+ body: JSON.stringify({ code, redirect_uri: redirectUri }),
80
75
  });
81
76
  if (!authRes.ok) {
82
- console.error('Error obteniendo API key:', await authRes.text());
77
+ console.error('Error getting API key:', await authRes.text());
83
78
  process.exit(1);
84
79
  }
85
80
  const authData = await authRes.json();
86
81
  saveConfig({ api_key: authData.api_key, github_username: authData.github_username });
87
- console.log(`\nLogueado como ${authData.github_username}`);
82
+ console.log(`Logged in as ${authData.github_username}`);
88
83
  }
89
84
  // --- Commands ---
90
85
  async function ruc(rucNumber) {
91
86
  const config = loadConfig();
92
87
  if (!config) {
93
- console.error('No logueado. Ejecuta: latinfo login');
88
+ console.error('Not logged in. Run: latinfo login');
94
89
  process.exit(1);
95
90
  }
96
91
  if (!/^\d{11}$/.test(rucNumber)) {
97
- console.error('RUC inválido. Debe tener 11 dígitos.');
92
+ console.error('Invalid RUC. Must be 11 digits.');
98
93
  process.exit(1);
99
94
  }
100
- const res = await fetch(`${API_URL}/ruc/${rucNumber}`, {
95
+ const res = await fetch(`${API_URL}/pe/ruc/${rucNumber}`, {
101
96
  headers: { Authorization: `Bearer ${config.api_key}` },
102
97
  });
103
98
  if (!res.ok) {
@@ -116,13 +111,43 @@ async function ruc(rucNumber) {
116
111
  Zona: ${[data.codigoZona, data.tipoZona].filter(v => v && v !== '-').join(' ')}
117
112
  `.trim());
118
113
  }
114
+ async function dni(dniNumber) {
115
+ const config = loadConfig();
116
+ if (!config) {
117
+ console.error('Not logged in. Run: latinfo login');
118
+ process.exit(1);
119
+ }
120
+ if (!/^\d{8}$/.test(dniNumber)) {
121
+ console.error('Invalid DNI. Must be 8 digits.');
122
+ process.exit(1);
123
+ }
124
+ const res = await fetch(`${API_URL}/pe/dni/${dniNumber}`, {
125
+ headers: { Authorization: `Bearer ${config.api_key}` },
126
+ });
127
+ if (!res.ok) {
128
+ const err = await res.json();
129
+ console.error(err.message || err.error);
130
+ process.exit(1);
131
+ }
132
+ const data = await res.json();
133
+ console.log(`
134
+ DNI: ${data.dni}
135
+ RUC: ${data.ruc}
136
+ Razón Social: ${data.razonSocial}
137
+ Estado: ${data.estado}
138
+ Condición: ${data.condicion}
139
+ Ubigeo: ${data.ubigeo}
140
+ Dirección: ${[data.tipoVia, data.nombreVia, data.numero].filter(v => v && v !== '-').join(' ')}
141
+ Zona: ${[data.codigoZona, data.tipoZona].filter(v => v && v !== '-').join(' ')}
142
+ `.trim());
143
+ }
119
144
  async function search(query) {
120
145
  const config = loadConfig();
121
146
  if (!config) {
122
- console.error('No logueado. Ejecuta: latinfo login');
147
+ console.error('Not logged in. Run: latinfo login');
123
148
  process.exit(1);
124
149
  }
125
- const res = await fetch(`${API_URL}/search?q=${encodeURIComponent(query)}`, {
150
+ const res = await fetch(`${API_URL}/pe/search?q=${encodeURIComponent(query)}`, {
126
151
  headers: { Authorization: `Bearer ${config.api_key}` },
127
152
  });
128
153
  if (!res.ok) {
@@ -132,37 +157,38 @@ async function search(query) {
132
157
  }
133
158
  const data = await res.json();
134
159
  if (data.count === 0) {
135
- console.log('Sin resultados.');
160
+ console.log('No results found.');
136
161
  return;
137
162
  }
138
163
  for (const r of data.results) {
139
164
  console.log(` ${r.ruc} ${r.razonSocial} [${r.estado}]`);
140
165
  }
141
- console.log(`\n${data.count} resultado(s)`);
166
+ console.log(`\n${data.count} result(s)`);
142
167
  }
143
168
  function whoami() {
144
169
  const config = loadConfig();
145
170
  if (!config) {
146
- console.error('No logueado. Ejecuta: latinfo login');
171
+ console.error('Not logged in. Run: latinfo login');
147
172
  process.exit(1);
148
173
  }
149
174
  console.log(config.github_username);
150
175
  }
151
176
  function logout() {
152
177
  deleteConfig();
153
- console.log('Sesión cerrada.');
178
+ console.log('Logged out.');
154
179
  }
155
180
  function help() {
156
181
  console.log(`
157
182
  latinfo - Public data API for Latin America
158
183
 
159
- Comandos:
160
- login Autenticarse con GitHub
161
- logout Cerrar sesión
162
- whoami Ver usuario actual
163
- ruc <ruc> Consultar un RUC (11 dígitos)
164
- search <texto> Buscar por razón social
165
- help Mostrar esta ayuda
184
+ Commands:
185
+ login Authenticate with GitHub
186
+ logout Sign out
187
+ whoami Show current user
188
+ ruc <ruc> Lookup by RUC (11 digits)
189
+ dni <dni> Lookup by DNI (8 digits)
190
+ search <query> Search by business name
191
+ help Show this help
166
192
  `.trim());
167
193
  }
168
194
  // --- Main ---
@@ -180,6 +206,9 @@ switch (command) {
180
206
  case 'ruc':
181
207
  ruc(args[0]).catch(e => { console.error(e); process.exit(1); });
182
208
  break;
209
+ case 'dni':
210
+ dni(args[0]).catch(e => { console.error(e); process.exit(1); });
211
+ break;
183
212
  case 'search':
184
213
  search(args.join(' ')).catch(e => { console.error(e); process.exit(1); });
185
214
  break;
package/dist/sdk.d.ts CHANGED
@@ -28,15 +28,25 @@ interface SearchResponse {
28
28
  count: number;
29
29
  results: SearchResult[];
30
30
  }
31
+ declare class Country {
32
+ private request;
33
+ private prefix;
34
+ constructor(request: <T>(path: string) => Promise<T>, prefix: string);
35
+ protected countryRequest<T>(path: string): Promise<T>;
36
+ }
37
+ declare class Peru extends Country {
38
+ constructor(request: <T>(path: string) => Promise<T>);
39
+ ruc(ruc: string): Promise<RucResult>;
40
+ dni(dni: string): Promise<DniResult>;
41
+ search(query: string): Promise<SearchResponse>;
42
+ }
31
43
  export declare class Latinfo {
32
44
  private apiKey;
33
45
  private baseUrl;
46
+ pe: Peru;
34
47
  constructor(apiKey: string, options?: {
35
48
  baseUrl?: string;
36
49
  });
37
50
  private request;
38
- ruc(ruc: string): Promise<RucResult>;
39
- dni(dni: string): Promise<DniResult>;
40
- search(query: string): Promise<SearchResponse>;
41
51
  }
42
52
  export {};
package/dist/sdk.js CHANGED
@@ -2,12 +2,39 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Latinfo = void 0;
4
4
  const DEFAULT_API_URL = 'https://api.latinfo.dev';
5
+ class Country {
6
+ request;
7
+ prefix;
8
+ constructor(request, prefix) {
9
+ this.request = request;
10
+ this.prefix = prefix;
11
+ }
12
+ countryRequest(path) {
13
+ return this.request(`/${this.prefix}${path}`);
14
+ }
15
+ }
16
+ class Peru extends Country {
17
+ constructor(request) {
18
+ super(request, 'pe');
19
+ }
20
+ async ruc(ruc) {
21
+ return this.countryRequest(`/ruc/${ruc}`);
22
+ }
23
+ async dni(dni) {
24
+ return this.countryRequest(`/dni/${dni}`);
25
+ }
26
+ async search(query) {
27
+ return this.countryRequest(`/search?q=${encodeURIComponent(query)}`);
28
+ }
29
+ }
5
30
  class Latinfo {
6
31
  apiKey;
7
32
  baseUrl;
33
+ pe;
8
34
  constructor(apiKey, options) {
9
35
  this.apiKey = apiKey;
10
36
  this.baseUrl = options?.baseUrl || DEFAULT_API_URL;
37
+ this.pe = new Peru(this.request.bind(this));
11
38
  }
12
39
  async request(path) {
13
40
  const res = await fetch(`${this.baseUrl}${path}`, {
@@ -19,14 +46,5 @@ class Latinfo {
19
46
  }
20
47
  return res.json();
21
48
  }
22
- async ruc(ruc) {
23
- return this.request(`/ruc/${ruc}`);
24
- }
25
- async dni(dni) {
26
- return this.request(`/dni/${dni}`);
27
- }
28
- async search(query) {
29
- return this.request(`/search?q=${encodeURIComponent(query)}`);
30
- }
31
49
  }
32
50
  exports.Latinfo = Latinfo;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latinfo",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Public data API for Latin America - SDK & CLI",
5
5
  "main": "dist/sdk.js",
6
6
  "types": "dist/sdk.d.ts",