@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
|
@@ -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
|
|
278
|
+
delete winRec[key];
|
|
274
279
|
}
|
|
275
280
|
}
|
|
276
281
|
|
|
277
282
|
// 4. HMR 서버에 POST /restart
|
|
278
|
-
const hmrPort = (window as
|
|
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
|
}
|
package/src/email/resend.ts
CHANGED
|
@@ -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
|
|
132
|
-
throw new Error(
|
|
133
|
-
`[@mandujs/core/email/resend] email send failed: ${
|
|
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
|
-
}
|
|
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
|
+
}
|