@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +11 -0
- package/dist/actions/ActionRunner.d.ts +228 -4
- package/dist/actions/ActionRunner.js +397 -45
- package/dist/actions/TransactionManager.d.ts +193 -0
- package/dist/actions/TransactionManager.js +410 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +1 -0
- package/dist/adapters/ApiDataSource.d.ts +69 -0
- package/dist/adapters/ApiDataSource.js +293 -0
- package/dist/adapters/ValueDataSource.d.ts +55 -0
- package/dist/adapters/ValueDataSource.js +287 -0
- package/dist/adapters/index.d.ts +3 -0
- package/dist/adapters/index.js +5 -2
- package/dist/adapters/resolveDataSource.d.ts +40 -0
- package/dist/adapters/resolveDataSource.js +59 -0
- package/dist/data-scope/DataScopeManager.d.ts +127 -0
- package/dist/data-scope/DataScopeManager.js +229 -0
- package/dist/data-scope/index.d.ts +10 -0
- package/dist/data-scope/index.js +10 -0
- package/dist/evaluator/ExpressionEvaluator.d.ts +11 -1
- package/dist/evaluator/ExpressionEvaluator.js +32 -8
- package/dist/evaluator/FormulaFunctions.d.ts +58 -0
- package/dist/evaluator/FormulaFunctions.js +350 -0
- package/dist/evaluator/index.d.ts +1 -0
- package/dist/evaluator/index.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -2
- package/dist/query/query-ast.d.ts +2 -2
- package/dist/query/query-ast.js +3 -3
- package/dist/registry/Registry.d.ts +10 -0
- package/dist/registry/Registry.js +2 -1
- package/dist/registry/WidgetRegistry.d.ts +120 -0
- package/dist/registry/WidgetRegistry.js +275 -0
- package/dist/theme/ThemeEngine.d.ts +82 -0
- package/dist/theme/ThemeEngine.js +400 -0
- package/dist/theme/index.d.ts +8 -0
- package/dist/theme/index.js +8 -0
- package/dist/validation/index.d.ts +1 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/validation-engine.d.ts +19 -1
- package/dist/validation/validation-engine.js +67 -2
- package/dist/validation/validators/index.d.ts +1 -1
- package/dist/validation/validators/index.js +1 -1
- package/dist/validation/validators/object-validation-engine.d.ts +2 -2
- package/dist/validation/validators/object-validation-engine.js +1 -1
- package/package.json +4 -3
- package/src/actions/ActionRunner.ts +577 -55
- package/src/actions/TransactionManager.ts +521 -0
- package/src/actions/__tests__/ActionRunner.params.test.ts +134 -0
- package/src/actions/__tests__/ActionRunner.test.ts +711 -0
- package/src/actions/__tests__/TransactionManager.test.ts +447 -0
- package/src/actions/index.ts +1 -0
- package/src/adapters/ApiDataSource.ts +349 -0
- package/src/adapters/ValueDataSource.ts +332 -0
- package/src/adapters/__tests__/ApiDataSource.test.ts +418 -0
- package/src/adapters/__tests__/ValueDataSource.test.ts +325 -0
- package/src/adapters/__tests__/resolveDataSource.test.ts +144 -0
- package/src/adapters/index.ts +6 -1
- package/src/adapters/resolveDataSource.ts +79 -0
- package/src/builder/__tests__/schema-builder.test.ts +235 -0
- package/src/data-scope/DataScopeManager.ts +269 -0
- package/src/data-scope/__tests__/DataScopeManager.test.ts +211 -0
- package/src/data-scope/index.ts +16 -0
- package/src/evaluator/ExpressionEvaluator.ts +34 -8
- package/src/evaluator/FormulaFunctions.ts +398 -0
- package/src/evaluator/__tests__/ExpressionContext.test.ts +110 -0
- package/src/evaluator/__tests__/FormulaFunctions.test.ts +447 -0
- package/src/evaluator/index.ts +1 -0
- package/src/index.ts +4 -3
- package/src/query/__tests__/window-functions.test.ts +1 -1
- package/src/query/query-ast.ts +3 -3
- package/src/registry/Registry.ts +12 -1
- package/src/registry/WidgetRegistry.ts +316 -0
- package/src/registry/__tests__/WidgetRegistry.test.ts +321 -0
- package/src/theme/ThemeEngine.ts +452 -0
- package/src/theme/__tests__/ThemeEngine.test.ts +606 -0
- package/src/theme/index.ts +22 -0
- package/src/validation/__tests__/object-validation-engine.test.ts +1 -1
- package/src/validation/__tests__/schema-validator.test.ts +118 -0
- package/src/validation/index.ts +1 -1
- package/src/validation/validation-engine.ts +61 -2
- package/src/validation/validators/index.ts +1 -1
- package/src/validation/validators/object-validation-engine.ts +2 -2
- 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
|
|
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';
|
package/dist/validation/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @object-ui/core - Validation Module
|
|
3
3
|
*
|
|
4
4
|
* Phase 3.5: Validation engine
|
|
5
|
-
* ObjectStack Spec
|
|
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 - Object-Level Validation Engine
|
|
10
10
|
*
|
|
11
|
-
* ObjectStack Spec
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
27
|
+
"@objectstack/spec": "^2.0.7",
|
|
27
28
|
"lodash": "^4.17.23",
|
|
28
29
|
"zod": "^4.3.6",
|
|
29
|
-
"@object-ui/types": "0.
|
|
30
|
+
"@object-ui/types": "2.0.0"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"typescript": "^5.9.3",
|