# @memberjunction/core-entities-server
Server-side entity subclasses for MemberJunction that provide extended functionality and business logic for core entities when running in a Node.js environment.
## Overview
This package contains server-side implementations of MemberJunction entity subclasses that require server-specific functionality such as direct database access, file system operations, server-side API integrations, complex business logic, and automatic creation/management of related entities.
```mermaid
graph TD
A["Core Entities Server"] --> B["AIPromptEntityExtended"]
A --> C["QueryEntityExtended"]
A --> D["ComponentEntityExtended"]
A --> E["MJActionEntityExtended"]
A --> F["ApplicationEntityExtended"]
A --> G["Other Server Entities"]
B -->|manages| H["Templates
Template Contents"]
C -->|manages| H
D -->|generates| I["Vector Embeddings"]
J["MJ Class Factory"] -->|returns server subclass| A
K["Core Entities
(base classes)"] -->|extended by| A
style A fill:#2d6a9f,stroke:#1a4971,color:#fff
style B fill:#7c5295,stroke:#563a6b,color:#fff
style C fill:#7c5295,stroke:#563a6b,color:#fff
style D fill:#7c5295,stroke:#563a6b,color:#fff
style E fill:#7c5295,stroke:#563a6b,color:#fff
style F fill:#7c5295,stroke:#563a6b,color:#fff
style H fill:#2d8659,stroke:#1a5c3a,color:#fff
style I fill:#b8762f,stroke:#8a5722,color:#fff
style J fill:#2d6a9f,stroke:#1a4971,color:#fff
style K fill:#b8762f,stroke:#8a5722,color:#fff
```
### Key Differences from Core Entities
- **Core Entities**: Work everywhere, basic CRUD operations, suitable for client-side usage
- **Core Entities Server**: Server-only, extended functionality, can directly interact with system resources
## Installation
```bash
npm install @memberjunction/core-entities-server
```
## Usage
### Basic Setup
```typescript
import { LoadCoreEntitiesServerSubClasses } from '@memberjunction/core-entities-server';
// Load all server-side entity subclasses at application startup
LoadCoreEntitiesServerSubClasses();
```
**Important**: Call `LoadCoreEntitiesServerSubClasses()` early in your application initialization to ensure the server-side subclasses are registered with the MemberJunction metadata system before any entities are instantiated.
### Example: AI Prompt Entity
The package includes an extended AI Prompt entity that automatically manages Template and Template Contents records:
```typescript
import { Metadata } from '@memberjunction/core';
// The metadata system will automatically use the server-side subclass
const md = new Metadata();
const aiPrompt = await md.GetEntityObject('AI Prompts');
// Setting TemplateText will automatically create/update Template records
aiPrompt.TemplateText = `You are a helpful assistant...`;
// Save will handle all the Template management automatically
await aiPrompt.Save();
```
## Available Server-Side Entities
### AIPromptEntityExtendedServer
Extends the base AI Prompt entity with automatic Template management:
- **Virtual Property**: `TemplateText` - A convenient way to get/set the prompt template content
- **Automatic Template Creation**: Creates Template and Template Contents records when saving
- **Automatic Template Updates**: Updates existing templates when content changes
- **Proper Relationship Management**: Maintains proper foreign key relationships
### ComponentEntityExtended_Server (v2.90.0+)
Extends the Component entity with automatic vector embedding generation:
- **Automatic Embeddings**: Generates vector embeddings for FunctionalRequirements and TechnicalDesign fields
- **Model Tracking**: Stores the AI model ID used for each embedding
- **Smart Updates**: Only regenerates embeddings when source text changes
- **Parallel Processing**: Generates multiple embeddings concurrently for performance
### QueryEntityExtendedServer
Extends the Query entity with automatic Template management similar to AIPromptEntity:
- **Virtual Property**: `TemplateText` - Manages the SQL query template content
- **Automatic Template Management**: Creates and updates Template records when saving
- **Dynamic SQL Support**: Supports parameterized queries with Nunjucks templates
### MJAIAgentNoteEntityServer (v5.30.x+)
Extends the agent note entity to keep the in-process vector store in sync with persisted state:
- **Vector Store Invariant**: Overrides `Save()` and `Delete()` so that `AIEngine.Instance._noteVectorService` contains an entry for a note iff its persisted `Status='Active'` and its `EmbeddingVector` is non-null
- **Inline Maintenance**: When a note's Status flips between `Active` and any other value, or when a note is deleted, the corresponding vector store entry is added/removed in the same operation — eliminating the need for an MJAPI restart after note revocation (e.g., during MemoryManagerAgent consolidation or contradiction resolution)
- **Consolidation Field Persistence**: New `AIAgentNote` columns introduced by `V202604260056__v5.30.x__Memory_Consolidation_Schema.sql` (`ConsolidatedIntoNoteID`, `ConsolidationCount`, `DerivedFromNoteIDs`, `ProtectionTier`, `ImportanceScore`) persist via the standard generated setters — no custom logic needed in this override
See [`@memberjunction/aiengine` README](../AI/Engine/README.md#vector-store-invariant-preservation) for the read-side and the consuming code in [`@memberjunction/ai-agents`](../AI/Agents/README.md#consolidation-pipeline).
### MJAIAgentExampleEntityServer
Companion to `MJAIAgentNoteEntityServer` — maintains the parallel invariant for the agent example vector store on `Save`/`Delete`.
### MJRemoteOperationEntityServer (v5.43.x+)
The **AI-from-Description** half of the [Remote Operations](../../guides/REMOTE_OPERATIONS_GUIDE.md) primitive (RO-4). For a `MJ: Remote Operations` row with `GenerationType='AI'`, the `Save` hook calls the `Generate Remote Operation Code` prompt to author the body of the operation's `InternalExecute` — against the ambient `input` / `provider` / `user` / `context` contract — stores it in `Code`, records the libraries it used in the JSONType `LibrariesObject`, and resets `CodeApprovalStatus='Pending'` for human review (skipped when `CodeLocked`). CodeGen then emits a complete registered class from the approved code. Mirrors `MJActionEntityServer`; transactional; no junction CRUD (the library list is a JSONType field).
## Architecture
### Entity Registration
The package uses MemberJunction's entity subclassing system:
1. At startup, `LoadCoreEntitiesServerSubClasses()` is called
2. This registers all server-side entity subclasses with the metadata system
3. When code requests an entity (e.g., "AI Prompts"), the metadata system returns the server-side subclass
4. The server-side subclass extends the base entity with additional functionality
### Virtual Properties
Server-side entities can expose virtual properties that don't exist in the database but provide convenient access to complex data:
```typescript
export class AIPromptEntityExtendedServer extends AIPromptEntity {
private _TemplateText?: string;
get TemplateText(): string | undefined {
return this._TemplateText;
}
set TemplateText(value: string | undefined) {
this._TemplateText = value;
}
}
```
## Development Guidelines
### Creating New Server-Side Entities
1. Create a new file in `src/custom/`
2. Import and extend the base entity class
3. Add server-specific functionality
4. Register the subclass in the loader
Example:
```typescript
import { BaseEntity, RegisterClass } from '@memberjunction/core';
import { UserEntity } from '@memberjunction/core-entities';
@RegisterClass(BaseEntity, 'Users')
export class UserEntityExtendedServer extends UserEntity {
async BeforeSave(): Promise {
// Server-side validation or processing
if (this.Email) {
// Verify email domain against company whitelist
const validDomain = await this.checkEmailDomain(this.Email);
if (!validDomain) {
throw new Error('Invalid email domain');
}
}
return super.BeforeSave();
}
private async checkEmailDomain(email: string): Promise {
// Server-side logic to validate email domain
return true;
}
}
```
### Best Practices
1. **Keep It Server-Side**: Only include code that must run on the server
2. **Extend, Don't Replace**: Always extend the base entity class, don't replace core functionality
3. **Handle Errors Gracefully**: Server-side operations can fail - handle errors appropriately
4. **Document Virtual Properties**: Clearly document any virtual properties and their behavior
5. **Test Thoroughly**: Server-side logic can be complex - ensure comprehensive testing
## Integration with Other MJ Packages
This package works seamlessly with:
- `@memberjunction/core`: Provides the base entity system
- `@memberjunction/core-entities`: Provides the base entity classes
- `@memberjunction/server`: Can be used in MJ server applications
- `@memberjunction/cli`: Used by CLI tools like MetadataSync
## Vector Embeddings Support (v2.90.0+)
This package provides a utility helper for server-side entities to easily implement vector embedding generation using the MemberJunction AI Engine.
### EmbedTextLocalHelper
A shared utility function that simplifies embedding generation for any server-side entity:
```typescript
import { EmbedTextLocalHelper } from '@memberjunction/core-entities-server';
import { BaseEntity, SimpleEmbeddingResult } from '@memberjunction/core';
@RegisterClass(BaseEntity, 'MyEntity')
export class MyEntityServer extends MyEntity {
protected async EmbedTextLocal(textToEmbed: string): Promise {
return EmbedTextLocalHelper(this, textToEmbed);
}
}
```
The helper:
- Configures the AI Engine with the current user context
- Calls the embedding generation
- Validates the response
- Returns a properly typed `SimpleEmbeddingResult`
### Using Vector Embeddings in Your Entity
1. **Add database fields** for storing vectors and model IDs
2. **Override EmbedTextLocal** using the helper function
3. **Call GenerateEmbeddings** in your Save method:
```typescript
public async Save(): Promise {
await this.GenerateEmbeddingsByFieldName([
{
fieldName: "Content",
vectorFieldName: "ContentVector",
modelFieldName: "ContentVectorModelID"
}
]);
return await super.Save();
}
```
## Common Use Cases
### 1. Automatic Related Entity Management
```typescript
// AI Prompts automatically manage their Template records
const prompt = await md.GetEntityObject('AI Prompts');
prompt.TemplateText = "New prompt content";
await prompt.Save(); // Template and Template Contents created/updated automatically
```
### 2. Vector Embedding Generation
```typescript
// Components automatically generate embeddings for text fields
const component = await md.GetEntityObject('MJ: Components');
component.FunctionalRequirements = "The system shall...";
component.TechnicalDesign = "Architecture overview...";
await component.Save(); // Embeddings generated automatically
```
### 3. Server-Side Validation
```typescript
@RegisterClass(BaseEntity, 'Documents')
export class DocumentEntityServer extends DocumentEntity {
async BeforeSave(): Promise {
// Scan document for viruses using server-side scanner
if (this.Content) {
const isSafe = await virusScanner.scan(this.Content);
if (!isSafe) {
throw new Error('Document failed security scan');
}
}
return super.BeforeSave();
}
}
```
### 3. Complex Business Logic
```typescript
@RegisterClass(BaseEntity, 'Orders')
export class OrderEntityServer extends OrderEntity {
async AfterSave(): Promise {
// Send order to fulfillment system
await fulfillmentAPI.submitOrder(this);
// Update inventory
await inventorySystem.decrementStock(this.OrderItems);
return super.AfterSave();
}
}
```
## SQL Parser
The `SQLParser` utility class extracts table/view references and column references from SQL statements. It uses `node-sql-parser` for AST-based parsing with automatic regex fallback when the AST parser fails. This class has **no dependency on MJ Metadata** — it returns raw parse results that callers cross-reference against entities themselves.
### When to Use
- **Virtual entity decoration**: Parse a view's SQL to identify which source entities it pulls from
- **Query entity metadata**: Parse SQL queries to find referenced tables and columns
- **Any server-side SQL analysis**: Extract structural information from SQL without executing it
### Basic Usage — Plain SQL
```typescript
import { SQLParser } from '@memberjunction/core-entities-server';
const sql = `
SELECT c.Name, c.Email, o.OrderDate, o.Total
FROM [dbo].[Customer] c
INNER JOIN [dbo].[Order] o ON o.CustomerID = c.ID
WHERE o.Status = 'Active'
`;
const result = SQLParser.Parse(sql);
// Tables found in the query
for (const table of result.Tables) {
console.log(`${table.SchemaName}.${table.TableName} (alias: ${table.Alias})`);
}
// Output:
// dbo.Customer (alias: c)
// dbo.Order (alias: o)
// Columns referenced in the query
for (const col of result.Columns) {
console.log(`${col.TableQualifier ? col.TableQualifier + '.' : ''}${col.ColumnName}`);
}
// Output:
// c.Name, c.Email, o.OrderDate, o.Total, o.CustomerID, c.ID, o.Status
// Whether AST parsing succeeded (true) or regex fallback was used (false)
console.log(`Used AST: ${result.UsedASTParsing}`);
```
### Nunjucks Template SQL
For SQL that contains Nunjucks template syntax (e.g., MemberJunction query templates), use `ParseWithTemplatePreprocessing`. This replaces template expressions with safe placeholder values before parsing:
```typescript
import { SQLParser } from '@memberjunction/core-entities-server';
const templateSQL = `
SELECT * FROM [dbo].[Customer]
WHERE Status = {{ status | sqlString }}
{% if regionId %}
AND RegionID = {{ regionId | sqlNumber }}
{% endif %}
`;
// Nunjucks syntax is replaced with placeholders, then parsed normally
const result = SQLParser.ParseWithTemplatePreprocessing(templateSQL);
console.log(result.Tables); // [{ TableName: 'Customer', SchemaName: 'dbo', Alias: 'Customer' }]
```
### Cross-Referencing with Entity Metadata
The parser returns raw table/column references. To resolve them to MemberJunction entities:
```typescript
import { Metadata } from '@memberjunction/core';
import { SQLParser } from '@memberjunction/core-entities-server';
const parseResult = SQLParser.Parse(viewDefinitionSQL);
const md = new Metadata();
for (const tableRef of parseResult.Tables) {
const entity = md.Entities.find(e =>
(e.BaseTable.toLowerCase() === tableRef.TableName.toLowerCase() ||
e.BaseView.toLowerCase() === tableRef.TableName.toLowerCase()) &&
e.SchemaName.toLowerCase() === tableRef.SchemaName.toLowerCase()
);
if (entity) {
console.log(`Table ${tableRef.TableName} → Entity "${entity.Name}"`);
// Access entity.Fields for full field metadata
}
}
```
### Return Types
```typescript
interface SQLParseResult {
Tables: SQLTableReference[]; // All table/view references (FROM, JOIN, subqueries, CTEs)
Columns: SQLColumnReference[]; // All column references (SELECT, WHERE, JOIN ON, etc.)
UsedASTParsing: boolean; // true = AST parsed, false = regex fallback
}
interface SQLTableReference {
TableName: string; // Table or view name
SchemaName: string; // Schema name (defaults to 'dbo')
Alias: string; // Query alias, or table name if no alias
}
interface SQLColumnReference {
ColumnName: string; // Column name
TableQualifier: string | null; // Table alias prefix (e.g., "c" in "c.Name")
}
```
### How It Works
1. **AST Parsing** (preferred): Uses `node-sql-parser` with `TransactSQL` dialect to build an AST, then recursively walks it to extract table references from `FROM`/`JOIN` clauses, subqueries, and CTEs, plus column references from `SELECT`, `WHERE`, `GROUP BY`, `ORDER BY`, and `ON` conditions.
2. **Regex Fallback**: If AST parsing fails (complex SQL, dialect edge cases), a regex-based extractor finds `FROM`/`JOIN` table references. Column references are not extracted in regex mode (the `Columns` array will be empty).
3. **Nunjucks Preprocessing**: `ParseWithTemplatePreprocessing` first renders the SQL through a Nunjucks environment with placeholder filters (`sqlString` → `'placeholder'`, `sqlNumber` → `0`, etc.), stripping template blocks while preserving the SQL structure. If Nunjucks rendering fails, it falls back to regex-based template removal.
## Troubleshooting
### Entity subclass not being used
Ensure `LoadCoreEntitiesServerSubClasses()` is called before any entity instantiation.
### Module loading errors
This package uses CommonJS. Ensure your Node.js application is configured to handle CommonJS modules.
### Virtual properties not persisting
Virtual properties are not saved to the database. Use them as convenient accessors that manage real database fields behind the scenes.
## Future Enhancements
- Additional server-side entity implementations
- Integration with file storage providers
- Advanced caching mechanisms
- Batch processing utilities
- Background job integration
## Contributing
When contributing new server-side entities:
1. Follow the existing patterns
2. Document all virtual properties
3. Include comprehensive tests
4. Ensure no client-side dependencies
5. Update this README with new entities
## License
This package is part of the MemberJunction open-source project.