komplian 0.5.0 → 0.5.3

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.
@@ -12,7 +12,7 @@ KOMPLIAN_DATABASE_URL_ADMIN_STAGING=
12
12
  KOMPLIAN_DATABASE_URL_ADMIN_PRODUCTION=
13
13
 
14
14
  # --- Web / pilot DB ---
15
- KOMPLIAN_DATABASE_URL_WEB_DEVELOPMENT=
15
+ KOMPLIAN_DATABASE_URL_WEB_DEVELOPMENT=postgresql://neondb_owner:npg_1crYb3PaCjlf@ep-frosty-bird-agql4oq5-pooler.c-2.eu-central-1.aws.neon.tech/neondb?sslmode=require&channel_binding=require
16
16
  KOMPLIAN_DATABASE_URL_WEB_STAGING=
17
17
  KOMPLIAN_DATABASE_URL_WEB_PRODUCTION=
18
18
 
@@ -22,5 +22,7 @@ KOMPLIAN_DATABASE_URL_WEB_PRODUCTION=
22
22
  # Operador para comprobar acceso a producción (alternativa: git config user.email).
23
23
  # KOMPLIAN_DATABASE_OPERATOR_EMAIL=josue.santana@komplian.com
24
24
 
25
- # Solo si NO usas Neon (p. ej. Postgres local en Docker):
25
+ # Staging/producción: host debe ser Neon (*.neon.tech), salvo:
26
26
  # KOMPLIAN_DATABASE_ALLOW_NON_NEON=1
27
+ # Development: Postgres local está permitido (mensaje informativo). Para exigir Neon también en dev:
28
+ # KOMPLIAN_DATABASE_REQUIRE_NEON_DEVELOPMENT=1
package/komplian-db.mjs CHANGED
@@ -16,7 +16,8 @@
16
16
  * Producción: solo emails @komplian.com en KOMPLIAN_DATABASE_PRODUCTION_ALLOWLIST
17
17
  * (por defecto josue.santana@komplian.com). Operador: KOMPLIAN_DATABASE_OPERATOR_EMAIL o git config user.email.
18
18
  *
19
- * Neon: host debe ser *.neon.tech salvo KOMPLIAN_DATABASE_ALLOW_NON_NEON=1
19
+ * Neon: staging/producción exigen *.neon.tech (salvo KOMPLIAN_DATABASE_ALLOW_NON_NEON=1).
20
+ * Development: permite host no Neon (Postgres local); aviso en consola. Forzar Neon en dev: KOMPLIAN_DATABASE_REQUIRE_NEON_DEVELOPMENT=1
20
21
  */
21
22
 
22
23
  import { spawn, spawnSync } from "node:child_process";
@@ -52,6 +53,32 @@ const ENVIRONMENTS = new Set(["development", "staging", "production"]);
52
53
  /** Lista por defecto si no configuras KOMPLIAN_DATABASE_PRODUCTION_ALLOWLIST */
53
54
  const DEFAULT_PRODUCTION_ALLOWLIST = ["josue.santana@komplian.com"];
54
55
 
56
+ /** Misma cadena que `komplian-localhost.mjs` cuando no hay URL real. */
57
+ const LOCALHOST_DB_PLACEHOLDER = "komplian_localhost_placeholder";
58
+
59
+ function isPlaceholderDbUrl(url) {
60
+ return !!(url && String(url).includes(LOCALHOST_DB_PLACEHOLDER));
61
+ }
62
+
63
+ /** Primera URL no vacía que no sea el placeholder de localhost. */
64
+ function pickFirstNonPlaceholder(...candidates) {
65
+ for (const c of candidates) {
66
+ const u = (c ?? "").trim();
67
+ if (u && !isPlaceholderDbUrl(u)) return u;
68
+ }
69
+ return "";
70
+ }
71
+
72
+ function assertNotPlaceholderDbUrl(url) {
73
+ if (!isPlaceholderDbUrl(url)) return;
74
+ log(
75
+ `${c.red}✗${c.reset} La URL es el ${c.bold}placeholder${c.reset} de \`komplian localhost\` (no apunta a una base de datos real).`
76
+ );
77
+ log(``);
78
+ log(` Pon una URL real (raíz del monorepo: ${c.dim}KOMPLIAN_LOCALHOST_*_DATABASE_URL${c.reset}, ${c.dim}KOMPLIAN_DATABASE_URLS.env${c.reset}, o ${c.dim}--url${c.reset}).`);
79
+ process.exit(1);
80
+ }
81
+
55
82
  function parseEnvFile(content) {
56
83
  const out = {};
57
84
  for (const line of content.split(/\r?\n/)) {
@@ -170,17 +197,31 @@ function loadProductionAllowlist(workspaceRoot) {
170
197
  return DEFAULT_PRODUCTION_ALLOWLIST.map((e) => e.toLowerCase());
171
198
  }
172
199
 
173
- function assertNeonHost(url) {
200
+ function assertNeonHost(url, environment) {
174
201
  if (process.env.KOMPLIAN_DATABASE_ALLOW_NON_NEON === "1") return;
175
202
  try {
176
203
  const u = new URL(url);
177
204
  const h = u.hostname.toLowerCase();
178
- if (!h.includes("neon.tech")) {
205
+ const isNeon = h.includes("neon.tech");
206
+ if (isNeon) return;
207
+
208
+ if (environment === "development") {
209
+ if (process.env.KOMPLIAN_DATABASE_REQUIRE_NEON_DEVELOPMENT === "1") {
210
+ log(
211
+ `${c.red}✗${c.reset} Development: host no Neon (${u.hostname}). Quita ${c.dim}KOMPLIAN_DATABASE_REQUIRE_NEON_DEVELOPMENT${c.reset} o usa URL *.neon.tech.`
212
+ );
213
+ process.exit(1);
214
+ }
179
215
  log(
180
- `${c.red}✗${c.reset} Solo hosts Neon (postgresql://*.neon.tech). Para excepciones: ${c.dim}KOMPLIAN_DATABASE_ALLOW_NON_NEON=1${c.reset}`
216
+ `${c.dim}○${c.reset} Development: conexión no Neon (${u.hostname}). Staging/producción siguen exigiendo *.neon.tech.`
181
217
  );
182
- process.exit(1);
218
+ return;
183
219
  }
220
+
221
+ log(
222
+ `${c.red}✗${c.reset} Solo hosts Neon (postgresql://*.neon.tech). Para excepciones: ${c.dim}KOMPLIAN_DATABASE_ALLOW_NON_NEON=1${c.reset}`
223
+ );
224
+ process.exit(1);
184
225
  } catch {
185
226
  log(`${c.red}✗${c.reset} URL de base de datos inválida.`);
186
227
  process.exit(1);
@@ -230,34 +271,40 @@ function resolveDatabaseUrl(workspaceRoot, platform, environment) {
230
271
  const envU = environment.toUpperCase();
231
272
  const primaryKey = `KOMPLIAN_DATABASE_URL_${dbKey}_${envU}`;
232
273
 
233
- let url =
234
- process.env[primaryKey]?.trim() || file[primaryKey]?.trim() || "";
274
+ let url = pickFirstNonPlaceholder(
275
+ process.env[primaryKey],
276
+ file[primaryKey]
277
+ );
235
278
 
236
279
  if (environment === "development" && !url) {
237
- const single =
238
- process.env.KOMPLIAN_LOCALHOST_DATABASE_URL?.trim() ||
239
- file.KOMPLIAN_LOCALHOST_DATABASE_URL?.trim();
280
+ const single = pickFirstNonPlaceholder(
281
+ process.env.KOMPLIAN_LOCALHOST_DATABASE_URL,
282
+ file.KOMPLIAN_LOCALHOST_DATABASE_URL
283
+ );
240
284
  if (dbKey === "APP") {
241
- url =
242
- process.env.KOMPLIAN_LOCALHOST_APP_DATABASE_URL?.trim() ||
243
- file.KOMPLIAN_LOCALHOST_APP_DATABASE_URL?.trim() ||
244
- single ||
245
- readEnvLocalKey(workspaceRoot, "app", ["DATABASE_URL"]) ||
246
- readEnvLocalKey(workspaceRoot, "api", ["APP_DATABASE_URL"]);
285
+ url = pickFirstNonPlaceholder(
286
+ process.env.KOMPLIAN_LOCALHOST_APP_DATABASE_URL,
287
+ file.KOMPLIAN_LOCALHOST_APP_DATABASE_URL,
288
+ single,
289
+ readEnvLocalKey(workspaceRoot, "app", ["DATABASE_URL"]),
290
+ readEnvLocalKey(workspaceRoot, "api", ["APP_DATABASE_URL"])
291
+ );
247
292
  } else if (dbKey === "ADMIN") {
248
- url =
249
- process.env.KOMPLIAN_LOCALHOST_ADMIN_DATABASE_URL?.trim() ||
250
- file.KOMPLIAN_LOCALHOST_ADMIN_DATABASE_URL?.trim() ||
251
- single ||
252
- readEnvLocalKey(workspaceRoot, "admin", ["DATABASE_URL"]) ||
253
- readEnvLocalKey(workspaceRoot, "api", ["ADMIN_DATABASE_URL"]);
293
+ url = pickFirstNonPlaceholder(
294
+ process.env.KOMPLIAN_LOCALHOST_ADMIN_DATABASE_URL,
295
+ file.KOMPLIAN_LOCALHOST_ADMIN_DATABASE_URL,
296
+ single,
297
+ readEnvLocalKey(workspaceRoot, "admin", ["DATABASE_URL"]),
298
+ readEnvLocalKey(workspaceRoot, "api", ["ADMIN_DATABASE_URL"])
299
+ );
254
300
  } else if (dbKey === "WEB") {
255
- url =
256
- process.env.KOMPLIAN_LOCALHOST_WEB_DATABASE_URL?.trim() ||
257
- file.KOMPLIAN_LOCALHOST_WEB_DATABASE_URL?.trim() ||
258
- single ||
259
- readEnvLocalKey(workspaceRoot, "web", ["DATABASE_URL"]) ||
260
- readEnvLocalKey(workspaceRoot, "api", ["WEB_DATABASE_URL"]);
301
+ url = pickFirstNonPlaceholder(
302
+ process.env.KOMPLIAN_LOCALHOST_WEB_DATABASE_URL,
303
+ file.KOMPLIAN_LOCALHOST_WEB_DATABASE_URL,
304
+ single,
305
+ readEnvLocalKey(workspaceRoot, "web", ["DATABASE_URL"]),
306
+ readEnvLocalKey(workspaceRoot, "api", ["WEB_DATABASE_URL"])
307
+ );
261
308
  }
262
309
  }
263
310
 
@@ -342,6 +389,7 @@ function usageDb() {
342
389
  log(` ${c.dim}KOMPLIAN_DATABASE_URL_APP_DEVELOPMENT${c.reset}`);
343
390
  log(` ${c.dim}KOMPLIAN_DATABASE_URL_APP_STAGING / _PRODUCTION${c.reset} (idem ADMIN, WEB)`);
344
391
  log(` Archivo opcional: ${c.dim}KOMPLIAN_DATABASE_URLS.env${c.reset} (gitignored)`);
392
+ log(` Neon: ${c.dim}staging/producción${c.reset} exigen host *.neon.tech; ${c.dim}development${c.reset} permite Postgres local (aviso).`);
345
393
  log(``);
346
394
  log(` Producción:`);
347
395
  log(` ${c.dim}KOMPLIAN_DATABASE_OPERATOR_EMAIL${c.reset} o git user.email`);
@@ -407,10 +455,17 @@ export async function runDb(argv) {
407
455
  log(
408
456
  ` Define ${c.bold}KOMPLIAN_DATABASE_URL_${platformToDbKey(opts.platform)}_${opts.environment.toUpperCase()}${c.reset} o ${c.dim}KOMPLIAN_DATABASE_URLS.env${c.reset}.`
409
457
  );
458
+ if (opts.environment === "development") {
459
+ log(
460
+ ` ${c.dim}(Los valores con ${LOCALHOST_DB_PLACEHOLDER} en .env.local se ignoran; usa una URL real en la raíz del monorepo.)${c.reset}`
461
+ );
462
+ }
410
463
  process.exit(1);
411
464
  }
412
465
 
413
- assertNeonHost(url);
466
+ assertNotPlaceholderDbUrl(url);
467
+
468
+ assertNeonHost(url, opts.environment);
414
469
 
415
470
  const psql = findPsqlBinary();
416
471
  if (!psql) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "komplian",
3
- "version": "0.5.0",
3
+ "version": "0.5.3",
4
4
  "description": "Komplian CLI: onboard, Postman, localhost, mcp-tools, db (psql). Node 18+. Published tarball has no .env / secrets.",
5
5
  "type": "module",
6
6
  "engines": {