@read-frog/api-contract 0.25.0 → 0.26.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 +655 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/contracts/card.ts +16 -0
- package/src/contracts/notebase-row.ts +2 -2
- package/src/public-errors.ts +24 -0
- package/src/schemas/card.ts +10 -0
- package/src/schemas/notebase-row.ts +30 -0
- package/src/schemas/notebase.ts +6 -1
- package/src/schemas/web-sync.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@read-frog/api-contract",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.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.
|
|
28
|
+
"@read-frog/definitions": "0.14.0",
|
|
29
29
|
"zod": "^4.6.7"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
package/src/contracts/card.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { defineAgentProcedure } from "./shared";
|
|
|
2
2
|
import { oc } from "@orpc/contract";
|
|
3
3
|
import { pickPublicErrorMap } from "#/public-errors";
|
|
4
4
|
import {
|
|
5
|
+
CardDeleteInputSchema,
|
|
6
|
+
CardDeleteOutputSchema,
|
|
5
7
|
CardGenerateInputSchema,
|
|
6
8
|
CardGenerateOutputSchema,
|
|
7
9
|
CardGetInputSchema,
|
|
@@ -11,6 +13,20 @@ import {
|
|
|
11
13
|
} from "#/schemas/card";
|
|
12
14
|
|
|
13
15
|
export const cardContract = {
|
|
16
|
+
delete: oc
|
|
17
|
+
.errors(
|
|
18
|
+
pickPublicErrorMap(
|
|
19
|
+
"CARD_NOT_FOUND",
|
|
20
|
+
"NOTEBASE_NOT_FOUND",
|
|
21
|
+
"CARD_DELETE_UNSUPPORTED",
|
|
22
|
+
"RESOURCE_UPDATE_CONFLICT",
|
|
23
|
+
"CELL_VALIDATION_FAILED",
|
|
24
|
+
),
|
|
25
|
+
)
|
|
26
|
+
.route({ method: "DELETE", path: "/cards/{id}", summary: "Delete card", tags: ["Cards"] })
|
|
27
|
+
.input(CardDeleteInputSchema)
|
|
28
|
+
.output(CardDeleteOutputSchema),
|
|
29
|
+
|
|
14
30
|
list: defineAgentProcedure({
|
|
15
31
|
method: "GET",
|
|
16
32
|
path: "/cards",
|
|
@@ -92,7 +92,7 @@ export const notebaseRowContract = {
|
|
|
92
92
|
path: "/notebase-rows/{notebaseRowId}",
|
|
93
93
|
summary: "Update note",
|
|
94
94
|
description:
|
|
95
|
-
"Merge supplied fields into an owned note, preserving omitted fields. Use null to clear a field. expectedUpdatedAt guards against editing stale data; reread on a modification conflict.",
|
|
95
|
+
"Merge supplied fields into an owned note, preserving omitted fields. Use null to clear a field. expectedUpdatedAt guards against editing stale data; reread on a modification conflict. In a cloze field, a deletion's number ({{c1::…}}) is its card's identity: keep the numbers of the deletions you keep, and leave any gaps for the save to close. Renumbering them yourself hands a card's review history to other text, and can delete it.",
|
|
96
96
|
tags: ["Notebase Rows"],
|
|
97
97
|
})
|
|
98
98
|
.errors(pickPublicErrorMap("NOTEBASE_ROW_NOT_FOUND", "CELL_VALIDATION_FAILED"))
|
|
@@ -105,7 +105,7 @@ export const notebaseRowContract = {
|
|
|
105
105
|
path: "/notebase-rows/batch",
|
|
106
106
|
summary: "Update notes",
|
|
107
107
|
description:
|
|
108
|
-
"Update 1–50 notes atomically in one owned notebase. Each row supplies its ID and fields to merge; omitted fields are preserved and null clears a field. Supply each note's expectedUpdatedAt to guard against stale edits. A missing row, invalid field or modification conflict rolls back the whole batch. Items are returned in input order.",
|
|
108
|
+
"Update 1–50 notes atomically in one owned notebase. Each row supplies its ID and fields to merge; omitted fields are preserved and null clears a field. Supply each note's expectedUpdatedAt to guard against stale edits. A missing row, invalid field or modification conflict rolls back the whole batch. Items are returned in input order. In a cloze field, a deletion's number ({{c1::…}}) is its card's identity: keep the numbers of the deletions you keep, and leave any gaps for the save to close. Renumbering them yourself hands a card's review history to other text, and can delete it.",
|
|
109
109
|
tags: ["Notebase Rows"],
|
|
110
110
|
})
|
|
111
111
|
.errors(
|
package/src/public-errors.ts
CHANGED
|
@@ -21,6 +21,24 @@ export const CellValidationFailureReasonSchema = z.enum([
|
|
|
21
21
|
"type_mismatch",
|
|
22
22
|
"invalid_select_option",
|
|
23
23
|
"invalid_date_format",
|
|
24
|
+
// A cloze deletion with a blank answer; receivedValue is the deletion as
|
|
25
|
+
// written (`{{c1::}}`).
|
|
26
|
+
"empty_cloze_answer",
|
|
27
|
+
// A cloze opening that never closes (unpaired braces, or no `}}`);
|
|
28
|
+
// receivedValue is the opening as written (`{{c1::`).
|
|
29
|
+
"unclosed_cloze",
|
|
30
|
+
// A cloze deletion whose ordinal list names no group (`{{c0::a}}`);
|
|
31
|
+
// receivedValue is the deletion as written.
|
|
32
|
+
"invalid_cloze_ordinal",
|
|
33
|
+
// A cloze deletion that cuts across a formula's delimiters (`\(a{{c1::b\) c}}`);
|
|
34
|
+
// receivedValue is the deletion as written.
|
|
35
|
+
"cloze_crosses_math",
|
|
36
|
+
// A cloze deletion in a formula that holds half of a `\left`/`\right` or
|
|
37
|
+
// `\begin`/`\end` pair; receivedValue is the deletion as written.
|
|
38
|
+
"cloze_splits_math_pair",
|
|
39
|
+
// More cloze groups in one note than CLOZE_MAX_GROUPS; receivedValue is
|
|
40
|
+
// the note's group count.
|
|
41
|
+
"too_many_cloze_groups",
|
|
24
42
|
]);
|
|
25
43
|
export type CellValidationFailureReason = z.infer<typeof CellValidationFailureReasonSchema>;
|
|
26
44
|
|
|
@@ -42,6 +60,8 @@ export const CellValidationFailedDataSchema = z
|
|
|
42
60
|
reason: CellValidationFailureReasonSchema,
|
|
43
61
|
/** Index of the failing row within a batch input (createMany / updateMany / initialRows). */
|
|
44
62
|
rowIndex: z.number().int().min(0).optional(),
|
|
63
|
+
/** The failing note, when the check ran over stored notes (a column type change). */
|
|
64
|
+
notebaseRowId: z.string().optional(),
|
|
45
65
|
})
|
|
46
66
|
.strict();
|
|
47
67
|
export type CellValidationFailedData = z.infer<typeof CellValidationFailedDataSchema>;
|
|
@@ -103,6 +123,10 @@ export const SpeechDailyLimitReachedDataSchema = z.strictObject({
|
|
|
103
123
|
});
|
|
104
124
|
|
|
105
125
|
export const PUBLIC_APP_ERROR_DEFS = {
|
|
126
|
+
CARD_DELETE_UNSUPPORTED: {
|
|
127
|
+
message: "This card cannot be deleted independently of its note.",
|
|
128
|
+
status: 400,
|
|
129
|
+
},
|
|
106
130
|
RESOURCE_UPDATE_CONFLICT: {
|
|
107
131
|
message: "The resource changed. Read it again before updating.",
|
|
108
132
|
status: 409,
|
package/src/schemas/card.ts
CHANGED
|
@@ -114,6 +114,16 @@ export type CardGetInput = z.infer<typeof CardGetInputSchema>;
|
|
|
114
114
|
export const CardGetOutputSchema = renderedCardSchema;
|
|
115
115
|
export type CardGetOutput = z.infer<typeof CardGetOutputSchema>;
|
|
116
116
|
|
|
117
|
+
export const CardDeleteInputSchema = z.strictObject({
|
|
118
|
+
id: z.uuid(),
|
|
119
|
+
expectedVariantKey: z.string().min(1),
|
|
120
|
+
expectedRowUpdatedAt: expectedUpdatedAtSchema,
|
|
121
|
+
});
|
|
122
|
+
export type CardDeleteInput = z.infer<typeof CardDeleteInputSchema>;
|
|
123
|
+
|
|
124
|
+
export const CardDeleteOutputSchema = z.object({ txid: z.number() });
|
|
125
|
+
export type CardDeleteOutput = z.infer<typeof CardDeleteOutputSchema>;
|
|
126
|
+
|
|
117
127
|
export const CardGenerateInputSchema = z.object({
|
|
118
128
|
notebaseId: z.uuid(),
|
|
119
129
|
templateId: z.uuid().optional(),
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { paginationShape, pageSchema } from "./pagination";
|
|
2
2
|
import { deletionResultSchema, expectedUpdatedAtSchema } from "./resource-mutation";
|
|
3
|
+
import {
|
|
4
|
+
noteCreatedViaSchema,
|
|
5
|
+
noteOriginInputSchema,
|
|
6
|
+
noteOriginSchema,
|
|
7
|
+
} from "@read-frog/definitions";
|
|
3
8
|
import { z } from "zod";
|
|
4
9
|
|
|
5
10
|
export const notebaseRowCellsSchema = z.record(z.string(), z.unknown());
|
|
@@ -9,15 +14,38 @@ export const notebaseRowSchema = z.object({
|
|
|
9
14
|
notebaseId: z.string(),
|
|
10
15
|
cells: notebaseRowCellsSchema,
|
|
11
16
|
position: z.number(),
|
|
17
|
+
origin: noteOriginSchema
|
|
18
|
+
.nullish()
|
|
19
|
+
.describe(
|
|
20
|
+
"Where the note's content came from, set when it was created: a web page (url, title), a book on the user's shelf (ebookId, its title then, and cfi, the EPUB location of the selection), or a video (url, title, seconds into it). Null when it has none, or when it was created before this was recorded.",
|
|
21
|
+
),
|
|
22
|
+
createdVia: noteCreatedViaSchema
|
|
23
|
+
.nullish()
|
|
24
|
+
.describe(
|
|
25
|
+
'Which client created the note: "web", "extension", "mobile", "mcp" (with the MCP client\'s id and the name it registered under) or "unknown". Null for notes created before this was recorded.',
|
|
26
|
+
),
|
|
12
27
|
createdAt: z.coerce.date(),
|
|
13
28
|
updatedAt: z.coerce.date(),
|
|
14
29
|
});
|
|
15
30
|
export type NotebaseRow = z.infer<typeof notebaseRowSchema>;
|
|
16
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Where a new note's content came from; see `noteOriginInputSchema`. Optional
|
|
34
|
+
* metadata never costs the note: an origin that does not parse is dropped and
|
|
35
|
+
* the note is saved without one.
|
|
36
|
+
*/
|
|
37
|
+
export const notebaseRowOriginInputSchema = noteOriginInputSchema
|
|
38
|
+
.optional()
|
|
39
|
+
.catch(undefined)
|
|
40
|
+
.describe(
|
|
41
|
+
"Where the note's content came from: a web page or video by its http(s) URL, or a book on the user's shelf by its id. Stored once and never edited. Optional: an origin that cannot be used is left out and the note is still saved.",
|
|
42
|
+
);
|
|
43
|
+
|
|
17
44
|
export const notebaseRowCreateDataSchema = z
|
|
18
45
|
.object({
|
|
19
46
|
id: z.uuid().optional(),
|
|
20
47
|
cells: notebaseRowCellsSchema.optional(),
|
|
48
|
+
origin: notebaseRowOriginInputSchema,
|
|
21
49
|
})
|
|
22
50
|
.strict();
|
|
23
51
|
export type NotebaseRowCreateData = z.infer<typeof notebaseRowCreateDataSchema>;
|
|
@@ -88,6 +116,8 @@ export const NotebaseRowUpdateOutputSchema = z.object({
|
|
|
88
116
|
notebaseId: z.uuid(),
|
|
89
117
|
cells: notebaseRowCellsSchema,
|
|
90
118
|
position: z.number().int(),
|
|
119
|
+
origin: notebaseRowSchema.shape.origin,
|
|
120
|
+
createdVia: notebaseRowSchema.shape.createdVia,
|
|
91
121
|
createdAt: z.date(),
|
|
92
122
|
updatedAt: z.date(),
|
|
93
123
|
txid: z.number(),
|
package/src/schemas/notebase.ts
CHANGED
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
} from "@read-frog/definitions";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { notebaseColumnSchema } from "./notebase-column";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
NOTEBASE_ROW_CREATE_MANY_MAX_ROWS,
|
|
13
|
+
notebaseRowOriginInputSchema,
|
|
14
|
+
notebaseRowSchema,
|
|
15
|
+
} from "./notebase-row";
|
|
12
16
|
import { notebaseViewSchema } from "./notebase-view";
|
|
13
17
|
|
|
14
18
|
export const NotebaseListInputSchema = z.object({});
|
|
@@ -55,6 +59,7 @@ const notebaseInitialRowSchema = z
|
|
|
55
59
|
.object({
|
|
56
60
|
id: z.uuid().optional(),
|
|
57
61
|
cells: z.record(z.string(), z.unknown()),
|
|
62
|
+
origin: notebaseRowOriginInputSchema,
|
|
58
63
|
})
|
|
59
64
|
.strict();
|
|
60
65
|
|