@read-frog/api-contract 0.12.6 → 0.15.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 +1171 -480
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +301 -103
- package/dist/index.js.map +1 -1
- package/package.json +15 -14
- package/src/contracts/beta-access.ts +3 -6
- package/src/contracts/billing.ts +5 -5
- package/src/contracts/card-template.ts +45 -39
- package/src/contracts/card.ts +22 -19
- package/src/contracts/ebook.ts +13 -11
- package/src/contracts/hosted-ai.ts +5 -5
- package/src/contracts/notebase-column.ts +40 -32
- package/src/contracts/notebase-row.ts +85 -45
- package/src/contracts/notebase.ts +60 -40
- package/src/contracts/shared.ts +21 -2
- package/src/contracts/srs.ts +96 -63
- package/src/contracts/stats.ts +9 -11
- package/src/contracts/user.ts +3 -3
- package/src/contracts/video-transcript.ts +22 -7
- package/src/index.ts +37 -33
- package/src/public-errors.ts +97 -51
- package/src/schemas/beta-access.ts +12 -10
- package/src/schemas/billing.ts +77 -53
- package/src/schemas/card.ts +66 -63
- package/src/schemas/ebook.ts +165 -129
- package/src/schemas/hosted-ai.ts +84 -74
- package/src/schemas/notebase-column.ts +50 -36
- package/src/schemas/notebase-row.ts +88 -57
- package/src/schemas/notebase-stats.ts +28 -20
- package/src/schemas/notebase-view.ts +59 -49
- package/src/schemas/notebase.ts +136 -96
- package/src/schemas/pagination.ts +9 -0
- package/src/schemas/resource-mutation.ts +6 -0
- package/src/schemas/srs-stats.ts +30 -22
- package/src/schemas/srs.ts +152 -106
- package/src/schemas/stats.ts +27 -21
- package/src/schemas/timezone.ts +19 -16
- package/src/schemas/user.ts +27 -19
- package/src/schemas/video-transcript.ts +53 -24
package/src/schemas/srs.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { paginationShape } from "./pagination";
|
|
1
2
|
import {
|
|
2
3
|
activeScheduleStatusSchema,
|
|
3
4
|
cardStateSchema,
|
|
@@ -7,82 +8,84 @@ import {
|
|
|
7
8
|
schedulingParamsSchema,
|
|
8
9
|
SRS_REVIEW_DURATION_MS_MAX,
|
|
9
10
|
srsStepSchema,
|
|
10
|
-
} from "@read-frog/definitions"
|
|
11
|
-
import { z } from "zod"
|
|
12
|
-
import { cardSchema } from "./card"
|
|
13
|
-
import { timezoneSchema } from "./timezone"
|
|
11
|
+
} from "@read-frog/definitions";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { cardSchema } from "./card";
|
|
14
|
+
import { timezoneSchema } from "./timezone";
|
|
14
15
|
|
|
15
|
-
export {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
export { timezoneSchema } from "./timezone"
|
|
21
|
-
export type { Timezone } from "./timezone"
|
|
22
|
-
export type { ReviewRating, SchedulingParams } from "@read-frog/definitions"
|
|
16
|
+
export { reviewRatingSchema, schedulingParamsSchema, SRS_REVIEW_DURATION_MS_MAX };
|
|
17
|
+
export { timezoneSchema } from "./timezone";
|
|
18
|
+
export type { Timezone } from "./timezone";
|
|
19
|
+
export type { ReviewRating, SchedulingParams } from "@read-frog/definitions";
|
|
23
20
|
|
|
24
|
-
export const stepSchema = srsStepSchema
|
|
25
|
-
export type Step = z.infer<typeof stepSchema
|
|
21
|
+
export const stepSchema = srsStepSchema;
|
|
22
|
+
export type Step = z.infer<typeof stepSchema>;
|
|
26
23
|
|
|
27
|
-
export const SRS_REVIEW_CLIENT_ID_NAMESPACE = "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
|
|
28
|
-
const srsReviewClientIdNamePrefix = "readfrog:srs-review"
|
|
29
|
-
const nullLastReviewTimeSentinel = "null"
|
|
24
|
+
export const SRS_REVIEW_CLIENT_ID_NAMESPACE = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
|
|
25
|
+
const srsReviewClientIdNamePrefix = "readfrog:srs-review";
|
|
26
|
+
const nullLastReviewTimeSentinel = "null";
|
|
30
27
|
|
|
31
28
|
export interface SrsReviewClientIdCard {
|
|
32
|
-
id: string
|
|
33
|
-
lastReviewTime: Date | string | null
|
|
29
|
+
id: string;
|
|
30
|
+
lastReviewTime: Date | string | null;
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
export async function createSrsReviewClientId(card: SrsReviewClientIdCard) {
|
|
37
34
|
return uuidV5(
|
|
38
35
|
`${srsReviewClientIdNamePrefix}:${card.id}:${formatLastReviewTime(card.lastReviewTime)}`,
|
|
39
36
|
SRS_REVIEW_CLIENT_ID_NAMESPACE,
|
|
40
|
-
)
|
|
37
|
+
);
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
export const srsReviewInputSchema = z
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
40
|
+
export const srsReviewInputSchema = z
|
|
41
|
+
.object({
|
|
42
|
+
cardId: z.uuid(),
|
|
43
|
+
id: z.uuid().optional(),
|
|
44
|
+
rating: reviewRatingSchema,
|
|
45
|
+
durationMs: z.number().int().min(0).max(SRS_REVIEW_DURATION_MS_MAX),
|
|
46
|
+
timezone: timezoneSchema,
|
|
47
|
+
})
|
|
48
|
+
.strict();
|
|
49
|
+
export type SrsReviewInput = z.infer<typeof srsReviewInputSchema>;
|
|
50
|
+
|
|
51
|
+
export const srsRollbackReviewInputSchema = z
|
|
52
|
+
.object({
|
|
53
|
+
expectedReviewId: z.uuid().optional(),
|
|
54
|
+
cardId: z.uuid(),
|
|
55
|
+
})
|
|
56
|
+
.strict();
|
|
57
|
+
export type SrsRollbackReviewInput = z.infer<typeof srsRollbackReviewInputSchema>;
|
|
56
58
|
|
|
57
59
|
export const srsScheduleStatusStatsSchema = z.record(
|
|
58
60
|
activeScheduleStatusSchema,
|
|
59
61
|
z.number().int().min(0),
|
|
60
|
-
)
|
|
61
|
-
export type SrsScheduleStatusStats = z.infer<typeof srsScheduleStatusStatsSchema
|
|
62
|
-
|
|
63
|
-
export const srsScheduleStatusStatsInputSchema = z
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
export
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
export
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
export
|
|
62
|
+
);
|
|
63
|
+
export type SrsScheduleStatusStats = z.infer<typeof srsScheduleStatusStatsSchema>;
|
|
64
|
+
|
|
65
|
+
export const srsScheduleStatusStatsInputSchema = z
|
|
66
|
+
.object({
|
|
67
|
+
notebaseIds: z.array(z.uuid()).optional(),
|
|
68
|
+
timezone: timezoneSchema,
|
|
69
|
+
})
|
|
70
|
+
.strict();
|
|
71
|
+
export type SrsScheduleStatusStatsInput = z.infer<typeof srsScheduleStatusStatsInputSchema>;
|
|
72
|
+
|
|
73
|
+
export const srsScheduleStatusStatsOutputSchema = z.record(z.uuid(), srsScheduleStatusStatsSchema);
|
|
74
|
+
export type SrsScheduleStatusStatsOutput = z.infer<typeof srsScheduleStatusStatsOutputSchema>;
|
|
75
|
+
|
|
76
|
+
export const srsManualCardStatusInputSchema = z
|
|
77
|
+
.object({
|
|
78
|
+
cardId: z.uuid(),
|
|
79
|
+
enabled: z.boolean(),
|
|
80
|
+
})
|
|
81
|
+
.strict();
|
|
82
|
+
export type SrsManualCardStatusInput = z.infer<typeof srsManualCardStatusInputSchema>;
|
|
83
|
+
|
|
84
|
+
export const srsSetCardBuriedInputSchema = srsManualCardStatusInputSchema;
|
|
85
|
+
export type SrsSetCardBuriedInput = SrsManualCardStatusInput;
|
|
86
|
+
|
|
87
|
+
export const srsSetCardSuspendedInputSchema = srsManualCardStatusInputSchema;
|
|
88
|
+
export type SrsSetCardSuspendedInput = SrsManualCardStatusInput;
|
|
86
89
|
|
|
87
90
|
export const srsRevlogSchema = z.object({
|
|
88
91
|
id: z.uuid(),
|
|
@@ -97,23 +100,30 @@ export const srsRevlogSchema = z.object({
|
|
|
97
100
|
durationMs: z.number().int(),
|
|
98
101
|
fsrsReviewLogSnapshot: fsrsReviewLogSnapshotSchema,
|
|
99
102
|
createdAt: z.coerce.date(),
|
|
100
|
-
})
|
|
101
|
-
export type SrsRevlog = z.infer<typeof srsRevlogSchema
|
|
103
|
+
});
|
|
104
|
+
export type SrsRevlog = z.infer<typeof srsRevlogSchema>;
|
|
102
105
|
|
|
103
106
|
export const srsRevlogWithoutSnapshotSchema = srsRevlogSchema.omit({
|
|
104
107
|
fsrsReviewLogSnapshot: true,
|
|
105
|
-
})
|
|
106
|
-
export type SrsRevlogWithoutSnapshot = z.infer<typeof srsRevlogWithoutSnapshotSchema
|
|
107
|
-
|
|
108
|
-
export const srsListCardRevlogsInputSchema = z
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
108
|
+
});
|
|
109
|
+
export type SrsRevlogWithoutSnapshot = z.infer<typeof srsRevlogWithoutSnapshotSchema>;
|
|
110
|
+
|
|
111
|
+
export const srsListCardRevlogsInputSchema = z
|
|
112
|
+
.object({
|
|
113
|
+
...paginationShape,
|
|
114
|
+
notebaseId: z.uuid().optional(),
|
|
115
|
+
cardId: z.uuid().optional(),
|
|
116
|
+
})
|
|
117
|
+
.strict();
|
|
118
|
+
export type SrsListCardRevlogsInput = z.infer<typeof srsListCardRevlogsInputSchema>;
|
|
119
|
+
|
|
120
|
+
export const srsListCardRevlogsOutputSchema = z
|
|
121
|
+
.object({
|
|
122
|
+
items: z.array(srsRevlogWithoutSnapshotSchema),
|
|
123
|
+
hasMore: z.boolean(),
|
|
124
|
+
})
|
|
125
|
+
.strict();
|
|
126
|
+
export type SrsListCardRevlogsOutput = z.infer<typeof srsListCardRevlogsOutputSchema>;
|
|
117
127
|
|
|
118
128
|
export const srsReviewOutputSchema = z.object({
|
|
119
129
|
card: cardSchema,
|
|
@@ -121,71 +131,107 @@ export const srsReviewOutputSchema = z.object({
|
|
|
121
131
|
txid: z.number(),
|
|
122
132
|
// False when an idempotent replay returned an existing revlog instead of creating one.
|
|
123
133
|
created: z.boolean(),
|
|
124
|
-
})
|
|
125
|
-
export type SrsReviewOutput = z.infer<typeof srsReviewOutputSchema
|
|
134
|
+
});
|
|
135
|
+
export type SrsReviewOutput = z.infer<typeof srsReviewOutputSchema>;
|
|
126
136
|
|
|
127
137
|
export const srsRollbackReviewOutputSchema = z.object({
|
|
128
138
|
card: cardSchema,
|
|
129
139
|
rolledBackRevlogId: z.uuid(),
|
|
130
140
|
txid: z.number(),
|
|
131
|
-
})
|
|
132
|
-
export type SrsRollbackReviewOutput = z.infer<typeof srsRollbackReviewOutputSchema
|
|
141
|
+
});
|
|
142
|
+
export type SrsRollbackReviewOutput = z.infer<typeof srsRollbackReviewOutputSchema>;
|
|
133
143
|
|
|
134
144
|
export const srsTxidOutputSchema = z.object({
|
|
135
145
|
txid: z.number(),
|
|
136
|
-
})
|
|
137
|
-
export type SrsTxidOutput = z.infer<typeof srsTxidOutputSchema
|
|
138
|
-
|
|
139
|
-
export const srsSetCardBuriedOutputSchema = srsTxidOutputSchema
|
|
140
|
-
export type SrsSetCardBuriedOutput = SrsTxidOutput
|
|
141
|
-
|
|
142
|
-
export const srsSetCardSuspendedOutputSchema = srsTxidOutputSchema
|
|
143
|
-
export type SrsSetCardSuspendedOutput = SrsTxidOutput
|
|
146
|
+
});
|
|
147
|
+
export type SrsTxidOutput = z.infer<typeof srsTxidOutputSchema>;
|
|
148
|
+
|
|
149
|
+
export const srsSetCardBuriedOutputSchema = srsTxidOutputSchema;
|
|
150
|
+
export type SrsSetCardBuriedOutput = SrsTxidOutput;
|
|
151
|
+
|
|
152
|
+
export const srsSetCardSuspendedOutputSchema = srsTxidOutputSchema;
|
|
153
|
+
export type SrsSetCardSuspendedOutput = SrsTxidOutput;
|
|
154
|
+
|
|
155
|
+
export const srsListUnreadOptimizerResultsInputSchema = z.object({}).strict();
|
|
156
|
+
|
|
157
|
+
export const srsOptimizerUnreadResultSchema = z
|
|
158
|
+
.object({
|
|
159
|
+
runId: z.uuid(),
|
|
160
|
+
notebaseName: z.string().min(1),
|
|
161
|
+
revlogCount: z.number().int().positive(),
|
|
162
|
+
improvement: z.number().nonnegative().nullable(),
|
|
163
|
+
})
|
|
164
|
+
.strict();
|
|
165
|
+
export type SrsOptimizerUnreadResult = z.infer<typeof srsOptimizerUnreadResultSchema>;
|
|
166
|
+
|
|
167
|
+
export const srsListUnreadOptimizerResultsOutputSchema = z.array(srsOptimizerUnreadResultSchema);
|
|
168
|
+
export type SrsListUnreadOptimizerResultsOutput = z.infer<
|
|
169
|
+
typeof srsListUnreadOptimizerResultsOutputSchema
|
|
170
|
+
>;
|
|
171
|
+
|
|
172
|
+
export const srsAcknowledgeOptimizerResultsInputSchema = z
|
|
173
|
+
.object({
|
|
174
|
+
runIds: z.array(z.uuid()).min(1),
|
|
175
|
+
})
|
|
176
|
+
.strict();
|
|
177
|
+
export type SrsAcknowledgeOptimizerResultsInput = z.infer<
|
|
178
|
+
typeof srsAcknowledgeOptimizerResultsInputSchema
|
|
179
|
+
>;
|
|
180
|
+
|
|
181
|
+
export const srsAcknowledgeOptimizerResultsOutputSchema = z
|
|
182
|
+
.object({
|
|
183
|
+
acknowledgedCount: z.number().int().nonnegative(),
|
|
184
|
+
})
|
|
185
|
+
.strict();
|
|
186
|
+
export type SrsAcknowledgeOptimizerResultsOutput = z.infer<
|
|
187
|
+
typeof srsAcknowledgeOptimizerResultsOutputSchema
|
|
188
|
+
>;
|
|
144
189
|
|
|
145
190
|
function formatLastReviewTime(lastReviewTime: Date | string | null) {
|
|
146
|
-
return lastReviewTime ? new Date(lastReviewTime).toISOString() : nullLastReviewTimeSentinel
|
|
191
|
+
return lastReviewTime ? new Date(lastReviewTime).toISOString() : nullLastReviewTimeSentinel;
|
|
147
192
|
}
|
|
148
193
|
|
|
149
194
|
async function uuidV5(name: string, namespace: string) {
|
|
150
|
-
const namespaceBytes = uuidToBytes(namespace)
|
|
151
|
-
const nameBytes = new TextEncoder().encode(name)
|
|
152
|
-
const input = new Uint8Array(namespaceBytes.length + nameBytes.length)
|
|
153
|
-
input.set(namespaceBytes)
|
|
154
|
-
input.set(nameBytes, namespaceBytes.length)
|
|
155
|
-
|
|
156
|
-
const hash = new Uint8Array(await crypto.subtle.digest("SHA-1", input))
|
|
157
|
-
const bytes = hash.slice(0, 16)
|
|
158
|
-
const versionByte = bytes[6]
|
|
159
|
-
const variantByte = bytes[8]
|
|
195
|
+
const namespaceBytes = uuidToBytes(namespace);
|
|
196
|
+
const nameBytes = new TextEncoder().encode(name);
|
|
197
|
+
const input = new Uint8Array(namespaceBytes.length + nameBytes.length);
|
|
198
|
+
input.set(namespaceBytes);
|
|
199
|
+
input.set(nameBytes, namespaceBytes.length);
|
|
200
|
+
|
|
201
|
+
const hash = new Uint8Array(await crypto.subtle.digest("SHA-1", input));
|
|
202
|
+
const bytes = hash.slice(0, 16);
|
|
203
|
+
const versionByte = bytes[6];
|
|
204
|
+
const variantByte = bytes[8];
|
|
160
205
|
if (versionByte === undefined || variantByte === undefined) {
|
|
161
|
-
throw new Error("SHA-1 digest was too short")
|
|
206
|
+
throw new Error("SHA-1 digest was too short");
|
|
162
207
|
}
|
|
163
|
-
bytes[6] = (versionByte &
|
|
164
|
-
bytes[8] = (variantByte &
|
|
208
|
+
bytes[6] = (versionByte & 0x0f) | 0x50;
|
|
209
|
+
bytes[8] = (variantByte & 0x3f) | 0x80;
|
|
165
210
|
|
|
166
|
-
return bytesToUuid(bytes)
|
|
211
|
+
return bytesToUuid(bytes);
|
|
167
212
|
}
|
|
168
213
|
|
|
169
214
|
function uuidToBytes(uuid: string) {
|
|
170
|
-
const hex = uuid.replaceAll("-", "").toLowerCase()
|
|
215
|
+
const hex = uuid.replaceAll("-", "").toLowerCase();
|
|
171
216
|
/* v8 ignore next 3 -- SRS_REVIEW_CLIENT_ID_NAMESPACE is a fixed valid UUID. */
|
|
172
217
|
if (!/^[0-9a-f]{32}$/.test(hex)) {
|
|
173
|
-
throw new Error("Invalid UUID namespace")
|
|
218
|
+
throw new Error("Invalid UUID namespace");
|
|
174
219
|
}
|
|
175
220
|
|
|
176
221
|
return new Uint8Array(
|
|
177
222
|
Array.from({ length: 16 }, (_, index) =>
|
|
178
|
-
Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16)
|
|
179
|
-
|
|
223
|
+
Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16),
|
|
224
|
+
),
|
|
225
|
+
);
|
|
180
226
|
}
|
|
181
227
|
|
|
182
228
|
function bytesToUuid(bytes: Uint8Array) {
|
|
183
|
-
const hex = Array.from(bytes, byte => byte.toString(16).padStart(2, "0"))
|
|
229
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
|
|
184
230
|
return [
|
|
185
231
|
hex.slice(0, 4).join(""),
|
|
186
232
|
hex.slice(4, 6).join(""),
|
|
187
233
|
hex.slice(6, 8).join(""),
|
|
188
234
|
hex.slice(8, 10).join(""),
|
|
189
235
|
hex.slice(10, 16).join(""),
|
|
190
|
-
].join("-")
|
|
236
|
+
].join("-");
|
|
191
237
|
}
|
package/src/schemas/stats.ts
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
|
-
import { z } from "zod"
|
|
2
|
-
import { notebaseActivityStatsOutputSchema } from "./notebase-stats"
|
|
3
|
-
import { srsActivityStatsOutputSchema } from "./srs-stats"
|
|
4
|
-
import { timezoneSchema } from "./timezone"
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { notebaseActivityStatsOutputSchema } from "./notebase-stats";
|
|
3
|
+
import { srsActivityStatsOutputSchema } from "./srs-stats";
|
|
4
|
+
import { timezoneSchema } from "./timezone";
|
|
5
5
|
|
|
6
|
-
const statsNotebaseIdsSchema = z
|
|
6
|
+
const statsNotebaseIdsSchema = z
|
|
7
|
+
.array(z.uuid())
|
|
7
8
|
.min(1)
|
|
8
|
-
.transform(ids => Array.from(new Set(ids)))
|
|
9
|
+
.transform((ids) => Array.from(new Set(ids)));
|
|
9
10
|
|
|
10
|
-
export const statsInputSchema = z
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
export const statsInputSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
notebaseIds: statsNotebaseIdsSchema.optional(),
|
|
14
|
+
from: z.iso.date().optional(),
|
|
15
|
+
to: z.iso.date(),
|
|
16
|
+
timezone: timezoneSchema,
|
|
17
|
+
})
|
|
18
|
+
.strict()
|
|
19
|
+
.refine((input) => !input.from || input.from <= input.to, {
|
|
20
|
+
message: "from must be before or equal to to",
|
|
21
|
+
path: ["from"],
|
|
22
|
+
});
|
|
23
|
+
export type StatsInput = z.infer<typeof statsInputSchema>;
|
|
20
24
|
|
|
21
|
-
export const activityStatsOutputSchema = z
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
export const activityStatsOutputSchema = z
|
|
26
|
+
.object({
|
|
27
|
+
srs: srsActivityStatsOutputSchema,
|
|
28
|
+
notebase: notebaseActivityStatsOutputSchema,
|
|
29
|
+
})
|
|
30
|
+
.strict();
|
|
31
|
+
export type ActivityStatsOutput = z.infer<typeof activityStatsOutputSchema>;
|
package/src/schemas/timezone.ts
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
|
-
import { z } from "zod"
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
// @ref https://zenn.dev/herp_inc/articles/js-intl-date-time-format-performance
|
|
4
|
-
const localeTimeZoneFormatter = new Map<string, Intl.DateTimeFormat>()
|
|
4
|
+
const localeTimeZoneFormatter = new Map<string, Intl.DateTimeFormat>();
|
|
5
5
|
function createTimezoneFormatter(timezone: string): Intl.DateTimeFormat {
|
|
6
|
-
let formatter = localeTimeZoneFormatter.get(timezone)
|
|
6
|
+
let formatter = localeTimeZoneFormatter.get(timezone);
|
|
7
7
|
if (!formatter) {
|
|
8
|
-
formatter = new Intl.DateTimeFormat("en-US", { timeZone: timezone })
|
|
9
|
-
localeTimeZoneFormatter.set(timezone, formatter)
|
|
8
|
+
formatter = new Intl.DateTimeFormat("en-US", { timeZone: timezone });
|
|
9
|
+
localeTimeZoneFormatter.set(timezone, formatter);
|
|
10
10
|
}
|
|
11
|
-
return formatter
|
|
11
|
+
return formatter;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export const timezoneSchema = z
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
14
|
+
export const timezoneSchema = z
|
|
15
|
+
.string()
|
|
16
|
+
.trim()
|
|
17
|
+
.min(1)
|
|
18
|
+
.refine((timezone) => {
|
|
19
|
+
try {
|
|
20
|
+
createTimezoneFormatter(timezone).format(new Date());
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}, "Invalid timezone");
|
|
26
|
+
export type Timezone = z.infer<typeof timezoneSchema>;
|
package/src/schemas/user.ts
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
|
-
import { z } from "zod"
|
|
2
|
-
import { timezoneSchema } from "./timezone"
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { timezoneSchema } from "./timezone";
|
|
3
3
|
|
|
4
|
-
export const userEnsureTimezoneInputSchema = z
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
export const userEnsureTimezoneInputSchema = z
|
|
5
|
+
.object({
|
|
6
|
+
timezone: timezoneSchema,
|
|
7
|
+
})
|
|
8
|
+
.strict();
|
|
9
|
+
export type UserEnsureTimezoneInput = z.infer<typeof userEnsureTimezoneInputSchema>;
|
|
8
10
|
|
|
9
|
-
export const userEnsureTimezoneOutputSchema = z
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
export const userEnsureTimezoneOutputSchema = z
|
|
12
|
+
.object({
|
|
13
|
+
timezone: timezoneSchema,
|
|
14
|
+
updated: z.boolean(),
|
|
15
|
+
})
|
|
16
|
+
.strict();
|
|
17
|
+
export type UserEnsureTimezoneOutput = z.infer<typeof userEnsureTimezoneOutputSchema>;
|
|
14
18
|
|
|
15
|
-
export const userUpdateTimezoneInputSchema = z
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
export const userUpdateTimezoneInputSchema = z
|
|
20
|
+
.object({
|
|
21
|
+
timezone: timezoneSchema,
|
|
22
|
+
})
|
|
23
|
+
.strict();
|
|
24
|
+
export type UserUpdateTimezoneInput = z.infer<typeof userUpdateTimezoneInputSchema>;
|
|
19
25
|
|
|
20
|
-
export const userUpdateTimezoneOutputSchema = z
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
export const userUpdateTimezoneOutputSchema = z
|
|
27
|
+
.object({
|
|
28
|
+
timezone: timezoneSchema,
|
|
29
|
+
})
|
|
30
|
+
.strict();
|
|
31
|
+
export type UserUpdateTimezoneOutput = z.infer<typeof userUpdateTimezoneOutputSchema>;
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { getVideoIdFromUrl, transcriptSegmentSchema } from "@read-frog/definitions"
|
|
2
|
-
import { z } from "zod"
|
|
1
|
+
import { getVideoIdFromUrl, transcriptSegmentSchema } from "@read-frog/definitions";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { billingPlanSchema } from "./billing";
|
|
3
4
|
|
|
4
|
-
export const videoTranscriptStatusSchema = z.enum([
|
|
5
|
-
|
|
6
|
-
"processing",
|
|
7
|
-
"completed",
|
|
8
|
-
"failed",
|
|
9
|
-
])
|
|
10
|
-
export type VideoTranscriptStatus = z.infer<typeof videoTranscriptStatusSchema>
|
|
5
|
+
export const videoTranscriptStatusSchema = z.enum(["pending", "processing", "completed", "failed"]);
|
|
6
|
+
export type VideoTranscriptStatus = z.infer<typeof videoTranscriptStatusSchema>;
|
|
11
7
|
|
|
12
8
|
// The transcript "job" resource: status + metadata, without the (heavy) segments payload.
|
|
13
9
|
export const videoTranscriptResourceSchema = z.object({
|
|
@@ -22,31 +18,64 @@ export const videoTranscriptResourceSchema = z.object({
|
|
|
22
18
|
error: z.string().nullable(),
|
|
23
19
|
createdAt: z.date(),
|
|
24
20
|
updatedAt: z.date(),
|
|
25
|
-
})
|
|
26
|
-
export type VideoTranscriptResource = z.infer<typeof videoTranscriptResourceSchema
|
|
21
|
+
});
|
|
22
|
+
export type VideoTranscriptResource = z.infer<typeof videoTranscriptResourceSchema>;
|
|
27
23
|
|
|
28
24
|
// The subtitles sub-resource: the segments + model-detected language, only available once completed.
|
|
29
25
|
export const videoTranscriptSubtitlesSchema = z.object({
|
|
30
26
|
id: z.string(),
|
|
31
27
|
detectedLanguage: z.string().nullable(),
|
|
32
28
|
segments: z.array(transcriptSegmentSchema),
|
|
33
|
-
})
|
|
34
|
-
export type VideoTranscriptSubtitles = z.infer<typeof videoTranscriptSubtitlesSchema
|
|
29
|
+
});
|
|
30
|
+
export type VideoTranscriptSubtitles = z.infer<typeof videoTranscriptSubtitlesSchema>;
|
|
35
31
|
|
|
36
|
-
export const videoTranscriptCreateInputSchema = z
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
export const videoTranscriptCreateInputSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
url: z.url().refine((url) => getVideoIdFromUrl(url) !== null, {
|
|
35
|
+
message: "url must be a supported video URL",
|
|
36
|
+
}),
|
|
37
|
+
// The player's own duration, read from the media element. REQUIRED, and an
|
|
38
|
+
// untrusted UX hint only: admission uses it to reject an over-quota request
|
|
39
|
+
// instantly, but every reservation and charge is made against the
|
|
40
|
+
// server-measured duration in the worker — lying here changes nothing about
|
|
41
|
+
// what gets billed.
|
|
42
|
+
durationSec: z.number().int().positive(),
|
|
43
|
+
})
|
|
44
|
+
.strict();
|
|
45
|
+
export type VideoTranscriptCreateInput = z.infer<typeof videoTranscriptCreateInputSchema>;
|
|
40
46
|
|
|
41
|
-
export const videoTranscriptGetInputSchema = z
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
export const videoTranscriptGetInputSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
id: z.uuid(),
|
|
50
|
+
})
|
|
51
|
+
.strict();
|
|
52
|
+
export type VideoTranscriptGetInput = z.infer<typeof videoTranscriptGetInputSchema>;
|
|
45
53
|
|
|
46
|
-
//
|
|
54
|
+
// One quota pool as shown to the user. `resetAt` is null for a one-time grant
|
|
55
|
+
// (it never resets); `expiresAt` is null for a monthly pool (its end is the
|
|
56
|
+
// reset). Minutes are display units: `usedMinutes` rounds up and
|
|
57
|
+
// `limitMinutes` rounds down, so the UI can never show headroom that
|
|
58
|
+
// admission (which compares raw seconds) would deny.
|
|
59
|
+
export const videoTranscriptUsagePoolSchema = z.object({
|
|
60
|
+
id: z.string(),
|
|
61
|
+
usedMinutes: z.number().int(),
|
|
62
|
+
limitMinutes: z.number().int(),
|
|
63
|
+
remainingMinutes: z.number().int(),
|
|
64
|
+
resetAt: z.iso.datetime().nullable(),
|
|
65
|
+
expiresAt: z.iso.datetime().nullable(),
|
|
66
|
+
});
|
|
67
|
+
export type VideoTranscriptUsagePool = z.infer<typeof videoTranscriptUsagePoolSchema>;
|
|
68
|
+
|
|
69
|
+
// The caller's transcription quota usage. Totals aggregate across pools;
|
|
70
|
+
// `remainingMinutes` nets out in-flight reservations (committed), while each
|
|
71
|
+
// pool's `usedMinutes` shows settled spend only, so a progress bar cannot run
|
|
72
|
+
// forward and then backwards when a job is released. Deliberately a non-strict
|
|
73
|
+
// object: old clients strip the keys they don't know.
|
|
47
74
|
export const videoTranscriptUsageSchema = z.object({
|
|
48
75
|
usedMinutes: z.number().int(),
|
|
49
76
|
limitMinutes: z.number().int(),
|
|
50
77
|
remainingMinutes: z.number().int(),
|
|
51
|
-
|
|
52
|
-
|
|
78
|
+
plan: billingPlanSchema,
|
|
79
|
+
pools: z.array(videoTranscriptUsagePoolSchema),
|
|
80
|
+
});
|
|
81
|
+
export type VideoTranscriptUsage = z.infer<typeof videoTranscriptUsageSchema>;
|