@studious-lms/server 1.1.22 → 1.1.24

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.
@@ -144,16 +144,28 @@ Easy LMS is a comprehensive Learning Management System built with tRPC, Prisma,
144
144
  **Input**:
145
145
  ```typescript
146
146
  {
147
- profile: Record<string, any>;
147
+ profile?: {
148
+ displayName?: string;
149
+ bio?: string;
150
+ location?: string;
151
+ website?: string;
152
+ };
153
+ // For custom profile picture (already uploaded to GCS)
148
154
  profilePicture?: {
149
- name: string;
150
- type: string;
151
- size: number;
152
- data: string; // base64
155
+ filePath: string;
156
+ fileName: string;
157
+ fileType: string; // image/jpeg, image/png, etc.
158
+ fileSize: number;
159
+ };
160
+ // OR use DiceBear avatar URL
161
+ dicebearAvatar?: {
162
+ url: string; // DiceBear avatar URL
153
163
  };
154
164
  }
155
165
  ```
156
166
 
167
+ **Note**: Profile pictures use direct upload to GCS. First get a signed upload URL using `user.getProfilePictureUploadUrl`, upload the file directly to GCS, then call this endpoint with the file path.
168
+
157
169
  ---
158
170
 
159
171
  ## 🏫 Class Management
@@ -1291,14 +1303,28 @@ Array<{
1291
1303
 
1292
1304
  ## 📊 Data Models
1293
1305
 
1294
- ### File Object
1306
+ ### File Object (Direct Upload)
1307
+ ```typescript
1308
+ {
1309
+ name: string;
1310
+ type: string;
1311
+ size: number;
1312
+ // Note: No data field - files are uploaded directly to GCS
1313
+ }
1314
+ ```
1315
+
1316
+ ### Uploaded File Object (Database)
1295
1317
  ```typescript
1296
1318
  {
1297
1319
  id: string;
1298
1320
  name: string;
1321
+ url: string; // GCS file path
1299
1322
  type: string;
1300
1323
  size: number;
1301
- data: string; // base64 encoded
1324
+ uploadStatus: 'PENDING' | 'UPLOADING' | 'COMPLETED' | 'FAILED' | 'CANCELLED';
1325
+ uploadProgress?: number;
1326
+ createdAt: Date;
1327
+ updatedAt: Date;
1302
1328
  }
1303
1329
  ```
1304
1330
 
@@ -1437,21 +1463,131 @@ const assignment = await trpc.assignment.create.mutate({
1437
1463
 
1438
1464
  ## 📝 Notes for Frontend Developers
1439
1465
 
1440
- 1. **File Uploads**: Files are sent as base64 encoded strings in the `data` field
1466
+ 1. **File Uploads**: Files are uploaded directly to Google Cloud Storage (GCS) using signed URLs. See the "File Upload Flow" section below for details.
1441
1467
  2. **Date Handling**: All dates are ISO 8601 strings
1442
1468
  3. **Authentication**: Store JWT token and include in all requests
1443
1469
  4. **Real-time Updates**: Use Socket.IO for live updates
1444
1470
  5. **Error Handling**: Always handle tRPC errors appropriately
1445
1471
  6. **Type Safety**: Use the exported TypeScript types for full type safety
1446
1472
 
1473
+ ### 📤 File Upload Flow (Direct Upload to GCS)
1474
+
1475
+ **Important**: Files are NO LONGER sent as base64 encoded strings. Instead, they are uploaded directly to Google Cloud Storage for better performance and scalability.
1476
+
1477
+ #### Step-by-Step Process:
1478
+
1479
+ **1. Get Signed Upload URLs**
1480
+ ```typescript
1481
+ // For assignment attachments
1482
+ const uploadResponse = await trpc.assignment.getAssignmentUploadUrls.mutate({
1483
+ assignmentId: "assignment-id",
1484
+ classId: "class-id",
1485
+ files: [
1486
+ { name: "homework.pdf", type: "application/pdf", size: 102400 },
1487
+ { name: "notes.docx", type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", size: 51200 }
1488
+ ]
1489
+ });
1490
+
1491
+ // For submission attachments
1492
+ const uploadResponse = await trpc.assignment.getSubmissionUploadUrls.mutate({
1493
+ assignmentId: "assignment-id",
1494
+ classId: "class-id",
1495
+ files: [
1496
+ { name: "solution.pdf", type: "application/pdf", size: 204800 }
1497
+ ]
1498
+ });
1499
+
1500
+ // For folder files
1501
+ const uploadResponse = await trpc.folder.getUploadUrls.mutate({
1502
+ folderId: "folder-id",
1503
+ classId: "class-id",
1504
+ files: [
1505
+ { name: "lecture.pptx", type: "application/vnd.openxmlformats-officedocument.presentationml.presentation", size: 512000 }
1506
+ ]
1507
+ });
1508
+ ```
1509
+
1510
+ **2. Upload Files Directly to GCS**
1511
+ ```typescript
1512
+ for (const uploadFile of uploadResponse.uploadFiles) {
1513
+ const file = files.find(f => f.name === uploadFile.name);
1514
+
1515
+ // Optional: Track upload progress
1516
+ const xhr = new XMLHttpRequest();
1517
+ xhr.upload.addEventListener('progress', async (e) => {
1518
+ if (e.lengthComputable) {
1519
+ const progress = (e.loaded / e.total) * 100;
1520
+ await trpc.assignment.updateUploadProgress.mutate({
1521
+ fileId: uploadFile.id,
1522
+ progress: Math.round(progress)
1523
+ });
1524
+ }
1525
+ });
1526
+
1527
+ // Upload to GCS using signed URL
1528
+ await fetch(uploadFile.uploadUrl, {
1529
+ method: 'PUT',
1530
+ body: file,
1531
+ headers: {
1532
+ 'Content-Type': file.type
1533
+ }
1534
+ });
1535
+ }
1536
+ ```
1537
+
1538
+ **3. Confirm Upload Success**
1539
+ ```typescript
1540
+ // Confirm each file upload
1541
+ for (const uploadFile of uploadResponse.uploadFiles) {
1542
+ await trpc.assignment.confirmAssignmentUpload.mutate({
1543
+ fileId: uploadFile.id,
1544
+ uploadSuccess: true // or false if upload failed
1545
+ });
1546
+ }
1547
+ ```
1548
+
1549
+ #### Benefits of Direct Upload:
1550
+ - ✅ **33% smaller payload** (no base64 encoding overhead)
1551
+ - ✅ **Faster uploads** (direct to GCS, no server processing)
1552
+ - ✅ **Better scalability** (server doesn't handle file data)
1553
+ - ✅ **Upload progress tracking** (real-time progress updates)
1554
+ - ✅ **Better error handling** (retry failed uploads individually)
1555
+
1556
+ #### Available Direct Upload Endpoints:
1557
+
1558
+ **Assignment Attachments:**
1559
+ - `assignment.getAssignmentUploadUrls` - Get signed URLs for assignment files
1560
+ - `assignment.confirmAssignmentUpload` - Confirm file upload success
1561
+
1562
+ **Submission Attachments:**
1563
+ - `assignment.getSubmissionUploadUrls` - Get signed URLs for submission files
1564
+ - `assignment.confirmSubmissionUpload` - Confirm file upload success
1565
+
1566
+ **Folder Files:**
1567
+ - `folder.getUploadUrls` - Get signed URLs for folder files
1568
+ - `folder.confirmUpload` - Confirm file upload success
1569
+
1570
+ **Progress Tracking:**
1571
+ - `assignment.updateUploadProgress` - Update upload progress for any file
1572
+
1447
1573
  ---
1448
1574
 
1449
1575
  *Generated on: September 14, 2025*
1450
- *Version: 1.1.0*
1451
- *Last Updated: September 2025*
1576
+ *Version: 1.2.0*
1577
+ *Last Updated: October 2025*
1452
1578
 
1453
1579
  ## 📋 Changelog
1454
1580
 
1581
+ ### Version 1.2.0 (October 2025)
1582
+ - 🚀 **BREAKING CHANGE**: Migrated from base64 file uploads to direct GCS uploads
1583
+ - ✅ Added comprehensive direct upload documentation with step-by-step flow
1584
+ - ✅ Updated File Object data models to reflect new upload system
1585
+ - ✅ Added upload status tracking (`PENDING`, `UPLOADING`, `COMPLETED`, `FAILED`, `CANCELLED`)
1586
+ - ✅ Added new direct upload endpoints for assignments, submissions, and folders
1587
+ - ✅ Added upload progress tracking endpoint
1588
+ - ✅ Updated user profile picture upload to support both custom uploads and DiceBear avatars
1589
+ - 📝 Documented all benefits of direct upload approach (33% smaller payload, faster uploads, etc.)
1590
+
1455
1591
  ### Version 1.1.0 (September 2025)
1456
1592
  - ✅ Added complete Folder Management endpoints (`folder.*`)
1457
1593
  - ✅ Added Section Management endpoints (`section.*`)
@@ -118,6 +118,17 @@ for (const uploadFile of uploadResponse.uploadFiles) {
118
118
  4. **Update PDF generation** in lab chat to use direct upload
119
119
  5. **Add cleanup job** for orphaned files
120
120
 
121
+ ## 📝 Documentation Updates (October 23, 2025)
122
+
123
+ - ✅ **API_SPECIFICATION.md** updated with direct upload flow
124
+ - Updated `user.updateProfile` endpoint documentation
125
+ - Updated File Object data models
126
+ - Added comprehensive direct upload flow documentation
127
+ - Added step-by-step examples for frontend developers
128
+ - Updated version to 1.2.0 with breaking changes documented
129
+ - ✅ **All base64 references removed** from code (except comments explaining removal)
130
+ - ✅ **Direct upload endpoints documented** with examples
131
+
121
132
  ## 📝 Files Modified
122
133
 
123
134
  - `prisma/schema.prisma` - Added upload tracking fields
@@ -1 +1 @@
1
- {"version":3,"file":"fileUpload.d.ts","sourceRoot":"","sources":["../../src/lib/fileUpload.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAKD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,CAAC,CAKvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAKzB;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUlE;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAyF3B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,OAAO,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA+Df;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAED;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,cAAc,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAiB7B"}
1
+ {"version":3,"file":"fileUpload.d.ts","sourceRoot":"","sources":["../../src/lib/fileUpload.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB;AAKD;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,CAAC,CAKvB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,QAAQ,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAKzB;AAED;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUlE;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,CAAC,CAqF3B;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,OAAO,EACtB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,cAAc,EAAE,EACvB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAa7B"}
@@ -1,8 +1,7 @@
1
1
  import { TRPCError } from "@trpc/server";
2
2
  import { v4 as uuidv4 } from "uuid";
3
- import { getSignedUrl, objectExists } from "./googleCloudStorage.js";
3
+ import { getSignedUrl } from "./googleCloudStorage.js";
4
4
  import { prisma } from "./prisma.js";
5
- import { logger } from "../utils/logger.js";
6
5
  // DEPRECATED: These functions are no longer used - files are uploaded directly to GCS
7
6
  // Use createDirectUploadFile() and createDirectUploadFiles() instead
8
7
  /**
@@ -121,11 +120,7 @@ export async function createDirectUploadFile(file, userId, directory, assignment
121
120
  };
122
121
  }
123
122
  catch (error) {
124
- logger.error('Error creating direct upload file:', { error: error instanceof Error ? {
125
- name: error.name,
126
- message: error.message,
127
- stack: error.stack,
128
- } : error });
123
+ console.error('Error creating direct upload file:', error);
129
124
  throw new TRPCError({
130
125
  code: 'INTERNAL_SERVER_ERROR',
131
126
  message: 'Failed to create direct upload file',
@@ -140,48 +135,15 @@ export async function createDirectUploadFile(file, userId, directory, assignment
140
135
  */
141
136
  export async function confirmDirectUpload(fileId, uploadSuccess, errorMessage) {
142
137
  try {
143
- // First fetch the file record to get the object path
144
- const fileRecord = await prisma.file.findUnique({
145
- where: { id: fileId },
146
- select: { path: true }
147
- });
148
- if (!fileRecord) {
149
- throw new TRPCError({
150
- code: 'NOT_FOUND',
151
- message: 'File record not found',
152
- });
153
- }
154
- let actualUploadSuccess = uploadSuccess;
155
- let actualErrorMessage = errorMessage;
156
- // If uploadSuccess is true, verify the object actually exists in GCS
157
- if (uploadSuccess) {
158
- try {
159
- const exists = await objectExists(process.env.GOOGLE_CLOUD_BUCKET_NAME, fileRecord.path);
160
- if (!exists) {
161
- actualUploadSuccess = false;
162
- actualErrorMessage = 'File upload reported as successful but object not found in Google Cloud Storage';
163
- logger.error(`File upload verification failed for ${fileId}: object ${fileRecord.path} not found in GCS`);
164
- }
165
- }
166
- catch (error) {
167
- logger.error(`Error verifying file existence in GCS for ${fileId}:`, { error: error instanceof Error ? {
168
- name: error.name,
169
- message: error.message,
170
- stack: error.stack,
171
- } : error });
172
- actualUploadSuccess = false;
173
- actualErrorMessage = 'Failed to verify file existence in Google Cloud Storage';
174
- }
175
- }
176
138
  const updateData = {
177
- uploadStatus: actualUploadSuccess ? 'COMPLETED' : 'FAILED',
178
- uploadProgress: actualUploadSuccess ? 100 : 0,
139
+ uploadStatus: uploadSuccess ? 'COMPLETED' : 'FAILED',
140
+ uploadProgress: uploadSuccess ? 100 : 0,
179
141
  };
180
- if (!actualUploadSuccess && actualErrorMessage) {
181
- updateData.uploadError = actualErrorMessage;
142
+ if (!uploadSuccess && errorMessage) {
143
+ updateData.uploadError = errorMessage;
182
144
  updateData.uploadRetryCount = { increment: 1 };
183
145
  }
184
- if (actualUploadSuccess) {
146
+ if (uploadSuccess) {
185
147
  updateData.uploadedAt = new Date();
186
148
  }
187
149
  await prisma.file.update({
@@ -190,7 +152,7 @@ export async function confirmDirectUpload(fileId, uploadSuccess, errorMessage) {
190
152
  });
191
153
  }
192
154
  catch (error) {
193
- logger.error('Error confirming direct upload:', { error });
155
+ console.error('Error confirming direct upload:', error);
194
156
  throw new TRPCError({
195
157
  code: 'INTERNAL_SERVER_ERROR',
196
158
  message: 'Failed to confirm upload',
@@ -204,31 +166,16 @@ export async function confirmDirectUpload(fileId, uploadSuccess, errorMessage) {
204
166
  */
205
167
  export async function updateUploadProgress(fileId, progress) {
206
168
  try {
207
- // await prisma.file.update({
208
- // where: { id: fileId },
209
- // data: {
210
- // uploadStatus: 'UPLOADING',
211
- // uploadProgress: Math.min(100, Math.max(0, progress))
212
- // }
213
- // });
214
- const current = await prisma.file.findUnique({ where: { id: fileId }, select: { uploadStatus: true } });
215
- if (!current || ['COMPLETED', 'FAILED', 'CANCELLED'].includes(current.uploadStatus))
216
- return;
217
- const clamped = Math.min(100, Math.max(0, progress));
218
169
  await prisma.file.update({
219
170
  where: { id: fileId },
220
171
  data: {
221
172
  uploadStatus: 'UPLOADING',
222
- uploadProgress: clamped
173
+ uploadProgress: Math.min(100, Math.max(0, progress))
223
174
  }
224
175
  });
225
176
  }
226
177
  catch (error) {
227
- logger.error('Error updating upload progress:', { error: error instanceof Error ? {
228
- name: error.name,
229
- message: error.message,
230
- stack: error.stack,
231
- } : error });
178
+ console.error('Error updating upload progress:', error);
232
179
  throw new TRPCError({
233
180
  code: 'INTERNAL_SERVER_ERROR',
234
181
  message: 'Failed to update upload progress',
@@ -250,11 +197,7 @@ export async function createDirectUploadFiles(files, userId, directory, assignme
250
197
  return await Promise.all(uploadPromises);
251
198
  }
252
199
  catch (error) {
253
- logger.error('Error creating direct upload files:', { error: error instanceof Error ? {
254
- name: error.name,
255
- message: error.message,
256
- stack: error.stack,
257
- } : error });
200
+ console.error('Error creating direct upload files:', error);
258
201
  throw new TRPCError({
259
202
  code: 'INTERNAL_SERVER_ERROR',
260
203
  message: 'Failed to create direct upload files',
@@ -10,11 +10,4 @@ export declare function getSignedUrl(filePath: string, action?: 'read' | 'write'
10
10
  * @param filePath The path of the file to delete
11
11
  */
12
12
  export declare function deleteFile(filePath: string): Promise<void>;
13
- /**
14
- * Checks if an object exists in Google Cloud Storage
15
- * @param bucketName The name of the bucket (unused, uses default bucket)
16
- * @param objectPath The path of the object to check
17
- * @returns Promise<boolean> True if the object exists, false otherwise
18
- */
19
- export declare function objectExists(bucketName: string, objectPath: string): Promise<boolean>;
20
13
  //# sourceMappingURL=googleCloudStorage.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"googleCloudStorage.d.ts","sourceRoot":"","sources":["../../src/lib/googleCloudStorage.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,MAAM,wCAAwD,CAAC;AAQ5E;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,OAAgB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB7H;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhE;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAW3F"}
1
+ {"version":3,"file":"googleCloudStorage.d.ts","sourceRoot":"","sources":["../../src/lib/googleCloudStorage.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,MAAM,wCAAwD,CAAC;AAQ5E;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,OAAgB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB7H;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhE"}
@@ -57,22 +57,3 @@ export async function deleteFile(filePath) {
57
57
  });
58
58
  }
59
59
  }
60
- /**
61
- * Checks if an object exists in Google Cloud Storage
62
- * @param bucketName The name of the bucket (unused, uses default bucket)
63
- * @param objectPath The path of the object to check
64
- * @returns Promise<boolean> True if the object exists, false otherwise
65
- */
66
- export async function objectExists(bucketName, objectPath) {
67
- try {
68
- const [exists] = await bucket.file(objectPath).exists();
69
- return exists;
70
- }
71
- catch (error) {
72
- console.error('Error checking if object exists in Google Cloud Storage:', error);
73
- throw new TRPCError({
74
- code: 'INTERNAL_SERVER_ERROR',
75
- message: 'Failed to check object existence',
76
- });
77
- }
78
- }
@@ -1326,18 +1326,6 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1326
1326
  student: {
1327
1327
  id: string;
1328
1328
  username: string;
1329
- profile: {
1330
- id: string;
1331
- location: string | null;
1332
- userId: string;
1333
- createdAt: Date;
1334
- displayName: string | null;
1335
- bio: string | null;
1336
- website: string | null;
1337
- profilePicture: string | null;
1338
- profilePictureThumbnail: string | null;
1339
- updatedAt: Date;
1340
- } | null;
1341
1329
  };
1342
1330
  attachments: {
1343
1331
  path: string;
@@ -1458,16 +1446,8 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
1458
1446
  id: string;
1459
1447
  username: string;
1460
1448
  profile: {
1461
- id: string;
1462
- location: string | null;
1463
- userId: string;
1464
- createdAt: Date;
1465
1449
  displayName: string | null;
1466
- bio: string | null;
1467
- website: string | null;
1468
1450
  profilePicture: string | null;
1469
- profilePictureThumbnail: string | null;
1470
- updatedAt: Date;
1471
1451
  } | null;
1472
1452
  };
1473
1453
  attachments: {
@@ -2439,6 +2419,37 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
2439
2419
  };
2440
2420
  meta: object;
2441
2421
  }>;
2422
+ getAnnotationUploadUrls: import("@trpc/server").TRPCMutationProcedure<{
2423
+ input: {
2424
+ [x: string]: unknown;
2425
+ classId: string;
2426
+ files: {
2427
+ type: string;
2428
+ name: string;
2429
+ size: number;
2430
+ }[];
2431
+ submissionId: string;
2432
+ };
2433
+ output: {
2434
+ success: boolean;
2435
+ uploadFiles: import("../lib/fileUpload.js").DirectUploadFile[];
2436
+ };
2437
+ meta: object;
2438
+ }>;
2439
+ confirmAnnotationUpload: import("@trpc/server").TRPCMutationProcedure<{
2440
+ input: {
2441
+ [x: string]: unknown;
2442
+ classId: string;
2443
+ fileId: string;
2444
+ uploadSuccess: boolean;
2445
+ errorMessage?: string | undefined;
2446
+ };
2447
+ output: {
2448
+ success: boolean;
2449
+ message: string;
2450
+ };
2451
+ meta: object;
2452
+ }>;
2442
2453
  updateUploadProgress: import("@trpc/server").TRPCMutationProcedure<{
2443
2454
  input: {
2444
2455
  fileId: string;
@@ -3586,6 +3597,32 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
3586
3597
  }[];
3587
3598
  meta: object;
3588
3599
  }>;
3600
+ getFolderUploadUrls: import("@trpc/server").TRPCMutationProcedure<{
3601
+ input: {
3602
+ [x: string]: unknown;
3603
+ classId: string;
3604
+ files: {
3605
+ type: string;
3606
+ name: string;
3607
+ size: number;
3608
+ }[];
3609
+ folderId: string;
3610
+ };
3611
+ output: import("../lib/fileUpload.js").DirectUploadFile[];
3612
+ meta: object;
3613
+ }>;
3614
+ confirmFolderUpload: import("@trpc/server").TRPCMutationProcedure<{
3615
+ input: {
3616
+ [x: string]: unknown;
3617
+ classId: string;
3618
+ fileId: string;
3619
+ uploadSuccess: boolean;
3620
+ };
3621
+ output: {
3622
+ success: boolean;
3623
+ };
3624
+ meta: object;
3625
+ }>;
3589
3626
  }>>;
3590
3627
  notification: import("@trpc/server").TRPCBuiltRouter<{
3591
3628
  ctx: import("../trpc.js").Context;
@@ -5582,18 +5619,6 @@ export declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
5582
5619
  student: {
5583
5620
  id: string;
5584
5621
  username: string;
5585
- profile: {
5586
- id: string;
5587
- location: string | null;
5588
- userId: string;
5589
- createdAt: Date;
5590
- displayName: string | null;
5591
- bio: string | null;
5592
- website: string | null;
5593
- profilePicture: string | null;
5594
- profilePictureThumbnail: string | null;
5595
- updatedAt: Date;
5596
- } | null;
5597
5622
  };
5598
5623
  attachments: {
5599
5624
  path: string;
@@ -5714,16 +5739,8 @@ export declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
5714
5739
  id: string;
5715
5740
  username: string;
5716
5741
  profile: {
5717
- id: string;
5718
- location: string | null;
5719
- userId: string;
5720
- createdAt: Date;
5721
5742
  displayName: string | null;
5722
- bio: string | null;
5723
- website: string | null;
5724
5743
  profilePicture: string | null;
5725
- profilePictureThumbnail: string | null;
5726
- updatedAt: Date;
5727
5744
  } | null;
5728
5745
  };
5729
5746
  attachments: {
@@ -6695,6 +6712,37 @@ export declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
6695
6712
  };
6696
6713
  meta: object;
6697
6714
  }>;
6715
+ getAnnotationUploadUrls: import("@trpc/server").TRPCMutationProcedure<{
6716
+ input: {
6717
+ [x: string]: unknown;
6718
+ classId: string;
6719
+ files: {
6720
+ type: string;
6721
+ name: string;
6722
+ size: number;
6723
+ }[];
6724
+ submissionId: string;
6725
+ };
6726
+ output: {
6727
+ success: boolean;
6728
+ uploadFiles: import("../lib/fileUpload.js").DirectUploadFile[];
6729
+ };
6730
+ meta: object;
6731
+ }>;
6732
+ confirmAnnotationUpload: import("@trpc/server").TRPCMutationProcedure<{
6733
+ input: {
6734
+ [x: string]: unknown;
6735
+ classId: string;
6736
+ fileId: string;
6737
+ uploadSuccess: boolean;
6738
+ errorMessage?: string | undefined;
6739
+ };
6740
+ output: {
6741
+ success: boolean;
6742
+ message: string;
6743
+ };
6744
+ meta: object;
6745
+ }>;
6698
6746
  updateUploadProgress: import("@trpc/server").TRPCMutationProcedure<{
6699
6747
  input: {
6700
6748
  fileId: string;
@@ -7842,6 +7890,32 @@ export declare const createCaller: import("@trpc/server").TRPCRouterCaller<{
7842
7890
  }[];
7843
7891
  meta: object;
7844
7892
  }>;
7893
+ getFolderUploadUrls: import("@trpc/server").TRPCMutationProcedure<{
7894
+ input: {
7895
+ [x: string]: unknown;
7896
+ classId: string;
7897
+ files: {
7898
+ type: string;
7899
+ name: string;
7900
+ size: number;
7901
+ }[];
7902
+ folderId: string;
7903
+ };
7904
+ output: import("../lib/fileUpload.js").DirectUploadFile[];
7905
+ meta: object;
7906
+ }>;
7907
+ confirmFolderUpload: import("@trpc/server").TRPCMutationProcedure<{
7908
+ input: {
7909
+ [x: string]: unknown;
7910
+ classId: string;
7911
+ fileId: string;
7912
+ uploadSuccess: boolean;
7913
+ };
7914
+ output: {
7915
+ success: boolean;
7916
+ };
7917
+ meta: object;
7918
+ }>;
7845
7919
  }>>;
7846
7920
  notification: import("@trpc/server").TRPCBuiltRouter<{
7847
7921
  ctx: import("../trpc.js").Context;
@@ -1 +1 @@
1
- {"version":3,"file":"_app.d.ts","sourceRoot":"","sources":["../../src/routers/_app.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAc1E,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiBpB,CAAC;AAGH,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC;AACzC,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAG1D,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAiC,CAAC"}
1
+ {"version":3,"file":"_app.d.ts","sourceRoot":"","sources":["../../src/routers/_app.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAc1E,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiBpB,CAAC;AAGH,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC;AACzC,MAAM,MAAM,YAAY,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;AAG1D,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAAiC,CAAC"}