@studious-lms/server 1.8.0 → 1.9.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.
Files changed (71) hide show
  1. package/dist/middleware/security.d.ts.map +1 -1
  2. package/dist/middleware/security.js +3 -3
  3. package/dist/middleware/security.js.map +1 -1
  4. package/dist/models/agenda.d.ts +2 -2
  5. package/dist/models/announcement.d.ts +2 -2
  6. package/dist/models/assignment.d.ts +33 -33
  7. package/dist/models/class.d.ts +2 -2
  8. package/dist/models/cloudStorage.d.ts +14 -1
  9. package/dist/models/cloudStorage.d.ts.map +1 -1
  10. package/dist/models/cloudStorage.js +123 -3
  11. package/dist/models/cloudStorage.js.map +1 -1
  12. package/dist/models/event.d.ts +3 -3
  13. package/dist/models/file.d.ts +27 -26
  14. package/dist/models/file.d.ts.map +1 -1
  15. package/dist/models/file.js +4 -39
  16. package/dist/models/file.js.map +1 -1
  17. package/dist/models/labChat.d.ts +6 -6
  18. package/dist/models/section.d.ts +1 -1
  19. package/dist/routers/_app.d.ts +354 -100
  20. package/dist/routers/_app.d.ts.map +1 -1
  21. package/dist/routers/_app.js +4 -2
  22. package/dist/routers/_app.js.map +1 -1
  23. package/dist/routers/agenda.d.ts +2 -2
  24. package/dist/routers/assignment.d.ts +31 -31
  25. package/dist/routers/class.d.ts +5 -5
  26. package/dist/routers/event.d.ts +2 -2
  27. package/dist/routers/file.d.ts +2 -2
  28. package/dist/routers/folder.d.ts +24 -0
  29. package/dist/routers/folder.d.ts.map +1 -1
  30. package/dist/routers/folder.js +27 -5
  31. package/dist/routers/folder.js.map +1 -1
  32. package/dist/routers/labChat.d.ts +2 -2
  33. package/dist/routers/newtonChat.d.ts +1 -1
  34. package/dist/routers/permissions.d.ts +120 -0
  35. package/dist/routers/permissions.d.ts.map +1 -0
  36. package/dist/routers/permissions.js +50 -0
  37. package/dist/routers/permissions.js.map +1 -0
  38. package/dist/routers/tag.d.ts +5 -5
  39. package/dist/services/agenda.d.ts +2 -2
  40. package/dist/services/assignment.d.ts +31 -31
  41. package/dist/services/class.d.ts +5 -5
  42. package/dist/services/class.js +3 -3
  43. package/dist/services/class.js.map +1 -1
  44. package/dist/services/event.d.ts +2 -2
  45. package/dist/services/file.d.ts +22 -5
  46. package/dist/services/file.d.ts.map +1 -1
  47. package/dist/services/file.js +81 -45
  48. package/dist/services/file.js.map +1 -1
  49. package/dist/services/folder.d.ts +97 -2
  50. package/dist/services/folder.d.ts.map +1 -1
  51. package/dist/services/folder.js +158 -87
  52. package/dist/services/folder.js.map +1 -1
  53. package/dist/services/labChat.d.ts +2 -2
  54. package/dist/services/permissions.d.ts +188 -0
  55. package/dist/services/permissions.d.ts.map +1 -0
  56. package/dist/services/permissions.js +661 -0
  57. package/dist/services/permissions.js.map +1 -0
  58. package/package.json +1 -1
  59. package/prisma/migrations/20260510170000_add_folder_permissions/migration.sql +93 -0
  60. package/prisma/schema.prisma +74 -0
  61. package/src/middleware/security.ts +1 -1
  62. package/src/models/cloudStorage.ts +134 -1
  63. package/src/models/file.ts +2 -37
  64. package/src/routers/_app.ts +2 -0
  65. package/src/routers/folder.ts +32 -2
  66. package/src/routers/permissions.ts +71 -0
  67. package/src/services/class.ts +1 -1
  68. package/src/services/file.ts +76 -47
  69. package/src/services/folder.ts +180 -91
  70. package/src/services/permissions.ts +778 -0
  71. package/tests/unit/services/permissions.test.ts +463 -0
@@ -30,6 +30,8 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
30
30
  } | {
31
31
  userId: string;
32
32
  kind: "PERSONAL";
33
+ } | {
34
+ kind: "SHARED_WITH_ME";
33
35
  };
34
36
  };
35
37
  output: {
@@ -75,6 +77,23 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
75
77
  }[];
76
78
  canWrite: boolean;
77
79
  scope: import("../models/cloudStorage.js").CloudScope;
80
+ displayRoot: {
81
+ kind: "SHARED_WITH_ME";
82
+ name: string;
83
+ } | null;
84
+ };
85
+ meta: object;
86
+ }>;
87
+ /** Top-most folders/files explicitly shared with the caller (Drive-style). */
88
+ listSharedWithMe: import("@trpc/server").TRPCQueryProcedure<{
89
+ input: void;
90
+ output: {
91
+ folders: {
92
+ id: string;
93
+ name: string;
94
+ color: string | null;
95
+ }[];
96
+ files: never[];
78
97
  };
79
98
  meta: object;
80
99
  }>;
@@ -100,6 +119,11 @@ export declare const folderRouter: import("@trpc/server").TRPCBuiltRouter<{
100
119
  color: string | null;
101
120
  section: string;
102
121
  }[];
122
+ sharedWithMe: {
123
+ kind: "SHARED_WITH_ME";
124
+ folderCount: number;
125
+ fileCount: number;
126
+ };
103
127
  };
104
128
  meta: object;
105
129
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"folder.d.ts","sourceRoot":"/","sources":["routers/folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA0CxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;IACvB,wEAAwE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAUxE,mEAAmE;;;;;;;;;;;;;;;;;;;;;;;;;IAKnE,+CAA+C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAiE/C,uDAAuD;;;;;;;;;;;;;IAevD,yDAAyD;;;;;;;;;;;;GAgBzD,CAAC"}
1
+ {"version":3,"file":"folder.d.ts","sourceRoot":"/","sources":["routers/folder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyDxB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;IACvB,wEAAwE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoBxE,8EAA8E;;;;;;;;;;;;;IAK9E,mEAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAKnE,+CAA+C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAiE/C,uDAAuD;;;;;;;;;;;;;IAevD,yDAAyD;;;;;;;;;;;;GAgBzD,CAAC"}
@@ -1,8 +1,9 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9f777a4d-302d-516f-8d82-00a1ba3392ab")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f76c8e6f-19ba-5dd6-9a07-530a455a8b5f")}catch(e){}}();
3
3
  import { z } from "zod";
4
4
  import { createTRPCRouter, protectedProcedure, } from "../trpc.js";
5
- import { browseFolder, confirmFolderUpload, createFolderRecord, deleteFolder, getFolder, getFolderParents, getFolderUploadUrls, getRootFolder, listUserScopes, moveFolder, updateFolderRecord, } from "../services/folder.js";
5
+ import { browseFolder, browseSharedWithMe, confirmFolderUpload, createFolderRecord, deleteFolder, getFolder, getFolderParents, getFolderUploadUrls, getRootFolder, listUserScopes, moveFolder, updateFolderRecord, } from "../services/folder.js";
6
+ import { listSharedWithMe } from "../services/permissions.js";
6
7
  const directFileSchema = z.object({
7
8
  name: z.string(),
8
9
  type: z.string(),
@@ -24,14 +25,35 @@ const cloudScopeSchema = z.discriminatedUnion("kind", [
24
25
  z.object({ kind: z.literal("SCHOOL_STORAGE"), schoolId: z.string() }),
25
26
  z.object({ kind: z.literal("PERSONAL"), userId: z.string() }),
26
27
  ]);
28
+ /**
29
+ * Extended scope schema for `browse` only. SHARED_WITH_ME is a *virtual*
30
+ * scope — it has no root row in the `Folder` table and you can't create
31
+ * folders inside it directly. It just collects every top-most explicit grant
32
+ * the user holds for "Shared with me"-style display.
33
+ */
34
+ const browseScopeSchema = z.discriminatedUnion("kind", [
35
+ z.object({ kind: z.literal("CLASS"), classId: z.string() }),
36
+ z.object({ kind: z.literal("SCHOOL_STORAGE"), schoolId: z.string() }),
37
+ z.object({ kind: z.literal("PERSONAL"), userId: z.string() }),
38
+ z.object({ kind: z.literal("SHARED_WITH_ME") }),
39
+ ]);
27
40
  export const folderRouter = createTRPCRouter({
28
41
  /** Browse a folder (or scope root) with breadcrumbs and access flag. */
29
42
  browse: protectedProcedure
30
43
  .input(z.union([
31
44
  z.object({ folderId: z.string() }),
32
- z.object({ scope: cloudScopeSchema }),
45
+ z.object({ scope: browseScopeSchema }),
33
46
  ]))
34
- .query(({ ctx, input }) => browseFolder(ctx.user.id, input)),
47
+ .query(({ ctx, input }) => {
48
+ if ("scope" in input && input.scope.kind === "SHARED_WITH_ME") {
49
+ return browseSharedWithMe(ctx.user.id);
50
+ }
51
+ return browseFolder(ctx.user.id, "scope" in input
52
+ ? { scope: input.scope }
53
+ : input);
54
+ }),
55
+ /** Top-most folders/files explicitly shared with the caller (Drive-style). */
56
+ listSharedWithMe: protectedProcedure.query(({ ctx }) => listSharedWithMe(ctx.user.id)),
35
57
  /** What scopes can this user pick from in the cloud-storage UI? */
36
58
  listScopes: protectedProcedure.query(({ ctx }) => listUserScopes(ctx.user.id)),
37
59
  /** Root folder for any scope (no folderId). */
@@ -104,4 +126,4 @@ export const folderRouter = createTRPCRouter({
104
126
  })),
105
127
  });
106
128
  //# sourceMappingURL=folder.js.map
107
- //# debugId=9f777a4d-302d-516f-8d82-00a1ba3392ab
129
+ //# debugId=f76c8e6f-19ba-5dd6-9a07-530a455a8b5f
@@ -1 +1 @@
1
- {"version":3,"file":"folder.js","sources":["routers/folder.ts"],"sourceRoot":"/","sourcesContent":["import { z } from \"zod\";\nimport {\n createTRPCRouter,\n protectedProcedure,\n} from \"../trpc.js\";\nimport {\n browseFolder,\n confirmFolderUpload,\n createFolderRecord,\n deleteFolder,\n getFolder,\n getFolderParents,\n getFolderUploadUrls,\n getRootFolder,\n listUserScopes,\n moveFolder,\n updateFolderRecord,\n} from \"../services/folder.js\";\n\nconst directFileSchema = z.object({\n name: z.string(),\n type: z.string(),\n size: z.number(),\n});\n\n/**\n * Cloud scope discriminator. Folders live under one of:\n * - CLASS: a class shared-files area\n * - SCHOOL_STORAGE: institution-wide files\n * - PERSONAL: a single user's storage\n *\n * All folder operations identify the location they apply to with either a\n * `folderId` (for an existing folder) or a `scope` (when no folder exists yet,\n * e.g. browsing a scope root). There is no `classId` shorthand — use\n * `{ scope: { kind: \"CLASS\", classId } }` instead.\n */\nconst cloudScopeSchema = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"CLASS\"), classId: z.string() }),\n z.object({ kind: z.literal(\"SCHOOL_STORAGE\"), schoolId: z.string() }),\n z.object({ kind: z.literal(\"PERSONAL\"), userId: z.string() }),\n]);\n\nexport const folderRouter = createTRPCRouter({\n /** Browse a folder (or scope root) with breadcrumbs and access flag. */\n browse: protectedProcedure\n .input(\n z.union([\n z.object({ folderId: z.string() }),\n z.object({ scope: cloudScopeSchema }),\n ])\n )\n .query(({ ctx, input }) => browseFolder(ctx.user!.id, input)),\n\n /** What scopes can this user pick from in the cloud-storage UI? */\n listScopes: protectedProcedure.query(({ ctx }) =>\n listUserScopes(ctx.user!.id)\n ),\n\n /** Root folder for any scope (no folderId). */\n getRootFolder: protectedProcedure\n .input(z.object({ scope: cloudScopeSchema }))\n .query(({ ctx, input }) => getRootFolder(ctx.user!.id, input.scope)),\n\n get: protectedProcedure\n .input(z.object({ folderId: z.string() }))\n .query(({ ctx, input }) => getFolder(ctx.user!.id, input.folderId)),\n\n getParents: protectedProcedure\n .input(z.object({ folderId: z.string() }))\n .query(({ ctx, input }) => getFolderParents(ctx.user!.id, input.folderId)),\n\n create: protectedProcedure\n .input(\n z.object({\n name: z.string().min(1),\n parentFolderId: z.string().nullable().optional(),\n color: z.string().optional(),\n scope: cloudScopeSchema.optional(),\n })\n )\n .mutation(({ ctx, input }) =>\n createFolderRecord(ctx.user!.id, {\n name: input.name,\n parentFolderId: input.parentFolderId ?? undefined,\n color: input.color,\n scope: input.scope,\n })\n ),\n\n update: protectedProcedure\n .input(\n z.object({\n folderId: z.string(),\n name: z.string().optional(),\n color: z.string().optional(),\n })\n )\n .mutation(({ ctx, input }) =>\n updateFolderRecord(ctx.user!.id, {\n folderId: input.folderId,\n name: input.name,\n color: input.color,\n })\n ),\n\n delete: protectedProcedure\n .input(z.object({ folderId: z.string() }))\n .mutation(({ ctx, input }) => deleteFolder(ctx.user!.id, input.folderId)),\n\n move: protectedProcedure\n .input(\n z.object({\n folderId: z.string(),\n targetParentFolderId: z.string(),\n })\n )\n .mutation(({ ctx, input }) =>\n moveFolder(ctx.user!.id, {\n folderId: input.folderId,\n targetParentFolderId: input.targetParentFolderId,\n })\n ),\n\n /** Get signed upload URLs for direct-to-GCS upload. */\n getFolderUploadUrls: protectedProcedure\n .input(\n z.object({\n folderId: z.string(),\n files: z.array(directFileSchema),\n })\n )\n .mutation(({ ctx, input }) =>\n getFolderUploadUrls(ctx.user!.id, {\n folderId: input.folderId,\n files: input.files,\n })\n ),\n\n /** Acknowledge a direct upload completed (or failed). */\n confirmFolderUpload: protectedProcedure\n .input(\n z.object({\n fileId: z.string(),\n uploadSuccess: z.boolean(),\n errorMessage: z.string().optional(),\n })\n )\n .mutation(({ ctx, input }) =>\n confirmFolderUpload(ctx.user!.id, {\n fileId: input.fileId,\n uploadSuccess: input.uploadSuccess,\n errorMessage: input.errorMessage,\n })\n ),\n});\n"],"names":[],"mappings":"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","debug_id":"9f777a4d-302d-516f-8d82-00a1ba3392ab"}
1
+ {"version":3,"file":"folder.js","sources":["routers/folder.ts"],"sourceRoot":"/","sourcesContent":["import { z } from \"zod\";\nimport {\n createTRPCRouter,\n protectedProcedure,\n} from \"../trpc.js\";\nimport {\n browseFolder,\n browseSharedWithMe,\n confirmFolderUpload,\n createFolderRecord,\n deleteFolder,\n getFolder,\n getFolderParents,\n getFolderUploadUrls,\n getRootFolder,\n listUserScopes,\n moveFolder,\n updateFolderRecord,\n} from \"../services/folder.js\";\nimport { listSharedWithMe } from \"../services/permissions.js\";\n\nconst directFileSchema = z.object({\n name: z.string(),\n type: z.string(),\n size: z.number(),\n});\n\n/**\n * Cloud scope discriminator. Folders live under one of:\n * - CLASS: a class shared-files area\n * - SCHOOL_STORAGE: institution-wide files\n * - PERSONAL: a single user's storage\n *\n * All folder operations identify the location they apply to with either a\n * `folderId` (for an existing folder) or a `scope` (when no folder exists yet,\n * e.g. browsing a scope root). There is no `classId` shorthand — use\n * `{ scope: { kind: \"CLASS\", classId } }` instead.\n */\nconst cloudScopeSchema = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"CLASS\"), classId: z.string() }),\n z.object({ kind: z.literal(\"SCHOOL_STORAGE\"), schoolId: z.string() }),\n z.object({ kind: z.literal(\"PERSONAL\"), userId: z.string() }),\n]);\n\n/**\n * Extended scope schema for `browse` only. SHARED_WITH_ME is a *virtual*\n * scope — it has no root row in the `Folder` table and you can't create\n * folders inside it directly. It just collects every top-most explicit grant\n * the user holds for \"Shared with me\"-style display.\n */\nconst browseScopeSchema = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"CLASS\"), classId: z.string() }),\n z.object({ kind: z.literal(\"SCHOOL_STORAGE\"), schoolId: z.string() }),\n z.object({ kind: z.literal(\"PERSONAL\"), userId: z.string() }),\n z.object({ kind: z.literal(\"SHARED_WITH_ME\") }),\n]);\n\nexport const folderRouter = createTRPCRouter({\n /** Browse a folder (or scope root) with breadcrumbs and access flag. */\n browse: protectedProcedure\n .input(\n z.union([\n z.object({ folderId: z.string() }),\n z.object({ scope: browseScopeSchema }),\n ])\n )\n .query(({ ctx, input }) => {\n if (\"scope\" in input && input.scope.kind === \"SHARED_WITH_ME\") {\n return browseSharedWithMe(ctx.user!.id);\n }\n return browseFolder(\n ctx.user!.id,\n \"scope\" in input\n ? { scope: input.scope as Exclude<typeof input.scope, { kind: \"SHARED_WITH_ME\" }> }\n : input\n );\n }),\n\n /** Top-most folders/files explicitly shared with the caller (Drive-style). */\n listSharedWithMe: protectedProcedure.query(({ ctx }) =>\n listSharedWithMe(ctx.user!.id)\n ),\n\n /** What scopes can this user pick from in the cloud-storage UI? */\n listScopes: protectedProcedure.query(({ ctx }) =>\n listUserScopes(ctx.user!.id)\n ),\n\n /** Root folder for any scope (no folderId). */\n getRootFolder: protectedProcedure\n .input(z.object({ scope: cloudScopeSchema }))\n .query(({ ctx, input }) => getRootFolder(ctx.user!.id, input.scope)),\n\n get: protectedProcedure\n .input(z.object({ folderId: z.string() }))\n .query(({ ctx, input }) => getFolder(ctx.user!.id, input.folderId)),\n\n getParents: protectedProcedure\n .input(z.object({ folderId: z.string() }))\n .query(({ ctx, input }) => getFolderParents(ctx.user!.id, input.folderId)),\n\n create: protectedProcedure\n .input(\n z.object({\n name: z.string().min(1),\n parentFolderId: z.string().nullable().optional(),\n color: z.string().optional(),\n scope: cloudScopeSchema.optional(),\n })\n )\n .mutation(({ ctx, input }) =>\n createFolderRecord(ctx.user!.id, {\n name: input.name,\n parentFolderId: input.parentFolderId ?? undefined,\n color: input.color,\n scope: input.scope,\n })\n ),\n\n update: protectedProcedure\n .input(\n z.object({\n folderId: z.string(),\n name: z.string().optional(),\n color: z.string().optional(),\n })\n )\n .mutation(({ ctx, input }) =>\n updateFolderRecord(ctx.user!.id, {\n folderId: input.folderId,\n name: input.name,\n color: input.color,\n })\n ),\n\n delete: protectedProcedure\n .input(z.object({ folderId: z.string() }))\n .mutation(({ ctx, input }) => deleteFolder(ctx.user!.id, input.folderId)),\n\n move: protectedProcedure\n .input(\n z.object({\n folderId: z.string(),\n targetParentFolderId: z.string(),\n })\n )\n .mutation(({ ctx, input }) =>\n moveFolder(ctx.user!.id, {\n folderId: input.folderId,\n targetParentFolderId: input.targetParentFolderId,\n })\n ),\n\n /** Get signed upload URLs for direct-to-GCS upload. */\n getFolderUploadUrls: protectedProcedure\n .input(\n z.object({\n folderId: z.string(),\n files: z.array(directFileSchema),\n })\n )\n .mutation(({ ctx, input }) =>\n getFolderUploadUrls(ctx.user!.id, {\n folderId: input.folderId,\n files: input.files,\n })\n ),\n\n /** Acknowledge a direct upload completed (or failed). */\n confirmFolderUpload: protectedProcedure\n .input(\n z.object({\n fileId: z.string(),\n uploadSuccess: z.boolean(),\n errorMessage: z.string().optional(),\n })\n )\n .mutation(({ ctx, input }) =>\n confirmFolderUpload(ctx.user!.id, {\n fileId: input.fileId,\n uploadSuccess: input.uploadSuccess,\n errorMessage: input.errorMessage,\n })\n ),\n});\n"],"names":[],"mappings":";;AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,cAAc,EACd,UAAU,EACV,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAE9D,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IACpD,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC3D,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IACrE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;CAC9D,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IACrD,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC3D,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IACrE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;IAC7D,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC;CAChD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;IAC3C,wEAAwE;IACxE,MAAM,EAAE,kBAAkB;SACvB,KAAK,CACJ,CAAC,CAAC,KAAK,CAAC;QACN,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC;KACvC,CAAC,CACH;SACA,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;QACxB,IAAI,OAAO,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YAC9D,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,YAAY,CACjB,GAAG,CAAC,IAAK,CAAC,EAAE,EACZ,OAAO,IAAI,KAAK;YACd,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAgE,EAAE;YACnF,CAAC,CAAC,KAAK,CACV,CAAC;IACJ,CAAC,CAAC;IAEJ,8EAA8E;IAC9E,gBAAgB,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CACrD,gBAAgB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,CAAC,CAC/B;IAED,mEAAmE;IACnE,UAAU,EAAE,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAC/C,cAAc,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,CAAC,CAC7B;IAED,+CAA+C;IAC/C,aAAa,EAAE,kBAAkB;SAC9B,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;SAC5C,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAEtE,GAAG,EAAE,kBAAkB;SACpB,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;SACzC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAErE,UAAU,EAAE,kBAAkB;SAC3B,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;SACzC,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAE5E,MAAM,EAAE,kBAAkB;SACvB,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACvB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;QAChD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC5B,KAAK,EAAE,gBAAgB,CAAC,QAAQ,EAAE;KACnC,CAAC,CACH;SACA,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAC3B,kBAAkB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE;QAC/B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,SAAS;QACjD,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CACH;IAEH,MAAM,EAAE,kBAAkB;SACvB,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC7B,CAAC,CACH;SACA,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAC3B,kBAAkB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE;QAC/B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CACH;IAEH,MAAM,EAAE,kBAAkB;SACvB,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;SACzC,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IAE3E,IAAI,EAAE,kBAAkB;SACrB,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE;KACjC,CAAC,CACH;SACA,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAC3B,UAAU,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE;QACvB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;KACjD,CAAC,CACH;IAEH,uDAAuD;IACvD,mBAAmB,EAAE,kBAAkB;SACpC,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC;KACjC,CAAC,CACH;SACA,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAC3B,mBAAmB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE;QAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK;KACnB,CAAC,CACH;IAEH,yDAAyD;IACzD,mBAAmB,EAAE,kBAAkB;SACpC,KAAK,CACJ,CAAC,CAAC,MAAM,CAAC;QACP,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE;QAC1B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACpC,CAAC,CACH;SACA,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAC3B,mBAAmB,CAAC,GAAG,CAAC,IAAK,CAAC,EAAE,EAAE;QAChC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,YAAY,EAAE,KAAK,CAAC,YAAY;KACjC,CAAC,CACH;CACJ,CAAC,CAAC","debug_id":"f76c8e6f-19ba-5dd6-9a07-530a455a8b5f"}
@@ -69,8 +69,8 @@ export declare const labChatRouter: import("@trpc/server").TRPCBuiltRouter<{
69
69
  id: string;
70
70
  createdAt: Date;
71
71
  updatedAt: Date;
72
- conversationId: string;
73
72
  classId: string;
73
+ conversationId: string;
74
74
  title: string;
75
75
  createdById: string;
76
76
  };
@@ -128,8 +128,8 @@ export declare const labChatRouter: import("@trpc/server").TRPCBuiltRouter<{
128
128
  id: string;
129
129
  createdAt: Date;
130
130
  updatedAt: Date;
131
- conversationId: string;
132
131
  classId: string;
132
+ conversationId: string;
133
133
  title: string;
134
134
  createdById: string;
135
135
  };
@@ -18,8 +18,8 @@ export declare const newtonChatRouter: import("@trpc/server").TRPCBuiltRouter<{
18
18
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
19
19
  getTutorConversation: import("@trpc/server").TRPCQueryProcedure<{
20
20
  input: {
21
- assignmentId: string;
22
21
  classId: string;
22
+ assignmentId: string;
23
23
  };
24
24
  output: {
25
25
  conversationId: string;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Permissions router. Backs the "Share" dialog and the "Shared with me" view.
3
+ *
4
+ * Permission semantics live in `services/permissions.ts`; this router is just
5
+ * a thin Zod-validated tRPC surface over those helpers.
6
+ *
7
+ * v1 grants only on folders. Files inherit from their parent folder. Adding
8
+ * file-level grants later is a non-breaking, additive change.
9
+ */
10
+ import { z } from "zod";
11
+ import { type PermissionPrincipalInput } from "../services/permissions.js";
12
+ export declare const permissionsRouter: import("@trpc/server").TRPCBuiltRouter<{
13
+ ctx: import("../trpc.js").Context;
14
+ meta: object;
15
+ errorShape: {
16
+ data: {
17
+ zodError: z.typeToFlattenedError<any, string> | null;
18
+ prismaError: import("../utils/prismaErrorHandler.js").PrismaErrorInfo | null;
19
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
20
+ httpStatus: number;
21
+ path?: string;
22
+ stack?: string;
23
+ };
24
+ message: string;
25
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
26
+ };
27
+ transformer: false;
28
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
29
+ /** List all explicit grants on a folder. Caller must have WRITE. */
30
+ list: import("@trpc/server").TRPCQueryProcedure<{
31
+ input: {
32
+ folderId: string;
33
+ };
34
+ output: ({
35
+ school: {
36
+ name: string;
37
+ id: string;
38
+ } | null;
39
+ user: {
40
+ id: string;
41
+ username: string;
42
+ email: string;
43
+ } | null;
44
+ class: {
45
+ name: string;
46
+ id: string;
47
+ section: string;
48
+ } | null;
49
+ department: {
50
+ name: string;
51
+ id: string;
52
+ color: string | null;
53
+ } | null;
54
+ tag: {
55
+ name: string;
56
+ id: string;
57
+ color: string;
58
+ } | null;
59
+ } & {
60
+ level: import(".prisma/client").$Enums.PermissionLevel;
61
+ id: string;
62
+ createdAt: Date;
63
+ schoolId: string | null;
64
+ userId: string | null;
65
+ departmentId: string | null;
66
+ folderId: string;
67
+ principalType: import(".prisma/client").$Enums.PermissionPrincipalType;
68
+ tagId: string | null;
69
+ classId: string | null;
70
+ grantedById: string;
71
+ })[];
72
+ meta: object;
73
+ }>;
74
+ /** Create a new explicit grant on a folder. */
75
+ grant: import("@trpc/server").TRPCMutationProcedure<{
76
+ input: {
77
+ level: "READ" | "WRITE";
78
+ folderId: string;
79
+ principal: PermissionPrincipalInput;
80
+ };
81
+ output: {
82
+ level: import(".prisma/client").$Enums.PermissionLevel;
83
+ id: string;
84
+ createdAt: Date;
85
+ schoolId: string | null;
86
+ userId: string | null;
87
+ departmentId: string | null;
88
+ folderId: string;
89
+ principalType: import(".prisma/client").$Enums.PermissionPrincipalType;
90
+ tagId: string | null;
91
+ classId: string | null;
92
+ grantedById: string;
93
+ };
94
+ meta: object;
95
+ }>;
96
+ /** Remove an explicit grant. Allowed for the granter or anyone with WRITE on the folder. */
97
+ revoke: import("@trpc/server").TRPCMutationProcedure<{
98
+ input: {
99
+ permissionId: string;
100
+ };
101
+ output: {
102
+ success: boolean;
103
+ };
104
+ meta: object;
105
+ }>;
106
+ /** Drive-style "Shared with me" — top-most folders explicitly granted to the caller. */
107
+ listSharedWithMe: import("@trpc/server").TRPCQueryProcedure<{
108
+ input: void;
109
+ output: {
110
+ folders: {
111
+ id: string;
112
+ name: string;
113
+ color: string | null;
114
+ }[];
115
+ files: never[];
116
+ };
117
+ meta: object;
118
+ }>;
119
+ }>>;
120
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"/","sources":["routers/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAKL,KAAK,wBAAwB,EAC9B,MAAM,4BAA4B,CAAC;AAiBpC,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;IAC5B,oEAAoE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAOpE,+CAA+C;;;;;;;;;;;;;;;;;;;;;;IAiB/C,4FAA4F;;;;;;;;;;IAO5F,wFAAwF;;;;;;;;;;;;;GAIxF,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Permissions router. Backs the "Share" dialog and the "Shared with me" view.
3
+ *
4
+ * Permission semantics live in `services/permissions.ts`; this router is just
5
+ * a thin Zod-validated tRPC surface over those helpers.
6
+ *
7
+ * v1 grants only on folders. Files inherit from their parent folder. Adding
8
+ * file-level grants later is a non-breaking, additive change.
9
+ */
10
+
11
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="dccc1159-4761-5abc-bf19-947124f66887")}catch(e){}}();
12
+ import { z } from "zod";
13
+ import { createTRPCRouter, protectedProcedure } from "../trpc.js";
14
+ import { grantPermission, listPermissions, listSharedWithMe, revokePermission, } from "../services/permissions.js";
15
+ const principalSchema = z.discriminatedUnion("kind", [
16
+ z.object({ kind: z.literal("USER"), userId: z.string() }),
17
+ z.object({ kind: z.literal("TAG"), tagId: z.string() }),
18
+ z.object({ kind: z.literal("CLASS"), classId: z.string() }),
19
+ z.object({ kind: z.literal("CLASS_TEACHERS"), classId: z.string() }),
20
+ z.object({ kind: z.literal("CLASS_STUDENTS"), classId: z.string() }),
21
+ z.object({ kind: z.literal("DEPARTMENT"), departmentId: z.string() }),
22
+ z.object({ kind: z.literal("SCHOOL"), schoolId: z.string() }),
23
+ ]);
24
+ const levelSchema = z.enum(["READ", "WRITE"]);
25
+ export const permissionsRouter = createTRPCRouter({
26
+ /** List all explicit grants on a folder. Caller must have WRITE. */
27
+ list: protectedProcedure
28
+ .input(z.object({ folderId: z.string() }))
29
+ .query(({ ctx, input }) => listPermissions(ctx.user.id, input.folderId)),
30
+ /** Create a new explicit grant on a folder. */
31
+ grant: protectedProcedure
32
+ .input(z.object({
33
+ folderId: z.string(),
34
+ principal: principalSchema,
35
+ level: levelSchema,
36
+ }))
37
+ .mutation(({ ctx, input }) => grantPermission(ctx.user.id, {
38
+ folderId: input.folderId,
39
+ principal: input.principal,
40
+ level: input.level,
41
+ })),
42
+ /** Remove an explicit grant. Allowed for the granter or anyone with WRITE on the folder. */
43
+ revoke: protectedProcedure
44
+ .input(z.object({ permissionId: z.string() }))
45
+ .mutation(({ ctx, input }) => revokePermission(ctx.user.id, input.permissionId)),
46
+ /** Drive-style "Shared with me" — top-most folders explicitly granted to the caller. */
47
+ listSharedWithMe: protectedProcedure.query(({ ctx }) => listSharedWithMe(ctx.user.id)),
48
+ });
49
+ //# sourceMappingURL=permissions.js.map
50
+ //# debugId=dccc1159-4761-5abc-bf19-947124f66887
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sources":["routers/permissions.ts"],"sourceRoot":"/","sourcesContent":["/**\n * Permissions router. Backs the \"Share\" dialog and the \"Shared with me\" view.\n *\n * Permission semantics live in `services/permissions.ts`; this router is just\n * a thin Zod-validated tRPC surface over those helpers.\n *\n * v1 grants only on folders. Files inherit from their parent folder. Adding\n * file-level grants later is a non-breaking, additive change.\n */\nimport { z } from \"zod\";\nimport { createTRPCRouter, protectedProcedure } from \"../trpc.js\";\nimport {\n grantPermission,\n listPermissions,\n listSharedWithMe,\n revokePermission,\n type PermissionPrincipalInput,\n} from \"../services/permissions.js\";\n\nconst principalSchema: z.ZodType<PermissionPrincipalInput> = z.discriminatedUnion(\n \"kind\",\n [\n z.object({ kind: z.literal(\"USER\"), userId: z.string() }),\n z.object({ kind: z.literal(\"TAG\"), tagId: z.string() }),\n z.object({ kind: z.literal(\"CLASS\"), classId: z.string() }),\n z.object({ kind: z.literal(\"CLASS_TEACHERS\"), classId: z.string() }),\n z.object({ kind: z.literal(\"CLASS_STUDENTS\"), classId: z.string() }),\n z.object({ kind: z.literal(\"DEPARTMENT\"), departmentId: z.string() }),\n z.object({ kind: z.literal(\"SCHOOL\"), schoolId: z.string() }),\n ]\n);\n\nconst levelSchema = z.enum([\"READ\", \"WRITE\"]);\n\nexport const permissionsRouter = createTRPCRouter({\n /** List all explicit grants on a folder. Caller must have WRITE. */\n list: protectedProcedure\n .input(z.object({ folderId: z.string() }))\n .query(({ ctx, input }) =>\n listPermissions(ctx.user!.id, input.folderId)\n ),\n\n /** Create a new explicit grant on a folder. */\n grant: protectedProcedure\n .input(\n z.object({\n folderId: z.string(),\n principal: principalSchema,\n level: levelSchema,\n })\n )\n .mutation(({ ctx, input }) =>\n grantPermission(ctx.user!.id, {\n folderId: input.folderId,\n principal: input.principal,\n level: input.level,\n })\n ),\n\n /** Remove an explicit grant. Allowed for the granter or anyone with WRITE on the folder. */\n revoke: protectedProcedure\n .input(z.object({ permissionId: z.string() }))\n .mutation(({ ctx, input }) =>\n revokePermission(ctx.user!.id, input.permissionId)\n ),\n\n /** Drive-style \"Shared with me\" — top-most folders explicitly granted to the caller. */\n listSharedWithMe: protectedProcedure.query(({ ctx }) =>\n listSharedWithMe(ctx.user!.id)\n ),\n});\n"],"names":[],"mappings":"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","debug_id":"dccc1159-4761-5abc-bf19-947124f66887"}
@@ -79,9 +79,9 @@ export declare const tagRouter: import("@trpc/server").TRPCBuiltRouter<{
79
79
  }>;
80
80
  attach: import("@trpc/server").TRPCMutationProcedure<{
81
81
  input: {
82
- target: "CLASS" | "USER" | "RUBRIC" | "BOUNDARY";
83
- targetId: string;
84
82
  tagId: string;
83
+ target: "USER" | "CLASS" | "RUBRIC" | "BOUNDARY";
84
+ targetId: string;
85
85
  };
86
86
  output: {
87
87
  id: string;
@@ -90,9 +90,9 @@ export declare const tagRouter: import("@trpc/server").TRPCBuiltRouter<{
90
90
  }>;
91
91
  detach: import("@trpc/server").TRPCMutationProcedure<{
92
92
  input: {
93
- target: "CLASS" | "USER" | "RUBRIC" | "BOUNDARY";
94
- targetId: string;
95
93
  tagId: string;
94
+ target: "USER" | "CLASS" | "RUBRIC" | "BOUNDARY";
95
+ targetId: string;
96
96
  };
97
97
  output: {
98
98
  id: string;
@@ -101,7 +101,7 @@ export declare const tagRouter: import("@trpc/server").TRPCBuiltRouter<{
101
101
  }>;
102
102
  getAttached: import("@trpc/server").TRPCQueryProcedure<{
103
103
  input: {
104
- target: "CLASS" | "USER" | "RUBRIC" | "BOUNDARY";
104
+ target: "USER" | "CLASS" | "RUBRIC" | "BOUNDARY";
105
105
  targetId: string;
106
106
  };
107
107
  output: {
@@ -16,8 +16,8 @@ export declare function getAgenda(userId: string, weekStart: string): Promise<{
16
16
  type: import(".prisma/client").$Enums.AssignmentType;
17
17
  id: string;
18
18
  createdAt: Date | null;
19
- modifiedAt: Date | null;
20
19
  classId: string;
20
+ modifiedAt: Date | null;
21
21
  title: string;
22
22
  instructions: string;
23
23
  dueDate: Date;
@@ -64,8 +64,8 @@ export declare function getAgenda(userId: string, weekStart: string): Promise<{
64
64
  type: import(".prisma/client").$Enums.AssignmentType;
65
65
  id: string;
66
66
  createdAt: Date | null;
67
- modifiedAt: Date | null;
68
67
  classId: string;
68
+ modifiedAt: Date | null;
69
69
  title: string;
70
70
  instructions: string;
71
71
  dueDate: Date;