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 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bicycle-codes/ailuropoda

v0.0.7

Published

Bamboo implemented in typescript

Downloads

23

Readme

ailuropoda

tests types module semantic versioning license

Implementing bamboo, using only browser compatible cryptography.

Ailuropoda is the science name for a panda.

install

npm i -S @bicycle-codes/ailuropoda

use

Import types and functions.

import {
    create as createMsg,
    SignedPost,
    lipmaaLink,
    createBatch,
    getLipmaaPath,
    isValid,
    verifyLipmaas
} from '@bicycle-codes/ailuropoda'

data format

Log entries are { metadata, content }, where metadata is a signed object like below.

Metadata

interface Metadata {
    timestamp:number;
    proof:string,
    key:string,  // <-- base64url encoded
    seq:number;
    lipmaalink:string|null;
    prev:string|null;
    username:string;
    author:DID;
}

SignedMetadata

import { SignedMessage } from '@bicycle-codes/message'

type SignedMetadata = SignedMessage<Metadata>

Content

export interface Content {
    text:string,
    alt?:string[],
    mentions?:string[]
}

SignedPost

type SignedPost = { metadata:SignedMetadata, content:Content }

example

Use the function createBatch to create a list with lipmaa links.

See the diagram for a nice visualization of the list structure.

import { Identity, create as createID } from '@bicycle-codes/identity'
import { createCryptoComponent } from '@ssc-half-light/node-components'
import { createBatch } from '@bicycle-codes/ailuropoda'

const alicesCrytpo = await createCryptoComponent()
const alice = await createID(alicesCrytpo, {
    humanName: 'alice',
    humanReadableDeviceName: 'computer'
})

const newMsgs = [
    { content: { text: 'hello 1' } },
    { content: { text: 'hello 2' } },
    { content: { text: 'hello 3' } },
    { content: { text: 'hello 4' } },
    { content: { text: 'hello 5' } }
]

const list = await createBatch(alice, alicesCrytpo, {
    // we are just using an in-memory array of messages
    getKeyFromIndex: async (i:number, msgs:SignedPost[]) => {
        const msg = msgs[i]
        if (!msg) return null
        return msg.metadata.key
    }
}, newMsgs)  // pass in a list with message content

API

lipmaaLink (n)

Get the lipmaa number number given a sequence number.

function lipmaaLink (n:number):number

lipmaaLink example

const lipmaas = ([...Array(41).keys()]).map(n => {
    return { lipmaa: lipmaaLink(n), n }
})

lipmaas is like this:

 [
    { lipmaa: 0, n: 0 },   { lipmaa: 0, n: 1 },
    { lipmaa: 1, n: 2 },   { lipmaa: 2, n: 3 },
    { lipmaa: 1, n: 4 },   { lipmaa: 4, n: 5 },
    { lipmaa: 5, n: 6 },   { lipmaa: 6, n: 7 },
    { lipmaa: 4, n: 8 },   { lipmaa: 8, n: 9 },
    { lipmaa: 9, n: 10 },  { lipmaa: 10, n: 11 },
    { lipmaa: 8, n: 12 },  { lipmaa: 4, n: 13 },
    { lipmaa: 13, n: 14 }, { lipmaa: 14, n: 15 },
    { lipmaa: 15, n: 16 }, { lipmaa: 13, n: 17 },
    { lipmaa: 17, n: 18 }, { lipmaa: 18, n: 19 },
    { lipmaa: 19, n: 20 }, { lipmaa: 17, n: 21 },
    { lipmaa: 21, n: 22 }, { lipmaa: 22, n: 23 },
    { lipmaa: 23, n: 24 }, { lipmaa: 21, n: 25 },
    { lipmaa: 13, n: 26 }, { lipmaa: 26, n: 27 },
    { lipmaa: 27, n: 28 }, { lipmaa: 28, n: 29 },
    { lipmaa: 26, n: 30 }, { lipmaa: 30, n: 31 },
    { lipmaa: 31, n: 32 }, { lipmaa: 32, n: 33 },
    { lipmaa: 30, n: 34 }, { lipmaa: 34, n: 35 },
    { lipmaa: 35, n: 36 }, { lipmaa: 36, n: 37 },
    { lipmaa: 34, n: 38 }, { lipmaa: 26, n: 39 },
    { lipmaa: 13, n: 40 }
]

Note the lipmaa vs n properties match with this diagram.

lipmaa diagram

create (user, crypto, opts)

Create a message. This does not deal with lipmaa links. You would need to pass them in.

async function create (
    user:Identity,
    crypto:Implementation,
    opts:{
        content:Content,
        limpaalink?:string|null,  // <-- the key of the lipmaa message
        seq:number,
        prev:SignedPost|null|undefined,
    }
):Promise<SignedPost>
import { create as createMsg } from '@bicycle-codes/ailuropoda'

const post = await createMsg(
    alice,
    alicesCrytpo,
    {
        seq: 1,
        prev: null,
        content: {
            text: 'hello'
        }
    }
)

isValid (message)

Verify a message. This does not look at links, only the signature and hash.

async function isValid (msg:SignedPost):Promise<boolean>
const { isOk } = await isValid(post)
// => true

verifyLipmaas ({ messageFromKey }, msg, path)

Check that all the messages between the given message and message number 1 are valid. This will use the shortest path from the given message to the first message.

async function verifyLipmaas ({
    messageFromKey
}:{
    messageFromKey:(key:string)=>Promise<SignedPost>
}, msg:SignedPost, path?:number[]):Promise<{
    isOk: boolean,
    path:number[]
}>
const { isOk, path } = await verifyLipmaas(list2, {
    messageFromKey
}, list2[39])  // array is 0 indexed, so 39 is seq number 40

// isOk = true
// path = [40, 13, 4, 1]

getLipmaaPath (seq, prev)

Get the shortest path between the given sequence number and the first message. The parameter prev is used internally, for recusion.

function getLipmaaPath (seq:number, prev?:number[]):number[]

Return an array of sequence numbers, starting with the first:

[ 1, 4, 13 ]

createBatch (user, crypto, opts, messages)

Create a linked list of the given messages, with lipmaa links.

async function createBatch (
    user:Identity,
    crypto:Implementation,
    opts: {
        getKeyFromIndex:(i:number, msgs:SignedPost[]) => Promise<string|null>
    },
    msgs:{
        content:Content,
        seq?:number,
        prev?:SignedPost|null|undefined,
    }[],
    _out?:SignedPost[]
):Promise<SignedPost[]>

createBatch example

Create a linked list with in-memory content, starting from entry number 1.

Note in the example, getKey is synchronous, but we need to return a promise because that's what the API expects.

Takes a parameter getKeyFromIndex that will return the key for an entry given its index.

const newMsgs = [
    { content: { text: 'hello 1' } },
    { content: { text: 'hello 2' } },
    { content: { text: 'hello 3' } },
    { content: { text: 'hello 4' } },
    { content: { text: 'hello 5' } }
]

const list = await createBatch(alice, alicesCrytpo, {
    getKeyFromIndex: getKey
}, newMsgs)

async function getKey (i:number, msgs:SignedPost[]):Promise<string|null> {
    const msg = msgs[i]
    if (!msg) return null
    return msg.metadata.key
}

append (user, crypto, opts)

Given a previous message and a function that will return a message by its sequence number, create a new message with the correct lipmaa key.

async function append (
    user:Identity,
    crypto:Implementation,
    opts:{
        getBySeq:(seq:number) => Promise<SignedPost>
        content:Content,
        prev:SignedPost
    }
):Promise<SignedPost>

append example

const list = await createBatch(alice, alicesCrytpo, {
    getKeyFromIndex: async (i, msgs) => {
        return msgs[i].metadata.key
    },
}, msgs)

const newMsg = await append(alice, alicesCrytpo, {
    getBySeq: async (seq) => {
        return list[seq - 1]  // 0 vs 1 indexed
    },
    prev: list[list.length - 1],
    content: { text: 'hello 40' }
})

docs

Generated via typescript.

bicycle-codes.github.io/ailuropoda