Skip to content

fix: ListView/DetailView/DetailSection lookup field display — $expand race condition, schema enrichment & data adapter raw GET populate routing#944

Merged
hotlong merged 15 commits intomainfrom
copilot/fix-field-type-display-issue
Mar 2, 2026
Merged

fix: ListView/DetailView/DetailSection lookup field display — $expand race condition, schema enrichment & data adapter raw GET populate routing#944
hotlong merged 15 commits intomainfrom
copilot/fix-field-type-display-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 1, 2026

Lookup and master_detail fields render raw IDs (o1, p1, 2) instead of expanded names across list and detail views due to six independent root causes spanning the UI layer and the data adapter layer.

Bug 1: ListView $expand race condition

expandFields depends on async objectDef, but the data fetch fires immediately on mount when objectDef is still nullexpandFields = [] → no $expand → server returns raw IDs.

  • Added objectDefLoaded flag; data fetch is gated until schema resolution completes
  • Flag is set in finally block to handle both success and error paths
  • objectDefLoaded and objectDef are reset to false/null when objectName/dataSource changes, preventing stale $expand on navigation

Bug 2: DetailView never passes $expand

findOne() was called without $expand and without loading objectSchema at all — unlike ObjectGrid which does both.

  • Fetch flow now mirrors ObjectGrid: getObjectSchema()buildExpandFields()findOne(objectName, id, { $expand })
  • objectSchema is forwarded to DetailSection for field enrichment
  • Graceful fallback when getObjectSchema is absent on the DataSource
  • objectSchema and data are cleared when starting a new fetch cycle to prevent stale metadata on navigation between objects/records
  • findOne() is only called with 3 args when $expand params exist; falls back to the original 2-arg signature when no expand is needed

Bug 3: DetailSection ignores fields without explicit type

When field.type is unset, displayValue falls through to String(value) — even if objectSchema has the metadata to select a proper CellRenderer.

  • New optional objectSchema prop on DetailSectionProps
  • Missing metadata properties (options, currency, precision, format, reference_to, reference_field) are merged from objectSchema regardless of whether field.type is explicitly set — matching ObjectGrid's enrichment behavior
  • When field.type is not set, the type is also resolved from objectSchema
  • Explicit field.type always wins for the resolved type

Bug 4: ObjectStackAdapter find() silently drops $expand

The @objectstack/client v3.0.10's data.find() (GET) does not support expand/populate in its QueryOptions interface. The server's REST plugin (rest-api-plugin / RestServer) does not register a POST /data/:object/query endpoint — only the dispatcher-plugin does. The only viable path for field expansion is GET /data/:object which routes to findData() that supports the populate query parameter.

  • When $expand is present, find() now makes a raw GET /api/v1/data/:object?populate=field1,field2&top=50&... request via rawFindWithPopulate(), bypassing the client SDK
  • Uses populate (comma-separated field names) which is the format the server's findDatatoQueryAST pipeline processes for lookup expansion
  • The adapter stores the fetchImpl (custom or globalThis.fetch) from the constructor config for making these raw requests
  • rawFindWithPopulate() detects /api/v\d+ suffix in baseUrl to avoid doubling the API path prefix (e.g., baseUrl: '/api/v1' won't produce /api/v1/api/v1/data/...)
  • Falls back to the original client.data.find() path when no expand is needed

Bug 5: ObjectStackAdapter findOne() ignores params entirely

The findOne() method declared its params argument as _params (underscore prefix = unused), so $expand was never sent to the server. Additionally, the installed server v3.0.10's getData() does not support expand or populate at all.

  • When $expand is present, findOne() now routes through findData via raw GET /api/v1/data/:object?filter={"_id":"..."}&populate=field1,field2&top=1
  • Falls back to the original client.data.get() path when no expand is needed

Bug 6: Server API format mismatch (root cause of earlier adapter-level failures)

In @objectstack/spec v3.0.10:

  • findData() supports populate (comma-separated string) but does not have $expand/expandpopulate normalization (added in a newer spec version)
  • getData() does not support expand/populate at all
  • The REST plugin does not register POST /data/:object/query — only the dispatcher plugin does

Previous attempts using client.data.query() (POST) silently failed because the endpoint doesn't exist in the REST plugin, and attempts to use expand (object map format) failed because v3.0.10 only processes populate (comma-separated string).

  • Fixed by using raw HTTP GET requests with populate as a URL query parameter, which correctly routes through the REST plugin's GET /data/:objectfindData() pipeline

Tests

Tests across DataFetch, DetailView, DetailSection, and data-objectstack covering $expand param propagation, fetch gating, schema enrichment, explicit-type precedence, raw GET routing with populate format, findOne expand support, Authorization header propagation, response envelope unwrapping, and baseUrl /api/v1 double-prefix prevention. All 42 build tasks pass. CodeQL: 0 alerts.

Original prompt

This section details on the original issue you should resolve

<issue_title>ListView/DetailView/DetailSection 关联字段&类型显示一致性重大缺陷($expand、objectSchema enrichment、类型推断)</issue_title>
<issue_description>### 问题背景 & 影响

当前 ObjectUI 在列表页和记录详情页存在如下重大字段类型显示缺陷:

  • lookup、master_detail 等关联字段在列表和详情页始终显示原始 ID(如 o1p1),而非展开后的名称。
  • 即便服务端已实现 $expand,客户端 fetch 时机和字段类型推断逻辑存在竞争和脱节。
  • 不同 view(ObjectGrid / ListView / DetailView / DetailSection)对字段类型和 objectSchema enrichment 支持不一致,部分代码直接 fallback 至原始值/ID。

用户反馈截图

列表页 — Order/Product 字段显示 o1p1 等原始 ID:

列表页字段显示异常

详情页 — ORDER 显示 o1,PRODUCT 显示 p1

详情页字段显示异常

具体问题归纳(3个独立根因)

Bug 1: ListView $expand 竞争条件

位置: packages/plugin-list/src/ListView.tsx

// ListView.tsx L509-513
const expandFields = React.useMemo(
    () => buildExpandFields(objectDef?.fields, schema.fields),
    [objectDef?.fields, schema.fields],
);
  • expandFields 依赖异步加载的 objectDef,首次 fetch 时 objectDef 可能仍为 null,导致 expandFields = []
  • 数据请求不带 $expand,服务端返回的 lookup 字段为原始 ID 字符串
  • 即使后续 objectSchema 加载完成,数据本身已经是 "o1" 而非展开对象,LookupCellRenderer 没有 options 可以 resolve label

Bug 2: DetailView 不传 $expand,也不加载 objectSchema

位置: packages/plugin-detail/src/DetailView.tsx

// DetailView.tsx L86-92
if (dataSource && schema.objectName && schema.resourceId) {
    dataSource.findOne(objectName, resourceId).then((result) => {
        setData(result);
    });
}
  • findOne() 调用完全不传 $expand 参数
  • DetailView 不加载 objectSchema(不像 ObjectGrid 那样调用 getObjectSchema
  • 因此不知道哪些字段是 lookup/master_detail 类型,无法计算需要 expand 的字段
  • 传给 DetailSection 的 fields 缺少 type 属性

Bug 3: DetailSection 缺少 objectSchema enrichment

位置: packages/plugin-detail/src/DetailSection.tsx

// DetailSection.tsx L76-86
const displayValue = (() => {
    if (value === null || value === undefined) return '-';
    if (field.type) {                      // ← 没有 type 就跳过 CellRenderer
        const CellRenderer = getCellRenderer(field.type);
        if (CellRenderer) {
            return <CellRenderer value={value} field={field} />;
        }
    }
    return String(value);                   // ← 直接 String()
})();
  • field.type 未指定时直接 String(value)
  • 即使数据被 expand 成对象也可能显示 [object Object]
  • select、percent 等类型也无法走到正确的 CellRenderer

根因总结

# 组件 问题 影响
1 ListView $expand 依赖异步 objectDef,首次 fetch 时 objectDef=null → 不带 $expand → 数据是原始 ID 列表 lookup 字段始终显示 ID
2 DetailView findOne() 不传 $expand,不加载 objectSchema 详情页 lookup 字段显示原始 ID
3 DetailSection field.type 未指定时直接 String(value) → 无法利用类型感知的 CellRenderer 所有无 type 的字段渲染失败

修复建议

  1. ListView:确保 objectDef 加载完成后再发起数据 fetch(或在 objectDef 变化后 re-fetch 带上 $expand
  2. DetailView:参照 ObjectGrid,先加载 objectSchemabuildExpandFieldsfindOne(resourceId, { $expand }),并将 objectSchema 传给 DetailSection
  3. DetailSection:当 field.type 为空时,从 objectSchema 中查找字段类型、options 等元数据,参考 ObjectGrid generateColumns 的 enrichment 逻辑

涉及文件

  • packages/plugin-list/src/ListView.tsx
  • packages/plugin-grid/src/ObjectGrid.tsx
  • packages/plugin-detail/src/DetailView.tsx
  • packages/plugin-detail/src/DetailSection.tsx

验收标准

  • 列表页 lookup/master_detail 字段显示展开后的名称(非原始 ID)
  • 详情页 lookup/master_detail 字段显示展开后的名称
  • percent 字段在详情页正确显示百分比格式
  • select 字段在详情页和列表页正确显示 label
  • 空值/null 统一显示 -
  • 补充相关 UT/Snapshot 测试
  • pnpm test 全部通过
  • 更新 ROADMAP.md

关联

Comments on the Issue (you are @copilot in this section)


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 1, 2026

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

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Mar 2, 2026 3:26am
objectui-demo Ready Ready Preview, Comment Mar 2, 2026 3:26am
objectui-storybook Canceled Canceled Mar 2, 2026 3:26am

Request Review

… DetailSection

Bug 1 (ListView): Gate data fetch on objectDefLoaded to prevent race condition
where $expand is empty on first fetch due to async objectDef loading.

Bug 2 (DetailView): Load objectSchema via getObjectSchema, compute $expand
fields, and pass them to findOne() so lookup/master_detail fields are expanded.

Bug 3 (DetailSection): Accept objectSchema prop and enrich fields missing
type/options/currency from objectSchema metadata before rendering.

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix major defect in field type display consistency fix: ListView/DetailView/DetailSection lookup field display — $expand race condition & schema enrichment Mar 1, 2026
@hotlong
Copy link
Copy Markdown
Collaborator

hotlong commented Mar 1, 2026

@copilot 改动完全没有效果,还是显示的ID

… with expand

The @objectstack/client's data.find() and data.get() don't support expand
in their API. When $expand is needed:
- find() now uses data.query() (POST) which supports the full query AST
  including expand
- findOne() now uses data.query() with an _id filter when $expand is present
  instead of ignoring the params argument entirely

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 1, 2026

@copilot 改动完全没有效果,还是显示的ID

Found the deeper root cause — the previous UI-level fix was correct but the $expand parameter was being silently dropped at the data adapter layer:

  1. ObjectStackAdapter.find(): The @objectstack/client's data.find() (GET) doesn't support expand in its QueryOptions interface, so $expand was stripped during param conversion.
  2. ObjectStackAdapter.findOne(): The params argument was declared as _params (unused) — $expand was never sent to the server.

Fix in eb32a38: When $expand is needed, the adapter now routes to client.data.query() (POST) which supports the full query AST including expand. For findOne(), it uses a ['_id', '=', id] filter with expand. 8 new unit tests confirm the routing.

Copilot AI changed the title fix: ListView/DetailView/DetailSection lookup field display — $expand race condition & schema enrichment fix: ListView/DetailView/DetailSection lookup field display — $expand race condition, schema enrichment & data adapter routing Mar 1, 2026
@hotlong
Copy link
Copy Markdown
Collaborator

hotlong commented Mar 1, 2026

@copilot 00:21:44.035 @object-ui/plugin-designer:build: �[96msrc/AppCreationWizard.tsx�[0m:�[93m51�[0m:�[93m34�[0m - �[91merror�[0m�[90m TS2307: �[0mCannot find module '@object-ui/react' or its corresponding type declarations.
00:21:44.035 @object-ui/plugin-designer:build: �[96msrc/NavigationDesigner.tsx�[0m:�[93m43�[0m:�[93m34�[0m - �[91merror�[0m�[90m TS2307: �[0mCannot find module '@object-ui/react' or its corresponding type declarations.
00:21:44.695 @object-ui/plugin-dashboard:build: �[96msrc/DashboardRenderer.tsx�[0m:�[93m203�[0m:�[93m44�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'filter' does not exist on type '{ provider: "object"; object?: string | undefined; aggregate?: any; }'.
00:21:44.696 @object-ui/plugin-dashboard:build: �[96msrc/DashboardRenderer.tsx�[0m:�[93m204�[0m:�[93m44�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'searchable' does not exist on type 'DashboardWidgetSchema'.
00:21:44.696 @object-ui/plugin-dashboard:build: �[96msrc/DashboardRenderer.tsx�[0m:�[93m205�[0m:�[93m44�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'pagination' does not exist on type 'DashboardWidgetSchema'.
00:21:44.696 @object-ui/plugin-dashboard:build: �[96msrc/DashboardRenderer.tsx�[0m:�[93m217�[0m:�[93m44�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'searchable' does not exist on type 'DashboardWidgetSchema'.
00:21:44.696 @object-ui/plugin-dashboard:build: �[96msrc/DashboardRenderer.tsx�[0m:�[93m218�[0m:�[93m44�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'pagination' does not exist on type 'DashboardWidgetSchema'.
00:21:44.696 @object-ui/plugin-dashboard:build: �[96msrc/DashboardRenderer.tsx�[0m:�[93m244�[0m:�[93m44�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'filter' does not exist on type '{ provider: "object"; object?: string | undefined; aggregate?: any; }'.
00:21:44.696 @object-ui/plugin-dashboard:build: �[96msrc/WidgetConfigPanel.tsx�[0m:�[93m523�[0m:�[93m70�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'type' does not exist on type '{ title: any; description: any; }'.
00:21:44.697 @object-ui/plugin-dashboard:build: �[96msrc/WidgetConfigPanel.tsx�[0m:�[93m524�[0m:�[93m47�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'type' does not exist on type '{ title: any; description: any; }'.
00:21:44.949 @object-ui/plugin-detail:build: �[96msrc/DetailView.tsx�[0m:�[93m125�[0m:�[93m36�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'startsWith' does not exist on type 'string | number'.
00:21:44.949 @object-ui/plugin-detail:build: Property 'startsWith' does not exist on type 'number'.
00:21:44.949 @object-ui/plugin-detail:build: �[96msrc/DetailView.tsx�[0m:�[93m126�[0m:�[93m26�[0m - �[91merror�[0m�[90m TS2339: �[0mProperty 'slice' does not exist on type 'string | number'.
00:21:44.949 @object-ui/plugin-detail:build: Property 'slice' does not exist on type 'number'.
00:21:48.166 @object-ui/data-objectstack:build: src/index.ts(286,11): error TS2322: Type 'Record<string, object>' is not assignable to type 'Record<string, QueryAST>'.
00:21:48.166 @object-ui/data-objectstack:build: 'string' index signatures are incompatible.
00:21:48.167 @object-ui/data-objectstack:build: Type 'object' is not assignable to type 'QueryAST'.
00:21:48.167 @object-ui/data-objectstack:build: Property 'object' is missing in type '{}' but required in type '{ object: string; fields?: any[] | undefined; where?: FilterCondition | undefined; search?: { query: string; fuzzy: boolean; operator: "and" | "or"; highlight: boolean; fields?: string[] | undefined; boost?: Record<...> | undefined; minScore?: number | undefined; language?: string | undefined; } | undefined; ... 10 ...'.
00:21:48.170 @object-ui/data-objectstack:build: �[31mError: error occurred in dts build
00:21:48.170 @object-ui/data-objectstack:build: at Worker. (/vercel/path0/node_modules/.pnpm/tsup@8.5.1_@microsoft+api-extractor@7.56.3_@types+node@25.2.3__@swc+core@1.15.11_jiti@2_825ae237bd52417136b93bbcbabddc06/node_modules/tsup/dist/index.js:1545:26)
00:21:48.170 @object-ui/data-objectstack:build: at Worker.emit (node:events:508:28)
00:21:48.170 @object-ui/data-objectstack:build: at MessagePort. (node:internal/worker:332:53)
00:21:48.170 @object-ui/data-objectstack:build: at [nodejs.internal.kHybridDispatch] (node:internal/event_target:845:20)
00:21:48.170 @object-ui/data-objectstack:build: at MessagePort. (node:internal/per_context/messageport:23:28)�[39m
00:21:48.237 @object-ui/data-objectstack:build:  ELIFECYCLE  Command failed with exit code 1.
00:21:48.248 @object-ui/data-objectstack:build: ERROR: command finished with error: command (/vercel/path0/packages/data-objectstack) /vercel/.local/share/pnpm/.tools/pnpm/10.28.2/bin/pnpm run build exited (1)
00:21:48.248 @object-ui/data-objectstack#build: command (/vercel/path0/packages/data-objectstack) /vercel/.local/share/pnpm/.tools/pnpm/10.28.2/bin/pnpm run build exited (1)
00:21:48.262 ERROR run failed: command exited (1)
00:21:48.280 Error: Command "cd ../.. && pnpm turbo run build --filter=@object-ui/console^... && cd apps/console && VITE_BASE_PATH=/ pnpm build:vercel" exited with 1

- data-objectstack: expand entries now include required `object` property
  to satisfy QueryAST type; findOne uses `where` instead of `filters`
- DetailView: convert resourceId to string before calling startsWith/slice

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 2, 2026

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 57.8 KB 60 KB
Entry file index-CfvxpgUX.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 7.36KB 1.78KB
auth (ForgotPasswordForm.js) 4.00KB 1.44KB
auth (LoginForm.js) 4.20KB 1.45KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 5.66KB 1.59KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (createAuthClient.js) 2.78KB 0.98KB
auth (createAuthenticatedFetch.js) 1.24KB 0.60KB
auth (index.js) 1.18KB 0.51KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 1.57KB 0.57KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (index.js) 1.16KB 0.50KB
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) 1877.68KB 439.40KB
core (index.js) 0.98KB 0.36KB
create-plugin (index.js) 10.13KB 3.17KB
data-objectstack (index.js) 44.31KB 10.88KB
fields (index.js) 97.26KB 19.73KB
i18n (i18n.js) 2.03KB 0.77KB
i18n (index.js) 1.90KB 0.76KB
i18n (provider.js) 4.63KB 1.47KB
i18n (useObjectLabel.js) 4.50KB 1.60KB
layout (index.js) 101.43KB 26.74KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (index.js) 1.19KB 0.53KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 4.42KB 1.27KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 1.77KB 0.77KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.11KB 0.87KB
permissions (evaluator.js) 4.00KB 1.23KB
permissions (index.js) 0.85KB 0.40KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 0.99KB 0.49KB
plugin-aggrid (AddressField-Ca6aSbFW.js) 3.05KB 0.76KB
plugin-aggrid (AgGridImpl-Brsm5CmH.js) 7.04KB 2.33KB
plugin-aggrid (AutoNumberField-DrGjmfKt.js) 0.28KB 0.27KB
plugin-aggrid (FileField-CMfNMmvO.js) 5.90KB 2.10KB
plugin-aggrid (FormulaField-Cpf8sXXM.js) 0.52KB 0.38KB
plugin-aggrid (GeolocationField-D-EW8C92.js) 4.46KB 1.49KB
plugin-aggrid (GridField--t291OLT.js) 1.74KB 0.68KB
plugin-aggrid (LocationField-C5pdO0VY.js) 0.93KB 0.54KB
plugin-aggrid (MasterDetailField--lQGMhKf.js) 3.86KB 1.17KB
plugin-aggrid (ObjectAgGridImpl-Cy851etj.js) 922.73KB 204.23KB
plugin-aggrid (ObjectField-CwbQIROw.js) 1.61KB 0.76KB
plugin-aggrid (QRCodeField-BksI6bwG.js) 3.38KB 1.23KB
plugin-aggrid (RichTextField-DSkfYMMR.js) 1.16KB 0.59KB
plugin-aggrid (SignatureField-sBd3qsYZ.js) 3.33KB 1.29KB
plugin-aggrid (SummaryField-fM-E9oD5.js) 0.48KB 0.37KB
plugin-aggrid (UserField-lwxkNoSY.js) 2.44KB 0.93KB
plugin-aggrid (VectorField-B5I-CcgP.js) 0.79KB 0.44KB
plugin-aggrid (index-Dipgpv5R.js) 19.95KB 5.03KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-ai (index.js) 25.36KB 6.40KB
plugin-calendar (index.js) 49.09KB 13.41KB
plugin-charts (AdvancedChartImpl-D5NQFQLZ.js) 127.01KB 26.26KB
plugin-charts (BarChart-C_I0OFbj.js) 542.77KB 135.30KB
plugin-charts (ChartImpl-WXTkPN08.js) 3.19KB 1.10KB
plugin-charts (index-xUWSanB8.js) 17.03KB 5.02KB
plugin-charts (index.js) 0.19KB 0.16KB
plugin-chatbot (index.js) 1140.69KB 333.05KB
plugin-dashboard (index.js) 181.92KB 44.73KB
plugin-designer (index.js) 262.79KB 47.73KB
plugin-detail (AddressField-i0NNdWUi.js) 2.98KB 0.75KB
plugin-detail (AutoNumberField-BxnFqllo.js) 0.28KB 0.26KB
plugin-detail (AvatarField-BjFsOFXB.js) 2.62KB 1.07KB
plugin-detail (BooleanField-CVvvygrK.js) 1.13KB 0.51KB
plugin-detail (CodeField-DNkJyIMn.js) 0.74KB 0.47KB
plugin-detail (ColorField-Vs8UGmwT.js) 1.23KB 0.54KB
plugin-detail (CurrencyField-Wy8-1ksu.js) 1.48KB 0.73KB
plugin-detail (DateField-C0nBirV2.js) 0.54KB 0.37KB
plugin-detail (DateTimeField-C1BNb8DO.js) 0.71KB 0.42KB
plugin-detail (EmailField-XwvtKLUf.js) 0.81KB 0.48KB
plugin-detail (FileField-C7WivMt1.js) 5.50KB 1.94KB
plugin-detail (FormulaField-CJkkwIK8.js) 0.51KB 0.38KB
plugin-detail (GeolocationField-6hy3T3J2.js) 3.71KB 1.21KB
plugin-detail (GridField-Ba484Lzc.js) 1.71KB 0.68KB
plugin-detail (ImageField-DUMBQ1-w.js) 2.83KB 1.14KB
plugin-detail (LocationField-BFLRNNb2.js) 0.95KB 0.53KB
plugin-detail (LookupField-CQOcE3a5.js) 4.00KB 1.38KB
plugin-detail (MasterDetailField-CjUcF3Dz.js) 3.39KB 1.00KB
plugin-detail (NumberField-DoOeQlk8.js) 0.68KB 0.44KB
plugin-detail (ObjectField-CrObVqds.js) 1.64KB 0.76KB
plugin-detail (PasswordField-DDcw_Uef.js) 1.21KB 0.62KB
plugin-detail (PercentField-BaV1OsxV.js) 1.89KB 0.84KB
plugin-detail (PhoneField-BA4u8FUo.js) 0.80KB 0.48KB
plugin-detail (QRCodeField-B0rOyDV0.js) 2.35KB 0.91KB
plugin-detail (RatingField-zWCEHhA7.js) 1.62KB 0.66KB
plugin-detail (RichTextField-aTDtMPf2.js) 1.15KB 0.59KB
plugin-detail (SelectField-B91Zua2v.js) 0.90KB 0.49KB
plugin-detail (SignatureField-DHluQ7_1.js) 2.97KB 1.10KB
plugin-detail (SliderField-BTRsSzhx.js) 1.00KB 0.49KB
plugin-detail (SummaryField-ugYPYxjP.js) 0.47KB 0.36KB
plugin-detail (TextAreaField-DzsOs_7d.js) 1.09KB 0.59KB
plugin-detail (TextField-YHZj9jpA.js) 0.82KB 0.43KB
plugin-detail (TimeField-4ilmBU5k.js) 0.50KB 0.34KB
plugin-detail (UrlField-CSuEdXa7.js) 0.98KB 0.52KB
plugin-detail (UserField-BcJwduMo.js) 2.33KB 0.90KB
plugin-detail (VectorField-CKg9jdGa.js) 0.78KB 0.44KB
plugin-detail (index-Beijc5NH.js) 1879.87KB 440.52KB
plugin-detail (index.js) 0.90KB 0.51KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 67.06KB 14.03KB
plugin-gantt (index.js) 247.83KB 59.72KB
plugin-grid (index.js) 108.68KB 28.50KB
plugin-kanban (KanbanEnhanced-KDTrbcBH.js) 32.31KB 9.09KB
plugin-kanban (KanbanImpl-BnBVjnXG.js) 14.19KB 4.18KB
plugin-kanban (index-Cv0GoQTa.js) 31.27KB 9.24KB
plugin-kanban (index.js) 0.42KB 0.25KB
plugin-kanban (sortable.esm-CNNHgHk5.js) 71.43KB 18.99KB
plugin-list (index.js) 1842.19KB 433.49KB
plugin-map (index.js) 130.49KB 31.72KB
plugin-map (maplibre-gl-DSpYxujd.js) 1416.27KB 302.72KB
plugin-markdown (MarkdownImpl-E6vCIsNj.js) 256.68KB 64.45KB
plugin-markdown (index-Dr10kVgr.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-report (index.js) 86.28KB 17.18KB
plugin-timeline (index.js) 108.70KB 25.29KB
plugin-view (index.js) 140.01KB 35.35KB
plugin-workflow (index.js) 82.94KB 17.11KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 9.04KB 2.82KB
react (index.js) 0.76KB 0.42KB
tenant (TenantContext.js) 0.31KB 0.25KB
tenant (TenantGuard.js) 1.04KB 0.43KB
tenant (TenantProvider.js) 2.76KB 0.98KB
tenant (TenantScopedQuery.js) 0.77KB 0.44KB
tenant (index.js) 0.75KB 0.38KB
tenant (resolver.js) 2.64KB 0.76KB
tenant (useTenant.js) 0.50KB 0.32KB
tenant (useTenantBranding.js) 0.62KB 0.39KB
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) 0.74KB 0.39KB
types (disclosure.js) 0.20KB 0.18KB
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) 1.25KB 0.58KB
types (layout.js) 0.20KB 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 (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (tenant.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB
types (workflow.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

…ble error state timing

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes lookup/master_detail display across ListView and DetailView by ensuring $expand is computed/applied consistently, enriching detail fields from objectSchema, and adding an ObjectStackAdapter fallback path that expands via raw REST GET populate= when the SDK/server path can’t support expand.

Changes:

  • ListView: gate the initial fetch until getObjectSchema() completes so $expand is available on the first request.
  • DetailView/DetailSection: load objectSchema, compute $expand for findOne(), and enrich fields without explicit type using schema metadata.
  • ObjectStackAdapter: when $expand is requested, route find() / findOne() through a raw GET /data/:object?...&populate=... and normalize responses; add targeted tests.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/plugin-list/src/ListView.tsx Adds objectDefLoaded gating to avoid $expand race on initial fetch.
packages/plugin-list/src/tests/DataFetch.test.tsx Adds regression tests for $expand propagation + fetch gating.
packages/plugin-detail/src/DetailView.tsx Fetches objectSchema, computes $expand, passes schema down to DetailSection.
packages/plugin-detail/src/DetailSection.tsx Enriches field metadata from objectSchema to select correct cell renderer.
packages/plugin-detail/src/tests/DetailView.test.tsx Tests $expand behavior and getObjectSchema absence fallback.
packages/plugin-detail/src/tests/DetailSection.test.tsx Tests enrichment/type precedence and fallback rendering.
packages/data-objectstack/src/index.ts Adds raw GET populate route for $expand and shared result normalization.
packages/data-objectstack/src/expand.test.ts Tests raw GET routing, query params, auth header, envelope unwrapping.
ROADMAP.md Documents the initiative and intended fixes (currently mismatched with implementation).

Comment on lines 514 to 533
// Fetch object definition
React.useEffect(() => {
let isMounted = true;
const fetchObjectDef = async () => {
if (!dataSource || !schema.objectName) return;
if (!dataSource || !schema.objectName) {
setObjectDefLoaded(true);
return;
}
try {
const def = await dataSource.getObjectSchema(schema.objectName);
if (isMounted) {
setObjectDef(def);
}
} catch (err) {
console.warn("Failed to fetch object schema for ListView:", err);
} finally {
if (isMounted) {
setObjectDefLoaded(true);
}
}
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

objectDefLoaded is never reset when schema.objectName / dataSource changes. That means the data-fetch effect can run immediately for a new object while objectDef is still from the previous object (or null), producing an incorrect $expand and potentially fetching the wrong shape of data. Consider setting objectDefLoaded back to false (and clearing objectDef) at the start of this effect whenever the objectName/dataSource changes, then setting it to true in finally once the new schema load attempt completes.

Copilot uses AI. Check for mistakes.
Comment on lines 90 to +116
if (dataSource && schema.objectName && schema.resourceId) {
setLoading(true);
const objectName = schema.objectName;
const resourceId = schema.resourceId;
const prefix = `${objectName}-`;

dataSource.findOne(objectName, resourceId).then((result) => {
if (result) {
setData(result);
setLoading(false);
return;
// Collect all visible fields from sections and top-level fields
const allFields = [
...(schema.sections?.flatMap(s => s.fields) || []),
...(schema.fields || []),
];

// Load objectSchema first, then fetch data with $expand
const schemaPromise = dataSource.getObjectSchema
? dataSource.getObjectSchema(objectName).catch(() => null)
: Promise.resolve(null);

schemaPromise.then((resolvedSchema) => {
if (!isMounted) return;
if (resolvedSchema) {
setObjectSchema(resolvedSchema);
}
// Fallback: try alternate ID format for backward compatibility
const altId = resourceId.startsWith(prefix)
? resourceId.slice(prefix.length) // strip prefix
: `${prefix}${resourceId}`; // prepend prefix
return dataSource.findOne(objectName, altId).then((fallbackResult) => {
setData(fallbackResult);
setLoading(false);
}).catch(() => {
setData(null);
setLoading(false);

// Compute $expand from objectSchema
const expandFields = buildExpandFields(resolvedSchema?.fields, allFields);
const params = expandFields.length > 0 ? { $expand: expandFields } : undefined;

Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

objectSchema state can become stale when navigating between objects/records in the same mounted DetailView. If schema.objectName changes (or getObjectSchema fails/returns null), the previous objectSchema remains and will still be passed to DetailSection, potentially enriching fields with the wrong metadata. Consider clearing objectSchema (and possibly data) when starting a new fetch cycle for a different objectName/resourceId, and explicitly setting objectSchema to null when schema resolution fails.

Copilot uses AI. Check for mistakes.
Comment on lines 81 to 99
// Enrich field with objectSchema metadata when field.type is not set
const objectDefField = objectSchema?.fields?.[field.name];
const resolvedType = field.type || objectDefField?.type;
const enrichedField: Record<string, any> = { ...field };
if (!field.type && objectDefField) {
if (objectDefField.type) enrichedField.type = objectDefField.type;
if (objectDefField.options && !enrichedField.options) enrichedField.options = objectDefField.options;
if (objectDefField.currency && !enrichedField.currency) enrichedField.currency = objectDefField.currency;
if (objectDefField.precision !== undefined && enrichedField.precision === undefined) enrichedField.precision = objectDefField.precision;
if (objectDefField.format && !enrichedField.format) enrichedField.format = objectDefField.format;
if (objectDefField.reference_to && !enrichedField.reference_to) enrichedField.reference_to = objectDefField.reference_to;
if (objectDefField.reference_field && !enrichedField.reference_field) enrichedField.reference_field = objectDefField.reference_field;
}
// Use type-aware cell renderer when field type is available (explicit or enriched)
if (resolvedType) {
const CellRenderer = getCellRenderer(resolvedType);
if (CellRenderer) {
return <CellRenderer value={value} field={field as unknown as FieldMetadata} />;
return <CellRenderer value={value} field={enrichedField as unknown as FieldMetadata} />;
}
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Field enrichment from objectSchema only runs when field.type is missing. If a schema field sets type but omits supporting metadata (e.g., type: 'select' without options, or lookup without reference_field), the cell renderer will run without the needed metadata and may still display raw values/IDs. To match ObjectGrid’s enrichment behavior, consider merging missing properties (options/currency/precision/format/reference_*) from objectSchema regardless of whether field.type is explicitly set, while still keeping the explicit type as the resolved type.

Copilot uses AI. Check for mistakes.
Comment thread packages/data-objectstack/src/index.ts Outdated

const baseUrl = this.baseUrl.replace(/\/$/, '');
const qs = queryParams.toString();
const url = `${baseUrl}/api/v1/data/${resource}${qs ? `?${qs}` : ''}`;
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rawFindWithPopulate() hardcodes the /api/v1 prefix when building the URL (${baseUrl}/api/v1/data/...). Elsewhere in the repo there are examples/configs that use baseUrl: '/api/v1' (API-prefix style), which would produce a doubled path (/api/v1/api/v1/data/...) for the raw $expand route. Consider normalizing baseUrl (e.g., detect and avoid duplicating /api/v1, or derive the data path from the same baseUrl semantics used by @objectstack/client) so raw and SDK requests are consistent.

Suggested change
const url = `${baseUrl}/api/v1/data/${resource}${qs ? `?${qs}` : ''}`;
const hasApiVersionSuffix = /\/api\/v\d+$/i.test(baseUrl);
const dataPath = hasApiVersionSuffix ? '/data' : '/api/v1/data';
const url = `${baseUrl}${dataPath}/${resource}${qs ? `?${qs}` : ''}`;

Copilot uses AI. Check for mistakes.
Comment thread ROADMAP.md Outdated
Comment on lines +1384 to +1385
- **ObjectStackAdapter `find()`** (`packages/data-objectstack/src/index.ts`): When `$expand` is present, routes to `client.data.query()` (POST) which supports the full query AST including expand. Added `buildQueryAST()` and `normalizeQueryResult()` private helpers.
- **ObjectStackAdapter `findOne()`** (`packages/data-objectstack/src/index.ts`): When `$expand` is present, uses `client.data.query()` with `['_id', '=', id]` filter and `expand` map. Falls back to `client.data.get()` when no expand is needed.
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ROADMAP entry for the ObjectStackAdapter $expand fix doesn’t match the implementation in this PR. It says $expand routes through client.data.query() (POST) and mentions buildQueryAST(), but the adapter changes here route $expand through a raw GET with populate= (via rawFindWithPopulate()), and there is no buildQueryAST() helper. Please update this section to reflect the actual approach (raw GET + populate) and the correct helper names, and ensure the listed “Root Causes” count matches the PR description.

Suggested change
- **ObjectStackAdapter `find()`** (`packages/data-objectstack/src/index.ts`): When `$expand` is present, routes to `client.data.query()` (POST) which supports the full query AST including expand. Added `buildQueryAST()` and `normalizeQueryResult()` private helpers.
- **ObjectStackAdapter `findOne()`** (`packages/data-objectstack/src/index.ts`): When `$expand` is present, uses `client.data.query()` with `['_id', '=', id]` filter and `expand` map. Falls back to `client.data.get()` when no expand is needed.
- **ObjectStackAdapter `find()`** (`packages/data-objectstack/src/index.ts`): When `$expand` is present, routes through `rawFindWithPopulate()` which issues a raw GET request with a `populate=` query parameter derived from the `$expand` map. When no expand is needed, the existing `client.data.find()` path is used unchanged.
- **ObjectStackAdapter `findOne()`** (`packages/data-objectstack/src/index.ts`): When `$expand` is present, uses the same raw GET + `populate=` mechanism (via `rawFindWithPopulate()` or an equivalent helper) to fetch the single record with its related data; falls back to `client.data.get()` when no expand is needed.

Copilot uses AI. Check for mistakes.
@hotlong
Copy link
Copy Markdown
Collaborator

hotlong commented Mar 2, 2026

@copilot fix all and ci errors

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 2, 2026

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 57.8 KB 60 KB
Entry file index-CfvxpgUX.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 7.36KB 1.78KB
auth (ForgotPasswordForm.js) 4.00KB 1.44KB
auth (LoginForm.js) 4.20KB 1.45KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 5.66KB 1.59KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (createAuthClient.js) 2.78KB 0.98KB
auth (createAuthenticatedFetch.js) 1.24KB 0.60KB
auth (index.js) 1.18KB 0.51KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 1.57KB 0.57KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (index.js) 1.16KB 0.50KB
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) 1877.68KB 439.40KB
core (index.js) 0.98KB 0.36KB
create-plugin (index.js) 10.13KB 3.17KB
data-objectstack (index.js) 44.31KB 10.88KB
fields (index.js) 97.26KB 19.73KB
i18n (i18n.js) 2.03KB 0.77KB
i18n (index.js) 1.90KB 0.76KB
i18n (provider.js) 4.63KB 1.47KB
i18n (useObjectLabel.js) 4.50KB 1.60KB
layout (index.js) 101.43KB 26.74KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (index.js) 1.19KB 0.53KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 4.42KB 1.27KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 1.77KB 0.77KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.11KB 0.87KB
permissions (evaluator.js) 4.00KB 1.23KB
permissions (index.js) 0.85KB 0.40KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 0.99KB 0.49KB
plugin-aggrid (AddressField-Ca6aSbFW.js) 3.05KB 0.76KB
plugin-aggrid (AgGridImpl-Brsm5CmH.js) 7.04KB 2.33KB
plugin-aggrid (AutoNumberField-DrGjmfKt.js) 0.28KB 0.27KB
plugin-aggrid (FileField-CMfNMmvO.js) 5.90KB 2.10KB
plugin-aggrid (FormulaField-Cpf8sXXM.js) 0.52KB 0.38KB
plugin-aggrid (GeolocationField-D-EW8C92.js) 4.46KB 1.49KB
plugin-aggrid (GridField--t291OLT.js) 1.74KB 0.68KB
plugin-aggrid (LocationField-C5pdO0VY.js) 0.93KB 0.54KB
plugin-aggrid (MasterDetailField--lQGMhKf.js) 3.86KB 1.17KB
plugin-aggrid (ObjectAgGridImpl-Cy851etj.js) 922.73KB 204.23KB
plugin-aggrid (ObjectField-CwbQIROw.js) 1.61KB 0.76KB
plugin-aggrid (QRCodeField-BksI6bwG.js) 3.38KB 1.23KB
plugin-aggrid (RichTextField-DSkfYMMR.js) 1.16KB 0.59KB
plugin-aggrid (SignatureField-sBd3qsYZ.js) 3.33KB 1.29KB
plugin-aggrid (SummaryField-fM-E9oD5.js) 0.48KB 0.37KB
plugin-aggrid (UserField-lwxkNoSY.js) 2.44KB 0.93KB
plugin-aggrid (VectorField-B5I-CcgP.js) 0.79KB 0.44KB
plugin-aggrid (index-Dipgpv5R.js) 19.95KB 5.03KB
plugin-aggrid (index.js) 0.22KB 0.16KB
plugin-ai (index.js) 25.36KB 6.40KB
plugin-calendar (index.js) 49.09KB 13.41KB
plugin-charts (AdvancedChartImpl-D5NQFQLZ.js) 127.01KB 26.26KB
plugin-charts (BarChart-C_I0OFbj.js) 542.77KB 135.30KB
plugin-charts (ChartImpl-WXTkPN08.js) 3.19KB 1.10KB
plugin-charts (index-xUWSanB8.js) 17.03KB 5.02KB
plugin-charts (index.js) 0.19KB 0.16KB
plugin-chatbot (index.js) 1140.69KB 333.05KB
plugin-dashboard (index.js) 181.92KB 44.73KB
plugin-designer (index.js) 262.79KB 47.73KB
plugin-detail (AddressField-i0NNdWUi.js) 2.98KB 0.75KB
plugin-detail (AutoNumberField-BxnFqllo.js) 0.28KB 0.26KB
plugin-detail (AvatarField-BjFsOFXB.js) 2.62KB 1.07KB
plugin-detail (BooleanField-CVvvygrK.js) 1.13KB 0.51KB
plugin-detail (CodeField-DNkJyIMn.js) 0.74KB 0.47KB
plugin-detail (ColorField-Vs8UGmwT.js) 1.23KB 0.54KB
plugin-detail (CurrencyField-Wy8-1ksu.js) 1.48KB 0.73KB
plugin-detail (DateField-C0nBirV2.js) 0.54KB 0.37KB
plugin-detail (DateTimeField-C1BNb8DO.js) 0.71KB 0.42KB
plugin-detail (EmailField-XwvtKLUf.js) 0.81KB 0.48KB
plugin-detail (FileField-C7WivMt1.js) 5.50KB 1.94KB
plugin-detail (FormulaField-CJkkwIK8.js) 0.51KB 0.38KB
plugin-detail (GeolocationField-6hy3T3J2.js) 3.71KB 1.21KB
plugin-detail (GridField-Ba484Lzc.js) 1.71KB 0.68KB
plugin-detail (ImageField-DUMBQ1-w.js) 2.83KB 1.14KB
plugin-detail (LocationField-BFLRNNb2.js) 0.95KB 0.53KB
plugin-detail (LookupField-CQOcE3a5.js) 4.00KB 1.38KB
plugin-detail (MasterDetailField-CjUcF3Dz.js) 3.39KB 1.00KB
plugin-detail (NumberField-DoOeQlk8.js) 0.68KB 0.44KB
plugin-detail (ObjectField-CrObVqds.js) 1.64KB 0.76KB
plugin-detail (PasswordField-DDcw_Uef.js) 1.21KB 0.62KB
plugin-detail (PercentField-BaV1OsxV.js) 1.89KB 0.84KB
plugin-detail (PhoneField-BA4u8FUo.js) 0.80KB 0.48KB
plugin-detail (QRCodeField-B0rOyDV0.js) 2.35KB 0.91KB
plugin-detail (RatingField-zWCEHhA7.js) 1.62KB 0.66KB
plugin-detail (RichTextField-aTDtMPf2.js) 1.15KB 0.59KB
plugin-detail (SelectField-B91Zua2v.js) 0.90KB 0.49KB
plugin-detail (SignatureField-DHluQ7_1.js) 2.97KB 1.10KB
plugin-detail (SliderField-BTRsSzhx.js) 1.00KB 0.49KB
plugin-detail (SummaryField-ugYPYxjP.js) 0.47KB 0.36KB
plugin-detail (TextAreaField-DzsOs_7d.js) 1.09KB 0.59KB
plugin-detail (TextField-YHZj9jpA.js) 0.82KB 0.43KB
plugin-detail (TimeField-4ilmBU5k.js) 0.50KB 0.34KB
plugin-detail (UrlField-CSuEdXa7.js) 0.98KB 0.52KB
plugin-detail (UserField-BcJwduMo.js) 2.33KB 0.90KB
plugin-detail (VectorField-CKg9jdGa.js) 0.78KB 0.44KB
plugin-detail (index-Beijc5NH.js) 1879.87KB 440.52KB
plugin-detail (index.js) 0.90KB 0.51KB
plugin-editor (MonacoImpl-hfdmoz6k.js) 18.15KB 5.59KB
plugin-editor (index-CuYbY6xb.js) 10.10KB 3.32KB
plugin-editor (index.js) 0.19KB 0.15KB
plugin-form (index.js) 67.06KB 14.03KB
plugin-gantt (index.js) 247.83KB 59.72KB
plugin-grid (index.js) 108.68KB 28.50KB
plugin-kanban (KanbanEnhanced-KDTrbcBH.js) 32.31KB 9.09KB
plugin-kanban (KanbanImpl-BnBVjnXG.js) 14.19KB 4.18KB
plugin-kanban (index-Cv0GoQTa.js) 31.27KB 9.24KB
plugin-kanban (index.js) 0.42KB 0.25KB
plugin-kanban (sortable.esm-CNNHgHk5.js) 71.43KB 18.99KB
plugin-list (index.js) 1842.19KB 433.49KB
plugin-map (index.js) 130.49KB 31.72KB
plugin-map (maplibre-gl-DSpYxujd.js) 1416.27KB 302.72KB
plugin-markdown (MarkdownImpl-E6vCIsNj.js) 256.68KB 64.45KB
plugin-markdown (index-Dr10kVgr.js) 9.63KB 3.17KB
plugin-markdown (index.js) 0.19KB 0.15KB
plugin-report (index.js) 86.28KB 17.18KB
plugin-timeline (index.js) 108.70KB 25.29KB
plugin-view (index.js) 140.01KB 35.35KB
plugin-workflow (index.js) 82.94KB 17.11KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 9.04KB 2.82KB
react (index.js) 0.76KB 0.42KB
tenant (TenantContext.js) 0.31KB 0.25KB
tenant (TenantGuard.js) 1.04KB 0.43KB
tenant (TenantProvider.js) 2.76KB 0.98KB
tenant (TenantScopedQuery.js) 0.77KB 0.44KB
tenant (index.js) 0.75KB 0.38KB
tenant (resolver.js) 2.64KB 0.76KB
tenant (useTenant.js) 0.50KB 0.32KB
tenant (useTenantBranding.js) 0.62KB 0.39KB
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) 0.74KB 0.39KB
types (disclosure.js) 0.20KB 0.18KB
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) 1.25KB 0.58KB
types (layout.js) 0.20KB 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 (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (tenant.js) 0.20KB 0.18KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 0.20KB 0.18KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB
types (workflow.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

- Fix RecordDetailEdit test: only pass 3rd arg to findOne when $expand
  params exist (avoids undefined 3rd arg assertion failure)
- ListView: reset objectDefLoaded + objectDef when objectName changes
  to prevent stale $expand on navigation
- DetailView: clear objectSchema + data when starting new fetch cycle
  to prevent stale metadata on navigation
- DetailSection: merge missing metadata props (options, reference_to,
  reference_field, etc.) from objectSchema even when field.type is set
- data-objectstack: detect /api/v1 suffix in baseUrl to avoid doubling
  the path prefix in rawFindWithPopulate()
- ROADMAP.md: update adapter descriptions to reflect raw GET + populate
  approach instead of data.query() POST
- Add test for baseUrl /api/v1 double-prefix prevention

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
…hout undefined 3rd arg

DetailView now only passes 3rd arg to findOne when $expand params exist,
matching the original 2-arg call signature when no expand is needed.

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 2, 2026

@copilot fix all and ci errors

Fixed all issues in bfc9017:

CI failures:

  • RecordDetailEdit tests: findOne now only passes 3rd arg when $expand params exist, matching the original 2-arg call signature
  • ObjectPivotTable failure is flaky/environment-specific (passes locally, not related to our changes)

Review comments addressed:

  • ListView: objectDefLoaded and objectDef now reset when objectName/dataSource changes, preventing stale $expand on navigation
  • DetailView: objectSchema and data cleared when starting a new fetch cycle, preventing stale metadata on navigation
  • DetailSection: missing metadata (options, reference_to, reference_field, etc.) now merged from objectSchema even when field.type is explicitly set
  • data-objectstack: rawFindWithPopulate() detects /api/v\d+ suffix in baseUrl to avoid doubling the API path prefix
  • ROADMAP.md updated to reflect actual raw GET + populate approach

Build: 42/42 tasks pass. Tests: 354 pass across 25 affected test files. CodeQL: 0 alerts.

@hotlong hotlong merged commit 1a25032 into main Mar 2, 2026
2 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

apps data-adapter documentation Improvements or additions to documentation plugin tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ListView/DetailView/DetailSection 关联字段&类型显示一致性重大缺陷($expand、objectSchema enrichment、类型推断)

3 participants