npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pubky-app-specs

v0.4.2

Published

Pubky.app Data Model Specifications

Readme

Pubky App Specs · pubky-app-specs

npm version License: MIT

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-specs

Note: This package uses WASM with embedded bytes for automatic initialization. No manual WASM loading required - just import and use!


🚀 Quick Start

  1. Import the library.
  2. Construct a PubkySpecsBuilder(pubkyId) object.
  3. Create validated domain objects (User, Post, Tag, etc.).
  4. 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