Skip to content

@memberjunction/interactive-component-types

Type definitions and interfaces for MemberJunction’s Interactive Components system, enabling dynamic, data-driven UI components with AI capabilities.

The @memberjunction/interactive-component-types package provides the foundational types and interfaces for building interactive components in MemberJunction. These components are designed to be dynamically generated, data-aware, and AI-enhanced, allowing for rich user experiences with minimal configuration.

graph TD
    A["InteractiveComponentSpec<br/>(Component Definition)"] --> B["ComponentInitFunction"]
    B --> C["Props & Events"]
    B --> D["SimpleDataContext"]
    B --> E["SimpleMetadata"]
    B --> F["SimpleRunView / RunQuery"]
    B --> G["SimpleAITools"]

    G --> H["ExecutePrompt<br/>(LLM Calls)"]
    G --> I["EmbedText<br/>(Vector Embeddings)"]
    G --> J["VectorService<br/>(Similarity / KNN)"]

    A --> K["ComponentObject<br/>(Runtime Interface)"]
    K --> L["Standard Methods<br/>(print, refresh, isDirty)"]
    K --> M["Custom Methods<br/>(RegisterMethod)"]

    style A fill:#2d6a9f,stroke:#1a4971,color:#fff
    style B fill:#7c5295,stroke:#563a6b,color:#fff
    style G fill:#2d8659,stroke:#1a5c3a,color:#fff
    style K fill:#b8762f,stroke:#8a5722,color:#fff
    style H fill:#2d8659,stroke:#1a5c3a,color:#fff
    style I fill:#2d8659,stroke:#1a5c3a,color:#fff
    style J fill:#2d8659,stroke:#1a5c3a,color:#fff
  • Dynamic Component Generation — Type-safe specifications for runtime component creation
  • Data Context Management — Support for both static and dynamic data loading
  • AI Tool Integration — Built-in interfaces for AI operations within components
  • Framework Agnostic — Core types that work across React, Angular, and other frameworks
  • Vector Operations — Support for similarity calculations and vector-based data operations
  • Metadata Access — Direct access to MemberJunction’s metadata system
Terminal window
npm install @memberjunction/interactive-component-types

Provides AI capabilities to interactive components:

interface SimpleAITools {
// Execute LLM prompts for qualitative analysis
ExecutePrompt: (params: SimpleExecutePromptParams) => Promise<SimpleExecutePromptResult>
// Generate vector embeddings for similarity calculations
EmbedText: (params: SimpleEmbedTextParams) => Promise<SimpleEmbedTextResult>
// Vector operations service for KNN, similarity scoring, etc.
VectorService: SimpleVectorService
}

Parameters for executing AI prompts within components:

interface SimpleExecutePromptParams {
// System prompt to set context
systemPrompt: string;
// Optional conversation history
messages?: Array<{message: string, role: 'user' | 'assistant'}>;
// Preferred models in order of preference
preferredModels?: string[];
// Model power selection: 'lowest' | 'medium' | 'highest'
modelPower?: 'lowest' | 'medium' | 'highest';
// Optional user context
contextUser?: UserInfo;
}

Parameters for generating text embeddings:

interface SimpleEmbedTextParams {
// Single string or array of strings to embed
textToEmbed: string | string[];
// Model size selection
modelSize: 'small' | 'medium';
// Optional user context
contextUser?: UserInfo;
}

Access to MemberJunction’s metadata system:

interface SimpleMetadata {
// Array of all entity metadata
Entities: EntityInfo[];
// Get entity object for CRUD operations
GetEntityObject(entityName: string, contextUser?: UserInfo): Promise<BaseEntity>;
}

Execute database views dynamically:

interface SimpleRunView {
// Run a single view
RunView: (params: RunViewParams, contextUser?: UserInfo) => Promise<RunViewResult>
// Run multiple views in parallel
RunViews: (params: RunViewParams[], contextUser?: UserInfo) => Promise<RunViewResult[]>
}

Execute predefined queries:

interface SimpleRunQuery {
// Run a predefined query with parameters
RunQuery: (params: RunQueryParams, contextUser?: UserInfo) => Promise<RunQueryResult>
}

Interactive components receive initialization functions with access to all tools:

type ComponentInitFunction = (
props: ComponentInitProps,
events: ComponentEvents,
data: SimpleDataContext,
metadata: SimpleMetadata,
runView: SimpleRunView,
runQuery: SimpleRunQuery,
ai: SimpleAITools
) => Promise<void>;
// In your interactive component initialization
async function initComponent(props, events, data, metadata, runView, runQuery, ai) {
// Execute a prompt for data analysis
const analysisResult = await ai.ExecutePrompt({
systemPrompt: 'You are a data analyst. Analyze the provided dataset.',
messages: [
{ message: 'What are the key trends?', role: 'user' }
],
modelPower: 'medium'
});
if (analysisResult.success) {
console.log('Analysis:', analysisResult.result);
// Parse JSON if available
if (analysisResult.resultObject) {
const insights = analysisResult.resultObject;
// Use insights in your component
}
}
// Generate embeddings for similarity matching
const embedResult = await ai.EmbedText({
textToEmbed: ['Product A description', 'Product B description'],
modelSize: 'small'
});
// Use vector service for similarity calculations
const similarity = ai.VectorService.cosineSimilarity(
embedResult.result[0],
embedResult.result[1]
);
console.log('Product similarity:', similarity);
}
async function loadComponentData(props, events, data, metadata, runView) {
// Get entity metadata
const userEntity = metadata.Entities.find(e => e.Name === 'Users');
// Run a view to get data
const viewResult = await runView.RunView({
EntityName: 'Users',
ExtraFilter: "Status = 'Active'",
OrderBy: 'LastName, FirstName',
MaxRows: 100
});
if (viewResult.Success) {
const users = viewResult.Results;
// Process users data
}
// Create/update an entity
const newUser = await metadata.GetEntityObject('Users');
newUser.NewRecord();
newUser.Set('FirstName', 'John');
newUser.Set('LastName', 'Doe');
await newUser.Save();
}
async function loadDashboardData(props, events, data, metadata, runView) {
// Run multiple views in parallel for better performance
const [salesData, customerData, productData] = await runView.RunViews([
{ EntityName: 'Sales', ExtraFilter: "Date >= '2024-01-01'" },
{ EntityName: 'Customers', ExtraFilter: "Active = 1" },
{ EntityName: 'Products', OrderBy: 'Name' }
]);
// All data loaded in parallel
console.log('Sales:', salesData.Results);
console.log('Customers:', customerData.Results);
console.log('Products:', productData.Results);
}

The ComponentObject interface defines the structure returned by compiled components, providing both the React component and method accessors:

interface ComponentObject {
// The React component function
component: Function;
// Standard methods (all optional)
print?: () => void;
refresh?: () => void;
getCurrentDataState?: () => any;
getDataStateHistory?: () => Array<{ timestamp: Date; state: any }>;
validate?: () => boolean | { valid: boolean; errors?: string[] };
isDirty?: () => boolean;
reset?: () => void;
scrollTo?: (target: string | HTMLElement | { top?: number; left?: number }) => void;
focus?: (target?: string | HTMLElement) => void;
// Dynamic method access
invokeMethod?: (methodName: string, ...args: any[]) => any;
hasMethod?: (methodName: string) => boolean;
}

Components receive callbacks that enable interaction with their container:

interface ComponentCallbacks {
// Open an entity record in the container
OpenEntityRecord: (entityName: string, key: CompositeKey) => void;
// Register a method that can be called by the container
RegisterMethod: (methodName: string, handler: Function) => void;
}
  1. Component registers methods during initialization using the RegisterMethod callback
  2. Runtime stores methods in an internal registry Map
  3. ComponentObject exposes methods with type-safe accessors for standard methods
  4. Containers call methods directly or via invokeMethod() for custom methods
function DataTableComponent({ callbacks, data, userState }) {
const [tableData, setTableData] = React.useState(data);
const [hasChanges, setHasChanges] = React.useState(false);
const [selectedRows, setSelectedRows] = React.useState([]);
React.useEffect(() => {
if (callbacks?.RegisterMethod) {
// Register standard methods
callbacks.RegisterMethod('getCurrentDataState', () => ({
data: tableData,
selectedRows: selectedRows,
totalCount: tableData.length
}));
callbacks.RegisterMethod('isDirty', () => hasChanges);
callbacks.RegisterMethod('validate', () => {
if (tableData.length === 0) {
return { valid: false, errors: ['Table cannot be empty'] };
}
return true;
});
callbacks.RegisterMethod('reset', () => {
setTableData(data);
setSelectedRows([]);
setHasChanges(false);
});
// Register custom business logic methods
callbacks.RegisterMethod('exportSelectedRows', () => {
return selectedRows.map(idx => tableData[idx]);
});
callbacks.RegisterMethod('deleteSelected', () => {
const newData = tableData.filter((_, idx) => !selectedRows.includes(idx));
setTableData(newData);
setHasChanges(true);
setSelectedRows([]);
});
}
}, [callbacks, tableData, selectedRows, hasChanges]);
return <div>{/* Table UI */}</div>;
}
// After component compilation
const componentObject = compiledComponent as ComponentObject;
// Use standard methods with type safety
if (componentObject.isDirty && componentObject.isDirty()) {
console.log('Component has unsaved changes');
const validation = componentObject.validate?.();
if (validation === true || validation?.valid) {
// Safe to proceed
const currentState = componentObject.getCurrentDataState?.();
await saveData(currentState);
} else {
console.error('Validation failed:', validation?.errors);
}
}
// Use custom methods via invokeMethod
if (componentObject.hasMethod?.('exportSelectedRows')) {
const selectedData = componentObject.invokeMethod('exportSelectedRows');
await exportToFile(selectedData);
}
// Reset the component
componentObject.reset?.();

Components can declare their supported methods in the spec for static discovery:

interface ComponentSpec {
name: string;
code: string;
// Optional method declarations for discovery/documentation
methods?: Array<{
name: string;
category?: 'standard' | 'custom';
description?: string;
parameters?: Array<{
name: string;
type: string; // Free-form type description
required?: boolean;
description?: string;
}>;
returnType?: string; // Free-form type description
}>;
}

Example declaration:

const spec: ComponentSpec = {
name: 'DataTable',
code: '...',
methods: [
{
name: 'getCurrentDataState',
category: 'standard',
description: 'Returns current table data and selection state',
returnType: '{data: any[], selectedRows: number[], totalCount: number}'
},
{
name: 'exportSelectedRows',
category: 'custom',
description: 'Exports currently selected rows',
returnType: 'any[]'
},
{
name: 'deleteSelected',
category: 'custom',
description: 'Deletes selected rows from the table'
}
]
};
  1. AI Agent Integration: AI agents can introspect component state for analysis
  2. Validation & State Management: Containers can check dirty state and validate before saving
  3. Custom Business Logic: Components can expose domain-specific operations
  4. Framework Agnostic: Works across Angular, React, and other frameworks
  5. Type Safety: Standard methods have full TypeScript support
  6. Discoverability: Method declarations enable static analysis without runtime

The package includes comprehensive types for component specifications:

  • InteractiveComponentSpec: Full component specification including metadata, props, and initialization
  • ComponentDataRequirement: Defines data loading requirements
  • ComponentOption: Configuration options for components
  • ComponentLibraryDependency: External library dependencies
  • ComponentSpecCategory: Component categorization and metadata

The included SimpleVectorService provides:

  • Cosine Similarity: Calculate similarity between vectors
  • Euclidean Distance: Measure distance between points
  • K-Nearest Neighbors (KNN): Find similar items
  • Clustering: Group similar items together
  • Dimensionality Reduction: PCA and t-SNE support
  1. Model Selection: Use modelPower parameter wisely - ‘lowest’ for simple tasks, ‘highest’ for complex analysis
  2. Batch Operations: Use RunViews for parallel data loading instead of sequential RunView calls
  3. Embedding Caching: Cache embeddings when possible as they’re deterministic for the same input
  4. Error Handling: Always check success flags in AI operation results
  5. JSON Parsing: Use resultObject when expecting structured data from AI prompts

All interfaces are fully typed with TypeScript, providing:

  • IntelliSense support in IDEs
  • Compile-time type checking
  • Better code documentation
  • Reduced runtime errors

Forms subpath (@memberjunction/interactive-component-types/forms)

Section titled “Forms subpath (@memberjunction/interactive-component-types/forms)”

The /forms subpath provides the form-role contract for components that declare componentRole: 'form' — the substrate that lets MJ render runtime-author forms inside Explorer with the same toolbar / save / delete behavior as a CodeGen-generated Angular form.

ExportPurpose
FormHostPropsWhat the host wrapper passes a form-role React component (entity name, primary key, record snapshot, mode, permissions).
FormEventNames / FormBeforeSaveArgs / etc.Standard event names + payload types the component emits via callbacks.NotifyEvent.
FormMethodNamesStandard methods the component registers (RequestSave, RequestCancel) so the host toolbar can drive its save flow.
isFormRole(spec)Type-guard: does this ComponentSpec declare componentRole: 'form'?
CuratedFormSchema, buildCuratedFormSchema(entityName, provider)Curated, LLM-friendly view of an entity’s schema for form authoring — FK references resolved to {entity, displayField}, value lists annotated with allowedValues, audit/virtual/computed fields stripped.
buildDefaultFormScaffold(entityName, provider)Produces a working form-role ComponentSpec that mirrors the CodeGen Angular default layout. Used as the baseline by the Form Builder agent and the dashboard’s “New form” flow.
buildFixtureFormHostProps(schema, mode?)Synthesizes type-appropriate fixture values for live-preview rendering when no real BaseEntity is available (used by Component Studio preview + the chat artifact viewer’s fallback mode).
getDeclaredFormEntityName(spec)Resolves the entity a form binds to from spec.entityName or spec.dataRequirements.entities[0].name. Pure helper shared by every form-role consumer.

See /plans/interactive-forms/phase-2-runtime-loop.md for the full architecture, lifecycle, and security model (Create / Modify / Activate / Revert + ownership checks).

  • @memberjunction/core: Core MemberJunction types and interfaces
  • @memberjunction/ai-vectors-memory: Vector operations and similarity calculations

ISC