@byre/vellum 1.0.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/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +504 -0
- package/dist/index.js.map +1 -0
- package/dist/register.cjs +2 -0
- package/dist/register.cjs.map +1 -0
- package/dist/register.js +8 -0
- package/dist/register.js.map +1 -0
- package/dist/spec.md +1292 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Byre
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @byre/vellum
|
|
2
|
+
|
|
3
|
+
Vellum is a minimal, DOM-centric **mod loader kernel** for web applications.
|
|
4
|
+
|
|
5
|
+
It provides two custom elements:
|
|
6
|
+
|
|
7
|
+
- `<vellum-host>` (or any registered name) — coordinates mod discovery and lifecycle
|
|
8
|
+
- `<vellum-mod>` (or any registered name) — declarative markers for loadable modules
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- DOM is the source of truth (state via attributes: `loading`, `loaded`,
|
|
13
|
+
`failed`, `unloading`)
|
|
14
|
+
- Hard (`require`) and soft (`after`) dependencies with cycle detection
|
|
15
|
+
- Priority + DOM order tie-breaking
|
|
16
|
+
- Dynamic addition/removal, `disabled`, `src` changes
|
|
17
|
+
- Nested hosts supported
|
|
18
|
+
- Native ES modules via `import()`
|
|
19
|
+
- No framework lock-in — just loading and lifecycle coordination
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @byre/vellum
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// app.js
|
|
31
|
+
import { VellumHost, VellumMod } from '@byre/vellum';
|
|
32
|
+
|
|
33
|
+
customElements.define('my-app', VellumHost);
|
|
34
|
+
customElements.define('my-mod', VellumMod);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<my-app mod-tag="my-mod">
|
|
39
|
+
<my-mod src="./core.mjs" name="core" priority="1"></my-mod>
|
|
40
|
+
<my-mod src="./ui.mjs" name="ui" require="core" after="logger"></my-mod>
|
|
41
|
+
</my-app>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
See the full [specification](./spec.md) for details.
|
|
45
|
+
|
|
46
|
+
## Development
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install
|
|
50
|
+
npm run dev
|
|
51
|
+
npm test
|
|
52
|
+
npm run build
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +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:u}={}){const i=e.join(", ");super(`Mod "${t}" has unmet dependencies: ${i}`,{cause:u}),this.name="VellumDependencyError",this.modName=t,this.missingDependencies=e}}class w extends f{constructor(t,{cause:e}={}){super(`Circular dependency detected: ${t.join(" → ")}`,{cause:e}),this.name="VellumCircularDependencyError",this.cycle=t}}class g 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}}const b=a=>a?a.split(",").map(t=>t.trim()).filter(Boolean):[],d=(a,t)=>{const e=["loading","loaded","failed","unloading"];for(const u of e)a.removeAttribute(u);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])),u=new Map,i=[],o=s=>{e.set(s,1);const r=t.get(s);if(r){for(const n of r.require){if(!t.has(n))continue;const l=e.get(n);if(l===1){const c=[n];let h=s;for(;h!==n;)c.unshift(h),h=u.get(h);c.push(n),i.push(c)}else l===0&&(u.set(n,s),o(n))}e.set(s,2)}};for(const s of a)e.get(s.name)===0&&o(s.name);return i},y=(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 u=new Map;for(const r of a){let n=0;for(const l of r.require)e.has(l)&&!t.has(l)&&n++;for(const l of r.after)e.has(l)&&!t.has(l)&&n++;u.set(r.name,n)}const i=new Map(a.map(r=>[r.name,[]]));for(const r of a){for(const n of r.require)e.has(n)&&!t.has(n)&&i.get(n).push(r.name);for(const n of r.after)e.has(n)&&!t.has(n)&&i.get(n).push(r.name)}const o=a.filter(r=>u.get(r.name)===0).sort(y),s=[];for(;o.length>0;){const r=o.shift();s.push(r);for(const n of i.get(r.name)){const l=u.get(n)-1;u.set(n,l),l===0&&(o.push(e.get(n)),o.sort(y))}}return s},v=(a,t=new Set)=>{const e=p(a),u=new Set([...a.map(o=>o.name),...t]),i={};for(const o of a){const s=o.require.filter(r=>!u.has(r));s.length>0&&(i[o.name]=s)}if(e.length>0||Object.keys(i).length>0){const o=new Set(e.flat()),s=new Set(Object.keys(i)),r=a.filter(n=>!o.has(n.name)&&!s.has(n.name));return{sorted:A(r,t),cycles:e,missing:i}}return{sorted:A(a,t),cycles:[],missing:{}}},E=({name:a,src:t,require:e=[],after:u=[],priority:i=1/0,index:o})=>({name:a||t,require:e,after:u,priority:i,index:o});class C extends HTMLElement{#e=new WeakMap;#i=null;#s=new Set;#c=!1;#f="vellum-mod";#o=!1;#a=!1;get modTagName(){return this.getAttribute("mod-tag")||this.#f}connectedCallback(){this.#c=!0,this.#m(),this.#r()}disconnectedCallback(){this.#c=!1,this.#i?.disconnect(),this.#i=null,this.#M()}#m(){this.#i=new MutationObserver(t=>{this.#g(t)}),this.#i.observe(this,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["src","disabled"]})}#g(t){const e=new Set,u=new Set,i=new Set,o=new Set;for(const s of t)if(s.type==="childList"){for(const r of s.addedNodes)this.#u(r)&&this.#l(r)&&e.add(r);for(const r of s.removedNodes)this.#u(r)&&u.add(r)}else if(s.type==="attributes"){const r=s.target;this.#u(r)&&this.#l(r)&&(s.attributeName==="src"?i.add(r):s.attributeName==="disabled"&&o.add(r))}for(const s of u)this.#n(s);for(const s of i)u.has(s)||this.#p(s);for(const s of o)!u.has(s)&&!i.has(s)&&this.#b(s);e.size>0&&this.#r()}#u(t){return t.nodeType===Node.ELEMENT_NODE&&t.tagName.toLowerCase()===this.modTagName.toLowerCase()}#l(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#p(t){const e=this.#e.get(t);e?.abortController&&e.abortController.abort(),await this.#n(t),!t.hasAttribute("disabled")&&t.getAttribute("src")&&this.#r()}async#b(t){t.hasAttribute("disabled")?await this.#n(t):this.#r()}async#r(){if(this.#o){this.#a=!0;return}this.#o=!0,this.#a=!1;try{await this.#y()}finally{this.#o=!1,this.#a&&this.#r()}}async#y(){const e=this.#d().filter(r=>r.hasAttribute("disabled")?!1:!(r.hasAttribute("loading")||r.hasAttribute("loaded")||r.hasAttribute("failed")||r.hasAttribute("unloading")));if(e.length===0)return;const u=e.map((r,n)=>E({name:r.getAttribute("name")||r.getAttribute("src"),src:r.getAttribute("src"),require:this.#h(r.getAttribute("require")),after:this.#h(r.getAttribute("after")),priority:this.#v(r.getAttribute("priority")),index:n})),i=p(u);if(i.length>0){const r=new w(i[0]);for(const n of i)for(const l of n){const c=e.find(h=>(h.getAttribute("name")||h.getAttribute("src"))===l);c&&c!==e.find(h=>(h.getAttribute("name")||h.getAttribute("src"))===n[0])||c&&(d(c,"failed"),this.#t("vellum:mod-error",c,{error:r}))}this.dispatchEvent(new CustomEvent("vellum:error",{bubbles:!0,detail:{error:r}}));return}const{sorted:o,missing:s}=v(u,this.#s);for(const[r,n]of Object.entries(s)){const l=e.find(c=>(c.getAttribute("name")||c.getAttribute("src"))===r);if(l){const c=new m(r,n);d(l,"failed"),this.#t("vellum:mod-error",l,{error:c})}}await this.#A(o,e)}async#A(t,e){const u=new Map(e.map(i=>[i.getAttribute("name")||i.getAttribute("src"),i]));for(const i of t){const o=u.get(i.name);if(!o)continue;if(!i.require.every(r=>this.#s.has(r))){const r=i.require.filter(l=>!this.#s.has(l)),n=new m(i.name,r);d(o,"failed"),this.#t("vellum:mod-error",o,{error:n});continue}await this.#w(o)}}async#w(t){const e=t.getAttribute("src"),u=t.getAttribute("name")||e;if(!e){const o=new g("");d(t,"failed"),this.#t("vellum:mod-error",t,{error:o});return}const i=new AbortController;this.#e.set(t,{module:null,mount:null,unmount:null,abortController:i}),d(t,"loading"),this.#t("vellum:mod-loading",t);try{const o=await import(e);if(i.signal.aborted){d(t,null);return}const s=typeof o.mount=="function"?o.mount:null,r=typeof o.unmount=="function"?o.unmount:null,n=this.#e.get(t);if(n&&(n.module=o,n.mount=s,n.unmount=r,n.abortController=null),s)try{if(await s({element:t,host:this,signal:i.signal}),i.signal.aborted){d(t,null);return}}catch(l){const c=new M(u,{cause:l});d(t,"failed"),this.#t("vellum:mod-error",t,{error:c});return}d(t,"loaded"),this.#s.add(u),this.#t("vellum:mod-loaded",t),this.#r()}catch(o){if(i.signal.aborted){d(t,null);return}const s=new g(e,{cause:o});d(t,"failed"),this.#t("vellum:mod-error",t,{error:s})}}async#n(t){const e=this.#e.get(t),u=t.getAttribute("name")||t.getAttribute("src"),i=t.hasAttribute("loaded");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(i&&d(t,"unloading"),e?.unmount&&i)try{await e.unmount({element:t,host:this})}catch(o){console.warn(`Unmount error for mod "${u}":`,o)}d(t,null),this.#s.delete(u),this.#e.delete(t),this.#t("vellum:mod-unloaded",t,{wasLoaded:i})}}async#M(){const t=this.#d();for(const e of t)await this.#n(e)}#d(){return Array.from(this.querySelectorAll(this.modTagName)).filter(e=>this.#l(e))}#h(t){return t?t.split(",").map(e=>e.trim()).filter(Boolean):[]}#v(t){if(t===null)return 1/0;const e=parseInt(t,10);return Number.isNaN(e)?1/0:e}#t(t,e,u={}){e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:{element:e,name:e.getAttribute("name")||e.getAttribute("src"),...u}}))}}class N extends HTMLElement{get src(){return this.getAttribute("src")||""}get name(){return this.getAttribute("name")||this.src}get dependencies(){return b(this.getAttribute("require"))}get softDependencies(){return b(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.VellumCircularDependencyError=w;exports.VellumDependencyError=m;exports.VellumError=f;exports.VellumHost=C;exports.VellumLoadError=g;exports.VellumMod=N;exports.VellumMountError=M;exports.createModDescriptor=E;exports.detectCycles=p;exports.resolveDependencies=v;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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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"}
|