fix security issues with smtp
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build": "tsc",
|
"build": "tsup",
|
||||||
"start": "node dist/server.js"
|
"start": "node dist/server.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.1.0",
|
"@types/node": "^22.1.0",
|
||||||
"@types/nodemailer": "^6.4.15",
|
"@types/nodemailer": "^6.4.15",
|
||||||
|
"tsup": "^8.0.2",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,98 +1,130 @@
|
|||||||
import { SMTPServer, SMTPServerOptions } from 'smtp-server';
|
import { SMTPServer, SMTPServerOptions, SMTPServerSession } from "smtp-server";
|
||||||
import { Readable } from 'stream';
|
import { Readable } from "stream";
|
||||||
import dotenv from 'dotenv';
|
import dotenv from "dotenv";
|
||||||
import { simpleParser } from 'mailparser';
|
import { simpleParser } from "mailparser";
|
||||||
|
import { readFileSync } from "fs";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const AUTH_USERNAME = process.env.SMTP_AUTH_USERNAME!;
|
const AUTH_USERNAME = process.env.SMTP_AUTH_USERNAME ?? "unsend";
|
||||||
const UNSEND_BASE_URL = process.env.UNSEND_BASE_URL!;
|
const UNSEND_BASE_URL = process.env.UNSEND_BASE_URL ?? "https://app.unsend.dev";
|
||||||
let API_KEY = '';
|
const SSL_KEY_PATH = process.env.UNSEND_API_KEY_PATH;
|
||||||
|
const SSL_CERT_PATH = process.env.UNSEND_API_CERT_PATH;
|
||||||
|
|
||||||
const ports = [25, 465, 2465, 587, 2587]; // Array of ports to listen on
|
async function sendEmailToUnsend(emailData: any, apiKey: string) {
|
||||||
|
|
||||||
const serverOptions: SMTPServerOptions = {
|
|
||||||
onData(stream: Readable, session: any, callback: (error?: Error) => void) {
|
|
||||||
console.log('Receiving email data...'); // Debug statement
|
|
||||||
simpleParser(stream, (err, parsed) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('Failed to parse email data:', err.message);
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const emailObject: any = {
|
|
||||||
to: Array.isArray(parsed.to) ? parsed.to.map(addr => addr.text).join(', ') : parsed.to?.text,
|
|
||||||
from: Array.isArray(parsed.from) ? parsed.from.map(addr => addr.text).join(', ') : parsed.from?.text,
|
|
||||||
subject: parsed.subject,
|
|
||||||
text: parsed.text,
|
|
||||||
html: parsed.html,
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('Parsed email data:', emailObject); // Debug statement
|
|
||||||
|
|
||||||
sendEmailToUnsend(emailObject)
|
|
||||||
.then(() => callback())
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('Failed to send email:', error.message);
|
|
||||||
callback(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onAuth(auth: any, session: any, callback: (error?: Error, user?: any) => void) {
|
|
||||||
API_KEY = auth.password;
|
|
||||||
if (auth.username === AUTH_USERNAME) {
|
|
||||||
console.log('Authenticated successfully'); // Debug statement
|
|
||||||
callback(undefined, { user: AUTH_USERNAME });
|
|
||||||
} else {
|
|
||||||
console.error('Invalid username or password');
|
|
||||||
callback(new Error('Invalid username or password'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
async function sendEmailToUnsend(emailData: any) {
|
|
||||||
try {
|
try {
|
||||||
const apiEndpoint = '/api/v1/emails';
|
const apiEndpoint = "/api/v1/emails";
|
||||||
const url = new URL(apiEndpoint, UNSEND_BASE_URL); // Combine base URL with endpoint
|
const url = new URL(apiEndpoint, UNSEND_BASE_URL); // Combine base URL with endpoint
|
||||||
console.log('Sending email to Unsend API at:', url.href); // Debug statement
|
console.log("Sending email to Unsend API at:", url.href); // Debug statement
|
||||||
|
|
||||||
const response = await fetch(url.href, {
|
const response = await fetch(url.href, {
|
||||||
method: 'POST',
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${API_KEY}`,
|
Authorization: `Bearer ${apiKey}`,
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(emailData),
|
body: JSON.stringify(emailData),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json();
|
const errorData = await response.json();
|
||||||
console.error('Unsend API error response:', errorData);
|
console.error("Unsend API error response:", errorData);
|
||||||
throw new Error(`Failed to send email: ${errorData.message || 'Unknown error from server'}`);
|
throw new Error(
|
||||||
|
`Failed to send email: ${errorData.error.message || "Unknown error from server"}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
console.log('Unsend API response:', responseData);
|
console.log("Unsend API response:", responseData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
console.error('Error message:', error.message);
|
console.error("Error message:", error.message);
|
||||||
throw new Error(`Failed to send email: ${error.message}`);
|
throw new Error(`Failed to send email: ${error.message}`);
|
||||||
} else {
|
} else {
|
||||||
console.error('Unexpected error:', error);
|
console.error("Unexpected error:", error);
|
||||||
throw new Error('Failed to send email: Unexpected error occurred');
|
throw new Error("Failed to send email: Unexpected error occurred");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const serverOptions: SMTPServerOptions = {
|
||||||
|
secure: false,
|
||||||
|
key: SSL_KEY_PATH ? readFileSync(SSL_KEY_PATH) : undefined,
|
||||||
|
cert: SSL_CERT_PATH ? readFileSync(SSL_CERT_PATH) : undefined,
|
||||||
|
onData(
|
||||||
|
stream: Readable,
|
||||||
|
session: SMTPServerSession,
|
||||||
|
callback: (error?: Error) => void
|
||||||
|
) {
|
||||||
|
console.log("Receiving email data..."); // Debug statement
|
||||||
|
simpleParser(stream, (err, parsed) => {
|
||||||
|
if (err) {
|
||||||
|
console.error("Failed to parse email data:", err.message);
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session.user) {
|
||||||
|
console.error("No API key found in session");
|
||||||
|
return callback(new Error("No API key found in session"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailObject = {
|
||||||
|
to: Array.isArray(parsed.to)
|
||||||
|
? parsed.to.map((addr) => addr.text).join(", ")
|
||||||
|
: parsed.to?.text,
|
||||||
|
from: Array.isArray(parsed.from)
|
||||||
|
? parsed.from.map((addr) => addr.text).join(", ")
|
||||||
|
: parsed.from?.text,
|
||||||
|
subject: parsed.subject,
|
||||||
|
text: parsed.text,
|
||||||
|
html: parsed.html,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Parsed email data:", emailObject); // Debug statement
|
||||||
|
|
||||||
|
sendEmailToUnsend(emailObject, session.user)
|
||||||
|
.then(() => callback())
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Failed to send email:", error.message);
|
||||||
|
callback(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onAuth(auth, session: any, callback: (error?: Error, user?: any) => void) {
|
||||||
|
if (auth.username === AUTH_USERNAME && auth.password) {
|
||||||
|
console.log("Authenticated successfully"); // Debug statement
|
||||||
|
callback(undefined, { user: auth.password });
|
||||||
|
} else {
|
||||||
|
console.error("Invalid username or password");
|
||||||
|
callback(new Error("Invalid username or password"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
size: 10485760,
|
||||||
|
};
|
||||||
|
|
||||||
function startServers() {
|
function startServers() {
|
||||||
ports.forEach(port => {
|
// Implicit SSL/TLS for ports 465 and 2465
|
||||||
|
[465, 2465].forEach((port) => {
|
||||||
|
const server = new SMTPServer({ ...serverOptions, secure: true });
|
||||||
|
|
||||||
|
server.listen(port, () => {
|
||||||
|
console.log(`Implicit SSL/TLS SMTP server is listening on port ${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("error", (err) => {
|
||||||
|
console.error(`Error occurred on port ${port}:`, err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// STARTTLS for ports 25, 587, and 2587
|
||||||
|
[25, 587, 2587].forEach((port) => {
|
||||||
const server = new SMTPServer(serverOptions);
|
const server = new SMTPServer(serverOptions);
|
||||||
|
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
console.log(`SMTP server is listening on port ${port}`);
|
console.log(`STARTTLS SMTP server is listening on port ${port}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
server.on('error', (err) => {
|
server.on("error", (err) => {
|
||||||
console.error(`Error occurred on port ${port}:`, err);
|
console.error(`Error occurred on port ${port}:`, err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
const nodemailer = require('nodemailer');
|
const nodemailer = require("nodemailer");
|
||||||
|
|
||||||
const transporter = nodemailer.createTransport({
|
const transporter = nodemailer.createTransport({
|
||||||
host: "localhost",
|
host: "localhost",
|
||||||
@@ -10,10 +10,8 @@ const transporter = nodemailer.createTransport({
|
|||||||
},
|
},
|
||||||
tls: {
|
tls: {
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
to: "harsh121102@gmail.com",
|
to: "harsh121102@gmail.com",
|
||||||
@@ -23,15 +21,10 @@ const mailOptions = {
|
|||||||
text: "hello,\n\nUnsend is the best open source sending platform",
|
text: "hello,\n\nUnsend is the best open source sending platform",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
transporter.sendMail(mailOptions, (error, info) => {
|
transporter.sendMail(mailOptions, (error, info) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error sending email:', error);
|
console.error("Error sending email:", error);
|
||||||
} else {
|
} else {
|
||||||
console.log('Email sent successfully:', info.response);
|
console.log("Email sent successfully:", info.response);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
13
apps/smtp-server/tsup.config.ts
Normal file
13
apps/smtp-server/tsup.config.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
import { defineConfig, Options } from "tsup";
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
export default defineConfig((options: Options) => ({
|
||||||
|
entry: ["src/server.ts"],
|
||||||
|
format: ["cjs"],
|
||||||
|
dts: true,
|
||||||
|
minify: true,
|
||||||
|
clean: true,
|
||||||
|
injectStyle: true,
|
||||||
|
...options,
|
||||||
|
}));
|
@@ -6,6 +6,7 @@
|
|||||||
"build:editor": "turbo build --filter=@unsend/email-editor",
|
"build:editor": "turbo build --filter=@unsend/email-editor",
|
||||||
"build:web": "turbo build --filter=@unsend/email-editor --filter=web ",
|
"build:web": "turbo build --filter=@unsend/email-editor --filter=web ",
|
||||||
"build:marketing": "turbo build --filter=@unsend/email-editor --filter=marketing",
|
"build:marketing": "turbo build --filter=@unsend/email-editor --filter=marketing",
|
||||||
|
"build:smtp": "turbo build --filter=smtp-server",
|
||||||
"dev": "pnpm load-env -- turbo dev",
|
"dev": "pnpm load-env -- turbo dev",
|
||||||
"dev:docs": "cd apps/docs && mintlify dev",
|
"dev:docs": "cd apps/docs && mintlify dev",
|
||||||
"lint": "turbo lint",
|
"lint": "turbo lint",
|
||||||
|
69
pnpm-lock.yaml
generated
69
pnpm-lock.yaml
generated
@@ -122,6 +122,9 @@ importers:
|
|||||||
'@types/nodemailer':
|
'@types/nodemailer':
|
||||||
specifier: ^6.4.15
|
specifier: ^6.4.15
|
||||||
version: 6.4.15
|
version: 6.4.15
|
||||||
|
tsup:
|
||||||
|
specifier: ^8.0.2
|
||||||
|
version: 8.0.2(typescript@5.5.4)
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.5.4
|
specifier: ^5.5.4
|
||||||
version: 5.5.4
|
version: 5.5.4
|
||||||
@@ -7493,12 +7496,13 @@ packages:
|
|||||||
/balanced-match@1.0.2:
|
/balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
|
/balanced-match@2.0.0:
|
||||||
|
resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/base32.js@0.1.0:
|
/base32.js@0.1.0:
|
||||||
resolution: {integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==}
|
resolution: {integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==}
|
||||||
engines: {node: '>=0.12.0'}
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
/balanced-match@2.0.0:
|
|
||||||
resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/base64-js@1.5.1:
|
/base64-js@1.5.1:
|
||||||
@@ -11491,6 +11495,12 @@ packages:
|
|||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/magic-string@0.30.10:
|
||||||
|
resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
|
dev: false
|
||||||
|
|
||||||
/mailparser@3.7.1:
|
/mailparser@3.7.1:
|
||||||
resolution: {integrity: sha512-RCnBhy5q8XtB3mXzxcAfT1huNqN93HTYYyL6XawlIKycfxM/rXPg9tXoZ7D46+SgCS1zxKzw+BayDQSvncSTTw==}
|
resolution: {integrity: sha512-RCnBhy5q8XtB3mXzxcAfT1huNqN93HTYYyL6XawlIKycfxM/rXPg9tXoZ7D46+SgCS1zxKzw+BayDQSvncSTTw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11512,10 +11522,6 @@ packages:
|
|||||||
libbase64: 1.2.1
|
libbase64: 1.2.1
|
||||||
libmime: 5.2.0
|
libmime: 5.2.0
|
||||||
libqp: 2.0.1
|
libqp: 2.0.1
|
||||||
/magic-string@0.30.10:
|
|
||||||
resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
|
|
||||||
dependencies:
|
|
||||||
'@jridgewell/sourcemap-codec': 1.4.15
|
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/map-age-cleaner@0.1.3:
|
/map-age-cleaner@0.1.3:
|
||||||
@@ -13764,7 +13770,6 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
|
||||||
/punycode@1.4.1:
|
/punycode@1.4.1:
|
||||||
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
|
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -15491,10 +15496,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/tlds@1.252.0:
|
|
||||||
resolution: {integrity: sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
/tippy.js@6.3.7:
|
/tippy.js@6.3.7:
|
||||||
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
|
resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -15510,6 +15511,11 @@ packages:
|
|||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/tlds@1.252.0:
|
||||||
|
resolution: {integrity: sha512-GA16+8HXvqtfEnw/DTcwB0UU354QE1n3+wh08oFjr6Znl7ZLAeUgYzCcK+/CCrOyE0vnHR8/pu3XXG3vDijXpQ==}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tldts-core@6.1.16:
|
/tldts-core@6.1.16:
|
||||||
resolution: {integrity: sha512-rxnuCux+zn3hMF57nBzr1m1qGZH7Od2ErbDZjVm04fk76cEynTg3zqvHjx5BsBl8lvRTjpzIhsEGMHDH/Hr2Vw==}
|
resolution: {integrity: sha512-rxnuCux+zn3hMF57nBzr1m1qGZH7Od2ErbDZjVm04fk76cEynTg3zqvHjx5BsBl8lvRTjpzIhsEGMHDH/Hr2Vw==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -15662,6 +15668,45 @@ packages:
|
|||||||
- ts-node
|
- ts-node
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/tsup@8.0.2(typescript@5.5.4):
|
||||||
|
resolution: {integrity: sha512-NY8xtQXdH7hDUAZwcQdY/Vzlw9johQsaqf7iwZ6g1DOUlFYQ5/AtVAjTvihhEyeRlGo4dLRVHtrRaL35M1daqQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@microsoft/api-extractor': ^7.36.0
|
||||||
|
'@swc/core': ^1
|
||||||
|
postcss: ^8.4.12
|
||||||
|
typescript: '>=4.5.0'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@microsoft/api-extractor':
|
||||||
|
optional: true
|
||||||
|
'@swc/core':
|
||||||
|
optional: true
|
||||||
|
postcss:
|
||||||
|
optional: true
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
bundle-require: 4.1.0(esbuild@0.19.12)
|
||||||
|
cac: 6.7.14
|
||||||
|
chokidar: 3.6.0
|
||||||
|
debug: 4.3.4
|
||||||
|
esbuild: 0.19.12
|
||||||
|
execa: 5.1.1
|
||||||
|
globby: 11.1.0
|
||||||
|
joycon: 3.1.1
|
||||||
|
postcss-load-config: 4.0.2(postcss@8.4.38)
|
||||||
|
resolve-from: 5.0.0
|
||||||
|
rollup: 4.18.0
|
||||||
|
source-map: 0.8.0-beta.0
|
||||||
|
sucrase: 3.35.0
|
||||||
|
tree-kill: 1.2.2
|
||||||
|
typescript: 5.5.4
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
- ts-node
|
||||||
|
dev: true
|
||||||
|
|
||||||
/tsutils@3.21.0(typescript@5.4.2):
|
/tsutils@3.21.0(typescript@5.4.2):
|
||||||
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
Reference in New Issue
Block a user