@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.
Files changed (88) hide show
  1. package/package.json +21 -4
  2. package/src/auth/__tests__/login.test.ts +420 -419
  3. package/src/auth/__tests__/reset.test.ts +296 -296
  4. package/src/brain/adapters/anthropic-oauth.ts +421 -420
  5. package/src/brain/adapters/index.ts +2 -1
  6. package/src/brain/adapters/ollama.ts +1 -1
  7. package/src/brain/adapters/openai-oauth.ts +534 -533
  8. package/src/brain/brain.ts +2 -1
  9. package/src/brain/redactor.ts +196 -196
  10. package/src/bundler/__tests__/cli-bench-utils.test.ts +149 -149
  11. package/src/bundler/__tests__/cold-start.test.ts +504 -504
  12. package/src/bundler/__tests__/fast-refresh.test.ts +607 -606
  13. package/src/bundler/__tests__/hdr.test.ts +1 -1
  14. package/src/bundler/analyzer.ts +958 -958
  15. package/src/bundler/build.ts +104 -14
  16. package/src/bundler/dev.ts +125 -0
  17. package/src/bundler/hmr-types.ts +1 -0
  18. package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -0
  19. package/src/bundler/plugins/index.ts +14 -0
  20. package/src/bundler/plugins/react-compiler-lint.ts +253 -0
  21. package/src/bundler/plugins/react-compiler.ts +162 -0
  22. package/src/bundler/types.ts +12 -0
  23. package/src/change/integrity.ts +2 -1
  24. package/src/client/index.ts +10 -0
  25. package/src/client/island.ts +38 -11
  26. package/src/client/router.ts +6 -1
  27. package/src/config/mandu.ts +57 -0
  28. package/src/config/validate.ts +42 -0
  29. package/src/content/collection.ts +844 -809
  30. package/src/content/content-layer.ts +316 -314
  31. package/src/content/content.test.ts +433 -433
  32. package/src/content/digest.ts +133 -133
  33. package/src/content/generate-types.ts +168 -168
  34. package/src/content/index.ts +6 -1
  35. package/src/content/llms-txt.ts +277 -277
  36. package/src/contract/define.ts +474 -474
  37. package/src/contract/route-helpers.ts +2 -1
  38. package/src/contract/zod-utils.ts +158 -155
  39. package/src/db/index.ts +513 -513
  40. package/src/desktop/__tests__/smoke.test.ts +100 -100
  41. package/src/desktop/webview-fallback.ts +583 -583
  42. package/src/desktop/window.ts +3 -1
  43. package/src/dev-error-overlay/overlay-client.ts +300 -300
  44. package/src/devtools/ai/mcp-connector.ts +499 -498
  45. package/src/devtools/client/components/kitchen-root.tsx +7 -2
  46. package/src/email/resend.ts +163 -163
  47. package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -0
  48. package/src/guard/ast-analyzer.ts +806 -806
  49. package/src/guard/graph.ts +898 -898
  50. package/src/guard/index.ts +16 -0
  51. package/src/guard/statistics.ts +578 -578
  52. package/src/guard/tsgolint-bridge.ts +512 -0
  53. package/src/i18n/locale-resolver.ts +214 -214
  54. package/src/id/__tests__/id.test.ts +120 -120
  55. package/src/intent/index.ts +321 -321
  56. package/src/island/index.ts +39 -23
  57. package/src/kitchen/api/contract-api.ts +15 -8
  58. package/src/kitchen/kitchen-ui.ts +2137 -2137
  59. package/src/lockfile/index.ts +3 -2
  60. package/src/middleware/oauth/__tests__/oauth.test.ts +575 -574
  61. package/src/middleware/rate-limit/__tests__/rate-limit.test.ts +642 -642
  62. package/src/middleware/secure/index.ts +417 -417
  63. package/src/observability/event-bus.ts +2 -2
  64. package/src/observability/metrics.ts +334 -334
  65. package/src/observability/tracing.ts +694 -694
  66. package/src/openapi/generator.ts +1 -1
  67. package/src/perf/user-marks.ts +553 -553
  68. package/src/plugins/registry.ts +387 -387
  69. package/src/resource/ddl/diff.ts +392 -392
  70. package/src/resource/ddl/snapshot.ts +448 -447
  71. package/src/resource/generator-schema.ts +477 -476
  72. package/src/resource/parser.ts +4 -2
  73. package/src/resource/schema.ts +1 -1
  74. package/src/router/fs-patterns.ts +422 -422
  75. package/src/runtime/fast-refresh-types.ts +126 -128
  76. package/src/runtime/image-handler.ts +206 -195
  77. package/src/runtime/router.test.ts +476 -476
  78. package/src/runtime/security.ts +155 -155
  79. package/src/runtime/server.ts +36 -19
  80. package/src/runtime/session-key.ts +328 -328
  81. package/src/scheduler/__tests__/scheduler.test.ts +514 -514
  82. package/src/seo/resolve/index.ts +353 -353
  83. package/src/spec/load.ts +1 -1
  84. package/src/testing/reporter.ts +676 -676
  85. package/src/testing/server.ts +196 -196
  86. package/src/testing/snapshot.ts +444 -444
  87. package/src/utils/__tests__/lru-cache.test.ts +186 -186
  88. package/src/utils/bun.ts +8 -8
@@ -1,186 +1,186 @@
1
- /**
2
- * Phase 17 — LRUCache behaviour matrix.
3
- *
4
- * Covers:
5
- * - Default + custom maxSize
6
- * - Eviction order (LRU semantics, not FIFO)
7
- * - `get` promotes the entry, `has` does not
8
- * - `onEvict` fires for LRU pressure / delete / clear
9
- * - Backward-compat numeric constructor
10
- * - Stats bookkeeping
11
- */
12
-
13
- import { describe, test, expect } from "bun:test";
14
- import { LRUCache } from "../lru-cache";
15
-
16
- describe("LRUCache", () => {
17
- test("default maxSize is 1000 when no options passed", () => {
18
- const cache = new LRUCache<string, number>();
19
- // Insert 1000 — all fit. The 1001st evicts the first.
20
- for (let i = 0; i < 1000; i++) cache.set(`k${i}`, i);
21
- expect(cache.size).toBe(1000);
22
- expect(cache.has("k0")).toBe(true);
23
-
24
- cache.set("k1000", 1000);
25
- expect(cache.size).toBe(1000);
26
- expect(cache.has("k0")).toBe(false);
27
- expect(cache.has("k1000")).toBe(true);
28
- });
29
-
30
- test("numeric constructor is still supported (backward-compat)", () => {
31
- const cache = new LRUCache<string, string>(3);
32
- cache.set("a", "1");
33
- cache.set("b", "2");
34
- cache.set("c", "3");
35
- cache.set("d", "4"); // evicts "a"
36
- expect(cache.has("a")).toBe(false);
37
- expect(cache.size).toBe(3);
38
- });
39
-
40
- test("rejects invalid maxSize", () => {
41
- expect(() => new LRUCache({ maxSize: 0 })).toThrow();
42
- expect(() => new LRUCache({ maxSize: -1 })).toThrow();
43
- expect(() => new LRUCache({ maxSize: Number.NaN })).toThrow();
44
- });
45
-
46
- test("evicts least-recently-used entry, not FIFO", () => {
47
- const cache = new LRUCache<string, number>({ maxSize: 3 });
48
- cache.set("a", 1);
49
- cache.set("b", 2);
50
- cache.set("c", 3);
51
- // Touch "a" so it becomes most-recently-used.
52
- cache.get("a");
53
- // Now inserting "d" must evict "b" (oldest), not "a".
54
- cache.set("d", 4);
55
- expect(cache.has("a")).toBe(true);
56
- expect(cache.has("b")).toBe(false);
57
- expect(cache.has("c")).toBe(true);
58
- expect(cache.has("d")).toBe(true);
59
- });
60
-
61
- test("has() does NOT promote (read-only probe)", () => {
62
- const cache = new LRUCache<string, number>({ maxSize: 3 });
63
- cache.set("a", 1);
64
- cache.set("b", 2);
65
- cache.set("c", 3);
66
- // `has` on "a" must not reorder. Inserting "d" should still evict "a".
67
- expect(cache.has("a")).toBe(true);
68
- cache.set("d", 4);
69
- expect(cache.has("a")).toBe(false);
70
- expect(cache.has("b")).toBe(true);
71
- });
72
-
73
- test("updating an existing key re-inserts as MRU but does not fire onEvict", () => {
74
- const evicted: Array<[string, number]> = [];
75
- const cache = new LRUCache<string, number>({
76
- maxSize: 3,
77
- onEvict: (k, v) => evicted.push([k, v]),
78
- });
79
- cache.set("a", 1);
80
- cache.set("b", 2);
81
- cache.set("c", 3);
82
- cache.set("a", 10); // update, a becomes MRU
83
- expect(evicted).toHaveLength(0);
84
- cache.set("d", 4); // should evict "b" (now oldest), not "a"
85
- expect(evicted).toEqual([["b", 2]]);
86
- expect(cache.get("a")).toBe(10);
87
- });
88
-
89
- test("onEvict fires on LRU pressure with (key, value)", () => {
90
- const evicted: Array<[string, number]> = [];
91
- const cache = new LRUCache<string, number>({
92
- maxSize: 2,
93
- onEvict: (k, v) => evicted.push([k, v]),
94
- });
95
- cache.set("a", 1);
96
- cache.set("b", 2);
97
- cache.set("c", 3); // evicts "a"
98
- cache.set("d", 4); // evicts "b"
99
- expect(evicted).toEqual([
100
- ["a", 1],
101
- ["b", 2],
102
- ]);
103
- });
104
-
105
- test("onEvict fires on delete() for present keys only", () => {
106
- const evicted: Array<[string, number]> = [];
107
- const cache = new LRUCache<string, number>({
108
- maxSize: 5,
109
- onEvict: (k, v) => evicted.push([k, v]),
110
- });
111
- cache.set("x", 1);
112
- expect(cache.delete("x")).toBe(true);
113
- expect(cache.delete("x")).toBe(false); // already gone
114
- expect(cache.delete("missing")).toBe(false);
115
- expect(evicted).toEqual([["x", 1]]);
116
- });
117
-
118
- test("onEvict fires for every entry during clear()", () => {
119
- const evicted: string[] = [];
120
- const cache = new LRUCache<string, number>({
121
- maxSize: 10,
122
- onEvict: (k) => evicted.push(k),
123
- });
124
- cache.set("a", 1);
125
- cache.set("b", 2);
126
- cache.set("c", 3);
127
- cache.clear();
128
- expect(evicted.sort()).toEqual(["a", "b", "c"]);
129
- expect(cache.size).toBe(0);
130
- });
131
-
132
- test("onEvict errors are swallowed (must not corrupt state)", () => {
133
- const cache = new LRUCache<string, number>({
134
- maxSize: 2,
135
- onEvict: () => {
136
- throw new Error("boom");
137
- },
138
- });
139
- cache.set("a", 1);
140
- cache.set("b", 2);
141
- // Would throw during eviction if errors weren't caught.
142
- expect(() => cache.set("c", 3)).not.toThrow();
143
- expect(cache.size).toBe(2);
144
- expect(cache.has("a")).toBe(false);
145
- });
146
-
147
- test("getWithStats records hits/misses; getStats reports hit rate", () => {
148
- const cache = new LRUCache<string, number>({ maxSize: 10 });
149
- cache.set("a", 1);
150
- cache.getWithStats("a"); // hit
151
- cache.getWithStats("a"); // hit
152
- cache.getWithStats("b"); // miss
153
- const stats = cache.getStats();
154
- expect(stats.hits).toBe(2);
155
- expect(stats.misses).toBe(1);
156
- expect(stats.hitRate).toBeCloseTo(2 / 3);
157
- expect(stats.size).toBe(1);
158
- expect(stats.maxSize).toBe(10);
159
- cache.resetStats();
160
- expect(cache.getStats().hits).toBe(0);
161
- expect(cache.getStats().misses).toBe(0);
162
- });
163
-
164
- test("entries() yields LRU → MRU order", () => {
165
- const cache = new LRUCache<string, number>({ maxSize: 5 });
166
- cache.set("a", 1);
167
- cache.set("b", 2);
168
- cache.set("c", 3);
169
- cache.get("a"); // a becomes MRU
170
- const keys = [...cache.entries()].map(([k]) => k);
171
- expect(keys).toEqual(["b", "c", "a"]);
172
- });
173
-
174
- test("fill-past-max proof: 1001 writes against a 1000-entry cache evict exactly the oldest", () => {
175
- const cache = new LRUCache<number, number>({ maxSize: 1000 });
176
- for (let i = 0; i < 1000; i++) cache.set(i, i);
177
- expect(cache.size).toBe(1000);
178
- expect(cache.has(0)).toBe(true);
179
-
180
- cache.set(1000, 1000);
181
- expect(cache.size).toBe(1000); // still bounded
182
- expect(cache.has(0)).toBe(false); // oldest gone
183
- expect(cache.has(1000)).toBe(true); // newest present
184
- expect(cache.has(500)).toBe(true); // middle survives
185
- });
186
- });
1
+ /**
2
+ * Phase 17 — LRUCache behaviour matrix.
3
+ *
4
+ * Covers:
5
+ * - Default + custom maxSize
6
+ * - Eviction order (LRU semantics, not FIFO)
7
+ * - `get` promotes the entry, `has` does not
8
+ * - `onEvict` fires for LRU pressure / delete / clear
9
+ * - Backward-compat numeric constructor
10
+ * - Stats bookkeeping
11
+ */
12
+
13
+ import { describe, test, expect } from "bun:test";
14
+ import { LRUCache } from "../lru-cache";
15
+
16
+ describe("LRUCache", () => {
17
+ test("default maxSize is 1000 when no options passed", () => {
18
+ const cache = new LRUCache<string, number>();
19
+ // Insert 1000 — all fit. The 1001st evicts the first.
20
+ for (let i = 0; i < 1000; i++) cache.set(`k${i}`, i);
21
+ expect(cache.size).toBe(1000);
22
+ expect(cache.has("k0")).toBe(true);
23
+
24
+ cache.set("k1000", 1000);
25
+ expect(cache.size).toBe(1000);
26
+ expect(cache.has("k0")).toBe(false);
27
+ expect(cache.has("k1000")).toBe(true);
28
+ });
29
+
30
+ test("numeric constructor is still supported (backward-compat)", () => {
31
+ const cache = new LRUCache<string, string>(3);
32
+ cache.set("a", "1");
33
+ cache.set("b", "2");
34
+ cache.set("c", "3");
35
+ cache.set("d", "4"); // evicts "a"
36
+ expect(cache.has("a")).toBe(false);
37
+ expect(cache.size).toBe(3);
38
+ });
39
+
40
+ test("rejects invalid maxSize", () => {
41
+ expect(() => new LRUCache({ maxSize: 0 })).toThrow();
42
+ expect(() => new LRUCache({ maxSize: -1 })).toThrow();
43
+ expect(() => new LRUCache({ maxSize: Number.NaN })).toThrow();
44
+ });
45
+
46
+ test("evicts least-recently-used entry, not FIFO", () => {
47
+ const cache = new LRUCache<string, number>({ maxSize: 3 });
48
+ cache.set("a", 1);
49
+ cache.set("b", 2);
50
+ cache.set("c", 3);
51
+ // Touch "a" so it becomes most-recently-used.
52
+ cache.get("a");
53
+ // Now inserting "d" must evict "b" (oldest), not "a".
54
+ cache.set("d", 4);
55
+ expect(cache.has("a")).toBe(true);
56
+ expect(cache.has("b")).toBe(false);
57
+ expect(cache.has("c")).toBe(true);
58
+ expect(cache.has("d")).toBe(true);
59
+ });
60
+
61
+ test("has() does NOT promote (read-only probe)", () => {
62
+ const cache = new LRUCache<string, number>({ maxSize: 3 });
63
+ cache.set("a", 1);
64
+ cache.set("b", 2);
65
+ cache.set("c", 3);
66
+ // `has` on "a" must not reorder. Inserting "d" should still evict "a".
67
+ expect(cache.has("a")).toBe(true);
68
+ cache.set("d", 4);
69
+ expect(cache.has("a")).toBe(false);
70
+ expect(cache.has("b")).toBe(true);
71
+ });
72
+
73
+ test("updating an existing key re-inserts as MRU but does not fire onEvict", () => {
74
+ const evicted: Array<[string, number]> = [];
75
+ const cache = new LRUCache<string, number>({
76
+ maxSize: 3,
77
+ onEvict: (k, v) => evicted.push([k, v]),
78
+ });
79
+ cache.set("a", 1);
80
+ cache.set("b", 2);
81
+ cache.set("c", 3);
82
+ cache.set("a", 10); // update, a becomes MRU
83
+ expect(evicted).toHaveLength(0);
84
+ cache.set("d", 4); // should evict "b" (now oldest), not "a"
85
+ expect(evicted).toEqual([["b", 2]]);
86
+ expect(cache.get("a")).toBe(10);
87
+ });
88
+
89
+ test("onEvict fires on LRU pressure with (key, value)", () => {
90
+ const evicted: Array<[string, number]> = [];
91
+ const cache = new LRUCache<string, number>({
92
+ maxSize: 2,
93
+ onEvict: (k, v) => evicted.push([k, v]),
94
+ });
95
+ cache.set("a", 1);
96
+ cache.set("b", 2);
97
+ cache.set("c", 3); // evicts "a"
98
+ cache.set("d", 4); // evicts "b"
99
+ expect(evicted).toEqual([
100
+ ["a", 1],
101
+ ["b", 2],
102
+ ]);
103
+ });
104
+
105
+ test("onEvict fires on delete() for present keys only", () => {
106
+ const evicted: Array<[string, number]> = [];
107
+ const cache = new LRUCache<string, number>({
108
+ maxSize: 5,
109
+ onEvict: (k, v) => evicted.push([k, v]),
110
+ });
111
+ cache.set("x", 1);
112
+ expect(cache.delete("x")).toBe(true);
113
+ expect(cache.delete("x")).toBe(false); // already gone
114
+ expect(cache.delete("missing")).toBe(false);
115
+ expect(evicted).toEqual([["x", 1]]);
116
+ });
117
+
118
+ test("onEvict fires for every entry during clear()", () => {
119
+ const evicted: string[] = [];
120
+ const cache = new LRUCache<string, number>({
121
+ maxSize: 10,
122
+ onEvict: (k) => evicted.push(k),
123
+ });
124
+ cache.set("a", 1);
125
+ cache.set("b", 2);
126
+ cache.set("c", 3);
127
+ cache.clear();
128
+ expect(evicted.sort()).toEqual(["a", "b", "c"]);
129
+ expect(cache.size).toBe(0);
130
+ });
131
+
132
+ test("onEvict errors are swallowed (must not corrupt state)", () => {
133
+ const cache = new LRUCache<string, number>({
134
+ maxSize: 2,
135
+ onEvict: () => {
136
+ throw new Error("boom");
137
+ },
138
+ });
139
+ cache.set("a", 1);
140
+ cache.set("b", 2);
141
+ // Would throw during eviction if errors weren't caught.
142
+ expect(() => cache.set("c", 3)).not.toThrow();
143
+ expect(cache.size).toBe(2);
144
+ expect(cache.has("a")).toBe(false);
145
+ });
146
+
147
+ test("getWithStats records hits/misses; getStats reports hit rate", () => {
148
+ const cache = new LRUCache<string, number>({ maxSize: 10 });
149
+ cache.set("a", 1);
150
+ cache.getWithStats("a"); // hit
151
+ cache.getWithStats("a"); // hit
152
+ cache.getWithStats("b"); // miss
153
+ const stats = cache.getStats();
154
+ expect(stats.hits).toBe(2);
155
+ expect(stats.misses).toBe(1);
156
+ expect(stats.hitRate).toBeCloseTo(2 / 3);
157
+ expect(stats.size).toBe(1);
158
+ expect(stats.maxSize).toBe(10);
159
+ cache.resetStats();
160
+ expect(cache.getStats().hits).toBe(0);
161
+ expect(cache.getStats().misses).toBe(0);
162
+ });
163
+
164
+ test("entries() yields LRU → MRU order", () => {
165
+ const cache = new LRUCache<string, number>({ maxSize: 5 });
166
+ cache.set("a", 1);
167
+ cache.set("b", 2);
168
+ cache.set("c", 3);
169
+ cache.get("a"); // a becomes MRU
170
+ const keys = [...cache.entries()].map(([k]) => k);
171
+ expect(keys).toEqual(["b", "c", "a"]);
172
+ });
173
+
174
+ test("fill-past-max proof: 1001 writes against a 1000-entry cache evict exactly the oldest", () => {
175
+ const cache = new LRUCache<number, number>({ maxSize: 1000 });
176
+ for (let i = 0; i < 1000; i++) cache.set(i, i);
177
+ expect(cache.size).toBe(1000);
178
+ expect(cache.has(0)).toBe(true);
179
+
180
+ cache.set(1000, 1000);
181
+ expect(cache.size).toBe(1000); // still bounded
182
+ expect(cache.has(0)).toBe(false); // oldest gone
183
+ expect(cache.has(1000)).toBe(true); // newest present
184
+ expect(cache.has(500)).toBe(true); // middle survives
185
+ });
186
+ });
package/src/utils/bun.ts CHANGED
@@ -1,8 +1,8 @@
1
- export async function readJsonFile(filePath: string): Promise<unknown> {
2
- try {
3
- return await Bun.file(filePath).json();
4
- } catch (error) {
5
- const message = error instanceof Error ? error.message : String(error);
6
- throw new Error(`Failed to parse JSON in ${filePath}: ${message}`);
7
- }
8
- }
1
+ export async function readJsonFile(filePath: string): Promise<unknown> {
2
+ try {
3
+ return await Bun.file(filePath).json();
4
+ } catch (error) {
5
+ const message = error instanceof Error ? error.message : String(error);
6
+ throw new Error(`Failed to parse JSON in ${filePath}: ${message}`, { cause: error });
7
+ }
8
+ }