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
insertis present andidis omitted, entries push to the root list - When
idspecifies a group, entries append to that group'sconfigarray - 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:
- Locate target via
entryMap.get(patch.id) - Verify optional
namematch (emits warning on mismatch) - Assign every patch key except
iddirectly 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:
metricsandqueueplugins load with their configurationscacheentry exists but remains inactive (disabled: true)databaseconnects 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
insertproperty, processed inpackages/include/src/index.tslines 101–135 - Override operation mutates existing entries by
id, supporting property-level updates without full replacement (lines 136–162) - Group management relies on recursive
entryMapconstruction enabling fast hierarchical lookups - Runtime propagation uses the
loader/patch-contextwaterfall 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →