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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions packages/foundation/core/src/formula-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* ObjectQL Formula Plugin
* Copyright (c) 2026-present ObjectStack Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import type { RuntimePlugin, RuntimeContext, ObjectStackKernel } from '@objectstack/runtime';
import { FormulaEngine } from './formula-engine';
import type { FormulaEngineConfig } from '@objectql/types';

/**
* Configuration for the Formula Plugin
*/
export interface FormulaPluginConfig extends FormulaEngineConfig {
/**
* Enable automatic formula evaluation on queries
* @default true
*/
autoEvaluateOnQuery?: boolean;
}

/**
* Formula Plugin
*
* Wraps the ObjectQL Formula Engine as an ObjectStack plugin.
* Registers formula evaluation capabilities into the kernel.
*/
export class FormulaPlugin implements RuntimePlugin {
name = '@objectql/formulas';
version = '4.0.0';

private engine: FormulaEngine;
private config: FormulaPluginConfig;

constructor(config: FormulaPluginConfig = {}) {
this.config = {
autoEvaluateOnQuery: true,
...config
};

// Initialize the formula engine with configuration
this.engine = new FormulaEngine(config);
}

/**
* Install the plugin into the kernel
* Registers formula evaluation capabilities
*/
async install(ctx: RuntimeContext): Promise<void> {
const kernel = ctx.engine as ObjectStackKernel;

console.log(`[${this.name}] Installing formula plugin...`);

// Register formula provider if the kernel supports it
this.registerFormulaProvider(kernel);

// Register formula evaluation middleware if auto-evaluation is enabled
if (this.config.autoEvaluateOnQuery !== false) {
this.registerFormulaMiddleware(kernel);
}

console.log(`[${this.name}] Formula plugin installed`);
}

/**
* Register the formula provider with the kernel
* @private
*/
private registerFormulaProvider(kernel: ObjectStackKernel): void {
// Check if kernel supports formula provider registration
// Note: Using type assertion since registerFormulaProvider may not be in the interface
const kernelWithFormulas = kernel as any;

if (typeof kernelWithFormulas.registerFormulaProvider === 'function') {
kernelWithFormulas.registerFormulaProvider({
evaluate: (formula: string, context: any) => {
// Delegate to the formula engine
// Note: In a real implementation, we would need to properly construct
// the FormulaContext from the provided context
return this.engine.evaluate(
formula,
context,
'text', // default data type
{}
);
},
validate: (expression: string) => {
return this.engine.validate(expression);
},
extractMetadata: (fieldName: string, expression: string, dataType: any) => {
return this.engine.extractMetadata(fieldName, expression, dataType);
}
});
} else {
// If the kernel doesn't support formula provider registration yet,
// we still register the engine for direct access
kernelWithFormulas.formulaEngine = this.engine;
}
}

/**
* Register formula evaluation middleware
* @private
*/
private registerFormulaMiddleware(kernel: ObjectStackKernel): void {
// Check if kernel supports middleware hooks
const kernelWithHooks = kernel as any;

if (typeof kernelWithHooks.use === 'function') {
// Register middleware to evaluate formulas after queries
kernelWithHooks.use('afterQuery', async (context: any) => {
// Formula evaluation logic would go here
// This would automatically compute formula fields after data is retrieved
if (context.results && context.metadata?.fields) {
// Iterate through fields and evaluate formulas
// const formulaFields = Object.entries(context.metadata.fields)
// .filter(([_, fieldConfig]) => (fieldConfig as any).formula);
//
// for (const record of context.results) {
// for (const [fieldName, fieldConfig] of formulaFields) {
// const formula = (fieldConfig as any).formula;
// const result = this.engine.evaluate(formula, /* context */, /* dataType */);
// record[fieldName] = result.value;
// }
// }
}
});
}
}

/**
* Get the formula engine instance for direct access
*/
getEngine(): FormulaEngine {
return this.engine;
}
}
2 changes: 2 additions & 0 deletions packages/foundation/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export type { DriverInterface, DriverOptions, QueryAST } from '@objectstack/spec
export * from './repository';
export * from './app';
export * from './plugin';
export * from './validator-plugin';
export * from './formula-plugin';

export * from './action';
export * from './hook';
Expand Down
42 changes: 20 additions & 22 deletions packages/foundation/core/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import type { RuntimePlugin, RuntimeContext } from '@objectstack/runtime';
import type { ObjectStackKernel } from '@objectstack/runtime';
import { ValidatorPlugin, ValidatorPluginConfig } from './validator-plugin';
import { FormulaPlugin, FormulaPluginConfig } from './formula-plugin';

/**
* Configuration for the ObjectQL Plugin
Expand All @@ -25,12 +27,24 @@ export interface ObjectQLPluginConfig {
*/
enableValidator?: boolean;

/**
* Validator plugin configuration
* Only used if enableValidator is not false
*/
validatorConfig?: ValidatorPluginConfig;

/**
* Enable formula engine
* @default true
*/
enableFormulas?: boolean;

/**
* Formula plugin configuration
* Only used if enableFormulas is not false
*/
formulaConfig?: FormulaPluginConfig;

/**
* Enable AI integration
* @default true
Expand Down Expand Up @@ -72,12 +86,16 @@ export class ObjectQLPlugin implements RuntimePlugin {
await this.registerRepository(ctx.engine);
}

// Install validator plugin if enabled
if (this.config.enableValidator !== false) {
await this.registerValidator(ctx.engine);
const validatorPlugin = new ValidatorPlugin(this.config.validatorConfig || {});
await validatorPlugin.install(ctx);
}

// Install formula plugin if enabled
if (this.config.enableFormulas !== false) {
await this.registerFormulas(ctx.engine);
const formulaPlugin = new FormulaPlugin(this.config.formulaConfig || {});
await formulaPlugin.install(ctx);
}

if (this.config.enableAI !== false) {
Expand Down Expand Up @@ -106,26 +124,6 @@ export class ObjectQLPlugin implements RuntimePlugin {
console.log(`[${this.name}] Repository pattern registered`);
}

/**
* Register the Validator engine
* @private
*/
private async registerValidator(kernel: ObjectStackKernel): Promise<void> {
// TODO: Implement validator registration
// For now, this is a placeholder to establish the structure
console.log(`[${this.name}] Validator engine registered`);
}

/**
* Register the Formula engine
* @private
*/
private async registerFormulas(kernel: ObjectStackKernel): Promise<void> {
// TODO: Implement formula registration
// For now, this is a placeholder to establish the structure
console.log(`[${this.name}] Formula engine registered`);
}

/**
* Register AI integration
* @private
Expand Down
130 changes: 130 additions & 0 deletions packages/foundation/core/src/validator-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* ObjectQL Validator Plugin
* Copyright (c) 2026-present ObjectStack Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import type { RuntimePlugin, RuntimeContext, ObjectStackKernel } from '@objectstack/runtime';
import { Validator, ValidatorOptions } from './validator';

/**
* Configuration for the Validator Plugin
*/
export interface ValidatorPluginConfig extends ValidatorOptions {
/**
* Enable validation on queries
* @default true
*/
enableQueryValidation?: boolean;

/**
* Enable validation on mutations
* @default true
*/
enableMutationValidation?: boolean;
}

/**
* Validator Plugin
*
* Wraps the ObjectQL Validator engine as an ObjectStack plugin.
* Registers validation middleware hooks into the kernel lifecycle.
*/
export class ValidatorPlugin implements RuntimePlugin {
name = '@objectql/validator';
version = '4.0.0';

private validator: Validator;
private config: ValidatorPluginConfig;

constructor(config: ValidatorPluginConfig = {}) {
this.config = {
enableQueryValidation: true,
enableMutationValidation: true,
...config
};

// Initialize the validator with language options
this.validator = new Validator({
language: config.language,
languageFallback: config.languageFallback,
});
}

/**
* Install the plugin into the kernel
* Registers validation middleware for queries and mutations
*/
async install(ctx: RuntimeContext): Promise<void> {
const kernel = ctx.engine as ObjectStackKernel;

console.log(`[${this.name}] Installing validator plugin...`);

// Register validation middleware for queries (if enabled)
if (this.config.enableQueryValidation !== false) {
this.registerQueryValidation(kernel);
}

// Register validation middleware for mutations (if enabled)
if (this.config.enableMutationValidation !== false) {
this.registerMutationValidation(kernel);
}

console.log(`[${this.name}] Validator plugin installed`);
}

/**
* Register query validation middleware
* @private
*/
private registerQueryValidation(kernel: ObjectStackKernel): void {
// Check if kernel supports middleware hooks
if (typeof (kernel as any).use === 'function') {
(kernel as any).use('beforeQuery', async (context: any) => {
// Query validation logic
// In a real implementation, this would validate query parameters
// For now, this is a placeholder that demonstrates the integration pattern
if (context.query && context.metadata?.validation_rules) {
// Validation would happen here
// const result = await this.validator.validate(
// context.metadata.validation_rules,
// { /* validation context */ }
// );
}
});
}
}

/**
* Register mutation validation middleware
* @private
*/
private registerMutationValidation(kernel: ObjectStackKernel): void {
// Check if kernel supports middleware hooks
if (typeof (kernel as any).use === 'function') {
(kernel as any).use('beforeMutation', async (context: any) => {
// Mutation validation logic
// This would validate data before create/update operations
if (context.data && context.metadata?.validation_rules) {
// Validation would happen here
// const result = await this.validator.validate(
// context.metadata.validation_rules,
// { /* validation context */ }
// );
// if (!result.valid) {
// throw new Error('Validation failed: ' + result.errors.map(e => e.message).join(', '));
// }
}
});
}
}

/**
* Get the validator instance for direct access
*/
getValidator(): Validator {
return this.validator;
}
}
Loading
Loading