@goscribe/server 1.5.0 → 1.6.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 (41) hide show
  1. package/dist/context.d.ts +14 -1
  2. package/dist/context.js +23 -2
  3. package/dist/lib/ai/index.d.ts +3 -2
  4. package/dist/lib/ai/index.js +3 -2
  5. package/dist/lib/ai/llm-client.d.ts +1 -0
  6. package/dist/lib/ai/llm-client.js +17 -0
  7. package/dist/routers/_app.d.ts +40 -80
  8. package/dist/routers/auth.js +1 -1
  9. package/dist/routers/flashcards.d.ts +12 -1
  10. package/dist/routers/payment.d.ts +1 -12
  11. package/dist/routers/workspace.d.ts +27 -67
  12. package/dist/routers/workspace.js +1 -0
  13. package/dist/services/billing/payment.service.d.ts +1 -12
  14. package/dist/services/billing/payment.service.js +3 -6
  15. package/dist/services/billing/usage.service.d.ts +30 -10
  16. package/dist/services/billing/usage.service.js +87 -15
  17. package/dist/services/content/copilot.service.js +15 -29
  18. package/dist/services/content/flashcard-progress.service.js +9 -9
  19. package/dist/services/content/flashcard.service.d.ts +45 -1
  20. package/dist/services/content/flashcard.service.js +81 -68
  21. package/dist/services/content/media-analysis.service.js +27 -27
  22. package/dist/services/content/worksheet-generation.service.test.js +2 -2
  23. package/dist/services/workspace/workspace.service.d.ts +23 -67
  24. package/dist/services/workspace/workspace.service.js +69 -62
  25. package/dist/trpc.d.ts +12 -4
  26. package/dist/trpc.js +5 -11
  27. package/package.json +1 -1
  28. package/src/context.ts +33 -3
  29. package/src/lib/ai/index.ts +3 -0
  30. package/src/lib/ai/llm-client.ts +23 -0
  31. package/src/routers/auth.ts +1 -1
  32. package/src/routers/workspace.ts +4 -0
  33. package/src/services/billing/payment.service.ts +3 -6
  34. package/src/services/billing/usage.service.ts +190 -77
  35. package/src/services/content/copilot.service.ts +23 -32
  36. package/src/services/content/flashcard-progress.service.ts +12 -9
  37. package/src/services/content/flashcard.service.ts +89 -66
  38. package/src/services/content/media-analysis.service.ts +34 -29
  39. package/src/services/content/worksheet-generation.service.test.ts +2 -2
  40. package/src/services/workspace/workspace.service.ts +73 -66
  41. package/src/trpc.ts +5 -13
package/dist/context.d.ts CHANGED
@@ -1,9 +1,22 @@
1
1
  import type { CreateExpressContextOptions } from "@trpc/server/adapters/express";
2
2
  import { prisma } from "./lib/prisma.js";
3
3
  import cookie from "cookie";
4
+ export type SessionUser = {
5
+ id: string;
6
+ emailVerified: boolean;
7
+ isSystemAdmin: boolean;
8
+ };
4
9
  export declare function createContext({ req, res }: CreateExpressContextOptions): Promise<{
5
10
  db: import("@prisma/client").PrismaClient<import("@prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
6
- session: any;
11
+ session: null;
12
+ req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
13
+ res: import("express").Response<any, Record<string, any>>;
14
+ cookies: cookie.Cookies;
15
+ } | {
16
+ db: import("@prisma/client").PrismaClient<import("@prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
17
+ session: {
18
+ user: SessionUser;
19
+ };
7
20
  req: import("express").Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
8
21
  res: import("express").Response<any, Record<string, any>>;
9
22
  cookies: cookie.Cookies;
package/dist/context.js CHANGED
@@ -3,10 +3,31 @@ import { verifyCustomAuthCookie } from "./lib/auth.js";
3
3
  import cookie from "cookie";
4
4
  export async function createContext({ req, res }) {
5
5
  const cookies = cookie.parse(req.headers.cookie ?? "");
6
- // Only use custom auth cookie
7
6
  const custom = verifyCustomAuthCookie(cookies["auth_token"]);
8
7
  if (custom) {
9
- return { db: prisma, session: { user: { id: custom.userId } }, req, res, cookies };
8
+ const user = await prisma.user.findUnique({
9
+ where: { id: custom.userId },
10
+ select: {
11
+ id: true,
12
+ emailVerified: true,
13
+ role: { select: { name: true } },
14
+ },
15
+ });
16
+ if (!user) {
17
+ return { db: prisma, session: null, req, res, cookies };
18
+ }
19
+ const sessionUser = {
20
+ id: user.id,
21
+ emailVerified: !!user.emailVerified,
22
+ isSystemAdmin: user.role?.name === "System Admin",
23
+ };
24
+ return {
25
+ db: prisma,
26
+ session: { user: sessionUser },
27
+ req,
28
+ res,
29
+ cookies,
30
+ };
10
31
  }
11
32
  return { db: prisma, session: null, req, res, cookies };
12
33
  }
@@ -1,5 +1,5 @@
1
1
  import { aiConfig } from './config.js';
2
- import { complete, completeText } from './llm-client.js';
2
+ import { complete, completeText, streamComplete } from './llm-client.js';
3
3
  import { DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, toVectorLiteral } from './embedding-client.js';
4
4
  import { extractJson, parseJsonField } from './json-parse.js';
5
5
  import { isAiMockMode, mockDelay } from './mock.js';
@@ -28,6 +28,7 @@ export declare const ai: {
28
28
  llm: {
29
29
  complete: typeof complete;
30
30
  completeText: typeof completeText;
31
+ streamComplete: typeof streamComplete;
31
32
  };
32
33
  embed: {
33
34
  texts: typeof embedTexts;
@@ -44,4 +45,4 @@ export declare const ai: {
44
45
  };
45
46
  export type { ChatMessage, ChatRole, CompleteOptions } from './types.js';
46
47
  export type { AISession, ProcessFileResult, PodcastSpeaker, StudyGuideSegment, WorksheetGenerationOptions, } from './inference-backend/types.js';
47
- export { aiConfig, complete, completeText, DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, extractJson, inferenceBackend, isAiMockMode, mockDelay, parseJsonField, toVectorLiteral, };
48
+ export { aiConfig, complete, completeText, streamComplete, DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, extractJson, inferenceBackend, isAiMockMode, mockDelay, parseJsonField, toVectorLiteral, };
@@ -1,5 +1,5 @@
1
1
  import { aiConfig } from './config.js';
2
- import { complete, completeText, } from './llm-client.js';
2
+ import { complete, completeText, streamComplete, } from './llm-client.js';
3
3
  import { DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, toVectorLiteral, } from './embedding-client.js';
4
4
  import { extractJson, parseJsonField } from './json-parse.js';
5
5
  import { isAiMockMode, mockDelay } from './mock.js';
@@ -11,6 +11,7 @@ export const ai = {
11
11
  llm: {
12
12
  complete,
13
13
  completeText,
14
+ streamComplete,
14
15
  },
15
16
  embed: {
16
17
  texts: embedTexts,
@@ -25,4 +26,4 @@ export const ai = {
25
26
  parseJsonField,
26
27
  },
27
28
  };
28
- export { aiConfig, complete, completeText, DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, extractJson, inferenceBackend, isAiMockMode, mockDelay, parseJsonField, toVectorLiteral, };
29
+ export { aiConfig, complete, completeText, streamComplete, DEFAULT_EMBEDDING_DIM, DEFAULT_EMBEDDING_MODEL, embedQuery, embedTexts, extractJson, inferenceBackend, isAiMockMode, mockDelay, parseJsonField, toVectorLiteral, };
@@ -2,5 +2,6 @@ import type { ChatCompletion } from 'openai/resources/chat/completions';
2
2
  import type { ChatMessage, CompleteOptions } from './types.js';
3
3
  export declare function complete(messages: ChatMessage[], options?: CompleteOptions): Promise<ChatCompletion>;
4
4
  export declare function completeText(messages: ChatMessage[], options?: CompleteOptions): Promise<string>;
5
+ export declare function streamComplete(messages: ChatMessage[], onDelta: (delta: string) => void | Promise<void>, options?: CompleteOptions): Promise<string>;
5
6
  /** @deprecated Use `complete()` from the ai module instead. */
6
7
  export default complete;
@@ -15,5 +15,22 @@ export async function completeText(messages, options = {}) {
15
15
  const response = await complete(messages, options);
16
16
  return response.choices?.[0]?.message?.content ?? '';
17
17
  }
18
+ export async function streamComplete(messages, onDelta, options = {}) {
19
+ const stream = await client.chat.completions.create({
20
+ model: options.model ?? aiConfig.llm.model,
21
+ messages,
22
+ stream: true,
23
+ ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
24
+ });
25
+ let content = '';
26
+ for await (const chunk of stream) {
27
+ const delta = chunk.choices?.[0]?.delta?.content ?? '';
28
+ if (!delta)
29
+ continue;
30
+ content += delta;
31
+ await onDelta(delta);
32
+ }
33
+ return content;
34
+ }
18
35
  /** @deprecated Use `complete()` from the ai module instead. */
19
36
  export default complete;
@@ -231,35 +231,26 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
231
231
  folders: {
232
232
  name: string;
233
233
  id: string;
234
- createdAt: Date;
235
234
  updatedAt: Date;
236
- ownerId: string;
237
235
  parentId: string | null;
238
236
  color: string;
239
237
  markerColor: string | null;
240
238
  }[];
241
- workspaces: ({
242
- uploads: {
243
- name: string;
244
- id: string;
245
- createdAt: Date;
246
- mimeType: string;
247
- }[];
248
- } & {
239
+ workspaces: {
249
240
  id: string;
250
- createdAt: Date;
251
241
  updatedAt: Date;
252
242
  title: string;
253
- description: string | null;
254
- ownerId: string;
255
243
  color: string;
256
244
  markerColor: string | null;
257
245
  icon: string;
258
246
  folderId: string | null;
259
- fileBeingAnalyzed: boolean;
260
- analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
261
- needsAnalysis: boolean;
262
- })[];
247
+ uploads: {
248
+ name: string;
249
+ id: string;
250
+ createdAt: Date;
251
+ mimeType: string;
252
+ }[];
253
+ }[];
263
254
  };
264
255
  meta: object;
265
256
  }>;
@@ -344,74 +335,43 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
344
335
  id: string;
345
336
  };
346
337
  output: {
338
+ id: string;
339
+ createdAt: Date;
340
+ updatedAt: Date;
341
+ title: string;
342
+ description: string | null;
347
343
  folder: {
348
344
  name: string;
349
345
  id: string;
350
- createdAt: Date;
351
- updatedAt: Date;
352
- ownerId: string;
353
- parentId: string | null;
354
346
  color: string;
355
- markerColor: string | null;
356
347
  } | null;
357
- artifacts: {
358
- id: string;
359
- createdAt: Date;
360
- updatedAt: Date;
361
- workspaceId: string;
362
- type: import("@prisma/client").$Enums.ArtifactType;
363
- title: string;
364
- isArchived: boolean;
365
- difficulty: import("@prisma/client").$Enums.Difficulty | null;
366
- estimatedTime: string | null;
367
- createdById: string | null;
368
- description: string | null;
369
- generating: boolean;
370
- generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
371
- worksheetConfig: import("@prisma/client/runtime/library").JsonValue | null;
372
- imageObjectKey: string | null;
373
- }[];
348
+ ownerId: string;
349
+ color: string;
350
+ markerColor: string | null;
351
+ icon: string;
352
+ folderId: string | null;
353
+ fileBeingAnalyzed: boolean;
354
+ analysisProgress: import("@prisma/client/runtime/library").JsonValue;
355
+ needsAnalysis: boolean;
374
356
  uploads: {
375
- meta: import("@prisma/client/runtime/library").JsonValue | null;
376
357
  name: string;
377
358
  id: string;
378
359
  createdAt: Date;
379
- workspaceId: string | null;
380
- userId: string | null;
381
360
  mimeType: string;
382
361
  size: number;
383
- bucket: string | null;
384
362
  objectKey: string | null;
385
- url: string | null;
386
- checksum: string | null;
387
- aiTranscription: import("@prisma/client/runtime/library").JsonValue | null;
388
363
  }[];
389
- } & {
390
- id: string;
391
- createdAt: Date;
392
- updatedAt: Date;
393
- title: string;
394
- description: string | null;
395
- ownerId: string;
396
- color: string;
397
- markerColor: string | null;
398
- icon: string;
399
- folderId: string | null;
400
- fileBeingAnalyzed: boolean;
401
- analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
402
- needsAnalysis: boolean;
403
364
  };
404
365
  meta: object;
405
366
  }>;
406
367
  getStats: import("@trpc/server").TRPCQueryProcedure<{
407
368
  input: void;
408
- output: {
409
- workspaces: number;
410
- folders: number;
411
- lastUpdated: Date | undefined;
412
- spaceUsed: number;
413
- spaceTotal: number;
414
- };
369
+ output: import("../services/billing/usage.service.js").AccountStats;
370
+ meta: object;
371
+ }>;
372
+ getAccountSummary: import("@trpc/server").TRPCQueryProcedure<{
373
+ input: void;
374
+ output: import("../services/billing/usage.service.js").AccountSummary;
415
375
  meta: object;
416
376
  }>;
417
377
  getStudyAnalytics: import("@trpc/server").TRPCQueryProcedure<{
@@ -866,7 +826,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
866
826
  input: {
867
827
  workspaceId: string;
868
828
  };
869
- output: boolean | undefined;
829
+ output: boolean;
870
830
  meta: object;
871
831
  }>;
872
832
  createCard: import("@trpc/server").TRPCMutationProcedure<{
@@ -949,6 +909,17 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
949
909
  };
950
910
  output: {
951
911
  artifact: {
912
+ flashcards: {
913
+ id: string;
914
+ createdAt: Date;
915
+ artifactId: string;
916
+ tags: string[];
917
+ order: number;
918
+ front: string;
919
+ back: string;
920
+ acceptedAnswers: string[];
921
+ }[];
922
+ } & {
952
923
  id: string;
953
924
  createdAt: Date;
954
925
  updatedAt: Date;
@@ -2665,18 +2636,7 @@ export declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
2665
2636
  input: void;
2666
2637
  output: {
2667
2638
  usage: import("../services/billing/usage.service.js").UserUsage;
2668
- limits: {
2669
- id: string;
2670
- planId: string;
2671
- maxStorageBytes: bigint;
2672
- maxWorksheets: number;
2673
- maxFlashcards: number;
2674
- maxPodcasts: number;
2675
- maxStudyGuides: number;
2676
- createdAt: Date;
2677
- updatedAt: Date;
2678
- isFallbackPlan: boolean;
2679
- };
2639
+ limits: import("../services/billing/usage.service.js").UserPlanLimits;
2680
2640
  hasActivePlan: boolean;
2681
2641
  };
2682
2642
  meta: object;
@@ -26,7 +26,7 @@ const passwordFieldSchema = z
26
26
  .regex(/[0-9]/, 'Password must contain at least one number')
27
27
  .regex(/[^a-zA-Z0-9]/, 'Password must contain at least one special character');
28
28
  export const auth = router({
29
- updateProfile: publicProcedure
29
+ updateProfile: authedProcedure
30
30
  .input(z.object({ name: z.string().min(1) }))
31
31
  .mutation(({ ctx, input }) => new AuthService(ctx.db).updateProfile(ctx.session.user.id, input)),
32
32
  changePassword: authedProcedure
@@ -85,7 +85,7 @@ export declare const flashcards: import("@trpc/server").TRPCBuiltRouter<{
85
85
  input: {
86
86
  workspaceId: string;
87
87
  };
88
- output: boolean | undefined;
88
+ output: boolean;
89
89
  meta: object;
90
90
  }>;
91
91
  createCard: import("@trpc/server").TRPCMutationProcedure<{
@@ -168,6 +168,17 @@ export declare const flashcards: import("@trpc/server").TRPCBuiltRouter<{
168
168
  };
169
169
  output: {
170
170
  artifact: {
171
+ flashcards: {
172
+ id: string;
173
+ createdAt: Date;
174
+ artifactId: string;
175
+ tags: string[];
176
+ order: number;
177
+ front: string;
178
+ back: string;
179
+ acceptedAnswers: string[];
180
+ }[];
181
+ } & {
171
182
  id: string;
172
183
  createdAt: Date;
173
184
  updatedAt: Date;
@@ -61,18 +61,7 @@ export declare const paymentRouter: import("@trpc/server").TRPCBuiltRouter<{
61
61
  input: void;
62
62
  output: {
63
63
  usage: import("../services/billing/usage.service.js").UserUsage;
64
- limits: {
65
- id: string;
66
- planId: string;
67
- maxStorageBytes: bigint;
68
- maxWorksheets: number;
69
- maxFlashcards: number;
70
- maxPodcasts: number;
71
- maxStudyGuides: number;
72
- createdAt: Date;
73
- updatedAt: Date;
74
- isFallbackPlan: boolean;
75
- };
64
+ limits: import("../services/billing/usage.service.js").UserPlanLimits;
76
65
  hasActivePlan: boolean;
77
66
  };
78
67
  meta: object;
@@ -53,35 +53,26 @@ export declare const workspace: import("@trpc/server").TRPCBuiltRouter<{
53
53
  folders: {
54
54
  name: string;
55
55
  id: string;
56
- createdAt: Date;
57
56
  updatedAt: Date;
58
- ownerId: string;
59
57
  parentId: string | null;
60
58
  color: string;
61
59
  markerColor: string | null;
62
60
  }[];
63
- workspaces: ({
64
- uploads: {
65
- name: string;
66
- id: string;
67
- createdAt: Date;
68
- mimeType: string;
69
- }[];
70
- } & {
61
+ workspaces: {
71
62
  id: string;
72
- createdAt: Date;
73
63
  updatedAt: Date;
74
64
  title: string;
75
- description: string | null;
76
- ownerId: string;
77
65
  color: string;
78
66
  markerColor: string | null;
79
67
  icon: string;
80
68
  folderId: string | null;
81
- fileBeingAnalyzed: boolean;
82
- analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
83
- needsAnalysis: boolean;
84
- })[];
69
+ uploads: {
70
+ name: string;
71
+ id: string;
72
+ createdAt: Date;
73
+ mimeType: string;
74
+ }[];
75
+ }[];
85
76
  };
86
77
  meta: object;
87
78
  }>;
@@ -166,74 +157,43 @@ export declare const workspace: import("@trpc/server").TRPCBuiltRouter<{
166
157
  id: string;
167
158
  };
168
159
  output: {
160
+ id: string;
161
+ createdAt: Date;
162
+ updatedAt: Date;
163
+ title: string;
164
+ description: string | null;
169
165
  folder: {
170
166
  name: string;
171
167
  id: string;
172
- createdAt: Date;
173
- updatedAt: Date;
174
- ownerId: string;
175
- parentId: string | null;
176
168
  color: string;
177
- markerColor: string | null;
178
169
  } | null;
179
- artifacts: {
180
- id: string;
181
- createdAt: Date;
182
- updatedAt: Date;
183
- workspaceId: string;
184
- type: import("@prisma/client").$Enums.ArtifactType;
185
- title: string;
186
- isArchived: boolean;
187
- difficulty: import("@prisma/client").$Enums.Difficulty | null;
188
- estimatedTime: string | null;
189
- createdById: string | null;
190
- description: string | null;
191
- generating: boolean;
192
- generatingMetadata: import("@prisma/client/runtime/library").JsonValue | null;
193
- worksheetConfig: import("@prisma/client/runtime/library").JsonValue | null;
194
- imageObjectKey: string | null;
195
- }[];
170
+ ownerId: string;
171
+ color: string;
172
+ markerColor: string | null;
173
+ icon: string;
174
+ folderId: string | null;
175
+ fileBeingAnalyzed: boolean;
176
+ analysisProgress: import("@prisma/client/runtime/library").JsonValue;
177
+ needsAnalysis: boolean;
196
178
  uploads: {
197
- meta: import("@prisma/client/runtime/library").JsonValue | null;
198
179
  name: string;
199
180
  id: string;
200
181
  createdAt: Date;
201
- workspaceId: string | null;
202
- userId: string | null;
203
182
  mimeType: string;
204
183
  size: number;
205
- bucket: string | null;
206
184
  objectKey: string | null;
207
- url: string | null;
208
- checksum: string | null;
209
- aiTranscription: import("@prisma/client/runtime/library").JsonValue | null;
210
185
  }[];
211
- } & {
212
- id: string;
213
- createdAt: Date;
214
- updatedAt: Date;
215
- title: string;
216
- description: string | null;
217
- ownerId: string;
218
- color: string;
219
- markerColor: string | null;
220
- icon: string;
221
- folderId: string | null;
222
- fileBeingAnalyzed: boolean;
223
- analysisProgress: import("@prisma/client/runtime/library").JsonValue | null;
224
- needsAnalysis: boolean;
225
186
  };
226
187
  meta: object;
227
188
  }>;
228
189
  getStats: import("@trpc/server").TRPCQueryProcedure<{
229
190
  input: void;
230
- output: {
231
- workspaces: number;
232
- folders: number;
233
- lastUpdated: Date | undefined;
234
- spaceUsed: number;
235
- spaceTotal: number;
236
- };
191
+ output: import("../services/billing/usage.service.js").AccountStats;
192
+ meta: object;
193
+ }>;
194
+ getAccountSummary: import("@trpc/server").TRPCQueryProcedure<{
195
+ input: void;
196
+ output: import("../services/billing/usage.service.js").AccountSummary;
237
197
  meta: object;
238
198
  }>;
239
199
  getStudyAnalytics: import("@trpc/server").TRPCQueryProcedure<{
@@ -40,6 +40,7 @@ export const workspace = router({
40
40
  .input(z.object({ id: z.string() }))
41
41
  .query(({ ctx, input }) => new WorkspaceService(ctx.db).get(ctx.session.user.id, input.id)),
42
42
  getStats: authedProcedure.query(({ ctx }) => new WorkspaceService(ctx.db).getStats(ctx.session.user.id)),
43
+ getAccountSummary: authedProcedure.query(({ ctx }) => new WorkspaceService(ctx.db).getAccountSummary(ctx.session.user.id)),
43
44
  getStudyAnalytics: authedProcedure.query(({ ctx }) => new WorkspaceAnalyticsService(ctx.db).getStudyAnalytics(ctx.session.user.id)),
44
45
  update: authedProcedure
45
46
  .input(z.object({
@@ -32,18 +32,7 @@ export declare class PaymentService extends BaseService {
32
32
  }>;
33
33
  getUsageOverview(userId: string): Promise<{
34
34
  usage: import("./usage.service.js").UserUsage;
35
- limits: {
36
- id: string;
37
- planId: string;
38
- maxStorageBytes: bigint;
39
- maxWorksheets: number;
40
- maxFlashcards: number;
41
- maxPodcasts: number;
42
- maxStudyGuides: number;
43
- createdAt: Date;
44
- updatedAt: Date;
45
- isFallbackPlan: boolean;
46
- };
35
+ limits: import("./usage.service.js").UserPlanLimits;
47
36
  hasActivePlan: boolean;
48
37
  }>;
49
38
  getResourcePrices(): import("@prisma/client").Prisma.PrismaPromise<{
@@ -2,7 +2,7 @@ import { TRPCError } from '@trpc/server';
2
2
  import { BaseService } from '../base.service.js';
3
3
  import { stripe } from '../../lib/stripe.js';
4
4
  import { env } from '../../lib/env.js';
5
- import { getUserUsage, getUserPlanLimits } from './usage.service.js';
5
+ import { getAccountSummary } from './usage.service.js';
6
6
  import { notifyPaymentSucceeded, notifySubscriptionActivated, notifySubscriptionPaymentSucceeded, } from '../notifications/notification.service.js';
7
7
  import { upsertSubscriptionFromStripe } from './subscription.service.js';
8
8
  /** Stripe Checkout URLs contain the session id (cs_…); we only store the URL in DB. */
@@ -356,11 +356,8 @@ export class PaymentService extends BaseService {
356
356
  }
357
357
  }
358
358
  async getUsageOverview(userId) {
359
- const [usage, limits] = await Promise.all([
360
- getUserUsage(userId),
361
- getUserPlanLimits(userId),
362
- ]);
363
- return { usage, limits, hasActivePlan: !!limits };
359
+ const { usage, limits, hasActivePlan } = await getAccountSummary(userId);
360
+ return { usage, limits, hasActivePlan };
364
361
  }
365
362
  getResourcePrices() {
366
363
  return this.db.resourcePrice.findMany();
@@ -5,15 +5,14 @@ export interface UserUsage {
5
5
  podcasts: number;
6
6
  storageBytes: number;
7
7
  }
8
- /**
9
- * Counts all resources consumed by a user across the platform.
10
- */
11
- export declare function getUserUsage(userId: string): Promise<UserUsage>;
12
- /**
13
- * Retrieves the specific plan limits for a user based on their active subscription
14
- * PLUS any extra credits purchased.
15
- */
16
- export declare function getUserPlanLimits(userId: string): Promise<{
8
+ export interface AccountStats {
9
+ workspaces: number;
10
+ folders: number;
11
+ lastUpdated: Date | null;
12
+ spaceUsed: number;
13
+ spaceTotal: number;
14
+ }
15
+ export interface UserPlanLimits {
17
16
  id: string;
18
17
  planId: string;
19
18
  maxStorageBytes: bigint;
@@ -24,4 +23,25 @@ export declare function getUserPlanLimits(userId: string): Promise<{
24
23
  createdAt: Date;
25
24
  updatedAt: Date;
26
25
  isFallbackPlan: boolean;
27
- }>;
26
+ }
27
+ export interface AccountSummary {
28
+ stats: AccountStats;
29
+ usage: UserUsage;
30
+ limits: UserPlanLimits;
31
+ hasActivePlan: boolean;
32
+ }
33
+ /** Bust cached usage/limit reads after creates, deletes, or billing changes. */
34
+ export declare function invalidateUserBillingCache(userId: string): void;
35
+ /**
36
+ * Counts all resources consumed by a user across the platform.
37
+ */
38
+ export declare function getUserUsage(userId: string): Promise<UserUsage>;
39
+ /**
40
+ * Retrieves the specific plan limits for a user based on their active subscription
41
+ * PLUS any extra credits purchased.
42
+ */
43
+ export declare function getUserPlanLimits(userId: string): Promise<UserPlanLimits>;
44
+ /**
45
+ * Sidebar/dashboard summary: stats + usage + limits in one parallel DB round-trip batch.
46
+ */
47
+ export declare function getAccountSummary(userId: string): Promise<AccountSummary>;