@read-frog/api-contract 0.21.1 → 0.23.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.
- package/dist/index.d.ts +814 -110
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +352 -141
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/contracts/ebook.ts +3 -1
- package/src/contracts/email-preferences.ts +55 -0
- package/src/contracts/feature-access.ts +17 -0
- package/src/contracts/notebase.ts +14 -0
- package/src/contracts/speech.ts +1 -1
- package/src/contracts/user.ts +22 -0
- package/src/index.ts +7 -3
- package/src/public-errors.ts +13 -12
- package/src/schemas/app-locale.ts +9 -0
- package/src/schemas/billing.ts +47 -35
- package/src/schemas/ebook.ts +16 -8
- package/src/schemas/email-preferences.ts +105 -0
- package/src/schemas/feature-access.ts +18 -0
- package/src/schemas/hosted-ai.ts +72 -7
- package/src/schemas/notebase.ts +16 -0
- package/src/schemas/speech.ts +11 -2
- package/src/schemas/srs.ts +4 -4
- package/src/schemas/stats.ts +2 -2
- package/src/schemas/timezone.ts +21 -0
- package/src/schemas/user.ts +33 -0
- package/src/contracts/beta-access.ts +0 -14
- package/src/schemas/beta-access.ts +0 -18
package/src/schemas/hosted-ai.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { billingPlanSchema } from "./billing";
|
|
2
3
|
|
|
3
4
|
export const HostedAiOutputFieldTypeSchema = z.enum(["string", "number"]);
|
|
4
5
|
|
|
@@ -159,13 +160,25 @@ export const HOSTED_AI_CLIENT_STREAM_PART_TYPES = [
|
|
|
159
160
|
*/
|
|
160
161
|
export const HostedAiStatusInputSchema = z.object({});
|
|
161
162
|
|
|
163
|
+
/**
|
|
164
|
+
* `pro_required` means the Pro plan or any plan above it; `ultra_required`
|
|
165
|
+
* means Ultra. Appended last, so a client that predates a value falls back on
|
|
166
|
+
* its own default instead of misreading it.
|
|
167
|
+
*/
|
|
162
168
|
export const HostedAiUnavailableReasonSchema = z.enum([
|
|
163
169
|
"authentication_required",
|
|
164
170
|
"ultra_required",
|
|
165
171
|
"quota_exhausted",
|
|
166
172
|
"service_unavailable",
|
|
173
|
+
"pro_required",
|
|
167
174
|
]);
|
|
168
175
|
|
|
176
|
+
/**
|
|
177
|
+
* The cheapest plan an account would need to buy for something it cannot use
|
|
178
|
+
* now. Any plan above it works too, so `pro` reads as "Pro or Ultra".
|
|
179
|
+
*/
|
|
180
|
+
export const HostedAiUpgradePlanSchema = billingPlanSchema.exclude(["free"]);
|
|
181
|
+
|
|
169
182
|
export const HostedAiCreditPolicyIdSchema = z.enum([
|
|
170
183
|
"custom-action-guest-daily-v1",
|
|
171
184
|
"custom-action-user-daily-v1",
|
|
@@ -196,13 +209,16 @@ export const HostedAiRateLimitErrorDataSchema = z.object({
|
|
|
196
209
|
});
|
|
197
210
|
|
|
198
211
|
/**
|
|
212
|
+
* A recurring pool: the plan's credit for a day or a week, which refills at
|
|
213
|
+
* `resetAt`.
|
|
214
|
+
*
|
|
199
215
|
* Deliberately free of dollar amounts and of the versioned policy id. Clients
|
|
200
216
|
* render a consumption bar and a reset time, so a percentage and a period kind
|
|
201
217
|
* are all they need, and neither leaks what a call costs us. `usedPercent`
|
|
202
218
|
* counts settled spend only: including in-flight reservations would make the
|
|
203
219
|
* bar jump forward and then back as each stream settles.
|
|
204
220
|
*/
|
|
205
|
-
export const
|
|
221
|
+
export const HostedAiRecurringCreditStatusSchema = z.strictObject({
|
|
206
222
|
periodKind: HostedAiCreditPeriodKindSchema,
|
|
207
223
|
usedPercent: z.number().min(0).max(100),
|
|
208
224
|
resetAt: z.iso.datetime().nullable(),
|
|
@@ -214,15 +230,47 @@ export const HostedAiCreditStatusSchema = z.strictObject({
|
|
|
214
230
|
features: z.array(HostedAiFeatureSchema).min(1),
|
|
215
231
|
});
|
|
216
232
|
|
|
233
|
+
/**
|
|
234
|
+
* A one-time pool, such as credit from a campaign or from support. It never
|
|
235
|
+
* refills: once spent or past `expiresAt` it is gone, so a client shows the
|
|
236
|
+
* expiry, not a reset. It is drawn on only after the plan's own pool for a
|
|
237
|
+
* feature is spent, or for a feature the plan does not fund. Listed until it
|
|
238
|
+
* expires, even when spent. Same fields as a recurring pool otherwise.
|
|
239
|
+
*/
|
|
240
|
+
export const HostedAiOneTimeCreditStatusSchema = z.strictObject({
|
|
241
|
+
periodKind: z.literal("once"),
|
|
242
|
+
usedPercent: z.number().min(0).max(100),
|
|
243
|
+
expiresAt: z.iso.datetime(),
|
|
244
|
+
features: z.array(HostedAiFeatureSchema).min(1),
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* One pool that can pay for Built-in AI, told apart by `periodKind`: a
|
|
249
|
+
* recurring `daily` or `weekly` pool refills at `resetAt`, a one-time (`once`)
|
|
250
|
+
* pool ends at `expiresAt`. Narrow on `periodKind` before reading either.
|
|
251
|
+
*/
|
|
252
|
+
export const HostedAiCreditPoolStatusSchema = z.discriminatedUnion("periodKind", [
|
|
253
|
+
HostedAiRecurringCreditStatusSchema,
|
|
254
|
+
HostedAiOneTimeCreditStatusSchema,
|
|
255
|
+
]);
|
|
256
|
+
|
|
217
257
|
export const HostedAiTierStatusSchema = z.strictObject({
|
|
218
258
|
accessAllowed: z.boolean(),
|
|
219
259
|
available: z.boolean(),
|
|
220
260
|
unavailableReason: HostedAiUnavailableReasonSchema.nullable(),
|
|
221
261
|
/**
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
262
|
+
* The cheapest plan this account would need to use the tier, or null when it
|
|
263
|
+
* needs none: its plan or a one-time pool already covers it, or the tier is
|
|
264
|
+
* open to every signed-in account. For a guest it is the plan needed after
|
|
265
|
+
* signing in. Clients badge an option with it and offer that plan.
|
|
266
|
+
*/
|
|
267
|
+
upgradeTo: HostedAiUpgradePlanSchema.nullable(),
|
|
268
|
+
/**
|
|
269
|
+
* @deprecated Read `upgradeTo`. Viewer-independent product fact: no
|
|
270
|
+
* non-Ultra account can ever use this feature/tier. Extensions released
|
|
271
|
+
* before `upgradeTo` badge options with it, including for Ultra accounts.
|
|
272
|
+
*
|
|
273
|
+
* TODO(2026-11-21): remove once those extensions have drained (#710).
|
|
226
274
|
*/
|
|
227
275
|
requiresUltra: z.boolean(),
|
|
228
276
|
modelRevision: z.string().trim().min(1),
|
|
@@ -234,7 +282,21 @@ export const HostedAiFeatureStatusSchema = z.strictObject({
|
|
|
234
282
|
});
|
|
235
283
|
|
|
236
284
|
export const HostedAiStatusOutputSchema = z.strictObject({
|
|
237
|
-
|
|
285
|
+
/**
|
|
286
|
+
* @deprecated Read `pools`. This lists the recurring pools only, never a
|
|
287
|
+
* one-time one: extensions released before mengxi-ream/read-frog#2395 look
|
|
288
|
+
* up a label for every entry's `periodKind`, and one they do not know
|
|
289
|
+
* crashes their usage page. They also throw if the field is missing.
|
|
290
|
+
*
|
|
291
|
+
* TODO(2026-11-07): remove once those extensions have drained (#710).
|
|
292
|
+
*/
|
|
293
|
+
credits: z.array(HostedAiRecurringCreditStatusSchema),
|
|
294
|
+
/**
|
|
295
|
+
* Everything that can pay for a feature, in the order the server draws on it:
|
|
296
|
+
* the plan's recurring pools first, then one-time pools by soonest expiry. A
|
|
297
|
+
* feature can appear in several entries.
|
|
298
|
+
*/
|
|
299
|
+
pools: z.array(HostedAiCreditPoolStatusSchema),
|
|
238
300
|
features: z.strictObject({
|
|
239
301
|
pageTranslation: HostedAiFeatureStatusSchema,
|
|
240
302
|
customAction: HostedAiFeatureStatusSchema,
|
|
@@ -261,11 +323,14 @@ export type HostedAiNoteSuggestionObject = z.infer<typeof HostedAiNoteSuggestion
|
|
|
261
323
|
export type HostedAiStreamPart = z.infer<typeof HostedAiStreamPartSchema>;
|
|
262
324
|
export type HostedAiStatusInput = z.infer<typeof HostedAiStatusInputSchema>;
|
|
263
325
|
export type HostedAiUnavailableReason = z.infer<typeof HostedAiUnavailableReasonSchema>;
|
|
326
|
+
export type HostedAiUpgradePlan = z.infer<typeof HostedAiUpgradePlanSchema>;
|
|
264
327
|
export type HostedAiCreditPolicyId = z.infer<typeof HostedAiCreditPolicyIdSchema>;
|
|
265
328
|
export type HostedAiCreditPeriodKind = z.infer<typeof HostedAiCreditPeriodKindSchema>;
|
|
266
329
|
export type HostedAiQuotaScope = z.infer<typeof HostedAiQuotaScopeSchema>;
|
|
267
330
|
export type HostedAiRateLimitErrorData = z.infer<typeof HostedAiRateLimitErrorDataSchema>;
|
|
268
|
-
export type
|
|
331
|
+
export type HostedAiRecurringCreditStatus = z.infer<typeof HostedAiRecurringCreditStatusSchema>;
|
|
332
|
+
export type HostedAiOneTimeCreditStatus = z.infer<typeof HostedAiOneTimeCreditStatusSchema>;
|
|
333
|
+
export type HostedAiCreditPoolStatus = z.infer<typeof HostedAiCreditPoolStatusSchema>;
|
|
269
334
|
export type HostedAiTierStatus = z.infer<typeof HostedAiTierStatusSchema>;
|
|
270
335
|
export type HostedAiFeatureStatus = z.infer<typeof HostedAiFeatureStatusSchema>;
|
|
271
336
|
export type HostedAiStatusOutput = z.infer<typeof HostedAiStatusOutputSchema>;
|
package/src/schemas/notebase.ts
CHANGED
|
@@ -41,6 +41,8 @@ export const notebaseSchema = z.object({
|
|
|
41
41
|
srsWeights: srsWeightsSchema.nullable(),
|
|
42
42
|
// Selects the scheduler/curve; read-only, changed only by the FSRS-7 migration.
|
|
43
43
|
srsModelVersion: srsModelVersionSchema,
|
|
44
|
+
// Null = awake; set = excluded from the combined review. Written by notebase.setSleep and auto sleep.
|
|
45
|
+
srsSleepingAt: z.date().nullable(),
|
|
44
46
|
createdAt: z.date(),
|
|
45
47
|
updatedAt: z.date(),
|
|
46
48
|
});
|
|
@@ -144,6 +146,20 @@ export const NotebaseUpdateOutputSchema = z.object({
|
|
|
144
146
|
});
|
|
145
147
|
export type NotebaseUpdateOutput = z.infer<typeof NotebaseUpdateOutputSchema>;
|
|
146
148
|
|
|
149
|
+
export const NotebaseSetSleepInputSchema = z
|
|
150
|
+
.object({
|
|
151
|
+
id: z.uuid(),
|
|
152
|
+
enabled: z.boolean(),
|
|
153
|
+
})
|
|
154
|
+
.strict();
|
|
155
|
+
export type NotebaseSetSleepInput = z.infer<typeof NotebaseSetSleepInputSchema>;
|
|
156
|
+
|
|
157
|
+
// txid is null when the notebase was already in the requested state: nothing to wait for.
|
|
158
|
+
export const NotebaseSetSleepOutputSchema = z.object({
|
|
159
|
+
txid: z.number().nullable(),
|
|
160
|
+
});
|
|
161
|
+
export type NotebaseSetSleepOutput = z.infer<typeof NotebaseSetSleepOutputSchema>;
|
|
162
|
+
|
|
147
163
|
export const NotebaseDeleteInputSchema = z.object({
|
|
148
164
|
id: z.uuid(),
|
|
149
165
|
});
|
package/src/schemas/speech.ts
CHANGED
|
@@ -109,5 +109,14 @@ export const speechSynthesizeInputSchema = z
|
|
|
109
109
|
.strict();
|
|
110
110
|
export type SpeechSynthesizeInput = z.infer<typeof speechSynthesizeInputSchema>;
|
|
111
111
|
|
|
112
|
-
/**
|
|
113
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Where to download the audio, an MP3: a link to fetch at once, which stops
|
|
114
|
+
* working at `expiresAt`.
|
|
115
|
+
*/
|
|
116
|
+
export const speechSynthesizeOutputSchema = z
|
|
117
|
+
.object({
|
|
118
|
+
url: z.url(),
|
|
119
|
+
expiresAt: z.coerce.date(),
|
|
120
|
+
})
|
|
121
|
+
.strict();
|
|
122
|
+
export type SpeechSynthesizeOutput = z.infer<typeof speechSynthesizeOutputSchema>;
|
package/src/schemas/srs.ts
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { cardSchema } from "./card";
|
|
15
15
|
import { notebaseSummarySchema } from "./notebase";
|
|
16
|
-
import {
|
|
16
|
+
import { canonicalTimezoneSchema } from "./timezone";
|
|
17
17
|
|
|
18
18
|
export { reviewRatingSchema, schedulingParamsSchema, SRS_REVIEW_DURATION_MS_MAX };
|
|
19
19
|
export { timezoneSchema } from "./timezone";
|
|
@@ -45,7 +45,7 @@ export const srsReviewInputSchema = z
|
|
|
45
45
|
id: z.uuid().optional(),
|
|
46
46
|
rating: reviewRatingSchema,
|
|
47
47
|
durationMs: z.number().int().min(0).max(SRS_REVIEW_DURATION_MS_MAX),
|
|
48
|
-
timezone:
|
|
48
|
+
timezone: canonicalTimezoneSchema,
|
|
49
49
|
})
|
|
50
50
|
.strict();
|
|
51
51
|
export type SrsReviewInput = z.infer<typeof srsReviewInputSchema>;
|
|
@@ -67,7 +67,7 @@ export type SrsScheduleStatusStats = z.infer<typeof srsScheduleStatusStatsSchema
|
|
|
67
67
|
export const srsScheduleStatusStatsInputSchema = z
|
|
68
68
|
.object({
|
|
69
69
|
notebaseIds: z.array(z.uuid()).optional(),
|
|
70
|
-
timezone:
|
|
70
|
+
timezone: canonicalTimezoneSchema,
|
|
71
71
|
})
|
|
72
72
|
.strict();
|
|
73
73
|
export type SrsScheduleStatusStatsInput = z.infer<typeof srsScheduleStatusStatsInputSchema>;
|
|
@@ -77,7 +77,7 @@ export type SrsScheduleStatusStatsOutput = z.infer<typeof srsScheduleStatusStats
|
|
|
77
77
|
|
|
78
78
|
export const srsRecentNotebaseScheduleStatusStatsInputSchema = z
|
|
79
79
|
.object({
|
|
80
|
-
timezone:
|
|
80
|
+
timezone: canonicalTimezoneSchema,
|
|
81
81
|
})
|
|
82
82
|
.strict();
|
|
83
83
|
export type SrsRecentNotebaseScheduleStatusStatsInput = z.infer<
|
package/src/schemas/stats.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { notebaseActivityStatsOutputSchema } from "./notebase-stats";
|
|
3
3
|
import { srsActivityStatsOutputSchema } from "./srs-stats";
|
|
4
|
-
import {
|
|
4
|
+
import { canonicalTimezoneSchema } from "./timezone";
|
|
5
5
|
|
|
6
6
|
const statsNotebaseIdsSchema = z
|
|
7
7
|
.array(z.uuid())
|
|
@@ -13,7 +13,7 @@ export const statsInputSchema = z
|
|
|
13
13
|
notebaseIds: statsNotebaseIdsSchema.optional(),
|
|
14
14
|
from: z.iso.date().optional(),
|
|
15
15
|
to: z.iso.date(),
|
|
16
|
-
timezone:
|
|
16
|
+
timezone: canonicalTimezoneSchema,
|
|
17
17
|
})
|
|
18
18
|
.strict()
|
|
19
19
|
.refine((input) => !input.from || input.from <= input.to, {
|
package/src/schemas/timezone.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { canonicalTimeZone } from "@read-frog/definitions";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
|
|
3
4
|
// @ref https://zenn.dev/herp_inc/articles/js-intl-date-time-format-performance
|
|
@@ -46,3 +47,23 @@ export const timezoneSchema = z
|
|
|
46
47
|
}
|
|
47
48
|
}, "Invalid timezone");
|
|
48
49
|
export type Timezone = z.infer<typeof timezoneSchema>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The zone a call reckons days in, under the name an account's zone is stored
|
|
53
|
+
* under (`canonicalTimeZone`): "Asia/Calcutta" arrives as "Asia/Kolkata" and
|
|
54
|
+
* "CST" as "America/Chicago", names that Temporal and a Debian Postgres both
|
|
55
|
+
* read. A fixed offset ("+08:00") is refused, as Postgres reads it with the
|
|
56
|
+
* opposite sign and would put the call's days on the wrong side of UTC.
|
|
57
|
+
*/
|
|
58
|
+
export const canonicalTimezoneSchema = timezoneSchema.transform((timezone, ctx) => {
|
|
59
|
+
const name = canonicalTimeZone(timezone);
|
|
60
|
+
if (!name) {
|
|
61
|
+
ctx.addIssue({
|
|
62
|
+
code: "custom",
|
|
63
|
+
message:
|
|
64
|
+
"Use an IANA time zone identifier instead of a fixed UTC offset. See https://www.iana.org/time-zones",
|
|
65
|
+
});
|
|
66
|
+
return z.NEVER;
|
|
67
|
+
}
|
|
68
|
+
return name;
|
|
69
|
+
});
|
package/src/schemas/user.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { appLocaleSchema } from "./app-locale";
|
|
2
3
|
import { timezoneSchema } from "./timezone";
|
|
3
4
|
|
|
4
5
|
export const userEnsureTimezoneInputSchema = z
|
|
@@ -29,3 +30,35 @@ export const userUpdateTimezoneOutputSchema = z
|
|
|
29
30
|
})
|
|
30
31
|
.strict();
|
|
31
32
|
export type UserUpdateTimezoneOutput = z.infer<typeof userUpdateTimezoneOutputSchema>;
|
|
33
|
+
|
|
34
|
+
// The account's language, which emails are written in. `ensureLocale` only
|
|
35
|
+
// fills it in when it is empty (the language www is showing on a signed-in
|
|
36
|
+
// visit); `updateLocale` is the user choosing a language.
|
|
37
|
+
export const userEnsureLocaleInputSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
locale: appLocaleSchema,
|
|
40
|
+
})
|
|
41
|
+
.strict();
|
|
42
|
+
export type UserEnsureLocaleInput = z.infer<typeof userEnsureLocaleInputSchema>;
|
|
43
|
+
|
|
44
|
+
export const userEnsureLocaleOutputSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
locale: appLocaleSchema,
|
|
47
|
+
updated: z.boolean(),
|
|
48
|
+
})
|
|
49
|
+
.strict();
|
|
50
|
+
export type UserEnsureLocaleOutput = z.infer<typeof userEnsureLocaleOutputSchema>;
|
|
51
|
+
|
|
52
|
+
export const userUpdateLocaleInputSchema = z
|
|
53
|
+
.object({
|
|
54
|
+
locale: appLocaleSchema,
|
|
55
|
+
})
|
|
56
|
+
.strict();
|
|
57
|
+
export type UserUpdateLocaleInput = z.infer<typeof userUpdateLocaleInputSchema>;
|
|
58
|
+
|
|
59
|
+
export const userUpdateLocaleOutputSchema = z
|
|
60
|
+
.object({
|
|
61
|
+
locale: appLocaleSchema,
|
|
62
|
+
})
|
|
63
|
+
.strict();
|
|
64
|
+
export type UserUpdateLocaleOutput = z.infer<typeof userUpdateLocaleOutputSchema>;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { oc } from "@orpc/contract";
|
|
2
|
-
import { BetaAccessStatusInputSchema, BetaAccessStatusOutputSchema } from "#/schemas/beta-access";
|
|
3
|
-
|
|
4
|
-
export const betaAccessContract = {
|
|
5
|
-
status: oc
|
|
6
|
-
.route({
|
|
7
|
-
method: "GET",
|
|
8
|
-
path: "/beta-access/status",
|
|
9
|
-
summary: "Get beta access status for a feature",
|
|
10
|
-
tags: ["Beta Access"],
|
|
11
|
-
})
|
|
12
|
-
.input(BetaAccessStatusInputSchema)
|
|
13
|
-
.output(BetaAccessStatusOutputSchema),
|
|
14
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { BETA_FEATURE_KEYS } from "@read-frog/definitions";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
|
-
export const BetaFeatureKeySchema = z.enum(BETA_FEATURE_KEYS);
|
|
5
|
-
export type BetaFeatureKey = z.infer<typeof BetaFeatureKeySchema>;
|
|
6
|
-
|
|
7
|
-
export const BetaAccessStatusInputSchema = z
|
|
8
|
-
.object({
|
|
9
|
-
featureKey: BetaFeatureKeySchema,
|
|
10
|
-
})
|
|
11
|
-
.strict();
|
|
12
|
-
export type BetaAccessStatusInput = z.infer<typeof BetaAccessStatusInputSchema>;
|
|
13
|
-
|
|
14
|
-
export const BetaAccessStatusOutputSchema = z.object({
|
|
15
|
-
featureKey: BetaFeatureKeySchema,
|
|
16
|
-
allowed: z.boolean(),
|
|
17
|
-
});
|
|
18
|
-
export type BetaAccessStatusOutput = z.infer<typeof BetaAccessStatusOutputSchema>;
|