@object-ui/core 0.5.0 → 2.0.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 (85) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +11 -0
  3. package/dist/actions/ActionRunner.d.ts +228 -4
  4. package/dist/actions/ActionRunner.js +397 -45
  5. package/dist/actions/TransactionManager.d.ts +193 -0
  6. package/dist/actions/TransactionManager.js +410 -0
  7. package/dist/actions/index.d.ts +1 -0
  8. package/dist/actions/index.js +1 -0
  9. package/dist/adapters/ApiDataSource.d.ts +69 -0
  10. package/dist/adapters/ApiDataSource.js +293 -0
  11. package/dist/adapters/ValueDataSource.d.ts +55 -0
  12. package/dist/adapters/ValueDataSource.js +287 -0
  13. package/dist/adapters/index.d.ts +3 -0
  14. package/dist/adapters/index.js +5 -2
  15. package/dist/adapters/resolveDataSource.d.ts +40 -0
  16. package/dist/adapters/resolveDataSource.js +59 -0
  17. package/dist/data-scope/DataScopeManager.d.ts +127 -0
  18. package/dist/data-scope/DataScopeManager.js +229 -0
  19. package/dist/data-scope/index.d.ts +10 -0
  20. package/dist/data-scope/index.js +10 -0
  21. package/dist/evaluator/ExpressionEvaluator.d.ts +11 -1
  22. package/dist/evaluator/ExpressionEvaluator.js +32 -8
  23. package/dist/evaluator/FormulaFunctions.d.ts +58 -0
  24. package/dist/evaluator/FormulaFunctions.js +350 -0
  25. package/dist/evaluator/index.d.ts +1 -0
  26. package/dist/evaluator/index.js +1 -0
  27. package/dist/index.d.ts +4 -0
  28. package/dist/index.js +4 -2
  29. package/dist/query/query-ast.d.ts +2 -2
  30. package/dist/query/query-ast.js +3 -3
  31. package/dist/registry/Registry.d.ts +10 -0
  32. package/dist/registry/Registry.js +2 -1
  33. package/dist/registry/WidgetRegistry.d.ts +120 -0
  34. package/dist/registry/WidgetRegistry.js +275 -0
  35. package/dist/theme/ThemeEngine.d.ts +82 -0
  36. package/dist/theme/ThemeEngine.js +400 -0
  37. package/dist/theme/index.d.ts +8 -0
  38. package/dist/theme/index.js +8 -0
  39. package/dist/validation/index.d.ts +1 -1
  40. package/dist/validation/index.js +1 -1
  41. package/dist/validation/validation-engine.d.ts +19 -1
  42. package/dist/validation/validation-engine.js +67 -2
  43. package/dist/validation/validators/index.d.ts +1 -1
  44. package/dist/validation/validators/index.js +1 -1
  45. package/dist/validation/validators/object-validation-engine.d.ts +2 -2
  46. package/dist/validation/validators/object-validation-engine.js +1 -1
  47. package/package.json +4 -3
  48. package/src/actions/ActionRunner.ts +577 -55
  49. package/src/actions/TransactionManager.ts +521 -0
  50. package/src/actions/__tests__/ActionRunner.params.test.ts +134 -0
  51. package/src/actions/__tests__/ActionRunner.test.ts +711 -0
  52. package/src/actions/__tests__/TransactionManager.test.ts +447 -0
  53. package/src/actions/index.ts +1 -0
  54. package/src/adapters/ApiDataSource.ts +349 -0
  55. package/src/adapters/ValueDataSource.ts +332 -0
  56. package/src/adapters/__tests__/ApiDataSource.test.ts +418 -0
  57. package/src/adapters/__tests__/ValueDataSource.test.ts +325 -0
  58. package/src/adapters/__tests__/resolveDataSource.test.ts +144 -0
  59. package/src/adapters/index.ts +6 -1
  60. package/src/adapters/resolveDataSource.ts +79 -0
  61. package/src/builder/__tests__/schema-builder.test.ts +235 -0
  62. package/src/data-scope/DataScopeManager.ts +269 -0
  63. package/src/data-scope/__tests__/DataScopeManager.test.ts +211 -0
  64. package/src/data-scope/index.ts +16 -0
  65. package/src/evaluator/ExpressionEvaluator.ts +34 -8
  66. package/src/evaluator/FormulaFunctions.ts +398 -0
  67. package/src/evaluator/__tests__/ExpressionContext.test.ts +110 -0
  68. package/src/evaluator/__tests__/FormulaFunctions.test.ts +447 -0
  69. package/src/evaluator/index.ts +1 -0
  70. package/src/index.ts +4 -3
  71. package/src/query/__tests__/window-functions.test.ts +1 -1
  72. package/src/query/query-ast.ts +3 -3
  73. package/src/registry/Registry.ts +12 -1
  74. package/src/registry/WidgetRegistry.ts +316 -0
  75. package/src/registry/__tests__/WidgetRegistry.test.ts +321 -0
  76. package/src/theme/ThemeEngine.ts +452 -0
  77. package/src/theme/__tests__/ThemeEngine.test.ts +606 -0
  78. package/src/theme/index.ts +22 -0
  79. package/src/validation/__tests__/object-validation-engine.test.ts +1 -1
  80. package/src/validation/__tests__/schema-validator.test.ts +118 -0
  81. package/src/validation/index.ts +1 -1
  82. package/src/validation/validation-engine.ts +61 -2
  83. package/src/validation/validators/index.ts +1 -1
  84. package/src/validation/validators/object-validation-engine.ts +2 -2
  85. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,400 @@
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
+ // Color Utilities
10
+ // ============================================================================
11
+ /**
12
+ * Convert a hex color (#RRGGBB or #RGB) to an HSL string "H S% L%".
13
+ * Returns null if the input is not a valid hex color.
14
+ */
15
+ export function hexToHSL(hex) {
16
+ // Expand shorthand (#RGB → #RRGGBB)
17
+ let clean = hex.replace(/^#/, '');
18
+ if (clean.length === 3) {
19
+ clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];
20
+ }
21
+ const match = /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(clean);
22
+ if (!match)
23
+ return null;
24
+ const r = parseInt(match[1], 16) / 255;
25
+ const g = parseInt(match[2], 16) / 255;
26
+ const b = parseInt(match[3], 16) / 255;
27
+ const max = Math.max(r, g, b);
28
+ const min = Math.min(r, g, b);
29
+ let h = 0;
30
+ let s = 0;
31
+ const l = (max + min) / 2;
32
+ if (max !== min) {
33
+ const d = max - min;
34
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
35
+ switch (max) {
36
+ case r:
37
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
38
+ break;
39
+ case g:
40
+ h = ((b - r) / d + 2) / 6;
41
+ break;
42
+ case b:
43
+ h = ((r - g) / d + 4) / 6;
44
+ break;
45
+ }
46
+ }
47
+ return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;
48
+ }
49
+ /**
50
+ * Detect if a color string is a hex value.
51
+ */
52
+ function isHex(color) {
53
+ return /^#([a-f\d]{3}|[a-f\d]{6})$/i.test(color);
54
+ }
55
+ /**
56
+ * Convert a color to a CSS-ready value.
57
+ * - Hex colors → HSL format for Shadcn CSS variable compatibility
58
+ * - Non-hex colors → passed through as-is (rgb, hsl, oklch, etc.)
59
+ */
60
+ export function toCSSColor(color) {
61
+ if (isHex(color)) {
62
+ return hexToHSL(color) ?? color;
63
+ }
64
+ return color;
65
+ }
66
+ // ============================================================================
67
+ // Theme → CSS Variable Mapping
68
+ // ============================================================================
69
+ /**
70
+ * Mapping from spec ColorPalette keys → Shadcn CSS variable names.
71
+ *
72
+ * The spec uses semantic names (primary, secondary, error, text, surface, etc.)
73
+ * while Shadcn uses its own naming (--primary, --secondary, --destructive, etc.)
74
+ * This maps the spec keys to the closest Shadcn equivalent.
75
+ */
76
+ const COLOR_TO_CSS_MAP = {
77
+ primary: '--primary',
78
+ secondary: '--secondary',
79
+ accent: '--accent',
80
+ success: '--success',
81
+ warning: '--warning',
82
+ error: '--destructive',
83
+ info: '--info',
84
+ background: '--background',
85
+ surface: '--card',
86
+ text: '--foreground',
87
+ textSecondary: '--muted-foreground',
88
+ border: '--border',
89
+ disabled: '--muted',
90
+ primaryLight: '--primary-light',
91
+ primaryDark: '--primary-dark',
92
+ secondaryLight: '--secondary-light',
93
+ secondaryDark: '--secondary-dark',
94
+ };
95
+ /**
96
+ * Generate CSS custom properties from a Theme's color palette.
97
+ */
98
+ export function generateColorVars(colors) {
99
+ const vars = {};
100
+ for (const [key, cssVar] of Object.entries(COLOR_TO_CSS_MAP)) {
101
+ const value = colors[key];
102
+ if (value) {
103
+ const cssValue = toCSSColor(value);
104
+ if (Array.isArray(cssVar)) {
105
+ for (const v of cssVar) {
106
+ vars[v] = cssValue;
107
+ }
108
+ }
109
+ else {
110
+ vars[cssVar] = cssValue;
111
+ }
112
+ }
113
+ }
114
+ return vars;
115
+ }
116
+ /**
117
+ * Generate CSS custom properties from a Theme's typography config.
118
+ */
119
+ export function generateTypographyVars(typography) {
120
+ const vars = {};
121
+ if (typography.fontFamily?.base) {
122
+ vars['--font-sans'] = typography.fontFamily.base;
123
+ }
124
+ if (typography.fontFamily?.heading) {
125
+ vars['--font-heading'] = typography.fontFamily.heading;
126
+ }
127
+ if (typography.fontFamily?.mono) {
128
+ vars['--font-mono'] = typography.fontFamily.mono;
129
+ }
130
+ if (typography.fontSize) {
131
+ for (const [key, value] of Object.entries(typography.fontSize)) {
132
+ if (value)
133
+ vars[`--font-size-${key}`] = value;
134
+ }
135
+ }
136
+ if (typography.fontWeight) {
137
+ for (const [key, value] of Object.entries(typography.fontWeight)) {
138
+ if (value != null)
139
+ vars[`--font-weight-${key}`] = String(value);
140
+ }
141
+ }
142
+ if (typography.lineHeight) {
143
+ for (const [key, value] of Object.entries(typography.lineHeight)) {
144
+ if (value)
145
+ vars[`--line-height-${key}`] = value;
146
+ }
147
+ }
148
+ if (typography.letterSpacing) {
149
+ for (const [key, value] of Object.entries(typography.letterSpacing)) {
150
+ if (value)
151
+ vars[`--letter-spacing-${key}`] = value;
152
+ }
153
+ }
154
+ return vars;
155
+ }
156
+ /**
157
+ * Generate CSS custom properties from a Theme's border radius config.
158
+ */
159
+ export function generateBorderRadiusVars(borderRadius) {
160
+ const vars = {};
161
+ const map = {
162
+ none: '--radius-none',
163
+ sm: '--radius-sm',
164
+ base: '--radius',
165
+ md: '--radius-md',
166
+ lg: '--radius-lg',
167
+ xl: '--radius-xl',
168
+ '2xl': '--radius-2xl',
169
+ full: '--radius-full',
170
+ };
171
+ for (const [key, cssVar] of Object.entries(map)) {
172
+ const value = borderRadius[key];
173
+ if (value)
174
+ vars[cssVar] = value;
175
+ }
176
+ return vars;
177
+ }
178
+ /**
179
+ * Generate CSS custom properties from a Theme's shadow config.
180
+ */
181
+ export function generateShadowVars(shadows) {
182
+ const vars = {};
183
+ const map = {
184
+ none: '--shadow-none',
185
+ sm: '--shadow-sm',
186
+ base: '--shadow',
187
+ md: '--shadow-md',
188
+ lg: '--shadow-lg',
189
+ xl: '--shadow-xl',
190
+ '2xl': '--shadow-2xl',
191
+ inner: '--shadow-inner',
192
+ };
193
+ for (const [key, cssVar] of Object.entries(map)) {
194
+ const value = shadows[key];
195
+ if (value)
196
+ vars[cssVar] = value;
197
+ }
198
+ return vars;
199
+ }
200
+ /**
201
+ * Generate CSS custom properties from a Theme's animation config.
202
+ */
203
+ export function generateAnimationVars(animation) {
204
+ const vars = {};
205
+ if (animation.duration) {
206
+ for (const [key, value] of Object.entries(animation.duration)) {
207
+ if (value)
208
+ vars[`--duration-${key}`] = value;
209
+ }
210
+ }
211
+ if (animation.timing) {
212
+ for (const [key, value] of Object.entries(animation.timing)) {
213
+ if (value)
214
+ vars[`--timing-${key}`] = value;
215
+ }
216
+ }
217
+ return vars;
218
+ }
219
+ /**
220
+ * Generate CSS custom properties from a Theme's z-index config.
221
+ */
222
+ export function generateZIndexVars(zIndex) {
223
+ const vars = {};
224
+ for (const [key, value] of Object.entries(zIndex)) {
225
+ if (value != null)
226
+ vars[`--z-${key}`] = String(value);
227
+ }
228
+ return vars;
229
+ }
230
+ /**
231
+ * Generate ALL CSS custom properties from a complete Theme.
232
+ * This is the main entry point for theme → CSS conversion.
233
+ */
234
+ export function generateThemeVars(theme) {
235
+ const vars = {};
236
+ // Colors (always present — colors.primary is required)
237
+ Object.assign(vars, generateColorVars(theme.colors));
238
+ // Typography
239
+ if (theme.typography) {
240
+ Object.assign(vars, generateTypographyVars(theme.typography));
241
+ }
242
+ // Border Radius
243
+ if (theme.borderRadius) {
244
+ Object.assign(vars, generateBorderRadiusVars(theme.borderRadius));
245
+ }
246
+ // Shadows
247
+ if (theme.shadows) {
248
+ Object.assign(vars, generateShadowVars(theme.shadows));
249
+ }
250
+ // Animation
251
+ if (theme.animation) {
252
+ Object.assign(vars, generateAnimationVars(theme.animation));
253
+ }
254
+ // Z-Index
255
+ if (theme.zIndex) {
256
+ Object.assign(vars, generateZIndexVars(theme.zIndex));
257
+ }
258
+ // Custom CSS variables (passthrough)
259
+ if (theme.customVars) {
260
+ for (const [key, value] of Object.entries(theme.customVars)) {
261
+ // Ensure CSS variable prefix
262
+ const varName = key.startsWith('--') ? key : `--${key}`;
263
+ vars[varName] = value;
264
+ }
265
+ }
266
+ return vars;
267
+ }
268
+ // ============================================================================
269
+ // Theme Inheritance
270
+ // ============================================================================
271
+ /**
272
+ * Deep-merge two Theme objects. The `child` overrides the `parent`.
273
+ * Only defined properties in child override; undefined falls back to parent.
274
+ */
275
+ export function mergeThemes(parent, child) {
276
+ return {
277
+ ...parent,
278
+ ...child,
279
+ // Deep-merge colors
280
+ colors: {
281
+ ...parent.colors,
282
+ ...(child.colors ?? {}),
283
+ },
284
+ // Deep-merge typography
285
+ typography: child.typography || parent.typography
286
+ ? {
287
+ ...parent.typography,
288
+ ...child.typography,
289
+ fontFamily: {
290
+ ...parent.typography?.fontFamily,
291
+ ...child.typography?.fontFamily,
292
+ },
293
+ fontSize: {
294
+ ...parent.typography?.fontSize,
295
+ ...child.typography?.fontSize,
296
+ },
297
+ fontWeight: {
298
+ ...parent.typography?.fontWeight,
299
+ ...child.typography?.fontWeight,
300
+ },
301
+ lineHeight: {
302
+ ...parent.typography?.lineHeight,
303
+ ...child.typography?.lineHeight,
304
+ },
305
+ letterSpacing: {
306
+ ...parent.typography?.letterSpacing,
307
+ ...child.typography?.letterSpacing,
308
+ },
309
+ }
310
+ : undefined,
311
+ // Deep-merge border radius
312
+ borderRadius: child.borderRadius || parent.borderRadius
313
+ ? { ...parent.borderRadius, ...child.borderRadius }
314
+ : undefined,
315
+ // Deep-merge shadows
316
+ shadows: child.shadows || parent.shadows
317
+ ? { ...parent.shadows, ...child.shadows }
318
+ : undefined,
319
+ // Deep-merge breakpoints
320
+ breakpoints: child.breakpoints || parent.breakpoints
321
+ ? { ...parent.breakpoints, ...child.breakpoints }
322
+ : undefined,
323
+ // Deep-merge animation
324
+ animation: child.animation || parent.animation
325
+ ? {
326
+ ...parent.animation,
327
+ ...child.animation,
328
+ duration: {
329
+ ...parent.animation?.duration,
330
+ ...child.animation?.duration,
331
+ },
332
+ timing: {
333
+ ...parent.animation?.timing,
334
+ ...child.animation?.timing,
335
+ },
336
+ }
337
+ : undefined,
338
+ // Deep-merge zIndex
339
+ zIndex: child.zIndex || parent.zIndex
340
+ ? { ...parent.zIndex, ...child.zIndex }
341
+ : undefined,
342
+ // Deep-merge spacing
343
+ spacing: child.spacing || parent.spacing
344
+ ? { ...parent.spacing, ...child.spacing }
345
+ : undefined,
346
+ // Deep-merge customVars
347
+ customVars: child.customVars || parent.customVars
348
+ ? { ...parent.customVars, ...child.customVars }
349
+ : undefined,
350
+ // Deep-merge logo
351
+ logo: child.logo || parent.logo
352
+ ? { ...parent.logo, ...child.logo }
353
+ : undefined,
354
+ };
355
+ }
356
+ /**
357
+ * Resolve theme inheritance from a registry of themes.
358
+ * If a theme has `extends`, the parent is looked up and merged recursively.
359
+ *
360
+ * @param theme - The theme to resolve
361
+ * @param registry - Map of theme name → Theme
362
+ * @param visited - Set of already-visited names (cycle detection)
363
+ * @returns The fully resolved theme
364
+ */
365
+ export function resolveThemeInheritance(theme, registry, visited = new Set()) {
366
+ if (!theme.extends)
367
+ return theme;
368
+ // Cycle detection
369
+ if (visited.has(theme.name))
370
+ return theme;
371
+ visited.add(theme.name);
372
+ const parent = registry.get(theme.extends);
373
+ if (!parent)
374
+ return theme;
375
+ // Recursively resolve parent first
376
+ const resolvedParent = resolveThemeInheritance(parent, registry, visited);
377
+ return mergeThemes(resolvedParent, theme);
378
+ }
379
+ // ============================================================================
380
+ // Mode Resolution
381
+ // ============================================================================
382
+ /**
383
+ * Resolve the effective mode from a ThemeMode value.
384
+ * 'auto' checks the system preference (prefers-color-scheme).
385
+ *
386
+ * @param mode - The declared mode
387
+ * @param systemDark - Whether the system prefers dark mode (for SSR or testing)
388
+ * @returns 'light' or 'dark'
389
+ */
390
+ export function resolveMode(mode = 'auto', systemDark) {
391
+ if (mode === 'light' || mode === 'dark')
392
+ return mode;
393
+ // 'auto' — check system preference
394
+ if (systemDark !== undefined)
395
+ return systemDark ? 'dark' : 'light';
396
+ if (typeof window !== 'undefined' && window.matchMedia) {
397
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
398
+ }
399
+ return 'light'; // fallback
400
+ }
@@ -0,0 +1,8 @@
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
+ export { hexToHSL, toCSSColor, generateColorVars, generateTypographyVars, generateBorderRadiusVars, generateShadowVars, generateAnimationVars, generateZIndexVars, generateThemeVars, mergeThemes, resolveThemeInheritance, resolveMode, } from './ThemeEngine';
@@ -0,0 +1,8 @@
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
+ export { hexToHSL, toCSSColor, generateColorVars, generateTypographyVars, generateBorderRadiusVars, generateShadowVars, generateAnimationVars, generateZIndexVars, generateThemeVars, mergeThemes, resolveThemeInheritance, resolveMode, } from './ThemeEngine';
@@ -2,7 +2,7 @@
2
2
  * @object-ui/core - Validation Module
3
3
  *
4
4
  * Phase 3.5: Validation engine
5
- * ObjectStack Spec v0.7.1: Object-level validation
5
+ * ObjectStack Spec v2.0.1: Object-level validation
6
6
  */
7
7
  export * from './validation-engine.js';
8
8
  export * from './schema-validator.js';
@@ -2,7 +2,7 @@
2
2
  * @object-ui/core - Validation Module
3
3
  *
4
4
  * Phase 3.5: Validation engine
5
- * ObjectStack Spec v0.7.1: Object-level validation
5
+ * ObjectStack Spec v2.0.1: Object-level validation
6
6
  */
7
7
  export * from './validation-engine.js';
8
8
  export * from './schema-validator.js';
@@ -17,11 +17,29 @@
17
17
  * @module validation-engine
18
18
  * @packageDocumentation
19
19
  */
20
- import type { AdvancedValidationSchema, ValidationContext, AdvancedValidationResult, AdvancedValidationError } from '@object-ui/types';
20
+ import type { AdvancedValidationSchema, ValidationFunction, AsyncValidationFunction, ValidationContext, AdvancedValidationResult, AdvancedValidationError } from '@object-ui/types';
21
21
  /**
22
22
  * Validation Engine - Executes validation rules
23
23
  */
24
24
  export declare class ValidationEngine {
25
+ private customValidators;
26
+ private customAsyncValidators;
27
+ /**
28
+ * Register a custom synchronous validator by name
29
+ */
30
+ registerValidator(name: string, fn: ValidationFunction): void;
31
+ /**
32
+ * Register a custom asynchronous validator by name
33
+ */
34
+ registerAsyncValidator(name: string, fn: AsyncValidationFunction): void;
35
+ /**
36
+ * Check if a custom validator is registered
37
+ */
38
+ hasValidator(name: string): boolean;
39
+ /**
40
+ * Get all registered custom validator names
41
+ */
42
+ getValidatorNames(): string[];
25
43
  /**
26
44
  * Validate a value against validation schema
27
45
  */
@@ -9,6 +9,47 @@
9
9
  * Validation Engine - Executes validation rules
10
10
  */
11
11
  export class ValidationEngine {
12
+ constructor() {
13
+ Object.defineProperty(this, "customValidators", {
14
+ enumerable: true,
15
+ configurable: true,
16
+ writable: true,
17
+ value: new Map()
18
+ });
19
+ Object.defineProperty(this, "customAsyncValidators", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: new Map()
24
+ });
25
+ }
26
+ /**
27
+ * Register a custom synchronous validator by name
28
+ */
29
+ registerValidator(name, fn) {
30
+ this.customValidators.set(name, fn);
31
+ }
32
+ /**
33
+ * Register a custom asynchronous validator by name
34
+ */
35
+ registerAsyncValidator(name, fn) {
36
+ this.customAsyncValidators.set(name, fn);
37
+ }
38
+ /**
39
+ * Check if a custom validator is registered
40
+ */
41
+ hasValidator(name) {
42
+ return this.customValidators.has(name) || this.customAsyncValidators.has(name);
43
+ }
44
+ /**
45
+ * Get all registered custom validator names
46
+ */
47
+ getValidatorNames() {
48
+ return [
49
+ ...Array.from(this.customValidators.keys()),
50
+ ...Array.from(this.customAsyncValidators.keys()),
51
+ ];
52
+ }
12
53
  /**
13
54
  * Validate a value against validation schema
14
55
  */
@@ -43,7 +84,7 @@ export class ValidationEngine {
43
84
  * Validate a single rule
44
85
  */
45
86
  async validateRule(value, rule, context) {
46
- // Custom async validator
87
+ // Custom async validator (inline)
47
88
  if (rule.async_validator) {
48
89
  const result = await rule.async_validator(value, context);
49
90
  if (result === false) {
@@ -54,7 +95,7 @@ export class ValidationEngine {
54
95
  }
55
96
  return null;
56
97
  }
57
- // Custom sync validator
98
+ // Custom sync validator (inline)
58
99
  if (rule.validator) {
59
100
  const result = rule.validator(value, context);
60
101
  if (result === false) {
@@ -65,6 +106,30 @@ export class ValidationEngine {
65
106
  }
66
107
  return null;
67
108
  }
109
+ // Registered custom async validator (by name)
110
+ const registeredAsync = this.customAsyncValidators.get(rule.type);
111
+ if (registeredAsync) {
112
+ const result = await registeredAsync(value, context);
113
+ if (result === false) {
114
+ return rule.message || 'Async validation failed';
115
+ }
116
+ if (typeof result === 'string') {
117
+ return result;
118
+ }
119
+ return null;
120
+ }
121
+ // Registered custom sync validator (by name)
122
+ const registeredSync = this.customValidators.get(rule.type);
123
+ if (registeredSync) {
124
+ const result = registeredSync(value, context);
125
+ if (result === false) {
126
+ return rule.message || 'Validation failed';
127
+ }
128
+ if (typeof result === 'string') {
129
+ return result;
130
+ }
131
+ return null;
132
+ }
68
133
  // Built-in validators
69
134
  return this.validateBuiltInRule(value, rule, context);
70
135
  }
@@ -8,7 +8,7 @@
8
8
  /**
9
9
  * @object-ui/core - Validators
10
10
  *
11
- * ObjectStack Spec v0.7.1 compliant validators
11
+ * ObjectStack Spec v2.0.1 compliant validators
12
12
  *
13
13
  * @module validators
14
14
  * @packageDocumentation
@@ -8,7 +8,7 @@
8
8
  /**
9
9
  * @object-ui/core - Validators
10
10
  *
11
- * ObjectStack Spec v0.7.1 compliant validators
11
+ * ObjectStack Spec v2.0.1 compliant validators
12
12
  *
13
13
  * @module validators
14
14
  * @packageDocumentation
@@ -8,7 +8,7 @@
8
8
  /**
9
9
  * @object-ui/core - Object-Level Validation Engine
10
10
  *
11
- * ObjectStack Spec v0.7.1 compliant validation engine for object-level validation rules.
11
+ * ObjectStack Spec v2.0.1 compliant validation engine for object-level validation rules.
12
12
  * Supports all 9 validation types from the specification:
13
13
  * - ScriptValidation
14
14
  * - UniquenessValidation
@@ -57,7 +57,7 @@ export interface ValidationExpressionEvaluator {
57
57
  }
58
58
  /**
59
59
  * Object-Level Validation Engine
60
- * Implements ObjectStack Spec v0.7.1 validation framework
60
+ * Implements ObjectStack Spec v2.0.1 validation framework
61
61
  */
62
62
  export declare class ObjectValidationEngine {
63
63
  private expressionEvaluator;
@@ -170,7 +170,7 @@ class SimpleExpressionEvaluator {
170
170
  }
171
171
  /**
172
172
  * Object-Level Validation Engine
173
- * Implements ObjectStack Spec v0.7.1 validation framework
173
+ * Implements ObjectStack Spec v2.0.1 validation framework
174
174
  */
175
175
  export class ObjectValidationEngine {
176
176
  constructor(expressionEvaluator, uniquenessChecker) {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@object-ui/core",
3
- "version": "0.5.0",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
+ "sideEffects": false,
5
6
  "license": "MIT",
6
7
  "description": "Core logic, types, and validation for Object UI. Zero React dependencies.",
7
8
  "homepage": "https://www.objectui.org",
@@ -23,10 +24,10 @@
23
24
  }
24
25
  },
25
26
  "dependencies": {
26
- "@objectstack/spec": "^0.9.1",
27
+ "@objectstack/spec": "^2.0.7",
27
28
  "lodash": "^4.17.23",
28
29
  "zod": "^4.3.6",
29
- "@object-ui/types": "0.5.0"
30
+ "@object-ui/types": "2.0.0"
30
31
  },
31
32
  "devDependencies": {
32
33
  "typescript": "^5.9.3",