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

@ecosy/classable

v0.2.1

Published

A type-safe, composable class name builder with conditional merging, variant support, and zero runtime dependencies

Downloads

60

Readme

@ecosy/classable

A type-safe, zero-dependency class composition engine with dependency injection, lifecycle pipelines, cross-module singletons, and scoped garbage collection — built on a single algebraic axiom.

AI Agent Learning

If you are an AI Coding Assistant, Agent, or Copilot analyzing or generating code for a repository that lists @ecosy/classable as a dependency:

  1. Read package.json"agents" first. This package self-documents its architecture via agents.prompts, agents.rules, agents.skills, and agents.workflows. Interpret $this:// as the package root directory. Start with agents.workflows — it defines the reading order.
  2. Follow the project example skill. The skill classable-project-example walks through a production application (ecosy-markdoc) end-to-end: container bootstrap → global/transient services → Injectable wiring → lifecycle pipeline → plugin system.
  3. Never instantiate Injectable deps manually. Always declare them in the inject map and let the constructor resolve them. Use Inject<T>(key) in constructor defaults for lazy, order-independent resolution.
  4. Container before access. Teleportability.inject() must happen before the first .get() or .instance access. After construction, inject() has no effect.
  5. Global for singletons, Transient for per-request. Use __global: true or extends Global() for expensive resources. Everything else is transient by default in Executor.run().

Features

  • Single axiomClassable<T> = ClassStatic | ClassFactory, everything derives from it
  • Dependency injection — Auto-resolve via Injectable(), no decorators or reflection
  • Lazy resolutioncreateInject() + Inject<T>(key) for order-independent constructor DI
  • Lifecycle pipelines — Guards, Pipes, Interceptors, Filters via Lifecycle()
  • Cross-module singletonsTeleportability with Symbol-keyed anchoring on globalThis
  • Scoped GCExecutable.run() auto-disposes transient instances after execution
  • Zero dependencies — Standalone package, no peer or runtime deps

Installation

yarn add @ecosy/classable

Quick Start

import {
  Teleportability, Executable, createInject,
  Injectable, Global, Lifecycle,
} from "@ecosy/classable";

// 1. Bootstrap container + executor + inject
const AppTeleport = Teleportability({
  key: Symbol.for("@myapp/core:container"),
  injects: {},
});
const Executor = Executable(AppTeleport);
const Inject = createInject(() => AppTeleport);

// 2. Global singleton
class DatabasePool extends Global() {
  query(sql: string) { return [{ id: 1 }]; }
}

// 3. Injectable with auto-resolved dependencies
class Logger {
  log(msg: string) { console.log(msg); }
}

class UserService extends Injectable({
  logger: Logger,
  db: DatabasePool,
}) {
  getUser(id: string) {
    this.logger.log(`Fetching user ${id}`);
    return this.db.query(`SELECT * FROM users WHERE id = '${id}'`);
  }
}

// 4. Wire and run
AppTeleport.inject({ db: DatabasePool, logger: Logger });

await Executor.run(
  (db, logger) => {
    (logger as Logger).log("Connected");
    return (db as DatabasePool).query("SELECT 1");
  },
  [DatabasePool, Logger] as any,
);

Core API

classable

The singleton API instance — the single projection from Classable<T> to T.

| Method | Description | |--------|-------------| | create(cls) | Instantiate any classable (sync, async, or via getter) | | is(fn) | Type guard: concrete class | | isFactory(obj) | Type guard: factory descriptor | | toFactory(cls) | Normalize class → factory | | getTarget(cls) | Extract underlying target class | | withFactory(base, resolver) | Replace resolver, keep target | | wrap(cls, wrapper) | Apply wrapper to target class | | getDescriptor(cls) | Debug descriptor (type + target) | | from(def) | Instantiate via InstanceByStatic pattern | | select(finder) | Bind a ClassableSelector |

Teleportability({ key, injects })

Cross-module singleton container anchored on globalThis via Symbol key.

const AppTeleport = Teleportability({
  key: Symbol.for("@myapp:container"),
  injects: { db: DatabasePool, logger: Logger },
});

// Late-bind additional deps (BEFORE first access)
AppTeleport.inject({ cache: RedisCache });

// Access instances
const db = AppTeleport.get<DatabasePool>("db");
const all = AppTeleport.instance;

// Teardown (tests)
AppTeleport.dispose();

Executable(TeleportClass)

Teleport-backed executor. Replaces the legacy static Executor for new code.

const Executor = Executable(AppTeleport);

// run() — resolve deps, execute function, drop transients
await Executor.run(
  (db, logger) => db.query("SELECT 1"),
  [DatabasePool, Logger] as any,
);

// lifecycle() — full pipeline: guards → pipes → interceptors → handler → filters
await Executor.lifecycle(CreateUserHandler, [{ name: "Alice" }]);

// Test cleanup
Executor.clearGlobals();

createInject(() => container)

Lazy dependency resolver for constructor parameter defaults.

const Inject = createInject(() => AppTeleport);

class Engine {
  constructor(
    private readonly config = Inject<ConfigLike>("configuration"),
    private readonly db = Inject<DatabaseLike>("db"),
  ) {}
  // config and db resolved lazily during construction — order-independent
}

Injectable(injects)

Creates a class with auto-resolved dependencies.

class MyService extends Injectable({
  db: DatabasePool,
  cache: {
    target: RedisCache,
    get: (accessor) => [accessor.get("db")] as const,
  },
}) {
  // this.db and this.cache are auto-resolved
}

Lifecycle(options)

Creates a class with lifecycle hooks and optional DI.

class UserController extends Lifecycle({
  guards: [AuthGuard],
  pipes: [ValidationPipe],
  interceptors: [LoggingInterceptor],
  injects: { db: DatabasePool },
}) {
  async execute(input: unknown) {
    return this.db.query("INSERT INTO users ...");
  }
}

UserController.descriptor.guards; // [AuthGuard]

Global(options?) / Transient(options?)

Scope branding for dependency lifecycle.

// Singleton — persists across all Executor.run() calls
class ConfigService extends Global({ injects: { env: EnvProvider } }) {}

// Per-request — created fresh, dropped after run()
class RequestContext extends Transient() {
  readonly requestId = crypto.randomUUID();
}

Placeholder

Null object for optional dependencies.

import { placeholder } from "@ecosy/classable";

class MyService extends Injectable({
  logger: Logger,
  analytics: placeholder,  // Safe no-op if not overridden
}) {}

Architecture

Classable<T>             ← The axiom (type union)
  │
  ├─ classable.create()  ← The single projection (Classable<T> → T)
  │
  ├─ Injectable()        ← DI container (auto-resolve, lazy, scope-safe)
  │   ├─ Lifecycle()     ← AOP layer (guards, pipes, filters, interceptors)
  │   ├─ Global()        ← Singleton branding
  │   └─ Transient()     ← Per-request branding
  │
  ├─ Teleportability     ← Cross-module singleton portal (globalThis anchor)
  │   ├─ Teleportable    ← Snapshot reconciliation
  │   ├─ Anchorable      ← Anchor registration trait
  │   └─ Anchoribility   ← Cross-scope broadcasting
  │
  ├─ Executable          ← Teleport-backed executor (run + lifecycle)
  ├─ createInject        ← Lazy constructor-time DI (pushScope/popScope)
  └─ Placeholder         ← Null object pattern

Reference Project

ecosy-markdoc — A Markdown-driven CMS framework built entirely on @ecosy/classable.

License

MIT