lume-js 2.2.1 → 2.3.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.
- package/AGENT_GUIDE.md +224 -0
- package/README.md +97 -16
- package/dist/addons.min.mjs +1 -1
- package/dist/addons.mjs +190 -7
- package/dist/addons.mjs.map +1 -1
- package/dist/handlers.min.mjs +1 -1
- package/dist/handlers.mjs +29 -2
- package/dist/handlers.mjs.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.mjs +4 -141
- package/dist/index.mjs.map +1 -1
- package/dist/lume.global.js +1 -1
- package/dist/lume.global.js.map +1 -1
- package/dist/shared-BGg9PbiG.mjs +249 -0
- package/dist/shared-BGg9PbiG.mjs.map +1 -0
- package/dist/shared-DmpHYKx7.mjs +15 -0
- package/dist/shared-DmpHYKx7.mjs.map +1 -0
- package/dist/shared-SUXdsYBx.mjs +233 -0
- package/dist/shared-SUXdsYBx.mjs.map +1 -0
- package/dist/state.min.mjs +1 -0
- package/dist/state.mjs +7 -0
- package/dist/state.mjs.map +1 -0
- package/llms-full.txt +6999 -0
- package/llms.txt +89 -0
- package/package.json +11 -2
- package/src/addons/index.d.ts +99 -7
- package/src/addons/index.js +13 -2
- package/src/addons/persist.js +190 -0
- package/src/addons/repeat.js +159 -10
- package/src/core/batch.js +139 -0
- package/src/core/bindDom.js +7 -3
- package/src/core/effect.js +34 -5
- package/src/core/state.js +118 -73
- package/src/handlers/index.d.ts +24 -0
- package/src/handlers/index.js +1 -0
- package/src/handlers/on.js +60 -0
- package/src/handlers/stringAttr.js +9 -2
- package/src/index.d.ts +14 -200
- package/src/index.js +3 -1
- package/src/state.d.ts +252 -0
- package/src/state.js +25 -0
- package/dist/shared-x2HJmEyO.mjs +0 -260
- package/dist/shared-x2HJmEyO.mjs.map +0 -1
package/dist/handlers.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { a as logWarn } from "./shared-DmpHYKx7.mjs";
|
|
1
2
|
const show = {
|
|
2
3
|
attr: "data-show",
|
|
3
4
|
apply(el, val) {
|
|
@@ -35,7 +36,8 @@ function classToggle(...names) {
|
|
|
35
36
|
}
|
|
36
37
|
}));
|
|
37
38
|
}
|
|
38
|
-
const DANGEROUS_SCHEME = /^(javascript
|
|
39
|
+
const DANGEROUS_SCHEME = /^(?:javascript:|vbscript:|data:text\/html)/;
|
|
40
|
+
const IGNORED_URL_CHARS = /[\u0000-\u0020\u007F]/g;
|
|
39
41
|
const URI_ATTRS = /* @__PURE__ */ new Set(["href", "src", "action", "srcset", "poster", "formaction"]);
|
|
40
42
|
function stringAttr(name) {
|
|
41
43
|
return {
|
|
@@ -46,7 +48,7 @@ function stringAttr(name) {
|
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
48
50
|
const strVal = String(val);
|
|
49
|
-
if (URI_ATTRS.has(name) && DANGEROUS_SCHEME.test(strVal)) {
|
|
51
|
+
if (URI_ATTRS.has(name) && DANGEROUS_SCHEME.test(strVal.replace(IGNORED_URL_CHARS, "").toLowerCase())) {
|
|
50
52
|
el.removeAttribute(name);
|
|
51
53
|
return;
|
|
52
54
|
}
|
|
@@ -54,6 +56,30 @@ function stringAttr(name) {
|
|
|
54
56
|
}
|
|
55
57
|
};
|
|
56
58
|
}
|
|
59
|
+
const attached = /* @__PURE__ */ new WeakMap();
|
|
60
|
+
function on(...types) {
|
|
61
|
+
return types.map((type) => ({
|
|
62
|
+
attr: `data-on${type}`,
|
|
63
|
+
apply(el, val) {
|
|
64
|
+
let byType = attached.get(el);
|
|
65
|
+
const prev = byType ? byType.get(type) : void 0;
|
|
66
|
+
if (prev) {
|
|
67
|
+
el.removeEventListener(type, prev);
|
|
68
|
+
byType.delete(type);
|
|
69
|
+
}
|
|
70
|
+
if (typeof val === "function") {
|
|
71
|
+
el.addEventListener(type, val);
|
|
72
|
+
if (!byType) {
|
|
73
|
+
byType = /* @__PURE__ */ new Map();
|
|
74
|
+
attached.set(el, byType);
|
|
75
|
+
}
|
|
76
|
+
byType.set(type, val);
|
|
77
|
+
} else if (val != null) {
|
|
78
|
+
logWarn(`[Lume.js] on('${type}'): bound value is not a function — listener detached`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
57
83
|
const BOOL_ATTRS = [
|
|
58
84
|
"readonly",
|
|
59
85
|
"open",
|
|
@@ -197,6 +223,7 @@ export {
|
|
|
197
223
|
classToggle,
|
|
198
224
|
formHandlers,
|
|
199
225
|
htmlAttrs,
|
|
226
|
+
on,
|
|
200
227
|
show,
|
|
201
228
|
stringAttr
|
|
202
229
|
};
|
package/dist/handlers.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.mjs","sources":["../src/handlers/show.js","../src/handlers/className.js","../src/handlers/boolAttr.js","../src/handlers/ariaAttr.js","../src/handlers/classToggle.js","../src/handlers/stringAttr.js","../src/handlers/htmlAttrs.js","../src/handlers/presets.js"],"sourcesContent":["/** data-show=\"key\" → el.hidden = !Boolean(val) */\nexport const show = {\n attr: 'data-show',\n apply(el, val) { el.hidden = !Boolean(val); }\n};\n","/** data-classname=\"key\" → el.className = val || '' */\nexport const className = {\n attr: 'data-classname',\n apply(el, val) { el.className = val || ''; }\n};\n","/**\n * Create a handler for any HTML boolean attribute.\n * Uses toggleAttribute() — works correctly with any attribute name\n * (readonly, contenteditable, etc.) without worrying about camelCase property names.\n *\n * @param {string} name - Attribute name (e.g., 'readonly', 'open', 'contenteditable')\n * @returns {{ attr: string, apply: function }}\n */\nexport function boolAttr(name) {\n return {\n attr: `data-${name}`,\n apply(el, val) { el.toggleAttribute(name, Boolean(val)); }\n };\n}\n","/**\n * Create a handler for an ARIA attribute.\n * Coerces value to \"true\"/\"false\" string — use stringAttr(\"aria-X\") for token/string ARIA attrs.\n *\n * @param {string} name - ARIA name, with or without \"aria-\" prefix\n * @returns {{ attr: string, apply: function }}\n */\nexport function ariaAttr(name) {\n const fullName = name.startsWith('aria-') ? name : `aria-${name}`;\n return {\n attr: `data-${fullName}`,\n apply(el, val) { el.setAttribute(fullName, val ? 'true' : 'false'); }\n };\n}\n","/**\n * Create handlers for CSS class toggling.\n * Each name creates a handler: data-class-{name}=\"key\" → el.classList.toggle(name, Boolean(val))\n * Returns an array — pass directly to handlers (auto-flattened by bindDom).\n *\n * @param {...string} names - CSS class names to create handlers for\n * @returns {Array<{ attr: string, apply: function }>}\n */\nexport function classToggle(...names) {\n return names.map(name => ({\n attr: `data-class-${name}`,\n apply(el, val) { el.classList.toggle(name, Boolean(val)); }\n }));\n}\n","/**\n * Create a handler for any string attribute (href, src, title, alt, action, etc.)\n * Sets the attribute value as a string. Removes the attribute when value is null/undefined.\n *\n * @param {string} name - HTML attribute name (e.g., 'href', 'src', 'title')\n * @returns {{ attr: string, apply: function }}\n */\n\nconst DANGEROUS_SCHEME = /^(javascript|vbscript|data\\s*:\\s*text\\/html)/i;\nconst URI_ATTRS = new Set(['href', 'src', 'action', 'srcset', 'poster', 'formaction']);\n\nexport function stringAttr(name) {\n return {\n attr: `data-${name}`,\n apply(el, val) {\n if (val == null) {\n el.removeAttribute(name);\n return;\n }\n const strVal = String(val);\n if (URI_ATTRS.has(name) && DANGEROUS_SCHEME.test(strVal)) {\n el.removeAttribute(name);\n return;\n }\n el.setAttribute(name, strVal);\n }\n };\n}\n","import { show } from './show.js';\nimport { boolAttr } from './boolAttr.js';\nimport { ariaAttr } from './ariaAttr.js';\nimport { stringAttr } from './stringAttr.js';\n\n/** @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#boolean_attributes */\nconst BOOL_ATTRS = [\n 'readonly', 'open', 'novalidate', 'formnovalidate', 'multiple',\n 'autofocus', 'autoplay', 'controls', 'loop', 'muted', 'defer',\n 'async', 'reversed', 'selected', 'inert', 'allowfullscreen',\n];\n\n/** @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes */\nconst STRING_ATTRS = [\n 'href', 'src', 'alt', 'title', 'placeholder', 'action', 'method',\n 'target', 'rel', 'type', 'name', 'role', 'lang', 'tabindex',\n 'pattern', 'min', 'max', 'step', 'minlength', 'maxlength',\n 'width', 'height', 'for', 'form', 'accept', 'autocomplete',\n 'loading', 'decoding', 'inputmode', 'enterkeyhint', 'draggable',\n 'contenteditable', 'spellcheck', 'translate', 'dir', 'id',\n 'poster', 'preload', 'download', 'media', 'sizes', 'srcset',\n 'colspan', 'rowspan', 'scope', 'headers', 'wrap', 'sandbox',\n];\n\n/** ARIA boolean state attributes — coerced to \"true\"/\"false\" string. */\nconst ARIA_BOOL_ATTRS = [\n 'pressed', 'selected', 'disabled', 'checked', 'invalid', 'required',\n 'busy', 'modal', 'multiselectable', 'multiline', 'readonly', 'atomic',\n];\n\n/** ARIA string/token/numeric attributes — value passed through as-is. */\nconst ARIA_STRING_ATTRS = [\n 'current', 'live', 'relevant', 'haspopup',\n 'sort', 'autocomplete', 'orientation',\n 'label', 'describedby', 'labelledby', 'controls', 'owns',\n 'activedescendant', 'errormessage', 'details', 'flowto',\n 'valuenow', 'valuemin', 'valuemax', 'valuetext',\n 'colcount', 'colindex', 'colspan', 'rowcount', 'rowindex', 'rowspan',\n 'level', 'setsize', 'posinset', 'placeholder', 'roledescription',\n 'keyshortcuts', 'braillelabel', 'brailleroledescription',\n];\n\n/**\n * One-import preset that enables all standard HTML attributes as reactive handlers.\n * Returns a flat array — pass directly to handlers option.\n *\n * @returns {Array<{ attr: string, apply: function }>}\n */\nexport function htmlAttrs() {\n return [\n show,\n ...BOOL_ATTRS.map(name => boolAttr(name)),\n ...STRING_ATTRS.map(name => stringAttr(name)),\n ...ARIA_BOOL_ATTRS.map(name => ariaAttr(name)),\n ...ARIA_STRING_ATTRS.map(name => stringAttr(`aria-${name}`)),\n ];\n}\n","import { boolAttr } from './boolAttr.js';\nimport { ariaAttr } from './ariaAttr.js';\n\n/** Form-related handlers (beyond built-in disabled/checked/required) */\nexport const formHandlers = [\n boolAttr('readonly'),\n];\n\n/** Additional ARIA handlers (beyond built-in aria-expanded/aria-hidden) */\nexport const a11yHandlers = [\n ariaAttr('pressed'),\n ariaAttr('selected'),\n ariaAttr('disabled'),\n];\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"handlers.mjs","sources":["../src/handlers/show.js","../src/handlers/className.js","../src/handlers/boolAttr.js","../src/handlers/ariaAttr.js","../src/handlers/classToggle.js","../src/handlers/stringAttr.js","../src/handlers/on.js","../src/handlers/htmlAttrs.js","../src/handlers/presets.js"],"sourcesContent":["/** data-show=\"key\" → el.hidden = !Boolean(val) */\nexport const show = {\n attr: 'data-show',\n apply(el, val) { el.hidden = !Boolean(val); }\n};\n","/** data-classname=\"key\" → el.className = val || '' */\nexport const className = {\n attr: 'data-classname',\n apply(el, val) { el.className = val || ''; }\n};\n","/**\n * Create a handler for any HTML boolean attribute.\n * Uses toggleAttribute() — works correctly with any attribute name\n * (readonly, contenteditable, etc.) without worrying about camelCase property names.\n *\n * @param {string} name - Attribute name (e.g., 'readonly', 'open', 'contenteditable')\n * @returns {{ attr: string, apply: function }}\n */\nexport function boolAttr(name) {\n return {\n attr: `data-${name}`,\n apply(el, val) { el.toggleAttribute(name, Boolean(val)); }\n };\n}\n","/**\n * Create a handler for an ARIA attribute.\n * Coerces value to \"true\"/\"false\" string — use stringAttr(\"aria-X\") for token/string ARIA attrs.\n *\n * @param {string} name - ARIA name, with or without \"aria-\" prefix\n * @returns {{ attr: string, apply: function }}\n */\nexport function ariaAttr(name) {\n const fullName = name.startsWith('aria-') ? name : `aria-${name}`;\n return {\n attr: `data-${fullName}`,\n apply(el, val) { el.setAttribute(fullName, val ? 'true' : 'false'); }\n };\n}\n","/**\n * Create handlers for CSS class toggling.\n * Each name creates a handler: data-class-{name}=\"key\" → el.classList.toggle(name, Boolean(val))\n * Returns an array — pass directly to handlers (auto-flattened by bindDom).\n *\n * @param {...string} names - CSS class names to create handlers for\n * @returns {Array<{ attr: string, apply: function }>}\n */\nexport function classToggle(...names) {\n return names.map(name => ({\n attr: `data-class-${name}`,\n apply(el, val) { el.classList.toggle(name, Boolean(val)); }\n }));\n}\n","/**\n * Create a handler for any string attribute (href, src, title, alt, action, etc.)\n * Sets the attribute value as a string. Removes the attribute when value is null/undefined.\n *\n * @param {string} name - HTML attribute name (e.g., 'href', 'src', 'title')\n * @returns {{ attr: string, apply: function }}\n */\n\n// Matched against a normalized copy of the value (C0 controls, space, and\n// DEL stripped; lowercased) because the browser URL parser ignores those\n// characters when determining the scheme — \"java\\tscript:alert(1)\" and\n// \" javascript:alert(1)\" both execute. The colon is required so legitimate\n// relative URLs like \"javascript-tutorial.html\" are not blocked.\nconst DANGEROUS_SCHEME = /^(?:javascript:|vbscript:|data:text\\/html)/;\nconst IGNORED_URL_CHARS = /[\\u0000-\\u0020\\u007F]/g;\nconst URI_ATTRS = new Set(['href', 'src', 'action', 'srcset', 'poster', 'formaction']);\n\nexport function stringAttr(name) {\n return {\n attr: `data-${name}`,\n apply(el, val) {\n if (val == null) {\n el.removeAttribute(name);\n return;\n }\n const strVal = String(val);\n if (URI_ATTRS.has(name) &&\n DANGEROUS_SCHEME.test(strVal.replace(IGNORED_URL_CHARS, '').toLowerCase())) {\n el.removeAttribute(name);\n return;\n }\n el.setAttribute(name, strVal);\n }\n };\n}\n","/**\n * Create handlers for declarative event wiring.\n * Each type creates a handler: data-on{type}=\"key\" wires the function held\n * at that store key as a DOM event listener.\n *\n * <button data-onclick=\"addTodo\">Add</button>\n *\n * const store = state({\n * addTodo: (event) => { ... } // a plain function in state\n * });\n * bindDom(root, store, { handlers: [on('click')] });\n *\n * Reactive like any binding: assigning a new function to the key re-wires\n * the listener; assigning null/undefined detaches it.\n *\n * Returns an array — pass directly to handlers (auto-flattened by bindDom).\n *\n * @security Same trust model as data-bind: an injected data-on* attribute\n * can only reference functions that already exist in reachable state — no\n * expressions, no eval. Ensure your HTML is trusted or sanitized.\n *\n * Note: bindDom's cleanup stops future re-wiring but does not detach\n * listeners already attached to elements that remain in the DOM. Discard\n * the bound subtree (the normal SPA teardown) to drop them.\n *\n * @param {...string} types - DOM event types ('click', 'input', 'submit', ...)\n * @returns {Array<{ attr: string, apply: function }>}\n */\n\nimport { logWarn } from '../utils/log.js';\n\n// element → Map<eventType, listener> — tracks what we attached so a\n// re-assigned store key swaps the listener instead of stacking a second one.\nconst attached = new WeakMap();\n\nexport function on(...types) {\n return types.map(type => ({\n attr: `data-on${type}`,\n apply(el, val) {\n let byType = attached.get(el);\n const prev = byType ? byType.get(type) : undefined;\n\n if (prev) {\n el.removeEventListener(type, prev);\n byType.delete(type);\n }\n\n if (typeof val === 'function') {\n el.addEventListener(type, val);\n if (!byType) {\n byType = new Map();\n attached.set(el, byType);\n }\n byType.set(type, val);\n } else if (val != null) {\n logWarn(`[Lume.js] on('${type}'): bound value is not a function — listener detached`);\n }\n }\n }));\n}\n","import { show } from './show.js';\nimport { boolAttr } from './boolAttr.js';\nimport { ariaAttr } from './ariaAttr.js';\nimport { stringAttr } from './stringAttr.js';\n\n/** @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#boolean_attributes */\nconst BOOL_ATTRS = [\n 'readonly', 'open', 'novalidate', 'formnovalidate', 'multiple',\n 'autofocus', 'autoplay', 'controls', 'loop', 'muted', 'defer',\n 'async', 'reversed', 'selected', 'inert', 'allowfullscreen',\n];\n\n/** @see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes */\nconst STRING_ATTRS = [\n 'href', 'src', 'alt', 'title', 'placeholder', 'action', 'method',\n 'target', 'rel', 'type', 'name', 'role', 'lang', 'tabindex',\n 'pattern', 'min', 'max', 'step', 'minlength', 'maxlength',\n 'width', 'height', 'for', 'form', 'accept', 'autocomplete',\n 'loading', 'decoding', 'inputmode', 'enterkeyhint', 'draggable',\n 'contenteditable', 'spellcheck', 'translate', 'dir', 'id',\n 'poster', 'preload', 'download', 'media', 'sizes', 'srcset',\n 'colspan', 'rowspan', 'scope', 'headers', 'wrap', 'sandbox',\n];\n\n/** ARIA boolean state attributes — coerced to \"true\"/\"false\" string. */\nconst ARIA_BOOL_ATTRS = [\n 'pressed', 'selected', 'disabled', 'checked', 'invalid', 'required',\n 'busy', 'modal', 'multiselectable', 'multiline', 'readonly', 'atomic',\n];\n\n/** ARIA string/token/numeric attributes — value passed through as-is. */\nconst ARIA_STRING_ATTRS = [\n 'current', 'live', 'relevant', 'haspopup',\n 'sort', 'autocomplete', 'orientation',\n 'label', 'describedby', 'labelledby', 'controls', 'owns',\n 'activedescendant', 'errormessage', 'details', 'flowto',\n 'valuenow', 'valuemin', 'valuemax', 'valuetext',\n 'colcount', 'colindex', 'colspan', 'rowcount', 'rowindex', 'rowspan',\n 'level', 'setsize', 'posinset', 'placeholder', 'roledescription',\n 'keyshortcuts', 'braillelabel', 'brailleroledescription',\n];\n\n/**\n * One-import preset that enables all standard HTML attributes as reactive handlers.\n * Returns a flat array — pass directly to handlers option.\n *\n * @returns {Array<{ attr: string, apply: function }>}\n */\nexport function htmlAttrs() {\n return [\n show,\n ...BOOL_ATTRS.map(name => boolAttr(name)),\n ...STRING_ATTRS.map(name => stringAttr(name)),\n ...ARIA_BOOL_ATTRS.map(name => ariaAttr(name)),\n ...ARIA_STRING_ATTRS.map(name => stringAttr(`aria-${name}`)),\n ];\n}\n","import { boolAttr } from './boolAttr.js';\nimport { ariaAttr } from './ariaAttr.js';\n\n/** Form-related handlers (beyond built-in disabled/checked/required) */\nexport const formHandlers = [\n boolAttr('readonly'),\n];\n\n/** Additional ARIA handlers (beyond built-in aria-expanded/aria-hidden) */\nexport const a11yHandlers = [\n ariaAttr('pressed'),\n ariaAttr('selected'),\n ariaAttr('disabled'),\n];\n"],"names":[],"mappings":"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"}
|
package/dist/index.min.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function
|
|
1
|
+
function t(t,...e){void 0!==console&&"function"==typeof console.warn&&console.warn(t,...e)}function e(t,...e){void 0!==console&&"function"==typeof console.error&&console.error(t,...e)}const r=100;let n=0;const o=new Set;function s(t){return 0!==n&&(o.add(t),!0)}function i(){let t=0;for(;o.size>0&&r>t;){t++;const r=Array.from(o);o.clear();const n=new Set;for(const t of r){t.runBeforeFlushHooks(),t.notifySubscribers();for(const e of t.takeEffects())n.add(e)}for(const t of n)try{t()}catch(t){e("[Lume.js state] Error in effect:",t)}}r>t||(o.clear(),e("[Lume.js state] Maximum batch flush iterations reached (100). This usually indicates an infinite loop caused by an effect or computed mutating state it depends on."))}function c(e){if("function"!=typeof e)throw Error("batch() requires a function");if(n>0)return e();let r;n++;try{return r=e(),r&&"function"==typeof r.then&&t("[Lume.js batch] batch() received an async function. Only writes before the first await are batched; later writes flush via normal microtasks."),r}finally{try{i()}finally{n--}}}const a=new Set,u=Symbol.for("lume.reactive");function f(t,e){a.add(t);try{return e()}finally{a.delete(t)}}function l(n){if(!n||"object"!=typeof n||Array.isArray(n))throw Error("state() requires a plain object");if(Object.isFrozen(n)||Object.isSealed(n))throw Error("state() requires a mutable plain object");const o=Object.create(null),i=new Map,c=new Set,f=[];let l=!1;function d(){for(let t=0;t<f.length;t++)try{f[t]()}catch(t){e("[Lume.js state] Error in beforeFlush hook:",t)}}function h(){const t=Array.from(i);i.clear();for(const[r,n]of t)if(o[r]){const t=o[r];let s=0;for(;s<t.length;){const o=t[s];try{o(n)}catch(t){e(`[Lume.js state] Error notifying subscriber for key "${r+""}":`,t)}t[s]===o&&s++}}}function p(){const t=Array.from(c);return c.clear(),t}const y={runBeforeFlushHooks:d,notifySubscribers:h,takeEffects:p};Object.defineProperty(n,u,{value:!0});const b=()=>{};function m(t,r,n){return o[t]||(o[t]=[]),1e3>o[t].length?(o[t].push(r),()=>{if(o[t]){const e=o[t].indexOf(r);-1!==e&&(o[t].splice(e,1),0===o[t].length&&delete o[t])}}):(e(`[Lume.js state] Subscriber limit (1000) reached for key "${t+""}". ${n} ignored — it will NOT receive updates. This usually means subscriptions are created in a loop without cleanup.`),b)}const g=(t,e)=>m(t,()=>{c.add(e)},"Effect subscription"),E=new Set(["__proto__","constructor","prototype"]),w=new Proxy(n,{get(t,e){if("string"==typeof e&&e.startsWith("$"))return t[e];const r=t[e];if(a.size>0)for(const t of a)t(w,e,g);return r},set(n,o,a){if("string"==typeof o&&E.has(o))return t(`[Lume.js state] Blocked write to reserved key "${o}"`),!0;const u=n[o];return Object.is(u,a)||(n[o]=a,i.set(o,a),s(y)||l||(l=!0,queueMicrotask(()=>{let t=0;try{for(;(i.size>0||c.size>0)&&r>t;){t++,d(),h();const r=p();for(let t=0;t<r.length;t++)try{r[t]()}catch(t){e("[Lume.js state] Error in effect:",t)}}}finally{l=!1}r>t||e("[Lume.js state] Maximum flush iterations reached (100). This usually indicates an infinite loop caused by an effect or computed mutating state it depends on.")}))),!0}});return n.$beforeFlush=t=>{if("function"!=typeof t)throw Error("$beforeFlush requires a function");return-1===f.indexOf(t)&&f.push(t),()=>{const e=f.indexOf(t);-1!==e&&f.splice(e,1)}},n.$subscribe=(t,e)=>{if("function"!=typeof e)throw Error("Subscriber must be a function");const r=m(t,e,"New subscriber");return r===b||e(w[t]),r},w}const d=t=>({attr:"data-"+t,apply(e,r){e[t]=!!r}}),h=t=>({attr:"data-"+t,apply(e,r){e.setAttribute(t,r?"true":"false")}}),p=[d("hidden"),d("disabled"),d("checked"),d("required"),h("aria-expanded"),h("aria-hidden")];function y(t,e){if(!e.length)return t;const r=new Map;for(const e of t)r.set(e.attr,e);for(const t of e.flat())r.set(t.attr,t);return[...r.values()]}function b(t,e,r={}){if(!(t instanceof HTMLElement))throw Error("bindDom() requires a valid HTMLElement as root");if(!e||"object"!=typeof e)throw Error("bindDom() requires a reactive state object");const{immediate:n=!1,handlers:o=[]}=r,s=y(p,o),i=()=>{const r=[],n=new WeakMap,o=["[data-bind]",...s.map(t=>`[${t.attr}]`)].join(","),i=t.querySelectorAll(o);for(const t of i){if(t.hasAttribute("data-bind")){const o=g(t,e,t.getAttribute("data-bind"),n);o&&r.push(o)}for(const n of s)if(t.hasAttribute(n.attr)){const o=m(t,e,t.getAttribute(n.attr),n);o&&r.push(o)}}const c=t=>{const e=n.get(t.target);e&&(e.target[e.key]=j(t.target))};return t.addEventListener("input",c),r.push(()=>t.removeEventListener("input",c)),()=>r.forEach(t=>t())};if(!n&&"loading"===document.readyState){let t=null;const e=()=>{t=i()};return document.addEventListener("DOMContentLoaded",e,{once:!0}),()=>t?t():document.removeEventListener("DOMContentLoaded",e)}return i()}function m(t,e,r,n){const o=w(e,r);if(!o)return null;const{target:s,key:i}=o;return s.$subscribe(i,e=>n.apply(t,e))}function g(t,e,r,n){const o=w(e,r);if(!o)return null;const{target:s,key:i}=o,c=s.$subscribe(i,e=>k(t,e));return v(t)&&n.set(t,{target:s,key:i}),c}function E(t,e){if(!e||0===e.length)return t;let r=t;for(let t=0;t<e.length;t++){const n=e[t];if(null==r)return null;if(!(n in r))return null;r=r[n]}return r}function w(e,r){if(!r)return null;const n=r.split("."),o=n.pop(),s=E(e,n);return null==s?(t(`[Lume.js] Invalid path "${r}"`),null):s?.$subscribe?{target:s,key:o}:(t(`[Lume.js] Target for "${r}" is not reactive`),null)}function k(t,e){"INPUT"===t.tagName?"checkbox"===t.type?t.checked=!!e:"radio"===t.type?t.checked=t.value===e+"":t.value=e??"":"TEXTAREA"===t.tagName||"SELECT"===t.tagName?t.value=e??"":t.textContent=e??""}function j(t){return"checkbox"===t.type?t.checked:"number"===t.type||"range"===t.type?t.valueAsNumber:t.value}function v(t){return"INPUT"===t.tagName||"TEXTAREA"===t.tagName||"SELECT"===t.tagName}let L=null;function A(t,r){if("function"!=typeof t)throw Error("effect() requires a function");const n=[];let o=!1;const s=()=>{if(!o){o=!0;try{t()}catch(t){throw e("[Lume.js effect] Error in effect:",t),t}finally{o=!1}}};if(Array.isArray(r)){let t=!1,e=!1;n.push(()=>{e=!0});const o=()=>{t||(t=!0,queueMicrotask(()=>{if(t=!1,!e)try{s()}catch{}}))};for(const t of r)if(Array.isArray(t)&&t.length>=2){const[e,...r]=t;if(e&&"function"==typeof e.$subscribe)for(const t of r){let r=!0;const s=e.$subscribe(t,()=>{r?r=!1:o()});n.push(s)}}s()}else{const r=()=>{if(o)return;const s=n.splice(0),i={fn:t,cleanups:n,execute:r,tracking:new WeakMap},c=L;L=i,o=!0;try{f((t,e,r)=>{if(L!==i)return;let n=i.tracking.get(t);n||(n=new Set,i.tracking.set(t,n)),n.has(e)||(n.add(e),i.cleanups.push(r(e,i.execute)))},t)}catch(t){throw n.length=0,n.push(...s),e("[Lume.js effect] Error in effect:",t),t}finally{L=c,o=!1}if(n.length>0)for(const t of s)t();else n.push(...s)};r()}return()=>{for(;n.length;)n.pop()()}}export{c as batch,b as bindDom,A as effect,l as state,f as withReadObserver};
|
package/dist/index.mjs
CHANGED
|
@@ -1,145 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
const boolHandler = (name) => ({
|
|
4
|
-
attr: `data-${name}`,
|
|
5
|
-
apply(el, val) {
|
|
6
|
-
el[name] = Boolean(val);
|
|
7
|
-
}
|
|
8
|
-
});
|
|
9
|
-
const ariaHandler = (name) => ({
|
|
10
|
-
attr: `data-${name}`,
|
|
11
|
-
apply(el, val) {
|
|
12
|
-
el.setAttribute(name, val ? "true" : "false");
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
const DEFAULT_HANDLERS = [
|
|
16
|
-
boolHandler("hidden"),
|
|
17
|
-
boolHandler("disabled"),
|
|
18
|
-
boolHandler("checked"),
|
|
19
|
-
boolHandler("required"),
|
|
20
|
-
ariaHandler("aria-expanded"),
|
|
21
|
-
ariaHandler("aria-hidden")
|
|
22
|
-
];
|
|
23
|
-
function mergeHandlers(defaults, userHandlers) {
|
|
24
|
-
if (!userHandlers.length) return defaults;
|
|
25
|
-
const merged = /* @__PURE__ */ new Map();
|
|
26
|
-
for (const h of defaults) merged.set(h.attr, h);
|
|
27
|
-
for (const h of userHandlers.flat()) merged.set(h.attr, h);
|
|
28
|
-
return [...merged.values()];
|
|
29
|
-
}
|
|
30
|
-
function bindDom(root, store, options = {}) {
|
|
31
|
-
if (!(root instanceof HTMLElement)) {
|
|
32
|
-
throw new Error("bindDom() requires a valid HTMLElement as root");
|
|
33
|
-
}
|
|
34
|
-
if (!store || typeof store !== "object") {
|
|
35
|
-
throw new Error("bindDom() requires a reactive state object");
|
|
36
|
-
}
|
|
37
|
-
const { immediate = false, handlers: userHandlers = [] } = options;
|
|
38
|
-
const handlers = mergeHandlers(DEFAULT_HANDLERS, userHandlers);
|
|
39
|
-
const performBinding = () => {
|
|
40
|
-
const cleanups = [];
|
|
41
|
-
const bindingMap = /* @__PURE__ */ new WeakMap();
|
|
42
|
-
const selector = ["[data-bind]", ...handlers.map((h) => `[${h.attr}]`)].join(",");
|
|
43
|
-
const elements = root.querySelectorAll(selector);
|
|
44
|
-
for (const el of elements) {
|
|
45
|
-
if (el.hasAttribute("data-bind")) {
|
|
46
|
-
const c = handleDataBind(el, store, el.getAttribute("data-bind"), bindingMap);
|
|
47
|
-
if (c) cleanups.push(c);
|
|
48
|
-
}
|
|
49
|
-
for (const handler of handlers) {
|
|
50
|
-
if (el.hasAttribute(handler.attr)) {
|
|
51
|
-
const c = applyHandler(el, store, el.getAttribute(handler.attr), handler);
|
|
52
|
-
if (c) cleanups.push(c);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
const inputHandler = (e2) => {
|
|
57
|
-
const binding = bindingMap.get(e2.target);
|
|
58
|
-
if (binding) binding.target[binding.key] = getInputValue(e2.target);
|
|
59
|
-
};
|
|
60
|
-
root.addEventListener("input", inputHandler);
|
|
61
|
-
cleanups.push(() => root.removeEventListener("input", inputHandler));
|
|
62
|
-
return () => cleanups.forEach((c) => c());
|
|
63
|
-
};
|
|
64
|
-
if (!immediate && document.readyState === "loading") {
|
|
65
|
-
let cleanup = null;
|
|
66
|
-
const onReady = () => {
|
|
67
|
-
cleanup = performBinding();
|
|
68
|
-
};
|
|
69
|
-
document.addEventListener("DOMContentLoaded", onReady, { once: true });
|
|
70
|
-
return () => cleanup ? cleanup() : document.removeEventListener("DOMContentLoaded", onReady);
|
|
71
|
-
}
|
|
72
|
-
return performBinding();
|
|
73
|
-
}
|
|
74
|
-
function applyHandler(el, store, path, handler) {
|
|
75
|
-
const result = resolveProp(store, path);
|
|
76
|
-
if (!result) return null;
|
|
77
|
-
const { target, key } = result;
|
|
78
|
-
return target.$subscribe(key, (val) => handler.apply(el, val));
|
|
79
|
-
}
|
|
80
|
-
function handleDataBind(el, store, path, bindingMap) {
|
|
81
|
-
const result = resolveProp(store, path);
|
|
82
|
-
if (!result) return null;
|
|
83
|
-
const { target, key } = result;
|
|
84
|
-
const unsub = target.$subscribe(key, (val) => updateElement(el, val));
|
|
85
|
-
if (isFormInput(el)) {
|
|
86
|
-
bindingMap.set(el, { target, key });
|
|
87
|
-
}
|
|
88
|
-
return unsub;
|
|
89
|
-
}
|
|
90
|
-
function resolvePath(obj, pathArr) {
|
|
91
|
-
if (!pathArr || pathArr.length === 0) {
|
|
92
|
-
return obj;
|
|
93
|
-
}
|
|
94
|
-
let current = obj;
|
|
95
|
-
for (let i = 0; i < pathArr.length; i++) {
|
|
96
|
-
const key = pathArr[i];
|
|
97
|
-
if (current === null || current === void 0) {
|
|
98
|
-
return null;
|
|
99
|
-
}
|
|
100
|
-
if (!(key in current)) {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
current = current[key];
|
|
104
|
-
}
|
|
105
|
-
return current;
|
|
106
|
-
}
|
|
107
|
-
function resolveProp(store, path) {
|
|
108
|
-
if (!path) return null;
|
|
109
|
-
const pathArr = path.split(".");
|
|
110
|
-
const key = pathArr.pop();
|
|
111
|
-
const target = resolvePath(store, pathArr);
|
|
112
|
-
if (target === null || target === void 0) {
|
|
113
|
-
logWarn(`[Lume.js] Invalid path "${path}"`);
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
if (!target?.$subscribe) {
|
|
117
|
-
logWarn(`[Lume.js] Target for "${path}" is not reactive`);
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
return { target, key };
|
|
121
|
-
}
|
|
122
|
-
function updateElement(el, val) {
|
|
123
|
-
if (el.tagName === "INPUT") {
|
|
124
|
-
if (el.type === "checkbox") el.checked = Boolean(val);
|
|
125
|
-
else if (el.type === "radio") el.checked = el.value === String(val);
|
|
126
|
-
else el.value = val ?? "";
|
|
127
|
-
} else if (el.tagName === "TEXTAREA" || el.tagName === "SELECT") {
|
|
128
|
-
el.value = val ?? "";
|
|
129
|
-
} else {
|
|
130
|
-
el.textContent = val ?? "";
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
function getInputValue(el) {
|
|
134
|
-
if (el.type === "checkbox") return el.checked;
|
|
135
|
-
if (el.type === "number" || el.type === "range") return el.valueAsNumber;
|
|
136
|
-
return el.value;
|
|
137
|
-
}
|
|
138
|
-
function isFormInput(el) {
|
|
139
|
-
return el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.tagName === "SELECT";
|
|
140
|
-
}
|
|
1
|
+
import { b, s, w } from "./shared-SUXdsYBx.mjs";
|
|
2
|
+
import { b as b2, e } from "./shared-BGg9PbiG.mjs";
|
|
141
3
|
export {
|
|
142
|
-
|
|
4
|
+
b as batch,
|
|
5
|
+
b2 as bindDom,
|
|
143
6
|
e as effect,
|
|
144
7
|
s as state,
|
|
145
8
|
w as withReadObserver
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/core/bindDom.js"],"sourcesContent":["// src/core/bindDom.js\n/**\n * Lume-JS DOM Binding\n *\n * Binds reactive state to DOM elements using data-* attributes.\n *\n * Built-in attributes (always available):\n * data-bind=\"key\" → Two-way binding for inputs, textContent for others\n * data-hidden=\"key\" → Toggles hidden (truthy = hidden)\n * data-disabled=\"key\" → Toggles disabled (truthy = disabled)\n * data-checked=\"key\" → Toggles checked (for checkboxes/radios)\n * data-required=\"key\" → Toggles required (truthy = required)\n * data-aria-expanded=\"key\" → Sets aria-expanded to \"true\"/\"false\"\n * data-aria-hidden=\"key\" → Sets aria-hidden to \"true\"/\"false\"\n *\n * Extensible via handlers option:\n * import { show, classToggle } from 'lume-js/handlers';\n * bindDom(root, store, { handlers: [show, classToggle('active')] });\n *\n * Custom handlers:\n * const tooltip = { attr: 'data-tooltip', apply(el, val) { el.title = val ?? ''; } };\n * bindDom(root, store, { handlers: [tooltip] });\n *\n * Usage:\n * import { bindDom } from \"lume-js\";\n * const cleanup = bindDom(document.body, store);\n *\n * @security `data-bind` attribute values are resolved once at bind time and\n * trusted as state path expressions. If an attacker can inject `data-bind`\n * attributes into the DOM, they can subscribe to any reachable reactive state.\n * Ensure your HTML is trusted or sanitize it before calling bindDom().\n */\n\nimport { logWarn } from '../utils/log.js';\n\n// --- Default Handlers (always active, backwards compatible) ---\n\nconst boolHandler = (name) => ({\n attr: `data-${name}`,\n apply(el, val) { el[name] = Boolean(val); }\n});\n\nconst ariaHandler = (name) => ({\n attr: `data-${name}`,\n apply(el, val) { el.setAttribute(name, val ? 'true' : 'false'); }\n});\n\nconst DEFAULT_HANDLERS = [\n boolHandler('hidden'),\n boolHandler('disabled'),\n boolHandler('checked'),\n boolHandler('required'),\n ariaHandler('aria-expanded'),\n ariaHandler('aria-hidden'),\n];\n\n/**\n * Merge default and user handlers.\n * User handlers override defaults with same attr (Map deduplicates).\n * User handler arrays are flattened one level (supports classToggle()).\n */\nfunction mergeHandlers(defaults, userHandlers) {\n if (!userHandlers.length) return defaults;\n const merged = new Map();\n for (const h of defaults) merged.set(h.attr, h);\n for (const h of userHandlers.flat()) merged.set(h.attr, h);\n return [...merged.values()];\n}\n\n/**\n * DOM binding for reactive state\n */\nexport function bindDom(root, store, options = {}) {\n if (!(root instanceof HTMLElement)) {\n throw new Error('bindDom() requires a valid HTMLElement as root');\n }\n if (!store || typeof store !== 'object') {\n throw new Error('bindDom() requires a reactive state object');\n }\n\n const { immediate = false, handlers: userHandlers = [] } = options;\n const handlers = mergeHandlers(DEFAULT_HANDLERS, userHandlers);\n\n const performBinding = () => {\n const cleanups = [];\n const bindingMap = new WeakMap();\n\n // Build compiled selector: data-bind (always) + all handler attrs\n const selector = ['[data-bind]', ...handlers.map(h => `[${h.attr}]`)].join(',');\n const elements = root.querySelectorAll(selector);\n\n for (const el of elements) {\n // data-bind (two-way) — always in core, special handling\n if (el.hasAttribute('data-bind')) {\n const c = handleDataBind(el, store, el.getAttribute('data-bind'), bindingMap);\n if (c) cleanups.push(c);\n }\n\n // All registered handlers (default + user)\n for (const handler of handlers) {\n if (el.hasAttribute(handler.attr)) {\n const c = applyHandler(el, store, el.getAttribute(handler.attr), handler);\n if (c) cleanups.push(c);\n }\n }\n }\n\n // Event delegation for two-way bindings\n const inputHandler = e => {\n const binding = bindingMap.get(e.target);\n if (binding) binding.target[binding.key] = getInputValue(e.target);\n };\n root.addEventListener(\"input\", inputHandler);\n cleanups.push(() => root.removeEventListener(\"input\", inputHandler));\n\n return () => cleanups.forEach(c => c());\n };\n\n // Auto-wait for DOM if needed\n if (!immediate && document.readyState === 'loading') {\n let cleanup = null;\n const onReady = () => { cleanup = performBinding(); };\n document.addEventListener('DOMContentLoaded', onReady, { once: true });\n return () => cleanup ? cleanup() : document.removeEventListener('DOMContentLoaded', onReady);\n }\n\n return performBinding();\n}\n\n/**\n * Apply a handler to an element via subscription.\n * Resolves the state path and subscribes to changes.\n */\nfunction applyHandler(el, store, path, handler) {\n const result = resolveProp(store, path);\n if (!result) return null;\n const { target, key } = result;\n return target.$subscribe(key, val => handler.apply(el, val));\n}\n\n/**\n * Handle data-bind (two-way for inputs, textContent for others)\n */\nfunction handleDataBind(el, store, path, bindingMap) {\n const result = resolveProp(store, path);\n if (!result) return null;\n\n const { target, key } = result;\n const unsub = target.$subscribe(key, val => updateElement(el, val));\n\n if (isFormInput(el)) {\n bindingMap.set(el, { target, key });\n }\n\n return unsub;\n}\n\n/**\n * Resolve a nested path in an object.\n * Example: resolvePath(obj, ['user', 'address']) returns obj.user.address\n */\nfunction resolvePath(obj, pathArr) {\n if (!pathArr || pathArr.length === 0) {\n return obj;\n }\n let current = obj;\n for (let i = 0; i < pathArr.length; i++) {\n const key = pathArr[i];\n if (current === null || current === undefined) {\n return null;\n }\n if (!(key in current)) {\n return null;\n }\n current = current[key];\n }\n return current;\n}\n\n/**\n * Resolve path to target and key.\n *\n * ⚠️ Path bindings are resolved once at bind time. If an intermediate\n * object in the path is null/undefined at bindDom call time, the binding\n * is permanently dead and will not self-heal when the path later becomes valid.\n */\nfunction resolveProp(store, path) {\n if (!path) return null;\n\n const pathArr = path.split(\".\");\n const key = pathArr.pop();\n const target = resolvePath(store, pathArr);\n\n if (target === null || target === undefined) {\n logWarn(`[Lume.js] Invalid path \"${path}\"`);\n return null;\n }\n\n if (!target?.$subscribe) {\n logWarn(`[Lume.js] Target for \"${path}\" is not reactive`);\n return null;\n }\n\n return { target, key };\n}\n\n/**\n * Update element with value (for data-bind)\n */\nfunction updateElement(el, val) {\n if (el.tagName === \"INPUT\") {\n if (el.type === \"checkbox\") el.checked = Boolean(val);\n else if (el.type === \"radio\") el.checked = el.value === String(val);\n else el.value = val ?? '';\n } else if (el.tagName === \"TEXTAREA\" || el.tagName === \"SELECT\") {\n el.value = val ?? '';\n } else {\n el.textContent = val ?? '';\n }\n}\n\n/**\n * Get value from input\n */\nfunction getInputValue(el) {\n if (el.type === \"checkbox\") return el.checked;\n if (el.type === \"number\" || el.type === \"range\") return el.valueAsNumber;\n return el.value;\n}\n\n/**\n * Check if element is form input\n */\nfunction isFormInput(el) {\n return el.tagName === \"INPUT\" || el.tagName === \"TEXTAREA\" || el.tagName === \"SELECT\";\n}"],"names":["e"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
package/dist/lume.global.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var Lume=function(e){"use strict";function t(e,...t){void 0!==console&&"function"==typeof console.warn&&console.warn(e,...t)}function o(e,...t){void 0!==console&&"function"==typeof console.error&&console.error(e,...t)}const n=new Set;function r(e,t){n.add(e);try{return t()}finally{n.delete(e)}}const c=e=>({attr:"data-"+e,apply(t,o){t[e]=!!o}}),s=e=>({attr:"data-"+e,apply(t,o){t.setAttribute(e,o?"true":"false")}}),i=[c("hidden"),c("disabled"),c("checked"),c("required"),s("aria-expanded"),s("aria-hidden")];function l(e,t,o,n){const r=u(t,o);if(!r)return null;const{target:c,key:s}=r;return c.$subscribe(s,t=>n.apply(e,t))}function a(e,t,o,n){const r=u(t,o);if(!r)return null;const{target:c,key:s}=r,i=c.$subscribe(s,t=>function(e,t){"INPUT"===e.tagName?"checkbox"===e.type?e.checked=!!t:"radio"===e.type?e.checked=e.value===t+"":e.value=t??"":"TEXTAREA"===e.tagName||"SELECT"===e.tagName?e.value=t??"":e.textContent=t??""}(e,t));return function(e){return"INPUT"===e.tagName||"TEXTAREA"===e.tagName||"SELECT"===e.tagName}(e)&&n.set(e,{target:c,key:s}),i}function u(e,o){if(!o)return null;const n=o.split("."),r=n.pop(),c=function(e,t){if(!t||0===t.length)return e;let o=e;for(let n=0;n<t.length;n++){const e=t[n];if(null==o)return null;if(!(e in o))return null;o=o[e]}return o}(e,n);return null==c?(t(`[Lume.js] Invalid path "${o}"`),null):c?.$subscribe?{target:c,key:r}:(t(`[Lume.js] Target for "${o}" is not reactive`),null)}let f=null;function d(e,t){if("function"!=typeof e)throw Error("effect() requires a function");const n=[];let c=!1;const s=()=>{if(!c){c=!0;try{e()}catch(t){throw o("[Lume.js effect] Error in effect:",t),t}finally{c=!1}}};if(Array.isArray(t)){for(const e of t)if(Array.isArray(e)&&e.length>=2){const[t,...o]=e;if(t&&"function"==typeof t.$subscribe)for(const e of o){let o=!0;const r=t.$subscribe(e,()=>{o?o=!1:s()});n.push(r)}}s()}else{const t=()=>{if(c)return;const s=n.splice(0),i={fn:e,cleanups:n,execute:t,tracking:{}},l=f;f=i,c=!0;try{r((e,t,o)=>{f===i&&(i.tracking[t]||(i.tracking[t]=!0,i.cleanups.push(o(t,i.execute))))},e)}catch(a){throw n.length=0,n.push(...s),o("[Lume.js effect] Error in effect:",a),a}finally{f=l,c=!1}if(n.length>0)for(const e of s)e();else n.push(...s)};t()}return()=>{for(;n.length;)n.pop()()}}function p(e){const t=document.activeElement;if(!e.contains(t))return null;let o=null,n=null;return"INPUT"!==t.tagName&&"TEXTAREA"!==t.tagName||(o=t.selectionStart,n=t.selectionEnd),()=>{document.body.contains(t)&&(t.focus(),null!==o&&null!==n&&t.setSelectionRange(o,n))}}function g(e,t={}){const{isReorder:o=!1}=t,n=e.scrollTop;if(0===n)return()=>{e.scrollTop=0};let r=null,c=0;if(!o){const t=e.getBoundingClientRect();for(let o=e.firstElementChild;o;o=o.nextElementSibling){const e=o.getBoundingClientRect();if(e.bottom>t.top){r=o,c=e.top-t.top;break}}}return()=>{if(r&&document.body.contains(r)){const t=r.getBoundingClientRect(),o=e.getBoundingClientRect(),n=t.top-o.top-c;e.scrollTop=e.scrollTop+n}else e.scrollTop=n}}let b=!0,h=null;const y=new Map;function m(e){return null===h||("string"==typeof h?e.includes(h):!(h instanceof RegExp)||h.test(e))}function w(e,t,o){const n=function(e){return y.has(e)||y.set(e,{gets:new Map,sets:new Map,notifies:new Map}),y.get(e)}(e),r=n[t];r.set(o,(r.get(o)||0)+1)}function v(e){try{const t=JSON.stringify(e);return t.length>100?t.slice(0,97)+"...":t}catch{return e+""}}const $={enable(){b=!0,console.log("%c[lume-debug]%c Logging enabled","color: #888; font-weight: bold","color: #4CAF50")},disable(){b=!1,console.log("%c[lume-debug]%c Logging disabled","color: #888; font-weight: bold","color: #F44336")},isEnabled:()=>b,filter(e){h=e,console.log(null===e?"%c[lume-debug]%c Filter cleared":"%c[lume-debug]%c Filter set: "+e,"color: #888; font-weight: bold","color: inherit")},getFilter:()=>h,stats(){const e={};for(const[t,o]of y)e[t]={gets:Object.fromEntries(o.gets),sets:Object.fromEntries(o.sets),notifies:Object.fromEntries(o.notifies)};return e},logStats(){const e=this.stats();if(0===Object.keys(e).length)return console.log("%c[lume-debug]%c No stats collected yet","color: #888; font-weight: bold","color: inherit"),e;console.group("%c[lume-debug] Statistics","color: #888; font-weight: bold");for(const[t,o]of Object.entries(e)){console.group("%c"+t,"color: #2196F3; font-weight: bold");const e=[],n=new Set([...Object.keys(o.gets),...Object.keys(o.sets),...Object.keys(o.notifies)]);for(const t of n)e.push({key:t,gets:o.gets[t]||0,sets:o.sets[t]||0,notifies:o.notifies[t]||0});e.length>0&&console.table(e),console.groupEnd()}return console.groupEnd(),e},resetStats(){y.clear(),console.log("%c[lume-debug]%c Stats reset","color: #888; font-weight: bold","color: inherit")}},E={attr:"data-show",apply(e,t){e.hidden=!t}},j={attr:"data-classname",apply(e,t){e.className=t||""}};function S(e){return{attr:"data-"+e,apply(t,o){t.toggleAttribute(e,!!o)}}}function k(e){const t=e.startsWith("aria-")?e:"aria-"+e;return{attr:"data-"+t,apply(e,o){e.setAttribute(t,o?"true":"false")}}}const L=/^(javascript|vbscript|data\s*:\s*text\/html)/i,A=new Set(["href","src","action","srcset","poster","formaction"]);function x(e){return{attr:"data-"+e,apply(t,o){if(null==o)return void t.removeAttribute(e);const n=o+"";A.has(e)&&L.test(n)?t.removeAttribute(e):t.setAttribute(e,n)}}}const O=["readonly","open","novalidate","formnovalidate","multiple","autofocus","autoplay","controls","loop","muted","defer","async","reversed","selected","inert","allowfullscreen"],T=["href","src","alt","title","placeholder","action","method","target","rel","type","name","role","lang","tabindex","pattern","min","max","step","minlength","maxlength","width","height","for","form","accept","autocomplete","loading","decoding","inputmode","enterkeyhint","draggable","contenteditable","spellcheck","translate","dir","id","poster","preload","download","media","sizes","srcset","colspan","rowspan","scope","headers","wrap","sandbox"],N=["pressed","selected","disabled","checked","invalid","required","busy","modal","multiselectable","multiline","readonly","atomic"],F=["current","live","relevant","haspopup","sort","autocomplete","orientation","label","describedby","labelledby","controls","owns","activedescendant","errormessage","details","flowto","valuenow","valuemin","valuemax","valuetext","colcount","colindex","colspan","rowcount","rowindex","rowspan","level","setsize","posinset","placeholder","roledescription","keyshortcuts","braillelabel","brailleroledescription"],C=[S("readonly")],M=[k("pressed"),k("selected"),k("disabled")];return e.a11yHandlers=M,e.ariaAttr=k,e.bindDom=function(e,t,o={}){if(!(e instanceof HTMLElement))throw Error("bindDom() requires a valid HTMLElement as root");if(!t||"object"!=typeof t)throw Error("bindDom() requires a reactive state object");const{immediate:n=!1,handlers:r=[]}=o,c=function(e,t){if(!t.length)return e;const o=new Map;for(const n of e)o.set(n.attr,n);for(const n of t.flat())o.set(n.attr,n);return[...o.values()]}(i,r),s=()=>{const o=[],n=new WeakMap,r=["[data-bind]",...c.map(e=>`[${e.attr}]`)].join(","),s=e.querySelectorAll(r);for(const e of s){if(e.hasAttribute("data-bind")){const r=a(e,t,e.getAttribute("data-bind"),n);r&&o.push(r)}for(const n of c)if(e.hasAttribute(n.attr)){const r=l(e,t,e.getAttribute(n.attr),n);r&&o.push(r)}}const i=e=>{const t=n.get(e.target);var o;t&&(t.target[t.key]="checkbox"===(o=e.target).type?o.checked:"number"===o.type||"range"===o.type?o.valueAsNumber:o.value)};return e.addEventListener("input",i),o.push(()=>e.removeEventListener("input",i)),()=>o.forEach(e=>e())};if(!n&&"loading"===document.readyState){let e=null;const t=()=>{e=s()};return document.addEventListener("DOMContentLoaded",t,{once:!0}),()=>e?e():document.removeEventListener("DOMContentLoaded",t)}return s()},e.boolAttr=S,e.className=j,e.classToggle=function(...e){return e.map(e=>({attr:"data-class-"+e,apply(t,o){t.classList.toggle(e,!!o)}}))},e.computed=function(e){if("function"!=typeof e)throw Error("computed() requires a function");let t,n=!1,r=!1,c=!1;const s=[],i=d(()=>{if(!r&&!c){r=!0;try{const o=e();n&&Object.is(o,t)||(t=o,n=!0,s.forEach(e=>e(t)))}catch(i){o("[Lume.js computed] Error in computation:",i),n&&void 0===t||(t=void 0,n=!0,s.forEach(e=>e(t)))}finally{queueMicrotask(()=>{c||(r=!1)})}}});return{get value(){if(!n)throw Error("Computed value accessed before initialization");return t},subscribe(e){if("function"!=typeof e)throw Error("subscribe() requires a function");return s.push(e),n&&e(t),()=>{const t=s.indexOf(e);t>-1&&s.splice(t,1)}},dispose(){c=!0,i(),s.length=0,n=!1,r=!1}}},e.createCleanupGroup=function(){const e=[];return{add(t){"function"==typeof t&&e.push(t)},dispose(){for(;e.length;){const o=e.pop();try{o()}catch(t){}}}}},e.createDebugPlugin=function(e={}){const t=e.label??"store",o=(t,o)=>{const n=e[t];return void 0!==n?n:o};return{name:"debug:"+t,onInit:()=>{b&&console.log(`%c[${t}]%c initialized`,"color: #888; font-weight: bold","color: inherit")},onGet:(e,n)=>("string"==typeof e&&e.startsWith("$")||(w(t,"gets",e),b&&o("logGet",!1)&&m(e)&&console.log(`%c[${t}]%c GET %c${e}%c = ${v(n)}`,"color: #888; font-weight: bold","color: #4CAF50","color: #2196F3; font-weight: bold","color: inherit")),n),onSet:(e,n,r)=>("string"==typeof e&&e.startsWith("$")||(w(t,"sets",e),b&&o("logSet",!0)&&m(e)&&(console.log(`%c[${t}]%c SET %c${e}%c: ${v(r)} → ${v(n)}`,"color: #888; font-weight: bold","color: #FF9800","color: #2196F3; font-weight: bold","color: inherit"),o("trace",!1)&&console.trace(`%c[${t}] Stack trace for ${e}`,"color: #888"))),n),onSubscribe:e=>{b&&m(e)&&console.log(`%c[${t}]%c SUBSCRIBE %c${e}`,"color: #888; font-weight: bold","color: #9C27B0","color: #2196F3; font-weight: bold")},onNotify:(e,n)=>{"string"==typeof e&&e.startsWith("$")||(w(t,"notifies",e),b&&o("logNotify",!0)&&m(e)&&console.log(`%c[${t}]%c NOTIFY %c${e}%c = ${v(n)}`,"color: #888; font-weight: bold","color: #E91E63","color: #2196F3; font-weight: bold","color: inherit"))}}},e.debug=$,e.defaultFocusPreservation=p,e.defaultScrollPreservation=g,e.effect=d,e.formHandlers=C,e.htmlAttrs=function(){return[E,...O.map(e=>S(e)),...T.map(e=>x(e)),...N.map(e=>k(e)),...F.map(e=>x("aria-"+e))]},e.hydrateState=function(e="#__LUME_DATA__",t){const o="undefined"!=typeof document?document.querySelector(e):null;if(!o)return{};if("SCRIPT"!==o.tagName||"application/json"!==o.type)return{};let n;try{n=JSON.parse(o.textContent)}catch{return{}}return"function"!=typeof t||t(n)?n:{}},e.isReactive=function(e){return!(!e||"object"!=typeof e||"function"!=typeof e.$subscribe)},e.repeat=function(e,n,r,c){const{key:s,render:i,create:l,update:a,remove:u,element:f="div",preserveFocus:d=p,preserveScroll:b=g}=c,h="string"==typeof e?document.querySelector(e):e;if(!h)return t(`[Lume.js] repeat(): container "${e}" not found`),()=>{};if("function"!=typeof s)throw Error("[Lume.js] repeat(): options.key must be a function");if("function"!=typeof i&&"function"!=typeof l)throw Error("[Lume.js] repeat(): options.render or options.create must be a function");const y=new Map,m=new Map,w=new Map,v=new Map,$=new Set;function E(){return"function"==typeof f?f():document.createElement(f)}function j(){const e=n[r];if(!Array.isArray(e))return void t(`[Lume.js] repeat(): store.${r} is not an array`);let c=!1;if(b&&y.size===e.length){c=!0;for(let t=0;t<e.length;t++)if(!y.has(s(e[t]))){c=!1;break}}$.clear();const f=[];for(let n=0;n<e.length;n++){const r=e[n],c=s(r);if($.has(c)){t(`[Lume.js] repeat(): duplicate key "${c}"`);continue}$.add(c);let u=y.get(c);const d=!u;d&&(u=E(),y.set(c,u));try{if(d&&l){const e=l(r,u,n);"function"==typeof e&&v.set(c,e)}const e=m.get(c),t=w.get(c);a?e===r&&t===n||a(r,u,n,{isFirstRender:d}):i&&i(r,u,n),m.set(c,r),w.set(c,n)}catch(p){o(`[Lume.js] repeat(): error rendering key "${c}":`,p)}f.push(u)}!function(e,t,n){const r=document.body.contains(e),c=r&&d?d(e):null,s=r&&b?b(e,{isReorder:n}):null;(()=>{if(function(e,t){let o=e.firstChild;for(let n=0;n<t.length;n++){const r=t[n];o!==r?e.insertBefore(r,o):o=o.nextSibling}for(;o;){const t=o.nextSibling;e.removeChild(o),o=t}}(h,f),y.size!==$.size)for(const e of y.keys())if(!$.has(e)){const t=y.get(e),n=m.get(e),r=v.get(e);if("function"==typeof r)try{r()}catch(p){o(`[Lume.js] repeat(): cleanup error for key "${e}":`,p)}"function"==typeof u&&t&&u(n,t),y.delete(e),m.delete(e),w.delete(e),v.delete(e)}})(),c&&c(),s&&s()}(h,0,c)}let S;if("function"==typeof n.$subscribe)S=n.$subscribe(r,j);else{if("function"!=typeof n.subscribe)return j(),t("[Lume.js] repeat(): store is not reactive (no $subscribe or subscribe method)"),()=>{for(const[t,n]of y){const r=m.get(t),c=v.get(t);if("function"==typeof c)try{c()}catch(e){o(`[Lume.js] repeat(): cleanup error for key "${t}":`,e)}"function"==typeof u&&u(r,n)}h.replaceChildren(),y.clear(),m.clear(),w.clear(),v.clear(),$.clear()};{const e=n.subscribe(()=>j());j(),S="function"==typeof e?e:()=>{e?.unsubscribe?.()}}}return()=>{"function"==typeof S&&S();for(const[t,n]of y){const r=m.get(t),c=v.get(t);if("function"==typeof c)try{c()}catch(e){o(`[Lume.js] repeat(): cleanup error for key "${t}":`,e)}"function"==typeof u&&u(r,n)}h.replaceChildren(),y.clear(),m.clear(),w.clear(),v.clear(),$.clear()}},e.show=E,e.state=function(e){if(!e||"object"!=typeof e||Array.isArray(e))throw Error("state() requires a plain object");if(Object.isFrozen(e)||Object.isSealed(e))throw Error("state() requires a mutable plain object");const r=Object.create(null),c=new Map,s=new Set,i=[];let l=!1;e[Symbol("lume.reactive")]=!0;const a=(e,t)=>{r[e]||(r[e]=[]);const o=()=>{s.add(t)};return r[e].push(o),()=>{if(r[e]){const t=r[e].indexOf(o);-1!==t&&(r[e].splice(t,1),0===r[e].length&&delete r[e])}}},u=new Set(["__proto__","constructor","prototype"]),f=new Proxy(e,{get(e,t){if("string"==typeof t&&t.startsWith("$"))return e[t];const o=e[t];if(n.size>0)for(const r of n)r(f,t,a);return o},set(e,n,a){if("string"==typeof n&&u.has(n))return t(`[Lume.js state] Blocked write to reserved key "${n}"`),!0;const f=e[n];return Object.is(f,a)||(e[n]=a,c.set(n,a),l||(l=!0,queueMicrotask(()=>{let e=0;try{for(;(c.size>0||s.size>0)&&100>e;){e++;for(let e=0;e<i.length;e++)try{i[e]()}catch(t){o("[Lume.js state] Error in beforeFlush hook:",t)}for(const[e,s]of c)if(r[e]){const n=r[e];let c=0;for(;c<n.length;){const r=n[c];try{r(s)}catch(t){o(`[Lume.js state] Error notifying subscriber for key "${e+""}":`,t)}n[c]===r&&c++}}c.clear();const n=Array(s.size);let l=0;for(const e of s)n[l++]=e;s.clear();for(let e=0;e<n.length;e++)try{n[e]()}catch(t){o("[Lume.js state] Error in effect:",t)}}}finally{l=!1}100>e||o("[Lume.js state] Maximum flush iterations reached (100). This usually indicates an infinite loop caused by an effect or computed mutating state it depends on.")}))),!0}});return e.$beforeFlush=e=>{if("function"!=typeof e)throw Error("$beforeFlush requires a function");return-1===i.indexOf(e)&&i.push(e),()=>{const t=i.indexOf(e);-1!==t&&i.splice(t,1)}},e.$subscribe=(e,o)=>{if("function"!=typeof o)throw Error("Subscriber must be a function");return r[e]||(r[e]=[]),1e3>r[e].length?(r[e].push(o),o(f[e]),()=>{if(r[e]){const t=r[e].indexOf(o);-1!==t&&(r[e].splice(t,1),0===r[e].length&&delete r[e])}}):(t(`[Lume.js state] Subscriber limit (1000) reached for key "${e}". New subscriber ignored.`),()=>{})},f},e.stringAttr=x,e.watch=function(e,t,o,{immediate:n=!0}={}){if(!e.$subscribe)throw Error("store must be created with state()");if(!n){let n=!1;return e.$subscribe(t,e=>{n?o(e):n=!0})}return e.$subscribe(t,o)},e.withPlugins=function(e,t=[]){if(!t.length)return e;for(const s of t){try{s.onInit?.()}catch(c){o(`[Lume.js] Plugin "${s.name}" error in onInit:`,c)}Object.freeze(s)}const n=new Map;let r;return"function"==typeof e.$beforeFlush&&(r=e.$beforeFlush(function(){for(const[e,r]of n)for(const n of t)try{n.onNotify?.(e,r)}catch(c){o(`[Lume.js] Plugin "${n.name}" error in onNotify:`,c)}n.clear()})),new Proxy(e,{get(e,s){if("$dispose"===s)return()=>{r&&r(),n.clear()};if("string"==typeof s&&s.startsWith("$")){const n=e[s];return"$subscribe"===s&&"function"==typeof n?(e,r)=>{for(const n of t)try{n.onSubscribe?.(e)}catch(c){o(`[Lume.js] Plugin "${n.name}" error in onSubscribe:`,c)}return n(e,r)}:n}let i=e[s];for(const n of t)try{const e=n.onGet?.(s,i);void 0!==e&&(i=e)}catch(c){o(`[Lume.js] Plugin "${n.name}" error in onGet:`,c)}return i},set(e,r,s){const i=e[r];let l=s;for(const n of t)try{const e=n.onSet?.(r,l,i);void 0!==e&&(l=e)}catch(c){o(`[Lume.js] Plugin "${n.name}" error in onSet:`,c)}return Object.is(l,i)||n.set(r,l),e[r]=l,!0}})},e.withReadObserver=r,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),e}({});
|
|
1
|
+
var Lume=function(e){"use strict";function t(e,...t){void 0!==console&&"function"==typeof console.warn&&console.warn(e,...t)}function o(e,...t){void 0!==console&&"function"==typeof console.error&&console.error(e,...t)}const n=100;let r=0;const s=new Set,i=new Set,c=Symbol.for("lume.reactive");function a(e,t){i.add(e);try{return t()}finally{i.delete(e)}}const l=e=>({attr:"data-"+e,apply(t,o){t[e]=!!o}}),u=e=>({attr:"data-"+e,apply(t,o){t.setAttribute(e,o?"true":"false")}}),f=[l("hidden"),l("disabled"),l("checked"),l("required"),u("aria-expanded"),u("aria-hidden")];function d(e,t,o,n){const r=h(t,o);if(!r)return null;const{target:s,key:i}=r;return s.$subscribe(i,t=>n.apply(e,t))}function p(e,t,o,n){const r=h(t,o);if(!r)return null;const{target:s,key:i}=r,c=s.$subscribe(i,t=>b(e,t));return function(e){return"INPUT"===e.tagName||"TEXTAREA"===e.tagName||"SELECT"===e.tagName}(e)&&n.set(e,{target:s,key:i}),c}function h(e,o){if(!o)return null;const n=o.split("."),r=n.pop(),s=function(e,t){if(!t||0===t.length)return e;let o=e;for(let n=0;n<t.length;n++){const e=t[n];if(null==o)return null;if(!(e in o))return null;o=o[e]}return o}(e,n);return null==s?(t(`[Lume.js] Invalid path "${o}"`),null):s?.$subscribe?{target:s,key:r}:(t(`[Lume.js] Target for "${o}" is not reactive`),null)}function b(e,t){"INPUT"===e.tagName?"checkbox"===e.type?e.checked=!!t:"radio"===e.type?e.checked=e.value===t+"":e.value=t??"":"TEXTAREA"===e.tagName||"SELECT"===e.tagName?e.value=t??"":e.textContent=t??""}let g=null;function y(e,t){if("function"!=typeof e)throw Error("effect() requires a function");const n=[];let r=!1;const s=()=>{if(!r){r=!0;try{e()}catch(t){throw o("[Lume.js effect] Error in effect:",t),t}finally{r=!1}}};if(Array.isArray(t)){let e=!1,o=!1;n.push(()=>{o=!0});const r=()=>{e||(e=!0,queueMicrotask(()=>{if(e=!1,!o)try{s()}catch{}}))};for(const s of t)if(Array.isArray(s)&&s.length>=2){const[e,...t]=s;if(e&&"function"==typeof e.$subscribe)for(const o of t){let t=!0;const s=e.$subscribe(o,()=>{t?t=!1:r()});n.push(s)}}s()}else{const t=()=>{if(r)return;const s=n.splice(0),i={fn:e,cleanups:n,execute:t,tracking:new WeakMap},c=g;g=i,r=!0;try{a((e,t,o)=>{if(g!==i)return;let n=i.tracking.get(e);n||(n=new Set,i.tracking.set(e,n)),n.has(t)||(n.add(t),i.cleanups.push(o(t,i.execute)))},e)}catch(l){throw n.length=0,n.push(...s),o("[Lume.js effect] Error in effect:",l),l}finally{g=c,r=!1}if(n.length>0)for(const e of s)e();else n.push(...s)};t()}return()=>{for(;n.length;)n.pop()()}}function m(e){const t=[],o=e=>{const o=e.getAttribute("data-bind");t.push({node:e,path:o,keys:"$item"===o||"$index"===o?null:o.split(".")})};e.hasAttribute("data-bind")&&o(e);for(const n of e.querySelectorAll("[data-bind]"))o(n);return t}function w(e,t,o){for(const n of e){let e;if("$index"===n.path)e=o;else if("$item"===n.path)e=t;else{e=t;for(let t=0;t<n.keys.length&&null!=e;t++)e=e[n.keys[t]]}b(n.node,e)}}function v(e){const t=document.activeElement;if(!e.contains(t))return null;let o=null,n=null;return"INPUT"!==t.tagName&&"TEXTAREA"!==t.tagName||(o=t.selectionStart,n=t.selectionEnd),()=>{document.body.contains(t)&&(t.focus(),null!==o&&null!==n&&t.setSelectionRange(o,n))}}function E(e,t={}){const{isReorder:o=!1}=t,n=e.scrollTop;if(0===n)return()=>{e.scrollTop=0};let r=null,s=0;if(!o){const t=e.getBoundingClientRect();for(let o=e.firstElementChild;o;o=o.nextElementSibling){const e=o.getBoundingClientRect();if(e.bottom>t.top){r=o,s=e.top-t.top;break}}}return()=>{if(r&&document.body.contains(r)){const t=r.getBoundingClientRect(),o=e.getBoundingClientRect(),n=t.top-o.top-s;e.scrollTop=e.scrollTop+n}else e.scrollTop=n}}let j=!0,$=null;const k=new Map;function L(e){return null===$||("string"==typeof $?e.includes($):!($ instanceof RegExp)||$.test(e))}function S(e,t,o){const n=function(e){return k.has(e)||k.set(e,{gets:new Map,sets:new Map,notifies:new Map}),k.get(e)}(e),r=n[t];r.set(o,(r.get(o)||0)+1)}function A(e){try{const t=JSON.stringify(e);return t.length>100?t.slice(0,97)+"...":t}catch{return e+""}}const O={enable(){j=!0,console.log("%c[lume-debug]%c Logging enabled","color: #888; font-weight: bold","color: #4CAF50")},disable(){j=!1,console.log("%c[lume-debug]%c Logging disabled","color: #888; font-weight: bold","color: #F44336")},isEnabled:()=>j,filter(e){$=e,console.log(null===e?"%c[lume-debug]%c Filter cleared":"%c[lume-debug]%c Filter set: "+e,"color: #888; font-weight: bold","color: inherit")},getFilter:()=>$,stats(){const e={};for(const[t,o]of k)e[t]={gets:Object.fromEntries(o.gets),sets:Object.fromEntries(o.sets),notifies:Object.fromEntries(o.notifies)};return e},logStats(){const e=this.stats();if(0===Object.keys(e).length)return console.log("%c[lume-debug]%c No stats collected yet","color: #888; font-weight: bold","color: inherit"),e;console.group("%c[lume-debug] Statistics","color: #888; font-weight: bold");for(const[t,o]of Object.entries(e)){console.group("%c"+t,"color: #2196F3; font-weight: bold");const e=[],n=new Set([...Object.keys(o.gets),...Object.keys(o.sets),...Object.keys(o.notifies)]);for(const t of n)e.push({key:t,gets:o.gets[t]||0,sets:o.sets[t]||0,notifies:o.notifies[t]||0});e.length>0&&console.table(e),console.groupEnd()}return console.groupEnd(),e},resetStats(){k.clear(),console.log("%c[lume-debug]%c Stats reset","color: #888; font-weight: bold","color: inherit")}};function x(e,t){const o={};for(const n of t)o[n]=e[n];return JSON.stringify(o)}const T=new WeakMap,N={attr:"data-show",apply(e,t){e.hidden=!t}},M={attr:"data-classname",apply(e,t){e.className=t||""}};function F(e){return{attr:"data-"+e,apply(t,o){t.toggleAttribute(e,!!o)}}}function C(e){const t=e.startsWith("aria-")?e:"aria-"+e;return{attr:"data-"+t,apply(e,o){e.setAttribute(t,o?"true":"false")}}}const q=/^(?:javascript:|vbscript:|data:text\/html)/,R=/[\u0000-\u0020\u007F]/g,P=new Set(["href","src","action","srcset","poster","formaction"]);function z(e){return{attr:"data-"+e,apply(t,o){if(null==o)return void t.removeAttribute(e);const n=o+"";P.has(e)&&q.test(n.replace(R,"").toLowerCase())?t.removeAttribute(e):t.setAttribute(e,n)}}}const I=new WeakMap,B=["readonly","open","novalidate","formnovalidate","multiple","autofocus","autoplay","controls","loop","muted","defer","async","reversed","selected","inert","allowfullscreen"],W=["href","src","alt","title","placeholder","action","method","target","rel","type","name","role","lang","tabindex","pattern","min","max","step","minlength","maxlength","width","height","for","form","accept","autocomplete","loading","decoding","inputmode","enterkeyhint","draggable","contenteditable","spellcheck","translate","dir","id","poster","preload","download","media","sizes","srcset","colspan","rowspan","scope","headers","wrap","sandbox"],_=["pressed","selected","disabled","checked","invalid","required","busy","modal","multiselectable","multiline","readonly","atomic"],D=["current","live","relevant","haspopup","sort","autocomplete","orientation","label","describedby","labelledby","controls","owns","activedescendant","errormessage","details","flowto","valuenow","valuemin","valuemax","valuetext","colcount","colindex","colspan","rowcount","rowindex","rowspan","level","setsize","posinset","placeholder","roledescription","keyshortcuts","braillelabel","brailleroledescription"],G=[F("readonly")],H=[C("pressed"),C("selected"),C("disabled")];return e.a11yHandlers=H,e.ariaAttr=C,e.batch=function(e){if("function"!=typeof e)throw Error("batch() requires a function");if(r>0)return e();let i;r++;try{return i=e(),i&&"function"==typeof i.then&&t("[Lume.js batch] batch() received an async function. Only writes before the first await are batched; later writes flush via normal microtasks."),i}finally{try{!function(){let e=0;for(;s.size>0&&n>e;){e++;const n=Array.from(s);s.clear();const r=new Set;for(const e of n){e.runBeforeFlushHooks(),e.notifySubscribers();for(const t of e.takeEffects())r.add(t)}for(const e of r)try{e()}catch(t){o("[Lume.js state] Error in effect:",t)}}n>e||(s.clear(),o("[Lume.js state] Maximum batch flush iterations reached (100). This usually indicates an infinite loop caused by an effect or computed mutating state it depends on."))}()}finally{r--}}},e.bindDom=function(e,t,o={}){if(!(e instanceof HTMLElement))throw Error("bindDom() requires a valid HTMLElement as root");if(!t||"object"!=typeof t)throw Error("bindDom() requires a reactive state object");const{immediate:n=!1,handlers:r=[]}=o,s=function(e,t){if(!t.length)return e;const o=new Map;for(const n of e)o.set(n.attr,n);for(const n of t.flat())o.set(n.attr,n);return[...o.values()]}(f,r),i=()=>{const o=[],n=new WeakMap,r=["[data-bind]",...s.map(e=>`[${e.attr}]`)].join(","),i=e.querySelectorAll(r);for(const e of i){if(e.hasAttribute("data-bind")){const r=p(e,t,e.getAttribute("data-bind"),n);r&&o.push(r)}for(const n of s)if(e.hasAttribute(n.attr)){const r=d(e,t,e.getAttribute(n.attr),n);r&&o.push(r)}}const c=e=>{const t=n.get(e.target);var o;t&&(t.target[t.key]="checkbox"===(o=e.target).type?o.checked:"number"===o.type||"range"===o.type?o.valueAsNumber:o.value)};return e.addEventListener("input",c),o.push(()=>e.removeEventListener("input",c)),()=>o.forEach(e=>e())};if(!n&&"loading"===document.readyState){let e=null;const t=()=>{e=i()};return document.addEventListener("DOMContentLoaded",t,{once:!0}),()=>e?e():document.removeEventListener("DOMContentLoaded",t)}return i()},e.boolAttr=F,e.className=M,e.classToggle=function(...e){return e.map(e=>({attr:"data-class-"+e,apply(t,o){t.classList.toggle(e,!!o)}}))},e.computed=function(e){if("function"!=typeof e)throw Error("computed() requires a function");let t,n=!1,r=!1,s=!1;const i=[],c=y(()=>{if(!r&&!s){r=!0;try{const o=e();n&&Object.is(o,t)||(t=o,n=!0,i.forEach(e=>e(t)))}catch(c){o("[Lume.js computed] Error in computation:",c),n&&void 0===t||(t=void 0,n=!0,i.forEach(e=>e(t)))}finally{queueMicrotask(()=>{s||(r=!1)})}}});return{get value(){if(!n)throw Error("Computed value accessed before initialization");return t},subscribe(e){if("function"!=typeof e)throw Error("subscribe() requires a function");return i.push(e),n&&e(t),()=>{const t=i.indexOf(e);t>-1&&i.splice(t,1)}},dispose(){s=!0,c(),i.length=0,n=!1,r=!1}}},e.createCleanupGroup=function(){const e=[];return{add(t){"function"==typeof t&&e.push(t)},dispose(){for(;e.length;){const o=e.pop();try{o()}catch(t){}}}}},e.createDebugPlugin=function(e={}){const t=e.label??"store",o=(t,o)=>{const n=e[t];return void 0!==n?n:o};return{name:"debug:"+t,onInit:()=>{j&&console.log(`%c[${t}]%c initialized`,"color: #888; font-weight: bold","color: inherit")},onGet:(e,n)=>("string"==typeof e&&e.startsWith("$")||(S(t,"gets",e),j&&o("logGet",!1)&&L(e)&&console.log(`%c[${t}]%c GET %c${e}%c = ${A(n)}`,"color: #888; font-weight: bold","color: #4CAF50","color: #2196F3; font-weight: bold","color: inherit")),n),onSet:(e,n,r)=>("string"==typeof e&&e.startsWith("$")||(S(t,"sets",e),j&&o("logSet",!0)&&L(e)&&(console.log(`%c[${t}]%c SET %c${e}%c: ${A(r)} → ${A(n)}`,"color: #888; font-weight: bold","color: #FF9800","color: #2196F3; font-weight: bold","color: inherit"),o("trace",!1)&&console.trace(`%c[${t}] Stack trace for ${e}`,"color: #888"))),n),onSubscribe:e=>{j&&L(e)&&console.log(`%c[${t}]%c SUBSCRIBE %c${e}`,"color: #888; font-weight: bold","color: #9C27B0","color: #2196F3; font-weight: bold")},onNotify:(e,n)=>{"string"==typeof e&&e.startsWith("$")||(S(t,"notifies",e),j&&o("logNotify",!0)&&L(e)&&console.log(`%c[${t}]%c NOTIFY %c${e}%c = ${A(n)}`,"color: #888; font-weight: bold","color: #E91E63","color: #2196F3; font-weight: bold","color: inherit"))}}},e.debug=O,e.defaultFocusPreservation=v,e.defaultScrollPreservation=E,e.effect=y,e.formHandlers=G,e.htmlAttrs=function(){return[N,...B.map(e=>F(e)),...W.map(e=>z(e)),..._.map(e=>C(e)),...D.map(e=>z("aria-"+e))]},e.hydrateState=function(e="#__LUME_DATA__",t){const o="undefined"!=typeof document?document.querySelector(e):null;if(!o)return{};if("SCRIPT"!==o.tagName||"application/json"!==o.type)return{};let n;try{n=JSON.parse(o.textContent)}catch{return{}}return"function"!=typeof t||t(n)?n:{}},e.isReactive=function(e){return!(!e||"object"!=typeof e||!(c in e)&&"function"!=typeof e.$subscribe)},e.on=function(...e){return e.map(e=>({attr:"data-on"+e,apply(o,n){let r=I.get(o);const s=r?r.get(e):void 0;s&&(o.removeEventListener(e,s),r.delete(e)),"function"==typeof n?(o.addEventListener(e,n),r||(r=new Map,I.set(o,r)),r.set(e,n)):null!=n&&t(`[Lume.js] on('${e}'): bound value is not a function — listener detached`)}}))},e.persist=function(e,o,n={}){if(!e||"function"!=typeof e.$subscribe)throw Error("[Lume.js] persist() requires a reactive store from state()");if("string"!=typeof o||0===o.length)throw Error("[Lume.js] persist() requires a non-empty storage key");const r=void 0!==n.storage?n.storage:function(){try{return"undefined"!=typeof localStorage?localStorage:void 0}catch{return}}();if(!r||"function"!=typeof r.getItem)return t("[Lume.js] persist(): no storage available — persistence disabled"),()=>{};const s=Array.isArray(n.keys)?n.keys.slice():Object.keys(e).filter(e=>!e.startsWith("$"));let i=T.get(r);i||(i=new Set,T.set(r,i));const c=!i.has(o);c?i.add(o):t(`[Lume.js] persist(): "${o}" is already managed by another persist() on this storage — instances will overwrite each other's data. Use a distinct key per store.`);const a=function(e,o){try{const t=e.getItem(o);if(!t)return null;const n=JSON.parse(t);return n&&"object"==typeof n&&!Array.isArray(n)?n:null}catch{return t(`[Lume.js] persist(): could not read "${o}" — starting fresh`),null}}(r,o);if(a)for(const t of s)Object.prototype.hasOwnProperty.call(a,t)&&(e[t]=a[t]);let l=null;try{l=x(e,s)}catch{}let u=!1,f=!1;const d=()=>{if(u=!1,f)return;let n;try{n=x(e,s)}catch(i){return void t("[Lume.js] persist(): state not serializable — skipping save",i)}if(n!==l)try{r.setItem(o,n),l=n}catch(i){t("[Lume.js] persist(): could not write — storage full or unavailable?",i)}},p=s.map(t=>{let o=!0;return e.$subscribe(t,()=>{o?o=!1:u||(u=!0,queueMicrotask(d))})});return()=>{for(f=!0,c&&i.delete(o);p.length;)p.pop()()}},e.repeat=function(e,n,r,s){const{key:i,render:c,create:a,update:l,remove:u,template:f=null,element:d="div",preserveFocus:p=v,preserveScroll:h=E}=s,b="string"==typeof e?document.querySelector(e):e;if(!b)return t(`[Lume.js] repeat(): container "${e}" not found`),()=>{};if("function"!=typeof i)throw Error("[Lume.js] repeat(): options.key must be a function");const{templateRoot:g,keepEl:y}=function(e,t){if(!e)return{templateRoot:null,keepEl:null};const o=function(e,t){let o=e;if(!0===e?o=t.querySelector("template"):"string"==typeof e&&(o=document.querySelector(e)),!o||"TEMPLATE"!==o.tagName)throw Error("[Lume.js] repeat(): template not found or not a <template> element");if(1!==o.content.children.length)throw Error("[Lume.js] repeat(): template must contain exactly one root element");return o}(e,t);let n=o;for(;n&&n.parentNode!==t;)n=n.parentNode;return{templateRoot:o.content.firstElementChild,keepEl:n}}(f,b);if(g&&"function"==typeof c&&t("[Lume.js] repeat(): options.render is ignored when options.template is set — use create/update instead"),!g&&"function"!=typeof c&&"function"!=typeof a)throw Error("[Lume.js] repeat(): options.render or options.create must be a function");const j=new Map,$=new Map,k=new Map,L=new Map,S=new Map,A=new Set;function O(){return g?g.cloneNode(!0):"function"==typeof d?d():document.createElement(d)}function x(){let e=b.firstChild;for(;e;){const t=e.nextSibling;e!==y&&b.removeChild(e),e=t}}function T(){const e=n[r];if(!Array.isArray(e))return void t(`[Lume.js] repeat(): store.${r} is not an array`);let s=!1;if(h&&j.size===e.length){s=!0;for(let t=0;t<e.length;t++)if(!j.has(i(e[t]))){s=!1;break}}A.clear();const f=[];for(let n=0;n<e.length;n++){const r=e[n],s=i(r);if(A.has(s)){t(`[Lume.js] repeat(): duplicate key "${s}"`);continue}A.add(s);let u=j.get(s);const p=!u;p&&(u=O(),j.set(s,u),g&&S.set(s,m(u)));try{if(p&&a){const e=a(r,u,n);"function"==typeof e&&L.set(s,e)}const e=$.get(s),t=k.get(s);g?e===r&&t===n||(w(S.get(s),r,n),l&&l(r,u,n,{isFirstRender:p})):l?e===r&&t===n||l(r,u,n,{isFirstRender:p}):c&&c(r,u,n),$.set(s,r),k.set(s,n)}catch(d){o(`[Lume.js] repeat(): error rendering key "${s}":`,d)}f.push(u)}!function(e,t,n){const r=document.body.contains(e),s=r&&p?p(e):null,i=r&&h?h(e,{isReorder:n}):null;(()=>{if(function(e,t){let o=e.firstChild;for(let n=0;n<t.length;n++){y&&o===y&&(o=o.nextSibling);const r=t[n];o!==r?e.insertBefore(r,o):o=o.nextSibling}for(;o;){const t=o.nextSibling;o!==y&&e.removeChild(o),o=t}}(b,f),j.size!==A.size)for(const e of j.keys())if(!A.has(e)){const t=j.get(e),n=$.get(e),r=L.get(e);if("function"==typeof r)try{r()}catch(d){o(`[Lume.js] repeat(): cleanup error for key "${e}":`,d)}"function"==typeof u&&t&&u(n,t),j.delete(e),$.delete(e),k.delete(e),L.delete(e),S.delete(e)}})(),s&&s(),i&&i()}(b,0,s)}let N;if("function"==typeof n.$subscribe)N=n.$subscribe(r,T);else{if("function"!=typeof n.subscribe)return T(),t("[Lume.js] repeat(): store is not reactive (no $subscribe or subscribe method)"),()=>{for(const[t,n]of j){const r=$.get(t),s=L.get(t);if("function"==typeof s)try{s()}catch(e){o(`[Lume.js] repeat(): cleanup error for key "${t}":`,e)}"function"==typeof u&&u(r,n)}x(),j.clear(),$.clear(),k.clear(),L.clear(),S.clear(),A.clear()};{const e=n.subscribe(()=>T());T(),N="function"==typeof e?e:()=>{e?.unsubscribe?.()}}}return()=>{"function"==typeof N&&N();for(const[t,n]of j){const r=$.get(t),s=L.get(t);if("function"==typeof s)try{s()}catch(e){o(`[Lume.js] repeat(): cleanup error for key "${t}":`,e)}"function"==typeof u&&u(r,n)}x(),j.clear(),$.clear(),k.clear(),L.clear(),S.clear(),A.clear()}},e.show=N,e.state=function(e){if(!e||"object"!=typeof e||Array.isArray(e))throw Error("state() requires a plain object");if(Object.isFrozen(e)||Object.isSealed(e))throw Error("state() requires a mutable plain object");const a=Object.create(null),l=new Map,u=new Set,f=[];let d=!1;function p(){for(let t=0;t<f.length;t++)try{f[t]()}catch(e){o("[Lume.js state] Error in beforeFlush hook:",e)}}function h(){const e=Array.from(l);l.clear();for(const[n,r]of e)if(a[n]){const e=a[n];let s=0;for(;s<e.length;){const i=e[s];try{i(r)}catch(t){o(`[Lume.js state] Error notifying subscriber for key "${n+""}":`,t)}e[s]===i&&s++}}}function b(){const e=Array.from(u);return u.clear(),e}const g={runBeforeFlushHooks:p,notifySubscribers:h,takeEffects:b};Object.defineProperty(e,c,{value:!0});const y=()=>{};function m(e,t,n){return a[e]||(a[e]=[]),1e3>a[e].length?(a[e].push(t),()=>{if(a[e]){const o=a[e].indexOf(t);-1!==o&&(a[e].splice(o,1),0===a[e].length&&delete a[e])}}):(o(`[Lume.js state] Subscriber limit (1000) reached for key "${e+""}". ${n} ignored — it will NOT receive updates. This usually means subscriptions are created in a loop without cleanup.`),y)}const w=(e,t)=>m(e,()=>{u.add(t)},"Effect subscription"),v=new Set(["__proto__","constructor","prototype"]),E=new Proxy(e,{get(e,t){if("string"==typeof t&&t.startsWith("$"))return e[t];const o=e[t];if(i.size>0)for(const n of i)n(E,t,w);return o},set(e,i,c){if("string"==typeof i&&v.has(i))return t(`[Lume.js state] Blocked write to reserved key "${i}"`),!0;const a=e[i];return Object.is(a,c)||(e[i]=c,l.set(i,c),f=g,(0===r||(s.add(f),0))&&(d||(d=!0,queueMicrotask(()=>{let e=0;try{for(;(l.size>0||u.size>0)&&n>e;){e++,p(),h();const n=b();for(let e=0;e<n.length;e++)try{n[e]()}catch(t){o("[Lume.js state] Error in effect:",t)}}}finally{d=!1}n>e||o("[Lume.js state] Maximum flush iterations reached (100). This usually indicates an infinite loop caused by an effect or computed mutating state it depends on.")})))),!0;var f}});return e.$beforeFlush=e=>{if("function"!=typeof e)throw Error("$beforeFlush requires a function");return-1===f.indexOf(e)&&f.push(e),()=>{const t=f.indexOf(e);-1!==t&&f.splice(t,1)}},e.$subscribe=(e,t)=>{if("function"!=typeof t)throw Error("Subscriber must be a function");const o=m(e,t,"New subscriber");return o===y||t(E[e]),o},E},e.stringAttr=z,e.watch=function(e,t,o,{immediate:n=!0}={}){if(!e.$subscribe)throw Error("store must be created with state()");if(!n){let n=!1;return e.$subscribe(t,e=>{n?o(e):n=!0})}return e.$subscribe(t,o)},e.withPlugins=function(e,t=[]){if(!t.length)return e;for(const i of t){try{i.onInit?.()}catch(s){o(`[Lume.js] Plugin "${i.name}" error in onInit:`,s)}Object.freeze(i)}const n=new Map;let r;return"function"==typeof e.$beforeFlush&&(r=e.$beforeFlush(function(){for(const[e,r]of n)for(const n of t)try{n.onNotify?.(e,r)}catch(s){o(`[Lume.js] Plugin "${n.name}" error in onNotify:`,s)}n.clear()})),new Proxy(e,{get(e,i){if("$dispose"===i)return()=>{r&&r(),n.clear()};if("string"==typeof i&&i.startsWith("$")){const n=e[i];return"$subscribe"===i&&"function"==typeof n?(e,r)=>{for(const n of t)try{n.onSubscribe?.(e)}catch(s){o(`[Lume.js] Plugin "${n.name}" error in onSubscribe:`,s)}return n(e,r)}:n}let c=e[i];for(const n of t)try{const e=n.onGet?.(i,c);void 0!==e&&(c=e)}catch(s){o(`[Lume.js] Plugin "${n.name}" error in onGet:`,s)}return c},set(e,r,i){const c=e[r];let a=i;for(const n of t)try{const e=n.onSet?.(r,a,c);void 0!==e&&(a=e)}catch(s){o(`[Lume.js] Plugin "${n.name}" error in onSet:`,s)}return Object.is(a,c)||n.set(r,a),e[r]=a,!0}})},e.withReadObserver=a,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),e}({});
|
|
2
2
|
//# sourceMappingURL=lume.global.js.map
|