Skip to content

Commit 9bf2eeb

Browse files
committed
fix(qa/dogfood): showcase 端点 fixture 显式声明它不携带 functions,而不是靠 JSON.stringify 偷偷丢掉
`showcase-declarative-endpoints.dogfood.test.ts` 用 `JSON.stringify(showcaseStack)` 手搓一份 artifact 顶替 `objectstack build`, 但只做了后者的一半:真实构建先跑 `lowerCallables` 把每个 callable 换成字符串 ref,并把函数本体放进 sibling ESM 模块;`JSON.stringify` 没有这一步,它只是把 函数值的键整个省略。 于是这份 artifact 从来就没携带过 showcase 的任何 function —— 它只是"看起来" 携带了:裸条目(`sweepProjectHealth: fn`)连键一起消失,剩下 `functions: {}`, 照样 parse 通过。两个函数都被静默丢弃,没人看得见。 showcase 换成诚实的声明形之后这份静默就破了:`{ handler: fn, effect: 'writes' }` 会保留对象、只丢 `handler`,留下 `{ effect: 'writes' }` —— 一个为自己并不携带 的函数声明了 effect 的条目,`FlowFunctionEntrySchema` 四个成员一致拒收,完全 正确。 改为显式剔除 `functions` 并写明原因:本 boot 真正运行的函数来自交给 `bootStack` 的**活栈**,不来自这个文件;该文件的职责是把 `apis:` 块喂给 `MetadataPlugin`。静默丢失变成声明式省略。
1 parent 375ded2 commit 9bf2eeb

1 file changed

Lines changed: 21 additions & 1 deletion

File tree

packages/qa/dogfood/test/showcase-declarative-endpoints.dogfood.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,27 @@ beforeAll(async () => {
103103
process.chdir(SHOWCASE_DIR);
104104
tempDir = mkdtempSync(join(tmpdir(), 'os-e8-endpoints-'));
105105
const artifactPath = join(tempDir, 'objectstack.json');
106-
writeFileSync(artifactPath, JSON.stringify(showcaseStack));
106+
// `functions` is dropped DELIBERATELY, and saying so is the point (#4976).
107+
//
108+
// This line stands in for `objectstack build`, but it is only half of it: the
109+
// real build runs `lowerCallables` first, replacing every callable with a
110+
// string ref and carrying the functions themselves in a sibling ESM module.
111+
// A plain `JSON.stringify` has no such step — it simply omits function-valued
112+
// keys — so this artifact never carried the showcase's functions at all. It
113+
// merely LOOKED like it did, because a bare entry (`sweepProjectHealth: fn`)
114+
// vanishes key and all and leaves `functions: {}` behind, which parses.
115+
//
116+
// That silence broke the moment the showcase spelled its writer the honest,
117+
// declared way: `{ handler: fn, effect: 'writes' }` keeps the object and drops
118+
// only `handler`, leaving `{ effect: 'writes' }` — an entry declaring an
119+
// effect for a function it does not carry, which `FlowFunctionEntrySchema`
120+
// refuses in all four of its members, exactly as it should.
121+
//
122+
// Nothing is lost by omitting the key: the functions this boot actually runs
123+
// come from the LIVE stack handed to `bootStack` below, not from this file,
124+
// whose job is to give `MetadataPlugin` the `apis:` block to ingest.
125+
const { functions: _functionsLiveOnly, ...artifact } = showcaseStack as Record<string, unknown>;
126+
writeFileSync(artifactPath, JSON.stringify(artifact));
107127

108128
stack = await bootStack(showcaseStack, {
109129
// The `flow`-typed endpoint delegates to `IAutomationService.execute`;

0 commit comments

Comments
 (0)