teak-sdk 1.0.88 → 1.0.89
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/index.js +96 -17
- package/package.json +1 -1
- package/types/convex/shared/tags.d.ts +13 -0
- package/types/convex/shared/utils/formatBytes.d.ts +5 -1
- package/types/index.d.ts +9 -1
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;
|
|
@@ -360,11 +366,49 @@ var FILES_OBJECT_KEY_ROOTS = ["", FILES_DEV_KEY_PREFIX].flatMap((envPrefix) => [
|
|
|
360
366
|
// ../files-protocol/src/inspect.ts
|
|
361
367
|
var FILES_INSPECT_MAX_ARCHIVE_BYTES = 25 * 1024 * 1024;
|
|
362
368
|
var FILES_INSPECT_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
|
|
369
|
+
// ../files-protocol/src/metadata.ts
|
|
370
|
+
var SHARED_LANGUAGE_RULES = `- In Japanese, Chinese and Korean, each tag is one term with no spaces, such as "デザイン", "设计" or "디자인".
|
|
371
|
+
- Never use "secret" or "hidden", or a translation of either, as a tag.`;
|
|
372
|
+
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.
|
|
373
|
+
${SHARED_LANGUAGE_RULES}
|
|
374
|
+
- Language is the content's main language as a lowercase ISO 639-1 code, such as "en", "ja" or "es".`;
|
|
375
|
+
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.
|
|
376
|
+
${SHARED_LANGUAGE_RULES}
|
|
377
|
+
- 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.`;
|
|
378
|
+
var TEXT_ANALYSIS_SYSTEM_PROMPT = `You are an expert content analyzer. Generate relevant tags and a concise summary for the given content. /no_think
|
|
379
|
+
|
|
380
|
+
Guidelines:
|
|
381
|
+
- Tags should be 5-6 specific, relevant single words only (no spaces, no hyphens)
|
|
382
|
+
- Summary should be 1-2 sentences that capture the essence
|
|
383
|
+
- Focus on the main topics, themes, and key information
|
|
384
|
+
- Use clear, searchable language
|
|
385
|
+
${CONTENT_LANGUAGE_RULES}
|
|
386
|
+
|
|
387
|
+
Respond with a single JSON object using exactly this shape and no other keys:
|
|
388
|
+
{"tags": ["word", "word"], "summary": "...", "language": "xx"}`;
|
|
389
|
+
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
|
|
390
|
+
|
|
391
|
+
Guidelines:
|
|
392
|
+
- Tags should be 5-6 single words capturing main topics, categories, and key concepts (no spaces, no hyphens)
|
|
393
|
+
- Include relevant technology, industry, or topic tags where applicable
|
|
394
|
+
- Summary should be 1-2 sentences capturing the essence and value of the content
|
|
395
|
+
- Focus on what makes this link useful and searchable
|
|
396
|
+
- Use clear, specific language that helps with discovery
|
|
397
|
+
- Consider the source, author, and context when available
|
|
398
|
+
${CONTENT_LANGUAGE_RULES}
|
|
399
|
+
|
|
400
|
+
Respond with a single JSON object using exactly this shape and no other keys:
|
|
401
|
+
{"tags": ["word", "word"], "summary": "...", "language": "xx"}`;
|
|
402
|
+
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
403
|
// ../files-protocol/src/remoteAsset.ts
|
|
364
404
|
var FILES_REMOTE_ASSET_MAX_BYTES = {
|
|
365
405
|
image: 20 * 1024 * 1024,
|
|
366
406
|
video: 50 * 1024 * 1024
|
|
367
407
|
};
|
|
408
|
+
var GENERIC_CONTENT_TYPES = new Set([
|
|
409
|
+
"application/octet-stream",
|
|
410
|
+
"binary/octet-stream"
|
|
411
|
+
]);
|
|
368
412
|
// ../files-protocol/src/workflows.ts
|
|
369
413
|
var WORKFLOW_RPC_MAX_TTL_SECONDS = 5 * 60;
|
|
370
414
|
var WORKFLOW_RPC_MAX_BODY_BYTES = 4 * 1024 * 1024;
|
|
@@ -850,6 +894,11 @@ var TAG_ERROR_MESSAGES = {
|
|
|
850
894
|
};
|
|
851
895
|
var HIDDEN_TAG_KEYS = ["secret", "hidden"];
|
|
852
896
|
var hiddenTagKeys = new Set(HIDDEN_TAG_KEYS);
|
|
897
|
+
var MAX_CARD_TAGS = 100;
|
|
898
|
+
var CARD_TAG_ERROR_MESSAGES = {
|
|
899
|
+
TAG_TOO_LONG: `Each tag must be at most ${MAX_TAG_NAME_LENGTH} characters`,
|
|
900
|
+
TOO_MANY_TAGS: `A card can have at most ${MAX_CARD_TAGS} tags`
|
|
901
|
+
};
|
|
853
902
|
// ../convex/shared/constants.ts
|
|
854
903
|
var cardTypes = [
|
|
855
904
|
"text",
|
|
@@ -1058,7 +1107,7 @@ var buildCardsSearchParams = (input) => {
|
|
|
1058
1107
|
if (input.mode === "keyword" || input.mode === "semantic") {
|
|
1059
1108
|
search.set("mode", input.mode);
|
|
1060
1109
|
}
|
|
1061
|
-
if (input.sort
|
|
1110
|
+
if (input.sort && input.sort !== "newest") {
|
|
1062
1111
|
search.set("sort", input.sort);
|
|
1063
1112
|
}
|
|
1064
1113
|
if (input.sort === "shuffle" && typeof input.shuffleSeed === "number") {
|
|
@@ -1073,12 +1122,38 @@ var buildCardsSearchParams = (input) => {
|
|
|
1073
1122
|
search.set("limit", String(normalizeLimit(input.limit)));
|
|
1074
1123
|
return search.toString();
|
|
1075
1124
|
};
|
|
1076
|
-
var
|
|
1077
|
-
|
|
1078
|
-
|
|
1125
|
+
var readStream = async (reader) => {
|
|
1126
|
+
const decoder = new TextDecoder;
|
|
1127
|
+
let text = "";
|
|
1128
|
+
for (;; ) {
|
|
1129
|
+
const { done, value } = await reader.read();
|
|
1130
|
+
if (done) {
|
|
1131
|
+
return text + decoder.decode();
|
|
1132
|
+
}
|
|
1133
|
+
text += decoder.decode(value, { stream: true });
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
var readBody = (response, signal) => new Promise((resolve, reject) => {
|
|
1137
|
+
const reader = response.body?.getReader();
|
|
1138
|
+
const onAbort = () => {
|
|
1139
|
+
reject(new TeakApiError("TIMEOUT"));
|
|
1140
|
+
reader?.cancel().catch(() => {
|
|
1141
|
+
return;
|
|
1142
|
+
});
|
|
1143
|
+
};
|
|
1144
|
+
if (signal.aborted) {
|
|
1145
|
+
onAbort();
|
|
1146
|
+
return;
|
|
1079
1147
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1148
|
+
if (!reader) {
|
|
1149
|
+
resolve("");
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1153
|
+
readStream(reader).then(resolve, () => reject(new TeakApiError(signal.aborted ? "TIMEOUT" : "NETWORK_ERROR"))).finally(() => signal.removeEventListener("abort", onAbort));
|
|
1154
|
+
});
|
|
1155
|
+
var parseJson = (response, text) => {
|
|
1156
|
+
if (response.status === 204 || !text.trim()) {
|
|
1082
1157
|
return null;
|
|
1083
1158
|
}
|
|
1084
1159
|
try {
|
|
@@ -1208,14 +1283,18 @@ var createTeakClient = (options) => {
|
|
|
1208
1283
|
headers.set("Content-Type", "application/json");
|
|
1209
1284
|
}
|
|
1210
1285
|
let response;
|
|
1286
|
+
let text;
|
|
1211
1287
|
try {
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1288
|
+
try {
|
|
1289
|
+
response = await fetchImpl(`${baseUrl}${path}`, {
|
|
1290
|
+
...init,
|
|
1291
|
+
headers,
|
|
1292
|
+
signal: controller.signal
|
|
1293
|
+
});
|
|
1294
|
+
} catch (error) {
|
|
1295
|
+
throw new TeakApiError(error instanceof DOMException && error.name === "AbortError" ? "TIMEOUT" : "NETWORK_ERROR");
|
|
1296
|
+
}
|
|
1297
|
+
text = response.status === 204 ? "" : await readBody(response, controller.signal);
|
|
1219
1298
|
} finally {
|
|
1220
1299
|
clearTimeout(timeout);
|
|
1221
1300
|
}
|
|
@@ -1225,7 +1304,7 @@ var createTeakClient = (options) => {
|
|
|
1225
1304
|
return request(path, init, parser, false, next);
|
|
1226
1305
|
}
|
|
1227
1306
|
}
|
|
1228
|
-
const payload =
|
|
1307
|
+
const payload = parseJson(response, text);
|
|
1229
1308
|
if (!response.ok) {
|
|
1230
1309
|
const object = isObject(payload) ? payload : {};
|
|
1231
1310
|
throw new TeakApiError(codeFrom(object.code, response.status === 404 ? "NOT_FOUND" : "REQUEST_FAILED"), String(object.error || response.statusText), {
|
package/package.json
CHANGED
|
@@ -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
|
@@ -290,6 +290,12 @@ export interface Me {
|
|
|
290
290
|
cardLimit: number | null;
|
|
291
291
|
email: string;
|
|
292
292
|
id: string;
|
|
293
|
+
/**
|
|
294
|
+
* The account's Teak language, chosen in Settings: a BCP 47 code such as
|
|
295
|
+
* `en`, `de` or `pt-BR`. More may be added, so treat an unknown code as
|
|
296
|
+
* English.
|
|
297
|
+
*/
|
|
298
|
+
locale: string;
|
|
293
299
|
name?: string;
|
|
294
300
|
plan: "free" | "pro";
|
|
295
301
|
settingsUrl: string;
|
|
@@ -414,7 +420,9 @@ export declare const buildCardsSearchParams: (input: {
|
|
|
414
420
|
* `is:fav`, `is:archived`, `site:github.com`, `folder:"Reading list"`,
|
|
415
421
|
* `price:<100` (also `>`, `<=`, `>=` and `=`), `text:"total due"` (only
|
|
416
422
|
* the words Teak read in images, video thumbnails, link previews and scanned
|
|
417
|
-
* PDFs) and `"quoted phrases"`.
|
|
423
|
+
* PDFs) and `"quoted phrases"`. A `-` leaves cards out (`design -figma`,
|
|
424
|
+
* `-tag:old`), uppercase `OR` matches either side and parentheses group:
|
|
425
|
+
* `(site:github.com OR site:gitlab.com) -is:archived`.
|
|
418
426
|
*/
|
|
419
427
|
query?: string;
|
|
420
428
|
/** Required with `sort: "shuffle"`: the same seed gives the same order. */
|