发现于 #6462 (为 bulk-write hook 预算护栏找「spec 导出运行期上限常量」的先例时对 origin/main ecff951 逐条核实)。属 PD #10 的范围外发现,未在该 PR 中修 。
事实(origin/main ecff951,已核对)
packages/spec/src/api/export.zod.ts:397
/**
* Hard ceiling on rows accepted by a single async import job. ...
*/
export const IMPORT_JOB_MAX_ROWS = 50_000 ;
packages/rest/src/rest-server.ts:1309-1310
/** Hard ceiling on rows per async import job (mirrors spec IMPORT_JOB_MAX_ROWS). */
const IMPORT_JOB_MAX_ROWS = 50_000 ;
rest 是这个上限唯一的执行方 (rest-server.ts:5572 的 maxRows:,以及 5577 那条
413 的补充文案 split the file into batches of ${IMPORT_JOB_MAX_ROWS}),而它读的是自己那份
本地字面量,不是 spec 的导出。两份定义之间只有一句注释("mirrors spec …")相连;
packages/rest 已经依赖 @objectstack/spec,import 是现成的。
为什么值得记(而不是「反正数一样」)
改一处不会让另一处红。 没有任何 gate 比较这两个数:check:api-surface 只记录
spec 导出存在 ,不记录谁读它;rest 侧没有对照断言。把 spec 的
IMPORT_JOB_MAX_ROWS 调成 20_000,pnpm test / pnpm typecheck / 全部
check:* 仍然全绿,而真正生效的上限一动没动 —— 声明与执行分家,只是恰好同值。
失效方向是「文档说一套、系统做一套」。 spec 的那段 TSDoc 会进
content/docs/references/,即上限的对外说明 ;真正拒绝请求的是 rest 的字面量。
两者一旦分叉,用户读到的和拿到的 413 就不是一个数,而 413 文案里还内插了
rest 的那一份,所以连报错都会自洽地说谎。
和仓里正在收敛的方向相反。 「一条规则一处定义、两处复用」是 ObjectQL.update 的 data.id 不做标量测试 —— 载荷里的算子对象被当成主键绑定,且盖过显式 options.multi: true #5748 / data.id 是算子对象 + multi: true 时,{"$in":[...]} 作为普通列进入 updateMany 的 SET 载荷,写向主键列 #6262 /
update 的 **by-id** 路径同样把非标量 data.id 交给驱动写主键列(#6262 的孪生形状,where.id 胜出时) #6435 (asScalarId 刻意不导出第三份拼写)、{ field: {} }(零个操作符的字段约束)在同仓有三个答案:driver-sql 组合子内 TRUE、顶层抛 INVALID_FILTER、formula/driver-memory FALSE #5240 / sharing: DELETE /sharing/rules/:idOrName answers 500 for both address forms — rules cannot be deleted over REST #4434 (五个后端五种答案)反复
写下的口径。这里是同一形状的更简单版本:一个数,两份定义。
可达性 / 影响
今天没有用户会撞到 —— 两个数当下相等(50_000),所以这是 observation-class:
不是活体缺陷,是一处「下一次编辑才会引爆」的漂移面。放这里等分诊评级,不自评严重度
(cloud#1004 的「转义细节」后来是 P0 filter bypass;cloud#897 自评的影响段本身就是错的)。
建议动作
rest 侧删掉本地 const,改 import { IMPORT_JOB_MAX_ROWS } from '@objectstack/spec'
(或 @objectstack/spec/api 子路径,按该文件既有 import 风格),一处定义两处复用;
顺带确认 413 文案与 maxRows: 两个读点都换过来。若认为上限本该由部署侧配置而不是
spec 常量,那是另一个决定 —— 但那也应当收敛成一处 ,而不是继续两份。
关联:#6462 (发现来源,在 data/bulk-write-hook-conformance.ts 里为「spec 导出上限常量」
找先例时命中;该模块自身的双定义是 contract-first 的临时 状态,已在文件内写明由
#5574 engine 半边收敛,与本单的长期双份不同)。
Generated by Claude Code
发现于 #6462(为 bulk-write hook 预算护栏找「spec 导出运行期上限常量」的先例时对 origin/main
ecff951逐条核实)。属 PD #10 的范围外发现,未在该 PR 中修。事实(origin/main
ecff951,已核对)packages/spec/src/api/export.zod.ts:397packages/rest/src/rest-server.ts:1309-1310rest 是这个上限唯一的执行方(
rest-server.ts:5572的maxRows:,以及 5577 那条413的补充文案split the file into batches of ${IMPORT_JOB_MAX_ROWS}),而它读的是自己那份本地字面量,不是 spec 的导出。两份定义之间只有一句注释("mirrors spec …")相连;
packages/rest已经依赖@objectstack/spec,import 是现成的。为什么值得记(而不是「反正数一样」)
check:api-surface只记录spec 导出存在,不记录谁读它;rest 侧没有对照断言。把 spec 的
IMPORT_JOB_MAX_ROWS调成 20_000,pnpm test/pnpm typecheck/ 全部check:*仍然全绿,而真正生效的上限一动没动 —— 声明与执行分家,只是恰好同值。content/docs/references/,即上限的对外说明;真正拒绝请求的是 rest 的字面量。两者一旦分叉,用户读到的和拿到的
413就不是一个数,而413文案里还内插了rest 的那一份,所以连报错都会自洽地说谎。
data.id不做标量测试 —— 载荷里的算子对象被当成主键绑定,且盖过显式options.multi: true#5748 /data.id是算子对象 +multi: true时,{"$in":[...]}作为普通列进入updateMany的 SET 载荷,写向主键列 #6262 /update的 **by-id** 路径同样把非标量data.id交给驱动写主键列(#6262 的孪生形状,where.id胜出时) #6435(asScalarId刻意不导出第三份拼写)、{ field: {} }(零个操作符的字段约束)在同仓有三个答案:driver-sql 组合子内 TRUE、顶层抛 INVALID_FILTER、formula/driver-memory FALSE #5240 / sharing: DELETE /sharing/rules/:idOrName answers 500 for both address forms — rules cannot be deleted over REST #4434(五个后端五种答案)反复写下的口径。这里是同一形状的更简单版本:一个数,两份定义。
可达性 / 影响
今天没有用户会撞到 —— 两个数当下相等(50_000),所以这是 observation-class:
不是活体缺陷,是一处「下一次编辑才会引爆」的漂移面。放这里等分诊评级,不自评严重度
(cloud#1004 的「转义细节」后来是 P0 filter bypass;cloud#897 自评的影响段本身就是错的)。
建议动作
rest 侧删掉本地
const,改import { IMPORT_JOB_MAX_ROWS } from '@objectstack/spec'(或
@objectstack/spec/api子路径,按该文件既有 import 风格),一处定义两处复用;顺带确认
413文案与maxRows:两个读点都换过来。若认为上限本该由部署侧配置而不是spec 常量,那是另一个决定 —— 但那也应当收敛成一处,而不是继续两份。
关联:#6462(发现来源,在
data/bulk-write-hook-conformance.ts里为「spec 导出上限常量」找先例时命中;该模块自身的双定义是 contract-first 的临时状态,已在文件内写明由
#5574 engine 半边收敛,与本单的长期双份不同)。
Generated by Claude Code