@constela/core 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +239 -0
- package/dist/index.d.ts +208 -8
- package/dist/index.js +258 -16
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# @constela/core
|
|
2
|
+
|
|
3
|
+
Core types and validation for Constela JSON programs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @constela/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## JSON Program Structure
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"version": "1.0",
|
|
16
|
+
"route": { "path": "/", "layout": "MainLayout" },
|
|
17
|
+
"imports": { "config": "./data/config.json" },
|
|
18
|
+
"data": { "posts": { "type": "glob", "pattern": "content/*.mdx" } },
|
|
19
|
+
"lifecycle": { "onMount": "loadData" },
|
|
20
|
+
"state": { ... },
|
|
21
|
+
"actions": [ ... ],
|
|
22
|
+
"view": { ... },
|
|
23
|
+
"components": { ... }
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
All fields except `version`, `state`, `actions`, and `view` are optional.
|
|
28
|
+
|
|
29
|
+
## State Types
|
|
30
|
+
|
|
31
|
+
5 supported state types:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"count": { "type": "number", "initial": 0 },
|
|
36
|
+
"query": { "type": "string", "initial": "" },
|
|
37
|
+
"items": { "type": "list", "initial": [] },
|
|
38
|
+
"isVisible": { "type": "boolean", "initial": true },
|
|
39
|
+
"form": { "type": "object", "initial": { "name": "", "email": "" } }
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Expression Types
|
|
44
|
+
|
|
45
|
+
12 expression types for constrained computation:
|
|
46
|
+
|
|
47
|
+
| Type | JSON Example | Description |
|
|
48
|
+
|------|-------------|-------------|
|
|
49
|
+
| `lit` | `{ "expr": "lit", "value": "Hello" }` | Literal value |
|
|
50
|
+
| `state` | `{ "expr": "state", "name": "count" }` | State reference |
|
|
51
|
+
| `var` | `{ "expr": "var", "name": "item" }` | Loop/event variable |
|
|
52
|
+
| `bin` | `{ "expr": "bin", "op": "+", "left": ..., "right": ... }` | Binary operation |
|
|
53
|
+
| `not` | `{ "expr": "not", "operand": ... }` | Logical negation |
|
|
54
|
+
| `param` | `{ "expr": "param", "name": "title" }` | Component parameter |
|
|
55
|
+
| `cond` | `{ "expr": "cond", "if": ..., "then": ..., "else": ... }` | Conditional |
|
|
56
|
+
| `get` | `{ "expr": "get", "base": ..., "path": "user.name" }` | Property access |
|
|
57
|
+
| `route` | `{ "expr": "route", "name": "id", "source": "param" }` | Route parameter |
|
|
58
|
+
| `import` | `{ "expr": "import", "name": "config" }` | External data |
|
|
59
|
+
| `data` | `{ "expr": "data", "name": "posts" }` | Build-time data |
|
|
60
|
+
| `ref` | `{ "expr": "ref", "name": "inputEl" }` | DOM element ref |
|
|
61
|
+
|
|
62
|
+
**Binary Operators:** `+`, `-`, `*`, `/`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`
|
|
63
|
+
|
|
64
|
+
## View Node Types
|
|
65
|
+
|
|
66
|
+
8 node types for building UI:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
// Element node
|
|
70
|
+
{ "kind": "element", "tag": "div", "props": { ... }, "children": [ ... ] }
|
|
71
|
+
|
|
72
|
+
// Text node
|
|
73
|
+
{ "kind": "text", "value": { "expr": "state", "name": "count" } }
|
|
74
|
+
|
|
75
|
+
// Conditional node
|
|
76
|
+
{ "kind": "if", "condition": { ... }, "then": { ... }, "else": { ... } }
|
|
77
|
+
|
|
78
|
+
// Loop node
|
|
79
|
+
{ "kind": "each", "items": { "expr": "state", "name": "todos" }, "as": "item", "body": { ... } }
|
|
80
|
+
|
|
81
|
+
// Component node
|
|
82
|
+
{ "kind": "component", "name": "Button", "props": { "label": { ... } } }
|
|
83
|
+
|
|
84
|
+
// Slot node (for layouts)
|
|
85
|
+
{ "kind": "slot" }
|
|
86
|
+
{ "kind": "slot", "name": "sidebar" }
|
|
87
|
+
|
|
88
|
+
// Markdown node
|
|
89
|
+
{ "kind": "markdown", "content": { "expr": "state", "name": "content" } }
|
|
90
|
+
|
|
91
|
+
// Code block node
|
|
92
|
+
{ "kind": "code", "code": { ... }, "language": { ... } }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Action Step Types
|
|
96
|
+
|
|
97
|
+
11 step types for declarative actions:
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
// Set state value
|
|
101
|
+
{ "do": "set", "target": "query", "value": { "expr": "lit", "value": "" } }
|
|
102
|
+
|
|
103
|
+
// Update with operation
|
|
104
|
+
{ "do": "update", "target": "count", "operation": "increment" }
|
|
105
|
+
{ "do": "update", "target": "todos", "operation": "push", "value": { ... } }
|
|
106
|
+
|
|
107
|
+
// HTTP request
|
|
108
|
+
{ "do": "fetch", "url": { ... }, "method": "GET", "onSuccess": [ ... ], "onError": [ ... ] }
|
|
109
|
+
|
|
110
|
+
// Storage operation
|
|
111
|
+
{ "do": "storage", "operation": "get", "key": { ... }, "storage": "local" }
|
|
112
|
+
|
|
113
|
+
// Clipboard operation
|
|
114
|
+
{ "do": "clipboard", "operation": "write", "value": { ... } }
|
|
115
|
+
|
|
116
|
+
// Navigation
|
|
117
|
+
{ "do": "navigate", "url": { ... } }
|
|
118
|
+
|
|
119
|
+
// Dynamic import
|
|
120
|
+
{ "do": "import", "module": "chart.js", "result": "Chart" }
|
|
121
|
+
|
|
122
|
+
// External function call
|
|
123
|
+
{ "do": "call", "ref": "Chart", "method": "create", "args": [ ... ] }
|
|
124
|
+
|
|
125
|
+
// Event subscription
|
|
126
|
+
{ "do": "subscribe", "ref": "eventSource", "event": "message", "action": "handleMessage" }
|
|
127
|
+
|
|
128
|
+
// Resource disposal
|
|
129
|
+
{ "do": "dispose", "ref": "chartInstance" }
|
|
130
|
+
|
|
131
|
+
// DOM manipulation
|
|
132
|
+
{ "do": "dom", "operation": "addClass", "ref": "myElement", "value": { ... } }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Update Operations:**
|
|
136
|
+
|
|
137
|
+
| Operation | State Type | Description |
|
|
138
|
+
|-----------|------------|-------------|
|
|
139
|
+
| `increment` | number | Add to number |
|
|
140
|
+
| `decrement` | number | Subtract from number |
|
|
141
|
+
| `push` | list | Add item to end |
|
|
142
|
+
| `pop` | list | Remove last item |
|
|
143
|
+
| `remove` | list | Remove by value/index |
|
|
144
|
+
| `toggle` | boolean | Flip boolean |
|
|
145
|
+
| `merge` | object | Shallow merge |
|
|
146
|
+
| `replaceAt` | list | Replace at index |
|
|
147
|
+
| `insertAt` | list | Insert at index |
|
|
148
|
+
| `splice` | list | Delete/insert items |
|
|
149
|
+
|
|
150
|
+
## Lifecycle Hooks
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"lifecycle": {
|
|
155
|
+
"onMount": "loadData",
|
|
156
|
+
"onUnmount": "cleanup",
|
|
157
|
+
"onRouteEnter": "fetchData",
|
|
158
|
+
"onRouteLeave": "saveState"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Error Codes
|
|
164
|
+
|
|
165
|
+
| Code | Description |
|
|
166
|
+
|------|-------------|
|
|
167
|
+
| `SCHEMA_INVALID` | JSON Schema validation error |
|
|
168
|
+
| `UNSUPPORTED_VERSION` | Unsupported version string |
|
|
169
|
+
| `UNDEFINED_STATE` | Reference to undefined state |
|
|
170
|
+
| `UNDEFINED_ACTION` | Reference to undefined action |
|
|
171
|
+
| `DUPLICATE_ACTION` | Duplicate action name |
|
|
172
|
+
| `VAR_UNDEFINED` | Undefined variable reference |
|
|
173
|
+
| `COMPONENT_NOT_FOUND` | Undefined component |
|
|
174
|
+
| `COMPONENT_PROP_MISSING` | Missing required prop |
|
|
175
|
+
| `COMPONENT_CYCLE` | Circular component reference |
|
|
176
|
+
| `COMPONENT_PROP_TYPE` | Prop type mismatch |
|
|
177
|
+
| `PARAM_UNDEFINED` | Undefined parameter |
|
|
178
|
+
| `OPERATION_INVALID_FOR_TYPE` | Invalid operation for state type |
|
|
179
|
+
| `OPERATION_MISSING_FIELD` | Missing required field for operation |
|
|
180
|
+
| `ROUTE_NOT_DEFINED` | Route not defined |
|
|
181
|
+
| `UNDEFINED_ROUTE_PARAM` | Undefined route parameter |
|
|
182
|
+
| `LAYOUT_MISSING_SLOT` | Layout missing slot node |
|
|
183
|
+
| `LAYOUT_NOT_FOUND` | Referenced layout not found |
|
|
184
|
+
| `INVALID_SLOT_NAME` | Invalid slot name |
|
|
185
|
+
| `DUPLICATE_SLOT_NAME` | Duplicate slot name |
|
|
186
|
+
| `DUPLICATE_DEFAULT_SLOT` | Multiple default slots |
|
|
187
|
+
| `SLOT_IN_LOOP` | Slot inside loop |
|
|
188
|
+
| `UNDEFINED_DATA_SOURCE` | Undefined data source |
|
|
189
|
+
| `UNDEFINED_IMPORT` | Undefined import reference |
|
|
190
|
+
| `UNDEFINED_REF` | Undefined element ref |
|
|
191
|
+
| `INVALID_STORAGE_OPERATION` | Invalid storage operation |
|
|
192
|
+
| `INVALID_CLIPBOARD_OPERATION` | Invalid clipboard operation |
|
|
193
|
+
| `INVALID_NAVIGATE_TARGET` | Invalid navigate target |
|
|
194
|
+
|
|
195
|
+
## Internal API
|
|
196
|
+
|
|
197
|
+
> For framework developers only.
|
|
198
|
+
|
|
199
|
+
### validateAst
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { validateAst } from '@constela/core';
|
|
203
|
+
|
|
204
|
+
const result = validateAst(input);
|
|
205
|
+
if (result.ok) {
|
|
206
|
+
console.log('Valid program:', result.program);
|
|
207
|
+
} else {
|
|
208
|
+
console.error('Validation failed:', result.error);
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Type Guards
|
|
213
|
+
|
|
214
|
+
47 type guard functions for runtime type checking:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import {
|
|
218
|
+
isLitExpr, isStateExpr, isVarExpr, isBinExpr,
|
|
219
|
+
isElementNode, isTextNode, isIfNode, isEachNode,
|
|
220
|
+
isSetStep, isUpdateStep, isFetchStep,
|
|
221
|
+
isNumberField, isStringField, isListField,
|
|
222
|
+
} from '@constela/core';
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### ConstelaError
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
import { ConstelaError } from '@constela/core';
|
|
229
|
+
|
|
230
|
+
const error = new ConstelaError(
|
|
231
|
+
'UNDEFINED_STATE',
|
|
232
|
+
'State "count" is not defined',
|
|
233
|
+
'/view/children/0/props/onClick'
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ interface LitExpr {
|
|
|
37
37
|
interface StateExpr {
|
|
38
38
|
expr: 'state';
|
|
39
39
|
name: string;
|
|
40
|
+
path?: string;
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
42
43
|
* Variable expression - references a loop variable or event data
|
|
@@ -118,7 +119,23 @@ interface RefExpr {
|
|
|
118
119
|
expr: 'ref';
|
|
119
120
|
name: string;
|
|
120
121
|
}
|
|
121
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Index expression - dynamic property/array access
|
|
124
|
+
*/
|
|
125
|
+
interface IndexExpr {
|
|
126
|
+
expr: 'index';
|
|
127
|
+
base: Expression;
|
|
128
|
+
key: Expression;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Style expression - references a style preset with optional variant values
|
|
132
|
+
*/
|
|
133
|
+
interface StyleExpr {
|
|
134
|
+
expr: 'style';
|
|
135
|
+
name: string;
|
|
136
|
+
variants?: Record<string, Expression>;
|
|
137
|
+
}
|
|
138
|
+
type Expression = LitExpr | StateExpr | VarExpr | BinExpr | NotExpr | ParamExpr | CondExpr | GetExpr | RouteExpr | ImportExpr | DataExpr | RefExpr | IndexExpr | StyleExpr;
|
|
122
139
|
/**
|
|
123
140
|
* Number state field
|
|
124
141
|
*/
|
|
@@ -275,7 +292,17 @@ interface DisposeStep {
|
|
|
275
292
|
do: 'dispose';
|
|
276
293
|
target: Expression;
|
|
277
294
|
}
|
|
278
|
-
|
|
295
|
+
/**
|
|
296
|
+
* DOM step - manipulate DOM elements (add/remove classes, attributes)
|
|
297
|
+
*/
|
|
298
|
+
interface DomStep {
|
|
299
|
+
do: 'dom';
|
|
300
|
+
operation: 'addClass' | 'removeClass' | 'toggleClass' | 'setAttribute' | 'removeAttribute';
|
|
301
|
+
selector: Expression;
|
|
302
|
+
value?: Expression;
|
|
303
|
+
attribute?: string;
|
|
304
|
+
}
|
|
305
|
+
type ActionStep = SetStep | UpdateStep | FetchStep | StorageStep | ClipboardStep | NavigateStep | ImportStep | CallStep | SubscribeStep | DisposeStep | DomStep;
|
|
279
306
|
/**
|
|
280
307
|
* Event handler - binds an event to an action
|
|
281
308
|
*/
|
|
@@ -365,6 +392,22 @@ interface ComponentDef {
|
|
|
365
392
|
params?: Record<string, ParamDef>;
|
|
366
393
|
view: ViewNode;
|
|
367
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* Compound variant - applies additional classes when multiple variants match
|
|
397
|
+
*/
|
|
398
|
+
interface CompoundVariant {
|
|
399
|
+
[key: string]: string;
|
|
400
|
+
class: string;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Style preset - defines a reusable style with variants (CVA-like pattern)
|
|
404
|
+
*/
|
|
405
|
+
interface StylePreset {
|
|
406
|
+
base: string;
|
|
407
|
+
variants?: Record<string, Record<string, string>>;
|
|
408
|
+
defaultVariants?: Record<string, string>;
|
|
409
|
+
compoundVariants?: CompoundVariant[];
|
|
410
|
+
}
|
|
368
411
|
/**
|
|
369
412
|
* Data transform types for build-time content loading
|
|
370
413
|
*/
|
|
@@ -375,6 +418,13 @@ type DataTransform = (typeof DATA_TRANSFORMS)[number];
|
|
|
375
418
|
*/
|
|
376
419
|
declare const DATA_SOURCE_TYPES: readonly ["glob", "file", "api"];
|
|
377
420
|
type DataSourceType = (typeof DATA_SOURCE_TYPES)[number];
|
|
421
|
+
/**
|
|
422
|
+
* Reference to imported components for MDX transformation
|
|
423
|
+
*/
|
|
424
|
+
interface ComponentsRef {
|
|
425
|
+
expr: 'import';
|
|
426
|
+
name: string;
|
|
427
|
+
}
|
|
378
428
|
/**
|
|
379
429
|
* Data source for build-time content loading
|
|
380
430
|
*/
|
|
@@ -384,6 +434,7 @@ interface DataSource {
|
|
|
384
434
|
path?: string;
|
|
385
435
|
url?: string;
|
|
386
436
|
transform?: DataTransform;
|
|
437
|
+
components?: string | ComponentsRef;
|
|
387
438
|
}
|
|
388
439
|
/**
|
|
389
440
|
* Static paths definition for SSG
|
|
@@ -399,6 +450,7 @@ interface RouteDefinition {
|
|
|
399
450
|
path: string;
|
|
400
451
|
title?: Expression;
|
|
401
452
|
layout?: string;
|
|
453
|
+
layoutParams?: Record<string, Expression>;
|
|
402
454
|
meta?: Record<string, Expression>;
|
|
403
455
|
getStaticPaths?: StaticPathsDefinition;
|
|
404
456
|
}
|
|
@@ -419,6 +471,7 @@ interface Program {
|
|
|
419
471
|
route?: RouteDefinition;
|
|
420
472
|
imports?: Record<string, string>;
|
|
421
473
|
data?: Record<string, DataSource>;
|
|
474
|
+
styles?: Record<string, StylePreset>;
|
|
422
475
|
lifecycle?: LifecycleHooks;
|
|
423
476
|
state: Record<string, StateField>;
|
|
424
477
|
actions: ActionDefinition[];
|
|
@@ -433,6 +486,8 @@ type ConstelaAst = Program;
|
|
|
433
486
|
interface LayoutProgram {
|
|
434
487
|
version: '1.0';
|
|
435
488
|
type: 'layout';
|
|
489
|
+
imports?: Record<string, string>;
|
|
490
|
+
importData?: Record<string, unknown>;
|
|
436
491
|
state?: Record<string, StateField>;
|
|
437
492
|
actions?: ActionDefinition[];
|
|
438
493
|
view: ViewNode;
|
|
@@ -521,6 +576,10 @@ declare function isDataExpr(value: unknown): value is DataExpr;
|
|
|
521
576
|
* Checks if value is a ref expression
|
|
522
577
|
*/
|
|
523
578
|
declare function isRefExpr(value: unknown): value is RefExpr;
|
|
579
|
+
/**
|
|
580
|
+
* Checks if value is a style expression
|
|
581
|
+
*/
|
|
582
|
+
declare function isStyleExpr(value: unknown): value is StyleExpr;
|
|
524
583
|
/**
|
|
525
584
|
* Checks if value is a data source
|
|
526
585
|
*/
|
|
@@ -669,14 +728,33 @@ declare function isLifecycleHooks(value: unknown): value is LifecycleHooks;
|
|
|
669
728
|
* This module defines error types, the ConstelaError class,
|
|
670
729
|
* and factory functions for creating specific errors.
|
|
671
730
|
*/
|
|
672
|
-
type ErrorCode = 'SCHEMA_INVALID' | 'UNDEFINED_STATE' | 'UNDEFINED_ACTION' | 'VAR_UNDEFINED' | 'DUPLICATE_ACTION' | 'UNSUPPORTED_VERSION' | 'COMPONENT_NOT_FOUND' | 'COMPONENT_PROP_MISSING' | 'COMPONENT_CYCLE' | 'COMPONENT_PROP_TYPE' | 'PARAM_UNDEFINED' | 'OPERATION_INVALID_FOR_TYPE' | 'OPERATION_MISSING_FIELD' | 'OPERATION_UNKNOWN' | 'EXPR_INVALID_BASE' | 'EXPR_INVALID_CONDITION' | 'EXPR_COND_ELSE_REQUIRED' | 'UNDEFINED_ROUTE_PARAM' | 'ROUTE_NOT_DEFINED' | 'UNDEFINED_IMPORT' | 'IMPORTS_NOT_DEFINED' | 'LAYOUT_MISSING_SLOT' | 'LAYOUT_NOT_FOUND' | 'INVALID_SLOT_NAME' | 'DUPLICATE_SLOT_NAME' | 'DUPLICATE_DEFAULT_SLOT' | 'SLOT_IN_LOOP' | 'INVALID_DATA_SOURCE' | 'UNDEFINED_DATA_SOURCE' | 'DATA_NOT_DEFINED' | 'UNDEFINED_DATA' | 'UNDEFINED_REF' | 'INVALID_STORAGE_OPERATION' | 'INVALID_STORAGE_TYPE' | 'STORAGE_SET_MISSING_VALUE' | 'INVALID_CLIPBOARD_OPERATION' | 'CLIPBOARD_WRITE_MISSING_VALUE' | 'INVALID_NAVIGATE_TARGET';
|
|
731
|
+
type ErrorCode = 'SCHEMA_INVALID' | 'UNDEFINED_STATE' | 'UNDEFINED_ACTION' | 'VAR_UNDEFINED' | 'DUPLICATE_ACTION' | 'UNSUPPORTED_VERSION' | 'COMPONENT_NOT_FOUND' | 'COMPONENT_PROP_MISSING' | 'COMPONENT_CYCLE' | 'COMPONENT_PROP_TYPE' | 'PARAM_UNDEFINED' | 'OPERATION_INVALID_FOR_TYPE' | 'OPERATION_MISSING_FIELD' | 'OPERATION_UNKNOWN' | 'EXPR_INVALID_BASE' | 'EXPR_INVALID_CONDITION' | 'EXPR_COND_ELSE_REQUIRED' | 'UNDEFINED_ROUTE_PARAM' | 'ROUTE_NOT_DEFINED' | 'UNDEFINED_IMPORT' | 'IMPORTS_NOT_DEFINED' | 'LAYOUT_MISSING_SLOT' | 'LAYOUT_NOT_FOUND' | 'INVALID_SLOT_NAME' | 'DUPLICATE_SLOT_NAME' | 'DUPLICATE_DEFAULT_SLOT' | 'SLOT_IN_LOOP' | 'INVALID_DATA_SOURCE' | 'UNDEFINED_DATA_SOURCE' | 'DATA_NOT_DEFINED' | 'UNDEFINED_DATA' | 'UNDEFINED_REF' | 'INVALID_STORAGE_OPERATION' | 'INVALID_STORAGE_TYPE' | 'STORAGE_SET_MISSING_VALUE' | 'INVALID_CLIPBOARD_OPERATION' | 'CLIPBOARD_WRITE_MISSING_VALUE' | 'INVALID_NAVIGATE_TARGET' | 'UNDEFINED_STYLE' | 'UNDEFINED_VARIANT';
|
|
732
|
+
/**
|
|
733
|
+
* Options for creating enhanced ConstelaError instances
|
|
734
|
+
*/
|
|
735
|
+
interface ErrorOptions {
|
|
736
|
+
severity?: 'error' | 'warning' | 'info' | undefined;
|
|
737
|
+
suggestion?: string | undefined;
|
|
738
|
+
expected?: string | undefined;
|
|
739
|
+
actual?: string | undefined;
|
|
740
|
+
context?: {
|
|
741
|
+
availableNames?: string[] | undefined;
|
|
742
|
+
} | undefined;
|
|
743
|
+
}
|
|
673
744
|
/**
|
|
674
745
|
* Custom error class for Constela validation errors
|
|
675
746
|
*/
|
|
676
747
|
declare class ConstelaError extends Error {
|
|
677
748
|
readonly code: ErrorCode;
|
|
678
749
|
readonly path: string | undefined;
|
|
679
|
-
|
|
750
|
+
readonly severity: 'error' | 'warning' | 'info';
|
|
751
|
+
readonly suggestion: string | undefined;
|
|
752
|
+
readonly expected: string | undefined;
|
|
753
|
+
readonly actual: string | undefined;
|
|
754
|
+
readonly context: {
|
|
755
|
+
availableNames?: string[] | undefined;
|
|
756
|
+
} | undefined;
|
|
757
|
+
constructor(code: ErrorCode, message: string, path?: string | undefined, options?: ErrorOptions);
|
|
680
758
|
/**
|
|
681
759
|
* Converts the error to a JSON-serializable object
|
|
682
760
|
*/
|
|
@@ -684,6 +762,13 @@ declare class ConstelaError extends Error {
|
|
|
684
762
|
code: ErrorCode;
|
|
685
763
|
message: string;
|
|
686
764
|
path: string | undefined;
|
|
765
|
+
severity: 'error' | 'warning' | 'info';
|
|
766
|
+
suggestion: string | undefined;
|
|
767
|
+
expected: string | undefined;
|
|
768
|
+
actual: string | undefined;
|
|
769
|
+
context: {
|
|
770
|
+
availableNames?: string[] | undefined;
|
|
771
|
+
} | undefined;
|
|
687
772
|
};
|
|
688
773
|
}
|
|
689
774
|
/**
|
|
@@ -697,11 +782,11 @@ declare function createSchemaError(message: string, path?: string): ConstelaErro
|
|
|
697
782
|
/**
|
|
698
783
|
* Creates an undefined state reference error
|
|
699
784
|
*/
|
|
700
|
-
declare function createUndefinedStateError(stateName: string, path?: string): ConstelaError;
|
|
785
|
+
declare function createUndefinedStateError(stateName: string, path?: string, options?: ErrorOptions): ConstelaError;
|
|
701
786
|
/**
|
|
702
787
|
* Creates an undefined action reference error
|
|
703
788
|
*/
|
|
704
|
-
declare function createUndefinedActionError(actionName: string, path?: string): ConstelaError;
|
|
789
|
+
declare function createUndefinedActionError(actionName: string, path?: string, options?: ErrorOptions): ConstelaError;
|
|
705
790
|
/**
|
|
706
791
|
* Creates a duplicate action name error
|
|
707
792
|
*/
|
|
@@ -717,7 +802,7 @@ declare function createUnsupportedVersionError(version: string): ConstelaError;
|
|
|
717
802
|
/**
|
|
718
803
|
* Creates a component not found error
|
|
719
804
|
*/
|
|
720
|
-
declare function createComponentNotFoundError(name: string, path?: string): ConstelaError;
|
|
805
|
+
declare function createComponentNotFoundError(name: string, path?: string, options?: ErrorOptions): ConstelaError;
|
|
721
806
|
/**
|
|
722
807
|
* Creates a missing required prop error
|
|
723
808
|
*/
|
|
@@ -834,6 +919,28 @@ declare function createClipboardWriteMissingValueError(path?: string): ConstelaE
|
|
|
834
919
|
* Creates an invalid navigate target error
|
|
835
920
|
*/
|
|
836
921
|
declare function createInvalidNavigateTargetError(target: string, path?: string): ConstelaError;
|
|
922
|
+
/**
|
|
923
|
+
* Creates an undefined style reference error
|
|
924
|
+
*/
|
|
925
|
+
declare function createUndefinedStyleError(styleName: string, path?: string, options?: ErrorOptions): ConstelaError;
|
|
926
|
+
/**
|
|
927
|
+
* Creates an undefined variant key error
|
|
928
|
+
*/
|
|
929
|
+
declare function createUndefinedVariantError(variantKey: string, styleName: string, path?: string, options?: ErrorOptions): ConstelaError;
|
|
930
|
+
/**
|
|
931
|
+
* Finds similar names from a set of candidates using Levenshtein distance
|
|
932
|
+
* and prefix matching.
|
|
933
|
+
*
|
|
934
|
+
* Matching strategies:
|
|
935
|
+
* 1. Levenshtein distance <= maxDistance (default: 2)
|
|
936
|
+
* 2. Target is a prefix of candidate with minimum 3 characters
|
|
937
|
+
*
|
|
938
|
+
* @param target - The target string to find similar names for
|
|
939
|
+
* @param candidates - A set of candidate names to search through
|
|
940
|
+
* @param maxDistance - Maximum Levenshtein distance to consider (default: 2)
|
|
941
|
+
* @returns Array of similar names sorted by distance (closest first)
|
|
942
|
+
*/
|
|
943
|
+
declare function findSimilarNames(target: string, candidates: Set<string>, maxDistance?: number): string[];
|
|
837
944
|
|
|
838
945
|
/**
|
|
839
946
|
* AST Validator for Constela
|
|
@@ -893,6 +1000,12 @@ declare const astSchema: {
|
|
|
893
1000
|
readonly view: {
|
|
894
1001
|
readonly $ref: "#/$defs/ViewNode";
|
|
895
1002
|
};
|
|
1003
|
+
readonly styles: {
|
|
1004
|
+
readonly type: "object";
|
|
1005
|
+
readonly additionalProperties: {
|
|
1006
|
+
readonly $ref: "#/$defs/StylePreset";
|
|
1007
|
+
};
|
|
1008
|
+
};
|
|
896
1009
|
readonly components: {
|
|
897
1010
|
readonly type: "object";
|
|
898
1011
|
readonly additionalProperties: {
|
|
@@ -918,6 +1031,10 @@ declare const astSchema: {
|
|
|
918
1031
|
readonly $ref: "#/$defs/CondExpr";
|
|
919
1032
|
}, {
|
|
920
1033
|
readonly $ref: "#/$defs/GetExpr";
|
|
1034
|
+
}, {
|
|
1035
|
+
readonly $ref: "#/$defs/IndexExpr";
|
|
1036
|
+
}, {
|
|
1037
|
+
readonly $ref: "#/$defs/StyleExpr";
|
|
921
1038
|
}];
|
|
922
1039
|
};
|
|
923
1040
|
readonly LitExpr: {
|
|
@@ -956,6 +1073,9 @@ declare const astSchema: {
|
|
|
956
1073
|
readonly name: {
|
|
957
1074
|
readonly type: "string";
|
|
958
1075
|
};
|
|
1076
|
+
readonly path: {
|
|
1077
|
+
readonly type: "string";
|
|
1078
|
+
};
|
|
959
1079
|
};
|
|
960
1080
|
};
|
|
961
1081
|
readonly VarExpr: {
|
|
@@ -1064,6 +1184,86 @@ declare const astSchema: {
|
|
|
1064
1184
|
};
|
|
1065
1185
|
};
|
|
1066
1186
|
};
|
|
1187
|
+
readonly IndexExpr: {
|
|
1188
|
+
readonly type: "object";
|
|
1189
|
+
readonly required: readonly ["expr", "base", "key"];
|
|
1190
|
+
readonly additionalProperties: false;
|
|
1191
|
+
readonly properties: {
|
|
1192
|
+
readonly expr: {
|
|
1193
|
+
readonly type: "string";
|
|
1194
|
+
readonly const: "index";
|
|
1195
|
+
};
|
|
1196
|
+
readonly base: {
|
|
1197
|
+
readonly $ref: "#/$defs/Expression";
|
|
1198
|
+
};
|
|
1199
|
+
readonly key: {
|
|
1200
|
+
readonly $ref: "#/$defs/Expression";
|
|
1201
|
+
};
|
|
1202
|
+
};
|
|
1203
|
+
};
|
|
1204
|
+
readonly StyleExpr: {
|
|
1205
|
+
readonly type: "object";
|
|
1206
|
+
readonly required: readonly ["expr", "name"];
|
|
1207
|
+
readonly additionalProperties: false;
|
|
1208
|
+
readonly properties: {
|
|
1209
|
+
readonly expr: {
|
|
1210
|
+
readonly type: "string";
|
|
1211
|
+
readonly const: "style";
|
|
1212
|
+
};
|
|
1213
|
+
readonly name: {
|
|
1214
|
+
readonly type: "string";
|
|
1215
|
+
};
|
|
1216
|
+
readonly variants: {
|
|
1217
|
+
readonly type: "object";
|
|
1218
|
+
readonly additionalProperties: {
|
|
1219
|
+
readonly $ref: "#/$defs/Expression";
|
|
1220
|
+
};
|
|
1221
|
+
};
|
|
1222
|
+
};
|
|
1223
|
+
};
|
|
1224
|
+
readonly StylePreset: {
|
|
1225
|
+
readonly type: "object";
|
|
1226
|
+
readonly required: readonly ["base"];
|
|
1227
|
+
readonly additionalProperties: false;
|
|
1228
|
+
readonly properties: {
|
|
1229
|
+
readonly base: {
|
|
1230
|
+
readonly type: "string";
|
|
1231
|
+
};
|
|
1232
|
+
readonly variants: {
|
|
1233
|
+
readonly type: "object";
|
|
1234
|
+
readonly additionalProperties: {
|
|
1235
|
+
readonly type: "object";
|
|
1236
|
+
readonly additionalProperties: {
|
|
1237
|
+
readonly type: "string";
|
|
1238
|
+
};
|
|
1239
|
+
};
|
|
1240
|
+
};
|
|
1241
|
+
readonly defaultVariants: {
|
|
1242
|
+
readonly type: "object";
|
|
1243
|
+
readonly additionalProperties: {
|
|
1244
|
+
readonly type: "string";
|
|
1245
|
+
};
|
|
1246
|
+
};
|
|
1247
|
+
readonly compoundVariants: {
|
|
1248
|
+
readonly type: "array";
|
|
1249
|
+
readonly items: {
|
|
1250
|
+
readonly $ref: "#/$defs/CompoundVariant";
|
|
1251
|
+
};
|
|
1252
|
+
};
|
|
1253
|
+
};
|
|
1254
|
+
};
|
|
1255
|
+
readonly CompoundVariant: {
|
|
1256
|
+
readonly type: "object";
|
|
1257
|
+
readonly required: readonly ["class"];
|
|
1258
|
+
readonly additionalProperties: {
|
|
1259
|
+
readonly type: "string";
|
|
1260
|
+
};
|
|
1261
|
+
readonly properties: {
|
|
1262
|
+
readonly class: {
|
|
1263
|
+
readonly type: "string";
|
|
1264
|
+
};
|
|
1265
|
+
};
|
|
1266
|
+
};
|
|
1067
1267
|
readonly StateField: {
|
|
1068
1268
|
readonly oneOf: readonly [{
|
|
1069
1269
|
readonly $ref: "#/$defs/NumberField";
|
|
@@ -1478,4 +1678,4 @@ declare const astSchema: {
|
|
|
1478
1678
|
};
|
|
1479
1679
|
};
|
|
1480
1680
|
|
|
1481
|
-
export { type ActionDefinition, type ActionStep, BINARY_OPERATORS, type BinExpr, type BinaryOperator, type BooleanField, CLIPBOARD_OPERATIONS, type CallStep, type ClipboardOperation, type ClipboardStep, type CodeNode, type ComponentDef, type ComponentNode, type CondExpr, type ConstelaAst, ConstelaError, type ConstelaProgram, DATA_SOURCE_TYPES, DATA_TRANSFORMS, type DataExpr, type DataSource, type DataSourceType, type DataTransform, type DisposeStep, type EachNode, type ElementNode, type ErrorCode, type EventHandler, type Expression, type FetchStep, type GetExpr, HTTP_METHODS, type HttpMethod, type IfNode, type ImportExpr, type ImportStep, type LayoutProgram, type LifecycleHooks, type ListField, type LitExpr, type MarkdownNode, NAVIGATE_TARGETS, type NavigateStep, type NavigateTarget, type NotExpr, type NumberField, type ObjectField, PARAM_TYPES, type ParamDef, type ParamExpr, type ParamType, type Program, type RefExpr, type RouteDefinition, type RouteExpr, STORAGE_OPERATIONS, STORAGE_TYPES, type SetStep, type SlotNode, type StateExpr, type StateField, type StaticPathsDefinition, type StorageOperation, type StorageStep, type StorageType, type StringField, type SubscribeStep, type TextNode, UPDATE_OPERATIONS, type UpdateOperation, type UpdateStep, type ValidationFailure, type ValidationResult, type ValidationSuccess, type VarExpr, type ViewNode, astSchema, createClipboardWriteMissingValueError, createComponentCycleError, createComponentNotFoundError, createComponentPropMissingError, createComponentPropTypeError, createCondElseRequiredError, createDataNotDefinedError, createDuplicateActionError, createDuplicateDefaultSlotError, createDuplicateSlotNameError, createImportsNotDefinedError, createInvalidClipboardOperationError, createInvalidDataSourceError, createInvalidNavigateTargetError, createInvalidSlotNameError, createInvalidStorageOperationError, createInvalidStorageTypeError, createLayoutMissingSlotError, createLayoutNotFoundError, createOperationInvalidForTypeError, createOperationMissingFieldError, createOperationUnknownError, createRouteNotDefinedError, createSchemaError, createSlotInLoopError, createStorageSetMissingValueError, createUndefinedActionError, createUndefinedDataError, createUndefinedDataSourceError, createUndefinedImportError, createUndefinedParamError, createUndefinedRefError, createUndefinedRouteParamError, createUndefinedStateError, createUndefinedVarError, createUnsupportedVersionError, isActionStep, isBinExpr, isBooleanField, isCallStep, isClipboardStep, isCodeNode, isComponentNode, isCondExpr, isConstelaError, isDataExpr, isDataSource, isDisposeStep, isEachNode, isElementNode, isEventHandler, isExpression, isFetchStep, isGetExpr, isIfNode, isImportExpr, isImportStep, isLayoutProgram, isLifecycleHooks, isListField, isLitExpr, isMarkdownNode, isNamedSlotNode, isNavigateStep, isNotExpr, isNumberField, isObjectField, isParamExpr, isRefExpr, isRouteDefinition, isRouteExpr, isSetStep, isSlotNode, isStateExpr, isStateField, isStaticPathsDefinition, isStorageStep, isStringField, isSubscribeStep, isTextNode, isUpdateStep, isVarExpr, isViewNode, validateAst };
|
|
1681
|
+
export { type ActionDefinition, type ActionStep, BINARY_OPERATORS, type BinExpr, type BinaryOperator, type BooleanField, CLIPBOARD_OPERATIONS, type CallStep, type ClipboardOperation, type ClipboardStep, type CodeNode, type ComponentDef, type ComponentNode, type ComponentsRef, type CompoundVariant, type CondExpr, type ConstelaAst, ConstelaError, type ConstelaProgram, DATA_SOURCE_TYPES, DATA_TRANSFORMS, type DataExpr, type DataSource, type DataSourceType, type DataTransform, type DisposeStep, type DomStep, type EachNode, type ElementNode, type ErrorCode, type ErrorOptions, type EventHandler, type Expression, type FetchStep, type GetExpr, HTTP_METHODS, type HttpMethod, type IfNode, type ImportExpr, type ImportStep, type LayoutProgram, type LifecycleHooks, type ListField, type LitExpr, type MarkdownNode, NAVIGATE_TARGETS, type NavigateStep, type NavigateTarget, type NotExpr, type NumberField, type ObjectField, PARAM_TYPES, type ParamDef, type ParamExpr, type ParamType, type Program, type RefExpr, type RouteDefinition, type RouteExpr, STORAGE_OPERATIONS, STORAGE_TYPES, type SetStep, type SlotNode, type StateExpr, type StateField, type StaticPathsDefinition, type StorageOperation, type StorageStep, type StorageType, type StringField, type StyleExpr, type StylePreset, type SubscribeStep, type TextNode, UPDATE_OPERATIONS, type UpdateOperation, type UpdateStep, type ValidationFailure, type ValidationResult, type ValidationSuccess, type VarExpr, type ViewNode, astSchema, createClipboardWriteMissingValueError, createComponentCycleError, createComponentNotFoundError, createComponentPropMissingError, createComponentPropTypeError, createCondElseRequiredError, createDataNotDefinedError, createDuplicateActionError, createDuplicateDefaultSlotError, createDuplicateSlotNameError, createImportsNotDefinedError, createInvalidClipboardOperationError, createInvalidDataSourceError, createInvalidNavigateTargetError, createInvalidSlotNameError, createInvalidStorageOperationError, createInvalidStorageTypeError, createLayoutMissingSlotError, createLayoutNotFoundError, createOperationInvalidForTypeError, createOperationMissingFieldError, createOperationUnknownError, createRouteNotDefinedError, createSchemaError, createSlotInLoopError, createStorageSetMissingValueError, createUndefinedActionError, createUndefinedDataError, createUndefinedDataSourceError, createUndefinedImportError, createUndefinedParamError, createUndefinedRefError, createUndefinedRouteParamError, createUndefinedStateError, createUndefinedStyleError, createUndefinedVarError, createUndefinedVariantError, createUnsupportedVersionError, findSimilarNames, isActionStep, isBinExpr, isBooleanField, isCallStep, isClipboardStep, isCodeNode, isComponentNode, isCondExpr, isConstelaError, isDataExpr, isDataSource, isDisposeStep, isEachNode, isElementNode, isEventHandler, isExpression, isFetchStep, isGetExpr, isIfNode, isImportExpr, isImportStep, isLayoutProgram, isLifecycleHooks, isListField, isLitExpr, isMarkdownNode, isNamedSlotNode, isNavigateStep, isNotExpr, isNumberField, isObjectField, isParamExpr, isRefExpr, isRouteDefinition, isRouteExpr, isSetStep, isSlotNode, isStateExpr, isStateField, isStaticPathsDefinition, isStorageStep, isStringField, isStyleExpr, isSubscribeStep, isTextNode, isUpdateStep, isVarExpr, isViewNode, validateAst };
|
package/dist/index.js
CHANGED
|
@@ -123,6 +123,22 @@ function isRefExpr(value) {
|
|
|
123
123
|
if (value["expr"] !== "ref") return false;
|
|
124
124
|
return typeof value["name"] === "string";
|
|
125
125
|
}
|
|
126
|
+
function isIndexExpr(value) {
|
|
127
|
+
if (!isObject(value)) return false;
|
|
128
|
+
if (value["expr"] !== "index") return false;
|
|
129
|
+
if (!("base" in value)) return false;
|
|
130
|
+
if (!("key" in value)) return false;
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
function isStyleExpr(value) {
|
|
134
|
+
if (!isObject(value)) return false;
|
|
135
|
+
if (value["expr"] !== "style") return false;
|
|
136
|
+
if (typeof value["name"] !== "string") return false;
|
|
137
|
+
if ("variants" in value && value["variants"] !== void 0) {
|
|
138
|
+
if (!isObject(value["variants"])) return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
126
142
|
function isDataSource(value) {
|
|
127
143
|
if (!isObject(value)) return false;
|
|
128
144
|
const type = value["type"];
|
|
@@ -159,7 +175,7 @@ function isRouteDefinition(value) {
|
|
|
159
175
|
return true;
|
|
160
176
|
}
|
|
161
177
|
function isExpression(value) {
|
|
162
|
-
return isLitExpr(value) || isStateExpr(value) || isVarExpr(value) || isBinExpr(value) || isNotExpr(value) || isParamExpr(value) || isCondExpr(value) || isGetExpr(value) || isRouteExpr(value) || isImportExpr(value) || isDataExpr(value) || isRefExpr(value);
|
|
178
|
+
return isLitExpr(value) || isStateExpr(value) || isVarExpr(value) || isBinExpr(value) || isNotExpr(value) || isParamExpr(value) || isCondExpr(value) || isGetExpr(value) || isRouteExpr(value) || isImportExpr(value) || isDataExpr(value) || isRefExpr(value) || isIndexExpr(value) || isStyleExpr(value);
|
|
163
179
|
}
|
|
164
180
|
function isElementNode(value) {
|
|
165
181
|
if (!isObject(value)) return false;
|
|
@@ -375,11 +391,21 @@ function isLifecycleHooks(value) {
|
|
|
375
391
|
var ConstelaError = class _ConstelaError extends Error {
|
|
376
392
|
code;
|
|
377
393
|
path;
|
|
378
|
-
|
|
394
|
+
severity;
|
|
395
|
+
suggestion;
|
|
396
|
+
expected;
|
|
397
|
+
actual;
|
|
398
|
+
context;
|
|
399
|
+
constructor(code, message, path, options) {
|
|
379
400
|
super(message);
|
|
380
401
|
this.name = "ConstelaError";
|
|
381
402
|
this.code = code;
|
|
382
403
|
this.path = path;
|
|
404
|
+
this.severity = options?.severity ?? "error";
|
|
405
|
+
this.suggestion = options?.suggestion;
|
|
406
|
+
this.expected = options?.expected;
|
|
407
|
+
this.actual = options?.actual;
|
|
408
|
+
this.context = options?.context;
|
|
383
409
|
Object.setPrototypeOf(this, _ConstelaError.prototype);
|
|
384
410
|
}
|
|
385
411
|
/**
|
|
@@ -389,7 +415,12 @@ var ConstelaError = class _ConstelaError extends Error {
|
|
|
389
415
|
return {
|
|
390
416
|
code: this.code,
|
|
391
417
|
message: this.message,
|
|
392
|
-
path: this.path
|
|
418
|
+
path: this.path,
|
|
419
|
+
severity: this.severity,
|
|
420
|
+
suggestion: this.suggestion,
|
|
421
|
+
expected: this.expected,
|
|
422
|
+
actual: this.actual,
|
|
423
|
+
context: this.context
|
|
393
424
|
};
|
|
394
425
|
}
|
|
395
426
|
};
|
|
@@ -399,18 +430,20 @@ function isConstelaError(value) {
|
|
|
399
430
|
function createSchemaError(message, path) {
|
|
400
431
|
return new ConstelaError("SCHEMA_INVALID", message, path);
|
|
401
432
|
}
|
|
402
|
-
function createUndefinedStateError(stateName, path) {
|
|
433
|
+
function createUndefinedStateError(stateName, path, options) {
|
|
403
434
|
return new ConstelaError(
|
|
404
435
|
"UNDEFINED_STATE",
|
|
405
436
|
`Undefined state reference: '${stateName}' is not defined in state`,
|
|
406
|
-
path
|
|
437
|
+
path,
|
|
438
|
+
options
|
|
407
439
|
);
|
|
408
440
|
}
|
|
409
|
-
function createUndefinedActionError(actionName, path) {
|
|
441
|
+
function createUndefinedActionError(actionName, path, options) {
|
|
410
442
|
return new ConstelaError(
|
|
411
443
|
"UNDEFINED_ACTION",
|
|
412
444
|
`Undefined action reference: '${actionName}' is not defined in actions`,
|
|
413
|
-
path
|
|
445
|
+
path,
|
|
446
|
+
options
|
|
414
447
|
);
|
|
415
448
|
}
|
|
416
449
|
function createDuplicateActionError(actionName, path) {
|
|
@@ -434,11 +467,12 @@ function createUnsupportedVersionError(version) {
|
|
|
434
467
|
"/version"
|
|
435
468
|
);
|
|
436
469
|
}
|
|
437
|
-
function createComponentNotFoundError(name, path) {
|
|
470
|
+
function createComponentNotFoundError(name, path, options) {
|
|
438
471
|
return new ConstelaError(
|
|
439
472
|
"COMPONENT_NOT_FOUND",
|
|
440
473
|
`Component '${name}' is not defined in components`,
|
|
441
|
-
path
|
|
474
|
+
path,
|
|
475
|
+
options
|
|
442
476
|
);
|
|
443
477
|
}
|
|
444
478
|
function createComponentPropMissingError(componentName, propName, path) {
|
|
@@ -644,13 +678,68 @@ function createInvalidNavigateTargetError(target, path) {
|
|
|
644
678
|
path
|
|
645
679
|
);
|
|
646
680
|
}
|
|
681
|
+
function createUndefinedStyleError(styleName, path, options) {
|
|
682
|
+
return new ConstelaError(
|
|
683
|
+
"UNDEFINED_STYLE",
|
|
684
|
+
`Undefined style reference: '${styleName}' is not defined in styles`,
|
|
685
|
+
path,
|
|
686
|
+
options
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
function createUndefinedVariantError(variantKey, styleName, path, options) {
|
|
690
|
+
return new ConstelaError(
|
|
691
|
+
"UNDEFINED_VARIANT",
|
|
692
|
+
`Undefined variant key: '${variantKey}' is not defined in style '${styleName}'`,
|
|
693
|
+
path,
|
|
694
|
+
options
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
function levenshteinDistance(a, b) {
|
|
698
|
+
const aLen = a.length;
|
|
699
|
+
const bLen = b.length;
|
|
700
|
+
let prev = Array.from({ length: bLen + 1 }, (_, j) => j);
|
|
701
|
+
let curr = new Array(bLen + 1).fill(0);
|
|
702
|
+
for (let i = 1; i <= aLen; i++) {
|
|
703
|
+
curr[0] = i;
|
|
704
|
+
for (let j = 1; j <= bLen; j++) {
|
|
705
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
706
|
+
const deletion = (prev[j] ?? 0) + 1;
|
|
707
|
+
const insertion = (curr[j - 1] ?? 0) + 1;
|
|
708
|
+
const substitution = (prev[j - 1] ?? 0) + cost;
|
|
709
|
+
curr[j] = Math.min(deletion, insertion, substitution);
|
|
710
|
+
}
|
|
711
|
+
[prev, curr] = [curr, prev];
|
|
712
|
+
}
|
|
713
|
+
return prev[bLen] ?? 0;
|
|
714
|
+
}
|
|
715
|
+
function findSimilarNames(target, candidates, maxDistance = 2) {
|
|
716
|
+
const results = [];
|
|
717
|
+
for (const candidate of candidates) {
|
|
718
|
+
const distance = levenshteinDistance(target, candidate);
|
|
719
|
+
if (distance <= maxDistance) {
|
|
720
|
+
results.push({ name: candidate, distance });
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
if (target.length >= 3 && candidate.toLowerCase().startsWith(target.toLowerCase())) {
|
|
724
|
+
const prefixDistance = candidate.length - target.length;
|
|
725
|
+
results.push({ name: candidate, distance: prefixDistance });
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
results.sort((a, b) => {
|
|
729
|
+
if (a.distance !== b.distance) {
|
|
730
|
+
return a.distance - b.distance;
|
|
731
|
+
}
|
|
732
|
+
return a.name.localeCompare(b.name);
|
|
733
|
+
});
|
|
734
|
+
return results.map((r) => r.name);
|
|
735
|
+
}
|
|
647
736
|
|
|
648
737
|
// src/schema/validator.ts
|
|
649
738
|
function isObject2(value) {
|
|
650
739
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
651
740
|
}
|
|
652
741
|
var VALID_VIEW_KINDS = ["element", "text", "if", "each", "component", "slot", "markdown", "code"];
|
|
653
|
-
var VALID_EXPR_TYPES = ["lit", "state", "var", "bin", "not", "param", "cond", "get"];
|
|
742
|
+
var VALID_EXPR_TYPES = ["lit", "state", "var", "bin", "not", "param", "cond", "get", "style"];
|
|
654
743
|
var VALID_PARAM_TYPES = ["string", "number", "boolean", "json"];
|
|
655
744
|
var VALID_ACTION_TYPES = ["set", "update", "fetch"];
|
|
656
745
|
var VALID_STATE_TYPES = ["number", "string", "list", "boolean", "object"];
|
|
@@ -777,7 +866,7 @@ function validateExpression(expr, path) {
|
|
|
777
866
|
return { path: path + "/expr", message: "expr is required" };
|
|
778
867
|
}
|
|
779
868
|
if (!VALID_EXPR_TYPES.includes(exprType)) {
|
|
780
|
-
return { path: path + "/expr", message: "must be one of: lit, state, var, bin, not, param, cond, get" };
|
|
869
|
+
return { path: path + "/expr", message: "must be one of: lit, state, var, bin, not, param, cond, get, style" };
|
|
781
870
|
}
|
|
782
871
|
switch (exprType) {
|
|
783
872
|
case "lit":
|
|
@@ -851,6 +940,20 @@ function validateExpression(expr, path) {
|
|
|
851
940
|
return { path: path + "/path", message: "path is required" };
|
|
852
941
|
}
|
|
853
942
|
return validateExpression(expr["base"], path + "/base");
|
|
943
|
+
case "style":
|
|
944
|
+
if (typeof expr["name"] !== "string") {
|
|
945
|
+
return { path: path + "/name", message: "name is required" };
|
|
946
|
+
}
|
|
947
|
+
if ("variants" in expr && expr["variants"] !== void 0) {
|
|
948
|
+
if (!isObject2(expr["variants"])) {
|
|
949
|
+
return { path: path + "/variants", message: "variants must be an object" };
|
|
950
|
+
}
|
|
951
|
+
for (const [variantKey, variantValue] of Object.entries(expr["variants"])) {
|
|
952
|
+
const error = validateExpression(variantValue, path + "/variants/" + variantKey);
|
|
953
|
+
if (error) return error;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
break;
|
|
854
957
|
}
|
|
855
958
|
return null;
|
|
856
959
|
}
|
|
@@ -985,6 +1088,61 @@ function validateComponentDef(def, path) {
|
|
|
985
1088
|
if (viewError) return viewError;
|
|
986
1089
|
return null;
|
|
987
1090
|
}
|
|
1091
|
+
function validateStylePreset(preset, path) {
|
|
1092
|
+
if (!isObject2(preset)) {
|
|
1093
|
+
return { path, message: "must be an object" };
|
|
1094
|
+
}
|
|
1095
|
+
if (!("base" in preset)) {
|
|
1096
|
+
return { path: path + "/base", message: "base is required" };
|
|
1097
|
+
}
|
|
1098
|
+
if (typeof preset["base"] !== "string") {
|
|
1099
|
+
return { path: path + "/base", message: "base must be a string" };
|
|
1100
|
+
}
|
|
1101
|
+
if ("variants" in preset && preset["variants"] !== void 0) {
|
|
1102
|
+
if (!isObject2(preset["variants"])) {
|
|
1103
|
+
return { path: path + "/variants", message: "variants must be an object" };
|
|
1104
|
+
}
|
|
1105
|
+
for (const [variantKey, variantOptions] of Object.entries(preset["variants"])) {
|
|
1106
|
+
if (!isObject2(variantOptions)) {
|
|
1107
|
+
return { path: path + "/variants/" + variantKey, message: "variant options must be an object" };
|
|
1108
|
+
}
|
|
1109
|
+
for (const [optionKey, optionValue] of Object.entries(variantOptions)) {
|
|
1110
|
+
if (typeof optionValue !== "string") {
|
|
1111
|
+
return { path: path + "/variants/" + variantKey + "/" + optionKey, message: "variant value must be a string" };
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
if ("defaultVariants" in preset && preset["defaultVariants"] !== void 0) {
|
|
1117
|
+
if (!isObject2(preset["defaultVariants"])) {
|
|
1118
|
+
return { path: path + "/defaultVariants", message: "defaultVariants must be an object" };
|
|
1119
|
+
}
|
|
1120
|
+
const variantKeys = isObject2(preset["variants"]) ? Object.keys(preset["variants"]) : [];
|
|
1121
|
+
for (const [key, value] of Object.entries(preset["defaultVariants"])) {
|
|
1122
|
+
if (typeof value !== "string") {
|
|
1123
|
+
return { path: path + "/defaultVariants/" + key, message: "default variant value must be a string" };
|
|
1124
|
+
}
|
|
1125
|
+
if (!variantKeys.includes(key)) {
|
|
1126
|
+
return { path: path + "/defaultVariants/" + key, message: `'${key}' is not a defined variant` };
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
if ("compoundVariants" in preset && preset["compoundVariants"] !== void 0) {
|
|
1131
|
+
if (!Array.isArray(preset["compoundVariants"])) {
|
|
1132
|
+
return { path: path + "/compoundVariants", message: "compoundVariants must be an array" };
|
|
1133
|
+
}
|
|
1134
|
+
for (let i = 0; i < preset["compoundVariants"].length; i++) {
|
|
1135
|
+
const compound = preset["compoundVariants"][i];
|
|
1136
|
+
if (!isObject2(compound)) {
|
|
1137
|
+
return { path: path + "/compoundVariants/" + i, message: "compound variant must be an object" };
|
|
1138
|
+
}
|
|
1139
|
+
if (typeof compound["class"] !== "string") {
|
|
1140
|
+
return { path: path + "/compoundVariants/" + i + "/class", message: "class is required" };
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
988
1146
|
function customValidateAst(input) {
|
|
989
1147
|
if (isObject2(input["state"])) {
|
|
990
1148
|
for (const [name, field] of Object.entries(input["state"])) {
|
|
@@ -992,6 +1150,12 @@ function customValidateAst(input) {
|
|
|
992
1150
|
if (error) return error;
|
|
993
1151
|
}
|
|
994
1152
|
}
|
|
1153
|
+
if ("styles" in input && isObject2(input["styles"])) {
|
|
1154
|
+
for (const [name, preset] of Object.entries(input["styles"])) {
|
|
1155
|
+
const error = validateStylePreset(preset, "/styles/" + name);
|
|
1156
|
+
if (error) return error;
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
995
1159
|
if (Array.isArray(input["actions"])) {
|
|
996
1160
|
for (let i = 0; i < input["actions"].length; i++) {
|
|
997
1161
|
const action = input["actions"][i];
|
|
@@ -1015,6 +1179,15 @@ function customValidateAst(input) {
|
|
|
1015
1179
|
}
|
|
1016
1180
|
return null;
|
|
1017
1181
|
}
|
|
1182
|
+
function createErrorOptionsWithSuggestion(name, availableNames) {
|
|
1183
|
+
const availableNamesArray = Array.from(availableNames);
|
|
1184
|
+
const similarNames = findSimilarNames(name, availableNames);
|
|
1185
|
+
const suggestion = similarNames.length > 0 ? `Did you mean '${similarNames[0]}'?` : void 0;
|
|
1186
|
+
return {
|
|
1187
|
+
suggestion,
|
|
1188
|
+
context: { availableNames: availableNamesArray }
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1018
1191
|
function collectSemanticContext(ast) {
|
|
1019
1192
|
const stateNames = /* @__PURE__ */ new Set();
|
|
1020
1193
|
const actionNames = /* @__PURE__ */ new Set();
|
|
@@ -1036,7 +1209,8 @@ function validateStateReferences(node, path, stateNames) {
|
|
|
1036
1209
|
if (!isObject2(node)) return null;
|
|
1037
1210
|
if (node["expr"] === "state" && typeof node["name"] === "string") {
|
|
1038
1211
|
if (!stateNames.has(node["name"])) {
|
|
1039
|
-
|
|
1212
|
+
const errorOptions = createErrorOptionsWithSuggestion(node["name"], stateNames);
|
|
1213
|
+
return createUndefinedStateError(node["name"], path + "/" + node["name"], errorOptions);
|
|
1040
1214
|
}
|
|
1041
1215
|
}
|
|
1042
1216
|
for (const [key, value] of Object.entries(node)) {
|
|
@@ -1062,7 +1236,8 @@ function validateActionTargets(ast, stateNames) {
|
|
|
1062
1236
|
if (!isObject2(step)) continue;
|
|
1063
1237
|
if ((step["do"] === "set" || step["do"] === "update") && typeof step["target"] === "string") {
|
|
1064
1238
|
if (!stateNames.has(step["target"])) {
|
|
1065
|
-
|
|
1239
|
+
const errorOptions = createErrorOptionsWithSuggestion(step["target"], stateNames);
|
|
1240
|
+
return createUndefinedStateError(step["target"], "/actions/" + i + "/steps/" + j + "/target", errorOptions);
|
|
1066
1241
|
}
|
|
1067
1242
|
}
|
|
1068
1243
|
}
|
|
@@ -1076,7 +1251,8 @@ function validateActionReferences(node, path, actionNames) {
|
|
|
1076
1251
|
if (isObject2(propValue) && "event" in propValue && "action" in propValue) {
|
|
1077
1252
|
const actionName = propValue["action"];
|
|
1078
1253
|
if (typeof actionName === "string" && !actionNames.has(actionName)) {
|
|
1079
|
-
|
|
1254
|
+
const errorOptions = createErrorOptionsWithSuggestion(actionName, actionNames);
|
|
1255
|
+
return createUndefinedActionError(actionName, path + "/props/" + propName, errorOptions);
|
|
1080
1256
|
}
|
|
1081
1257
|
}
|
|
1082
1258
|
}
|
|
@@ -1223,6 +1399,10 @@ var astSchema = {
|
|
|
1223
1399
|
view: {
|
|
1224
1400
|
$ref: "#/$defs/ViewNode"
|
|
1225
1401
|
},
|
|
1402
|
+
styles: {
|
|
1403
|
+
type: "object",
|
|
1404
|
+
additionalProperties: { $ref: "#/$defs/StylePreset" }
|
|
1405
|
+
},
|
|
1226
1406
|
components: {
|
|
1227
1407
|
type: "object",
|
|
1228
1408
|
additionalProperties: { $ref: "#/$defs/ComponentDef" }
|
|
@@ -1239,7 +1419,9 @@ var astSchema = {
|
|
|
1239
1419
|
{ $ref: "#/$defs/NotExpr" },
|
|
1240
1420
|
{ $ref: "#/$defs/ParamExpr" },
|
|
1241
1421
|
{ $ref: "#/$defs/CondExpr" },
|
|
1242
|
-
{ $ref: "#/$defs/GetExpr" }
|
|
1422
|
+
{ $ref: "#/$defs/GetExpr" },
|
|
1423
|
+
{ $ref: "#/$defs/IndexExpr" },
|
|
1424
|
+
{ $ref: "#/$defs/StyleExpr" }
|
|
1243
1425
|
]
|
|
1244
1426
|
},
|
|
1245
1427
|
LitExpr: {
|
|
@@ -1265,7 +1447,8 @@ var astSchema = {
|
|
|
1265
1447
|
additionalProperties: false,
|
|
1266
1448
|
properties: {
|
|
1267
1449
|
expr: { type: "string", const: "state" },
|
|
1268
|
-
name: { type: "string" }
|
|
1450
|
+
name: { type: "string" },
|
|
1451
|
+
path: { type: "string" }
|
|
1269
1452
|
}
|
|
1270
1453
|
},
|
|
1271
1454
|
VarExpr: {
|
|
@@ -1332,6 +1515,61 @@ var astSchema = {
|
|
|
1332
1515
|
path: { type: "string" }
|
|
1333
1516
|
}
|
|
1334
1517
|
},
|
|
1518
|
+
IndexExpr: {
|
|
1519
|
+
type: "object",
|
|
1520
|
+
required: ["expr", "base", "key"],
|
|
1521
|
+
additionalProperties: false,
|
|
1522
|
+
properties: {
|
|
1523
|
+
expr: { type: "string", const: "index" },
|
|
1524
|
+
base: { $ref: "#/$defs/Expression" },
|
|
1525
|
+
key: { $ref: "#/$defs/Expression" }
|
|
1526
|
+
}
|
|
1527
|
+
},
|
|
1528
|
+
StyleExpr: {
|
|
1529
|
+
type: "object",
|
|
1530
|
+
required: ["expr", "name"],
|
|
1531
|
+
additionalProperties: false,
|
|
1532
|
+
properties: {
|
|
1533
|
+
expr: { type: "string", const: "style" },
|
|
1534
|
+
name: { type: "string" },
|
|
1535
|
+
variants: {
|
|
1536
|
+
type: "object",
|
|
1537
|
+
additionalProperties: { $ref: "#/$defs/Expression" }
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
},
|
|
1541
|
+
// ==================== Style Presets ====================
|
|
1542
|
+
StylePreset: {
|
|
1543
|
+
type: "object",
|
|
1544
|
+
required: ["base"],
|
|
1545
|
+
additionalProperties: false,
|
|
1546
|
+
properties: {
|
|
1547
|
+
base: { type: "string" },
|
|
1548
|
+
variants: {
|
|
1549
|
+
type: "object",
|
|
1550
|
+
additionalProperties: {
|
|
1551
|
+
type: "object",
|
|
1552
|
+
additionalProperties: { type: "string" }
|
|
1553
|
+
}
|
|
1554
|
+
},
|
|
1555
|
+
defaultVariants: {
|
|
1556
|
+
type: "object",
|
|
1557
|
+
additionalProperties: { type: "string" }
|
|
1558
|
+
},
|
|
1559
|
+
compoundVariants: {
|
|
1560
|
+
type: "array",
|
|
1561
|
+
items: { $ref: "#/$defs/CompoundVariant" }
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
},
|
|
1565
|
+
CompoundVariant: {
|
|
1566
|
+
type: "object",
|
|
1567
|
+
required: ["class"],
|
|
1568
|
+
additionalProperties: { type: "string" },
|
|
1569
|
+
properties: {
|
|
1570
|
+
class: { type: "string" }
|
|
1571
|
+
}
|
|
1572
|
+
},
|
|
1335
1573
|
// ==================== State Fields ====================
|
|
1336
1574
|
StateField: {
|
|
1337
1575
|
oneOf: [
|
|
@@ -1654,8 +1892,11 @@ export {
|
|
|
1654
1892
|
createUndefinedRefError,
|
|
1655
1893
|
createUndefinedRouteParamError,
|
|
1656
1894
|
createUndefinedStateError,
|
|
1895
|
+
createUndefinedStyleError,
|
|
1657
1896
|
createUndefinedVarError,
|
|
1897
|
+
createUndefinedVariantError,
|
|
1658
1898
|
createUnsupportedVersionError,
|
|
1899
|
+
findSimilarNames,
|
|
1659
1900
|
isActionStep,
|
|
1660
1901
|
isBinExpr,
|
|
1661
1902
|
isBooleanField,
|
|
@@ -1698,6 +1939,7 @@ export {
|
|
|
1698
1939
|
isStaticPathsDefinition,
|
|
1699
1940
|
isStorageStep,
|
|
1700
1941
|
isStringField,
|
|
1942
|
+
isStyleExpr,
|
|
1701
1943
|
isSubscribeStep,
|
|
1702
1944
|
isTextNode,
|
|
1703
1945
|
isUpdateStep,
|