Cordis Config Patching System for Insert, Override, and Group Management

Cordis lets a project modify an include configuration at load time without touching the original file through a declarative patching system that supports insertions, property overrides, and group-aware hierarchy management.

The Cordis config patching system provides runtime modification of plugin configurations through the @cordisjs/plugin-include package. This architecture enables teams to overlay environment-specific changes, toggle features, and extend modular plugin trees—all while keeping base configuration files pristine.

How the Config Patching System Works

The patching lifecycle operates in two stages: static configuration transformation performed by the include plugin, followed by runtime context propagation handled by the loader. Together, these enable hot-reloadable, hierarchical plugin management.

Core Patch Operations

Cordis recognizes three fundamental operations based on patch object structure:

Operation Trigger Target Behavior
Insert Patch contains insert property Add new entries to root list or specified group
Override Patch has id but no insert Modify existing entry properties
Group-aware lookup Any patch with id referencing a group Navigate nested config arrays via entryMap

Insert Operation: Adding New Entries

The insert operation appends plugin entries to either the root configuration array or a specific group's nested configuration.

In packages/include/src/index.ts, the applyPatches function handles insertion at lines 101–135:

  • When insert is present and id is omitted, entries push to the root list
  • When id specifies a group, entries append to that group's config array
  • Non-group targets trigger a warning via the loader logger
// packages/include/src/index.ts (simplified)
if (patch.insert) {
  const target = patch.id ? entryMap.get(patch.id) : undefined
  if (target?.options.group) {
    // Insert into group's nested config
    target.options.config ??= []
    target.options.config.push(...patch.insert)
  } else if (!patch.id) {
    // Insert at root level
    result.push(...patch.insert)
  }
}

Override Operation: Modifying Existing Entries

The override operation updates properties of an existing entry without full replacement. This requires a mandatory id and optionally validates against name.

From packages/include/src/index.ts lines 136–162:

  1. Locate target via entryMap.get(patch.id)
  2. Verify optional name match (emits warning on mismatch)
  3. Assign every patch key except id directly onto the entry

Common override targets include disabled (boolean), config (object), name (string), and group (boolean).

Group Management and Entry Map

Cordis builds a recursive entry map to enable fast lookup across arbitrarily nested group hierarchies. This structure is constructed at lines 105–114:

// Recursive map construction for O(1) lookups
const entryMap = new Map<string, EntryOptions>()

function walk(entries: EntryOptions[], path: string) {
  for (const entry of entries) {
    entryMap.set(entry.id, { path, options: entry })
    if (entry.group && entry.config) {
      walk(entry.config, `${path}.${entry.id}`)
    }
  }
}

This group management capability allows patches to target deeply nested plugin structures without knowledge of absolute array indices.

Runtime Propagation via Patch Context

When configuration changes affect running services, Cordis triggers the patch-context waterfall in packages/loader/src/config/entry.ts lines 84–92:

// packages/loader/src/config/entry.ts
private _patchContext(diff: string[]) {
  this.context.waterfall('loader/patch-context', this, () => {
    Object.setPrototypeOf(this.ctx, this.parent.ctx)

    if (this.fiber?.uid && (diff.includes('config') || this.options.group)) {
      this.fiber.update(this._resolveConfig(this.fiber.runtime!.callback), true)
    }
  })
}

The waterfall mechanism ensures fiber re-initialization occurs with proper context inheritance when config changes or the entry represents a group.

Complete Patch Configuration Example


# base.yml — original include file (immutable)

- id: core-group
  name: ./group-plugin
  group: true
  config:
    - id: database
      name: ./database-plugin
      config: { host: 'localhost' }
    - id: cache
      name: ./cache-plugin
// Runtime patch application via @cordisjs/plugin-include
await ctx.loader.create({
  name: '@cordisjs/plugin-include',
  config: {
    path: './base.yml',
    patches: [
      // Insert: Add plugin to root
      { insert: [{ id: 'metrics', name: './metrics-plugin' }] },

      // Insert: Add plugin to existing group
      { id: 'core-group', insert: [{ id: 'queue', name: './queue-plugin' }] },

      // Override: Disable existing entry
      { id: 'cache', disabled: true },

      // Override: Modify configuration
      { id: 'database', config: { host: 'prod.db.internal', poolSize: 20 } },

      // Override: With name validation
      { id: 'metrics', name: './metrics-plugin', config: { interval: 5000 } }
    ]
  }
})

Result after patch application:

  • metrics and queue plugins load with their configurations
  • cache entry exists but remains inactive (disabled: true)
  • database connects to production host with enlarged connection pool

Key Implementation Files

File Responsibility
packages/include/src/index.ts Patch processing logic (applyPatches, entryMap construction)
packages/loader/src/config/entry.ts Runtime fiber updates via _patchContext
packages/include/tests/patch.spec.ts Comprehensive test coverage for all patch variants
packages/include/tests/fixtures/base.yml Reference configuration for test scenarios

Summary

  • Insert operation adds entries to root lists or group configurations via the insert property, processed in packages/include/src/index.ts lines 101–135
  • Override operation mutates existing entries by id, supporting property-level updates without full replacement (lines 136–162)
  • Group management relies on recursive entryMap construction enabling fast hierarchical lookups
  • Runtime propagation uses the loader/patch-context waterfall to re-initialize fibers when configuration changes
  • The complete system spans static transformation (@cordisjs/plugin-include) and dynamic updates (@cordisjs/loader)

Frequently Asked Questions

How does Cordis handle patches targeting non-existent entries?

The patching system emits warnings through the loader logger when an id cannot be found in entryMap, or when a name verification fails. The patch is skipped but processing continues for remaining patches.

Can I insert entries into multiple nested groups simultaneously?

No—each insert patch targets a single group via one id. To insert across multiple branches, specify separate patch objects with distinct id references in the patches array.

What happens when I override the group property of an existing entry?

Changing group: true to group: false (or vice versa) alters how subsequent patches resolve the entry's nested config array. The entryMap is rebuilt during the next patch cycle, so structural changes take effect immediately for remaining patches in the same operation.

Is there a performance cost to large patch arrays?

The entryMap construction is O(n) over total entry count, making patch application efficient even for deep hierarchies. However, each config change triggering _patchContext may cause fiber re-initialization—batch related overrides into single patches where possible to minimize runtime overhead.

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 →