@lancedb/lancedb
Version:
LanceDB: A serverless, low-latency vector database for AI applications
218 lines (217 loc) • 7.28 kB
JavaScript
"use strict";
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright The LanceDB Authors
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuthHeaderProvider = exports.StaticHeaderProvider = exports.HeaderProvider = void 0;
/**
* Header providers for LanceDB remote connections.
*
* This module provides a flexible header management framework for LanceDB remote
* connections, allowing users to implement custom header strategies for
* authentication, request tracking, custom metadata, or any other header-based
* requirements.
*
* @module header
*/
/**
* Abstract base class for providing custom headers for each request.
*
* Users can implement this interface to provide dynamic headers for various purposes
* such as authentication (OAuth tokens, API keys), request tracking (correlation IDs),
* custom metadata, or any other header-based requirements. The provider is called
* before each request to ensure fresh header values are always used.
*
* @example
* Simple JWT token provider:
* ```typescript
* class JWTProvider extends HeaderProvider {
* constructor(private token: string) {
* super();
* }
*
* getHeaders(): Record<string, string> {
* return { authorization: `Bearer ${this.token}` };
* }
* }
* ```
*
* @example
* Provider with request tracking:
* ```typescript
* class RequestTrackingProvider extends HeaderProvider {
* constructor(private sessionId: string) {
* super();
* }
*
* getHeaders(): Record<string, string> {
* return {
* "X-Session-Id": this.sessionId,
* "X-Request-Id": `req-${Date.now()}`
* };
* }
* }
* ```
*/
class HeaderProvider {
}
exports.HeaderProvider = HeaderProvider;
/**
* Example implementation: A simple header provider that returns static headers.
*
* This is an example implementation showing how to create a HeaderProvider
* for cases where headers don't change during the session.
*
* @example
* ```typescript
* const provider = new StaticHeaderProvider({
* authorization: "Bearer my-token",
* "X-Custom-Header": "custom-value"
* });
* const headers = provider.getHeaders();
* // Returns: {authorization: 'Bearer my-token', 'X-Custom-Header': 'custom-value'}
* ```
*/
class StaticHeaderProvider extends HeaderProvider {
_headers;
/**
* Initialize with static headers.
* @param headers - Headers to return for every request.
*/
constructor(headers) {
super();
this._headers = { ...headers };
}
/**
* Return the static headers.
* @returns Copy of the static headers.
*/
getHeaders() {
return { ...this._headers };
}
}
exports.StaticHeaderProvider = StaticHeaderProvider;
/**
* Example implementation: OAuth token provider with automatic refresh.
*
* This is an example implementation showing how to manage OAuth tokens
* with automatic refresh when they expire.
*
* @example
* ```typescript
* async function fetchToken(): Promise<TokenResponse> {
* const response = await fetch("https://oauth.example.com/token", {
* method: "POST",
* body: JSON.stringify({
* grant_type: "client_credentials",
* client_id: "your-client-id",
* client_secret: "your-client-secret"
* }),
* headers: { "Content-Type": "application/json" }
* });
* const data = await response.json();
* return {
* accessToken: data.access_token,
* expiresIn: data.expires_in
* };
* }
*
* const provider = new OAuthHeaderProvider(fetchToken);
* const headers = provider.getHeaders();
* // Returns: {"authorization": "Bearer <your-token>"}
* ```
*/
class OAuthHeaderProvider extends HeaderProvider {
_tokenFetcher;
_refreshBufferSeconds;
_currentToken = null;
_tokenExpiresAt = null;
_refreshPromise = null;
/**
* Initialize the OAuth provider.
* @param tokenFetcher - Function to fetch new tokens. Should return object with 'accessToken' and optionally 'expiresIn'.
* @param refreshBufferSeconds - Seconds before expiry to refresh token. Default 300 (5 minutes).
*/
constructor(tokenFetcher, refreshBufferSeconds = 300) {
super();
this._tokenFetcher = tokenFetcher;
this._refreshBufferSeconds = refreshBufferSeconds;
}
/**
* Check if token needs refresh.
*/
_needsRefresh() {
if (this._currentToken === null) {
return true;
}
if (this._tokenExpiresAt === null) {
// No expiration info, assume token is valid
return false;
}
// Refresh if we're within the buffer time of expiration
const now = Date.now() / 1000;
return now >= this._tokenExpiresAt - this._refreshBufferSeconds;
}
/**
* Refresh the token if it's expired or close to expiring.
*/
async _refreshTokenIfNeeded() {
if (!this._needsRefresh()) {
return;
}
// If refresh is already in progress, wait for it
if (this._refreshPromise) {
await this._refreshPromise;
return;
}
// Start refresh
this._refreshPromise = (async () => {
try {
const tokenData = await this._tokenFetcher();
this._currentToken = tokenData.accessToken;
if (!this._currentToken) {
throw new Error("Token fetcher did not return 'accessToken'");
}
// Set expiration if provided
if (tokenData.expiresIn) {
this._tokenExpiresAt = Date.now() / 1000 + tokenData.expiresIn;
}
else {
// Token doesn't expire or expiration unknown
this._tokenExpiresAt = null;
}
}
finally {
this._refreshPromise = null;
}
})();
await this._refreshPromise;
}
/**
* Get OAuth headers, refreshing token if needed.
* Note: This is synchronous for now as the Rust implementation expects sync.
* In a real implementation, this would need to handle async properly.
* @returns Headers with Bearer token authorization.
* @throws If unable to fetch or refresh token.
*/
getHeaders() {
// For simplicity in this example, we assume the token is already fetched
// In a real implementation, this would need to handle the async nature properly
if (!this._currentToken && !this._refreshPromise) {
// Synchronously trigger refresh - this is a limitation of the current implementation
throw new Error("Token not initialized. Call refreshToken() first or use async initialization.");
}
if (!this._currentToken) {
throw new Error("Failed to obtain OAuth token");
}
return { authorization: `Bearer ${this._currentToken}` };
}
/**
* Manually refresh the token.
* Call this before using getHeaders() to ensure token is available.
*/
async refreshToken() {
this._currentToken = null; // Force refresh
await this._refreshTokenIfNeeded();
}
}
exports.OAuthHeaderProvider = OAuthHeaderProvider;