diff --git a/content/docs/references/api/requests/ExportRequest.mdx b/content/docs/references/api/requests/ExportRequest.mdx index 795b07b30..707d85ecc 100644 --- a/content/docs/references/api/requests/ExportRequest.mdx +++ b/content/docs/references/api/requests/ExportRequest.mdx @@ -9,8 +9,14 @@ description: ExportRequest Schema Reference | :--- | :--- | :--- | :--- | | **object** | `string` | ✅ | Object name (e.g. account) | | **fields** | `string \| object[]` | optional | Fields to retrieve | +| **aggregations** | `object[]` | optional | Aggregation functions (GROUP BY) | +| **windowFunctions** | `object[]` | optional | Window functions with OVER clause | | **filters** | `any[] \| string \| any[]` | optional | Filtering criteria | +| **joins** | `object[]` | optional | Table joins | +| **groupBy** | `string[]` | optional | GROUP BY fields | +| **having** | `any[] \| string \| any[]` | optional | HAVING clause for aggregation filtering | | **sort** | `object[]` | optional | Sorting instructions | | **top** | `number` | optional | Limit results | | **skip** | `number` | optional | Offset results | +| **distinct** | `boolean` | optional | SELECT DISTINCT flag | | **format** | `Enum<'csv' \| 'json' \| 'xlsx'>` | optional | | diff --git a/content/docs/references/data/Address.mdx b/content/docs/references/data/Address.mdx new file mode 100644 index 000000000..1889b8213 --- /dev/null +++ b/content/docs/references/data/Address.mdx @@ -0,0 +1,16 @@ +--- +title: Address +description: Address Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **street** | `string` | optional | Street address | +| **city** | `string` | optional | City name | +| **state** | `string` | optional | State/Province | +| **postalCode** | `string` | optional | Postal/ZIP code | +| **country** | `string` | optional | Country name or code | +| **countryCode** | `string` | optional | ISO country code (e.g., US, GB) | +| **formatted** | `string` | optional | Formatted address string | diff --git a/content/docs/references/data/AggregationFunction.mdx b/content/docs/references/data/AggregationFunction.mdx new file mode 100644 index 000000000..9477b17f8 --- /dev/null +++ b/content/docs/references/data/AggregationFunction.mdx @@ -0,0 +1,15 @@ +--- +title: AggregationFunction +description: AggregationFunction Schema Reference +--- + +## Allowed Values + +* `count` +* `sum` +* `avg` +* `min` +* `max` +* `count_distinct` +* `array_agg` +* `string_agg` \ No newline at end of file diff --git a/content/docs/references/data/AggregationNode.mdx b/content/docs/references/data/AggregationNode.mdx new file mode 100644 index 000000000..0c892a534 --- /dev/null +++ b/content/docs/references/data/AggregationNode.mdx @@ -0,0 +1,13 @@ +--- +title: AggregationNode +description: AggregationNode Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **function** | `Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max' \| 'count_distinct' \| 'array_agg' \| 'string_agg'>` | ✅ | Aggregation function | +| **field** | `string` | optional | Field to aggregate (optional for COUNT(*)) | +| **alias** | `string` | ✅ | Result column alias | +| **distinct** | `boolean` | optional | Apply DISTINCT before aggregation | diff --git a/content/docs/references/data/AsyncValidation.mdx b/content/docs/references/data/AsyncValidation.mdx new file mode 100644 index 000000000..7d2c35c66 --- /dev/null +++ b/content/docs/references/data/AsyncValidation.mdx @@ -0,0 +1,20 @@ +--- +title: AsyncValidation +description: AsyncValidation Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Unique rule name | +| **active** | `boolean` | optional | | +| **severity** | `Enum<'error' \| 'warning' \| 'info'>` | optional | | +| **message** | `string` | ✅ | Error message to display | +| **type** | `string` | ✅ | | +| **field** | `string` | ✅ | Field to validate | +| **validatorUrl** | `string` | optional | External API endpoint for validation | +| **validatorFunction** | `string` | optional | Reference to custom validator function | +| **timeout** | `number` | optional | Timeout in milliseconds | +| **debounce** | `number` | optional | Debounce delay in milliseconds | +| **params** | `Record` | optional | Additional parameters to pass to validator | diff --git a/content/docs/references/data/ConditionalValidation.mdx b/content/docs/references/data/ConditionalValidation.mdx new file mode 100644 index 000000000..5fd1f3e45 --- /dev/null +++ b/content/docs/references/data/ConditionalValidation.mdx @@ -0,0 +1,17 @@ +--- +title: ConditionalValidation +description: ConditionalValidation Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Unique rule name | +| **active** | `boolean` | optional | | +| **severity** | `Enum<'error' \| 'warning' \| 'info'>` | optional | | +| **message** | `string` | ✅ | Error message to display | +| **type** | `string` | ✅ | | +| **when** | `string` | ✅ | Condition formula (e.g. "type = 'enterprise'") | +| **then** | `object \| object \| object \| object \| object \| object \| object \| any` | ✅ | Validation rule to apply when condition is true | +| **otherwise** | `object \| object \| object \| object \| object \| object \| object \| any` | optional | Validation rule to apply when condition is false | diff --git a/content/docs/references/data/CrossFieldValidation.mdx b/content/docs/references/data/CrossFieldValidation.mdx new file mode 100644 index 000000000..93c626a52 --- /dev/null +++ b/content/docs/references/data/CrossFieldValidation.mdx @@ -0,0 +1,16 @@ +--- +title: CrossFieldValidation +description: CrossFieldValidation Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Unique rule name | +| **active** | `boolean` | optional | | +| **severity** | `Enum<'error' \| 'warning' \| 'info'>` | optional | | +| **message** | `string` | ✅ | Error message to display | +| **type** | `string` | ✅ | | +| **condition** | `string` | ✅ | Formula expression comparing fields (e.g. "end_date > start_date") | +| **fields** | `string[]` | ✅ | Fields involved in the validation | diff --git a/content/docs/references/data/CustomValidator.mdx b/content/docs/references/data/CustomValidator.mdx new file mode 100644 index 000000000..fea294cff --- /dev/null +++ b/content/docs/references/data/CustomValidator.mdx @@ -0,0 +1,17 @@ +--- +title: CustomValidator +description: CustomValidator Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Unique rule name | +| **active** | `boolean` | optional | | +| **severity** | `Enum<'error' \| 'warning' \| 'info'>` | optional | | +| **message** | `string` | ✅ | Error message to display | +| **type** | `string` | ✅ | | +| **field** | `string` | optional | Field to validate (optional for record-level validation) | +| **validatorFunction** | `string` | ✅ | Function name or reference to custom validator | +| **params** | `Record` | optional | Additional parameters for the validator | diff --git a/content/docs/references/data/JoinNode.mdx b/content/docs/references/data/JoinNode.mdx new file mode 100644 index 000000000..1ee6c2d84 --- /dev/null +++ b/content/docs/references/data/JoinNode.mdx @@ -0,0 +1,14 @@ +--- +title: JoinNode +description: JoinNode Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **type** | `Enum<'inner' \| 'left' \| 'right' \| 'full'>` | ✅ | Join type | +| **object** | `string` | ✅ | Object/table to join | +| **alias** | `string` | optional | Table alias | +| **on** | `any[] \| string \| any[] \| string \| any[][]` | ✅ | Join condition | +| **subquery** | `object` | optional | Subquery instead of object | diff --git a/content/docs/references/data/JoinType.mdx b/content/docs/references/data/JoinType.mdx new file mode 100644 index 000000000..b83d28253 --- /dev/null +++ b/content/docs/references/data/JoinType.mdx @@ -0,0 +1,11 @@ +--- +title: JoinType +description: JoinType Schema Reference +--- + +## Allowed Values + +* `inner` +* `left` +* `right` +* `full` \ No newline at end of file diff --git a/content/docs/references/data/LocationCoordinates.mdx b/content/docs/references/data/LocationCoordinates.mdx new file mode 100644 index 000000000..d450f72c7 --- /dev/null +++ b/content/docs/references/data/LocationCoordinates.mdx @@ -0,0 +1,13 @@ +--- +title: LocationCoordinates +description: LocationCoordinates Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **latitude** | `number` | ✅ | Latitude coordinate | +| **longitude** | `number` | ✅ | Longitude coordinate | +| **altitude** | `number` | optional | Altitude in meters | +| **accuracy** | `number` | optional | Accuracy in meters | diff --git a/content/docs/references/data/WindowFunction.mdx b/content/docs/references/data/WindowFunction.mdx new file mode 100644 index 000000000..598d21b6b --- /dev/null +++ b/content/docs/references/data/WindowFunction.mdx @@ -0,0 +1,20 @@ +--- +title: WindowFunction +description: WindowFunction Schema Reference +--- + +## Allowed Values + +* `row_number` +* `rank` +* `dense_rank` +* `percent_rank` +* `lag` +* `lead` +* `first_value` +* `last_value` +* `sum` +* `avg` +* `count` +* `min` +* `max` \ No newline at end of file diff --git a/content/docs/references/data/WindowFunctionNode.mdx b/content/docs/references/data/WindowFunctionNode.mdx new file mode 100644 index 000000000..c0a9f0e9b --- /dev/null +++ b/content/docs/references/data/WindowFunctionNode.mdx @@ -0,0 +1,13 @@ +--- +title: WindowFunctionNode +description: WindowFunctionNode Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **function** | `Enum<'row_number' \| 'rank' \| 'dense_rank' \| 'percent_rank' \| 'lag' \| 'lead' \| 'first_value' \| 'last_value' \| 'sum' \| 'avg' \| 'count' \| 'min' \| 'max'>` | ✅ | Window function name | +| **field** | `string` | optional | Field to operate on (for aggregate window functions) | +| **alias** | `string` | ✅ | Result column alias | +| **over** | `object` | ✅ | Window specification (OVER clause) | diff --git a/content/docs/references/data/WindowSpec.mdx b/content/docs/references/data/WindowSpec.mdx new file mode 100644 index 000000000..0093f8533 --- /dev/null +++ b/content/docs/references/data/WindowSpec.mdx @@ -0,0 +1,12 @@ +--- +title: WindowSpec +description: WindowSpec Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **partitionBy** | `string[]` | optional | PARTITION BY fields | +| **orderBy** | `object[]` | optional | ORDER BY specification | +| **frame** | `object` | optional | Window frame specification | diff --git a/content/docs/references/data/core/Field.mdx b/content/docs/references/data/core/Field.mdx index 6838ebea7..15f2478bd 100644 --- a/content/docs/references/data/core/Field.mdx +++ b/content/docs/references/data/core/Field.mdx @@ -9,7 +9,7 @@ description: Field Schema Reference | :--- | :--- | :--- | :--- | | **name** | `string` | optional | Machine name (snake_case) | | **label** | `string` | optional | Human readable label | -| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'markdown' \| 'html' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'select' \| 'lookup' \| 'master_detail' \| 'image' \| 'file' \| 'avatar' \| 'formula' \| 'summary' \| 'autonumber'>` | ✅ | Field Data Type | +| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'markdown' \| 'html' \| 'richtext' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'select' \| 'lookup' \| 'master_detail' \| 'image' \| 'file' \| 'avatar' \| 'formula' \| 'summary' \| 'autonumber' \| 'location' \| 'address' \| 'code' \| 'color' \| 'rating' \| 'signature'>` | ✅ | Field Data Type | | **description** | `string` | optional | Tooltip/Help text | | **format** | `string` | optional | Format string (e.g. email, phone) | | **required** | `boolean` | optional | Is required | @@ -31,6 +31,17 @@ description: Field Schema Reference | **expression** | `string` | optional | Formula expression | | **formula** | `string` | optional | Deprecated: Use expression | | **summaryOperations** | `object` | optional | Roll-up summary definition | +| **language** | `string` | optional | Programming language for syntax highlighting (e.g., javascript, python, sql) | +| **theme** | `string` | optional | Code editor theme (e.g., dark, light, monokai) | +| **lineNumbers** | `boolean` | optional | Show line numbers in code editor | +| **maxRating** | `number` | optional | Maximum rating value (default: 5) | +| **allowHalf** | `boolean` | optional | Allow half-star ratings | +| **displayMap** | `boolean` | optional | Display map widget for location field | +| **allowGeocoding** | `boolean` | optional | Allow address-to-coordinate conversion | +| **addressFormat** | `Enum<'us' \| 'uk' \| 'international'>` | optional | Address format template | +| **colorFormat** | `Enum<'hex' \| 'rgb' \| 'rgba' \| 'hsl'>` | optional | Color value format | +| **allowAlpha** | `boolean` | optional | Allow transparency/alpha channel | +| **presetColors** | `string[]` | optional | Preset color options | | **hidden** | `boolean` | optional | Hidden from default UI | | **readonly** | `boolean` | optional | Read-only in UI | | **encryption** | `boolean` | optional | Encrypt at rest | diff --git a/content/docs/references/data/types/FieldType.mdx b/content/docs/references/data/types/FieldType.mdx index 8623caf0d..adbc43f48 100644 --- a/content/docs/references/data/types/FieldType.mdx +++ b/content/docs/references/data/types/FieldType.mdx @@ -13,6 +13,7 @@ description: FieldType Schema Reference * `password` * `markdown` * `html` +* `richtext` * `number` * `currency` * `percent` @@ -28,4 +29,10 @@ description: FieldType Schema Reference * `avatar` * `formula` * `summary` -* `autonumber` \ No newline at end of file +* `autonumber` +* `location` +* `address` +* `code` +* `color` +* `rating` +* `signature` \ No newline at end of file diff --git a/content/docs/references/data/types/Query.mdx b/content/docs/references/data/types/Query.mdx index f8679d322..30a8eb3f7 100644 --- a/content/docs/references/data/types/Query.mdx +++ b/content/docs/references/data/types/Query.mdx @@ -9,7 +9,13 @@ description: Query Schema Reference | :--- | :--- | :--- | :--- | | **object** | `string` | ✅ | Object name (e.g. account) | | **fields** | `string \| object[]` | optional | Fields to retrieve | +| **aggregations** | `object[]` | optional | Aggregation functions (GROUP BY) | +| **windowFunctions** | `object[]` | optional | Window functions with OVER clause | | **filters** | `any[] \| string \| any[]` | optional | Filtering criteria | +| **joins** | `object[]` | optional | Table joins | +| **groupBy** | `string[]` | optional | GROUP BY fields | +| **having** | `any[] \| string \| any[]` | optional | HAVING clause for aggregation filtering | | **sort** | `object[]` | optional | Sorting instructions | | **top** | `number` | optional | Limit results | | **skip** | `number` | optional | Offset results | +| **distinct** | `boolean` | optional | SELECT DISTINCT flag | diff --git a/content/docs/references/ui/Animation.mdx b/content/docs/references/ui/Animation.mdx new file mode 100644 index 000000000..ff48ac9ba --- /dev/null +++ b/content/docs/references/ui/Animation.mdx @@ -0,0 +1,11 @@ +--- +title: Animation +description: Animation Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **duration** | `object` | optional | | +| **timing** | `object` | optional | | diff --git a/content/docs/references/ui/BorderRadius.mdx b/content/docs/references/ui/BorderRadius.mdx new file mode 100644 index 000000000..e4a7dc165 --- /dev/null +++ b/content/docs/references/ui/BorderRadius.mdx @@ -0,0 +1,17 @@ +--- +title: BorderRadius +description: BorderRadius Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **none** | `string` | optional | No border radius (0) | +| **sm** | `string` | optional | Small border radius (e.g., 0.125rem) | +| **base** | `string` | optional | Base border radius (e.g., 0.25rem) | +| **md** | `string` | optional | Medium border radius (e.g., 0.375rem) | +| **lg** | `string` | optional | Large border radius (e.g., 0.5rem) | +| **xl** | `string` | optional | Extra large border radius (e.g., 0.75rem) | +| **2xl** | `string` | optional | 2X large border radius (e.g., 1rem) | +| **full** | `string` | optional | Full border radius (50%) | diff --git a/content/docs/references/ui/Breakpoints.mdx b/content/docs/references/ui/Breakpoints.mdx new file mode 100644 index 000000000..bdbaeed87 --- /dev/null +++ b/content/docs/references/ui/Breakpoints.mdx @@ -0,0 +1,15 @@ +--- +title: Breakpoints +description: Breakpoints Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **xs** | `string` | optional | Extra small breakpoint (e.g., 480px) | +| **sm** | `string` | optional | Small breakpoint (e.g., 640px) | +| **md** | `string` | optional | Medium breakpoint (e.g., 768px) | +| **lg** | `string` | optional | Large breakpoint (e.g., 1024px) | +| **xl** | `string` | optional | Extra large breakpoint (e.g., 1280px) | +| **2xl** | `string` | optional | 2X large breakpoint (e.g., 1536px) | diff --git a/content/docs/references/ui/ColorPalette.mdx b/content/docs/references/ui/ColorPalette.mdx new file mode 100644 index 000000000..346dd8fe4 --- /dev/null +++ b/content/docs/references/ui/ColorPalette.mdx @@ -0,0 +1,26 @@ +--- +title: ColorPalette +description: ColorPalette Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **primary** | `string` | ✅ | Primary brand color (hex, rgb, or hsl) | +| **secondary** | `string` | optional | Secondary brand color | +| **accent** | `string` | optional | Accent color for highlights | +| **success** | `string` | optional | Success state color (default: green) | +| **warning** | `string` | optional | Warning state color (default: yellow) | +| **error** | `string` | optional | Error state color (default: red) | +| **info** | `string` | optional | Info state color (default: blue) | +| **background** | `string` | optional | Background color | +| **surface** | `string` | optional | Surface/card background color | +| **text** | `string` | optional | Primary text color | +| **textSecondary** | `string` | optional | Secondary text color | +| **border** | `string` | optional | Border color | +| **disabled** | `string` | optional | Disabled state color | +| **primaryLight** | `string` | optional | Lighter shade of primary | +| **primaryDark** | `string` | optional | Darker shade of primary | +| **secondaryLight** | `string` | optional | Lighter shade of secondary | +| **secondaryDark** | `string` | optional | Darker shade of secondary | diff --git a/content/docs/references/ui/Shadow.mdx b/content/docs/references/ui/Shadow.mdx new file mode 100644 index 000000000..4fe3c269f --- /dev/null +++ b/content/docs/references/ui/Shadow.mdx @@ -0,0 +1,17 @@ +--- +title: Shadow +description: Shadow Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **none** | `string` | optional | No shadow | +| **sm** | `string` | optional | Small shadow | +| **base** | `string` | optional | Base shadow | +| **md** | `string` | optional | Medium shadow | +| **lg** | `string` | optional | Large shadow | +| **xl** | `string` | optional | Extra large shadow | +| **2xl** | `string` | optional | 2X large shadow | +| **inner** | `string` | optional | Inner shadow (inset) | diff --git a/content/docs/references/ui/Spacing.mdx b/content/docs/references/ui/Spacing.mdx new file mode 100644 index 000000000..127bb81f1 --- /dev/null +++ b/content/docs/references/ui/Spacing.mdx @@ -0,0 +1,22 @@ +--- +title: Spacing +description: Spacing Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **0** | `string` | optional | 0 spacing (0) | +| **1** | `string` | optional | Spacing unit 1 (e.g., 0.25rem) | +| **2** | `string` | optional | Spacing unit 2 (e.g., 0.5rem) | +| **3** | `string` | optional | Spacing unit 3 (e.g., 0.75rem) | +| **4** | `string` | optional | Spacing unit 4 (e.g., 1rem) | +| **5** | `string` | optional | Spacing unit 5 (e.g., 1.25rem) | +| **6** | `string` | optional | Spacing unit 6 (e.g., 1.5rem) | +| **8** | `string` | optional | Spacing unit 8 (e.g., 2rem) | +| **10** | `string` | optional | Spacing unit 10 (e.g., 2.5rem) | +| **12** | `string` | optional | Spacing unit 12 (e.g., 3rem) | +| **16** | `string` | optional | Spacing unit 16 (e.g., 4rem) | +| **20** | `string` | optional | Spacing unit 20 (e.g., 5rem) | +| **24** | `string` | optional | Spacing unit 24 (e.g., 6rem) | diff --git a/content/docs/references/ui/Theme.mdx b/content/docs/references/ui/Theme.mdx new file mode 100644 index 000000000..e1131ecb1 --- /dev/null +++ b/content/docs/references/ui/Theme.mdx @@ -0,0 +1,24 @@ +--- +title: Theme +description: Theme Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **name** | `string` | ✅ | Unique theme identifier (snake_case) | +| **label** | `string` | ✅ | Human-readable theme name | +| **description** | `string` | optional | Theme description | +| **mode** | `Enum<'light' \| 'dark' \| 'auto'>` | optional | Theme mode (light, dark, or auto) | +| **colors** | `object` | ✅ | Color palette configuration | +| **typography** | `object` | optional | Typography settings | +| **spacing** | `object` | optional | Spacing scale | +| **borderRadius** | `object` | optional | Border radius scale | +| **shadows** | `object` | optional | Box shadow effects | +| **breakpoints** | `object` | optional | Responsive breakpoints | +| **animation** | `object` | optional | Animation settings | +| **zIndex** | `object` | optional | Z-index scale for layering | +| **customVars** | `Record` | optional | Custom CSS variables (key-value pairs) | +| **logo** | `object` | optional | Logo assets | +| **extends** | `string` | optional | Base theme to extend from | diff --git a/content/docs/references/ui/ThemeMode.mdx b/content/docs/references/ui/ThemeMode.mdx new file mode 100644 index 000000000..553ecaeb7 --- /dev/null +++ b/content/docs/references/ui/ThemeMode.mdx @@ -0,0 +1,10 @@ +--- +title: ThemeMode +description: ThemeMode Schema Reference +--- + +## Allowed Values + +* `light` +* `dark` +* `auto` \ No newline at end of file diff --git a/content/docs/references/ui/Typography.mdx b/content/docs/references/ui/Typography.mdx new file mode 100644 index 000000000..e04d15c48 --- /dev/null +++ b/content/docs/references/ui/Typography.mdx @@ -0,0 +1,14 @@ +--- +title: Typography +description: Typography Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **fontFamily** | `object` | optional | | +| **fontSize** | `object` | optional | | +| **fontWeight** | `object` | optional | | +| **lineHeight** | `object` | optional | | +| **letterSpacing** | `object` | optional | | diff --git a/content/docs/references/ui/ZIndex.mdx b/content/docs/references/ui/ZIndex.mdx new file mode 100644 index 000000000..1e570762c --- /dev/null +++ b/content/docs/references/ui/ZIndex.mdx @@ -0,0 +1,17 @@ +--- +title: ZIndex +description: ZIndex Schema Reference +--- + +## Properties + +| Property | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| **base** | `number` | optional | Base z-index (e.g., 0) | +| **dropdown** | `number` | optional | Dropdown z-index (e.g., 1000) | +| **sticky** | `number` | optional | Sticky z-index (e.g., 1020) | +| **fixed** | `number` | optional | Fixed z-index (e.g., 1030) | +| **modalBackdrop** | `number` | optional | Modal backdrop z-index (e.g., 1040) | +| **modal** | `number` | optional | Modal z-index (e.g., 1050) | +| **popover** | `number` | optional | Popover z-index (e.g., 1060) | +| **tooltip** | `number` | optional | Tooltip z-index (e.g., 1070) | diff --git a/content/docs/references/ui/interaction/ActionParam.mdx b/content/docs/references/ui/interaction/ActionParam.mdx index 6aa36be35..eb075bf7c 100644 --- a/content/docs/references/ui/interaction/ActionParam.mdx +++ b/content/docs/references/ui/interaction/ActionParam.mdx @@ -9,6 +9,6 @@ description: ActionParam Schema Reference | :--- | :--- | :--- | :--- | | **name** | `string` | ✅ | | | **label** | `string` | ✅ | | -| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'markdown' \| 'html' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'select' \| 'lookup' \| 'master_detail' \| 'image' \| 'file' \| 'avatar' \| 'formula' \| 'summary' \| 'autonumber'>` | ✅ | | +| **type** | `Enum<'text' \| 'textarea' \| 'email' \| 'url' \| 'phone' \| 'password' \| 'markdown' \| 'html' \| 'richtext' \| 'number' \| 'currency' \| 'percent' \| 'date' \| 'datetime' \| 'time' \| 'boolean' \| 'select' \| 'lookup' \| 'master_detail' \| 'image' \| 'file' \| 'avatar' \| 'formula' \| 'summary' \| 'autonumber' \| 'location' \| 'address' \| 'code' \| 'color' \| 'rating' \| 'signature'>` | ✅ | | | **required** | `boolean` | optional | | | **options** | `object[]` | optional | | diff --git a/packages/spec/json-schema/Action.json b/packages/spec/json-schema/Action.json index e34beb439..c03771324 100644 --- a/packages/spec/json-schema/Action.json +++ b/packages/spec/json-schema/Action.json @@ -75,6 +75,7 @@ "password", "markdown", "html", + "richtext", "number", "currency", "percent", @@ -90,7 +91,13 @@ "avatar", "formula", "summary", - "autonumber" + "autonumber", + "location", + "address", + "code", + "color", + "rating", + "signature" ] }, "required": { diff --git a/packages/spec/json-schema/ActionParam.json b/packages/spec/json-schema/ActionParam.json index ebe4affac..5b266dd9e 100644 --- a/packages/spec/json-schema/ActionParam.json +++ b/packages/spec/json-schema/ActionParam.json @@ -21,6 +21,7 @@ "password", "markdown", "html", + "richtext", "number", "currency", "percent", @@ -36,7 +37,13 @@ "avatar", "formula", "summary", - "autonumber" + "autonumber", + "location", + "address", + "code", + "color", + "rating", + "signature" ] }, "required": { diff --git a/packages/spec/json-schema/Address.json b/packages/spec/json-schema/Address.json new file mode 100644 index 000000000..e50cce781 --- /dev/null +++ b/packages/spec/json-schema/Address.json @@ -0,0 +1,40 @@ +{ + "$ref": "#/definitions/Address", + "definitions": { + "Address": { + "type": "object", + "properties": { + "street": { + "type": "string", + "description": "Street address" + }, + "city": { + "type": "string", + "description": "City name" + }, + "state": { + "type": "string", + "description": "State/Province" + }, + "postalCode": { + "type": "string", + "description": "Postal/ZIP code" + }, + "country": { + "type": "string", + "description": "Country name or code" + }, + "countryCode": { + "type": "string", + "description": "ISO country code (e.g., US, GB)" + }, + "formatted": { + "type": "string", + "description": "Formatted address string" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AggregationFunction.json b/packages/spec/json-schema/AggregationFunction.json new file mode 100644 index 000000000..55454e685 --- /dev/null +++ b/packages/spec/json-schema/AggregationFunction.json @@ -0,0 +1,19 @@ +{ + "$ref": "#/definitions/AggregationFunction", + "definitions": { + "AggregationFunction": { + "type": "string", + "enum": [ + "count", + "sum", + "avg", + "min", + "max", + "count_distinct", + "array_agg", + "string_agg" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AggregationNode.json b/packages/spec/json-schema/AggregationNode.json new file mode 100644 index 000000000..4b8161740 --- /dev/null +++ b/packages/spec/json-schema/AggregationNode.json @@ -0,0 +1,42 @@ +{ + "$ref": "#/definitions/AggregationNode", + "definitions": { + "AggregationNode": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "count", + "sum", + "avg", + "min", + "max", + "count_distinct", + "array_agg", + "string_agg" + ], + "description": "Aggregation function" + }, + "field": { + "type": "string", + "description": "Field to aggregate (optional for COUNT(*))" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "distinct": { + "type": "boolean", + "description": "Apply DISTINCT before aggregation" + } + }, + "required": [ + "function", + "alias" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/Animation.json b/packages/spec/json-schema/Animation.json new file mode 100644 index 000000000..ec60c5efb --- /dev/null +++ b/packages/spec/json-schema/Animation.json @@ -0,0 +1,56 @@ +{ + "$ref": "#/definitions/Animation", + "definitions": { + "Animation": { + "type": "object", + "properties": { + "duration": { + "type": "object", + "properties": { + "fast": { + "type": "string", + "description": "Fast animation (e.g., 150ms)" + }, + "base": { + "type": "string", + "description": "Base animation (e.g., 300ms)" + }, + "slow": { + "type": "string", + "description": "Slow animation (e.g., 500ms)" + } + }, + "additionalProperties": false + }, + "timing": { + "type": "object", + "properties": { + "linear": { + "type": "string", + "description": "Linear timing function" + }, + "ease": { + "type": "string", + "description": "Ease timing function" + }, + "easeIn": { + "type": "string", + "description": "Ease-in timing function" + }, + "easeOut": { + "type": "string", + "description": "Ease-out timing function" + }, + "easeInOut": { + "type": "string", + "description": "Ease-in-out timing function" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/AsyncValidation.json b/packages/spec/json-schema/AsyncValidation.json new file mode 100644 index 000000000..41aaf737a --- /dev/null +++ b/packages/spec/json-schema/AsyncValidation.json @@ -0,0 +1,70 @@ +{ + "$ref": "#/definitions/AsyncValidation", + "definitions": { + "AsyncValidation": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "async" + }, + "field": { + "type": "string", + "description": "Field to validate" + }, + "validatorUrl": { + "type": "string", + "description": "External API endpoint for validation" + }, + "validatorFunction": { + "type": "string", + "description": "Reference to custom validator function" + }, + "timeout": { + "type": "number", + "default": 5000, + "description": "Timeout in milliseconds" + }, + "debounce": { + "type": "number", + "description": "Debounce delay in milliseconds" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters to pass to validator" + } + }, + "required": [ + "name", + "message", + "type", + "field" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/BorderRadius.json b/packages/spec/json-schema/BorderRadius.json new file mode 100644 index 000000000..5c35832e4 --- /dev/null +++ b/packages/spec/json-schema/BorderRadius.json @@ -0,0 +1,44 @@ +{ + "$ref": "#/definitions/BorderRadius", + "definitions": { + "BorderRadius": { + "type": "object", + "properties": { + "none": { + "type": "string", + "description": "No border radius (0)" + }, + "sm": { + "type": "string", + "description": "Small border radius (e.g., 0.125rem)" + }, + "base": { + "type": "string", + "description": "Base border radius (e.g., 0.25rem)" + }, + "md": { + "type": "string", + "description": "Medium border radius (e.g., 0.375rem)" + }, + "lg": { + "type": "string", + "description": "Large border radius (e.g., 0.5rem)" + }, + "xl": { + "type": "string", + "description": "Extra large border radius (e.g., 0.75rem)" + }, + "2xl": { + "type": "string", + "description": "2X large border radius (e.g., 1rem)" + }, + "full": { + "type": "string", + "description": "Full border radius (50%)" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/Breakpoints.json b/packages/spec/json-schema/Breakpoints.json new file mode 100644 index 000000000..772b21185 --- /dev/null +++ b/packages/spec/json-schema/Breakpoints.json @@ -0,0 +1,36 @@ +{ + "$ref": "#/definitions/Breakpoints", + "definitions": { + "Breakpoints": { + "type": "object", + "properties": { + "xs": { + "type": "string", + "description": "Extra small breakpoint (e.g., 480px)" + }, + "sm": { + "type": "string", + "description": "Small breakpoint (e.g., 640px)" + }, + "md": { + "type": "string", + "description": "Medium breakpoint (e.g., 768px)" + }, + "lg": { + "type": "string", + "description": "Large breakpoint (e.g., 1024px)" + }, + "xl": { + "type": "string", + "description": "Extra large breakpoint (e.g., 1280px)" + }, + "2xl": { + "type": "string", + "description": "2X large breakpoint (e.g., 1536px)" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ColorPalette.json b/packages/spec/json-schema/ColorPalette.json new file mode 100644 index 000000000..bd15708c1 --- /dev/null +++ b/packages/spec/json-schema/ColorPalette.json @@ -0,0 +1,83 @@ +{ + "$ref": "#/definitions/ColorPalette", + "definitions": { + "ColorPalette": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "Primary brand color (hex, rgb, or hsl)" + }, + "secondary": { + "type": "string", + "description": "Secondary brand color" + }, + "accent": { + "type": "string", + "description": "Accent color for highlights" + }, + "success": { + "type": "string", + "description": "Success state color (default: green)" + }, + "warning": { + "type": "string", + "description": "Warning state color (default: yellow)" + }, + "error": { + "type": "string", + "description": "Error state color (default: red)" + }, + "info": { + "type": "string", + "description": "Info state color (default: blue)" + }, + "background": { + "type": "string", + "description": "Background color" + }, + "surface": { + "type": "string", + "description": "Surface/card background color" + }, + "text": { + "type": "string", + "description": "Primary text color" + }, + "textSecondary": { + "type": "string", + "description": "Secondary text color" + }, + "border": { + "type": "string", + "description": "Border color" + }, + "disabled": { + "type": "string", + "description": "Disabled state color" + }, + "primaryLight": { + "type": "string", + "description": "Lighter shade of primary" + }, + "primaryDark": { + "type": "string", + "description": "Darker shade of primary" + }, + "secondaryLight": { + "type": "string", + "description": "Lighter shade of secondary" + }, + "secondaryDark": { + "type": "string", + "description": "Darker shade of secondary" + } + }, + "required": [ + "primary" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ConditionalValidation.json b/packages/spec/json-schema/ConditionalValidation.json new file mode 100644 index 000000000..2920bed32 --- /dev/null +++ b/packages/spec/json-schema/ConditionalValidation.json @@ -0,0 +1,793 @@ +{ + "$ref": "#/definitions/ConditionalValidation", + "definitions": { + "ConditionalValidation": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "conditional" + }, + "when": { + "type": "string", + "description": "Condition formula (e.g. \"type = 'enterprise'\")" + }, + "then": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "script" + }, + "condition": { + "type": "string", + "description": "Formula expression. If TRUE, validation fails. (e.g. amount < 0)" + } + }, + "required": [ + "name", + "message", + "type", + "condition" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "unique" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Fields that must be combined unique" + }, + "scope": { + "type": "string", + "description": "Formula condition for scope (e.g. active = true)" + }, + "caseSensitive": { + "type": "boolean", + "default": true + } + }, + "required": [ + "name", + "message", + "type", + "fields" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "state_machine" + }, + "field": { + "type": "string", + "description": "State field (e.g. status)" + }, + "transitions": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Map of { OldState: [AllowedNewStates] }" + } + }, + "required": [ + "name", + "message", + "type", + "field", + "transitions" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "format" + }, + "field": { + "type": "string" + }, + "regex": { + "type": "string" + }, + "format": { + "type": "string", + "enum": [ + "email", + "url", + "phone", + "json" + ] + } + }, + "required": [ + "name", + "message", + "type", + "field" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "cross_field" + }, + "condition": { + "type": "string", + "description": "Formula expression comparing fields (e.g. \"end_date > start_date\")" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Fields involved in the validation" + } + }, + "required": [ + "name", + "message", + "type", + "condition", + "fields" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "async" + }, + "field": { + "type": "string", + "description": "Field to validate" + }, + "validatorUrl": { + "type": "string", + "description": "External API endpoint for validation" + }, + "validatorFunction": { + "type": "string", + "description": "Reference to custom validator function" + }, + "timeout": { + "type": "number", + "default": 5000, + "description": "Timeout in milliseconds" + }, + "debounce": { + "type": "number", + "description": "Debounce delay in milliseconds" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters to pass to validator" + } + }, + "required": [ + "name", + "message", + "type", + "field" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "custom" + }, + "field": { + "type": "string", + "description": "Field to validate (optional for record-level validation)" + }, + "validatorFunction": { + "type": "string", + "description": "Function name or reference to custom validator" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters for the validator" + } + }, + "required": [ + "name", + "message", + "type", + "validatorFunction" + ], + "additionalProperties": false + }, + {} + ], + "description": "Validation rule to apply when condition is true" + }, + "otherwise": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "script" + }, + "condition": { + "type": "string", + "description": "Formula expression. If TRUE, validation fails. (e.g. amount < 0)" + } + }, + "required": [ + "name", + "message", + "type", + "condition" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "unique" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Fields that must be combined unique" + }, + "scope": { + "type": "string", + "description": "Formula condition for scope (e.g. active = true)" + }, + "caseSensitive": { + "type": "boolean", + "default": true + } + }, + "required": [ + "name", + "message", + "type", + "fields" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "state_machine" + }, + "field": { + "type": "string", + "description": "State field (e.g. status)" + }, + "transitions": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Map of { OldState: [AllowedNewStates] }" + } + }, + "required": [ + "name", + "message", + "type", + "field", + "transitions" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "format" + }, + "field": { + "type": "string" + }, + "regex": { + "type": "string" + }, + "format": { + "type": "string", + "enum": [ + "email", + "url", + "phone", + "json" + ] + } + }, + "required": [ + "name", + "message", + "type", + "field" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "cross_field" + }, + "condition": { + "type": "string", + "description": "Formula expression comparing fields (e.g. \"end_date > start_date\")" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Fields involved in the validation" + } + }, + "required": [ + "name", + "message", + "type", + "condition", + "fields" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "async" + }, + "field": { + "type": "string", + "description": "Field to validate" + }, + "validatorUrl": { + "type": "string", + "description": "External API endpoint for validation" + }, + "validatorFunction": { + "type": "string", + "description": "Reference to custom validator function" + }, + "timeout": { + "type": "number", + "default": 5000, + "description": "Timeout in milliseconds" + }, + "debounce": { + "type": "number", + "description": "Debounce delay in milliseconds" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters to pass to validator" + } + }, + "required": [ + "name", + "message", + "type", + "field" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "custom" + }, + "field": { + "type": "string", + "description": "Field to validate (optional for record-level validation)" + }, + "validatorFunction": { + "type": "string", + "description": "Function name or reference to custom validator" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters for the validator" + } + }, + "required": [ + "name", + "message", + "type", + "validatorFunction" + ], + "additionalProperties": false + }, + {} + ], + "description": "Validation rule to apply when condition is false" + } + }, + "required": [ + "name", + "message", + "type", + "when", + "then" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CrossFieldValidation.json b/packages/spec/json-schema/CrossFieldValidation.json new file mode 100644 index 000000000..cd3044f27 --- /dev/null +++ b/packages/spec/json-schema/CrossFieldValidation.json @@ -0,0 +1,56 @@ +{ + "$ref": "#/definitions/CrossFieldValidation", + "definitions": { + "CrossFieldValidation": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "cross_field" + }, + "condition": { + "type": "string", + "description": "Formula expression comparing fields (e.g. \"end_date > start_date\")" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Fields involved in the validation" + } + }, + "required": [ + "name", + "message", + "type", + "condition", + "fields" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/CustomValidator.json b/packages/spec/json-schema/CustomValidator.json new file mode 100644 index 000000000..cfd9dd225 --- /dev/null +++ b/packages/spec/json-schema/CustomValidator.json @@ -0,0 +1,57 @@ +{ + "$ref": "#/definitions/CustomValidator", + "definitions": { + "CustomValidator": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "custom" + }, + "field": { + "type": "string", + "description": "Field to validate (optional for record-level validation)" + }, + "validatorFunction": { + "type": "string", + "description": "Function name or reference to custom validator" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters for the validator" + } + }, + "required": [ + "name", + "message", + "type", + "validatorFunction" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ExportRequest.json b/packages/spec/json-schema/ExportRequest.json index 94185da1f..4ad7adce1 100644 --- a/packages/spec/json-schema/ExportRequest.json +++ b/packages/spec/json-schema/ExportRequest.json @@ -38,6 +38,148 @@ }, "description": "Fields to retrieve" }, + "aggregations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "count", + "sum", + "avg", + "min", + "max", + "count_distinct", + "array_agg", + "string_agg" + ], + "description": "Aggregation function" + }, + "field": { + "type": "string", + "description": "Field to aggregate (optional for COUNT(*))" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "distinct": { + "type": "boolean", + "description": "Apply DISTINCT before aggregation" + } + }, + "required": [ + "function", + "alias" + ], + "additionalProperties": false + }, + "description": "Aggregation functions (GROUP BY)" + }, + "windowFunctions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "row_number", + "rank", + "dense_rank", + "percent_rank", + "lag", + "lead", + "first_value", + "last_value", + "sum", + "avg", + "count", + "min", + "max" + ], + "description": "Window function name" + }, + "field": { + "type": "string", + "description": "Field to operate on (for aggregate window functions)" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "over": { + "type": "object", + "properties": { + "partitionBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "PARTITION BY fields" + }, + "orderBy": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "ORDER BY specification" + }, + "frame": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "rows", + "range" + ] + }, + "start": { + "type": "string", + "description": "Frame start (e.g., \"UNBOUNDED PRECEDING\", \"1 PRECEDING\")" + }, + "end": { + "type": "string", + "description": "Frame end (e.g., \"CURRENT ROW\", \"1 FOLLOWING\")" + } + }, + "additionalProperties": false, + "description": "Window frame specification" + } + }, + "additionalProperties": false, + "description": "Window specification (OVER clause)" + } + }, + "required": [ + "function", + "alias", + "over" + ], + "additionalProperties": false + }, + "description": "Window functions with OVER clause" + }, "filters": { "anyOf": [ { @@ -85,6 +227,509 @@ ], "description": "Filtering criteria" }, + "joins": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "inner", + "left", + "right", + "full" + ], + "description": "Join type" + }, + "object": { + "type": "string", + "description": "Object/table to join" + }, + "alias": { + "type": "string", + "description": "Table alias" + }, + "on": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ] + } + ] + } + } + ], + "description": "Join condition" + }, + "subquery": { + "type": "object", + "properties": { + "object": { + "type": "string", + "description": "Object name (e.g. account)" + }, + "fields": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "fields": { + "type": "array", + "items": {} + }, + "alias": { + "type": "string" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + } + ] + }, + "description": "Fields to retrieve" + }, + "aggregations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "count", + "sum", + "avg", + "min", + "max", + "count_distinct", + "array_agg", + "string_agg" + ], + "description": "Aggregation function" + }, + "field": { + "type": "string", + "description": "Field to aggregate (optional for COUNT(*))" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "distinct": { + "type": "boolean", + "description": "Apply DISTINCT before aggregation" + } + }, + "required": [ + "function", + "alias" + ], + "additionalProperties": false + }, + "description": "Aggregation functions (GROUP BY)" + }, + "windowFunctions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "row_number", + "rank", + "dense_rank", + "percent_rank", + "lag", + "lead", + "first_value", + "last_value", + "sum", + "avg", + "count", + "min", + "max" + ], + "description": "Window function name" + }, + "field": { + "type": "string", + "description": "Field to operate on (for aggregate window functions)" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "over": { + "type": "object", + "properties": { + "partitionBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "PARTITION BY fields" + }, + "orderBy": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "ORDER BY specification" + }, + "frame": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "rows", + "range" + ] + }, + "start": { + "type": "string", + "description": "Frame start (e.g., \"UNBOUNDED PRECEDING\", \"1 PRECEDING\")" + }, + "end": { + "type": "string", + "description": "Frame end (e.g., \"CURRENT ROW\", \"1 FOLLOWING\")" + } + }, + "additionalProperties": false, + "description": "Window frame specification" + } + }, + "additionalProperties": false, + "description": "Window specification (OVER clause)" + } + }, + "required": [ + "function", + "alias", + "over" + ], + "additionalProperties": false + }, + "description": "Window functions with OVER clause" + }, + "filters": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ], + "description": "Filtering criteria" + }, + "joins": {}, + "groupBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "GROUP BY fields" + }, + "having": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ], + "description": "HAVING clause for aggregation filtering" + }, + "sort": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "Sorting instructions" + }, + "top": { + "type": "number", + "description": "Limit results" + }, + "skip": { + "type": "number", + "description": "Offset results" + }, + "distinct": { + "type": "boolean", + "description": "SELECT DISTINCT flag" + } + }, + "required": [ + "object" + ], + "additionalProperties": false, + "description": "Subquery instead of object" + } + }, + "required": [ + "type", + "object", + "on" + ], + "additionalProperties": false + }, + "description": "Table joins" + }, + "groupBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "GROUP BY fields" + }, + "having": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ], + "description": "HAVING clause for aggregation filtering" + }, "sort": { "type": "array", "items": { @@ -117,6 +762,10 @@ "type": "number", "description": "Offset results" }, + "distinct": { + "type": "boolean", + "description": "SELECT DISTINCT flag" + }, "format": { "type": "string", "enum": [ diff --git a/packages/spec/json-schema/Field.json b/packages/spec/json-schema/Field.json index afb62cbb8..6ba1736f7 100644 --- a/packages/spec/json-schema/Field.json +++ b/packages/spec/json-schema/Field.json @@ -24,6 +24,7 @@ "password", "markdown", "html", + "richtext", "number", "currency", "percent", @@ -39,7 +40,13 @@ "avatar", "formula", "summary", - "autonumber" + "autonumber", + "location", + "address", + "code", + "color", + "rating", + "signature" ], "description": "Field Data Type" }, @@ -189,6 +196,64 @@ "additionalProperties": false, "description": "Roll-up summary definition" }, + "language": { + "type": "string", + "description": "Programming language for syntax highlighting (e.g., javascript, python, sql)" + }, + "theme": { + "type": "string", + "description": "Code editor theme (e.g., dark, light, monokai)" + }, + "lineNumbers": { + "type": "boolean", + "description": "Show line numbers in code editor" + }, + "maxRating": { + "type": "number", + "description": "Maximum rating value (default: 5)" + }, + "allowHalf": { + "type": "boolean", + "description": "Allow half-star ratings" + }, + "displayMap": { + "type": "boolean", + "description": "Display map widget for location field" + }, + "allowGeocoding": { + "type": "boolean", + "description": "Allow address-to-coordinate conversion" + }, + "addressFormat": { + "type": "string", + "enum": [ + "us", + "uk", + "international" + ], + "description": "Address format template" + }, + "colorFormat": { + "type": "string", + "enum": [ + "hex", + "rgb", + "rgba", + "hsl" + ], + "description": "Color value format" + }, + "allowAlpha": { + "type": "boolean", + "description": "Allow transparency/alpha channel" + }, + "presetColors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Preset color options" + }, "hidden": { "type": "boolean", "default": false, diff --git a/packages/spec/json-schema/FieldType.json b/packages/spec/json-schema/FieldType.json index ca1550e91..0595f463c 100644 --- a/packages/spec/json-schema/FieldType.json +++ b/packages/spec/json-schema/FieldType.json @@ -12,6 +12,7 @@ "password", "markdown", "html", + "richtext", "number", "currency", "percent", @@ -27,7 +28,13 @@ "avatar", "formula", "summary", - "autonumber" + "autonumber", + "location", + "address", + "code", + "color", + "rating", + "signature" ] } }, diff --git a/packages/spec/json-schema/FieldWidgetProps.json b/packages/spec/json-schema/FieldWidgetProps.json index ab78a1016..9ec1f9418 100644 --- a/packages/spec/json-schema/FieldWidgetProps.json +++ b/packages/spec/json-schema/FieldWidgetProps.json @@ -44,6 +44,7 @@ "password", "markdown", "html", + "richtext", "number", "currency", "percent", @@ -59,7 +60,13 @@ "avatar", "formula", "summary", - "autonumber" + "autonumber", + "location", + "address", + "code", + "color", + "rating", + "signature" ], "description": "Field Data Type" }, @@ -209,6 +216,64 @@ "additionalProperties": false, "description": "Roll-up summary definition" }, + "language": { + "type": "string", + "description": "Programming language for syntax highlighting (e.g., javascript, python, sql)" + }, + "theme": { + "type": "string", + "description": "Code editor theme (e.g., dark, light, monokai)" + }, + "lineNumbers": { + "type": "boolean", + "description": "Show line numbers in code editor" + }, + "maxRating": { + "type": "number", + "description": "Maximum rating value (default: 5)" + }, + "allowHalf": { + "type": "boolean", + "description": "Allow half-star ratings" + }, + "displayMap": { + "type": "boolean", + "description": "Display map widget for location field" + }, + "allowGeocoding": { + "type": "boolean", + "description": "Allow address-to-coordinate conversion" + }, + "addressFormat": { + "type": "string", + "enum": [ + "us", + "uk", + "international" + ], + "description": "Address format template" + }, + "colorFormat": { + "type": "string", + "enum": [ + "hex", + "rgb", + "rgba", + "hsl" + ], + "description": "Color value format" + }, + "allowAlpha": { + "type": "boolean", + "description": "Allow transparency/alpha channel" + }, + "presetColors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Preset color options" + }, "hidden": { "type": "boolean", "default": false, diff --git a/packages/spec/json-schema/JoinNode.json b/packages/spec/json-schema/JoinNode.json new file mode 100644 index 000000000..b3ef86731 --- /dev/null +++ b/packages/spec/json-schema/JoinNode.json @@ -0,0 +1,455 @@ +{ + "$ref": "#/definitions/JoinNode", + "definitions": { + "JoinNode": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "inner", + "left", + "right", + "full" + ], + "description": "Join type" + }, + "object": { + "type": "string", + "description": "Object/table to join" + }, + "alias": { + "type": "string", + "description": "Table alias" + }, + "on": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ] + } + ] + } + } + ], + "description": "Join condition" + }, + "subquery": { + "type": "object", + "properties": { + "object": { + "type": "string", + "description": "Object name (e.g. account)" + }, + "fields": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "fields": { + "type": "array", + "items": {} + }, + "alias": { + "type": "string" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + } + ] + }, + "description": "Fields to retrieve" + }, + "aggregations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "count", + "sum", + "avg", + "min", + "max", + "count_distinct", + "array_agg", + "string_agg" + ], + "description": "Aggregation function" + }, + "field": { + "type": "string", + "description": "Field to aggregate (optional for COUNT(*))" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "distinct": { + "type": "boolean", + "description": "Apply DISTINCT before aggregation" + } + }, + "required": [ + "function", + "alias" + ], + "additionalProperties": false + }, + "description": "Aggregation functions (GROUP BY)" + }, + "windowFunctions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "row_number", + "rank", + "dense_rank", + "percent_rank", + "lag", + "lead", + "first_value", + "last_value", + "sum", + "avg", + "count", + "min", + "max" + ], + "description": "Window function name" + }, + "field": { + "type": "string", + "description": "Field to operate on (for aggregate window functions)" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "over": { + "type": "object", + "properties": { + "partitionBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "PARTITION BY fields" + }, + "orderBy": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "ORDER BY specification" + }, + "frame": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "rows", + "range" + ] + }, + "start": { + "type": "string", + "description": "Frame start (e.g., \"UNBOUNDED PRECEDING\", \"1 PRECEDING\")" + }, + "end": { + "type": "string", + "description": "Frame end (e.g., \"CURRENT ROW\", \"1 FOLLOWING\")" + } + }, + "additionalProperties": false, + "description": "Window frame specification" + } + }, + "additionalProperties": false, + "description": "Window specification (OVER clause)" + } + }, + "required": [ + "function", + "alias", + "over" + ], + "additionalProperties": false + }, + "description": "Window functions with OVER clause" + }, + "filters": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ], + "description": "Filtering criteria" + }, + "joins": { + "type": "array", + "items": {}, + "description": "Table joins" + }, + "groupBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "GROUP BY fields" + }, + "having": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ], + "description": "HAVING clause for aggregation filtering" + }, + "sort": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "Sorting instructions" + }, + "top": { + "type": "number", + "description": "Limit results" + }, + "skip": { + "type": "number", + "description": "Offset results" + }, + "distinct": { + "type": "boolean", + "description": "SELECT DISTINCT flag" + } + }, + "required": [ + "object" + ], + "additionalProperties": false, + "description": "Subquery instead of object" + } + }, + "required": [ + "type", + "object", + "on" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/JoinType.json b/packages/spec/json-schema/JoinType.json new file mode 100644 index 000000000..e8eb5f9b6 --- /dev/null +++ b/packages/spec/json-schema/JoinType.json @@ -0,0 +1,15 @@ +{ + "$ref": "#/definitions/JoinType", + "definitions": { + "JoinType": { + "type": "string", + "enum": [ + "inner", + "left", + "right", + "full" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/LocationCoordinates.json b/packages/spec/json-schema/LocationCoordinates.json new file mode 100644 index 000000000..20fda6b53 --- /dev/null +++ b/packages/spec/json-schema/LocationCoordinates.json @@ -0,0 +1,36 @@ +{ + "$ref": "#/definitions/LocationCoordinates", + "definitions": { + "LocationCoordinates": { + "type": "object", + "properties": { + "latitude": { + "type": "number", + "minimum": -90, + "maximum": 90, + "description": "Latitude coordinate" + }, + "longitude": { + "type": "number", + "minimum": -180, + "maximum": 180, + "description": "Longitude coordinate" + }, + "altitude": { + "type": "number", + "description": "Altitude in meters" + }, + "accuracy": { + "type": "number", + "description": "Accuracy in meters" + } + }, + "required": [ + "latitude", + "longitude" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/Mapping.json b/packages/spec/json-schema/Mapping.json index 75da71ab5..0c96e0dc4 100644 --- a/packages/spec/json-schema/Mapping.json +++ b/packages/spec/json-schema/Mapping.json @@ -158,6 +158,148 @@ }, "description": "Fields to retrieve" }, + "aggregations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "count", + "sum", + "avg", + "min", + "max", + "count_distinct", + "array_agg", + "string_agg" + ], + "description": "Aggregation function" + }, + "field": { + "type": "string", + "description": "Field to aggregate (optional for COUNT(*))" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "distinct": { + "type": "boolean", + "description": "Apply DISTINCT before aggregation" + } + }, + "required": [ + "function", + "alias" + ], + "additionalProperties": false + }, + "description": "Aggregation functions (GROUP BY)" + }, + "windowFunctions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "row_number", + "rank", + "dense_rank", + "percent_rank", + "lag", + "lead", + "first_value", + "last_value", + "sum", + "avg", + "count", + "min", + "max" + ], + "description": "Window function name" + }, + "field": { + "type": "string", + "description": "Field to operate on (for aggregate window functions)" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "over": { + "type": "object", + "properties": { + "partitionBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "PARTITION BY fields" + }, + "orderBy": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "ORDER BY specification" + }, + "frame": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "rows", + "range" + ] + }, + "start": { + "type": "string", + "description": "Frame start (e.g., \"UNBOUNDED PRECEDING\", \"1 PRECEDING\")" + }, + "end": { + "type": "string", + "description": "Frame end (e.g., \"CURRENT ROW\", \"1 FOLLOWING\")" + } + }, + "additionalProperties": false, + "description": "Window frame specification" + } + }, + "additionalProperties": false, + "description": "Window specification (OVER clause)" + } + }, + "required": [ + "function", + "alias", + "over" + ], + "additionalProperties": false + }, + "description": "Window functions with OVER clause" + }, "filters": { "anyOf": [ { @@ -205,6 +347,188 @@ ], "description": "Filtering criteria" }, + "joins": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "inner", + "left", + "right", + "full" + ], + "description": "Join type" + }, + "object": { + "type": "string", + "description": "Object/table to join" + }, + "alias": { + "type": "string", + "description": "Table alias" + }, + "on": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ] + } + ] + } + } + ], + "description": "Join condition" + }, + "subquery": { + "description": "Subquery instead of object" + } + }, + "required": [ + "type", + "object", + "on" + ], + "additionalProperties": false + }, + "description": "Table joins" + }, + "groupBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "GROUP BY fields" + }, + "having": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ], + "description": "HAVING clause for aggregation filtering" + }, "sort": { "type": "array", "items": { @@ -236,6 +560,10 @@ "skip": { "type": "number", "description": "Offset results" + }, + "distinct": { + "type": "boolean", + "description": "SELECT DISTINCT flag" } }, "required": [ diff --git a/packages/spec/json-schema/Object.json b/packages/spec/json-schema/Object.json index 36e7f77ea..4c474e5f4 100644 --- a/packages/spec/json-schema/Object.json +++ b/packages/spec/json-schema/Object.json @@ -64,6 +64,7 @@ "password", "markdown", "html", + "richtext", "number", "currency", "percent", @@ -79,7 +80,13 @@ "avatar", "formula", "summary", - "autonumber" + "autonumber", + "location", + "address", + "code", + "color", + "rating", + "signature" ], "description": "Field Data Type" }, @@ -229,6 +236,64 @@ "additionalProperties": false, "description": "Roll-up summary definition" }, + "language": { + "type": "string", + "description": "Programming language for syntax highlighting (e.g., javascript, python, sql)" + }, + "theme": { + "type": "string", + "description": "Code editor theme (e.g., dark, light, monokai)" + }, + "lineNumbers": { + "type": "boolean", + "description": "Show line numbers in code editor" + }, + "maxRating": { + "type": "number", + "description": "Maximum rating value (default: 5)" + }, + "allowHalf": { + "type": "boolean", + "description": "Allow half-star ratings" + }, + "displayMap": { + "type": "boolean", + "description": "Display map widget for location field" + }, + "allowGeocoding": { + "type": "boolean", + "description": "Allow address-to-coordinate conversion" + }, + "addressFormat": { + "type": "string", + "enum": [ + "us", + "uk", + "international" + ], + "description": "Address format template" + }, + "colorFormat": { + "type": "string", + "enum": [ + "hex", + "rgb", + "rgba", + "hsl" + ], + "description": "Color value format" + }, + "allowAlpha": { + "type": "boolean", + "description": "Allow transparency/alpha channel" + }, + "presetColors": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Preset color options" + }, "hidden": { "type": "boolean", "default": false, diff --git a/packages/spec/json-schema/Query.json b/packages/spec/json-schema/Query.json index 5037170e5..e690ae9d1 100644 --- a/packages/spec/json-schema/Query.json +++ b/packages/spec/json-schema/Query.json @@ -38,6 +38,148 @@ }, "description": "Fields to retrieve" }, + "aggregations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "count", + "sum", + "avg", + "min", + "max", + "count_distinct", + "array_agg", + "string_agg" + ], + "description": "Aggregation function" + }, + "field": { + "type": "string", + "description": "Field to aggregate (optional for COUNT(*))" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "distinct": { + "type": "boolean", + "description": "Apply DISTINCT before aggregation" + } + }, + "required": [ + "function", + "alias" + ], + "additionalProperties": false + }, + "description": "Aggregation functions (GROUP BY)" + }, + "windowFunctions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "row_number", + "rank", + "dense_rank", + "percent_rank", + "lag", + "lead", + "first_value", + "last_value", + "sum", + "avg", + "count", + "min", + "max" + ], + "description": "Window function name" + }, + "field": { + "type": "string", + "description": "Field to operate on (for aggregate window functions)" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "over": { + "type": "object", + "properties": { + "partitionBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "PARTITION BY fields" + }, + "orderBy": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "ORDER BY specification" + }, + "frame": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "rows", + "range" + ] + }, + "start": { + "type": "string", + "description": "Frame start (e.g., \"UNBOUNDED PRECEDING\", \"1 PRECEDING\")" + }, + "end": { + "type": "string", + "description": "Frame end (e.g., \"CURRENT ROW\", \"1 FOLLOWING\")" + } + }, + "additionalProperties": false, + "description": "Window frame specification" + } + }, + "additionalProperties": false, + "description": "Window specification (OVER clause)" + } + }, + "required": [ + "function", + "alias", + "over" + ], + "additionalProperties": false + }, + "description": "Window functions with OVER clause" + }, "filters": { "anyOf": [ { @@ -85,6 +227,188 @@ ], "description": "Filtering criteria" }, + "joins": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "inner", + "left", + "right", + "full" + ], + "description": "Join type" + }, + "object": { + "type": "string", + "description": "Object/table to join" + }, + "alias": { + "type": "string", + "description": "Table alias" + }, + "on": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ] + } + ] + } + } + ], + "description": "Join condition" + }, + "subquery": { + "description": "Subquery instead of object" + } + }, + "required": [ + "type", + "object", + "on" + ], + "additionalProperties": false + }, + "description": "Table joins" + }, + "groupBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "GROUP BY fields" + }, + "having": { + "anyOf": [ + { + "type": "array", + "minItems": 3, + "maxItems": 3, + "items": [ + { + "type": "string" + }, + { + "type": "string", + "enum": [ + "=", + "!=", + "<>", + ">", + ">=", + "<", + "<=", + "startswith", + "contains", + "notcontains", + "between", + "in", + "notin", + "is_null", + "is_not_null" + ] + }, + {} + ] + }, + { + "type": "array", + "items": { + "anyOf": [ + { + "type": "string" + }, + {} + ] + } + } + ], + "description": "HAVING clause for aggregation filtering" + }, "sort": { "type": "array", "items": { @@ -116,6 +440,10 @@ "skip": { "type": "number", "description": "Offset results" + }, + "distinct": { + "type": "boolean", + "description": "SELECT DISTINCT flag" } }, "required": [ diff --git a/packages/spec/json-schema/Shadow.json b/packages/spec/json-schema/Shadow.json new file mode 100644 index 000000000..30b96ea17 --- /dev/null +++ b/packages/spec/json-schema/Shadow.json @@ -0,0 +1,44 @@ +{ + "$ref": "#/definitions/Shadow", + "definitions": { + "Shadow": { + "type": "object", + "properties": { + "none": { + "type": "string", + "description": "No shadow" + }, + "sm": { + "type": "string", + "description": "Small shadow" + }, + "base": { + "type": "string", + "description": "Base shadow" + }, + "md": { + "type": "string", + "description": "Medium shadow" + }, + "lg": { + "type": "string", + "description": "Large shadow" + }, + "xl": { + "type": "string", + "description": "Extra large shadow" + }, + "2xl": { + "type": "string", + "description": "2X large shadow" + }, + "inner": { + "type": "string", + "description": "Inner shadow (inset)" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/Spacing.json b/packages/spec/json-schema/Spacing.json new file mode 100644 index 000000000..9d70891f8 --- /dev/null +++ b/packages/spec/json-schema/Spacing.json @@ -0,0 +1,64 @@ +{ + "$ref": "#/definitions/Spacing", + "definitions": { + "Spacing": { + "type": "object", + "properties": { + "0": { + "type": "string", + "description": "0 spacing (0)" + }, + "1": { + "type": "string", + "description": "Spacing unit 1 (e.g., 0.25rem)" + }, + "2": { + "type": "string", + "description": "Spacing unit 2 (e.g., 0.5rem)" + }, + "3": { + "type": "string", + "description": "Spacing unit 3 (e.g., 0.75rem)" + }, + "4": { + "type": "string", + "description": "Spacing unit 4 (e.g., 1rem)" + }, + "5": { + "type": "string", + "description": "Spacing unit 5 (e.g., 1.25rem)" + }, + "6": { + "type": "string", + "description": "Spacing unit 6 (e.g., 1.5rem)" + }, + "8": { + "type": "string", + "description": "Spacing unit 8 (e.g., 2rem)" + }, + "10": { + "type": "string", + "description": "Spacing unit 10 (e.g., 2.5rem)" + }, + "12": { + "type": "string", + "description": "Spacing unit 12 (e.g., 3rem)" + }, + "16": { + "type": "string", + "description": "Spacing unit 16 (e.g., 4rem)" + }, + "20": { + "type": "string", + "description": "Spacing unit 20 (e.g., 5rem)" + }, + "24": { + "type": "string", + "description": "Spacing unit 24 (e.g., 6rem)" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/Theme.json b/packages/spec/json-schema/Theme.json new file mode 100644 index 000000000..92f43b884 --- /dev/null +++ b/packages/spec/json-schema/Theme.json @@ -0,0 +1,543 @@ +{ + "$ref": "#/definitions/Theme", + "definitions": { + "Theme": { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique theme identifier (snake_case)" + }, + "label": { + "type": "string", + "description": "Human-readable theme name" + }, + "description": { + "type": "string", + "description": "Theme description" + }, + "mode": { + "type": "string", + "enum": [ + "light", + "dark", + "auto" + ], + "default": "light", + "description": "Theme mode (light, dark, or auto)" + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "Primary brand color (hex, rgb, or hsl)" + }, + "secondary": { + "type": "string", + "description": "Secondary brand color" + }, + "accent": { + "type": "string", + "description": "Accent color for highlights" + }, + "success": { + "type": "string", + "description": "Success state color (default: green)" + }, + "warning": { + "type": "string", + "description": "Warning state color (default: yellow)" + }, + "error": { + "type": "string", + "description": "Error state color (default: red)" + }, + "info": { + "type": "string", + "description": "Info state color (default: blue)" + }, + "background": { + "type": "string", + "description": "Background color" + }, + "surface": { + "type": "string", + "description": "Surface/card background color" + }, + "text": { + "type": "string", + "description": "Primary text color" + }, + "textSecondary": { + "type": "string", + "description": "Secondary text color" + }, + "border": { + "type": "string", + "description": "Border color" + }, + "disabled": { + "type": "string", + "description": "Disabled state color" + }, + "primaryLight": { + "type": "string", + "description": "Lighter shade of primary" + }, + "primaryDark": { + "type": "string", + "description": "Darker shade of primary" + }, + "secondaryLight": { + "type": "string", + "description": "Lighter shade of secondary" + }, + "secondaryDark": { + "type": "string", + "description": "Darker shade of secondary" + } + }, + "required": [ + "primary" + ], + "additionalProperties": false, + "description": "Color palette configuration" + }, + "typography": { + "type": "object", + "properties": { + "fontFamily": { + "type": "object", + "properties": { + "base": { + "type": "string", + "description": "Base font family (default: system fonts)" + }, + "heading": { + "type": "string", + "description": "Heading font family" + }, + "mono": { + "type": "string", + "description": "Monospace font family for code" + } + }, + "additionalProperties": false + }, + "fontSize": { + "type": "object", + "properties": { + "xs": { + "type": "string", + "description": "Extra small font size (e.g., 0.75rem)" + }, + "sm": { + "type": "string", + "description": "Small font size (e.g., 0.875rem)" + }, + "base": { + "type": "string", + "description": "Base font size (e.g., 1rem)" + }, + "lg": { + "type": "string", + "description": "Large font size (e.g., 1.125rem)" + }, + "xl": { + "type": "string", + "description": "Extra large font size (e.g., 1.25rem)" + }, + "2xl": { + "type": "string", + "description": "2X large font size (e.g., 1.5rem)" + }, + "3xl": { + "type": "string", + "description": "3X large font size (e.g., 1.875rem)" + }, + "4xl": { + "type": "string", + "description": "4X large font size (e.g., 2.25rem)" + } + }, + "additionalProperties": false + }, + "fontWeight": { + "type": "object", + "properties": { + "light": { + "type": "number", + "description": "Light weight (default: 300)" + }, + "normal": { + "type": "number", + "description": "Normal weight (default: 400)" + }, + "medium": { + "type": "number", + "description": "Medium weight (default: 500)" + }, + "semibold": { + "type": "number", + "description": "Semibold weight (default: 600)" + }, + "bold": { + "type": "number", + "description": "Bold weight (default: 700)" + } + }, + "additionalProperties": false + }, + "lineHeight": { + "type": "object", + "properties": { + "tight": { + "type": "string", + "description": "Tight line height (e.g., 1.25)" + }, + "normal": { + "type": "string", + "description": "Normal line height (e.g., 1.5)" + }, + "relaxed": { + "type": "string", + "description": "Relaxed line height (e.g., 1.75)" + }, + "loose": { + "type": "string", + "description": "Loose line height (e.g., 2)" + } + }, + "additionalProperties": false + }, + "letterSpacing": { + "type": "object", + "properties": { + "tighter": { + "type": "string", + "description": "Tighter letter spacing (e.g., -0.05em)" + }, + "tight": { + "type": "string", + "description": "Tight letter spacing (e.g., -0.025em)" + }, + "normal": { + "type": "string", + "description": "Normal letter spacing (e.g., 0)" + }, + "wide": { + "type": "string", + "description": "Wide letter spacing (e.g., 0.025em)" + }, + "wider": { + "type": "string", + "description": "Wider letter spacing (e.g., 0.05em)" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "description": "Typography settings" + }, + "spacing": { + "type": "object", + "properties": { + "0": { + "type": "string", + "description": "0 spacing (0)" + }, + "1": { + "type": "string", + "description": "Spacing unit 1 (e.g., 0.25rem)" + }, + "2": { + "type": "string", + "description": "Spacing unit 2 (e.g., 0.5rem)" + }, + "3": { + "type": "string", + "description": "Spacing unit 3 (e.g., 0.75rem)" + }, + "4": { + "type": "string", + "description": "Spacing unit 4 (e.g., 1rem)" + }, + "5": { + "type": "string", + "description": "Spacing unit 5 (e.g., 1.25rem)" + }, + "6": { + "type": "string", + "description": "Spacing unit 6 (e.g., 1.5rem)" + }, + "8": { + "type": "string", + "description": "Spacing unit 8 (e.g., 2rem)" + }, + "10": { + "type": "string", + "description": "Spacing unit 10 (e.g., 2.5rem)" + }, + "12": { + "type": "string", + "description": "Spacing unit 12 (e.g., 3rem)" + }, + "16": { + "type": "string", + "description": "Spacing unit 16 (e.g., 4rem)" + }, + "20": { + "type": "string", + "description": "Spacing unit 20 (e.g., 5rem)" + }, + "24": { + "type": "string", + "description": "Spacing unit 24 (e.g., 6rem)" + } + }, + "additionalProperties": false, + "description": "Spacing scale" + }, + "borderRadius": { + "type": "object", + "properties": { + "none": { + "type": "string", + "description": "No border radius (0)" + }, + "sm": { + "type": "string", + "description": "Small border radius (e.g., 0.125rem)" + }, + "base": { + "type": "string", + "description": "Base border radius (e.g., 0.25rem)" + }, + "md": { + "type": "string", + "description": "Medium border radius (e.g., 0.375rem)" + }, + "lg": { + "type": "string", + "description": "Large border radius (e.g., 0.5rem)" + }, + "xl": { + "type": "string", + "description": "Extra large border radius (e.g., 0.75rem)" + }, + "2xl": { + "type": "string", + "description": "2X large border radius (e.g., 1rem)" + }, + "full": { + "type": "string", + "description": "Full border radius (50%)" + } + }, + "additionalProperties": false, + "description": "Border radius scale" + }, + "shadows": { + "type": "object", + "properties": { + "none": { + "type": "string", + "description": "No shadow" + }, + "sm": { + "type": "string", + "description": "Small shadow" + }, + "base": { + "type": "string", + "description": "Base shadow" + }, + "md": { + "type": "string", + "description": "Medium shadow" + }, + "lg": { + "type": "string", + "description": "Large shadow" + }, + "xl": { + "type": "string", + "description": "Extra large shadow" + }, + "2xl": { + "type": "string", + "description": "2X large shadow" + }, + "inner": { + "type": "string", + "description": "Inner shadow (inset)" + } + }, + "additionalProperties": false, + "description": "Box shadow effects" + }, + "breakpoints": { + "type": "object", + "properties": { + "xs": { + "type": "string", + "description": "Extra small breakpoint (e.g., 480px)" + }, + "sm": { + "type": "string", + "description": "Small breakpoint (e.g., 640px)" + }, + "md": { + "type": "string", + "description": "Medium breakpoint (e.g., 768px)" + }, + "lg": { + "type": "string", + "description": "Large breakpoint (e.g., 1024px)" + }, + "xl": { + "type": "string", + "description": "Extra large breakpoint (e.g., 1280px)" + }, + "2xl": { + "type": "string", + "description": "2X large breakpoint (e.g., 1536px)" + } + }, + "additionalProperties": false, + "description": "Responsive breakpoints" + }, + "animation": { + "type": "object", + "properties": { + "duration": { + "type": "object", + "properties": { + "fast": { + "type": "string", + "description": "Fast animation (e.g., 150ms)" + }, + "base": { + "type": "string", + "description": "Base animation (e.g., 300ms)" + }, + "slow": { + "type": "string", + "description": "Slow animation (e.g., 500ms)" + } + }, + "additionalProperties": false + }, + "timing": { + "type": "object", + "properties": { + "linear": { + "type": "string", + "description": "Linear timing function" + }, + "ease": { + "type": "string", + "description": "Ease timing function" + }, + "easeIn": { + "type": "string", + "description": "Ease-in timing function" + }, + "easeOut": { + "type": "string", + "description": "Ease-out timing function" + }, + "easeInOut": { + "type": "string", + "description": "Ease-in-out timing function" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "description": "Animation settings" + }, + "zIndex": { + "type": "object", + "properties": { + "base": { + "type": "number", + "description": "Base z-index (e.g., 0)" + }, + "dropdown": { + "type": "number", + "description": "Dropdown z-index (e.g., 1000)" + }, + "sticky": { + "type": "number", + "description": "Sticky z-index (e.g., 1020)" + }, + "fixed": { + "type": "number", + "description": "Fixed z-index (e.g., 1030)" + }, + "modalBackdrop": { + "type": "number", + "description": "Modal backdrop z-index (e.g., 1040)" + }, + "modal": { + "type": "number", + "description": "Modal z-index (e.g., 1050)" + }, + "popover": { + "type": "number", + "description": "Popover z-index (e.g., 1060)" + }, + "tooltip": { + "type": "number", + "description": "Tooltip z-index (e.g., 1070)" + } + }, + "additionalProperties": false, + "description": "Z-index scale for layering" + }, + "customVars": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Custom CSS variables (key-value pairs)" + }, + "logo": { + "type": "object", + "properties": { + "light": { + "type": "string", + "description": "Logo URL for light mode" + }, + "dark": { + "type": "string", + "description": "Logo URL for dark mode" + }, + "favicon": { + "type": "string", + "description": "Favicon URL" + } + }, + "additionalProperties": false, + "description": "Logo assets" + }, + "extends": { + "type": "string", + "description": "Base theme to extend from" + } + }, + "required": [ + "name", + "label", + "colors" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ThemeMode.json b/packages/spec/json-schema/ThemeMode.json new file mode 100644 index 000000000..92a3c1e95 --- /dev/null +++ b/packages/spec/json-schema/ThemeMode.json @@ -0,0 +1,14 @@ +{ + "$ref": "#/definitions/ThemeMode", + "definitions": { + "ThemeMode": { + "type": "string", + "enum": [ + "light", + "dark", + "auto" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/Typography.json b/packages/spec/json-schema/Typography.json new file mode 100644 index 000000000..e6add092f --- /dev/null +++ b/packages/spec/json-schema/Typography.json @@ -0,0 +1,142 @@ +{ + "$ref": "#/definitions/Typography", + "definitions": { + "Typography": { + "type": "object", + "properties": { + "fontFamily": { + "type": "object", + "properties": { + "base": { + "type": "string", + "description": "Base font family (default: system fonts)" + }, + "heading": { + "type": "string", + "description": "Heading font family" + }, + "mono": { + "type": "string", + "description": "Monospace font family for code" + } + }, + "additionalProperties": false + }, + "fontSize": { + "type": "object", + "properties": { + "xs": { + "type": "string", + "description": "Extra small font size (e.g., 0.75rem)" + }, + "sm": { + "type": "string", + "description": "Small font size (e.g., 0.875rem)" + }, + "base": { + "type": "string", + "description": "Base font size (e.g., 1rem)" + }, + "lg": { + "type": "string", + "description": "Large font size (e.g., 1.125rem)" + }, + "xl": { + "type": "string", + "description": "Extra large font size (e.g., 1.25rem)" + }, + "2xl": { + "type": "string", + "description": "2X large font size (e.g., 1.5rem)" + }, + "3xl": { + "type": "string", + "description": "3X large font size (e.g., 1.875rem)" + }, + "4xl": { + "type": "string", + "description": "4X large font size (e.g., 2.25rem)" + } + }, + "additionalProperties": false + }, + "fontWeight": { + "type": "object", + "properties": { + "light": { + "type": "number", + "description": "Light weight (default: 300)" + }, + "normal": { + "type": "number", + "description": "Normal weight (default: 400)" + }, + "medium": { + "type": "number", + "description": "Medium weight (default: 500)" + }, + "semibold": { + "type": "number", + "description": "Semibold weight (default: 600)" + }, + "bold": { + "type": "number", + "description": "Bold weight (default: 700)" + } + }, + "additionalProperties": false + }, + "lineHeight": { + "type": "object", + "properties": { + "tight": { + "type": "string", + "description": "Tight line height (e.g., 1.25)" + }, + "normal": { + "type": "string", + "description": "Normal line height (e.g., 1.5)" + }, + "relaxed": { + "type": "string", + "description": "Relaxed line height (e.g., 1.75)" + }, + "loose": { + "type": "string", + "description": "Loose line height (e.g., 2)" + } + }, + "additionalProperties": false + }, + "letterSpacing": { + "type": "object", + "properties": { + "tighter": { + "type": "string", + "description": "Tighter letter spacing (e.g., -0.05em)" + }, + "tight": { + "type": "string", + "description": "Tight letter spacing (e.g., -0.025em)" + }, + "normal": { + "type": "string", + "description": "Normal letter spacing (e.g., 0)" + }, + "wide": { + "type": "string", + "description": "Wide letter spacing (e.g., 0.025em)" + }, + "wider": { + "type": "string", + "description": "Wider letter spacing (e.g., 0.05em)" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ValidationRule.json b/packages/spec/json-schema/ValidationRule.json index 1b8da1a86..13f76a2e8 100644 --- a/packages/spec/json-schema/ValidationRule.json +++ b/packages/spec/json-schema/ValidationRule.json @@ -203,6 +203,589 @@ "field" ], "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "cross_field" + }, + "condition": { + "type": "string", + "description": "Formula expression comparing fields (e.g. \"end_date > start_date\")" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Fields involved in the validation" + } + }, + "required": [ + "name", + "message", + "type", + "condition", + "fields" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "async" + }, + "field": { + "type": "string", + "description": "Field to validate" + }, + "validatorUrl": { + "type": "string", + "description": "External API endpoint for validation" + }, + "validatorFunction": { + "type": "string", + "description": "Reference to custom validator function" + }, + "timeout": { + "type": "number", + "default": 5000, + "description": "Timeout in milliseconds" + }, + "debounce": { + "type": "number", + "description": "Debounce delay in milliseconds" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters to pass to validator" + } + }, + "required": [ + "name", + "message", + "type", + "field" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "custom" + }, + "field": { + "type": "string", + "description": "Field to validate (optional for record-level validation)" + }, + "validatorFunction": { + "type": "string", + "description": "Function name or reference to custom validator" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters for the validator" + } + }, + "required": [ + "name", + "message", + "type", + "validatorFunction" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "conditional" + }, + "when": { + "type": "string", + "description": "Condition formula (e.g. \"type = 'enterprise'\")" + }, + "then": { + "anyOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "script" + }, + "condition": { + "type": "string", + "description": "Formula expression. If TRUE, validation fails. (e.g. amount < 0)" + } + }, + "required": [ + "name", + "message", + "type", + "condition" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "unique" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Fields that must be combined unique" + }, + "scope": { + "type": "string", + "description": "Formula condition for scope (e.g. active = true)" + }, + "caseSensitive": { + "type": "boolean", + "default": true + } + }, + "required": [ + "name", + "message", + "type", + "fields" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "state_machine" + }, + "field": { + "type": "string", + "description": "State field (e.g. status)" + }, + "transitions": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": "Map of { OldState: [AllowedNewStates] }" + } + }, + "required": [ + "name", + "message", + "type", + "field", + "transitions" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "format" + }, + "field": { + "type": "string" + }, + "regex": { + "type": "string" + }, + "format": { + "type": "string", + "enum": [ + "email", + "url", + "phone", + "json" + ] + } + }, + "required": [ + "name", + "message", + "type", + "field" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "cross_field" + }, + "condition": { + "type": "string", + "description": "Formula expression comparing fields (e.g. \"end_date > start_date\")" + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Fields involved in the validation" + } + }, + "required": [ + "name", + "message", + "type", + "condition", + "fields" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "async" + }, + "field": { + "type": "string", + "description": "Field to validate" + }, + "validatorUrl": { + "type": "string", + "description": "External API endpoint for validation" + }, + "validatorFunction": { + "type": "string", + "description": "Reference to custom validator function" + }, + "timeout": { + "type": "number", + "default": 5000, + "description": "Timeout in milliseconds" + }, + "debounce": { + "type": "number", + "description": "Debounce delay in milliseconds" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters to pass to validator" + } + }, + "required": [ + "name", + "message", + "type", + "field" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z_][a-z0-9_]*$", + "description": "Unique rule name" + }, + "active": { + "type": "boolean", + "default": true + }, + "severity": { + "type": "string", + "enum": [ + "error", + "warning", + "info" + ], + "default": "error" + }, + "message": { + "type": "string", + "description": "Error message to display" + }, + "type": { + "type": "string", + "const": "custom" + }, + "field": { + "type": "string", + "description": "Field to validate (optional for record-level validation)" + }, + "validatorFunction": { + "type": "string", + "description": "Function name or reference to custom validator" + }, + "params": { + "type": "object", + "additionalProperties": {}, + "description": "Additional parameters for the validator" + } + }, + "required": [ + "name", + "message", + "type", + "validatorFunction" + ], + "additionalProperties": false + }, + {} + ], + "description": "Validation rule to apply when condition is true" + }, + "otherwise": { + "description": "Validation rule to apply when condition is false" + } + }, + "required": [ + "name", + "message", + "type", + "when", + "then" + ], + "additionalProperties": false } ] } diff --git a/packages/spec/json-schema/WindowFunction.json b/packages/spec/json-schema/WindowFunction.json new file mode 100644 index 000000000..6b0cdc7a6 --- /dev/null +++ b/packages/spec/json-schema/WindowFunction.json @@ -0,0 +1,24 @@ +{ + "$ref": "#/definitions/WindowFunction", + "definitions": { + "WindowFunction": { + "type": "string", + "enum": [ + "row_number", + "rank", + "dense_rank", + "percent_rank", + "lag", + "lead", + "first_value", + "last_value", + "sum", + "avg", + "count", + "min", + "max" + ] + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/WindowFunctionNode.json b/packages/spec/json-schema/WindowFunctionNode.json new file mode 100644 index 000000000..69317e40f --- /dev/null +++ b/packages/spec/json-schema/WindowFunctionNode.json @@ -0,0 +1,104 @@ +{ + "$ref": "#/definitions/WindowFunctionNode", + "definitions": { + "WindowFunctionNode": { + "type": "object", + "properties": { + "function": { + "type": "string", + "enum": [ + "row_number", + "rank", + "dense_rank", + "percent_rank", + "lag", + "lead", + "first_value", + "last_value", + "sum", + "avg", + "count", + "min", + "max" + ], + "description": "Window function name" + }, + "field": { + "type": "string", + "description": "Field to operate on (for aggregate window functions)" + }, + "alias": { + "type": "string", + "description": "Result column alias" + }, + "over": { + "type": "object", + "properties": { + "partitionBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "PARTITION BY fields" + }, + "orderBy": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "ORDER BY specification" + }, + "frame": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "rows", + "range" + ] + }, + "start": { + "type": "string", + "description": "Frame start (e.g., \"UNBOUNDED PRECEDING\", \"1 PRECEDING\")" + }, + "end": { + "type": "string", + "description": "Frame end (e.g., \"CURRENT ROW\", \"1 FOLLOWING\")" + } + }, + "additionalProperties": false, + "description": "Window frame specification" + } + }, + "additionalProperties": false, + "description": "Window specification (OVER clause)" + } + }, + "required": [ + "function", + "alias", + "over" + ], + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/WindowSpec.json b/packages/spec/json-schema/WindowSpec.json new file mode 100644 index 000000000..213b69b11 --- /dev/null +++ b/packages/spec/json-schema/WindowSpec.json @@ -0,0 +1,65 @@ +{ + "$ref": "#/definitions/WindowSpec", + "definitions": { + "WindowSpec": { + "type": "object", + "properties": { + "partitionBy": { + "type": "array", + "items": { + "type": "string" + }, + "description": "PARTITION BY fields" + }, + "orderBy": { + "type": "array", + "items": { + "type": "object", + "properties": { + "field": { + "type": "string" + }, + "order": { + "type": "string", + "enum": [ + "asc", + "desc" + ], + "default": "asc" + } + }, + "required": [ + "field" + ], + "additionalProperties": false + }, + "description": "ORDER BY specification" + }, + "frame": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "rows", + "range" + ] + }, + "start": { + "type": "string", + "description": "Frame start (e.g., \"UNBOUNDED PRECEDING\", \"1 PRECEDING\")" + }, + "end": { + "type": "string", + "description": "Frame end (e.g., \"CURRENT ROW\", \"1 FOLLOWING\")" + } + }, + "additionalProperties": false, + "description": "Window frame specification" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/json-schema/ZIndex.json b/packages/spec/json-schema/ZIndex.json new file mode 100644 index 000000000..d525d1ee8 --- /dev/null +++ b/packages/spec/json-schema/ZIndex.json @@ -0,0 +1,44 @@ +{ + "$ref": "#/definitions/ZIndex", + "definitions": { + "ZIndex": { + "type": "object", + "properties": { + "base": { + "type": "number", + "description": "Base z-index (e.g., 0)" + }, + "dropdown": { + "type": "number", + "description": "Dropdown z-index (e.g., 1000)" + }, + "sticky": { + "type": "number", + "description": "Sticky z-index (e.g., 1020)" + }, + "fixed": { + "type": "number", + "description": "Fixed z-index (e.g., 1030)" + }, + "modalBackdrop": { + "type": "number", + "description": "Modal backdrop z-index (e.g., 1040)" + }, + "modal": { + "type": "number", + "description": "Modal z-index (e.g., 1050)" + }, + "popover": { + "type": "number", + "description": "Popover z-index (e.g., 1060)" + }, + "tooltip": { + "type": "number", + "description": "Tooltip z-index (e.g., 1070)" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" +} \ No newline at end of file diff --git a/packages/spec/src/data/field.test.ts b/packages/spec/src/data/field.test.ts index b08c9f90b..0280853e0 100644 --- a/packages/spec/src/data/field.test.ts +++ b/packages/spec/src/data/field.test.ts @@ -11,14 +11,15 @@ describe('FieldType', () => { it('should accept valid field types', () => { const validTypes = [ 'text', 'textarea', 'email', 'url', 'phone', 'password', - 'markdown', 'html', + 'markdown', 'html', 'richtext', 'number', 'currency', 'percent', 'date', 'datetime', 'time', 'boolean', 'select', 'lookup', 'master_detail', 'image', 'file', 'avatar', - 'formula', 'summary', 'autonumber' + 'formula', 'summary', 'autonumber', + 'location', 'address', 'code', 'color', 'rating', 'signature' ]; validTypes.forEach(type => { @@ -383,4 +384,114 @@ describe('Field Factory Helpers', () => { expect(masterDetailField.deleteBehavior).toBe('cascade'); }); }); + + describe('Enhanced Field Types', () => { + it('should accept location field type', () => { + expect(() => FieldType.parse('location')).not.toThrow(); + }); + + it('should accept address field type', () => { + expect(() => FieldType.parse('address')).not.toThrow(); + }); + + it('should accept richtext field type', () => { + expect(() => FieldType.parse('richtext')).not.toThrow(); + }); + + it('should accept code field type', () => { + expect(() => FieldType.parse('code')).not.toThrow(); + }); + + it('should accept color field type', () => { + expect(() => FieldType.parse('color')).not.toThrow(); + }); + + it('should accept rating field type', () => { + expect(() => FieldType.parse('rating')).not.toThrow(); + }); + + it('should accept signature field type', () => { + expect(() => FieldType.parse('signature')).not.toThrow(); + }); + + it('should create location field with config', () => { + const locationField = Field.location({ + label: 'Office Location', + displayMap: true, + allowGeocoding: true, + }); + + expect(locationField.type).toBe('location'); + expect(locationField.label).toBe('Office Location'); + expect(locationField.displayMap).toBe(true); + }); + + it('should create address field with format', () => { + const addressField = Field.address({ + label: 'Mailing Address', + addressFormat: 'us', + required: true, + }); + + expect(addressField.type).toBe('address'); + expect(addressField.addressFormat).toBe('us'); + expect(addressField.required).toBe(true); + }); + + it('should create richtext field', () => { + const richtextField = Field.richtext({ + label: 'Description', + maxLength: 5000, + }); + + expect(richtextField.type).toBe('richtext'); + expect(richtextField.label).toBe('Description'); + }); + + it('should create code field with language', () => { + const codeField = Field.code('javascript', { + label: 'Script', + lineNumbers: true, + theme: 'dark', + }); + + expect(codeField.type).toBe('code'); + expect(codeField.language).toBe('javascript'); + expect(codeField.lineNumbers).toBe(true); + }); + + it('should create color field with format', () => { + const colorField = Field.color({ + label: 'Brand Color', + colorFormat: 'hex', + allowAlpha: false, + presetColors: ['#FF0000', '#00FF00', '#0000FF'], + }); + + expect(colorField.type).toBe('color'); + expect(colorField.colorFormat).toBe('hex'); + expect(colorField.presetColors).toHaveLength(3); + }); + + it('should create rating field with max rating', () => { + const ratingField = Field.rating(10, { + label: 'Customer Satisfaction', + allowHalf: true, + }); + + expect(ratingField.type).toBe('rating'); + expect(ratingField.maxRating).toBe(10); + expect(ratingField.allowHalf).toBe(true); + }); + + it('should create signature field', () => { + const signatureField = Field.signature({ + label: 'Customer Signature', + required: true, + }); + + expect(signatureField.type).toBe('signature'); + expect(signatureField.required).toBe(true); + }); + }); }); diff --git a/packages/spec/src/data/field.zod.ts b/packages/spec/src/data/field.zod.ts index f1f246cfb..99f158a68 100644 --- a/packages/spec/src/data/field.zod.ts +++ b/packages/spec/src/data/field.zod.ts @@ -7,7 +7,7 @@ export const FieldType = z.enum([ // Core Text 'text', 'textarea', 'email', 'url', 'phone', 'password', // Rich Content - 'markdown', 'html', + 'markdown', 'html', 'richtext', // Numbers 'number', 'currency', 'percent', // Date & Time @@ -21,7 +21,14 @@ export const FieldType = z.enum([ // Media 'image', 'file', 'avatar', // Calculated / System - 'formula', 'summary', 'autonumber' + 'formula', 'summary', 'autonumber', + // Enhanced Types + 'location', // GPS coordinates + 'address', // Structured address + 'code', // Code with syntax highlighting + 'color', // Color picker + 'rating', // Star rating + 'signature' // Digital signature ]); export type FieldType = z.infer; @@ -36,6 +43,31 @@ export const SelectOptionSchema = z.object({ default: z.boolean().optional().describe('Is default option'), }); +/** + * Location Coordinates Schema + * GPS coordinates for location field type + */ +export const LocationCoordinatesSchema = z.object({ + latitude: z.number().min(-90).max(90).describe('Latitude coordinate'), + longitude: z.number().min(-180).max(180).describe('Longitude coordinate'), + altitude: z.number().optional().describe('Altitude in meters'), + accuracy: z.number().optional().describe('Accuracy in meters'), +}); + +/** + * Address Schema + * Structured address for address field type + */ +export const AddressSchema = z.object({ + street: z.string().optional().describe('Street address'), + city: z.string().optional().describe('City name'), + state: z.string().optional().describe('State/Province'), + postalCode: z.string().optional().describe('Postal/ZIP code'), + country: z.string().optional().describe('Country name or code'), + countryCode: z.string().optional().describe('ISO country code (e.g., US, GB)'), + formatted: z.string().optional().describe('Formatted address string'), +}); + /** * Field Schema - Best Practice Enterprise Pattern */ @@ -82,6 +114,28 @@ export const FieldSchema = z.object({ function: z.enum(['count', 'sum', 'min', 'max', 'avg']) }).optional().describe('Roll-up summary definition'), + /** Enhanced Field Type Configurations */ + // Code field config + language: z.string().optional().describe('Programming language for syntax highlighting (e.g., javascript, python, sql)'), + theme: z.string().optional().describe('Code editor theme (e.g., dark, light, monokai)'), + lineNumbers: z.boolean().optional().describe('Show line numbers in code editor'), + + // Rating field config + maxRating: z.number().optional().describe('Maximum rating value (default: 5)'), + allowHalf: z.boolean().optional().describe('Allow half-star ratings'), + + // Location field config + displayMap: z.boolean().optional().describe('Display map widget for location field'), + allowGeocoding: z.boolean().optional().describe('Allow address-to-coordinate conversion'), + + // Address field config + addressFormat: z.enum(['us', 'uk', 'international']).optional().describe('Address format template'), + + // Color field config + colorFormat: z.enum(['hex', 'rgb', 'rgba', 'hsl']).optional().describe('Color value format'), + allowAlpha: z.boolean().optional().describe('Allow transparency/alpha channel'), + presetColors: z.array(z.string()).optional().describe('Preset color options'), + /** Security & Visibility */ hidden: z.boolean().default(false).describe('Hidden from default UI'), readonly: z.boolean().default(false).describe('Read-only in UI'), @@ -94,6 +148,8 @@ export const FieldSchema = z.object({ export type Field = z.infer; export type SelectOption = z.infer; +export type LocationCoordinates = z.infer; +export type Address = z.infer; /** * Field Factory Helper @@ -165,4 +221,42 @@ export const Field = { reference, ...config } as const), + + // Enhanced Field Type Helpers + location: (config: FieldInput = {}) => ({ + type: 'location', + ...config + } as const), + + address: (config: FieldInput = {}) => ({ + type: 'address', + ...config + } as const), + + richtext: (config: FieldInput = {}) => ({ + type: 'richtext', + ...config + } as const), + + code: (language?: string, config: FieldInput = {}) => ({ + type: 'code', + language, + ...config + } as const), + + color: (config: FieldInput = {}) => ({ + type: 'color', + ...config + } as const), + + rating: (maxRating: number = 5, config: FieldInput = {}) => ({ + type: 'rating', + maxRating, + ...config + } as const), + + signature: (config: FieldInput = {}) => ({ + type: 'signature', + ...config + } as const), }; diff --git a/packages/spec/src/data/query.test.ts b/packages/spec/src/data/query.test.ts new file mode 100644 index 000000000..a6eb80917 --- /dev/null +++ b/packages/spec/src/data/query.test.ts @@ -0,0 +1,440 @@ +import { describe, it, expect } from 'vitest'; +import { + QuerySchema, + FilterOperator, + LogicOperator, + AggregationFunction, + JoinType, + WindowFunction, + type QueryAST, + type AggregationNode, + type JoinNode, + type WindowFunctionNode, +} from './query.zod'; + +describe('FilterOperator', () => { + it('should accept valid filter operators', () => { + const validOperators = [ + '=', '!=', '<>', + '>', '>=', '<', '<=', + 'startswith', 'contains', 'notcontains', + 'between', 'in', 'notin', + 'is_null', 'is_not_null' + ]; + + validOperators.forEach(op => { + expect(() => FilterOperator.parse(op)).not.toThrow(); + }); + }); + + it('should reject invalid operators', () => { + expect(() => FilterOperator.parse('LIKE')).toThrow(); + expect(() => FilterOperator.parse('equals')).toThrow(); + }); +}); + +describe('LogicOperator', () => { + it('should accept valid logic operators', () => { + expect(() => LogicOperator.parse('and')).not.toThrow(); + expect(() => LogicOperator.parse('or')).not.toThrow(); + expect(() => LogicOperator.parse('not')).not.toThrow(); + }); +}); + +describe('AggregationFunction', () => { + it('should accept valid aggregation functions', () => { + const validFunctions = [ + 'count', 'sum', 'avg', 'min', 'max', + 'count_distinct', 'array_agg', 'string_agg' + ]; + + validFunctions.forEach(fn => { + expect(() => AggregationFunction.parse(fn)).not.toThrow(); + }); + }); + + it('should reject invalid aggregation functions', () => { + expect(() => AggregationFunction.parse('COUNT')).toThrow(); + expect(() => AggregationFunction.parse('median')).toThrow(); + }); +}); + +describe('JoinType', () => { + it('should accept valid join types', () => { + expect(() => JoinType.parse('inner')).not.toThrow(); + expect(() => JoinType.parse('left')).not.toThrow(); + expect(() => JoinType.parse('right')).not.toThrow(); + expect(() => JoinType.parse('full')).not.toThrow(); + }); + + it('should reject invalid join types', () => { + expect(() => JoinType.parse('INNER')).toThrow(); + expect(() => JoinType.parse('cross')).toThrow(); + }); +}); + +describe('WindowFunction', () => { + it('should accept valid window functions', () => { + const validFunctions = [ + 'row_number', 'rank', 'dense_rank', 'percent_rank', + 'lag', 'lead', 'first_value', 'last_value', + 'sum', 'avg', 'count', 'min', 'max' + ]; + + validFunctions.forEach(fn => { + expect(() => WindowFunction.parse(fn)).not.toThrow(); + }); + }); +}); + +describe('QuerySchema - Basic', () => { + it('should accept simple query', () => { + const query: QueryAST = { + object: 'account', + fields: ['name', 'email'], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with filters', () => { + const query: QueryAST = { + object: 'account', + fields: ['name', 'email'], + filters: ['status', '=', 'active'], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with sort', () => { + const query: QueryAST = { + object: 'account', + fields: ['name', 'email'], + sort: [ + { field: 'name', order: 'asc' }, + { field: 'created_at', order: 'desc' }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with pagination', () => { + const query: QueryAST = { + object: 'account', + fields: ['name'], + top: 10, + skip: 20, + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with distinct', () => { + const query: QueryAST = { + object: 'account', + fields: ['status'], + distinct: true, + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); +}); + +describe('QuerySchema - Aggregations', () => { + it('should accept query with simple aggregation', () => { + const query: QueryAST = { + object: 'order', + aggregations: [ + { function: 'count', alias: 'total_orders' }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with field aggregation', () => { + const query: QueryAST = { + object: 'order', + aggregations: [ + { function: 'sum', field: 'amount', alias: 'total_amount' }, + { function: 'avg', field: 'amount', alias: 'avg_amount' }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with group by', () => { + const query: QueryAST = { + object: 'order', + fields: ['customer_id'], + aggregations: [ + { function: 'count', alias: 'order_count' }, + { function: 'sum', field: 'amount', alias: 'total_amount' }, + ], + groupBy: ['customer_id'], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with having clause', () => { + const query: QueryAST = { + object: 'order', + fields: ['customer_id'], + aggregations: [ + { function: 'sum', field: 'amount', alias: 'total_amount' }, + ], + groupBy: ['customer_id'], + having: ['total_amount', '>', 1000], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept count distinct aggregation', () => { + const query: QueryAST = { + object: 'order', + aggregations: [ + { function: 'count_distinct', field: 'customer_id', alias: 'unique_customers' }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); +}); + +describe('QuerySchema - Joins', () => { + it('should accept query with inner join', () => { + const query: QueryAST = { + object: 'order', + fields: ['id', 'amount'], + joins: [ + { + type: 'inner', + object: 'customer', + alias: 'c', + on: ['order.customer_id', '=', 'c.id'], + }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with left join', () => { + const query: QueryAST = { + object: 'customer', + fields: ['name'], + joins: [ + { + type: 'left', + object: 'order', + on: ['customer.id', '=', 'order.customer_id'], + }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with multiple joins', () => { + const query: QueryAST = { + object: 'order', + fields: ['id'], + joins: [ + { + type: 'inner', + object: 'customer', + alias: 'c', + on: ['order.customer_id', '=', 'c.id'], + }, + { + type: 'left', + object: 'product', + alias: 'p', + on: ['order.product_id', '=', 'p.id'], + }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with subquery join', () => { + const query: QueryAST = { + object: 'order', + fields: ['id', 'amount'], + joins: [ + { + type: 'inner', + object: 'customer', + alias: 'high_value_customers', + on: ['order.customer_id', '=', 'high_value_customers.id'], + subquery: { + object: 'customer', + fields: ['id'], + filters: ['total_spent', '>', 10000], + }, + }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); +}); + +describe('QuerySchema - Window Functions', () => { + it('should accept query with row_number window function', () => { + const query: QueryAST = { + object: 'order', + fields: ['id', 'customer_id', 'amount'], + windowFunctions: [ + { + function: 'row_number', + alias: 'row_num', + over: { + partitionBy: ['customer_id'], + orderBy: [{ field: 'amount', order: 'desc' }], + }, + }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with rank window function', () => { + const query: QueryAST = { + object: 'student', + fields: ['name', 'score'], + windowFunctions: [ + { + function: 'rank', + alias: 'rank', + over: { + orderBy: [{ field: 'score', order: 'desc' }], + }, + }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with aggregate window function', () => { + const query: QueryAST = { + object: 'order', + fields: ['id', 'amount'], + windowFunctions: [ + { + function: 'sum', + field: 'amount', + alias: 'running_total', + over: { + orderBy: [{ field: 'created_at', order: 'asc' }], + frame: { + type: 'rows', + start: 'UNBOUNDED PRECEDING', + end: 'CURRENT ROW', + }, + }, + }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with lag/lead window function', () => { + const query: QueryAST = { + object: 'sales', + fields: ['month', 'revenue'], + windowFunctions: [ + { + function: 'lag', + field: 'revenue', + alias: 'prev_month_revenue', + over: { + orderBy: [{ field: 'month', order: 'asc' }], + }, + }, + { + function: 'lead', + field: 'revenue', + alias: 'next_month_revenue', + over: { + orderBy: [{ field: 'month', order: 'asc' }], + }, + }, + ], + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); +}); + +describe('QuerySchema - Complex Queries', () => { + it('should accept complex query with joins, aggregations, and window functions', () => { + const query: QueryAST = { + object: 'order', + fields: ['customer_id'], + joins: [ + { + type: 'inner', + object: 'customer', + alias: 'c', + on: ['order.customer_id', '=', 'c.id'], + }, + ], + aggregations: [ + { function: 'sum', field: 'amount', alias: 'total_amount' }, + { function: 'count', alias: 'order_count' }, + ], + groupBy: ['customer_id'], + having: ['order_count', '>', 5], + sort: [{ field: 'total_amount', order: 'desc' }], + top: 100, + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); + + it('should accept query with all features', () => { + const query: QueryAST = { + object: 'order', + fields: ['id', 'customer_id', 'amount'], + distinct: true, + filters: ['status', '=', 'completed'], + joins: [ + { + type: 'inner', + object: 'customer', + on: ['order.customer_id', '=', 'customer.id'], + }, + ], + aggregations: [ + { function: 'avg', field: 'amount', alias: 'avg_amount' }, + ], + windowFunctions: [ + { + function: 'rank', + alias: 'customer_rank', + over: { + partitionBy: ['customer_id'], + orderBy: [{ field: 'amount', order: 'desc' }], + }, + }, + ], + groupBy: ['customer_id'], + having: ['avg_amount', '>', 500], + sort: [{ field: 'avg_amount', order: 'desc' }], + top: 50, + skip: 0, + }; + + expect(() => QuerySchema.parse(query)).not.toThrow(); + }); +}); diff --git a/packages/spec/src/data/query.zod.ts b/packages/spec/src/data/query.zod.ts index 7e211307f..a84cf7bd3 100644 --- a/packages/spec/src/data/query.zod.ts +++ b/packages/spec/src/data/query.zod.ts @@ -45,6 +45,79 @@ export const SortNodeSchema = z.object({ order: z.enum(['asc', 'desc']).default('asc') }); +/** + * Aggregation Function Enum + * Standard aggregation functions. + */ +export const AggregationFunction = z.enum([ + 'count', 'sum', 'avg', 'min', 'max', + 'count_distinct', 'array_agg', 'string_agg' +]); + +/** + * Aggregation Node + * Represents aggregated field with function. + */ +export const AggregationNodeSchema = z.object({ + function: AggregationFunction.describe('Aggregation function'), + field: z.string().optional().describe('Field to aggregate (optional for COUNT(*))'), + alias: z.string().describe('Result column alias'), + distinct: z.boolean().optional().describe('Apply DISTINCT before aggregation'), +}); + +/** + * Join Type Enum + */ +export const JoinType = z.enum(['inner', 'left', 'right', 'full']); + +/** + * Join Node + * Represents table joins. + */ +export const JoinNodeSchema: z.ZodType = z.lazy(() => + z.object({ + type: JoinType.describe('Join type'), + object: z.string().describe('Object/table to join'), + alias: z.string().optional().describe('Table alias'), + on: FilterNodeSchema.describe('Join condition'), + subquery: z.lazy(() => QuerySchema).optional().describe('Subquery instead of object'), + }) +); + +/** + * Window Function Enum + */ +export const WindowFunction = z.enum([ + 'row_number', 'rank', 'dense_rank', 'percent_rank', + 'lag', 'lead', 'first_value', 'last_value', + 'sum', 'avg', 'count', 'min', 'max' +]); + +/** + * Window Specification + * Defines PARTITION BY and ORDER BY for window functions. + */ +export const WindowSpecSchema = z.object({ + partitionBy: z.array(z.string()).optional().describe('PARTITION BY fields'), + orderBy: z.array(SortNodeSchema).optional().describe('ORDER BY specification'), + frame: z.object({ + type: z.enum(['rows', 'range']).optional(), + start: z.string().optional().describe('Frame start (e.g., "UNBOUNDED PRECEDING", "1 PRECEDING")'), + end: z.string().optional().describe('Frame end (e.g., "CURRENT ROW", "1 FOLLOWING")'), + }).optional().describe('Window frame specification'), +}); + +/** + * Window Function Node + * Represents window function with OVER clause. + */ +export const WindowFunctionNodeSchema = z.object({ + function: WindowFunction.describe('Window function name'), + field: z.string().optional().describe('Field to operate on (for aggregate window functions)'), + alias: z.string().describe('Result column alias'), + over: WindowSpecSchema.describe('Window specification (OVER clause)'), +}); + /** * Field Selection Node * Represents "Select" attributes, including joins. @@ -71,17 +144,39 @@ export const QuerySchema = z.object({ /** Select Clause */ fields: z.array(FieldNodeSchema).optional().describe('Fields to retrieve'), + /** Aggregations */ + aggregations: z.array(AggregationNodeSchema).optional().describe('Aggregation functions (GROUP BY)'), + + /** Window Functions */ + windowFunctions: z.array(WindowFunctionNodeSchema).optional().describe('Window functions with OVER clause'), + /** Where Clause */ filters: FilterNodeSchema.optional().describe('Filtering criteria'), + /** Joins */ + joins: z.array(JoinNodeSchema).optional().describe('Table joins'), + + /** Group By Clause */ + groupBy: z.array(z.string()).optional().describe('GROUP BY fields'), + + /** Having Clause */ + having: FilterNodeSchema.optional().describe('HAVING clause for aggregation filtering'), + /** Order By Clause */ sort: z.array(SortNodeSchema).optional().describe('Sorting instructions'), /** Pagination */ top: z.number().optional().describe('Limit results'), skip: z.number().optional().describe('Offset results'), + + /** Subquery flag */ + distinct: z.boolean().optional().describe('SELECT DISTINCT flag'), }); export type QueryAST = z.infer; export type FilterNode = z.infer; export type SortNode = z.infer; +export type AggregationNode = z.infer; +export type JoinNode = z.infer; +export type WindowFunctionNode = z.infer; +export type WindowSpec = z.infer; diff --git a/packages/spec/src/data/validation.test.ts b/packages/spec/src/data/validation.test.ts index 2244ab46e..12cfec874 100644 --- a/packages/spec/src/data/validation.test.ts +++ b/packages/spec/src/data/validation.test.ts @@ -321,4 +321,225 @@ describe('ValidationRuleSchema (Discriminated Union)', () => { }); }); }); + + describe('CrossFieldValidationSchema', () => { + it('should accept cross-field date validation', () => { + const crossFieldValidation = { + type: 'cross_field' as const, + name: 'end_after_start', + message: 'End date must be after start date', + condition: 'end_date > start_date', + fields: ['start_date', 'end_date'], + }; + + expect(() => ValidationRuleSchema.parse(crossFieldValidation)).not.toThrow(); + }); + + it('should accept complex cross-field validation', () => { + const validation = { + type: 'cross_field' as const, + name: 'discount_validation', + message: 'Discount cannot exceed 50% for amounts over $1000', + condition: 'amount > 1000 AND discount_percent > 50', + fields: ['amount', 'discount_percent'], + }; + + expect(() => ValidationRuleSchema.parse(validation)).not.toThrow(); + }); + }); + + describe('AsyncValidationSchema', () => { + it('should accept async validation with URL', () => { + const asyncValidation = { + type: 'async' as const, + name: 'check_username_available', + message: 'Username is already taken', + field: 'username', + validatorUrl: 'https://api.example.com/validate/username', + timeout: 3000, + debounce: 500, + }; + + expect(() => ValidationRuleSchema.parse(asyncValidation)).not.toThrow(); + }); + + it('should accept async validation with function reference', () => { + const asyncValidation = { + type: 'async' as const, + name: 'verify_vat_number', + message: 'Invalid VAT number', + field: 'vat_number', + validatorFunction: 'validateVatNumber', + params: { country: 'GB' }, + }; + + expect(() => ValidationRuleSchema.parse(asyncValidation)).not.toThrow(); + }); + + it('should apply default timeout', () => { + const validation = { + type: 'async' as const, + name: 'test_async', + message: 'Test', + field: 'email', + validatorUrl: 'https://api.example.com/validate', + }; + + const result = ValidationRuleSchema.parse(validation); + if (result.type === 'async') { + expect(result.timeout).toBe(5000); + } + }); + }); + + describe('CustomValidatorSchema', () => { + it('should accept custom field validator', () => { + const customValidation = { + type: 'custom' as const, + name: 'custom_business_rule', + message: 'Custom validation failed', + field: 'business_field', + validatorFunction: 'validateBusinessRule', + }; + + expect(() => ValidationRuleSchema.parse(customValidation)).not.toThrow(); + }); + + it('should accept custom validator with params', () => { + const validation = { + type: 'custom' as const, + name: 'complex_validation', + message: 'Validation failed', + field: 'data', + validatorFunction: 'complexValidator', + params: { + threshold: 100, + mode: 'strict', + }, + }; + + expect(() => ValidationRuleSchema.parse(validation)).not.toThrow(); + }); + + it('should accept record-level custom validator', () => { + const validation = { + type: 'custom' as const, + name: 'record_level_check', + message: 'Record validation failed', + validatorFunction: 'validateEntireRecord', + }; + + expect(() => ValidationRuleSchema.parse(validation)).not.toThrow(); + }); + }); + + describe('ConditionalValidationSchema', () => { + it('should accept conditional validation with then clause', () => { + const conditionalValidation = { + type: 'conditional' as const, + name: 'enterprise_validation', + message: 'Enterprise accounts require approval', + when: 'account_type = "enterprise"', + then: { + type: 'script' as const, + name: 'require_approval', + message: 'Approval required for enterprise accounts', + condition: 'approval_status = null', + }, + }; + + expect(() => ValidationRuleSchema.parse(conditionalValidation)).not.toThrow(); + }); + + it('should accept conditional validation with otherwise clause', () => { + const validation = { + type: 'conditional' as const, + name: 'amount_validation', + message: 'Amount validation', + when: 'type = "wholesale"', + then: { + type: 'script' as const, + name: 'wholesale_min', + message: 'Wholesale orders must be at least $1000', + condition: 'amount < 1000', + }, + otherwise: { + type: 'script' as const, + name: 'retail_min', + message: 'Retail orders must be at least $10', + condition: 'amount < 10', + }, + }; + + expect(() => ValidationRuleSchema.parse(validation)).not.toThrow(); + }); + + it('should accept nested conditional validation', () => { + const validation = { + type: 'conditional' as const, + name: 'nested_validation', + message: 'Complex conditional validation', + when: 'country = "US"', + then: { + type: 'conditional' as const, + name: 'state_validation', + message: 'State-specific validation', + when: 'state = "CA"', + then: { + type: 'script' as const, + name: 'ca_tax', + message: 'California requires tax ID', + condition: 'tax_id = null', + }, + }, + }; + + expect(() => ValidationRuleSchema.parse(validation)).not.toThrow(); + }); + }); + + describe('Advanced Validation Examples', () => { + it('should accept comprehensive validation ruleset', () => { + const advancedRules: ValidationRule[] = [ + { + type: 'cross_field', + name: 'date_range', + message: 'End date must be after start date', + condition: 'end_date > start_date', + fields: ['start_date', 'end_date'], + }, + { + type: 'async', + name: 'email_available', + message: 'Email is already registered', + field: 'email', + validatorUrl: '/api/validate/email', + debounce: 300, + }, + { + type: 'custom', + name: 'business_logic', + message: 'Business logic validation failed', + validatorFunction: 'validateBusinessRules', + }, + { + type: 'conditional', + name: 'type_based_validation', + message: 'Type-based validation', + when: 'type = "special"', + then: { + type: 'cross_field', + name: 'special_amount', + message: 'Special orders must have amount between min and max', + condition: 'amount >= min_amount AND amount <= max_amount', + fields: ['amount', 'min_amount', 'max_amount'], + }, + }, + ]; + + advancedRules.forEach(rule => { + expect(() => ValidationRuleSchema.parse(rule)).not.toThrow(); + }); + }); + }); }); diff --git a/packages/spec/src/data/validation.zod.ts b/packages/spec/src/data/validation.zod.ts index 25f12a822..bc9199c2e 100644 --- a/packages/spec/src/data/validation.zod.ts +++ b/packages/spec/src/data/validation.zod.ts @@ -52,16 +52,73 @@ export const FormatValidationSchema = BaseValidationSchema.extend({ }); /** - * Master Validation Rule Schema + * 5. Cross-Field Validation + * Validates relationships between multiple fields. */ -export const ValidationRuleSchema = z.discriminatedUnion('type', [ - ScriptValidationSchema, - UniquenessValidationSchema, - StateMachineValidationSchema, - FormatValidationSchema -]); +export const CrossFieldValidationSchema = BaseValidationSchema.extend({ + type: z.literal('cross_field'), + condition: z.string().describe('Formula expression comparing fields (e.g. "end_date > start_date")'), + fields: z.array(z.string()).describe('Fields involved in the validation'), +}); + +/** + * 6. Async Validation + * Remote validation via API call or database query. + */ +export const AsyncValidationSchema = BaseValidationSchema.extend({ + type: z.literal('async'), + field: z.string().describe('Field to validate'), + validatorUrl: z.string().optional().describe('External API endpoint for validation'), + validatorFunction: z.string().optional().describe('Reference to custom validator function'), + timeout: z.number().optional().default(5000).describe('Timeout in milliseconds'), + debounce: z.number().optional().describe('Debounce delay in milliseconds'), + params: z.record(z.any()).optional().describe('Additional parameters to pass to validator'), +}); + +/** + * 7. Custom Validator Function + * User-defined validation logic with code reference. + */ +export const CustomValidatorSchema = BaseValidationSchema.extend({ + type: z.literal('custom'), + field: z.string().optional().describe('Field to validate (optional for record-level validation)'), + validatorFunction: z.string().describe('Function name or reference to custom validator'), + params: z.record(z.any()).optional().describe('Additional parameters for the validator'), +}); + +/** + * Master Validation Rule Schema (forward declared for circular reference) + */ +export const ValidationRuleSchema: z.ZodType = z.lazy(() => + z.discriminatedUnion('type', [ + ScriptValidationSchema, + UniquenessValidationSchema, + StateMachineValidationSchema, + FormatValidationSchema, + CrossFieldValidationSchema, + AsyncValidationSchema, + CustomValidatorSchema, + ConditionalValidationSchema, + ]) +); + +/** + * 8. Conditional Validation + * Validation that only applies when a condition is met. + */ +export const ConditionalValidationSchema = BaseValidationSchema.extend({ + type: z.literal('conditional'), + when: z.string().describe('Condition formula (e.g. "type = \'enterprise\'")'), + then: ValidationRuleSchema.describe('Validation rule to apply when condition is true'), + otherwise: ValidationRuleSchema.optional().describe('Validation rule to apply when condition is false'), +}); export type ValidationRule = z.infer; export type ScriptValidation = z.infer; export type UniquenessValidation = z.infer; export type StateMachineValidation = z.infer; +export type FormatValidation = z.infer; +export type CrossFieldValidation = z.infer; +export type AsyncValidation = z.infer; +export type CustomValidation = z.infer; +export type ConditionalValidation = z.infer; diff --git a/packages/spec/src/index.ts b/packages/spec/src/index.ts index 964dbd446..15a7d9003 100644 --- a/packages/spec/src/index.ts +++ b/packages/spec/src/index.ts @@ -34,6 +34,7 @@ export * from './ui/report.zod'; export * from './ui/action.zod'; export * from './ui/page.zod'; export * from './ui/widget.zod'; +export * from './ui/theme.zod'; // System Protocol (Manifest, Runtime, Constants) export * from './system/manifest.zod'; diff --git a/packages/spec/src/ui/theme.test.ts b/packages/spec/src/ui/theme.test.ts new file mode 100644 index 000000000..58a312585 --- /dev/null +++ b/packages/spec/src/ui/theme.test.ts @@ -0,0 +1,479 @@ +import { describe, it, expect } from 'vitest'; +import { + ThemeSchema, + ThemeMode, + ColorPaletteSchema, + TypographySchema, + SpacingSchema, + BorderRadiusSchema, + ShadowSchema, + type Theme, + type ColorPalette, +} from './theme.zod'; + +describe('ThemeMode', () => { + it('should accept valid theme modes', () => { + expect(() => ThemeMode.parse('light')).not.toThrow(); + expect(() => ThemeMode.parse('dark')).not.toThrow(); + expect(() => ThemeMode.parse('auto')).not.toThrow(); + }); + + it('should reject invalid theme modes', () => { + expect(() => ThemeMode.parse('custom')).toThrow(); + expect(() => ThemeMode.parse('system')).toThrow(); + }); +}); + +describe('ColorPaletteSchema', () => { + it('should accept minimal color palette with primary color', () => { + const palette: ColorPalette = { + primary: '#007BFF', + }; + + expect(() => ColorPaletteSchema.parse(palette)).not.toThrow(); + }); + + it('should accept complete color palette', () => { + const palette: ColorPalette = { + primary: '#007BFF', + secondary: '#6C757D', + accent: '#FFC107', + success: '#28A745', + warning: '#FFC107', + error: '#DC3545', + info: '#17A2B8', + background: '#FFFFFF', + surface: '#F8F9FA', + text: '#212529', + textSecondary: '#6C757D', + border: '#DEE2E6', + disabled: '#E9ECEF', + primaryLight: '#4DA3FF', + primaryDark: '#0056B3', + }; + + expect(() => ColorPaletteSchema.parse(palette)).not.toThrow(); + }); + + it('should accept colors in different formats', () => { + const palette: ColorPalette = { + primary: '#007BFF', // hex + secondary: 'rgb(108, 117, 125)', // rgb + accent: 'hsl(45, 100%, 51%)', // hsl + }; + + expect(() => ColorPaletteSchema.parse(palette)).not.toThrow(); + }); +}); + +describe('TypographySchema', () => { + it('should accept minimal typography settings', () => { + const typography = { + fontFamily: { + base: 'Inter, system-ui, sans-serif', + }, + }; + + expect(() => TypographySchema.parse(typography)).not.toThrow(); + }); + + it('should accept complete typography settings', () => { + const typography = { + fontFamily: { + base: 'Inter, system-ui, sans-serif', + heading: 'Poppins, sans-serif', + mono: 'Fira Code, monospace', + }, + fontSize: { + xs: '0.75rem', + sm: '0.875rem', + base: '1rem', + lg: '1.125rem', + xl: '1.25rem', + '2xl': '1.5rem', + '3xl': '1.875rem', + '4xl': '2.25rem', + }, + fontWeight: { + light: 300, + normal: 400, + medium: 500, + semibold: 600, + bold: 700, + }, + lineHeight: { + tight: '1.25', + normal: '1.5', + relaxed: '1.75', + loose: '2', + }, + letterSpacing: { + tighter: '-0.05em', + tight: '-0.025em', + normal: '0', + wide: '0.025em', + wider: '0.05em', + }, + }; + + expect(() => TypographySchema.parse(typography)).not.toThrow(); + }); +}); + +describe('SpacingSchema', () => { + it('should accept spacing scale', () => { + const spacing = { + '0': '0', + '1': '0.25rem', + '2': '0.5rem', + '4': '1rem', + '8': '2rem', + }; + + expect(() => SpacingSchema.parse(spacing)).not.toThrow(); + }); +}); + +describe('BorderRadiusSchema', () => { + it('should accept border radius scale', () => { + const borderRadius = { + none: '0', + sm: '0.125rem', + base: '0.25rem', + md: '0.375rem', + lg: '0.5rem', + full: '9999px', + }; + + expect(() => BorderRadiusSchema.parse(borderRadius)).not.toThrow(); + }); +}); + +describe('ShadowSchema', () => { + it('should accept shadow definitions', () => { + const shadows = { + none: 'none', + sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)', + base: '0 1px 3px 0 rgba(0, 0, 0, 0.1)', + md: '0 4px 6px -1px rgba(0, 0, 0, 0.1)', + lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1)', + }; + + expect(() => ShadowSchema.parse(shadows)).not.toThrow(); + }); +}); + +describe('ThemeSchema', () => { + it('should accept minimal theme with required fields', () => { + const theme: Theme = { + name: 'default_theme', + label: 'Default Theme', + colors: { + primary: '#007BFF', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should enforce snake_case for theme name', () => { + const validNames = ['default_theme', 'dark_theme', 'custom_2023']; + validNames.forEach(name => { + const theme: Theme = { + name, + label: 'Test Theme', + colors: { primary: '#000000' }, + }; + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + const invalidNames = ['DefaultTheme', 'dark-theme', '123theme']; + invalidNames.forEach(name => { + const theme = { + name, + label: 'Test Theme', + colors: { primary: '#000000' }, + }; + expect(() => ThemeSchema.parse(theme)).toThrow(); + }); + }); + + it('should accept complete theme configuration', () => { + const theme: Theme = { + name: 'enterprise_theme', + label: 'Enterprise Theme', + description: 'Professional theme for enterprise applications', + mode: 'light', + colors: { + primary: '#0066CC', + secondary: '#6C757D', + accent: '#FFC107', + success: '#28A745', + warning: '#FFC107', + error: '#DC3545', + info: '#17A2B8', + background: '#FFFFFF', + surface: '#F8F9FA', + text: '#212529', + textSecondary: '#6C757D', + border: '#DEE2E6', + }, + typography: { + fontFamily: { + base: 'Inter, sans-serif', + heading: 'Poppins, sans-serif', + mono: 'Fira Code, monospace', + }, + fontSize: { + base: '1rem', + lg: '1.125rem', + xl: '1.25rem', + }, + }, + spacing: { + '4': '1rem', + '8': '2rem', + }, + borderRadius: { + base: '0.25rem', + lg: '0.5rem', + }, + shadows: { + base: '0 1px 3px 0 rgba(0, 0, 0, 0.1)', + lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1)', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should accept theme with logo configuration', () => { + const theme: Theme = { + name: 'branded_theme', + label: 'Branded Theme', + colors: { + primary: '#007BFF', + }, + logo: { + light: '/assets/logo-light.svg', + dark: '/assets/logo-dark.svg', + favicon: '/assets/favicon.ico', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should accept theme with custom CSS variables', () => { + const theme: Theme = { + name: 'custom_vars_theme', + label: 'Custom Variables Theme', + colors: { + primary: '#007BFF', + }, + customVars: { + '--header-height': '64px', + '--sidebar-width': '256px', + '--transition-speed': '0.3s', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should accept theme that extends another theme', () => { + const theme: Theme = { + name: 'dark_extended', + label: 'Dark Extended Theme', + extends: 'default_theme', + colors: { + primary: '#007BFF', + background: '#1A1A1A', + text: '#FFFFFF', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should apply default mode', () => { + const theme = { + name: 'test_theme', + label: 'Test Theme', + colors: { + primary: '#007BFF', + }, + }; + + const result = ThemeSchema.parse(theme); + expect(result.mode).toBe('light'); + }); + + it('should accept dark mode theme', () => { + const theme: Theme = { + name: 'dark_theme', + label: 'Dark Theme', + mode: 'dark', + colors: { + primary: '#4DA3FF', + background: '#1A1A1A', + surface: '#2D2D2D', + text: '#FFFFFF', + textSecondary: '#B0B0B0', + border: '#404040', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should accept auto mode theme', () => { + const theme: Theme = { + name: 'auto_theme', + label: 'Auto Theme', + mode: 'auto', + colors: { + primary: '#007BFF', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should accept theme with z-index configuration', () => { + const theme: Theme = { + name: 'layered_theme', + label: 'Layered Theme', + colors: { + primary: '#007BFF', + }, + zIndex: { + base: 0, + dropdown: 1000, + sticky: 1020, + fixed: 1030, + modalBackdrop: 1040, + modal: 1050, + popover: 1060, + tooltip: 1070, + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should accept theme with animation settings', () => { + const theme: Theme = { + name: 'animated_theme', + label: 'Animated Theme', + colors: { + primary: '#007BFF', + }, + animation: { + duration: { + fast: '150ms', + base: '300ms', + slow: '500ms', + }, + timing: { + ease: 'cubic-bezier(0.4, 0, 0.2, 1)', + easeIn: 'cubic-bezier(0.4, 0, 1, 1)', + easeOut: 'cubic-bezier(0, 0, 0.2, 1)', + easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)', + }, + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should accept theme with breakpoints', () => { + const theme: Theme = { + name: 'responsive_theme', + label: 'Responsive Theme', + colors: { + primary: '#007BFF', + }, + breakpoints: { + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + '2xl': '1536px', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); +}); + +describe('Real-World Theme Examples', () => { + it('should accept enterprise light theme', () => { + const theme: Theme = { + name: 'enterprise_light', + label: 'Enterprise Light', + description: 'Professional light theme for enterprise applications', + mode: 'light', + colors: { + primary: '#0066CC', + secondary: '#4A5568', + accent: '#ED8936', + success: '#48BB78', + warning: '#ECC94B', + error: '#F56565', + info: '#4299E1', + background: '#FFFFFF', + surface: '#F7FAFC', + text: '#1A202C', + textSecondary: '#718096', + border: '#E2E8F0', + }, + typography: { + fontFamily: { + base: 'Inter, -apple-system, BlinkMacSystemFont, sans-serif', + heading: 'Poppins, sans-serif', + }, + }, + logo: { + light: '/assets/logo.svg', + favicon: '/assets/favicon.ico', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); + + it('should accept dark theme with extended configuration', () => { + const theme: Theme = { + name: 'professional_dark', + label: 'Professional Dark', + mode: 'dark', + colors: { + primary: '#60A5FA', + secondary: '#9CA3AF', + accent: '#FBBF24', + success: '#34D399', + warning: '#FBBF24', + error: '#F87171', + info: '#60A5FA', + background: '#0F172A', + surface: '#1E293B', + text: '#F1F5F9', + textSecondary: '#94A3B8', + border: '#334155', + }, + borderRadius: { + base: '0.375rem', + lg: '0.5rem', + xl: '0.75rem', + }, + shadows: { + base: '0 1px 3px 0 rgba(0, 0, 0, 0.3)', + md: '0 4px 6px -1px rgba(0, 0, 0, 0.3)', + lg: '0 10px 15px -3px rgba(0, 0, 0, 0.3)', + }, + }; + + expect(() => ThemeSchema.parse(theme)).not.toThrow(); + }); +}); diff --git a/packages/spec/src/ui/theme.zod.ts b/packages/spec/src/ui/theme.zod.ts new file mode 100644 index 000000000..d84028253 --- /dev/null +++ b/packages/spec/src/ui/theme.zod.ts @@ -0,0 +1,239 @@ +import { z } from 'zod'; + +/** + * Color Palette Schema + * Defines brand colors and their variants. + */ +export const ColorPaletteSchema = z.object({ + primary: z.string().describe('Primary brand color (hex, rgb, or hsl)'), + secondary: z.string().optional().describe('Secondary brand color'), + accent: z.string().optional().describe('Accent color for highlights'), + success: z.string().optional().describe('Success state color (default: green)'), + warning: z.string().optional().describe('Warning state color (default: yellow)'), + error: z.string().optional().describe('Error state color (default: red)'), + info: z.string().optional().describe('Info state color (default: blue)'), + + // Neutral colors + background: z.string().optional().describe('Background color'), + surface: z.string().optional().describe('Surface/card background color'), + text: z.string().optional().describe('Primary text color'), + textSecondary: z.string().optional().describe('Secondary text color'), + border: z.string().optional().describe('Border color'), + disabled: z.string().optional().describe('Disabled state color'), + + // Color variants (shades) + primaryLight: z.string().optional().describe('Lighter shade of primary'), + primaryDark: z.string().optional().describe('Darker shade of primary'), + secondaryLight: z.string().optional().describe('Lighter shade of secondary'), + secondaryDark: z.string().optional().describe('Darker shade of secondary'), +}); + +/** + * Typography Settings Schema + * Font families, sizes, weights, and line heights. + */ +export const TypographySchema = z.object({ + fontFamily: z.object({ + base: z.string().optional().describe('Base font family (default: system fonts)'), + heading: z.string().optional().describe('Heading font family'), + mono: z.string().optional().describe('Monospace font family for code'), + }).optional(), + + fontSize: z.object({ + xs: z.string().optional().describe('Extra small font size (e.g., 0.75rem)'), + sm: z.string().optional().describe('Small font size (e.g., 0.875rem)'), + base: z.string().optional().describe('Base font size (e.g., 1rem)'), + lg: z.string().optional().describe('Large font size (e.g., 1.125rem)'), + xl: z.string().optional().describe('Extra large font size (e.g., 1.25rem)'), + '2xl': z.string().optional().describe('2X large font size (e.g., 1.5rem)'), + '3xl': z.string().optional().describe('3X large font size (e.g., 1.875rem)'), + '4xl': z.string().optional().describe('4X large font size (e.g., 2.25rem)'), + }).optional(), + + fontWeight: z.object({ + light: z.number().optional().describe('Light weight (default: 300)'), + normal: z.number().optional().describe('Normal weight (default: 400)'), + medium: z.number().optional().describe('Medium weight (default: 500)'), + semibold: z.number().optional().describe('Semibold weight (default: 600)'), + bold: z.number().optional().describe('Bold weight (default: 700)'), + }).optional(), + + lineHeight: z.object({ + tight: z.string().optional().describe('Tight line height (e.g., 1.25)'), + normal: z.string().optional().describe('Normal line height (e.g., 1.5)'), + relaxed: z.string().optional().describe('Relaxed line height (e.g., 1.75)'), + loose: z.string().optional().describe('Loose line height (e.g., 2)'), + }).optional(), + + letterSpacing: z.object({ + tighter: z.string().optional().describe('Tighter letter spacing (e.g., -0.05em)'), + tight: z.string().optional().describe('Tight letter spacing (e.g., -0.025em)'), + normal: z.string().optional().describe('Normal letter spacing (e.g., 0)'), + wide: z.string().optional().describe('Wide letter spacing (e.g., 0.025em)'), + wider: z.string().optional().describe('Wider letter spacing (e.g., 0.05em)'), + }).optional(), +}); + +/** + * Spacing Units Schema + * Defines spacing scale for margins, padding, gaps. + */ +export const SpacingSchema = z.object({ + '0': z.string().optional().describe('0 spacing (0)'), + '1': z.string().optional().describe('Spacing unit 1 (e.g., 0.25rem)'), + '2': z.string().optional().describe('Spacing unit 2 (e.g., 0.5rem)'), + '3': z.string().optional().describe('Spacing unit 3 (e.g., 0.75rem)'), + '4': z.string().optional().describe('Spacing unit 4 (e.g., 1rem)'), + '5': z.string().optional().describe('Spacing unit 5 (e.g., 1.25rem)'), + '6': z.string().optional().describe('Spacing unit 6 (e.g., 1.5rem)'), + '8': z.string().optional().describe('Spacing unit 8 (e.g., 2rem)'), + '10': z.string().optional().describe('Spacing unit 10 (e.g., 2.5rem)'), + '12': z.string().optional().describe('Spacing unit 12 (e.g., 3rem)'), + '16': z.string().optional().describe('Spacing unit 16 (e.g., 4rem)'), + '20': z.string().optional().describe('Spacing unit 20 (e.g., 5rem)'), + '24': z.string().optional().describe('Spacing unit 24 (e.g., 6rem)'), +}); + +/** + * Border Radius Schema + * Rounded corners configuration. + */ +export const BorderRadiusSchema = z.object({ + none: z.string().optional().describe('No border radius (0)'), + sm: z.string().optional().describe('Small border radius (e.g., 0.125rem)'), + base: z.string().optional().describe('Base border radius (e.g., 0.25rem)'), + md: z.string().optional().describe('Medium border radius (e.g., 0.375rem)'), + lg: z.string().optional().describe('Large border radius (e.g., 0.5rem)'), + xl: z.string().optional().describe('Extra large border radius (e.g., 0.75rem)'), + '2xl': z.string().optional().describe('2X large border radius (e.g., 1rem)'), + full: z.string().optional().describe('Full border radius (50%)'), +}); + +/** + * Shadow Schema + * Box shadow effects. + */ +export const ShadowSchema = z.object({ + none: z.string().optional().describe('No shadow'), + sm: z.string().optional().describe('Small shadow'), + base: z.string().optional().describe('Base shadow'), + md: z.string().optional().describe('Medium shadow'), + lg: z.string().optional().describe('Large shadow'), + xl: z.string().optional().describe('Extra large shadow'), + '2xl': z.string().optional().describe('2X large shadow'), + inner: z.string().optional().describe('Inner shadow (inset)'), +}); + +/** + * Breakpoints Schema + * Responsive design breakpoints. + */ +export const BreakpointsSchema = z.object({ + xs: z.string().optional().describe('Extra small breakpoint (e.g., 480px)'), + sm: z.string().optional().describe('Small breakpoint (e.g., 640px)'), + md: z.string().optional().describe('Medium breakpoint (e.g., 768px)'), + lg: z.string().optional().describe('Large breakpoint (e.g., 1024px)'), + xl: z.string().optional().describe('Extra large breakpoint (e.g., 1280px)'), + '2xl': z.string().optional().describe('2X large breakpoint (e.g., 1536px)'), +}); + +/** + * Animation Schema + * Animation timing and duration settings. + */ +export const AnimationSchema = z.object({ + duration: z.object({ + fast: z.string().optional().describe('Fast animation (e.g., 150ms)'), + base: z.string().optional().describe('Base animation (e.g., 300ms)'), + slow: z.string().optional().describe('Slow animation (e.g., 500ms)'), + }).optional(), + + timing: z.object({ + linear: z.string().optional().describe('Linear timing function'), + ease: z.string().optional().describe('Ease timing function'), + easeIn: z.string().optional().describe('Ease-in timing function'), + easeOut: z.string().optional().describe('Ease-out timing function'), + easeInOut: z.string().optional().describe('Ease-in-out timing function'), + }).optional(), +}); + +/** + * Z-Index Scale Schema + * Layering and stacking order. + */ +export const ZIndexSchema = z.object({ + base: z.number().optional().describe('Base z-index (e.g., 0)'), + dropdown: z.number().optional().describe('Dropdown z-index (e.g., 1000)'), + sticky: z.number().optional().describe('Sticky z-index (e.g., 1020)'), + fixed: z.number().optional().describe('Fixed z-index (e.g., 1030)'), + modalBackdrop: z.number().optional().describe('Modal backdrop z-index (e.g., 1040)'), + modal: z.number().optional().describe('Modal z-index (e.g., 1050)'), + popover: z.number().optional().describe('Popover z-index (e.g., 1060)'), + tooltip: z.number().optional().describe('Tooltip z-index (e.g., 1070)'), +}); + +/** + * Theme Mode Enum + */ +export const ThemeMode = z.enum(['light', 'dark', 'auto']); + +/** + * Theme Configuration Schema + * Complete theme definition for brand customization. + */ +export const ThemeSchema = z.object({ + name: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Unique theme identifier (snake_case)'), + label: z.string().describe('Human-readable theme name'), + description: z.string().optional().describe('Theme description'), + + /** Theme mode */ + mode: ThemeMode.default('light').describe('Theme mode (light, dark, or auto)'), + + /** Color system */ + colors: ColorPaletteSchema.describe('Color palette configuration'), + + /** Typography */ + typography: TypographySchema.optional().describe('Typography settings'), + + /** Spacing */ + spacing: SpacingSchema.optional().describe('Spacing scale'), + + /** Border radius */ + borderRadius: BorderRadiusSchema.optional().describe('Border radius scale'), + + /** Shadows */ + shadows: ShadowSchema.optional().describe('Box shadow effects'), + + /** Breakpoints */ + breakpoints: BreakpointsSchema.optional().describe('Responsive breakpoints'), + + /** Animation */ + animation: AnimationSchema.optional().describe('Animation settings'), + + /** Z-Index */ + zIndex: ZIndexSchema.optional().describe('Z-index scale for layering'), + + /** Custom CSS variables */ + customVars: z.record(z.string()).optional().describe('Custom CSS variables (key-value pairs)'), + + /** Logo */ + logo: z.object({ + light: z.string().optional().describe('Logo URL for light mode'), + dark: z.string().optional().describe('Logo URL for dark mode'), + favicon: z.string().optional().describe('Favicon URL'), + }).optional().describe('Logo assets'), + + /** Extends another theme */ + extends: z.string().optional().describe('Base theme to extend from'), +}); + +export type Theme = z.infer; +export type ColorPalette = z.infer; +export type Typography = z.infer; +export type Spacing = z.infer; +export type BorderRadius = z.infer; +export type Shadow = z.infer; +export type Breakpoints = z.infer; +export type Animation = z.infer; +export type ZIndex = z.infer; +export type ThemeMode = z.infer;