@dsh-cc/settings-cascade 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts ADDED
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Claude Code-style five-level settings cascade Provider for `ctx.settings`.
3
+ * Five sources merge low-to-high — user settings, project settings, local
4
+ * settings, flag settings, policy settings — under a plugin-default base.
5
+ * The merged raw document feeds the user-settings seam, whose namespace
6
+ * resolution layers schema defaults, the registrant `base`, and this user
7
+ * layer in turn. A top-level `env` section is split out and applied in two
8
+ * stages, holding dangerous variables until trust. Writes are write-through
9
+ * to the user layer: they arrive here as a complete merged section and are
10
+ * diffed back onto the user settings file, so higher-layer contributions stay
11
+ * read-side only.
12
+ * @module @dsh-cc/settings-cascade
13
+ */
14
+ import { Context, Service } from '@deepseek-ai/cordis';
15
+ import z from '@deepseek-ai/schemastery';
16
+ import { SettingsProvider, type SettingsNamespace } from '@deepseek-ai/dsh-settings';
17
+ import { type LocalRootDeps } from './local-root.ts';
18
+ import { type EnvSettings } from './env.ts';
19
+ export { applyCcKeyAliases, CC_KEY_ALIASES } from './cc-key-aliases.ts';
20
+ export { resolveLocalSettingsDir, type LocalRootDeps, type LocalRootExec, type LocalRootExecResult } from './local-root.ts';
21
+ export { mergeValue, mergeSettingsSection, unionDenyPrecedence } from './merge.ts';
22
+ export { AutoModeSchema, AutoModeClassifierSchema, type AutoMode, type AutoModeClassifier } from './auto-mode.ts';
23
+ export { PermissionsSchema, PERMISSION_MODES, type Permissions, type PermissionMode } from './permissions.ts';
24
+ export { applyEnv, applyTrustedEnv, coerceEnv, DANGEROUS_ENV_VARS, type EnvSettings } from './env.ts';
25
+ /** All settings sources in merge order, low to high priority. */
26
+ export declare const SETTING_SOURCES: readonly ["userSettings", "projectSettings", "localSettings", "flagSettings", "policySettings"];
27
+ /** One source in the settings cascade. */
28
+ export type SettingsSource = (typeof SETTING_SOURCES)[number];
29
+ /** Policy sub-sources in first-source-wins priority order. */
30
+ export interface CascadePolicyConfig {
31
+ /** Hosted remote settings — the highest-priority policy source. */
32
+ remoteSettings?: unknown;
33
+ /** System-level managed settings file. */
34
+ systemPath?: string;
35
+ /** User-writable managed settings file. */
36
+ userPath?: string;
37
+ }
38
+ /** Plugin configuration: source file locations and switches. */
39
+ export interface Config {
40
+ /** Launch directory: seeds the project settings path and the git probe for the local settings path. */
41
+ projectDir?: string;
42
+ /** Harness home used for the default user settings path. */
43
+ dshHome?: string;
44
+ /** User settings file; defaults to `settings.json` under the harness home. */
45
+ userSettingsPath?: string;
46
+ /** Project settings file; defaults to `<project>/.claude/settings.json`. */
47
+ projectSettingsPath?: string;
48
+ /** Local settings file; defaults to git main-checkout / toplevel `.claude/settings.local.json`. */
49
+ localSettingsPath?: string;
50
+ /** Command-line `--settings` file; the flag layer's file half. */
51
+ flagSettingsPath?: string;
52
+ /** Inline `--settings` content; merged over the flag file. */
53
+ flagSettingsInline?: unknown;
54
+ /** Policy sub-sources; the first non-empty one wins. */
55
+ policy?: CascadePolicyConfig;
56
+ }
57
+ /** Fully resolved provider parameters; defaulting happens here, never inline. */
58
+ export interface ResolvedSpec {
59
+ sources: {
60
+ userSettings: string | undefined;
61
+ projectSettings: string | undefined;
62
+ localSettings: string | undefined;
63
+ flagSettings: string | undefined;
64
+ };
65
+ flagSettingsInline: unknown;
66
+ policy: {
67
+ systemPath: string | undefined;
68
+ userPath: string | undefined;
69
+ remoteSettings: unknown;
70
+ };
71
+ }
72
+ /**
73
+ * Resolve the runtime spec from plugin config: explicit paths win, otherwise
74
+ * the defaults derive from the harness home and the project directory. The
75
+ * project settings stay at the launch directory, but the local settings file
76
+ * path hoists Claude Code-style to the git main checkout root (linked
77
+ * worktree) or git toplevel (subdirectory start) via
78
+ * {@link resolveLocalSettingsDir} — the session cwd and git operations are
79
+ * untouched, and explicit `localSettingsPath` never hoists.
80
+ * @param config - raw plugin config.
81
+ * @param deps - injectable local-root environment (tests only).
82
+ * @returns the resolved source locations, inline flag settings, and policy sources.
83
+ */
84
+ export declare function resolveSpec(config: Config, deps?: LocalRootDeps): ResolvedSpec;
85
+ /**
86
+ * Claude Code-style five-level settings cascade Provider. Reads the user,
87
+ * project, local, flag, and policy sources in low-to-high priority, deep-merges
88
+ * them (permission arrays union with `deny` precedence), resolves policy by
89
+ * first-source-wins, splits out the top-level `env` section, and publishes the
90
+ * merged per-namespace document into `ctx.settings`. Writes are write-through
91
+ * to the user layer: a merged section is persisted as a surgical leaf-delta
92
+ * applied onto the user settings file, keeping project/local/flag/policy
93
+ * contributions read-side only.
94
+ */
95
+ export declare class SettingsCascadeProvider extends SettingsProvider {
96
+ static Config: z<Config>;
97
+ private readonly spec;
98
+ /** The top-level `env` section split out of the merged document, string-valued. */
99
+ private env;
100
+ /**
101
+ * Mirror of exactly what we last published to the seam via `load()`, and what
102
+ * persist has durably stored. Invariant: shadow moves only when the seam's
103
+ * document moves — it is updated at the end of a successful `load()` and,
104
+ * in `persist()`, only AFTER the atomic rename resolves. Never update it in
105
+ * a `finally` or ahead of a throw. If a file watcher is ever added, any
106
+ * `publish(doc)` path must also update this shadow.
107
+ */
108
+ private shadow;
109
+ /**
110
+ * Single exclusive operation chain: watcher-triggered reloads and persists
111
+ * run one at a time in queue order (settled tail), so a persist can never
112
+ * race a reload's read-modify-publish, and a reload can never read a
113
+ * half-committed atomic rename.
114
+ */
115
+ private operations;
116
+ /** Set at dispose: refuse new watcher events and let in-flight work no-op. */
117
+ private closed;
118
+ /** Opaque read of {@link closed}: control flow cannot narrow it across awaits. */
119
+ private isClosed;
120
+ constructor(ctx: Context, config: Config);
121
+ /** The cascade is now writable; writes edit the user layer. */
122
+ get writable(): boolean;
123
+ /** Absolute path of the user-editable document (the cascade's user source). */
124
+ get documentPath(): string;
125
+ /**
126
+ * Materialize the user-editable document for a native editor. The parent
127
+ * directory is created and an absent document is seeded with an empty
128
+ * namespace map. It intentionally does NOT call {@link SettingsProvider.publish}:
129
+ * the cascade document is the five-layer merge, and publishing `{}` here
130
+ * would clobber project/local/flag/policy contributions in-process (the
131
+ * stock file provider publishes `{}` only because its document is the user
132
+ * file one-to-one).
133
+ * @returns the absolute local document path after materialization.
134
+ */
135
+ prepareDocument(): Promise<string>;
136
+ /**
137
+ * Durably persist one namespace's merged user section by editing the user
138
+ * layer. The section (the seam's complete merged section for the namespace)
139
+ * is diffed against the shadow of what we last published, the delta is
140
+ * applied onto the user settings file's own section for that namespace, and
141
+ * the file is rewritten atomically. The shadow advances only after the
142
+ * atomic rename resolves, so an interrupted persist never desyncs it.
143
+ * @param ns - the namespace being written.
144
+ * @param section - the complete merged user section to store.
145
+ */
146
+ protected persist(ns: SettingsNamespace, section: Record<string, unknown>): Promise<void>;
147
+ /** Queue one exclusive document operation behind every earlier one. */
148
+ private enqueue;
149
+ /** Queue a reload; a failed reload keeps the last-good document and stays only a warning. */
150
+ private queueRefresh;
151
+ /**
152
+ * Persist one namespace with optimistic concurrency: the user-file bytes
153
+ * are re-read immediately before the atomic rename, and when an external
154
+ * writer changed them since the read the op built on, the whole round
155
+ * restarts from a fresh read (bounded, then loud). The shadow still moves
156
+ * only after the write has durably succeeded.
157
+ */
158
+ private persistSection;
159
+ /** Raw user-file text, or `undefined` when the file is absent. */
160
+ private readUserText;
161
+ /** Every concrete settings FILE path worth watching, canonicalized and deduped. */
162
+ private watchPaths;
163
+ [Service.init](): AsyncGenerator<() => Promise<void> | void, void, void>;
164
+ /**
165
+ * Read the merged top-level `env` section. Values are already coerced to
166
+ * strings. Pass the result to {@link applyEnv} / {@link applyTrustedEnv} for
167
+ * the untrusted and trusted application stages.
168
+ * @returns the detached `env` section (empty object when none is configured).
169
+ */
170
+ getEnv(): EnvSettings;
171
+ /**
172
+ * Merge all five sources low-to-high and publish the per-namespace document,
173
+ * splitting the top-level `env` section into {@link getEnv}. A present-but-
174
+ * invalid source file fails load loud; absent sources contribute nothing.
175
+ * @returns the merged document minus its top-level `env` key.
176
+ */
177
+ protected load(): Promise<Record<string, unknown>>;
178
+ /** Read a source file into raw settings, skipping absence and failing loud on invalidity. */
179
+ private readOptional;
180
+ /** Parse one settings document, throwing loud on a non-object root or invalid JSON. */
181
+ private parse;
182
+ /** Merge the flag file and inline content, inline last (highest within the flag layer). */
183
+ private resolveFlag;
184
+ /** Resolve policy by first-source-wins: remote, then system file, then user file. */
185
+ private resolvePolicy;
186
+ /** Coerce every `env` value to its string process-environment form. */
187
+ private coerceEnvSection;
188
+ }
189
+ export default SettingsCascadeProvider;
190
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAKxC,OAAO,EAAE,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AACpF,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAE7E,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AAItD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEvE,OAAO,EAAE,uBAAuB,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAC3H,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACjH,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAC7G,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AAErG,iEAAiE;AACjE,eAAO,MAAM,eAAe,iGAMlB,CAAA;AAEV,0CAA0C;AAC1C,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7D,8DAA8D;AAC9D,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,gEAAgE;AAChE,MAAM,WAAW,MAAM;IACrB,uGAAuG;IACvG,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,4EAA4E;IAC5E,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,mGAAmG;IACnG,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,wDAAwD;IACxD,MAAM,CAAC,EAAE,mBAAmB,CAAA;CAC7B;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE;QACP,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;QAChC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;QACnC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;QACjC,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;KACjC,CAAA;IACD,kBAAkB,EAAE,OAAO,CAAA;IAC3B,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;QAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;QAC5B,cAAc,EAAE,OAAO,CAAA;KACxB,CAAA;CACF;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAkB9E;AAED;;;;;;;;;GASG;AACH,qBAAa,uBAAwB,SAAQ,gBAAgB;IAC3D,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAatB;IAEF,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,mFAAmF;IACnF,OAAO,CAAC,GAAG,CAAkB;IAC7B;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAA8B;IAC5C;;;;;OAKG;IACH,OAAO,CAAC,UAAU,CAAmC;IACrD,8EAA8E;IAC9E,OAAO,CAAC,MAAM,CAAQ;IAEtB,kFAAkF;IAClF,OAAO,CAAC,QAAQ;gBAIJ,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM;IAOxC,+DAA+D;IAC/D,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,+EAA+E;IAC/E,IAAa,YAAY,IAAI,MAAM,CAMlC;IAED;;;;;;;;;OASG;IACY,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC;IASjD;;;;;;;;;OASG;cACa,OAAO,CAAC,EAAE,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/F,uEAAuE;IACvE,OAAO,CAAC,OAAO;IAMf,6FAA6F;IAC7F,OAAO,CAAC,YAAY;IAoBpB;;;;;;OAMG;YACW,cAAc;IAwB5B,kEAAkE;YACpD,YAAY;IAS1B,mFAAmF;YACrE,UAAU;IAaR,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IAiCxF;;;;;OAKG;IACH,MAAM,IAAI,WAAW;IAIrB;;;;;OAKG;cACa,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IA6BxD,6FAA6F;YAC/E,YAAY;IAe1B,uFAAuF;IACvF,OAAO,CAAC,KAAK;IAab,2FAA2F;YAC7E,WAAW;IAMzB,qFAAqF;YACvE,aAAa;IAa3B,uEAAuE;IACvE,OAAO,CAAC,gBAAgB;CAQzB;AAED,eAAe,uBAAuB,CAAA"}
package/lib/index.js ADDED
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Claude Code-style five-level settings cascade Provider for `ctx.settings`.
3
+ * Five sources merge low-to-high — user settings, project settings, local
4
+ * settings, flag settings, policy settings — under a plugin-default base.
5
+ * The merged raw document feeds the user-settings seam, whose namespace
6
+ * resolution layers schema defaults, the registrant `base`, and this user
7
+ * layer in turn. A top-level `env` section is split out and applied in two
8
+ * stages, holding dangerous variables until trust. Writes are write-through
9
+ * to the user layer: they arrive here as a complete merged section and are
10
+ * diffed back onto the user settings file, so higher-layer contributions stay
11
+ * read-side only.
12
+ * @module @dsh-cc/settings-cascade
13
+ */
14
+ import { Service } from '@deepseek-ai/cordis';
15
+ import z from '@deepseek-ai/schemastery';
16
+ import { watch as chokidarWatch } from 'chokidar';
17
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
18
+ import { dirname, join, resolve } from 'node:path';
19
+ import { canonicalizeWatchPath, resolveDshHome } from '@deepseek-ai/dsh-home-paths';
20
+ import { SettingsProvider } from '@deepseek-ai/dsh-settings';
21
+ import { resolveLocalSettingsDir } from "./local-root.js";
22
+ import { mergeSettingsSection } from "./merge.js";
23
+ import { coerceEnv } from "./env.js";
24
+ import { applyOpsToSection, diffSections, writeJsonAtomic } from "./persist.js";
25
+ import { applyCcKeyAliases } from "./cc-key-aliases.js";
26
+ export { applyCcKeyAliases, CC_KEY_ALIASES } from "./cc-key-aliases.js";
27
+ export { resolveLocalSettingsDir } from "./local-root.js";
28
+ export { mergeValue, mergeSettingsSection, unionDenyPrecedence } from "./merge.js";
29
+ export { AutoModeSchema, AutoModeClassifierSchema } from "./auto-mode.js";
30
+ export { PermissionsSchema, PERMISSION_MODES } from "./permissions.js";
31
+ export { applyEnv, applyTrustedEnv, coerceEnv, DANGEROUS_ENV_VARS } from "./env.js";
32
+ /** All settings sources in merge order, low to high priority. */
33
+ export const SETTING_SOURCES = [
34
+ 'userSettings',
35
+ 'projectSettings',
36
+ 'localSettings',
37
+ 'flagSettings',
38
+ 'policySettings',
39
+ ];
40
+ /** Whether a value is a plain data object (not an array, null, or instance). */
41
+ function isPlainObject(value) {
42
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
43
+ return false;
44
+ const proto = Object.getPrototypeOf(value);
45
+ return proto === Object.prototype || proto === null;
46
+ }
47
+ /** Whether a filesystem error simply means the file is absent. */
48
+ function isENOENT(error) {
49
+ return error?.code === 'ENOENT';
50
+ }
51
+ /** Whether a filesystem error is a permission denial (EACCES/EPERM). */
52
+ function isAccessDenied(error) {
53
+ const code = error?.code;
54
+ return code === 'EACCES' || code === 'EPERM';
55
+ }
56
+ /** Watcher write-settle window in milliseconds (mirrors the harness file provider's default). */
57
+ const DEBOUNCE_MS = 100;
58
+ /** Optimistic-retry bound for persist: how many read-check-write rounds before failing loud. */
59
+ const MAX_PERSIST_ATTEMPTS = 5;
60
+ /**
61
+ * Resolve the runtime spec from plugin config: explicit paths win, otherwise
62
+ * the defaults derive from the harness home and the project directory. The
63
+ * project settings stay at the launch directory, but the local settings file
64
+ * path hoists Claude Code-style to the git main checkout root (linked
65
+ * worktree) or git toplevel (subdirectory start) via
66
+ * {@link resolveLocalSettingsDir} — the session cwd and git operations are
67
+ * untouched, and explicit `localSettingsPath` never hoists.
68
+ * @param config - raw plugin config.
69
+ * @param deps - injectable local-root environment (tests only).
70
+ * @returns the resolved source locations, inline flag settings, and policy sources.
71
+ */
72
+ export function resolveSpec(config, deps) {
73
+ const launchDir = resolve(config.projectDir ?? process.cwd());
74
+ const home = resolveDshHome(config.dshHome);
75
+ return {
76
+ sources: {
77
+ userSettings: config.userSettingsPath ?? join(home, 'settings.json'),
78
+ projectSettings: config.projectSettingsPath ?? join(launchDir, '.claude', 'settings.json'),
79
+ localSettings: config.localSettingsPath
80
+ ?? join(resolveLocalSettingsDir(launchDir, deps), '.claude', 'settings.local.json'),
81
+ flagSettings: config.flagSettingsPath,
82
+ },
83
+ flagSettingsInline: config.flagSettingsInline,
84
+ policy: {
85
+ remoteSettings: config.policy?.remoteSettings,
86
+ systemPath: config.policy?.systemPath,
87
+ userPath: config.policy?.userPath,
88
+ },
89
+ };
90
+ }
91
+ /**
92
+ * Claude Code-style five-level settings cascade Provider. Reads the user,
93
+ * project, local, flag, and policy sources in low-to-high priority, deep-merges
94
+ * them (permission arrays union with `deny` precedence), resolves policy by
95
+ * first-source-wins, splits out the top-level `env` section, and publishes the
96
+ * merged per-namespace document into `ctx.settings`. Writes are write-through
97
+ * to the user layer: a merged section is persisted as a surgical leaf-delta
98
+ * applied onto the user settings file, keeping project/local/flag/policy
99
+ * contributions read-side only.
100
+ */
101
+ export class SettingsCascadeProvider extends SettingsProvider {
102
+ static Config = z.object({
103
+ projectDir: z.string(),
104
+ dshHome: z.string(),
105
+ userSettingsPath: z.string(),
106
+ projectSettingsPath: z.string(),
107
+ localSettingsPath: z.string(),
108
+ flagSettingsPath: z.string(),
109
+ flagSettingsInline: z.any(),
110
+ policy: z.object({
111
+ remoteSettings: z.any(),
112
+ systemPath: z.string(),
113
+ userPath: z.string(),
114
+ }),
115
+ });
116
+ spec;
117
+ /** The top-level `env` section split out of the merged document, string-valued. */
118
+ env = {};
119
+ /**
120
+ * Mirror of exactly what we last published to the seam via `load()`, and what
121
+ * persist has durably stored. Invariant: shadow moves only when the seam's
122
+ * document moves — it is updated at the end of a successful `load()` and,
123
+ * in `persist()`, only AFTER the atomic rename resolves. Never update it in
124
+ * a `finally` or ahead of a throw. If a file watcher is ever added, any
125
+ * `publish(doc)` path must also update this shadow.
126
+ */
127
+ shadow = {};
128
+ /**
129
+ * Single exclusive operation chain: watcher-triggered reloads and persists
130
+ * run one at a time in queue order (settled tail), so a persist can never
131
+ * race a reload's read-modify-publish, and a reload can never read a
132
+ * half-committed atomic rename.
133
+ */
134
+ operations = Promise.resolve();
135
+ /** Set at dispose: refuse new watcher events and let in-flight work no-op. */
136
+ closed = false;
137
+ /** Opaque read of {@link closed}: control flow cannot narrow it across awaits. */
138
+ isClosed() {
139
+ return this.closed;
140
+ }
141
+ constructor(ctx, config) {
142
+ super(ctx);
143
+ // Programmatic construction may bypass Schemastery normalization; resolve
144
+ // the same defaults in one explicit step either way.
145
+ this.spec = resolveSpec(config);
146
+ }
147
+ /** The cascade is now writable; writes edit the user layer. */
148
+ get writable() {
149
+ return true;
150
+ }
151
+ /** Absolute path of the user-editable document (the cascade's user source). */
152
+ get documentPath() {
153
+ const path = this.spec.sources.userSettings;
154
+ if (path === undefined) {
155
+ throw new Error('settings-cascade: userSettings source is not configured; no user layer to edit');
156
+ }
157
+ return path;
158
+ }
159
+ /**
160
+ * Materialize the user-editable document for a native editor. The parent
161
+ * directory is created and an absent document is seeded with an empty
162
+ * namespace map. It intentionally does NOT call {@link SettingsProvider.publish}:
163
+ * the cascade document is the five-layer merge, and publishing `{}` here
164
+ * would clobber project/local/flag/policy contributions in-process (the
165
+ * stock file provider publishes `{}` only because its document is the user
166
+ * file one-to-one).
167
+ * @returns the absolute local document path after materialization.
168
+ */
169
+ async prepareDocument() {
170
+ const path = this.documentPath;
171
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
172
+ await writeFile(path, '{}\n', { flag: 'wx', mode: 0o600 }).catch(error => {
173
+ if (error?.code !== 'EEXIST')
174
+ throw error;
175
+ });
176
+ return path;
177
+ }
178
+ /**
179
+ * Durably persist one namespace's merged user section by editing the user
180
+ * layer. The section (the seam's complete merged section for the namespace)
181
+ * is diffed against the shadow of what we last published, the delta is
182
+ * applied onto the user settings file's own section for that namespace, and
183
+ * the file is rewritten atomically. The shadow advances only after the
184
+ * atomic rename resolves, so an interrupted persist never desyncs it.
185
+ * @param ns - the namespace being written.
186
+ * @param section - the complete merged user section to store.
187
+ */
188
+ async persist(ns, section) {
189
+ // Serialize with watcher-triggered reloads on the one operation chain, and
190
+ // retry optimistically when an external edit lands between the read and
191
+ // the atomic rename.
192
+ return this.enqueue(() => this.persistSection(ns, section));
193
+ }
194
+ /** Queue one exclusive document operation behind every earlier one. */
195
+ enqueue(operation) {
196
+ const task = this.operations.then(operation);
197
+ this.operations = task.then(() => undefined, () => undefined);
198
+ return task;
199
+ }
200
+ /** Queue a reload; a failed reload keeps the last-good document and stays only a warning. */
201
+ queueRefresh() {
202
+ void this.enqueue(async () => {
203
+ if (this.isClosed())
204
+ return;
205
+ try {
206
+ this.publish(await this.load());
207
+ }
208
+ catch (error) {
209
+ // A malformed or unreadable source must never take a live session
210
+ // down: keep the last good document published and keep watching.
211
+ this.ctx.logger.warn('settings-cascade: reload failed; keeping the last good document');
212
+ this.ctx.logger.warn(error);
213
+ }
214
+ }).catch((error) => {
215
+ // Only an invariant violation escaping the commit path can reject a
216
+ // reload past the warn-and-keep guard; keep the queue alive so one
217
+ // poisoned commit cannot silently end hot reloading forever.
218
+ this.ctx.logger.warn('settings-cascade: reload commit failed');
219
+ this.ctx.logger.warn(error);
220
+ });
221
+ }
222
+ /**
223
+ * Persist one namespace with optimistic concurrency: the user-file bytes
224
+ * are re-read immediately before the atomic rename, and when an external
225
+ * writer changed them since the read the op built on, the whole round
226
+ * restarts from a fresh read (bounded, then loud). The shadow still moves
227
+ * only after the write has durably succeeded.
228
+ */
229
+ async persistSection(ns, section) {
230
+ const path = this.documentPath;
231
+ let lastError;
232
+ for (let attempt = 0; attempt < MAX_PERSIST_ATTEMPTS; attempt++) {
233
+ const ops = diffSections(this.shadow[ns] ?? {}, section);
234
+ if (ops.length === 0) {
235
+ this.shadow[ns] = structuredClone(section);
236
+ return;
237
+ }
238
+ const before = await this.readUserText(path);
239
+ const root = before === undefined || before.trim().length === 0 ? {} : this.parse(path, before);
240
+ const next = applyOpsToSection(root[ns], ops);
241
+ const updated = { ...root, [ns]: next };
242
+ if ((await this.readUserText(path)) !== before) {
243
+ lastError = new Error(`settings-cascade: user settings file at ${path} changed concurrently during persist`);
244
+ continue;
245
+ }
246
+ await writeJsonAtomic(path, updated);
247
+ this.shadow[ns] = structuredClone(section);
248
+ return;
249
+ }
250
+ throw lastError ?? new Error(`settings-cascade: persist at ${path} exhausted ${MAX_PERSIST_ATTEMPTS} optimistic-retry attempts`);
251
+ }
252
+ /** Raw user-file text, or `undefined` when the file is absent. */
253
+ async readUserText(path) {
254
+ try {
255
+ return await readFile(path, 'utf8');
256
+ }
257
+ catch (error) {
258
+ if (isENOENT(error))
259
+ return undefined;
260
+ throw error;
261
+ }
262
+ }
263
+ /** Every concrete settings FILE path worth watching, canonicalized and deduped. */
264
+ async watchPaths() {
265
+ const paths = [
266
+ this.spec.sources.userSettings,
267
+ this.spec.sources.projectSettings,
268
+ this.spec.sources.localSettings,
269
+ this.spec.sources.flagSettings,
270
+ this.spec.policy.systemPath,
271
+ this.spec.policy.userPath,
272
+ ].filter((path) => path !== undefined);
273
+ const canonical = await Promise.all(paths.map(path => canonicalizeWatchPath(path)));
274
+ return [...new Set(canonical)];
275
+ }
276
+ async *[Service.init]() {
277
+ // The base init loads and publishes the merged document; a parse failure
278
+ // there is a boot failure and stays loud.
279
+ yield* super[Service.init]();
280
+ const watcher = chokidarWatch(await this.watchPaths(), {
281
+ ignoreInitial: true,
282
+ awaitWriteFinish: {
283
+ stabilityThreshold: DEBOUNCE_MS,
284
+ pollInterval: Math.max(1, Math.min(DEBOUNCE_MS, 10)),
285
+ },
286
+ });
287
+ watcher.on('all', () => {
288
+ if (this.isClosed())
289
+ return;
290
+ this.queueRefresh();
291
+ });
292
+ watcher.on('ready', () => {
293
+ // The base init's load raced the watcher's own setup: a change written
294
+ // between that read and the watcher becoming active never fires an
295
+ // event. One reconcile at ready closes the gap.
296
+ if (this.isClosed())
297
+ return;
298
+ this.queueRefresh();
299
+ });
300
+ watcher.on('error', (error) => {
301
+ this.ctx.logger.warn('settings-cascade: watcher error');
302
+ this.ctx.logger.warn(error);
303
+ });
304
+ yield async () => {
305
+ this.closed = true;
306
+ await watcher.close();
307
+ await this.operations;
308
+ };
309
+ }
310
+ /**
311
+ * Read the merged top-level `env` section. Values are already coerced to
312
+ * strings. Pass the result to {@link applyEnv} / {@link applyTrustedEnv} for
313
+ * the untrusted and trusted application stages.
314
+ * @returns the detached `env` section (empty object when none is configured).
315
+ */
316
+ getEnv() {
317
+ return structuredClone(this.env);
318
+ }
319
+ /**
320
+ * Merge all five sources low-to-high and publish the per-namespace document,
321
+ * splitting the top-level `env` section into {@link getEnv}. A present-but-
322
+ * invalid source file fails load loud; absent sources contribute nothing.
323
+ * @returns the merged document minus its top-level `env` key.
324
+ */
325
+ async load() {
326
+ // The five source resolutions are independent; read them concurrently.
327
+ // With MULTIPLE invalid sources, serial loading surfaced the lowest layer
328
+ // deterministically while Promise.all surfaces whichever rejects first in
329
+ // time — both fail loud. Single-invalid behavior is unchanged.
330
+ const [user, project, local, flag, policy] = await Promise.all([
331
+ this.readOptional(this.spec.sources.userSettings),
332
+ this.readOptional(this.spec.sources.projectSettings),
333
+ this.readOptional(this.spec.sources.localSettings, true),
334
+ this.resolveFlag(),
335
+ this.resolvePolicy(),
336
+ ]);
337
+ const merged = [user, project, local, flag, policy]
338
+ .reduce((acc, layer) => (Object.keys(layer).length === 0
339
+ ? acc
340
+ : mergeSettingsSection(acc, layer)), {});
341
+ // CC camelCase top-level keys alias onto kebab namespaces before the env
342
+ // split / publish, so the shadow mirrors exactly what the seam resolves.
343
+ const { env, ...document } = applyCcKeyAliases(merged);
344
+ this.env = this.coerceEnvSection(env);
345
+ this.shadow = structuredClone(document);
346
+ return document;
347
+ }
348
+ /** Read a source file into raw settings, skipping absence and failing loud on invalidity. */
349
+ async readOptional(path, local = false) {
350
+ if (path === undefined)
351
+ return {};
352
+ let text;
353
+ try {
354
+ text = await readFile(path, 'utf8');
355
+ }
356
+ catch (error) {
357
+ // The hoisted local file may sit in a main checkout whose `.claude`
358
+ // denies us mid-session (EACCES/EPERM); treat that as absent so a
359
+ // cross-boundary read cannot crash boot. Other sources stay loud.
360
+ if (isENOENT(error) || (local && isAccessDenied(error)))
361
+ return {};
362
+ throw error;
363
+ }
364
+ return this.parse(path, text);
365
+ }
366
+ /** Parse one settings document, throwing loud on a non-object root or invalid JSON. */
367
+ parse(path, text) {
368
+ let root;
369
+ try {
370
+ root = text.trim().length === 0 ? {} : JSON.parse(text);
371
+ }
372
+ catch (error) {
373
+ throw new Error(`settings-cascade: invalid settings document at ${path}: ${error.message}`, { cause: error });
374
+ }
375
+ if (!isPlainObject(root)) {
376
+ throw new TypeError(`settings-cascade: ${path} must be a JSON object of namespace sections`);
377
+ }
378
+ return root;
379
+ }
380
+ /** Merge the flag file and inline content, inline last (highest within the flag layer). */
381
+ async resolveFlag() {
382
+ const file = await this.readOptional(this.spec.sources.flagSettings);
383
+ if (!isPlainObject(this.spec.flagSettingsInline))
384
+ return file;
385
+ return mergeSettingsSection(file, this.spec.flagSettingsInline);
386
+ }
387
+ /** Resolve policy by first-source-wins: remote, then system file, then user file. */
388
+ async resolvePolicy() {
389
+ const { remoteSettings, systemPath, userPath } = this.spec.policy;
390
+ if (isPlainObject(remoteSettings) && Object.keys(remoteSettings).length > 0) {
391
+ return remoteSettings;
392
+ }
393
+ for (const path of [systemPath, userPath]) {
394
+ if (path === undefined)
395
+ continue;
396
+ const settings = await this.readOptional(path);
397
+ if (Object.keys(settings).length > 0)
398
+ return settings;
399
+ }
400
+ return {};
401
+ }
402
+ /** Coerce every `env` value to its string process-environment form. */
403
+ coerceEnvSection(value) {
404
+ if (!isPlainObject(value))
405
+ return {};
406
+ const out = {};
407
+ for (const [name, raw] of Object.entries(value)) {
408
+ out[name] = coerceEnv(raw);
409
+ }
410
+ return out;
411
+ }
412
+ }
413
+ export default SettingsCascadeProvider;
414
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@dsh-cc/settings-cascade`.
3
+ * @module @dsh-cc/settings-cascade/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "settings-cascade-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,oCAAoC;AACpC,eAAO,MAAM,IAAI,+BAA+B,CAAA;AAChD,2EAA2E;AAC3E,eAAO,MAAM,MAAM,UAAiB,CAAA;AASpC;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,OAAO,KAAG,OAAO,CAAC,MAAM,IAAI,CACU,CAAA"}