From 1674f72b31d6a179996d39233703f4491ee381e8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 4 Aug 2026 05:05:24 +0000 Subject: [PATCH] =?UTF-8?q?docs(protocol/kernel):=20config-resolution=20?= =?UTF-8?q?=E7=9A=84=20email=20=E7=A4=BA=E4=BE=8B=E6=94=B9=E7=94=A8?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=E5=AD=98=E5=9C=A8=E7=9A=84=E9=94=AE=E4=B8=8E?= =?UTF-8?q?=E5=8F=96=E5=80=BC=20(#5105)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 「Example 3: Development Overrides」的示例有三处不对应任何真实配置面: `provider: 'sendgrid'` + `fromAddress`、开发覆盖 `provider: 'console'`、 env 覆盖 `OS_EMAIL_PROVIDER=mailhog`。对照 packages/spec/src/system/email-config.zod.ts:EmailProviderSchema 是 z.enum(['log','resend','postmark']),三个取值一个都不在里面;发件人键叫 defaultFrom(且是 { name?, address } 对象),不叫 fromAddress。 改用今天在 main 上就成立的取值:生产段 provider: 'resend' + defaultFrom, 并补上 apiKey(非 log provider 缺 key 时 serve.ts 会回落到 LogTransport); 开发覆盖 provider: 'log'。刻意不写 'smtp' —— 那要等 #5104 把它加进 EmailProviderSchema 之后才成立。 env 覆盖层顺带修实:单纯把 mailhog 改成 log 会与开发段重复、令 「further override」失去示范意义,故按本页 §Merge Strategies 自己教的深合并 语义补一行 OS_EMAIL_FROM 覆盖继承下来的 defaultFrom。层叠要点(生产 → 开发 → env 的优先级)因此比改之前更完整。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_017MCKJaEomEqg4tvz4SzdNd --- .changeset/config-resolution-email-example.md | 16 ++++++++++++++++ .../docs/protocol/kernel/config-resolution.mdx | 16 +++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 .changeset/config-resolution-email-example.md diff --git a/.changeset/config-resolution-email-example.md b/.changeset/config-resolution-email-example.md new file mode 100644 index 0000000000..e28160ba5a --- /dev/null +++ b/.changeset/config-resolution-email-example.md @@ -0,0 +1,16 @@ +--- +--- + +docs(protocol/kernel): config-resolution 的 email 示例改用真实存在的键与取值 (#5105) + +`content/docs/protocol/kernel/config-resolution.mdx`「Example 3: Development Overrides」的示例有三处不对应任何真实配置面:生产段写 `provider: 'sendgrid'` + `fromAddress`,开发覆盖写 `provider: 'console'`,env 覆盖写 `OS_EMAIL_PROVIDER=mailhog`。对照 `packages/spec/src/system/email-config.zod.ts`:`EmailProviderSchema` 是 `z.enum(['log','resend','postmark'])`,`sendgrid` / `console` / `mailhog` 三个取值一个都不在里面;承载发件人的键叫 `defaultFrom`,不叫 `fromAddress`,而且它是 `{ name?, address }` 对象(`EmailAddressConfigSchema`),不是字符串。照这一页粘贴出来的 `email` 配置,`fromAddress` 会被静默丢弃,provider 则要到 `makeTransport` 才抛 unknown provider —— 错误全部推迟到运行时,正是 Prime Directive #10 说的「advertise a capability the runtime doesn't deliver」。 + +这一页本身带着一条 warn callout,说明 `database` / `http` / `secrets.provider` 等嵌套形状只是意图示意、并非 paste-ready。但 `email` 恰恰**不在**那份豁免名单里,而且它是真实配置面:`packages/cli/src/commands/serve.ts` 的 `cap === 'email'` 分支实打实地读 `config.email.{provider,apiKey,defaultFrom,retries}`,并按 `OS_EMAIL_*` 覆盖它们。所以这段示例不该靠「示意」免责,它应当是真的。 + +改法是把三处换成今天在 `main` 上就成立的取值,而不是改讲一个与 email 无关的例子 —— 这一页要讲的是配置层叠,email 只是载体,换载体会连累上下文。生产段用 `provider: 'resend'` + `defaultFrom: { name, address }`,并补上 `apiKey`:非 `log` 的 provider 缺 key 时 `serve.ts` 会打 warning 并回落到 LogTransport,示例不写它就等于示范一份「看着配好了、其实没发出去」的配置,是同一个缺陷换个方向再犯。开发覆盖用 `provider: 'log'`。 + +**没有写 `smtp`。** `EmailProviderSchema` 目前仍是三值枚举,把 `smtp` 加进去是 #5104 的工作、尚未落地;此刻写它就是把本单刚修掉的 declared ≠ implemented 重新引入一遍。等 #5104 落地后这一页要不要提 SMTP,是那一单自己的事。 + +env 覆盖层顺带修实:原文只有一行 `OS_EMAIL_PROVIDER=mailhog`,若单纯改成 `=log`,而开发配置段已经选了 `log`,这一层就退化成一条无效果的示例,「further override」讲不通。现按本页 §Merge Strategies 自己教的「对象深合并、原始值替换」把语义补全:开发文件只替换了 `provider`,`apiKey` / `defaultFrom` 仍从生产配置继承,于是再加一行 `OS_EMAIL_FROM=Dev Mailer ` 覆盖 `defaultFrom`。`OS_EMAIL_FROM` 是真实变量(`content/docs/deployment/environment-variables.mdx` 有登记),`serve.ts` 也确实解析 `Name ` 两种写法。层叠要点(生产配置 → 开发覆盖 → env 覆盖的优先级)因此比改之前更完整,而不是被讲丢。 + +该代码块未加 `{/* os:check */}` 标记:它是把 shell 赋值行混在 `typescript` 围栏里的伪代码片段,本就不可编译,加标记只会让 `check:skill-examples` 变红。纯文档,releases nothing。 diff --git a/content/docs/protocol/kernel/config-resolution.mdx b/content/docs/protocol/kernel/config-resolution.mdx index 4394b4895f..c5d3b5a1e4 100644 --- a/content/docs/protocol/kernel/config-resolution.mdx +++ b/content/docs/protocol/kernel/config-resolution.mdx @@ -801,13 +801,15 @@ NODE_ENV=eu node server.js # Loads objectstack.config.eu.ts ```typescript // Production config +// objectstack.config.ts { stripe: { apiKey: process.env.STRIPE_API_KEY, // Live key }, email: { - provider: 'sendgrid', - fromAddress: 'noreply@company.com', + provider: 'resend', // 'log' | 'resend' | 'postmark' + apiKey: process.env.RESEND_API_KEY, // Required by every non-'log' provider + defaultFrom: { name: 'Acme', address: 'noreply@company.com' }, }, } @@ -818,12 +820,16 @@ NODE_ENV=eu node server.js # Loads objectstack.config.eu.ts apiKey: 'sk_test_...', // Test key }, email: { - provider: 'console', // Log emails instead of sending + provider: 'log', // Print emails to stdout instead of sending }, } -// Developer can further override with .env.local -OS_EMAIL_PROVIDER=mailhog # Use local Mailhog for testing +// Developer can further override with .env.local — ENVIRONMENT beats both +// files. Merging is per key (deep merge, see above): the development file +// only replaces `provider`, so `apiKey` / `defaultFrom` are still inherited +// from the production config until something names them too. +OS_EMAIL_PROVIDER=log # Force the log transport +OS_EMAIL_FROM=Dev Mailer # ...and now defaultFrom, too ``` ## Best Practices