Files
deepseek-harness/packages/settings/settings/tests/memory.ts
T
Yichen Jiang ec0786e099 feat(settings): add user-settings seam (ctx.settings) + file provider
Two-package capability family mirroring session-persistence/:

- dsh-settings: abstract Settings service — namespace registry with
  caller-fiber effect registrations, layered resolution (schema defaults
  < composition base < user document), schemastery validation,
  per-namespace deep-equal commit detection, and the settings/updated
  event. Boot/registration validation fails loud; provider publishes
  keep last-good per namespace.
- dsh-settings-local: settings.yaml/.json provider — resolveSpec
  defaulting to $DSH_HOME/settings.yaml, chokidar hot reload,
  content-equality self-write suppression, atomic 0600 tmp+rename
  writes, comment-preserving YAML namespace patching.

Consumers register inside ctx.inject(['settings'], …), so every
composition works unchanged without a mounted provider. Real Loader +
Include composition test proves cordis.yml boot and external-edit hot
propagation; HMR disposal test proves registry cleanup. Both packages
hold per-file 100% coverage.

Doc budgets rise 1705→1710 (AGENTS.md) and 835→845 (packages/README.md):
one structural line per file for the new package group.

Agent Note: .agents/notes/implemented/architecture/2026-07-28-user-settings-seam.md
2026-07-29 10:19:32 +08:00

54 lines
1.8 KiB
TypeScript

/**
* In-memory settings provider fixture: the smallest real subclass of the seam,
* used by the base-class behavior suite in place of a file- or network-backed
* provider. Kept in `tests/` because production providers live in their own
* packages.
*/
import { Service } from 'cordis'
import { Settings, type SettingsNamespace } from '../src/index.ts'
/** In-memory provider exposing the protected seam hooks to tests. */
export class MemorySettings extends Settings {
/** Raw document the provider "storage" currently holds. */
doc: Record<string, unknown>
/** Every persist() call observed, in order. */
persisted: Array<{ ns: SettingsNamespace; section: Record<string, unknown> }> = []
/** When false, update() must reject before reaching persist(). */
writableFlag: boolean
constructor(ctx: ConstructorParameters<typeof Settings>[0], options?: {
doc?: Record<string, unknown>
writable?: boolean
}) {
super(ctx)
this.doc = structuredClone(options?.doc ?? {})
this.writableFlag = options?.writable ?? true
}
get writable(): boolean {
return this.writableFlag
}
protected load(): Promise<Record<string, unknown>> {
return Promise.resolve(structuredClone(this.doc))
}
protected persist(ns: SettingsNamespace, section: Record<string, unknown>): Promise<void> {
this.persisted.push({ ns, section: structuredClone(section) })
this.doc[ns] = structuredClone(section)
return Promise.resolve()
}
/** Simulate an external storage change reaching the provider. */
pushExternal(doc: Record<string, unknown>): void {
this.doc = structuredClone(doc)
this.publish(structuredClone(doc))
}
async* [Service.init](): AsyncGenerator<() => void, void, void> {
this.publish(await this.load())
yield () => { this.persisted.length = 0 }
}
}