cdom 0.0.15 → 0.0.16

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/index.js CHANGED
@@ -35,20 +35,20 @@
35
35
  };
36
36
 
37
37
 
38
+ function tokenize(src) {
39
+ const tokenRegex = /\s*(\d*\.\d+|\d+|"([^"\\]|\\.)*"|'([^'\\]|\\.)*'|=\/|=\$this|=\$event|=\$query|=\$|[a-zA-Z_$][\w$]*|==|!=|<=|>=|&&|\|\||[-+*/%^<>!?:.,(){}[\]])\s*/g;
40
+ const results = [];
41
+ let match;
42
+ while ((match = tokenRegex.exec(src)) !== null) {
43
+ results.push(match[1]);
44
+ }
45
+ return results;
46
+ }
47
+
38
48
  function createExpression(text) {
39
49
  let at = 0;
40
50
  const tokens = tokenize(text);
41
51
 
42
- function tokenize(src) {
43
- const results = [];
44
- const tokenRegex = /\s*(\d*\.\d+|\d+|"([^"\\]|\\.)*"|'([^'\\]|\\.)*'|=\/|=\$this|=\$event|=\$query|=\$|[a-zA-Z_$][\w$]*|==|!=|<=|>=|&&|\|\||[-+*/%^<>!?:.,(){}[\]])\s*/g;
45
- let match;
46
- while ((match = tokenRegex.exec(src)) !== null) {
47
- results.push(match[1]);
48
- }
49
- return results;
50
- }
51
-
52
52
  function peek(offset = 0) { return tokens[at + offset]; }
53
53
  function next() { return tokens[at++]; }
54
54
  function consume(expected) {
@@ -852,6 +852,7 @@
852
852
  }
853
853
 
854
854
  async function loadHelper(name) {
855
+ if (helpers.has(name)) return Promise.resolve(helpers.get(name));
855
856
  if (pendingHelpers.has(name)) return pendingHelpers.get(name);
856
857
 
857
858
  const promise = (async () => {
@@ -906,6 +907,42 @@
906
907
  set.add(sub);
907
908
  }
908
909
 
910
+ function findNeededHelpers(obj, found = new Set()) {
911
+ if (!obj) return found;
912
+
913
+ if (typeof obj === 'string') {
914
+ if (obj.startsWith('=') && obj.length > 1) {
915
+ const tokens = tokenize(obj);
916
+ for (let i = 0; i < tokens.length; i++) {
917
+ const t = tokens[i];
918
+ if (/^[a-zA-Z_$][\w$]*$/.test(t) && !['true', 'false', 'null', 'undefined'].includes(t)) {
919
+ if (i > 0 && tokens[i - 1] === '.') continue;
920
+ found.add(t);
921
+ }
922
+ }
923
+ }
924
+ return found;
925
+ }
926
+
927
+ if (typeof obj !== 'object' || obj.nodeType) return found;
928
+
929
+ if (Array.isArray(obj)) {
930
+ for (const item of obj) findNeededHelpers(item, found);
931
+ return found;
932
+ }
933
+
934
+ for (const key in obj) {
935
+ const alias = symbolicOperators.get(key);
936
+ if (key.startsWith('=') && key.length > 1) {
937
+ found.add(key.slice(1));
938
+ } else if (alias) {
939
+ found.add(alias);
940
+ }
941
+ findNeededHelpers(obj[key], found);
942
+ }
943
+ return found;
944
+ }
945
+
909
946
 
910
947
  function evaluateStructural(obj, context, event, unsafe) {
911
948
  if (typeof obj !== 'object' || obj === null || obj.nodeType) {
@@ -1497,6 +1534,13 @@
1497
1534
  const query = Object.fromEntries(url.searchParams.entries());
1498
1535
  element._queryContext = query;
1499
1536
  if (!element._macroContext) element._macroContext = query;
1537
+
1538
+ // Pre-load all needed helpers to avoid "suspense" flickering during hypermedia loads
1539
+ const helpersToLoad = findNeededHelpers(cdomData);
1540
+ if (helpersToLoad.size > 0) {
1541
+ await Promise.all(Array.from(helpersToLoad).map(name => loadHelper(name)));
1542
+ }
1543
+
1500
1544
  const dom = cdomToDOM(cdomData, true, false, element);
1501
1545
  element.replaceChildren(dom);
1502
1546
  } else if (isHTML) {
@@ -1554,6 +1598,65 @@
1554
1598
  }
1555
1599
  }
1556
1600
 
1601
+ // Core Library Functions (Inlined for performance/reliability)
1602
+ const _sum = (...args) => args.flat(Infinity).reduce((a, b) => a + (Number(b) || 0), 0);
1603
+ const _sub = (...args) => {
1604
+ const flat = args.flat(Infinity).map(v => Number(v) || 0);
1605
+ if (flat.length === 0) return 0;
1606
+ if (flat.length === 1) return -flat[0];
1607
+ return flat.reduce((a, b) => a - b);
1608
+ };
1609
+ const _mul = (...args) => args.flat(Infinity).reduce((a, b) => a * (Number(b) || 1), 1);
1610
+ const _div = (a, b) => (Number(a) || 0) / (Number(b) || 1);
1611
+
1612
+ const _set = function (target, val) {
1613
+ if (target && typeof target === 'object' && 'value' in target) {
1614
+ target.value = val;
1615
+ } else if (target && typeof target === 'function' && 'value' in target) {
1616
+ target.value = val;
1617
+ } else if (target && typeof target === 'object' && val && typeof val === 'object') {
1618
+ Object.assign(target, val);
1619
+ }
1620
+ return val;
1621
+ };
1622
+ _set.mutates = true;
1623
+
1624
+ const _inc = function (target, by = 1) {
1625
+ const hasValue = target && (typeof target === 'object' || typeof target === 'function') && 'value' in target;
1626
+ const current = hasValue ? target.value : 0;
1627
+ return _set(target, Number(current) + Number(by));
1628
+ };
1629
+ _inc.mutates = true;
1630
+
1631
+ const _dec = function (target, by = 1) {
1632
+ const hasValue = target && (typeof target === 'object' || typeof target === 'function') && 'value' in target;
1633
+ const current = hasValue ? target.value : 0;
1634
+ return _set(target, Number(current) - Number(by));
1635
+ };
1636
+ _dec.mutates = true;
1637
+
1638
+ const _toggle = function (target) {
1639
+ const hasValue = target && (typeof target === 'object' || typeof target === 'function') && 'value' in target;
1640
+ const current = hasValue ? target.value : false;
1641
+ return _set(target, !current);
1642
+ };
1643
+ _toggle.mutates = true;
1644
+
1645
+ const _if = (c, t, e) => c ? t : e;
1646
+ const _ifs = (...args) => {
1647
+ for (let i = 0; i < args.length; i += 2) {
1648
+ if (i + 1 >= args.length) return args[i]; // Default case
1649
+ if (args[i]) return args[i + 1];
1650
+ }
1651
+ };
1652
+
1653
+ const _switch = (exp, ...args) => {
1654
+ for (let i = 0; i < args.length - 1; i += 2) {
1655
+ if (exp === args[i]) return args[i + 1];
1656
+ }
1657
+ return args.length % 2 === 1 ? args[args.length - 1] : undefined;
1658
+ };
1659
+
1557
1660
  // Export to global scope
1558
1661
  cDOM.signal = signal;
1559
1662
  cDOM.state = state;
@@ -1571,6 +1674,27 @@
1571
1674
  // Register core helpers
1572
1675
  helpers.set('state', state);
1573
1676
  helpers.set('signal', signal);
1677
+ helpers.set('sum', _sum);
1678
+ helpers.set('add', _sum);
1679
+ helpers.set('subtract', _sub);
1680
+ helpers.set('multiply', _mul);
1681
+ helpers.set('divide', _div);
1682
+ helpers.set('eq', (a, b) => a == b);
1683
+ helpers.set('neq', (a, b) => a != b);
1684
+ helpers.set('gt', (a, b) => a > b);
1685
+ helpers.set('lt', (a, b) => a < b);
1686
+ helpers.set('gte', (a, b) => a >= b);
1687
+ helpers.set('lte', (a, b) => a <= b);
1688
+ helpers.set('and', (...args) => args.every(Boolean));
1689
+ helpers.set('or', (...args) => args.some(Boolean));
1690
+ helpers.set('not', (a) => !a);
1691
+ helpers.set('set', _set);
1692
+ helpers.set('increment', _inc);
1693
+ helpers.set('decrement', _dec);
1694
+ helpers.set('toggle', _toggle);
1695
+ helpers.set('if', _if);
1696
+ helpers.set('ifs', _ifs);
1697
+ helpers.set('switch', _switch);
1574
1698
 
1575
1699
  // Default Operator Mappings
1576
1700
  cDOM.operator('+', 'add');
@@ -1578,8 +1702,11 @@
1578
1702
  cDOM.operator('*', 'multiply');
1579
1703
  cDOM.operator('/', 'divide');
1580
1704
  cDOM.operator('==', 'eq');
1705
+ cDOM.operator('!=', 'neq');
1581
1706
  cDOM.operator('>', 'gt');
1582
1707
  cDOM.operator('<', 'lt');
1708
+ cDOM.operator('>=', 'gte');
1709
+ cDOM.operator('<=', 'lte');
1583
1710
  cDOM.operator('&&', 'and');
1584
1711
  cDOM.operator('||', 'or');
1585
1712
  cDOM.operator('!', 'not');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdom",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "description": "Safe, reactive UIs based on JSON Pointer, XPath, JSON Schema",
5
5
  "keywords": [
6
6
  "JPRX",
package/helpers/add.js DELETED
@@ -1,3 +0,0 @@
1
- // Example: { p: { "=add": [10, 20] } }
2
- import sum from './sum.js';
3
- export default sum;
package/helpers/and.js DELETED
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=and": [true, true] } }
2
- export default function (...args) {
3
- return args.every(Boolean);
4
- }
@@ -1,10 +0,0 @@
1
- // Example: { onclick: { "=decrement": ["/counter/count", 1] } }
2
- import set from './set.js';
3
- const decrement = function (target, by = 1) {
4
- const hasValue = target && (typeof target === 'object' || typeof target === 'function') && 'value' in target;
5
- const current = hasValue ? target.value : 0;
6
- const next = Number(current) - Number(by);
7
- return set(target, next);
8
- }
9
- decrement.mutates = true;
10
- export default decrement;
package/helpers/divide.js DELETED
@@ -1,7 +0,0 @@
1
- // Example: { p: { "=divide": [10, 2] } }
2
- export default function (...args) {
3
- const flat = args.flat(Infinity).map(v => Number(v) || 0);
4
- if (flat.length === 0) return 0;
5
- if (flat.length === 1) return 1 / flat[0];
6
- return flat.reduce((a, b) => a / b);
7
- }
package/helpers/eq.js DELETED
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=eq": [1, 1] } }
2
- export default function (a, b) {
3
- return a == b;
4
- }
package/helpers/gt.js DELETED
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=gt": [10, 5] } }
2
- export default function (a, b) {
3
- return Number(a) > Number(b);
4
- }
package/helpers/if.js DELETED
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=if": [true, "Yes", "No"] } }
2
- export default function (condition, thenVal, elseVal) {
3
- return condition ? thenVal : elseVal;
4
- }
package/helpers/ifs.js DELETED
@@ -1,7 +0,0 @@
1
- // Example: { p: { "=ifs": [true, "A", false, "B", "Default"] } }
2
- export default function (...args) {
3
- for (let i = 0; i < args.length - 1; i += 2) {
4
- if (args[i]) return args[i + 1];
5
- }
6
- return args.length % 2 === 1 ? args[args.length - 1] : undefined;
7
- }
@@ -1,10 +0,0 @@
1
- // Example: { onclick: { "=increment": ["/counter/count", 1] } }
2
- import set from './set.js';
3
- const increment = function (target, by = 1) {
4
- const hasValue = target && (typeof target === 'object' || typeof target === 'function') && 'value' in target;
5
- const current = hasValue ? target.value : 0;
6
- const next = Number(current) + Number(by);
7
- return set(target, next);
8
- }
9
- increment.mutates = true;
10
- export default increment;
package/helpers/lt.js DELETED
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=lt": [5, 10] } }
2
- export default function (a, b) {
3
- return Number(a) < Number(b);
4
- }
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=multiply": [2, 3, 4] } }
2
- export default function (...args) {
3
- return args.flat(Infinity).reduce((a, b) => a * (Number(b) || 0), 1);
4
- }
package/helpers/not.js DELETED
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=not": [true] } }
2
- export default function (val) {
3
- return !val;
4
- }
package/helpers/or.js DELETED
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=or": [true, false] } }
2
- export default function (...args) {
3
- return args.some(Boolean);
4
- }
package/helpers/set.js DELETED
@@ -1,13 +0,0 @@
1
- // Example: { onclick: { "=set": ["/app/title", "New Title"] } }
2
- const set = function (target, val) {
3
- if (target && typeof target === 'object' && 'value' in target) {
4
- target.value = val;
5
- } else if (target && typeof target === 'function' && 'value' in target) {
6
- target.value = val;
7
- } else if (target && typeof target === 'object' && val && typeof val === 'object') {
8
- Object.assign(target, val);
9
- }
10
- return val;
11
- }
12
- set.mutates = true;
13
- export default set;
@@ -1,7 +0,0 @@
1
- // Example: { p: { "=subtract": [10, 5, 2] } }
2
- export default function (...args) {
3
- const flat = args.flat(Infinity).map(v => Number(v) || 0);
4
- if (flat.length === 0) return 0;
5
- if (flat.length === 1) return -flat[0];
6
- return flat.reduce((a, b) => a - b);
7
- }
package/helpers/sum.js DELETED
@@ -1,4 +0,0 @@
1
- // Example: { p: { "=sum": [10, 20, 30] } }
2
- export default function (...args) {
3
- return args.flat(Infinity).reduce((a, b) => a + (Number(b) || 0), 0);
4
- }
package/helpers/switch.js DELETED
@@ -1,7 +0,0 @@
1
- // Example: { p: { "=switch": [2, 1, "One", 2, "Two", "Default"] } }
2
- export default function (expression, ...args) {
3
- for (let i = 0; i < args.length - 1; i += 2) {
4
- if (expression === args[i]) return args[i + 1];
5
- }
6
- return args.length % 2 === 1 ? args[args.length - 1] : undefined;
7
- }
package/helpers/toggle.js DELETED
@@ -1,9 +0,0 @@
1
- // Example: { onclick: { "=toggle": ["/user/isActive"] } }
2
- import set from './set.js';
3
- const toggle = function (target) {
4
- const hasValue = target && (typeof target === 'object' || typeof target === 'function') && 'value' in target;
5
- const current = hasValue ? target.value : false;
6
- return set(target, !current);
7
- }
8
- toggle.mutates = true;
9
- export default toggle;