@object-ui/core 0.5.0 → 3.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 (96) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +28 -0
  3. package/dist/__benchmarks__/core.bench.d.ts +8 -0
  4. package/dist/__benchmarks__/core.bench.js +53 -0
  5. package/dist/actions/ActionRunner.d.ts +228 -4
  6. package/dist/actions/ActionRunner.js +397 -45
  7. package/dist/actions/TransactionManager.d.ts +193 -0
  8. package/dist/actions/TransactionManager.js +410 -0
  9. package/dist/actions/index.d.ts +1 -0
  10. package/dist/actions/index.js +1 -0
  11. package/dist/adapters/ApiDataSource.d.ts +69 -0
  12. package/dist/adapters/ApiDataSource.js +293 -0
  13. package/dist/adapters/ValueDataSource.d.ts +55 -0
  14. package/dist/adapters/ValueDataSource.js +287 -0
  15. package/dist/adapters/index.d.ts +3 -0
  16. package/dist/adapters/index.js +5 -2
  17. package/dist/adapters/resolveDataSource.d.ts +40 -0
  18. package/dist/adapters/resolveDataSource.js +59 -0
  19. package/dist/data-scope/DataScopeManager.d.ts +127 -0
  20. package/dist/data-scope/DataScopeManager.js +229 -0
  21. package/dist/data-scope/index.d.ts +10 -0
  22. package/dist/data-scope/index.js +10 -0
  23. package/dist/errors/index.d.ts +75 -0
  24. package/dist/errors/index.js +224 -0
  25. package/dist/evaluator/ExpressionEvaluator.d.ts +11 -1
  26. package/dist/evaluator/ExpressionEvaluator.js +32 -8
  27. package/dist/evaluator/FormulaFunctions.d.ts +58 -0
  28. package/dist/evaluator/FormulaFunctions.js +350 -0
  29. package/dist/evaluator/index.d.ts +1 -0
  30. package/dist/evaluator/index.js +1 -0
  31. package/dist/index.d.ts +6 -0
  32. package/dist/index.js +6 -2
  33. package/dist/query/query-ast.d.ts +2 -2
  34. package/dist/query/query-ast.js +3 -3
  35. package/dist/registry/Registry.d.ts +10 -0
  36. package/dist/registry/Registry.js +9 -2
  37. package/dist/registry/WidgetRegistry.d.ts +120 -0
  38. package/dist/registry/WidgetRegistry.js +275 -0
  39. package/dist/theme/ThemeEngine.d.ts +105 -0
  40. package/dist/theme/ThemeEngine.js +469 -0
  41. package/dist/theme/index.d.ts +8 -0
  42. package/dist/theme/index.js +8 -0
  43. package/dist/utils/debug.d.ts +31 -0
  44. package/dist/utils/debug.js +62 -0
  45. package/dist/validation/index.d.ts +1 -1
  46. package/dist/validation/index.js +1 -1
  47. package/dist/validation/validation-engine.d.ts +19 -1
  48. package/dist/validation/validation-engine.js +74 -3
  49. package/dist/validation/validators/index.d.ts +1 -1
  50. package/dist/validation/validators/index.js +1 -1
  51. package/dist/validation/validators/object-validation-engine.d.ts +2 -2
  52. package/dist/validation/validators/object-validation-engine.js +1 -1
  53. package/package.json +4 -3
  54. package/src/__benchmarks__/core.bench.ts +64 -0
  55. package/src/actions/ActionRunner.ts +577 -55
  56. package/src/actions/TransactionManager.ts +521 -0
  57. package/src/actions/__tests__/ActionRunner.params.test.ts +134 -0
  58. package/src/actions/__tests__/ActionRunner.test.ts +711 -0
  59. package/src/actions/__tests__/TransactionManager.test.ts +447 -0
  60. package/src/actions/index.ts +1 -0
  61. package/src/adapters/ApiDataSource.ts +349 -0
  62. package/src/adapters/ValueDataSource.ts +332 -0
  63. package/src/adapters/__tests__/ApiDataSource.test.ts +418 -0
  64. package/src/adapters/__tests__/ValueDataSource.test.ts +325 -0
  65. package/src/adapters/__tests__/resolveDataSource.test.ts +144 -0
  66. package/src/adapters/index.ts +6 -1
  67. package/src/adapters/resolveDataSource.ts +79 -0
  68. package/src/builder/__tests__/schema-builder.test.ts +235 -0
  69. package/src/data-scope/DataScopeManager.ts +269 -0
  70. package/src/data-scope/__tests__/DataScopeManager.test.ts +211 -0
  71. package/src/data-scope/index.ts +16 -0
  72. package/src/errors/__tests__/errors.test.ts +292 -0
  73. package/src/errors/index.ts +270 -0
  74. package/src/evaluator/ExpressionEvaluator.ts +34 -8
  75. package/src/evaluator/FormulaFunctions.ts +398 -0
  76. package/src/evaluator/__tests__/ExpressionContext.test.ts +110 -0
  77. package/src/evaluator/__tests__/FormulaFunctions.test.ts +447 -0
  78. package/src/evaluator/index.ts +1 -0
  79. package/src/index.ts +6 -3
  80. package/src/query/__tests__/window-functions.test.ts +1 -1
  81. package/src/query/query-ast.ts +3 -3
  82. package/src/registry/Registry.ts +19 -2
  83. package/src/registry/WidgetRegistry.ts +316 -0
  84. package/src/registry/__tests__/WidgetRegistry.test.ts +321 -0
  85. package/src/theme/ThemeEngine.ts +530 -0
  86. package/src/theme/__tests__/ThemeEngine.test.ts +668 -0
  87. package/src/theme/index.ts +24 -0
  88. package/src/utils/__tests__/debug.test.ts +83 -0
  89. package/src/utils/debug.ts +66 -0
  90. package/src/validation/__tests__/object-validation-engine.test.ts +1 -1
  91. package/src/validation/__tests__/schema-validator.test.ts +118 -0
  92. package/src/validation/index.ts +1 -1
  93. package/src/validation/validation-engine.ts +70 -3
  94. package/src/validation/validators/index.ts +1 -1
  95. package/src/validation/validators/object-validation-engine.ts +2 -2
  96. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,469 @@
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
+ }
401
+ // ============================================================================
402
+ // WCAG Contrast Checking (v2.0.7)
403
+ // ============================================================================
404
+ /**
405
+ * Parse a hex color string to RGB values [0-255].
406
+ */
407
+ function hexToRGB(hex) {
408
+ let clean = hex.replace(/^#/, '');
409
+ if (clean.length === 3) {
410
+ clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];
411
+ }
412
+ const match = /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(clean);
413
+ if (!match)
414
+ return null;
415
+ return [parseInt(match[1], 16), parseInt(match[2], 16), parseInt(match[3], 16)];
416
+ }
417
+ /**
418
+ * Calculate relative luminance per WCAG 2.1 spec.
419
+ * @see https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
420
+ */
421
+ function relativeLuminance(r, g, b) {
422
+ const [rs, gs, bs] = [r, g, b].map(c => {
423
+ const s = c / 255;
424
+ return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
425
+ });
426
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
427
+ }
428
+ /**
429
+ * Calculate the WCAG 2.1 contrast ratio between two hex colors.
430
+ * Returns a value between 1 and 21.
431
+ *
432
+ * @param hex1 - First color in hex format (#RGB or #RRGGBB)
433
+ * @param hex2 - Second color in hex format (#RGB or #RRGGBB)
434
+ * @returns Contrast ratio (1-21), or null if colors are invalid
435
+ */
436
+ export function contrastRatio(hex1, hex2) {
437
+ const rgb1 = hexToRGB(hex1);
438
+ const rgb2 = hexToRGB(hex2);
439
+ if (!rgb1 || !rgb2)
440
+ return null;
441
+ const l1 = relativeLuminance(...rgb1);
442
+ const l2 = relativeLuminance(...rgb2);
443
+ const lighter = Math.max(l1, l2);
444
+ const darker = Math.min(l1, l2);
445
+ return (lighter + 0.05) / (darker + 0.05);
446
+ }
447
+ /**
448
+ * Check if two colors meet the specified WCAG contrast level.
449
+ *
450
+ * WCAG levels:
451
+ * - AA: 4.5:1 for normal text, 3:1 for large text
452
+ * - AAA: 7:1 for normal text, 4.5:1 for large text
453
+ *
454
+ * @param hex1 - First color in hex format
455
+ * @param hex2 - Second color in hex format
456
+ * @param level - WCAG level: 'AA' or 'AAA'
457
+ * @param isLargeText - Whether the text is large (18pt+ or 14pt+ bold)
458
+ * @returns true if the color pair meets the required contrast level
459
+ */
460
+ export function meetsContrastLevel(hex1, hex2, level = 'AA', isLargeText = false) {
461
+ const ratio = contrastRatio(hex1, hex2);
462
+ if (ratio === null)
463
+ return false;
464
+ if (level === 'AAA') {
465
+ return isLargeText ? ratio >= 4.5 : ratio >= 7;
466
+ }
467
+ // AA
468
+ return isLargeText ? ratio >= 3 : ratio >= 4.5;
469
+ }
@@ -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, contrastRatio, meetsContrastLevel, } 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, contrastRatio, meetsContrastLevel, } from './ThemeEngine';
@@ -0,0 +1,31 @@
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
+ type DebugCategory = 'schema' | 'registry' | 'expression' | 'action' | 'plugin' | 'render';
9
+ /**
10
+ * Log a debug message when OBJECTUI_DEBUG is enabled.
11
+ * No-op in production or when debug mode is off.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * // Enable debug mode
16
+ * globalThis.OBJECTUI_DEBUG = true;
17
+ *
18
+ * debugLog('schema', 'Resolving component', { type: 'Button' });
19
+ * // [ObjectUI Debug][schema] Resolving component { type: 'Button' }
20
+ * ```
21
+ */
22
+ export declare function debugLog(category: DebugCategory, message: string, data?: unknown): void;
23
+ /**
24
+ * Start a debug timer. Pair with {@link debugTimeEnd}.
25
+ */
26
+ export declare function debugTime(label: string): void;
27
+ /**
28
+ * End a debug timer and log the elapsed time.
29
+ */
30
+ export declare function debugTimeEnd(label: string): void;
31
+ export {};
@@ -0,0 +1,62 @@
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
+ function isDebugEnabled() {
9
+ try {
10
+ const g = typeof globalThis !== 'undefined' && globalThis.OBJECTUI_DEBUG;
11
+ return ((g === true || g === 'true') ||
12
+ (typeof process !== 'undefined' && process.env?.OBJECTUI_DEBUG === 'true'));
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ /**
19
+ * Log a debug message when OBJECTUI_DEBUG is enabled.
20
+ * No-op in production or when debug mode is off.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * // Enable debug mode
25
+ * globalThis.OBJECTUI_DEBUG = true;
26
+ *
27
+ * debugLog('schema', 'Resolving component', { type: 'Button' });
28
+ * // [ObjectUI Debug][schema] Resolving component { type: 'Button' }
29
+ * ```
30
+ */
31
+ export function debugLog(category, message, data) {
32
+ if (!isDebugEnabled())
33
+ return;
34
+ if (data !== undefined) {
35
+ console.log(`[ObjectUI Debug][${category}] ${message}`, data);
36
+ }
37
+ else {
38
+ console.log(`[ObjectUI Debug][${category}] ${message}`);
39
+ }
40
+ }
41
+ const timers = new Map();
42
+ /**
43
+ * Start a debug timer. Pair with {@link debugTimeEnd}.
44
+ */
45
+ export function debugTime(label) {
46
+ if (!isDebugEnabled())
47
+ return;
48
+ timers.set(label, performance.now());
49
+ }
50
+ /**
51
+ * End a debug timer and log the elapsed time.
52
+ */
53
+ export function debugTimeEnd(label) {
54
+ if (!isDebugEnabled())
55
+ return;
56
+ const start = timers.get(label);
57
+ if (start !== undefined) {
58
+ const elapsed = (performance.now() - start).toFixed(2);
59
+ console.log(`[ObjectUI Debug][perf] ${label}: ${elapsed}ms`);
60
+ timers.delete(label);
61
+ }
62
+ }
@@ -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
  */