@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.
Files changed (76) hide show
  1. package/package.json +9 -1
  2. package/scripts/postinstall-lock.ts +153 -153
  3. package/src/a11y/run-audit.ts +15 -15
  4. package/src/agent/__tests__/context.test.ts +49 -1
  5. package/src/agent/context.ts +535 -535
  6. package/src/agent/index.ts +6 -6
  7. package/src/agent/plan.ts +282 -282
  8. package/src/agent/repair.ts +171 -171
  9. package/src/agent/sync.ts +200 -200
  10. package/src/agent/types.ts +8 -0
  11. package/src/agent/verify.ts +100 -2
  12. package/src/brain/doctor/analyzer.ts +7 -7
  13. package/src/bundler/__tests__/build-runner.ts +5 -4
  14. package/src/bundler/__tests__/cold-start.test.ts +60 -60
  15. package/src/bundler/__tests__/css.test.ts +20 -20
  16. package/src/bundler/analyzer.ts +15 -15
  17. package/src/bundler/build.test.ts +66 -15
  18. package/src/bundler/build.ts +165 -103
  19. package/src/bundler/css.ts +42 -42
  20. package/src/bundler/manifest-schema.ts +21 -21
  21. package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
  22. package/src/bundler/plugins/block-generated-imports.ts +13 -13
  23. package/src/bundler/types.ts +31 -31
  24. package/src/client/island.ts +79 -79
  25. package/src/config/validate.ts +1 -1
  26. package/src/contract/schema.ts +7 -0
  27. package/src/deploy/inference/context.ts +82 -82
  28. package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
  29. package/src/devtools/client/components/panel/panel-container.tsx +1 -1
  30. package/src/error/formatter.ts +10 -1
  31. package/src/experimental/index.ts +10 -0
  32. package/src/filling/context.ts +17 -17
  33. package/src/filling/filling.ts +22 -1
  34. package/src/filling/index.ts +15 -1
  35. package/src/generator/generate.ts +30 -30
  36. package/src/generator/index.ts +3 -3
  37. package/src/generator/templates.ts +210 -210
  38. package/src/guard/check.ts +9 -9
  39. package/src/guard/config-guard.ts +13 -13
  40. package/src/guard/fs-routes-policy.ts +51 -51
  41. package/src/guard/index.ts +11 -11
  42. package/src/index.ts +0 -10
  43. package/src/internal/index.ts +25 -0
  44. package/src/kitchen/api/file-api.ts +11 -11
  45. package/src/report/index.ts +1 -1
  46. package/src/resource/__tests__/generator.test.ts +6 -6
  47. package/src/resource/__tests__/schema.test.ts +14 -14
  48. package/src/resource/ddl/__tests__/emit.test.ts +165 -165
  49. package/src/resource/ddl/emit.ts +146 -146
  50. package/src/resource/generator-schema.ts +11 -11
  51. package/src/resource/generators/slot.ts +72 -72
  52. package/src/resource/schema.ts +21 -21
  53. package/src/router/client-entry.test.ts +69 -33
  54. package/src/router/client-entry.ts +134 -74
  55. package/src/router/fs-routes.ts +24 -22
  56. package/src/router/fs-scanner.ts +21 -17
  57. package/src/router/fs-types.ts +8 -5
  58. package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
  59. package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
  60. package/src/runtime/__tests__/page-render-response.test.ts +103 -103
  61. package/src/runtime/__tests__/request-middleware.test.ts +70 -70
  62. package/src/runtime/devtools-adapter.ts +68 -68
  63. package/src/runtime/escape.ts +34 -34
  64. package/src/runtime/image-feature.ts +15 -0
  65. package/src/runtime/observability-lifecycle.ts +290 -290
  66. package/src/runtime/page-render-response.ts +106 -106
  67. package/src/runtime/rate-limit.ts +231 -0
  68. package/src/runtime/request-middleware.ts +31 -31
  69. package/src/runtime/scheduler-lifecycle.ts +64 -0
  70. package/src/runtime/server.ts +27 -295
  71. package/src/runtime/ssr.ts +59 -59
  72. package/src/runtime/static-files.ts +289 -289
  73. package/src/runtime/streaming-ssr.ts +22 -22
  74. package/src/spec/schema.ts +4 -3
  75. package/src/watcher/__tests__/watcher.test.ts +59 -59
  76. package/src/watcher/watcher.ts +61 -61
@@ -1,70 +1,70 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { defineMiddleware } from "../../middleware/define";
3
- import {
4
- buildRequestMiddlewareChain,
5
- runRequestMiddleware,
6
- } from "../request-middleware";
7
-
8
- const req = new Request("https://example.test/api/echo");
9
-
10
- describe("runtime request middleware wiring", () => {
11
- it("keeps the no-middleware path unset for hot-path passthrough", () => {
12
- expect(buildRequestMiddlewareChain(undefined)).toBeUndefined();
13
- expect(buildRequestMiddlewareChain([])).toBeUndefined();
14
- });
15
-
16
- it("returns undefined when there is no chain or middleware is skipped", async () => {
17
- let finalHits = 0;
18
- const finalHandler = async (): Promise<Response> => {
19
- finalHits++;
20
- return new Response("ok");
21
- };
22
- const middlewareChain = buildRequestMiddlewareChain([
23
- defineMiddleware({
24
- name: "marker",
25
- handler: async (_req, next) => next(),
26
- }),
27
- ]);
28
-
29
- await expect(
30
- runRequestMiddleware({ req, middlewareChain: undefined, finalHandler })
31
- ).resolves.toBeUndefined();
32
- await expect(
33
- runRequestMiddleware({
34
- req,
35
- middlewareChain,
36
- finalHandler,
37
- skipMiddleware: true,
38
- })
39
- ).resolves.toBeUndefined();
40
- expect(finalHits).toBe(0);
41
- });
42
-
43
- it("dispatches through the composed chain when configured", async () => {
44
- const trace: string[] = [];
45
- const middlewareChain = buildRequestMiddlewareChain([
46
- defineMiddleware({
47
- name: "outer",
48
- handler: async (_req, next) => {
49
- trace.push("before");
50
- const response = await next();
51
- trace.push("after");
52
- return response;
53
- },
54
- }),
55
- ]);
56
-
57
- const response = await runRequestMiddleware({
58
- req,
59
- middlewareChain,
60
- finalHandler: async () => {
61
- trace.push("final");
62
- return new Response("ok");
63
- },
64
- });
65
-
66
- expect(response?.status).toBe(200);
67
- expect(await response?.text()).toBe("ok");
68
- expect(trace).toEqual(["before", "final", "after"]);
69
- });
70
- });
1
+ import { describe, expect, it } from "bun:test";
2
+ import { defineMiddleware } from "../../middleware/define";
3
+ import {
4
+ buildRequestMiddlewareChain,
5
+ runRequestMiddleware,
6
+ } from "../request-middleware";
7
+
8
+ const req = new Request("https://example.test/api/echo");
9
+
10
+ describe("runtime request middleware wiring", () => {
11
+ it("keeps the no-middleware path unset for hot-path passthrough", () => {
12
+ expect(buildRequestMiddlewareChain(undefined)).toBeUndefined();
13
+ expect(buildRequestMiddlewareChain([])).toBeUndefined();
14
+ });
15
+
16
+ it("returns undefined when there is no chain or middleware is skipped", async () => {
17
+ let finalHits = 0;
18
+ const finalHandler = async (): Promise<Response> => {
19
+ finalHits++;
20
+ return new Response("ok");
21
+ };
22
+ const middlewareChain = buildRequestMiddlewareChain([
23
+ defineMiddleware({
24
+ name: "marker",
25
+ handler: async (_req, next) => next(),
26
+ }),
27
+ ]);
28
+
29
+ await expect(
30
+ runRequestMiddleware({ req, middlewareChain: undefined, finalHandler })
31
+ ).resolves.toBeUndefined();
32
+ await expect(
33
+ runRequestMiddleware({
34
+ req,
35
+ middlewareChain,
36
+ finalHandler,
37
+ skipMiddleware: true,
38
+ })
39
+ ).resolves.toBeUndefined();
40
+ expect(finalHits).toBe(0);
41
+ });
42
+
43
+ it("dispatches through the composed chain when configured", async () => {
44
+ const trace: string[] = [];
45
+ const middlewareChain = buildRequestMiddlewareChain([
46
+ defineMiddleware({
47
+ name: "outer",
48
+ handler: async (_req, next) => {
49
+ trace.push("before");
50
+ const response = await next();
51
+ trace.push("after");
52
+ return response;
53
+ },
54
+ }),
55
+ ]);
56
+
57
+ const response = await runRequestMiddleware({
58
+ req,
59
+ middlewareChain,
60
+ finalHandler: async () => {
61
+ trace.push("final");
62
+ return new Response("ok");
63
+ },
64
+ });
65
+
66
+ expect(response?.status).toBe(200);
67
+ expect(await response?.text()).toBe("ok");
68
+ expect(trace).toEqual(["before", "final", "after"]);
69
+ });
70
+ });
@@ -1,68 +1,68 @@
1
- import type { RoutesManifest } from "../spec/schema";
2
- import type { GuardConfig } from "../guard/types";
3
- import {
4
- KITCHEN_PREFIX,
5
- KitchenHandler,
6
- recordRequest,
7
- type RequestEntry,
8
- } from "../kitchen/kitchen-handler";
9
-
10
- export type RuntimeKitchenHandler = KitchenHandler;
11
-
12
- export interface RuntimeDevtoolsAdapter {
13
- readonly kitchen: RuntimeKitchenHandler | null;
14
- readonly dashboardPath: string | null;
15
- start(): void;
16
- stop(): void;
17
- updateManifest(manifest: RoutesManifest): void;
18
- handleRequest(req: Request, pathname: string): Promise<Response | null>;
19
- }
20
-
21
- export interface CreateRuntimeDevtoolsAdapterOptions {
22
- isDev: boolean;
23
- rootDir: string;
24
- manifest: RoutesManifest;
25
- guardConfig: GuardConfig | null;
26
- }
27
-
28
- export function createRuntimeDevtoolsAdapter(
29
- options: CreateRuntimeDevtoolsAdapterOptions
30
- ): RuntimeDevtoolsAdapter {
31
- const kitchen = options.isDev
32
- ? new KitchenHandler({
33
- rootDir: options.rootDir,
34
- manifest: options.manifest,
35
- guardConfig: options.guardConfig,
36
- })
37
- : null;
38
-
39
- return {
40
- kitchen,
41
- dashboardPath: kitchen ? KITCHEN_PREFIX : null,
42
- start() {
43
- if (kitchen) void kitchen.start();
44
- },
45
- stop() {
46
- kitchen?.stop();
47
- },
48
- updateManifest(manifest: RoutesManifest) {
49
- kitchen?.updateManifest(manifest);
50
- },
51
- async handleRequest(req: Request, pathname: string): Promise<Response | null> {
52
- if (!kitchen || !pathname.startsWith(KITCHEN_PREFIX)) return null;
53
- return await kitchen.handle(req, pathname);
54
- },
55
- };
56
- }
57
-
58
- export function shouldRecordRuntimeRequest(pathname: string): boolean {
59
- return (
60
- !pathname.startsWith("/.mandu/") &&
61
- !pathname.startsWith(KITCHEN_PREFIX) &&
62
- !pathname.startsWith("/__mandu/")
63
- );
64
- }
65
-
66
- export function recordRuntimeRequest(entry: RequestEntry): void {
67
- recordRequest(entry);
68
- }
1
+ import type { RoutesManifest } from "../spec/schema";
2
+ import type { GuardConfig } from "../guard/types";
3
+ import {
4
+ KITCHEN_PREFIX,
5
+ KitchenHandler,
6
+ recordRequest,
7
+ type RequestEntry,
8
+ } from "../kitchen/kitchen-handler";
9
+
10
+ export type RuntimeKitchenHandler = KitchenHandler;
11
+
12
+ export interface RuntimeDevtoolsAdapter {
13
+ readonly kitchen: RuntimeKitchenHandler | null;
14
+ readonly dashboardPath: string | null;
15
+ start(): void;
16
+ stop(): void;
17
+ updateManifest(manifest: RoutesManifest): void;
18
+ handleRequest(req: Request, pathname: string): Promise<Response | null>;
19
+ }
20
+
21
+ export interface CreateRuntimeDevtoolsAdapterOptions {
22
+ isDev: boolean;
23
+ rootDir: string;
24
+ manifest: RoutesManifest;
25
+ guardConfig: GuardConfig | null;
26
+ }
27
+
28
+ export function createRuntimeDevtoolsAdapter(
29
+ options: CreateRuntimeDevtoolsAdapterOptions
30
+ ): RuntimeDevtoolsAdapter {
31
+ const kitchen = options.isDev
32
+ ? new KitchenHandler({
33
+ rootDir: options.rootDir,
34
+ manifest: options.manifest,
35
+ guardConfig: options.guardConfig,
36
+ })
37
+ : null;
38
+
39
+ return {
40
+ kitchen,
41
+ dashboardPath: kitchen ? KITCHEN_PREFIX : null,
42
+ start() {
43
+ if (kitchen) void kitchen.start();
44
+ },
45
+ stop() {
46
+ kitchen?.stop();
47
+ },
48
+ updateManifest(manifest: RoutesManifest) {
49
+ kitchen?.updateManifest(manifest);
50
+ },
51
+ async handleRequest(req: Request, pathname: string): Promise<Response | null> {
52
+ if (!kitchen || !pathname.startsWith(KITCHEN_PREFIX)) return null;
53
+ return await kitchen.handle(req, pathname);
54
+ },
55
+ };
56
+ }
57
+
58
+ export function shouldRecordRuntimeRequest(pathname: string): boolean {
59
+ return (
60
+ !pathname.startsWith("/.mandu/") &&
61
+ !pathname.startsWith(KITCHEN_PREFIX) &&
62
+ !pathname.startsWith("/__mandu/")
63
+ );
64
+ }
65
+
66
+ export function recordRuntimeRequest(entry: RequestEntry): void {
67
+ recordRequest(entry);
68
+ }
@@ -3,40 +3,40 @@
3
3
  * <title>, <p> 등 텍스트 노드에 들어갈 문자열을 안전하게 처리.
4
4
  * 속성값과 달리 " ' 는 이스케이프 불필요.
5
5
  */
6
- export function escapeHtmlText(value: string): string {
7
- return value
8
- .replace(/&/g, "&amp;")
9
- .replace(/</g, "&lt;")
10
- .replace(/>/g, "&gt;");
11
- }
12
-
13
- /**
14
- * Decode the small HTML entity set React can emit inside text metadata.
15
- * Callers should still escape the returned string before writing HTML.
16
- */
17
- export function decodeHtmlText(value: string): string {
18
- return value.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos|#39);/gi, (match, entity: string) => {
19
- const normalized = entity.toLowerCase();
20
- if (normalized === "amp") return "&";
21
- if (normalized === "lt") return "<";
22
- if (normalized === "gt") return ">";
23
- if (normalized === "quot") return '"';
24
- if (normalized === "apos" || normalized === "#39") return "'";
25
- if (normalized.startsWith("#x")) {
26
- const codePoint = Number.parseInt(normalized.slice(2), 16);
27
- return isValidCodePoint(codePoint) ? String.fromCodePoint(codePoint) : match;
28
- }
29
- if (normalized.startsWith("#")) {
30
- const codePoint = Number.parseInt(normalized.slice(1), 10);
31
- return isValidCodePoint(codePoint) ? String.fromCodePoint(codePoint) : match;
32
- }
33
- return match;
34
- });
35
- }
36
-
37
- function isValidCodePoint(value: number): boolean {
38
- return Number.isInteger(value) && value >= 0 && value <= 0x10ffff;
39
- }
6
+ export function escapeHtmlText(value: string): string {
7
+ return value
8
+ .replace(/&/g, "&amp;")
9
+ .replace(/</g, "&lt;")
10
+ .replace(/>/g, "&gt;");
11
+ }
12
+
13
+ /**
14
+ * Decode the small HTML entity set React can emit inside text metadata.
15
+ * Callers should still escape the returned string before writing HTML.
16
+ */
17
+ export function decodeHtmlText(value: string): string {
18
+ return value.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos|#39);/gi, (match, entity: string) => {
19
+ const normalized = entity.toLowerCase();
20
+ if (normalized === "amp") return "&";
21
+ if (normalized === "lt") return "<";
22
+ if (normalized === "gt") return ">";
23
+ if (normalized === "quot") return '"';
24
+ if (normalized === "apos" || normalized === "#39") return "'";
25
+ if (normalized.startsWith("#x")) {
26
+ const codePoint = Number.parseInt(normalized.slice(2), 16);
27
+ return isValidCodePoint(codePoint) ? String.fromCodePoint(codePoint) : match;
28
+ }
29
+ if (normalized.startsWith("#")) {
30
+ const codePoint = Number.parseInt(normalized.slice(1), 10);
31
+ return isValidCodePoint(codePoint) ? String.fromCodePoint(codePoint) : match;
32
+ }
33
+ return match;
34
+ });
35
+ }
36
+
37
+ function isValidCodePoint(value: number): boolean {
38
+ return Number.isInteger(value) && value >= 0 && value <= 0x10ffff;
39
+ }
40
40
 
41
41
  /**
42
42
  * HTML 속성값 이스케이프
@@ -0,0 +1,15 @@
1
+ export async function maybeHandleImageFeatureRequest(
2
+ request: Request,
3
+ settings: {
4
+ edge?: boolean;
5
+ rootDir: string;
6
+ publicDir: string;
7
+ },
8
+ ): Promise<Response | null> {
9
+ if (settings.edge) return null;
10
+ const pathname = new URL(request.url).pathname;
11
+ if (pathname !== "/_mandu/image") return null;
12
+
13
+ const { handleImageRequest } = await import("./image-handler");
14
+ return await handleImageRequest(request, settings.rootDir, settings.publicDir);
15
+ }