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

@wikibonsai/almanac

v0.0.1

Published

A type system (doctypes + reltypes) for markdown-based PKM systems.

Readme

almanac

A WikiBonsai Project NPM package

Type system (doctypes + reltypes) that reads doctypes (t.doc.toml) and reltypes (t.rel.toml) and defines the type system. Supports type resolution, affixes, placeholders, and validation.

Supports both TOML and YAML formats.

📔 Check your types in your 🎋 WikiBonsai digital garden.

Install

npm install almanac

Usage

Load

Load doctype and reltype definitions from TOML or YAML manifest files. Both loaders return an empty object if the file doesn't exist, so you can fall back to built-in defaults.

import type { DocTypeData, RelTypeData } from 'almanac';

const docData: DocTypeData = loadDocTypes('./t.doc.toml');
// docData = {
//   type_tree: 'default\n'
//            + '    entry\n'
//            + '    book\n'
//            + '        scifi\n'
//            + '    ...',
//   entry: { path: '/bonsai/entries/', emoji: '🏷', attrs: ['hypernym', 'synonym', ...] },
//   book:  { path: '/books/', prefix: 'bk.', attrs: ['title', 'author', ...] },
//   ...
// }

loadRelTypes()

import { loadDocTypes, loadRelTypes } from 'almanac';

const relData: RelTypeData = loadRelTypes('./t.rel.toml');
// relData = {
//   types: [
//     { name: 'antonym',  kind: 'attr', sync: 'antonym'  },
//     { name: 'hypernym', kind: 'attr', sync: 'hyponym'  },
//     { name: 'id',       kind: 'attr', computed: true    },
//     { name: 'title',    kind: 'attr'                    },
//     ...
//   ],
// }

Filtering

Specific types can be filtered for targeting types by relationship kind:

import type { RelTypeEntry } from 'almanac';

// filter types by kind
const attrTypes: RelTypeEntry[] = relData.types?.filter((t: RelTypeEntry) => t.kind === 'attr') || [];
// attrTypes = [
//   { name: 'antonym', kind: 'attr', sync: 'antonym' },
//   { name: 'author',  kind: 'attr' },
//   ...
// ]
const linkTypes: RelTypeEntry[] = relData.types?.filter((t: RelTypeEntry) => t.kind === 'link') || [];
// linkTypes = []  // no default link types

Resolve

Resolve a filename to its doctype. Resolution follows a precedence order: prefix > attr > path. Returns the default type when no match is found.

resolve()

import { resolve } from 'almanac';
import type { DocOptsIndex } from 'almanac';

const typeOpts: DocOptsIndex = docData as DocOptsIndex;
const type: string = resolve(typeOpts, 'e.my-entry');
// type = 'entry'  (matched prefix 'e.')

Affixes

Inspect and manipulate type prefixes and suffixes. These handle both literal strings and placeholder-based patterns (like :date.).

prefixes() / suffixes()

import { prefixes, suffixes } from 'almanac';

const allPrefixes: string[] = prefixes(typeOpts);
// allPrefixes = ['i.', 'e.', 'bk.', 'p.']
const allSuffixes: string[] = suffixes(typeOpts);
// allSuffixes = []

hasAffix()

import { hasAffix } from 'almanac';

const [unfixed, affixed]: [string, string] = hasAffix(typeOpts, 'e.my-entry');
// unfixed = 'my-entry', affixed = 'e.'

addAffixes() / stripAffixes()

import { addAffixes, stripAffixes } from 'almanac';

const withPrefix: string = addAffixes('my-entry', 'e.', undefined);
// withPrefix = 'e.my-entry'
const stripped: string = stripAffixes('e.my-entry', 'e.', undefined);
// stripped = 'my-entry'

Placeholders

Prefix and suffix strings can contain placeholders that are filled at runtime or matched via regex:

| Placeholder | Description | Example Output | |-------------|---------------------|----------------| | :id | Generated ID | abc123 | | :date | ISO date | 2024-01-15 | | :year | 4-digit year | 2024 | | :month | 2-digit month | 01 | | :day | 2-digit day | 15 | | :hour | 2-digit hour | 09 | | :minute | 2-digit minute | 05 |

fillPlaceholderData()

import { fillPlaceholderData } from 'almanac';

const filled: string = fillPlaceholderData(':date.daily', {
  date: new Date(2024, 0, 15),
});
// filled = '2024-01-15.daily'

convertPlaceholderToRegex()

import { convertPlaceholderToRegex } from 'almanac';

const regex: RegExp = convertPlaceholderToRegex(':date.', 'prefix');
// regex = /^\d{4}-\d{2}-\d{2}\./

Checks

Validates doctype and reltype data for internal consistency. Returns a CheckResult with errors (broken invariants) and warnings (suspicious but not broken).

| Skip name | Level | Description | |-----------|-------|-------------| | sync-pairs | error | Sync targets must exist and sync back reciprocally | | type-overrides | warning | Named sections should match a type in the types array | | type-tree | warning | Tree nodes and flat sections should match; skips HTML comments | | doctype-attrs | warning | Attrs in doctype definitions should exist in reltype types |

check()

import { check } from 'almanac';
import type { CheckResult } from 'almanac';

const result: CheckResult = check(docData, relData);
// result = {
//   errors:   [{ rule: 'sync-pairs', message: '"foo" syncs to "bar" but "bar" does not exist' }],
//   warnings: [{ rule: 'type-overrides', message: 'Named section [stale] does not match any type' }],
// }

Skip specific validators with the skip option:

const skipped: CheckResult = check(docData, relData, { skip: ['sync-pairs'] });
// skipped = { errors: [...], warnings: [...] }  // sync-pairs errors omitted

DocType File Structure

# t.doc.toml

# type hierarchy defined as a semtree string
type_tree = """
default
    entry
    book
        scifi
        non-fiction
    person
"""

# type definitions (flat sections)
[entry]
prefix = "e."
color = "#FF0000"

[book]
path = "/books/"
prefix = "bk."
attrs = ['title', 'subtitle', 'author']

[scifi]
path = "/books/sci-fi"

[daily]
prefix = ":date."
path = "/daily"

DocType Type Properties

  • attr — attribute name used to identify this type in file metadata. String.
  • attrs — polymorphic: array of inline attribute names (e.g. ['title', 'author']) or a string template reference (e.g. "t.book"). Template takes precedence at runtime if both exist.
  • color — display color. Hex string, e.g. "#FF0000".
  • emoji — display emoji. String, e.g. "🏷".
  • template — template file reference. String.
  • root — root path override. String.
  • suffix — filename suffix. String.
  • path — directory path for this type. String, e.g. "/books/".
  • prefix — filename prefix. String, e.g. "e.", "bk.", or placeholder ":date.".

Type Resolution

Doctypes are resolved with the following precedence:

  1. prefix — filename starts with the type's prefix
  2. attrs — file attributes contain the type key
  3. path — file is in the type's directory

Why type_tree instead of TOML or YAML nesting?

Type hierarchy is defined as a semtree string rather than TOML or YAML nesting to avoid namespace collisions between type names and property names. With nested sections, book.color could mean "book's color property" or "a child type called color under book." With a semtree string for hierarchy and flat sections for properties, there is no ambiguity.

Doc kind/status defaults

Doc kind categories (default, template, media, zombie) and status categories (orphan, isolate) are config-level concerns —- see trug's [doc] section.

RelType File Structure

# t.rel.toml

types = [
  # name                 kind           computed        sync
  { name = "antonym"   , kind = "attr",                  sync = "antonym"  },
  { name = "author"    , kind = "attr"                                     },
  { name = "hypernym"  , kind = "attr",                  sync = "hyponym"  },
  { name = "hyponym"   , kind = "attr",                  sync = "hypernym" },
  { name = "id"        , kind = "attr", computed = true                    },
  { name = "synonym"   , kind = "attr",                  sync = "synonym"  },
  { name = "title"     , kind = "attr"                                     },
]

# types with extra properties pulled out into their own sections:
# [author]
# target = "person"
# color  = "#FF0000"
#
# [hypernym]
# target = "entry"
# color  = "#FF0000"

RelType Type Properties

  • computed — (optional) system-managed attribute. Boolean. See [attr] in trug's config for details.
  • kind — relationship kind. "attr" (CAML attribute / wikiattr), "link" (typed wikilink), or "ref" (attr + link — appears as both an attribute and a wikilink).
  • name — the type identifier. String.
  • sync — (optional) bidirectional sync partner. String. Symmetric when self-referencing (e.g. synonym syncs with synonym). Asymmetric when cross-referencing (e.g. hypernym syncs with hyponym and vice versa) — editing one side auto-updates the other.
  • target — (optional) constrains which doctype this reltype can point to. String, e.g. author targets person.