@studious-lms/server 1.0.4 → 1.0.7

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 (101) hide show
  1. package/API_SPECIFICATION.md +1117 -0
  2. package/dist/exportType.js +1 -2
  3. package/dist/index.js +25 -30
  4. package/dist/lib/fileUpload.d.ts.map +1 -1
  5. package/dist/lib/fileUpload.js +31 -29
  6. package/dist/lib/googleCloudStorage.js +9 -14
  7. package/dist/lib/prisma.js +4 -7
  8. package/dist/lib/thumbnailGenerator.js +12 -20
  9. package/dist/middleware/auth.d.ts.map +1 -1
  10. package/dist/middleware/auth.js +17 -22
  11. package/dist/middleware/logging.js +5 -9
  12. package/dist/routers/_app.d.ts +3483 -1801
  13. package/dist/routers/_app.d.ts.map +1 -1
  14. package/dist/routers/_app.js +28 -27
  15. package/dist/routers/agenda.d.ts +13 -8
  16. package/dist/routers/agenda.d.ts.map +1 -1
  17. package/dist/routers/agenda.js +14 -17
  18. package/dist/routers/announcement.d.ts +4 -3
  19. package/dist/routers/announcement.d.ts.map +1 -1
  20. package/dist/routers/announcement.js +28 -31
  21. package/dist/routers/assignment.d.ts +282 -196
  22. package/dist/routers/assignment.d.ts.map +1 -1
  23. package/dist/routers/assignment.js +256 -202
  24. package/dist/routers/attendance.d.ts +5 -4
  25. package/dist/routers/attendance.d.ts.map +1 -1
  26. package/dist/routers/attendance.js +31 -34
  27. package/dist/routers/auth.d.ts +1 -0
  28. package/dist/routers/auth.d.ts.map +1 -1
  29. package/dist/routers/auth.js +80 -75
  30. package/dist/routers/class.d.ts +284 -14
  31. package/dist/routers/class.d.ts.map +1 -1
  32. package/dist/routers/class.js +435 -164
  33. package/dist/routers/event.d.ts +47 -38
  34. package/dist/routers/event.d.ts.map +1 -1
  35. package/dist/routers/event.js +76 -79
  36. package/dist/routers/file.d.ts +71 -1
  37. package/dist/routers/file.d.ts.map +1 -1
  38. package/dist/routers/file.js +267 -32
  39. package/dist/routers/folder.d.ts +296 -0
  40. package/dist/routers/folder.d.ts.map +1 -0
  41. package/dist/routers/folder.js +693 -0
  42. package/dist/routers/notifications.d.ts +103 -0
  43. package/dist/routers/notifications.d.ts.map +1 -0
  44. package/dist/routers/notifications.js +91 -0
  45. package/dist/routers/school.d.ts +208 -0
  46. package/dist/routers/school.d.ts.map +1 -0
  47. package/dist/routers/school.js +481 -0
  48. package/dist/routers/section.d.ts +1 -0
  49. package/dist/routers/section.d.ts.map +1 -1
  50. package/dist/routers/section.js +30 -33
  51. package/dist/routers/user.d.ts +2 -1
  52. package/dist/routers/user.d.ts.map +1 -1
  53. package/dist/routers/user.js +21 -24
  54. package/dist/seedDatabase.d.ts +22 -0
  55. package/dist/seedDatabase.d.ts.map +1 -0
  56. package/dist/seedDatabase.js +57 -0
  57. package/dist/socket/handlers.js +26 -30
  58. package/dist/trpc.d.ts +5 -0
  59. package/dist/trpc.d.ts.map +1 -1
  60. package/dist/trpc.js +35 -26
  61. package/dist/types/trpc.js +1 -2
  62. package/dist/utils/email.js +2 -8
  63. package/dist/utils/generateInviteCode.js +1 -5
  64. package/dist/utils/logger.d.ts.map +1 -1
  65. package/dist/utils/logger.js +13 -9
  66. package/dist/utils/prismaErrorHandler.d.ts +9 -0
  67. package/dist/utils/prismaErrorHandler.d.ts.map +1 -0
  68. package/dist/utils/prismaErrorHandler.js +234 -0
  69. package/dist/utils/prismaWrapper.d.ts +14 -0
  70. package/dist/utils/prismaWrapper.d.ts.map +1 -0
  71. package/dist/utils/prismaWrapper.js +64 -0
  72. package/package.json +17 -4
  73. package/prisma/migrations/20250807062924_init/migration.sql +436 -0
  74. package/prisma/migrations/migration_lock.toml +3 -0
  75. package/prisma/schema.prisma +67 -0
  76. package/src/index.ts +2 -2
  77. package/src/lib/fileUpload.ts +16 -7
  78. package/src/middleware/auth.ts +0 -2
  79. package/src/routers/_app.ts +5 -1
  80. package/src/routers/assignment.ts +82 -22
  81. package/src/routers/auth.ts +80 -54
  82. package/src/routers/class.ts +330 -36
  83. package/src/routers/file.ts +283 -20
  84. package/src/routers/folder.ts +755 -0
  85. package/src/routers/notifications.ts +93 -0
  86. package/src/seedDatabase.ts +66 -0
  87. package/src/socket/handlers.ts +4 -4
  88. package/src/trpc.ts +13 -0
  89. package/src/utils/logger.ts +14 -4
  90. package/src/utils/prismaErrorHandler.ts +275 -0
  91. package/src/utils/prismaWrapper.ts +91 -0
  92. package/tests/auth.test.ts +25 -0
  93. package/tests/class.test.ts +281 -0
  94. package/tests/setup.ts +98 -0
  95. package/tests/startup.test.ts +5 -0
  96. package/tsconfig.json +2 -1
  97. package/vitest.config.ts +11 -0
  98. package/dist/logger.d.ts +0 -26
  99. package/dist/logger.d.ts.map +0 -1
  100. package/dist/logger.js +0 -135
  101. package/src/logger.ts +0 -163
@@ -1,27 +1,25 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fileRouter = void 0;
4
- const zod_1 = require("zod");
5
- const trpc_1 = require("../trpc");
6
- const server_1 = require("@trpc/server");
7
- const googleCloudStorage_1 = require("../lib/googleCloudStorage");
8
- const prisma_1 = require("../lib/prisma");
9
- exports.fileRouter = (0, trpc_1.createTRPCRouter)({
10
- getSignedUrl: trpc_1.protectedProcedure
11
- .input(zod_1.z.object({
12
- fileId: zod_1.z.string(),
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, protectedProcedure, protectedTeacherProcedure } from "../trpc";
3
+ import { TRPCError } from "@trpc/server";
4
+ import { getSignedUrl, deleteFile } from "../lib/googleCloudStorage";
5
+ import { prisma } from "../lib/prisma";
6
+ import { logger } from "../utils/logger";
7
+ export const fileRouter = createTRPCRouter({
8
+ getSignedUrl: protectedProcedure
9
+ .input(z.object({
10
+ fileId: z.string(),
13
11
  }))
14
12
  .mutation(async ({ ctx, input }) => {
15
13
  const { fileId } = input;
16
14
  const userId = ctx.user?.id;
17
15
  if (!userId) {
18
- throw new server_1.TRPCError({
16
+ throw new TRPCError({
19
17
  code: "UNAUTHORIZED",
20
18
  message: "You must be logged in to access files",
21
19
  });
22
20
  }
23
21
  // Get file metadata from database
24
- const file = await prisma_1.prisma.file.findUnique({
22
+ const file = await prisma.file.findUnique({
25
23
  where: { id: fileId },
26
24
  include: {
27
25
  assignment: {
@@ -47,42 +45,279 @@ exports.fileRouter = (0, trpc_1.createTRPCRouter)({
47
45
  }
48
46
  }
49
47
  }
48
+ },
49
+ annotations: {
50
+ include: {
51
+ student: true,
52
+ assignment: {
53
+ include: {
54
+ class: {
55
+ include: {
56
+ teachers: true,
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+ },
63
+ folder: {
64
+ include: {
65
+ class: {
66
+ include: {
67
+ students: true,
68
+ teachers: true
69
+ }
70
+ }
71
+ }
50
72
  }
51
73
  }
52
74
  });
53
75
  if (!file) {
54
- throw new server_1.TRPCError({
76
+ throw new TRPCError({
55
77
  code: "NOT_FOUND",
56
78
  message: "File does not exist",
57
79
  });
58
80
  }
59
- // Check if user has access to the file
60
- const hasAccess =
61
- // File owner
62
- file.userId === userId ||
63
- // Assignment file - student in class or teacher
64
- (file.assignment && (file.assignment.class.students.some((s) => s.id === userId) ||
65
- file.assignment.class.teachers.some((t) => t.id === userId))) ||
66
- // Submission file - student who submitted or teacher of class
67
- (file.submission && (file.submission.student.id === userId ||
68
- file.submission.assignment.class.teachers.some((t) => t.id === userId)));
81
+ // Check if user has access to this file
82
+ let hasAccess = false;
83
+ let classId = null;
84
+ // Check if user is a teacher of the class
85
+ if (file.assignment?.class) {
86
+ classId = file.assignment.class.id;
87
+ hasAccess = file.assignment.class.teachers.some(teacher => teacher.id === userId) || false;
88
+ }
89
+ if (file.submission?.assignment?.classId) {
90
+ classId = file.submission.assignment.classId;
91
+ hasAccess = file.submission?.studentId === userId || false;
92
+ if (!hasAccess)
93
+ hasAccess = file.submission.assignment.class.teachers.some(teacher => teacher.id === userId) || false;
94
+ }
95
+ if (file.annotations?.assignment?.classId) {
96
+ classId = file.annotations?.assignment.classId;
97
+ hasAccess = file.annotations?.studentId === userId || false;
98
+ if (!hasAccess)
99
+ hasAccess = file.annotations.assignment.class.teachers.some(teacher => teacher.id === userId) || false;
100
+ }
101
+ // Check if user is the file owner
102
+ if (file.userId === userId) {
103
+ hasAccess = true;
104
+ }
105
+ // Check if file is in a folder and user has access to the class
106
+ if (file.folder?.class) {
107
+ hasAccess = hasAccess || file.folder.class.teachers.some(teacher => teacher.id === userId);
108
+ hasAccess = hasAccess || file.folder.class.students.some(student => student.id === userId);
109
+ }
69
110
  if (!hasAccess) {
70
- throw new server_1.TRPCError({
111
+ throw new TRPCError({
71
112
  code: "FORBIDDEN",
72
113
  message: "You do not have access to this file",
73
114
  });
74
115
  }
75
116
  try {
76
- // Generate a signed URL with short expiration
77
- const signedUrl = await (0, googleCloudStorage_1.getSignedUrl)(file.path);
78
- return { signedUrl };
117
+ const signedUrl = await getSignedUrl(file.path);
118
+ return { url: signedUrl };
79
119
  }
80
120
  catch (error) {
81
- console.error('Error generating signed URL:', error);
82
- throw new server_1.TRPCError({
121
+ logger.error('Error generating signed URL:', error);
122
+ throw new TRPCError({
83
123
  code: "INTERNAL_SERVER_ERROR",
84
- message: "Failed to generate signed URL",
124
+ message: "Failed to generate download URL",
125
+ });
126
+ }
127
+ }),
128
+ move: protectedTeacherProcedure
129
+ .input(z.object({
130
+ fileId: z.string(),
131
+ targetFolderId: z.string(),
132
+ classId: z.string(),
133
+ }))
134
+ .mutation(async ({ ctx, input }) => {
135
+ const { fileId, targetFolderId } = input;
136
+ // Get the file
137
+ const file = await prisma.file.findUnique({
138
+ where: { id: fileId },
139
+ include: {
140
+ folder: {
141
+ include: {
142
+ class: true,
143
+ },
144
+ },
145
+ },
146
+ });
147
+ if (!file) {
148
+ throw new TRPCError({
149
+ code: "NOT_FOUND",
150
+ message: "File not found",
151
+ });
152
+ }
153
+ // Get the target folder
154
+ const targetFolder = await prisma.folder.findUnique({
155
+ where: { id: targetFolderId },
156
+ include: {
157
+ class: true,
158
+ },
159
+ });
160
+ if (!targetFolder) {
161
+ throw new TRPCError({
162
+ code: "NOT_FOUND",
163
+ message: "Target folder not found",
164
+ });
165
+ }
166
+ // Move the file
167
+ const updatedFile = await prisma.file.update({
168
+ where: { id: fileId },
169
+ data: {
170
+ folderId: targetFolderId,
171
+ },
172
+ include: {
173
+ user: {
174
+ select: {
175
+ id: true,
176
+ username: true,
177
+ },
178
+ },
179
+ },
180
+ });
181
+ return updatedFile;
182
+ }),
183
+ rename: protectedTeacherProcedure
184
+ .input(z.object({
185
+ fileId: z.string(),
186
+ newName: z.string(),
187
+ classId: z.string(),
188
+ }))
189
+ .mutation(async ({ ctx, input }) => {
190
+ const { fileId, newName, classId } = input;
191
+ // Verify user is a teacher of the class
192
+ const classData = await prisma.class.findFirst({
193
+ where: {
194
+ id: classId,
195
+ teachers: {
196
+ some: {
197
+ id: ctx.user.id,
198
+ },
199
+ },
200
+ },
201
+ });
202
+ if (!classData) {
203
+ throw new TRPCError({
204
+ code: "FORBIDDEN",
205
+ message: "You must be a teacher of this class to rename files",
206
+ });
207
+ }
208
+ // Get the file
209
+ const file = await prisma.file.findUnique({
210
+ where: { id: fileId },
211
+ include: {
212
+ folder: {
213
+ include: {
214
+ class: true,
215
+ },
216
+ },
217
+ },
218
+ });
219
+ if (!file) {
220
+ throw new TRPCError({
221
+ code: "NOT_FOUND",
222
+ message: "File not found",
223
+ });
224
+ }
225
+ // Verify the file belongs to this class
226
+ if (file.folder?.classId !== classId) {
227
+ throw new TRPCError({
228
+ code: "FORBIDDEN",
229
+ message: "File does not belong to this class",
230
+ });
231
+ }
232
+ // Validate new name
233
+ if (!newName.trim()) {
234
+ throw new TRPCError({
235
+ code: "BAD_REQUEST",
236
+ message: "File name cannot be empty",
237
+ });
238
+ }
239
+ // Rename the file
240
+ const updatedFile = await prisma.file.update({
241
+ where: { id: fileId },
242
+ data: {
243
+ name: newName.trim(),
244
+ },
245
+ include: {
246
+ user: {
247
+ select: {
248
+ id: true,
249
+ username: true,
250
+ },
251
+ },
252
+ },
253
+ });
254
+ return updatedFile;
255
+ }),
256
+ delete: protectedTeacherProcedure
257
+ .input(z.object({
258
+ fileId: z.string(),
259
+ classId: z.string(),
260
+ }))
261
+ .mutation(async ({ ctx, input }) => {
262
+ const { fileId, classId } = input;
263
+ // Verify user is a teacher of the class
264
+ const classData = await prisma.class.findFirst({
265
+ where: {
266
+ id: classId,
267
+ teachers: {
268
+ some: {
269
+ id: ctx.user.id,
270
+ },
271
+ },
272
+ },
273
+ });
274
+ if (!classData) {
275
+ throw new TRPCError({
276
+ code: "FORBIDDEN",
277
+ message: "You must be a teacher of this class to delete files",
85
278
  });
86
279
  }
280
+ // Get the file
281
+ const file = await prisma.file.findUnique({
282
+ where: { id: fileId },
283
+ include: {
284
+ folder: {
285
+ include: {
286
+ class: true,
287
+ },
288
+ },
289
+ thumbnail: true,
290
+ },
291
+ });
292
+ if (!file) {
293
+ throw new TRPCError({
294
+ code: "NOT_FOUND",
295
+ message: "File not found",
296
+ });
297
+ }
298
+ // Verify the file belongs to this class
299
+ if (file.folder?.classId !== classId) {
300
+ throw new TRPCError({
301
+ code: "FORBIDDEN",
302
+ message: "File does not belong to this class",
303
+ });
304
+ }
305
+ // Delete files from storage
306
+ try {
307
+ // Delete the main file
308
+ await deleteFile(file.path);
309
+ // Delete thumbnail if it exists
310
+ if (file.thumbnail) {
311
+ await deleteFile(file.thumbnail.path);
312
+ }
313
+ }
314
+ catch (error) {
315
+ logger.warn(`Failed to delete file ${file.path}:`, error);
316
+ }
317
+ // Delete the file record from database
318
+ await prisma.file.delete({
319
+ where: { id: fileId },
320
+ });
321
+ return { success: true };
87
322
  }),
88
323
  });
@@ -0,0 +1,296 @@
1
+ import { z } from "zod";
2
+ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
3
+ ctx: import("../trpc").Context;
4
+ meta: object;
5
+ errorShape: {
6
+ data: {
7
+ zodError: z.typeToFlattenedError<any, string> | null;
8
+ prismaError: import("../utils/prismaErrorHandler").PrismaErrorInfo | null;
9
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
10
+ httpStatus: number;
11
+ path?: string;
12
+ stack?: string;
13
+ };
14
+ message: string;
15
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
16
+ };
17
+ transformer: false;
18
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
19
+ create: import("@trpc/server").TRPCMutationProcedure<{
20
+ input: {
21
+ [x: string]: unknown;
22
+ classId: string;
23
+ name: string;
24
+ parentFolderId?: string | undefined;
25
+ };
26
+ output: {
27
+ files: {
28
+ type: string;
29
+ user: {
30
+ id: string;
31
+ username: string;
32
+ } | null;
33
+ id: string;
34
+ name: string;
35
+ size: number | null;
36
+ uploadedAt: Date | null;
37
+ }[];
38
+ childFolders: {
39
+ id: string;
40
+ _count: {
41
+ files: number;
42
+ childFolders: number;
43
+ };
44
+ name: string;
45
+ }[];
46
+ } & {
47
+ id: string;
48
+ name: string;
49
+ classId: string | null;
50
+ parentFolderId: string | null;
51
+ };
52
+ meta: object;
53
+ }>;
54
+ get: import("@trpc/server").TRPCQueryProcedure<{
55
+ input: {
56
+ [x: string]: unknown;
57
+ classId: string;
58
+ folderId: string;
59
+ };
60
+ output: {
61
+ files: {
62
+ type: string;
63
+ user: {
64
+ id: string;
65
+ username: string;
66
+ } | null;
67
+ id: string;
68
+ name: string;
69
+ size: number | null;
70
+ uploadedAt: Date | null;
71
+ }[];
72
+ childFolders: {
73
+ id: string;
74
+ _count: {
75
+ files: number;
76
+ childFolders: number;
77
+ };
78
+ name: string;
79
+ }[];
80
+ parentFolder: {
81
+ id: string;
82
+ name: string;
83
+ } | null;
84
+ } & {
85
+ id: string;
86
+ name: string;
87
+ classId: string | null;
88
+ parentFolderId: string | null;
89
+ };
90
+ meta: object;
91
+ }>;
92
+ getChildFolders: import("@trpc/server").TRPCQueryProcedure<{
93
+ input: {
94
+ [x: string]: unknown;
95
+ classId: string;
96
+ };
97
+ output: ({
98
+ files: {
99
+ type: string;
100
+ user: {
101
+ id: string;
102
+ username: string;
103
+ } | null;
104
+ id: string;
105
+ name: string;
106
+ size: number | null;
107
+ uploadedAt: Date | null;
108
+ }[];
109
+ childFolders: {
110
+ id: string;
111
+ _count: {
112
+ files: number;
113
+ childFolders: number;
114
+ };
115
+ name: string;
116
+ }[];
117
+ } & {
118
+ id: string;
119
+ name: string;
120
+ classId: string | null;
121
+ parentFolderId: string | null;
122
+ })[];
123
+ meta: object;
124
+ }>;
125
+ getFolderChildren: import("@trpc/server").TRPCQueryProcedure<{
126
+ input: {
127
+ [x: string]: unknown;
128
+ classId: string;
129
+ folderId: string;
130
+ };
131
+ output: ({
132
+ files: {
133
+ type: string;
134
+ user: {
135
+ id: string;
136
+ username: string;
137
+ } | null;
138
+ id: string;
139
+ name: string;
140
+ size: number | null;
141
+ uploadedAt: Date | null;
142
+ }[];
143
+ childFolders: {
144
+ id: string;
145
+ _count: {
146
+ files: number;
147
+ childFolders: number;
148
+ };
149
+ name: string;
150
+ }[];
151
+ } & {
152
+ id: string;
153
+ name: string;
154
+ classId: string | null;
155
+ parentFolderId: string | null;
156
+ })[];
157
+ meta: object;
158
+ }>;
159
+ getRootFolder: import("@trpc/server").TRPCQueryProcedure<{
160
+ input: {
161
+ [x: string]: unknown;
162
+ classId: string;
163
+ };
164
+ output: ({
165
+ files: {
166
+ type: string;
167
+ user: {
168
+ id: string;
169
+ username: string;
170
+ } | null;
171
+ id: string;
172
+ name: string;
173
+ size: number | null;
174
+ uploadedAt: Date | null;
175
+ }[];
176
+ childFolders: {
177
+ id: string;
178
+ files: {
179
+ id: string;
180
+ }[];
181
+ name: string;
182
+ childFolders: {
183
+ id: string;
184
+ }[];
185
+ }[];
186
+ } & {
187
+ id: string;
188
+ name: string;
189
+ classId: string | null;
190
+ parentFolderId: string | null;
191
+ }) | null;
192
+ meta: object;
193
+ }>;
194
+ uploadFiles: import("@trpc/server").TRPCMutationProcedure<{
195
+ input: {
196
+ [x: string]: unknown;
197
+ classId: string;
198
+ files: {
199
+ type: string;
200
+ name: string;
201
+ size: number;
202
+ data: string;
203
+ }[];
204
+ folderId: string;
205
+ };
206
+ output: {
207
+ success: boolean;
208
+ uploadedCount: number;
209
+ };
210
+ meta: object;
211
+ }>;
212
+ delete: import("@trpc/server").TRPCMutationProcedure<{
213
+ input: {
214
+ [x: string]: unknown;
215
+ classId: string;
216
+ folderId: string;
217
+ };
218
+ output: {
219
+ success: boolean;
220
+ deletedFiles: number;
221
+ deletedFolders: number;
222
+ };
223
+ meta: object;
224
+ }>;
225
+ move: import("@trpc/server").TRPCMutationProcedure<{
226
+ input: {
227
+ [x: string]: unknown;
228
+ classId: string;
229
+ folderId: string;
230
+ targetParentFolderId?: string | undefined;
231
+ };
232
+ output: {
233
+ files: {
234
+ type: string;
235
+ user: {
236
+ id: string;
237
+ username: string;
238
+ } | null;
239
+ id: string;
240
+ name: string;
241
+ size: number | null;
242
+ uploadedAt: Date | null;
243
+ }[];
244
+ childFolders: {
245
+ id: string;
246
+ _count: {
247
+ files: number;
248
+ childFolders: number;
249
+ };
250
+ name: string;
251
+ }[];
252
+ } & {
253
+ id: string;
254
+ name: string;
255
+ classId: string | null;
256
+ parentFolderId: string | null;
257
+ };
258
+ meta: object;
259
+ }>;
260
+ rename: import("@trpc/server").TRPCMutationProcedure<{
261
+ input: {
262
+ [x: string]: unknown;
263
+ classId: string;
264
+ folderId: string;
265
+ newName: string;
266
+ };
267
+ output: {
268
+ files: {
269
+ type: string;
270
+ user: {
271
+ id: string;
272
+ username: string;
273
+ } | null;
274
+ id: string;
275
+ name: string;
276
+ size: number | null;
277
+ uploadedAt: Date | null;
278
+ }[];
279
+ childFolders: {
280
+ id: string;
281
+ _count: {
282
+ files: number;
283
+ childFolders: number;
284
+ };
285
+ name: string;
286
+ }[];
287
+ } & {
288
+ id: string;
289
+ name: string;
290
+ classId: string | null;
291
+ parentFolderId: string | null;
292
+ };
293
+ meta: object;
294
+ }>;
295
+ }>>;
296
+ //# sourceMappingURL=folder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"folder.d.ts","sourceRoot":"","sources":["../../src/routers/folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA2BxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAutBvB,CAAC"}