@mandujs/core 0.9.12 → 0.9.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.9.12",
3
+ "version": "0.9.14",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -162,6 +162,8 @@ export interface WatchWarning {
162
162
  timestamp: Date;
163
163
  /** Event type that triggered the warning */
164
164
  event: "create" | "modify" | "delete";
165
+ /** Warning level */
166
+ level?: "info" | "warn";
165
167
  }
166
168
 
167
169
  /**
@@ -2,6 +2,7 @@ import type { RoutesManifest, RouteSpec } from "../spec/schema";
2
2
  import { generateApiHandler, generatePageComponent, generateSlotLogic } from "./templates";
3
3
  import { generateContractTypeGlue, generateContractTemplate, generateContractTypesIndex } from "./contract-glue";
4
4
  import { computeHash } from "../spec/lock";
5
+ import { getWatcher } from "../watcher/watcher";
5
6
  import path from "path";
6
7
  import fs from "fs/promises";
7
8
 
@@ -135,6 +136,10 @@ export async function generateRoutes(
135
136
  warnings: [],
136
137
  };
137
138
 
139
+ // Suppress watcher during generation to avoid false positives
140
+ const watcher = getWatcher();
141
+ watcher?.suppress();
142
+
138
143
  const serverRoutesDir = path.join(rootDir, "apps/server/generated/routes");
139
144
  const webRoutesDir = path.join(rootDir, "apps/web/generated/routes");
140
145
  const typesDir = path.join(rootDir, "apps/server/generated/types");
@@ -338,5 +343,8 @@ export async function generateRoutes(
338
343
  const mapPath = path.join(mapDir, "generated.map.json");
339
344
  await Bun.write(mapPath, JSON.stringify(generatedMap, null, 2));
340
345
 
346
+ // Resume watcher after generation
347
+ watcher?.resume();
348
+
341
349
  return result;
342
350
  }
@@ -63,6 +63,14 @@ export const MVP_RULES: ArchRule[] = [
63
63
  message: "Generated 파일에서 금지된 모듈이 import되었습니다.",
64
64
  forbiddenImports: ["fs", "child_process", "cluster", "worker_threads"],
65
65
  },
66
+ {
67
+ id: "SLOT_MODIFIED",
68
+ name: "Slot File Modified",
69
+ description: "Slot 파일이 수정되었습니다",
70
+ pattern: "spec/slots/*.slot.ts",
71
+ action: "warn",
72
+ message: "Slot 수정 감지. mandu_validate_slot 또는 mandu_guard_check로 검증하세요.",
73
+ },
66
74
  {
67
75
  id: "ISLAND_FIRST_MODIFIED",
68
76
  name: "Island-First ComponentModule Modified",
@@ -236,6 +244,18 @@ export async function validateFile(
236
244
  event,
237
245
  });
238
246
  }
247
+
248
+ // Slot modified: info level notification
249
+ if (rule.id === "SLOT_MODIFIED" && event !== "delete") {
250
+ warnings.push({
251
+ ruleId: rule.id,
252
+ file: relativePath,
253
+ message: rule.message,
254
+ timestamp: new Date(),
255
+ event,
256
+ level: "info",
257
+ });
258
+ }
239
259
  }
240
260
 
241
261
  return warnings;
@@ -21,7 +21,7 @@ import chokidar, { type FSWatcher } from "chokidar";
21
21
  */
22
22
  function formatWarning(warning: WatchWarning): string {
23
23
  const time = new Date().toLocaleTimeString("ko-KR", { hour12: false });
24
- const icon = warning.event === "delete" ? "[DEL]" : "[WARN]";
24
+ const icon = warning.level === "info" ? "[INFO]" : warning.event === "delete" ? "[DEL]" : "[WARN]";
25
25
  return `${time} ${icon} ${warning.ruleId}\n ${warning.file}\n ${warning.message}\n`;
26
26
  }
27
27
 
@@ -67,6 +67,7 @@ export class FileWatcher {
67
67
  private logFile: string | null = null;
68
68
  private logStream: fs.WriteStream | null = null;
69
69
  private tailProcess: ChildProcess | null = null;
70
+ private _suppressed: boolean = false;
70
71
 
71
72
  constructor(config: WatcherConfig) {
72
73
  this.config = {
@@ -105,8 +106,7 @@ export class FileWatcher {
105
106
  `${"=".repeat(50)}\n\n`
106
107
  );
107
108
 
108
- // Auto-open terminal window to tail the log
109
- this.openLogTerminal(this.logFile, rootDir);
109
+ // Terminal is now handled by ActivityMonitor in MCP server
110
110
 
111
111
  // Build sets for fast lookup
112
112
  const ignoredSet = new Set(ignoreDirs || []);
@@ -230,6 +230,20 @@ export class FileWatcher {
230
230
  this.recentWarnings = [];
231
231
  }
232
232
 
233
+ /**
234
+ * Suppress warnings (e.g. during generate)
235
+ */
236
+ suppress(): void {
237
+ this._suppressed = true;
238
+ }
239
+
240
+ /**
241
+ * Resume warnings after suppression
242
+ */
243
+ resume(): void {
244
+ this._suppressed = false;
245
+ }
246
+
233
247
  /**
234
248
  * Open a new terminal window tailing the log file
235
249
  */
@@ -267,6 +281,8 @@ export class FileWatcher {
267
281
  event: "create" | "modify" | "delete",
268
282
  filePath: string
269
283
  ): Promise<void> {
284
+ if (this._suppressed) return;
285
+
270
286
  const { rootDir } = this.config;
271
287
 
272
288
  // Validate file against rules