mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
openConnect() gives browser apps a pi-ai Models collection: credentials and model catalogs in IndexedDB (Web Locks serialize token refreshes across tabs), OAuth sign-in in pi.dev's popup, the token requests browsers cannot send routed through pi.dev's relay, and support(), which says for every provider and method whether it works in the browser and why not. Auth documents (metadata only, bundled and refreshed from pi.dev) describe the browser tiers; scripts/probe-cors.ts checks them against the providers' endpoints. The relay allowlist in relay.ts is the source pi.dev vendors.
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
/// <reference lib="dom" />
|
|
|
|
// The IndexedDB database where pi-connect keeps credentials, model catalogs and auth documents.
|
|
|
|
export type StoreName = "credentials" | "models" | "documents";
|
|
|
|
const STORES: readonly StoreName[] = ["credentials", "models", "documents"];
|
|
|
|
const DATABASE_VERSION = 1;
|
|
|
|
/** One open pi-connect database: key-value stores of structured-cloned values. */
|
|
export class ConnectDatabase {
|
|
private readonly db: IDBDatabase;
|
|
|
|
private constructor(db: IDBDatabase) {
|
|
this.db = db;
|
|
}
|
|
|
|
/** Open (and on first use create) the database `name`. */
|
|
static open(name: string, factory: IDBFactory = indexedDB): Promise<ConnectDatabase> {
|
|
const request = factory.open(name, DATABASE_VERSION);
|
|
request.onupgradeneeded = () => {
|
|
for (const store of STORES) {
|
|
if (!request.result.objectStoreNames.contains(store)) request.result.createObjectStore(store);
|
|
}
|
|
};
|
|
return settle(request).then((db) => new ConnectDatabase(db));
|
|
}
|
|
|
|
/** Read one value. */
|
|
get(store: StoreName, key: string): Promise<unknown> {
|
|
return settle(this.db.transaction(store, "readonly").objectStore(store).get(key));
|
|
}
|
|
|
|
/** List the keys of a store. */
|
|
async keys(store: StoreName): Promise<string[]> {
|
|
const keys = await settle(this.db.transaction(store, "readonly").objectStore(store).getAllKeys());
|
|
return keys.map(String);
|
|
}
|
|
|
|
/** Write one value; resolves once the write is committed. */
|
|
put(store: StoreName, key: string, value: unknown): Promise<void> {
|
|
return this.write(store, (objects) => objects.put(value, key));
|
|
}
|
|
|
|
/** Delete one value; resolves once the deletion is committed. */
|
|
delete(store: StoreName, key: string): Promise<void> {
|
|
return this.write(store, (objects) => objects.delete(key));
|
|
}
|
|
|
|
/** Close the connection. */
|
|
close(): void {
|
|
this.db.close();
|
|
}
|
|
|
|
private write(store: StoreName, change: (objects: IDBObjectStore) => void): Promise<void> {
|
|
const transaction = this.db.transaction(store, "readwrite");
|
|
change(transaction.objectStore(store));
|
|
return new Promise((resolve, reject) => {
|
|
transaction.oncomplete = () => resolve();
|
|
transaction.onerror = () => reject(transaction.error);
|
|
transaction.onabort = () => reject(transaction.error ?? new Error("IndexedDB transaction aborted"));
|
|
});
|
|
}
|
|
}
|
|
|
|
function settle<T>(request: IDBRequest<T>): Promise<T> {
|
|
return new Promise((resolve, reject) => {
|
|
request.onsuccess = () => resolve(request.result);
|
|
request.onerror = () => reject(request.error);
|
|
});
|
|
}
|