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}`; + } 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: {}