teak-sdk 1.0.88 → 1.0.90
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/README.md +13 -20
- package/index.js +124 -20
- package/package.json +1 -1
- package/types/convex/shared/constants.d.ts +6 -0
- package/types/convex/shared/tags.d.ts +13 -0
- package/types/convex/shared/utils/formatBytes.d.ts +5 -1
- package/types/index.d.ts +119 -6
package/README.md
CHANGED
|
@@ -1,30 +1,23 @@
|
|
|
1
1
|
# Teak SDK
|
|
2
2
|
|
|
3
|
-
Typed API client and OAuth discovery for Teak.
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Typed API client and OAuth discovery for [Teak](https://teakvault.com). One
|
|
4
|
+
bundle with no runtime dependencies.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm i teak-sdk
|
|
8
|
+
```
|
|
6
9
|
|
|
7
10
|
```ts
|
|
8
|
-
import { createTeakClient,
|
|
11
|
+
import { createTeakClient, staticTokenProvider } from "teak-sdk";
|
|
9
12
|
|
|
10
|
-
const auth = await discoverAuthServer("https://teakvault.com/api");
|
|
11
13
|
const client = createTeakClient({
|
|
12
14
|
baseUrl: "https://teakvault.com/api",
|
|
13
|
-
tokenProvider:
|
|
15
|
+
tokenProvider: staticTokenProvider(process.env.TEAK_API_KEY!),
|
|
14
16
|
});
|
|
15
|
-
```
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
`requestConnectTokens`, `fetchConnectOwnerId`, `disconnectConnectGrant`) cover
|
|
20
|
-
PKCE sign-in, refresh, account binding and disconnect. Refresh
|
|
21
|
-
discovery after sign-in or refresh failures. Keep tokens in your platform's secure
|
|
22
|
-
storage; this package does not store credentials or open a browser.
|
|
23
|
-
|
|
24
|
-
The optional `TokenProvider.onUnauthorized(rejectedToken: string)` callback receives
|
|
25
|
-
the token used by a retry-eligible 401 response. Return a new or already-refreshed
|
|
26
|
-
token to retry once, or `null` to stop. Check the rejected token against the current
|
|
27
|
-
session before refreshing so an older response cannot invalidate newer credentials.
|
|
18
|
+
await client.cards.create({ content: "A note from my script" });
|
|
19
|
+
```
|
|
28
20
|
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
The guide, with token providers and WorkOS Connect sign-in, is at
|
|
22
|
+
[teakvault.com/docs/sdk](https://teakvault.com/docs/sdk). Each release shares
|
|
23
|
+
the lockstep version of the other Teak products.
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ../convex/shared/utils/formatBytes.ts
|
|
2
2
|
var BYTE_UNITS = ["B", "KB", "MB", "GB"];
|
|
3
|
-
var formatBytes = (bytes) => {
|
|
3
|
+
var formatBytes = (bytes, locale) => {
|
|
4
4
|
if (!(bytes && Number.isFinite(bytes) && bytes > 0)) {
|
|
5
5
|
return "0 B";
|
|
6
6
|
}
|
|
@@ -11,7 +11,12 @@ var formatBytes = (bytes) => {
|
|
|
11
11
|
unit += 1;
|
|
12
12
|
}
|
|
13
13
|
const digits = unit > 0 && value < 10 ? 1 : 0;
|
|
14
|
-
|
|
14
|
+
const number = locale ? new Intl.NumberFormat(locale, {
|
|
15
|
+
maximumFractionDigits: digits,
|
|
16
|
+
minimumFractionDigits: digits,
|
|
17
|
+
useGrouping: false
|
|
18
|
+
}).format(value) : value.toFixed(digits);
|
|
19
|
+
return `${number} ${BYTE_UNITS[unit]}`;
|
|
15
20
|
};
|
|
16
21
|
// ../convex/shared/boundedResponse.ts
|
|
17
22
|
class ResponseSizeLimitError extends Error {
|
|
@@ -69,7 +74,8 @@ var validateOAuthUrl = (raw, local = false) => {
|
|
|
69
74
|
throw new Error("Missing OAuth discovery URL");
|
|
70
75
|
}
|
|
71
76
|
const url = new URL(raw);
|
|
72
|
-
|
|
77
|
+
const hostname = url.hostname.replace(/\.+$/, "");
|
|
78
|
+
if (url.username || url.password || url.hash || url.protocol !== "https:" && !(local && url.protocol === "http:" && isLoopbackHostname(hostname)) || !local && isLoopbackHostname(hostname) || privateHost(hostname) && !(local && isLoopbackHostname(hostname))) {
|
|
73
79
|
throw new Error("Unsafe OAuth discovery URL");
|
|
74
80
|
}
|
|
75
81
|
return url;
|
|
@@ -329,7 +335,6 @@ var fetchConnectOwnerId = async (apiUrl, accessToken, options = {}) => {
|
|
|
329
335
|
};
|
|
330
336
|
// ../convex/shared/markdown.ts
|
|
331
337
|
var MARKDOWN_CONTENT_MAX_BYTES = 512 * 1024;
|
|
332
|
-
|
|
333
338
|
// ../files-protocol/src/decisions.ts
|
|
334
339
|
var CLEF_MODEL_ID = "@cf/cloudflare/clef";
|
|
335
340
|
var CLEF_FLASH_MODEL_ID = "@cf/cloudflare/clef-flash";
|
|
@@ -360,11 +365,49 @@ var FILES_OBJECT_KEY_ROOTS = ["", FILES_DEV_KEY_PREFIX].flatMap((envPrefix) => [
|
|
|
360
365
|
// ../files-protocol/src/inspect.ts
|
|
361
366
|
var FILES_INSPECT_MAX_ARCHIVE_BYTES = 25 * 1024 * 1024;
|
|
362
367
|
var FILES_INSPECT_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
368
|
+
// ../files-protocol/src/metadata.ts
|
|
369
|
+
var SHARED_LANGUAGE_RULES = `- In Japanese, Chinese and Korean, each tag is one term with no spaces, such as "デザイン", "设计" or "디자인".
|
|
370
|
+
- Never use "secret" or "hidden", or a translation of either, as a tag.`;
|
|
371
|
+
var CONTENT_LANGUAGE_RULES = `- Write the tags and the summary in the same language as the content. If the content mixes languages, use the main one. If you can't tell the language, use English.
|
|
372
|
+
${SHARED_LANGUAGE_RULES}
|
|
373
|
+
- Language is the content's main language as a lowercase ISO 639-1 code, such as "en", "ja" or "es".`;
|
|
374
|
+
var IMAGE_LANGUAGE_RULES = `- Write the tags and the summary in the language of the text in the image. If the text mixes languages, use the main one. If the image has no legible text, use English.
|
|
375
|
+
${SHARED_LANGUAGE_RULES}
|
|
376
|
+
- Language is the main language of the text in the image as a lowercase ISO 639-1 code, such as "en", "ja" or "es", and "en" when it has no legible text.`;
|
|
377
|
+
var TEXT_ANALYSIS_SYSTEM_PROMPT = `You are an expert content analyzer. Generate relevant tags and a concise summary for the given content. /no_think
|
|
378
|
+
|
|
379
|
+
Guidelines:
|
|
380
|
+
- Tags should be 5-6 specific, relevant single words only (no spaces, no hyphens)
|
|
381
|
+
- Summary should be 1-2 sentences that capture the essence
|
|
382
|
+
- Focus on the main topics, themes, and key information
|
|
383
|
+
- Use clear, searchable language
|
|
384
|
+
${CONTENT_LANGUAGE_RULES}
|
|
385
|
+
|
|
386
|
+
Respond with a single JSON object using exactly this shape and no other keys:
|
|
387
|
+
{"tags": ["word", "word"], "summary": "...", "language": "xx"}`;
|
|
388
|
+
var LINK_ANALYSIS_SYSTEM_PROMPT = `You are an expert web content analyzer. Generate relevant tags and a concise summary for the given web page content. /no_think
|
|
389
|
+
|
|
390
|
+
Guidelines:
|
|
391
|
+
- Tags should be 5-6 single words capturing main topics, categories, and key concepts (no spaces, no hyphens)
|
|
392
|
+
- Include relevant technology, industry, or topic tags where applicable
|
|
393
|
+
- Summary should be 1-2 sentences capturing the essence and value of the content
|
|
394
|
+
- Focus on what makes this link useful and searchable
|
|
395
|
+
- Use clear, specific language that helps with discovery
|
|
396
|
+
- Consider the source, author, and context when available
|
|
397
|
+
${CONTENT_LANGUAGE_RULES}
|
|
398
|
+
|
|
399
|
+
Respond with a single JSON object using exactly this shape and no other keys:
|
|
400
|
+
{"tags": ["word", "word"], "summary": "...", "language": "xx"}`;
|
|
401
|
+
var ISO_639_1 = new Set(("aa ab ae af ak am an ar as av ay az ba be bg bi bm bn bo br bs ca ce ch " + "co cr cs cu cv cy da de dv dz ee el en eo es et eu fa ff fi fj fo fr fy " + "ga gd gl gn gu gv ha he hi ho hr ht hu hy hz ia id ie ig ii ik io is it " + "iu ja jv ka kg ki kj kk kl km kn ko kr ks ku kv kw ky la lb lg li ln lo " + "lt lu lv mg mh mi mk ml mn mr ms mt my na nb nd ne ng nl nn no nr nv ny " + "oc oj om or os pa pi pl ps pt qu rm rn ro ru rw sa sc sd se sg si sk sl " + "sm sn so sq sr ss st su sv sw ta te tg th ti tk tl tn to tr ts tt tw ty " + "ug uk ur uz ve vi vo wa wo xh yi yo za zh zu").split(" "));
|
|
363
402
|
// ../files-protocol/src/remoteAsset.ts
|
|
364
403
|
var FILES_REMOTE_ASSET_MAX_BYTES = {
|
|
365
404
|
image: 20 * 1024 * 1024,
|
|
366
405
|
video: 50 * 1024 * 1024
|
|
367
406
|
};
|
|
407
|
+
var GENERIC_CONTENT_TYPES = new Set([
|
|
408
|
+
"application/octet-stream",
|
|
409
|
+
"binary/octet-stream"
|
|
410
|
+
]);
|
|
368
411
|
// ../files-protocol/src/workflows.ts
|
|
369
412
|
var WORKFLOW_RPC_MAX_TTL_SECONDS = 5 * 60;
|
|
370
413
|
var WORKFLOW_RPC_MAX_BODY_BYTES = 4 * 1024 * 1024;
|
|
@@ -850,6 +893,11 @@ var TAG_ERROR_MESSAGES = {
|
|
|
850
893
|
};
|
|
851
894
|
var HIDDEN_TAG_KEYS = ["secret", "hidden"];
|
|
852
895
|
var hiddenTagKeys = new Set(HIDDEN_TAG_KEYS);
|
|
896
|
+
var MAX_CARD_TAGS = 100;
|
|
897
|
+
var CARD_TAG_ERROR_MESSAGES = {
|
|
898
|
+
TAG_TOO_LONG: `Each tag must be at most ${MAX_TAG_NAME_LENGTH} characters`,
|
|
899
|
+
TOO_MANY_TAGS: `A card can have at most ${MAX_CARD_TAGS} tags`
|
|
900
|
+
};
|
|
853
901
|
// ../convex/shared/constants.ts
|
|
854
902
|
var cardTypes = [
|
|
855
903
|
"text",
|
|
@@ -929,6 +977,7 @@ var ERROR_CODES = [
|
|
|
929
977
|
"FILE_TOO_LARGE",
|
|
930
978
|
"FOLDER_LIMIT",
|
|
931
979
|
"FORBIDDEN",
|
|
980
|
+
"HIGHLIGHT_LIMIT",
|
|
932
981
|
"IN_PROGRESS",
|
|
933
982
|
"INTERNAL_ERROR",
|
|
934
983
|
"INVALID_API_KEY",
|
|
@@ -939,9 +988,12 @@ var ERROR_CODES = [
|
|
|
939
988
|
"METHOD_NOT_ALLOWED",
|
|
940
989
|
"NETWORK_ERROR",
|
|
941
990
|
"NOT_FOUND",
|
|
991
|
+
"NO_ARTICLE",
|
|
942
992
|
"NO_CONTENT",
|
|
943
993
|
"NOT_IN_TRASH",
|
|
994
|
+
"PAGE_SNAPSHOT_TOO_LARGE",
|
|
944
995
|
"PARSE_ERROR",
|
|
996
|
+
"QUOTE_NOT_FOUND",
|
|
945
997
|
"RATE_LIMITED",
|
|
946
998
|
"REQUEST_FAILED",
|
|
947
999
|
"SERVICE_UNAVAILABLE",
|
|
@@ -1058,7 +1110,7 @@ var buildCardsSearchParams = (input) => {
|
|
|
1058
1110
|
if (input.mode === "keyword" || input.mode === "semantic") {
|
|
1059
1111
|
search.set("mode", input.mode);
|
|
1060
1112
|
}
|
|
1061
|
-
if (input.sort
|
|
1113
|
+
if (input.sort && input.sort !== "newest") {
|
|
1062
1114
|
search.set("sort", input.sort);
|
|
1063
1115
|
}
|
|
1064
1116
|
if (input.sort === "shuffle" && typeof input.shuffleSeed === "number") {
|
|
@@ -1073,12 +1125,38 @@ var buildCardsSearchParams = (input) => {
|
|
|
1073
1125
|
search.set("limit", String(normalizeLimit(input.limit)));
|
|
1074
1126
|
return search.toString();
|
|
1075
1127
|
};
|
|
1076
|
-
var
|
|
1077
|
-
|
|
1078
|
-
|
|
1128
|
+
var readStream = async (reader) => {
|
|
1129
|
+
const decoder = new TextDecoder;
|
|
1130
|
+
let text = "";
|
|
1131
|
+
for (;; ) {
|
|
1132
|
+
const { done, value } = await reader.read();
|
|
1133
|
+
if (done) {
|
|
1134
|
+
return text + decoder.decode();
|
|
1135
|
+
}
|
|
1136
|
+
text += decoder.decode(value, { stream: true });
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
var readBody = (response, signal) => new Promise((resolve, reject) => {
|
|
1140
|
+
const reader = response.body?.getReader();
|
|
1141
|
+
const onAbort = () => {
|
|
1142
|
+
reject(new TeakApiError("TIMEOUT"));
|
|
1143
|
+
reader?.cancel().catch(() => {
|
|
1144
|
+
return;
|
|
1145
|
+
});
|
|
1146
|
+
};
|
|
1147
|
+
if (signal.aborted) {
|
|
1148
|
+
onAbort();
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (!reader) {
|
|
1152
|
+
resolve("");
|
|
1153
|
+
return;
|
|
1079
1154
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1155
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1156
|
+
readStream(reader).then(resolve, () => reject(new TeakApiError(signal.aborted ? "TIMEOUT" : "NETWORK_ERROR"))).finally(() => signal.removeEventListener("abort", onAbort));
|
|
1157
|
+
});
|
|
1158
|
+
var parseJson = (response, text) => {
|
|
1159
|
+
if (response.status === 204 || !text.trim()) {
|
|
1082
1160
|
return null;
|
|
1083
1161
|
}
|
|
1084
1162
|
try {
|
|
@@ -1113,6 +1191,18 @@ var asFolders = (value) => {
|
|
|
1113
1191
|
}
|
|
1114
1192
|
return { items: value.items.map(asFolder) };
|
|
1115
1193
|
};
|
|
1194
|
+
var asHighlight = (value) => {
|
|
1195
|
+
if (!isObject(value) || typeof value.id !== "string" || typeof value.quote !== "string") {
|
|
1196
|
+
throw new TeakApiError("PARSE_ERROR", "Teak returned an unexpected highlight payload.");
|
|
1197
|
+
}
|
|
1198
|
+
return value;
|
|
1199
|
+
};
|
|
1200
|
+
var asHighlights = (value) => {
|
|
1201
|
+
if (!(isObject(value) && Array.isArray(value.items))) {
|
|
1202
|
+
throw new TeakApiError("PARSE_ERROR");
|
|
1203
|
+
}
|
|
1204
|
+
return { items: value.items.map(asHighlight) };
|
|
1205
|
+
};
|
|
1116
1206
|
var asFolderCards = (value) => {
|
|
1117
1207
|
if (!(isObject(value) && isStringArray(value.updatedIds) && isStringArray(value.failedIds))) {
|
|
1118
1208
|
throw new TeakApiError("PARSE_ERROR");
|
|
@@ -1160,7 +1250,7 @@ var asSavedPdf = (value) => {
|
|
|
1160
1250
|
return { cardId: value.cardId, created: value.created };
|
|
1161
1251
|
};
|
|
1162
1252
|
var asCardContent = (value) => {
|
|
1163
|
-
if (!isObject(value) || typeof value.cardId !== "string" || typeof value.text !== "string" || typeof value.charCount !== "number" || typeof value.truncated !== "boolean" ||
|
|
1253
|
+
if (!isObject(value) || typeof value.cardId !== "string" || typeof value.text !== "string" || typeof value.charCount !== "number" || typeof value.truncated !== "boolean" || typeof value.kind !== "string") {
|
|
1164
1254
|
throw new TeakApiError("PARSE_ERROR");
|
|
1165
1255
|
}
|
|
1166
1256
|
return value;
|
|
@@ -1208,14 +1298,18 @@ var createTeakClient = (options) => {
|
|
|
1208
1298
|
headers.set("Content-Type", "application/json");
|
|
1209
1299
|
}
|
|
1210
1300
|
let response;
|
|
1301
|
+
let text;
|
|
1211
1302
|
try {
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1303
|
+
try {
|
|
1304
|
+
response = await fetchImpl(`${baseUrl}${path}`, {
|
|
1305
|
+
...init,
|
|
1306
|
+
headers,
|
|
1307
|
+
signal: controller.signal
|
|
1308
|
+
});
|
|
1309
|
+
} catch (error) {
|
|
1310
|
+
throw new TeakApiError(error instanceof DOMException && error.name === "AbortError" ? "TIMEOUT" : "NETWORK_ERROR");
|
|
1311
|
+
}
|
|
1312
|
+
text = response.status === 204 ? "" : await readBody(response, controller.signal);
|
|
1219
1313
|
} finally {
|
|
1220
1314
|
clearTimeout(timeout);
|
|
1221
1315
|
}
|
|
@@ -1225,7 +1319,7 @@ var createTeakClient = (options) => {
|
|
|
1225
1319
|
return request(path, init, parser, false, next);
|
|
1226
1320
|
}
|
|
1227
1321
|
}
|
|
1228
|
-
const payload =
|
|
1322
|
+
const payload = parseJson(response, text);
|
|
1229
1323
|
if (!response.ok) {
|
|
1230
1324
|
const object = isObject(payload) ? payload : {};
|
|
1231
1325
|
throw new TeakApiError(codeFrom(object.code, response.status === 404 ? "NOT_FOUND" : "REQUEST_FAILED"), String(object.error || response.statusText), {
|
|
@@ -1240,6 +1334,7 @@ var createTeakClient = (options) => {
|
|
|
1240
1334
|
const idempotency = (requestOptions) => requestOptions?.idempotencyKey ? { "Idempotency-Key": requestOptions.idempotencyKey } : {};
|
|
1241
1335
|
const cardPath = (id) => `/v1/cards/${encodeURIComponent(id)}`;
|
|
1242
1336
|
const folderPath = (id) => `/v1/folders/${encodeURIComponent(id)}`;
|
|
1337
|
+
const highlightPath = (id) => `/v1/highlights/${encodeURIComponent(id)}`;
|
|
1243
1338
|
const putFile = async (uploadUrl, bytes, mimeType) => {
|
|
1244
1339
|
const headers = new Headers({ "Content-Type": mimeType });
|
|
1245
1340
|
const byteLength = byteLengthForBody(bytes);
|
|
@@ -1275,7 +1370,7 @@ var createTeakClient = (options) => {
|
|
|
1275
1370
|
headers: idempotency(requestOptions),
|
|
1276
1371
|
method: "POST"
|
|
1277
1372
|
}, asCreate),
|
|
1278
|
-
content: (id) => request(`${cardPath(id)}/content`, { method: "GET" }, asCardContent),
|
|
1373
|
+
content: (id, contentOptions) => request(`${cardPath(id)}/content${contentOptions?.kind ? `?kind=${encodeURIComponent(contentOptions.kind)}` : ""}`, { method: "GET" }, asCardContent),
|
|
1279
1374
|
delete: (id, deleteOptions) => request(`${cardPath(id)}${deleteOptions?.permanent ? "?permanent=true" : ""}`, { method: "DELETE" }, () => null),
|
|
1280
1375
|
duplicate: (url) => request(`/v1/cards/duplicate?${new URLSearchParams({ url }).toString()}`, { method: "GET" }, (v) => isObject(v) && typeof v.cardId === "string" ? v.cardId : null),
|
|
1281
1376
|
favorites: (input = {}) => request(`/v1/cards?${qs({ include: "content,metadata", ...input, favorited: true })}`, { method: "GET" }, asPage),
|
|
@@ -1328,6 +1423,12 @@ var createTeakClient = (options) => {
|
|
|
1328
1423
|
removeCards: (id, cardIds) => request(`${folderPath(id)}/cards`, { body: JSON.stringify({ cardIds }), method: "DELETE" }, asFolderCards),
|
|
1329
1424
|
update: (id, input) => request(folderPath(id), { body: JSON.stringify(input), method: "PATCH" }, asFolder)
|
|
1330
1425
|
},
|
|
1426
|
+
highlights: {
|
|
1427
|
+
create: (cardId, input) => request(`${cardPath(cardId)}/highlights`, { body: JSON.stringify(input), method: "POST" }, asHighlight),
|
|
1428
|
+
delete: (id) => request(highlightPath(id), { method: "DELETE" }, () => null),
|
|
1429
|
+
list: (cardId) => request(`${cardPath(cardId)}/highlights`, { method: "GET" }, asHighlights),
|
|
1430
|
+
update: (id, input) => request(highlightPath(id), { body: JSON.stringify(input), method: "PATCH" }, asHighlight)
|
|
1431
|
+
},
|
|
1331
1432
|
me: () => request("/v1/me", { method: "GET" }, asMe),
|
|
1332
1433
|
tags: {
|
|
1333
1434
|
delete: (tag) => request("/v1/tags/delete", { body: JSON.stringify({ tag }), method: "POST" }, asTagOperation),
|
|
@@ -1336,6 +1437,9 @@ var createTeakClient = (options) => {
|
|
|
1336
1437
|
operation: (id) => request(`/v1/tags/operations/${encodeURIComponent(id)}`, { method: "GET" }, asTagOperation),
|
|
1337
1438
|
rename: (from, to) => request("/v1/tags/rename", { body: JSON.stringify({ from, to }), method: "POST" }, asTagOperation)
|
|
1338
1439
|
},
|
|
1440
|
+
pageSnapshots: {
|
|
1441
|
+
createUpload: (input) => request("/v1/page-snapshots/upload", { body: JSON.stringify(input), method: "POST" }, asUpload)
|
|
1442
|
+
},
|
|
1339
1443
|
uploads: {
|
|
1340
1444
|
create: (input) => request("/v1/uploads", { body: JSON.stringify(input), method: "POST" }, asUpload),
|
|
1341
1445
|
putFile
|
package/package.json
CHANGED
|
@@ -20,6 +20,12 @@ export declare const COLOR_HUE_ALIAS_MAP: Record<string, ColorHueBucket>;
|
|
|
20
20
|
export declare function normalizeColorHueBucket(value: string): ColorHueBucket | null;
|
|
21
21
|
export declare function normalizeHueFilters(values?: string[]): ColorHueBucket[];
|
|
22
22
|
export declare const FREE_TIER_LIMIT = 200;
|
|
23
|
+
/** The most links one paste into the web composer saves (card/pasteLinks.ts). */
|
|
24
|
+
export declare const LINK_PASTE_LIMIT = 100;
|
|
25
|
+
/** The most lines one paste may send; the composer sends no more. */
|
|
26
|
+
export declare const LINK_PASTE_MAX_INPUT = 1000;
|
|
27
|
+
/** Refused while the account's last pasted links are still being saved. */
|
|
28
|
+
export declare const LINK_PASTE_ACTIVE_CODE = "PASTE_ACTIVE";
|
|
23
29
|
export declare const TRASH_RETENTION_MS: number;
|
|
24
30
|
export { MAX_FILE_SIZE } from "./fileFormats.js";
|
|
25
31
|
export declare const MAX_FILES_PER_UPLOAD = 20;
|
|
@@ -19,6 +19,19 @@ export declare const isCardHidden: (card: {
|
|
|
19
19
|
tags?: readonly string[];
|
|
20
20
|
}) => boolean;
|
|
21
21
|
export declare const explainInvalidTagName: (name: string) => string | null;
|
|
22
|
+
export declare const MAX_CARD_TAGS = 100;
|
|
23
|
+
export declare const CARD_TAG_ERROR_MESSAGES: {
|
|
24
|
+
readonly TAG_TOO_LONG: "Each tag must be at most 64 characters";
|
|
25
|
+
readonly TOO_MANY_TAGS: "A card can have at most 100 tags";
|
|
26
|
+
};
|
|
27
|
+
/** Trims each tag, drops empty ones and keeps the first of each case variant. */
|
|
28
|
+
export declare const normalizeTagList: (tags: readonly string[]) => string[];
|
|
29
|
+
/**
|
|
30
|
+
* Why a card can't hold these normalized tags, or null. Tags the card already
|
|
31
|
+
* has (saved before the limits existed) may stay, and a card already past the
|
|
32
|
+
* count may keep or drop tags, never add.
|
|
33
|
+
*/
|
|
34
|
+
export declare const explainInvalidCardTags: (tags: readonly string[], previousTags?: readonly string[]) => string | null;
|
|
22
35
|
export declare const MAX_TAG_KEY_LENGTH = 256;
|
|
23
36
|
export declare const explainInvalidTagSource: (name: string) => string | null;
|
|
24
37
|
export declare const tagExistsMessage: (name: string) => string;
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* A byte size such as "2.1 MB". Pass a locale to write the number in that
|
|
3
|
+
* language ("2,1 MB" in German); without one it keeps the "." of before.
|
|
4
|
+
*/
|
|
5
|
+
export declare const formatBytes: (bytes?: number | null, locale?: string) => string;
|
package/types/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { FOLDER_COLORS } from "./convex/shared/folders.js";
|
|
|
8
8
|
export type { CardType } from "./convex/shared/constants.js";
|
|
9
9
|
export declare const CARD_TYPES: readonly ["text", "link", "image", "video", "audio", "document", "palette", "quote"];
|
|
10
10
|
export declare const CARD_SORTS: readonly ["newest", "oldest", "relevance", "shuffle"];
|
|
11
|
-
export declare const ERROR_CODES: readonly ["AUTH_REQUIRED", "BAD_REQUEST", "CARD_LIMIT_REACHED", "CONFIG_ERROR", "CONFLICT", "CONTENT_TOO_LARGE", "FILE_TOO_LARGE", "FOLDER_LIMIT", "FORBIDDEN", "IN_PROGRESS", "INTERNAL_ERROR", "INVALID_API_KEY", "INVALID_INPUT", "INVALID_STORAGE_KEY", "INVALID_UTF8", "LIMIT_REACHED", "METHOD_NOT_ALLOWED", "NETWORK_ERROR", "NOT_FOUND", "NO_CONTENT", "NOT_IN_TRASH", "PARSE_ERROR", "RATE_LIMITED", "REQUEST_FAILED", "SERVICE_UNAVAILABLE", "SMART_FOLDER", "TAG_EXISTS", "TAG_OPERATION_RUNNING", "TIMEOUT", "TYPE_MISMATCH", "UNAUTHORIZED", "UNSUPPORTED_TYPE"];
|
|
11
|
+
export declare const ERROR_CODES: readonly ["AUTH_REQUIRED", "BAD_REQUEST", "CARD_LIMIT_REACHED", "CONFIG_ERROR", "CONFLICT", "CONTENT_TOO_LARGE", "FILE_TOO_LARGE", "FOLDER_LIMIT", "FORBIDDEN", "HIGHLIGHT_LIMIT", "IN_PROGRESS", "INTERNAL_ERROR", "INVALID_API_KEY", "INVALID_INPUT", "INVALID_STORAGE_KEY", "INVALID_UTF8", "LIMIT_REACHED", "METHOD_NOT_ALLOWED", "NETWORK_ERROR", "NOT_FOUND", "NO_ARTICLE", "NO_CONTENT", "NOT_IN_TRASH", "PAGE_SNAPSHOT_TOO_LARGE", "PARSE_ERROR", "QUOTE_NOT_FOUND", "RATE_LIMITED", "REQUEST_FAILED", "SERVICE_UNAVAILABLE", "SMART_FOLDER", "TAG_EXISTS", "TAG_OPERATION_RUNNING", "TIMEOUT", "TYPE_MISMATCH", "UNAUTHORIZED", "UNSUPPORTED_TYPE"];
|
|
12
12
|
export type CardSort = (typeof CARD_SORTS)[number];
|
|
13
13
|
/**
|
|
14
14
|
* How a search finds cards: `hybrid` (the API's default) matches words and
|
|
@@ -21,13 +21,32 @@ export interface TokenProvider {
|
|
|
21
21
|
getAccessToken: () => Promise<string | null> | string | null;
|
|
22
22
|
onUnauthorized?: (rejectedToken: string) => Promise<string | null> | string | null;
|
|
23
23
|
}
|
|
24
|
+
/** A link's saved copy of its page. The HTML itself stays in Teak. */
|
|
25
|
+
export interface CardArchive {
|
|
26
|
+
bytes: number;
|
|
27
|
+
capturedAt: number;
|
|
28
|
+
kind: "full" | "text_only";
|
|
29
|
+
}
|
|
30
|
+
/** A link's article, from its saved copy; read it with `cards.content(id, { kind: "article" })`. */
|
|
31
|
+
export interface CardArticle {
|
|
32
|
+
readingMinutes: number;
|
|
33
|
+
wordCount: number;
|
|
34
|
+
}
|
|
24
35
|
export interface Card {
|
|
25
36
|
aiSummary: string | null;
|
|
26
37
|
aiTags: string[];
|
|
27
38
|
aiTranscript: string | null;
|
|
28
39
|
appUrl: string;
|
|
40
|
+
/**
|
|
41
|
+
* A link's saved copy of its page: `full`, or `text_only` when the page
|
|
42
|
+
* was too large; null until it is saved and for other cards. Servers that
|
|
43
|
+
* predate saved copies leave it out.
|
|
44
|
+
*/
|
|
45
|
+
archive?: CardArchive | null;
|
|
29
46
|
/** When the card was archived, or null. */
|
|
30
47
|
archivedAt: number | null;
|
|
48
|
+
/** A link's article; null when it has none or it isn't ready. */
|
|
49
|
+
article?: CardArticle | null;
|
|
31
50
|
/** Bar heights 0-100: 45 for a grid card, 200 for a player. */
|
|
32
51
|
audioWaveform: {
|
|
33
52
|
detail: number[];
|
|
@@ -58,6 +77,11 @@ export interface Card {
|
|
|
58
77
|
fileUrl: string | null;
|
|
59
78
|
/** Ids of the folders the card is in. */
|
|
60
79
|
folderIds: string[];
|
|
80
|
+
/**
|
|
81
|
+
* Passages highlighted in a link's article; 0 for other cards. Servers
|
|
82
|
+
* that predate highlights leave it out.
|
|
83
|
+
*/
|
|
84
|
+
highlightCount?: number;
|
|
61
85
|
id: string;
|
|
62
86
|
/** Archived cards are hidden from the library but still count. */
|
|
63
87
|
isArchived: boolean;
|
|
@@ -115,6 +139,11 @@ export interface Card {
|
|
|
115
139
|
notes: string | null;
|
|
116
140
|
placeholderUrl: string | null;
|
|
117
141
|
processingStatus: Record<string, unknown> | null;
|
|
142
|
+
/**
|
|
143
|
+
* When the card was last saved: `createdAt`, or later when the same link
|
|
144
|
+
* was saved again. Lists order by it. Servers that predate it leave it out.
|
|
145
|
+
*/
|
|
146
|
+
savedAt?: number;
|
|
118
147
|
screenshotUrl: string | null;
|
|
119
148
|
/**
|
|
120
149
|
* The title of the page in `sourceUrl`. Servers that predate sources
|
|
@@ -220,6 +249,28 @@ export interface TagOperation {
|
|
|
220
249
|
total: number;
|
|
221
250
|
updatedAt: number;
|
|
222
251
|
}
|
|
252
|
+
/** A passage highlighted in a link's article, with an optional note. */
|
|
253
|
+
export interface Highlight {
|
|
254
|
+
/** False until the quote is found in the card's saved article. */
|
|
255
|
+
anchored: boolean;
|
|
256
|
+
cardId: string;
|
|
257
|
+
createdAt: number;
|
|
258
|
+
id: string;
|
|
259
|
+
note: string | null;
|
|
260
|
+
quote: string;
|
|
261
|
+
/** `reader`, `api` or `import`; accept values this SDK doesn't know. */
|
|
262
|
+
source: string;
|
|
263
|
+
updatedAt: number;
|
|
264
|
+
}
|
|
265
|
+
export interface CreateHighlightInput {
|
|
266
|
+
note?: string;
|
|
267
|
+
/** Text just before the quote, to pick one of its repeats. */
|
|
268
|
+
prefix?: string;
|
|
269
|
+
/** Text from the article; whitespace, curly quotes and case needn't match. */
|
|
270
|
+
quote: string;
|
|
271
|
+
/** Text just after the quote, to pick one of its repeats. */
|
|
272
|
+
suffix?: string;
|
|
273
|
+
}
|
|
223
274
|
export interface FolderCardsResult {
|
|
224
275
|
failedIds: string[];
|
|
225
276
|
/** Failed cards that are already in the most folders a card can be in. */
|
|
@@ -251,6 +302,12 @@ export interface CreateCardInput {
|
|
|
251
302
|
mimeType?: string;
|
|
252
303
|
/** Free-form notes attached to the card. */
|
|
253
304
|
notes?: string | null;
|
|
305
|
+
/**
|
|
306
|
+
* The `fileKey` from `pageSnapshots.createUpload`: the page as the browser
|
|
307
|
+
* showed it, which becomes a link's saved copy. Ignored when it isn't one
|
|
308
|
+
* of your snapshot uploads.
|
|
309
|
+
*/
|
|
310
|
+
pageSnapshotKey?: string;
|
|
254
311
|
/**
|
|
255
312
|
* A free label for what saved the card (extension, CLI, API, and so on),
|
|
256
313
|
* kept in its metadata. For the page the card came from, use `sourceUrl`.
|
|
@@ -290,6 +347,12 @@ export interface Me {
|
|
|
290
347
|
cardLimit: number | null;
|
|
291
348
|
email: string;
|
|
292
349
|
id: string;
|
|
350
|
+
/**
|
|
351
|
+
* The account's Teak language, chosen in Settings: a BCP 47 code such as
|
|
352
|
+
* `en`, `de` or `pt-BR`. More may be added, so treat an unknown code as
|
|
353
|
+
* English.
|
|
354
|
+
*/
|
|
355
|
+
locale: string;
|
|
293
356
|
name?: string;
|
|
294
357
|
plan: "free" | "pro";
|
|
295
358
|
settingsUrl: string;
|
|
@@ -325,6 +388,10 @@ export interface CreateUploadInput {
|
|
|
325
388
|
mimeType: string;
|
|
326
389
|
}
|
|
327
390
|
/** A link image upload: JPEG, PNG, WebP, GIF or AVIF, under 20 MB. */
|
|
391
|
+
export interface CreatePageSnapshotUploadInput {
|
|
392
|
+
/** The gzipped page's exact size; at most 20 MiB. */
|
|
393
|
+
bytes: number;
|
|
394
|
+
}
|
|
328
395
|
export interface CreateThumbnailUploadInput {
|
|
329
396
|
fileSize: number;
|
|
330
397
|
mimeType: string;
|
|
@@ -340,7 +407,16 @@ export interface CreateCardResponse {
|
|
|
340
407
|
appUrl: string;
|
|
341
408
|
card?: Card;
|
|
342
409
|
cardId: string;
|
|
410
|
+
/**
|
|
411
|
+
* True when the link was already in Teak: no card was made, and that card
|
|
412
|
+
* (`cardId`) moved to the top. Tags and notes sent with the save were not
|
|
413
|
+
* applied; update the card to add them.
|
|
414
|
+
*/
|
|
415
|
+
resaved?: true;
|
|
416
|
+
/** `status` stays `created` for a re-save too. */
|
|
343
417
|
status: "created";
|
|
418
|
+
/** True when the re-saved card was archived; it is back in the library. */
|
|
419
|
+
wasArchived?: true;
|
|
344
420
|
}
|
|
345
421
|
export interface RefreshCardResponse {
|
|
346
422
|
cardId: string;
|
|
@@ -354,6 +430,8 @@ export interface SavedPdf {
|
|
|
354
430
|
/** False when an earlier save already made that card. */
|
|
355
431
|
created: boolean;
|
|
356
432
|
}
|
|
433
|
+
/** The kinds of stored text a card can have. */
|
|
434
|
+
export type CardContentKind = "article" | "document" | "transcript" | "content";
|
|
357
435
|
/** A card's full stored text (GET /v1/cards/:id/content). */
|
|
358
436
|
export interface CardContent {
|
|
359
437
|
cardId: string;
|
|
@@ -361,9 +439,11 @@ export interface CardContent {
|
|
|
361
439
|
charCount: number;
|
|
362
440
|
/**
|
|
363
441
|
* document: extracted from the card's file; transcript: its speech;
|
|
364
|
-
* content: a text or quote card's written text
|
|
442
|
+
* content: a text or quote card's written text; article: a link's article
|
|
443
|
+
* as Markdown, with image links valid for 7 days. A kind this SDK doesn't
|
|
444
|
+
* know yet comes through as a string rather than an error.
|
|
365
445
|
*/
|
|
366
|
-
kind:
|
|
446
|
+
kind: CardContentKind | (string & {});
|
|
367
447
|
text: string;
|
|
368
448
|
/** The document is longer than Teak keeps. */
|
|
369
449
|
truncated: boolean;
|
|
@@ -414,7 +494,9 @@ export declare const buildCardsSearchParams: (input: {
|
|
|
414
494
|
* `is:fav`, `is:archived`, `site:github.com`, `folder:"Reading list"`,
|
|
415
495
|
* `price:<100` (also `>`, `<=`, `>=` and `=`), `text:"total due"` (only
|
|
416
496
|
* the words Teak read in images, video thumbnails, link previews and scanned
|
|
417
|
-
* PDFs) and `"quoted phrases"`.
|
|
497
|
+
* PDFs) and `"quoted phrases"`. A `-` leaves cards out (`design -figma`,
|
|
498
|
+
* `-tag:old`), uppercase `OR` matches either side and parentheses group:
|
|
499
|
+
* `(site:github.com OR site:gitlab.com) -is:archived`.
|
|
418
500
|
*/
|
|
419
501
|
query?: string;
|
|
420
502
|
/** Required with `sort: "shuffle"`: the same seed gives the same order. */
|
|
@@ -444,9 +526,13 @@ export declare const createTeakClient: (options: {
|
|
|
444
526
|
create: (input: CreateCardInput, requestOptions?: RequestOptions) => Promise<CreateCardResponse>;
|
|
445
527
|
/**
|
|
446
528
|
* The card's full stored text: document text, transcript, or a text
|
|
447
|
-
* card's content.
|
|
529
|
+
* card's content. `{ kind: "article" }` reads a link's article as
|
|
530
|
+
* Markdown; `kind` picks one kind when a card has several. Throws
|
|
531
|
+
* NO_CONTENT when the card has none (of that kind).
|
|
448
532
|
*/
|
|
449
|
-
content: (id: string
|
|
533
|
+
content: (id: string, contentOptions?: {
|
|
534
|
+
kind?: CardContentKind;
|
|
535
|
+
}) => Promise<CardContent>;
|
|
450
536
|
/** Moves the card to Trash, or deletes it for good with `permanent`. */
|
|
451
537
|
delete: (id: string, deleteOptions?: {
|
|
452
538
|
permanent?: boolean;
|
|
@@ -539,6 +625,25 @@ export declare const createTeakClient: (options: {
|
|
|
539
625
|
query?: string;
|
|
540
626
|
}) => Promise<Folder>;
|
|
541
627
|
};
|
|
628
|
+
highlights: {
|
|
629
|
+
/**
|
|
630
|
+
* Highlights a passage of a link's saved article, found by its text:
|
|
631
|
+
* the first match unless `prefix` or `suffix` points at another. An
|
|
632
|
+
* overlapping highlight merges with it. 409 NO_ARTICLE without a saved
|
|
633
|
+
* article, 422 QUOTE_NOT_FOUND, 409 HIGHLIGHT_LIMIT past 200.
|
|
634
|
+
*/
|
|
635
|
+
create: (cardId: string, input: CreateHighlightInput) => Promise<Highlight>;
|
|
636
|
+
/** Removes a highlight and its note. */
|
|
637
|
+
delete: (id: string) => Promise<null>;
|
|
638
|
+
/** A card's highlights, in the order they appear in its article. */
|
|
639
|
+
list: (cardId: string) => Promise<{
|
|
640
|
+
items: Highlight[];
|
|
641
|
+
}>;
|
|
642
|
+
/** Writes or changes a highlight's note; `note: null` clears it. */
|
|
643
|
+
update: (id: string, input: {
|
|
644
|
+
note: string | null;
|
|
645
|
+
}) => Promise<Highlight>;
|
|
646
|
+
};
|
|
542
647
|
me: () => Promise<Me>;
|
|
543
648
|
tags: {
|
|
544
649
|
/**
|
|
@@ -556,6 +661,14 @@ export declare const createTeakClient: (options: {
|
|
|
556
661
|
/** Renames a tag on every card; it finishes in the background. */
|
|
557
662
|
rename: (from: string, to: string) => Promise<TagOperation>;
|
|
558
663
|
};
|
|
664
|
+
pageSnapshots: {
|
|
665
|
+
/**
|
|
666
|
+
* A signed URL for the page a browser extension saw, gzipped: `PUT`
|
|
667
|
+
* it with `Content-Type: application/gzip`, then pass the `fileKey`
|
|
668
|
+
* to `cards.create` as `pageSnapshotKey`.
|
|
669
|
+
*/
|
|
670
|
+
createUpload: (input: CreatePageSnapshotUploadInput) => Promise<CreateUploadResponse>;
|
|
671
|
+
};
|
|
559
672
|
uploads: {
|
|
560
673
|
create: (input: CreateUploadInput) => Promise<CreateUploadResponse>;
|
|
561
674
|
putFile: (uploadUrl: string, bytes: BodyInit, mimeType: string) => Promise<{
|