Functional Weave
Code in TypeScript

todo.parse-tags

Read a tags box as typed, "#Home, work errands", into normalised tags: split on spaces and commas.

1.0.0 · published 2026-10-03 by charlie · Anterra

Pinned by 12 tests, run in TypeScript, Python and Rust.

What it does

Reads a tags box as a person types it into the tags a todo stores: `#Home, work errands` is `["home", "work", "errands"]`.

- **Separators:** every run of ASCII whitespace (space, tab, CR, LF, VT, FF) or commas ends one tag and starts the next. So `home office` is two tags; type `home-office` for one. Semicolons and other punctuation are not separators: they end up as hyphens inside the slug (`a;b` is `a-b`). - **Each piece** then goes through todo.normalise-tags: leading `#`s dropped, lower-case slug form (`Café` is `cafe`), pieces that come out empty (`#`, `日本`) dropped, and repeats removed, keeping the first position. - Only ASCII whitespace separates, so every language splits the same way (Python's `\s` and JavaScript's `\s` disagree on Unicode spaces).

For example

  • parseTags(home work) → home, work words separated by a space are two tags
  • parseTags(#Home, #Work) → home, work hashes and a comma with a space after it
  • parseTags(home,work,, errands) → home, work, errands runs of commas and spaces are one separator, and empty pieces vanish

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

export function parseTags(text: string): readonly string[]
textstringwhat was typed in the box, e.g. "#Home, work errands"
returnsstring[]e.g. ["home", "work", "errands"], normalised by todo.normalise-tags

Your code names it in one line, in the file that uses it

import { parseTags } from "#fune/todo.parse-tags@^1";
impl/typescript.ts · 11 lines · open · raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { normaliseTags } from "./todo_normalise_tags.ts";  ← from todo.normalise-tags ^1.0.0 · built alongside by fune

/**
 * A tags box as typed: every run of ASCII whitespace or commas separates two
 * tags, and each piece then goes through todo.normalise-tags (no #, slug
 * form, empties and repeats dropped). A tag of two words is typed with a
 * hyphen, "home-office", since a space starts the next tag.
 */
export function parseTags(text: string): readonly string[] {
  return normaliseTags(text.split(/[ \t\n\r\v\f,]+/));
}

Install

fune build

With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:

fune add todo.parse-tags
Download for TypeScript todo.parse-tags-1.0.0-typescript.fune · 4,434 bytes sha256 9b65db954aabe0afcbe1012976baadb41e906c0d99e9cb7b91f8bf514fb26935

The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./todo.parse-tags-1.0.0-typescript.fune, or fetch it from a terminal with fune pull todo.parse-tags@1.0.0:typescript.

The whole function, every language, is one file too: todo.parse-tags-1.0.0.fune, 5,584 bytes, sha256 734fe72a197d9ff6cf0d95a156b13bbc0e71941a27aa25bb703f1bc63f4a768c. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before todo.parse-tags

after — your function gets the result and the arguments, and returns the final result.

// fune: after todo.parse-tags

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

// fune: replace todo.normalise-tags in todo.parse-tags

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show todo.parse-tags --steps.

// fune: step todo.parse-tags after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
words separated by a space are two tags home work → home, work
hashes and a comma with a space after it #Home, #Work → home, work
runs of commas and spaces are one separator, and empty pieces vanish home,work,, errands → home, work, errands
an empty box is no tags →
only separators is no tags , , →
the same tag typed three ways is one tag Home HOME #home → home
a hyphen keeps two words in one tag home-office Q3 → home-office, q3
tabs and line breaks separate too; Latin-1 letters are transliterated Café Straße urgent → cafe, strasse, urgent
a tag in another script normalises to nothing and is dropped #日本 home → home
a semicolon is not a separator: it becomes a hyphen inside the slug a_b;c → a-b-c
Show the other 2 tests
CaseArgumentsExpected
repeats keep their first position work, Errands, #WORK, errands → work, errands
a lone hash between tags is dropped home # work → home, work

More from the author

It never throws and does not enforce the limits (10 tags, 30 characters): todo.item's `validateDraft` reports those, so the form can say what is wrong instead of silently cutting what was typed.

Files

PathBytes
README.md1,001
impl/python.py465
impl/rust.rs615
impl/typescript.ts472
vectors.json1,371