diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c82b042
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 TxtDot
+
+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.
diff --git a/packages/plugins/LICENSE b/packages/plugins/LICENSE
index 8ff8881..1355f0d 100644
--- a/packages/plugins/LICENSE
+++ b/packages/plugins/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 Artemy
+Copyright (c) 2024 TxtDot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/packages/plugins/package.json b/packages/plugins/package.json
index 2f1a8cd..a7d5490 100644
--- a/packages/plugins/package.json
+++ b/packages/plugins/package.json
@@ -1,6 +1,6 @@
{
"name": "@txtdot/plugins",
- "version": "1.1.1",
+ "version": "2.0.0",
"description": "Official txtdot plugins",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -19,9 +19,12 @@
"license": "MIT",
"dependencies": {
"@mozilla/readability": "^0.5.0",
- "@txtdot/sdk": "workspace:*"
+ "@txtdot/sdk": "workspace:*",
+ "html-to-text": "^9.0.5",
+ "linkedom": "^0.18.0"
},
"devDependencies": {
+ "@types/html-to-text": "^9.0.4",
"typescript": "^5.4.5"
}
}
diff --git a/packages/plugins/src/components/searchers.tsx b/packages/plugins/src/components/searchers.tsx
new file mode 100644
index 0000000..0b4f05c
--- /dev/null
+++ b/packages/plugins/src/components/searchers.tsx
@@ -0,0 +1,35 @@
+import { JSX } from '@txtdot/sdk';
+
+export function PageFooter({
+ page,
+ previous,
+ next,
+}: {
+ page: number;
+ previous: string | null;
+ next: string | null;
+}) {
+ return (
+ <>
+ {page !== 1 ? Previous : <>>}| {page} |
+ Next
+ >
+ );
+}
+
+export function ResultItem({
+ url,
+ title,
+ content,
+}: {
+ url: string;
+ title: string;
+ content: string;
+}) {
+ return (
+ <>
+ {title}
+
{content}
+ >
+ );
+}
diff --git a/packages/plugins/src/engines/readability.ts b/packages/plugins/src/engines/readability.ts
index 7e74f6c..f35d8f3 100644
--- a/packages/plugins/src/engines/readability.ts
+++ b/packages/plugins/src/engines/readability.ts
@@ -1,7 +1,7 @@
import { Readability as OReadability } from '@mozilla/readability';
-import { EngineParseError } from '@txtdot/sdk/dist/types/errors';
-import { Engine } from '@txtdot/sdk';
+import { Engine, EngineParseError } from '@txtdot/sdk';
+import { parseHTML } from 'linkedom';
const Readability = new Engine(
'Readability',
@@ -10,7 +10,7 @@ const Readability = new Engine(
);
Readability.route('*path', async (input, ro) => {
- const reader = new OReadability(input.parseDom().window.document);
+ const reader = new OReadability(input.document);
const parsed = reader.parse();
if (!parsed) {
@@ -19,7 +19,6 @@ Readability.route('*path', async (input, ro) => {
return {
content: parsed.content,
- textContent: parsed.textContent,
title: parsed.title,
lang: parsed.lang,
};
diff --git a/packages/plugins/src/engines/searx.ts b/packages/plugins/src/engines/searx.tsx
similarity index 52%
rename from packages/plugins/src/engines/searx.ts
rename to packages/plugins/src/engines/searx.tsx
index d4fa247..cb7c2d3 100644
--- a/packages/plugins/src/engines/searx.ts
+++ b/packages/plugins/src/engines/searx.tsx
@@ -1,5 +1,7 @@
-import { Engine } from '@txtdot/sdk';
-import { HandlerInput, Route } from '@txtdot/sdk/dist/types/handler';
+import { Engine, JSX } from '@txtdot/sdk';
+import { HandlerInput, Route } from '@txtdot/sdk';
+import { parseHTML } from 'linkedom';
+import { PageFooter, ResultItem } from '../components/searchers';
const SearX = new Engine('SearX', "Engine for searching with 'SearXNG'", [
'searx.*',
@@ -9,17 +11,23 @@ async function search(
input: HandlerInput,
ro: Route<{ search: string; pageno?: string }>
) {
- const document = input.parseDom().window.document;
+ const document = input.document;
const search = ro.q.search;
const page = parseInt(ro.q.pageno || '1');
- const page_footer = `${
- page !== 1
- ? `Previous |`
- : ''
- } Next`;
+ let previous: string | null;
+ let next: string | null;
+
+ if (ro.q.pageno) {
+ previous = ro.reverse({ search, pageno: page - 1 }) || null;
+ next = ro.reverse({ search, pageno: page + 1 }) || null;
+ } else {
+ previous = null;
+ next = `/search?q=${search}&pageno=${page + 1}`;
+ }
const articles = Array.from(document.querySelectorAll('.result'));
+
const articles_parsed = articles.map((a) => {
const parsed = {
url:
@@ -33,22 +41,18 @@ async function search(
.textContent || '',
};
- return {
- html: `${parsed.title}${parsed.content}
`,
- text: `${parsed.title} (${parsed.url})\n${parsed.content}\n---\n\n`,
- };
+ return ;
});
- const content = `${articles_parsed
- .map((a) => a.html)
- .join('')}${page_footer}`;
- const textContent = articles_parsed.map((a) => a.text).join('');
-
+ const content = (
+ <>
+ {articles_parsed}
+
+ >
+ );
return {
- content,
- textContent,
- title: `${search} - Searx - Page ${page}`,
- lang: document.documentElement.lang,
+ content: content,
+ title: `"${(document.getElementById('q') as HTMLInputElement).value}" - Searx - Page ${page}`,
};
}
diff --git a/packages/plugins/src/engines/stackoverflow/questions.ts b/packages/plugins/src/engines/stackoverflow/questions.tsx
similarity index 64%
rename from packages/plugins/src/engines/stackoverflow/questions.ts
rename to packages/plugins/src/engines/stackoverflow/questions.tsx
index f29ba47..3bd47e2 100644
--- a/packages/plugins/src/engines/stackoverflow/questions.ts
+++ b/packages/plugins/src/engines/stackoverflow/questions.tsx
@@ -1,10 +1,11 @@
-import { HandlerInput, Route } from '@txtdot/sdk/dist/types/handler';
+import { HandlerInput, Route } from '@txtdot/sdk';
+import { JSX } from '@txtdot/sdk';
async function questions(
input: HandlerInput,
ro: Route<{ id: string; slug: string }>
) {
- const document = input.parseDom().window.document;
+ const document = input.document;
const questionEl = document.getElementById('question');
const question = postParser(questionEl);
@@ -15,12 +16,15 @@ async function questions(
const answers = allAnswers.map((a) => postParser(a));
return {
- content: `${question}
${answers.length} answers
${answers.join(
- '
'
- )}`,
- textContent: `${ro.q.id}/${ro.q.slug}\nText output not supported`, // TODO
+ content: (
+ <>
+ {question}
+
+ {answers.length} answers
+ {answers.join(
)}
+ >
+ ),
title,
- lang: document.documentElement.lang,
};
}
@@ -37,12 +41,27 @@ function postParser(el: Element | null): string {
(el.querySelector('.user-details a') as HTMLAnchorElement)?.href || '';
const userTitle = el.querySelector('.user-action-time')?.textContent || '';
- return `${userTitle}${
- userUrl ? ` by ${userName}` : ''
- }
`;
+ return (
+
+ {userTitle}
+ {userUrl ? (
+ <>
+ by {userName}
+ >
+ ) : (
+ <>>
+ )}
+
+ );
});
- return `${voteCount} votes
${body}${footer.join('')}`;
+ return (
+ <>
+ {voteCount} votes
+ {body}
+ {footer}
+ >
+ );
}
export default questions;
diff --git a/packages/plugins/src/engines/stackoverflow/users.ts b/packages/plugins/src/engines/stackoverflow/users.tsx
similarity index 63%
rename from packages/plugins/src/engines/stackoverflow/users.ts
rename to packages/plugins/src/engines/stackoverflow/users.tsx
index 284a675..d6de4f6 100644
--- a/packages/plugins/src/engines/stackoverflow/users.ts
+++ b/packages/plugins/src/engines/stackoverflow/users.tsx
@@ -1,10 +1,11 @@
-import { HandlerInput, Route } from '@txtdot/sdk/dist/types/handler';
+import { HandlerInput, Route } from '@txtdot/sdk';
+import { JSX } from '@txtdot/sdk';
async function users(
input: HandlerInput,
ro: Route<{ id: string; slug: string }>
) {
- const document = input.parseDom().window.document;
+ const document = input.document;
const userInfo =
document.querySelector('.md\\:ai-start > div:nth-child(2)')?.textContent ||
@@ -21,15 +22,27 @@ async function users(
const type =
el.querySelector('.iconAnswer, .iconQuestion')?.textContent || '';
- return `${type} (${votes}) ${title}`;
+ return (
+ <>
+
+ {type} ({votes}){' '}
+
+ {title}
+ >
+ );
})
- .join('
');
+ .join(
);
return {
- content: `${userInfo}
Top Posts
${topPosts}`,
- textContent: `${ro.q.id}/${ro.q.slug}\n`, // TODO
+ content: (
+ <>
+ {userInfo}
+
+ Top Posts
+ {topPosts}
+ >
+ ),
title: document.querySelector('title')?.textContent || '',
- lang: document.documentElement.lang,
};
}
diff --git a/packages/plugins/src/lib.ts b/packages/plugins/src/lib.ts
index be7f77f..a06217c 100644
--- a/packages/plugins/src/lib.ts
+++ b/packages/plugins/src/lib.ts
@@ -7,3 +7,12 @@ export const engineList = [
engines.SearX,
engines.Readability,
];
+
+import { compile } from 'html-to-text';
+
+export const html2text = compile({
+ longWordSplit: {
+ forceWrapOnLimit: true,
+ },
+ selectors: [{ selector: 'img', format: 'skip' }],
+});
diff --git a/packages/plugins/tsconfig.json b/packages/plugins/tsconfig.json
index 073c804..b4a1192 100644
--- a/packages/plugins/tsconfig.json
+++ b/packages/plugins/tsconfig.json
@@ -3,9 +3,9 @@
/* 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. */
+ "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. */
@@ -13,13 +13,13 @@
/* 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. */
+ "jsx": "react" /* 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. */
+ "reactNamespace": "JSX" /* 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. */
diff --git a/packages/sdk/LICENSE b/packages/sdk/LICENSE
index 8ff8881..1355f0d 100644
--- a/packages/sdk/LICENSE
+++ b/packages/sdk/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 Artemy
+Copyright (c) 2024 TxtDot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/packages/sdk/package.json b/packages/sdk/package.json
index 9479746..fbe73a0 100644
--- a/packages/sdk/package.json
+++ b/packages/sdk/package.json
@@ -1,6 +1,6 @@
{
"name": "@txtdot/sdk",
- "version": "1.1.1",
+ "version": "2.0.0",
"description": "SDK for creating plugins for TxtDot",
"main": "dist/lib.js",
"types": "dist/lib.d.ts",
@@ -18,7 +18,7 @@
],
"license": "MIT",
"dependencies": {
- "linkedom": "^0.16.11",
+ "linkedom": "^0.18.0",
"route-parser": "^0.0.5"
},
"devDependencies": {
diff --git a/packages/sdk/src/engine.ts b/packages/sdk/src/engine.ts
index 20a9a91..601e647 100644
--- a/packages/sdk/src/engine.ts
+++ b/packages/sdk/src/engine.ts
@@ -2,9 +2,9 @@ import Route from 'route-parser';
import {
HandlerInput,
- IHandlerOutput,
EngineFunction,
RouteValues,
+ EngineOutput,
} from './types/handler';
import { NoHandlerFoundError } from './types/errors';
@@ -33,7 +33,7 @@ export class Engine {
this.routes.push({ route: new Route(path), handler });
}
- async handle(input: HandlerInput): Promise {
+ async handle(input: HandlerInput): Promise {
const url = new URL(input.getUrl());
const path = url.pathname + url.search + url.hash;
for (const route of this.routes) {
diff --git a/packages/sdk/src/jsx.ts b/packages/sdk/src/jsx.ts
new file mode 100644
index 0000000..81f921f
--- /dev/null
+++ b/packages/sdk/src/jsx.ts
@@ -0,0 +1,35 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-namespace */
+export namespace JSX {
+ export type Element = string;
+ export interface IntrinsicElements {
+ [elemName: string]: any;
+ }
+}
+
+export function createElement(
+ name: any,
+ props: { [id: string]: any },
+ ...inner: (string | string[])[]
+) {
+ const content = inner.flat().join('');
+
+ if (typeof name === 'string') {
+ props = props || {};
+ const propsstr = Object.keys(props)
+ .map((key) => {
+ const value = props[key];
+ if (key === 'className') return `class=${value}`;
+ else return `${key}=${value}`;
+ })
+ .join(' ');
+
+ return inner.length === 0
+ ? `<${name} ${propsstr}/>`
+ : `<${name} ${propsstr}>${content}${name}>`;
+ } else if (typeof name === 'function') {
+ return name(props, content);
+ } else {
+ return content;
+ }
+}
diff --git a/packages/sdk/src/lib.ts b/packages/sdk/src/lib.ts
index f7d5aca..31909b6 100644
--- a/packages/sdk/src/lib.ts
+++ b/packages/sdk/src/lib.ts
@@ -1,3 +1,38 @@
import { Engine } from './engine';
-export { Engine };
+import {
+ EngineParseError,
+ NoHandlerFoundError,
+ TxtDotError,
+} from './types/errors';
+
+import {
+ EngineFunction,
+ EngineMatch,
+ Engines,
+ RouteValues,
+ EnginesMatch,
+ HandlerInput,
+ HandlerOutput,
+ Route,
+ handlerSchema,
+} from './types/handler';
+
+import * as JSX from './jsx';
+
+export {
+ Engine,
+ EngineParseError,
+ NoHandlerFoundError,
+ TxtDotError,
+ EngineFunction,
+ EngineMatch,
+ Engines,
+ RouteValues,
+ EnginesMatch,
+ HandlerInput,
+ HandlerOutput,
+ Route,
+ handlerSchema,
+ JSX,
+};
diff --git a/packages/sdk/src/types/handler.ts b/packages/sdk/src/types/handler.ts
index 9af088a..30b2eb0 100644
--- a/packages/sdk/src/types/handler.ts
+++ b/packages/sdk/src/types/handler.ts
@@ -4,7 +4,7 @@ import { Engine } from '../engine';
export class HandlerInput {
private data: string;
private url: string;
- private dom?: Window;
+ private window?: Window;
constructor(data: string, url: string) {
this.data = data;
@@ -15,19 +15,26 @@ export class HandlerInput {
return this.url;
}
- parseDom(): Window {
- if (this.dom) {
- return this.dom;
+ get document(): Document {
+ if (this.window) {
+ return this.window.document;
}
- this.dom = parseHTML(this.data);
- return this.dom;
+ this.window = parseHTML(this.data);
+ return this.window.document;
}
}
-export interface IHandlerOutput {
+export interface HandlerOutput {
content: string;
textContent: string;
+ title: string;
+ lang: string;
+}
+
+export interface EngineOutput {
+ content: string;
+ textContent?: string;
title?: string;
lang?: string;
}
@@ -66,7 +73,7 @@ export interface RouteValues {
export type EngineFunction = (
input: HandlerInput,
ro: Route
-) => Promise;
+) => Promise;
export type EnginesMatch = EngineMatch[];
diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json
index 073c804..f6d630c 100644
--- a/packages/sdk/tsconfig.json
+++ b/packages/sdk/tsconfig.json
@@ -3,9 +3,9 @@
/* 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. */
+ "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. */
diff --git a/packages/server/package.json b/packages/server/package.json
index dd9205d..077012f 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -1,6 +1,6 @@
{
"name": "@txtdot/server",
- "version": "1.7.0",
+ "version": "1.8.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",
@@ -18,6 +18,7 @@
"dev": "tsc-watch --onSuccess \"node ./dist/src/app.js\""
},
"dependencies": {
+ "@fastify/one-line-logger": "^1.3.0",
"@fastify/static": "^7.0.3",
"@fastify/swagger": "^8.14.0",
"@fastify/swagger-ui": "^3.0.0",
@@ -32,7 +33,7 @@
"iconv-lite": "^0.6.3",
"ip-range-check": "^0.2.0",
"json-schema-to-ts": "^3.0.1",
- "linkedom": "^0.16.11",
+ "linkedom": "^0.18.0",
"micromatch": "^4.0.5",
"sharp": "^0.33.3"
},
diff --git a/packages/server/src/app.ts b/packages/server/src/app.ts
index 513b07d..b13c74f 100644
--- a/packages/server/src/app.ts
+++ b/packages/server/src/app.ts
@@ -23,7 +23,11 @@ import config from './config';
class App {
async init() {
const fastify = Fastify({
- logger: true,
+ logger: {
+ transport: {
+ target: '@fastify/one-line-logger',
+ },
+ },
trustProxy: config.env.reverse_proxy,
connectionTimeout: config.env.timeout,
});
diff --git a/packages/server/src/config/pluginConfig.ts b/packages/server/src/config/pluginConfig.ts
index 0d51d9f..3fc3dcb 100644
--- a/packages/server/src/config/pluginConfig.ts
+++ b/packages/server/src/config/pluginConfig.ts
@@ -1,5 +1,5 @@
-import { IAppConfig } from '../types/appConfig';
-import { engineList } from '@txtdot/plugins';
+import { IAppConfig } from '../types/pluginConfig';
+import { engineList, html2text } from '@txtdot/plugins';
/**
* Configuration of plugins
@@ -7,6 +7,7 @@ import { engineList } from '@txtdot/plugins';
*/
const plugin_config: IAppConfig = {
engines: [...engineList],
+ html2text,
};
export default plugin_config;
diff --git a/packages/server/src/distributor.ts b/packages/server/src/distributor.ts
index f20088a..bb1a037 100644
--- a/packages/server/src/distributor.ts
+++ b/packages/server/src/distributor.ts
@@ -5,11 +5,12 @@ import { Readable } from 'stream';
import { NotHtmlMimetypeError } from './errors/main';
import { decodeStream, parseEncodingName } from './utils/http';
import replaceHref from './utils/replace-href';
-import { parseHTML } from 'linkedom';
import { Engine } from '@txtdot/sdk';
-import { HandlerInput, IHandlerOutput } from '@txtdot/sdk/dist/types/handler';
+import { HandlerInput, HandlerOutput } from '@txtdot/sdk';
import config from './config';
+import { parseHTML } from 'linkedom';
+import { html2text } from './utils/html2text';
interface IEngineId {
[key: string]: number;
@@ -32,7 +33,7 @@ export class Distributor {
requestUrl: URL, // proxy URL
engineName?: string,
redirectPath: string = 'get'
- ): Promise {
+ ): Promise {
const urlObj = new URL(remoteUrl);
const webder_url = config.env.third_party.webder_url;
@@ -52,6 +53,7 @@ export class Distributor {
}
const engine = this.getFallbackEngine(urlObj.hostname, engineName);
+
const output = await engine.handle(
new HandlerInput(
await decodeStream(data, parseEncodingName(mime)),
@@ -59,15 +61,35 @@ export class Distributor {
)
);
+ const dom = parseHTML(output.content);
+
+ // Get text content before link replacement, because in text format we need original links
+ const stdTextContent = dom.document.documentElement.textContent;
+
// post-process
// TODO: generate dom in handler and not parse here twice
- const dom = parseHTML(output.content);
- replaceHref(dom, requestUrl, new URL(remoteUrl), engineName, redirectPath);
+ replaceHref(
+ dom.document,
+ requestUrl,
+ new URL(remoteUrl),
+ engineName,
+ redirectPath
+ );
- const purify = DOMPurify(dom.window);
- output.content = purify.sanitize(dom.document.toString());
+ const purify = DOMPurify(dom);
+ const content = purify.sanitize(dom.document.toString());
+ const title = output.title || dom.document.title;
+ const lang = output.lang || dom.document.documentElement.lang;
+ const textContent =
+ html2text(stdTextContent, output, title) ||
+ 'Text output cannot be generated.';
- return output;
+ return {
+ content,
+ textContent,
+ title,
+ lang,
+ };
}
getFallbackEngine(host: string, specified?: string): Engine {
diff --git a/packages/server/src/errors/handler.ts b/packages/server/src/errors/handler.ts
index 8b373d5..306e4c2 100644
--- a/packages/server/src/errors/handler.ts
+++ b/packages/server/src/errors/handler.ts
@@ -2,7 +2,7 @@ import { FastifyReply, FastifyRequest } from 'fastify';
import { NotHtmlMimetypeError } from './main';
import { getFastifyError } from './validation';
-import { TxtDotError } from '@txtdot/sdk/dist/types/errors';
+import { TxtDotError } from '@txtdot/sdk';
import { IGetSchema } from '../types/requests/browser';
import config from '../config';
diff --git a/packages/server/src/errors/main.ts b/packages/server/src/errors/main.ts
index ebd80a7..209d5e2 100644
--- a/packages/server/src/errors/main.ts
+++ b/packages/server/src/errors/main.ts
@@ -1,5 +1,5 @@
import config from '../config';
-import { TxtDotError } from '@txtdot/sdk/dist/types/errors';
+import { TxtDotError } from '@txtdot/sdk';
export class LocalResourceError extends TxtDotError {
constructor() {
diff --git a/packages/server/src/types/appConfig.ts b/packages/server/src/types/appConfig.ts
deleted file mode 100644
index 49bccd6..0000000
--- a/packages/server/src/types/appConfig.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { Engine } from '@txtdot/sdk';
-
-export interface IAppConfig {
- engines: Engine[];
-}
diff --git a/packages/server/src/types/pluginConfig.ts b/packages/server/src/types/pluginConfig.ts
new file mode 100644
index 0000000..634d48c
--- /dev/null
+++ b/packages/server/src/types/pluginConfig.ts
@@ -0,0 +1,14 @@
+import { Engine } from '@txtdot/sdk';
+
+type Html2TextConverter = (html: string) => string;
+
+export interface IAppConfig {
+ /**
+ * List of engines, ordered
+ */
+ engines: Engine[];
+ /**
+ * HTML to text converter, if engine doesn't support text
+ */
+ html2text?: Html2TextConverter;
+}
diff --git a/packages/server/src/types/requests/api.ts b/packages/server/src/types/requests/api.ts
index 91e49c2..a756b0f 100644
--- a/packages/server/src/types/requests/api.ts
+++ b/packages/server/src/types/requests/api.ts
@@ -2,7 +2,7 @@ import { FastifySchema, FastifyRequest } from 'fastify';
import { IApiError, errorResponseSchema } from '../../errors/api';
import { engineList } from '../../plugin_manager';
import { FromSchema } from 'json-schema-to-ts';
-import { handlerSchema } from '@txtdot/sdk/dist/types/handler';
+import { handlerSchema } from '@txtdot/sdk';
export interface IApiResponse {
data?: T;
diff --git a/packages/server/src/utils/html2text.ts b/packages/server/src/utils/html2text.ts
new file mode 100644
index 0000000..d4d60c0
--- /dev/null
+++ b/packages/server/src/utils/html2text.ts
@@ -0,0 +1,18 @@
+import { EngineOutput } from '@txtdot/sdk/dist/types/handler';
+import config from '../config';
+
+function setTitle(body: string | null, title: string) {
+ if (!body) return null;
+ return `${title.toUpperCase()}\n${'='.repeat(title.length)}\n\n${body}`;
+}
+
+export function html2text(
+ stdTextContent: string | null,
+ output: EngineOutput,
+ title: string
+) {
+ if (output.textContent) return output.textContent;
+ else if (config.plugin.html2text)
+ return setTitle(config.plugin.html2text(output.content), title);
+ else return setTitle(stdTextContent, title);
+}
diff --git a/packages/server/src/utils/replace-href.ts b/packages/server/src/utils/replace-href.ts
index 4c38e66..0b2fd0e 100644
--- a/packages/server/src/utils/replace-href.ts
+++ b/packages/server/src/utils/replace-href.ts
@@ -2,13 +2,12 @@ import config from '../config';
import { generateParserUrl, generateProxyUrl } from './generate';
export default function replaceHref(
- dom: Window,
+ doc: Document,
requestUrl: URL,
remoteUrl: URL,
engine?: string,
redirectPath: string = 'get'
) {
- const doc: Document = dom.window.document;
const parserUrl = (href: string) =>
generateParserUrl(requestUrl, remoteUrl, href, engine, redirectPath);
diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json
index b7d87b0..f772019 100644
--- a/packages/server/tsconfig.json
+++ b/packages/server/tsconfig.json
@@ -3,9 +3,9 @@
/* 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. */
+ "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. */
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2169f13..66e2073 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -41,7 +41,16 @@ importers:
'@txtdot/sdk':
specifier: workspace:*
version: link:../sdk
+ html-to-text:
+ specifier: ^9.0.5
+ version: 9.0.5
+ linkedom:
+ specifier: ^0.18.0
+ version: 0.18.0
devDependencies:
+ '@types/html-to-text':
+ specifier: ^9.0.4
+ version: 9.0.4
typescript:
specifier: ^5.4.5
version: 5.4.5
@@ -49,8 +58,8 @@ importers:
packages/sdk:
dependencies:
linkedom:
- specifier: ^0.16.11
- version: 0.16.11
+ specifier: ^0.18.0
+ version: 0.18.0
route-parser:
specifier: ^0.0.5
version: 0.0.5
@@ -64,6 +73,9 @@ importers:
packages/server:
dependencies:
+ '@fastify/one-line-logger':
+ specifier: ^1.3.0
+ version: 1.3.0
'@fastify/static':
specifier: ^7.0.3
version: 7.0.4
@@ -107,8 +119,8 @@ importers:
specifier: ^3.0.1
version: 3.1.0
linkedom:
- specifier: ^0.16.11
- version: 0.16.11
+ specifier: ^0.18.0
+ version: 0.18.0
micromatch:
specifier: ^4.0.5
version: 4.0.5
@@ -190,6 +202,9 @@ packages:
'@fastify/merge-json-schemas@0.1.1':
resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==}
+ '@fastify/one-line-logger@1.3.0':
+ resolution: {integrity: sha512-91Rqm1UPCcF6eL/g0LkjNrIZBBK/7/lmUbKdIcqZAor5ZN8VV/CiHkEwK9SGl2IUN9CS1BFhSZBwTU6ovzH7Yw==}
+
'@fastify/send@2.1.0':
resolution: {integrity: sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==}
@@ -534,6 +549,9 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
+ '@selderee/plugin-htmlparser2@0.11.0':
+ resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
+
'@sigstore/bundle@1.1.0':
resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -606,6 +624,9 @@ packages:
'@types/ejs@3.1.5':
resolution: {integrity: sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==}
+ '@types/html-to-text@9.0.4':
+ resolution: {integrity: sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==}
+
'@types/jsdom@21.1.6':
resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
@@ -1031,6 +1052,9 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
+ colorette@2.0.20:
+ resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+
columnify@1.6.0:
resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==}
engines: {node: '>=8.0.0'}
@@ -1132,6 +1156,9 @@ packages:
dateformat@3.0.3:
resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==}
+ dateformat@4.6.3:
+ resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==}
+
debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
@@ -1155,6 +1182,10 @@ packages:
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
defaults@1.0.4:
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
@@ -1355,6 +1386,9 @@ packages:
fast-content-type-parse@1.1.0:
resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
+ fast-copy@3.0.2:
+ resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==}
+
fast-decode-uri-component@1.0.1:
resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==}
@@ -1381,6 +1415,9 @@ packages:
resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==}
engines: {node: '>=6'}
+ fast-safe-stringify@2.1.1:
+ resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
+
fast-uri@2.3.0:
resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==}
@@ -1591,6 +1628,9 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
+ help-me@5.0.0:
+ resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
+
hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
@@ -1613,6 +1653,13 @@ packages:
html-escaper@3.0.3:
resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==}
+ html-to-text@9.0.5:
+ resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==}
+ engines: {node: '>=14'}
+
+ htmlparser2@8.0.2:
+ resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
+
htmlparser2@9.1.0:
resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==}
@@ -1832,6 +1879,10 @@ packages:
resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
+ joycon@3.1.1:
+ resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
+ engines: {node: '>=10'}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -1904,6 +1955,9 @@ packages:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
+ leac@0.6.0:
+ resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
+
lerna@8.1.2:
resolution: {integrity: sha512-RCyBAn3XsqqvHbz3TxLfD7ylqzCi1A2UJnFEZmhURgx589vM3qYWQa/uOMeEEf565q6cAdtmulITciX1wgkAtw==}
engines: {node: '>=18.0.0'}
@@ -1931,8 +1985,8 @@ packages:
resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
- linkedom@0.16.11:
- resolution: {integrity: sha512-WgaTVbj7itjyXTsCvgerpneERXShcnNJF5VIV+/4SLtyRLN+HppPre/WDHRofAr2IpEuujSNgJbCBd5lMl6lRw==}
+ linkedom@0.18.0:
+ resolution: {integrity: sha512-Xtu+w437ENHrgggnSHssua47vYXX/a/MzNdo+AR3UuWoOAxGZNwDSvjRPnPbVRFoygT990HS+7BDVBE1MK6FLQ==}
load-json-file@4.0.0:
resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==}
@@ -2130,6 +2184,9 @@ packages:
ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
multimatch@5.0.0:
resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==}
engines: {node: '>=10'}
@@ -2396,6 +2453,9 @@ packages:
parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+ parseley@0.12.1:
+ resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
+
path-exists@3.0.0:
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
engines: {node: '>=4'}
@@ -2430,6 +2490,9 @@ packages:
pause-stream@0.0.11:
resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==}
+ peberminta@0.9.0:
+ resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
+
picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
@@ -2456,6 +2519,10 @@ packages:
pino-abstract-transport@1.2.0:
resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==}
+ pino-pretty@10.3.1:
+ resolution: {integrity: sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g==}
+ hasBin: true
+
pino-std-serializers@6.2.2:
resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==}
@@ -2529,6 +2596,9 @@ packages:
engines: {node: '>= 0.10'}
hasBin: true
+ pump@3.0.0:
+ resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
+
punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -2700,6 +2770,9 @@ packages:
secure-json-parse@2.7.0:
resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==}
+ selderee@0.11.0:
+ resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
+
semver@5.7.2:
resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
hasBin: true
@@ -3233,6 +3306,10 @@ snapshots:
dependencies:
fast-deep-equal: 3.1.3
+ '@fastify/one-line-logger@1.3.0':
+ dependencies:
+ pino-pretty: 10.3.1
+
'@fastify/send@2.1.0':
dependencies:
'@lukeed/ms': 2.0.2
@@ -3659,6 +3736,11 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
+ '@selderee/plugin-htmlparser2@0.11.0':
+ dependencies:
+ domhandler: 5.0.3
+ selderee: 0.11.0
+
'@sigstore/bundle@1.1.0':
dependencies:
'@sigstore/protobuf-specs': 0.2.1
@@ -3738,6 +3820,8 @@ snapshots:
'@types/ejs@3.1.5': {}
+ '@types/html-to-text@9.0.4': {}
+
'@types/jsdom@21.1.6':
dependencies:
'@types/node': 20.12.11
@@ -4198,6 +4282,8 @@ snapshots:
color-convert: 2.0.1
color-string: 1.9.1
+ colorette@2.0.20: {}
+
columnify@1.6.0:
dependencies:
strip-ansi: 6.0.1
@@ -4326,6 +4412,8 @@ snapshots:
dateformat@3.0.3: {}
+ dateformat@4.6.3: {}
+
debug@4.3.4:
dependencies:
ms: 2.1.2
@@ -4341,6 +4429,8 @@ snapshots:
deep-is@0.1.4: {}
+ deepmerge@4.3.1: {}
+
defaults@1.0.4:
dependencies:
clone: 1.0.4
@@ -4552,6 +4642,8 @@ snapshots:
fast-content-type-parse@1.1.0: {}
+ fast-copy@3.0.2: {}
+
fast-decode-uri-component@1.0.1: {}
fast-deep-equal@3.1.3: {}
@@ -4584,6 +4676,8 @@ snapshots:
fast-redact@3.5.0: {}
+ fast-safe-stringify@2.1.1: {}
+
fast-uri@2.3.0: {}
fastify-plugin@4.5.1: {}
@@ -4830,6 +4924,8 @@ snapshots:
dependencies:
function-bind: 1.1.2
+ help-me@5.0.0: {}
+
hosted-git-info@2.8.9: {}
hosted-git-info@3.0.8:
@@ -4850,6 +4946,21 @@ snapshots:
html-escaper@3.0.3: {}
+ html-to-text@9.0.5:
+ dependencies:
+ '@selderee/plugin-htmlparser2': 0.11.0
+ deepmerge: 4.3.1
+ dom-serializer: 2.0.0
+ htmlparser2: 8.0.2
+ selderee: 0.11.0
+
+ htmlparser2@8.0.2:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ entities: 4.5.0
+
htmlparser2@9.1.0:
dependencies:
domelementtype: 2.3.0
@@ -4900,7 +5011,7 @@ snapshots:
humanize-ms@1.2.1:
dependencies:
- ms: 2.1.2
+ ms: 2.1.3
iconv-lite@0.4.24:
dependencies:
@@ -5069,13 +5180,15 @@ snapshots:
jest-diff@29.7.0:
dependencies:
- chalk: 4.1.2
+ chalk: 4.1.0
diff-sequences: 29.6.3
jest-get-type: 29.6.3
pretty-format: 29.7.0
jest-get-type@29.6.3: {}
+ joycon@3.1.1: {}
+
js-tokens@4.0.0: {}
js-yaml@3.14.1:
@@ -5140,6 +5253,8 @@ snapshots:
kind-of@6.0.3: {}
+ leac@0.6.0: {}
+
lerna@8.1.2(encoding@0.1.13):
dependencies:
'@lerna/create': 8.1.2(encoding@0.1.13)(typescript@5.4.5)
@@ -5259,7 +5374,7 @@ snapshots:
lines-and-columns@2.0.4: {}
- linkedom@0.16.11:
+ linkedom@0.18.0:
dependencies:
css-select: 5.1.0
cssom: 0.5.0
@@ -5486,13 +5601,15 @@ snapshots:
ms@2.1.2: {}
+ ms@2.1.3: {}
+
multimatch@5.0.0:
dependencies:
'@types/minimatch': 3.0.5
array-differ: 3.0.0
array-union: 2.1.0
arrify: 2.0.1
- minimatch: 3.1.2
+ minimatch: 3.0.5
mute-stream@0.0.8: {}
@@ -5669,7 +5786,7 @@ snapshots:
'@yarnpkg/parsers': 3.0.0-rc.46
'@zkochan/js-yaml': 0.0.6
axios: 1.6.8
- chalk: 4.1.2
+ chalk: 4.1.0
cli-cursor: 3.1.0
cli-spinners: 2.6.1
cliui: 8.0.1
@@ -5742,7 +5859,7 @@ snapshots:
ora@5.3.0:
dependencies:
bl: 4.1.0
- chalk: 4.1.2
+ chalk: 4.1.0
cli-cursor: 3.1.0
cli-spinners: 2.6.1
is-interactive: 1.0.0
@@ -5869,6 +5986,11 @@ snapshots:
dependencies:
entities: 4.5.0
+ parseley@0.12.1:
+ dependencies:
+ leac: 0.6.0
+ peberminta: 0.9.0
+
path-exists@3.0.0: {}
path-exists@4.0.0: {}
@@ -5894,6 +6016,8 @@ snapshots:
dependencies:
through: 2.3.8
+ peberminta@0.9.0: {}
+
picocolors@1.0.0: {}
picomatch@2.3.1: {}
@@ -5911,6 +6035,23 @@ snapshots:
readable-stream: 4.5.2
split2: 4.2.0
+ pino-pretty@10.3.1:
+ dependencies:
+ colorette: 2.0.20
+ dateformat: 4.6.3
+ fast-copy: 3.0.2
+ fast-safe-stringify: 2.1.1
+ help-me: 5.0.0
+ joycon: 3.1.1
+ minimist: 1.2.8
+ on-exit-leak-free: 2.1.2
+ pino-abstract-transport: 1.2.0
+ pump: 3.0.0
+ readable-stream: 4.5.2
+ secure-json-parse: 2.7.0
+ sonic-boom: 3.8.1
+ strip-json-comments: 3.1.1
+
pino-std-serializers@6.2.2: {}
pino@9.0.0:
@@ -5975,6 +6116,11 @@ snapshots:
dependencies:
event-stream: 3.3.4
+ pump@3.0.0:
+ dependencies:
+ end-of-stream: 1.4.4
+ once: 1.4.0
+
punycode@2.3.1: {}
queue-microtask@1.2.3: {}
@@ -6147,6 +6293,10 @@ snapshots:
secure-json-parse@2.7.0: {}
+ selderee@0.11.0:
+ dependencies:
+ parseley: 0.12.1
+
semver@5.7.2: {}
semver@7.6.2: {}