entexto-cli 2.0.0 → 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.
@@ -195,7 +195,7 @@ entexto api query <slug>
195
195
 
196
196
  ```javascript
197
197
  // Autenticación con API Key
198
- const resp = await fetch('https://api.entexto.com/v1/api/mi-api/collections/usuarios/documents', {
198
+ const resp = await fetch('https://api.entexto.com/v1/api/projects/mi-api/data/usuarios', {
199
199
  method: 'POST',
200
200
  headers: {
201
201
  'Content-Type': 'application/json',
@@ -214,7 +214,7 @@ const login = await fetch('https://api.entexto.com/v1/api/auth/login', {
214
214
  });
215
215
  const { token } = await login.json();
216
216
 
217
- const docs = await fetch('https://api.entexto.com/v1/api/mi-api/collections/usuarios/documents', {
217
+ const docs = await fetch('https://api.entexto.com/v1/api/projects/mi-api/data/usuarios', {
218
218
  headers: { 'Authorization': `Bearer ${token}` }
219
219
  });
220
220
  ```
@@ -476,17 +476,17 @@ entexto api insert tienda-api
476
476
 
477
477
  Usar desde el frontend:
478
478
  ```javascript
479
- const API_BASE = 'https://api.entexto.com/v1/api/tienda-api';
479
+ const API_BASE = 'https://api.entexto.com/v1/api/projects/tienda-api';
480
480
  const API_KEY = 'tu-api-key';
481
481
 
482
482
  // Listar productos
483
- const res = await fetch(`${API_BASE}/collections/productos/documents`, {
483
+ const res = await fetch(`${API_BASE}/data/productos`, {
484
484
  headers: { 'X-API-Key': API_KEY }
485
485
  });
486
486
  const { documents } = await res.json();
487
487
 
488
488
  // Crear pedido
489
- await fetch(`${API_BASE}/collections/pedidos/documents`, {
489
+ await fetch(`${API_BASE}/data/pedidos`, {
490
490
  method: 'POST',
491
491
  headers: {
492
492
  'Content-Type': 'application/json',
@@ -126,7 +126,7 @@ async function ejecutarDeploy(uuid, dir, publish, full) {
126
126
  : await deployFiles(uuid, form, false);
127
127
 
128
128
  spinner.stop();
129
- (result.archivos || []).forEach(r => console.log(' ' + chalk.green('') + ' ' + chalk.white(r)));
129
+ (result.archivos || []).forEach(r => console.log(' ' + chalk.green('') + ' ' + chalk.white(typeof r === 'string' ? r : r.ruta || r)));
130
130
  console.log(
131
131
  chalk.bold.green(`\n Deploy ${result.total || aSubir.length} subido(s)`)
132
132
  + (omitidos.length ? chalk.gray(`, ${omitidos.length} sin cambios`) : '') + '\n'
@@ -202,4 +202,4 @@ module.exports = async function (options) {
202
202
  watcher.on('error', err => console.error(chalk.red(' [watch] Error: ' + err.message)));
203
203
  };
204
204
 
205
- module.exports.subirArchivoSolo = subirArchivoSolo;
205
+ module.exports.subirArchivoSolo = subirArchivoSolo;
@@ -102,7 +102,7 @@ module.exports = async function tunnel(opts) {
102
102
 
103
103
  // Procesar requests del server
104
104
  socket.on('tunnel-request', (payload, cb) => {
105
- const { method, path: reqPath, headers, body, id } = payload;
105
+ const { method, path: reqPath, headers, body, id, streamId } = payload;
106
106
 
107
107
  // Construir URL completa al target local
108
108
  const localUrl = new URL(reqPath, targetUrl);
@@ -126,55 +126,114 @@ module.exports = async function tunnel(opts) {
126
126
  const timestamp = new Date().toLocaleTimeString();
127
127
  process.stdout.write(chalk.gray(` [${timestamp}] `) + chalk.yellow(method.padEnd(6)) + chalk.white(reqPath) + ' ');
128
128
 
129
+ // Detectar si el server soporta streaming (envía streamId)
130
+ const useStreaming = !!streamId;
131
+
129
132
  const localReq = proto.request(reqOpts, (localRes) => {
130
- const chunks = [];
131
- localRes.on('data', (chunk) => chunks.push(chunk));
132
- localRes.on('end', () => {
133
- let bodyBuf = Buffer.concat(chunks);
134
-
135
- // Reescribir el host local → URL pública del túnel en respuestas de texto.
136
- // Esto cubre el caso de Elementor/webpack cuyo __webpack_public_path__
137
- // queda hardcodeado como cmy-rent-car.local (o el dominio local) en el JS.
138
- const ct = (localRes.headers['content-type'] || '').toLowerCase();
139
- const isText = /\b(javascript|css|html|json|xml|svg|text\/)/i.test(ct);
140
- if (isText && publicUrl && bodyBuf.length > 0) {
141
- const localOrigins = [
142
- parsedTarget.origin, // http://cmy-rent-car.local
143
- parsedTarget.origin.replace(/^http:/, 'https:'), // https://cmy-rent-car.local
144
- parsedTarget.host, // cmy-rent-car.local
145
- ];
146
- let text = bodyBuf.toString('utf8');
147
- for (const origin of localOrigins) {
148
- // Reemplazar como origen completo primero, luego como host suelto
149
- text = text.split(origin).join(publicUrl);
150
- }
151
- bodyBuf = Buffer.from(text, 'utf8');
152
- // Corregir Content-Length si venía fijo
153
- localRes.headers['content-length'] = String(bodyBuf.length);
154
- }
133
+ const ct = (localRes.headers['content-type'] || '').toLowerCase();
134
+ const isText = /\b(javascript|css|html|json|xml|svg|text\/)/i.test(ct);
135
+ const status = localRes.statusCode;
155
136
 
156
- // Color del status
157
- const status = localRes.statusCode;
137
+ if (useStreaming) {
138
+ // ── Modo streaming: enviar head + chunks + end ──
158
139
  const statusColor = status < 300 ? chalk.green(status) : status < 400 ? chalk.cyan(status) : chalk.red(status);
159
- console.log(statusColor + chalk.gray(` (${bodyBuf.length}B)`));
140
+ process.stdout.write(statusColor + ' ');
141
+
142
+ // Para responses de texto pequeños, acumular para poder reescribir URLs
143
+ // Para binarios o responses grandes, hacer streaming directo
144
+ const isSmallText = isText && (!localRes.headers['content-length'] || parseInt(localRes.headers['content-length']) < 5 * 1024 * 1024);
145
+
146
+ if (isSmallText) {
147
+ // Acumular texto para URL rewriting, luego enviar en un solo stream
148
+ const chunks = [];
149
+ localRes.on('data', (chunk) => chunks.push(chunk));
150
+ localRes.on('end', () => {
151
+ let bodyBuf = Buffer.concat(chunks);
152
+ if (publicUrl && bodyBuf.length > 0) {
153
+ const localOrigins = [
154
+ parsedTarget.origin,
155
+ parsedTarget.origin.replace(/^http:/, 'https:'),
156
+ parsedTarget.host,
157
+ ];
158
+ let text = bodyBuf.toString('utf8');
159
+ for (const origin of localOrigins) {
160
+ text = text.split(origin).join(publicUrl);
161
+ }
162
+ bodyBuf = Buffer.from(text, 'utf8');
163
+ }
164
+ const h = { ...localRes.headers };
165
+ h['content-length'] = String(bodyBuf.length);
166
+ socket.emit('tunnel-stream-head', { streamId, status, headers: h });
167
+ socket.emit('tunnel-stream-chunk', { streamId, data: bodyBuf.toString('base64') });
168
+ socket.emit('tunnel-stream-end', { streamId });
169
+ console.log(chalk.gray(`(${bodyBuf.length}B)`));
170
+ });
171
+ } else {
172
+ // Streaming real: enviar chunks conforme llegan
173
+ const h = { ...localRes.headers };
174
+ delete h['content-length']; // no sabemos el tamaño final
175
+ socket.emit('tunnel-stream-head', { streamId, status, headers: h });
176
+
177
+ let totalBytes = 0;
178
+ localRes.on('data', (chunk) => {
179
+ totalBytes += chunk.length;
180
+ socket.emit('tunnel-stream-chunk', { streamId, data: chunk.toString('base64') });
181
+ });
182
+ localRes.on('end', () => {
183
+ socket.emit('tunnel-stream-end', { streamId });
184
+ console.log(chalk.gray(`(${totalBytes}B streamed)`));
185
+ });
186
+ }
187
+ } else {
188
+ // ── Modo clásico: acumular todo y callback ──
189
+ const chunks = [];
190
+ localRes.on('data', (chunk) => chunks.push(chunk));
191
+ localRes.on('end', () => {
192
+ let bodyBuf = Buffer.concat(chunks);
193
+
194
+ if (isText && publicUrl && bodyBuf.length > 0) {
195
+ const localOrigins = [
196
+ parsedTarget.origin,
197
+ parsedTarget.origin.replace(/^http:/, 'https:'),
198
+ parsedTarget.host,
199
+ ];
200
+ let text = bodyBuf.toString('utf8');
201
+ for (const origin of localOrigins) {
202
+ text = text.split(origin).join(publicUrl);
203
+ }
204
+ bodyBuf = Buffer.from(text, 'utf8');
205
+ localRes.headers['content-length'] = String(bodyBuf.length);
206
+ }
160
207
 
161
- cb({
162
- status: status,
163
- headers: localRes.headers,
164
- body: bodyBuf.toString('base64'),
208
+ const statusColor = status < 300 ? chalk.green(status) : status < 400 ? chalk.cyan(status) : chalk.red(status);
209
+ console.log(statusColor + chalk.gray(` (${bodyBuf.length}B)`));
210
+
211
+ cb({
212
+ status: status,
213
+ headers: localRes.headers,
214
+ body: bodyBuf.toString('base64'),
215
+ });
165
216
  });
166
- });
217
+ }
167
218
  });
168
219
 
169
220
  localReq.on('error', (err) => {
170
221
  console.log(chalk.red('ERR ') + chalk.gray(err.message));
171
- cb({ error: err.message, status: 502 });
222
+ if (useStreaming) {
223
+ socket.emit('tunnel-stream-error', { streamId, error: err.message, status: 502 });
224
+ } else {
225
+ cb({ error: err.message, status: 502 });
226
+ }
172
227
  });
173
228
 
174
- localReq.setTimeout(25000, () => {
229
+ localReq.setTimeout(useStreaming ? 120000 : 25000, () => {
175
230
  localReq.destroy();
176
231
  console.log(chalk.red('TIMEOUT'));
177
- cb({ error: 'Local server timeout', status: 504 });
232
+ if (useStreaming) {
233
+ socket.emit('tunnel-stream-error', { streamId, error: 'Local server timeout', status: 504 });
234
+ } else {
235
+ cb({ error: 'Local server timeout', status: 504 });
236
+ }
178
237
  });
179
238
 
180
239
  if (body) {
package/lib/utils/api.js CHANGED
@@ -104,16 +104,16 @@ async function deleteFileRemote(uuid, ruta) {
104
104
  // ─── Projects CRUD ─────────────────────────────────────────
105
105
  async function createProject(data) {
106
106
  const res = await client().post('/api/projects', data);
107
- return res.data;
107
+ return res.data.project || res.data;
108
108
  }
109
109
 
110
110
  // ─── Dominios ──────────────────────────────────────────────
111
111
  async function listDomains() {
112
112
  const res = await client().get('/api/domains/list');
113
- return res.data;
113
+ return res.data.domains || res.data || [];
114
114
  }
115
115
 
116
- async function addDomain(domain, projectId) {
116
+ async function addDomain(projectId, domain) {
117
117
  const res = await client().post('/api/domains/add', { domain, project_id: projectId });
118
118
  return res.data;
119
119
  }
@@ -158,7 +158,7 @@ async function apiCreateCollection(slug, name) {
158
158
 
159
159
  async function apiInsertDoc(slug, collection, data) {
160
160
  const res = await client().post(
161
- `/v1/api/projects/${encodeURIComponent(slug)}/collections/${encodeURIComponent(collection)}/documents`,
161
+ `/v1/api/projects/${encodeURIComponent(slug)}/data/${encodeURIComponent(collection)}`,
162
162
  { data }
163
163
  );
164
164
  return res.data;
@@ -166,7 +166,7 @@ async function apiInsertDoc(slug, collection, data) {
166
166
 
167
167
  async function apiQueryDocs(slug, collection) {
168
168
  const res = await client().get(
169
- `/v1/api/projects/${encodeURIComponent(slug)}/collections/${encodeURIComponent(collection)}/documents`
169
+ `/v1/api/projects/${encodeURIComponent(slug)}/data/${encodeURIComponent(collection)}`
170
170
  );
171
171
  return res.data;
172
172
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "entexto-cli",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "CLI oficial de Entexto — Crea, deploya y gestiona proyectos, dominios, APIs y Live SDK desde tu terminal",
5
5
  "main": "lib/index.js",
6
6
  "bin": {