@read-frog/api-contract 0.23.0 → 0.25.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 +2149 -80
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +732 -110
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/contracts/billing.ts +1 -0
- package/src/contracts/ebook.ts +7 -15
- package/src/contracts/hosted-ai.ts +11 -0
- package/src/contracts/mcp-connection.ts +11 -0
- package/src/contracts/media.ts +60 -0
- package/src/contracts/notebase-column.ts +1 -1
- package/src/contracts/pdf-translation.ts +140 -0
- package/src/contracts/srs.ts +32 -0
- package/src/contracts/stats.ts +16 -0
- package/src/contracts/telemetry.ts +17 -0
- package/src/index.ts +9 -0
- package/src/public-errors.ts +126 -4
- package/src/schemas/billing.ts +19 -0
- package/src/schemas/ebook.ts +7 -0
- package/src/schemas/email-preferences.ts +44 -0
- package/src/schemas/hosted-ai.ts +15 -0
- package/src/schemas/mcp-connection.ts +17 -0
- package/src/schemas/media.ts +77 -0
- package/src/schemas/notebase.ts +3 -0
- package/src/schemas/pdf-translation.ts +240 -0
- package/src/schemas/srs.ts +178 -1
- package/src/schemas/web-sync.ts +121 -0
package/src/schemas/billing.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AppLocale } from "./app-locale";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { checkoutAttributionInputSchema } from "@read-frog/definitions";
|
|
3
4
|
import { appLocaleSchema } from "./app-locale";
|
|
4
5
|
|
|
5
6
|
export const billingIntervalSchema = z.enum(["month", "year"]);
|
|
@@ -36,6 +37,22 @@ export const MCP_DAILY_CALL_LIMITS = {
|
|
|
36
37
|
ultra: null,
|
|
37
38
|
} as const satisfies Record<BillingPlan, number | null>;
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* How many ebooks each plan may keep at once. Free keeps one, so it reads one
|
|
42
|
+
* book at a time; deleting a book frees its place for the next.
|
|
43
|
+
*
|
|
44
|
+
* Read by the API's upload gate AND by the website's copy (the pricing page),
|
|
45
|
+
* so the pages cannot promise books the server refuses.
|
|
46
|
+
*
|
|
47
|
+
* Companion edit when this changes: `account-and-plans` under
|
|
48
|
+
* `apps/www/content/docs/`, which states the numbers.
|
|
49
|
+
*/
|
|
50
|
+
export const EBOOK_LIMIT_BY_PLAN = {
|
|
51
|
+
free: 1,
|
|
52
|
+
pro: 10,
|
|
53
|
+
ultra: 80,
|
|
54
|
+
} as const satisfies Record<BillingPlan, number>;
|
|
55
|
+
|
|
39
56
|
// Plans a user can actively check out into (the free tier has no price).
|
|
40
57
|
export const billingCheckoutPlanSchema = z.enum(["pro", "ultra"]);
|
|
41
58
|
export type BillingCheckoutPlan = z.infer<typeof billingCheckoutPlanSchema>;
|
|
@@ -116,6 +133,8 @@ export const billingCreateCheckoutSessionInputSchema = z
|
|
|
116
133
|
interval: billingIntervalSchema,
|
|
117
134
|
locale: appLocaleSchema,
|
|
118
135
|
plan: billingCheckoutPlanSchema.default("pro"),
|
|
136
|
+
requestId: z.uuid().optional(),
|
|
137
|
+
attribution: checkoutAttributionInputSchema.optional(),
|
|
119
138
|
})
|
|
120
139
|
.strict();
|
|
121
140
|
export type BillingCreateCheckoutSessionInput = z.infer<
|
package/src/schemas/ebook.ts
CHANGED
|
@@ -77,6 +77,13 @@ export const ebookRequestUploadInputSchema = z
|
|
|
77
77
|
})
|
|
78
78
|
.strict()
|
|
79
79
|
.optional(),
|
|
80
|
+
/**
|
|
81
|
+
* The client's key for this upload. Asking again with the same key, after
|
|
82
|
+
* an answer that never arrived or for a retry, returns the same pending
|
|
83
|
+
* book with fresh targets instead of reserving another place in the
|
|
84
|
+
* library's allowance.
|
|
85
|
+
*/
|
|
86
|
+
uploadKey: z.uuid().optional(),
|
|
80
87
|
})
|
|
81
88
|
.strict();
|
|
82
89
|
export type EbookRequestUploadInput = z.infer<typeof ebookRequestUploadInputSchema>;
|
|
@@ -34,11 +34,52 @@ export const emailCategoryStateSchema = z
|
|
|
34
34
|
.strict();
|
|
35
35
|
export type EmailCategoryState = z.infer<typeof emailCategoryStateSchema>;
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* The category whose emails a person can time: Practice reminders, seeded as
|
|
39
|
+
* `practice` (packages/db's migration 0067). The settings page shows the
|
|
40
|
+
* time under its switch.
|
|
41
|
+
*/
|
|
42
|
+
export const PRACTICE_REMINDER_CATEGORY_KEY = "practice";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* When practice reminders reach someone who never chose a time: 18:00 in
|
|
46
|
+
* their own time zone. The page shows it as the choice until one is made;
|
|
47
|
+
* Ribbit's reminder automation sets the same as its own time, for people
|
|
48
|
+
* without one (docs/ribbit/phase-3-automations.md).
|
|
49
|
+
*/
|
|
50
|
+
export const PRACTICE_REMINDER_DEFAULT_TIME = "18:00";
|
|
51
|
+
|
|
52
|
+
/** A time of day on the hour, `HH:00`: Ribbit takes people into a schedule once an hour. */
|
|
53
|
+
export const practiceReminderTimeSchema = z
|
|
54
|
+
.string()
|
|
55
|
+
.regex(/^([01]\d|2[0-3]):00$/, "Choose a time on the hour.");
|
|
56
|
+
export type PracticeReminderTime = z.infer<typeof practiceReminderTimeSchema>;
|
|
57
|
+
|
|
58
|
+
/** The times the page offers: every hour of the day. */
|
|
59
|
+
export const PRACTICE_REMINDER_TIMES: readonly PracticeReminderTime[] = Array.from(
|
|
60
|
+
{ length: 24 },
|
|
61
|
+
(_, hour) => `${String(hour).padStart(2, "0")}:00`,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The reminders area of `user_preferences`, as the database holds it: what
|
|
66
|
+
* is not there, or no longer reads, is the default. `practiceTime` is the
|
|
67
|
+
* practice reminder's time of day, in the person's own zone; null until they
|
|
68
|
+
* choose one.
|
|
69
|
+
*/
|
|
70
|
+
export const remindersPreferencesSchema = z.object({
|
|
71
|
+
practiceTime: practiceReminderTimeSchema.nullable().catch(null).default(null),
|
|
72
|
+
});
|
|
73
|
+
export type RemindersPreferences = z.infer<typeof remindersPreferencesSchema>;
|
|
74
|
+
|
|
37
75
|
export const emailPreferencesStateSchema = z
|
|
38
76
|
.object({
|
|
39
77
|
// In display order; archived categories are left out.
|
|
40
78
|
categories: z.array(emailCategoryStateSchema),
|
|
41
79
|
unsubscribedAll: z.boolean(),
|
|
80
|
+
// When practice reminders reach them, in their own time zone; null while
|
|
81
|
+
// they never chose one (the page then shows PRACTICE_REMINDER_DEFAULT_TIME).
|
|
82
|
+
practiceReminderTime: practiceReminderTimeSchema.nullable(),
|
|
42
83
|
})
|
|
43
84
|
.strict();
|
|
44
85
|
export type EmailPreferencesState = z.infer<typeof emailPreferencesStateSchema>;
|
|
@@ -68,6 +109,9 @@ const emailPreferencesChangesShape = {
|
|
|
68
109
|
// categories added later; false ends that and leaves each category as it was
|
|
69
110
|
// before.
|
|
70
111
|
unsubscribedAll: z.boolean().optional(),
|
|
112
|
+
// Sets when practice reminders reach them (null: back to the default); left
|
|
113
|
+
// out, it stays as it is.
|
|
114
|
+
practiceReminderTime: practiceReminderTimeSchema.nullable().optional(),
|
|
71
115
|
};
|
|
72
116
|
|
|
73
117
|
export const emailPreferencesGetInputSchema = z
|
package/src/schemas/hosted-ai.ts
CHANGED
|
@@ -32,6 +32,9 @@ export const HostedAiFeatureSchema = z.enum([
|
|
|
32
32
|
"videoSubtitles",
|
|
33
33
|
"inputTranslation",
|
|
34
34
|
"languageDetection",
|
|
35
|
+
// Charged by the PDF worker's proxy, not called by clients; `status`
|
|
36
|
+
// leaves it out (see the router's HOSTED_AI_FEATURES).
|
|
37
|
+
"pdfTranslation",
|
|
35
38
|
]);
|
|
36
39
|
export const HostedAiRequestIdSchema = z.uuid();
|
|
37
40
|
|
|
@@ -182,8 +185,14 @@ export const HostedAiUpgradePlanSchema = billingPlanSchema.exclude(["free"]);
|
|
|
182
185
|
export const HostedAiCreditPolicyIdSchema = z.enum([
|
|
183
186
|
"custom-action-guest-daily-v1",
|
|
184
187
|
"custom-action-user-daily-v1",
|
|
188
|
+
// Retired: Pro draws on a weekly pool now. Kept because the database still
|
|
189
|
+
// holds pools under it.
|
|
185
190
|
"custom-action-pro-daily-v1",
|
|
186
191
|
"ultra-weekly-v1",
|
|
192
|
+
// Retired: Pro's weekly pool in UTC calendar weeks. Kept for the same reason.
|
|
193
|
+
"pro-weekly-v1",
|
|
194
|
+
// Pro's weekly pool, in weeks that run from when the plan started, as Ultra's do.
|
|
195
|
+
"pro-weekly-v2",
|
|
187
196
|
]);
|
|
188
197
|
|
|
189
198
|
export const HostedAiCreditPeriodKindSchema = z.enum(["daily", "weekly"]);
|
|
@@ -254,6 +263,12 @@ export const HostedAiCreditPoolStatusSchema = z.discriminatedUnion("periodKind",
|
|
|
254
263
|
HostedAiOneTimeCreditStatusSchema,
|
|
255
264
|
]);
|
|
256
265
|
|
|
266
|
+
/** Feature-scoped credit display, separate from the legacy extension status shape. */
|
|
267
|
+
export const HostedAiCreditPoolsInputSchema = z.strictObject({ feature: HostedAiFeatureSchema });
|
|
268
|
+
export const HostedAiCreditPoolsOutputSchema = z.strictObject({
|
|
269
|
+
pools: z.array(HostedAiCreditPoolStatusSchema),
|
|
270
|
+
});
|
|
271
|
+
|
|
257
272
|
export const HostedAiTierStatusSchema = z.strictObject({
|
|
258
273
|
accessAllowed: z.boolean(),
|
|
259
274
|
available: z.boolean(),
|
|
@@ -26,3 +26,20 @@ export const mcpConnectionRevokeInputSchema = z.object({ clientId: z.string().mi
|
|
|
26
26
|
export type McpConnectionRevokeInput = z.infer<typeof mcpConnectionRevokeInputSchema>;
|
|
27
27
|
|
|
28
28
|
export const mcpConnectionRevokeOutputSchema = z.object({ clientId: z.string() }).strict();
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* How many MCP tool calls the caller has used of their plan's
|
|
32
|
+
* `MCP_DAILY_CALL_LIMITS`, read without counting one. The window is 24 hours
|
|
33
|
+
* from the first call in it, not a calendar day.
|
|
34
|
+
*/
|
|
35
|
+
export const mcpCallUsageSchema = z
|
|
36
|
+
.object({
|
|
37
|
+
/** Calls used in the current window, at most `limit`. */
|
|
38
|
+
usedCalls: z.number().int().min(0),
|
|
39
|
+
/** null: the plan has no daily limit. */
|
|
40
|
+
limit: z.number().int().positive().nullable(),
|
|
41
|
+
/** When the current 24-hour window ends; null when no window is open or there is no limit. */
|
|
42
|
+
resetAt: z.iso.datetime().nullable(),
|
|
43
|
+
})
|
|
44
|
+
.strict();
|
|
45
|
+
export type McpCallUsage = z.infer<typeof mcpCallUsageSchema>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { MEDIA_ASSET_STATUS_VALUES, MEDIA_CONTENT_TYPES } from "@read-frog/definitions";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
export const mediaContentTypeSchema = z.enum(MEDIA_CONTENT_TYPES);
|
|
5
|
+
export type MediaContentType = z.infer<typeof mediaContentTypeSchema>;
|
|
6
|
+
|
|
7
|
+
export const mediaAssetStatusSchema = z.enum(MEDIA_ASSET_STATUS_VALUES);
|
|
8
|
+
export type MediaAssetStatus = z.infer<typeof mediaAssetStatusSchema>;
|
|
9
|
+
|
|
10
|
+
/** Why the worker rejected an upload. */
|
|
11
|
+
export const mediaFailureCodeSchema = z.enum([
|
|
12
|
+
"unsupported_format",
|
|
13
|
+
"invalid_file",
|
|
14
|
+
"file_too_large",
|
|
15
|
+
"image_too_large",
|
|
16
|
+
"audio_too_long",
|
|
17
|
+
"quota_exceeded",
|
|
18
|
+
"processing_failed",
|
|
19
|
+
]);
|
|
20
|
+
export type MediaFailureCode = z.infer<typeof mediaFailureCodeSchema>;
|
|
21
|
+
|
|
22
|
+
export const mediaAssetMetadataSchema = z.discriminatedUnion("kind", [
|
|
23
|
+
z
|
|
24
|
+
.object({ kind: z.literal("image"), width: z.number().int(), height: z.number().int() })
|
|
25
|
+
.strict(),
|
|
26
|
+
z.object({ kind: z.literal("audio"), durationMs: z.number().int() }).strict(),
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
export const mediaAssetSchema = z
|
|
30
|
+
.object({
|
|
31
|
+
id: z.uuid(),
|
|
32
|
+
/** What a cell stores to reference the asset; null until it is ready. */
|
|
33
|
+
uri: z.string().nullable(),
|
|
34
|
+
status: mediaAssetStatusSchema,
|
|
35
|
+
contentType: z.string().nullable(),
|
|
36
|
+
fileByteSize: z.number().int(),
|
|
37
|
+
metadata: mediaAssetMetadataSchema.nullable(),
|
|
38
|
+
failureCode: mediaFailureCodeSchema.nullable(),
|
|
39
|
+
createdAt: z.coerce.date(),
|
|
40
|
+
})
|
|
41
|
+
.strict();
|
|
42
|
+
export type MediaAsset = z.infer<typeof mediaAssetSchema>;
|
|
43
|
+
|
|
44
|
+
export const mediaRequestUploadInputSchema = z
|
|
45
|
+
.object({
|
|
46
|
+
contentType: mediaContentTypeSchema,
|
|
47
|
+
// The exact size: the upload is signed for it, and storage refuses any other file.
|
|
48
|
+
byteSize: z.number().int().min(1),
|
|
49
|
+
})
|
|
50
|
+
.strict();
|
|
51
|
+
export type MediaRequestUploadInput = z.infer<typeof mediaRequestUploadInputSchema>;
|
|
52
|
+
|
|
53
|
+
export const mediaRequestUploadOutputSchema = z
|
|
54
|
+
.object({
|
|
55
|
+
assetId: z.uuid(),
|
|
56
|
+
/** A signed PUT: send the file to `url` with exactly `headers`. */
|
|
57
|
+
upload: z.object({ url: z.url(), headers: z.record(z.string(), z.string()) }).strict(),
|
|
58
|
+
expiresAt: z.coerce.date(),
|
|
59
|
+
})
|
|
60
|
+
.strict();
|
|
61
|
+
export type MediaRequestUploadOutput = z.infer<typeof mediaRequestUploadOutputSchema>;
|
|
62
|
+
|
|
63
|
+
export const mediaAssetIdInputSchema = z.object({ id: z.uuid() }).strict();
|
|
64
|
+
export type MediaAssetIdInput = z.infer<typeof mediaAssetIdInputSchema>;
|
|
65
|
+
|
|
66
|
+
export const mediaAssetOutputSchema = z.object({ asset: mediaAssetSchema }).strict();
|
|
67
|
+
export type MediaAssetOutput = z.infer<typeof mediaAssetOutputSchema>;
|
|
68
|
+
|
|
69
|
+
export const mediaUsageOutputSchema = z
|
|
70
|
+
.object({
|
|
71
|
+
usedBytes: z.number().int().min(0),
|
|
72
|
+
/** Held by uploads that are not done yet. */
|
|
73
|
+
reservedBytes: z.number().int().min(0),
|
|
74
|
+
limitBytes: z.number().int().min(0),
|
|
75
|
+
})
|
|
76
|
+
.strict();
|
|
77
|
+
export type MediaUsageOutput = z.infer<typeof mediaUsageOutputSchema>;
|
package/src/schemas/notebase.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
notebaseColumnConfigSchema,
|
|
4
4
|
schedulingParamsShape,
|
|
5
5
|
srsModelVersionSchema,
|
|
6
|
+
srsSleepSourceSchema,
|
|
6
7
|
srsWeightsSchema,
|
|
7
8
|
} from "@read-frog/definitions";
|
|
8
9
|
import { z } from "zod";
|
|
@@ -43,6 +44,8 @@ export const notebaseSchema = z.object({
|
|
|
43
44
|
srsModelVersion: srsModelVersionSchema,
|
|
44
45
|
// Null = awake; set = excluded from the combined review. Written by notebase.setSleep and auto sleep.
|
|
45
46
|
srsSleepingAt: z.date().nullable(),
|
|
47
|
+
// Who put it to sleep; null exactly when awake.
|
|
48
|
+
srsSleepSource: srsSleepSourceSchema.nullable(),
|
|
46
49
|
createdAt: z.date(),
|
|
47
50
|
updatedAt: z.date(),
|
|
48
51
|
});
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { PDF_TRANSLATION_MAX_BATCH_FILES } from "@read-frog/definitions";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { HostedAiModelTierSchema } from "./hosted-ai";
|
|
4
|
+
|
|
5
|
+
export const pdfTranslationJobStatusSchema = z.enum([
|
|
6
|
+
"queued",
|
|
7
|
+
"running",
|
|
8
|
+
"cancel_requested",
|
|
9
|
+
"succeeded",
|
|
10
|
+
"failed",
|
|
11
|
+
"cancelled",
|
|
12
|
+
// Succeeded, but the translation is past its retention and gone.
|
|
13
|
+
"expired",
|
|
14
|
+
]);
|
|
15
|
+
export type PdfTranslationJobStatus = z.infer<typeof pdfTranslationJobStatusSchema>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The two PDFs one translation makes: the translation alone, with the
|
|
19
|
+
* original's layout, and the original and translated pages side by side.
|
|
20
|
+
*/
|
|
21
|
+
export const pdfTranslationOutputVariantSchema = z.enum(["translated", "bilingual"]);
|
|
22
|
+
export type PdfTranslationOutputVariant = z.infer<typeof pdfTranslationOutputVariantSchema>;
|
|
23
|
+
|
|
24
|
+
const languageSchema = z
|
|
25
|
+
.string()
|
|
26
|
+
.min(2)
|
|
27
|
+
.max(40)
|
|
28
|
+
.regex(/^[a-z]{2,3}(?:-[a-z0-9]{2,8})*$/i);
|
|
29
|
+
|
|
30
|
+
export const pdfTranslationRequestUploadInputSchema = z
|
|
31
|
+
.object({
|
|
32
|
+
fileName: z
|
|
33
|
+
.string()
|
|
34
|
+
.trim()
|
|
35
|
+
.min(1)
|
|
36
|
+
.max(255)
|
|
37
|
+
.refine((fileName) => fileName.toLowerCase().endsWith(".pdf"), {
|
|
38
|
+
message: "fileName must end with .pdf",
|
|
39
|
+
}),
|
|
40
|
+
// The exact size: the upload is signed for it, and storage refuses any other file.
|
|
41
|
+
fileByteSize: z.number().int().min(1),
|
|
42
|
+
})
|
|
43
|
+
.strict();
|
|
44
|
+
export type PdfTranslationRequestUploadInput = z.infer<
|
|
45
|
+
typeof pdfTranslationRequestUploadInputSchema
|
|
46
|
+
>;
|
|
47
|
+
|
|
48
|
+
export const pdfTranslationRequestUploadOutputSchema = z
|
|
49
|
+
.object({
|
|
50
|
+
uploadId: z.uuid(),
|
|
51
|
+
// A PUT of the file to `url` with exactly these headers.
|
|
52
|
+
upload: z
|
|
53
|
+
.object({
|
|
54
|
+
url: z.url(),
|
|
55
|
+
headers: z.record(z.string(), z.string()),
|
|
56
|
+
})
|
|
57
|
+
.strict(),
|
|
58
|
+
expiresAt: z.coerce.date(),
|
|
59
|
+
})
|
|
60
|
+
.strict();
|
|
61
|
+
export type PdfTranslationRequestUploadOutput = z.infer<
|
|
62
|
+
typeof pdfTranslationRequestUploadOutputSchema
|
|
63
|
+
>;
|
|
64
|
+
|
|
65
|
+
export const pdfTranslationSubmitInputSchema = z
|
|
66
|
+
.object({
|
|
67
|
+
// The PDFs to translate with the same settings, in the order they run.
|
|
68
|
+
uploadIds: z
|
|
69
|
+
.array(z.uuid())
|
|
70
|
+
.min(1)
|
|
71
|
+
.max(PDF_TRANSLATION_MAX_BATCH_FILES)
|
|
72
|
+
.refine((ids) => new Set(ids).size === ids.length, {
|
|
73
|
+
message: "uploadIds must not repeat",
|
|
74
|
+
}),
|
|
75
|
+
// A retry with the same key returns the jobs the first request made.
|
|
76
|
+
idempotencyKey: z.string().trim().min(1).max(200),
|
|
77
|
+
// Only the target: the model works out the PDF's own language from its text.
|
|
78
|
+
targetLanguage: languageSchema,
|
|
79
|
+
// Under the same rules as every Built-in AI feature: advance is Ultra's.
|
|
80
|
+
modelTier: HostedAiModelTierSchema,
|
|
81
|
+
})
|
|
82
|
+
.strict();
|
|
83
|
+
export type PdfTranslationSubmitInput = z.infer<typeof pdfTranslationSubmitInputSchema>;
|
|
84
|
+
|
|
85
|
+
export const pdfTranslationJobIdInputSchema = z
|
|
86
|
+
.object({
|
|
87
|
+
id: z.uuid(),
|
|
88
|
+
})
|
|
89
|
+
.strict();
|
|
90
|
+
export type PdfTranslationJobIdInput = z.infer<typeof pdfTranslationJobIdInputSchema>;
|
|
91
|
+
|
|
92
|
+
export const pdfTranslationOutputInputSchema = z
|
|
93
|
+
.object({
|
|
94
|
+
id: z.uuid(),
|
|
95
|
+
variant: pdfTranslationOutputVariantSchema,
|
|
96
|
+
})
|
|
97
|
+
.strict();
|
|
98
|
+
export type PdfTranslationOutputInput = z.infer<typeof pdfTranslationOutputInputSchema>;
|
|
99
|
+
|
|
100
|
+
export const pdfTranslationListInputSchema = z.object({}).strict();
|
|
101
|
+
export type PdfTranslationListInput = z.infer<typeof pdfTranslationListInputSchema>;
|
|
102
|
+
|
|
103
|
+
export const pdfTranslationJobResourceSchema = z
|
|
104
|
+
.object({
|
|
105
|
+
id: z.uuid(),
|
|
106
|
+
status: pdfTranslationJobStatusSchema,
|
|
107
|
+
sourceFileName: z.string(),
|
|
108
|
+
targetLanguage: z.string(),
|
|
109
|
+
modelTier: HostedAiModelTierSchema,
|
|
110
|
+
// The PDFs submitted together, which run one at a time in this order.
|
|
111
|
+
batchId: z.uuid(),
|
|
112
|
+
batchPosition: z.number().int().min(0),
|
|
113
|
+
// How many of the account's translations run before a queued one: null
|
|
114
|
+
// once it has started, or when it never will.
|
|
115
|
+
waitingAhead: z.number().int().min(0).nullable(),
|
|
116
|
+
// A Free account's one translation, which it can read here but not download.
|
|
117
|
+
freeTrial: z.boolean(),
|
|
118
|
+
// Which PDFs a succeeded job has. A job translated before both were made
|
|
119
|
+
// has only the one it asked for; any other job has neither.
|
|
120
|
+
outputs: z
|
|
121
|
+
.object({
|
|
122
|
+
translated: z.boolean(),
|
|
123
|
+
bilingual: z.boolean(),
|
|
124
|
+
})
|
|
125
|
+
.strict(),
|
|
126
|
+
pageCount: z.number().int().min(1),
|
|
127
|
+
progressStage: z.string().nullable(),
|
|
128
|
+
progressPercent: z.number().min(0).max(100),
|
|
129
|
+
// Built-in AI credit, in billionths of a US dollar: what admission
|
|
130
|
+
// expected, and what the model calls have used so far.
|
|
131
|
+
estimatedCostNanoUsd: z.number().int().min(0),
|
|
132
|
+
chargedCostNanoUsd: z.number().int().min(0),
|
|
133
|
+
error: z
|
|
134
|
+
.object({
|
|
135
|
+
category: z.string(),
|
|
136
|
+
message: z.string(),
|
|
137
|
+
})
|
|
138
|
+
.strict()
|
|
139
|
+
.nullable(),
|
|
140
|
+
// The worker version that last ran the job, for support.
|
|
141
|
+
workerRelease: z.string().nullable(),
|
|
142
|
+
createdAt: z.coerce.date(),
|
|
143
|
+
startedAt: z.coerce.date().nullable(),
|
|
144
|
+
completedAt: z.coerce.date().nullable(),
|
|
145
|
+
// When a succeeded job's translation stops being downloadable.
|
|
146
|
+
outputExpiresAt: z.coerce.date().nullable(),
|
|
147
|
+
})
|
|
148
|
+
.strict();
|
|
149
|
+
export type PdfTranslationJobResource = z.infer<typeof pdfTranslationJobResourceSchema>;
|
|
150
|
+
|
|
151
|
+
export const pdfTranslationListOutputSchema = z
|
|
152
|
+
.object({
|
|
153
|
+
items: z.array(pdfTranslationJobResourceSchema),
|
|
154
|
+
})
|
|
155
|
+
.strict();
|
|
156
|
+
export type PdfTranslationListOutput = z.infer<typeof pdfTranslationListOutputSchema>;
|
|
157
|
+
|
|
158
|
+
/** What a submission made: its batch, and one job per PDF in their order. */
|
|
159
|
+
export const pdfTranslationSubmitOutputSchema = z
|
|
160
|
+
.object({
|
|
161
|
+
batchId: z.uuid(),
|
|
162
|
+
jobs: z.array(pdfTranslationJobResourceSchema).min(1),
|
|
163
|
+
})
|
|
164
|
+
.strict();
|
|
165
|
+
export type PdfTranslationSubmitOutput = z.infer<typeof pdfTranslationSubmitOutputSchema>;
|
|
166
|
+
|
|
167
|
+
/** A job, and every job of its batch in their order (itself included). */
|
|
168
|
+
export const pdfTranslationGetOutputSchema = z
|
|
169
|
+
.object({
|
|
170
|
+
job: pdfTranslationJobResourceSchema,
|
|
171
|
+
batch: z.array(pdfTranslationJobResourceSchema).min(1),
|
|
172
|
+
})
|
|
173
|
+
.strict();
|
|
174
|
+
export type PdfTranslationGetOutput = z.infer<typeof pdfTranslationGetOutputSchema>;
|
|
175
|
+
|
|
176
|
+
export const pdfTranslationStatusInputSchema = z.object({}).strict();
|
|
177
|
+
export type PdfTranslationStatusInput = z.infer<typeof pdfTranslationStatusInputSchema>;
|
|
178
|
+
|
|
179
|
+
export const pdfTranslationStatusOutputSchema = z
|
|
180
|
+
.object({
|
|
181
|
+
// An account nothing funds translates one PDF, once, on the normal tier:
|
|
182
|
+
// 1 until it has, and always 0 for a funded account.
|
|
183
|
+
freeTrialsRemaining: z.number().int().min(0),
|
|
184
|
+
/**
|
|
185
|
+
* Whether Built-in AI credit pays for PDF translation on the normal tier:
|
|
186
|
+
* a paid plan, or credit the account was given for every feature. Only a
|
|
187
|
+
* funded account submits more than one PDF or downloads a translation.
|
|
188
|
+
*/
|
|
189
|
+
funded: z.boolean(),
|
|
190
|
+
})
|
|
191
|
+
.strict();
|
|
192
|
+
export type PdfTranslationStatusOutput = z.infer<typeof pdfTranslationStatusOutputSchema>;
|
|
193
|
+
|
|
194
|
+
export const pdfTranslationDownloadOutputSchema = z
|
|
195
|
+
.object({
|
|
196
|
+
url: z.url(),
|
|
197
|
+
expiresAt: z.coerce.date(),
|
|
198
|
+
})
|
|
199
|
+
.strict();
|
|
200
|
+
export type PdfTranslationDownloadOutput = z.infer<typeof pdfTranslationDownloadOutputSchema>;
|
|
201
|
+
|
|
202
|
+
export const pdfTranslationPreviewOutputSchema = z
|
|
203
|
+
.object({
|
|
204
|
+
url: z.url(),
|
|
205
|
+
expiresAt: z.coerce.date(),
|
|
206
|
+
/**
|
|
207
|
+
* Whether the URL opens the free translation's copy with the Read Frog
|
|
208
|
+
* watermark, which an account nothing funds reads: upgrading opens the
|
|
209
|
+
* PDF itself.
|
|
210
|
+
*/
|
|
211
|
+
watermarked: z.boolean(),
|
|
212
|
+
})
|
|
213
|
+
.strict();
|
|
214
|
+
export type PdfTranslationPreviewOutput = z.infer<typeof pdfTranslationPreviewOutputSchema>;
|
|
215
|
+
|
|
216
|
+
export const pdfTranslationDeleteOutputSchema = z
|
|
217
|
+
.object({
|
|
218
|
+
id: z.uuid(),
|
|
219
|
+
})
|
|
220
|
+
.strict();
|
|
221
|
+
export type PdfTranslationDeleteOutput = z.infer<typeof pdfTranslationDeleteOutputSchema>;
|
|
222
|
+
|
|
223
|
+
export const PdfTranslationInsufficientCreditDataSchema = z.strictObject({
|
|
224
|
+
estimatedCostNanoUsd: z.number().int().min(0),
|
|
225
|
+
remainingNanoUsd: z.number().int().min(0),
|
|
226
|
+
resetAt: z.iso.datetime().nullable(),
|
|
227
|
+
});
|
|
228
|
+
export type PdfTranslationInsufficientCreditData = z.infer<
|
|
229
|
+
typeof PdfTranslationInsufficientCreditDataSchema
|
|
230
|
+
>;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Which of a submission's PDFs a refusal is about, so the page can point at
|
|
234
|
+
* it. For an upload an earlier request used up, also the translation it made.
|
|
235
|
+
*/
|
|
236
|
+
export const PdfTranslationUploadErrorDataSchema = z.strictObject({
|
|
237
|
+
uploadId: z.uuid(),
|
|
238
|
+
jobId: z.uuid().optional(),
|
|
239
|
+
});
|
|
240
|
+
export type PdfTranslationUploadErrorData = z.infer<typeof PdfTranslationUploadErrorDataSchema>;
|