Repository navigation
Releases
v1.7.0
OpenCore CLI v1.7.0
Typed library events
Typegen now types the events of OpenCore libraries, in the new serverLibraryEvents and
clientLibraryEvents maps of each resource's .opencore/opencore.gen.ts, keyed by library name:
@Server.OnLibraryEvent(library, event)/@Client.OnLibraryEvent(library, event)listeners type
an event by their first parameter. An optional (payload?: T), defaulted or rest parameter keeps
its declared type.- When several listeners take a payload, the event is typed by the intersection of their
parameters, since the payload reaches each of them. Listeners that disagree makeemit()fail to
compile instead of one of them being dropped silently. - A listener that ignores the payload says nothing about it: the event is typed by its
emit()
calls, or asunknownwhen there are none. library.emit(event, payload)calls on a library created from a literal name type the events no
listener types, with the union of the payloads they pass. A payload forwarded from a method
parameter is typed through that parameter when the class is exported, and from the argument
otherwise.
Library and event names must resolve to string literals; a plain string is skipped with a
warning. Typed library events require @open-core/framework with library event support in its
Register.
Library name collisions
The framework merges the events of every library sharing a name across the project, although each
resource has its own library bus at runtime. Typegen now warns, at the createServerLibrary /
createClientLibrary call, when another resource creates a library of the same side and name:
server library "core" is also created by resource:resources/characters; typegen merges the events
of libraries sharing a name, so give one of them a different name
The factory is found through its symbol, so aliased imports, re-exports and
Server.createServerLibrary count. Listening to or emitting on a library does not, and libraries
created from a non-literal name are not checked.
opencore build now generates the types of every resource before bundling, with or without
build.typecheck, so both resources of a collision report it on the first build. In opencore dev
a resource reports its warnings once per session.
Typegen fixes
@Client.OnViewhandlers with an optional, defaulted or rest first parameter no longer type their
WebView message asundefined.- A
WebView.send()that forwards a method parameter of a class that is not exported is typed from
its argument, instead of importing the class and breaking the generated file. - The
__Payloadhelper is only declared when a map uses it, so projects compiled with
noUnusedLocalsno longer fail on it. - The typegen summary reads
typegen: N warning(s) in <resource>: it also countssend()and
emit()calls, which are not handlers.
v1.6.2
OpenCore CLI v1.6.2
Typegen on large resources
Resources whose analysis is larger than 64 KB (a few hundred source files) no longer skip type
generation with typegen analyzer returned invalid JSON: unexpected end of JSON input. The analyzer
exited before Node had flushed its output to the CLI, so only the first 64 KB arrived and that
resource's .opencore/opencore.gen.ts got no events, RPCs or commands.
v1.6.1
OpenCore CLI v1.6.1
Typegen reads the TypeScript AST
Typegen now walks each resource with the project's own TypeScript (5.x or 7.x) instead of matching
source text, so every listener the runtime registers is typed:
- Decorators are matched by the symbol they resolve to: named (
import { OnRPC }), aliased
(import { OnRPC as Rpc }), namespace and barrel re-exports of@open-core/framework/serverand
/clientall count, not only@Server.*/@Client.*. A local decorator that happens to be
calledOnNetdoes not. - Names come from the argument's literal type:
BankEvents.deposit(as const), a local constant or a
framework constant such asSYSTEM_EVENTS.chat.message(requires@open-core/frameworkwith literal
SYSTEM_EVENTStypes). A warning is reported only when the name's type is plainstring. - The decorated method is taken from the AST: multi-line decorator arguments, stacked decorators and
method names such asdo,deleteornewno longer skip a handler. WebView.send()payloads are typed from the checker whatever the expression (object literals,
locals, class fields). Exported types are imported, others are expanded. A typed forwarder,
send<K extends keyof M>(name: K, data: M[K]), registers one message per key ofM, and its call
sites are no longer read as sends of their own.- Generated maps key constants by their value (
'bank:withdraw') instead of an__Entry<typeof import(…)>
reference. Literal names produce the same output as before.
Typegen requires typescript in the project, like build.typecheck.
Source validation
The mixed-decorator check only counts real decorators: a file whose comments, strings or type-only
imports mention @Client.* and @Server.* no longer cancels the build.
v1.6.0
OpenCore CLI v1.6.0
Requires @open-core/framework v1.2.0 or later for the generated types to take effect.
Type generation for net events, RPCs, commands and WebView messages
opencore build and opencore dev now scan every resource and emit a .opencore/opencore.gen.ts that
registers its handlers with the framework's type registry. Emitting sites then autocomplete to the
declared names and check their payloads against the handler signatures:
| Source | Types |
|---|---|
@Server.OnNet |
events.emit() on the client (the Player parameter is dropped) |
@Client.OnNet |
player.emit() and events.emit() on the server |
@Server.OnRPC / @Client.OnRPC |
rpc.call() and rpc.notify() arguments, and the call() result |
@Server.Command |
the command map, with description and usage |
@Client.OnView / WebView.send() |
messages in both directions between the client and its WebViews |
Names can be string literals, file-local constants or imported constants such as BankEvents.WITHDRAW.
Every resource registers under its own key and the framework merges them, so a name declared in one
resource is known to all of them.
A resource with a WebView directory (ui/, nui/ or the configured one) also gets a
<view>/.opencore/opencore.gen.ts exporting UiCanSend and UiReceives for the UI code. A send()
payload is typed when it forwards a parameter of the enclosing method; any other expression is typed
unknown and reported as a warning.
Generated files are rewritten only when their content changes, and opencore dev regenerates them on
every rebuild.
Strict mode on by default
With build.typegen.strict (default true), an event, RPC or WebView message name that no handler
declares is a compile error. The framework's own opencore:* events are always accepted. Set
strict: false when a project emits names handled outside OpenCore, such as events of a Lua resource:
unknown names are then accepted while known ones keep their autocomplete and payload checks.
Existing projects get these errors in the editor and in tsc after upgrading. They only fail the
build when build.typecheck is enabled.
build.typecheck
Bundling only strips types, so until now no type error could fail a build. With build.typecheck: true,
opencore build regenerates the types and runs the project's own tsc --noEmit before bundling, and
fails without writing any output. opencore dev reports the errors as warnings and keeps watching.
It is off by default so upgrading never breaks an existing build, and requires typescript installed
in the project.
Configuration
build: {
typecheck: true,
typegen: {
enabled: true, // default: true. When false, generated files are deleted and every signature is loose again
strict: true, // default: true
},
}Templates and tooling
- New projects enable
typecheckandtypegeninopencore.config.ts. - The starter
tsconfig.jsonincludes the.opencoredirectories so the generated types are part of the program. opencore create resource --with-nuiadds aui/tsconfig.jsonthat includes the view's generated types.opencore doctorreports the type generation mode, and warns when strict mode is on withoutbuild.typecheck.
v2.0.0-beta.1
OpenCore CLI v1.5.1
Fix for FiveM Enhanced msgpack codec
Fixed server resources failing on every event and cross-resource export under FiveM Enhanced (Node 26) with:
this.codec.encode is not a function
this.codec.decode is not a function
Large CJS server bundles declare their modules at the script top level, which corrupts the runtime's global msgpack codec and breaks command registration and net events.
The server bundle is now wrapped in an IIFE so those declarations live in a function scope instead of the top level. This applies only to server builds using the default cjs format, client builds already use iife and are unaffected.
v1.5.1
OpenCore CLI v1.5.1
Fix for FiveM Enhanced msgpack codec
Fixed server resources failing on every event and cross-resource export under FiveM Enhanced (Node 26) with:
this.codec.encode is not a function
this.codec.decode is not a function
Large CJS server bundles declare their modules at the script top level, which corrupts the runtime's global msgpack codec and breaks command registration and net events.
The server bundle is now wrapped in an IIFE so those declarations live in a function scope instead of the top level. This applies only to server builds using the default cjs format, client builds already use iife and are unaffected.
v1.5.0
OpenCore CLI v1.5.0
Dependency Resolver
Added build.dependencyResolution for server resources that use build.server.external.
This is mainly for FiveM/RedM resources that need runtime npm packages such as pg, typeorm, database clients, or Prisma adapters while staying inside the FXServer Node.js 22 filesystem sandbox.
Modes
auto: uses OpenCore's runtime default. For FiveM/RedM this currently resolves toisolated.isolated: installs physical resource-local dependencies. This is the recommended production mode.symlink: legacy opt-in mode for linkingnode_modules; not recommended for FXServer sandboxed runtimes.shared-resource: experimental shared dependency resource.bundle: experimental bundling mode for pure JavaScript packages.
Isolated Installs
isolated mode now writes a minimal package.json into each built resource and installs only the external packages that resource actually uses.
The resolver refuses unsafe deploy specs such as latest, *, workspace:, file:, link:, portal:, and local paths.
Sandbox validation is enabled by default and rejects symlinks that resolve outside the generated resource.
pnpm Runtime Layout Fix
pnpm installs now use a hoisted physical layout for generated dependency installs:
pnpm install --prod --ignore-workspace --no-lockfile --reporter=append-only --config.node-linker=hoisted --package-import-method=copyThis fixes transitive runtime imports under FXServer, for example typeorm resolving tslib from the built resource instead of climbing to the workspace root and failing the sandbox check.
Dependency Cache
Generated dependency trees are cached under:
node_modules/.cache/opencore/dependencies/<hash>
Matching resources reuse the cached physical dependency tree when .opencore-deps.json matches the dependency set, package manager, install-script setting, pnpm linker mode, platform, architecture, and Node major version.
Native Package Checks
The resolver detects known native packages and native package indicators such as binding.gyp, prebuilds, .node files, and native package metadata.
Native packages are rejected in bundle mode and reported clearly for dependency-resolution flows.
Bug Fixes
Dev Mode UI Hot-Reload (#16): Fixed an issue where server.js and client.js would disappear from the resource output directory when editing UI files (.tsx, .jsx) in dev mode. The incremental build now preserves sibling artifacts when rebuilding only views, instead of wiping the entire resource directory before the partial rebuild.
Docs
Added dependency resolver documentation and FiveM sandbox guidance:
docs/cli/dependency-resolution.mddocs/adapters/fivem.md
Use isolated for production FiveM/RedM server resources that require runtime npm dependencies.
v1.4.1
OpenCore CLI v1.4.1
Fix — Dev mode build loop
Fixed an infinite rebuild loop that could happen when running:
opencore buildopencore dev
The watcher now ignores build-generated artifacts (including .opencore/autoload.*.controllers.ts) and output/deploy directories, preventing recursive self-triggered rebuilds.
Result: dev mode stabilizes after the initial build and only rebuilds on real source changes.
v1.4.0
OpenCore CLI v1.4.0
Feature — Environment System
Angular-style environment support without touching files on disk. esbuild resolves the correct file at build time via aliases.
CLI flags
opencore build --environment production(shorthand-e)opencore dev --environment development- Priority: flag >
OPENCORE_ENVIRONMENTenv var >build.environmentin config >development
Per-environment build overrides
build: {
environments: {
production: {
minify: true,
sourceMaps: false,
logLevel: 'ERROR',
fileReplacements: [
{ replace: './src/config/api.ts', with: './src/config/api.prod.ts' },
],
},
},
}Usage in resources
import { environment } from '@opencore/environment'
if (!environment.production) {
logger.debug('...')
}Project structure (generated by opencore init)
environments/
├── environment.model.ts # Define your AppEnvironment interface here
├── environment.development.ts
└── environment.production.ts
Validation — if environments/ exists but the requested environment file is missing, the build fails with the list of available environments.
v1.3.1
OpenCore CLI v1.3.1
Fix
- Fixed explicit resources build config issue
Improve
- Improved entrypoint init templates, reduced and more elegant using
loggersconst