diff --git a/src/auth/auth.ts b/src/auth/auth.ts index 69c239f9..9d69cdae 100644 --- a/src/auth/auth.ts +++ b/src/auth/auth.ts @@ -602,7 +602,7 @@ export class Auth { return { writeCap: cap }; } catch (err) { - return new ValidationError(err); + return new ValidationError(err as string); } } diff --git a/src/schemes/schemes.ts b/src/schemes/schemes.ts index f429126e..c8582fd3 100644 --- a/src/schemes/schemes.ts +++ b/src/schemes/schemes.ts @@ -233,10 +233,7 @@ export function makeMeadowcapParams( }; } -export function makeAuthorisationScheme( - ed25519: Ed25519Driver, - blake3: Blake3Driver, -): Willow.AuthorisationScheme< +export type AuthorizationScheme = Willow.AuthorisationScheme< SharePublicKey, IdentityPublicKey, Uint8Array, @@ -255,7 +252,12 @@ export function makeAuthorisationScheme( Uint8Array, Uint8Array > -> { +>; + +export function makeAuthorisationScheme( + ed25519: Ed25519Driver, + blake3: Blake3Driver, +): AuthorizationScheme { const meadowcapParams = makeMeadowcapParams(ed25519, blake3); const meadowcap = new Meadowcap.Meadowcap( meadowcapParams, diff --git a/src/store/store.test.ts b/src/store/store.test.ts index 0443e1c1..81008b58 100644 --- a/src/store/store.test.ts +++ b/src/store/store.test.ts @@ -556,3 +556,34 @@ Deno.test("Store.queryIdentities", async () => { })); assertEquals(identities, [identityDisplay]); }); + +Deno.test("Store.createDrop() and Store.ingestDrop()", async () => { + const source = newStore(); + await source.set({ + identity: identityDisplay, + path: Path.fromStrings("hello", "a"), + payload: new TextEncoder().encode("b"), + timestamp: 1000n, + }); + await source.set({ + identity: identityDisplay, + path: Path.fromStrings("hello", "c"), + payload: new TextEncoder().encode("d"), + timestamp: 2000n, + }); + + const dropStream = source.createDrop({ + timestampGte: 1500n, + }); + if (isErr(dropStream)) throw dropStream; + + const target = newStore(); + await target.ingestDrop(dropStream); + + const v1 = await target.get(identityDisplay, Path.fromStrings("hello", "a")); + assertEquals(undefined, v1); + + const v2 = await target.get(identityDisplay, Path.fromStrings("hello", "c")); + if (isErr(v2)) throw v2; + assertEquals(new TextEncoder().encode("d"), await v2?.payload?.bytes()); +}); diff --git a/src/store/store.ts b/src/store/store.ts index f4fb44c8..276a751a 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -2,6 +2,7 @@ import * as Willow from "@earthstar/willow"; import { OPEN_END, successorPath } from "@earthstar/willow-utils"; import type { Auth, AuthorisationToken } from "../auth/auth.ts"; import { + type AuthorizationScheme, fingerprintScheme, makeAuthorisationScheme, makePayloadScheme, @@ -59,6 +60,7 @@ import { Path } from "../path/path.ts"; */ export class Store extends EventTarget { private auth: Auth; + private payloadScheme: Willow.PayloadScheme; /** The underlying Willow `Store`, made accessible for advanced usage and shenanigans. */ willow: Willow.Store< @@ -70,6 +72,7 @@ export class Store extends EventTarget { PreFingerprint, Uint8Array >; + authorizationScheme: AuthorizationScheme; /** The tag of the share this {@linkcode Store} belongs to. */ get share(): ShareTag { @@ -93,18 +96,20 @@ export class Store extends EventTarget { throw sharePublicKey; } + this.payloadScheme = makePayloadScheme(drivers.runtimeDriver.blake3); + this.authorizationScheme = makeAuthorisationScheme( + drivers.runtimeDriver.ed25519, + drivers.runtimeDriver.blake3 + ); this.willow = new Willow.Store({ namespace: sharePublicKey, schemes: { namespace: namespaceScheme, subspace: subspaceScheme, path: pathScheme, - payload: makePayloadScheme(drivers.runtimeDriver.blake3), + payload: this.payloadScheme, fingerprint: fingerprintScheme, - authorisation: makeAuthorisationScheme( - drivers.runtimeDriver.ed25519, - drivers.runtimeDriver.blake3, - ), + authorisation: this.authorizationScheme, }, entryDriver: drivers.entryDriver || undefined, payloadDriver: drivers.payloadDriver || undefined, @@ -113,6 +118,57 @@ export class Store extends EventTarget { relayWillowEvents(this, this.willow); } + + createDrop( + query: Query, + encryptTransform: TransformStream = new TransformStream() + ): ReadableStream | ValidationError { + const willowQueryParams = queryToWillowQueryParams(query); + + if (isErr(willowQueryParams)) { + return willowQueryParams; + } + + return Willow.createDrop({ + areaOfInterest: willowQueryParams.areaOfInterest, + schemes: { + namespace: namespaceScheme, + subspace: subspaceScheme, + path: pathScheme, + payload: this.payloadScheme, + }, + store: this.willow, + encodeAuthorisationToken: this.authorizationScheme.tokenEncoding.encode, + encryptTransform, + }); + } + + async ingestDrop( + drop: ReadableStream, + decryptTransform: TransformStream = new TransformStream() + ) { + await Willow.ingestDrop({ + decodeStreamAuthorisationToken: + this.authorizationScheme.tokenEncoding.decodeStream, + decryptTransform, + dropStream: drop, + // deno-lint-ignore require-await + getStore: async (namespace) => { + if (namespaceScheme.isEqual(namespace, this.willow.namespace)) { + return this.willow; + } else { + throw new EarthstarError("Cannot ingest drop: drop is for different namespace") + } + }, + schemes: { + namespace: namespaceScheme, + subspace: subspaceScheme, + path: pathScheme, + payload: this.payloadScheme, + }, + }); + } + /** Create (or update) a document for a given identity and path. * * ```ts