diff --git a/deno.lock b/deno.lock index f86a0a3..b921d32 100644 --- a/deno.lock +++ b/deno.lock @@ -1,20 +1,16 @@ { "version": "5", "specifiers": { - "jsr:@noble/ciphers@*": "2.0.1", "jsr:@noble/ciphers@^2.0.1": "2.0.1", "jsr:@noble/curves@2.0": "2.0.1", - "jsr:@noble/hashes@*": "2.0.1", "jsr:@noble/hashes@2": "2.0.1", "jsr:@noble/hashes@2.0": "2.0.1", "jsr:@noble/hashes@^2.0.1": "2.0.1", - "jsr:@noble/post-quantum@*": "0.5.2", "jsr:@noble/post-quantum@~0.5.2": "0.5.2", - "jsr:@std/assert@^1.0.14": "1.0.15", + "jsr:@std/assert@^1.0.14": "1.0.14", "jsr:@std/expect@^1.0.17": "1.0.17", - "jsr:@std/internal@^1.0.10": "1.0.12", - "jsr:@std/internal@^1.0.12": "1.0.12", - "npm:@atcute/lex-cli@*": "2.3.1", + "jsr:@std/internal@^1.0.10": "1.0.10", + "npm:@atcute/client@^4.0.5": "4.0.5", "npm:@atcute/lex-cli@^2.3.1": "2.3.1", "npm:@atcute/lexicons@^1.2.2": "1.2.2", "npm:@atproto/lexicon@~0.5.1": "0.5.1" @@ -39,24 +35,38 @@ "jsr:@noble/hashes@2.0" ] }, - "@std/assert@1.0.15": { - "integrity": "d64018e951dbdfab9777335ecdb000c0b4e3df036984083be219ce5941e4703b", + "@std/assert@1.0.14": { + "integrity": "68d0d4a43b365abc927f45a9b85c639ea18a9fab96ad92281e493e4ed84abaa4", "dependencies": [ - "jsr:@std/internal@^1.0.12" + "jsr:@std/internal" ] }, "@std/expect@1.0.17": { "integrity": "316b47dd65c33e3151344eb3267bf42efba17d1415425f07ed96185d67fc04d9", "dependencies": [ "jsr:@std/assert", - "jsr:@std/internal@^1.0.10" + "jsr:@std/internal" ] }, - "@std/internal@1.0.12": { - "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" + "@std/internal@1.0.10": { + "integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7" } }, "npm": { + "@atcute/client@4.0.5": { + "integrity": "sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA==", + "dependencies": [ + "@atcute/identity", + "@atcute/lexicons" + ] + }, + "@atcute/identity@1.1.1": { + "integrity": "sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q==", + "dependencies": [ + "@atcute/lexicons", + "@badrap/valita" + ] + }, "@atcute/lex-cli@2.3.1": { "integrity": "sha512-HrHD91CFSFd/p0UFe3akFA1HXiboQwd5LbYiU0srKdLxGX+NLTX/EdCdhbLV6M7LsXdmxk7PB6BMcprsX4rbvg==", "dependencies": [ @@ -150,6 +160,12 @@ }, "workspace": { "members": { + "packages/consumer": { + "dependencies": [ + "npm:@atcute/client@^4.0.5", + "npm:@atcute/lexicons@^1.2.2" + ] + }, "packages/crypto": { "dependencies": [ "jsr:@noble/ciphers@^2.0.1", @@ -164,6 +180,11 @@ "npm:@atcute/lexicons@^1.2.2", "npm:@atproto/lexicon@~0.5.1" ] + }, + "packages/shared": { + "dependencies": [ + "npm:@atcute/lexicons@^1.2.2" + ] } } } diff --git a/packages/consumer/README.md b/packages/consumer/README.md new file mode 100644 index 0000000..c41d62b --- /dev/null +++ b/packages/consumer/README.md @@ -0,0 +1,7 @@ +# `@cistern/consumer` + +The Consumer module is responsible for the following: + +- Generating key pairs +- Retrieving and decrypting items +- Subscribing to Jetstream to monitor for items diff --git a/packages/consumer/deno.jsonc b/packages/consumer/deno.jsonc new file mode 100644 index 0000000..c3c9de6 --- /dev/null +++ b/packages/consumer/deno.jsonc @@ -0,0 +1,10 @@ +{ + "name": "@cistern/consumer", + "exports": { + ".": "./mod.ts" + }, + "imports": { + "@atcute/client": "npm:@atcute/client@^4.0.5", + "@atcute/lexicons": "npm:@atcute/lexicons@^1.2.2" + } +} diff --git a/packages/consumer/mod.ts b/packages/consumer/mod.ts new file mode 100644 index 0000000..0c8f022 --- /dev/null +++ b/packages/consumer/mod.ts @@ -0,0 +1,67 @@ +import { Client, CredentialManager } from "@atcute/client"; +import { resolveMiniDoc } from "@cistern/shared"; +import type { Did } from "@atcute/lexicons/syntax"; +import type { ConsumerOptions, ConsumerParams, LocalKeyPair } from "./types.ts"; + +export async function createConsumer( + options: ConsumerOptions, +): Promise { + const miniDoc = await resolveMiniDoc(options.handle); + const manager = new CredentialManager({ service: miniDoc.pds }); + const rpc = new Client({ handler: manager }); + await manager.login({ + identifier: miniDoc.handle, + password: options.appPassword, + }); + + return new Consumer({ + miniDoc, + manager, + rpc, + options, + }); +} + +/** + * Client for generating keys and decoding Cistern items. + */ +export class Consumer { + did: Did; + keypair?: LocalKeyPair; + rpc: Client; + manager: CredentialManager; + + constructor(params: ConsumerParams) { + this.did = params.miniDoc.did; + this.keypair = params.options.keypair; + this.rpc = params.rpc; + this.manager = params.manager; + } + + /** + * Generates a key pair, uploading the public key to PDS and returning the pair. + * @todo Generate key pair + * @todo Store private key in local registry + * @todo Upload public key to PDS + */ + async generateKeyPair() {} + + /** + * Returns an async iterator that returns pages of the user's items from their PDS. + * @todo List items from repo + */ + async listItems() {} + + /** + * Subscribes to the Jetstreams for the user's items. + * @todo Add `@atcute/jetstream` dependency + * @todo Return an async iterator + */ + async getItemSubscription() {} + + /** + * Deletes an item from the user's PDS by record key. + * @todo Delete item from PDS + */ + async deleteItem() {} +} diff --git a/packages/consumer/types.ts b/packages/consumer/types.ts new file mode 100644 index 0000000..ceb3110 --- /dev/null +++ b/packages/consumer/types.ts @@ -0,0 +1,21 @@ +import type { Client, CredentialManager } from "@atcute/client"; +import type { MiniDoc } from "@cistern/shared"; +import type { AppCisternLexiconPubkey } from "@cistern/lexicon"; + +export interface LocalKeyPair { + privateKey: string; + publicKey: AppCisternLexiconPubkey.Main; +} + +export interface ConsumerOptions { + handle: string; + appPassword: string; + keypair?: LocalKeyPair; +} + +export interface ConsumerParams { + miniDoc: MiniDoc; + manager: CredentialManager; + rpc: Client; + options: ConsumerOptions; +}