@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.
- package/package.json +38 -2
- package/src/a11y/run-audit.ts +6 -6
- package/src/brain/adapters/oauth-flow.ts +1 -1
- package/src/brain/architecture/analyzer.ts +0 -2
- package/src/brain/credentials.ts +2 -2
- package/src/bundler/__tests__/build-runner.ts +13 -1
- package/src/bundler/__tests__/extended-watch.test.ts +3 -2
- package/src/bundler/__tests__/fast-refresh.test.ts +10 -12
- package/src/bundler/build.test.ts +30 -33
- package/src/bundler/build.ts +2 -1
- package/src/bundler/dev.ts +23 -22
- package/src/bundler/prerender.ts +63 -0
- package/src/bundler/safe-build.test.ts +2 -1
- package/src/bundler/types.ts +5 -3
- package/src/change/history.ts +1 -1
- package/src/change/snapshot.ts +18 -9
- package/src/client/Link.tsx +9 -9
- package/src/client/globals.ts +3 -2
- package/src/client/hooks.ts +0 -4
- package/src/client/hydrate.ts +1 -1
- package/src/client/router.ts +12 -12
- package/src/client/rpc.ts +1 -1
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-sse.ts +5 -5
- package/src/config/mcp-status.ts +0 -1
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +1 -1
- package/src/content/collection.ts +8 -12
- package/src/content/content-layer.ts +5 -5
- package/src/content/data-store.ts +2 -2
- package/src/content/loader-context.ts +0 -1
- package/src/content/loaders/file.ts +1 -1
- package/src/content/loaders/glob.ts +4 -4
- package/src/content/loaders/types.ts +1 -1
- package/src/content/meta-store.ts +1 -1
- package/src/content/types.ts +1 -1
- package/src/contract/define.ts +11 -12
- package/src/contract/index.ts +3 -3
- package/src/contract/protection.ts +2 -2
- package/src/contract/types.ts +1 -1
- package/src/contract/validator.ts +0 -1
- package/src/db/__tests__/db.test.ts +4 -7
- package/src/db/migrations/index.ts +3 -0
- package/src/db/migrations/lock.ts +11 -11
- package/src/deploy/cache.ts +1 -0
- package/src/design/__tests__/agents-link.test.ts +109 -0
- package/src/design/__tests__/lint.test.ts +110 -0
- package/src/design/agents-link.ts +165 -0
- package/src/design/extract.ts +2 -1
- package/src/design/index.ts +16 -0
- package/src/design/lint.ts +209 -0
- package/src/design/patch.ts +0 -1
- package/src/devtools/ai/mcp-connector.ts +24 -24
- package/src/devtools/client/components/kitchen-root.tsx +1 -1
- package/src/devtools/client/components/panel/errors-panel.tsx +1 -1
- package/src/devtools/client/state-manager.ts +1 -1
- package/src/devtools/init.ts +2 -3
- package/src/devtools/server/source-context.ts +1 -1
- package/src/devtools/worker/redaction-worker.ts +8 -4
- package/src/devtools/worker/worker-manager.ts +4 -3
- package/src/error/classifier.ts +1 -1
- package/src/error/domains.ts +1 -1
- package/src/filling/sse.ts +5 -5
- package/src/generator/generate.ts +2 -2
- package/src/guard/analyzer.ts +1 -1
- package/src/guard/ast-analyzer.ts +7 -7
- package/src/guard/auto-correct.ts +2 -2
- package/src/guard/config-guard.ts +0 -2
- package/src/guard/contract-guard.ts +1 -1
- package/src/guard/decision-memory.ts +2 -2
- package/src/guard/design-inline-class.ts +2 -1
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -41
- package/src/guard/negotiation.ts +3 -3
- package/src/guard/reporter.ts +1 -4
- package/src/guard/semantic-slots.ts +2 -2
- package/src/guard/suggestions.ts +2 -2
- package/src/guard/watcher.ts +5 -4
- package/src/index.ts +5 -5
- package/src/intent/index.ts +1 -1
- package/src/kitchen/api/contract-api.ts +1 -1
- package/src/kitchen/api/file-api.ts +3 -3
- package/src/kitchen/kitchen-handler.ts +29 -21
- package/src/lockfile/validate.ts +0 -1
- package/src/openapi/generator.ts +2 -1
- package/src/openapi/openapi.test.ts +43 -14
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/registry.ts +1 -2
- package/src/plugins/types.ts +1 -1
- package/src/resource/ddl/diff.ts +0 -1
- package/src/resource/generator.ts +1 -1
- package/src/resource/generators/contract.ts +1 -1
- package/src/resource/schema.ts +2 -1
- package/src/router/fs-routes.ts +6 -6
- package/src/router/fs-scanner.ts +1 -1
- package/src/runtime/env.ts +1 -1
- package/src/runtime/image-handler.ts +2 -1
- package/src/runtime/middleware.ts +2 -1
- package/src/runtime/server.ts +67 -60
- package/src/runtime/ssr.ts +3 -3
- package/src/runtime/streaming-ssr.ts +25 -27
- package/src/scheduler/validate.ts +1 -1
- package/src/seo/integration/ssr.ts +2 -3
- package/src/seo/render/opengraph.ts +1 -1
- package/src/seo/resolve/index.ts +0 -4
- package/src/slot/corrector.ts +1 -1
- package/src/slot/validator.ts +4 -4
- package/src/watcher/reporter.ts +1 -1
- 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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
92
|
-
this.worker.
|
|
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 [
|
|
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
|
}
|
package/src/error/classifier.ts
CHANGED
|
@@ -54,7 +54,7 @@ export class ErrorClassifier {
|
|
|
54
54
|
/**
|
|
55
55
|
* ValidationError 처리
|
|
56
56
|
*/
|
|
57
|
-
private createValidationError(
|
|
57
|
+
private createValidationError(_error: { errors: unknown[] }): ManduError {
|
|
58
58
|
const slotFile = this.findSlotFile();
|
|
59
59
|
|
|
60
60
|
return {
|
package/src/error/domains.ts
CHANGED
package/src/filling/sse.ts
CHANGED
|
@@ -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
|
|
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
|
|
105
|
+
} catch {
|
|
106
106
|
// ignore if exists
|
|
107
107
|
}
|
|
108
108
|
}
|
package/src/guard/analyzer.ts
CHANGED
|
@@ -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
|
|
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
|
|
386
|
-
let
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
798
|
+
const _isTypeOnly = !hasRuntimeExport && !hasRuntimeImport && exports.length > 0;
|
|
799
799
|
|
|
800
800
|
return {
|
|
801
801
|
imports,
|
|
802
802
|
exports,
|
|
803
803
|
isPublicAPI,
|
|
804
|
-
|
|
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
|
-
|
|
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
|
|
54
|
+
} catch {
|
|
55
55
|
// 이미 활성 트랜잭션이 있는 경우 그대로 진행 (사용자가 수동으로 시작한 경우)
|
|
56
56
|
// 다른 오류는 트랜잭션 없이 진행
|
|
57
57
|
}
|
|
@@ -79,7 +79,7 @@ export interface ContractViolation extends GuardViolation {
|
|
|
79
79
|
*/
|
|
80
80
|
export async function checkMissingContract(
|
|
81
81
|
manifest: RoutesManifest,
|
|
82
|
-
|
|
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,
|
|
29
|
-
import { mkdir, readdir, readFile, writeFile
|
|
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:
|
|
96
|
+
let entries: __ManduNodeFsTypes0.Dirent[];
|
|
96
97
|
try {
|
|
97
98
|
entries = await fs.readdir(rootDir, { withFileTypes: true });
|
|
98
99
|
} catch {
|
package/src/guard/graph.ts
CHANGED
package/src/guard/healing.ts
CHANGED
|
@@ -22,18 +22,16 @@
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import { readFile, writeFile, mkdir,
|
|
26
|
-
import { existsSync } from "fs";
|
|
27
|
-
import { dirname, join,
|
|
28
|
-
import type {
|
|
29
|
-
Violation,
|
|
30
|
-
ViolationType,
|
|
31
|
-
GuardConfig,
|
|
32
|
-
GuardPreset,
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
332
|
-
rootDir: string
|
|
333
|
-
): HealingOption[] {
|
|
334
|
-
const options: HealingOption[] = [];
|
|
335
|
-
const { filePath, importPath, importStatement,
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
): HealingOption[] {
|
|
417
|
-
const options: HealingOption[] = [];
|
|
418
|
-
const { fromLayer, toLayer
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
452
|
-
|
|
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
|
-
|
|
489
|
-
|
|
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
|
|
package/src/guard/negotiation.ts
CHANGED
|
@@ -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
|
|
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. 카테고리 감지
|
package/src/guard/reporter.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
463
|
+
_rootDir?: string
|
|
464
464
|
): Promise<SemanticSlotValidationResult> {
|
|
465
465
|
const violations: ConstraintViolation[] = [];
|
|
466
466
|
const suggestions: string[] = [];
|
package/src/guard/suggestions.ts
CHANGED
|
@@ -80,7 +80,7 @@ interface SuggestionContext {
|
|
|
80
80
|
* 스마트 제안 생성
|
|
81
81
|
*/
|
|
82
82
|
export function generateSmartSuggestions(context: SuggestionContext): string[] {
|
|
83
|
-
const { type
|
|
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
|
|
156
|
+
const { fromLayer, toLayer } = context;
|
|
157
157
|
const suggestions: string[] = [];
|
|
158
158
|
|
|
159
159
|
suggestions.push(
|
package/src/guard/watcher.ts
CHANGED
|
@@ -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
|
|
49
|
+
let globModulePromise: Promise<typeof __ManduGlobTypes0> | null = null;
|
|
49
50
|
|
|
50
|
-
async function getGlobModule(): Promise<typeof
|
|
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
|
|
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 {
|
|
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
|
|
101
|
-
import { intent, isIntent
|
|
100
|
+
import { island, isIsland } from "./island";
|
|
101
|
+
import { intent, isIntent } from "./intent";
|
|
102
102
|
import { initializeHook, reportError, ManduDevTools, getStateManager } from "./devtools";
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
|
|
104
|
+
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
107
|
* Mandu - Unified Namespace
|
package/src/intent/index.ts
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import {
|
|
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';
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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)
|
|
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
|
}
|
package/src/lockfile/validate.ts
CHANGED
package/src/openapi/generator.ts
CHANGED
|
@@ -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
|
|
897
|
+
const nodeCrypto = require("node:crypto") as typeof __ManduNodeCryptoTypes0;
|
|
897
898
|
return nodeCrypto.createHash("sha256").update(input).digest("hex");
|
|
898
899
|
}
|
|
899
900
|
|