@read-frog/api-contract 0.6.1 → 0.8.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.
@@ -4,10 +4,27 @@ import {
4
4
  srsReviewOutputSchema,
5
5
  srsRollbackReviewInputSchema,
6
6
  srsRollbackReviewOutputSchema,
7
+ srsScheduleStatusStatsInputSchema,
8
+ srsScheduleStatusStatsOutputSchema,
9
+ srsSetCardBuriedInputSchema,
10
+ srsSetCardBuriedOutputSchema,
11
+ srsSetCardSuspendedInputSchema,
12
+ srsSetCardSuspendedOutputSchema,
7
13
  } from "#/schemas/srs"
8
14
  import { notebaseProcedure } from "./shared"
9
15
 
10
16
  export const srsContract = {
17
+ scheduleStatusStats: notebaseProcedure
18
+ .errors(pickPublicErrorMap("NOTEBASE_NOT_FOUND"))
19
+ .route({
20
+ method: "GET",
21
+ path: "/srs/schedule-status-stats",
22
+ summary: "List today schedule status stats for notebases",
23
+ tags: ["SRS"],
24
+ })
25
+ .input(srsScheduleStatusStatsInputSchema)
26
+ .output(srsScheduleStatusStatsOutputSchema),
27
+
11
28
  review: notebaseProcedure
12
29
  .errors(pickPublicErrorMap(
13
30
  "CARD_NOT_FOUND",
@@ -35,4 +52,34 @@ export const srsContract = {
35
52
  })
36
53
  .input(srsRollbackReviewInputSchema)
37
54
  .output(srsRollbackReviewOutputSchema),
55
+ setCardBuried: notebaseProcedure
56
+ .errors(pickPublicErrorMap(
57
+ "CARD_NOT_FOUND",
58
+ "CARD_ALREADY_BURIED",
59
+ "CARD_NOT_BURIED",
60
+ "CARD_STATUS_CHANGE_UNAVAILABLE",
61
+ ))
62
+ .route({
63
+ method: "POST",
64
+ path: "/srs/card/bury",
65
+ summary: "Manually bury or unbury a card",
66
+ tags: ["SRS"],
67
+ })
68
+ .input(srsSetCardBuriedInputSchema)
69
+ .output(srsSetCardBuriedOutputSchema),
70
+ setCardSuspended: notebaseProcedure
71
+ .errors(pickPublicErrorMap(
72
+ "CARD_NOT_FOUND",
73
+ "CARD_ALREADY_SUSPENDED",
74
+ "CARD_NOT_SUSPENDED",
75
+ "CARD_STATUS_CHANGE_UNAVAILABLE",
76
+ ))
77
+ .route({
78
+ method: "POST",
79
+ path: "/srs/card/suspend",
80
+ summary: "Manually suspend or unsuspend a card",
81
+ tags: ["SRS"],
82
+ })
83
+ .input(srsSetCardSuspendedInputSchema)
84
+ .output(srsSetCardSuspendedOutputSchema),
38
85
  }
@@ -2,6 +2,8 @@ import { oc } from "@orpc/contract"
2
2
  import {
3
3
  userEnsureTimezoneInputSchema,
4
4
  userEnsureTimezoneOutputSchema,
5
+ userUpdateTimezoneInputSchema,
6
+ userUpdateTimezoneOutputSchema,
5
7
  } from "#/schemas/user"
6
8
 
7
9
  export const userContract = {
@@ -14,4 +16,13 @@ export const userContract = {
14
16
  })
15
17
  .input(userEnsureTimezoneInputSchema)
16
18
  .output(userEnsureTimezoneOutputSchema),
19
+ updateTimezone: oc
20
+ .route({
21
+ method: "PATCH",
22
+ path: "/users/me/timezone",
23
+ summary: "Update the authenticated user's timezone",
24
+ tags: ["Users"],
25
+ })
26
+ .input(userUpdateTimezoneInputSchema)
27
+ .output(userUpdateTimezoneOutputSchema),
17
28
  }
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ import type { ContractRouterClient } from "@orpc/contract"
2
2
  import { betaAccessContract } from "./contracts/beta-access"
3
3
  import { cardContract } from "./contracts/card"
4
4
  import { cardTemplateContract } from "./contracts/card-template"
5
+ import { hostedAiContract } from "./contracts/hosted-ai"
5
6
  import { notebaseContract } from "./contracts/notebase"
6
7
  import { notebaseColumnContract } from "./contracts/notebase-column"
7
8
  import { notebaseRowContract } from "./contracts/notebase-row"
@@ -12,6 +13,7 @@ export const contract = {
12
13
  betaAccess: betaAccessContract,
13
14
  card: cardContract,
14
15
  cardTemplate: cardTemplateContract,
16
+ hostedAi: hostedAiContract,
15
17
  notebase: notebaseContract,
16
18
  notebaseColumn: notebaseColumnContract,
17
19
  notebaseRow: notebaseRowContract,
@@ -24,6 +26,7 @@ export type ORPCRouterClient = ContractRouterClient<typeof contract>
24
26
  export * from "./public-errors"
25
27
  export * from "./schemas/beta-access"
26
28
  export * from "./schemas/card"
29
+ export * from "./schemas/hosted-ai"
27
30
  export * from "./schemas/notebase"
28
31
  export * from "./schemas/notebase-column"
29
32
  export * from "./schemas/notebase-row"
@@ -82,6 +82,26 @@ export const PUBLIC_APP_ERROR_DEFS = {
82
82
  message: "Card review cannot be rolled back",
83
83
  status: 409,
84
84
  },
85
+ CARD_ALREADY_BURIED: {
86
+ message: "Card is already buried",
87
+ status: 409,
88
+ },
89
+ CARD_NOT_BURIED: {
90
+ message: "Card is not buried",
91
+ status: 409,
92
+ },
93
+ CARD_ALREADY_SUSPENDED: {
94
+ message: "Card is already suspended",
95
+ status: 409,
96
+ },
97
+ CARD_NOT_SUSPENDED: {
98
+ message: "Card is not suspended",
99
+ status: 409,
100
+ },
101
+ CARD_STATUS_CHANGE_UNAVAILABLE: {
102
+ message: "Card status change is unavailable",
103
+ status: 409,
104
+ },
85
105
  NOTEBASE_VIEW_NOT_FOUND: {
86
106
  message: "Notebase view not found",
87
107
  status: 404,
@@ -0,0 +1,46 @@
1
+ import { z } from "zod"
2
+
3
+ export const HostedAiOutputFieldTypeSchema = z.enum(["string", "number"])
4
+
5
+ export const HostedAiOutputFieldSchema = z.strictObject({
6
+ name: z.string().trim().min(1).max(80),
7
+ type: HostedAiOutputFieldTypeSchema,
8
+ })
9
+
10
+ export const HostedAiStreamTextInputSchema = z.strictObject({
11
+ system: z.string().trim().min(1).max(16000),
12
+ prompt: z.string().trim().min(1).max(32000),
13
+ temperature: z.number().min(0).max(2).optional(),
14
+ })
15
+
16
+ export const HostedAiStreamStructuredObjectInputSchema = z.strictObject({
17
+ system: z.string().trim().min(1).max(16000),
18
+ prompt: z.string().trim().min(1).max(32000),
19
+ outputSchema: z.array(HostedAiOutputFieldSchema).min(1).max(32),
20
+ temperature: z.number().min(0).max(2).optional(),
21
+ }).superRefine((input, ctx) => {
22
+ const fieldNames = new Set<string>()
23
+
24
+ input.outputSchema.forEach((field, index) => {
25
+ if (fieldNames.has(field.name)) {
26
+ ctx.addIssue({
27
+ code: "custom",
28
+ message: `Duplicate output schema name "${field.name}".`,
29
+ path: ["outputSchema", index, "name"],
30
+ })
31
+ return
32
+ }
33
+
34
+ fieldNames.add(field.name)
35
+ })
36
+ })
37
+
38
+ export const HostedAiStreamPartSchema = z.looseObject({
39
+ type: z.string().trim().min(1),
40
+ })
41
+
42
+ export type HostedAiOutputFieldType = z.infer<typeof HostedAiOutputFieldTypeSchema>
43
+ export type HostedAiOutputField = z.infer<typeof HostedAiOutputFieldSchema>
44
+ export type HostedAiStreamTextInput = z.infer<typeof HostedAiStreamTextInputSchema>
45
+ export type HostedAiStreamStructuredObjectInput = z.infer<typeof HostedAiStreamStructuredObjectInputSchema>
46
+ export type HostedAiStreamPart = z.infer<typeof HostedAiStreamPartSchema>
@@ -1,4 +1,4 @@
1
- import { schedulingParamsShape, srsWeightsSchema } from "@read-frog/definitions"
1
+ import { notebaseColumnConfigSchema, schedulingParamsShape, srsWeightsSchema } from "@read-frog/definitions"
2
2
  import { z } from "zod"
3
3
  import { notebaseColumnSchema } from "./notebase-column"
4
4
  import { notebaseRowSchema } from "./notebase-row"
@@ -38,6 +38,17 @@ export type Notebase = z.infer<typeof notebaseSchema>
38
38
  export const notebaseCreateDataSchema = z.object({
39
39
  id: z.uuid().optional(),
40
40
  name: z.string().min(1),
41
+ options: z.object({
42
+ initialColumns: z.array(z.object({
43
+ id: z.uuid(),
44
+ name: z.string().min(1),
45
+ config: notebaseColumnConfigSchema,
46
+ }).strict()).min(1),
47
+ initialRow: z.object({
48
+ id: z.uuid().optional(),
49
+ cells: z.record(z.string(), z.unknown()),
50
+ }).strict().optional(),
51
+ }).strict().optional(),
41
52
  }).strict()
42
53
  export type NotebaseCreateData = z.infer<typeof notebaseCreateDataSchema>
43
54
 
@@ -51,6 +62,8 @@ export type NotebaseCreateOutput = z.infer<typeof NotebaseCreateOutputSchema>
51
62
 
52
63
  export const notebaseUpdateDataSchema = z.object({
53
64
  name: z.string().min(1).optional(),
65
+ srsNewPerDay: schedulingParamsShape.newPerDay.optional(),
66
+ srsReviewsPerDay: schedulingParamsShape.reviewsPerDay.optional(),
54
67
  }).strict()
55
68
  export type NotebaseUpdateData = z.infer<typeof notebaseUpdateDataSchema>
56
69
 
@@ -1,4 +1,5 @@
1
1
  import {
2
+ activeScheduleStatusSchema,
2
3
  cardStateSchema,
3
4
  fsrsReviewLogSnapshotSchema,
4
5
  reviewRatingSchema,
@@ -53,6 +54,36 @@ export const srsRollbackReviewInputSchema = z.object({
53
54
  }).strict()
54
55
  export type SrsRollbackReviewInput = z.infer<typeof srsRollbackReviewInputSchema>
55
56
 
57
+ export const srsScheduleStatusStatsSchema = z.record(
58
+ activeScheduleStatusSchema,
59
+ z.number().int().min(0),
60
+ )
61
+ export type SrsScheduleStatusStats = z.infer<typeof srsScheduleStatusStatsSchema>
62
+
63
+ export const srsScheduleStatusStatsInputSchema = z.object({
64
+ notebaseIds: z.array(z.uuid()).optional(),
65
+ timezone: timezoneSchema,
66
+ }).strict()
67
+ export type SrsScheduleStatusStatsInput = z.infer<typeof srsScheduleStatusStatsInputSchema>
68
+
69
+ export const srsScheduleStatusStatsOutputSchema = z.record(
70
+ z.uuid(),
71
+ srsScheduleStatusStatsSchema,
72
+ )
73
+ export type SrsScheduleStatusStatsOutput = z.infer<typeof srsScheduleStatusStatsOutputSchema>
74
+
75
+ export const srsManualCardStatusInputSchema = z.object({
76
+ cardId: z.uuid(),
77
+ enabled: z.boolean(),
78
+ }).strict()
79
+ export type SrsManualCardStatusInput = z.infer<typeof srsManualCardStatusInputSchema>
80
+
81
+ export const srsSetCardBuriedInputSchema = srsManualCardStatusInputSchema
82
+ export type SrsSetCardBuriedInput = SrsManualCardStatusInput
83
+
84
+ export const srsSetCardSuspendedInputSchema = srsManualCardStatusInputSchema
85
+ export type SrsSetCardSuspendedInput = SrsManualCardStatusInput
86
+
56
87
  export const srsRevlogSchema = z.object({
57
88
  id: z.uuid(),
58
89
  notebaseId: z.uuid(),
@@ -67,6 +98,11 @@ export const srsRevlogSchema = z.object({
67
98
  })
68
99
  export type SrsRevlog = z.infer<typeof srsRevlogSchema>
69
100
 
101
+ export const srsRevlogWithoutSnapshotSchema = srsRevlogSchema.omit({
102
+ fsrsReviewLogSnapshot: true,
103
+ })
104
+ export type SrsRevlogWithoutSnapshot = z.infer<typeof srsRevlogWithoutSnapshotSchema>
105
+
70
106
  export const srsReviewOutputSchema = z.object({
71
107
  card: cardSchema,
72
108
  revlog: srsRevlogSchema,
@@ -81,6 +117,17 @@ export const srsRollbackReviewOutputSchema = z.object({
81
117
  })
82
118
  export type SrsRollbackReviewOutput = z.infer<typeof srsRollbackReviewOutputSchema>
83
119
 
120
+ export const srsTxidOutputSchema = z.object({
121
+ txid: z.number(),
122
+ })
123
+ export type SrsTxidOutput = z.infer<typeof srsTxidOutputSchema>
124
+
125
+ export const srsSetCardBuriedOutputSchema = srsTxidOutputSchema
126
+ export type SrsSetCardBuriedOutput = SrsTxidOutput
127
+
128
+ export const srsSetCardSuspendedOutputSchema = srsTxidOutputSchema
129
+ export type SrsSetCardSuspendedOutput = SrsTxidOutput
130
+
84
131
  function formatLastReviewTime(lastReviewTime: Date | string | null) {
85
132
  return lastReviewTime ? new Date(lastReviewTime).toISOString() : nullLastReviewTimeSentinel
86
133
  }
@@ -107,6 +154,7 @@ async function uuidV5(name: string, namespace: string) {
107
154
 
108
155
  function uuidToBytes(uuid: string) {
109
156
  const hex = uuid.replaceAll("-", "").toLowerCase()
157
+ /* v8 ignore next 3 -- SRS_REVIEW_CLIENT_ID_NAMESPACE is a fixed valid UUID. */
110
158
  if (!/^[0-9a-f]{32}$/.test(hex)) {
111
159
  throw new Error("Invalid UUID namespace")
112
160
  }
@@ -11,3 +11,13 @@ export const userEnsureTimezoneOutputSchema = z.object({
11
11
  updated: z.boolean(),
12
12
  }).strict()
13
13
  export type UserEnsureTimezoneOutput = z.infer<typeof userEnsureTimezoneOutputSchema>
14
+
15
+ export const userUpdateTimezoneInputSchema = z.object({
16
+ timezone: timezoneSchema,
17
+ }).strict()
18
+ export type UserUpdateTimezoneInput = z.infer<typeof userUpdateTimezoneInputSchema>
19
+
20
+ export const userUpdateTimezoneOutputSchema = z.object({
21
+ timezone: timezoneSchema,
22
+ }).strict()
23
+ export type UserUpdateTimezoneOutput = z.infer<typeof userUpdateTimezoneOutputSchema>