cdom 0.0.8 → 0.0.10

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 CHANGED
@@ -1,18 +1,21 @@
1
1
  # cDOM - Computational DOM
2
2
 
3
- **A reactive UI library with hypermedia capabilities with JPRX support (reactive JSON Pointers and XPath) plus JSON Schema validation and state persistence.**
3
+ > **⚠️ EXPERIMENTAL**: cDOM is currently in version v0.0.10. The syntax and API are rapidly evolving and may change without notice. Use with caution.
4
4
 
5
- cDOM is a reactive framework that lets you build dynamic UIs using declarative object notation. Originally based on [Lightview](https://github.com/anywhichway/lightview), cDOM provides a focused subset of features for developers who want reactivity and hypermedia without the full framework.
5
+ **A reactive UI library with hypermedia capabilities with modified JPRX support (reactive JSON Pointers and XPath) plus JSON Schema validation and state persistence.**
6
+
7
+ cDOM is a reactive framework that lets you build dynamic UIs using declarative object notation. Uniquely, cDOM uses **structural reactivity**, where logic is defined via JSON structure rather than string parsing.
6
8
 
7
9
  ## Features
8
10
 
9
11
  - 🎯 **Reactive State Management** - Signals and state with automatic dependency tracking
10
12
  - 🔄 **Declarative UI** - Build interfaces using simple JavaScript objects
13
+ - 🧱 **Structural Reactivity** - Define complex logic using nested objects `{ "=": ... }`
11
14
  - 🛡️ **Schema Validation** - Built-in JSON Schema validation for robust state
12
15
  - 💾 **Persistence** - Automatic sync to `localStorage` or `sessionStorage`
13
16
  - 🌐 **Hypermedia Support** - `src` and `href` attributes for dynamic content loading
14
- - 📊 **XPath & CSS Queries** - Navigate and query the DOM with reactive `$()` expressions
15
- - 🧮 **Safe Expression Engine** - Built-in secure parser for reactive calculations `_()`
17
+ - 📊 **XPath & CSS Queries** - Navigate and query the DOM with reactive `{ "$": ... }` expressions
18
+ - 🧮 **Direct Operator Support** - Use operators like `*`, `+`, `>=` directly in your structure
16
19
  - 🪶 **Lightweight** - ~15KB minified, ZERO dependencies
17
20
  - 🔌 **Standalone** - Works independently of Lightview
18
21
 
@@ -43,175 +46,169 @@ Download `cdom.js` and include it in your project:
43
46
 
44
47
  <body>
45
48
  <script>
46
- // Create a simple counter
47
- cDOM({
48
- div: {
49
- oncreate: function() {
50
- cDOM.state({ count: 0 }, { name: 'counter', scope: this });
51
- },
52
- children: [
53
- { h2: "Counter Example" },
54
- { p: ["Count: ", "_(/counter/count)"] },
55
- { button: {
56
- onclick: "_(++/counter/count)",
57
- children: ["Increment"]
58
- }}
59
- ]
60
- }
61
- }, { target: document.body, location: 'beforeend' });
62
- </script>
63
- </body>
64
- </html>
65
- ```
49
+ // Register helper
50
+ cDOM.helper('increment', (s) => { if(s && typeof s.count === 'number') s.count++; return s.count; });
66
51
 
67
- ### Inline Usage
68
-
69
- cDOM will replace the script is runs in if an emty options object is provided, just put the script where you want the HTML.
70
-
71
- ```html
72
- <html>
73
- <script src="https://cdn.jsdelivr.net/npm/cdom/index.min.js"></script>
74
-
75
- <body>
76
- <script>
77
52
  // Create a simple counter
78
53
  cDOM({
79
54
  div: {
80
- oncreate: function() {
81
- cDOM.state({ count: 0 }, { name: 'counter', scope: this });
55
+ oncreate: {
56
+ "=state": [{ count: 0 }, { name: 'counter', scope: "$this" }]
82
57
  },
83
58
  children: [
84
59
  { h2: "Counter Example" },
85
- { p: ["Count: ", "_(/counter/count)"] },
60
+ { p: ["Count: ", { "=": "/counter/count" }] },
86
61
  { button: {
87
- onclick: "_(++/counter/count)",
62
+ onclick: { "=increment": ["/counter"] },
88
63
  children: ["Increment"]
89
64
  }}
90
65
  ]
91
66
  }
92
- }, { });
67
+ }, { target: document.body, location: 'beforeend' });
93
68
  </script>
94
69
  </body>
95
70
  </html>
96
71
  ```
97
72
 
98
- **Resulting DOM (as seen in DevTools):**
99
-
100
- ```html
101
- <body>
102
- <div>
103
- <h2>Counter Example</h2>
104
- <p>Count: 0</p>
105
- <button onclick="_(++/counter/count)">Increment</button>
106
- </div>
107
- </body>
108
- ```
109
-
110
-
111
73
  ### Component Functions
112
74
 
113
- Create reusable components as functions. Components can either return a rendered DOM element by calling `cDOM()` internally (without options), or return a raw oDOM object to be processed by a parent container.
114
-
115
- **Option A: Returning a rendered element**
75
+ Create reusable components as functions.
116
76
 
117
77
  ```javascript
118
- const { state } = cDOM;
119
78
  function Counter(initialValue = 0) {
120
- // Calling cDOM() here returns a ready-to-use DOM element
121
- return cDOM({
79
+ return {
122
80
  div: {
123
81
  class: "counter-widget",
124
- oncreate() {
125
- state({ count: initialValue }, { name: 'local', scope: this });
82
+ // Use structural object for initialization
83
+ oncreate: {
84
+ "=state": [{ count: initialValue }, { name: 'local', scope: "$this" }]
126
85
  },
127
86
  children: [
128
87
  { h3: "Counter" },
129
- { p: ["Current: ", "_(/local/count)"] },
130
- { button: { onclick: "_(++/local/count)", children: ["+"] }}
88
+ { p: ["Current: ", { "=": "/local/count" }] },
89
+ // Structural event handler
90
+ { button: {
91
+ onclick: { "=increment": ["/local"] },
92
+ children: ["+"]
93
+ }}
131
94
  ]
132
95
  }
133
- });
134
- }
135
- ```
136
-
137
- **Option B: Deferring to a wrapper (Returning oDOM)**
138
-
139
- Alternatively, skip the `cDOM()` call to return a plain object. This is often cleaner for nesting components within larger structures.
140
-
141
- ```javascript
142
- function Header(title) {
143
- return { h1: title }; // Return raw oDOM
96
+ };
144
97
  }
145
98
 
146
99
  cDOM({
147
100
  div: [
148
- Header("My App"),
149
- Counter(0) // Works with either pattern!
101
+ { h1: "My App" },
102
+ Counter(0)
150
103
  ]
151
104
  }, {});
152
105
  ```
153
106
 
154
107
  ## Core Concepts
155
108
 
156
- ### 1. Object DOM (oDOM) Syntax
109
+ ### 1. Structural Reactivity (The `=` Key)
110
+
111
+ cDOM v0.0.10 moves away from complex string parsing (`_()`) in favor of **structural reactivity**. You express logic using JSON keys starting with `=`.
112
+
113
+ **State Lookup:**
114
+ ```javascript
115
+ { "=": "/user/name" } // Resolves to state value
116
+ ```
117
+
118
+ **Math Expressions:**
119
+ For simple math, you can still use string expressions inside the value:
120
+ ```javascript
121
+ { "=": "/price * /quantity" }
122
+ ```
123
+
124
+ **Helper Calls:**
125
+ Complex logic uses the key as the helper name:
126
+ ```javascript
127
+ { "=increment": ["/counter"] } // Calls 'increment' helper with resolving args
128
+ ```
129
+
130
+ **Direct Operators:**
131
+ You can use mathematical and logical operators directly as keys:
132
+ ```javascript
133
+ { "*": ["/price", "/qty"] }
134
+ { ">=": ["/age", 18] }
135
+ ```
136
+
137
+ ### 3. Sequential Actions (Array Handlers)
157
138
 
158
- cDOM uses a concise object notation where the key is the tag name.The property 'children' is used to define the children of the element. All other properties are attributes. As a shortcut, if the value of a property is a string, it will be used as the text content of the element.
139
+ Event handlers (`onclick`, `onmount`, `onchange`, etc.) can accept an **Array** of structural expressions. Each expression in the array will be executed sequentially. This is useful for performing multiple side-effects in a single interaction.
159
140
 
160
141
  ```javascript
161
142
  {
162
- div: {
163
- class: "container",
164
- children: [
165
- { h1: "Title" }, // shortcut for text content
166
- { p: { children: ["Paragraph text"] } }
167
- ]
143
+ button: {
144
+ onclick: [
145
+ { "set": ["/ui/loading", true] },
146
+ { "=analytics.track": ["save_clicked"] }, // Resolves via global path (window.analytics.track)
147
+ { "=saveData": "/form" },
148
+ { "set": ["/ui/status", "Saved!"] },
149
+ { "set": ["/ui/loading", false] }
150
+ ],
151
+ children: ["Save Now"]
168
152
  }
169
153
  }
170
154
  ```
171
155
 
172
- ### 2. Signals and State
156
+ ### 4. DOM Queries (The `$` Key)
173
157
 
174
- #### Creating Signals
158
+ Query the DOM using XPath or CSS selectors via the `$` key.
159
+
160
+ **Structural Usage (Reactive):**
161
+ When used as a key in a cDOM object, queries are reactive to DOM changes using a `MutationObserver`. They will automatically update when nodes are added, removed, or attributes change.
175
162
 
176
163
  ```javascript
177
- // Simple reactive value
178
- const count = cDOM.signal(0, { name: 'count' });
164
+ // XPath - Count buttons (updates automatically on DOM change)
165
+ { "$": "count(//button)" }
179
166
 
180
- // Update
181
- count.value = 5;
167
+ // CSS - Get value
168
+ { "$": "#myInput" }
182
169
  ```
183
170
 
184
- #### Creating State
171
+ **Functional Usage (Non-Reactive to DOM):**
172
+ When used inside an expression string or directly in JavaScript, the query is a one-time evaluation. It will not re-run when the DOM changes, unless the surrounding expression is triggered by a state change.
185
173
 
186
174
  ```javascript
187
- // Global state
188
- cDOM.state({ user: 'Alice' }, { name: 'currentUser' });
189
-
190
- // Scoped state (component-local)
191
- cDOM.state({ count: 0 }, { name: 'counter', scope: this });
175
+ // This counts buttons once, or when a state dependency triggers a re-eval
176
+ { "=": "count($('//button')) + 1" }
192
177
  ```
193
178
 
194
- ### Accessing Named Signals or State in Expressions
179
+ > **Design Note: Why aren't all queries live?**
180
+ > While making every `$(...)` call live would be convenient, it carries significant performance overhead. Structural reactivity (`{ "$": ... }`) allows the engine to explicitly track which elements are watching the DOM, preventing "mutation storms" and infinite loops while ensuring efficient memory cleanup.
181
+
182
+
183
+ ### 5. Signals and State
184
+
185
+ #### Initialization
186
+
187
+ Use the `state` helper in `oncreate`:
195
188
 
196
189
  ```javascript
197
- // Absolute path (looks in global registry only)
198
- "_(/currentUser/user)"
190
+ oncreate: {
191
+ "=state": [
192
+ { user: 'Alice' },
193
+ { name: 'currentUser' }
194
+ ]
195
+ }
196
+ ```
199
197
 
200
- // Scoped path (searches current element, then bubbles up the DOM tree)
201
- "_(counter/count)"
202
- // OR
203
- "_(./counter/count)"
198
+ #### Scoped State
204
199
 
205
- // With math
206
- "_(/price * /quantity + /shipping)"
200
+ Scope state to a specific component using the `$this` keyword in the options:
207
201
 
208
- // Note: Explicit parent navigation `..` is NOT supported.
209
- // Ambiguity prevents distinguishing between "parent object data" and "parent DOM element data".
210
- // cDOM automatically bubbles up to find the nearest matching state name, so `..` is unnecessary.
211
- // Use unique state names to disambiguate if needed.
202
+ ```javascript
203
+ oncreate: {
204
+ "=state": [
205
+ { count: 0 },
206
+ { name: 'counter', scope: "$this" }
207
+ ]
208
+ }
212
209
  ```
213
210
 
214
- ### 3. Schema Validation
211
+ ### 6. Schema Validation
215
212
 
216
213
  cDOM supports JSON Schema validation to ensure your state remains consistent.
217
214
 
@@ -227,367 +224,133 @@ cDOM.schema('User', {
227
224
  });
228
225
 
229
226
  // Apply to state
230
- const user = cDOM.state({ name: 'Bob', age: 25 }, {
231
- name: 'user',
232
- schema: 'User'
233
- });
234
-
235
- user.age = -1; // Throws Validation Error
227
+ oncreate: {
228
+ "=state": [
229
+ { name: 'Bob', age: 25 },
230
+ { name: 'user', schema: 'User' }
231
+ ]
232
+ }
236
233
  ```
237
234
 
238
- ### 4. Persistence
235
+ ### 7. Persistence
239
236
 
240
237
  Sync your signals and state automatically to `localStorage` or `sessionStorage`.
241
238
 
242
239
  ```javascript
243
240
  // Retrieve on every access, update on every change
244
- const theme = cDOM.signal('light', {
245
- name: 'site-theme',
246
- storage: localStorage
247
- });
248
-
249
- // Works with complex objects too
250
- const settings = cDOM.state({ zoom: 1 }, {
251
- name: 'app-settings',
252
- storage: sessionStorage
253
- });
241
+ oncreate: {
242
+ "=signal": [
243
+ 'light',
244
+ { name: 'site-theme', storage: localStorage }
245
+ ]
246
+ }
254
247
  ```
255
248
 
256
- ### 5. Transformations
249
+ ### 8. Persistence & Transformations
257
250
 
258
- Automatically cast incoming values using transformation helpers.
251
+ Automatically cast incoming values or sync with storage.
259
252
 
260
253
  ```javascript
261
- const count = cDOM.signal(0, {
254
+ cDOM.signal(0, {
262
255
  name: 'count',
263
256
  transform: 'Integer' // Built-in: Integer, Number, String, Boolean
264
257
  });
265
-
266
- count.value = "10"; // Automatically becomes 10 (Number)
267
258
  ```
268
259
 
269
- ### 2. Reactivity with Functions
260
+ ## Supported Operators and Helpers
270
261
 
271
- ### 3. Reactivity with `_()` Expressions
262
+ ### Operators (Structural Keys)
272
263
 
273
- State paths use `_()` for reactive binding:
264
+ These can be used directly as keys in your cDOM structure (e.g., `{ "+": [1, 2] }`).
274
265
 
275
- ```javascript
276
- // String syntax (parsed by cDOM)
277
- "_(/user/name)"
266
+ * **Math:** `+`, `-`, `*`, `/`, `%`
267
+ * **Comparison:** `==`, `!=`, `>`, `<`, `>=`, `<=`
268
+ * **Logic:** `&&`, `||`, `!` (unary)
269
+ * **Mutation:** `++`, `--`
270
+ * **Ternary:** `?`, `:`
278
271
 
279
- // Function syntax (for use in JavaScript)
280
- _('/user/name') // Returns the expression string for lazy binding
281
- ```
272
+ ### Built-in Helpers
282
273
 
283
- This can be tranported as JSON:
274
+ Helpers are dynamically loaded if not already registered. You can use them structurally (e.g., `{ "=sum": [1, 2] }`) or within expression strings.
284
275
 
285
- ```javascript
286
- {"span": "_(/user/name)"}
287
- ```
276
+ #### Math & Statistics
277
+ `abs`, `add`, `average`, `avg`, `ceil`, `ceiling`, `floor`, `int`, `max`, `median`, `min`, `mod`, `multiply`, `percent`, `pow`, `power`, `rand`, `random`, `round`, `sign`, `sqrt`, `stddev`, `stdev`, `subtract`, `sum`, `trunc`, `var`, `variance`
288
278
 
289
- This can't be transported as JSON:
279
+ #### Logic & Flow
280
+ `and`, `or`, `not`, `if`, `ifs`, `switch`, `choose`, `coalesce`, `iferror`
290
281
 
291
- ```javascript
292
- {span: _('/user/name')}
293
- ```
282
+ #### String Manipulation
283
+ `concat`, `join`, `split`, `trim`, `upper`, `lower`, `proper`, `titlecase`, `tocamelcase`, `toslugcase`, `left`, `right`, `mid`, `len`, `length`, `slice`, `substring`, `replace`, `substitute`, `padend`, `padstart`, `startswith`, `endswith`, `includes`, `charat`, `text`, `textjoin`, `fixed`
294
284
 
295
- **Math expressions:**
296
-
297
- The built-in secure parser handles math and logic operations natively.
285
+ #### Array & Object
286
+ `count`, `map`, `filter`, `reduce`, `every`, `some`, `find`, `findindex`, `sort`, `reverse`, `push`, `pop`, `first`, `last`, `unique`, `flat`, `keys`, `object`, `isarray`, `xlookup`
298
287
 
299
- ```javascript
300
- "_(/price * /quantity)" // Reactive calculation (Global paths)
301
- _('./local/price * 1.1') // Scoped path (requires ./ prefix)
302
- ```
288
+ #### Type Checking
289
+ `isnumber`, `isstring`, `istext`, `isblank`, `isempty`, `isarray`
303
290
 
304
- **Important:** In math expressions, you **MUST** use either `/` (absolute) or `./` (scoped) prefixes for all state paths. This disambiguates state paths from mathematical division operators.
291
+ #### Date & Time
292
+ `now`, `today`, `day`, `month`, `year`, `weekday`, `datedif`
305
293
 
306
- **Note: No Inline Text Interpolation**
294
+ #### Formatting
295
+ `currency`, `tofixed`, `tolocalestring`
307
296
 
308
- In **Text Nodes**, expressions must be the *entire* content string. Granularity is enforced by using valid child arrays, **OR** by using string concatenation within the expression itself.
297
+ #### State Mutation
298
+ `set`, `assign`, `increment`, `decrement`, `clear`, `toggle`
309
299
 
310
- * ❌ `"Hello _(/name)"` (Will render literally, ignoring the expression)
311
- * ✅ `["Hello ", "_(/name)"]` (Using child array - Recommended)
312
- * ✅ `"_( 'Hello ' + /name )"` (Using string concatenation in math expression)
313
- * ✅ `"_(concat('Hello ', /name))"` (Using concat helper)
300
+ #### Network
301
+ `fetch`, `webservice`
314
302
 
315
- In **Attributes**, inline interpolation *is* supported because attributes cannot be split into children:
316
- * ✅ `class="btn _(/isActive ? 'active' : '') text-lg"`
317
- * ✅ `href="/users/_(/id)/profile"`
303
+ ### Defining Custom Helpers
318
304
 
319
- ### 4. DOM Queries with `$()` Expressions
305
+ You can register custom helpers using `cDOM.helper(name, fn)`.
320
306
 
321
- Query the DOM using XPath or CSS selectors:
307
+ #### Mutation Helpers
308
+ If a helper is designed to mutate state data (rather than just calculating a value), you **must** set the `.mutates = true` property on the function. This informs the cDOM parser to pass the underlying state reference (wrapper) rather than the unwrapped value, allowing the helper to perform the update.
322
309
 
310
+ **Example: Custom Increment**
323
311
  ```javascript
324
- // XPath
325
- "$(count(//button))" // Count all buttons
326
- $(//div[@id='main']/@class) // Get class of #main
327
-
328
- // CSS (when not XPath)
329
- "$(.active)" // Query by class
330
- ```
331
-
332
- DOM queries are reactive, if the DOM changes, the query will be re-evaluated.
333
-
334
- ### 5. Lifecycle Hooks
335
-
336
- - **`oncreate`**: Called when element is created (before DOM insertion)
337
- - **`onmount`**: Called when element is added to the DOM
338
-
339
- ```javascript
340
- {
341
- div: {
342
- oncreate() {
343
- // Initialize state here
344
- cDOM.state({ data: [] }, { name: 'myData', scope: this });
345
- },
346
- onmount() {
347
- // DOM is ready, can access this element
348
- console.log('Mounted:', this);
349
- }
350
- }
312
+ const increment = function (target, by = 1) {
313
+ // target here is a state wrapper with a .value property
314
+ const current = (target && typeof target === 'object' && 'value' in target) ? target.value : 0;
315
+ target.value = Number(current) + Number(by);
316
+ return target.value;
351
317
  }
352
- ```
353
-
354
- ## Hypermedia Features
355
-
356
- ### The `src` Attribute
357
-
358
- Load content dynamically into any element:
359
318
 
360
- ```html
361
- <!-- Load from CSS selector -->
362
- <div src="#template"></div>
363
-
364
- <!-- Load HTML file -->
365
- <div src="/components/header.html"></div>
366
-
367
- <!-- Load cDOM JSON -->
368
- <div src="/data/widget.cdom"></div>
369
-
370
- <!-- Load any text (displays in <pre>) -->
371
- <div src="/data/config.json"></div>
372
- ```
373
-
374
- **Important:** For non-standard elements (anything other than `<img>`, `<script>`, etc.), src paths **MUST** start with `./`, `../`, or `/`. This ensures they are correctly identified as URLs and not CSS selectors.
375
-
376
- **Supported content types:**
377
- - `.cdom` / `application/cdom` → Parsed as cDOM JSON
378
- - `.html` / `text/html` → Inserted as HTML
379
- - Everything else → Displayed in `<pre>` tag
380
-
381
- ### The `href` Attribute (Non-`<a>` Elements)
382
-
383
- Make any element clickable with navigation:
384
-
385
- ```html
386
- <!-- Load content on click -->
387
- <button href="/api/data" target="#results">Load Data</button>
319
+ // CRITICAL: Must flag as mutation for the parser to pass the state reference
320
+ increment.mutates = true;
388
321
 
389
- <!-- Hash scrolling -->
390
- <button href="/docs.html#section-2">Jump to Section</button>
322
+ cDOM.helper('myIncrement', increment);
391
323
  ```
392
324
 
393
- **Note:** Standard `<a>` tags always maintain their default browser behavior.
394
-
395
-
396
- ## Helpers
397
-
398
- Register custom helper functions:
399
-
325
+ Usage in cDOM:
400
326
  ```javascript
401
- const double = cDOM.helper('double', (value) => value * 2);
402
- const greet = cDOM.helper('greet', (name) => `Hello, ${name}!`);
403
-
404
- // Use in expressions
405
- "_(double(/count))"
406
- "_(greet(/user/name))"
407
- ```
408
-
409
- // use directly in functions
410
- double(_('/user/name'))
411
- ```
412
-
413
- When `_()` is used a string in a cDOM it establishes reatcive context and calls to wrapped helpers do not need to wrap state paths in nested `_()`. When a helper is called directly it does not establish a reactive context, but it know how to handle reactive arguments.
414
-
415
- ## Complete Example: Todo List
416
-
417
- ```html
418
- <html>
419
- <script src="https://cdn.jsdelivr.net/npm/cdom/index.min.js"></script>
420
-
421
- <body>
422
- <script>
423
- const { state } = cDOM;
424
-
425
- // Create global state
426
- const appState = state({
427
- todos: [],
428
- input: ''
429
- }, { name: 'app' });
430
-
431
- // Helper to add todo
432
- const addTodo = () => {
433
- if (!appState.input.trim()) return;
434
- appState.todos.push({
435
- text: appState.input,
436
- done: false
437
- });
438
- appState.input = '';
439
- });
440
-
441
- // Helper to toggle todo
442
- cDOM.helper('toggleTodo', (index) => {
443
- appState.todos[index].done = !appState.todos[index].done;
444
- });
445
-
446
- // Helper to remove todo
447
- cDOM.helper('removeTodo', (index) => {
448
- appState.todos.splice(index, 1);
449
- });
450
-
451
- cDOM({
452
- div: {
453
- class: "todo-app",
454
- children: [
455
- { h1: "Todo List" },
456
- { div: {
457
- children: [
458
- { input: {
459
- type: "text",
460
- placeholder: "New todo...",
461
- value: _('/app/input'),
462
- oninput: "_(set(/app/input, $event.target.value))"
463
- }},
464
- { button: {
465
- onclick() { addTodo() },
466
- children: ["Add"]
467
- }}
468
- ]
469
- }},
470
- { ul: {
471
- id: "todo-list",
472
- children: _('/app/todos') // Reactive list rendering
473
- }}
474
- ]
475
- }
476
- }, {});
477
- </script>
478
- </body>
479
- </html>
327
+ { button: {
328
+ onclick: { "=myIncrement": ["/counter/count", 5] },
329
+ children: ["+5"]
330
+ }}
480
331
  ```
481
332
 
482
333
  ## API Reference
483
334
 
484
335
  ### `cDOM(object, options?)`
485
336
 
486
- Converts cDOM object to DOM and optionally inserts it.
487
-
488
- **Parameters:**
489
- - `object`: cDOM object or JSON string
490
- - `options`: Optional configuration
491
- - `target`: Element or CSS selector string (default: `document.currentScript`). If a selector matches multiple elements, the component will be instantiated for each.
492
- - `location`: Where to insert - `'innerHTML'`, `'outerHTML'`, `'beforeend'`, etc.
493
- - `unsafe`: Allow unsafe eval (default: `false`)
494
-
495
- **Returns:** The (first) created DOM element
496
-
497
- If an options object is provided, the default location will be outerHTML on the current script, i.e. replace the script. If you do not want to replace anything and just want the reactive element, DO NOT pass in an options object.
498
-
499
- ```javascript
500
- // Target a specific element by ID
501
- cDOM({ h1: "Hello" }, { target: "#header" });
502
-
503
- // Target all elements with a class
504
- cDOM({ button: "Click Me" }, { target: ".action-buttons", location: "beforeend" });
505
- ```
506
-
507
- ### `cDOM.state(value, options)` / `cDOM.signal(value, options)`
508
-
509
- Create reactive state or signals.
337
+ Converts cDOM object to DOM.
510
338
 
511
339
  **Options:**
512
- - `name`: String. Required for persistence or global lookup.
513
- - `scope`: DOM element. Scope state to a specific tree (bubbles up).
514
- - `storage`: `localStorage` or `sessionStorage` (or custom object).
515
- - `schema`: String (named schema) or Object (inline schema).
516
- - `transform`: String (helper name) or Function.
517
-
518
- ### `cDOM.helper(name, function)`
519
-
520
- Register a helper function for use in expressions or transformations.
521
-
522
- ### `cDOM.schema(name, definition)`
523
-
524
- Register a JSON Schema for state validation.
525
-
526
- ### `_(expression, contextNode?)`
527
-
528
- Evaluate state expression.
340
+ * `target`: Element or CSS selector.
341
+ * `location`: Insertion position (`innerHTML`, `beforeend`, etc.).
342
+ * `unsafe`: Allow unsafe eval (default: `false`).
529
343
 
530
- **Parameters:**
531
- - `expression`: State path or expression
532
- - `contextNode`: Optional context element (omit for lazy binding)
344
+ ### `cDOM.operator(symbol, helperName)`
533
345
 
534
- ### `$(expression, contextNode?)`
535
-
536
- Evaluate DOM query (XPath or CSS).
537
-
538
- **Parameters:**
539
- - `expression`: XPath or CSS selector
540
- - `contextNode`: Optional context element (omit for lazy binding)
541
-
542
- ## Expression Syntax
543
-
544
- ### State Expressions `_()`
346
+ Map a custom symbol to a helper function.
545
347
 
546
348
  ```javascript
547
- "_(/path/to/value)" // Simple path
548
- "_(/a + /b)" // Math
549
- "_(/price * 1.1)" // With literal
550
- "_(helper(/arg1, /arg2))" // Helper function
551
- ```
552
-
553
- ### Supported Expressions
349
+ // Map '^' to 'pow' helper
350
+ cDOM.operator('^', 'pow');
554
351
 
555
- The built-in expression engine supports a safe subset of JavaScript. This ensures security by avoiding `eval()` while providing enough power for UI logic.
556
-
557
- **Supported:**
558
- * **Math:** `+`, `-`, `*`, `/`, `%`
559
- * **comparison:** `==`, `!=`, `<`, `>`, `<=`, `>=`
560
- * **Logic:** `&&`, `||`, `!`
561
- * **Ternary:** `condition ? trueVal : falseVal`
562
- * **Grouping:** `( )`
563
- * **Data Types:** Strings (`'hello'`, `"world"`), Numbers (`123`, `4.5`), Booleans (`true`, `false`), `null`
564
- * **Objects & Arrays:** `{ key: val }`, `[1, 2, 3]`
565
- * **Member Access:** `obj.prop`, `obj['key']`, `arr[0]`
566
- * **Functions:** Calls to registered helpers (e.g., `sum(1, 2)`)
567
-
568
- **Unsupported (Use Helpers instead):**
569
- * **Assignment/Mutation:** `=`, `+=`, `-=`, `++`, `--` (Expressions should be side-effect free)
570
- * *Workaround:* Use `onclick` handlers calling helpers to mutate state.
571
- * **Bitwise Operators:** `&`, `|`, `^`, `~`, `<<`, `>>`
572
- * **`Math` Object:** Direct access to `Math.max`, `Math.random` etc. is not exposed globally.
573
- * *Workaround:* Register needed Math functions as helpers: `cDOM.helper('max', Math.max)`.
574
- * **New Object Creation:** `new Date()`, `new RegExp()`
575
- * *Workaround:* Create helpers like `now()` or `timestamp()`.
576
- * **Arrow Functions / Lambdas:** `x => x * 2`
577
- * *Workaround:* Define complex logic in a helper.
578
- * **Try/Catch/Throw**: No control flow statements.
579
-
580
- ### DOM Queries `$()`
581
-
582
- ```javascript
583
- // XPath
584
- "$(//button)" // All buttons
585
- "$(count(//div))" // Count divs
586
- "$(../../@id)" // Parent's parent id
587
-
588
- // CSS
589
- "$(.active)" // By class
590
- "$(#main)" // By id
352
+ // Use in HTML structure
353
+ { "^": [2, 3] } // Returns 8
591
354
  ```
592
355
 
593
356
  ## Browser Support
@@ -596,37 +359,6 @@ The built-in expression engine supports a safe subset of JavaScript. This ensure
596
359
  - Requires `MutationObserver`, `Proxy`, and `XPath` APIs
597
360
  - IE11 not supported
598
361
 
599
- ## Performance
600
-
601
- - Reactive updates are batched using `queueMicrotask`
602
- - Expressions are cached after first parse
603
- - DOM queries are evaluated on-demand
604
- - Minimal overhead for static content
605
-
606
- ## Limitations
607
-
608
- - No virtual DOM diffing (direct DOM manipulation)
609
- - No component lifecycle beyond `oncreate`/`onmount`
610
- - No built-in routing
611
- - No server-side rendering
612
-
613
- ## cDOM is great for simple reactive UIs, but consider [Lightview](https://github.com/anywhichway/lightview) if you need:
614
-
615
- - **Advanced routing** with middleware chains
616
- - **Component system** with 40+ pre-built components
617
- - **Template literals** in attributes (cDOM uses `${}` style in Lightview-X mode)
618
- - **Deep integration** with CSS-in-JS and shadow DOM defaults
619
-
620
- cDOM was extracted from Lightview to provide a lightweight alternative for developers who want reactivity without the full framework.
621
-
622
362
  ## License
623
363
 
624
364
  MIT
625
-
626
- ## Contributing
627
-
628
- Issues and pull requests welcome at the [Lightview repository](https://github.com/anywhichway/lightview).
629
-
630
- ---
631
-
632
- **Made with ❤️ by the Lightview team**