teak-sdk 1.0.80 → 1.0.82

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
@@ -14,7 +14,10 @@ const client = createTeakClient({
14
14
  });
15
15
  ```
16
16
 
17
- Use the discovered endpoints and client ID for your OAuth surface. Refresh
17
+ Use the discovered endpoints and client ID for your OAuth surface. The WorkOS
18
+ Connect helpers (`createPkceChallenge`, `createConnectAuthorizeUrl`,
19
+ `requestConnectTokens`, `fetchConnectOwnerId`, `disconnectConnectGrant`) cover
20
+ PKCE sign-in, refresh, account binding and disconnect. Refresh
18
21
  discovery after sign-in or refresh failures. Keep tokens in your platform's secure
19
22
  storage; this package does not store credentials or open a browser.
20
23
 
package/dist/sdk.js CHANGED
@@ -143,7 +143,7 @@ function discoverAuthServer(siteUrl, options = {}) {
143
143
  if (metadata.issuer !== servers[0]) {
144
144
  throw new Error("OAuth issuer mismatch");
145
145
  }
146
- if (clientDocument.primary !== "betterauth" && clientDocument.primary !== "workos") {
146
+ if (clientDocument.primary !== "workos") {
147
147
  throw new Error("Unknown OAuth provider");
148
148
  }
149
149
  if (!(Array.isArray(metadata.code_challenge_methods_supported) && metadata.code_challenge_methods_supported.includes("S256"))) {
@@ -155,18 +155,14 @@ function discoverAuthServer(siteUrl, options = {}) {
155
155
  throw new Error("Missing OAuth client registration");
156
156
  }
157
157
  }
158
- const expectedResource = clientDocument.primary === "workos" ? WORKOS_RESOURCES.mcp : `${site.origin}/mcp`;
159
- if (resource.resource !== expectedResource) {
158
+ if (resource.resource !== WORKOS_RESOURCES.mcp) {
160
159
  throw new Error("OAuth resource mismatch");
161
160
  }
162
161
  return Object.freeze({
163
- primary: clientDocument.primary,
162
+ primary: "workos",
164
163
  issuer: servers[0],
165
164
  authorizationEndpoint: validateUrl(metadata.authorization_endpoint).href,
166
165
  tokenEndpoint: validateUrl(metadata.token_endpoint).href,
167
- ...metadata.revocation_endpoint === undefined ? {} : {
168
- revocationEndpoint: validateUrl(metadata.revocation_endpoint).href
169
- },
170
166
  resource: resource.resource,
171
167
  clients: Object.freeze(Object.fromEntries(OAUTH_SURFACES.map((surface) => [surface, clients[surface]])))
172
168
  });
@@ -183,6 +179,125 @@ function discoverAuthServer(siteUrl, options = {}) {
183
179
  });
184
180
  return pending;
185
181
  }
182
+ // ../convex/client/workosConnect.ts
183
+ var WORKOS_CONNECT_SCOPE = "openid profile email offline_access";
184
+ var MAX_RESPONSE_BYTES = 64 * 1024;
185
+ var MAX_TOKEN_LIFETIME_SECONDS = 365 * 24 * 3600;
186
+ var REQUEST_TIMEOUT_MS = 1e4;
187
+ var base64Url = (bytes) => {
188
+ let binary = "";
189
+ for (const byte of bytes) {
190
+ binary += String.fromCharCode(byte);
191
+ }
192
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]+$/, "");
193
+ };
194
+ var randomBase64Url = (byteLength) => base64Url(crypto.getRandomValues(new Uint8Array(byteLength)));
195
+ var createPkceChallenge = async (verifier) => base64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier))));
196
+ var apiResource = (auth) => new URL("/api", auth.resource).href;
197
+ var createConnectAuthorizeUrl = (auth, params) => {
198
+ const url = new URL(auth.authorizationEndpoint);
199
+ url.searchParams.set("response_type", "code");
200
+ url.searchParams.set("client_id", params.clientId);
201
+ url.searchParams.set("resource", apiResource(auth));
202
+ url.searchParams.set("redirect_uri", params.redirectUri);
203
+ url.searchParams.set("code_challenge", params.codeChallenge);
204
+ url.searchParams.set("code_challenge_method", "S256");
205
+ url.searchParams.set("scope", WORKOS_CONNECT_SCOPE);
206
+ url.searchParams.set("state", params.state);
207
+ return url;
208
+ };
209
+ var send = (url, init, local) => fetch(validateOAuthUrl(url, local).href, {
210
+ ...init,
211
+ credentials: "omit",
212
+ redirect: "error",
213
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
214
+ });
215
+ var readObject = async (response) => {
216
+ const text = await readResponseTextWithinLimit(response, MAX_RESPONSE_BYTES);
217
+ if (text === null) {
218
+ return null;
219
+ }
220
+ try {
221
+ const value = JSON.parse(text);
222
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
223
+ } catch {
224
+ return null;
225
+ }
226
+ };
227
+ var requestConnectTokens = async (auth, options) => {
228
+ const response = await send(auth.tokenEndpoint, {
229
+ method: "POST",
230
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
231
+ body: new URLSearchParams({
232
+ ...options.grant,
233
+ client_id: options.clientId,
234
+ resource: apiResource(auth)
235
+ })
236
+ }, options.local ?? false);
237
+ const payload = await readObject(response);
238
+ const { status } = response;
239
+ if (!response.ok) {
240
+ if (status !== 400 && status !== 401) {
241
+ return { ok: false, reason: "failed", status };
242
+ }
243
+ const refreshTokenRejected = options.grant.grant_type === "refresh_token" && (payload?.error === "invalid_grant" || payload?.error === "invalid_refresh_token" || payload?.code === "invalid_refresh_token");
244
+ if (refreshTokenRejected) {
245
+ return { ok: false, reason: "refresh_token_rejected", status };
246
+ }
247
+ const clientRejected = payload?.error === "invalid_client" || payload?.error === "unauthorized_client";
248
+ return {
249
+ ok: false,
250
+ reason: clientRejected ? "client_rejected" : "rejected",
251
+ status
252
+ };
253
+ }
254
+ const expiresIn = payload?.expires_in;
255
+ const keepsRefreshToken = options.grant.grant_type === "refresh_token" && payload !== null && !("refresh_token" in payload);
256
+ const refreshToken = keepsRefreshToken ? options.grant.refresh_token : payload?.refresh_token;
257
+ if (typeof payload?.access_token !== "string" || !payload.access_token || typeof refreshToken !== "string" || !refreshToken || typeof expiresIn !== "number" || !Number.isSafeInteger(expiresIn) || expiresIn <= 0 || expiresIn > MAX_TOKEN_LIFETIME_SECONDS) {
258
+ return { ok: false, reason: "invalid_response", status };
259
+ }
260
+ return {
261
+ ok: true,
262
+ tokens: {
263
+ accessToken: payload.access_token,
264
+ refreshToken,
265
+ expiresAt: Date.now() + expiresIn * 1000,
266
+ expiresIn
267
+ }
268
+ };
269
+ };
270
+ var apiEndpoint = (apiUrl, path, local) => {
271
+ let base = validateOAuthUrl(apiUrl, local).href;
272
+ while (base.endsWith("/")) {
273
+ base = base.slice(0, -1);
274
+ }
275
+ return `${base.replace(/\/v1$/, "")}${path}`;
276
+ };
277
+ var disconnectConnectGrant = async (apiUrl, accessToken, options = {}) => {
278
+ const local = options.local ?? false;
279
+ const endpoint = apiEndpoint(apiUrl, "/v1/oauth/disconnect", local);
280
+ const disconnect = (token) => send(endpoint, { method: "POST", headers: { Authorization: `Bearer ${token}` } }, local);
281
+ let response = await disconnect(accessToken);
282
+ if (response.status === 401 && options.refreshAccessToken) {
283
+ const renewed = await options.refreshAccessToken();
284
+ if (renewed === null) {
285
+ return "refresh_rejected";
286
+ }
287
+ response = await disconnect(renewed);
288
+ }
289
+ return response.status === 204 ? "disconnected" : "unconfirmed";
290
+ };
291
+ var fetchConnectOwnerId = async (apiUrl, accessToken, options = {}) => {
292
+ const local = options.local ?? false;
293
+ const response = await send(apiEndpoint(apiUrl, "/v1/me", local), { headers: { Authorization: `Bearer ${accessToken}` } }, local);
294
+ if (!response.ok) {
295
+ return null;
296
+ }
297
+ const data = (await readObject(response))?.data;
298
+ const id = data && typeof data === "object" && "id" in data ? data.id : undefined;
299
+ return typeof id === "string" && id ? id : null;
300
+ };
186
301
  // ../convex/shared/markdown.ts
187
302
  var MARKDOWN_CONTENT_MAX_BYTES = 512 * 1024;
188
303
 
@@ -986,13 +1101,20 @@ export {
986
1101
  CARD_TYPES2 as CARD_TYPES,
987
1102
  ERROR_CODES,
988
1103
  TeakApiError,
1104
+ WORKOS_CONNECT_SCOPE,
989
1105
  buildCardsSearchParams,
1106
+ createConnectAuthorizeUrl,
1107
+ createPkceChallenge,
990
1108
  createTeakClient,
1109
+ disconnectConnectGrant,
991
1110
  discoverAuthServer,
1111
+ fetchConnectOwnerId,
992
1112
  isCardType,
993
1113
  isStringList,
994
1114
  normalizeLimit,
995
1115
  parseTags,
1116
+ randomBase64Url,
1117
+ requestConnectTokens,
996
1118
  staticTokenProvider,
997
1119
  validateOAuthUrl
998
1120
  };
@@ -4,9 +4,8 @@ export interface AuthDiscovery {
4
4
  readonly authorizationEndpoint: string;
5
5
  readonly clients: Readonly<Record<OAuthSurface, string>>;
6
6
  readonly issuer: string;
7
- readonly primary: "betterauth" | "workos";
7
+ readonly primary: "workos";
8
8
  readonly resource: string;
9
- readonly revocationEndpoint?: string;
10
9
  readonly tokenEndpoint: string;
11
10
  }
12
11
  export declare const validateOAuthUrl: (raw: unknown, local?: boolean) => URL;
@@ -1,4 +1,5 @@
1
1
  export { type AuthDiscovery, discoverAuthServer, type OAuthSurface, validateOAuthUrl, } from "./authDiscovery.js";
2
+ export { type ConnectTokenResult, type ConnectTokens, createConnectAuthorizeUrl, createPkceChallenge, disconnectConnectGrant, fetchConnectOwnerId, randomBase64Url, requestConnectTokens, WORKOS_CONNECT_SCOPE, } from "./workosConnect.js";
2
3
  import { type CardType } from "../shared/constants.js";
3
4
  import type { FileKind, FilePreviewFacts } from "../shared/fileFormats.js";
4
5
  export type { CardType } from "../shared/constants.js";
@@ -0,0 +1,36 @@
1
+ import { type AuthDiscovery } from "./authDiscovery.js";
2
+ export declare const WORKOS_CONNECT_SCOPE = "openid profile email offline_access";
3
+ export declare const randomBase64Url: (byteLength: number) => string;
4
+ export declare const createPkceChallenge: (verifier: string) => Promise<string>;
5
+ export declare const createConnectAuthorizeUrl: (auth: AuthDiscovery, params: {
6
+ clientId: string;
7
+ codeChallenge: string;
8
+ redirectUri: string;
9
+ state: string;
10
+ }) => URL;
11
+ export interface ConnectTokens {
12
+ accessToken: string;
13
+ expiresAt: number;
14
+ expiresIn: number;
15
+ refreshToken: string;
16
+ }
17
+ export type ConnectTokenResult = {
18
+ ok: true;
19
+ tokens: ConnectTokens;
20
+ } | {
21
+ ok: false;
22
+ reason: "refresh_token_rejected" | "client_rejected" | "rejected" | "failed" | "invalid_response";
23
+ status: number;
24
+ };
25
+ export declare const requestConnectTokens: (auth: AuthDiscovery, options: {
26
+ clientId: string;
27
+ grant: Record<string, string>;
28
+ local?: boolean;
29
+ }) => Promise<ConnectTokenResult>;
30
+ export declare const disconnectConnectGrant: (apiUrl: string, accessToken: string, options?: {
31
+ local?: boolean;
32
+ refreshAccessToken?: () => Promise<string | null>;
33
+ }) => Promise<"disconnected" | "unconfirmed" | "refresh_rejected">;
34
+ export declare const fetchConnectOwnerId: (apiUrl: string, accessToken: string, options?: {
35
+ local?: boolean;
36
+ }) => Promise<string | null>;
@@ -1,4 +1,5 @@
1
- export declare const SIGNUPS_PAUSED_MESSAGE = "New sign-ups are paused while we upgrade sign-in";
1
+ export declare const SIGNUPS_PAUSED_MESSAGE = "New sign-ups are paused right now. Please try again later.";
2
+ export declare const ACCOUNT_CHANGES_PAUSED_MESSAGE = "Account changes are paused right now. Please try again later.";
2
3
  export declare const cardTypes: readonly ["text", "link", "image", "video", "audio", "document", "palette", "quote"];
3
4
  export type CardType = (typeof cardTypes)[number];
4
5
  export declare const VISUAL_STYLE_TAXONOMY: readonly ["abstract", "cinematic", "dark", "illustrative", "minimal", "monochrome", "moody", "pastel", "photographic", "retro", "surreal", "vintage", "vibrant"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teak-sdk",
3
- "version": "1.0.80",
3
+ "version": "1.0.82",
4
4
  "description": "Teak API client and OAuth discovery for browser and Node applications.",
5
5
  "repository": {
6
6
  "type": "git",