From 71aec679cd603b0adc7a26a866eedc141e41eda2 Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Mon, 15 Jun 2026 15:59:19 +0100 Subject: [PATCH 01/24] fix: types for bun (#2956) --- .changeset/all-planets-know.md | 5 +++++ packages/myst-execute/src/execute.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/all-planets-know.md diff --git a/.changeset/all-planets-know.md b/.changeset/all-planets-know.md new file mode 100644 index 0000000000..bb2c151218 --- /dev/null +++ b/.changeset/all-planets-know.md @@ -0,0 +1,5 @@ +--- +'myst-execute': patch +--- + +Fix type export diff --git a/packages/myst-execute/src/execute.ts b/packages/myst-execute/src/execute.ts index 47789f8691..f8e08184ec 100644 --- a/packages/myst-execute/src/execute.ts +++ b/packages/myst-execute/src/execute.ts @@ -2,7 +2,7 @@ import { select, selectAll } from 'unist-util-select'; import type { Kernel } from '@jupyterlab/services'; import type { Code, InlineExpression, Outputs } from 'myst-spec-ext'; import type { IOutput } from '@jupyterlab/nbformat'; -import type { GenericParent, IExpressionError } from 'myst-common'; +import type { GenericParent, GenericNode, IExpressionError } from 'myst-common'; import { NotebookCell, fileError, RuleId } from 'myst-common'; import type { VFile } from 'vfile'; import assert from 'node:assert'; @@ -122,7 +122,7 @@ export function applyComputedOutputsToNodes( } } -export function getExecutableNodes(tree: GenericParent) { +export function getExecutableNodes(tree: GenericParent): ExecutableNode[] { // Pull out code-like nodes return ( ( From 659279c56399ad8e08dc29ee6e0a93ec73b1eb0a Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Mon, 15 Jun 2026 16:29:11 +0100 Subject: [PATCH 02/24] Use availableParallelism rather than CPUs (#2957) * fix: use availableParallelism rather than cpus This (should) gives physical cores. * chore: add changeset * fix: restore parallelism * Apply suggestion from @agoose77 --- .changeset/vast-brooms-clap.md | 5 +++++ packages/mystmd/src/clirun.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/vast-brooms-clap.md diff --git a/.changeset/vast-brooms-clap.md b/.changeset/vast-brooms-clap.md new file mode 100644 index 0000000000..025de5f099 --- /dev/null +++ b/.changeset/vast-brooms-clap.md @@ -0,0 +1,5 @@ +--- +'mystmd': patch +--- + +Use NodeJS parallelism parallelism feature diff --git a/packages/mystmd/src/clirun.ts b/packages/mystmd/src/clirun.ts index 3c260104e6..7523151098 100644 --- a/packages/mystmd/src/clirun.ts +++ b/packages/mystmd/src/clirun.ts @@ -3,7 +3,7 @@ import type { ISession, Session } from 'myst-cli'; import { checkNodeVersion, getNodeVersion, logVersions } from 'myst-cli'; import { chalkLogger, LogLevel } from 'myst-cli-utils'; import { Semaphore } from 'async-mutex'; -import { cpus } from 'node:os'; +import { availableParallelism } from 'node:os'; type SessionOpts = { debug?: boolean; @@ -35,7 +35,7 @@ export function clirun( const logger = chalkLogger(opts?.debug ? LogLevel.debug : LogLevel.info, process.cwd()); // Override default myst.yml if --config option is given. const configFiles = opts?.config ? [opts.config] : undefined; - const parallelCount = opts?.executeParallel ?? Math.max(1, cpus().length - 1); + const parallelCount = opts?.executeParallel ?? Math.max(1, availableParallelism() - 1); const executionSemaphore = new Semaphore(parallelCount); const session = new sessionClass({ logger, configFiles, executionSemaphore }); await session.reload(); From f2af8e9ec532437b63fed5583a7b15a59e05c653 Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Wed, 17 Jun 2026 10:48:29 -0600 Subject: [PATCH 03/24] Document multi-page transform pattern (#2950) --- docs/javascript-plugins.md | 6 ++++++ docs/plugins.md | 1 + 2 files changed, 7 insertions(+) diff --git a/docs/javascript-plugins.md b/docs/javascript-plugins.md index e90c6b4447..e0428feed4 100644 --- a/docs/javascript-plugins.md +++ b/docs/javascript-plugins.md @@ -85,6 +85,12 @@ I am **special bold text**, whilst I am **normal bold text** I am **special bold text**, whilst I am **normal bold text** +(plugins:multi-page)= +:::{seealso} Aggregate information across multiple pages +A transform runs on one page at a time, but a plugin can still collect information from every page in a project and render it in one place - for example, for a site-wide glossary or a list of figures. +See the [multi-page plugin demo](https://github.com/myst-contrib/plugin-multi-page-demo) for a working example and a walk-through of the pattern. +::: + ## Examples of plugins The documentation you're reading now defines several of its own plugins to extend MyST functionality. diff --git a/docs/plugins.md b/docs/plugins.md index d76c671981..5abd13e187 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -27,6 +27,7 @@ roles transforms : These plugins transform the document source while it is rendered. : For example, add metadata or transform a link to a DOI. +: Transforms can also [aggregate information across multiple pages](#plugins:multi-page), like a site-wide glossary. :::{warning} Planned - Not Yet Implemented :class: dropdown From aba633ae8a4c5088800d9787cb4e9fa859912167 Mon Sep 17 00:00:00 2001 From: Franklin Koch Date: Fri, 19 Jun 2026 01:51:52 -0600 Subject: [PATCH 04/24] =?UTF-8?q?=F0=9F=94=97=20Relax=20overaggressive=20D?= =?UTF-8?q?OI=20coercion=20(#2960)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/fruity-ties-speak.md | 7 ++ docs/settings.md | 7 ++ packages/myst-cli/src/process/mdast.ts | 3 +- packages/myst-cli/src/transforms/doi.spec.ts | 40 ++++++- packages/myst-cli/src/transforms/dois.ts | 8 +- .../src/settings/settings.yml | 16 +++ .../myst-frontmatter/src/settings/types.ts | 1 + .../src/settings/validators.ts | 17 ++- .../myst-transforms/src/links/doi.spec.ts | 112 +++++++++++++++++- packages/myst-transforms/src/links/doi.ts | 38 +++++- packages/myst-transforms/src/links/index.ts | 3 +- 11 files changed, 239 insertions(+), 13 deletions(-) create mode 100644 .changeset/fruity-ties-speak.md diff --git a/.changeset/fruity-ties-speak.md b/.changeset/fruity-ties-speak.md new file mode 100644 index 0000000000..70d28ba5a4 --- /dev/null +++ b/.changeset/fruity-ties-speak.md @@ -0,0 +1,7 @@ +--- +'myst-frontmatter': patch +'myst-transforms': patch +'myst-cli': patch +--- + +Relax overaggressive DOI coercion diff --git a/docs/settings.md b/docs/settings.md index 0b40d8a7a7..13d7fef788 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -85,6 +85,13 @@ dollarmath When disabled, dollar signs (`$`) will be treated as regular text characters and not parsed as math delimiters. This can be useful if you have frequent uses of dollar signs, such as currency, in your content that are not meant to be math. +(setting:infer_dois_from_urls)= +infer_dois_from_urls +: Infer DOIs from non-doi.org link URLs (for example, publisher article pages, biorxiv, or zenodo). + + - `false` (default): Only `doi.org` links, raw DOI strings, and `doi:` links are recognized + - `true`: infer DOIs from all links that end in a valid DOI (legacy behavior) + ## LaTeX Rendering Settings Adding an object of `myst_to_tex` to the settings will allow you to control various default parts of how the LaTeX renderer behaves. diff --git a/packages/myst-cli/src/process/mdast.ts b/packages/myst-cli/src/process/mdast.ts index 24602edd2f..68004e7445 100644 --- a/packages/myst-cli/src/process/mdast.ts +++ b/packages/myst-cli/src/process/mdast.ts @@ -234,6 +234,7 @@ export async function transformMdast( mdast, cache.$doiRenderers, file, + { inferDoisFromUrls: frontmatter.settings?.infer_dois_from_urls }, ); const rendererFiles = [file]; if (projectPath) { @@ -352,7 +353,7 @@ export async function postProcessMdast( new GithubTransformer(), new RRIDTransformer(), new RORTransformer(), - new DOITransformer(), // This also is picked up in the next transform + new DOITransformer({ inferDoisFromUrls: frontmatter.settings?.infer_dois_from_urls }), // This also is picked up in the next transform new MystTransformer(externalReferences), new SphinxTransformer(externalReferences), new StaticFileTransformer(session, file), // Links static files and internally linked files diff --git a/packages/myst-cli/src/transforms/doi.spec.ts b/packages/myst-cli/src/transforms/doi.spec.ts index 4b3348689c..edfb3f7603 100644 --- a/packages/myst-cli/src/transforms/doi.spec.ts +++ b/packages/myst-cli/src/transforms/doi.spec.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; +import { VFile } from 'vfile'; import { Session } from '../session'; -import { resolveDOIAsBibTeX, resolveDOIAsCSLJSON } from './dois'; +import { resolveDOIAsBibTeX, resolveDOIAsCSLJSON, transformLinkedDOIs } from './dois'; const PRIESTLEY_1972_CSL_JSON = [ { @@ -90,3 +91,40 @@ describe.each([ expect(data).toMatchObject(BARTELS_1997_CSL_JSON); }); }); + +describe('transformLinkedDOIs', () => { + describe.each([ + { + name: 'mybinder zenodo dataverse link', + url: 'https://mybinder.org/v2/zenodo/10.7910/DVN/EOYZKH/', + }, + { + name: 'iopscience article link', + url: 'https://iopscience.iop.org/article/10.3847/1538-3881/ace32f#ajace32ff10', + }, + { + name: 'frontiers article link', + url: 'https://www.frontiersin.org/articles/10.3389/fonc.2018.00134/full#supplementary-material', + }, + ])('$name', ({ url }) => { + it('does not auto-cite by default', async () => { + const mdast = { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ + { + type: 'link', + url, + children: [{ type: 'text', value: 'article' }], + }, + ], + }, + ], + }; + await transformLinkedDOIs(new Session(), new VFile(), mdast, {}, 'test.md'); + expect(mdast.children[0].children[0].type).toBe('link'); + }); + }); +}); diff --git a/packages/myst-cli/src/transforms/dois.ts b/packages/myst-cli/src/transforms/dois.ts index 8a463d1105..bc1fbf9238 100644 --- a/packages/myst-cli/src/transforms/dois.ts +++ b/packages/myst-cli/src/transforms/dois.ts @@ -1,6 +1,7 @@ import type { CitationRenderer, CSL } from 'citation-js-utils'; import { getCitationRenderers, parseBibTeX, parseCSLJSON } from 'citation-js-utils'; import { doi } from 'doi-utils'; +import { isRecognizedDoi, type DoiOptions } from 'myst-transforms'; import type { Link } from 'myst-spec'; import type { GenericNode, GenericParent } from 'myst-common'; import { toText, RuleId, plural, fileError } from 'myst-common'; @@ -229,6 +230,7 @@ export async function transformLinkedDOIs( mdast: GenericParent, doiRenderer: Record, path: string, + doiOpts?: DoiOptions, ): Promise { const toc = tic(); const renderer: CitationRenderer = {}; @@ -236,12 +238,12 @@ export async function transformLinkedDOIs( const citeDois: Cite[] = []; selectAll('link', mdast).forEach((node: GenericNode) => { const { url } = node as Link; - if (!doi.validate(url)) return; + if (!isRecognizedDoi(url, doiOpts)) return; linkedDois.push(node as Link); }); selectAll('cite', mdast).forEach((node: GenericNode) => { const { label } = node as Cite; - if (!doi.validate(label)) return; + if (!isRecognizedDoi(label, doiOpts)) return; citeDois.push(node as Cite); }); if (linkedDois.length === 0 && citeDois.length === 0) return renderer; @@ -278,7 +280,7 @@ export async function transformLinkedDOIs( citeNode.kind = 'narrative'; citeNode.label = label; citeNode.identifier = node.url; - if (doi.validate(toText(citeNode.children))) { + if (isRecognizedDoi(toText(citeNode.children), doiOpts)) { // If the link text is the DOI, update with a citation in a following pass citeNode.children = []; } diff --git a/packages/myst-frontmatter/src/settings/settings.yml b/packages/myst-frontmatter/src/settings/settings.yml index 32e751b4d6..f824ef8e25 100644 --- a/packages/myst-frontmatter/src/settings/settings.yml +++ b/packages/myst-frontmatter/src/settings/settings.yml @@ -24,3 +24,19 @@ cases: what: ok warnings: 1 normalized: {} + - title: infer dois from urls + raw: + settings: + infer_dois_from_urls: true + errors: 0 + normalized: + settings: + infer_dois_from_urls: true + - title: infer dois from urls alias + raw: + settings: + inferDoisFromUrls: true + errors: 0 + normalized: + settings: + infer_dois_from_urls: true diff --git a/packages/myst-frontmatter/src/settings/types.ts b/packages/myst-frontmatter/src/settings/types.ts index 2c659c80ee..71d818b63c 100644 --- a/packages/myst-frontmatter/src/settings/types.ts +++ b/packages/myst-frontmatter/src/settings/types.ts @@ -15,4 +15,5 @@ export type ProjectSettings = { output_matplotlib_strings?: OutputRemovalOptions; myst_to_tex?: MystToTexSettings; parser?: ParserSettings; + infer_dois_from_urls?: boolean; }; diff --git a/packages/myst-frontmatter/src/settings/validators.ts b/packages/myst-frontmatter/src/settings/validators.ts index 83e7c4c2a7..4ae0c06fd6 100644 --- a/packages/myst-frontmatter/src/settings/validators.ts +++ b/packages/myst-frontmatter/src/settings/validators.ts @@ -1,5 +1,11 @@ import type { ValidationOptions } from 'simple-validators'; -import { defined, incrementOptions, validateChoice, validateObjectKeys } from 'simple-validators'; +import { + defined, + incrementOptions, + validateBoolean, + validateChoice, + validateObjectKeys, +} from 'simple-validators'; import type { ProjectSettings } from './types.js'; import { validateMystToTexSettings } from './validatorsMystToTex.js'; import { validateParserSettings } from './validatorsParser.js'; @@ -18,12 +24,14 @@ export const PROJECT_SETTINGS = [ 'output_matplotlib_strings', 'myst_to_tex', 'parser', + 'infer_dois_from_urls', ]; export const PROJECT_SETTINGS_ALIAS = { stderr_output: 'output_stderr', stdout_output: 'output_stdout', mystToTex: 'myst_to_tex', tex: 'myst_to_tex', // The default is the renderer, not the parser + inferDoisFromUrls: 'infer_dois_from_urls', }; export function validateProjectAndPageSettings( @@ -69,6 +77,13 @@ export function validateProjectAndPageSettings( const parser = validateParserSettings(settings.parser, incrementOptions('parser', opts)); if (parser) output.parser = parser; } + if (defined(settings.infer_dois_from_urls)) { + const infer_dois_from_urls = validateBoolean( + settings.infer_dois_from_urls, + incrementOptions('infer_dois_from_urls', opts), + ); + if (infer_dois_from_urls != null) output.infer_dois_from_urls = infer_dois_from_urls; + } if (Object.keys(output).length === 0) return undefined; return output; } diff --git a/packages/myst-transforms/src/links/doi.spec.ts b/packages/myst-transforms/src/links/doi.spec.ts index 176732d6bb..51824a95b1 100644 --- a/packages/myst-transforms/src/links/doi.spec.ts +++ b/packages/myst-transforms/src/links/doi.spec.ts @@ -1,10 +1,75 @@ import { describe, expect, test } from 'vitest'; import { VFile } from 'vfile'; import type { Link } from 'myst-spec-ext'; -import { DOITransformer } from './doi'; +import { DOITransformer, isRecognizedDoi } from './doi'; -describe('Test GithubTransformer', () => { - test('any github link', async () => { +describe('isRecognizedDoi', () => { + test('dx.doi.org URL', () => { + expect(isRecognizedDoi('https://dx.doi.org/10.1016/j.ultramic.2018.06.001')).toBe(true); + }); + test('www.doi.org URL', () => { + expect(isRecognizedDoi('https://www.doi.org/10.1016/j.ultramic.2018.06.001')).toBe(true); + }); + test('hostname ending in doi.org is not treated as doi.org', () => { + expect(isRecognizedDoi('https://notadoi.org/10.1038/s41586-020-2649-2')).toBe(false); + }); + test('short doi.org URL', () => { + expect(isRecognizedDoi('https://doi.org/cr3qwn')).toBe(true); + }); + test('doi.org URL with special characters', () => { + expect( + isRecognizedDoi( + 'https://doi.org/10.1002/(SICI)1096-987X(199709)18:12%3C1450::AID-JCC3%3E3.0.CO;2-I', + ), + ).toBe(true); + }); + test('raw DOI', () => { + expect(isRecognizedDoi('10.1016/j.ultramic.2018.06.001')).toBe(true); + }); + test('doi: prefix', () => { + expect(isRecognizedDoi('doi:10.1016/j.ultramic.2018.06.001')).toBe(true); + }); + test('invalid doi: prefix is not recognized', () => { + expect(isRecognizedDoi('doi:invalid')).toBe(false); + }); + test('publisher URL with embedded DOI', () => { + expect(isRecognizedDoi('https://www.nature.com/articles/10.1038/s41586-020-2649-2')).toBe( + false, + ); + }); + test('publisher URL with embedded DOI when inferDoisFromUrls is enabled', () => { + expect( + isRecognizedDoi('https://www.nature.com/articles/10.1038/s41586-020-2649-2', { + inferDoisFromUrls: true, + }), + ).toBe(true); + }); + + describe.each([ + { + name: 'mybinder zenodo dataverse link', + url: 'https://mybinder.org/v2/zenodo/10.7910/DVN/EOYZKH/', + }, + { + name: 'iopscience article link', + url: 'https://iopscience.iop.org/article/10.3847/1538-3881/ace32f#ajace32ff10', + }, + { + name: 'frontiers article link', + url: 'https://www.frontiersin.org/articles/10.3389/fonc.2018.00134/full#supplementary-material', + }, + ])('$name', ({ url }) => { + test('is not recognized by default', () => { + expect(isRecognizedDoi(url)).toBe(false); + }); + test('is recognized when inferDoisFromUrls is enabled', () => { + expect(isRecognizedDoi(url, { inferDoisFromUrls: true })).toBe(true); + }); + }); +}); + +describe('DOITransformer', () => { + test('doi.org link', async () => { const file = new VFile(); const t = new DOITransformer(); const url = 'https://doi.org/10.1016/j.ultramic.2018.06.001'; @@ -18,4 +83,45 @@ describe('Test GithubTransformer', () => { expect(link.url).toBe(url); expect(link.data?.doi).toEqual('10.1016/j.ultramic.2018.06.001'); }); + test('publisher URL with embedded DOI is not matched', () => { + const t = new DOITransformer(); + const url = 'https://www.nature.com/articles/10.1038/s41586-020-2649-2'; + expect(t.test(url)).toBe(false); + }); + test('publisher URL with embedded DOI is matched when enabled', () => { + const t = new DOITransformer({ inferDoisFromUrls: true }); + const url = 'https://www.nature.com/articles/10.1038/s41586-020-2649-2'; + expect(t.test(url)).toBe(true); + }); + test('invalid doi: link fails transform with error', () => { + const file = new VFile(); + const t = new DOITransformer(); + const url = 'doi:invalid'; + const link: Link = { type: 'link', url, children: [] }; + expect(t.test(url)).toBe(true); + expect(t.transform(link, file)).toBe(false); + expect(file.messages.length).toBeGreaterThan(0); + }); + test('short doi.org link transforms', () => { + const file = new VFile(); + const t = new DOITransformer(); + const url = 'https://doi.org/cr3qwn'; + const link: Link = { type: 'link', url, children: [] }; + expect(t.test(url)).toBe(true); + expect(t.transform(link, file)).toBe(true); + expect(link.url).toBe(url); + expect(link.data?.doi).toEqual('cr3qwn'); + }); + test('doi.org link with special characters transforms', () => { + const file = new VFile(); + const t = new DOITransformer(); + const url = + 'https://doi.org/10.1002/(SICI)1096-987X(199709)18:12%3C1450::AID-JCC3%3E3.0.CO;2-I'; + const link: Link = { type: 'link', url, children: [] }; + expect(t.test(url)).toBe(true); + expect(t.transform(link, file)).toBe(true); + expect(link.data?.doi).toEqual( + '10.1002/(SICI)1096-987X(199709)18:12%3C1450::AID-JCC3%3E3.0.CO;2-I', + ); + }); }); diff --git a/packages/myst-transforms/src/links/doi.ts b/packages/myst-transforms/src/links/doi.ts index b6ca0aee05..9f33fb5453 100644 --- a/packages/myst-transforms/src/links/doi.ts +++ b/packages/myst-transforms/src/links/doi.ts @@ -7,16 +7,48 @@ import { updateLinkTextIfEmpty } from './utils.js'; const TRANSFORM_SOURCE = 'LinkTransform:DOITransformer'; +/** Canonical doi.org resolver hosts (exact hostname match). */ +const DOI_ORG_HOSTNAMES = new Set(['doi.org', 'www.doi.org', 'dx.doi.org']); + +function isDoiOrgHostname(hostname: string): boolean { + return DOI_ORG_HOSTNAMES.has(hostname.toLowerCase()); +} + +export type DoiOptions = { + /** When true, infer DOIs from non-doi.org URLs (publisher pages, biorxiv, zenodo, etc.). */ + inferDoisFromUrls?: boolean; +}; + +/** + * Decide whether a string should be treated as a DOI link or citation label. + * + * Unless `inferDoisFromUrls` is enabled, URLs with a non-doi.org hostname are rejected. + * Otherwise, delegates to doi-utils validation. + */ +export function isRecognizedDoi(uri?: string, opts?: DoiOptions): boolean { + if (!uri) return false; + if (!opts?.inferDoisFromUrls) { + try { + const url = new URL(uri); + if (url.hostname && !isDoiOrgHostname(url.hostname)) return false; + } catch { + // Not a URL — fall through to doi-utils validation. + } + } + return doi.validate(uri); +} + export class DOITransformer implements LinkTransformer { protocol = 'doi'; + constructor(private doiOpts?: DoiOptions) {} + test(uri?: string): boolean { if (uri?.startsWith('doi:')) { - // This may not be valid but flag it for the transform + // May not be valid, but flag for transform to report errors. return true; } - if (uri && doi.validate(uri)) return true; - return false; + return isRecognizedDoi(uri, this.doiOpts); } transform(link: Link, file: VFile): boolean { diff --git a/packages/myst-transforms/src/links/index.ts b/packages/myst-transforms/src/links/index.ts index 74e0be07fc..427f88b0a3 100644 --- a/packages/myst-transforms/src/links/index.ts +++ b/packages/myst-transforms/src/links/index.ts @@ -4,7 +4,8 @@ export { SphinxTransformer } from './sphinx.js'; export { WikiTransformer } from './wiki.js'; export { RRIDTransformer } from './rrid.js'; export { RORTransformer } from './ror.js'; -export { DOITransformer } from './doi.js'; +export { DOITransformer, isRecognizedDoi } from './doi.js'; +export type { DoiOptions } from './doi.js'; export { GithubTransformer } from './github.js'; export type { LinkTransformer, MystXRef, MystXRefs, ResolvedExternalReference } from './types.js'; export { updateLinkTextIfEmpty } from './utils.js'; From 7fda16f2b369f239b49d9a2ca41c4b85f1d320ef Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Sat, 20 Jun 2026 18:44:02 +0100 Subject: [PATCH 05/24] Switch to `.ipynb` build cache (#2970) * feat: specify cache extension * wip: refactor cache * feat: add tiered cache * fix: support ipynb * revert: changes to inline expression metadata * fix: more cleanup * fix: more missing imports * fix: type of block * chore: add changeset * fix: linter warning * fix: more linter fixes * Revert "fix: linter warning" This reverts commit 8a1337329ec8b479b8742f412611fee61b9082a4. * feat: add duration (ms) --- .changeset/dirty-steaks-burn.md | 9 + bun.lock | 2 + packages/myst-cli-utils/src/session.ts | 1 + packages/myst-cli/src/process/mdast.ts | 16 +- packages/myst-cli/src/process/notebook.ts | 8 +- .../src/transforms/inlineExpressions.ts | 8 +- packages/myst-common/src/index.ts | 3 - packages/myst-common/src/types.ts | 27 --- packages/myst-execute/package.json | 1 + packages/myst-execute/src/cache.ts | 228 +++++++++++++++++- packages/myst-execute/src/execute.ts | 39 ++- packages/myst-execute/src/index.ts | 9 +- packages/myst-execute/src/kernel.ts | 8 +- packages/myst-execute/src/transform.ts | 28 ++- packages/myst-execute/src/types.ts | 50 ++-- packages/myst-execute/src/utils.ts | 4 +- packages/myst-spec/src/ext.ts | 37 +++ packages/myst-spec/src/index.ts | 4 + packages/myst-to-html/package.json | 1 + 19 files changed, 389 insertions(+), 94 deletions(-) create mode 100644 .changeset/dirty-steaks-burn.md diff --git a/.changeset/dirty-steaks-burn.md b/.changeset/dirty-steaks-burn.md new file mode 100644 index 0000000000..6f4b17cf48 --- /dev/null +++ b/.changeset/dirty-steaks-burn.md @@ -0,0 +1,9 @@ +--- +'myst-execute': minor +'myst-cli': minor +'myst-to-html': patch +'myst-common': patch +'myst-spec': patch +--- + +Switch to ipynb build cache diff --git a/bun.lock b/bun.lock index de69f493d7..0105527721 100644 --- a/bun.lock +++ b/bun.lock @@ -259,6 +259,7 @@ "get-port": "^6.1.2", "myst-cli-utils": "^2.0.13", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "node-fetch": "^3.3.0", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", @@ -488,6 +489,7 @@ "mdast-util-find-and-replace": "^2.1.0", "mdast-util-to-hast": "^12.3.0", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "rehype-format": "^4.0.1", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", diff --git a/packages/myst-cli-utils/src/session.ts b/packages/myst-cli-utils/src/session.ts index ffacd68d0a..1a5034c2ae 100644 --- a/packages/myst-cli-utils/src/session.ts +++ b/packages/myst-cli-utils/src/session.ts @@ -7,6 +7,7 @@ export class Session implements ISession { constructor(opts?: { logger?: Logger }) { this.log = opts?.logger ?? chalkLogger(LogLevel.debug, process.cwd()); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars fetch(url: URL | RequestInfo, init?: RequestInit | undefined): Promise { throw new Error('fetch not implemented on session'); } diff --git a/packages/myst-cli/src/process/mdast.ts b/packages/myst-cli/src/process/mdast.ts index 68004e7445..fcd71cba43 100644 --- a/packages/myst-cli/src/process/mdast.ts +++ b/packages/myst-cli/src/process/mdast.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import { tic } from 'myst-cli-utils'; -import type { GenericParent, IExpressionResult, PluginUtils, References } from 'myst-common'; +import type { GenericParent, PluginUtils, References } from 'myst-common'; import { fileError, fileWarn, RuleId, slugToUrl } from 'myst-common'; import type { PageFrontmatter } from 'myst-frontmatter'; import { SourceFileKind } from 'myst-spec-ext'; @@ -74,8 +74,13 @@ import { logMessagesFromVFile } from '../utils/logging.js'; import { combineCitationRenderers } from './citations.js'; import { bibFilesInDir, selectFile } from './file.js'; import { parseMyst } from './myst.js'; -import { kernelExecutionTransform, LocalDiskCache } from 'myst-execute'; -import type { IOutput } from '@jupyterlab/nbformat'; +import { + kernelExecutionTransform, + LocalDiskCache, + LegacyExecutionCache, + NotebookExecutionCache, + TieredExecutionCache, +} from 'myst-execute'; import { rawDirectiveTransform } from '../transforms/raw.js'; import { addEditUrl } from '../utils/addEditUrl.js'; import { @@ -191,7 +196,10 @@ export async function transformMdast( session.log.debug(`▶️ Executing: ${fileName}`); await kernelExecutionTransform(mdast, vfile, { basePath: session.sourcePath(), - cache: new LocalDiskCache<(IExpressionResult | IOutput[])[]>(cachePath), + cache: new TieredExecutionCache( + new NotebookExecutionCache(new LocalDiskCache(cachePath, '.ipynb')), + new LegacyExecutionCache(cachePath), + ), sessionFactory: () => session.jupyterSessionManager(), frontmatter: frontmatter, ignoreCache: false, diff --git a/packages/myst-cli/src/process/notebook.ts b/packages/myst-cli/src/process/notebook.ts index e782084ea4..d6f842e705 100644 --- a/packages/myst-cli/src/process/notebook.ts +++ b/packages/myst-cli/src/process/notebook.ts @@ -16,9 +16,9 @@ import { logMessagesFromVFile } from '../utils/logging.js'; import type { ISession } from '../session/types.js'; import { BASE64_HEADER_SPLIT } from '../transforms/images.js'; import { parseMyst } from './myst.js'; -import type { Code, InlineExpression } from 'myst-spec-ext'; -import type { IUserExpressionMetadata } from '../transforms/inlineExpressions.js'; +import type { Code, InlineExpression } from 'myst-spec'; import { findExpression, metadataSection } from '../transforms/inlineExpressions.js'; +import type { IUserExpressionMetadata } from '../transforms/inlineExpressions.js'; import { frontmatterValidationOpts } from '../frontmatter.js'; import { filterKeys } from 'simple-validators'; @@ -92,9 +92,7 @@ function embedInlineExpressions( ) { const inlineNodes = selectAll('inlineExpression', block) as InlineExpression[]; inlineNodes.forEach((inlineExpression) => { - const data = findExpression(userExpressions, inlineExpression.value); - if (!data) return; - inlineExpression.result = data.result as unknown as Record; + inlineExpression.result = findExpression(userExpressions, inlineExpression.value)?.result; }); } diff --git a/packages/myst-cli/src/transforms/inlineExpressions.ts b/packages/myst-cli/src/transforms/inlineExpressions.ts index e467a8ea39..3054320cca 100644 --- a/packages/myst-cli/src/transforms/inlineExpressions.ts +++ b/packages/myst-cli/src/transforms/inlineExpressions.ts @@ -1,8 +1,7 @@ -import type { GenericParent, IExpressionResult } from 'myst-common'; +import type { GenericParent } from 'myst-common'; import { fileWarn, RuleId } from 'myst-common'; import { selectAll } from 'unist-util-select'; -import type { InlineExpression } from 'myst-spec-ext'; -import type { StaticPhrasingContent } from 'myst-spec'; +import type { StaticPhrasingContent, InlineExpression, IExpressionResult } from 'myst-spec'; import type { Plugin } from 'unified'; import type { VFile } from 'vfile'; import { BASE64_HEADER_SPLIT } from './images.js'; @@ -79,9 +78,6 @@ function renderExpression(node: InlineExpression, file: VFile): StaticPhrasingCo export function transformRenderInlineExpressions(mdast: GenericParent, file: VFile) { const inlineNodes = selectAll('inlineExpression', mdast) as InlineExpression[]; inlineNodes.forEach((inlineExpression) => { - if (!inlineExpression.result) { - return; - } inlineExpression.children = renderExpression(inlineExpression, file); }); } diff --git a/packages/myst-common/src/index.ts b/packages/myst-common/src/index.ts index 18ab060cd3..e2147d0e8f 100644 --- a/packages/myst-common/src/index.ts +++ b/packages/myst-common/src/index.ts @@ -34,9 +34,6 @@ export { export type { IndexTypeLists } from './indices.js'; export type { MessageInfo } from './utils.js'; export type { - IExpressionResult, - IExpressionError, - IExpressionOutput, GenericNode, GenericParent, Citations, diff --git a/packages/myst-common/src/types.ts b/packages/myst-common/src/types.ts index 90a2165ba5..e6643de669 100644 --- a/packages/myst-common/src/types.ts +++ b/packages/myst-common/src/types.ts @@ -1,8 +1,6 @@ import type { Plugin } from 'unified'; import type { Directive, Node, Role } from 'myst-spec'; import type { VFile } from 'vfile'; -import type * as nbformat from '@jupyterlab/nbformat'; -import type { PartialJSONObject } from '@lumino/coreutils'; import type { PageFrontmatter } from 'myst-frontmatter'; export type GenericNode = Record> = { @@ -173,31 +171,6 @@ export enum AdmonitionKind { tip = 'tip', warning = 'warning', } - -export interface IExpressionOutput { - status: 'ok'; - data: nbformat.IMimeBundle; - metadata: PartialJSONObject; -} - -export interface IExpressionError { - status: 'error'; - /** - * Exception name - */ - ename: string; - /** - * Exception value - */ - evalue: string; - /** - * Traceback - */ - traceback: string[]; -} - -export type IExpressionResult = IExpressionError | IExpressionOutput; - export type FrontmatterPart = { mdast: GenericParent; frontmatter?: PageFrontmatter }; export type FrontmatterParts = Record; diff --git a/packages/myst-execute/package.json b/packages/myst-execute/package.json index 4adb5b8d2d..2417a618e7 100644 --- a/packages/myst-execute/package.json +++ b/packages/myst-execute/package.json @@ -38,6 +38,7 @@ "get-port": "^6.1.2", "myst-cli-utils": "^2.0.13", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "node-fetch": "^3.3.0", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", diff --git a/packages/myst-execute/src/cache.ts b/packages/myst-execute/src/cache.ts index af6f25f918..f35643760e 100644 --- a/packages/myst-execute/src/cache.ts +++ b/packages/myst-execute/src/cache.ts @@ -1,20 +1,33 @@ +import type { + DocumentExecutionResult, + ExecutionResult, + LegacyExecutionResult, + CodeResult, +} from './types.js'; +import { isCodeResult } from './types.js'; import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; +import type { INotebookContent, IOutput } from '@jupyterlab/nbformat'; +import { isCode, isError, isDisplayData } from '@jupyterlab/nbformat'; +import type { IExpressionError, IExpressionDisplay } from 'myst-spec'; export interface ICache { test(key: string): boolean; get(key: string): T | undefined; - set(key: string, value: T): void; + set(key: string, result: T): void; } +export type IDocumentExecutionCache = ICache; + /** * An implementation of a basic cache */ export class LocalDiskCache implements ICache { - constructor(cachePath: string) { + constructor(cachePath: string, extension: string) { this._cachePath = cachePath; + this._extension = extension; if (!existsSync(cachePath)) { mkdirSync(cachePath, { recursive: true }); @@ -22,9 +35,10 @@ export class LocalDiskCache implements ICache { } private readonly _cachePath: string; + private readonly _extension: string; private _makeKeyPath(key: string): string { - return path.join(this._cachePath, `${key}.json`); + return path.join(this._cachePath, `${key}${this._extension}`); } test(key: string): boolean { @@ -39,8 +53,212 @@ export class LocalDiskCache implements ICache { return JSON.parse(readFileSync(keyPath, { encoding: 'utf8' })); } - set(key: string, item: T) { + set(key: string, document: T) { const keyPath = this._makeKeyPath(key); - return writeFileSync(keyPath, JSON.stringify(item), { encoding: 'utf8' }); + return writeFileSync(keyPath, JSON.stringify(document), { encoding: 'utf8' }); + } +} + +/** + * Type guard for legacy IOutput[] arrays + * + * @param result legacy cache result + */ +function isLegacyOutputArray(result: LegacyExecutionResult): result is IOutput[] { + return Array.isArray(result); +} + +/** + * Implement IDocumentExecutionCache for legacy caches + */ +export class LegacyExecutionCache implements IDocumentExecutionCache { + private readonly cache: LocalDiskCache; + constructor(cachePath: string) { + this.cache = new LocalDiskCache(cachePath, '.json'); + } + + test(key: string): boolean { + return this.cache.test(key); + } + + get(key: string): DocumentExecutionResult | undefined { + const legacyHit = this.cache.get(key); + if (legacyHit === undefined) { + return undefined; + } + return { + context: {}, + results: legacyHit.map((item) => { + if (isLegacyOutputArray(item)) { + return { + type: 'code', + responses: item, + }; + } else { + return { + type: 'inlineExpression', + response: item, + }; + } + }), + }; + } + + set(key: string, document: DocumentExecutionResult) { + const legacyDocument = document.results.map((result) => { + if (result.type === 'code') { + return result.responses; + } else { + return result.response; + } + }); + this.cache.set(key, legacyDocument); + } +} + +export type LocalExecutionCache = LocalDiskCache; + +type NotebookResultMetadata = { + mystResultType: ExecutionResult['type']; +}; + +/** + * IDocumentExecutionCache that stores outputs and expression results in ipynb files + */ +export class NotebookExecutionCache implements IDocumentExecutionCache { + private baseCache: ICache; + + constructor(baseCache: ICache) { + this.baseCache = baseCache; + } + + test(key: string): boolean { + return this.baseCache.test(key); + } + get(key: string): DocumentExecutionResult | undefined { + const notebook = this.baseCache.get(key); + if (notebook === undefined) { + return undefined; + } + return { + context: (notebook.metadata?.mystContext ?? {}) as Record, + results: notebook.cells.map((cell) => { + if (!isCode(cell)) { + throw new Error('Invalid cell in cache'); + } else { + const resultType = (cell.metadata as NotebookResultMetadata).mystResultType; + if (resultType === 'code') { + return { + type: 'code', + responses: cell.outputs, + } satisfies CodeResult; + } else { + return { + type: 'inlineExpression', + response: cell.outputs + .map((output) => { + if (isError(output)) { + return { + status: 'error', + ename: output.ename, + evalue: output.evalue, + traceback: output.traceback, + } satisfies IExpressionError; + } else if (isDisplayData(output)) { + return { + status: 'ok', + data: output.data, + metadata: output.metadata, + } satisfies IExpressionDisplay; + } else { + throw new Error('Invalid cell for inlineExpression result'); + } + }) + .shift()!, + }; + } + } + }), + } satisfies DocumentExecutionResult; + } + set(key: string, document: DocumentExecutionResult) { + const notebook: INotebookContent = { + nbformat: 4, + nbformat_minor: 5, + metadata: { + mystContext: document.context, + }, + cells: document.results.map((result, index) => { + if (isCodeResult(result)) { + return { + cell_type: 'code', + source: [], + metadata: { + mystResultType: result.type, + } satisfies NotebookResultMetadata, + outputs: result.responses, + execution_count: index, + }; + } else { + return { + cell_type: 'code', + source: [], + metadata: { + mystResultType: result.type, + } satisfies NotebookResultMetadata, + outputs: [result.response].map((response) => { + switch (response.status) { + case 'ok': { + return { + output_type: 'display_data', + data: response.data, + metadata: response.metadata, + }; + } + case 'error': { + return { + output_type: 'error', + ename: response.ename, + evalue: response.evalue, + traceback: response.traceback, + }; + } + default: { + throw new Error('This cannot happen'); + } + } + }), + execution_count: index, + }; + } + }), + }; + this.baseCache.set(key, notebook); + } +} + +/** + * IDocumentExecutionCache that reads and writes to a primary cache, + * but falls back upon a secondary for reading + */ +export class TieredExecutionCache implements IDocumentExecutionCache { + private primary: IDocumentExecutionCache; + private secondary: IDocumentExecutionCache; + + constructor(primary: IDocumentExecutionCache, secondary: IDocumentExecutionCache) { + this.primary = primary; + this.secondary = secondary; + } + + test(key: string): boolean { + return this.primary.test(key) || this.secondary.test(key); + } + + get(key: string): DocumentExecutionResult | undefined { + return this.primary.get(key) || this.secondary.get(key); + } + + set(key: string, document: DocumentExecutionResult) { + return this.primary.set(key, document); } } diff --git a/packages/myst-execute/src/execute.ts b/packages/myst-execute/src/execute.ts index f8e08184ec..35f4d30f5c 100644 --- a/packages/myst-execute/src/execute.ts +++ b/packages/myst-execute/src/execute.ts @@ -1,12 +1,11 @@ import { select, selectAll } from 'unist-util-select'; import type { Kernel } from '@jupyterlab/services'; -import type { Code, InlineExpression, Outputs } from 'myst-spec-ext'; -import type { IOutput } from '@jupyterlab/nbformat'; -import type { GenericParent, GenericNode, IExpressionError } from 'myst-common'; +import type { Code, InlineExpression, Outputs, CodeBlock, IExpressionError } from 'myst-spec'; +import type { GenericParent } from 'myst-common'; import { NotebookCell, fileError, RuleId } from 'myst-common'; import type { VFile } from 'vfile'; import assert from 'node:assert'; -import type { CodeBlock, ExecutionResult, ExecutableNode } from './types.js'; +import type { ExecutionResult, ExecutableNode, DocumentExecutionResult } from './types.js'; import { executeCodeCell, evaluateInlineExpression } from './kernel.js'; import { isCodeBlock, @@ -14,6 +13,7 @@ import { codeBlockSkipsExecution, isInlineExpression, } from './utils.js'; +import { isCodeResult, isExpressionResult } from './types.js'; /** * For each executable node, perform a kernel execution request and return the results. @@ -28,7 +28,7 @@ export async function computeExecutableNodes( nodes: ExecutableNode[], opts: { vfile: VFile }, ): Promise<{ - results: ExecutionResult[]; + results: DocumentExecutionResult['results']; errorOccurred: boolean; }> { let errorOccurred = false; @@ -40,7 +40,10 @@ export async function computeExecutableNodes( const code = select('code', matchedNode) as Code; const { status, outputs } = await executeCodeCell(kernel, code.value); // Cache result - results.push(outputs); + results.push({ + type: 'code', + responses: outputs, + }); // Check for errors const allowErrors = codeBlockRaisesException(matchedNode); @@ -78,12 +81,18 @@ export async function computeExecutableNodes( break; } // Cache result - results.push(result); + results.push({ + type: 'inlineExpression', + response: result, + }); } else { assert(false); } } - return { results, errorOccurred }; + return { + results, + errorOccurred, + }; } /** @@ -96,25 +105,29 @@ export async function computeExecutableNodes( */ export function applyComputedOutputsToNodes( nodes: ExecutableNode[], - computedResult: ExecutionResult[], + computedResult: DocumentExecutionResult['results'], ) { for (const matchedNode of nodes) { // Pull out the result for this node const thisResult = computedResult.shift(); if (isCodeBlock(matchedNode)) { - const rawOutputData = (thisResult as IOutput[]) ?? []; + if (thisResult !== undefined && !isCodeResult(thisResult)) { + throw new Error('Expected code result for code cell'); + } // Pull out outputs to set data const outputs = select('outputs', matchedNode) as Outputs; // Ensure that whether this fails or succeeds, we write to `children` (e.g. due to a kernel error) - outputs.children = rawOutputData.map((data, index) => { + outputs.children = (thisResult?.responses ?? []).map((data, index) => { const identifier = outputs.identifier ? `${outputs.identifier}-${index}` : undefined; return { type: 'output', children: [], jupyter_data: data as any, identifier }; }); } else if (isInlineExpression(matchedNode)) { - const rawOutputData = thisResult as Record | undefined; + if (thisResult !== undefined && !isExpressionResult(thisResult)) { + throw new Error('Expected expression result for inline expression'); + } // Set data of expression to the result, or empty if we don't have one - matchedNode.result = rawOutputData; + matchedNode.result = thisResult?.response; } else { // This should never happen throw new Error('Node must be either code block or inline expression.'); diff --git a/packages/myst-execute/src/index.ts b/packages/myst-execute/src/index.ts index 5e33996ded..a1aa6451aa 100644 --- a/packages/myst-execute/src/index.ts +++ b/packages/myst-execute/src/index.ts @@ -1,5 +1,10 @@ export { kernelExecutionTransform } from './transform.js'; export type { JupyterServerSettings } from './manager.js'; export { findExistingJupyterServer, launchJupyterServer } from './manager.js'; -export { LocalDiskCache } from './cache.js'; -export type { ICache } from './cache.js'; +export { + NotebookExecutionCache, + LegacyExecutionCache, + LocalDiskCache, + TieredExecutionCache, +} from './cache.js'; +export type { ICache, LocalExecutionCache } from './cache.js'; diff --git a/packages/myst-execute/src/kernel.ts b/packages/myst-execute/src/kernel.ts index 6ade13ee96..4e6951778d 100644 --- a/packages/myst-execute/src/kernel.ts +++ b/packages/myst-execute/src/kernel.ts @@ -1,7 +1,7 @@ import type { KernelSpec } from 'myst-frontmatter'; import type { Kernel, KernelMessage, SessionManager } from '@jupyterlab/services'; import type { IOutput } from '@jupyterlab/nbformat'; -import type { IExpressionResult } from 'myst-common'; +import type { IExpressionResult } from 'myst-spec'; import type { Logger } from 'myst-cli-utils'; import type { VFile } from 'vfile'; import path from 'node:path'; @@ -76,7 +76,9 @@ function IOPubAsOutput(msg: KernelMessage.IIOPubMessage): IOutput { */ export async function executeCodeCell(kernel: Kernel.IKernelConnection, code: string) { const future = kernel.requestExecute({ - code: code, + code, + allow_stdin: false, + stop_on_error: false, }); const outputs: IOutput[] = []; @@ -153,6 +155,8 @@ export async function evaluateInlineExpression(kernel: Kernel.IKernelConnection, user_expressions: { expr: expr, }, + allow_stdin: false, + stop_on_error: false, }); let result: IExpressionResult | undefined; future.onReply = (msg: KernelMessage.IExecuteReplyMsg) => { diff --git a/packages/myst-execute/src/transform.ts b/packages/myst-execute/src/transform.ts index 9aac044d07..de5fcdb531 100644 --- a/packages/myst-execute/src/transform.ts +++ b/packages/myst-execute/src/transform.ts @@ -2,22 +2,24 @@ import { select } from 'unist-util-select'; import type { Logger } from 'myst-cli-utils'; import type { PageFrontmatter, KernelSpec } from 'myst-frontmatter'; import type { SessionManager } from '@jupyterlab/services'; -import type { Code } from 'myst-spec-ext'; +import type { Code } from 'myst-spec'; import type { GenericParent } from 'myst-common'; import { fileError } from 'myst-common'; import type { VFile } from 'vfile'; import assert from 'node:assert'; +import path from 'node:path'; import type { Plugin } from 'unified'; import { createHash } from 'node:crypto'; -import type { ICache } from './cache.js'; +import type { IDocumentExecutionCache } from './cache.js'; import { createKernelConnection } from './kernel.js'; import { isCodeBlock, isInlineExpression, codeBlockRaisesException } from './utils.js'; -import type { ExecutableNode, ExecutionResult } from './types.js'; +import type { ExecutableNode } from './types.js'; import { getExecutableNodes, computeExecutableNodes, applyComputedOutputsToNodes, } from './execute.js'; + /** * Build a cache key from an array of executable nodes * @@ -65,7 +67,7 @@ function buildCacheKey( export type Options = { basePath: string; - cache: ICache; + cache: IDocumentExecutionCache; sessionFactory: () => Promise; frontmatter: PageFrontmatter; ignoreCache?: boolean; @@ -107,7 +109,9 @@ export async function kernelExecutionTransform(tree: GenericParent, vfile: VFile // See if we already cached this execution const cacheEnv = getCacheEnvironment(executeConfig?.depends_on_env ?? []); const cacheKey = buildCacheKey(kernelspec, executableNodes, cacheEnv); - let cachedResults = opts.cache.get(cacheKey); + + const cacheHit = opts.cache.get(cacheKey); + let cachedResults = cacheHit?.results; const ignoreCachedDocument = // If we don't globally ignore caching' @@ -153,14 +157,26 @@ export async function kernelExecutionTransform(tree: GenericParent, vfile: VFile } const { kernel } = sessionConnection; log.debug(`Connected to kernel ${kernel.name}`); + + // Execution count + const startTimeNS = process.hrtime.bigint(); try { // Execute notebook const { results, errorOccurred } = await computeExecutableNodes(kernel, executableNodes, { vfile, }); + const stopTimeNS = process.hrtime.bigint(); // Populate cache if things were successful if (!errorOccurred) { - opts.cache.set(cacheKey, results); + opts.cache.set(cacheKey, { + context: { + kernelspec, + path: path.basename(opts.basePath, vfile.path), + timestamp: new Date().toISOString(), + duration_ms: Number((stopTimeNS - startTimeNS) / 1_000_000n), + }, + results, + }); } // Refer to these computed results cachedResults = results; diff --git a/packages/myst-execute/src/types.ts b/packages/myst-execute/src/types.ts index c1eca19af8..f133589214 100644 --- a/packages/myst-execute/src/types.ts +++ b/packages/myst-execute/src/types.ts @@ -1,28 +1,40 @@ -import type { Block, Code, Output, InlineExpression } from 'myst-spec-ext'; +import type { InlineExpression, CodeBlock, IExpressionResult } from 'myst-spec'; import type { IOutput } from '@jupyterlab/nbformat'; -import type { IExpressionResult } from 'myst-common'; + /** - * Type narrowing Output to contain IOutput data - * - * TODO: lift this to the myst-spec definition + * Kinds of nodes that we can "execute" */ -export type CodeBlockOutput = Output & { - data: IOutput[]; -}; +export type ExecutableNode = CodeBlock | InlineExpression; /** - * Type narrowing Block to contain code-cells and code-cell outputs - * - * TODO: lift this to the myst-spec definition + * Interface types for serialising execution results */ +export type CodeResult = { + type: 'code'; + responses: IOutput[]; +}; -export type CodeBlock = Block & { - kind: 'code'; - data?: { - tags?: string[]; - }; - children: (Code | CodeBlockOutput)[]; +export type ExpressionResult = { + type: 'inlineExpression'; + response: IExpressionResult; }; -export type ExecutableNode = CodeBlock | InlineExpression; -export type ExecutionResult = IOutput[] | IExpressionResult; +export type ExecutionResult = CodeResult | ExpressionResult; +export type LegacyExecutionResult = IOutput[] | IExpressionResult; + +export function isCodeResult(result: ExecutionResult): result is CodeResult { + return result.type === 'code'; +} +export function isExpressionResult(result: ExecutionResult): result is ExpressionResult { + return result.type === 'inlineExpression'; +} + +/** + * Result of document execution + */ +export type DocumentExecutionResult = { + // Additional execution metadata + context: Record; + // Array of results, one for each "executable" AST node + results: ExecutionResult[]; +}; diff --git a/packages/myst-execute/src/utils.ts b/packages/myst-execute/src/utils.ts index 8d46b49d96..536a0750bc 100644 --- a/packages/myst-execute/src/utils.ts +++ b/packages/myst-execute/src/utils.ts @@ -1,7 +1,7 @@ -import type { InlineExpression } from 'myst-spec-ext'; +import type { InlineExpression, CodeBlock } from 'myst-spec'; import type { GenericNode } from 'myst-common'; import { NotebookCell, NotebookCellTags } from 'myst-common'; -import type { CodeBlock } from './types.js'; + /** * Return true if the given node is a block over a code node and output node * diff --git a/packages/myst-spec/src/ext.ts b/packages/myst-spec/src/ext.ts index 498b85f23d..e846397d94 100644 --- a/packages/myst-spec/src/ext.ts +++ b/packages/myst-spec/src/ext.ts @@ -20,6 +20,7 @@ import type { CrossReference as SpecCrossReference, Link as SpecLink, } from './schema.d.ts'; +import type { IOutput, IMimeBundle } from '@jupyterlab/nbformat'; type Visibility = 'show' | 'hide' | 'remove'; @@ -179,6 +180,30 @@ export type SiUnit = { value: string; }; +export interface IExpressionDisplay { + status: 'ok'; + data: IMimeBundle; + metadata: Record; +} + +export interface IExpressionError { + status: 'error'; + /** + * Exception name + */ + ename: string; + /** + * Exception value + */ + evalue: string; + /** + * Traceback + */ + traceback: string[]; +} + +export type IExpressionResult = IExpressionError | IExpressionDisplay; + export type InlineExpression = { type: 'inlineExpression'; value: string; @@ -314,3 +339,15 @@ export type AnyWidget = { /** Tailwind classes to apply to the container element */ class?: string; }; + +/** + * Type narrowed Block to contain code-cells + */ + +export type CodeBlock = Block & { + kind: 'notebook-code'; + data?: { + tags?: string[]; + }; + children: Code[]; +}; diff --git a/packages/myst-spec/src/index.ts b/packages/myst-spec/src/index.ts index feb90f0e14..ef85999f9b 100644 --- a/packages/myst-spec/src/index.ts +++ b/packages/myst-spec/src/index.ts @@ -59,6 +59,7 @@ export type { CiteGroup, CiteKind, Code, + CodeBlock, Container, CrossReference, DefinitionDescription, @@ -74,6 +75,9 @@ export type { Image, Include, IndexEntry, + IExpressionResult, + IExpressionError, + IExpressionDisplay, InlineExpression, InlineMath, Link, diff --git a/packages/myst-to-html/package.json b/packages/myst-to-html/package.json index 9408600fe8..16347138ad 100644 --- a/packages/myst-to-html/package.json +++ b/packages/myst-to-html/package.json @@ -46,6 +46,7 @@ "mdast-util-find-and-replace": "^2.1.0", "mdast-util-to-hast": "^12.3.0", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "rehype-format": "^4.0.1", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", From aa08678c7767951667d1e93529f8403ed394d615 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 1 Jul 2026 14:57:55 -0700 Subject: [PATCH 06/24] fix: tolerate failure on unconfigured favicon (#2982) --- packages/myst-cli/src/build/html/index.ts | 57 ++++++++++++++--------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/packages/myst-cli/src/build/html/index.ts b/packages/myst-cli/src/build/html/index.ts index 648c452db7..5446853c20 100644 --- a/packages/myst-cli/src/build/html/index.ts +++ b/packages/myst-cli/src/build/html/index.ts @@ -29,9 +29,13 @@ export async function currentSiteRoutes( session: ISession, host: string, baseurl: string | undefined, -): Promise<{ url: string; path: string; binary?: boolean }[]> { +): Promise<{ url: string; path: string; binary?: boolean; optional?: boolean }[]> { const state = session.store.getState(); const siteConfig = selectors.selectCurrentSiteConfig(state); + // Without a configured favicon, the theme falls back to fetching a default + // icon from mystmd.org; that fetch is optional, so that if the remote site is + // down it doesn't break the build. + const hasFavicon = !!siteConfig?.options?.favicon; return (siteConfig?.projects ?? []) ?.map((proj) => { if (!proj.path) return []; @@ -70,11 +74,12 @@ export async function currentSiteRoutes( url: `${host}/${asset}`, path: asset, })), - ...['favicon.ico'].map((asset) => ({ - url: `${host}/${asset}`, - path: asset, + { + url: `${host}/favicon.ico`, + path: 'favicon.ico', binary: true, - })), + optional: !hasFavicon, + }, ]; }) .flat(); @@ -178,22 +183,32 @@ export async function buildHtml(session: ISession, opts: StartOptions) { await Promise.all( routes.map(async (route) => limitConnections(async () => { - const resp = await fetchWithRetry(session, route.url); - if (!resp.ok) { - session.log.error(`Error fetching ${route.url}`); - return; - } - if (route.binary && resp.body) { - await new Promise((resolve) => { - const filename = path.join(htmlDir, route.path); - if (!fs.existsSync(filename)) fs.mkdirSync(path.dirname(filename), { recursive: true }); - const fileWriteStream = fs.createWriteStream(filename); - resp.body!.pipe(fileWriteStream); - fileWriteStream.on('finish', resolve); - }); - } else { - const content = await resp.text(); - writeFileToFolder(path.join(htmlDir, route.path), content); + try { + const resp = await fetchWithRetry(session, route.url); + if (!resp.ok) { + session.log.error(`Error fetching ${route.url}`); + return; + } + if (route.binary && resp.body) { + await new Promise((resolve, reject) => { + const filename = path.join(htmlDir, route.path); + if (!fs.existsSync(filename)) + fs.mkdirSync(path.dirname(filename), { recursive: true }); + const fileWriteStream = fs.createWriteStream(filename); + resp.body!.pipe(fileWriteStream); + resp.body!.on('error', reject); + fileWriteStream.on('error', reject); + fileWriteStream.on('finish', resolve); + }); + } else { + const content = await resp.text(); + writeFileToFolder(path.join(htmlDir, route.path), content); + } + } catch (error) { + if (!route.optional) throw error; + session.log.warn( + `Could not fetch optional asset ${route.url}: ${(error as Error).message}`, + ); } }), ), From 403caed6c03aa431c67b53560eb55920252c3362 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Fri, 3 Jul 2026 06:04:04 -0700 Subject: [PATCH 07/24] Add proof:kind spelling for proof directives and ref role (#2983) --- .changeset/yellow-bikes-carry.md | 6 ++ docs/embed.md | 4 +- docs/proofs-and-theorems.md | 72 +++++++++++---------- packages/myst-ext-proof/src/proof.ts | 24 ++----- packages/myst-ext-proof/src/types.ts | 2 +- packages/myst-ext-proof/tests/proof.spec.ts | 20 ++++++ packages/myst-roles/src/reference.ts | 2 +- 7 files changed, 73 insertions(+), 57 deletions(-) create mode 100644 .changeset/yellow-bikes-carry.md diff --git a/.changeset/yellow-bikes-carry.md b/.changeset/yellow-bikes-carry.md new file mode 100644 index 0000000000..881365ffc3 --- /dev/null +++ b/.changeset/yellow-bikes-carry.md @@ -0,0 +1,6 @@ +--- +'myst-ext-proof': patch +'myst-roles': patch +--- + +Add proof:_ as alias for prf:_ diff --git a/docs/embed.md b/docs/embed.md index 1139090d4d..0006d46c51 100644 --- a/docs/embed.md +++ b/docs/embed.md @@ -137,7 +137,7 @@ You can then refer to content in these sites in two ways: When you build your MyST project, the content that your `#label` points to will be embedded in-place of the directive. -:::{prf:example} Embedding from the MyST Spec +:::{proof:example} Embedding from the MyST Spec The following content is embedded from `![](xref:spec#admonition)`: @@ -157,7 +157,7 @@ This is also helpful for including content snippets, such as a table or an equat See {myst:directive}`the {include} directive documentation ` for details about all the arguments you can give to `{include}`. -:::{prf:example} Equation Bank +:::{proof:example} Equation Bank :label: eg:equation-bank It is common practice to keep complex equations out of the main document so that they can be shared between slides, papers, and different renderings of a document. The `equations` folder in our documentation has a `curl.tex` file: diff --git a/docs/proofs-and-theorems.md b/docs/proofs-and-theorems.md index 008fd44f52..78c3962b92 100644 --- a/docs/proofs-and-theorems.md +++ b/docs/proofs-and-theorems.md @@ -5,7 +5,7 @@ description: MyST supports creating and referencing algorithms, axioms, conjectu thumbnail: ./thumbnails/proof.png --- -All proof directives can be included using the `prf:kind` pattern, where the proof directives are shown in [](#proof-list). The directive is enumerated by default and can take in an optional title argument which is shown in brackets after the proof. +All proof directives can be included using the `proof:kind` pattern, where the proof directives are shown in [](#proof-list). A plain proof can also be written without a kind, as `{proof}`. The directive is enumerated by default and can take in an optional title argument which is shown in brackets after the proof. :::{note} Same as Sphinx Proof 🎉 :class: dropdown @@ -14,12 +14,14 @@ The implementation and documentation for proofs, theorems, etc. is based on [Sph Changes to the original extension include being able to click on the proof label (e.g. "Theorem 1"), and having a link to that proof anchor. We have also updated the styles from both Sphinx and Jupyter Book to be more distinct from admonitions. -You can also reference proofs with any cross-reference syntax (including the {myst:role}`prf:ref` role). We recommend the Markdown link syntax. +A plain proof does not need a kind prefix at all — use `{proof}`. + +You can also reference proofs with any cross-reference syntax (including the {myst:role}`proof:ref` role). We recommend the Markdown link syntax. ::: -Here is an example of a `{prf:theorem}` with a custom title: +Here is an example of a `{proof:theorem}` with a custom title: -:::{prf:theorem} Orthogonal-Projection-Theorem +:::{proof:theorem} Orthogonal-Projection-Theorem :label: my-theorem Given $y \in \mathbb R^n$ and linear subspace $S \subset \mathbb R^n$, @@ -42,20 +44,20 @@ The vector $\hat y$ is called the **orthogonal projection** of $y$ onto $S$. :label: proof-list :header-rows: 0 -* - `prf:algorithm` - - `prf:axiom` - - `prf:conjecture` -* - `prf:corollary` - - `prf:criteria` - - `prf:definition` -* - `prf:example` - - `prf:lemma` - - `prf:observation` -* - `prf:property` - - `prf:proposition` - - `prf:proof` -* - `prf:remark` - - `prf:theorem` +* - `proof:algorithm` + - `proof:axiom` + - `proof:conjecture` +* - `proof:corollary` + - `proof:criteria` + - `proof:definition` +* - `proof:example` + - `proof:lemma` + - `proof:observation` +* - `proof:property` + - `proof:proposition` + - `proof` (plain) +* - `proof:remark` + - `proof:theorem` - ``` :::{myst:directive} proof @@ -73,7 +75,7 @@ You can refer to a proof using the standard link syntax: :::{tip} Compatibility with Sphinx Proof :class: dropdown -You may also use the `{prf:ref}` role like: `` {prf:ref}`my-theorem` ``, which will replace the reference with the theorem number like so: {prf:ref}`my-theorem`. When an explicit text is provided, this caption will serve as the title of the reference. For example, ``{prf:ref}`Orthogonal-Projection-Theorem ` `` will produce: {prf:ref}`Orthogonal-Projection-Theorem `. +You may also use the `{proof:ref}` role like: `` {proof:ref}`my-theorem` ``, which will replace the reference with the theorem number like so: {proof:ref}`my-theorem`. When an explicit text is provided, this caption will serve as the title of the reference. For example, ``{proof:ref}`Orthogonal-Projection-Theorem ` `` will produce: {proof:ref}`Orthogonal-Projection-Theorem `. ::: ## Hiding Proof Content @@ -83,7 +85,7 @@ To hide the directive, simply add `:class: dropdown` as a directive option. **Example** ```{myst} -:::{prf:theorem} +:::{proof:theorem} :class: dropdown This is an example of how to hide the content of a directive. @@ -95,7 +97,7 @@ This is an example of how to hide the content of a directive. ### Proofs ````{myst} -:::{prf:proof} +:::{proof} :label: full-proof Let $z$ be any other point in $S$ and use the fact that $S$ is a linear subspace to deduce @@ -114,7 +116,7 @@ _Source:_ Adapted from [QuantEcon](https://python-advanced.quantecon.org/orth_pr ### Theorems ````{myst} -:::{prf:theorem} Orthogonal-Projection-Theorem +:::{proof:theorem} Orthogonal-Projection-Theorem :label: my-theorem Given $y \in \mathbb R^n$ and linear subspace $S \subset \mathbb R^n$, @@ -140,7 +142,7 @@ _Source:_ [QuantEcon](https://python-advanced.quantecon.org/orth_proj.html#The-O ### Axioms ```{myst} -:::{prf:axiom} Completeness of $\mathbb{R}$ +:::{proof:axiom} Completeness of $\mathbb{R}$ :label: my-axiom Every Cauchy sequence on the real line is convergent. @@ -152,7 +154,7 @@ _Source:_ {cite}`economic-dynamics-book` ### Lemmas ````{myst} -:::{prf:lemma} +:::{proof:lemma} :label: my-lemma If $\hat P$ is the fixed point of the map $\mathcal B \circ \mathcal D$ and $\hat F$ is the robust policy as given in [(7)](https://python-advanced.quantecon.org/robustness.html#equation-rb-oc-ih), then @@ -170,7 +172,7 @@ _Source:_ [QuantEcon](https://python-advanced.quantecon.org/robustness.html#Appe ### Definitions ```{myst} -:::{prf:definition} +:::{proof:definition} :label: my-definition The *economical expansion problem* (EEP) for @@ -191,7 +193,7 @@ _Source:_ [QuantEcon](https://python-advanced.quantecon.org) ### Criteria ````{myst} -:::{prf:criterion} Weyl's criterion +:::{proof:criterion} Weyl's criterion :label: weyls-criterion Weyl's criterion states that the sequence $a_n$ is equidistributed modulo $1$ if @@ -208,7 +210,7 @@ _Source:_ [Wikipedia](https://en.wikipedia.org/wiki/Equidistributed_sequence#Wey ### Remarks ```{myst} -:::{prf:remark} +:::{proof:remark} :label: my-remark More generally there is a class of density functions @@ -229,7 +231,7 @@ _Source:_ [QuantEcon](https://python-advanced.quantecon.org/black_litterman.html ### Conjectures ```{myst} -:::{prf:conjecture} Fake $\gamma$ conjecture +:::{proof:conjecture} Fake $\gamma$ conjecture :label: my-conjecture This is a dummy conjecture to illustrate that one can use math in titles. ::: @@ -238,7 +240,7 @@ This is a dummy conjecture to illustrate that one can use math in titles. ### Corollaries ```{myst} -:::{prf:corollary} +:::{proof:corollary} :label: my-corollary If $A$ is a convergent matrix, then there exists a matrix norm such @@ -251,7 +253,7 @@ _Source:_ [QuantEcon](https://python-intro.quantecon.org/_static/lecture_specifi ### Algorithms ```{myst} -:::{prf:algorithm} Ford–Fulkerson +:::{proof:algorithm} Ford–Fulkerson :label: my-algorithm **Inputs** Given a Network $G=(V,E)$ with flow capacity $c$, a source node $s$, and a sink node $t$ @@ -274,7 +276,7 @@ _Source:_ [Wikipedia](https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algor ### Examples ````{myst} -:::{prf:example} +:::{proof:example} :label: my-example Next, we shut down randomness in demand and assume that the demand shock @@ -301,7 +303,7 @@ _Source:_ [QuantEcon](https://python.quantecon.org/lq_inventories.html#Example-2 ### Properties ```{myst} -:::{prf:property} +:::{proof:property} :label: my-property This is a dummy property to illustrate the directive. @@ -311,7 +313,7 @@ This is a dummy property to illustrate the directive. ### Observations ```{myst} -:::{prf:observation} +:::{proof:observation} :label: my-observation This is a dummy observation directive. @@ -321,7 +323,7 @@ This is a dummy observation directive. ### Propositions ```{myst} -:::{prf:proposition} +:::{proof:proposition} :label: my-proposition This is a dummy proposition directive. @@ -331,7 +333,7 @@ This is a dummy proposition directive. ### Assumptions ```{myst} -:::{prf:assumption} +:::{proof:assumption} :label: my-assumption This is a dummy assumption directive. diff --git a/packages/myst-ext-proof/src/proof.ts b/packages/myst-ext-proof/src/proof.ts index 7458acb1a1..e2a74f9967 100644 --- a/packages/myst-ext-proof/src/proof.ts +++ b/packages/myst-ext-proof/src/proof.ts @@ -1,25 +1,13 @@ import type { DirectiveSpec, DirectiveData, GenericNode } from 'myst-common'; import { addCommonDirectiveOptions, commonDirectiveOptions } from 'myst-directives'; +import { PROOF_KINDS } from './types.js'; + +// e.g. 'prf:theorem' (legacy, kept for backward compatibility) and 'proof:theorem' (preferred) +const KIND_ALIASES = PROOF_KINDS.flatMap((kind) => [`prf:${kind}`, `proof:${kind}`]); export const proofDirective: DirectiveSpec = { name: 'proof', - alias: [ - 'prf:proof', - 'prf:theorem', - 'prf:axiom', - 'prf:lemma', - 'prf:definition', - 'prf:criterion', - 'prf:remark', - 'prf:conjecture', - 'prf:corollary', - 'prf:algorithm', - 'prf:example', - 'prf:property', - 'prf:observation', - 'prf:proposition', - 'prf:assumption', - ], + alias: KIND_ALIASES, arg: { type: 'myst', }, @@ -55,7 +43,7 @@ export const proofDirective: DirectiveSpec = { } const proof = { type: 'proof', - kind: data.name !== 'proof' ? data.name.replace('prf:', '') : undefined, + kind: data.name !== 'proof' ? data.name.replace(/^(prf|proof):/, '') : undefined, enumerated, children: children as any[], }; diff --git a/packages/myst-ext-proof/src/types.ts b/packages/myst-ext-proof/src/types.ts index e1f41af0b1..0e959e5b39 100644 --- a/packages/myst-ext-proof/src/types.ts +++ b/packages/myst-ext-proof/src/types.ts @@ -1,6 +1,6 @@ import type { Container } from 'myst-spec'; -const PROOF_KINDS = [ +export const PROOF_KINDS = [ 'proof', 'axiom', 'lemma', diff --git a/packages/myst-ext-proof/tests/proof.spec.ts b/packages/myst-ext-proof/tests/proof.spec.ts index 4f0fd0d2c2..2a34933d7c 100644 --- a/packages/myst-ext-proof/tests/proof.spec.ts +++ b/packages/myst-ext-proof/tests/proof.spec.ts @@ -88,4 +88,24 @@ describe('proof directive', () => { }); expect(output).toEqual(expected); }); + + it('parses proof directive without prf: prefix', async () => { + const content = '```{proof} Proof Title\nProof content\n```'; + const output = mystParse(content, { + directives: [proofDirective], + }); + const proof = (output as any).children[0].children[0]; + expect(proof.type).toEqual('proof'); + expect(proof.kind).toEqual(undefined); + }); + + it('parses proof:theorem the same as prf:theorem', async () => { + const content = '```{proof:theorem} Theorem Title\nTheorem content\n```'; + const output = mystParse(content, { + directives: [proofDirective], + }); + const theorem = (output as any).children[0].children[0]; + expect(theorem.type).toEqual('proof'); + expect(theorem.kind).toEqual('theorem'); + }); }); diff --git a/packages/myst-roles/src/reference.ts b/packages/myst-roles/src/reference.ts index 6cd83f36d3..7fae85a1cf 100644 --- a/packages/myst-roles/src/reference.ts +++ b/packages/myst-roles/src/reference.ts @@ -5,7 +5,7 @@ const REF_PATTERN = /^(.+?)<([^<>]+)>$/; // e.g. 'Labeled Reference ' export const refRole: RoleSpec = { name: 'ref', - alias: ['eq', 'numref', 'prf:ref'], + alias: ['eq', 'numref', 'prf:ref', 'proof:ref'], body: { type: String, required: true, From bde9e9c02d91e49c222b4adabf655981a225b86b Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Sat, 4 Jul 2026 10:31:58 -0700 Subject: [PATCH 08/24] Fix IReplyAbortContent type (#2981) * fix: add 'aborted' to execute reply status type @jupyterlab/services@7.6 widened IReplyAbortContent.status to 'abort' | 'aborted'; the local variable type needs to match. * Bump @jupyterlite/services * Read reply type from KernelMessage.IExecuteReplyMsg Better than specifying by hand, like we did before, which can get out of sync. --- .changeset/true-buses-fail.md | 6 +++ bun.lock | 75 +++++++++++++++-------------- packages/myst-cli/package.json | 2 +- packages/myst-execute/package.json | 2 +- packages/myst-execute/src/kernel.ts | 2 +- 5 files changed, 47 insertions(+), 40 deletions(-) create mode 100644 .changeset/true-buses-fail.md diff --git a/.changeset/true-buses-fail.md b/.changeset/true-buses-fail.md new file mode 100644 index 0000000000..53cbb03ee3 --- /dev/null +++ b/.changeset/true-buses-fail.md @@ -0,0 +1,6 @@ +--- +'myst-execute': patch +'myst-cli': patch +--- + +Bump @jupyterlite/services and fix types diff --git a/bun.lock b/bun.lock index 0105527721..206d0abb7c 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "mystmd", @@ -99,7 +100,7 @@ "name": "myst-cli", "version": "1.10.1", "dependencies": { - "@jupyterlab/services": "^7.3.0", + "@jupyterlab/services": "^7.6.0", "@reduxjs/toolkit": "^2.1.0", "adm-zip": "^0.5.10", "async-mutex": "^0.5.0", @@ -126,31 +127,31 @@ "mdast": "^3.0.0", "meca": "^1.0.8", "mime-types": "^2.1.35", - "myst-cli-utils": "^2.0.13", - "myst-common": "^1.9.5", - "myst-config": "^1.9.5", - "myst-execute": "^0.3.3", + "myst-cli-utils": "^2.0.14", + "myst-common": "^1.10.0", + "myst-config": "^1.10.0", + "myst-execute": "^0.4.0", "myst-ext-button": "^0.0.1", "myst-ext-card": "^1.0.9", "myst-ext-exercise": "^1.0.9", - "myst-ext-grid": "^1.0.9", + "myst-ext-grid": "^1.1.0", "myst-ext-icon": "^0.0.2", "myst-ext-proof": "^1.0.12", "myst-ext-reactive": "^1.0.9", "myst-ext-tabs": "^1.0.9", - "myst-frontmatter": "^1.9.5", - "myst-migrate": "^1.8.2", - "myst-parser": "^1.7.0", + "myst-frontmatter": "^1.10.0", + "myst-migrate": "^1.10.1", + "myst-parser": "^1.7.3", "myst-spec": "^0.0.5", - "myst-spec-ext": "^1.9.5", + "myst-spec-ext": "^1.10.0", "myst-templates": "^1.0.27", "myst-to-docx": "^1.0.16", "myst-to-jats": "^1.0.35", "myst-to-md": "^1.0.16", - "myst-to-tex": "^1.0.45", - "myst-to-typst": "^0.0.36", + "myst-to-tex": "^1.0.46", + "myst-to-typst": "^0.0.38", "myst-toc": "^0.1.4", - "myst-transforms": "^1.3.47", + "myst-transforms": "^1.3.50", "nanoid": "^5.1.6", "nbtx": "^0.4.0", "node-fetch": "^3.3.1", @@ -159,7 +160,7 @@ "redux": "^5.0.1", "simple-validators": "^1.2.0", "strip-ansi": "^7.0.1", - "tex-to-myst": "^1.0.45", + "tex-to-myst": "^1.0.46", "unified": "^10.1.2", "unist-util-filter": "^4.0.0", "unist-util-remove": "^3.1.0", @@ -226,8 +227,8 @@ "name": "myst-config", "version": "1.10.0", "dependencies": { - "myst-common": "^1.9.5", - "myst-frontmatter": "^1.9.5", + "myst-common": "^1.10.0", + "myst-frontmatter": "^1.10.0", "simple-validators": "^1.2.0", }, "devDependencies": { @@ -254,7 +255,7 @@ "version": "0.4.0", "dependencies": { "@jupyterlab/nbformat": "^3.5.2", - "@jupyterlab/services": "^7.3.0", + "@jupyterlab/services": "^7.6.0", "chalk": "^5.2.0", "get-port": "^6.1.2", "myst-cli-utils": "^2.0.13", @@ -307,7 +308,7 @@ "myst-common": "^1.7.2", }, "devDependencies": { - "myst-parser": "^1.5.9", + "myst-parser": "^1.7.1", }, }, "packages/myst-ext-icon": { @@ -565,7 +566,7 @@ "vfile-reporter": "^7.0.4", }, "devDependencies": { - "myst-parser": "^1.6.4", + "myst-parser": "^1.7.1", }, }, "packages/myst-to-typst": { @@ -796,37 +797,37 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - "@jupyter/ydoc": ["@jupyter/ydoc@3.1.0", "", { "dependencies": { "@jupyterlab/nbformat": "^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0", "@lumino/coreutils": "^1.11.0 || ^2.0.0", "@lumino/disposable": "^1.10.0 || ^2.0.0", "@lumino/signaling": "^1.10.0 || ^2.0.0", "y-protocols": "^1.0.5", "yjs": "^13.5.40" } }, "sha512-+hLNUBZr8Zz8NiuaoYKINDURDJHbjFGxg8EcSU52y+rBe412TsoFxfPSng4eP7w3cZFrVlm7D+8K0nAMHxj0ZQ=="], + "@jupyter/ydoc": ["@jupyter/ydoc@4.1.0", "", { "dependencies": { "@jupyterlab/nbformat": "^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0", "@lumino/coreutils": "^1.11.0 || ^2.0.0", "@lumino/disposable": "^1.10.0 || ^2.0.0", "@lumino/signaling": "^1.10.0 || ^2.0.0", "y-protocols": "^1.0.5", "yjs": "^13.5.40" } }, "sha512-XOJbuSKCWpyd87ekpppGTm7iGiEZPONxLiiM6jOBZ2y8uvDhEdb6ltn0Rt8GR2ZEvcLC+MgSE+IGTnguKEhzNA=="], - "@jupyterlab/coreutils": ["@jupyterlab/coreutils@6.4.8", "", { "dependencies": { "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4", "minimist": "~1.2.0", "path-browserify": "^1.0.0", "url-parse": "~1.5.4" } }, "sha512-y7NGkh31fJaRMDGPfi6uwUeP3eCJrwNSVqmEo0bN9qxiJsysRGOBKPAFuFUS3IKD/au79EwyKHgxhBZxLwiqBQ=="], + "@jupyterlab/coreutils": ["@jupyterlab/coreutils@6.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/signaling": "^2.1.5", "minimist": "~1.2.0", "path-browserify": "^1.0.0", "url-parse": "~1.5.4" } }, "sha512-1DjMEoiHi3CwycFVM0RWfn53+0zGhJ064SKqVeYKxxFFJdN3u7ezClduRiIGEQXAiums8w+9QLlljBx/M+kaUw=="], "@jupyterlab/nbformat": ["@jupyterlab/nbformat@3.5.2", "", { "dependencies": { "@lumino/coreutils": "^1.11.0" } }, "sha512-Ml5hNpS9tMqZ9ThI24+iXHgX71XWQAysyPOU1vA3idvTGCbGhVc4FaZcDX17uepA7yIEUitlj4xQGtJR8hNzuA=="], - "@jupyterlab/services": ["@jupyterlab/services@7.4.8", "", { "dependencies": { "@jupyter/ydoc": "^3.1.0", "@jupyterlab/coreutils": "^6.4.8", "@jupyterlab/nbformat": "^4.4.8", "@jupyterlab/settingregistry": "^4.4.8", "@jupyterlab/statedb": "^4.4.8", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/polling": "^2.1.4", "@lumino/properties": "^2.0.3", "@lumino/signaling": "^2.1.4", "ws": "^8.11.0" } }, "sha512-ftv5+It2nGu+i0lWFgTIqMR9KV4YfG+Mg37lscndDGOfJ5YM3sxl61pU2Zv7YrdYLyMwA9xzXpcMHQdzCoIuZw=="], + "@jupyterlab/services": ["@jupyterlab/services@7.6.1", "", { "dependencies": { "@jupyter/ydoc": "^4.0.0", "@jupyterlab/coreutils": "^6.6.1", "@jupyterlab/nbformat": "^4.6.1", "@jupyterlab/settingregistry": "^4.6.1", "@jupyterlab/statedb": "^4.6.1", "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/polling": "^2.1.5", "@lumino/properties": "^2.0.4", "@lumino/signaling": "^2.1.5", "ws": "^8.11.0" } }, "sha512-pZ3B+m6bPZs3oUO0hgNd61hI9LlDKN3q931XWe4gWr8S/BwY9RHxoIe/mDgP38G+dS5P1ONKpaFXGICJkvUoQg=="], - "@jupyterlab/settingregistry": ["@jupyterlab/settingregistry@4.4.8", "", { "dependencies": { "@jupyterlab/nbformat": "^4.4.8", "@jupyterlab/statedb": "^4.4.8", "@lumino/commands": "^2.3.2", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4", "@rjsf/utils": "^5.13.4", "ajv": "^8.12.0", "json5": "^2.2.3" }, "peerDependencies": { "react": ">=16" } }, "sha512-V7fP4nmstjzGttCMP0ISJB9Gy1S76Z1AYmyXtEgsSFzOeF51hjWAUJpLUiRqMebSmy5aVDj1CdjWIYAxx/sGXw=="], + "@jupyterlab/settingregistry": ["@jupyterlab/settingregistry@4.6.1", "", { "dependencies": { "@jupyterlab/nbformat": "^4.6.1", "@jupyterlab/statedb": "^4.6.1", "@lumino/commands": "^2.3.3", "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/signaling": "^2.1.5", "@rjsf/utils": "^5.13.4", "ajv": "^8.12.0", "json5": "^2.2.3" }, "peerDependencies": { "react": ">=16" } }, "sha512-iQlzSchm7TsnXZUKSXJApGHO3KB1WQE3svenGkPBuqP+kO6DiXY7w5v4c+SCRmAXSrvSZ03QfGLWzCeAi8DsRQ=="], - "@jupyterlab/statedb": ["@jupyterlab/statedb@4.4.8", "", { "dependencies": { "@lumino/commands": "^2.3.2", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/properties": "^2.0.3", "@lumino/signaling": "^2.1.4" } }, "sha512-VbFEKSDb6Rz8BLToYg4Fz33FNLLp2EYXulA6fcBmELJVyQNB0ZtdTnHKtzpRr0ihmdVLi7vv4PCChHdm49lSaQ=="], + "@jupyterlab/statedb": ["@jupyterlab/statedb@4.6.1", "", { "dependencies": { "@lumino/commands": "^2.3.3", "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/properties": "^2.0.4", "@lumino/signaling": "^2.1.5" } }, "sha512-rF+3oTMCCyiw4OV0caIB0E/lUHncxR73S2uxFEzMxTnIPyA1usO3X5Ln92XqrLLj0acUqexZELCY2PYZSX0wZA=="], - "@lumino/algorithm": ["@lumino/algorithm@2.0.3", "", {}, "sha512-DIcF7cIrGEC1Wh8DNjdwaL7IdcNs4Jj1VjO/90oHefeQPszKgc6DSfCxvbQiRanKR6tl/JL7tq4ZRPZES2oVAA=="], + "@lumino/algorithm": ["@lumino/algorithm@2.0.4", "", {}, "sha512-gddBhESPqu25KWLeAK9Kz8tS9Ph7P45i0CNG7Ia4XMhK9PHLtTsBdJTC9jP+MqhbzC8zDT/4ekvYRV9ojRPj7Q=="], - "@lumino/commands": ["@lumino/commands@2.3.2", "", { "dependencies": { "@lumino/algorithm": "^2.0.3", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/domutils": "^2.0.3", "@lumino/keyboard": "^2.0.3", "@lumino/signaling": "^2.1.4", "@lumino/virtualdom": "^2.0.3" } }, "sha512-aAFEiUpp2hrkQU82Z85w1L80g0iDzsQRncLBa+pqVR/k0k1lz6H9F6xZ1ff+lBumZKKtsxBxNomvd0hfxLLqGw=="], + "@lumino/commands": ["@lumino/commands@2.3.3", "", { "dependencies": { "@lumino/algorithm": "^2.0.4", "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/domutils": "^2.0.4", "@lumino/keyboard": "^2.0.4", "@lumino/signaling": "^2.1.5", "@lumino/virtualdom": "^2.0.4" } }, "sha512-7Ci0QdFzt4NKFMhULr19sJPpOLHJw/oYlq6Pb0/Kq1s05+cIoLimr5wiyjkbAlNoGO/8A8SEBGHy3uctZz6G3A=="], - "@lumino/coreutils": ["@lumino/coreutils@2.2.1", "", { "dependencies": { "@lumino/algorithm": "^2.0.3" } }, "sha512-yij4TnxDIum7xfFUsVvZB0oLv4shs2mNbn3juwtEIsruvVBPmurNzKX0Y8z2QetbP2AZ6MSFtBzEKsihf0H0VA=="], + "@lumino/coreutils": ["@lumino/coreutils@2.2.2", "", { "dependencies": { "@lumino/algorithm": "^2.0.4" } }, "sha512-zaKJaK7rawPATn2BGHkbMrR6oK3s9PxNe9KreLwWF2dB4ZBHDiEmNLRyHRorfJ7XqVOEXAsAAj0jFn+qJPC/4Q=="], - "@lumino/disposable": ["@lumino/disposable@2.1.4", "", { "dependencies": { "@lumino/signaling": "^2.1.4" } }, "sha512-qTJiDbglPE2QnG4x4gtBcRbcfKQibxyyinNGKcNDrcK2TGTbbhK5PpMQ8d70l2V2Xw2pb/LfksBAg5pxkJ/G4A=="], + "@lumino/disposable": ["@lumino/disposable@2.1.5", "", { "dependencies": { "@lumino/signaling": "^2.1.5" } }, "sha512-hO9AkJK0oEGzxopuxI8LaZqwzSNwXJTGCdr5K4gh6al+zxpN7rOCh6Aq3zDxkIHJU4zybxv8r02ardx9XJsG3A=="], - "@lumino/domutils": ["@lumino/domutils@2.0.3", "", {}, "sha512-bXAbZg3mf2ZDNdBBpCGDike3U+osRGHePTh8H2Ud2KwaN4g/5IryFJm/TiO4K5IYs91bWF2Zqhf3FsdbZKHlGw=="], + "@lumino/domutils": ["@lumino/domutils@2.0.4", "", {}, "sha512-naYGUQn3e0CLtz/tjKOZP8SOBg0SW7EguhkxLpNUXlVUvx7rVsfr0VI22FVL+jgI0FbxXpEkxpSMxtK73jxJAg=="], - "@lumino/keyboard": ["@lumino/keyboard@2.0.3", "", {}, "sha512-bU2OxAR8a9eNBdV0YFjU6/lVVpbOw1gM7yHOuDGDdNu4J0UpKapFoR9gopNGSaHTmTwDtx9RHdFfIAgHwjZ+VQ=="], + "@lumino/keyboard": ["@lumino/keyboard@2.0.4", "", {}, "sha512-kIVkdSz8F5wtZr8hZp0CMX+E0eMCOnFH6XCT7j2UBQ80ERJHFy0eX+IbNo3dtRQ7+CcDhBV4hQquFNFa+/04QQ=="], - "@lumino/polling": ["@lumino/polling@2.1.4", "", { "dependencies": { "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4" } }, "sha512-gSkxlIJ/4/esY2G7bsRrY9A4KimDMHTo0shaD+MCbhd09fZMCWJoDMcA447/dykB1rM5NXgugNLjpdGGL/e8cw=="], + "@lumino/polling": ["@lumino/polling@2.1.5", "", { "dependencies": { "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/signaling": "^2.1.5" } }, "sha512-YhQRWTNRVSi5R5uatwh1jkxASY5JKyAGWmtnfQOZWLDUFmsIjOTsS8NaYg1BgneZjWM3fbA18dCDDT7PPs5X1g=="], - "@lumino/properties": ["@lumino/properties@2.0.3", "", {}, "sha512-zkXIU5uYz/ScHCHGl5Bt4gMYsfPxZEduZd80zqDslBWvTIMro3NnzLe66NMnecbdr5N3hDJagYyA8//Qy3XjiA=="], + "@lumino/properties": ["@lumino/properties@2.0.4", "", {}, "sha512-XsL2qLZk+1FbfuTrkyjciI8PMDw3YcaBkqVQ+iv7OOJf9bUlrmTpCMY0Hu5d3hV2W3TWlRsdbvRRLEBJSKv0iA=="], - "@lumino/signaling": ["@lumino/signaling@2.1.4", "", { "dependencies": { "@lumino/algorithm": "^2.0.3", "@lumino/coreutils": "^2.2.1" } }, "sha512-nC5Z6d9om369Jkh1Vp3b7C89hV4cjr1fQDVcxhemyKXwc9r6VW7FpKixC+jElcAknP5KLj1FAa8Np+K06mMkEA=="], + "@lumino/signaling": ["@lumino/signaling@2.1.5", "", { "dependencies": { "@lumino/algorithm": "^2.0.4", "@lumino/coreutils": "^2.2.2" } }, "sha512-Wkx6WR45ynmKBlW0GBEoh4xk9+QluKr1JHuMftqcStBHSQBCnN54UKRRDbySXHGRhhx6p4neu7sGomgQSlQK8w=="], - "@lumino/virtualdom": ["@lumino/virtualdom@2.0.3", "", { "dependencies": { "@lumino/algorithm": "^2.0.3" } }, "sha512-q2C8eBxPvvOOQjN3KuxZ+vJi082JH/GF7KwMdaWsy5g+7wjKdnXPuLQFTBLOrVqIzmbxBDlLeFr93CEhdQXcyQ=="], + "@lumino/virtualdom": ["@lumino/virtualdom@2.0.4", "", { "dependencies": { "@lumino/algorithm": "^2.0.4" } }, "sha512-7MFthA9KUsqZTGm/D98FZt1QupjIGyd3XyB4SIugn6DQAqhjBiyykCZydnRq3qmuMHybQel33dNIbHpzyNyQwA=="], "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], @@ -2664,13 +2665,13 @@ "@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "@jupyter/ydoc/@lumino/coreutils": ["@lumino/coreutils@1.12.1", "", { "peerDependencies": { "crypto": "1.0.1" } }, "sha512-JLu3nTHzJk9N8ohZ85u75YxemMrmDzJdNgZztfP7F7T7mxND3YVNCkJG35a6aJ7edu1sIgCjBxOvV+hv27iYvQ=="], + "@jupyter/ydoc/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2" } }, "sha512-+dB/edJjn7JDFiyN4nGw+quxBwUFGktgbR8udQeJweDne5mPMArpwCez/HBFkIW/816UX6+gSji01wTP4y10Lw=="], "@jupyterlab/nbformat/@lumino/coreutils": ["@lumino/coreutils@1.12.1", "", { "peerDependencies": { "crypto": "1.0.1" } }, "sha512-JLu3nTHzJk9N8ohZ85u75YxemMrmDzJdNgZztfP7F7T7mxND3YVNCkJG35a6aJ7edu1sIgCjBxOvV+hv27iYvQ=="], - "@jupyterlab/services/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.4.8", "", { "dependencies": { "@lumino/coreutils": "^2.2.1" } }, "sha512-zhpmu4jkIGgjLRzClifnVmM1NKfrC+A7Gcz8hnXJBinJwf6nMZaQnnXo+J6G93e0epw0E/YBrJ1kI/JJWLMeCg=="], + "@jupyterlab/services/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2" } }, "sha512-+dB/edJjn7JDFiyN4nGw+quxBwUFGktgbR8udQeJweDne5mPMArpwCez/HBFkIW/816UX6+gSji01wTP4y10Lw=="], - "@jupyterlab/settingregistry/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.4.8", "", { "dependencies": { "@lumino/coreutils": "^2.2.1" } }, "sha512-zhpmu4jkIGgjLRzClifnVmM1NKfrC+A7Gcz8hnXJBinJwf6nMZaQnnXo+J6G93e0epw0E/YBrJ1kI/JJWLMeCg=="], + "@jupyterlab/settingregistry/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2" } }, "sha512-+dB/edJjn7JDFiyN4nGw+quxBwUFGktgbR8udQeJweDne5mPMArpwCez/HBFkIW/816UX6+gSji01wTP4y10Lw=="], "@jupyterlab/settingregistry/json5": ["json5@2.2.3", "", { "bin": "lib/cli.js" }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], diff --git a/packages/myst-cli/package.json b/packages/myst-cli/package.json index 370529c005..cb63fd0ae9 100644 --- a/packages/myst-cli/package.json +++ b/packages/myst-cli/package.json @@ -42,7 +42,7 @@ "npm": ">=10" }, "dependencies": { - "@jupyterlab/services": "^7.3.0", + "@jupyterlab/services": "^7.6.0", "@reduxjs/toolkit": "^2.1.0", "adm-zip": "^0.5.10", "async-mutex": "^0.5.0", diff --git a/packages/myst-execute/package.json b/packages/myst-execute/package.json index 2417a618e7..abc0075e86 100644 --- a/packages/myst-execute/package.json +++ b/packages/myst-execute/package.json @@ -32,7 +32,7 @@ "url": "https://github.com/jupyter-book/mystmd/issues" }, "dependencies": { - "@jupyterlab/services": "^7.3.0", + "@jupyterlab/services": "^7.6.0", "@jupyterlab/nbformat": "^3.5.2", "chalk": "^5.2.0", "get-port": "^6.1.2", diff --git a/packages/myst-execute/src/kernel.ts b/packages/myst-execute/src/kernel.ts index 4e6951778d..8460404ba9 100644 --- a/packages/myst-execute/src/kernel.ts +++ b/packages/myst-execute/src/kernel.ts @@ -133,7 +133,7 @@ export async function executeCodeCell(kernel: Kernel.IKernelConnection, code: st } } }; - let status: 'abort' | 'error' | 'ok' | undefined; + let status: KernelMessage.IExecuteReplyMsg['content']['status'] | undefined; future.onReply = (msg: KernelMessage.IExecuteReplyMsg) => { status = msg.content.status; }; From efabece65785630304a90265383202cf4314f59b Mon Sep 17 00:00:00 2001 From: Maikel Mardjan Date: Mon, 6 Jul 2026 19:57:48 +0200 Subject: [PATCH 09/24] Update Node.js version to 24.x in GitHub Pages setup (#2990) node-version: 18.x is depreciated on GitHub --- docs/deployment-github-pages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deployment-github-pages.md b/docs/deployment-github-pages.md index 56de922479..c8e54bf6ef 100644 --- a/docs/deployment-github-pages.md +++ b/docs/deployment-github-pages.md @@ -77,7 +77,7 @@ jobs: uses: actions/configure-pages@v3 - uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 24.x - name: Install MyST Markdown run: npm install -g mystmd - name: Build HTML Assets From 95aa76d1ffa8670a2d40b935894d83131b7dcd48 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 8 Jul 2026 09:58:34 -0700 Subject: [PATCH 10/24] Switch bun to isolated linker mode (#2987) * Switch bun to isolated linker mode Sets linker = "isolated" in bunfig.toml so each package's node_modules only contains what it actually declares, instead of a hoisted flat tree. This surfaced ~30 phantom dependencies across the workspace (types/packages used but not declared) which are now fixed. Temporarily works around a bug in meca/jats-utils/jats-xml (misdeclared as devDependencies upstream, PR pending at https://github.com/continuous-foundation/jats/pull/109) The workarounds: - bumped jats-xml to the already-fixed 1.1.7 - added esbuild --alias flags so mystmd's bundled CLI resolves myst-cli-utils/myst-common correctly regardless - disabled (renamed .skip) three test files that load meca/jats-utils directly and have no bundler to alias around; jats-to-myst's test script now uses `--pass-with-no-tests` to make it green * Declare eslint and its plugins as direct root devDependencies Under the isolated linker, eslint (and the typescript-eslint/plugin packages eslint-config-curvenote needs) were only reachable transitively through eslint-config-curvenote's own dependencies. * Also temporarily disable mystmd's clirun.spec.ts (blocked on jats-utils upstream bug) * Drop meca/jats-utils/jats-xml workarounds now that 1.1.8 is released --- .changeset/tangy-cameras-cross.md | 35 +++ bun.lock | 252 ++++++++++++---------- bunfig.toml | 2 + package.json | 8 + packages/citation-js-utils/package.json | 1 + packages/jats-to-myst/package.json | 2 +- packages/jtex/package.json | 1 + packages/markdown-it-myst/package.json | 1 + packages/myst-cli-utils/package.json | 1 + packages/myst-cli/package.json | 6 +- packages/myst-common/package.json | 3 +- packages/myst-config/package.json | 2 + packages/myst-directives/package.json | 5 + packages/myst-execute/package.json | 3 + packages/myst-ext-button/package.json | 3 +- packages/myst-ext-exercise/package.json | 6 +- packages/myst-ext-grid/package.json | 3 +- packages/myst-ext-icon/package.json | 3 +- packages/myst-ext-proof/package.json | 3 +- packages/myst-ext-tabs/package.json | 6 +- packages/myst-frontmatter/package.json | 2 + packages/myst-migrate/package.json | 3 +- packages/myst-parser/package.json | 1 + packages/myst-parser/tests/myst.spec.ts | 7 +- packages/myst-roles/package.json | 3 + packages/myst-spec/package.json | 1 + packages/myst-templates/package.json | 2 + packages/myst-to-docx/package.json | 7 +- packages/myst-to-html/package.json | 5 +- packages/myst-to-jats/package.json | 7 +- packages/myst-to-jats/tests/basic.spec.ts | 1 - packages/myst-to-md/package.json | 5 + packages/myst-to-tex/package.json | 7 +- packages/myst-to-typst/package.json | 7 + packages/myst-transforms/package.json | 5 +- packages/mystmd/package.json | 6 +- packages/tex-to-myst/package.json | 4 + 37 files changed, 285 insertions(+), 134 deletions(-) create mode 100644 .changeset/tangy-cameras-cross.md diff --git a/.changeset/tangy-cameras-cross.md b/.changeset/tangy-cameras-cross.md new file mode 100644 index 0000000000..831cccbc13 --- /dev/null +++ b/.changeset/tangy-cameras-cross.md @@ -0,0 +1,35 @@ +--- +'citation-js-utils': patch +'myst-ext-exercise': patch +'markdown-it-myst': patch +'myst-frontmatter': patch +'myst-directives': patch +'myst-ext-button': patch +'myst-transforms': patch +'myst-cli-utils': patch +'myst-ext-proof': patch +'myst-templates': patch +'myst-ext-grid': patch +'myst-ext-icon': patch +'myst-ext-tabs': patch +'myst-to-typst': patch +'jats-to-myst': patch +'myst-execute': patch +'myst-migrate': patch +'myst-to-docx': patch +'myst-to-html': patch +'myst-to-jats': patch +'myst-common': patch +'myst-config': patch +'myst-parser': patch +'myst-to-tex': patch +'tex-to-myst': patch +'myst-roles': patch +'myst-to-md': patch +'myst-spec': patch +'myst-cli': patch +'mystmd': patch +'jtex': patch +--- + +Update package metadata diff --git a/bun.lock b/bun.lock index 206d0abb7c..69d8ca2b7c 100644 --- a/bun.lock +++ b/bun.lock @@ -7,8 +7,16 @@ "devDependencies": { "@changesets/cli": "^2.26.1", "@types/node": "^24.6.2", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "esbuild": "^0.25.10", + "eslint": "^8.45.0", "eslint-config-curvenote": "^0.0.4", + "eslint-config-next": "^13.4.10", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-react": "7.32.2", "husky": "^9.1.7", "lint-staged": "^15.5.0", "madge": "^6.1.0", @@ -27,6 +35,7 @@ "@citation-js/core": "^0.7.18", "@citation-js/plugin-bibtex": "^0.7.18", "@citation-js/plugin-csl": "^0.7.18", + "doi-utils": "^2.0.5", "sanitize-html": "^2.7.0", }, "devDependencies": { @@ -39,7 +48,7 @@ "dependencies": { "doi-utils": "^2.0.5", "jats-tags": "^1.0.8", - "jats-xml": "^1.0.8", + "jats-xml": "^1.1.7", "myst-common": "^1.9.2", "myst-frontmatter": "^1.9.2", "myst-spec": "^0.0.5", @@ -76,6 +85,7 @@ }, "devDependencies": { "@types/adm-zip": "^0.5.0", + "@types/js-yaml": "^4.0.5", "@types/nunjucks": "^3.2.2", "memfs": "^3.5.3", }, @@ -89,6 +99,7 @@ "vfile-message": "^3.0.0", }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "markdown-it-footnote": "^4.0.0", }, "peerDependencies": { @@ -168,6 +179,7 @@ "unist-util-visit": "^4.0.0", "uuid": "^8.3.2", "vfile": "^5.3.5", + "vfile-message": "^3.0.0", "which": "^3.0.1", "ws": "^8.18.1", "xml-js": "^1.6.11", @@ -180,12 +192,15 @@ "@types/express": "^4.17.17", "@types/fs-extra": "^11.0.1", "@types/inquirer": "^9.0.7", + "@types/js-yaml": "^4.0.5", + "@types/mdast": "^3.0.12", "@types/mime-types": "^2.1.1", "@types/picomatch": "^4.0.2", "@types/uuid": "^8.3.4", "@types/which": "^3.0.0", "@types/ws": "^8.18.1", "concurrently": "^8.2.0", + "memfs": "^3.5.3", }, }, "packages/myst-cli-utils": { @@ -193,6 +208,7 @@ "version": "2.0.14", "dependencies": { "chalk": "^5.2.0", + "commander": "^10.0.1", "pretty-hrtime": "^1.0.3", "tree-kill": "^1.2.2", }, @@ -219,6 +235,7 @@ "devDependencies": { "@jupyterlab/nbformat": "^3.5.2", "@lumino/coreutils": "^2.0.0", + "@types/unist": "^2.0.7", "myst-spec-ext": "^1.10.0", "unist-builder": "3.0.0", }, @@ -232,6 +249,8 @@ "simple-validators": "^1.2.0", }, "devDependencies": { + "glob": "^10.3.1", + "js-yaml": "^4.1.0", "moment": "^2.29.4", }, }, @@ -244,11 +263,16 @@ "js-yaml": "^4.1.0", "json5": "^1.0.2", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "nanoid": "^5.1.6", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/json5": "^0.0.29", + }, }, "packages/myst-execute": { "name": "myst-execute", @@ -260,13 +284,16 @@ "get-port": "^6.1.2", "myst-cli-utils": "^2.0.13", "myst-common": "^1.10.0", + "myst-frontmatter": "^1.10.0", "myst-spec": "^0.0.5", "node-fetch": "^3.3.0", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "which": "^4.0.0", }, "devDependencies": { + "@types/which": "^3.0.4", "js-yaml": "^4.1.0", }, }, @@ -279,6 +306,7 @@ }, "devDependencies": { "myst-parser": "^1.5.11", + "vfile": "^5.3.7", }, }, "packages/myst-ext-card": { @@ -296,9 +324,11 @@ "version": "1.0.9", "dependencies": { "myst-common": "^1.7.2", + "myst-directives": "^1.7.3", }, "devDependencies": { "myst-parser": "^1.5.9", + "unist-util-select": "^4.0.3", }, }, "packages/myst-ext-grid": { @@ -306,6 +336,7 @@ "version": "1.1.0", "dependencies": { "myst-common": "^1.7.2", + "myst-directives": "^1.7.3", }, "devDependencies": { "myst-parser": "^1.7.1", @@ -319,6 +350,7 @@ }, "devDependencies": { "myst-parser": "^1.5.7", + "unist-util-select": "^4.0.3", }, }, "packages/myst-ext-proof": { @@ -326,6 +358,7 @@ "version": "1.0.12", "dependencies": { "myst-common": "^1.7.2", + "myst-directives": "^1.7.3", "myst-spec": "^0.0.5", }, "devDependencies": { @@ -347,9 +380,11 @@ "version": "1.0.9", "dependencies": { "myst-common": "^1.7.2", + "myst-directives": "^1.7.3", }, "devDependencies": { "myst-parser": "^1.5.9", + "unist-util-select": "^4.0.3", }, }, "packages/myst-frontmatter": { @@ -364,6 +399,8 @@ "spdx-correct": "^3.2.0", }, "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/json-schema": "^7.0.15", "@types/spdx-correct": "^3.1.0", "glob": "^10.3.1", "js-yaml": "^4.1.0", @@ -382,6 +419,7 @@ }, "devDependencies": { "@jupyterlab/nbformat": "^3.5.2", + "@types/mdast": "^3.0.12", }, }, "packages/myst-parser": { @@ -416,6 +454,7 @@ "@types/js-yaml": "^4.0.5", "@types/mdast": "^3.0.10", "js-yaml": "^4.1.0", + "myst-spec-ext": "^1.9.4", "myst-to-html": "^1.7.3", "myst-transforms": "^1.3.50", "rehype-stringify": "^9.0.3", @@ -428,11 +467,15 @@ "myst-common": "^1.10.0", "myst-spec-ext": "^1.10.0", }, + "devDependencies": { + "vfile": "^5.3.7", + }, }, "packages/myst-spec": { "name": "myst-spec", "version": "0.0.5", "devDependencies": { + "@jupyterlab/nbformat": "^3.5.2", "copyfiles": "^2.4.1", }, }, @@ -460,6 +503,8 @@ "simple-validators": "^1.2.0", }, "devDependencies": { + "@types/adm-zip": "^0.5.0", + "@types/js-yaml": "^4.0.5", "@types/nunjucks": "^3.2.2", "memfs": "^3.5.3", }, @@ -474,7 +519,12 @@ "myst-frontmatter": "^1.9.3", "myst-spec": "^0.0.5", "myst-spec-ext": "^1.9.3", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", + }, + "devDependencies": { + "unist-builder": "^3.0.0", }, }, "packages/myst-to-html": { @@ -503,7 +553,10 @@ "unist-util-visit": "^4.1.0", }, "devDependencies": { + "@types/hast": "^2.3.4", + "@types/mdast": "^3.0.12", "hastscript": "^7.0.0", + "js-yaml": "^4.1.0", }, }, "packages/myst-to-jats": { @@ -522,6 +575,7 @@ "myst-spec-ext": "^1.9.2", "myst-transforms": "^1.3.44", "nbtx": "^0.4.0", + "orcid": "^1.0.0", "unified": "^10.1.2", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", @@ -533,9 +587,11 @@ "@types/js-yaml": "^4.0.5", "@types/katex": "^0.14.0", "@types/mdast": "^3.0.10", - "jats-xml": "^1.0.7", + "jats-xml": "^1.1.7", "js-yaml": "^4.1.0", "myst-cli-utils": "^2.0.11", + "simple-validators": "^1.2.0", + "unist-builder": "^3.0.0", }, }, "packages/myst-to-md": { @@ -548,10 +604,15 @@ "mdast-util-to-markdown": "^1.5.0", "myst-common": "^1.8.3", "myst-frontmatter": "^1.8.3", + "myst-spec": "^0.0.5", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + }, }, "packages/myst-to-tex": { "name": "myst-to-tex", @@ -560,13 +621,18 @@ "myst-common": "^1.9.4", "myst-ext-proof": "^1.0.11", "myst-frontmatter": "^1.9.4", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.9.4", + "unified": "^10.1.2", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, "devDependencies": { + "js-yaml": "^4.1.0", "myst-parser": "^1.7.1", + "unist-builder": "^3.0.0", }, }, "packages/myst-to-typst": { @@ -575,11 +641,18 @@ "dependencies": { "myst-common": "^1.10.0", "myst-frontmatter": "^1.10.0", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "tex-to-typst": "^0.0.20", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, + "devDependencies": { + "js-yaml": "^4.1.0", + "unist-builder": "^3.0.0", + }, }, "packages/myst-toc": { "name": "myst-toc", @@ -604,6 +677,7 @@ "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "myst-to-html": "1.7.3", + "rehype-format": "^4.0.1", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", "unified": "^10.0.0", @@ -618,7 +692,9 @@ "vfile-message": "^3.1.2", }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "@types/katex": "^0.14.0", + "@types/mdast": "^3.0.12", }, }, "packages/mystmd": { @@ -628,12 +704,16 @@ "myst": "./dist/myst.cjs", }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "async-mutex": "^0.5.0", "chalk": "^5.2.0", "commander": "^10.0.1", "core-js": "^3.31.1", "js-yaml": "^4.1.0", - "myst-cli": "^1.8.2", + "myst-cli": "^1.10.1", + "myst-cli-utils": "^2.0.14", + "myst-common": "^1.10.0", + "myst-templates": "^1.0.27", }, }, "packages/simple-validators": { @@ -655,8 +735,12 @@ "unist-builder": "^3.0.0", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, + "devDependencies": { + "js-yaml": "^4.1.0", + }, }, "packages/tsconfig": { "name": "tsconfig", @@ -999,7 +1083,7 @@ "@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="], - "@types/which": ["@types/which@3.0.0", "", {}, "sha512-ASCxdbsrwNfSMXALlC3Decif9rwDMu+80KGp5zI2RLRotfMsTv7fHL8W8VDp24wymzDyIFudhUeSCugrgRFfHQ=="], + "@types/which": ["@types/which@3.0.4", "", {}, "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], @@ -1091,7 +1175,7 @@ "agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="], - "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], @@ -1281,7 +1365,7 @@ "credit-roles": ["credit-roles@2.1.0", "", {}, "sha512-wt1jw7lDnzY1Ob4cDHpXboN4Bfu6l7reKal0zxtKnxogqw916l+Iu862LxIze4U4y44ssAd0TOQqVg2cXY2V6Q=="], - "cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "crypto": ["crypto@1.0.1", "", {}, "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="], @@ -1361,7 +1445,7 @@ "docx": ["docx@7.8.2", "", { "dependencies": { "@types/node": "^18.0.0", "jszip": "^3.1.5", "nanoid": "^3.1.20", "xml": "^1.0.1", "xml-js": "^1.6.8" } }, "sha512-ptaiVZunYO04z113E/jR3yZx7/VaeUap2CHM9V1YwlmGygJ2W8y2KBmAsVgzlzs+qT7+3d5W3Q7+57BgvGiL+Q=="], - "doi-utils": ["doi-utils@2.0.5", "", {}, "sha512-BAaEF9L9KqJmQfk1frtgAySPnwOdaY2UzZ85XvFAMLdGeqrH/GiwIziQU2y3WgrCuQ0ng74bq5uYBo8Qma/WqA=="], + "doi-utils": ["doi-utils@2.0.6", "", {}, "sha512-QkaDmWbr5lIugDgl9fCxDMos2OJJ9Rp4c9XB7wgRGm6hJQ+hgeqI78DqToU8+vmtU+1XAyyVYJoLZUAgBdsFtg=="], "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], @@ -1469,8 +1553,6 @@ "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="], - "fair-principles": ["fair-principles@2.0.0", "", {}, "sha512-yjZ2BW52nOWnpEeiY16v3O5NogwER53pSKiRLslPkAcgn0cZX3k7udWUgpaGMXdora4MxA3bwVo6V2ZMfQDwrg=="], - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], @@ -1553,7 +1635,7 @@ "glob": ["glob@10.3.3", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.0.3", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": "dist/cjs/src/bin.js" }, "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw=="], - "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "globals": ["globals@13.20.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ=="], @@ -1757,17 +1839,15 @@ "jackspeak": ["jackspeak@2.3.6", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ=="], - "jats-fetch": ["jats-fetch@1.0.8", "", { "dependencies": { "doi-utils": "^2.0.0", "myst-cli-utils": "^2.0.0", "node-fetch": "^3.3.1" } }, "sha512-NszocWquZ5bB36gzw45c6Tv9+wyGqJ4/c9Q4MggqqxzNQ4urADXjsypK7j9douBR+RjKJy73/LitDJnEBN3JuA=="], - - "jats-tags": ["jats-tags@1.0.8", "", {}, "sha512-eTnBvOc5XKha7j8SyWupKIkV9lbkqr7c4CjW6wNNALMrpTJ39GgV5JZ6ZaoUl6zUC2/T2W97A51MXvQWAAoF/Q=="], + "jats-tags": ["jats-tags@1.1.8", "", {}, "sha512-/sTtXe3cacc8VgHLBV5rdKmSDNALM6SCbaruAY5PVwR2+J60XGk1PvWkyAMQ/th5RY698OeDxQ2LmY162gC1pg=="], "jats-to-myst": ["jats-to-myst@workspace:packages/jats-to-myst"], - "jats-utils": ["jats-utils@1.0.8", "", { "dependencies": { "unist-util-select": "^4.0.0" }, "peerDependencies": { "jats-tags": "^1.0.8", "xml-js": "^1" } }, "sha512-e2RUoznZXK1GLPll6opEMt2aQePdWQOwBCHCWkLnhphlnE356sw4WRUM2leCG5xVxMV7XQBIfhHFulWCO47RnA=="], + "jats-utils": ["jats-utils@1.1.8", "", { "dependencies": { "jats-tags": "^1.1.8", "myst-common": "^1.0.0", "unist-util-select": "^4.0.0" }, "peerDependencies": { "xml-js": "^1" } }, "sha512-7aClX64cv55orWlnOdgLifKJ8H4XcD/sO+qOd7kqloD8AzlddpgiGiSReTAzRDDStwDttMKfjE7qVTdm3TnQsA=="], - "jats-xml": ["jats-xml@1.0.8", "", { "dependencies": { "adm-zip": "^0.5.10", "doi-utils": "^2.0.0", "fair-principles": "^2.0.0", "jats-fetch": "^1.0.8", "jats-tags": "^1.0.8", "jats-utils": "^1.0.8", "js-yaml": "^4.1.0", "node-fetch": "^3.3.1", "unist-util-is": "^5.2.1", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.0", "which": "^3.0.1", "xml-js": "^1.6.11" }, "peerDependencies": { "chalk": "^5.2.0", "commander": "^10.0.1" }, "bin": { "jats": "dist/jats.cjs" } }, "sha512-yI1rbW/toz4x04/pDp1gsNcySxC4kFhJcU91sqBZdliCg5PXvCOCnb/OhGGPAD/vehBWGiGbdjuLbQ4WDcBSNA=="], + "jats-xml": ["jats-xml@1.1.8", "", { "dependencies": { "adm-zip": "^0.5.10", "character-entities": "^2.0.2", "doi-utils": "^2.0.6", "jats-tags": "^1.1.8", "jats-utils": "^1.1.8", "myst-cli-utils": "^2.0.11", "myst-common": "^1.7.5", "myst-frontmatter": "^1.7.5", "myst-spec": "^0.0.5", "node-fetch": "^3.3.1", "unist-util-remove": "4.0.0", "unist-util-select": "^4.0.0", "vfile": "^5.0.0", "which": "^3.0.1", "xml-js": "^1.6.11" }, "peerDependencies": { "chalk": "^5.2.0" } }, "sha512-S4hslkbXM/EN5KLxMNFgXIC+u2y6nvOUhVFPQK6CN0y6DReTSGiMOFxTct3gv0Ba2B3dTDtN4L5M7zsxqXqc8w=="], - "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": "bin/js-yaml.js" }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -1779,7 +1859,7 @@ "json-schema-merge-allof": ["json-schema-merge-allof@0.8.1", "", { "dependencies": { "compute-lcm": "^1.1.2", "json-schema-compare": "^0.2.2", "lodash": "^4.17.20" } }, "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w=="], - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], @@ -1893,7 +1973,7 @@ "mdurl": ["mdurl@1.0.1", "", {}, "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="], - "meca": ["meca@1.0.8", "", { "dependencies": { "adm-zip": "^0.5.10", "jats-utils": "^1.0.8", "jats-xml": "^1.0.8", "node-fetch": "^3.3.1", "unist-util-select": "^4.0.0", "xml-js": "^1.6.11" }, "peerDependencies": { "chalk": "^5.2.0", "commander": "^10.0.1" }, "bin": "dist/meca.cjs" }, "sha512-CIctq7IldEEKHcYsNeCw0+MOtoblXX5pzI7qShnaWpP+WHETf27blyqNAMyJk6O/YUxXwuJ2eqYmcwmNy2k40Q=="], + "meca": ["meca@1.1.8", "", { "dependencies": { "adm-zip": "^0.5.10", "jats-utils": "^1.1.8", "jats-xml": "^1.1.8", "myst-cli-utils": "^2.0.11", "myst-common": "^1.7.5", "node-fetch": "^3.3.1", "unist-util-select": "^4.0.0", "xml-js": "^1.6.11" }, "peerDependencies": { "chalk": "^5.2.0", "commander": "^10.0.1" }, "bin": { "meca": "dist/meca.cjs" } }, "sha512-GnRyStOHuD9IRSBoaZQgh1DvgD3q2983BXJe7lmCpzoTcacNc3c6ZrYYnIbDLst+5Uz2FBo42eEpg7kSyqUxZQ=="], "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], @@ -2153,7 +2233,7 @@ "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - "path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], @@ -2323,9 +2403,9 @@ "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - "shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], "shell-quote": ["shell-quote@1.8.1", "", {}, "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA=="], @@ -2401,7 +2481,7 @@ "strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], - "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], @@ -2661,10 +2741,6 @@ "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": "bin-prettier.js" }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], - "@eslint/eslintrc/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - - "@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "@jupyter/ydoc/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2" } }, "sha512-+dB/edJjn7JDFiyN4nGw+quxBwUFGktgbR8udQeJweDne5mPMArpwCez/HBFkIW/816UX6+gSji01wTP4y10Lw=="], "@jupyterlab/nbformat/@lumino/coreutils": ["@lumino/coreutils@1.12.1", "", { "peerDependencies": { "crypto": "1.0.1" } }, "sha512-JLu3nTHzJk9N8ohZ85u75YxemMrmDzJdNgZztfP7F7T7mxND3YVNCkJG35a6aJ7edu1sIgCjBxOvV+hv27iYvQ=="], @@ -2673,6 +2749,8 @@ "@jupyterlab/settingregistry/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2" } }, "sha512-+dB/edJjn7JDFiyN4nGw+quxBwUFGktgbR8udQeJweDne5mPMArpwCez/HBFkIW/816UX6+gSji01wTP4y10Lw=="], + "@jupyterlab/settingregistry/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "@jupyterlab/settingregistry/json5": ["json5@2.2.3", "", { "bin": "lib/cli.js" }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], "@manypkg/find-root/@types/node": ["@types/node@12.20.55", "", {}, "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="], @@ -2687,8 +2765,6 @@ "@next/eslint-plugin-next/glob": ["glob@7.1.7", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ=="], - "@pkgr/utils/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], "@rjsf/utils/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -2699,6 +2775,8 @@ "@unified-latex/unified-latex-util-to-string/prettier": ["prettier@2.8.8", "", { "bin": "bin-prettier.js" }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + "ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], @@ -2713,10 +2791,14 @@ "cacheable-request/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "cffjs/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "check-node-version/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], "check-node-version/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "cli-truncate/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2739,9 +2821,7 @@ "copyfiles/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], - "cross-spawn/semver": ["semver@5.7.2", "", { "bin": "bin/semver" }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - - "cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": "bin/which" }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], @@ -2769,14 +2849,8 @@ "enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "eslint/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - "eslint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "eslint/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "eslint/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - "eslint/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "eslint-config-next/@typescript-eslint/parser": ["@typescript-eslint/parser@5.62.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA=="], @@ -2801,10 +2875,10 @@ "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "execa/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "fetch-ponyfill/node-fetch": ["node-fetch@2.6.12", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g=="], @@ -2817,8 +2891,6 @@ "flat-cache/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": "bin.js" }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], - "foreground-child/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "glob/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], "globals/type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], @@ -2851,6 +2923,8 @@ "is-wsl/is-docker": ["is-docker@2.2.1", "", { "bin": "cli.js" }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + "jats-xml/unist-util-remove": ["unist-util-remove@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg=="], + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "lint-staged/commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], @@ -2869,8 +2943,6 @@ "log-update/wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], - "loose-envify/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "madge/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "madge/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], @@ -2895,6 +2967,8 @@ "npm-run-all/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + "npm-run-all/cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], + "npm-run-all/pidtree": ["pidtree@0.3.1", "", { "bin": "bin/pidtree.js" }, "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], @@ -2933,6 +3007,8 @@ "raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "read-yaml-file/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": "bin/js-yaml.js" }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], @@ -2957,8 +3033,6 @@ "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], - "spawndamnit/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -2967,6 +3041,8 @@ "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "stylus-lookup/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], "sync-fetch/node-fetch": ["node-fetch@2.6.12", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g=="], @@ -3015,7 +3091,7 @@ "@changesets/write/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "@eslint/eslintrc/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "@jupyterlab/settingregistry/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@manypkg/find-root/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], @@ -3027,20 +3103,18 @@ "@manypkg/get-packages/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "@pkgr/utils/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "@pkgr/utils/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "@pkgr/utils/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "@typescript-eslint/utils/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "cffjs/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "check-node-version/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "check-node-version/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -3063,8 +3137,6 @@ "copyfiles/yargs/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], - "default-browser/execa/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "default-browser/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "default-browser/execa/human-signals": ["human-signals@4.3.1", "", {}, "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ=="], @@ -3091,38 +3163,18 @@ "eslint-import-resolver-typescript/globby/slash": ["slash@4.0.0", "", {}, "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew=="], - "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - "eslint/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "eslint/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "eslint/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "eslint/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "eslint/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "eslint/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "execa/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "execa/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "execa/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "flat-cache/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "foreground-child/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "foreground-child/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "foreground-child/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "hast-util-from-html/@types/hast/@types/unist": ["@types/unist@3.0.0", "", {}, "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="], @@ -3151,6 +3203,12 @@ "intersphinx/myst-cli-utils/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "jats-xml/unist-util-remove/@types/unist": ["@types/unist@3.0.0", "", {}, "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="], + + "jats-xml/unist-util-remove/unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "jats-xml/unist-util-remove/unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + "listr2/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], "listr2/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -3183,6 +3241,14 @@ "npm-run-all/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], + "npm-run-all/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], + + "npm-run-all/cross-spawn/semver": ["semver@5.7.2", "", { "bin": "bin/semver" }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + + "npm-run-all/cross-spawn/shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], + + "npm-run-all/cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": "bin/which" }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + "ora/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "ora/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -3213,8 +3279,6 @@ "rimraf/glob/path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="], - "run-applescript/execa/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "run-applescript/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "run-applescript/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], @@ -3231,12 +3295,6 @@ "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "spawndamnit/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "spawndamnit/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "spawndamnit/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -3251,8 +3309,6 @@ "@manypkg/find-root/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - "@pkgr/utils/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "check-node-version/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -3269,24 +3325,12 @@ "copyfiles/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "default-browser/execa/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "default-browser/execa/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "default-browser/execa/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" } }, "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="], "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" } }, "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="], "eslint/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "eslint/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "foreground-child/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "hast-util-from-html/hast-util-from-parse5/hastscript/hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], "inquirer/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -3307,6 +3351,8 @@ "npm-run-all/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + "npm-run-all/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], + "ora/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "precinct/detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@4.33.0", "", {}, "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ=="], @@ -3315,18 +3361,8 @@ "rimraf/glob/path-scurry/lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="], - "run-applescript/execa/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "run-applescript/execa/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "run-applescript/execa/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "run-applescript/execa/npm-run-path/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "run-applescript/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "spawndamnit/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "@manypkg/find-root/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], @@ -3337,14 +3373,10 @@ "copyfiles/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "default-browser/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "intersphinx/myst-cli-utils/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "precinct/detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="], - "run-applescript/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "copyfiles/yargs/cliui/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], } } diff --git a/bunfig.toml b/bunfig.toml index f1e441c4bc..284f1af3fc 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -1,6 +1,8 @@ [install] # Use the new text-based lockfile (Bun 1.2+) lockfile = "bun.lock" +# Use pnpm-style isolated node_modules instead of the hoisted layout +linker = "isolated" [test] # Enable some Bun test features, in case we decide to use bun test diff --git a/package.json b/package.json index 0cfaff8b9a..a481d06f75 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,16 @@ "devDependencies": { "@changesets/cli": "^2.26.1", "@types/node": "^24.6.2", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "esbuild": "^0.25.10", + "eslint": "^8.45.0", "eslint-config-curvenote": "^0.0.4", + "eslint-config-next": "^13.4.10", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-react": "7.32.2", "husky": "^9.1.7", "lint-staged": "^15.5.0", "madge": "^6.1.0", diff --git a/packages/citation-js-utils/package.json b/packages/citation-js-utils/package.json index 352f537b0c..c9e077bf13 100644 --- a/packages/citation-js-utils/package.json +++ b/packages/citation-js-utils/package.json @@ -38,6 +38,7 @@ "@citation-js/core": "^0.7.18", "@citation-js/plugin-bibtex": "^0.7.18", "@citation-js/plugin-csl": "^0.7.18", + "doi-utils": "^2.0.5", "sanitize-html": "^2.7.0" }, "devDependencies": { diff --git a/packages/jats-to-myst/package.json b/packages/jats-to-myst/package.json index f6b35911bd..234f83f47d 100644 --- a/packages/jats-to-myst/package.json +++ b/packages/jats-to-myst/package.json @@ -37,7 +37,7 @@ "dependencies": { "doi-utils": "^2.0.5", "jats-tags": "^1.0.8", - "jats-xml": "^1.0.8", + "jats-xml": "^1.1.7", "myst-common": "^1.9.2", "myst-frontmatter": "^1.9.2", "myst-spec": "^0.0.5", diff --git a/packages/jtex/package.json b/packages/jtex/package.json index 09b226993d..da085aa9bb 100644 --- a/packages/jtex/package.json +++ b/packages/jtex/package.json @@ -59,6 +59,7 @@ }, "devDependencies": { "@types/adm-zip": "^0.5.0", + "@types/js-yaml": "^4.0.5", "@types/nunjucks": "^3.2.2", "memfs": "^3.5.3" } diff --git a/packages/markdown-it-myst/package.json b/packages/markdown-it-myst/package.json index b57d861af5..6988e1c6bd 100644 --- a/packages/markdown-it-myst/package.json +++ b/packages/markdown-it-myst/package.json @@ -37,6 +37,7 @@ "vfile-message": "^3.0.0" }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "markdown-it-footnote": "^4.0.0" }, "peerDependencies": { diff --git a/packages/myst-cli-utils/package.json b/packages/myst-cli-utils/package.json index 8b541e4a9d..7b7eb498eb 100644 --- a/packages/myst-cli-utils/package.json +++ b/packages/myst-cli-utils/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "chalk": "^5.2.0", + "commander": "^10.0.1", "pretty-hrtime": "^1.0.3", "tree-kill": "^1.2.2" }, diff --git a/packages/myst-cli/package.json b/packages/myst-cli/package.json index cb63fd0ae9..99439deb2b 100644 --- a/packages/myst-cli/package.json +++ b/packages/myst-cli/package.json @@ -110,6 +110,7 @@ "unist-util-visit": "^4.0.0", "uuid": "^8.3.2", "vfile": "^5.3.5", + "vfile-message": "^3.0.0", "which": "^3.0.1", "ws": "^8.18.1", "xml-js": "^1.6.11", @@ -122,11 +123,14 @@ "@types/express": "^4.17.17", "@types/fs-extra": "^11.0.1", "@types/inquirer": "^9.0.7", + "@types/js-yaml": "^4.0.5", + "@types/mdast": "^3.0.12", "@types/mime-types": "^2.1.1", "@types/picomatch": "^4.0.2", "@types/uuid": "^8.3.4", "@types/which": "^3.0.0", "@types/ws": "^8.18.1", - "concurrently": "^8.2.0" + "concurrently": "^8.2.0", + "memfs": "^3.5.3" } } diff --git a/packages/myst-common/package.json b/packages/myst-common/package.json index 23ef1e4d49..878aafadf6 100644 --- a/packages/myst-common/package.json +++ b/packages/myst-common/package.json @@ -39,6 +39,7 @@ "myst-spec-ext": "^1.10.0", "unist-builder": "3.0.0", "@jupyterlab/nbformat": "^3.5.2", - "@lumino/coreutils": "^2.0.0" + "@lumino/coreutils": "^2.0.0", + "@types/unist": "^2.0.7" } } diff --git a/packages/myst-config/package.json b/packages/myst-config/package.json index b9568162d2..80369ccda0 100644 --- a/packages/myst-config/package.json +++ b/packages/myst-config/package.json @@ -36,6 +36,8 @@ "simple-validators": "^1.2.0" }, "devDependencies": { + "glob": "^10.3.1", + "js-yaml": "^4.1.0", "moment": "^2.29.4" } } diff --git a/packages/myst-directives/package.json b/packages/myst-directives/package.json index b69e1adf04..cd88a2cf7b 100644 --- a/packages/myst-directives/package.json +++ b/packages/myst-directives/package.json @@ -36,10 +36,15 @@ "csv-parse": "^5.5.5", "js-yaml": "^4.1.0", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "json5": "^1.0.2", "nanoid": "^5.1.6", "unist-util-select": "^4.0.3", "vfile": "^5.3.7" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/json5": "^0.0.29" } } diff --git a/packages/myst-execute/package.json b/packages/myst-execute/package.json index abc0075e86..178a7aa5d2 100644 --- a/packages/myst-execute/package.json +++ b/packages/myst-execute/package.json @@ -38,13 +38,16 @@ "get-port": "^6.1.2", "myst-cli-utils": "^2.0.13", "myst-common": "^1.10.0", + "myst-frontmatter": "^1.10.0", "myst-spec": "^0.0.5", "node-fetch": "^3.3.0", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "which": "^4.0.0" }, "devDependencies": { + "@types/which": "^3.0.4", "js-yaml": "^4.1.0" } } diff --git a/packages/myst-ext-button/package.json b/packages/myst-ext-button/package.json index 8cc630d6cd..5e455f57c4 100644 --- a/packages/myst-ext-button/package.json +++ b/packages/myst-ext-button/package.json @@ -36,6 +36,7 @@ "myst-spec-ext": "^1.7.8" }, "devDependencies": { - "myst-parser": "^1.5.11" + "myst-parser": "^1.5.11", + "vfile": "^5.3.7" } } diff --git a/packages/myst-ext-exercise/package.json b/packages/myst-ext-exercise/package.json index b6a4e324d8..fb39c3b5c5 100644 --- a/packages/myst-ext-exercise/package.json +++ b/packages/myst-ext-exercise/package.json @@ -32,9 +32,11 @@ "url": "https://github.com/jupyter-book/mystmd/issues" }, "dependencies": { - "myst-common": "^1.7.2" + "myst-common": "^1.7.2", + "myst-directives": "^1.7.3" }, "devDependencies": { - "myst-parser": "^1.5.9" + "myst-parser": "^1.5.9", + "unist-util-select": "^4.0.3" } } diff --git a/packages/myst-ext-grid/package.json b/packages/myst-ext-grid/package.json index 9af95701ea..ff3a6e2cca 100644 --- a/packages/myst-ext-grid/package.json +++ b/packages/myst-ext-grid/package.json @@ -32,7 +32,8 @@ "url": "https://github.com/jupyter-book/mystmd/issues" }, "dependencies": { - "myst-common": "^1.7.2" + "myst-common": "^1.7.2", + "myst-directives": "^1.7.3" }, "devDependencies": { "myst-parser": "^1.7.1" diff --git a/packages/myst-ext-icon/package.json b/packages/myst-ext-icon/package.json index f17e893680..4c99db29fb 100644 --- a/packages/myst-ext-icon/package.json +++ b/packages/myst-ext-icon/package.json @@ -35,6 +35,7 @@ "myst-common": "^1.7.2" }, "devDependencies": { - "myst-parser": "^1.5.7" + "myst-parser": "^1.5.7", + "unist-util-select": "^4.0.3" } } diff --git a/packages/myst-ext-proof/package.json b/packages/myst-ext-proof/package.json index 25935e33ec..6443aa4771 100644 --- a/packages/myst-ext-proof/package.json +++ b/packages/myst-ext-proof/package.json @@ -33,7 +33,8 @@ }, "dependencies": { "myst-spec": "^0.0.5", - "myst-common": "^1.7.2" + "myst-common": "^1.7.2", + "myst-directives": "^1.7.3" }, "devDependencies": { "myst-parser": "^1.5.9" diff --git a/packages/myst-ext-tabs/package.json b/packages/myst-ext-tabs/package.json index c2a49b05ba..b4ff6a897c 100644 --- a/packages/myst-ext-tabs/package.json +++ b/packages/myst-ext-tabs/package.json @@ -32,9 +32,11 @@ "url": "https://github.com/jupyter-book/mystmd/issues" }, "dependencies": { - "myst-common": "^1.7.2" + "myst-common": "^1.7.2", + "myst-directives": "^1.7.3" }, "devDependencies": { - "myst-parser": "^1.5.9" + "myst-parser": "^1.5.9", + "unist-util-select": "^4.0.3" } } diff --git a/packages/myst-frontmatter/package.json b/packages/myst-frontmatter/package.json index c9d9d47c35..6fcf92e546 100644 --- a/packages/myst-frontmatter/package.json +++ b/packages/myst-frontmatter/package.json @@ -40,6 +40,8 @@ "myst-toc": "^0.1.4" }, "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/json-schema": "^7.0.15", "@types/spdx-correct": "^3.1.0", "glob": "^10.3.1", "js-yaml": "^4.1.0", diff --git a/packages/myst-migrate/package.json b/packages/myst-migrate/package.json index b3aa76ec1f..95d7adacb5 100644 --- a/packages/myst-migrate/package.json +++ b/packages/myst-migrate/package.json @@ -29,6 +29,7 @@ "vfile-message": "^3.0.0" }, "devDependencies": { - "@jupyterlab/nbformat": "^3.5.2" + "@jupyterlab/nbformat": "^3.5.2", + "@types/mdast": "^3.0.12" } } diff --git a/packages/myst-parser/package.json b/packages/myst-parser/package.json index 6e04856fc9..2b20a8bf3a 100644 --- a/packages/myst-parser/package.json +++ b/packages/myst-parser/package.json @@ -69,6 +69,7 @@ "@types/js-yaml": "^4.0.5", "@types/mdast": "^3.0.10", "js-yaml": "^4.1.0", + "myst-spec-ext": "^1.9.4", "myst-to-html": "^1.7.3", "myst-transforms": "^1.3.50", "rehype-stringify": "^9.0.3" diff --git a/packages/myst-parser/tests/myst.spec.ts b/packages/myst-parser/tests/myst.spec.ts index 78a24fe010..c989b78dfb 100644 --- a/packages/myst-parser/tests/myst.spec.ts +++ b/packages/myst-parser/tests/myst.spec.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from 'vitest'; import fs from 'node:fs'; import path from 'node:path'; +import { createRequire } from 'node:module'; import yaml from 'js-yaml'; import { visit } from 'unist-util-visit'; import { mystParse } from '../src'; @@ -34,10 +35,8 @@ const SKIP_TESTS = [ '506', // This is a link issue? ]; -// TODO: import this from myst-spec properly! -const directory = fs.existsSync('../../node_modules/myst-spec/dist/examples') - ? '../../node_modules/myst-spec/dist/examples' - : '../../../node_modules/myst-spec/dist/examples'; +const mystSpecPackageJson = createRequire(import.meta.url).resolve('myst-spec/package.json'); +const directory = path.join(path.dirname(mystSpecPackageJson), 'dist/examples'); const files: string[] = fs.readdirSync(directory).filter((name) => name.endsWith('.yml')); diff --git a/packages/myst-roles/package.json b/packages/myst-roles/package.json index cf4acf4571..c16b4700d1 100644 --- a/packages/myst-roles/package.json +++ b/packages/myst-roles/package.json @@ -34,5 +34,8 @@ "dependencies": { "myst-common": "^1.10.0", "myst-spec-ext": "^1.10.0" + }, + "devDependencies": { + "vfile": "^5.3.7" } } diff --git a/packages/myst-spec/package.json b/packages/myst-spec/package.json index df56f46b49..11b57a63b6 100644 --- a/packages/myst-spec/package.json +++ b/packages/myst-spec/package.json @@ -30,6 +30,7 @@ "url": "https://github.com/jupyter-book/myst-spec/issues" }, "devDependencies": { + "@jupyterlab/nbformat": "^3.5.2", "copyfiles": "^2.4.1" } } diff --git a/packages/myst-templates/package.json b/packages/myst-templates/package.json index 3cd43c32b9..b1ac8c2a0e 100644 --- a/packages/myst-templates/package.json +++ b/packages/myst-templates/package.json @@ -50,6 +50,8 @@ "simple-validators": "^1.2.0" }, "devDependencies": { + "@types/adm-zip": "^0.5.0", + "@types/js-yaml": "^4.0.5", "@types/nunjucks": "^3.2.2", "memfs": "^3.5.3" } diff --git a/packages/myst-to-docx/package.json b/packages/myst-to-docx/package.json index a83cb07b22..4fa90b3684 100644 --- a/packages/myst-to-docx/package.json +++ b/packages/myst-to-docx/package.json @@ -40,6 +40,11 @@ "myst-frontmatter": "^1.9.3", "myst-spec": "^0.0.5", "myst-spec-ext": "^1.9.3", - "unist-util-select": "^4.0.3" + "unified": "^10.1.2", + "unist-util-select": "^4.0.3", + "vfile": "^5.3.7" + }, + "devDependencies": { + "unist-builder": "^3.0.0" } } diff --git a/packages/myst-to-html/package.json b/packages/myst-to-html/package.json index 16347138ad..b49e89a210 100644 --- a/packages/myst-to-html/package.json +++ b/packages/myst-to-html/package.json @@ -59,6 +59,9 @@ "unist-util-visit": "^4.1.0" }, "devDependencies": { - "hastscript": "^7.0.0" + "@types/hast": "^2.3.4", + "@types/mdast": "^3.0.12", + "hastscript": "^7.0.0", + "js-yaml": "^4.1.0" } } diff --git a/packages/myst-to-jats/package.json b/packages/myst-to-jats/package.json index ae38135705..adff9009ef 100644 --- a/packages/myst-to-jats/package.json +++ b/packages/myst-to-jats/package.json @@ -47,6 +47,7 @@ "myst-spec-ext": "^1.9.2", "myst-transforms": "^1.3.44", "nbtx": "^0.4.0", + "orcid": "^1.0.0", "unified": "^10.1.2", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", @@ -58,8 +59,10 @@ "@types/js-yaml": "^4.0.5", "@types/katex": "^0.14.0", "@types/mdast": "^3.0.10", - "jats-xml": "^1.0.7", + "jats-xml": "^1.1.7", "js-yaml": "^4.1.0", - "myst-cli-utils": "^2.0.11" + "myst-cli-utils": "^2.0.11", + "simple-validators": "^1.2.0", + "unist-builder": "^3.0.0" } } diff --git a/packages/myst-to-jats/tests/basic.spec.ts b/packages/myst-to-jats/tests/basic.spec.ts index fd9ffbb25c..d979336be9 100644 --- a/packages/myst-to-jats/tests/basic.spec.ts +++ b/packages/myst-to-jats/tests/basic.spec.ts @@ -6,7 +6,6 @@ import { validateProjectFrontmatter } from 'myst-frontmatter'; import { SourceFileKind } from 'myst-spec-ext'; import type { ValidationOptions } from 'simple-validators'; import path from 'node:path'; -import { validateJatsAgainstDtd } from 'jats-xml'; import yaml from 'js-yaml'; import { VFile } from 'vfile'; import mystToJats, { writeJats } from '../src'; diff --git a/packages/myst-to-md/package.json b/packages/myst-to-md/package.json index 9e89707114..6fd3c877b6 100644 --- a/packages/myst-to-md/package.json +++ b/packages/myst-to-md/package.json @@ -42,8 +42,13 @@ "mdast-util-to-markdown": "^1.5.0", "myst-common": "^1.8.3", "myst-frontmatter": "^1.8.3", + "myst-spec": "^0.0.5", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.5" } } diff --git a/packages/myst-to-tex/package.json b/packages/myst-to-tex/package.json index bc1c6c43c3..1d49139800 100644 --- a/packages/myst-to-tex/package.json +++ b/packages/myst-to-tex/package.json @@ -39,13 +39,18 @@ "dependencies": { "myst-common": "^1.9.4", "myst-frontmatter": "^1.9.4", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.9.4", "myst-ext-proof": "^1.0.11", + "unified": "^10.1.2", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" }, "devDependencies": { - "myst-parser": "^1.7.1" + "js-yaml": "^4.1.0", + "myst-parser": "^1.7.1", + "unist-builder": "^3.0.0" } } diff --git a/packages/myst-to-typst/package.json b/packages/myst-to-typst/package.json index ed69e7c95f..a834c9b036 100644 --- a/packages/myst-to-typst/package.json +++ b/packages/myst-to-typst/package.json @@ -39,9 +39,16 @@ "dependencies": { "myst-common": "^1.10.0", "myst-frontmatter": "^1.10.0", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "tex-to-typst": "^0.0.20", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" + }, + "devDependencies": { + "js-yaml": "^4.1.0", + "unist-builder": "^3.0.0" } } diff --git a/packages/myst-transforms/package.json b/packages/myst-transforms/package.json index 3e46abf70c..3f8dda6e53 100644 --- a/packages/myst-transforms/package.json +++ b/packages/myst-transforms/package.json @@ -35,6 +35,7 @@ "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "myst-to-html": "1.7.3", + "rehype-format": "^4.0.1", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", "unified": "^10.0.0", @@ -49,6 +50,8 @@ "vfile-message": "^3.1.2" }, "devDependencies": { - "@types/katex": "^0.14.0" + "@types/js-yaml": "^4.0.5", + "@types/katex": "^0.14.0", + "@types/mdast": "^3.0.12" } } diff --git a/packages/mystmd/package.json b/packages/mystmd/package.json index ef4ef831f4..a2966557b8 100644 --- a/packages/mystmd/package.json +++ b/packages/mystmd/package.json @@ -42,11 +42,15 @@ "build": "npm-run-all -l clean copy:version -p build:cli" }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "async-mutex": "^0.5.0", "chalk": "^5.2.0", "commander": "^10.0.1", "core-js": "^3.31.1", "js-yaml": "^4.1.0", - "myst-cli": "^1.10.1" + "myst-cli": "^1.10.1", + "myst-cli-utils": "^2.0.14", + "myst-common": "^1.10.0", + "myst-templates": "^1.0.27" } } diff --git a/packages/tex-to-myst/package.json b/packages/tex-to-myst/package.json index 41c5937da3..3f74f76d57 100644 --- a/packages/tex-to-myst/package.json +++ b/packages/tex-to-myst/package.json @@ -44,6 +44,10 @@ "unist-builder": "^3.0.0", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" + }, + "devDependencies": { + "js-yaml": "^4.1.0" } } From f33e0fd66db3425a79268ea0099abc6daf88f1b8 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Thu, 9 Jul 2026 20:07:38 -0700 Subject: [PATCH 11/24] Rewrite docs (#2997) Co-authored-by: Rowan Cockett --- bun.lock | 22 ++++++++++++++++++++++ docs/contribute-docs.md | 17 +++++++++++------ docs/developer.md | 6 ++++++ docs/package.json | 21 +++++++++++++++++++++ package.json | 3 ++- 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 docs/package.json diff --git a/bun.lock b/bun.lock index 69d8ca2b7c..7cbb0716af 100644 --- a/bun.lock +++ b/bun.lock @@ -28,6 +28,26 @@ "vitest": "^3.2.4", }, }, + "docs": { + "name": "mystmd-docs", + "version": "0.0.0", + "dependencies": { + "myst-common": "workspace:*", + "myst-directives": "workspace:*", + "myst-ext-button": "workspace:*", + "myst-ext-card": "workspace:*", + "myst-ext-exercise": "workspace:*", + "myst-ext-grid": "workspace:*", + "myst-ext-proof": "workspace:*", + "myst-ext-tabs": "workspace:*", + "myst-migrate": "workspace:*", + "myst-parser": "workspace:*", + "myst-roles": "workspace:*", + "tex-to-myst": "workspace:*", + "unist-builder": "^3.0.0", + "unist-util-select": "^4.0.3", + }, + }, "packages/citation-js-utils": { "name": "citation-js-utils", "version": "1.2.8", @@ -2127,6 +2147,8 @@ "mystmd": ["mystmd@workspace:packages/mystmd"], + "mystmd-docs": ["mystmd-docs@workspace:docs"], + "nanoid": ["nanoid@5.1.6", "", { "bin": "bin/nanoid.js" }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], diff --git a/docs/contribute-docs.md b/docs/contribute-docs.md index 3c1e426c91..ac6d992f76 100644 --- a/docs/contribute-docs.md +++ b/docs/contribute-docs.md @@ -27,28 +27,33 @@ Alternatively, you can use the [**`github.dev` web-based editor**](https://docs. ## How to build the MyST guide documentation locally +The documentation in `docs/` uses its own MyST plugins (`docs/*.mjs`) that require an install and build of the packages, so previewing it requires the full local development setup rather than a globally installed `myst`. + To build the MyST guide documentation: -1. Clone this repository: +1. Clone this repository and build: ``` git clone https://github.com/jupyter-book/mystmd + cd mystmd + bun install + bun run build + bun run link # install this version of mystmd globally ``` -2. [Install MyST Markdown by following these instructions](https://mystmd.org/guide/quickstart) -3. Navigate to the docs folder: +2. Navigate to the docs folder: ``` - cd docs/ + cd docs ``` -4. Start a MyST server to preview the documentation: +3. Start a MyST server to preview the documentation: ``` myst start ``` -This will build the documentation locally so that you can preview what your changes will look like. +The `bun run docs` command is a shorthand for steps 2 and 3 (see [the developer guide](#local-docs-workflows)). ### Build with executable content diff --git a/docs/developer.md b/docs/developer.md index 799e3e5651..4c7fbf4fea 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -315,6 +315,8 @@ The build process uses unix commands that might not work properly on Windows. When building on Windows, use either WSL or a unix-like shell (such as Git Bash or MSYS2). ``` +(local-docs-workflows)= + ### Local docs workflows (preview/build) To run a MyST server with your local changes and a preview of the documentation, run: @@ -323,6 +325,10 @@ To run a MyST server with your local changes and a preview of the documentation, $ bun run docs ``` +This builds the `mystmd` packages, and starts a live-reloading preview server. + +The `docs/` plugins import several `myst-*` packages as `workspace:*` dependencies, so that they build against the source versions in this repository (not against npm). If you only change the plugins in `docs/*.mjs`, you will need to rebuild the packages and restart the server. + ## Local development: myst-theme The [`myst-theme` README](https://github.com/jupyter-book/myst-theme/) provides a more detailed overview of the components of that package. diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000000..f5cc6c0aa2 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,21 @@ +{ + "name": "mystmd-docs", + "private": true, + "version": "0.0.0", + "dependencies": { + "myst-common": "workspace:*", + "myst-directives": "workspace:*", + "myst-ext-button": "workspace:*", + "myst-ext-card": "workspace:*", + "myst-ext-exercise": "workspace:*", + "myst-ext-grid": "workspace:*", + "myst-ext-proof": "workspace:*", + "myst-ext-tabs": "workspace:*", + "myst-migrate": "workspace:*", + "myst-parser": "workspace:*", + "myst-roles": "workspace:*", + "tex-to-myst": "workspace:*", + "unist-builder": "^3.0.0", + "unist-util-select": "^4.0.3" + } +} diff --git a/package.json b/package.json index a481d06f75..b611396627 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "mystmd", "private": true, "workspaces": [ - "packages/*" + "packages/*", + "docs" ], "scripts": { "build": "turbo run build", From 41782d26a7f22576215d5093b602f90adbfef41f Mon Sep 17 00:00:00 2001 From: Rowan Cockett Date: Fri, 17 Jul 2026 09:04:20 -0600 Subject: [PATCH 12/24] =?UTF-8?q?=E2=9C=8A=20Bump=20`tex-to-typst`=20to=20?= =?UTF-8?q?0.0.21=20(#3008)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See #3007 --- .changeset/khaki-papayas-jog.md | 6 ++++++ bun.lock | 4 ++-- packages/myst-to-typst/package.json | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 .changeset/khaki-papayas-jog.md diff --git a/.changeset/khaki-papayas-jog.md b/.changeset/khaki-papayas-jog.md new file mode 100644 index 0000000000..26254d098a --- /dev/null +++ b/.changeset/khaki-papayas-jog.md @@ -0,0 +1,6 @@ +--- +'myst-to-typst': patch +'mystmd': patch +--- + +`tex-to-typst` bumped to 0.0.21 diff --git a/bun.lock b/bun.lock index 7cbb0716af..3beeca823f 100644 --- a/bun.lock +++ b/bun.lock @@ -663,7 +663,7 @@ "myst-frontmatter": "^1.10.0", "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", - "tex-to-typst": "^0.0.20", + "tex-to-typst": "^0.0.21", "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", @@ -2523,7 +2523,7 @@ "tex-to-myst": ["tex-to-myst@workspace:packages/tex-to-myst"], - "tex-to-typst": ["tex-to-typst@0.0.20", "", { "dependencies": { "@unified-latex/unified-latex": "^1.4.0", "@unified-latex/unified-latex-util-arguments": "^1.4.0", "@unified-latex/unified-latex-util-parse": "^1.4.0", "unified": "^10.1.2" } }, "sha512-q6cVBkEYAX+N8wgA5pmyw4I1URm12X0JDpBEEozo626G8MjJuOfsDeMwuLzodHJPuvsk/LRRtANxpqK8JwpiXQ=="], + "tex-to-typst": ["tex-to-typst@0.0.21", "", { "dependencies": { "@unified-latex/unified-latex": "^1.4.0", "@unified-latex/unified-latex-util-arguments": "^1.4.0", "@unified-latex/unified-latex-util-parse": "^1.4.0", "unified": "^10.1.2" } }, "sha512-EgAErpOxUaE0heF/GZ1aQ10QXCEr9Wy7FIMBkgwU5dLzXu1d3/MijvTvc5wJOgeKrx2ptzXz/2GfUDyguD9XlQ=="], "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], diff --git a/packages/myst-to-typst/package.json b/packages/myst-to-typst/package.json index a834c9b036..3862c12935 100644 --- a/packages/myst-to-typst/package.json +++ b/packages/myst-to-typst/package.json @@ -41,7 +41,7 @@ "myst-frontmatter": "^1.10.0", "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", - "tex-to-typst": "^0.0.20", + "tex-to-typst": "^0.0.21", "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", From 4de8d726c6fc1ade6d5cb16a7a136534639497cb Mon Sep 17 00:00:00 2001 From: Jim Madge Date: Tue, 28 Jul 2026 16:22:10 +0100 Subject: [PATCH 13/24] Bump actions and node (#3015) --- .changeset/neat-shirts-attend.md | 5 +++++ packages/myst-cli/src/init/gh-actions/index.ts | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 .changeset/neat-shirts-attend.md diff --git a/.changeset/neat-shirts-attend.md b/.changeset/neat-shirts-attend.md new file mode 100644 index 0000000000..96b22b9014 --- /dev/null +++ b/.changeset/neat-shirts-attend.md @@ -0,0 +1,5 @@ +--- +'myst-cli': patch +--- + +Bump version for GitHub actions and node in GH pages publish action diff --git a/packages/myst-cli/src/init/gh-actions/index.ts b/packages/myst-cli/src/init/gh-actions/index.ts index 4205fa263f..03f0a51312 100644 --- a/packages/myst-cli/src/init/gh-actions/index.ts +++ b/packages/myst-cli/src/init/gh-actions/index.ts @@ -52,23 +52,23 @@ jobs: url: \${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Pages - uses: actions/configure-pages@v3 - - uses: actions/setup-node@v4 + uses: actions/configure-pages@v6 + - uses: actions/setup-node@v7 with: - node-version: 18.x + node-version: 24.x - name: Install ${readableName()} run: npm install -g ${npmPackageName()} - name: Build HTML Assets run: ${npmBinaryName()} build --html - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: './_build/html' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 `; } From 4d55c80a67b4a208184a9cea77f9c10518fe6ba7 Mon Sep 17 00:00:00 2001 From: Fernando Basso Date: Fri, 31 Jul 2026 17:15:18 -0300 Subject: [PATCH 14/24] docs: Correctly use complementary instead of complimentary (#3013) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, https://mystmd.org/guide/frontmatter#titles uses the word “complimentary” incorrectly: > You may specify subtitle; this conveys **complimentary** information > to the title and may be displayed below the title. But “complimentary” is a form of praise or admiration for someone or something. The word “complementary” should be used here, which means subtitle adds, gives extra information and “complements” (not compliments) the title. --- docs/frontmatter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontmatter.md b/docs/frontmatter.md index 89433682cd..290fd51433 100644 --- a/docs/frontmatter.md +++ b/docs/frontmatter.md @@ -189,7 +189,7 @@ project: ## Titles -There are several fields to title MyST projects and pages. Primary page and project titles can be specified simply as `title`. Pages and projects also both have `short_title`; this should provide a summarized title in less than 40 characters. It is used where space is limited, for example a site navigation panel, running-head titles in an static export, etc. You may specify `subtitle`; this conveys complimentary information to the title and may be displayed below the title. +There are several fields to title MyST projects and pages. Primary page and project titles can be specified simply as `title`. Pages and projects also both have `short_title`; this should provide a summarized title in less than 40 characters. It is used where space is limited, for example a site navigation panel, running-head titles in an static export, etc. You may specify `subtitle`; this conveys complementary information to the title and may be displayed below the title. ````{note} Defining Page Title in Markdown From 105739aa12eff10b5f4de044e627756b3fe0b2f1 Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Sat, 1 Aug 2026 15:08:41 +0700 Subject: [PATCH 15/24] Authors field in pixi.toml should be list of strings (#3017) * Authors field in pixi.toml should be list of strings * Strip whitespace --- docs/deployment-gitlab.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/deployment-gitlab.md b/docs/deployment-gitlab.md index c05010ec6e..d6dc3e1945 100644 --- a/docs/deployment-gitlab.md +++ b/docs/deployment-gitlab.md @@ -59,7 +59,7 @@ pages: only: - main ``` -```` +```` ````{tab-item} Poetry based ```{code} yaml :filename: .gitlab-ci.yml @@ -137,7 +137,7 @@ A minimal version is shown below. :filename: pixi.toml [workspace] -authors = [{name = "Me", email = "me@me.com"}] +authors = ["Me "] channels = ["conda-forge"] name = "jbtest" platforms = ["win-64", "linux-64"] @@ -153,7 +153,7 @@ jupyter-book = ">=2.1.2,<3" ## External server through GitLab CI/CD -Another option is to deploy your MyST site to an external server through GitLab CI/CD. A main difference is setting 'variables' (settings $\rightarrow$ CI/CD $\rightarrow$ variables) for connecting to the server. +Another option is to deploy your MyST site to an external server through GitLab CI/CD. A main difference is setting 'variables' (settings $\rightarrow$ CI/CD $\rightarrow$ variables) for connecting to the server. An example `.gitlab-ci.yml` file for this deployment method is shown below. @@ -171,12 +171,12 @@ variables: LOCAL_BUILD_DIR: "_build/html" HOST: "127.0.0.1" BASE_URL: "" - + before_script: - apt-get update - apt-get install -y --no-install-recommends curl rsync openssh-client git - # Node.js + # Node.js - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - - apt-get install -y --no-install-recommends nodejs - node --version @@ -186,7 +186,7 @@ before_script: - python -m pip install --upgrade pip - pip install mystmd - # Load SSH key + # Load SSH key - eval "$(ssh-agent -s)" - chmod 400 "$WEBSITE_UPLOAD_KEY" - ssh-add "$WEBSITE_UPLOAD_KEY" @@ -195,10 +195,10 @@ deploy: stage: deploy script: # Build (important: define the project path) - - myst build --html - + - myst build --html + - rsync -ravz "${LOCAL_BUILD_DIR}/" -e "${SSH_COMMAND} -i ${WEBSITE_UPLOAD_KEY}" "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/" ``` -Note that this way of deploying requires a gitlab runner. \ No newline at end of file +Note that this way of deploying requires a gitlab runner. From e8045bc692c31626dc040775e41def5d82d62d3e Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Mon, 3 Aug 2026 11:24:44 +0100 Subject: [PATCH 16/24] Change execution session type to console (#3019) * fix: set console type * chore: add changeset --- .changeset/fifty-waves-smile.md | 5 +++++ packages/myst-execute/src/kernel.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/fifty-waves-smile.md diff --git a/.changeset/fifty-waves-smile.md b/.changeset/fifty-waves-smile.md new file mode 100644 index 0000000000..ef6cca11b8 --- /dev/null +++ b/.changeset/fifty-waves-smile.md @@ -0,0 +1,5 @@ +--- +'myst-execute': patch +--- + +Set type to console for execution options diff --git a/packages/myst-execute/src/kernel.ts b/packages/myst-execute/src/kernel.ts index 8460404ba9..91c0682c72 100644 --- a/packages/myst-execute/src/kernel.ts +++ b/packages/myst-execute/src/kernel.ts @@ -28,9 +28,9 @@ export async function createKernelConnection( log?: Logger, ): Promise { const sessionOpts = { - type: 'notebook', path: path.relative(basePath, vfile.path), name: path.basename(vfile.path), + type: 'console', kernel: { name: kernelspec.name, }, From cf9086e4bc346a022eb737cb0578e90e81bc964e Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Tue, 4 Aug 2026 21:20:21 +0100 Subject: [PATCH 17/24] Add public manifest to site builds (#3002) * feat: add public manifest * chore: add changeset * docs: add note about public.json --- .changeset/grumpy-lions-push.md | 5 +++++ docs/theme-developer.md | 1 + packages/myst-cli/src/build/html/index.ts | 1 + packages/myst-cli/src/build/site/start.ts | 1 + packages/myst-cli/src/process/site.ts | 17 ++++++++++++++++- 5 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changeset/grumpy-lions-push.md diff --git a/.changeset/grumpy-lions-push.md b/.changeset/grumpy-lions-push.md new file mode 100644 index 0000000000..1ce1be7d28 --- /dev/null +++ b/.changeset/grumpy-lions-push.md @@ -0,0 +1,5 @@ +--- +'myst-cli': patch +--- + +Add public.json file manifest diff --git a/docs/theme-developer.md b/docs/theme-developer.md index dd916421e5..54fbc0997e 100644 --- a/docs/theme-developer.md +++ b/docs/theme-developer.md @@ -44,6 +44,7 @@ It then produces `_build/site/config.json`, validates it against your `doc`/`opt - Reference and search files at `/objects.inv`, `/myst.xref.json`, `/myst.search.json`. - Static assets from `/` and a `/socket` websocket for `LOG` and `RELOAD` events. Your start script receives `HOST`, `CONTENT_CDN_PORT`, `PORT`, `MODE` (set to `static` for `myst build --html`), and optional `BASE_URL`; any framework or server is fine as long as it reads these and fetches content from the endpoints above. +- An index of known static files is included in `public.json`, to enable the theme to retrieve _all_ static files without access to the file-system of the content server. ## Building HTML diff --git a/packages/myst-cli/src/build/html/index.ts b/packages/myst-cli/src/build/html/index.ts index 5446853c20..01126785cb 100644 --- a/packages/myst-cli/src/build/html/index.ts +++ b/packages/myst-cli/src/build/html/index.ts @@ -234,6 +234,7 @@ export async function buildHtml(session: ISession, opts: StartOptions) { copyStaticFiles(session, projectConfig?.static_files ?? [], htmlDir, proj.path); } fs.copySync(path.join(session.sitePath(), 'config.json'), path.join(htmlDir, 'config.json')); + fs.copySync(path.join(session.sitePath(), 'public.json'), path.join(htmlDir, 'public.json')); fs.copySync(path.join(session.sitePath(), 'objects.inv'), path.join(htmlDir, 'objects.inv')); // NOTE: HTML static output needs to patch the contents, this is done on the fly by the server diff --git a/packages/myst-cli/src/build/site/start.ts b/packages/myst-cli/src/build/site/start.ts index 124b6c4d3a..c521f42e6c 100644 --- a/packages/myst-cli/src/build/site/start.ts +++ b/packages/myst-cli/src/build/site/start.ts @@ -83,6 +83,7 @@ export async function startContentServer(session: ISession, opts?: ServerOptions app.use('/', express.static(session.publicPath())); app.use('/content', express.static(session.contentPath())); app.use('/config.json', express.static(join(session.sitePath(), 'config.json'))); + app.use('/public.json', express.static(join(session.sitePath(), 'public.json'))); app.use('/objects.inv', express.static(join(session.sitePath(), 'objects.inv'))); app.use('/myst.xref.json', express.static(join(session.sitePath(), 'myst.xref.json'))); app.use('/myst.search.json', express.static(join(session.sitePath(), 'myst.search.json'))); diff --git a/packages/myst-cli/src/process/site.ts b/packages/myst-cli/src/process/site.ts index 3867708db1..e8a26d6548 100644 --- a/packages/myst-cli/src/process/site.ts +++ b/packages/myst-cli/src/process/site.ts @@ -1,5 +1,5 @@ import yaml from 'js-yaml'; -import { basename, extname, join } from 'node:path'; +import { basename, extname, join, relative } from 'node:path'; import fs from 'fs-extra'; import chalk from 'chalk'; import { Inventory, Domains } from 'intersphinx'; @@ -49,6 +49,7 @@ import { finalizeMdast, postProcessMdast, transformMdast } from './mdast.js'; import { toSectionedParts, buildHierarchy, sectionToHeadingLevel } from './search.js'; import { SPEC_VERSION } from '../spec-version.js'; import { cpus } from 'node:os'; +import { opendir } from 'node:fs/promises'; const WEB_IMAGE_EXTENSIONS = [ ImageExtensions.mp4, @@ -162,6 +163,19 @@ export async function writeMystXRefJson(session: ISession, states: ReferenceStat writeFileToFolder(filename, JSON.stringify(mystXRefs)); } +export async function writePublicJson(session: ISession) { + const paths = []; + const publicPath = session.publicPath(); + const dir = await opendir(publicPath, { recursive: true }); + for await (const path of dir) { + const subPath = relative(publicPath, join(path.parentPath, path.name)); + paths.push(`/${subPath}`); + } + + const filename = join(session.sitePath(), 'public.json'); + writeFileToFolder(filename, JSON.stringify(paths)); +} + export async function writeMystSearchJson(session: ISession, pages: LocalProjectPage[]) { const records = [...pages] // Ensure deterministic ordering @@ -741,6 +755,7 @@ export async function processSite(session: ISession, opts?: ProcessSiteOptions): await writeMystXRefJson(session, states); // Search does not include parts await writeMystSearchJson(session, allPages); + await writePublicJson(session); } return true; } From 34fce1297d03ccff20d6ab873e9030414de05553 Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Tue, 4 Aug 2026 21:22:47 +0100 Subject: [PATCH 18/24] Add a brief guide about AST structure and metadata (#2865) * Add a brief guide to the AST structure * Apply suggestions from code review Co-authored-by: Rowan Cockett * Move parts docs to myst guide and clarify * Address a few more comments --------- Co-authored-by: Rowan Cockett --- docs/developer.md | 4 + docs/document-parts.md | 77 ++++++++++++ docs/glossary.md | 4 +- docs/javascript-plugins.md | 1 + docs/overview.md | 2 + docs/plugins-ast.md | 4 + docs/theme-developer.md | 2 + packages/myst-spec/docs/ast-primer.md | 171 ++++++++++++++++++++++++++ packages/myst-spec/docs/myst.yml | 5 +- 9 files changed, 267 insertions(+), 3 deletions(-) create mode 100644 packages/myst-spec/docs/ast-primer.md diff --git a/docs/developer.md b/docs/developer.md index 4c7fbf4fea..988278d333 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -23,6 +23,10 @@ However, for a developer it is important to understand that `mystmd` consists of 1. An engine that converts input documents into an AST (abstract syntax tree). 2. A renderer, that converts that AST into a given output format. +:::{seealso} Learn more about the AST +If the AST is unfamiliar, start with the [AST primer](xref:spec/ast-primer) for a short introduction to its structure. +::: + This model is equally applicable to exporting/converting static documents and rich web applications. Here's a workflow for static documents: ```{mermaid} diff --git a/docs/document-parts.md b/docs/document-parts.md index f22d85dbbe..ab0671101f 100644 --- a/docs/document-parts.md +++ b/docs/document-parts.md @@ -228,3 +228,80 @@ site: :::{seealso} Referring to parts directly with a URL is coming See the [issue tracking this enhancement](https://github.com/jupyter-book/mystmd/issues/2127). ::: + + +(parts-ast)= +## How parts appear in the AST + +Each way of defining a part lands in a different place in the JSON that `myst build` emits. +This section describes how these are represented in the AST. +See [](xref:spec) for more details about the MyST AST. + +### Page-level parts in the page AST + +There are three ways to mark a region as a named part in a document. +Each one ends up in a different place in the served JSON, even though they are often treated the same in the rendered output (see [](#ast-parse-helper) for guidance in parsing them). +We show each method below, and where it lands in the AST: + +````{list-table} +- - Method + - Example + - Location in AST +- - **Page frontmatter** (in the `parts:` key) + - ```markdown + --- + parts: + acknowledgments: "..." + abstract: "My abstract" + --- + My content... + ``` + - Stored in the page frontmatter key: + + `frontmatter.parts..mdast`. +- - **Block metadata** (within content of a page) + - ```markdown + +++ {"part": "abstract"} + + My abstract content. + + +++ + + My content + + +++ {"part": "acknowledgements"} + Thanks to ... + ``` + - Stored in a `block` node inside `mdast` with `data.part` set. + + `.mdast.children..data.part = "acknowledgements"` +- - **Heading auto-match** (heading text equals a known part name) + - ```markdown + ## Abstract + + My abstract ... + + ## Header one + + A normal (non-part) header + + ## Acknowledgments + + Thanks to ... + ``` + - Remains a regular `heading` + paragraphs sequence. + Only becomes a part block when [`extractPart()`](#ast-parse-helper) is called. +```` + +### Site-level parts in the project AST + +Site-level `parts:` (under `site:` in `myst.yml`) is a different `mystmd`-specific system for content shared across pages (like a project footer or header). +These are project-level and not part of any page's AST. +The [theme developer guide](./theme-developer.md) covers the rest of what mystmd serves alongside per-page JSON. + +(ast-parse-helper)= +### Programmatically extract a part from an AST + +Instead of manually parsing the page AST for these parts, use the [`extractPart()`](https://github.com/jupyter-book/mystmd/blob/0d626b198093fa740125d02267a111d84547fd36/packages/myst-common/src/extractParts.ts) helper to retrieve a named part regardless of how it was defined. + +For example, exporters like [`myst-to-jats`](https://github.com/jupyter-book/mystmd/blob/0d626b198093fa740125d02267a111d84547fd36/packages/myst-to-jats/src/index.ts#L720-L729) call `extractPart()` to pull these regions into their output. diff --git a/docs/glossary.md b/docs/glossary.md index c7a1b5a0a4..3502f0c3e1 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -71,10 +71,10 @@ MyST Document Engine [^myst]: Often we focus upon a MyST Engine's ability to parse {term}`MyST Markdown`. However, a MyST Engine may consume _any_ kind of input markup as long as the _result_ is a {term}`MyST AST` that follows the {term}`MyST Specification`. This is why the [official MyST Document Engine](https://mystmd.org) can parse other kinds of markup, such as a subset of LaTeX. MyST Specification -: A specification that describes the structure and function of the MysT AST. It provides a framework for defining all of the kinds of content and metadata that a MyST document can contain. For example, the MyST specification defines how sections can contain paragraphs, how paragraphs can contain sentences, and how sentences can contain a "bolded" chunk of text. The specification is [defined at mystmd.org/spec](xref:spec/#overview). +: A specification that describes the structure and function of the MysT AST. It provides a framework for defining all of the kinds of content and metadata that a MyST document can contain. For example, the MyST specification defines how sections can contain paragraphs, how paragraphs can contain sentences, and how sentences can contain a "bolded" chunk of text. The specification is [defined at mystmd.org/spec](xref:spec#overview). MyST AST -: A structured representation of a MyST Document, typically built from a markup language (like MyST Markdown) by a {term}`MyST Document Engine`, that adheres to the {term}`MyST Specification`. Usually, a MyST [Abstract Syntax Tree (AST)](wiki:Abstract_syntax_tree) is represented as a JSON data structure, with metadata attached to each piece of content that describes its role, relationships with other content, etc. The AST allows us to separate the steps of parsing content (such as {term}`MyST Markdown`) from generating output like HTML, PDF, docx, etc. +: A structured representation of a MyST Document, typically built from a markup language (like MyST Markdown) by a {term}`MyST Document Engine`, that adheres to the {term}`MyST Specification`. Usually, a MyST [Abstract Syntax Tree (AST)](wiki:Abstract_syntax_tree) is represented as a JSON data structure, with metadata attached to each piece of content that describes its role, relationships with other content, etc. The AST allows us to separate the steps of parsing content (such as {term}`MyST Markdown`) from generating output like HTML, PDF, docx, etc. For an introduction to its structure, see the [AST primer](xref:spec/ast-primer). AST node : A single node in the {term}`MyST AST`. It has an explicit `type` and contains 0 or more children. diff --git a/docs/javascript-plugins.md b/docs/javascript-plugins.md index e0428feed4..e686bd6e5c 100644 --- a/docs/javascript-plugins.md +++ b/docs/javascript-plugins.md @@ -5,6 +5,7 @@ description: Plugins implemented in JavaScript are easily used across different JavaScript plugins are native MyST plugins, which are loaded as modules into the MyST engine. Transforms defined in these modules have access to helpful AST manipulation routines made available by MyST. Edits to JavaScript plugins have no effect during execution of a MyST build, instead the build must be restarted. +(defining-a-new-directive)= ## Defining a new directive To create a plugin, you will need a single JavaScript file[^esm] that exports one or more of the objects above. For example, a simple directive that pulls a random image from [Unsplash](https://picsum.photos/) can be created with a single file that exports an `picsum` directive. diff --git a/docs/overview.md b/docs/overview.md index 7954c7e3da..75ea64f9b2 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -76,6 +76,8 @@ Here's an example of MyST content, and the same content parsed as MyST AST: The easiest way to understand how {term}`MyST Markdown` gets converted to {term}`MyST AST` is to [explore the MyST Sandbox](https://mystmd.org/sandbox). Click the {kbd}`AST` tab to see the underlying AST structure for anything that you type. The {kbd}`PRE` tab represents the initial parsing phase, and the {kbd}`POST` tab represents the AST after the resolving phase. +If you're building your own renderer or parser, start with the [AST primer](xref:spec/ast-primer). It walks through the node structure, the PRE/POST distinction, and what the spec formalizes vs. what's implementation-specific. + (overview-transformers)= ## How do MyST Transformers work? diff --git a/docs/plugins-ast.md b/docs/plugins-ast.md index 6ebafa74ca..c0c6d21954 100644 --- a/docs/plugins-ast.md +++ b/docs/plugins-ast.md @@ -5,6 +5,10 @@ title: Generate MyST AST with Plugins A common use case with plugins involves generating {term}`MyST AST` and inserting it into the document. This page covers a few ways that you can do so. +:::{tip} New to the MyST AST? +Start with the [AST primer](xref:spec/ast-primer) for an overview of how MyST documents are structured at a high- and low-level. +::: + (plugins:ctx)= ## Parse MyST markdown to AST in a directive or role diff --git a/docs/theme-developer.md b/docs/theme-developer.md index 54fbc0997e..03202d4527 100644 --- a/docs/theme-developer.md +++ b/docs/theme-developer.md @@ -21,6 +21,8 @@ Specifically: - When you run `myst start`, the engine builds `_build/site` (content JSON, config, assets), serves it on `CONTENT_CDN_PORT`, and launches your theme with the template’s `build.start` command. - A theme should read `_build/site/config.json` first, fetch page JSON from `/content/{slug}.json`, and serve the app on `PORT` (Remix/Vite/Next-style). +Each page JSON contains an `mdast` field holding the resolved AST that the theme then renders - see the [AST primer](xref:spec/ast-primer) for its structure. + Live reloads are facilitated by a websocket endpoint at `/socket`, which processes JSON messages. Static assets, and template options with type `file` are copied into `_build/site/public` and served from `/`. ## Theme metadata (`template.yml`) diff --git a/packages/myst-spec/docs/ast-primer.md b/packages/myst-spec/docs/ast-primer.md new file mode 100644 index 0000000000..47e57133c3 --- /dev/null +++ b/packages/myst-spec/docs/ast-primer.md @@ -0,0 +1,171 @@ +--- +title: A primer on the MyST AST +short_title: AST primer +description: A getting-started guide to the structure of the MyST abstract syntax tree, for renderer and parser authors. +--- + +The MyST Abstract Syntax Tree (AST) is the structured representation of a MyST document after parsing. +It is plain JSON, consumed by tools that render the document (to HTML, LaTeX, Typst, etc). +This page is a brief overview of the structure and how the pieces fit together. + +:::{seealso} The MyST User Guide is for users of the CLI +If you just want to author MyST documents, you probably don't need this page - head to the [MyST Guide](xref:guide). +If you want to build a renderer, write a parser, or generate AST from a plugin, read on. +::: + +:::{seealso} The MyST Sandbox is a great place to play around with AST +The [MyST Sandbox](https://mystmd.org/sandbox) is an interactive place to write your own MyST and see what AST happens as a result. +::: + +## The spec and mystmd + +This primer covers both **the MyST Specification** (this site) and **mystmd** (the official reference implementation): + +- The **spec** defines what a conforming MyST parser must produce - node types, their properties, and how they correspond to source. It evolves through the [MyST Enhancement Proposal process](https://mep.mystmd.org). +- **mystmd** is a command-line application that bundles several modular packages that make use of MyST. `myst-parser` is a parser for this spec, `myst-transforms` have a collection of useful transforms to modify a MyST AST (e.g., resolving cross-references and appling numbering numbering). `mystmd` may emits additional node types that aren't yet in the spec as part of its build pipeline (like [named parts](xref:guide#parts-ast)). + +If you're writing a parser, stick to the spec. mystmd's conventions (like specific named parts) aren't part of the spec, but they're still useful to know, since most MyST content today comes from mystmd. This page flags which is which as we go. + +## The shape of a node + +Every node in the AST is a JSON object with a `type` field that names what kind of node it is. +Beyond `type`, a node has either a `value` or `children`: + +- **Leaf nodes** typically have a `value` (typically a string). +- **Parent nodes** have a `children` array (more nodes). + +Nodes may also carry additional properties - `url` on a link, `depth` on a heading, `identifier` on a cross-reference, etc. +These properties are specific to each node type and are documented in the [node reference](./myst.schema.md). + +The **interactive demo** below lets you explore the AST for simple MyST text: a single line of text. Change the text to see the results, and click each box to see a different representation of the data, and stage in the processing pipeline (`PRE` or `POST` transforms). + +```{myst} +Hello, world! +``` + +Click the **AST** tab above to see the AST. (The tab displays YAML for readability; MyST sites serve it as JSON.) +You'll find a `root` node (always the outermost node) wrapping a `paragraph`, which wraps a `text` leaf. + +## Inline formatting nests parent nodes + +Inline formatting introduces parent nodes around the text they decorate. + +```{myst} +A paragraph with **bold** and *emphasis*. +``` + +In the AST tab, the paragraph's `children` is a flat sequence of `text`, `strong`, and `emphasis` nodes. Each `strong` and `emphasis` wraps its own `text` node. +The markup characters (`**`, `*`) are gone from the source text. They've been turned into nested nodes (e.g. `*word*` becomes a `type: emphasis` node wrapping a `text` node). + +(pre-and-post)= +## Two processing stages: PRE and POST transforms + +In the `myst-cli`, a MyST document is processed through a series of transformations, and the AST has a different shape after each one. +The short-hand for this in MyST documentation is `PRE`- and `POST`-transforms. You can get very granular by applying various [`myst-transforms`](https://npmjs.com/package/myst-transforms) in your own libraries or packages. + +1. **PRE**: the output of *parsing*. The parser walks the source and expands directives and roles by calling their `run` functions. Cross-references still look like ordinary `link` nodes (e.g. `url: "#some-label"`). Directives appear as `mystDirective` wrapper nodes containing their expanded children. Numbering has not yet been applied. +2. **POST**: the output after [transforms](xref:guide#develop:transforms) run over the PRE tree. Link nodes pointing at labels become `crossReference` nodes with resolved targets. Most directive wrappers are unwrapped, leaving only their semantic children. Numbering and enumeration are applied. This is the form most renderers consume. + +You can see both stages inline: the `{myst}` directive used on this page exposes a **PRE** / **POST** toggle inside its AST tab, and so does the [MyST sandbox](https://mystmd.org/sandbox). Try it on this cross-reference: + +```{myst} +(my-label)= +**A labeled paragraph.** + +See [this paragraph](#my-label) for context. +``` + +In **PRE**, the second paragraph contains an ordinary `link` node with `url: "#my-label"`. +In **POST**, that same node has become a `crossReference` with `identifier`, `kind`, and `resolved: true` properties. + +### Which stage will you encounter? + +- The **spec's [test cases](https://unpkg.com/browse/myst-spec/dist/)** describe the **PRE** form. They are the canonical "source -> AST" pairs that any conforming parser must produce. +- The [node reference](./myst.schema.md) documents node *types* that may appear in either stage. Some properties (like `resolved` on `crossReference`, or computed numbering fields) appear only after transforms. + +In short: + +- A parser produces PRE. Match the test cases. +- A renderer receives POST. It's what `myst build` emits. +- A transform pipeline applies a series of transformations that change the tree from `PRE` to `POST` + +## Directives in PRE vs. POST + +Directives change shape between PRE and POST (roles behave similarly): + +```{myst} +:::{note} +Heads up! +::: +``` + +In **PRE**, you'll see a `mystDirective` wrapper (name: `note`) containing an `admonition` child. The wrapper records the directive that produced it. The child is what its `run` function returned from a `mystmd` plugin. + +In **POST**, the `mystDirective` wrapper is typically stripped, and transforms may add extra structure. For example, [`admonitionHeadersTransform`](https://github.com/jupyter-book/mystmd/blob/0d626b198093fa740125d02267a111d84547fd36/packages/myst-transforms/src/admonitions.ts#L36) inserts an `admonitionTitle` child. + +When directive parsing fails, the POST AST signals it in one of two ways: + +- **Unknown directive name** (no plugin registered): the node stays as a `mystDirective` leaf, with the original source text in its `value`. +- **Invalid arguments or body**: the node's type becomes `mystDirectiveError`, and its children are dropped. Renderers can use this to display error messages in the output. + +## Plugins often emit a custom node that is transformed + +A common plugin pattern uses a directive (or role) to emit a *placeholder* node, then a separate transform to expand that placeholder into real content. + +For a working in-repo example of this pattern, see [`docs/templates.mjs`](https://github.com/jupyter-book/mystmd/blob/main/docs/templates.mjs) (the `myst:template` directive emits a `myst-template-ref` placeholder, then `mystTemplateTransform` fetches template metadata and expands them). + +This split is useful for anything you can't do inside a directive's [`run()` function](xref:guide#defining-a-new-directive): async work, anything that needs the whole document for context, or anything that depends on cross-references being resolved first. + +## Frontmatter and document structure + +A document's frontmatter (the YAML at the top of a `.md` file) doesn't appear as a node in the tree. +It is attached separately, alongside the `mdast` (the AST) in the JSON output of `myst build`. +A built page on disk looks roughly like: + +```{code} json +:caption: A page from `myst build --site` +{ + "kind": "Article", + "frontmatter": { "title": "...", "authors": [...] }, + "mdast": { "type": "root", "children": [ ... ] }, + "references": { ... } +} +``` + +The `mdast` field is the tree you've been inspecting in the AST tabs above. +The `references` field holds cross-document data like resolved cross-references and citations, which a renderer needs when working across pages. + +## Chunking a page into blocks + +The `+++` syntax divides a page into `block` nodes. Each `block` wraps the content that follows it, and optional JSON after `+++` becomes block metadata: + +```{myst} ++++ {"label": "intro", "foo": "bar"} + +A first chunk. + ++++ + +A second chunk. +``` + +In the AST (POST), each chunk ends up inside a `block` node. Recognized keys (`kind`, `class`, `label`, `identifier`) are moved onto the `block` node, anything else is put under `block.data`. See [](./features/blocks.md) for the formal block specification. + +## "Parts": named regions of a page or project + +Plugins and themes often need to pull out a specific region of a page (the abstract, the acknowledgments, etc.). MyST has "parts" for this. +Parts aren't part of the `myst-specification`, they are a concept used by the [MyST Document Engine](xref:guide) to structure pages and projects. +See [](xref:guide#parts-ast) for details. + +## How the MyST AST and its upstream foundation (`mdast`) relate + +The MyST AST extends [`mdast`](https://github.com/syntax-tree/mdast), the Markdown AST used widely in the JavaScript [unified](https://unifiedjs.com/) ecosystem, which in turn builds on [`unist`](https://github.com/syntax-tree/unist) (Universal Syntax Tree). +If you've used `remark` or `rehype`, the shape will feel familiar. +MyST adds node types for directives, roles, cross-references, citations, admonitions, equations, and other constructs that don't exist in plain Markdown. + +## Where to go next + +- The [MyST sandbox](https://mystmd.org/sandbox) parses any MyST input you type and shows the AST in PRE and POST stages. +- The [AST Node index](./myst.schema.md) describes the AST of all nodes defined in the specification. +- The [test cases](https://unpkg.com/browse/myst-spec/dist/) provide source -> expected (PRE) AST pairs. +- [Generate MyST AST with Plugins](xref:guide/plugins-ast) in the MyST Guide shows a few ways to generate AST from a plugin. diff --git a/packages/myst-spec/docs/myst.yml b/packages/myst-spec/docs/myst.yml index 565ee73364..501bc1264a 100644 --- a/packages/myst-spec/docs/myst.yml +++ b/packages/myst-spec/docs/myst.yml @@ -8,10 +8,14 @@ project: subject: Specification venue: title: MyST Spec + references: + guide: https://mystmd.org/guide toc: # Auto-generated by `myst init --write-toc` - file: index.md - file: features/overview.md + - file: ast-primer.md + - file: myst.schema.md - file: features/commonmark.md - file: features/admonitions.md - file: features/figures.md @@ -20,7 +24,6 @@ project: - file: features/references.md - file: features/footnotes.md - file: features/blocks.md - - file: myst.schema.md site: title: MyST Spec actions: From 4f12f1bfb81b07f1ad1079d98f8b5c7b06123cde Mon Sep 17 00:00:00 2001 From: Stefan van der Walt Date: Wed, 5 Aug 2026 03:25:09 +0700 Subject: [PATCH 19/24] Decode HTML entities in CSL text fields (#2986) CrossRef's CSL-JSON metadata sometimes contains HTML entities in title and journal name fields. Co-authored-by: Angus Hollands --- .changeset/silly-phones-warn.md | 5 +++++ packages/citation-js-utils/package.json | 2 ++ packages/citation-js-utils/src/index.ts | 6 ++++-- .../citation-js-utils/tests/basic.spec.ts | 20 +++++++++++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 .changeset/silly-phones-warn.md diff --git a/.changeset/silly-phones-warn.md b/.changeset/silly-phones-warn.md new file mode 100644 index 0000000000..afa8bbb6ae --- /dev/null +++ b/.changeset/silly-phones-warn.md @@ -0,0 +1,5 @@ +--- +'citation-js-utils': patch +--- + +Decode HTML entities in CrossRef citation data diff --git a/packages/citation-js-utils/package.json b/packages/citation-js-utils/package.json index c9e077bf13..086b5b2076 100644 --- a/packages/citation-js-utils/package.json +++ b/packages/citation-js-utils/package.json @@ -38,10 +38,12 @@ "@citation-js/core": "^0.7.18", "@citation-js/plugin-bibtex": "^0.7.18", "@citation-js/plugin-csl": "^0.7.18", + "he": "^1.2.0", "doi-utils": "^2.0.5", "sanitize-html": "^2.7.0" }, "devDependencies": { + "@types/he": "^1.2.0", "@types/sanitize-html": "^2.6.2" } } diff --git a/packages/citation-js-utils/src/index.ts b/packages/citation-js-utils/src/index.ts index 6c3abc080f..6f2b5af590 100644 --- a/packages/citation-js-utils/src/index.ts +++ b/packages/citation-js-utils/src/index.ts @@ -2,6 +2,7 @@ import { Cite, plugins } from '@citation-js/core'; import { doi as doiUtils } from 'doi-utils'; import { clean as cleanCSL } from '@citation-js/core/lib/plugins/input/csl.js'; import sanitizeHtml from 'sanitize-html'; +import he from 'he'; import '@citation-js/plugin-bibtex'; import '@citation-js/plugin-csl'; @@ -267,7 +268,8 @@ export function getCitationRenderers(data: CSL[]): CitationRenderer { if (!c.id) { c.id = formatLabel(c); } - // Trim the titles, DOIs, etc. on load + // Trim the titles, DOIs, etc. on load; decode HTML entities from + // CrossRef metadata. [ 'title', 'note', @@ -279,7 +281,7 @@ export function getCitationRenderers(data: CSL[]): CitationRenderer { 'DOI', 'ISSN', ].forEach((tag) => { - if (c[tag] && typeof c[tag] === 'string') c[tag] = c[tag].trim(); + if (c[tag] && typeof c[tag] === 'string') c[tag] = he.decode(c[tag].trim()); }); // Trim the DOIs and URLs (these are encoded) on load if (c.URL) c.URL = c.URL.replace(/^(%20)*/, '').replace(/(%20)*$/, ''); diff --git a/packages/citation-js-utils/tests/basic.spec.ts b/packages/citation-js-utils/tests/basic.spec.ts index ea75c89215..154255d0df 100644 --- a/packages/citation-js-utils/tests/basic.spec.ts +++ b/packages/citation-js-utils/tests/basic.spec.ts @@ -79,6 +79,26 @@ describe('yearFromCitation', () => { }); }); +describe('HTML entity decoding in CSL fields', () => { + it('decodes HTML entities in title and container-title from CrossRef CSL-JSON', () => { + const csl = [ + { + id: 'test2024', + type: 'article-journal', + title: 'Signal & Image Processing & More', + 'container-title': 'Computers & Geosciences', + author: [{ family: 'Smith', given: 'J.' }], + issued: { 'date-parts': [[2024]] }, + DOI: '10.1234/test', + }, + ]; + const renderers = getCitationRenderers(csl); + const cite = renderers['test2024']; + expect(cite.cite.title).toEqual('Signal & Image Processing & More'); + expect(cite.cite['container-title']).toEqual('Computers & Geosciences'); + }); +}); + describe('firstNonDoiUrl', () => { it('no url returns undefined', async () => { expect(firstNonDoiUrl('my citation', 'abc123')).toEqual(undefined); From 891bda7a78a2c3827f7f1d3d76686dc5108cc77e Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Tue, 4 Aug 2026 22:40:32 +0200 Subject: [PATCH 20/24] Properly discard glob entries that are explicitly enumerated in ToC (#2978) * bugfix for issue #2973 when pattern matching and avoiding previously mentioned entries there's a need to resolve new items when searching in `ignore` because it contains resolved entries * changeset * Apply suggestion from @agoose77 --------- Co-authored-by: Angus Hollands --- .changeset/eighty-dots-train.md | 5 +++++ packages/myst-cli/src/project/fromTOC.ts | 3 ++- .../myst-cli/src/project/toc.pattern.spec.ts | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 .changeset/eighty-dots-train.md diff --git a/.changeset/eighty-dots-train.md b/.changeset/eighty-dots-train.md new file mode 100644 index 0000000000..169bdc7cbd --- /dev/null +++ b/.changeset/eighty-dots-train.md @@ -0,0 +1,5 @@ +--- +'myst-cli': patch +--- + +effectively discard files matched by pattern if already mentioned diff --git a/packages/myst-cli/src/project/fromTOC.ts b/packages/myst-cli/src/project/fromTOC.ts index 6ad01d00d6..dd5bfb75ca 100644 --- a/packages/myst-cli/src/project/fromTOC.ts +++ b/packages/myst-cli/src/project/fromTOC.ts @@ -120,7 +120,8 @@ export function patternsToFileEntries( const { pattern, sort, ...leftover } = entry as PatternEntry; // Glob matches, relative to `path`, ordered naturally let matches = globSync(pattern, { cwd: path, nodir: true, ...opts }) - .filter((item) => !ignore || !ignore.includes(item)) + // ignore contains resolved entries, so we need to search for item resolved + .filter((item) => !ignore || !ignore.includes(resolve(path, item))) .sort(comparePaths); // Reverse order if descending sort is requested if (sort === 'descending') { diff --git a/packages/myst-cli/src/project/toc.pattern.spec.ts b/packages/myst-cli/src/project/toc.pattern.spec.ts index 6d6c7cd142..ecc5b25e68 100644 --- a/packages/myst-cli/src/project/toc.pattern.spec.ts +++ b/packages/myst-cli/src/project/toc.pattern.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it, beforeEach, vi } from 'vitest'; +import { resolve } from 'node:path'; import memfs from 'memfs'; import { Session } from '../session'; import { listExplicitFiles, patternsToFileEntries } from './fromTOC'; @@ -141,6 +142,21 @@ describe('patternsToFileEntries', () => { { file: 'meetings/2026-01-01.md', implicit: true }, ]); }); + it('explicitly listed files are excluded from subsequent glob patterns', () => { + memfs.vol.fromJSON({ + 'index.md': '', + 'test1.md': '', + 'test2.md': '', + }); + // Simulate ignore list built from explicit file entries (absolute paths, as listExplicitFiles returns) + const cwd = '.'; + const ignore = [resolve(cwd, 'index.md'), resolve(cwd, 'test1.md')]; + expect( + patternsToFileEntries(session, [{ pattern: '*.md' }], cwd, ignore, '/tmp/warn.txt', { + fs: memfs, + }), + ).toEqual([{ file: 'test2.md', implicit: true }]); + }); }); describe('listExplicitFiles', () => { From 1ad857c61d787bfcfa02da2af88cff7a93c214e6 Mon Sep 17 00:00:00 2001 From: Yehor Korotenko <60360745+DobbiKov@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:45:49 +0300 Subject: [PATCH 21/24] docs: improved GitLab deployment docs (#2966) * docs: divided gitlab deployment into two sections (pixi + poetry) and improved the tutorial for gitlab deployment with pixi * added section about deployment without technical details * added section for beginners * added info about where to find link to deployed gitlab pages * small edits to script and description --------- Co-authored-by: FreekPols Co-authored-by: Angus Hollands --- docs/deployment-gitlab.md | 172 ++++++++++++++++++++++++++++++----- docs/images/gitlab-pages.png | Bin 0 -> 156012 bytes 2 files changed, 148 insertions(+), 24 deletions(-) create mode 100644 docs/images/gitlab-pages.png diff --git a/docs/deployment-gitlab.md b/docs/deployment-gitlab.md index d6dc3e1945..f69025c096 100644 --- a/docs/deployment-gitlab.md +++ b/docs/deployment-gitlab.md @@ -9,41 +9,56 @@ This page has important information for how to do so. ## Instructions +:::{warning} +Because of your institution’s GitLab configuration, the descriptions below may differ from the actual deployment on GitLab Pages. + +If you are new to GitLab or you are not familiar with `git`, please proceed to [](#instructions-for-beginners) +::: + To get setup with GitLab Pages, ensure that your repository is hosted in GitLab and you are in the root of the Git repository. -Create a file called `.gitlab-ci.yml` with the following content: +### Deployment with uv +Create `.gitlab-ci.yml` file in the root of your project with the content provided below: -`````{tab-set} -````{tab-item} Pixi based ```{code} yaml :filename: .gitlab-ci.yml +:linenos: +:emphasize-lines: 47 - -image: ghcr.io/prefix-dev/pixi:latest +image: ghcr.io/astral-sh/uv:debian-slim stages: - build - deploy variables: - PIXI_CACHE_DIR: "$CI_PROJECT_DIR/.pixi" HOST: "127.0.0.1" cache: paths: - - .pixi + - .venv before_script: - - pixi --version + - uv --version build: stage: build script: - # install environment from pixi.toml + pixi.lock - - pixi install --locked - - # run jupyter-book via pixi environment - - pixi run jupyter-book build --html + # initialize uv project and install jupyter-book + - uv init + - uv add "jupyter-book>=2.1.2,<3" + + # install node + - apt-get update + - apt-get install -y curl + - apt-get install -y procps + - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + - apt-get install -y nodejs + + # run jupyter-book via uv + - uv run jupyter-book clean --all -y + - export BASE_URL="" + - uv run jupyter-book build --html artifacts: paths: - _build/html @@ -51,16 +66,27 @@ build: pages: stage: deploy script: - - mkdir public - - cp -r _build/html/* public/ + - mv _build/html/ public artifacts: paths: - public only: - - main + - main # replace it with YOUR branch name +``` + +then push this file to your GitLab repo. + +> If your Git branch is different from `main`, you should replace main in the `.gitlab-ci.yml` file on the highlighted line with your branch name. + +Once everything done, you should see `GitLab Pages` link in the right menu as shown below: +```{figure} ./images/gitlab-pages.png +:height:300px ``` -```` -````{tab-item} Poetry based + +### Deployment with poetry +Create a file called `.gitlab-ci.yml` with the following content: + + ```{code} yaml :filename: .gitlab-ci.yml variables: @@ -123,15 +149,66 @@ pages: tags: - ubuntu ``` -```` -````` +### Deployment with pixi +*Note*: You may not use pixi in your project but you may use it for GitLab deployment. + +1. Make sure that you have [`pixi`](https://pixi.prefix.dev/latest/#installation) installed on your machine. + +2. Create a file called `.gitlab-ci.yml` with the following content: + +```{code} yaml +:filename: .gitlab-ci.yml +:linenos: +:emphasize-lines: 39 + +image: ghcr.io/prefix-dev/pixi:latest + +stages: + - build + - deploy + +variables: + PIXI_CACHE_DIR: "$CI_PROJECT_DIR/.pixi" + HOST: "127.0.0.1" + +cache: + paths: + - .pixi -You must set the `HOST` - this is a fix for a [known issue](https://github.com/jupyter-book/mystmd/issues/2471). +before_script: + - pixi --version -Note that a [pixi.toml](https://pixi.prefix.dev/latest/python/pyproject_toml/) and pixi.lock file should be included! +build: + stage: build + script: + # install environment from pixi.toml + pixi.lock + - pixi install --locked -A minimal version is shown below. + # run jupyter-book via pixi environment + - pixi run jupyter-book build --html + artifacts: + paths: + - _build/html + +pages: + stage: deploy + script: + - mkdir public + - cp -r _build/html/* public/ + artifacts: + paths: + - public + only: + - main # replace it with your branch name! +``` + +> If your Git branch is different from `main`, you should replace main in the `.gitlab-ci.yml` file on the highlighted line with your branch name. + +3. Make sure your pixi project is initialized (you may use `pixi init` CLI command to do so) + +4. Make sure that your `pixi.toml` file contains `linux-64` as a platform and `python` with `jupyter-book` as dependencies. + A minimal version of the `pixi.toml` file is shown below. ```{code-block} toml :filename: pixi.toml @@ -150,6 +227,14 @@ python = ">=3.14.3,<3.15" jupyter-book = ">=2.1.2,<3" ``` +5. Synchronize `pixi.lock` file using `pixi lock` CLI command. + +> Note that a [pixi.toml](https://pixi.prefix.dev/latest/python/pyproject_toml/) and pixi.lock file should be tracked by Git and pushed to the repository with other files. + +6. Add your project files to git using `git add ` (e.g `git add myst.yml main.md pixi.toml pixi.lock`) +7. Commit the changes +8. Push your branch to GitLab (e.g `git push -u origin main`) + ## External server through GitLab CI/CD @@ -174,7 +259,7 @@ variables: before_script: - apt-get update - - apt-get install -y --no-install-recommends curl rsync openssh-client git + - apt-get install -y --no-install-recommends curl rsync openssh-client git procps # Node.js - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - @@ -202,3 +287,42 @@ deploy: ``` Note that this way of deploying requires a gitlab runner. + +## Instructions for beginners +If this is your first time using GitLab, complete these one-time setup steps: +1. [Install Git](https://docs.gitlab.com/topics/git/how_to_install_git/) +2. Configure your git by using the following commands in the terminal + ```shell + git config --global user.name "Your Name" + git config --global user.email "your-mail@your-domain.com" + ``` +3. Create a GitLab account [on official + GitLab](https://gitlab.com/users/sign_in/) or on the GitLab of your + institution. +4. [Set up SSH authentication](https://docs.gitlab.com/user/ssh/) + +Once set up, you have two options to connect your project to GitLab: +1. *Start from GitLab*: [Create a repository on GitLab, clone it to your computer, add your files, and push them](https://docs.gitlab.com/tutorials/make_first_git_commit/#steps) +2. *Start from your computer*: Initialize your local folder as a Git repository and push it to GitLab using the following commands: + ```bash + cd your-project-folder # go to your local folder with your myst project + git init # initialize git + git add . # stage all files + git commit -m "first commit" + git remote add origin git@gitlab.com:username/project.git # you may change this link with the link of your repository + git push -u origin main + ``` + +Once your git project is initialized, create `.gitlab-ci.yml` file in the root +of your myst project and paste the contents described in [](#deployment-with-uv) to the file. + + +Once you saved the file with provided contents, execute the next commands: +```shell +git add .gitlab-ci.yml +git commit -m "added .gitlab-ci.yml to set up GitLab Pages" +git push +``` + +You are all done. You may proceed to the page of your GitLab repository and +open GitLab Pages. diff --git a/docs/images/gitlab-pages.png b/docs/images/gitlab-pages.png new file mode 100644 index 0000000000000000000000000000000000000000..52e1e728d4ea175af625ab3e1ef8548c2feb862a GIT binary patch literal 156012 zcmeFZhg*}+vo}mpP!X_#(u;yfQ)$vcK$`U4MS2ZA5Fm(%2q?Ws2_U@}>4X3(D!qjc z0i=h}0|XKhp71T_=X;*>{sGT*op2?&vpcgp_hxoxXXX zAtBo(A)&Z-iGtX2vjU7FA-SsQq@bX!s-VE8?FF!RadY$tdSIEN0Nmj*JCtXl+?2-A;UVADzvjHbT)QbSYIIzEf^ z!b`cEPsyp<xkhTO3pL|iJis}+kySOvKVA3 z)d{+06um6(sUTBiEA%R2ZL2TNQ(WOxug&!lMFGJ;@Y-&F66U+?BtzPiS7>mo&o*RO64)n}PezYg>Bq?Fy1zlDzmljdpXqH{Fo`0G zz@-a(Ij{Md`$(qbX2)lSsH*g(zrLomWxM(Q_4jK+QXEdz%4D&gIoD2U;xBb6|Gaqh zC`yiWS@z?rdrJE@%-p}?j()-i#kfl>^3P9W-yc_Beo50@ z&4W9WmhA&uKaGpL^Xk@jp)c#-vpU&cu{}<@^P=ttwTIO#W9)0?kV$h7(QWa}U-Tj0 zY@SK}CjBk(IoiXerAbBfVUKDK)dnT)7&jh;T`<&!Q`2??=Sb-POlu zxwc2G2jUC?Wy+Ld_myhV$+VoncRn#MGSiX=7dr&sz5kJTiRo93d8YX+sWv!7zpByb zaoGIrw^P_~vwcPq8E#4n3h?Q-aL9#)9Lk{kOt^1T)xV3Bs9#h4x^TrLH0paYC0UU7 z&~JDV?T1kD>d=!W65s;$sr|WA+qNEFZrF~`m^~W4CS4v5F zkz-OOGU}YWnG~|2>I+wzXxzV#!Kt&tWOf;XH`Ujz?p=zM zdlheckGfm&-g~mN2tIkWcwd!^U&u@3X*hnx!Ml|5nVZ9Bl|gZ`@t6niO7kR&uRDjw zC{I10q*h)UE^*|NxXG`Wnp-yHRd)puF`id6tiJABM-?1%WJ99&{nIC&)z>wT1?a*H zUf%t5)$DchOFQG+h7lrPaY46H-#|6Mr`H`8_-08f7qq2F=@*3dE*`73l2!dg?bRL| zoXX5agm4XAdUql44SlHcrx#uK`Z#Xg?70NJtbBQGp>|ZmU*q15G)0$O${@PL$f?fe z&g9ODPJE|cr{AK&E7?fJ5$>tPAQhc^GCbM(FQ#8iD@&+uu=ys=C5&}(+0uJG5)bEN zr;qRL3hd_YVqWH6roE*Rb^k~74W;Y4tJ;Qoemdz7Qh5+bwEFy|B4ZH@TK$Crg(-Td z;s%6YUu@T%j;pGyWTCi%Qx!&4Cxfi^?icr5VMNW{tX-CU^RqHl~jQ`pj3I zSJ~~k(fB)gk24?VKIZ*7`Vn~t{Txu9)t;qNr3h%B^Mzrse0VlI6TTixjRjJBkV}T$ z2{WOXxjJz7AZF|?O4{&IgYD+$ns$cVcYg0`-g#3485$Ul81FKM-%mzZe{SO7bi`8BxE9JH2GI|PdZM|?L4O|pXYd>y(*v*FUv-ca^cvtPjWcf{)9jueP}Ucr4*ncSGNoS=)fQ{g27@1_@(N$|%Z;#zEs! zsAnisl%%LIqijZ+sDbE&Yh;7#4AV?SgVCn8q!^SBss`;nDuzK}li;#o+|zGQ2R#!! z4VK)W7E5|CU14%&-j;mA%*hnZ#K>gzq47@k6Y$nCdP!EYS$sue%d2Ipe2Zz-e0g^H zv?v-9-BB>IGJRqC?ZRa$ZedM-r1Q>0FT^Sc{_(eHw_BQ7^b9wU7MK?a&}ANPj)t5ApcXNTG~NkL#$P?dsZh`*%&n*YurSYjBI>$q4MNgLc3bSxto z)DmRU=F~QS;&+mU-NTojvK(jNsPI6X?C(jhFXFhTf@GOwwxr7Ba~`lJbaN3|O-Ecu zLPrDJR?gd;sGPvB_FvP!)_)CVcW3uj&7T8>Q)+v^&ML#oz^<612@o$m5Py1pmVX)Qj~0p?ZYIXpkRqwpazrsP8a6N*Vv z@F<9h(;YNB*EF$5x>vRY;GVqq;D+f9WMoOCNF*SEEOGq4uLg^30AX3Zo1%Mh`R6TN zwO2jcNdz8wJyA`hrms4`=2oV&wC~YqH2iAeil(FtL=Wzo}D}!&fN6lvs zsLvF`Rfc`-AUYEur8i6AnkVCw9qphf&RoSN3{0hc>sS z0A9IXN6S~2xmLCZIoAqSm=hhGkN>_y`4EC5{%uR_X~U!u%qpEsn_VfM;zb{NoQ& zZqFO9!d!7)LCyXFGf|g$MXQd@IrvVRM@#C;50=!v_GGvF5*kitLJjVgO&db1<{I8L zJ^xbZT`@ns;cJSzG9hg)>lBQq7=M>Q9paYRw)ZN{W$H*{R3l2mV&(eE#|(bas;Sc& zjL_agTte0p1=rLlD4J zE@8-b8fD%Gdf+}U{6;J9E3-mVWeTMQ0$=Utua@+}SF;wfcn1rmP+);z!so_gV8wi6 zYjuFVA73Ef9}@U`7RAjLifMSjrTkX<7Z@}3F}3X+g*($^_hNyxVuOZ4<{-{lqb1E=LL z5N;Z7u~SkLnn%BeU_V(N1HH*#lS4_W-(U~)NphV?Is?gkzP*hXZB1pI1wQR^rghY*$@xQKhNgw<}LquN@_(->t`Yl4fSZ7|E#Q@%fWMo^`8Xs}{u0DXH#;9|Hh(u)cW()Q zX^y`rB#77N&4L_kf06jONOKr#XtOB*yzJOS1s(}J;*hz*#>OV)Wos{?tN8pMcH$># z4o4p!4+%j*AP^`36cPYSim|M{-|Vg9r8zccqA|?1=S}SwqEL(`U3FSys73KB)NjDLfTd7wVJNA@$6zg(0%=B-mW$`=C+v*)tN-3)kfSehJOL_LPU`C;_7QMbp%He5=jc-C zRX&e{b)6I3PPt~H3+5VpklbE!|zuJy^@ zuk4ZEEDo&}>B3G?>*73MPgUetS=HuDqu>&|N9Uz{&d_-2cjEeGlnayM~+H82C4|24P zAC_jEKqkGxc&$|3^N)q!eHwIJz0NEJ>`4dNsQr4)JGI-qisl2~mgw(DJC`T5)_quWINTTuy=_=#C~~je9X8D#V9WMeZt;Ti zM9J+hH4l?{!qhYG<$52Hfn`(55F^#cL1bNLafPj{9T@#|kL|_y@M% zv0eDQ6?NOWR&g(sTjN_kZq7ZjRCC>DAenIsuQi#OY5%1RK!T_ni^^$CwbF%a9v6t; zBwypaXPHPgr?Q{ZL}k5xl}^7&R`KQFbmen1y~uxUT83@TET143`jJtVw!2EV;LB{_ z4ubu}Hq$?}!Ocv)%n)M&S=JO^NxIptut`dG*PKl`hUtmp5PYQl>}HChHoEbuX6J9t z*Bv1$L=A&vz6im&AS(8alGV8bQyUON)(A}ro|Xvd5}u3(m>O<6O$d!j^iy({V{0J0 zuw8SQFM8pcN;a7yTZxn)uMwEuDHKS-xgWJsV0o=Ri z#}C1T3{H*d5;L=OF~CDg8m8)Tvzk{0 zL0F`7S#x)?QA%*Q7^}KDQu6{+ z6AXD$w68lrRX*X~I#8S^88hdp2c=%mX3& zR{m%@emZGc;u=)9v4X&ANe=`xG7x-rJ?1Q5vLwF)EMOp!=B0rd#hKq5)hXU<1F8L1 zt~CR}@f=Fa8!nXzGk|sjftK}WK#@>5h_rbjEc{CVnr`Z47DaG_eTBi`%4*YLkcH2% z(m2q#ea%Tj_O#k81>Dq!t#qGl(m6oFd5SV07N}V~q_+x+H-9w{V1bst0t{USKF}|Y zbJVNN$zN;ID-!VPyfamzHrC^XLGW*ypd>LW4m&5Vo)Zw=0=4^T;@%A(LQV{7V;Pks zbLQ-&C~1#k_?Gl<HZs*3i{Z)o4<`u_Nz^mgX@#x>91(<}z$~jdCmD z3}}G@t&F9X=hoW8Y|{7ng+hQb^=)}rv3&iKgmKSQUL(2=JjRstbgL0@`%9LEI~Ju^K@~3@c%(KXxfbVzHD@tUPe_|&6HgLZ|ogxO_;gE>YHoL$-AGVDr1rqlu6RX(HbrYYLr-d zG#Ma5+zl|3=Kw5n$0!Z~f5K+7#@#+Jh`iWe8w5B6$l%980yFgh{*MfzH3F4V;TA$d zbLlLe5A>M>kZm~ABFADkLN+V*!(~@qsIwL#KV|`5=qt&4C#J5DqI8g!nL{c19fb^- zaw~E7>l+Mt8Jy|{o(2lH;CdRef#l9CKU{rN1rh;pFO!mEd##ku5|p8`t4Z)k(|VWm z`Kg+$Ru3WhHT>9R!HgZvu?~ZSZ<>;F;G?POjupQ`u+{+}6!&b>lCHmqs5lc-4Z+bq zsA+{ljSQBCD|pG`HCeL;S9ZHo-{&}t0&mpVe07bDW!xML7jWeEt#fZcsVOkjvf2+h zVquw07~H7~Obaw^M=NCAy*E}C*sf4=GJ~H!;PLuSJ<(id$3Q$atJKQ*Y%z?qrk(j+ z)GK-seWw*Q?BX$7qUSKo#ZjZ?G9JX0%!{~Vuv5>zW<{`Z(`ydpYLX+dB4p4)0iKn&m)*tuX~G(=(*Hky;qB2gHuMlaV= z*`~PJU}n}8E!f7SZx9urC9{__8}JLkL$PBTFH_%4Xz9@jY1Etbe@L|s7bXhV^HA5o zd`HHpLRb37f(7r`j_Qiq*fLD~D?NY7^lp22_7g1}$Hg7f7dr!DE`Ay7H>(_Vg+pW) z@nU?AvWJ1QIpC#RVL%I`-i9VL3vvcCdC^K6vdm75A0p}4UdM(Wza|aKD^=P#TIW15 zzUP+Uxg%YXd*$8iOol59efxgT1)$+V9Yw2NU%09>JOj{{yeNZI3f+c^c1Lo-9u45U z_$?fY?$*wSH1=1j?+}oX+MLmbt+2uX_)(71}+So;H8 z%ZmDLZDC|v%ICc0(^K@|NIfbqw5)9YqLSbx^Fj@%4`TG5o0>?_adC%%kL2$GqciUB zvSU}u`JSguD{|-{7rt7Rv*`>5JL}?(m@BA$kCxx8EVsO>oOrNkz5ITdhwkI*j&keS zyYLxRH-X#RZ$xJ$B>Ku(Brl(9iXhpA-N#ZlY3tgo4z4%LJfRi?eQ4mXhv!MR^!yx@ ztpz`iy=(0tnT=_5M)FN~X{3w#r68GWbD&Gp<y)L3Wsr^3S!f024-fEnGB){){ z!?u`SecHAgLUj2IUNr@{M#sG%lSXhoGPPR8u*!r-Osu&ZmR>&)EttP8(0Zh^%UxlV zk&X2838bPMpmwUNTC>C{XBPX+1*}Da#j*my@5hc{F;35JqVm^FcMe(--HqBqvH_+p zB7otYs3U4mZt4Wqor!r!@~RUvjX{QaKX{ElVE3z^VK#k8ffnUvRr`lPnc4YRMFMp# zMmd|KDdWoY2^LrTYDM?NZ$dOpa>oLdIn%p27(?qz$GmNnh;Iz+PmkUDF;HX~Hytu@ zC=?Ia_w##0bO*p^48k-hL^SvfS7&p+phRRa zh+Tn7Ifk}NH~9^WYQCM;>c|#;G_0AK-jmGhB4IJdA!cqhb#utkBM&%RuTTm}7jb#z zy8%D?Ha#q6)fTkBQ6?COy>p94*ElQKy?V3+(P7z-Jza)tRYbFIwL0KdMaBvF^be1` z&b}nAhNW+;Cz1x&LxU@+GIk=sS_7i=j}<6nQ6;(=B?s$M{ME1;XimfJYzenq_^_t3 zK&ib%fX*8*Qcy~v&b%%9$B&e_1J?`$A05S zb{o7y6LdN;?*S8TJ~3{;9ymqG%J-<>DSfimTJ}30ud+P<2r~d<*genzybyi@Io-;i zhZ69tsXOhsxs5ySYc4`UTMsIONjKXO)1kB3`w-x^=NV#m;)m4v0bldA;Ry6KkHFLhL|!@P+nbhZg--=GxZbf*I`hGO?U7835z za@(!+ot@cd2{GV$*(QEZOWD6B3>f(Pu0tlfh|5^D14Ju?(TyKo@8SUOb9m!=vhSTH z781kl-)PjA%v&Ul404~nCb8O9`{?;ixE1Xd6K2y}A#(u%y9ZK5m zrT$S!M+Mq5$yYIfU#ADhn;rTeJe@oA`3x3(V@X{FQCw>kQ?KK!^SeL;`trMER?K@@ z#Sw}$vPxT@9j=Qm3zLyflWN}vuEBxY@2&3&*+Y5-wbu6mt_SUCut>L=%*aHmbdehRLSRZVDd|kOMbp^RVMkErb zqQ{W;f7&M;tfZ``m!C}7un>*o`+8!T+lI|lv=5=e9BUl$8{KYw*b$oD&s{PgI44T zeE8+8Q(oGHwK(Vfvx{*Sq7UZh-~J3PLRe~LUqhl6>I2kQ)NU8(BXzyLUQ}9MPSk3M zW|}D4juo}?;D(`3r(NI$O3{)^%7#1^E&1!rX9sbWEu}%`JFPRnn!pH9;K+%TBbI)B zT}>YOi#6nG_Z)U?!hUkhMPXkrQKt%wsQ$qyT8+Zr4TZ-O!1p!)p8|i0 zo`BnZrnGQJ;q`u1E?TJK=l9M}rqY+Lb^Kc3yHGFQP&Nj&uh9f?cW3+bp*?uKSNq19 z;rwk-WriW8X-p2=qgm0-)7Aj=bRrLUTE4OUrSs3GaeEGi3j)Gg_YaWIoh8Yzjm*&} zut0?{J{YukjothTgTSzK8+Zr=+WuxyhfMsCq7BNA5fv9tk7SaD3%KcTjLY@`e1_wdR=!%B|jTQbF{T!?ui)niCAgP_k{z2Y-xH zPv*1nU4|DiA=&*Wmi8N^Ct7n0ZOroX1%395V5FoG&<}`^Z;;e}ho;B?xlw}5h={Zl70$|2HR$SN7X<9TP zHMe`WcIIv}bQCp!w;n;p^%qWU@am_&kbu=R0v`(4kLRe3Bje&6A$lS_wfBzE5S$9L z?^uY}ILS4MctXq*$w^(fw6OGRqq{~vtS>~qhQ;3}uW>b5Z9-yQK zijYW;@r&Phnq@at8(}$wKhRr_F33TrzNb=v?FOzP8Y`|x*EJidsR||>6i8#oXGgiV z-ZuyR&@Dsz2y58+6t}ndGQ6$p*4pxed!HDe(mZ#zq}!@-(M#}9&6vJeprV!ZrU#1F z?3}MwJe6sVjdwa%w=A;A=KfoQoL#SdvoSke%-aeRk_zYf9?4!Im^-AAJWoJM2SP3_ z>Bl4_;iWt@iIZ^t$=@iP%-dHKv-^rtdDv%CItFnG8WqE`tR+v_eli_1l|3MmXL66j zNr;KLMkR#kDek+BmnRSQ4gM-K3}?ofMs!`p9Kls^B`MkQfd`## zGp#&C>d>uVdf@2;q-=+Lzk03{H?#hH}# zf5U_;gb>7@0go0x`?SLz{wnh`z0UL?`-2LCkA}+8$;JJM&%P z5!4>-gSd2?LcVk7rcRnZ)Tt{Z?~5?5aPCSu5J3OpH40xwEgfznKyQ~_;0dEXbkrpa z7wlb6rRq3M!YB?E-T)Cl3e>}t(eLdsc>#>h+R`B9(X)L{{)zt9L;Wz@kdekD3hDbp z1DShjSeU=V5p;D$anKY%I@WgKa6|OQ*(GVv*S$gQf5b4K71H#Cue7#_QoK2@+ zVk&Nny2o?l}sgFy48{4hCVS(%NtBH-`z9 z$A7ymm5(H(LC$p}swT|0If`Lk5j3=s1Wp_7U?ph7ZL8K}1A|AXj5f*zo~K%r4F;*y1fW-0fgV13dGFW{kpV;K&udDF6lkpQXAKk$yIsAw zb`$iFJv^3EVdphQ41gY^S3&kV9mF)uRe+H{_Soy4Uf(>pz&*`n3@U01#>M1TVzDFCauh?rIsBtmU{)Hb#=52<% zZwOmF)NJ)>xkb*>M=SiczF>%2{G{&XamB5VOtg zJr_I(8aDhz$to&1T*`y*Q!17`$v#Ftkq(grf>8oLh0hhz`&=P2g&*HO3T%xXty&9K zD5c=V#(wad>kHJJiz~czl!eUm+S2}1j(n5*Sz*peE>B_K;E@6?!Je>Z(dZ6akA9P@ zp%iU#O;>*+1OpNGYPKWK|QfyG0MV!^=syB6h3g5}l6>uFGxF4Gxa$;A2h=w2sXo0u!s)9U@?+x9eguPe! zhOMm*e;B8OGKy%hM# zVE?gYz`_|QbRWo!#e5GL{%y&!br5;XINsvRnYVOpMMdmc4 z)rAbR(|#titUaCHNC{1p`7!F9qcGW`cj7e>W%(A>CAMT0pc6&kJ_-Bumyz!$2CGh7 zQjc#tdWR9yXt#n7wzL?!P+Hm}%3Wa*SCeeFKNM&@5Pm%*`F1)@-&YeIQ{c zss-zYz~yQ}`eh2aq<1^;?4+%gRlwy#Zu5r*rTM~cM_P;l(7yT!i<|zQwt~PA=iRfp zKyQS}6X@iET1@+t@VSl{l=V!@S!plc^uh_9!u`VhI&2fm1Pr zkW~3j;swX6W{By1n&hAh*Pgl)HIqpU5ox7E4g=$#SyiyO4+`G_?ck?!&)(nn9$D~T z%-_>NTqNc|na2^^Nimr6+JOY(ZhzwXnA-V`<Oa3iJDqon);GaYe&*f_!MfYy~*uNB|e=OMM#NwwV_(af?L3rIM zAaIp&{1T@%6_)No{m#Md%W3`Ygm_vt`R65fzuud1nvxoGCK2);&lrEY!(e=zIH$uT zb1IIUKl9v+6=!yra&%)YQ#eM3YidXN(95hJ*TtX*2L)^A1A2us0(wAM0!>Nb$@^zwO}NyOnV(E$s|<+76Exy|Bp7`1{WcFLmvP z%#%eaCXW^NlRxpBE$)!v-KH-=Dmh8sywoO(fxK_;o#s>S4Q}!Y)i2j)FAaWQ7$>^- z>Fg^mC0IMa*RsyPEs4iFU}&+U)$t76i3S)i#H9807(DDwprtpa@5Mr>)Y7fm!4^W9 z8_MGtzF7|l7Aos7z=sNC)*oz~-@ekrMSGq}N|fV*Tl4rBe-Jk4?%`IGliWqh(#?lp zU}Gmw94a#t2CqU=#vf;BjqWKzE$~-Ro-<1Z^gOzg65AeU3(xg-0PT4Dv49cPwDLuX zO+*PPGULL>_9%E0nR2ns}v4%&y}eUTN!}No60ii(~01z z4{&+#8qH}P?ZLE|Cy63vhGp4b_pZ8umb=bu2eS}{Gj>}a%7JZ5HSJE)1Wx4`V~A@# z3t~_uPY#Izq&i^;c#Lev(le| zpnI~IA~W{TWMky1dvfV@Ig-$uoC|K|pjSts6&!z|9^#xyVZHdniJ9eKxQ@7E)9m8? zJNWx!#pT3aRd(XQw~8YFMosKewc?NX0*Y-MLK;M>m@D(#B3O+{)6P%!5_CB?T_j}0 zHyMa7?sdoBWnniEdat6(^t|)NB@arO%bGW!@6TZr*C4NBo(bD;cIi{cGFbE{{T1)T z5)T|$ar({6=LEn|??LGaA@_jQL@iD9C3VkvdjExKz-yZSE($ z^qpsM|7ne@BsRehk=AQ}@a|r@))6TENr4!k{nN^3K}EC`DERk*KX?~zv%SWE9{nMQ zTnjNbtTj;-0r`WsgIA7Z=kO)Xe+@cdZ5?*IE9Naj#qnf{fGC?nF8Sl_hBX7T%j zm$Zt6v~?m({9nn=gUEW~qPECLZ~n<^NlNBlMfvB==erQ&0H0vco!)8+G?Z&rQSM8oz6G<0)=a=mz-dY^c^#amGIoQ47ZxpWxj__NoTb z|JWXEaVn(S9o)JZZhvTb+;gJZ2$Ox0_;*;MVn*H~s(HTX%pbrjwvXp0t_!xle<o2oc(l{ansa`M(B}V~Z!s8#!uk_J<((Iz&M} zzAh(%SN?64;~>g=v!uV_5A&dsM-+sC)P(t8$%w{Qktpx~F5Lev+`kI;|DMLhiN`bA zm?5ne8t1`@Q+xelP!%h4PEF_($#7ui-Bk^|1gs@bl`@Hy!~$bv;G^5UpN zc13boRdbJ(umRe|uV9K8CkJm>Z0O?0+J%dZPDAfP|IP(z1xse?4Ef&`snmJx2jZF%?(@iIYh zsEFadT9U^=*j%WfL5r;}^VkTjY(kbaNF3Mddh*I)T3V;_G& zMoC0TCH3~2+rJrDWJmohn~S5y|9V1kbwhBcKVZJ)X{k&5iy{k@2ee2Y|M{l+DQ+c(=FKIzrz~(h{mk4LrJ>4R$!n z>{!6Ba(yfq)}YRp3q@Dh(`T2P^1}iTsi)?cgUK-5^=!?6}Yp8~! zal5CLHG7nM6Wi=MwI88HnR&9L(lpFp4MR}YBS3rUwLra&{_DaPZ&He%b z{N*3+8W6`$b|4UUOsYdRGnZNWG{a#!)GwGj789t(vN38Ib&H4T5VZI3&Ov96Cd4!} z16sFyKS3}Ae6l?suU{8u`N{ED7qRGwsMT!UICSk4%gbw$+B??s+Fg)$QALgvEXPO! z(8*UI_*ZcIfW057kc%39@CYK-&3kn#a;L93g9uLw!XNsfRC~2#2@f3B2KY~2poITB{`qB%lwJF?b?k^2_sP z)vcjy)OclTv-esrF%- zS!5m)BySL~zZpDp`okU`@|$Yv;Dp{#db)1&TXa-+{e&eaJ$-vuEsfCM&bw2s16So$ z?u@6M5OSMFv44fmRg6EbprRMBWp1WuqLMMT#ea9#TYtdJ`TRpX0gvk`Davwo$c`Hv z&Wq?~uZ{H^A>kUS4wonWOrcA-RRzM&cn+SIey_}JvFXZY2)pr_)IMi<=XtaV{9&n# z3e4~QzU{nHlzr5#E*O*AXy3ZkJ(2d^UjHt$iTz4X>lLdFmWVHd<<-E+ISec=N<>X2UbcPN3DS(b-{aX)fnTeu&lWNHk~}?X~yg zcNjpL7LS!difcjzwv4{`@7)Nno>kwMLm3u2>3fSK@6Dw;;C3U=PfOPriAF{L=?3#4 zZ7SbkxlW^{o6~4<8@HB2Wz(Y9%??1y_3z5jZJjD`un^6WoHGan=Q@0r zs|qqguuu#ORM~u|w0PTDW*G?mEki8MZe1>v!8(TPm+11Cdk!z4@t9SKUSBD%Eg#pS zGyG1-2+T}ts>wPJa(fT`IR;|4*`$-J#k-h%5CcR}N*K%g=DV#AeSp1)s{#&l7Dz)e zDl_BE&&Z~7e1#tbS1UN&6#*3=dI|F1^wFWO+umEQFsU`1u18)QEA^g(*kpa!jC06h zcFcOa{Jtqh<}%flIRr2A;xniyt?62A*Trx|bh%S72Yj3_wvhaIUB{+8`Xdc(19eHn z)xWLJ2nX@pc?D)tJ9n5=X^8*%{;PlF4SMxo5z}eVy0FZ{)j?dx#v~E{u+HpSq%Dy) zR7e^t6;v+%7G|c=&%9liZ#)s$UvFdv)D$@$Ihf0AzD|Ce%xf4xHUT=^@TYM&?X3d6 z0m683awuFu=lvfQa_33c3}`uP&nOQ(^AL2j&JXDOMArzd;CvUmcVP#cGMj=yaHd)q znrilYl?K%YN4yXP<{u8!$=rRPM8Kj1k?^(4v7wep^{xSpj@z5cMaVKNPwwL zy^96tdJzy6cyPLg%bCe0U~i#bITB98_xIJ8AR3zKr%^%>5i?N-iS)}b?SMYx7Px^? za_TCvc6Pe9mP!NxA~G`uD=;@Emu6Anw zcIRoTh_KfPgV=_84K3B|8y^_ax%KKe3~L-Uqb!h&0R!HcL=3m~bz2f0@AM(6NQS!D zsJ+s$I^St(#BQ}fmGa&!Rd}67Jw*5u^r)q5%cKn1*DJ_OB}YZi!$Cw0y1tW^8YeX= z5;=y;kSzUdds$H?4{0cCUmm%kX;G6t1VRYTj0A)CRvu?@@k{t1$0m|eV`%+Sv}8|& z7Yh%`>?;CHk{~`$f4L)A_oNJl_mI7o0pVjGly){;K*YMhNCE1*p&0SKk>=W5PkL2sQnmYlOI z-SAaV0)tZkz-5g?mAW1Rg`PwYpe{B+Hpa^sy@ovs9y3ZUJ2{On8$v&=ufM2ZaYXhB zh5i*M$(a&Eq>nkGV#od~+__aC+SG%A90Iv}(Z=dFwB05f)^}!-XD`D2dcqF93V!)! zX$Su!Gr%oNd%Cr+=;h}w=7Y@JuO5A-Af9t*%@QeOuw>j@ai7vxZ|;>&wCB~kO!$!b zTQqdlT?9-%s&h(gup+`#KG(KT5Vlw4K*~p9;X4^@{`Ews(pOj1-hH!Gk~clF<uF}@aAc^m#L#|*L^p}y|%x(v{}wJXE09L+3b}7ClJ9jb^X{d<{3&u27 zfL|QbBg0e+O9@09?YB8AnZE+v1g+O3bD6cF>uXd>|h*oBZcLVWw37m(BQ@2k@__~(s#;_GO-k6 zqlJl@65bt`oNUY&n~q(Z*A%ro7P-Z{s#qZ&5cVe^?}LpMPD_ zsUu`qiGI442kg9N7`(LI;>);P(P(eluRhD`G_!Ba?e=Eb-P_{JasiL~vkW(8zzB3< z+S5ciI_;_Tm#a^J>UON3F7*(2}#+w1Pw|58EgkAD(&b8lm65EOQm!TEORRI}YV81<^8N zUvz%v-eUe}1oOjh50;7+YI2i5}A*Pj3Voo&uj|P%EiP=0_OdOb@(M*-qe*0S!Yu&e$%P%*ArS=h|LGc{ndDDuaWNU#v%)9k^7`_i*Kgb}4 z*cEV}bb>Z@TsEB#7nQRgOlg45BTP(Vi`eF!!y{18KWTGQVzo?>$L}3dc#S4Y2JcfZ zGHNMY)-DtLuISTTfqF{5!g9UagoG_{BE$Zklj>e+V?&unBZaYqZ2`pRDp-<;M<_+6 zF1XEC^wy=l$hM57q}Tx|AAac-6oMF`&La@JNVWlPfz4{R(aKHQHgDcb$7NLySi6?)PXpRv^B z>hq265xBJilqhJx(=9Kr0*fq^m0B=alv4CNC6iZ`{!Xb~;w47C)xw{t9vYS0OXSk@ zr2B9epL_Rueyx*O;Jd`NY=7%nf4IyMB{#J1wq4&Pjy-B@=EsXKi1567%lBm^$W(_O z+&kK^>$SAjlE^0F#8+RS6whOrdr^0TLsJ@T8h%}}YI<2??xlJe^-4WMa_HK|T+({((ycYfu3L>*(cZ}r0|fhS9l z@j_87WQdg+qSi0Ao>KL}#Mf5pN@ zai5u~4}XaJja)Y;5O9FO3{#PgiN^zBBSZjWz?Y&n(eBxgZQb5;TV&IGJ@X&FS<;ma z!o9P$OEyqdje)CS_PSRV5O@+{|LjM5_fd9=gar%KxafYQ+FDj%jDwy8pM#oH1}gVO z@2aoGh5S1A@)XoAWLivq{*sj6tcpu!01!cpY&-{_eEi!frxTsBp6X;>R5a)w)C^Yz)9FGXqI9y-Gg;E;{TemX!VcurF`tj zDy$uX{@D18?)GEBcF!XJG1T1iI{282zIT)})Ry^+cRcU=5fa&12Lnmu0}+Dg z_|X&mXrp_f03On^SXf69K@74z#^IlDggu|Ap>o9G?*iJ~1}VTTRw{-CY6b7z>U@RM z6${FO6a+Q>eZ4w0T)3Bb5{ty`h(8u$cOd@6EG_oeTgy5W(CNp1vUQoDO%|C!iRllM zZSek*?UR2FAPhvfnO$IyQuvSK|aG5 zZ>BfrMmGIWALIStH(}87s&c>#_G?GO$b@C!M~Cc1gDMA3oq_depSqF+$N`yRhV5iG z^TJHPk%OiY^q5$S16j`5r4A2Tg-G8Mj0x<9BrhIpBLa2#ZtCYG>;&d%z4%6LA|5cJ zKWJtcwjudlME_MjbatlEO)O7{nJO-9W29A2bFZN@+9&k?u=kchacx_)@Bsn|o}j@Y zxVvjYa1HM6?u`>d5|W_78xI=X-9zxk-MR_xjk~-}&dI&!tNMPvU$1Ue7e!HY_u6}} zHP@VDjxpA=Qmn!0FxnQI%Gg#z-p#Fq76TZ}B*>Vz%r}B$Ps;U@l-I48=lk@GL8dre z)afp6h2!;C>L8)(CGzC4J`2R(=yNa+K%S&{oL=h#AFs>z@$`$pn`V2HRWCBzRu;z| zWckGs_l<VP?6N?yd(>>Y7#U`3?%p4%4+BjYP84D-=?JSR9cq|1J8I? zmdR>+QkakKX5I?oC2VU_h7bsJgm?OW{OSx{Yp@Rea6&Xe9R#}&MzR4W)%~HCmEJ8#^7wS4SvnVIZFk~m*%h@a6XcbqHjv}_V{&3s8nPoOq ziA=!~JzE6@bVsjt#`0dD*EoF9`8!tHuVc zWAh?PT-$Ebc=yOAmT1(*rho2S?Eeh}e70%@(rLu72BhmuL|?+`TY_v?szvo$3Uq^Y9{wZ@5p^GdqyfcEjzav&q6wOLF$+Jj$yS zt^0Jd_O2C@uo~we>m2{+jBRmMI^|R*LOe#6 zP$ie*&i8pIWG-5KxEqAM@GSw6Uz4A+#C=Dkby3TIqNg6fCx4@kOgQoF3BY!w0|WtyrG~E0q zQ8coYmPA{WSoveLKVASiD;oK9_-txFiIJK-wqk;dx{9Ff5xLO?DvyZy?4GJI8JIW| zirk)zudQhtmA~T>RzW(mR7lhfP7(HY9y_s8iN$VezUE%gxg1?+0ZpstK`h7ax9Asi zwK6ZZ=rj5YqqKeAo%UpGuFM7v%UemqjOLnDR8-D}$b^eqpXwzF|26rIMqS>wVx-H! zGfnHx29Vl~~_ixAZUk+pYO%cEBsd|-aB?Nx^*%1;kkO6z#hTm?_9xx0{}rVyngI;PG%N$hB&TGerDGPiBQ*! zvjPyT2Ygi1$mbLpx7ol<``HCinJ*H5!Yix2IbKm(p3L*8qJ72#csSIS%sEoCM-p3} znCA?Ty77#7=>@NvWFAwnQSQwk>;dTS`C`1^xMu>3Xb2QwgAP~WMYnd zyt$I#f@SsX#e2CGrR)0vbrI#g#~gN$C&yD`FZP*Ix6IZ*NU^u_Xe|s%dzjX{PBClL zL(y_PMT4%^Q^=IHB6oYBrQ2do`ES7^^k{pE)~;kBt-JA-i(w*w1oL$Jed25QT&Tjn z`dK1t+0ea5=hW zJBO204XVBI#U1e)p6gSCjR0!#Af2Xs$J^u8KGs?FCw4E$6q>gqst0x0#;xAWD_Rf1 zy#kyg;*Gz%ycC2tyO&UM*UXP5zTP661FD8uo|i%dy?M3dtZmM)z*)WIcgK|av_LNGhzi&fh$m(bC`IqaVD zL9)7D(>Udack+Wl|9u#l-=cTWHiL#R$lso~Xv|a~ED1fUI(^<7&VBm3hM{Q#z=fj> z$;dw~9{^cLLLGTehm_`kh5w({Amt^n5y#$>x;^?Em3(N;55d{a#Y47TnLDrsG_WeS8PwOg74t!%g3{6YAZ{%N`ro_j?++`Gr-~Wo?Z^RyUfR+ z;G4TTDRCNXyS!Vm0{V*Kq?|%Fo=5+B>yu}hnEp@t#EIa8;2qtuOjFSUwT7(S z+d~I}fT$p@|1@K+xrj~{{R(l>NKy{omE@_y$wln4U1a+y-0s5lt_AEs`?@jiV8$f2$JI+-|jBA27jH9LQ zA*Lu@zD7BiJ2hROud-Zv+i}L`Ev6!NdC^J?sHGD9PbanVOU3Um#=%xE&Jc}L(EHMa zy>##?1Ppov0YB|0?Q4d+|q*X9C;GX<1m*%HFKvP5b z0T5{$>CaR4?iYSla|d0n5z>24H**h(bQ-PtTMY*$bC=-GE2-my=4$syfG_Tgo#&<~ z13$P8%saP)Er}YnPlWwpsQlCgaxjE;>(s9U?x6u)U*cofVGU{%)oXV=^$u>TEQ45)xZf`Ano{;=je~FDK0XZyR}3k zOvZd7KoF^cMtH_Ij>_^p6apYe>}0~8(fY}YFY9}NyHeWDL6_; z|4VHh0?u6v0EOza3??tK0CLP#Tc;Iv|0!MTzCA0CWZbVpA~VX#K>H0?ihc|HZD&=U zN24;#7AxJumdAuCYYxXWSrWlFY87h81zIJA7JV_ayJDN;VU`18l?jQ=pj;l_QWAcD zHuZevMBbul0TsOUW^o0#bqJB#aAVDm<^ICu(OzBCY7u)|rBQ$Ud8+&BJM=HIgL;!| zOz!EY1}LS|r`W&P-Q*FxrXV28q11Cp=+DBJ;aKRpneCU)>@Q9ejV(W-N)WQ@;i&ERGY{x_ zu0pmP-6_;KJ`#D_^T&~{Z%MTE`1MH<8rn?yUmTb|)QZ;3fPqFuGf!YKJ$o_{Sx5c@ zlVTL`kLc6G-Hm)MHr98nk_xE9xJ*ofj%k*0?qAs70fl1U$+S^j-p*8!J@O(@<~(7% z-uJ62#_B$M$Ex4TW4;>&AcdiAPsS?&lbXL9njW`(K_bo&ZzZDu%dFgR`(Fpxm|63!1T{S@GCt$d4*-akkjzArwm6tCAm)KD4Ltt(f$8tMhVsT*@_@|- zyLbxcNR{P)9sYF1=Jf@lYK#5?uz6Y)-=Ztt=J!n-JI8M2f&0Dy)-;|x&nKHsMSZ1P z1(L+!NJTf3(*~^ZMH+E{lTAWrC{`F$sV)P@CYT?VLc47~xn>hZFhqG$RXA+P;*;4q&vitTBz1q_ACDRZ&j>(^zgA~)0UY}0!JD(rxI~F&PQn;P+uI0MtmsYy1^=P$1KTivf6K~4M z>%q8(F)QajJ&xCJFpJ;#A%S!YwEmve)Tu+=kMxwp8$I{79v#A4xn&wX0(8{urVX2v zS-0-K*L@B*k~J{6c`;0Ozz!K`xIt)!N(CK#MmTfQ$pD&DOf`0sE=NP6=cJ4PL^O%o z=vm}7ly(4Eh{S!tcm6cfY6SBY_PB6Q&wJfyJU4V-tH1A2TQPbvctF2ccb6v@C5%aS zR-FGCa=EclH`i)x6?r~uonH6F4wuq#-9yBA-_tqlcfkkTmQTt+_9H~EQlJ>q=dpXO zBc@xU$CuQU4v1?HS)zEx8&3n=x{1E6bj#?`&n9}pM<;Osg>kmyPl~rPt=seKjp1|?2Aie9Mt z6{R&BRok?o?`e>1T^~c-Lqt283^Q6~cZUc-NoUu%#VmU_J1TbpHtl_o1*{=Eu_P%7^j9fNvXZN>yidug`J)NzdM?$rG>Ixj+{^=prUo8MT$+-+8ZA zY1EjsTMcH03VcW3Ku@B|$9-{&uVY1vzo)@+2RhFzmaHR)Cn4iuqgm9uB6qLhKoCN? zvCat4G7(KK$X=b1gD1mhtESUy#1_d)YRsJ%8-HcsZB^9P&(A-Sk-4P$XSlx)*%Tfkh*WSLk+FVI1 zyQM;LV~49(Rw=@F`zO<*Z3|UY48vngUB1I- z(P>w=g0%*u`)_G+k zEyv(`IdN+9f#0DucBA>1yWhKW-wEypx*ISA02Sk0tGJfi?+kUFhi`3q#S@Ye; znG@z+u0ex^Ef$zU*}^~;IQ@9vK;@wHrC*a5cJErAWJ%lLCD53WR=0%ouX0P;1_Bv( zQEQ2QTcmDF#rNJ8XMU}s{_^MwiCnC|uWgyKoq>ljb2}^v>hGSHKk;DLl>w$Yk!3{p zCzFb9TuC8I|8lf?=oNXw5^mNl`Z@~*j!V}{gFhJd>87JeE7x%q7rdbKg)bnmSkgWx z;M0lpwCql<*R0@1`c82cyTgk=6Z;BaibuH^ohaK*vx%;y&BA@?KA zpeg3*Ub{C_x>2Zqa*qcf>G=x0_1Y+%h>ORp{iK1&%Q@Ufn2xbh0%#Y8q4>SD-Hw~K zSZp*0`8{mYt_x?GLN`9vNmvzLW5>_g9AUMFdCFh{Cs%``G7xfKV{k`E`d2_NN#&yN z>&oprAz%+SdLnu|A#tn)6Gt5AesO$7QhKU!JQ9*dtA?9Bo!3GIFc#I4`CF8Td2L># zw9PE*W)S@u--Bl7`SjMfbuUE_DSCq2YOiTa^mh$L_~NhOsbZzWE=g0qucnp!j&n32 zC}Q?4QWIjE*)mc63qZ=-`z(8pW#<@h_3>MevR;3|U7Vjr;9o9bHSF611S8M+D}vE$ zy;?7!IA64%uF|1|EgR6LG*+9?sUF{xua3sfpd$~n;eF98LNTG#><6nDoI@!kuZES| ziLY<>8?TkEJzLOWqmK@Ognr-06x{0LeUwwcWp*K(??7-IxAHDaD%FSXpR$-T5YfX7 zvwSksZgI}sabcJzjp_Pii&4-9=^#LwIAalkVz?lVA7MV3MvFdfe~ zUHKS+ioLMd6l5JXZ}GVy+X-Z)nKAEdPjzLMUz`_GEmkrT;6We2z3o^BTORr{;1Sle zADv6V=6>dQUF+xuuue-Hw;IOrR?l5FB8jd79ur{P|6MdwzC>II#(sMi^+m0ZXQw;_ z#2Of4YZtw8GM>g8^kn?e560+Vdh)HYJ~TzgcbXJsmHSNBJ*3Vf<*n7c)LlPc7`~|u zKj9-Z*spf)!5Xza^8Q}|Qpk?aAb{N5*F ztKBDp0L+nobeJ?6#=OWctOIhE+`9=BIH_SB%vyhOZK?`>{h%Jm2Y9p+Z3op$%910C3wtEi0$7AX9q zwIz49l3bQtehPX!)i6dm8zmYZMqo%GpRG^1xu1{ckFc2OaBYe>H;em?f@n5(VdVJJ zpt6vDoAefEQC}}?VS;9qea%9xuYKzDvWty1otjc&fA*V~l(9rv`*4AYlnG5c64+zC z8c^h~2U)CGfS>%?ZoaM$1*<+4d#eCc7htH-jyHd6;2wTm{x))^>cO+H&biIzFYo<^ zjb0#kN>l_dBuaMdw`|$w8p92%TBweUwuHMTXpiPzAkn=s;Mfe^%M8lEVmG5 zO<5I&8wmDXyl?vcxprSC?i$#j@*2jCF1x~8b5yX=2-#?Sj@Cb4`-FA1sz3gF#P?D_ zFM`9`bM;fj(N=!X$LAAGF>{I%NMOv?v%VHzaVBECn17bV!!Guz4lpA5@npIP?nsbX z)?)nN1ax3jV`us9oa3X#f8{kljexx<+!i2HD1_mC-4KDVxY_XlACbd8F@ZfO2PmiE6N_Jrmk z|KJ>QK>pW2{{OE5!QU)(7VTdH;RFT}g-RX&pOHPGjD8M`_oqM=;A#82hW~vJ0q!UT zt~ArX$IJPkS#q^v{?|Z$z6J(D7q0Q|@tOhS#r@xn`#U@P{hS+ia;zFSFAh4f zn=Ap+o#p)<64iiOzs*Rwlf0D9fAv_=FM&4Hipw%A$ho!oY;LE=kQ?E~b-d!mpNV;p zK?qFCL&m_p8va#)X5Cek!Xc zhtlA>6-F&WW94r|Rg6X;%w>u(v|-{HdT|HMZFg4+fmet0f`aZ=f;+|BwCZ_^sEeTw zM2f)_C|%IaVQ9_bSYmtv5V=3~iT#lod64khX~hDnd5T!y|1SIAT0o3ydZlDKfXBaC zAcyqR<{ijBx9$F{IAs&u;ujQWtDZk#=Kkz(vD7eqkdRqR18_j5J>8!E3K;GVRf!0E-xIrWe-zJ`Dz4+m6tJ7AY2o~X`@tzjF*e)vJI=TEcmT6?J3q(=_&^F+f zlbC4i+chf<04n$`Z$HIFl8Zjr___q1puSR_YqBoGZQeQR487kj%IfYu3eq^yl@Z%luCcI zjc$-_Ub&}s0}i5zKF4AQ#R2LoAJy`el~mG)Y?^j#$M1-=)KUsPcA&a!&E-0>4X>@5 z53ew)RL0d?`T_$oqWv$2W7TraNN%>%jc*XN1@NIg00gTOUUb0`VCwgP}8{wvOXvjuXEqM>--oQ$7wF}vpQs_ zk3NItHhq&dh~jp>QP^nrN3W9g%{AWO&8&3>ZQWw}k>Rb)cGe%+7NtwpL(#;TF}Gw` zG%eis(59%$z5igFzlZnf_@~AAmR8Eoi*a|!Q17`^AbouO;B0I=efCmiGy*W2evodF zFd1z0sq82g!?*K2gLEkVGwGs$xL+W^JZCx?w9(?aB5ew|v1v~84Dv2GTa~=&1m%l$ zI4%K8yR^lAXv4h4*ZUpFFBlr(JXI_w>z*PNM$kj^7RV){ZKt@jwTg8Zg+cpVk~((` z$Y^-w>8b{9wy4~Q%7g8BJV=i*zP9+C_k&=!Ch)@3*`stVw>{5kopIeNi};?5-4&K; zKIlL~fW;soyOF`ZqfcTLi;dZy^^V0lWVUi|XU_`er6<*Itf^eudc|blc2dwWvhi_X z$hoa1fw%RNuBx~>zYr)+;uo7h`aYi96AAM1%-#u2)@R_e88(FBAY1sEm-uL49vC0>$CbbG-o$nd#1@epw#FVTpyxx{t8q+3x`9M)id_y zAG{#njBC&vT&*PR8X{WEd-XxJQ$2y_1#?BFrB}u{n;{Q)+;7$FPr^ zus0JgcWuMYPetMq23CE2so)9@CcQ^)WCh0uP( zIa}Y?kh>@33|429`>RU&aQ4nb=>F`rJ&dSrt0hnG24|)=fT*sjma5eaYczkPdoH(;Zt44u>Bw{y3 z;Kj?x{HiU0mTm!n$?1*@0mFuB1!~AUUGjjsj$cVcOAqvQ?-f!yxyAmEMAzi^7$kSa zfN6>KtUZ)?zCx0|DM@55JEg(h-=V&DqE*i8=K*JBz8CQyA&)cc9DvCG9zoQ1Veh^LT{gAkc3}#CDb`k5!A>TN05*;`eJw@-133 zc1+Tzy0|(pCYCez%*$l-#Pb-n;pvP(jre4xn76x)0lUMYZ)Z4fOB8>nT@Sol2`5( zIcoXmIOVk-<~dFxh8os5OhO9pN}IUkh;luq3vWM}h`(ig*mFRD-V7u9z=$r}q0qRZ zAK~H2;XAK$kFUK!Qcw>L#cU#S&f_i48l#OviSY1xBJq}vCvd5r5w0q_h#St zuJhjZo7N8jXt7p*K3l-mSv4bkoqOgMn{;B>1!UVTwfrSkjnMz3LKEK#qF?P`_u(`J z5&1{~KnY41cKt5&e6YVb>(+Bv{S`-Tt=!4`y`Xu=(|n274&ox(-=jEq7X5qJzXu=o z7rr;T6l5@E9CifuaURS0rct8DNcZLm=)h_v)u*gf(G4JS;=U??RvI;X_tAe#aobn) z(65|NePc&AQ^%6=I`UJE-Elr65tl{u9ALu!GGj-nUs%OG^qud?=CIh-5By++cZ^bD z`0@RkH|HRswY^-!YwN+)XAMq^W*LV7RGwS&a=HZJl%mL<#DY}JV}@0CW|>1^LYo$V zqq6w&udr?)Xvb><_9U4fD)WD^#QwSM_0OJ+yzd?zfVZ80b%5Svgoz$Mvw|{fmYSeQ zHZDKXP9oY=Yf@G!C=+RU{VIlDWnFzxJm`J}cO(2_XSgl(O+Rg>v#!`@(r13H^X6Uc zEY!6`rzi4nrq&L=QoB|*O%J#`nX%JyT6-jtm=D{SMT(CkhT27%oqzSG2B_NcaJ3^@^rn z)m-0GBoA}mrwjWjmq};(cLVX7bh_$hho%!>Mu0HjK<#sOEu zb7lffJGl<(Yp;5ll=MjU?ZeB72az9U!%n&xaK2wN>|)kw=g$N`-IArFHTIxU($=u|=>q!1qe>n=sKb^K> z1r@`Z9Y#LPD$yY4CvD@f09@CV^S?N)uggF4+QuxcKZ&7>pt!#i;)llm{RG-im4Qe$ z6(UH6cKs8TU18X0F<$UP8M+j!e*3(SU8kMd8|diB<9KoR{Py+xOExmce9vuLfqz!c7>Z4Sda-bM01GM;%69k_ zM#w(*W9Y4z$qxyesZ^pN8dPHClrv@hTcG|p+i@C8`0x4)&V2HSUz9*&blP}1l-zgZ z;H6XT&C!~;<-!cA+l8vjmt=5I!VxvxL+JuwH_`M$nr8Dv`ju$o#0^dSnitpJYjF=Q zODPQM@8~ru+BU2u=ognyr-Xye_*j?Q^M1ZyBwLpNw3gvDwnli~i;zt~rGPMSb9dc> zkg;Myt6u))8=RhF1@|Z!%GufGoUus5N;TizDEE7!$WkUhQ4OB<#dSiT7nzp_R*+NI z-M5>_C?ecu9Kd)?xDx961n(vpuypKV5~gxm>NGenajG?4nEA{%PQZQlR?Q+`Da{F` zq{!!0KK`#m>EXwI7c(N+H@D}oN{1~pLATZK`NS?bHU^Ij`V-l$Ee)@>)KFI|k1~Sp z-iXZ->TTUFt`27m`{Jd%yE`p)F0OXsr$K=%DCN3zcdlVzilFXoX}dW^$GVbnJuUY#kozxRLBgA__jhY! zm^LkC@es zYL=Z85)}9Q;>Xce=rjxOXHoP8C8*Rxhx4+TaBHNZg_Q_J2OzUO-9Ru*@(De1 zLQ`}i_dh`3wlS=Nk*%<~FV1-`53?}npA~D7A1v0JR=xe8PRtwFKSkl(ET*&$NvEHY z`G#(w=e098Fr|%MzdJl~e31U<7A=V*@)yib)z6YH{PoDxT*U3CPA+Me1DoQv$hYwM zx=@S$xY5wRlwCZ@5%HD9kHqjlK1GQAAlCA@cZ#(l#dLF$$6-Q3$jgr6;E}3m%e<(6 zfVtL;h>5I92(BtsQpr@yB&E~X0B(-WXsD}w?A*{G47s@!R$P@v);R%SyXuWB6)LYos zf#q{Hbau0a4b@KL{TO=F*y1{rxZNt1KkEBZX~R<0<*#NAvsrTQdnf{9)(3Yl5ATw| zW%*cC_Ry>X&F*bYdy6G>$>MUK5I6S%gy##oLf3$H3m&U&oIyIa(O(?;XCwdQg_s(Q zJ<_Q+E z+QvBQy3_U@ZxQc3*2um%!R}fN<3X!?6Ad9o?_IHDDdT?eG#tupequ1VKp^6@OJXHh zbGWuk8D&%Vk$Ly66J_3^)e&5Z977|QHHH}z8g_vtasqnqCalZ|G0M?D-&otqYqe6< z;cS<9=}g_2m1Eh zI>$y5=0Nus3nu{byh>Q+eP~DH*y=xbrdNj6*#VKT!IZQ zzp{pfQ;`IUq}P9`G7<3@pk8qX90 zfV9H$PX+mR9r=_P5c$6Ej%xV#7s*m)JrY2oc(YKFxWpi~aN0jNVi|7Eo1# zK^s4G{y(1}8wVr=^!-kz+5ee655Kgh0ej&8KKOsP;(xOu?okX~zZUH~=`=xiEk4H^ z10V|{hL`lk@K)h59;r4@ccT6KCY1&uYAhLE9h^?*4@|KdH;8<0`;I}xCEKICM+3x- zLv2(Ax__<--+ z&PdD%5~FKNyJmesCuh}ZdgpO9VJql;5Zy5qz7hXBOi(&KQqB;ghC@==xuO0Fk>t06 zP9Hc3QKKb#9|4B~P%YFP+q2S)6@fr~iR@9VYzx86+NHhGRMe}9=55cV;ByW7z_FZ9 z_w8;H9(Vc9j^S52%%TaNRozA%0%u*ZX%=aI1^oJYL$Jk`>M%A!9vXFk%>lSrw?(Tq zpl0w=hg{WCv((^C;q-SVMzx#+A9DGh0J(_m;e-nn0=5L_w#z9kjdd1X8i3LYoS7)a zX#6d8PuBo3);LhJb#B|qPP4>U)Aq=Ndblg_q62YqU8;1e>bqCebKZ$lB5K?Oi8e9z z?;Tq~uoM)`jF{vaI~s=&r=>YSq(_U}(`NBh_Ok81FHW0ov7KjmW0I3s}%uQ(DfjQEAyUG&~^E}J<;>bC0O#LeU4i0m+tEvCBJ-oJ@&7#fa|nV z$#umj-NFZGjz%se6NuP$cU08%Ja>hE88~M&K1+%ME68$MK0Kif z{}AO9GOYYzHh@~xa&TTb$-k9{sgigiF~W!FN^~mJ8pUALnT-AWi!j-N^%5i|IB-C~ zaJnYixx_<>A}XYOZtQ=TD>9$gV!XDfy9p3h<$wzDJ4J?bYSa&$?j}cA>$tkEU+Xor z>}bE>?GDFiH)cxi6t!nb(DX0n@;(}u&S&+^$A)%h%o(T9=M0M=XDVh=3jQ1YXA~N` zRyz#<%bfPj=+KQ^S`QO0{pNK=Bj$7!b5MnZqX~{@X6U7KwygbMeWaTv9FN= zT6uBcfFw1`NG4R9=(U#-4>YQtdu=wjEm9Ba^TgjFT?xhZhf?@kc=sOqJpK3yI6@PJ z1(~G+M}q3Vp|I>x{pU&M0tPv2-=i4Vx@sP>NNOns4XWyiYscGJ?_<-XWNPE6?sig-5@!M{;aKo44HcNdPM} z^AGSQxRLGDjV*IAtbKCZ%`&;#XPnSYCtzJULx{Fu@-H60Gl!#t&JVc>+nqCfdOcc3 za~@kXGLgIQye53A6K+&|cu+MD_E&n8Dlw<#YVFM(Oh5?%hs(4;eQ$QDycZ$hzZp%c zPF}cFf(jd5E@ZbCQ5!tH-e!6H3t&{sOqI-27RCALxp%T^&40mXr(5e!{0el}(3+)9 zR~mrxw!E;sGfDdw0NAKl?-V>K#xan6C90BcfWoba_FkRMpguE+THEDq|^PXvjkjMILUYc{} z`(ls;MQ1#lh{vy{kce}Dj?dXzBUJf0Q+)wF1={$vW6SY%UzCE8K*lNmOdb-gm?ynNFB;gnJ!#OFMd4Dcu8>7IZ1N&P1L+6 zJke>fsdq(X;#{v9=ApW@^zzAl>bvtU@!j(F%x~6ZP6c6R6CZ3%B4`*#vK9ud*I=4UqAl0>SU}@|4m_A%y8~_<9FV zcE!#gW0S_Ei(X$kL`w^X5wh~#b(~rjStrk5iRtMtwC-5x8kKG46UKoU^OG{1cQ=SQ$3U9HU?|4Q@%3DhdOZ6vXpmvY z&bRkRAt}mZ!}yI7AROrohqMU#t%r9JAkn9mar)2LbmAr$C0w#4>9iMa^uFo*nGwp8 zYHu27yx+`S0ywxT2#HPWvJcgRt}NPNcZ8uBbw}Go9*KKsF1!`GVEXkEWTW%FEP#6%WY!`O8cAi^H5jl0cx_jq>v)Iyw&i2 zfI&RSI!#qts2c7tx#29a0ofqvCP!s(HG)Je8QKDPu~V0hYe(GqmA!NCKy024+!TNL z7s}q=R869ikMmS>K?TAHIN+)p{I0RNdF8~`x&JXdXqDv$8vHgequO>6Dl=M(W6%V? z-}mD>jo?okEj9Ib{$<(kWAvVzb57jr#M`{9Y-0uQ0{zpDxTC5ax(}#?PF}1);Q=WBCI9QLH=`F zW$K|*)qNO2fZfG=P~L~WF!gf(eJPHj`8K$ON`JZ$xs?%cs0!Hz*t`DbCq@%vQc00v zex67UMBAj&T?hQ4`EHJ9Hb*grQWa5)=@^rA?n8XWxwv-^$L!n7E(nn`k?lcS!?Bsw zaV+CFtKrhNd;hBTDTB}M;Z-++`PO(ozak2d_%CQd-$jY>DVnI3sW->h&GN~D#qR?e zw86-L`l~}X6?U*D&ZdCPJ2Q%nXTOm1wc2rdj(#|Sz3$5Uqur5*Ib%VqFpzn~b6XGf zPxq4|g>b0TDSOLtEDbC_FE=}L8_E1uJB(s*&+B|xy{cAqCxCfIjT3?)1IVAfurYQIgGW^e-^C>8b!#$<1K2iN~JGWhL8jbClVb>XjWU3 zFNE(9Rdpu4`N%lAP`5$kDWqNHKvaed1)rxoe!!?&KFC9?dgd~JHtE&5`h@#1FPvCV z%P3uhYIX8avHx4_N3ayDLUEHTBum0|oAJC>+Y+-Edl#;_mJ^%5thf!ze1(gqztf$I zyJTF6Sa6=5(6p_Sp=>BFkwK2pRG(^zm|ng6OjsO4D&iu$w9vjlaheeZ>)`SmN^#pM zscW1fW8l&la=q1m_Fc(e8gWz_fGw&|ZJxudTPq8A?lV)mUOlS^(0{9uMl&!G9dYBk z;LydUQ=A)FD|Np9@-&cooPO;UJoG0M%cqoZwyG_XxPbktVX>8MD2DtblQ{Qsl3};> zyedruvFcKf?gq;BT>t>)mZ+IXJM0i#qah#VDyQ|UpB}74QTXJG-2F6HYxURd^U$WV zR6ELbZ7g!9!wk@9RAg*ZSRLXzJ?OUoBDn0f3Sm??O0y9D@C>c5{8vcsAopG|kqGH? zE2gpa?I(QrJ2BMOSfVzIlC$sog4vKz&SGWA3!J1n_?jQo!it!Ib9Fm5T~Q>$FM+0+ zsRM+MI)t(8Ro78JubqqnRhJ^~j4zn%y`gJ{7*aUW0HiU)e;d`OR8pVYrb)=UBVXB1 z39N=!J(i=8ICOEdkX^sEj854tzt~%l6QSta-Hn-> z%{9o0?D-0zD=BiW^4cbcw$6>&{Zrvvbd# zNzT|09$he1>!8(M*SbU9fJRsJfsbT#2KtkQuSTb7ktFQ-6`6-7nO^A|^EkSf-mlhPjsqF+;9=XoE>U0MtmQ zxOA)|QO+u4KeK#~?VD5>rgC{1Ip2+xxtGlFV=4D}?Roz=5X0LLW76-tKKk2zA&ouZ zvFaSM?J+`D6CN+0AKvjn?gFtSi!g^h9_gT#SNv@tY^pxvB_pQ$tMpXgV^%^SBcSWV?SS82n>$(yhZF3KR28or(SO|9@NtM!!A#tD%q5G&H7Xq zgr?>1LB!>espP|-Vd}nL{!0>)31i-^t;jN!$K>_48|C^~MHI!1q9$NCi|2pdrbS29 zf!3@5E)wVsYxS<(<1XzoIei@aS4Cfce37FZT?-F1uQ>&~BDfm%7RooI5a;M!zuILQ zVsMR=V(=Ji+qtNFKo>_c6!Wa8IlEj%tfDLn%jXbz8qR99d4gRn(W~{I(nJ!py~s7QCh|^nvrCv_oFUD- zf6Ar}wM~78S-y~hkFHhLC<6~v=8^T%2uinbR@|~N0SRNvmQfiA zIrIuL@WGK|sq~wkuBG*@GR%!r;0@w7u}Vf=F|Hh(03Y6!>b&R%cpjLIBB)rpAOG*v1D1) z&n)gShZ26+=V#AFE|zYm?67wiJwGe>Afw}tgdO!4 zhln{$h!u!&s}7l}RrsAxYmt=R*=LZ6`mTQeKkS`lR8?#H?rEe%Ktw=5r5iyymG18D z?rsqYk&y0?6lv*hke2T5Ty!nsJnaA3zI%^x&i6CUc*o$wWa^mA^{glF`?`Mj89Mvb z$!2-LJe->PBQo%b?i+GcXc?J=(a5GS^^j?m)eqkZ7IzEaBahRkCfo9 zHXVI@QS1rKIi~Bp_r)3XGU}<#RMg8&bHX zAIE!=qBKa=Oh>)1eZl2qCoro!)wkQXM^QauEI~+kq{nVc^-{Cx*giT~eWX8;rQYU% zr4Pj^0GrFB-)?&%Cr`TeC?2`$+RpD$&dk%xcaQU0d@mQu-k|dIE*!6`2Esu$=;80S z8+)nG{12X1d!v{TP_Z+qj^IA$;m$JIl;T!ov9_8-!bp4TWtx0`A;*;4^k)7t=-rck z*QI2Ff+yRf-BZuHRf?N8?V`<5y_`I;(8;9UK~Ebo?5T(7XsZA#JY}S8)DgCseel!_ zB+hIWZUSx&+aszlN(U=!^sQXR$$xLQNd=8Z39Q^SDCe);mWMdlv%M?LY(`d5YCwNG z47q!H3DQKjWo5a{JTLdqvU^-sZi(!Q)A9 znbzamgfh}la_6c1Hp9-}%l@&pcDgK&E}H^1J8Do(hPuN^=noZ^WivWtl-QQ1%<^a< zVvw8RW7WXP%aGV!DA~M&_2^EO^{jVVZO(V#316Lx3q>hcEB&sHA-Zx2894FUk$?%9gL&XhcZdipMwvLm})9k5b z8EK~gdQoInDi3ZwC@4LNl{i+17y9GfNLxm%Ys`MdaCg~7nRp=jHarE2h@T_vul+-g zYA27ByIZI8yfm5Sp(Iul+YiJMP{-a}J|#}~W25iF&(o3&Xc0^;wjAzm5P6@EHS#@q zFS7aLqo%%^u>W>4^&oSPTzC1jzlLZEGSE-j zG~^CN94bzWBvY>uckUjCo^ z;~ztH3KcjoCmm=NW~mgEuhc)GyZAi@u}BR}Pu7IK4F-Zmn-HgxtT?R? zO5toJF+BDagvj06gp3X@hPvwLL-u8D9PI<1?|Ej6MULnmrL7(@+?k9HyiX~g>WP{+ z;X}Pn+GF!v09g8VyM02Ywhv(Czfsn!s7IJC@= zg5Z33nTmFbd4iVkw5f4F4O1>qb)CrEb$>4Gi?q`sqzZWwuJa4tiTqMEEn-v8XxLm7 zG|WG&bab{q)gn55yXrUnD!awuQcS=4Ub)WTracP3LV2LHpD2Fc*oLyV3zj`Aaem+z zQ+qx@t3qkI~+4%3-;$7+_A1T zDW80AQ#c(j6PB#zYSjCzo1GscU{XCRqdv5O*JxDYUZ{BLGDTvEG09Eb zi(GMIO%#Q8v`rUThm!i4kphDt3@-u7I?;G{)hD8i&#!ntx;|rHCrU=tXUn4H^P}M^ z*6QS32k3MMk?A&>970SGK}(5W1g=!H{sT-P#{&DFQVX5LMrbWuQ>J^YNu znhB)0d}(ho`Z;rAxI!tP+p}EQ+f0o0`G1b){8H4NccZ(-5@OKX*?f7MIBb#t*f zt@XLhA9)V0OZYSvt7L)6X9}-;8=J4>%@h+k!@Bz!9}sT~`{7|Fc!d)1wiS-ltjMvP z`X}o-mNVR5UqmwWdXt+3&&N!(OQ4)m4*G4mGWaW-E_f3#ZnAuG4!}t#^(OJtyteD$ zyr(c=rDtp1F25r)mVowtimq+;S`NQhiQH_t^RKcHydsi-Fhw{#wz0<5Mti|$bnj){ zZ?@RK8`m8wNtZHkekcJqECqCe3-vRYjl}!wcPjy3j?=hGH^xVzEc^mM)LWS40mzUF# zBg+6|)BZ>X7L%6bN4wSBTBT~<%uNWXGze>n|4_Pjlq6Po!IR0MeY(w}Np&+^=gedM zdz26^LO{lkGb))H2Z;_O$($(IXK?W%-hiA2Gtal0)ZdmWABP0nMP!rStzMs$Eg_r! zQmZO-C~}u>dAvdDhfn}hSMVDZcaMSBpB~h|g;&T`LfoF*L|kPeX=vv6!+Wau5Kvx? zZgMTTE7l40xml)UNw#<97HyG&L7Z6>k}B@D&S(=EH`TlurrBJ+@SMQyCm0^Sd^`b7egx zL0OY;drEA-A2NDuR)@_9#JL*?bV$kTf#}2=(MWx(jnMNLNjydrwzMngW{RJqqcoBy z1ojr`s>Z^<7Di@b4AcA#;m6hfR5(O|d*lEmycY!7_e{w{OZqGAn9sfsSZ;<+yCvwN z6I^EYC+~7Y!=v$5CE*f}Gh}Lf(N&gguqk9@@VT6_w|bX^$0In1wfW_K_2a~Ql&NNF z#3qg8II`pOS`-p|zfM28f%Yz%D0kroA#FSK(Yx&O`j(G!%q!xh6z|xwJ?q`q>)xz0 z$U*gRS*?)ceJ$sHIoAl{I;5O3>o0Po{1|la6o-fpiIHQrlf9P_&HBF|@!fT#`mf`3 z&$COuuXRY9+L`zuN>XI~<)8qG#K!YwO9RNhRaMx)x-s~@HbcE8CN8)^L6Qd*pjc#9 zi{y){=yY!Ko3njZ9oL(>Ia;TsK1erS-qfNrtqLMxIC~m>DbUKv#O>0zWki3z=bG~v zr8^woR?1WeM&B$Y{vCTYB$3U)&ODavtngmfz{XD;}oTa63gT3 z1x0VtM4Cn(j|e0&ju*yDuc(z2p+~^dv0#$0jj*yl&0rg-$)`bvevacE7wYtsPYXXWSxwDenCIJ z%xlA}e?yjkCZ? z6>HrqVF1Z+b`^yMUkE(!%-LN}9&tJK+@%)Jks5sK4vvb9$zTHgP$h&8BhAW6@tHPS z{WFvZg}N5Qap1E-@E6i%I(fW4|MWC|7G}}w0D@YcSG7MgMcj^Vo}9!v*}3P+SiW{S z2Nrv86&EWe;_H=1hCdN|%c+?z2>7gG_L&%Jm@rIO^3>yI&*oh>XyfbAtQQHbY(($3 zOzD;&f~8QyxgE>jhvKsrE_d~i9?uhgrn!Qo?W4v{^I^mBwP*$oHySk*cGD7H+K<+h zqtQUo-NJ|A(%38wSIKYKh2BLXcB7V<37s9tzroq%TR$^3xfauA)Ir}XB4gP9kjZRu z%H#|)cEZ;;ptr&XUpVQXcwe?EdUYefg)IC_ai-G?fvtO$GBomF-ebG7%OgU)lYt?` zt`Of}3`RIRd?xgAhqeT|_K`iUCZQ}N1A0(D$-(Ed5nAol8_*W$YXg^)A)B7YR&Q7B zWMeO@cK$(QsyW@IK%HzL+GR40}rIQGQjP=V&kvvMvClMjjm71o!y>*yM z#B)X~Z#hd(P#2T})CR-gokSQlNFamA=of7-}3^m?pMEbd zc?f3pjf#J%{R>ab6cajnO;$X19PaWt0o6hs@9S;mKBGsF-?fUWt1MCK)=vXR>OEaU zk?AVn(=mKMLNxC2t_k4rSTGu>mPziAmRUlly-XKZ)$7lT2IyXCFOL#0Viw-$@k{xA zyjo9hzJG(%b`3$M*&fdm729Aj5!4rq7)@}F?uj2WwX0%|?MbDqj@lS>sM)ugBxT@PXUdV#H$KMyloarH$TaXFXN;42(ssfzy*Z>6udRkD+9)vtv6$^%yIr z18MqH0epzHLOg|5R+Ll`uXNaV>Ygrh{*V}#C^j!pvX5|;-or-02?LwQS|0ldxLtPB zrdK*%{`K)M17&?*2$rBd#iXk*Q9ntHIOa20lQXu}6_mA^6YTl)apmcrZp#NuO0J}j z6^c6E;zZcEC#h{LZj`N|If~04oA1qSn{23tOz^K+<=o}cQrF*Ydl>G}+HV97xuiFp zw;D5E{+cwI77Zsbp*r&5cC}j6cY8Au>q?a$!GDyH)_+F4DC+jb@5_k5$Gd!uB8LpJ ziE^9ybk)2Awz9Sjj2M&CW<^-5sqcPotv8Yv`tfFQ{TCxG4zrS9y~5-po!ZQxg)p6& z4gWN^5+?Bf407=mMIkSvukc-Vp(wq^G)SM8zv{OmYnCrRZ!o}xfvhMkKry#y*T=X6 zBx#g?ykvY#WJLS;Y_&encd)5FM3Txo=$&Q_2l7dE+xbWIuL3a-&X9%H8Rj2g$HfP- zvCL+6zLVM8F+t&JjdgW=EUyK>Px^j4kYx&fqkj2u&(S$c{j!2xt$SFL>4M5AV;ovxC)w4DeD zv>kZsq!!#ad7IBx_WbJ3b~?Bmw<{Y=2GXCU{P^PEHYWs07PzaE?_%#|o7*GxVgrJI z(*~U00P`V9#t78|GF|y%k@$Ka7k!Db3UxzPM`+x5%v#UfYybk&Me92YkYZLwJvV9Z zhwM!|Mkyxl>1|YgopJioI(M|%U33gLuV+$u>o_D4nTgp4wP5@YOE*+uv%NDoA(f}=xLzl^kaU}Cul8)AU7ccN zg+&YS@C@ai?6``Q5K?HDE6p>_4^G&@5E7`DIbm0g_@3!H)+Yi3A(`R;(g=4Qf716R`&*>_r=u{LA8x18-wxV1^nd_ z-s1GDd3H4<<-~zUCdX@&MtNLrkD}h1E3nS8lZWO|YRTK-e!hiOi)%Wb#1;Qxq5znq z#x(<{$61fKm2QKO%B`|QFfUHuPtkdv{u*-7AcFT%o;DN2`jtQ}Sjbn#i<>5fqkXsf z)8Xi%;7z%wvUibUuGlL((oLrxyupoj8={mq$tiEmv0Ectv`jAbAL}PcwL&MMPxdRw4L2mAYE{v6HUu#%bb9)9$;E zg%+dCYz&6&E?&P}7Y`!Yie}Y?hZ3Q2b6HOtrw|q`t*H3CcPfrt(HOYioDk^t>&-Z= z9m;17CfhdkEPdqbmh#p@G$u$VKfAhJz2FZWnxD5gd;NQpF`;fLtUz}WuaGX8PuFx_ zwk|E^BjFXuOcN(2Jvxwpfi(W8~ZHjz}gW@vdoci@+wA zRP&eH?bNbq>)}jj#$}M!e~Kc60Xx`Ysr^|{#|i&OLr#nm!`S;Pzu$e1_57J8iPGyt zLq~!{^l$<25S&R@2VygYTq)`yv(mkgQT|mAxbK@G*z)hEYzcsz^bwsGPVkv$Uk#(BSx=Z>4ukfk4p~Zy|CQ!)jR{@kE_W}V2!skJ#BP5SOsS*6E4fAz*?X1o^+PnwAiA6CKCd~*G0$dsT^XF`PZ2Zy zkF<#c9wHDr;0~PU%xvaFa%|rtpS*BTZ1jjNy}St%@+6h-e$(BPic9NY{2F^sz57fy zLmSH0OJ|!``R2I|+OiMUM?X6U<8!C2->+t2roGOA^DfNkh6H5`A)^5-B0hDjT1|n5 zSwD3!Awa0zfRh0SX+teXh1c0yJ^myg`TFo=*s$tH2zR(+HLuZwsOmKg;j%qW*Y{{4 z%1gJ9VQmEs{4wdqYv&wb(eN5H_&o}oGg5FIc>2dNpUXTvnMY3~fJVy)?CjfzV#ovi zV6;fX@d+Ibtlq63hrb8y9|psg*%m-1!;qRchjei3QxZ`x)SvN8Zc|Z7j*{ANJrc_pAeItuGwVE9TOX};EqT0w{Fufd~H5~#(d$KKu8y0pfUq=d!JAQbmOdJw2CA-(*7 z5!~&|`>J}urNDm1+N(CF0aor9_d$&QvK5HKWe*wj;Qy&*6>H&!_4sx)7()GT!TSI8 zKhqp8sJ|iqTWerHY1$d{FERbsr3b;C8(t~wziux*DAyr{2~q#jzGJ5VqW}T~jtzX)fBgLQ z-KOIHOI`a_;3N3P)0ebckp8^w!^Ho8Lb= z-jWCDS2pXNVD?0oLoCrj)PIkz0|c9ERq?rU7RrAOq^|;GD0WDEQ5EVyll_mu^hpeK zs$iJJ)PMYe4+yUxJRoG|Az?2t|RrVk0Kne|qG@#go@y;J$)0 za{1R2g2}+$$n1f-e-0zDmqK6^2qOI2`}Y&X!Cg}EWar3#yzWKm0keucd`p_@)uO~!4-2HC@KlHT!Ht=6#;Q!?w{Pk_ejePQL9?3-?&|9xB zwFVg)giTo41;&Q3u&@UOi{=C^F>&L2e6xJLRF;PSs!7snKXqbKpa<@H4y1tbw$<-N~`H} zb3{pM9-hKUZjKFgV^iK4i+W>-)DuG;4V?a}A@Nic;?W+^-ka-P`-emYfGaf@<+eO6 z@Qh$k;YdgZD6gK1eA}6yN+t5r1nlN!Uj$!9-7YC|lKz>NS5iN)Tazs$W8LBke3ngm z`x&4?0?vN2nD(yT!uSG}TQwT8h7!RdE zcw=|tF2HfxX1Lg1u8!S$&dBo|2+WXkT1fbPMkXeJe}Agi5iMIhS_BkuCJiHC|N4|# zp@zI{WiW-0l!BiHd*5a_Ge288ej@bP>vqW>vFiNwYI~kc{YHcme~rqZ*xxg?pBtrK zxv;A8m=;<|iXn~M9RB;$U^ijD;7Ay;@JaC*~`~PgUo+tyGC1fO(owgiL+XmZH z&3B_=QgP?Pq;7*gEl$&>R#>Ivu0Ir;>IfW8VtCvg)a@f15;@FM=Cau)l8zzc1rAcw zVL%K0a?$6c$a({>7u1Pg#4D(kJeaG^_6~9}i7eA$F>Ruqcq;~j`R)sz_fC>aEb|v2 z%>KMvKJ~ggoUn)op_8cwoY6U*cVb#{wq`1K*MBXyt6+8e+^HwMwE?w48Yuz}=*>Dk zYXQ;kTT-iUFH_NR6tOwzo7=4UvUQg`$mt=^-~GL6J$VDh&b%Bqt8LJGNL**==zfPU zbicvt{+90~(%E}!xeZ{H5 zIf@bHW9SifuL;lGPs478R;e0l`j~xglg?KFL@ktNGs8i(K1G)YlAb>JK-a3#BoCix zZ%8qP#Nt10(Xq#A+f~QjQV6d~b$_Vz$s18;?FWIB_fNaX`0Qfyl>!niT0^qGw;TdO zfTrQv>UH*`30FC$=a+K4(t*7CPZN0wsBhKk=3r`cnZ)+V>xWW0@kS_7xyA(>J9-6u z%oqFaBDbIxOTE=%m0$>Nwddq#q18A>O?_p>Oe2(hg`A%`ErOhP2eZ8IhC(zEuWMUH z)g4pM49ZzRK2)t`ZPkA$3n$C_)3?%Pog)O#YvP!^jkV-v!z5ge_o&)9}kl zXNU#mRMnOR*wY!p-4~I^3VC@}@$@@tXfB;iXg}vqXGxs+B~VByN-ZyM*ma zlkoclt=fY!We4Cw(B!~|-85NLus|XNgru>?ii4S(kSU{ScY-|YRMB^**_FZAQ-s=fndM9(jI z#s}mNnP)5p51UTi1G6yvvkUP(!3mlC+-(k<^UlXnBT{-$f2#Tios;dWo(!)FK|6^9 zFaZ->FhM{8Dn{P)6iXx2W8cmYT&(#R#9p@n4OY0gDAMkEINg)n`d-A|ed9)t2&8^^ z-LtwSUVf!3sz19)b~doP*fwW=|Z9B{lV_##k+R4Xl68m-=Lt=|7e5 z_7cClxhl*oN=rvxpMTn3JIi2Ku3<4ik#-CWeYSi|Ot*3B_vsK}!=pXyL zvZ`X2DjaGBRYlRMGdEJL-i?y?71qq!Hf#6B&>IK%*P^)cBPgR0b(^&p1rflqWPEOmK|>2-w_nLZ7sMkK@W1*Na+Lq zZjz-gQV>YU8M1Af_K5-|z^B=*+5%RN*9+GA6BFPAv_i@D;4qY%b3|k zMbnRWphWXt-A%9n(I~NaI$!Ejss!DLs)N)t+d|~eYZSUoUcKH1n9XdngC@^>zS|X= zyfH}=Ahy=)Di=GMP^?gsZ)TZi%6fzA78CaFnT&iGrv2lE6bjb6od$q8%GS9R4I8zN zL@Idk{E5j>+CXontqsz})H$IVzP3%O+|!J^W!L4jWT8*hS6eQ2Q5^tN@UN@}b@U z>s0);b9#jo=f;4yD6wZ@@zBQbrCEdyg~@PgW^z17`nJAX&~%e`-cx!12)o{9ic4}LJwA`1i(ICZU(jeZ zSqV?!UfMQYsmOqJq>_R`!ezeBnGTT{KFqf1PUXONiBCOu#!_N`rV`=ypT5V3cvUJA z#2R7g&3dZ)3B56HuN_dsAQ?*-(lY&O$(2sF=G3UNcm=?rWu3K%X^^o23zmjjr~L_a zy$$TgwO}tFO32cxq6D~>R{D&vH334&{o|i1EhiFkioGm=4L(KoPt}s2U zD_lHJ8Q&i2D#|4)2@H3@3hR)+YW#g^NoqAUTV+WqsQbc(+}*f$_xRYT9?p^%{UnT! zFf22fXI>5vu!t?`Q_~si7JR?%RUD9J*XI!X z$MN8SQs+XP_Fkvqw_cXv+eZtPfAYk zejt#D$4(&4t>GJf=$S^o135kyUDuv*&q_jjjvlQ}#Z-G)+&9YaKco_*5Q&d~xnG5X zBkYu{3W_AlRuuq`$aKQzaLQ!7;$WL$l$NHSJD=a;kGS(F=+g}5vKf5epP0)d#)rk4 zXFH$^uh)@iR=nPnTMZM40F-Hz=qMBRA6LJa^8L*rK2BF{QAq=re599cw~TB>nZ}@T zY7Qn&%V{Q0^U1quj_Jnwars;;^H~#>?ELnD0-Nv8MgQLT+J%7(08Hdp0T{RhYX(h* zu31zql4A8nHi7UZV&Kzv+#1Ex^)l}Z#ZmFcO}G=7W_#FZKWv6swziXA2_hM;!xAb( zPxs0DR2d?HLAgWRz4>fHQ+u~VWZC0vS7S)dG%~Zbh)pfA-7f1w@Og+K)}P(Tk-!p# zaRCug*Npxr$HOK4b$8=W$_C{d(%D{DKUFxi*oP`COmUTEsJjO=d8`B6|8PYAnrdtw zjv*oe_J1DtaPd_PL|kOEiP}y66)XEd2y0{D{LP`f{rg|yn=g(n_-z&^X zbxm)Y{>l{m`2vG6AD;C+SIj;rhDLj2UHnEU2Nb4}h@pGNA5i|lKluB{@B3>3_9*%B zR4@F$Z-IaFGox(5zchpBHRA8+?5`qsvs*xX9RiL&JY_82y6RK`A^R>S#LDdv$`h20Z+H z(%z0;yL|Sc{~3d@*fnJj14H0mkaO{;aWX(($Ui3Hdf`W)j`+{vSMd-CkKnDhU5xl? zF-|ajK7Q_27$E%52;rO<7$Dt(w&~m#$^FUfi_t4-fW!u<5CKcqpvv*D;Vk}c!7plm zj`9zqy5fPkv5__hd?w766EA?!Uv@IeajixZR6+;j_qAvMgzEu#6oVY-^{%b)NJo!K z0{cJ5I@uD4x-W>6eL75;J~7&#IpZS-1?C?R5A+X3chT`jpgioP4BDsjzbyE^oSq%8 z_c?5w;)^?)5{ReqxMvqcn0Yt9fC@ij&?2jM^(6%uI#9v3S^J;~YV%-(+U(5(?vwH`Bgw4}F-Tkrym1`7H2KDs%!cMQ{ zFN#%5bYjbaYj@oDKDNRf5|eBzt z-t;6sH3D|L0A&+P6b%FF2+7kHU9IOMMM!pq;uo3T+4NuBJg|1i03;$NTYS$yRViPw z;3u<&DQu%>fo zr#1J|(}R?s0A@gi;=AkbSeHhk;CsMO2=zVLXrg@pqG(i-lqKG8ZI{GDM=b)r2|@>b zk~M!gp{z~=y{eUY3tV|SwZfeK7tKIsv=on|)(_G=55!|${O&dsmHoVTHAouk0!HqdO zrGeEm@3d^Q8IOtr@sb8ez-`?LEYSM9>Y zaT=AxZ(=@2Q_4zMu+AbCAE_F2|NhKMH)qZ7!@;-;-g2 zhxU}}r5dcC9>qx$6w-Je6W<}V^eZxJq{xyHAfPV7E`jtA?pzz z!T4iZE5N3pZBJ*p%1Hyh9?#WYxifHI7ECP0W}qHMKlhC|V)p`MD`uCyM?H}&LFqA} z0G<{6kTDS48V0~6Xu<%1awY4Q5!)jU0F^afCN;f$y*Uj| z*QacJVh)rb?zOI-H|bbc>>JJ1o3J{cWih_`85@YpKG4Wb9jfcEQqoQoUD(fREZl7c?X&Y%=dzdjwleWy z+ZhQ$CVViKDG7BRSRj0NTK}74alUBF`H8QDb(a30vwkd^N!qnB*C!oj!9Y`+4 zh7--!(U2iy62~=S9w`P?)%0)H1+erL@xq$HgW+}p=SnJ}KBhgG!k9(LX+|<@1NBG{ecQCZ> zpmIt%H3X0n;)e|b3kH^^tr;V^T^S4o`LA7Xras4gM2n6lwqWd?bGNNcQkh0Yc?F}| z$XyXz9W>TNT;j0YhKSGRlugem_2K#)lA{HiSJ8<_rv{EzMMhE{=v(|#GsvaD9~zif zw&3kCHByfVHntnC0p&%^+}9h)w#IV_C{>?hTm6H2KwMj7oR6HH?SVM?1ZA*m73o#r zs6uymB2|`1cr2(m8RhMHadk5q>!@cw{-gF=!z!`?2!OGz&vyhmh=jc`MY2}ym)Jhm zTSh+BQ-hT0JAYpdGF!@k9VxsTeZ0<_t1*=l$Ef>&TzEsFFyF|2FQ|Fxfnn7D6iQLP zdp191gB>gjclbQp7Bv9nsg!U^Z~X!i`bZ!;5mio%DT5+YR$+2=!qIZWfkLvhbgPA7 z23yj0|87iA$?Q4Gc!BOD6R8so#Kc4Ejh?&vF+wFCjX&;fPn+<^RspJy&D|ICt|c?S zp+CwZTIkSNj!GK2#?g--q)D9*XOu0ilr%1nnEI%FF$=^pbj`YcUw)z8No(vWS!lFj zukkX4v7q|(43ap@{IxUTfF_7-HG(SW)2KmEB>t%DV@qk{3ygYi#$&t4pcv0i3TD%D zEmBUaSbb;`y{1a@bXmh)Ph02l+2rD{Z~j4JNWZ^Cah_=J5S4!?KX(9S)dM~d8XOs3 ztbAQoxq?SJWu2>NJ!$_K@vN7xvKG>DwAvLxZZdY*j#TfwNztRt?bNYg;d z4Dc5XMHtiibj;InLC4pA9o(B_`Kp1T?(8sCw5X$((jy(mxn`m1Kt!fR3yjLP1w zu<`}~i7rsDGde!{C~K5sk+*$@=)Jo!zcw}0jZh#4nBz|q0#j8A@XSYZqCu_nC;%6r zBF5FaKm?4Ag-gB{in%y-6*l7&3JXgvwp)j-6NVM$4_G7)-@BV*soiH2EY-=47J3a^ z(d1nj`02t4Of48@Aj=>&kihMPEE57yIe;go_&X@QNIg$0sQ5`? zc&cJmR{#l3p-R>Pdv80sB}eD!0~-6jN!AV7&8KZKPIPzTF#yWzayLO;B93WtYHliY zAMf$&6*A$1IAtm9vtqe#T7iNKzC(P$zWQ?f>GTinR#?U91imy*Ugjw zgwftwTsE#FB5#B7D47#d}6dH1xSkVzq90JMXtWOnp2X39g)u zx3bb)n%AIQmaxtXF0mVW(+BvHbsz+DJ?k!Pc+?mm z`?iD$_N4D3#=L~C^K-1{H-Rh7)M|Z-x!?oe7q{y%{A9TMShg){t*QH;+&7&dRXSu! zN}|>O4z`7Vs;|l;x3;LM%GwYK%`E;T71TmMuqviUcYN7+TNT8x-fE;j^@Vh;kI`_? zFB17|2Tqpv!KFogjdo_Xdgsc@)VhRV{1D)$kRjO2@_Inyv8j!r4bMVXn*Hg>p&0~g zi9?`>kLr*?Jv%bFiN@_Kq_|6DS;_>guE1R>9~JdMi*z(96RL|Vnfao3Vd@9DVaW*h zFTJ5>*j||LalJ%!e65r8qO%nBcKm#wbLjhLs$T6^pD-K!mP>CEPWQ*XYveTrr4jVe zCjH_k>wHw;xgFcz9D`CkZD&Kb2a?Xdk*4xZo zE`AvCnkYA&{6OJq!gUhz4h88bw?G_SsqI@!mKW6#@SQ0gsCp)i_W;`*?j*tg914a- zAIcNtCE&KcdyxxRxSa`MW4@R5uDgbOY{g*&{KinhySyZI)^!F}ZuOfPs}V}Hcs1XK zA7zu-TsVD8OQgyNPvHp=__HlgUSc3k+#I3vJ8>ytXKKuus&5M*o z+&s4Bh%S~SY=?<^b7`WlZ2&09`zeoUqnV*@<>s=cvE{CCoy+!e`RBn})RmkUjOL|3 zM)M6-oBiP%(Ww#=j=DEmr%Y1sRTd+3c(1RiARg{D-5iyf zhkQ`idp2Aqi0AC!c<^ES@bHsE`*f`$yR6nB=Z%7Uw1?$H#RpnFKN{_77ytb?Cg=XR zd-Q&?Zi=U2$tGGH#n0Dvit)>;y9)|6YP%#*hs4xAql+*_GF`=r#}zEu-P5-rX(S*l z+fpM$zg1fD-+q-#?zbBuaAWay52NgCsdG{k@mAHQ$6`OZU&@5(LG!MAEe>a`#eX7o)jrK8n?M?cO} z$r!qC{kFY?#Ew3yK@6?ww@`?;UhKti5Lyg!sKoIHX3q~G*Ct^dm2AvwJ6hM6W7W=+ zDH_2r!NQ1atNwW$L&^XtUtPA}A?YK$Wm2`929ZW|pIX55$@~SgVy#zuA!}SMjsg&F zm?>cfR4HvMD14WLt<;DVs;!YUW<@*L1}Js!M<)ac`Ym)YX>58lp9cDK!S+FnPm5_$ z<0^s2I5XvbpPkcvr^0D_Za^wmMfCc)a}rkT{TC>RDr}sRnyJ!CSjveQezCJSVqv z#ic~9G!W@zUV7ZI?dJT^Y&NNgXN(%xzUPB*3n43CR)fAi5sFc$LqtH2Ik0&Wt5632 znIBF9HS;4j%{Mq~q*rR$uy7wk5K)7j;+@23Gw!eG`e-{R@psA0?{H(g;Lt^G7I5_{xL zz%qQg-J^x88E1>>W?B=^c6;6lppgA~3c?PALwcY9vkU57SAv;wZ-cvTg1C8>%9JA- z-2p(wCZ{$!nWtbEusoz~_6-7tWcd?w%8RTOD=jQJ5GUW}h()%UFq1(K<9(n-_ThCD zQUrNVzrnc>i%{AqHuVIIpLK^3*>1L*UP~M2A5%Ve2_l5Qvoojml*C_W*P3&{7RVjO8tYIZ{x1D#p>BksyH;f4%GeH= z0!+!W>$6>PK-_;}J%;xY6n~3e<>FSubr{>iq6sx|(e9W*c9dF4J{%OHCFzBL$u2u) z=ZQcD+45@Ima3Ytx&)Q2P+*&ZhDJt1dq@h1Os5(T^t3d@q|JLylVZ5>jph!@^;#g; zr&JSFe?gx?rQQ6ks_S_99wO%mcjIirtG(Lb08jYl#<}~Oe!H%`qdCp2`}3=Iug6xD zG6_bTp0j?m$=le7_gDHfZ+}#sFMQufU8?_GrER%wcTalG9qjya3G=|#scqDGKujh_ zshc4Q(Nfi{RierdRRpKfm~itcnjRV1?>a&C$Ibd0zjZ3OUlCzwBUgiS#IW>B8R+K& zm%v8nU}D3&H@y&4-;Js!!$;$zLZc3@a~pC>^BCO`R@1a-qYa!Uh3y8jQdeWw8u>%o zs_#d-V+>4C>Ab(i34d7$OqO@OAf9hvHuu#f?92+CN4UI6LerQ=4ke-@uvyn8vH4zT zDA!*p)Qi1-F!!nriDJ7J5vNawnCDk2(a~G3=LuF@`C(@0TqR$c)nCfdB(qu4`0IEr zd}~O?u$k%S#K**`Y^Od%VMnxPiFNfxCt`DwV{*tqBqcfIz97G-YUGB5C)YIMBZGm09LDKl8k zexM$i^(s{Ul6#LpYOVnWO|EN}&6nZNBpCOubTiMs*Ju-$ptU)WY_YzVmto||+vT#q z`LwVe2V&azAS&m({;e*y%d7aCO!dc!_rigP6ujdIc@n*i?ex!AZgi_D2K z2z@`C8p?P@Lvr_02&`m&TqQ#_RVqm@{Q}Wwb(P{~I5D^DbrO3#gR%77=yvwhYl3k0 zz&c*J5Pd8WrDwVm(*QTKt5{eBC31WrWduhG-&}k%UW_R3Xrr)8>xL8hoM-gb_gr6% zV!3xs643kV9fGciO_1&V39Q9}6Xf&kd^Q<~8ddxuVy>&iJh&a+iCSD#36|ijwXE zOKlsDMC#9GCMO3UwGS@v4!U-~=skDg^u1WxZ;)G$e{XQVRq<{lLZh@8?=qihY)D`>A`*;m zgR{C7OD)&;VUKjvA>hnP{l4`+JR|``g#QWzmGH#zGLX~h-Zh-VCl?qq7675Sy-dEU#`Pe!We-|J{zMW8XwTYDAc44mUiSGKK6Nbcb0pV` zm#;%FvCAFT^XHURh0iNphG-9QOSG#eQ!q15Ue`*eGibNO3#ti+hUj50iQt-d?`0rr zi3g(+E5?~9!s&-yJ7h&ps@Kai;h*SFwG@9E zUqH}8sThVCO0%1DHq84|uI$I&ju&Mk{QSru@?i-Mp^v*mTU22#CEUs1((qlm0K%|c z1NC9IcJwGbnCFnR`oGp5tfr0BDp@(wC;TQ1`?#ocJNxk*kq{0$m9jtUcbw~mnRUfn zIX>2-ed**l--WF8=~5!HCy(K70(HMxT(=}Y)2W7do;>qYUmOUkl)j2@+te;u%a0f@ z(N#TYs?U)TF-oRYog>lGI;@8@sp=!pB6(XE=Ki3BPx7T3$#d0kneQ#CMoUc1E{H`_ zp!+;K`8@jDfEj(-0ir%imiiI}i`h~i&D%}(qomK9+EM|Q{qcQYenu^yoYnlz-3 z=d{0+vgoU8xz3<puW(uU)1-;+=v(j1bii}V_BlRgBm2COU>#gO?nL_Rb6HS%}~0R zMk(R&;R!qQY1=Dmp}o3>8R)FG9`V|HpD9VoRSg;!a6NFMyJ=MFUa*bwwYCpd70vF? zr4-8qdcTx#oqJR0`u+JDpiosaRyS+5lbGo`Tvv?VkE>1#gj-LQzDYX{@5ul_Q0z~n zPLdkNIia77T*GYkRKC5EygE`Br5thcvzaLUztvOc@VaOZZqD!Vt z+wp32UL0L&WhCQ}$}491Y7OFMV-k&!8DXpeBz+qH$THhzQZ*$Bv`mr!h1y$b;hSNF zN(7?p+{&M!qijWlop1=N#gE}*pXo$VZR=a9d3^q zCS9x$eL3J|3*Eg_v!#Vl`G?zmN9fAKYEMb^vlvg=#~80v!vu|V`T%K43)sF77xhy|=dc{OBpx->h8~ZPPHx#6k_<94K{D5KKpre9 zr-OFq7Egs={o#=a9s!$024)|gMccu_ybNM5pPMmq*3(?2#|DwOCk@#%-Jg`Cn8uF( z7k6*{71j6sk1K+dN=PZ)4FUqvAsx~%bW3**DJ6&kBHhy6(%s$N-7rHB&G({DpYP{u zt_WvzEn0<46;>$%xXeviYAbF|AkrSQ-~wOaP?cEgRX;|U+1N)UGGg#ctu0SAMC|;> z)D_AFc${^F$tNk>B*U%4+ktrMtE-v{NO@L0|CDqN7j8^ zm3Tm^+d=jY(~}O*&2Mt+H73_|9s3sa@qTqOZ!=TtVS7tmBN?G`&ShjF?^v~wpPjjQ zU3rHJR2TBLyA~^DSFZKrKCNFrL%W(}oHOd*uG&8~)hvnV-;ao}%?3McZOf!A4(0a@ z4PK0x5-nDEc_PA(kFpwDSN5fj88N;6NG#6-2(CLmOG)U$tWDXNWjN0TT7Vy!Tlwz| zQdIoN7O<GjCL3dEmk{f`Jd1{Wq#)yjRb>Lw1+yRuVMD9Q2xJT<`&W>L2DN(+r%85Qu zmuvn+p0j90R*&6lZ@|u+a(8H->;j^ei&=$P=t+XXlOGaJfrmej@x?c{)?~;!bQNUf zHb3oB@5L#P*YxhIn^tR~*AOPz>eK3P0Ra(msh{I1C%CcB~N#V!q=F>)2=lb9dw8Wk89u)!oY zU0OZj?lHnTsRZ{q9&W)#>o!8U#x*7F4H&xxW58qnk;8Oiwkxa59N8lkS>MpO6#;!A zg3HcW*Jy~qgiD&{6j7y3NinK^fWE^x;u7u{^TJdO#L#uCzjj&RcBEg9-~`l+sCux% z)NxwcMQo=QLt#eqLpGsPHVDzs19v{%=OR?k*Op|o$kPHb=4g$oI}$CcYwseJeDOAJ zi{QqlCexB6<`DE$K_i)XddO~n3&_XRY0O*5E(YQgej}cIjbRRNbG~0QdX;?M>&w?> zH@HV0DE%~MFE(8e4wGK(#ylkoRE6Dt>sqOXR>!|DbEy~q%LexIjIe77$x&w5ZswO+ z`9!JZd`TR8TE3hxb(-$|BCwvmVu!%+i9s{oS39^H_NO(D(DQ<`>$24t>`?BfIa}zE ziF!g@BcO>PNe_}OX1?(KHaX$CE_>o0&IMu9y-uRNf0UB=Gh#EwT0oqDGgSS4v*u9u zGkbycf<`pGzx#w}xuqfmrqtJ;Z)e$MBwDa(TC}}g=n2wyYDT@~{ea}hUSiXv6J3@! z6iEM^AB0I1F7kq|65_ggm}xV~`yqE^tFskHs{d!qQw?$~4VC~!4yWF?VFS>M(+H^= zV(3xqAdru(<@T<%(iYGDxu%p<*e0*1(1fGQ(&`kaR~f5!du2g>prl8uU3P&_>*PRt z)|-PIWzMeTnc_?;z_UR=pNr$E@iEGGb>x?wbe}9zK6vojL$$eaC}?a^yg_#ehHt8C zH{Q(>-BDv&nx?wQlt~jGarOODxr>@0+TePfVisIG7BT4}Cm_!7VxscQb5g1}j?Eae zJ(yFQ{4j})u?F(daMcmYKh*na3vni}Gla0XpnZ)|;0)kEsH3E+Q==G==C*&x3A7mN z9x0YyTD(1gVZs8Ci#93L>hz0}Vi22XjC&)UH>mh?srS;ec%Te1+b96+%LKUTD(Q%Z z2es(4FUe`{;Fkbt6=FJ{P7`qx0ePQ|+o)9LzyST9R(f0jd8!-pCxXrqla~?;twNe> zy_^%aeyYP(Hh~;G|FUh^oA=H;a7fgyFg_P`fl(j_P-)nsYkS$6EtMpd@*_i21_%Fi z^bz>9G|T+M4g>5qt8v{_0293!ijf znh(vmM6hpPTpNwL>6s;g;&l(~(ooXm8%>)q`wNb}h+m_DZ|8+;16`*Htf`=l0wZ=- zH2b#VLNZoL;HIiGJ~#oe_h_a?wyWsRn`IrDlxx=jUZe3MZ6zILLMo+UD#vGscJ$CB zZkI?z?s*d&x#L@lavl1Y)L~&TLLag)Xi%=H%kGV%V3Bc9^zrEOI|Q923T|)tj|1Fx z&yqem^7=jhlC#HNCI8({U~~TYNp-fTf1Ss{nZ9Da^K-etmr;mct1pQn8!~0mcE_lz znu-hSxgCQHf4wI@sSKFw_m^A~uHF3HsJccKQm~X{>-g!Ez52m}% zJvy8^rBW|53jf@@s#Evjc^HHkAd@&Jx(%?$5$-r=!vKxhPrSxpoG!S)s=A%ODlWLuBDy|5CpM@NZpKD#TLvBA9F6J|HmV2k z`WrxWUJ6bfEN-e6ED>>bqKIE5XuMe5zxe6kc>m-e6JScO+K`!*JR5l-f(M5BePLbS zNR1ZWvBqZzZg3Nz2c)^9#fjWn`S%%vk%UVIZ(M7?5uHW1F%wb)fQL%RpqBI6n@b%} zVbC~7z5&6 zud5?m_SE~A`D~eCahe`bs8zb~&SWt?z;j_i3$P?^LHeZ^NgC-ujLz?{c%vS@x9)hh z&&ya6;DQX*4h)3EXijt+(!fKXG>UW1+xpGxKsfdqKCJ`+>IvVEkuns+AFW=8X7Il7 zf?lX$OXFd@lxfM!3Q=y40$xM>Oz3S0oz5GNt%_yDizqeS@t&Od)+*xCqoJJCRhhUV z9MISk1xt^GXVGR$RWLbeJ5hg>8(s<>~&^9^ieMV#o_=IPH~ zgYc{;h-kf%XSeN0^j>TOrjkM`ovjZfEc8bJd-Ad1D3AhsE3%A;=CjJ>Dshu)PT-eT z?lQ!$-mvKcbhvn4X4&$_Yao7N|EYjlk%khmx9;U26l_OOHJU78ig5~a z)!Y2ORVsX?<)S$~R8W2tlus5wwqga`f`Sc*3SV9b>VE-h_Pm=-aFyrD?9XidR+9;Hm2o4+dj)a0!ibHuuty#tDQ;;>^)$q$ zv}9I#t7|~xAOck(&;5ha^jCFUS46a$BL6U$6{J7mT#yij6wfrqMh0}K>mBr@2&Akx zm5Iu@N*z;wR}htcmD;-UocP?W9*O?rI$nf`bOpR;Op%u;Cg2q! zyE@~Aibg(`R%iYh>6BTr)fb~Sn%9jH87ciJTp3;Xcq*z1S-8(i5hJMIb%(?cZ-!EO zo`PLve(yE=yEv|mM#g%Fe;B+H?;hSzBH<7P(B|xcMFJ3yFx!j7*U9|#GXAZH`Yvby z)RM?O<4czQ%gF}JNe~W$>U)U_h-_X$C-iTL&L6#vWmaIA9xoqe@!vm^&kyw|sev6? z|5g(Hz5%vtp-f&yLH244h`Qa)+ z*~H)Z`e)YAf#I;6mQ4Sb&J3ufzzw{f5v#)KHfW`^bd~T;m)x4+=&kv|CSKpq` z83R4_MkX05|5SaEpdeB3Alw0hf>CQ-VST;`sA-Rw6Mo+inH*@eevj-m1T@1l_QX_0 z9r{BEmk5Rb5rrCZ=jQ^jyw@)ZT;+hdpbdHy2?KaVC|*;4%j)znB~)=-ZxNxb0jQgN z_9+i9#vt^O*U;VN7V#V$nl$gXf^(DHLXzq|XX@j!tpB^+w&GhK zqx9wkISlW^`5ypnpH?aBTgHc^{ilTURlN`J#50U$XcXth{FsgUvxV0Q05=7AbaX1X z9z`EmJ4&!SKpv#Dn*F7`%J5+FvY=346rXmjBSsSkv&5gD*TE}(gG1}!bJ*OI0QUOL`K?RKNuf_wTHr)A(SeH@`O<0&20(YIgJTO#{$QpX)z( zW+mvuldzMmAU5`2AQG=Ai`k>hBL%EPfzAg13oh(w zTh8BVA0I*F3_Jv)i1weq+Az^vUfV=;!Xb3o|9&JCxC_}dAyO;~b;Jid%OH5TY8QFH zXc4wcgDh8AyvqYGM;2bshtoSEE!WEOPbHP0<4Zl6j%_b~OTzy#&vy?sw#$0|_B8)( zjcEyhDgXcWw}P|V);hbt^o$JRadhhZ_M=I#vHF=IwAts0 zSa97xw*PxAQha~V0pcsstUcvXuht7E;B_5tQ-G=i#ja5R%dC@r__zp%b*(LIbwk{t zbEe#6qy6aR4@P$mfHdVfEX(J0Q{8b0uuDdN>htv^aLNrk9m4--WAnKH_P@GVKnJ*{ zKaOuG3Lj+O7hPz7afs0qPF}a>?{;R6%AX|@f7*CcawE=0F9}=8ZgL&rWc9woEi(qN zW4K~Umtc4EIXD!K=7oZS0lIkD05LT#~8s%|@rq9JZ!M3`4K(`0uO0?|RO|2)O7ptE|h5 zQuxF}gDTnv+V%pNVJSwCL@?{=#ESj>6Ijoh950#K*s%pP>J{4G(LO_8*0ovra2qNt0+8hg5Y8_AgxeLofGgqkknLql<`wd&yRv3hlS z9-Gzmqv*`(!Q~I#$TCT+vXe!(@5Mr2lLGrWD)_eG``;s!g!LhDo+x+c)GP7DasL7& zflWvQEip(qZhmrLJO$x9J}86s?+5LN0j|Es%?Nd<#YTUe63|kR5zvm0*eh!Kpl7_8 zNw(b|dsJ#1FclEPa4BY8ee`r^{E{|lV;XQSMH;W{eT+SAhD#cadLw)662AbBQ4m1i z5GBAiz%p9vls)d6bqXE%%5F|!G(U|zdgh%(uo3s~i5El&tcwtnvnB7rAhy(Bij6$r zU_hS0e$AXre$iqwj|ocO!9NNVB*eS+x@Qj(X3EaF6BaQbpyJ3kLaU_v>f$vaqQxPu zZPs)QumBY7$WGbUvtT!B&C0!v#_PEqiYk2y`8;Jwb`52!MeI`t_0u!VSMT(`dwOhs z3(Igya9{t7Z$AT`|F`A{7*KLjtYP*o%g89L68~vlJ$1+Po0A~{R-;4tu1v&+ck;7K zQnS{Ds<|` ztQkWj%+I}AX3@61y&>01n}bQrb5+pDh2D^z?Qn8BduivMLM=a_VP|f#f^1JB*So0{ zpzj#XRBAvOfbar1t$;Gk;F@BNEh)YEpf*6r(XU9s8qKkL0&r*jJ$IhtbIe{t2>tI8 zv#TGNyw1?s%;X8L4$^Yt(qj2i-?x^^0yKMkH;}EtIOe%Z>li?x7Zg)>GRTwR5x|pa zR^B<8G0T#^q&lQm>{E=?t zn)t)LL^Ol?Zcet-o_hfj)5pz&4eRngm6O!!*e!~WOAA>5pZi%JcXg*J0ueP6=+KE5 zfrx%+_e~s7uaTmCZ_`wWLvx!f64OHJfE6+8$g=@rsR10Lv{Z z58B*PC!4Z-AIc`3Y?d0|yYWpi>u9@rUA=1Lx#VlvR^Fh)J~NyD$Ill00Z7~tXNkWt z?4*X7(aFS7_y~t>lS=l4PoQ*zNvEF1(+yUIwJW&mmZd2NHfaUb)Hu?nbPUn; zxla}GtTqt2&sZzR0v=A=sdRwlnMs-*_G8Bbu0-o$8wTzHxa55N5`J79Z~T{nN6 zCXZAQtE+3rGIAyawkep$tCvg_Mkqv1T?`GRy9czDLi0+R<%BbG=yxT5I(-A5JmoLV}v%W9`2!V=%T?t%a3#Mc5!l@~p*g_p~YDuORoR2PA~;-(UqGXWjq z*{;i(X`AZ%k#+xiDt|cgqFN1su|U<05pT1y*=cj32Ra7>mB^F6Vt4az>v}~WLzad* zi>QWyq$SQ_J&iRa+g;Q>Z8o;4vol**p5!ONj8|GMOIsx8vTv~U{<2W5sYuJ+$~K+Y z89@kQDSx+LLS;bze46LS>jfs>9DJ`6*?11j{_J5m(c89H zN(A|j438KKOnz=$KE7pW@=6*cc~#&xnjiW(bkVP)U$y9Bq7*NVX^8Qk$q|VM_OS?8 zB!NkiiOmN^|}_&JdOi; zVShDgC{ieoPtS6pYGeeea;g}HI2V-X^ZU@b$!bp4OSJ>eX*(tvku=Sun(jOadh7T< zD@5~qx~rG&$_8isvPs+(0C{xw zYWw=Aa-fJ?xJ2UpJUMr_X7#d#B||PKH;sJt$KaIaH$mQo9oknnWn-D5#9huE%uVr< z;Qi7-&kweruSch=y5?}M7wVjOur3J9-?m_hbnh&#d4G&<2peGo!sOo$uL%BQy2H+w zl9XS`cy52uz@A<;DZt73`uu?IbgL?q3`u)i?j|6}|K4$jm2-}Xe6Er! zec~d2Y3?XguA^q(l|KDsW$^gsQzJjNo;Hr=#ULQtC0tTQ&u~c}iT?%Y4^wP$#0DxD z7!)=Wi14-xArnP4TvMsEoLPPMSt0#oYmn!7{>yxg4OVrpAUZ}*r0?#vS#NfV2h}wU zs+MjX64i8L#ovUs37-E)bI~W`LHdN`KE?kdriBmOWr#)twDIT)&*BHURK|x0ZZfZ9 zTXGhJ-Z*B2MTUgLbGcj^+LBL(qO&3RoIF%oRO|eyc)AH^8co?aie9kUElthR+hnXX zxWh~DjfoiE+VX8_Jzr|7Uw4OXa)7g~+U2jS-8qE=ji3b_#^tUp|RA$ z?2+0XdsrQ;%4!bM-CFK;UPA=rJ1v~~sc`n*WZ4GLsX?6kmI%qsB?-BDtSMan!KIc_ zfETuzj}AxdTF&i#ZR;=$hiXCrPyQrpf1zB?gI2zzz_5k9D)uX!wv)<*_>1`|In&qC zr&W=f!~Jo@)W1-|wh&%NQQIu?hMwq44t{6xBWE7}f`Z*EZOD$B4wQTPeu-OL|2ouE zM%EV_A=(gi7VUjCZq}8mRb$URnv4mkkNrw}46cKe8g}s};n)(bp44S-Xb&*DP|FqZ zl+3UBxxn{&0j@Ga(c6{aZ|m0hWB(jx%VEG;^7j8RbCk#wfI|AxlTp!7_KuC}kvT!r zOR^}g}VAtgPWY<(*cYiJG$E#=&V7z%M%PUFdey|b?GABu=qcZf!xvXnh?!4r`_>aqiB{W$3Q37gkP z+NFTNv%_izE$aiXT-C9c&7OM(;I!tkDZNQXMcx#3eF;Mva^Bd|t#g*O=_tI>Hvf^! zEQBP@=;~b>1j>Pj7|JGaRPrx>>PUGAK#9FibJI^gBUA$p_eSh9{5*~lqJP47q-ep_ zia@>M?qkUhKY?VcGrpd_2XukfnfrSZv{4gM5*tlpr$mxX365jW_jdWE-zh#;&=JW? z3zhHmJ5vbc`5`cqchg&;mzr(*9att@@jjvzwR~e8C|`Ga?(rjv8<5jrv8vtEaD-DjYRKkht-bYiT&vDgaaypY?#N*PGGOorkS|5Kpyxp0C4{XO@p2S?oav( ztw{CrsC;E-8g(W9SaWs4CuW_q72e$^=EjOj=t9(`$GY&F#FmN?Di!mNW3thjK_BX> zyl+YulQuX3zh5iG_zjn`xR_82oeKRW197gOX4O0F?f8m3_lCDv+q!DpZQQk}ZA&_+ zy@LtYH2^)8Yvi3$}l>Xjh7A_g4{%Oc20a;Yq~jC|T(RHgp z^OSlmJO%H5`4DPJW1(KoP2I_j;wo9apyUj({d{#|uaQ<(j+dV-{OWy>h#b(`@m1Vg zQdiFB3>@$sBM~=eQ<7l2VZAO=@6v6;fVNm$aZ+x_!;-Fip(Cf_>D^pCzo%yeGsTUp z{W|z9yq^?88yiwwzHP>Ko$;q}$9X~D>k=|G-PTGGhgNB4bL%!F@6m?|Tt&VuSW?~` zx^Gq39096X&#j2RlltkZz#PC8+TuR+$o+8_K;MRg_sHO(gNT~H!Xcy&y0rdp40GuU zSk(*NRKz0?c?BerRfBn{&Iq|(yUTQ10TG-N95BEdIleHI7gYzZJM@S&m+iihO)*Wq}b5BFbKR2a%vg&{q$%$?v?%$Osd=1n&eD@FY?H2wmNfN=_P9z5f|ABe+ob& zEJJn8{;LI$lRfOcI6GJ5skt-1QFe@|mNKP_Z-(0e$1jcJoqm%n-Q#U8lm?`+xFap= z^d9lp_5jR`h|EKw8j=1pIJ!ozNM|{(;`)>*8H4WU>e4)FHIBA0n@hzcLqwT_@H-&5S!b1su1(Uw=>Ptn>ZwSd8n} z>A}L%tY~E7-o({YO=a;h?XI>kUOXFLW~#zaV$K%T!R!xlR2R!}Q8Wr#p?N{Vkf68Q z1ss;jy1Mb2|Ey^VMIfb3O?#Ko@NU(niPz{m@9@gq&)0Ar)>q$X6@P)ke-1j}LPpse z&%A~p3(*F~SNU~A(x$JA$R4u#D^t%9@G-z8L1AX3;#fE|8$Kw)@&?D*I5hR!cr{Tj zjviD^JVI%Vrk3!Cz!h^Fe@7i;R# z_oR(E0Qg!*KANfr@B6Cb#Y%1bGwUwqulYC~}cBMu7d>YY+B|@8y zkT4g!kAFcpOn`rv0zaI-^8?B7Lbk_MhJdMx;%2EFUY)pr~3GLK*8gdfc}ogcliWUx@j|ue6fb zF1~<3$Ek%3Gjg!uI#N#@TQ)ea0S)zddFcpF5OaY3t17v{;QkdwsB{;47*&7UANyy>M~c>ePX@}pMeT_Db?&nHar z5*7>fT3F*r?~7s76kCjYqgw0e$*uDYto@X}!0VkuFiO;nvPs8W+7ra7q|~<06y_PU zIvxH0z-c}rm=c*(S+_CW+-a+h^H0e>B=Wd?h-T8J8%W@E`IRpg^7{Cwi&)b;Xe?8D zi{Hhw#Wvy`cLG)O}AUd&OFXd1x(gI}j z3BTJ)(Rh`&&hSsl{)yfj;J2UH@FvU`A$-ws1Lr6!lI{x^O01&6>^I8vrT8;lMZPvq zv=hcr%v<@D!BevQ@U>_qBM$A!kcT_^BI=WgAN+>@3kFU5{mpHdu3EmQ`DzI*AVBdZ zLI*Z7f+}i+=h5VlXR-SJAN0@%L;|?Cu2KNr{5_6MAlmz`MsI_N%LBAIa6M0hk*r;9 zpj@y%lS$62W>iRl&JTyw^bWxMl^tV_qIm{0T$aLb%!P}oeJbt`syew|Y}h$v5F-8` zR*HVrjL-ZSYp3#n4BF}Y#d(FOh|@Zvjsjg3Zpjo<|k>q)^`tuEA=o4mU{Un%r%&&)r!pP?x1Rk z4we_51K{}?%3Ad{fB!ic@CJX{mc9WX*EjI!Jz`-WlhjCGVN!%PyJWviv-vOfbCG?6Yu|1@&82Qe=hM|0`Tx&B7e{RmoM-es{TjdUFUyQ@i8Bo z29P~IcKm0i|8Wxkk^Hy#uk6-$K{DhF+4V~nhby4ZUWU`&%` zsee)XhmrW8KS&M11r_{nR+H>OD=b6eg!_M<8yya=XB4p!RBCYCf=JvQ$7Zq)q`8lQ zA!AEWb65iCaie$zTQ(^_aWSRiWrQwiZ^smD|3{V5$!V-KjZM;rH?0-`?N`(dM25cv`L0|*R z-yJ7Jq6N#8mKQF5j6CwYo{R+qS0u84Om>QlO<2{R*9RC?PcP!)OVm5kQ;~qbSn6*l ztyJn$XjJLdI-p$f$9jIhIjjf63ijBqH~a6mAb^a_ubM(I_h&c%gUEg-dr-m)4@=_w z@3-jrz+&p!jWDeH?=n?&|^ zC>{m;T$KrbH{^$REBbJ=RzT|;`n3!?+=D*Zo%cUGMzbVIAzP2KF!D|9ogCP?9q#fv zMcQ8OHen;~MHHq6_7I>#^6wx?9v1`YCRi|4^^>KW`ZmQyyU9dKd|w`}S5f?q``_wM zMo_@Fx5P$UjcYyIgRb-s$I<;AEQNsAP2-%Hi*j0S2kvRt9XEB|9k0Gs4@G7ZM!h$1 zc~0QqSP;RCZFJnxz3xZFT{}PM$D09LHWT<@7HxcS?~@#R&AF2?#CZB5(fhPrJ%8SE zB3%8=olsA#PjQ@_EiaxO9NKvjWtUuXdC^>!zFAGN*$jMDs%%);dZK-bAMg0Fs+mfa zvY=C{-}PN0`|%jmePey+B-FuPCq*)Ny-sJAla{_Jrgp@xU&#R45;1CgxMjbywf3k ze{+m>dDUeVy;^i~Rw)Q6P;9bSXgN7POIWq8;};k%Oc4FKRZ8$+6CytZ=b4=;ihC_Y zDSgi7Ui5uF2qd&hxVxPFgnhw>_+h%2hpxtPM?k&Qx>%W6ZCJtN7jQf`8GV zZIqlb=`1wh;6z4m50ZZFW9Y37v7LP-1QFC9FB_Vk0z()Vi@nludg5gf=L7fOa*tfx zRDTSMWQtdE;ffdR9s9VHoek~8E8@RWDe1!OTT*i;+ND+^-0hf{->9r z1Yv9_u6d85&zasg+TM6o_oepx%c=I7e4Xq|xP2{shhJX%6=Wu71p^IBXC(y081z3F zu@A*V!6Zcou{Lf>osnVv)0MDI$Sw8ihI?-_u`=jI1)$+C1*zr=`_S26dFzlOaWXho zFcb;SH_TeFG(CMe-3Y7xb#NIjx3S=f>ut$Lfp(Ta8EX;|S!TbGQf9d^_?`2y6aVm* zQc7@MuBPD5f5%iiL zRVwSJ)_4KhRrsULz*FLi>C}3Yo_?Fw{E{$EN;XZ=hLz{ou(9q;&m1eF|!E7hv8$xaf zae@b@7X8KBT(5{{L`^b{H(p;o2dm@gH=;~-XU*hzRt#2lH2NR*4Ic@N!&@~H#lmH$ zABoM09d_XkdXFuNuv{6lhwjMpIrgU{t$`QiOYz3y$jzrh9hJM5_(c_ zMJpaY`SO|NBwxU2<+!itU1solBoUg*P2FoxhdvJ72&MuW8ZQ}y$LxD4CHy$qEG zMa7+H4}Qk`rh*0PHI->83A!^}uhp-NygK40AMPxp6hAqu_l$Pf&m3JXr8LZ*=dzRg z)(eXcVf`35U27! z&yPZ&hq_{e+p%0c+uS(5TTXq%;te!And+|O>)Ke4JFz!Bw-~<(oLBtdnYW}cT-*WD zeZqt}wt3HFG2w2vyiIGNOyd&DFbO}n>w@*&Eh>0Bjin-q8|otN9(a~RtzO)-+vbJY-!UR1)ZLBRK*k?P8s)X*N`~;UaoNuJ<$4zZAq%C4g;-14Jzny%0`{&uu8EM zDWVHS60g68$~%Nc6jA(iHIq&cU0=;jRE2j^FMqbGzvlM=fgvjWngeGH&NRBipF_?m z*0}$6fR6PuQwmkZ=+mO^NxrUqkEHCXTHevXu#}MO=C+R1u99MSa_p?1hD ztHIns-ISibgTXK+G*JTLYJC-k1hvlsbItk2*jqoJoc8+L5JE6&nJ)a-%Gb9JShZ6(7|BGB`k;C2dUNZ>FYeU?^mGT;X zNzD$zQ%TF5HG10^uN*umDNOnP>X(7Wu(xKD?r?b4AD8KZS`f9~Tdb!R9?OVERO1c! zLmwlhb;+IHUQc1U^38_WMDR`<@~rTez5F!sSYS!_!;JRqRqOWvbfFJtBKe58@K<4U zwO7~*s?qs&rI+|4%yAkgOfAn@c3Q)vyAqU{>`afi`bi_hnfFO36l3K+VU*Ki8v^P#w1B@#nzj5aYS zRqIheOi$70JxBRfWeY>qmGO4me>nF&^xSM>${3K{@oHk#O&SEcHJi?rd{TV#l?Fk> z=I6#CVLkVnN_5}Mu;kKQ_H)-0e6Y=-Hvhu>ek+l)xf%^cidVxT|FK@mP{#Xm&GnY( zDr78=KQ8~M2g71Jl!!=0ujftfDRuU$JbqW|gmZl=33wByI}y-u+`2Dp03 zat`HrfcO44?9syal;ZC1qdplRP-6LV?7R@`X`MOnfK`aq*UhW;+wCu2aq`RtcE18~ zVOqaU(n+>Sxog#wHnM({da?YTU$`c@8I3z00Ycd3HjX>Sj)WoKi#0@R<8he2a4Hb# z9;NBi_4>(J6UZXQC%|Pl-=*()Z=eopS{bkN5aIFo&l+bD9FSM`kS;xplRp2e=mCzk znnp0Lqgdj|W$I#@p>jx=iP z7?XDC7{-%3Kd=RSN^jL>Ol=fOz6(`~mbjPmPm4%xI4q;MY>cXCsMTHFCyc~(C#S~(_c&@E7!6rNvj7b|~OA%iB z_MYf)zNT6w#E3T)YuII+?gx5eo&5rT3jKI1>^?*%gh0wJ)`nJS!qC6DuzoKXQsh$3 zE_6B6mR(R#p}8u~6t||qYbn!2^TE)1@a^-0+bzNm&F5z_1(7uK9264i8`BTPhGd| zF8HFsC#`%o_s`a!AOo6>dxmCP<*2*XK0WyzcaFk)BxsW@#M9Iekp)qRf#tfbESEfa zNwX<;31n2;jovcKs`$UGYNmw4F=H%`8jY0q5gk6&78Ai9hsx>&9LyJ`dE&KQj9?Cq zHlVDt5z<{bl5Y>Msb#()gKD}03hevFi`hPg+!4Ew`+Q`M*BP2B1 z%IWE;REfU*>UUBXz*jxtg&WRrOI+fKv>nUljOC~sqSxgJTN1aid?-ou_vB7%BX?vC=xfmd0Mf7FUA0Lc8C^E``gt z1mSP~?DxLrZwV4?qw9dTwn~O~px1D|ls~;el{1e0t!3Vm1e_=Jy!zF@mS5&PQ+Yq> zFcF-o5iRE_0~OoAAtf5Xe*Sg6ZV_c>JGXQo5`Q4@bIP$LwiKjBJl~COmkk=!XRiA8 z{*1qGjpXy7>=&HAxfMe@a$Hm0hOQ;fgdG>}J%}NHpOFMEzOF3D(DMuP{cXB87r-KO zk6M2D&2q_|%rqugZx>Be{UUa6uup-#iVE}C9Cl@&9seF9C_@_eh5+@VziGOr($Km~ zdM&y)>2d$T;S^Q%iD?W`zZ2+Y>;-)yqsNTw4#z2K+?$+$ijM(t&_y&(RM^=A)&MxF z=t0jJNiiuDr`f%<%BM6a#Fsc2S|^ATGa~q_$!xr5DV$)4JH72LOH%0iC_%44NI7yzS95M7=RIP0#XQ~tV=i1Pprt=SQNPvl%CIw`bCFc^Y=F%I}&P zHPk2S8!+_rkmMm^^!EXR|2Kjm0dy>nhZ)_L#k?p{c07rA%>o_;8h>*?S?q=Xk#bVo@600eA?f;~ zI}hOi&;3>08CZ(ZhM2Zx9u0t>dNmsF;8V;^jo|W`}obYFoJZU)hXMlZ8TJ}+}o1NsBYf> z)S%#Z=#>@=%rlMGyA|`-t8)pyl%tyCM*?4P?5Zm=pZC?a`9iGH;?$cKn}h;#-x{9` zrh8#u6uO7TXG=g52f2R=#@cUBn-w?S&{XMP>zBLw!b;^MaK33#wCCjwp>1&}J!`>Hue~3rIwt+F0JKAu6!35_$Gq1$rMu=lm%kib5FZn&K}e?RL{krdyC%U zVtq7Zw>0x3z(FM7xM)tNPFP>)ojhOfCul3~l2s*+50A@E8WWDqS9Ulr#U}Fa`D|L0 zFLf<mxSVuvYM`UrjzjxI{hjw+>K!Ph?&d?wg1s)rwQBJo%JQ$aY!mx?9)t zZl$0p2Hj!1*)nV%LNht!!`8G9KDBhTWno5_!naPk2X z_L^6nU(U(Arhlk^!c!cquasuSj^B@NQivNsHC{J+Fr34y=W&OHYe{B0^j_6!P1pf7 zg7?|x?;@ciO5VrrqBM*0=SM>Rc{Zw+F!vWI3|d|@TROOd?(cCL`hGLsGVlNlO{qV< z(Ifq^g*{4(M&6pfl9K|6E9?m38{S!C&~!a!My9chJ$D2{=x@up3wv}D~UA$q1)aW`}`HSBr9X@f3;M$GexO39E_XO$9p z7b4!JYy;E|TnGhYtxQZ^trypUg8<%=!(8NXQV~6qJrbX@ON#q$<;Kys7cV$8sq(0-)NSm%3ZKfOhqwl23PRUi7^a z)-srwup{Owgf4I^pSwP9N1KA%I32kwN0@X*iX@>Kl52Q3R2me?1T{+-D%mcSr_Zu( zD6_1uvkVJNJOD!~5RNxoG*@YjtGkHWn#$w?5he+j7Wwf$6MplE9ZelFIqsYFR24@| zA_NKOzOL&xu#W!P?zIpsO7N0iz=Imm6G zTps`_!CmbO{3xkDi8Z?1WjiB~ovXdhd3*3yMaLbAxYszCCz*#A-r67sfGw~2*gG(G zv8kKxw8;Aw+7-eB;nNo2-Q;Kjp^^q0i%&VF-}E}JnRgH}x#DTpr-mun(yGMs=Y&k95G1~kAN}S7Gsj;9X z8av#Vu-4Aii*oN+cl>zjvGhVYni{qP_@t-Y>*WQ)UM$p`i2v1)+fmI?)Y!oYPA@nBEn!+5y zl-bKM>%$O&;I#db6znm%^V^H*C9vW2J^Yr{7hYxGO`NF(5|ces zX$4!3N;9t2q;pAM$mt(nNNZd0Bxe?vzZpGKjNv}73o2V?@i?~M^9!$#c{Rp`bIrQf z2JKjbU8Foq#A4`o{!=K}Dg@N8+RBHFa&sQ=8g$8vz09B2Xf8hM^`wuo>DhW`vdEe( zx}?3R^cwa3nLQ&s!jdEC^vDjaVQJ}CfuprE5#F?|d zbi2&&#OLx0pQa(lCP2e(G%}`kJUWYg^_s$v;t>SUVQoQ)(1x6N0Sf7)HFRQ|Avjbyuwyd6Bhh<-4wRWzI1^bIiv> zyTJi>CU=KtADYRh6*E$}AT7Dc3}U|JIL@wdCeiW8vvCCBKyBJBVf9+yA*t8OuW7zlCuIM_ za^(LGllvqq{#|y@)fRk@X4SAI;Hw30sUU^?Q;B%sy4E52M#O20-}zMt$XhvA+{6>e zu5M^J@`?pLcREdp;^d8Hl>Ddm$I6YYa=q``*ve`xz{d0g;Zp2m5=G9TlBGD-uzN4M zVf|TY{~DpD7d9O#h&bh`d5$x45yCL&uLCp0Kgh2q>kHAcYfcor7B8gv+|LL~!i2AN zt(%C2e@W+7fDoMXA*Cr0Ar#F15-QpaJV}B`lW{;)%s!EOdv-@kj|PPNaAt?PBXHx9 z%lqfj-OGXwD}D%bTV84N zzumWgys*D0HsQ-bT}R|4s4y^Q#*l7g{)`r2AK& zZ|4=>3#2t#wwsce6T4Z=HUH@xp$Y=h^pAr}t&q-MSucBk1tHRGixnM4HkkjkZ2c2` z?iXFYwIi2Ea)2Yx8eWai>|nDT#{8SF`mnW-H2`=hDBdKv6ydTF#k|#_D%H;X4^Ey& z(~HVY0ve3pf4Xw|q=0p<4>@}DpYEI3*I4j_@1G@-|HsDlZYkhBCW6$s{ilV63QYnO zrCGT{|6`r}C}6+^VbPcTw{B%yaIEl*%snRuaaz_}Qg#;6Ebxi{E2=b!u`d`iR!FX=^a7 zE3uuFmu9MO+C;M}qCjd7fOBsV-``KV?$%AznoT3+Ir6jFQ&pC(T&LFi%ev0Pzn!<2=-P zoy4sj1fHXa`_{wI_0@SC-<_&SBjr~T(xr2XraJ4lYU%iHbIQ{_9zyTy@|?N-9Ejtc z%@(T!r!Yy)c=?*trVht@c`nzJikdsGuUzlN72Kuc8sTZ|70zHwQ-x5J<5kFOgtCjI)}D;dQSMPSH#vLZjs-VC;OregeT%_fiAQckR!^hxw69A-S|0V%pEwYMc>mq>$u3XV7e*|mQ?d%c zyE%11tmt_6b5NPHSru?f_!D=-)^QRknvrp;=<2*aLT7$2(PF}f5ef}XNRejyH54$G z6c_+}%}S4HByY3y%D1CHfWj9dxoc_OHDlKX6HOf`vBq*gJQ>rOAnV*7sL=*zI_?3=;yH!SWqP1kk z#j>Xq@5`36FNqEuCzT+n2i0o^{0T>=SsJIKUVT1wl3$J0F)Kt(K#Su0WXN{f2a!?a z=I%8VLlrXHv)FNrenEs{R>^B1@ue3mpAurn`O?_ zl~T&pFbkcyhFap`^@S!+7FA1Pf- z5rFZwKE3x!d+t#^RSuUHzXee(c^t^L?7HS;7Du-;z1zR+x;e<_`eg08S!Ru^yskwY z1b;foUp^Au6@|q2Y|LVf+DPi;xof}7hfjn;*Zvv?^bZU2Yf?hVRsV_r>V@=e)LmZq zNvDZL3Tj_2)hT@22WJ?wC*eB}dc0VS@a!0^iTaxzL`v;+zuL{oyRzD$sUjN)G{*@< zG|4gRi?vf;AB``%o@1X9Px6VIb13?$R+qR^EP`4)DwByLtz!hix*NcjCQ;x@o#-hw z=SWpK!J{#oOl=OV6-pcQuG}q)N9i2(cD|!z!utGNA>+_&rT^<_0c32O;(Tu`Rsr@c zsOMbSXp8zR@gaDqIh{C<>8|&m@!QJ}AOs+OM!M*EsChNaR2#W35d^n5i$MubR&yv_^XPz(3fT(b;Gq>kL`;fhfz1!N| zlDom339q_v!AtD-@AjhV|KI?XS|As=`D407%QN7;F(%`hb8B2y<21W71y6Voc=76@ z0U)I9@0JbQO?r0wt4FWULdBFMa4UO7CSU@I9jERQn&)a?_k(KSwWw?L#-U^H-Qq$9 zkHQi>w-`|FJEIis1S99{TXXu9L~+}fkQ7zlr?%@M{mnELwwZ)}U1RZJDJR2u|3izi9q?u_>);pv((0orpJXBHTY-gXKWfTsR zm{GK1vl1T)U8VjI?k{A3%oJxvfp^)ArW4V4DlxD$G|thRz7tc^d($vvAD(bACOj9)cUz(A;P zq0BqIcEoUvM8Jn5>l5f z?FP5~@6kfyJ+g&Ak#pOJ{7#s1d*o@ucCNaAT-MTVUwl1pa%I4)fd8*1b zZv@mmZn}kM0rz9KR*TFR$REPK?@yxpZTDWK8geMb8wz0CgV7lcn)e;juY0(>Uj4zC z)?o5n6iHP>z8^Qic9@5`evgcioFRXoL*5v!0hVa8{oT#`K+14t>S1JZ5J|7KsGo57 zM@tpd!4WqZ+m(fc>oNRUy}1<)Pi*M$!aVygb2qqreju_?bgT-3O!*6bIxMF&|J_XxHacOujE3Oji%yI&~0!z z58bg|IyhC@y552ixe1GYcG=rJdmbCC)#rum#lrY$s1aF`+lN*E7zj>`31iTRj=X53 zi8aqw*|1%QOR%;tWs97`8W?Wrbc*I@{^-RKWWT{Ie}Yh_g8eDtN0|@@eX^eY#!u7@ z^>!g>T!gOjnF}1L#9*BbgCq8yI*Dm8rfdY}t&wP{7bCAR^!;a^QNVzs?JIr{=HSVs zRprGE{6B2E-dnt|OyMR%-!?O!qWm*S9yJ2^?c)GOaH5V8Aca+Kfo_&IOualnEjv$Hw-7zcn!gCH()pss$_PHGY`@^h5bd!07tS7#E%Mlmib%TGd`BAk_PK1H$wxlf)d!9-U(| z=}d(}Z@X?OsCtD0ihCk~#4gJ2_pOnHW!}Y+@s&Ir%RX@7%vSIG@5MjM_mnZ(v{wNJ1NWdnhLU*oD${`e!ImQa1^XWiFf{1O{)y7 zt=G}3LbmU1B700Y7C_%7NNsTaK8IF;o=anD{F@l96gtLg0|cmjW&L0du2PXD-J)PO zA$5274Rfx_SdFF%I+DaIo)cYMMGsfLX)i4w_i^LW8<_QYLWVFTO(_KkFugkHs|ySL z@gE-0EwfMMRFa-!1~!!-|BE^}#wS>mU|d?d^$q@Wl{a@w<`gp2mJbT~e~<&8lCID9 zEBZl`Jxx^er`H_dNbB!xZY*}>eSa+N*~bT{qPqQpJC|5q=?U3M&;J&DHo>G_oD9(As{c*~* zwf-}Pyn{t;$?uAxs^px4)60&nY|zbzx_M#`)ck^{7((lMl=|E1XDN?_;&?}i1z7Ib z*K@B`Q|Vt)V(utS2m9sjmQ+_{W(tlm*nMr`w64t}l@pu0wj-tD$>~Q^uJeWxbac+r ziL&kGM5xQ33sTSZR8&Xr^_C)Uli{5%V@%%a@2g^>vAFo@1we^65`cMbq~FM($XVz_ z6w7hn$>NAyg;_Ypq4k5P*h*Dq48W3==<-z*waoGSP{TKMPp!5t!ReCR7Ssr60g7Sm zwz+JBvPOS7s!(oYo&v6n0^0NNJZ;ELTBN1N9~!NF2QlxV15EEA3sN@-D81p)g$j>Nt*KpbOhMvHG` zKxLpTBK>4whGKMG$?qeDwqosB!(Eiap@Sb8g~mlQ3Pn=is0?$~SQ8SZ<)|(eB8H6U zT_tm?=XcN5n=G;-)jK0r(3~-AT&d%atvXKbJOf3QULaZeJm~+xp_DT%G*xj29?*96 z!o9x|(smf%U<(5%lnbO3DtdV*P4Oz8u78|Ibnvk?EcNCac)5}EQEZ&+NP}7sgWi`- z)tDtPU;O&X;RKzQ-LIo$#$tW^^ESPwy7(c_K5nm1;uSt^+?zAYA-ikQVnb+%`5)n$^hcNhy=^+BJ6FQI*1SPM}h@76PL^RIUj1v*H zt+lCZWjhv6_nDbpNh*B~8C@*gFJ^kW?$fgPrrt?)YvJ+!r((NF?Pa@9UI6&!dA0q6 zMX&XT+GpXW*LK0xAYaeqq4?8r=AehY3sdcKT6 zi6WgMtUu#6%uuM6!*|D{qUc+Yhp_?-(p(te|Il==crqCExgNv`_ht4QU*Q#lxR1gLG`-pc4GK&RS^7K!M=c3+Mw^7>6U>*tl_xon zO5TiSRgRRy_{AQx3=`i;4Sq0rKmLepp@^9c9qBsrLnmuXcNOX8H2&TXKN_h9FD66I z^7x*hWMKE#93-h;FGr7%EcE)f5IthALl%WeW8$}r&~bLucqvIxtTg_kzk!TT+P^Pv zdsN-agN4&^yLM!wC@=cpxLUGMRIyMre!*x#j;8gkek-lp zPPV%|$8#kjVdEcoCO1=yaewrH744)f$gLjG^LcveYuQ@Hq=hg`4h~-*wkLF&`zi?9 zlaR1^j15yCy)Doi5g$VcjnYlZ4G7C-ah#W1B+$fP@D{lg4~w?OMK%#BwaO{5(yE4j9Ru}h7mWWq1bjMDME*BW=m^u{+2Qwg^X0$156*l{b5DBP zTBD&blBm4wn-VYy5J)kJ{t6px7LGCw3dM=ju+Y2XXuY8#(Ea?`uijvINX~quW>cV4 zq-a#3X=0YqT;s0ZPN_(TUkcMl;uNB>^FpwLvd~>Fy!MYDuf)zT2J}OdPvCWzss$bs zbxD_GtRTD|u2mtdZHqO^mdO~V_%327D z9;czL^*pd8SW~&z=Lg@`-*}I>uAOP7cLCEWkVF_6(NL;KcbVC5iZC8_uFxi^cb|yI zgDMlRyQ%B^vRUyhJ_^g6RSSR;{YfJ^98mMctik&mvnN)j+p?5Yc>A*6@E!U%GEg_a zt{<7@D>EJSCKx1W#?SD3WL@Z8kt!w^xyqnod@y5;9Q?lknr}`GhiT+Se`4oIe$D_g1lPJ%O z+cnz0E+g?f+yCaL!(2Q0T`&=Kef~4J8wh0r4eph*v-GoBxI4So94E|d3~Df(7u(N9 z^De0}J#2|3ig}W2-cV(GCV#HY*{#{-UEx_d`aI1qfnZFY66HMs{T}O?P}_dipI;yS zW>0sDm>3VnOv8!ii+!iwh>4i)2K=4@>*@u-%g-|;n^)mfK^2GkEHbR9oH9d1)wmS( zU_yoHIX)Yc&VuG`=IPttl~9y`&R7Eh{&G(x{nDZa^)H}5f~Lq2DVt*p2t)`rQT~bS9UauSi8f4FrSgrdyDk_SRS1 z@z2Z+p(u=I)`aFA2!kL&cGHZ|=xMLR3UH?BRAF z&%SNr63BTwPCpOFia7+R=Ne0`B7ZgJ+A>SIIxvr+2|)EDk?vx-V>7<>hgbt>5vlWA zPB&gDUq+B)meR%tL65gAg3;Q#z`nr7jyc2>)9pFu4xRLaS0_+a*B zkr)wTG4BlE1=wA`jrl%&4lm7g$=Ng*c40~1sBYlST~rzP`tNmDTOl@E1^P?Sk2vKVkjuF4tY;5$Bi@W zmZ8<%>&rR%3mjj^FNqph>>ct9=9W;lRey&+wtLF9Mv#1&D>e%xS){(GFFBTYPas=t z$&9@7aEK*3N#XrDPFZr_lz?CsTTiy+OE@J-(OUV5fX`J!=V)G@woH48GtcPtYN}WO zBB|eO(Dvd)sk<+o@RAPDNgQk`V^i=?Xa}8L048ye;#ZYqI4da8!TT^ zmfeH9P$9a;<#tm>#Q^$@T!X&le7e|~D0U;g^-*mJV&u_!#DZt zN0U-$Wj$?Vv?5g?%(u|=^o2Cg4#~qW4h_TerD>Jjs<}1(=Jc@E?Xb1dLaU?}-qz95 zpra`9$8$ez-3++44BOWe=aH~tZh$QD$c$Rgj$6hZ3R+tip%)0()Pv~vQNOQqP>2F{ zjWS%+HC|u-zzIIC{I-gFFTHQh_^V{hz}cUVUTGg?gamEQIpcuv0xm*mqnX9xXM-VC zYgDXh{;RsQm$5|VP|7It*)5fZw#9k3Clk%noiA}IyxjfDi*!&7#L5}(7mom~Mb0Xj z&D5YkXh^SYGGdyMwE5YDf^+lJ18I0i9-F4;qX6?<{jm#U_3?vH!OI<6)WHM54mZgp z)7$G7l4PK5kt~5DYIjqMT+&T01Ro&a_y!GyRkYEA6aed0=?hNq*Z}Di?`{Qcxhcwh zv`=6`c5&!TMfAHYG{Ulx2jaq);O%7=^f*ORg(BgT;$H;bN?Ci?DqSe5-gMVdEz$XB zF2Cl%nEToroHu$=RudC7#0Zlg0b4hRk>yj(`VbU}6Nk?5d0(o!)|IO|S5HRGJKb_` zN3jG7MV-}s>lxy+bt_02kH*f-z8cs0NT8KNOw11^RKyjWS>}~<(aOaSt!1#3OyhO8 zo-vtBEb?%YLr?tTs`q3^Zydew6MZthPU|pXLaOnw@91z0rLt}Ddz9Q?3o;=-}rVtT{H1K!ai zM$Ez(p8EzAT*Sycph<6vsBI@qGUH!9L3dcv6nT=WXvJQSkh1t#6u+wdcJ)M=tiATz&}Vc$u;57*QA6=1n)qwYi1r#KxN-St(491m=YKn2kAN z@cots6Qcp$0|OGO7#(=3amS6FGKCGEa@@18%7(cLMLH;p7@n}zLiKvNSmUH6vSZd< zKwlZ(^NwLTabNdgg_xRqlxpK+)?`#_U{D9AkO9HV^<{z%_}uMJjfH8rf}grjrH^M~ z@{_bobvqeUv?=Hq%uBaJgP}QN@sBb58XqBVIV}(!Rt|9la&rkKL^l(+g|HOq2*%PZ z1%E137#KYCgH^-P|Bo;I0W3It2imb9wyR=hW{N*S36=pSFOF*>kBjJ-?Sa+BivLNo zv;t${Yntg;j197)G>-lX;Nbp5rJ8k0fjeCx{~$O2xYxcvQK7l9>5}sp&mE*c$xHLP z*O%-l`|@{GQ&c9xY5W-&lGv;ieg49^7xGgkyJCJ`Qv;M!m35YexvBL zF8}+6fiwBv6aGIJ#{d5>37%ZntsSgKXXn-+4o0f~`aam84j!%Ss3^cttJi4byE;~< zayzP&pUP?DH}6j68pq{NEVsQEhNd@*gyZtjs5X>P?$VJb=2ry8$#Q{*XRs5s+!I7) zL)c4}r!I0=JT$$(#k4_%4;?a@#(+yz_U^>xTamO=*%H@)cze$Qns}pHD<3}qNOe|p; zO~eZiCs2MxFxUCo)~pk47~pk%=XW)`fX?XrsPc!;u6vw-%GJ&&!7MI#XiRk`zIj%H zGaO=Rp`J?T%-iZ$Do?`2k}%Q6c0olfaG!+W>@#^!lMRbXdq(xj`6x8kRCRw8sMqN? zz`NNOx22`V9mjs#+M_aGR&PAEBCsh#8%f;{ zdarYuD^Zb1Z*GWV@?CmzT!VB4Jd=#qGd%JZwXBt?%zlWeDpRAO47MPyKL*Qb=LZe$ zVtb=WxTe1>LsKgi+S3}RWEk&O)9*M{@V(8GOJB?r40ILPyxfi+Q=Gs0~cw z@HjnbJenS5R0HLDDo;wG@rJWvxuKV-R!iy4iGpI`-=KilKVqFhpIE2}h{cy}?@laW73E~a#9-BM}`Ro9|S%+}eFy}ysA zsaD$c85w{Pqpww7GR+T}>yFx!pBl4djaIC;PCPw(U70nV7B^4j*dq+ZvxFp3Z#-r7 zkQx2e>=?eP;{wa~Rkqz6ww%)~%68oi@H)-2OZpBnz1@jF%g%~3xWIPPgQZU;BG%zF zt)uyE$^C0pRhYqY92^04n$B-70&mBARmAb-N~4TX?Rw$B*IFTpP$Q!Yp0ZNNh>jba zhLeWlmxN}Mx1IAcs1xS5@MF*B6F{IG)Fkps;HoI2Yj7wdN&-FD+1x9+cBZ?>Z6AKu!r8?>J8EQnR>%CO~ohSJCy#J>`u>Jiw8v>t<$yPjB^dxm#{HW>{F zWW2sP#nPyER(l;9RCdl+c=@hLF^!~0ghx|7ds)72f(eTAfM@o$UhbY>0n#xT<@wnX zFdB96EIf6;2;8S43m_KUO0C!J)!g_lYZNpZnvnulbXwh7MzvZg z44c)+e)po{m0HQ|R2<*8;c}mxbU{0Xs}Atu*Qx2eIMk~y<~@33a(7(cz7%Rfh2kGb z3%up`@u=xPShn+eq~8E31WP|rsd*v*j zJs)P!<0X3-cEP+Y2h2d>SOdUY2``3C>aDGL9LWWtkKGQG7R&$ZdWbH1I(ZX`3q11%CeE@TM3j1HAqK^{of3o_ix9=lrp z)p2@#iQ{%tH+0qSGeSL&_ssu=sqIxoC0wx9ZYg z&7d)RZ+WqQe$0I`Z=2BSHd(C2@7BFf8)z8}*o93QiuHCzF`aLW=61Qxn>ybWM^p_U zTTde8qtZPR!>;(^11Cugcq`D57xh+&TzVLvO)47DA3R*Ql$NeQrUN?Lc39o2JBG*mN+5SF`N4;-}tbiLLp z$#6A-fA?Yey?1$fCuqJ!b3670FT!E{{OB1-R=57>f}6_Qd#pQxpYT{JG*zscv*SH@ z&X5F<{$PZfG*xP$G_@-_wuOB724BHjO{btJe1fJReHr$lHnQvGRLYfLTQMA3XrJwa z)^?)7f5?0SQz1~52%-Rltf*&eixVK&WBbCrz0?)RfcJ*7Z{Sb5Ci_{LY`{KZqzVWWOdx!1vLhjSUR{R7%n2L*BT$@hrn* z`BD)IGKsNrj}v#L_MDCCBwxasOHh6%EZq2m*ZjZ~o3|h5zB8s^d~o_)+IePgaybPC zTfz5buc{*f+Xb#Ck|HBQ$Jojah(r4d4PbG8okQe70B}fCnfcRnx-eTysr@(0Uzm6S zXu=c!&!%8jaEg1Rjay&8i@s~799X>4W>tC=K=SBUqC?)N@i@x_qYcuaOJc^BEUPNU z&oBzmy#Ln8YRO_MqGO9K(z{xe9f{4jGNuwom@O;m@kAtO({gOHuD(l|6uF4fycl5R zl7#Q4D#%W0giu+g7>0mZz14K?r}AFkj^;8!bi{+8Hn)rzulpr}-IY1yMJHQZ<0+5u zdlWB0n<8bzXe$-ru5*A22oxE3Vs4lQCSdltoN~e=8_CN^ECT2PXp?RyV5}=&D z==fITVfQCYg;5^eUSJmsA#wT%c)pP+@1T&^-OlL34DtYiXUZ;tN7Sa{O3kv{)^|{7HVM1y89Uo zCC)(!8=n5`Z*6p9u&2V=0QKzjFOd=qc#X3q&dE=?_s^Pyuw&;`_0VqfUtR9I)xcHh zKNgF9Y*F&N9zqs%Or-a$vcy)cR9R*%Hl)a1NNjo_K;M1~-5JDxiBc~@yQ+IXsT)UP zB^Rgq#aZUq{rH}#w}})6P(-afh4!5h@PxzSOhH{l>dVfnd-xMZRGdgnSLn^GoJ?yAP8HqA+bI?Q87UG zGp`$!>qr_&R-R1pN`P+o{fLTeh4gFxv4$XFFm0agB89i?w3q*mk>qNb_weDhS7IZ= z8n&O-&=82HONDRDY0Bw(m}%)4s?1uA4%kgt9IsLYdltG|Zk6OYcIM%kD+db|R@$({ z)%xb^^D(M{EZxtc*F5nRcMYNswfUBdrNlze2jaK^;(cLxNPQ8|Z_#&&60-}hgA^RE z4fr(LYut31z(`Us-w?=JBXSNP+uRWjF8m5aOp-HLdv zjMTs3?Dg6C3%7GK|8k{S3XhQEjNYsb^|rAW`i%b%0{HfBGx%(Q-ylW#QYsksip>;x zoC?0OtrRh_l2YUXttalWNF7C5h^i_I%;Zxc2RblXNNz(vNWP2~=S9Pw|LGSJiT_L{ ztNFG+8`f3g0!5_bt^gr|=dUpPYu&iZ!`6+dlmzm(_mz~Yz$fsR+jdG@9luIOJwnvM zOz>YRicI9Op$?03ktlSUaHM>b3Yu-9S!xGJ7ZYZI$`5E{qM5x2ZU)6Yc%XiVXU*;D z8^Ym(sJ|gzC+|ZnL&%5E--iv+J01cfc|M7k;b?if6`pAgT1LEEm`qn1o9>Pz{X_tO zl@3q%%Ppw(q=Ppfuu4|vj?(;C>g0i|0+HP4MM<_rFd~LZ>u}f-s0LvB^p~N?ZU{Sd z(_zzE6MsWOJ`7f-S9r zA48L$1Blj7C<#G*|WEB2-U%* z-R^Q$oOx#8w58Mv+JIhTk|PwwEnh+7_V)J^>xgBn|32*zdYCPUT>ZC*65lE4xRTvj zx;+VdbPzIyKAipcmgmaxehnws$?Y963kw&9%EiI(65_~j00}dJ+`w@m9GnzCsRtyNOsBB1dnj(cueFPlldWoo@^=d_qL=iSxi5LxhJPhF0gf-%GKaATem;qybW zvKQ8jtf7&pjpS+vTsjf2fQ)X{RXbui9-_~alRD+)?PbOtc#{;3q;hmOv1Ale5vp^ZhE zjTRy#$Hsh)4;0FEb7GB};1kJ%p+*yzY%&B=%@vX9)BtJ$X@oKxidfkzv3AM!NYX4b zv%}ZuCwJ>iMVeMcKHR(nbfgVDas4f45gT}MHC=Q~DpAS!V(w$PcSf$Ke-?+sm);-Ta(8I`0wALz% zq4_+4wW{#;(Ab|67q7xV(z-eM=Keqy9&jMcys|-@e-?U}v~u2KfS;S)HXnVR)R;&j zO37;^qEr}f%w-$SR_=%$#Vn5!Jhvu#ci+Tp!4~9AEMGx$e_| z=cO7S&E|}VK3i1g&ej0vm0G}=wfN|L{`M&*r}cS=ai_ckV2h-`lU_4-EnI(QIh5jJ zxnK7=n92@bU4nLlnpBWmNTqbX5FJR4W2SK3PSl>_qx)5bB*VupA`8^l@U=eYLp zK9;+4cn!xzs+b?skFH2;!%-wW^RuiPI%_G z`;D=+wvADNj%E-4%-J@_g`nrhIy3m!T&<{fw@YlQ+9H{eJhg2WWJa|KQh1&7@Km~p z8!HUf^j4HN)_vM_GtE~p^sVzXNQseG;>f5E71sV91ru}bVu-TbnL78konRv+j67Ym zA{+e8_fj*O-&3C&5WMXI>~bdGqS<*h0$O{7@S=uw5~zgO+xstw*hpe;`=xNk92<0< zo_TwSD6RaGWw;Z21EA-zPFB?L{btz{T{b&(g~n*jU}H5a&|MlqO(DE8>@p;aPwaWN z9|>#si1J?|+bw%>nHYd>e(Gx|!)Ng_``QV2sQ}tuu3pI`))SQu9=(r75E30S_{cLy~QohZOpGN%%H2kgE`l={*@WY&}1TZyxyI zQ9q(p4L8;NB;nq=;}Uq&Xbi&CA@{=@iB=R9iC2MgY?01V=@UTo`=LnUplE}lhcaernTF*48y3QsR*`p)arLT(l{Fy4q&c{8UB5XO5b&Fp6u{zi z*e2~6qncuTlXsl{G}S)-M#Nc2l%a%+?w^Or=07WT2-5JZtE7#K*smt>_wTAlEH z8+A~bZ#sevQ`(A6`9-m`WqNkq;PvqH2Q!lYkt9NNk1`T7BjF=ck9>aMvG|yy&9n47 zr?o+JF%G~FB?nyj-xd(r9fXb!HEodAJMMq;BtIeX;<6u~xC)7hX6hlK&e_|r8og-Q z^Y}SgNrkrS5A;t}q&?mKYES1-c@IfpRF$|UUs$uXNVwc@URrr7Lx|enwv?Aox7{GN znWegL(W3^(+lI^sMHN(Pr?RUI!iB#-jG=6Q?$>X8Y&`bqgv!s1LX*;%p`v5cmLJ)l zpytE&oAI0dGa&Z03{?zoNlqg#LF5=g_teHy6-=9u)Z0vl&4t?jz6oH9Xo|j^K$;uS zcZHVi`LL_q{DRpUg?c{d#R>D(d-Ui0K#N*Xs6`CcPqB_-m26+MelNx%QjxIL!EEIc zp=E}L@ySZJg~LP=nvM!YwI2CepGf+V%%%RR^HccY_2L-j>5&bahJ9#Jcs(=Pm5<~! zkT0IW1TxPpHum!|H~Ev~2-CT2g0*g&j&=SLiQE1e7UOvNI+yN5gK=(Hoi!6s^x6h9 zfJYjcd!j!Omj4!n205~#*C2y>K$^_0O+=t=qbsA%Xs@W#nm^}yetf+d=$p#x3;33E zpP5ip414?pXmX{JY3~Dbkx>aNQpggHxBa~g!c~b>o!U@VfPF;_#7}Cpnh%vSbsHrA zIzaBxZRFg^c=YWaP2AM@>!?nmHJ5zbzr(>9=H8@lz6H+3t^zPfTVTQiXuUy*q)&pM zsioNm=;dQD$N`A-(FyK&o1$qHZ{J~i!In4IAd{0uw}I(z7TW51-$fRrHF-ePVBt^+ z-jF6M`T0UW@sHp2xkmojFF$3#bMK)vy0hN_`3EE1HYg959^!;_Il-xtYVi48_WlDSCDuY7>amo1MpZ=HC@+VcTG&K^yLD>DHkW{-vS&EV+-jQNA%JqSMZh6_$QsA|nO5)LL$^8$}3o zvYM8kPGGk>fF{E^?d;VWQ{!{if#tp3VKUy;=rdF40b!ZDHz^>V?#iInS=vm4_OV48 zJ?`q;m!4Utv9i!)IUqH*HRVVkwK2<+j?0AR?y$RfflWN6$woLbCzrz@Umn(%9BKH` zI`STrHN6fo2%t{@cKgtVv$tZ^*=KLKpFiQ7YXZhe)g^@TqW5zpeU7bBQx;rAiJ~j&Ie~GT~8?mF|p$Mde+n?gs|5Mkib7JaiNA&K|9m z(GNAa_4;xGW{EqGHm-&Y?|(X9QncL~q?<(i!9Ha}B-tKBVbIE&+HO_r%?}w6Bh!HX zNtURU&}%OW?tH*H#=C9Gc6``e1*QOUWwO_~2s=uAK>Y1&o?~+GCNQ*&&g7WvpFT6M=(rTN zZ6l{&Qay#soiW)f)J7nHqRP$K{WQS%DZcfBHJh`D|KZ|Y+Xo8`LmRn7je^H^3o zRZ{Z}CW4~2atDN-y6lYftgPqkg{3{?d@EFqcdyKjVK)FU6p=EEkoGE?+0^3h8HDm_ zLT-J(U$Y;ATNPS6Byfq6$X{9-l_2H=gZ-%sU=L0kOJYYgSmZ`ksd?E zJ&6%=?u}w)N!E570*P5Ylf8Y{Uxa8Q^3Hcys@U zbNU&uS%PHrWIEe|B4H!D=22P`ffL}m)`uQEDEZ1Yn91f{mx0F(mME<>c_)Q| zhL0v2=&j8c{#u3q4fSRG=_IcH__A0Mi{w7eFYa+C2;%zISn>Ijt(Q1xO351M$SetHqNkN(MLn#xI$L zwH?fpnE0P_zv3F^{z~9RqkQ0oL{+O_!(LE7Yx-6j&{2jh1_1%f`)UjZ=1Qc(z$>HS?OirMoiOr7Ev~bRc?pS~zO-f_t0DVY*(W$9we<><#Xnz>jQR zN|io{d2!~J)LN-^iHiFwtca?IXZ>zmJbXGf0w#C)zi>;&^ zvPKMUT_W9f6~OaU7V?uo8NtmGnB@1<6!w)ID&pwl?!F$L0rn8ZkyjojQ?j+ybKwgP=+jUs@IHkjM2&}kHrzz}ai@#r?{aUkAc@s)a~xa6D;O(cN))NQRPX_oOv zd4Bqs#Q1XBuVn*B!?#xrpa$jLPEqntOdAg3h*22dF!uWiJ%lw1o*}7r8-=-|8%_BE zNA-C^MM$=S&a zZdA4!Iy7(dzdadR-D;t$UdwH;ovO4?_oL@Y(IDaTep_}Xh!-4Qh#|2z-qlIeL_SV~ zSKRZeoVgL~G`*ALn@7K@-;x0jSwA|-QjzA_+P&H9Lii5*5B6_fp{f* zWEPsis?q<+PBDK>)&{X%6vK0ZKxF&86lY_+oCL$^3aZckPudCeoMr+(X}KULp7T2e zC@5s~Y?%h7;MIok|6TvW&kO^OABH)mkmTG<`v0)^mQiswO}l8Y5G*8k@PrWD-3bKO z;10pvVX%P!!QCymy9al7cXwxy;I?m)XMgYeoOSlUbAFw()~s2q;g;&|s_O3QtFHQ< z_bHWULGi5MbLxMS6hT=5$=QD%r5@o`R}>Irsz8xr*iTI{bP!XW(V{Pk(`fu3j0J#& z@{I^))G-7=v;Tvp;)m8kOG65YV@|;SuYwyum4$?7E=L%{%m2RgT`7jaUyo4xNj?7CG4vbKBvatCLk_5Mzc40bscs{=5d!@X-ABAdMtnX%*2Zer=Z zcF$Ni{*UrGlCg}bWHOe0No-;IL*hwjsYuT%^vVy=2W#?tW}}gm7(u_+g)M^6*#sVC zAG*4>WIb#md0ZV2HlkR}V(IPl8J}G;$w5W$^^?;2+_`XibPU3%{ZoyX(- zK3%DPU;CADw+o|FHK0ln&x}zo8B`XojuxyaPGuMEc7|zg&bF$fK@V#PBzT|x78qie z;l~q5>+I-WQxoToz5qWPIuII*$97DZ`7A= z%jk=S{=NL20Ja+n%^$%NmXtU5g0`DWRRlUnY>gU_={u|(2b5`UH;TYd5KxGKHHuy* zh8OaZu;dSew0r({Ui|2=*TKb}e z{pSYClbzHT$l>ihP)tSRw<%*DSbs16LfnR!I^!Z@M{j0Y&s2m2tW3n;ra_nXrgA&W zNvHDC%vBgf)z;!uIMAuk{_!iozR`(o<5`&Hxj&lJ+Y2aCVS@)?X9E@9sIZzwjAtXB zGtxk=ff~ZNbAI?>(OFaJJiT)UV`)U_d{j340p1jIXCfE9 zJ)>26_{GAMVTsS``kZ7W{BszEo-=bw+$Gt`$1CjsGK&b8AQ(4<*w!+*_KA6K9CIYmeR z)89Az^ZiFsy^e(JzYqN9`#agQ-}j10_}8!e=C9~aoXy!QG0;TcLW_jrM*xE@nl1oG z3j9qW2alD=4GoV<7`ZoL)L)+215_%DmD~4<>>HpDFe2h{QMd(o6@kj+W?QoLzv0d% zC&I`W*iwZ5Ol0BsK!-&M(jo59YF0=J2VYMQa{!eH8Gp~Ud6{%lJR%_Dv1(w^Xqb=W zCGmN*3&>>|zs;A=jn%OKiYb!#y8_Awyk9zDaNKxfd(sN!8VaMK z4b<7r`Vz1{o-W8`GFj^hI4);YxjXO^Q22XuE}gC)%7N}Zli^?_Xc?@zzP_%jbrFD` zJKMs5;Vw~%r>6yvCHhy&=Nh<_u4wKL7i=6rHBn8fqEd}IHPfkrZ;+r1aku+#H;|Se zeI;q^S*#Mrzls;|1AY2rD!rhqoF@#;?D{vg|I~l}u&`HgxtC${Bfz5h!7*Ag={BiZ zKb15mb)`TB1@F?w^rJgTlVy__E{o}ER47-5%!@#0H@8Mj*%WT)d)Ba3Rc3(MH1aENm~L?}^5PsgIzS4@)^IlngGU*kuM5E2?Io3-jaY_%hSU44LB zb(B7c*s%|Hm#QXXNeadDU6;<~Y6+|k^-D?Kge3Z5*FAm3(Z06N9E#c?TbK6r)aYI!ej{dA+pU}gEC!feJ;wb3b{*q?Ze@5wx}=Oy?1 zk(QHSgBZ}y_?N6Y5d@sS;pVFC;xc~$waTL}3FgHU7_${OK8792Bd*?F;OXC5G2u61 zZuC5TFdF`uF0h>zSI_zq#YN?!XfRxFpTlw@CwsF$nJNdflZn;+{!7E4E2Kf9!f-3M z<7NAwZWV=sc0%k+l_6gU2O#Ct2743BM0Sd6$71%mZwKopxoD8U%tjJ zrBXq-uEV#szs8LKizCb!!;Qe@co@CZ09vT7s}j>=89ftlb@dGt_$2LYW(Hk>F^qeB+j>`jx0Q=tjXe^?gR=!`nk61 z9B$WT#B!r>*wK-;{a#DudR!E;t?%IAZ>Y5z72IzODw4M}L6@=XlpiF?Z#FkZ40?u> zl`6RF>?B$qQD<`{6=71O>W=FRJCw?3TkaWE%kNQd?jRmT)Yiay$u2p}<6?ixqd$x< z^w;?2K$3C@_O2Plt&`SFk*e9vlHRDbmIJcnLcyJj*b?WrC1qwDtb*Ooc&zspTa^zh zN7I#ICqK5RqT^ngrWwXi#tcbFPIKY{CMp$UvG5Dwr5_=%5UK|O3jc;VK8dA4!Ir9f zEwE*$K{Vn6mY-6w`ZYn#*>M9=|KkJv=J#8st!!-V$`qo*fe4<4Rx+&tI`mI{x|qp` za{0P&b*DLaMGhyff)CSSc_*RaL5ki_x5qOo2W^kuR8uu8k2+*^jS^(}>JT(n^-^N< ztx1y!gE|-K%cCWuQ?Dnk#fsI^l@hR7;pe;wyFW{qk8ijD2d~$|sUvp%CTV(w)4^0d zP;#k0u2)$*v}(kA9K-a*9x!hl8dI^qC0f&|&}Kojx5r{y`c?dNyA;N3=z`pBM|D#F z_FK+`vmQO+qf&{60m`uJW&}JO%!Qo5D&u&Df!#tftT=Vie6|8iN2*RZImV~j#dxm+ zAweV$d5>g6)fz4bQ>!JxBE#B${-n_J9d)F0KkbC#JzB?W``Z zSsj2fH(f74sb`SO+`;;h&m^jL3}NZmC>}U9Uan{1rYG?u6WZd=!p@JKgQ!=^exXbC zG^aJ!siypsm60RH%H3|Jw`&BO)FSQs~LGOx>DM!pnBKYFlOsItXipj+i%k2$-yqvJY`+UPvT zD;s@c-#5BheUQQTdkZ4;7ONg)>1NvnA8#G12Ivu6Bg5cSw+12_s7X*wy%|GH1tt^( zA-rh?8vkDJTX^)^`CYAa{=LzYR;KSpZK^TE03Nsy73*?UZ#?*T``3M3RX0JYAb)Y|2@gs#L ztK$xC#FHX(@;rz>Gd%^4`OC8V9XP?{H(}z9_1_w=#4bRs$1uW$GP@KLWUVgvW~HOV z%Q~q<41g84`IEG!h@b#*So5#Ht5ID7oky`2+=;BzICL&KnVIJzUKA zrqO;dEJbG5vbR}}e^slHFrH@vwqrWHkf5{BuVY_>T?`WRTaVdl{JwHNrlV51EpvcV zG@z{M;KOoqF_=&pEB91cUt0gwb4!HCoidD%p_Q!ktu|OMx8rlI9<$O#s|Gx<1FO=o zYrejxy5kU_`+~i-9TELbwB_B6w_N?zyhAJGAdsp-WdqnfQ4}pkIrc$X=-x0vPjZP< zfFTcTVB9Zj+$*xvYk}DVuoE-duIkH9=LQEiME8@#14oF**4s=|555PRx*Lza3pj$@ z*CTvS<4V6yX-$^7%kXB=E`IJCVaU!GI34G-8klCR?fd8zFH}W-+JEfa3XWpgXV*9w z6khZ%6&AO+7(UzKG~6C?_`9ZnJTnA^8bsQ*!xN^XR8J}droYQ5{?>7OcEq-F(BXw@ zPmSS3E;oORy7iT+7Lzi%=4v5^vxQNj&L=*QblCMKW(QyM_5{Q|DmXVmhJs zuIFoJw#dlAl4`d;-s5y(v;^vA>*ri6#>>xZ%rS(Lx_Z-?&4(d)Y*o6~9dW7yB9z0o z2Ud;xeoy9e1$D)(BtyW22t#?2q>$PAGaRwMg(3%)C0ecqb4n7=oWT7q2cO%C9@y(l zw6Sc%VLDE@Nx$97Ne|SAiNoif2nQy6kyskdbhmZ019_UKo9g}{VTq&p>P^q5r9!r1 z(u^$(Iwi>zx3hPyPYPSo=zD{y))(;%dO6APpCB%0eLc};66@?}MS9ksy0PyI7E_6h z-HMc(bPc@`?mEEDW8^MRO<@*=d?}E*${p3OS)=UjMDBPw(RA9ZtB`)l9W9Z}nr|I1 ze-&c3XRO!Rj0hKvFlC~y)wS_Vcp2f_!Ni}n!d!9U0(2jyGtD8yw`1`P8%0^{kf<;| zfCv`c%N?@<&B^z_xd5KIDuB<|kE<3QmKaotT9v*m0}8K~rYrAn&N&RZf3TogLgjzW zk(QGEZohQg@@sQt;Yl4rBC*X6kI zs~CT7Q1P4G141`Jk@iy@_*G!b+=0*M!TR*jFG~&D2eQjY=u`ce%8rhb+&;qZwhPk! zZo@i#`7>T(CY`AS27NERlSENQ%sqQ);4xQ$$!j9#0$-FIKP3;WYlgCHW5iu1i~@`~ zW#3e_nl?CE2y_xO(J4tVoxS=H60d61})??bl8haIWrYsWn<>xmgbuO||+h z&CPo9b_(XAaSF-%z(1Clwt+)u)-5;Qpk1*yn$;q?i8G$)Gg7UC?AsV<1l+Y_f3|H1 z->CC5TL7Rmt0=%`cG+4|j<(D{p*iaUZxY?ZTVzdGuRnONa&SnY{P>cfR4dc8*F~}P z>rjN@1YZ)tLis$qDf!X7X~BRmoHm^D)vCGu@q0zPMLuhn}-(bM5V-d?+!ectFcf5HFNz4nQJ z3&bNNWHpx=B?J!8qK<}7wfb?@`NBYcIN9O#wp9N)N==6BH|-(Ds7lLvm9{6svFJtJ z%YFVeHdpWPX}9S$O7z`#Gj~Xs;`T!AHwy|lvj0R3%wGZ22&_LJVs&5t6oejZ-j_*J zm@ZJ9KwR?O*ewxO2H9$m-N!`z1j9!|ssNZDx?M}%v16+B&bOS9ZB7AFQ(zIx@f|%@ zS^jqUDy{CFEx0g{!xE9Y67W%SykmAnORsVfe8{DTtn&?G-&(vvwBq4osFwZKK!v05qDsBN~IWAM15)&sm6z=bI;WL%jO6ukGqL`xlu@-h25?l|O7btR`=Q|>u%2JiDdoD7+dbiNE+|QdHE{+%G9?4j4qYrbs zv!%M<`cTvBZu{A#a7oFfSb+$~7Jbd+rc4#5;)8yWfA-gb#l|KW>Ndy7ARJP^#zku7 z~Lj^rt{4(JgP9eGc z_R$8_9tPThQhwfqhZX*2jU5+1!rwKVeAvfKDB^?Ap{%!c=JUoh<|wjj0N*Fy_WmME8#&*IEqp(H07sae8_PBUqzxP@|5z zbxU&o;@V`9r1T3qY=vkWvGpG2jpTSXoF2xTh#ndp-DPjl)QfgXYO;T#i>yE_pvSjT zhGKVJI#lOZk4~lUe&dLb|Do=!$AMjB1v5=MyT`(>qhDul$oXoaz7glf3Vb#^o-}FH z(iK*a$JL%6U5xhf&BjA~-7etr-XH&L36xn>L{IfegELKGh~blyS0t*zN~+HN_k8O$ zZo3RJkDb~p0>w(F#IIte)Aqjf4!J#-E&U@L(ats`lhAPW8g&&e6KYsr(XmWC9nhLP(=k+%d1ot3DkUu72=}n423#j{ zLI}JQVJOr@Qx$KG@ALIq*d#oS&TtXr#`O~tLSkEctg!@3Wj$e?~w*Fbb5 zZ*kd#Q$}T0I`P{j^SCb;Uv@dyY>3Y`)h!ambh>UxV$|Q&Y2$`{ggqv)1Q9Z60#X-J>Or8jn0M3M=I^(v!X-71#pNnaUCN2QmI>=0rH(yXNpBC@5Q|7 z!Hlo<;054cyP21DNWoMN-v+sc7<1;(o#YX@M`BVby`+XKKV+cV8lY?X1X`L?&XG*P zr7wO*XbVJcsI{$=ar(h|46wUv{9o{BoCs5#?oA|2ChqQOJmkf%T_NC2_8$oGgD>MZ z(#k!3N8+?^TS|?io6d*fLw%79^H|{e4>QA%BS-k~EQf9bIN{Nb5WP0mCWn&4=E8-V zU|8~2#eYA5`08aq<|4OlPx-j8uihfDm2(uNhrA4;P^~m%U?X6{;d=e0*7GEcR3f%& zL_pBxskyT)JIBk4jI%}e(Y^ej3tQ`JOGKyL(q2#AoQg>3z4S{yjxWbeJ053oF24Un z+y8OgM{+vVYE|@OX9xqtMaOI6K)4BC?4j#NsX|=_$V6oGTY}tul9+m&#nx!ba zG$X5|#~O>NE+NR=F1bp*h6;b02bmwA$)&Mlgg|_9Fu~5P2og`+hcs8|T6(AeBY#1( zsK4q`rStd!)6goI`}b<**{zus2QIDb>$?I~J(xPKMf(Yq(1NCiX;&{XtF-F`=z)fJ zVy|Y@GKC$O0`!`E&-6wTm>7<4DKVXef)?sat{UR3sTj_`3vk=_A}K+jaX~n;fiA?X z?EBotqw;4LuW}CtNgWPeebPZxe)DMGek~S?5Fbj!_g0~NG3(?Z?0io})5981JegH)iCSequv zq{;|@QI_SM?K1b#yE(&*mD3gO<4-}K+GL)uM*tC!7r!A#ku|Jp6*rMi1t3ZTrj*>t zDb|QPycd(Q5++|BQSTwVTZoZqSsz$$hH4hcJ6f*$dBKOXr;Il1`eO_$S3r<{MuKdA zwj4aF%IHvU9a}cfTM|8K4M%;)ci)nRe#~E2tXx_G#M&o^9cRnviY^5DMR;2f*ZiQm z5^JO<3lt@KvV;J9V@BJkKpnGW%6;Lo0gVkEi+m3hA0Ey*{dNB5*bnCQ3A?hv?$S1g zUfEA24VIuoq+g!h&?#h{_(W`$UiHm#w$ejcfo;Xxc0Syedk&dlcajEEwbaVZKUtk1V;c%3y}E!e2QAD^mCmo(fTorA%M8onp46q>itZL7ye1;b8S z?J=?=R{`i`G(@(b6rjw2>{mau9=Y8!%7O!-5hPeWq}wYzow}Cyj1vuzrZw~%;Z$tX zxhye~RAJm<2FyEt=fyPL9y(j*JHWV+TC2*-#(}iKnnshPx^`9!5 z;oojIJpV| zvD|@qt{hVl7jm0kvk+SmhHRfi*&OB4q(RNtNLV;H4@=UkieUn9jy~uoel`lT{`@a9 z62Vl0L*+VKY}YFukZ9$$)KUY@6>KeZUFpI2r;sTJ&7-L0rp%bN%A&ApIi*qlCMeF3 zy3VxuofHq@57lzoj;bOzE8bT(VClj8p$mATLo@Zz^gMyLerT4w%Ay{EVF%d(x~np) zCEl;Na)?W-&1O_B6%DnF=~q>0OQRn};X)=phw<&#@ZzvxG4sDqQb!L zD&4>4ZAcG+>tZqjMw@O|ka$-hCfqSHI2tX0bA7)3E%~H>Gqm;%kLx8R$nS?yH<-xX|-QDgEH0oW30@5z#kw*^S-Uo14Gj7z@0?x-+}vkQevCfu8+ ztceBAY$g)dSQG@I`fn->0xWTAE}DhtXKi8vPu3mZ3%n~Ux!RimYIf%j*wdw})F{GK zjHK`w-5ew#rb`FiDKrJ8%!e{aM?f-)ku7?FHZV+DQJA(rOny}9lGD9t4&7oY zr+%Kn{iFFmWq*Ib-V@uSdFT##Qi=N^ioU0q*2oVvQn<(i-L>_4Xa2O7-!nwHdCH#_ zdIrZXypRi(nb47vZ==bSm>{|f8V)fJXR!+(MsR5-%g3S?=WxDy!Su!2Eraxn$F4S& z&a-;!)Yp0b%negb0+aFF;;NmULdLT~A5M~Hjs@7QW{rsEb34?6KR1X>@z842hDR&R zgc)Rcy@c1WZe@NSZIs}TN52-0uwqfWb2wWrQBmkC`3|!JZN#cJgx$j+egw2FnLtDG z;sfMM|2v0{bJ!=3dtM}}nIhQ`DvyhRZr&f#T~?31Ya|71B!#$@@@j9+ShDxy-Y(CK z)rJj50oV%gGZf&MjPKRnLHhQTlL&Dc7d@c88u1{0(Bdoo zQ`YwH_7V6V#{dEcCi{-%6@T|xf4|=)1MBVJN7$3w|K^zbKVC~_+rKOGqBIrO>rHQ| z;m(&?5m84>_vrtr?iPLqi{m4_1S8BMdIKN9@@MH~{ptiv@R~FSn3O~zKrV39GK9V~ z?0HlB&xcs^xm}jIDZN`jSaxrJ`%L%wKg2w5MN*dZy<)P zeE-kBSdI+pA#@sYIH@Wz9r*mf3>g3zp!-uws_=DN^!Z zmgg=2kSoR6VJFhu01jKvRy>}BQaL4_#RXK&|Hblt>EBZS4 zp8sV2xqpdS(7t=ZL5v#iMZF#gY|v>lV3zNm$Y!%hGl1i**7&Irr?sQoj6 z!ijWu!#kdAC*?l`W`7bXJRT8x0ff_q4ZF+lJbXTB<;38}j@1hQ!It$J+B*3UT88}O z|9VFvOX227>Qf^oZ;BEy<^z9kK!iq^zH&Nv28Jw8{Y8*GQvu@1@IT%=14{*J+rwD~ z4*OT)+$7^TPPO9CPL+Z5JBWU{%ZY5``L@4X6@DoJ@mB$`C!XYhX|OOIc*4Ld)*RWW zt)rh>hdg(puJDUkP^0%LZTDc|Gh{{T?H z&@*7~doJuMD@+H%a+B;P@IJ1DzX}>IN*sl|CNsnw;iqQ-xI6%5#&AF)SEd8C<&mJ1 zAo3CRZ{+Da2=;`+tNnE<=!g|&3kVftEMvC+{G*e}&-;jV;o}7me;1LozjzdtGHoFh z8vwsjsB`5fa*~)W>VYi_}=?`=|`P6tqm9+@!v3`nD^KrobfNf%P1-uq|Yjf z4QLE4u_tSKBN3;)$^$_KFX#1%j<^;1sg2DaFdG%G&2&+4~K6$%^=b>=2=J`S9 z?>PDYeBna(?UiRW0aKA}WKQq9Gi1P!f&nA?scbfha4+o^65C;5YG z*$bF{STMHyzd)M&fdIlIHRD>9{a+}cFT#LK`i=34|J%X*@icPL&yj_$k@o*~bfUzJ zfU%NnbWJ{Y!2f*0GXM+7C3nXHP*eZ&BY^Y+{1nkUx}5(I1U~x`41h|ISLn3T`nkRT z^YIS`Uf1iy!16EAvdDp%))b0n0UfNTuwcE zToU6*q2VRT@1-2^H4( zfZa{SoQNNP)Q4_NubiSA$w^eJpaPadj(&X(X`1FjhBOnnqKecp{{?r8k?~I$kI!W{ zF!enCn%+rdwc;aJcf^mbAVs>yw}bE}>iXC&V7aNR9w@i8CR0yYT>;rkokSeprYenF!)N_| z4E7*^FfS&oSzDj9Hwz6&JTY7C-0wBwqlu~;U{yB(VEC4Pclf+6bpYp9-+UdoC9WOOYG;I9g}Ovk>fj$5>uGw7_{Vze24>8jC&N%=wcV+6zE&1;3PEC3^hdEACp zLW6msZdK=eGK+-a&F&r*f!N|r)4jC_#ZSK6RbDq9cOUab7yk9Il<-KQvw~C9?5vbq zv#%|U#HH5y3nfl5BSaN)JlI@U1ixH-#4(4+6csWngdS|iAIHVl^wGa2b`{nP)U*^S zIIPg!y(ax4TcDPn$6Tx~y;PIvl_w|27f+G&i>U1E$Xyj>Z#1hSGnlTYBs?MLvHX%H zUtYfo#Ul!15POFmdt|sa!OUPdl4gI7Lmwq&Fp{HEJWjqq znfB96-pc3XAPM$i2DZg3*b(V5uR4Qf7t36_YZ0Z#YKdP*occUr*@4>;mj%bW-+~6- zUSB{UF0YArNckkSEVgP4H!L~a&#Ex!qD5%sMy{lkCx4~bRcf_OJ7`_gSgzQ82_@vq zPs!Anu2cnxt!%-$`;k2Au{x?>+zwMcPiXA>M!$LT)XMV^ziCL>>Z7`84;^@aeF@RxzG}E6ddqF+1X+I= zRpcR>qQqp0;+lTov1?map1`)OST^8{0=T7V48%8QlAz+AnecJFePD0SI{WZoIB%p5 zwyK>TLW6&6hMK8X*(Mb!K|-E+m6i!`v>m_Rw9B;!Q>22zw22oJ-oN^BB{A}bobgyl zI-zFF=Jc7I>DRx>nIwC|U@+(!?TL4W;X-8#_+I=aW^z2@`fx3n2~Epkg}P4-5P>m8 z%^vt;tpqp_b*{e&m4dZcAqyfBHgt1B$bqOHF+y?HM`l zD3zR^o09-dYB66g-x)^m7^{(SyIvMu;I%%qI%Fy%hfsKk7q_^cje&wT2W+V99jDPo zNFJ>{#&*F+5zrRkS1Y2UDFqsT}+X|3lD{42j`tFury|8UVbaVdNWIy$6 zTlu_*TygvTkJ7G{#x3>D0|bxW4N!HsVO%sz)JGEnNIEuOG7Y*R=?_0%u=wuPR!Z3&%8H$2+6g{>Dl-$GYxDIqq~D1*3|r$4BB+TGZT8?Xb_0= zz3wMGPHLl_B<3@&Dn^)Y~<})dEe7)a{i{|A)EveFdT#*RYt3WQ@0jm zXL^3yHp(eTeU^!)7^}ysM5db-EdTsYXYsCY%smDKA{ea%McsaS$rN5UvgQcPYDjLk zjiukTF){Sz1NLT_znYX5Yt@%s?_pm9v{zFDiOL9ERvrkap9y|J`Y_J+=VHAz|sWqSKKZYnymUlEGoVr+@Wb?=k;k@$ftlhvEN#3NBWYP-cwFUPx*tlz4t=R z3#sCK4q{xTF}K>Sl~hC4_D_drkPZP{s{_}RzKvN?LJ9#{J*y6n&uXG&gSbtRgJ&m2 zO(04^+-lTU34Hui1(g1B$TGRJoe^j&vNpiBx;3Q4C~m(0;eBcsDlKQ(t*K`WrlnC+ zY4aCIMVxgdZn}%CWSjYrljRvXEt0Zjjmzz*SvFk1yZlxp#^8e_&>UF0TG0esgM%Qr zZiM?^j>kM7Rtj%N6j9za8m&sBi377qiF}i9rUGgT)}petnO14zmTvXc>eqLV=eAqE zF?lD=7gKJ|bsq5G!0OHhP};##P|6TjbOZKc+G;ddb` zDLa-Hh2j7sugd$Mry9ByqHglU#(8bUQ%iZLdHsW*LVyi?s<5R(RRM>)Z>4&j;KUaq zy;|IGFHp0@x_?#@kdDtkqd-G7A=z}?N5Ig z!}iBRd^h`ImgEE?*!F*%5kDD0xL33Cn%D{9vPoWzXNi}7(Bvd1$O=mHg8LnoOzRb> z$zS#j+x&YPxgwMh6vYSPChY7hm*4w*EuMph6_rPpSI3u^SG=hWCJB;fLx;x$0|OF$ z>Jl%X3xb|bN}QP zdI1h$;`KHKzv`%_cR_TMYBWu(V5@DOe#m_rE5Hn77##DsBcAp%*uAp2mlV7k(~4bV z)*v41fNe?hMy0z)T;d!|)1R;Z*=meXb~ak4$*Xk5c;lq6i`%b021&f|P+w$6UY$k5fGth> zq5#X*tt73=oqEaQ4GU%Xs;2K63gPgu!J}i&Pj!ZrcWE{8E$72*DOad$uOqj5@L!PT!QixztjO2a z!9`{{Y46a^ZIe&uDXVjQaQoJbu-(;}%t^+@uROl~z)P)ZK+h-Cr*N}WsJ^^*5XvDK zzX9c$%EFtvv{{dRI5*rLYQSzI$JX zwo}AcgLqvEM}n6X^Cd2Vk9qqdku zaeWmf5hT}xjs~S3ITrm`$A$$i*{_WUf&sd^9agfJoz6Wj#j+|ayb1n+ztLkcJS7&? z3mM&+gM1^#9tojvEK`40^cl`PhDvw-4Yl^+2vP+HKFr3a49tcaeWS1|D zn&&Fhjqe88T`|Npk*WxOtMH`ua~=D%PiavWGHtAgQ9eOXQuhdfx z+6%m>)4y-2NoaVL1zlLm*b82F4*DbsLEz1(*5dI{ytUi?9cU!RkrsR!iulo z*;+=(El)X{SL%zKIq)_}%rKB%FJI-h!k>eE0;X^b6q8R@Ho3e;Jd^G+>hg%6)j$>d zfdfzj-=W8j+(VIX!=4LPALi}inRi<)&|jjFUqsJ-S}o@2Vnr>yB$Ls8L*VLR^kCeH z-IEv4Msd7*R$B6Nk3fy6o|lvxrjw-X1UT;P*DmcI zxc&ijg2Dihh<15Ko0|W-TTF>9hMUH6MGZzvT4%NM>=}9)Na?-`MsT0y>@(&qAb~Z&65Ml= zCeHvhuHH@*IWnZybQt8j`cYnT?J}j?Kt&isi>$vB6xlQCCQbbJRZ08${@|1 zUXsd^6PhTix-q+#$G{Twv#4LHPupu>aW7QI#tG!*cJ1V-l+ZAI5RK9eyWu3CgZ;P@O<=v{1 zcQytXC@VYhW#+hy^lF(hhCLwYXb;kek{}o9+FxyDbz;Kg^0Z(a%|$Tra7s{49L!kl ziO74XH>)3&;;vq#*Xf*!N#2N1+6U0&#AMpgjEq)j#Tr)gxI<*y9a5cnG}FN6&6MM8 zY~igQ%5fL!$s`1~)t!slu2V6BJm~3kr^DB`g1t@D3EUlXo^BRYVqNUGSqc$J_gG9G zgd;zpz%OGoYw$D?0I z=-&@xj=7&G;@X*YBk~$=e#?<5+VB?nIYx7w>pE`3D)H@2Pi<-C4Dqzs5jc$JSPIB-(aR5im+n=Hl6D|22e6e7498cA81Np_lzvNWb**D!wL zfhN6nNBZcbd@&}>)T9NkArX9qIpP_Fws`WpugN-J&QkF@^oIo8Hp(|?ucY{urh3<4 zb(eGe`3Y^DHJz5y(XkGz!bIyJ*|uH&-Q%zIa$z9U5}Eoh^|ZH@?d3$sNhL>=YUdr3 zjWBNTI=1YOt!Y0r;@&btXykgoIhv{_@s8s3e3+JU` z7xdKa0rSgS1fkG@Sx9|ec>XPi9bZ#$xEImb56Wr7W^0*UDnZi3{*naR$qlzU z7>^QJGD#ezSn1A*OVTz9E1UX4J9+J_Uhxfmj&L&b-iC=xHB}>I$Q+0KK z<_dKX3%mm23=DVhVLu`MK_w+^Rzx0r_w~bS_h#SiG3zyjt;e@czOEb<=APGtvC!)* zisx0u1W8fA5jsCm?j%-6M|cwBY^jv#HdKdsNSR@s0|_Y74&_LRtpxTu^v*4iGVP`h z@7G-63H0v4j~y$qkh=kyX^=y?m=1%JYxHxJ-v9-xWL+0JjiQZ`{CZ5A*w4yL@zvo@ zAs>lFBGvoflN{u|v(lcNO-F-6~JFchn+`}Q-~ z39qc7F2(Ejhj-iHzW*?^W+npd{QZh~cEs1Yv{X+rev+hScaOg9#Bi_qdmc3@k`cBm z>dzCzmb_fHEAPF{4<$`<%QhY^aRHl@+CVCl@N2OvPEGrP%sD8;>SU=n+y{>4%L}6q zA0+6#R^gff|ER2p#f|s&J7G8!Y*{$!^qcQonG`3P$2|7FyK~v~Bmw~(>6j3eyqEEl z73g@c+g`ne2Fh{HS#K#IatmYr9>I+U%SXy<4pxwE;*bPrH!XMLMkF!1W@5UlKz@#{CYb=63(}U&>=w32i@i%i7km0BBSt* zCyf7Yo2i_!^tbz^t2!}=tcC5a_BuVdxEM);o8AVU_DxRdW%L_trg_}X4?OYYxe|_i zsZ{nlX`=C4mNSMso1;hcD_}Y_((-GZ$(e$TI+F| zJO3;USO&aqFr8#h{9=A6n=ev5hMP(4M)BI9etMkAR~UVLlhkp2Ot4&9K5|mK%|v3e zsN^dE_89y?Wk#Nb1J$l?QSA#w%ijmGgAcOTj6CB)L$CcND=Q2~?cz@^sq$8>xDJYS zcN00$@BRsyG?(<<3j=9s1o=LbMud3)EVOX@HiacuQ6U|OkuJv4as=7_=3fiP! z1h4a2t>*Ub*QUsn-qY1W@7k{RilsK+i{@grk3s@Qc(F|7yy)arY&#eli_VZ|SOwLL z?hc!9$H$Z(sj7rr-Les8Zm4aY9W^Gm((7!|8?X@x!_6YJ)%g=K=yTq@l#0RmDQ5NM;#0t9vKxUhtECg6enx9 z$on@9M?2AD>I(W{{898a9%&=Oa*d^y7jio%v?N|gk8fU{*MU5$A2v^ue_z$}DBU|| zvF!vD&AAbaVP5Che&)|^&2;leo~lw}Xt-yGCtZ}K>OMTnY*e&EsTzP2Xrk{ln>fk@3eUuMu^9TwTLU7uY#DR+i3{wuvG~-=#@|P4 zxCy0V_>H>gd8i#RQE@{uAwQ>WQvA3y6R2u_+E59x-_+s9CKdHQwOIIz_^!Q!fnDjiznR@K9d0D=l?i zizUN)2~5cs%^4kjkPy00v^7P?mIjUQ<#E5c^I63~exd!)oosR4F*Q@kW8TIZ`O@(6 zRxGf76rc}qJ{jIb zj>#)?Qg5r65(WkDdi8SLqN2yX0pUS+g^j-8*u)9m4N5Zq{cY$=C(2UTRfkPbF&wMg zR@mA|9PdxP;$AgK`d8h>-z?3Mz7O2B6bT<6q`-9u$PpHA1BlEn%xf$vIa6>BamJk2 zt4*N$ljxkad`ARvnqp-_F^f}gS+%w-EP`3VCr8?^2U1;FRk%<0WxheI6V7=ZAy)*5 z=*gJD$3G~Wd?D@aIED+W>iuY`huZ}i`%oLoj|!txGEvs z*XN?rb@gcE@do7hAXgMwc?!gJ}?h@SH-KBAt zdB`jCPR(~Qw^Q}6;v$u5(%q-iXP>k7Z>_yFe}REB3rq{FFX%}7<$;EPKv|rt+K$6K z@V0*U5|dq>{bqzR#Pg-jFi8a2typz#ymfL*v_kafQc4DjJr%OO$N;EMIixoDcZ3I3 zJ8+MEUa`Muq6;~ym(~I1wBKTyJ{a3Fv6w7nrF*_;a?rVHT|t=8>V-p)@J0cr0VQhK z16)#w^7jYv*1q+=HAlL?jP}mOcMt9}TOw}PfeSw;yH&IlXKT3zkMnben*3OXyv%Zh zd8=BwFpSt%aHOgp$8yf>+mZ82941+lA14D|-3@sM8)^ zNYTqy%0E8A}Qu0nS`+ z@ARAEP@Z!e(Qu7lr3=PisPQ`yTM>H%Hb`Gc!&_O|EbiXBQtyUg;beeO-oN(~#o*s; z?|Z{aafTG@+TK)E%tCnJQGFFb|L!O;BUPbQ3q6Jxx6FfnwkRfe)H9-k0XsvpJ5wm1 zJK@MCGg;wEY%YI{<>0y#b#VA?hBVO}TRxN(cxtZO-VA-3 z^fR9B-LQJQ3M0Yu9r$QrF!u+DR?w=y;6Ltx=ORGYQEzXX6gRTlI12Q-LK1XB>b@X9 zgodMX`XF#|^^fPp4Y~*pT(YB7Mu51NAyCpti7a9irx^i~HzDUSyjZ&RN4EOuQvm3# z_*X^mReo`e-&ql&TJ-}ItJb1NK$LEie8h0FU zj~-~f1kpWZHXT1vUz;2Ef;?7OXSTJd1%-Mh-&I*~2wp|}Dd&36#|E2#Vac=z5W2pl zyAau+z&q!k7x$N+xKu%~3VQrg3Aaki^3HN8omY9rHrNQLLTdX}vxh(maXO4A2gg^fCdV4izF+ebZj zTfddjcX^k5VzQz+n0fH}7%n|pVdIYQxc&f_$#jFmsb3f2g1d9Q<_=e<#XqB_`)-sw zzqv5f9V7{qF5r{qcL$j&NVqX>i{_)XVcc@I?_#cc-X3)-`Gd^W*Y1;9(ag(PW0V#R z4|}CTEMt+FFUNHE?ha2#I(`;VdtzR?=8Y_)4w;94y`4BQq6r!oN z3N0vbEf=_L1GGh5d(z8`ImHb3CwG32#8j-pD4x+z>ncJG&8klMwS7h$jpr*3JtbK- zh!memt{;Mh)e&k+e<}_x2<@W8t5kJ)IXYO4`fKc4Z%|`dwzM5hC3=IHyRl}SKf|d( zta{W2t+ISH(8$DYqS6XH=W9S9TrU=r@f2F|oHft-Z9Y;ybnt+1D3Cb7##!gcOpWsa zsVx6>N{p=2+r#M;3f-fFiQ>}U7bPx`B_3BHoW;#;-A3;ccJ#GlTcbaXCaL*7Ke7FH zb2``;iM9Ju>->ecM1h;2AVGQ|6+hPR?K-7cR)_A6Tc}Nz=XE}Pm?S|xfGm8Qi4hPT3l8ZR1)yo4c4d(L1W%w`H9Syvs1=;N|}fh_LP_x9NPvQ;gf7WwLU>^e)W zBGn?IdQ=;J5Kvr$^7R9_Gx&5!BH2h$7PKGBn|e6D59}pu)F2Sq9YtQ3W*F)PvaT~c z8U+lLW_+&;AijRMw_1drdDyeuf=9ngNH4%RP4nvj1J&*e=FOs{ZQ-U_VraI`EZjd& zm1%QLfxs?2>_h#ghN8fkb2W0A>(&cG1{i*|5}dg;`v8h!HLSGD7??bh+1V2$!JuL# z3U=S=&(+?iSh2O_mz^yo5&?aoiNlSFPrF0m5ooDLz;bfKe)j&tbLt* zXm%JU&cT^KJ!0w)Zomp)km5bDy4h+^3u)Za1(&s3Vy6q1pd?8pP zgWf1k5)!7;_%ClA`;5@$tMX!I&OTAzlyJWv!4MikPgH}XR+F8l{{3%D zuO}Jd&?S633Wd)SRB)nuv^udrc)?l0^5-SnI-epx#Kv;y=C@d@whYDB|LUM*k!DGdN+IRBJ9_rWvqo8_@&enRz@AoxK?~c-uibZ)U z)ykOjLX#ze2g4oi&66ANbteB#WH!M%dg#{YiCElHhRo3q+ZTv?%9u%_LsuwcNp0sG zD6OEqZWm27G0eAP#+{nH5Ub;fs#~TW(!3XvLl1wIY<@`SRWYyD6yzA1jJQVtK17gB zX)pT}AC=uZ8q5UO$+b=*-V&5CUiJ(97Z53GE?PWkbKyP^CcKEF_Fp z@i(4xdg^58)IG@DN|K}?#QNUP17j!?kgd6)d(WA2v-;jBkChS)gm0APS zr~lB3K>`5qq!T64F?#3LNiReyAcuxxO&$yDd9;Qa8YM4A2(A?G_Pokg9EUJXAk^2B zdJ2gFBVO$*+1SbiHlb~ki^UMJMg9cqOJkZ&;-1?ox~m{=f|#YoWM{djqKL|*q3oNR&~z5L5H_NBW2S6kdPDMo-@xT)ncw|NO|HzdqUj;h(i$3wwWZzQk|+ z^(5537TC2nR(7^SQC9+#!dqV0+JfO;a4Tta1bWLfEU6GJSyOGcZn4hVe*`(iTV5M| z7Da3A=iGt+4_?YW0L1yN7_CyV!7myDV`j}k4-0parD^o1Evwz$+ich(8qSp-b8(LB zYZKdp8>e)V4I^o7drs5{_&t_p;#fGkZz6lLXes7?z0I87phBPqfO|DF){LODP7bfM0q^a!&IEE7-4pRIeC=SqI=^!5aboE#x8Fsoc~I#@96 zp>nPY#=olyil;B*mbSdS=bX#)E|BJIOJp_lX)k~n@GszCsnSQ~YITiJ$9YNzu5ZDM zmct~qAQs}*)N0=1rFuzhRtHhNkw}Fl1^T)IIpT-{*b8F<5xiVuhgLbEQbm4`B_&CsrwgBBunHz zRZ)lbM>LyOFqD?XCwDHYmw?1gz zY;^uX`Yj_=6TdaRal#sEver}XyY7Saai5vVA456@j<@4{yX3?Jn)`m5mE_4n5 z<_|}LLjkB}pP(Q8&D19@78%8a_-(M4qT_D~bC9dTJ_ctxv)R!j1$=D|}I!`p0_Lf=GRDX1XJd{8T3dwBH|4A@ZZsB$Xkx3BGWo3vl<^i6n&K)J>RnvbgBU0YLHQ>|1{tz!fr~3IIJ-t7H8#!y z0mS+O7W*pI&lx^CC(*|`E0yLBO(Svk{3Q81=65A#v}6ssRh2W(J_{Sl@&h|)%b;Jy zxJ2jM%^fQUBw}`v8h3O1rG7{m3vn=k1Ns)|7}RR$4CrR@$EOME0ghV*5XGmnA{zgc zE}$B`1~q3m_(?BriC$uS0BcJB{SATvwDOz6HI->*FtH+ zpOWmT*HLBF)sHJU2-mieRAMe?+omNwVaz#IvYC@|`8GVgqSZxA$8}|gY?kg7?FuH= zxEH8)iurC07UgPQW6DmC)5g~&KAgA92U}Vd#Kn24;`fPsV><(Iggxw#+Qy%kZrp+24f+*v`2jc@b0(Ntw*K z^gu;8c8xofcZxXBN<$f~tM2K%cgPLh1oYmK$bXmxd(3mDRLUFCI%0KEw2@4_g@?8A z7CtmD#t(V!PIz&SDX@-z7v0bLfH4G=uipDuJ&?R*L_A-^CE-Rz5Jd@>)u&|&7X+g{?CQVb_I1n$uMy!WCBQ-5WoLHgB`uAMGPLVYC*o^o)$fm$(9)rNx6ZIhux=1kI|hNGa)TX+ z@mK$>thEPOH`x3g#~GL)@M>?R@qAOP%U-PT<~LinoZv-*d#-RUmq!NiN_ zcPKhf{T&49_g~a~EHv-LPVC`Oe!RXQD7;4@P^p<8nEW=6aQ`gVQx|Kuut1NDGe02H z3&63QF*OYJOX+XN|GRMXuY*N@P{ig)Le`)rPTg`Jo`?5`l+VFqHGJlVk7`M!sUrao zE+fg6bWB6b6;u}rwIK?KaJimtkg5yxNl)D>SGy$p;w_N9K@g=ie^;v7Gf6G&AjR42 z`C{?WosDgH<4?}?4@vg3cTj% zT||;V{EeXfR8DUs6KL~rdSgbJoFl{_k})%Rl(&r?qjc31`nX+Gnma?>UuY}R@LgBw zX&eehpwc+XlDC0Ih7uGXW$Kyz5{c`jqJO#v(t52z3q!%AhfVa~!=`Y7Bblgi)f0b zs&G>#ywj_K0+tBBZ|saHWY-e3|?x6t%v?S z^}U|pq21UQZQBX#i+kLsj+LzKE~nxS#ww^X1%l^4E55lR@hn^31Xa$~Fis#B1$cc$ z;r7D0>!R=7AF$DvCDISxvbyaZod$$Zpp6oOr#&s>n(Jbf1guoTyJMa;{rU zSoF~1`OTt2379!OYD1-3r`UYCj%_M1;u4%oj$LIhOe)ky)?#tca zgxkYr@fCQ8^!~Y0FZq3D%}d`h8V(E=kJppdk)}=tWde0MIM?GNq0kd~0vyLkQ@UZ! zwJV3za+Lk5Hbc*)bBZ5J@0<7=4L-v;V{NZ8l@SKsOzN${IOXGDBAeV9Do&lAfEfLd z#&k48y3UK=h8NvD1Vc?|bl}u}3PDq`nR25$ z9cOhIA%a;ZEMJ>&8Ft@U9`}TP)Q-+QsRcXSAFp5IW7CnLLcN6;bPUW}ZbS0ZA9B1E zwp_HG8tlo{zQPZemy-vJ5&}V*1a=VY7Rl-qt6iaZ1`23!`xh=b-p00!MTz>}-j;w! zbKzsA(WSi(6sQSzjHr2D8ye&U2O-ni%a}EVnlf5fO0wh${ibCDA=d{JFG3;1Nwyf> zYw2O#$8{RYI6y@N8LJ4ou{b9^d5EVpNv|P*K5V>fsx#~||7=-c6i+wTq2H$a=c>j1 z2mm*DTo%{rRwQW#{XDwjejZZoJCt^d{hWQOvEz?4|kdAt8U%Q45J~*AJ$;(5;C!Q*$8+n&y|3`eMIOaSGiS&qp1Y z@j?$Mu)!KKsjor^78FCm#0HIDXIUV>R~krA{Dw6qDU55<0b`o%Qd!*1{3wIeJUQM^ zJGqXje|vB>6TiK1%sP@8GoF2Mydl;=Z=Rl#iexs$aeq)`Jn8g!JZ)cRPI{Bmu<{~W zOzvq{I!5MPWyojxjz5jL&M4w0!<0B3q#^V(ZRs87DL_jIe*L99b$FgU{LnJu7oz{Y zq4OZ;hEzFvwUFnj~z{%t{I`w@Uqz84aJ+#lwSIY$lHNS$OF4k8)7s z6dc7@!W3BCjzZ@eK=t3OwsM5i4pgEv8{ebFp3u)B2++2@QH#_B6w8q228+`ap?al_ z80d_f61IroD#Ou`B^G*Bx`%aofq!dFAP%tjy7?u_vj6PxuVM)*_vf#Zy3hr*MgK9# z0u^#x9N2=t*5MBR*E)-6`$~zZ#Z-0vTSNc%(EqK#|5o7tk1K$w3OZ^C(Yf)er`_o* z{#WLF{X@IR=bDR1p);aWfcME7Rb}XDxJck{^3N5B7^&-JrdauFYREr32IuPlD3tF; z7zQs_Wx36prTuMX2**39vG>4zb7K+|AGoebg}Uf~_nn7PR~~qRXbLbO@+o??iN^h# z5T!;7TUO6f^;0<}y-h3T)us&~q?++(^bEa{`072JO7tbri ztxhF1?n5}Qd!{hus}7F7PwF(%SOVE|?+LF0{Vmq&G3?3MevD|f0h9>1rSrrRo>9bR zPN46fx1tdhYPxzylspYbG{mWUbXb7*?uMAyWS6ozKm$|zEnG64)zF=Jo$Wib)Hh@Z z>vF9-2_bT&P2hn4kFHAi9#EZwcvwW!>9%j+G>Dd4cN6C-ZW3pm>rn1&4Ut$ZhO{(A z;|@Mb|GFH$wyr+1J7D8ky6VO}1f~3<$jY6gB!TLt^tzX~w4Q45VBD?P)B%S#QKVd} zZ?bCrB(M`|jJ@mhSGR%ZM=KogOwj3Irg!2jmq*0129~Ijc7+EH{fA4vG_{B*9?B#V zK(Sh2Bqy54ZyBM0lYABIxxYcN+gmb9f74W`Fq!li&ry(;&Nb76g=Aidln8apjk`UY zwZ}KC4b8erBkEFy`)j@y4-nmg_q8xK&1!uhZR62T%z;RCn&u0MpL#`~WLlLp`$|P& z{m#dS%2Axh0*;l6;L*e8Kd*D0_=hu7;Im(wuvif9e2;C!RQRQCBG>E5=#~h$v9vkq z>~3tWvPanlqxBJ=L_;@6$K;QOY#-hf@>%>hnISsz4b`d02d)P8`^$Nl4M}4!MR3oF z4Tvh$aos7xksz6l*=*K=>KzB*V!-?TGll%OfW0ejAbD;RlW%1+zYGa5v)2+Qs5iPP z>y1Y9EJZUH8BP9-t$&(v^Pqb9fOQiDs|wX12#@3jw0(u1cffetsFYflS=IB5B-k`w z6k`+ux%Bve8Pj(J9iN5vYX&J?P9&1S@PcQ@=TEg!V;J zhZPH}xwF_(f*w{`X$_JyWBBt9oLIgA&dCYqx9h?k+q;C1s#0IvZq znsxq2@X|a({~dxezrdYhlMIhnvv-vG!=@f*QMbvR#u1xa=SQ>wTUi5XIR|o6Me@%d zmo&a5nmy)?uO<~IO$OE7e??Bv|3Fk9Or>6>TD)w-jm_zyq6erKwO4TQ@^ub9-X5!b zPhN>r-%fVrb#jY+Trf)8sN=R`HKyvR+O9Nqhm(}sdUl@$u$E2kn}>Chu^|?}tWq?m zlrt%kCJB8l(3sn>K6w;4o1>UKVs~rFCg4pKlP(Sl2k&{`qHI?L$ z;D>tP&5YijF7{mif>cpNtRdg3i?~YDKfDe`Xnh*woZih$o-u?WxnzRdacv*z);H{= zF{Ro$K<1>mT_VLgJ+ahT%|w~`(wdz-O`S*mSC1~}HK8OcQ=%2&UVmLdX#GHuLJwzE zx~4ogazpDxrn{kybV3dE66dv1v%bnV^OQiNc?VVpAL=QJBxigz_<)dsddrs&c%4BU zvvRjGPKffIR0teg?w*sid=4(YF|w8VyGRestogMr&yW%6*D`Cf!hU75kyzjaXpP+S zDizupS2rzk(2X@EgeT@25^6;pin&~#XIYzf`Fa{H0na-2W^y0R_~4{Xc3d}Nt3q8? z<{%wgT6gRj+88P?W<96Cn=Y~^_>g+>mdXp`1}XeSiSAUmIF+QxS8tCH=9TA*E6eKu z6MjPQsrA`|=1X9OOoQiHMPt_5u=4R?=E)uiHdfnC#5~uR%f#sjV|mcS{2Ov-<;2DGMZN? zVjVlqab{L|=hr*4tX)#lXjk}Ju-5_d{&DG=!BALNph{-epbx%)2Erq+IWOU9w;{!i z{FFgo4~xV6S&g|sNa;97vx>&I{=($vIEqxq8G{Q;A0aLXv?U<0KA+J2 zvO22GdjAoB8Xh+Xb@Y<+(02#B6)(N3ih?8M5z-}>`<-k&xVG+yk{VEg-vwKj2J+9i zb2tB}aM9jBJZ(*6VgpCT<-nyPGf~Nm<+`$~S5@#6+9MK^msqW5SSdPvL_cLv2~KCY zy2hJ#@Zu&h!*Op(IrrVl|TZFgKbK|*-@$k_>%lu?4r?}5uR&WrS#j)Rk&T^hf z^%r0B!JSOzFEVwq$Nq&PoOm>b8CK8e z_$lQOb^^94j2DV$m6pK`-FQ5To5K5zkHzT-wS}JxSCSP|xjaQEm1@-=iP<8`J1M+A zb?tSSKdD^Ft9Ys^RV9=z>kGr6{)>#yv|KlI$1WFSHB5KBiZ%q@9?yye+$C^SS&mdN2GS zn$=A;-$TSwStApEK!tZ`CD>ewJ-*_>PsE7Jkp$b|AWBWV0%K zyd0mNJ)61-LCqRZW_PGO>cGvduKc9!?vDMzLCiT}++e7-0>$c&+8>?x2F(c%?g!qI zvZn=_mHr1oCgE9Fb-@9=S*>@^$#DZ)6{s0n69H_))hsF4%1p)UXLSC9Z{5PDp#!8U z;YBczJzJN;ht%NnhUVAyXt|wV&eB}5i)uPwaGqNb;1l&!Gtc;5#_Nf|mf_WKv8KqB z;4*!ArHFHtVq>9WSS<}M-)t_plXE zcs93&nhC6NF8q~CI_aH}8gMwJJRdd{Yn9*w*g8KN%L_sZVHDH7DXKvxmB?N-qp%Ud zb0NxwUL|a-Eh2or0TS&n5I@Y2V6vIl)Rzgc#8L97lJ;!|)Hs&=uRh^SLx(tU{GLx! za(hp!2+n!d*!(bz|1@UvP^d&6;SPVr16kvgX*R)VlIQN>e`)Di?UU~l2$SfTxx$5= zt*adm2TdJ22gtwkIIuZaL{T1!!@DyjGE#2eMm85YUqCn;C%|ADZ(XPQ@v;?e6bGp+>n({$x z;`xJMS#8Rx1mvJ1u(0dBTSn^6b(XPgdq+R33U#3-(TuaviXM=%JVZO7e zL$1UAmTQ8iQWp-X^dFyaSp`g{WkY$BpdZq^w{{mujI}T{+Qc}iB>rAxyYy!UqzxfAo>PJX&$}pJQV_NBd-ki*@NBS2 zUp6rRdO8tF5nHVFZq$1IcKi~(Xu0GKid(wbS1%^S-2<``RmPsuaBJxL)fV!Ij6kup z-{E*e@+v7&nrb|T6nv~1+}O%h4#cE+WlANTmvf@jeyx?t{-P*sHl$Hxh1sWhs@KC^~W?^)?|>#b$m zznhUdB~hk#=9IkOMYvhJ082n2y6G1geO~%DCe5ASp22^wesyJLC0l8;$=O5@ZkxE) z>gGf8^>`D9BO4KOdyK@?r)K4@h-5js^NVNc5`aFOSbKHtUif$oLkPP4iQ|u40y4-P zjlb}-i-?;#IT-hd2ctdZVa|;Hu2~X#1Dt3~WWA9~ABGk&yyxgZMKaW!X=6hY)vI1E7ss6|oMT zyEIB0>?0L6M-E0qRK#>rH@TtqZOIU^wen^R>i}uyy5)J8Uzx>)_f-P(GWxuB+=K|1 zE8$DunG50BF`xwu6Acd4`Ki~jS8BqnY@rP{$#;zw&sk%&XcdD#5w@c`Ea^N^e3b?2 zVqA{C=WyKHBkR4wze_`zel?o3N2n$E2;#mPL%E!~8Em|Enf9*bg(FrgyV}4rn7Hdl zB5~0>x}Pa$or<|$+`Cm~w?jk*A)a}oJRNXs?HizoB0BHDA6PNS*4z{k23hFIRbrkg zeWOzeNRCD{nun7^OO^sIzdm0N-xLZzj}02^Sngn*I8M8zvr+^6l`b;7nt|Lm5pi*a ztQ`TZqMgN|1Y^1^u%xow9_%1`0pwD>8^EZ*v9b^!ui#kUDE)e`|E%&FzZV}5ZOcOE zR-85X*;L8b)Da77O7`dNjYqk^X^ZIhq1trH|OcU`@y zX;D%cuB2W!0r$;YiMXOxy!xw|6j8y*(Ge`5E-IQT0w`uS+18k|WuZPl|5*L`X567O zYXVKpG!u@pB|$rkr;pb$2kL&R(!5ga+&Pd+r##FdOb>C z)#zl28Umvat$&;Qi$OdlzwHl_-8~{So+OM=WD1jpcfVbwF3!Ly*7$h1F%zF@Gn9IE z|4z5NAUuUpee|}^I=?erqcpl`&~TLoO>U9)s3KEu6Xg9U>4q;M?G;C?9C zlf6{CN0I|SA_=#*>E^+aKy<(nEy_kNQOp;t$MCsvYIb2fSL&HuzjP=@N;Q>FrT#k< z)|LnY-p7;ED5v;nj|sfz#mr;ybA|ne*BnT%9KuNU>dYKMM4!>$2-$5t^4>h&JsG_6 zYqka#xAhxXfRXy6)!avl+5-ZJK<;%(+F#=te$*0&u(yLGV>Q^y94CT_k4*af`P_Ac zA%~~^*CZqoF05kD6MmZ?cRcncTKMA11>(jp;HnXrQ@Np=_s?^BH+vt9$w7_-r!hFm zu}D2I29Dwyg32DMTR{an@xywR$`TInIqjA!C!QL@GOa}e9D>uf{mIMp2eJ7C5zYwY z&}g;a6m0d&H};8iD^HJY%zs#Y*nw+)QDZn;$hXaHnvA>VX5)PNZD;BZ=2@z_?!ka^HiJ|U&jg*6%=KQZcq?2Oa| zxwhwoU|lt%x$103dTm(d>80j}L<$X7MD9v?$%JyqdW?eXWXHLx?8(^(2Bu2o#uU~P z2C5Tff4;k`4_hcW%rxV2$@F7HKIOe>oFN}Ik)h}81x^Ht&F6YgQkA+YeT)f{`Ruap<1ld?hJLHG1iAV(lwgP4MlLS%Gv%oafUd zdMDOW)4Fd-OeOL|iV6z(iU_$T#N8XxF0Nq-9#@Bt)mREYuC~Lo&m|5Vui&XZ!m$S4 z!jk?{@4s`Ni9iZ6Xoy!B6>j{8-7|?;ZD)((rSlB{0Grd-9eAJWHx~rE&I>0}v_a(uTi)C0{SM@53)Fvi zO})^R4>`VyhNiR%st4bqFU5Y2&8%c9v^8PluNC+Vp^^(XTdRx{Q3><1N1~Ck69{I( zxqElxfnw>#Bw6idQ*87!Ri{lxLoA4Z)zI!1YajZ&Sk>ieZ`XL~h#6F==Pg!p)L|x+=_V!RDfuF@iJBIZQ%UMA()}_J$wpT-smFb z1}vA&R7C>1?MHMchSnAr4Z>Q?yRlr`7rc_dPkVc72*DE+6Xx8Saf*_7{qDEHy2}tm zdurF%^vlp41qdG4@iD?cD|vs?{4;zf6LutU3)ip73fO zS2kNMsNSR#(1*2Ef92s4*9jtir^Vs)DeKJO=RYKSWGr}=e}@nfx3e=-;b5FOs1Hr5 z6>TPn8+3K?q&Wd@)7iS208&i6-E?5OE%(&KPh(pG4ROV1iU$fjsojeE>_uX-6fdbY zPN|;md|@~n9+U3>@J9`hd$;#CLvAZ(uR(U#sdy*RO}R;@BU02RK~3LgJs9VjBPJK^ zxguQtXp@`m7U|?d>-$+=XiAyhRJXp)Wpy0AOfTAtrV{OUF{|D@efQGH4}ST8P(#ip zwtg_>=shh>yDYz!j_7z^oHk>gUl|&l>3?VKiSg^SQzft`@%@b%p9T(u@u4W* z3X9ltO_lRiOv4PP;CtF}>&5hJ4SQoCKxH+K>v*WMdQy(Mn{g*U&Vl78V&S(rCrCTN zj#DVviefWF;>+Q5xNdhDEO~GE*|%?rRgM*lxgD$@yOnADFt<-0q`upWdDj}L(%xM@ z5N!;K3{n=I3e2CpoN#Vv`2=V^yzx z%@jr!1~Lv{Sk-3~Ts+rKZ&w~S4h~E;NQAVuxlS`u!cLDWeSmJ=FW=i7m0Quqa?O&J zw{Mz7kD}J_$ZDN+R;TOfam~$|ENGmB>pLXQi2(-vEpvD9d59^1Y?i$HErt)jspCez zApo;NqdlCc7nDBRYH2;(=wla$+!GIVYlzEUmYZQEs9#`TWMmhu7~o2zLihQ2CrUwM z$~y4gKacSf^v!AOcR+_7A!X`dAEX){TM0Xo{?Z(P2{Bi>S)OqIT%DS_kYgZSN7)@P z%3R0xlN@Y`t9*l0tta*;l{b`M;JDve*9p3pf-=sn5zhEQI5N3w+hUHJ;$gp4bY3Eo z$&`KFg}pxH=LzpUdB6!{GsM&GN_#Zu7v=Hw;+4(ziUiK}2v|uEaI?1|>U$nySs7`r zjB3d)R#lC-FS_>BW9adt@9SJ+fw`GT@bq#GMH*0I#!c!DAKXN+_V3UBvN?HCJtnSQ z-!nL>6fqy*z;2}p3ASNfI<@>1f?j(I1U{Lo#W6C-KvVWTHBCn=Z25b{QPscsY_@!{ zd{{3yA#9swOH27ZQBj8~k|gBzaXUUMy;{#;_$ZVI0bqZR$%ByIrZkb?xjgu)jMxb_ zyK&IC=CN z=bLWDHG=T55GBU1Rgc!A6oYZBaUc(xi4H&Tb;>AsZ7>*7n5s6{4b@uTr2vsAp&Wh8 z0XG{}xr5H0L7o4j^XNchNLH#;;#-}Toua)+<6$aFAeV=lgSf5!C@J(#!xt@Vhliaa zHYY=ez8frC7}{VdQcHz>`ZxjKCfU67CbjsRpji)@0EJ#mXq(SL@s5mA98P~G%IZY@ zm^{J=dDkgtSnsD68xoGq;Q?U3=#_xwrz1rxdr^x~L5b%KgBq%uR-c4m^uD*`Hxm>%0*#y&DVAV2aCR7@v;(ob+$={V=1# z-AgpKH4OLi4a0`FmjLtiRU|gj!oBu(x<3MqFm1E586HZ1)Ze{CO~qO*awvCF^)fa! zd_dkix6wO9>s@;mESK>@_4o%2!3wu9d~HJgt?ssC$aeQ`*cubft-ov*+`(Vg7BN2e z$VH(AeFdWwzW~fWJsj?>j_ifvm{bShq0T^ld18Ch>Z36kr~se%wAS4qHL=YtKv!Ny ztqWh`)hkRV-FSsl!FCTx{0B7-L<6=iAa6~mJUv5FY3D<3pwJg=ZNF$|{98GL@8D!H z?Ntb~m7R$?USuEeKVN;i4ylP?fk_;4-J2sTBW0=g?=SreS@w#R{DFQOyZSpwgO5K7 z^U6f5%xOMI1CcZG9pH}H33sC}@H3So@Xg^5l;L2cu&=Tl3!ArODTSyn_JpVueWn~*s>WdP-BFbztb-2j1EYpmR*WwBv zMCwOEQ%~#Aa-pQ->^evlE5Mn9f73I$ldcn9vH#7JBBs5LWM^Fr%LT&I1L?qfw}Pvi zu6vaaKWxPuLi3YT>W6CU+rM_%K9d7mQ^bIQZsvb`ZaQ&q+}Ct#wv{da3!xOedZ)-! z!u&$9;J?xW*n?@j z+GdZD+*gvx03?lYfsvBR#jcg3RhcN{VI;MM6ds#}%wiy!Cr8pzy^>Tm2f9KRL`Uba zU{ay8$ys5$Y8?d_Ch%S3VY5`5g0nw0Jo<9?CqawsCI0KfAk6k)2{tYambVnxg|Of+1POE{$3xlA5PFxiqaL zx^(W*aF*EMqD(CB%kF@B*6L}}#zgMR(Y=l*fURzH|FWHF@DY*>f`k;WE*pJt{S=3k zA~(@19+bAyiLBLgA&qEX{&O zBK~)@tU@mFs0NLJ+k|qCWJ+ZiFa)yFz--D2FaVb~IK4LmMym75Bg^+X9|^3Si8mVryE!b%k8Z=W`WW8_z}2x$9H zQLKNBw-+Q59O<|RayUIjiyjt!nug-tO17%vqf;(Ya{|&vy@Coywmf?2+&U?%e<_`> zI-w_11&}?R8JhsW<8ulcOH$uf!{k@e;jsf&40p1^J{{oG}zvX_&)N9A&{?kB+%0V7irV5*jkBo z){Nz|#={oE8DPc_f5_g~TA4Tw-|zrnj3}nzxCs6R9hPI$|1v5w5mat#WQ2&u;N!O| zJk~ER12&(AO(j>6t_bWM^zo}N&O)fV-?3CWAHce;i3N#cl}3!kBm(|gZc%)ofDzRg ze6UxpwJ1wC_W(fq4NsiHK0LKiDa&zi-X#xHjqzW~EOffh24{eY@i>1-sa!dhI+?FJ zNXGB_XDWN%*33qO*|vJxWE~N-KE^1Aj4D@;J96MRFC$lw7LP}j5TEtkx#x^p61{*8 zf2wpf*XMw*vFtN+*MekOEz3$CuQpw|wU|bT=(W4Va@Mt$Rcy%*GF| zPatJd&Z=XZcH3OPprkrh4Ro_QR71>ep7{Nj8=y#Ui2Rhse7Hh^9Jgj7@)M4 z=g3Gk-qG6#Es2c2D*~L2yNb@UMH&~ik68j~8#;xlx6nM*@qna_R=0J9b8l95Ng|Z^ zJC0tQfY`hvf>SKd4&U)QG)sCo3xJ7rz)RU$w{C-Mvvwo*ueZOnd!WE{7AW15p*?f#8a7 z2*9PYKtH{($F% zY=`=8cAypzX*Skt(cTGq2@cSgExL@Qb8{SbJ>cVJ+1}l^=saFBp0}{zUk^(C`c>L8 zIk@x55 zF?4=N*9ETh6vd;9nY#JK^vnMkP6@a$l<8Uce&U9)nM&@goPxdxS>t(lM7zV{osfV^ z4Db%wpxn7GoAJ-?psw777Q-K$D>NW#;;>9(vo?V9$1xZNPP04Rs1wr2I^ zSXtqA8SrhglyW>DeDxYf6;3{s4ME7Tw9uU8`jiX3i`ar&V4-QdC_=rOZ=ug8ywV_6 ztD3Q2ql%!kWV4bejhbLj<2?ACzT1YZ=-J}t9Q)51zMwteH-$jXv+)VB5C^Y4-2P{R zF;WOzEFNk115Oa018g?Jla5!S&h7jrE_F08D9xdRJoHFm_-eIy?A!w;PC z{_qvxpQeEEA7%Bk$K_e}p3a3u=N80*Nr3)nOa{KgShopyFPzCh2CPbpnoci{nTI~V z)H>^vZiu#=;;Lr=C?+~V#&9P{7;kTmFt_=YzX>i@{lD0I>!>KZFANj|5fK4{ZUh9R zONQ}8hiII*WL)Gh7o08c3 zY(RlcfX>GiHw|5gd?e#EW&z_DX&3li=9i^cKkf+11fdR!`)ctU7w_&$O3sa@fk*tVY$&A8B}8 zMNGZXSfUEfGJQrx87(rSwz%`NnPYv=eKSI4sVBQfF>AY(HV3||zkTrmGjg2VQeQTz zmbO7Bd|$ZCzC&oQuJWVUjNjw9sB*RXA`t`ZfXPOWKAjk*udsOEz_E490@AL|&!%j{ zFVxp`8{I?W8V>c##HQpiqb`ZZBx zKG=*fXMXTrXIyY)tHg;-bCa*p++mYr2$($c1rtD+d;OuSOccYaT{YD}!J}tYOa!>m z1M@F|yBc4z)t&o-ugl?nE?M?>m>Pxn=EFtf9=+rLor%1||BQBe!d~pmXeR#BC_;L) z(_#onL!QYrvnVJNpZ)4kZmY6lLG*G{?r>6NsLQYY)Jx**;K|^*kb3hSLZ4CeVtXgk z*T>rXW@Xg&fND|NgQsoDAA_KjKmS?z0vy1t(I4d>ONpmUg7OH{Ka)g0HuY5R@u|M~ znxowS&NAdp=ZAn!ltj@tbV*CTuSB*w`U*OWgjJrXL`fr=5`RXl8wKhwQQ%+|NRvEZ z#t3z#E=eP&&C)ljMHj05kT+twJW%u!Zs5eCUrh4SdjY5W%2cy);&d&0lC)of(x-&g z3+?dLp$0>>bs6a+re$KwXX+0G#>6(%mD!Zk25^XbvY%sICdfV6IxgsAB7Hz@#2-pA z1;Z07`LZQ2QQ{tG0e47RQGQ|#3zx%KIc453Tdt-g@JHxh7*sEf`qF|IBy-Y8zxp8g z3&Y-+P_E-P9gbS>T{#Sqq7pFHN3fA|oe32NdRVBv2|g0i7?+cJ8k6tJ-w)QHMolye zCgugYI7)M^Wawl%o_N#?`V=dlncnel$QcGYvLZ)ngPx6A^!A=kSvo#g9dLT`*w~KT zvA5ZysLJ>qHrYHV8tWTI<>Y6>?J3Ej7Lfoc0{zslA6h8B?w#(Tnlqtq7@gYg8v~i7 zTPktGAHRdTuuL6ly@$ezX+oX9TW2>gSl#mxCS-7AU|E|uG0Mwzm{Tf-=jImzK{Azw zDd&lTqPtR-(FV4US0_9BB&SDYKeZo}BP;J@gfo;y zxb>~mT!eK}2}W#nFT*7H;~GE7`mW44wPh842;4Y;RoO@aIvHUcL#FE!NP}s3kK2WY zAxGZTowVE@p{S=SZt9l5^);Ud?wTB#MzSXtvQ=~$a*_N;}b8)t-OeD`24!#7oCQB17nZz*|KZ&0)7B}{#Jih0z zHH`)5rClSA(dBL{5>rgJXlI@5g1WCzql+I0{333ut_4g?@_AwvkVm0;vV8IqWIslz zGOR~ZrG#Qy%HfJ)SlvB&%;pV55JtDV`kWt2(Cri4Aq}^9EdHoZi1y&yYBy(ToiT{g)!3DCD@#i~y1pR2D z?=+*l`N~Qrw)qH=xElr2t#6pNal5Zn0L|sYJ{D&4q9>Nz<6C3GDg+v#?Rl*fF;DQE z80eM^F=)QfloYPyq&B;?2&0IzoDMd44(_j&X#xs(!K>8f6qT&j;AR%u{?keh{3_wm zIXmEXYDwSXzY^JP0w0oW$crUYZZV}AKxrJmvj7#ny%ZSgDop?ci`~FXyPp67;sh_#o`sJpd z-xw#?`zP1WaE3WEI0$Mhf3j*2U7QNAOLmK^mji5nRbpW^FVU!|!Q~iD#q-M2fR*`M zx({GmJ2-i{Z9-A@*{z737iRU#t{yreKh|&8q>DuG$Cq4ut3ARDzYGxYD#PrfPe+jp zZCWRuEW5nX(m#@ffZ$FAG7wM3y`6PQy_t0JAhxIzUx>zZb#&(?vu%~J7i0l9n;tko zpXUw_yKpq0-)H{xMwu6gm8U*JjB7lMiLBm(C4Ty@a&o>n?PSj)1FF-LAlndgrR9i= zkGTo=wcR9u2oyXG-(Mw-Cp$WET2v#tw~gOZXYi<0nM#aC%pm$5p5W{0`&n30A<4Ui z4+{#D+va0Q$4q$Cs^{s|Q?M&^DT;>d$>p_7ze3GJrZqUf~7GpR?$c zMbu}P+q-?*K}8-vXP%j6h${;3y;-lSwfB{AB-xOR5{6glgmJc7;oXFY=fEztKfN%B zUS&1qV&E0c{Y(0jo}Tp3iC>ciKR2M&sezt!Eo0^U>59i&;zshYgXA1J@hPyqpV7;G zvj;((O#x6`!G?D@?nhuw9CF}hP#r3O73hY2KIDKpZ?yAbJ|l2j>?gbJ6|;)tC;aln z$QIBPI6wY8q{382{W*ap(bO@;pnc6y6GFTC!UvS9Q}e-R%(NnD7+1yzPHOZN_hj)b z@`)T!T3R&kj^k$9f5aT zOPLzuX5cclrSPEE+WjY>n{@yDwklJdttje658=m42T#86&jK-2yoMHEYwgb;B?2U4 z;rP0CIHSXoM#8hrO6)D@PiFTpmWI9sL%JyBt0By0$2;r7lF4%Dm&F8lm?^-u*2<54 zIvqw{%T8o8vJ7pyQ{QcsJGmkRtD||VCb2mbEpZsVW@5kG;I%=$SY-^Z>tgh+uc8ZO zmovywvh6u_;HSr6(O=U{>Q~oH-#U$aXqg-tJhbb#+N9Agn3N&S>4W1@`E4d#t71kN zbu#tjYDMFFE>NJc^DvY@5ISZf6+7c?Gmew#C47U*g@n^@^DO=9pxhgKa#V<$k+UDt zh_}PT{yd>Y32z%SS?zP^#cAd_v#?v&!xnyTj6K1;u^WErrvgwbhBxx5_v)j%xUj2C zF4;#I#;RQgcO&b&hcs;H{mvh@?=ZYIB%NfZF0vjiL~J%TlN~K_T)ts(e%EW4G>?ND zf44xNk%;xuqU>iVYnu_Bk3XDUIb*F3rsU-c-5gp}{aN-i>hTGG=E7wkkYisoNr)%J z!G|fPxf#j3#)4_h*JUD58J?KRdru5k>#6YwG$~6 zMFU(S_Xg4A9C&ar3sl}t)if}wAWxVuZ)el_;*V5$eb!~Z8u)d9xu8M23NLvh{v7P0 zGt1I}yt%da7jU_@$Hk1~cJiD0b3vVfw^u;*0e&NIeil8!kL~-tX$+6x>(wly4hhqC zS0_hAAQh1|he)8*La-`fbei$o3b9|E_tl3V_Ef714u8M5e`lD+K3i0!x{so04Sqc& zu8yEyD=UyA-YsmpfPp&Vp8BsS+hWs1!0-QR2E-{0aquFvI((eK8??a4;l>|(XVej>(jAI)p@g_P9%%gPwdZ^&UM9LdMRJIiCjrt zoF{`cqUQ+(Buf={{G`C+FyVi^TT5+V3l4bgO6-qy7zrSdp{}r?mm8vCi*1NDRkatdRH<1Y_CeSnzN_EPUlSE?M8-%3p;ebQ>rSExi|^;wKr1|8jYR!7O= zZPR>mLi7`$z(scI{w!Fl;XC2MxSY0+Sgy$BeKEd~XA-q@oymt1`+NN_$?1P$2mr~V z5DunUMBcP_wY_q>bj>#3<_ip#W274AD9Es8*@-bJRY8Ms7lUH^CY7}Rn@bhy4tF7& z%M|7*xKKy*nKxlDBjp+!kUH02oGa%$68=}&h@x#lg@N9_kbx~wrsp087Qc?@t{&n^ zx!seL(c*MY3hA7_b+kpDJ(dC&*ZwW++up7%Y;_k1-q|17*lT}HyY2|3vs~XO3n3Zq z4(=zLbMmF4XVIK}$8(-PKTo4n)2!tQpoXCmi^(@}n82kx&b7`#rTY7YA4r34&}R=w ztqN3_&(g8n`}C@nGgl|u>8676sCc0SS#jU;W*q>(3h@BCVoFP=fYnO`s9B3||Ek2_ zKD+=Zd9{Cj=kuhhFeEQqo%n*SJ>bVX7(4Oq9v1WM;@SLX!ZX`qJ|x0hww9j9wavB9 z?9+mL=QkgP(oQpDLq+#UPwklkSgYg;n2@C(1?Q@A>Yr9KgS3Fl%F2t)ANt+(cIO+( zbKQr%|4M9w9)vs8{xs-po$B>XWM!+^%~t{WA{TQ-|i{KExtT5r_J?zG3H5e>=ir0Cjp@`ijDxh%f+jU@Amf^E_Y8? zL&C-91N!nm9E`V=>_Rsj(0~&6u4_3TX@oeLc>Q?HY5BZUe>I$^PdyvV*Gi_WEsQVr zF4~NJ>I>mHlpB5mV48Hi`_Q|%S}Cu|B<~t5h`xpaiBI3k#?`J{n)rPhj5hDQx(da& zRE<$Hk+-0=io(y5BvB6UpGv`RP>u-8mSDo0MNvyI$!W^dX22@HOJFf-!cK8GhX#`y z1W+*Z9S4Zl+T~$gMY4lg^O_LsItj8G#7=5l#Pp(qMe;gS7tS_Qw<2j)5>+KQmV5kO zfz&axkzmJXaFBv`z(3p{IFurSquBq01>%rQ2JA1H0zl~;qEEz(nppf`-ivK zJ>Lux$*f~*6r4q8`Heb{nmP7-O)E7d zIB7gZ4z@D8w#g3MOV|aR{*_@pQY;ZT!f+ECXU|QPNY7WeoAG2R+)CY_jcK&mW%QUo zaOlYrs>!yBGgu#cpU}8T_`W3)9GnPVo8yW+x-5lqYI+OrdBB~11j0&>NvcPZj>nNL z>I&~ujbDemrVAWwDkGwT6(3w9E3Q5roIO!kDZ>lw;*T6BUP{;W^v%oBxaKL}_g0$Y zE~1_+NTCjGEM?nrcrr`ltzE&GG42q7D?(}KLwZffuxIo_l;U}k<;8?`cHtSF8sdW5 zb5lbJn+xTE>mR zv$;hr78W0fm^XaOgh`s3Kqi^r@jl#5<{{axnIu$90}l_{9FC;g+2b+TiM5rs*^aE` z3T|RtpA4LMt(57rthzjEK?<9AQ8>9nTY>`=lsIe!97!DuJBSN8j#^SctCtNFUg38z zuEKtGKm88k5&^r0e$G-%ydqJ{Xsfzc7q0vU9_o7SMV;K>kSb(c-gknG7BbK9@rYOM zA!4;-Bt@|@oE3g~M0qI~Tm zS6(DJPN8W9UH`L2`? zB9tWecTthp#PRYHXg*mFc5$s9^N~z5(%U4y4aC_=4l0DZ7RlfCdcfa#=Vig{r9Rz&O3;I?#jz0QbVCB{W z?Hp-BL?~^27S11iQ7m!^Vh{DOG@c!_aQM-qi${d_>NfhxIp1xPXn8SUihzO9`-O|) zilE&Bn&14mFZEH=;*(tt{-57(K8&CiK}&rq^+5EW{}Q1jymkA;ASn<1?{*^pPracD z5t_gMZ%c;TcXwqCvvB|OfKPaoA91?lwf-{>nQy?WC=)W={m%mo?%x0D`@f<6{Vn`& z*8Z<6_y3P;O$3@16|w5KHCx3$RPu3L`tNTaSH@3dG{f&sNeuZ(YhNDjfA+}TYVvDx9?Ma{OgBE7_dD>->VS(^9kB* z%m;v(xG&@U-yI9sejMe*`v30OHy_c2L`BMJes^@!8*Ub`iNj_m{QlGbC!l}K+Yw~{ z4$X%W9oW|sIV}I_|1r?N5^mVm-@^hrxXU1dMv=wiMD=g~VQE1BpFW!WJ1HWRGQcZQ zR^?&+r~kV^|4;5d=l{Lk*A>hpZ=e}0IVD(d+>X~ zWW|bvcn3x4f_GjBsmZOPVkO~Q3l=3}yonC-_yb5G(u5>us zp3~3QbM>jT9M>Y|ZHB_gJKK>h*DU8Kq>HL*sR31AG}C3m(a$#DVv%>O`5I+L#a6~$ z%xQ3O^W9=dD)FI2S{p>F+4o?z3E7NhY}0-sdv}+L(faqs`0Jt6=be=9ArL?6-aVu#CBT zon=>@ckHa^u_w6?<#)&$Dyr$jpzv5`v>Zt@r@prfLi7(3RhUky>-X`(J(%>1&g34E zzA^yNoj+elonrFrSel_A-)fw(&dTO)5;VC!ehvl!H(R9^uNOYAvYO_qQ7?S=*8RAV zf0=Nh)O?scp3Cl=9gL#ied(S_&+)c$h6HBbo3h3zW^FZMoDjdY`*GnXu;Npc%jQf&UgX0e2*sQWb4^OUv?{~Ud50iuNigy&rgi@OTp9futqn} zitEF1mW{~+z0-qX;qA*JaFgWwsY0p>v!QVH?x5(&0(Fw%w-f-QQh41W40i_rHMRFlgqQCa_8;o@~JplsTQ!QA`^{yl2<(!2YG+7+V)sj z%B5?IZ-eu8L`pYy$-!Akk-EBV{e~1`UrY$KGs85VJIv*^r)A;}o~`VQTWU@_U6|l$ zUhFnguCvb$^MGPExi6Ssy*IOI*vtpnA22hogbH8bCQt1zxWFTw^u3L%P2)LCmw(B# zM5y^+YnmrpnlAo6kbPd#wsJ*;th#RInJ>rZraD;TIlKtc$7wuBE7Gp%KUHOMw&jS0 zyP*{F-lTi<9@EMTMV;?J*s|7O5QD3W(=2d{&{#%Si@2-fb*Qi{@7l#NIijYxVreje zd#Fl2fMwk`;POmxb#|v_tO4a~KexU8IAKx>Ea&dnBAugsox@n=c z{zIb3p~Tl~JTbqI^4Cf`kS*U6mT5MW^rYNo`OBz%#}Th2HjF#- zxHF**Ik7>t(pe!2_i}ToNnuAvYBk?G#U|n6fz&VMAxF}MvrmJIsP7@QitnL3DcrbL z8DXxTN%OAaf#2I2@L$=^->qA3AE3VXew1-aAE&tRE&u!7;uVWT!4nZ=7)jd)CyU`4 zURM-(!S+=4zyi>Jg8E#MU8b2-1KG}oOUz91Hd*&XFiIL2Pv|2YD=%aD(=&t9AvO{D9+5?#h&_iOo~>3Cgt z0FH~2I-%9AOsU{U@woyzFd4x^r~q-+(Ux1PgUW;{cK#7Ts4Gk}NBq@nRTF29!L%Fo znH6+`VMkv$>Ac9)CemVKC036;HSddal0(atGMmTABDseZJc$n+FD_N+w3h32eF#}s z*qr{Aqy20DAm-V?SsZ(b*$E_n?Z@iWT(BYAXs+EPGU1VSAnk$|NyT#KuTJ*AUajp0 ziuN@abv>Y>y)<~(@PV#n5e;CnH~z6>z*^MhOOaS zq1aK#=5wfCsHrG~@6sP)@$pBU14cYIq<%1tuZvU+Pu|Tv=vcyKeS~)T19y^u=_l(S zC&7i9=h;CV4;y*Ku?Sv9SlV4g?42wrok4lBI@stG$XWGEv}rlb6`ioz%M?HKh7lBY zeNXKQBp|REUb`o36XIE7{xE$sYiSFDdSnwhhqRyvNyyLHY?RZoS0y%Q{`6Xs>P|f5 zPn%AMw>ZHIW4=aq``z6KEk#TklObJ3v`gUidL?kwJ9FQ$n*GhH{PUvjiaW5ZwUf@) z+{VO?;#gMi6wRD!_(=M2A46_!z^sK-(<&Livp=&h*&l2p_Ax*sGAjibBX`E{!$%yYBJ`wXqs7n7`y6+xBc#xNvYfu!{UE8!yHR_J5#V>*H+Q zCdERY3m6nMNQaVBO)k%9`J~1!u($HBFczaS$ZyVDk zgaTEdjY4Q#i}^k9+d8)5O-44Q?pc41RunvnikmBPK?V$BJz$V+l!%9US3O*DRy$(rmZn1F@LiSR?9PidzgQ@Dn5nOA~+t8wSRR+ zDn4M?2Ef^IwbiB`GYU4R(hXdu-F)gCJO}|7Gr870dim~qT9t)&@zwj_Ceq>d=fnlp%dHdUx$8 zq$y#0W-X=K*TtND_R%akjO+Rm9i~8qxI^we@d#FwjE;qm+r)Q}!`kx2#=41@0`bR+ z#h?l9%0?|Tm@COxy6rDx&g78TFZWW_Ijr%ZY~an}j&zAO+BJRZQfXV$Sa}`NzZ5`@ z?dMh>uarxzXL<|+CpIvXEsr)Qp_AVS;6!cZyxi~SM;iL{ru7aimVOGKZ#kU1S{VXh zJ)`36gmz&~0kp6@V;*}1)aahh6Ku*Wv&+QWL6 zezvMv8Hf#FW~JHAX(6^8)3mH6h6|E?D(@mZu9`n_>IGk1>4$8vcBF{h=D#Rb4{TUX zDDaSxws6OYjktyU5#PZ3a}2W6a||YVgGchmjHu0;*f$mptJQ%&Zmu5e9xHtg!m1k zDT)^oxNU679v^Y)G+2VqPWGOd&Wwc)Aq^%z-G0@Ibgv3ns2|BtBB2eOAlS0_1T@?Tee>94jNte=%|^-Hvb($PoJ%e$ z892iJ@}qjh*_^$u;IQ`SXO`zN%-S^D&f9Z~!$duQaL3D1Y`vBmYZZve!r?okC8Ne;IYkFUjh%Ymawr#0r#YRXyF)zu zWoefhFZz2Em!0_?*Td$gc8Y;B{@5Kcl9Hyb(8U<6vW&oQSCX=^?i=#cwaU z5BEO&GB#+eLY9bFB8Zp|wEUigtI!;Leu;ty2M}Y-Af`SES-XuZ@X9<}QZb({H5-n5 zWmoPb)1FFyGN_h$D*@Ub9&Qpm6G|Hj`=T-aNUH~Na_QctV3==Idghms;s^nE!*46%8(5FY%=Z~h$46oKM_7yohkv@;DGl@)RqMOoqn&xG0j%*LUEA(+mCC-70#9KoeGfJ9Q@AK$OrRk zC78+nd-Vun|GP-{GYXHN!Ub|KV^>jha@$U}KT4aK8tzE}9*3QcL-G1RV!iVQ%bD8X zvu^n;YHV-2)ry#c>F>78v#DA+Aw9=**dD1K`4$LmYFzcHCx&s~cV3XY3PJ(3 zaQ+dgZBRva;gy%KT98w7f9f~-eU)+JRCBF0AfcST!q{j|Q-sNS;2(iBL58fn^3nYS zUz-=l6CLAxn;FLA_tyrHI^419FZJ=t6?_Zz1d-S?JAnGk=QdU1+7LwPqT7wva`x5o zkA|JC7l8!cKWB?^0u|%fWK+DaB#m>D81`E)apXJh1+Q!tEbyWgzZPOr*Hz^=DS&Y~ zTEHa>`Bth}n)E!sbHuzu&3eNXHwD!6E39gDp)RfEG zPk-@N1o4(sdCgjyW+`WXo65h;oBnwrDcPh?a_+w;mdAd~J|$M9rLGld=Xq8&M^;mV zGiU3Ify3>qRk1+K!=7UxFmHQ$4Vj5PkT~=+f@O*B{ZKb1(Tfg8rqz(Q9MJ`f!!b%N zMjwx^NgwYl0C#I4gU_t^T_YdKj;C8Z|GkVtOn}gjgyjN5WbWy4X}~~?u(y!gVpm%+ z%ti3Ha(v*k$!LmU@*O?6N8NSnEzLT6)j^*~eCIQwT)(J1pJ{C{l)IZFmLniWfcFHR z0)QW@1RT2eIAxp*uHsmHh(gX_UTrnbZa4*ve6S_@(RMm)*+VCDdIW-BtX)SnY_;ydhcj_%+gY$xo#wE(bJs!^oI!NDn+$#%Zbf2+%l9ZkJP5 zI+_dC(nt-@OFyY=)u!FsGZ!8B4p8@&3(xbT9pkwD)Yj$4wrW8N z>jK2_V0&d+L}-2>*Rimr*@<^FYn#Wlm($)fcowrpJCZJVWz_4Am|`D9{HeXjXKbR= zM^Z1kVcEkb>vK-5C2BOw?njg@iBFjvO7s>aAwOk*A2g&Jw;!gf-l^fg zAN6}G=pzm<)`)n8nYFlZw1d1@52cnbPmvN$mSqq6Al75Y34@_36enn;qZ*&V>pI=YtkK$96i+VxM z;jD-1N;qUr)LMNjy~0+p&h=$<)pmLs3O?VHo@p>IAvJwC{i=gdUB6I+{eZw%0}*`m zO0Y#C(JM^f>s0b^QM$?JK1%LfV_;&*ajRO9zRvU9*;p-AN4ea@j;|}uUfJBPFDT35 ztl*%!Xm0RVAU-o=wh{}i--BoUL7N@}69INd?$@tQ0!S~GaQHfE-Cp&r*Fk$L)4Z=v zbJrh=fH=d1Omw?z`}h_=FL@#Yos0$GZ5E~oczIh1plY@g}oxcLZvFPodS1BA8j?rzMxC;c0J5vhJc`El4; z0gC=_s3@xfz>J_8^0mLIV4&qTaBx-VMmn_p-f1_y{l-gR9YTwG>i%tM!ViqYdU{!h z?Vs?D2nY+eHTQQY{qJDWj3N-q`QOm~4#fSxFl!=EN1@X#_Z^H!q+ya+vAy+9aG7AH z0(rj&Yo?4{ZN3<$d%ti@wi8OSrGlBV#Jcw5we&OmC_s)fUZ<>q! z>9w*h0QkH=4o!XTk}|Jc`(g3(!1(P-vLk`eK1k40#&lsbHP^W{pXjjKpY=6Tx5vaJ z8>(8BJvqe85h3aS&`Xp~G0tSoZDS?e-np*)*u9)U&%^MuAv=`CdHK7c{f^t(4}_-S z;X`<3e83)gaAsuS=KX z^c#25?s6?*+qn&0b!)rqr8TUv>jk)a&=X;Ex)SSs$A$@>9ji-?O*|C~kPl`y-!@dh zrK`2vI`X9Mb=wIbu8jW+5=Qkr(v>eG3s|eHjy4y&9og~3t*jcQy{hmCS_+dqG(AWXa%Yyt zn&0A0!Sx~Np3q+0@^I>~lb{#VY_Z@@v$XTp54`eo6x^G)h2-pVB<+0kDzulqhxCbk zl1oRzq*b<6>`un%<(RY-|7J^~7FTRY@=gRPpZy8h>TI>$()Ds1#@XljN(~In>1LC@ zfU)Z`=^(3N@b<62!_{N&DEHu7dD@*(y&4Q>A)9rie*XOi2>*ue%^qW;Dz!@0Vpd#yB6Mn zsab6b2G6rTbNh1}7e8!RvRhARBIO+lis#;!N3jkLz54*89y>R;@QlaR#cU)om>;?R ze6IR(eSVPRkAcZb2cm9QR$OHK7h~({CR+t1bqRa5OCb)}PI0f?lA;(@$Arwrb0|#C zk4N;h&e4Y32~xeON`i>2z)jo7)nhp}RuI7$bTWRy>r1~?!jlAUE1532XGVSaD0VG) zJ9?n$wwCe3M)uWa`;~8YvC)G;vu3>9Et1u?Ge!j36>18W1`nedT%a@iJrn`UbKvG^ zb$vGlwPGDp3YBy*HzFRR?@c>8Bk3wOTJY6+su6uz;0e^x1@AcAyCj9!Kkb>{6I?sf zvmkk|eMY-Y3w9f4F4nb~g)zqJ*oPo!v54^VtvsbTDP**IMtpZDZlJVdg(zeJc)mtyRn}Uh_PS{OfZZWsveLcc)y#MXk*Q07Q;g+cx*U$H#E*? zz=}9|+AdyKr?F#s;si_umDZDDAV+}itieBw^}*+@a$Z<|oysSvD6n@If&0(f4c#Ra zTN*c*MF@E;W37sR>x*ma%r{zzEzoI$6eMatZq$Bd=izlYs;piNK_ZAAHzICOHN+=HrKoC&(NuNNJYz>Ck%aMrCT3b0gi2R{^PGabP74Ve48Vs)$q zUXu_5&AYxOKew4B-N~CS+b7|@7=|Kr;&@E!oAOOzlUL4=g}N{xIJq&JS>O=acyQcM z?DS8hxaqFzm&tLxSQ5^U0=qw(luHdnhf_M$rTIUJGjDgVPWPyOe^ldonyo(A$Y)Rb zjN`cxyxDWC1ZnI%lMHeRQ&Hrb(L=F#9B-^~cwI<1I20$jo6+oJKeD96LE^ zzdRB=9Jy5f{()+gBxYmWte+QN981=%al{FIrJJ9muh}0ftOUfRMcc>uZP~l?wKp;X zS%O{-$n8Du%9iMha8{~>YkVM6LMGYd=v`XTc;PUr<^w^*Xioo@LT)AFy^Jm@alJ~6 zdA_;Vm5b7#*FEuvNsXKjw||tWtPTr%sHAmRQ!9wNm9f8*hOF~mm(IKd#q(xE9ZZS- zh(T@?Q`f_F7U9(PE~w(MO3kG>v+G`qgY~cgVKDzB8XE4Htc2)0CE?QvXigInq~szX z>#OAH%Ao^tTvwXD;<3>R;GdYk8dnmo3qg@z3=9iP&m)V#E$k+yoli1{HVBeUH(ohf zy>wr&LJ;%HrFZBi^+;rpa30-O-c#*8^b?myjhG%Agz*LrsFUUNaZ^(6IJhA!0D7#4(f*1xN zUPA=uKMB1=ye??bGJehMR@NjN0I#%|nb!-gE_DuukfVTr0A^qL;(73KHCBEKm{prK zan|Ip60w^YsVMMPE}Dhm)Wi1n*6WvltBqzc0UgeW2}ojP08lPi)t|-MlR{+4G#BO)I1ZMId+=;Pex_nL;;K7{ zP3_Emfj6GK)vBcSV5sVdWFj_#R$e35zq+bb4W=%irYg&O3vMZo#awE1&A!n2Gf zrQl5+)d9fxRn{!ci){x4phUf>+ z7)_X^E(N*?2f*3--b1cvC9LlCxoe(kc&p~adVtJ)$N54o4FlwRiV-~HZ3azYjLZ#+ zL=Rl;8zH!tc<@6T9f4H>a%!A&ZJVT+qWfpMZjK(#ve6jt#`ekcVlaGS<5}pT<7&LQ zL}TZ#uE4qTWboT6UOGcj_}J|EVskMS!x3USQmWf^_X9&qCcW-KPr}tJ*H@IsiJU^p ze6uuOHGBJXp6e5GI7eQTTX!lUC{prOW3rOe4yi^;5K}0(nUKrQ{A!|cs{0w{X#D%a zEY`1DYUuOuw-=HORv{?A#L1L=wLpw*b{s}O&^QF^oJHt1l%mTsvkWAF>?pLR?lqa zs_cevQe-EU3#r%+PYpB1-)YJAEaP}&v{;llX1yC>_l6W2cO99void5}+&-mIHAM0o z3`Tp0vNxJ9`C}jRT>AZlHrQ57U*hi*-KygS^f`-`OikyGx~X*|B!o;w*T?);;s56u zu;AMEq!4@`RIm(03x8>u(>mOBB$iqW;ye>s8y^<3Jd}{eDFpmjqtuDb$?Im_Nf<8h zd>#$KtS@_Fr53Vq=-IA#Xsy5c2FXWcU5WGr-(INTv4;8{aa<3Fb?&z%>=KZz9$4TH zmrWU#0=S^@BewFwXFn#-z2>%UXlcZmB0S~X#qk(SO=@Mb?3J6qt3fjyMkg=Mxg2E% z!>E+zmfE+;4DtK<{nOVq?8tsSW-dBEBblax5%AYJ6EhLNLJYD)c{ftL+fK)7^uc^U zB&8`Jpb$|Rog2eo3=ZYS75?xuEm@l2m#14y0q#HNFfA|^S+naJ)TL>|yKQzL4WhIO zdFHQTK3is->%eW|cC^V&H8*DC+x&d9kN)0FZqbgoKIzLYc~UQfDHxGix|rX7u^_Ug zrL}6OhQnot|9JIDFmEb86aJ~GtoXeaO605Vv{)u}C@E}-w|ar)N5z{ySF6(KLv%wB zDZSQNXB>1wE?Y?`9u3z1^N$>b>n7S{pJ_R~S?`3{Qr50)lzs5@g+U2MSr{)^Z^oGDm<`AMRQLNr4rm_H(COJkIqVW%P9{TinnU5~Bm>Yl@ zwz~BV-`wqf9ITS)>yj92@NIt2%u<+}It~W~nVpn&h}&c`M{O(&*U8$}vR{y1 zkLN(yQUYIH3}?+d@ag_3ocgdZ@!Nzz+3=Z1ZPa9YA=}^e*d*f4W}OV(xdgik3rAwsYnS_24X( z0Ndi~=I*$&z2!*xxt>=9utZ+{6!ODDhe&~wk*0mXFh~maqk1Dvx*-!;ZS9-4WZ}E+qLT-@Zkm52`96n_d*?Ch#s{d=D<;!i z55IRDG+QAd<7Rmqo5^|>f5AI(t{RBvcBoz#w)!48u85=DqL@54*TYZX3^1ST zc}T>(35$z0oYo`}evd3x*t&BhOc%SPfEA>=0XPENp2Gr{Np)K``UtcISPinH8SGjW z@<%~+>u!1TO>p=!b>P*l|X^7Tdq8p ztLGL3_q4)Mq)v&CW{9}mHT=mpCq4c|1#o-afkpau_PJ72?6t-Wy<(CPbAn)%LPAb4 zY5^4fm{z_wup^Z~kYH`Xw_g9w@Qz@8eA7@}hE8Th?Chpr_2$U%e4PWII;~4eFYD`m z<3oFTniX#MC{89_=NVcK&u$aTi4zcb+=cToV136w#9#IYLvw%g_ByB6^u{(l1)y08vH3{tmEkeuD@=_6U%M*mLj98_FV}jAH-F(|d((B)YQN z2rmBtVLd?q*Ti5oU0gL>uTxjCZHoV|^gP4^*os`1O8{dl<}EH3?L!>W{=f(VIY&$#ser`a=}HEilXA^ z%p2x^sm1)eaZ((r`xAkGGij6`MM#0(zupOc`EQT+v*ZA}@&ER3cX#d5!vC8x^A3Q& zxpFD)fBfYeM)`|4(DD&i`5%OQ^O!J3;Qik9sj&Vt47A&JK Date: Sat, 8 Aug 2026 13:21:12 -0500 Subject: [PATCH 22/24] normalize kernel path with forward slashes (#3023) --- packages/myst-execute/src/kernel.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/myst-execute/src/kernel.ts b/packages/myst-execute/src/kernel.ts index 91c0682c72..24342823cd 100644 --- a/packages/myst-execute/src/kernel.ts +++ b/packages/myst-execute/src/kernel.ts @@ -27,8 +27,13 @@ export async function createKernelConnection( vfile: VFile, log?: Logger, ): Promise { + // Jupyter Server API paths are `/`-delimited, but `path.relative` returns + // native separators (backslashes on Windows). + // With a windows path, jupyter_server silently fall back to the server root. + // Normalize to a forward-slash path so kernels start in same location on all platforms. + const sessionPath = path.relative(basePath, vfile.path).split(path.sep).join('/'); const sessionOpts = { - path: path.relative(basePath, vfile.path), + path: sessionPath, name: path.basename(vfile.path), type: 'console', kernel: { From d41a821e2244c51375241b982c103d8b9c395092 Mon Sep 17 00:00:00 2001 From: Angus Hollands Date: Sat, 8 Aug 2026 19:22:31 +0100 Subject: [PATCH 23/24] chore: add changeset --- .changeset/nine-times-dream.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/nine-times-dream.md diff --git a/.changeset/nine-times-dream.md b/.changeset/nine-times-dream.md new file mode 100644 index 0000000000..c06441e899 --- /dev/null +++ b/.changeset/nine-times-dream.md @@ -0,0 +1,5 @@ +--- +'myst-execute': patch +--- + +Fix path handling on Windows From 362bfd7320439f8fbba5599ce91264af2c54cab8 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Mon, 10 Aug 2026 20:49:47 +1000 Subject: [PATCH 24/24] chore(quantecon): bump upstream_base to 1.10.1, note ipynb cherry-pick deps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `upstream_base` was still 1.9.1 — the 2026-06-12 sync brought in v1.10.0 and v1.10.1 without bumping it. Also records a cherry-pick caveat on the myst-to-ipynb upstream candidate: the phantom-dependency declarations that upstream's isolated linker now requires live in the sync merge commit, not in either of the candidate's squash shas, so they have to be re-added on the upstream branch. Co-Authored-By: Claude Opus 5 (1M context) --- quantecon/UPSTREAM-PRS.yml | 8 ++++++++ quantecon/VERSION.yml | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/quantecon/UPSTREAM-PRS.yml b/quantecon/UPSTREAM-PRS.yml index 7b9da33e4f..5957b14071 100644 --- a/quantecon/UPSTREAM-PRS.yml +++ b/quantecon/UPSTREAM-PRS.yml @@ -48,6 +48,14 @@ upstream_candidates: review (image titles, files/ output-folder resolution, async reads) — cherry-pick it in the same upstream PR. + Cherry-pick caveat: `packages/myst-to-ipynb/package.json` needs + `myst-spec`, `myst-spec-ext`, `unified` and `@types/js-yaml` + declared. Both squash commits predate upstream's isolated-linker + switch (jupyter-book/mystmd#2987) and relied on hoisting; the + declarations were added in the 2026-08-10 sync merge, so they are + *not* carried by either sha. Add them on the upstream branch after + cherry-picking, or the package will not build upstream. + - id: book-mode-with-section-scope title: Book-style numbering (with LaTeX-style section scope and counter sharing) description: | diff --git a/quantecon/VERSION.yml b/quantecon/VERSION.yml index 0e63d59071..50988b42cc 100644 --- a/quantecon/VERSION.yml +++ b/quantecon/VERSION.yml @@ -33,7 +33,7 @@ # `qe-v` sees a self-consistent file (qe_version matches the tag). qe_version: qe-v8 -upstream_base: 1.9.1 +upstream_base: 1.10.1 merged_features: - id: 1