@mandujs/core 0.54.12 → 0.54.13
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 +9 -1
- package/scripts/postinstall-lock.ts +153 -153
- package/src/a11y/run-audit.ts +15 -15
- package/src/agent/__tests__/context.test.ts +49 -1
- package/src/agent/context.ts +535 -535
- package/src/agent/index.ts +6 -6
- package/src/agent/plan.ts +282 -282
- package/src/agent/repair.ts +171 -171
- package/src/agent/sync.ts +200 -200
- package/src/agent/types.ts +8 -0
- package/src/agent/verify.ts +100 -2
- package/src/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__tests__/build-runner.ts +5 -4
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +20 -20
- package/src/bundler/analyzer.ts +15 -15
- package/src/bundler/build.test.ts +66 -15
- package/src/bundler/build.ts +165 -103
- package/src/bundler/css.ts +42 -42
- package/src/bundler/manifest-schema.ts +21 -21
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- package/src/bundler/plugins/block-generated-imports.ts +13 -13
- package/src/bundler/types.ts +31 -31
- package/src/client/island.ts +79 -79
- package/src/config/validate.ts +1 -1
- package/src/contract/schema.ts +7 -0
- package/src/deploy/inference/context.ts +82 -82
- package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- package/src/error/formatter.ts +10 -1
- package/src/experimental/index.ts +10 -0
- package/src/filling/context.ts +17 -17
- package/src/filling/filling.ts +22 -1
- package/src/filling/index.ts +15 -1
- package/src/generator/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.ts +210 -210
- package/src/guard/check.ts +9 -9
- package/src/guard/config-guard.ts +13 -13
- package/src/guard/fs-routes-policy.ts +51 -51
- package/src/guard/index.ts +11 -11
- package/src/index.ts +0 -10
- package/src/internal/index.ts +25 -0
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +6 -6
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/emit.ts +146 -146
- package/src/resource/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +69 -33
- package/src/router/client-entry.ts +134 -74
- package/src/router/fs-routes.ts +24 -22
- package/src/router/fs-scanner.ts +21 -17
- package/src/router/fs-types.ts +8 -5
- package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +103 -103
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/image-feature.ts +15 -0
- package/src/runtime/observability-lifecycle.ts +290 -290
- package/src/runtime/page-render-response.ts +106 -106
- package/src/runtime/rate-limit.ts +231 -0
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/scheduler-lifecycle.ts +64 -0
- package/src/runtime/server.ts +27 -295
- package/src/runtime/ssr.ts +59 -59
- package/src/runtime/static-files.ts +289 -289
- package/src/runtime/streaming-ssr.ts +22 -22
- package/src/spec/schema.ts +4 -3
- package/src/watcher/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
import { afterEach, expect, test } from "bun:test";
|
|
2
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs";
|
|
3
|
-
import { tmpdir } from "os";
|
|
4
|
-
import { join } from "path";
|
|
5
|
-
import { hasRecentGenerateStamp } from "../watcher";
|
|
6
|
-
|
|
7
|
-
const tmpRoots: string[] = [];
|
|
8
|
-
|
|
9
|
-
function makeRoot(): string {
|
|
10
|
-
const root = mkdtempSync(join(tmpdir(), "mandu-watch-"));
|
|
11
|
-
tmpRoots.push(root);
|
|
12
|
-
return root;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
for (const root of tmpRoots.splice(0)) {
|
|
17
|
-
rmSync(root, { recursive: true, force: true });
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("hasRecentGenerateStamp suppresses generated events using the root stamp", () => {
|
|
22
|
-
const root = makeRoot();
|
|
23
|
-
const now = 1_700_000_000_000;
|
|
24
|
-
mkdirSync(join(root, ".mandu"), { recursive: true });
|
|
25
|
-
writeFileSync(join(root, ".mandu", "generate.stamp"), String(now - 500));
|
|
26
|
-
|
|
27
|
-
const generatedFile = join(
|
|
28
|
-
root,
|
|
29
|
-
".mandu",
|
|
30
|
-
"generated",
|
|
31
|
-
"server",
|
|
32
|
-
"repos",
|
|
33
|
-
"notification.repo.ts",
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
expect(hasRecentGenerateStamp(root, generatedFile, now)).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("hasRecentGenerateStamp does not suppress stale generated events", () => {
|
|
40
|
-
const root = makeRoot();
|
|
41
|
-
const now = 1_700_000_000_000;
|
|
42
|
-
mkdirSync(join(root, ".mandu"), { recursive: true });
|
|
43
|
-
writeFileSync(join(root, ".mandu", "generate.stamp"), String(now - 30_000));
|
|
44
|
-
|
|
45
|
-
const generatedFile = join(root, ".mandu", "generated", "server", "old.route.ts");
|
|
46
|
-
|
|
47
|
-
expect(hasRecentGenerateStamp(root, generatedFile, now)).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("hasRecentGenerateStamp does not suppress source files after generation", () => {
|
|
51
|
-
const root = makeRoot();
|
|
52
|
-
const now = 1_700_000_000_000;
|
|
53
|
-
mkdirSync(join(root, ".mandu"), { recursive: true });
|
|
54
|
-
writeFileSync(join(root, ".mandu", "generate.stamp"), String(now - 500));
|
|
55
|
-
|
|
56
|
-
const sourceFile = join(root, "app", "page.tsx");
|
|
57
|
-
|
|
58
|
-
expect(hasRecentGenerateStamp(root, sourceFile, now)).toBe(false);
|
|
59
|
-
});
|
|
1
|
+
import { afterEach, expect, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { hasRecentGenerateStamp } from "../watcher";
|
|
6
|
+
|
|
7
|
+
const tmpRoots: string[] = [];
|
|
8
|
+
|
|
9
|
+
function makeRoot(): string {
|
|
10
|
+
const root = mkdtempSync(join(tmpdir(), "mandu-watch-"));
|
|
11
|
+
tmpRoots.push(root);
|
|
12
|
+
return root;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
for (const root of tmpRoots.splice(0)) {
|
|
17
|
+
rmSync(root, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("hasRecentGenerateStamp suppresses generated events using the root stamp", () => {
|
|
22
|
+
const root = makeRoot();
|
|
23
|
+
const now = 1_700_000_000_000;
|
|
24
|
+
mkdirSync(join(root, ".mandu"), { recursive: true });
|
|
25
|
+
writeFileSync(join(root, ".mandu", "generate.stamp"), String(now - 500));
|
|
26
|
+
|
|
27
|
+
const generatedFile = join(
|
|
28
|
+
root,
|
|
29
|
+
".mandu",
|
|
30
|
+
"generated",
|
|
31
|
+
"server",
|
|
32
|
+
"repos",
|
|
33
|
+
"notification.repo.ts",
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
expect(hasRecentGenerateStamp(root, generatedFile, now)).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("hasRecentGenerateStamp does not suppress stale generated events", () => {
|
|
40
|
+
const root = makeRoot();
|
|
41
|
+
const now = 1_700_000_000_000;
|
|
42
|
+
mkdirSync(join(root, ".mandu"), { recursive: true });
|
|
43
|
+
writeFileSync(join(root, ".mandu", "generate.stamp"), String(now - 30_000));
|
|
44
|
+
|
|
45
|
+
const generatedFile = join(root, ".mandu", "generated", "server", "old.route.ts");
|
|
46
|
+
|
|
47
|
+
expect(hasRecentGenerateStamp(root, generatedFile, now)).toBe(false);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("hasRecentGenerateStamp does not suppress source files after generation", () => {
|
|
51
|
+
const root = makeRoot();
|
|
52
|
+
const now = 1_700_000_000_000;
|
|
53
|
+
mkdirSync(join(root, ".mandu"), { recursive: true });
|
|
54
|
+
writeFileSync(join(root, ".mandu", "generate.stamp"), String(now - 500));
|
|
55
|
+
|
|
56
|
+
const sourceFile = join(root, "app", "page.tsx");
|
|
57
|
+
|
|
58
|
+
expect(hasRecentGenerateStamp(root, sourceFile, now)).toBe(false);
|
|
59
|
+
});
|
package/src/watcher/watcher.ts
CHANGED
|
@@ -61,63 +61,63 @@ const DEFAULT_CONFIG: Partial<WatcherConfig> = {
|
|
|
61
61
|
* These cause EISDIR/ENOENT errors when file watchers try to access them.
|
|
62
62
|
* See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
|
|
63
63
|
*/
|
|
64
|
-
const WINDOWS_RESERVED_NAMES = new Set([
|
|
65
|
-
"CON", "PRN", "AUX", "NUL",
|
|
66
|
-
"COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
|
67
|
-
"LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
|
68
|
-
]);
|
|
69
|
-
|
|
70
|
-
const GENERATE_STAMP_SUPPRESS_MS = 10_000;
|
|
71
|
-
|
|
72
|
-
function readGenerateStamp(stampFile: string): number | null {
|
|
73
|
-
try {
|
|
74
|
-
const stamp = Number.parseInt(fs.readFileSync(stampFile, "utf-8"), 10);
|
|
75
|
-
return Number.isFinite(stamp) ? stamp : null;
|
|
76
|
-
} catch {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function isManduGeneratedPath(rootDir: string, filePath: string): boolean {
|
|
82
|
-
const relativePath = path.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
83
|
-
return relativePath === ".mandu/generated" || relativePath.startsWith(".mandu/generated/");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function hasRecentGenerateStamp(
|
|
87
|
-
rootDir: string,
|
|
88
|
-
filePath: string,
|
|
89
|
-
now: number = Date.now()
|
|
90
|
-
): boolean {
|
|
91
|
-
if (!isManduGeneratedPath(rootDir, filePath)) {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const candidates = new Set<string>([
|
|
96
|
-
path.join(rootDir, ".mandu", "generate.stamp"),
|
|
97
|
-
]);
|
|
98
|
-
const resolvedRoot = path.resolve(rootDir);
|
|
99
|
-
let stampDir = path.dirname(path.resolve(filePath));
|
|
100
|
-
|
|
101
|
-
while (stampDir !== path.dirname(stampDir)) {
|
|
102
|
-
candidates.add(path.join(stampDir, ".mandu", "generate.stamp"));
|
|
103
|
-
if (stampDir === resolvedRoot) break;
|
|
104
|
-
stampDir = path.dirname(stampDir);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
for (const stampFile of candidates) {
|
|
108
|
-
const stamp = readGenerateStamp(stampFile);
|
|
109
|
-
if (stamp === null) continue;
|
|
110
|
-
|
|
111
|
-
const ageMs = now - stamp;
|
|
112
|
-
if (ageMs >= 0 && ageMs < GENERATE_STAMP_SUPPRESS_MS) {
|
|
113
|
-
return true;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export class FileWatcher {
|
|
64
|
+
const WINDOWS_RESERVED_NAMES = new Set([
|
|
65
|
+
"CON", "PRN", "AUX", "NUL",
|
|
66
|
+
"COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
|
67
|
+
"LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
const GENERATE_STAMP_SUPPRESS_MS = 10_000;
|
|
71
|
+
|
|
72
|
+
function readGenerateStamp(stampFile: string): number | null {
|
|
73
|
+
try {
|
|
74
|
+
const stamp = Number.parseInt(fs.readFileSync(stampFile, "utf-8"), 10);
|
|
75
|
+
return Number.isFinite(stamp) ? stamp : null;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isManduGeneratedPath(rootDir: string, filePath: string): boolean {
|
|
82
|
+
const relativePath = path.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
83
|
+
return relativePath === ".mandu/generated" || relativePath.startsWith(".mandu/generated/");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function hasRecentGenerateStamp(
|
|
87
|
+
rootDir: string,
|
|
88
|
+
filePath: string,
|
|
89
|
+
now: number = Date.now()
|
|
90
|
+
): boolean {
|
|
91
|
+
if (!isManduGeneratedPath(rootDir, filePath)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const candidates = new Set<string>([
|
|
96
|
+
path.join(rootDir, ".mandu", "generate.stamp"),
|
|
97
|
+
]);
|
|
98
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
99
|
+
let stampDir = path.dirname(path.resolve(filePath));
|
|
100
|
+
|
|
101
|
+
while (stampDir !== path.dirname(stampDir)) {
|
|
102
|
+
candidates.add(path.join(stampDir, ".mandu", "generate.stamp"));
|
|
103
|
+
if (stampDir === resolvedRoot) break;
|
|
104
|
+
stampDir = path.dirname(stampDir);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const stampFile of candidates) {
|
|
108
|
+
const stamp = readGenerateStamp(stampFile);
|
|
109
|
+
if (stamp === null) continue;
|
|
110
|
+
|
|
111
|
+
const ageMs = now - stamp;
|
|
112
|
+
if (ageMs >= 0 && ageMs < GENERATE_STAMP_SUPPRESS_MS) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export class FileWatcher {
|
|
121
121
|
private config: WatcherConfig;
|
|
122
122
|
private chokidarWatcher: FSWatcher | null = null;
|
|
123
123
|
private handlers: Set<WatchEventHandler> = new Set();
|
|
@@ -361,10 +361,10 @@ export class FileWatcher {
|
|
|
361
361
|
|
|
362
362
|
const { rootDir } = this.config;
|
|
363
363
|
|
|
364
|
-
// Cross-process: skip generated-file churn from a recent mandu generate.
|
|
365
|
-
if (hasRecentGenerateStamp(rootDir, filePath)) return;
|
|
366
|
-
|
|
367
|
-
// Validate file against rules
|
|
364
|
+
// Cross-process: skip generated-file churn from a recent mandu generate.
|
|
365
|
+
if (hasRecentGenerateStamp(rootDir, filePath)) return;
|
|
366
|
+
|
|
367
|
+
// Validate file against rules
|
|
368
368
|
try {
|
|
369
369
|
const warnings = await validateFile(filePath, event, rootDir);
|
|
370
370
|
|