@read-frog/api-contract 0.27.0 → 0.29.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@read-frog/api-contract",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@orpc/contract": "^1.16.1",
28
- "@read-frog/definitions": "0.15.0",
28
+ "@read-frog/definitions": "0.16.0",
29
29
  "zod": "^4.6.7"
30
30
  },
31
31
  "devDependencies": {
@@ -280,14 +280,6 @@ export const HostedAiTierStatusSchema = z.strictObject({
280
280
  * signing in. Clients badge an option with it and offer that plan.
281
281
  */
282
282
  upgradeTo: HostedAiUpgradePlanSchema.nullable(),
283
- /**
284
- * @deprecated Read `upgradeTo`. Viewer-independent product fact: no
285
- * non-Ultra account can ever use this feature/tier. Extensions released
286
- * before `upgradeTo` badge options with it, including for Ultra accounts.
287
- *
288
- * TODO(2026-11-21): remove once those extensions have drained (#710).
289
- */
290
- requiresUltra: z.boolean(),
291
283
  modelRevision: z.string().trim().min(1),
292
284
  });
293
285
 
@@ -297,15 +289,6 @@ export const HostedAiFeatureStatusSchema = z.strictObject({
297
289
  });
298
290
 
299
291
  export const HostedAiStatusOutputSchema = z.strictObject({
300
- /**
301
- * @deprecated Read `pools`. This lists the recurring pools only, never a
302
- * one-time one: extensions released before mengxi-ream/read-frog#2395 look
303
- * up a label for every entry's `periodKind`, and one they do not know
304
- * crashes their usage page. They also throw if the field is missing.
305
- *
306
- * TODO(2026-11-07): remove once those extensions have drained (#710).
307
- */
308
- credits: z.array(HostedAiRecurringCreditStatusSchema),
309
292
  /**
310
293
  * Everything that can pay for a feature, in the order the server draws on it:
311
294
  * the plan's recurring pools first, then one-time pools by soonest expiry. A
@@ -1,5 +1,5 @@
1
1
  import type { BillingPlan } from "./billing";
2
- import { langCodeISO6393Schema } from "@read-frog/definitions";
2
+ import { langCodeISO6393Schema, languageTagSchema, resolveLanguage } from "@read-frog/definitions";
3
3
  import { z } from "zod";
4
4
 
5
5
  // Reading text aloud in the account's voices: the reader's settings, the
@@ -32,6 +32,14 @@ export const SPEECH_RATE_MAX = 100;
32
32
  /** Reading speed: percent faster (+) or slower (−) than normal. */
33
33
  export const speechRateSchema = z.number().int().min(SPEECH_RATE_MIN).max(SPEECH_RATE_MAX);
34
34
 
35
+ // A language a request names. Pages loaded before languages were keyed by tag
36
+ // still send ISO 639-3 codes (`deu`) until they are reloaded, so those are
37
+ // read as their tags. Answers always name languages by tag.
38
+ const speechLanguageInputSchema = z.union([
39
+ languageTagSchema,
40
+ langCodeISO6393Schema.transform((code) => resolveLanguage(code)).pipe(languageTagSchema),
41
+ ]);
42
+
35
43
  /**
36
44
  * How the account reads aloud. A language left out of `languageVoices`, and a
37
45
  * `null` `multilingualVoice`, read in the default voice (`speech.listVoices`
@@ -39,7 +47,7 @@ export const speechRateSchema = z.number().int().min(SPEECH_RATE_MIN).max(SPEECH
39
47
  */
40
48
  export const speechPreferencesSchema = z
41
49
  .object({
42
- languageVoices: z.partialRecord(langCodeISO6393Schema, speechVoiceNameSchema),
50
+ languageVoices: z.partialRecord(languageTagSchema, speechVoiceNameSchema),
43
51
  // Reads text whose language is not known, working the language out itself.
44
52
  multilingualVoice: speechVoiceNameSchema.nullable(),
45
53
  rate: speechRateSchema,
@@ -60,7 +68,7 @@ export const DEFAULT_SPEECH_PREFERENCES: SpeechPreferences = {
60
68
  export const speechPreferencesUpdateInputSchema = z
61
69
  .object({
62
70
  languageVoices: z
63
- .partialRecord(langCodeISO6393Schema, speechVoiceNameSchema.nullable())
71
+ .partialRecord(speechLanguageInputSchema, speechVoiceNameSchema.nullable())
64
72
  .optional(),
65
73
  multilingualVoice: speechVoiceNameSchema.nullable().optional(),
66
74
  rate: speechRateSchema.optional(),
@@ -78,7 +86,7 @@ export const speechVoiceSchema = z
78
86
  displayName: z.string(),
79
87
  locale: z.string(),
80
88
  // The language the voice is for; null for one this list has no code for.
81
- language: langCodeISO6393Schema.nullable(),
89
+ language: languageTagSchema.nullable(),
82
90
  gender: speechVoiceGenderSchema,
83
91
  // Reads many languages, working out the text's language itself.
84
92
  multilingual: z.boolean(),
@@ -90,7 +98,7 @@ export const speechVoicesOutputSchema = z
90
98
  .object({
91
99
  voices: z.array(speechVoiceSchema),
92
100
  // The voice each language reads in until the reader picks one.
93
- defaultVoices: z.partialRecord(langCodeISO6393Schema, speechVoiceNameSchema),
101
+ defaultVoices: z.partialRecord(languageTagSchema, speechVoiceNameSchema),
94
102
  defaultMultilingualVoice: speechVoiceNameSchema,
95
103
  })
96
104
  .strict();
@@ -101,7 +109,7 @@ export const speechSynthesizeInputSchema = z
101
109
  text: z.string().trim().min(1).max(SPEECH_TEXT_MAX_LENGTH),
102
110
  // The text's language, when the caller knows it; without one, a
103
111
  // multilingual voice works it out.
104
- language: langCodeISO6393Schema.optional(),
112
+ language: speechLanguageInputSchema.optional(),
105
113
  // Reads in this voice instead of the account's, such as one the reader is
106
114
  // trying out in the settings.
107
115
  voice: speechVoiceNameSchema.optional(),