diff --git a/.vscode/launch.json b/.vscode/launch.json index 759aa27..a433482 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -63,7 +63,7 @@ "args": [ "--forceExit", "--runInBand", - "src/modules/metaServer/lib/page/usePage/spec/useSpaPage.spec.ts", + "src/modules/metaServer/contentHandlers/liveShare/spec/liveShare.spec.ts", ], "pauseForSourceMap": true, "console": "integratedTerminal", diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/dynamicShareHandler.ts b/src/modules/metaServer/contentHandlers/dynamicShare/dynamicShareHandler.ts index 517d055..78bddd2 100644 --- a/src/modules/metaServer/contentHandlers/dynamicShare/dynamicShareHandler.ts +++ b/src/modules/metaServer/contentHandlers/dynamicShare/dynamicShareHandler.ts @@ -4,6 +4,7 @@ import Path from 'node:path' import { assertEx } from '@xylabs/assert' import { exists } from '@xylabs/exists' import { asyncHandler } from '@xylabs/sdk-api-express-ecs' +import { HttpStatusCode } from 'axios' import { RequestHandler } from 'express' import { @@ -26,7 +27,7 @@ import { useIndexAndDynamicPreviewImage } from './lib/index.js' const indexHtmlMaxAge = 60 * 10 const indexHtmlCacheControlHeader = `public, max-age=${indexHtmlMaxAge}` -const disableCaching = false +const enableCaching = false const getPageHandler = (baseDir: string) => { // Ensure file containing base HTML exists @@ -39,28 +40,36 @@ const getPageHandler = (baseDir: string) => { const pageHandler: RequestHandler = asyncHandler(async (req, res, next) => { const adjustedPath = getAdjustedPath(req) if (Path.extname(adjustedPath) === '.html') { + const uri = getUriBehindProxy(req) try { - const uri = getUriBehindProxy(req) console.log(`[dynamicShare][pageHandler][${uri}]: called`) - const cachedHtml = await pageRepository.findFile(adjustedPath) - if (cachedHtml && !disableCaching) { - console.log(`[dynamicShare][pageHandler][${uri}]: return cached`) - const html = arrayBufferToString(await cachedHtml.data) - res.type('html').set('Cache-Control', indexHtmlCacheControlHeader).send(html) - return - } else { - console.log(`[dynamicShare][pageHandler][${uri}]: rendering`) - const updatedHtml = await useIndexAndDynamicPreviewImage(uri, indexHtml) - console.log(`[dynamicShare][pageHandler][${uri}]: caching`) - const data = stringToArrayBuffer(updatedHtml) - const file: RepositoryFile = { data, type: 'text/html', uri: adjustedPath } - await pageRepository.addFile(file) - console.log(`[dynamicShare][pageHandler][${uri}]: return html`) - res.type('html').set('Cache-Control', indexHtmlCacheControlHeader).send(updatedHtml) - return + if (enableCaching) { + console.log(`[dynamicShare][pageHandler][${uri}]: checking for cached`) + const cachedHtml = await pageRepository.findFile(adjustedPath) + if (cachedHtml) { + console.log(`[dynamicShare][pageHandler][${uri}]: return cached`) + const html = arrayBufferToString(await cachedHtml.data) + res.type('html').set('Cache-Control', indexHtmlCacheControlHeader).send(html) + return + } } + console.log(`[dynamicShare][pageHandler][${uri}]: rendering`) + const updatedHtml = await useIndexAndDynamicPreviewImage(uri, indexHtml) + console.log(`[dynamicShare][pageHandler][${uri}]: caching`) + const data = stringToArrayBuffer(updatedHtml) + const file: RepositoryFile = { data, type: 'text/html', uri: adjustedPath } + await pageRepository.addFile(file) + console.log(`[dynamicShare][pageHandler][${uri}]: return html`) + res.type('html').set('Cache-Control', indexHtmlCacheControlHeader).send(updatedHtml) + return } catch (error) { + const status = HttpStatusCode.ServiceUnavailable + console.log(`[dynamicShare][useIndexAndDynamicPreviewImage][${uri}]: error, returning status code ${status}`) console.log(error) + res.status(status) + .set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate') + .set('Retry-After', '60') // Retry after 60 seconds + .send() } } next() diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/getImageMeta.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/getImageMeta.ts index 60590fd..cb10eef 100644 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/getImageMeta.ts +++ b/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/getImageMeta.ts @@ -1,6 +1,6 @@ import { Meta, OpenGraphMeta, TwitterMeta } from '@xyo-network/sdk-meta' -import { defaultViewportSize } from '../../../../lib/index.js' +import { defaultViewportSize } from '../../../../lib/index.ts' import { getImageUrlFromPage } from './getImageUrlFromPage.ts' /** diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/getPreviewUrlFromPage.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/getPreviewUrlFromPage.ts deleted file mode 100644 index a641a1d..0000000 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/getPreviewUrlFromPage.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { assertEx } from '@xylabs/assert' -import he from 'he' -const { decode } = he - -import { useSpaPage } from '../../../../lib/index.js' - -/** - * The property name of the meta element - * which contains the preview image URL - */ -const xyoOgImageProperty = 'xyo:og:image' - -// Define the regex pattern to match the meta element -const xyoOgImageElementRegex = /]*property="xyo:og:image"[^>]*content="([^"]*)"[^>]*>/ - -/** - * Returns the URL of the preview image from within the Live Share page's meta tags - * @param url The URL of the Live Share page - * @returns The URL of the preview image from within the Live Share page's meta tags - */ -export const getPreviewUrlFromPage = async (url: string): Promise => { - // TODO: Optimize this with something like React SSR - const content = await useSpaPage(url, async (page) => { - console.log(`[dynamicShare][getPreviewUrlFromPage][${url}]: navigated to ${url}`) - await page.waitForSelector('head > meta[property="xyo:og:image"]', { timeout: 15_000 }) - console.log(`[dynamicShare][getPreviewUrlFromPage][${url}]: found meta property ${xyoOgImageProperty}`) - return await page.content() - }) - const html = assertEx(content, `[dynamicShare][getPreviewUrlFromPage][${url}]: error retrieving html`) - // Use the regex to extract the expected meta element - const match = html.match(xyoOgImageElementRegex) - // Extract the preview image URL from the meta element & decode it - const previewUrl = decode( - assertEx(match?.[1], `[dynamicShare][getPreviewUrlFromPage][${url}]: error, missing meta element with ${xyoOgImageProperty} property`), - ) - return previewUrl -} - -/** - * Returns the URL of the preview image from within the Live Share page's meta tags - * or undefined if the preview image URL could not be obtained - * @param url The URL of the Live Share page - * @returns The URL of the preview image from within the Live Share page's meta tags - * or undefined if the preview image URL could not be obtained - */ -export const tryGetPreviewUrlFromPage = async (url: string): Promise => { - try { - return await getPreviewUrlFromPage(url) - } catch { - return undefined - } -} diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/index.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/index.ts index 2f471ed..628c421 100644 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/index.ts +++ b/src/modules/metaServer/contentHandlers/dynamicShare/lib/image/index.ts @@ -1 +1 @@ -export * from './getImageMeta.js' +export * from './getImageMeta.ts' diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/getRenderedPageHtml.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/getRenderedPageHtml.ts deleted file mode 100644 index 0fa73c0..0000000 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/getRenderedPageHtml.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { usePage } from '../../../../../lib/index.js' - -/** - * Gets the rendered page html - * @param url The url to navigate to - * @param navigateToRootFirst Should navigate to the root of the url first, then navigate to the relative path - * @returns The rendered page html - */ -export const getRenderedPageHtml = async (url: string): Promise => { - try { - console.log(`[dynamicShare][getRenderedPageHtml][${url}]: rendering`) - const html = await usePage(url, undefined, async (page) => { - console.log(`[dynamicShare][getRenderedPageHtml][${url}]: navigated to ${url}`) - return await page.content() - }) - console.log(`[dynamicShare][getRenderedPageHtml][${url}]: rendered`) - return html - } catch (error) { - console.log(error) - } - return undefined -} diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/getRenderedSpaPageHtml.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/getRenderedSpaPageHtml.ts deleted file mode 100644 index 03c4c6b..0000000 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/getRenderedSpaPageHtml.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useSpaPage } from '../../../../../lib/index.js' - -/** - * Gets the rendered page html - * @param url The url to navigate to - * @param navigateToRootFirst Should navigate to the root of the url first, then navigate to the relative path - * @returns The rendered page html - */ -export const getRenderedSpaPageHtml = async (url: string): Promise => { - try { - console.log(`[dynamicShare][getRenderedPageHtml][${url}]: rendering`) - const html = await useSpaPage(url, async (page) => { - console.log(`[dynamicShare][getRenderedPageHtml][${url}]: navigated to ${url}`) - return await page.content() - }) - console.log(`[dynamicShare][getRenderedPageHtml][${url}]: rendered`) - return html - } catch (error) { - console.log(error) - } - return undefined -} diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/index.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/index.ts deleted file mode 100644 index bfff53f..0000000 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './getRenderedPageHtml.js' -export * from './getRenderedSpaPageHtml.js' diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/spec/getRenderedSpaPageHtml.spec.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/spec/getRenderedSpaPageHtml.spec.ts deleted file mode 100644 index f8959fc..0000000 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/spec/getRenderedSpaPageHtml.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { getRenderedSpaPageHtml } from '../getRenderedSpaPageHtml.js' - -describe.skip('getRenderedSpaPageHtml', () => { - const uri = 'https://xyo.network/brand' - const expected = 'XYO: Brand Assets & Logos' - it('gets the page', async () => { - const result = await getRenderedSpaPageHtml(uri) - expect(result).toContain(expected) - }) -}) diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/index.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/index.ts index a83461a..1e35759 100644 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/index.ts +++ b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/index.ts @@ -1,2 +1 @@ -export * from './getRenderedPageHtml/index.js' -export * from './useIndexAndDeferredPreviewImage/index.ts' +export * from './useIndexAndDynamicPreviewImage/index.ts' diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDeferredPreviewImage/useIndexAndDynamicPreviewImage.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDeferredPreviewImage/useIndexAndDynamicPreviewImage.ts deleted file mode 100644 index 41afe5c..0000000 --- a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDeferredPreviewImage/useIndexAndDynamicPreviewImage.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { metaBuilder } from '@xyo-network/sdk-meta' - -import { getImageMeta } from '../../image/index.ts' - -export const useIndexAndDynamicPreviewImage = async (url: string, indexHtml: string): Promise => { - try { - console.log(`[dynamicShare][useIndexAndDynamicPreviewImage][${url}]: generating preview image meta`) - const meta = await getImageMeta(url) - const updatedHtml = metaBuilder(indexHtml, meta) - console.log(`[dynamicShare][useIndexAndDynamicPreviewImage][${url}]: returning index.html & preview image meta`) - return updatedHtml - } catch (error) { - console.log(error) - } - console.log(`[dynamicShare][useIndexAndDynamicPreviewImage][${url}]: error, returning index.html`) - return indexHtml -} diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDeferredPreviewImage/index.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDynamicPreviewImage/index.ts similarity index 100% rename from src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDeferredPreviewImage/index.ts rename to src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDynamicPreviewImage/index.ts diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDynamicPreviewImage/useIndexAndDynamicPreviewImage.ts b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDynamicPreviewImage/useIndexAndDynamicPreviewImage.ts new file mode 100644 index 0000000..60662e3 --- /dev/null +++ b/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/useIndexAndDynamicPreviewImage/useIndexAndDynamicPreviewImage.ts @@ -0,0 +1,11 @@ +import { metaBuilder } from '@xyo-network/sdk-meta' + +import { getImageMeta } from '../../image/index.ts' + +export const useIndexAndDynamicPreviewImage = async (url: string, indexHtml: string): Promise => { + console.log(`[dynamicShare][useIndexAndDynamicPreviewImage][${url}]: generating preview image meta`) + const meta = await getImageMeta(url) + const updatedHtml = metaBuilder(indexHtml, meta) + console.log(`[dynamicShare][useIndexAndDynamicPreviewImage][${url}]: returning index.html & preview image meta`) + return updatedHtml +} diff --git a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/getRenderedPageHtml.ts b/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/getRenderedPageHtml.ts deleted file mode 100644 index 3a46894..0000000 --- a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/getRenderedPageHtml.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { usePage } from '../../../../../lib/index.js' - -/** - * Gets the rendered page html - * @param url The url to navigate to - * @param navigateToRootFirst Should navigate to the root of the url first, then navigate to the relative path - * @returns The rendered page html - */ -export const getRenderedPageHtml = async (url: string): Promise => { - try { - console.log(`[liveShare][getRenderedPageHtml][${url}]: rendering`) - const html = await usePage(url, undefined, async (page) => { - console.log(`[liveShare][getRenderedPageHtml][${url}]: navigated to ${url}`) - return await page.content() - }) - console.log(`[liveShare][getRenderedPageHtml][${url}]: rendered`) - return html - } catch (error) { - console.log(error) - } - return undefined -} diff --git a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/getRenderedSpaPageHtml.ts b/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/getRenderedSpaPageHtml.ts deleted file mode 100644 index 1f14365..0000000 --- a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/getRenderedSpaPageHtml.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useSpaPage } from '../../../../../lib/index.js' - -/** - * Gets the rendered page html - * @param url The url to navigate to - * @param navigateToRootFirst Should navigate to the root of the url first, then navigate to the relative path - * @returns The rendered page html - */ -export const getRenderedSpaPageHtml = async (url: string): Promise => { - try { - console.log(`[liveShare][getRenderedPageHtml][${url}]: rendering`) - const html = await useSpaPage(url, async (page) => { - console.log(`[liveShare][getRenderedPageHtml][${url}]: navigated to ${url}`) - return await page.content() - }) - console.log(`[liveShare][getRenderedPageHtml][${url}]: rendered`) - return html - } catch (error) { - console.log(error) - } - return undefined -} diff --git a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/index.ts b/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/index.ts deleted file mode 100644 index bfff53f..0000000 --- a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './getRenderedPageHtml.js' -export * from './getRenderedSpaPageHtml.js' diff --git a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/spec/getRenderedSpaPageHtml.spec.ts b/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/spec/getRenderedSpaPageHtml.spec.ts deleted file mode 100644 index f8959fc..0000000 --- a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/spec/getRenderedSpaPageHtml.spec.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { getRenderedSpaPageHtml } from '../getRenderedSpaPageHtml.js' - -describe.skip('getRenderedSpaPageHtml', () => { - const uri = 'https://xyo.network/brand' - const expected = 'XYO: Brand Assets & Logos' - it('gets the page', async () => { - const result = await getRenderedSpaPageHtml(uri) - expect(result).toContain(expected) - }) -}) diff --git a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/spec/tsconfig.json b/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/spec/tsconfig.json deleted file mode 100644 index 8208316..0000000 --- a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/getRenderedPageHtml/spec/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "allowImportingTsExtensions": true, - }, - "extends": "@xylabs/tsconfig-jest" -} \ No newline at end of file diff --git a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/index.ts b/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/index.ts index ffc11e3..21a5ffe 100644 --- a/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/index.ts +++ b/src/modules/metaServer/contentHandlers/liveShare/lib/pageStrategies/index.ts @@ -1,2 +1 @@ -export * from './getRenderedPageHtml/index.js' -export * from './useIndexAndDeferredPreviewImage/index.js' +export * from './useIndexAndDeferredPreviewImage/index.ts' diff --git a/src/modules/metaServer/contentHandlers/liveShare/liveShareHandlers.ts b/src/modules/metaServer/contentHandlers/liveShare/liveShareHandlers.ts index 3791d69..dbef52d 100644 --- a/src/modules/metaServer/contentHandlers/liveShare/liveShareHandlers.ts +++ b/src/modules/metaServer/contentHandlers/liveShare/liveShareHandlers.ts @@ -54,7 +54,7 @@ const maxImageGenerationWait = 8000 * the environment is fully initialized. */ const imageRepository = () => getFileRepository() -const disableCaching = false +const enableCaching = true const getPageHandler = (baseDir: string) => { // Ensure file containing base HTML exists @@ -70,23 +70,25 @@ const getPageHandler = (baseDir: string) => { try { const uri = getUriBehindProxy(req) console.log(`[liveShare][pageHandler][${uri}]: called`) - const cachedHtml = await pageRepository.findFile(adjustedPath) - if (cachedHtml && !disableCaching) { - console.log(`[liveShare][pageHandler][${uri}]: return cached`) - const html = arrayBufferToString(await cachedHtml.data) - res.type('html').set('Cache-Control', indexHtmlCacheControlHeader).send(html) - return - } else { - console.log(`[liveShare][pageHandler][${uri}]: rendering`) - const updatedHtml = useIndexAndDeferredPreviewImage(uri, imageRepository(), indexHtml) - console.log(`[liveShare][pageHandler][${uri}]: caching`) - const data = stringToArrayBuffer(updatedHtml) - const file: RepositoryFile = { data, type: 'text/html', uri: adjustedPath } - await pageRepository.addFile(file) - console.log(`[liveShare][pageHandler][${uri}]: return html`) - res.type('html').set('Cache-Control', indexHtmlCacheControlHeader).send(updatedHtml) - return + if (enableCaching) { + console.log(`[liveShare][pageHandler][${uri}]: checking for cached`) + const cachedHtml = await pageRepository.findFile(adjustedPath) + if (cachedHtml) { + console.log(`[liveShare][pageHandler][${uri}]: return cached`) + const html = arrayBufferToString(await cachedHtml.data) + res.type('html').set('Cache-Control', indexHtmlCacheControlHeader).send(html) + return + } } + console.log(`[liveShare][pageHandler][${uri}]: rendering`) + const updatedHtml = useIndexAndDeferredPreviewImage(uri, imageRepository(), indexHtml) + console.log(`[liveShare][pageHandler][${uri}]: caching`) + const data = stringToArrayBuffer(updatedHtml) + const file: RepositoryFile = { data, type: 'text/html', uri: adjustedPath } + await pageRepository.addFile(file) + console.log(`[liveShare][pageHandler][${uri}]: return html`) + res.type('html').set('Cache-Control', indexHtmlCacheControlHeader).send(updatedHtml) + return } catch (error) { console.log(error) } diff --git a/src/modules/metaServer/lib/index.ts b/src/modules/metaServer/lib/index.ts index ec485d8..2c13672 100644 --- a/src/modules/metaServer/lib/index.ts +++ b/src/modules/metaServer/lib/index.ts @@ -4,6 +4,7 @@ export * from './file/index.js' export * from './head/index.js' export * from './image/index.js' export * from './page/index.js' +export * from './pageHtml/index.js' export * from './path/index.js' export * from './repository/index.js' export * from './socialMedia/index.js' diff --git a/src/modules/metaServer/lib/logging/ScopedLogFunction.ts b/src/modules/metaServer/lib/logging/ScopedLogFunction.ts new file mode 100644 index 0000000..ae3fc30 --- /dev/null +++ b/src/modules/metaServer/lib/logging/ScopedLogFunction.ts @@ -0,0 +1 @@ +export type ScopedLogFunction = (message: string, scopes: string[]) => void diff --git a/src/modules/metaServer/lib/logging/index.ts b/src/modules/metaServer/lib/logging/index.ts new file mode 100644 index 0000000..72cc927 --- /dev/null +++ b/src/modules/metaServer/lib/logging/index.ts @@ -0,0 +1,2 @@ +export * from './ScopedLogFunction.ts' +export * from './scopedLoggers.ts' diff --git a/src/modules/metaServer/lib/logging/scopedLoggers.ts b/src/modules/metaServer/lib/logging/scopedLoggers.ts new file mode 100644 index 0000000..22005cc --- /dev/null +++ b/src/modules/metaServer/lib/logging/scopedLoggers.ts @@ -0,0 +1,51 @@ +import { ScopedLogFunction } from './ScopedLogFunction.ts' + +type ConsoleLoggerMethod = keyof Pick + +/** + * Logs a log message to the console + * @param message The message to log + * @param scopes The scopes to log the message under + */ +const scopedLoggerWithVerbosity = (verbosity: ConsoleLoggerMethod): ScopedLogFunction => { + const logFunction: ScopedLogFunction = (message: string, scopes: string[] = []) => { + const prefix = scopes.length > 0 ? `[${scopes.join('][')}]` : '' + console[verbosity](`${prefix}: ${message}`) + } + return logFunction +} + +/** + * Logs a debug message to the console + * @param message The message to log + * @param scopes The scopes to log the message under + */ +export const debug: ScopedLogFunction = scopedLoggerWithVerbosity('debug') + +/** + * Logs an info message to the console + * @param message The message to log + * @param scopes The scopes to log the message under + */ +export const info: ScopedLogFunction = scopedLoggerWithVerbosity('info') + +/** + * Logs a log message to the console + * @param message The message to log + * @param scopes The scopes to log the message under + */ +export const log: ScopedLogFunction = scopedLoggerWithVerbosity('log') + +/** + * Logs a warning message to the console + * @param message The message to log + * @param scopes The scopes to log the message under + */ +export const warn: ScopedLogFunction = scopedLoggerWithVerbosity('warn') + +/** + * Logs an error message to the console + * @param message The message to log + * @param scopes The scopes to log the message under + */ +export const error: ScopedLogFunction = scopedLoggerWithVerbosity('error') diff --git a/src/modules/metaServer/lib/page/usePage/spec/usePage.spec.ts b/src/modules/metaServer/lib/page/usePage/spec/usePage.spec.ts index 3c824dc..ac6e6d8 100644 --- a/src/modules/metaServer/lib/page/usePage/spec/usePage.spec.ts +++ b/src/modules/metaServer/lib/page/usePage/spec/usePage.spec.ts @@ -1,24 +1,24 @@ import { usePage } from '../usePage.js' +const waitForElementToInclude = (selector: string, expectedValue: string) => { + const element = document.querySelector(selector) + return element && element.textContent?.includes(expectedValue) +} + describe('usePage', () => { - const uri = 'https://xyo.network/brand' - const expected = 'XYO: Brand Assets & Logos' - describe('with navigateToRootFirst=false', () => { - it('gets the page', async () => { - const result = await usePage(uri, undefined, async (page) => { - await page.waitForSelector('title', { timeout: 10_000 }) - return page.content() - }) - expect(result).toContain(expected) - }, 60_000) - }) - describe('with navigateToRootFirst=true', () => { - it('gets the page', async () => { - const result = await usePage(uri, undefined, async (page) => { - await page.waitForSelector('title', { timeout: 10_000 }) - return page.content() - }) - expect(result).toContain(expected) - }, 60_000) - }) + const uri = 'https://react.dev/learn' + const expected = 'Quick Start' + it('gets the page', async () => { + const result = await usePage(uri, undefined, async (page) => { + await page.waitForFunction(waitForElementToInclude, + { + polling: 100, + timeout: 30_000, + }, + 'title', + expected) + return page.content() + }) + expect(result).toContain(expected) + }, 60_000) }) diff --git a/src/modules/metaServer/lib/page/usePage/spec/useSpaPage.perf.spec.ts b/src/modules/metaServer/lib/page/usePage/spec/useSpaPage.perf.spec.ts new file mode 100644 index 0000000..e6b2a9c --- /dev/null +++ b/src/modules/metaServer/lib/page/usePage/spec/useSpaPage.perf.spec.ts @@ -0,0 +1,14 @@ +import { useSpaPage } from '../useSpaPage.js' + +describe.skip('useSpaPage [perf]', () => { + const uri = 'https://react.dev/learn' + const expected = 'Quick Start' + it('gets the page', async () => { + await Promise.all(Array.from({ length: 200 }).map(async () => { + const content = await useSpaPage(uri, async (page) => { + return await page.content() + }) + expect(content).toContain(expected) + })) + }, 60_000) +}) diff --git a/src/modules/metaServer/lib/page/usePage/spec/useSpaPage.spec.ts b/src/modules/metaServer/lib/page/usePage/spec/useSpaPage.spec.ts index c7a9178..2e07f1d 100644 --- a/src/modules/metaServer/lib/page/usePage/spec/useSpaPage.spec.ts +++ b/src/modules/metaServer/lib/page/usePage/spec/useSpaPage.spec.ts @@ -1,26 +1,12 @@ import { useSpaPage } from '../useSpaPage.js' -const waitForElementToInclude = (selector: string, expectedValue: string) => { - const element = document.querySelector(selector) - return element && element.textContent?.includes(expectedValue) -} - describe('useSpaPage', () => { - const uri = 'https://xyo.network/brand' - const expected = 'XYO: Brand Assets' - describe('with navigateToRootFirst=true', () => { - it('gets the page', async () => { - const content = await useSpaPage(uri, async (page) => { - await page.waitForFunction(waitForElementToInclude, - { - polling: 100, - timeout: 30_000, - }, - 'title', - expected) - return page.content() - }) - expect(content).toContain(expected) - }, 60_000) - }) + const uri = 'https://react.dev/learn' + const expected = 'Quick Start' + it('gets the page', async () => { + const content = await useSpaPage(uri, async (page) => { + return await page.content() + }) + expect(content).toContain(expected) + }, 60_000) }) diff --git a/src/modules/metaServer/lib/pageHtml/getRenderedPageHtml.ts b/src/modules/metaServer/lib/pageHtml/getRenderedPageHtml.ts new file mode 100644 index 0000000..892700c --- /dev/null +++ b/src/modules/metaServer/lib/pageHtml/getRenderedPageHtml.ts @@ -0,0 +1,23 @@ +import { log } from '../logging/index.ts' +import { usePage } from '../page/index.ts' + +/** + * Gets the rendered page html + * @param url The url to navigate to + * @param logScopePrefix The prefix to use for logging + * @returns The rendered page html + */ +export const getRenderedPageHtml = async (url: string, logScopePrefix: string): Promise => { + try { + log('rendering', [logScopePrefix, 'getRenderedPageHtml', url]) + const html = await usePage(url, undefined, async (page) => { + log('navigated to url', [logScopePrefix, 'getRenderedPageHtml', url]) + return await page.content() + }) + log('rendered', [logScopePrefix, 'getRenderedPageHtml', url]) + return html + } catch (error) { + console.log(error) + } + return undefined +} diff --git a/src/modules/metaServer/lib/pageHtml/getRenderedSpaPageHtml.ts b/src/modules/metaServer/lib/pageHtml/getRenderedSpaPageHtml.ts new file mode 100644 index 0000000..824d38b --- /dev/null +++ b/src/modules/metaServer/lib/pageHtml/getRenderedSpaPageHtml.ts @@ -0,0 +1,24 @@ +import { log } from '../logging/index.ts' +import { useSpaPage } from '../page/index.ts' + +/** + * Gets the rendered page html by navigating to the root of the url first, then to the relative path, just like a user would + * when navigating a SPA + * @param url The url to navigate to + * @param logScopePrefix The prefix to use for logging + * @returns The rendered page html + */ +export const getRenderedSpaPageHtml = async (url: string, logScopePrefix: string): Promise => { + try { + log('rendering', [logScopePrefix, 'getRenderedSpaPageHtml', url]) + const html = await useSpaPage(url, async (page) => { + log('navigated to url', [logScopePrefix, 'getRenderedSpaPageHtml', url]) + return await page.content() + }) + log('rendered', [logScopePrefix, 'getRenderedSpaPageHtml', url]) + return html + } catch (error) { + console.log(error) + } + return undefined +} diff --git a/src/modules/metaServer/lib/pageHtml/index.ts b/src/modules/metaServer/lib/pageHtml/index.ts new file mode 100644 index 0000000..1afe4ac --- /dev/null +++ b/src/modules/metaServer/lib/pageHtml/index.ts @@ -0,0 +1,2 @@ +export * from './getRenderedPageHtml.ts' +export * from './getRenderedSpaPageHtml.ts' diff --git a/src/modules/metaServer/lib/pageHtml/spec/getRenderedPageHtml.spec.ts b/src/modules/metaServer/lib/pageHtml/spec/getRenderedPageHtml.spec.ts new file mode 100644 index 0000000..ab84aaf --- /dev/null +++ b/src/modules/metaServer/lib/pageHtml/spec/getRenderedPageHtml.spec.ts @@ -0,0 +1,10 @@ +import { getRenderedPageHtml } from '../getRenderedPageHtml.ts' + +describe('getRenderedSpaPageHtml', () => { + const uri = 'https://react.dev/learn' + const expected = 'Quick Start' + it('gets the page', async () => { + const result = await getRenderedPageHtml(uri, 'test') + expect(result).toContain(expected) + }) +}) diff --git a/src/modules/metaServer/lib/pageHtml/spec/getRenderedSpaPageHtml.spec.ts b/src/modules/metaServer/lib/pageHtml/spec/getRenderedSpaPageHtml.spec.ts new file mode 100644 index 0000000..d955f94 --- /dev/null +++ b/src/modules/metaServer/lib/pageHtml/spec/getRenderedSpaPageHtml.spec.ts @@ -0,0 +1,10 @@ +import { getRenderedSpaPageHtml } from '../getRenderedSpaPageHtml.ts' + +describe('getRenderedSpaPageHtml', () => { + const uri = 'https://react.dev/learn' + const expected = 'Quick Start' + it('gets the page', async () => { + const result = await getRenderedSpaPageHtml(uri, 'test') + expect(result).toContain(expected) + }) +}) diff --git a/src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/spec/tsconfig.json b/src/modules/metaServer/lib/pageHtml/spec/tsconfig.json similarity index 100% rename from src/modules/metaServer/contentHandlers/dynamicShare/lib/pageStrategies/getRenderedPageHtml/spec/tsconfig.json rename to src/modules/metaServer/lib/pageHtml/spec/tsconfig.json