@mandujs/core 0.52.0 → 0.53.1

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.
Files changed (109) hide show
  1. package/package.json +38 -2
  2. package/src/a11y/run-audit.ts +6 -6
  3. package/src/brain/adapters/oauth-flow.ts +1 -1
  4. package/src/brain/architecture/analyzer.ts +0 -2
  5. package/src/brain/credentials.ts +2 -2
  6. package/src/bundler/__tests__/build-runner.ts +13 -1
  7. package/src/bundler/__tests__/extended-watch.test.ts +3 -2
  8. package/src/bundler/__tests__/fast-refresh.test.ts +10 -12
  9. package/src/bundler/build.test.ts +30 -33
  10. package/src/bundler/build.ts +2 -1
  11. package/src/bundler/dev.ts +23 -22
  12. package/src/bundler/prerender.ts +63 -0
  13. package/src/bundler/safe-build.test.ts +2 -1
  14. package/src/bundler/types.ts +5 -3
  15. package/src/change/history.ts +1 -1
  16. package/src/change/snapshot.ts +18 -9
  17. package/src/client/Link.tsx +9 -9
  18. package/src/client/globals.ts +3 -2
  19. package/src/client/hooks.ts +0 -4
  20. package/src/client/hydrate.ts +1 -1
  21. package/src/client/router.ts +12 -12
  22. package/src/client/rpc.ts +1 -1
  23. package/src/client/use-fetch.ts +6 -6
  24. package/src/client/use-sse.ts +5 -5
  25. package/src/config/mcp-status.ts +0 -1
  26. package/src/config/validate.ts +1 -1
  27. package/src/config/watcher.ts +1 -1
  28. package/src/content/collection.ts +8 -12
  29. package/src/content/content-layer.ts +5 -5
  30. package/src/content/data-store.ts +2 -2
  31. package/src/content/loader-context.ts +0 -1
  32. package/src/content/loaders/file.ts +1 -1
  33. package/src/content/loaders/glob.ts +4 -4
  34. package/src/content/loaders/types.ts +1 -1
  35. package/src/content/meta-store.ts +1 -1
  36. package/src/content/types.ts +1 -1
  37. package/src/contract/define.ts +11 -12
  38. package/src/contract/index.ts +3 -3
  39. package/src/contract/protection.ts +2 -2
  40. package/src/contract/types.ts +1 -1
  41. package/src/contract/validator.ts +0 -1
  42. package/src/db/__tests__/db.test.ts +4 -7
  43. package/src/db/migrations/index.ts +3 -0
  44. package/src/db/migrations/lock.ts +11 -11
  45. package/src/deploy/cache.ts +1 -0
  46. package/src/design/__tests__/agents-link.test.ts +109 -0
  47. package/src/design/__tests__/lint.test.ts +110 -0
  48. package/src/design/agents-link.ts +165 -0
  49. package/src/design/extract.ts +2 -1
  50. package/src/design/index.ts +16 -0
  51. package/src/design/lint.ts +209 -0
  52. package/src/design/patch.ts +0 -1
  53. package/src/devtools/ai/mcp-connector.ts +24 -24
  54. package/src/devtools/client/components/kitchen-root.tsx +1 -1
  55. package/src/devtools/client/components/panel/errors-panel.tsx +1 -1
  56. package/src/devtools/client/state-manager.ts +1 -1
  57. package/src/devtools/init.ts +2 -3
  58. package/src/devtools/server/source-context.ts +1 -1
  59. package/src/devtools/worker/redaction-worker.ts +8 -4
  60. package/src/devtools/worker/worker-manager.ts +4 -3
  61. package/src/error/classifier.ts +1 -1
  62. package/src/error/domains.ts +1 -1
  63. package/src/filling/sse.ts +5 -5
  64. package/src/generator/generate.ts +2 -2
  65. package/src/guard/analyzer.ts +1 -1
  66. package/src/guard/ast-analyzer.ts +7 -7
  67. package/src/guard/auto-correct.ts +2 -2
  68. package/src/guard/config-guard.ts +0 -2
  69. package/src/guard/contract-guard.ts +1 -1
  70. package/src/guard/decision-memory.ts +2 -2
  71. package/src/guard/design-inline-class.ts +2 -1
  72. package/src/guard/graph.ts +1 -1
  73. package/src/guard/healing.ts +36 -41
  74. package/src/guard/negotiation.ts +3 -3
  75. package/src/guard/reporter.ts +1 -4
  76. package/src/guard/semantic-slots.ts +2 -2
  77. package/src/guard/suggestions.ts +2 -2
  78. package/src/guard/watcher.ts +5 -4
  79. package/src/index.ts +5 -5
  80. package/src/intent/index.ts +1 -1
  81. package/src/kitchen/api/contract-api.ts +1 -1
  82. package/src/kitchen/api/file-api.ts +3 -3
  83. package/src/kitchen/kitchen-handler.ts +29 -21
  84. package/src/lockfile/validate.ts +0 -1
  85. package/src/openapi/generator.ts +2 -1
  86. package/src/openapi/openapi.test.ts +43 -14
  87. package/src/perf/user-marks.ts +1 -1
  88. package/src/plugins/registry.ts +1 -2
  89. package/src/plugins/types.ts +1 -1
  90. package/src/resource/ddl/diff.ts +0 -1
  91. package/src/resource/generator.ts +1 -1
  92. package/src/resource/generators/contract.ts +1 -1
  93. package/src/resource/schema.ts +2 -1
  94. package/src/router/fs-routes.ts +6 -6
  95. package/src/router/fs-scanner.ts +1 -1
  96. package/src/runtime/env.ts +1 -1
  97. package/src/runtime/image-handler.ts +2 -1
  98. package/src/runtime/middleware.ts +2 -1
  99. package/src/runtime/server.ts +67 -60
  100. package/src/runtime/ssr.ts +3 -3
  101. package/src/runtime/streaming-ssr.ts +25 -27
  102. package/src/scheduler/validate.ts +1 -1
  103. package/src/seo/integration/ssr.ts +2 -3
  104. package/src/seo/render/opengraph.ts +1 -1
  105. package/src/seo/resolve/index.ts +0 -4
  106. package/src/slot/corrector.ts +1 -1
  107. package/src/slot/validator.ts +4 -4
  108. package/src/watcher/reporter.ts +1 -1
  109. package/src/watcher/watcher.ts +19 -19
@@ -6,7 +6,7 @@
6
6
  * Prevents main thread blocking during large payload processing
7
7
  */
8
8
 
9
- import type { RedactPattern, WorkerTask } from '../types';
9
+ import type { RedactPattern } from '../types';
10
10
 
11
11
  // ============================================================================
12
12
  // Worker Message Types
@@ -206,14 +206,18 @@ function handleMessage(request: WorkerRequest): WorkerResponse {
206
206
  // Worker 컨텍스트에서만 실행
207
207
  interface WorkerSelf {
208
208
  postMessage: (message: WorkerResponse) => void;
209
- onmessage: ((event: MessageEvent<WorkerRequest>) => void) | null;
209
+ addEventListener: (
210
+ type: 'message',
211
+ listener: (event: MessageEvent<WorkerRequest>) => void
212
+ ) => void;
210
213
  }
211
214
  const workerSelf = typeof self !== 'undefined' ? (self as unknown as WorkerSelf) : undefined;
212
215
  if (workerSelf && typeof workerSelf.postMessage === 'function') {
213
- workerSelf.onmessage = (event: MessageEvent<WorkerRequest>) => {
216
+ workerSelf.addEventListener('message', (event: MessageEvent<WorkerRequest>) => {
214
217
  const response = handleMessage(event.data);
218
+ // oxlint-disable-next-line require-post-message-target-origin -- Worker.postMessage has no targetOrigin parameter.
215
219
  workerSelf.postMessage(response);
216
- };
220
+ });
217
221
  }
218
222
 
219
223
  // ============================================================================
@@ -88,8 +88,8 @@ export class WorkerManager {
88
88
  }
89
89
 
90
90
  // 메시지 핸들러 설정
91
- this.worker.onmessage = this.handleWorkerMessage.bind(this);
92
- this.worker.onerror = this.handleWorkerError.bind(this);
91
+ this.worker.addEventListener('message', this.handleWorkerMessage.bind(this));
92
+ this.worker.addEventListener('error', this.handleWorkerError.bind(this));
93
93
 
94
94
  // Ping 테스트
95
95
  const pingResult = await this.sendRequest({ type: 'ping', data: {} });
@@ -117,7 +117,7 @@ export class WorkerManager {
117
117
  }
118
118
 
119
119
  // 대기 중인 요청 모두 reject
120
- for (const [id, pending] of this.pendingRequests) {
120
+ for (const [, pending] of this.pendingRequests) {
121
121
  clearTimeout(pending.timeout);
122
122
  pending.reject(new Error('Worker terminated'));
123
123
  }
@@ -323,6 +323,7 @@ export class WorkerManager {
323
323
 
324
324
  this.pendingRequests.set(id, { resolve, reject, timeout });
325
325
 
326
+ // oxlint-disable-next-line require-post-message-target-origin -- Worker.postMessage has no targetOrigin parameter.
326
327
  this.worker!.postMessage({ id, ...request });
327
328
  });
328
329
  }
@@ -54,7 +54,7 @@ export class ErrorClassifier {
54
54
  /**
55
55
  * ValidationError 처리
56
56
  */
57
- private createValidationError(error: { errors: unknown[] }): ManduError {
57
+ private createValidationError(_error: { errors: unknown[] }): ManduError {
58
58
  const slotFile = this.findSlotFile();
59
59
 
60
60
  return {
@@ -5,7 +5,7 @@
5
5
  * try-catch에서 instanceof 체크 가능.
6
6
  */
7
7
 
8
- import { ErrorCode, ERROR_MESSAGES, ERROR_SUMMARIES } from "./types";
8
+ import { ErrorCode, ERROR_SUMMARIES } from "./types";
9
9
  import type { ManduError, RouteContext, ErrorType } from "./types";
10
10
 
11
11
  /**
@@ -45,11 +45,11 @@ export class SSEConnection {
45
45
  controller.enqueue(this.encoder.encode(chunk));
46
46
  }
47
47
  this.pending = [];
48
- },
49
- cancel: () => {
50
- this.close();
51
- },
52
- });
48
+ },
49
+ cancel: () => {
50
+ void this.close();
51
+ },
52
+ });
53
53
 
54
54
  const headers = new Headers(DEFAULT_SSE_HEADERS);
55
55
  if (options.headers) {
@@ -1,4 +1,4 @@
1
- import type { RoutesManifest, RouteSpec } from "../spec/schema";
1
+ import type { RoutesManifest } from "../spec/schema";
2
2
  import { generateApiHandler, generatePageComponent, generateSlotLogic } from "./templates";
3
3
  import { generateContractTypeGlue, generateContractTemplate, generateContractTypesIndex } from "./contract-glue";
4
4
  import { createHash } from "crypto";
@@ -102,7 +102,7 @@ export interface GeneratedMap {
102
102
  async function ensureDir(dirPath: string): Promise<void> {
103
103
  try {
104
104
  await fs.mkdir(dirPath, { recursive: true });
105
- } catch (error) {
105
+ } catch {
106
106
  // ignore if exists
107
107
  }
108
108
  }
@@ -63,7 +63,7 @@ const DEFAULT_IMPORT_PATTERN = /^import\s+(\w+)/;
63
63
  */
64
64
  export function extractImports(content: string): ImportInfo[] {
65
65
  const imports: ImportInfo[] = [];
66
- const lines = content.split("\n");
66
+ const _lines = content.split("\n");
67
67
 
68
68
  // Static imports
69
69
  let match: RegExpExecArray | null;
@@ -382,14 +382,14 @@ export function extractImportsAST(content: string): ImportInfo[] {
382
382
 
383
383
  let namedImports: string[] | undefined;
384
384
  let defaultImport: string | undefined;
385
- let namespaceImport: string | undefined;
386
- let isTypeOnly = false;
385
+ let _namespaceImport: string | undefined;
386
+ let _isTypeOnly = false;
387
387
 
388
388
  // Check for type-only import
389
389
  if (is("keyword", "type")) {
390
390
  consume("keyword", "type");
391
391
  skip();
392
- isTypeOnly = true;
392
+ _isTypeOnly = true;
393
393
  }
394
394
 
395
395
  // Side-effect import: import '...'
@@ -427,7 +427,7 @@ export function extractImportsAST(content: string): ImportInfo[] {
427
427
  consume("keyword", "as");
428
428
  skip();
429
429
  if (is("identifier")) {
430
- namespaceImport = consume("identifier")!.value;
430
+ _namespaceImport = consume("identifier")!.value;
431
431
  skip();
432
432
  }
433
433
  }
@@ -777,7 +777,7 @@ export interface ModuleAnalysis {
777
777
  /** Public API 여부 (index 파일) */
778
778
  isPublicAPI: boolean;
779
779
  /** 순수 타입 모듈 여부 */
780
- isTypeOnly: boolean;
780
+ _isTypeOnly: boolean;
781
781
  }
782
782
 
783
783
  /**
@@ -795,12 +795,12 @@ export function analyzeModuleAST(content: string, filePath: string): ModuleAnaly
795
795
  // 타입만 있는 모듈인지 확인
796
796
  const hasRuntimeExport = exports.some((e) => e.type !== "type");
797
797
  const hasRuntimeImport = imports.some((i) => !i.statement.includes("import type"));
798
- const isTypeOnly = !hasRuntimeExport && !hasRuntimeImport && exports.length > 0;
798
+ const _isTypeOnly = !hasRuntimeExport && !hasRuntimeImport && exports.length > 0;
799
799
 
800
800
  return {
801
801
  imports,
802
802
  exports,
803
803
  isPublicAPI,
804
- isTypeOnly,
804
+ _isTypeOnly,
805
805
  };
806
806
  }
@@ -4,7 +4,7 @@ import { GUARD_RULES } from "./rules";
4
4
  import { runGuardCheck } from "./check";
5
5
  import { generateRoutes } from "../generator/generate";
6
6
  import { beginChange, commitChange, rollbackChange } from "../change";
7
- import path from "path";
7
+
8
8
 
9
9
  export interface AutoCorrectStep {
10
10
  ruleId: string;
@@ -51,7 +51,7 @@ export async function runAutoCorrect(
51
51
  message: `Auto-correct: ${violations.length} violations`,
52
52
  autoGenerated: true,
53
53
  });
54
- } catch (error) {
54
+ } catch {
55
55
  // 이미 활성 트랜잭션이 있는 경우 그대로 진행 (사용자가 수동으로 시작한 경우)
56
56
  // 다른 오류는 트랜잭션 없이 진행
57
57
  }
@@ -12,8 +12,6 @@ import {
12
12
  validateLockfile,
13
13
  validateWithPolicy,
14
14
  detectMode,
15
- type ManduLockfile,
16
- type LockfileValidationResult,
17
15
  type LockfileMode,
18
16
  } from "../lockfile";
19
17
  import type { ConfigDiff } from "../utils/differ";
@@ -79,7 +79,7 @@ export interface ContractViolation extends GuardViolation {
79
79
  */
80
80
  export async function checkMissingContract(
81
81
  manifest: RoutesManifest,
82
- rootDir: string
82
+ _rootDir: string
83
83
  ): Promise<ContractViolation[]> {
84
84
  const violations: ContractViolation[] = [];
85
85
 
@@ -25,8 +25,8 @@
25
25
  * ```
26
26
  */
27
27
 
28
- import { join, basename, extname } from "path";
29
- import { mkdir, readdir, readFile, writeFile, stat } from "fs/promises";
28
+ import { join, extname } from "path";
29
+ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
30
30
 
31
31
  // ═══════════════════════════════════════════════════════════════════════════
32
32
  // Types
@@ -1,3 +1,4 @@
1
+ import type * as __ManduNodeFsTypes0 from "node:fs";
1
2
  /**
2
3
  * Guard rule — `DESIGN_INLINE_CLASS` (Issue #245).
3
4
  *
@@ -92,7 +93,7 @@ async function pathExists(p: string): Promise<boolean> {
92
93
  }
93
94
 
94
95
  async function* walk(rootDir: string): AsyncIterable<string> {
95
- let entries: import("node:fs").Dirent[];
96
+ let entries: __ManduNodeFsTypes0.Dirent[];
96
97
  try {
97
98
  entries = await fs.readdir(rootDir, { withFileTypes: true });
98
99
  } catch {
@@ -34,7 +34,7 @@
34
34
 
35
35
  import { existsSync } from "fs";
36
36
  import { relative, resolve, sep, isAbsolute, dirname, extname } from "path";
37
- import { minimatch } from "minimatch";
37
+
38
38
  import type {
39
39
  GuardConfig,
40
40
  LayerDefinition,
@@ -22,18 +22,16 @@
22
22
  * ```
23
23
  */
24
24
 
25
- import { readFile, writeFile, mkdir, rename, unlink } from "fs/promises";
26
- import { existsSync } from "fs";
27
- import { dirname, join, relative, basename, extname, resolve, normalize } from "path";
28
- import type {
29
- Violation,
30
- ViolationType,
31
- GuardConfig,
32
- GuardPreset,
33
- LayerDefinition,
34
- ViolationReport,
35
- } from "./types";
36
- import { generateSmartSuggestions, getDocumentationLink } from "./suggestions";
25
+ import { readFile, writeFile, mkdir, unlink } from "fs/promises";
26
+ import { existsSync } from "fs";
27
+ import { dirname, join, extname, resolve, normalize } from "path";
28
+ import type {
29
+ Violation,
30
+ ViolationType,
31
+ GuardConfig,
32
+ GuardPreset,
33
+ } from "./types";
34
+ import { getDocumentationLink } from "./suggestions";
37
35
  import { checkDirectory } from "./watcher";
38
36
 
39
37
  // ═══════════════════════════════════════════════════════════════════════════
@@ -326,14 +324,13 @@ function generateHealingOptions(
326
324
  /**
327
325
  * 레이어 위반 옵션 생성
328
326
  */
329
- function generateLayerViolationOptions(
330
- violation: Violation,
331
- config: GuardConfig,
332
- rootDir: string
333
- ): HealingOption[] {
334
- const options: HealingOption[] = [];
335
- const { filePath, importPath, importStatement, fromLayer, toLayer, allowedLayers } =
336
- violation;
327
+ function generateLayerViolationOptions(
328
+ violation: Violation,
329
+ _config: GuardConfig,
330
+ rootDir: string
331
+ ): HealingOption[] {
332
+ const options: HealingOption[] = [];
333
+ const { filePath, importPath, importStatement, toLayer, allowedLayers } = violation;
337
334
 
338
335
  const targetModule = extractModuleName(importPath);
339
336
 
@@ -409,17 +406,15 @@ function generateLayerViolationOptions(
409
406
  /**
410
407
  * 순환 의존 옵션 생성
411
408
  */
412
- function generateCircularDependencyOptions(
413
- violation: Violation,
414
- config: GuardConfig,
415
- rootDir: string
416
- ): HealingOption[] {
417
- const options: HealingOption[] = [];
418
- const { fromLayer, toLayer, importPath } = violation;
419
-
420
- const targetModule = extractModuleName(importPath);
421
-
422
- // 옵션 1: 공통 코드를 shared로 추출
409
+ function generateCircularDependencyOptions(
410
+ violation: Violation,
411
+ _config: GuardConfig,
412
+ _rootDir: string
413
+ ): HealingOption[] {
414
+ const options: HealingOption[] = [];
415
+ const { fromLayer, toLayer } = violation;
416
+
417
+ // 옵션 1: 공통 코드를 shared로 추출
423
418
  options.push({
424
419
  label: "공통 코드를 shared로 추출",
425
420
  explanation: `${fromLayer}와 ${toLayer}가 공유하는 코드를 shared 레이어로 이동하세요.`,
@@ -446,11 +441,11 @@ function generateCircularDependencyOptions(
446
441
  /**
447
442
  * Cross-slice 옵션 생성
448
443
  */
449
- function generateCrossSliceOptions(
450
- violation: Violation,
451
- config: GuardConfig,
452
- rootDir: string
453
- ): HealingOption[] {
444
+ function generateCrossSliceOptions(
445
+ violation: Violation,
446
+ _config: GuardConfig,
447
+ _rootDir: string
448
+ ): HealingOption[] {
454
449
  const options: HealingOption[] = [];
455
450
  const { fromLayer, importPath } = violation;
456
451
 
@@ -483,11 +478,11 @@ function generateCrossSliceOptions(
483
478
  /**
484
479
  * 깊은 중첩 옵션 생성
485
480
  */
486
- function generateDeepNestingOptions(
487
- violation: Violation,
488
- config: GuardConfig,
489
- rootDir: string
490
- ): HealingOption[] {
481
+ function generateDeepNestingOptions(
482
+ violation: Violation,
483
+ _config: GuardConfig,
484
+ _rootDir: string
485
+ ): HealingOption[] {
491
486
  const options: HealingOption[] = [];
492
487
  const { filePath, importPath, importStatement } = violation;
493
488
 
@@ -23,7 +23,7 @@
23
23
 
24
24
  import { join, dirname } from "path";
25
25
  import { mkdir, writeFile, readdir, stat } from "fs/promises";
26
- import { searchDecisions, type ArchitectureDecision } from "./decision-memory";
26
+ import { searchDecisions } from "./decision-memory";
27
27
  import { getPreset, type GuardPreset, type PresetDefinition } from "./presets";
28
28
 
29
29
  // ═══════════════════════════════════════════════════════════════════════════
@@ -1176,10 +1176,10 @@ export async function negotiate(
1176
1176
  ): Promise<NegotiationResponse> {
1177
1177
  const {
1178
1178
  intent,
1179
- requirements = [],
1179
+ requirements: _requirements = [],
1180
1180
  constraints = [],
1181
1181
  preset = "mandu",
1182
- context,
1182
+ context: _context,
1183
1183
  } = request;
1184
1184
 
1185
1185
  // 1. 카테고리 감지
@@ -8,13 +8,10 @@ import type {
8
8
  Violation,
9
9
  ViolationReport,
10
10
  Severity,
11
- LayerDefinition,
12
11
  GuardPreset,
13
12
  } from "./types";
14
13
  import {
15
- getDocumentationLink,
16
14
  toAgentFormat,
17
- type AgentViolationFormat,
18
15
  } from "./suggestions";
19
16
 
20
17
  // ═══════════════════════════════════════════════════════════════════════════
@@ -89,7 +86,7 @@ export function formatViolation(
89
86
  ): string {
90
87
  const icon = getSeverityIcon(violation.severity);
91
88
  const color = getSeverityColor(violation.severity);
92
- const label = getSeverityLabel(violation.severity);
89
+ const _label = getSeverityLabel(violation.severity);
93
90
 
94
91
  const lines: string[] = [
95
92
  "",
@@ -22,7 +22,7 @@
22
22
  */
23
23
 
24
24
  import { readFile } from "fs/promises";
25
- import { join, relative, normalize } from "path";
25
+
26
26
 
27
27
  // ═══════════════════════════════════════════════════════════════════════════
28
28
  // Types
@@ -460,7 +460,7 @@ function safeRegexTest(
460
460
  export async function validateSlotConstraints(
461
461
  filePath: string,
462
462
  constraints: SlotConstraints,
463
- rootDir?: string
463
+ _rootDir?: string
464
464
  ): Promise<SemanticSlotValidationResult> {
465
465
  const violations: ConstraintViolation[] = [];
466
466
  const suggestions: string[] = [];
@@ -80,7 +80,7 @@ interface SuggestionContext {
80
80
  * 스마트 제안 생성
81
81
  */
82
82
  export function generateSmartSuggestions(context: SuggestionContext): string[] {
83
- const { type, fromLayer, toLayer, importPath, allowedLayers, layers, preset, slice } = context;
83
+ const { type } = context;
84
84
  const suggestions: string[] = [];
85
85
 
86
86
  switch (type) {
@@ -153,7 +153,7 @@ function generateLayerViolationSuggestions(context: SuggestionContext): string[]
153
153
  * 순환 의존 제안 생성
154
154
  */
155
155
  function generateCircularDependencySuggestions(context: SuggestionContext): string[] {
156
- const { fromLayer, toLayer, importPath } = context;
156
+ const { fromLayer, toLayer } = context;
157
157
  const suggestions: string[] = [];
158
158
 
159
159
  suggestions.push(
@@ -1,3 +1,4 @@
1
+ import type * as __ManduGlobTypes0 from "glob";
1
2
  /**
2
3
  * Mandu Guard Watcher
3
4
  *
@@ -45,9 +46,9 @@ interface WatcherOptions {
45
46
 
46
47
  const analysisCache = new Map<string, FileAnalysis>();
47
48
 
48
- let globModulePromise: Promise<typeof import("glob")> | null = null;
49
+ let globModulePromise: Promise<typeof __ManduGlobTypes0> | null = null;
49
50
 
50
- async function getGlobModule(): Promise<typeof import("glob")> {
51
+ async function getGlobModule(): Promise<typeof __ManduGlobTypes0> {
51
52
  if (!globModulePromise) {
52
53
  globModulePromise = import("glob");
53
54
  }
@@ -93,7 +94,7 @@ export function createGuardWatcher(options: WatcherOptions): GuardWatcher {
93
94
 
94
95
  // 레이어 정의 가져오기
95
96
  const layers = resolveLayerDefinitions(config);
96
- const hierarchy = resolveHierarchy(config);
97
+ const _hierarchy = resolveHierarchy(config);
97
98
 
98
99
  // 설정 기본값 적용
99
100
  const srcDir = config.srcDir ?? DEFAULT_GUARD_CONFIG.srcDir;
@@ -272,7 +273,7 @@ export function createGuardWatcher(options: WatcherOptions): GuardWatcher {
272
273
 
273
274
  close(): void {
274
275
  if (watcher) {
275
- watcher.close();
276
+ void watcher.close();
276
277
  watcher = null;
277
278
  }
278
279
  clearAnalysisCache();
package/src/index.ts CHANGED
@@ -94,14 +94,14 @@ export {
94
94
  } from "./observability/tracing";
95
95
 
96
96
  // Consolidated Mandu namespace
97
- import { ManduFilling, ManduContext, ManduFillingFactory, createSSEConnection } from "./filling";
97
+ import { ManduFillingFactory, createSSEConnection } from "./filling";
98
98
  import { createContract, defineHandler, defineRoute, createClient, contractFetch, createClientContract, querySchema, bodySchema, apiError } from "./contract";
99
99
  import { defineContract, generateAllFromContract, generateOpenAPISpec } from "./contract/define";
100
- import { island, isIsland, type IslandComponent, type IslandHydrationStrategy, type ClientIslandDefinition, type CompiledClientIsland } from "./island";
101
- import { intent, isIntent, getIntentDocs, generateOpenAPIFromIntent } from "./intent";
100
+ import { island, isIsland } from "./island";
101
+ import { intent, isIntent } from "./intent";
102
102
  import { initializeHook, reportError, ManduDevTools, getStateManager } from "./devtools";
103
- import type { ContractDefinition, ContractInstance, ContractSchema } from "./contract";
104
- import type { ContractHandlers, ClientOptions } from "./contract";
103
+
104
+
105
105
 
106
106
  /**
107
107
  * Mandu - Unified Namespace
@@ -22,7 +22,7 @@
22
22
  * ```
23
23
  */
24
24
 
25
- import { z, type ZodType } from 'zod';
25
+ import { type ZodType } from 'zod';
26
26
  import { ManduFillingFactory, type ManduFilling } from '../filling/filling';
27
27
  import type { ManduContext } from '../filling/context';
28
28
  import { getZodTypeName, getZodObjectShape, getZodArrayElementType } from '../contract/zod-utils';
@@ -193,7 +193,7 @@ export class ContractPlaygroundAPI {
193
193
  }
194
194
 
195
195
  return Response.json({ valid: true });
196
- } catch (e) {
196
+ } catch {
197
197
  return Response.json(
198
198
  { error: "Invalid request body" },
199
199
  { status: 400 },
@@ -52,7 +52,7 @@ export class FileAPI {
52
52
  const language = detectLanguage(filePath);
53
53
 
54
54
  return Response.json({ filePath, content, language });
55
- } catch (error) {
55
+ } catch {
56
56
  return Response.json(
57
57
  { error: "Failed to read file" },
58
58
  { status: 500 },
@@ -84,7 +84,7 @@ export class FileAPI {
84
84
  try {
85
85
  const diff = await this.getFileDiff(filePath, resolved);
86
86
  return Response.json(diff);
87
- } catch (error) {
87
+ } catch {
88
88
  return Response.json(
89
89
  { error: "Failed to get diff" },
90
90
  { status: 500 },
@@ -100,7 +100,7 @@ export class FileAPI {
100
100
  try {
101
101
  const changes = await this.getGitStatus();
102
102
  return Response.json({ changes });
103
- } catch (error) {
103
+ } catch {
104
104
  return Response.json(
105
105
  { error: "Failed to get changes" },
106
106
  { status: 500 },
@@ -19,9 +19,17 @@ import { eventBus } from "../observability/event-bus";
19
19
  import fs from "fs/promises";
20
20
  import path from "path";
21
21
 
22
- export const KITCHEN_PREFIX = "/__kitchen";
23
-
24
- export interface KitchenOptions {
22
+ export const KITCHEN_PREFIX = "/__kitchen";
23
+
24
+ function reverseCopy<T>(items: readonly T[]): T[] {
25
+ const reversed: T[] = [];
26
+ for (let index = items.length - 1; index >= 0; index -= 1) {
27
+ reversed.push(items[index] as T);
28
+ }
29
+ return reversed;
30
+ }
31
+
32
+ export interface KitchenOptions {
25
33
  rootDir: string;
26
34
  manifest: RoutesManifest;
27
35
  guardConfig: GuardConfig | null;
@@ -74,9 +82,9 @@ export function recordRequest(entry: RequestEntry): void {
74
82
  if (recentRequests.length > MAX_REQUESTS) recentRequests.shift();
75
83
  }
76
84
 
77
- export function getRecentRequests(): RequestEntry[] {
78
- return [...recentRequests].reverse();
79
- }
85
+ export function getRecentRequests(): RequestEntry[] {
86
+ return reverseCopy(recentRequests);
87
+ }
80
88
 
81
89
  /** Parse a window string like "5m", "30s", "1h" into milliseconds. */
82
90
  function parseWindow(input: string): number {
@@ -322,11 +330,11 @@ export class KitchenHandler {
322
330
  // Requests API — recent HTTP events from eventBus (fallback: ring buffer)
323
331
  if (sub === "/api/requests" && req.method === "GET") {
324
332
  const url = new URL(req.url);
325
- const limit = Math.min(parseInt(url.searchParams.get("limit") || "100", 10) || 100, 500);
326
- const busEvents = eventBus.getRecent(limit, { type: "http" });
327
- if (busEvents.length > 0) {
328
- return Response.json({ requests: busEvents.slice().reverse() });
329
- }
333
+ const limit = Math.min(parseInt(url.searchParams.get("limit") || "100", 10) || 100, 500);
334
+ const busEvents = eventBus.getRecent(limit, { type: "http" });
335
+ if (busEvents.length > 0) {
336
+ return Response.json({ requests: reverseCopy(busEvents) });
337
+ }
330
338
  return Response.json({ requests: getRecentRequests().slice(0, limit) });
331
339
  }
332
340
 
@@ -343,11 +351,11 @@ export class KitchenHandler {
343
351
  // Activity API — recent MCP events from eventBus (fallback: activity.jsonl)
344
352
  if (sub === "/api/activity" && req.method === "GET") {
345
353
  const url = new URL(req.url);
346
- const limit = Math.min(parseInt(url.searchParams.get("limit") || "100", 10) || 100, 500);
347
- const busEvents = eventBus.getRecent(limit, { type: "mcp" });
348
- if (busEvents.length > 0) {
349
- return Response.json({ events: busEvents.slice().reverse() });
350
- }
354
+ const limit = Math.min(parseInt(url.searchParams.get("limit") || "100", 10) || 100, 500);
355
+ const busEvents = eventBus.getRecent(limit, { type: "mcp" });
356
+ if (busEvents.length > 0) {
357
+ return Response.json({ events: reverseCopy(busEvents) });
358
+ }
351
359
  const events = await this.readRecentActivity();
352
360
  return Response.json({ events });
353
361
  }
@@ -488,11 +496,11 @@ export class KitchenHandler {
488
496
  private async readRecentActivity(): Promise<unknown[]> {
489
497
  const logPath = path.join(this.options.rootDir, ".mandu", "activity.jsonl");
490
498
  try {
491
- const content = await fs.readFile(logPath, "utf-8");
492
- const lines = content.trim().split("\n").filter(Boolean);
493
- return lines.slice(-50).reverse().map((line) => {
494
- try { return JSON.parse(line); } catch { return null; }
495
- }).filter(Boolean);
499
+ const content = await fs.readFile(logPath, "utf-8");
500
+ const lines = content.trim().split("\n").filter(Boolean);
501
+ return reverseCopy(lines.slice(-50)).map((line) => {
502
+ try { return JSON.parse(line); } catch { return null; }
503
+ }).filter(Boolean);
496
504
  } catch { return []; }
497
505
  }
498
506
  }
@@ -12,7 +12,6 @@ import {
12
12
  type LockfileError,
13
13
  type LockfileWarning,
14
14
  type LockfileMode,
15
- type LockfilePolicyOptions,
16
15
  DEFAULT_POLICIES,
17
16
  LOCKFILE_SCHEMA_VERSION,
18
17
  BYPASS_ENV_VAR,
@@ -1,3 +1,4 @@
1
+ import type * as __ManduNodeCryptoTypes0 from "node:crypto";
1
2
  /**
2
3
  * Mandu OpenAPI Generator
3
4
  * Contract에서 OpenAPI 3.0 스펙 자동 생성
@@ -893,7 +894,7 @@ function sha256Hex(input: string): string {
893
894
  // path (it would turn `hoistSharedSchemas` into an unnecessarily async
894
895
  // cascade on the hot path).
895
896
  // eslint-disable-next-line @typescript-eslint/no-require-imports
896
- const nodeCrypto = require("node:crypto") as typeof import("node:crypto");
897
+ const nodeCrypto = require("node:crypto") as typeof __ManduNodeCryptoTypes0;
897
898
  return nodeCrypto.createHash("sha256").update(input).digest("hex");
898
899
  }
899
900