student life social platform
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(mails): use MJML to render pretty emails :3

+1555 -169
+4
.editorconfig
··· 11 11 12 12 [*.bash] 13 13 indent_style = tab 14 + 15 + [*.mjml] 16 + indent_style = space 17 + indent_size = 2
+1
.prettierignore
··· 5 5 packages/api/scripts/*.json 6 6 packages/api/storage/ 7 7 .venv/ 8 + *.mjml
+3 -1
.vscode/extensions.json
··· 5 5 "phoenisx.cssvar", 6 6 "prisma.prisma", 7 7 "stylelint.vscode-stylelint", 8 - "svelte.svelte-vscode" 8 + "svelte.svelte-vscode", 9 + // "mjmlio.vscode-mjml" 10 + "rbremont.vscode-handlebars-mjml" 9 11 ] 10 12 }
+46
.vscode/mjml.code-snippets
··· 1 + { 2 + // Place your churros workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and 3 + // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope 4 + // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is 5 + // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: 6 + // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. 7 + // Placeholders with the same ids are connected. 8 + // Example: 9 + // "Print to console": { 10 + // "scope": "javascript,typescript", 11 + // "prefix": "log", 12 + // "body": [ 13 + // "console.log('$1');", 14 + // "$2" 15 + // ], 16 + // "description": "Log output to console" 17 + // } 18 + "MJML template": { 19 + "scope": "mjml", 20 + "prefix": "mailtemplate", 21 + "body": [ 22 + "<mjml>", 23 + "\t<mj-head>", 24 + "\t\t<mj-title>$1</mj-title>", 25 + "\t\t<mj-include path=\"./_head.mjml\"></mj-include>", 26 + "\t</mj-head>", 27 + "\t<mj-body>", 28 + "\t\t<mj-include path=\"./_header.mjml\"></mj-include>", 29 + "", 30 + "\t\t<mj-section>", 31 + "\t\t\t<mj-column>", 32 + "\t\t\t\t<mj-text>", 33 + "\t\t\t\t\t<h1>$1</h1>", 34 + "\t\t\t\t</mj-text>", 35 + "\t\t\t\t<mj-text>", 36 + "\t\t\t\t\t$0", 37 + "\t\t\t\t</mj-text>", 38 + "\t\t\t</mj-column>", 39 + "\t\t</mj-section>", 40 + "", 41 + "\t\t<mj-include path=\"./_footer.mjml\"></mj-include>", 42 + "\t</mj-body>", 43 + "</mjml>", 44 + ], 45 + }, 46 + }
+4
CHANGELOG.md
··· 11 11 12 12 ## [Unreleased] 13 13 14 + ### Améliorations 15 + 16 + - Les mails sont jolis maintenant :) 17 + 14 18 ## [1.58.5] - 2024-05-29 15 19 16 20 ### Technique
+4 -1
packages/api/package.json
··· 38 38 "graphql-scalars": "^1.23.0", 39 39 "graphql-ws": "^5.16.0", 40 40 "graphql-yoga": "^5.3.0", 41 + "handlebars": "^4.7.8", 41 42 "helmet": "^7.1.0", 42 43 "html-to-text": "^9.0.5", 43 44 "image-type": "^5.2.0", ··· 53 54 "lodash.omit": "^4.5.0", 54 55 "lodash.range": "^3.2.0", 55 56 "lodash.uniqby": "^4.7.0", 57 + "mjml": "^4.15.3", 56 58 "multer": "^1.4.5-lts.1", 57 59 "nanoid": "^5.0.6", 58 60 "nodemailer": "^6.9.13", ··· 100 102 "@types/lodash.countby": "^4.6.9", 101 103 "@types/lodash.omit": "^4.5.9", 102 104 "@types/lodash.range": "^3.2.9", 105 + "@types/mjml": "^4", 103 106 "@types/multer": "^1.4.11", 104 107 "@types/node": "^20.12.2", 105 108 "@types/nodemailer": "^6.4.14", ··· 135 138 "build": "prisma generate && yarn patch -u @prisma/client && yarn copy-changelog && tsx ./scripts/update-id-prefix-to-typename-map.ts && yarn barrelize && tsc && tsx ./src/post-build.ts", 136 139 "copy-changelog": "cp ../../CHANGELOG.md ./static/CHANGELOG.md", 137 140 "clean": "git clean -Xdf .", 138 - "dev": "yarn barrelize && yarn run -T generate-buildinfo && NODE_ENV=development tsx watch --conditions=development --clear-screen=false ./src/index.ts", 141 + "dev": "yarn tsx ./scripts/update-mail-templates-type.ts && yarn barrelize && yarn run -T generate-buildinfo && NODE_ENV=development tsx watch --conditions=development --clear-screen=false ./src/index.ts", 139 142 "migrate-old-data": "cd scripts && yarn tsc import-old-data.ts --lib es2021,DOM --module es2022 --target es2022 --moduleResolution node --allowSyntheticDefaultImports && yarn node import-old-data.js", 140 143 "start": "node ./build/src/index.js", 141 144 "barrelize": "tsx scripts/generate-barrelsby-config.ts && echo Running barrelsby && barrelsby --config barrelsby.config.json && tsx scripts/fix-extensionless-barrel-imports.ts && echo Formatting barrels for consistency && yarn format-at ./src/modules/**/index.ts --log-level silent",
+23
packages/api/scripts/update-mail-templates-type.ts
··· 1 + import { readdirSync, readFileSync, writeFileSync } from 'fs'; 2 + import path from 'path'; 3 + import { isMJMLTemplateFilename, mailTemplatesDirectory } from '../src/mail-templates/props.js'; 4 + 5 + function getMailTemplates(): string[] { 6 + return readdirSync(mailTemplatesDirectory) 7 + .filter(isMJMLTemplateFilename) 8 + .map((file) => path.basename(file, '.mjml')); 9 + } 10 + 11 + const targetFilepath = path.join(mailTemplatesDirectory, 'props.ts'); 12 + 13 + const content = readFileSync(targetFilepath, 'utf8') 14 + .split('\n') 15 + .map((line) => 16 + line.startsWith('export type MailTemplate = ') 17 + ? `export type MailTemplate = /* @generated */ ${getMailTemplates() 18 + .map((file) => `"${file}"`) 19 + .join(' | ')};` 20 + : line, 21 + ); 22 + 23 + writeFileSync(targetFilepath, content.join('\n'));
+1
packages/api/src/lib/index.ts
··· 15 15 export * from './ldap-school.js'; 16 16 export * from './ldap.js'; 17 17 export * from './logger.js'; 18 + export * from './mail.js'; 18 19 export * from './markdown.js'; 19 20 export * from './pictures.js'; 20 21 export * from './prisma.js';
+149
packages/api/src/lib/mail.ts
··· 1 + import Handlebars from 'handlebars'; 2 + import { htmlToText } from 'html-to-text'; 3 + import mjml2html from 'mjml'; 4 + import type { MJMLJsonObject, MJMLParseError } from 'mjml-core'; 5 + import { readFile, readdir } from 'node:fs/promises'; 6 + import path from 'node:path'; 7 + import { createTransport } from 'nodemailer'; 8 + import type Mail from 'nodemailer/lib/mailer/index.js'; 9 + import { 10 + isMJMLTemplateFilename, 11 + mailTemplatesDirectory, 12 + type MailProps, 13 + type MailRequiredContentIDs, 14 + type MailTemplate, 15 + } from '../mail-templates/props.js'; 16 + 17 + const compiledTemplates = await precompileTemplates(); 18 + const mailer = createTransport(process.env.SMTP_URL); 19 + 20 + /** 21 + * Maps attachments to their CIDs 22 + */ 23 + type AttachmentsMap<K extends string | number | symbol> = { 24 + [cid in K]: Omit<Mail.Attachment, 'cid'>; 25 + }; 26 + 27 + /** 28 + * 29 + * @param templateName the template name. See src/mail-templates/ 30 + * @param to Who to send the mail to 31 + * @param data The data to give. Should be pretty explicit from the type 32 + * @param options.from The sender of the mail 33 + * @param options.subjectOverride Override the subject of the mail. By default, the subject is the email's <title> 34 + * @param options.attachments Attachments to add to the mail. Typing should help you to know what attachments are required 35 + */ 36 + export async function sendMail<Template extends MailTemplate>( 37 + templateName: Template, 38 + to: string | string[], 39 + data: Template extends keyof MailProps ? MailProps[Template] : Record<string, never>, 40 + { 41 + from = process.env.PUBLIC_SUPPORT_EMAIL, 42 + attachments = {}, 43 + subjectOverride = '', 44 + }: { 45 + from?: string; 46 + subjectOverride?: string; 47 + } & (Template extends keyof MailRequiredContentIDs 48 + ? { 49 + attachments: AttachmentsMap<MailRequiredContentIDs[Template][number]> & 50 + AttachmentsMap<string>; 51 + } 52 + : { 53 + // when no attachments are required, only additional attachments, or no attachments at all 54 + attachments?: AttachmentsMap<string>; 55 + }), 56 + ) { 57 + const template = compiledTemplates.get(templateName); 58 + if (!template) throw new Error(`Template "${template}" not found`); 59 + 60 + const content = template.html({ to, ...data, env: process.env }); 61 + const subject = subjectOverride || template.subject({ to, ...data }); 62 + 63 + await mailer.sendMail({ 64 + to, 65 + from, 66 + subject, 67 + html: content, 68 + text: htmlToText(content), 69 + attachments: Object.entries(attachments).map(([cid, data]) => ({ 70 + cid, 71 + ...data, 72 + })), 73 + }); 74 + } 75 + 76 + type PrecompiledTemplate = { 77 + html: HandlebarsTemplateDelegate<unknown>; 78 + subject: HandlebarsTemplateDelegate<unknown>; 79 + }; 80 + 81 + async function precompileTemplates(): Promise<Map<MailTemplate, PrecompiledTemplate>> { 82 + const compiledTemplates = new Map<MailTemplate, PrecompiledTemplate>(); 83 + const compilationErrors = new Map<string, string[]>(); 84 + for (const templateFilename of await readdir(mailTemplatesDirectory)) { 85 + if (!isMJMLTemplateFilename(templateFilename)) continue; 86 + try { 87 + const result = await compileTemplate(path.join(mailTemplatesDirectory, templateFilename)); 88 + 89 + if ('errors' in result) { 90 + compilationErrors.set( 91 + templateFilename, 92 + result.errors.map((e) => e.formattedMessage), 93 + ); 94 + } else { 95 + compiledTemplates.set(path.basename(templateFilename, '.mjml') as MailTemplate, result); 96 + } 97 + } catch (error) { 98 + compilationErrors.set(templateFilename, [error?.toString() ?? '']); 99 + } 100 + } 101 + if (compilationErrors.size > 0) { 102 + console.error( 103 + 'Error compiling templates:\n' + 104 + [...compilationErrors.entries()] 105 + .map(([filename, errors]) => `${filename}:\n${errors.join('\n')}`) 106 + .join('\n'), 107 + ); 108 + } 109 + console.info( 110 + `Successfully compiled ${compiledTemplates.size} mail templates: ${[...compiledTemplates.keys()].join(', ')}`, 111 + ); 112 + return compiledTemplates; 113 + } 114 + 115 + /** 116 + * Rust FTW 117 + */ 118 + type Result<T, E> = T | { errors: E[] }; 119 + 120 + async function compileTemplate( 121 + filepath: string, 122 + ): Promise<Result<PrecompiledTemplate, MJMLParseError>> { 123 + const content = await readFile(filepath, 'utf8'); 124 + if (!content.trim()) { 125 + console.warn(`${path.basename(filepath)} is an empty MJML template`); 126 + return { 127 + html: Handlebars.compile(''), 128 + subject: Handlebars.compile(''), 129 + }; 130 + } 131 + const rendered = mjml2html(content, { filePath: filepath }); 132 + if (rendered.errors.length > 0) return { errors: rendered.errors }; 133 + 134 + return { 135 + html: Handlebars.compile(rendered.html), 136 + subject: Handlebars.compile(extractMjTitle(rendered.json)), 137 + }; 138 + } 139 + 140 + function extractMjTitle(json: MJMLJsonObject): string { 141 + if ('children' in json) { 142 + const head = json.children.find((c) => c.tagName === 'mj-head'); 143 + if (head && 'children' in head) { 144 + const title = head.children.find((c) => c.tagName === 'mj-title'); 145 + if (title && 'content' in title) return title.content; 146 + } 147 + } 148 + throw new Error('No title found in MJML template'); 149 + }
+5
packages/api/src/mail-templates/README.md
··· 1 + # Templates mails 2 + 3 + Utilise [MJML](https://mjml.io/) pour faire des jolis mails. 4 + 5 + Ils ont un playground pratique ici: <https://mjml.io/try-it-live>
+6
packages/api/src/mail-templates/_base.sample.json
··· 1 + { 2 + "env": { 3 + "FRONTEND_ORIGIN": "http://localhost:5173", 4 + "PUBLIC_SUPPORT_EMAIL": "support@localhost" 5 + } 6 + }
+13
packages/api/src/mail-templates/_footer.mjml
··· 1 + <mj-section> 2 + <mj-column width="100%"> 3 + <mj-divider /> 4 + <mj-text align="center"> 5 + <a href="{{env.FRONTEND_ORIGIN}}">Churros</a> par <a href="https://net7.dev">net7</a> 6 + </mj-text> 7 + <mj-text align="center"> 8 + Contactez-nous en répondant à ce mail ou en écrivant à <a href="mailto:{{env.PUBLIC_SUPPORT_EMAIL}}">{{env.PUBLIC_SUPPORT_EMAIL}}</a> 9 + </mj-text> 10 + <mj-spacer height="16px" /> 11 + <mj-image height="32px" src="https://net7.dev/images/net7_dark.svg" alt="net7" /> 12 + </mj-column> 13 + </mj-section>
+5
packages/api/src/mail-templates/_head.mjml
··· 1 + <mj-font name="Space Grotesk" href="{{env.FRONTEND_ORIGIN}}/fonts.css" /> 2 + <mj-attributes> 3 + <mj-all font-family="Space Grotesk, sans-serif" /> 4 + <mj-button background-color="#1d4ed8" color="#ffffff" font-weight="bold" border-radius="24px" /> 5 + </mj-attributes>
+5
packages/api/src/mail-templates/_header.mjml
··· 1 + <mj-section> 2 + <mj-column> 3 + <mj-image height="64px" src="{{env.FRONTEND_ORIGIN}}/wordmark.svg" alt="Churros" /> 4 + </mj-column> 5 + </mj-section>
+49
packages/api/src/mail-templates/booking.mjml
··· 1 + <mjml> 2 + <mj-head> 3 + <mj-title> 4 + {{#if beneficiary}} 5 + Place pour {{beneficiary}} à {{eventTitle}} 6 + {{else}} 7 + Ta place pour {{eventTitle}} 8 + {{/if}} 9 + </mj-title> 10 + <mj-include path="./_head.mjml" /> 11 + </mj-head> 12 + <mj-body> 13 + <mj-include path="./_header.mjml" /> 14 + 15 + <mj-section> 16 + <mj-column> 17 + <mj-text> 18 + Ta place pour {{eventTitle}} a bien été réservée. 19 + </mj-text> 20 + <mj-button href="{{bookingLink}}"> 21 + Accéder à ma place 22 + </mj-button> 23 + </mj-text> 24 + </mj-column> 25 + </mj-section> 26 + 27 + <mj-section> 28 + <mj-column width="33%"> 29 + <mj-text>Montre le QR code pour rentrer.</mj-text> 30 + </mj-column> 31 + <mj-column width="66%"> 32 + <mj-image src="cid:qrcode" alt="{{bookingCode}}" /> 33 + </mj-column> 34 + </mj-section> 35 + 36 + <mj-section> 37 + <mj-column> 38 + <mj-text> 39 + En cas de problème, ton code de réservation est le: 40 + </mj-text> 41 + <mj-text font-size="24px" align="center"> 42 + <code>{{bookingCode}}</code> 43 + </mj-text> 44 + </mj-column> 45 + </mj-section> 46 + 47 + <mj-include path="./_footer.mjml" /> 48 + </mj-body> 49 + </mjml>
+6
packages/api/src/mail-templates/booking.sample.json
··· 1 + { 2 + "bookingCode": "5MQZTPQ7GXU5TVVU", 3 + "bookingLink": "http://localhost:5173/bookings/5MQZTPQ7GXU5TVVU", 4 + "eventTitle": "odit est eveniet", 5 + "beneficiary": "" 6 + }
+45
packages/api/src/mail-templates/form-answers.mjml
··· 1 + <mjml> 2 + <mj-head> 3 + <mj-title>Tes réponses à {{title}}</mj-title> 4 + <mj-include path="./_head.mjml"></mj-include> 5 + </mj-head> 6 + <mj-body> 7 + <mj-include path="./_header.mjml"></mj-include> 8 + 9 + <mj-section> 10 + <mj-column> 11 + <mj-text> 12 + <h1>Tes réponses à {{title}}</h1> 13 + </mj-text> 14 + <mj-text> 15 + <p>Réponses du {{ answersDate }}</p> 16 + <ul> 17 + {{#each answers}} 18 + <li> 19 + <strong>{{ this.questionTitle }}</strong>: {{ this.answer }} 20 + </li> 21 + {{/each}} 22 + </ul> 23 + </mj-text> 24 + </mj-column> 25 + </mj-section> 26 + 27 + <mj-section> 28 + <mj-column> 29 + <mj-button href="{{linkToAnswers}}"> 30 + Modifier mes réponses 31 + </mj-button> 32 + </mj-column> 33 + </mj-section> 34 + 35 + <mj-section> 36 + <mj-column> 37 + <mj-text> 38 + <pre>{{formId}}</pre> 39 + </mj-text> 40 + </mj-column> 41 + </mj-section> 42 + 43 + <mj-include path="./_footer.mjml"></mj-include> 44 + </mj-body> 45 + </mjml>
+38
packages/api/src/mail-templates/group-board-updated.mjml
··· 1 + <mjml> 2 + <mj-head> 3 + <mj-title>Bureau de {{groupName}} modifié</mj-title> 4 + <mj-include path="./_head.mjml"></mj-include> 5 + </mj-head> 6 + <mj-body> 7 + <mj-include path="./_header.mjml"></mj-include> 8 + 9 + <mj-section> 10 + <mj-column> 11 + <mj-text> 12 + <h1>Bureau de {{groupName}} modifié</h1> 13 + </mj-text> 14 + <mj-text> 15 + <a href="{{memberProfileLink}}">{{memberFullname}}</a> a maintenant les rôles suivants: 16 + </mj-text> 17 + </mj-column> 18 + </mj-section> 19 + 20 + <mj-section> 21 + <mj-column> 22 + <mj-text align="center"> 23 + <p>{{rolesText}}</p> 24 + </mj-text> 25 + </mj-column> 26 + </mj-section> 27 + 28 + <mj-section> 29 + <mj-column> 30 + <mj-text> 31 + <p>(avant, iel avait les rôles {{rolesText}})</p> 32 + </mj-text> 33 + </mj-column> 34 + </mj-section> 35 + 36 + <mj-include path="./_footer.mjml"></mj-include> 37 + </mj-body> 38 + </mjml>
+68
packages/api/src/mail-templates/props.ts
··· 1 + import path from 'node:path'; 2 + 3 + /** 4 + * Maps mail template names to an object type that represents the data the mail template takes 5 + * If the template's name is not here, it means it doesn't take any data. 6 + */ 7 + export type MailProps = { 8 + 'booking': { 9 + bookingCode: string; 10 + eventTitle: string; 11 + bookingLink: string; 12 + beneficiary: string | null; 13 + }; 14 + 'form-answers': { 15 + title: string; 16 + answersDate: string; 17 + answers: Array<{ 18 + questionTitle: string; 19 + answerString: string; 20 + }>; 21 + linkToAnswers: string; 22 + formId: string; 23 + }; 24 + 'group-board-updated': { 25 + groupName: string; 26 + memberFullName: string; 27 + rolesText: string; 28 + }; 29 + 'reset-password': { 30 + resetLink: string; 31 + }; 32 + 'signup-accepted': { 33 + fullName: string; 34 + }; 35 + 'signup-done': { 36 + fullName: string; 37 + }; 38 + 'signup-rejected': { 39 + reason: string; 40 + }; 41 + 'signup-verify-mail': { 42 + fullName: string; 43 + url: string; 44 + }; 45 + 'verify-mail': { 46 + url: string; 47 + }; 48 + 'welcome': { 49 + url: string; 50 + }; 51 + }; 52 + 53 + /** 54 + * Maps template names to CIDs that are required to be included in the mail for the template to work 55 + */ 56 + export type MailRequiredContentIDs = { 57 + booking: ['qrcode']; 58 + }; 59 + 60 + // prettier-ignore 61 + export type MailTemplate = /* @generated */ "booking" | "form-answers" | "group-board-updated" | "reset-password" | "signup-accepted" | "signup-done" | "signup-rejected" | "signup-verify-mail" | "verify-mail" | "welcome"; 62 + 63 + export const mailTemplatesDirectory = path.dirname(new URL(import.meta.url).pathname); 64 + 65 + export function isMJMLTemplateFilename(filepath: string): boolean { 66 + const filename = path.basename(filepath); 67 + return filename.endsWith('.mjml') && !filename.startsWith('_'); 68 + }
+27
packages/api/src/mail-templates/reset-password.mjml
··· 1 + <mjml> 2 + <mj-head> 3 + <mj-title>Réinitialisation de ton mot de passe</mj-title> 4 + <mj-include path="./_head.mjml"></mj-include> 5 + </mj-head> 6 + <mj-body> 7 + <mj-include path="./_header.mjml"></mj-include> 8 + 9 + <mj-section> 10 + <mj-column> 11 + <mj-text> 12 + <h1>Réinitialisation de ton mot de passe</h1> 13 + </mj-text> 14 + </mj-column> 15 + </mj-section> 16 + 17 + <mj-section> 18 + <mj-column> 19 + <mj-button href="{{resetLink}}"> 20 + Réinitialiser mon mot de passe 21 + </mj-button> 22 + </mj-column> 23 + </mj-section> 24 + 25 + <mj-include path="./_footer.mjml"></mj-include> 26 + </mj-body> 27 + </mjml>
packages/api/src/mail-templates/signup-accepted.mjml

This is a binary file and will not be displayed.

packages/api/src/mail-templates/signup-done.mjml

This is a binary file and will not be displayed.

+38
packages/api/src/mail-templates/signup-rejected.mjml
··· 1 + <mjml> 2 + <mj-head> 3 + <mj-title>Inscription refusée</mj-title> 4 + <mj-include path="./_head.mjml"></mj-include> 5 + </mj-head> 6 + <mj-body> 7 + <mj-include path="./_header.mjml"></mj-include> 8 + 9 + <mj-section> 10 + <mj-column> 11 + <mj-text> 12 + <h1>Inscription refusée</h1> 13 + </mj-text> 14 + <mj-text> 15 + Votre inscription a été refusée pour la raison suivante: 16 + </mj-text> 17 + </mj-column> 18 + </mj-section> 19 + 20 + <mj-section> 21 + <mj-column> 22 + <mj-text align="center"> 23 + {{reason}} 24 + </mj-text> 25 + </mj-column> 26 + </mj-section> 27 + 28 + <mj-section> 29 + <mj-column> 30 + <mj-text> 31 + Si vous pensez qu'il s'agit d'une erreur, répondez à ce mail 32 + </mj-text> 33 + </mj-column> 34 + </mj-section> 35 + 36 + <mj-include path="./_footer.mjml"></mj-include> 37 + </mj-body> 38 + </mjml>
+23
packages/api/src/mail-templates/signup-verify-mail.mjml
··· 1 + <mjml> 2 + <mj-head> 3 + <mj-title>Finaliser mon inscription sur Churros</mj-title> 4 + <mj-include path="./_head.mjml" /> 5 + </mj-head> 6 + <mj-body> 7 + <mj-include path="./_header.mjml" /> 8 + 9 + <mj-section> 10 + <mj-column width="100%"> 11 + <mj-text align="center"> 12 + <h1>Yo!</h1> 13 + </mj-text> 14 + <mj-button href="{{url}}"> 15 + Finaliser mon inscription 16 + </mj-button> 17 + </mj-text> 18 + </mj-column> 19 + </mj-section> 20 + 21 + <mj-include path="./_footer.mjml" /> 22 + </mj-body> 23 + </mjml>
+27
packages/api/src/mail-templates/verify-mail.mjml
··· 1 + <mjml> 2 + <mj-head> 3 + <mj-title>Validation de votre adresse e-mail</mj-title> 4 + <mj-include path="./_head.mjml"></mj-include> 5 + </mj-head> 6 + <mj-body> 7 + <mj-include path="./_header.mjml"></mj-include> 8 + 9 + <mj-section> 10 + <mj-column> 11 + <mj-text> 12 + <h1>Validation de votre adresse e-mail</h1> 13 + </mj-text> 14 + </mj-column> 15 + </mj-section> 16 + 17 + <mj-section> 18 + <mj-column> 19 + <mj-button href="{{url}}"> 20 + Valide ton adresse e-mail 21 + </mj-button> 22 + </mj-column> 23 + </mj-section> 24 + 25 + <mj-include path="./_footer.mjml"></mj-include> 26 + </mj-body> 27 + </mjml>
+27
packages/api/src/mail-templates/welcome.mjml
··· 1 + <mjml> 2 + <mj-head> 3 + <mj-title>Bienvenue sur Churros!</mj-title> 4 + <mj-include path="./_head.mjml"></mj-include> 5 + </mj-head> 6 + <mj-body> 7 + <mj-include path="./_header.mjml"></mj-include> 8 + 9 + <mj-section> 10 + <mj-column> 11 + <mj-text> 12 + <h1>Bienvenue sur Churros!</h1> 13 + </mj-text> 14 + </mj-column> 15 + </mj-section> 16 + 17 + <mj-section> 18 + <mj-column> 19 + <mj-button href="{{url}}"> 20 + Ça se passe ici 21 + </mj-button> 22 + </mj-column> 23 + </mj-section> 24 + 25 + <mj-include path="./_footer.mjml"></mj-include> 26 + </mj-body> 27 + </mjml>
+19 -43
packages/api/src/modules/forms/resolvers/mutation.mail-form-answers.ts
··· 1 - import { builder, formatDateTime, prisma, splitID } from '#lib'; 1 + import { builder, formatDateTime, prisma, sendMail, splitID } from '#lib'; 2 2 import { GraphQLError } from 'graphql'; 3 - import { createTransport } from 'nodemailer'; 4 3 import { answerToString } from '../utils/answers.js'; 5 4 6 5 builder.mutationField('mailFormAnswers', (t) => ··· 40 39 }, 41 40 }); 42 41 43 - const mailer = createTransport(process.env.SMTP_URL); 44 - await mailer.sendMail({ 45 - to: user.email, 46 - from: process.env.PUBLIC_SUPPORT_EMAIL, 47 - subject: `Tes réponses à ${form.title}`, 48 - html: ` 49 - <h1>Tes réponses à ${form.title}</h1> 50 - 51 - <p>Réponses du ${formatDateTime(new Date())} 52 - 53 - <ul>${answers 54 - .map( 55 - (a) => ` 56 - <li><strong>${a.question.title}</strong>: ${answerToString(a, user)}</li> 57 - `, 58 - ) 59 - .join('')} 60 - </ul> 61 - 62 - <a href="${new URL(`/forms/${splitID(form.id)[1]}/answer`, process.env.FRONTEND_ORIGIN)}">Modifier mes réponses</a> 63 - 64 - <pre>${form.id}</pre> 65 - `, 66 - text: ` 67 - Tes réponses à ${form.title} 68 - 69 - Réponses du ${formatDateTime(new Date())} 70 - 71 - ${answers 72 - .map( 73 - (a) => ` 74 - ${a.question.title}: ${answerToString(a, user)} 75 - `, 76 - ) 77 - .join('')} 78 - 79 - Modifier mes réponses: ${new URL(`/forms/${splitID(form.id)[1]}/answer`, process.env.FRONTEND_ORIGIN)} 80 - 81 - ${form.id} 82 - `, 83 - }); 42 + await sendMail( 43 + 'form-answers', 44 + user.email, 45 + { 46 + answers: answers.map((a) => ({ 47 + answerString: answerToString(a, user), 48 + questionTitle: a.question.title, 49 + })), 50 + answersDate: formatDateTime(new Date()), 51 + title: form.title, 52 + formId: form.id, 53 + linkToAnswers: new URL( 54 + `/forms/${splitID(form.id)[1]}/answer`, 55 + process.env.FRONTEND_ORIGIN, 56 + ).toString(), 57 + }, 58 + {}, 59 + ); 84 60 85 61 return user.email; 86 62 },
+21 -22
packages/api/src/modules/groups/resolvers/mutation.upsert-group-member.ts
··· 1 - import { builder, objectValuesFlat, prisma, purgeUserSessions } from '#lib'; 2 - 1 + import { builder, objectValuesFlat, prisma, purgeUserSessions, sendMail } from '#lib'; 3 2 import { updateMemberBoardLists } from '#modules/mails'; 3 + import { fullName } from '#modules/users'; 4 4 import { onBoard, userIsAdminOf, userIsGroupEditorOf } from '#permissions'; 5 - import { createTransport } from 'nodemailer'; 6 5 import { GroupMemberType } from '../index.js'; 7 6 8 7 // TODO centralize the mailer object in #lib instead of creating it here ··· 59 58 }, 60 59 { user: me }, 61 60 ) { 62 - const group = await prisma.group.findUniqueOrThrow({ where: { id: groupId } }); 61 + const group = await prisma.group.findUniqueOrThrow({ 62 + where: { id: groupId }, 63 + include: { 64 + studentAssociation: true, 65 + }, 66 + }); 63 67 const { uid } = await prisma.user.findUniqueOrThrow({ 64 68 where: { id: memberId }, 65 69 select: { uid: true }, ··· 143 147 boardKeys.some((k) => groupMember[k] !== oldMember[k]) && 144 148 group.type === 'Club' 145 149 ) { 146 - // TODO send notification too 147 - const school = await prisma.school.findUnique({ 148 - where: { id: group.schoolId ?? undefined }, 149 - }); 150 - const mailer = createTransport(process.env.SMTP_URL); 151 - await mailer.sendMail({ 152 - from: process.env.PUBLIC_CONTACT_EMAIL, 153 - to: `respos-clubs@bde.${school?.name.toLowerCase()}.fr`, 154 - subject: `Bureau de ${group.name} modifié`, 155 - text: `${groupMember.member.firstName} ${groupMember.member.lastName} (@${ 156 - groupMember.member.uid 157 - }) a maintenant les rôles ${rolesText(groupMember)} (avant: ${rolesText(oldMember)})`, 158 - html: `<a href="${process.env.FRONTEND_ORIGIN}/@${groupMember.member.uid}">${ 159 - groupMember.member.firstName 160 - } ${groupMember.member.lastName} (@${ 161 - groupMember.member.uid 162 - })</a> a maintenant les rôles ${rolesText(groupMember)} (avant: ${rolesText(oldMember)})`, 163 - }); 150 + // TODO store in DB 151 + const resposClubMail = `respos-clubs@${group.studentAssociation?.allBoardMailingList.split('@')[1]}`; 152 + 153 + await sendMail( 154 + 'group-board-updated', 155 + resposClubMail, 156 + { 157 + groupName: group.name, 158 + memberFullName: fullName(groupMember.member), 159 + rolesText: rolesText(groupMember), 160 + }, 161 + {}, 162 + ); 164 163 } 165 164 166 165 return groupMember;
+1 -2
packages/api/src/modules/health-checks/resolvers/query.healthcheck.ts
··· 10 10 import ldap from 'ldapjs'; 11 11 import { createTransport } from 'nodemailer'; 12 12 import { HealthCheck } from '../index.js'; 13 + 13 14 // TODO maybe rename to query.check-health ? 14 - // TODO centralize the mailer object in #lib 15 - 16 15 builder.queryField('healthcheck', (t) => 17 16 t.field({ 18 17 type: HealthCheck,
+18 -22
packages/api/src/modules/ticketing/resolvers/mutation.upsert-registration.ts
··· 1 - import { builder, log, prisma, publish } from '#lib'; 1 + import { builder, log, prisma, publish, sendMail } from '#lib'; 2 2 3 3 import { PaymentMethodEnum } from '#modules/payments'; 4 4 import { ··· 10 10 import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library.js'; 11 11 import { isFuture, isPast } from 'date-fns'; 12 12 import { GraphQLError } from 'graphql'; 13 - import { createTransport } from 'nodemailer'; 14 13 import * as qrcode from 'qrcode'; 15 14 import { RegistrationType, placesLeft } from '../index.js'; 16 15 // TODO rename to book.ts ··· 280 279 281 280 const recipient = user?.email ?? authorEmail; 282 281 if (!recipient) throw new GraphQLError('No recipient found to send email to.'); 283 - const mailer = createTransport(process.env.SMTP_URL); 284 - await mailer.sendMail({ 285 - to: recipient, 286 - from: process.env.PUBLIC_SUPPORT_EMAIL, 287 - attachments: [ 288 - { 289 - filename: `qrcode-${pseudoID.toLowerCase()}.png`, 290 - content: qrcodeBuffer, 291 - cid: 'qrcode', 282 + await sendMail( 283 + 'booking', 284 + recipient, 285 + { 286 + beneficiary: beneficiary ?? null, 287 + bookingCode: pseudoID, 288 + eventTitle: ticket.event.title, 289 + bookingLink: new URL(`/bookings/${pseudoID}`, process.env.FRONTEND_ORIGIN).toString(), 290 + }, 291 + { 292 + attachments: { 293 + qrcode: { 294 + filename: `qrcode-${pseudoID.toLowerCase()}.png`, 295 + content: qrcodeBuffer, 296 + }, 292 297 }, 293 - ], 294 - subject: beneficiary 295 - ? `Place pour ${beneficiary} à ${ticket.event.title}` 296 - : `Ta place pour ${ticket.event.title}`, 297 - html: `<p>Ta place pour ${ticket.event.title} a bien été réservée.</p> 298 - <p>Montre le QR code pour rentrer.</p> 299 - <img src="cid:qrcode" alt="${pseudoID}" /> 300 - <p>En cas de problème, ton code de réservation est le:</p> 301 - <p style="font-size: 32px; text-align: center;" align="center" size="32px"><code>${pseudoID}</code></p><p><a href="https://churros.inpt.fr/bookings/${pseudoID}">Accéder à ma place</a></p>`, 302 - text: `Ton code de réservation est le ${pseudoID}`, 303 - }); 298 + }, 299 + ); 304 300 } 305 301 306 302 return registration;
+10 -16
packages/api/src/modules/users/resolvers/mutation.create-password-reset.ts
··· 1 - import { builder, prisma } from '#lib'; 2 - 1 + import { builder, prisma, sendMail } from '#lib'; 3 2 import { addSeconds } from 'date-fns'; 4 - import { createTransport } from 'nodemailer'; 3 + import { PASSWORD_RESET_EXPIRES_AFTER } from '../utils/password-resets.js'; 5 4 6 - import { PASSWORD_RESET_EXPIRES_AFTER } from '../utils/password-resets.js'; 7 5 // TODO rename to request-password-reset 8 - 9 6 builder.mutationField('createPasswordReset', (t) => 10 7 t.field({ 11 8 type: 'Boolean', ··· 26 23 process.env.FRONTEND_ORIGIN, 27 24 ); 28 25 29 - const transporter = createTransport(process.env.SMTP_URL); 30 - 31 - await transporter.sendMail({ 32 - to: email, 33 - from: process.env.PUBLIC_SUPPORT_EMAIL, 34 - subject: 'Réinitialisation de votre mot de passe', 35 - html: `<p> 36 - <a href="${url.toString()}">Réinitialiser mon mot de passe</a> 37 - </p>`, 38 - text: `Réinitialiser mon mot de passe sur ${url.toString()}`, 39 - }); 26 + await sendMail( 27 + 'reset-password', 28 + email, 29 + { 30 + resetLink: url.toString(), 31 + }, 32 + {}, 33 + ); 40 34 41 35 await prisma.logEntry.create({ 42 36 data: {
+3 -12
packages/api/src/modules/users/resolvers/mutation.refuse-registration.ts
··· 1 - import { builder, prisma } from '#lib'; 1 + import { builder, prisma, sendMail } from '#lib'; 2 2 import { GraphQLError } from 'graphql'; 3 - 4 - import { createTransport } from 'nodemailer'; 5 3 import { prismaUserFilterForStudentAssociationAdmins } from '../utils/index.js'; 6 4 7 5 // TODO rename registration to reject-user-candidate 8 - 9 6 builder.mutationField('refuseRegistration', (t) => 10 7 t.field({ 11 8 authScopes: { studentAssociationAdmin: true }, ··· 19 16 }); 20 17 if (!candidate) throw new GraphQLError('Candidat·e introuvable'); 21 18 22 - const mailer = createTransport(process.env.SMTP_URL); 23 - await mailer.sendMail({ 24 - to: email, 25 - from: process.env.PUBLIC_SUPPORT_EMAIL, 26 - subject: 'Inscription refusée', 27 - text: `Votre inscription a été refusée pour la raison suivante:\n\n ${reason}\n\n Si vous pensez qu'il s'agit d'une erreur, répondez à ce mail.`, 28 - html: `<p>Votre inscription a été refusée pour la raison suivante:<br><br> ${reason}<br><br> Si vous pensez qu'il s'agit d'une erreur, répondez à ce mail</p>`, 29 - }); 19 + await sendMail('signup-rejected', email, { reason }, {}); 30 20 candidate = await prisma.userCandidate.delete({ where: { email } }); 21 + 31 22 await prisma.logEntry.create({ 32 23 data: { 33 24 action: 'refuse',
+2 -13
packages/api/src/modules/users/resolvers/mutation.request-email-change.ts
··· 1 - import { builder, prisma } from '#lib'; 2 - 1 + import { builder, prisma, sendMail } from '#lib'; 3 2 import { GraphQLError } from 'graphql'; 4 - import { createTransport } from 'nodemailer'; 5 3 6 4 export async function requestEmailChange(email: string, userId: string): Promise<void> { 7 - const transporter = createTransport(process.env.SMTP_URL); 8 5 const request = await prisma.emailChange.create({ 9 6 data: { 10 7 user: { connect: { id: userId } }, ··· 17 14 process.env.FRONTEND_ORIGIN, 18 15 ); 19 16 20 - await transporter.sendMail({ 21 - from: process.env.PUBLIC_CONTACT_EMAIL, 22 - to: email, 23 - subject: `Validation de votre adresse e-mail`, 24 - html: ` 25 - <p><a href="${url.toString()}">Validez votre adresse e-mail</a></p> 26 - `, 27 - text: `Validez votre adresse e-mail sur ${url.toString()}`, 28 - }); 17 + await sendMail('verify-mail', email, { url: url.toString() }, {}); 29 18 } 30 19 31 20 builder.mutationField('requestEmailChange', (t) =>
+14 -13
packages/api/src/modules/users/resolvers/mutation.start-registration.ts
··· 1 - import { builder, findSchoolUser, fromYearTier, prisma } from '#lib'; 1 + import { builder, findSchoolUser, fromYearTier, prisma, sendMail } from '#lib'; 2 2 3 3 import { nanoid } from 'nanoid'; 4 - import { createTransport } from 'nodemailer'; 5 4 import { ZodError } from 'zod'; 5 + import { fullName } from '../utils/names.js'; 6 6 /** Registers a new user. */ 7 7 builder.mutationField('startRegistration', (t) => 8 8 t.field({ ··· 66 66 const url = new URL('/register/continue', process.env.FRONTEND_ORIGIN); 67 67 url.searchParams.append('token', token); 68 68 69 - await createTransport(process.env.SMTP_URL).sendMail({ 70 - to: email, 71 - from: process.env.PUBLIC_SUPPORT_EMAIL, 72 - subject: `Finaliser mon inscription sur Churros`, 73 - html: ` 74 - <p> 75 - <a href="${url.toString()}">Finaliser mon inscription</a> 76 - </p> 77 - `, 78 - text: `Finaliser mon inscription sur ${url.toString()}`, 79 - }); 69 + await sendMail( 70 + 'signup-verify-mail', 71 + email, 72 + { 73 + fullName: fullName({ 74 + firstName: schoolUser?.firstName ?? '', 75 + lastName: schoolUser?.lastName ?? '', 76 + }).trim(), 77 + url: url.toString(), 78 + }, 79 + {}, 80 + ); 80 81 81 82 return true; 82 83 };
+7 -14
packages/api/src/modules/users/utils/registration.ts
··· 1 - import { prisma } from '#lib'; 1 + import { prisma, sendMail } from '#lib'; 2 2 import { 3 3 CredentialType, 4 4 type Major, ··· 6 6 type User, 7 7 type UserCandidate, 8 8 } from '@prisma/client'; 9 - import { createTransport } from 'nodemailer'; 10 9 import { createUid } from './uid.js'; 11 10 12 11 export const saveUser = async ({ ··· 60 59 61 60 await prisma.userCandidate.delete({ where: { id } }); 62 61 63 - const url = new URL('/welcome/', process.env.FRONTEND_ORIGIN); 64 - await createTransport(process.env.SMTP_URL).sendMail({ 65 - subject: `Bienvenue sur Churros!`, 66 - to: email, 67 - from: process.env.PUBLIC_SUPPORT_EMAIL, 68 - html: ` 69 - <p> 70 - <a href="${url.toString()}">Bienvenue sur Churros!</a> 71 - </p> 72 - `, 73 - text: `Bienvenue sur Churros ! Ça se passe ici : ${url.toString()}`, 74 - }); 62 + await sendMail( 63 + 'welcome', 64 + email, 65 + { url: new URL('/welcome/', process.env.FRONTEND_ORIGIN).toString() }, 66 + {}, 67 + ); 75 68 76 69 return user; 77 70 };
+843 -10
yarn.lock
··· 282 282 languageName: node 283 283 linkType: hard 284 284 285 + "@babel/runtime@npm:^7.23.9": 286 + version: 7.24.6 287 + resolution: "@babel/runtime@npm:7.24.6" 288 + dependencies: 289 + regenerator-runtime: "npm:^0.14.0" 290 + checksum: 10c0/224ad205de33ea28979baaec89eea4c4d4e9482000dd87d15b97859365511cdd4d06517712504024f5d33a5fb9412f9b91c96f1d923974adf9359e1575cde049 291 + languageName: node 292 + linkType: hard 293 + 285 294 "@babel/template@npm:^7.22.15, @babel/template@npm:^7.24.0": 286 295 version: 7.24.0 287 296 resolution: "@babel/template@npm:7.24.0" ··· 432 441 "@types/lodash.countby": "npm:^4.6.9" 433 442 "@types/lodash.omit": "npm:^4.5.9" 434 443 "@types/lodash.range": "npm:^3.2.9" 444 + "@types/mjml": "npm:^4" 435 445 "@types/multer": "npm:^1.4.11" 436 446 "@types/node": "npm:^20.12.2" 437 447 "@types/nodemailer": "npm:^6.4.14" ··· 464 474 graphql-scalars: "npm:^1.23.0" 465 475 graphql-ws: "npm:^5.16.0" 466 476 graphql-yoga: "npm:^5.3.0" 477 + handlebars: "npm:^4.7.8" 467 478 helmet: "npm:^7.1.0" 468 479 html-to-text: "npm:^9.0.5" 469 480 image-type: "npm:^5.2.0" ··· 479 490 lodash.omit: "npm:^4.5.0" 480 491 lodash.range: "npm:^3.2.0" 481 492 lodash.uniqby: "npm:^4.7.0" 493 + mjml: "npm:^4.15.3" 482 494 multer: "npm:^1.4.5-lts.1" 483 495 nanoid: "npm:^5.0.6" 484 496 nodemailer: "npm:^6.9.13" ··· 2251 2263 languageName: node 2252 2264 linkType: hard 2253 2265 2266 + "@one-ini/wasm@npm:0.1.1": 2267 + version: 0.1.1 2268 + resolution: "@one-ini/wasm@npm:0.1.1" 2269 + checksum: 10c0/54700e055037f1a63bfcc86d24822203b25759598c2c3e295d1435130a449108aebc119c9c2e467744767dbe0b6ab47a182c61aa1071ba7368f5e20ab197ba65 2270 + languageName: node 2271 + linkType: hard 2272 + 2254 2273 "@opentelemetry/api@npm:^1.4.0": 2255 2274 version: 1.8.0 2256 2275 resolution: "@opentelemetry/api@npm:1.8.0" ··· 3486 3505 languageName: node 3487 3506 linkType: hard 3488 3507 3508 + "@types/mjml-core@npm:*": 3509 + version: 4.7.4 3510 + resolution: "@types/mjml-core@npm:4.7.4" 3511 + checksum: 10c0/1c93f5a89b7620fa827fb4bb36f0cb2e034a0c5fbbafb35ddd227a90de375378e2b600ff8730fb99232fc6df1ee8639539841169501451329498abe98272a06c 3512 + languageName: node 3513 + linkType: hard 3514 + 3515 + "@types/mjml@npm:^4": 3516 + version: 4.7.4 3517 + resolution: "@types/mjml@npm:4.7.4" 3518 + dependencies: 3519 + "@types/mjml-core": "npm:*" 3520 + checksum: 10c0/6f4bbdf709e1f6c9b26be67146b1e4c759142fb4ddfa4c079b600835701bb2039c60cd530d016f1d2f1aef4256580e30cdc942c3acd6935e2fe56b5a665795ae 3521 + languageName: node 3522 + linkType: hard 3523 + 3489 3524 "@types/ms@npm:*": 3490 3525 version: 0.7.34 3491 3526 resolution: "@types/ms@npm:0.7.34" ··· 4045 4080 dependencies: 4046 4081 string-width: "npm:^4.1.0" 4047 4082 checksum: 10c0/ad8b755a253a1bc8234eb341e0cec68a857ab18bf97ba2bda529e86f6e30460416523e0ec58c32e5c21f0ca470d779503244892873a5895dbd0c39c788e82467 4083 + languageName: node 4084 + linkType: hard 4085 + 4086 + "ansi-colors@npm:^4.1.1": 4087 + version: 4.1.3 4088 + resolution: "ansi-colors@npm:4.1.3" 4089 + checksum: 10c0/ec87a2f59902f74e61eada7f6e6fe20094a628dab765cfdbd03c3477599368768cffccdb5d3bb19a1b6c99126783a143b1fee31aab729b31ffe5836c7e5e28b9 4048 4090 languageName: node 4049 4091 linkType: hard 4050 4092 ··· 4782 4824 languageName: node 4783 4825 linkType: hard 4784 4826 4827 + "camel-case@npm:^3.0.0": 4828 + version: 3.0.0 4829 + resolution: "camel-case@npm:3.0.0" 4830 + dependencies: 4831 + no-case: "npm:^2.2.0" 4832 + upper-case: "npm:^1.1.1" 4833 + checksum: 10c0/491c6bbf986b9d8355e12cca6beb719b44c2fe96e8526c09958a1b4e0dbb081a82ea59c13b5a6ccf9158ce5979cbe56a8a10d7322bfeed2d84725c6b89d8f934 4834 + languageName: node 4835 + linkType: hard 4836 + 4785 4837 "camelcase@npm:^5.0.0": 4786 4838 version: 5.3.1 4787 4839 resolution: "camelcase@npm:5.3.1" ··· 4933 4985 languageName: node 4934 4986 linkType: hard 4935 4987 4936 - "cheerio@npm:^1.0.0-rc.12": 4988 + "cheerio@npm:1.0.0-rc.12, cheerio@npm:^1.0.0-rc.12": 4937 4989 version: 1.0.0-rc.12 4938 4990 resolution: "cheerio@npm:1.0.0-rc.12" 4939 4991 dependencies: ··· 4948 5000 languageName: node 4949 5001 linkType: hard 4950 5002 4951 - "chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.4.1, chokidar@npm:^3.5.2, chokidar@npm:^3.6.0": 5003 + "chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.0.0, chokidar@npm:^3.4.1, chokidar@npm:^3.5.2, chokidar@npm:^3.6.0": 4952 5004 version: 3.6.0 4953 5005 resolution: "chokidar@npm:3.6.0" 4954 5006 dependencies: ··· 4985 5037 version: 4.0.0 4986 5038 resolution: "ci-info@npm:4.0.0" 4987 5039 checksum: 10c0/ecc003e5b60580bd081d83dd61d398ddb8607537f916313e40af4667f9c92a1243bd8e8a591a5aa78e418afec245dbe8e90a0e26e39ca0825129a99b978dd3f9 5040 + languageName: node 5041 + linkType: hard 5042 + 5043 + "clean-css@npm:^4.2.1": 5044 + version: 4.2.4 5045 + resolution: "clean-css@npm:4.2.4" 5046 + dependencies: 5047 + source-map: "npm:~0.6.0" 5048 + checksum: 10c0/0e41795fdc9d65e5e17a3b0016d90bf2a653e3a680829b5bcebdbab48604cfe36d96d8af6346338d2c2aca8aa9af024ac4fb752ac3eb5b71bef68a34a129b58a 4988 5049 languageName: node 4989 5050 linkType: hard 4990 5051 ··· 5221 5282 languageName: node 5222 5283 linkType: hard 5223 5284 5224 - "commander@npm:2, commander@npm:^2.20.3": 5285 + "commander@npm:2, commander@npm:^2.19.0, commander@npm:^2.20.3": 5225 5286 version: 2.20.3 5226 5287 resolution: "commander@npm:2.20.3" 5227 5288 checksum: 10c0/74c781a5248c2402a0a3e966a0a2bba3c054aad144f5c023364be83265e796b20565aa9feff624132ff629aa64e16999fa40a743c10c12f7c61e96a794b99288 ··· 5232 5293 version: 7.2.0 5233 5294 resolution: "commander@npm:7.2.0" 5234 5295 checksum: 10c0/8d690ff13b0356df7e0ebbe6c59b4712f754f4b724d4f473d3cc5b3fdcf978e3a5dc3078717858a2ceb50b0f84d0660a7f22a96cdc50fb877d0c9bb31593d23a 5296 + languageName: node 5297 + linkType: hard 5298 + 5299 + "commander@npm:^10.0.0": 5300 + version: 10.0.1 5301 + resolution: "commander@npm:10.0.1" 5302 + checksum: 10c0/53f33d8927758a911094adadda4b2cbac111a5b377d8706700587650fd8f45b0bbe336de4b5c3fe47fd61f420a3d9bd452b6e0e6e5600a7e74d7bf0174f6efe3 5303 + languageName: node 5304 + linkType: hard 5305 + 5306 + "commander@npm:^6.1.0": 5307 + version: 6.2.1 5308 + resolution: "commander@npm:6.2.1" 5309 + checksum: 10c0/85748abd9d18c8bc88febed58b98f66b7c591d9b5017cad459565761d7b29ca13b7783ea2ee5ce84bf235897333706c4ce29adf1ce15c8252780e7000e2ce9ea 5235 5310 languageName: node 5236 5311 linkType: hard 5237 5312 ··· 5304 5379 languageName: node 5305 5380 linkType: hard 5306 5381 5307 - "config-chain@npm:^1.1.11": 5382 + "config-chain@npm:^1.1.11, config-chain@npm:^1.1.13": 5308 5383 version: 1.1.13 5309 5384 resolution: "config-chain@npm:1.1.13" 5310 5385 dependencies: ··· 6321 6396 languageName: node 6322 6397 linkType: hard 6323 6398 6399 + "detect-node@npm:2.1.0, detect-node@npm:^2.0.4": 6400 + version: 2.1.0 6401 + resolution: "detect-node@npm:2.1.0" 6402 + checksum: 10c0/f039f601790f2e9d4654e499913259a798b1f5246ae24f86ab5e8bd4aaf3bce50484234c494f11fb00aecb0c6e2733aa7b1cf3f530865640b65fbbd65b2c4e09 6403 + languageName: node 6404 + linkType: hard 6405 + 6324 6406 "devalue@npm:^4.3.2": 6325 6407 version: 4.3.2 6326 6408 resolution: "devalue@npm:4.3.2" ··· 6383 6465 languageName: node 6384 6466 linkType: hard 6385 6467 6468 + "dom-serializer@npm:^1.0.1": 6469 + version: 1.4.1 6470 + resolution: "dom-serializer@npm:1.4.1" 6471 + dependencies: 6472 + domelementtype: "npm:^2.0.1" 6473 + domhandler: "npm:^4.2.0" 6474 + entities: "npm:^2.0.0" 6475 + checksum: 10c0/67d775fa1ea3de52035c98168ddcd59418356943b5eccb80e3c8b3da53adb8e37edb2cc2f885802b7b1765bf5022aec21dfc32910d7f9e6de4c3148f095ab5e0 6476 + languageName: node 6477 + linkType: hard 6478 + 6386 6479 "dom-serializer@npm:^2.0.0": 6387 6480 version: 2.0.0 6388 6481 resolution: "dom-serializer@npm:2.0.0" ··· 6394 6487 languageName: node 6395 6488 linkType: hard 6396 6489 6397 - "domelementtype@npm:^2.3.0": 6490 + "domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": 6398 6491 version: 2.3.0 6399 6492 resolution: "domelementtype@npm:2.3.0" 6400 6493 checksum: 10c0/686f5a9ef0fff078c1412c05db73a0dce096190036f33e400a07e2a4518e9f56b1e324f5c576a0a747ef0e75b5d985c040b0d51945ce780c0dd3c625a18cd8c9 6401 6494 languageName: node 6402 6495 linkType: hard 6403 6496 6497 + "domhandler@npm:^3.3.0": 6498 + version: 3.3.0 6499 + resolution: "domhandler@npm:3.3.0" 6500 + dependencies: 6501 + domelementtype: "npm:^2.0.1" 6502 + checksum: 10c0/376e6462a6144121f6ae50c9c1b8e0b22d2e0c68f9fb2ef6e57a5f4f9395854b1258cb638c58b171ee291359a5f41a4a57f403954db976484a59ffcee4c1e405 6503 + languageName: node 6504 + linkType: hard 6505 + 6506 + "domhandler@npm:^4.2.0": 6507 + version: 4.3.1 6508 + resolution: "domhandler@npm:4.3.1" 6509 + dependencies: 6510 + domelementtype: "npm:^2.2.0" 6511 + checksum: 10c0/5c199c7468cb052a8b5ab80b13528f0db3d794c64fc050ba793b574e158e67c93f8336e87fd81e9d5ee43b0e04aea4d8b93ed7be4899cb726a1601b3ba18538b 6512 + languageName: node 6513 + linkType: hard 6514 + 6404 6515 "domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": 6405 6516 version: 5.0.3 6406 6517 resolution: "domhandler@npm:5.0.3" ··· 6417 6528 languageName: node 6418 6529 linkType: hard 6419 6530 6420 - "domutils@npm:^3.0.1": 6531 + "domutils@npm:^2.4.2": 6532 + version: 2.8.0 6533 + resolution: "domutils@npm:2.8.0" 6534 + dependencies: 6535 + dom-serializer: "npm:^1.0.1" 6536 + domelementtype: "npm:^2.2.0" 6537 + domhandler: "npm:^4.2.0" 6538 + checksum: 10c0/d58e2ae01922f0dd55894e61d18119924d88091837887bf1438f2327f32c65eb76426bd9384f81e7d6dcfb048e0f83c19b222ad7101176ad68cdc9c695b563db 6539 + languageName: node 6540 + linkType: hard 6541 + 6542 + "domutils@npm:^3.0.1, domutils@npm:^3.1.0": 6421 6543 version: 3.1.0 6422 6544 resolution: "domutils@npm:3.1.0" 6423 6545 dependencies: ··· 6521 6643 languageName: node 6522 6644 linkType: hard 6523 6645 6646 + "editorconfig@npm:^1.0.4": 6647 + version: 1.0.4 6648 + resolution: "editorconfig@npm:1.0.4" 6649 + dependencies: 6650 + "@one-ini/wasm": "npm:0.1.1" 6651 + commander: "npm:^10.0.0" 6652 + minimatch: "npm:9.0.1" 6653 + semver: "npm:^7.5.3" 6654 + bin: 6655 + editorconfig: bin/editorconfig 6656 + checksum: 10c0/ed6985959d7b34a56e1c09bef118758c81c969489b768d152c93689fce8403b0452462e934f665febaba3478eebc0fd41c0a36100783eaadf6d926c4abc87a3d 6657 + languageName: node 6658 + linkType: hard 6659 + 6524 6660 "ee-first@npm:1.1.1": 6525 6661 version: 1.1.1 6526 6662 resolution: "ee-first@npm:1.1.1" ··· 6595 6731 languageName: node 6596 6732 linkType: hard 6597 6733 6598 - "entities@npm:^4.2.0, entities@npm:^4.4.0": 6734 + "entities@npm:^2.0.0": 6735 + version: 2.2.0 6736 + resolution: "entities@npm:2.2.0" 6737 + checksum: 10c0/7fba6af1f116300d2ba1c5673fc218af1961b20908638391b4e1e6d5850314ee2ac3ec22d741b3a8060479911c99305164aed19b6254bde75e7e6b1b2c3f3aa3 6738 + languageName: node 6739 + linkType: hard 6740 + 6741 + "entities@npm:^4.2.0, entities@npm:^4.4.0, entities@npm:^4.5.0": 6599 6742 version: 4.5.0 6600 6743 resolution: "entities@npm:4.5.0" 6601 6744 checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 ··· 7983 8126 languageName: node 7984 8127 linkType: hard 7985 8128 8129 + "glob@npm:^10.3.3": 8130 + version: 10.4.1 8131 + resolution: "glob@npm:10.4.1" 8132 + dependencies: 8133 + foreground-child: "npm:^3.1.0" 8134 + jackspeak: "npm:^3.1.2" 8135 + minimatch: "npm:^9.0.4" 8136 + minipass: "npm:^7.1.2" 8137 + path-scurry: "npm:^1.11.1" 8138 + bin: 8139 + glob: dist/esm/bin.mjs 8140 + checksum: 10c0/77f2900ed98b9cc2a0e1901ee5e476d664dae3cd0f1b662b8bfd4ccf00d0edc31a11595807706a274ca10e1e251411bbf2e8e976c82bed0d879a9b89343ed379 8141 + languageName: node 8142 + linkType: hard 8143 + 7986 8144 "glob@npm:^6.0.1": 7987 8145 version: 6.0.4 7988 8146 resolution: "glob@npm:6.0.4" ··· 8443 8601 languageName: node 8444 8602 linkType: hard 8445 8603 8604 + "handlebars@npm:^4.7.8": 8605 + version: 4.7.8 8606 + resolution: "handlebars@npm:4.7.8" 8607 + dependencies: 8608 + minimist: "npm:^1.2.5" 8609 + neo-async: "npm:^2.6.2" 8610 + source-map: "npm:^0.6.1" 8611 + uglify-js: "npm:^3.1.4" 8612 + wordwrap: "npm:^1.0.0" 8613 + dependenciesMeta: 8614 + uglify-js: 8615 + optional: true 8616 + bin: 8617 + handlebars: bin/handlebars 8618 + checksum: 10c0/7aff423ea38a14bb379316f3857fe0df3c5d66119270944247f155ba1f08e07a92b340c58edaa00cfe985c21508870ee5183e0634dcb53dd405f35c93ef7f10d 8619 + languageName: node 8620 + linkType: hard 8621 + 8446 8622 "has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2": 8447 8623 version: 1.0.2 8448 8624 resolution: "has-bigints@npm:1.0.2" ··· 8684 8860 languageName: node 8685 8861 linkType: hard 8686 8862 8863 + "he@npm:^1.2.0": 8864 + version: 1.2.0 8865 + resolution: "he@npm:1.2.0" 8866 + bin: 8867 + he: bin/he 8868 + checksum: 10c0/a27d478befe3c8192f006cdd0639a66798979dfa6e2125c6ac582a19a5ebfec62ad83e8382e6036170d873f46e4536a7e795bf8b95bf7c247f4cc0825ccc8c17 8869 + languageName: node 8870 + linkType: hard 8871 + 8687 8872 "helmet@npm:^7.1.0": 8688 8873 version: 7.1.0 8689 8874 resolution: "helmet@npm:7.1.0" ··· 8721 8906 languageName: node 8722 8907 linkType: hard 8723 8908 8909 + "html-minifier@npm:^4.0.0": 8910 + version: 4.0.0 8911 + resolution: "html-minifier@npm:4.0.0" 8912 + dependencies: 8913 + camel-case: "npm:^3.0.0" 8914 + clean-css: "npm:^4.2.1" 8915 + commander: "npm:^2.19.0" 8916 + he: "npm:^1.2.0" 8917 + param-case: "npm:^2.1.1" 8918 + relateurl: "npm:^0.2.7" 8919 + uglify-js: "npm:^3.5.1" 8920 + bin: 8921 + html-minifier: ./cli.js 8922 + checksum: 10c0/38c2d1cab49593671b104e3bc120b1c5fdf00c75930fcb32e257322219c9d31515af6b39af76e8ecd71fdf3a77d168f8b7b7ce02beba0b72eb5631599b5561bc 8923 + languageName: node 8924 + linkType: hard 8925 + 8724 8926 "html-tags@npm:^3.3.1": 8725 8927 version: 3.3.1 8726 8928 resolution: "html-tags@npm:3.3.1" ··· 8762 8964 languageName: node 8763 8965 linkType: hard 8764 8966 8967 + "htmlparser2@npm:^5.0.0": 8968 + version: 5.0.1 8969 + resolution: "htmlparser2@npm:5.0.1" 8970 + dependencies: 8971 + domelementtype: "npm:^2.0.1" 8972 + domhandler: "npm:^3.3.0" 8973 + domutils: "npm:^2.4.2" 8974 + entities: "npm:^2.0.0" 8975 + checksum: 10c0/3f276f7ac518930f5330cfe5129dd5764a63e9bae6f57350e90b26affc94b11b2fb6750f056fed245b726d500e78197b4a09c7108c71964fe91303e6e2a29107 8976 + languageName: node 8977 + linkType: hard 8978 + 8765 8979 "htmlparser2@npm:^8.0.0, htmlparser2@npm:^8.0.1, htmlparser2@npm:^8.0.2": 8766 8980 version: 8.0.2 8767 8981 resolution: "htmlparser2@npm:8.0.2" ··· 8771 8985 domutils: "npm:^3.0.1" 8772 8986 entities: "npm:^4.4.0" 8773 8987 checksum: 10c0/609cca85886d0bf2c9a5db8c6926a89f3764596877492e2caa7a25a789af4065bc6ee2cdc81807fe6b1d03a87bf8a373b5a754528a4cc05146b713c20575aab4 8988 + languageName: node 8989 + linkType: hard 8990 + 8991 + "htmlparser2@npm:^9.1.0": 8992 + version: 9.1.0 8993 + resolution: "htmlparser2@npm:9.1.0" 8994 + dependencies: 8995 + domelementtype: "npm:^2.3.0" 8996 + domhandler: "npm:^5.0.3" 8997 + domutils: "npm:^3.1.0" 8998 + entities: "npm:^4.5.0" 8999 + checksum: 10c0/394f6323efc265bbc791d8c0d96bfe95984e0407565248521ab92e2dc7668e5ceeca7bc6ed18d408b9ee3b25032c5743368a4280d280332d782821d5d467ad8f 8774 9000 languageName: node 8775 9001 linkType: hard 8776 9002 ··· 9662 9888 languageName: node 9663 9889 linkType: hard 9664 9890 9891 + "jackspeak@npm:^3.1.2": 9892 + version: 3.1.2 9893 + resolution: "jackspeak@npm:3.1.2" 9894 + dependencies: 9895 + "@isaacs/cliui": "npm:^8.0.2" 9896 + "@pkgjs/parseargs": "npm:^0.11.0" 9897 + dependenciesMeta: 9898 + "@pkgjs/parseargs": 9899 + optional: true 9900 + checksum: 10c0/5f1922a1ca0f19869e23f0dc4374c60d36e922f7926c76fecf8080cc6f7f798d6a9caac1b9428327d14c67731fd551bb3454cb270a5e13a0718f3b3660ec3d5d 9901 + languageName: node 9902 + linkType: hard 9903 + 9665 9904 "jiti@npm:^1.19.1": 9666 9905 version: 1.21.0 9667 9906 resolution: "jiti@npm:1.21.0" ··· 9671 9910 languageName: node 9672 9911 linkType: hard 9673 9912 9913 + "js-beautify@npm:^1.6.14": 9914 + version: 1.15.1 9915 + resolution: "js-beautify@npm:1.15.1" 9916 + dependencies: 9917 + config-chain: "npm:^1.1.13" 9918 + editorconfig: "npm:^1.0.4" 9919 + glob: "npm:^10.3.3" 9920 + js-cookie: "npm:^3.0.5" 9921 + nopt: "npm:^7.2.0" 9922 + bin: 9923 + css-beautify: js/bin/css-beautify.js 9924 + html-beautify: js/bin/html-beautify.js 9925 + js-beautify: js/bin/js-beautify.js 9926 + checksum: 10c0/4140dd95537143eb429b6c8e47e21310f16c032d97a03163c6c7c0502bc663242a5db08d3ad941b87f24a142ce4f9190c556d2340bcd056545326377dfae5362 9927 + languageName: node 9928 + linkType: hard 9929 + 9930 + "js-cookie@npm:^3.0.5": 9931 + version: 3.0.5 9932 + resolution: "js-cookie@npm:3.0.5" 9933 + checksum: 10c0/04a0e560407b4489daac3a63e231d35f4e86f78bff9d792011391b49c59f721b513411cd75714c418049c8dc9750b20fcddad1ca5a2ca616c3aca4874cce5b3a 9934 + languageName: node 9935 + linkType: hard 9936 + 9674 9937 "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": 9675 9938 version: 4.0.0 9676 9939 resolution: "js-tokens@npm:4.0.0" ··· 9874 10137 readable-stream: "npm:~2.3.6" 9875 10138 setimmediate: "npm:^1.0.5" 9876 10139 checksum: 10c0/58e01ec9c4960383fb8b38dd5f67b83ccc1ec215bf74c8a5b32f42b6e5fb79fada5176842a11409c4051b5b94275044851814a31076bf49e1be218d3ef57c863 10140 + languageName: node 10141 + linkType: hard 10142 + 10143 + "juice@npm:^10.0.0": 10144 + version: 10.0.0 10145 + resolution: "juice@npm:10.0.0" 10146 + dependencies: 10147 + cheerio: "npm:^1.0.0-rc.12" 10148 + commander: "npm:^6.1.0" 10149 + mensch: "npm:^0.3.4" 10150 + slick: "npm:^1.12.2" 10151 + web-resource-inliner: "npm:^6.0.1" 10152 + bin: 10153 + juice: bin/juice 10154 + checksum: 10c0/55461554aa564d945460ed9ba6257813cca95c33202449beb37868d14e2dbb46b31d5e4b2eeaefb7e7390d7d7504d0de32668fcbfdcb7e579a732ee954738631 9877 10155 languageName: node 9878 10156 linkType: hard 9879 10157 ··· 10378 10656 languageName: node 10379 10657 linkType: hard 10380 10658 10381 - "lodash@npm:4.17.21, lodash@npm:^4.17.21": 10659 + "lodash@npm:4.17.21, lodash@npm:^4.17.15, lodash@npm:^4.17.21": 10382 10660 version: 4.17.21 10383 10661 resolution: "lodash@npm:4.17.21" 10384 10662 checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c ··· 10436 10714 languageName: node 10437 10715 linkType: hard 10438 10716 10717 + "lower-case@npm:^1.1.1": 10718 + version: 1.1.4 10719 + resolution: "lower-case@npm:1.1.4" 10720 + checksum: 10c0/2153ae5490d655a63addc8e7d2f848c6c94803b342ed2d177f75e8073e9fbb50a733d1432c82e1cb8425fa6eae14b2877bf5bbdcb93ab93bb982fb5c3962c57b 10721 + languageName: node 10722 + linkType: hard 10723 + 10439 10724 "lowercase-keys@npm:^3.0.0": 10440 10725 version: 3.0.0 10441 10726 resolution: "lowercase-keys@npm:3.0.0" ··· 10801 11086 version: 0.3.0 10802 11087 resolution: "media-typer@npm:0.3.0" 10803 11088 checksum: 10c0/d160f31246907e79fed398470285f21bafb45a62869dc469b1c8877f3f064f5eabc4bcc122f9479b8b605bc5c76187d7871cf84c4ee3ecd3e487da1993279928 11089 + languageName: node 11090 + linkType: hard 11091 + 11092 + "mensch@npm:^0.3.4": 11093 + version: 0.3.4 11094 + resolution: "mensch@npm:0.3.4" 11095 + checksum: 10c0/177f9c1cb1acd93da98a971288a5da99f819ac06de19ca450040b18ddf8728c7ae0ce22309fadbbfd4ceb773bc5c03bf1cb93ceb91441da9e76e010d314da2ea 10804 11096 languageName: node 10805 11097 linkType: hard 10806 11098 ··· 11237 11529 languageName: node 11238 11530 linkType: hard 11239 11531 11532 + "mime@npm:^2.4.6": 11533 + version: 2.6.0 11534 + resolution: "mime@npm:2.6.0" 11535 + bin: 11536 + mime: cli.js 11537 + checksum: 10c0/a7f2589900d9c16e3bdf7672d16a6274df903da958c1643c9c45771f0478f3846dcb1097f31eb9178452570271361e2149310931ec705c037210fc69639c8e6c 11538 + languageName: node 11539 + linkType: hard 11540 + 11240 11541 "mimic-fn@npm:^2.1.0": 11241 11542 version: 2.1.0 11242 11543 resolution: "mimic-fn@npm:2.1.0" ··· 11288 11589 languageName: node 11289 11590 linkType: hard 11290 11591 11592 + "minimatch@npm:9.0.1": 11593 + version: 9.0.1 11594 + resolution: "minimatch@npm:9.0.1" 11595 + dependencies: 11596 + brace-expansion: "npm:^2.0.1" 11597 + checksum: 10c0/aa043eb8822210b39888a5d0d28df0017b365af5add9bd522f180d2a6962de1cbbf1bdeacdb1b17f410dc3336bc8d76fb1d3e814cdc65d00c2f68e01f0010096 11598 + languageName: node 11599 + linkType: hard 11600 + 11291 11601 "minimatch@npm:9.0.3, minimatch@npm:^9.0.1": 11292 11602 version: 9.0.3 11293 11603 resolution: "minimatch@npm:9.0.3" ··· 11303 11613 dependencies: 11304 11614 brace-expansion: "npm:^2.0.1" 11305 11615 checksum: 10c0/3defdfd230914f22a8da203747c42ee3c405c39d4d37ffda284dac5e45b7e1f6c49aa8be606509002898e73091ff2a3bbfc59c2c6c71d4660609f63aa92f98e3 11616 + languageName: node 11617 + linkType: hard 11618 + 11619 + "minimatch@npm:^9.0.3, minimatch@npm:^9.0.4": 11620 + version: 9.0.4 11621 + resolution: "minimatch@npm:9.0.4" 11622 + dependencies: 11623 + brace-expansion: "npm:^2.0.1" 11624 + checksum: 10c0/2c16f21f50e64922864e560ff97c587d15fd491f65d92a677a344e970fe62aafdbeafe648965fa96d33c061b4d0eabfe0213466203dd793367e7f28658cf6414 11306 11625 languageName: node 11307 11626 linkType: hard 11308 11627 ··· 11387 11706 languageName: node 11388 11707 linkType: hard 11389 11708 11709 + "minipass@npm:^7.1.2": 11710 + version: 7.1.2 11711 + resolution: "minipass@npm:7.1.2" 11712 + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 11713 + languageName: node 11714 + linkType: hard 11715 + 11390 11716 "minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": 11391 11717 version: 2.1.2 11392 11718 resolution: "minizlib@npm:2.1.2" ··· 11397 11723 languageName: node 11398 11724 linkType: hard 11399 11725 11726 + "mjml-accordion@npm:4.15.3": 11727 + version: 4.15.3 11728 + resolution: "mjml-accordion@npm:4.15.3" 11729 + dependencies: 11730 + "@babel/runtime": "npm:^7.23.9" 11731 + lodash: "npm:^4.17.21" 11732 + mjml-core: "npm:4.15.3" 11733 + checksum: 10c0/d03bfbb6cf904f8c3a2aeef5c722e8d29aeb69c57dfc2f569ac90ca374a6c896276057dea5ec4742bf537d46cf789f5d41eb98baeaa54614946dcab0cd24bbff 11734 + languageName: node 11735 + linkType: hard 11736 + 11737 + "mjml-body@npm:4.15.3": 11738 + version: 4.15.3 11739 + resolution: "mjml-body@npm:4.15.3" 11740 + dependencies: 11741 + "@babel/runtime": "npm:^7.23.9" 11742 + lodash: "npm:^4.17.21" 11743 + mjml-core: "npm:4.15.3" 11744 + checksum: 10c0/59e177506bd08fd65865ac8c17bf8b96ae5a1e841e86b2967371162b06442f300afdac24031ec847dca0313dc0ba33b63c20886b1f0f26aa8a4715f0db2ddeda 11745 + languageName: node 11746 + linkType: hard 11747 + 11748 + "mjml-button@npm:4.15.3": 11749 + version: 4.15.3 11750 + resolution: "mjml-button@npm:4.15.3" 11751 + dependencies: 11752 + "@babel/runtime": "npm:^7.23.9" 11753 + lodash: "npm:^4.17.21" 11754 + mjml-core: "npm:4.15.3" 11755 + checksum: 10c0/6619ab6a407d117863a7683591f27082a2131e107b8f849e77ecd3db89a177ed1af4a83ed96b08e84564cc9414b91c909f2b1c775d80bce529f284db910032d6 11756 + languageName: node 11757 + linkType: hard 11758 + 11759 + "mjml-carousel@npm:4.15.3": 11760 + version: 4.15.3 11761 + resolution: "mjml-carousel@npm:4.15.3" 11762 + dependencies: 11763 + "@babel/runtime": "npm:^7.23.9" 11764 + lodash: "npm:^4.17.21" 11765 + mjml-core: "npm:4.15.3" 11766 + checksum: 10c0/29a5fad7814792e1ccda464f7d77350cc0817ab941170fbb9aecbfcc4f85cf9daefb697fed93d7621f930b4e73f5a8ce69158f6e8434aff018c1f69bd13ff80b 11767 + languageName: node 11768 + linkType: hard 11769 + 11770 + "mjml-cli@npm:4.15.3": 11771 + version: 4.15.3 11772 + resolution: "mjml-cli@npm:4.15.3" 11773 + dependencies: 11774 + "@babel/runtime": "npm:^7.23.9" 11775 + chokidar: "npm:^3.0.0" 11776 + glob: "npm:^10.3.10" 11777 + html-minifier: "npm:^4.0.0" 11778 + js-beautify: "npm:^1.6.14" 11779 + lodash: "npm:^4.17.21" 11780 + minimatch: "npm:^9.0.3" 11781 + mjml-core: "npm:4.15.3" 11782 + mjml-migrate: "npm:4.15.3" 11783 + mjml-parser-xml: "npm:4.15.3" 11784 + mjml-validator: "npm:4.15.3" 11785 + yargs: "npm:^17.7.2" 11786 + bin: 11787 + mjml-cli: bin/mjml 11788 + checksum: 10c0/82aa3fe91a4457a887ac00d86830681e88fda65365422ef0370bd03a327e4b2baa75dc6d822534cbfc91f9e4b820229de43410a9c432627fc439ddd8a9c3caf8 11789 + languageName: node 11790 + linkType: hard 11791 + 11792 + "mjml-column@npm:4.15.3": 11793 + version: 4.15.3 11794 + resolution: "mjml-column@npm:4.15.3" 11795 + dependencies: 11796 + "@babel/runtime": "npm:^7.23.9" 11797 + lodash: "npm:^4.17.21" 11798 + mjml-core: "npm:4.15.3" 11799 + checksum: 10c0/55f6f179d5f18de873d59579e62f2b3dfa67325a7d8d7ac77f9ed4c9451b3c70af68d36c0256f3244c4cc567aff77373f6c54b67819b8de9d08673f69f113952 11800 + languageName: node 11801 + linkType: hard 11802 + 11803 + "mjml-core@npm:4.15.3": 11804 + version: 4.15.3 11805 + resolution: "mjml-core@npm:4.15.3" 11806 + dependencies: 11807 + "@babel/runtime": "npm:^7.23.9" 11808 + cheerio: "npm:1.0.0-rc.12" 11809 + detect-node: "npm:^2.0.4" 11810 + html-minifier: "npm:^4.0.0" 11811 + js-beautify: "npm:^1.6.14" 11812 + juice: "npm:^10.0.0" 11813 + lodash: "npm:^4.17.21" 11814 + mjml-migrate: "npm:4.15.3" 11815 + mjml-parser-xml: "npm:4.15.3" 11816 + mjml-validator: "npm:4.15.3" 11817 + checksum: 10c0/75612453055a3e07e4da40e3f88db7925170f5a210fdaa8ec798eacde2b825862d9d6228ab3b45158e0a9a063720fdc3e83f9a2fd2c0978eaa2323a668bed2f0 11818 + languageName: node 11819 + linkType: hard 11820 + 11821 + "mjml-divider@npm:4.15.3": 11822 + version: 4.15.3 11823 + resolution: "mjml-divider@npm:4.15.3" 11824 + dependencies: 11825 + "@babel/runtime": "npm:^7.23.9" 11826 + lodash: "npm:^4.17.21" 11827 + mjml-core: "npm:4.15.3" 11828 + checksum: 10c0/122c2c3af5736ae163624fd5342781d3611c04768ddf33a5f755a8f76b21df000a911b1f44c5b325f5eb05bd9d726cdc72852e304ff939bfca701430a97466ff 11829 + languageName: node 11830 + linkType: hard 11831 + 11832 + "mjml-group@npm:4.15.3": 11833 + version: 4.15.3 11834 + resolution: "mjml-group@npm:4.15.3" 11835 + dependencies: 11836 + "@babel/runtime": "npm:^7.23.9" 11837 + lodash: "npm:^4.17.21" 11838 + mjml-core: "npm:4.15.3" 11839 + checksum: 10c0/61ac4504c75d07a2430172d4e057c933ff1d428315c2acf73f773961e02795a042e058487040aacb0da4822f1031c4b889e08ffba574098c9b3a166184b02f0c 11840 + languageName: node 11841 + linkType: hard 11842 + 11843 + "mjml-head-attributes@npm:4.15.3": 11844 + version: 4.15.3 11845 + resolution: "mjml-head-attributes@npm:4.15.3" 11846 + dependencies: 11847 + "@babel/runtime": "npm:^7.23.9" 11848 + lodash: "npm:^4.17.21" 11849 + mjml-core: "npm:4.15.3" 11850 + checksum: 10c0/733000b0d6e823b2ea91518e795f271f35fd410698ee4cb02db29a22b267d8f0daf9fb5562cd4d431421a4b8e70371ba962ccb41469f5ed8a31be4deca60905a 11851 + languageName: node 11852 + linkType: hard 11853 + 11854 + "mjml-head-breakpoint@npm:4.15.3": 11855 + version: 4.15.3 11856 + resolution: "mjml-head-breakpoint@npm:4.15.3" 11857 + dependencies: 11858 + "@babel/runtime": "npm:^7.23.9" 11859 + lodash: "npm:^4.17.21" 11860 + mjml-core: "npm:4.15.3" 11861 + checksum: 10c0/23930e576a6e4f7b7add412d255bafac917612d9a317b5086893a4b3688fa78d99112748067579d76308a545975a8deec7dd32013b326c08a4864ff7c4e86917 11862 + languageName: node 11863 + linkType: hard 11864 + 11865 + "mjml-head-font@npm:4.15.3": 11866 + version: 4.15.3 11867 + resolution: "mjml-head-font@npm:4.15.3" 11868 + dependencies: 11869 + "@babel/runtime": "npm:^7.23.9" 11870 + lodash: "npm:^4.17.21" 11871 + mjml-core: "npm:4.15.3" 11872 + checksum: 10c0/4ee959a3742694e21de42b156c2e245c4ade84977dc95b82ca232882129672f66dac59bdbb3398b9471c9583d9876088be59be6261beb86ac23941a21877d413 11873 + languageName: node 11874 + linkType: hard 11875 + 11876 + "mjml-head-html-attributes@npm:4.15.3": 11877 + version: 4.15.3 11878 + resolution: "mjml-head-html-attributes@npm:4.15.3" 11879 + dependencies: 11880 + "@babel/runtime": "npm:^7.23.9" 11881 + lodash: "npm:^4.17.21" 11882 + mjml-core: "npm:4.15.3" 11883 + checksum: 10c0/3161edf6cff97f47be0780ddcdba8d4c9c195c0a42d824c191c234fa3944b5f7ec61d762f9e59ecf307e53040d1d667fb6ba5729f308d85aff082a14e48ce2b6 11884 + languageName: node 11885 + linkType: hard 11886 + 11887 + "mjml-head-preview@npm:4.15.3": 11888 + version: 4.15.3 11889 + resolution: "mjml-head-preview@npm:4.15.3" 11890 + dependencies: 11891 + "@babel/runtime": "npm:^7.23.9" 11892 + lodash: "npm:^4.17.21" 11893 + mjml-core: "npm:4.15.3" 11894 + checksum: 10c0/9d53c0615e691699f94d5686376ca7342fb403f8511b9031b482a8b60e4b54ddda9a924153149b8ea650411db54e9a84c4292e66e3d5b097ad3725fbbd567a41 11895 + languageName: node 11896 + linkType: hard 11897 + 11898 + "mjml-head-style@npm:4.15.3": 11899 + version: 4.15.3 11900 + resolution: "mjml-head-style@npm:4.15.3" 11901 + dependencies: 11902 + "@babel/runtime": "npm:^7.23.9" 11903 + lodash: "npm:^4.17.21" 11904 + mjml-core: "npm:4.15.3" 11905 + checksum: 10c0/b7bc2ec2065c5758016a792fbb3fd5c31beaa48257581a36a3e710225d4b6c6efe807c23f2f69e883d71a670e696fd9ea17654805603c7f67ee2556e28f96ba6 11906 + languageName: node 11907 + linkType: hard 11908 + 11909 + "mjml-head-title@npm:4.15.3": 11910 + version: 4.15.3 11911 + resolution: "mjml-head-title@npm:4.15.3" 11912 + dependencies: 11913 + "@babel/runtime": "npm:^7.23.9" 11914 + lodash: "npm:^4.17.21" 11915 + mjml-core: "npm:4.15.3" 11916 + checksum: 10c0/2f0d9b0326acfb9fdd7e0c4270e8359dc661a4c250c07b0966da82f0998030f8e4cdd95c9539a4ca41264bd7ee0c11cd0cb98bbe19d3bad9af38afb92fb16582 11917 + languageName: node 11918 + linkType: hard 11919 + 11920 + "mjml-head@npm:4.15.3": 11921 + version: 4.15.3 11922 + resolution: "mjml-head@npm:4.15.3" 11923 + dependencies: 11924 + "@babel/runtime": "npm:^7.23.9" 11925 + lodash: "npm:^4.17.21" 11926 + mjml-core: "npm:4.15.3" 11927 + checksum: 10c0/db09298416864d8ddfc45ea7c18e2117d1786668aec4b8b8dfa14e36ef48c9f0c4d1d097fb6c3b769b76f5a105ce641ce9f3db5e82971e1bd412bc100f24c824 11928 + languageName: node 11929 + linkType: hard 11930 + 11931 + "mjml-hero@npm:4.15.3": 11932 + version: 4.15.3 11933 + resolution: "mjml-hero@npm:4.15.3" 11934 + dependencies: 11935 + "@babel/runtime": "npm:^7.23.9" 11936 + lodash: "npm:^4.17.21" 11937 + mjml-core: "npm:4.15.3" 11938 + checksum: 10c0/d7960de47f0fc23099a94d201c27285729765e7072e4eb8ea456141d8dcb05be52e39b01c27ba23ca82a43e35a0c5cbc3b042dcaf724bc7c4b21e62d450aa4e2 11939 + languageName: node 11940 + linkType: hard 11941 + 11942 + "mjml-image@npm:4.15.3": 11943 + version: 4.15.3 11944 + resolution: "mjml-image@npm:4.15.3" 11945 + dependencies: 11946 + "@babel/runtime": "npm:^7.23.9" 11947 + lodash: "npm:^4.17.21" 11948 + mjml-core: "npm:4.15.3" 11949 + checksum: 10c0/2f4442dc03196c0697aad3fcfd42d5991ef902587d37a416169d7c6c605df7044d8081a4beec673689b7ab20f95d31c6d1834948c2229d84283eee138ccf655a 11950 + languageName: node 11951 + linkType: hard 11952 + 11953 + "mjml-migrate@npm:4.15.3": 11954 + version: 4.15.3 11955 + resolution: "mjml-migrate@npm:4.15.3" 11956 + dependencies: 11957 + "@babel/runtime": "npm:^7.23.9" 11958 + js-beautify: "npm:^1.6.14" 11959 + lodash: "npm:^4.17.21" 11960 + mjml-core: "npm:4.15.3" 11961 + mjml-parser-xml: "npm:4.15.3" 11962 + yargs: "npm:^17.7.2" 11963 + bin: 11964 + migrate: lib/cli.js 11965 + checksum: 10c0/6e5a82426d19d466372325b7aad4170744992ee69d8bf5bd437cea20bec30803458456a933592f6f1a5af9ed3aa6a6f5d4e8b44150c21ca268fcd7a8f7f9f51e 11966 + languageName: node 11967 + linkType: hard 11968 + 11969 + "mjml-navbar@npm:4.15.3": 11970 + version: 4.15.3 11971 + resolution: "mjml-navbar@npm:4.15.3" 11972 + dependencies: 11973 + "@babel/runtime": "npm:^7.23.9" 11974 + lodash: "npm:^4.17.21" 11975 + mjml-core: "npm:4.15.3" 11976 + checksum: 10c0/abfa04c5b6d734e645ff24f7f557e5bab44d2b7ccc1f0ce8854c5121efe449118475ef525f940ccfc5b3dad1de9c71f1e15d2fbf9fb896a84e1a26a2bfbb0e23 11977 + languageName: node 11978 + linkType: hard 11979 + 11980 + "mjml-parser-xml@npm:4.15.3": 11981 + version: 4.15.3 11982 + resolution: "mjml-parser-xml@npm:4.15.3" 11983 + dependencies: 11984 + "@babel/runtime": "npm:^7.23.9" 11985 + detect-node: "npm:2.1.0" 11986 + htmlparser2: "npm:^9.1.0" 11987 + lodash: "npm:^4.17.15" 11988 + checksum: 10c0/d88a0552360589c7e82a0772d734c0d8975ad91ee5764756fc3e3cc28d062fecc87ca9b9b763f3a0309e96d1e1b66c0e707aa4bc8d7065f8121b256aa8efc85d 11989 + languageName: node 11990 + linkType: hard 11991 + 11992 + "mjml-preset-core@npm:4.15.3": 11993 + version: 4.15.3 11994 + resolution: "mjml-preset-core@npm:4.15.3" 11995 + dependencies: 11996 + "@babel/runtime": "npm:^7.23.9" 11997 + mjml-accordion: "npm:4.15.3" 11998 + mjml-body: "npm:4.15.3" 11999 + mjml-button: "npm:4.15.3" 12000 + mjml-carousel: "npm:4.15.3" 12001 + mjml-column: "npm:4.15.3" 12002 + mjml-divider: "npm:4.15.3" 12003 + mjml-group: "npm:4.15.3" 12004 + mjml-head: "npm:4.15.3" 12005 + mjml-head-attributes: "npm:4.15.3" 12006 + mjml-head-breakpoint: "npm:4.15.3" 12007 + mjml-head-font: "npm:4.15.3" 12008 + mjml-head-html-attributes: "npm:4.15.3" 12009 + mjml-head-preview: "npm:4.15.3" 12010 + mjml-head-style: "npm:4.15.3" 12011 + mjml-head-title: "npm:4.15.3" 12012 + mjml-hero: "npm:4.15.3" 12013 + mjml-image: "npm:4.15.3" 12014 + mjml-navbar: "npm:4.15.3" 12015 + mjml-raw: "npm:4.15.3" 12016 + mjml-section: "npm:4.15.3" 12017 + mjml-social: "npm:4.15.3" 12018 + mjml-spacer: "npm:4.15.3" 12019 + mjml-table: "npm:4.15.3" 12020 + mjml-text: "npm:4.15.3" 12021 + mjml-wrapper: "npm:4.15.3" 12022 + checksum: 10c0/07112b5e0a72a1c71b65f5a0936d61e06b11b5d6e0ec7a68351c1bef37b0ba2b95e90bed9ecdf3e823f44c9ad80ceec3d2b217078463bf97655b9de604d398e4 12023 + languageName: node 12024 + linkType: hard 12025 + 12026 + "mjml-raw@npm:4.15.3": 12027 + version: 4.15.3 12028 + resolution: "mjml-raw@npm:4.15.3" 12029 + dependencies: 12030 + "@babel/runtime": "npm:^7.23.9" 12031 + lodash: "npm:^4.17.21" 12032 + mjml-core: "npm:4.15.3" 12033 + checksum: 10c0/ad6f42d293bc428b24eb1bea3f749a92e6de10a9b6fef927166ce3b7c7eb0b651c1f9a10a105dc1ae9c7247c7cb50091d64bb0a35d851620c7589596dde764fb 12034 + languageName: node 12035 + linkType: hard 12036 + 12037 + "mjml-section@npm:4.15.3": 12038 + version: 4.15.3 12039 + resolution: "mjml-section@npm:4.15.3" 12040 + dependencies: 12041 + "@babel/runtime": "npm:^7.23.9" 12042 + lodash: "npm:^4.17.21" 12043 + mjml-core: "npm:4.15.3" 12044 + checksum: 10c0/395dae3f4bd6201a5a1ac66af4cea7c7a988017e122ce661853935b3312e3c1722d35028b889024b0abe78c086c64d1ad387f683f9a9c44fd2b1e3a528f6d881 12045 + languageName: node 12046 + linkType: hard 12047 + 12048 + "mjml-social@npm:4.15.3": 12049 + version: 4.15.3 12050 + resolution: "mjml-social@npm:4.15.3" 12051 + dependencies: 12052 + "@babel/runtime": "npm:^7.23.9" 12053 + lodash: "npm:^4.17.21" 12054 + mjml-core: "npm:4.15.3" 12055 + checksum: 10c0/2c7e24fbd81498690c3dab2cea9f2ac1858a20caad3fa00c11fa833fbccb57eaf01b1cfaadf12b9a279b620b46ce1027d5b0291f9b61ccb8bfe5c0ac9847722d 12056 + languageName: node 12057 + linkType: hard 12058 + 12059 + "mjml-spacer@npm:4.15.3": 12060 + version: 4.15.3 12061 + resolution: "mjml-spacer@npm:4.15.3" 12062 + dependencies: 12063 + "@babel/runtime": "npm:^7.23.9" 12064 + lodash: "npm:^4.17.21" 12065 + mjml-core: "npm:4.15.3" 12066 + checksum: 10c0/0956418975f12bd0002393e52d4468bf44aff4df57c50711f77be6dbc3856669278366f1f8d264163950793670246fb0f64f1544ba67249c9a74e397b0df06f9 12067 + languageName: node 12068 + linkType: hard 12069 + 12070 + "mjml-table@npm:4.15.3": 12071 + version: 4.15.3 12072 + resolution: "mjml-table@npm:4.15.3" 12073 + dependencies: 12074 + "@babel/runtime": "npm:^7.23.9" 12075 + lodash: "npm:^4.17.21" 12076 + mjml-core: "npm:4.15.3" 12077 + checksum: 10c0/a4ea3c476ce1c1c15ce42092e8a8e7db3044ab3ddaf0542f62c00c483d087ebb6cc42ef7d1dd777e052378becc4591395f649752ecb4769b08f0740222a217f7 12078 + languageName: node 12079 + linkType: hard 12080 + 12081 + "mjml-text@npm:4.15.3": 12082 + version: 4.15.3 12083 + resolution: "mjml-text@npm:4.15.3" 12084 + dependencies: 12085 + "@babel/runtime": "npm:^7.23.9" 12086 + lodash: "npm:^4.17.21" 12087 + mjml-core: "npm:4.15.3" 12088 + checksum: 10c0/e652ad95269d262a3d67d3b00eb28a356d4a830db77858ce31705ff980ba3f63a5775992b9c8189225056db6d3157e7dda994cbd771a2eef9ad7913a01ea0ef7 12089 + languageName: node 12090 + linkType: hard 12091 + 12092 + "mjml-validator@npm:4.15.3": 12093 + version: 4.15.3 12094 + resolution: "mjml-validator@npm:4.15.3" 12095 + dependencies: 12096 + "@babel/runtime": "npm:^7.23.9" 12097 + checksum: 10c0/d9321b483e7e1fca1e0a64d44d24a6d8c9e55c1f01966500a6f8f0e8ee8c0755930d9745c722d4ebcd5daf783fcbf52cd3cc04fb8f2ec46c1799a7af55866ea1 12098 + languageName: node 12099 + linkType: hard 12100 + 12101 + "mjml-wrapper@npm:4.15.3": 12102 + version: 4.15.3 12103 + resolution: "mjml-wrapper@npm:4.15.3" 12104 + dependencies: 12105 + "@babel/runtime": "npm:^7.23.9" 12106 + lodash: "npm:^4.17.21" 12107 + mjml-core: "npm:4.15.3" 12108 + mjml-section: "npm:4.15.3" 12109 + checksum: 10c0/d3b4bbcb16bcacd5c2b7cbb0291c4a947fa0d14b5ba4fca82dc12719746e9bedf385c4030a204c79eca230c1c740b62ead4d5dd30e691c8d10fb31c0af070981 12110 + languageName: node 12111 + linkType: hard 12112 + 12113 + "mjml@npm:^4.15.3": 12114 + version: 4.15.3 12115 + resolution: "mjml@npm:4.15.3" 12116 + dependencies: 12117 + "@babel/runtime": "npm:^7.23.9" 12118 + mjml-cli: "npm:4.15.3" 12119 + mjml-core: "npm:4.15.3" 12120 + mjml-migrate: "npm:4.15.3" 12121 + mjml-preset-core: "npm:4.15.3" 12122 + mjml-validator: "npm:4.15.3" 12123 + bin: 12124 + mjml: bin/mjml 12125 + checksum: 10c0/e4ccd0111dcbf7b9cba0ef899a68747e02cdae3ff125ee628184d594b595649db501076b86ed41f9ecffca1f62c8626d44c5fcca2375e5c83cef7c6e4351a784 12126 + languageName: node 12127 + linkType: hard 12128 + 11400 12129 "mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.4, mkdirp@npm:~0.5.1": 11401 12130 version: 0.5.6 11402 12131 resolution: "mkdirp@npm:0.5.6" ··· 11554 12283 languageName: node 11555 12284 linkType: hard 11556 12285 12286 + "neo-async@npm:^2.6.2": 12287 + version: 2.6.2 12288 + resolution: "neo-async@npm:2.6.2" 12289 + checksum: 10c0/c2f5a604a54a8ec5438a342e1f356dff4bc33ccccdb6dc668d94fe8e5eccfc9d2c2eea6064b0967a767ba63b33763f51ccf2cd2441b461a7322656c1f06b3f5d 12290 + languageName: node 12291 + linkType: hard 12292 + 11557 12293 "netmask@npm:^2.0.2": 11558 12294 version: 2.0.2 11559 12295 resolution: "netmask@npm:2.0.2" ··· 11567 12303 dependencies: 11568 12304 type-fest: "npm:^2.5.1" 11569 12305 checksum: 10c0/9faec009b8b403efbc407f45306d07de5cc58e09df5b00bdd55b01384cd18b0fd29a97aef6915428ba3b5abb0a5c132c3507468c0c3c101e8d737c1337386786 12306 + languageName: node 12307 + linkType: hard 12308 + 12309 + "no-case@npm:^2.2.0": 12310 + version: 2.3.2 12311 + resolution: "no-case@npm:2.3.2" 12312 + dependencies: 12313 + lower-case: "npm:^1.1.1" 12314 + checksum: 10c0/63f306e83c18efa0bb37f1c23a25baf4ccf5ebaec70b482fa04d4c5bf8bbb8bcc9a8fbcd818af828ab69f2b602153daf81ec26e448b2bda2d704b8d0c7eec8fa 11570 12315 languageName: node 11571 12316 linkType: hard 11572 12317 ··· 11597 12342 languageName: node 11598 12343 linkType: hard 11599 12344 11600 - "node-fetch@npm:^2.6.12, node-fetch@npm:^2.6.9": 12345 + "node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.12, node-fetch@npm:^2.6.9": 11601 12346 version: 2.7.0 11602 12347 resolution: "node-fetch@npm:2.7.0" 11603 12348 dependencies: ··· 11691 12436 bin: 11692 12437 nopt: bin/nopt.js 11693 12438 checksum: 10c0/9bd7198df6f16eb29ff16892c77bcf7f0cc41f9fb5c26280ac0def2cf8cf319f3b821b3af83eba0e74c85807cc430a16efe0db58fe6ae1f41e69519f585b6aff 12439 + languageName: node 12440 + linkType: hard 12441 + 12442 + "nopt@npm:^7.2.0": 12443 + version: 7.2.1 12444 + resolution: "nopt@npm:7.2.1" 12445 + dependencies: 12446 + abbrev: "npm:^2.0.0" 12447 + bin: 12448 + nopt: bin/nopt.js 12449 + checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81 11694 12450 languageName: node 11695 12451 linkType: hard 11696 12452 ··· 12075 12831 languageName: node 12076 12832 linkType: hard 12077 12833 12834 + "param-case@npm:^2.1.1": 12835 + version: 2.1.1 12836 + resolution: "param-case@npm:2.1.1" 12837 + dependencies: 12838 + no-case: "npm:^2.2.0" 12839 + checksum: 10c0/8ea1b8472fd51d5f50b28d1d754899713805d05f2241e9b8c4acafa2c500b3f47457a3b4932ab75220f14d2c69180bb7338b78a45576e2b4d90da1e6f0285833 12840 + languageName: node 12841 + linkType: hard 12842 + 12078 12843 "parent-module@npm:^1.0.0": 12079 12844 version: 1.0.1 12080 12845 resolution: "parent-module@npm:1.0.1" ··· 12233 12998 lru-cache: "npm:^10.2.0" 12234 12999 minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" 12235 13000 checksum: 10c0/d723777fbf9627f201e64656680f66ebd940957eebacf780e6cce1c2919c29c116678b2d7dbf8821b3a2caa758d125f4444005ccec886a25c8f324504e48e601 13001 + languageName: node 13002 + linkType: hard 13003 + 13004 + "path-scurry@npm:^1.11.1": 13005 + version: 1.11.1 13006 + resolution: "path-scurry@npm:1.11.1" 13007 + dependencies: 13008 + lru-cache: "npm:^10.2.0" 13009 + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" 13010 + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d 12236 13011 languageName: node 12237 13012 linkType: hard 12238 13013 ··· 13123 13898 hast-util-to-html: "npm:^9.0.0" 13124 13899 unified: "npm:^11.0.0" 13125 13900 checksum: 10c0/6a5f763cfc51cefbcfe504c9661da39b2f28e49c6caea085ff7ad97457b9574289076471f6eeca7af6479f1f90fd1bf1e8d176640b66da262372b76a1f1b3147 13901 + languageName: node 13902 + linkType: hard 13903 + 13904 + "relateurl@npm:^0.2.7": 13905 + version: 0.2.7 13906 + resolution: "relateurl@npm:0.2.7" 13907 + checksum: 10c0/c248b4e3b32474f116a804b537fa6343d731b80056fb506dffd91e737eef4cac6be47a65aae39b522b0db9d0b1011d1a12e288d82a109ecd94a5299d82f6573a 13126 13908 languageName: node 13127 13909 linkType: hard 13128 13910 ··· 14105 14887 languageName: node 14106 14888 linkType: hard 14107 14889 14890 + "slick@npm:^1.12.2": 14891 + version: 1.12.2 14892 + resolution: "slick@npm:1.12.2" 14893 + checksum: 10c0/fea97c36b2bdcd1b80caea150cd8135dc9d3ffe659bbe04fa6f4b4dff373f5d5aef09a8ef384b331c3fdd9567faf447b75b850ab35d2c69ff8a8a92def3d49e1 14894 + languageName: node 14895 + linkType: hard 14896 + 14108 14897 "slug@npm:^9.0.0": 14109 14898 version: 9.0.0 14110 14899 resolution: "slug@npm:9.0.0" ··· 14170 14959 languageName: node 14171 14960 linkType: hard 14172 14961 14173 - "source-map@npm:~0.6.1": 14962 + "source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": 14174 14963 version: 0.6.1 14175 14964 resolution: "source-map@npm:0.6.1" 14176 14965 checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 ··· 15419 16208 languageName: node 15420 16209 linkType: hard 15421 16210 16211 + "uglify-js@npm:^3.1.4, uglify-js@npm:^3.5.1": 16212 + version: 3.17.4 16213 + resolution: "uglify-js@npm:3.17.4" 16214 + bin: 16215 + uglifyjs: bin/uglifyjs 16216 + checksum: 10c0/8b7fcdca69deb284fed7d2025b73eb747ce37f9aca6af53422844f46427152d5440601b6e2a033e77856a2f0591e4167153d5a21b68674ad11f662034ec13ced 16217 + languageName: node 16218 + linkType: hard 16219 + 15422 16220 "unbox-primitive@npm:^1.0.2": 15423 16221 version: 1.0.2 15424 16222 resolution: "unbox-primitive@npm:1.0.2" ··· 15725 16523 languageName: node 15726 16524 linkType: hard 15727 16525 16526 + "upper-case@npm:^1.1.1": 16527 + version: 1.1.3 16528 + resolution: "upper-case@npm:1.1.3" 16529 + checksum: 10c0/3e4d3a90519915bb591db84d72610392518806d8287b8f7541d87642d30388f42b2def1ed2f687e5792ee025e8f7e17d3a0dcbd5b3b59e306ceb1f3b8121ef54 16530 + languageName: node 16531 + linkType: hard 16532 + 15728 16533 "uri-js@npm:^4.2.2": 15729 16534 version: 4.4.1 15730 16535 resolution: "uri-js@npm:4.4.1" ··· 15813 16618 languageName: node 15814 16619 linkType: hard 15815 16620 16621 + "valid-data-url@npm:^3.0.0": 16622 + version: 3.0.1 16623 + resolution: "valid-data-url@npm:3.0.1" 16624 + checksum: 10c0/ffc7cac681976ca2db01003dc14286f75241309e90d96e505580469125c83c2de6b5203f0222226cb08f6daf0aff7de9855655c28a64e8590e7b58c01694a896 16625 + languageName: node 16626 + linkType: hard 16627 + 15816 16628 "validate-npm-package-license@npm:^3.0.1": 15817 16629 version: 3.0.4 15818 16630 resolution: "validate-npm-package-license@npm:3.0.4" ··· 16033 16845 languageName: node 16034 16846 linkType: hard 16035 16847 16848 + "web-resource-inliner@npm:^6.0.1": 16849 + version: 6.0.1 16850 + resolution: "web-resource-inliner@npm:6.0.1" 16851 + dependencies: 16852 + ansi-colors: "npm:^4.1.1" 16853 + escape-goat: "npm:^3.0.0" 16854 + htmlparser2: "npm:^5.0.0" 16855 + mime: "npm:^2.4.6" 16856 + node-fetch: "npm:^2.6.0" 16857 + valid-data-url: "npm:^3.0.0" 16858 + checksum: 10c0/b4b457de2448255100797b1eaefa0f62a8846b2452de8495b9ec17d3e223ebb4848a31b11a645e3541a5b114eb9f201219cda2f99d1b513631777f8c89d1c8a6 16859 + languageName: node 16860 + linkType: hard 16861 + 16036 16862 "web-streams-polyfill@npm:^3.0.3, web-streams-polyfill@npm:^3.2.1": 16037 16863 version: 3.3.3 16038 16864 resolution: "web-streams-polyfill@npm:3.3.3" ··· 16205 17031 dependencies: 16206 17032 execa: "npm:^5.1.1" 16207 17033 checksum: 10c0/934fcd8620fc7cedec6939601c5735a9589d03fa0500874860e13797cc934d48771a97f677d5c162b4c8d72a594bbd522a69b6a1fcd0bc7ff8dfbbdbc1146ba5 17034 + languageName: node 17035 + linkType: hard 17036 + 17037 + "wordwrap@npm:^1.0.0": 17038 + version: 1.0.0 17039 + resolution: "wordwrap@npm:1.0.0" 17040 + checksum: 10c0/7ed2e44f3c33c5c3e3771134d2b0aee4314c9e49c749e37f464bf69f2bcdf0cbf9419ca638098e2717cff4875c47f56a007532f6111c3319f557a2ca91278e92 16208 17041 languageName: node 16209 17042 linkType: hard 16210 17043