@vestcards/server-types 1.2.0 → 1.3.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 (30) hide show
  1. package/dist/apps/server/src/app.d.ts +152 -1
  2. package/dist/apps/server/src/libs/mappers.d.ts +5 -0
  3. package/dist/apps/server/src/modules/auth/lib.d.ts +18 -14
  4. package/dist/apps/server/src/modules/card/index.d.ts +90 -1
  5. package/dist/apps/server/src/modules/card/model.d.ts +17 -0
  6. package/dist/apps/server/src/modules/card/service.d.ts +8 -1
  7. package/dist/apps/server/src/modules/study/service.d.ts +3 -3
  8. package/dist/apps/server/src/modules/user/errors.d.ts +3 -0
  9. package/dist/apps/server/src/modules/user/index.d.ts +62 -0
  10. package/dist/apps/server/src/modules/user/model.d.ts +6 -0
  11. package/dist/apps/server/src/modules/user/service.d.ts +4 -0
  12. package/dist/apps/server/src/tests/helpers/fixtures.d.ts +1 -0
  13. package/dist/apps/server/src/utils/api.d.ts +1 -1
  14. package/dist/apps/server/src/utils/select.d.ts +17 -0
  15. package/dist/packages/db-core/src/schema/auth.d.ts +60 -0
  16. package/dist/packages/db-core/src/schema/deck.d.ts +17 -0
  17. package/dist/packages/db-core/src/schema/index.d.ts +1 -0
  18. package/dist/packages/email-core/src/templates/deleteAccountRequest.d.ts +8 -0
  19. package/dist/packages/email-core/src/templates/footer.d.ts +1 -1
  20. package/dist/packages/email-core/src/templates/index.d.ts +2 -0
  21. package/dist/packages/shared/src/spaced-repetition/config.d.ts +5 -0
  22. package/dist/packages/shared/src/spaced-repetition/fsrs.d.ts +13 -0
  23. package/dist/packages/shared/src/spaced-repetition/index.d.ts +4 -0
  24. package/dist/packages/shared/src/spaced-repetition/session-manager.d.ts +25 -0
  25. package/dist/packages/shared/src/spaced-repetition/utils.d.ts +26 -0
  26. package/dist/packages/shared/src/theme/tokens/border.d.ts +1 -1
  27. package/dist/packages/shared/src/types/deck.d.ts +10 -0
  28. package/package.json +1 -1
  29. package/dist/apps/server/src/modules/study/lib/fsrs.d.ts +0 -12
  30. /package/dist/apps/server/src/modules/study/{lib/constants.d.ts → constants.d.ts} +0 -0
@@ -455,8 +455,8 @@ declare const app: Elysia<"", {
455
455
  createdAt: Date;
456
456
  updatedAt: Date;
457
457
  userId: string;
458
- content: string;
459
458
  cardId: string;
459
+ content: string;
460
460
  } | null;
461
461
  422: {
462
462
  type: "validation";
@@ -479,7 +479,36 @@ declare const app: Elysia<"", {
479
479
  post: {
480
480
  body: {
481
481
  type: string;
482
+ cardId: string;
482
483
  content: string;
484
+ };
485
+ params: {};
486
+ query: {};
487
+ headers: {};
488
+ response: {
489
+ 200: {
490
+ success: boolean;
491
+ };
492
+ 422: {
493
+ type: "validation";
494
+ on: string;
495
+ summary?: string;
496
+ message?: string;
497
+ found?: unknown;
498
+ property?: string;
499
+ expected?: string;
500
+ };
501
+ };
502
+ };
503
+ };
504
+ };
505
+ };
506
+ } & {
507
+ v1: {
508
+ card: {
509
+ suspend: {
510
+ post: {
511
+ body: {
483
512
  cardId: string;
484
513
  };
485
514
  params: {};
@@ -503,6 +532,66 @@ declare const app: Elysia<"", {
503
532
  };
504
533
  };
505
534
  };
535
+ } & {
536
+ v1: {
537
+ card: {
538
+ suspend: {
539
+ delete: {
540
+ body: {};
541
+ params: {};
542
+ query: {
543
+ cardId: string;
544
+ };
545
+ headers: {};
546
+ response: {
547
+ 200: {
548
+ success: boolean;
549
+ };
550
+ 422: {
551
+ type: "validation";
552
+ on: string;
553
+ summary?: string;
554
+ message?: string;
555
+ found?: unknown;
556
+ property?: string;
557
+ expected?: string;
558
+ };
559
+ };
560
+ };
561
+ };
562
+ };
563
+ };
564
+ } & {
565
+ v1: {
566
+ card: {
567
+ "user-state": {
568
+ get: {
569
+ body: {};
570
+ params: {};
571
+ query: {
572
+ deckId: string;
573
+ };
574
+ headers: {};
575
+ response: {
576
+ 200: {
577
+ cardId: string;
578
+ suspended: boolean;
579
+ toReview: boolean;
580
+ }[];
581
+ 422: {
582
+ type: "validation";
583
+ on: string;
584
+ summary?: string;
585
+ message?: string;
586
+ found?: unknown;
587
+ property?: string;
588
+ expected?: string;
589
+ };
590
+ };
591
+ };
592
+ };
593
+ };
594
+ };
506
595
  } & {
507
596
  v1: {
508
597
  decks: {
@@ -1165,6 +1254,68 @@ declare const app: Elysia<"", {
1165
1254
  };
1166
1255
  };
1167
1256
  };
1257
+ } & {
1258
+ v1: {
1259
+ user: {
1260
+ account: {
1261
+ "delete-request": {
1262
+ post: {
1263
+ body: {
1264
+ email: string;
1265
+ };
1266
+ params: {};
1267
+ query: {};
1268
+ headers: {};
1269
+ response: {
1270
+ 200: {
1271
+ success: boolean;
1272
+ };
1273
+ 422: {
1274
+ type: "validation";
1275
+ on: string;
1276
+ summary?: string;
1277
+ message?: string;
1278
+ found?: unknown;
1279
+ property?: string;
1280
+ expected?: string;
1281
+ };
1282
+ };
1283
+ };
1284
+ };
1285
+ };
1286
+ };
1287
+ };
1288
+ } & {
1289
+ v1: {
1290
+ user: {
1291
+ account: {
1292
+ "confirm-delete": {
1293
+ post: {
1294
+ body: {
1295
+ token: string;
1296
+ };
1297
+ params: {};
1298
+ query: {};
1299
+ headers: {};
1300
+ response: {
1301
+ 200: {
1302
+ success: boolean;
1303
+ };
1304
+ 422: {
1305
+ type: "validation";
1306
+ on: string;
1307
+ summary?: string;
1308
+ message?: string;
1309
+ found?: unknown;
1310
+ property?: string;
1311
+ expected?: string;
1312
+ };
1313
+ };
1314
+ };
1315
+ };
1316
+ };
1317
+ };
1318
+ };
1168
1319
  } & {
1169
1320
  v1: {
1170
1321
  user: {
@@ -0,0 +1,5 @@
1
+ import type { CardReview, NewCardReview, NewReviewLog } from '@vestcards/db-core';
2
+ import type { ICardReview, IReviewLog } from '@vestcards/shared';
3
+ export declare function cardReviewToICardReview(row: CardReview): ICardReview;
4
+ export declare function iCardReviewToNewCardReview(card: ICardReview): NewCardReview;
5
+ export declare function iReviewLogToNewReviewLog(log: IReviewLog): NewReviewLog;
@@ -1,8 +1,9 @@
1
1
  import { UserRole } from '@vestcards/shared';
2
2
  import { type BetterAuthOptions } from 'better-auth';
3
3
  export declare const auth: import("better-auth").Auth<{
4
- plugins: [{
4
+ plugins: ({
5
5
  id: "open-api";
6
+ version: string;
6
7
  endpoints: {
7
8
  generateOpenAPISchema: import("better-call").StrictEndpoint<"/open-api/generate-schema", {
8
9
  method: "GET";
@@ -52,8 +53,9 @@ export declare const auth: import("better-auth").Auth<{
52
53
  }, Response>;
53
54
  };
54
55
  options: NoInfer<import("better-auth/plugins").OpenAPIOptions>;
55
- }, {
56
+ } | {
56
57
  id: "expo";
58
+ version: string;
57
59
  init: (ctx: import("better-auth").AuthContext) => {
58
60
  options: {
59
61
  trustedOrigins: string[];
@@ -71,10 +73,10 @@ export declare const auth: import("better-auth").Auth<{
71
73
  endpoints: {
72
74
  expoAuthorizationProxy: import("better-call").StrictEndpoint<"/expo-authorization-proxy", {
73
75
  method: "GET";
74
- query: import("better-auth").ZodObject<{
75
- authorizationURL: import("better-auth").ZodString;
76
- oauthState: import("better-auth").ZodOptional<import("better-auth").ZodString>;
77
- }, import("better-auth").$strip>;
76
+ query: import("zod").ZodObject<{
77
+ authorizationURL: import("zod").ZodString;
78
+ oauthState: import("zod").ZodOptional<import("zod").ZodString>;
79
+ }, import("zod/v4/core").$strip>;
78
80
  metadata: {
79
81
  readonly scope: "server";
80
82
  };
@@ -94,8 +96,9 @@ export declare const auth: import("better-auth").Auth<{
94
96
  }>;
95
97
  };
96
98
  options: import("@better-auth/expo").ExpoOptions | undefined;
97
- }, {
99
+ } | {
98
100
  id: "bearer";
101
+ version: string;
99
102
  hooks: {
100
103
  before: {
101
104
  matcher(context: import("better-auth").HookEndpointContext): boolean;
@@ -111,8 +114,9 @@ export declare const auth: import("better-auth").Auth<{
111
114
  }[];
112
115
  };
113
116
  options: import("better-auth/plugins").BearerOptions | undefined;
114
- }, {
117
+ } | {
115
118
  id: "custom-session";
119
+ version: string;
116
120
  hooks: {
117
121
  after: {
118
122
  matcher: (ctx: import("better-auth").HookEndpointContext) => boolean;
@@ -147,10 +151,10 @@ export declare const auth: import("better-auth").Auth<{
147
151
  endpoints: {
148
152
  getSession: import("better-call").StrictEndpoint<"/get-session", {
149
153
  method: "GET";
150
- query: import("better-auth").ZodOptional<import("better-auth").ZodObject<{
151
- disableCookieCache: import("better-auth").ZodOptional<import("better-auth").ZodUnion<[import("better-auth").ZodBoolean, import("better-auth").ZodPipe<import("better-auth").ZodString, import("better-auth").ZodTransform<boolean, string>>]>>;
152
- disableRefresh: import("better-auth").ZodOptional<import("better-auth").ZodBoolean>;
153
- }, import("better-auth").$strip>>;
154
+ query: import("zod").ZodOptional<import("zod").ZodObject<{
155
+ disableCookieCache: import("zod").ZodOptional<import("zod").ZodUnion<[import("zod").ZodBoolean, import("zod").ZodPipe<import("zod").ZodString, import("zod").ZodTransform<boolean, string>>]>>;
156
+ disableRefresh: import("zod").ZodOptional<import("zod").ZodBoolean>;
157
+ }, import("zod/v4/core").$strip>>;
154
158
  metadata: {
155
159
  CUSTOM_SESSION: boolean;
156
160
  openapi: {
@@ -230,11 +234,11 @@ export declare const auth: import("better-auth").Auth<{
230
234
  };
231
235
  };
232
236
  options: import("better-auth/plugins").CustomSessionPluginOptions | undefined;
233
- }];
237
+ })[];
234
238
  baseURL: string;
235
239
  basePath: string;
236
240
  database: (options: BetterAuthOptions) => import("better-auth").DBAdapter<BetterAuthOptions>;
237
- secondaryStorage: import("better-auth").SecondaryStorage;
241
+ secondaryStorage: import("better-auth").SecondaryStorage | undefined;
238
242
  emailAndPassword: {
239
243
  enabled: true;
240
244
  autoSignIn: true;
@@ -397,8 +397,8 @@ export declare const cardModule: Elysia<"/v1/card", {
397
397
  createdAt: Date;
398
398
  updatedAt: Date;
399
399
  userId: string;
400
- content: string;
401
400
  cardId: string;
401
+ content: string;
402
402
  } | null;
403
403
  422: {
404
404
  type: "validation";
@@ -421,7 +421,36 @@ export declare const cardModule: Elysia<"/v1/card", {
421
421
  post: {
422
422
  body: {
423
423
  type: string;
424
+ cardId: string;
424
425
  content: string;
426
+ };
427
+ params: {};
428
+ query: {};
429
+ headers: {};
430
+ response: {
431
+ 200: {
432
+ success: boolean;
433
+ };
434
+ 422: {
435
+ type: "validation";
436
+ on: string;
437
+ summary?: string;
438
+ message?: string;
439
+ found?: unknown;
440
+ property?: string;
441
+ expected?: string;
442
+ };
443
+ };
444
+ };
445
+ };
446
+ };
447
+ };
448
+ } & {
449
+ v1: {
450
+ card: {
451
+ suspend: {
452
+ post: {
453
+ body: {
425
454
  cardId: string;
426
455
  };
427
456
  params: {};
@@ -445,6 +474,66 @@ export declare const cardModule: Elysia<"/v1/card", {
445
474
  };
446
475
  };
447
476
  };
477
+ } & {
478
+ v1: {
479
+ card: {
480
+ suspend: {
481
+ delete: {
482
+ body: {};
483
+ params: {};
484
+ query: {
485
+ cardId: string;
486
+ };
487
+ headers: {};
488
+ response: {
489
+ 200: {
490
+ success: boolean;
491
+ };
492
+ 422: {
493
+ type: "validation";
494
+ on: string;
495
+ summary?: string;
496
+ message?: string;
497
+ found?: unknown;
498
+ property?: string;
499
+ expected?: string;
500
+ };
501
+ };
502
+ };
503
+ };
504
+ };
505
+ };
506
+ } & {
507
+ v1: {
508
+ card: {
509
+ "user-state": {
510
+ get: {
511
+ body: {};
512
+ params: {};
513
+ query: {
514
+ deckId: string;
515
+ };
516
+ headers: {};
517
+ response: {
518
+ 200: {
519
+ cardId: string;
520
+ suspended: boolean;
521
+ toReview: boolean;
522
+ }[];
523
+ 422: {
524
+ type: "validation";
525
+ on: string;
526
+ summary?: string;
527
+ message?: string;
528
+ found?: unknown;
529
+ property?: string;
530
+ expected?: string;
531
+ };
532
+ };
533
+ };
534
+ };
535
+ };
536
+ };
448
537
  }, {
449
538
  derive: {};
450
539
  resolve: {};
@@ -1,4 +1,9 @@
1
1
  export declare namespace CardModel {
2
+ /**
3
+ * Whether a card counts as "to review" in the deck view.
4
+ * Uses end-of-day cutoff to match StudyService.getReviewCards behaviour.
5
+ */
6
+ function isCardToReview(state: string, suspended: boolean, due: Date | null, now?: Date): boolean;
2
7
  const reportBody: import("@sinclair/typebox").TObject<{
3
8
  cardId: import("@sinclair/typebox").TString;
4
9
  content: import("@sinclair/typebox").TString;
@@ -9,4 +14,16 @@ export declare namespace CardModel {
9
14
  cardId: import("@sinclair/typebox").TString;
10
15
  }>;
11
16
  type ReportQuery = typeof reportQuery.static;
17
+ const suspendBody: import("@sinclair/typebox").TObject<{
18
+ cardId: import("@sinclair/typebox").TString;
19
+ }>;
20
+ type SuspendBody = typeof suspendBody.static;
21
+ const suspendQuery: import("@sinclair/typebox").TObject<{
22
+ cardId: import("@sinclair/typebox").TString;
23
+ }>;
24
+ type SuspendQuery = typeof suspendQuery.static;
25
+ const userStateQuery: import("@sinclair/typebox").TObject<{
26
+ deckId: import("@sinclair/typebox").TString;
27
+ }>;
28
+ type UserStateQuery = typeof userStateQuery.static;
12
29
  }
@@ -1,6 +1,13 @@
1
1
  import type { CardDemand } from '@vestcards/db-core';
2
- import type { CardModel } from './model';
2
+ import { CardModel } from './model';
3
3
  export declare abstract class CardService {
4
4
  static getReport(userId: string, cardId: string): Promise<CardDemand | null>;
5
+ static suspendCard(userId: string, cardId: string): Promise<boolean>;
6
+ static unsuspendCard(userId: string, cardId: string): Promise<boolean>;
7
+ static getUserState(userId: string, deckId: string): Promise<{
8
+ cardId: string;
9
+ suspended: boolean;
10
+ toReview: boolean;
11
+ }[]>;
5
12
  static reportCard(userId: string, body: CardModel.ReportBody): Promise<void>;
6
13
  }
@@ -9,11 +9,11 @@ export declare abstract class StudyService {
9
9
  static getReviewCards(userId: string, type: StudyType, id?: string, maxToFetch?: number): Promise<import("@vestcards/shared").ISessionCard[]>;
10
10
  static getNewCards(userId: string, allocatedNewForToday: number, type: StudyType, id?: string): Promise<import("@vestcards/shared").ISessionCard[]>;
11
11
  static getUserDeckStudy(userId: string, deckId: string): Promise<{
12
- suspended: boolean;
13
12
  id: string;
14
- createdAt: Date;
15
- userId: string;
16
13
  deckId: string;
14
+ userId: string;
15
+ suspended: boolean;
16
+ createdAt: Date;
17
17
  }>;
18
18
  static getCardReview(userId: string, cardId: string): Promise<CardReview>;
19
19
  static gradeCard(userId: string, input: StudyModel.GradeCardBody): Promise<void>;
@@ -0,0 +1,3 @@
1
+ export declare class InvalidTokenError extends Error {
2
+ constructor();
3
+ }
@@ -457,6 +457,68 @@ export declare const userModule: Elysia<"/v1/user", {
457
457
  };
458
458
  };
459
459
  };
460
+ } & {
461
+ v1: {
462
+ user: {
463
+ account: {
464
+ "delete-request": {
465
+ post: {
466
+ body: {
467
+ email: string;
468
+ };
469
+ params: {};
470
+ query: {};
471
+ headers: {};
472
+ response: {
473
+ 200: {
474
+ success: boolean;
475
+ };
476
+ 422: {
477
+ type: "validation";
478
+ on: string;
479
+ summary?: string;
480
+ message?: string;
481
+ found?: unknown;
482
+ property?: string;
483
+ expected?: string;
484
+ };
485
+ };
486
+ };
487
+ };
488
+ };
489
+ };
490
+ };
491
+ } & {
492
+ v1: {
493
+ user: {
494
+ account: {
495
+ "confirm-delete": {
496
+ post: {
497
+ body: {
498
+ token: string;
499
+ };
500
+ params: {};
501
+ query: {};
502
+ headers: {};
503
+ response: {
504
+ 200: {
505
+ success: boolean;
506
+ };
507
+ 422: {
508
+ type: "validation";
509
+ on: string;
510
+ summary?: string;
511
+ message?: string;
512
+ found?: unknown;
513
+ property?: string;
514
+ expected?: string;
515
+ };
516
+ };
517
+ };
518
+ };
519
+ };
520
+ };
521
+ };
460
522
  } & {
461
523
  v1: {
462
524
  user: {
@@ -15,6 +15,12 @@ export declare namespace TopicModel {
15
15
  }
16
16
  }
17
17
  export declare namespace UserModel {
18
+ const deleteRequestBody: import("@sinclair/typebox").TObject<{
19
+ email: import("@sinclair/typebox").TString;
20
+ }>;
21
+ const confirmDeleteBody: import("@sinclair/typebox").TObject<{
22
+ token: import("@sinclair/typebox").TString;
23
+ }>;
18
24
  const updateSettingsBody: import("@sinclair/typebox").TObject<{
19
25
  learnDailyLimit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TInteger>;
20
26
  university: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TInteger, import("@sinclair/typebox").TNull]>>;
@@ -1,5 +1,6 @@
1
1
  import { MembershipStatus } from '@vestcards/shared';
2
2
  export declare abstract class UserService {
3
+ private static getUserById;
3
4
  static getLearnDailyLimit(userId: string): Promise<number>;
4
5
  static getProfile(userId: string): Promise<{
5
6
  id: string;
@@ -23,7 +24,10 @@ export declare abstract class UserService {
23
24
  course?: number;
24
25
  dayTimeReminder?: string;
25
26
  }): Promise<void>;
27
+ private static sendDeletionEmail;
26
28
  static deleteAccount(userId: string): Promise<void>;
29
+ static requestDeletion(email: string): Promise<void>;
30
+ static confirmDeletion(token: string): Promise<void>;
27
31
  static getMembership(userId: string): Promise<{
28
32
  status: MembershipStatus;
29
33
  expiresAt: Date | null;
@@ -125,6 +125,7 @@ export declare function createCardReview(data: {
125
125
  lapses?: number;
126
126
  last_review?: Date | null;
127
127
  }): Promise<{
128
+ suspended: boolean;
128
129
  id: string;
129
130
  createdAt: Date;
130
131
  cardId: string;
@@ -27,5 +27,5 @@ export declare const paginateResults: <T, TOtherData = {}>(data: T[], { size, to
27
27
  size: number;
28
28
  total: number;
29
29
  }, aggregatedData?: TOtherData) => Paginated<T, TOtherData>;
30
- export { paginationModel, searchModel, sortModel };
31
30
  export type { Paginated, Pagination, Search, Sort };
31
+ export { paginationModel, searchModel, sortModel };
@@ -331,6 +331,23 @@ export declare const sessionCardSelect: {
331
331
  identity: undefined;
332
332
  generated: undefined;
333
333
  }, {}, {}>;
334
+ suspended: import("drizzle-orm/pg-core").PgColumn<{
335
+ name: "suspended";
336
+ tableName: "card_review";
337
+ dataType: "boolean";
338
+ columnType: "PgBoolean";
339
+ data: boolean;
340
+ driverParam: boolean;
341
+ notNull: true;
342
+ hasDefault: true;
343
+ isPrimaryKey: false;
344
+ isAutoincrement: false;
345
+ hasRuntimeDefault: false;
346
+ enumValues: undefined;
347
+ baseColumn: never;
348
+ identity: undefined;
349
+ generated: undefined;
350
+ }, {}, {}>;
334
351
  createdAt: import("drizzle-orm/pg-core").PgColumn<{
335
352
  name: "created_at";
336
353
  tableName: "card_review";
@@ -336,6 +336,66 @@ export declare const session: import("drizzle-orm/pg-core").PgTableWithColumns<{
336
336
  identity: undefined;
337
337
  generated: undefined;
338
338
  }, {}, {}>;
339
+ entitlements: import("drizzle-orm/pg-core").PgColumn<{
340
+ name: "entitlements";
341
+ tableName: "session";
342
+ dataType: "array";
343
+ columnType: "PgArray";
344
+ data: string[];
345
+ driverParam: string | string[];
346
+ notNull: true;
347
+ hasDefault: true;
348
+ isPrimaryKey: false;
349
+ isAutoincrement: false;
350
+ hasRuntimeDefault: false;
351
+ enumValues: [string, ...string[]];
352
+ baseColumn: import("drizzle-orm").Column<{
353
+ name: "entitlements";
354
+ tableName: "session";
355
+ dataType: "string";
356
+ columnType: "PgText";
357
+ data: string;
358
+ driverParam: string;
359
+ notNull: false;
360
+ hasDefault: false;
361
+ isPrimaryKey: false;
362
+ isAutoincrement: false;
363
+ hasRuntimeDefault: false;
364
+ enumValues: [string, ...string[]];
365
+ baseColumn: never;
366
+ identity: undefined;
367
+ generated: undefined;
368
+ }, {}, {}>;
369
+ identity: undefined;
370
+ generated: undefined;
371
+ }, {}, {
372
+ baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
373
+ name: "entitlements";
374
+ dataType: "string";
375
+ columnType: "PgText";
376
+ data: string;
377
+ enumValues: [string, ...string[]];
378
+ driverParam: string;
379
+ }, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
380
+ size: undefined;
381
+ }>;
382
+ completedProfile: import("drizzle-orm/pg-core").PgColumn<{
383
+ name: "completed_profile";
384
+ tableName: "session";
385
+ dataType: "boolean";
386
+ columnType: "PgBoolean";
387
+ data: boolean;
388
+ driverParam: boolean;
389
+ notNull: true;
390
+ hasDefault: true;
391
+ isPrimaryKey: false;
392
+ isAutoincrement: false;
393
+ hasRuntimeDefault: false;
394
+ enumValues: undefined;
395
+ baseColumn: never;
396
+ identity: undefined;
397
+ generated: undefined;
398
+ }, {}, {}>;
339
399
  };
340
400
  dialect: "pg";
341
401
  }>;
@@ -920,6 +920,23 @@ export declare const cardReviews: import("drizzle-orm/pg-core").PgTableWithColum
920
920
  identity: undefined;
921
921
  generated: undefined;
922
922
  }, {}, {}>;
923
+ suspended: import("drizzle-orm/pg-core").PgColumn<{
924
+ name: "suspended";
925
+ tableName: "card_review";
926
+ dataType: "boolean";
927
+ columnType: "PgBoolean";
928
+ data: boolean;
929
+ driverParam: boolean;
930
+ notNull: true;
931
+ hasDefault: true;
932
+ isPrimaryKey: false;
933
+ isAutoincrement: false;
934
+ hasRuntimeDefault: false;
935
+ enumValues: undefined;
936
+ baseColumn: never;
937
+ identity: undefined;
938
+ generated: undefined;
939
+ }, {}, {}>;
923
940
  userDeckStudyId: import("drizzle-orm/pg-core").PgColumn<{
924
941
  name: "user_deck_study_id";
925
942
  tableName: "card_review";
@@ -1,3 +1,4 @@
1
+ export * from '../views/deck';
1
2
  export * from './auth';
2
3
  export * from './blog';
3
4
  export * from './data';
@@ -0,0 +1,8 @@
1
+ export interface EmailDeleteAccountRequestValues {
2
+ userName: string;
3
+ confirmUrl: string;
4
+ }
5
+ export declare const EMAILDeleteAccountRequest: ({ userName, confirmUrl }: EmailDeleteAccountRequestValues) => {
6
+ subject: string;
7
+ html: string;
8
+ };
@@ -1 +1 @@
1
- export declare const EMAILFooter = "\n <div class=\"mt\">\n <img src=\"https://storage.googleapis.com/vestcards-public-bucket/logos/logoIconPrimary.png\" width=\"35\" height=\"25\" alt=\"Logo VestCards\" title=\"Logo VestCards\" style=\"display: block;\">\n <p class=\"text-gray\">\n <a href=\"https://vestcards.com.br\" style=\"color: gray !important;\">Vestcards</a>, 2025. Todos os direitos reservados.\n </p>\n </div>\n";
1
+ export declare const EMAILFooter: string;
@@ -2,6 +2,7 @@ import { type EmailAccountDeletionValues } from './accountDeletion';
2
2
  import { type EmailAdminPurchaseAlertValues } from './adminPurchaseAlert';
3
3
  import { type EmailCardDemandValues } from './cardDemand';
4
4
  import { type EmailCardDemandActionValues } from './cardDemandAction';
5
+ import { type EmailDeleteAccountRequestValues } from './deleteAccountRequest';
5
6
  import { type EmailConfirmAccountValues } from './emailVerification';
6
7
  import { type EmailForgotPasswordValues } from './forgotPassword';
7
8
  import { type EmailPurchaseConfirmationValues } from './purchaseConfirmation';
@@ -19,6 +20,7 @@ export interface EmailTemplateFunctions {
19
20
  PurchaseConfirmation: (data: EmailPurchaseConfirmationValues) => EmailTemplateType;
20
21
  AdminPurchaseAlert: (data: EmailAdminPurchaseAlertValues) => EmailTemplateType;
21
22
  AccountDeletion: (data: EmailAccountDeletionValues) => EmailTemplateType;
23
+ DeleteAccountRequest: (data: EmailDeleteAccountRequestValues) => EmailTemplateType;
22
24
  CardDemand: (data: EmailCardDemandValues) => EmailTemplateType;
23
25
  CardDemandAction: (data: EmailCardDemandActionValues) => EmailTemplateType;
24
26
  }
@@ -0,0 +1,5 @@
1
+ export declare const FSRS_RETENTION = 0.9;
2
+ export declare const MAX_CARDS_TO_FETCH = 50;
3
+ export declare const MAX_LEARN_PER_DAY = 200;
4
+ export declare const MAX_NEW_PER_DECK_DAY = 10;
5
+ export declare const THRESHOLD_CARDS_FOR_REFETCH = 10;
@@ -0,0 +1,13 @@
1
+ import { type Card as FSRSCard } from 'ts-fsrs';
2
+ import { type ReviewRating } from '../types/fsrs';
3
+ import type { ICardReview, IReviewLog } from '../types/study';
4
+ export declare const gradeCard: (card: ICardReview, rating: ReviewRating, durationInSeconds: number) => {
5
+ nextCard: ICardReview;
6
+ reviewLog: IReviewLog;
7
+ };
8
+ /**
9
+ * Give a {@link ICardReview}, returns the review {@link Date} for each {@link ReviewRating}.
10
+ */
11
+ export declare function getReviewDateForEachRating(card: ICardReview): Record<ReviewRating, Date>;
12
+ export declare function mergeFsrsCard(fsrsCard: FSRSCard, card: ICardReview): ICardReview;
13
+ export declare function newCardReview(userDeckStudyId: string, cardId: string): ICardReview;
@@ -0,0 +1,4 @@
1
+ export * from './config';
2
+ export * from './fsrs';
3
+ export * from './session-manager';
4
+ export * from './utils';
@@ -0,0 +1,25 @@
1
+ import type { ReviewRating } from '../types/fsrs';
2
+ import type { ISessionCard, ISessionData } from '../types/study';
3
+ export declare const RECENTLY_REVIEWED_SET_SIZE = 5;
4
+ export declare const defaultSessionData: ISessionData;
5
+ export interface SessionProgress {
6
+ reviewsCompleted: number;
7
+ correctGrades: number;
8
+ }
9
+ /**
10
+ * Encapsulates the stateful logic shared across platforms for a flashcard study session:
11
+ * - Recently-reviewed set (prevents immediate card repetition)
12
+ * - Progress counters (total reviews, correct grades)
13
+ * - Next card selection (delegates to getNextCardFromSession)
14
+ *
15
+ * Usage: hold an instance in a ref (React/RN), call nextCard() on each render
16
+ * and onRate() whenever the user grades a card.
17
+ */
18
+ export declare class FlashcardSessionManager {
19
+ private recentlyReviewed;
20
+ private _reviewsCompleted;
21
+ private _correctGrades;
22
+ getProgress(): SessionProgress;
23
+ nextCard(data: ISessionData): ISessionCard | undefined;
24
+ onRate(cardId: string, rating: ReviewRating): SessionProgress;
25
+ }
@@ -0,0 +1,26 @@
1
+ import type { ReviewRating } from '../types/fsrs';
2
+ import type { ISessionCard, ISessionData } from '../types/study';
3
+ export declare function getNextCardFromSession(data: ISessionData, recentlyReviewed?: Set<string>): ISessionCard | undefined;
4
+ /**
5
+ * Remove a card from session data without grading it (e.g. suspend + skip).
6
+ * Properly decrements the matching stats bucket and total.
7
+ */
8
+ export declare function skipCardFromSessionData(data: ISessionData, cardId: string): ISessionData;
9
+ /**
10
+ * Computes the next session data after grading a card.
11
+ *
12
+ * Simulates the card grading and if the next review is within ~10 minutes,
13
+ * keeps the card in the session and updates the respective counter.
14
+ *
15
+ * If the card's state is `New`, then the card exists in `newCards`.
16
+ * If the card is any other state, then the card exists in `reviewCards`.
17
+ *
18
+ * @param grade The grade given to the card
19
+ * @param data The current session data
20
+ * @param cardId The id of the card that was graded
21
+ */
22
+ export declare function removeCardFromSessionData(grade: ReviewRating, data: ISessionData, cardId: string): ISessionData;
23
+ /**
24
+ * Gets the card from the session data.
25
+ */
26
+ export declare function getSessionCard(sessionData: ISessionData, cardId: string): ISessionCard | undefined;
@@ -8,4 +8,4 @@ declare const borderRadius: {
8
8
  button: string;
9
9
  input: string;
10
10
  };
11
- export { borderWidth, borderRadius };
11
+ export { borderRadius, borderWidth };
@@ -50,3 +50,13 @@ export interface IDeckDetail extends IDeck {
50
50
  ownerInfo: IOwnerInfo;
51
51
  cards: ICard[];
52
52
  }
53
+ export interface ICardUserState {
54
+ cardId: string;
55
+ suspended: boolean;
56
+ toReview: boolean;
57
+ }
58
+ export interface ICardMedia {
59
+ id: string;
60
+ side: 'front' | 'back';
61
+ url: string;
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestcards/server-types",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "private": false,
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -1,12 +0,0 @@
1
- import { type CardReview, type NewCardReview, type NewReviewLog, type Rating } from '@vestcards/db-core';
2
- import { type Card as FSRSCard } from 'ts-fsrs';
3
- export declare const gradeCard: (card: CardReview, schemaRating: Rating, durationInSeconds: number) => {
4
- nextCard: CardReview;
5
- reviewLog: NewReviewLog;
6
- };
7
- /**
8
- * Give a {@link Card}, returns the review {@link Date} for each {@link Rating}.
9
- */
10
- export declare function getReviewDateForEachRating(card: CardReview | NewCardReview): Record<Rating, Date>;
11
- export declare function mergeFsrsCard(fsrsCard: FSRSCard, card: CardReview): CardReview;
12
- export declare function newCardReview(userDeckStudyId: string, cardId: string): NewCardReview;