teak-sdk 1.0.86 → 1.0.88

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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # Teak SDK
2
2
 
3
- Typed API client and OAuth discovery for Teak. The distribution bundles the
4
- canonical implementation from `packages/convex/client/sdk.ts`; it has no runtime
5
- dependencies and works without the Teak monorepo.
3
+ Typed API client and OAuth discovery for Teak. The source lives in `src/`, and
4
+ `bun run build` writes the npm package to `dist/`: one bundle with no runtime
5
+ dependencies, so it works without the Teak monorepo.
6
6
 
7
7
  ```ts
8
8
  import { createTeakClient, discoverAuthServer } from "teak-sdk";
@@ -1,12 +1,18 @@
1
- // ../convex/devUrls.ts
2
- var TEAK_DEV_DEPLOYMENT_NAME = "reminiscent-kangaroo-59";
3
- var DEFAULT_TEAK_DEV_CONVEX_URL = `https://${TEAK_DEV_DEPLOYMENT_NAME}.convex.cloud`;
4
- var DEFAULT_TEAK_DEV_CONVEX_SITE_URL = `https://${TEAK_DEV_DEPLOYMENT_NAME}.convex.site`;
5
- var isLocalDevelopmentHostname = (hostname) => {
6
- const normalizedHostname = hostname.trim().toLowerCase();
7
- return normalizedHostname === "localhost" || normalizedHostname === "127.0.0.1" || normalizedHostname.endsWith(".localhost");
1
+ // ../convex/shared/utils/formatBytes.ts
2
+ var BYTE_UNITS = ["B", "KB", "MB", "GB"];
3
+ var formatBytes = (bytes) => {
4
+ if (!(bytes && Number.isFinite(bytes) && bytes > 0)) {
5
+ return "0 B";
6
+ }
7
+ let value = bytes;
8
+ let unit = 0;
9
+ while (value >= 1024 && unit < BYTE_UNITS.length - 1) {
10
+ value /= 1024;
11
+ unit += 1;
12
+ }
13
+ const digits = unit > 0 && value < 10 ? 1 : 0;
14
+ return `${value.toFixed(digits)} ${BYTE_UNITS[unit]}`;
8
15
  };
9
-
10
16
  // ../convex/shared/boundedResponse.ts
11
17
  class ResponseSizeLimitError extends Error {
12
18
  }
@@ -40,28 +46,20 @@ var consumeResponseWithinLimit = async (response, maxBytes, consume) => {
40
46
  };
41
47
  var readResponseTextWithinLimit = (response, maxBytes) => consumeResponseWithinLimit(response, maxBytes, (boundedResponse) => boundedResponse.text());
42
48
 
43
- // ../convex/shared/workosResources.ts
44
- var WORKOS_RESOURCES = {
45
- api: "https://teakvault.com/api",
46
- mcp: "https://teakvault.com/mcp"
47
- };
49
+ // ../convex/shared/oauthSurfaces.ts
50
+ var OAUTH_SURFACES = ["cli", "raycast", "chrome", "firefox"];
48
51
 
49
- // ../convex/client/authDiscovery.ts
50
- var OAUTH_SURFACES = [
51
- "cli",
52
- "raycast",
53
- "chrome",
54
- "firefox",
55
- "safari"
56
- ];
57
- var caches = new WeakMap;
58
- var object = (value) => {
59
- if (!value || typeof value !== "object" || Array.isArray(value)) {
60
- throw new Error("Invalid OAuth discovery document");
61
- }
62
- return value;
52
+ // ../convex/devUrls.ts
53
+ var TEAK_DEV_DEPLOYMENT_NAME = "reminiscent-kangaroo-59";
54
+ var DEFAULT_TEAK_DEV_CONVEX_URL = `https://${TEAK_DEV_DEPLOYMENT_NAME}.convex.cloud`;
55
+ var DEFAULT_TEAK_DEV_CONVEX_SITE_URL = `https://${TEAK_DEV_DEPLOYMENT_NAME}.convex.site`;
56
+ var isLocalDevelopmentHostname = (hostname) => {
57
+ const normalizedHostname = hostname.trim().toLowerCase();
58
+ return normalizedHostname === "localhost" || normalizedHostname === "127.0.0.1" || normalizedHostname.endsWith(".localhost");
63
59
  };
64
- var loopback = (hostname) => isLocalDevelopmentHostname(hostname) || hostname === "[::1]";
60
+
61
+ // ../convex/shared/oauthUrl.ts
62
+ var isLoopbackHostname = (hostname) => isLocalDevelopmentHostname(hostname) || hostname === "[::1]";
65
63
  var privateHost = (hostname) => {
66
64
  const octets = hostname.split(".").map(Number);
67
65
  return hostname.endsWith(".localhost") || hostname.endsWith(".local") || hostname.startsWith("[") || octets.length === 4 && octets.every((n) => Number.isInteger(n) && n >= 0 && n <= 255) && (octets[0] === 0 || octets[0] === 10 || octets[0] === 100 && octets[1] >= 64 && octets[1] <= 127 || octets[0] === 127 || octets[0] === 169 && octets[1] === 254 || octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31 || octets[0] === 192 && octets[1] === 168);
@@ -71,22 +69,39 @@ var validateOAuthUrl = (raw, local = false) => {
71
69
  throw new Error("Missing OAuth discovery URL");
72
70
  }
73
71
  const url = new URL(raw);
74
- if (url.username || url.password || url.hash || url.protocol !== "https:" && !(local && url.protocol === "http:" && loopback(url.hostname)) || !local && loopback(url.hostname) || privateHost(url.hostname) && !(local && loopback(url.hostname))) {
72
+ if (url.username || url.password || url.hash || url.protocol !== "https:" && !(local && url.protocol === "http:" && isLoopbackHostname(url.hostname)) || !local && isLoopbackHostname(url.hostname) || privateHost(url.hostname) && !(local && isLoopbackHostname(url.hostname))) {
75
73
  throw new Error("Unsafe OAuth discovery URL");
76
74
  }
77
75
  return url;
78
76
  };
77
+
78
+ // ../convex/shared/workosResources.ts
79
+ var PRODUCTION_WORKOS_RESOURCE_ORIGIN = "https://teakvault.com";
80
+ var workosResourcesFor = (url) => {
81
+ const { origin } = new URL(url);
82
+ return Object.freeze({ api: `${origin}/api`, mcp: `${origin}/mcp` });
83
+ };
84
+ var PRODUCTION_WORKOS_RESOURCES = workosResourcesFor(PRODUCTION_WORKOS_RESOURCE_ORIGIN);
85
+
86
+ // src/authDiscovery.ts
87
+ var caches = new WeakMap;
88
+ var object = (value) => {
89
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
90
+ throw new Error("Invalid OAuth discovery document");
91
+ }
92
+ return value;
93
+ };
79
94
  function discoverAuthServer(siteUrl, options = {}) {
80
95
  const site = validateOAuthUrl(siteUrl, true);
81
- const siteIsLocal = loopback(site.hostname);
96
+ const siteIsLocal = isLoopbackHostname(site.hostname);
82
97
  const permittedLocalIssuer = options.localIssuer === undefined ? undefined : validateOAuthUrl(options.localIssuer, true);
83
- if (permittedLocalIssuer && !loopback(permittedLocalIssuer.hostname)) {
98
+ if (permittedLocalIssuer && !isLoopbackHostname(permittedLocalIssuer.hostname)) {
84
99
  throw new Error("Development issuer must use loopback");
85
100
  }
86
101
  const local = siteIsLocal || permittedLocalIssuer !== undefined;
87
102
  const validateUrl = (raw) => {
88
103
  const url = validateOAuthUrl(raw, local);
89
- if (loopback(url.hostname) && !siteIsLocal && url.origin !== permittedLocalIssuer?.origin) {
104
+ if (isLoopbackHostname(url.hostname) && !siteIsLocal && url.origin !== permittedLocalIssuer?.origin) {
90
105
  throw new Error("Unapproved loopback OAuth server");
91
106
  }
92
107
  return url;
@@ -113,7 +128,7 @@ function discoverAuthServer(siteUrl, options = {}) {
113
128
  const read = async (url) => {
114
129
  const response = await fetchImpl(url, {
115
130
  method: "GET",
116
- credentials: "omit",
131
+ ...{ credentials: "omit" },
117
132
  redirect: "error",
118
133
  headers: { Accept: "application/json" },
119
134
  signal: controller.signal
@@ -158,7 +173,7 @@ function discoverAuthServer(siteUrl, options = {}) {
158
173
  throw new Error("Missing OAuth client registration");
159
174
  }
160
175
  }
161
- if (resource.resource !== WORKOS_RESOURCES.mcp) {
176
+ if (resource.resource !== PRODUCTION_WORKOS_RESOURCES.mcp && resource.resource !== workosResourcesFor(site.origin).mcp) {
162
177
  throw new Error("OAuth resource mismatch");
163
178
  }
164
179
  return Object.freeze({
@@ -182,7 +197,18 @@ function discoverAuthServer(siteUrl, options = {}) {
182
197
  });
183
198
  return pending;
184
199
  }
185
- // ../convex/client/workosConnect.ts
200
+ // src/oauthCredentials.ts
201
+ var isWorkosCredential = (saved) => Boolean(saved?.origin && saved.issuer && saved.clientId?.startsWith("client_"));
202
+ var matchesBinding = (saved, current) => saved?.origin === current.origin && saved.issuer === current.issuer && saved.clientId === current.clientId;
203
+ var isRefreshTokenDead = (result) => !result.ok && result.reason === "refresh_token_rejected";
204
+ var refreshAndSave = async (refresh, save) => {
205
+ const renewed = await refresh();
206
+ if (renewed !== null && renewed !== undefined) {
207
+ await save(renewed);
208
+ }
209
+ return renewed;
210
+ };
211
+ // src/workosConnect.ts
186
212
  var WORKOS_CONNECT_SCOPE = "openid profile email offline_access";
187
213
  var MAX_RESPONSE_BYTES = 64 * 1024;
188
214
  var MAX_TOKEN_LIFETIME_SECONDS = 365 * 24 * 3600;
@@ -196,7 +222,7 @@ var base64Url = (bytes) => {
196
222
  };
197
223
  var randomBase64Url = (byteLength) => base64Url(crypto.getRandomValues(new Uint8Array(byteLength)));
198
224
  var createPkceChallenge = async (verifier) => base64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier))));
199
- var apiResource = (auth) => new URL("/api", auth.resource).href;
225
+ var apiResource = (auth) => workosResourcesFor(auth.resource).api;
200
226
  var createConnectAuthorizeUrl = (auth, params) => {
201
227
  const url = new URL(auth.authorizationEndpoint);
202
228
  url.searchParams.set("response_type", "code");
@@ -211,7 +237,7 @@ var createConnectAuthorizeUrl = (auth, params) => {
211
237
  };
212
238
  var send = (url, init, local) => fetch(validateOAuthUrl(url, local).href, {
213
239
  ...init,
214
- credentials: "omit",
240
+ ...{ credentials: "omit" },
215
241
  redirect: "error",
216
242
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
217
243
  });
@@ -304,8 +330,55 @@ var fetchConnectOwnerId = async (apiUrl, accessToken, options = {}) => {
304
330
  // ../convex/shared/markdown.ts
305
331
  var MARKDOWN_CONTENT_MAX_BYTES = 512 * 1024;
306
332
 
333
+ // ../files-protocol/src/decisions.ts
334
+ var CLEF_MODEL_ID = "@cf/cloudflare/clef";
335
+ var CLEF_FLASH_MODEL_ID = "@cf/cloudflare/clef-flash";
336
+ var CLEF_INPUT_USD_PER_MILLION = {
337
+ [CLEF_MODEL_ID]: 0.24,
338
+ [CLEF_FLASH_MODEL_ID]: 0.038
339
+ };
340
+ var RELEVANCE_LEVELS = [
341
+ "Not related",
342
+ "Loosely related",
343
+ "Relevant",
344
+ "Exactly what they are looking for"
345
+ ];
346
+ var RELEVANCE_MAX_SCORE = RELEVANCE_LEVELS.length - 1;
347
+ // ../files-protocol/src/embeddings.ts
348
+ var ASCII_TOKEN_COST = 1 / 3;
349
+ // ../files-protocol/src/storage.ts
350
+ var FILES_UPLOAD_MAX_BYTES = 100 * 1024 * 1024;
351
+ var FILES_SIGNED_URL_MIN_VALIDITY_SECONDS = 7 * 24 * 60 * 60;
352
+ var FILE_CACHE_MAX_AGE_SECONDS = FILES_SIGNED_URL_MIN_VALIDITY_SECONDS - 24 * 60 * 60;
353
+ var FILES_PRIVATE_CACHE_CONTROL = `private, max-age=${FILE_CACHE_MAX_AGE_SECONDS}, immutable`;
354
+ var FILES_EDGE_CACHE_CONTROL = `public, max-age=${FILE_CACHE_MAX_AGE_SECONDS}, immutable`;
355
+ var FILES_DEV_KEY_PREFIX = "dev/";
356
+ var filesUserKeyRoot = (envPrefix) => `${envPrefix}users/`;
357
+ var filesExportKeyRoot = (envPrefix) => `${envPrefix}exports/`;
358
+ var FILES_OBJECT_KEY_ROOTS = ["", FILES_DEV_KEY_PREFIX].flatMap((envPrefix) => [filesUserKeyRoot(envPrefix), filesExportKeyRoot(envPrefix)]);
359
+
360
+ // ../files-protocol/src/inspect.ts
361
+ var FILES_INSPECT_MAX_ARCHIVE_BYTES = 25 * 1024 * 1024;
362
+ var FILES_INSPECT_MAX_SOURCE_BYTES = 2 * 1024 * 1024;
363
+ // ../files-protocol/src/remoteAsset.ts
364
+ var FILES_REMOTE_ASSET_MAX_BYTES = {
365
+ image: 20 * 1024 * 1024,
366
+ video: 50 * 1024 * 1024
367
+ };
368
+ // ../files-protocol/src/workflows.ts
369
+ var WORKFLOW_RPC_MAX_TTL_SECONDS = 5 * 60;
370
+ var WORKFLOW_RPC_MAX_BODY_BYTES = 4 * 1024 * 1024;
371
+ var RPC_KINDS = new Set(["action", "mutation", "query"]);
372
+ // ../files-protocol/src/index.ts
373
+ var FILES_UPLOAD_MAX_TTL_SECONDS = 15 * 60;
374
+ var FILES_OP_MAX_TTL_SECONDS = 15 * 60;
375
+ var FILES_OP_TTL_SECONDS = 10 * 60;
376
+ var FILES_TRANSCRIPT_MAX_BYTES = 512 * 1024;
377
+ var FILES_EXTRACTED_TEXT_MAX_CHARACTERS = 512 * 1024;
378
+ var FILES_EXTRACTED_TEXT_MAX_BYTES = 512 * 1024;
379
+ var FILES_IMPORT_PAGE_BYTES = 4 * 1024 * 1024;
380
+
307
381
  // ../convex/shared/fileFormats.ts
308
- var MAX_FILE_SIZE = 100 * 1024 * 1024;
309
382
  var GENERIC_MIME_TYPES = new Set([
310
383
  "",
311
384
  "application/binary",
@@ -764,6 +837,19 @@ var FILE_FORMATS = [
764
837
  preview: "audio"
765
838
  })
766
839
  ];
840
+ // ../convex/shared/tags.ts
841
+ var MAX_TAG_NAME_LENGTH = 64;
842
+ var TAG_OPERATION_STALL_MS = 2 * 60000;
843
+ var TAG_ERROR_MESSAGES = {
844
+ EMPTY_NAME: "Name the tag",
845
+ NAME_TOO_LONG: `Tags can be at most ${MAX_TAG_NAME_LENGTH} characters`,
846
+ NOT_FOUND: "Tag not found",
847
+ SAME_TAG: "Choose a different tag to merge into",
848
+ OPERATION_RUNNING: "Another tag change is still running",
849
+ OPERATION_NOT_FOUND: "Tag operation not found"
850
+ };
851
+ var HIDDEN_TAG_KEYS = ["secret", "hidden"];
852
+ var hiddenTagKeys = new Set(HIDDEN_TAG_KEYS);
767
853
  // ../convex/shared/constants.ts
768
854
  var cardTypes = [
769
855
  "text",
@@ -775,7 +861,9 @@ var cardTypes = [
775
861
  "palette",
776
862
  "quote"
777
863
  ];
864
+ var TRASH_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
778
865
  var MULTIPART_UPLOAD_THRESHOLD = 8 * 1024 * 1024;
866
+ var API_IDEMPOTENCY_KEY_TTL_MS = 24 * 60 * 60 * 1000;
779
867
  var CARD_TYPES = [...cardTypes];
780
868
  var CARD_TYPE_REGISTRY = {
781
869
  text: {
@@ -828,27 +916,42 @@ var RESERVED_KEYWORDS = [
828
916
  { value: "trash", label: "Trash" }
829
917
  ];
830
918
 
831
- // ../convex/client/sdk.ts
919
+ // src/index.ts
832
920
  var CARD_TYPES2 = cardTypes;
833
- var CARD_SORTS = ["newest", "oldest"];
921
+ var CARD_SORTS = ["newest", "oldest", "relevance", "shuffle"];
834
922
  var ERROR_CODES = [
835
923
  "AUTH_REQUIRED",
836
924
  "BAD_REQUEST",
925
+ "CARD_LIMIT_REACHED",
837
926
  "CONFIG_ERROR",
838
927
  "CONFLICT",
839
928
  "CONTENT_TOO_LARGE",
929
+ "FILE_TOO_LARGE",
930
+ "FOLDER_LIMIT",
931
+ "FORBIDDEN",
932
+ "IN_PROGRESS",
840
933
  "INTERNAL_ERROR",
841
934
  "INVALID_API_KEY",
842
935
  "INVALID_INPUT",
936
+ "INVALID_STORAGE_KEY",
843
937
  "INVALID_UTF8",
938
+ "LIMIT_REACHED",
844
939
  "METHOD_NOT_ALLOWED",
845
940
  "NETWORK_ERROR",
846
941
  "NOT_FOUND",
942
+ "NO_CONTENT",
943
+ "NOT_IN_TRASH",
847
944
  "PARSE_ERROR",
848
945
  "RATE_LIMITED",
849
946
  "REQUEST_FAILED",
947
+ "SERVICE_UNAVAILABLE",
948
+ "SMART_FOLDER",
949
+ "TAG_EXISTS",
950
+ "TAG_OPERATION_RUNNING",
850
951
  "TIMEOUT",
851
- "UNAUTHORIZED"
952
+ "TYPE_MISMATCH",
953
+ "UNAUTHORIZED",
954
+ "UNSUPPORTED_TYPE"
852
955
  ];
853
956
 
854
957
  class TeakApiError extends Error {
@@ -931,6 +1034,9 @@ var buildCardsSearchParams = (input) => {
931
1034
  if (input.tag?.trim()) {
932
1035
  search.set("tag", input.tag.trim());
933
1036
  }
1037
+ if (input.folder?.trim()) {
1038
+ search.set("folder", input.folder.trim());
1039
+ }
934
1040
  if (input.cursor?.trim()) {
935
1041
  search.set("cursor", input.cursor.trim());
936
1042
  }
@@ -943,8 +1049,20 @@ var buildCardsSearchParams = (input) => {
943
1049
  if (input.trashed) {
944
1050
  search.set("trashed", "true");
945
1051
  }
946
- if (input.sort === "oldest") {
947
- search.set("sort", "oldest");
1052
+ if (input.archived === "only" || input.archived === "include") {
1053
+ search.set("archived", input.archived);
1054
+ }
1055
+ if (input.similarTo?.trim()) {
1056
+ search.set("similarTo", input.similarTo.trim());
1057
+ }
1058
+ if (input.mode === "keyword" || input.mode === "semantic") {
1059
+ search.set("mode", input.mode);
1060
+ }
1061
+ if (input.sort === "oldest" || input.sort === "shuffle") {
1062
+ search.set("sort", input.sort);
1063
+ }
1064
+ if (input.sort === "shuffle" && typeof input.shuffleSeed === "number") {
1065
+ search.set("shuffleSeed", String(input.shuffleSeed));
948
1066
  }
949
1067
  if (typeof input.createdAfter === "number") {
950
1068
  search.set("createdAfter", String(input.createdAfter));
@@ -983,6 +1101,35 @@ var asPage = (value) => {
983
1101
  }
984
1102
  return value;
985
1103
  };
1104
+ var asFolder = (value) => {
1105
+ if (!isObject(value) || typeof value.id !== "string" || typeof value.name !== "string") {
1106
+ throw new TeakApiError("PARSE_ERROR", "Teak returned an unexpected folder payload.");
1107
+ }
1108
+ return value;
1109
+ };
1110
+ var asFolders = (value) => {
1111
+ if (!(isObject(value) && Array.isArray(value.items))) {
1112
+ throw new TeakApiError("PARSE_ERROR");
1113
+ }
1114
+ return { items: value.items.map(asFolder) };
1115
+ };
1116
+ var asFolderCards = (value) => {
1117
+ if (!(isObject(value) && isStringArray(value.updatedIds) && isStringArray(value.failedIds))) {
1118
+ throw new TeakApiError("PARSE_ERROR");
1119
+ }
1120
+ return {
1121
+ updatedIds: value.updatedIds,
1122
+ failedIds: value.failedIds,
1123
+ folderLimitIds: isStringArray(value.folderLimitIds) ? value.folderLimitIds : []
1124
+ };
1125
+ };
1126
+ var asTagOperation = (value) => {
1127
+ if (!isObject(value) || typeof value.id !== "string" || typeof value.status !== "string") {
1128
+ throw new TeakApiError("PARSE_ERROR", "Teak returned an unexpected tag operation payload.");
1129
+ }
1130
+ const { operationId: _operationId, ...operation } = value;
1131
+ return operation;
1132
+ };
986
1133
  var asTags = (value) => {
987
1134
  if (!(isObject(value) && Array.isArray(value.items))) {
988
1135
  throw new TeakApiError("PARSE_ERROR");
@@ -1000,6 +1147,24 @@ var asCreate = (value) => {
1000
1147
  }
1001
1148
  return value;
1002
1149
  };
1150
+ var asRefresh = (value) => {
1151
+ if (!isObject(value) || typeof value.cardId !== "string" || value.status !== "queued") {
1152
+ throw new TeakApiError("PARSE_ERROR");
1153
+ }
1154
+ return value;
1155
+ };
1156
+ var asSavedPdf = (value) => {
1157
+ if (!isObject(value) || typeof value.cardId !== "string" || typeof value.created !== "boolean") {
1158
+ throw new TeakApiError("PARSE_ERROR");
1159
+ }
1160
+ return { cardId: value.cardId, created: value.created };
1161
+ };
1162
+ var asCardContent = (value) => {
1163
+ if (!isObject(value) || typeof value.cardId !== "string" || typeof value.text !== "string" || typeof value.charCount !== "number" || typeof value.truncated !== "boolean" || !["document", "transcript", "content"].includes(String(value.kind))) {
1164
+ throw new TeakApiError("PARSE_ERROR");
1165
+ }
1166
+ return value;
1167
+ };
1003
1168
  var asMe = (value) => {
1004
1169
  if (!(isObject(value) && isObject(value.data)) || typeof value.data.id !== "string") {
1005
1170
  throw new TeakApiError("PARSE_ERROR");
@@ -1074,6 +1239,25 @@ var createTeakClient = (options) => {
1074
1239
  const qs = (params) => buildCardsSearchParams(params);
1075
1240
  const idempotency = (requestOptions) => requestOptions?.idempotencyKey ? { "Idempotency-Key": requestOptions.idempotencyKey } : {};
1076
1241
  const cardPath = (id) => `/v1/cards/${encodeURIComponent(id)}`;
1242
+ const folderPath = (id) => `/v1/folders/${encodeURIComponent(id)}`;
1243
+ const putFile = async (uploadUrl, bytes, mimeType) => {
1244
+ const headers = new Headers({ "Content-Type": mimeType });
1245
+ const byteLength = byteLengthForBody(bytes);
1246
+ if (byteLength !== null) {
1247
+ headers.set("Content-Length", String(byteLength));
1248
+ }
1249
+ const response = await fetchImpl(uploadUrl, {
1250
+ body: bytes,
1251
+ headers,
1252
+ method: "PUT"
1253
+ });
1254
+ if (!response.ok) {
1255
+ throw new TeakApiError("REQUEST_FAILED", `Upload failed with status ${response.status}`, { status: response.status });
1256
+ }
1257
+ return { fileEtag: response.headers.get("etag") ?? undefined };
1258
+ };
1259
+ const createThumbnailUpload = (id, input) => request(`${cardPath(id)}/thumbnail/upload`, { body: JSON.stringify(input), method: "POST" }, asUpload);
1260
+ const applyThumbnail = (id, fileKey) => request(`${cardPath(id)}/thumbnail`, { body: JSON.stringify({ fileKey }), method: "PUT" }, asCard);
1077
1261
  return {
1078
1262
  cards: {
1079
1263
  bulk: (operation, items, requestOptions) => request("/v1/cards/bulk", {
@@ -1091,13 +1275,43 @@ var createTeakClient = (options) => {
1091
1275
  headers: idempotency(requestOptions),
1092
1276
  method: "POST"
1093
1277
  }, asCreate),
1278
+ content: (id) => request(`${cardPath(id)}/content`, { method: "GET" }, asCardContent),
1094
1279
  delete: (id, deleteOptions) => request(`${cardPath(id)}${deleteOptions?.permanent ? "?permanent=true" : ""}`, { method: "DELETE" }, () => null),
1095
1280
  duplicate: (url) => request(`/v1/cards/duplicate?${new URLSearchParams({ url }).toString()}`, { method: "GET" }, (v) => isObject(v) && typeof v.cardId === "string" ? v.cardId : null),
1096
1281
  favorites: (input = {}) => request(`/v1/cards?${qs({ include: "content,metadata", ...input, favorited: true })}`, { method: "GET" }, asPage),
1097
1282
  get: (id) => request(cardPath(id), { method: "GET" }, asCard),
1283
+ links: (id) => request(cardPath(id), { method: "GET" }, (value) => {
1284
+ const card = asCard(value);
1285
+ return {
1286
+ backlinks: card.backlinks ?? [],
1287
+ backlinksTruncated: card.backlinksTruncated === true,
1288
+ links: card.links ?? [],
1289
+ linksTruncated: card.linksTruncated === true
1290
+ };
1291
+ }),
1098
1292
  list: (input = {}) => request(`/v1/cards?${qs(input)}`, { method: "GET" }, asPage),
1293
+ createThumbnailUpload,
1294
+ refresh: (id) => request(`${cardPath(id)}/refresh`, { method: "POST" }, asRefresh),
1295
+ resetThumbnail: (id) => request(`${cardPath(id)}/thumbnail`, { method: "DELETE" }, asCard),
1099
1296
  restore: (id) => request(`${cardPath(id)}/restore`, { method: "POST" }, () => null),
1297
+ similar: (id, input = {}) => request(`/v1/cards?${qs({ include: "content,metadata", ...input, similarTo: id })}`, { method: "GET" }, asPage),
1100
1298
  search: (input = {}) => request(`/v1/cards?${qs({ include: "content,metadata", ...input })}`, { method: "GET" }, asPage),
1299
+ setArchived: (id, isArchived) => request(cardPath(id), { body: JSON.stringify({ isArchived }), method: "PATCH" }, asCard),
1300
+ setThumbnail: async (id, image, mimeType) => {
1301
+ if (isObject(image) && typeof image.fileKey === "string") {
1302
+ return applyThumbnail(id, image.fileKey);
1303
+ }
1304
+ const bytes = image;
1305
+ const fileSize = byteLengthForBody(bytes);
1306
+ if (fileSize === null || !mimeType) {
1307
+ throw new TeakApiError("INVALID_INPUT", "setThumbnail needs the image's bytes and its MIME type.");
1308
+ }
1309
+ const upload = await createThumbnailUpload(id, { fileSize, mimeType });
1310
+ await putFile(upload.uploadUrl, bytes, mimeType);
1311
+ return applyThumbnail(id, upload.fileKey);
1312
+ },
1313
+ savePdf: (id) => request(`${cardPath(id)}/save-pdf`, { method: "POST" }, asSavedPdf),
1314
+ setDone: (id, done) => request(`${cardPath(id)}/done`, { method: done ? "POST" : "DELETE" }, asCard),
1101
1315
  setFavorite: (id, isFavorited) => request(`${cardPath(id)}/favorite`, { body: JSON.stringify({ isFavorited }), method: "PATCH" }, asCard),
1102
1316
  update: (id, input) => request(cardPath(id), { body: JSON.stringify(input), method: "PATCH" }, asCard)
1103
1317
  },
@@ -1105,28 +1319,26 @@ var createTeakClient = (options) => {
1105
1319
  latest: () => request("/v1/exports/latest", { method: "GET" }, asLatestExport),
1106
1320
  start: () => request("/v1/exports", { method: "POST" }, (v) => v.job)
1107
1321
  },
1322
+ folders: {
1323
+ addCards: (id, cardIds) => request(`${folderPath(id)}/cards`, { body: JSON.stringify({ cardIds }), method: "POST" }, asFolderCards),
1324
+ create: (input) => request("/v1/folders", { body: JSON.stringify(input), method: "POST" }, asFolder),
1325
+ delete: (id) => request(folderPath(id), { method: "DELETE" }, () => null),
1326
+ get: (id) => request(folderPath(id), { method: "GET" }, asFolder),
1327
+ list: () => request("/v1/folders", { method: "GET" }, asFolders),
1328
+ removeCards: (id, cardIds) => request(`${folderPath(id)}/cards`, { body: JSON.stringify({ cardIds }), method: "DELETE" }, asFolderCards),
1329
+ update: (id, input) => request(folderPath(id), { body: JSON.stringify(input), method: "PATCH" }, asFolder)
1330
+ },
1108
1331
  me: () => request("/v1/me", { method: "GET" }, asMe),
1109
1332
  tags: {
1110
- list: () => request("/v1/tags", { method: "GET" }, asTags)
1333
+ delete: (tag) => request("/v1/tags/delete", { body: JSON.stringify({ tag }), method: "POST" }, asTagOperation),
1334
+ list: () => request("/v1/tags", { method: "GET" }, asTags),
1335
+ merge: (from, into) => request("/v1/tags/merge", { body: JSON.stringify({ from, into }), method: "POST" }, asTagOperation),
1336
+ operation: (id) => request(`/v1/tags/operations/${encodeURIComponent(id)}`, { method: "GET" }, asTagOperation),
1337
+ rename: (from, to) => request("/v1/tags/rename", { body: JSON.stringify({ from, to }), method: "POST" }, asTagOperation)
1111
1338
  },
1112
1339
  uploads: {
1113
1340
  create: (input) => request("/v1/uploads", { body: JSON.stringify(input), method: "POST" }, asUpload),
1114
- putFile: async (uploadUrl, bytes, mimeType) => {
1115
- const headers = new Headers({ "Content-Type": mimeType });
1116
- const byteLength = byteLengthForBody(bytes);
1117
- if (byteLength !== null) {
1118
- headers.set("Content-Length", String(byteLength));
1119
- }
1120
- const response = await fetchImpl(uploadUrl, {
1121
- body: bytes,
1122
- headers,
1123
- method: "PUT"
1124
- });
1125
- if (!response.ok) {
1126
- throw new TeakApiError("REQUEST_FAILED", `Upload failed with status ${response.status}`, { status: response.status });
1127
- }
1128
- return { fileEtag: response.headers.get("etag") ?? undefined };
1129
- }
1341
+ putFile
1130
1342
  }
1131
1343
  };
1132
1344
  };
@@ -1146,11 +1358,16 @@ export {
1146
1358
  disconnectConnectGrant,
1147
1359
  discoverAuthServer,
1148
1360
  fetchConnectOwnerId,
1361
+ formatBytes,
1149
1362
  isCardType,
1363
+ isRefreshTokenDead,
1150
1364
  isStringList,
1365
+ isWorkosCredential,
1366
+ matchesBinding,
1151
1367
  normalizeLimit,
1152
1368
  parseTags,
1153
1369
  randomBase64Url,
1370
+ refreshAndSave,
1154
1371
  requestConnectTokens,
1155
1372
  staticTokenProvider,
1156
1373
  validateOAuthUrl
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teak-sdk",
3
- "version": "1.0.86",
3
+ "version": "1.0.88",
4
4
  "description": "Teak API client and OAuth discovery for browser and Node applications.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,25 +11,10 @@
11
11
  "type": "module",
12
12
  "exports": {
13
13
  ".": {
14
- "types": "./dist/types/client/sdk.d.ts",
15
- "import": "./dist/sdk.js"
14
+ "types": "./types/index.d.ts",
15
+ "import": "./index.js"
16
16
  }
17
17
  },
18
- "files": [
19
- "dist",
20
- "README.md"
21
- ],
22
- "scripts": {
23
- "build": "bun --no-env-file run build.ts",
24
- "prepack": "bun run build",
25
- "typecheck": "tsc build.ts ../convex/client/sdk.ts --strict --noEmit --moduleResolution bundler --module esnext --target es2022 --skipLibCheck --types bun,node"
26
- },
27
- "devDependencies": {
28
- "@teak/convex": "workspace:*",
29
- "@types/bun": "^1.4.0",
30
- "@types/node": "^26.2.0",
31
- "typescript": "^6.0.3"
32
- },
33
18
  "publishConfig": {
34
19
  "access": "public",
35
20
  "provenance": true
@@ -1,5 +1,6 @@
1
- export declare const OAUTH_SURFACES: readonly ["cli", "raycast", "chrome", "firefox", "safari"];
2
- export type OAuthSurface = (typeof OAUTH_SURFACES)[number];
1
+ import { type OAuthSurface } from "./convex/shared/oauthSurfaces.js";
2
+ export { OAUTH_SURFACES, type OAuthSurface, } from "./convex/shared/oauthSurfaces.js";
3
+ export { validateOAuthUrl } from "./convex/shared/oauthUrl.js";
3
4
  export interface AuthDiscovery {
4
5
  readonly authorizationEndpoint: string;
5
6
  readonly clients: Readonly<Record<OAuthSurface, string>>;
@@ -8,7 +9,6 @@ export interface AuthDiscovery {
8
9
  readonly resource: string;
9
10
  readonly tokenEndpoint: string;
10
11
  }
11
- export declare const validateOAuthUrl: (raw: unknown, local?: boolean) => URL;
12
12
  export declare function discoverAuthServer(siteUrl: string, options?: {
13
13
  fetch?: typeof fetch;
14
14
  forceRefresh?: boolean;
@@ -1,4 +1,3 @@
1
- /** The shared cloud dev deployment every checkout and cloud session uses. */
2
1
  export declare const TEAK_DEV_DEPLOYMENT_NAME = "reminiscent-kangaroo-59";
3
2
  export declare const DEFAULT_TEAK_DEV_APP_URL = "http://localhost:3000";
4
3
  export declare const DEFAULT_TEAK_DEV_CONVEX_URL = "https://reminiscent-kangaroo-59.convex.cloud";