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

nocuft

v0.1.1

Published

Build and deploy typed DiamondFire projects

Readme

Nocuft

Nocuft is a TypeScript toolchain for building and deploying DiamondFire code. This public package contains both the compile-time SDK and the nocuft CLI.

npx nocuft init arena

init creates entry.ts and tsconfig.json, then installs nocuft as a development dependency. To install it before initialization instead:

npm install --save-dev nocuft
npx nocuft init arena

The SDK declarations describe DiamondFire values and operations. Nocuft reads the TypeScript source directly; the declarations are not a JavaScript runtime.

First Project

import { events, players } from "nocuft";

export const join = events.player.join((event) => {
    event.player.sendMessage("Welcome!");
    players.all().sendMessage(`${event.player.name()} joined`);
});

Functions, events, and processes compile into DiamondFire code templates.

Values

The SDK supports text, numbers, booleans, components, locations, items, sounds, lists, dictionaries, players, and typed line variables. Constant values are embedded directly; supported dynamic expressions lower to native DiamondFire actions.

Lists

List<T> is a nominal DiamondFire value created with list(). It supports indexed reads, length, and functional updates:

import { list, plot, type List } from "nocuft";

const route: List<number> = list(10, 20, 30);
const first = route[0];
const changed = route.with(1, 25);
const extended = changed.appended(40).concatenated(list(50, 60));
const middle = extended.slice(1, 4);

const savedRoute = plot.var.saved.list<number>("route");
savedRoute.set(middle);

const nested: List<List<number>> = list(route, middle);

Player-scoped lists use the same factory and take a player target when read or written:

import { events, players } from "nocuft";

const recentScores = players.var.game.list<number>("recent-scores");

export const join = events.player.join((event) => {
    const scores = recentScores.get(event.player);
    recentScores.set(event.player, scores.appended(100));
});

List updates do not mutate their receiver; assign or store the returned list. Only indexing, length, with, appended, concatenated, and slice are SDK list operations. JavaScript array iteration and methods such as map, push, and splice are not available. TypeScript arrays remain compile-time argument containers (for example, giveItems([item(...)])) and are not assignable to List<T> or accepted as portable runtime values. Nested List values are portable, including as process and package-function inputs. Literal list indices and slice bounds must be non-negative integers and use zero-based semantics. Dynamic number indices and bounds remain supported and are passed through to the runtime.

Dictionaries

Dictionary<V> is a nominal DiamondFire runtime value, not a TypeScript Record or ordinary object. Keys are always text; an object literal passed to dictionary() is only constructor syntax and its values must all share one type:

import { dictionary, list, plot, type Dictionary } from "nocuft";

const scores = dictionary({ red: 10, blue: 20 });
// Dictionary<number>
const [red, foundRed] = scores.get("red");
const changed = scores.with("red", 15).without("blue");
const combined = changed.merged(dictionary({ red: 30, blue: 5 }));

const empty: Dictionary<number> = dictionary<number>();
const savedScores = plot.var.saved.dictionary<number>("scores");
savedScores.set(combined);

The entries argument must be written inline; an object variable is not a constructor. Empty list() and dictionary() calls need explicit type arguments, and mixed value types need an explicit value type such as dictionary<AnyValueInput>({ ... }).

size, get, has, with, without, merged, keys, and values are the supported dictionary operations. Updates are functional: they return a new dictionary and do not mutate the receiver. keys() and values() return runtime List values. Dictionaries can contain other dictionaries or lists and can be nested inside lists, and plot- and player-scoped variable factories can store them.

Dictionary iteration yields readonly [key, value] pairs:

for (const [team, score] of scores) {
    // team is string and score is number
}

const grouped = dictionary({ rounds: list(scores, changed) });

Lists and dictionaries can also be made explicit line variables:

const values = line.list(list(1, 2, 3));
const scores = line.dictionary(dictionary({ red: 10, blue: 20 }));

get returns a [value, found] pair that must be destructured in a declaration. A missing key sets found to false and writes a typed empty value: an empty list or dictionary, 0, "", false, an empty component, a zeroed location, or an air item. Dictionaries of sounds have no empty value and reject get. Runtime dictionary order follows DiamondFire and should not be treated as TypeScript object property ordering.

Locations

import { events, line } from "nocuft";

export const launch = events.player.join((event) => {
    const destination = line.location(event.player.location());
    destination.shift(0, 10, 0);
    destination.face(event.player.location(), "away");
    event.player.teleport(destination);
});

Mutable line locations support shift, shiftDirection, shiftAxis, shiftToward, setCoordinate, and face.

Items

import { item, line, players } from "nocuft";

let sword = line.item(item("diamond_sword"));
sword = sword
    .withCount(2)
    .withName("Sword of Doom")
    .withEnchantment("minecraft:sharpness", 5)
    .withLoreAppended("Forged for the arena");

players.all().giveItems([sword]);

Item transformations are functional and must be assigned or consumed. Available operations include material, count, name, enchantment, and lore changes.

Captured catalog items are generated as ordinary typed item values:

import { players } from "nocuft";
import { items } from "./nocuft/items.js";

players.all().giveItems([items["sword-of-doom"]]);

Their complete SNBT remains vendored in the project and is preserved through High IR, Low IR, and native template emission.

Processes

import { control, players, process } from "nocuft";

export const countdown = process.create((message: string, delay: number) => {
    control.waitWith({ timeUnit: "seconds" }, delay);
    players.all().sendMessage(message);
});

export function start(): void {
    countdown.startWith(
        { targetMode: "withNoTargets", localVariables: "dontCopy" },
        "Ready",
        2,
    );
}

Processes currently support up to 25 fixed value parameters. Anonymous process callbacks and delayed callback scheduling are not implemented.

Arithmetic And Control Flow

Initialized let declarations of type number, string, or boolean become line variables. Numeric arithmetic, assignment, comparisons, boolean logic, if/else, canonical for loops, while, do while, break, continue, and value-less return are supported.

import { control, players, process } from "nocuft";

export const timer = process.create((seconds: number) => {
    for (let remaining = seconds; remaining >= 0; remaining -= 1) {
        players.all().sendMessage(`${remaining} seconds remaining`);
        control.waitWith({ timeUnit: "seconds" }, 1);
    }
});

Player State And Selections

import { events, players } from "nocuft";

const queued = players.var.game.boolean("queued");
const wins = players.var.saved.number("wins");

export const join = events.player.join((event) => {
    queued.set(event.player, true);
    wins.set(event.player, wins.get(event.player) + 1);
});

Selections can be filtered, randomized, counted, and narrowed to one player. Assigning a selection to a local const creates an eager UUID snapshot so later operations reuse the same membership rather than rerunning the query.

const bearer = players.all().where(queued, true).one();
bearer.sendMessage("You have been selected");
players.all().sendMessage(`${bearer.name()} has the sword`);

Selection ordering is not guaranteed. An empty one() selection performs no targeted actions and uses DiamondFire's empty-selection result for values.

CLI

See the full CLI documentation for project tracking, live builds, vendored packages, and captured item catalogs.

License

MIT