@rapidd/core 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +71 -0
- package/.env.example +70 -0
- package/.gitignore +11 -0
- package/LICENSE +15 -0
- package/README.md +231 -0
- package/bin/cli.js +145 -0
- package/config/app.json +166 -0
- package/config/rate-limit.json +12 -0
- package/dist/main.js +26 -0
- package/dockerfile +57 -0
- package/locales/ar_SA.json +179 -0
- package/locales/de_DE.json +179 -0
- package/locales/en_US.json +180 -0
- package/locales/es_ES.json +179 -0
- package/locales/fr_FR.json +179 -0
- package/locales/it_IT.json +179 -0
- package/locales/ja_JP.json +179 -0
- package/locales/pt_BR.json +179 -0
- package/locales/ru_RU.json +179 -0
- package/locales/tr_TR.json +179 -0
- package/main.ts +25 -0
- package/package.json +126 -0
- package/prisma/schema.prisma +9 -0
- package/prisma.config.ts +12 -0
- package/public/static/favicon.ico +0 -0
- package/public/static/image/logo.png +0 -0
- package/routes/api/v1/index.ts +113 -0
- package/src/app.ts +197 -0
- package/src/auth/Auth.ts +446 -0
- package/src/auth/stores/ISessionStore.ts +19 -0
- package/src/auth/stores/MemoryStore.ts +70 -0
- package/src/auth/stores/RedisStore.ts +92 -0
- package/src/auth/stores/index.ts +149 -0
- package/src/config/acl.ts +9 -0
- package/src/config/rls.ts +38 -0
- package/src/core/dmmf.ts +226 -0
- package/src/core/env.ts +183 -0
- package/src/core/errors.ts +87 -0
- package/src/core/i18n.ts +144 -0
- package/src/core/middleware.ts +123 -0
- package/src/core/prisma.ts +236 -0
- package/src/index.ts +112 -0
- package/src/middleware/model.ts +61 -0
- package/src/orm/Model.ts +881 -0
- package/src/orm/QueryBuilder.ts +2078 -0
- package/src/plugins/auth.ts +162 -0
- package/src/plugins/language.ts +79 -0
- package/src/plugins/rateLimit.ts +210 -0
- package/src/plugins/response.ts +80 -0
- package/src/plugins/rls.ts +51 -0
- package/src/plugins/security.ts +23 -0
- package/src/plugins/upload.ts +299 -0
- package/src/types.ts +308 -0
- package/src/utils/ApiClient.ts +526 -0
- package/src/utils/Mailer.ts +348 -0
- package/src/utils/index.ts +25 -0
- package/templates/email/example.ejs +17 -0
- package/templates/layouts/email.ejs +35 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
import ejs from 'ejs';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { LanguageDict } from '../core/i18n';
|
|
6
|
+
|
|
7
|
+
// ── Types ────────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
export interface EmailConfig {
|
|
10
|
+
host: string;
|
|
11
|
+
port: number;
|
|
12
|
+
secure?: boolean;
|
|
13
|
+
user: string;
|
|
14
|
+
password: string;
|
|
15
|
+
from?: string;
|
|
16
|
+
name?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface EmailAttachment {
|
|
20
|
+
filename?: string;
|
|
21
|
+
content?: string | Buffer;
|
|
22
|
+
path?: string;
|
|
23
|
+
href?: string;
|
|
24
|
+
contentType?: string;
|
|
25
|
+
encoding?: string;
|
|
26
|
+
cid?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface EmailOptions {
|
|
30
|
+
to: string | string[];
|
|
31
|
+
subject: string;
|
|
32
|
+
html?: string;
|
|
33
|
+
text?: string;
|
|
34
|
+
cc?: string | string[];
|
|
35
|
+
bcc?: string | string[];
|
|
36
|
+
replyTo?: string;
|
|
37
|
+
attachments?: EmailAttachment[];
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface EmailResult {
|
|
42
|
+
success: boolean;
|
|
43
|
+
messageId?: string;
|
|
44
|
+
error?: Error;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Configuration ────────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
let _config: { emails?: Record<string, EmailConfig> } = { emails: {} };
|
|
50
|
+
let _configLoaded = false;
|
|
51
|
+
|
|
52
|
+
function getConfig(): { emails?: Record<string, EmailConfig> } {
|
|
53
|
+
if (!_configLoaded) {
|
|
54
|
+
try {
|
|
55
|
+
_config = require(path.join(process.cwd(), 'config', 'app.json'));
|
|
56
|
+
} catch {
|
|
57
|
+
_config = { emails: {} };
|
|
58
|
+
}
|
|
59
|
+
_configLoaded = true;
|
|
60
|
+
}
|
|
61
|
+
return _config;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getEmailConfig(configKey: string): EmailConfig {
|
|
65
|
+
const config = getConfig();
|
|
66
|
+
|
|
67
|
+
if (!config.emails || typeof config.emails !== 'object') {
|
|
68
|
+
throw new Error('Email configuration not found in config/app.json');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const emailConfig = config.emails[configKey];
|
|
72
|
+
if (!emailConfig) {
|
|
73
|
+
const available = Object.keys(config.emails).join(', ') || 'none';
|
|
74
|
+
throw new Error(`Email config '${configKey}' not found. Available: ${available}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const required: (keyof EmailConfig)[] = ['host', 'port', 'user', 'password'];
|
|
78
|
+
const missing = required.filter(field => !emailConfig[field]);
|
|
79
|
+
|
|
80
|
+
if (missing.length > 0) {
|
|
81
|
+
throw new Error(`Email config '${configKey}' missing required fields: ${missing.join(', ')}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return emailConfig;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function createTransporter(config: EmailConfig) {
|
|
88
|
+
return nodemailer.createTransport({
|
|
89
|
+
host: config.host,
|
|
90
|
+
port: config.port,
|
|
91
|
+
secure: config.secure ?? config.port === 465,
|
|
92
|
+
auth: {
|
|
93
|
+
user: config.user,
|
|
94
|
+
pass: config.password
|
|
95
|
+
},
|
|
96
|
+
tls: {
|
|
97
|
+
rejectUnauthorized: process.env.NODE_ENV === 'production'
|
|
98
|
+
},
|
|
99
|
+
pool: true,
|
|
100
|
+
maxConnections: 5,
|
|
101
|
+
maxMessages: 100
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── Template Rendering ──────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
const TEMPLATES_PATH = path.join(process.cwd(), 'templates', 'email');
|
|
108
|
+
const LAYOUTS_PATH = path.join(process.cwd(), 'templates', 'layouts');
|
|
109
|
+
const templateCache = new Map<string, string>();
|
|
110
|
+
|
|
111
|
+
function loadTemplate(name: string): string {
|
|
112
|
+
if (templateCache.has(name)) {
|
|
113
|
+
return templateCache.get(name)!;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const filePath = path.join(TEMPLATES_PATH, `${name}.ejs`);
|
|
117
|
+
if (!fs.existsSync(filePath)) {
|
|
118
|
+
throw new Error(`Email template '${name}' not found at ${filePath}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
122
|
+
templateCache.set(name, content);
|
|
123
|
+
return content;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function loadLayout(name: string): string | null {
|
|
127
|
+
const cacheKey = `layout:${name}`;
|
|
128
|
+
if (templateCache.has(cacheKey)) {
|
|
129
|
+
return templateCache.get(cacheKey)!;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const filePath = path.join(LAYOUTS_PATH, `${name}.ejs`);
|
|
133
|
+
if (!fs.existsSync(filePath)) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
138
|
+
templateCache.set(cacheKey, content);
|
|
139
|
+
return content;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function renderTemplate(name: string, data: Record<string, unknown> = {}, layout: string | false = 'email', language?: string): string {
|
|
143
|
+
const template = loadTemplate(name);
|
|
144
|
+
|
|
145
|
+
// Inject __() translation helper into template data
|
|
146
|
+
const lang = language || (data.language as string) || undefined;
|
|
147
|
+
const templateData = {
|
|
148
|
+
...data,
|
|
149
|
+
__: (key: string, params?: Record<string, unknown>) => LanguageDict.get(key, params || null, lang || null),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const body = ejs.render(template, templateData, { filename: path.join(TEMPLATES_PATH, `${name}.ejs`) });
|
|
153
|
+
|
|
154
|
+
// Wrap in layout (default: 'email', pass false to skip)
|
|
155
|
+
if (layout !== false) {
|
|
156
|
+
const layoutContent = loadLayout(layout);
|
|
157
|
+
if (layoutContent) {
|
|
158
|
+
return ejs.render(layoutContent, { ...templateData, body }, { filename: path.join(LAYOUTS_PATH, `${layout}.ejs`) });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return body;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── Validation ──────────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
function validateEmail(email: string): boolean {
|
|
168
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function validateOptions(options: EmailOptions): void {
|
|
172
|
+
if (!options.to) {
|
|
173
|
+
throw new Error('Recipient (to) is required');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (!options.subject) {
|
|
177
|
+
throw new Error('Subject is required');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!options.html && !options.text) {
|
|
181
|
+
throw new Error('Email body (html or text) is required');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const recipients = Array.isArray(options.to) ? options.to : [options.to];
|
|
185
|
+
for (const email of recipients) {
|
|
186
|
+
if (!validateEmail(email)) {
|
|
187
|
+
throw new Error(`Invalid email address: ${email}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ── Main API ─────────────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Config-driven email client with multiple SMTP profiles.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* // Simple email
|
|
199
|
+
* await Mailer.send('default', {
|
|
200
|
+
* to: 'user@example.com',
|
|
201
|
+
* subject: 'Hello',
|
|
202
|
+
* html: '<h1>Welcome!</h1>'
|
|
203
|
+
* });
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* // With attachments
|
|
207
|
+
* await Mailer.send('support', {
|
|
208
|
+
* to: ['user1@example.com', 'user2@example.com'],
|
|
209
|
+
* subject: 'Invoice',
|
|
210
|
+
* html: '<p>Please find your invoice attached.</p>',
|
|
211
|
+
* attachments: [
|
|
212
|
+
* { filename: 'invoice.pdf', path: './invoice.pdf' }
|
|
213
|
+
* ]
|
|
214
|
+
* });
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* // Render EJS template and send
|
|
218
|
+
* await Mailer.sendTemplate('default', 'confirmation', {
|
|
219
|
+
* to: 'user@example.com',
|
|
220
|
+
* subject: 'Confirm your email',
|
|
221
|
+
* data: { name: 'John', confirmationUrl: 'https://...' }
|
|
222
|
+
* });
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* // Render template without sending
|
|
226
|
+
* const html = Mailer.render('resetPassword', { name: 'John', resetUrl: '...' });
|
|
227
|
+
*/
|
|
228
|
+
export const Mailer = {
|
|
229
|
+
/**
|
|
230
|
+
* Send a single email using a configured SMTP profile
|
|
231
|
+
*/
|
|
232
|
+
async send(configKey: string, options: EmailOptions): Promise<EmailResult> {
|
|
233
|
+
try {
|
|
234
|
+
const config = getEmailConfig(configKey);
|
|
235
|
+
validateOptions(options);
|
|
236
|
+
|
|
237
|
+
const transporter = createTransporter(config);
|
|
238
|
+
|
|
239
|
+
const mailOptions = {
|
|
240
|
+
from: config.from || `"${config.name || 'No Reply'}" <${config.user}>`,
|
|
241
|
+
to: options.to,
|
|
242
|
+
subject: options.subject,
|
|
243
|
+
html: options.html,
|
|
244
|
+
text: options.text,
|
|
245
|
+
cc: options.cc,
|
|
246
|
+
bcc: options.bcc,
|
|
247
|
+
replyTo: options.replyTo,
|
|
248
|
+
attachments: options.attachments,
|
|
249
|
+
headers: options.headers
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const info = await transporter.sendMail(mailOptions);
|
|
253
|
+
transporter.close();
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
success: true,
|
|
257
|
+
messageId: info.messageId
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return {
|
|
261
|
+
success: false,
|
|
262
|
+
error: error as Error
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Send multiple emails in parallel
|
|
269
|
+
*/
|
|
270
|
+
async sendBatch(
|
|
271
|
+
configKey: string,
|
|
272
|
+
emails: EmailOptions[]
|
|
273
|
+
): Promise<EmailResult[]> {
|
|
274
|
+
const results = await Promise.allSettled(
|
|
275
|
+
emails.map(options => this.send(configKey, options))
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
return results.map((result) => {
|
|
279
|
+
if (result.status === 'fulfilled') {
|
|
280
|
+
return result.value;
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
success: false,
|
|
284
|
+
error: result.reason
|
|
285
|
+
};
|
|
286
|
+
});
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Verify SMTP connection for a configuration
|
|
291
|
+
*/
|
|
292
|
+
async verify(configKey: string): Promise<boolean> {
|
|
293
|
+
try {
|
|
294
|
+
const config = getEmailConfig(configKey);
|
|
295
|
+
const transporter = createTransporter(config);
|
|
296
|
+
|
|
297
|
+
await transporter.verify();
|
|
298
|
+
transporter.close();
|
|
299
|
+
|
|
300
|
+
return true;
|
|
301
|
+
} catch {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Render an EJS email template from templates/email/
|
|
308
|
+
* Templates have access to __() for translations: <%= __('signIn') %>
|
|
309
|
+
* @param layout - Layout name from templates/layouts/ (default: 'email'), or false to skip layout
|
|
310
|
+
* @param language - Language code for translations (e.g. 'de_DE'), defaults to 'en_US'
|
|
311
|
+
*/
|
|
312
|
+
render(template: string, data: Record<string, unknown> = {}, layout: string | false = 'email', language?: string): string {
|
|
313
|
+
return renderTemplate(template, data, layout, language);
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Render an EJS template and send it as an email
|
|
318
|
+
* Templates have access to __() for translations: <%= __('signIn') %>
|
|
319
|
+
* @param options.layout - Layout name from templates/layouts/ (default: 'email'), or false to skip layout
|
|
320
|
+
* @param options.language - Language code for translations (e.g. 'de_DE'), defaults to 'en_US'
|
|
321
|
+
*/
|
|
322
|
+
async sendTemplate(
|
|
323
|
+
configKey: string,
|
|
324
|
+
template: string,
|
|
325
|
+
options: { to: string | string[]; subject: string; data?: Record<string, unknown>; layout?: string | false; language?: string } & Omit<EmailOptions, 'html'>
|
|
326
|
+
): Promise<EmailResult> {
|
|
327
|
+
const { data = {}, layout = 'email', language, ...emailOptions } = options;
|
|
328
|
+
const html = renderTemplate(template, { ...data, subject: options.subject }, layout, language);
|
|
329
|
+
return this.send(configKey, { ...emailOptions, html });
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Clear the template cache (e.g. after editing templates in dev)
|
|
334
|
+
*/
|
|
335
|
+
clearTemplateCache(): void {
|
|
336
|
+
templateCache.clear();
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* List available email configurations
|
|
341
|
+
*/
|
|
342
|
+
listConfigs(): string[] {
|
|
343
|
+
const config = getConfig();
|
|
344
|
+
return Object.keys(config.emails || {});
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
export default Mailer;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export { ApiClient, ApiClientError } from './ApiClient';
|
|
2
|
+
export type { ServiceConfig, EndpointConfig, AuthConfig, RequestOptions, ApiResponse } from './ApiClient';
|
|
3
|
+
|
|
4
|
+
export { Mailer } from './Mailer';
|
|
5
|
+
export type { EmailConfig, EmailOptions, EmailAttachment, EmailResult } from './Mailer';
|
|
6
|
+
|
|
7
|
+
export const env = {
|
|
8
|
+
isProduction: () => process.env.NODE_ENV === 'production',
|
|
9
|
+
isDevelopment: () => __filename.endsWith('.ts') || process.env.NODE_ENV === 'development',
|
|
10
|
+
isTest: () => process.env.NODE_ENV === 'test',
|
|
11
|
+
current: () => process.env.NODE_ENV || 'development',
|
|
12
|
+
|
|
13
|
+
get: <T extends string | number | boolean>(key: string, defaultValue: T): T => {
|
|
14
|
+
const value = process.env[key];
|
|
15
|
+
if (value === undefined) return defaultValue;
|
|
16
|
+
|
|
17
|
+
if (typeof defaultValue === 'number') {
|
|
18
|
+
return parseInt(value, 10) as T;
|
|
19
|
+
}
|
|
20
|
+
if (typeof defaultValue === 'boolean') {
|
|
21
|
+
return (value.toLowerCase() === 'true') as T;
|
|
22
|
+
}
|
|
23
|
+
return value as T;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<p>Hi <%= name %>,</p>
|
|
2
|
+
|
|
3
|
+
<p>Welcome to <strong><%= appName %></strong>. Your account has been created successfully.</p>
|
|
4
|
+
|
|
5
|
+
<% if (locals.activationLink) { %>
|
|
6
|
+
<p>Please verify your email address to get started:</p>
|
|
7
|
+
<p style="text-align: center; margin: 32px 0;">
|
|
8
|
+
<a href="<%= activationLink %>" class="btn">Verify Email</a>
|
|
9
|
+
</p>
|
|
10
|
+
<% } %>
|
|
11
|
+
|
|
12
|
+
<p>If you have any questions, feel free to reach out to our support team.</p>
|
|
13
|
+
|
|
14
|
+
<p>
|
|
15
|
+
Best regards,<br>
|
|
16
|
+
The <%= appName %> Team
|
|
17
|
+
</p>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title><%= subject %></title>
|
|
7
|
+
<style>
|
|
8
|
+
body { margin: 0; padding: 0; background-color: #f4f4f7; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; }
|
|
9
|
+
.container { max-width: 600px; margin: 0 auto; padding: 40px 20px; }
|
|
10
|
+
.card { background: #ffffff; border-radius: 8px; padding: 40px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
|
|
11
|
+
.header { text-align: center; margin-bottom: 32px; }
|
|
12
|
+
.header h1 { margin: 0; font-size: 24px; color: #1a1a2e; }
|
|
13
|
+
.content { color: #333; font-size: 16px; line-height: 1.6; }
|
|
14
|
+
.content p { margin: 0 0 16px; }
|
|
15
|
+
.btn { display: inline-block; padding: 12px 32px; background-color: #1a1a2e; color: #ffffff; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 14px; }
|
|
16
|
+
.footer { text-align: center; margin-top: 32px; font-size: 13px; color: #999; }
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<div class="container">
|
|
21
|
+
<div class="card">
|
|
22
|
+
<div class="header">
|
|
23
|
+
<% if (locals.logoUrl) { %><img src="<%= logoUrl %>" alt="" height="40" style="margin-bottom: 16px;"><% } %>
|
|
24
|
+
<h1><%= subject %></h1>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="content">
|
|
27
|
+
<%- body %>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="footer">
|
|
31
|
+
<% if (locals.footerText) { %><%= footerText %><% } %>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</body>
|
|
35
|
+
</html>
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2024",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2024"],
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": ".",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"declarationMap": true,
|
|
13
|
+
"sourceMap": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"noUnusedLocals": false,
|
|
18
|
+
"noUnusedParameters": false,
|
|
19
|
+
"noImplicitReturns": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true
|
|
21
|
+
},
|
|
22
|
+
"include": [
|
|
23
|
+
"src/**/*.ts",
|
|
24
|
+
"main.ts",
|
|
25
|
+
"routes/**/*.ts"
|
|
26
|
+
],
|
|
27
|
+
"exclude": [
|
|
28
|
+
"node_modules",
|
|
29
|
+
"dist",
|
|
30
|
+
"__test__",
|
|
31
|
+
"**/*.js"
|
|
32
|
+
]
|
|
33
|
+
}
|