How to Derive Child Contexts in Cordis: 5 Methods for Service Isolation and Extension

Cordis supports five distinct mechanisms to derive child contexts: Context.isolate() for service-level isolation, Context.extend() for property extension, Context.intercept() for configuration overrides, loader-generated entry contexts for plugin scoping, and group-based contexts for shared service bundles.

The cordiverse/cordis repository implements a hierarchical context system that enables fine-grained control over service visibility, configuration, and lifecycle. Mastering how to derive child contexts in Cordis is essential for building modular plugins with isolated state, intercepted behaviors, or custom metadata extensions.

Using Context.isolate() for Service‑Level Isolation

The Context.isolate() method creates a child context with an isolated service instance, preventing state leakage between plugins or logical boundaries.

According to the Cordis source code in packages/core/src/context.ts (lines 65–69), isolate() accepts a service name and an optional label, then generates a new isolate map entry. The loader middleware in packages/loader/src/config/isolate.ts handles the underlying map management and context derivation.

import { Context } from 'cordis'

const root = new Context()

// Auto-generated Symbol label
const child = root.isolate('myService')

// Custom label for debugging
const childWithLabel = root.isolate('myService', Symbol('myLabel'))

When you derive child contexts in Cordis using isolation, the framework clones the parent's service registry but replaces the specified service with a fresh instance mapped to the new context.

Extending Contexts with Context.extend()

For scenarios requiring custom metadata or shallow copies without service isolation, Context.extend() provides a general-purpose extension mechanism.

As implemented in packages/core/src/context.ts (lines 55–63), this method produces a shallow copy of the current context while preserving the traceable prototype chain. It merges any additional properties supplied via the meta parameter into the new child context.

const root = new Context()
const child = root.extend({ customProp: 123, apiVersion: 'v2' })

console.log(child.apiVersion)  // → 'v2'

This approach is ideal when you need to derive child contexts in Cordis that carry contextual metadata—such as API versions or request IDs—without altering service implementations.

Intercepting Services via Context.intercept()

The Context.intercept() method derives child contexts that override configuration for existing injectable services, enabling behavioral modifications without changing the service implementation.

The core routine resides in packages/core/src/context.ts (lines 71–77). It builds a new intercept map entry that overrides configuration for a specific service, then returns an extended context containing that map.

const root = new Context()

// Override HTTP timeout for this context branch
const httpCtx = root.intercept('http', { timeout: 5000 })
httpCtx.logger.info('http service intercepted')

This mechanism allows you to derive child contexts in Cordis with specific operational constraints—such as reduced timeouts or modified retry policies—that apply only to that context branch and its descendants.

Loader‑Generated Entry Contexts for Plugins

When the Cordis loader instantiates a plugin (an entry), it automatically derives a child context based on the entry's configuration options. This process involves cloning isolate and intercept maps and generating fresh service implementations.

The implementation spans two key events in packages/loader/src/config/isolate.ts: loader/entry-init clones the parent's maps, while loader/patch-context generates the new isolate map, swaps contexts, reloads the fiber, and replaces service implementations.

import { Loader } from '@cordis/loader'

const loader = new Loader(root)

loader.entry({
  id: 'my-plugin',
  isolate: { 
    db: true,      // Creates isolated database service
    cache: true    // Creates isolated cache service
  }
})

This method automatically derives child contexts in Cordis when loading plugins, ensuring that services marked for isolation receive fresh instances scoped specifically to that entry.

Group‑Based Context Derivation

Groups bundle multiple entries under a shared child context, merging isolation configurations across members to create collective service scopes.

The logic for this hierarchical derivation lives in packages/loader/src/config/group.ts. When a group is defined, the loader derives a single child context that applies shared isolation settings to all member entries.

loader.group('admin-group', [
  { id: 'admin-a', isolate: { admin: true } },
  { id: 'admin-b', isolate: { admin: true } }
])
// Both plugins share the same isolated `admin` service

This approach efficiently derives child contexts in Cordis for multi-plugin architectures where several components require access to the same isolated service instances.

Summary

  • Context.isolate() creates service-level isolation by generating new service instances in packages/core/src/context.ts, managed by the loader at packages/loader/src/config/isolate.ts.
  • Context.extend() performs shallow copying with metadata merging for custom properties without service replacement.
  • Context.intercept() overrides service configurations via intercept maps for behavioral modifications.
  • Loader entry contexts automatically derive children during plugin instantiation through loader/entry-init and loader/patch-context events.
  • Group contexts merge isolation configurations across multiple entries via packages/loader/src/config/group.ts for shared service scopes.

Frequently Asked Questions

What is the performance cost of deriving child contexts in Cordis?

Deriving child contexts in Cordis uses shallow copying and map cloning, making it lightweight for most operations. The Context.extend() method specifically preserves the prototype chain to minimize memory overhead, while isolation only instantiates new service implementations for the specific services marked for isolation, not the entire service registry.

When should I use isolate() versus intercept()?

Use Context.isolate() when you need separate state or instances of a service between contexts—such as isolated database connections per plugin. Use Context.intercept() when you need to modify configuration parameters—like timeouts or retry limits—while sharing the same underlying service instance across contexts.

Can I combine multiple derivation methods in a single child context?

Yes, the Cordis architecture supports composition of derivation methods. The loader routinely combines isolate and intercept configurations when processing entries, as seen in packages/loader/src/config/entry.ts. You can manually chain extend() after isolate() to add custom metadata to an isolated context, or apply intercept() to an already isolated service branch.

How does group isolation differ from entry isolation?

Group isolation, defined in packages/loader/src/config/group.ts, derives a single child context shared across multiple entries, merging their isolation requirements. Entry isolation creates distinct child contexts per plugin. Groups optimize resource usage when several plugins require the same isolated service instance, while entry isolation provides strict separation between unrelated plugins.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →