Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@sealday
Copy link
Contributor

@sealday sealday commented Jul 21, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive schema initializer system, including plugins, UI components, and utilities for dynamic schema-driven UI construction and management.
    • Added numerous React components and hooks for form item, table column, action, and block initializers, supporting advanced customization and association handling.
    • Implemented searchable, hierarchical menus for selecting collections and templates during schema initialization.
    • Added support for dynamic ARIA labeling, custom field and action initializers, and context-aware UI behaviors.
    • Provided extensive documentation and consolidated exports for easier integration and usage.
  • Documentation

    • Added detailed documentation covering schema initializer usage, customization, and example scenarios.
  • Chores

    • Added a new runtime dependency for improved TypeScript project management.
  • Refactor

    • Updated type annotations for improved specificity and consistency in variable and option handling throughout the codebase.
  • Style

    • Introduced new style hooks for consistent UI appearance in schema initializer menus.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 21, 2025

Walkthrough

This update introduces a comprehensive schema initializer system for a schema-driven UI framework, primarily within the packages/module-web package. It adds a new plugin (SchemaInitializerPlugin) and numerous React components, hooks, and utilities for dynamic schema creation, insertion, and management. Type refinements and export adjustments are made throughout the client codebase to support these features. Documentation and style modules are also included.

Changes

Files/Paths Change Summary
apps/tego/src/plugin-presets.ts Reordered the 'web' string in the private #builtInPlugins array of PluginPresets.
package.json Added "ts-morph": "^26.0.0" as a runtime dependency.
packages/client/src/built-in/index.tsx Removed registration of SchemaInitializerPlugin from built-in plugins.
packages/client/src/schema-component/antd/action/index.tsx
packages/client/src/schema-component/common/index.ts
Added exports: linkageAction and parseVariables.
packages/client/src/schema-initializer/components/assigned-field/AssignedField.tsx
packages/client/src/schema-settings/SchemaSettingsDefaultValue.tsx
packages/client/src/schema-settings/VariableInput/VariableInput.tsx
packages/client/src/schema-settings/VariableInput/hooks/useBaseVariable.tsx
packages/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx
packages/client/src/schema-settings/VariableInput/index.ts
packages/client/src/schema-settings/VariableInput/type.ts
Renamed type Option to VariableInputOption throughout, updated type annotations, and adjusted related exports.
packages/module-web/src/client/index.tsx Changed plugin addition to be asynchronous; added SchemaInitializerPlugin to app plugin manager.
packages/module-web/src/client/schema-initializer/SchemaInitializerPlugin.ts New: Implements SchemaInitializerPlugin class for registering schema initializers and components.
packages/module-web/src/client/schema-initializer/buttons/CustomFormItemInitializers.tsx
FormItemInitializers.tsx
RecordBlockInitializers.tsx
SubTableActionInitializers.tsx
TabPaneInitializers.tsx
New: Added multiple React components and initializers for form items, record blocks, sub-table actions, and tab panes.
packages/module-web/src/client/schema-initializer/buttons/index.ts New: Re-exports all button initializers and association filter components.
packages/module-web/src/client/schema-initializer/components/CreateRecordAction.tsx
DeletedField.tsx
components/assigned-field/AssignedField.tsx
components/assigned-field/index.ts
components/index.ts
New: Added components for record creation, deleted field display, and assigned field handling; created index exports.
packages/module-web/src/client/schema-initializer/hooks/useGetAriaLabelOfSchemaInitializer.ts New: Added hook to generate ARIA labels for schema initializers.
packages/module-web/src/client/schema-initializer/index.md New: Documentation for schema initializer usage and scenarios.
packages/module-web/src/client/schema-initializer/index.ts New: Centralized export file for schema initializer modules, utilities, and plugin.
packages/module-web/src/client/schema-initializer/items/ActionInitializer.tsx
BlockInitializer.tsx
CreateFilterActionInitializer.tsx
CreateResetActionInitializer.tsx
CustomFilterFormItemInitializer.tsx
CustomizeActionInitializer.tsx
DataBlockInitializer.tsx
DeleteEventActionInitializer.tsx
FilterBlockInitializer.tsx
G2PlotInitializer.tsx
InitializerWithSwitch.tsx
RecordAssociationBlockInitializer.tsx
RecordAssociationDetailsBlockInitializer.tsx
RecordAssociationFormBlockInitializer.tsx
RecordAssociationGridCardBlockInitializer.tsx
RecordAssociationListBlockInitializer.tsx
RecordReadPrettyAssociationFormBlockInitializer.tsx
SelectActionInitializer.tsx
SubmitActionInitializer.tsx
TableActionColumnInitializer.tsx
items/index.tsx
New: Added a suite of schema initializer item components and hooks for actions, blocks, filters, associations, and custom items.
packages/module-web/src/client/schema-initializer/style.ts New: Added style hook for menu item groups and submenus.
packages/module-web/src/client/schema-initializer/utils.ts New: Introduced comprehensive utilities and hooks for schema initialization, manipulation, and data source management.

Sequence Diagram(s)

sequenceDiagram
    participant App
    participant PluginManager
    participant SchemaInitializerPlugin
    participant UIComponents
    participant SchemaInitializerManager

    App->>PluginManager: Add SchemaInitializerPlugin (async)
    PluginManager->>SchemaInitializerPlugin: load()
    SchemaInitializerPlugin->>UIComponents: Register components (blocks, actions, fields)
    SchemaInitializerPlugin->>SchemaInitializerManager: Register initializer groups (blocks, actions, etc)
    Note over App,SchemaInitializerManager: App now supports dynamic schema initialization via registered components and initializers
Loading

Estimated code review effort

5 (~2+ hours): The changes are extensive, introducing many new files, components, hooks, and utilities, along with complex schema manipulation logic and type refactoring across the codebase.

Possibly related PRs

  • feat: main app signin #639: Adds a new plugin entry 'auth-main-app' to the same private plugin array in apps/tego/src/plugin-presets.ts, directly relating to the management of built-in or local plugins.

Poem

In fields of code where schemas grow,
A bunny hops, with joy to show—
New plugins bloom, and blocks align,
Forms and tables intertwine.
With hooks and utils, menus spin,
Dynamic UIs now begin!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/move-to-module-web

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sealday sealday marked this pull request as draft July 21, 2025 19:26
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 47

♻️ Duplicate comments (3)
packages/module-web/src/client/schema-initializer/items/RecordAssociationDetailsBlockInitializer.tsx (1)

25-37: Add consistent error handling

This component has the same async error handling issue as other initializers.

packages/module-web/src/client/schema-initializer/items/RecordAssociationGridCardBlockInitializer.tsx (1)

52-64: Apply consistent null checking pattern

Like other association block creators, this should include defensive null checks.

packages/module-web/src/client/schema-initializer/items/RecordAssociationFormBlockInitializer.tsx (1)

27-50: Add error handling for async template fetching.

Same issue as in RecordReadPrettyAssociationFormBlockInitializer - the async call could fail without proper error handling.

🧹 Nitpick comments (36)
packages/client/src/schema-component/antd/action/index.tsx (1)

11-12: Export looks good – remember to re-export typings

The new named export matches existing usage in schema-initializer/components/CreateRecordAction.tsx.
Ensure linkageAction’s TypeScript types are also exported from ./utils to avoid any leakage.

packages/module-web/src/client/schema-initializer/components/assigned-field/index.ts (1)

1-1: Minor: maintain naming consistency

Elsewhere barrel files are named index.tsx; here it’s index.ts.
Not harmful, but using the same extension across packages avoids needless double compilation.

packages/module-web/src/client/schema-initializer/items/FilterBlockInitializer.tsx (1)

1-3: Consider documenting the alias relationship.

While the alias export pattern is syntactically correct, consider adding a comment explaining why FilterBlockInitializer is aliased to DataBlockInitializer. This would help future maintainers understand the relationship and prevent confusion about whether these should diverge in functionality.

+// FilterBlockInitializer is currently an alias for DataBlockInitializer
+// TODO: Consider if FilterBlockInitializer should have specialized behavior
 import { DataBlockInitializer } from './DataBlockInitializer';

 export const FilterBlockInitializer = DataBlockInitializer;
packages/module-web/src/client/schema-initializer/components/DeletedField.tsx (1)

5-5: Consider using CSS classes instead of inline styles.

For better maintainability and consistency with design systems, consider using CSS classes or styled components instead of inline styles.

-  return <div style={{ color: '#ccc' }}>{t('The field has been deleted')}</div>;
+  return <div className="deleted-field-message">{t('The field has been deleted')}</div>;
packages/module-web/src/client/schema-initializer/items/SubmitActionInitializer.tsx (1)

10-10: Consider removing or documenting the commented code.

The commented x-designer line should either be removed if it's no longer needed or documented with a TODO comment explaining why it's temporarily disabled.

-    // 'x-designer': 'Action.Designer',
+    // TODO: Re-enable when Action.Designer is available
+    // 'x-designer': 'Action.Designer',
packages/module-web/src/client/schema-initializer/items/G2PlotInitializer.tsx (1)

5-18: Consider adding error boundary or validation.

The component assumes useSchemaInitializerItem and useSchemaInitializer will always return valid objects. Consider adding basic validation to make the component more robust.

export const G2PlotInitializer = () => {
  const itemConfig = useSchemaInitializerItem();
  const { insert } = useSchemaInitializer();
+  
+  if (!itemConfig || !insert) {
+    return null;
+  }
+  
  return (
    <SchemaInitializerItem
      {...itemConfig}
      onClick={() => {
        insert({
          ...(itemConfig.schema || {}),
        });
      }}
    />
  );
};
packages/module-web/src/client/schema-initializer/items/CustomizeActionInitializer.tsx (2)

8-8: Clarify duplicate prop passing pattern.

The component passes itemConfig both as spread props ({...itemConfig}) and as an explicit item prop. This could lead to confusion or unintended prop overrides.

Consider clarifying the intent or removing redundancy:

-  return <BlockInitializer {...itemConfig} item={itemConfig} />;
+  return <BlockInitializer item={itemConfig} />;

Or if both patterns are needed, add a comment explaining why:

+  // Pass itemConfig as both spread props and item prop for BlockInitializer compatibility
   return <BlockInitializer {...itemConfig} item={itemConfig} />;

6-9: Consider adding error handling.

Like other initializer components, this could benefit from basic validation to handle cases where the hook returns undefined.

export const CustomizeActionInitializer = () => {
  const itemConfig = useSchemaInitializerItem();
+  
+  if (!itemConfig) {
+    return null;
+  }
+  
  return <BlockInitializer {...itemConfig} item={itemConfig} />;
};
packages/module-web/src/client/schema-initializer/hooks/useGetAriaLabelOfSchemaInitializer.ts (1)

12-12: Consider migrating from deprecated hook.

The hook uses useCollection_deprecated which suggests there's a newer version available. Consider updating to the non-deprecated alternative if available.

packages/module-web/src/client/schema-initializer/buttons/CustomFormItemInitializers.tsx (2)

10-10: Replace Chinese comment with English.

The comment should be in English for better maintainability in an international codebase.

-// 表单里配置字段
+// Configure fields in form

7-7: Consider adding type annotations for better maintainability.

The component lacks proper TypeScript type annotations which could help catch potential runtime errors.

-export const InitializerWithSwitch = (props) => {
+interface InitializerWithSwitchProps {
+  type: string;
+  schema?: any;
+  item: any;
+  remove?: () => void;
+  disabled?: boolean;
+}
+
+export const InitializerWithSwitch: React.FC<InitializerWithSwitchProps> = (props) => {
packages/module-web/src/client/schema-initializer/items/TableActionColumnInitializer.tsx (2)

7-34: Add proper TypeScript types for better maintainability.

The component lacks type annotations which could help prevent runtime errors and improve developer experience.

+interface TableActionColumnInitializerProps {
+  // Add props interface based on usage
+}
+
-export const TableActionColumnInitializer = () => {
+export const TableActionColumnInitializer: React.FC<TableActionColumnInitializerProps> = () => {

13-16: Consider making component properties configurable.

The hardcoded width and fixed position values could benefit from being configurable through props or context.

    'x-component-props': {
-     width: 150,
-     fixed: 'right',
+     width: props.width ?? 150,
+     fixed: props.fixed ?? 'right',
    },
packages/module-web/src/client/schema-initializer/items/SelectActionInitializer.tsx (2)

5-5: Add TypeScript interface for props.

The component lacks proper type annotations for its props.

+interface SelectActionInitializerProps {
+  [key: string]: any; // Based on ActionInitializer props
+}
+
-export const SelectActionInitializer = (props) => {
+export const SelectActionInitializer: React.FC<SelectActionInitializerProps> = (props) => {

6-59: Consider extracting large schema definition.

The schema object is quite large and complex. Consider extracting it to improve readability and reusability.

+const SELECT_ACTION_SCHEMA = {
+  type: 'void',
+  title: '{{ t("Select") }}',
+  'x-action': 'update',
+  // ... rest of schema
+};
+
export const SelectActionInitializer: React.FC<SelectActionInitializerProps> = (props) => {
-  const schema = {
-    type: 'void',
-    title: '{{ t("Select") }}',
-    // ... large schema object
-  };
-  return <ActionInitializer {...props} schema={schema} />;
+  return <ActionInitializer {...props} schema={SELECT_ACTION_SCHEMA} />;
};
packages/module-web/src/client/schema-initializer/buttons/SubTableActionInitializers.tsx (1)

3-35: Consider adding type annotations for the schema initializer configuration.

While the current configuration works, adding proper TypeScript types would improve maintainability and catch potential configuration errors.

+import type { SchemaInitializerOptions } from '@tachybase/client';
+
+const subTableActionInitializerConfig: SchemaInitializerOptions = {
  name: 'subTable:configureActions',
  title: "{{t('Configure actions')}}",
  icon: 'SettingOutlined',
  // ... rest of configuration
+};
+
-export const subTableActionInitializers = new SchemaInitializer({
-  name: 'subTable:configureActions',
-  // ... configuration
-});
+export const subTableActionInitializers = new SchemaInitializer(subTableActionInitializerConfig);
packages/module-web/src/client/schema-initializer/items/BlockInitializer.tsx (1)

15-15: Add error handling for optional method calls.

The optional chaining for item?.schemaInitialize?.(s) is good, but consider adding error handling in case the method throws an exception.

        const s = merge(schema || {}, item.schema || {});
-       item?.schemaInitialize?.(s);
+       try {
+         item?.schemaInitialize?.(s);
+       } catch (error) {
+         console.error('Schema initialization failed:', error);
+       }
        insert(s);
packages/module-web/src/client/schema-initializer/items/RecordAssociationListBlockInitializer.tsx (1)

23-23: Icon doesn't match the block type

The component uses TableOutlined icon for a List block initializer. Consider using a more appropriate icon like UnorderedListOutlined or OrderedListOutlined to better represent the list block type.

-      icon={<TableOutlined />}
+      icon={<UnorderedListOutlined />}

Don't forget to import the new icon:

-import { TableOutlined } from '@ant-design/icons';
+import { UnorderedListOutlined } from '@ant-design/icons';
packages/module-web/src/client/schema-initializer/SchemaInitializerPlugin.ts (1)

3-63: Clean up commented imports

There are 60+ lines of commented imports. If these are planned features, consider adding a TODO comment explaining the timeline. If they're no longer needed, remove them to improve code clarity.

Would you like me to help identify which of these commented imports are still needed based on the current codebase?

packages/module-web/src/client/schema-initializer/items/RecordAssociationDetailsBlockInitializer.tsx (1)

22-22: Consider more appropriate icon for Details block

Using FormOutlined for a Details block might be misleading. Consider using FileTextOutlined or ProfileOutlined which better represents detail views.

-      icon={<FormOutlined />}
+      icon={<FileTextOutlined />}
packages/client/src/schema-settings/VariableInput/hooks/useBaseVariable.tsx (1)

151-206: Consider using requestIdleCallback instead of setTimeout

Using setTimeout with a 5ms delay to prevent UI blocking is a workaround. Consider using requestIdleCallback for better performance optimization or proper async handling.

     return new Promise((resolve) => {
-      setTimeout(() => {
+      const processChildren = () => {
         const children = (
           getChildren(returnFields(getFilterOptions(target, dataSource), option), {
             // ... parameters
           }) || []
         )
         // ... sorting logic
         
         if (children.length === 0) {
           option.disabled = true;
           option.isLeaf = true;
           resolve();
           return;
         }
         option.children = children;
         resolve();
-      }, 5);
+      };
+      
+      if ('requestIdleCallback' in window) {
+        requestIdleCallback(processChildren);
+      } else {
+        setTimeout(processChildren, 0);
+      }
     });
packages/module-web/src/client/schema-initializer/items/RecordAssociationGridCardBlockInitializer.tsx (1)

26-26: Icon consistency for GridCard

Using TableOutlined for a GridCard block. Consider using AppstoreOutlined which better represents a grid/card layout.

-      icon={<TableOutlined />}
+      icon={<AppstoreOutlined />}
packages/module-web/src/client/schema-initializer/items/RecordReadPrettyAssociationFormBlockInitializer.tsx (1)

61-103: Consider extracting shared schema creation logic.

There's significant code duplication between the component's onClick handler and the hook's callbacks. Both implement similar logic for creating details UI schemas with templates.

Consider extracting the shared logic into a utility function:

function createDetailsSchemaWithTemplate(params: {
  collection: any;
  association: string;
  templateSchema?: any;
  mode?: string;
  componentName?: string;
}) {
  const { collection, association, templateSchema, mode, componentName } = params;
  
  if (templateSchema && componentName === 'ReadPrettyFormItem') {
    const blockSchema = createDetailsUISchema({
      dataSource: collection.dataSource,
      association,
      templateSchema,
    });
    if (mode === 'reference') {
      blockSchema['x-template-key'] = templateSchema.key;
    }
    return blockSchema;
  }
  
  return templateSchema || createDetailsUISchema({
    dataSource: collection.dataSource,
    association,
  });
}

This would reduce duplication and make the code more maintainable.

packages/module-web/src/client/schema-initializer/buttons/RecordBlockInitializers.tsx (1)

357-366: Remove commented code.

Commented-out code should be removed to keep the codebase clean. Version control preserves the history if needed.

-    // {
-    //   type: 'itemGroup',
-    //   name: 'relationshipBlocks',
-    //   title: '{{t("Relationship blocks")}}',
-    //   useChildren: useRelationFields,
-    //   useVisible() {
-    //     const res = useRelationFields();
-    //     return res.length > 0;
-    //   },
-    // },
packages/module-web/src/client/schema-initializer/buttons/FormItemInitializers.tsx (3)

23-32: Use map instead of forEach with push.

The current pattern of using forEach with push can be simplified using map and filter.

-  const res = [];
-  inheritFields.forEach((inherit) => {
-    Object.values(inherit)[0].length &&
-      res.push({
-        type: 'itemGroup',
-        divider: true,
-        title: t(`Parent collection fields`) + '(' + compile(`${Object.keys(inherit)[0]}`) + ')',
-        children: Object.values(inherit)[0],
-      });
-  });
+  const res = inheritFields
+    .filter((inherit) => Object.values(inherit)[0].length > 0)
+    .map((inherit) => ({
+      type: 'itemGroup',
+      divider: true,
+      title: t(`Parent collection fields`) + '(' + compile(`${Object.keys(inherit)[0]}`) + ')',
+      children: Object.values(inherit)[0],
+    }));

70-81: Simplify nested ternary operators for better readability.

The nested ternary operators make the code hard to read and maintain.

-'x-component-props': isFileCollection
-  ? {
-      fieldNames: {
-        label: 'preview',
-        value: 'id',
-      },
-    }
-  : isAssociationField && fieldNames
-    ? {
-        fieldNames: { ...fieldNames, label: targetCollection?.titleField || fieldNames.label },
-      }
-    : {},
+'x-component-props': (() => {
+  if (isFileCollection) {
+    return {
+      fieldNames: {
+        label: 'preview',
+        value: 'id',
+      },
+    };
+  }
+  if (isAssociationField && fieldNames) {
+    return {
+      fieldNames: { 
+        ...fieldNames, 
+        label: targetCollection?.titleField || fieldNames.label 
+      },
+    };
+  }
+  return {};
+})(),

136-145: Use map for consistency with suggested refactoring.

Apply the same map pattern for consistency if the previous suggestion is accepted.

-  const res = [];
-  if (inheritFields?.length > 0) {
-    inheritFields.forEach((inherit) => {
-      Object.values(inherit)[0].length &&
-        res.push({
-          divider: true,
-          type: 'itemGroup',
-          title: t(`Parent collection fields`) + '(' + compile(`${Object.keys(inherit)[0]}`) + ')',
-          children: Object.values(inherit)[0],
-        });
-    });
-  }
+  const res = inheritFields?.length > 0
+    ? inheritFields
+        .filter((inherit) => Object.values(inherit)[0].length > 0)
+        .map((inherit) => ({
+          divider: true,
+          type: 'itemGroup',
+          title: t(`Parent collection fields`) + '(' + compile(`${Object.keys(inherit)[0]}`) + ')',
+          children: Object.values(inherit)[0],
+        }))
+    : [];
packages/module-web/src/client/schema-initializer/components/CreateRecordAction.tsx (2)

25-102: Consolidate duplicate style definitions.

The designer and actionDesigner styles are nearly identical. Consider extracting common styles.

const useStyles = createStyles(({ css }) => {
  const commonStyle = css`
    position: relative;
    &:hover {
      .general-schema-designer {
        display: block;
      }
    }
    .general-schema-designer {
      position: absolute;
      z-index: 999;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      display: none;
      background: var(--colorBgSettingsHover);
      border: 0;
      pointer-events: none;
      > .general-schema-designer-icons {
        position: absolute;
        right: 2px;
        top: 2px;
        line-height: 16px;
        pointer-events: all;
        .ant-space-item {
          background-color: var(--colorSettings);
          color: #fff;
          line-height: 16px;
          width: 16px;
          padding-left: 1px;
          align-self: stretch;
        }
      }
    }
  `;
  
  return {
    designer: commonStyle,
    actionDesigner: commonStyle,
  };
});

383-383: Update function call to match suggested rename.

If the getLinkageCollection function is renamed as suggested, update the call here.

-          const collectionName = getLinkageCollection(linkageFromForm, form, field);
+          const collectionName = parseLinkageCollectionVariable(linkageFromForm, form, field);
packages/module-web/src/client/schema-initializer/components/assigned-field/AssignedField.tsx (3)

60-62: Remove commented code

Dead code should be removed to improve code maintainability.

-    // if (!field.validator && uiSchema['x-validator']) {
-    //   field.validator = uiSchema['x-validator'];
-    // }

76-76: Optimize useEffect dependency

Using JSON.stringify(uiSchema) in the dependency array is inefficient as it runs on every render. Consider using a deep comparison hook or memoizing the stringified value.

-  }, [JSON.stringify(uiSchema)]);
+  }, [uiSchema]);

If deep comparison is needed, consider using a custom hook like useDeepCompareEffect or memoizing the value:

const uiSchemaKey = useMemo(() => JSON.stringify(uiSchema), [uiSchema]);
useEffect(() => {
  // effect logic
}, [uiSchemaKey]);

136-136: Optimize dependency array in useCallback

Using JSON.stringify with _.omit in the dependency array is inefficient and runs on every render.

Consider memoizing the filtered props:

const memoizedProps = useMemo(() => _.omit(props, 'value'), [props]);

const renderSchemaComponent = useCallback(
  ({ value, onChange }): React.JSX.Element => {
    return <CollectionField {...props} value={value} onChange={onChange} />;
  },
  [memoizedProps],
);
packages/module-web/src/client/schema-initializer/items/DataBlockInitializer.tsx (2)

103-105: Replace delete operator with undefined assignment

The delete operator can impact performance. Use undefined assignment instead.

-        delete container.style.height;
+        container.style.height = undefined;

116-126: Add return type annotation for hook

The hook should have a proper return type for better type safety.

-export function useMenuSearch({
+export function useMenuSearch({
   data,
   openKeys,
   showType,
   hideSearch,
 }: {
   data: any[];
   openKeys: string[];
   showType?: boolean;
   hideSearch?: boolean;
-}) {
+}): MenuProps['items'] {
packages/module-web/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx (1)

162-322: Refactor long handleClick function

This function is very long (160 lines) and handles multiple responsibilities. Consider breaking it down into smaller, more focused functions.

Consider extracting the schema generation and dialog configuration into separate functions:

const createFieldSchema = (values, cm, getInterface) => {
  const { title, component, collection, associationField, props } = values;
  const defaultSchema = getInterface(component)?.default?.uiSchema || {};
  const titleField = cm.getCollection(collection)?.titleField;
  const name = uid();
  
  return {
    ...defaultSchema,
    type: 'string',
    title: title,
    name: '__custom.' + name,
    // ... rest of schema
  };
};

const createDialogSchema = (fieldComponents, allCollection, t) => {
  return {
    properties: {
      name: { type: 'string', required: true },
      // ... rest of properties
    }
  };
};
packages/module-web/src/client/schema-initializer/buttons/TabPaneInitializers.tsx (1)

30-36: Replace delete operators with undefined assignments

Using the delete operator can impact performance. Assign undefined instead.

        const deleteUid = (s: ISchema) => {
-          delete s['name'];
-          delete s['x-uid'];
+          s['name'] = undefined;
+          s['x-uid'] = undefined;
          Object.keys(s.properties || {}).forEach((key) => {
            deleteUid(s.properties[key]);
          });
        };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b32a538 and 8087af2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (51)
  • apps/tego/src/plugin-presets.ts (1 hunks)
  • package.json (1 hunks)
  • packages/client/src/built-in/index.tsx (0 hunks)
  • packages/client/src/schema-component/antd/action/index.tsx (1 hunks)
  • packages/client/src/schema-component/common/index.ts (1 hunks)
  • packages/client/src/schema-initializer/components/assigned-field/AssignedField.tsx (2 hunks)
  • packages/client/src/schema-settings/SchemaSettingsDefaultValue.tsx (4 hunks)
  • packages/client/src/schema-settings/VariableInput/VariableInput.tsx (3 hunks)
  • packages/client/src/schema-settings/VariableInput/hooks/useBaseVariable.tsx (6 hunks)
  • packages/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx (5 hunks)
  • packages/client/src/schema-settings/VariableInput/index.ts (1 hunks)
  • packages/client/src/schema-settings/VariableInput/type.ts (1 hunks)
  • packages/module-web/src/client/index.tsx (2 hunks)
  • packages/module-web/src/client/schema-initializer/SchemaInitializerPlugin.ts (1 hunks)
  • packages/module-web/src/client/schema-initializer/buttons/CustomFormItemInitializers.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/buttons/FormItemInitializers.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/buttons/RecordBlockInitializers.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/buttons/SubTableActionInitializers.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/buttons/TabPaneInitializers.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/buttons/index.ts (1 hunks)
  • packages/module-web/src/client/schema-initializer/components/CreateRecordAction.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/components/DeletedField.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/components/assigned-field/AssignedField.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/components/assigned-field/index.ts (1 hunks)
  • packages/module-web/src/client/schema-initializer/components/index.ts (1 hunks)
  • packages/module-web/src/client/schema-initializer/hooks/useGetAriaLabelOfSchemaInitializer.ts (1 hunks)
  • packages/module-web/src/client/schema-initializer/index.md (1 hunks)
  • packages/module-web/src/client/schema-initializer/index.ts (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/ActionInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/BlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/CreateFilterActionInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/CreateResetActionInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/CustomizeActionInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/DataBlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/DeleteEventActionInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/FilterBlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/G2PlotInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/InitializerWithSwitch.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/RecordAssociationBlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/RecordAssociationDetailsBlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/RecordAssociationFormBlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/RecordAssociationGridCardBlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/RecordAssociationListBlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/RecordReadPrettyAssociationFormBlockInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/SelectActionInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/SubmitActionInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/TableActionColumnInitializer.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/items/index.tsx (1 hunks)
  • packages/module-web/src/client/schema-initializer/style.ts (1 hunks)
  • packages/module-web/src/client/schema-initializer/utils.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/client/src/built-in/index.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
packages/module-web/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx

[error] 414-414: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

packages/module-web/src/client/schema-initializer/items/DataBlockInitializer.tsx

[error] 116-119: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

packages/module-web/src/client/schema-initializer/buttons/TabPaneInitializers.tsx

[error] 31-31: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 32-32: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI
🔇 Additional comments (28)
apps/tego/src/plugin-presets.ts (1)

18-20: Re-ordering web can affect plugin dependency resolution – re-verify boot sequence

web is now loaded immediately after file instead of much later.
If any built-in/local plugin implicitly assumes that web is registered after something (e.g. it monkey-patches hooks exported by web), this silent reorder could break runtime.
No problems are obvious from this diff alone, but please run an integration smoke-test (all presets enabled) before merging.

packages/module-web/src/client/schema-initializer/components/index.ts (1)

1-2: 👍 Consolidated barrel export

Barrel file keeps import paths short; no issues spotted.

packages/client/src/schema-component/common/index.ts (1)

3-3: LGTM - Clean export addition.

The export follows the existing pattern and makes the parseVariables utility publicly available, which aligns with the schema initializer refactoring objectives.

packages/client/src/schema-initializer/components/assigned-field/AssignedField.tsx (2)

16-16: LGTM - Type system improvement.

The change from Option to VariableInputOption provides better type specificity and aligns with the broader type system refinements mentioned in the AI summary.


115-115: LGTM - Consistent type annotation update.

The type annotation update correctly corresponds to the import change, maintaining type consistency throughout the callback function.

packages/module-web/src/client/schema-initializer/items/SubmitActionInitializer.tsx (1)

5-19: LGTM - Well-structured submit action schema.

The component properly defines a submit action schema with appropriate properties:

  • Correct action type and component
  • Proper toolbar and settings configuration
  • HTML submit behavior configured correctly
  • Props are properly forwarded to ActionInitializer

The implementation follows good React patterns and schema conventions.

packages/client/src/schema-settings/VariableInput/index.ts (1)

3-4: LGTM! Clean export consolidation.

The new exports for formatVariableScop utils and VariableInputOption type are correctly formatted and align with the broader type refactoring effort across the codebase.

packages/client/src/schema-settings/SchemaSettingsDefaultValue.tsx (3)

24-24: LGTM! Consistent type refactoring.

The import change from Option to VariableInputOption aligns with the broader type standardization effort across the codebase.


80-80: LGTM! Type annotation updated correctly.

The parameter type change to VariableInputOption[] maintains consistency with the imported type.


117-117: LGTM! Clean object spread simplification.

Removing the redundant fallback expressions || {} simplifies the code without changing behavior, as the spread operator handles undefined values appropriately.

Also applies to: 158-158

packages/module-web/src/client/schema-initializer/style.ts (1)

3-17: LGTM! Well-structured styling module.

The style definitions are clean and appropriate for menu components:

  • Reasonable 50vh max-height constraint prevents overly tall menus
  • Vertical scrolling enables navigation of long menu lists
  • Proper use of theme tokens for consistent design system integration
  • Descriptive naming convention for style objects
packages/module-web/src/client/schema-initializer/items/DeleteEventActionInitializer.tsx (1)

16-16: CalendarV2.DeleteEvent is properly exported and available at runtime

  • packages/plugin-block-calendar/src/client/calendar/DeleteEvent.tsx defines and exports DeleteEvent
  • packages/plugin-block-calendar/src/client/calendar/index.ts attaches it via
    Calendar.DeleteEvent = DeleteEvent and then aliases const CalendarV2 = Calendar
  • The schema initializer’s 'x-component': 'CalendarV2.DeleteEvent' will resolve correctly

No further changes required.

packages/client/src/schema-settings/VariableInput/type.ts (1)

5-18: LGTM! Improved type name clarity.

The rename from Option to VariableInputOption provides better semantic clarity and reduces potential naming conflicts. All internal references have been consistently updated.

packages/module-web/src/client/schema-initializer/items/index.tsx (1)

1-24: LGTM! Well-organized index file.

The index file provides a clean consolidation of schema initializer exports with good organization:

  • Clear separation of association filter exports with comments
  • Logical grouping of related initializers
  • Consistent export patterns
packages/module-web/src/client/index.tsx (2)

2-2: LGTM: Proper import of SchemaInitializerPlugin.

The import statement correctly includes the new SchemaInitializerPlugin for integration into the module.


27-33: Verify async support in Plugin.afterAdd

I wasn’t able to locate the base Plugin class or its afterAdd signature in the codebase. Please ensure that the afterAdd hook in your Plugin base class is declared to support async operations (i.e., marked async or returning a Promise<void>) so that awaiting in subclasses works correctly.

  • Confirm the afterAdd method signature in the Plugin base class
  • Update it to async afterAdd() or afterAdd(): Promise<void> if needed
packages/module-web/src/client/schema-initializer/buttons/index.ts (1)

1-8: LGTM: Well-organized barrel export file.

The export structure is clean and follows good practices:

  • Clear grouping of related button initializers
  • Descriptive comment for the association filter export
  • Consistent use of wildcard exports for related modules
packages/module-web/src/client/schema-initializer/index.md (1)

1-72: Excellent comprehensive documentation.

This documentation file provides thorough coverage of the SchemaInitializer feature:

  • Clear explanation of insertion positions with visual code examples
  • Well-organized sections covering basic to advanced usage
  • Multiple practical examples with references to demo files
  • Good coverage of customization options and dynamic features

The documentation will be valuable for developers using this schema initializer system.

packages/module-web/src/client/schema-initializer/buttons/SubTableActionInitializers.tsx (1)

19-19: String references verified – no changes needed.

Confirmed that both CreateActionInitializer and BulkDestroyActionInitializer are exported in
packages/client/src/modules/actions/... and imported & registered in
packages/client/src/schema-initializer/SchemaInitializerPlugin.ts. The string keys in
SubTableActionInitializers.tsx will resolve correctly at runtime.

packages/client/src/schema-settings/VariableInput/VariableInput.tsx (1)

17-17: LGTM! Type refinement improves specificity.

The consistent replacement of the generic Option type with VariableInputOption throughout the file enhances type safety and specificity without altering functionality.

Also applies to: 60-60, 246-246, 253-253

packages/client/src/schema-settings/VariableInput/hooks/useContextAssociationFields.tsx (1)

8-8: LGTM! Consistent type refinement across the hook.

The systematic replacement of Option with VariableInputOption maintains consistency with the broader type refinement effort while preserving all functionality.

Also applies to: 25-25, 34-34, 36-36, 82-82, 136-136

packages/module-web/src/client/schema-initializer/items/RecordAssociationBlockInitializer.tsx (2)

11-14: Question: Why is new component immediately deprecated?

It's unusual to introduce a new component with a @deprecated annotation. This suggests the component might be transitional or part of a migration strategy. Consider documenting the migration path or timeline for replacement.


6-6: Note: Usage of deprecated collection manager hooks.

The component uses useCollectionManager_deprecated which suggests this code might need updating when the deprecated hook is removed. Monitor for migration opportunities to the non-deprecated version.

Also applies to: 19-19, 48-48

packages/module-web/src/client/schema-initializer/index.ts (1)

1-21: LGTM! Clean module organization with appropriate export structure.

The barrel export pattern provides a clean public API and logical grouping of related functionality. The mix of wildcard and named exports is appropriate for the different module types.

packages/client/src/schema-settings/VariableInput/hooks/useBaseVariable.tsx (1)

7-7: Type refinement looks good

The change from generic Option to specific VariableInputOption improves type safety and code clarity.

packages/module-web/src/client/schema-initializer/items/RecordAssociationGridCardBlockInitializer.tsx (1)

11-14: Clarify deprecation status

This component is marked as deprecated but is being added as new code. This is confusing - why introduce already-deprecated functionality? Consider either:

  1. Removing the deprecation if this is intended for use
  2. Not adding this component if it's truly deprecated
  3. Adding more context about the deprecation timeline and migration path
packages/module-web/src/client/schema-initializer/buttons/FormItemInitializers.tsx (1)

46-47: Address the FIXME comment.

The FIXME comment indicates unresolved technical debt that should be addressed.

Could you provide more context about what needs to be fixed with the readPretty value? This will help in properly addressing the issue.

packages/module-web/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx (1)

389-401: Add protection against potential infinite loop

The while loop could run indefinitely if there are circular references in the field relationships.

     let cName: any = collectionName;
     let field: any;
     let title = Schema.compile(collection?.title, { t: i18n.t });
+    const visited = new Set<string>();
     while (cName && fieldNames.length > 0) {
+      if (visited.has(cName)) {
+        console.warn('Circular reference detected in collection fields');
+        break;
+      }
+      visited.add(cName);
       const fileName = fieldNames.shift();
       field = getCollectionField(`${cName}.${fileName}`);

Likely an incorrect or invalid review comment.

Comment on lines +39 to +41
"dependencies": {
"ts-morph": "^26.0.0"
},
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

ts-morph is heavy – confirm it’s really needed at runtime

ts-morph (~13 MB install, many transitive deps) has been added to the main dependencies block, which means:

  1. It ships to production containers/functions even if used only in build-time tooling.
  2. It increases cold-start size for serverless environments.

If the new schema-initializer code imports ts-morph only during code-gen/build (e.g. inside a CLI), move it to devDependencies:

-  "dependencies": {
-    "ts-morph": "^26.0.0"
-  },
+  "devDependencies": {
+    "ts-morph": "^26.0.0",
+    ...
+  },

Keep it here only when the browser / Node runtime dynamically requires it at execution time.


🏁 Script executed:

#!/bin/bash
# Find ts-morph entry in package.json
rg -n '"ts-morph"' -n package.json

# Search for any imports or requires of ts-morph in the codebase
rg -n "from ['\"]ts-morph['\"]|require\(['\"]ts-morph['\"]\)" .

Length of output: 139


Move ts-morph into devDependencies

We searched the repo and found no import or require of ts-morph in runtime code, so it isn’t used in production. To avoid bloating your production bundle/container, please relocate it to devDependencies.

• File: package.json (line 40)

   "dependencies": {
-    "ts-morph": "^26.0.0"
   },
+  "devDependencies": {
+    "ts-morph": "^26.0.0",
+    …other dev deps…
+  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"dependencies": {
"ts-morph": "^26.0.0"
},
"dependencies": {
},
"devDependencies": {
"ts-morph": "^26.0.0",
…other dev deps…
},
🤖 Prompt for AI Agents
In package.json around lines 39 to 41, the dependency "ts-morph" is currently
listed under "dependencies" but is not used in production runtime code. Move
"ts-morph" from the "dependencies" section to the "devDependencies" section to
prevent it from being included in the production bundle or container.

Comment on lines +17 to +25
inheritFields.forEach((inherit) => {
Object.values(inherit)[0].length &&
res.push({
type: 'itemGroup',
divider: true,
title: t(`Parent collection fields`) + '(' + compile(`${Object.keys(inherit)[0]}`) + ')',
children: Object.values(inherit)[0],
});
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify complex object manipulation and improve type safety.

The current implementation has complex nested object access patterns that could be fragile and lacks proper type safety.

Consider refactoring for better readability and type safety:

-  const res = [];
-  inheritFields.forEach((inherit) => {
-    Object.values(inherit)[0].length &&
-      res.push({
-        type: 'itemGroup',
-        divider: true,
-        title: t(`Parent collection fields`) + '(' + compile(`${Object.keys(inherit)[0]}`) + ')',
-        children: Object.values(inherit)[0],
-      });
-  });
+  const res = inheritFields.reduce((acc, inherit) => {
+    const [collectionName, fields] = Object.entries(inherit)[0];
+    if (fields?.length > 0) {
+      acc.push({
+        type: 'itemGroup',
+        divider: true,
+        title: `${t('Parent collection fields')} (${compile(collectionName)})`,
+        children: fields,
+      });
+    }
+    return acc;
+  }, []);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
inheritFields.forEach((inherit) => {
Object.values(inherit)[0].length &&
res.push({
type: 'itemGroup',
divider: true,
title: t(`Parent collection fields`) + '(' + compile(`${Object.keys(inherit)[0]}`) + ')',
children: Object.values(inherit)[0],
});
});
const res = inheritFields.reduce((acc, inherit) => {
const [collectionName, fields] = Object.entries(inherit)[0];
if (fields?.length > 0) {
acc.push({
type: 'itemGroup',
divider: true,
title: `${t('Parent collection fields')} (${compile(collectionName)})`,
children: fields,
});
}
return acc;
}, []);
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/buttons/CustomFormItemInitializers.tsx
around lines 17 to 25, the code uses complex nested object access with
Object.values and Object.keys which is fragile and not type-safe. Refactor by
explicitly typing the inheritFields array and destructuring the objects to
access keys and values directly. Replace Object.values(inherit)[0] and
Object.keys(inherit)[0] with clearer variable names and direct property access
to improve readability and ensure type safety.

Comment on lines +25 to +33
const recursiveParent = (schema: Schema) => {
if (!schema) return null;

if (schema['x-decorator']?.endsWith('BlockProvider')) {
return schema['x-decorator-props']?.['collection'];
} else {
return recursiveParent(schema.parent);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add recursion depth limit to prevent stack overflow.

The recursiveParent function could cause stack overflow with deeply nested or circular schema references.

Consider adding a depth limit:

-const recursiveParent = (schema: Schema) => {
+const recursiveParent = (schema: Schema, depth = 0, maxDepth = 50) => {
   if (!schema) return null;
+  if (depth > maxDepth) {
+    console.warn('Maximum recursion depth reached in recursiveParent');
+    return null;
+  }

   if (schema['x-decorator']?.endsWith('BlockProvider')) {
     return schema['x-decorator-props']?.['collection'];
   } else {
-    return recursiveParent(schema.parent);
+    return recursiveParent(schema.parent, depth + 1, maxDepth);
   }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const recursiveParent = (schema: Schema) => {
if (!schema) return null;
if (schema['x-decorator']?.endsWith('BlockProvider')) {
return schema['x-decorator-props']?.['collection'];
} else {
return recursiveParent(schema.parent);
}
};
const recursiveParent = (schema: Schema, depth = 0, maxDepth = 50) => {
if (!schema) return null;
if (depth > maxDepth) {
console.warn('Maximum recursion depth reached in recursiveParent');
return null;
}
if (schema['x-decorator']?.endsWith('BlockProvider')) {
return schema['x-decorator-props']?.['collection'];
} else {
return recursiveParent(schema.parent, depth + 1, maxDepth);
}
};
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/buttons/RecordBlockInitializers.tsx
around lines 25 to 33, the recursiveParent function lacks a recursion depth
limit, risking stack overflow on deeply nested or circular schema references.
Modify the function to accept a depth parameter that increments with each
recursive call and add a maximum depth check to stop recursion and return null
if the limit is exceeded, preventing infinite recursion and stack overflow.

Comment on lines +35 to +37
export const canMakeAssociationBlock = (field) => {
return ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for field parameter.

The function doesn't validate the field parameter before accessing its properties.

 export const canMakeAssociationBlock = (field) => {
+  if (!field?.interface) return false;
   return ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface);
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const canMakeAssociationBlock = (field) => {
return ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface);
};
export const canMakeAssociationBlock = (field) => {
if (!field?.interface) return false;
return ['linkTo', 'subTable', 'o2m', 'm2m', 'obo', 'oho', 'o2o', 'm2o'].includes(field.interface);
};
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/buttons/RecordBlockInitializers.tsx
around lines 35 to 37, the function canMakeAssociationBlock accesses
field.interface without checking if field is null or undefined. Add a null check
for the field parameter at the start of the function to ensure it is not null or
undefined before accessing its properties, returning false if the check fails.

Comment on lines +163 to +168
createBlockSchema: ({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return createFormBlock({ item, fromOthersInPopup });
}
createAssociationFormBlock({ item });
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add missing return statements in createBlockSchema.

The createBlockSchema function doesn't return values from the function calls, which might cause issues if the caller expects a return value.

 createBlockSchema: ({ item, fromOthersInPopup }) => {
   if (fromOthersInPopup) {
     return createFormBlock({ item, fromOthersInPopup });
   }
-  createAssociationFormBlock({ item });
+  return createAssociationFormBlock({ item });
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
createBlockSchema: ({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return createFormBlock({ item, fromOthersInPopup });
}
createAssociationFormBlock({ item });
},
createBlockSchema: ({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return createFormBlock({ item, fromOthersInPopup });
}
return createAssociationFormBlock({ item });
},
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/buttons/RecordBlockInitializers.tsx
around lines 163 to 168, the createBlockSchema function is missing return
statements for the createAssociationFormBlock call. Add a return statement
before createAssociationFormBlock({ item }) so that both branches of the
conditional return their results, ensuring the function consistently returns a
value.

Comment on lines +48 to +60
const createAssociationListBlock = useCallback(
({ item }) => {
const field = item.associationField;
const collection = getCollection(field.target);

insert(
createListBlockUISchema({
rowKey: collection.filterTargetKey,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
}),
);
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add defensive null checks

The callback directly accesses nested properties without checking if they exist, which could cause runtime errors.

     ({ item }) => {
       const field = item.associationField;
+      if (!field || !field.target) {
+        console.warn('Invalid association field provided');
+        return;
+      }
       const collection = getCollection(field.target);
+      if (!collection) {
+        console.warn('Collection not found for target:', field.target);
+        return;
+      }

       insert(
         createListBlockUISchema({
           rowKey: collection.filterTargetKey,
           dataSource: collection.dataSource,
           association: `${field.collectionName}.${field.name}`,
         }),
       );
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const createAssociationListBlock = useCallback(
({ item }) => {
const field = item.associationField;
const collection = getCollection(field.target);
insert(
createListBlockUISchema({
rowKey: collection.filterTargetKey,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
}),
);
},
const createAssociationListBlock = useCallback(
({ item }) => {
const field = item.associationField;
if (!field || !field.target) {
console.warn('Invalid association field provided');
return;
}
const collection = getCollection(field.target);
if (!collection) {
console.warn('Collection not found for target:', field.target);
return;
}
insert(
createListBlockUISchema({
rowKey: collection.filterTargetKey,
dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`,
}),
);
},
[getCollection, insert],
);
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/items/RecordAssociationListBlockInitializer.tsx
around lines 48 to 60, the createAssociationListBlock callback accesses nested
properties like item.associationField and field.target without null checks,
risking runtime errors. Add defensive checks to ensure item,
item.associationField, and field.target exist before accessing their properties,
and handle cases where these might be undefined to prevent crashes.

Comment on lines +31 to +46
onClick={async ({ item }) => {
if (item.template) {
const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createDetailsUISchema({
dataSource: collection.dataSource,
association: resource,
templateSchema: s,
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
insert(blockSchema);
} else {
insert(s);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for async template fetching.

The async call to getTemplateSchemaByMode could fail, but there's no error handling. This could cause unhandled promise rejections.

Consider wrapping the async operation in a try-catch block:

 onClick={async ({ item }) => {
+  try {
     if (item.template) {
       const s = await getTemplateSchemaByMode(item);
       if (item.template.componentName === 'ReadPrettyFormItem') {
         const blockSchema = createDetailsUISchema({
           dataSource: collection.dataSource,
           association: resource,
           templateSchema: s,
         });
         if (item.mode === 'reference') {
           blockSchema['x-template-key'] = item.template.key;
         }
         insert(blockSchema);
       } else {
         insert(s);
       }
     } else {
       insert(
         createDetailsUISchema({
           association: resource,
           dataSource: collection.dataSource,
         }),
       );
     }
+  } catch (error) {
+    console.error('Failed to create block schema:', error);
+    // Consider showing user-friendly error message
+  }
 }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onClick={async ({ item }) => {
if (item.template) {
const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createDetailsUISchema({
dataSource: collection.dataSource,
association: resource,
templateSchema: s,
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
insert(blockSchema);
} else {
insert(s);
}
onClick={async ({ item }) => {
try {
if (item.template) {
const s = await getTemplateSchemaByMode(item);
if (item.template.componentName === 'ReadPrettyFormItem') {
const blockSchema = createDetailsUISchema({
dataSource: collection.dataSource,
association: resource,
templateSchema: s,
});
if (item.mode === 'reference') {
blockSchema['x-template-key'] = item.template.key;
}
insert(blockSchema);
} else {
insert(s);
}
} else {
insert(
createDetailsUISchema({
association: resource,
dataSource: collection.dataSource,
}),
);
}
} catch (error) {
console.error('Failed to create block schema:', error);
// Consider showing user-friendly error message
}
}}
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/items/RecordReadPrettyAssociationFormBlockInitializer.tsx
around lines 31 to 46, the async call to getTemplateSchemaByMode lacks error
handling, risking unhandled promise rejections. Wrap the entire async onClick
handler logic in a try-catch block to catch any errors from
getTemplateSchemaByMode or subsequent code, and handle or log the error
appropriately to prevent unhandled exceptions.

Comment on lines +78 to +151
async load() {
this.app.addComponents({
...initializerComponents,
...items,
DestroyActionInitializer,
CreateFormBlockInitializer,
FormBlockInitializer,
RecordFormBlockInitializer,
TableBlockInitializer,
TableSelectorInitializer,
RecordReadPrettyFormBlockInitializer,
DetailsBlockInitializer,
ListBlockInitializer,
GridCardBlockInitializer,
FilterFormBlockInitializer,
FilterTreeBlockInitializer,
FilterCollapseBlockInitializer,
MarkdownBlockInitializer,
MarkdownFormItemInitializer,
TableCollectionFieldInitializer,
CollectionFieldInitializer,
CreateActionInitializer,
CustomizeAddRecordActionInitializer,
CreateChildInitializer,
ViewActionInitializer,
UpdateActionInitializer,
PopupActionInitializer,
SaveRecordActionInitializer,
UpdateRecordActionInitializer,
CreateSubmitActionInitializer,
UpdateSubmitActionInitializer,
BulkDestroyActionInitializer,
ExpandableActionInitializer,
DisassociateActionInitializer,
FilterActionInitializer,
RefreshActionInitializer,
FilterFormItemCustom,
AssociateActionInitializer,
AssociateActionProvider,
} as any);

this.app.schemaInitializerManager.add(blockInitializers);
this.app.schemaInitializerManager.add(tableActionInitializers);
this.app.schemaInitializerManager.add(tableColumnInitializers);
this.app.schemaInitializerManager.add(tableActionColumnInitializers);
this.app.schemaInitializerManager.add(formItemInitializers);
this.app.schemaInitializerManager.add(formActionInitializers);
this.app.schemaInitializerManager.add(detailsActionInitializers);
this.app.schemaInitializerManager.add(readPrettyFormItemInitializers);
this.app.schemaInitializerManager.add(readPrettyFormActionInitializers);
this.app.schemaInitializerManager.add(createFormBlockInitializers);
this.app.schemaInitializerManager.add(customizeCreateFormBlockInitializers);
this.app.schemaInitializerManager.add(customFormItemInitializers);
this.app.schemaInitializerManager.add(filterFormActionInitializers);
this.app.schemaInitializerManager.add(filterTreeActionInitializers);
this.app.schemaInitializerManager.add(createFormActionInitializers);
this.app.schemaInitializerManager.add(updateFormActionInitializers);
this.app.schemaInitializerManager.add(filterFormItemInitializers);
this.app.schemaInitializerManager.add(gridCardActionInitializers);
this.app.schemaInitializerManager.add(gridCardItemActionInitializers);

this.app.schemaInitializerManager.add(listActionInitializers);
this.app.schemaInitializerManager.add(listItemActionInitializers);
this.app.schemaInitializerManager.add(recordBlockInitializers);
this.app.schemaInitializerManager.add(recordFormBlockInitializers);
this.app.schemaInitializerManager.add(subTableActionInitializers);
this.app.schemaInitializerManager.add(tableSelectorInitializers);
this.app.schemaInitializerManager.add(tabPaneInitializers_deprecated);
this.app.schemaInitializerManager.add(tabPaneInitializersForRecordBlock);
this.app.schemaInitializerManager.add(tabPaneInitializersForBulkEditFormBlock);
this.app.schemaInitializerManager.add(menuItemInitializer);
this.app.schemaInitializerManager.add(tabPaneInitializers);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for plugin loading

The async load method could fail during component or initializer registration but lacks error handling.

   async load() {
+    try {
       this.app.addComponents({
         // ... components
       });

       this.app.schemaInitializerManager.add(blockInitializers);
       // ... other initializers
+    } catch (error) {
+      console.error('Failed to load SchemaInitializerPlugin:', error);
+      throw error; // Re-throw to notify the plugin system
+    }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async load() {
this.app.addComponents({
...initializerComponents,
...items,
DestroyActionInitializer,
CreateFormBlockInitializer,
FormBlockInitializer,
RecordFormBlockInitializer,
TableBlockInitializer,
TableSelectorInitializer,
RecordReadPrettyFormBlockInitializer,
DetailsBlockInitializer,
ListBlockInitializer,
GridCardBlockInitializer,
FilterFormBlockInitializer,
FilterTreeBlockInitializer,
FilterCollapseBlockInitializer,
MarkdownBlockInitializer,
MarkdownFormItemInitializer,
TableCollectionFieldInitializer,
CollectionFieldInitializer,
CreateActionInitializer,
CustomizeAddRecordActionInitializer,
CreateChildInitializer,
ViewActionInitializer,
UpdateActionInitializer,
PopupActionInitializer,
SaveRecordActionInitializer,
UpdateRecordActionInitializer,
CreateSubmitActionInitializer,
UpdateSubmitActionInitializer,
BulkDestroyActionInitializer,
ExpandableActionInitializer,
DisassociateActionInitializer,
FilterActionInitializer,
RefreshActionInitializer,
FilterFormItemCustom,
AssociateActionInitializer,
AssociateActionProvider,
} as any);
this.app.schemaInitializerManager.add(blockInitializers);
this.app.schemaInitializerManager.add(tableActionInitializers);
this.app.schemaInitializerManager.add(tableColumnInitializers);
this.app.schemaInitializerManager.add(tableActionColumnInitializers);
this.app.schemaInitializerManager.add(formItemInitializers);
this.app.schemaInitializerManager.add(formActionInitializers);
this.app.schemaInitializerManager.add(detailsActionInitializers);
this.app.schemaInitializerManager.add(readPrettyFormItemInitializers);
this.app.schemaInitializerManager.add(readPrettyFormActionInitializers);
this.app.schemaInitializerManager.add(createFormBlockInitializers);
this.app.schemaInitializerManager.add(customizeCreateFormBlockInitializers);
this.app.schemaInitializerManager.add(customFormItemInitializers);
this.app.schemaInitializerManager.add(filterFormActionInitializers);
this.app.schemaInitializerManager.add(filterTreeActionInitializers);
this.app.schemaInitializerManager.add(createFormActionInitializers);
this.app.schemaInitializerManager.add(updateFormActionInitializers);
this.app.schemaInitializerManager.add(filterFormItemInitializers);
this.app.schemaInitializerManager.add(gridCardActionInitializers);
this.app.schemaInitializerManager.add(gridCardItemActionInitializers);
this.app.schemaInitializerManager.add(listActionInitializers);
this.app.schemaInitializerManager.add(listItemActionInitializers);
this.app.schemaInitializerManager.add(recordBlockInitializers);
this.app.schemaInitializerManager.add(recordFormBlockInitializers);
this.app.schemaInitializerManager.add(subTableActionInitializers);
this.app.schemaInitializerManager.add(tableSelectorInitializers);
this.app.schemaInitializerManager.add(tabPaneInitializers_deprecated);
this.app.schemaInitializerManager.add(tabPaneInitializersForRecordBlock);
this.app.schemaInitializerManager.add(tabPaneInitializersForBulkEditFormBlock);
this.app.schemaInitializerManager.add(menuItemInitializer);
this.app.schemaInitializerManager.add(tabPaneInitializers);
}
}
async load() {
try {
this.app.addComponents({
...initializerComponents,
...items,
DestroyActionInitializer,
CreateFormBlockInitializer,
FormBlockInitializer,
RecordFormBlockInitializer,
TableBlockInitializer,
TableSelectorInitializer,
RecordReadPrettyFormBlockInitializer,
DetailsBlockInitializer,
ListBlockInitializer,
GridCardBlockInitializer,
FilterFormBlockInitializer,
FilterTreeBlockInitializer,
FilterCollapseBlockInitializer,
MarkdownBlockInitializer,
MarkdownFormItemInitializer,
TableCollectionFieldInitializer,
CollectionFieldInitializer,
CreateActionInitializer,
CustomizeAddRecordActionInitializer,
CreateChildInitializer,
ViewActionInitializer,
UpdateActionInitializer,
PopupActionInitializer,
SaveRecordActionInitializer,
UpdateRecordActionInitializer,
CreateSubmitActionInitializer,
UpdateSubmitActionInitializer,
BulkDestroyActionInitializer,
ExpandableActionInitializer,
DisassociateActionInitializer,
FilterActionInitializer,
RefreshActionInitializer,
FilterFormItemCustom,
AssociateActionInitializer,
AssociateActionProvider,
} as any);
this.app.schemaInitializerManager.add(blockInitializers);
this.app.schemaInitializerManager.add(tableActionInitializers);
this.app.schemaInitializerManager.add(tableColumnInitializers);
this.app.schemaInitializerManager.add(tableActionColumnInitializers);
this.app.schemaInitializerManager.add(formItemInitializers);
this.app.schemaInitializerManager.add(formActionInitializers);
this.app.schemaInitializerManager.add(detailsActionInitializers);
this.app.schemaInitializerManager.add(readPrettyFormItemInitializers);
this.app.schemaInitializerManager.add(readPrettyFormActionInitializers);
this.app.schemaInitializerManager.add(createFormBlockInitializers);
this.app.schemaInitializerManager.add(customizeCreateFormBlockInitializers);
this.app.schemaInitializerManager.add(customFormItemInitializers);
this.app.schemaInitializerManager.add(filterFormActionInitializers);
this.app.schemaInitializerManager.add(filterTreeActionInitializers);
this.app.schemaInitializerManager.add(createFormActionInitializers);
this.app.schemaInitializerManager.add(updateFormActionInitializers);
this.app.schemaInitializerManager.add(filterFormItemInitializers);
this.app.schemaInitializerManager.add(gridCardActionInitializers);
this.app.schemaInitializerManager.add(gridCardItemActionInitializers);
this.app.schemaInitializerManager.add(listActionInitializers);
this.app.schemaInitializerManager.add(listItemActionInitializers);
this.app.schemaInitializerManager.add(recordBlockInitializers);
this.app.schemaInitializerManager.add(recordFormBlockInitializers);
this.app.schemaInitializerManager.add(subTableActionInitializers);
this.app.schemaInitializerManager.add(tableSelectorInitializers);
this.app.schemaInitializerManager.add(tabPaneInitializers_deprecated);
this.app.schemaInitializerManager.add(tabPaneInitializersForRecordBlock);
this.app.schemaInitializerManager.add(tabPaneInitializersForBulkEditFormBlock);
this.app.schemaInitializerManager.add(menuItemInitializer);
this.app.schemaInitializerManager.add(tabPaneInitializers);
} catch (error) {
console.error('Failed to load SchemaInitializerPlugin:', error);
throw error; // Re-throw to notify the plugin system
}
}
🤖 Prompt for AI Agents
In packages/module-web/src/client/schema-initializer/SchemaInitializerPlugin.ts
between lines 78 and 151, the async load method lacks error handling for
potential failures during component or initializer registration. Wrap the entire
load method's logic in a try-catch block to catch any errors, and in the catch
block, log or handle the error appropriately to prevent unhandled promise
rejections and improve robustness.

Comment on lines +79 to +117
this.app.addComponents({
...initializerComponents,
...items,
DestroyActionInitializer,
CreateFormBlockInitializer,
FormBlockInitializer,
RecordFormBlockInitializer,
TableBlockInitializer,
TableSelectorInitializer,
RecordReadPrettyFormBlockInitializer,
DetailsBlockInitializer,
ListBlockInitializer,
GridCardBlockInitializer,
FilterFormBlockInitializer,
FilterTreeBlockInitializer,
FilterCollapseBlockInitializer,
MarkdownBlockInitializer,
MarkdownFormItemInitializer,
TableCollectionFieldInitializer,
CollectionFieldInitializer,
CreateActionInitializer,
CustomizeAddRecordActionInitializer,
CreateChildInitializer,
ViewActionInitializer,
UpdateActionInitializer,
PopupActionInitializer,
SaveRecordActionInitializer,
UpdateRecordActionInitializer,
CreateSubmitActionInitializer,
UpdateSubmitActionInitializer,
BulkDestroyActionInitializer,
ExpandableActionInitializer,
DisassociateActionInitializer,
FilterActionInitializer,
RefreshActionInitializer,
FilterFormItemCustom,
AssociateActionInitializer,
AssociateActionProvider,
} as any);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unsafe type assertion

Using as any bypasses TypeScript's type checking and could hide type errors. Consider properly typing the components object.

     this.app.addComponents({
       ...initializerComponents,
       ...items,
       DestroyActionInitializer,
       // ... other components
       AssociateActionInitializer,
       AssociateActionProvider,
-    } as any);
+    });

If there are type conflicts, address them properly rather than using as any.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.app.addComponents({
...initializerComponents,
...items,
DestroyActionInitializer,
CreateFormBlockInitializer,
FormBlockInitializer,
RecordFormBlockInitializer,
TableBlockInitializer,
TableSelectorInitializer,
RecordReadPrettyFormBlockInitializer,
DetailsBlockInitializer,
ListBlockInitializer,
GridCardBlockInitializer,
FilterFormBlockInitializer,
FilterTreeBlockInitializer,
FilterCollapseBlockInitializer,
MarkdownBlockInitializer,
MarkdownFormItemInitializer,
TableCollectionFieldInitializer,
CollectionFieldInitializer,
CreateActionInitializer,
CustomizeAddRecordActionInitializer,
CreateChildInitializer,
ViewActionInitializer,
UpdateActionInitializer,
PopupActionInitializer,
SaveRecordActionInitializer,
UpdateRecordActionInitializer,
CreateSubmitActionInitializer,
UpdateSubmitActionInitializer,
BulkDestroyActionInitializer,
ExpandableActionInitializer,
DisassociateActionInitializer,
FilterActionInitializer,
RefreshActionInitializer,
FilterFormItemCustom,
AssociateActionInitializer,
AssociateActionProvider,
} as any);
this.app.addComponents({
...initializerComponents,
...items,
DestroyActionInitializer,
CreateFormBlockInitializer,
FormBlockInitializer,
RecordFormBlockInitializer,
TableBlockInitializer,
TableSelectorInitializer,
RecordReadPrettyFormBlockInitializer,
DetailsBlockInitializer,
ListBlockInitializer,
GridCardBlockInitializer,
FilterFormBlockInitializer,
FilterTreeBlockInitializer,
FilterCollapseBlockInitializer,
MarkdownBlockInitializer,
MarkdownFormItemInitializer,
TableCollectionFieldInitializer,
CollectionFieldInitializer,
CreateActionInitializer,
CustomizeAddRecordActionInitializer,
CreateChildInitializer,
ViewActionInitializer,
UpdateActionInitializer,
PopupActionInitializer,
SaveRecordActionInitializer,
UpdateRecordActionInitializer,
CreateSubmitActionInitializer,
UpdateSubmitActionInitializer,
BulkDestroyActionInitializer,
ExpandableActionInitializer,
DisassociateActionInitializer,
FilterActionInitializer,
RefreshActionInitializer,
FilterFormItemCustom,
AssociateActionInitializer,
AssociateActionProvider,
});
🤖 Prompt for AI Agents
In packages/module-web/src/client/schema-initializer/SchemaInitializerPlugin.ts
between lines 79 and 117, the use of 'as any' to type cast the components object
disables TypeScript's type checking and may hide type errors. To fix this,
define or import the correct type for the components object expected by
this.app.addComponents and apply it explicitly instead of using 'as any'. If
there are type conflicts, resolve them by adjusting the component types or the
expected type rather than bypassing type checking.

Comment on lines +82 to +117
DestroyActionInitializer,
CreateFormBlockInitializer,
FormBlockInitializer,
RecordFormBlockInitializer,
TableBlockInitializer,
TableSelectorInitializer,
RecordReadPrettyFormBlockInitializer,
DetailsBlockInitializer,
ListBlockInitializer,
GridCardBlockInitializer,
FilterFormBlockInitializer,
FilterTreeBlockInitializer,
FilterCollapseBlockInitializer,
MarkdownBlockInitializer,
MarkdownFormItemInitializer,
TableCollectionFieldInitializer,
CollectionFieldInitializer,
CreateActionInitializer,
CustomizeAddRecordActionInitializer,
CreateChildInitializer,
ViewActionInitializer,
UpdateActionInitializer,
PopupActionInitializer,
SaveRecordActionInitializer,
UpdateRecordActionInitializer,
CreateSubmitActionInitializer,
UpdateSubmitActionInitializer,
BulkDestroyActionInitializer,
ExpandableActionInitializer,
DisassociateActionInitializer,
FilterActionInitializer,
RefreshActionInitializer,
FilterFormItemCustom,
AssociateActionInitializer,
AssociateActionProvider,
} as any);
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Runtime errors from undefined components

Many components being registered (like DestroyActionInitializer, CreateFormBlockInitializer, etc.) are from commented imports and will be undefined at runtime, causing errors.


🏁 Script executed:

#!/bin/bash
# Check if these components are defined elsewhere or will cause runtime errors
rg -A 2 "DestroyActionInitializer|CreateFormBlockInitializer|FormBlockInitializer|RecordFormBlockInitializer" --type ts --glob "!*.d.ts" --glob "!node_modules/**"

Length of output: 39978


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' packages/module-web/src/client/schema-initializer/SchemaInitializerPlugin.ts

Length of output: 9857


🏁 Script executed:

#!/bin/bash
find packages/module-web/src/client/schema-initializer/components -maxdepth 1 -type f

Length of output: 319


Fix missing component imports in SchemaInitializerPlugin

The load() method in
packages/module-web/src/client/schema-initializer/SchemaInitializerPlugin.ts
references many initializers whose imports are commented out, causing compile-time “Cannot find name” errors and potential runtime failures. Please:

  • Uncomment or add imports for these identifiers from their respective modules:

    • DestroyActionInitializer
    • CreateFormBlockInitializer
    • FormBlockInitializer
    • RecordFormBlockInitializer
    • TableBlockInitializer
    • TableSelectorInitializer
    • RecordReadPrettyFormBlockInitializer
    • DetailsBlockInitializer
    • ListBlockInitializer
    • GridCardBlockInitializer
    • FilterFormBlockInitializer
    • FilterTreeBlockInitializer
    • FilterCollapseBlockInitializer
    • MarkdownBlockInitializer
    • MarkdownFormItemInitializer
    • TableCollectionFieldInitializer
    • CollectionFieldInitializer
    • CreateActionInitializer
    • CustomizeAddRecordActionInitializer
    • CreateChildInitializer
    • ViewActionInitializer
    • UpdateActionInitializer
    • PopupActionInitializer
    • SaveRecordActionInitializer
    • UpdateRecordActionInitializer
    • CreateSubmitActionInitializer
    • UpdateSubmitActionInitializer
    • BulkDestroyActionInitializer
    • ExpandableActionInitializer
    • DisassociateActionInitializer
    • FilterActionInitializer
    • RefreshActionInitializer
    • AssociateActionInitializer
    • AssociateActionProvider
  • Or ensure these exports are included in the spread imports (initializerComponents / items) instead of listing them manually.

  • Add error handling (e.g. try/catch) around the async load() implementation to capture and report initialization failures.

🤖 Prompt for AI Agents
In packages/module-web/src/client/schema-initializer/SchemaInitializerPlugin.ts
between lines 82 and 117, the load() method references many initializers whose
imports are currently commented out or missing, causing compile errors. To fix
this, uncomment or add explicit import statements for all the listed
initializers from their respective modules, or alternatively include them in the
existing spread imports like initializerComponents or items to avoid manual
listing. Additionally, wrap the async load() method's implementation in a
try/catch block to handle and log any initialization errors gracefully.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Review continued from previous batch...

Comment on lines +192 to +222
/**
* @deprecated
*/
export const tabPaneInitializers_deprecated = new SchemaInitializer({
name: 'TabPaneInitializers',
Component: TabPaneInitializers,
popover: false,
});

export const tabPaneInitializers = new SchemaInitializer({
name: 'popup:addTab',
...commonOptions,
});

/**
* @deprecated
*/
export const tabPaneInitializersForRecordBlock = new SchemaInitializer({
name: 'TabPaneInitializersForCreateFormBlock',
Component: TabPaneInitializersForCreateFormBlock,
popover: false,
});

/**
* @deprecated
*/
export const tabPaneInitializersForBulkEditFormBlock = new SchemaInitializer({
name: 'TabPaneInitializersForBulkEditFormBlock',
Component: TabPaneInitializersForBulkEditFormBlock,
popover: false,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Provide migration guidance for deprecated exports

The deprecated exports should include information about what to use instead.

 /**
  * @deprecated
+ * Use `tabPaneInitializers` instead
  */
 export const tabPaneInitializers_deprecated = new SchemaInitializer({

Consider adding a console warning when these deprecated exports are used:

export const tabPaneInitializers_deprecated = new SchemaInitializer({
  name: 'TabPaneInitializers',
  Component: (...args) => {
    console.warn('tabPaneInitializers_deprecated is deprecated. Use tabPaneInitializers instead.');
    return TabPaneInitializers(...args);
  },
  popover: false,
});
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/buttons/TabPaneInitializers.tsx
between lines 192 and 222, the deprecated exports lack guidance on what to use
instead. Update each deprecated export to wrap their Component property with a
function that logs a console warning indicating the deprecation and suggesting
the preferred alternative. This will inform developers at runtime about the
deprecation and the recommended replacement.

Comment on lines +411 to +421
const extra = useMemo(() => {
return typeof field.description === 'string' ? (
<div
dangerouslySetInnerHTML={{
__html: HTMLEncode(field.description).split('\n').join('<br/>'),
}}
/>
) : (
field.description
);
}, [field.description]);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using dangerouslySetInnerHTML for security

Using dangerouslySetInnerHTML can expose users to XSS attacks. Consider a safer approach for rendering line breaks.

-    const extra = useMemo(() => {
-      return typeof field.description === 'string' ? (
-        <div
-          dangerouslySetInnerHTML={{
-            __html: HTMLEncode(field.description).split('\n').join('<br/>'),
-          }}
-        />
-      ) : (
-        field.description
-      );
-    }, [field.description]);
+    const extra = useMemo(() => {
+      if (typeof field.description === 'string') {
+        return (
+          <div style={{ whiteSpace: 'pre-wrap' }}>
+            {field.description}
+          </div>
+        );
+      }
+      return field.description;
+    }, [field.description]);

Alternatively, you can split the description and render each line separately:

const extra = useMemo(() => {
  if (typeof field.description === 'string') {
    return (
      <div>
        {field.description.split('\n').map((line, index) => (
          <React.Fragment key={index}>
            {line}
            {index < field.description.split('\n').length - 1 && <br />}
          </React.Fragment>
        ))}
      </div>
    );
  }
  return field.description;
}, [field.description]);
🧰 Tools
🪛 Biome (1.9.4)

[error] 414-414: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/items/CustomFilterFormItemInitializer.tsx
around lines 411 to 421, the code uses dangerouslySetInnerHTML to render
field.description with line breaks, which poses an XSS security risk. Replace
this by splitting the description string on newline characters and rendering
each line inside a div with explicit <br /> elements between lines, avoiding the
use of dangerouslySetInnerHTML entirely. This ensures safe rendering of line
breaks without exposing to XSS.

Comment on lines +35 to +36
// 1. 组件在第一次渲染时自动 focus,提高用户体验
inputRef.current.input.focus();
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Translate Chinese comments to English

-    // 1. 组件在第一次渲染时自动 focus,提高用户体验
+    // 1. Auto-focus on first render to improve user experience
    inputRef.current.input.focus();

-    // 2. 当组件已经渲染,并再次显示时,自动 focus
+    // 2. Auto-focus when component is already rendered and shown again

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/items/DataBlockInitializer.tsx
around lines 35 to 36, translate the Chinese comment above the focus call into
English. Replace the comment with a clear English version that explains the
component automatically focuses on the input on first render to improve user
experience.

Comment on lines +86 to +87
// 判断滚动是否到达底部
if (Math.abs(container.scrollHeight - container.scrollTop - container.clientHeight) <= MENU_ITEM_HEIGHT) {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Translate Chinese comment to English

-      // 判断滚动是否到达底部
+      // Check if scroll has reached the bottom
      if (Math.abs(container.scrollHeight - container.scrollTop - container.clientHeight) <= MENU_ITEM_HEIGHT) {
-        // 到达底部,执行加载更多的操作
+        // Reached bottom, execute load more operation
        loadMore();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// 判断滚动是否到达底部
if (Math.abs(container.scrollHeight - container.scrollTop - container.clientHeight) <= MENU_ITEM_HEIGHT) {
// Check if scroll has reached the bottom
if (Math.abs(container.scrollHeight - container.scrollTop - container.clientHeight) <= MENU_ITEM_HEIGHT) {
// Reached bottom, execute load more operation
loadMore();
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/items/DataBlockInitializer.tsx
at lines 86 to 87, translate the Chinese comment "// 判断滚动是否到达底部" to English.
Replace it with a clear English comment such as "// Check if scroll has reached
the bottom" to maintain code clarity and consistency.

Comment on lines +331 to +342
const items =
itemsFromProps ||
// eslint-disable-next-line react-hooks/rules-of-hooks
useCollectionDataSourceItems({
componentName: componentType,
filter,
filterDataSource,
onlyCurrentDataSource,
showAssociationFields,
dataBlockInitializerProps: props,
hideOtherRecordsInPopup,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix conditional hook usage

The useCollectionDataSourceItems hook is called conditionally, which violates React's Rules of Hooks. Hooks must be called at the top level.

-  const items =
-    itemsFromProps ||
-    // eslint-disable-next-line react-hooks/rules-of-hooks
-    useCollectionDataSourceItems({
+  const defaultItems = useCollectionDataSourceItems({
       componentName: componentType,
       filter,
       filterDataSource,
       onlyCurrentDataSource,
       showAssociationFields,
       dataBlockInitializerProps: props,
       hideOtherRecordsInPopup,
     });
+  const items = itemsFromProps || defaultItems;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const items =
itemsFromProps ||
// eslint-disable-next-line react-hooks/rules-of-hooks
useCollectionDataSourceItems({
componentName: componentType,
filter,
filterDataSource,
onlyCurrentDataSource,
showAssociationFields,
dataBlockInitializerProps: props,
hideOtherRecordsInPopup,
});
const defaultItems = useCollectionDataSourceItems({
componentName: componentType,
filter,
filterDataSource,
onlyCurrentDataSource,
showAssociationFields,
dataBlockInitializerProps: props,
hideOtherRecordsInPopup,
});
const items = itemsFromProps || defaultItems;
🤖 Prompt for AI Agents
In
packages/module-web/src/client/schema-initializer/items/DataBlockInitializer.tsx
around lines 331 to 342, the hook useCollectionDataSourceItems is called
conditionally inside a logical OR expression, violating React's Rules of Hooks.
To fix this, call useCollectionDataSourceItems unconditionally at the top level
and then use the result conditionally by assigning items to either
itemsFromProps or the hook result afterward.

Comment on lines +70 to +86
export const findTableColumn = (schema: Schema, key: string, action: string, deepth = 0) => {
return schema.reduceProperties((buf, s) => {
if (s[key] === action) {
return s;
}
const c = s.reduceProperties((buf, s) => {
if (s[key] === action) {
return s;
}
return buf;
});
if (c) {
return c;
}
return buf;
});
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo and implement depth protection in recursive function

The deepth parameter has a typo and is not being used, which could lead to stack overflow.

-export const findTableColumn = (schema: Schema, key: string, action: string, deepth = 0) => {
+export const findTableColumn = (schema: Schema, key: string, action: string, depth = 0) => {
+  const MAX_DEPTH = 10;
+  if (depth > MAX_DEPTH) {
+    console.warn('Maximum depth reached in findTableColumn');
+    return null;
+  }
   return schema.reduceProperties((buf, s) => {
     if (s[key] === action) {
       return s;
     }
-    const c = s.reduceProperties((buf, s) => {
-      if (s[key] === action) {
-        return s;
-      }
-      return buf;
-    });
+    const c = findTableColumn(s, key, action, depth + 1);
     if (c) {
       return c;
     }
     return buf;
   });
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const findTableColumn = (schema: Schema, key: string, action: string, deepth = 0) => {
return schema.reduceProperties((buf, s) => {
if (s[key] === action) {
return s;
}
const c = s.reduceProperties((buf, s) => {
if (s[key] === action) {
return s;
}
return buf;
});
if (c) {
return c;
}
return buf;
});
};
export const findTableColumn = (schema: Schema, key: string, action: string, depth = 0) => {
const MAX_DEPTH = 10;
if (depth > MAX_DEPTH) {
console.warn('Maximum depth reached in findTableColumn');
return null;
}
return schema.reduceProperties((buf, s) => {
if (s[key] === action) {
return s;
}
const c = findTableColumn(s, key, action, depth + 1);
if (c) {
return c;
}
return buf;
});
};
🤖 Prompt for AI Agents
In packages/module-web/src/client/schema-initializer/utils.ts lines 70 to 86,
fix the typo in the parameter name from "deepth" to "depth" and implement a
check to limit recursion depth using this parameter. Modify the function to
decrement the depth on each recursive call and stop recursion when depth reaches
zero to prevent potential stack overflow.

Comment on lines +1045 to +1062
* @deprecated
* 已弃用,请使用 createDetailsUISchema 和 createDetailsWithPaginationUISchema 替代
* @param options
* @returns
*/
export const createDetailsBlockSchema = (options: {
collection: string;
dataSource: string;
rowKey?: string;
formItemInitializers?: string;
actionInitializers?: string;
association?: string;
template?: any;
settings?: string;
action?: string;
[key: string]: any;
}) => {
const {
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add deprecation warnings to deprecated functions

Deprecated functions should log warnings to help developers migrate.

 export const createDetailsBlockSchema = (options: {
   collection: string;
   dataSource: string;
   rowKey?: string;
   formItemInitializers?: string;
   actionInitializers?: string;
   association?: string;
   template?: any;
   settings?: string;
   action?: string;
   [key: string]: any;
 }) => {
+  console.warn(
+    'createDetailsBlockSchema is deprecated. Use createDetailsUISchema or createDetailsWithPaginationUISchema instead.'
+  );
   const {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @deprecated
* 已弃用,请使用 createDetailsUISchema createDetailsWithPaginationUISchema 替代
* @param options
* @returns
*/
export const createDetailsBlockSchema = (options: {
collection: string;
dataSource: string;
rowKey?: string;
formItemInitializers?: string;
actionInitializers?: string;
association?: string;
template?: any;
settings?: string;
action?: string;
[key: string]: any;
}) => {
const {
export const createDetailsBlockSchema = (options: {
collection: string;
dataSource: string;
rowKey?: string;
formItemInitializers?: string;
actionInitializers?: string;
association?: string;
template?: any;
settings?: string;
action?: string;
[key: string]: any;
}) => {
console.warn(
'createDetailsBlockSchema is deprecated. Use createDetailsUISchema or createDetailsWithPaginationUISchema instead.'
);
const {
// …rest of the implementation
🤖 Prompt for AI Agents
In packages/module-web/src/client/schema-initializer/utils.ts around lines 1045
to 1062, the deprecated function createDetailsBlockSchema lacks a deprecation
warning. Add a console warning or logger message at the start of the function to
notify developers that this function is deprecated and suggest using
createDetailsUISchema or createDetailsWithPaginationUISchema instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants