Run oxfmt across repo, add format script and docs

Add .oxfmtignore to skip generated bindings and wasm-pack output.
Add npm format script, update DEVELOPMENT.md for Vite+ toolchain,
and format all non-generated files with oxfmt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gregory Schier
2026-03-13 10:15:49 -07:00
parent 45262edfbd
commit b4a1c418bb
664 changed files with 13638 additions and 13492 deletions

View File

@@ -1,17 +1,17 @@
import { createHash, randomBytes } from 'node:crypto';
import type { Context } from '@yaakapp/api';
import { getRedirectUrlViaExternalBrowser } from '../callbackServer';
import { fetchAccessToken } from '../fetchAccessToken';
import { getOrRefreshAccessToken } from '../getOrRefreshAccessToken';
import type { AccessToken, TokenStoreArgs } from '../store';
import { getDataDirKey, storeToken } from '../store';
import { extractCode } from '../util';
import { createHash, randomBytes } from "node:crypto";
import type { Context } from "@yaakapp/api";
import { getRedirectUrlViaExternalBrowser } from "../callbackServer";
import { fetchAccessToken } from "../fetchAccessToken";
import { getOrRefreshAccessToken } from "../getOrRefreshAccessToken";
import type { AccessToken, TokenStoreArgs } from "../store";
import { getDataDirKey, storeToken } from "../store";
import { extractCode } from "../util";
export const PKCE_SHA256 = 'S256';
export const PKCE_PLAIN = 'plain';
export const PKCE_SHA256 = "S256";
export const PKCE_PLAIN = "plain";
export const DEFAULT_PKCE_METHOD = PKCE_SHA256;
export type CallbackType = 'localhost' | 'hosted';
export type CallbackType = "localhost" | "hosted";
export interface ExternalBrowserOptions {
useExternalBrowser: boolean;
@@ -50,7 +50,7 @@ export async function getAuthorizationCode(
challengeMethod: string;
codeVerifier: string;
} | null;
tokenName: 'access_token' | 'id_token';
tokenName: "access_token" | "id_token";
externalBrowser?: ExternalBrowserOptions;
},
): Promise<AccessToken> {
@@ -74,21 +74,21 @@ export async function getAuthorizationCode(
let authorizationUrl: URL;
try {
authorizationUrl = new URL(`${authorizationUrlRaw ?? ''}`);
authorizationUrl = new URL(`${authorizationUrlRaw ?? ""}`);
} catch {
throw new Error(`Invalid authorization URL "${authorizationUrlRaw}"`);
}
authorizationUrl.searchParams.set('response_type', 'code');
authorizationUrl.searchParams.set('client_id', clientId);
if (scope) authorizationUrl.searchParams.set('scope', scope);
if (state) authorizationUrl.searchParams.set('state', state);
if (audience) authorizationUrl.searchParams.set('audience', audience);
authorizationUrl.searchParams.set("response_type", "code");
authorizationUrl.searchParams.set("client_id", clientId);
if (scope) authorizationUrl.searchParams.set("scope", scope);
if (state) authorizationUrl.searchParams.set("state", state);
if (audience) authorizationUrl.searchParams.set("audience", audience);
if (pkce) {
authorizationUrl.searchParams.set(
'code_challenge',
"code_challenge",
pkceCodeChallenge(pkce.codeVerifier, pkce.challengeMethod),
);
authorizationUrl.searchParams.set('code_challenge_method', pkce.challengeMethod);
authorizationUrl.searchParams.set("code_challenge_method", pkce.challengeMethod);
}
let code: string;
@@ -103,21 +103,21 @@ export async function getAuthorizationCode(
// Pass null to skip redirect URI matching — the callback came from our own local server
const extractedCode = extractCode(result.callbackUrl, null);
if (!extractedCode) {
throw new Error('No authorization code found in callback URL');
throw new Error("No authorization code found in callback URL");
}
code = extractedCode;
actualRedirectUri = result.redirectUri;
} else {
// Use embedded browser flow (original behavior)
if (redirectUri) {
authorizationUrl.searchParams.set('redirect_uri', redirectUri);
authorizationUrl.searchParams.set("redirect_uri", redirectUri);
}
code = await getCodeViaEmbeddedBrowser(ctx, contextId, authorizationUrl, redirectUri);
}
console.log('[oauth2] Code found');
console.log("[oauth2] Code found");
const response = await fetchAccessToken(ctx, {
grantType: 'authorization_code',
grantType: "authorization_code",
accessTokenUrl,
clientId,
clientSecret,
@@ -125,9 +125,9 @@ export async function getAuthorizationCode(
audience,
credentialsInBody,
params: [
{ name: 'code', value: code },
...(pkce ? [{ name: 'code_verifier', value: pkce.codeVerifier }] : []),
...(actualRedirectUri ? [{ name: 'redirect_uri', value: actualRedirectUri }] : []),
{ name: "code", value: code },
...(pkce ? [{ name: "code_verifier", value: pkce.codeVerifier }] : []),
...(actualRedirectUri ? [{ name: "redirect_uri", value: actualRedirectUri }] : []),
],
});
@@ -146,7 +146,7 @@ async function getCodeViaEmbeddedBrowser(
): Promise<string> {
const dataDirKey = await getDataDirKey(ctx, contextId);
const authorizationUrlStr = authorizationUrl.toString();
console.log('[oauth2] Authorizing via embedded browser', authorizationUrlStr);
console.log("[oauth2] Authorizing via embedded browser", authorizationUrlStr);
// oxlint-disable-next-line no-async-promise-executor -- Required for this pattern
return new Promise<string>(async (resolve, reject) => {
@@ -154,10 +154,10 @@ async function getCodeViaEmbeddedBrowser(
const { close } = await ctx.window.openUrl({
dataDirKey,
url: authorizationUrlStr,
label: 'oauth-authorization-url',
label: "oauth-authorization-url",
async onClose() {
if (!foundCode) {
reject(new Error('Authorization window closed'));
reject(new Error("Authorization window closed"));
}
},
async onNavigate({ url: urlStr }) {
@@ -187,21 +187,21 @@ export function genPkceCodeVerifier() {
}
function pkceCodeChallenge(verifier: string, method: string) {
if (method === 'plain') {
if (method === "plain") {
return verifier;
}
const hash = encodeForPkce(createHash('sha256').update(verifier).digest());
const hash = encodeForPkce(createHash("sha256").update(verifier).digest());
return hash
.replace(/=/g, '') // Remove padding '='
.replace(/\+/g, '-') // Replace '+' with '-'
.replace(/\//g, '_'); // Replace '/' with '_'
.replace(/=/g, "") // Remove padding '='
.replace(/\+/g, "-") // Replace '+' with '-'
.replace(/\//g, "_"); // Replace '/' with '_'
}
function encodeForPkce(bytes: Buffer) {
return bytes
.toString('base64')
.replace(/=/g, '') // Remove padding '='
.replace(/\+/g, '-') // Replace '+' with '-'
.replace(/\//g, '_'); // Replace '/' with '_'
.toString("base64")
.replace(/=/g, "") // Remove padding '='
.replace(/\+/g, "-") // Replace '+' with '-'
.replace(/\//g, "_"); // Replace '/' with '_'
}

View File

@@ -1,25 +1,25 @@
import { createPrivateKey, randomUUID } from 'node:crypto';
import type { Context } from '@yaakapp/api';
import jwt, { type Algorithm } from 'jsonwebtoken';
import { fetchAccessToken } from '../fetchAccessToken';
import type { TokenStoreArgs } from '../store';
import { getToken, storeToken } from '../store';
import { isTokenExpired } from '../util';
import { createPrivateKey, randomUUID } from "node:crypto";
import type { Context } from "@yaakapp/api";
import jwt, { type Algorithm } from "jsonwebtoken";
import { fetchAccessToken } from "../fetchAccessToken";
import type { TokenStoreArgs } from "../store";
import { getToken, storeToken } from "../store";
import { isTokenExpired } from "../util";
export const jwtAlgorithms = [
'HS256',
'HS384',
'HS512',
'RS256',
'RS384',
'RS512',
'PS256',
'PS384',
'PS512',
'ES256',
'ES384',
'ES512',
'none',
"HS256",
"HS384",
"HS512",
"RS256",
"RS384",
"RS512",
"PS256",
"PS384",
"PS512",
"ES256",
"ES384",
"ES512",
"none",
] as const;
export const defaultJwtAlgorithm = jwtAlgorithms[0];
@@ -40,7 +40,7 @@ function buildClientAssertionJwt(params: {
}): string {
const { clientId, accessTokenUrl, secret, algorithm } = params;
const isHmac = algorithm.startsWith('HS') || algorithm === 'none';
const isHmac = algorithm.startsWith("HS") || algorithm === "none";
// Resolve the signing key depending on format
let signingKey: jwt.Secret;
@@ -51,25 +51,25 @@ function buildClientAssertionJwt(params: {
if (isHmac) {
// HMAC algorithms use the raw secret (string or Buffer)
signingKey = secret;
} else if (trimmed.startsWith('{')) {
} else if (trimmed.startsWith("{")) {
// Looks like JSON - treat as JWK. There is surely a better way to detect JWK vs a raw secret, but this should work in most cases.
// oxlint-disable-next-line no-explicit-any
let jwk: any;
try {
jwk = JSON.parse(trimmed);
} catch {
throw new Error('Client Assertion secret looks like JSON but is not valid');
throw new Error("Client Assertion secret looks like JSON but is not valid");
}
kid = jwk?.kid;
signingKey = createPrivateKey({ key: jwk, format: 'jwk' });
} else if (trimmed.startsWith('-----')) {
signingKey = createPrivateKey({ key: jwk, format: "jwk" });
} else if (trimmed.startsWith("-----")) {
// PEM-encoded key
signingKey = createPrivateKey({ key: trimmed, format: 'pem' });
signingKey = createPrivateKey({ key: trimmed, format: "pem" });
} else {
throw new Error(
'Client Assertion secret must be a JWK JSON object, a PEM-encoded key ' +
'(starting with -----), or a raw secret for HMAC algorithms.',
"Client Assertion secret must be a JWK JSON object, a PEM-encoded key " +
"(starting with -----), or a raw secret for HMAC algorithms.",
);
}
@@ -84,7 +84,7 @@ function buildClientAssertionJwt(params: {
};
// Build the JWT header; include "kid" when available
const header: jwt.JwtHeader = { alg: algorithm, typ: 'JWT' };
const header: jwt.JwtHeader = { alg: algorithm, typ: "JWT" };
if (kid) {
header.kid = kid;
}
@@ -135,9 +135,9 @@ export async function getClientCredentials(
const common: Omit<
Parameters<typeof fetchAccessToken>[1],
'clientAssertion' | 'clientSecret' | 'credentialsInBody'
"clientAssertion" | "clientSecret" | "credentialsInBody"
> = {
grantType: 'client_credentials',
grantType: "client_credentials",
accessTokenUrl,
audience,
clientId,
@@ -146,7 +146,7 @@ export async function getClientCredentials(
};
const fetchParams: Parameters<typeof fetchAccessToken>[1] =
clientCredentialsMethod === 'client_assertion'
clientCredentialsMethod === "client_assertion"
? {
...common,
clientAssertion: buildClientAssertionJwt({
@@ -154,7 +154,7 @@ export async function getClientCredentials(
algorithm: clientAssertionAlgorithm as Algorithm,
accessTokenUrl,
secret: clientAssertionSecretBase64
? Buffer.from(clientAssertionSecret, 'base64').toString('utf-8')
? Buffer.from(clientAssertionSecret, "base64").toString("utf-8")
: clientAssertionSecret,
}),
}

View File

@@ -1,9 +1,9 @@
import type { Context } from '@yaakapp/api';
import { getRedirectUrlViaExternalBrowser } from '../callbackServer';
import type { AccessToken, AccessTokenRawResponse } from '../store';
import { getDataDirKey, getToken, storeToken } from '../store';
import { isTokenExpired } from '../util';
import type { ExternalBrowserOptions } from './authorizationCode';
import type { Context } from "@yaakapp/api";
import { getRedirectUrlViaExternalBrowser } from "../callbackServer";
import type { AccessToken, AccessTokenRawResponse } from "../store";
import { getDataDirKey, getToken, storeToken } from "../store";
import { isTokenExpired } from "../util";
import type { ExternalBrowserOptions } from "./authorizationCode";
export async function getImplicit(
ctx: Context,
@@ -26,7 +26,7 @@ export async function getImplicit(
scope: string | null;
state: string | null;
audience: string | null;
tokenName: 'access_token' | 'id_token';
tokenName: "access_token" | "id_token";
externalBrowser?: ExternalBrowserOptions;
},
): Promise<AccessToken> {
@@ -43,18 +43,18 @@ export async function getImplicit(
let authorizationUrl: URL;
try {
authorizationUrl = new URL(`${authorizationUrlRaw ?? ''}`);
authorizationUrl = new URL(`${authorizationUrlRaw ?? ""}`);
} catch {
throw new Error(`Invalid authorization URL "${authorizationUrlRaw}"`);
}
authorizationUrl.searchParams.set('response_type', responseType);
authorizationUrl.searchParams.set('client_id', clientId);
if (scope) authorizationUrl.searchParams.set('scope', scope);
if (state) authorizationUrl.searchParams.set('state', state);
if (audience) authorizationUrl.searchParams.set('audience', audience);
if (responseType.includes('id_token')) {
authorizationUrl.searchParams.set("response_type", responseType);
authorizationUrl.searchParams.set("client_id", clientId);
if (scope) authorizationUrl.searchParams.set("scope", scope);
if (state) authorizationUrl.searchParams.set("state", state);
if (audience) authorizationUrl.searchParams.set("audience", audience);
if (responseType.includes("id_token")) {
authorizationUrl.searchParams.set(
'nonce',
"nonce",
String(Math.floor(Math.random() * 9999999999999) + 1),
);
}
@@ -71,7 +71,7 @@ export async function getImplicit(
} else {
// Use embedded browser flow (original behavior)
if (redirectUri) {
authorizationUrl.searchParams.set('redirect_uri', redirectUri);
authorizationUrl.searchParams.set("redirect_uri", redirectUri);
}
newToken = await getTokenViaEmbeddedBrowser(
ctx,
@@ -99,11 +99,11 @@ async function getTokenViaEmbeddedBrowser(
accessTokenUrl: null;
authorizationUrl: string;
},
tokenName: 'access_token' | 'id_token',
tokenName: "access_token" | "id_token",
): Promise<AccessToken> {
const dataDirKey = await getDataDirKey(ctx, contextId);
const authorizationUrlStr = authorizationUrl.toString();
console.log('[oauth2] Authorizing via embedded browser (implicit)', authorizationUrlStr);
console.log("[oauth2] Authorizing via embedded browser (implicit)", authorizationUrlStr);
// oxlint-disable-next-line no-async-promise-executor -- Required for this pattern
return new Promise<AccessToken>(async (resolve, reject) => {
@@ -111,16 +111,16 @@ async function getTokenViaEmbeddedBrowser(
const { close } = await ctx.window.openUrl({
dataDirKey,
url: authorizationUrlStr,
label: 'oauth-authorization-url',
label: "oauth-authorization-url",
async onClose() {
if (!foundAccessToken) {
reject(new Error('Authorization window closed'));
reject(new Error("Authorization window closed"));
}
},
async onNavigate({ url: urlStr }) {
const url = new URL(urlStr);
if (url.searchParams.has('error')) {
return reject(Error(`Failed to authorize: ${url.searchParams.get('error')}`));
if (url.searchParams.has("error")) {
return reject(Error(`Failed to authorize: ${url.searchParams.get("error")}`));
}
const hash = url.hash.slice(1);
@@ -158,13 +158,13 @@ async function extractImplicitToken(
accessTokenUrl: null;
authorizationUrl: string;
},
tokenName: 'access_token' | 'id_token',
tokenName: "access_token" | "id_token",
): Promise<AccessToken> {
const url = new URL(callbackUrl);
// Check for errors
if (url.searchParams.has('error')) {
throw new Error(`Failed to authorize: ${url.searchParams.get('error')}`);
if (url.searchParams.has("error")) {
throw new Error(`Failed to authorize: ${url.searchParams.get("error")}`);
}
// Extract token from fragment
@@ -179,18 +179,18 @@ async function extractImplicitToken(
// Build response from params (prefer fragment, fall back to query)
const response: AccessTokenRawResponse = {
access_token: params.get('access_token') ?? url.searchParams.get('access_token') ?? '',
token_type: params.get('token_type') ?? url.searchParams.get('token_type') ?? undefined,
expires_in: params.has('expires_in')
? parseInt(params.get('expires_in') ?? '0', 10)
: url.searchParams.has('expires_in')
? parseInt(url.searchParams.get('expires_in') ?? '0', 10)
access_token: params.get("access_token") ?? url.searchParams.get("access_token") ?? "",
token_type: params.get("token_type") ?? url.searchParams.get("token_type") ?? undefined,
expires_in: params.has("expires_in")
? parseInt(params.get("expires_in") ?? "0", 10)
: url.searchParams.has("expires_in")
? parseInt(url.searchParams.get("expires_in") ?? "0", 10)
: undefined,
scope: params.get('scope') ?? url.searchParams.get('scope') ?? undefined,
scope: params.get("scope") ?? url.searchParams.get("scope") ?? undefined,
};
// Include id_token if present
const idToken = params.get('id_token') ?? url.searchParams.get('id_token');
const idToken = params.get("id_token") ?? url.searchParams.get("id_token");
if (idToken) {
response.id_token = idToken;
}

View File

@@ -1,8 +1,8 @@
import type { Context } from '@yaakapp/api';
import { fetchAccessToken } from '../fetchAccessToken';
import { getOrRefreshAccessToken } from '../getOrRefreshAccessToken';
import type { AccessToken, TokenStoreArgs } from '../store';
import { storeToken } from '../store';
import type { Context } from "@yaakapp/api";
import { fetchAccessToken } from "../fetchAccessToken";
import { getOrRefreshAccessToken } from "../getOrRefreshAccessToken";
import type { AccessToken, TokenStoreArgs } from "../store";
import { storeToken } from "../store";
export async function getPassword(
ctx: Context,
@@ -50,11 +50,11 @@ export async function getPassword(
clientSecret,
scope,
audience,
grantType: 'password',
grantType: "password",
credentialsInBody,
params: [
{ name: 'username', value: username },
{ name: 'password', value: password },
{ name: "username", value: username },
{ name: "password", value: password },
],
});