@byre/vellum 1.0.0 → 1.1.0
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/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +250 -193
- package/dist/index.js.map +1 -1
- package/dist/spec.md +8 -8
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class f extends Error{constructor(t,{cause:e}={}){super(t),this.name="VellumError",this.cause=e}}class m extends f{constructor(t,e,{cause:
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class f extends Error{constructor(t,{cause:e}={}){super(t),this.name="VellumError",this.cause=e}}class m extends f{constructor(t,e,{cause:i}={}){const o=e.join(", ");super(`Mod "${t}" has unmet dependencies: ${o}`,{cause:i}),this.name="VellumDependencyError",this.modName=t,this.missingDependencies=e}}class E extends f{constructor(t,{cause:e}={}){super(`Circular dependency detected: ${t.join(" → ")}`,{cause:e}),this.name="VellumCircularDependencyError",this.cycle=t}}class v extends f{constructor(t,{cause:e}={}){super(`Failed to load mod from "${t}"`,{cause:e}),this.name="VellumLoadError",this.src=t}}class M extends f{constructor(t,{cause:e}={}){super(`Mount failed for mod "${t}"`,{cause:e}),this.name="VellumMountError",this.modName=t}}class g extends f{constructor(t,e,{cause:i}={}){const o=e==="both"?`Mod "${t}" has both src and ref attributes; exactly one is required`:`Mod "${t}" has neither src nor ref attribute; exactly one is required`;super(o,{cause:i}),this.name="VellumAttributeError",this.modName=t}}class C extends f{constructor(t,{cause:e}={}){super(`Could not resolve mod reference "${t}"`,{cause:e}),this.name="VellumReferenceError",this.ref=t}}const y=a=>a?a.split(",").map(t=>t.trim()).filter(Boolean):[],d=(a,t)=>{const e=["loading","loaded","failed","unloading"];for(const i of e)a.removeAttribute(i);t&&a.setAttribute(t,"")},p=a=>{const t=new Map(a.map(s=>[s.name,s])),e=new Map(a.map(s=>[s.name,0])),i=new Map,o=[],l=s=>{e.set(s,1);const r=t.get(s);for(const n of r.require){if(!t.has(n))continue;const u=e.get(n);if(u===1){const h=[n];let c=s;for(;c!==n;)h.unshift(c),c=i.get(c);h.push(n),o.push(h)}else u===0&&(i.set(n,s),l(n))}e.set(s,2)};for(const s of a)e.get(s.name)===0&&l(s.name);return o},w=(a,t)=>a.priority!==t.priority?a.priority-t.priority:a.index-t.index,A=(a,t=new Set)=>{const e=new Map(a.map(r=>[r.name,r]));[...a.map(r=>r.name),...t];const i=new Map;for(const r of a){let n=0;for(const u of r.require)e.has(u)&&!t.has(u)&&n++;for(const u of r.after)e.has(u)&&!t.has(u)&&n++;i.set(r.name,n)}const o=new Map(a.map(r=>[r.name,[]]));for(const r of a){for(const n of r.require)e.has(n)&&!t.has(n)&&o.get(n).push(r.name);for(const n of r.after)e.has(n)&&!t.has(n)&&o.get(n).push(r.name)}const l=a.filter(r=>i.get(r.name)===0).sort(w),s=[];for(;l.length>0;){const r=l.shift();s.push(r);for(const n of o.get(r.name)){const u=i.get(n)-1;i.set(n,u),u===0&&(l.push(e.get(n)),l.sort(w))}}return s},N=(a,t=new Set)=>{const e=p(a),i=new Set([...a.map(l=>l.name),...t]),o={};for(const l of a){const s=l.require.filter(r=>!i.has(r));s.length>0&&(o[l.name]=s)}if(e.length>0||Object.keys(o).length>0){const l=new Set(e.flat()),s=new Set(Object.keys(o)),r=a.filter(n=>!l.has(n.name)&&!s.has(n.name));return{sorted:A(r,t),cycles:e,missing:o}}return{sorted:A(a,t),cycles:[],missing:{}}},V=({name:a,src:t,require:e=[],after:i=[],priority:o=1/0,index:l})=>({name:a||t,require:e,after:i,priority:o,index:l});class D extends HTMLElement{#e=new WeakMap;#o=null;#s=new Set;#n=!1;#m="vellum-mod";#a=!1;#l=!1;get modTagName(){return this.getAttribute("mod-tag")||this.#m}connectedCallback(){this.#n=!0,this.#g(),setTimeout(()=>{this.#n&&this.#r()},0)}disconnectedCallback(){this.#n=!1,this.#o?.disconnect(),this.#o=null,this.dispatchEvent(new CustomEvent("vellum:teardown",{bubbles:!0,composed:!0,detail:{}})),this.#v()}#g(){this.#o=new MutationObserver(t=>{this.#p(t)}),this.#o.observe(this,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["src","disabled"]})}#p(t){const e=new Set,i=new Set,o=new Set,l=new Set;for(const s of t)if(s.type==="childList"){for(const r of s.addedNodes)this.#u(r)&&this.#d(r)&&e.add(r);for(const r of s.removedNodes)this.#u(r)&&i.add(r)}else if(s.type==="attributes"){const r=s.target;this.#u(r)&&this.#d(r)&&(s.attributeName==="src"?o.add(r):s.attributeName==="disabled"&&l.add(r))}for(const s of i)this.#i(s);for(const s of o)i.has(s)||this.#b(s);for(const s of l)!i.has(s)&&!o.has(s)&&this.#y(s);e.size>0&&this.#r()}#u(t){return t.nodeType===Node.ELEMENT_NODE&&t.tagName.toLowerCase()===this.modTagName.toLowerCase()}#d(t){let e=t.parentElement;for(;e;){if(e===this)return!0;if(e.tagName.toLowerCase()===this.tagName.toLowerCase())return!1;e=e.parentElement}return!1}async#b(t){const e=this.#e.get(t);e?.abortController&&e.abortController.abort(),await this.#i(t),!t.hasAttribute("disabled")&&t.getAttribute("src")&&this.#r()}async#y(t){t.hasAttribute("disabled")?await this.#i(t):this.#r()}async#r(){if(this.#a){this.#l=!0;return}this.#a=!0,this.#l=!1;try{await this.#w()}finally{this.#a=!1,this.#l&&this.#r()}}async#w(){const e=this.#h().filter(r=>r.hasAttribute("disabled")?!1:!(r.hasAttribute("loading")||r.hasAttribute("loaded")||r.hasAttribute("failed")||r.hasAttribute("unloading")));if(e.length===0)return;this.dispatchEvent(new CustomEvent("vellum:loading-started",{bubbles:!0,composed:!0,detail:{total:e.length}}));const i=e.map((r,n)=>V({name:r.getAttribute("name")||r.getAttribute("src"),src:r.getAttribute("src"),require:this.#f(r.getAttribute("require")),after:this.#f(r.getAttribute("after")),priority:this.#M(r.getAttribute("priority")),index:n})),o=p(i);if(o.length>0){const r=new Set;for(const n of o){const u=new E(n);for(const h of n){if(r.has(h))continue;r.add(h);const c=e.find(b=>(b.getAttribute("name")||b.getAttribute("src"))===h);c&&(d(c,"failed"),this.#t("vellum:mod-failed",c,{error:u}))}}this.#c(e);return}const{sorted:l,missing:s}=N(i,this.#s);for(const[r,n]of Object.entries(s)){const u=e.find(c=>(c.getAttribute("name")||c.getAttribute("src"))===r),h=new m(r,n);d(u,"failed"),this.#t("vellum:mod-failed",u,{error:h})}await this.#A(l,e),this.#c(e)}#c(t){let e=0,i=0;for(const o of t)o.hasAttribute("loaded")&&e++,o.hasAttribute("failed")&&i++;this.dispatchEvent(new CustomEvent("vellum:loading-complete",{bubbles:!0,composed:!0,detail:{loaded:e,failed:i}}))}async#A(t,e){const i=new Map(e.map(o=>[o.getAttribute("name")||o.getAttribute("src"),o]));for(const o of t){const l=i.get(o.name);if(!o.require.every(r=>this.#s.has(r))){const r=o.require.filter(u=>!this.#s.has(u)),n=new m(o.name,r);d(l,"failed"),this.#t("vellum:mod-failed",l,{error:n});continue}await this.#E(l)}}async#E(t){const e=t.getAttribute("src"),i=t.getAttribute("ref"),o=t.getAttribute("name")||e||i;if(e&&i){const s=new g(o,"both");d(t,"failed"),this.#t("vellum:mod-failed",t,{error:s});return}if(!e&&!i){const s=new g(o,"neither");d(t,"failed"),this.#t("vellum:mod-failed",t,{error:s});return}if(i){const s=new C(i);d(t,"failed"),this.#t("vellum:mod-failed",t,{error:s});return}const l=new AbortController;this.#e.set(t,{module:null,mount:null,unmount:null,abortController:l}),d(t,"loading");try{const s=await this._importModule(e);if(l.signal.aborted){d(t,null);return}const r=typeof s.mount=="function"?s.mount:null,n=typeof s.unmount=="function"?s.unmount:null,u=this.#e.get(t);if(u&&(u.module={mount:r,unmount:n},u.mount=r,u.unmount=n),r)try{if(await r({element:t,host:this,signal:l.signal}),l.signal.aborted){d(t,null);return}}catch(h){if(l.signal.aborted){d(t,null);return}const c=new M(o,{cause:h});d(t,"failed"),this.#t("vellum:mod-failed",t,{error:c});return}u&&(u.abortController=null),d(t,"loaded"),this.#s.add(o),this.#t("vellum:mod-loaded",t,{module:u.module}),this.#r()}catch(s){if(l.signal.aborted){d(t,null);return}const r=new v(e,{cause:s});d(t,"failed"),this.#t("vellum:mod-failed",t,{error:r})}}async#i(t){const e=this.#e.get(t),i=t.getAttribute("name")||t.getAttribute("src"),o=t.hasAttribute("loaded"),l=e?.module||null;if(e?.abortController){e.abortController.abort(),d(t,null),this.#e.delete(t);return}if(!t.hasAttribute("unloading")){if(!t.hasAttribute("loaded")&&!t.hasAttribute("failed")){d(t,null),this.#e.delete(t);return}if(o&&d(t,"unloading"),e?.unmount&&o)try{await e.unmount({element:t,host:this})}catch(s){console.warn(`Unmount error for mod "${i}":`,s)}d(t,null),this.#s.delete(i),this.#e.delete(t),this.#t("vellum:mod-unloaded",t,{module:l,wasLoaded:o})}}async#v(){const t=this.#h();for(const e of t)await this.#i(e)}#h(){const e=Array.from(this.querySelectorAll(this.modTagName)).filter(i=>this.#d(i));return e.length>0&&!customElements.get(this.modTagName.toLowerCase())?(console.warn(`<${this.modTagName}> elements found but not registered as custom elements. Register the mod element class before connecting the host.`),[]):e}#f(t){return t?t.split(",").map(e=>e.trim()).filter(Boolean):[]}#M(t){if(t===null)return 1/0;const e=parseInt(t,10);return Number.isNaN(e)?1/0:e}_importModule(t){return import(t)}#t(t,e,i={}){e.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,detail:{element:e,...i}}))}}class S extends HTMLElement{get src(){return this.getAttribute("src")||""}get name(){return this.getAttribute("name")||this.src}get dependencies(){return y(this.getAttribute("require"))}get softDependencies(){return y(this.getAttribute("after"))}get priority(){const t=this.getAttribute("priority");if(t===null)return 1/0;const e=parseInt(t,10);return Number.isNaN(e)?1/0:e}get disabled(){return this.hasAttribute("disabled")}get state(){return this.hasAttribute("loading")?"loading":this.hasAttribute("loaded")?"loaded":this.hasAttribute("failed")?"failed":this.hasAttribute("unloading")?"unloading":"initial"}}exports.VellumAttributeError=g;exports.VellumCircularDependencyError=E;exports.VellumDependencyError=m;exports.VellumError=f;exports.VellumHost=D;exports.VellumLoadError=v;exports.VellumMod=S;exports.VellumMountError=M;exports.VellumReferenceError=C;exports.createModDescriptor=V;exports.detectCycles=p;exports.resolveDependencies=N;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/errors.mjs","../src/utils/attributes.mjs","../src/utils/dependencies.mjs","../src/host.mjs","../src/mod.mjs"],"sourcesContent":["/**\n * Base class for all Vellum errors.\n */\nexport class VellumError extends Error {\n constructor(message, { cause } = {}) {\n super(message);\n this.name = 'VellumError';\n this.cause = cause;\n }\n}\n\n/**\n * Thrown when a mod element's hard dependencies cannot be satisfied.\n */\nexport class VellumDependencyError extends VellumError {\n constructor(modName, missingDependencies, { cause } = {}) {\n const deps = missingDependencies.join(', ');\n super(`Mod \"${modName}\" has unmet dependencies: ${deps}`, { cause });\n this.name = 'VellumDependencyError';\n this.modName = modName;\n this.missingDependencies = missingDependencies;\n }\n}\n\n/**\n * Thrown when circular hard dependencies are detected.\n */\nexport class VellumCircularDependencyError extends VellumError {\n constructor(cycle, { cause } = {}) {\n super(`Circular dependency detected: ${cycle.join(' → ')}`, { cause });\n this.name = 'VellumCircularDependencyError';\n this.cycle = cycle;\n }\n}\n\n/**\n * Thrown when a mod script fails to import.\n */\nexport class VellumLoadError extends VellumError {\n constructor(src, { cause } = {}) {\n super(`Failed to load mod from \"${src}\"`, { cause });\n this.name = 'VellumLoadError';\n this.src = src;\n }\n}\n\n/**\n * Thrown when a mod script's mount function fails.\n */\nexport class VellumMountError extends VellumError {\n constructor(modName, { cause } = {}) {\n super(`Mount failed for mod \"${modName}\"`, { cause });\n this.name = 'VellumMountError';\n this.modName = modName;\n }\n}\n","/**\n * Parse a comma-separated attribute value into an array of trimmed, non-empty strings.\n * @param {string|null} attr - The attribute value\n * @returns {string[]} Array of parsed values\n */\nexport const parseList = (attr) => {\n if (!attr) return [];\n return attr.split(',').map(s => s.trim()).filter(Boolean);\n};\n\n/**\n * Get the name of a mod element (name attribute or src as fallback).\n * @param {HTMLElement} element - The mod element\n * @returns {string} The mod name\n */\nexport const getName = (element) => {\n return element.getAttribute('name') || element.getAttribute('src') || '';\n};\n\n/**\n * Get the priority of a mod element (lower = higher priority).\n * @param {HTMLElement} element - The mod element\n * @returns {number} The priority (Infinity if not set)\n */\nexport const getPriority = (element) => {\n const attr = element.getAttribute('priority');\n if (attr === null) return Infinity;\n const num = parseInt(attr, 10);\n return Number.isNaN(num) ? Infinity : num;\n};\n\n/**\n * Get the src attribute of a mod element.\n * @param {HTMLElement} element - The mod element\n * @returns {string} The src URL\n */\nexport const getSrc = (element) => {\n return element.getAttribute('src') || '';\n};\n\n/**\n * Get hard dependencies (require attribute) of a mod element.\n * @param {HTMLElement} element - The mod element\n * @returns {string[]} Array of dependency names\n */\nexport const getRequire = (element) => {\n return parseList(element.getAttribute('require'));\n};\n\n/**\n * Get soft dependencies (after attribute) of a mod element.\n * @param {HTMLElement} element - The mod element\n * @returns {string[]} Array of soft dependency names\n */\nexport const getAfter = (element) => {\n return parseList(element.getAttribute('after'));\n};\n\n/**\n * Check if a mod element is disabled.\n * @param {HTMLElement} element - The mod element\n * @returns {boolean} True if disabled\n */\nexport const isDisabled = (element) => {\n return element.hasAttribute('disabled');\n};\n\n/**\n * Check if a mod element has a specific state attribute.\n * @param {HTMLElement} element - The mod element\n * @param {'loading'|'loaded'|'failed'|'unloading'} state - The state to check\n * @returns {boolean} True if in that state\n */\nexport const hasState = (element, state) => {\n return element.hasAttribute(state);\n};\n\n/**\n * Set a state attribute on a mod element (removes other state attributes first).\n * @param {HTMLElement} element - The mod element\n * @param {'loading'|'loaded'|'failed'|'unloading'|null} state - The state to set, or null to clear\n */\nexport const setState = (element, state) => {\n const states = ['loading', 'loaded', 'failed', 'unloading'];\n for (const s of states) {\n element.removeAttribute(s);\n }\n if (state) {\n element.setAttribute(state, '');\n }\n};\n\n/**\n * Get the current state of a mod element.\n * @param {HTMLElement} element - The mod element\n * @returns {'loading'|'loaded'|'failed'|'unloading'|'initial'} The current state\n */\nexport const getState = (element) => {\n if (element.hasAttribute('loading')) return 'loading';\n if (element.hasAttribute('loaded')) return 'loaded';\n if (element.hasAttribute('failed')) return 'failed';\n if (element.hasAttribute('unloading')) return 'unloading';\n return 'initial';\n};\n","/**\n * @typedef {Object} ModDescriptor\n * @property {string} name - Unique identifier\n * @property {string[]} require - Hard dependencies (must exist and load first)\n * @property {string[]} after - Soft dependencies (load after if present)\n * @property {number} priority - Lower loads first (Infinity if unset)\n * @property {number} index - DOM order index\n */\n\n/**\n * @typedef {Object} ResolutionResult\n * @property {ModDescriptor[]} sorted - Mods in load order\n * @property {string[][]} cycles - Array of detected cycles (empty if none)\n * @property {Object.<string, string[]>} missing - Map of mod name to missing hard dependencies\n */\n\n/**\n * Detect circular dependencies in hard requirements.\n * Uses DFS with coloring: 0=unvisited, 1=visiting, 2=visited\n * \n * @param {ModDescriptor[]} mods - List of mod descriptors\n * @returns {string[][]} Array of cycles found (each cycle is array of names)\n */\nexport const detectCycles = (mods) => {\n const modMap = new Map(mods.map(m => [m.name, m]));\n const color = new Map(mods.map(m => [m.name, 0]));\n const parent = new Map();\n const cycles = [];\n\n const dfs = (name) => {\n color.set(name, 1); // visiting\n const mod = modMap.get(name);\n if (!mod) return;\n\n for (const dep of mod.require) {\n if (!modMap.has(dep)) continue; // missing dep handled elsewhere\n \n const depColor = color.get(dep);\n if (depColor === 1) {\n // Found cycle - reconstruct it\n const cycle = [dep];\n let curr = name;\n while (curr !== dep) {\n cycle.unshift(curr);\n curr = parent.get(curr);\n }\n cycle.push(dep); // complete the cycle\n cycles.push(cycle);\n } else if (depColor === 0) {\n parent.set(dep, name);\n dfs(dep);\n }\n }\n color.set(name, 2); // visited\n };\n\n for (const mod of mods) {\n if (color.get(mod.name) === 0) {\n dfs(mod.name);\n }\n }\n\n return cycles;\n};\n\n/**\n * Find missing hard dependencies for each mod.\n * \n * @param {ModDescriptor[]} mods - List of mod descriptors\n * @returns {Object.<string, string[]>} Map of mod name to missing dependencies\n */\nexport const findMissingDependencies = (mods) => {\n const available = new Set(mods.map(m => m.name));\n const missing = {};\n\n for (const mod of mods) {\n const missingDeps = mod.require.filter(dep => !available.has(dep));\n if (missingDeps.length > 0) {\n missing[mod.name] = missingDeps;\n }\n }\n\n return missing;\n};\n\n/**\n * Compare two mods for sort order (priority, then DOM index).\n * \n * @param {ModDescriptor} a \n * @param {ModDescriptor} b \n * @returns {number}\n */\nexport const compareMods = (a, b) => {\n if (a.priority !== b.priority) {\n return a.priority - b.priority;\n }\n return a.index - b.index;\n};\n\n/**\n * Topological sort using Kahn's algorithm.\n * Respects priority and DOM order as tiebreakers.\n * \n * @param {ModDescriptor[]} mods - Mods to sort (assumed no cycles)\n * @param {Set<string>} [loaded] - Already loaded mod names (for dynamic additions)\n * @returns {ModDescriptor[]} Sorted mods\n */\nexport const topoSort = (mods, loaded = new Set()) => {\n const modMap = new Map(mods.map(m => [m.name, m]));\n const available = new Set([...mods.map(m => m.name), ...loaded]);\n \n // Calculate in-degree (number of unsatisfied hard deps)\n const inDegree = new Map();\n for (const mod of mods) {\n let degree = 0;\n for (const dep of mod.require) {\n // Only count deps that exist and aren't already loaded\n if (modMap.has(dep) && !loaded.has(dep)) {\n degree++;\n }\n }\n // Also count soft deps that exist and aren't loaded\n for (const dep of mod.after) {\n if (modMap.has(dep) && !loaded.has(dep)) {\n degree++;\n }\n }\n inDegree.set(mod.name, degree);\n }\n\n // Build reverse adjacency (who depends on me)\n const dependents = new Map(mods.map(m => [m.name, []]));\n for (const mod of mods) {\n for (const dep of mod.require) {\n if (modMap.has(dep) && !loaded.has(dep)) {\n dependents.get(dep).push(mod.name);\n }\n }\n for (const dep of mod.after) {\n if (modMap.has(dep) && !loaded.has(dep)) {\n dependents.get(dep).push(mod.name);\n }\n }\n }\n\n // Start with mods that have no dependencies\n const ready = mods\n .filter(m => inDegree.get(m.name) === 0)\n .sort(compareMods);\n \n const sorted = [];\n \n while (ready.length > 0) {\n // Take highest priority (first after sort)\n const mod = ready.shift();\n sorted.push(mod);\n\n // Decrement in-degree of dependents\n for (const depName of dependents.get(mod.name)) {\n const newDegree = inDegree.get(depName) - 1;\n inDegree.set(depName, newDegree);\n if (newDegree === 0) {\n ready.push(modMap.get(depName));\n ready.sort(compareMods);\n }\n }\n }\n\n return sorted;\n};\n\n/**\n * Get mods that are ready to load given currently loaded mods.\n * A mod is ready if all its hard dependencies are loaded.\n * \n * @param {ModDescriptor[]} pending - Mods waiting to load\n * @param {Set<string>} loaded - Names of loaded mods\n * @returns {ModDescriptor[]} Mods ready to load (sorted by priority/index)\n */\nexport const getReadyMods = (pending, loaded) => {\n return pending\n .filter(mod => mod.require.every(dep => loaded.has(dep)))\n .sort(compareMods);\n};\n\n/**\n * Full dependency resolution.\n * \n * @param {ModDescriptor[]} mods - All mod descriptors\n * @param {Set<string>} [alreadyLoaded] - Already loaded mod names\n * @returns {ResolutionResult} Resolution result\n */\nexport const resolveDependencies = (mods, alreadyLoaded = new Set()) => {\n // Find cycles in hard dependencies\n const cycles = detectCycles(mods);\n \n // Find missing hard dependencies\n const allAvailable = new Set([...mods.map(m => m.name), ...alreadyLoaded]);\n const missing = {};\n for (const mod of mods) {\n const missingDeps = mod.require.filter(dep => !allAvailable.has(dep));\n if (missingDeps.length > 0) {\n missing[mod.name] = missingDeps;\n }\n }\n\n // If there are cycles or missing deps, we can't sort properly\n // Return what we can\n if (cycles.length > 0 || Object.keys(missing).length > 0) {\n // Filter out mods that are in cycles or have missing deps\n const cycleNames = new Set(cycles.flat());\n const missingNames = new Set(Object.keys(missing));\n const validMods = mods.filter(m => \n !cycleNames.has(m.name) && !missingNames.has(m.name)\n );\n \n return {\n sorted: topoSort(validMods, alreadyLoaded),\n cycles,\n missing,\n };\n }\n\n return {\n sorted: topoSort(mods, alreadyLoaded),\n cycles: [],\n missing: {},\n };\n};\n\n/**\n * Create a ModDescriptor from basic properties.\n * Utility for converting from DOM elements or test data.\n * \n * @param {Object} props\n * @param {string} props.name\n * @param {string} [props.src]\n * @param {string[]} [props.require]\n * @param {string[]} [props.after]\n * @param {number} [props.priority]\n * @param {number} props.index\n * @returns {ModDescriptor}\n */\nexport const createModDescriptor = ({ \n name, \n src, \n require = [], \n after = [], \n priority = Infinity, \n index \n}) => ({\n name: name || src,\n require,\n after,\n priority,\n index,\n});\n","import {\n VellumLoadError,\n VellumMountError,\n VellumDependencyError,\n VellumCircularDependencyError,\n} from './errors.mjs';\nimport { setState } from './utils/attributes.mjs';\nimport {\n createModDescriptor,\n resolveDependencies,\n detectCycles,\n} from './utils/dependencies.mjs';\n\n/**\n * @typedef {Object} ModState\n * @property {Object|null} module - The imported module\n * @property {Function|null} mount - The mount function\n * @property {Function|null} unmount - The unmount function\n * @property {AbortController|null} abortController - For cancelling in-progress loads\n */\n\n/**\n * VellumHost is the container element that discovers and manages mod elements.\n * It handles loading, mounting, unmounting, and observes DOM mutations.\n */\nexport class VellumHost extends HTMLElement {\n /** @type {WeakMap<HTMLElement, ModState>} */\n #modState = new WeakMap();\n\n /** @type {MutationObserver|null} */\n #observer = null;\n\n /** @type {Set<string>} */\n #loadedNames = new Set();\n\n /** @type {boolean} */\n #isConnected = false;\n\n /** @type {string} */\n #modTagName = 'vellum-mod';\n\n /** @type {boolean} */\n #isDiscovering = false;\n\n /** @type {boolean} */\n #needsRediscovery = false;\n\n /**\n * Get the tag name used for mod elements.\n * @returns {string}\n */\n get modTagName() {\n return this.getAttribute('mod-tag') || this.#modTagName;\n }\n\n connectedCallback() {\n this.#isConnected = true;\n this.#setupObserver();\n this.#discoverAndLoad();\n }\n\n disconnectedCallback() {\n this.#isConnected = false;\n this.#observer?.disconnect();\n this.#observer = null;\n // Unload all mods\n this.#unloadAllMods();\n }\n\n /**\n * Set up the MutationObserver to watch for mod element changes.\n */\n #setupObserver() {\n this.#observer = new MutationObserver((mutations) => {\n this.#handleMutations(mutations);\n });\n\n this.#observer.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['src', 'disabled'],\n });\n }\n\n /**\n * Handle batched mutations.\n * @param {MutationRecord[]} mutations\n */\n #handleMutations(mutations) {\n const added = new Set();\n const removed = new Set();\n const srcChanged = new Set();\n const disabledChanged = new Set();\n\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n for (const node of mutation.addedNodes) {\n if (this.#isModElement(node) && this.#isDirectMod(node)) {\n added.add(node);\n }\n }\n for (const node of mutation.removedNodes) {\n if (this.#isModElement(node)) {\n removed.add(node);\n }\n }\n } else if (mutation.type === 'attributes') {\n const target = mutation.target;\n if (this.#isModElement(target) && this.#isDirectMod(target)) {\n if (mutation.attributeName === 'src') {\n srcChanged.add(target);\n } else if (mutation.attributeName === 'disabled') {\n disabledChanged.add(target);\n }\n }\n }\n }\n\n // Process removals first\n for (const element of removed) {\n this.#unloadMod(element);\n }\n\n // Process src changes (abort/unload then reload)\n for (const element of srcChanged) {\n if (!removed.has(element)) {\n this.#handleSrcChange(element);\n }\n }\n\n // Process disabled changes\n for (const element of disabledChanged) {\n if (!removed.has(element) && !srcChanged.has(element)) {\n this.#handleDisabledChange(element);\n }\n }\n\n // Process additions (will trigger dependency resolution)\n if (added.size > 0) {\n this.#discoverAndLoad();\n }\n }\n\n /**\n * Check if a node is a mod element.\n * @param {Node} node\n * @returns {boolean}\n */\n #isModElement(node) {\n return (\n node.nodeType === Node.ELEMENT_NODE &&\n node.tagName.toLowerCase() === this.modTagName.toLowerCase()\n );\n }\n\n /**\n * Check if a mod element belongs to this host (not a nested host).\n * @param {HTMLElement} element\n * @returns {boolean}\n */\n #isDirectMod(element) {\n // Walk up to find the closest host\n let parent = element.parentElement;\n while (parent) {\n if (parent === this) return true;\n if (parent.tagName.toLowerCase() === this.tagName.toLowerCase()) {\n return false; // Belongs to a nested host\n }\n parent = parent.parentElement;\n }\n return false;\n }\n\n /**\n * Handle src attribute change on a mod element.\n * @param {HTMLElement} element\n */\n async #handleSrcChange(element) {\n const state = this.#modState.get(element);\n \n // Abort if loading\n if (state?.abortController) {\n state.abortController.abort();\n }\n \n // Unload if loaded\n await this.#unloadMod(element);\n \n // Reload with new src\n if (!element.hasAttribute('disabled') && element.getAttribute('src')) {\n this.#discoverAndLoad();\n }\n }\n\n /**\n * Handle disabled attribute change on a mod element.\n * @param {HTMLElement} element\n */\n async #handleDisabledChange(element) {\n if (element.hasAttribute('disabled')) {\n // Disabled added - unload\n await this.#unloadMod(element);\n } else {\n // Disabled removed - try to load\n this.#discoverAndLoad();\n }\n }\n\n /**\n * Discover all mod elements and initiate loading.\n */\n async #discoverAndLoad() {\n // Prevent concurrent discovery\n if (this.#isDiscovering) {\n this.#needsRediscovery = true;\n return;\n }\n\n this.#isDiscovering = true;\n this.#needsRediscovery = false;\n\n try {\n await this.#doDiscoverAndLoad();\n } finally {\n this.#isDiscovering = false;\n \n // If something requested rediscovery while we were busy, do it now\n if (this.#needsRediscovery) {\n this.#discoverAndLoad();\n }\n }\n }\n\n /**\n * Internal implementation of discover and load.\n */\n async #doDiscoverAndLoad() {\n const mods = this.#getModElements();\n \n // Filter to only pending mods (not disabled, not already loading/loaded/failed)\n const pending = mods.filter((el) => {\n if (el.hasAttribute('disabled')) return false;\n const hasState = el.hasAttribute('loading') ||\n el.hasAttribute('loaded') ||\n el.hasAttribute('failed') ||\n el.hasAttribute('unloading');\n return !hasState;\n });\n\n if (pending.length === 0) return;\n\n // Build descriptors\n const descriptors = pending.map((el, index) => \n createModDescriptor({\n name: el.getAttribute('name') || el.getAttribute('src'),\n src: el.getAttribute('src'),\n require: this.#parseList(el.getAttribute('require')),\n after: this.#parseList(el.getAttribute('after')),\n priority: this.#parsePriority(el.getAttribute('priority')),\n index,\n })\n );\n\n // Check for cycles\n const cycles = detectCycles(descriptors);\n if (cycles.length > 0) {\n const error = new VellumCircularDependencyError(cycles[0]);\n // Mark all mods in cycles as failed\n for (const cycle of cycles) {\n for (const name of cycle) {\n const el = pending.find(\n (e) => (e.getAttribute('name') || e.getAttribute('src')) === name\n );\n if (el && el !== pending.find(\n (e) => (e.getAttribute('name') || e.getAttribute('src')) === cycle[0]\n )) {\n continue;\n }\n if (el) {\n setState(el, 'failed');\n this.#dispatchModEvent('vellum:mod-error', el, { error });\n }\n }\n }\n // Dispatch error on host\n this.dispatchEvent(new CustomEvent('vellum:error', {\n bubbles: true,\n detail: { error },\n }));\n return;\n }\n\n // Resolve dependencies\n const { sorted, missing } = resolveDependencies(descriptors, this.#loadedNames);\n\n // Handle missing dependencies\n for (const [modName, missingDeps] of Object.entries(missing)) {\n const el = pending.find(\n (e) => (e.getAttribute('name') || e.getAttribute('src')) === modName\n );\n if (el) {\n const error = new VellumDependencyError(modName, missingDeps);\n setState(el, 'failed');\n this.#dispatchModEvent('vellum:mod-error', el, { error });\n }\n }\n\n // Load sorted mods in parallel batches based on dependency levels\n await this.#loadModsInOrder(sorted, pending);\n }\n\n /**\n * Load mods respecting dependency order but parallelizing where possible.\n * @param {Object[]} sorted - Sorted mod descriptors\n * @param {HTMLElement[]} elements - Pending mod elements\n */\n async #loadModsInOrder(sorted, elements) {\n const elementMap = new Map(\n elements.map((el) => [el.getAttribute('name') || el.getAttribute('src'), el])\n );\n\n for (const descriptor of sorted) {\n const element = elementMap.get(descriptor.name);\n if (!element) continue;\n\n // Check if all hard deps are loaded\n const allDepsLoaded = descriptor.require.every(\n (dep) => this.#loadedNames.has(dep)\n );\n\n if (!allDepsLoaded) {\n // This shouldn't happen if resolveDependencies worked correctly\n const missing = descriptor.require.filter((d) => !this.#loadedNames.has(d));\n const error = new VellumDependencyError(descriptor.name, missing);\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-error', element, { error });\n continue;\n }\n\n await this.#loadMod(element);\n }\n }\n\n /**\n * Load a single mod element.\n * @param {HTMLElement} element\n */\n async #loadMod(element) {\n const src = element.getAttribute('src');\n const name = element.getAttribute('name') || src;\n\n if (!src) {\n const error = new VellumLoadError('');\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-error', element, { error });\n return;\n }\n\n // Set up state\n const abortController = new AbortController();\n this.#modState.set(element, {\n module: null,\n mount: null,\n unmount: null,\n abortController,\n });\n\n setState(element, 'loading');\n this.#dispatchModEvent('vellum:mod-loading', element);\n\n try {\n // Import the module\n const module = await import(/* @vite-ignore */ src);\n\n // Check if aborted\n if (abortController.signal.aborted) {\n setState(element, null);\n return;\n }\n\n const mount = typeof module.mount === 'function' ? module.mount : null;\n const unmount = typeof module.unmount === 'function' ? module.unmount : null;\n\n // Update state\n const state = this.#modState.get(element);\n if (state) {\n state.module = module;\n state.mount = mount;\n state.unmount = unmount;\n state.abortController = null;\n }\n\n // Call mount if available\n if (mount) {\n try {\n await mount({\n element,\n host: this,\n signal: abortController.signal,\n });\n\n // Check if aborted during mount\n if (abortController.signal.aborted) {\n setState(element, null);\n return;\n }\n } catch (mountError) {\n const error = new VellumMountError(name, { cause: mountError });\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-error', element, { error });\n return;\n }\n }\n\n // Success\n setState(element, 'loaded');\n this.#loadedNames.add(name);\n this.#dispatchModEvent('vellum:mod-loaded', element);\n\n // Check if any pending mods can now load\n this.#discoverAndLoad();\n\n } catch (importError) {\n if (abortController.signal.aborted) {\n setState(element, null);\n return;\n }\n\n const error = new VellumLoadError(src, { cause: importError });\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-error', element, { error });\n }\n }\n\n /**\n * Unload a single mod element.\n * @param {HTMLElement} element\n */\n async #unloadMod(element) {\n const state = this.#modState.get(element);\n const name = element.getAttribute('name') || element.getAttribute('src');\n const wasLoaded = element.hasAttribute('loaded');\n\n // Abort if loading\n if (state?.abortController) {\n state.abortController.abort();\n setState(element, null);\n this.#modState.delete(element);\n return;\n }\n\n // If unloading already, skip\n if (element.hasAttribute('unloading')) {\n return;\n }\n\n // If not loaded or failed, just clean up\n if (!element.hasAttribute('loaded') && !element.hasAttribute('failed')) {\n setState(element, null);\n this.#modState.delete(element);\n return;\n }\n\n // Set unloading state\n if (wasLoaded) {\n setState(element, 'unloading');\n }\n\n // Call unmount if available\n if (state?.unmount && wasLoaded) {\n try {\n await state.unmount({\n element,\n host: this,\n });\n } catch (err) {\n // Log but don't fail\n console.warn(`Unmount error for mod \"${name}\":`, err);\n }\n }\n\n // Clear state\n setState(element, null);\n this.#loadedNames.delete(name);\n this.#modState.delete(element);\n\n // Dispatch event\n this.#dispatchModEvent('vellum:mod-unloaded', element, {\n wasLoaded,\n });\n }\n\n /**\n * Unload all mod elements.\n */\n async #unloadAllMods() {\n const mods = this.#getModElements();\n for (const element of mods) {\n await this.#unloadMod(element);\n }\n }\n\n /**\n * Get all mod elements that belong to this host.\n * @returns {HTMLElement[]}\n */\n #getModElements() {\n const all = Array.from(this.querySelectorAll(this.modTagName));\n return all.filter((el) => this.#isDirectMod(el));\n }\n\n /**\n * Parse a comma-separated list attribute.\n * @param {string|null} attr\n * @returns {string[]}\n */\n #parseList(attr) {\n if (!attr) return [];\n return attr.split(',').map((s) => s.trim()).filter(Boolean);\n }\n\n /**\n * Parse a priority attribute.\n * @param {string|null} attr\n * @returns {number}\n */\n #parsePriority(attr) {\n if (attr === null) return Infinity;\n const num = parseInt(attr, 10);\n return Number.isNaN(num) ? Infinity : num;\n }\n\n /**\n * Dispatch a mod-related event.\n * @param {string} type\n * @param {HTMLElement} element\n * @param {Object} [extra]\n */\n #dispatchModEvent(type, element, extra = {}) {\n element.dispatchEvent(\n new CustomEvent(type, {\n bubbles: true,\n detail: {\n element,\n name: element.getAttribute('name') || element.getAttribute('src'),\n ...extra,\n },\n })\n );\n }\n}\n","import { parseList } from './utils/attributes.mjs';\n\n/**\n * VellumMod is an inert custom element representing a loadable module.\n * It carries configuration but does not perform loading itself.\n * Loading is the sole responsibility of the host element.\n */\nexport class VellumMod extends HTMLElement {\n /**\n * Get the module source URL.\n * @returns {string}\n */\n get src() {\n return this.getAttribute('src') || '';\n }\n\n /**\n * Get the mod name (for dependency resolution).\n * Falls back to src if name not specified.\n * @returns {string}\n */\n get name() {\n return this.getAttribute('name') || this.src;\n }\n\n /**\n * Get hard dependencies (require attribute).\n * @returns {string[]}\n */\n get dependencies() {\n return parseList(this.getAttribute('require'));\n }\n\n /**\n * Get soft dependencies (after attribute).\n * @returns {string[]}\n */\n get softDependencies() {\n return parseList(this.getAttribute('after'));\n }\n\n /**\n * Get the priority (lower = higher priority).\n * @returns {number}\n */\n get priority() {\n const attr = this.getAttribute('priority');\n if (attr === null) return Infinity;\n const num = parseInt(attr, 10);\n return Number.isNaN(num) ? Infinity : num;\n }\n\n /**\n * Check if the mod is disabled.\n * @returns {boolean}\n */\n get disabled() {\n return this.hasAttribute('disabled');\n }\n\n /**\n * Get the current state.\n * @returns {'initial'|'loading'|'loaded'|'failed'|'unloading'}\n */\n get state() {\n if (this.hasAttribute('loading')) return 'loading';\n if (this.hasAttribute('loaded')) return 'loaded';\n if (this.hasAttribute('failed')) return 'failed';\n if (this.hasAttribute('unloading')) return 'unloading';\n return 'initial';\n }\n}\n"],"names":["VellumError","message","cause","VellumDependencyError","modName","missingDependencies","deps","VellumCircularDependencyError","cycle","VellumLoadError","src","VellumMountError","parseList","attr","s","setState","element","state","states","detectCycles","mods","modMap","m","color","parent","cycles","dfs","name","mod","dep","depColor","curr","compareMods","b","topoSort","loaded","inDegree","degree","dependents","ready","sorted","depName","newDegree","resolveDependencies","alreadyLoaded","allAvailable","missing","missingDeps","cycleNames","missingNames","validMods","createModDescriptor","require","after","priority","index","VellumHost","#modState","#observer","#loadedNames","#isConnected","#modTagName","#isDiscovering","#needsRediscovery","#setupObserver","#discoverAndLoad","#unloadAllMods","mutations","#handleMutations","added","removed","srcChanged","disabledChanged","mutation","node","#isModElement","#isDirectMod","target","#unloadMod","#handleSrcChange","#handleDisabledChange","#doDiscoverAndLoad","pending","#getModElements","el","descriptors","#parseList","#parsePriority","error","e","#dispatchModEvent","#loadModsInOrder","elements","elementMap","descriptor","d","#loadMod","abortController","module","mount","unmount","mountError","importError","wasLoaded","err","num","type","extra","VellumMod"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/errors.mjs","../src/utils/attributes.mjs","../src/utils/dependencies.mjs","../src/host.mjs","../src/mod.mjs"],"sourcesContent":["/**\n * Base class for all Vellum errors.\n */\nexport class VellumError extends Error {\n constructor(message, { cause } = {}) {\n super(message);\n this.name = 'VellumError';\n this.cause = cause;\n }\n}\n\n/**\n * Thrown when a mod element's hard dependencies cannot be satisfied.\n */\nexport class VellumDependencyError extends VellumError {\n constructor(modName, missingDependencies, { cause } = {}) {\n const deps = missingDependencies.join(', ');\n super(`Mod \"${modName}\" has unmet dependencies: ${deps}`, { cause });\n this.name = 'VellumDependencyError';\n this.modName = modName;\n this.missingDependencies = missingDependencies;\n }\n}\n\n/**\n * Thrown when circular hard dependencies are detected.\n */\nexport class VellumCircularDependencyError extends VellumError {\n constructor(cycle, { cause } = {}) {\n super(`Circular dependency detected: ${cycle.join(' → ')}`, { cause });\n this.name = 'VellumCircularDependencyError';\n this.cycle = cycle;\n }\n}\n\n/**\n * Thrown when a mod script fails to import.\n */\nexport class VellumLoadError extends VellumError {\n constructor(src, { cause } = {}) {\n super(`Failed to load mod from \"${src}\"`, { cause });\n this.name = 'VellumLoadError';\n this.src = src;\n }\n}\n\n/**\n * Thrown when a mod script's mount function fails.\n */\nexport class VellumMountError extends VellumError {\n constructor(modName, { cause } = {}) {\n super(`Mount failed for mod \"${modName}\"`, { cause });\n this.name = 'VellumMountError';\n this.modName = modName;\n }\n}\n\n/**\n * Thrown when a mod element has invalid attribute configuration.\n */\nexport class VellumAttributeError extends VellumError {\n constructor(modName, variant, { cause } = {}) {\n const message = variant === 'both'\n ? `Mod \"${modName}\" has both src and ref attributes; exactly one is required`\n : `Mod \"${modName}\" has neither src nor ref attribute; exactly one is required`;\n super(message, { cause });\n this.name = 'VellumAttributeError';\n this.modName = modName;\n }\n}\n\n/**\n * Thrown when a mod element's ref attribute cannot be resolved.\n */\nexport class VellumReferenceError extends VellumError {\n constructor(ref, { cause } = {}) {\n super(`Could not resolve mod reference \"${ref}\"`, { cause });\n this.name = 'VellumReferenceError';\n this.ref = ref;\n }\n}\n","/**\n * Parse a comma-separated attribute value into an array of trimmed, non-empty strings.\n * @param {string|null} attr - The attribute value\n * @returns {string[]} Array of parsed values\n */\nexport const parseList = (attr) => {\n if (!attr) return [];\n return attr.split(',').map(s => s.trim()).filter(Boolean);\n};\n\n/**\n * Get the name of a mod element (name attribute or src as fallback).\n * @param {HTMLElement} element - The mod element\n * @returns {string} The mod name\n */\nexport const getName = (element) => {\n return element.getAttribute('name') || element.getAttribute('src') || '';\n};\n\n/**\n * Get the priority of a mod element (lower = higher priority).\n * @param {HTMLElement} element - The mod element\n * @returns {number} The priority (Infinity if not set)\n */\nexport const getPriority = (element) => {\n const attr = element.getAttribute('priority');\n if (attr === null) return Infinity;\n const num = parseInt(attr, 10);\n return Number.isNaN(num) ? Infinity : num;\n};\n\n/**\n * Get the src attribute of a mod element.\n * @param {HTMLElement} element - The mod element\n * @returns {string} The src URL\n */\nexport const getSrc = (element) => {\n return element.getAttribute('src') || '';\n};\n\n/**\n * Get hard dependencies (require attribute) of a mod element.\n * @param {HTMLElement} element - The mod element\n * @returns {string[]} Array of dependency names\n */\nexport const getRequire = (element) => {\n return parseList(element.getAttribute('require'));\n};\n\n/**\n * Get soft dependencies (after attribute) of a mod element.\n * @param {HTMLElement} element - The mod element\n * @returns {string[]} Array of soft dependency names\n */\nexport const getAfter = (element) => {\n return parseList(element.getAttribute('after'));\n};\n\n/**\n * Check if a mod element is disabled.\n * @param {HTMLElement} element - The mod element\n * @returns {boolean} True if disabled\n */\nexport const isDisabled = (element) => {\n return element.hasAttribute('disabled');\n};\n\n/**\n * Check if a mod element has a specific state attribute.\n * @param {HTMLElement} element - The mod element\n * @param {'loading'|'loaded'|'failed'|'unloading'} state - The state to check\n * @returns {boolean} True if in that state\n */\nexport const hasState = (element, state) => {\n return element.hasAttribute(state);\n};\n\n/**\n * Set a state attribute on a mod element (removes other state attributes first).\n * @param {HTMLElement} element - The mod element\n * @param {'loading'|'loaded'|'failed'|'unloading'|null} state - The state to set, or null to clear\n */\nexport const setState = (element, state) => {\n const states = ['loading', 'loaded', 'failed', 'unloading'];\n for (const s of states) {\n element.removeAttribute(s);\n }\n if (state) {\n element.setAttribute(state, '');\n }\n};\n\n/**\n * Get the current state of a mod element.\n * @param {HTMLElement} element - The mod element\n * @returns {'loading'|'loaded'|'failed'|'unloading'|'initial'} The current state\n */\nexport const getState = (element) => {\n if (element.hasAttribute('loading')) return 'loading';\n if (element.hasAttribute('loaded')) return 'loaded';\n if (element.hasAttribute('failed')) return 'failed';\n if (element.hasAttribute('unloading')) return 'unloading';\n return 'initial';\n};\n","/**\n * @typedef {Object} ModDescriptor\n * @property {string} name - Unique identifier\n * @property {string[]} require - Hard dependencies (must exist and load first)\n * @property {string[]} after - Soft dependencies (load after if present)\n * @property {number} priority - Lower loads first (Infinity if unset)\n * @property {number} index - DOM order index\n */\n\n/**\n * @typedef {Object} ResolutionResult\n * @property {ModDescriptor[]} sorted - Mods in load order\n * @property {string[][]} cycles - Array of detected cycles (empty if none)\n * @property {Object.<string, string[]>} missing - Map of mod name to missing hard dependencies\n */\n\n/**\n * Detect circular dependencies in hard requirements.\n * Uses DFS with coloring: 0=unvisited, 1=visiting, 2=visited\n * \n * @param {ModDescriptor[]} mods - List of mod descriptors\n * @returns {string[][]} Array of cycles found (each cycle is array of names)\n */\nexport const detectCycles = (mods) => {\n const modMap = new Map(mods.map(m => [m.name, m]));\n const color = new Map(mods.map(m => [m.name, 0]));\n const parent = new Map();\n const cycles = [];\n\n const dfs = (name) => {\n color.set(name, 1); // visiting\n const mod = modMap.get(name);\n\n for (const dep of mod.require) {\n if (!modMap.has(dep)) continue; // missing dep handled elsewhere\n \n const depColor = color.get(dep);\n if (depColor === 1) {\n // Found cycle - reconstruct it\n const cycle = [dep];\n let curr = name;\n while (curr !== dep) {\n cycle.unshift(curr);\n curr = parent.get(curr);\n }\n cycle.push(dep); // complete the cycle\n cycles.push(cycle);\n } else if (depColor === 0) {\n parent.set(dep, name);\n dfs(dep);\n }\n }\n color.set(name, 2); // visited\n };\n\n for (const mod of mods) {\n if (color.get(mod.name) === 0) {\n dfs(mod.name);\n }\n }\n\n return cycles;\n};\n\n/**\n * Find missing hard dependencies for each mod.\n * \n * @param {ModDescriptor[]} mods - List of mod descriptors\n * @returns {Object.<string, string[]>} Map of mod name to missing dependencies\n */\nexport const findMissingDependencies = (mods) => {\n const available = new Set(mods.map(m => m.name));\n const missing = {};\n\n for (const mod of mods) {\n const missingDeps = mod.require.filter(dep => !available.has(dep));\n if (missingDeps.length > 0) {\n missing[mod.name] = missingDeps;\n }\n }\n\n return missing;\n};\n\n/**\n * Compare two mods for sort order (priority, then DOM index).\n * \n * @param {ModDescriptor} a \n * @param {ModDescriptor} b \n * @returns {number}\n */\nexport const compareMods = (a, b) => {\n if (a.priority !== b.priority) {\n return a.priority - b.priority;\n }\n return a.index - b.index;\n};\n\n/**\n * Topological sort using Kahn's algorithm.\n * Respects priority and DOM order as tiebreakers.\n * \n * @param {ModDescriptor[]} mods - Mods to sort (assumed no cycles)\n * @param {Set<string>} [loaded] - Already loaded mod names (for dynamic additions)\n * @returns {ModDescriptor[]} Sorted mods\n */\nexport const topoSort = (mods, loaded = new Set()) => {\n const modMap = new Map(mods.map(m => [m.name, m]));\n const available = new Set([...mods.map(m => m.name), ...loaded]);\n \n // Calculate in-degree (number of unsatisfied hard deps)\n const inDegree = new Map();\n for (const mod of mods) {\n let degree = 0;\n for (const dep of mod.require) {\n // Only count deps that exist and aren't already loaded\n if (modMap.has(dep) && !loaded.has(dep)) {\n degree++;\n }\n }\n // Also count soft deps that exist and aren't loaded\n for (const dep of mod.after) {\n if (modMap.has(dep) && !loaded.has(dep)) {\n degree++;\n }\n }\n inDegree.set(mod.name, degree);\n }\n\n // Build reverse adjacency (who depends on me)\n const dependents = new Map(mods.map(m => [m.name, []]));\n for (const mod of mods) {\n for (const dep of mod.require) {\n if (modMap.has(dep) && !loaded.has(dep)) {\n dependents.get(dep).push(mod.name);\n }\n }\n for (const dep of mod.after) {\n if (modMap.has(dep) && !loaded.has(dep)) {\n dependents.get(dep).push(mod.name);\n }\n }\n }\n\n // Start with mods that have no dependencies\n const ready = mods\n .filter(m => inDegree.get(m.name) === 0)\n .sort(compareMods);\n \n const sorted = [];\n \n while (ready.length > 0) {\n // Take highest priority (first after sort)\n const mod = ready.shift();\n sorted.push(mod);\n\n // Decrement in-degree of dependents\n for (const depName of dependents.get(mod.name)) {\n const newDegree = inDegree.get(depName) - 1;\n inDegree.set(depName, newDegree);\n if (newDegree === 0) {\n ready.push(modMap.get(depName));\n ready.sort(compareMods);\n }\n }\n }\n\n return sorted;\n};\n\n/**\n * Get mods that are ready to load given currently loaded mods.\n * A mod is ready if all its hard dependencies are loaded.\n * \n * @param {ModDescriptor[]} pending - Mods waiting to load\n * @param {Set<string>} loaded - Names of loaded mods\n * @returns {ModDescriptor[]} Mods ready to load (sorted by priority/index)\n */\nexport const getReadyMods = (pending, loaded) => {\n return pending\n .filter(mod => mod.require.every(dep => loaded.has(dep)))\n .sort(compareMods);\n};\n\n/**\n * Full dependency resolution.\n * \n * @param {ModDescriptor[]} mods - All mod descriptors\n * @param {Set<string>} [alreadyLoaded] - Already loaded mod names\n * @returns {ResolutionResult} Resolution result\n */\nexport const resolveDependencies = (mods, alreadyLoaded = new Set()) => {\n // Find cycles in hard dependencies\n const cycles = detectCycles(mods);\n \n // Find missing hard dependencies\n const allAvailable = new Set([...mods.map(m => m.name), ...alreadyLoaded]);\n const missing = {};\n for (const mod of mods) {\n const missingDeps = mod.require.filter(dep => !allAvailable.has(dep));\n if (missingDeps.length > 0) {\n missing[mod.name] = missingDeps;\n }\n }\n\n // If there are cycles or missing deps, we can't sort properly\n // Return what we can\n if (cycles.length > 0 || Object.keys(missing).length > 0) {\n // Filter out mods that are in cycles or have missing deps\n const cycleNames = new Set(cycles.flat());\n const missingNames = new Set(Object.keys(missing));\n const validMods = mods.filter(m => \n !cycleNames.has(m.name) && !missingNames.has(m.name)\n );\n \n return {\n sorted: topoSort(validMods, alreadyLoaded),\n cycles,\n missing,\n };\n }\n\n return {\n sorted: topoSort(mods, alreadyLoaded),\n cycles: [],\n missing: {},\n };\n};\n\n/**\n * Create a ModDescriptor from basic properties.\n * Utility for converting from DOM elements or test data.\n * \n * @param {Object} props\n * @param {string} props.name\n * @param {string} [props.src]\n * @param {string[]} [props.require]\n * @param {string[]} [props.after]\n * @param {number} [props.priority]\n * @param {number} props.index\n * @returns {ModDescriptor}\n */\nexport const createModDescriptor = ({ \n name, \n src, \n require = [], \n after = [], \n priority = Infinity, \n index \n}) => ({\n name: name || src,\n require,\n after,\n priority,\n index,\n});\n","import {\n VellumLoadError,\n VellumMountError,\n VellumDependencyError,\n VellumCircularDependencyError,\n VellumAttributeError,\n VellumReferenceError,\n} from './errors.mjs';\nimport { setState } from './utils/attributes.mjs';\nimport {\n createModDescriptor,\n resolveDependencies,\n detectCycles,\n} from './utils/dependencies.mjs';\n\n/**\n * @typedef {Object} ModState\n * @property {Object|null} module - The sanitized module (mount/unmount only)\n * @property {Function|null} mount - The mount function\n * @property {Function|null} unmount - The unmount function\n * @property {AbortController|null} abortController - For cancelling in-progress loads\n */\n\n/**\n * VellumHost is the container element that discovers and manages mod elements.\n * It handles loading, mounting, unmounting, and observes DOM mutations.\n */\nexport class VellumHost extends HTMLElement {\n /** @type {WeakMap<HTMLElement, ModState>} */\n #modState = new WeakMap();\n\n /** @type {MutationObserver|null} */\n #observer = null;\n\n /** @type {Set<string>} */\n #loadedNames = new Set();\n\n /** @type {boolean} */\n #isConnected = false;\n\n /** @type {string} */\n #modTagName = 'vellum-mod';\n\n /** @type {boolean} */\n #isDiscovering = false;\n\n /** @type {boolean} */\n #needsRediscovery = false;\n\n /**\n * Get the tag name used for mod elements.\n * @returns {string}\n */\n get modTagName() {\n return this.getAttribute('mod-tag') || this.#modTagName;\n }\n\n connectedCallback() {\n this.#isConnected = true;\n this.#setupObserver();\n // Defer discovery to allow descendant elements to be parsed\n setTimeout(() => {\n if (this.#isConnected) {\n this.#discoverAndLoad();\n }\n }, 0);\n }\n\n disconnectedCallback() {\n this.#isConnected = false;\n this.#observer?.disconnect();\n this.#observer = null;\n // Dispatch teardown before unloading\n this.dispatchEvent(new CustomEvent('vellum:teardown', {\n bubbles: true,\n composed: true,\n detail: {},\n }));\n // Unload all mods\n this.#unloadAllMods();\n }\n\n /**\n * Set up the MutationObserver to watch for mod element changes.\n */\n #setupObserver() {\n this.#observer = new MutationObserver((mutations) => {\n this.#handleMutations(mutations);\n });\n\n this.#observer.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['src', 'disabled'],\n });\n }\n\n /**\n * Handle batched mutations.\n * @param {MutationRecord[]} mutations\n */\n #handleMutations(mutations) {\n const added = new Set();\n const removed = new Set();\n const srcChanged = new Set();\n const disabledChanged = new Set();\n\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n for (const node of mutation.addedNodes) {\n if (this.#isModElement(node) && this.#isDirectMod(node)) {\n added.add(node);\n }\n }\n for (const node of mutation.removedNodes) {\n if (this.#isModElement(node)) {\n removed.add(node);\n }\n }\n } else if (mutation.type === 'attributes') {\n const target = mutation.target;\n if (this.#isModElement(target) && this.#isDirectMod(target)) {\n if (mutation.attributeName === 'src') {\n srcChanged.add(target);\n } else if (mutation.attributeName === 'disabled') {\n disabledChanged.add(target);\n }\n }\n }\n }\n\n // Process removals first\n for (const element of removed) {\n this.#unloadMod(element);\n }\n\n // Process src changes (abort/unload then reload)\n for (const element of srcChanged) {\n if (!removed.has(element)) {\n this.#handleSrcChange(element);\n }\n }\n\n // Process disabled changes\n for (const element of disabledChanged) {\n if (!removed.has(element) && !srcChanged.has(element)) {\n this.#handleDisabledChange(element);\n }\n }\n\n // Process additions (will trigger dependency resolution)\n if (added.size > 0) {\n this.#discoverAndLoad();\n }\n }\n\n /**\n * Check if a node is a mod element.\n * @param {Node} node\n * @returns {boolean}\n */\n #isModElement(node) {\n return (\n node.nodeType === Node.ELEMENT_NODE &&\n node.tagName.toLowerCase() === this.modTagName.toLowerCase()\n );\n }\n\n /**\n * Check if a mod element belongs to this host (not a nested host).\n * @param {HTMLElement} element\n * @returns {boolean}\n */\n #isDirectMod(element) {\n // Walk up to find the closest host\n let parent = element.parentElement;\n while (parent) {\n if (parent === this) return true;\n if (parent.tagName.toLowerCase() === this.tagName.toLowerCase()) {\n return false; // Belongs to a nested host\n }\n parent = parent.parentElement;\n }\n return false;\n }\n\n /**\n * Handle src attribute change on a mod element.\n * @param {HTMLElement} element\n */\n async #handleSrcChange(element) {\n const state = this.#modState.get(element);\n \n // Abort if loading\n if (state?.abortController) {\n state.abortController.abort();\n }\n \n // Unload if loaded\n await this.#unloadMod(element);\n \n // Reload with new src\n if (!element.hasAttribute('disabled') && element.getAttribute('src')) {\n this.#discoverAndLoad();\n }\n }\n\n /**\n * Handle disabled attribute change on a mod element.\n * @param {HTMLElement} element\n */\n async #handleDisabledChange(element) {\n if (element.hasAttribute('disabled')) {\n // Disabled added - unload\n await this.#unloadMod(element);\n } else {\n // Disabled removed - try to load\n this.#discoverAndLoad();\n }\n }\n\n /**\n * Discover all mod elements and initiate loading.\n */\n async #discoverAndLoad() {\n // Prevent concurrent discovery\n if (this.#isDiscovering) {\n this.#needsRediscovery = true;\n return;\n }\n\n this.#isDiscovering = true;\n this.#needsRediscovery = false;\n\n try {\n await this.#doDiscoverAndLoad();\n } finally {\n this.#isDiscovering = false;\n \n // If something requested rediscovery while we were busy, do it now\n if (this.#needsRediscovery) {\n this.#discoverAndLoad();\n }\n }\n }\n\n /**\n * Internal implementation of discover and load.\n */\n async #doDiscoverAndLoad() {\n const mods = this.#getModElements();\n \n // Filter to only pending mods (not disabled, not already loading/loaded/failed)\n const pending = mods.filter((el) => {\n if (el.hasAttribute('disabled')) return false;\n const hasState = el.hasAttribute('loading') ||\n el.hasAttribute('loaded') ||\n el.hasAttribute('failed') ||\n el.hasAttribute('unloading');\n return !hasState;\n });\n\n if (pending.length === 0) return;\n\n // Dispatch loading-started\n this.dispatchEvent(new CustomEvent('vellum:loading-started', {\n bubbles: true,\n composed: true,\n detail: { total: pending.length },\n }));\n\n // Build descriptors\n const descriptors = pending.map((el, index) => \n createModDescriptor({\n name: el.getAttribute('name') || el.getAttribute('src'),\n src: el.getAttribute('src'),\n require: this.#parseList(el.getAttribute('require')),\n after: this.#parseList(el.getAttribute('after')),\n priority: this.#parsePriority(el.getAttribute('priority')),\n index,\n })\n );\n\n // Check for cycles\n const cycles = detectCycles(descriptors);\n if (cycles.length > 0) {\n // Collect all unique names across all cycles and fail each mod\n const failedNames = new Set();\n for (const cycle of cycles) {\n const error = new VellumCircularDependencyError(cycle);\n for (const name of cycle) {\n if (failedNames.has(name)) continue;\n failedNames.add(name);\n const el = pending.find(\n (e) => (e.getAttribute('name') || e.getAttribute('src')) === name\n );\n if (el) {\n setState(el, 'failed');\n this.#dispatchModEvent('vellum:mod-failed', el, { error });\n }\n }\n }\n this.#dispatchLoadingComplete(pending);\n return;\n }\n\n // Resolve dependencies\n const { sorted, missing } = resolveDependencies(descriptors, this.#loadedNames);\n\n // Handle missing dependencies\n for (const [modName, missingDeps] of Object.entries(missing)) {\n const el = pending.find(\n (e) => (e.getAttribute('name') || e.getAttribute('src')) === modName\n );\n const error = new VellumDependencyError(modName, missingDeps);\n setState(el, 'failed');\n this.#dispatchModEvent('vellum:mod-failed', el, { error });\n }\n\n // Load sorted mods in parallel batches based on dependency levels\n await this.#loadModsInOrder(sorted, pending);\n this.#dispatchLoadingComplete(pending);\n }\n\n /**\n * Dispatch vellum:loading-complete with counts from processed elements.\n * @param {HTMLElement[]} elements\n */\n #dispatchLoadingComplete(elements) {\n let loaded = 0;\n let failed = 0;\n for (const el of elements) {\n if (el.hasAttribute('loaded')) loaded++;\n if (el.hasAttribute('failed')) failed++;\n }\n this.dispatchEvent(new CustomEvent('vellum:loading-complete', {\n bubbles: true,\n composed: true,\n detail: { loaded, failed },\n }));\n }\n\n /**\n * Load mods respecting dependency order but parallelizing where possible.\n * @param {Object[]} sorted - Sorted mod descriptors\n * @param {HTMLElement[]} elements - Pending mod elements\n */\n async #loadModsInOrder(sorted, elements) {\n const elementMap = new Map(\n elements.map((el) => [el.getAttribute('name') || el.getAttribute('src'), el])\n );\n\n for (const descriptor of sorted) {\n const element = elementMap.get(descriptor.name);\n\n // Check if all hard deps are loaded\n const allDepsLoaded = descriptor.require.every(\n (dep) => this.#loadedNames.has(dep)\n );\n\n if (!allDepsLoaded) {\n // This shouldn't happen if resolveDependencies worked correctly\n const missing = descriptor.require.filter((d) => !this.#loadedNames.has(d));\n const error = new VellumDependencyError(descriptor.name, missing);\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-failed', element, { error });\n continue;\n }\n\n await this.#loadMod(element);\n }\n }\n\n /**\n * Load a single mod element.\n * @param {HTMLElement} element\n */\n async #loadMod(element) {\n const src = element.getAttribute('src');\n const ref = element.getAttribute('ref');\n const name = element.getAttribute('name') || src || ref;\n\n // Validate: exactly one of src or ref must be present\n if (src && ref) {\n const error = new VellumAttributeError(name, 'both');\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-failed', element, { error });\n return;\n }\n\n if (!src && !ref) {\n const error = new VellumAttributeError(name, 'neither');\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-failed', element, { error });\n return;\n }\n\n // ref resolution is not yet implemented\n if (ref) {\n const error = new VellumReferenceError(ref);\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-failed', element, { error });\n return;\n }\n\n // Set up state\n const abortController = new AbortController();\n this.#modState.set(element, {\n module: null,\n mount: null,\n unmount: null,\n abortController,\n });\n\n setState(element, 'loading');\n\n try {\n // Import the module\n const module = await this._importModule(src);\n\n // Check if aborted\n if (abortController.signal.aborted) {\n setState(element, null);\n return;\n }\n\n const mount = typeof module.mount === 'function' ? module.mount : null;\n const unmount = typeof module.unmount === 'function' ? module.unmount : null;\n\n // Update state — only retain mount/unmount per §9.4 module isolation\n const state = this.#modState.get(element);\n if (state) {\n state.module = { mount, unmount };\n state.mount = mount;\n state.unmount = unmount;\n }\n\n // Call mount if available\n if (mount) {\n try {\n await mount({\n element,\n host: this,\n signal: abortController.signal,\n });\n\n // Check if aborted during mount\n if (abortController.signal.aborted) {\n setState(element, null);\n return;\n }\n } catch (mountError) {\n if (abortController.signal.aborted) {\n setState(element, null);\n return;\n }\n const error = new VellumMountError(name, { cause: mountError });\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-failed', element, { error });\n return;\n }\n }\n\n // Success — clear abortController now that all async work is done\n if (state) {\n state.abortController = null;\n }\n setState(element, 'loaded');\n this.#loadedNames.add(name);\n this.#dispatchModEvent('vellum:mod-loaded', element, { module: state.module });\n\n // Check if any pending mods can now load\n this.#discoverAndLoad();\n\n } catch (importError) {\n if (abortController.signal.aborted) {\n setState(element, null);\n return;\n }\n\n const error = new VellumLoadError(src, { cause: importError });\n setState(element, 'failed');\n this.#dispatchModEvent('vellum:mod-failed', element, { error });\n }\n }\n\n /**\n * Unload a single mod element.\n * @param {HTMLElement} element\n */\n async #unloadMod(element) {\n const state = this.#modState.get(element);\n const name = element.getAttribute('name') || element.getAttribute('src');\n const wasLoaded = element.hasAttribute('loaded');\n const module = state?.module || null;\n\n // Abort if loading\n if (state?.abortController) {\n state.abortController.abort();\n setState(element, null);\n this.#modState.delete(element);\n return;\n }\n\n // If unloading already, skip\n if (element.hasAttribute('unloading')) {\n return;\n }\n\n // If not loaded or failed, just clean up\n if (!element.hasAttribute('loaded') && !element.hasAttribute('failed')) {\n setState(element, null);\n this.#modState.delete(element);\n return;\n }\n\n // Set unloading state\n if (wasLoaded) {\n setState(element, 'unloading');\n }\n\n // Call unmount if available\n if (state?.unmount && wasLoaded) {\n try {\n await state.unmount({\n element,\n host: this,\n });\n } catch (err) {\n // Log but don't fail\n console.warn(`Unmount error for mod \"${name}\":`, err);\n }\n }\n\n // Clear state\n setState(element, null);\n this.#loadedNames.delete(name);\n this.#modState.delete(element);\n\n // Dispatch event\n this.#dispatchModEvent('vellum:mod-unloaded', element, {\n module,\n wasLoaded,\n });\n }\n\n /**\n * Unload all mod elements.\n */\n async #unloadAllMods() {\n const mods = this.#getModElements();\n for (const element of mods) {\n await this.#unloadMod(element);\n }\n }\n\n /**\n * Get all mod elements that belong to this host.\n * @returns {HTMLElement[]}\n */\n #getModElements() {\n const all = Array.from(this.querySelectorAll(this.modTagName));\n const direct = all.filter((el) => this.#isDirectMod(el));\n\n // Warn and skip elements that are not registered custom elements (§3.7)\n if (direct.length > 0 && !customElements.get(this.modTagName.toLowerCase())) {\n console.warn(\n `<${this.modTagName}> elements found but not registered as custom elements. ` +\n `Register the mod element class before connecting the host.`\n );\n return [];\n }\n\n return direct;\n }\n\n /**\n * Parse a comma-separated list attribute.\n * @param {string|null} attr\n * @returns {string[]}\n */\n #parseList(attr) {\n if (!attr) return [];\n return attr.split(',').map((s) => s.trim()).filter(Boolean);\n }\n\n /**\n * Parse a priority attribute.\n * @param {string|null} attr\n * @returns {number}\n */\n #parsePriority(attr) {\n if (attr === null) return Infinity;\n const num = parseInt(attr, 10);\n return Number.isNaN(num) ? Infinity : num;\n }\n\n /**\n * Import a module by URL. Extracted for testability.\n * @param {string} src\n * @returns {Promise<Object>}\n */\n _importModule(src) {\n return import(/* @vite-ignore */ src);\n }\n\n /**\n * Dispatch a mod-related event.\n * @param {string} type\n * @param {HTMLElement} element\n * @param {Object} [extra]\n */\n #dispatchModEvent(type, element, extra = {}) {\n element.dispatchEvent(\n new CustomEvent(type, {\n bubbles: true,\n composed: true,\n detail: {\n element,\n ...extra,\n },\n })\n );\n }\n}\n","import { parseList } from './utils/attributes.mjs';\n\n/**\n * VellumMod is an inert custom element representing a loadable module.\n * It carries configuration but does not perform loading itself.\n * Loading is the sole responsibility of the host element.\n */\nexport class VellumMod extends HTMLElement {\n /**\n * Get the module source URL.\n * @returns {string}\n */\n get src() {\n return this.getAttribute('src') || '';\n }\n\n /**\n * Get the mod name (for dependency resolution).\n * Falls back to src if name not specified.\n * @returns {string}\n */\n get name() {\n return this.getAttribute('name') || this.src;\n }\n\n /**\n * Get hard dependencies (require attribute).\n * @returns {string[]}\n */\n get dependencies() {\n return parseList(this.getAttribute('require'));\n }\n\n /**\n * Get soft dependencies (after attribute).\n * @returns {string[]}\n */\n get softDependencies() {\n return parseList(this.getAttribute('after'));\n }\n\n /**\n * Get the priority (lower = higher priority).\n * @returns {number}\n */\n get priority() {\n const attr = this.getAttribute('priority');\n if (attr === null) return Infinity;\n const num = parseInt(attr, 10);\n return Number.isNaN(num) ? Infinity : num;\n }\n\n /**\n * Check if the mod is disabled.\n * @returns {boolean}\n */\n get disabled() {\n return this.hasAttribute('disabled');\n }\n\n /**\n * Get the current state.\n * @returns {'initial'|'loading'|'loaded'|'failed'|'unloading'}\n */\n get state() {\n if (this.hasAttribute('loading')) return 'loading';\n if (this.hasAttribute('loaded')) return 'loaded';\n if (this.hasAttribute('failed')) return 'failed';\n if (this.hasAttribute('unloading')) return 'unloading';\n return 'initial';\n }\n}\n"],"names":["VellumError","message","cause","VellumDependencyError","modName","missingDependencies","deps","VellumCircularDependencyError","cycle","VellumLoadError","src","VellumMountError","VellumAttributeError","variant","VellumReferenceError","ref","parseList","attr","s","setState","element","state","states","detectCycles","mods","modMap","m","color","parent","cycles","dfs","name","mod","dep","depColor","curr","compareMods","b","topoSort","loaded","inDegree","degree","dependents","ready","sorted","depName","newDegree","resolveDependencies","alreadyLoaded","allAvailable","missing","missingDeps","cycleNames","missingNames","validMods","createModDescriptor","require","after","priority","index","VellumHost","#modState","#observer","#loadedNames","#isConnected","#modTagName","#isDiscovering","#needsRediscovery","#setupObserver","#discoverAndLoad","#unloadAllMods","mutations","#handleMutations","added","removed","srcChanged","disabledChanged","mutation","node","#isModElement","#isDirectMod","target","#unloadMod","#handleSrcChange","#handleDisabledChange","#doDiscoverAndLoad","pending","#getModElements","el","descriptors","#parseList","#parsePriority","failedNames","error","e","#dispatchModEvent","#dispatchLoadingComplete","#loadModsInOrder","elements","failed","elementMap","descriptor","d","#loadMod","abortController","module","mount","unmount","mountError","importError","wasLoaded","err","direct","num","type","extra","VellumMod"],"mappings":"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"}
|