@object-ui/core 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/dist/actions/index.d.ts +1 -1
  3. package/dist/actions/index.js +1 -1
  4. package/dist/evaluator/ExpressionCache.d.ts +101 -0
  5. package/dist/evaluator/ExpressionCache.js +135 -0
  6. package/dist/evaluator/ExpressionEvaluator.d.ts +20 -2
  7. package/dist/evaluator/ExpressionEvaluator.js +34 -14
  8. package/dist/evaluator/index.d.ts +3 -2
  9. package/dist/evaluator/index.js +3 -2
  10. package/dist/index.d.ts +10 -7
  11. package/dist/index.js +9 -7
  12. package/dist/query/index.d.ts +6 -0
  13. package/dist/query/index.js +6 -0
  14. package/dist/query/query-ast.d.ts +32 -0
  15. package/dist/query/query-ast.js +268 -0
  16. package/dist/registry/PluginScopeImpl.d.ts +80 -0
  17. package/dist/registry/PluginScopeImpl.js +243 -0
  18. package/dist/registry/PluginSystem.d.ts +66 -0
  19. package/dist/registry/PluginSystem.js +142 -0
  20. package/dist/registry/Registry.d.ts +73 -4
  21. package/dist/registry/Registry.js +112 -7
  22. package/dist/validation/index.d.ts +9 -0
  23. package/dist/validation/index.js +9 -0
  24. package/dist/validation/validation-engine.d.ts +70 -0
  25. package/dist/validation/validation-engine.js +363 -0
  26. package/dist/validation/validators/index.d.ts +16 -0
  27. package/dist/validation/validators/index.js +16 -0
  28. package/dist/validation/validators/object-validation-engine.d.ts +118 -0
  29. package/dist/validation/validators/object-validation-engine.js +538 -0
  30. package/package.json +13 -5
  31. package/src/actions/index.ts +1 -1
  32. package/src/evaluator/ExpressionCache.ts +192 -0
  33. package/src/evaluator/ExpressionEvaluator.ts +33 -14
  34. package/src/evaluator/__tests__/ExpressionCache.test.ts +135 -0
  35. package/src/evaluator/index.ts +3 -2
  36. package/src/index.ts +10 -7
  37. package/src/query/__tests__/query-ast.test.ts +211 -0
  38. package/src/query/__tests__/window-functions.test.ts +275 -0
  39. package/src/query/index.ts +7 -0
  40. package/src/query/query-ast.ts +341 -0
  41. package/src/registry/PluginScopeImpl.ts +259 -0
  42. package/src/registry/PluginSystem.ts +161 -0
  43. package/src/registry/Registry.ts +125 -8
  44. package/src/registry/__tests__/PluginSystem.test.ts +226 -0
  45. package/src/registry/__tests__/Registry.test.ts +293 -0
  46. package/src/registry/__tests__/plugin-scope-integration.test.ts +283 -0
  47. package/src/validation/__tests__/object-validation-engine.test.ts +567 -0
  48. package/src/validation/__tests__/validation-engine.test.ts +102 -0
  49. package/src/validation/index.ts +10 -0
  50. package/src/validation/validation-engine.ts +461 -0
  51. package/src/validation/validators/index.ts +25 -0
  52. package/src/validation/validators/object-validation-engine.ts +722 -0
  53. package/tsconfig.tsbuildinfo +1 -1
  54. package/vitest.config.ts +2 -0
  55. package/src/adapters/index.d.ts +0 -8
  56. package/src/adapters/index.js +0 -10
  57. package/src/builder/schema-builder.d.ts +0 -294
  58. package/src/builder/schema-builder.js +0 -503
  59. package/src/index.d.ts +0 -13
  60. package/src/index.js +0 -16
  61. package/src/registry/Registry.d.ts +0 -56
  62. package/src/registry/Registry.js +0 -43
  63. package/src/types/index.d.ts +0 -19
  64. package/src/types/index.js +0 -8
  65. package/src/utils/filter-converter.d.ts +0 -57
  66. package/src/utils/filter-converter.js +0 -100
  67. package/src/validation/schema-validator.d.ts +0 -94
  68. package/src/validation/schema-validator.js +0 -278
@@ -0,0 +1,142 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ import { PluginScopeImpl } from './PluginScopeImpl.js';
9
+ export class PluginSystem {
10
+ constructor() {
11
+ Object.defineProperty(this, "plugins", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: new Map()
16
+ });
17
+ Object.defineProperty(this, "loaded", {
18
+ enumerable: true,
19
+ configurable: true,
20
+ writable: true,
21
+ value: new Set()
22
+ });
23
+ Object.defineProperty(this, "scopes", {
24
+ enumerable: true,
25
+ configurable: true,
26
+ writable: true,
27
+ value: new Map()
28
+ });
29
+ }
30
+ /**
31
+ * Load a plugin into the system with optional scope isolation
32
+ * @param plugin The plugin definition to load
33
+ * @param registry The component registry to use for registration
34
+ * @param useScope Whether to use scoped loading (default: true for better isolation)
35
+ * @throws Error if dependencies are missing
36
+ */
37
+ async loadPlugin(plugin, registry, useScope = true) {
38
+ // Check if already loaded
39
+ if (this.loaded.has(plugin.name)) {
40
+ console.warn(`Plugin "${plugin.name}" is already loaded. Skipping.`);
41
+ return;
42
+ }
43
+ // Check dependencies
44
+ for (const dep of plugin.dependencies || []) {
45
+ if (!this.loaded.has(dep)) {
46
+ throw new Error(`Missing dependency: ${dep} required by ${plugin.name}`);
47
+ }
48
+ }
49
+ try {
50
+ if (useScope) {
51
+ // Create scoped environment for plugin
52
+ const scope = new PluginScopeImpl(plugin.name, plugin.version, registry, plugin.scopeConfig);
53
+ // Store scope for cleanup
54
+ this.scopes.set(plugin.name, scope);
55
+ // Execute registration with scope
56
+ plugin.register(scope);
57
+ }
58
+ else {
59
+ // Legacy mode: direct registry access
60
+ plugin.register(registry);
61
+ }
62
+ // Store plugin definition
63
+ this.plugins.set(plugin.name, plugin);
64
+ // Execute lifecycle hook
65
+ await plugin.onLoad?.();
66
+ // Mark as loaded
67
+ this.loaded.add(plugin.name);
68
+ }
69
+ catch (error) {
70
+ // Clean up on failure
71
+ this.plugins.delete(plugin.name);
72
+ this.scopes.delete(plugin.name);
73
+ throw error;
74
+ }
75
+ }
76
+ /**
77
+ * Unload a plugin from the system
78
+ * @param name The name of the plugin to unload
79
+ * @throws Error if other plugins depend on this plugin
80
+ */
81
+ async unloadPlugin(name) {
82
+ const plugin = this.plugins.get(name);
83
+ if (!plugin) {
84
+ throw new Error(`Plugin "${name}" is not loaded`);
85
+ }
86
+ // Check if any loaded plugins depend on this one
87
+ for (const [pluginName, pluginDef] of this.plugins.entries()) {
88
+ if (this.loaded.has(pluginName) && pluginDef.dependencies?.includes(name)) {
89
+ throw new Error(`Cannot unload plugin "${name}" - plugin "${pluginName}" depends on it`);
90
+ }
91
+ }
92
+ // Execute lifecycle hook
93
+ await plugin.onUnload?.();
94
+ // Clean up scope if exists
95
+ const scope = this.scopes.get(name);
96
+ if (scope) {
97
+ scope.cleanup();
98
+ this.scopes.delete(name);
99
+ }
100
+ // Remove from loaded set
101
+ this.loaded.delete(name);
102
+ this.plugins.delete(name);
103
+ }
104
+ /**
105
+ * Get the scope for a loaded plugin
106
+ * @param name The name of the plugin
107
+ * @returns The plugin scope or undefined
108
+ */
109
+ getScope(name) {
110
+ return this.scopes.get(name);
111
+ }
112
+ /**
113
+ * Check if a plugin is loaded
114
+ * @param name The name of the plugin
115
+ * @returns true if the plugin is loaded
116
+ */
117
+ isLoaded(name) {
118
+ return this.loaded.has(name);
119
+ }
120
+ /**
121
+ * Get a loaded plugin definition
122
+ * @param name The name of the plugin
123
+ * @returns The plugin definition or undefined
124
+ */
125
+ getPlugin(name) {
126
+ return this.plugins.get(name);
127
+ }
128
+ /**
129
+ * Get all loaded plugin names
130
+ * @returns Array of loaded plugin names
131
+ */
132
+ getLoadedPlugins() {
133
+ return Array.from(this.loaded);
134
+ }
135
+ /**
136
+ * Get all plugin definitions
137
+ * @returns Array of all plugin definitions
138
+ */
139
+ getAllPlugins() {
140
+ return Array.from(this.plugins.values());
141
+ }
142
+ }
@@ -5,7 +5,7 @@
5
5
  * This source code is licensed under the MIT license found in the
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
- import type { SchemaNode } from '../types';
8
+ import type { SchemaNode } from '../types/index.js';
9
9
  export type ComponentRenderer<T = any> = T;
10
10
  export type ComponentInput = {
11
11
  name: string;
@@ -25,6 +25,7 @@ export type ComponentMeta = {
25
25
  label?: string;
26
26
  icon?: string;
27
27
  category?: string;
28
+ namespace?: string;
28
29
  inputs?: ComponentInput[];
29
30
  defaultProps?: Record<string, any>;
30
31
  defaultChildren?: SchemaNode[];
@@ -46,11 +47,79 @@ export type ComponentConfig<T = any> = ComponentMeta & {
46
47
  };
47
48
  export declare class Registry<T = any> {
48
49
  private components;
50
+ /**
51
+ * Register a component with optional namespace support.
52
+ * If namespace is provided in meta, the component will be registered as "namespace:type".
53
+ *
54
+ * @param type - Component type identifier
55
+ * @param component - Component renderer
56
+ * @param meta - Component metadata (including optional namespace)
57
+ *
58
+ * @example
59
+ * // Register with namespace
60
+ * registry.register('button', ButtonComponent, { namespace: 'ui' });
61
+ * // Accessible as 'ui:button' or 'button' (fallback)
62
+ *
63
+ * @example
64
+ * // Register without namespace (backward compatible)
65
+ * registry.register('button', ButtonComponent);
66
+ * // Accessible as 'button'
67
+ */
49
68
  register(type: string, component: ComponentRenderer<T>, meta?: ComponentMeta): void;
50
- get(type: string): ComponentRenderer<T> | undefined;
51
- getConfig(type: string): ComponentConfig<T> | undefined;
52
- has(type: string): boolean;
69
+ /**
70
+ * Get a component by type. Supports both namespaced and non-namespaced lookups.
71
+ *
72
+ * @param type - Component type (e.g., 'button' or 'ui:button')
73
+ * @param namespace - Optional namespace for lookup priority
74
+ * @returns Component renderer or undefined
75
+ *
76
+ * @example
77
+ * // Direct lookup
78
+ * registry.get('ui:button') // Gets ui:button
79
+ *
80
+ * @example
81
+ * // Fallback lookup
82
+ * registry.get('button') // Gets first registered button
83
+ *
84
+ * @example
85
+ * // Namespaced lookup with priority
86
+ * registry.get('button', 'ui') // Tries 'ui:button' first, then 'button'
87
+ */
88
+ get(type: string, namespace?: string): ComponentRenderer<T> | undefined;
89
+ /**
90
+ * Get component configuration by type with namespace support.
91
+ *
92
+ * @param type - Component type (e.g., 'button' or 'ui:button')
93
+ * @param namespace - Optional namespace for lookup priority
94
+ * @returns Component configuration or undefined
95
+ */
96
+ getConfig(type: string, namespace?: string): ComponentConfig<T> | undefined;
97
+ /**
98
+ * Check if a component type is registered.
99
+ *
100
+ * @param type - Component type (e.g., 'button' or 'ui:button')
101
+ * @param namespace - Optional namespace for lookup
102
+ * @returns True if component is registered
103
+ */
104
+ has(type: string, namespace?: string): boolean;
105
+ /**
106
+ * Get all registered component types.
107
+ *
108
+ * @returns Array of all component type identifiers
109
+ */
53
110
  getAllTypes(): string[];
111
+ /**
112
+ * Get all registered component configurations.
113
+ *
114
+ * @returns Array of all component configurations
115
+ */
54
116
  getAllConfigs(): ComponentConfig<T>[];
117
+ /**
118
+ * Get all components in a specific namespace.
119
+ *
120
+ * @param namespace - Namespace to filter by
121
+ * @returns Array of component configurations in the namespace
122
+ */
123
+ getNamespaceComponents(namespace: string): ComponentConfig<T>[];
55
124
  }
56
125
  export declare const ComponentRegistry: Registry<any>;
@@ -14,30 +14,135 @@ export class Registry {
14
14
  value: new Map()
15
15
  });
16
16
  }
17
+ /**
18
+ * Register a component with optional namespace support.
19
+ * If namespace is provided in meta, the component will be registered as "namespace:type".
20
+ *
21
+ * @param type - Component type identifier
22
+ * @param component - Component renderer
23
+ * @param meta - Component metadata (including optional namespace)
24
+ *
25
+ * @example
26
+ * // Register with namespace
27
+ * registry.register('button', ButtonComponent, { namespace: 'ui' });
28
+ * // Accessible as 'ui:button' or 'button' (fallback)
29
+ *
30
+ * @example
31
+ * // Register without namespace (backward compatible)
32
+ * registry.register('button', ButtonComponent);
33
+ * // Accessible as 'button'
34
+ */
17
35
  register(type, component, meta) {
18
- if (this.components.has(type)) {
19
- console.warn(`Component type "${type}" is already registered. Overwriting.`);
36
+ const fullType = meta?.namespace ? `${meta.namespace}:${type}` : type;
37
+ // Warn if registering without namespace (deprecated pattern)
38
+ if (!meta?.namespace) {
39
+ console.warn(`Registering component "${type}" without a namespace is deprecated. ` +
40
+ `Please provide a namespace in the meta parameter.`);
20
41
  }
21
- this.components.set(type, {
22
- type,
42
+ if (this.components.has(fullType)) {
43
+ // console.warn(`Component type "${fullType}" is already registered. Overwriting.`);
44
+ }
45
+ this.components.set(fullType, {
46
+ type: fullType,
23
47
  component,
24
48
  ...meta
25
49
  });
50
+ // Also register without namespace for backward compatibility
51
+ // This allows "button" to work even when registered as "ui:button"
52
+ // Note: If multiple namespaced components share the same short name,
53
+ // the last registration wins for non-namespaced lookups
54
+ if (meta?.namespace) {
55
+ this.components.set(type, {
56
+ type: fullType, // Keep reference to namespaced type
57
+ component,
58
+ ...meta
59
+ });
60
+ }
26
61
  }
27
- get(type) {
62
+ /**
63
+ * Get a component by type. Supports both namespaced and non-namespaced lookups.
64
+ *
65
+ * @param type - Component type (e.g., 'button' or 'ui:button')
66
+ * @param namespace - Optional namespace for lookup priority
67
+ * @returns Component renderer or undefined
68
+ *
69
+ * @example
70
+ * // Direct lookup
71
+ * registry.get('ui:button') // Gets ui:button
72
+ *
73
+ * @example
74
+ * // Fallback lookup
75
+ * registry.get('button') // Gets first registered button
76
+ *
77
+ * @example
78
+ * // Namespaced lookup with priority
79
+ * registry.get('button', 'ui') // Tries 'ui:button' first, then 'button'
80
+ */
81
+ get(type, namespace) {
82
+ // If namespace is explicitly provided, ONLY look in that namespace (no fallback)
83
+ if (namespace) {
84
+ const namespacedType = `${namespace}:${type}`;
85
+ return this.components.get(namespacedType)?.component;
86
+ }
87
+ // When no namespace provided, use backward compatibility lookup
28
88
  return this.components.get(type)?.component;
29
89
  }
30
- getConfig(type) {
90
+ /**
91
+ * Get component configuration by type with namespace support.
92
+ *
93
+ * @param type - Component type (e.g., 'button' or 'ui:button')
94
+ * @param namespace - Optional namespace for lookup priority
95
+ * @returns Component configuration or undefined
96
+ */
97
+ getConfig(type, namespace) {
98
+ // If namespace is explicitly provided, ONLY look in that namespace (no fallback)
99
+ if (namespace) {
100
+ const namespacedType = `${namespace}:${type}`;
101
+ return this.components.get(namespacedType);
102
+ }
103
+ // When no namespace provided, use backward compatibility lookup
31
104
  return this.components.get(type);
32
105
  }
33
- has(type) {
106
+ /**
107
+ * Check if a component type is registered.
108
+ *
109
+ * @param type - Component type (e.g., 'button' or 'ui:button')
110
+ * @param namespace - Optional namespace for lookup
111
+ * @returns True if component is registered
112
+ */
113
+ has(type, namespace) {
114
+ // If namespace is explicitly provided, ONLY look in that namespace (no fallback)
115
+ if (namespace) {
116
+ const namespacedType = `${namespace}:${type}`;
117
+ return this.components.has(namespacedType);
118
+ }
119
+ // When no namespace provided, use backward compatibility lookup
34
120
  return this.components.has(type);
35
121
  }
122
+ /**
123
+ * Get all registered component types.
124
+ *
125
+ * @returns Array of all component type identifiers
126
+ */
36
127
  getAllTypes() {
37
128
  return Array.from(this.components.keys());
38
129
  }
130
+ /**
131
+ * Get all registered component configurations.
132
+ *
133
+ * @returns Array of all component configurations
134
+ */
39
135
  getAllConfigs() {
40
136
  return Array.from(this.components.values());
41
137
  }
138
+ /**
139
+ * Get all components in a specific namespace.
140
+ *
141
+ * @param namespace - Namespace to filter by
142
+ * @returns Array of component configurations in the namespace
143
+ */
144
+ getNamespaceComponents(namespace) {
145
+ return Array.from(this.components.values()).filter(config => config.namespace === namespace);
146
+ }
42
147
  }
43
148
  export const ComponentRegistry = new Registry();
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @object-ui/core - Validation Module
3
+ *
4
+ * Phase 3.5: Validation engine
5
+ * ObjectStack Spec v0.7.1: Object-level validation
6
+ */
7
+ export * from './validation-engine.js';
8
+ export * from './schema-validator.js';
9
+ export * from './validators/index.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @object-ui/core - Validation Module
3
+ *
4
+ * Phase 3.5: Validation engine
5
+ * ObjectStack Spec v0.7.1: Object-level validation
6
+ */
7
+ export * from './validation-engine.js';
8
+ export * from './schema-validator.js';
9
+ export * from './validators/index.js';
@@ -0,0 +1,70 @@
1
+ /**
2
+ * ObjectUI
3
+ * Copyright (c) 2024-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ /**
9
+ * @object-ui/core - Validation Engine
10
+ *
11
+ * Phase 3.5: Complete validation engine implementation with support for:
12
+ * - Sync and async validation
13
+ * - Cross-field validation
14
+ * - Custom validation functions
15
+ * - Improved error messages
16
+ *
17
+ * @module validation-engine
18
+ * @packageDocumentation
19
+ */
20
+ import type { AdvancedValidationSchema, ValidationContext, AdvancedValidationResult, AdvancedValidationError } from '@object-ui/types';
21
+ /**
22
+ * Validation Engine - Executes validation rules
23
+ */
24
+ export declare class ValidationEngine {
25
+ /**
26
+ * Validate a value against validation schema
27
+ */
28
+ validate(value: any, schema: AdvancedValidationSchema, context?: ValidationContext): Promise<AdvancedValidationResult>;
29
+ /**
30
+ * Validate a single rule
31
+ */
32
+ private validateRule;
33
+ /**
34
+ * Validate built-in rules
35
+ */
36
+ private validateBuiltInRule;
37
+ /**
38
+ * Evaluate a condition
39
+ * Note: Conditions must be declarative objects, not functions, for security.
40
+ */
41
+ private evaluateCondition;
42
+ /**
43
+ * Validate multiple fields
44
+ */
45
+ validateFields(values: Record<string, any>, schemas: Record<string, AdvancedValidationSchema>): Promise<Record<string, AdvancedValidationResult>>;
46
+ /**
47
+ * Check if all fields are valid
48
+ */
49
+ isValid(results: Record<string, AdvancedValidationResult>): boolean;
50
+ /**
51
+ * Get all errors from validation results
52
+ */
53
+ getAllErrors(results: Record<string, AdvancedValidationResult>): AdvancedValidationError[];
54
+ /**
55
+ * Get all warnings from validation results
56
+ */
57
+ getAllWarnings(results: Record<string, AdvancedValidationResult>): AdvancedValidationError[];
58
+ }
59
+ /**
60
+ * Default validation engine instance
61
+ */
62
+ export declare const defaultValidationEngine: ValidationEngine;
63
+ /**
64
+ * Convenience function to validate a value
65
+ */
66
+ export declare function validate(value: any, schema: AdvancedValidationSchema, context?: ValidationContext): Promise<AdvancedValidationResult>;
67
+ /**
68
+ * Convenience function to validate multiple fields
69
+ */
70
+ export declare function validateFields(values: Record<string, any>, schemas: Record<string, AdvancedValidationSchema>): Promise<Record<string, AdvancedValidationResult>>;