@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
@@ -268,14 +268,19 @@ function KitchenApp({ config }: KitchenAppProps): React.ReactElement | null {
268
268
  }
269
269
 
270
270
  // 3. window.__MANDU_* globals 삭제
271
+ //
272
+ // Window is an augmented global type; Mandu injects `__MANDU_*` keys
273
+ // at runtime that aren't declared in lib.dom.d.ts. Cast through a
274
+ // `Record<string, unknown>` to keep the delete legal without `any`.
275
+ const winRec = window as unknown as Record<string, unknown>;
271
276
  for (const key of Object.keys(window)) {
272
277
  if (key.startsWith('__MANDU_')) {
273
- delete (window as any)[key];
278
+ delete winRec[key];
274
279
  }
275
280
  }
276
281
 
277
282
  // 4. HMR 서버에 POST /restart
278
- const hmrPort = (window as any).__MANDU_HMR_PORT__;
283
+ const hmrPort = (window as unknown as { __MANDU_HMR_PORT__?: number }).__MANDU_HMR_PORT__;
279
284
  if (hmrPort) {
280
285
  await fetch(`http://localhost:${hmrPort}/restart`, { method: 'POST' });
281
286
  }
@@ -1,163 +1,163 @@
1
- /**
2
- * @mandujs/core/email — Resend adapter
3
- *
4
- * Thin HTTP client for the Resend Send-Email API. Zero deps: uses `fetch`.
5
- *
6
- * Contract (https://resend.com/docs/api-reference/emails/send-email):
7
- * POST https://api.resend.com/emails
8
- * Headers:
9
- * Authorization: Bearer <apiKey>
10
- * Content-Type: application/json
11
- * Body: {
12
- * from, to (string | string[]), subject,
13
- * html?, text?, cc?, bcc?, reply_to?, headers?
14
- * }
15
- * Response 200: { id: string, ... }
16
- * Error 4xx/5xx: { name, message, statusCode }
17
- *
18
- * @module email/resend
19
- */
20
-
21
- import {
22
- type EmailMessage,
23
- type EmailSender,
24
- type EmailSendResult,
25
- _coerceRecipients,
26
- _lowercaseHeaders,
27
- _validateMessage,
28
- } from "./index.js";
29
-
30
- /** Config for the Resend adapter. */
31
- export interface ResendOptions {
32
- /** Resend API key. Required. Pull from env (e.g. `RESEND_API_KEY`). */
33
- apiKey: string;
34
- /**
35
- * Override API base URL. Useful for tests or private Resend deployments.
36
- * Default: `"https://api.resend.com"`. Trailing slash is stripped.
37
- */
38
- baseUrl?: string;
39
- /**
40
- * Fetch implementation. Default: `globalThis.fetch`. Injected in tests so
41
- * we never hit the real network.
42
- */
43
- fetch?: typeof globalThis.fetch;
44
- }
45
-
46
- /** Body shape posted to Resend. Fields are omitted when undefined. */
47
- interface ResendRequestBody {
48
- from: string;
49
- to: string[];
50
- subject: string;
51
- html?: string;
52
- text?: string;
53
- cc?: string[];
54
- bcc?: string[];
55
- reply_to?: string;
56
- headers?: Record<string, string>;
57
- }
58
-
59
- /** Successful Resend response shape. We only care about `id`. */
60
- interface ResendSuccessResponse {
61
- id: string;
62
- }
63
-
64
- /** How much of a failed-response body we include in the thrown error. */
65
- const ERROR_BODY_EXCERPT_CHARS = 200;
66
-
67
- /**
68
- * Builds the request body in exactly the shape Resend expects. Exported for
69
- * tests so they can assert the payload shape without running `send()`.
70
- *
71
- * @internal
72
- */
73
- export function _buildResendBody(message: EmailMessage): ResendRequestBody {
74
- const recipients = _coerceRecipients(message);
75
- const body: ResendRequestBody = {
76
- from: message.from,
77
- to: recipients.to,
78
- subject: message.subject,
79
- };
80
- if (message.html !== undefined) body.html = message.html;
81
- if (message.text !== undefined) body.text = message.text;
82
- if (recipients.cc) body.cc = recipients.cc;
83
- if (recipients.bcc) body.bcc = recipients.bcc;
84
- if (message.replyTo !== undefined) body.reply_to = message.replyTo;
85
- const headers = _lowercaseHeaders(message.headers);
86
- if (headers && Object.keys(headers).length > 0) body.headers = headers;
87
- return body;
88
- }
89
-
90
- /**
91
- * Creates a Resend-backed `EmailSender`.
92
- *
93
- * @throws `TypeError` if `apiKey` is missing.
94
- */
95
- export function createResendSender(options: ResendOptions): EmailSender {
96
- if (!options || typeof options.apiKey !== "string" || options.apiKey.length === 0) {
97
- throw new TypeError(
98
- "[@mandujs/core/email/resend] createResendSender: 'apiKey' is required.",
99
- );
100
- }
101
-
102
- const baseUrl = (options.baseUrl ?? "https://api.resend.com").replace(/\/+$/, "");
103
- const fetchImpl = options.fetch ?? globalThis.fetch;
104
- if (typeof fetchImpl !== "function") {
105
- throw new Error(
106
- "[@mandujs/core/email/resend] globalThis.fetch is unavailable — provide `options.fetch` or run in an environment that supplies fetch.",
107
- );
108
- }
109
-
110
- const url = `${baseUrl}/emails`;
111
- const authHeader = `Bearer ${options.apiKey}`;
112
-
113
- async function send(message: EmailMessage): Promise<EmailSendResult> {
114
- _validateMessage(message);
115
- const body = _buildResendBody(message);
116
-
117
- let response: Response;
118
- try {
119
- response = await fetchImpl(url, {
120
- method: "POST",
121
- headers: {
122
- Authorization: authHeader,
123
- "Content-Type": "application/json",
124
- },
125
- body: JSON.stringify(body),
126
- });
127
- } catch (err) {
128
- // Network-level failure (DNS, TCP reset, abort, offline). Re-throw
129
- // with clear context so ops dashboards can distinguish "provider said
130
- // no" from "we never reached the provider".
131
- const cause = err instanceof Error ? err.message : String(err);
132
- throw new Error(
133
- `[@mandujs/core/email/resend] email send failed: ${cause}`,
134
- { cause: err instanceof Error ? err : undefined },
135
- );
136
- }
137
-
138
- if (!response.ok) {
139
- // Read the body best-effort so the error is actionable, but don't let
140
- // a malformed body mask the HTTP status.
141
- let excerpt = "";
142
- try {
143
- const text = await response.text();
144
- excerpt = text.slice(0, ERROR_BODY_EXCERPT_CHARS);
145
- } catch {
146
- excerpt = "<unreadable body>";
147
- }
148
- throw new Error(
149
- `[@mandujs/core/email/resend] email send failed: status=${response.status} body=${excerpt}`,
150
- );
151
- }
152
-
153
- const parsed = (await response.json()) as ResendSuccessResponse;
154
- if (!parsed || typeof parsed.id !== "string") {
155
- throw new Error(
156
- "[@mandujs/core/email/resend] email send failed: provider response missing 'id'.",
157
- );
158
- }
159
- return { id: parsed.id, sentAt: Date.now() };
160
- }
161
-
162
- return { send };
163
- }
1
+ /**
2
+ * @mandujs/core/email — Resend adapter
3
+ *
4
+ * Thin HTTP client for the Resend Send-Email API. Zero deps: uses `fetch`.
5
+ *
6
+ * Contract (https://resend.com/docs/api-reference/emails/send-email):
7
+ * POST https://api.resend.com/emails
8
+ * Headers:
9
+ * Authorization: Bearer <apiKey>
10
+ * Content-Type: application/json
11
+ * Body: {
12
+ * from, to (string | string[]), subject,
13
+ * html?, text?, cc?, bcc?, reply_to?, headers?
14
+ * }
15
+ * Response 200: { id: string, ... }
16
+ * Error 4xx/5xx: { name, message, statusCode }
17
+ *
18
+ * @module email/resend
19
+ */
20
+
21
+ import {
22
+ type EmailMessage,
23
+ type EmailSender,
24
+ type EmailSendResult,
25
+ _coerceRecipients,
26
+ _lowercaseHeaders,
27
+ _validateMessage,
28
+ } from "./index.js";
29
+
30
+ /** Config for the Resend adapter. */
31
+ export interface ResendOptions {
32
+ /** Resend API key. Required. Pull from env (e.g. `RESEND_API_KEY`). */
33
+ apiKey: string;
34
+ /**
35
+ * Override API base URL. Useful for tests or private Resend deployments.
36
+ * Default: `"https://api.resend.com"`. Trailing slash is stripped.
37
+ */
38
+ baseUrl?: string;
39
+ /**
40
+ * Fetch implementation. Default: `globalThis.fetch`. Injected in tests so
41
+ * we never hit the real network.
42
+ */
43
+ fetch?: typeof globalThis.fetch;
44
+ }
45
+
46
+ /** Body shape posted to Resend. Fields are omitted when undefined. */
47
+ interface ResendRequestBody {
48
+ from: string;
49
+ to: string[];
50
+ subject: string;
51
+ html?: string;
52
+ text?: string;
53
+ cc?: string[];
54
+ bcc?: string[];
55
+ reply_to?: string;
56
+ headers?: Record<string, string>;
57
+ }
58
+
59
+ /** Successful Resend response shape. We only care about `id`. */
60
+ interface ResendSuccessResponse {
61
+ id: string;
62
+ }
63
+
64
+ /** How much of a failed-response body we include in the thrown error. */
65
+ const ERROR_BODY_EXCERPT_CHARS = 200;
66
+
67
+ /**
68
+ * Builds the request body in exactly the shape Resend expects. Exported for
69
+ * tests so they can assert the payload shape without running `send()`.
70
+ *
71
+ * @internal
72
+ */
73
+ export function _buildResendBody(message: EmailMessage): ResendRequestBody {
74
+ const recipients = _coerceRecipients(message);
75
+ const body: ResendRequestBody = {
76
+ from: message.from,
77
+ to: recipients.to,
78
+ subject: message.subject,
79
+ };
80
+ if (message.html !== undefined) body.html = message.html;
81
+ if (message.text !== undefined) body.text = message.text;
82
+ if (recipients.cc) body.cc = recipients.cc;
83
+ if (recipients.bcc) body.bcc = recipients.bcc;
84
+ if (message.replyTo !== undefined) body.reply_to = message.replyTo;
85
+ const headers = _lowercaseHeaders(message.headers);
86
+ if (headers && Object.keys(headers).length > 0) body.headers = headers;
87
+ return body;
88
+ }
89
+
90
+ /**
91
+ * Creates a Resend-backed `EmailSender`.
92
+ *
93
+ * @throws `TypeError` if `apiKey` is missing.
94
+ */
95
+ export function createResendSender(options: ResendOptions): EmailSender {
96
+ if (!options || typeof options.apiKey !== "string" || options.apiKey.length === 0) {
97
+ throw new TypeError(
98
+ "[@mandujs/core/email/resend] createResendSender: 'apiKey' is required.",
99
+ );
100
+ }
101
+
102
+ const baseUrl = (options.baseUrl ?? "https://api.resend.com").replace(/\/+$/, "");
103
+ const fetchImpl = options.fetch ?? globalThis.fetch;
104
+ if (typeof fetchImpl !== "function") {
105
+ throw new Error(
106
+ "[@mandujs/core/email/resend] globalThis.fetch is unavailable — provide `options.fetch` or run in an environment that supplies fetch.",
107
+ );
108
+ }
109
+
110
+ const url = `${baseUrl}/emails`;
111
+ const authHeader = `Bearer ${options.apiKey}`;
112
+
113
+ async function send(message: EmailMessage): Promise<EmailSendResult> {
114
+ _validateMessage(message);
115
+ const body = _buildResendBody(message);
116
+
117
+ let response: Response;
118
+ try {
119
+ response = await fetchImpl(url, {
120
+ method: "POST",
121
+ headers: {
122
+ Authorization: authHeader,
123
+ "Content-Type": "application/json",
124
+ },
125
+ body: JSON.stringify(body),
126
+ });
127
+ } catch (err) {
128
+ // Network-level failure (DNS, TCP reset, abort, offline). Re-throw
129
+ // with clear context so ops dashboards can distinguish "provider said
130
+ // no" from "we never reached the provider".
131
+ const causeMsg = err instanceof Error ? err.message : String(err);
132
+ throw new Error(
133
+ `[@mandujs/core/email/resend] email send failed: ${causeMsg}`,
134
+ { cause: err },
135
+ );
136
+ }
137
+
138
+ if (!response.ok) {
139
+ // Read the body best-effort so the error is actionable, but don't let
140
+ // a malformed body mask the HTTP status.
141
+ let excerpt = "";
142
+ try {
143
+ const text = await response.text();
144
+ excerpt = text.slice(0, ERROR_BODY_EXCERPT_CHARS);
145
+ } catch {
146
+ excerpt = "<unreadable body>";
147
+ }
148
+ throw new Error(
149
+ `[@mandujs/core/email/resend] email send failed: status=${response.status} body=${excerpt}`,
150
+ );
151
+ }
152
+
153
+ const parsed = (await response.json()) as ResendSuccessResponse;
154
+ if (!parsed || typeof parsed.id !== "string") {
155
+ throw new Error(
156
+ "[@mandujs/core/email/resend] email send failed: provider response missing 'id'.",
157
+ );
158
+ }
159
+ return { id: parsed.id, sentAt: Date.now() };
160
+ }
161
+
162
+ return { send };
163
+ }