git » sdk » commit 76b4341

Add more docs

author Stephen Paul Weber
2025-09-29 20:37:03 UTC
committer Stephen Paul Weber
2025-09-29 20:37:03 UTC
parent cded225623ed3bab27e7809eaf9527b0fcdbd716

Add more docs

HaxeCBridge.hx +20 -11
HaxeSwiftBridge.hx +8 -1
borogove/Chat.hx +23 -6
borogove/ChatMessage.hx +25 -1
borogove/ChatMessageBuilder.hx +16 -2
borogove/Client.hx +5 -0
borogove/Hash.hx +36 -0
borogove/Notification.hx +36 -0
borogove/Push.hx +7 -0
borogove/Reaction.hx +34 -7
borogove/persistence/MediaStoreFS.hx +12 -0
borogove/persistence/Sqlite.hx +20 -0

diff --git a/HaxeCBridge.hx b/HaxeCBridge.hx
index 5a8b2a4..2803fea 100644
--- a/HaxeCBridge.hx
+++ b/HaxeCBridge.hx
@@ -249,7 +249,7 @@ class HaxeCBridge {
 						wrap = true;
 						final atype = convertSecondaryTPtoType(path.params[0]);
 						final aargs = atype.args;
-						args.push({name: "handler", type: TPath({name: "Callable", pack: ["cpp"], params: [TPType(TFunction(aargs.concat([TPath({name: "RawPointer", pack: ["cpp"], params: [TPType(TPath({ name: "Void", pack: ["cpp"] }))]})]), TPath({name: "Void", pack: []})))]})});
+						args.push({name: "handler", type: TPath({name: "Callable", pack: ["cpp"], params: [TPType(TFunction(aargs.concat([TNamed("handler__context", TPath({name: "RawPointer", pack: ["cpp"], params: [TPType(TPath({ name: "Void", pack: ["cpp"] }))]}))]), TPath({name: "Void", pack: []})))]})});
 						promisify.push(macro v);
 						if (atype.retainType == null) {
 							promisifyE.push(macro false);
@@ -264,13 +264,14 @@ class HaxeCBridge {
 						promisify.push(macro handler__context);
 						promisifyE.push(macro handler__context);
 						wrapper.ret = TPath({name: "Void", pack: []});
+						if (wrapper.doc != null) wrapper.doc = ~/@returns Promise resolving to/.replace(wrapper.doc, "@param handler which receives");
 					default:
 					}
 					if (wrap) {
 						if (outPtr) {
 							wrapper.kind = FFun({ret: wrapper.ret, params: fun.params, expr: macro { final out = $i{field.name}($a{passArgs}); if (outPtr != null) { cpp.Pointer.fromRaw(outPtr).set_ref(out); } return out.length; }, args: args});
 						} else if (promisify.length > 0) {
-							wrapper.kind = FFun({ret: wrapper.ret, params: fun.params, expr: macro $i{field.name}($a{passArgs}).then(v->handler($a{promisify}), e->handler($a{promisifyE})), args: args});
+							wrapper.kind = FFun({ret: wrapper.ret, params: fun.params, expr: macro if (handler == null) $i{field.name}($a{passArgs}); else $i{field.name}($a{passArgs}).then(v->handler($a{promisify}), e->handler($a{promisifyE})), args: args});
 						} else {
 							wrapper.kind = FFun({ret: wrapper.ret, params: fun.params, expr: macro return $i{field.name}($a{passArgs}), args: args});
 						}
@@ -487,6 +488,7 @@ class HaxeCBridge {
 				case FVar(_), FMethod(MethMacro): false; // skip macro methods
 				case FMethod(_): true;
 			}
+
 			if (!isConvertibleMethod) return;
 
 			// f is public static function
@@ -535,7 +537,7 @@ class HaxeCBridge {
 					cFuncName = hx.strings.Strings.toLowerUnderscore(cFuncName);
 
 					var cleanDoc = f.doc != null ? StringTools.trim(removeIndentation(f.doc)) : null;
-					
+
 					cConversionContext.addTypedFunctionDeclaration(cFuncName, functionDescriptor, cleanDoc, f.pos);
 
 					inline function getRootCType(t: Type) {
@@ -627,6 +629,9 @@ class HaxeCBridge {
 			 * Everything returned from an SDK procedure or passed to a function
 			 * pointer, both strings and opaque types, must be passed to
 			 * borogove_release when you are done with it.
+			 *
+			 * Thread-safety: All calls can be made from any thread.
+			 *                Callbacks may run on any thread.
 			 */
 
 			#ifndef __${hx.strings.Strings.toUpperUnderscore(namespace)}_H
@@ -677,8 +682,6 @@ class HaxeCBridge {
 			 *
 			 * After executing no more calls to SDK functions can be made (as these will hang waiting for a response).
 			 *
-			 * Thread-safety: Can be called safely called on any thread.
-			 *
 			 * @param wait If `true`, this function will wait for all events scheduled to execute in the future on the SDK thread to complete. If `false`, immediate pending events will be finished and the SDK stopped without executing events scheduled in the future
 			 */
 			$prefix void ${namespace}_stop(bool wait);
@@ -1140,7 +1143,7 @@ enum CModifier {
 enum CType {
 	Ident(name: String, ?modifiers: Array<CModifier>);
 	Pointer(t: CType, ?modifiers: Array<CModifier>);
-	FunctionPointer(name: String, argTypes: Array<CType>, ret: CType, ?modifiers: Array<CModifier>);
+	FunctionPointer(name: String, argNames: Array<String>, argTypes: Array<CType>, ret: CType, ?modifiers: Array<CModifier>);
 	InlineStruct(struct: CStruct);
 	Enum(name: String);
 }
@@ -1205,13 +1208,18 @@ class CPrinter {
 				(hasModifiers(modifiers) ? (printModifiers(modifiers) + ' ') : '') + name + (argName == null ? '' : ' $argName');
 			case Pointer(t, modifiers):
 				printType(t) + '*' + (hasModifiers(modifiers) ? (' ' + printModifiers(modifiers)) : '') + (argName == null ? '' : ' $argName');
-			case FunctionPointer(name, argTypes, ret):
+			case FunctionPointer(name, argNames, argTypes, ret):
+				final args = [];
+				for (i in 0...argTypes.length) {
+					final atype = printType(argTypes[i]);
+					args.push(argNames[i] == "" ? atype : ~/\*$/.replace(atype, " *") + " " + argNames[i]);
+				}
 				if (argName == "") {
-					'${printType(ret)}(${argTypes.length > 0 ? argTypes.map((t) -> printType(t)).join(', ') : 'void'})';
+					'${printType(ret)}(${argTypes.length > 0 ? args.join(', ') : 'void'})';
 				} else if (argName == null) {
-					'${printType(ret)} (* $name) (${argTypes.length > 0 ? argTypes.map((t) -> printType(t)).join(', ') : 'void'})';
+					'${printType(ret)} (* $name) (${argTypes.length > 0 ? args.join(', ') : 'void'})';
 				} else {
-					'${printType(ret)} (* $argName) (${argTypes.length > 0 ? argTypes.map((t) -> printType(t)).join(', ') : 'void'})';
+					'${printType(ret)} (* $argName) (${argTypes.length > 0 ? args.join(', ') : 'void'})';
 				}
 			case InlineStruct(struct):
 				'struct {${printFields(struct.fields, false)}}' + (argName == null ? '' : ' $argName');
@@ -1653,7 +1661,7 @@ class CConverterContext {
 		return switch cType {
 			case Ident(name, modifiers): Ident(name, _setModifier(modifiers));
 			case Pointer(type, modifiers): Pointer(type, _setModifier(modifiers));
-			case FunctionPointer(name, argTypes, ret, modifiers): FunctionPointer(name, argTypes, ret, _setModifier(modifiers));
+			case FunctionPointer(name, argNames, argTypes, ret, modifiers): FunctionPointer(name, argNames, argTypes, ret, _setModifier(modifiers));
 			case InlineStruct(struct): cType;
 			case Enum(name): cType; 
 		}
@@ -1731,6 +1739,7 @@ class CConverterContext {
 		var ident = safeIdent('function_' + args.map(arg -> typeDeclarationIdent(arg.t, false)).concat([typeDeclarationIdent(ret, false)]).join('_'));
 		var funcPointer: CType = FunctionPointer(
 			ident,
+			args.map(arg -> arg.name),
 			args.map(arg -> convertType(arg.t, false, false, pos)),
 			convertType(ret, false, false, pos)
 		);
diff --git a/HaxeSwiftBridge.hx b/HaxeSwiftBridge.hx
index 3889780..883c78f 100644
--- a/HaxeSwiftBridge.hx
+++ b/HaxeSwiftBridge.hx
@@ -424,7 +424,14 @@ class HaxeSwiftBridge {
 
 					cFuncName = hx.strings.Strings.toLowerUnderscore(cFuncName);
 
-					final cleanDoc = f.doc != null ? StringTools.trim(removeIndentation(f.doc)) : null;
+					var cleanDoc = f.doc != null ? StringTools.trim(removeIndentation(f.doc)) : null;
+					if (cleanDoc != null) {
+							switch tret {
+							case TType(_.get().name => "Promise", params):
+								cleanDoc = ~/@returns Promise resolving to/.replace(cleanDoc, "@returns");
+							default:
+						}
+					}
 
 					if (cleanDoc != null) builder.add('\t/**\n${cleanDoc.split('\n').map(l -> '\t ' + l).join('\n')}\n\t */\n');
 					switch kind {
diff --git a/borogove/Chat.hx b/borogove/Chat.hx
index 8828e79..e0f397d 100644
--- a/borogove/Chat.hx
+++ b/borogove/Chat.hx
@@ -77,6 +77,9 @@ abstract class Chat {
 	**/
 	@:allow(borogove)
 	public var uiState(default, null): UiState = Open;
+	/**
+		Is this chat blocked?
+	**/
 	public var isBlocked(default, null): Bool = false;
 	@:allow(borogove)
 	private var extensions: Stanza;
@@ -121,7 +124,7 @@ abstract class Chat {
 		@param beforeId id of the message to look before
 		@param beforeTime timestamp of the message to look before,
 		       String in format YYYY-MM-DDThh:mm:ss[.sss]+00:00
-		@returns Promise of an array of ChatMessage that are found
+		@returns Promise resolving to an array of ChatMessage that are found
 	**/
 	abstract public function getMessagesBefore(beforeId:Null<String>, beforeTime:Null<String>):Promise<Array<ChatMessage>>;
 
@@ -131,7 +134,7 @@ abstract class Chat {
 		@param afterId id of the message to look after
 		@param afterTime timestamp of the message to look after,
 		       String in format YYYY-MM-DDThh:mm:ss[.sss]+00:00
-		@returns Promise of an array of ChatMessage that are found
+		@returns Promise resolving to an array of ChatMessage that are found
 	**/
 	abstract public function getMessagesAfter(afterId:Null<String>, afterTime:Null<String>):Promise<Array<ChatMessage>>;
 
@@ -141,7 +144,7 @@ abstract class Chat {
 		@param aroundId id of the message to look around
 		@param aroundTime timestamp of the message to look around,
 		       String in format YYYY-MM-DDThh:mm:ss[.sss]+00:00
-		@returns Promise of an array of ChatMessage that are found
+		@returns Promise resolving to an array of ChatMessage that are found
 	**/
 	abstract public function getMessagesAround(aroundId:Null<String>, aroundTime:Null<String>):Promise<Array<ChatMessage>>;
 
@@ -224,11 +227,12 @@ abstract class Chat {
 		reaction.render(
 			(text) -> {
 				toSend.text = text.replace("\u{fe0f}", "");
-				return;
+				return "";
 			},
 			(text, uri) -> {
 				final hash = Hash.fromUri(uri);
 				toSend.setHtml('<img alt="' + Util.xmlEscape(text) + '" src="' + Util.xmlEscape(hash == null ? uri : hash.bobUri()) + '" />');
+				return "";
 			}
 		);
 		sendMessage(toSend);
@@ -483,8 +487,13 @@ abstract class Chat {
 		lastMessage = message;
 	}
 
-	public function setDisplayName(fn:String) {
-		this.displayName = fn;
+	/**
+		Set the display name to use for this chat
+
+		@param displayName String to use as display name
+	**/
+	public function setDisplayName(displayName: String) {
+		this.displayName = displayName;
 		bookmark();
 	}
 
@@ -550,6 +559,11 @@ abstract class Chat {
 		this.avatarSha1 = sha1;
 	}
 
+	/**
+		Set if this chat is to be trusted with our presence, etc
+
+		@param trusted Bool if trusted or not
+	**/
 	public function setTrusted(trusted:Bool) {
 		this.trusted = trusted;
 	}
@@ -566,6 +580,9 @@ abstract class Chat {
 		return true;
 	}
 
+	/**
+		@returns if this chat is currently syncing with the server
+	**/
 	public function syncing() {
 		return !client.inSync;
 	}
diff --git a/borogove/ChatMessage.hx b/borogove/ChatMessage.hx
index 5a14895..f496667 100644
--- a/borogove/ChatMessage.hx
+++ b/borogove/ChatMessage.hx
@@ -30,10 +30,25 @@ import borogove.Util;
 @:build(HaxeSwiftBridge.expose())
 #end
 class ChatAttachment {
+	/**
+		Filename
+	**/
 	public final name: Null<String>;
+	/**
+		MIME Type
+	**/
 	public final mime: String;
+	/**
+		Size in bytes
+	**/
 	public final size: Null<Int>;
+	/**
+		URIs to data
+	**/
 	public final uris: ReadOnlyArray<String>;
+	/**
+		Hashes of data
+	**/
 	public final hashes: ReadOnlyArray<Hash>;
 
 	#if cpp
@@ -51,6 +66,14 @@ class ChatAttachment {
 	}
 
 	#if cpp
+	/**
+		Create a new attachment for adding to a ChatMessage
+
+		@param name Optional filename
+		@param mime MIME type
+		@param size Size in bytes
+		@param uri URI to attachment
+	**/
 	public static function create(name: Null<String>, mime: String, size: Int, uri: String) {
 		return new ChatAttachment(name, mime, size > 0 ? size : null, [uri], []);
 	}
@@ -261,7 +284,8 @@ class ChatMessage {
 		return m;
 	}
 
-	public function getReplyId() {
+	@:allow(borogove)
+	private function getReplyId() {
 		if (replyId != null) return replyId;
 		return type == MessageChannel || type == MessageChannelPrivate ? serverId : localId;
 	}
diff --git a/borogove/ChatMessageBuilder.hx b/borogove/ChatMessageBuilder.hx
index 73b5a5c..4f5eba4 100644
--- a/borogove/ChatMessageBuilder.hx
+++ b/borogove/ChatMessageBuilder.hx
@@ -72,6 +72,9 @@ class ChatMessageBuilder {
 	@:allow(borogove)
 	private var replyTo: Array<JID> = [];
 
+	/**
+		The ID of the message sender
+	**/
 	public var senderId (get, default): Null<String> = null;
 
 	/**
@@ -234,6 +237,11 @@ class ChatMessageBuilder {
 		if (uris.length > 0) attachments.push(new ChatAttachment(name, mime, size == null ? null : Std.parseInt(size), uris, hashes));
 	}
 
+	/**
+		Add an attachment to this message
+
+		@param attachment The ChatAttachment to add
+	**/
 	public function addAttachment(attachment: ChatAttachment) {
 		attachments.push(attachment);
 	}
@@ -282,7 +290,7 @@ class ChatMessageBuilder {
 		throw "node was neither text nor element?";
 	}
 
-  	/**
+	/**
 		The ID of the Chat this message is associated with
 	**/
 	public function chatId():String {
@@ -300,10 +308,16 @@ class ChatMessageBuilder {
 		return senderId ?? sender?.asString() ?? throw "sender is null";
 	}
 
-	public function isIncoming():Bool {
+	@:allow(borogove)
+	private function isIncoming():Bool {
 		return direction == MessageReceived;
 	}
 
+	/**
+		Build this builder into an immutable ChatMessage
+
+		@returns the ChatMessage
+	**/
 	public function build() {
 		if (serverId == null && localId == null) throw "Cannot build a ChatMessage with no id";
 		final to = this.to;
diff --git a/borogove/Client.hx b/borogove/Client.hx
index ae3b363..c1fff97 100644
--- a/borogove/Client.hx
+++ b/borogove/Client.hx
@@ -641,6 +641,8 @@ class Client extends EventEmitter {
 
 	/**
 		Gets the client ready to use but does not connect to the server
+
+		@returns Promise resolving to true once the Client is ready
 	**/
 	public function startOffline(): Promise<Bool> {
 		#if cpp
@@ -813,6 +815,9 @@ class Client extends EventEmitter {
 
 	/**
 		Turn a file into a ChatAttachment for attaching to a ChatMessage
+
+		@param source The AttachmentSource to use
+		@returns Promise resolving to a ChatAttachment or null
 	**/
 	public function prepareAttachment(source: AttachmentSource): Promise<Null<ChatAttachment>> {
 		return persistence.findServicesWithFeature(accountId(), "urn:xmpp:http:upload:0").then((services) -> {
diff --git a/borogove/Hash.hx b/borogove/Hash.hx
index 27c6c37..b96f6e1 100644
--- a/borogove/Hash.hx
+++ b/borogove/Hash.hx
@@ -20,6 +20,9 @@ import HaxeCBridge;
 @:build(HaxeSwiftBridge.expose())
 #end
 class Hash {
+	/**
+		Hash algorithm name
+	**/
 	public final algorithm: String;
 	@:allow(borogove)
 	private final hash: BytesData;
@@ -30,6 +33,13 @@ class Hash {
 		this.hash = hash;
 	}
 
+	/**
+		Create a new Hash from a hex string
+
+		@param algorithm name per https://xmpp.org/extensions/xep-0300.html
+		@param hash in hex format
+		@returns Hash or null on error
+	**/
 	public static function fromHex(algorithm: String, hash: String): Null<Hash> {
 		try {
 			return new Hash(algorithm, Bytes.ofHex(hash).getData());
@@ -38,6 +48,12 @@ class Hash {
 		}
 	}
 
+	/**
+		Create a new Hash from a ni:, cid: or similar URI
+
+		@param uri The URI
+		@returns Hash or null on error
+	**/
 	public static function fromUri(uri: String): Null<Hash> {
 		if (uri.startsWith("cid:") && uri.endsWith("@bob.xmpp.org") && uri.contains("+")) {
 			final parts = uri.substr(4).split("@")[0].split("+");
@@ -64,6 +80,11 @@ class Hash {
 		return new Hash("sha-256", Sha256.make(bytes).getData());
 	}
 
+	/**
+		Represent this Hash as a URI
+
+		@returns URI as a string
+	**/
 	public function toUri() {
 		if (Config.relativeHashUri) {
 			return "/.well-known/ni/" + algorithm.urlEncode() + "/" + toBase64Url();
@@ -82,14 +103,29 @@ class Hash {
 		return "ni:///" + algorithm.urlEncode() + ";" + toBase64Url();
 	}
 
+	/**
+		Represent this Hash as a hex string
+
+		@returns hex string
+	**/
 	public function toHex() {
 		return Bytes.ofData(hash).toHex();
 	}
 
+	/**
+		Represent this Hash as a Base64 string
+
+		@returns Base64-encoded string
+	**/
 	public function toBase64() {
 		return Base64.encode(Bytes.ofData(hash), true);
 	}
 
+	/**
+		Represent this Hash as a Base64url string
+
+		@returns Base64url-encoded string
+	**/
 	public function toBase64Url() {
 		return Base64.urlEncode(Bytes.ofData(hash));
 	}
diff --git a/borogove/Notification.hx b/borogove/Notification.hx
index d0dc567..fe91a4e 100644
--- a/borogove/Notification.hx
+++ b/borogove/Notification.hx
@@ -14,17 +14,53 @@ import HaxeCBridge;
 @:build(HaxeSwiftBridge.expose())
 #end
 class Notification {
+	/**
+		The title
+	**/
 	public final title: String;
+	/**
+		The body text
+	**/
 	public final body: String;
+	/**
+		The ID of the associated account
+	**/
 	public final accountId: String;
+	/**
+		The ID of the associated chat
+	**/
 	public final chatId: String;
+	/**
+		The ID of the message sender
+	**/
 	public final senderId: String;
+	/**
+		The serverId of the message
+	**/
 	public final messageId: String;
+	/**
+		The type of the message
+	**/
 	public final type: MessageType;
+	/**
+		If this is a call notification, the call status
+	**/
 	public final callStatus: Null<String>;
+	/**
+		If this is a call notification, the call session ID
+	**/
 	public final callSid: Null<String>;
+	/**
+		Optional image URI
+	**/
 	public final imageUri: Null<String>;
+	/**
+		Optional language code
+	**/
 	public final lang: Null<String>;
+	/**
+		Optional date and time of the event
+	**/
 	public final timestamp: Null<String>;
 
 	@:allow(borogove)
diff --git a/borogove/Push.hx b/borogove/Push.hx
index fcacbe6..1390996 100644
--- a/borogove/Push.hx
+++ b/borogove/Push.hx
@@ -18,6 +18,13 @@ import HaxeCBridge;
 @:build(HaxeSwiftBridge.expose())
 #end
 class Push {
+	/**
+		Receive a new push notification from some external system
+
+		@param data the raw data from the push
+		@param persistence the persistence layer to write into
+		@returns a Notification representing the push data
+	**/
 	public static function receive(data: String, persistence: Persistence) {
 		var stanza = Stanza.parse(data);
 		if (stanza == null) return null;
diff --git a/borogove/Reaction.hx b/borogove/Reaction.hx
index d8bc0e9..a105955 100644
--- a/borogove/Reaction.hx
+++ b/borogove/Reaction.hx
@@ -13,14 +13,24 @@ import HaxeCBridge;
 @:build(HaxeSwiftBridge.expose())
 #end
 class Reaction {
+	/**
+		ID of who sent this Reaction
+	**/
 	public final senderId: String;
+	/**
+		Date and time when this Reaction was sent,
+		in format YYYY-MM-DDThh:mm:ss[.sss]+00:00
+	**/
 	public final timestamp: String;
-	public final text: String;
-	public final key: String;
-	public final envelopeId: Null<String>;
+	@:allow(borogove)
+	private final text: String;
+	@:allow(borogove)
+	private final key: String;
+	@:allow(borogove)
+	private final envelopeId: Null<String>;
 
 	@:allow(borogove)
-	public function new(senderId: String, timestamp: String, text: String, envelopeId: Null<String> = null, key: Null<String> = null) {
+	private function new(senderId: String, timestamp: String, text: String, envelopeId: Null<String> = null, key: Null<String> = null) {
 		this.senderId = senderId;
 		this.timestamp = timestamp;
 		this.text = text.replace("\u{fe0f}", "");
@@ -32,13 +42,26 @@ class Reaction {
 		Create a new Unicode reaction to send
 
 		@param unicode emoji of the reaction
+		@returns Reaction
 	**/
 	public static function unicode(unicode: String) {
 		return new Reaction("", "", unicode);
 	}
 
-	@:allow(borogove)
-	private function render<T>(forText: (String) -> T, forImage: (String, String) -> T) {
+	/**
+		Create a new Unicode reaction to send
+
+		@param forText Callback called if this is a textual reaction.
+		               Called with the unicode String.
+		@param forImage Callback called if this is a custom/image reaction.
+		               Called with the name and the URI to the image.
+		@returns the return value of the callback
+	**/
+	#if cpp
+	public function render(forText: (String) -> String, forImage: (String, String) -> String) {
+	#else
+	public function render<T>(forText: (String) -> T, forImage: (String, String) -> T) {
+	#end
 		return forText(text + "\u{fe0f}");
 	}
 }
@@ -62,13 +85,17 @@ class CustomEmojiReaction extends Reaction {
 
 		@param text name of custom emoji
 		@param uri URI for media of custom emoji
+		@returns Reaction
 	**/
 	public static function custom(text: String, uri: String) {
 		return new CustomEmojiReaction("", "", text, uri);
 	}
 
-	@:allow(borogove)
+	#if cpp
+	override public function render(forText: (String) -> String, forImage: (String, String) -> String) {
+	#else
 	override public function render<T>(forText: (String) -> T, forImage: (String, String) -> T) {
+	#end
 		final hash = Hash.fromUri(uri);
 		return forImage(text, hash?.toUri() ?? uri);
 	}
diff --git a/borogove/persistence/MediaStoreFS.hx b/borogove/persistence/MediaStoreFS.hx
index ef2300e..c52d2cf 100644
--- a/borogove/persistence/MediaStoreFS.hx
+++ b/borogove/persistence/MediaStoreFS.hx
@@ -12,11 +12,17 @@ import thenshim.Promise;
 #if cpp
 @:build(HaxeCBridge.expose())
 @:build(HaxeSwiftBridge.expose())
+@HaxeCBridge.name("borogove_persistence_media_store_fs")
 #end
 class MediaStoreFS implements MediaStore {
 	private final blobpath: String;
 	private var kv: Null<KeyValueStore> = null;
 
+	/**
+		Store media on the filesystem
+
+		@param path where on filesystem to store media
+	**/
 	public function new(path: String) {
 		blobpath = path;
 	}
@@ -26,6 +32,12 @@ class MediaStoreFS implements MediaStore {
 		this.kv = kv;
 	}
 
+	/**
+		Get absolute path on filesystem to a particular piece of media
+
+		@param uri The URI to the media (ni:// or similar)
+		@returns Promise resolving to the path or null
+	**/
 	public function getMediaPath(uri: String): Promise<Null<String>> {
 		final hash = Hash.fromUri(uri);
 		if (hash.algorithm == "sha-256") {
diff --git a/borogove/persistence/Sqlite.hx b/borogove/persistence/Sqlite.hx
index 8d38c27..68994c8 100644
--- a/borogove/persistence/Sqlite.hx
+++ b/borogove/persistence/Sqlite.hx
@@ -341,6 +341,15 @@ class Sqlite implements Persistence implements KeyValueStore {
 		storeMessages(accountId, [message]);
 	}
 
+	/**
+		Get a single message
+
+		@param accountId the account the message was sent or received on
+		@param chatId the chat the message was sent or received on
+		@param serverId the serverId of the message (optional if localId is specified)
+		@param localId the localId of the message (optional if serverId is specified)
+		@returns Promise resolving to the message or null
+	**/
 	public function getMessage(accountId: String, chatId: String, serverId: Null<String>, localId: Null<String>): Promise<Null<ChatMessage>> {
 		var q = "SELECT stanza, direction, type, status, strftime('%FT%H:%M:%fZ', created_at / 1000.0, 'unixepoch') AS timestamp, sender_id, mam_id, mam_by, sync_point FROM messages WHERE account_id=? AND chat_id=?";
 		final params = [accountId, chatId];
@@ -628,6 +637,12 @@ class Sqlite implements Persistence implements KeyValueStore {
 		});
 	}
 
+	/**
+		Remove an account from storage
+
+		@param accountId the account to remove
+		@param completely if message history, etc should be removed also
+	**/
 	public function removeAccount(accountId:String, completely:Bool) {
 		db.exec("DELETE FROM accounts WHERE account_id=?", [accountId]);
 
@@ -639,6 +654,11 @@ class Sqlite implements Persistence implements KeyValueStore {
 	}
 
 
+	/**
+		List all known accounts
+
+		@returns Promise resolving to array of account IDs
+	**/
 	public function listAccounts(): Promise<Array<String>> {
 		return db.exec("SELECT account_id FROM accounts").then(result ->
 			result == null ? [] : { iterator: () -> result }.map(row -> row.account_id)