Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/console/vercel.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"installCommand": "cd ../.. && pnpm install --frozen-lockfile",
"buildCommand": "cd ../.. && pnpm turbo run build --filter=@object-ui/console^... && cd apps/console && VITE_BASE_PATH=/ pnpm build:vercel",
"buildCommand": "pnpm msw:init && NODE_OPTIONS=--max-old-space-size=4096 VITE_BASE_PATH=/ VITE_USE_MOCK_SERVER=true vite build",
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

buildCommand invokes vite build directly. Outside of a package-script runner, node_modules/.bin may not be on PATH (common on CI), which can cause vite: command not found. To make the command robust, use pnpm exec vite build (or pnpm vite build) instead of calling vite directly.

Suggested change
"buildCommand": "pnpm msw:init && NODE_OPTIONS=--max-old-space-size=4096 VITE_BASE_PATH=/ VITE_USE_MOCK_SERVER=true vite build",
"buildCommand": "pnpm msw:init && NODE_OPTIONS=--max-old-space-size=4096 VITE_BASE_PATH=/ VITE_USE_MOCK_SERVER=true pnpm exec vite build",

Copilot uses AI. Check for mistakes.
"outputDirectory": "dist",
"framework": "vite",
"rewrites": [
Expand Down
124 changes: 70 additions & 54 deletions apps/console/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,55 @@ function preloadCriticalChunks(): Plugin {
// auto-mount slug. Override with VITE_BASE_PATH only if deploying standalone.
const basePath = process.env.VITE_BASE_PATH || '/console/';

// On Vercel/CI we skip the compression and visualizer plugins because the
// Vercel CDN handles gzip/brotli automatically and bundle analysis is not
// needed during CI builds. This reduces peak memory by ~1.5 GB.
//
// Workspace src/ aliases are kept in ALL environments (dev + CI) so that
// plugin side-effect imports (ComponentRegistry.register) resolve correctly.
// Without them, Vite would import pre-built dist/ bundles where the
// singleton ComponentRegistry can get duplicated across chunks, causing
// "Unknown component type" errors at runtime.
const isCI = !!(process.env.VERCEL || process.env.CI);

// Workspace src/ aliases — gives instant HMR in dev and ensures correct
// side-effect resolution (plugin registrations) in production builds.
const workspaceAliases: Record<string, string> = {
'@object-ui/components': path.resolve(__dirname, '../../packages/components/src'),
'@object-ui/core': path.resolve(__dirname, '../../packages/core/src'),
'@object-ui/fields': path.resolve(__dirname, '../../packages/fields/src'),
'@object-ui/layout': path.resolve(__dirname, '../../packages/layout/src'),
'@object-ui/plugin-dashboard': path.resolve(__dirname, '../../packages/plugin-dashboard/src'),
'@object-ui/plugin-report': path.resolve(__dirname, '../../packages/plugin-report/src'),
'@object-ui/plugin-form': path.resolve(__dirname, '../../packages/plugin-form/src'),
'@object-ui/plugin-grid': path.resolve(__dirname, '../../packages/plugin-grid/src'),
'@object-ui/react': path.resolve(__dirname, '../../packages/react/src'),
'@object-ui/types': path.resolve(__dirname, '../../packages/types/src'),
'@object-ui/data-objectstack': path.resolve(__dirname, '../../packages/data-objectstack/src'),
'@object-ui/auth': path.resolve(__dirname, '../../packages/auth/src'),
'@object-ui/permissions': path.resolve(__dirname, '../../packages/permissions/src'),
'@object-ui/collaboration': path.resolve(__dirname, '../../packages/collaboration/src'),
'@object-ui/tenant': path.resolve(__dirname, '../../packages/tenant/src'),
'@object-ui/i18n': path.resolve(__dirname, '../../packages/i18n/src'),
'@object-ui/mobile': path.resolve(__dirname, '../../packages/mobile/src'),

// Plugin Aliases
'@object-ui/plugin-aggrid': path.resolve(__dirname, '../../packages/plugin-aggrid/src'),
'@object-ui/plugin-calendar': path.resolve(__dirname, '../../packages/plugin-calendar/src'),
'@object-ui/plugin-charts': path.resolve(__dirname, '../../packages/plugin-charts/src'),
'@object-ui/plugin-chatbot': path.resolve(__dirname, '../../packages/plugin-chatbot/src'),
'@object-ui/plugin-detail': path.resolve(__dirname, '../../packages/plugin-detail/src'),
'@object-ui/plugin-editor': path.resolve(__dirname, '../../packages/plugin-editor/src'),
'@object-ui/plugin-gantt': path.resolve(__dirname, '../../packages/plugin-gantt/src'),
'@object-ui/plugin-kanban': path.resolve(__dirname, '../../packages/plugin-kanban/src'),
'@object-ui/plugin-list': path.resolve(__dirname, '../../packages/plugin-list/src'),
'@object-ui/plugin-map': path.resolve(__dirname, '../../packages/plugin-map/src'),
'@object-ui/plugin-markdown': path.resolve(__dirname, '../../packages/plugin-markdown/src'),
'@object-ui/plugin-timeline': path.resolve(__dirname, '../../packages/plugin-timeline/src'),
'@object-ui/plugin-view': path.resolve(__dirname, '../../packages/plugin-view/src'),
'@object-ui/plugin-designer': path.resolve(__dirname, '../../packages/plugin-designer/src'),
};

// https://vitejs.dev/config/
export default defineConfig({
base: basePath,
Expand All @@ -54,63 +103,30 @@ export default defineConfig({
react(),
// Inject <link rel="modulepreload"> for critical chunks
preloadCriticalChunks(),
// Gzip compression for production assets
compression({
algorithm: 'gzip',
exclude: [/\.(br)$/, /\.(gz)$/],
threshold: 1024,
}),
// Brotli compression for modern browsers
compression({
algorithm: 'brotliCompress',
exclude: [/\.(br)$/, /\.(gz)$/],
threshold: 1024,
}),
// Bundle analysis (generates stats.html in dist/)
visualizer({
filename: 'dist/stats.html',
gzipSize: true,
brotliSize: true,
open: false,
}),
// Gzip/Brotli compression & bundle visualizer are skipped on Vercel/CI to
// reduce memory usage — Vercel's CDN compresses assets automatically.
...(!isCI ? [
compression({
algorithm: 'gzip',
exclude: [/\.(br)$/, /\.(gz)$/],
threshold: 1024,
}),
compression({
algorithm: 'brotliCompress',
exclude: [/\.(br)$/, /\.(gz)$/],
threshold: 1024,
}),
visualizer({
filename: 'dist/stats.html',
gzipSize: true,
brotliSize: true,
open: false,
}),
] : []),
],
resolve: {
extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json'],
alias: {
'@object-ui/components': path.resolve(__dirname, '../../packages/components/src'),
'@object-ui/core': path.resolve(__dirname, '../../packages/core/src'),
'@object-ui/fields': path.resolve(__dirname, '../../packages/fields/src'),
'@object-ui/layout': path.resolve(__dirname, '../../packages/layout/src'),
'@object-ui/plugin-dashboard': path.resolve(__dirname, '../../packages/plugin-dashboard/src'),
'@object-ui/plugin-report': path.resolve(__dirname, '../../packages/plugin-report/src'),
'@object-ui/plugin-form': path.resolve(__dirname, '../../packages/plugin-form/src'),
'@object-ui/plugin-grid': path.resolve(__dirname, '../../packages/plugin-grid/src'),
'@object-ui/react': path.resolve(__dirname, '../../packages/react/src'),
'@object-ui/types': path.resolve(__dirname, '../../packages/types/src'),
'@object-ui/data-objectstack': path.resolve(__dirname, '../../packages/data-objectstack/src'),
'@object-ui/auth': path.resolve(__dirname, '../../packages/auth/src'),
'@object-ui/permissions': path.resolve(__dirname, '../../packages/permissions/src'),
'@object-ui/collaboration': path.resolve(__dirname, '../../packages/collaboration/src'),
'@object-ui/tenant': path.resolve(__dirname, '../../packages/tenant/src'),
'@object-ui/i18n': path.resolve(__dirname, '../../packages/i18n/src'),

// Missing Plugin Aliases
'@object-ui/plugin-aggrid': path.resolve(__dirname, '../../packages/plugin-aggrid/src'),
'@object-ui/plugin-calendar': path.resolve(__dirname, '../../packages/plugin-calendar/src'),
'@object-ui/plugin-charts': path.resolve(__dirname, '../../packages/plugin-charts/src'),
'@object-ui/plugin-chatbot': path.resolve(__dirname, '../../packages/plugin-chatbot/src'),
'@object-ui/plugin-detail': path.resolve(__dirname, '../../packages/plugin-detail/src'),
'@object-ui/plugin-editor': path.resolve(__dirname, '../../packages/plugin-editor/src'),
'@object-ui/plugin-gantt': path.resolve(__dirname, '../../packages/plugin-gantt/src'),
'@object-ui/plugin-kanban': path.resolve(__dirname, '../../packages/plugin-kanban/src'),
'@object-ui/plugin-list': path.resolve(__dirname, '../../packages/plugin-list/src'),
'@object-ui/plugin-map': path.resolve(__dirname, '../../packages/plugin-map/src'),
'@object-ui/plugin-markdown': path.resolve(__dirname, '../../packages/plugin-markdown/src'),
'@object-ui/plugin-timeline': path.resolve(__dirname, '../../packages/plugin-timeline/src'),
'@object-ui/plugin-view': path.resolve(__dirname, '../../packages/plugin-view/src'),


},
alias: workspaceAliases,
},
optimizeDeps: {
include: [
Expand Down
3 changes: 2 additions & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
Expand Down
3 changes: 2 additions & 1 deletion packages/collaboration/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
Expand Down
3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"scripts": {
Expand Down
6 changes: 4 additions & 2 deletions packages/i18n/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
},
"./locales/*": {
"types": "./dist/locales/*.d.ts",
"import": "./dist/locales/*.js"
"import": "./dist/locales/*.js",
"default": "./dist/locales/*.js"
}
},
"scripts": {
Expand Down
3 changes: 2 additions & 1 deletion packages/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
Expand Down
3 changes: 2 additions & 1 deletion packages/permissions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-dashboard/src/WidgetConfigPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ export function WidgetConfigPanel({
availableFields,
}: WidgetConfigPanelProps) {
// Pre-process config to resolve any I18nLabel values for title/description
const normalizedConfig = React.useMemo(() => ({
const normalizedConfig: Record<string, any> = React.useMemo(() => ({
...config,
title: typeof config.title === 'object' ? resolveLabel(config.title) : config.title,
description: typeof config.description === 'object' ? resolveLabel(config.description) : config.description,
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-dashboard/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

/** Returns true when the widget data config uses provider: 'object' (async data source). */
export function isObjectProvider(widgetData: unknown): widgetData is { provider: 'object'; object?: string; aggregate?: any } {
export function isObjectProvider(widgetData: unknown): widgetData is { provider: 'object'; object?: string; aggregate?: any; filter?: any } {
return (
widgetData != null &&
typeof widgetData === 'object' &&
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-detail/src/DetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ export const DetailView: React.FC<DetailViewProps> = ({
}, [schema.autoDiscoverRelated, schema.related, objectSchema]);

// Merge explicit and auto-discovered related lists
const effectiveRelated = React.useMemo(() => {
const effectiveRelated: NonNullable<DetailViewSchema['related']> = React.useMemo(() => {
if (schema.related && schema.related.length > 0) return schema.related;
return discoveredRelated.map((r) => ({
title: r.title,
Expand Down
10 changes: 9 additions & 1 deletion packages/plugin-grid/src/ObjectGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import React, { useEffect, useState, useCallback, useMemo } from 'react';
import type { ObjectGridSchema, DataSource, ListColumn, ViewData } from '@object-ui/types';
import type { I18nLabel } from '@objectstack/spec/ui';
import { SchemaRenderer, useDataScope, useNavigationOverlay, useAction, useObjectTranslation, useSafeFieldLabel } from '@object-ui/react';
import { getCellRenderer, formatCurrency, formatCompactCurrency, formatDate, formatPercent, humanizeLabel } from '@object-ui/fields';
import {
Comment on lines 25 to 29
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

ObjectGrid.tsx now imports I18nLabel from @objectstack/spec/ui, but @object-ui/plugin-grid does not declare @objectstack/spec in its dependencies. Under pnpm this can fail to typecheck/build with "Cannot find module '@objectstack/spec/ui'". Prefer importing I18nLabel via @object-ui/types (it re-exports the spec types), or add @objectstack/spec as a direct dependency of this package.

Suggested change
import type { ObjectGridSchema, DataSource, ListColumn, ViewData } from '@object-ui/types';
import type { I18nLabel } from '@objectstack/spec/ui';
import { SchemaRenderer, useDataScope, useNavigationOverlay, useAction, useObjectTranslation, useSafeFieldLabel } from '@object-ui/react';
import { getCellRenderer, formatCurrency, formatCompactCurrency, formatDate, formatPercent, humanizeLabel } from '@object-ui/fields';
import {
import type { ObjectGridSchema, DataSource, ListColumn, ViewData, I18nLabel } from '@object-ui/types';
import { SchemaRenderer, useDataScope, useNavigationOverlay, useAction, useObjectTranslation, useSafeFieldLabel } from '@object-ui/react';
import { getCellRenderer, formatCurrency, formatCompactCurrency, formatDate, formatPercent, humanizeLabel } from '@object-ui/fields';
import {
import {

Copilot uses AI. Check for mistakes.
Expand Down Expand Up @@ -90,6 +91,13 @@ function useGridTranslation() {
}
}

/** Resolve an I18nLabel (string | {key, defaultValue}) to a plain string. */
function resolveColumnLabel(label: string | I18nLabel | undefined): string | undefined {
if (label == null) return undefined;
if (typeof label === 'string') return label;
return label.defaultValue || label.key;
}

export interface ObjectGridProps {
schema: ObjectGridSchema;
dataSource?: DataSource;
Expand Down Expand Up @@ -619,7 +627,7 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
return (cols as ListColumn[])
.filter((col) => col?.field && typeof col.field === 'string' && !col.hidden)
.map((col, colIndex) => {
const rawHeader = col.label || col.field.charAt(0).toUpperCase() + col.field.slice(1).replace(/_/g, ' ');
const rawHeader = resolveColumnLabel(col.label) || col.field.charAt(0).toUpperCase() + col.field.slice(1).replace(/_/g, ' ');
const header = schema.objectName ? resolveFieldLabel(schema.objectName, col.field, rawHeader) : rawHeader;

// Build custom cell renderer based on column configuration
Expand Down
3 changes: 2 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"scripts": {
Expand Down
3 changes: 2 additions & 1 deletion packages/tenant/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
Expand Down
10 changes: 10 additions & 0 deletions packages/types/src/complex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,16 @@ export interface DashboardWidgetSchema {
* Aligned with @objectstack/spec DashboardWidgetSchema.responsive.
*/
responsive?: any;
/**
* Enable search input for table-type widgets.
* @default false
*/
searchable?: boolean;
/**
* Enable pagination for table-type widgets.
* @default false
*/
pagination?: boolean;
Comment on lines +547 to +556
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

DashboardWidgetSchema gained searchable/pagination, but the corresponding Zod validators in packages/types/src/zod/complex.zod.ts (both DashboardWidgetSchema and DashboardWidgetConfigSchema) don't include these keys. Because Zod object() strips unknown properties by default, validating/parsing dashboard configs can silently drop these new flags. Please extend the Zod schemas (and add/adjust a unit test in packages/types/src/__tests__/dashboard-config.test.ts to cover them).

Copilot uses AI. Check for mistakes.
/**
* ARIA accessibility attributes.
* Aligned with @objectstack/spec AriaPropsSchema.
Expand Down