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

fury-scheme

v1.0.4

Published

An alternative to OOP without compromise.

Readme

Fury-Scheme

An alternative to OOP without compromise.

Flexible: Fury-Scheme offers far more flexibility than traditional OOP solutions in terms of encapsulation, polymorphism, project structure, composition of complex objects, etc.

Efficient: Fury-Scheme removes a lot of the inefficiencies caused by OOP such as large call stacks due to excessive implementation chunking for the sake of encapsulation, unnecessary references for the sake of encapsulation, unused and unnecessary polymorphism for the sake of fulfilling the logical contracts of what an object actually implements, etc.

Explicit: Fury-Scheme encourages explicit declaration of all variants to allow for developers to get a more holistic view of the project and grants far better compile time knowledge of the state structure.

NOTE: Fury-Scheme is still in development. The docs are just rough drafts and the API is incomplete.

Setting Up

Fury-Scheme was written in TypeScript and is almost entirely compile time. As such, using Fury-Scheme as a normal JavaScript module isn't very useful. If you aren't already using TypeScript, please do. I recommend using Parcel if you're developing a web app and ts-node, if you're developing a node app.

One that is done, you can install Fury-Scheme: npm i --save fury-scheme.

Usage

Projects are split into three parts:

  1. Scheme, which defines the structure of the global state object.
  2. Modules, which define functions used to interact with the state. They can also contain constants for the configuration of polymorphic attributes.
  3. Entries, which define the initial state and startup procedure of the different run environments of the application.

Declaring a State Scheme

1. The basics: Scheme declarations are exclusively compile-time and composed of TypeScript type declaration statements (type =). Type declarations define what properties a given state instance will have:

import { WrapScheme } from "fury-scheme";
export type Scheme_MyFirstObject = WrapScheme<typeof MODULES, [], [], {
	my_first_property: number,
    the_sequel: string,
    our_property: {
        foo: HTMLButtonElement,
        bar: () => void,
        baz: boolean[]
    }
}>;

Notice the use of WrapScheme. WrapScheme is the recommended way to create valid scheme declarations succinctly with local error checking. The first argument tells the scheme declaration what modules in the package are available to it. The 2nd and 3rd are extra metadata used exclusively during compile-time. They are covered in [Linking Scheme and Modules](#Linking Scheme and Modules).

2. Creating References to Other Scheme Instances: Creating references to other scheme instances is a bit different than creating one to any other object. This is because scheme declarations contain a lot of metadata that isn't part of an actual instance and as such, a wrapper was needed. In order to reference another scheme instance, we use the class SchemeInstance<Scheme_XYZ>:

import { WrapScheme, SchemeInstance } from "fury-scheme";
export type Scheme_Object1 = WrapScheme<typeof MODULES, [], [], {
    reference_1: SchemeInstance<Scheme_Object2>,
    reference_2: Record<keyof any, SchemeInstance<Scheme_Object1>>
};

export type Scheme_Object2 = WrapScheme<typeof MODULES, [], [], {
	reference_3: SchemeInstance<Scheme_Object1>[]
}>;

Creating Multiple Variants Using Generics

3. Simple generic conditions: Scheme declarations can accept generic parameters, not only to specify generic type information but also to configure the scheme in a more limited and explicit manner using TypeScript ternaries and type literals:

import { WrapScheme } from "fury-scheme";
type RuntimeType = "server" | "client";

export type Scheme_Player<Runtime extends RuntimeType> = WrapScheme<typeof MODULES, [], [], { // Shared state
    name: string,
    position: [number, number]
}> & (Runtime extends "client" ? WrapScheme<typeof MODULES, [], [], {
    animation_state: { /* ... */ },
    color: string
}> : {}) & (Runtime extends "server" ? WrapScheme<typeof MODULES, [], [], {
    /* ... */
}> : {});

export type Scheme_Bomb<IsDud extends boolean> = WrapScheme<typeof MODULES, [], [], {
   	is_dud: IsDud,
    position: [number, number],
    explosion_timer: number
}> & (IsDud extends false ? WrapScheme<typeof MODULES, [], [], {
    explosion_radius: number
}> : {});

We can use {} as the fallback when nothing gets added to the scheme because, under the hood, WrapScheme converts the generic parameters into an object. If we were to type, WrapScheme<typeof MODULES, [], [], {}> instead of {}, no properties would be added and any added metadata would have no effect on the behavior of the scheme. Also note, while RuntimeType is a union of string literals, other types of literals can be used, most notably Symbols and numbers. Symbols become a very helpful optimization tool when the same enum values are stored during runtime, taking up less memory and being faster to compare. TypeScript enums work as well however they add a bit of extra runtime data that might not be particularly useful, especially if the enum is used exclusively at compile time.

4. Multiple truthy ternary values: Ternaries can treat multiple input values as truthy using unions:

// TODO: Replace "..." with example properties 
import { WrapScheme } from "fury-scheme";

type RuntimeModes = "server-dedicated" | "client-host" | "client-puppet" | "editor";
type RuntimeModes_Client = "client-host" | "client-puppet";
type RuntimeModes_Server = "server-dedicated" | "client-host";

export type Scheme_Player<Runtime extends RuntimeType> = WrapScheme<typeof MODULES, [], [], {
    /* ... */
}> & (Runtime extends RuntimeModes_Client ? WrapScheme<typeof MODULES, [], [], {
    /* ... */
}> : {}) & (Runtime extends RuntimeModes_Client | "editor" ? WrapScheme<typeof MODULES, [], [], {
    /* ... */
}> : {}) & (Runtime extends "editor" ? WrapScheme<typeof MODULES, [], [], {
    /* ... */
}> : {});

5. Multiple condition ternaries: Ternaries can check for multiple conditions using tuple inheritance checks:

// TODO: Replace "..." with example properties
import { WrapScheme } from "fury-scheme";

type RuntimeModes = "server-dedicated" | "client-host" | "client-puppet" | "editor";
type RuntimeModes_Client = "client-host" | "client-puppet";
type RuntimeModes_Server = "server-dedicated" | "client-host";

export type Scheme_Bomb<Runtime extends RuntimeModes, IsDud extends boolean> = WrapScheme<typeof MODULES, [], [], {
    /* ... */
}> & ([Runtime, IsDud] extends [RuntimeModes_Client | "editor", true] ? WrapScheme<typeof MODULES, [], [], {
    /* ... */
}> : {}) & ([Runtime, IsDud] extends ["editor", false] ? WrapScheme<typeof MODULES, [], [], {
    /* ... */
}> : {});

TODO: Ternary switch statements.

7. Generic context types: In order to simplify generic signatures, compile-time exclusive context types can be used:

import { WrapScheme, SchemeInstance } from "fury-scheme";
type RuntimeModes = "server-dedicated" | "client-host" | "client-puppet" | "editor";
type RuntimeModes_Client = "client-host" | "client-puppet";
type RuntimeModes_Server = "server-dedicated" | "client-host";
type GameMode = "classic" | "deathmatch";

type GameConfiguration = { runtime: RuntimeModes } & (
	{ game_mode: "classic" }
	| { game_mode: "deathmatch", weapon_roster: "classic" | "expanded" }
);

export type Scheme_World<Ctx extends GameConfiguration> = WrapScheme<typeof MODULES, [], [], {
    config: { game_max_time: number } & {
        classic: {
            mode: "classic",
            round_count: number
        },
        deathmatch: {
            mode: "deathmatch",
            kill_goal: number
        }
    }[GameConfiguration["game_mode"]]
}>;

export type Scheme_Player<Ctx extends GameConfiguration, SomethingElse extends boolean> = WrapScheme<typeof MODULES, [], [], {
    name: string,
    health: number
}> & (Ctx["game_mode"] extends "classic" ? WrapScheme<typeof MODULES, [], [], {
    team: "blue" | "red"
}> : {}) & (Ctx["runtime"] extends RuntimeModes_Server ? WrapScheme<typeof MODULES, [], [], {
    anticheat_warnings: SchemeInstance<Scheme_AnticheatWarning<Ctx, "some argument">>[]
}> : {}) & ([Ctx, SomethingElse] extends [{ runtime: RuntimeModes_Server, game_mode: "deathmatch", weapon_roster: any}, false] ? WrapScheme<typeof MODULES, [], [], {
    damage_dealers: SchemeInstance<Scheme_Player<Ctx, SomethingElse>>[]
}> : {});

8. Context type variants: Sometimes, it is necessary to limit an entire scheme instance to only specific context type variants. Most of the techniques used to create multiple variants of schemes are applicable here too:

import { WrapScheme, UnionIntersection } from "fury-scheme";
type RuntimeModes = "server-dedicated" | "client-host" | "client-puppet" | "editor";
type RuntimeModes_Client = "client-host" | "client-puppet";
type RuntimeModes_Server = "server-dedicated" | "client-host";
type GameMode = "classic" | "deathmatch";

type GameConfiguration<ApplicableRuntimes = RuntimeModes> = (
		{ runtime: UnionIntersection<RuntimeModes_Server, ApplicableRuntimes>, anticheat_allows_reporting: boolean }
		| { runtime: UnionIntersection<RuntimeModes_Client, ApplicableRuntimes> }
		| { runtime: "editor", not_readonly: boolean }
	) & (
	{ game_mode: "classic" } |
	{ game_mode: "deathmatch", weapon_roster: "classic" | "expanded" }
);

export type Scheme_AnticheatWarning<Ctx extends GameConfiguration<RuntimeModes_Server>> = WrapScheme<typeof MODULES, [], [], {
    case_id: string,
    cheat_id: "no-fall" | "blink" | "timer" | "fly" | "kill-aura"
}> & (Ctx["anticheat_allows_reporting"] extends true ? WrapScheme<typeof MODULES, [], [], {
    reporter_uuid: string,
    report_reason: string,
    report_time: number
}> : {});

9. Using Boolean Algebra Helpers: Fury-Scheme comes a set of helpers which allow for more succinct variant composition through conditions:

// TODO: Show off "ConditionallyCompose" and related.

Combining Types

9. Flat type composition: Fury-Scheme allows "multiple inheritance" through TypeScript type unions. This works well in Fury-Scheme because polymorphic method implementations aren't linked to the scheme but rather to an explicit polymorphic behavior configuration object passed at each polymorphic method invocation. As such, the ambiguities of polymorphism with multiple inheritance disappear. See more at Accepting Multiple Different Absolute Types.

import { WrapScheme, SchemeInstance } from "fury-scheme";
type RuntimeModes = "server-dedicated" | "client-host" | "client-puppet" | "editor";
type RuntimeModes_Client = "client-host" | "client-puppet";
type RuntimeModes_Server = "server-dedicated" | "client-host";

export type Scheme_Particles<Runtime extends RuntimeModes> = Runtime extends RuntimeModes_Client ? WrapScheme<typeof MODULES, [], [], {
    particles: SchemeInstance<SchemeH_Particles<Runtime>>
}> : {};

export type Scheme_Explosive<Runtime extends RuntimeModes> = WrapScheme<typeof MODULES, [], [], {
    ticks_left: number,
    is_active: boolean,
    explosion_seed: number
}> & Scheme_Particles<Runtime>;

export type Scheme_ProjectileBase<Runtime extends RuntimeModes> = WrapScheme<typeof MODULES, [], [], {
    direction: [number, number],
    air_time: number
}>  & Scheme_Particles<Runtime>;

export type Scheme_ProjectileNormal<Runtime extends RuntimeModes> = Scheme_ProjectileBase<Runtime> & WrapScheme<typeof MODULES, [], [], {
    draw_damage_multiplier: number
}>;

export type Scheme_ProjectileExplosive<Runtime extends RuntimeModes> = Scheme_ProjectileBase<Runtime> & Scheme_Explosive<Runtime>;

export type Scheme_Bomb<Runtime extends RuntimeModes> = Scheme_Explosive<Runtime> & WrapScheme<typeof MODULES, [], [], {
    detonator: Scheme_Instance<Scheme_Player<Runtime>>
}>;

TODO: Required siblings

TODO: Using symbol identified properties to avoid property name conflicts

Differentiating Types

TODO: Literal identified type unions

Implementing Modules

TODO: Declaring modules

TODO: Constructing scheme instances

Accepting Scheme Instances

TODO: Absolute types

TODO: Base types

TODO: Using generics to enforce type consistency

Accepting Multiple Different Absolute Types

TODO: Naïve if statements

TODO: Polymorphic configuration records

Dealing with static data

TODO: As an argument

TODO: Polymorphic

Working with Abstraction

TODO: Passing polymorphic configuration objects

TODO: Adding typed handlers

TODO: Required minimum types based on a configuration

Linking Scheme and Modules

TODO: Relevant modules

TODO: Encapsulation

Best Practices

TODO

API Reference

TODO

Examples

TODO

Contributing

TODO

License

TODO