UNPKG

akasharender

Version:

Rendering support for generating static HTML websites or EPUB eBooks

685 lines 103 kB
/** * * Copyright 2014-2025 David Herron * * This file is part of AkashaCMS (http://akashacms.com/). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import path from 'node:path'; import { promises as fsp } from 'node:fs'; import util from 'node:util'; import * as data from './data.js'; import mahabhuta, { FilesystemPerfDataStore } from 'mahabhuta'; import fastq from 'fastq'; import { performance } from 'node:perf_hooks'; function createRenderingData(config, docInfo) { const ret = { config, renderFirstContext: { fspath: docInfo.vpath, content: docInfo.docContent, body: docInfo.docBody, metadata: docInfo.metadata }, renderer: config.findRendererPath(docInfo.vpath), docInfo, vpath: docInfo.vpath, renderPath: docInfo.renderPath, mountPoint: docInfo.mountPoint, renderTo: config.renderTo, results: { vpath: docInfo.vpath, renderPath: docInfo.renderPath, renderStart: performance.now(), errors: new Array() } }; if (ret.renderer) { ret.results.renderFormat = ret.renderer.renderFormat(ret.renderFirstContext); } return ret; } ////////////////////////////////////////////////////////// /** * The core part of rendering content using a renderer. * This function looks for the renderer, and if none is * found it simply returns. It then does a little setup * to the metadata object, and calls the render function * * @param config - AkashaCMS Configuration * @param rc - RenderingContext for use with Renderers * @returns */ export async function renderContent(config, rc) { // console.log(`renderContent `, rc); const renderer = config.findRendererPath(rc.fspath); if (!renderer) { return { rendererName: undefined, format: undefined, rendered: rc.body }; } // Add necessary items to the metadata rc.metadata.config = config; rc.metadata.partial = (fname, metadata) => { return config.akasha.partial(config, fname, metadata); }; rc.metadata.partialSync = (fname, metadata) => { return config.akasha.partialSync(config, fname, metadata); }; rc.metadata.akasha = config.akasha; rc.metadata.plugin = config.plugin; // Render the primary content let docrendered = await renderer.render(rc); // console.log(`renderContent rendered=`, docrendered); return { rendererName: renderer.name, format: renderer.renderFormat(rc), rendered: docrendered }; } // This function and the next exist solely to // simplify renderDocument. async function writeCSStoOutput(config, docInfo, docRendered, renderStart) { const renderToFpath = path.join(config.renderTo, docInfo.renderPath); const renderToDir = path.dirname(renderToFpath); await fsp.mkdir(renderToDir, { recursive: true }); await fsp.writeFile(renderToFpath, docRendered, 'utf8'); const renderEndRendered = new Date(); await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "RENDERED", renderStart); return `CSS ${docInfo.vpath} ==> ${docInfo.renderPath} (${(renderEndRendered.valueOf() - renderStart.valueOf()) / 1000} seconds)\n${await data.data4file(docInfo.mountPoint, docInfo.vpath)}`; } async function copyAssetToOutput(config, docInfo, docRendered, renderStart) { const renderToFpath = path.join(config.renderTo, docInfo.renderPath); const renderToDir = path.dirname(renderToFpath); await fsp.mkdir(renderToDir, { recursive: true }); await fsp.copyFile(docInfo.fspath, renderToFpath); // console.log(`COPIED ${docInfo.path} ==> ${renderToFpath}`); const renderEndCopied = new Date(); return `COPY ${docInfo.vpath} ==> ${renderToFpath} (${(renderEndCopied.valueOf() - renderStart.valueOf()) / 1000} seconds)`; } function copyProperties(dest, src, exceptLayout) { for (var yprop in src) { if (exceptLayout && yprop === 'layout') continue; dest[yprop] = src[yprop]; } return dest; } async function renderCSSFile(ret) { try { ret.results.renderFormat = 'CSS'; ret.results.renderFirstStart = performance.now(); // Render the CSS content ret.renderedFirst = await ret.renderer.render(ret.renderFirstContext); ret.results.renderFirstEnd = performance.now(); // Write the rendered CSS to output const renderDest = path.join(ret.config.renderTo, ret.docInfo.renderPath); await fsp.mkdir(path.dirname(renderDest), { recursive: true }); await fsp.writeFile(renderDest, ret.renderedFirst, 'utf-8'); ret.results.renderFirstEnd = performance.now(); ret.results.renderEnd = performance.now(); } catch (error) { ret.results.errors = ret.results.errors || []; ret.results.errors.push(error instanceof Error ? error : new Error(String(error))); } // Calculate elapsed times if (ret.results.renderFirstStart && ret.results.renderFirstEnd) { ret.results.renderFirstElapsed = ret.results.renderFirstEnd - ret.results.renderFirstStart; } ret.results.renderLayoutElapsed = 0; ret.results.renderMahaElapsed = 0; if (ret.results.renderStart && ret.results.renderEnd) { ret.results.renderTotalElapsed = ret.results.renderEnd - ret.results.renderStart; } // console.log(`renderCSSFile ${ret.vpath}`, ret); return ret; } async function copyAssetFile(ret) { try { ret.results.renderFormat = 'COPY'; ret.results.renderFirstStart = performance.now(); // Copy the asset file to output directory const renderDest = path.join(ret.config.renderTo, ret.docInfo.renderPath); await fsp.mkdir(path.dirname(renderDest), { recursive: true }); await fsp.copyFile(ret.docInfo.fspath, renderDest); ret.results.renderFirstEnd = performance.now(); ret.results.renderEnd = performance.now(); } catch (error) { ret.results.errors = ret.results.errors || []; ret.results.errors.push(error instanceof Error ? error : new Error(String(error))); } // Calculate elapsed times if (ret.results.renderFirstStart && ret.results.renderFirstEnd) { ret.results.renderFirstElapsed = ret.results.renderFirstEnd - ret.results.renderFirstStart; } ret.results.renderLayoutElapsed = 0; ret.results.renderMahaElapsed = 0; if (ret.results.renderStart && ret.results.renderEnd) { ret.results.renderTotalElapsed = ret.results.renderEnd - ret.results.renderStart; } // Use this to verify error handling // ret.results.errors.push(new Error(`Random error`)); // console.log(`copyAssetFile ${ret.vpath}`, ret); return ret; } /** * Attempt to rewrite renderDocument with cleaner code, and a * different method for collecting performance/timing data. * * The existing renderDocument is messy and hard to understand. * Goal: make it more straight-forward, easy to understand. * Goal: store all data in a well designed object * * The existing performance measurements are imprecise by using * the Date object, and by not computing the elapsed time of * each segment. Instead, it computs the time from the start * for each segment, which isn't useful. We want to see the * elapsed time. * * For precise time measures this uses the Node.js performance * hooks to get accurate timestamps. * * This code has not been executed as yet. * * Tasks: * * TODO Implement CSS renderFormat * * TODO Implement the != HTML renderFormat * * TODO Test and fix bugs * * @param config * @param docInfo * @returns */ export async function renderDocument2(config, docInfo) { // Create the master object to hold all data const ret = createRenderingData(config, docInfo); // Peel off to mode-specific functions if (ret?.renderer?.renderFormat(ret.renderFirstContext) === 'CSS') { const cssResult = await renderCSSFile(ret); return cssResult.results; } else if (!ret.renderer || (ret.renderer.renderFormat(ret.renderFirstContext) !== 'HTML')) { const assetResult = await copyAssetFile(ret); return assetResult.results; } // Otherwise it is HTML // This is where we render the content, then render that // into the layout (if one exists), then run Mahabhuta. // These functions are duplicates between the first // two stages. Save a couple microseconds by instantiating // the functions once. const doPartial = (fname, metadata) => { return config.akasha.partial(config, fname, metadata); }; const doPartialSync = (fname, metadata) => { return config.akasha.partialSync(config, fname, metadata); }; // First Render ret.results.renderFirstStart = performance.now(); try { // Add necessary items to the metadata ret.renderFirstContext.metadata.config = config; ret.renderFirstContext.metadata.partial = doPartial; ret.renderFirstContext.metadata.partialSync = doPartialSync; ret.renderFirstContext.metadata.akasha = config.akasha; ret.renderFirstContext.metadata.plugin = config.plugin; // Render the primary content ret.renderedFirst = await ret.renderer.render(ret.renderFirstContext); } catch (error) { ret.results.errors = ret.results.errors || []; ret.results.errors.push(error instanceof Error ? error : new Error(String(error))); // Use empty string as fallback if rendering fails ret.renderedFirst = ''; } ret.results.renderFirstEnd = performance.now(); // END First Render // Layout Render ret.results.renderLayoutStart = performance.now(); if (ret?.docInfo?.metadata?.layout) { try { const layouts = config.akasha.filecache.layoutsCache; // await layouts.isReady(); let found = await layouts.find(ret.docInfo.metadata.layout); if (!found) { const error = new Error(`No layout found in ${util.inspect(ret.config.layoutDirs)} for ${ret?.docInfo?.metadata?.layout} in file ${ret.docInfo.vpath}`); ret.results.errors = ret.results.errors || []; ret.results.errors.push(error); // Skip layout rendering, use first render result ret.renderedLayout = ret.renderedFirst; } else { const renderer = config.findRendererPath(ret.docInfo.metadata.layout); ret.renderLayoutContext = { fspath: ret.docInfo.metadata.layout, content: found.docContent, body: found.docBody, metadata: {} }; ret.renderLayoutContext.metadata = copyProperties(ret.renderLayoutContext.metadata, found.metadata, false); ret.renderLayoutContext.metadata = copyProperties(ret.renderLayoutContext.metadata, ret.docInfo.metadata, true); ret.renderLayoutContext.metadata.content = ret.renderedFirst; ret.renderLayoutContext.metadata.config = config; ret.renderLayoutContext.metadata.partial = doPartial; ret.renderLayoutContext.metadata.partialSync = doPartialSync; ret.renderLayoutContext.metadata.akasha = config.akasha; ret.renderLayoutContext.metadata.plugin = config.plugin; // Render the layout content ret.renderedLayout = await renderer.render(ret.renderLayoutContext); } } catch (e) { const error = new Error(`Error rendering ${docInfo.vpath} with ${docInfo?.metadata?.layout} ${e.stack ? e.stack : e}`); ret.results.errors = ret.results.errors || []; ret.results.errors.push(error); // Use first render result as fallback ret.renderedLayout = ret.renderedFirst; } } ret.results.renderLayoutEnd = performance.now(); // END Layout Render // Mahabhuta ret.results.renderMahaStart = performance.now(); ret.renderMahaContext = { fspath: ret.docInfo.metadata.layout, content: ret.renderedLayout ? ret.renderedLayout : ret.renderedFirst, body: ret.renderedLayout ? ret.renderedLayout : ret.renderedFirst, metadata: {} }; ret.renderMahaContext.metadata = copyProperties(ret.renderMahaContext.metadata, ret.docInfo.metadata, false); try { if (ret.docInfo?.metadata?.config?.mahabhutaConfig) { mahabhuta.config(ret.docInfo?.metadata?.config?.mahabhutaConfig); } ret.renderedMaha = await mahabhuta.processAsync(ret.renderMahaContext.content, ret.renderMahaContext.metadata, ret.config.mahafuncs, // For performance collection config.perfDataDir ? new FilesystemPerfDataStore(config.perfDataDir) : undefined, config.perfDataDir ? ret.docInfo.vpath : undefined); } catch (e2) { const error = new Error(`Error with Mahabhuta ${ret.docInfo.vpath} with ${ret.docInfo?.metadata?.layout} ${e2.stack ? e2.stack : e2}`); ret.results.errors = ret.results.errors || []; ret.results.errors.push(error); // Use layout result or first render as fallback ret.renderedMaha = ret.renderMahaContext.content; } ret.results.renderMahaEnd = performance.now(); // END Mahabhuta try { const renderDest = path.join(ret.config.renderTo, ret.docInfo.renderPath); await fsp.mkdir(path.dirname(renderDest), { recursive: true }); await fsp.writeFile(renderDest, ret.renderedMaha, 'utf-8'); } catch (error) { ret.results.errors = ret.results.errors || []; ret.results.errors.push(error instanceof Error ? error : new Error(String(error))); } ret.results.renderEnd = performance.now(); // Calculate elapsed times if (ret.results.renderFirstStart && ret.results.renderFirstEnd) { ret.results.renderFirstElapsed = ret.results.renderFirstEnd - ret.results.renderFirstStart; } if (ret.results.renderLayoutStart && ret.results.renderLayoutEnd) { ret.results.renderLayoutElapsed = ret.results.renderLayoutEnd - ret.results.renderLayoutStart; } if (ret.results.renderMahaStart && ret.results.renderMahaEnd) { ret.results.renderMahaElapsed = ret.results.renderMahaEnd - ret.results.renderMahaStart; } if (ret.results.renderStart && ret.results.renderEnd) { ret.results.renderTotalElapsed = ret.results.renderEnd - ret.results.renderStart; } // console.log(`renderDocument2 ${ret.vpath}`, ret); return ret.results; } /** * Render a document, accounting for the main content, * a layout template (if any), and Mahabhuta (if the content * output is HTML). This also handles rendering other types * of content such as LESS CSS files. * * @param config * @param docInfo * @returns */ export async function renderDocument(config, docInfo) { const renderStart = new Date(); // Render the main content if (typeof docInfo.docContent !== 'string' || typeof docInfo.docBody !== 'string') { // console.warn(`No content to render for `, docInfo); } const rc = { fspath: docInfo.vpath, content: docInfo.docContent, body: docInfo.docBody, metadata: docInfo.metadata }; // console.log(`renderDocument context= ${rc.fspath}`, rc); let docFormat; // Knowing the format let docRendered; try { const result = await renderContent(config, rc); docFormat = result.format; docRendered = result.rendered; } catch (err) { console.error(`Error rendering ${docInfo.vpath} ${(err.stack ? err.stack : err)}`); throw new Error(`Error rendering ${docInfo.vpath} ${(err.stack ? err.stack : err)}`); } // console.log(`renderDocument ${docFormat} ${docInfo.vpath} rendered=`, docRendered); await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "FIRST RENDER", renderStart); // Handle these cases up front so that the remaining // code can be cleaner and focus on HTML layout rendering. if (docFormat === 'CSS') { try { return writeCSStoOutput(config, docInfo, docRendered, renderStart); } catch (err) { console.error(`in RENDER CSS branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`); throw new Error(`in RENDER CSS branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`); } } if (docFormat !== 'HTML') { try { copyAssetToOutput(config, docInfo, docRendered, renderStart); } catch (err) { console.error(`in copy branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`); throw new Error(`in copy branch for ${docInfo.vpath} to ${docInfo.renderPath} error=${err.stack ? err.stack : err}`); } } // Render the main content into a layout template, // if one is specified let layoutFormat; let layoutRendered; let result; // console.log(`renderDocument layout ${docInfo?.metadata?.layout} docMetadata ${util.inspect(docInfo.docMetadata)} metadata ${util.inspect(docInfo.metadata)}`); if (docInfo?.metadata?.layout) { const layouts = config.akasha.filecache.layoutsCache; // await layouts.isReady(); let found = await layouts.find(docInfo.metadata.layout); if (!found) { throw new Error(`No layout found in ${util.inspect(config.layoutDirs)} for ${docInfo?.metadata?.layout} in file ${docInfo.vpath}`); } const rcLayout = { fspath: docInfo.metadata.layout, content: found.docContent, body: found.docBody, metadata: {} }; rcLayout.metadata = copyProperties(rcLayout.metadata, found.metadata, false); rcLayout.metadata = copyProperties(rcLayout.metadata, docInfo.metadata, true); rcLayout.metadata.content = docRendered; try { result = await renderContent(config, rcLayout); layoutFormat = result.format; layoutRendered = result.rendered; } catch (e) { let ee = new Error(`Error rendering ${docInfo.vpath} with ${docInfo?.metadata?.layout} ${e.stack ? e.stack : e}`); console.error(ee); throw ee; } } else { layoutFormat = docFormat; layoutRendered = docRendered; } // console.log(`renderDocument ${docInfo.vpath} after layout render format ${layoutFormat} `, result); const renderSecondRender = new Date(); await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "SECOND RENDER", renderStart); // Next step is to run Mahabhuta on the rendered content // Of course, Mahabhuta is not appropriate for everything // because not everything is HTML try { const mahametadata = copyProperties({}, docInfo.metadata, false); mahametadata.content = docRendered; if (docInfo?.metadata?.config?.mahabhutaConfig) { mahabhuta.config(docInfo?.metadata?.config?.mahabhutaConfig); } // console.log(`mahametadata`, mahametadata); layoutRendered = await mahabhuta.processAsync(layoutRendered, mahametadata, config.mahafuncs); // OLD docrendered = await this.maharun(layoutrendered, docdata, config.mahafuncs); } catch (e2) { let eee = new Error(`Error with Mahabhuta ${docInfo.vpath} with ${docInfo?.metadata?.layout} ${e2.stack ? e2.stack : e2}`); console.error(eee); throw eee; } await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "MAHABHUTA", renderStart); const renderDest = path.join(config.renderTo, docInfo.renderPath); await fsp.mkdir(path.dirname(renderDest), { recursive: true }); await fsp.writeFile(renderDest, layoutRendered, 'utf-8'); // console.log(`RENDERED ${renderer.name} ${docInfo.path} ==> ${renderToFpath}`); const renderEndRendered = new Date(); await data.report(docInfo.mountPoint, docInfo.vpath, config.renderTo, "RENDERED", renderStart); return `${layoutFormat} ${docInfo.vpath} ==> ${docInfo.renderPath} (${(renderEndRendered.valueOf() - renderStart.valueOf()) / 1000} seconds)\n${await data.data4file(docInfo.mountPoint, docInfo.vpath)}`; } /** * Render all the documents in a site, limiting * the number of simultaneous rendering tasks * to the number in config.concurrency. * * @param config * @returns */ export async function render(config) { const documents = config.akasha.filecache.documentsCache; // await documents.isReady(); // console.log('CALLING config.hookBeforeSiteRendered'); await config.hookBeforeSiteRendered(); // 1. Gather list of files from RenderFileCache const filez = await documents.paths(); // console.log(`newerrender filez ${filez.length}`); // 2. Exclude any that we want to ignore const filez2 = []; for (let entry of filez) { let include = true; // console.log(entry); let stats; try { stats = await fsp.stat(entry.fspath); } catch (err) { stats = undefined; } if (!entry) include = false; else if (!stats || stats.isDirectory()) include = false; // This should arise using an ignore clause // else if (path.basename(entry.vpath) === '.DS_Store') include = false; // else if (path.basename(entry.vpath) === '.placeholder') include = false; if (include) { // The queue is an array of tuples containing the // config object and the path string filez2.push({ config: config, info: await documents.find(entry.vpath) }); } } // console.log(`newerrender filez2 after ignore ${filez2.length}`); // 3. Make a fastq to process using renderDocument, // pushing results to the results array // This sets up the queue processor // The concurrency setting lets us process documents // in parallel while limiting total impact. const queue = fastq.promise( // This function is invoked for each entry in the // queue. It handles rendering the queue // The queue has config objects and path strings // which is exactly what's required by // renderDocument async function renderDocumentInQueue(entry) { // console.log(`renderDocumentInQueue ${entry.info.vpath}`); try { let result = await renderDocument(entry.config, entry.info); // console.log(`DONE renderDocumentInQueue ${entry.info.vpath}`, result); return { result }; } catch (error) { // console.log(`ERROR renderDocumentInQueue ${entry.info.vpath}`, error.stack); return { error }; } }, config.concurrency); // queue.push returns a Promise that's fulfilled when // the task finishes. // Hence waitFor is an array of Promises. const waitFor = []; for (let entry of filez2) { waitFor.push(queue.push(entry)); } // This automatically waits for all those // Promises to resolve, while making the results // array contain results. const results = []; for (let result of waitFor) { results.push(await result); } // 4. Invoke hookSiteRendered try { // console.log('Invoking hookSiteRendered'); await config.hookSiteRendered(); } catch (e) { console.error(e.stack); throw new Error(`hookSiteRendered failed because ${e}`); } // 5. return results return results; } ; /** * Render all the documents in a site using renderDocument2, * limiting the number of simultaneous rendering tasks * to the number in config.concurrency. * * Returns structured RenderingResults data instead of text strings. * * @param config * @returns Array of RenderingResults with performance and error data */ export async function render2(config) { const documents = config.akasha.filecache.documentsCache; // await documents.isReady(); // console.log('CALLING config.hookBeforeSiteRendered'); await config.hookBeforeSiteRendered(); // 1. Gather list of files from RenderFileCache const filez = await documents.paths(); // console.log(`render2 filez ${filez.length}`); // 2. Exclude any that we want to ignore const filez2 = []; for (let entry of filez) { let include = true; // console.log(entry); let stats; try { stats = await fsp.stat(entry.fspath); } catch (err) { stats = undefined; } if (!entry) include = false; else if (!stats || stats.isDirectory()) include = false; // This should arise using an ignore clause // else if (path.basename(entry.vpath) === '.DS_Store') include = false; // else if (path.basename(entry.vpath) === '.placeholder') include = false; if (include) { // The queue is an array of tuples containing the // config object and the path string filez2.push({ config: config, info: await documents.find(entry.vpath) }); } } // console.log(`render2 filez2 after ignore ${filez2.length}`); // 3. Make a fastq to process using renderDocument2, // pushing results to the results array // This sets up the queue processor // The concurrency setting lets us process documents // in parallel while limiting total impact. const queue = fastq.promise( // This function is invoked for each entry in the // queue. It handles rendering the queue // The queue has config objects and path strings // which is exactly what's required by // renderDocument2 async function renderDocument2InQueue(entry) { // console.log(`renderDocument2InQueue ${entry.info.vpath}`); try { let result = await renderDocument2(entry.config, entry.info); // console.log(`DONE renderDocument2InQueue ${entry.info.vpath}`); return result; } catch (error) { console.log(`ERROR renderDocument2InQueue ${entry.info.vpath}`, error.stack); return undefined; } }, config.concurrency); // queue.push returns a Promise that's fulfilled when // the task finishes. // Hence waitFor is an array of Promises. const waitFor = []; for (let entry of filez2) { waitFor.push(queue.push(entry)); } // This automatically waits for all those // Promises to resolve, while making the results // array contain results. const results = []; for (let result of waitFor) { results.push(await result); } // 4. Invoke hookSiteRendered try { // console.log('Invoking hookSiteRendered'); await config.hookSiteRendered(); } catch (e) { console.error(e.stack); throw new Error(`hookSiteRendered failed because ${e}`); } // 5. return results return results; } ; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVuZGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vbGliL3JlbmRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFFSCxPQUFPLElBQUksTUFBTSxXQUFXLENBQUM7QUFDN0IsT0FBTyxFQUFFLFFBQVEsSUFBSSxHQUFHLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDMUMsT0FBTyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQzdCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sU0FBUyxFQUFFLEVBQ2QsdUJBQXVCLEVBQzFCLE1BQU0sV0FBVyxDQUFDO0FBRW5CLE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQVUxQixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUE0RDlDLFNBQVMsbUJBQW1CLENBQ3hCLE1BQXFCLEVBQ3JCLE9BQU87SUFFUCxNQUFNLEdBQUcsR0FBa0I7UUFDdkIsTUFBTTtRQUVOLGtCQUFrQixFQUFvQjtZQUNsQyxNQUFNLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDckIsT0FBTyxFQUFFLE9BQU8sQ0FBQyxVQUFVO1lBQzNCLElBQUksRUFBRSxPQUFPLENBQUMsT0FBTztZQUNyQixRQUFRLEVBQUUsT0FBTyxDQUFDLFFBQVE7U0FDN0I7UUFFRCxRQUFRLEVBQUUsTUFBTSxDQUFDLGdCQUFnQixDQUNqQixPQUFPLENBQUMsS0FBSyxDQUM1QjtRQUVELE9BQU87UUFDUCxLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7UUFDcEIsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVO1FBQzlCLFVBQVUsRUFBRSxPQUFPLENBQUMsVUFBVTtRQUM5QixRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVE7UUFFekIsT0FBTyxFQUFvQjtZQUN2QixLQUFLLEVBQUUsT0FBTyxDQUFDLEtBQUs7WUFDcEIsVUFBVSxFQUFFLE9BQU8sQ0FBQyxVQUFVO1lB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