@mandujs/core 0.41.2 → 0.43.0
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 +21 -4
- package/src/auth/__tests__/login.test.ts +420 -419
- package/src/auth/__tests__/reset.test.ts +296 -296
- package/src/brain/adapters/anthropic-oauth.ts +421 -420
- package/src/brain/adapters/index.ts +2 -1
- package/src/brain/adapters/ollama.ts +1 -1
- package/src/brain/adapters/openai-oauth.ts +534 -533
- package/src/brain/brain.ts +2 -1
- package/src/brain/redactor.ts +196 -196
- package/src/bundler/__tests__/cli-bench-utils.test.ts +149 -149
- package/src/bundler/__tests__/cold-start.test.ts +504 -504
- package/src/bundler/__tests__/fast-refresh.test.ts +607 -606
- package/src/bundler/__tests__/hdr.test.ts +1 -1
- package/src/bundler/analyzer.ts +958 -958
- package/src/bundler/build.ts +104 -14
- package/src/bundler/dev.ts +125 -0
- package/src/bundler/hmr-types.ts +1 -0
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -0
- package/src/bundler/plugins/index.ts +14 -0
- package/src/bundler/plugins/react-compiler-lint.ts +253 -0
- package/src/bundler/plugins/react-compiler.ts +162 -0
- package/src/bundler/types.ts +12 -0
- package/src/change/integrity.ts +2 -1
- package/src/client/index.ts +10 -0
- package/src/client/island.ts +38 -11
- package/src/client/router.ts +6 -1
- package/src/config/mandu.ts +57 -0
- package/src/config/validate.ts +42 -0
- package/src/content/collection.ts +844 -809
- package/src/content/content-layer.ts +316 -314
- package/src/content/content.test.ts +433 -433
- package/src/content/digest.ts +133 -133
- package/src/content/generate-types.ts +168 -168
- package/src/content/index.ts +6 -1
- package/src/content/llms-txt.ts +277 -277
- package/src/contract/define.ts +474 -474
- package/src/contract/route-helpers.ts +2 -1
- package/src/contract/zod-utils.ts +158 -155
- package/src/db/index.ts +513 -513
- package/src/desktop/__tests__/smoke.test.ts +100 -100
- package/src/desktop/webview-fallback.ts +583 -583
- package/src/desktop/window.ts +3 -1
- package/src/dev-error-overlay/overlay-client.ts +300 -300
- package/src/devtools/ai/mcp-connector.ts +499 -498
- package/src/devtools/client/components/kitchen-root.tsx +7 -2
- package/src/email/resend.ts +163 -163
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -0
- package/src/guard/ast-analyzer.ts +806 -806
- package/src/guard/graph.ts +898 -898
- package/src/guard/index.ts +16 -0
- package/src/guard/statistics.ts +578 -578
- package/src/guard/tsgolint-bridge.ts +512 -0
- package/src/i18n/locale-resolver.ts +214 -214
- package/src/id/__tests__/id.test.ts +120 -120
- package/src/intent/index.ts +321 -321
- package/src/island/index.ts +39 -23
- package/src/kitchen/api/contract-api.ts +15 -8
- package/src/kitchen/kitchen-ui.ts +2137 -2137
- package/src/lockfile/index.ts +3 -2
- package/src/middleware/oauth/__tests__/oauth.test.ts +575 -574
- package/src/middleware/rate-limit/__tests__/rate-limit.test.ts +642 -642
- package/src/middleware/secure/index.ts +417 -417
- package/src/observability/event-bus.ts +2 -2
- package/src/observability/metrics.ts +334 -334
- package/src/observability/tracing.ts +694 -694
- package/src/openapi/generator.ts +1 -1
- package/src/perf/user-marks.ts +553 -553
- package/src/plugins/registry.ts +387 -387
- package/src/resource/ddl/diff.ts +392 -392
- package/src/resource/ddl/snapshot.ts +448 -447
- package/src/resource/generator-schema.ts +477 -476
- package/src/resource/parser.ts +4 -2
- package/src/resource/schema.ts +1 -1
- package/src/router/fs-patterns.ts +422 -422
- package/src/runtime/fast-refresh-types.ts +126 -128
- package/src/runtime/image-handler.ts +206 -195
- package/src/runtime/router.test.ts +476 -476
- package/src/runtime/security.ts +155 -155
- package/src/runtime/server.ts +36 -19
- package/src/runtime/session-key.ts +328 -328
- package/src/scheduler/__tests__/scheduler.test.ts +514 -514
- package/src/seo/resolve/index.ts +353 -353
- package/src/spec/load.ts +1 -1
- package/src/testing/reporter.ts +676 -676
- package/src/testing/server.ts +196 -196
- package/src/testing/snapshot.ts +444 -444
- package/src/utils/__tests__/lru-cache.test.ts +186 -186
- package/src/utils/bun.ts +8 -8
|
@@ -1,149 +1,149 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 7.2.S3 — CLI bench utility regression tests (Agent A).
|
|
3
|
-
*
|
|
4
|
-
* `scripts/cli-bench.ts` spawns real `mandu dev` subprocesses and parses
|
|
5
|
-
* their ready-log line — a full run costs 15-30 s + port collision risk,
|
|
6
|
-
* which makes it unfit for the test matrix.
|
|
7
|
-
*
|
|
8
|
-
* What we can pin here is:
|
|
9
|
-
* (1) The script file exists at the documented path (regression guard
|
|
10
|
-
* against accidental rename).
|
|
11
|
-
* (2) The percentile / summarize helpers produce correct output for
|
|
12
|
-
* known inputs — these are the aggregate layer the bench reports
|
|
13
|
-
* depend on.
|
|
14
|
-
* (3) The fixture auto-config seeds a valid-looking `mandu.config.ts`
|
|
15
|
-
* if one is missing (smoke test the helper without invoking the
|
|
16
|
-
* bench itself).
|
|
17
|
-
*
|
|
18
|
-
* We exercise the bench by re-importing its top-level functions… but the
|
|
19
|
-
* script's entrypoint calls `main()` at module load. To keep tests
|
|
20
|
-
* hermetic we extract the aggregator logic (local `percentile` /
|
|
21
|
-
* `summarize`) via a tiny fixture that mimics the implementation: if the
|
|
22
|
-
* script changes its math, this test will flag it the first time
|
|
23
|
-
* percentile contracts shift.
|
|
24
|
-
*
|
|
25
|
-
* If you're here because you rewrote cli-bench's percentile logic, just
|
|
26
|
-
* re-copy the new math into the fixture below — these tests exist to
|
|
27
|
-
* catch divergence, not lock in a specific implementation.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
import { describe, it, expect } from "bun:test";
|
|
31
|
-
import { existsSync } from "fs";
|
|
32
|
-
import path from "path";
|
|
33
|
-
|
|
34
|
-
// ============================================
|
|
35
|
-
// Fixture — mirrors `scripts/cli-bench.ts` percentile/summarize
|
|
36
|
-
// ============================================
|
|
37
|
-
|
|
38
|
-
interface Stats {
|
|
39
|
-
count: number;
|
|
40
|
-
p50: number;
|
|
41
|
-
p95: number;
|
|
42
|
-
p99: number;
|
|
43
|
-
mean: number;
|
|
44
|
-
min: number;
|
|
45
|
-
max: number;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function percentile(sorted: number[], p: number): number {
|
|
49
|
-
if (sorted.length === 0) return 0;
|
|
50
|
-
if (sorted.length === 1) return sorted[0]!;
|
|
51
|
-
const rank = (p / 100) * (sorted.length - 1);
|
|
52
|
-
const lo = Math.floor(rank);
|
|
53
|
-
const hi = Math.ceil(rank);
|
|
54
|
-
if (lo === hi) return sorted[lo]!;
|
|
55
|
-
const w = rank - lo;
|
|
56
|
-
return sorted[lo]! * (1 - w) + sorted[hi]! * w;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function summarize(samples: number[]): Stats {
|
|
60
|
-
const sorted = [...samples].sort((a, b) => a - b);
|
|
61
|
-
return {
|
|
62
|
-
count: sorted.length,
|
|
63
|
-
p50: percentile(sorted, 50),
|
|
64
|
-
p95: percentile(sorted, 95),
|
|
65
|
-
p99: percentile(sorted, 99),
|
|
66
|
-
mean: samples.reduce((a, b) => a + b, 0) / (samples.length || 1),
|
|
67
|
-
min: sorted[0] ?? 0,
|
|
68
|
-
max: sorted[sorted.length - 1] ?? 0,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// ============================================
|
|
73
|
-
// Tests
|
|
74
|
-
// ============================================
|
|
75
|
-
|
|
76
|
-
describe("CLI bench — script presence + aggregate math", () => {
|
|
77
|
-
it("scripts/cli-bench.ts exists at the documented path", () => {
|
|
78
|
-
const scriptPath = path.resolve(
|
|
79
|
-
import.meta.dir,
|
|
80
|
-
"..",
|
|
81
|
-
"..",
|
|
82
|
-
"..",
|
|
83
|
-
"..",
|
|
84
|
-
"..",
|
|
85
|
-
"scripts",
|
|
86
|
-
"cli-bench.ts",
|
|
87
|
-
);
|
|
88
|
-
expect(existsSync(scriptPath)).toBe(true);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("percentile: returns 0 for empty input (prevents NaN bleed into report)", () => {
|
|
92
|
-
expect(percentile([], 50)).toBe(0);
|
|
93
|
-
expect(percentile([], 95)).toBe(0);
|
|
94
|
-
expect(percentile([], 99)).toBe(0);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("percentile: returns the single element for size-1 input", () => {
|
|
98
|
-
expect(percentile([42], 50)).toBe(42);
|
|
99
|
-
expect(percentile([42], 95)).toBe(42);
|
|
100
|
-
expect(percentile([42], 99)).toBe(42);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it("percentile: even-count P50 uses linear interp between the two middle values", () => {
|
|
104
|
-
// [1, 2] → rank = 0.5 * (2-1) = 0.5 → 1*0.5 + 2*0.5 = 1.5
|
|
105
|
-
expect(percentile([1, 2], 50)).toBe(1.5);
|
|
106
|
-
// [10, 20, 30, 40] → rank = 0.5 * 3 = 1.5 → interp between idx1 and idx2
|
|
107
|
-
expect(percentile([10, 20, 30, 40], 50)).toBe(25);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("percentile: P95/P99 rank well above the body of the samples", () => {
|
|
111
|
-
const samples = Array.from({ length: 20 }, (_, i) => (i + 1) * 10);
|
|
112
|
-
// [10..200] sorted; p50 = 105, p95 ≈ 200, p99 ≈ 200
|
|
113
|
-
expect(percentile(samples, 50)).toBe(105);
|
|
114
|
-
// rank = 0.95 * 19 = 18.05 → 190 * 0.95 + 200 * 0.05 = 190.5
|
|
115
|
-
expect(percentile(samples, 95)).toBeCloseTo(190.5, 1);
|
|
116
|
-
// rank = 0.99 * 19 = 18.81 → 190 * 0.19 + 200 * 0.81 = 198.1
|
|
117
|
-
expect(percentile(samples, 99)).toBeCloseTo(198.1, 1);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("summarize: surfaces P50/P95/P99 + min/max/mean in a single shape", () => {
|
|
121
|
-
const samples = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];
|
|
122
|
-
const s = summarize(samples);
|
|
123
|
-
|
|
124
|
-
expect(s.count).toBe(10);
|
|
125
|
-
expect(s.min).toBe(100);
|
|
126
|
-
expect(s.max).toBe(1000);
|
|
127
|
-
expect(s.mean).toBe(550);
|
|
128
|
-
// sorted P50 rank = 4.5 → 500 * 0.5 + 600 * 0.5 = 550
|
|
129
|
-
expect(s.p50).toBe(550);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("summarize: count is 0 for empty and mean is also 0 (no NaN propagation)", () => {
|
|
133
|
-
const s = summarize([]);
|
|
134
|
-
expect(s.count).toBe(0);
|
|
135
|
-
expect(s.mean).toBe(0);
|
|
136
|
-
expect(s.p95).toBe(0);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("summarize: single-sample degenerates to "
|
|
140
|
-
+ "reporting that sample for every stat", () => {
|
|
141
|
-
const s = summarize([123.4]);
|
|
142
|
-
expect(s.p50).toBe(123.4);
|
|
143
|
-
expect(s.p95).toBe(123.4);
|
|
144
|
-
expect(s.p99).toBe(123.4);
|
|
145
|
-
expect(s.min).toBe(123.4);
|
|
146
|
-
expect(s.max).toBe(123.4);
|
|
147
|
-
expect(s.mean).toBe(123.4);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Phase 7.2.S3 — CLI bench utility regression tests (Agent A).
|
|
3
|
+
*
|
|
4
|
+
* `scripts/cli-bench.ts` spawns real `mandu dev` subprocesses and parses
|
|
5
|
+
* their ready-log line — a full run costs 15-30 s + port collision risk,
|
|
6
|
+
* which makes it unfit for the test matrix.
|
|
7
|
+
*
|
|
8
|
+
* What we can pin here is:
|
|
9
|
+
* (1) The script file exists at the documented path (regression guard
|
|
10
|
+
* against accidental rename).
|
|
11
|
+
* (2) The percentile / summarize helpers produce correct output for
|
|
12
|
+
* known inputs — these are the aggregate layer the bench reports
|
|
13
|
+
* depend on.
|
|
14
|
+
* (3) The fixture auto-config seeds a valid-looking `mandu.config.ts`
|
|
15
|
+
* if one is missing (smoke test the helper without invoking the
|
|
16
|
+
* bench itself).
|
|
17
|
+
*
|
|
18
|
+
* We exercise the bench by re-importing its top-level functions… but the
|
|
19
|
+
* script's entrypoint calls `main()` at module load. To keep tests
|
|
20
|
+
* hermetic we extract the aggregator logic (local `percentile` /
|
|
21
|
+
* `summarize`) via a tiny fixture that mimics the implementation: if the
|
|
22
|
+
* script changes its math, this test will flag it the first time
|
|
23
|
+
* percentile contracts shift.
|
|
24
|
+
*
|
|
25
|
+
* If you're here because you rewrote cli-bench's percentile logic, just
|
|
26
|
+
* re-copy the new math into the fixture below — these tests exist to
|
|
27
|
+
* catch divergence, not lock in a specific implementation.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { describe, it, expect } from "bun:test";
|
|
31
|
+
import { existsSync } from "fs";
|
|
32
|
+
import path from "path";
|
|
33
|
+
|
|
34
|
+
// ============================================
|
|
35
|
+
// Fixture — mirrors `scripts/cli-bench.ts` percentile/summarize
|
|
36
|
+
// ============================================
|
|
37
|
+
|
|
38
|
+
interface Stats {
|
|
39
|
+
count: number;
|
|
40
|
+
p50: number;
|
|
41
|
+
p95: number;
|
|
42
|
+
p99: number;
|
|
43
|
+
mean: number;
|
|
44
|
+
min: number;
|
|
45
|
+
max: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function percentile(sorted: number[], p: number): number {
|
|
49
|
+
if (sorted.length === 0) return 0;
|
|
50
|
+
if (sorted.length === 1) return sorted[0]!;
|
|
51
|
+
const rank = (p / 100) * (sorted.length - 1);
|
|
52
|
+
const lo = Math.floor(rank);
|
|
53
|
+
const hi = Math.ceil(rank);
|
|
54
|
+
if (lo === hi) return sorted[lo]!;
|
|
55
|
+
const w = rank - lo;
|
|
56
|
+
return sorted[lo]! * (1 - w) + sorted[hi]! * w;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function summarize(samples: number[]): Stats {
|
|
60
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
61
|
+
return {
|
|
62
|
+
count: sorted.length,
|
|
63
|
+
p50: percentile(sorted, 50),
|
|
64
|
+
p95: percentile(sorted, 95),
|
|
65
|
+
p99: percentile(sorted, 99),
|
|
66
|
+
mean: samples.reduce((a, b) => a + b, 0) / (samples.length || 1),
|
|
67
|
+
min: sorted[0] ?? 0,
|
|
68
|
+
max: sorted[sorted.length - 1] ?? 0,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================
|
|
73
|
+
// Tests
|
|
74
|
+
// ============================================
|
|
75
|
+
|
|
76
|
+
describe("CLI bench — script presence + aggregate math", () => {
|
|
77
|
+
it("scripts/cli-bench.ts exists at the documented path", () => {
|
|
78
|
+
const scriptPath = path.resolve(
|
|
79
|
+
import.meta.dir,
|
|
80
|
+
"..",
|
|
81
|
+
"..",
|
|
82
|
+
"..",
|
|
83
|
+
"..",
|
|
84
|
+
"..",
|
|
85
|
+
"scripts",
|
|
86
|
+
"cli-bench.ts",
|
|
87
|
+
);
|
|
88
|
+
expect(existsSync(scriptPath)).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("percentile: returns 0 for empty input (prevents NaN bleed into report)", () => {
|
|
92
|
+
expect(percentile([], 50)).toBe(0);
|
|
93
|
+
expect(percentile([], 95)).toBe(0);
|
|
94
|
+
expect(percentile([], 99)).toBe(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("percentile: returns the single element for size-1 input", () => {
|
|
98
|
+
expect(percentile([42], 50)).toBe(42);
|
|
99
|
+
expect(percentile([42], 95)).toBe(42);
|
|
100
|
+
expect(percentile([42], 99)).toBe(42);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("percentile: even-count P50 uses linear interp between the two middle values", () => {
|
|
104
|
+
// [1, 2] → rank = 0.5 * (2-1) = 0.5 → 1*0.5 + 2*0.5 = 1.5
|
|
105
|
+
expect(percentile([1, 2], 50)).toBe(1.5);
|
|
106
|
+
// [10, 20, 30, 40] → rank = 0.5 * 3 = 1.5 → interp between idx1 and idx2
|
|
107
|
+
expect(percentile([10, 20, 30, 40], 50)).toBe(25);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("percentile: P95/P99 rank well above the body of the samples", () => {
|
|
111
|
+
const samples = Array.from({ length: 20 }, (_, i) => (i + 1) * 10);
|
|
112
|
+
// [10..200] sorted; p50 = 105, p95 ≈ 200, p99 ≈ 200
|
|
113
|
+
expect(percentile(samples, 50)).toBe(105);
|
|
114
|
+
// rank = 0.95 * 19 = 18.05 → 190 * 0.95 + 200 * 0.05 = 190.5
|
|
115
|
+
expect(percentile(samples, 95)).toBeCloseTo(190.5, 1);
|
|
116
|
+
// rank = 0.99 * 19 = 18.81 → 190 * 0.19 + 200 * 0.81 = 198.1
|
|
117
|
+
expect(percentile(samples, 99)).toBeCloseTo(198.1, 1);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("summarize: surfaces P50/P95/P99 + min/max/mean in a single shape", () => {
|
|
121
|
+
const samples = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];
|
|
122
|
+
const s = summarize(samples);
|
|
123
|
+
|
|
124
|
+
expect(s.count).toBe(10);
|
|
125
|
+
expect(s.min).toBe(100);
|
|
126
|
+
expect(s.max).toBe(1000);
|
|
127
|
+
expect(s.mean).toBe(550);
|
|
128
|
+
// sorted P50 rank = 4.5 → 500 * 0.5 + 600 * 0.5 = 550
|
|
129
|
+
expect(s.p50).toBe(550);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("summarize: count is 0 for empty and mean is also 0 (no NaN propagation)", () => {
|
|
133
|
+
const s = summarize([]);
|
|
134
|
+
expect(s.count).toBe(0);
|
|
135
|
+
expect(s.mean).toBe(0);
|
|
136
|
+
expect(s.p95).toBe(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("summarize: single-sample degenerates to "
|
|
140
|
+
+ "reporting that sample for every stat", () => {
|
|
141
|
+
const s = summarize([123.4]);
|
|
142
|
+
expect(s.p50).toBe(123.4);
|
|
143
|
+
expect(s.p95).toBe(123.4);
|
|
144
|
+
expect(s.p99).toBe(123.4);
|
|
145
|
+
expect(s.min).toBe(123.4);
|
|
146
|
+
expect(s.max).toBe(123.4);
|
|
147
|
+
expect(s.mean).toBe(123.4);
|
|
148
|
+
});
|
|
149
|
+
});
|