@open-mercato/cli 0.6.3 → 0.6.4-canary.3917.1.4d8c013983

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,4 +1,4 @@
1
- [build:cli] found 74 entry points
1
+ [build:cli] found 73 entry points
2
2
  Copied create-app/agentic/ → dist/agentic/
3
3
  Discovered 16 standalone guides → dist/agentic/guides/
4
4
  [build:cli] built successfully
package/AGENTS.md CHANGED
@@ -72,7 +72,7 @@ The structural cache purge does two things:
72
72
  1. Deletes Redis cache keys matching `nav:*` so navigation/sidebar caches are rebuilt next render.
73
73
  2. Touches every `*.generated.ts` and `*.generated.checksum` file in the current app's `.mercato/generated/` directory by rewriting them with identical bytes. This advances mtime without changing content, which forces Turbopack's filesystem watcher to invalidate the import graph and recompile leaf files that imported a barrel. Without this, Turbopack can keep serving a cached compile error against a since-fixed module file until the dev server is restarted.
74
74
 
75
- The dev escape hatch is `yarn dev:reset`, which clears the configured Next dev cache (`.mercato/next/dev`) plus legacy `.next` cache directories for the rare case where Turbopack's internal cache stays stuck after a structural purge.
75
+ The dev escape hatch is `yarn dev:reset`, which clears `.next/cache/turbopack` and `.next/cache/webpack` for the rare case where Turbopack's internal cache stays stuck after a structural purge.
76
76
 
77
77
  ## Database Migrations
78
78
 
@@ -201,7 +201,6 @@ When the user asks to **create a new application** or a **new module**, do not i
201
201
  - Entity classes belong in `src/modules/<module>/data/entities.ts` and use decorators from `@mikro-orm/decorators/legacy`
202
202
  - Standalone apps expose `yarn mercato configs cache ...` because the template enables the `configs` module from `@open-mercato/core`
203
203
  - `yarn generate` automatically runs a best-effort structural cache purge (`yarn mercato configs cache structural --all-tenants`) after successful generation; if the cache command is unavailable, generation still succeeds
204
- - Use `yarn dev:reset` only as the stale-Turbopack escape hatch; it clears `.mercato/next/dev` plus legacy `.next` caches.
205
204
  - Detail/read-model APIs that expose `customFields` MUST return bare field keys via `normalizeCustomFieldResponse()` (for example `{ priority: 3 }`). Keep `cf_` / `cf:` prefixes for request payloads, filters, and form field IDs only.
206
205
  - Sidebar icons MUST use `lucide-react` components — never inline SVG via `React.createElement`
207
206
  - `page.meta.ts` MUST include `pageGroup`, `pageGroupKey`, and `pageOrder` for sidebar grouping
@@ -1,7 +1,6 @@
1
1
  import dotenv from "dotenv";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- const RUNTIME_GRAPH_FILE_PATTERN = /\.generated(?:\.[a-z0-9]+)?(?:\.tsx?|\.checksum)$/i;
5
4
  const DEV_ENV_PRIORITY = [
6
5
  ".env",
7
6
  ".env.development",
@@ -82,47 +81,9 @@ function watchDevEnvFiles(appDir, onChange, options = {}) {
82
81
  }
83
82
  };
84
83
  }
85
- function watchDevRuntimeFiles(appDir, onChange, options = {}) {
86
- const debounceMs = options.debounceMs ?? 250;
87
- const generatedDir = path.join(appDir, ".mercato", "generated");
88
- const timers = /* @__PURE__ */ new Map();
89
- if (!fs.existsSync(generatedDir)) {
90
- return () => {
91
- };
92
- }
93
- let watcher = null;
94
- try {
95
- watcher = fs.watch(generatedDir, (eventType, fileName) => {
96
- if (eventType !== "change" && eventType !== "rename") return;
97
- const name = String(fileName ?? "");
98
- if (!name) return;
99
- if (!RUNTIME_GRAPH_FILE_PATTERN.test(name)) return;
100
- const filePath = path.join(generatedDir, name);
101
- const existingTimer = timers.get(filePath);
102
- if (existingTimer) {
103
- clearTimeout(existingTimer);
104
- }
105
- timers.set(filePath, setTimeout(() => {
106
- timers.delete(filePath);
107
- onChange(filePath);
108
- }, debounceMs));
109
- });
110
- } catch {
111
- return () => {
112
- };
113
- }
114
- return () => {
115
- for (const timer of timers.values()) {
116
- clearTimeout(timer);
117
- }
118
- timers.clear();
119
- watcher?.close();
120
- };
121
- }
122
84
  export {
123
85
  createDevEnvReloader,
124
86
  resolveDevEnvFilePaths,
125
- watchDevEnvFiles,
126
- watchDevRuntimeFiles
87
+ watchDevEnvFiles
127
88
  };
128
89
  //# sourceMappingURL=dev-env-reload.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/dev-env-reload.ts"],
4
- "sourcesContent": ["import dotenv from 'dotenv'\nimport fs from 'node:fs'\nimport path from 'node:path'\n\nconst RUNTIME_GRAPH_FILE_PATTERN = /\\.generated(?:\\.[a-z0-9]+)?(?:\\.tsx?|\\.checksum)$/i\n\nconst DEV_ENV_PRIORITY = [\n '.env',\n '.env.development',\n '.env.local',\n '.env.development.local',\n] as const\n\nexport type DevEnvReloader = {\n reload: () => void\n getWatchedFiles: () => string[]\n}\n\nexport function resolveDevEnvFilePaths(appDir: string): string[] {\n return DEV_ENV_PRIORITY.map((fileName) => path.join(appDir, fileName))\n}\n\nexport function createDevEnvReloader(\n appDir: string,\n environment: NodeJS.ProcessEnv = process.env,\n baseEnvironmentEntries: Iterable<[string, string | undefined]> = Object.entries(environment),\n): DevEnvReloader {\n const baseEnvironment = new Map<string, string | undefined>(\n Array.from(baseEnvironmentEntries, ([key, value]) => [key, value]),\n )\n const managedKeys = new Set<string>()\n const envFilePaths = resolveDevEnvFilePaths(appDir)\n\n const resetManagedKeys = () => {\n for (const key of managedKeys) {\n const baseValue = baseEnvironment.get(key)\n if (baseValue === undefined) {\n delete environment[key]\n } else {\n environment[key] = baseValue\n }\n }\n managedKeys.clear()\n }\n\n const reload = () => {\n resetManagedKeys()\n\n for (const envFilePath of envFilePaths) {\n if (!fs.existsSync(envFilePath)) continue\n\n const parsed = dotenv.parse(fs.readFileSync(envFilePath))\n for (const [key, value] of Object.entries(parsed)) {\n if (baseEnvironment.has(key) && baseEnvironment.get(key) !== undefined) {\n continue\n }\n environment[key] = value\n managedKeys.add(key)\n }\n }\n }\n\n return {\n reload,\n getWatchedFiles: () => [...envFilePaths],\n }\n}\n\nexport function watchDevEnvFiles(\n appDir: string,\n onChange: (filePath: string) => void,\n options: { debounceMs?: number } = {},\n): () => void {\n const debounceMs = options.debounceMs ?? 250\n const envFilePaths = resolveDevEnvFilePaths(appDir)\n const timers = new Map<string, NodeJS.Timeout>()\n const watchers = envFilePaths.map((envFilePath) => {\n const watchDir = path.dirname(envFilePath)\n const watchFileName = path.basename(envFilePath)\n\n if (!fs.existsSync(watchDir)) return null\n\n try {\n return fs.watch(watchDir, (eventType, fileName) => {\n if (eventType !== 'change' && eventType !== 'rename') return\n if (String(fileName ?? '') !== watchFileName) return\n\n const existingTimer = timers.get(envFilePath)\n if (existingTimer) {\n clearTimeout(existingTimer)\n }\n\n timers.set(envFilePath, setTimeout(() => {\n timers.delete(envFilePath)\n onChange(envFilePath)\n }, debounceMs))\n })\n } catch {\n return null\n }\n }).filter((watcher): watcher is fs.FSWatcher => watcher !== null)\n\n return () => {\n for (const timer of timers.values()) {\n clearTimeout(timer)\n }\n timers.clear()\n for (const watcher of watchers) {\n watcher.close()\n }\n }\n}\n\nexport function watchDevRuntimeFiles(\n appDir: string,\n onChange: (filePath: string) => void,\n options: { debounceMs?: number } = {},\n): () => void {\n const debounceMs = options.debounceMs ?? 250\n const generatedDir = path.join(appDir, '.mercato', 'generated')\n const timers = new Map<string, NodeJS.Timeout>()\n\n if (!fs.existsSync(generatedDir)) {\n return () => {}\n }\n\n let watcher: fs.FSWatcher | null = null\n try {\n watcher = fs.watch(generatedDir, (eventType, fileName) => {\n if (eventType !== 'change' && eventType !== 'rename') return\n const name = String(fileName ?? '')\n if (!name) return\n if (!RUNTIME_GRAPH_FILE_PATTERN.test(name)) return\n\n const filePath = path.join(generatedDir, name)\n const existingTimer = timers.get(filePath)\n if (existingTimer) {\n clearTimeout(existingTimer)\n }\n\n timers.set(filePath, setTimeout(() => {\n timers.delete(filePath)\n onChange(filePath)\n }, debounceMs))\n })\n } catch {\n return () => {}\n }\n\n return () => {\n for (const timer of timers.values()) {\n clearTimeout(timer)\n }\n timers.clear()\n watcher?.close()\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import dotenv from 'dotenv'\nimport fs from 'node:fs'\nimport path from 'node:path'\n\nconst DEV_ENV_PRIORITY = [\n '.env',\n '.env.development',\n '.env.local',\n '.env.development.local',\n] as const\n\nexport type DevEnvReloader = {\n reload: () => void\n getWatchedFiles: () => string[]\n}\n\nexport function resolveDevEnvFilePaths(appDir: string): string[] {\n return DEV_ENV_PRIORITY.map((fileName) => path.join(appDir, fileName))\n}\n\nexport function createDevEnvReloader(\n appDir: string,\n environment: NodeJS.ProcessEnv = process.env,\n baseEnvironmentEntries: Iterable<[string, string | undefined]> = Object.entries(environment),\n): DevEnvReloader {\n const baseEnvironment = new Map<string, string | undefined>(\n Array.from(baseEnvironmentEntries, ([key, value]) => [key, value]),\n )\n const managedKeys = new Set<string>()\n const envFilePaths = resolveDevEnvFilePaths(appDir)\n\n const resetManagedKeys = () => {\n for (const key of managedKeys) {\n const baseValue = baseEnvironment.get(key)\n if (baseValue === undefined) {\n delete environment[key]\n } else {\n environment[key] = baseValue\n }\n }\n managedKeys.clear()\n }\n\n const reload = () => {\n resetManagedKeys()\n\n for (const envFilePath of envFilePaths) {\n if (!fs.existsSync(envFilePath)) continue\n\n const parsed = dotenv.parse(fs.readFileSync(envFilePath))\n for (const [key, value] of Object.entries(parsed)) {\n if (baseEnvironment.has(key) && baseEnvironment.get(key) !== undefined) {\n continue\n }\n environment[key] = value\n managedKeys.add(key)\n }\n }\n }\n\n return {\n reload,\n getWatchedFiles: () => [...envFilePaths],\n }\n}\n\nexport function watchDevEnvFiles(\n appDir: string,\n onChange: (filePath: string) => void,\n options: { debounceMs?: number } = {},\n): () => void {\n const debounceMs = options.debounceMs ?? 250\n const envFilePaths = resolveDevEnvFilePaths(appDir)\n const timers = new Map<string, NodeJS.Timeout>()\n const watchers = envFilePaths.map((envFilePath) => {\n const watchDir = path.dirname(envFilePath)\n const watchFileName = path.basename(envFilePath)\n\n if (!fs.existsSync(watchDir)) return null\n\n try {\n return fs.watch(watchDir, (eventType, fileName) => {\n if (eventType !== 'change' && eventType !== 'rename') return\n if (String(fileName ?? '') !== watchFileName) return\n\n const existingTimer = timers.get(envFilePath)\n if (existingTimer) {\n clearTimeout(existingTimer)\n }\n\n timers.set(envFilePath, setTimeout(() => {\n timers.delete(envFilePath)\n onChange(envFilePath)\n }, debounceMs))\n })\n } catch {\n return null\n }\n }).filter((watcher): watcher is fs.FSWatcher => watcher !== null)\n\n return () => {\n for (const timer of timers.values()) {\n clearTimeout(timer)\n }\n timers.clear()\n for (const watcher of watchers) {\n watcher.close()\n }\n }\n}\n"],
5
+ "mappings": "AAAA,OAAO,YAAY;AACnB,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,MAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAOO,SAAS,uBAAuB,QAA0B;AAC/D,SAAO,iBAAiB,IAAI,CAAC,aAAa,KAAK,KAAK,QAAQ,QAAQ,CAAC;AACvE;AAEO,SAAS,qBACd,QACA,cAAiC,QAAQ,KACzC,yBAAiE,OAAO,QAAQ,WAAW,GAC3E;AAChB,QAAM,kBAAkB,IAAI;AAAA,IAC1B,MAAM,KAAK,wBAAwB,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,KAAK,CAAC;AAAA,EACnE;AACA,QAAM,cAAc,oBAAI,IAAY;AACpC,QAAM,eAAe,uBAAuB,MAAM;AAElD,QAAM,mBAAmB,MAAM;AAC7B,eAAW,OAAO,aAAa;AAC7B,YAAM,YAAY,gBAAgB,IAAI,GAAG;AACzC,UAAI,cAAc,QAAW;AAC3B,eAAO,YAAY,GAAG;AAAA,MACxB,OAAO;AACL,oBAAY,GAAG,IAAI;AAAA,MACrB;AAAA,IACF;AACA,gBAAY,MAAM;AAAA,EACpB;AAEA,QAAM,SAAS,MAAM;AACnB,qBAAiB;AAEjB,eAAW,eAAe,cAAc;AACtC,UAAI,CAAC,GAAG,WAAW,WAAW,EAAG;AAEjC,YAAM,SAAS,OAAO,MAAM,GAAG,aAAa,WAAW,CAAC;AACxD,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,YAAI,gBAAgB,IAAI,GAAG,KAAK,gBAAgB,IAAI,GAAG,MAAM,QAAW;AACtE;AAAA,QACF;AACA,oBAAY,GAAG,IAAI;AACnB,oBAAY,IAAI,GAAG;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,iBAAiB,MAAM,CAAC,GAAG,YAAY;AAAA,EACzC;AACF;AAEO,SAAS,iBACd,QACA,UACA,UAAmC,CAAC,GACxB;AACZ,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,eAAe,uBAAuB,MAAM;AAClD,QAAM,SAAS,oBAAI,IAA4B;AAC/C,QAAM,WAAW,aAAa,IAAI,CAAC,gBAAgB;AACjD,UAAM,WAAW,KAAK,QAAQ,WAAW;AACzC,UAAM,gBAAgB,KAAK,SAAS,WAAW;AAE/C,QAAI,CAAC,GAAG,WAAW,QAAQ,EAAG,QAAO;AAErC,QAAI;AACF,aAAO,GAAG,MAAM,UAAU,CAAC,WAAW,aAAa;AACjD,YAAI,cAAc,YAAY,cAAc,SAAU;AACtD,YAAI,OAAO,YAAY,EAAE,MAAM,cAAe;AAE9C,cAAM,gBAAgB,OAAO,IAAI,WAAW;AAC5C,YAAI,eAAe;AACjB,uBAAa,aAAa;AAAA,QAC5B;AAEA,eAAO,IAAI,aAAa,WAAW,MAAM;AACvC,iBAAO,OAAO,WAAW;AACzB,mBAAS,WAAW;AAAA,QACtB,GAAG,UAAU,CAAC;AAAA,MAChB,CAAC;AAAA,IACH,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF,CAAC,EAAE,OAAO,CAAC,YAAqC,YAAY,IAAI;AAEhE,SAAO,MAAM;AACX,eAAW,SAAS,OAAO,OAAO,GAAG;AACnC,mBAAa,KAAK;AAAA,IACpB;AACA,WAAO,MAAM;AACb,eAAW,WAAW,UAAU;AAC9B,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
package/dist/mercato.js CHANGED
@@ -246,102 +246,6 @@ function buildServerProcessEnvironment(environment) {
246
246
  }
247
247
  return runtimeEnv;
248
248
  }
249
- function resolveDevRuntimeBaseUrl(environment = process.env) {
250
- const configured = environment.APP_URL ?? environment.NEXT_PUBLIC_APP_URL ?? environment.NEXTAUTH_URL;
251
- if (configured?.trim()) {
252
- return configured.trim().replace(/\/+$/, "");
253
- }
254
- return `http://localhost:${environment.PORT?.trim() || "3000"}`;
255
- }
256
- function writeDevSplashChildState(state) {
257
- if (process.env.OM_DEV_SPLASH_RUNTIME_WRAPPER === "1") return;
258
- const stateFile = process.env.OM_DEV_SPLASH_CHILD_STATE_FILE;
259
- if (!stateFile?.trim()) return;
260
- try {
261
- fs.mkdirSync(path.dirname(stateFile), { recursive: true });
262
- fs.writeFileSync(stateFile, `${JSON.stringify({
263
- mode: process.env.OM_DEV_SPLASH_MODE || "dev",
264
- failed: false,
265
- failureLines: [],
266
- failureCommand: null,
267
- ...state
268
- }, null, 2)}
269
- `);
270
- } catch {
271
- }
272
- }
273
- function writeDevSplashRuntimeStarting(detail = "Starting Next.js dev server") {
274
- writeDevSplashChildState({
275
- phase: "Preparing app runtime",
276
- detail,
277
- ready: false,
278
- readyUrl: null,
279
- loginUrl: null,
280
- progressLabel: "Launching app runtime",
281
- activity: detail
282
- });
283
- }
284
- function resolveSplashProgressFallback() {
285
- const current = Number.parseInt(process.env.OM_DEV_SPLASH_STAGE_CURRENT ?? "", 10);
286
- const total = Number.parseInt(process.env.OM_DEV_SPLASH_STAGE_TOTAL ?? "", 10);
287
- if (Number.isFinite(current) && Number.isFinite(total) && total > 0) {
288
- return { current, total };
289
- }
290
- if (process.env.OM_DEV_SPLASH_MODE === "greenfield" || process.env.OM_DEV_SPLASH_MODE === "setup") {
291
- return { current: 5, total: 5 };
292
- }
293
- return { current: 3, total: 3 };
294
- }
295
- function writeDevSplashRuntimeRestarting(reason) {
296
- const progress = resolveSplashProgressFallback();
297
- writeDevSplashChildState({
298
- phase: "App runtime is restarting",
299
- detail: `Reason: ${reason}`,
300
- ready: false,
301
- readyUrl: null,
302
- loginUrl: null,
303
- progressCurrent: progress.current,
304
- progressTotal: progress.total,
305
- progressLabel: "Restarting app runtime",
306
- activity: `App runtime restart: ${reason}`
307
- });
308
- }
309
- function writeDevSplashRuntimeReady(reason) {
310
- const readyUrl = resolveDevRuntimeBaseUrl();
311
- const progress = resolveSplashProgressFallback();
312
- writeDevSplashChildState({
313
- phase: "App is ready",
314
- detail: reason ? `Restart completed after ${reason}` : "Next.js dev server is ready",
315
- ready: true,
316
- readyUrl,
317
- loginUrl: `${readyUrl}/login`,
318
- progressCurrent: progress.current,
319
- progressTotal: progress.total,
320
- progressPercent: 100,
321
- progressLabel: "App is ready",
322
- activity: reason ? `Restart completed after ${reason}` : "App runtime is ready"
323
- });
324
- }
325
- function resolveDevWarmupReadyTimeoutMs(environment = process.env) {
326
- const parsed = Number.parseInt(environment.OM_DEV_WARMUP_READY_TIMEOUT_MS ?? "", 10);
327
- if (Number.isFinite(parsed) && parsed >= 0) return parsed;
328
- return 3e5;
329
- }
330
- async function waitForDevWarmupReadyFile(filePath, options = {}) {
331
- const normalized = filePath?.trim();
332
- if (!normalized) return "ready";
333
- const timeoutMs = options.timeoutMs ?? resolveDevWarmupReadyTimeoutMs();
334
- const startedAt = Date.now();
335
- while (true) {
336
- if (options.signal?.aborted) return "aborted";
337
- try {
338
- if (fs.existsSync(normalized)) return "ready";
339
- } catch {
340
- }
341
- if (timeoutMs >= 0 && Date.now() - startedAt >= timeoutMs) return "timeout";
342
- await new Promise((resolve) => setTimeout(resolve, 250));
343
- }
344
- }
345
249
  function waitForManagedProcessExit(proc, label) {
346
250
  return new Promise((resolve) => {
347
251
  proc.on("exit", (code, signal) => {
@@ -541,17 +445,18 @@ async function runGeneratorSuite(quiet) {
541
445
  function createGenerateWatchChecksumFn() {
542
446
  return async () => {
543
447
  const { createResolver } = await import("./lib/resolver.js");
544
- const { calculateGenerateWatchStructureChecksum } = await import("./lib/generate-watch-structure.js");
448
+ const { calculateStructureChecksum } = await import("./lib/utils.js");
545
449
  const resolver = createResolver();
546
- const moduleRoots = [];
450
+ const tracked = /* @__PURE__ */ new Set([
451
+ path.join(resolver.getAppDir(), "src", "modules.ts"),
452
+ path.join(resolver.getAppDir(), "src", "modules")
453
+ ]);
547
454
  for (const entry of resolver.loadEnabledModules()) {
548
455
  const roots = resolver.getModulePaths(entry);
549
- moduleRoots.push({ appBase: roots.appBase, pkgBase: roots.pkgBase });
456
+ tracked.add(roots.appBase);
457
+ tracked.add(roots.pkgBase);
550
458
  }
551
- return calculateGenerateWatchStructureChecksum({
552
- modulesFile: path.join(resolver.getAppDir(), "src", "modules.ts"),
553
- moduleRoots
554
- });
459
+ return calculateStructureChecksum(Array.from(tracked));
555
460
  };
556
461
  }
557
462
  async function buildAllModules() {
@@ -1485,11 +1390,10 @@ async function run(argv = process.argv) {
1485
1390
  let processes = [];
1486
1391
  let didRetryCorruptedTurbopackCache = false;
1487
1392
  let stopping = false;
1488
- let devRestartPromiseResolve = null;
1393
+ let envChangePromiseResolve = null;
1489
1394
  let activeLazySupervisor = null;
1490
1395
  let activeLazySchedulerSupervisor = null;
1491
1396
  let activeGenerateWatcher = null;
1492
- let lastRestartReason = null;
1493
1397
  const generateWatcherMode = resolveGenerateWatcherMode(process.env);
1494
1398
  const envReloader = createDevEnvReloader(appDir, process.env, initialProcessEnvironmentEntries);
1495
1399
  function cleanup() {
@@ -1562,74 +1466,53 @@ async function run(argv = process.argv) {
1562
1466
  const nextBin = resolveInstalledBinary(nodeModulesBases, "next/dist/bin/next");
1563
1467
  const mercatoBin = resolveInstalledBinary(nodeModulesBases, "@open-mercato/cli/bin/mercato");
1564
1468
  const stopEnvWatcher = watchDevEnvFiles(appDir, (filePath) => {
1565
- devRestartPromiseResolve?.({
1469
+ envChangePromiseResolve?.({
1566
1470
  label: "Environment file change",
1567
1471
  restart: true,
1568
1472
  filePath
1569
1473
  });
1570
1474
  });
1571
- const waitForDevRestart = () => new Promise((resolve) => {
1572
- devRestartPromiseResolve = resolve;
1475
+ const waitForEnvChange = () => new Promise((resolve) => {
1476
+ envChangePromiseResolve = resolve;
1573
1477
  });
1574
- const startNextDev = (runtimeEnv) => {
1575
- let readyResolve = () => void 0;
1576
- const readyPromise = new Promise((resolve) => {
1577
- readyResolve = resolve;
1478
+ const startNextDev = (runtimeEnv) => new Promise((resolve) => {
1479
+ const nextProcess = spawn("node", [nextBin, "dev", "--turbopack"], {
1480
+ stdio: ["inherit", "pipe", "pipe"],
1481
+ env: runtimeEnv,
1482
+ cwd: appDir
1578
1483
  });
1579
- const exitPromise = new Promise((resolve) => {
1580
- writeDevSplashRuntimeStarting(
1581
- lastRestartReason ? `Restarting Next.js dev server. Reason: ${lastRestartReason}` : "Starting Next.js dev server"
1582
- );
1583
- const nextProcess = spawn("node", [nextBin, "dev", "--turbopack"], {
1584
- stdio: ["inherit", "pipe", "pipe"],
1585
- env: runtimeEnv,
1586
- cwd: appDir
1587
- });
1588
- processes.push(nextProcess);
1589
- let combinedOutput = "";
1590
- let reportedReady = false;
1591
- const appendOutput = (chunk) => {
1592
- combinedOutput += chunk;
1593
- if (combinedOutput.length > 32768) {
1594
- combinedOutput = combinedOutput.slice(-32768);
1595
- }
1596
- if (!reportedReady && /\bready in\b/i.test(chunk)) {
1597
- reportedReady = true;
1598
- writeDevSplashRuntimeReady(lastRestartReason ?? void 0);
1599
- lastRestartReason = null;
1600
- readyResolve();
1601
- }
1602
- };
1603
- nextProcess.stdout?.on("data", (chunk) => {
1604
- const text = typeof chunk === "string" ? chunk : chunk.toString();
1605
- process.stdout.write(text);
1606
- appendOutput(text);
1607
- });
1608
- nextProcess.stderr?.on("data", (chunk) => {
1609
- const text = typeof chunk === "string" ? chunk : chunk.toString();
1610
- process.stderr.write(text);
1611
- appendOutput(text);
1612
- });
1613
- nextProcess.on("exit", async (code, signal) => {
1614
- if (!didRetryCorruptedTurbopackCache && isTurbopackCacheCorruption(combinedOutput)) {
1615
- didRetryCorruptedTurbopackCache = true;
1616
- lastRestartReason = "corrupted Turbopack dev cache";
1617
- writeDevSplashRuntimeRestarting(lastRestartReason);
1618
- console.log("[server] Detected corrupted Turbopack dev cache. Clearing .mercato/next/dev and restarting Next.js once...");
1619
- removeTurbopackDevCache(appDir);
1620
- const restarted = startNextDev(runtimeEnv);
1621
- restarted.readyPromise.then(readyResolve);
1622
- return resolve(await restarted.exitPromise);
1623
- }
1624
- resolve({
1625
- label: "Next.js dev server",
1626
- code,
1627
- signal
1628
- });
1484
+ processes.push(nextProcess);
1485
+ let combinedOutput = "";
1486
+ const appendOutput = (chunk) => {
1487
+ combinedOutput += chunk;
1488
+ if (combinedOutput.length > 32768) {
1489
+ combinedOutput = combinedOutput.slice(-32768);
1490
+ }
1491
+ };
1492
+ nextProcess.stdout?.on("data", (chunk) => {
1493
+ const text = typeof chunk === "string" ? chunk : chunk.toString();
1494
+ process.stdout.write(text);
1495
+ appendOutput(text);
1496
+ });
1497
+ nextProcess.stderr?.on("data", (chunk) => {
1498
+ const text = typeof chunk === "string" ? chunk : chunk.toString();
1499
+ process.stderr.write(text);
1500
+ appendOutput(text);
1501
+ });
1502
+ nextProcess.on("exit", async (code, signal) => {
1503
+ if (!didRetryCorruptedTurbopackCache && isTurbopackCacheCorruption(combinedOutput)) {
1504
+ didRetryCorruptedTurbopackCache = true;
1505
+ console.log("[server] Detected corrupted Turbopack dev cache. Clearing .mercato/next/dev and restarting Next.js once...");
1506
+ removeTurbopackDevCache(appDir);
1507
+ return resolve(await startNextDev(runtimeEnv));
1508
+ }
1509
+ resolve({
1510
+ label: "Next.js dev server",
1511
+ code,
1512
+ signal
1629
1513
  });
1630
1514
  });
1631
- return { exitPromise, readyPromise };
1632
- };
1515
+ });
1633
1516
  try {
1634
1517
  while (!stopping) {
1635
1518
  envReloader.reload();
@@ -1638,84 +1521,59 @@ async function run(argv = process.argv) {
1638
1521
  const autoSpawnSchedulerMode = resolveAutoSpawnSchedulerMode(process.env);
1639
1522
  const queueStrategy = process.env.QUEUE_STRATEGY || "local";
1640
1523
  const schedulerCommand = lookupModuleCommand(getCliModules(), "scheduler", "start");
1641
- const nextRuntime = startNextDev(runtimeEnv);
1642
- const restartPromise = waitForDevRestart();
1643
- const backgroundStartAbort = new AbortController();
1644
- const cancelBackgroundStart = () => backgroundStartAbort.abort();
1645
- nextRuntime.exitPromise.finally(cancelBackgroundStart);
1646
- restartPromise.then(cancelBackgroundStart);
1647
- let backgroundExitResolve = () => void 0;
1648
- const backgroundExitPromise = new Promise((resolve) => {
1649
- backgroundExitResolve = resolve;
1650
- });
1651
1524
  const managedExitPromises = [
1652
- nextRuntime.exitPromise,
1653
- restartPromise,
1654
- backgroundExitPromise
1525
+ startNextDev(runtimeEnv),
1526
+ waitForEnvChange()
1655
1527
  ];
1656
- const startBackgroundServices = async () => {
1657
- if (stopping || backgroundStartAbort.signal.aborted) return;
1658
- const warmupReady = await waitForDevWarmupReadyFile(process.env.OM_DEV_WARMUP_READY_FILE, {
1659
- timeoutMs: resolveDevWarmupReadyTimeoutMs(process.env),
1660
- signal: backgroundStartAbort.signal
1661
- });
1662
- if (warmupReady === "aborted" || stopping || backgroundStartAbort.signal.aborted) return;
1663
- if (warmupReady === "timeout") {
1664
- console.warn("[server] Timed out waiting for dev warmup marker; starting background services anyway.");
1665
- }
1666
- if (autoSpawnWorkersMode !== "off") {
1667
- const discoveredWorkers = getRegisteredCliWorkers();
1668
- const discoveredWorkerQueues = [...new Set(discoveredWorkers.map((worker) => worker.queue))];
1669
- if (discoveredWorkerQueues.length === 0) {
1670
- console.error("[server] AUTO_SPAWN_WORKERS is enabled, but no queues were discovered from CLI modules. Run `yarn generate` and verify `.mercato/generated/modules.cli.generated.ts` contains worker entries. Continuing without auto-spawned workers.");
1671
- } else if (autoSpawnWorkersMode === "lazy") {
1672
- console.log(`[server] Lazy worker auto-spawn enabled \u2014 workers will start on first job (${discoveredWorkerQueues.length} queue(s) watched).`);
1673
- activeLazySupervisor = startLazyWorkerSupervisor({
1674
- mercatoBin,
1675
- appDir,
1676
- runtimeEnv,
1677
- workers: discoveredWorkers,
1678
- pollMs: resolveLazyPollMs(process.env),
1679
- restartOnUnexpectedExit: resolveLazyRestart(process.env)
1680
- });
1681
- } else {
1682
- console.log("[server] Eager worker auto-spawn enabled - starting workers for all queues...");
1683
- const workerProcess = spawn("node", [mercatoBin, "queue", "worker", "--all"], {
1684
- stdio: "inherit",
1685
- env: runtimeEnv,
1686
- cwd: appDir
1687
- });
1688
- processes.push(workerProcess);
1689
- waitForManagedProcessExit(workerProcess, formatQueueWorkerLabel(discoveredWorkerQueues)).then(backgroundExitResolve);
1690
- }
1528
+ if (autoSpawnWorkersMode !== "off") {
1529
+ const discoveredWorkers = getRegisteredCliWorkers();
1530
+ const discoveredWorkerQueues = [...new Set(discoveredWorkers.map((worker) => worker.queue))];
1531
+ if (discoveredWorkerQueues.length === 0) {
1532
+ console.error("[server] AUTO_SPAWN_WORKERS is enabled, but no queues were discovered from CLI modules. Run `yarn generate` and verify `.mercato/generated/modules.cli.generated.ts` contains worker entries. Continuing without auto-spawned workers.");
1533
+ } else if (autoSpawnWorkersMode === "lazy") {
1534
+ console.log(`[server] Lazy worker auto-spawn enabled \u2014 workers will start on first job (${discoveredWorkerQueues.length} queue(s) watched).`);
1535
+ activeLazySupervisor = startLazyWorkerSupervisor({
1536
+ mercatoBin,
1537
+ appDir,
1538
+ runtimeEnv,
1539
+ workers: discoveredWorkers,
1540
+ pollMs: resolveLazyPollMs(process.env),
1541
+ restartOnUnexpectedExit: resolveLazyRestart(process.env)
1542
+ });
1543
+ } else {
1544
+ console.log("[server] Eager worker auto-spawn enabled - starting workers for all queues...");
1545
+ const workerProcess = spawn("node", [mercatoBin, "queue", "worker", "--all"], {
1546
+ stdio: "inherit",
1547
+ env: runtimeEnv,
1548
+ cwd: appDir
1549
+ });
1550
+ processes.push(workerProcess);
1551
+ managedExitPromises.push(waitForManagedProcessExit(workerProcess, formatQueueWorkerLabel(discoveredWorkerQueues)));
1691
1552
  }
1692
- if (autoSpawnSchedulerMode !== "off" && queueStrategy === "local") {
1693
- if (schedulerCommand.status !== "ok") {
1694
- console.log(`[server] Skipping scheduler auto-start \u2014 ${describeMissingModuleCommand(schedulerCommand)}`);
1695
- } else if (autoSpawnSchedulerMode === "lazy") {
1696
- console.log("[server] Lazy scheduler auto-spawn enabled - scheduler will start when an enabled schedule exists.");
1697
- activeLazySchedulerSupervisor = startLazySchedulerSupervisor({
1698
- mercatoBin,
1699
- appDir,
1700
- runtimeEnv,
1701
- pollMs: resolveLazySchedulerPollMs(process.env),
1702
- restartOnUnexpectedExit: resolveLazySchedulerRestart(process.env)
1703
- });
1704
- } else {
1705
- console.log("[server] Eager scheduler auto-spawn enabled - starting scheduler polling engine...");
1706
- const schedulerProcess = spawn("node", [mercatoBin, "scheduler", "start"], {
1707
- stdio: "inherit",
1708
- env: runtimeEnv,
1709
- cwd: appDir
1710
- });
1711
- processes.push(schedulerProcess);
1712
- waitForManagedProcessExit(schedulerProcess, "Scheduler polling engine").then(backgroundExitResolve);
1713
- }
1553
+ }
1554
+ if (autoSpawnSchedulerMode !== "off" && queueStrategy === "local") {
1555
+ if (schedulerCommand.status !== "ok") {
1556
+ console.log(`[server] Skipping scheduler auto-start \u2014 ${describeMissingModuleCommand(schedulerCommand)}`);
1557
+ } else if (autoSpawnSchedulerMode === "lazy") {
1558
+ console.log("[server] Lazy scheduler auto-spawn enabled - scheduler will start when an enabled schedule exists.");
1559
+ activeLazySchedulerSupervisor = startLazySchedulerSupervisor({
1560
+ mercatoBin,
1561
+ appDir,
1562
+ runtimeEnv,
1563
+ pollMs: resolveLazySchedulerPollMs(process.env),
1564
+ restartOnUnexpectedExit: resolveLazySchedulerRestart(process.env)
1565
+ });
1566
+ } else {
1567
+ console.log("[server] Eager scheduler auto-spawn enabled - starting scheduler polling engine...");
1568
+ const schedulerProcess = spawn("node", [mercatoBin, "scheduler", "start"], {
1569
+ stdio: "inherit",
1570
+ env: runtimeEnv,
1571
+ cwd: appDir
1572
+ });
1573
+ processes.push(schedulerProcess);
1574
+ managedExitPromises.push(waitForManagedProcessExit(schedulerProcess, "Scheduler polling engine"));
1714
1575
  }
1715
- };
1716
- nextRuntime.readyPromise.then(() => {
1717
- void startBackgroundServices();
1718
- });
1576
+ }
1719
1577
  if (generateWatcherMode === "in-process") {
1720
1578
  console.log("[server] In-process generate watcher enabled \u2014 structural changes will regenerate without a sidecar process.");
1721
1579
  activeGenerateWatcher = startInProcessGenerateWatcher({
@@ -1736,14 +1594,10 @@ async function run(argv = process.argv) {
1736
1594
  console.log("[server] Legacy out-of-process generate watcher selected via OM_DEV_GENERATE_WATCH_MODE=legacy \u2014 expect the dev orchestrator to spawn `mercato generate watch --skip-initial`.");
1737
1595
  }
1738
1596
  const firstExit = await Promise.race(managedExitPromises);
1739
- if (isDevServerRestartResult(firstExit)) {
1740
- lastRestartReason = `${firstExit.label.toLowerCase()} (${path.basename(firstExit.filePath)})`;
1741
- writeDevSplashRuntimeRestarting(lastRestartReason);
1742
- }
1743
1597
  await cleanupAndWait();
1744
- devRestartPromiseResolve = null;
1598
+ envChangePromiseResolve = null;
1745
1599
  if (isDevServerRestartResult(firstExit)) {
1746
- console.log(`[server] Detected ${firstExit.label.toLowerCase()} (${path.basename(firstExit.filePath)}). Restarting app runtime...`);
1600
+ console.log(`[server] Detected environment file change (${path.basename(firstExit.filePath)}). Restarting app runtime...`);
1747
1601
  continue;
1748
1602
  }
1749
1603
  if (!isExpectedManagedExit(firstExit, { stopping })) {