├── Makefile ├── static └── index.js ├── package.json ├── README.md ├── src ├── VariableExtractor.ts ├── ReachElement.ts ├── DataElement.ts └── HTMLParser.ts ├── tsconfig.json ├── index.ts ├── client └── index.ts └── bun.lock /Makefile: -------------------------------------------------------------------------------- 1 | bundle: 2 | bun build ./client/index.js --outdir ./static --watch 3 | 4 | run: 5 | bun run --hot index.ts 6 | 7 | -------------------------------------------------------------------------------- /static/index.js: -------------------------------------------------------------------------------- 1 | // client/index.ts 2 | class Reach { 3 | signals; 4 | constructor() { 5 | this.signals = {}; 6 | } 7 | } 8 | var $ = new Reach; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reach", 3 | "module": "index.ts", 4 | "type": "module", 5 | "private": true, 6 | "devDependencies": { 7 | "@types/bun": "latest" 8 | }, 9 | "peerDependencies": { 10 | "typescript": "^5" 11 | }, 12 | "dependencies": { 13 | "@types/jsdom": "^21.1.7", 14 | "jsdom": "^26.0.0", 15 | "xerus": "github:phillip-england/xerus" 16 | } 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reach 2 | The ultimate html preprocessor. Provide Reach with your HTML templates, and let it compile them into reactive web components. 3 | 4 | ## Moto 5 | 1. Put that 💩 in you're markup. 6 | 2. Stay language agnostic. 7 | 3. Keep the DX dead-simple. 8 | 9 | Reach is all about banking on *the future*. With the rising adoption of view transitions, developers will be able to lean more on the server without sacraficing UX. 10 | 11 | Write the HTML templates and let reach generate your web components. 12 | 13 | Dead simple. -------------------------------------------------------------------------------- /src/VariableExtractor.ts: -------------------------------------------------------------------------------- 1 | export class TemplateVariable { 2 | raw: string; 3 | inner: string; 4 | 5 | constructor(raw: string) { 6 | this.raw = raw; 7 | this.inner = raw.slice(2, -2).trim(); 8 | } 9 | } 10 | 11 | export class VariableExtractor { 12 | variables: TemplateVariable[]; 13 | private constructor(variables: TemplateVariable[]) { 14 | this.variables = variables; 15 | } 16 | static fromStr(str: string): VariableExtractor { 17 | const matches = str.match(/{{\s*[^}]+\s*}}/g) || []; 18 | const variables = matches.map((m) => new TemplateVariable(m)); 19 | return new VariableExtractor(variables); 20 | } 21 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Environment setup & latest features 4 | "lib": ["esnext", "DOM"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedIndexedAccess": true, 22 | 23 | // Some stricter flags (disabled by default) 24 | "noUnusedLocals": false, 25 | "noUnusedParameters": false, 26 | "noPropertyAccessFromIndexSignature": false 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import { HTTPContext, logger, Xerus } from "xerus/xerus"; 4 | import { tmpl } from "./src/HTMLParser"; 5 | 6 | let app = new Xerus() 7 | 8 | app.use(logger) 9 | 10 | app.get("/static/*", async (c: HTTPContext) => { 11 | return await c.file("." + c.path); 12 | }); 13 | 14 | app.get('/', async (c: HTTPContext) => { 15 | 16 | let colors: string[] = ['blue', 'red', 'orange'] 17 | 18 | return c.html(tmpl(` 19 | 20 |
21 |

{{ count }}

22 | 23 |
24 | 25 |
26 | ${colors.map((color) => { 27 | return ` 28 |

${color}

29 | ` 30 | })} 31 |
32 | 33 | 34 | 35 | 36 | 37 | `)) 38 | }) 39 | 40 | await app.listen() -------------------------------------------------------------------------------- /src/ReachElement.ts: -------------------------------------------------------------------------------- 1 | export enum ReachElementType { 2 | DATA = '__data' 3 | } 4 | 5 | export class ReachElement { 6 | elm: HTMLElement 7 | reachAttrs: string[] 8 | constructor(elm: HTMLElement, reachAttrs: string[]) { 9 | this.elm = elm 10 | this.reachAttrs = reachAttrs 11 | } 12 | isTypeOf(str: ReachElementType): boolean { 13 | if (this.reachAttrs.includes(str)) { 14 | return true 15 | } 16 | return false 17 | } 18 | static isReachElement(elm: HTMLElement): [string[], boolean] { 19 | let validAttrs: ReachElementType[] = [ReachElementType.DATA] 20 | let foundAttrs: string[] = [] 21 | for (let i = 0; i < validAttrs.length; i++) { 22 | let attr = validAttrs[i] as string 23 | if (elm.hasAttribute(attr)) { 24 | foundAttrs.push(attr) 25 | } 26 | } 27 | if (foundAttrs.length == 0) { 28 | return [foundAttrs, false] 29 | } 30 | return [foundAttrs, true] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/DataElement.ts: -------------------------------------------------------------------------------- 1 | import type { ReachElement } from "./ReachElement" 2 | import { TemplateVariable, VariableExtractor } from "./VariableExtractor" 3 | 4 | export class DataElement { 5 | reachElm: ReachElement 6 | varExtractor: VariableExtractor 7 | dataAttr: string 8 | data: Record = {} 9 | dataJSON: any 10 | html: string = '' 11 | constructor(reachElm: ReachElement) { 12 | this.reachElm = reachElm 13 | this.html = this.reachElm.elm.outerHTML 14 | this.varExtractor = VariableExtractor.fromStr(this.reachElm.elm.outerHTML) 15 | this.dataAttr = this.reachElm.elm.getAttribute('__data') as string 16 | let json = JSON.parse(this.dataAttr) 17 | this.data = json 18 | for (let i = 0; i < this.varExtractor.variables.length; i++) { 19 | let variable = this.varExtractor.variables[i] as TemplateVariable 20 | let stateValue = this.data[variable.inner] 21 | this.html = this.html.replace(variable.raw, stateValue) 22 | } 23 | console.log(this.html) 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/HTMLParser.ts: -------------------------------------------------------------------------------- 1 | import { JSDOM } from 'jsdom'; 2 | 3 | import { ReachElement, ReachElementType } from './ReachElement'; 4 | import { DataElement } from './DataElement'; 5 | 6 | export class HTMLParser { 7 | dom: JSDOM 8 | doc: Document 9 | elms: NodeListOf 10 | reachElms: ReachElement[] = [] 11 | dataElms: DataElement[] = [] 12 | constructor(str: string) { 13 | this.dom = new JSDOM(str) 14 | this.doc = this.dom.window.document 15 | this.elms = this.doc.querySelectorAll('*') 16 | for (let i = 0; i < this.elms.length; i++) { 17 | let elm = this.elms[i] as HTMLElement 18 | let [attrs, isReachElement] = ReachElement.isReachElement(elm) 19 | if (!isReachElement) { 20 | continue 21 | } 22 | let reachElm = new ReachElement(elm, attrs) 23 | this.reachElms.push(reachElm) 24 | for (const [k, v] of Object.entries(ReachElementType)) { 25 | switch (v) { 26 | case ReachElementType.DATA: { 27 | let dataElm = new DataElement(reachElm) 28 | this.dataElms.push(dataElm) 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | 36 | 37 | export function tmpl(str: string): string { 38 | let parser = new HTMLParser(str) 39 | return str 40 | } -------------------------------------------------------------------------------- /client/index.ts: -------------------------------------------------------------------------------- 1 | 2 | //================================== 3 | // util 4 | //================================== 5 | 6 | // generate a random string contains only letters and numbers at a given length 7 | function randomStr(length: number): string { 8 | const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 9 | let result = ''; 10 | for (let i = 0; i < length; i++) { 11 | result += chars.charAt(Math.floor(Math.random() * chars.length)); 12 | } 13 | return result; 14 | } 15 | 16 | 17 | //================================== 18 | // signal 19 | //================================== 20 | 21 | // instantiate a reactive value which can subscribe to changes 22 | class Signal { 23 | private value: any; 24 | private listeners: Set<(value: any) => void>; 25 | dataType: string; 26 | 27 | constructor(value: any, dataType: string) { 28 | this.value = value; 29 | this.listeners = new Set(); 30 | this.dataType = dataType; 31 | } 32 | 33 | get() { 34 | switch (this.dataType) { 35 | case 'bool': 36 | return Boolean(this.value); 37 | case 'int': 38 | return Number(this.value); 39 | case 'str': 40 | return String(this.value); 41 | case 'obj': 42 | return Object(this.value); 43 | default: 44 | return String(this.value); 45 | } 46 | } 47 | 48 | set(newValue: any): void { 49 | if (!this.checkType(newValue)) { 50 | console.warn( 51 | `Signal.set() received value of type '${typeof newValue}' but expected '${this.dataType}'. Value:`, 52 | newValue 53 | ); 54 | } 55 | this.value = newValue; 56 | this.listeners.forEach((listener) => listener(newValue)); 57 | } 58 | 59 | subscribe(listener: (value: any) => void): () => void { 60 | if (typeof listener !== 'function') { 61 | console.warn( 62 | `Signal.subscribe() expected a function but received:`, 63 | listener 64 | ); 65 | return () => {}; 66 | } 67 | 68 | const wrappedListener = (value: any) => { 69 | if (!this.checkType(value)) { 70 | console.warn( 71 | `Signal listener received value of type '${typeof value}' but expected '${this.dataType}'. Value:`, 72 | value 73 | ); 74 | } 75 | listener(value); 76 | }; 77 | 78 | this.listeners.add(wrappedListener); 79 | return () => this.listeners.delete(wrappedListener); 80 | } 81 | 82 | private checkType(value: any): boolean { 83 | switch (this.dataType) { 84 | case 'bool': 85 | return typeof value === 'boolean'; 86 | case 'int': 87 | return typeof value === 'number' && Number.isInteger(value); 88 | case 'str': 89 | return typeof value === 'string'; 90 | case 'obj': 91 | return typeof value === 'object' && value !== null; 92 | default: 93 | return true; 94 | } 95 | } 96 | } 97 | 98 | 99 | //================================== 100 | // reach 101 | //================================== 102 | 103 | class Reach { 104 | signals: Record 105 | constructor() { 106 | this.signals = {} 107 | 108 | } 109 | } 110 | 111 | //================================== 112 | // reach component 113 | //================================== 114 | 115 | 116 | 117 | //================================== 118 | // running 119 | //================================== 120 | 121 | 122 | const $ = new Reach() -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "reach", 6 | "dependencies": { 7 | "@types/jsdom": "^21.1.7", 8 | "jsdom": "^26.0.0", 9 | "xerus": "github:phillip-england/xerus", 10 | }, 11 | "devDependencies": { 12 | "@types/bun": "latest", 13 | }, 14 | "peerDependencies": { 15 | "typescript": "^5", 16 | }, 17 | }, 18 | }, 19 | "packages": { 20 | "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.1.1", "", { "dependencies": { "@csstools/css-calc": "^2.1.2", "@csstools/css-color-parser": "^3.0.8", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA=="], 21 | 22 | "@csstools/color-helpers": ["@csstools/color-helpers@5.0.2", "", {}, "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA=="], 23 | 24 | "@csstools/css-calc": ["@csstools/css-calc@2.1.2", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw=="], 25 | 26 | "@csstools/css-color-parser": ["@csstools/css-color-parser@3.0.8", "", { "dependencies": { "@csstools/color-helpers": "^5.0.2", "@csstools/css-calc": "^2.1.2" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ=="], 27 | 28 | "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@3.0.4", "", { "peerDependencies": { "@csstools/css-tokenizer": "^3.0.3" } }, "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A=="], 29 | 30 | "@csstools/css-tokenizer": ["@csstools/css-tokenizer@3.0.3", "", {}, "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw=="], 31 | 32 | "@types/bun": ["@types/bun@1.2.5", "", { "dependencies": { "bun-types": "1.2.5" } }, "sha512-w2OZTzrZTVtbnJew1pdFmgV99H0/L+Pvw+z1P67HaR18MHOzYnTYOi6qzErhK8HyT+DB782ADVPPE92Xu2/Opg=="], 33 | 34 | "@types/jsdom": ["@types/jsdom@21.1.7", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA=="], 35 | 36 | "@types/node": ["@types/node@22.13.11", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g=="], 37 | 38 | "@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="], 39 | 40 | "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], 41 | 42 | "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="], 43 | 44 | "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], 45 | 46 | "bun-types": ["bun-types@1.2.5", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-3oO6LVGGRRKI4kHINx5PIdIgnLRb7l/SprhzqXapmoYkFl5m4j6EvALvbDVuuBFaamB46Ap6HCUxIXNLCGy+tg=="], 47 | 48 | "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], 49 | 50 | "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], 51 | 52 | "cssstyle": ["cssstyle@4.3.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.1.1", "rrweb-cssom": "^0.8.0" } }, "sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ=="], 53 | 54 | "data-urls": ["data-urls@5.0.0", "", { "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" } }, "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg=="], 55 | 56 | "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], 57 | 58 | "decimal.js": ["decimal.js@10.5.0", "", {}, "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw=="], 59 | 60 | "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], 61 | 62 | "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], 63 | 64 | "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], 65 | 66 | "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], 67 | 68 | "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], 69 | 70 | "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], 71 | 72 | "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], 73 | 74 | "form-data": ["form-data@4.0.2", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w=="], 75 | 76 | "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 77 | 78 | "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], 79 | 80 | "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], 81 | 82 | "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], 83 | 84 | "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], 85 | 86 | "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], 87 | 88 | "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 89 | 90 | "html-encoding-sniffer": ["html-encoding-sniffer@4.0.0", "", { "dependencies": { "whatwg-encoding": "^3.1.1" } }, "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ=="], 91 | 92 | "http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="], 93 | 94 | "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], 95 | 96 | "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], 97 | 98 | "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], 99 | 100 | "jsdom": ["jsdom@26.0.0", "", { "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", "decimal.js": "^10.4.3", "form-data": "^4.0.1", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.16", "parse5": "^7.2.1", "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^5.0.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.1.0", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, "peerDependencies": { "canvas": "^3.0.0" }, "optionalPeers": ["canvas"] }, "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw=="], 101 | 102 | "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], 103 | 104 | "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], 105 | 106 | "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], 107 | 108 | "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], 109 | 110 | "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 111 | 112 | "nwsapi": ["nwsapi@2.2.19", "", {}, "sha512-94bcyI3RsqiZufXjkr3ltkI86iEl+I7uiHVDtcq9wJUTwYQJ5odHDeSzkkrRzi80jJ8MaeZgqKjH1bAWAFw9bA=="], 113 | 114 | "parse5": ["parse5@7.2.1", "", { "dependencies": { "entities": "^4.5.0" } }, "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ=="], 115 | 116 | "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], 117 | 118 | "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], 119 | 120 | "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], 121 | 122 | "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], 123 | 124 | "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], 125 | 126 | "tldts": ["tldts@6.1.85", "", { "dependencies": { "tldts-core": "^6.1.85" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w=="], 127 | 128 | "tldts-core": ["tldts-core@6.1.85", "", {}, "sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA=="], 129 | 130 | "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], 131 | 132 | "tr46": ["tr46@5.1.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw=="], 133 | 134 | "typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], 135 | 136 | "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], 137 | 138 | "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], 139 | 140 | "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], 141 | 142 | "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], 143 | 144 | "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], 145 | 146 | "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], 147 | 148 | "ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="], 149 | 150 | "xerus": ["xerus@github:phillip-england/xerus#071edd1", { "peerDependencies": { "typescript": "^5.0.0" } }, "Phillip-England-xerus-071edd1"], 151 | 152 | "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], 153 | 154 | "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], 155 | } 156 | } 157 | --------------------------------------------------------------------------------