@the_library/public 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2298 -0
- package/assets/branding/data-pond.png +0 -0
- package/assets/branding/dsafe-logo.png +0 -0
- package/assets/branding/founder-photo.jpg +0 -0
- package/assets/branding/icon_transparent.png +0 -0
- package/assets/branding/library-favicon.png +0 -0
- package/assets/branding/pond-enterprise-logo.jpg +0 -0
- package/assets/branding/pond-enterprise-logo.svg +27 -0
- package/dist/branding/index.d.ts +48 -0
- package/dist/branding/index.js +67 -0
- package/dist/branding/index.js.map +1 -0
- package/dist/chunk-2HFOWE5R.js +296 -0
- package/dist/chunk-2HFOWE5R.js.map +1 -0
- package/dist/chunk-3L6LH2XK.js +465 -0
- package/dist/chunk-3L6LH2XK.js.map +1 -0
- package/dist/chunk-4CWVFPBN.js +28 -0
- package/dist/chunk-4CWVFPBN.js.map +1 -0
- package/dist/chunk-AGHPATVL.js +65 -0
- package/dist/chunk-AGHPATVL.js.map +1 -0
- package/dist/chunk-JDUYLUDJ.js +4835 -0
- package/dist/chunk-JDUYLUDJ.js.map +1 -0
- package/dist/chunk-OEZK7IX7.js +77 -0
- package/dist/chunk-OEZK7IX7.js.map +1 -0
- package/dist/chunk-PUYDAUCI.js +2873 -0
- package/dist/chunk-PUYDAUCI.js.map +1 -0
- package/dist/chunk-WHWMTS7D.js +99 -0
- package/dist/chunk-WHWMTS7D.js.map +1 -0
- package/dist/chunk-Y7RS6RTM.js +342 -0
- package/dist/chunk-Y7RS6RTM.js.map +1 -0
- package/dist/chunk-ZEZGI2TP.js +197 -0
- package/dist/chunk-ZEZGI2TP.js.map +1 -0
- package/dist/core-dao-core-logo-VDT2I5IW.svg +33 -0
- package/dist/downloader/dl2-sw.js +199 -0
- package/dist/downloader/dl2-sw.js.map +1 -0
- package/dist/downloader/file-worker-v2.js +258 -0
- package/dist/downloader/file-worker-v2.js.map +1 -0
- package/dist/downloader/index.d.ts +45 -0
- package/dist/downloader/index.js +538 -0
- package/dist/downloader/index.js.map +1 -0
- package/dist/downloader/mu-worker-v2.js +197 -0
- package/dist/downloader/mu-worker-v2.js.map +1 -0
- package/dist/downloader/react.d.ts +19 -0
- package/dist/downloader/react.js +32 -0
- package/dist/downloader/react.js.map +1 -0
- package/dist/downloader/solid.d.ts +21 -0
- package/dist/downloader/solid.js +19 -0
- package/dist/downloader/solid.js.map +1 -0
- package/dist/downloader/sw.d.ts +22 -0
- package/dist/downloader/sw.js +119 -0
- package/dist/downloader/sw.js.map +1 -0
- package/dist/downloader/vue.d.ts +23 -0
- package/dist/downloader/vue.js +19 -0
- package/dist/downloader/vue.js.map +1 -0
- package/dist/level-state-J3pj64Tr.d.ts +15 -0
- package/dist/models/astro.d.ts +47 -0
- package/dist/models/astro.js +44 -0
- package/dist/models/astro.js.map +1 -0
- package/dist/models/index.d.ts +244 -0
- package/dist/models/index.js +340 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/models.d.ts +524 -0
- package/dist/models/models.js +46 -0
- package/dist/models/models.js.map +1 -0
- package/dist/models/models_v2.global.js +1 -0
- package/dist/models/patch-worker.js +543 -0
- package/dist/models/patch-worker.js.map +1 -0
- package/dist/models/react.d.ts +43 -0
- package/dist/models/react.js +73 -0
- package/dist/models/react.js.map +1 -0
- package/dist/models/solid.d.ts +73 -0
- package/dist/models/solid.js +95 -0
- package/dist/models/solid.js.map +1 -0
- package/dist/models/vue.d.ts +51 -0
- package/dist/models/vue.js +98 -0
- package/dist/models/vue.js.map +1 -0
- package/dist/orm-BvkX0aBT.d.ts +507 -0
- package/dist/session-vqOnyxga.d.ts +184 -0
- package/dist/state-BTsp-E4v.d.ts +111 -0
- package/dist/types-Dn0EFMO_.d.ts +293 -0
- package/dist/types-Xb8Ta9tZ.d.ts +155 -0
- package/dist/web3/index.d.ts +820 -0
- package/dist/web3/index.js +1020 -0
- package/dist/web3/index.js.map +1 -0
- package/dist/web3/registry.d.ts +145 -0
- package/dist/web3/registry.js +23 -0
- package/dist/web3/registry.js.map +1 -0
- package/dist/web3/vue.d.ts +174 -0
- package/dist/web3/vue.js +391 -0
- package/dist/web3/vue.js.map +1 -0
- package/package.json +124 -0
package/README.md
ADDED
|
@@ -0,0 +1,2298 @@
|
|
|
1
|
+
# @the_library/public — Complete API Documentation
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [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
|
+
|
|
19
|
+
2. [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
|
+
|
|
28
|
+
3. [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
|
+
|
|
40
|
+
4. [Branding API](#4-branding-api--the_libraryPublicbranding)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 1. Downloader API (`@the_library/public/downloader`)
|
|
45
|
+
|
|
46
|
+
The 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
|
+
|
|
52
|
+
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.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { InitDownloaderV2 } from '@the_library/public/downloader';
|
|
56
|
+
|
|
57
|
+
await 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
|
+
|
|
81
|
+
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).
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { preloadWorkersV2 } from '@the_library/public/downloader';
|
|
85
|
+
|
|
86
|
+
// Call immediately when app boots, before any heavy imports:
|
|
87
|
+
preloadWorkersV2();
|
|
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
|
+
|
|
100
|
+
Fetches, decrypts, and decompresses a PDF by Arweave TX ID. Returns a `Response` object wrapping the decrypted bytes.
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { GetPdf, GetObjectUrl } from '@the_library/public/downloader';
|
|
104
|
+
|
|
105
|
+
const response = await GetPdf(txId);
|
|
106
|
+
const blob = await response.blob();
|
|
107
|
+
const 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
|
+
|
|
126
|
+
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.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { GetImage } from '@the_library/public/downloader';
|
|
130
|
+
|
|
131
|
+
// Get the cover image (first page)
|
|
132
|
+
const coverResponse = await GetImage(txId);
|
|
133
|
+
const imageBlob = await coverResponse.blob();
|
|
134
|
+
const imgSrc = URL.createObjectURL(imageBlob);
|
|
135
|
+
|
|
136
|
+
// Get a specific page
|
|
137
|
+
const 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
|
+
|
|
156
|
+
Pre-downloads and caches a PDF without needing the `Response` object back. Useful for background prefetching or "warm up" operations.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { EnsurePdf } from '@the_library/public/downloader';
|
|
160
|
+
|
|
161
|
+
// Background prefetch (fire and forget)
|
|
162
|
+
EnsurePdf(txId).catch(e => console.warn('Prefetch failed:', e));
|
|
163
|
+
|
|
164
|
+
// Wait for it
|
|
165
|
+
await 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
|
+
|
|
179
|
+
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.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { EnsurePageImage } from '@the_library/public/downloader';
|
|
183
|
+
|
|
184
|
+
// Render pages 2-5 in the background while user reads page 1
|
|
185
|
+
for (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
|
+
|
|
202
|
+
Triggers a browser "Save As" dialog and writes the decrypted PDF to the user's local Downloads folder.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { DownloadPdfToDisk } from '@the_library/public/downloader';
|
|
206
|
+
|
|
207
|
+
await 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
|
+
|
|
224
|
+
Converts a `GetPdf` or `GetImage` response to a blob URL suitable for `<iframe src="">`, `<embed src="">`, or `<img src="">`.
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { GetPdf, GetObjectUrl } from '@the_library/public/downloader';
|
|
228
|
+
|
|
229
|
+
const pdfResponse = await GetPdf(txId);
|
|
230
|
+
const 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
|
+
|
|
243
|
+
Explicitly revokes a blob URL created by `GetObjectUrl` to release memory.
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
const blobUrl = await GetObjectUrl(response);
|
|
247
|
+
// ... later ...
|
|
248
|
+
revokeObjectUrl(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
|
+
|
|
262
|
+
Queries the browser's storage quota and current usage.
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { storageEstimate } from '@the_library/public/downloader';
|
|
266
|
+
|
|
267
|
+
const { usage, quota } = await storageEstimate();
|
|
268
|
+
const percentUsed = (usage / quota) * 100;
|
|
269
|
+
console.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
|
+
|
|
282
|
+
Reads cached book metadata (title, page count, etc.) from IndexedDB.
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { getBookData } from '@the_library/public/downloader';
|
|
286
|
+
|
|
287
|
+
const bookData = await getBookData(txId);
|
|
288
|
+
if (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
|
|
300
|
+
interface 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
|
+
|
|
312
|
+
Retrieves all cached book metadata records from IndexedDB.
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { getAllBookData } from '@the_library/public/downloader';
|
|
316
|
+
|
|
317
|
+
const allBooks = await getAllBookData();
|
|
318
|
+
console.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
|
+
|
|
329
|
+
Reads the download progress record for a TX ID.
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import { getDownloadRecord } from '@the_library/public/downloader';
|
|
333
|
+
|
|
334
|
+
const record = await getDownloadRecord(txId);
|
|
335
|
+
if (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
|
|
347
|
+
interface 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
|
+
|
|
359
|
+
Retrieves all download progress records.
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { getAllDownloadRecords } from '@the_library/public/downloader';
|
|
363
|
+
|
|
364
|
+
const records = await getAllDownloadRecords();
|
|
365
|
+
console.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
|
+
|
|
378
|
+
The 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
|
|
381
|
+
import { downloadState } from '@the_library/public/downloader';
|
|
382
|
+
|
|
383
|
+
// Subscribe to changes
|
|
384
|
+
const unsubscribe = downloadState.subscribe((changedTxId) => {
|
|
385
|
+
console.log(`File state changed for ${changedTxId}`);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// Read current state
|
|
389
|
+
const fileState = downloadState.get(txId);
|
|
390
|
+
if (fileState?.phase === 'done') {
|
|
391
|
+
console.log('Download complete');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Read render state (page images, book metadata)
|
|
395
|
+
const renderState = downloadState.getRender(txId);
|
|
396
|
+
console.log(`${renderState.pages.size} page images cached`);
|
|
397
|
+
|
|
398
|
+
// Global summary
|
|
399
|
+
const summary = downloadState.summary();
|
|
400
|
+
console.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
|
|
415
|
+
interface 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
|
|
426
|
+
interface 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
|
+
|
|
436
|
+
Removes a cached PDF from Cache API storage.
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
import { deletePdf } from '@the_library/public/downloader';
|
|
440
|
+
|
|
441
|
+
const deleted = await deletePdf(txId);
|
|
442
|
+
console.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
|
+
|
|
454
|
+
Removes all cached page preview images for a TX ID.
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
import { deletePreview } from '@the_library/public/downloader';
|
|
458
|
+
|
|
459
|
+
await 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
|
+
|
|
471
|
+
Removes the download progress record from IndexedDB.
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
import { deleteDownloadRecord } from '@the_library/public/downloader';
|
|
475
|
+
|
|
476
|
+
await 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
|
+
|
|
488
|
+
Raw cache and IndexedDB access (rarely called directly; use the higher-level APIs above when possible).
|
|
489
|
+
|
|
490
|
+
#### `openPdfCache(): Promise<Cache>`
|
|
491
|
+
|
|
492
|
+
Gets the Cache API store for PDFs.
|
|
493
|
+
|
|
494
|
+
```typescript
|
|
495
|
+
const cache = await openPdfCache();
|
|
496
|
+
const keys = await cache.keys();
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### `openPreviewCache(): Promise<Cache>`
|
|
500
|
+
|
|
501
|
+
Gets the Cache API store for preview images.
|
|
502
|
+
|
|
503
|
+
#### `openPagesCache(): Promise<Cache>`
|
|
504
|
+
|
|
505
|
+
Gets the Cache API store for individual page images.
|
|
506
|
+
|
|
507
|
+
#### `matchPdf(txId: string): Promise<Response | undefined>`
|
|
508
|
+
|
|
509
|
+
Low-level cache lookup for a single PDF.
|
|
510
|
+
|
|
511
|
+
#### `matchPreview(txId: string): Promise<Response | undefined>`
|
|
512
|
+
|
|
513
|
+
Low-level cache lookup for a cover/first-page image.
|
|
514
|
+
|
|
515
|
+
#### `matchPage(txId: string, pageNumber: number): Promise<Response | undefined>`
|
|
516
|
+
|
|
517
|
+
Low-level cache lookup for a specific page image.
|
|
518
|
+
|
|
519
|
+
#### `putPdf(txId: string, response: Response): Promise<void>`
|
|
520
|
+
|
|
521
|
+
Low-level cache write for a PDF.
|
|
522
|
+
|
|
523
|
+
#### `putPreview(txId: string, response: Response): Promise<void>`
|
|
524
|
+
|
|
525
|
+
Low-level cache write for a cover image.
|
|
526
|
+
|
|
527
|
+
#### `putPage(txId: string, pageNumber: number, response: Response): Promise<void>`
|
|
528
|
+
|
|
529
|
+
Low-level cache write for a page image.
|
|
530
|
+
|
|
531
|
+
#### `pdfKeys(): Promise<string[]>`
|
|
532
|
+
|
|
533
|
+
Lists all cached PDF keys.
|
|
534
|
+
|
|
535
|
+
#### `previewKeys(): Promise<string[]>`
|
|
536
|
+
|
|
537
|
+
Lists all cached preview-image keys.
|
|
538
|
+
|
|
539
|
+
#### `pageKeys(): Promise<string[]>`
|
|
540
|
+
|
|
541
|
+
Lists all cached page-image keys.
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
### Keys & Naming
|
|
546
|
+
|
|
547
|
+
#### `pdfKey(txId: string): string`
|
|
548
|
+
|
|
549
|
+
Generates the cache key for a PDF.
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
const key = pdfKey(txId); // e.g., "arweave_pdf_{txId}"
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
#### `previewKey(txId: string): string`
|
|
556
|
+
|
|
557
|
+
Generates the cache key for a cover image.
|
|
558
|
+
|
|
559
|
+
#### `pageKey(txId: string, pageNumber: number): string`
|
|
560
|
+
|
|
561
|
+
Generates the cache key for a specific page.
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
const key = pageKey(txId, 5); // e.g., "arweave_page_5_{txId}"
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
#### `CACHES` (constant)
|
|
568
|
+
|
|
569
|
+
```typescript
|
|
570
|
+
import { 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
|
|
582
|
+
import { CACHE_VERSION } from '@the_library/public/downloader';
|
|
583
|
+
// CACHE_VERSION = 1
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
#### `DL2_SW_VERSION` (constant)
|
|
587
|
+
|
|
588
|
+
Service 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
|
|
597
|
+
import {
|
|
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
|
|
605
|
+
const status = useFileStatus(txId); // ComputedRef<FileState | undefined>
|
|
606
|
+
|
|
607
|
+
// Reactive ref for render/page state
|
|
608
|
+
const render = useRenderStatus(txId); // ComputedRef<RenderState | undefined>
|
|
609
|
+
|
|
610
|
+
// Global download progress
|
|
611
|
+
const progress = useGlobalProgress(); // ComputedRef<DownloadSummary>
|
|
612
|
+
|
|
613
|
+
// LRU pruning activity
|
|
614
|
+
const pruning = usePruning(); // ComputedRef<PruningState>
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
#### React Hooks
|
|
618
|
+
|
|
619
|
+
```typescript
|
|
620
|
+
import {
|
|
621
|
+
useFileStatus,
|
|
622
|
+
useRenderStatus,
|
|
623
|
+
useGlobalProgress,
|
|
624
|
+
usePruning
|
|
625
|
+
} from '@the_library/public/downloader/react';
|
|
626
|
+
|
|
627
|
+
// Synchronized external store hooks
|
|
628
|
+
const status = useFileStatus(txId); // FileState | undefined
|
|
629
|
+
const render = useRenderStatus(txId); // RenderState | undefined
|
|
630
|
+
const progress = useGlobalProgress(); // DownloadSummary
|
|
631
|
+
const pruning = usePruning(); // PruningState
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
#### Solid.js Primitives
|
|
635
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
import {
|
|
638
|
+
useFileStatus,
|
|
639
|
+
useRenderStatus,
|
|
640
|
+
useGlobalProgress,
|
|
641
|
+
usePruning
|
|
642
|
+
} from '@the_library/public/downloader/solid';
|
|
643
|
+
|
|
644
|
+
// Solid createMemo/createSignal-based reactivity
|
|
645
|
+
const [status] = useFileStatus(txId);
|
|
646
|
+
const [render] = useRenderStatus(txId);
|
|
647
|
+
const [progress] = useGlobalProgress();
|
|
648
|
+
const [pruning] = usePruning();
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
### Service Worker API (`@the_library/public/downloader/sw`)
|
|
654
|
+
|
|
655
|
+
Installs a service worker that intercepts PDF/image requests, serving cached responses offline and persisting downloads across tab reloads.
|
|
656
|
+
|
|
657
|
+
#### `registerDownloaderSW(): Promise<ServiceWorkerState>`
|
|
658
|
+
|
|
659
|
+
Registers (or reuses) the downloader service worker.
|
|
660
|
+
|
|
661
|
+
```typescript
|
|
662
|
+
import { registerDownloaderSW } from '@the_library/public/downloader/sw';
|
|
663
|
+
|
|
664
|
+
const state = await registerDownloaderSW();
|
|
665
|
+
if (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
|
|
676
|
+
interface 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
|
+
|
|
689
|
+
Checks current service worker status without re-registering.
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
import { swStatus } from '@the_library/public/downloader/sw';
|
|
693
|
+
|
|
694
|
+
const state = await swStatus();
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**Parameters:** None
|
|
698
|
+
|
|
699
|
+
**Returns:** `Promise<ServiceWorkerState>`
|
|
700
|
+
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
#### `clearDownloaderCaches(): Promise<void>`
|
|
704
|
+
|
|
705
|
+
Drops all Cache API entries (PDFs, page images, previews), but keeps IndexedDB metadata records intact.
|
|
706
|
+
|
|
707
|
+
```typescript
|
|
708
|
+
import { clearDownloaderCaches } from '@the_library/public/downloader/sw';
|
|
709
|
+
|
|
710
|
+
await clearDownloaderCaches();
|
|
711
|
+
console.log('Cache storage cleared, IndexedDB preserved');
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
**Parameters:** None
|
|
715
|
+
|
|
716
|
+
**Returns:** `Promise<void>`
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
#### `nukeDownloaderStorage(): Promise<void>`
|
|
721
|
+
|
|
722
|
+
Full 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
|
|
725
|
+
import { nukeDownloaderStorage } from '@the_library/public/downloader/sw';
|
|
726
|
+
|
|
727
|
+
await nukeDownloaderStorage();
|
|
728
|
+
console.log('All download data erased');
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
**Parameters:** None
|
|
732
|
+
|
|
733
|
+
**Returns:** `Promise<void>`
|
|
734
|
+
|
|
735
|
+
---
|
|
736
|
+
|
|
737
|
+
#### `unregisterDownloaderSW(): Promise<void>`
|
|
738
|
+
|
|
739
|
+
Unregisters the service worker.
|
|
740
|
+
|
|
741
|
+
```typescript
|
|
742
|
+
import { unregisterDownloaderSW } from '@the_library/public/downloader/sw';
|
|
743
|
+
|
|
744
|
+
await unregisterDownloaderSW();
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
**Parameters:** None
|
|
748
|
+
|
|
749
|
+
**Returns:** `Promise<void>`
|
|
750
|
+
|
|
751
|
+
---
|
|
752
|
+
|
|
753
|
+
## 2. Models API (`@the_library/public/models`)
|
|
754
|
+
|
|
755
|
+
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}`.
|
|
756
|
+
|
|
757
|
+
### Core Types & Registry
|
|
758
|
+
|
|
759
|
+
#### `Registry` (singleton, exported from `registry.ts`)
|
|
760
|
+
|
|
761
|
+
Holds enriched `ModelDefinition` for every registered object type — property IDs, field schemas, and relations.
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
import { Registry, type ModelDefinition } from '@the_library/public/models';
|
|
765
|
+
|
|
766
|
+
// Get schema by numeric type ID
|
|
767
|
+
const bookSchema: ModelDefinition = Registry.get(Book.type);
|
|
768
|
+
// bookSchema.name, bookSchema.typePrefix, bookSchema.props, bookSchema.instanceFields, ...
|
|
769
|
+
|
|
770
|
+
// Get schema by class name
|
|
771
|
+
const 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
|
|
784
|
+
interface 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
|
+
|
|
800
|
+
Sets the in-memory LRU cache size. Call before loading any records.
|
|
801
|
+
|
|
802
|
+
```typescript
|
|
803
|
+
import { configureDb } from '@the_library/public/models';
|
|
804
|
+
|
|
805
|
+
// Lower cache limit for mobile devices
|
|
806
|
+
configureDb({ 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
|
+
|
|
818
|
+
Synchronously retrieves a record from the cache.
|
|
819
|
+
|
|
820
|
+
```typescript
|
|
821
|
+
import { getRecord } from '@the_library/public/models';
|
|
822
|
+
import { Book } from '@the_library/public/models';
|
|
823
|
+
|
|
824
|
+
if (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
|
+
|
|
842
|
+
Adds or updates a record in the cache.
|
|
843
|
+
|
|
844
|
+
```typescript
|
|
845
|
+
import { addRecord } from '@the_library/public/models';
|
|
846
|
+
|
|
847
|
+
addRecord(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
|
+
|
|
861
|
+
Removes a record from the cache.
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
import { removeRecord } from '@the_library/public/models';
|
|
865
|
+
|
|
866
|
+
removeRecord(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
|
+
|
|
879
|
+
Checks if a record is currently cached.
|
|
880
|
+
|
|
881
|
+
```typescript
|
|
882
|
+
import { hasRecord } from '@the_library/public/models';
|
|
883
|
+
|
|
884
|
+
if (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
|
+
|
|
899
|
+
Pins a record so it won't be evicted from the LRU cache. Use for the currently-open record, search results, etc.
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
import { pinRecord } from '@the_library/public/models';
|
|
903
|
+
|
|
904
|
+
// User opened a book — don't evict it while they're reading
|
|
905
|
+
pinRecord(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
|
+
|
|
918
|
+
Unpins a record so it can be evicted again.
|
|
919
|
+
|
|
920
|
+
```typescript
|
|
921
|
+
import { unpinRecord } from '@the_library/public/models';
|
|
922
|
+
|
|
923
|
+
// User closed the book
|
|
924
|
+
unpinRecord(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
|
+
|
|
937
|
+
Installs a framework-agnostic reactivity hook that fires when records change. Framework adapters call this once at boot.
|
|
938
|
+
|
|
939
|
+
```typescript
|
|
940
|
+
import { addReactivitySystem } from '@the_library/public/models';
|
|
941
|
+
|
|
942
|
+
addReactivitySystem({
|
|
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
|
+
|
|
952
|
+
Replaces the current reactivity system. Typically called by framework adapters during initialization.
|
|
953
|
+
|
|
954
|
+
---
|
|
955
|
+
|
|
956
|
+
### Generated Models
|
|
957
|
+
|
|
958
|
+
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.
|
|
959
|
+
|
|
960
|
+
```typescript
|
|
961
|
+
import { 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)
|
|
965
|
+
const book = NewBookWithId('book:abc123', 'username');
|
|
966
|
+
book.title = 'A Study in Scarlet';
|
|
967
|
+
book.author = 'Sir Arthur Conan Doyle';
|
|
968
|
+
|
|
969
|
+
// Load an existing record from disk
|
|
970
|
+
const loaded = await Book.LoadAsync('book:abc123');
|
|
971
|
+
|
|
972
|
+
// Check type at runtime
|
|
973
|
+
if (loaded instanceof Book) {
|
|
974
|
+
console.log('It is a book');
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Generic record hydration (type unknown at compile time)
|
|
978
|
+
const typeId = 10; // some numeric type
|
|
979
|
+
const RecordClass = ModelTypes[typeId];
|
|
980
|
+
const 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
|
+
|
|
995
|
+
The `Registry` singleton (above) holds `ModelDefinition` for each type. Accessed via `Registry.get(typeId)` or `Registry.getByName(name)`.
|
|
996
|
+
|
|
997
|
+
---
|
|
998
|
+
|
|
999
|
+
### Trash Index
|
|
1000
|
+
|
|
1001
|
+
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.
|
|
1002
|
+
|
|
1003
|
+
```typescript
|
|
1004
|
+
import { 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
|
|
1015
|
+
import { Tag } from '@the_library/public/models/models';
|
|
1016
|
+
import { trashStore } from '@the_library/public/models';
|
|
1017
|
+
|
|
1018
|
+
// All ids currently trashed for Tag, merged across every bucketed username
|
|
1019
|
+
const allTrashedTagIds = trashStore.list(Tag.type);
|
|
1020
|
+
|
|
1021
|
+
// Merge only specific usernames' buckets
|
|
1022
|
+
const mineOnly = trashStore.list(Tag.type, ['mine']);
|
|
1023
|
+
|
|
1024
|
+
const count = trashStore.count(Tag.type);
|
|
1025
|
+
const buckets = trashStore.usernames(Tag.type); // e.g. ['canonical']
|
|
1026
|
+
|
|
1027
|
+
const 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
|
|
1046
|
+
interface 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
|
+
|
|
1058
|
+
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.
|
|
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
|
|
1071
|
+
import {
|
|
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
|
+
|
|
1082
|
+
Initializes the models subsystem once at app boot. Idempotent across Astro `<ClientRouter />` navigations.
|
|
1083
|
+
|
|
1084
|
+
```typescript
|
|
1085
|
+
await 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
|
+
|
|
1106
|
+
Reactive per-username level state and corpus metadata.
|
|
1107
|
+
|
|
1108
|
+
```typescript
|
|
1109
|
+
import { useLevel } from '@the_library/public/models/vue';
|
|
1110
|
+
|
|
1111
|
+
const { level, progress } = useLevel('alice').value;
|
|
1112
|
+
console.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
|
|
1122
|
+
interface 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
|
+
|
|
1134
|
+
Tracks edits to a model instance before committing them back to the original.
|
|
1135
|
+
|
|
1136
|
+
```typescript
|
|
1137
|
+
import { useDraft } from '@the_library/public/models/vue';
|
|
1138
|
+
|
|
1139
|
+
const { draft, isDirty, commit, rollback } = useDraft(() => book);
|
|
1140
|
+
|
|
1141
|
+
// Edit the draft
|
|
1142
|
+
draft.value.title = 'New Title';
|
|
1143
|
+
|
|
1144
|
+
// Check if unsaved changes exist
|
|
1145
|
+
if (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
|
+
|
|
1168
|
+
Reactive 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
|
|
1171
|
+
import { useTrash } from '@the_library/public/models/vue';
|
|
1172
|
+
import { Tag } from '@the_library/public/models/models';
|
|
1173
|
+
|
|
1174
|
+
const { 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
|
+
|
|
1193
|
+
Clears the session and cached level state. Used for logout and testing/HMR.
|
|
1194
|
+
|
|
1195
|
+
```typescript
|
|
1196
|
+
import { resetModelsV2Session } from '@the_library/public/models/vue';
|
|
1197
|
+
|
|
1198
|
+
resetModelsV2Session();
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
---
|
|
1202
|
+
|
|
1203
|
+
#### React Hooks
|
|
1204
|
+
|
|
1205
|
+
Same API as Vue, but exported from `@the_library/public/models/react`:
|
|
1206
|
+
|
|
1207
|
+
```typescript
|
|
1208
|
+
import {
|
|
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
|
+
|
|
1219
|
+
Hook returning the current level data. Re-renders when level changes.
|
|
1220
|
+
|
|
1221
|
+
**`useModelUpdate(record: ModelInstance): boolean`**
|
|
1222
|
+
|
|
1223
|
+
Subscribes 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
|
+
|
|
1227
|
+
Re-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
|
|
1230
|
+
import { useTrash } from '@the_library/public/models/react';
|
|
1231
|
+
import { Tag } from '@the_library/public/models/models';
|
|
1232
|
+
|
|
1233
|
+
const { 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
|
|
1243
|
+
import {
|
|
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
|
+
|
|
1254
|
+
Signal factory returning the current level data.
|
|
1255
|
+
|
|
1256
|
+
**`subscribeRecord(record: ModelInstance, callback: (record) => void): () => void`**
|
|
1257
|
+
|
|
1258
|
+
Manual subscription to record updates. Returns unsubscribe function.
|
|
1259
|
+
|
|
1260
|
+
**`useTrash(objectType: number, usernames?: string[]): { count: () => number; getIds: () => number[] }`**
|
|
1261
|
+
|
|
1262
|
+
Signal 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
|
|
1265
|
+
import { useTrash } from '@the_library/public/models/solid';
|
|
1266
|
+
import { Tag } from '@the_library/public/models/models';
|
|
1267
|
+
|
|
1268
|
+
const { count, getIds } = useTrash(Tag.type);
|
|
1269
|
+
return <span>{count()}</span>;
|
|
1270
|
+
```
|
|
1271
|
+
|
|
1272
|
+
---
|
|
1273
|
+
|
|
1274
|
+
#### Astro / Static Generation
|
|
1275
|
+
|
|
1276
|
+
```typescript
|
|
1277
|
+
import {
|
|
1278
|
+
bootstrapModelsV2,
|
|
1279
|
+
resetModelsV2Session,
|
|
1280
|
+
subscribeRecord
|
|
1281
|
+
} from '@the_library/public/models/astro';
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
**`bootstrapModelsV2(options): Promise<void>`**
|
|
1285
|
+
|
|
1286
|
+
Same as Vue/React.
|
|
1287
|
+
|
|
1288
|
+
**`subscribeRecord(record, callback): () => void`**
|
|
1289
|
+
|
|
1290
|
+
Manual 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
|
+
|
|
1298
|
+
Low-level APIs (not normally called directly; used by `bootstrapModelsV2`):
|
|
1299
|
+
|
|
1300
|
+
#### `serializePatches(patches: Patch[]): Uint8Array`
|
|
1301
|
+
|
|
1302
|
+
Encodes patches to binary for transmission or storage.
|
|
1303
|
+
|
|
1304
|
+
#### `deserializePatches(data: Uint8Array): Patch[]`
|
|
1305
|
+
|
|
1306
|
+
Decodes binary patches.
|
|
1307
|
+
|
|
1308
|
+
#### `applyRestoredPatches(patches: Patch[], models: ModelFactory[]): void`
|
|
1309
|
+
|
|
1310
|
+
Applies a set of deserialized patches to the in-memory records.
|
|
1311
|
+
|
|
1312
|
+
#### `getPatchInstanceByAccount(username: string): Patches`
|
|
1313
|
+
|
|
1314
|
+
Retrieves the patch store for a specific account.
|
|
1315
|
+
|
|
1316
|
+
---
|
|
1317
|
+
|
|
1318
|
+
## 3. Web3 API (`@the_library/public/web3`)
|
|
1319
|
+
|
|
1320
|
+
The 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
|
+
|
|
1324
|
+
Manages wallet accounts, session persistence, and the centralized write-transaction pipeline.
|
|
1325
|
+
|
|
1326
|
+
#### `sessionStore` (singleton)
|
|
1327
|
+
|
|
1328
|
+
The canonical, framework-agnostic store for known and active wallet accounts.
|
|
1329
|
+
|
|
1330
|
+
```typescript
|
|
1331
|
+
import { sessionStore } from '@the_library/public/web3';
|
|
1332
|
+
|
|
1333
|
+
const { active, known } = sessionStore.getState();
|
|
1334
|
+
console.log(`Active account: ${active?.account}`);
|
|
1335
|
+
console.log(`${known.length} known accounts`);
|
|
1336
|
+
|
|
1337
|
+
// React to changes
|
|
1338
|
+
const 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
|
|
1352
|
+
interface 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
|
+
|
|
1378
|
+
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`.
|
|
1379
|
+
|
|
1380
|
+
```typescript
|
|
1381
|
+
import { syncAccount } from '@the_library/public/web3';
|
|
1382
|
+
|
|
1383
|
+
const account = await syncAccount('0xabc...', 1116, 'metamask');
|
|
1384
|
+
console.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
|
+
|
|
1403
|
+
Reactive wrapper over `sessionStore`.
|
|
1404
|
+
|
|
1405
|
+
```typescript
|
|
1406
|
+
import { useWeb3Session } from '@the_library/public/web3/vue';
|
|
1407
|
+
|
|
1408
|
+
const {
|
|
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
|
+
|
|
1421
|
+
Typed read and write adapters over deployed contracts.
|
|
1422
|
+
|
|
1423
|
+
#### `getReadClient(chainId: number, environment?: 'production' | 'staging'): ReadOnlyDomainAPI`
|
|
1424
|
+
|
|
1425
|
+
Gets the read-only client for a chain. Blocks until contract addresses/ABIs are loaded from the registry.
|
|
1426
|
+
|
|
1427
|
+
```typescript
|
|
1428
|
+
import { getReadClient } from '@the_library/public/web3';
|
|
1429
|
+
|
|
1430
|
+
const client = getReadClient(1116);
|
|
1431
|
+
await client.ready; // wait for registry load
|
|
1432
|
+
|
|
1433
|
+
const balance = await client.projectManagerStorage.balanceOfAddress('0xabc...');
|
|
1434
|
+
const level = await client.archivistStorage.userLevel(userId);
|
|
1435
|
+
const 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
|
|
1446
|
+
interface 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
|
+
|
|
1469
|
+
Account 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
|
+
|
|
1488
|
+
User 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
|
+
|
|
1499
|
+
Anonymized 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
|
+
|
|
1509
|
+
Project 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
|
+
|
|
1522
|
+
Global 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
|
+
|
|
1533
|
+
Dynamic 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
|
+
|
|
1544
|
+
Who-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
|
+
|
|
1555
|
+
Library 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
|
+
|
|
1565
|
+
Library 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
|
+
|
|
1580
|
+
Public 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
|
+
|
|
1590
|
+
Public 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
|
|
1600
|
+
import { writeOperations, resetTransactionState } from '@the_library/public/web3';
|
|
1601
|
+
|
|
1602
|
+
await writeOperations.register(walletType, chainId, address, registrationFee);
|
|
1603
|
+
await writeOperations.vote(walletType, chainId, address, projectId, language, voteAmount);
|
|
1604
|
+
await writeOperations.save(walletType, chainId, address, compressedActions, textIndexes, textData, level);
|
|
1605
|
+
await writeOperations.subscribe(walletType, chainId, address, subscriberUserId, targetUserId);
|
|
1606
|
+
await writeOperations.unsubscribe(walletType, chainId, address, subscriberUserId, targetUserId);
|
|
1607
|
+
```
|
|
1608
|
+
|
|
1609
|
+
All 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
|
+
|
|
1614
|
+
If a transaction throws and the run-lock needs to be cleared manually, call `resetTransactionState()`.
|
|
1615
|
+
|
|
1616
|
+
---
|
|
1617
|
+
|
|
1618
|
+
#### Chain Metadata
|
|
1619
|
+
|
|
1620
|
+
```typescript
|
|
1621
|
+
import { getChain, EVM_CHAINS, DEFAULT_CHAIN_ID } from '@the_library/public/web3';
|
|
1622
|
+
|
|
1623
|
+
const 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
|
+
|
|
1627
|
+
const deployedChains = Object.values(EVM_CHAINS).filter(c => c.deployed);
|
|
1628
|
+
```
|
|
1629
|
+
|
|
1630
|
+
**Type: `EvmChainConfig`**
|
|
1631
|
+
```typescript
|
|
1632
|
+
interface 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
|
|
1651
|
+
import { useChain } from '@the_library/public/web3/vue';
|
|
1652
|
+
|
|
1653
|
+
const { 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
|
+
|
|
1666
|
+
Dynamic contract address and ABI resolution from the on-chain `AddressRegistry`.
|
|
1667
|
+
|
|
1668
|
+
#### `addressLoader` (singleton)
|
|
1669
|
+
|
|
1670
|
+
```typescript
|
|
1671
|
+
import { addressLoader, setRegistryBaseUrl } from '@the_library/public/web3';
|
|
1672
|
+
|
|
1673
|
+
// Set custom registry host (before anything else runs)
|
|
1674
|
+
setRegistryBaseUrl('https://registry.world.bibliotech.com');
|
|
1675
|
+
|
|
1676
|
+
// Subscribe to per-contract load progress
|
|
1677
|
+
const 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
|
|
1682
|
+
const addr = addressLoader.getAddress(chainId, 'Factory');
|
|
1683
|
+
const 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
|
+
|
|
1700
|
+
Override 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
|
+
|
|
1706
|
+
Walks the on-chain subscription graph (who-follows-whom) and incrementally fetches patches.
|
|
1707
|
+
|
|
1708
|
+
#### `getSubscriptionClient(chainId: number): SubscriptionClient`
|
|
1709
|
+
|
|
1710
|
+
Gets a chain-scoped subscription client.
|
|
1711
|
+
|
|
1712
|
+
```typescript
|
|
1713
|
+
import { getSubscriptionClient } from '@the_library/public/web3';
|
|
1714
|
+
|
|
1715
|
+
const subs = getSubscriptionClient(1116);
|
|
1716
|
+
|
|
1717
|
+
// First sync: full BFS walk + patch fetch
|
|
1718
|
+
const { patches, cursor, truncated } = await subs.sync(rootUserId);
|
|
1719
|
+
|
|
1720
|
+
// Later: incremental — only fetch new patches since cursor
|
|
1721
|
+
const next = await subs.syncSince(cursor);
|
|
1722
|
+
|
|
1723
|
+
// Query the graph
|
|
1724
|
+
const followees = await subs.getSubscriptions(userId);
|
|
1725
|
+
const 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
|
|
1740
|
+
interface 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
|
|
1752
|
+
import { cursorStore } from '@the_library/public/web3';
|
|
1753
|
+
|
|
1754
|
+
const cursor = await cursorStore.get(userId);
|
|
1755
|
+
const result = cursor
|
|
1756
|
+
? await subs.syncSince(cursor)
|
|
1757
|
+
: await subs.sync(userId);
|
|
1758
|
+
await 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
|
|
1772
|
+
import { useSubscriptionSync } from '@the_library/public/web3/vue';
|
|
1773
|
+
|
|
1774
|
+
const { 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
|
+
|
|
1785
|
+
Static ABI exports for contracts that don't use dynamic resolution.
|
|
1786
|
+
|
|
1787
|
+
```typescript
|
|
1788
|
+
import { addressRegistryAbi, pythAbi } from '@the_library/public/web3';
|
|
1789
|
+
|
|
1790
|
+
// Use with viem directly:
|
|
1791
|
+
import { createPublicClient, http } from 'viem';
|
|
1792
|
+
|
|
1793
|
+
const client = createPublicClient({
|
|
1794
|
+
chain: /*...*/,
|
|
1795
|
+
transport: http()
|
|
1796
|
+
});
|
|
1797
|
+
|
|
1798
|
+
const 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
|
+
|
|
1810
|
+
Typed pub/sub bus for wallet and transaction state notifications.
|
|
1811
|
+
|
|
1812
|
+
#### `WalletEvents` (typed pub/sub)
|
|
1813
|
+
|
|
1814
|
+
```typescript
|
|
1815
|
+
import { WalletEvents, type WalletState, type WalletError } from '@the_library/public/web3';
|
|
1816
|
+
|
|
1817
|
+
const 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
|
+
|
|
1824
|
+
const unsubError = WalletEvents.onWalletError((e) => {
|
|
1825
|
+
// e: { wallet, chainId, reason: WalletError, timestamp }
|
|
1826
|
+
toast.error(e.reason);
|
|
1827
|
+
});
|
|
1828
|
+
|
|
1829
|
+
const unsubSaveRestored = WalletEvents.onSaveRestored((e) => {
|
|
1830
|
+
// e: { compressedActions, indexes, stringIndexes, timestamp }
|
|
1831
|
+
// Patches from on-chain state that outran local edits
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1834
|
+
const unsubPatchesSynced = WalletEvents.onSubscriptionPatchesSynced((e) => {
|
|
1835
|
+
// e: { rootUserId, patches: Patch[], timestamp }
|
|
1836
|
+
// Subscription graph patches fetched in background
|
|
1837
|
+
});
|
|
1838
|
+
|
|
1839
|
+
// Unsubscribe
|
|
1840
|
+
unsubWalletState();
|
|
1841
|
+
unsubError();
|
|
1842
|
+
unsubSaveRestored();
|
|
1843
|
+
unsubPatchesSynced();
|
|
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
|
|
1870
|
+
import { getEventBus } from '@the_library/public/web3';
|
|
1871
|
+
|
|
1872
|
+
const bus = getEventBus();
|
|
1873
|
+
const unsubscribe = bus.on('topic', (data) => { /* handle */ });
|
|
1874
|
+
bus.emit('topic', data);
|
|
1875
|
+
bus.once('topic', (data) => { /* one-time listener */ });
|
|
1876
|
+
bus.clear('topic'); // remove all listeners
|
|
1877
|
+
```
|
|
1878
|
+
|
|
1879
|
+
---
|
|
1880
|
+
|
|
1881
|
+
### 3.7 Wallets API
|
|
1882
|
+
|
|
1883
|
+
Wallet connectors (MetaMask, PrivateKey) and metadata.
|
|
1884
|
+
|
|
1885
|
+
#### `getConnector(type: WalletType): WalletConnector | undefined`
|
|
1886
|
+
|
|
1887
|
+
Retrieves a registered wallet connector by type.
|
|
1888
|
+
|
|
1889
|
+
```typescript
|
|
1890
|
+
import { getConnector } from '@the_library/public/web3';
|
|
1891
|
+
|
|
1892
|
+
const metamask = getConnector('metamask');
|
|
1893
|
+
if (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
|
+
|
|
1907
|
+
Lists all registered connectors.
|
|
1908
|
+
|
|
1909
|
+
```typescript
|
|
1910
|
+
import { getRegisteredConnectors } from '@the_library/public/web3';
|
|
1911
|
+
|
|
1912
|
+
const connectors = getRegisteredConnectors();
|
|
1913
|
+
// array of MetaMaskConnector, PrivateKeyConnector, etc.
|
|
1914
|
+
```
|
|
1915
|
+
|
|
1916
|
+
---
|
|
1917
|
+
|
|
1918
|
+
#### `walletMeta` (metadata for each wallet)
|
|
1919
|
+
|
|
1920
|
+
```typescript
|
|
1921
|
+
import { walletMeta } from '@the_library/public/web3';
|
|
1922
|
+
|
|
1923
|
+
const 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
|
+
|
|
1934
|
+
if (!meta.isInstalled() && meta.isInstallable()) {
|
|
1935
|
+
window.open(meta.getInstallUrl(), '_blank');
|
|
1936
|
+
}
|
|
1937
|
+
```
|
|
1938
|
+
|
|
1939
|
+
---
|
|
1940
|
+
|
|
1941
|
+
#### Private Key Storage
|
|
1942
|
+
|
|
1943
|
+
```typescript
|
|
1944
|
+
import { storePrivateKey, hasPrivateKey, hasAnyPrivateKey } from '@the_library/public/web3';
|
|
1945
|
+
|
|
1946
|
+
storePrivateKey(1116, '0x1234...'); // raw hex, no '0x' prefix required
|
|
1947
|
+
if (hasPrivateKey(1116)) {
|
|
1948
|
+
// PrivateKeyConnector can connect to this chain
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
if (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
|
+
|
|
1962
|
+
Project discovery and voting.
|
|
1963
|
+
|
|
1964
|
+
#### `loadProject(chainId: number, language: string, projectId: number, refresh?: boolean): Promise<ProjectLocale>`
|
|
1965
|
+
|
|
1966
|
+
Loads a single project by ID with language fallback. Cached automatically.
|
|
1967
|
+
|
|
1968
|
+
```typescript
|
|
1969
|
+
import { loadProject } from '@the_library/public/web3';
|
|
1970
|
+
|
|
1971
|
+
const project = await loadProject(1116, 'eng', 5);
|
|
1972
|
+
console.log(`${project.version.name} — ${project.balance} tokens`);
|
|
1973
|
+
|
|
1974
|
+
// Force re-fetch from chain, bypassing cache
|
|
1975
|
+
const 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
|
+
|
|
1992
|
+
Loads all projects for a chain/language. Returns cached projects if chain is unreachable (with console warning).
|
|
1993
|
+
|
|
1994
|
+
```typescript
|
|
1995
|
+
import { loadProjects } from '@the_library/public/web3';
|
|
1996
|
+
|
|
1997
|
+
const projects = await loadProjects(1116, 'eng');
|
|
1998
|
+
const 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
|
+
|
|
2011
|
+
Helper to convert raw `Project` data to `ProjectLocale` with language selection and decimal normalization.
|
|
2012
|
+
|
|
2013
|
+
```typescript
|
|
2014
|
+
import { projectLocale } from '@the_library/public/web3';
|
|
2015
|
+
|
|
2016
|
+
const raw = await client.projectManagerStorage.loadProject(5);
|
|
2017
|
+
const 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
|
|
2032
|
+
import { useLoadProjects } from '@the_library/public/web3/vue';
|
|
2033
|
+
|
|
2034
|
+
const chainId = ref(1116);
|
|
2035
|
+
const language = ref('eng');
|
|
2036
|
+
|
|
2037
|
+
const { 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
|
|
2051
|
+
import { 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
|
+
|
|
2064
|
+
The default chain for new users (usually production mainnet, e.g., `1116`).
|
|
2065
|
+
|
|
2066
|
+
---
|
|
2067
|
+
|
|
2068
|
+
#### `getChain(chainId: number): EvmChainConfig`
|
|
2069
|
+
|
|
2070
|
+
Gets full metadata for a chain. Throws if `chainId` is not registered.
|
|
2071
|
+
|
|
2072
|
+
```typescript
|
|
2073
|
+
import { getChain } from '@the_library/public/web3';
|
|
2074
|
+
|
|
2075
|
+
const chain = getChain(1116);
|
|
2076
|
+
console.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
|
+
|
|
2085
|
+
Single app-wide initialization: registers connectors, loads persisted session, hydrates accounts.
|
|
2086
|
+
|
|
2087
|
+
```typescript
|
|
2088
|
+
import { bootstrapWebSDK, BootstrapResultKey } from '@the_library/public/web3/vue';
|
|
2089
|
+
|
|
2090
|
+
const result = await bootstrapWebSDK({
|
|
2091
|
+
withTest: true,
|
|
2092
|
+
environment: 'production',
|
|
2093
|
+
chainFilter: [1116, 1114], // optional: limit to these chains
|
|
2094
|
+
});
|
|
2095
|
+
|
|
2096
|
+
await result.ready; // wait for account discovery
|
|
2097
|
+
|
|
2098
|
+
// Provide at app root
|
|
2099
|
+
app.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
|
|
2111
|
+
interface 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
|
+
|
|
2124
|
+
Retrieves the app-wide bootstrap result (must have been provided via `app.provide(BootstrapResultKey, result)`).
|
|
2125
|
+
|
|
2126
|
+
```typescript
|
|
2127
|
+
import { useBootstrap } from '@the_library/public/web3/vue';
|
|
2128
|
+
|
|
2129
|
+
const { chains, installedConnectors, primaryAccount } = useBootstrap();
|
|
2130
|
+
```
|
|
2131
|
+
|
|
2132
|
+
---
|
|
2133
|
+
|
|
2134
|
+
#### `useWeb3Session()`
|
|
2135
|
+
|
|
2136
|
+
Reactive wrapper over `sessionStore`.
|
|
2137
|
+
|
|
2138
|
+
```typescript
|
|
2139
|
+
import { useWeb3Session } from '@the_library/public/web3/vue';
|
|
2140
|
+
|
|
2141
|
+
const {
|
|
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
|
|
2155
|
+
import { useChain } from '@the_library/public/web3/vue';
|
|
2156
|
+
|
|
2157
|
+
const { 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
|
|
2171
|
+
import { useLoadProjects } from '@the_library/public/web3/vue';
|
|
2172
|
+
|
|
2173
|
+
const { 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
|
|
2184
|
+
import { useSubscriptionSync } from '@the_library/public/web3/vue';
|
|
2185
|
+
|
|
2186
|
+
const { cursor, patches, isLoading, refresh } = useSubscriptionSync(userId);
|
|
2187
|
+
```
|
|
2188
|
+
|
|
2189
|
+
---
|
|
2190
|
+
|
|
2191
|
+
## 4. Branding API (`@the_library/public/branding`)
|
|
2192
|
+
|
|
2193
|
+
Static, framework-agnostic branding and contact data shared across apps.
|
|
2194
|
+
|
|
2195
|
+
```typescript
|
|
2196
|
+
import {
|
|
2197
|
+
globalBranding,
|
|
2198
|
+
worldBiblioTechBranding,
|
|
2199
|
+
dsafeLegalBranding,
|
|
2200
|
+
worldBibliotekBranding,
|
|
2201
|
+
} from '@the_library/public/branding';
|
|
2202
|
+
|
|
2203
|
+
import type { AppBranding, FounderInfo, BuilderInfo, SocialMediaUrls } from '@the_library/public/branding';
|
|
2204
|
+
|
|
2205
|
+
// Access global founder/contact data
|
|
2206
|
+
console.log(globalBranding.founder.email); // 'data@datapond.earth'
|
|
2207
|
+
console.log(globalBranding.founder.name);
|
|
2208
|
+
console.log(globalBranding.builder.name);
|
|
2209
|
+
console.log(globalBranding.socialMedia.x); // 'https://x.com/realDataPond'
|
|
2210
|
+
|
|
2211
|
+
// Access app-specific branding
|
|
2212
|
+
console.log(worldBiblioTechBranding.appName); // 'World Biblio Tech'
|
|
2213
|
+
console.log(worldBiblioTechBranding.url);
|
|
2214
|
+
console.log(worldBiblioTechBranding.images.logo); // asset path
|
|
2215
|
+
```
|
|
2216
|
+
|
|
2217
|
+
**Type: `AppBranding`**
|
|
2218
|
+
```typescript
|
|
2219
|
+
interface 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
|
|
2237
|
+
interface FounderInfo {
|
|
2238
|
+
name: string;
|
|
2239
|
+
email: string;
|
|
2240
|
+
title?: string;
|
|
2241
|
+
}
|
|
2242
|
+
```
|
|
2243
|
+
|
|
2244
|
+
**Type: `BuilderInfo`**
|
|
2245
|
+
```typescript
|
|
2246
|
+
interface BuilderInfo {
|
|
2247
|
+
name: string;
|
|
2248
|
+
title?: string;
|
|
2249
|
+
}
|
|
2250
|
+
```
|
|
2251
|
+
|
|
2252
|
+
**Type: `SocialMediaUrls`**
|
|
2253
|
+
```typescript
|
|
2254
|
+
interface 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
|