refactor: create monorepo
This commit is contained in:
parent
b21ee84629
commit
30977d1357
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@ -10,10 +10,14 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: "20"
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
run: pnpm install
|
||||
|
||||
- name: Start build
|
||||
run: npm run build
|
||||
run: pnpm run build
|
||||
|
5
.github/workflows/deploy.yml
vendored
5
.github/workflows/deploy.yml
vendored
@ -24,8 +24,11 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
#
|
||||
|
||||
steps:
|
||||
- name: Go to server
|
||||
run: cd ./packages/server
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
|
||||
|
12
.github/workflows/format-check.yml
vendored
12
.github/workflows/format-check.yml
vendored
@ -10,10 +10,14 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
node-version: "20"
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
run: pnpm install
|
||||
|
||||
- name: Check formatting
|
||||
run: npm run format:check
|
||||
- name: Start build
|
||||
run: pnpm run format:check
|
||||
|
128
.gitignore
vendored
128
.gitignore
vendored
@ -1,127 +1 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
node_modules
|
@ -1 +1,2 @@
|
||||
pnpm-lock.yaml
|
||||
pnpm-lock.yaml
|
||||
packages/**/dist/**/*
|
5
lerna.json
Normal file
5
lerna.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "0.0.0",
|
||||
"npmClient": "pnpm"
|
||||
}
|
63
package.json
63
package.json
@ -1,57 +1,20 @@
|
||||
{
|
||||
"name": "txtdot",
|
||||
"version": "1.7.0",
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"description": "txtdot is an HTTP proxy that parses only text, links and pictures from pages reducing internet bandwidth usage, removing ads and heavy scripts",
|
||||
"main": "dist/app.js",
|
||||
"dependencies": {
|
||||
"@fastify/static": "^7.0.3",
|
||||
"@fastify/swagger": "^8.14.0",
|
||||
"@fastify/swagger-ui": "^3.0.0",
|
||||
"@fastify/view": "^9.0.0",
|
||||
"@txtdot/plugins": "^1.1.1",
|
||||
"@txtdot/sdk": "^1.1.1",
|
||||
"axios": "^1.6.8",
|
||||
"dompurify": "^3.1.2",
|
||||
"dotenv": "^16.3.1",
|
||||
"ejs": "^3.1.10",
|
||||
"fastify": "^4.26.2",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"ip-range-check": "^0.2.0",
|
||||
"json-schema-to-ts": "^3.0.1",
|
||||
"linkedom": "^0.16.11",
|
||||
"micromatch": "^4.0.5",
|
||||
"sharp": "^0.33.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/ejs": "^3.1.5",
|
||||
"@types/jsdom": "^21.1.6",
|
||||
"@types/micromatch": "^4.0.7",
|
||||
"@types/node": "^20.12.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
||||
"@typescript-eslint/parser": "^7.7.0",
|
||||
"clean-css-cli": "^5.6.3",
|
||||
"copyfiles": "^2.4.1",
|
||||
"eslint": "^8.56.0",
|
||||
"prettier": "^3.1.1",
|
||||
"tsc-watch": "^6.2.0",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"dependencies": {},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "npm run clean-css && copyfiles ./templates/*.ejs ./templates/**/*.ejs .env ./dist/ && tsc",
|
||||
"start": "cd ./dist && node ./src/app.js",
|
||||
"start:docker": "node ./src/app.js",
|
||||
"clean-css": "cleancss --batch static/*.css -o dist/static --batch-suffix \"\"",
|
||||
"dev": "tsc-watch --onSuccess \"node ./dist/src/app.js\"",
|
||||
"build": "lerna run build",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check ."
|
||||
},
|
||||
"keywords": [],
|
||||
"authors": [
|
||||
"Artemy Egorov <me@artegoser.ru> https://github.com/artegoser",
|
||||
"DarkCat09 <darkcat09@vivaldi.net> https://dc09.ru/"
|
||||
],
|
||||
"license": "MIT"
|
||||
"devDependencies": {
|
||||
"lerna": "^8.1.2",
|
||||
"eslint": "^8.56.0",
|
||||
"prettier": "^3.1.1",
|
||||
"tsc-watch": "^6.2.0",
|
||||
"typescript": "^5.4.5",
|
||||
"@types/node": "^20.12.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.7.0",
|
||||
"@typescript-eslint/parser": "^7.7.0"
|
||||
}
|
||||
}
|
||||
|
127
packages/plugins/.gitignore
vendored
Normal file
127
packages/plugins/.gitignore
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
21
packages/plugins/LICENSE
Normal file
21
packages/plugins/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Artemy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
3
packages/plugins/README.md
Normal file
3
packages/plugins/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# plugins
|
||||
|
||||
txtdot official plugins
|
27
packages/plugins/package.json
Normal file
27
packages/plugins/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@txtdot/plugins",
|
||||
"version": "1.1.1",
|
||||
"description": "Official txtdot plugins",
|
||||
"main": "dist/lib.js",
|
||||
"types": "dist/lib.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/txtdot/plugins.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prepublishOnly": "pnpm run build"
|
||||
},
|
||||
"keywords": [],
|
||||
"authors": [
|
||||
"Artemy Egorov <me@artegoser.ru> https://github.com/artegoser"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mozilla/readability": "^0.5.0",
|
||||
"@txtdot/sdk": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
}
|
5
packages/plugins/src/engines/index.ts
Normal file
5
packages/plugins/src/engines/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import StackOverflow from "./stackoverflow";
|
||||
import Readability from "./readability";
|
||||
import SearX from "./searx";
|
||||
|
||||
export { StackOverflow, Readability, SearX };
|
28
packages/plugins/src/engines/readability.ts
Normal file
28
packages/plugins/src/engines/readability.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Readability as OReadability } from "@mozilla/readability";
|
||||
import { EngineParseError } from "@txtdot/sdk/dist/types/errors";
|
||||
|
||||
import { Engine } from "@txtdot/sdk";
|
||||
|
||||
const Readability = new Engine(
|
||||
"Readability",
|
||||
"Engine for parsing content with Readability",
|
||||
["*"]
|
||||
);
|
||||
|
||||
Readability.route("*path", async (input, ro) => {
|
||||
const reader = new OReadability(input.parseDom().window.document);
|
||||
const parsed = reader.parse();
|
||||
|
||||
if (!parsed) {
|
||||
throw new EngineParseError(`(${ro.q.path}). [${Readability.name}]`);
|
||||
}
|
||||
|
||||
return {
|
||||
content: parsed.content,
|
||||
textContent: parsed.textContent,
|
||||
title: parsed.title,
|
||||
lang: parsed.lang,
|
||||
};
|
||||
});
|
||||
|
||||
export default Readability;
|
58
packages/plugins/src/engines/searx.ts
Normal file
58
packages/plugins/src/engines/searx.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { Engine } from "@txtdot/sdk";
|
||||
import { HandlerInput, Route } from "@txtdot/sdk/dist/types/handler";
|
||||
|
||||
const SearX = new Engine("SearX", "Engine for searching with 'SearXNG'", [
|
||||
"searx.*",
|
||||
]);
|
||||
|
||||
async function search(
|
||||
input: HandlerInput,
|
||||
ro: Route<{ search: string; pageno?: string }>
|
||||
) {
|
||||
const document = input.parseDom().window.document;
|
||||
const search = ro.q.search;
|
||||
const page = parseInt(ro.q.pageno || "1");
|
||||
|
||||
const page_footer = `${
|
||||
page !== 1
|
||||
? `<a href="${ro.reverse({ search, pageno: page - 1 })}">Previous </a>|`
|
||||
: ""
|
||||
}<a href="${ro.reverse({ search, pageno: page + 1 })}"> Next</a>`;
|
||||
|
||||
const articles = Array.from(document.querySelectorAll(".result"));
|
||||
const articles_parsed = articles.map((a) => {
|
||||
const parsed = {
|
||||
url:
|
||||
(a.getElementsByClassName("url_wrapper")[0] as HTMLAnchorElement)
|
||||
.href || "",
|
||||
title:
|
||||
(a.getElementsByTagName("h3")[0] as HTMLHeadingElement).textContent ||
|
||||
"",
|
||||
content:
|
||||
(a.getElementsByClassName("content")[0] as HTMLDivElement)
|
||||
.textContent || "",
|
||||
};
|
||||
|
||||
return {
|
||||
html: `<a href="${parsed.url}">${parsed.title}</a><p>${parsed.content}</p><hr>`,
|
||||
text: `${parsed.title} (${parsed.url})\n${parsed.content}\n---\n\n`,
|
||||
};
|
||||
});
|
||||
|
||||
const content = `${articles_parsed
|
||||
.map((a) => a.html)
|
||||
.join("")}${page_footer}`;
|
||||
const textContent = articles_parsed.map((a) => a.text).join("");
|
||||
|
||||
return {
|
||||
content,
|
||||
textContent,
|
||||
title: `${search} - Searx - Page ${page}`,
|
||||
lang: document.documentElement.lang,
|
||||
};
|
||||
}
|
||||
|
||||
SearX.route("/search?q=:search&pageno=:pageno", search);
|
||||
SearX.route("/search?q=:search", search);
|
||||
|
||||
export default SearX;
|
23
packages/plugins/src/engines/stackoverflow/index.ts
Normal file
23
packages/plugins/src/engines/stackoverflow/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Engine } from "@txtdot/sdk";
|
||||
import questions from "./questions";
|
||||
import users from "./users";
|
||||
|
||||
const StackOverflow = new Engine(
|
||||
"StackOverflow",
|
||||
"Engine for 'StackOverflow' and other 'Stack' sites. Available routes: '/questions/' and '/users/'",
|
||||
[
|
||||
"stackoverflow.com",
|
||||
"*.stackoverflow.com",
|
||||
"*.stackexchange.com",
|
||||
"askubuntu.com",
|
||||
"stackapps.com",
|
||||
"mathoverflow.net",
|
||||
"superuser.com",
|
||||
"serverfault.com",
|
||||
]
|
||||
);
|
||||
|
||||
StackOverflow.route("/questions/:id/*slug", questions);
|
||||
StackOverflow.route("/users/:id/*slug", users);
|
||||
|
||||
export default StackOverflow;
|
48
packages/plugins/src/engines/stackoverflow/questions.ts
Normal file
48
packages/plugins/src/engines/stackoverflow/questions.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { HandlerInput, Route } from "@txtdot/sdk/dist/types/handler";
|
||||
|
||||
async function questions(
|
||||
input: HandlerInput,
|
||||
ro: Route<{ id: string; slug: string }>
|
||||
) {
|
||||
const document = input.parseDom().window.document;
|
||||
|
||||
const questionEl = document.getElementById("question");
|
||||
const question = postParser(questionEl);
|
||||
|
||||
const title = document.querySelector(".question-hyperlink")?.innerHTML || "";
|
||||
|
||||
const allAnswers = [...document.querySelectorAll(".answer")];
|
||||
const answers = allAnswers.map((a) => postParser(a));
|
||||
|
||||
return {
|
||||
content: `${question}<hr>${answers.length} answers <hr>${answers.join(
|
||||
"<hr>"
|
||||
)}`,
|
||||
textContent: `${ro.q.id}/${ro.q.slug}\nText output not supported`, // TODO
|
||||
title,
|
||||
lang: document.documentElement.lang,
|
||||
};
|
||||
}
|
||||
|
||||
function postParser(el: Element | null): string {
|
||||
if (!el) {
|
||||
return "";
|
||||
}
|
||||
const body = el.querySelector(".js-post-body")?.innerHTML || "";
|
||||
const voteCount = el.querySelector(".js-vote-count")?.textContent || "";
|
||||
|
||||
const footer = [...el.querySelectorAll(".post-signature")].map((el) => {
|
||||
const userName = el.querySelector(".user-details a")?.textContent || "";
|
||||
const userUrl =
|
||||
(el.querySelector(".user-details a") as HTMLAnchorElement)?.href || "";
|
||||
const userTitle = el.querySelector(".user-action-time")?.textContent || "";
|
||||
|
||||
return `<h4>${userTitle}${
|
||||
userUrl ? ` by <a href="${userUrl}">${userName}</a>` : ""
|
||||
}</h4>`;
|
||||
});
|
||||
|
||||
return `<h3>${voteCount} votes</h3>${body}${footer.join("")}`;
|
||||
}
|
||||
|
||||
export default questions;
|
36
packages/plugins/src/engines/stackoverflow/users.ts
Normal file
36
packages/plugins/src/engines/stackoverflow/users.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { HandlerInput, Route } from '@txtdot/sdk/dist/types/handler';
|
||||
|
||||
async function users(
|
||||
input: HandlerInput,
|
||||
ro: Route<{ id: string; slug: string }>
|
||||
) {
|
||||
const document = input.parseDom().window.document;
|
||||
|
||||
const userInfo =
|
||||
document.querySelector('.md\\:ai-start > div:nth-child(2)')?.textContent ||
|
||||
'';
|
||||
|
||||
const topPosts = [
|
||||
...(document.querySelector('#js-top-posts > div:nth-child(2)')?.children ||
|
||||
[]),
|
||||
]
|
||||
.map((el) => {
|
||||
const title = el.querySelector('a')?.textContent || '';
|
||||
const url = el.querySelector('a')?.href || '';
|
||||
const votes = el.querySelector('.s-badge__votes')?.textContent || '';
|
||||
const type =
|
||||
el.querySelector('.iconAnswer, .iconQuestion')?.textContent || '';
|
||||
|
||||
return `<strong>${type} (${votes}) </strong><a href="${url}">${title}</a>`;
|
||||
})
|
||||
.join('<br/>');
|
||||
|
||||
return {
|
||||
content: `${userInfo}<hr><h3>Top Posts</h3>${topPosts}`,
|
||||
textContent: `${ro.q.id}/${ro.q.slug}\n`, // TODO
|
||||
title: document.querySelector('title')?.textContent || '',
|
||||
lang: document.documentElement.lang,
|
||||
};
|
||||
}
|
||||
|
||||
export default users;
|
9
packages/plugins/src/lib.ts
Normal file
9
packages/plugins/src/lib.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import * as engines from "./engines";
|
||||
|
||||
export { engines };
|
||||
|
||||
export const engineList = [
|
||||
engines.StackOverflow,
|
||||
engines.SearX,
|
||||
engines.Readability,
|
||||
];
|
109
packages/plugins/tsconfig.json
Normal file
109
packages/plugins/tsconfig.json
Normal file
@ -0,0 +1,109 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true /* Save .tsbuildinfo files to allow for incremental compilation of projects. */,
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "ES2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
"resolveJsonModule": true /* Enable importing .json files. */,
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
|
||||
// "declarationMap": true /* Create sourcemaps for d.ts files. */,
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist/" /* Specify an output folder for all emitted files. */,
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
2
packages/sdk/.gitattributes
vendored
Normal file
2
packages/sdk/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
127
packages/sdk/.gitignore
vendored
Normal file
127
packages/sdk/.gitignore
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
21
packages/sdk/LICENSE
Normal file
21
packages/sdk/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Artemy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
3
packages/sdk/README.md
Normal file
3
packages/sdk/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# TxtDot SDK
|
||||
|
||||
Create plugins for TxtDot.
|
28
packages/sdk/package.json
Normal file
28
packages/sdk/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@txtdot/sdk",
|
||||
"version": "1.1.1",
|
||||
"description": "SDK for creating plugins for TxtDot",
|
||||
"main": "dist/lib.js",
|
||||
"types": "dist/lib.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/txtdot/sdk.git"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prepublishOnly": "pnpm run build"
|
||||
},
|
||||
"keywords": [],
|
||||
"authors": [
|
||||
"Artemy Egorov <me@artegoser.ru> https://github.com/artegoser"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"linkedom": "^0.16.11",
|
||||
"route-parser": "^0.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/route-parser": "^0.1.7",
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
}
|
52
packages/sdk/src/engine.ts
Normal file
52
packages/sdk/src/engine.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import Route from "route-parser";
|
||||
|
||||
import {
|
||||
HandlerInput,
|
||||
IHandlerOutput,
|
||||
EngineFunction,
|
||||
RouteValues,
|
||||
} from "./types/handler";
|
||||
|
||||
import { NoHandlerFoundError } from "./types/errors";
|
||||
|
||||
interface IRoute<TParams extends RouteValues> {
|
||||
route: Route;
|
||||
handler: EngineFunction<TParams>;
|
||||
}
|
||||
|
||||
export class Engine {
|
||||
name: string;
|
||||
description: string;
|
||||
domains: string[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
routes: IRoute<any>[] = [];
|
||||
constructor(name: string, description: string, domains: string[] = []) {
|
||||
this.domains = domains;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
route<TParams extends RouteValues>(
|
||||
path: string,
|
||||
handler: EngineFunction<TParams>
|
||||
) {
|
||||
this.routes.push({ route: new Route<TParams>(path), handler });
|
||||
}
|
||||
|
||||
async handle(input: HandlerInput): Promise<IHandlerOutput> {
|
||||
const url = new URL(input.getUrl());
|
||||
const path = url.pathname + url.search + url.hash;
|
||||
for (const route of this.routes) {
|
||||
const match = route.route.match(path);
|
||||
|
||||
if (match) {
|
||||
return await route.handler(input, {
|
||||
q: match,
|
||||
reverse: (req) => route.route.reverse(req),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
throw new NoHandlerFoundError(`${path}. [${this.name}]`);
|
||||
}
|
||||
}
|
3
packages/sdk/src/lib.ts
Normal file
3
packages/sdk/src/lib.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { Engine } from "./engine";
|
||||
|
||||
export { Engine };
|
24
packages/sdk/src/types/errors.ts
Normal file
24
packages/sdk/src/types/errors.ts
Normal file
@ -0,0 +1,24 @@
|
||||
export abstract class TxtDotError extends Error {
|
||||
code: number;
|
||||
name: string;
|
||||
description: string;
|
||||
|
||||
constructor(code: number, name: string, description: string) {
|
||||
super(description);
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
export class NoHandlerFoundError extends TxtDotError {
|
||||
constructor(message: string) {
|
||||
super(404, "NoHandlerFoundError", `No handler found for: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export class EngineParseError extends TxtDotError {
|
||||
constructor(message: string) {
|
||||
super(422, "EngineParseError", `Parse error: ${message}`);
|
||||
}
|
||||
}
|
78
packages/sdk/src/types/handler.ts
Normal file
78
packages/sdk/src/types/handler.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { parseHTML } from "linkedom";
|
||||
import { Engine } from "../engine";
|
||||
|
||||
export class HandlerInput {
|
||||
private data: string;
|
||||
private url: string;
|
||||
private dom?: Window;
|
||||
|
||||
constructor(data: string, url: string) {
|
||||
this.data = data;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
getUrl(): string {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
parseDom(): Window {
|
||||
if (this.dom) {
|
||||
return this.dom;
|
||||
}
|
||||
|
||||
this.dom = parseHTML(this.data);
|
||||
return this.dom;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IHandlerOutput {
|
||||
content: string;
|
||||
textContent: string;
|
||||
title?: string;
|
||||
lang?: string;
|
||||
}
|
||||
|
||||
export const handlerSchema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
content: {
|
||||
type: "string",
|
||||
},
|
||||
textContent: {
|
||||
type: "string",
|
||||
},
|
||||
title: {
|
||||
type: "string",
|
||||
},
|
||||
lang: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export interface Engines {
|
||||
[key: string]: Engine;
|
||||
}
|
||||
|
||||
export type EngineMatch<TParams extends RouteValues> = {
|
||||
pattern: string | string[];
|
||||
engine: EngineFunction<TParams>;
|
||||
};
|
||||
|
||||
export interface RouteValues {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export type EngineFunction<TParams extends RouteValues> = (
|
||||
input: HandlerInput,
|
||||
ro: Route<TParams>
|
||||
) => Promise<IHandlerOutput>;
|
||||
|
||||
export type EnginesMatch<TParams extends RouteValues> = EngineMatch<TParams>[];
|
||||
|
||||
export interface Route<TParams extends RouteValues> {
|
||||
q: TParams;
|
||||
reverse: (req: { [K in keyof TParams]: string | number | boolean }) =>
|
||||
| string
|
||||
| false;
|
||||
}
|
109
packages/sdk/tsconfig.json
Normal file
109
packages/sdk/tsconfig.json
Normal file
@ -0,0 +1,109 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true /* Save .tsbuildinfo files to allow for incremental compilation of projects. */,
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "ES2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
"resolveJsonModule": true /* Enable importing .json files. */,
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
|
||||
// "declarationMap": true /* Create sourcemaps for d.ts files. */,
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist/" /* Specify an output folder for all emitted files. */,
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
14
packages/server/.eslintrc.json
Normal file
14
packages/server/.eslintrc.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {}
|
||||
}
|
2
packages/server/.gitattributes
vendored
Normal file
2
packages/server/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
127
packages/server/.gitignore
vendored
Normal file
127
packages/server/.gitignore
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
110
packages/server/README.md
Normal file
110
packages/server/README.md
Normal file
@ -0,0 +1,110 @@
|
||||
<h1 align="center">
|
||||
<a href="https://github.com/TxtDot/txtdot"><img src="https://github.com/TxtDot/.github/blob/main/imgs/TXTDot%20gh.png?raw=true" alt="txt." width="200"></a>
|
||||
<br>
|
||||
<a href="https://txtdot.github.io/documentation"><img alt="Documentation" src="https://img.shields.io/badge/Documentation-green"></a>
|
||||
<a href="https://github.com/TxtDot/instances"><img alt="Instances" src="https://img.shields.io/badge/Instances-green"></a>
|
||||
<br>
|
||||
<a href="https://github.com/TxtDot/txtdot/blob/main/LICENSE"><img alt="MIT license" src="https://img.shields.io/github/license/txtdot/txtdot?color=blue"></a>
|
||||
<a href="https://github.com/TxtDot/txtdot/releases/latest"><img alt="Latest release" src="https://img.shields.io/github/v/release/TxtDot/txtdot?display_name=release"></a>
|
||||
<a href="https://matrix.to/#/#txtdot:matrix.org"><img alt="Matrix chat" src="https://img.shields.io/badge/chat-matrix-blue"></a>
|
||||
</h1>
|
||||
|
||||
HTTP proxy that parses only text, links and pictures from pages
|
||||
reducing internet traffic, removing ads and heavy scripts.
|
||||
Mozilla's Readability library is used under the hood.
|
||||
|
||||
## Features
|
||||
|
||||
- Server-side page simplification
|
||||
- Media proxy
|
||||
- Image compression with Sharp
|
||||
- Rendering client-side apps (Vanilla, React, Vue, etc) with [webder](https://github.com/TxtDot/webder)
|
||||
- Search with SearXNG
|
||||
- Custom parsers for StackOverflow and SearXNG
|
||||
- Handy API endpoints
|
||||
- No client JavaScript
|
||||
- Some kind of Material Design 3
|
||||
- Customization with plugins, see [@txtdot/sdk](https://github.com/TxtDot/sdk) and [@txtdot/plugins](https://github.com/TxtDot/plugins)
|
||||
|
||||
## Running
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Production
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
npm run start
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Screenshots
|
||||
|
||||
<div align="center">
|
||||
<img src="https://raw.githubusercontent.com/TxtDot/.github/main/imgs/ui_url_input.png" alt="Main page with URL input field">
|
||||
<img src="https://raw.githubusercontent.com/TxtDot/.github/main/imgs/ui_search_page.png" alt="SearXNG search results page">
|
||||
</div>
|
||||
|
||||
## Performance tests
|
||||
|
||||
txtdot is a great tool in case of slow internet connection or weak signal.
|
||||
Here is the comparision of performance metrics from pagespeed.web.dev
|
||||
between original page and proxied one.
|
||||
|
||||
"Mobile" test includes "Slow 4G" artificial network throttling.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
| | Original page | Proxied through txtdot |
|
||||
| :------------------------------- | :-------------------: | :--------------------: |
|
||||
| [Habr][habr-link] Desktop | ![56%][habr-do-img] | ![99%][habr-dt-img] |
|
||||
| [Habr][habr-link] Mobile | ![21%][habr-mo-img] | ![100%][habr-mt-img] |
|
||||
| [Medium][medium-link] Desktop | ![44%][medium-do-img] | ![100%][medium-dt-img] |
|
||||
| [Medium][medium-link] Mobile | ![36%][medium-mo-img] | ![100%][medium-mt-img] |
|
||||
| [Nginx Blog][nginx-link] Desktop | ![53%][nginx-do-img] | ![100%][nginx-dt-img] |
|
||||
| [Nginx Blog][nginx-link] Mobile | ![26%][nginx-mo-img] | ![100%][nginx-mt-img] |
|
||||
|
||||
[habr-link]: https://habr.com/ru/articles/780692/
|
||||
[habr-do-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/habr/desktop_orig.png
|
||||
[habr-dt-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/habr/desktop_txtdot.png
|
||||
[habr-mo-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/habr/mobile_orig.png
|
||||
[habr-mt-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/habr/mobile_txtdot.png
|
||||
[medium-link]: https://levelup.gitconnected.com/proxy-servers-how-proxies-work-0ec083fc1030
|
||||
[medium-do-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/medium/desktop_orig.png
|
||||
[medium-dt-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/medium/desktop_txtdot.png
|
||||
[medium-mo-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/medium/mobile_orig.png
|
||||
[medium-mt-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/medium/mobile_txtdot.png
|
||||
[nginx-link]: https://www.nginx.com/blog/rate-limiting-nginx/
|
||||
[nginx-do-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/nginx-blog/desktop_orig.png
|
||||
[nginx-dt-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/nginx-blog/desktop_txtdot.png
|
||||
[nginx-mo-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/nginx-blog/mobile_orig.png
|
||||
[nginx-mt-img]: https://raw.githubusercontent.com/TxtDot/.github/main/tests/nginx-blog/mobile_txtdot.png
|
||||
|
||||
</details>
|
||||
|
||||
## Credits
|
||||
|
||||
- [Readability.js](https://github.com/mozilla/readability)
|
||||
- [🔗 LinkeDOM](https://github.com/WebReflection/linkedom)
|
||||
- [Fastify web framework](https://github.com/fastify/fastify)
|
||||
- [EJS](https://github.com/mde/ejs)
|
||||
- [Axios](https://github.com/axios/axios)
|
||||
- [DOMPurify](https://github.com/cure53/DOMPurify)
|
||||
- [Sharp](https://github.com/lovell/sharp)
|
||||
- [MicroMatch](https://github.com/micromatch/micromatch)
|
||||
- [RouteParser](https://github.com/rcs/route-parser)
|
||||
- [IconvLite](https://github.com/ashtuchkin/iconv-lite)
|
||||
|
||||
<a href="https://www.producthunt.com/posts/txtdot?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-txtdot" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=443317&theme=neutral" alt="txtdot - HTTP proxy that saves bandwidth, removing ads and scripts. | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
48
packages/server/package.json
Normal file
48
packages/server/package.json
Normal file
@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "txtdot",
|
||||
"version": "1.7.0",
|
||||
"private": true,
|
||||
"description": "txtdot is an HTTP proxy that parses only text, links and pictures from pages reducing internet bandwidth usage, removing ads and heavy scripts",
|
||||
"main": "dist/app.js",
|
||||
"keywords": [],
|
||||
"authors": [
|
||||
"Artemy Egorov <me@artegoser.ru> https://github.com/artegoser",
|
||||
"DarkCat09 <darkcat09@vivaldi.net> https://dc09.ru/"
|
||||
],
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "npm run clean-css && copyfiles ./templates/*.ejs ./templates/**/*.ejs .env ./dist/ && tsc",
|
||||
"start": "cd ./dist && node ./src/app.js",
|
||||
"start:docker": "node ./src/app.js",
|
||||
"clean-css": "cleancss --batch static/*.css -o dist/static --batch-suffix \"\"",
|
||||
"dev": "tsc-watch --onSuccess \"node ./dist/src/app.js\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/static": "^7.0.3",
|
||||
"@fastify/swagger": "^8.14.0",
|
||||
"@fastify/swagger-ui": "^3.0.0",
|
||||
"@fastify/view": "^9.0.0",
|
||||
"@txtdot/plugins": "workspace:*",
|
||||
"@txtdot/sdk": "workspace:*",
|
||||
"axios": "^1.6.8",
|
||||
"dompurify": "^3.1.2",
|
||||
"dotenv": "^16.3.1",
|
||||
"ejs": "^3.1.10",
|
||||
"fastify": "^4.26.2",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"ip-range-check": "^0.2.0",
|
||||
"json-schema-to-ts": "^3.0.1",
|
||||
"linkedom": "^0.16.11",
|
||||
"micromatch": "^4.0.5",
|
||||
"sharp": "^0.33.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/ejs": "^3.1.5",
|
||||
"@types/jsdom": "^21.1.6",
|
||||
"@types/micromatch": "^4.0.7",
|
||||
"clean-css-cli": "^5.6.3",
|
||||
"copyfiles": "^2.4.1"
|
||||
}
|
||||
}
|
2491
packages/server/pnpm-lock.yaml
generated
Normal file
2491
packages/server/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
7044
pnpm-lock.yaml
generated
7044
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
packages:
|
||||
- "packages/*"
|
Loading…
x
Reference in New Issue
Block a user