How to Integrate Cordis with Other Tools: A Complete Guide to Service Wrapping and Plugin Architecture
Cordis integrates with external tools by wrapping them as injectable services that extend the Service base class, registering them on the shared Context, and loading dependent plugins through the Loader to enable hot-reload and runtime isolation.
Cordis is a meta-framework designed for composing, isolating, and hot-reloading runtime modules. When you integrate Cordis with other tools, you transform external JavaScript or TypeScript libraries into first-class plugins that benefit from dependency injection and state isolation. This guide explains how to bridge any npm package or local utility into the cordiverse/cordis ecosystem using the core APIs found in packages/core/src/context.ts and packages/core/src/service.ts.
Understanding Cordis Core Concepts for Integration
Before bridging external tools, you must understand the three primitives that mediate all integrations: the Context, Service, and Loader.
The Context Isolation Model
The Context class, defined in packages/core/src/context.ts, acts as the central object holding services, configuration, and isolation state. Every plugin receives a unique Context instance, ensuring that state conflicts are avoided through the internal isolation map (ctx[Symbol.isolate]). When you integrate an external tool, you register it on this context so other plugins can access it via ctx.<serviceName>.
Services as Injectable Wrappers
Services are reusable components that extend the Service base class from packages/core/src/service.ts. To integrate an external library, you create a wrapper class that instantiates the tool inside its constructor and exposes typed methods. Registration occurs via ctx.add('serviceName', new YourService(ctx)), which inserts the service into the context’s dependency graph.
Plugin Resolution and Loading
The Loader, implemented in packages/loader/src/index.ts, resolves and instantiates plugins from local files, npm packages, or remote URLs. It builds a dependency tree ensuring each plugin receives its own isolated context while maintaining access to shared services. This is the entry point for loading third-party code that consumes your integrated tools.
Step-by-Step Integration Workflow
Follow this sequence to safely integrate any external tool into Cordis:
-
Scaffold the project using the
createpackage (packages/create/src/index.ts) to establish the standard directory structure. -
Expose the target tool as a service by extending the
Serviceclass, wrapping the library’s API, and registering it on the context withctx.add(). -
Load dependent plugins via the
loader, passing file paths or package names. The loader automatically injects the registered services into each plugin’s context. -
Augment behavior with
include(packages/include/src/index.ts) if you need to monkey-patch the external tool at runtime without modifying source code. -
Enable HMR (
packages/hmr/src/index.ts) to watch the integrated tool’s files and hot-reload changes without restarting the entire application.
Practical Implementation Examples
These runnable examples demonstrate integrating a Git client, consuming it in a plugin, patching a logger, and enabling hot-reload.
Wrapping an External Library as a Service
Create a service file that wraps simple-git and exposes it through the context:
// src/services/git.ts
import { Service } from 'cordis'
import simpleGit, { SimpleGit } from 'simple-git'
export class GitService extends Service {
public git: SimpleGit
constructor(ctx: any) {
super(ctx, 'git')
this.git = simpleGit()
}
async clone(repo: string, dir: string) {
return this.git.clone(repo, dir)
}
}
// Register in your main entry
import { Context } from 'cordis'
import { GitService } from './services/git'
export const ctx = new Context()
ctx.add('git', new GitService(ctx))
The Service constructor receives the context and a service name string ('git'), which becomes the property name other plugins use to access the instance.
Loading a Third-Party Plugin That Consumes the Service
Plugins receive the context and can immediately access registered services:
// plugins/auto-deploy.ts
export default (ctx) => {
ctx.on('commit:pushed', async ({ repo, dir }) => {
await ctx.git.clone(repo, dir)
// deployment logic...
})
}
// Boot script
import { loader } from 'cordis'
await loader(ctx, ['./plugins/auto-deploy.ts'])
The loader function in packages/loader/src/index.ts resolves the file path, creates an isolated context for the plugin, and executes the default export with full access to ctx.git.
Runtime Patching with Include
Use include to modify existing services without altering their source files:
import { include } from 'cordis'
include(ctx, {
logger: {
async warn(...args) {
// prepend timestamp to all warnings
this.info('[WARN]', ...args)
},
},
})
This lightweight patch system, defined in packages/include/src/index.ts, intercepts method calls on the context’s logger service and injects custom behavior at runtime.
Enabling Hot Module Replacement for Rapid Development
Watch plugin files and hot-reload them while preserving context state:
import { hmr } from 'cordis'
hmr(ctx, {
files: ['./plugins/auto-deploy.ts'],
debounce: 200,
})
The hmr function from packages/hmr/src/index.ts monitors the specified glob patterns and swaps module implementations without destroying the context isolation map, ensuring that service state persists across reloads.
Key Source Files for Integration Developers
When building integrations, reference these specific files in the cordiverse/cordis repository:
-
packages/core/src/context.ts– Central context manager handling service registration and event propagation (ctx.emit,ctx.on). -
packages/core/src/service.ts– Base class constructor signature and lifecycle hooks for injectable services. -
packages/loader/src/index.ts– Plugin resolution logic and dependency tree construction. -
packages/include/src/index.ts– Runtime augmentation and monkey-patching utilities. -
packages/hmr/src/index.ts– File watcher and hot-swap implementation. -
packages/create/src/index.ts– CLI scaffolding entry point for new projects. -
packages/logger-console/src/index.ts– Reference implementation of a service that wraps the Node.js console API.
Summary
-
Wrap external tools by extending
Servicefrompackages/core/src/service.tsand registering instances viactx.add(). -
Maintain isolation by allowing the
Loaderinpackages/loader/src/index.tsto create separate contexts for each plugin while sharing services through the dependency graph. -
Patch at runtime using the
includesystem inpackages/include/src/index.tsto modify third-party behavior without forking code. -
Preserve state during development by enabling HMR through
packages/hmr/src/index.ts, which reloads modules without resetting the context’s isolation map. -
Access integrated tools within any plugin through the context object (
ctx.<serviceName>) after registration.
Frequently Asked Questions
How do I expose an existing npm package as a Cordis service?
Create a class that extends Service, instantiate the npm package’s client in the constructor, and register it using ctx.add('name', new YourService(ctx)) before loading plugins that depend on it. The service becomes accessible as ctx.name throughout the application lifecycle.
Can I integrate CLI tools that spawn child processes?
Yes. Wrap the child process logic inside a service method. Because each plugin runs in an isolated context via the Context isolation map (ctx[Symbol.isolate]), spawning processes from within a service does not leak state or event listeners to other plugins.
What is the difference between the Loader and Include when integrating tools?
The Loader in packages/loader/src/index.ts resolves and instantiates entire plugins from files or packages, building a fresh isolated context for each. The Include system in packages/include/src/index.ts patches existing service methods at runtime without creating new plugin instances, useful for modifying behavior without reloading modules.
How does hot module replacement affect integrated third-party libraries?
HMR watches the files you specify and reloads only those modules while preserving the Context state. If you wrap a third-party library in a service, updates to your wrapper file trigger a hot-reload, but the service instance remains attached to the context unless you explicitly dispose of it, ensuring continuous availability during development.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →