@korajs/store 1.0.0-beta.0 → 1.0.0-beta.1
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/dist/adapters/better-sqlite3.d.cts +1 -1
- package/dist/adapters/better-sqlite3.d.ts +1 -1
- package/dist/adapters/indexeddb.d.cts +1 -1
- package/dist/adapters/indexeddb.d.ts +1 -1
- package/dist/adapters/sqlite-wasm.d.cts +1 -1
- package/dist/adapters/sqlite-wasm.d.ts +1 -1
- package/dist/{backup-5XZ7THNJ.js → backup-ZV4Y424B.js} +3 -3
- package/dist/blob-fs.cjs +166 -0
- package/dist/blob-fs.cjs.map +1 -0
- package/dist/blob-fs.d.cts +29 -0
- package/dist/blob-fs.d.ts +29 -0
- package/dist/blob-fs.js +128 -0
- package/dist/blob-fs.js.map +1 -0
- package/dist/{chunk-ETDJEH2I.js → chunk-6NKLDBTY.js} +11 -1
- package/dist/{chunk-ETDJEH2I.js.map → chunk-6NKLDBTY.js.map} +1 -1
- package/dist/{chunk-EIVXWF4B.js → chunk-FFYA7J5H.js} +2 -2
- package/dist/chunk-W4MTTNEZ.js +55 -0
- package/dist/chunk-W4MTTNEZ.js.map +1 -0
- package/dist/{chunk-5R4QMY3Z.js → chunk-X57MWW35.js} +2 -2
- package/dist/{chunk-OUQDC5Z6.js → chunk-ZLWAYQEQ.js} +28 -12
- package/dist/chunk-ZLWAYQEQ.js.map +1 -0
- package/dist/content-addressed-blob-store-Dcj3SB9r.d.cts +60 -0
- package/dist/content-addressed-blob-store-Dcj3SB9r.d.ts +60 -0
- package/dist/{export-audit-VPGQWNR6.js → export-audit-AJ7RORNK.js} +3 -3
- package/dist/index.cjs +632 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +397 -6
- package/dist/index.d.ts +397 -6
- package/dist/index.js +561 -26
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +25 -5
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +3 -3
- package/dist/internal.d.ts +3 -3
- package/dist/internal.js +3 -3
- package/dist/{types-CyjXwrjW.d.cts → types-5h4Hqcwc.d.ts} +10 -3
- package/dist/{types-DAxCladl.d.ts → types-DfPiPwKr.d.cts} +10 -3
- package/dist/{types-CJOVKxLw.d.ts → types-k6AZgiH4.d.cts} +6 -1
- package/dist/{types-CJOVKxLw.d.cts → types-k6AZgiH4.d.ts} +6 -1
- package/package.json +13 -2
- package/dist/chunk-OUQDC5Z6.js.map +0 -1
- /package/dist/{backup-5XZ7THNJ.js.map → backup-ZV4Y424B.js.map} +0 -0
- /package/dist/{chunk-EIVXWF4B.js.map → chunk-FFYA7J5H.js.map} +0 -0
- /package/dist/{chunk-5R4QMY3Z.js.map → chunk-X57MWW35.js.map} +0 -0
- /package/dist/{export-audit-VPGQWNR6.js.map → export-audit-AJ7RORNK.js.map} +0 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { C as CollectionRecord, S as StorageAdapter, Q as QueryDescriptor, c as SubscriptionCallback, W as WhereClause, d as OrderByDirection, L as LocalMutationHandler, e as StoreConfig, A as ApplyRemoteOptions, f as MaterializedRowSnapshot, g as RowVersionState } from './types-
|
|
2
|
-
export { M as MigrationPlan, h as OrderByClause, i as StoreIsolation, T as Transaction, j as TransactionBufferedEntry, k as TransactionCommitBatch, l as TransactionCommitResult, m as WhereOperators } from './types-
|
|
3
|
-
import { Operation, KoraError, VersionVector, SchemaDefinition, HLCTimestamp, MergeTrace, CollectionDefinition, SequenceConfig, HybridLogicalClock, CausalTracker, OperationLog, ApplyResult, StateMachineDefinition, KoraBytesValue, KoraEvent } from '@korajs/core';
|
|
1
|
+
import { C as CollectionRecord, S as StorageAdapter, Q as QueryDescriptor, c as SubscriptionCallback, W as WhereClause, d as OrderByDirection, L as LocalMutationHandler, e as StoreConfig, A as ApplyRemoteOptions, f as MaterializedRowSnapshot, g as RowVersionState } from './types-k6AZgiH4.js';
|
|
2
|
+
export { M as MigrationPlan, h as OrderByClause, i as StoreIsolation, T as Transaction, j as TransactionBufferedEntry, k as TransactionCommitBatch, l as TransactionCommitResult, m as WhereOperators } from './types-k6AZgiH4.js';
|
|
3
|
+
import { Operation, KoraError, VersionVector, SchemaDefinition, HLCTimestamp, MergeTrace, CollectionDefinition, SequenceConfig, HybridLogicalClock, CausalTracker, OperationLog, ApplyResult, StateMachineDefinition, KoraBytesValue, KoraEvent, BlobRefMetadata, BlobRef } from '@korajs/core';
|
|
4
4
|
export { ApplyResult } from '@korajs/core';
|
|
5
|
-
import { R as RelationEnforcer, L as LocalMutationContext } from './types-
|
|
6
|
-
export { C as Collection } from './types-
|
|
5
|
+
import { R as RelationEnforcer, L as LocalMutationContext } from './types-5h4Hqcwc.js';
|
|
6
|
+
export { C as Collection } from './types-5h4Hqcwc.js';
|
|
7
7
|
import * as yjs from 'yjs';
|
|
8
|
+
import { C as ContentAddressedBlobStore, M as MemoryBlobStore } from './content-addressed-blob-store-Dcj3SB9r.js';
|
|
9
|
+
export { B as BlobIntegrityError } from './content-addressed-blob-store-Dcj3SB9r.js';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* In-memory materialized state at a causal cut in the operation log.
|
|
@@ -718,6 +720,7 @@ declare class Store implements OperationLog {
|
|
|
718
720
|
private localMutationHandler;
|
|
719
721
|
private relationEnforcer;
|
|
720
722
|
private causalTracker;
|
|
723
|
+
private readonly secretKeyProvider;
|
|
721
724
|
constructor(config: StoreConfig);
|
|
722
725
|
/**
|
|
723
726
|
* Open the store: initialize the database, load or generate a node ID,
|
|
@@ -1357,4 +1360,392 @@ declare function readAuditExportManifest(data: Uint8Array): AuditExportManifest;
|
|
|
1357
1360
|
*/
|
|
1358
1361
|
declare function verifyAuditExportChecksum(data: Uint8Array): Promise<boolean>;
|
|
1359
1362
|
|
|
1360
|
-
|
|
1363
|
+
/**
|
|
1364
|
+
* The minimal directory abstraction an {@link OpfsBlobStore} needs. Blobs are
|
|
1365
|
+
* laid out as `<shard>/<name>` (shard = first two hex chars of the hash) so a
|
|
1366
|
+
* single directory never holds millions of entries.
|
|
1367
|
+
*
|
|
1368
|
+
* The real backend ({@link createOpfsBlobDirectory}) is the browser Origin
|
|
1369
|
+
* Private File System; tests supply an in-memory implementation. Keeping this
|
|
1370
|
+
* port small means the content-addressed logic (hashing, integrity, dedup,
|
|
1371
|
+
* sharding) is fully testable without a browser.
|
|
1372
|
+
*/
|
|
1373
|
+
interface OpfsBlobDirectory {
|
|
1374
|
+
/** Read the bytes at `<shard>/<name>`, or null if absent. */
|
|
1375
|
+
read(shard: string, name: string): Promise<Uint8Array | null>;
|
|
1376
|
+
/**
|
|
1377
|
+
* Write bytes at `<shard>/<name>`, creating the shard directory as needed.
|
|
1378
|
+
* Must be atomic: a crash mid-write may not leave a partially-written file
|
|
1379
|
+
* that a later read would trust.
|
|
1380
|
+
*/
|
|
1381
|
+
write(shard: string, name: string, bytes: Uint8Array): Promise<void>;
|
|
1382
|
+
/** Remove `<shard>/<name>`. Returns whether anything was removed. */
|
|
1383
|
+
remove(shard: string, name: string): Promise<boolean>;
|
|
1384
|
+
/** Whether `<shard>/<name>` exists. */
|
|
1385
|
+
has(shard: string, name: string): Promise<boolean>;
|
|
1386
|
+
/** Count of stored blobs across all shards (temp files excluded). */
|
|
1387
|
+
count(): Promise<number>;
|
|
1388
|
+
/** Every stored blob name (hash) across all shards (temp files excluded). */
|
|
1389
|
+
keys(): Promise<string[]>;
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* A persistent {@link ContentAddressedBlobStore} for the browser, backed by the
|
|
1393
|
+
* Origin Private File System (OPFS) — the same durable storage the SQLite WASM
|
|
1394
|
+
* adapter uses. Blobs survive reloads, are sharded by hash prefix, deduplicated,
|
|
1395
|
+
* and integrity-verified on read (the returned bytes are checked to hash to the
|
|
1396
|
+
* requested key). Writes commit atomically so a torn write cannot surface as
|
|
1397
|
+
* trusted data.
|
|
1398
|
+
*
|
|
1399
|
+
* The store itself is environment-neutral: it talks to an {@link OpfsBlobDirectory}.
|
|
1400
|
+
* Use {@link createOpfsBlobStore} in a browser to get one backed by real OPFS.
|
|
1401
|
+
*/
|
|
1402
|
+
declare class OpfsBlobStore implements ContentAddressedBlobStore {
|
|
1403
|
+
private readonly dir;
|
|
1404
|
+
constructor(dir: OpfsBlobDirectory);
|
|
1405
|
+
private shardFor;
|
|
1406
|
+
put(bytes: Uint8Array, metadata?: BlobRefMetadata): Promise<BlobRef>;
|
|
1407
|
+
get(hash: string): Promise<Uint8Array | null>;
|
|
1408
|
+
has(hash: string): Promise<boolean>;
|
|
1409
|
+
delete(hash: string): Promise<boolean>;
|
|
1410
|
+
size(): Promise<number>;
|
|
1411
|
+
list(): Promise<string[]>;
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Create an {@link OpfsBlobDirectory} backed by the browser's Origin Private
|
|
1415
|
+
* File System, rooted at a named subdirectory. Best-effort requests persistent
|
|
1416
|
+
* storage so the browser is less likely to evict blobs under storage pressure.
|
|
1417
|
+
*
|
|
1418
|
+
* @param rootDirName - The OPFS subdirectory to store blobs under (default `kora-blobs`)
|
|
1419
|
+
* @throws {Error} If OPFS is unavailable (e.g. called outside a browser)
|
|
1420
|
+
*/
|
|
1421
|
+
declare function createOpfsBlobDirectory(rootDirName?: string): Promise<OpfsBlobDirectory>;
|
|
1422
|
+
/**
|
|
1423
|
+
* Create a persistent {@link OpfsBlobStore} backed by real browser OPFS.
|
|
1424
|
+
*
|
|
1425
|
+
* @param rootDirName - The OPFS subdirectory to store blobs under (default `kora-blobs`)
|
|
1426
|
+
* @throws {Error} If OPFS is unavailable (e.g. called outside a browser)
|
|
1427
|
+
*/
|
|
1428
|
+
declare function createOpfsBlobStore(rootDirName?: string): Promise<OpfsBlobStore>;
|
|
1429
|
+
|
|
1430
|
+
/** Default chunk size for out-of-band blob transfer (256 KiB). */
|
|
1431
|
+
declare const DEFAULT_CHUNK_SIZE: number;
|
|
1432
|
+
/**
|
|
1433
|
+
* A manifest describing how a blob is split into content-addressed chunks.
|
|
1434
|
+
*
|
|
1435
|
+
* The manifest is small and travels with the operation stream (or alongside it);
|
|
1436
|
+
* the chunk bytes travel out of band. Because every chunk is content-addressed,
|
|
1437
|
+
* identical chunks across blobs are stored and transferred once, and a peer only
|
|
1438
|
+
* ever requests chunk hashes it does not already hold.
|
|
1439
|
+
*/
|
|
1440
|
+
interface BlobManifest {
|
|
1441
|
+
/** Hex SHA-256 hash of the full reassembled blob (its content address). */
|
|
1442
|
+
blobHash: string;
|
|
1443
|
+
/** Total size of the full blob in bytes. */
|
|
1444
|
+
size: number;
|
|
1445
|
+
/** The chunk size used to split the blob. */
|
|
1446
|
+
chunkSize: number;
|
|
1447
|
+
/** Ordered hashes of each chunk. Repeats when identical chunks recur. */
|
|
1448
|
+
chunkHashes: string[];
|
|
1449
|
+
mimeType?: string;
|
|
1450
|
+
filename?: string;
|
|
1451
|
+
}
|
|
1452
|
+
/** Optional metadata carried on a manifest. */
|
|
1453
|
+
interface BlobManifestMetadata {
|
|
1454
|
+
mimeType?: string;
|
|
1455
|
+
filename?: string;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Split a blob into content-addressed chunks and produce its manifest.
|
|
1459
|
+
*
|
|
1460
|
+
* @param bytes - The full blob content
|
|
1461
|
+
* @param chunkSize - Bytes per chunk (defaults to {@link DEFAULT_CHUNK_SIZE})
|
|
1462
|
+
* @param metadata - Optional mime type / filename to record on the manifest
|
|
1463
|
+
* @returns The manifest and a map of chunk hash to chunk bytes (deduplicated)
|
|
1464
|
+
*/
|
|
1465
|
+
declare function chunkBlob(bytes: Uint8Array, chunkSize?: number, metadata?: BlobManifestMetadata): Promise<{
|
|
1466
|
+
manifest: BlobManifest;
|
|
1467
|
+
chunks: Map<string, Uint8Array>;
|
|
1468
|
+
}>;
|
|
1469
|
+
/**
|
|
1470
|
+
* Reassemble a blob from chunks held in a content-addressed store, verifying
|
|
1471
|
+
* integrity at both levels: each chunk is verified by the store on read, and the
|
|
1472
|
+
* reassembled whole is verified against the manifest's blob hash.
|
|
1473
|
+
*
|
|
1474
|
+
* @param manifest - The blob manifest
|
|
1475
|
+
* @param chunkStore - A store holding the chunk bytes keyed by chunk hash
|
|
1476
|
+
* @returns The full reassembled blob bytes
|
|
1477
|
+
* @throws {BlobIntegrityError} If the reassembled bytes do not match blobHash
|
|
1478
|
+
* @throws {Error} If a chunk named in the manifest is missing from the store
|
|
1479
|
+
*/
|
|
1480
|
+
declare function reassembleBlob(manifest: BlobManifest, chunkStore: ContentAddressedBlobStore): Promise<Uint8Array>;
|
|
1481
|
+
|
|
1482
|
+
/**
|
|
1483
|
+
* A source of chunk bytes on the sending side of a transfer. In production this
|
|
1484
|
+
* is backed by the sync connection (the receiver requests a chunk hash, the peer
|
|
1485
|
+
* that holds the blob answers with the bytes); in tests it is backed by a map.
|
|
1486
|
+
*/
|
|
1487
|
+
interface ChunkProvider {
|
|
1488
|
+
/** Fetch the bytes for a chunk hash, or null if the provider does not have it. */
|
|
1489
|
+
getChunk(hash: string): Promise<Uint8Array | null>;
|
|
1490
|
+
}
|
|
1491
|
+
/** Result of receiving a blob out of band. */
|
|
1492
|
+
interface ReceiveBlobResult {
|
|
1493
|
+
/** The content-addressed reference to the reassembled blob. */
|
|
1494
|
+
ref: BlobRef;
|
|
1495
|
+
/** Number of chunks actually fetched from the provider. */
|
|
1496
|
+
chunksFetched: number;
|
|
1497
|
+
/** Number of chunks skipped because they were already held locally. */
|
|
1498
|
+
chunksSkipped: number;
|
|
1499
|
+
}
|
|
1500
|
+
/** Stores used while receiving a blob. */
|
|
1501
|
+
interface ReceiveBlobStores {
|
|
1502
|
+
/**
|
|
1503
|
+
* Where fetched chunks are staged, keyed by chunk hash. A persistent chunk
|
|
1504
|
+
* store makes a transfer resumable: an interrupted transfer leaves its chunks
|
|
1505
|
+
* here, and a resumed transfer skips them.
|
|
1506
|
+
*/
|
|
1507
|
+
chunkStore: ContentAddressedBlobStore;
|
|
1508
|
+
/** Where the fully reassembled, verified blob is written. */
|
|
1509
|
+
blobStore: ContentAddressedBlobStore;
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Receive a blob out of band, chunk by chunk, resumably and with integrity
|
|
1513
|
+
* verification at every step.
|
|
1514
|
+
*
|
|
1515
|
+
* For each chunk named in the manifest: if the chunk store already holds it (a
|
|
1516
|
+
* prior, possibly interrupted transfer), it is skipped; otherwise it is fetched
|
|
1517
|
+
* from the provider and verified to hash to the expected chunk hash before being
|
|
1518
|
+
* staged. Once every chunk is present, the blob is reassembled, verified against
|
|
1519
|
+
* the manifest's blob hash, and written to the blob store (which deduplicates).
|
|
1520
|
+
*
|
|
1521
|
+
* The transfer is idempotent: running it again after completion fetches nothing
|
|
1522
|
+
* and returns the same reference.
|
|
1523
|
+
*
|
|
1524
|
+
* @param manifest - The manifest of the blob to receive
|
|
1525
|
+
* @param provider - The source of chunk bytes
|
|
1526
|
+
* @param stores - The chunk staging store and the destination blob store
|
|
1527
|
+
* @returns The reference plus how many chunks were fetched versus skipped
|
|
1528
|
+
* @throws {BlobIntegrityError} If a fetched chunk or the whole blob fails verification
|
|
1529
|
+
* @throws {Error} If the provider cannot supply a required chunk
|
|
1530
|
+
*/
|
|
1531
|
+
declare function receiveBlob(manifest: BlobManifest, provider: ChunkProvider, stores: ReceiveBlobStores): Promise<ReceiveBlobResult>;
|
|
1532
|
+
/**
|
|
1533
|
+
* Prepare a blob for out-of-band sending: split it into chunks, stage those
|
|
1534
|
+
* chunks in a content-addressed store, and return the manifest plus a
|
|
1535
|
+
* {@link ChunkProvider} that serves the chunks from that store.
|
|
1536
|
+
*
|
|
1537
|
+
* @param bytes - The full blob content
|
|
1538
|
+
* @param chunkStore - Where to stage the chunks (served to receivers)
|
|
1539
|
+
* @param options - Optional chunk size and metadata
|
|
1540
|
+
* @returns The manifest and a provider that serves the staged chunks
|
|
1541
|
+
*/
|
|
1542
|
+
declare function prepareBlobForSend(bytes: Uint8Array, chunkStore: ContentAddressedBlobStore, options?: {
|
|
1543
|
+
chunkSize?: number;
|
|
1544
|
+
mimeType?: string;
|
|
1545
|
+
filename?: string;
|
|
1546
|
+
}): Promise<{
|
|
1547
|
+
manifest: BlobManifest;
|
|
1548
|
+
provider: ChunkProvider;
|
|
1549
|
+
}>;
|
|
1550
|
+
|
|
1551
|
+
/**
|
|
1552
|
+
* Serialize a {@link BlobManifest} to canonical bytes for content addressing.
|
|
1553
|
+
*
|
|
1554
|
+
* Fields are emitted in a fixed order so the same manifest always produces the
|
|
1555
|
+
* same bytes, which is what lets the manifest be stored and fetched under its own
|
|
1556
|
+
* SHA-256 hash. Optional fields are omitted when absent.
|
|
1557
|
+
*/
|
|
1558
|
+
declare function serializeBlobManifest(manifest: BlobManifest): Uint8Array;
|
|
1559
|
+
/**
|
|
1560
|
+
* Parse canonical manifest bytes back into a {@link BlobManifest}, validating the
|
|
1561
|
+
* shape. Throws on malformed input rather than returning a partial manifest.
|
|
1562
|
+
*/
|
|
1563
|
+
declare function parseBlobManifest(bytes: Uint8Array): BlobManifest;
|
|
1564
|
+
/**
|
|
1565
|
+
* Store a blob for out-of-band transfer, addressed entirely by content.
|
|
1566
|
+
*
|
|
1567
|
+
* Stages the blob's chunks (so this device can serve them), stores the full blob
|
|
1568
|
+
* (so local reads by hash are O(1)), and stores the blob's manifest as its own
|
|
1569
|
+
* content-addressed object. The returned {@link BlobRef} carries `manifestHash`,
|
|
1570
|
+
* so a peer that receives the reference in a synced record can pull the bytes
|
|
1571
|
+
* knowing only the reference — no separate manifest hand-off.
|
|
1572
|
+
*
|
|
1573
|
+
* @param store - The content-addressed store to stage chunks, blob, and manifest in
|
|
1574
|
+
* @param bytes - The full blob content
|
|
1575
|
+
* @param options - Optional chunk size and metadata
|
|
1576
|
+
* @returns The reference (including `manifestHash`) and the manifest
|
|
1577
|
+
*/
|
|
1578
|
+
declare function putBlobForTransfer(store: ContentAddressedBlobStore, bytes: Uint8Array, options?: {
|
|
1579
|
+
chunkSize?: number;
|
|
1580
|
+
} & BlobRefMetadata): Promise<{
|
|
1581
|
+
ref: BlobRef;
|
|
1582
|
+
manifest: BlobManifest;
|
|
1583
|
+
}>;
|
|
1584
|
+
/**
|
|
1585
|
+
* Fetch and integrity-verify a blob's manifest from a {@link ChunkProvider}.
|
|
1586
|
+
*
|
|
1587
|
+
* The manifest is a content-addressed object like a chunk, so it is requested by
|
|
1588
|
+
* its hash, verified to hash to that value, and parsed. Used to resolve a
|
|
1589
|
+
* {@link BlobRef}'s manifest before pulling the blob's chunks.
|
|
1590
|
+
*
|
|
1591
|
+
* @param provider - The source to fetch the manifest bytes from
|
|
1592
|
+
* @param manifestHash - The manifest's content hash (from `BlobRef.manifestHash`)
|
|
1593
|
+
* @throws {BlobIntegrityError} If the fetched bytes do not hash to `manifestHash`
|
|
1594
|
+
* @throws {Error} If the provider cannot supply the manifest
|
|
1595
|
+
*/
|
|
1596
|
+
declare function fetchBlobManifest(provider: ChunkProvider, manifestHash: string): Promise<BlobManifest>;
|
|
1597
|
+
/**
|
|
1598
|
+
* Resolve the manifest for a {@link BlobRef} that was stored via
|
|
1599
|
+
* {@link putBlobForTransfer}. Fetches and verifies the manifest by the ref's
|
|
1600
|
+
* `manifestHash`.
|
|
1601
|
+
*
|
|
1602
|
+
* @throws {Error} If the ref has no `manifestHash` (it was not stored for transfer)
|
|
1603
|
+
*/
|
|
1604
|
+
declare function resolveBlobManifest(provider: ChunkProvider, ref: BlobRef): Promise<BlobManifest>;
|
|
1605
|
+
|
|
1606
|
+
/**
|
|
1607
|
+
* The server-side blob callbacks a `KoraSyncServer` accepts: read a chunk by
|
|
1608
|
+
* hash (to serve it) and persist an uploaded chunk by hash (to store it).
|
|
1609
|
+
*/
|
|
1610
|
+
interface ServerBlobCallbacks {
|
|
1611
|
+
resolveBlobChunk: (hash: string) => Promise<Uint8Array | null>;
|
|
1612
|
+
persistBlobChunk: (hash: string, bytes: Uint8Array) => Promise<void>;
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Adapt a {@link ContentAddressedBlobStore} into the read/persist callbacks a
|
|
1616
|
+
* `KoraSyncServer` needs for central blob storage. The server package depends
|
|
1617
|
+
* only on these plain function signatures, so a self-hosted server can back its
|
|
1618
|
+
* blob storage with any store (for example a `FilesystemBlobStore`) without the
|
|
1619
|
+
* server package taking a dependency on `@korajs/store`.
|
|
1620
|
+
*
|
|
1621
|
+
* @example
|
|
1622
|
+
* ```typescript
|
|
1623
|
+
* const server = new KoraSyncServer({
|
|
1624
|
+
* store,
|
|
1625
|
+
* ...toServerBlobCallbacks(new FilesystemBlobStore('/var/kora/blobs')),
|
|
1626
|
+
* })
|
|
1627
|
+
* ```
|
|
1628
|
+
*/
|
|
1629
|
+
declare function toServerBlobCallbacks(store: ContentAddressedBlobStore): ServerBlobCallbacks;
|
|
1630
|
+
/**
|
|
1631
|
+
* Convenience: an in-memory central blob store plus its server callbacks. For
|
|
1632
|
+
* tests and single-process deployments. Persistent deployments should back
|
|
1633
|
+
* {@link toServerBlobCallbacks} with a durable store instead.
|
|
1634
|
+
*/
|
|
1635
|
+
declare function createMemoryServerBlobStore(): {
|
|
1636
|
+
store: MemoryBlobStore;
|
|
1637
|
+
callbacks: ServerBlobCallbacks;
|
|
1638
|
+
};
|
|
1639
|
+
|
|
1640
|
+
/** Outcome of a blob garbage-collection sweep. */
|
|
1641
|
+
interface BlobGcResult {
|
|
1642
|
+
/** Total distinct blobs held before the sweep. */
|
|
1643
|
+
scanned: number;
|
|
1644
|
+
/** Distinct hashes kept because a live reference points to them (blob, manifest, or chunk). */
|
|
1645
|
+
live: number;
|
|
1646
|
+
/** Number of blobs deleted (or, with `dryRun`, that would be deleted). */
|
|
1647
|
+
collected: number;
|
|
1648
|
+
/** The hashes deleted (or, with `dryRun`, that would be deleted). */
|
|
1649
|
+
collectedHashes: string[];
|
|
1650
|
+
}
|
|
1651
|
+
/** Options for {@link collectBlobGarbage}. */
|
|
1652
|
+
interface BlobGcOptions {
|
|
1653
|
+
/** Compute what would be collected without deleting anything. */
|
|
1654
|
+
dryRun?: boolean;
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Extract the {@link BlobRef}s referenced by a materialized record. Scans the
|
|
1658
|
+
* record's top-level field values, so it works without the schema; a `blob`
|
|
1659
|
+
* field simply holds a `BlobRef`.
|
|
1660
|
+
*/
|
|
1661
|
+
declare function extractBlobRefs(record: Record<string, unknown>): BlobRef[];
|
|
1662
|
+
/**
|
|
1663
|
+
* Garbage-collect a content-addressed blob store by mark-and-sweep: keep every
|
|
1664
|
+
* blob reachable from a live reference, delete the rest.
|
|
1665
|
+
*
|
|
1666
|
+
* The live set is closed over the reference graph — for each live {@link BlobRef}
|
|
1667
|
+
* it retains the blob hash, the manifest hash, and every chunk hash named in that
|
|
1668
|
+
* manifest. Because chunks (and blobs) are shared by content address, a chunk
|
|
1669
|
+
* referenced by any surviving blob is retained even if another blob that used it
|
|
1670
|
+
* was collected. This makes GC safe under deduplication.
|
|
1671
|
+
*
|
|
1672
|
+
* Mark-and-sweep (rather than reference counting) is deliberate: counts are
|
|
1673
|
+
* fragile under concurrent edits and CRDT merges, whereas a sweep against the
|
|
1674
|
+
* current live set is always correct for the state it observes. Run it
|
|
1675
|
+
* periodically or on demand.
|
|
1676
|
+
*
|
|
1677
|
+
* A manifest that cannot be read or parsed (corruption) contributes no chunk
|
|
1678
|
+
* hashes; its own hash still counts as live. The blob it describes is already
|
|
1679
|
+
* unusable, so its chunks becoming collectable is acceptable, and GC never
|
|
1680
|
+
* throws on a single bad manifest.
|
|
1681
|
+
*
|
|
1682
|
+
* @param store - The blob store to sweep
|
|
1683
|
+
* @param liveRefs - Every reference still reachable from live records
|
|
1684
|
+
* @param options - `dryRun` to report without deleting
|
|
1685
|
+
*/
|
|
1686
|
+
declare function collectBlobGarbage(store: ContentAddressedBlobStore, liveRefs: Iterable<BlobRef>, options?: BlobGcOptions): Promise<BlobGcResult>;
|
|
1687
|
+
|
|
1688
|
+
/**
|
|
1689
|
+
* Blob chunk exchange messages. These ride any message transport (the sync
|
|
1690
|
+
* WebSocket in production, an in-memory pair in tests). A receiver requests a
|
|
1691
|
+
* chunk by hash and correlates the answer by `requestId`.
|
|
1692
|
+
*/
|
|
1693
|
+
interface ChunkRequestMessage {
|
|
1694
|
+
type: 'blob-chunk-request';
|
|
1695
|
+
requestId: string;
|
|
1696
|
+
hash: string;
|
|
1697
|
+
}
|
|
1698
|
+
interface ChunkResponseMessage {
|
|
1699
|
+
type: 'blob-chunk-response';
|
|
1700
|
+
requestId: string;
|
|
1701
|
+
/** The chunk bytes, or null when the responder does not hold that hash. */
|
|
1702
|
+
bytes: Uint8Array | null;
|
|
1703
|
+
}
|
|
1704
|
+
type ChunkMessage = ChunkRequestMessage | ChunkResponseMessage;
|
|
1705
|
+
/**
|
|
1706
|
+
* A bidirectional message port carrying {@link ChunkMessage}s. Implemented over
|
|
1707
|
+
* the sync connection in production; `createChunkPortPair` provides an in-memory
|
|
1708
|
+
* duplex pair for tests.
|
|
1709
|
+
*/
|
|
1710
|
+
interface ChunkMessagePort {
|
|
1711
|
+
send(message: ChunkMessage): void;
|
|
1712
|
+
/** Register a handler for incoming messages. Multiple handlers are allowed. */
|
|
1713
|
+
onMessage(handler: (message: ChunkMessage) => void): void;
|
|
1714
|
+
}
|
|
1715
|
+
/** A {@link ChunkProvider} that fetches chunks over a message port. */
|
|
1716
|
+
interface RemoteChunkProvider extends ChunkProvider {
|
|
1717
|
+
/** Number of requests still awaiting a response (for diagnostics/tests). */
|
|
1718
|
+
pendingCount(): number;
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Create a {@link ChunkProvider} that requests chunks over a message port,
|
|
1722
|
+
* correlating each answer to its request and timing out a stalled request so a
|
|
1723
|
+
* dropped response cannot hang a transfer forever (the transfer is resumable, so
|
|
1724
|
+
* a timed-out request can simply be retried).
|
|
1725
|
+
*
|
|
1726
|
+
* @param port - The message port to the peer that holds the blob
|
|
1727
|
+
* @param options.timeoutMs - Per-request timeout (default 30s)
|
|
1728
|
+
*/
|
|
1729
|
+
declare function createRemoteChunkProvider(port: ChunkMessagePort, options?: {
|
|
1730
|
+
timeoutMs?: number;
|
|
1731
|
+
}): RemoteChunkProvider;
|
|
1732
|
+
/**
|
|
1733
|
+
* Serve blob chunk requests arriving on a message port from a blob store. Answers
|
|
1734
|
+
* each `blob-chunk-request` with the bytes for that hash, or null when the store
|
|
1735
|
+
* does not hold it. Returns nothing; attach it once per connection.
|
|
1736
|
+
*
|
|
1737
|
+
* @param port - The message port from the peer requesting chunks
|
|
1738
|
+
* @param blobStore - The store to serve chunks from (keyed by chunk hash)
|
|
1739
|
+
*/
|
|
1740
|
+
declare function serveBlobChunks(port: ChunkMessagePort, blobStore: ContentAddressedBlobStore): void;
|
|
1741
|
+
/**
|
|
1742
|
+
* Create a connected in-memory pair of {@link ChunkMessagePort}s. Messages sent
|
|
1743
|
+
* on one arrive on the other, asynchronously (next microtask) so the pair models
|
|
1744
|
+
* a real transport rather than re-entrant synchronous delivery. For tests.
|
|
1745
|
+
*/
|
|
1746
|
+
declare function createChunkPortPair(): {
|
|
1747
|
+
a: ChunkMessagePort;
|
|
1748
|
+
b: ChunkMessagePort;
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
export { AdapterError, ApplyRemoteOptions, type AuditExportManifest, type AuditExportOptions, type AuditExportPayload, type AuditExportProgress, type AuditTraceQuery, type BackupManifest, type BackupOptions, type BackupProgress, type BlobGcOptions, type BlobGcResult, type BlobManifest, type BlobManifestMetadata, COMPACTION_BASELINE_META_KEY, type ChunkMessage, type ChunkMessagePort, type ChunkProvider, type ChunkRequestMessage, type ChunkResponseMessage, type ClockRebaseResult, type CollectionAccessor, CollectionRecord, type CompactionResult, type CompactionStrategy, ContentAddressedBlobStore, type CreateRichTextControllerOptions, DEFAULT_CHUNK_SIZE, InvalidStateTransitionError, LAST_ACKED_SERVER_VECTOR_META_KEY, LocalMutationHandler, MaterializedRowSnapshot, MemoryBlobStore, type OpfsBlobDirectory, OpfsBlobStore, OptimisticLockError, OrderByDirection, type PersistedAuditTrace, PersistenceError, QueryBuilder, QueryDescriptor, QueryError, QueryStore, QueryStoreCache, type ReceiveBlobResult, type ReceiveBlobStores, RecordNotFoundError, type RemoteChunkProvider, type ReplaySnapshot, type RestoreOptions, type RestoreResult, type RichTextAwarenessUser, type RichTextController, type RichTextControllerSnapshot, type RichTextCursorInfo, type RichTextSyncEngine, RowVersionState, SequenceManager, type ServerBlobCallbacks, StorageAdapter, Store, StoreConfig, StoreNotOpenError, SubscriptionBloomFilter, SubscriptionCallback, SubscriptionManager, type SubscriptionManagerOptions, type SubscriptionStats, type TransactionCollectionAccessor, TransactionContext, type TransactionContextConfig, WhereClause, WorkerInitError, WorkerTimeoutError, asRichTextSyncEngine, assertQueryReady, chunkBlob, collectBlobGarbage, collectOperationsAheadOfServer, compactOperationLog, computeAckCompactionWatermark, createChunkPortPair, createMemoryServerBlobStore, createOpfsBlobDirectory, createOpfsBlobStore, createRemoteChunkProvider, createRichTextController, decodeAuditExport, decodeRichtext, deserializeVersionVectorFromMeta, encodeRichtext, exportBackup, extractBlobRefs, fetchBlobManifest, getSharedQueryStoreCache, mergeVersionVectors, parseBlobManifest, persistedAuditTraceFromEvent, pluralize, prepareBlobForSend, putBlobForTransfer, readAuditExportManifest, readBackupManifest, reassembleBlob, receiveBlob, resolveBlobManifest, restoreBackup, richtextStatesEqual, richtextToPlainText, serializeBlobManifest, serializeVersionVectorToMeta, serveBlobChunks, singularize, toServerBlobCallbacks, validateStateTransition, validateUpdateStateMachine, verifyAuditExportChecksum, verifyBackupChecksum };
|