@the_library/public
v1.0.0
Published
Consolidated public libraries and Web3 SDK for DataPond and World Library
Readme
@the_library/public — Complete API Documentation
This document provides exhaustive API reference for all exports from @the_library/public and its subpaths. It is organized by subpath/module and includes every function, type, interface, and composable with full parameter and return type documentation.
Table of Contents
1. Downloader API (@the_library/public/downloader)
The Downloader manages parallelized, cross-worker PDF and image fetching, decryption, decompression, and rendering using Web Workers and IndexedDB/Cache API storage backends.
Initialization
InitDownloaderV2(options?: InitOptions): Promise<void>
Single-call initialization of the download orchestrator. Must be called once at app boot before calling any file-fetch functions. Idempotent — subsequent calls are no-ops.
import { InitDownloaderV2 } from '@the_library/public/downloader';
await InitDownloaderV2({
gateways: ['https://arweave.net', 'https://arweave.dev'],
requestPersistence: true,
concurrency: { pdf: 3, preview: 6 },
mupdfUrl: '/js/mupdf.js',
});Parameters:
options.gateways?: string[]— Array of Arweave gateway URLs to try in sequence. Defaults to['https://arweave.net'].options.requestPersistence?: boolean— Iftrue, asks the browser for persistent storage vianavigator.storage.persist(), which may prevent auto-eviction of cached files. Defaults tofalse.options.concurrency?: { pdf: number; preview: number }— Limits simultaneous downloads:pdfcaps concurrent PDF fetches,previewcaps concurrent page-image renders. Defaults to{ pdf: 3, preview: 6 }.options.mupdfUrl?: string— URL tomupdf.js(WASM runtime). Must be adjacent tomupdf-wasm.jsandmupdf-wasm.wasmin the app'spublic/directory. Defaults to'/js/mupdf.js'.
Returns: Promise<void> — resolves once worker threads are initialized.
Throws:
- If
InitDownloaderV2is called simultaneously from multiple call sites, the first promise is returned (idempotency guard). - Worker thread setup errors (e.g., broken WASM URL) surface on the returned promise.
preloadWorkersV2(): void
Eagerly constructs worker threads so the worker JS bundles download in the background. Call as early as possible (e.g., during app shell initialization before the app mounts components).
import { preloadWorkersV2 } from '@the_library/public/downloader';
// Call immediately when app boots, before any heavy imports:
preloadWorkersV2();Parameters: None
Returns: void (synchronous; starts background downloads, doesn't wait for them)
File Operations
GetPdf(txId: string, options?: { priority?: 'low' | 'high' }): Promise<Response>
Fetches, decrypts, and decompresses a PDF by Arweave TX ID. Returns a Response object wrapping the decrypted bytes.
import { GetPdf, GetObjectUrl } from '@the_library/public/downloader';
const response = await GetPdf(txId);
const blob = await response.blob();
const objectUrl = await GetObjectUrl(response); // for <iframe> or <embed>Parameters:
txId: string— Arweave TX ID identifying the encrypted PDF.options.priority?: 'low' | 'high'— Queue priority. Defaults to'low'. Use'high'for user-initiated "open book" actions to bump ahead of background prefetches.
Returns: Promise<Response> — HTTP Response object (status 200) wrapping the decrypted PDF bytes. Use .blob(), .arrayBuffer(), or .text() to read the body.
Throws:
- If all gateways are unreachable or return 404/500, rejects with a
NetworkErrororNotFoundError. - If decryption fails, rejects with a
DecryptionError.
Caching: The result is cached by txId in the browser's Cache API (under CACHES.pdf). Subsequent calls return the cached response synchronously.
GetImage(txId: string, pageOverride?: number): Promise<Response>
Renders a page preview image (JPEG/PNG) via the mupdf worker. By default, renders the first page of a PDF. Returns a Response wrapping the image bytes.
import { GetImage } from '@the_library/public/downloader';
// Get the cover image (first page)
const coverResponse = await GetImage(txId);
const imageBlob = await coverResponse.blob();
const imgSrc = URL.createObjectURL(imageBlob);
// Get a specific page
const page5Image = await GetImage(txId, 5);Parameters:
txId: string— Arweave TX ID of the PDF.pageOverride?: number— 1-indexed page number. Defaults to1(first page).
Returns: Promise<Response> — HTTP Response object (status 200) wrapping JPEG/PNG bytes.
Throws:
- Same as
GetPdf(gateway errors, decryption failures). - If
pageOverrideis out of range (e.g., page 1000 of a 10-page PDF), rejects withRangeError.
Caching: Rendered images are cached in CACHES.preview under the key {txId}_page{n}. Cached preview images persist across sessions.
EnsurePdf(txId: string): Promise<void>
Pre-downloads and caches a PDF without needing the Response object back. Useful for background prefetching or "warm up" operations.
import { EnsurePdf } from '@the_library/public/downloader';
// Background prefetch (fire and forget)
EnsurePdf(txId).catch(e => console.warn('Prefetch failed:', e));
// Wait for it
await EnsurePdf(txId);Parameters:
txId: string— Arweave TX ID of the PDF.
Returns: Promise<void> — resolves once the PDF is cached.
Throws: Same as GetPdf.
EnsurePageImage(txId: string, page: number): Promise<void>
Pre-renders and caches a specific page image without returning the bytes. Used to warm up page-image cache before the user scrolls to that page.
import { EnsurePageImage } from '@the_library/public/downloader';
// Render pages 2-5 in the background while user reads page 1
for (let i = 2; i <= 5; i++) {
EnsurePageImage(txId, i).catch(e => console.warn(`Page ${i} prefetch failed`));
}Parameters:
txId: string— Arweave TX ID.page: number— 1-indexed page number.
Returns: Promise<void> — resolves once the image is cached.
Throws: Same as GetImage.
DownloadPdfToDisk(txId: string, filename?: string): Promise<void>
Triggers a browser "Save As" dialog and writes the decrypted PDF to the user's local Downloads folder.
import { DownloadPdfToDisk } from '@the_library/public/downloader';
await DownloadPdfToDisk(txId, 'My Book Title.pdf');Parameters:
txId: string— Arweave TX ID.filename?: string— Suggested filename for the browser's save dialog. If omitted, the browser uses a default name.
Returns: Promise<void> — resolves once the download is initiated (does not wait for the user to confirm the save dialog).
Throws:
- Same as
GetPdf(fetch/decrypt errors). - If the browser blocks the download (e.g., security policy), the promise rejects with a
SecurityError.
GetObjectUrl(response: Response): Promise<string>
Converts a GetPdf or GetImage response to a blob URL suitable for <iframe src="">, <embed src="">, or <img src="">.
import { GetPdf, GetObjectUrl } from '@the_library/public/downloader';
const pdfResponse = await GetPdf(txId);
const blobUrl = await GetObjectUrl(pdfResponse);
// Now use: <iframe :src="blobUrl" />Parameters:
response: Response— AResponseobject returned byGetPdforGetImage.
Returns: Promise<string> — A blob: URL (e.g., blob:https://example.com/...).
revokeObjectUrl(blobUrl: string): void
Explicitly revokes a blob URL created by GetObjectUrl to release memory.
const blobUrl = await GetObjectUrl(response);
// ... later ...
revokeObjectUrl(blobUrl);Parameters:
blobUrl: string— The blob URL to revoke.
Returns: void
Note: Blob URLs are automatically revoked when the page navigates away or the tab closes. Manual revocation is only necessary to free memory during a long-running session (e.g., a single-page app that opens/closes many books).
storageEstimate(): Promise<{ usage: number; quota: number }>
Queries the browser's storage quota and current usage.
import { storageEstimate } from '@the_library/public/downloader';
const { usage, quota } = await storageEstimate();
const percentUsed = (usage / quota) * 100;
console.log(`${percentUsed.toFixed(1)}% of storage in use`);Parameters: None
Returns: Promise<{ usage: number; quota: number }> — usage is bytes currently used by Cache API + IndexedDB, quota is the browser's total storage quota in bytes.
Metadata & Storage
getBookData(txId: string): Promise<BookData | undefined>
Reads cached book metadata (title, page count, etc.) from IndexedDB.
import { getBookData } from '@the_library/public/downloader';
const bookData = await getBookData(txId);
if (bookData) {
console.log(`${bookData.title} has ${bookData.pages} pages`);
}Parameters:
txId: string— Arweave TX ID.
Returns: Promise<BookData | undefined> — Book metadata if cached, undefined if no metadata has been hydrated yet.
Type: BookData
interface BookData {
txId: string;
title: string;
pages: number;
// ... other fields set by the mupdf worker
}getAllBookData(): Promise<BookData[]>
Retrieves all cached book metadata records from IndexedDB.
import { getAllBookData } from '@the_library/public/downloader';
const allBooks = await getAllBookData();
console.log(`${allBooks.length} books cached`);Parameters: None
Returns: Promise<BookData[]> — Array of all cached BookData records. Empty array if no books have been hydrated yet.
getDownloadRecord(txId: string): Promise<DownloadRecord | undefined>
Reads the download progress record for a TX ID.
import { getDownloadRecord } from '@the_library/public/downloader';
const record = await getDownloadRecord(txId);
if (record) {
console.log(`${record.loaded}/${record.total} bytes downloaded`);
}Parameters:
txId: string— Arweave TX ID.
Returns: Promise<DownloadRecord | undefined>
Type: DownloadRecord
interface DownloadRecord {
txId: string;
loaded: number; // bytes downloaded so far
total: number; // total file size in bytes
timestamp: number; // last update time (ms since epoch)
}getAllDownloadRecords(): Promise<DownloadRecord[]>
Retrieves all download progress records.
import { getAllDownloadRecords } from '@the_library/public/downloader';
const records = await getAllDownloadRecords();
console.log(`${records.length} active/recent downloads`);Parameters: None
Returns: Promise<DownloadRecord[]> — Array of all cached progress records.
Cache Management
downloadState
The canonical, synchronous, framework-agnostic store for all file state. Every framework adapter (vue, react, solid) is a thin reactive wrapper around this store.
import { downloadState } from '@the_library/public/downloader';
// Subscribe to changes
const unsubscribe = downloadState.subscribe((changedTxId) => {
console.log(`File state changed for ${changedTxId}`);
});
// Read current state
const fileState = downloadState.get(txId);
if (fileState?.phase === 'done') {
console.log('Download complete');
}
// Read render state (page images, book metadata)
const renderState = downloadState.getRender(txId);
console.log(`${renderState.pages.size} page images cached`);
// Global summary
const summary = downloadState.summary();
console.log(`${summary.downloading} active downloads`);Methods:
downloadState.get(txId: string): FileState | undefined— Returns current state of one file.downloadState.getRender(txId: string): RenderState | undefined— Returns render/page state for one file.downloadState.summary(): DownloadSummary— Returns global aggregate (total files, total bytes, etc.).downloadState.subscribe(callback: (txId: string) => void): () => void— Registers a listener. Returns unsubscribe function.
Type: FileState
interface FileState {
phase: 'unknown' | 'queued' | 'downloading' | 'done' | 'error';
loaded: number;
total: number;
error?: string;
progress?: number; // 0-1
}Type: RenderState
interface RenderState {
bookData?: BookData;
pages: Map<number, FilePhase>; // page number -> 'done' | 'error' | ...
}deletePdf(txId: string): Promise<boolean>
Removes a cached PDF from Cache API storage.
import { deletePdf } from '@the_library/public/downloader';
const deleted = await deletePdf(txId);
console.log(deleted ? 'Deleted' : 'Not found in cache');Parameters:
txId: string— Arweave TX ID.
Returns: Promise<boolean> — true if deletion succeeded, false if the PDF was not cached.
deletePreview(txId: string): Promise<boolean>
Removes all cached page preview images for a TX ID.
import { deletePreview } from '@the_library/public/downloader';
await deletePreview(txId);Parameters:
txId: string— Arweave TX ID.
Returns: Promise<boolean> — true if deletion succeeded.
deleteDownloadRecord(txId: string): Promise<void>
Removes the download progress record from IndexedDB.
import { deleteDownloadRecord } from '@the_library/public/downloader';
await deleteDownloadRecord(txId);Parameters:
txId: string— Arweave TX ID.
Returns: Promise<void>
Cache Store API
Raw cache and IndexedDB access (rarely called directly; use the higher-level APIs above when possible).
openPdfCache(): Promise<Cache>
Gets the Cache API store for PDFs.
const cache = await openPdfCache();
const keys = await cache.keys();openPreviewCache(): Promise<Cache>
Gets the Cache API store for preview images.
openPagesCache(): Promise<Cache>
Gets the Cache API store for individual page images.
matchPdf(txId: string): Promise<Response | undefined>
Low-level cache lookup for a single PDF.
matchPreview(txId: string): Promise<Response | undefined>
Low-level cache lookup for a cover/first-page image.
matchPage(txId: string, pageNumber: number): Promise<Response | undefined>
Low-level cache lookup for a specific page image.
putPdf(txId: string, response: Response): Promise<void>
Low-level cache write for a PDF.
putPreview(txId: string, response: Response): Promise<void>
Low-level cache write for a cover image.
putPage(txId: string, pageNumber: number, response: Response): Promise<void>
Low-level cache write for a page image.
pdfKeys(): Promise<string[]>
Lists all cached PDF keys.
previewKeys(): Promise<string[]>
Lists all cached preview-image keys.
pageKeys(): Promise<string[]>
Lists all cached page-image keys.
Keys & Naming
pdfKey(txId: string): string
Generates the cache key for a PDF.
const key = pdfKey(txId); // e.g., "arweave_pdf_{txId}"previewKey(txId: string): string
Generates the cache key for a cover image.
pageKey(txId: string, pageNumber: number): string
Generates the cache key for a specific page.
const key = pageKey(txId, 5); // e.g., "arweave_page_5_{txId}"CACHES (constant)
import { CACHES } from '@the_library/public/downloader';
// CACHES = {
// pdf: 'dl2-pdf-cache-v1',
// preview: 'dl2-preview-cache-v1',
// pages: 'dl2-pages-cache-v1'
// }CACHE_VERSION (constant)
import { CACHE_VERSION } from '@the_library/public/downloader';
// CACHE_VERSION = 1DL2_SW_VERSION (constant)
Service worker build version. Used internally to detect when the SW script has been updated.
Framework Adapters (/downloader/{vue,react,solid})
Vue Composables
import {
useFileStatus,
useRenderStatus,
useGlobalProgress,
usePruning
} from '@the_library/public/downloader/vue';
// Reactive ref for one file's download state
const status = useFileStatus(txId); // ComputedRef<FileState | undefined>
// Reactive ref for render/page state
const render = useRenderStatus(txId); // ComputedRef<RenderState | undefined>
// Global download progress
const progress = useGlobalProgress(); // ComputedRef<DownloadSummary>
// LRU pruning activity
const pruning = usePruning(); // ComputedRef<PruningState>React Hooks
import {
useFileStatus,
useRenderStatus,
useGlobalProgress,
usePruning
} from '@the_library/public/downloader/react';
// Synchronized external store hooks
const status = useFileStatus(txId); // FileState | undefined
const render = useRenderStatus(txId); // RenderState | undefined
const progress = useGlobalProgress(); // DownloadSummary
const pruning = usePruning(); // PruningStateSolid.js Primitives
import {
useFileStatus,
useRenderStatus,
useGlobalProgress,
usePruning
} from '@the_library/public/downloader/solid';
// Solid createMemo/createSignal-based reactivity
const [status] = useFileStatus(txId);
const [render] = useRenderStatus(txId);
const [progress] = useGlobalProgress();
const [pruning] = usePruning();Service Worker API (@the_library/public/downloader/sw)
Installs a service worker that intercepts PDF/image requests, serving cached responses offline and persisting downloads across tab reloads.
registerDownloaderSW(): Promise<ServiceWorkerState>
Registers (or reuses) the downloader service worker.
import { registerDownloaderSW } from '@the_library/public/downloader/sw';
const state = await registerDownloaderSW();
if (state.active) {
console.log('Service worker active and controlling pages');
}Parameters: None
Returns: Promise<ServiceWorkerState>
Type: ServiceWorkerState
interface ServiceWorkerState {
registered: boolean; // SW is registered
active: boolean; // SW is controlling this page
waiting: boolean; // Update pending
installing: boolean; // New registration in progress
controller: ServiceWorkerContainer | null;
}swStatus(): Promise<ServiceWorkerState>
Checks current service worker status without re-registering.
import { swStatus } from '@the_library/public/downloader/sw';
const state = await swStatus();Parameters: None
Returns: Promise<ServiceWorkerState>
clearDownloaderCaches(): Promise<void>
Drops all Cache API entries (PDFs, page images, previews), but keeps IndexedDB metadata records intact.
import { clearDownloaderCaches } from '@the_library/public/downloader/sw';
await clearDownloaderCaches();
console.log('Cache storage cleared, IndexedDB preserved');Parameters: None
Returns: Promise<void>
nukeDownloaderStorage(): Promise<void>
Full reset: deletes everything — Cache API entries, IndexedDB records, service worker registration. Use when the user logs out or to recover storage quota.
import { nukeDownloaderStorage } from '@the_library/public/downloader/sw';
await nukeDownloaderStorage();
console.log('All download data erased');Parameters: None
Returns: Promise<void>
unregisterDownloaderSW(): Promise<void>
Unregisters the service worker.
import { unregisterDownloaderSW } from '@the_library/public/downloader/sw';
await unregisterDownloaderSW();Parameters: None
Returns: Promise<void>
2. Models API (@the_library/public/models)
The Models API provides the in-memory ORM, record cache, patch/replay engine, and schema registry for Sanctuary's data model. The root subpath exports only the framework-agnostic core; session bootstrap and reactive views are framework-specific and live under /models/{vue,react,solid,astro}.
Core Types & Registry
Registry (singleton, exported from registry.ts)
Holds enriched ModelDefinition for every registered object type — property IDs, field schemas, and relations.
import { Registry, type ModelDefinition } from '@the_library/public/models';
// Get schema by numeric type ID
const bookSchema: ModelDefinition = Registry.get(Book.type);
// bookSchema.name, bookSchema.typePrefix, bookSchema.props, bookSchema.instanceFields, ...
// Get schema by class name
const byName = Registry.getByName('Book');Methods:
Registry.get(typeId: number): ModelDefinition— Returns the enriched schema for a type. Throws iftypeIdis not registered.Registry.getByName(name: string): ModelDefinition— Returns the schema for a named type (e.g., 'Book'). Throws if no type with that name is registered.Registry.all(): ModelDefinition[]— Returns all registered type schemas.
Type: ModelDefinition
interface ModelDefinition {
name: string; // e.g., 'Book'
type: number; // numeric type ID
typePrefix: string; // e.g., 'B' for Book
props: PropertyDefinition[]; // field schemas
instanceFields: InstanceFieldInfo[]; // relation/reference fields
relations: RelationDefinition[];
}Record Cache & Reactivity
configureDb(options?: { cacheLimit?: number }): void
Sets the in-memory LRU cache size. Call before loading any records.
import { configureDb } from '@the_library/public/models';
// Lower cache limit for mobile devices
configureDb({ cacheLimit: 500 });Parameters:
options.cacheLimit?: number— Maximum number of records to hold in memory. Default is2000. Older records are evicted when the limit is exceeded.
Returns: void
getRecord(typeId: number, id: string): any
Synchronously retrieves a record from the cache.
import { getRecord } from '@the_library/public/models';
import { Book } from '@the_library/public/models';
if (hasRecord(Book.type, bookId)) {
const book = getRecord(Book.type, bookId);
console.log(book.title);
}Parameters:
typeId: number— The model type ID (e.g.,Book.type).id: string— The record UID.
Returns: The record object (e.g., a Book instance) if cached.
Throws: If the record is not in cache (LRU eviction or never loaded). Use hasRecord() to check first, or call the model's LoadAsync() method to load from disk.
addRecord(typeId: number, id: string, record: any): void
Adds or updates a record in the cache.
import { addRecord } from '@the_library/public/models';
addRecord(Book.type, bookId, bookInstance);Parameters:
typeId: number— Model type ID.id: string— Record UID.record: any— The record object.
Returns: void
removeRecord(typeId: number, id: string): void
Removes a record from the cache.
import { removeRecord } from '@the_library/public/models';
removeRecord(Book.type, bookId);Parameters:
typeId: number— Model type ID.id: string— Record UID.
Returns: void
hasRecord(typeId: number, id: string): boolean
Checks if a record is currently cached.
import { hasRecord } from '@the_library/public/models';
if (hasRecord(Book.type, bookId)) {
// safe to call getRecord()
}Parameters:
typeId: number— Model type ID.id: string— Record UID.
Returns: boolean
pinRecord(typeId: number, id: string): void
Pins a record so it won't be evicted from the LRU cache. Use for the currently-open record, search results, etc.
import { pinRecord } from '@the_library/public/models';
// User opened a book — don't evict it while they're reading
pinRecord(Book.type, openBookId);Parameters:
typeId: number— Model type ID.id: string— Record UID.
Returns: void
unpinRecord(typeId: number, id: string): void
Unpins a record so it can be evicted again.
import { unpinRecord } from '@the_library/public/models';
// User closed the book
unpinRecord(Book.type, openBookId);Parameters:
typeId: number— Model type ID.id: string— Record UID.
Returns: void
addReactivitySystem(system: ReactivitySystem): void
Installs a framework-agnostic reactivity hook that fires when records change. Framework adapters call this once at boot.
import { addReactivitySystem } from '@the_library/public/models';
addReactivitySystem({
track: (typeId, id) => { /* called when record added/changed */ },
trigger: (typeId, id) => { /* called when record needs re-render */ },
});setReactivitySystem(system: ReactivitySystem): void
Replaces the current reactivity system. Typically called by framework adapters during initialization.
Generated Models
Generated model classes (e.g., Book, Person, Tag) are built from your schema via pnpm generate and exported from both the root (@the_library/public/models) and the dedicated /models/models subpath.
import { Book, Person, NewBookWithId, ModelTypes } from '@the_library/public/models';
// equivalently: from '@the_library/public/models/models'
// Create a new record in memory (not yet persisted)
const book = NewBookWithId('book:abc123', 'username');
book.title = 'A Study in Scarlet';
book.author = 'Sir Arthur Conan Doyle';
// Load an existing record from disk
const loaded = await Book.LoadAsync('book:abc123');
// Check type at runtime
if (loaded instanceof Book) {
console.log('It is a book');
}
// Generic record hydration (type unknown at compile time)
const typeId = 10; // some numeric type
const RecordClass = ModelTypes[typeId];
const generic = new RecordClass();Key Functions:
NewBookWithId(id: string, creatorUsername: string): Book— Factory to create a new Book record in memory.Book.LoadAsync(id: string): Promise<Book>— Loads a Book by UID from IndexedDB.ModelTypes: { [typeId: number]: typeof ModelClass }— Maps numeric type IDs to model classes.
Schema Registry
The Registry singleton (above) holds ModelDefinition for each type. Accessed via Registry.get(typeId) or Registry.getByName(name).
Trash Index
A global, boot-owned index of which record ids (any object type) are currently deleted or flagged !safe — bucketed by the creatorUsername namespace the record belongs to. Fully automatic: there is nothing to register or feed it.
import { trashStore, scanTrash, type TrashStore } from '@the_library/public/models';How it populates:
- At boot —
scanTrash()runs a full pass over every currently-hydrated record right afterhydrateAll()(both the initialbootModelsV2()call and everyloadLanguage()call), deriving membership directly from each record's livedeleted/safestate. No per-type registration needed —deleted/safeare universal base properties present on every model. - Live — registered internally as a
ReactivitySystem(seeaddReactivitySystem, above).Orm.patch()fires this unconditionally for every mutation source — a local edit,restoreAccountSave()'s cross-device replay, and subscription-graph sync alike — so the index stays current automatically as patches land from anywhere, for any username. (An item lands in the trash by having itsdeleted/safeproperty patched — the same op every property edit uses — so this module never creates patches, it only mirrors them.) - Instant paint — a localStorage snapshot seeds the in-memory state synchronously at import time, so
useTrash()reads a cached value immediately instead of empty untilbootModelsV2()resolves;scanTrash()then reconciles it against the live corpus once boot completes.
trashStore (singleton)
import { Tag } from '@the_library/public/models/models';
import { trashStore } from '@the_library/public/models';
// All ids currently trashed for Tag, merged across every bucketed username
const allTrashedTagIds = trashStore.list(Tag.type);
// Merge only specific usernames' buckets
const mineOnly = trashStore.list(Tag.type, ['mine']);
const count = trashStore.count(Tag.type);
const buckets = trashStore.usernames(Tag.type); // e.g. ['canonical']
const unsubscribe = trashStore.subscribe(Tag.type, () => {
console.log('Tag trash changed:', trashStore.count(Tag.type));
});Methods:
trashStore.list(objectType: number, usernames?: string[]): number[]— Merges trashed ids acrossusernames. Empty array (default) merges every bucketed username for that type.trashStore.count(objectType: number, usernames?: string[]): number— Same merge semantics aslist(); returns the count.trashStore.usernames(objectType: number): string[]— Usernames currently bucketed for a type (e.g. to populate a filter picker).trashStore.subscribe(objectType: number, listener: () => void): () => void— Registers a listener, fired whenever that type's trash membership changes for any username. Returns an unsubscribe function.
Important — 'canonical' vs 'mine': Orm.creatorUsername resolves to the fixed string 'canonical' for any record id without the top bit set (isUserCreated() false) — which is essentially the entire shared catalog (Book, Tag, ...), regardless of who edited it or which sync path delivered the patch. Only records with a genuinely user-created id (top bit set) carry a specific creator's username. Filtering list()/count() to ['mine'] for catalog types will silently return nothing — call with no argument (or []) unless you specifically know the type is user-created content.
Type: TrashStore
interface TrashStore {
list(objectType: number, usernames?: string[]): number[];
count(objectType: number, usernames?: string[]): number;
usernames(objectType: number): string[];
subscribe(objectType: number, listener: () => void): () => void;
}scanTrash(): void
Full (re)scan — walks every currently-hydrated record and reconciles trash membership against its live deleted/safe state. Called automatically by session.ts's hydrateAll() (boot + every loadLanguage() call); not normally called directly.
Parameters: None
Returns: void
Framework Adapters (/models/{vue,react,solid,astro})
Vue Composables
import {
bootstrapModelsV2,
useLevel,
useDraft,
useTrash,
resetModelsV2Session
} from '@the_library/public/models/vue';bootstrapModelsV2(options: BootstrapOptions): Promise<void>
Initializes the models subsystem once at app boot. Idempotent across Astro <ClientRouter /> navigations.
await bootstrapModelsV2({
userLang: 'en',
models: [{ type: Book.type, create: NewBookWithId }],
localMode: '/corpus_v2', // OR registry: { dataBlock, gateway }
onBoot: (data) => console.log(`booted ${data.sidecars.length} sidecars`),
});Parameters:
userLang: string— User's preferred language (ISO 639-1 or custom code).models: ModelFactory[]— Array of{ type: number; create: (id, username) => ModelInstance }.localMode?: string— Folder path to serve corpus from (dev mode, mutually exclusive withregistry).registry?: { dataBlock: string; gateway: string }— Arweave data block & gateway (production mode).onBoot?: (data: BootData) => void— Callback fired when bootstrap completes.
Returns: Promise<void> — resolves once all sidecars are hydrated.
useLevel(username?: string): Ref<LevelData>
Reactive per-username level state and corpus metadata.
import { useLevel } from '@the_library/public/models/vue';
const { level, progress } = useLevel('alice').value;
console.log(`Alice's level: ${level}`);Parameters:
username?: string— Username. Defaults to the currently-logged-in user.
Returns: Ref<LevelData>
Type: LevelData
interface LevelData {
username: string;
level: number;
progress: number; // 0-1
lastUpdated: number; // timestamp
}useDraft(getter: () => ModelInstance): { draft, isDirty, commit, rollback }
Tracks edits to a model instance before committing them back to the original.
import { useDraft } from '@the_library/public/models/vue';
const { draft, isDirty, commit, rollback } = useDraft(() => book);
// Edit the draft
draft.value.title = 'New Title';
// Check if unsaved changes exist
if (isDirty.value) {
await commit(); // apply to original book
// or rollback() to discard
}Parameters:
getter: () => ModelInstance— A function returning the record to edit (usually a computed ref).
Returns:
{
draft: Ref<ModelInstance>; // editable proxy of the original
isDirty: Ref<boolean>; // true if any properties differ
commit: () => Promise<void>; // apply changes to original
rollback: () => void; // discard changes
}useTrash(objectType: number, usernames?: string[]): { count: Ref<number>; getIds: () => number[] }
Reactive view over the global Trash Index for one object type, merged across usernames. Population and live updates are entirely automatic — nothing to register, nothing to feed it.
import { useTrash } from '@the_library/public/models/vue';
import { Tag } from '@the_library/public/models/models';
const { count, getIds } = useTrash(Tag.type); // merges every username (canonical catalog trash)Parameters:
objectType: number— Model type ID (e.g.Tag.type).usernames?: string[]— Usernames to merge. Defaults to[](every known username — see the'canonical'vs'mine'note in Trash Index).
Returns:
{
count: Ref<number>;
getIds: () => number[];
}resetModelsV2Session(): void
Clears the session and cached level state. Used for logout and testing/HMR.
import { resetModelsV2Session } from '@the_library/public/models/vue';
resetModelsV2Session();React Hooks
Same API as Vue, but exported from @the_library/public/models/react:
import {
bootstrapModelsV2,
useLevel,
useModelUpdate,
useTrash,
resetModelsV2Session
} from '@the_library/public/models/react';useLevel(username?: string): LevelData
Hook returning the current level data. Re-renders when level changes.
useModelUpdate(record: ModelInstance): boolean
Subscribes to updates for a single record, triggering re-render on change. Returns true if subscription is active.
useTrash(objectType: number, usernames?: string[]): { count: number; getIds: () => number[] }
Re-renders the calling component whenever the global Trash Index changes for objectType, merged across usernames (defaults to [] — every known username). Population and live updates are entirely automatic.
import { useTrash } from '@the_library/public/models/react';
import { Tag } from '@the_library/public/models/models';
const { count, getIds } = useTrash(Tag.type);Note: React does not export useDraft (no fine-grained reactivity primitive like Vue's Ref). Use manual imperative edits or a state manager instead.
Solid.js Primitives
import {
bootstrapModelsV2,
useLevel,
subscribeRecord,
useTrash,
resetModelsV2Session
} from '@the_library/public/models/solid';useLevel(username?: string): () => LevelData
Signal factory returning the current level data.
subscribeRecord(record: ModelInstance, callback: (record) => void): () => void
Manual subscription to record updates. Returns unsubscribe function.
useTrash(objectType: number, usernames?: string[]): { count: () => number; getIds: () => number[] }
Signal accessor over the global Trash Index for one object type, merged across usernames (defaults to [] — every known username). Population and live updates are entirely automatic.
import { useTrash } from '@the_library/public/models/solid';
import { Tag } from '@the_library/public/models/models';
const { count, getIds } = useTrash(Tag.type);
return <span>{count()}</span>;Astro / Static Generation
import {
bootstrapModelsV2,
resetModelsV2Session,
subscribeRecord
} from '@the_library/public/models/astro';bootstrapModelsV2(options): Promise<void>
Same as Vue/React.
subscribeRecord(record, callback): () => void
Manual subscription (islands use this for client-side updates).
Note: Astro does not export useLevel, useDraft, or useTrash — use vanilla JS on the client (trashStore directly from @the_library/public/models works fine, it's framework-agnostic).
Patch & Session Management
Low-level APIs (not normally called directly; used by bootstrapModelsV2):
serializePatches(patches: Patch[]): Uint8Array
Encodes patches to binary for transmission or storage.
deserializePatches(data: Uint8Array): Patch[]
Decodes binary patches.
applyRestoredPatches(patches: Patch[], models: ModelFactory[]): void
Applies a set of deserialized patches to the in-memory records.
getPatchInstanceByAccount(username: string): Patches
Retrieves the patch store for a specific account.
3. Web3 API (@the_library/public/web3)
The Web3 API consolidates blockchain login, wallet connections, transaction mutations, session state, on-chain subscriptions, and contract/address resolution.
3.1 Session API
Manages wallet accounts, session persistence, and the centralized write-transaction pipeline.
sessionStore (singleton)
The canonical, framework-agnostic store for known and active wallet accounts.
import { sessionStore } from '@the_library/public/web3';
const { active, known } = sessionStore.getState();
console.log(`Active account: ${active?.account}`);
console.log(`${known.length} known accounts`);
// React to changes
const unsubscribe = sessionStore.subscribe(() => {
const newState = sessionStore.getState();
console.log('Session changed', newState);
});Methods:
sessionStore.getState(): SessionState— Returns current state:{ active: Web3SessionAccount | null; known: Web3SessionAccount[] }.sessionStore.subscribe(callback: () => void): () => void— Registers a listener, returns unsubscribe function.
Type: Web3SessionAccount
interface Web3SessionAccount {
account: string; // hex address
chainId: number;
wallet: WalletType; // 'metamask' | 'private_key'
username?: string;
userId?: number;
hasAccount: boolean; // registered on-chain
level?: number;
projectBalance?: {
contributions: number;
allocated: number;
remaining: number;
};
donationsPerProject?: number[];
metadata?: {
country?: string;
language?: string;
lastSyncTime?: number;
};
}syncAccount(address: string, chainId: number, walletType: WalletType): Promise<Web3SessionAccount>
Hydrates a wallet account after connection by pulling hasAccount, username, level, and other on-chain data. Also kicks off background subscription sync for the account's userId.
import { syncAccount } from '@the_library/public/web3';
const account = await syncAccount('0xabc...', 1116, 'metamask');
console.log(`${account.username} is level ${account.level}`);Parameters:
address: string— Wallet address (hex).chainId: number— Chain ID (1116, 1114, etc.).walletType: WalletType—'metamask'or'private_key'.
Returns: Promise<Web3SessionAccount> — The hydrated account record.
Side effects:
- Adds/updates the account in
sessionStore. - Persists the account to localStorage.
- Kicks off a background
subscriptionSyncfor the account'suserId.
useWeb3Session() (Vue composable)
Reactive wrapper over sessionStore.
import { useWeb3Session } from '@the_library/public/web3/vue';
const {
accounts, // Ref<Web3SessionAccount[]> — all known accounts
registeredAccounts, // Ref<Web3SessionAccount[]> — only hasAccount=true
activeAccount, // Ref<Web3SessionAccount | null>
isConnecting, // Ref<boolean>
setActive, // (account: Web3SessionAccount) => void
} = useWeb3Session();3.2 Client API
Typed read and write adapters over deployed contracts.
getReadClient(chainId: number, environment?: 'production' | 'staging'): ReadOnlyDomainAPI
Gets the read-only client for a chain. Blocks until contract addresses/ABIs are loaded from the registry.
import { getReadClient } from '@the_library/public/web3';
const client = getReadClient(1116);
await client.ready; // wait for registry load
const balance = await client.projectManagerStorage.balanceOfAddress('0xabc...');
const level = await client.archivistStorage.userLevel(userId);
const nbAccounts = await client.bouncerStorage.nbAccounts();Parameters:
chainId: number— EVM chain ID (1116, 1114, 84532, etc.).environment?: 'production' | 'staging'— Defaults to'production'. Use'staging'to read staging-deployed contract addresses.
Returns: ReadOnlyDomainAPI — Typed interface to all contract reads.
Type: ReadOnlyDomainAPI
interface ReadOnlyDomainAPI {
readonly chainId: number;
readonly ready: Promise<void>;
readonly bouncerStorage: BouncerStorageReadAPI;
readonly archivistStorage: ArchivistStorageReadAPI;
readonly scientistStorage: ScientistStorageReadAPI;
readonly projectManagerStorage: ProjectManagerStorageReadAPI;
readonly factory: FactoryReadAPI;
readonly addressRegistry: AddressRegistryReadAPI;
readonly subscriptionManager: SubscriptionManagerReadAPI;
readonly libraryRegistryStorage: LibraryRegistryStorageReadAPI;
readonly publicLibraryStorage: PublicLibraryStorageReadAPI;
getUsdPrice(): Promise<number>;
getRegistryStatus(): Promise<any>;
}Contract APIs
BouncerStorageReadAPI
Account identity and registration queries:
hasAccount(address: string): Promise<boolean>— Is the address registered?userInfos(address: string): Promise<[username, country, language, userId]>— Fetch user metadata by address.userData(userId: number): Promise<[id, addr, username, language, country]>— Fetch user metadata by ID.getUsernameByAddress(address: string): Promise<string>— Get username for an address.userIdOf(address: string): Promise<number>— Get user ID for an address.userIdToUsername(userId: number): Promise<string>— Get username for a user ID.usernameToUserId(username: string): Promise<number>— Get user ID for a username.userAddr(userId: number): Promise<string>— Get address for a user ID.nbAccounts(): Promise<number>— Total registered accounts on this chain.getAllLocationCounts(): Promise<any[]>— Aggregate account counts by location.getActiveLocationCount(): Promise<number>— Number of locations with at least one account.identityStats(): Promise<[nbVerbs, nbAdjectives]>— Stats on generated usernames.
ArchivistStorageReadAPI
User level and backup history:
userLevel(userId: number): Promise<number>— Reading level for a user.nbWrites(userId: number): Promise<number>— Total patches authored.loadActions(userId: number): Promise<[bigint[], bigint[], string[]]>— All patches for a user (raw format).loadActionsSince(userId: number, index: number): Promise<[bigint[], bigint[], string[]]>— Patches since a given index.
ScientistStorageReadAPI
Anonymized reading statistics:
getLastMonthId(): Promise<number>— Latest month in the stats index.getDeploymentDate(): Promise<[year, month]>— When the contract was deployed.getStatsRaw(action: number, objectType: number, monthId: number): Promise<[bigint[], number[]]>— Raw stats for a category/type/month.
ProjectManagerStorageReadAPI
Project discovery and donation voting:
nbProjects(): Promise<number>— Total projects on-chain.getProjectSupportedLanguages(projectId: number): Promise<string[]>— Languages available for a project.loadProject(projectId: number): Promise<Project>— Fetch full project metadata.balanceOfAddress(address: string): Promise<{ total, donations, unspent }>— Wallet's donation balance breakdown.addressDonationsPerProject(address: string): Promise<number[]>— Vote allocation by project (array indexed by projectId-1).projectVotingBalances(projectId: number): Promise<number>— Total votes allocated to a project.
FactoryReadAPI
Global corpus metadata:
load(): Promise<[bigint[], bigint[], string[]]>— All patches in the corpus (raw format).loadUsername(username: string): Promise<[bigint[], bigint[], string[]]>— Patches for one user.loadUsernameSince(username: string, index: number): Promise<[bigint[], bigint[], string[]]>— Incremental patch fetch.nbWritesByUsername(username: string): Promise<number>— Patch count for a user.
AddressRegistryReadAPI
Dynamic contract address/ABI resolution:
getLatestContract(name: string): Promise<[address, abiUrl]>— Latest address for a named contract.getContractHistory(name: string): Promise<AddressRegistryContractRecord[]>— All addresses ever deployed for a contract.getAllContractNames(): Promise<string[]>— List of all contracts.getLatestData(key: string): Promise<string>— Fetch arbitrary key-value data from the registry.
SubscriptionManagerReadAPI
Who-follows-whom graph:
getSubscriptions(userId: number): Promise<number[]>— List of user IDs that this user subscribes to.getSubscriptionCount(userId: number): Promise<bigint>— Total subscriptions for a user.isSubscribed(subscriber: number, target: number): Promise<boolean>— Issubscriberfollowingtarget?scoreOf(userId: number): Promise<bigint>— Social score (based on follower count, etc.).
LibraryRegistryStorageReadAPI
Library Collection and Registry metadata:
getUnverifiedCollectionIds(): Promise<string[]>— List of pending collection IDs.getVerifiedCollectionIds(): Promise<string[]>— List of verified collection IDs.getCollection(id: string): Promise<Collection>— Get full collection metadata by ID.
LibraryRegistryStorageWriteAPI
Library Collection and Registry mutations (Requires Admin Authorization):
addUnverifiedCollection(input: CollectionInput, options?: TransactionOptions): Promise<any>— Submits a new collection for review.verifyCollection(id: string, options?: TransactionOptions): Promise<any>— Approves a pending collection.rejectCollection(id: string, options?: TransactionOptions): Promise<any>— Rejects and removes a pending collection.updateCollectionStats(id: string, initialBookCount: number, dSafeBookCount: number, options?: TransactionOptions): Promise<any>— Updates statistics for a collection.setImportStatus(id: string, status: number, options?: TransactionOptions): Promise<any>— Updates the import status enum.setPublished(id: string, isPublished: boolean, options?: TransactionOptions): Promise<any>— Toggles publication status of a collection.setCollectionPublicLibrary(id: string, publicLibraryId: number, options?: TransactionOptions): Promise<any>— Links a collection to a public library.setPublicLibraryStorage(storageAddr: string, options?: TransactionOptions): Promise<any>— Sets the address of the PublicLibraryStorage contract.
PublicLibraryStorageReadAPI
Public Library cataloging metadata:
libraryExists(id: number): Promise<boolean>— Checks if a library ID exists.getPublicLibrary(id: number): Promise<PublicLibrary>— Get full public library metadata by ID.nbLibraries(): Promise<number>— Returns the total number of registered public libraries.
PublicLibraryStorageWriteAPI
Public Library management (Requires Admin Authorization):
addPublicLibrary(input: PublicLibraryInput, options?: TransactionOptions): Promise<any>— Creates a new public library record.updatePublicLibrary(id: number, input: PublicLibraryInput, options?: TransactionOptions): Promise<any>— Updates an existing public library record.
Write Operations
import { writeOperations, resetTransactionState } from '@the_library/public/web3';
await writeOperations.register(walletType, chainId, address, registrationFee);
await writeOperations.vote(walletType, chainId, address, projectId, language, voteAmount);
await writeOperations.save(walletType, chainId, address, compressedActions, textIndexes, textData, level);
await writeOperations.subscribe(walletType, chainId, address, subscriberUserId, targetUserId);
await writeOperations.unsubscribe(walletType, chainId, address, subscriberUserId, targetUserId);All write operations:
- Acquire a 1-minute run-lock (only one transaction per address/chain at a time).
- Emit
WalletEventsstate notifications. - Automatically refresh the account via
syncAccount()on success.
If a transaction throws and the run-lock needs to be cleared manually, call resetTransactionState().
Chain Metadata
import { getChain, EVM_CHAINS, DEFAULT_CHAIN_ID } from '@the_library/public/web3';
const chain = getChain(1116);
// chain.name, chain.symbol, chain.shortName, chain.logo, chain.explorer,
// chain.rpc, chain.main (true for mainnet), chain.deployed, chain.stagingDeployed
const deployedChains = Object.values(EVM_CHAINS).filter(c => c.deployed);Type: EvmChainConfig
interface EvmChainConfig {
name: string; // 'CORE' | 'tCORE2' | 'Base Sepolia'
shortName: string; // 'core' | 'tcore2' | ...
chainId: number;
symbol: string; // 'CORE' | 'CORETST' | ...
logo: string; // URL to chain logo
explorer: string; // Block explorer URL
rpc: string[]; // RPC endpoints
main: boolean; // Mainnet vs testnet
deployed: boolean; // Contracts deployed on this chain
stagingDeployed?: boolean;
}Vue Composable: useChain(chainId)
import { useChain } from '@the_library/public/web3/vue';
const { chain, client, usdPrice, refreshPrice, isMainnet, chainRouteQuery } = useChain(1116);
// chain: EvmChainConfig
// client: ReadOnlyDomainAPI (ready to use)
// usdPrice: Ref<number> — live USD price polling
// refreshPrice: () => Promise<void>
// isMainnet: boolean
// chainRouteQuery: { chainId: '1116' } — useful for router links3.3 Registry API
Dynamic contract address and ABI resolution from the on-chain AddressRegistry.
addressLoader (singleton)
import { addressLoader, setRegistryBaseUrl } from '@the_library/public/web3';
// Set custom registry host (before anything else runs)
setRegistryBaseUrl('https://registry.world.bibliotech.com');
// Subscribe to per-contract load progress
const unsubscribe = addressLoader.subscribe((chainId, status) => {
console.log(`chain ${chainId}: ${status.loaded}/${status.total} contracts loaded`);
});
// Get resolved address/ABI for a contract
const addr = addressLoader.getAddress(chainId, 'Factory');
const abi = addressLoader.getAbi(chainId, 'Factory');Methods:
addressLoader.subscribe(callback: (chainId, LoadingStatus) => void): () => void— Listen for load progress.addressLoader.getAddress(chainId: number, contractName: string): string | undefined— Resolved contract address, orundefinedif not yet loaded.addressLoader.getAbi(chainId: number, contractName: string): any[] | undefined— Resolved ABI array, orundefinedif not yet loaded.addressLoader.loadManifest(environment: 'production' | 'staging'): Promise<Manifest>— Manually load the registry manifest (auto-called by clients).
setRegistryBaseUrl(url: string): void
Override the default registry host URL (normally https://registry.world.bibliotech.com). Call before any clients are instantiated.
3.4 Subscription API
Walks the on-chain subscription graph (who-follows-whom) and incrementally fetches patches.
getSubscriptionClient(chainId: number): SubscriptionClient
Gets a chain-scoped subscription client.
import { getSubscriptionClient } from '@the_library/public/web3';
const subs = getSubscriptionClient(1116);
// First sync: full BFS walk + patch fetch
const { patches, cursor, truncated } = await subs.sync(rootUserId);
// Later: incremental — only fetch new patches since cursor
const next = await subs.syncSince(cursor);
// Query the graph
const followees = await subs.getSubscriptions(userId);
const score = await subs.getScore(userId);Methods:
subs.sync(rootUserId: number): Promise<SyncResult>— Walk subscription graph starting from a user, fetch all patches.subs.syncSince(cursor: SyncCursor): Promise<SyncResult>— Incremental sync using a persisted cursor.subs.getSubscriptions(userId: number): Promise<number[]>— List of users this user subscribes to.subs.getScore(userId: number): Promise<number>— Social score for a user.
Type: SyncResult
interface SyncResult {
patches: Patch[];
cursor: SyncCursor; // Persist this to resume next sync
truncated: boolean; // true if result was truncated (more data available)
}cursorStore (IndexedDB persistence)
import { cursorStore } from '@the_library/public/web3';
const cursor = await cursorStore.get(userId);
const result = cursor
? await subs.syncSince(cursor)
: await subs.sync(userId);
await cursorStore.set(userId, result.cursor);Methods:
cursorStore.get(userId: number): Promise<SyncCursor | null>— Retrieve persisted cursor, ornullif never synced.cursorStore.set(userId: number, cursor: SyncCursor): Promise<void>— Save cursor for next sync.
Vue Composable: useSubscriptionSync(userId)
import { useSubscriptionSync } from '@the_library/public/web3/vue';
const { cursor, patches, isLoading, refresh } = useSubscriptionSync(userId);
// cursor: Ref<SyncCursor>
// patches: Ref<Patch[]>
// isLoading: Ref<boolean>
// refresh: () => Promise<void>3.5 Contracts API
Static ABI exports for contracts that don't use dynamic resolution.
import { addressRegistryAbi, pythAbi } from '@the_library/public/web3';
// Use with viem directly:
import { createPublicClient, http } from 'viem';
const client = createPublicClient({
chain: /*...*/,
transport: http()
});
const data = await client.readContract({
address: '0x...',
abi: addressRegistryAbi,
functionName: 'getLatestContract',
args: ['Factory'],
});3.6 Events API
Typed pub/sub bus for wallet and transaction state notifications.
WalletEvents (typed pub/sub)
import { WalletEvents, type WalletState, type WalletError } from '@the_library/public/web3';
const unsubWalletState = WalletEvents.onWalletStateEvent((e) => {
// e: { wallet, chainId, event: WalletState, functionName?, timestamp }
if (e.event === 'TRANSACTION_STARTED') showSpinner();
if (e.event === 'TRANSACTION_DONE') hideSpinner();
if (e.event === 'ACCOUNT_SYNCED') updateUI();
});
const unsubError = WalletEvents.onWalletError((e) => {
// e: { wallet, chainId, reason: WalletError, timestamp }
toast.error(e.reason);
});
const unsubSaveRestored = WalletEvents.onSaveRestored((e) => {
// e: { compressedActions, indexes, stringIndexes, timestamp }
// Patches from on-chain state that outran local edits
});
const unsubPatchesSynced = WalletEvents.onSubscriptionPatchesSynced((e) => {
// e: { rootUserId, patches: Patch[], timestamp }
// Subscription graph patches fetched in background
});
// Unsubscribe
unsubWalletState();
unsubError();
unsubSaveRestored();
unsubPatchesSynced();Enums:
WalletState — transaction lifecycle events:
'WALLET_CONNECTED''WALLET_DISCONNECTED''TRANSACTION_STARTED''TRANSACTION_DONE''TRANSACTION_FAILED''ACCOUNT_SYNCED''SUBSCRIPTION_SYNC_STARTED''SUBSCRIPTION_SYNC_DONE'
WalletError — error reasons:
'NETWORK_ERROR''TRANSACTION_REJECTED''INSUFFICIENT_BALANCE''UNKNOWN_ERROR'- etc. (see source for full list)
Low-level EventBus (rarely used)
import { getEventBus } from '@the_library/public/web3';
const bus = getEventBus();
const unsubscribe = bus.on('topic', (data) => { /* handle */ });
bus.emit('topic', data);
bus.once('topic', (data) => { /* one-time listener */ });
bus.clear('topic'); // remove all listeners3.7 Wallets API
Wallet connectors (MetaMask, PrivateKey) and metadata.
getConnector(type: WalletType): WalletConnector | undefined
Retrieves a registered wallet connector by type.
import { getConnector } from '@the_library/public/web3';
const metamask = getConnector('metamask');
if (metamask?.isAvailable()) {
const { address, chainId } = await metamask.connect();
}Parameters:
type: WalletType—'metamask'|'private_key'
Returns: The connector, or undefined if not registered (e.g., bootstrapWebSDK() hasn't run yet).
getRegisteredConnectors(): WalletConnector[]
Lists all registered connectors.
import { getRegisteredConnectors } from '@the_library/public/web3';
const connectors = getRegisteredConnectors();
// array of MetaMaskConnector, PrivateKeyConnector, etc.walletMeta (metadata for each wallet)
import { walletMeta } from '@the_library/public/web3';
const meta = walletMeta.metamask;
// {
// displayName: 'MetaMask',
// logoUrl: 'https://...',
// description: '...',
// website: 'https://metamask.io',
// isInstalled(): boolean,
// isInstallable(): boolean,
// getInstallUrl(): string,
// }
if (!meta.isInstalled() && meta.isInstallable()) {
window.open(meta.getInstallUrl(), '_blank');
}Private Key Storage
import { storePrivateKey, hasPrivateKey, hasAnyPrivateKey } from '@the_library/public/web3';
storePrivateKey(1116, '0x1234...'); // raw hex, no '0x' prefix required
if (hasPrivateKey(1116)) {
// PrivateKeyConnector can connect to this chain
}
if (hasAnyPrivateKey()) {
// At least one chain has a stored key
}Warning: Private keys are stored in plaintext localStorage. Only use for dev/test wallets, never for real user funds.
3.8 Projects API
Project discovery and voting.
loadProject(chainId: number, language: string, projectId: number, refresh?: boolean): Promise<ProjectLocale>
Loads a single project by ID with language fallback. Cached automatically.
import { loadProject } from '@the_library/public/web3';
const project = await loadProject(1116, 'eng', 5);
console.log(`${project.version.name} — ${project.balance} tokens`);
// Force re-fetch from chain, bypassing cache
const fresh = await loadProject(1116, 'eng', 5, true);Parameters:
chainId: number— Chain ID.language: string— Preferred language code (e.g., 'eng', 'deu', 'spa').projectId: number— On-chain project ID (1-indexed).refresh?: boolean— Iftrue, bypass cache and refetch from chain.
Returns: Promise<ProjectLocale>
Throws: If cache miss and chain unreachable.
loadProjects(chainId: number, language: string): Promise<Array<ProjectLocale | null>>
Loads all projects for a chain/language. Returns cached projects if chain is unreachable (with console warning).
import { loadProjects } from '@the_library/public/web3';
const projects = await loadProjects(1116, 'eng');
const validProjects = projects.filter(p => p !== null);Parameters:
chainId: number— Chain ID.language: string— Preferred language code.
Returns: Promise<Array<ProjectLocale | null>> — Array where null entries represent projects that failed to load.
projectLocale(project: Project, language: string, chainId: number): ProjectLocale
Helper to convert raw Project data to ProjectLocale with language selection and decimal normalization.
import { projectLocale } from '@the_library/public/web3';
const raw = await client.projectManagerStorage.loadProject(5);
const locale = projectLocale(raw, 'eng', 1116);Parameters:
project: Project— Raw contract data.language: string— Preferred language.chainId: number— For decimal normalization.
Returns: ProjectLocale
Vue Composable: useLoadProjects(chainId, language)
import { useLoadProjects } from '@the_library/public/web3/vue';
const chainId = ref(1116);
const language = ref('eng');
const { projects, isLoading, error, reload } = useLoadProjects(chainId, language);
// projects: Ref<Array<ProjectLocale | null>>
// isLoading: Ref<boolean>
// error: Ref<Error | null>
// reload: () => Promise<void>3.9 Chain & Network Configuration
EVM_CHAINS (constant)
import { EVM_CHAINS } from '@the_library/public/web3';
// EVM_CHAINS = {
// 1116: { name: 'CORE', ... },
// 1114: { name: 'tCORE2', ... },
// 84532: { name: 'Base Sepolia', ... },
// }DEFAULT_CHAIN_ID (constant)
The default chain for new users (usually production mainnet, e.g., 1116).
getChain(chainId: number): EvmChainConfig
Gets full metadata for a chain. Throws if chainId is not registered.
import { getChain } from '@the_library/public/web3';
const chain = getChain(1116);
console.log(`${chain.name} (${chain.symbol})`);3.10 Vue Composables (@the_library/public/web3/vue)
bootstrapWebSDK(options?: BootstrapOptions): Promise<BootstrapResult>
Single app-wide initialization: registers connectors, loads persisted session, hydrates accounts.
import { bootstrapWebSDK, BootstrapResultKey } from '@the_library/public/web3/vue';
const result = await bootstrapWebSDK({
withTest: true,
environment: 'production',
chainFilter: [1116, 1114], // optional: limit to these chains
});
await result.ready; // wait for account discovery
// Provide at app root
app.provide(BootstrapResultKey, result);Parameters:
options.withTest?: boolean— Include test chains (Base Sepolia).options.environment?: 'production' | 'staging'— Contract environment.options.chainFilter?: number[]— Optional: only bootstrap these chains.
Returns: Promise<BootstrapResult>
Type: BootstrapResult
interface BootstrapResult {
ready: Promise<void>;
chains: EvmChainConfig[];
installedConnectors: WalletConnector[];
installableConnectors: WalletConnector[];
primaryAccount: Web3SessionAccount | null;
}useBootstrap(): BootstrapResult
Retrieves the app-wide bootstrap result (must have been provided via app.provide(BootstrapResultKey, result)).
import { useBootstrap } from '@the_library/public/web3/vue';
const { chains, installedConnectors, primaryAccount } = useBootstrap();useWeb3Session()
Reactive wrapper over sessionStore.
import { useWeb3Session } from '@the_library/public/web3/vue';
const {
accounts, // Ref<Web3SessionAccount[]>
registeredAccounts, // Ref<Web3SessionAccount[]> filtered by hasAccou