Skip to content

fix(app-shell): 去掉让 useDatasetFields 对 grep 变 binary 的裸控制字节,并给仓库加扫描门禁 - #3388

Merged
yinlianghui merged 3 commits into
mainfrom
claude/issue-5425-nul-bytes-gate
Aug 5, 2026
Merged

fix(app-shell): 去掉让 useDatasetFields 对 grep 变 binary 的裸控制字节,并给仓库加扫描门禁#3388
yinlianghui merged 3 commits into
mainfrom
claude/issue-5425-nul-bytes-gate

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixesobjectstack-ai/objectstack#5425

⚠️ 全文只用 U+XXXX 形式描述控制字符,不贴字节本体(issue Notes 的写作纪律;这一族五次前科里有两次就发生在写规则本身的时候)。

问题

useDatasetFieldCataloginclude: string[] 压成一个标量放进 React.useEffect 依赖数组,再在 effect 内还原。这对编解码原先是 join / split 在一个裸 U+0000 字节上,理由是"字段路径里不可能出现的字符"。

一个裸 U+0000 会让 grep 和 ripgrep 把整个文件判为 binary —— 它们只打印 binary file matches,一行内容都不给:

grep -rn "includeKey" packages/app-shell/src/views/metadata-admin/inspectors/
# => grep: .../useDatasetFields.ts: binary file matches
# 实际匹配行数:0

也就是说这个文件对所有按内容搜索的人和 agent 都是隐形的,对每一个基于 grep 的 lint 也一样,而且没有任何报错提示。git 同样不会告警:它只看前 8000 字节判断 binary,所以偏移量更靠后的控制字节会一直以普通文本形式参与 diff。

改动一:源文件

依赖键改为 JSON.stringify(include) / JSON.parse(includeKey)

选它而不是把分隔符换成转义拼写,是因为它根本不需要"数据里不可能出现的字符"这个前提。把 U+0000 换成一个可打印字符(逗号、竖线)只是把碰撞挪了个位置;JSON 没有这个假设,任何路径值都能无损往返。源文件也因此变成纯 ASCII。

行为不变:这个值只做 effect 依赖比较,从不落库、不上网、不进快照。全仓 grep -a 确认它只有三处引用,都在本文件内。

改动二:仓级门禁(issue 的第 2 点)

新增 scripts/check-control-bytes.mjs,接成 pnpm check:control-bytes 与独立 workflow control-bytes.yml

按载体划范围,而不是按后缀(objectstack#4890):扫描每一个 tracked 的文本文件。危害是 grep 的性质,不是 JavaScript 的性质 —— markdown、YAML、.claude/ 下的 agent 指令一样中招。文本/二进制只按内容判定:先剥掉被查的控制字节再做 UTF-8 严格解码,所以控制字节永远不能成为"不检查它自己"的理由(这正是 git 掉进去的循环)。实测扫 3573 个文本文件、跳过 85 个真二进制资源,零误判。

覆盖面超出 U+0000,这是 issue 明确要求的。但这里有一处我把 issue 的前提测了一遍,结论需要纠正:

字节危害实测(GNU grep 3.11 / ripgrep 14)
U+0000代码搜索直接失效两个工具都拒绝打印匹配行
其余控制字节字面量里的隐形字符,无法 review两个工具都正常打印匹配行

每种字节单独写进 fixture 实测的结果是:只有 U+0000 会触发 binary 判定,U+0001 / U+0002 / U+0007 / U+0008 / U+000B / U+000C / U+000E / U+001A / U+001B / U+001F / U+007F 都不会。所以 objectstack#5157 的教训仍然成立(NUL-only 扫描器确实会漏),但漏掉的那颗的危害是"隐形、不可 review",不是"grep 变瞎"。两类都拦,报告分开说,不把一个夸大的理由套在两类上。

为什么单独一个 workflow:ci.ymllint.yml 都把 '**/*.md'content/**docs/**.changeset/** 放进了 paths-ignore,而 GitHub 没有 job 级路径过滤。markdown 恰恰是这个 bug 最严重那次的载体(objectstack#4890:写"禁止裸 NUL"这条规则的那个 PR 自己往 .claude/ skill 文件里写了一颗)。带路径过滤的门禁看不见那种 PR。changeset-guard.yml 在本仓就是同样理由,它的头注释也这么写。测试会在有人给这个 workflow 加 pathspaths-ignore 时变红。

顺带发现:另有四个文件(未在本 PR 修)

扫描一上线就报出四个既有问题文件,已按 Prime Directive #10 单独立 issue objectstack#5450,不在本 PR 修(超出本 issue 的文件面):

  • packages/core/src/evaluator/listConditional.ts — U+0000
  • packages/core/src/utils/record-title.ts — U+0000
  • packages/fields/src/widgets/PeoplePicker.tsx — U+0000
  • packages/plugin-dashboard/src/DatasetWidget.tsx — U+0001

前三个今天就是 grep 隐形的。它们通过 KNOWN_OFFENDERS 基线放行,让门禁能以 ratchet 方式先上线。

这是基线,不是 skip-list:条目声明了它覆盖哪些字节值,文件多出别的控制字节照样红;文件被修干净或删除而条目没删,扫描同样红。所以修 objectstack#5450 时必须在同一个 PR 里删掉对应条目,两边不可能漂移。形制照本仓 check-lint-coverage.mjs / check-type-check-coverage.mjsDEBT 惯例(带 issue 号、注明是临时债)。

反向验证(方向在跑之前先写死)

四种情况,预测全是"变红",但红的原因各不相同 —— 全部命中:

用例预测实测
干净树绿exit=0
往无关文件注入 U+0000红,grep 失效那一类exit=1,报在 INVISIBLE TO CODE SEARCH
往无关文件注入 U+0001红,隐形字面量那一类;NUL-only 扫描器会漏exit=1;同时确认该文件不含 U+0000,即 NUL-only 扫描器确实看不见
把源码改回 U+0000 分隔符红,且文件重新变 binaryexit=1;grep -n includeKey 打印 0 行,回到 binary file matches
把某个基线文件修干净但不删条目红,stale 条目那一类exit=1,报 stale KNOWN_OFFENDERS

第三行是这次真正想钉住的那条:证明的不是"字节没了",而是"grep 又能看见这个文件了" —— 这才是 issue 报的那个危害。测试里也是这么断言的。

测试

  • scripts/__tests__/check-control-bytes.test.ts(28 例):分类器、覆盖字节集、载体范围、行列定位跨过 8000 字节窗口、基线 ratchet 的四个方向、仓库现状、以及接线本身。
  • packages/app-shell/src/views/metadata-admin/inspectors/useDatasetFields.catalog.test.tsx(7 例):往返行为 —— 单跳、多跳(ADR-0071)、多路径、空数组、内容变化才重跑而不是数组身份变化就重跑,外加一条"路径段里带逗号仍然可用"专门盯着这次修法本身。

pnpm check:control-bytescheck-changeset-fixedcheck-lint-coveragecheck-type-check-coverage 全绿;app-shell type-check 通过;改动文件 eslint 0 error(6 条是未触碰行上既有的 React Compiler warning,该门禁按设计只拦 error)。

没加 changeset

.changeset/README.md 明确列出"内部重构、无 API 变化"不该开 changeset。源码这处是内部 memo key 的编码方式,无任何可观测行为差异;其余都是工具链(脚本、workflow、文档、测试)。仓内也没有"改 packages 必须带 changeset"的门禁 —— ci.yml 的 changeset job 只校验 fixed group 成员资格。如果维护者希望这类修复仍然出现在 release notes 里,补一个 patch 即可。


Generated by Claude Code

…binary to grep, and gate the repo (objectstack#5425)
`useDatasetFieldCatalog` joined and split its React dependency key on a raw
U+0000 byte, chosen as "a character no path can contain". One such byte makes
grep and ripgrep classify the WHOLE file as binary: they print `binary file
matches` and no line at all, so the file was invisible to every content search
and every grep-based lint, with nothing to say so.
The key is now `JSON.stringify(include)` / `JSON.parse(includeKey)`. That needs
no impossible character at all, so the round-trip cannot be broken by any path
value, and the source is plain ASCII.
Second half, which is the part that stops a recurrence: `scripts/check-control-bytes.mjs`
scans every tracked TEXT file (carrier, not extension - objectstack#4890) and
rejects the C0 range apart from tab, line feed and carriage return, plus U+007F.
Wired as `pnpm check:control-bytes` and as its own workflow with no path filter,
because `ci.yml` and `lint.yml` both paths-ignore markdown - the carrier of the
worst instance of this bug.
Measured rather than assumed: on GNU grep 3.11 and ripgrep 14, U+0000 is the
only byte that triggers binary classification; U+0001 and the rest print the
matching line normally. Both classes fail the gate, for the two different
reasons the report names.
Four pre-existing offenders in other packages are baselined in KNOWN_OFFENDERS
against objectstack#5450 rather than fixed here (out of this issue's scope). The
baseline is a ratchet, not a skip-list: the scan fails on a stale entry.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
The separator swap in the previous commit is only safe if the encode/decode
pair still yields the same walked paths, so pin the behaviour it is responsible
for: single-hop and multi-hop include paths, several paths at once, the empty
list (the old ternary's job), and re-running on content change but not on a new
array identity.
One case is aimed at the fix itself: a path segment containing a comma still
round-trips. The obvious repair for a U+0000 separator is "pick a printable
character no name contains", which merely relocates the collision; JSON has no
such assumption and the test says so.
The client mock is deliberately identity-stable. The real useMetadataClient
memoizes, and the effect lists `client` in its deps, so a per-render object
loops forever and OOMs the worker instead of failing an assertion.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
@vercel

vercelBot commented Aug 5, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectuiIgnoredIgnoredAug 5, 2026 12:43pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-D64ZDYnB.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.47KB3.09KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)18.38KB4.49KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)3.65KB1.42KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.25KB0.53KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)477.32KB104.75KB
core (index.js)2.25KB0.80KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)136.23KB34.75KB
fields (index.js)227.18KB55.72KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.65KB1.06KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)9.48KB3.27KB
i18n (useObjectLabel.js)26.14KB6.07KB
i18n (useSafeTranslation.js)3.26KB1.44KB
layout (index.js)38.53KB10.71KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.05KB1.53KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)44.98KB12.37KB
plugin-charts (index.js)61.04KB17.31KB
plugin-chatbot (index.js)180.09KB42.72KB
plugin-dashboard (index.js)112.01KB28.86KB
plugin-designer (index.js)210.51KB42.51KB
plugin-detail (index.js)231.29KB57.04KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)111.54KB26.97KB
plugin-gantt (index.js)162.55KB39.57KB
plugin-grid (index.js)185.08KB49.04KB
plugin-kanban (index.js)47.89KB13.18KB
plugin-list (index.js)105.02KB25.36KB
plugin-map (index.js)16.81KB5.24KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.55KB10.59KB
plugin-timeline (index.js)25.76KB7.33KB
plugin-tree (index.js)8.34KB2.82KB
plugin-view (index.js)83.67KB20.43KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)19.28KB6.38KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.02KB0.55KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)2.46KB1.21KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)0.20KB0.18KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

Matches scripts/check-changeset-no-major.mjs: resolve(process.argv[1]) against
fileURLToPath(import.meta.url), which survives relative and symlinked paths.
Flag handling now sits inside that guard, so importing the module for tests can
never trigger a repo scan or a process.exit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Main entry (gzip)28.1 KB350 KB
Entry fileindex-D64ZDYnB.js
StatusPASS

📦 Bundle Size Report

PackageSizeGzipped
app-shell (index.js)8.47KB3.09KB
app-shell (runtime-config.js)7.42KB2.32KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)7.57KB2.97KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)1.17KB0.53KB
auth (AuthProvider.js)22.10KB4.37KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.13KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.64KB2.21KB
auth (SocialSignInButtons.js)9.60KB3.89KB
auth (UserMenu.js)3.40KB1.22KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)35.76KB9.11KB
auth (createAuthenticatedFetch.js)4.37KB1.69KB
auth (index.js)2.35KB1.07KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)4.91KB0.87KB
auth (useIsWorkspaceAdmin.js)1.61KB0.85KB
collaboration (CommentThread.js)18.38KB4.49KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)3.65KB1.42KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.25KB0.53KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)477.32KB104.75KB
core (index.js)2.25KB0.80KB
create-plugin (index.js)9.28KB2.98KB
data-objectstack (index.js)136.23KB34.75KB
fields (index.js)227.18KB55.72KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (i18n.js)4.32KB1.77KB
i18n (index.js)2.65KB1.06KB
i18n (pickLocalized.js)1.70KB0.83KB
i18n (provider.js)9.48KB3.27KB
i18n (useObjectLabel.js)26.14KB6.07KB
i18n (useSafeTranslation.js)3.26KB1.44KB
layout (index.js)38.53KB10.71KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.74KB
mobile (index.js)1.50KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.71KB0.42KB
mobile (useResponsiveConfig.js)1.36KB0.63KB
mobile (useSpecGesture.js)4.05KB1.53KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)8.75KB3.06KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)3.67KB1.12KB
permissions (evaluator.js)4.41KB1.44KB
permissions (index.js)0.91KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.52KB
permissions (usePermissions.js)1.55KB0.71KB
plugin-ai (index.js)15.71KB3.79KB
plugin-calendar (index.js)44.98KB12.37KB
plugin-charts (index.js)61.04KB17.31KB
plugin-chatbot (index.js)180.09KB42.72KB
plugin-dashboard (index.js)112.01KB28.86KB
plugin-designer (index.js)210.51KB42.51KB
plugin-detail (index.js)231.29KB57.04KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)111.54KB26.97KB
plugin-gantt (index.js)162.55KB39.57KB
plugin-grid (index.js)185.08KB49.04KB
plugin-kanban (index.js)47.89KB13.18KB
plugin-list (index.js)105.02KB25.36KB
plugin-map (index.js)16.81KB5.24KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)40.55KB10.59KB
plugin-timeline (index.js)25.76KB7.33KB
plugin-tree (index.js)8.34KB2.82KB
plugin-view (index.js)83.67KB20.43KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.71KB3.53KB
providers (index.js)0.44KB0.22KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.67KB2.37KB
react (LazyPluginLoader.js)3.77KB1.33KB
react (SchemaRenderer.js)19.28KB6.38KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)1.02KB0.55KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)4.09KB1.74KB
sdui-parser (index.js)4.47KB2.03KB
sdui-parser (parse.js)10.04KB2.82KB
sdui-parser (types.js)0.29KB0.24KB
sdui-parser (validate.js)4.69KB1.48KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)0.20KB0.18KB
types (crud.js)0.20KB0.18KB
types (data-display.js)0.20KB0.18KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.87KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-retry.js)4.32KB2.02KB
types (index.js)2.46KB1.21KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)0.20KB0.18KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)0.20KB0.18KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@yinlianghui
yinlianghui marked this pull request as ready for review August 5, 2026 12:58
@yinlianghui
yinlianghui added this pull request to the merge queueAug 5, 2026
Merged via the queue into main with commit 7c9a4e5Aug 5, 2026
16 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-5425-nul-bytes-gate branch August 5, 2026 12:59
Sign up for freeto 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.

objectui: two raw U+0000 bytes in useDatasetFields.ts make the whole file binary to grep

2 participants

@yinlianghui@claude