@constela/core 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +20 -1
  2. package/dist/index.js +80 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -40,9 +40,27 @@ All fields except `version`, `state`, `actions`, and `view` are optional.
40
40
  }
41
41
  ```
42
42
 
43
+ ### Cookie Expression for Initial Value
44
+
45
+ String state can use a cookie expression to read initial value from cookies (SSR/SSG-safe):
46
+
47
+ ```json
48
+ {
49
+ "theme": {
50
+ "type": "string",
51
+ "initial": { "expr": "cookie", "key": "theme", "default": "dark" }
52
+ }
53
+ }
54
+ ```
55
+
56
+ - `key`: Cookie name to read
57
+ - `default`: Fallback value when cookie is not set
58
+ - Works in both SSR and client-side rendering
59
+ - Useful for theme persistence, user preferences, etc.
60
+
43
61
  ## Expression Types
44
62
 
45
- 14 expression types for constrained computation:
63
+ 15 expression types for constrained computation:
46
64
 
47
65
  | Type | JSON Example | Description |
48
66
  |------|-------------|-------------|
@@ -60,6 +78,7 @@ All fields except `version`, `state`, `actions`, and `view` are optional.
60
78
  | `ref` | `{ "expr": "ref", "name": "inputEl" }` | DOM element ref |
61
79
  | `style` | `{ "expr": "style", "name": "button", "variants": {...} }` | Style reference |
62
80
  | `concat` | `{ "expr": "concat", "items": [...] }` | String concatenation |
81
+ | `cookie` | `{ "expr": "cookie", "key": "theme", "default": "dark" }` | Cookie value (SSR-safe) |
63
82
 
64
83
  **Binary Operators:** `+`, `-`, `*`, `/`, `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`
65
84
 
package/dist/index.js CHANGED
@@ -839,10 +839,10 @@ function findSimilarNames(target, candidates, maxDistance = 2) {
839
839
  function isObject2(value) {
840
840
  return typeof value === "object" && value !== null && !Array.isArray(value);
841
841
  }
842
- var VALID_VIEW_KINDS = ["element", "text", "if", "each", "component", "slot", "markdown", "code"];
843
- var VALID_EXPR_TYPES = ["lit", "state", "var", "bin", "not", "param", "cond", "get", "style"];
842
+ var VALID_VIEW_KINDS = ["element", "text", "if", "each", "component", "slot", "markdown", "code", "portal"];
843
+ var VALID_EXPR_TYPES = ["lit", "state", "var", "bin", "not", "param", "cond", "get", "style", "validity", "index"];
844
844
  var VALID_PARAM_TYPES = ["string", "number", "boolean", "json"];
845
- var VALID_ACTION_TYPES = ["set", "update", "fetch"];
845
+ var VALID_ACTION_TYPES = ["set", "update", "fetch", "delay", "interval", "clearTimer", "focus"];
846
846
  var VALID_STATE_TYPES = ["number", "string", "list", "boolean", "object"];
847
847
  var VALID_BIN_OPS = BINARY_OPERATORS;
848
848
  var VALID_UPDATE_OPS = UPDATE_OPERATIONS;
@@ -856,7 +856,7 @@ function validateViewNode(node, path) {
856
856
  return { path: path + "/kind", message: "kind is required" };
857
857
  }
858
858
  if (!VALID_VIEW_KINDS.includes(kind)) {
859
- return { path: path + "/kind", message: "must be one of: element, text, if, each, component, slot, markdown, code" };
859
+ return { path: path + "/kind", message: "must be one of: element, text, if, each, component, slot, markdown, code, portal" };
860
860
  }
861
861
  switch (kind) {
862
862
  case "element":
@@ -955,6 +955,17 @@ function validateViewNode(node, path) {
955
955
  if (langError) return langError;
956
956
  return validateExpression(node["content"], path + "/content");
957
957
  }
958
+ case "portal":
959
+ if (typeof node["target"] !== "string") {
960
+ return { path: path + "/target", message: "target is required" };
961
+ }
962
+ if (Array.isArray(node["children"])) {
963
+ for (let i = 0; i < node["children"].length; i++) {
964
+ const error = validateViewNode(node["children"][i], path + "/children/" + i);
965
+ if (error) return error;
966
+ }
967
+ }
968
+ break;
958
969
  }
959
970
  return null;
960
971
  }
@@ -967,7 +978,7 @@ function validateExpression(expr, path) {
967
978
  return { path: path + "/expr", message: "expr is required" };
968
979
  }
969
980
  if (!VALID_EXPR_TYPES.includes(exprType)) {
970
- return { path: path + "/expr", message: "must be one of: lit, state, var, bin, not, param, cond, get, style" };
981
+ return { path: path + "/expr", message: "must be one of: lit, state, var, bin, not, param, cond, get, style, validity, index" };
971
982
  }
972
983
  switch (exprType) {
973
984
  case "lit":
@@ -1055,6 +1066,25 @@ function validateExpression(expr, path) {
1055
1066
  }
1056
1067
  }
1057
1068
  break;
1069
+ case "validity":
1070
+ if (typeof expr["ref"] !== "string") {
1071
+ return { path: path + "/ref", message: "ref is required" };
1072
+ }
1073
+ break;
1074
+ case "index":
1075
+ if (!("base" in expr)) {
1076
+ return { path: path + "/base", message: "base is required" };
1077
+ }
1078
+ if (!("key" in expr)) {
1079
+ return { path: path + "/key", message: "key is required" };
1080
+ }
1081
+ {
1082
+ const baseError = validateExpression(expr["base"], path + "/base");
1083
+ if (baseError) return baseError;
1084
+ const keyError = validateExpression(expr["key"], path + "/key");
1085
+ if (keyError) return keyError;
1086
+ }
1087
+ break;
1058
1088
  }
1059
1089
  return null;
1060
1090
  }
@@ -1067,7 +1097,7 @@ function validateActionStep(step, path) {
1067
1097
  return { path: path + "/do", message: "do is required" };
1068
1098
  }
1069
1099
  if (!VALID_ACTION_TYPES.includes(doType)) {
1070
- return { path: path + "/do", message: "must be one of: set, update, fetch" };
1100
+ return { path: path + "/do", message: "must be one of: set, update, fetch, delay, interval, clearTimer, focus" };
1071
1101
  }
1072
1102
  switch (doType) {
1073
1103
  case "set":
@@ -1110,6 +1140,50 @@ function validateActionStep(step, path) {
1110
1140
  if (bodyError) return bodyError;
1111
1141
  }
1112
1142
  break;
1143
+ case "delay":
1144
+ if (!("ms" in step)) {
1145
+ return { path: path + "/ms", message: "ms is required" };
1146
+ }
1147
+ if (!("then" in step)) {
1148
+ return { path: path + "/then", message: "then is required" };
1149
+ }
1150
+ {
1151
+ const msError = validateExpression(step["ms"], path + "/ms");
1152
+ if (msError) return msError;
1153
+ if (!Array.isArray(step["then"])) {
1154
+ return { path: path + "/then", message: "then must be an array" };
1155
+ }
1156
+ for (let i = 0; i < step["then"].length; i++) {
1157
+ const thenError = validateActionStep(step["then"][i], path + "/then/" + i);
1158
+ if (thenError) return thenError;
1159
+ }
1160
+ }
1161
+ break;
1162
+ case "interval":
1163
+ if (!("ms" in step)) {
1164
+ return { path: path + "/ms", message: "ms is required" };
1165
+ }
1166
+ if (typeof step["action"] !== "string") {
1167
+ return { path: path + "/action", message: "action is required" };
1168
+ }
1169
+ {
1170
+ const msError = validateExpression(step["ms"], path + "/ms");
1171
+ if (msError) return msError;
1172
+ }
1173
+ break;
1174
+ case "clearTimer":
1175
+ if (!("target" in step)) {
1176
+ return { path: path + "/target", message: "target is required" };
1177
+ }
1178
+ return validateExpression(step["target"], path + "/target");
1179
+ case "focus":
1180
+ if (!("target" in step)) {
1181
+ return { path: path + "/target", message: "target is required" };
1182
+ }
1183
+ if (typeof step["operation"] !== "string") {
1184
+ return { path: path + "/operation", message: "operation is required" };
1185
+ }
1186
+ return validateExpression(step["target"], path + "/target");
1113
1187
  }
1114
1188
  return null;
1115
1189
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/core",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Core types, schema, and validator for Constela UI framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",