@constela/core 0.3.1 → 0.3.2
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/dist/index.d.ts +2 -1
- package/dist/index.js +579 -604
- package/package.json +2 -4
package/dist/index.d.ts
CHANGED
|
@@ -479,7 +479,7 @@ declare function createCondElseRequiredError(path?: string): ConstelaError;
|
|
|
479
479
|
* AST Validator for Constela
|
|
480
480
|
*
|
|
481
481
|
* This module provides validation for Constela AST structures using
|
|
482
|
-
*
|
|
482
|
+
* custom validation and semantic validation.
|
|
483
483
|
*/
|
|
484
484
|
|
|
485
485
|
interface ValidationSuccess {
|
|
@@ -503,6 +503,7 @@ declare function validateAst(input: unknown): ValidationResult;
|
|
|
503
503
|
* JSON Schema for Constela AST
|
|
504
504
|
*
|
|
505
505
|
* This module defines the JSON Schema for validating Constela AST structures.
|
|
506
|
+
* The schema is kept for documentation and external tooling purposes.
|
|
506
507
|
*/
|
|
507
508
|
declare const astSchema: {
|
|
508
509
|
readonly $schema: "http://json-schema.org/draft-07/schema#";
|
package/dist/index.js
CHANGED
|
@@ -324,595 +324,193 @@ function createCondElseRequiredError(path) {
|
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
// src/schema/validator.ts
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
var
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
327
|
+
function isObject2(value) {
|
|
328
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
329
|
+
}
|
|
330
|
+
var VALID_VIEW_KINDS = ["element", "text", "if", "each", "component", "slot"];
|
|
331
|
+
var VALID_EXPR_TYPES = ["lit", "state", "var", "bin", "not", "param"];
|
|
332
|
+
var VALID_PARAM_TYPES = ["string", "number", "boolean", "json"];
|
|
333
|
+
var VALID_ACTION_TYPES = ["set", "update", "fetch"];
|
|
334
|
+
var VALID_STATE_TYPES = ["number", "string", "list", "boolean", "object"];
|
|
335
|
+
var VALID_BIN_OPS = BINARY_OPERATORS;
|
|
336
|
+
var VALID_UPDATE_OPS = UPDATE_OPERATIONS;
|
|
337
|
+
var VALID_HTTP_METHODS = HTTP_METHODS;
|
|
338
|
+
function validateViewNode(node, path) {
|
|
339
|
+
if (!isObject2(node)) {
|
|
340
|
+
return { path, message: "must be an object" };
|
|
341
|
+
}
|
|
342
|
+
const kind = node["kind"];
|
|
343
|
+
if (typeof kind !== "string") {
|
|
344
|
+
return { path: path + "/kind", message: "kind is required" };
|
|
345
|
+
}
|
|
346
|
+
if (!VALID_VIEW_KINDS.includes(kind)) {
|
|
347
|
+
return { path: path + "/kind", message: "must be one of: element, text, if, each, component, slot" };
|
|
348
|
+
}
|
|
349
|
+
switch (kind) {
|
|
350
|
+
case "element":
|
|
351
|
+
if (typeof node["tag"] !== "string") {
|
|
352
|
+
return { path: path + "/tag", message: "tag is required" };
|
|
347
353
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
354
|
+
if (node["props"] !== void 0 && isObject2(node["props"])) {
|
|
355
|
+
for (const [propName, propValue] of Object.entries(node["props"])) {
|
|
356
|
+
if (isObject2(propValue) && "event" in propValue) {
|
|
357
|
+
} else {
|
|
358
|
+
const error = validateExpression(propValue, path + "/props/" + propName);
|
|
359
|
+
if (error) return error;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
353
362
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
components: {
|
|
359
|
-
type: "object",
|
|
360
|
-
additionalProperties: { $ref: "#/$defs/ComponentDef" }
|
|
361
|
-
}
|
|
362
|
-
},
|
|
363
|
-
$defs: {
|
|
364
|
-
// ==================== Expressions ====================
|
|
365
|
-
Expression: {
|
|
366
|
-
oneOf: [
|
|
367
|
-
{ $ref: "#/$defs/LitExpr" },
|
|
368
|
-
{ $ref: "#/$defs/StateExpr" },
|
|
369
|
-
{ $ref: "#/$defs/VarExpr" },
|
|
370
|
-
{ $ref: "#/$defs/BinExpr" },
|
|
371
|
-
{ $ref: "#/$defs/NotExpr" },
|
|
372
|
-
{ $ref: "#/$defs/ParamExpr" },
|
|
373
|
-
{ $ref: "#/$defs/CondExpr" },
|
|
374
|
-
{ $ref: "#/$defs/GetExpr" }
|
|
375
|
-
]
|
|
376
|
-
},
|
|
377
|
-
LitExpr: {
|
|
378
|
-
type: "object",
|
|
379
|
-
required: ["expr", "value"],
|
|
380
|
-
additionalProperties: false,
|
|
381
|
-
properties: {
|
|
382
|
-
expr: { type: "string", const: "lit" },
|
|
383
|
-
value: {
|
|
384
|
-
oneOf: [
|
|
385
|
-
{ type: "string" },
|
|
386
|
-
{ type: "number" },
|
|
387
|
-
{ type: "boolean" },
|
|
388
|
-
{ type: "null" },
|
|
389
|
-
{ type: "array" }
|
|
390
|
-
]
|
|
363
|
+
if (Array.isArray(node["children"])) {
|
|
364
|
+
for (let i = 0; i < node["children"].length; i++) {
|
|
365
|
+
const error = validateViewNode(node["children"][i], path + "/children/" + i);
|
|
366
|
+
if (error) return error;
|
|
391
367
|
}
|
|
392
368
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
additionalProperties: false,
|
|
398
|
-
properties: {
|
|
399
|
-
expr: { type: "string", const: "state" },
|
|
400
|
-
name: { type: "string" }
|
|
369
|
+
break;
|
|
370
|
+
case "text":
|
|
371
|
+
if (!("value" in node)) {
|
|
372
|
+
return { path: path + "/value", message: "value is required" };
|
|
401
373
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
additionalProperties: false,
|
|
407
|
-
properties: {
|
|
408
|
-
expr: { type: "string", const: "var" },
|
|
409
|
-
name: { type: "string" },
|
|
410
|
-
path: { type: "string" }
|
|
374
|
+
return validateExpression(node["value"], path + "/value");
|
|
375
|
+
case "if":
|
|
376
|
+
if (!("condition" in node)) {
|
|
377
|
+
return { path: path + "/condition", message: "condition is required" };
|
|
411
378
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
type: "object",
|
|
415
|
-
required: ["expr", "op", "left", "right"],
|
|
416
|
-
additionalProperties: false,
|
|
417
|
-
properties: {
|
|
418
|
-
expr: { type: "string", const: "bin" },
|
|
419
|
-
op: {
|
|
420
|
-
type: "string",
|
|
421
|
-
enum: ["+", "-", "*", "/", "==", "!=", "<", "<=", ">", ">=", "&&", "||"]
|
|
422
|
-
},
|
|
423
|
-
left: { $ref: "#/$defs/Expression" },
|
|
424
|
-
right: { $ref: "#/$defs/Expression" }
|
|
379
|
+
if (!("then" in node)) {
|
|
380
|
+
return { path: path + "/then", message: "then is required" };
|
|
425
381
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
382
|
+
{
|
|
383
|
+
const condError = validateExpression(node["condition"], path + "/condition");
|
|
384
|
+
if (condError) return condError;
|
|
385
|
+
const thenError = validateViewNode(node["then"], path + "/then");
|
|
386
|
+
if (thenError) return thenError;
|
|
387
|
+
if ("else" in node) {
|
|
388
|
+
const elseError = validateViewNode(node["else"], path + "/else");
|
|
389
|
+
if (elseError) return elseError;
|
|
390
|
+
}
|
|
434
391
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
additionalProperties: false,
|
|
440
|
-
properties: {
|
|
441
|
-
expr: { type: "string", const: "param" },
|
|
442
|
-
name: { type: "string" },
|
|
443
|
-
path: { type: "string" }
|
|
392
|
+
break;
|
|
393
|
+
case "each":
|
|
394
|
+
if (!("items" in node)) {
|
|
395
|
+
return { path: path + "/items", message: "items is required" };
|
|
444
396
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
type: "object",
|
|
448
|
-
required: ["expr", "if", "then", "else"],
|
|
449
|
-
additionalProperties: false,
|
|
450
|
-
properties: {
|
|
451
|
-
expr: { type: "string", const: "cond" },
|
|
452
|
-
if: { $ref: "#/$defs/Expression" },
|
|
453
|
-
then: { $ref: "#/$defs/Expression" },
|
|
454
|
-
else: { $ref: "#/$defs/Expression" }
|
|
397
|
+
if (typeof node["as"] !== "string") {
|
|
398
|
+
return { path: path + "/as", message: "as is required" };
|
|
455
399
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
type: "object",
|
|
459
|
-
required: ["expr", "base", "path"],
|
|
460
|
-
additionalProperties: false,
|
|
461
|
-
properties: {
|
|
462
|
-
expr: { type: "string", const: "get" },
|
|
463
|
-
base: { $ref: "#/$defs/Expression" },
|
|
464
|
-
path: { type: "string" }
|
|
400
|
+
if (!("body" in node)) {
|
|
401
|
+
return { path: path + "/body", message: "body is required" };
|
|
465
402
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
{ $ref: "#/$defs/StringField" },
|
|
472
|
-
{ $ref: "#/$defs/ListField" },
|
|
473
|
-
{ $ref: "#/$defs/BooleanField" },
|
|
474
|
-
{ $ref: "#/$defs/ObjectField" }
|
|
475
|
-
]
|
|
476
|
-
},
|
|
477
|
-
NumberField: {
|
|
478
|
-
type: "object",
|
|
479
|
-
required: ["type", "initial"],
|
|
480
|
-
additionalProperties: false,
|
|
481
|
-
properties: {
|
|
482
|
-
type: { type: "string", const: "number" },
|
|
483
|
-
initial: { type: "number" }
|
|
403
|
+
{
|
|
404
|
+
const itemsError = validateExpression(node["items"], path + "/items");
|
|
405
|
+
if (itemsError) return itemsError;
|
|
406
|
+
const bodyError = validateViewNode(node["body"], path + "/body");
|
|
407
|
+
if (bodyError) return bodyError;
|
|
484
408
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
additionalProperties: false,
|
|
490
|
-
properties: {
|
|
491
|
-
type: { type: "string", const: "string" },
|
|
492
|
-
initial: { type: "string" }
|
|
409
|
+
break;
|
|
410
|
+
case "component":
|
|
411
|
+
if (typeof node["name"] !== "string") {
|
|
412
|
+
return { path: path + "/name", message: "name is required" };
|
|
493
413
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
properties: {
|
|
500
|
-
type: { type: "string", const: "list" },
|
|
501
|
-
initial: { type: "array" }
|
|
414
|
+
if (node["props"] !== void 0 && isObject2(node["props"])) {
|
|
415
|
+
for (const [propName, propValue] of Object.entries(node["props"])) {
|
|
416
|
+
const error = validateExpression(propValue, path + "/props/" + propName);
|
|
417
|
+
if (error) return error;
|
|
418
|
+
}
|
|
502
419
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
properties: {
|
|
509
|
-
type: { type: "string", const: "boolean" },
|
|
510
|
-
initial: { type: "boolean" }
|
|
420
|
+
if (Array.isArray(node["children"])) {
|
|
421
|
+
for (let i = 0; i < node["children"].length; i++) {
|
|
422
|
+
const error = validateViewNode(node["children"][i], path + "/children/" + i);
|
|
423
|
+
if (error) return error;
|
|
424
|
+
}
|
|
511
425
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
426
|
+
break;
|
|
427
|
+
case "slot":
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
function validateExpression(expr, path) {
|
|
433
|
+
if (!isObject2(expr)) {
|
|
434
|
+
return { path, message: "must be an object" };
|
|
435
|
+
}
|
|
436
|
+
const exprType = expr["expr"];
|
|
437
|
+
if (typeof exprType !== "string") {
|
|
438
|
+
return { path: path + "/expr", message: "expr is required" };
|
|
439
|
+
}
|
|
440
|
+
if (!VALID_EXPR_TYPES.includes(exprType)) {
|
|
441
|
+
return { path: path + "/expr", message: "must be one of: lit, state, var, bin, not, param" };
|
|
442
|
+
}
|
|
443
|
+
switch (exprType) {
|
|
444
|
+
case "lit":
|
|
445
|
+
if (!("value" in expr)) {
|
|
446
|
+
return { path: path + "/value", message: "value is required" };
|
|
520
447
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
{
|
|
526
|
-
{ $ref: "#/$defs/UpdateStep" },
|
|
527
|
-
{ $ref: "#/$defs/FetchStep" }
|
|
528
|
-
]
|
|
529
|
-
},
|
|
530
|
-
SetStep: {
|
|
531
|
-
type: "object",
|
|
532
|
-
required: ["do", "target", "value"],
|
|
533
|
-
additionalProperties: false,
|
|
534
|
-
properties: {
|
|
535
|
-
do: { type: "string", const: "set" },
|
|
536
|
-
target: { type: "string" },
|
|
537
|
-
value: { $ref: "#/$defs/Expression" }
|
|
448
|
+
break;
|
|
449
|
+
case "state":
|
|
450
|
+
case "var":
|
|
451
|
+
if (typeof expr["name"] !== "string") {
|
|
452
|
+
return { path: path + "/name", message: "name is required" };
|
|
538
453
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
additionalProperties: false,
|
|
544
|
-
properties: {
|
|
545
|
-
do: { type: "string", const: "update" },
|
|
546
|
-
target: { type: "string" },
|
|
547
|
-
operation: {
|
|
548
|
-
type: "string",
|
|
549
|
-
enum: ["increment", "decrement", "push", "pop", "remove", "toggle", "merge", "replaceAt", "insertAt", "splice"]
|
|
550
|
-
},
|
|
551
|
-
value: { $ref: "#/$defs/Expression" },
|
|
552
|
-
index: { $ref: "#/$defs/Expression" },
|
|
553
|
-
deleteCount: { $ref: "#/$defs/Expression" }
|
|
454
|
+
break;
|
|
455
|
+
case "bin":
|
|
456
|
+
if (!("op" in expr)) {
|
|
457
|
+
return { path: path + "/op", message: "op is required" };
|
|
554
458
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
type: "object",
|
|
558
|
-
required: ["do", "url"],
|
|
559
|
-
additionalProperties: false,
|
|
560
|
-
properties: {
|
|
561
|
-
do: { type: "string", const: "fetch" },
|
|
562
|
-
url: { $ref: "#/$defs/Expression" },
|
|
563
|
-
method: {
|
|
564
|
-
type: "string",
|
|
565
|
-
enum: ["GET", "POST", "PUT", "DELETE"]
|
|
566
|
-
},
|
|
567
|
-
body: { $ref: "#/$defs/Expression" },
|
|
568
|
-
result: { type: "string" },
|
|
569
|
-
onSuccess: {
|
|
570
|
-
type: "array",
|
|
571
|
-
items: { $ref: "#/$defs/ActionStep" }
|
|
572
|
-
},
|
|
573
|
-
onError: {
|
|
574
|
-
type: "array",
|
|
575
|
-
items: { $ref: "#/$defs/ActionStep" }
|
|
576
|
-
}
|
|
459
|
+
if (!VALID_BIN_OPS.includes(expr["op"])) {
|
|
460
|
+
return { path: path + "/op", message: "must be a valid operator" };
|
|
577
461
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
EventHandler: {
|
|
581
|
-
type: "object",
|
|
582
|
-
required: ["event", "action"],
|
|
583
|
-
additionalProperties: false,
|
|
584
|
-
properties: {
|
|
585
|
-
event: { type: "string" },
|
|
586
|
-
action: { type: "string" },
|
|
587
|
-
payload: { $ref: "#/$defs/Expression" }
|
|
462
|
+
if (!("left" in expr)) {
|
|
463
|
+
return { path: path + "/left", message: "left is required" };
|
|
588
464
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
ActionDefinition: {
|
|
592
|
-
type: "object",
|
|
593
|
-
required: ["name", "steps"],
|
|
594
|
-
additionalProperties: false,
|
|
595
|
-
properties: {
|
|
596
|
-
name: { type: "string" },
|
|
597
|
-
steps: {
|
|
598
|
-
type: "array",
|
|
599
|
-
items: { $ref: "#/$defs/ActionStep" }
|
|
600
|
-
}
|
|
465
|
+
if (!("right" in expr)) {
|
|
466
|
+
return { path: path + "/right", message: "right is required" };
|
|
601
467
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
{ $ref: "#/$defs/TextNode" },
|
|
608
|
-
{ $ref: "#/$defs/IfNode" },
|
|
609
|
-
{ $ref: "#/$defs/EachNode" },
|
|
610
|
-
{ $ref: "#/$defs/ComponentNode" },
|
|
611
|
-
{ $ref: "#/$defs/SlotNode" }
|
|
612
|
-
]
|
|
613
|
-
},
|
|
614
|
-
ElementNode: {
|
|
615
|
-
type: "object",
|
|
616
|
-
required: ["kind", "tag"],
|
|
617
|
-
additionalProperties: false,
|
|
618
|
-
properties: {
|
|
619
|
-
kind: { type: "string", const: "element" },
|
|
620
|
-
tag: { type: "string" },
|
|
621
|
-
props: {
|
|
622
|
-
type: "object",
|
|
623
|
-
additionalProperties: {
|
|
624
|
-
oneOf: [
|
|
625
|
-
{ $ref: "#/$defs/Expression" },
|
|
626
|
-
{ $ref: "#/$defs/EventHandler" }
|
|
627
|
-
]
|
|
628
|
-
}
|
|
629
|
-
},
|
|
630
|
-
children: {
|
|
631
|
-
type: "array",
|
|
632
|
-
items: { $ref: "#/$defs/ViewNode" }
|
|
633
|
-
}
|
|
468
|
+
{
|
|
469
|
+
const leftError = validateExpression(expr["left"], path + "/left");
|
|
470
|
+
if (leftError) return leftError;
|
|
471
|
+
const rightError = validateExpression(expr["right"], path + "/right");
|
|
472
|
+
if (rightError) return rightError;
|
|
634
473
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
additionalProperties: false,
|
|
640
|
-
properties: {
|
|
641
|
-
kind: { type: "string", const: "text" },
|
|
642
|
-
value: { $ref: "#/$defs/Expression" }
|
|
474
|
+
break;
|
|
475
|
+
case "not":
|
|
476
|
+
if (!("operand" in expr)) {
|
|
477
|
+
return { path: path + "/operand", message: "operand is required" };
|
|
643
478
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
additionalProperties: false,
|
|
649
|
-
properties: {
|
|
650
|
-
kind: { type: "string", const: "if" },
|
|
651
|
-
condition: { $ref: "#/$defs/Expression" },
|
|
652
|
-
then: { $ref: "#/$defs/ViewNode" },
|
|
653
|
-
else: { $ref: "#/$defs/ViewNode" }
|
|
479
|
+
return validateExpression(expr["operand"], path + "/operand");
|
|
480
|
+
case "param":
|
|
481
|
+
if (typeof expr["name"] !== "string") {
|
|
482
|
+
return { path: path + "/name", message: "name is required" };
|
|
654
483
|
}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
type: "object",
|
|
658
|
-
required: ["kind", "items", "as", "body"],
|
|
659
|
-
additionalProperties: false,
|
|
660
|
-
properties: {
|
|
661
|
-
kind: { type: "string", const: "each" },
|
|
662
|
-
items: { $ref: "#/$defs/Expression" },
|
|
663
|
-
as: { type: "string" },
|
|
664
|
-
index: { type: "string" },
|
|
665
|
-
key: { $ref: "#/$defs/Expression" },
|
|
666
|
-
body: { $ref: "#/$defs/ViewNode" }
|
|
484
|
+
if ("path" in expr && typeof expr["path"] !== "string") {
|
|
485
|
+
return { path: path + "/path", message: "path must be a string" };
|
|
667
486
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
function validateActionStep(step, path) {
|
|
492
|
+
if (!isObject2(step)) {
|
|
493
|
+
return { path, message: "must be an object" };
|
|
494
|
+
}
|
|
495
|
+
const doType = step["do"];
|
|
496
|
+
if (typeof doType !== "string") {
|
|
497
|
+
return { path: path + "/do", message: "do is required" };
|
|
498
|
+
}
|
|
499
|
+
if (!VALID_ACTION_TYPES.includes(doType)) {
|
|
500
|
+
return { path: path + "/do", message: "must be one of: set, update, fetch" };
|
|
501
|
+
}
|
|
502
|
+
switch (doType) {
|
|
503
|
+
case "set":
|
|
504
|
+
if (typeof step["target"] !== "string") {
|
|
505
|
+
return { path: path + "/target", message: "target is required" };
|
|
684
506
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
type: "object",
|
|
688
|
-
required: ["kind"],
|
|
689
|
-
additionalProperties: false,
|
|
690
|
-
properties: {
|
|
691
|
-
kind: { type: "string", const: "slot" }
|
|
507
|
+
if (!("value" in step)) {
|
|
508
|
+
return { path: path + "/value", message: "value is required" };
|
|
692
509
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
required: ["type"],
|
|
698
|
-
additionalProperties: false,
|
|
699
|
-
properties: {
|
|
700
|
-
type: {
|
|
701
|
-
type: "string",
|
|
702
|
-
enum: ["string", "number", "boolean", "json"]
|
|
703
|
-
},
|
|
704
|
-
required: { type: "boolean" }
|
|
705
|
-
}
|
|
706
|
-
},
|
|
707
|
-
ComponentDef: {
|
|
708
|
-
type: "object",
|
|
709
|
-
required: ["view"],
|
|
710
|
-
additionalProperties: false,
|
|
711
|
-
properties: {
|
|
712
|
-
params: {
|
|
713
|
-
type: "object",
|
|
714
|
-
additionalProperties: { $ref: "#/$defs/ParamDef" }
|
|
715
|
-
},
|
|
716
|
-
view: { $ref: "#/$defs/ViewNode" }
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
|
|
722
|
-
// src/schema/validator.ts
|
|
723
|
-
var ajv = new Ajv({
|
|
724
|
-
allErrors: true,
|
|
725
|
-
verbose: true,
|
|
726
|
-
strict: false
|
|
727
|
-
});
|
|
728
|
-
var validate = ajv.compile(astSchema);
|
|
729
|
-
function isObject2(value) {
|
|
730
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
731
|
-
}
|
|
732
|
-
var VALID_VIEW_KINDS = ["element", "text", "if", "each", "component", "slot"];
|
|
733
|
-
var VALID_EXPR_TYPES = ["lit", "state", "var", "bin", "not", "param"];
|
|
734
|
-
var VALID_PARAM_TYPES = ["string", "number", "boolean", "json"];
|
|
735
|
-
var VALID_ACTION_TYPES = ["set", "update", "fetch"];
|
|
736
|
-
var VALID_STATE_TYPES = ["number", "string", "list", "boolean", "object"];
|
|
737
|
-
var VALID_BIN_OPS = BINARY_OPERATORS;
|
|
738
|
-
var VALID_UPDATE_OPS = UPDATE_OPERATIONS;
|
|
739
|
-
var VALID_HTTP_METHODS = HTTP_METHODS;
|
|
740
|
-
function validateViewNode(node, path) {
|
|
741
|
-
if (!isObject2(node)) {
|
|
742
|
-
return { path, message: "must be an object" };
|
|
743
|
-
}
|
|
744
|
-
const kind = node["kind"];
|
|
745
|
-
if (typeof kind !== "string") {
|
|
746
|
-
return { path: path + "/kind", message: "kind is required" };
|
|
747
|
-
}
|
|
748
|
-
if (!VALID_VIEW_KINDS.includes(kind)) {
|
|
749
|
-
return { path: path + "/kind", message: "must be one of: element, text, if, each, component, slot" };
|
|
750
|
-
}
|
|
751
|
-
switch (kind) {
|
|
752
|
-
case "element":
|
|
753
|
-
if (typeof node["tag"] !== "string") {
|
|
754
|
-
return { path: path + "/tag", message: "tag is required" };
|
|
755
|
-
}
|
|
756
|
-
if (node["props"] !== void 0 && isObject2(node["props"])) {
|
|
757
|
-
for (const [propName, propValue] of Object.entries(node["props"])) {
|
|
758
|
-
if (isObject2(propValue) && "event" in propValue) {
|
|
759
|
-
} else {
|
|
760
|
-
const error = validateExpression(propValue, path + "/props/" + propName);
|
|
761
|
-
if (error) return error;
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
if (Array.isArray(node["children"])) {
|
|
766
|
-
for (let i = 0; i < node["children"].length; i++) {
|
|
767
|
-
const error = validateViewNode(node["children"][i], path + "/children/" + i);
|
|
768
|
-
if (error) return error;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
break;
|
|
772
|
-
case "text":
|
|
773
|
-
if (!("value" in node)) {
|
|
774
|
-
return { path: path + "/value", message: "value is required" };
|
|
775
|
-
}
|
|
776
|
-
return validateExpression(node["value"], path + "/value");
|
|
777
|
-
case "if":
|
|
778
|
-
if (!("condition" in node)) {
|
|
779
|
-
return { path: path + "/condition", message: "condition is required" };
|
|
780
|
-
}
|
|
781
|
-
if (!("then" in node)) {
|
|
782
|
-
return { path: path + "/then", message: "then is required" };
|
|
783
|
-
}
|
|
784
|
-
{
|
|
785
|
-
const condError = validateExpression(node["condition"], path + "/condition");
|
|
786
|
-
if (condError) return condError;
|
|
787
|
-
const thenError = validateViewNode(node["then"], path + "/then");
|
|
788
|
-
if (thenError) return thenError;
|
|
789
|
-
if ("else" in node) {
|
|
790
|
-
const elseError = validateViewNode(node["else"], path + "/else");
|
|
791
|
-
if (elseError) return elseError;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
break;
|
|
795
|
-
case "each":
|
|
796
|
-
if (!("items" in node)) {
|
|
797
|
-
return { path: path + "/items", message: "items is required" };
|
|
798
|
-
}
|
|
799
|
-
if (typeof node["as"] !== "string") {
|
|
800
|
-
return { path: path + "/as", message: "as is required" };
|
|
801
|
-
}
|
|
802
|
-
if (!("body" in node)) {
|
|
803
|
-
return { path: path + "/body", message: "body is required" };
|
|
804
|
-
}
|
|
805
|
-
{
|
|
806
|
-
const itemsError = validateExpression(node["items"], path + "/items");
|
|
807
|
-
if (itemsError) return itemsError;
|
|
808
|
-
const bodyError = validateViewNode(node["body"], path + "/body");
|
|
809
|
-
if (bodyError) return bodyError;
|
|
810
|
-
}
|
|
811
|
-
break;
|
|
812
|
-
case "component":
|
|
813
|
-
if (typeof node["name"] !== "string") {
|
|
814
|
-
return { path: path + "/name", message: "name is required" };
|
|
815
|
-
}
|
|
816
|
-
if (node["props"] !== void 0 && isObject2(node["props"])) {
|
|
817
|
-
for (const [propName, propValue] of Object.entries(node["props"])) {
|
|
818
|
-
const error = validateExpression(propValue, path + "/props/" + propName);
|
|
819
|
-
if (error) return error;
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
if (Array.isArray(node["children"])) {
|
|
823
|
-
for (let i = 0; i < node["children"].length; i++) {
|
|
824
|
-
const error = validateViewNode(node["children"][i], path + "/children/" + i);
|
|
825
|
-
if (error) return error;
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
break;
|
|
829
|
-
case "slot":
|
|
830
|
-
break;
|
|
831
|
-
}
|
|
832
|
-
return null;
|
|
833
|
-
}
|
|
834
|
-
function validateExpression(expr, path) {
|
|
835
|
-
if (!isObject2(expr)) {
|
|
836
|
-
return { path, message: "must be an object" };
|
|
837
|
-
}
|
|
838
|
-
const exprType = expr["expr"];
|
|
839
|
-
if (typeof exprType !== "string") {
|
|
840
|
-
return { path: path + "/expr", message: "expr is required" };
|
|
841
|
-
}
|
|
842
|
-
if (!VALID_EXPR_TYPES.includes(exprType)) {
|
|
843
|
-
return { path: path + "/expr", message: "must be one of: lit, state, var, bin, not, param" };
|
|
844
|
-
}
|
|
845
|
-
switch (exprType) {
|
|
846
|
-
case "lit":
|
|
847
|
-
if (!("value" in expr)) {
|
|
848
|
-
return { path: path + "/value", message: "value is required" };
|
|
849
|
-
}
|
|
850
|
-
break;
|
|
851
|
-
case "state":
|
|
852
|
-
case "var":
|
|
853
|
-
if (typeof expr["name"] !== "string") {
|
|
854
|
-
return { path: path + "/name", message: "name is required" };
|
|
855
|
-
}
|
|
856
|
-
break;
|
|
857
|
-
case "bin":
|
|
858
|
-
if (!("op" in expr)) {
|
|
859
|
-
return { path: path + "/op", message: "op is required" };
|
|
860
|
-
}
|
|
861
|
-
if (!VALID_BIN_OPS.includes(expr["op"])) {
|
|
862
|
-
return { path: path + "/op", message: "must be a valid operator" };
|
|
863
|
-
}
|
|
864
|
-
if (!("left" in expr)) {
|
|
865
|
-
return { path: path + "/left", message: "left is required" };
|
|
866
|
-
}
|
|
867
|
-
if (!("right" in expr)) {
|
|
868
|
-
return { path: path + "/right", message: "right is required" };
|
|
869
|
-
}
|
|
870
|
-
{
|
|
871
|
-
const leftError = validateExpression(expr["left"], path + "/left");
|
|
872
|
-
if (leftError) return leftError;
|
|
873
|
-
const rightError = validateExpression(expr["right"], path + "/right");
|
|
874
|
-
if (rightError) return rightError;
|
|
875
|
-
}
|
|
876
|
-
break;
|
|
877
|
-
case "not":
|
|
878
|
-
if (!("operand" in expr)) {
|
|
879
|
-
return { path: path + "/operand", message: "operand is required" };
|
|
880
|
-
}
|
|
881
|
-
return validateExpression(expr["operand"], path + "/operand");
|
|
882
|
-
case "param":
|
|
883
|
-
if (typeof expr["name"] !== "string") {
|
|
884
|
-
return { path: path + "/name", message: "name is required" };
|
|
885
|
-
}
|
|
886
|
-
if ("path" in expr && typeof expr["path"] !== "string") {
|
|
887
|
-
return { path: path + "/path", message: "path must be a string" };
|
|
888
|
-
}
|
|
889
|
-
break;
|
|
890
|
-
}
|
|
891
|
-
return null;
|
|
892
|
-
}
|
|
893
|
-
function validateActionStep(step, path) {
|
|
894
|
-
if (!isObject2(step)) {
|
|
895
|
-
return { path, message: "must be an object" };
|
|
896
|
-
}
|
|
897
|
-
const doType = step["do"];
|
|
898
|
-
if (typeof doType !== "string") {
|
|
899
|
-
return { path: path + "/do", message: "do is required" };
|
|
900
|
-
}
|
|
901
|
-
if (!VALID_ACTION_TYPES.includes(doType)) {
|
|
902
|
-
return { path: path + "/do", message: "must be one of: set, update, fetch" };
|
|
903
|
-
}
|
|
904
|
-
switch (doType) {
|
|
905
|
-
case "set":
|
|
906
|
-
if (typeof step["target"] !== "string") {
|
|
907
|
-
return { path: path + "/target", message: "target is required" };
|
|
908
|
-
}
|
|
909
|
-
if (!("value" in step)) {
|
|
910
|
-
return { path: path + "/value", message: "value is required" };
|
|
911
|
-
}
|
|
912
|
-
return validateExpression(step["value"], path + "/value");
|
|
913
|
-
case "update":
|
|
914
|
-
if (typeof step["target"] !== "string") {
|
|
915
|
-
return { path: path + "/target", message: "target is required" };
|
|
510
|
+
return validateExpression(step["value"], path + "/value");
|
|
511
|
+
case "update":
|
|
512
|
+
if (typeof step["target"] !== "string") {
|
|
513
|
+
return { path: path + "/target", message: "target is required" };
|
|
916
514
|
}
|
|
917
515
|
if (!("operation" in step)) {
|
|
918
516
|
return { path: path + "/operation", message: "operation is required" };
|
|
@@ -1051,20 +649,6 @@ function customValidateAst(input) {
|
|
|
1051
649
|
}
|
|
1052
650
|
return null;
|
|
1053
651
|
}
|
|
1054
|
-
function findTopLevelRequiredError(errors) {
|
|
1055
|
-
for (const error of errors) {
|
|
1056
|
-
if (error.keyword === "required" && error.instancePath === "") {
|
|
1057
|
-
const params = error.params;
|
|
1058
|
-
if (typeof params["missingProperty"] === "string") {
|
|
1059
|
-
return {
|
|
1060
|
-
path: "/" + params["missingProperty"],
|
|
1061
|
-
message: error.message ?? "Required field missing"
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
return null;
|
|
1067
|
-
}
|
|
1068
652
|
function collectSemanticContext(ast) {
|
|
1069
653
|
const stateNames = /* @__PURE__ */ new Set();
|
|
1070
654
|
const actionNames = /* @__PURE__ */ new Set();
|
|
@@ -1200,37 +784,35 @@ function validateAst(input) {
|
|
|
1200
784
|
};
|
|
1201
785
|
}
|
|
1202
786
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
)
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
787
|
+
if (!("version" in input)) {
|
|
788
|
+
return {
|
|
789
|
+
ok: false,
|
|
790
|
+
error: createSchemaError("must have required property 'version'", "/version")
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
if (!("state" in input)) {
|
|
794
|
+
return {
|
|
795
|
+
ok: false,
|
|
796
|
+
error: createSchemaError("must have required property 'state'", "/state")
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
if (!("actions" in input)) {
|
|
800
|
+
return {
|
|
801
|
+
ok: false,
|
|
802
|
+
error: createSchemaError("must have required property 'actions'", "/actions")
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
if (!("view" in input)) {
|
|
806
|
+
return {
|
|
807
|
+
ok: false,
|
|
808
|
+
error: createSchemaError("must have required property 'view'", "/view")
|
|
809
|
+
};
|
|
1229
810
|
}
|
|
1230
|
-
|
|
811
|
+
const customError = customValidateAst(input);
|
|
812
|
+
if (customError) {
|
|
1231
813
|
return {
|
|
1232
814
|
ok: false,
|
|
1233
|
-
error: createSchemaError(
|
|
815
|
+
error: createSchemaError(customError.message, customError.path)
|
|
1234
816
|
};
|
|
1235
817
|
}
|
|
1236
818
|
const semanticError = performSemanticValidation(input);
|
|
@@ -1245,6 +827,399 @@ function validateAst(input) {
|
|
|
1245
827
|
ast: input
|
|
1246
828
|
};
|
|
1247
829
|
}
|
|
830
|
+
|
|
831
|
+
// src/schema/ast.schema.ts
|
|
832
|
+
var astSchema = {
|
|
833
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
834
|
+
$id: "https://constela.dev/schemas/ast.json",
|
|
835
|
+
title: "Constela AST",
|
|
836
|
+
description: "Schema for Constela UI framework AST",
|
|
837
|
+
type: "object",
|
|
838
|
+
required: ["version", "state", "actions", "view"],
|
|
839
|
+
additionalProperties: false,
|
|
840
|
+
properties: {
|
|
841
|
+
version: {
|
|
842
|
+
type: "string",
|
|
843
|
+
const: "1.0"
|
|
844
|
+
},
|
|
845
|
+
state: {
|
|
846
|
+
type: "object",
|
|
847
|
+
additionalProperties: {
|
|
848
|
+
$ref: "#/$defs/StateField"
|
|
849
|
+
}
|
|
850
|
+
},
|
|
851
|
+
actions: {
|
|
852
|
+
type: "array",
|
|
853
|
+
items: {
|
|
854
|
+
$ref: "#/$defs/ActionDefinition"
|
|
855
|
+
}
|
|
856
|
+
},
|
|
857
|
+
view: {
|
|
858
|
+
$ref: "#/$defs/ViewNode"
|
|
859
|
+
},
|
|
860
|
+
components: {
|
|
861
|
+
type: "object",
|
|
862
|
+
additionalProperties: { $ref: "#/$defs/ComponentDef" }
|
|
863
|
+
}
|
|
864
|
+
},
|
|
865
|
+
$defs: {
|
|
866
|
+
// ==================== Expressions ====================
|
|
867
|
+
Expression: {
|
|
868
|
+
oneOf: [
|
|
869
|
+
{ $ref: "#/$defs/LitExpr" },
|
|
870
|
+
{ $ref: "#/$defs/StateExpr" },
|
|
871
|
+
{ $ref: "#/$defs/VarExpr" },
|
|
872
|
+
{ $ref: "#/$defs/BinExpr" },
|
|
873
|
+
{ $ref: "#/$defs/NotExpr" },
|
|
874
|
+
{ $ref: "#/$defs/ParamExpr" },
|
|
875
|
+
{ $ref: "#/$defs/CondExpr" },
|
|
876
|
+
{ $ref: "#/$defs/GetExpr" }
|
|
877
|
+
]
|
|
878
|
+
},
|
|
879
|
+
LitExpr: {
|
|
880
|
+
type: "object",
|
|
881
|
+
required: ["expr", "value"],
|
|
882
|
+
additionalProperties: false,
|
|
883
|
+
properties: {
|
|
884
|
+
expr: { type: "string", const: "lit" },
|
|
885
|
+
value: {
|
|
886
|
+
oneOf: [
|
|
887
|
+
{ type: "string" },
|
|
888
|
+
{ type: "number" },
|
|
889
|
+
{ type: "boolean" },
|
|
890
|
+
{ type: "null" },
|
|
891
|
+
{ type: "array" }
|
|
892
|
+
]
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
StateExpr: {
|
|
897
|
+
type: "object",
|
|
898
|
+
required: ["expr", "name"],
|
|
899
|
+
additionalProperties: false,
|
|
900
|
+
properties: {
|
|
901
|
+
expr: { type: "string", const: "state" },
|
|
902
|
+
name: { type: "string" }
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
VarExpr: {
|
|
906
|
+
type: "object",
|
|
907
|
+
required: ["expr", "name"],
|
|
908
|
+
additionalProperties: false,
|
|
909
|
+
properties: {
|
|
910
|
+
expr: { type: "string", const: "var" },
|
|
911
|
+
name: { type: "string" },
|
|
912
|
+
path: { type: "string" }
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
BinExpr: {
|
|
916
|
+
type: "object",
|
|
917
|
+
required: ["expr", "op", "left", "right"],
|
|
918
|
+
additionalProperties: false,
|
|
919
|
+
properties: {
|
|
920
|
+
expr: { type: "string", const: "bin" },
|
|
921
|
+
op: {
|
|
922
|
+
type: "string",
|
|
923
|
+
enum: ["+", "-", "*", "/", "==", "!=", "<", "<=", ">", ">=", "&&", "||"]
|
|
924
|
+
},
|
|
925
|
+
left: { $ref: "#/$defs/Expression" },
|
|
926
|
+
right: { $ref: "#/$defs/Expression" }
|
|
927
|
+
}
|
|
928
|
+
},
|
|
929
|
+
NotExpr: {
|
|
930
|
+
type: "object",
|
|
931
|
+
required: ["expr", "operand"],
|
|
932
|
+
additionalProperties: false,
|
|
933
|
+
properties: {
|
|
934
|
+
expr: { type: "string", const: "not" },
|
|
935
|
+
operand: { $ref: "#/$defs/Expression" }
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
ParamExpr: {
|
|
939
|
+
type: "object",
|
|
940
|
+
required: ["expr", "name"],
|
|
941
|
+
additionalProperties: false,
|
|
942
|
+
properties: {
|
|
943
|
+
expr: { type: "string", const: "param" },
|
|
944
|
+
name: { type: "string" },
|
|
945
|
+
path: { type: "string" }
|
|
946
|
+
}
|
|
947
|
+
},
|
|
948
|
+
CondExpr: {
|
|
949
|
+
type: "object",
|
|
950
|
+
required: ["expr", "if", "then", "else"],
|
|
951
|
+
additionalProperties: false,
|
|
952
|
+
properties: {
|
|
953
|
+
expr: { type: "string", const: "cond" },
|
|
954
|
+
if: { $ref: "#/$defs/Expression" },
|
|
955
|
+
then: { $ref: "#/$defs/Expression" },
|
|
956
|
+
else: { $ref: "#/$defs/Expression" }
|
|
957
|
+
}
|
|
958
|
+
},
|
|
959
|
+
GetExpr: {
|
|
960
|
+
type: "object",
|
|
961
|
+
required: ["expr", "base", "path"],
|
|
962
|
+
additionalProperties: false,
|
|
963
|
+
properties: {
|
|
964
|
+
expr: { type: "string", const: "get" },
|
|
965
|
+
base: { $ref: "#/$defs/Expression" },
|
|
966
|
+
path: { type: "string" }
|
|
967
|
+
}
|
|
968
|
+
},
|
|
969
|
+
// ==================== State Fields ====================
|
|
970
|
+
StateField: {
|
|
971
|
+
oneOf: [
|
|
972
|
+
{ $ref: "#/$defs/NumberField" },
|
|
973
|
+
{ $ref: "#/$defs/StringField" },
|
|
974
|
+
{ $ref: "#/$defs/ListField" },
|
|
975
|
+
{ $ref: "#/$defs/BooleanField" },
|
|
976
|
+
{ $ref: "#/$defs/ObjectField" }
|
|
977
|
+
]
|
|
978
|
+
},
|
|
979
|
+
NumberField: {
|
|
980
|
+
type: "object",
|
|
981
|
+
required: ["type", "initial"],
|
|
982
|
+
additionalProperties: false,
|
|
983
|
+
properties: {
|
|
984
|
+
type: { type: "string", const: "number" },
|
|
985
|
+
initial: { type: "number" }
|
|
986
|
+
}
|
|
987
|
+
},
|
|
988
|
+
StringField: {
|
|
989
|
+
type: "object",
|
|
990
|
+
required: ["type", "initial"],
|
|
991
|
+
additionalProperties: false,
|
|
992
|
+
properties: {
|
|
993
|
+
type: { type: "string", const: "string" },
|
|
994
|
+
initial: { type: "string" }
|
|
995
|
+
}
|
|
996
|
+
},
|
|
997
|
+
ListField: {
|
|
998
|
+
type: "object",
|
|
999
|
+
required: ["type", "initial"],
|
|
1000
|
+
additionalProperties: false,
|
|
1001
|
+
properties: {
|
|
1002
|
+
type: { type: "string", const: "list" },
|
|
1003
|
+
initial: { type: "array" }
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
BooleanField: {
|
|
1007
|
+
type: "object",
|
|
1008
|
+
required: ["type", "initial"],
|
|
1009
|
+
additionalProperties: false,
|
|
1010
|
+
properties: {
|
|
1011
|
+
type: { type: "string", const: "boolean" },
|
|
1012
|
+
initial: { type: "boolean" }
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
ObjectField: {
|
|
1016
|
+
type: "object",
|
|
1017
|
+
required: ["type", "initial"],
|
|
1018
|
+
additionalProperties: false,
|
|
1019
|
+
properties: {
|
|
1020
|
+
type: { type: "string", const: "object" },
|
|
1021
|
+
initial: { type: "object" }
|
|
1022
|
+
}
|
|
1023
|
+
},
|
|
1024
|
+
// ==================== Action Steps ====================
|
|
1025
|
+
ActionStep: {
|
|
1026
|
+
oneOf: [
|
|
1027
|
+
{ $ref: "#/$defs/SetStep" },
|
|
1028
|
+
{ $ref: "#/$defs/UpdateStep" },
|
|
1029
|
+
{ $ref: "#/$defs/FetchStep" }
|
|
1030
|
+
]
|
|
1031
|
+
},
|
|
1032
|
+
SetStep: {
|
|
1033
|
+
type: "object",
|
|
1034
|
+
required: ["do", "target", "value"],
|
|
1035
|
+
additionalProperties: false,
|
|
1036
|
+
properties: {
|
|
1037
|
+
do: { type: "string", const: "set" },
|
|
1038
|
+
target: { type: "string" },
|
|
1039
|
+
value: { $ref: "#/$defs/Expression" }
|
|
1040
|
+
}
|
|
1041
|
+
},
|
|
1042
|
+
UpdateStep: {
|
|
1043
|
+
type: "object",
|
|
1044
|
+
required: ["do", "target", "operation"],
|
|
1045
|
+
additionalProperties: false,
|
|
1046
|
+
properties: {
|
|
1047
|
+
do: { type: "string", const: "update" },
|
|
1048
|
+
target: { type: "string" },
|
|
1049
|
+
operation: {
|
|
1050
|
+
type: "string",
|
|
1051
|
+
enum: ["increment", "decrement", "push", "pop", "remove", "toggle", "merge", "replaceAt", "insertAt", "splice"]
|
|
1052
|
+
},
|
|
1053
|
+
value: { $ref: "#/$defs/Expression" },
|
|
1054
|
+
index: { $ref: "#/$defs/Expression" },
|
|
1055
|
+
deleteCount: { $ref: "#/$defs/Expression" }
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
FetchStep: {
|
|
1059
|
+
type: "object",
|
|
1060
|
+
required: ["do", "url"],
|
|
1061
|
+
additionalProperties: false,
|
|
1062
|
+
properties: {
|
|
1063
|
+
do: { type: "string", const: "fetch" },
|
|
1064
|
+
url: { $ref: "#/$defs/Expression" },
|
|
1065
|
+
method: {
|
|
1066
|
+
type: "string",
|
|
1067
|
+
enum: ["GET", "POST", "PUT", "DELETE"]
|
|
1068
|
+
},
|
|
1069
|
+
body: { $ref: "#/$defs/Expression" },
|
|
1070
|
+
result: { type: "string" },
|
|
1071
|
+
onSuccess: {
|
|
1072
|
+
type: "array",
|
|
1073
|
+
items: { $ref: "#/$defs/ActionStep" }
|
|
1074
|
+
},
|
|
1075
|
+
onError: {
|
|
1076
|
+
type: "array",
|
|
1077
|
+
items: { $ref: "#/$defs/ActionStep" }
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
},
|
|
1081
|
+
// ==================== Event Handler ====================
|
|
1082
|
+
EventHandler: {
|
|
1083
|
+
type: "object",
|
|
1084
|
+
required: ["event", "action"],
|
|
1085
|
+
additionalProperties: false,
|
|
1086
|
+
properties: {
|
|
1087
|
+
event: { type: "string" },
|
|
1088
|
+
action: { type: "string" },
|
|
1089
|
+
payload: { $ref: "#/$defs/Expression" }
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
// ==================== Action Definition ====================
|
|
1093
|
+
ActionDefinition: {
|
|
1094
|
+
type: "object",
|
|
1095
|
+
required: ["name", "steps"],
|
|
1096
|
+
additionalProperties: false,
|
|
1097
|
+
properties: {
|
|
1098
|
+
name: { type: "string" },
|
|
1099
|
+
steps: {
|
|
1100
|
+
type: "array",
|
|
1101
|
+
items: { $ref: "#/$defs/ActionStep" }
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
},
|
|
1105
|
+
// ==================== View Nodes ====================
|
|
1106
|
+
ViewNode: {
|
|
1107
|
+
oneOf: [
|
|
1108
|
+
{ $ref: "#/$defs/ElementNode" },
|
|
1109
|
+
{ $ref: "#/$defs/TextNode" },
|
|
1110
|
+
{ $ref: "#/$defs/IfNode" },
|
|
1111
|
+
{ $ref: "#/$defs/EachNode" },
|
|
1112
|
+
{ $ref: "#/$defs/ComponentNode" },
|
|
1113
|
+
{ $ref: "#/$defs/SlotNode" }
|
|
1114
|
+
]
|
|
1115
|
+
},
|
|
1116
|
+
ElementNode: {
|
|
1117
|
+
type: "object",
|
|
1118
|
+
required: ["kind", "tag"],
|
|
1119
|
+
additionalProperties: false,
|
|
1120
|
+
properties: {
|
|
1121
|
+
kind: { type: "string", const: "element" },
|
|
1122
|
+
tag: { type: "string" },
|
|
1123
|
+
props: {
|
|
1124
|
+
type: "object",
|
|
1125
|
+
additionalProperties: {
|
|
1126
|
+
oneOf: [
|
|
1127
|
+
{ $ref: "#/$defs/Expression" },
|
|
1128
|
+
{ $ref: "#/$defs/EventHandler" }
|
|
1129
|
+
]
|
|
1130
|
+
}
|
|
1131
|
+
},
|
|
1132
|
+
children: {
|
|
1133
|
+
type: "array",
|
|
1134
|
+
items: { $ref: "#/$defs/ViewNode" }
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
TextNode: {
|
|
1139
|
+
type: "object",
|
|
1140
|
+
required: ["kind", "value"],
|
|
1141
|
+
additionalProperties: false,
|
|
1142
|
+
properties: {
|
|
1143
|
+
kind: { type: "string", const: "text" },
|
|
1144
|
+
value: { $ref: "#/$defs/Expression" }
|
|
1145
|
+
}
|
|
1146
|
+
},
|
|
1147
|
+
IfNode: {
|
|
1148
|
+
type: "object",
|
|
1149
|
+
required: ["kind", "condition", "then"],
|
|
1150
|
+
additionalProperties: false,
|
|
1151
|
+
properties: {
|
|
1152
|
+
kind: { type: "string", const: "if" },
|
|
1153
|
+
condition: { $ref: "#/$defs/Expression" },
|
|
1154
|
+
then: { $ref: "#/$defs/ViewNode" },
|
|
1155
|
+
else: { $ref: "#/$defs/ViewNode" }
|
|
1156
|
+
}
|
|
1157
|
+
},
|
|
1158
|
+
EachNode: {
|
|
1159
|
+
type: "object",
|
|
1160
|
+
required: ["kind", "items", "as", "body"],
|
|
1161
|
+
additionalProperties: false,
|
|
1162
|
+
properties: {
|
|
1163
|
+
kind: { type: "string", const: "each" },
|
|
1164
|
+
items: { $ref: "#/$defs/Expression" },
|
|
1165
|
+
as: { type: "string" },
|
|
1166
|
+
index: { type: "string" },
|
|
1167
|
+
key: { $ref: "#/$defs/Expression" },
|
|
1168
|
+
body: { $ref: "#/$defs/ViewNode" }
|
|
1169
|
+
}
|
|
1170
|
+
},
|
|
1171
|
+
ComponentNode: {
|
|
1172
|
+
type: "object",
|
|
1173
|
+
required: ["kind", "name"],
|
|
1174
|
+
additionalProperties: false,
|
|
1175
|
+
properties: {
|
|
1176
|
+
kind: { type: "string", const: "component" },
|
|
1177
|
+
name: { type: "string" },
|
|
1178
|
+
props: {
|
|
1179
|
+
type: "object",
|
|
1180
|
+
additionalProperties: { $ref: "#/$defs/Expression" }
|
|
1181
|
+
},
|
|
1182
|
+
children: {
|
|
1183
|
+
type: "array",
|
|
1184
|
+
items: { $ref: "#/$defs/ViewNode" }
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
},
|
|
1188
|
+
SlotNode: {
|
|
1189
|
+
type: "object",
|
|
1190
|
+
required: ["kind"],
|
|
1191
|
+
additionalProperties: false,
|
|
1192
|
+
properties: {
|
|
1193
|
+
kind: { type: "string", const: "slot" }
|
|
1194
|
+
}
|
|
1195
|
+
},
|
|
1196
|
+
// ==================== Component Definition ====================
|
|
1197
|
+
ParamDef: {
|
|
1198
|
+
type: "object",
|
|
1199
|
+
required: ["type"],
|
|
1200
|
+
additionalProperties: false,
|
|
1201
|
+
properties: {
|
|
1202
|
+
type: {
|
|
1203
|
+
type: "string",
|
|
1204
|
+
enum: ["string", "number", "boolean", "json"]
|
|
1205
|
+
},
|
|
1206
|
+
required: { type: "boolean" }
|
|
1207
|
+
}
|
|
1208
|
+
},
|
|
1209
|
+
ComponentDef: {
|
|
1210
|
+
type: "object",
|
|
1211
|
+
required: ["view"],
|
|
1212
|
+
additionalProperties: false,
|
|
1213
|
+
properties: {
|
|
1214
|
+
params: {
|
|
1215
|
+
type: "object",
|
|
1216
|
+
additionalProperties: { $ref: "#/$defs/ParamDef" }
|
|
1217
|
+
},
|
|
1218
|
+
view: { $ref: "#/$defs/ViewNode" }
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1248
1223
|
export {
|
|
1249
1224
|
BINARY_OPERATORS,
|
|
1250
1225
|
ConstelaError,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constela/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Core types, schema, and validator for Constela UI framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -14,9 +14,7 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"dist"
|
|
16
16
|
],
|
|
17
|
-
"dependencies": {
|
|
18
|
-
"ajv": "^8.12.0"
|
|
19
|
-
},
|
|
17
|
+
"dependencies": {},
|
|
20
18
|
"devDependencies": {
|
|
21
19
|
"@types/node": "^20.10.0",
|
|
22
20
|
"tsup": "^8.0.0",
|