├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── boreDOMCLI
├── README.md
├── cli.js
├── package.json
└── pnpm-lock.yaml
├── dist
├── boreDOM.d.ts
└── boreDOM.min.js
├── examples
├── counter
│ ├── boreDOM.min.js
│ ├── index.html
│ ├── main.js
│ └── simple-counter.js
├── tic-tac-toe
│ ├── README.md
│ ├── boreDOM.d.ts
│ ├── boreDOM.min.js
│ ├── game-board.css
│ ├── game-board.js
│ ├── game-button.css
│ ├── game-turn.js
│ ├── index.html
│ ├── main.js
│ ├── pnpm-lock.yaml
│ └── style.css
└── todo-list
│ ├── boreDOM.min.js
│ └── todo.html
├── jsr.json
├── package.json
├── pnpm-lock.yaml
├── src
├── bore.ts
├── dom.ts
├── index.ts
├── types.ts
└── utils
│ ├── access.ts
│ ├── flatten.ts
│ └── isPojo.ts
├── tests
├── dom.test.ts
├── index.html
├── js
│ └── README.md
├── list-component1.js
├── list-item1.js
├── on-event-component1.js
├── on-event-component2.js
├── runner.ts
├── stateful-component1.js
├── stateful-component2.js
├── stateful-component3.js
├── stateful-component4.js
├── stateful-component5.js
├── stateful-component6.js
├── stateful-component7.js
└── stateful-component8.js
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | tests/dist
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Install dependencies
2 | node_modules:
3 | pnpm install
4 |
5 | dist/boreDOM.min.js:
6 | pnpm run build_module
7 |
8 | dist/boreDOM.d.ts:
9 | pnpm run build_decls
10 |
11 | tests/dist/boreDOM.min.js: dist/boreDOM.min.js
12 | cd tests && ln -fs ../dist .
13 |
14 | build: node_modules dist/boreDOM.min.js dist/boreDOM.d.ts
15 |
16 | place: dist/boreDOM.min.js dist/boreDOM.d.ts
17 | cp dist/boreDOM.min.js ../fun/drawshader/static/js/external/boreDOM.js
18 | cp dist/boreDOM.d.ts ../fun/drawshader/static/js/external
19 |
20 | # Run the development server
21 | dev: node_modules
22 | pnpm run dev
23 |
24 | # Run the development server
25 | test: node_modules tests/dist/boreDOM.min.js
26 | find src/ | entr -s 'make clean && make build && pnpm run test'
27 |
28 | clean:
29 | rm dist/*
30 |
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # boreDOM
2 |
3 | ## Install
4 |
5 | `pnpm install @mr_hugo/boredom`
6 |
7 | ## Description
8 |
9 | Check out the official page:
10 | [https://hugodaniel.com/pages/boredom/](https://hugodaniel.com/pages/boredom/).
11 |
12 | # License
13 |
14 |
boreDOM by Hugo Daniel is marked with CC0 1.0
15 |
--------------------------------------------------------------------------------
/boreDOMCLI/README.md:
--------------------------------------------------------------------------------
1 | ## boreDOM CLI
2 |
3 | To build this, first run `pnpm run build_cli` in the parent folder.
4 |
5 | This command will create the binary .js for boreDOM CLI that contains the
6 | boredom .js code bundled in it.
7 |
8 | After that, you can install boredom locally by running `npm link` in this folder.
9 |
--------------------------------------------------------------------------------
/boreDOMCLI/cli.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs-extra");
2 | const path = require("path");
3 | const glob = require("glob");
4 | const cheerio = require("cheerio");
5 | const { program } = require("commander");
6 | const http = require("http");
7 | const finalhandler = require("finalhandler");
8 | const beautify = require("js-beautify").html;
9 | const chokidar = require("chokidar");
10 | const handler = require("serve-handler");
11 |
12 | const BUILD_DIR = "build";
13 | let serverStarted = false;
14 | let numberOfRefreshes = 0;
15 |
16 | console.log("## boreDOM CLI options");
17 | console.log(
18 | "## ",
19 | "--index ",
20 | "Index file to serve",
21 | "defaults to index.html",
22 | );
23 | console.log(
24 | "## ",
25 | "--html ",
26 | "Folder containing HTML component files",
27 | 'defaults to "components"',
28 | );
29 |
30 | program
31 | .option("--index ", "Index file to serve", "index.html")
32 | .option(
33 | "--html ",
34 | "Folder containing HTML component files",
35 | "components",
36 | )
37 | .parse(process.argv);
38 |
39 | const options = program.opts();
40 |
41 | async function copyStatic() {
42 | const staticDir = path.join(process.cwd(), "static");
43 | if (await fs.pathExists(staticDir)) {
44 | await fs.copy(staticDir, path.join(BUILD_DIR, "static"));
45 | console.log("Static folder copied.");
46 | }
47 | }
48 |
49 | async function copyBoreDOM() {
50 | return fs.writeFile(path.join(BUILD_DIR, "boreDOM.js"), atob(boredom));
51 | }
52 |
53 | async function processComponents() {
54 | let components = {};
55 |
56 | if (options.html) {
57 | const htmlFolder = path.resolve(options.html);
58 | const htmlFiles = glob.sync("**/*.html", { cwd: htmlFolder });
59 | for (const file of htmlFiles) {
60 | const filePath = path.join(htmlFolder, file);
61 | const content = await fs.readFile(filePath, "utf-8");
62 | const $ = cheerio.load(content, { decodeEntities: false });
63 | const template = $("template[data-component]");
64 | if (template.length) {
65 | const componentName = template.attr("data-component");
66 | const fullTemplate = $.html(template);
67 |
68 | // Create a dedicated folder for this component
69 | const componentBuildDir = path.join(
70 | BUILD_DIR,
71 | "components",
72 | componentName,
73 | );
74 | await fs.ensureDir(componentBuildDir);
75 |
76 | // Copy the HTML file into the component folder
77 | const destHtmlPath = path.join(
78 | componentBuildDir,
79 | `${componentName}.html`,
80 | );
81 | await fs.copy(filePath, destHtmlPath);
82 | console.log(`Copied ${componentName}.html to ${componentBuildDir}`);
83 |
84 | // Look for corresponding JS and CSS files (even in subfolders)
85 | const componentDir = path.dirname(filePath);
86 | const jsMatch = glob.sync(`**/${componentName}.js`, {
87 | cwd: componentDir,
88 | });
89 | const cssMatch = glob.sync(`**/${componentName}.css`, {
90 | cwd: componentDir,
91 | });
92 |
93 | const hasJS = jsMatch.length > 0;
94 | if (jsMatch.length > 0) {
95 | const jsSrc = path.join(componentDir, jsMatch[0]);
96 | const destJsPath = path.join(
97 | componentBuildDir,
98 | `${componentName}.js`,
99 | );
100 | await fs.copy(jsSrc, destJsPath);
101 | console.log(`Copied ${componentName}.js to ${componentBuildDir}`);
102 | }
103 | const hasCSS = cssMatch.length > 0;
104 | if (cssMatch.length > 0) {
105 | const cssSrc = path.join(componentDir, cssMatch[0]);
106 | const destCssPath = path.join(
107 | componentBuildDir,
108 | `${componentName}.css`,
109 | );
110 | await fs.copy(cssSrc, destCssPath);
111 | console.log(`Copied ${componentName}.css to ${componentBuildDir}`);
112 | }
113 |
114 | components[componentName] = {
115 | templateTag: fullTemplate,
116 | hasJS,
117 | hasCSS,
118 | };
119 | }
120 | }
121 | }
122 | return components;
123 | }
124 |
125 | async function updateIndex(components) {
126 | console.log(
127 | "UPDATING WITH COMPONENTS:\n\n",
128 | JSON.stringify(components, null, 2),
129 | );
130 | const indexPath = path.resolve(options.index);
131 | let indexContent = await fs.readFile(indexPath, "utf-8");
132 | const $ = cheerio.load(indexContent, { decodeEntities: false });
133 | $("body").append(`\n `);
134 |
135 | // For each component, add references to its JS/CSS files and inject its full tag
136 | Object.keys(components).forEach((component) => {
137 | if (
138 | components[component].hasJS &&
139 | $(`script[src="components/${component}/${component}.js"]`).length === 0
140 | ) {
141 | $("body").append(
142 | `\n `,
143 | );
144 | console.log(`Added script reference for ${component}`);
145 | }
146 | if (
147 | components[component].hasCSS &&
148 | $(`link[href="components/${component}/${component}.css"]`).length === 0
149 | ) {
150 | $("head").append(
151 | `\n `,
152 | );
153 | console.log(`Added stylesheet reference for ${component}`);
154 | }
155 | if ($(`template[data-component="${component}"]`).length === 0) {
156 | $("body").append(`\n ${components[component].templateTag}`);
157 | console.log(`Injected template for ${component}`);
158 | }
159 | });
160 |
161 | // Remove any tags that no longer correspond to a component file
162 | $("template[data-component]").each((i, el) => {
163 | const comp = $(el).attr("data-component");
164 | if (!components[comp]) {
165 | $(el).remove();
166 | console.log(`Removed unused template for ${comp}`);
167 | }
168 | });
169 |
170 | // Pretty print the final HTML using js-beautify
171 | const prettyHtml = beautify($.html(), {
172 | indent_size: 2,
173 | space_in_empty_paren: true,
174 | });
175 | const buildIndex = path.join(BUILD_DIR, "index.html");
176 | await fs.outputFile(buildIndex, prettyHtml);
177 | console.log("Index updated with pretty printed HTML.");
178 | }
179 |
180 | function startServer() {
181 | if (serverStarted) return;
182 | const server = http.createServer((req, res) => {
183 | return handler(req, res, {
184 | cleanUrls: true,
185 | public: path.resolve(BUILD_DIR),
186 | });
187 | });
188 | const port = process.env.PORT || 8080;
189 | server.listen(port, () => {
190 | console.log(`Server running at http://localhost:${port}`);
191 | });
192 | serverStarted = true;
193 | }
194 |
195 | async function build() {
196 | console.log("Starting build process...");
197 | // Clean the build directory
198 | await fs.remove(BUILD_DIR);
199 | await fs.ensureDir(BUILD_DIR);
200 |
201 | // Run build steps
202 | await copyStatic();
203 | await copyBoreDOM();
204 | const components = await processComponents();
205 | await updateIndex(components);
206 | console.log("Build process complete.");
207 | }
208 |
209 | async function watchFiles() {
210 | const pathsToWatch = [];
211 |
212 | // Watch the index file
213 | if (options.index) {
214 | pathsToWatch.push(path.resolve(options.index));
215 | }
216 | // Watch the components source folder (including all HTML, JS, CSS, etc.)
217 | if (options.html) {
218 | pathsToWatch.push(path.resolve(options.html));
219 | }
220 | // Watch the static folder if it exists
221 | const staticDir = path.join(process.cwd(), "static");
222 | if (await fs.pathExists(staticDir)) {
223 | pathsToWatch.push(staticDir);
224 | }
225 |
226 | console.log("Watching for file changes in:", pathsToWatch);
227 | // chokidar will recursively watch all files in the specified paths
228 | const watcher = chokidar.watch(pathsToWatch, { ignoreInitial: true });
229 | let rebuildTimeout;
230 | watcher.on("all", (event, filePath) => {
231 | console.log(`Detected ${event} on ${filePath}. Scheduling rebuild...`);
232 | if (rebuildTimeout) clearTimeout(rebuildTimeout);
233 | // Debounce rebuilds in case multiple file events fire together
234 | rebuildTimeout = setTimeout(() => {
235 | build().then(() => {
236 | console.log(
237 | `#${++numberOfRefreshes} - ${
238 | (new Date()).toISOString()
239 | } - Build refreshed.`,
240 | );
241 | }).catch((err) => console.error("Error during rebuild:", err));
242 | }, 100);
243 | });
244 | }
245 |
246 | async function main() {
247 | await build();
248 | startServer();
249 | await watchFiles();
250 | }
251 |
252 | main().catch((err) => {
253 | console.error(err);
254 | process.exit(1);
255 | });
256 |
--------------------------------------------------------------------------------
/boreDOMCLI/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "boreDOM",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "bin": {
7 | "boreDOM": "./index.js"
8 | },
9 | "scripts": {
10 | "test": "echo \"Error: no test specified\" && exit 1"
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "CC0",
15 | "dependencies": {
16 | "cheerio": "^1.0.0",
17 | "chokidar": "^4.0.3",
18 | "commander": "^13.0.0",
19 | "finalhandler": "^2.1.0",
20 | "fs-extra": "^11.3.0",
21 | "glob": "^11.0.1",
22 | "js-beautify": "^1.15.4",
23 | "jsdom": "^26.0.0",
24 | "jsom": "^1.0.0",
25 | "pretty": "^2.0.0",
26 | "serve-handler": "^6.1.6"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/boreDOMCLI/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 |
9 | .:
10 | dependencies:
11 | cheerio:
12 | specifier: ^1.0.0
13 | version: 1.0.0
14 | chokidar:
15 | specifier: ^4.0.3
16 | version: 4.0.3
17 | commander:
18 | specifier: ^13.0.0
19 | version: 13.0.0
20 | finalhandler:
21 | specifier: ^2.1.0
22 | version: 2.1.0
23 | fs-extra:
24 | specifier: ^11.3.0
25 | version: 11.3.0
26 | glob:
27 | specifier: ^11.0.1
28 | version: 11.0.1
29 | js-beautify:
30 | specifier: ^1.15.4
31 | version: 1.15.4
32 | jsdom:
33 | specifier: ^26.0.0
34 | version: 26.0.0
35 | jsom:
36 | specifier: ^1.0.0
37 | version: 1.0.0
38 | pretty:
39 | specifier: ^2.0.0
40 | version: 2.0.0
41 | serve-handler:
42 | specifier: ^6.1.6
43 | version: 6.1.6
44 |
45 | packages:
46 |
47 | '@asamuzakjp/css-color@2.8.2':
48 | resolution: {integrity: sha512-RtWv9jFN2/bLExuZgFFZ0I3pWWeezAHGgrmjqGGWclATl1aDe3yhCUaI0Ilkp6OCk9zX7+FjvDasEX8Q9Rxc5w==}
49 |
50 | '@csstools/color-helpers@5.0.1':
51 | resolution: {integrity: sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==}
52 | engines: {node: '>=18'}
53 |
54 | '@csstools/css-calc@2.1.1':
55 | resolution: {integrity: sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==}
56 | engines: {node: '>=18'}
57 | peerDependencies:
58 | '@csstools/css-parser-algorithms': ^3.0.4
59 | '@csstools/css-tokenizer': ^3.0.3
60 |
61 | '@csstools/css-color-parser@3.0.7':
62 | resolution: {integrity: sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==}
63 | engines: {node: '>=18'}
64 | peerDependencies:
65 | '@csstools/css-parser-algorithms': ^3.0.4
66 | '@csstools/css-tokenizer': ^3.0.3
67 |
68 | '@csstools/css-parser-algorithms@3.0.4':
69 | resolution: {integrity: sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==}
70 | engines: {node: '>=18'}
71 | peerDependencies:
72 | '@csstools/css-tokenizer': ^3.0.3
73 |
74 | '@csstools/css-tokenizer@3.0.3':
75 | resolution: {integrity: sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==}
76 | engines: {node: '>=18'}
77 |
78 | '@isaacs/cliui@8.0.2':
79 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
80 | engines: {node: '>=12'}
81 |
82 | '@one-ini/wasm@0.1.1':
83 | resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
84 |
85 | '@pkgjs/parseargs@0.11.0':
86 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
87 | engines: {node: '>=14'}
88 |
89 | '@types/node@7.10.14':
90 | resolution: {integrity: sha512-29GS75BE8asnTno3yB6ubOJOO0FboExEqNJy4bpz0GSmW/8wPTNL4h9h63c6s1uTrOopCmJYe/4yJLh5r92ZUA==}
91 |
92 | abbrev@2.0.0:
93 | resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
94 | engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
95 |
96 | agent-base@7.1.3:
97 | resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==}
98 | engines: {node: '>= 14'}
99 |
100 | ansi-regex@5.0.1:
101 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
102 | engines: {node: '>=8'}
103 |
104 | ansi-regex@6.1.0:
105 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==}
106 | engines: {node: '>=12'}
107 |
108 | ansi-styles@4.3.0:
109 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
110 | engines: {node: '>=8'}
111 |
112 | ansi-styles@6.2.1:
113 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
114 | engines: {node: '>=12'}
115 |
116 | asynckit@0.4.0:
117 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
118 |
119 | balanced-match@1.0.2:
120 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
121 |
122 | boolbase@1.0.0:
123 | resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
124 |
125 | brace-expansion@1.1.11:
126 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
127 |
128 | brace-expansion@2.0.1:
129 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
130 |
131 | bytes@3.0.0:
132 | resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
133 | engines: {node: '>= 0.8'}
134 |
135 | cheerio-select@2.1.0:
136 | resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
137 |
138 | cheerio@1.0.0:
139 | resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==}
140 | engines: {node: '>=18.17'}
141 |
142 | chokidar@4.0.3:
143 | resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
144 | engines: {node: '>= 14.16.0'}
145 |
146 | color-convert@2.0.1:
147 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
148 | engines: {node: '>=7.0.0'}
149 |
150 | color-name@1.1.4:
151 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
152 |
153 | combined-stream@1.0.8:
154 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
155 | engines: {node: '>= 0.8'}
156 |
157 | commander@10.0.1:
158 | resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
159 | engines: {node: '>=14'}
160 |
161 | commander@13.0.0:
162 | resolution: {integrity: sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==}
163 | engines: {node: '>=18'}
164 |
165 | concat-map@0.0.1:
166 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
167 |
168 | condense-newlines@0.2.1:
169 | resolution: {integrity: sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==}
170 | engines: {node: '>=0.10.0'}
171 |
172 | config-chain@1.1.13:
173 | resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
174 |
175 | content-disposition@0.5.2:
176 | resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==}
177 | engines: {node: '>= 0.6'}
178 |
179 | cross-spawn@7.0.6:
180 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
181 | engines: {node: '>= 8'}
182 |
183 | css-select@5.1.0:
184 | resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
185 |
186 | css-what@6.1.0:
187 | resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
188 | engines: {node: '>= 6'}
189 |
190 | cssstyle@4.2.1:
191 | resolution: {integrity: sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==}
192 | engines: {node: '>=18'}
193 |
194 | data-urls@5.0.0:
195 | resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
196 | engines: {node: '>=18'}
197 |
198 | debug@2.6.9:
199 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
200 | peerDependencies:
201 | supports-color: '*'
202 | peerDependenciesMeta:
203 | supports-color:
204 | optional: true
205 |
206 | debug@4.4.0:
207 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==}
208 | engines: {node: '>=6.0'}
209 | peerDependencies:
210 | supports-color: '*'
211 | peerDependenciesMeta:
212 | supports-color:
213 | optional: true
214 |
215 | decimal.js@10.4.3:
216 | resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
217 |
218 | delayed-stream@1.0.0:
219 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
220 | engines: {node: '>=0.4.0'}
221 |
222 | dom-serializer@2.0.0:
223 | resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
224 |
225 | domelementtype@2.3.0:
226 | resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
227 |
228 | domhandler@5.0.3:
229 | resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
230 | engines: {node: '>= 4'}
231 |
232 | domutils@3.2.2:
233 | resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
234 |
235 | eastasianwidth@0.2.0:
236 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
237 |
238 | editorconfig@1.0.4:
239 | resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
240 | engines: {node: '>=14'}
241 | hasBin: true
242 |
243 | ee-first@1.1.1:
244 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
245 |
246 | emoji-regex@8.0.0:
247 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
248 |
249 | emoji-regex@9.2.2:
250 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
251 |
252 | encodeurl@2.0.0:
253 | resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
254 | engines: {node: '>= 0.8'}
255 |
256 | encoding-sniffer@0.2.0:
257 | resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==}
258 |
259 | entities@4.5.0:
260 | resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
261 | engines: {node: '>=0.12'}
262 |
263 | escape-html@1.0.3:
264 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
265 |
266 | extend-shallow@2.0.1:
267 | resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
268 | engines: {node: '>=0.10.0'}
269 |
270 | finalhandler@2.1.0:
271 | resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
272 | engines: {node: '>= 0.8'}
273 |
274 | foreground-child@3.3.0:
275 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
276 | engines: {node: '>=14'}
277 |
278 | form-data@4.0.1:
279 | resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
280 | engines: {node: '>= 6'}
281 |
282 | fs-extra@11.3.0:
283 | resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==}
284 | engines: {node: '>=14.14'}
285 |
286 | glob@10.4.5:
287 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
288 | hasBin: true
289 |
290 | glob@11.0.1:
291 | resolution: {integrity: sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==}
292 | engines: {node: 20 || >=22}
293 | hasBin: true
294 |
295 | graceful-fs@4.2.11:
296 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
297 |
298 | html-encoding-sniffer@4.0.0:
299 | resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
300 | engines: {node: '>=18'}
301 |
302 | htmlparser2@9.1.0:
303 | resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==}
304 |
305 | http-proxy-agent@7.0.2:
306 | resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
307 | engines: {node: '>= 14'}
308 |
309 | https-proxy-agent@7.0.6:
310 | resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
311 | engines: {node: '>= 14'}
312 |
313 | iconv-lite@0.6.3:
314 | resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
315 | engines: {node: '>=0.10.0'}
316 |
317 | ini@1.3.8:
318 | resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
319 |
320 | is-buffer@1.1.6:
321 | resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
322 |
323 | is-extendable@0.1.1:
324 | resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
325 | engines: {node: '>=0.10.0'}
326 |
327 | is-fullwidth-code-point@3.0.0:
328 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
329 | engines: {node: '>=8'}
330 |
331 | is-potential-custom-element-name@1.0.1:
332 | resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
333 |
334 | is-whitespace@0.3.0:
335 | resolution: {integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==}
336 | engines: {node: '>=0.10.0'}
337 |
338 | isexe@2.0.0:
339 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
340 |
341 | jackspeak@3.4.3:
342 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
343 |
344 | jackspeak@4.1.0:
345 | resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==}
346 | engines: {node: 20 || >=22}
347 |
348 | js-beautify@1.15.4:
349 | resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==}
350 | engines: {node: '>=14'}
351 | hasBin: true
352 |
353 | js-cookie@3.0.5:
354 | resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
355 | engines: {node: '>=14'}
356 |
357 | jsdom@26.0.0:
358 | resolution: {integrity: sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==}
359 | engines: {node: '>=18'}
360 | peerDependencies:
361 | canvas: ^3.0.0
362 | peerDependenciesMeta:
363 | canvas:
364 | optional: true
365 |
366 | jsom@1.0.0:
367 | resolution: {integrity: sha512-sBqpZRsRz1Sm+Xw61e2zAxfjFRJ2Hkathh5HMkRJO+6bK5aNlGjwyNv3gbq5zjDIzLnzIqdqVXaA6UE0XpMq5g==}
368 |
369 | jsonfile@6.1.0:
370 | resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
371 |
372 | kind-of@3.2.2:
373 | resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
374 | engines: {node: '>=0.10.0'}
375 |
376 | lru-cache@10.4.3:
377 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
378 |
379 | lru-cache@11.0.2:
380 | resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==}
381 | engines: {node: 20 || >=22}
382 |
383 | mime-db@1.33.0:
384 | resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==}
385 | engines: {node: '>= 0.6'}
386 |
387 | mime-db@1.52.0:
388 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
389 | engines: {node: '>= 0.6'}
390 |
391 | mime-types@2.1.18:
392 | resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==}
393 | engines: {node: '>= 0.6'}
394 |
395 | mime-types@2.1.35:
396 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
397 | engines: {node: '>= 0.6'}
398 |
399 | minimatch@10.0.1:
400 | resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==}
401 | engines: {node: 20 || >=22}
402 |
403 | minimatch@3.1.2:
404 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
405 |
406 | minimatch@9.0.1:
407 | resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==}
408 | engines: {node: '>=16 || 14 >=14.17'}
409 |
410 | minimatch@9.0.5:
411 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
412 | engines: {node: '>=16 || 14 >=14.17'}
413 |
414 | minipass@7.1.2:
415 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
416 | engines: {node: '>=16 || 14 >=14.17'}
417 |
418 | ms@2.0.0:
419 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
420 |
421 | ms@2.1.3:
422 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
423 |
424 | nopt@7.2.1:
425 | resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==}
426 | engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
427 | hasBin: true
428 |
429 | nth-check@2.1.1:
430 | resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
431 |
432 | nwsapi@2.2.16:
433 | resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==}
434 |
435 | on-finished@2.4.1:
436 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
437 | engines: {node: '>= 0.8'}
438 |
439 | package-json-from-dist@1.0.1:
440 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
441 |
442 | parse5-htmlparser2-tree-adapter@7.1.0:
443 | resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==}
444 |
445 | parse5-parser-stream@7.1.2:
446 | resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==}
447 |
448 | parse5@7.2.1:
449 | resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==}
450 |
451 | parseurl@1.3.3:
452 | resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
453 | engines: {node: '>= 0.8'}
454 |
455 | path-is-inside@1.0.2:
456 | resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==}
457 |
458 | path-key@3.1.1:
459 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
460 | engines: {node: '>=8'}
461 |
462 | path-scurry@1.11.1:
463 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
464 | engines: {node: '>=16 || 14 >=14.18'}
465 |
466 | path-scurry@2.0.0:
467 | resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==}
468 | engines: {node: 20 || >=22}
469 |
470 | path-to-regexp@3.3.0:
471 | resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==}
472 |
473 | pretty@2.0.0:
474 | resolution: {integrity: sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==}
475 | engines: {node: '>=0.10.0'}
476 |
477 | proto-list@1.2.4:
478 | resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
479 |
480 | punycode@2.3.1:
481 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
482 | engines: {node: '>=6'}
483 |
484 | range-parser@1.2.0:
485 | resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==}
486 | engines: {node: '>= 0.6'}
487 |
488 | readdirp@4.1.2:
489 | resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
490 | engines: {node: '>= 14.18.0'}
491 |
492 | rrweb-cssom@0.8.0:
493 | resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
494 |
495 | safer-buffer@2.1.2:
496 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
497 |
498 | saxes@6.0.0:
499 | resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
500 | engines: {node: '>=v12.22.7'}
501 |
502 | semver@7.6.3:
503 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
504 | engines: {node: '>=10'}
505 | hasBin: true
506 |
507 | serve-handler@6.1.6:
508 | resolution: {integrity: sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==}
509 |
510 | shebang-command@2.0.0:
511 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
512 | engines: {node: '>=8'}
513 |
514 | shebang-regex@3.0.0:
515 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
516 | engines: {node: '>=8'}
517 |
518 | signal-exit@4.1.0:
519 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
520 | engines: {node: '>=14'}
521 |
522 | statuses@2.0.1:
523 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
524 | engines: {node: '>= 0.8'}
525 |
526 | string-width@4.2.3:
527 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
528 | engines: {node: '>=8'}
529 |
530 | string-width@5.1.2:
531 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
532 | engines: {node: '>=12'}
533 |
534 | strip-ansi@6.0.1:
535 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
536 | engines: {node: '>=8'}
537 |
538 | strip-ansi@7.1.0:
539 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
540 | engines: {node: '>=12'}
541 |
542 | switchjs@1.0.2:
543 | resolution: {integrity: sha512-7Z+bxBEuXNsECoSzlwDEfbxJV3svPklx1Uj7rP7MFGdhXdhxlQuEQiVL4u33Ej/TEVEslUiXkgmLIlHty//AZA==}
544 |
545 | symbol-tree@3.2.4:
546 | resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
547 |
548 | tldts-core@6.1.71:
549 | resolution: {integrity: sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg==}
550 |
551 | tldts@6.1.71:
552 | resolution: {integrity: sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==}
553 | hasBin: true
554 |
555 | tough-cookie@5.1.0:
556 | resolution: {integrity: sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==}
557 | engines: {node: '>=16'}
558 |
559 | tr46@5.0.0:
560 | resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
561 | engines: {node: '>=18'}
562 |
563 | tslib@1.14.1:
564 | resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
565 |
566 | undici@6.21.1:
567 | resolution: {integrity: sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==}
568 | engines: {node: '>=18.17'}
569 |
570 | universalify@2.0.1:
571 | resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
572 | engines: {node: '>= 10.0.0'}
573 |
574 | w3c-xmlserializer@5.0.0:
575 | resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
576 | engines: {node: '>=18'}
577 |
578 | webidl-conversions@7.0.0:
579 | resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
580 | engines: {node: '>=12'}
581 |
582 | whatwg-encoding@3.1.1:
583 | resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
584 | engines: {node: '>=18'}
585 |
586 | whatwg-mimetype@4.0.0:
587 | resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
588 | engines: {node: '>=18'}
589 |
590 | whatwg-url@14.1.0:
591 | resolution: {integrity: sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==}
592 | engines: {node: '>=18'}
593 |
594 | which@2.0.2:
595 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
596 | engines: {node: '>= 8'}
597 | hasBin: true
598 |
599 | wrap-ansi@7.0.0:
600 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
601 | engines: {node: '>=10'}
602 |
603 | wrap-ansi@8.1.0:
604 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
605 | engines: {node: '>=12'}
606 |
607 | ws@8.18.0:
608 | resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
609 | engines: {node: '>=10.0.0'}
610 | peerDependencies:
611 | bufferutil: ^4.0.1
612 | utf-8-validate: '>=5.0.2'
613 | peerDependenciesMeta:
614 | bufferutil:
615 | optional: true
616 | utf-8-validate:
617 | optional: true
618 |
619 | xml-name-validator@5.0.0:
620 | resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
621 | engines: {node: '>=18'}
622 |
623 | xmlchars@2.2.0:
624 | resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
625 |
626 | snapshots:
627 |
628 | '@asamuzakjp/css-color@2.8.2':
629 | dependencies:
630 | '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
631 | '@csstools/css-color-parser': 3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
632 | '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
633 | '@csstools/css-tokenizer': 3.0.3
634 | lru-cache: 11.0.2
635 |
636 | '@csstools/color-helpers@5.0.1': {}
637 |
638 | '@csstools/css-calc@2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
639 | dependencies:
640 | '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
641 | '@csstools/css-tokenizer': 3.0.3
642 |
643 | '@csstools/css-color-parser@3.0.7(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)':
644 | dependencies:
645 | '@csstools/color-helpers': 5.0.1
646 | '@csstools/css-calc': 2.1.1(@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3))(@csstools/css-tokenizer@3.0.3)
647 | '@csstools/css-parser-algorithms': 3.0.4(@csstools/css-tokenizer@3.0.3)
648 | '@csstools/css-tokenizer': 3.0.3
649 |
650 | '@csstools/css-parser-algorithms@3.0.4(@csstools/css-tokenizer@3.0.3)':
651 | dependencies:
652 | '@csstools/css-tokenizer': 3.0.3
653 |
654 | '@csstools/css-tokenizer@3.0.3': {}
655 |
656 | '@isaacs/cliui@8.0.2':
657 | dependencies:
658 | string-width: 5.1.2
659 | string-width-cjs: string-width@4.2.3
660 | strip-ansi: 7.1.0
661 | strip-ansi-cjs: strip-ansi@6.0.1
662 | wrap-ansi: 8.1.0
663 | wrap-ansi-cjs: wrap-ansi@7.0.0
664 |
665 | '@one-ini/wasm@0.1.1': {}
666 |
667 | '@pkgjs/parseargs@0.11.0':
668 | optional: true
669 |
670 | '@types/node@7.10.14': {}
671 |
672 | abbrev@2.0.0: {}
673 |
674 | agent-base@7.1.3: {}
675 |
676 | ansi-regex@5.0.1: {}
677 |
678 | ansi-regex@6.1.0: {}
679 |
680 | ansi-styles@4.3.0:
681 | dependencies:
682 | color-convert: 2.0.1
683 |
684 | ansi-styles@6.2.1: {}
685 |
686 | asynckit@0.4.0: {}
687 |
688 | balanced-match@1.0.2: {}
689 |
690 | boolbase@1.0.0: {}
691 |
692 | brace-expansion@1.1.11:
693 | dependencies:
694 | balanced-match: 1.0.2
695 | concat-map: 0.0.1
696 |
697 | brace-expansion@2.0.1:
698 | dependencies:
699 | balanced-match: 1.0.2
700 |
701 | bytes@3.0.0: {}
702 |
703 | cheerio-select@2.1.0:
704 | dependencies:
705 | boolbase: 1.0.0
706 | css-select: 5.1.0
707 | css-what: 6.1.0
708 | domelementtype: 2.3.0
709 | domhandler: 5.0.3
710 | domutils: 3.2.2
711 |
712 | cheerio@1.0.0:
713 | dependencies:
714 | cheerio-select: 2.1.0
715 | dom-serializer: 2.0.0
716 | domhandler: 5.0.3
717 | domutils: 3.2.2
718 | encoding-sniffer: 0.2.0
719 | htmlparser2: 9.1.0
720 | parse5: 7.2.1
721 | parse5-htmlparser2-tree-adapter: 7.1.0
722 | parse5-parser-stream: 7.1.2
723 | undici: 6.21.1
724 | whatwg-mimetype: 4.0.0
725 |
726 | chokidar@4.0.3:
727 | dependencies:
728 | readdirp: 4.1.2
729 |
730 | color-convert@2.0.1:
731 | dependencies:
732 | color-name: 1.1.4
733 |
734 | color-name@1.1.4: {}
735 |
736 | combined-stream@1.0.8:
737 | dependencies:
738 | delayed-stream: 1.0.0
739 |
740 | commander@10.0.1: {}
741 |
742 | commander@13.0.0: {}
743 |
744 | concat-map@0.0.1: {}
745 |
746 | condense-newlines@0.2.1:
747 | dependencies:
748 | extend-shallow: 2.0.1
749 | is-whitespace: 0.3.0
750 | kind-of: 3.2.2
751 |
752 | config-chain@1.1.13:
753 | dependencies:
754 | ini: 1.3.8
755 | proto-list: 1.2.4
756 |
757 | content-disposition@0.5.2: {}
758 |
759 | cross-spawn@7.0.6:
760 | dependencies:
761 | path-key: 3.1.1
762 | shebang-command: 2.0.0
763 | which: 2.0.2
764 |
765 | css-select@5.1.0:
766 | dependencies:
767 | boolbase: 1.0.0
768 | css-what: 6.1.0
769 | domhandler: 5.0.3
770 | domutils: 3.2.2
771 | nth-check: 2.1.1
772 |
773 | css-what@6.1.0: {}
774 |
775 | cssstyle@4.2.1:
776 | dependencies:
777 | '@asamuzakjp/css-color': 2.8.2
778 | rrweb-cssom: 0.8.0
779 |
780 | data-urls@5.0.0:
781 | dependencies:
782 | whatwg-mimetype: 4.0.0
783 | whatwg-url: 14.1.0
784 |
785 | debug@2.6.9:
786 | dependencies:
787 | ms: 2.0.0
788 |
789 | debug@4.4.0:
790 | dependencies:
791 | ms: 2.1.3
792 |
793 | decimal.js@10.4.3: {}
794 |
795 | delayed-stream@1.0.0: {}
796 |
797 | dom-serializer@2.0.0:
798 | dependencies:
799 | domelementtype: 2.3.0
800 | domhandler: 5.0.3
801 | entities: 4.5.0
802 |
803 | domelementtype@2.3.0: {}
804 |
805 | domhandler@5.0.3:
806 | dependencies:
807 | domelementtype: 2.3.0
808 |
809 | domutils@3.2.2:
810 | dependencies:
811 | dom-serializer: 2.0.0
812 | domelementtype: 2.3.0
813 | domhandler: 5.0.3
814 |
815 | eastasianwidth@0.2.0: {}
816 |
817 | editorconfig@1.0.4:
818 | dependencies:
819 | '@one-ini/wasm': 0.1.1
820 | commander: 10.0.1
821 | minimatch: 9.0.1
822 | semver: 7.6.3
823 |
824 | ee-first@1.1.1: {}
825 |
826 | emoji-regex@8.0.0: {}
827 |
828 | emoji-regex@9.2.2: {}
829 |
830 | encodeurl@2.0.0: {}
831 |
832 | encoding-sniffer@0.2.0:
833 | dependencies:
834 | iconv-lite: 0.6.3
835 | whatwg-encoding: 3.1.1
836 |
837 | entities@4.5.0: {}
838 |
839 | escape-html@1.0.3: {}
840 |
841 | extend-shallow@2.0.1:
842 | dependencies:
843 | is-extendable: 0.1.1
844 |
845 | finalhandler@2.1.0:
846 | dependencies:
847 | debug: 4.4.0
848 | encodeurl: 2.0.0
849 | escape-html: 1.0.3
850 | on-finished: 2.4.1
851 | parseurl: 1.3.3
852 | statuses: 2.0.1
853 | transitivePeerDependencies:
854 | - supports-color
855 |
856 | foreground-child@3.3.0:
857 | dependencies:
858 | cross-spawn: 7.0.6
859 | signal-exit: 4.1.0
860 |
861 | form-data@4.0.1:
862 | dependencies:
863 | asynckit: 0.4.0
864 | combined-stream: 1.0.8
865 | mime-types: 2.1.35
866 |
867 | fs-extra@11.3.0:
868 | dependencies:
869 | graceful-fs: 4.2.11
870 | jsonfile: 6.1.0
871 | universalify: 2.0.1
872 |
873 | glob@10.4.5:
874 | dependencies:
875 | foreground-child: 3.3.0
876 | jackspeak: 3.4.3
877 | minimatch: 9.0.5
878 | minipass: 7.1.2
879 | package-json-from-dist: 1.0.1
880 | path-scurry: 1.11.1
881 |
882 | glob@11.0.1:
883 | dependencies:
884 | foreground-child: 3.3.0
885 | jackspeak: 4.1.0
886 | minimatch: 10.0.1
887 | minipass: 7.1.2
888 | package-json-from-dist: 1.0.1
889 | path-scurry: 2.0.0
890 |
891 | graceful-fs@4.2.11: {}
892 |
893 | html-encoding-sniffer@4.0.0:
894 | dependencies:
895 | whatwg-encoding: 3.1.1
896 |
897 | htmlparser2@9.1.0:
898 | dependencies:
899 | domelementtype: 2.3.0
900 | domhandler: 5.0.3
901 | domutils: 3.2.2
902 | entities: 4.5.0
903 |
904 | http-proxy-agent@7.0.2:
905 | dependencies:
906 | agent-base: 7.1.3
907 | debug: 4.4.0
908 | transitivePeerDependencies:
909 | - supports-color
910 |
911 | https-proxy-agent@7.0.6:
912 | dependencies:
913 | agent-base: 7.1.3
914 | debug: 4.4.0
915 | transitivePeerDependencies:
916 | - supports-color
917 |
918 | iconv-lite@0.6.3:
919 | dependencies:
920 | safer-buffer: 2.1.2
921 |
922 | ini@1.3.8: {}
923 |
924 | is-buffer@1.1.6: {}
925 |
926 | is-extendable@0.1.1: {}
927 |
928 | is-fullwidth-code-point@3.0.0: {}
929 |
930 | is-potential-custom-element-name@1.0.1: {}
931 |
932 | is-whitespace@0.3.0: {}
933 |
934 | isexe@2.0.0: {}
935 |
936 | jackspeak@3.4.3:
937 | dependencies:
938 | '@isaacs/cliui': 8.0.2
939 | optionalDependencies:
940 | '@pkgjs/parseargs': 0.11.0
941 |
942 | jackspeak@4.1.0:
943 | dependencies:
944 | '@isaacs/cliui': 8.0.2
945 |
946 | js-beautify@1.15.4:
947 | dependencies:
948 | config-chain: 1.1.13
949 | editorconfig: 1.0.4
950 | glob: 10.4.5
951 | js-cookie: 3.0.5
952 | nopt: 7.2.1
953 |
954 | js-cookie@3.0.5: {}
955 |
956 | jsdom@26.0.0:
957 | dependencies:
958 | cssstyle: 4.2.1
959 | data-urls: 5.0.0
960 | decimal.js: 10.4.3
961 | form-data: 4.0.1
962 | html-encoding-sniffer: 4.0.0
963 | http-proxy-agent: 7.0.2
964 | https-proxy-agent: 7.0.6
965 | is-potential-custom-element-name: 1.0.1
966 | nwsapi: 2.2.16
967 | parse5: 7.2.1
968 | rrweb-cssom: 0.8.0
969 | saxes: 6.0.0
970 | symbol-tree: 3.2.4
971 | tough-cookie: 5.1.0
972 | w3c-xmlserializer: 5.0.0
973 | webidl-conversions: 7.0.0
974 | whatwg-encoding: 3.1.1
975 | whatwg-mimetype: 4.0.0
976 | whatwg-url: 14.1.0
977 | ws: 8.18.0
978 | xml-name-validator: 5.0.0
979 | transitivePeerDependencies:
980 | - bufferutil
981 | - supports-color
982 | - utf-8-validate
983 |
984 | jsom@1.0.0:
985 | dependencies:
986 | '@types/node': 7.10.14
987 | switchjs: 1.0.2
988 | tslib: 1.14.1
989 | transitivePeerDependencies:
990 | - supports-color
991 |
992 | jsonfile@6.1.0:
993 | dependencies:
994 | universalify: 2.0.1
995 | optionalDependencies:
996 | graceful-fs: 4.2.11
997 |
998 | kind-of@3.2.2:
999 | dependencies:
1000 | is-buffer: 1.1.6
1001 |
1002 | lru-cache@10.4.3: {}
1003 |
1004 | lru-cache@11.0.2: {}
1005 |
1006 | mime-db@1.33.0: {}
1007 |
1008 | mime-db@1.52.0: {}
1009 |
1010 | mime-types@2.1.18:
1011 | dependencies:
1012 | mime-db: 1.33.0
1013 |
1014 | mime-types@2.1.35:
1015 | dependencies:
1016 | mime-db: 1.52.0
1017 |
1018 | minimatch@10.0.1:
1019 | dependencies:
1020 | brace-expansion: 2.0.1
1021 |
1022 | minimatch@3.1.2:
1023 | dependencies:
1024 | brace-expansion: 1.1.11
1025 |
1026 | minimatch@9.0.1:
1027 | dependencies:
1028 | brace-expansion: 2.0.1
1029 |
1030 | minimatch@9.0.5:
1031 | dependencies:
1032 | brace-expansion: 2.0.1
1033 |
1034 | minipass@7.1.2: {}
1035 |
1036 | ms@2.0.0: {}
1037 |
1038 | ms@2.1.3: {}
1039 |
1040 | nopt@7.2.1:
1041 | dependencies:
1042 | abbrev: 2.0.0
1043 |
1044 | nth-check@2.1.1:
1045 | dependencies:
1046 | boolbase: 1.0.0
1047 |
1048 | nwsapi@2.2.16: {}
1049 |
1050 | on-finished@2.4.1:
1051 | dependencies:
1052 | ee-first: 1.1.1
1053 |
1054 | package-json-from-dist@1.0.1: {}
1055 |
1056 | parse5-htmlparser2-tree-adapter@7.1.0:
1057 | dependencies:
1058 | domhandler: 5.0.3
1059 | parse5: 7.2.1
1060 |
1061 | parse5-parser-stream@7.1.2:
1062 | dependencies:
1063 | parse5: 7.2.1
1064 |
1065 | parse5@7.2.1:
1066 | dependencies:
1067 | entities: 4.5.0
1068 |
1069 | parseurl@1.3.3: {}
1070 |
1071 | path-is-inside@1.0.2: {}
1072 |
1073 | path-key@3.1.1: {}
1074 |
1075 | path-scurry@1.11.1:
1076 | dependencies:
1077 | lru-cache: 10.4.3
1078 | minipass: 7.1.2
1079 |
1080 | path-scurry@2.0.0:
1081 | dependencies:
1082 | lru-cache: 11.0.2
1083 | minipass: 7.1.2
1084 |
1085 | path-to-regexp@3.3.0: {}
1086 |
1087 | pretty@2.0.0:
1088 | dependencies:
1089 | condense-newlines: 0.2.1
1090 | extend-shallow: 2.0.1
1091 | js-beautify: 1.15.4
1092 |
1093 | proto-list@1.2.4: {}
1094 |
1095 | punycode@2.3.1: {}
1096 |
1097 | range-parser@1.2.0: {}
1098 |
1099 | readdirp@4.1.2: {}
1100 |
1101 | rrweb-cssom@0.8.0: {}
1102 |
1103 | safer-buffer@2.1.2: {}
1104 |
1105 | saxes@6.0.0:
1106 | dependencies:
1107 | xmlchars: 2.2.0
1108 |
1109 | semver@7.6.3: {}
1110 |
1111 | serve-handler@6.1.6:
1112 | dependencies:
1113 | bytes: 3.0.0
1114 | content-disposition: 0.5.2
1115 | mime-types: 2.1.18
1116 | minimatch: 3.1.2
1117 | path-is-inside: 1.0.2
1118 | path-to-regexp: 3.3.0
1119 | range-parser: 1.2.0
1120 |
1121 | shebang-command@2.0.0:
1122 | dependencies:
1123 | shebang-regex: 3.0.0
1124 |
1125 | shebang-regex@3.0.0: {}
1126 |
1127 | signal-exit@4.1.0: {}
1128 |
1129 | statuses@2.0.1: {}
1130 |
1131 | string-width@4.2.3:
1132 | dependencies:
1133 | emoji-regex: 8.0.0
1134 | is-fullwidth-code-point: 3.0.0
1135 | strip-ansi: 6.0.1
1136 |
1137 | string-width@5.1.2:
1138 | dependencies:
1139 | eastasianwidth: 0.2.0
1140 | emoji-regex: 9.2.2
1141 | strip-ansi: 7.1.0
1142 |
1143 | strip-ansi@6.0.1:
1144 | dependencies:
1145 | ansi-regex: 5.0.1
1146 |
1147 | strip-ansi@7.1.0:
1148 | dependencies:
1149 | ansi-regex: 6.1.0
1150 |
1151 | switchjs@1.0.2:
1152 | dependencies:
1153 | debug: 2.6.9
1154 | tslib: 1.14.1
1155 | transitivePeerDependencies:
1156 | - supports-color
1157 |
1158 | symbol-tree@3.2.4: {}
1159 |
1160 | tldts-core@6.1.71: {}
1161 |
1162 | tldts@6.1.71:
1163 | dependencies:
1164 | tldts-core: 6.1.71
1165 |
1166 | tough-cookie@5.1.0:
1167 | dependencies:
1168 | tldts: 6.1.71
1169 |
1170 | tr46@5.0.0:
1171 | dependencies:
1172 | punycode: 2.3.1
1173 |
1174 | tslib@1.14.1: {}
1175 |
1176 | undici@6.21.1: {}
1177 |
1178 | universalify@2.0.1: {}
1179 |
1180 | w3c-xmlserializer@5.0.0:
1181 | dependencies:
1182 | xml-name-validator: 5.0.0
1183 |
1184 | webidl-conversions@7.0.0: {}
1185 |
1186 | whatwg-encoding@3.1.1:
1187 | dependencies:
1188 | iconv-lite: 0.6.3
1189 |
1190 | whatwg-mimetype@4.0.0: {}
1191 |
1192 | whatwg-url@14.1.0:
1193 | dependencies:
1194 | tr46: 5.0.0
1195 | webidl-conversions: 7.0.0
1196 |
1197 | which@2.0.2:
1198 | dependencies:
1199 | isexe: 2.0.0
1200 |
1201 | wrap-ansi@7.0.0:
1202 | dependencies:
1203 | ansi-styles: 4.3.0
1204 | string-width: 4.2.3
1205 | strip-ansi: 6.0.1
1206 |
1207 | wrap-ansi@8.1.0:
1208 | dependencies:
1209 | ansi-styles: 6.2.1
1210 | string-width: 5.1.2
1211 | strip-ansi: 7.1.0
1212 |
1213 | ws@8.18.0: {}
1214 |
1215 | xml-name-validator@5.0.0: {}
1216 |
1217 | xmlchars@2.2.0: {}
1218 |
--------------------------------------------------------------------------------
/dist/boreDOM.d.ts:
--------------------------------------------------------------------------------
1 | // Generated by dts-bundle-generator v9.5.1
2 |
3 | export type WebComponentDetail = {
4 | index: number;
5 | name: string;
6 | data?: any;
7 | };
8 | export type WebComponentInitParams = {
9 | detail: WebComponentDetail;
10 | state: S;
11 | refs: Refs;
12 | self: Bored;
13 | on: (eventName: string, eventHandler: (options: {
14 | state: S | undefined;
15 | e: CustomEvent["detail"];
16 | detail: WebComponentDetail;
17 | }) => void) => void;
18 | };
19 | export type WebComponentRenderParams = {
20 | detail: WebComponentDetail;
21 | state: S;
22 | refs: Refs;
23 | slots: Slots;
24 | self: Bored;
25 | makeComponent: (tag: string, options?: {
26 | detail?: WebComponentDetail;
27 | }) => Bored;
28 | };
29 | /** The function returned by `webComponent`, used to create subscribers and call the initialize function */
30 | export type LoadedFunction = ReturnType;
31 | /** The function passed as parameter to `webComponent`, used to initialize the component and create the render function */
32 | export type InitFunction = (options: WebComponentInitParams) => RenderFunction;
33 | /** The function used to render function and update it visually */
34 | export type RenderFunction = (renderOpts: WebComponentRenderParams) => void;
35 | export type AppState = {
36 | app: S | undefined;
37 | internal: {
38 | customTags: string[];
39 | components: Map;
40 | updates: {
41 | path: string[];
42 | value: object[];
43 | raf: number | undefined;
44 | subscribers: Map void)[]>;
45 | };
46 | };
47 | };
48 | export type Letter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
49 | export type Refs = {
50 | [key: `${Letter}${string}`]: HTMLElement;
51 | };
52 | export type Slots = {
53 | [key: `${Letter}${string}`]: HTMLElement;
54 | };
55 | /**
56 | * Queries for the component tag name in the DOM. Throws error if not found.
57 | */
58 | export declare const queryComponent: (q: string) => Bored | undefined;
59 | declare abstract class Bored extends HTMLElement {
60 | abstract renderCallback: (elem: Bored) => void;
61 | }
62 | /**
63 | * Queries all `` elements that
64 | * have a `data-component` attribute defined and creates web components
65 | * with the tag name in that attribute.
66 | *
67 | * @param state An optional initial app state object. When provided this will
68 | * be proxified to allow for automatic updates of the dom whenever it
69 | * changes.
70 | *
71 | * @param componentsLogic An optional object that allows you to specify the
72 | * web components script code without having to place it in a separate file.
73 | * Its keys are the tag names and its value is the return type of
74 | * the `webComponent()` function. This overrides any external file
75 | * associated with the component.
76 | *
77 | * @returns The app initial state.
78 | */
79 | export declare function inflictBoreDOM(state?: S, componentsLogic?: {
80 | [key: string]: ReturnType;
81 | }): Promise;
82 | /**
83 | * Creates a Web Component render updater
84 | *
85 | * @param initFunction Initialization function that returns the render function
86 | * @return A curried function to use as callback for component initialization
87 | */
88 | export declare function webComponent(initFunction: InitFunction): (appState: AppState, detail?: any) => (c: Bored) => void;
89 |
90 | export {};
91 |
--------------------------------------------------------------------------------
/dist/boreDOM.min.js:
--------------------------------------------------------------------------------
1 | var M=async t=>{let e=new Map;for(let n=0;nArray.from(D("template[data-component]")).filter(t=>t instanceof HTMLElement).map(t=>{let e={name:"",attributes:[]};for(let n in t.dataset)n==="component"?e.name=t.dataset[n]??"":e.attributes.push([F(n),t.dataset[n]??""]);if(e.name==="")throw new Error(`A was found with an invalid data-component: "${t.dataset.component}"`);return e}).map(({name:t,attributes:e})=>(Q(t,{attributes:e}),t)),w=(t,e)=>{let n=f(t);if(!O(n)){let r=`The tag name "${t}" is not a BoreDOM component.
2 |
3 | "createComponent" only accepts tag-names with matching tags that have a data-component attribute in them.`;throw console.error(r),new Error(r)}return e&&(n.renderCallback=e),n},y=t=>{let e=A(t);if(!(e===null||!O(e)))return e},A=t=>document.querySelector(t),D=t=>document.querySelectorAll(t),f=(t,e)=>{let n=document.createElement(t);return e&&Array.isArray(e)&&e.length>0&&e.map(r=>n.appendChild(r)),n};var I=(t,e)=>{document.readyState==="loading"?addEventListener("DOMContentLoaded",()=>dispatchEvent(new CustomEvent(t,{detail:e}))):dispatchEvent(new CustomEvent(t,{detail:e}))};var H=t=>typeof t=="object",v=t=>typeof t=="function",O=t=>H(t)&&"isBored"in t&&!!t.isBored;var F=t=>{if(t===""||!t.split("").some(n=>n!==n.toLowerCase()))return t;let e="";for(let n=0;nt.startsWith("on"),z=t=>t.startsWith("queriedOn"),S=t=>C(t)?t.slice(2).toLowerCase():t.slice(9).toLowerCase(),E=class extends HTMLElement{},Q=(t,e={})=>{customElements.get(t)||customElements.define(t,class extends E{static get observedAttributes(){return typeof e.attributeChangedCallback=="object"?Object.keys(e.attributeChangedCallback):[]}constructor(){super()}isBored=!0;traverse(n,{traverseShadowRoot:r,query:i}={}){Array.from(r?this.shadowRoot?.querySelectorAll(i??"*")??[]:[]).concat(Array.from(this.querySelectorAll(i??"*"))).filter(o=>o instanceof HTMLElement).forEach(n)}#t(n){return n.split("'").filter(r=>r.length>2&&!(r.includes("(")||r.includes(",")||r.includes(")")))}#e(){let n;this.traverse(r=>{if(r instanceof HTMLElement){customElements.get(r.tagName.toLowerCase())&&(n=r);for(let o=0;o0&&a.forEach(c=>{r.addEventListener(S(s.name),l=>I(c,{event:l}))}),r.setAttribute(`data-${s.name}-dispatches`,a.join()),r.removeAttribute(s.name)}}}},{traverseShadowRoot:!0})}isInitialized=!1;#n(){let n=A(`[data-component="${t}"]`)??f("template"),r=n.getAttribute("shadowrootmode");if(e.style||e.shadow||r){let o=e.shadowrootmode??r??"open",s=this.attachShadow({mode:o});if(e.style){let a=f("style");a.textContent=e.style,s.appendChild(a)}if(e.shadow){let a=f("template");a.innerHTML=e.shadow,s.appendChild(a.content.cloneNode(!0))}else r&&s.appendChild(n.content.cloneNode(!0))}n&&!r&&this.appendChild(n.content.cloneNode(!0)),e.onSlotChange&&this.traverse(o=>{o instanceof HTMLSlotElement&&o.addEventListener("slotchange",s=>e.onSlotChange?.(s))},{traverseShadowRoot:!0}),v(e.onClick)&&this.addEventListener("click",e.onClick);for(let[o,s]of Object.entries(e))if(C(o)){if(!v(s))continue;this.addEventListener(S(o),s)}else if(z(o)){let a=s;if(!H(a))continue;let c=S(o);for(let[l,d]of Object.entries(a))this.traverse(u=>{u.addEventListener(c,d)},{traverseShadowRoot:!0,query:l})}e.attributes&&Array.isArray(e.attributes)&&e.attributes.map(([o,s])=>this.setAttribute(o,s)),this.#e(),this.isInitialized=!0}renderCallback=n=>{};connectedCallback(){this.isInitialized||this.#n(),this.renderCallback(this),e.connectedCallback?.(this)}slots=b(this);updateSlot(n,r,i){document.createElement(i).setAttribute("slot",n)}disconnectedCallback(){console.log("disconnected "+this.tagName),e.disconnectedCallback?.(this)}adoptedCallback(){console.log("adopted "+this.tagName),e.adoptedCallback?.(this)}attributeChangedCallback(n,r,i){e.attributeChangedCallback&&e.attributeChangedCallback[n]({element:this,name:n,oldValue:r,newValue:i})}})};function j(t,e){let n=e;return e===null||t.forEach(r=>{n=n[r]}),n}function R(t,e=[]){let n=[{path:[],obj:t}],r=[];for(;n.length>0;){let{path:i,obj:o}=n.pop();for(let s in o){if(e.includes(s))continue;let a=o[s],c=i.concat(s);typeof a=="object"&&a!==null&&n.push({path:c,obj:a}),r.push({path:c,value:a})}}return r}function x(t){if(t==null||typeof t!="object")return!1;let e=Object.getPrototypeOf(t);return e==null?!0:e===Object.prototype}function $(t,e,n){return(r,i)=>{addEventListener(r,o=>{let s=o?.detail?.event.currentTarget;for(;s;){if(s===t){i({state:e,e:o.detail,detail:n});return}s instanceof HTMLElement?s=s.parentElement:s=void 0}})}}function W(t){return new Proxy({},{get(e,n,r){let i=new Error(`Ref "${String(n)}" not found in <${t.tagName}>`);if(typeof n=="string"){let o=t.querySelectorAll(`[data-ref="${n}"]`);if(!o)throw i;let s=Array.from(o).filter(a=>a instanceof HTMLElement);if(s.length===0)throw i;return s.length===1?s[0]:s}}})}function b(t){return new Proxy({},{get(e,n,r){let i=new Error(`Slot "${String(n)}" not found in <${t.tagName}>`);if(typeof n=="string"){let o=t.querySelectorAll(`slot[name="${n}"]`);if(!o)throw i;let s=Array.from(o).filter(a=>a instanceof HTMLSlotElement);if(s.length===0)throw i;return s.length===1?s[0]:s}},set(e,n,r){if(typeof n!="string")return!1;let i=r;if(r instanceof HTMLElement)r.setAttribute("data-slot",n);else if(typeof r=="string")i=f("span"),i.setAttribute("data-slot",n),i.innerText=r;else throw new Error(`Invalid value for slot ${n} in <${t.tagName}>`);let o=Array.from(t.querySelectorAll(`[data-slot="${n}"]`));return o.length>0?o.forEach(s=>s.parentElement?.replaceChild(i,s)):Array.from(t.querySelectorAll(`slot[name="${n}"]`)).forEach(a=>a.parentElement?.replaceChild(i,a)),!0}})}function L(t,e,n){let r=n||{targets:new WeakMap,path:[]};if(t!==void 0)return new Proxy(t,{set(i,o,s){return typeof o=="string"&&console.error(`State is read-only for web components. Unable to set '${o}'.`),!1},get(i,o,s){let a=Reflect.get(i,o,s),c=o==="__proto__";if(typeof o=="string"&&!c&&(r.targets.has(i)||(r.targets.set(i,r.path.join(".")),r.path.push(o))),c||Array.isArray(a)||x(a))return L(a,e,r);let l=r.targets.get(i)??"";return typeof l=="string"&&typeof o=="string"&&(Array.isArray(i)||(l+=l!==""?`.${o}`:o),e.indexOf(l)===-1&&e.push(l)),r.path.length=0,r.path.push(l),a}})}function _(t){return()=>{let e=t.internal.updates;for(let n=0;n{if(Array.isArray(o)||x(o)&&!r.has(o)){let a=i.join("."),c=j(i.slice(0,-1),n);if(c===o)return;c[i.at(-1)]=new Proxy(o,{set(d,u,h){return!(d[u]!==h)||(Reflect.set(d,u,h),typeof u!="string")||(Array.isArray(o)?e.updates.path.push(`${a}`):e.updates.path.push(`${a}.${u}`),e.updates.value.push(d),e.updates.raf||(e.updates.raf=requestAnimationFrame(_(t)))),!0}}),r.add(o)}}),t}function N(t){let e=t.internal.customTags.filter(r=>y(r)!==void 0),n=t.internal.components;for(let[r,i]of n.entries()){if(i===null||!e.includes(r))continue;let o=y(r);if(!o){console.log(`<${r}> is not yet in the DOM. The associated JS script will be called when the component is connected.`);return}i(t,{index:0,name:r,data:void 0})(o)}}function q(t,e,n){let r=e.internal.components.get(t);if(r){let i={...n,tagName:t};return w(t,r(e,i))}return w(t)}async function ct(t,e){let n=k(),r=await M(n);if(e)for(let s of Object.keys(e))r.set(s,e[s]);let o=B({app:t,internal:{customTags:n,components:r,updates:{path:[],value:[],raf:void 0,subscribers:new Map}}});return N(o),o.app}function lt(t){let e=null,n;return(r,i)=>o=>{let{internal:s,app:a}=r,c=[],l=L(a,c),d=W(o),u=b(o),h=$(o,a,i);if(e!==o){let T=async()=>{let p=s.updates.subscribers;for(let g of c){let m=p.get(g);m?m.includes(n)||m.push(n):p.set(g,[n])}},P=t({detail:i,state:l,refs:d,on:h,self:o});n=p=>{P({state:p,refs:d,slots:u,self:o,detail:i,makeComponent:(g,m)=>q(g,r,m?.detail)}),T()}}n(l),e=o}}export{ct as inflictBoreDOM,y as queryComponent,lt as webComponent};
4 |
--------------------------------------------------------------------------------
/examples/counter/boreDOM.min.js:
--------------------------------------------------------------------------------
1 | var M=async t=>{let e=new Map;for(let n=0;nArray.from(I("template[data-component]")).filter(t=>t instanceof HTMLElement).map(t=>{let e={name:"",attributes:[]};for(let n in t.dataset)n==="component"?e.name=t.dataset[n]??"":e.attributes.push([F(n),t.dataset[n]??""]);if(e.name==="")throw new Error(`A was found with an invalid data-component: "${t.dataset.component}"`);return e}).map(({name:t,attributes:e})=>(Q(t,{attributes:e}),t)),E=(t,e)=>{let n=f(t);if(!O(n)){let r=`The tag name "${t}" is not a BoreDOM component.
2 |
3 | "createComponent" only accepts tag-names with matching tags that have a data-component attribute in them.`;throw console.error(r),new Error(r)}return e&&(n.renderCallback=e),n},w=t=>{let e=L(t);if(!(e===null||!O(e)))return e},L=t=>document.querySelector(t),I=t=>document.querySelectorAll(t),f=(t,e)=>{let n=document.createElement(t);return e&&Array.isArray(e)&&e.length>0&&e.map(r=>n.appendChild(r)),n};var D=(t,e)=>{document.readyState==="loading"?addEventListener("DOMContentLoaded",()=>dispatchEvent(new CustomEvent(t,{detail:e}))):dispatchEvent(new CustomEvent(t,{detail:e}))};var H=t=>typeof t=="object",v=t=>typeof t=="function",O=t=>H(t)&&"isBored"in t&&!!t.isBored;var F=t=>{if(t===""||!t.split("").some(n=>n!==n.toLowerCase()))return t;let e="";for(let n=0;nt.startsWith("on"),z=t=>t.startsWith("queriedOn"),b=t=>S(t)?t.slice(2).toLowerCase():t.slice(9).toLowerCase(),C=class extends HTMLElement{},Q=(t,e={})=>{customElements.get(t)||customElements.define(t,class extends C{static get observedAttributes(){return typeof e.attributeChangedCallback=="object"?Object.keys(e.attributeChangedCallback):[]}constructor(){super()}isBored=!0;traverse(n,{traverseShadowRoot:r,query:s}={}){Array.from(r?this.shadowRoot?.querySelectorAll(s??"*")??[]:[]).concat(Array.from(this.querySelectorAll(s??"*"))).filter(o=>o instanceof HTMLElement).forEach(n)}#t(n){return n.split("'").filter(r=>r.length>2&&!(r.includes("(")||r.includes(",")||r.includes(")")))}#e(){let n;this.traverse(r=>{if(r instanceof HTMLElement){customElements.get(r.tagName.toLowerCase())&&(n=r);for(let o=0;o0&&a.forEach(c=>{r.addEventListener(b(i.name),l=>D(c,{event:l}))}),r.setAttribute(`data-${i.name}-dispatches`,a.join()),r.removeAttribute(i.name)}}}},{traverseShadowRoot:!0})}isInitialized=!1;#n(){let n=L(`[data-component="${t}"]`)??f("template"),r=n.getAttribute("shadowrootmode");if(e.style||e.shadow||r){let o=e.shadowrootmode??r??"open",i=this.attachShadow({mode:o});if(e.style){let a=f("style");a.textContent=e.style,i.appendChild(a)}if(e.shadow){let a=f("template");a.innerHTML=e.shadow,i.appendChild(a.content.cloneNode(!0))}else r&&i.appendChild(n.content.cloneNode(!0))}n&&!r&&this.appendChild(n.content.cloneNode(!0)),e.onSlotChange&&this.traverse(o=>{o instanceof HTMLSlotElement&&o.addEventListener("slotchange",i=>e.onSlotChange?.(i))},{traverseShadowRoot:!0}),v(e.onClick)&&this.addEventListener("click",e.onClick);for(let[o,i]of Object.entries(e))if(S(o)){if(!v(i))continue;this.addEventListener(b(o),i)}else if(z(o)){let a=i;if(!H(a))continue;let c=b(o);for(let[l,d]of Object.entries(a))this.traverse(u=>{u.addEventListener(c,d)},{traverseShadowRoot:!0,query:l})}e.attributes&&Array.isArray(e.attributes)&&e.attributes.map(([o,i])=>this.setAttribute(o,i)),this.#e(),this.isInitialized=!0}renderCallback=n=>{};connectedCallback(){this.isInitialized||this.#n(),this.renderCallback(this),e.connectedCallback?.(this)}slots=y(this);updateSlot(n,r,s){document.createElement(s).setAttribute("slot",n)}disconnectedCallback(){console.log("disconnected "+this.tagName),e.disconnectedCallback?.(this)}adoptedCallback(){console.log("adopted "+this.tagName),e.adoptedCallback?.(this)}attributeChangedCallback(n,r,s){e.attributeChangedCallback&&e.attributeChangedCallback[n]({element:this,name:n,oldValue:r,newValue:s})}})};function j(t,e){let n=e;return e===null||t.forEach(r=>{n=n[r]}),n}function R(t,e=[]){let n=[{path:[],obj:t}],r=[];for(;n.length>0;){let{path:s,obj:o}=n.pop();for(let i in o){if(e.includes(i))continue;let a=o[i],c=s.concat(i);typeof a=="object"&&a!==null&&n.push({path:c,obj:a}),r.push({path:c,value:a})}}return r}function A(t){if(t==null||typeof t!="object")return!1;let e=Object.getPrototypeOf(t);return e==null?!0:e===Object.prototype}function $(t,e){return(n,r)=>{addEventListener(n,s=>{let o=s?.detail?.event.currentTarget;for(;o;){if(o===t){r(e,s.detail);return}o instanceof HTMLElement?o=o.parentElement:o=void 0}})}}function B(t){return new Proxy({},{get(e,n,r){let s=new Error(`Ref "${String(n)}" not found in <${t.tagName}>`);if(typeof n=="string"){let o=t.querySelectorAll(`[data-ref="${n}"]`);if(!o)throw s;let i=Array.from(o).filter(a=>a instanceof HTMLElement);if(i.length===0)throw s;return i.length===1?i[0]:i}}})}function y(t){return new Proxy({},{get(e,n,r){let s=new Error(`Slot "${String(n)}" not found in <${t.tagName}>`);if(typeof n=="string"){let o=t.querySelectorAll(`slot[name="${n}"]`);if(!o)throw s;let i=Array.from(o).filter(a=>a instanceof HTMLSlotElement);if(i.length===0)throw s;return i.length===1?i[0]:i}},set(e,n,r){if(typeof n!="string")return!1;let s=r;if(r instanceof HTMLElement)r.setAttribute("data-slot",n);else if(typeof r=="string")s=f("span"),s.setAttribute("data-slot",n),s.innerText=r;else throw new Error(`Invalid value for slot ${n} in <${t.tagName}>`);let o=Array.from(t.querySelectorAll(`[data-slot="${n}"]`));return o.length>0?o.forEach(i=>i.parentElement?.replaceChild(s,i)):Array.from(t.querySelectorAll(`slot[name="${n}"]`)).forEach(a=>a.parentElement?.replaceChild(s,a)),!0}})}function x(t,e,n){let r=n||{targets:new WeakMap,path:[]};if(t!==void 0)return new Proxy(t,{set(s,o,i){return typeof o=="string"&&console.error(`State is read-only for web components. Unable to set '${o}'.`),!1},get(s,o,i){let a=Reflect.get(s,o,i),c=o==="__proto__";if(typeof o=="string"&&!c&&(r.targets.has(s)||(r.targets.set(s,r.path.join(".")),r.path.push(o))),c||Array.isArray(a)||A(a))return x(a,e,r);let l=r.targets.get(s)??"";return typeof l=="string"&&typeof o=="string"&&(l+=l!==""?`.${o}`:o,e.indexOf(l)===-1&&e.push(l)),r.path.length=0,r.path.push(l),a}})}function _(t){return()=>{let e=t.internal.updates;for(let n=0;n{if(Array.isArray(o)||A(o)&&!r.has(o)){let a=s.join("."),c=j(s.slice(0,-1),n);if(c===o)return;c[s.at(-1)]=new Proxy(o,{set(d,u,h){return!(d[u]!==h)||(Reflect.set(d,u,h),typeof u!="string")||(e.updates.path.push(`${a}.${u}`),e.updates.value.push(d),e.updates.raf||(e.updates.raf=requestAnimationFrame(_(t)))),!0}}),r.add(o)}}),t}function N(t){let e=t.internal.customTags.filter(r=>w(r)!==void 0),n=t.internal.components;for(let[r,s]of n.entries()){if(s===null||!e.includes(r))continue;let o=w(r);if(!o){console.log(`<${r}> is not yet in the DOM. The associated JS script will be called when the component is connected.`);return}s(t,{index:0,name:r,data:void 0})(o)}}function q(t,e,n){let r=e.internal.components.get(t);if(r){let s={...n,tagName:t};return E(t,r(e,s))}return E(t)}async function ct(t){let e=k(),n=await M(e),s=W({app:t,internal:{customTags:e,components:n,updates:{path:[],value:[],raf:void 0,subscribers:new Map}}});return console.log("inflictBoreDOM()",t),N(s),s.app}function lt(t){let e=null,n;return(r,s)=>o=>{let{internal:i,app:a}=r,c=[],l=x(a,c),d=B(o),u=y(o),h=$(o,a);if(e!==o){let T=async()=>{let p=i.updates.subscribers;for(let g of c){let m=p.get(g);m?m.includes(n)||m.push(n):p.set(g,[n])}},P=t({detail:s,state:l,refs:d,on:h,self:o});n=p=>{P({state:p,refs:d,slots:u,self:o,detail:s,makeComponent:(g,m)=>q(g,r,m?.detail)}),T()}}n(l),e=o}}export{ct as inflictBoreDOM,lt as webComponent};
4 |
--------------------------------------------------------------------------------
/examples/counter/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | boreDOM Counter
8 |
9 |
10 |
11 |
12 | Simple counter
13 |
14 |
15 |
16 |
17 | Some value
18 |
19 |
20 | Increase
21 |
22 |
23 |
24 | Decrease
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/examples/counter/main.js:
--------------------------------------------------------------------------------
1 | import { inflictBoreDOM } from "./boreDOM.min.js";
2 |
3 | inflictBoreDOM({ value: 0 });
4 |
--------------------------------------------------------------------------------
/examples/counter/simple-counter.js:
--------------------------------------------------------------------------------
1 | import { webComponent } from "./boreDOM.min.js";
2 |
3 | export const SimpleCounter = webComponent(({ on }) => {
4 | on("increase", ({ state: mutableState }) => {
5 | mutableState.value += 1;
6 | });
7 | on("decrease", ({ state: mutableState }) => {
8 | mutableState.value -= 1;
9 | });
10 |
11 | return (({ state, self }) => {
12 | self.slots.counter = `${state.value}`;
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/README.md:
--------------------------------------------------------------------------------
1 | # Steps
2 |
3 | 1. `pnpm install @mr_hugo/boredom`
4 | 2. `pnpm install --save-exact --save-dev esbuild`
5 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/boreDOM.d.ts:
--------------------------------------------------------------------------------
1 | // Generated by dts-bundle-generator v9.5.1
2 |
3 | export type WebComponentDetail = {
4 | index: number;
5 | name: string;
6 | data?: any;
7 | };
8 | export type WebComponentInitParams = {
9 | detail: WebComponentDetail;
10 | state: S;
11 | refs: Refs;
12 | self: Bored;
13 | on: (eventName: string, eventHandler: () => void) => void;
14 | };
15 | export type WebComponentRenderParams = {
16 | detail: WebComponentDetail;
17 | state: S;
18 | refs: Refs;
19 | slots: Slots;
20 | self: Bored;
21 | makeComponent: (tag: string, options?: {
22 | detail?: WebComponentDetail;
23 | }) => Bored;
24 | };
25 | /** The function returned by `webComponent`, used to create subscribers and call the initialize function */
26 | export type LoadedFunction = ReturnType;
27 | /** The function passed as parameter to `webComponent`, used to initialize the component and create the render function */
28 | export type InitFunction = (options: WebComponentInitParams) => RenderFunction;
29 | /** The function used to render function and update it visually */
30 | export type RenderFunction = (renderOpts: WebComponentRenderParams) => void;
31 | export type AppState = {
32 | app: S | undefined;
33 | internal: {
34 | customTags: string[];
35 | components: Map;
36 | updates: {
37 | path: string[];
38 | value: object[];
39 | raf: number | undefined;
40 | subscribers: Map void)[]>;
41 | };
42 | };
43 | };
44 | export type Letter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
45 | export type Refs = {
46 | [key: `${Letter}${string}`]: HTMLElement;
47 | };
48 | export type Slots = {
49 | [key: `${Letter}${string}`]: HTMLElement;
50 | };
51 | declare abstract class Bored extends HTMLElement {
52 | abstract renderCallback: (elem: Bored) => void;
53 | }
54 | /**
55 | * Queries all `` elements that
56 | * have a `data-component` attribute defined and creates web components
57 | * with the tag name in that attribute.
58 | *
59 | * @param state An optional initial app state object. When provided this will
60 | * be proxified to allow for automatic updates of the dom whenever it
61 | * changes.
62 | *
63 | * @returns The app initial state.
64 | */
65 | export declare function inflictBoreDOM(state?: S): Promise;
66 | /**
67 | * Creates a Web Component render updater
68 | *
69 | * @param initFunction Initialization function that returns the render function
70 | * @return A curried function to use as callback for component initialization
71 | */
72 | export declare function webComponent(initFunction: InitFunction): (appState: AppState, detail?: any) => (c: Bored) => void;
73 |
74 | export {};
75 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/boreDOM.min.js:
--------------------------------------------------------------------------------
1 | ../../dist/boreDOM.min.js
--------------------------------------------------------------------------------
/examples/tic-tac-toe/game-board.css:
--------------------------------------------------------------------------------
1 | :where(game-board) {
2 | display: grid;
3 | grid-template-columns: 1fr 1fr 1fr;
4 | }
5 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/game-board.js:
--------------------------------------------------------------------------------
1 | import { webComponent } from "./boreDOM.min.js";
2 |
3 | export const GameBoard = webComponent(({ on, self }) => {
4 | on("play", ({ state: mutableState, e: { event } }) => {
5 | const index = Array.from(self.children).indexOf(
6 | event.currentTarget.parentNode,
7 | );
8 | const isGameWon = mutableState.gameState.winner !== null;
9 | const isEmptySquare = mutableState.gameState.board[index] === undefined;
10 |
11 | if (!isGameWon && isEmptySquare) {
12 | mutableState.gameState.board[index] = mutableState.gameState.nextToPlay;
13 | // Update the next to play:
14 | mutableState.gameState.nextToPlay =
15 | mutableState.gameState.nextToPlay === "O" ? "X" : "O";
16 | // Update the winner state
17 | mutableState.gameState.winner = calculateWinner(
18 | mutableState.gameState.board,
19 | );
20 | }
21 | });
22 |
23 | return (({ state }) => {
24 | let index = 0;
25 | for (const child of self.children) {
26 | const boardValue = state.gameState.board[index++];
27 |
28 | if (boardValue) {
29 | child.slots.valuePlayed = boardValue;
30 | }
31 | }
32 | });
33 | });
34 |
35 | /**
36 | * Taken from https://react.dev/learn/tutorial-tic-tac-toe
37 | */
38 | function calculateWinner(squares) {
39 | const lines = [
40 | [0, 1, 2],
41 | [3, 4, 5],
42 | [6, 7, 8],
43 | [0, 3, 6],
44 | [1, 4, 7],
45 | [2, 5, 8],
46 | [0, 4, 8],
47 | [2, 4, 6],
48 | ];
49 | for (let i = 0; i < lines.length; i++) {
50 | const [a, b, c] = lines[i];
51 | if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
52 | return squares[a];
53 | }
54 | }
55 | return null;
56 | }
57 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/game-button.css:
--------------------------------------------------------------------------------
1 | :where(game-button button) {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | margin: auto;
6 | width: 100%;
7 | height: 10rem;
8 | font-size: 5rem;
9 | }
10 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/game-turn.js:
--------------------------------------------------------------------------------
1 | import { webComponent } from "./boreDOM.min.js";
2 |
3 | export const GameTurn = webComponent(() => {
4 | return (({ state, self }) => {
5 | if (state.gameState.winner) {
6 | self.slots.playerTurn = `Victory for Player ${state.gameState.winner}`;
7 | } else {
8 | self.slots.playerTurn = `Next player: ${state.gameState.nextToPlay}`;
9 | }
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | boreDOM Tic Tac Toe
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Tic Tac Toe
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/main.js:
--------------------------------------------------------------------------------
1 | import { inflictBoreDOM } from "./boreDOM.min.js";
2 |
3 | inflictBoreDOM({
4 | gameState: {
5 | board: new Array(9),
6 | nextToPlay: "O",
7 | winner: null,
8 | },
9 | });
10 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 |
9 | .:
10 | dependencies:
11 | '@mr_hugo/boredom':
12 | specifier: ^0.25.2
13 | version: 0.25.2
14 | devDependencies:
15 | esbuild:
16 | specifier: 0.24.2
17 | version: 0.24.2
18 |
19 | packages:
20 |
21 | '@esbuild/aix-ppc64@0.24.2':
22 | resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==}
23 | engines: {node: '>=18'}
24 | cpu: [ppc64]
25 | os: [aix]
26 |
27 | '@esbuild/android-arm64@0.24.2':
28 | resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==}
29 | engines: {node: '>=18'}
30 | cpu: [arm64]
31 | os: [android]
32 |
33 | '@esbuild/android-arm@0.24.2':
34 | resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==}
35 | engines: {node: '>=18'}
36 | cpu: [arm]
37 | os: [android]
38 |
39 | '@esbuild/android-x64@0.24.2':
40 | resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==}
41 | engines: {node: '>=18'}
42 | cpu: [x64]
43 | os: [android]
44 |
45 | '@esbuild/darwin-arm64@0.24.2':
46 | resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==}
47 | engines: {node: '>=18'}
48 | cpu: [arm64]
49 | os: [darwin]
50 |
51 | '@esbuild/darwin-x64@0.24.2':
52 | resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==}
53 | engines: {node: '>=18'}
54 | cpu: [x64]
55 | os: [darwin]
56 |
57 | '@esbuild/freebsd-arm64@0.24.2':
58 | resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==}
59 | engines: {node: '>=18'}
60 | cpu: [arm64]
61 | os: [freebsd]
62 |
63 | '@esbuild/freebsd-x64@0.24.2':
64 | resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==}
65 | engines: {node: '>=18'}
66 | cpu: [x64]
67 | os: [freebsd]
68 |
69 | '@esbuild/linux-arm64@0.24.2':
70 | resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==}
71 | engines: {node: '>=18'}
72 | cpu: [arm64]
73 | os: [linux]
74 |
75 | '@esbuild/linux-arm@0.24.2':
76 | resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==}
77 | engines: {node: '>=18'}
78 | cpu: [arm]
79 | os: [linux]
80 |
81 | '@esbuild/linux-ia32@0.24.2':
82 | resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==}
83 | engines: {node: '>=18'}
84 | cpu: [ia32]
85 | os: [linux]
86 |
87 | '@esbuild/linux-loong64@0.24.2':
88 | resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==}
89 | engines: {node: '>=18'}
90 | cpu: [loong64]
91 | os: [linux]
92 |
93 | '@esbuild/linux-mips64el@0.24.2':
94 | resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==}
95 | engines: {node: '>=18'}
96 | cpu: [mips64el]
97 | os: [linux]
98 |
99 | '@esbuild/linux-ppc64@0.24.2':
100 | resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==}
101 | engines: {node: '>=18'}
102 | cpu: [ppc64]
103 | os: [linux]
104 |
105 | '@esbuild/linux-riscv64@0.24.2':
106 | resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==}
107 | engines: {node: '>=18'}
108 | cpu: [riscv64]
109 | os: [linux]
110 |
111 | '@esbuild/linux-s390x@0.24.2':
112 | resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==}
113 | engines: {node: '>=18'}
114 | cpu: [s390x]
115 | os: [linux]
116 |
117 | '@esbuild/linux-x64@0.24.2':
118 | resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==}
119 | engines: {node: '>=18'}
120 | cpu: [x64]
121 | os: [linux]
122 |
123 | '@esbuild/netbsd-arm64@0.24.2':
124 | resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==}
125 | engines: {node: '>=18'}
126 | cpu: [arm64]
127 | os: [netbsd]
128 |
129 | '@esbuild/netbsd-x64@0.24.2':
130 | resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==}
131 | engines: {node: '>=18'}
132 | cpu: [x64]
133 | os: [netbsd]
134 |
135 | '@esbuild/openbsd-arm64@0.24.2':
136 | resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==}
137 | engines: {node: '>=18'}
138 | cpu: [arm64]
139 | os: [openbsd]
140 |
141 | '@esbuild/openbsd-x64@0.24.2':
142 | resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==}
143 | engines: {node: '>=18'}
144 | cpu: [x64]
145 | os: [openbsd]
146 |
147 | '@esbuild/sunos-x64@0.24.2':
148 | resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==}
149 | engines: {node: '>=18'}
150 | cpu: [x64]
151 | os: [sunos]
152 |
153 | '@esbuild/win32-arm64@0.24.2':
154 | resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==}
155 | engines: {node: '>=18'}
156 | cpu: [arm64]
157 | os: [win32]
158 |
159 | '@esbuild/win32-ia32@0.24.2':
160 | resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==}
161 | engines: {node: '>=18'}
162 | cpu: [ia32]
163 | os: [win32]
164 |
165 | '@esbuild/win32-x64@0.24.2':
166 | resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==}
167 | engines: {node: '>=18'}
168 | cpu: [x64]
169 | os: [win32]
170 |
171 | '@mr_hugo/boredom@0.25.2':
172 | resolution: {integrity: sha512-qbN08hWv4PoLoDT8oJ5doIA0Fv1XW1z0XE2fxodhw7zhmWj5aaGLqkDUodbdCv+ol824t4caijzfn/I5labqrA==}
173 |
174 | esbuild@0.24.2:
175 | resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==}
176 | engines: {node: '>=18'}
177 | hasBin: true
178 |
179 | snapshots:
180 |
181 | '@esbuild/aix-ppc64@0.24.2':
182 | optional: true
183 |
184 | '@esbuild/android-arm64@0.24.2':
185 | optional: true
186 |
187 | '@esbuild/android-arm@0.24.2':
188 | optional: true
189 |
190 | '@esbuild/android-x64@0.24.2':
191 | optional: true
192 |
193 | '@esbuild/darwin-arm64@0.24.2':
194 | optional: true
195 |
196 | '@esbuild/darwin-x64@0.24.2':
197 | optional: true
198 |
199 | '@esbuild/freebsd-arm64@0.24.2':
200 | optional: true
201 |
202 | '@esbuild/freebsd-x64@0.24.2':
203 | optional: true
204 |
205 | '@esbuild/linux-arm64@0.24.2':
206 | optional: true
207 |
208 | '@esbuild/linux-arm@0.24.2':
209 | optional: true
210 |
211 | '@esbuild/linux-ia32@0.24.2':
212 | optional: true
213 |
214 | '@esbuild/linux-loong64@0.24.2':
215 | optional: true
216 |
217 | '@esbuild/linux-mips64el@0.24.2':
218 | optional: true
219 |
220 | '@esbuild/linux-ppc64@0.24.2':
221 | optional: true
222 |
223 | '@esbuild/linux-riscv64@0.24.2':
224 | optional: true
225 |
226 | '@esbuild/linux-s390x@0.24.2':
227 | optional: true
228 |
229 | '@esbuild/linux-x64@0.24.2':
230 | optional: true
231 |
232 | '@esbuild/netbsd-arm64@0.24.2':
233 | optional: true
234 |
235 | '@esbuild/netbsd-x64@0.24.2':
236 | optional: true
237 |
238 | '@esbuild/openbsd-arm64@0.24.2':
239 | optional: true
240 |
241 | '@esbuild/openbsd-x64@0.24.2':
242 | optional: true
243 |
244 | '@esbuild/sunos-x64@0.24.2':
245 | optional: true
246 |
247 | '@esbuild/win32-arm64@0.24.2':
248 | optional: true
249 |
250 | '@esbuild/win32-ia32@0.24.2':
251 | optional: true
252 |
253 | '@esbuild/win32-x64@0.24.2':
254 | optional: true
255 |
256 | '@mr_hugo/boredom@0.25.2': {}
257 |
258 | esbuild@0.24.2:
259 | optionalDependencies:
260 | '@esbuild/aix-ppc64': 0.24.2
261 | '@esbuild/android-arm': 0.24.2
262 | '@esbuild/android-arm64': 0.24.2
263 | '@esbuild/android-x64': 0.24.2
264 | '@esbuild/darwin-arm64': 0.24.2
265 | '@esbuild/darwin-x64': 0.24.2
266 | '@esbuild/freebsd-arm64': 0.24.2
267 | '@esbuild/freebsd-x64': 0.24.2
268 | '@esbuild/linux-arm': 0.24.2
269 | '@esbuild/linux-arm64': 0.24.2
270 | '@esbuild/linux-ia32': 0.24.2
271 | '@esbuild/linux-loong64': 0.24.2
272 | '@esbuild/linux-mips64el': 0.24.2
273 | '@esbuild/linux-ppc64': 0.24.2
274 | '@esbuild/linux-riscv64': 0.24.2
275 | '@esbuild/linux-s390x': 0.24.2
276 | '@esbuild/linux-x64': 0.24.2
277 | '@esbuild/netbsd-arm64': 0.24.2
278 | '@esbuild/netbsd-x64': 0.24.2
279 | '@esbuild/openbsd-arm64': 0.24.2
280 | '@esbuild/openbsd-x64': 0.24.2
281 | '@esbuild/sunos-x64': 0.24.2
282 | '@esbuild/win32-arm64': 0.24.2
283 | '@esbuild/win32-ia32': 0.24.2
284 | '@esbuild/win32-x64': 0.24.2
285 |
--------------------------------------------------------------------------------
/examples/tic-tac-toe/style.css:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/examples/todo-list/boreDOM.min.js:
--------------------------------------------------------------------------------
1 | ../../dist/boreDOM.min.js
--------------------------------------------------------------------------------
/examples/todo-list/todo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | boreDOM TODO List
8 |
26 |
27 |
28 |
29 | To-Do List
30 |
31 |
32 |
33 |
34 |
35 |
36 | Add
37 |
38 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/jsr.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@hugodaniel/boredom",
3 | "version": "0.25.1",
4 | "license": "CC0",
5 | "exports": "./src/index.ts"
6 | }
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@mr_hugo/boredom",
3 | "version": "0.25.3",
4 | "description": "Another boring JavaScript framework.",
5 | "main": "dist/boreDOM.min.js",
6 | "scripts": {
7 | "build_decls": "dts-bundle-generator -o dist/boreDOM.d.ts src/index.ts",
8 | "build_module": "esbuild src/index.ts --bundle --outfile=dist/boreDOM.min.js --target=es2022 --minify --platform=neutral",
9 | "build_full_module": "esbuild src/index.ts --bundle --outfile=dist/boreDOM.full.js --target=es2022 --platform=neutral",
10 | "build": "pnpm run build_decls && pnpm run build_module",
11 | "build_cli": "pnpm run build_full_module && pnpm run build_decls && echo '#!/usr/bin/env node\nconst boredom=`' > dist/boreDOM.js && cat dist/boreDOM.full.js | base64 >> dist/boreDOM.js && echo '`;' >> dist/boreDOM.js && cat boreDOMCLI/cli.js >> dist/boreDOM.js && chmod +x dist/boreDOM.js && cp dist/boreDOM.js boreDOMCLI/index.js",
12 | "dev": "esbuild src/boreDOM.ts --bundle --outdir=www/js --watch --servedir=www --platform=neutral",
13 | "test": "esbuild tests/runner.ts --bundle --outdir=tests/js --watch --servedir=tests --platform=browser"
14 | },
15 | "keywords": [
16 | "dom",
17 | "framework"
18 | ],
19 | "author": "Hugo Daniel",
20 | "license": "CC0",
21 | "repository": "github:HugoDaniel/boreDOM",
22 | "homespage": "https://hugodaniel.com/pages/boredom/",
23 | "devDependencies": {
24 | "@testing-library/dom": "^10.4.0",
25 | "@types/chai": "^5.0.1",
26 | "@types/mocha": "^10.0.10",
27 | "@webgpu/types": "^0.1.52",
28 | "chai": "^5.1.2",
29 | "dts-bundle-generator": "^9.5.1",
30 | "esbuild": "0.23.1",
31 | "mocha": "^10.8.2",
32 | "nyc": "^17.1.0",
33 | "typedoc": "0.26.7",
34 | "typescript": "^5.7.2"
35 | }
36 | }
--------------------------------------------------------------------------------
/src/bore.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Mostly state management things
3 | */
4 | import { Bored, create, createComponent, queryComponent } from "./dom";
5 | import {
6 | AppState,
7 | ReadonlyProxy,
8 | Refs,
9 | Slots,
10 | WebComponentDetail,
11 | } from "./types";
12 | import { access } from "./utils/access";
13 | import { flatten } from "./utils/flatten";
14 | import { isPOJO } from "./utils/isPojo";
15 |
16 | /**
17 | * Called during initialization. This function sets
18 | * the custom event listeners for all events that modify state.
19 | * This does not register the "update" event (used for modifying the DOM).
20 | *
21 | * @param {State} state The state reference that will be transformed
22 | * at each event
23 | export const addCustomEvents = (state) => {
24 | for (const eventName in allEvents) {
25 | // @ts-ignore
26 | listener(eventName, state, allEvents[eventName]);
27 | }
28 | };
29 | */
30 |
31 | /**
32 | * Listens for an event, and logs it in the event logger
33 | *
34 | * @param {keyof Event} name - the event name to listen
35 | * @param {State} state - the state to transform
36 | * @param {(s: State, e: Event[keyof Event]) => any} h - the handler to call
37 | const listener = (name, state, h) => {
38 | addEventListener(name, (evt) => {
39 | if (evt instanceof CustomEvent) {
40 | // log(name, evt.detail);
41 | h(state, evt.detail);
42 |
43 | // Log it
44 | state.runtime.log.push({ e: name });
45 | }
46 | });
47 | };
48 | */
49 |
50 | /** */
51 | export function createEventsHandler(
52 | c: Bored,
53 | app: S,
54 | detail: WebComponentDetail,
55 | ) {
56 | return (
57 | eventName: string,
58 | handler: (
59 | options: {
60 | state: S | undefined;
61 | e: CustomEvent;
62 | detail: WebComponentDetail;
63 | },
64 | ) => void,
65 | ) => {
66 | addEventListener(eventName as any, (e) => {
67 | let target: HTMLElement | undefined | null = e?.detail?.event
68 | .currentTarget;
69 |
70 | // Only dispatch if the component 'c' is found in the hierarchy:
71 | while (target) {
72 | if (target === c) {
73 | handler({ state: app, e: e.detail, detail });
74 | return;
75 | }
76 |
77 | if (target instanceof HTMLElement) {
78 | target = target.parentElement;
79 | } else {
80 | target = undefined;
81 | }
82 | }
83 | });
84 | };
85 | }
86 | /** */
87 | export function createRefsAccessor(c: Bored): ReadonlyProxy {
88 | return new Proxy({}, {
89 | get(target, prop, receiver) {
90 | const error = new Error(
91 | `Ref "${String(prop)}" not found in <${c.tagName}>`,
92 | );
93 | if (typeof prop === "string") {
94 | const nodeList = c.querySelectorAll(`[data-ref="${prop}"]`);
95 | if (!nodeList) throw error;
96 | const refs = Array.from(nodeList).filter((ref) =>
97 | ref instanceof HTMLElement
98 | );
99 | if (refs.length === 0) throw error;
100 |
101 | if (refs.length === 1) return refs[0];
102 | return refs;
103 | }
104 | },
105 | });
106 | }
107 |
108 | /** */
109 | export function createSlotsAccessor(c: Bored): Slots {
110 | return new Proxy({}, {
111 | get(target, prop, reciever) {
112 | const error = new Error(
113 | `Slot "${String(prop)}" not found in <${c.tagName}>`,
114 | );
115 | if (typeof prop === "string") {
116 | const nodeList = c.querySelectorAll(`slot[name="${prop}"]`);
117 | if (!nodeList) throw error;
118 | const refs = Array.from(nodeList).filter((ref) =>
119 | ref instanceof HTMLSlotElement
120 | );
121 | if (refs.length === 0) throw error;
122 |
123 | if (refs.length === 1) return refs[0];
124 | return refs;
125 | }
126 | },
127 | set(target, prop, value) {
128 | if (typeof prop !== "string") return false;
129 |
130 | let elem = value;
131 | if (value instanceof HTMLElement) {
132 | value.setAttribute("data-slot", prop);
133 | } else if (typeof value === "string") {
134 | elem = create("span");
135 | elem.setAttribute("data-slot", prop);
136 | elem.innerText = value;
137 | } else {
138 | throw new Error(`Invalid value for slot ${prop} in <${c.tagName}>`);
139 | }
140 | const existingSlots = Array.from(
141 | c.querySelectorAll(`[data-slot="${prop}"]`),
142 | );
143 |
144 | if (existingSlots.length > 0) {
145 | existingSlots.forEach((s) => s.parentElement?.replaceChild(elem, s));
146 | } else {
147 | const slots = Array.from(c.querySelectorAll(`slot[name="${prop}"]`));
148 | slots.forEach((s) => s.parentElement?.replaceChild(elem, s));
149 | }
150 |
151 | return true;
152 | },
153 | });
154 | }
155 |
156 | /**
157 | * Creates a Web Component render updater
158 | *
159 | * @param state
160 | * @param log This array is updated with the paths being accessed
161 | * @param accum
162 | */
163 | export function createStateAccessor(
164 | state: S | undefined,
165 | log: (string[] | string)[],
166 | accum?: {
167 | targets: WeakMap;
168 | path: (string | symbol)[];
169 | },
170 | ) {
171 | const current = accum || { targets: new WeakMap(), path: [] };
172 | if (state === undefined) return undefined;
173 |
174 | return new Proxy(state as any, {
175 | // State accessors are read-only:
176 | set(target, prop, newValue) {
177 | if (typeof prop === "string") {
178 | console.error(
179 | `State is read-only for web components. Unable to set '${prop}'.`,
180 | );
181 | }
182 |
183 | return false;
184 | },
185 | // Recursively build a proxy for each state prop being read:
186 | get(target, prop, receiver) {
187 | const value = Reflect.get(target, prop, receiver);
188 | const isProto = prop === "__proto__";
189 |
190 | // This is a recursive function, keep track of the target of each prop
191 | // as the inner objects are being traversed
192 | if (typeof prop === "string" && !isProto) {
193 | if (!current.targets.has(target)) {
194 | current.targets.set(target, current.path.join("."));
195 | current.path.push(prop);
196 | }
197 | }
198 |
199 | // Go recursive when the value is a nested object:
200 | if (isProto || Array.isArray(value) || isPOJO(value)) {
201 | return createStateAccessor(value, log, current);
202 | }
203 |
204 | // Create current path, this is made by appending the current target path
205 | // with the prop being accesed:
206 | let path = current.targets.get(target) ?? "";
207 | if (typeof path === "string" && typeof prop === "string") {
208 | if (Array.isArray(target)) {
209 | // For now the path is kept as is, and all the array is triggered as updated
210 | path;
211 | } else {
212 | path += path !== "" ? `.${prop}` : prop;
213 | }
214 | if (log.indexOf(path) === -1) {
215 | // Only log the path if it is not already logged:
216 | log.push(path);
217 | }
218 | }
219 | current.path.length = 0;
220 | current.path.push(path);
221 | return value;
222 | },
223 | });
224 | }
225 |
226 | function createSubscribersDispatcher(state: AppState) {
227 | return () => {
228 | const updates = state.internal.updates;
229 | // Call the subscribers for each path that was updated
230 | for (let i = 0; i < updates.path.length; i++) {
231 | const path = updates.path[i];
232 | const functions =
233 | updates.subscribers.get(path.slice(path.indexOf(".") + 1)) ?? [];
234 | for (let j = 0; j < functions.length; j++) {
235 | functions[j](state.app);
236 | }
237 | }
238 |
239 | // clear the updates arrays
240 | updates.path = [];
241 | updates.value = [];
242 | updates.raf = undefined;
243 | };
244 | }
245 |
246 | /**
247 | * Registers the callbacks to trigger the state change subscribed functions.
248 | *
249 | * Batches subscribed functions to run in a rAF.
250 | *
251 | * @returns The same reference as provided in argument, but with
252 | * proxies in its attributes.
253 | */
254 | export function proxify(boredom: AppState) {
255 | const runtime = boredom.internal;
256 | const state = boredom;
257 | if (state === undefined) return boredom;
258 |
259 | // Keep track of which objects have been proxified:
260 | const objectsWithProxies = new WeakSet();
261 |
262 | // Traverse through all the properties in state
263 | flatten(boredom, ["internal"]).forEach(({ path, value }) => {
264 | const needsProxy = Array.isArray(value) ||
265 | (isPOJO(value) && !objectsWithProxies.has(value));
266 | if (needsProxy) {
267 | const dottedPath = path.join(".");
268 | const parent = access(path.slice(0, -1), state);
269 | // Don't proxify the root
270 | const isRoot = parent === value;
271 | if (isRoot) return;
272 |
273 | // @ts-ignore
274 | parent[path.at(-1)] = new Proxy(value, {
275 | set(target, prop, newValue) {
276 | // @ts-ignore Always do the default op when the value is changed
277 | const isChanged = target[prop] !== newValue;
278 | if (!isChanged) return true;
279 |
280 | // Update the value and issue the "update" event on the next frame
281 | // Issuing the event on the next frame gives us time to batch a few
282 | // of these in case they are happening too fast, which is a good thing
283 | // since most of the listeners are DOM transformation templates.
284 | Reflect.set(target, prop, newValue);
285 | if (typeof prop !== "string") return true;
286 | if (Array.isArray(value)) {
287 | runtime.updates.path.push(`${dottedPath}`);
288 | } else {
289 | runtime.updates.path.push(`${dottedPath}.${prop}`);
290 | }
291 | runtime.updates.value.push(target);
292 | if (!runtime.updates.raf) {
293 | runtime.updates.raf = requestAnimationFrame(
294 | createSubscribersDispatcher(boredom),
295 | );
296 | }
297 |
298 | return true;
299 | },
300 | });
301 | objectsWithProxies.add(value);
302 | }
303 | });
304 | // boredom.app = state.app;
305 |
306 | return boredom;
307 | }
308 |
309 | /**
310 | * Runs the init function of every webComponent tag that exists in the DOM
311 | */
312 | export function runComponentsInitializer(state: AppState) {
313 | // Start by finding all bored web component tags that are in the dom:
314 | const tagsInDom = state.internal.customTags.filter((tag) =>
315 | queryComponent(tag) !== undefined
316 | );
317 |
318 | const components = state.internal.components;
319 | for (const [tagName, code] of components.entries()) {
320 | // Only proceed if there is a registered init function and if the tag is in the DOM
321 | if (code === null || !tagsInDom.includes(tagName)) continue;
322 | // From this point forward, the `code` will be run for tags that are in the dom, this
323 | // way, it prevents the `code` function from being run more than once if a given component
324 | // `code` dynamically creates another component that is not yet in the DOM by now.
325 |
326 | const componentClass = queryComponent(tagName);
327 |
328 | if (!componentClass) {
329 | console.log(
330 | `<${tagName}> is not yet in the DOM. The associated JS script will be called when the component is connected.`,
331 | );
332 | return;
333 | }
334 |
335 | code(state as any, { index: 0, name: tagName, data: undefined })(
336 | componentClass,
337 | );
338 | }
339 |
340 | return;
341 | }
342 |
343 | /**
344 | * Creates a web component and runs the associated script if it has one defined.
345 | *
346 | * @param name the tagname of the component to create
347 | * @param state the
348 | * @param [detail]
349 | */
350 | export function createAndRunCode(
351 | name: string,
352 | state: AppState,
353 | detail?: WebComponentDetail,
354 | ) {
355 | // "code" is the function returned by the `webComponent()` above, it
356 | // creates the state reactive proxy and calls the initialization from
357 | // the corresponding template .js file
358 | const code = state.internal.components.get(name);
359 | if (code) {
360 | const info = { ...detail, tagName: name };
361 | return createComponent(name, code(state as any, info));
362 | }
363 |
364 | return createComponent(name);
365 | }
366 |
--------------------------------------------------------------------------------
/src/dom.ts:
--------------------------------------------------------------------------------
1 | import { createSlotsAccessor } from "./bore";
2 | import type { LoadedFunction } from "./types";
3 |
4 | /**
5 | * It dynamically imports all scripts that have a filename that matches the
6 | * provided name. This is intended to load the string of the `` `data-component` attribute.
7 | *
8 | * @param names - The list of names to try to dynamically load. It appends .js to them.
9 | *
10 | * @returns A Map of the registered web-components tag names, and their corresponding
11 | * dynamically loaded .js file exported function (or null if there is no .js file).
12 | */
13 | export const dynamicImportScripts = async (names: string[]) => {
14 | const result: Map = new Map();
15 |
16 | for (let i = 0; i < names.length; ++i) {
17 | // Load the associated script if it exists
18 | const scriptLocation = query(`script[src*="${names[i]}"]`)?.getAttribute(
19 | "src",
20 | );
21 | let f: null | LoadedFunction = null;
22 | if (scriptLocation) {
23 | // Dynamic import it and get the default export
24 | try {
25 | const exports = await import(scriptLocation);
26 | for (const exported of Object.keys(exports)) {
27 | f = exports[exported];
28 | break;
29 | }
30 | result.set(names[i], f);
31 | } catch (e) {
32 | console.error(`Unable to import "${scriptLocation}"`, e);
33 | }
34 | }
35 | }
36 |
37 | return result;
38 | };
39 |
40 | /**
41 | * Set of helper functions to handle the DOM.
42 | *
43 | * Reads the DOM and queries for all the `` tags that
44 | * have a `data-component=""`. For each of them:
45 | * - Registers a web-component with that tag name (@see `component()` local function).
46 | * - All attributes in the `` tag are passed as is to the web component when connected.
47 | * - Returns the registered web component name.
48 | *
49 | * @returns A list of the tag names that were registered.
50 | */
51 | export const searchForComponents = () => {
52 | // Query all templates with a data-component attribute, these will be used to
53 | // create custom web components with the tag name similar to the id
54 | return Array.from(queryAll("template[data-component]"))
55 | .filter((elem): elem is HTMLElement => elem instanceof HTMLElement)
56 | .map((t) => {
57 | const result: { name: string; attributes: [string, string][] } = {
58 | name: "",
59 | attributes: [],
60 | };
61 |
62 | for (const attribute in t.dataset) {
63 | if (attribute === "component") {
64 | result.name = t.dataset[attribute] ?? "";
65 | } else {
66 | // Attribute is not "component", pass it as is but
67 | // assume a value of true (""):
68 | result.attributes.push([
69 | decamelize(attribute),
70 | t.dataset[attribute] ?? "",
71 | ]);
72 | }
73 | }
74 | if (result.name === "") {
75 | throw new Error(
76 | `A was found with an invalid data-component: "${t.dataset.component}"`,
77 | );
78 | }
79 | return result;
80 | })
81 | .map(({ name, attributes }) => {
82 | // Create and register the web component:
83 | component(name, { attributes });
84 | return name;
85 | });
86 | };
87 |
88 | /**
89 | * Creates a new web-component and registers it with the provided tag name
90 | */
91 | export const createComponent = (
92 | name: string,
93 | update?: (c: Bored) => void,
94 | ): Bored => {
95 | const element = create(name);
96 | if (!isBored(element)) {
97 | const error = `The tag name "${name}" is not a BoreDOM component.
98 | \n"createComponent" only accepts tag-names with matching tags that have a data-component attribute in them.`;
99 |
100 | console.error(error);
101 | throw new Error(error);
102 | }
103 |
104 | if (update) {
105 | element.renderCallback = update;
106 | }
107 |
108 | return element;
109 | };
110 |
111 | /**
112 | * Queries for the component tag name in the DOM. Throws error if not found.
113 | */
114 | export const queryComponent = (q: string): Bored | undefined => {
115 | const elem = query(q);
116 |
117 | if (elem === null || !(isBored(elem))) {
118 | return undefined;
119 | }
120 |
121 | return elem;
122 | };
123 |
124 | /** `document.querySelector` */
125 | export const query = (query: string) => document.querySelector(query);
126 | /** `document.querySelectorAll` */
127 | export const queryAll = (query: string) => document.querySelectorAll(query);
128 | /** `document.createElement` */
129 | export const create = (tagName: string, children?: HTMLElement[]) => {
130 | const e = document.createElement(tagName);
131 | if (children && Array.isArray(children) && children.length > 0) {
132 | children.map((c) => e.appendChild(c));
133 | }
134 | return e;
135 | };
136 | export const queryHtml = (q: string): HTMLElement => {
137 | const html = query(q);
138 | if (!(html instanceof HTMLElement)) {
139 | throw new Error(`Cannot find an HTMLElement with selector "${q}"`);
140 | }
141 |
142 | return html;
143 | };
144 | /** `dispatchEvent(new CustomEvent(name, { detail }))` */
145 | export const dispatch = (name: string, detail?: any) => {
146 | if (document.readyState === "loading") {
147 | addEventListener(
148 | "DOMContentLoaded",
149 | () => dispatchEvent(new CustomEvent(name, { detail })),
150 | );
151 | } else {
152 | dispatchEvent(new CustomEvent(name, { detail }));
153 | }
154 | };
155 | /** Calls addEventListener, returns the function used as listener */
156 | export const handle = (name: string, f: (detail: T) => void) => {
157 | const handler = (e: CustomEvent) => f(e.detail);
158 | addEventListener(name as any, handler);
159 | return handler;
160 | };
161 |
162 | export const isTemplate = (e: HTMLElement): e is HTMLTemplateElement =>
163 | e instanceof HTMLTemplateElement;
164 | const isObject = (t: any): t is object => typeof t === "object";
165 | const isFunction = (t: any): t is Function => typeof t === "function";
166 | const isBored = (t: unknown): t is Bored =>
167 | isObject(t) && "isBored" in t && Boolean(t.isBored);
168 | const camelize = (str: string) => {
169 | return str.split("-")
170 | .map((item, index) =>
171 | index
172 | ? item.charAt(0).toUpperCase() + item.slice(1).toLowerCase()
173 | : item.toLowerCase()
174 | )
175 | .join("");
176 | };
177 | const decamelize = (str: string): string => {
178 | if (
179 | str === "" || !str.split("").some((char) => char !== char.toLowerCase())
180 | ) {
181 | return str;
182 | }
183 |
184 | let result = "";
185 | for (let i = 0; i < str.length; i++) {
186 | const char = str[i];
187 | if (char === char.toUpperCase() && i !== 0) {
188 | result += "-";
189 | }
190 | result += char.toLowerCase();
191 | }
192 | return result;
193 | };
194 |
195 | const firstWebComponentParent = (elem: HTMLElement) => {
196 | let currentParent = elem.parentElement;
197 |
198 | while (currentParent && currentParent.tagName.indexOf("-") < 0) {
199 | currentParent = currentParent.parentElement;
200 | }
201 |
202 | return currentParent;
203 | };
204 |
205 | type StartsWithOn = `on${string}`;
206 | type StartsWithQueriedOn = `queriedOn${string}`;
207 | const isStartsWithOn = (s: string): s is StartsWithOn => s.startsWith("on");
208 | const isStartsWithQueriedOn = (s: string): s is StartsWithQueriedOn =>
209 | s.startsWith("queriedOn");
210 | const getEventName = (s: StartsWithOn | StartsWithQueriedOn) => {
211 | if (isStartsWithOn(s)) {
212 | return s.slice(2).toLowerCase();
213 | }
214 |
215 | return s.slice(9).toLowerCase();
216 | };
217 |
218 | export abstract class Bored extends HTMLElement {
219 | abstract renderCallback: (elem: Bored) => void;
220 | }
221 |
222 | const component = (tag: string, props: {
223 | /** Shadow-root content for this component */
224 | shadow?: string;
225 | shadowrootmode?: ShadowRootMode;
226 | /** Style for this component, placed in a
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
23 |
24 |