Skip to content

fix(grid): 删掉 bulkEnabled 推导 —— 这个 spec key 已是墓碑 (#3002) - #3053

Merged
os-zhuang merged 1 commit into
mainfrom
claude/bulk-enabled-retired
Jul 30, 2026
Merged

fix(grid): 删掉 bulkEnabled 推导 —— 这个 spec key 已是墓碑 (#3002)#3053
os-zhuang merged 1 commit into
mainfrom
claude/bulk-enabled-retired

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#3031 的跟进修复。#3031 的一半设计是错的,浏览器实测把它抓出来了。

发生了什么

#3031 让选择栏折叠两路来源:view 的 bulkActions 名字解析到 objectDef.actions,以及对象 action 上的 ActionSchema.bulkEnabled第二路是死的。

@objectstack/spec 17.0.0 在 #3896 审计收尾里退役了 action.bulkEnabledframework#4054 12a19a88a)—— 这个提交在 #3031 进行途中落地,我定设计时读到的 spec 源码里这个 key 还在。

它现在是 retiredKey() 墓碑,不是「被忽略」而是硬拒绝

✗ `action.bulkEnabled` was removed in @objectstack/spec 17.0.0 (#3896 audit
  close-out) — the multi-select toolbar is driven by the LIST VIEW's
  `bulkActions` / `bulkActionDefs`, never by this flag, so setting it changed
  nothing. Delete the key and declare the action in the view's `bulkActions`
  instead.
✗ Compile failed — fix errors above before starting dev server

defineStack 直接拒绝编译,后端起不来。所以那条推导分支永远不可能执行,而 #3031 的 changeset 还在教作者用一个会让应用启动失败的 key。

是浏览器验证发现的 —— 我在 showcase 里加 bulkEnabled: true 想跑端到端,后端直接拒绝启动。单测和 type-check 都抓不到:折叠函数读的是带索引签名的 NamedActionDef,退役 key 不会显现成 never(见 objectui 的 spec-tombstone 经验)。

改动

墓碑自己给的处方正是 #3031 活下来的那一半 —— "declare the action in the view's bulkActions instead"。所以 resolveBulkActions 现在只折叠两路词汇:

  1. view JSON 里内联写的 bulkActionDefs
  2. bulkActions 里的名字,解析到对象 action 后提升成该 def。

折叠之下的一切都没变:提升出的 def 照样带 action 的 label / icon / visible / 确认文案 / 参数,照样走 BulkActionDialog(params → confirm → progress → result),照样逐记录带 _rowRecord 派发、逐条归因失败。对象 action 上残留的 bulkEnabled: true 现在是惰性的,不再是进入选择栏的第二条路。

DERIVED_BULK_BATCH_SIZEPROMOTED_BULK_BATCH_SIZE(未导出到包外,只在本模块和测试里用)。

验证

真实 console + 真实后端(objectstack worktree 起 showcase 于 :3010,console dev 从本 worktree 起于 :5182):

  • view 声明 bulkActions: ['showcase_mark_done', 'showcase_recalc_estimate'],两个名字都被提升 —— 选择栏显示的是 action 自己的标签 "Mark Done" / "Recalculate Estimate"(不是 humanize 出来的 "Showcase Mark Done")和 action 的图标。
  • 全选 10 条点 Mark Done → 10 个 POST /api/v1/actions/showcase_task/showcase_mark_done 全部 200,逐记录一次。
  • 数据侧确认:10 条记录服务端全部变成 done: true, progress: 100。修复前这条路径零请求、零改动。

测试plugin-grid 44 文件 / 351 测试全绿;types + plugin-grid + app-shell type-check 31 任务全过;改动文件 0 lint error。

🤖 Generated with Claude Code

…stone (#3002)

Follow-up to #3031. `@objectstack/spec` 17.0.0 retired `action.bulkEnabled` in
the #3896 audit close-out (framework#4054), which landed while #3031 was in
flight — the spec source still carried the key when its design was settled.

It is now a retiredKey() tombstone, so it is not merely ignored: defineStack
HARD-REJECTS a config that sets it and the backend refuses to boot. The
derivation branch could never run, and #3031's changeset pointed authors at a
key that breaks their app. Browser verification against a real showcase backend
is what surfaced it.

The tombstone prescribes the surviving path — "declare the action in the view's
`bulkActions` instead" — which is #3031's other half, and the half the
end-to-end run exercised: naming `showcase_mark_done` in the view issued one
POST /api/v1/actions/showcase_task/showcase_mark_done per selected record
(10/10 → done: true, progress: 100 server-side).

Everything downstream of the fold is unchanged. A stale `bulkEnabled: true` is
now inert rather than a second path into the bar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Jul 30, 2026 2:21pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 27.9 KB 350 KB
Entry file index-BUrkPMdY.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.20KB 2.97KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.12KB 3.41KB
auth (LoginForm.js) 17.86KB 5.29KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.43KB 2.09KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 458.88KB 100.29KB
core (index.js) 2.16KB 0.78KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 134.67KB 34.24KB
fields (index.js) 222.07KB 54.35KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.46KB 0.96KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 5.37KB 1.72KB
i18n (useObjectLabel.js) 25.17KB 5.80KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 38.45KB 10.67KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.05KB 1.53KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 6.84KB 2.42KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.90KB 12.35KB
plugin-charts (index.js) 60.52KB 17.11KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 111.59KB 28.74KB
plugin-designer (index.js) 210.51KB 42.50KB
plugin-detail (index.js) 221.42KB 54.12KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 106.54KB 25.88KB
plugin-gantt (index.js) 162.26KB 39.53KB
plugin-grid (index.js) 183.79KB 48.20KB
plugin-kanban (index.js) 47.82KB 13.18KB
plugin-list (index.js) 102.39KB 24.18KB
plugin-map (index.js) 16.80KB 5.24KB
plugin-markdown (index.js) 13.65KB 4.67KB
plugin-report (index.js) 40.32KB 10.53KB
plugin-timeline (index.js) 25.75KB 7.32KB
plugin-tree (index.js) 8.36KB 2.81KB
plugin-view (index.js) 85.95KB 21.02KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 3.47KB 1.54KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (index.js) 2.07KB 0.99KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.04KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 1.08KB 0.64KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@os-zhuang
os-zhuang merged commit 19e9fa0 into main Jul 30, 2026
16 checks passed
@os-zhuang
os-zhuang deleted the claude/bulk-enabled-retired branch July 30, 2026 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant