pubky-app-specs
v0.4.2
Published
Pubky.app Data Model Specifications
Readme
Pubky App Specs · pubky-app-specs
A WASM library for building and validating structured JSON models compatible with Pubky.App social powered by @synonymdev/pubky. It handles domain objects like Users, Posts, Feeds, Bookmarks, Tags, and more. Each object is:
- Sanitized and Validated via Rust logic.
- Auto-ID’ed and Auto-Pathed according to your domain rules.
- Exported to JavaScript/TypeScript with minimal overhead.
🤔 Why Use This Crate Instead of Manual JSONs?
- Validation Consistency: Ensures your app uses the same sanitization and validation rules as Pubky indexers, avoiding errors.
- Schema Versioning: Automatically stay up-to-date with schema changes, reducing maintenance overhead.
- Auto IDs & Paths: Generates unique IDs, paths, and URLs according to Pubky standards.
- Rust-to-JavaScript Compatibility: Type-safe models that work seamlessly across Rust and JavaScript/TypeScript.
- Future-Proof: Easily adapt to new Pubky object types without rewriting JSON manually.
⚙️ Installation
npm install pubky-app-specs
# or
yarn add pubky-app-specsNote: This package uses WASM with embedded bytes for automatic initialization. No manual WASM loading required - just import and use!
🚀 Quick Start
- Import the library.
- Construct a
PubkySpecsBuilder(pubkyId)object. - Create validated domain objects (User, Post, Tag, etc.).
- Store them on the PubKy homeserver or any distributed storage solution you prefer.
Import & Usage
// ES Modules
import { PubkySpecsBuilder } from "pubky-app-specs";
// OR CommonJS
const { PubkySpecsBuilder } = require("pubky-app-specs");
function loadSpecs(pubkyId) {
// Create a specs builder instance - WASM is already initialized
const specs = new PubkySpecsBuilder(pubkyId);
return specs;
}🎨 Example Usage
Below are succinct examples to illustrate how to create or update data using pubky-app-specs and then store it with @synonymdev/pubky.
1) Creating a New User
import { Client, PublicKey } from "@synonymdev/pubky";
import { PubkySpecsBuilder } from "pubky-app-specs";
async function createUser(pubkyId) {
const client = new Client();
const specs = new PubkySpecsBuilder(pubkyId);
// Create user object with minimal fields
const {user, meta} = specs.createUser(
"Alice", // Name
"Hello from WASM", // Bio
null, // Image URL or File
null, // Links
"active" // Status
);
// meta contains { id, path, url }.
// user is the Rust "PubkyAppUser" object.
// We bring the Rust object to JS using the .toJson() method.
const userJson = user.toJson();
// Store in homeserver via pubky
const response = await client.fetch(meta.url, {
method: "PUT",
body: JSON.stringify(userJson),
credentials: "include",
});
if (!response.ok) {
throw new Error(`Failed to store user: ${response.statusText}`);
}
console.log("User stored at:", meta.url);
return { user, meta };
}2) Creating a Post
import { Client } from "@synonymdev/pubky";
import { PubkySpecsBuilder, PubkyAppPostKind } from "pubky-app-specs";
async function createPost(pubkyId, content) {
const client = new Client();
const specs = new PubkySpecsBuilder(pubkyId);
// Create the Post object
const {post, meta} = specs.createPost(
content,
PubkyAppPostKind.Short,
null, // parent post URI (for replies)
null, // embed object (for reposts)
null // attachments (array of file URLs, max 3)
);
// Store the post
const postJson = post.toJson();
await client.fetch(meta.url, {
method: "PUT",
body: JSON.stringify(postJson),
});
console.log("Post stored at:", meta.url);
return {post, meta};
}3) Creating a Post with Attachments
import { Client } from "@synonymdev/pubky";
import { PubkySpecsBuilder, PubkyAppPostKind } from "pubky-app-specs";
async function createPostWithAttachments(pubkyId, content, fileUrls) {
const client = new Client();
const specs = new PubkySpecsBuilder(pubkyId);
// Create post with attachments (max 3 allowed)
const {post, meta} = specs.createPost(
content,
PubkyAppPostKind.Image,
null, // parent
null, // embed
fileUrls // e.g. ["pubky://user/pub/pubky.app/files/abc123"]
);
const postJson = post.toJson();
console.log("Attachments:", postJson.attachments);
await client.fetch(meta.url, {
method: "PUT",
body: JSON.stringify(postJson),
});
console.log("Post with attachments stored at:", meta.url);
return {post, meta};
}4) Following a User
import { Client } from "@synonymdev/pubky";
import { PubkySpecsBuilder } from "pubky-app-specs";
async function followUser(myPubkyId, userToFollow) {
const client = new Client();
const specs = new PubkySpecsBuilder(myPubkyId);
const {follow, meta} = specs.createFollow(userToFollow);
// We only need to store the JSON in the homeserver
await client.fetch(meta.url, {
method: "PUT",
body: JSON.stringify(follow.toJson()),
});
console.log(`Successfully followed: ${userToFollow}`);
}📁 Additional Models
This library supports many more domain objects beyond User and Post. Here are a few more you can explore:
- Feeds:
createFeed(...) - Bookmarks:
createBookmark(...) - Tags:
createTag(...) - Mutes:
createMute(...) - Follows:
createFollow(...) - LastRead:
createLastRead(...) - Blobs:
createBlob(...) - Files:
createFile(...)
Each has a meta field for storing relevant IDs/paths and a typed data object.
Creating a File with Blob
import { Client } from "@synonymdev/pubky";
import { PubkySpecsBuilder, getValidMimeTypes } from "pubky-app-specs";
async function uploadFile(pubkyId, fileData, fileName, contentType, fileSize) {
const client = new Client();
const specs = new PubkySpecsBuilder(pubkyId);
// First, create and store the blob (raw binary data)
const { blob, meta: blobMeta } = specs.createBlob(fileData);
await client.fetch(blobMeta.url, {
method: "PUT",
body: JSON.stringify(blob.toJson()),
});
// Then create the file metadata pointing to the blob
const { file, meta: fileMeta } = specs.createFile(
fileName, // e.g. "vacation-photo.jpg"
blobMeta.url, // Reference to the blob
contentType, // e.g. "image/jpeg"
fileSize // Size in bytes
);
await client.fetch(fileMeta.url, {
method: "PUT",
body: JSON.stringify(file.toJson()),
});
console.log("File stored at:", fileMeta.url);
return { file, meta: fileMeta };
}✅ Validating File MIME Types
Use getValidMimeTypes() to get the list of allowed MIME types for file attachments. This helps validate files before upload without duplicating the validation list.
import { getValidMimeTypes } from "pubky-app-specs";
// Get the list of valid MIME types
const validMimeTypes = getValidMimeTypes();
// Returns: ["application/javascript", "application/json", "application/pdf", "image/png", ...]
// Validate a file before upload
function isValidFileType(mimeType) {
return validMimeTypes.includes(mimeType);
}
// Example usage
if (isValidFileType(file.type)) {
// Proceed with upload
} else {
console.error(`Invalid file type: ${file.type}`);
}🔗 URI Builder Utilities
These helper functions construct properly formatted Pubky URIs:
import {
userUriBuilder,
postUriBuilder,
bookmarkUriBuilder,
followUriBuilder,
tagUriBuilder,
muteUriBuilder,
lastReadUriBuilder,
blobUriBuilder,
fileUriBuilder,
feedUriBuilder,
} from "pubky-app-specs";
const userId = "8kkppkmiubfq4pxn6f73nqrhhhgkb5xyfprntc9si3np9ydbotto";
const targetUserId = "dzswkfy7ek3bqnoc89jxuqqfbzhjrj6mi8qthgbxxcqkdugm3rio";
// Build URIs for different resources
userUriBuilder(userId); // pubky://{userId}/pub/pubky.app/profile.json
postUriBuilder(userId, "0033SSE3B1FQ0"); // pubky://{userId}/pub/pubky.app/posts/{postId}
bookmarkUriBuilder(userId, "ABC123"); // pubky://{userId}/pub/pubky.app/bookmarks/{bookmarkId}
followUriBuilder(userId, targetUserId); // pubky://{userId}/pub/pubky.app/follows/{targetUserId}
tagUriBuilder(userId, "XYZ789"); // pubky://{userId}/pub/pubky.app/tags/{tagId}
muteUriBuilder(userId, targetUserId); // pubky://{userId}/pub/pubky.app/mutes/{targetUserId}
lastReadUriBuilder(userId); // pubky://{userId}/pub/pubky.app/last_read
blobUriBuilder(userId, "BLOB123"); // pubky://{userId}/pub/pubky.app/blobs/{blobId}
fileUriBuilder(userId, "FILE456"); // pubky://{userId}/pub/pubky.app/files/{fileId}
feedUriBuilder(userId, "FEED789"); // pubky://{userId}/pub/pubky.app/feeds/{feedId}📌 Parsing a Pubky URI
The parse_uri() function converts a Pubky URI string into a strongly typed object.
Usage:
import { parse_uri } from "pubky-app-specs";
try {
const result = parse_uri("pubky://userID/pub/pubky.app/posts/postID");
console.log(result.user_id); // "userID"
console.log(result.resource); // e.g. "posts"
console.log(result.resource_id); // "postID" or null
} catch (error) {
console.error("URI parse error:", error);
}Returns:
A ParsedUriResult object with:
- user_id: The parsed user identifier.
- resource: A string indicating the resource type.
- resource_id: An optional resource identifier.
📄 License
MIT
