@open-mercato/cli 0.6.8-develop.6899.1.433952fd93 → 0.6.8-develop.6903.1.0ec850a22b
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/agentic/guides/module-facts.json +110 -110
- package/dist/agentic/guides/modules/ai_assistant.md +1 -1
- package/dist/agentic/guides/modules/api_docs.md +1 -1
- package/dist/agentic/guides/modules/api_keys.md +1 -1
- package/dist/agentic/guides/modules/attachments.md +1 -1
- package/dist/agentic/guides/modules/audit_logs.md +1 -1
- package/dist/agentic/guides/modules/auth.md +1 -1
- package/dist/agentic/guides/modules/business_rules.md +1 -1
- package/dist/agentic/guides/modules/catalog.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail.md +1 -1
- package/dist/agentic/guides/modules/channel_imap.md +1 -1
- package/dist/agentic/guides/modules/checkout.md +1 -1
- package/dist/agentic/guides/modules/communication_channels.md +1 -1
- package/dist/agentic/guides/modules/configs.md +1 -1
- package/dist/agentic/guides/modules/content.md +1 -1
- package/dist/agentic/guides/modules/currencies.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts.md +1 -1
- package/dist/agentic/guides/modules/customers.md +1 -1
- package/dist/agentic/guides/modules/dashboards.md +1 -1
- package/dist/agentic/guides/modules/data_sync.md +1 -1
- package/dist/agentic/guides/modules/design_system.md +1 -1
- package/dist/agentic/guides/modules/dictionaries.md +1 -1
- package/dist/agentic/guides/modules/directory.md +1 -1
- package/dist/agentic/guides/modules/entities.md +1 -1
- package/dist/agentic/guides/modules/events.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe.md +1 -1
- package/dist/agentic/guides/modules/generators.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops.md +1 -1
- package/dist/agentic/guides/modules/integrations.md +1 -1
- package/dist/agentic/guides/modules/messages.md +1 -1
- package/dist/agentic/guides/modules/notifications.md +1 -1
- package/dist/agentic/guides/modules/onboarding.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways.md +1 -1
- package/dist/agentic/guides/modules/perspectives.md +1 -1
- package/dist/agentic/guides/modules/planner.md +1 -1
- package/dist/agentic/guides/modules/portal.md +1 -1
- package/dist/agentic/guides/modules/progress.md +1 -1
- package/dist/agentic/guides/modules/query_index.md +1 -1
- package/dist/agentic/guides/modules/record_locks.md +1 -1
- package/dist/agentic/guides/modules/resources.md +1 -1
- package/dist/agentic/guides/modules/sales.md +1 -1
- package/dist/agentic/guides/modules/scheduler.md +1 -1
- package/dist/agentic/guides/modules/search.md +1 -1
- package/dist/agentic/guides/modules/security.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers.md +1 -1
- package/dist/agentic/guides/modules/sso.md +1 -1
- package/dist/agentic/guides/modules/staff.md +1 -1
- package/dist/agentic/guides/modules/storage_s3.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo.md +1 -1
- package/dist/agentic/guides/modules/sync_excel.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays.md +1 -1
- package/dist/agentic/guides/modules/translations.md +1 -1
- package/dist/agentic/guides/modules/webhooks.md +1 -1
- package/dist/agentic/guides/modules/wms.md +1 -1
- package/dist/agentic/guides/modules/workflows.md +1 -1
- package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +17 -0
- package/dist/agentic/guides/upstream/manifest.json +2 -2
- package/dist/agentic/shared/ai/harness/RELEASE.md +2 -0
- package/dist/lib/generate-watch-events.js +15 -0
- package/dist/lib/generate-watch-events.js.map +2 -2
- package/dist/lib/in-process-generate-watcher.js +12 -3
- package/dist/lib/in-process-generate-watcher.js.map +2 -2
- package/dist/mercato.js +3 -2
- package/dist/mercato.js.map +2 -2
- package/package.json +6 -6
- package/src/lib/__tests__/generate-watch-events.test.ts +76 -0
- package/src/lib/__tests__/in-process-generate-watcher.test.ts +52 -1
- package/src/lib/generate-watch-events.ts +14 -0
- package/src/lib/in-process-generate-watcher.ts +13 -4
- package/src/mercato.ts +5 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/generate-watch-events.ts"],
|
|
4
|
-
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { GenerateWatcherChangeSignal } from './in-process-generate-watcher'\n\nexport type GenerateWatchTarget = {\n directory: string\n recursive: boolean\n fileName?: string\n}\n\ntype WatchHandle = {\n close(): void\n}\n\ntype WatchDirectory = (\n target: GenerateWatchTarget,\n onChange: (fileName?: string) => void,\n onError: () => void,\n) => WatchHandle\n\nexport type GenerateWatchChangeSignalOptions = {\n getWatchTargets: () => Promise<GenerateWatchTarget[]> | GenerateWatchTarget[]\n watchDirectory?: WatchDirectory\n}\n\nfunction targetKey(target: GenerateWatchTarget): string {\n return JSON.stringify([\n path.resolve(target.directory),\n target.recursive,\n target.fileName ?? '',\n ])\n}\n\nconst defaultWatchDirectory: WatchDirectory = (target, onChange, onError) => {\n const watcher = fs.watch(\n target.directory,\n { recursive: target.recursive },\n (_eventType, fileName) => {\n const normalizedName = String(fileName ?? '')\n if (target.fileName && normalizedName && normalizedName !== target.fileName) return\n onChange(normalizedName || undefined)\n },\n )\n watcher.on('error', onError)\n return watcher\n}\n\nexport function createGenerateWatchChangeSignal(\n options: GenerateWatchChangeSignalOptions,\n): GenerateWatcherChangeSignal {\n const watchDirectory = options.watchDirectory ?? defaultWatchDirectory\n const watchers = new Map<string, WatchHandle>()\n let version = 0\n let pollingFallback = false\n let closed = false\n\n const closeWatchers = () => {\n for (const watcher of watchers.values()) {\n try { watcher.close() } catch {}\n }\n watchers.clear()\n }\n\n const enterPollingFallback = () => {\n if (pollingFallback) return\n pollingFallback = true\n version += 1\n closeWatchers()\n }\n\n return {\n currentVersion: () => version,\n usesPollingFallback: () => pollingFallback,\n refresh: async () => {\n if (closed || pollingFallback) return\n\n let targets: GenerateWatchTarget[]\n try {\n targets = await options.getWatchTargets()\n } catch {\n enterPollingFallback()\n return\n }\n\n const normalizedTargets = new Map<string, GenerateWatchTarget>()\n for (const target of targets) {\n const normalized: GenerateWatchTarget = {\n ...target,\n directory: path.resolve(target.directory),\n }\n normalizedTargets.set(targetKey(normalized), normalized)\n }\n\n for (const [key, watcher] of watchers) {\n if (normalizedTargets.has(key)) continue\n try { watcher.close() } catch {}\n watchers.delete(key)\n }\n\n for (const [key, target] of normalizedTargets) {\n if (watchers.has(key)) continue\n try {\n const watcher = watchDirectory(\n target,\n () => { version += 1 },\n enterPollingFallback,\n )\n if (closed || pollingFallback) {\n try { watcher.close() } catch {}\n continue\n }\n watchers.set(key, watcher)\n } catch {\n enterPollingFallback()\n return\n }\n }\n },\n close: () => {\n if (closed) return\n closed = true\n closeWatchers()\n },\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport type { GenerateWatcherChangeSignal } from './in-process-generate-watcher'\n\nexport type GenerateWatchTarget = {\n directory: string\n recursive: boolean\n fileName?: string\n}\n\ntype WatchHandle = {\n close(): void\n}\n\ntype WatchDirectory = (\n target: GenerateWatchTarget,\n onChange: (fileName?: string) => void,\n onError: () => void,\n) => WatchHandle\n\nexport type GenerateWatchChangeSignalOptions = {\n getWatchTargets: () => Promise<GenerateWatchTarget[]> | GenerateWatchTarget[]\n watchDirectory?: WatchDirectory\n directoryExists?: (directory: string) => boolean\n onSkippedDirectory?: (directory: string) => void\n}\n\nfunction targetKey(target: GenerateWatchTarget): string {\n return JSON.stringify([\n path.resolve(target.directory),\n target.recursive,\n target.fileName ?? '',\n ])\n}\n\nconst defaultWatchDirectory: WatchDirectory = (target, onChange, onError) => {\n const watcher = fs.watch(\n target.directory,\n { recursive: target.recursive },\n (_eventType, fileName) => {\n const normalizedName = String(fileName ?? '')\n if (target.fileName && normalizedName && normalizedName !== target.fileName) return\n onChange(normalizedName || undefined)\n },\n )\n watcher.on('error', onError)\n return watcher\n}\n\nexport function createGenerateWatchChangeSignal(\n options: GenerateWatchChangeSignalOptions,\n): GenerateWatcherChangeSignal {\n const watchDirectory = options.watchDirectory ?? defaultWatchDirectory\n const directoryExists = options.directoryExists ?? fs.existsSync\n const watchers = new Map<string, WatchHandle>()\n let skippedDirectories = new Set<string>()\n let version = 0\n let pollingFallback = false\n let closed = false\n\n const closeWatchers = () => {\n for (const watcher of watchers.values()) {\n try { watcher.close() } catch {}\n }\n watchers.clear()\n }\n\n const enterPollingFallback = () => {\n if (pollingFallback) return\n pollingFallback = true\n version += 1\n closeWatchers()\n }\n\n return {\n currentVersion: () => version,\n hasSkippedTargets: () => skippedDirectories.size > 0,\n usesPollingFallback: () => pollingFallback,\n refresh: async () => {\n if (closed || pollingFallback) return\n\n let targets: GenerateWatchTarget[]\n try {\n targets = await options.getWatchTargets()\n } catch {\n enterPollingFallback()\n return\n }\n\n const normalizedTargets = new Map<string, GenerateWatchTarget>()\n const nextSkippedDirectories = new Set<string>()\n for (const target of targets) {\n const normalized: GenerateWatchTarget = {\n ...target,\n directory: path.resolve(target.directory),\n }\n if (!directoryExists(normalized.directory)) {\n nextSkippedDirectories.add(normalized.directory)\n if (!skippedDirectories.has(normalized.directory)) {\n try { options.onSkippedDirectory?.(normalized.directory) } catch {}\n }\n continue\n }\n normalizedTargets.set(targetKey(normalized), normalized)\n }\n skippedDirectories = nextSkippedDirectories\n\n for (const [key, watcher] of watchers) {\n if (normalizedTargets.has(key)) continue\n try { watcher.close() } catch {}\n watchers.delete(key)\n }\n\n for (const [key, target] of normalizedTargets) {\n if (watchers.has(key)) continue\n try {\n const watcher = watchDirectory(\n target,\n () => { version += 1 },\n enterPollingFallback,\n )\n if (closed || pollingFallback) {\n try { watcher.close() } catch {}\n continue\n }\n watchers.set(key, watcher)\n } catch {\n enterPollingFallback()\n return\n }\n }\n },\n close: () => {\n if (closed) return\n closed = true\n closeWatchers()\n },\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -54,12 +54,21 @@ function startInProcessGenerateWatcher(options) {
|
|
|
54
54
|
if (stopping) return;
|
|
55
55
|
try {
|
|
56
56
|
const changeVersion = changeSignal?.currentVersion() ?? 0;
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const eventGatedIdle = Boolean(
|
|
58
|
+
changeSignal && !changeSignal.usesPollingFallback() && changeVersion === observedChangeVersion
|
|
59
|
+
);
|
|
60
|
+
let refreshedSkippedTargets = false;
|
|
61
|
+
if (eventGatedIdle) {
|
|
62
|
+
if (!changeSignal?.hasSkippedTargets?.()) return;
|
|
63
|
+
await changeSignal.refresh();
|
|
64
|
+
refreshedSkippedTargets = true;
|
|
65
|
+
if (changeSignal.hasSkippedTargets?.()) return;
|
|
59
66
|
}
|
|
60
67
|
const nextChecksum = await computeStructureChecksum();
|
|
61
68
|
observedChangeVersion = changeVersion;
|
|
62
|
-
|
|
69
|
+
if (!refreshedSkippedTargets) {
|
|
70
|
+
await changeSignal?.refresh();
|
|
71
|
+
}
|
|
63
72
|
if (nextChecksum !== previousChecksum) {
|
|
64
73
|
previousChecksum = nextChecksum;
|
|
65
74
|
await runOnce("structure change");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/in-process-generate-watcher.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * In-process generate watcher.\n *\n * Coalesces filesystem events on a configurable interval, then verifies them\n * with the existing structural fingerprint before invoking the generator.\n * If filesystem watching is unavailable, it falls back to fingerprint polling.\n * The polling loop is identical to the legacy standalone watcher; the only\n * difference is that it now runs inside whatever process calls it\n * (typically `mercato server dev`), so the dev runtime no longer needs a\n * sidecar `mercato generate watch` Node process.\n *\n * Contract preserved from the prior standalone watcher:\n * - Default debounce/fallback poll interval 1000 ms (minimum 250 ms).\n * - One initial generator run unless `skipInitial` is true.\n * - Concurrent regeneration requests are coalesced (running + pending).\n * - Generator errors are logged but never crash the watcher.\n * - The polling timer uses `.unref()` so it never blocks process exit.\n */\n\nexport type GenerateWatcherLogger = Pick<Console, 'log' | 'error'>\n\nexport type GenerateWatcherChangeSignal = {\n /** Monotonic event generation. Changes indicate that a checksum may be stale. */\n currentVersion(): number\n /** Refresh filesystem subscriptions after module configuration changes. */\n refresh(): Promise<void> | void\n /** When true, preserve the legacy full-checksum-on-every-poll behavior. */\n usesPollingFallback(): boolean\n /** Close filesystem subscriptions. Idempotent. */\n close(): Promise<void> | void\n}\n\nexport type GenerateWatcherOptions = {\n /**\n * Function that returns the current structural fingerprint of the module\n * tree. The watcher re-runs `runGenerators` whenever this value changes.\n */\n computeStructureChecksum: () => Promise<string> | string\n /** Optional event signal that avoids full checksum work during idle polls. */\n changeSignal?: GenerateWatcherChangeSignal\n /**\n * Function that performs the actual regeneration work. Called once on\n * startup (unless `skipInitial`) and again whenever the checksum changes.\n * The `reason` argument is suitable for logging (`'initial'`,\n * `'structure change'`, `'queued change'`).\n */\n runGenerators: (reason: string) => Promise<void>\n /** Poll interval in milliseconds. Defaults to 1000. Clamped to >= 250. */\n pollMs?: number\n /** Skip the initial regeneration on startup. Defaults to false. */\n skipInitial?: boolean\n /** Suppress informational logs. Errors are always logged. */\n quiet?: boolean\n /** Logger override. Defaults to `console`. */\n logger?: GenerateWatcherLogger\n}\n\nexport type GenerateWatcherHandle = {\n /** Resolves when the watcher loop has stopped (after `close()`). */\n readonly done: Promise<void>\n /** Stop the polling loop and resolve `done`. Idempotent. */\n close(): Promise<void>\n}\n\nconst MIN_POLL_MS = 250\nconst DEFAULT_POLL_MS = 1000\n\nfunction resolvePollMs(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return DEFAULT_POLL_MS\n const numeric = Math.floor(value)\n return numeric >= MIN_POLL_MS ? numeric : DEFAULT_POLL_MS\n}\n\n/**\n * Start an in-process generate watcher. The returned handle exposes a\n * `close()` to stop polling and a `done` promise that resolves once the\n * watcher loop has finished.\n */\nexport function startInProcessGenerateWatcher(\n options: GenerateWatcherOptions,\n): GenerateWatcherHandle {\n const logger = options.logger ?? console\n const quiet = options.quiet === true\n const pollMs = resolvePollMs(options.pollMs)\n const skipInitial = options.skipInitial === true\n const { changeSignal, computeStructureChecksum, runGenerators } = options\n\n let stopping = false\n let pollTimer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingReason: string | null = null\n let previousChecksum = ''\n let observedChangeVersion = -1\n let doneResolve: (() => void) | null = null\n const done = new Promise<void>((resolve) => {\n doneResolve = resolve\n })\n\n async function runOnce(reason: string): Promise<void> {\n if (running) {\n pendingReason = reason\n return\n }\n running = true\n try {\n if (!quiet) {\n logger.log(`[generate:watch] Regenerating (${reason})...`)\n }\n await runGenerators(reason)\n if (!quiet) {\n logger.log('[generate:watch] Generators completed.')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Generation failed: ${message}`)\n } finally {\n running = false\n if (pendingReason && !stopping) {\n const queued = pendingReason\n pendingReason = null\n await runOnce(queued)\n }\n }\n }\n\n function scheduleNext(): void {\n if (stopping) return\n pollTimer = setTimeout(() => {\n void (async () => {\n if (stopping) return\n try {\n const changeVersion = changeSignal?.currentVersion() ?? 0\n
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * In-process generate watcher.\n *\n * Coalesces filesystem events on a configurable interval, then verifies them\n * with the existing structural fingerprint before invoking the generator.\n * If filesystem watching is unavailable, it falls back to fingerprint polling.\n * The polling loop is identical to the legacy standalone watcher; the only\n * difference is that it now runs inside whatever process calls it\n * (typically `mercato server dev`), so the dev runtime no longer needs a\n * sidecar `mercato generate watch` Node process.\n *\n * Contract preserved from the prior standalone watcher:\n * - Default debounce/fallback poll interval 1000 ms (minimum 250 ms).\n * - One initial generator run unless `skipInitial` is true.\n * - Concurrent regeneration requests are coalesced (running + pending).\n * - Generator errors are logged but never crash the watcher.\n * - The polling timer uses `.unref()` so it never blocks process exit.\n */\n\nexport type GenerateWatcherLogger = Pick<Console, 'log' | 'error'>\n\nexport type GenerateWatcherChangeSignal = {\n /** Monotonic event generation. Changes indicate that a checksum may be stale. */\n currentVersion(): number\n /** Refresh filesystem subscriptions after module configuration changes. */\n refresh(): Promise<void> | void\n /** Whether configured roots are missing and should be retried on idle polls. */\n hasSkippedTargets?(): boolean\n /** When true, preserve the legacy full-checksum-on-every-poll behavior. */\n usesPollingFallback(): boolean\n /** Close filesystem subscriptions. Idempotent. */\n close(): Promise<void> | void\n}\n\nexport type GenerateWatcherOptions = {\n /**\n * Function that returns the current structural fingerprint of the module\n * tree. The watcher re-runs `runGenerators` whenever this value changes.\n */\n computeStructureChecksum: () => Promise<string> | string\n /** Optional event signal that avoids full checksum work during idle polls. */\n changeSignal?: GenerateWatcherChangeSignal\n /**\n * Function that performs the actual regeneration work. Called once on\n * startup (unless `skipInitial`) and again whenever the checksum changes.\n * The `reason` argument is suitable for logging (`'initial'`,\n * `'structure change'`, `'queued change'`).\n */\n runGenerators: (reason: string) => Promise<void>\n /** Poll interval in milliseconds. Defaults to 1000. Clamped to >= 250. */\n pollMs?: number\n /** Skip the initial regeneration on startup. Defaults to false. */\n skipInitial?: boolean\n /** Suppress informational logs. Errors are always logged. */\n quiet?: boolean\n /** Logger override. Defaults to `console`. */\n logger?: GenerateWatcherLogger\n}\n\nexport type GenerateWatcherHandle = {\n /** Resolves when the watcher loop has stopped (after `close()`). */\n readonly done: Promise<void>\n /** Stop the polling loop and resolve `done`. Idempotent. */\n close(): Promise<void>\n}\n\nconst MIN_POLL_MS = 250\nconst DEFAULT_POLL_MS = 1000\n\nfunction resolvePollMs(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return DEFAULT_POLL_MS\n const numeric = Math.floor(value)\n return numeric >= MIN_POLL_MS ? numeric : DEFAULT_POLL_MS\n}\n\n/**\n * Start an in-process generate watcher. The returned handle exposes a\n * `close()` to stop polling and a `done` promise that resolves once the\n * watcher loop has finished.\n */\nexport function startInProcessGenerateWatcher(\n options: GenerateWatcherOptions,\n): GenerateWatcherHandle {\n const logger = options.logger ?? console\n const quiet = options.quiet === true\n const pollMs = resolvePollMs(options.pollMs)\n const skipInitial = options.skipInitial === true\n const { changeSignal, computeStructureChecksum, runGenerators } = options\n\n let stopping = false\n let pollTimer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingReason: string | null = null\n let previousChecksum = ''\n let observedChangeVersion = -1\n let doneResolve: (() => void) | null = null\n const done = new Promise<void>((resolve) => {\n doneResolve = resolve\n })\n\n async function runOnce(reason: string): Promise<void> {\n if (running) {\n pendingReason = reason\n return\n }\n running = true\n try {\n if (!quiet) {\n logger.log(`[generate:watch] Regenerating (${reason})...`)\n }\n await runGenerators(reason)\n if (!quiet) {\n logger.log('[generate:watch] Generators completed.')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Generation failed: ${message}`)\n } finally {\n running = false\n if (pendingReason && !stopping) {\n const queued = pendingReason\n pendingReason = null\n await runOnce(queued)\n }\n }\n }\n\n function scheduleNext(): void {\n if (stopping) return\n pollTimer = setTimeout(() => {\n void (async () => {\n if (stopping) return\n try {\n const changeVersion = changeSignal?.currentVersion() ?? 0\n const eventGatedIdle = Boolean(\n changeSignal\n && !changeSignal.usesPollingFallback()\n && changeVersion === observedChangeVersion\n )\n let refreshedSkippedTargets = false\n if (eventGatedIdle) {\n if (!changeSignal?.hasSkippedTargets?.()) return\n await changeSignal.refresh()\n refreshedSkippedTargets = true\n if (changeSignal.hasSkippedTargets?.()) return\n }\n const nextChecksum = await computeStructureChecksum()\n observedChangeVersion = changeVersion\n if (!refreshedSkippedTargets) {\n await changeSignal?.refresh()\n }\n if (nextChecksum !== previousChecksum) {\n previousChecksum = nextChecksum\n await runOnce('structure change')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Poll cycle failed: ${message}`)\n } finally {\n if (!stopping) scheduleNext()\n }\n })()\n }, pollMs)\n pollTimer.unref?.()\n }\n\n void (async () => {\n try {\n await changeSignal?.refresh()\n const initialChangeVersion = changeSignal?.currentVersion() ?? 0\n if (!skipInitial) {\n await runOnce('initial')\n }\n previousChecksum = await computeStructureChecksum()\n observedChangeVersion = initialChangeVersion\n if (!quiet) {\n if (skipInitial) {\n logger.log('[generate:watch] Skipping initial regeneration and watching the current generated state.')\n }\n logger.log(`[generate:watch] Watching structural module files with a ${pollMs}ms debounce (in-process).`)\n }\n scheduleNext()\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Initial setup failed: ${message}`)\n // Even if the initial bootstrap fails, schedule polling so a later\n // checksum recovery still picks up the next change on disk.\n scheduleNext()\n }\n })()\n\n async function close(): Promise<void> {\n if (stopping) {\n await done\n return\n }\n stopping = true\n if (pollTimer) {\n clearTimeout(pollTimer)\n pollTimer = null\n }\n await changeSignal?.close()\n if (doneResolve) {\n doneResolve()\n doneResolve = null\n }\n }\n\n return {\n done,\n close,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/mercato.js
CHANGED
|
@@ -585,7 +585,7 @@ async function runGeneratorSuiteWithStructuralInvalidation(quiet) {
|
|
|
585
585
|
}
|
|
586
586
|
await runPostGenerateStructuralInvalidation(quiet);
|
|
587
587
|
}
|
|
588
|
-
async function createGenerateWatchRuntime() {
|
|
588
|
+
async function createGenerateWatchRuntime(quiet = false) {
|
|
589
589
|
const [
|
|
590
590
|
{ createResolver },
|
|
591
591
|
{ calculateGenerateWatchStructureChecksum },
|
|
@@ -614,6 +614,7 @@ async function createGenerateWatchRuntime() {
|
|
|
614
614
|
return calculateGenerateWatchStructureChecksum(collectWatchState());
|
|
615
615
|
},
|
|
616
616
|
changeSignal: createGenerateWatchChangeSignal({
|
|
617
|
+
onSkippedDirectory: quiet ? void 0 : (directory) => console.log(`[generate:watch] Skipping missing watch directory: ${directory}`),
|
|
617
618
|
getWatchTargets: () => {
|
|
618
619
|
const state = collectWatchState();
|
|
619
620
|
const targets = [{
|
|
@@ -1501,7 +1502,7 @@ async function run(argv = process.argv) {
|
|
|
1501
1502
|
const intervalArg = args.find((arg) => arg.startsWith("--interval="));
|
|
1502
1503
|
const parsedInterval = intervalArg ? Number.parseInt(intervalArg.split("=")[1] ?? "", 10) : NaN;
|
|
1503
1504
|
const intervalMs = Number.isFinite(parsedInterval) && parsedInterval >= 250 ? parsedInterval : 1e3;
|
|
1504
|
-
const generateWatchRuntime = await createGenerateWatchRuntime();
|
|
1505
|
+
const generateWatchRuntime = await createGenerateWatchRuntime(quiet);
|
|
1505
1506
|
const watcher = startInProcessGenerateWatcher({
|
|
1506
1507
|
pollMs: intervalMs,
|
|
1507
1508
|
skipInitial,
|