Files
twtxt-lib/README.md
Eric Woodward c776b5df6a add support for v2 hashing algorithm.
update README.md and demo file to be more in sync.
update to v0.10.0.
2026-03-29 22:34:43 -04:00

248 lines
9.2 KiB
Markdown

# twtxt-lib
An isomorphic TypeScript library of utility functions for parsing and interacting with
[`twtxt.txt` files](https://twtxt.dev/).
These functions include:
- `hashTwt`: takes the constituent parts of a "twt" and generates an
[extension-compatible hash](https://twtxt.dev/exts/twt-hash.html) for it.
- `parseTwtxt`: parses a twtxt file string, returning an object with information about the file and
its owner (including [hashes](https://twtxt.dev/exts/twt-hash.html) for each twt and any
[metadata](https://twtxt.dev/exts/metadata.html) in the file).
- `loadAndParseTwtxt`: fetches a twtxt file from _the internet_ and parses it into an object (as above).
## Features
- Isomorphic, available as an ([optionally minified](/dist-browser/twtxt-lib.min.js))
[ES6+ library for the browser](/dist-browser/twtxt-lib.js), with NPM and JSR versions coming soon.
or as a package (from [NPM](https://www.npmjs.com/package/twtxt-lib) or
[JSR](https://jsr.io/@itsericwoodward/twtxt-lib)).
- Fully typed and source-mapped (I hope).
- Built as an [ES6 Module](https://caniuse.com/es6-module) (and [ESM only](https://antfu.me/posts/move-on-to-esm-only)).
- Includes sample files and an [interactive demo](https://twtxt-lib.itsericwoodward.com/).
## Installation
This library can be installed several different ways:
### For the Browser
1. Grab the latest copy of the `twtxt-lib.js` file, either by downloading it from the
[git repo](https://git.itsericwoodward.com/eric/twtxt-lib/raw/branch/main/dist-browser/twtxt-lib.js),
the [website](https://twtxt-lib.itsericwoodward.com/dist-browser/twtxt-lib.js), or by doing a
`git clone https://git.itsericwoodward.com/eric/twtxt-lib.git` and pulling it out of the
`dist-browser` folder.
- Alternatively, you can grab the minified version from the
[same](https://git.itsericwoodward.com/eric/twtxt-lib/raw/branch/main/dist-browser/twtxt-lib.min.js)
[sources](https://twtxt-lib.itsericwoodward.com/dist-browser/twtxt-lib.min.js).
2. Add the newly acquired file to your static site / progressive web app / over-engineered blog.
3. Import the desired function(s) via ESM: `import { hashTwt, loadAndParseTwtxtFile } from "./twtxt-lib.js";`
### For Node
1. Add the package to your project.
- Via [NPM](https://www.npmjs.com/package/twtxt-lib): `yarn add twtxt-lib`
- Via [JSR](https://jsr.io/@itsericwoodward/twtxt-lib):
`yarn add jsr:@itsericwoodward/twtxt-lib`
2. Import the desired function(s) via ESM:
`import { hashTwt, loadAndParseTwtxtFile, parseTwtxt } from "twtxt-lib";`
## Usage
See the included tests and demo file for more information on how to use it.
### hashTwt
A function that takes the constituent parts of a "twt" and generates an
[extension-compatible hash](https://twtxt.dev/exts/twt-hash.html) for it, which is then returned.
Version 0.10.0 and above includes support for
[V2 of the Hashing Spec](https://git.mills.io/yarnsocial/twtxt.dev/pulls/28):
- A specific hashing version can be provided as an optional argument.
- When no version argument is provided, it defaults to using version 1 for all twts with a created
date before the epoch date (`2026-07-01T00:00:00Z`), and version 2 for all twts created on or
after the epoch.
```
import { hashTwt } from "/web/dist/twtxt-lib.js";
const hash = hashTwt({
content: "Prow scuttle parley provost Sail ho shrouds spirits boom mizzenmast yardarm.",
created: "2026-02-01T01:23:45Z",
url: "https://example.org/~pirate/twtxt.txt",
});
console.log(`Hash: ${hash}`);
```
Result
```
Hash: 7uftieq
```
### parseTwtxt
A function that parses a twtxt file string, returning an object with information about the file and
its owner (including generating [hashes](#hashTwt) for each twt and
any [metadata](https://twtxt.dev/exts/metadata.html) in the file).
```
import { base32Encode, hashTwt, loadAndParseTwtxt, parseTwtxt } from "twtxt-lib";
const fileText = `
# nick = demo_sagan
# url = https://example.net/~saganos/twtxt.txt
# avatar = https://i.pravatar.cc/150?img=69
# description = Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit?
# finger = sagaan@example.net
# link = Web https://example.net/~saganos/
#
# follow = demo_sagan https://example.net/~saganos/twtxt.txt
# follow = demo_pirate https://example.org/~pirate/twtxt.txt
# follow = demo_hipster https://example.com/demo-hipster-twtxt.txt
#
# following = 3
#
2026-02-05T23:17:47Z The ash of stellar alchemy permanence of the stars extraordinary claims require extraordinary evidence rings of Uranus vanquish the impossible encyclopaedia galactica?
2026-01-22T12:09:44Z Something incredible is waiting to be known another world hearts of the stars tendrils of gossamer clouds a still more glorious dawn awaits venture.
2026-01-10T13:44:37Z Dream of the mind's eye citizens of distant epochs a still more glorious dawn awaits preserve and cherish that pale blue dot hearts of the stars preserve and cherish that pale blue dot and billions upon billions upon billions upon billions upon billions upon billions upon billions.
2026-01-01T02:05:04Z With pretty stories for which there's little good evidence muse about a billion trillion globular star cluster inconspicuous motes of rock and gas dream of the mind's eye.
`.trim();
console.log(parseTwtxt(fileText));
```
Result:
```
{
"following": [
{
"nick": "demo_sagan",
"url": "https://example.net/~saganos/twtxt.txt"
},
{
"nick": "demo_pirate",
"url": "https://example.org/~pirate/twtxt.txt"
},
{
"nick": "demo_hipster",
"url": "https://example.com/demo-hipster-twtxt.txt"
}
],
"metadata": {
"nick": "demo_sagan",
"url": "https://example.net/~saganos/twtxt.txt",
"avatar": "https://i.pravatar.cc/150?img=69",
"description": "Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit?",
"finger": "sagaan@example.net",
"link": "Web https://example.net/~saganos/",
"following": "3"
},
"twts": [
{
"content": "With pretty stories for which there's little good evidence muse about a billion trillion globular star cluster inconspicuous motes of rock and gas dream of the mind's eye.",
"created": "2026-01-01T02:05:04Z",
"createdUTC": "2026-01-01T02:05:04.000Z",
"hash": "64wq3va"
},
{
"content": "Dream of the mind's eye citizens of distant epochs a still more glorious dawn awaits preserve and cherish that pale blue dot hearts of the stars preserve and cherish that pale blue dot and billions upon billions upon billions upon billions upon billions upon billions upon billions.",
"created": "2026-01-10T13:44:37Z",
"createdUTC": "2026-01-10T13:44:37.000Z",
"hash": "72vgpyq"
},
{
"content": "Something incredible is waiting to be known another world hearts of the stars tendrils of gossamer clouds a still more glorious dawn awaits venture.",
"created": "2026-01-22T12:09:44Z",
"createdUTC": "2026-01-22T12:09:44.000Z",
"hash": "bgg5rqq"
},
{
"content": "The ash of stellar alchemy permanence of the stars extraordinary claims require extraordinary evidence rings of Uranus vanquish the impossible encyclopaedia galactica?",
"created": "2026-02-05T23:17:47Z",
"createdUTC": "2026-02-05T23:17:47.000Z",
"hash": "qa4xrla"
}
]
}
```
### loadAndParseText
Aan async function that fetches a `twtxt.txt`-compatible file from a URL and parses it,
returning the extracted data as an object.
```
import { loadAndParseTwtxtFile } from "/web/dist/twtxt-lib.js";
// run in an IIFE (or event listener) to avoid issues with top-level await
(async () => {
try {
const parsedFile = await loadAndParseTwtxtFile(
"/twtxt-demos/demo-hipster-twtxt.txt",
);
console.log(parsedFile);
} catch (err) {
console.error(err);
}
})();
```
Result:
```
{
"following": [
{
"nick": "demo_hipster",
"url": "https://example.com/demo-hipster-twtxt.txt"
},
{
"nick": "demo_pirate",
"url": "https://example.org/~pirate/twtxt.txt"
},
{
"nick": "demo_sagan",
"url": "https://example.net/~saganos/twtxt.txt"
}
],
"metadata": {
"nick": "demo_hipster",
"url": "https://example.com/demo-hipster-twtxt.txt",
"avatar": "https://i.pravatar.cc/150?img=67",
"description": "Kitsch ut post-ironic, bruh tilde non shabby chic iceland fixie consequat?"
},
"twts": [
{
"content": "Normcore tilde ad selfies, culpa cupping nostrud gatekeep aesthetic PBR&B 3 wolf moon mustache twee.",
"created": "2025-06-02T18:47:01+01:00",
"createdUTC": "2025-06-02T17:47:01.000Z",
"hash": "ymiydvq"
},
{
"content": "Cardigan jean shorts eu 90's. Kitsch knausgaard culpa, marfa mumblecore portland raclette banjo retro exercitation pariatur snackwave williamsburg.",
"created": "2025-07-05T00:17:46+02:00",
"createdUTC": "2025-07-04T22:17:46.000Z",
"hash": "c6bm4sq"
},
...
],
"lastModified": "2026-02-22T20:56:59.000Z"
}
```
When using in a web browser, be aware of the potential for issues with
[Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS).
## License
Copyright (c) 2026 Eric Woodward, released under the [MIT License](https://www.itsericwoodward.com/licenses/mit/).