diff --git a/deno.lock b/deno.lock index 0559670..9c3df80 100644 --- a/deno.lock +++ b/deno.lock @@ -16,6 +16,7 @@ "npm:@atcute/jetstream@^1.1.2": "1.1.2", "npm:@atcute/lex-cli@^2.3.1": "2.3.1", "npm:@atcute/lexicons@^1.2.2": "1.2.2", + "npm:@atcute/tid@^1.0.3": "1.0.3", "npm:@atproto/lexicon@~0.5.1": "0.5.1" }, "jsr": { @@ -116,6 +117,9 @@ "esm-env" ] }, + "@atcute/tid@1.0.3": { + "integrity": "sha512-wfMJx1IMdnu0CZgWl0uR4JO2s6PGT1YPhpytD4ZHzEYKKQVuqV6Eb/7vieaVo1eYNMp2FrY67FZObeR7utRl2w==" + }, "@atproto/common-web@0.4.3": { "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", "dependencies": [ @@ -236,7 +240,8 @@ "dependencies": [ "npm:@atcute/atproto@^3.1.9", "npm:@atcute/client@^4.0.5", - "npm:@atcute/lexicons@^1.2.2" + "npm:@atcute/lexicons@^1.2.2", + "npm:@atcute/tid@^1.0.3" ] }, "packages/shared": { diff --git a/packages/producer/deno.jsonc b/packages/producer/deno.jsonc index 270de51..9ce47da 100644 --- a/packages/producer/deno.jsonc +++ b/packages/producer/deno.jsonc @@ -6,6 +6,7 @@ "imports": { "@atcute/atproto": "npm:@atcute/atproto@^3.1.9", "@atcute/client": "npm:@atcute/client@^4.0.5", - "@atcute/lexicons": "npm:@atcute/lexicons@^1.2.2" + "@atcute/lexicons": "npm:@atcute/lexicons@^1.2.2", + "@atcute/tid": "npm:@atcute/tid@^1.0.3" } } diff --git a/packages/producer/mod.ts b/packages/producer/mod.ts index 721d95c..f31ff26 100644 --- a/packages/producer/mod.ts +++ b/packages/producer/mod.ts @@ -1,4 +1,5 @@ import { produceRequirements } from "@cistern/shared"; +import { encryptText } from "@cistern/crypto"; import type { LocalPublicKey, ProducerOptions, @@ -6,7 +7,9 @@ import type { } from "./types.ts"; import { type Did, parse, type ResourceUri } from "@atcute/lexicons"; import type { Client, CredentialManager } from "@atcute/client"; +import { now } from "@atcute/tid"; import { + type AppCisternLexiconItem, AppCisternLexiconPubkey, } from "@cistern/lexicon"; @@ -60,11 +63,46 @@ export class Producer { /** * Creates an item and saves it as a record in the user's PDS - * @todo Error if there is no selected public key - * @todo Construct valid item - * @todo Save item to PDS */ - async createItem() {} + async createItem(text: string): Promise { + if (!this.publicKey) { + throw new Error( + "no public key set; select a public key before creating an item", + ); + } + + const payload = encryptText( + Uint8Array.fromBase64(this.publicKey.record.content), + text, + ); + const record: AppCisternLexiconItem.Main = { + $type: "app.cistern.lexicon.item", + tid: now(), + algorithm: "x_wing-xchacha20_poly1305-sha3_512", + ciphertext: payload.cipherText, + contentHash: payload.hash, + contentLength: payload.length, + nonce: payload.nonce, + payload: payload.content, + pubkey: this.publicKey.uri, + }; + + const res = await this.rpc.post("com.atproto.repo.createRecord", { + input: { + collection: "app.cistern.lexicon.item", + repo: this.did, + record, + }, + }); + + if (!res.ok) { + throw new Error( + `failed to create new item: ${res.status} ${res.data.error}`, + ); + } + + return res.data.uri; + } /** * Lists public keys registered in the user's PDS