@open-mercato/cli 0.6.6-develop.5598.1.5e7d48d297 → 0.6.6-develop.5617.1.62538c48ca

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.
@@ -1,6 +1,5 @@
1
1
  import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
2
2
  import { randomBytes } from "node:crypto";
3
- const DEFAULT_DERIVED_PASSWORD = "secret";
4
3
  function readEnvValue(env, key) {
5
4
  const value = env[key];
6
5
  if (typeof value !== "string") return void 0;
@@ -11,20 +10,28 @@ function resolveEmailFromDomain(domain, prefix) {
11
10
  if (!domain) return null;
12
11
  return `${prefix}@${domain}`;
13
12
  }
13
+ const DEMO_DERIVED_PASSWORD = "secret";
14
+ let warnedAboutDeprecatedRandomToggle = false;
14
15
  function resolveInitDerivedSecrets(options) {
15
16
  const env = options.env ?? process.env;
16
17
  const envRead = (key) => readEnvValue(env, key);
17
18
  const [, domain] = String(options.email ?? "").split("@");
18
19
  const adminEmail = envRead("OM_INIT_ADMIN_EMAIL") ?? resolveEmailFromDomain(domain, "admin");
19
20
  const employeeEmail = envRead("OM_INIT_EMPLOYEE_EMAIL") ?? resolveEmailFromDomain(domain, "employee");
20
- const randomEnabled = parseBooleanToken(envRead("OM_INIT_GENERATE_RANDOM_PASSWORD") ?? "") === true;
21
+ if (parseBooleanToken(envRead("OM_INIT_GENERATE_RANDOM_PASSWORD") ?? "") === true && !warnedAboutDeprecatedRandomToggle) {
22
+ warnedAboutDeprecatedRandomToggle = true;
23
+ console.warn(
24
+ "\u26A0\uFE0F OM_INIT_GENERATE_RANDOM_PASSWORD is deprecated and no longer required: derived admin/employee passwords are always randomized in production when overrides are unset."
25
+ );
26
+ }
27
+ const isProduction = (env.NODE_ENV ?? "").trim().toLowerCase() === "production";
21
28
  const randomize = options.randomSource ?? randomBytes;
22
- const randomSecret = () => randomize(9).toString("base64url");
29
+ const randomSecret = () => randomize(12).toString("base64url");
23
30
  const resolvePassword = (key, emailValue) => {
24
31
  if (!emailValue) return null;
25
32
  const envValue = envRead(key);
26
33
  if (envValue) return envValue;
27
- return randomEnabled ? randomSecret() : DEFAULT_DERIVED_PASSWORD;
34
+ return isProduction ? randomSecret() : DEMO_DERIVED_PASSWORD;
28
35
  };
29
36
  return {
30
37
  adminEmail,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/init-secrets.ts"],
4
- "sourcesContent": ["import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { randomBytes } from 'node:crypto'\n\ntype EnvReader = (key: string) => string | undefined\n\nconst DEFAULT_DERIVED_PASSWORD = 'secret'\n\nfunction readEnvValue(env: NodeJS.ProcessEnv, key: string): string | undefined {\n const value = env[key]\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\nfunction resolveEmailFromDomain(domain: string | undefined, prefix: string): string | null {\n if (!domain) return null\n return `${prefix}@${domain}`\n}\n\nexport type InitDerivedSecrets = {\n adminEmail: string | null\n employeeEmail: string | null\n adminPassword: string | null\n employeePassword: string | null\n}\n\nexport function resolveInitDerivedSecrets(options: {\n email: string\n env?: NodeJS.ProcessEnv\n randomSource?: (size: number) => Buffer\n}): InitDerivedSecrets {\n const env = options.env ?? process.env\n const envRead: EnvReader = (key) => readEnvValue(env, key)\n const [, domain] = String(options.email ?? '').split('@')\n const adminEmail = envRead('OM_INIT_ADMIN_EMAIL') ?? resolveEmailFromDomain(domain, 'admin')\n const employeeEmail = envRead('OM_INIT_EMPLOYEE_EMAIL') ?? resolveEmailFromDomain(domain, 'employee')\n const randomEnabled = parseBooleanToken(envRead('OM_INIT_GENERATE_RANDOM_PASSWORD') ?? '') === true\n const randomize = options.randomSource ?? randomBytes\n const randomSecret = () => randomize(9).toString('base64url')\n const resolvePassword = (key: string, emailValue: string | null) => {\n if (!emailValue) return null\n const envValue = envRead(key)\n if (envValue) return envValue\n return randomEnabled ? randomSecret() : DEFAULT_DERIVED_PASSWORD\n }\n\n return {\n adminEmail,\n employeeEmail,\n adminPassword: resolvePassword('OM_INIT_ADMIN_PASSWORD', adminEmail),\n employeePassword: resolvePassword('OM_INIT_EMPLOYEE_PASSWORD', employeeEmail),\n }\n}\n\n"],
5
- "mappings": "AAAA,SAAS,yBAAyB;AAClC,SAAS,mBAAmB;AAI5B,MAAM,2BAA2B;AAEjC,SAAS,aAAa,KAAwB,KAAiC;AAC7E,QAAM,QAAQ,IAAI,GAAG;AACrB,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEA,SAAS,uBAAuB,QAA4B,QAA+B;AACzF,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,GAAG,MAAM,IAAI,MAAM;AAC5B;AASO,SAAS,0BAA0B,SAInB;AACrB,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,UAAqB,CAAC,QAAQ,aAAa,KAAK,GAAG;AACzD,QAAM,CAAC,EAAE,MAAM,IAAI,OAAO,QAAQ,SAAS,EAAE,EAAE,MAAM,GAAG;AACxD,QAAM,aAAa,QAAQ,qBAAqB,KAAK,uBAAuB,QAAQ,OAAO;AAC3F,QAAM,gBAAgB,QAAQ,wBAAwB,KAAK,uBAAuB,QAAQ,UAAU;AACpG,QAAM,gBAAgB,kBAAkB,QAAQ,kCAAkC,KAAK,EAAE,MAAM;AAC/F,QAAM,YAAY,QAAQ,gBAAgB;AAC1C,QAAM,eAAe,MAAM,UAAU,CAAC,EAAE,SAAS,WAAW;AAC5D,QAAM,kBAAkB,CAAC,KAAa,eAA8B;AAClE,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,WAAW,QAAQ,GAAG;AAC5B,QAAI,SAAU,QAAO;AACrB,WAAO,gBAAgB,aAAa,IAAI;AAAA,EAC1C;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe,gBAAgB,0BAA0B,UAAU;AAAA,IACnE,kBAAkB,gBAAgB,6BAA6B,aAAa;AAAA,EAC9E;AACF;",
4
+ "sourcesContent": ["import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { randomBytes } from 'node:crypto'\n\ntype EnvReader = (key: string) => string | undefined\n\nfunction readEnvValue(env: NodeJS.ProcessEnv, key: string): string | undefined {\n const value = env[key]\n if (typeof value !== 'string') return undefined\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : undefined\n}\n\nfunction resolveEmailFromDomain(domain: string | undefined, prefix: string): string | null {\n if (!domain) return null\n return `${prefix}@${domain}`\n}\n\nexport type InitDerivedSecrets = {\n adminEmail: string | null\n employeeEmail: string | null\n adminPassword: string | null\n employeePassword: string | null\n}\n\n// Well-known dev/demo password baked into local bootstrap flows so developers\n// keep the documented \"log in as admin@<domain> with password 'secret'\" UX.\n// Only used when NODE_ENV !== 'production' and the OM_INIT_*_PASSWORD env vars\n// are unset \u2014 production paths always use a random 96-bit secret.\nconst DEMO_DERIVED_PASSWORD = 'secret'\n\nlet warnedAboutDeprecatedRandomToggle = false\n\nexport function resolveInitDerivedSecrets(options: {\n email: string\n env?: NodeJS.ProcessEnv\n randomSource?: (size: number) => Buffer\n}): InitDerivedSecrets {\n const env = options.env ?? process.env\n const envRead: EnvReader = (key) => readEnvValue(env, key)\n const [, domain] = String(options.email ?? '').split('@')\n const adminEmail = envRead('OM_INIT_ADMIN_EMAIL') ?? resolveEmailFromDomain(domain, 'admin')\n const employeeEmail = envRead('OM_INIT_EMPLOYEE_EMAIL') ?? resolveEmailFromDomain(domain, 'employee')\n // OM_INIT_GENERATE_RANDOM_PASSWORD used to be an opt-in toggle for random\n // derived secrets; in production it is now the unconditional behaviour when\n // no override is set. Surface a one-time deprecation warning so existing\n // operators notice their config is no longer required.\n if (parseBooleanToken(envRead('OM_INIT_GENERATE_RANDOM_PASSWORD') ?? '') === true && !warnedAboutDeprecatedRandomToggle) {\n warnedAboutDeprecatedRandomToggle = true\n console.warn(\n '\u26A0\uFE0F OM_INIT_GENERATE_RANDOM_PASSWORD is deprecated and no longer required: derived admin/employee passwords are always randomized in production when overrides are unset.',\n )\n }\n const isProduction = (env.NODE_ENV ?? '').trim().toLowerCase() === 'production'\n const randomize = options.randomSource ?? randomBytes\n const randomSecret = () => randomize(12).toString('base64url')\n const resolvePassword = (key: string, emailValue: string | null) => {\n if (!emailValue) return null\n const envValue = envRead(key)\n if (envValue) return envValue\n // Non-production (dev/test/staging defaults): seed the documented demo\n // password so `yarn dev` / `mercato init` workflows stay predictable.\n // Production: generate a fresh random secret so credentials are never\n // hardcoded and the operator-facing CLI prints the value once.\n return isProduction ? randomSecret() : DEMO_DERIVED_PASSWORD\n }\n\n return {\n adminEmail,\n employeeEmail,\n adminPassword: resolvePassword('OM_INIT_ADMIN_PASSWORD', adminEmail),\n employeePassword: resolvePassword('OM_INIT_EMPLOYEE_PASSWORD', employeeEmail),\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,yBAAyB;AAClC,SAAS,mBAAmB;AAI5B,SAAS,aAAa,KAAwB,KAAiC;AAC7E,QAAM,QAAQ,IAAI,GAAG;AACrB,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,QAAM,UAAU,MAAM,KAAK;AAC3B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAEA,SAAS,uBAAuB,QAA4B,QAA+B;AACzF,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,GAAG,MAAM,IAAI,MAAM;AAC5B;AAaA,MAAM,wBAAwB;AAE9B,IAAI,oCAAoC;AAEjC,SAAS,0BAA0B,SAInB;AACrB,QAAM,MAAM,QAAQ,OAAO,QAAQ;AACnC,QAAM,UAAqB,CAAC,QAAQ,aAAa,KAAK,GAAG;AACzD,QAAM,CAAC,EAAE,MAAM,IAAI,OAAO,QAAQ,SAAS,EAAE,EAAE,MAAM,GAAG;AACxD,QAAM,aAAa,QAAQ,qBAAqB,KAAK,uBAAuB,QAAQ,OAAO;AAC3F,QAAM,gBAAgB,QAAQ,wBAAwB,KAAK,uBAAuB,QAAQ,UAAU;AAKpG,MAAI,kBAAkB,QAAQ,kCAAkC,KAAK,EAAE,MAAM,QAAQ,CAAC,mCAAmC;AACvH,wCAAoC;AACpC,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AACA,QAAM,gBAAgB,IAAI,YAAY,IAAI,KAAK,EAAE,YAAY,MAAM;AACnE,QAAM,YAAY,QAAQ,gBAAgB;AAC1C,QAAM,eAAe,MAAM,UAAU,EAAE,EAAE,SAAS,WAAW;AAC7D,QAAM,kBAAkB,CAAC,KAAa,eAA8B;AAClE,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,WAAW,QAAQ,GAAG;AAC5B,QAAI,SAAU,QAAO;AAKrB,WAAO,eAAe,aAAa,IAAI;AAAA,EACzC;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,eAAe,gBAAgB,0BAA0B,UAAU;AAAA,IACnE,kBAAkB,gBAAgB,6BAA6B,aAAa;AAAA,EAC9E;AACF;",
6
6
  "names": []
7
7
  }
@@ -1215,6 +1215,15 @@ function buildReusableEnvironment(baseUrl, databaseUrl, queueBaseDir, captureScr
1215
1215
  NODE_ENV: "production",
1216
1216
  JWT_SECRET: process.env.JWT_SECRET ?? "om-ephemeral-integration-jwt-secret",
1217
1217
  OM_SECURITY_MFA_SETUP_SECRET: process.env.OM_SECURITY_MFA_SETUP_SECRET ?? "om-ephemeral-integration-mfa-setup-secret",
1218
+ // Integration probe + tests expect `admin@acme.com / secret` and
1219
+ // `employee@acme.com / secret`. NODE_ENV=production routes derived-user
1220
+ // password resolution through the random-fallback branch unless these
1221
+ // env vars are explicitly set; without the override every fresh
1222
+ // ephemeral run would mint random passwords and the login probe would
1223
+ // never converge. This is the documented production contract: set
1224
+ // OM_INIT_*_PASSWORD to fix the seeded credential.
1225
+ OM_INIT_ADMIN_PASSWORD: process.env.OM_INIT_ADMIN_PASSWORD ?? "secret",
1226
+ OM_INIT_EMPLOYEE_PASSWORD: process.env.OM_INIT_EMPLOYEE_PASSWORD ?? "secret",
1218
1227
  OM_INTEGRATION_TEST: "true",
1219
1228
  OM_ENABLE_ENTERPRISE_MODULES: enterpriseModulesFlag,
1220
1229
  OM_ENABLE_ENTERPRISE_MODULES_SSO: process.env.OM_ENABLE_ENTERPRISE_MODULES_SSO ?? enterpriseModulesFlag,
@@ -1226,6 +1235,11 @@ function buildReusableEnvironment(baseUrl, databaseUrl, queueBaseDir, captureScr
1226
1235
  // not have to call flushPendingCrudAccessLogs() explicitly.
1227
1236
  OM_CRUD_ACCESS_LOG_BLOCKING: process.env.OM_CRUD_ACCESS_LOG_BLOCKING ?? "1",
1228
1237
  OM_WEBHOOKS_ALLOW_PRIVATE_URLS: process.env.OM_WEBHOOKS_ALLOW_PRIVATE_URLS ?? "1",
1238
+ // Keep the bus in the Playwright process (used by in-test queue-drain helpers)
1239
+ // on the same delivery mode as the app server it drives: inline persistent
1240
+ // delivery so event side effects are deterministic for assertions. See the
1241
+ // matching OM_EVENTS_SINGLE_DELIVERY note on the app server environment below.
1242
+ OM_EVENTS_SINGLE_DELIVERY: process.env.OM_EVENTS_SINGLE_DELIVERY ?? "false",
1229
1243
  ENABLE_CRUD_API_CACHE: "true",
1230
1244
  MOCK_GATEWAY_WEBHOOK_SECRET: "open-mercato-mock-dev-webhook-secret",
1231
1245
  MOCK_CARRIER_WEBHOOK_SECRET: "open-mercato-mock-dev-carrier-webhook-secret",
@@ -2343,6 +2357,11 @@ async function startEphemeralEnvironment(options) {
2343
2357
  JWT_SECRET: process.env.JWT_SECRET ?? "om-ephemeral-integration-jwt-secret",
2344
2358
  OM_SECURITY_MFA_SETUP_SECRET: process.env.OM_SECURITY_MFA_SETUP_SECRET ?? "om-ephemeral-integration-mfa-setup-secret",
2345
2359
  NODE_ENV: "production",
2360
+ // See the auth-probe block above: pin derived-user passwords to the
2361
+ // documented 'secret' so the ephemeral login probe converges under
2362
+ // NODE_ENV=production.
2363
+ OM_INIT_ADMIN_PASSWORD: process.env.OM_INIT_ADMIN_PASSWORD ?? "secret",
2364
+ OM_INIT_EMPLOYEE_PASSWORD: process.env.OM_INIT_EMPLOYEE_PASSWORD ?? "secret",
2346
2365
  // Pool sizing for the ephemeral integration runtime. Defaults were once
2347
2366
  // very aggressive (max=5, idle=1000) which exposed flaky 'timeout exceeded
2348
2367
  // when trying to connect' errors on `progressService.createJob`-backed
@@ -2374,6 +2393,18 @@ async function startEphemeralEnvironment(options) {
2374
2393
  CI: "true",
2375
2394
  TENANT_DATA_ENCRYPTION_FALLBACK_KEY: process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY ?? "om-ephemeral-integration-fallback-key",
2376
2395
  AUTO_SPAWN_WORKERS: process.env.AUTO_SPAWN_WORKERS ?? "true",
2396
+ // Process persistent event subscribers INLINE in the request that emits
2397
+ // the event (legacy dual-dispatch), rather than the production default of
2398
+ // worker-only single delivery. Integration specs assert event side effects
2399
+ // (sync mappings, workflow-trigger instances, notifications) immediately
2400
+ // after the emitting API call and poll for them on short budgets; routing
2401
+ // those subscribers through the async events worker makes the side effect
2402
+ // race the poll under the 15-shard CI load, which surfaced as flaky
2403
+ // timeouts in TC-CRM-028 (inbound sync mapping) and TC-WF-008 (event-
2404
+ // triggered workflow) once single-delivery became default-ON. Inline
2405
+ // delivery makes the side effects deterministic for tests; production keeps
2406
+ // the single-delivery default. Honor an explicit override if the caller set one.
2407
+ OM_EVENTS_SINGLE_DELIVERY: process.env.OM_EVENTS_SINGLE_DELIVERY ?? "false",
2377
2408
  AUTO_SPAWN_SCHEDULER: "false",
2378
2409
  // Hide the demo feedback floating action button — it lives at
2379
2410
  // `fixed bottom-6 right-6 z-banner` and consistently intercepts pointer