📄 README.md
D-OPEN SOVEREIGN
1# @the_library/public — Complete API Documentation
2
3This 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.
4
5---
6
7## Table of Contents
8
91. [Downloader API](#1-downloader-api--the_libraryPublicdownloader)
10 - [Initialization](#initialization)
11 - [File Operations](#file-operations)
12 - [Metadata & Storage](#metadata--storage)
13 - [Cache Management](#cache-management)
14 - [Cache Store API](#cache-store-api)
15 - [Keys & Naming](#keys--naming)
16 - [Framework Adapters](#framework-adapters-downloaderframeworknames)
17 - [Service Worker API](#service-worker-api--the_libraryPublicdownloadersw)
18
192. [Models API](#2-models-api--the_libraryPublicmodels)
20 - [Core Types & Registry](#core-types--registry)
21 - [Record Cache & Reactivity](#record-cache--reactivity)
22 - [Generated Models](#generated-models)
23 - [Schema Registry](#schema-registry)
24 - [Trash Index](#trash-index)
25 - [Framework Adapters](#framework-adapters-modelsframeworknames)
26 - [Patch & Session Management](#patch--session-management)
27
283. [Web3 API](#3-web3-api--the_libraryPublicweb3)
29 - [Session API](#31-session-api)
30 - [Client API](#32-client-api)
31 - [Registry API](#33-registry-api)
32 - [Subscription API](#34-subscription-api)
33 - [Contracts API](#35-contracts-api)
34 - [Events API](#36-events-api)
35 - [Wallets API](#37-wallets-api)
36 - [Projects API](#38-projects-api)
37 - [Chain & Network Configuration](#39-chain--network-configuration)
38 - [Vue Composables](#310-vue-composables--the_libraryPublicweb3vue)
39
404. [Branding API](#4-branding-api--the_libraryPublicbranding)
41
42---
43
44## 1. Downloader API (`@the_library/public/downloader`)
45
46The Downloader manages parallelized, cross-worker PDF and image fetching, decryption, decompression, and rendering using Web Workers and IndexedDB/Cache API storage backends.
47
48### Initialization
49
50#### `InitDownloaderV2(options?: InitOptions): Promise<void>`
51
52Single-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.
53
54```typescript
55import { InitDownloaderV2 } from '@the_library/public/downloader';
56
57await InitDownloaderV2({
58 gateways: ['https://arweave.net', 'https://arweave.dev'],
59 requestPersistence: true,
60 concurrency: { pdf: 3, preview: 6 },
61 mupdfUrl: '/js/mupdf.js',
62});
63```
64
65**Parameters:**
66- `options.gateways?: string[]` — Array of Arweave gateway URLs to try in sequence. Defaults to `['https://arweave.net']`.
67- `options.requestPersistence?: boolean` — If `true`, asks the browser for persistent storage via `navigator.storage.persist()`, which may prevent auto-eviction of cached files. Defaults to `false`.
68- `options.concurrency?: { pdf: number; preview: number }` — Limits simultaneous downloads: `pdf` caps concurrent PDF fetches, `preview` caps concurrent page-image renders. Defaults to `{ pdf: 3, preview: 6 }`.
69- `options.mupdfUrl?: string` — URL to `mupdf.js` (WASM runtime). Must be adjacent to `mupdf-wasm.js` and `mupdf-wasm.wasm` in the app's `public/` directory. Defaults to `'/js/mupdf.js'`.
70
71**Returns:** `Promise<void>` — resolves once worker threads are initialized.
72
73**Throws:**
74- If `InitDownloaderV2` is called simultaneously from multiple call sites, the first promise is returned (idempotency guard).
75- Worker thread setup errors (e.g., broken WASM URL) surface on the returned promise.
76
77---
78
79#### `preloadWorkersV2(): void`
80
81Eagerly 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).
82
83```typescript
84import { preloadWorkersV2 } from '@the_library/public/downloader';
85
86// Call immediately when app boots, before any heavy imports:
87preloadWorkersV2();
88```
89
90**Parameters:** None
91
92**Returns:** `void` (synchronous; starts background downloads, doesn't wait for them)
93
94---
95
96### File Operations
97
98#### `GetPdf(txId: string, options?: { priority?: 'low' | 'high' }): Promise<Response>`
99
100Fetches, decrypts, and decompresses a PDF by Arweave TX ID. Returns a `Response` object wrapping the decrypted bytes.
101
102```typescript
103import { GetPdf, GetObjectUrl } from '@the_library/public/downloader';
104
105const response = await GetPdf(txId);
106const blob = await response.blob();
107const objectUrl = await GetObjectUrl(response); // for <iframe> or <embed>
108```
109
110**Parameters:**
111- `txId: string` — Arweave TX ID identifying the encrypted PDF.
112- `options.priority?: 'low' | 'high'` — Queue priority. Defaults to `'low'`. Use `'high'` for user-initiated "open book" actions to bump ahead of background prefetches.
113
114**Returns:** `Promise<Response>` — HTTP `Response` object (status 200) wrapping the decrypted PDF bytes. Use `.blob()`, `.arrayBuffer()`, or `.text()` to read the body.
115
116**Throws:**
117- If all gateways are unreachable or return 404/500, rejects with a `NetworkError` or `NotFoundError`.
118- If decryption fails, rejects with a `DecryptionError`.
119
120**Caching:** The result is cached by `txId` in the browser's Cache API (under `CACHES.pdf`). Subsequent calls return the cached response synchronously.
121
122---
123
124#### `GetImage(txId: string, pageOverride?: number): Promise<Response>`
125
126Renders 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.
127
128```typescript
129import { GetImage } from '@the_library/public/downloader';
130
131// Get the cover image (first page)
132const coverResponse = await GetImage(txId);
133const imageBlob = await coverResponse.blob();
134const imgSrc = URL.createObjectURL(imageBlob);
135
136// Get a specific page
137const page5Image = await GetImage(txId, 5);
138```
139
140**Parameters:**
141- `txId: string` — Arweave TX ID of the PDF.
142- `pageOverride?: number` — 1-indexed page number. Defaults to `1` (first page).
143
144**Returns:** `Promise<Response>` — HTTP `Response` object (status 200) wrapping JPEG/PNG bytes.
145
146**Throws:**
147- Same as `GetPdf` (gateway errors, decryption failures).
148- If `pageOverride` is out of range (e.g., page 1000 of a 10-page PDF), rejects with `RangeError`.
149
150**Caching:** Rendered images are cached in `CACHES.preview` under the key `{txId}_page{n}`. Cached preview images persist across sessions.
151
152---
153
154#### `EnsurePdf(txId: string): Promise<void>`
155
156Pre-downloads and caches a PDF without needing the `Response` object back. Useful for background prefetching or "warm up" operations.
157
158```typescript
159import { EnsurePdf } from '@the_library/public/downloader';
160
161// Background prefetch (fire and forget)
162EnsurePdf(txId).catch(e => console.warn('Prefetch failed:', e));
163
164// Wait for it
165await EnsurePdf(txId);
166```
167
168**Parameters:**
169- `txId: string` — Arweave TX ID of the PDF.
170
171**Returns:** `Promise<void>` — resolves once the PDF is cached.
172
173**Throws:** Same as `GetPdf`.
174
175---
176
177#### `EnsurePageImage(txId: string, page: number): Promise<void>`
178
179Pre-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.
180
181```typescript
182import { EnsurePageImage } from '@the_library/public/downloader';
183
184// Render pages 2-5 in the background while user reads page 1
185for (let i = 2; i <= 5; i++) {
186 EnsurePageImage(txId, i).catch(e => console.warn(`Page ${i} prefetch failed`));
187}
188```
189
190**Parameters:**
191- `txId: string` — Arweave TX ID.
192- `page: number` — 1-indexed page number.
193
194**Returns:** `Promise<void>` — resolves once the image is cached.
195
196**Throws:** Same as `GetImage`.
197
198---
199
200#### `DownloadPdfToDisk(txId: string, filename?: string): Promise<void>`
201
202Triggers a browser "Save As" dialog and writes the decrypted PDF to the user's local Downloads folder.
203
204```typescript
205import { DownloadPdfToDisk } from '@the_library/public/downloader';
206
207await DownloadPdfToDisk(txId, 'My Book Title.pdf');
208```
209
210**Parameters:**
211- `txId: string` — Arweave TX ID.
212- `filename?: string` — Suggested filename for the browser's save dialog. If omitted, the browser uses a default name.
213
214**Returns:** `Promise<void>` — resolves once the download is initiated (does not wait for the user to confirm the save dialog).
215
216**Throws:**
217- Same as `GetPdf` (fetch/decrypt errors).
218- If the browser blocks the download (e.g., security policy), the promise rejects with a `SecurityError`.
219
220---
221
222#### `GetObjectUrl(response: Response): Promise<string>`
223
224Converts a `GetPdf` or `GetImage` response to a blob URL suitable for `<iframe src="">`, `<embed src="">`, or `<img src="">`.
225
226```typescript
227import { GetPdf, GetObjectUrl } from '@the_library/public/downloader';
228
229const pdfResponse = await GetPdf(txId);
230const blobUrl = await GetObjectUrl(pdfResponse);
231// Now use: <iframe :src="blobUrl" />
232```
233
234**Parameters:**
235- `response: Response` — A `Response` object returned by `GetPdf` or `GetImage`.
236
237**Returns:** `Promise<string>` — A `blob:` URL (e.g., `blob:https://example.com/...`).
238
239---
240
241#### `revokeObjectUrl(blobUrl: string): void`
242
243Explicitly revokes a blob URL created by `GetObjectUrl` to release memory.
244
245```typescript
246const blobUrl = await GetObjectUrl(response);
247// ... later ...
248revokeObjectUrl(blobUrl);
249```
250
251**Parameters:**
252- `blobUrl: string` — The blob URL to revoke.
253
254**Returns:** `void`
255
256**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).
257
258---
259
260#### `storageEstimate(): Promise<{ usage: number; quota: number }>`
261
262Queries the browser's storage quota and current usage.
263
264```typescript
265import { storageEstimate } from '@the_library/public/downloader';
266
267const { usage, quota } = await storageEstimate();
268const percentUsed = (usage / quota) * 100;
269console.log(`${percentUsed.toFixed(1)}% of storage in use`);
270```
271
272**Parameters:** None
273
274**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.
275
276---
277
278### Metadata & Storage
279
280#### `getBookData(txId: string): Promise<BookData | undefined>`
281
282Reads cached book metadata (title, page count, etc.) from IndexedDB.
283
284```typescript
285import { getBookData } from '@the_library/public/downloader';
286
287const bookData = await getBookData(txId);
288if (bookData) {
289 console.log(`${bookData.title} has ${bookData.pages} pages`);
290}
291```
292
293**Parameters:**
294- `txId: string` — Arweave TX ID.
295
296**Returns:** `Promise<BookData | undefined>` — Book metadata if cached, `undefined` if no metadata has been hydrated yet.
297
298**Type: `BookData`**
299```typescript
300interface BookData {
301 txId: string;
302 title: string;
303 pages: number;
304 // ... other fields set by the mupdf worker
305}
306```
307
308---
309
310#### `getAllBookData(): Promise<BookData[]>`
311
312Retrieves all cached book metadata records from IndexedDB.
313
314```typescript
315import { getAllBookData } from '@the_library/public/downloader';
316
317const allBooks = await getAllBookData();
318console.log(`${allBooks.length} books cached`);
319```
320
321**Parameters:** None
322
323**Returns:** `Promise<BookData[]>` — Array of all cached `BookData` records. Empty array if no books have been hydrated yet.
324
325---
326
327#### `getDownloadRecord(txId: string): Promise<DownloadRecord | undefined>`
328
329Reads the download progress record for a TX ID.
330
331```typescript
332import { getDownloadRecord } from '@the_library/public/downloader';
333
334const record = await getDownloadRecord(txId);
335if (record) {
336 console.log(`${record.loaded}/${record.total} bytes downloaded`);
337}
338```
339
340**Parameters:**
341- `txId: string` — Arweave TX ID.
342
343**Returns:** `Promise<DownloadRecord | undefined>`
344
345**Type: `DownloadRecord`**
346```typescript
347interface DownloadRecord {
348 txId: string;
349 loaded: number; // bytes downloaded so far
350 total: number; // total file size in bytes
351 timestamp: number; // last update time (ms since epoch)
352}
353```
354
355---
356
357#### `getAllDownloadRecords(): Promise<DownloadRecord[]>`
358
359Retrieves all download progress records.
360
361```typescript
362import { getAllDownloadRecords } from '@the_library/public/downloader';
363
364const records = await getAllDownloadRecords();
365console.log(`${records.length} active/recent downloads`);
366```
367
368**Parameters:** None
369
370**Returns:** `Promise<DownloadRecord[]>` — Array of all cached progress records.
371
372---
373
374### Cache Management
375
376#### `downloadState`
377
378The canonical, synchronous, framework-agnostic store for all file state. Every framework adapter (`vue`, `react`, `solid`) is a thin reactive wrapper around this store.
379
380```typescript
381import { downloadState } from '@the_library/public/downloader';
382
383// Subscribe to changes
384const unsubscribe = downloadState.subscribe((changedTxId) => {
385 console.log(`File state changed for ${changedTxId}`);
386});
387
388// Read current state
389const fileState = downloadState.get(txId);
390if (fileState?.phase === 'done') {
391 console.log('Download complete');
392}
393
394// Read render state (page images, book metadata)
395const renderState = downloadState.getRender(txId);
396console.log(`${renderState.pages.size} page images cached`);
397
398// Global summary
399const summary = downloadState.summary();
400console.log(`${summary.downloading} active downloads`);
401```
402
403**Methods:**
404
405- `downloadState.get(txId: string): FileState | undefined` — Returns current state of one file.
406
407- `downloadState.getRender(txId: string): RenderState | undefined` — Returns render/page state for one file.
408
409- `downloadState.summary(): DownloadSummary` — Returns global aggregate (total files, total bytes, etc.).
410
411- `downloadState.subscribe(callback: (txId: string) => void): () => void` — Registers a listener. Returns unsubscribe function.
412
413**Type: `FileState`**
414```typescript
415interface FileState {
416 phase: 'unknown' | 'queued' | 'downloading' | 'done' | 'error';
417 loaded: number;
418 total: number;
419 error?: string;
420 progress?: number; // 0-1
421}
422```
423
424**Type: `RenderState`**
425```typescript
426interface RenderState {
427 bookData?: BookData;
428 pages: Map<number, FilePhase>; // page number -> 'done' | 'error' | ...
429}
430```
431
432---
433
434#### `deletePdf(txId: string): Promise<boolean>`
435
436Removes a cached PDF from Cache API storage.
437
438```typescript
439import { deletePdf } from '@the_library/public/downloader';
440
441const deleted = await deletePdf(txId);
442console.log(deleted ? 'Deleted' : 'Not found in cache');
443```
444
445**Parameters:**
446- `txId: string` — Arweave TX ID.
447
448**Returns:** `Promise<boolean>` — `true` if deletion succeeded, `false` if the PDF was not cached.
449
450---
451
452#### `deletePreview(txId: string): Promise<boolean>`
453
454Removes all cached page preview images for a TX ID.
455
456```typescript
457import { deletePreview } from '@the_library/public/downloader';
458
459await deletePreview(txId);
460```
461
462**Parameters:**
463- `txId: string` — Arweave TX ID.
464
465**Returns:** `Promise<boolean>` — `true` if deletion succeeded.
466
467---
468
469#### `deleteDownloadRecord(txId: string): Promise<void>`
470
471Removes the download progress record from IndexedDB.
472
473```typescript
474import { deleteDownloadRecord } from '@the_library/public/downloader';
475
476await deleteDownloadRecord(txId);
477```
478
479**Parameters:**
480- `txId: string` — Arweave TX ID.
481
482**Returns:** `Promise<void>`
483
484---
485
486### Cache Store API
487
488Raw cache and IndexedDB access (rarely called directly; use the higher-level APIs above when possible).
489
490#### `openPdfCache(): Promise<Cache>`
491
492Gets the Cache API store for PDFs.
493
494```typescript
495const cache = await openPdfCache();
496const keys = await cache.keys();
497```
498
499#### `openPreviewCache(): Promise<Cache>`
500
501Gets the Cache API store for preview images.
502
503#### `openPagesCache(): Promise<Cache>`
504
505Gets the Cache API store for individual page images.
506
507#### `matchPdf(txId: string): Promise<Response | undefined>`
508
509Low-level cache lookup for a single PDF.
510
511#### `matchPreview(txId: string): Promise<Response | undefined>`
512
513Low-level cache lookup for a cover/first-page image.
514
515#### `matchPage(txId: string, pageNumber: number): Promise<Response | undefined>`
516
517Low-level cache lookup for a specific page image.
518
519#### `putPdf(txId: string, response: Response): Promise<void>`
520
521Low-level cache write for a PDF.
522
523#### `putPreview(txId: string, response: Response): Promise<void>`
524
525Low-level cache write for a cover image.
526
527#### `putPage(txId: string, pageNumber: number, response: Response): Promise<void>`
528
529Low-level cache write for a page image.
530
531#### `pdfKeys(): Promise<string[]>`
532
533Lists all cached PDF keys.
534
535#### `previewKeys(): Promise<string[]>`
536
537Lists all cached preview-image keys.
538
539#### `pageKeys(): Promise<string[]>`
540
541Lists all cached page-image keys.
542
543---
544
545### Keys & Naming
546
547#### `pdfKey(txId: string): string`
548
549Generates the cache key for a PDF.
550
551```typescript
552const key = pdfKey(txId); // e.g., "arweave_pdf_{txId}"
553```
554
555#### `previewKey(txId: string): string`
556
557Generates the cache key for a cover image.
558
559#### `pageKey(txId: string, pageNumber: number): string`
560
561Generates the cache key for a specific page.
562
563```typescript
564const key = pageKey(txId, 5); // e.g., "arweave_page_5_{txId}"
565```
566
567#### `CACHES` (constant)
568
569```typescript
570import { CACHES } from '@the_library/public/downloader';
571
572// CACHES = {
573// pdf: 'dl2-pdf-cache-v1',
574// preview: 'dl2-preview-cache-v1',
575// pages: 'dl2-pages-cache-v1'
576// }
577```
578
579#### `CACHE_VERSION` (constant)
580
581```typescript
582import { CACHE_VERSION } from '@the_library/public/downloader';
583// CACHE_VERSION = 1
584```
585
586#### `DL2_SW_VERSION` (constant)
587
588Service worker build version. Used internally to detect when the SW script has been updated.
589
590---
591
592### Framework Adapters (`/downloader/{vue,react,solid}`)
593
594#### Vue Composables
595
596```typescript
597import {
598 useFileStatus,
599 useRenderStatus,
600 useGlobalProgress,
601 usePruning
602} from '@the_library/public/downloader/vue';
603
604// Reactive ref for one file's download state
605const status = useFileStatus(txId); // ComputedRef<FileState | undefined>
606
607// Reactive ref for render/page state
608const render = useRenderStatus(txId); // ComputedRef<RenderState | undefined>
609
610// Global download progress
611const progress = useGlobalProgress(); // ComputedRef<DownloadSummary>
612
613// LRU pruning activity
614const pruning = usePruning(); // ComputedRef<PruningState>
615```
616
617#### React Hooks
618
619```typescript
620import {
621 useFileStatus,
622 useRenderStatus,
623 useGlobalProgress,
624 usePruning
625} from '@the_library/public/downloader/react';
626
627// Synchronized external store hooks
628const status = useFileStatus(txId); // FileState | undefined
629const render = useRenderStatus(txId); // RenderState | undefined
630const progress = useGlobalProgress(); // DownloadSummary
631const pruning = usePruning(); // PruningState
632```
633
634#### Solid.js Primitives
635
636```typescript
637import {
638 useFileStatus,
639 useRenderStatus,
640 useGlobalProgress,
641 usePruning
642} from '@the_library/public/downloader/solid';
643
644// Solid createMemo/createSignal-based reactivity
645const [status] = useFileStatus(txId);
646const [render] = useRenderStatus(txId);
647const [progress] = useGlobalProgress();
648const [pruning] = usePruning();
649```
650
651---
652
653### Service Worker API (`@the_library/public/downloader/sw`)
654
655Installs a service worker that intercepts PDF/image requests, serving cached responses offline and persisting downloads across tab reloads.
656
657#### `registerDownloaderSW(): Promise<ServiceWorkerState>`
658
659Registers (or reuses) the downloader service worker.
660
661```typescript
662import { registerDownloaderSW } from '@the_library/public/downloader/sw';
663
664const state = await registerDownloaderSW();
665if (state.active) {
666 console.log('Service worker active and controlling pages');
667}
668```
669
670**Parameters:** None
671
672**Returns:** `Promise<ServiceWorkerState>`
673
674**Type: `ServiceWorkerState`**
675```typescript
676interface ServiceWorkerState {
677 registered: boolean; // SW is registered
678 active: boolean; // SW is controlling this page
679 waiting: boolean; // Update pending
680 installing: boolean; // New registration in progress
681 controller: ServiceWorkerContainer | null;
682}
683```
684
685---
686
687#### `swStatus(): Promise<ServiceWorkerState>`
688
689Checks current service worker status without re-registering.
690
691```typescript
692import { swStatus } from '@the_library/public/downloader/sw';
693
694const state = await swStatus();
695```
696
697**Parameters:** None
698
699**Returns:** `Promise<ServiceWorkerState>`
700
701---
702
703#### `clearDownloaderCaches(): Promise<void>`
704
705Drops all Cache API entries (PDFs, page images, previews), but keeps IndexedDB metadata records intact.
706
707```typescript
708import { clearDownloaderCaches } from '@the_library/public/downloader/sw';
709
710await clearDownloaderCaches();
711console.log('Cache storage cleared, IndexedDB preserved');
712```
713
714**Parameters:** None
715
716**Returns:** `Promise<void>`
717
718---
719
720#### `nukeDownloaderStorage(): Promise<void>`
721
722Full reset: deletes everything — Cache API entries, IndexedDB records, service worker registration. Use when the user logs out or to recover storage quota.
723
724```typescript
725import { nukeDownloaderStorage } from '@the_library/public/downloader/sw';
726
727await nukeDownloaderStorage();
728console.log('All download data erased');
729```
730
731**Parameters:** None
732
733**Returns:** `Promise<void>`
734
735---
736
737#### `unregisterDownloaderSW(): Promise<void>`
738
739Unregisters the service worker.
740
741```typescript
742import { unregisterDownloaderSW } from '@the_library/public/downloader/sw';
743
744await unregisterDownloaderSW();
745```
746
747**Parameters:** None
748
749**Returns:** `Promise<void>`
750
751---
752
753## 2. Models API (`@the_library/public/models`)
754
755The 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}`.
756
757### Core Types & Registry
758
759#### `Registry` (singleton, exported from `registry.ts`)
760
761Holds enriched `ModelDefinition` for every registered object type — property IDs, field schemas, and relations.
762
763```typescript
764import { Registry, type ModelDefinition } from '@the_library/public/models';
765
766// Get schema by numeric type ID
767const bookSchema: ModelDefinition = Registry.get(Book.type);
768// bookSchema.name, bookSchema.typePrefix, bookSchema.props, bookSchema.instanceFields, ...
769
770// Get schema by class name
771const byName = Registry.getByName('Book');
772```
773
774**Methods:**
775
776- `Registry.get(typeId: number): ModelDefinition` — Returns the enriched schema for a type. Throws if `typeId` is not registered.
777
778- `Registry.getByName(name: string): ModelDefinition` — Returns the schema for a named type (e.g., 'Book'). Throws if no type with that name is registered.
779
780- `Registry.all(): ModelDefinition[]` — Returns all registered type schemas.
781
782**Type: `ModelDefinition`**
783```typescript
784interface ModelDefinition {
785 name: string; // e.g., 'Book'
786 type: number; // numeric type ID
787 typePrefix: string; // e.g., 'B' for Book
788 props: PropertyDefinition[]; // field schemas
789 instanceFields: InstanceFieldInfo[]; // relation/reference fields
790 relations: RelationDefinition[];
791}
792```
793
794---
795
796### Record Cache & Reactivity
797
798#### `configureDb(options?: { cacheLimit?: number }): void`
799
800Sets the in-memory LRU cache size. Call before loading any records.
801
802```typescript
803import { configureDb } from '@the_library/public/models';
804
805// Lower cache limit for mobile devices
806configureDb({ cacheLimit: 500 });
807```
808
809**Parameters:**
810- `options.cacheLimit?: number` — Maximum number of records to hold in memory. Default is `2000`. Older records are evicted when the limit is exceeded.
811
812**Returns:** `void`
813
814---
815
816#### `getRecord(typeId: number, id: string): any`
817
818Synchronously retrieves a record from the cache.
819
820```typescript
821import { getRecord } from '@the_library/public/models';
822import { Book } from '@the_library/public/models';
823
824if (hasRecord(Book.type, bookId)) {
825 const book = getRecord(Book.type, bookId);
826 console.log(book.title);
827}
828```
829
830**Parameters:**
831- `typeId: number` — The model type ID (e.g., `Book.type`).
832- `id: string` — The record UID.
833
834**Returns:** The record object (e.g., a `Book` instance) if cached.
835
836**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.
837
838---
839
840#### `addRecord(typeId: number, id: string, record: any): void`
841
842Adds or updates a record in the cache.
843
844```typescript
845import { addRecord } from '@the_library/public/models';
846
847addRecord(Book.type, bookId, bookInstance);
848```
849
850**Parameters:**
851- `typeId: number` — Model type ID.
852- `id: string` — Record UID.
853- `record: any` — The record object.
854
855**Returns:** `void`
856
857---
858
859#### `removeRecord(typeId: number, id: string): void`
860
861Removes a record from the cache.
862
863```typescript
864import { removeRecord } from '@the_library/public/models';
865
866removeRecord(Book.type, bookId);
867```
868
869**Parameters:**
870- `typeId: number` — Model type ID.
871- `id: string` — Record UID.
872
873**Returns:** `void`
874
875---
876
877#### `hasRecord(typeId: number, id: string): boolean`
878
879Checks if a record is currently cached.
880
881```typescript
882import { hasRecord } from '@the_library/public/models';
883
884if (hasRecord(Book.type, bookId)) {
885 // safe to call getRecord()
886}
887```
888
889**Parameters:**
890- `typeId: number` — Model type ID.
891- `id: string` — Record UID.
892
893**Returns:** `boolean`
894
895---
896
897#### `pinRecord(typeId: number, id: string): void`
898
899Pins a record so it won't be evicted from the LRU cache. Use for the currently-open record, search results, etc.
900
901```typescript
902import { pinRecord } from '@the_library/public/models';
903
904// User opened a book — don't evict it while they're reading
905pinRecord(Book.type, openBookId);
906```
907
908**Parameters:**
909- `typeId: number` — Model type ID.
910- `id: string` — Record UID.
911
912**Returns:** `void`
913
914---
915
916#### `unpinRecord(typeId: number, id: string): void`
917
918Unpins a record so it can be evicted again.
919
920```typescript
921import { unpinRecord } from '@the_library/public/models';
922
923// User closed the book
924unpinRecord(Book.type, openBookId);
925```
926
927**Parameters:**
928- `typeId: number` — Model type ID.
929- `id: string` — Record UID.
930
931**Returns:** `void`
932
933---
934
935#### `addReactivitySystem(system: ReactivitySystem): void`
936
937Installs a framework-agnostic reactivity hook that fires when records change. Framework adapters call this once at boot.
938
939```typescript
940import { addReactivitySystem } from '@the_library/public/models';
941
942addReactivitySystem({
943 track: (typeId, id) => { /* called when record added/changed */ },
944 trigger: (typeId, id) => { /* called when record needs re-render */ },
945});
946```
947
948---
949
950#### `setReactivitySystem(system: ReactivitySystem): void`
951
952Replaces the current reactivity system. Typically called by framework adapters during initialization.
953
954---
955
956### Generated Models
957
958Generated 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.
959
960```typescript
961import { Book, Person, NewBookWithId, ModelTypes } from '@the_library/public/models';
962// equivalently: from '@the_library/public/models/models'
963
964// Create a new record in memory (not yet persisted)
965const book = NewBookWithId('book:abc123', 'username');
966book.title = 'A Study in Scarlet';
967book.author = 'Sir Arthur Conan Doyle';
968
969// Load an existing record from disk
970const loaded = await Book.LoadAsync('book:abc123');
971
972// Check type at runtime
973if (loaded instanceof Book) {
974 console.log('It is a book');
975}
976
977// Generic record hydration (type unknown at compile time)
978const typeId = 10; // some numeric type
979const RecordClass = ModelTypes[typeId];
980const generic = new RecordClass();
981```
982
983**Key Functions:**
984
985- `NewBookWithId(id: string, creatorUsername: string): Book` — Factory to create a new Book record in memory.
986
987- `Book.LoadAsync(id: string): Promise<Book>` — Loads a Book by UID from IndexedDB.
988
989- `ModelTypes: { [typeId: number]: typeof ModelClass }` — Maps numeric type IDs to model classes.
990
991---
992
993### Schema Registry
994
995The `Registry` singleton (above) holds `ModelDefinition` for each type. Accessed via `Registry.get(typeId)` or `Registry.getByName(name)`.
996
997---
998
999### Trash Index
1000
1001A 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.
1002
1003```typescript
1004import { trashStore, scanTrash, type TrashStore } from '@the_library/public/models';
1005```
1006
1007**How it populates:**
1008- **At boot** — `scanTrash()` runs a full pass over every currently-hydrated record right after `hydrateAll()` (both the initial `bootModelsV2()` call and every `loadLanguage()` call), deriving membership directly from each record's live `deleted`/`safe` state. No per-type registration needed — `deleted`/`safe` are universal base properties present on every model.
1009- **Live** — registered internally as a `ReactivitySystem` (see `addReactivitySystem`, 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 its `deleted`/`safe` property patched — the same op every property edit uses — so this module never creates patches, it only mirrors them.)
1010- **Instant paint** — a localStorage snapshot seeds the in-memory state synchronously at import time, so `useTrash()` reads a cached value immediately instead of empty until `bootModelsV2()` resolves; `scanTrash()` then reconciles it against the live corpus once boot completes.
1011
1012#### `trashStore` (singleton)
1013
1014```typescript
1015import { Tag } from '@the_library/public/models/models';
1016import { trashStore } from '@the_library/public/models';
1017
1018// All ids currently trashed for Tag, merged across every bucketed username
1019const allTrashedTagIds = trashStore.list(Tag.type);
1020
1021// Merge only specific usernames' buckets
1022const mineOnly = trashStore.list(Tag.type, ['mine']);
1023
1024const count = trashStore.count(Tag.type);
1025const buckets = trashStore.usernames(Tag.type); // e.g. ['canonical']
1026
1027const unsubscribe = trashStore.subscribe(Tag.type, () => {
1028 console.log('Tag trash changed:', trashStore.count(Tag.type));
1029});
1030```
1031
1032**Methods:**
1033
1034- `trashStore.list(objectType: number, usernames?: string[]): number[]` — Merges trashed ids across `usernames`. Empty array (default) merges **every** bucketed username for that type.
1035
1036- `trashStore.count(objectType: number, usernames?: string[]): number` — Same merge semantics as `list()`; returns the count.
1037
1038- `trashStore.usernames(objectType: number): string[]` — Usernames currently bucketed for a type (e.g. to populate a filter picker).
1039
1040- `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.
1041
1042**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.
1043
1044**Type: `TrashStore`**
1045```typescript
1046interface TrashStore {
1047 list(objectType: number, usernames?: string[]): number[];
1048 count(objectType: number, usernames?: string[]): number;
1049 usernames(objectType: number): string[];
1050 subscribe(objectType: number, listener: () => void): () => void;
1051}
1052```
1053
1054---
1055
1056#### `scanTrash(): void`
1057
1058Full (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.
1059
1060**Parameters:** None
1061
1062**Returns:** `void`
1063
1064---
1065
1066### Framework Adapters (`/models/{vue,react,solid,astro}`)
1067
1068#### Vue Composables
1069
1070```typescript
1071import {
1072 bootstrapModelsV2,
1073 useLevel,
1074 useDraft,
1075 useTrash,
1076 resetModelsV2Session
1077} from '@the_library/public/models/vue';
1078```
1079
1080**`bootstrapModelsV2(options: BootstrapOptions): Promise<void>`**
1081
1082Initializes the models subsystem once at app boot. Idempotent across Astro `<ClientRouter />` navigations.
1083
1084```typescript
1085await bootstrapModelsV2({
1086 userLang: 'en',
1087 models: [{ type: Book.type, create: NewBookWithId }],
1088 localMode: '/corpus_v2', // OR registry: { dataBlock, gateway }
1089 onBoot: (data) => console.log(`booted ${data.sidecars.length} sidecars`),
1090});
1091```
1092
1093**Parameters:**
1094- `userLang: string` — User's preferred language (ISO 639-1 or custom code).
1095- `models: ModelFactory[]` — Array of `{ type: number; create: (id, username) => ModelInstance }`.
1096- `localMode?: string` — Folder path to serve corpus from (dev mode, mutually exclusive with `registry`).
1097- `registry?: { dataBlock: string; gateway: string }` — Arweave data block & gateway (production mode).
1098- `onBoot?: (data: BootData) => void` — Callback fired when bootstrap completes.
1099
1100**Returns:** `Promise<void>` — resolves once all sidecars are hydrated.
1101
1102---
1103
1104**`useLevel(username?: string): Ref<LevelData>`**
1105
1106Reactive per-username level state and corpus metadata.
1107
1108```typescript
1109import { useLevel } from '@the_library/public/models/vue';
1110
1111const { level, progress } = useLevel('alice').value;
1112console.log(`Alice's level: ${level}`);
1113```
1114
1115**Parameters:**
1116- `username?: string` — Username. Defaults to the currently-logged-in user.
1117
1118**Returns:** `Ref<LevelData>`
1119
1120**Type: `LevelData`**
1121```typescript
1122interface LevelData {
1123 username: string;
1124 level: number;
1125 progress: number; // 0-1
1126 lastUpdated: number; // timestamp
1127}
1128```
1129
1130---
1131
1132**`useDraft(getter: () => ModelInstance): { draft, isDirty, commit, rollback }`**
1133
1134Tracks edits to a model instance before committing them back to the original.
1135
1136```typescript
1137import { useDraft } from '@the_library/public/models/vue';
1138
1139const { draft, isDirty, commit, rollback } = useDraft(() => book);
1140
1141// Edit the draft
1142draft.value.title = 'New Title';
1143
1144// Check if unsaved changes exist
1145if (isDirty.value) {
1146 await commit(); // apply to original book
1147 // or rollback() to discard
1148}
1149```
1150
1151**Parameters:**
1152- `getter: () => ModelInstance` — A function returning the record to edit (usually a computed ref).
1153
1154**Returns:**
1155```typescript
1156{
1157 draft: Ref<ModelInstance>; // editable proxy of the original
1158 isDirty: Ref<boolean>; // true if any properties differ
1159 commit: () => Promise<void>; // apply changes to original
1160 rollback: () => void; // discard changes
1161}
1162```
1163
1164---
1165
1166**`useTrash(objectType: number, usernames?: string[]): { count: Ref<number>; getIds: () => number[] }`**
1167
1168Reactive view over the global [Trash Index](#trash-index) for one object type, merged across `usernames`. Population and live updates are entirely automatic — nothing to register, nothing to feed it.
1169
1170```typescript
1171import { useTrash } from '@the_library/public/models/vue';
1172import { Tag } from '@the_library/public/models/models';
1173
1174const { count, getIds } = useTrash(Tag.type); // merges every username (canonical catalog trash)
1175```
1176
1177**Parameters:**
1178- `objectType: number` — Model type ID (e.g. `Tag.type`).
1179- `usernames?: string[]` — Usernames to merge. Defaults to `[]` (every known username — see the `'canonical'` vs `'mine'` note in [Trash Index](#trash-index)).
1180
1181**Returns:**
1182```typescript
1183{
1184 count: Ref<number>;
1185 getIds: () => number[];
1186}
1187```
1188
1189---
1190
1191**`resetModelsV2Session(): void`**
1192
1193Clears the session and cached level state. Used for logout and testing/HMR.
1194
1195```typescript
1196import { resetModelsV2Session } from '@the_library/public/models/vue';
1197
1198resetModelsV2Session();
1199```
1200
1201---
1202
1203#### React Hooks
1204
1205Same API as Vue, but exported from `@the_library/public/models/react`:
1206
1207```typescript
1208import {
1209 bootstrapModelsV2,
1210 useLevel,
1211 useModelUpdate,
1212 useTrash,
1213 resetModelsV2Session
1214} from '@the_library/public/models/react';
1215```
1216
1217**`useLevel(username?: string): LevelData`**
1218
1219Hook returning the current level data. Re-renders when level changes.
1220
1221**`useModelUpdate(record: ModelInstance): boolean`**
1222
1223Subscribes to updates for a single record, triggering re-render on change. Returns `true` if subscription is active.
1224
1225**`useTrash(objectType: number, usernames?: string[]): { count: number; getIds: () => number[] }`**
1226
1227Re-renders the calling component whenever the global [Trash Index](#trash-index) changes for `objectType`, merged across `usernames` (defaults to `[]` — every known username). Population and live updates are entirely automatic.
1228
1229```typescript
1230import { useTrash } from '@the_library/public/models/react';
1231import { Tag } from '@the_library/public/models/models';
1232
1233const { count, getIds } = useTrash(Tag.type);
1234```
1235
1236**Note:** React does **not** export `useDraft` (no fine-grained reactivity primitive like Vue's `Ref`). Use manual imperative edits or a state manager instead.
1237
1238---
1239
1240#### Solid.js Primitives
1241
1242```typescript
1243import {
1244 bootstrapModelsV2,
1245 useLevel,
1246 subscribeRecord,
1247 useTrash,
1248 resetModelsV2Session
1249} from '@the_library/public/models/solid';
1250```
1251
1252**`useLevel(username?: string): () => LevelData`**
1253
1254Signal factory returning the current level data.
1255
1256**`subscribeRecord(record: ModelInstance, callback: (record) => void): () => void`**
1257
1258Manual subscription to record updates. Returns unsubscribe function.
1259
1260**`useTrash(objectType: number, usernames?: string[]): { count: () => number; getIds: () => number[] }`**
1261
1262Signal accessor over the global [Trash Index](#trash-index) for one object type, merged across `usernames` (defaults to `[]` — every known username). Population and live updates are entirely automatic.
1263
1264```typescript
1265import { useTrash } from '@the_library/public/models/solid';
1266import { Tag } from '@the_library/public/models/models';
1267
1268const { count, getIds } = useTrash(Tag.type);
1269return <span>{count()}</span>;
1270```
1271
1272---
1273
1274#### Astro / Static Generation
1275
1276```typescript
1277import {
1278 bootstrapModelsV2,
1279 resetModelsV2Session,
1280 subscribeRecord
1281} from '@the_library/public/models/astro';
1282```
1283
1284**`bootstrapModelsV2(options): Promise<void>`**
1285
1286Same as Vue/React.
1287
1288**`subscribeRecord(record, callback): () => void`**
1289
1290Manual subscription (islands use this for client-side updates).
1291
1292**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).
1293
1294---
1295
1296### Patch & Session Management
1297
1298Low-level APIs (not normally called directly; used by `bootstrapModelsV2`):
1299
1300#### `serializePatches(patches: Patch[]): Uint8Array`
1301
1302Encodes patches to binary for transmission or storage.
1303
1304#### `deserializePatches(data: Uint8Array): Patch[]`
1305
1306Decodes binary patches.
1307
1308#### `applyRestoredPatches(patches: Patch[], models: ModelFactory[]): void`
1309
1310Applies a set of deserialized patches to the in-memory records.
1311
1312#### `getPatchInstanceByAccount(username: string): Patches`
1313
1314Retrieves the patch store for a specific account.
1315
1316---
1317
1318## 3. Web3 API (`@the_library/public/web3`)
1319
1320The Web3 API consolidates blockchain login, wallet connections, transaction mutations, session state, on-chain subscriptions, and contract/address resolution.
1321
1322### 3.1 Session API
1323
1324Manages wallet accounts, session persistence, and the centralized write-transaction pipeline.
1325
1326#### `sessionStore` (singleton)
1327
1328The canonical, framework-agnostic store for known and active wallet accounts.
1329
1330```typescript
1331import { sessionStore } from '@the_library/public/web3';
1332
1333const { active, known } = sessionStore.getState();
1334console.log(`Active account: ${active?.account}`);
1335console.log(`${known.length} known accounts`);
1336
1337// React to changes
1338const unsubscribe = sessionStore.subscribe(() => {
1339 const newState = sessionStore.getState();
1340 console.log('Session changed', newState);
1341});
1342```
1343
1344**Methods:**
1345
1346- `sessionStore.getState(): SessionState` — Returns current state: `{ active: Web3SessionAccount | null; known: Web3SessionAccount[] }`.
1347
1348- `sessionStore.subscribe(callback: () => void): () => void` — Registers a listener, returns unsubscribe function.
1349
1350**Type: `Web3SessionAccount`**
1351```typescript
1352interface Web3SessionAccount {
1353 account: string; // hex address
1354 chainId: number;
1355 wallet: WalletType; // 'metamask' | 'private_key'
1356 username?: string;
1357 userId?: number;
1358 hasAccount: boolean; // registered on-chain
1359 level?: number;
1360 projectBalance?: {
1361 contributions: number;
1362 allocated: number;
1363 remaining: number;
1364 };
1365 donationsPerProject?: number[];
1366 metadata?: {
1367 country?: string;
1368 language?: string;
1369 lastSyncTime?: number;
1370 };
1371}
1372```
1373
1374---
1375
1376#### `syncAccount(address: string, chainId: number, walletType: WalletType): Promise<Web3SessionAccount>`
1377
1378Hydrates 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`.
1379
1380```typescript
1381import { syncAccount } from '@the_library/public/web3';
1382
1383const account = await syncAccount('0xabc...', 1116, 'metamask');
1384console.log(`${account.username} is level ${account.level}`);
1385```
1386
1387**Parameters:**
1388- `address: string` — Wallet address (hex).
1389- `chainId: number` — Chain ID (1116, 1114, etc.).
1390- `walletType: WalletType` — `'metamask'` or `'private_key'`.
1391
1392**Returns:** `Promise<Web3SessionAccount>` — The hydrated account record.
1393
1394**Side effects:**
1395- Adds/updates the account in `sessionStore`.
1396- Persists the account to localStorage.
1397- Kicks off a background `subscriptionSync` for the account's `userId`.
1398
1399---
1400
1401#### `useWeb3Session()` (Vue composable)
1402
1403Reactive wrapper over `sessionStore`.
1404
1405```typescript
1406import { useWeb3Session } from '@the_library/public/web3/vue';
1407
1408const {
1409 accounts, // Ref<Web3SessionAccount[]> — all known accounts
1410 registeredAccounts, // Ref<Web3SessionAccount[]> — only hasAccount=true
1411 activeAccount, // Ref<Web3SessionAccount | null>
1412 isConnecting, // Ref<boolean>
1413 setActive, // (account: Web3SessionAccount) => void
1414} = useWeb3Session();
1415```
1416
1417---
1418
1419### 3.2 Client API
1420
1421Typed read and write adapters over deployed contracts.
1422
1423#### `getReadClient(chainId: number, environment?: 'production' | 'staging'): ReadOnlyDomainAPI`
1424
1425Gets the read-only client for a chain. Blocks until contract addresses/ABIs are loaded from the registry.
1426
1427```typescript
1428import { getReadClient } from '@the_library/public/web3';
1429
1430const client = getReadClient(1116);
1431await client.ready; // wait for registry load
1432
1433const balance = await client.projectManagerStorage.balanceOfAddress('0xabc...');
1434const level = await client.archivistStorage.userLevel(userId);
1435const nbAccounts = await client.bouncerStorage.nbAccounts();
1436```
1437
1438**Parameters:**
1439- `chainId: number` — EVM chain ID (1116, 1114, 84532, etc.).
1440- `environment?: 'production' | 'staging'` — Defaults to `'production'`. Use `'staging'` to read staging-deployed contract addresses.
1441
1442**Returns:** `ReadOnlyDomainAPI` — Typed interface to all contract reads.
1443
1444**Type: `ReadOnlyDomainAPI`**
1445```typescript
1446interface ReadOnlyDomainAPI {
1447 readonly chainId: number;
1448 readonly ready: Promise<void>;
1449 readonly bouncerStorage: BouncerStorageReadAPI;
1450 readonly archivistStorage: ArchivistStorageReadAPI;
1451 readonly scientistStorage: ScientistStorageReadAPI;
1452 readonly projectManagerStorage: ProjectManagerStorageReadAPI;
1453 readonly factory: FactoryReadAPI;
1454 readonly addressRegistry: AddressRegistryReadAPI;
1455 readonly subscriptionManager: SubscriptionManagerReadAPI;
1456 readonly libraryRegistryStorage: LibraryRegistryStorageReadAPI;
1457 readonly publicLibraryStorage: PublicLibraryStorageReadAPI;
1458 getUsdPrice(): Promise<number>;
1459 getRegistryStatus(): Promise<any>;
1460}
1461```
1462
1463---
1464
1465#### Contract APIs
1466
1467**`BouncerStorageReadAPI`**
1468
1469Account identity and registration queries:
1470
1471- `hasAccount(address: string): Promise<boolean>` — Is the address registered?
1472- `userInfos(address: string): Promise<[username, country, language, userId]>` — Fetch user metadata by address.
1473- `userData(userId: number): Promise<[id, addr, username, language, country]>` — Fetch user metadata by ID.
1474- `getUsernameByAddress(address: string): Promise<string>` — Get username for an address.
1475- `userIdOf(address: string): Promise<number>` — Get user ID for an address.
1476- `userIdToUsername(userId: number): Promise<string>` — Get username for a user ID.
1477- `usernameToUserId(username: string): Promise<number>` — Get user ID for a username.
1478- `userAddr(userId: number): Promise<string>` — Get address for a user ID.
1479- `nbAccounts(): Promise<number>` — Total registered accounts on this chain.
1480- `getAllLocationCounts(): Promise<any[]>` — Aggregate account counts by location.
1481- `getActiveLocationCount(): Promise<number>` — Number of locations with at least one account.
1482- `identityStats(): Promise<[nbVerbs, nbAdjectives]>` — Stats on generated usernames.
1483
1484---
1485
1486**`ArchivistStorageReadAPI`**
1487
1488User level and backup history:
1489
1490- `userLevel(userId: number): Promise<number>` — Reading level for a user.
1491- `nbWrites(userId: number): Promise<number>` — Total patches authored.
1492- `loadActions(userId: number): Promise<[bigint[], bigint[], string[]]>` — All patches for a user (raw format).
1493- `loadActionsSince(userId: number, index: number): Promise<[bigint[], bigint[], string[]]>` — Patches since a given index.
1494
1495---
1496
1497**`ScientistStorageReadAPI`**
1498
1499Anonymized reading statistics:
1500
1501- `getLastMonthId(): Promise<number>` — Latest month in the stats index.
1502- `getDeploymentDate(): Promise<[year, month]>` — When the contract was deployed.
1503- `getStatsRaw(action: number, objectType: number, monthId: number): Promise<[bigint[], number[]]>` — Raw stats for a category/type/month.
1504
1505---
1506
1507**`ProjectManagerStorageReadAPI`**
1508
1509Project discovery and donation voting:
1510
1511- `nbProjects(): Promise<number>` — Total projects on-chain.
1512- `getProjectSupportedLanguages(projectId: number): Promise<string[]>` — Languages available for a project.
1513- `loadProject(projectId: number): Promise<Project>` — Fetch full project metadata.
1514- `balanceOfAddress(address: string): Promise<{ total, donations, unspent }>` — Wallet's donation balance breakdown.
1515- `addressDonationsPerProject(address: string): Promise<number[]>` — Vote allocation by project (array indexed by projectId-1).
1516- `projectVotingBalances(projectId: number): Promise<number>` — Total votes allocated to a project.
1517
1518---
1519
1520**`FactoryReadAPI`**
1521
1522Global corpus metadata:
1523
1524- `load(): Promise<[bigint[], bigint[], string[]]>` — All patches in the corpus (raw format).
1525- `loadUsername(username: string): Promise<[bigint[], bigint[], string[]]>` — Patches for one user.
1526- `loadUsernameSince(username: string, index: number): Promise<[bigint[], bigint[], string[]]>` — Incremental patch fetch.
1527- `nbWritesByUsername(username: string): Promise<number>` — Patch count for a user.
1528
1529---
1530
1531**`AddressRegistryReadAPI`**
1532
1533Dynamic contract address/ABI resolution:
1534
1535- `getLatestContract(name: string): Promise<[address, abiUrl]>` — Latest address for a named contract.
1536- `getContractHistory(name: string): Promise<AddressRegistryContractRecord[]>` — All addresses ever deployed for a contract.
1537- `getAllContractNames(): Promise<string[]>` — List of all contracts.
1538- `getLatestData(key: string): Promise<string>` — Fetch arbitrary key-value data from the registry.
1539
1540---
1541
1542**`SubscriptionManagerReadAPI`**
1543
1544Who-follows-whom graph:
1545
1546- `getSubscriptions(userId: number): Promise<number[]>` — List of user IDs that this user subscribes to.
1547- `getSubscriptionCount(userId: number): Promise<bigint>` — Total subscriptions for a user.
1548- `isSubscribed(subscriber: number, target: number): Promise<boolean>` — Is `subscriber` following `target`?
1549- `scoreOf(userId: number): Promise<bigint>` — Social score (based on follower count, etc.).
1550
1551---
1552
1553**`LibraryRegistryStorageReadAPI`**
1554
1555Library Collection and Registry metadata:
1556
1557- `getUnverifiedCollectionIds(): Promise<string[]>` — List of pending collection IDs.
1558- `getVerifiedCollectionIds(): Promise<string[]>` — List of verified collection IDs.
1559- `getCollection(id: string): Promise<Collection>` — Get full collection metadata by ID.
1560
1561---
1562
1563**`LibraryRegistryStorageWriteAPI`**
1564
1565Library Collection and Registry mutations (Requires Admin Authorization):
1566
1567- `addUnverifiedCollection(input: CollectionInput, options?: TransactionOptions): Promise<any>` — Submits a new collection for review.
1568- `verifyCollection(id: string, options?: TransactionOptions): Promise<any>` — Approves a pending collection.
1569- `rejectCollection(id: string, options?: TransactionOptions): Promise<any>` — Rejects and removes a pending collection.
1570- `updateCollectionStats(id: string, initialBookCount: number, dSafeBookCount: number, options?: TransactionOptions): Promise<any>` — Updates statistics for a collection.
1571- `setImportStatus(id: string, status: number, options?: TransactionOptions): Promise<any>` — Updates the import status enum.
1572- `setPublished(id: string, isPublished: boolean, options?: TransactionOptions): Promise<any>` — Toggles publication status of a collection.
1573- `setCollectionPublicLibrary(id: string, publicLibraryId: number, options?: TransactionOptions): Promise<any>` — Links a collection to a public library.
1574- `setPublicLibraryStorage(storageAddr: string, options?: TransactionOptions): Promise<any>` — Sets the address of the PublicLibraryStorage contract.
1575
1576---
1577
1578**`PublicLibraryStorageReadAPI`**
1579
1580Public Library cataloging metadata:
1581
1582- `libraryExists(id: number): Promise<boolean>` — Checks if a library ID exists.
1583- `getPublicLibrary(id: number): Promise<PublicLibrary>` — Get full public library metadata by ID.
1584- `nbLibraries(): Promise<number>` — Returns the total number of registered public libraries.
1585
1586---
1587
1588**`PublicLibraryStorageWriteAPI`**
1589
1590Public Library management (Requires Admin Authorization):
1591
1592- `addPublicLibrary(input: PublicLibraryInput, options?: TransactionOptions): Promise<any>` — Creates a new public library record.
1593- `updatePublicLibrary(id: number, input: PublicLibraryInput, options?: TransactionOptions): Promise<any>` — Updates an existing public library record.
1594
1595---
1596
1597#### Write Operations
1598
1599```typescript
1600import { writeOperations, resetTransactionState } from '@the_library/public/web3';
1601
1602await writeOperations.register(walletType, chainId, address, registrationFee);
1603await writeOperations.vote(walletType, chainId, address, projectId, language, voteAmount);
1604await writeOperations.save(walletType, chainId, address, compressedActions, textIndexes, textData, level);
1605await writeOperations.subscribe(walletType, chainId, address, subscriberUserId, targetUserId);
1606await writeOperations.unsubscribe(walletType, chainId, address, subscriberUserId, targetUserId);
1607```
1608
1609All write operations:
1610- Acquire a 1-minute run-lock (only one transaction per address/chain at a time).
1611- Emit `WalletEvents` state notifications.
1612- Automatically refresh the account via `syncAccount()` on success.
1613
1614If a transaction throws and the run-lock needs to be cleared manually, call `resetTransactionState()`.
1615
1616---
1617
1618#### Chain Metadata
1619
1620```typescript
1621import { getChain, EVM_CHAINS, DEFAULT_CHAIN_ID } from '@the_library/public/web3';
1622
1623const chain = getChain(1116);
1624// chain.name, chain.symbol, chain.shortName, chain.logo, chain.explorer,
1625// chain.rpc, chain.main (true for mainnet), chain.deployed, chain.stagingDeployed
1626
1627const deployedChains = Object.values(EVM_CHAINS).filter(c => c.deployed);
1628```
1629
1630**Type: `EvmChainConfig`**
1631```typescript
1632interface EvmChainConfig {
1633 name: string; // 'CORE' | 'tCORE2' | 'Base Sepolia'
1634 shortName: string; // 'core' | 'tcore2' | ...
1635 chainId: number;
1636 symbol: string; // 'CORE' | 'CORETST' | ...
1637 logo: string; // URL to chain logo
1638 explorer: string; // Block explorer URL
1639 rpc: string[]; // RPC endpoints
1640 main: boolean; // Mainnet vs testnet
1641 deployed: boolean; // Contracts deployed on this chain
1642 stagingDeployed?: boolean;
1643}
1644```
1645
1646---
1647
1648#### Vue Composable: `useChain(chainId)`
1649
1650```typescript
1651import { useChain } from '@the_library/public/web3/vue';
1652
1653const { chain, client, usdPrice, refreshPrice, isMainnet, chainRouteQuery } = useChain(1116);
1654// chain: EvmChainConfig
1655// client: ReadOnlyDomainAPI (ready to use)
1656// usdPrice: Ref<number> — live USD price polling
1657// refreshPrice: () => Promise<void>
1658// isMainnet: boolean
1659// chainRouteQuery: { chainId: '1116' } — useful for router links
1660```
1661
1662---
1663
1664### 3.3 Registry API
1665
1666Dynamic contract address and ABI resolution from the on-chain `AddressRegistry`.
1667
1668#### `addressLoader` (singleton)
1669
1670```typescript
1671import { addressLoader, setRegistryBaseUrl } from '@the_library/public/web3';
1672
1673// Set custom registry host (before anything else runs)
1674setRegistryBaseUrl('https://registry.world.bibliotech.com');
1675
1676// Subscribe to per-contract load progress
1677const unsubscribe = addressLoader.subscribe((chainId, status) => {
1678 console.log(`chain ${chainId}: ${status.loaded}/${status.total} contracts loaded`);
1679});
1680
1681// Get resolved address/ABI for a contract
1682const addr = addressLoader.getAddress(chainId, 'Factory');
1683const abi = addressLoader.getAbi(chainId, 'Factory');
1684```
1685
1686**Methods:**
1687
1688- `addressLoader.subscribe(callback: (chainId, LoadingStatus) => void): () => void` — Listen for load progress.
1689
1690- `addressLoader.getAddress(chainId: number, contractName: string): string | undefined` — Resolved contract address, or `undefined` if not yet loaded.
1691
1692- `addressLoader.getAbi(chainId: number, contractName: string): any[] | undefined` — Resolved ABI array, or `undefined` if not yet loaded.
1693
1694- `addressLoader.loadManifest(environment: 'production' | 'staging'): Promise<Manifest>` — Manually load the registry manifest (auto-called by clients).
1695
1696---
1697
1698#### `setRegistryBaseUrl(url: string): void`
1699
1700Override the default registry host URL (normally `https://registry.world.bibliotech.com`). Call before any clients are instantiated.
1701
1702---
1703
1704### 3.4 Subscription API
1705
1706Walks the on-chain subscription graph (who-follows-whom) and incrementally fetches patches.
1707
1708#### `getSubscriptionClient(chainId: number): SubscriptionClient`
1709
1710Gets a chain-scoped subscription client.
1711
1712```typescript
1713import { getSubscriptionClient } from '@the_library/public/web3';
1714
1715const subs = getSubscriptionClient(1116);
1716
1717// First sync: full BFS walk + patch fetch
1718const { patches, cursor, truncated } = await subs.sync(rootUserId);
1719
1720// Later: incremental — only fetch new patches since cursor
1721const next = await subs.syncSince(cursor);
1722
1723// Query the graph
1724const followees = await subs.getSubscriptions(userId);
1725const score = await subs.getScore(userId);
1726```
1727
1728**Methods:**
1729
1730- `subs.sync(rootUserId: number): Promise<SyncResult>` — Walk subscription graph starting from a user, fetch all patches.
1731
1732- `subs.syncSince(cursor: SyncCursor): Promise<SyncResult>` — Incremental sync using a persisted cursor.
1733
1734- `subs.getSubscriptions(userId: number): Promise<number[]>` — List of users this user subscribes to.
1735
1736- `subs.getScore(userId: number): Promise<number>` — Social score for a user.
1737
1738**Type: `SyncResult`**
1739```typescript
1740interface SyncResult {
1741 patches: Patch[];
1742 cursor: SyncCursor; // Persist this to resume next sync
1743 truncated: boolean; // true if result was truncated (more data available)
1744}
1745```
1746
1747---
1748
1749#### `cursorStore` (IndexedDB persistence)
1750
1751```typescript
1752import { cursorStore } from '@the_library/public/web3';
1753
1754const cursor = await cursorStore.get(userId);
1755const result = cursor
1756 ? await subs.syncSince(cursor)
1757 : await subs.sync(userId);
1758await cursorStore.set(userId, result.cursor);
1759```
1760
1761**Methods:**
1762
1763- `cursorStore.get(userId: number): Promise<SyncCursor | null>` — Retrieve persisted cursor, or `null` if never synced.
1764
1765- `cursorStore.set(userId: number, cursor: SyncCursor): Promise<void>` — Save cursor for next sync.
1766
1767---
1768
1769#### Vue Composable: `useSubscriptionSync(userId)`
1770
1771```typescript
1772import { useSubscriptionSync } from '@the_library/public/web3/vue';
1773
1774const { cursor, patches, isLoading, refresh } = useSubscriptionSync(userId);
1775// cursor: Ref<SyncCursor>
1776// patches: Ref<Patch[]>
1777// isLoading: Ref<boolean>
1778// refresh: () => Promise<void>
1779```
1780
1781---
1782
1783### 3.5 Contracts API
1784
1785Static ABI exports for contracts that don't use dynamic resolution.
1786
1787```typescript
1788import { addressRegistryAbi, pythAbi } from '@the_library/public/web3';
1789
1790// Use with viem directly:
1791import { createPublicClient, http } from 'viem';
1792
1793const client = createPublicClient({
1794 chain: /*...*/,
1795 transport: http()
1796});
1797
1798const data = await client.readContract({
1799 address: '0x...',
1800 abi: addressRegistryAbi,
1801 functionName: 'getLatestContract',
1802 args: ['Factory'],
1803});
1804```
1805
1806---
1807
1808### 3.6 Events API
1809
1810Typed pub/sub bus for wallet and transaction state notifications.
1811
1812#### `WalletEvents` (typed pub/sub)
1813
1814```typescript
1815import { WalletEvents, type WalletState, type WalletError } from '@the_library/public/web3';
1816
1817const unsubWalletState = WalletEvents.onWalletStateEvent((e) => {
1818 // e: { wallet, chainId, event: WalletState, functionName?, timestamp }
1819 if (e.event === 'TRANSACTION_STARTED') showSpinner();
1820 if (e.event === 'TRANSACTION_DONE') hideSpinner();
1821 if (e.event === 'ACCOUNT_SYNCED') updateUI();
1822});
1823
1824const unsubError = WalletEvents.onWalletError((e) => {
1825 // e: { wallet, chainId, reason: WalletError, timestamp }
1826 toast.error(e.reason);
1827});
1828
1829const unsubSaveRestored = WalletEvents.onSaveRestored((e) => {
1830 // e: { compressedActions, indexes, stringIndexes, timestamp }
1831 // Patches from on-chain state that outran local edits
1832});
1833
1834const unsubPatchesSynced = WalletEvents.onSubscriptionPatchesSynced((e) => {
1835 // e: { rootUserId, patches: Patch[], timestamp }
1836 // Subscription graph patches fetched in background
1837});
1838
1839// Unsubscribe
1840unsubWalletState();
1841unsubError();
1842unsubSaveRestored();
1843unsubPatchesSynced();
1844```
1845
1846**Enums:**
1847
1848**`WalletState`** — transaction lifecycle events:
1849- `'WALLET_CONNECTED'`
1850- `'WALLET_DISCONNECTED'`
1851- `'TRANSACTION_STARTED'`
1852- `'TRANSACTION_DONE'`
1853- `'TRANSACTION_FAILED'`
1854- `'ACCOUNT_SYNCED'`
1855- `'SUBSCRIPTION_SYNC_STARTED'`
1856- `'SUBSCRIPTION_SYNC_DONE'`
1857
1858**`WalletError`** — error reasons:
1859- `'NETWORK_ERROR'`
1860- `'TRANSACTION_REJECTED'`
1861- `'INSUFFICIENT_BALANCE'`
1862- `'UNKNOWN_ERROR'`
1863- etc. (see source for full list)
1864
1865---
1866
1867#### Low-level `EventBus` (rarely used)
1868
1869```typescript
1870import { getEventBus } from '@the_library/public/web3';
1871
1872const bus = getEventBus();
1873const unsubscribe = bus.on('topic', (data) => { /* handle */ });
1874bus.emit('topic', data);
1875bus.once('topic', (data) => { /* one-time listener */ });
1876bus.clear('topic'); // remove all listeners
1877```
1878
1879---
1880
1881### 3.7 Wallets API
1882
1883Wallet connectors (MetaMask, PrivateKey) and metadata.
1884
1885#### `getConnector(type: WalletType): WalletConnector | undefined`
1886
1887Retrieves a registered wallet connector by type.
1888
1889```typescript
1890import { getConnector } from '@the_library/public/web3';
1891
1892const metamask = getConnector('metamask');
1893if (metamask?.isAvailable()) {
1894 const { address, chainId } = await metamask.connect();
1895}
1896```
1897
1898**Parameters:**
1899- `type: WalletType` — `'metamask'` | `'private_key'`
1900
1901**Returns:** The connector, or `undefined` if not registered (e.g., `bootstrapWebSDK()` hasn't run yet).
1902
1903---
1904
1905#### `getRegisteredConnectors(): WalletConnector[]`
1906
1907Lists all registered connectors.
1908
1909```typescript
1910import { getRegisteredConnectors } from '@the_library/public/web3';
1911
1912const connectors = getRegisteredConnectors();
1913// array of MetaMaskConnector, PrivateKeyConnector, etc.
1914```
1915
1916---
1917
1918#### `walletMeta` (metadata for each wallet)
1919
1920```typescript
1921import { walletMeta } from '@the_library/public/web3';
1922
1923const meta = walletMeta.metamask;
1924// {
1925// displayName: 'MetaMask',
1926// logoUrl: 'https://...',
1927// description: '...',
1928// website: 'https://metamask.io',
1929// isInstalled(): boolean,
1930// isInstallable(): boolean,
1931// getInstallUrl(): string,
1932// }
1933
1934if (!meta.isInstalled() && meta.isInstallable()) {
1935 window.open(meta.getInstallUrl(), '_blank');
1936}
1937```
1938
1939---
1940
1941#### Private Key Storage
1942
1943```typescript
1944import { storePrivateKey, hasPrivateKey, hasAnyPrivateKey } from '@the_library/public/web3';
1945
1946storePrivateKey(1116, '0x1234...'); // raw hex, no '0x' prefix required
1947if (hasPrivateKey(1116)) {
1948 // PrivateKeyConnector can connect to this chain
1949}
1950
1951if (hasAnyPrivateKey()) {
1952 // At least one chain has a stored key
1953}
1954```
1955
1956**Warning:** Private keys are stored in plaintext localStorage. **Only use for dev/test wallets, never for real user funds.**
1957
1958---
1959
1960### 3.8 Projects API
1961
1962Project discovery and voting.
1963
1964#### `loadProject(chainId: number, language: string, projectId: number, refresh?: boolean): Promise<ProjectLocale>`
1965
1966Loads a single project by ID with language fallback. Cached automatically.
1967
1968```typescript
1969import { loadProject } from '@the_library/public/web3';
1970
1971const project = await loadProject(1116, 'eng', 5);
1972console.log(`${project.version.name} — ${project.balance} tokens`);
1973
1974// Force re-fetch from chain, bypassing cache
1975const fresh = await loadProject(1116, 'eng', 5, true);
1976```
1977
1978**Parameters:**
1979- `chainId: number` — Chain ID.
1980- `language: string` — Preferred language code (e.g., 'eng', 'deu', 'spa').
1981- `projectId: number` — On-chain project ID (1-indexed).
1982- `refresh?: boolean` — If `true`, bypass cache and refetch from chain.
1983
1984**Returns:** `Promise<ProjectLocale>`
1985
1986**Throws:** If cache miss and chain unreachable.
1987
1988---
1989
1990#### `loadProjects(chainId: number, language: string): Promise<Array<ProjectLocale | null>>`
1991
1992Loads all projects for a chain/language. Returns cached projects if chain is unreachable (with console warning).
1993
1994```typescript
1995import { loadProjects } from '@the_library/public/web3';
1996
1997const projects = await loadProjects(1116, 'eng');
1998const validProjects = projects.filter(p => p !== null);
1999```
2000
2001**Parameters:**
2002- `chainId: number` — Chain ID.
2003- `language: string` — Preferred language code.
2004
2005**Returns:** `Promise<Array<ProjectLocale | null>>` — Array where `null` entries represent projects that failed to load.
2006
2007---
2008
2009#### `projectLocale(project: Project, language: string, chainId: number): ProjectLocale`
2010
2011Helper to convert raw `Project` data to `ProjectLocale` with language selection and decimal normalization.
2012
2013```typescript
2014import { projectLocale } from '@the_library/public/web3';
2015
2016const raw = await client.projectManagerStorage.loadProject(5);
2017const locale = projectLocale(raw, 'eng', 1116);
2018```
2019
2020**Parameters:**
2021- `project: Project` — Raw contract data.
2022- `language: string` — Preferred language.
2023- `chainId: number` — For decimal normalization.
2024
2025**Returns:** `ProjectLocale`
2026
2027---
2028
2029#### Vue Composable: `useLoadProjects(chainId, language)`
2030
2031```typescript
2032import { useLoadProjects } from '@the_library/public/web3/vue';
2033
2034const chainId = ref(1116);
2035const language = ref('eng');
2036
2037const { projects, isLoading, error, reload } = useLoadProjects(chainId, language);
2038// projects: Ref<Array<ProjectLocale | null>>
2039// isLoading: Ref<boolean>
2040// error: Ref<Error | null>
2041// reload: () => Promise<void>
2042```
2043
2044---
2045
2046### 3.9 Chain & Network Configuration
2047
2048#### `EVM_CHAINS` (constant)
2049
2050```typescript
2051import { EVM_CHAINS } from '@the_library/public/web3';
2052
2053// EVM_CHAINS = {
2054// 1116: { name: 'CORE', ... },
2055// 1114: { name: 'tCORE2', ... },
2056// 84532: { name: 'Base Sepolia', ... },
2057// }
2058```
2059
2060---
2061
2062#### `DEFAULT_CHAIN_ID` (constant)
2063
2064The default chain for new users (usually production mainnet, e.g., `1116`).
2065
2066---
2067
2068#### `getChain(chainId: number): EvmChainConfig`
2069
2070Gets full metadata for a chain. Throws if `chainId` is not registered.
2071
2072```typescript
2073import { getChain } from '@the_library/public/web3';
2074
2075const chain = getChain(1116);
2076console.log(`${chain.name} (${chain.symbol})`);
2077```
2078
2079---
2080
2081### 3.10 Vue Composables (`@the_library/public/web3/vue`)
2082
2083#### `bootstrapWebSDK(options?: BootstrapOptions): Promise<BootstrapResult>`
2084
2085Single app-wide initialization: registers connectors, loads persisted session, hydrates accounts.
2086
2087```typescript
2088import { bootstrapWebSDK, BootstrapResultKey } from '@the_library/public/web3/vue';
2089
2090const result = await bootstrapWebSDK({
2091 withTest: true,
2092 environment: 'production',
2093 chainFilter: [1116, 1114], // optional: limit to these chains
2094});
2095
2096await result.ready; // wait for account discovery
2097
2098// Provide at app root
2099app.provide(BootstrapResultKey, result);
2100```
2101
2102**Parameters:**
2103- `options.withTest?: boolean` — Include test chains (Base Sepolia).
2104- `options.environment?: 'production' | 'staging'` — Contract environment.
2105- `options.chainFilter?: number[]` — Optional: only bootstrap these chains.
2106
2107**Returns:** `Promise<BootstrapResult>`
2108
2109**Type: `BootstrapResult`**
2110```typescript
2111interface BootstrapResult {
2112 ready: Promise<void>;
2113 chains: EvmChainConfig[];
2114 installedConnectors: WalletConnector[];
2115 installableConnectors: WalletConnector[];
2116 primaryAccount: Web3SessionAccount | null;
2117}
2118```
2119
2120---
2121
2122#### `useBootstrap(): BootstrapResult`
2123
2124Retrieves the app-wide bootstrap result (must have been provided via `app.provide(BootstrapResultKey, result)`).
2125
2126```typescript
2127import { useBootstrap } from '@the_library/public/web3/vue';
2128
2129const { chains, installedConnectors, primaryAccount } = useBootstrap();
2130```
2131
2132---
2133
2134#### `useWeb3Session()`
2135
2136Reactive wrapper over `sessionStore`.
2137
2138```typescript
2139import { useWeb3Session } from '@the_library/public/web3/vue';
2140
2141const {
2142 accounts, // Ref<Web3SessionAccount[]>
2143 registeredAccounts, // Ref<Web3SessionAccount[]> filtered by hasAccount=true
2144 activeAccount, // Ref<Web3SessionAccount | null>
2145 isConnecting, // Ref<boolean>
2146 setActive, // (account: Web3SessionAccount) => void
2147} = useWeb3Session();
2148```
2149
2150---
2151
2152#### `useChain(chainId: number | Ref<number>)`
2153
2154```typescript
2155import { useChain } from '@the_library/public/web3/vue';
2156
2157const { chain, client, usdPrice, refreshPrice, isMainnet, chainRouteQuery } = useChain(1116);
2158// chain: Ref<EvmChainConfig>
2159// client: Ref<ReadOnlyDomainAPI>
2160// usdPrice: Ref<number>
2161// refreshPrice: () => Promise<void>
2162// isMainnet: boolean
2163// chainRouteQuery: { chainId: '1116' }
2164```
2165
2166---
2167
2168#### `useLoadProjects(chainId: number | Ref<number>, language: string | Ref<string>)`
2169
2170```typescript
2171import { useLoadProjects } from '@the_library/public/web3/vue';
2172
2173const { projects, isLoading, error, reload } = useLoadProjects(
2174 ref(1116),
2175 ref('eng')
2176);
2177```
2178
2179---
2180
2181#### `useSubscriptionSync(userId: number | Ref<number>)`
2182
2183```typescript
2184import { useSubscriptionSync } from '@the_library/public/web3/vue';
2185
2186const { cursor, patches, isLoading, refresh } = useSubscriptionSync(userId);
2187```
2188
2189---
2190
2191## 4. Branding API (`@the_library/public/branding`)
2192
2193Static, framework-agnostic branding and contact data shared across apps.
2194
2195```typescript
2196import {
2197 globalBranding,
2198 worldBiblioTechBranding,
2199 dsafeLegalBranding,
2200 worldBibliotekBranding,
2201} from '@the_library/public/branding';
2202
2203import type { AppBranding, FounderInfo, BuilderInfo, SocialMediaUrls } from '@the_library/public/branding';
2204
2205// Access global founder/contact data
2206console.log(globalBranding.founder.email); // 'data@datapond.earth'
2207console.log(globalBranding.founder.name);
2208console.log(globalBranding.builder.name);
2209console.log(globalBranding.socialMedia.x); // 'https://x.com/realDataPond'
2210
2211// Access app-specific branding
2212console.log(worldBiblioTechBranding.appName); // 'World Biblio Tech'
2213console.log(worldBiblioTechBranding.url);
2214console.log(worldBiblioTechBranding.images.logo); // asset path
2215```
2216
2217**Type: `AppBranding`**
2218```typescript
2219interface AppBranding {
2220 appName: string;
2221 url: string;
2222 description?: string;
2223 images: {
2224 logo: string; // relative path (e.g., '@the_library/public/branding/assets/...')
2225 favicon?: string;
2226 banner?: string;
2227 };
2228 support?: {
2229 email?: string;
2230 url?: string;
2231 };
2232}
2233```
2234
2235**Type: `FounderInfo`**
2236```typescript
2237interface FounderInfo {
2238 name: string;
2239 email: string;
2240 title?: string;
2241}
2242```
2243
2244**Type: `BuilderInfo`**
2245```typescript
2246interface BuilderInfo {
2247 name: string;
2248 title?: string;
2249}
2250```
2251
2252**Type: `SocialMediaUrls`**
2253```typescript
2254interface SocialMediaUrls {
2255 x?: string; // Twitter/X
2256 github?: string;
2257 linkedin?: string;
2258 // ... other platforms
2259}
2260```
2261
2262---
2263
2264## Appendix: Type Quick Reference
2265
2266### Download Types
2267- `FileState` — download/error state for one file
2268- `RenderState` — page images and book metadata
2269- `BookData` — cached book title, page count, etc.
2270- `DownloadRecord` — progress (loaded/total bytes)
2271- `PruningState` — LRU cache eviction activity
2272- `InitOptions` — downloader setup parameters
2273
2274### Models Types
2275- `ModelDefinition` — schema for a model type
2276- `LevelData` — user's reading level and corpus progress
2277- `BootData` — result of `bootstrapModelsV2()`
2278- `TrashStore` — global trash index API (`list`/`count`/`usernames`/`subscribe`)
2279
2280### Web3 Types
2281- `Web3SessionAccount` — wallet state in session store
2282- `SessionState` — `{ active, known }`
2283- `ReadOnlyDomainAPI` — read client interface
2284- `EvmChainConfig` — chain metadata
2285- `ProjectLocale` — project with selected language
2286- `Project` — raw contract data
2287- `ProjectVersion` — project version metadata
2288- `SyncResult` — subscription sync result
2289- `SyncCursor` — resume point for incremental sync
2290- `WalletState` — transaction event enums
2291- `WalletError` — error reason enums
2292- `BootstrapResult` — app initialization result
2293
2294### Branding Types
2295- `AppBranding` — app-specific branding data
2296- `FounderInfo` — founder contact info
2297- `BuilderInfo` — builder/team info
2298- `SocialMediaUrls` — social media links
2299