@daz4126/helium 1.0.0-beta.1 → 1.0.0-beta.3

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/helium.js CHANGED
@@ -46,19 +46,23 @@ const defaultEngine = {
46
46
  }
47
47
  };
48
48
 
49
- // Factory function to create helium with custom engine and prefixes
49
+ // Factory function to create helium with custom engine
50
50
  export function createHelium(options = {}) {
51
51
  const engine = options.engine ?? defaultEngine;
52
- const prefix = options.prefix ?? 'he'; // 'he' or 'xe'
53
- const rootAttr = options.rootAttr ?? 'helium'; // 'helium' or 'xenon'
54
52
 
55
- // Build prefix-aware regex and matcher
56
- const ATTR_REGEX = new RegExp(`^(@|:|data-${prefix})`);
53
+ // Engine-aware expression parser (CSP-safe when using jexpr engine)
54
+ const evalExpr = v => {
55
+ try {
56
+ const compiled = engine.compile(v, true);
57
+ const scope = engine.createScope ? engine.createScope({ state: {}, el: null, event: {}, refs: {}, $: null, html: null, get: null, post: null, put: null, patch: null, del: null }) : {};
58
+ return compiled.execute(scope);
59
+ } catch { return v; }
60
+ };
57
61
 
58
62
  const match = (name, ...attrs) => {
59
63
  const p = name.split(/[.:]/)[0];
60
64
  if (p === ":" || p === "") return false;
61
- return attrs.some(a => p === `@${a}` || p === `data-${prefix}-${a}`);
65
+ return attrs.some(a => p === `@${a}` || p === 'data-he-' + a);
62
66
  };
63
67
 
64
68
  // Single global object to hold all settings (scoped to this instance)
@@ -67,7 +71,8 @@ export function createHelium(options = {}) {
67
71
  async function helium(initialState = {}) {
68
72
  const inits = [];
69
73
  const ALL = Symbol("all");
70
- const root = document.querySelector(`[\\@${rootAttr}]`) || document.querySelector(`[data-${rootAttr}]`) || document.body;
74
+ const root = document.querySelector('[\\@helium]') || document.querySelector('[data-helium]') || document.body;
75
+ const storageKey = root.getAttribute('@local-storage') || root.getAttribute('data-he-local-storage');
71
76
 
72
77
  // Initialize or reuse HELIUM object
73
78
  if (!HELIUM) {
@@ -225,6 +230,7 @@ export function createHelium(options = {}) {
225
230
  const parentKey = HELIUM.parentKeys.get(t);
226
231
  if (parentKey) HELIUM.bindings.get(parentKey)?.forEach(safeApplyBinding);
227
232
  HELIUM.bindings.get(ALL)?.forEach(safeApplyBinding);
233
+ if (storageKey) localStorage.setItem(storageKey, JSON.stringify(state));
228
234
  return res
229
235
  }
230
236
  };
@@ -232,6 +238,9 @@ export function createHelium(options = {}) {
232
238
  // Initialize state if it doesn't exist
233
239
  const state = new Proxy({}, handler);
234
240
 
241
+ // Load from localStorage if key exists
242
+ if (storageKey) try { Object.assign(state, JSON.parse(localStorage.getItem(storageKey))); } catch {}
243
+
235
244
  // Merge initial state
236
245
  Object.assign(state, initialState);
237
246
 
@@ -373,7 +382,8 @@ export function createHelium(options = {}) {
373
382
  const heElements = [element, ...element.querySelectorAll("*")].filter(e => {
374
383
  if (HELIUM.processed.has(e)) return false;
375
384
  for (let i = 0; i < e.attributes.length; i++) {
376
- if (ATTR_REGEX.test(e.attributes[i].name)) return true;
385
+ const n = e.attributes[i].name;
386
+ if (n[0] === '@' || n[0] === ':' || n.startsWith('data-he')) return true;
377
387
  }
378
388
  return false;
379
389
  });
@@ -386,7 +396,7 @@ export function createHelium(options = {}) {
386
396
  const importPromises = [];
387
397
 
388
398
  heElements.forEach((el) => {
389
- const importAttr = el.getAttribute("@import") || el.getAttribute(`data-${prefix}-import`);
399
+ const importAttr = el.getAttribute("@import") || el.getAttribute('data-he-import');
390
400
  if (importAttr) {
391
401
  importAttr.split(",").map((m) => m.trim()).forEach((moduleName) => {
392
402
  // Build path: use as-is for URLs, otherwise resolve relative to document location
@@ -434,7 +444,7 @@ export function createHelium(options = {}) {
434
444
  const {name, value} = attrs[i];
435
445
 
436
446
  // Skip non-helium attributes early
437
- if (!name.startsWith('@') && !name.startsWith(':') && !name.startsWith(`data-${prefix}`)) continue;
447
+ if (name[0] !== '@' && name[0] !== ':' && !name.startsWith('data-he')) continue;
438
448
 
439
449
  // Initialize state first if needed (skip reserved words, only if not already set)
440
450
  if (match(name, "text", "html", "bind") && isValidIdentifier(value) && !RESERVED.has(value) && state[value] == null) {
@@ -442,11 +452,11 @@ export function createHelium(options = {}) {
442
452
  }
443
453
 
444
454
  // Process the attribute
445
- if (name === "@data" || name === `data-${prefix}`) {
446
- Object.assign(state, parseEx(value));
455
+ if (name === "@data" || name === 'data-he') {
456
+ Object.assign(state, evalExpr(value));
447
457
  }
448
- else if (name.startsWith(":") || name.startsWith(`data-${prefix}-attr:`)) {
449
- const propName = name.startsWith(":") ? name.slice(1) : name.slice(prefix.length + 11);
458
+ else if (name.startsWith(":") || name.startsWith('data-he-attr:')) {
459
+ const propName = name.startsWith(":") ? name.slice(1) : name.slice(13);
450
460
  deferredBindings.push({el, prop: propName, compiled: compile(value, true)});
451
461
  }
452
462
  else if (match(name, "ref")) {
@@ -482,8 +492,8 @@ export function createHelium(options = {}) {
482
492
  else if (match(name, "init")) {
483
493
  inits.push({ compiled: compile(value, true), el });
484
494
  }
485
- else if (name.startsWith("@") || name.startsWith(`data-${prefix}`)) {
486
- const fullName = name.startsWith("@") ? name.slice(1) : name.slice(prefix.length + 6);
495
+ else if (name.startsWith("@") || name.startsWith('data-he')) {
496
+ const fullName = name.startsWith("@") ? name.slice(1) : name.slice(8);
487
497
  const [eventName, ...mods] = fullName.split(".");
488
498
  const isHttpMethod = ["get", "post", "put", "patch", "delete"].includes(eventName);
489
499
  const event = isHttpMethod ? getEvent(el) : eventName;
@@ -503,12 +513,12 @@ export function createHelium(options = {}) {
503
513
  }
504
514
  if (!mods.includes("outside") || !el.contains(e.target)) {
505
515
  if (isHttpMethod) {
506
- const getAttr = name => el.getAttribute(`data-${prefix}-${name}`) || el.getAttribute(`@${name}`);
516
+ const getAttr = a => el.getAttribute('data-he-' + a) || el.getAttribute('@' + a);
507
517
  const pairs = (getAttr('target') || "").split(",").map(p => p.split(":").map(s => s.trim()));
508
518
  const target = pairs.map(([target]) => target);
509
519
  const action = pairs.map(([, action]) => action);
510
520
  const options = {
511
- ...(getAttr("options") && parseEx(getAttr("options") || "{}")),
521
+ ...(getAttr("options") && evalExpr(getAttr("options") || "{}")),
512
522
  ...(target && { target }),
513
523
  ...(action && { action }),
514
524
  ...(getAttr("template") && { template: execExpr(getAttr("template")),}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daz4126/helium",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.3",
4
4
  "main": "helium.js",
5
5
  "type": "module",
6
6
  "exports": {