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

@open-core/framework

v1.0.8

Published

Secure, event-driven TypeScript Framework & Runtime engine for CitizenFX (Cfx).

Readme

CI npm license typescript cli website

OpenCore Framework - Stable v1

OpenCore is a TypeScript multiplayer runtime framework targeting CitizenFX runtimes (Cfx/RageMP) via adapters.

It is not a gamemode or RP framework. It provides:

  • A stable execution model (server and client)
  • Dependency Injection and metadata-driven wiring
  • An event/command system
  • Security primitives (validation, access control, rate limiting)

License: MPL-2.0

Discord Community | Docs | OpenCore CLI

Installation

pnpm add @open-core/framework reflect-metadata tsyringe zod uuid

This framework uses TypeScript decorators. Ensure your project has decorators enabled.

Imports and entry points

The package exposes subpath entry points:

  • @open-core/framework (root)
  • @open-core/framework/server
  • @open-core/framework/client

Architecture

OpenCore follows a Ports & Adapters (Hexagonal) architecture.

  • Kernel (src/kernel): engine-agnostic infrastructure (DI, logger, metadata scanning)
  • Runtime (src/runtime): multiplayer execution model (controllers, processors, security, lifecycle)
  • Adapters (src/adapters): platform integration (Cfx, Node testing)

The runtime never auto-detects the platform. Adapters are selected explicitly at bootstrap time.

Cfx game profiles

OpenCore treats CitizenFX (cfx) as the platform and supports game profiles (gta5 and rdr3).

  • Shared runtime APIs (events, exports, transport, DI) are registered through the Cfx adapter.
  • Game-specific behavior is controlled through platform capabilities/config (gameProfile, defaultSpawnModel, etc.).
  • Optional RedM-specific enhancements can be layered as external libraries without changing core runtime contracts.

Operating modes

Each instance runs in exactly one mode configured via Server.init():

  • CORE: authoritative runtime. Typically provides identity/auth/players via exports.
  • RESOURCE: a normal Cfx resource using CORE as provider for some features.
  • STANDALONE: a self-contained runtime (useful for tooling, simulations, or small servers).

Server bootstrap

Initialize the server runtime:

import { Server } from '@open-core/framework/server'

await Server.init({
  mode: 'CORE'
})

Some features require providers (depending on your mode and configuration). Configure them before calling init():

import { Server } from '@open-core/framework/server'

Server.setPrincipalProvider(MyPrincipalProvider)
Server.setSecurityHandler(MySecurityHandler)
Server.setPersistenceProvider(MyPlayerPersistence)
Server.setNetEventSecurityObserver(MyNetEventSecurityObserver)

Controllers and decorators

OpenCore uses a decorator + processor pattern.

Decorators store metadata with Reflect.defineMetadata(). During bootstrap, the MetadataScanner reads metadata and processors register handlers.

Commands

import { Controller, Command, Guard, Throttle, Player } from '@open-core/framework/server'
import { z } from 'zod'

const TransferSchema = z.tuple([z.coerce.number().int().positive(), z.coerce.number().min(1)])

@Controller()
export class BankController {
  @Command({
    command: 'transfer',
    usage: '/transfer <id> <amount>',
    schema: TransferSchema,
  })
  @Guard({ rank: 1 })
  @Throttle(1, 2000)
  async transfer(player: Player, args: z.infer<typeof TransferSchema>) {
    const [targetId, amount] = args
    player.emit('chat:message', `transfer -> ${targetId} (${amount})`)
  }
}

Network events

@OnNet() handlers always receive Player as the first parameter.

import { Controller, OnNet, Player } from '@open-core/framework/server'
import { z } from 'zod'

const PayloadSchema = z.object({ action: z.string(), amount: z.number().int().positive() })

@Controller()
export class ExampleNetController {
  @OnNet('bank:action', { schema: PayloadSchema })
  async onBankAction(player: Player, payload: z.infer<typeof PayloadSchema>) {
    player.emit('chat:message', `action=${payload.action} amount=${payload.amount}`)
  }
}

Security decorators

  • @Guard({ rank }) or @Guard({ permission })
  • @Throttle(limit, windowMs)
  • @RequiresState({ missing: [...] })

Library events

Use library wrappers to emit domain events and @OnLibraryEvent() to observe them.

@OnLibraryEvent() listens to events emitted through library.emit(...) only. It does not listen to emitExternal, emitNetExternal, or emitServer.

import { Server } from '@open-core/framework/server'

const characters = Server.createServerLibrary('characters')

@Controller()
export class CharacterListeners {
  @OnLibraryEvent('characters', 'session:created')
  onSessionCreated(payload: { sessionId: string; playerId: number }) {
    // optional listener for library domain events
  }
}

characters.emit('session:created', { sessionId: 's-1', playerId: 10 })

Client usage follows the same pattern with Client.createClientLibrary(...) and @Client.OnLibraryEvent(...).

Plugins

Plugin contracts are exposed by runtime entrypoint, not by root:

  • Server plugins: @open-core/framework/server
  • Client plugins: @open-core/framework/client
import { Server, type OpenCorePlugin } from '@open-core/framework/server'
import { Client, type OpenCoreClientPlugin } from '@open-core/framework/client'

const serverPlugin: OpenCorePlugin = {
  name: 'server-example',
  install(ctx) {
    ctx.server.registerApiExtension('ExampleServerDecorator', () => {})
  },
}

const clientPlugin: OpenCoreClientPlugin = {
  name: 'client-example',
  install(ctx) {
    ctx.client.registerApiExtension('ExampleClientDecorator', () => {})
  },
}

await Server.init({ mode: 'CORE', plugins: [serverPlugin] })
await Client.init({ mode: 'CORE', plugins: [clientPlugin] })

Module augmentation for plugin APIs:

declare module '@open-core/framework/server' {
  interface ServerPluginApi {
    ExampleServerDecorator: () => void
  }
}

declare module '@open-core/framework/client' {
  interface ClientPluginApi {
    ExampleClientDecorator: () => void
  }
}

Testing

Tests run with Vitest.

pnpm test
pnpm test:unit
pnpm test:integration
pnpm test:coverage

Note: pnpm test does not run benchmarks.

Benchmarks

Benchmarks are split by value, so the default run focuses on framework features that matter for real servers.

pnpm bench
pnpm bench:value
pnpm bench:gold
pnpm bench:startup
pnpm bench:diagnostic
pnpm bench:soak
pnpm bench:load
pnpm bench:all
  • bench / bench:value: value-focused suite. Commands, net events, RPC, lifecycle, ticks, binary path, bootstrap.
  • bench:gold: hot-path load scenarios only.
  • bench:startup: startup and registration cost.
  • bench:diagnostic: internal and low-level synthetic benchmarks.
  • bench:soak: long-running stress scenario.

Snapshot (latest local run)

Use benchmark/reports/ as the source of truth. Results vary by machine and should be compared relatively, not treated as product guarantees.

  • Primary benchmark targets:
    • full command execution
    • full net event handling
    • RPC processing
    • player lifecycle churn
    • tick budget impact
    • bootstrap cost
    • binary transport cost

Full reports and methodology are available in benchmark/README.md.

Reports

Benchmark reports are generated under benchmark/reports/.

  • pnpm bench:all generates aggregated reports (text/json/html)
  • Load metrics used by load benchmarks are persisted in benchmark/reports/.load-metrics.json

For details about the benchmark system, see benchmark/README.md.

Development scripts

pnpm build
pnpm watch
pnpm lint
pnpm lint:fix
pnpm format

License

MPL-2.0. See LICENSE.