@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.
Files changed (90) hide show
  1. package/README.md +2298 -0
  2. package/assets/branding/data-pond.png +0 -0
  3. package/assets/branding/dsafe-logo.png +0 -0
  4. package/assets/branding/founder-photo.jpg +0 -0
  5. package/assets/branding/icon_transparent.png +0 -0
  6. package/assets/branding/library-favicon.png +0 -0
  7. package/assets/branding/pond-enterprise-logo.jpg +0 -0
  8. package/assets/branding/pond-enterprise-logo.svg +27 -0
  9. package/dist/branding/index.d.ts +48 -0
  10. package/dist/branding/index.js +67 -0
  11. package/dist/branding/index.js.map +1 -0
  12. package/dist/chunk-2HFOWE5R.js +296 -0
  13. package/dist/chunk-2HFOWE5R.js.map +1 -0
  14. package/dist/chunk-3L6LH2XK.js +465 -0
  15. package/dist/chunk-3L6LH2XK.js.map +1 -0
  16. package/dist/chunk-4CWVFPBN.js +28 -0
  17. package/dist/chunk-4CWVFPBN.js.map +1 -0
  18. package/dist/chunk-AGHPATVL.js +65 -0
  19. package/dist/chunk-AGHPATVL.js.map +1 -0
  20. package/dist/chunk-JDUYLUDJ.js +4835 -0
  21. package/dist/chunk-JDUYLUDJ.js.map +1 -0
  22. package/dist/chunk-OEZK7IX7.js +77 -0
  23. package/dist/chunk-OEZK7IX7.js.map +1 -0
  24. package/dist/chunk-PUYDAUCI.js +2873 -0
  25. package/dist/chunk-PUYDAUCI.js.map +1 -0
  26. package/dist/chunk-WHWMTS7D.js +99 -0
  27. package/dist/chunk-WHWMTS7D.js.map +1 -0
  28. package/dist/chunk-Y7RS6RTM.js +342 -0
  29. package/dist/chunk-Y7RS6RTM.js.map +1 -0
  30. package/dist/chunk-ZEZGI2TP.js +197 -0
  31. package/dist/chunk-ZEZGI2TP.js.map +1 -0
  32. package/dist/core-dao-core-logo-VDT2I5IW.svg +33 -0
  33. package/dist/downloader/dl2-sw.js +199 -0
  34. package/dist/downloader/dl2-sw.js.map +1 -0
  35. package/dist/downloader/file-worker-v2.js +258 -0
  36. package/dist/downloader/file-worker-v2.js.map +1 -0
  37. package/dist/downloader/index.d.ts +45 -0
  38. package/dist/downloader/index.js +538 -0
  39. package/dist/downloader/index.js.map +1 -0
  40. package/dist/downloader/mu-worker-v2.js +197 -0
  41. package/dist/downloader/mu-worker-v2.js.map +1 -0
  42. package/dist/downloader/react.d.ts +19 -0
  43. package/dist/downloader/react.js +32 -0
  44. package/dist/downloader/react.js.map +1 -0
  45. package/dist/downloader/solid.d.ts +21 -0
  46. package/dist/downloader/solid.js +19 -0
  47. package/dist/downloader/solid.js.map +1 -0
  48. package/dist/downloader/sw.d.ts +22 -0
  49. package/dist/downloader/sw.js +119 -0
  50. package/dist/downloader/sw.js.map +1 -0
  51. package/dist/downloader/vue.d.ts +23 -0
  52. package/dist/downloader/vue.js +19 -0
  53. package/dist/downloader/vue.js.map +1 -0
  54. package/dist/level-state-J3pj64Tr.d.ts +15 -0
  55. package/dist/models/astro.d.ts +47 -0
  56. package/dist/models/astro.js +44 -0
  57. package/dist/models/astro.js.map +1 -0
  58. package/dist/models/index.d.ts +244 -0
  59. package/dist/models/index.js +340 -0
  60. package/dist/models/index.js.map +1 -0
  61. package/dist/models/models.d.ts +524 -0
  62. package/dist/models/models.js +46 -0
  63. package/dist/models/models.js.map +1 -0
  64. package/dist/models/models_v2.global.js +1 -0
  65. package/dist/models/patch-worker.js +543 -0
  66. package/dist/models/patch-worker.js.map +1 -0
  67. package/dist/models/react.d.ts +43 -0
  68. package/dist/models/react.js +73 -0
  69. package/dist/models/react.js.map +1 -0
  70. package/dist/models/solid.d.ts +73 -0
  71. package/dist/models/solid.js +95 -0
  72. package/dist/models/solid.js.map +1 -0
  73. package/dist/models/vue.d.ts +51 -0
  74. package/dist/models/vue.js +98 -0
  75. package/dist/models/vue.js.map +1 -0
  76. package/dist/orm-BvkX0aBT.d.ts +507 -0
  77. package/dist/session-vqOnyxga.d.ts +184 -0
  78. package/dist/state-BTsp-E4v.d.ts +111 -0
  79. package/dist/types-Dn0EFMO_.d.ts +293 -0
  80. package/dist/types-Xb8Ta9tZ.d.ts +155 -0
  81. package/dist/web3/index.d.ts +820 -0
  82. package/dist/web3/index.js +1020 -0
  83. package/dist/web3/index.js.map +1 -0
  84. package/dist/web3/registry.d.ts +145 -0
  85. package/dist/web3/registry.js +23 -0
  86. package/dist/web3/registry.js.map +1 -0
  87. package/dist/web3/vue.d.ts +174 -0
  88. package/dist/web3/vue.js +391 -0
  89. package/dist/web3/vue.js.map +1 -0
  90. 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