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

@frmwrkd/snag-core

v0.1.3

Published

Snag schemas: the single source of truth for captures, tasks and plans.

Readme

@frmwrkd/snag-core

Zod schemas and TypeScript types for Snag — the single source of truth.

Licence: Apache-2.0 Spec: docs/02-BACKEND-SPEC.md §5 (data model) and §3.1 (repo map). Locked decisions are docs/00-MASTER-BUILD-PLAN.md §7 — do not relitigate them.

Every type in this package comes from z.infer. There are no hand-written type duplicates, and nothing outside this package may define a schema it owns.


If you are building against this package, read these six things

1. IDs are strict. Use the minting helpers in your fixtures.

cap_, thr_, tsk_, plan_ followed by exactly 26 Crockford-base32 characters. The abbreviated ids printed in the spec — cap_01J8X, tsk_01J9C... — are documentation shorthand and will not parse.

import { newCaptureId, newTaskUid, formatTaskLabel } from '@frmwrkd/snag-core';

const id = newCaptureId();                 // cap_01K1R7Q0F8ZC4M7B9V2XE6HTPD
const uid = newTaskUid(1_700_000_000_000); // seeded — deterministic in tests
const label = formatTaskLabel(12);         // SNAG-12

Tasks carry both: uid (ULID) is what depends_on, blocks, plan.waves and all sync resolve against; id (SNAG-<n>) is a human label from next_task_id in .snag/config.json and is never reused or renumbered.

2. Severity: never compare the strings.

p0 is the most severe, so 'p1' > 'p0' is true and inverts everything.

import { rank, precedes, compareOrder, sortByOrder } from '@frmwrkd/snag-core';

rank('p0') === 3;  rank('p3') === 0;

precedes(a, b) is a total order over (rank desc, blast_radius desc, effort asc, uid asc). Wave assignment (doc 02 §8 Stage 5) depends on ties being impossible — a plain severity comparison emits no edge for the common p2-vs-p2 case and silently lets two tasks edit the same file inside one wave.

3. The field is v, never version.

Including map.json, whose §3.1 example prints version. §5 is the authority; RepoMapSchema uses v and src/map.ts records the deviation. The nested framework.version is untouched — that is the framework's own version.

4. SnagConfigSchema.parse({}) is the shipped config.

Every default from doc 02 §5.6 is baked in, so there is no second copy to drift. Two that matter: privacy.mask_all_inputs is true (rrweb ships it false), and plan.cluster_distance 0.18 is a cosine DISTANCE while plan.cross_route_similarity 0.93 is a cosine SIMILARITY.

5. Statuses are snake_case, and the transition table is data.

import { TASK_TRANSITIONS, canTransition, findTransition } from '@frmwrkd/snag-core';

canTransition('open', 'claimed');   // true
canTransition('open', 'resolved');  // false — resolved is Verify-only
findTransition('verifying', 'resolved')?.verify_only;   // true
findTransition('in_progress', 'resolved')?.condition;
// 'plan.verify_on_resolve === false'

snagd should enforce the table rather than re-deriving it. Note that last row: when Verify is disabled, snag_resolve legally jumps in_progress → resolved and records verified: false.

6. Zod strips unknown keys.

Schemas are in zod's default (strip) mode, not .strict() and not .passthrough(). A key your producer writes that the schema does not know about is dropped silently by parse. If you need a new field, add it here first.


JSON Schema for MCP

import { taskJsonSchema, toInlineJsonSchema } from '@frmwrkd/snag-core';

server.registerTool('snag_get', { outputSchema: taskJsonSchema() }, handler);

The output is draft-2020-12 and self-contained — no $ref, no $defs, so it validates standalone. [email protected] has no 2020-12 target, so src/jsonschema.ts generates draft-7 and rewrites tuple items → prefixItems and draft-4 boolean exclusive bounds → numeric ones. All seven exported schemas compile under Ajv 2020.

Use toJsonSchema(schema, name) instead if you want the $defs-wrapped document.


Layout

| File | Contents | |---|---| | src/ids.ts | ULID minting, prefixes, branded id types, SNAG-<n> labels, task filenames | | src/severity.ts | p0…p3, rank(), effort, precedes() / compareOrder() | | src/anchor.ts | Anchor (tiers 0–4), data-snag parse/format | | src/relation.ts | The nine relation types, origins, inverses | | src/capture.ts | Capture and everything under it — context, evidence, derived, redaction | | src/task.ts | Task, the status lifecycle table, files(T) / conflict_set | | src/plan.ts | Plan, wave and merged_from invariant checks | | src/thread.ts | Thread — a first-class object over ordered captures | | src/config.ts | .snag/config.json, every default from §5.6 | | src/map.ts | .snag/map.json | | src/jsonschema.ts | draft-2020-12 generation |

Commands

pnpm --filter @frmwrkd/snag-core typecheck   # src and tests
pnpm --filter @frmwrkd/snag-core test        # vitest
pnpm --filter @frmwrkd/snag-core build       # tsc → dist/

test/schemas.test.ts transcribes every worked example from doc 02 §5.1, §5.3, §5.4, §5.5, §5.6 and §3.1 and asserts each one parses and round-trips unchanged. Its header comment documents the single mechanical deviation (the spec's elided ULIDs and "…" placeholders are expanded) and the structural assertion that proves nothing else changed.