@studious-lms/server 1.0.2 → 1.0.4

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.
Files changed (75) hide show
  1. package/dist/routers/_app.d.ts +216 -88
  2. package/dist/routers/_app.d.ts.map +1 -1
  3. package/dist/routers/agenda.d.ts +5 -4
  4. package/dist/routers/agenda.d.ts.map +1 -1
  5. package/dist/routers/announcement.d.ts +8 -4
  6. package/dist/routers/announcement.d.ts.map +1 -1
  7. package/dist/routers/assignment.d.ts +20 -4
  8. package/dist/routers/assignment.d.ts.map +1 -1
  9. package/dist/routers/attendance.d.ts +6 -4
  10. package/dist/routers/attendance.d.ts.map +1 -1
  11. package/dist/routers/auth.d.ts +10 -4
  12. package/dist/routers/auth.d.ts.map +1 -1
  13. package/dist/routers/class.d.ts +26 -4
  14. package/dist/routers/class.d.ts.map +1 -1
  15. package/dist/routers/event.d.ts +11 -4
  16. package/dist/routers/event.d.ts.map +1 -1
  17. package/dist/routers/file.d.ts +5 -4
  18. package/dist/routers/file.d.ts.map +1 -1
  19. package/dist/routers/section.d.ts +7 -4
  20. package/dist/routers/section.d.ts.map +1 -1
  21. package/dist/routers/user.d.ts +6 -4
  22. package/dist/routers/user.d.ts.map +1 -1
  23. package/dist/trpc.d.ts +43 -90
  24. package/dist/trpc.d.ts.map +1 -1
  25. package/package.json +2 -7
  26. package/prisma/schema.prisma +228 -0
  27. package/src/exportType.ts +9 -0
  28. package/src/index.ts +94 -0
  29. package/src/lib/fileUpload.ts +163 -0
  30. package/src/lib/googleCloudStorage.ts +94 -0
  31. package/src/lib/prisma.ts +16 -0
  32. package/src/lib/thumbnailGenerator.ts +185 -0
  33. package/src/logger.ts +163 -0
  34. package/src/middleware/auth.ts +191 -0
  35. package/src/middleware/logging.ts +54 -0
  36. package/src/routers/_app.ts +34 -0
  37. package/src/routers/agenda.ts +79 -0
  38. package/src/routers/announcement.ts +134 -0
  39. package/src/routers/assignment.ts +1614 -0
  40. package/src/routers/attendance.ts +284 -0
  41. package/src/routers/auth.ts +286 -0
  42. package/src/routers/class.ts +753 -0
  43. package/src/routers/event.ts +509 -0
  44. package/src/routers/file.ts +96 -0
  45. package/src/routers/section.ts +138 -0
  46. package/src/routers/user.ts +82 -0
  47. package/src/socket/handlers.ts +143 -0
  48. package/src/trpc.ts +90 -0
  49. package/src/types/trpc.ts +15 -0
  50. package/src/utils/email.ts +11 -0
  51. package/src/utils/generateInviteCode.ts +8 -0
  52. package/src/utils/logger.ts +156 -0
  53. package/tsconfig.json +17 -0
  54. package/generated/prisma/client.d.ts +0 -1
  55. package/generated/prisma/client.js +0 -4
  56. package/generated/prisma/default.d.ts +0 -1
  57. package/generated/prisma/default.js +0 -4
  58. package/generated/prisma/edge.d.ts +0 -1
  59. package/generated/prisma/edge.js +0 -389
  60. package/generated/prisma/index-browser.js +0 -375
  61. package/generated/prisma/index.d.ts +0 -34865
  62. package/generated/prisma/index.js +0 -410
  63. package/generated/prisma/libquery_engine-darwin-arm64.dylib.node +0 -0
  64. package/generated/prisma/package.json +0 -140
  65. package/generated/prisma/runtime/edge-esm.js +0 -34
  66. package/generated/prisma/runtime/edge.js +0 -34
  67. package/generated/prisma/runtime/index-browser.d.ts +0 -370
  68. package/generated/prisma/runtime/index-browser.js +0 -16
  69. package/generated/prisma/runtime/library.d.ts +0 -3647
  70. package/generated/prisma/runtime/library.js +0 -146
  71. package/generated/prisma/runtime/react-native.js +0 -83
  72. package/generated/prisma/runtime/wasm.js +0 -35
  73. package/generated/prisma/schema.prisma +0 -304
  74. package/generated/prisma/wasm.d.ts +0 -1
  75. package/generated/prisma/wasm.js +0 -375
@@ -0,0 +1,138 @@
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure } from "../trpc";
3
+ import { TRPCError } from "@trpc/server";
4
+ import { prisma } from "../lib/prisma";
5
+
6
+ const createSectionSchema = z.object({
7
+ classId: z.string(),
8
+ name: z.string(),
9
+ });
10
+
11
+ const updateSectionSchema = z.object({
12
+ id: z.string(),
13
+ classId: z.string(),
14
+ name: z.string(),
15
+ });
16
+
17
+ const deleteSectionSchema = z.object({
18
+ id: z.string(),
19
+ classId: z.string(),
20
+ });
21
+
22
+ export const sectionRouter = createTRPCRouter({
23
+ create: protectedProcedure
24
+ .input(createSectionSchema)
25
+ .mutation(async ({ ctx, input }) => {
26
+ if (!ctx.user) {
27
+ throw new TRPCError({
28
+ code: "UNAUTHORIZED",
29
+ message: "User must be authenticated",
30
+ });
31
+ }
32
+
33
+ // Verify user is a teacher of the class
34
+ const classData = await prisma.class.findFirst({
35
+ where: {
36
+ id: input.classId,
37
+ teachers: {
38
+ some: {
39
+ id: ctx.user.id,
40
+ },
41
+ },
42
+ },
43
+ });
44
+
45
+ if (!classData) {
46
+ throw new TRPCError({
47
+ code: "NOT_FOUND",
48
+ message: "Class not found or you are not a teacher",
49
+ });
50
+ }
51
+
52
+ const section = await prisma.section.create({
53
+ data: {
54
+ name: input.name,
55
+ class: {
56
+ connect: { id: input.classId },
57
+ },
58
+ },
59
+ });
60
+
61
+ return section;
62
+ }),
63
+
64
+ update: protectedProcedure
65
+ .input(updateSectionSchema)
66
+ .mutation(async ({ ctx, input }) => {
67
+ if (!ctx.user) {
68
+ throw new TRPCError({
69
+ code: "UNAUTHORIZED",
70
+ message: "User must be authenticated",
71
+ });
72
+ }
73
+
74
+ // Verify user is a teacher of the class
75
+ const classData = await prisma.class.findFirst({
76
+ where: {
77
+ id: input.classId,
78
+ teachers: {
79
+ some: {
80
+ id: ctx.user.id,
81
+ },
82
+ },
83
+ },
84
+ });
85
+
86
+ if (!classData) {
87
+ throw new TRPCError({
88
+ code: "NOT_FOUND",
89
+ message: "Class not found or you are not a teacher",
90
+ });
91
+ }
92
+
93
+ const section = await prisma.section.update({
94
+ where: { id: input.id },
95
+ data: {
96
+ name: input.name,
97
+ },
98
+ });
99
+
100
+ return section;
101
+ }),
102
+
103
+ delete: protectedProcedure
104
+ .input(deleteSectionSchema)
105
+ .mutation(async ({ ctx, input }) => {
106
+ if (!ctx.user) {
107
+ throw new TRPCError({
108
+ code: "UNAUTHORIZED",
109
+ message: "User must be authenticated",
110
+ });
111
+ }
112
+
113
+ // Verify user is a teacher of the class
114
+ const classData = await prisma.class.findFirst({
115
+ where: {
116
+ id: input.classId,
117
+ teachers: {
118
+ some: {
119
+ id: ctx.user.id,
120
+ },
121
+ },
122
+ },
123
+ });
124
+
125
+ if (!classData) {
126
+ throw new TRPCError({
127
+ code: "NOT_FOUND",
128
+ message: "Class not found or you are not a teacher",
129
+ });
130
+ }
131
+
132
+ await prisma.section.delete({
133
+ where: { id: input.id },
134
+ });
135
+
136
+ return { id: input.id };
137
+ }),
138
+ });
@@ -0,0 +1,82 @@
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure } from "../trpc";
3
+ import { TRPCError } from "@trpc/server";
4
+ import { prisma } from "../lib/prisma";
5
+ import { uploadFiles, type UploadedFile } from "../lib/fileUpload";
6
+
7
+ const fileSchema = z.object({
8
+ name: z.string(),
9
+ type: z.string(),
10
+ size: z.number(),
11
+ data: z.string(), // base64 encoded file data
12
+ });
13
+
14
+ const updateProfileSchema = z.object({
15
+ profile: z.record(z.any()),
16
+ profilePicture: fileSchema.optional(),
17
+ });
18
+
19
+ export const userRouter = createTRPCRouter({
20
+ getProfile: protectedProcedure
21
+ .query(async ({ ctx }) => {
22
+ if (!ctx.user) {
23
+ throw new TRPCError({
24
+ code: "UNAUTHORIZED",
25
+ message: "User must be authenticated",
26
+ });
27
+ }
28
+
29
+ const user = await prisma.user.findUnique({
30
+ where: { id: ctx.user.id },
31
+ select: {
32
+ id: true,
33
+ username: true,
34
+ profile: true,
35
+ },
36
+ });
37
+
38
+ if (!user) {
39
+ throw new TRPCError({
40
+ code: "NOT_FOUND",
41
+ message: "User not found",
42
+ });
43
+ }
44
+
45
+ return user;
46
+ }),
47
+
48
+ updateProfile: protectedProcedure
49
+ .input(updateProfileSchema)
50
+ .mutation(async ({ ctx, input }) => {
51
+ if (!ctx.user) {
52
+ throw new TRPCError({
53
+ code: "UNAUTHORIZED",
54
+ message: "User must be authenticated",
55
+ });
56
+ }
57
+
58
+ let uploadedFiles: UploadedFile[] = [];
59
+ if (input.profilePicture) {
60
+ // Store profile picture in a user-specific directory
61
+ uploadedFiles = await uploadFiles([input.profilePicture], ctx.user.id, `users/${ctx.user.id}/profile`);
62
+
63
+ // Add profile picture path to profile data
64
+ input.profile.profilePicture = uploadedFiles[0].path;
65
+ input.profile.profilePictureThumbnail = uploadedFiles[0].thumbnailId;
66
+ }
67
+
68
+ const updatedUser = await prisma.user.update({
69
+ where: { id: ctx.user.id },
70
+ data: {
71
+ profile: input.profile,
72
+ },
73
+ select: {
74
+ id: true,
75
+ username: true,
76
+ profile: true,
77
+ },
78
+ });
79
+
80
+ return updatedUser;
81
+ }),
82
+ });
@@ -0,0 +1,143 @@
1
+ import { Socket, Server } from 'socket.io';
2
+ import { logger } from '../utils/logger';
3
+
4
+ export const setupSocketHandlers = (io: Server) => {
5
+ io.on('connection', (socket: Socket) => {
6
+ logger.info('Client connected', { socketId: socket.id });
7
+
8
+ socket.on('disconnect', (reason) => {
9
+ logger.info('Client disconnected', { socketId: socket.id, reason });
10
+ });
11
+
12
+ socket.on('error', (error) => {
13
+ logger.error('Socket error', { socketId: socket.id, error });
14
+ });
15
+
16
+ // Class Room Management
17
+ socket.on('create-class', (data: { class: any }) => {
18
+ io.emit('class-created', data.class);
19
+ logger.info('Class created', { class: data.class });
20
+ });
21
+
22
+ socket.on('delete-class', (data: { classId: string }) => {
23
+ io.emit('class-deleted', data.classId);
24
+ logger.info('Class deleted', { classId: data.classId });
25
+ });
26
+
27
+ socket.on('update-class', (data: { class: any }) => {
28
+ io.emit('class-updated', data.class);
29
+ logger.info('Class updated', { class: data.class });
30
+ });
31
+
32
+ socket.on('join-class', (classId: string, callback) => {
33
+ try {
34
+ socket.join(`class-${classId}`);
35
+ logger.info('Client joined class room', { socketId: socket.id, classId });
36
+
37
+ if (callback) {
38
+ callback(classId);
39
+ } else {
40
+ socket.emit('joined-class', classId);
41
+ }
42
+ } catch (error) {
43
+ logger.error('Error joining class room', { socketId: socket.id, classId, error });
44
+ if (callback) {
45
+ callback(null);
46
+ }
47
+ }
48
+ });
49
+
50
+ // Assignment Events
51
+ socket.on('assignment-create', (data: { classId: string, assignment: any }) => {
52
+ if (data.classId && data.assignment) {
53
+ io.in(`class-${data.classId}`).emit('assignment-created', data.assignment);
54
+ logger.info('Assignment created', { classId: data.classId, assignmentId: data.assignment.id });
55
+ } else {
56
+ logger.error('Invalid assignment data format', { data });
57
+ }
58
+ });
59
+
60
+ socket.on('assignment-update', (data: { classId: string, assignment: any }) => {
61
+ io.in(`class-${data.classId}`).emit('assignment-updated', data.assignment);
62
+ logger.info('Assignment updated', { classId: data.classId, assignmentId: data.assignment.id });
63
+ });
64
+
65
+ socket.on('assignment-delete', (data: { classId: string, assignmentId: string }) => {
66
+ io.in(`class-${data.classId}`).emit('assignment-deleted', data.assignmentId);
67
+ logger.info('Assignment deleted', { classId: data.classId, assignmentId: data.assignmentId });
68
+ });
69
+
70
+ // Submission Events
71
+ socket.on('submission-update', (data: { classId: string, submission: any }) => {
72
+ io.in(`class-${data.classId}`).emit('submission-updated', data.submission);
73
+ logger.info('Submission updated', { classId: data.classId, submissionId: data.submission.id });
74
+ });
75
+
76
+ // Announcement Events
77
+ socket.on('new-announcement', (data: { classId: string, announcement: any }) => {
78
+ io.in(`class-${data.classId}`).emit('announcement-created', data.announcement);
79
+ logger.info('New announcement created', { classId: data.classId, announcementId: data.announcement.id });
80
+ });
81
+
82
+ // Section Events
83
+ socket.on('section-create', (data: { classId: string, section: any }) => {
84
+ io.in(`class-${data.classId}`).emit('section-created', data.section);
85
+ logger.info('Section created', { classId: data.classId, sectionId: data.section.id });
86
+ });
87
+
88
+ socket.on('section-update', (data: { classId: string, section: any }) => {
89
+ io.in(`class-${data.classId}`).emit('section-updated', data.section);
90
+ logger.info('Section updated', { classId: data.classId, sectionId: data.section.id });
91
+ });
92
+
93
+ socket.on('section-delete', (data: { classId: string, sectionId: string }) => {
94
+ io.in(`class-${data.classId}`).emit('section-deleted', data.sectionId);
95
+ logger.info('Section deleted', { classId: data.classId, sectionId: data.sectionId });
96
+ });
97
+
98
+ // Member Events
99
+ socket.on('member-update', (data: { classId: string, member: any }) => {
100
+ io.in(`class-${data.classId}`).emit('member-updated', data.member);
101
+ logger.info('Member updated', { classId: data.classId, memberId: data.member.id });
102
+ });
103
+
104
+ socket.on('member-delete', (data: { classId: string, memberId: string }) => {
105
+ io.in(`class-${data.classId}`).emit('member-deleted', data.memberId);
106
+ logger.info('Member deleted', { classId: data.classId, memberId: data.memberId });
107
+ });
108
+
109
+ // Attendance Events
110
+ socket.on('attendance-update', (data: { classId: string, attendance: any }) => {
111
+ io.in(`class-${data.classId}`).emit('attendance-updated', data.attendance);
112
+ logger.info('Attendance updated', { classId: data.classId, attendanceId: data.attendance.id });
113
+ });
114
+
115
+ // Event Events
116
+ socket.on('event-create', (data: { classId: string, event: any }) => {
117
+ if (data.classId) {
118
+ io.in(`class-${data.classId}`).emit('event-created', data.event);
119
+ } else {
120
+ io.emit('event-created', data.event);
121
+ }
122
+ logger.info('Event created', { classId: data.classId, eventId: data.event.id });
123
+ });
124
+
125
+ socket.on('event-update', (data: { classId: string, event: any }) => {
126
+ if (data.classId) {
127
+ io.in(`class-${data.classId}`).emit('event-updated', data.event);
128
+ } else {
129
+ io.emit('event-updated', data.event);
130
+ }
131
+ logger.info('Event updated', { classId: data.classId, eventId: data.event.id });
132
+ });
133
+
134
+ socket.on('event-delete', (data: { classId: string, eventId: string }) => {
135
+ if (data.classId) {
136
+ io.in(`class-${data.classId}`).emit('event-deleted', data.eventId);
137
+ } else {
138
+ io.emit('event-deleted', data.eventId);
139
+ }
140
+ logger.info('Event deleted', { classId: data.classId, eventId: data.eventId });
141
+ });
142
+ });
143
+ };
package/src/trpc.ts ADDED
@@ -0,0 +1,90 @@
1
+ import { initTRPC, TRPCError } from '@trpc/server';
2
+ import { ZodError } from 'zod';
3
+ import { logger } from './utils/logger';
4
+ import { prisma } from './lib/prisma';
5
+ import { createLoggingMiddleware } from './middleware/logging';
6
+ import { createAuthMiddleware } from './middleware/auth';
7
+ import { Request, Response } from 'express';
8
+ import { z } from 'zod';
9
+
10
+ interface CreateContextOptions {
11
+ req: Request;
12
+ res: Response;
13
+ }
14
+
15
+ export type Context = {
16
+ req: Request;
17
+ res: Response;
18
+ user: { id: string } | null;
19
+ meta?: {
20
+ classId?: string;
21
+ institutionId?: string;
22
+ };
23
+ };
24
+
25
+ export const createTRPCContext = async (opts: CreateContextOptions): Promise<Context> => {
26
+ const { req, res } = opts;
27
+
28
+ // Get user from session/token
29
+ const token = req.headers.authorization?.split(' ')[1];
30
+ const user = token ? await prisma.user.findFirst({
31
+ where: {
32
+ sessions: {
33
+ some: {
34
+ id: token
35
+ }
36
+ },
37
+ },
38
+ select: {
39
+ id: true,
40
+ }
41
+ }) : null;
42
+
43
+ return {
44
+ req,
45
+ res,
46
+ user,
47
+ meta: {},
48
+ };
49
+ };
50
+
51
+ export const t = initTRPC.context<Context>().create({
52
+ errorFormatter({ shape, error }) {
53
+ logger.error('tRPC Error', {
54
+ code: shape.code,
55
+ message: error.message,
56
+ cause: error.cause,
57
+ stack: error.stack,
58
+ });
59
+
60
+ return {
61
+ ...shape,
62
+ data: {
63
+ ...shape.data,
64
+ zodError:
65
+ error.cause instanceof ZodError ? error.cause.flatten() : null,
66
+ },
67
+ };
68
+ },
69
+ });
70
+
71
+ // Create middleware
72
+ const loggingMiddleware = createLoggingMiddleware(t);
73
+ const { isAuthed, isMemberInClass, isTeacherInClass } = createAuthMiddleware(t);
74
+
75
+ // Base procedures
76
+ export const createTRPCRouter = t.router;
77
+ export const publicProcedure = t.procedure.use(loggingMiddleware);
78
+
79
+ // Protected procedures
80
+ export const protectedProcedure = publicProcedure.use(isAuthed);
81
+ export const protectedClassMemberProcedure = protectedProcedure
82
+ .input(z.object({ classId: z.string() }).passthrough())
83
+ .use(isMemberInClass);
84
+ export const protectedTeacherProcedure = protectedProcedure
85
+ .input(z.object({ classId: z.string() }).passthrough())
86
+ .use(isTeacherInClass);
87
+
88
+
89
+ // Create caller factory
90
+ export const createCallerFactory = t.createCallerFactory;
@@ -0,0 +1,15 @@
1
+ import { inferAsyncReturnType } from '@trpc/server';
2
+ import { createTRPCContext } from '../trpc';
3
+
4
+ export type Context = inferAsyncReturnType<typeof createTRPCContext> & {
5
+ isTeacher?: boolean;
6
+ teacherClassIds?: string[];
7
+ };
8
+
9
+ export interface MiddlewareContext {
10
+ ctx: Context;
11
+ next: (opts?: { ctx: Partial<Context> }) => Promise<any>;
12
+ input?: any;
13
+ path?: string;
14
+ type?: string;
15
+ }
@@ -0,0 +1,11 @@
1
+ import nodemailer from 'nodemailer';
2
+
3
+ export const transport = nodemailer.createTransport({
4
+ host: process.env.EMAIL_HOST,
5
+ port: 587,
6
+ secure: false,
7
+ auth: {
8
+ user: process.env.EMAIL_USER,
9
+ pass: process.env.EMAIL_PASS,
10
+ },
11
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Generates a random invite code
3
+ * @returns {string} The invite code with length 5
4
+ */
5
+
6
+ export const generateInviteCode = () => {
7
+ return (Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)).slice(0, 5);
8
+ }
@@ -0,0 +1,156 @@
1
+ export enum LogLevel {
2
+ INFO = 'info',
3
+ WARN = 'warn',
4
+ ERROR = 'error',
5
+ DEBUG = 'debug'
6
+ }
7
+
8
+ type LogMode = 'silent' | 'minimal' | 'normal' | 'verbose';
9
+
10
+ interface LogMessage {
11
+ level: LogLevel;
12
+ message: string;
13
+ timestamp: string;
14
+ context?: Record<string, any>;
15
+ }
16
+
17
+ // ANSI color codes
18
+ const colors = {
19
+ reset: '\x1b[0m',
20
+ bright: '\x1b[1m',
21
+ dim: '\x1b[2m',
22
+ red: '\x1b[31m',
23
+ green: '\x1b[32m',
24
+ yellow: '\x1b[33m',
25
+ blue: '\x1b[34m',
26
+ magenta: '\x1b[35m',
27
+ cyan: '\x1b[36m',
28
+ white: '\x1b[37m',
29
+ gray: '\x1b[90m'
30
+ };
31
+
32
+ class Logger {
33
+ private static instance: Logger;
34
+ private isDevelopment: boolean;
35
+ private mode: LogMode;
36
+ private levelColors: Record<LogLevel, string>;
37
+ private levelEmojis: Record<LogLevel, string>;
38
+
39
+ private constructor() {
40
+ this.isDevelopment = process.env.NODE_ENV === 'development';
41
+
42
+ this.mode = (process.env.LOG_MODE as LogMode) || 'normal';
43
+
44
+ this.levelColors = {
45
+ [LogLevel.INFO]: colors.blue,
46
+ [LogLevel.WARN]: colors.yellow,
47
+ [LogLevel.ERROR]: colors.red,
48
+ [LogLevel.DEBUG]: colors.magenta
49
+ };
50
+
51
+ this.levelEmojis = {
52
+ [LogLevel.INFO]: 'ℹ️',
53
+ [LogLevel.WARN]: '⚠️',
54
+ [LogLevel.ERROR]: '❌',
55
+ [LogLevel.DEBUG]: '🔍'
56
+ };
57
+ }
58
+
59
+ public static getInstance(): Logger {
60
+ if (!Logger.instance) {
61
+ Logger.instance = new Logger();
62
+ }
63
+ return Logger.instance;
64
+ }
65
+
66
+ public setMode(mode: LogMode) {
67
+ this.mode = mode;
68
+ }
69
+
70
+ private shouldLog(level: LogLevel): boolean {
71
+ const silent = [
72
+ LogLevel.ERROR,
73
+ ];
74
+
75
+ const minimal = [
76
+ LogLevel.ERROR,
77
+ LogLevel.WARN,
78
+ ];
79
+
80
+ const normal = [
81
+ LogLevel.ERROR,
82
+ LogLevel.WARN,
83
+ LogLevel.INFO,
84
+ ];
85
+
86
+ if (this.mode === 'silent') return silent.includes(level);
87
+ if (this.mode === 'minimal') return minimal.includes(level);
88
+ if (this.mode === 'normal') return normal.includes(level);
89
+ return true; // verbose mode
90
+ }
91
+
92
+ private formatMessage(logMessage: LogMessage): string {
93
+ const { level, message, timestamp, context } = logMessage;
94
+ const color = this.levelColors[level];
95
+ const emoji = this.levelEmojis[level];
96
+
97
+ const timestampStr = colors.gray + `[${timestamp}]` + colors.reset;
98
+ const levelStr = color + `[${level.toUpperCase()}]` + colors.reset;
99
+ const emojiStr = emoji + ' ';
100
+ const messageStr = colors.bright + message + colors.reset;
101
+
102
+ const contextStr = context
103
+ ? '\n' + colors.dim + 'Context: ' + JSON.stringify(context, null, 2) + colors.reset
104
+ : '';
105
+
106
+ return `${timestampStr} ${levelStr} ${emojiStr}${messageStr}${contextStr}`;
107
+ }
108
+
109
+ private log(level: LogLevel, message: string, context?: Record<string, any>) {
110
+ // if (this.shouldLog(level))
111
+ // return;
112
+
113
+ const logMessage: LogMessage = {
114
+ level,
115
+ message,
116
+ timestamp: new Date().toISOString(),
117
+ context
118
+ };
119
+
120
+ const formattedMessage = this.formatMessage(logMessage);
121
+
122
+ switch (level) {
123
+ case LogLevel.ERROR:
124
+ console.error(formattedMessage);
125
+ break;
126
+ case LogLevel.WARN:
127
+ console.warn(formattedMessage);
128
+ break;
129
+ case LogLevel.DEBUG:
130
+ if (this.isDevelopment) {
131
+ console.debug(formattedMessage);
132
+ }
133
+ break;
134
+ default:
135
+ console.log(formattedMessage);
136
+ }
137
+ }
138
+
139
+ public info(message: string, context?: Record<string, any>) {
140
+ this.log(LogLevel.INFO, message, context);
141
+ }
142
+
143
+ public warn(message: string, context?: Record<string, any>) {
144
+ this.log(LogLevel.WARN, message, context);
145
+ }
146
+
147
+ public error(message: string, context?: Record<string, any>) {
148
+ this.log(LogLevel.ERROR, message, context);
149
+ }
150
+
151
+ public debug(message: string, context?: Record<string, any>) {
152
+ this.log(LogLevel.DEBUG, message, context);
153
+ }
154
+ }
155
+
156
+ export const logger = Logger.getInstance();
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "CommonJS",
5
+ "moduleResolution": "node",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "outDir": "dist",
11
+ "baseUrl": ".",
12
+ "declaration": true,
13
+ "declarationMap": true
14
+ },
15
+ "include": ["src/**/*"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }
@@ -1 +0,0 @@
1
- export * from "./index"
@@ -1,4 +0,0 @@
1
-
2
- /* !!! This is code generated by Prisma. Do not edit directly. !!!
3
- /* eslint-disable */
4
- module.exports = { ...require('.') }
@@ -1 +0,0 @@
1
- export * from "./index"
@@ -1,4 +0,0 @@
1
-
2
- /* !!! This is code generated by Prisma. Do not edit directly. !!!
3
- /* eslint-disable */
4
- module.exports = { ...require('.') }
@@ -1 +0,0 @@
1
- export * from "./default"